Repository: winsiderss/systeminformer
Branch: master
Commit: 0d2893cc8929
Files: 1190
Total size: 44.0 MB
Directory structure:
gitextract_4bdkv5wf/
├── .editorconfig
├── .gitattributes
├── .github/
│ ├── CODEOWNERS
│ ├── FUNDING.yml
│ ├── ISSUE_TEMPLATE/
│ │ ├── bug_report.yml
│ │ ├── config.yml
│ │ └── feature_request.yml
│ └── workflows/
│ ├── cla.yml
│ ├── cmake.yml
│ ├── msbuild.yml
│ └── scan.yml
├── .gitignore
├── .vscode/
│ ├── launch.json
│ └── tasks.json
├── .vsconfig
├── CHANGELOG.txt
├── CLA.md
├── CMakeLists.txt
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── COPYRIGHT.txt
├── Common.Kernel.props
├── Common.Kernel.ruleset
├── Common.User.props
├── Directory.Build.props
├── HACKING.md
├── KSystemInformer/
│ ├── Directory.Build.props
│ ├── KSystemInformer.inf
│ ├── KSystemInformer.sln
│ ├── KSystemInformer.slnx
│ ├── KSystemInformer.vcxproj
│ ├── KSystemInformer.vcxproj.filters
│ ├── README.md
│ ├── alloc.c
│ ├── alpc.c
│ ├── back_trace.c
│ ├── bin-signed/
│ │ ├── amd64/
│ │ │ ├── ksi.pdb
│ │ │ ├── systeminformer.pdb
│ │ │ └── systeminformer.sys
│ │ └── arm64/
│ │ ├── ksi.pdb
│ │ ├── systeminformer.pdb
│ │ └── systeminformer.sys
│ ├── cid_table.c
│ ├── cid_tracking.c
│ ├── comms.c
│ ├── comms_handlers.c
│ ├── device.c
│ ├── driver.c
│ ├── dyndata.c
│ ├── dynimp.c
│ ├── file.c
│ ├── hash.c
│ ├── imgcoherency.c
│ ├── include/
│ │ ├── comms.h
│ │ ├── informer.h
│ │ ├── informer_filep.h
│ │ ├── kph.h
│ │ ├── ntfill.h
│ │ ├── pooltags.h
│ │ └── trace.h
│ ├── informer.c
│ ├── informer_debug.c
│ ├── informer_file.c
│ ├── informer_filenc.c
│ ├── informer_fileop.c
│ ├── informer_image.c
│ ├── informer_object.c
│ ├── informer_process.c
│ ├── informer_registry.c
│ ├── informer_thread.c
│ ├── knowndll.c
│ ├── kphobject.c
│ ├── kphthread.c
│ ├── ksidll.c
│ ├── ksidll.def
│ ├── ksidll.vcxproj
│ ├── lsa.c
│ ├── main.c
│ ├── object.c
│ ├── parameters.c
│ ├── process.c
│ ├── protection.c
│ ├── ratelmt.c
│ ├── resource.rc
│ ├── ringbuff.c
│ ├── session_token.c
│ ├── system.c
│ ├── thread.c
│ ├── umaccess.c
│ ├── util.c
│ ├── verify.c
│ └── vm.c
├── LICENSE.txt
├── README.md
├── README.txt
├── SECURITY.md
├── SystemInformer/
│ ├── CMakeLists.txt
│ ├── Directory.Build.props
│ ├── SystemInformer.def
│ ├── SystemInformer.def.h
│ ├── SystemInformer.manifest
│ ├── SystemInformer.rc
│ ├── SystemInformer.vcxproj
│ ├── SystemInformer.vcxproj.filters
│ ├── about.c
│ ├── actions.c
│ ├── admintask.c
│ ├── affinity.c
│ ├── anawait.c
│ ├── appsup.c
│ ├── chcol.c
│ ├── chdlg.c
│ ├── chproc.c
│ ├── colmgr.c
│ ├── colsetmgr.c
│ ├── dbgcon.c
│ ├── delayhook.c
│ ├── delayload.c
│ ├── devprv.c
│ ├── extmgr.c
│ ├── findobj.c
│ ├── gdihndl.c
│ ├── heapinfo.c
│ ├── hidnproc.c
│ ├── hndllist.c
│ ├── hndlmenu.c
│ ├── hndlprp.c
│ ├── hndlprv.c
│ ├── hndlstat.c
│ ├── include/
│ │ ├── actions.h
│ │ ├── appsup.h
│ │ ├── colmgr.h
│ │ ├── colsetmgr.h
│ │ ├── devprv.h
│ │ ├── extmgr.h
│ │ ├── extmgri.h
│ │ ├── heapstruct.h
│ │ ├── hidnproc.h
│ │ ├── hndllist.h
│ │ ├── hndlmenu.h
│ │ ├── hndlprv.h
│ │ ├── informer.h
│ │ ├── ksisup.h
│ │ ├── mainwnd.h
│ │ ├── mainwndp.h
│ │ ├── memlist.h
│ │ ├── memprv.h
│ │ ├── memsrch.h
│ │ ├── miniinfo.h
│ │ ├── miniinfop.h
│ │ ├── modlist.h
│ │ ├── modprv.h
│ │ ├── netlist.h
│ │ ├── netprv.h
│ │ ├── notifico.h
│ │ ├── notificop.h
│ │ ├── notiftoast.h
│ │ ├── phapp.h
│ │ ├── phappres.h
│ │ ├── phfwddef.h
│ │ ├── phplug.h
│ │ ├── phsettings.h
│ │ ├── phsvc.h
│ │ ├── phsvcapi.h
│ │ ├── phsvccl.h
│ │ ├── phuisup.h
│ │ ├── procgrp.h
│ │ ├── procmtgn.h
│ │ ├── procprp.h
│ │ ├── procprpp.h
│ │ ├── procprv.h
│ │ ├── proctree.h
│ │ ├── srvlist.h
│ │ ├── srvprv.h
│ │ ├── sysinfo.h
│ │ ├── sysinfop.h
│ │ ├── thrdlist.h
│ │ └── thrdprv.h
│ ├── infodlg.c
│ ├── informer.c
│ ├── itemtips.c
│ ├── jobprp.c
│ ├── kdump.c
│ ├── ksidbg.c
│ ├── ksisup.c
│ ├── ksyscall.c
│ ├── log.c
│ ├── logwnd.c
│ ├── main.c
│ ├── mainwnd.c
│ ├── mdump.c
│ ├── memedit.c
│ ├── memlist.c
│ ├── memlists.c
│ ├── memmod.c
│ ├── memprot.c
│ ├── memprv.c
│ ├── memrslt.c
│ ├── memsrch.c
│ ├── memsrcht.c
│ ├── miniinfo.c
│ ├── modlist.c
│ ├── modprv.c
│ ├── mtgndlg.c
│ ├── mwpgdev.c
│ ├── mwpgnet.c
│ ├── mwpgproc.c
│ ├── mwpgsrv.c
│ ├── netlist.c
│ ├── netprv.c
│ ├── netstk.c
│ ├── netsup.c
│ ├── notifico.c
│ ├── notiftoast.cpp
│ ├── ntobjprp.c
│ ├── options.c
│ ├── pagfiles.c
│ ├── phsvc/
│ │ ├── clapi.c
│ │ ├── svcapi.c
│ │ ├── svcapiport.c
│ │ ├── svcclient.c
│ │ └── svcmain.c
│ ├── plugin.c
│ ├── plugman.c
│ ├── procgrp.c
│ ├── procmtgn.c
│ ├── procprp.c
│ ├── procprv.c
│ ├── procrec.c
│ ├── proctree.c
│ ├── prpgenv.c
│ ├── prpggen.c
│ ├── prpghndl.c
│ ├── prpgjob.c
│ ├── prpgmem.c
│ ├── prpgmod.c
│ ├── prpgperf.c
│ ├── prpgsrv.c
│ ├── prpgstat.c
│ ├── prpgthrd.c
│ ├── prpgtok.c
│ ├── prpgvdm.c
│ ├── prpgwmi.c
│ ├── resource.h
│ ├── resources/
│ │ ├── capslist.txt
│ │ ├── etwguids.txt
│ │ └── pooltag.txt
│ ├── runas.c
│ ├── searchbox.c
│ ├── sessmsg.c
│ ├── sessprp.c
│ ├── sessshad.c
│ ├── settings.c
│ ├── srvcr.c
│ ├── srvctl.c
│ ├── srvlist.c
│ ├── srvprp.c
│ ├── srvprv.c
│ ├── sysinfo.c
│ ├── syssccpu.c
│ ├── sysscio.c
│ ├── sysscmem.c
│ ├── thrdlist.c
│ ├── thrdprv.c
│ ├── thrdstk.c
│ ├── thrdstks.c
│ ├── tokprp.c
│ ├── usrlist.c
│ └── version.rc
├── SystemInformer.natvis
├── SystemInformer.sln
├── SystemInformer.slnx
├── build/
│ ├── KSystemInformer.ddf
│ ├── README.md
│ ├── build_clean.cmd
│ ├── build_cmake.cmd
│ ├── build_compile_commands.cmd
│ ├── build_debug.cmd
│ ├── build_devenv.cmd
│ ├── build_dyndata.cmd
│ ├── build_header.cmd
│ ├── build_init.cmd
│ ├── build_msix.cmd
│ ├── build_release.cmd
│ ├── build_sdk.cmd
│ ├── build_sdk_rebuild.cmd
│ ├── build_thirdparty.cmd
│ ├── build_tools.cmd
│ ├── build_verbose.cmd
│ ├── build_zdriver.cmd
│ ├── build_zdriver_cab.cmd
│ ├── build_zdriver_sdk.cmd
│ ├── build_zsign.cmd
│ └── build_zwntapi.ps1
├── cmake/
│ ├── commands.cmake
│ ├── platform.cmake
│ ├── prefast.cmake
│ ├── toolchain/
│ │ ├── clang-msvc-amd64.cmake
│ │ ├── clang-msvc-arm64.cmake
│ │ ├── clang-msvc-x86.cmake
│ │ ├── clang-msvc.cmake
│ │ ├── finalize-msvc.cmake
│ │ ├── msvc-amd64.cmake
│ │ ├── msvc-arm64.cmake
│ │ ├── msvc-x86.cmake
│ │ ├── msvc.cmake
│ │ └── override-msvc.cmake
│ ├── tools.cmake
│ └── tracewpp.cmake
├── kphlib/
│ ├── CMakeLists.txt
│ ├── Directory.Build.props
│ ├── include/
│ │ ├── kphapi.h
│ │ ├── kphdyn.h
│ │ ├── kphdyndata.h
│ │ ├── kphlibbase.h
│ │ ├── kphmsg.h
│ │ ├── kphmsgdefs.h
│ │ ├── kphmsgdyn.h
│ │ ├── kphringbuff.h
│ │ ├── sistatus.h
│ │ └── sistatus.rc
│ ├── kphdyn.c
│ ├── kphdyn.xml
│ ├── kphdyndata.c
│ ├── kphlib_km.filters
│ ├── kphlib_km.vcxproj
│ ├── kphlib_km.vcxproj.filters
│ ├── kphlib_um.vcxproj
│ ├── kphlib_um.vcxproj.filters
│ ├── kphmsg.c
│ ├── kphmsgdyn.c
│ ├── kphringbuff.c
│ └── sistatus.mc
├── packages.config
├── phlib/
│ ├── CMakeLists.txt
│ ├── Directory.Build.props
│ ├── apiimport.c
│ ├── apistubs.cpp
│ ├── appresolver.c
│ ├── appruntime.c
│ ├── avltree.c
│ ├── basestring.c
│ ├── basesup.c
│ ├── bcd.cpp
│ ├── circbuf.c
│ ├── circbuf_i.h
│ ├── colorbox.c
│ ├── cpysave.c
│ ├── data.c
│ ├── directdraw.cpp
│ ├── dspick.c
│ ├── emenu.c
│ ├── error.c
│ ├── extlv.c
│ ├── fastlock.c
│ ├── filepool.c
│ ├── filestream.c
│ ├── firmware.c
│ ├── format.c
│ ├── format_i.h
│ ├── format_std.cpp
│ ├── global.c
│ ├── graph.c
│ ├── guisup.c
│ ├── guisuplistview.cpp
│ ├── handle.c
│ ├── hexedit.c
│ ├── hndlinfo.c
│ ├── http.c
│ ├── hvsocketcontrol.c
│ ├── icotobmp.c
│ ├── imgcoherency.c
│ ├── include/
│ │ ├── apiimport.h
│ │ ├── appresolver.h
│ │ ├── appresolverp.h
│ │ ├── bcd.h
│ │ ├── circbuf.h
│ │ ├── circbuf_h.h
│ │ ├── colorbox.h
│ │ ├── cpysave.h
│ │ ├── dltmgr.h
│ │ ├── dspick.h
│ │ ├── emenu.h
│ │ ├── exlf.h
│ │ ├── exprodid.h
│ │ ├── fastlock.h
│ │ ├── filepool.h
│ │ ├── filepoolp.h
│ │ ├── filestream.h
│ │ ├── filestreamp.h
│ │ ├── graph.h
│ │ ├── guisup.h
│ │ ├── guisupp.h
│ │ ├── guisupview.h
│ │ ├── handle.h
│ │ ├── handlep.h
│ │ ├── hexedit.h
│ │ ├── hexeditp.h
│ │ ├── hndlinfo.h
│ │ ├── hvsocketcontrol.h
│ │ ├── json.h
│ │ ├── kphcomms.h
│ │ ├── kphuser.h
│ │ ├── lsamsup.h
│ │ ├── lsasup.h
│ │ ├── mapimg.h
│ │ ├── mapldr.h
│ │ ├── ph.h
│ │ ├── phafd.h
│ │ ├── phbase.h
│ │ ├── phbasesup.h
│ │ ├── phconfig.h
│ │ ├── phconsole.h
│ │ ├── phdata.h
│ │ ├── phfirmware.h
│ │ ├── phintrin.h
│ │ ├── phintrnl.h
│ │ ├── phnative.h
│ │ ├── phnativeinl.h
│ │ ├── phnet.h
│ │ ├── phsup.h
│ │ ├── phutil.h
│ │ ├── provider.h
│ │ ├── queuedlock.h
│ │ ├── ref.h
│ │ ├── refp.h
│ │ ├── searchbox.h
│ │ ├── secedit.h
│ │ ├── seceditp.h
│ │ ├── secwmi.h
│ │ ├── settings.h
│ │ ├── strsrch.h
│ │ ├── svcsup.h
│ │ ├── symprv.h
│ │ ├── symprvp.h
│ │ ├── templ.h
│ │ ├── thirdparty.h
│ │ ├── trace.h
│ │ ├── treenew.h
│ │ ├── treenewp.h
│ │ ├── verify.h
│ │ ├── verifyp.h
│ │ ├── workqueue.h
│ │ ├── workqueuep.h
│ │ └── wslsup.h
│ ├── json.c
│ ├── kph.c
│ ├── kphcomms.c
│ ├── lsamsup.c
│ ├── lsasup.c
│ ├── mapexlf.c
│ ├── mapimg.c
│ ├── mapldr.c
│ ├── maplib.c
│ ├── native.c
│ ├── nativefile.c
│ ├── nativeflt.c
│ ├── nativejob.c
│ ├── nativekey.c
│ ├── nativemodule.c
│ ├── nativepipe.c
│ ├── nativeprocess.c
│ ├── nativesocket.c
│ ├── nativethread.c
│ ├── nativetoken.c
│ ├── nativetxn.c
│ ├── nativeuser.c
│ ├── phlib.vcxproj
│ ├── phlib.vcxproj.filters
│ ├── provider.c
│ ├── queuedlock.c
│ ├── ref.c
│ ├── searchbox.c
│ ├── secdata.c
│ ├── secedit.c
│ ├── secwmi.c
│ ├── settings.c
│ ├── strsrch.c
│ ├── svcsup.c
│ ├── symprv.c
│ ├── symprv_std.cpp
│ ├── sync.c
│ ├── theme.c
│ ├── treenew.c
│ ├── util.c
│ ├── verify.c
│ ├── workqueue.c
│ └── wslsup.c
├── phnt/
│ ├── CMakeLists.txt
│ ├── README.md
│ ├── include/
│ │ ├── ntafd.h
│ │ ├── ntbcd.h
│ │ ├── ntdbg.h
│ │ ├── ntexapi.h
│ │ ├── ntgdi.h
│ │ ├── ntimage.h
│ │ ├── ntintsafe.h
│ │ ├── ntioapi.h
│ │ ├── ntkeapi.h
│ │ ├── ntldr.h
│ │ ├── ntlpcapi.h
│ │ ├── ntmisc.h
│ │ ├── ntmmapi.h
│ │ ├── ntnls.h
│ │ ├── ntobapi.h
│ │ ├── ntpebteb.h
│ │ ├── ntpfapi.h
│ │ ├── ntpnpapi.h
│ │ ├── ntpoapi.h
│ │ ├── ntpsapi.h
│ │ ├── ntregapi.h
│ │ ├── ntrtl.h
│ │ ├── ntsam.h
│ │ ├── ntseapi.h
│ │ ├── ntsmss.h
│ │ ├── ntstrsafe.h
│ │ ├── ntsxs.h
│ │ ├── nttmapi.h
│ │ ├── nttp.h
│ │ ├── ntuser.h
│ │ ├── ntwmi.h
│ │ ├── ntwow64.h
│ │ ├── ntxcapi.h
│ │ ├── ntzwapi.h
│ │ ├── phnt.h
│ │ ├── phnt_ntdef.h
│ │ ├── phnt_windows.h
│ │ ├── smbios.h
│ │ ├── subprocesstag.h
│ │ ├── usermgr.h
│ │ └── winsta.h
│ ├── meson.build
│ └── zw_options.txt
├── plugins/
│ ├── CMakeLists.txt
│ ├── Directory.Build.props
│ ├── DotNetTools/
│ │ ├── CHANGELOG.txt
│ │ ├── CMakeLists.txt
│ │ ├── DotNetTools.rc
│ │ ├── DotNetTools.vcxproj
│ │ ├── DotNetTools.vcxproj.filters
│ │ ├── asmpage.c
│ │ ├── clr/
│ │ │ ├── LICENSE.TXT
│ │ │ ├── clrdata.h
│ │ │ ├── cor.h
│ │ │ ├── corsym.h
│ │ │ ├── dacprivate.h
│ │ │ ├── dbgappdomain.h
│ │ │ ├── ipcenums.h
│ │ │ ├── ipcheader.h
│ │ │ ├── ipcshared.h
│ │ │ ├── perfcounterdefs.h
│ │ │ ├── sospriv.h
│ │ │ └── xclrdata.h
│ │ ├── clretw.h
│ │ ├── clrsup.c
│ │ ├── clrsup.h
│ │ ├── counters.c
│ │ ├── dn.h
│ │ ├── main.c
│ │ ├── perfpage.c
│ │ ├── resource.h
│ │ ├── stackext.c
│ │ ├── svcext.c
│ │ ├── svcext.h
│ │ ├── treeext.c
│ │ └── version.rc
│ ├── ExtendedNotifications/
│ │ ├── CHANGELOG.txt
│ │ ├── CMakeLists.txt
│ │ ├── ExtendedNotifications.rc
│ │ ├── ExtendedNotifications.vcxproj
│ │ ├── ExtendedNotifications.vcxproj.filters
│ │ ├── extnoti.h
│ │ ├── filelog.c
│ │ ├── main.c
│ │ ├── resource.h
│ │ └── version.rc
│ ├── ExtendedServices/
│ │ ├── CHANGELOG.txt
│ │ ├── CMakeLists.txt
│ │ ├── ExtendedServices.rc
│ │ ├── ExtendedServices.vcxproj
│ │ ├── ExtendedServices.vcxproj.filters
│ │ ├── depend.c
│ │ ├── extsrv.h
│ │ ├── main.c
│ │ ├── other.c
│ │ ├── recovery.c
│ │ ├── resource.h
│ │ ├── srvprgrs.c
│ │ ├── svcpkg.c
│ │ ├── svcpnp.c
│ │ ├── trigger.c
│ │ ├── triggpg.c
│ │ └── version.rc
│ ├── ExtendedTools/
│ │ ├── CHANGELOG.txt
│ │ ├── CMakeLists.txt
│ │ ├── Efi/
│ │ │ ├── EfiDevicePath.h
│ │ │ └── EfiTypes.h
│ │ ├── ExtendedTools.rc
│ │ ├── ExtendedTools.vcxproj
│ │ ├── ExtendedTools.vcxproj.filters
│ │ ├── PresentMon/
│ │ │ ├── ETW/
│ │ │ │ ├── Microsoft_Windows_D3D9.h
│ │ │ │ ├── Microsoft_Windows_DXGI.h
│ │ │ │ ├── Microsoft_Windows_Dwm_Core.h
│ │ │ │ ├── Microsoft_Windows_DxgKrnl.h
│ │ │ │ ├── Microsoft_Windows_EventMetadata.h
│ │ │ │ └── Microsoft_Windows_Win32k.h
│ │ │ ├── LICENSE.txt
│ │ │ ├── PresentMon.cpp
│ │ │ ├── PresentMon.hpp
│ │ │ ├── PresentMonTraceConsumer.cpp
│ │ │ ├── PresentMonTraceConsumer.hpp
│ │ │ ├── TraceConsumer.cpp
│ │ │ ├── TraceConsumer.hpp
│ │ │ └── TraceSession.cpp
│ │ ├── counters.c
│ │ ├── d3dkmt/
│ │ │ ├── LICENSE
│ │ │ ├── d3dkmdt.h
│ │ │ ├── d3dkmthk.h
│ │ │ └── d3dukmdt.h
│ │ ├── disktab.c
│ │ ├── disktabp.h
│ │ ├── efi_guid_list.h
│ │ ├── etwdisk.c
│ │ ├── etwmini.c
│ │ ├── etwmini.h
│ │ ├── etwmon.c
│ │ ├── etwmon.h
│ │ ├── etwprprp.c
│ │ ├── etwstat.c
│ │ ├── etwsys.c
│ │ ├── etwsys.h
│ │ ├── extension/
│ │ │ └── plugin.h
│ │ ├── exttools.h
│ │ ├── firmware.c
│ │ ├── firmware_editor.c
│ │ ├── framemon.cpp
│ │ ├── framemon.h
│ │ ├── frameprp.c
│ │ ├── fwmon.c
│ │ ├── fwtab.c
│ │ ├── gpudetails.c
│ │ ├── gpumini.c
│ │ ├── gpumini.h
│ │ ├── gpumon.c
│ │ ├── gpumon.h
│ │ ├── gpunodes.c
│ │ ├── gpuprprp.c
│ │ ├── gpusys.c
│ │ ├── gpusys.h
│ │ ├── iconext.c
│ │ ├── main.c
│ │ ├── modsrv.c
│ │ ├── namedpipes.c
│ │ ├── npudetails.c
│ │ ├── npumini.c
│ │ ├── npumini.h
│ │ ├── npumon.c
│ │ ├── npumon.h
│ │ ├── npunodes.c
│ │ ├── npuprprp.c
│ │ ├── npusys.c
│ │ ├── npusys.h
│ │ ├── objmgr.c
│ │ ├── objprp.c
│ │ ├── options.c
│ │ ├── pooldb.c
│ │ ├── pooldialog.c
│ │ ├── pooldialogbig.c
│ │ ├── poolmon.h
│ │ ├── pooltree.c
│ │ ├── reparse.c
│ │ ├── resource.h
│ │ ├── smbios.c
│ │ ├── svcext.c
│ │ ├── thrdact.c
│ │ ├── tpm.c
│ │ ├── tpm.h
│ │ ├── tpm_editor.c
│ │ ├── treeext.c
│ │ ├── unldll.c
│ │ ├── utils.c
│ │ ├── version.rc
│ │ ├── waitchain.c
│ │ └── wswatch.c
│ ├── HardwareDevices/
│ │ ├── CHANGELOG.txt
│ │ ├── CMakeLists.txt
│ │ ├── HardwareDevices.rc
│ │ ├── HardwareDevices.vcxproj
│ │ ├── HardwareDevices.vcxproj.filters
│ │ ├── adapter.c
│ │ ├── d3dkmt/
│ │ │ ├── LICENSE
│ │ │ ├── d3dkmdt.h
│ │ │ ├── d3dkmthk.h
│ │ │ └── d3dukmdt.h
│ │ ├── deviceprops.c
│ │ ├── devices.h
│ │ ├── devicetree.c
│ │ ├── disk.c
│ │ ├── diskdetails.c
│ │ ├── diskgraph.c
│ │ ├── disknotify.c
│ │ ├── diskoptions.c
│ │ ├── fmifs.h
│ │ ├── gpu.c
│ │ ├── gpudetails.c
│ │ ├── gpugraph.c
│ │ ├── gpunodes.c
│ │ ├── gpuoptions.c
│ │ ├── graphics.c
│ │ ├── main.c
│ │ ├── ndis.c
│ │ ├── ndiswlan.c
│ │ ├── ndiswlan.h
│ │ ├── netdetails.c
│ │ ├── netgraph.c
│ │ ├── netoptions.c
│ │ ├── power.c
│ │ ├── powergraph.c
│ │ ├── poweroptions.c
│ │ ├── prpsh.c
│ │ ├── prpsh.h
│ │ ├── resource.h
│ │ ├── storage.c
│ │ └── version.rc
│ ├── NetworkTools/
│ │ ├── CMakeLists.txt
│ │ ├── NetworkTools.rc
│ │ ├── NetworkTools.vcxproj
│ │ ├── NetworkTools.vcxproj.filters
│ │ ├── country.c
│ │ ├── main.c
│ │ ├── nettools.h
│ │ ├── options.c
│ │ ├── pages.c
│ │ ├── ping.c
│ │ ├── ports.c
│ │ ├── resource.h
│ │ ├── resources/
│ │ │ └── licence.txt
│ │ ├── tracert.c
│ │ ├── tracert.h
│ │ ├── tracetree.c
│ │ ├── update.c
│ │ ├── version.rc
│ │ └── whois.c
│ ├── OnlineChecks/
│ │ ├── CHANGELOG.txt
│ │ ├── CMakeLists.txt
│ │ ├── OnlineChecks.rc
│ │ ├── OnlineChecks.vcxproj
│ │ ├── OnlineChecks.vcxproj.filters
│ │ ├── api.c
│ │ ├── main.c
│ │ ├── onlnchk.h
│ │ ├── options.c
│ │ ├── page1.c
│ │ ├── page2.c
│ │ ├── page3.c
│ │ ├── page4.c
│ │ ├── resource.h
│ │ ├── scan.c
│ │ ├── upload.c
│ │ └── version.rc
│ ├── Plugins.props
│ ├── Plugins.sln
│ ├── Plugins.slnx
│ ├── ToolStatus/
│ │ ├── CHANGELOG.txt
│ │ ├── CMakeLists.txt
│ │ ├── ToolStatus.rc
│ │ ├── ToolStatus.vcxproj
│ │ ├── ToolStatus.vcxproj.filters
│ │ ├── customizesb.c
│ │ ├── customizetb.c
│ │ ├── filter.c
│ │ ├── find.c
│ │ ├── graph.c
│ │ ├── main.c
│ │ ├── options.c
│ │ ├── plugin.c
│ │ ├── resource.h
│ │ ├── statusbar.c
│ │ ├── taskbar.c
│ │ ├── toolbar.c
│ │ ├── toolbarsup.c
│ │ ├── toolstatus.h
│ │ └── version.rc
│ ├── Updater/
│ │ ├── CHANGELOG.txt
│ │ ├── CMakeLists.txt
│ │ ├── Updater.rc
│ │ ├── Updater.vcxproj
│ │ ├── Updater.vcxproj.filters
│ │ ├── main.c
│ │ ├── options.c
│ │ ├── page1.c
│ │ ├── page2.c
│ │ ├── page3.c
│ │ ├── page4.c
│ │ ├── page5.c
│ │ ├── resource.h
│ │ ├── updater.c
│ │ ├── updater.h
│ │ ├── verify.c
│ │ └── version.rc
│ ├── UserNotes/
│ │ ├── CHANGELOG.txt
│ │ ├── CMakeLists.txt
│ │ ├── UserNotes.rc
│ │ ├── UserNotes.vcxproj
│ │ ├── UserNotes.vcxproj.filters
│ │ ├── db.c
│ │ ├── db.h
│ │ ├── main.c
│ │ ├── options.c
│ │ ├── prpcmpage.c
│ │ ├── resource.h
│ │ ├── usernotes.h
│ │ └── version.rc
│ ├── WindowExplorer/
│ │ ├── CHANGELOG.txt
│ │ ├── CMakeLists.txt
│ │ ├── WindowExplorer.rc
│ │ ├── WindowExplorer.vcxproj
│ │ ├── WindowExplorer.vcxproj.filters
│ │ ├── main.c
│ │ ├── prpsh.c
│ │ ├── prpsh.h
│ │ ├── resource.h
│ │ ├── utils.c
│ │ ├── version.rc
│ │ ├── wnddlg.c
│ │ ├── wndexp.h
│ │ ├── wndprp.c
│ │ ├── wndtree.c
│ │ └── wndtree.h
│ ├── include/
│ │ ├── commonutil.h
│ │ ├── networktoolsintf.h
│ │ └── toolstatusintf.h
│ └── readme.txt
└── tools/
├── CMakeLists.txt
├── CompileCommandsJson/
│ ├── CompileCommandsJson.cs
│ ├── CompileCommandsJson.csproj
│ ├── CompileCommandsJson.sln
│ ├── CompileCommandsJson.slnx
│ └── Properties/
│ └── PublishProfiles/
│ ├── amd64.pubxml
│ └── arm64.pubxml
├── CustomBuildTool/
│ ├── AzureClient.cs
│ ├── AzureSignTool/
│ │ ├── AuthenticodeKeyVaultSigner.cs
│ │ ├── ECDsaKeyVault.cs
│ │ ├── ECDsaKeyVaultExtensions.cs
│ │ ├── KeyVaultConfigurationDiscoverer.cs
│ │ ├── KeyVaultContext.cs
│ │ ├── LICENSE.txt
│ │ ├── MemoryCertificateStore.cs
│ │ ├── NativeMethods.cs
│ │ ├── RSAKeyVault.cs
│ │ ├── RSAKeyVaultExtensions.cs
│ │ └── TimeStampConfiguration.cs
│ ├── Build.cs
│ ├── BuildAzure.cs
│ ├── BuildConfig.cs
│ ├── BuildDevOps.cs
│ ├── BuildGithub.cs
│ ├── BuildHttpClient.cs
│ ├── BuildSourceForge.cs
│ ├── BuildToolsId.cs
│ ├── BuildVerify.cs
│ ├── BuildVirusTotal.cs
│ ├── BuildVisualStudio.cs
│ ├── CustomBuildTool.csproj
│ ├── CustomBuildTool.sln
│ ├── CustomBuildTool.slnx
│ ├── DynData.cs
│ ├── GlobalSuppressions.cs
│ ├── HeaderGen.cs
│ ├── NativeImports.txt
│ ├── NativeMethods.json
│ ├── NativeMethods.txt
│ ├── Program.cs
│ ├── Properties/
│ │ └── PublishProfiles/
│ │ ├── amd64.pubxml
│ │ └── arm64.pubxml
│ ├── Utils.cs
│ ├── Win32.cs
│ ├── Zip.cs
│ └── app.manifest
├── CustomCmdTool/
│ ├── CustomCmdTool.sln
│ ├── CustomCmdTool.slnx
│ ├── CustomCmdTool.vcxproj
│ ├── CustomCmdTool.vcxproj.filters
│ ├── app.manifest
│ └── main.c
├── CustomDebugTool/
│ └── CustomDebugTool.slnx
├── CustomSetupTool/
│ ├── CustomSetupTool.sln
│ ├── CustomSetupTool.slnx
│ ├── CustomSetupTool.vcxproj
│ ├── CustomSetupTool.vcxproj.filters
│ ├── app.manifest
│ ├── extract.c
│ ├── install.c
│ ├── main.c
│ ├── resource.h
│ ├── resource.rc
│ ├── setup.h
│ ├── startpage.c
│ ├── uninstall.c
│ ├── update.c
│ ├── util.c
│ └── version.rc
├── CustomSignTool/
│ ├── CustomSignTool.manifest
│ ├── CustomSignTool.sln
│ ├── CustomSignTool.slnx
│ ├── CustomSignTool.vcxproj
│ ├── CustomSignTool.vcxproj.filters
│ ├── main.c
│ ├── resource.h
│ └── resource.rc
├── CustomStartTool/
│ ├── CustomStartTool.sln
│ ├── CustomStartTool.slnx
│ ├── CustomStartTool.vcxproj
│ ├── CustomStartTool.vcxproj.filters
│ ├── app.manifest
│ └── main.c
├── Directory.Build.props
├── GenerateZw/
│ ├── GenerateZw.csproj
│ ├── GenerateZw.sln
│ ├── GenerateZw.slnx
│ ├── Program.cs
│ ├── Properties/
│ │ └── PublishProfiles/
│ │ └── 64Bit.pubxml
│ └── ZwGen.cs
├── fixlib/
│ ├── fixlib.c
│ ├── fixlib.sln
│ ├── fixlib.slnx
│ ├── fixlib.vcxproj
│ └── fixlib.vcxproj.filters
├── fixlib_bcd/
│ ├── lib32/
│ │ └── bcd.def
│ ├── lib64/
│ │ └── bcd.def
│ └── make_bcd_lib.cmd
├── msix/
│ ├── PackageManifest64.msix.xml
│ ├── PackageTemplate.appinstaller
│ ├── PackageTemplate.msix.xml
│ └── build.cmd
├── peview/
│ ├── CMakeLists.txt
│ ├── appmanifest.c
│ ├── attributes.c
│ ├── cfgprp.c
│ ├── chcol.c
│ ├── clrprp.c
│ ├── clrprptables.c
│ ├── clrtableimportprp.c
│ ├── clrtableimports.cpp
│ ├── colmgr.c
│ ├── colmgr.h
│ ├── debugprp.c
│ ├── delayhook.c
│ ├── disimp.c
│ ├── ehcontprp.c
│ ├── exlfdynamic.c
│ ├── exlfexports.c
│ ├── exlfimports.c
│ ├── exlfprp.c
│ ├── expprp.c
│ ├── hashprp.c
│ ├── impprp.c
│ ├── include/
│ │ ├── peview.h
│ │ └── prpsh.h
│ ├── layout.c
│ ├── ldprp.c
│ ├── libprp.c
│ ├── links.c
│ ├── main.c
│ ├── mappings.c
│ ├── metahost/
│ │ ├── CorError.h
│ │ ├── CorHdr.h
│ │ ├── cor.h
│ │ ├── gchost.h
│ │ ├── ivalidator.h
│ │ ├── ivehandler.h
│ │ ├── metahost.h
│ │ └── mscoree.h
│ ├── misc.c
│ ├── options.c
│ ├── pdb.c
│ ├── pdbprp.c
│ ├── pechpeprp.c
│ ├── pedirprp.c
│ ├── pedynrelocprp.c
│ ├── peexceptprp.c
│ ├── peheaderprp.c
│ ├── pemuiprp.c
│ ├── pepogoprp.c
│ ├── peprp.c
│ ├── peprpwnd.c
│ ├── perelocprp.c
│ ├── pesectionprp.c
│ ├── peview.manifest
│ ├── peview.rc
│ ├── peview.vcxproj
│ ├── peview.vcxproj.filters
│ ├── previewprp.c
│ ├── processes.c
│ ├── propstore.c
│ ├── prpsh.c
│ ├── resource.h
│ ├── resprp.c
│ ├── richprp.c
│ ├── searchbox.c
│ ├── secprp.c
│ ├── settings.c
│ ├── streams.c
│ ├── strings.c
│ ├── tlsprp.c
│ ├── version.rc
│ ├── versioninfoprp.c
│ └── volatileprp.c
├── tests/
│ └── phlib-test/
│ ├── main.c
│ ├── phlib-test.manifest
│ ├── phlib-test.sln
│ ├── phlib-test.slnx
│ ├── phlib-test.vcxproj
│ ├── phlib-test.vcxproj.filters
│ ├── t_avltree.c
│ ├── t_basesup.c
│ ├── t_format.c
│ ├── t_util.c
│ └── tests.h
├── thirdparty/
│ ├── CMakeLists.txt
│ ├── detours/
│ │ ├── LICENSE.txt
│ │ ├── detours.cpp
│ │ ├── detours.h
│ │ ├── disasm.cpp
│ │ ├── disolarm.cpp
│ │ ├── disolarm64.cpp
│ │ ├── disolia64.cpp
│ │ ├── disolx64.cpp
│ │ ├── disolx86.cpp
│ │ └── modules.cpp
│ ├── jsonc/
│ │ ├── COPYING
│ │ ├── ChangeLog
│ │ ├── arraylist.c
│ │ ├── arraylist.h
│ │ ├── config.h
│ │ ├── debug.c
│ │ ├── debug.h
│ │ ├── json.h
│ │ ├── json_c_version.c
│ │ ├── json_c_version.h
│ │ ├── json_config.h
│ │ ├── json_inttypes.h
│ │ ├── json_object.c
│ │ ├── json_object.h
│ │ ├── json_object_iterator.c
│ │ ├── json_object_iterator.h
│ │ ├── json_object_private.h
│ │ ├── json_pointer.c
│ │ ├── json_pointer.h
│ │ ├── json_pointer_private.h
│ │ ├── json_tokener.c
│ │ ├── json_tokener.h
│ │ ├── json_types.h
│ │ ├── json_util.c
│ │ ├── json_util.h
│ │ ├── json_visit.c
│ │ ├── json_visit.h
│ │ ├── libjson.c
│ │ ├── linkhash.c
│ │ ├── linkhash.h
│ │ ├── math_compat.h
│ │ ├── printbuf.c
│ │ ├── printbuf.h
│ │ ├── random_seed.c
│ │ ├── random_seed.h
│ │ ├── snprintf_compat.h
│ │ ├── strdup_compat.h
│ │ ├── strerror_override.c
│ │ ├── strerror_override.h
│ │ └── vasprintf_compat.h
│ ├── maxminddb/
│ │ ├── LICENSE
│ │ ├── data-pool.c
│ │ ├── data-pool.h
│ │ ├── maxminddb-compat-util.h
│ │ ├── maxminddb.c
│ │ ├── maxminddb.h
│ │ └── maxminddb_config.h
│ ├── md5/
│ │ ├── LICENSE
│ │ ├── md5.c
│ │ └── md5.h
│ ├── miniz/
│ │ ├── LICENSE
│ │ ├── miniz.c
│ │ ├── miniz.h
│ │ ├── miniz_common.h
│ │ ├── miniz_tdef.c
│ │ ├── miniz_tdef.h
│ │ ├── miniz_tinfl.c
│ │ ├── miniz_tinfl.h
│ │ ├── miniz_zip.c
│ │ └── miniz_zip.h
│ ├── mxml/
│ │ ├── LICENSE
│ │ ├── config.h
│ │ ├── mxml-attr.c
│ │ ├── mxml-file.c
│ │ ├── mxml-get.c
│ │ ├── mxml-index.c
│ │ ├── mxml-node.c
│ │ ├── mxml-options.c
│ │ ├── mxml-private.c
│ │ ├── mxml-private.h
│ │ ├── mxml-search.c
│ │ ├── mxml-set.c
│ │ └── mxml.h
│ ├── pcre/
│ │ ├── LICENCE
│ │ ├── README
│ │ ├── config.h
│ │ ├── pcre2.h
│ │ ├── pcre2_auto_possess.c
│ │ ├── pcre2_chartables.c
│ │ ├── pcre2_chkdint.c
│ │ ├── pcre2_compile.c
│ │ ├── pcre2_compile.h
│ │ ├── pcre2_compile_class.c
│ │ ├── pcre2_config.c
│ │ ├── pcre2_context.c
│ │ ├── pcre2_convert.c
│ │ ├── pcre2_dfa_match.c
│ │ ├── pcre2_dftables.c
│ │ ├── pcre2_error.c
│ │ ├── pcre2_extuni.c
│ │ ├── pcre2_find_bracket.c
│ │ ├── pcre2_fuzzsupport.c
│ │ ├── pcre2_internal.h
│ │ ├── pcre2_intmodedep.h
│ │ ├── pcre2_jit_char_inc.h
│ │ ├── pcre2_jit_compile.c
│ │ ├── pcre2_jit_match.c
│ │ ├── pcre2_jit_misc.c
│ │ ├── pcre2_jit_neon_inc.h
│ │ ├── pcre2_jit_simd_inc.h
│ │ ├── pcre2_jit_test.c
│ │ ├── pcre2_maketables.c
│ │ ├── pcre2_match.c
│ │ ├── pcre2_match_data.c
│ │ ├── pcre2_newline.c
│ │ ├── pcre2_ord2utf.c
│ │ ├── pcre2_pattern_info.c
│ │ ├── pcre2_printint.c
│ │ ├── pcre2_script_run.c
│ │ ├── pcre2_serialize.c
│ │ ├── pcre2_string_utils.c
│ │ ├── pcre2_study.c
│ │ ├── pcre2_substitute.c
│ │ ├── pcre2_substring.c
│ │ ├── pcre2_tables.c
│ │ ├── pcre2_ucd.c
│ │ ├── pcre2_ucp.h
│ │ ├── pcre2_ucptables.c
│ │ ├── pcre2_util.h
│ │ ├── pcre2_valid_utf.c
│ │ ├── pcre2_xclass.c
│ │ ├── pcre2posix.c
│ │ └── pcre2posix.h
│ ├── sha/
│ │ ├── LICENSE
│ │ ├── sha.c
│ │ └── sha.h
│ ├── sha256/
│ │ ├── LICENSE
│ │ ├── sha256.c
│ │ └── sha256.h
│ ├── ssdeep/
│ │ ├── COPYING.txt
│ │ ├── fuzzy.c
│ │ └── fuzzy.h
│ ├── thirdparty.sln
│ ├── thirdparty.slnx
│ ├── thirdparty.vcxproj
│ ├── thirdparty.vcxproj.filters
│ ├── tlsh/
│ │ ├── LICENSE
│ │ ├── tlsh.cpp
│ │ ├── tlsh.h
│ │ ├── tlsh_impl.cpp
│ │ ├── tlsh_impl.h
│ │ ├── tlsh_util.cpp
│ │ ├── tlsh_util.h
│ │ ├── tlsh_win_version.h
│ │ ├── tlsh_wrapper.cpp
│ │ └── tlsh_wrapper.h
│ ├── winsdk/
│ │ ├── cvconst.h
│ │ ├── dia2.h
│ │ └── dia3.h
│ ├── xxhash/
│ │ ├── CHANGELOG
│ │ ├── LICENSE
│ │ ├── xxhash.c
│ │ ├── xxhash.h
│ │ ├── xxhashwrapper.c
│ │ └── xxhashwrapper.h
│ └── zydis/
│ ├── LICENSE
│ ├── Zydis.c
│ └── Zydis.h
└── versioning/
├── build_version.cmd
└── version.rc
================================================
FILE CONTENTS
================================================
================================================
FILE: .editorconfig
================================================
root = true
[*.{c,c++,cc,cpp,cppm,cxx,h,h++,hh,hpp,hxx,inl,ipp,ixx,tlh,tli}]
charset = utf-8
end_of_line = crlf
indent_style = space
indent_size = 4
trim_trailing_whitespace = true
insert_final_newline = true
================================================
FILE: .gitattributes
================================================
# Auto detect text files
* text=auto working-tree-encoding=UTF-8
# Custom for Visual Studio
*.cs diff=csharp
*.sln merge=union
*.slnx merge=union
*.csproj merge=union
*.vbproj merge=union
*.fsproj merge=union
*.dbproj merge=union
# Project files
*.appinstaller text
*.bat text
*.c text
*.cs text
*.cpp text
*.config text
*.cmake text
*.cmd text
*.csproj text
*.def text
*.ddf text
*.editorconfig text
*.gitattributes text
*.gitignore text
*.h text
*.hpp text
*.inc text
*.inf text
*.json text
*.map text
*.manifest text
*.mc text
*.md text
*.natvis text
*.ps1 text
*.props text
*.pubxml text
*.resx text
*.rc text
*.sh text eol=lf
*.sln text
*.slnx text
*.txt text
*.vsconfig text
*.vcxproj text
*.filters text
*.xml text
*.yml text
*.yaml text
**AUTHORS text
**COPYING text
**CHANGELOG text
**CODEOWNERS text
**LICENSE text
**LICENCE text
**README text
*.bmp binary
*.dll binary
*.exe binary
*.ico binary
*.lib binary
*.pdb binary
*.png binary
*.s binary
*.sys binary
*.zip binary
# svg standard require LF
*.svg text eol=lf
# Standard to msysgit
*.doc diff=astextplain
*.DOC diff=astextplain
*.docx diff=astextplain
*.DOCX diff=astextplain
*.dot diff=astextplain
*.DOT diff=astextplain
*.pdf diff=astextplain
*.PDF diff=astextplain
*.rtf diff=astextplain
*.RTF diff=astextplain
# Ignore files during repository export
.gitattributes export-ignore
.gitignore export-ignore
.gitmodules export-ignore
.github export-ignore
================================================
FILE: .github/CODEOWNERS
================================================
# Below is a list of System Informer team members' GitHub handles who are
# suggested reviewers for contributions to this repository.
#
# These names are just suggestions. It is fine to have your changes
# reviewed by someone else.
#
# Use git ls-files '' without a / prefix to see the list of matching files.
/CODEOWNERS @dmex @jxy-s
/KSystemInformer @jxy-s
/SystemInformer @dmex @jxy-s
/build @dmex @jxy-s
/kphlib @jxy-s
/phlib @dmex @jxy-s
/phnt @dmex @jxy-s
/plugins @dmex @jxy-s
/tools @dmex
/tools/CustomBuildTool/DynData.cs @jxy-s
.github/workflows @dmex @jxy-s
================================================
FILE: .github/FUNDING.yml
================================================
# These are supported funding model platforms
github: [dmex, jxy-s]
================================================
FILE: .github/ISSUE_TEMPLATE/bug_report.yml
================================================
name: 'Bug report'
description: Report bugs or other issues
body:
- type: textarea
attributes:
label: Brief description of your issue
placeholder: Briefly describe your issue here.
validations:
required: true
- type: textarea
attributes:
label: Steps to reproduce (optional)
placeholder: How to reproduce the issue?
validations:
required: false
- type: textarea
attributes:
label: Expected behavior (optional)
placeholder: What did you expect to happen?
validations:
required: false
- type: textarea
attributes:
label: Actual behavior (optional)
placeholder: What is currently happening?
validations:
required: false
- type: textarea
attributes:
label: Environment (optional)
placeholder: |
System Informer version
Windows version
Any other information?
render: shell
validations:
required: false
================================================
FILE: .github/ISSUE_TEMPLATE/config.yml
================================================
blank_issues_enabled: true
contact_links:
- name: General Questions
url: https://github.com/winsiderss/systeminformer/discussions/new
about: Have a question? Start a new discussion.
- name: Review open issues
url: https://github.com/winsiderss/systeminformer/issues
about: Check existing bug reports for already reported issues.
- name: Create a blank issue
url: https://github.com/winsiderss/systeminformer/issues/new
about: Create a blank issue for anything else.
================================================
FILE: .github/ISSUE_TEMPLATE/feature_request.yml
================================================
name: "Feature request"
description: Suggest features, modifications or ideas
type: "Feature"
labels: ["enhancement"]
body:
- type: textarea
attributes:
label: Description of the feature, modification, idea or suggestion
validations:
required: true
- type: textarea
attributes:
label: Proposed implementation details (optional)
placeholder: Suggest how to implement the feature.
validations:
required: false
================================================
FILE: .github/workflows/cla.yml
================================================
name: "CLA Assistant"
on:
issue_comment:
types: [created]
pull_request_target:
types: [opened,closed,synchronize]
permissions:
actions: write
checks: read
contents: read
issues: read
discussions: read
pull-requests: write
statuses: read
jobs:
CLAssistant:
if: ${{ github.event_name == 'pull_request_target' || github.event.issue.pull_request }}
runs-on: ubuntu-latest
steps:
- name: "CLA Assistant"
if: (github.event.comment.body == 'recheck' || github.event.comment.body == 'I have read the CLA Document and I hereby sign the CLA') || github.event_name == 'pull_request_target'
uses: cla-assistant/github-action@dbc1c64d82d3aad5072007a41fff2828ae6d23ec
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
PERSONAL_ACCESS_TOKEN : ${{ secrets.CLA_ASSISTANT_TOKEN }}
with:
path-to-signatures: 'systeminformer/cla-signatures.json'
path-to-document: 'https://github.com/winsiderss/systeminformer/blob/master/CLA.md'
branch: 'main'
remote-organization-name: 'winsiderss'
remote-repository-name: 'winsiderss-cla'
lock-pullrequest-aftermerge: false
================================================
FILE: .github/workflows/cmake.yml
================================================
name: 'cmake-continuous-integration'
on:
workflow_dispatch:
push:
branches: ['master']
pull_request:
branches: ['master']
jobs:
build:
permissions:
contents: read
strategy:
fail-fast: false
matrix:
os:
- windows-latest
toolchain:
- msvc-amd64
- msvc-arm64
- msvc-x86
- clang-msvc-amd64
- clang-msvc-arm64
- clang-msvc-x86
configuration:
- Debug
- Release
generator:
- Ninja
runs-on: ${{ matrix.os }}
name: ${{ matrix.configuration }} ${{ matrix.toolchain }}
steps:
- name: Checkout repository
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # 6.0.1
- name: NuGet Cache
uses: actions/cache@a7833574556fa59680c1b7cb190c1735db73ebf0 # 5.0.0
with:
path: packages
key: nuget-${{ runner.os }}-${{ hashFiles('**/packages.config') }}
restore-keys: nuget-${{ runner.os }}-
- name: Nuget Restore
shell: cmd
working-directory: ${{github.workspace}}
run: nuget restore .\packages.config -PackagesDirectory .\packages\
- name: Build Tools
shell: cmd
working-directory: ${{github.workspace}}
run: build\build_tools.cmd
- name: Generate
shell: cmd
working-directory: ${{github.workspace}}
run: build\build_cmake.cmd "${{ matrix.generator }}" ${{ matrix.configuration }} ${{ matrix.toolchain }} generate
- name: Build
shell: cmd
working-directory: ${{github.workspace}}
run: build\build_cmake.cmd "${{ matrix.generator }}" ${{ matrix.configuration }} ${{ matrix.toolchain }} build
================================================
FILE: .github/workflows/msbuild.yml
================================================
name: 'continuous-integration'
on:
workflow_dispatch:
push:
branches: ['master']
pull_request:
branches: ['master']
jobs:
build:
permissions:
contents: read
strategy:
fail-fast: false
matrix:
os: ['windows-latest']
runs-on: ${{ matrix.os }}
steps:
- name: Checkout repository
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # 6.0.1
- name: NuGet Cache
uses: actions/cache@a7833574556fa59680c1b7cb190c1735db73ebf0 # 5.0.0
with:
path: packages
key: nuget-${{ runner.os }}-${{ hashFiles('**/packages.config') }}
restore-keys: nuget-${{ runner.os }}-
- name: Nuget Restore
shell: cmd
working-directory: ${{github.workspace}}
run: nuget restore .\packages.config -PackagesDirectory .\packages\
- name: Build Tools
shell: cmd
working-directory: ${{github.workspace}}
run: build\build_init.cmd
- name: Build Solution
shell: cmd
working-directory: ${{github.workspace}}
run: build\build_debug.cmd
build-release:
permissions:
contents: read
strategy:
matrix:
os: ['windows-latest']
runs-on: ${{ matrix.os }}
steps:
- name: Checkout repository
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # 6.0.1
- name: NuGet Cache
uses: actions/cache@a7833574556fa59680c1b7cb190c1735db73ebf0 # 5.0.0
with:
path: packages
key: nuget-${{ runner.os }}-${{ hashFiles('**/packages.config') }}
restore-keys: nuget-${{ runner.os }}-
- name: Nuget Restore
shell: cmd
working-directory: ${{github.workspace}}
run: nuget restore .\packages.config -PackagesDirectory .\packages\
- name: Build Tools
shell: cmd
working-directory: ${{github.workspace}}
run: build\build_init.cmd
- name: Build Solution
shell: cmd
working-directory: ${{github.workspace}}
run: build\build_release.cmd
build-driver:
permissions:
contents: read
strategy:
matrix:
os: ['windows-latest']
runs-on: ${{ matrix.os }}
steps:
- name: Checkout repository
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # 6.0.1
- name: NuGet Cache
uses: actions/cache@a7833574556fa59680c1b7cb190c1735db73ebf0 # 5.0.0
with:
path: packages
key: nuget-${{ runner.os }}-${{ hashFiles('**/packages.config') }}
restore-keys: nuget-${{ runner.os }}-
- name: Nuget Restore
shell: cmd
working-directory: ${{github.workspace}}
run: nuget restore .\packages.config -PackagesDirectory .\packages\
- name: Build Tools
shell: cmd
working-directory: ${{github.workspace}}
run: build\build_tools.cmd
- name: Build Driver
shell: cmd
working-directory: ${{github.workspace}}
run: build\build_zdriver.cmd prefast
build-driver-release:
permissions:
contents: read
strategy:
matrix:
os: ['windows-latest']
runs-on: ${{ matrix.os }}
steps:
- name: Checkout repository
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # 6.0.1
- name: NuGet Cache
uses: actions/cache@a7833574556fa59680c1b7cb190c1735db73ebf0 # 5.0.0
with:
path: packages
key: nuget-${{ runner.os }}-${{ hashFiles('**/packages.config') }}
restore-keys: nuget-${{ runner.os }}-
- name: Nuget Restore
shell: cmd
working-directory: ${{github.workspace}}
run: nuget restore .\packages.config -PackagesDirectory .\packages\
- name: Build Tools
shell: cmd
working-directory: ${{github.workspace}}
run: build\build_tools.cmd
- name: Build Driver
shell: cmd
working-directory: ${{github.workspace}}
run: build\build_zdriver.cmd release
================================================
FILE: .github/workflows/scan.yml
================================================
name: 'CodeQL Analysis'
on:
workflow_dispatch:
schedule:
- cron: '0 0 * * 0'
jobs:
analyze_driver:
name: analyze-driver
runs-on: windows-2025
permissions:
security-events: write
actions: read
contents: read
strategy:
fail-fast: false
matrix:
language: ['cpp']
steps:
- name: Checkout repository
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # 6.0.1
with:
fetch-depth: 0
- name: Nuget Cache
uses: actions/cache@a7833574556fa59680c1b7cb190c1735db73ebf0 # 5.0.0
with:
path: packages
key: nuget-${{ runner.os }}-${{ hashFiles('**/packages.config') }}
restore-keys: nuget-${{ runner.os }}-
- name: Nuget Restore
shell: cmd
run: nuget restore .\packages.config -PackagesDirectory .\packages\
- name: Build Tools
shell: cmd
run: build\build_tools.cmd
- name: Initialize CodeQL
uses: github/codeql-action/init@cf1bb45a277cb3c205638b2cd5c984db1c46a412 # 4.31.7
with:
languages: ${{ matrix.language }}
- name: Build Driver
shell: cmd
run: build\build_zdriver.cmd
- name: Finalize CodeQL Database
run: |
$codeqlPath = (Get-ChildItem -Path "$env:RUNNER_TOOL_CACHE\CodeQL" -Recurse -Filter "codeql.exe" | Select-Object -First 1).FullName
& $codeqlPath database finalize "${{ runner.temp }}\codeql_databases\cpp"
- name: Download Windows Driver Query Packs
shell: pwsh
run: |
$codeqlPath = (Get-ChildItem -Path "$env:RUNNER_TOOL_CACHE\CodeQL" -Recurse -Filter "codeql.exe" | Select-Object -First 1).FullName
& $codeqlPath pack download microsoft/windows-drivers@1.8.2
& $codeqlPath pack download microsoft/cpp-queries@0.0.4
- name: Run Windows Driver Analysis
shell: pwsh
run: |
$codeqlPath = (Get-ChildItem -Path "$env:RUNNER_TOOL_CACHE\CodeQL" -Recurse -Filter "codeql.exe" | Select-Object -First 1).FullName
$sarifOut = Join-Path "${{ runner.temp }}" "driver-analysis.sarif"
& $codeqlPath database analyze "${{ runner.temp }}\codeql_databases\cpp" microsoft/windows-drivers --format=sarifv2.1.0 --output=$sarifOut
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
- name: Filter CodeQL Results
uses: advanced-security/filter-sarif@f3b8118a9349d88f7b1c0c488476411145b6270d # 1.0.1
with:
input: ${{ runner.temp }}\driver-analysis.sarif
output: ${{ runner.temp }}\driver-filtered.sarif
patterns: packages/**
- name: Upload CodeQL Results
uses: github/codeql-action/upload-sarif@cf1bb45a277cb3c205638b2cd5c984db1c46a412 # 4.31.7
with:
sarif_file: ${{ runner.temp }}\driver-filtered.sarif
analyze_library:
name: analyze-software
runs-on: windows-2025
permissions:
actions: read
contents: read
security-events: write
strategy:
fail-fast: false
matrix:
language: ['cpp']
steps:
- name: Checkout repository
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # 6.0.1
with:
fetch-depth: 0
- name: Nuget Cache
uses: actions/cache@a7833574556fa59680c1b7cb190c1735db73ebf0 # 5.0.0
with:
path: packages
key: nuget-${{ runner.os }}-${{ hashFiles('**/packages.config') }}
restore-keys: nuget-${{ runner.os }}-
- name: Nuget Restore
shell: cmd
run: nuget restore .\packages.config -PackagesDirectory .\packages\
- name: Build Init
shell: cmd
run: build\build_init.cmd
- name: Initialize CodeQL
uses: github/codeql-action/init@cf1bb45a277cb3c205638b2cd5c984db1c46a412 # 4.31.7
with:
languages: ${{ matrix.language }}
- name: Build Driver
shell: cmd
run: build\build_zdriver.cmd
- name: Build Release
shell: cmd
run: build\build_release.cmd
- name: Perform CodeQL analysis
uses: github/codeql-action/analyze@cf1bb45a277cb3c205638b2cd5c984db1c46a412 # 4.31.7
with:
category: '/language:${{ matrix.language }}'
output: sarif-results
upload: never
- name: Filter CodeQL Results
uses: advanced-security/filter-sarif@f3b8118a9349d88f7b1c0c488476411145b6270d # 1.0.1
with:
input: sarif-results/cpp.sarif
output: sarif-results/filtered.sarif
patterns: |
packages/**/*
tools/CustomBuildTool/generated/**/*
tools/CustomBuildTool/obj/**/*
tools/thirdparty/**/*
- name: Upload CodeQL Results
uses: github/codeql-action/upload-sarif@cf1bb45a277cb3c205638b2cd5c984db1c46a412 # 4.31.7
with:
sarif_file: sarif-results/filtered.sarif
analyze_csharp:
name: analyze-tools
runs-on: windows-2025
permissions:
actions: read
contents: read
security-events: write
strategy:
fail-fast: false
matrix:
language: ['csharp']
steps:
- name: Checkout repository
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # 6.0.1
with:
fetch-depth: 0
- name: Nuget Cache
uses: actions/cache@a7833574556fa59680c1b7cb190c1735db73ebf0 # 5.0.0
with:
path: packages
key: nuget-${{ runner.os }}-${{ hashFiles('**/packages.config') }}
restore-keys: nuget-${{ runner.os }}-
- name: Nuget Restore
shell: cmd
run: nuget restore .\packages.config -PackagesDirectory .\packages\
- name: Initialize CodeQL
uses: github/codeql-action/init@cf1bb45a277cb3c205638b2cd5c984db1c46a412 # 4.31.7
with:
languages: ${{ matrix.language }}
- name: Build Tools
shell: cmd
run: build\build_tools.cmd
- name: Perform CodeQL analysis
uses: github/codeql-action/analyze@cf1bb45a277cb3c205638b2cd5c984db1c46a412 # 4.31.7
with:
category: '/language:${{ matrix.language }}'
output: sarif-results
upload: never
- name: Filter CodeQL Results
uses: advanced-security/filter-sarif@f3b8118a9349d88f7b1c0c488476411145b6270d # 1.0.1
with:
input: sarif-results/csharp.sarif
output: sarif-results/filtered.sarif
patterns: |
packages/**/*
tools/CustomBuildTool/generated/**/*
tools/CustomBuildTool/obj/**/*
tools/thirdparty/**/*
- name: Upload CodeQL Results
uses: github/codeql-action/upload-sarif@cf1bb45a277cb3c205638b2cd5c984db1c46a412 # 4.31.7
with:
sarif_file: sarif-results/filtered.sarif
================================================
FILE: .gitignore
================================================
#################
## Visual Studio
#################
# User-specific files
*.suo
*.user
*.key
*.sig
*.orig
# Build results
*.exe
*.sys
*.dll
*.lib
*.appx
*.appxbundle
*.cer
*.ilk
*.meta
*.obj
*.pch
*.pdb
*.pfx
*.pgc
*.pgd
*.pvk
*.res
*.rsp
*.sbr
*.tlb
*.tli
*.tlh
*.tlog
*.tmp
*.vspscc
*_i.c
*_p.c
*.bak
# Visual C++ cache files
*.aps
*.idb
*.ncb
*.opendb
*.opensdf
*.sdf
*.db
# Visual Studio profiler
*.psess
*.vsp
# ReSharper is a .NET coding add-in
_ReSharper*
# Click-Once directory
publish
# Others
[Bb]in
[Oo]bj
*.Cache
~$*
# Backup & report files
_UpgradeReport_Files/
#Backup*/
UpgradeLog*.XML
# Windows image file caches
Thumbs.db
# Folder config file
Desktop.ini
# Visual Studio files
ipch/
.vs/
# VS Code files
.vscode/*
.vscode/settings.json
!.vscode/launch.json
!.vscode/tasks.json
!.vscode/extensions.json
*.code-workspace
# NuGet Packages
*.nupkg
**/packages/
**/vcpkg_installed/
# CMake files
**/.cmake/
**/CMakeCache.txt
**/CMakeFiles/
# JSON Compilation Database
**/compile_commands.json
**/.cache/
# AI instructions and caches
.github/copilot-instructions.md
.clinerules
memory-bank/
##########################
## Project specific rules
##########################
**/bin/
**/out/
build/output/
plugins-extra/
sdk/
target/
tools/thirdparty/vcpkg_installed/*
bin-cmake/
build-*/
!KSystemInformer/bin-signed/*/ksi.dll
!KSystemInformer/bin-signed/*/ksi.pdb
!KSystemInformer/bin-signed/*/systeminformer.sys
!KSystemInformer/bin-signed/*/systeminformer.pdb
!tools/CustomSignTool/bin/Release*/CustomSignTool.exe
!tools/fixlib/bin/Release/fixlib.exe
================================================
FILE: .vscode/launch.json
================================================
{
"version": "0.2.0",
"configurations": [
{
"name": "Launch SystemInformer (Debug64)",
"type": "cppvsdbg",
"request": "launch",
"program": "${workspaceFolder}/bin/Debug64/SystemInformer.exe",
"args": [],
"stopAtEntry": false,
"cwd": "${workspaceFolder}",
"environment": [],
"console": "integratedTerminal"
},
{
"name": "Attach to Process",
"type": "cppvsdbg",
"request": "attach",
"processId": "${command:pickProcess}"
}
]
}
================================================
FILE: .vscode/tasks.json
================================================
{
"version": "2.0.0",
"tasks": [
{
"label": "Build SystemInformer (Debug64)",
"type": "shell",
"command": "msbuild",
"args": [
"${workspaceFolder}/SystemInformer.sln",
"/p:Configuration=Debug",
"/p:Platform=x64",
"/t:Rebuild",
"/m"
],
"group": {
"kind": "build",
"isDefault": true
},
"problemMatcher": "$msCompile"
},
{
"label": "Build Plugins (Debug64)",
"type": "shell",
"command": "msbuild",
"args": [
"${workspaceFolder}/Plugins/Plugins.sln",
"/p:Configuration=Debug",
"/p:Platform=x64",
"/t:Rebuild",
"/m"
],
"group": {
"kind": "build",
"isDefault": true
},
"problemMatcher": "$msCompile"
},
{
"label": "Build ThirdParty (Debug64)",
"type": "shell",
"command": "msbuild",
"args": [
"${workspaceFolder}/tools/thirdparty/thirdparty.sln",
"/p:Configuration=Debug",
"/p:Platform=x64",
"/t:Rebuild",
"/m"
],
"group": {
"kind": "build",
"isDefault": true
},
"problemMatcher": "$msCompile"
},
{
"label": "Build SystemInformer (kernel driver) (Debug64)",
"type": "shell",
"command": "msbuild",
"args": [
"${workspaceFolder}/KSystemInformer/KSystemInformer.sln",
"/p:Configuration=Debug",
"/p:Platform=x64",
"/t:Rebuild",
"/m"
],
"options": {
"env": {
"KSI_NO_WPP": "1"
}
},
"group": {
"kind": "build",
"isDefault": true
},
"problemMatcher": "$msCompile"
},
{
"label": "Build Initialize (Debug64)",
"type": "shell",
"command": "${workspaceFolder}/build/build_init.cmd",
"group": {
"kind": "build",
"isDefault": true
},
},
{
"label": "Build devenv [CMD] (Debug64)",
"type": "shell",
"command": "${workspaceFolder}/build/build_devenv.cmd",
"group": { "kind": "build", }
},
{
"label": "Build prefast [CMD] (Debug64)",
"type": "shell",
"command": "${workspaceFolder}/build/build_zdriver.cmd",
"args": [
"debug",
"rebuild",
"prefast"
],
"options": {
"env": {
"KSI_NO_WPP": "1"
}
},
"group": {
"kind": "build",
}
}
]
}
================================================
FILE: .vsconfig
================================================
{
"version": "1.0",
"components": [
"Microsoft.Component.MSBuild",
"Microsoft.VisualStudio.Component.Git",
"Microsoft.VisualStudio.Component.VC.Redist.14.Latest",
"Microsoft.VisualStudio.Component.VC.Tools.x86.x64",
"Microsoft.VisualStudio.Component.VC.Tools.ARM64",
"Microsoft.VisualStudio.Component.VC.Runtimes.x86.x64.Spectre",
"Microsoft.VisualStudio.Component.VC.Runtimes.ARM64.Spectre",
"Microsoft.VisualStudio.Component.NuGet.BuildTools",
"Microsoft.VisualStudio.Workload.NativeDesktop"
],
"extensions": []
}
================================================
FILE: CHANGELOG.txt
================================================
Process Hacker
3.0
* HIGHLIGHTS:
* New Process Hacker setup.
* New process properties handle search.
* Added F11 hotkey for fullscreen System Information window.
* OTHER CHANGES:
* Updated Updater plugin:
* New design and layout.
* Updated WindowExplorer plugin:
* Added Windows process properties page.
* NOTE:
* Support for Windows XP and Vista has been dropped. For those platforms, use Process Hacker 2.38.
* This release has significant internal code changes. Please make sure all plugins are up-to-date.
2.39
* HIGHLIGHTS:
* Improved compatibility with security and anti-cheat software
* Added ability to edit process environment variables
* Fixed .NET process detection
* OTHER CHANGES:
* Improved tooltip information for dllhost.exe
* Removed Terminator
* Updated DotNetTools plugin:
* Fixed .NET assembly tab performance issues
* Added extra .NET memory counters to the .NET performance tab
* Added "Show sizes in bytes" checkbox to the .NET performance tab
* Added right-click menu to the .NET assembly tab
* Updated ExtendedTools plugin:
* Fixed "No process" disk event bug
* Updated HardwareDevices plugin:
* Fixed incorrect drive letters
* Fixed drive letter and panel clipping issue
2.38
* HIGHLIGHTS:
* Added labels to indicate the maximum data point in each I/O graph
* Graph grids now scale correctly when resized
* Improved high DPI scaling
* Added exploit mitigation policy information to process properties (Windows 8 and above)
* Added File modified time and File size columns for processes and modules
* Added Key modified time column for services
* Clicking a tray icon now shows the pop-up UI (useful for touch-enabled devices)
* The NetAdapters plugin has been renamed to HardwareDevices
* This plugin shows network adapter and disk drive graphs
* If you are manually upgrading, please delete NetAdapters.dll from the plugins folder
* Updated UserNotes plugin:
* Added "Collapse by default" option for processes
* OTHER CHANGES:
* Added "Start when I log on" option
* Added "Not responding" text to tray icon rich pop-up for programs that are hung
* Added right-click menu and double-click action for environment variables
* Added dialog box to show long command line strings
* Added Time stamp column for processes
* Added -sysinfo command line parameter for opening System Information at startup
* Added 32x32 icons for high DPI displays
* Digital signature verification is now performed with very low I/O priority
* Improved performance when handling a large number of threads, modules or handles
* The pop-up UI no longer displays when double-clicking the tray icon
* Fixed ASLR state being shown as N/A in process properties
* Fixed multi monitor window placement bug
* Fixed handle enumeration bug affecting processes with PID >= 65536
* Fixed Interrupts being missing from the max CPU usage history
* Updated ToolStatus plugin:
* Added 32x32 icons for high DPI displays
* Fixed status bar crash
* NOTE:
* This release has significant internal code changes. Please make sure all plugins are up-to-date.
2.37
* HIGHLIGHTS:
* Updated for Windows 10
* The "Include CPU (and other) usage of children in collapsed processes" option now aggregates memory and I/O statistics
* Added regex search to "Find Handles or DLLs"
* Added process exit codes to log
* Fixed crash that occurred under some conditions when processes terminated
* OTHER CHANGES:
* Added warning when trying to search for handles when the system has too many handles open
* Upgraded to PCRE2
* Updated DotNetTools plugin:
* Rewrite of .NET Performance statistics and AppDomain enumeration
* Updated OnlineChecks plugin:
* Fixed virusscan.jotti.org uploader
* Updated NetAdapters plugin:
* Added adapter details window
* Updated ToolStatus plugin:
* Added CPU, Memory and I/O graphs to the toolbar (not enabled by default)
* Added toolbar and status bar customization, as well as a new theme
* Added option to auto-hide the main menu
* Updated UserNotes plugin:
* Added individual process highlighting support
2.36
* HIGHLIGHTS:
* New rich pop-up UI when hovering the cursor over a tray icon, showing the most active processes
* Completely new Memory tab for processes, with heap, stack and working set usage
* Process Hacker now takes 32-bit dumps of 32-bit processes on 64-bit Windows
* NOTE: When using the portable (.zip) release, the entire archive must be extracted
* Updated DotNetTools plugin:
* Process Hacker now displays managed stack traces for 32-bit .NET processes on 64-bit Windows
* Fixed inaccurate stack traces when clicking Refresh
* Added AppDomain column for threads in .NET programs
* OTHER CHANGES:
* Added customizable bytes per row setting for memory editor
* Dramatically faster handle listing and search when running without administrative privileges
* Improved accuracy and speed of symbol resolution, especially when new modules are loaded
* Added trigger and delayed start information to service list
* Added file information to service list tooltips
* Balloon tips for process/service notifications are now clickable
* Added handle names for unnamed File objects
* Added I/O Priority to tray icon process menu
* Added warning for users who attempt to start the 32-bit version on 64-bit Windows
* Updated ExtendedServices plugin:
* Added service protection and SID information
* Added auto-elevation when saving recovery information, triggers and other service settings
* Updated ExtendedTools plugin:
* Added tray icon mini info window support
* Improved automatic GPU node selection
* Updated UserNotes plugin:
* Added tray icon mini info window support
* Fixed a bug in phsvc that caused hangs when automatically elevating actions
* Fixed hang when viewing handle security for certain File objects
* Fixed lack of information on startup when using slower refresh intervals
* Fixed Read/Write Address crash
* Fixed service non-polling mode on Windows 8 and above
* Fixed file dialog crash in Windows PE environments
* Fixed string scanning false positive case
* Fixed process window detection for Modern UI apps
* Fixed handle list selection bug when disabling "Hide unnamed handles"
* NOTE:
* This release has significant internal code changes. Please make sure all plugins are up-to-date.
2.35
* NEW/IMPROVED:
* Added Load Time and Load Reason columns for modules (Windows 8 and above)
* Added handle names for Job and Section objects
* Added Read/Write Memory for Section objects (in process Handles tab)
* Added CF Guard (Control Flow Guard) column for processes and modules
* Added highlighting for AppContainer DLLs
* Added AppContainer and CF Guard image characteristics to peview
* Added Open Key and Open File Location menu items for services
* Set priority and I/O priority for multiple processes at once
* Support for up to 64 processors when setting process/thread affinity
* Updated ExtendedTools plugin:
* Added Disk and Network graphs for all processes
* Updated UserNotes plugin:
* Added ability to save I/O priority
* FIXED:
* Fixed memory editor copy bug
2.34
* NEW/IMPROVED:
* Proper Unicode support
* CPU and GPU graphs are displayed in a grid now (thanks pavel_kv!)
* Start Task Manager now elevates when necessary
* Better names for memory regions in Memory tab (for PEBs, TEBs, thread stacks)
* Added tooltip information for user-mode driver framework (UMDF) host processes
* Added option to reduce row height (set ThinRows to 1 in settings.xml)
* Added NetAdapters plugin: adds graphs for selected network adapters to the System Information window
* Updated ExtendedTools plugin:
* Added GPU graphs for all processes
* Can now use the search box in the Disk tab
* Improved kernel logger handling
* FIXED:
* Fixed touch scrolling
* Fixed EtwRegistration object names for 64-bit Windows 8.1
* Fixed tray icons being clipped in high DPI environments
* Fixed crash in memory editor
* Fixed multi monitor window placement bug
2.33
* NEW/IMPROVED:
* View digital signature information from process properties and peview
* Signatures for Windows 8 apps are now detected
* Improved file, key, process and thread handle properties
* Added DPI Awareness column
* Added new Windows 8.1 process protection information
* KProcessHacker is no longer needed for highlighting of GUI threads
* Added suspend count for threads on Windows 8.1
* Updated DotNetTools plugin:
* Improved .NET assembly enumeration timeout handling
* FIXED:
* Service start type and error control are never updated if modified outside of Process Hacker
2.32
* NOTE:
* All executable files are now signed.
* NEW/IMPROVED:
* Updated for Windows 8.1
* Added progress display for thread stacks
* Updated ExtendedServices plugin:
* Added new trigger data types
* Updated NetworkTools plugin:
* Updated UI
* Updated OnlineChecks plugin:
* Added file analyzed prompt
* FIXED:
* Fixed handling of long symbol names
* Fixed Run As preventing Windows 8 apps from starting
* Fixed console host information for Windows 8.1
* Fixed reflected processes not terminating on Windows 8.1
* Fixed CPU frequency on Windows 8.1
2.31
* NEW/IMPROVED:
* Updated ExtendedServices plugin:
* Fixed some bugs relating to Windows 8
* Updated OnlineChecks plugin:
* Added upload progress
* Updated UserNotes plugin:
* Fixed bug where process priorities were not actually saved
* FIXED:
* Fixed module list not updating properly
* DLL enumeration crash
2.30
* NEW/IMPROVED:
* Added "Icon click toggles visibility" option
* Re-enabled powerful process termination on 32-bit Windows 8
* Updated UserNotes plugin:
* Added ability to save process priority
* Added "Only for processes with the same command line" option for process comments
* FIXED:
* Fixed crash on CPUs without SSE2
2.29
* NEW/IMPROVED:
* Added App ID column for processes
* Added new ASLR information for Windows 8
* Added Restart to Boot Options and Hybrid Shutdown menu items for Windows 8
* Added ability to specify processes by their names and inject and unload DLLs in command line
* Removed 512 character limit when copying text
* Moved Terminator to Miscellaneous menu
* Updated default dbghelp.dll path for Windows SDK v8
* Updated ExtendedServices plugin:
* Added new triggers for Windows 8
* Fixed bug when restarting services
* Updated ExtendedTools plugin:
* Improved support for multiple GPUs (again)
* GPU column now respects "Include CPU usage of children" option
* Updated ToolStatus plugin:
* Fixed search box fonts
* Fixed controls not being properly hidden/removed from the window when disabled
* Updated WindowExplorer plugin:
* Fixed window list not displaying Modern UI windows
* FIXED:
* Fixed Load Count column sorting bug
* Fixed signature verification on Windows 8
* Fixed task scheduler information on Windows 8
* Fixed drag bug in tree list
* Fixed KProcessHacker bug affecting TmTx objects
* Fixed Run As feature on Windows 8
* Fixed bug where -settings parameter is not propagated
* Fixed tab key behavior on main window
* Fixed recognition of Modern UI windows
2.28
* NEW/IMPROVED:
* peview now resolves .lnk targets
* Fixed Ctrl+A for processes, services and network connections and added Ctrl+A for other windows
* Changed confirmation prompts to select the destructive action by default
* Updated DotNetTools plugin:
* Fixed inaccurate stack traces for certain .NET programs
* Updated ExtendedTools plugin:
* Fixed network graph scaling
* Updated ToolStatus plugin:
* Added search box
* Updated Updater plugin
* FIXED:
* Fixed Verification Status column sorting bug in module list
* Fixed rare System Information crash
* Fixed bug in opening process handles
* Fixed freezing when viewing stack traces of certain system threads
2.27
* NEW/IMPROVED:
* Updated OnlineChecks plugin:
* 2012-01-16: Updated VirusTotal uploader and added hash checking
* FIXED:
* Fixed Description column sorting bug
* Fixed notification icon bug
2.26
* NEW/IMPROVED:
* Added option to show Commit Charge in system information summary view
* Added -priority and -selectpid command line options
* Updated ExtendedTools plugin:
* Improved support for multiple GPUs
* FIXED:
* Fixed 100% CPU when starting on some machines
2.25
* NEW/IMPROVED:
* Improved CPU frequency calculation
* Updated ExtendedTools plugin:
* Added GPU node selection
* Fixed incorrect GPU usage calculation
* FIXED:
* Graph tooltip position with large cursors
* Fixed .NET process detection
* Fixed incorrect values in Bits column
2.24
* NOTE:
* This release has significant internal code changes. Please make sure all plugins are up-to-date.
* NEW/IMPROVED:
* Completely new system information window
* Added option to scroll to new processes
* Added option to hide driver services
* Added menu item to copy individual cells
* Improved module scanning
* Added Start Task Manager menu item
* Added Image base to peview
* Updated ExtendedTools plugin:
* Added support for new system information window
* Added Disk, Network and GPU tray icons
* Added support for custom fonts in the Disk tab
* Updated Updater plugin:
* Added download speed
* Added remaining time
* FIXED:
* Fixed retrieval of version information for certain files
* Fixed driver file names on Windows XP
* Fixed Run As Administrator when used with complex commands
2.23
* NEW/IMPROVED:
* Added display of token capabilities, user/device claims and security attributes
* Added ability to change token integrity levels
* Added Description column to service list
* Added option to reset all settings
* Made grid color darker
* Enabled multi-selection in the hidden processes window
* Added UserNotes plugin
* Updated ExtendedNotifications plugin:
* Added Growl support
* Updated ExtendedTools plugin:
* Added GPU monitoring
* Added rate columns for disk and network I/O
* FIXED:
* Fixed copying lists when plugin columns are enabled
* Freezing when viewing the tooltip for a process with a very long command line
* Disabled Hidden Processes feature on 64-bit systems
2.22
* NEW/IMPROVED:
* Added highlighting for metro style apps
* Added Package Name column
* Added package name to process tooltip
* Improved .NET process detection
* Updated OS Context column for Windows 8
* Updated ExtendedTools plugin:
* Updated disk monitoring for Windows 8
* Updated memory list information for Windows 8
* Updated WindowExplorer plugin:
* Fixed hook support for low integrity processes
* FIXED:
* Fixed memory leaks
* Fixed bug preventing Interrupts/DPCs from being shown as the max. CPU process on 64-bit systems
* Fixed DEP Status column on 64-bit systems
2.21
* NEW/IMPROVED:
* Added Private Bytes Delta, ASLR and Subsystem columns
* Added ASLR and Time Stamp columns to modules list
* Added check for debugger in Terminator
* FIXED:
* Fixed Show CPU Below 0.01 not respecting locale
* Fixed copying from network list
2.20
* NEW/IMPROVED:
* Added support for managed thread stacks on x64
* Added column selection for handle list
* Added CPU column to threads list
* Improved module detection
* Added Ideal Processor to Threads tab
* Added pool usage and minimum/maximum working set columns
* Implemented Properties button for Thread handles
* Set descending sort as the default for most numeric columns
* Extended header context menu
* Removed tooltip text truncation
* Improved cycle-based CPU usage calculation
* Set default KProcessHacker security level to only allow connections when Process Hacker is running as administrator.
See README.txt for instructions on how to restore the old behavior.
* Added Updater plugin
* Updated DotNetTools plugin:
* Added managed symbol resolution for thread stacks
* Updated ExtendedTools plugin:
* Added Disk tab
* Added Hard Faults, Hard Faults Delta and Peak Threads columns to process tree list
* Added Firewall Status column
* FIXED:
* Fixed file name resolution bug
* Save settings on shutdown/logoff
* Fixed state highlighting bug
* Fixed command line propagation for -elevate
* Fixed tree list mouse wheel handling
* Fixed saving network list
2.19
* NEW/IMPROVED:
* Added cycle-based CPU usage for Windows 7
* Added Show CPU Below 0.01
* Added OS Context column
* Rewrote graph drawing code for improved performance
* Optimized retrieval of cycle time and private working set information for Windows 7
* Added Open File Location to process context menu and reorganized some items
* Added checkboxes to Terminator
* FIXED:
* Crash when sorting by Time Stamp
* GDI handle leak in drag selection
2.18
* NEW/IMPROVED:
* Completely rewritten tree list control:
* Process Name column is now fixed to the left
* Tooltips for column headers
* Improved performance
* Bug fixes
* Added more process tree list columns
* Added Time stamp column to network list
* Date/time display is now swapped (so time is shown before date)
* Added W3 terminator test
* Added DotNetTools plugin
* Updated ExtendedServices plugin:
* Disabled editing of required privileges for drivers
* Updated ExtendedTools plugin:
* Added ETW columns for processes and network connections
* Updated OnlineChecks plugin:
* Added Comodo Instant Malware Analysis
* Updated WindowExplorer plugin:
* Fixed hook bugs
* FIXED:
* Fixed Run As This User
* Verification Status sorting
2.17
* NEW/IMPROVED:
* Added support for setting page priority
* Added elevation support for setting priority
* Added support for automatically using a settings file in the program directory (e.g. ProcessHacker.exe.settings.xml)
* Improved Run As mechanism
* Updated ExtendedServices plugin:
* Added support for editing triggers
* Added support for editing preshutdown time-out
* Added support for editing required privileges
* Added elevation support for restarting services
* Updated WindowExplorer plugin:
* Added more window properties
* FIXED:
* Handle leak
2.16
* NEW/IMPROVED:
* Updated WindowExplorer plugin
* PE viewer: Added version string to CLR tab
* PE viewer: Added display of delay imports
* PE viewer: Added Load Config tab
* Improved wait analysis
* Added arrows to the service list to indicate whether a service is running
* FIXED:
* Fixed the IPv6-related workaround causing crashes
* Incorrect handling of window positions
2.15
* NEW/IMPROVED:
* Updated ExtendedServices plugin
* Updated ToolStatus plugin
* Added DEP Status column
* Improved User Name column
* FIXED:
* Image file versions
* Workaround for an IPv6-related bug in Windows XP
* DPCs and Interrupts in System Information tooltips
* File dialog crash on Windows XP
* ExtendedTools plugin: WS Watch refresh bug
2.14
* NEW/IMPROVED:
* ExtendedServices plugin: Option to add a Services menu for processes
* Command line support for setting process priority and I/O priority
* Improved termination of explorer.exe
* FIXED:
* Icon should restore the main window if it is minimized
* System Information window crashes
* Hide Processes From Other Users and Hide Signed Processes settings are now saved
* Font selection on Windows XP
* ToolStatus plugin: Always on Top status being reset by Find Window
* Service-related crashes
* WindowExplorer plugin: sorting in tree list
* Process minidump creation with old versions of dbghelp.dll
2.13
* NEW/IMPROVED:
* Added copy support to PE viewer
* Added Connect Time, Disconnect Time and Last Input Time to session properties
* Added more working set counters to the Statistics tab
* FIXED:
* Column sort arrows
* CPU usage calculations
2.12
* NEW/IMPROVED:
* Updated KProcessHacker for Windows 7 SP1
* Added elevation support for more actions
* Added ability to disable plugins
* Updated ToolStatus plugin
* Added Remote Control for sessions
* More command line options
* FIXED:
* Memory leaks
* Run As issues with different sessions
2.11
* NEW/IMPROVED:
* Added WS Watch and other features to ExtendedTools plugin
* Added WindowExplorer plugin
* Properties for hidden processes
* Improved menus
* Debug console can now be closed without affecting the entire program
* FIXED:
* Always on Top issues
* Hang when setting DEP status of a terminating process
* Encoding bug in NetworkTools plugin
* LSA interfacing issues
* Creating dumps of self
2.10
* NEW/IMPROVED:
* KProcessHacker is now signed, so it works on 64-bit systems. Thank you to the ReactOS Foundation.
* Added Run As Limited User
* Added CPU, private bytes and I/O history columns
* Added font selection
* Slightly improved highlighting configuration
* FIXED:
* High DPI support
* Multi-monitor support in graph tooltips
* DEP status retrieval
* ExtendedTools plugin crash
* Notification icon menu crash
* Memory leaks
* Other small bug fixes
2.9
* NEW/IMPROVED:
* Added column selection for modules list
* Added wait analysis for 64-bit systems
* Added signature verification for modules
* Added ExtendedTools plugin (Vista and above only) with Disk and Network information
* Updated ExtendedNotifications plugin: added ability to log events to a file
* Updated ExtendedServices plugin: new tab on Vista and above
* Updated ToolStatus plugin: resolves ghost windows to hung windows
* Environment variables and current directory are now correctly shown for WOW64 processes
* I/O priority names are now used instead of numbers
* FIXED:
* Network list bug
* Memory leaks
2.8
* NEW/IMPROVED:
* Better service list (including column selection)
* Added Peak Handles
* Process tree sorting is now preserved
* Save works for services and network connections
* Pausing now works correctly with the Network tab
* Added option to display inclusive CPU usages for collapsed processes
* Added CLR tab to peview
* Added ability to destroy heaps
* Improved process tree list appearance
* Certain command line parameters are now propagated
* FIXED:
* Icon handling bugs
* Memory leaks
* Extended tooltips for WOW64 processes
2.7
* NEW/IMPROVED:
* Vastly improved startup time and lower memory usage
* Added Cycles and Cycles Delta columns
* Added option to disable address resolution for network connections
* Added Logon Time to session properties
* Added time stamp display to peview
* FIXED:
* ToolStatus layout problems
* .NET highlighting crashes
* Run As on Windows XP
2.6
* NEW/IMPROVED:
* Sorting for most lists is now much faster
* Hide Signed Processes option
* Added plugin for uploading files to online virus scanners
* Added Network tools plugin
* Updated ExtendedServices plugin
* PE viewer now verifies checksums
* Performance improvements
* FIXED:
* Fixed service handle leak
2.5
* NEW/IMPROVED:
* Unmap section views in Memory tab
* Plugin for extended service information (including recovery information, dependencies and dependents)
* FIXED:
* Critical bug for file dialogs on Windows XP
* Esc couldn't close Service Properties on open
* Small bug fixes
2.4
* NEW/IMPROVED:
* Better Run As behaviour
* Show Processes From All Users option
* Can now unmap section views
* Control over thread affinity
* Window Title and Window Status columns
* Plugin for filtering notifications
* Plugin for toolbar and status bar
* Performance improvements
* FIXED:
* Memory leak
* SbieSupport plugin on 64-bit
* Crash when running under certain conditions
* Memory case-insensitive filter
* Process parent association bug
* REMOVED:
* Process database
2.3
* NEW/IMPROVED:
* Can add processes to jobs
* Double-clicking in the system information graphs now opens information for the relevant process
* Setting I/O priority doesn't need KProcessHacker anymore
* Elevation for certain actions
* FIXED:
* HKCU key name resolution
* Network connection host resolution
* Information window resizing
* Log clearing
2.2
* NEW/IMPROVED:
* Plugins support
* Can now unload 32-bit modules on 64-bit systems
* Tasks are shown in tooltips for taskeng.exe/taskhost.exe processes
* Run As can now start processes elevated
* Handle count by type
* Process priorities in notification icon menu
* CSV export
* Relative start times
* FIXED:
* Run and Run As shortcuts
* Command line handling
* Process tree selection
2.1
* NEW/IMPROVED:
* Add Pause key shortcut to pause/resume updates
* Added Ctrl+Tab and Ctrl+Shift+Tab shortcuts
* Grid is a bit darker
* Checks for digital signatures and packing is now off by default and optional
* FIXED:
* MD5 calculation code for files was wrong
* Process record bugs
2.0
* First release in the Process Hacker 2.x branch.
================================================
FILE: CLA.md
================================================
# Contribution License Agreement
This Contribution License Agreement (“Agreement”) is agreed to by the party signing (“You”),
and conveys certain license rights to Winsider Seminars & Solutions, Inc. and its affiliates
(“Winsiderss”) for Your contributions to Winsider Seminars & Solutions, Inc. open source projects.
This Agreement is effective as of the latest signature date.
1. **Definitions**. “**Code**” means the computer software code, whether in human-readable or
machine-executable form, that is delivered by You to Winsider Seminars & Solutions, Inc. under
this Agreement. “**Project**” means the System Informer project owned or managed by Winsider
Seminars & Solutions, Inc. and offered under the terms of the following license(s) MIT (including
any right to adopt any future version of a license if permitted). “**Submit**” is the act of
uploading, submitting, transmitting, or distributing code or other content to any Project,
including but not limited to communication on electronic mailing lists, source code control
systems, and issue tracking systems that are managed by, or on behalf of, the Project for the
purpose of discussing and improving that Project, but excluding communication that is conspicuously
marked or otherwise designated in writing by You as “Not a Submission.” “**Submission**” means the
Code and any other copyrightable material Submitted by You, including any associated comments and
documentation.
2. **Your Submission**. You must agree to the terms of this Agreement before making a Submission
to any Project. This Agreement covers any and all Submissions that You, now or in the future
(except as described in Section 4 below), Submit to any Project.
3. **Originality of Work**. You represent that each of Your Submissions is entirely Your original
work. Should You wish to Submit materials that are not Your original work, You may Submit them
separately to the Project if You (a) retain all copyright and license information that was in the
materials as You received them, (b) in the description accompanying Your Submission, include the
phrase “Submission containing materials of a third party:” followed by the names of the third
party and any licenses or other restrictions of which You are aware, and (c) follow any other
instructions in the Project’s written guidelines concerning Submissions.
4. **Your Employer**. References to “employer” in this Agreement include Your employer or anyone
else for whom You are acting in making Your Submission, e.g. as a contractor, vendor, or agent.
If Your Submission is made in the course of Your work for an employer or Your employer has
intellectual property rights in Your Submission by contract or applicable law, You must secure
permission from Your employer to make the Submission before signing this Agreement. In that case,
the term “You” in this Agreement will refer to You and the employer collectively. If You change
employers in the future and desire to Submit additional Submissions for the new employer, then
You agree to sign a new Agreement and secure permission from the new employer before Submitting
those Submissions.
5. **Licenses**.
- **Copyright License**. You grant Winsider Seminars & Solutions, Inc., and those who receive
the Submission directly or indirectly from Winsider Seminars & Solutions, Inc., a perpetual,
worldwide, non-exclusive, royalty-free, irrevocable license in the Submission to reproduce,
prepare derivative works of, publicly display, publicly perform, and distribute the Submission
and such derivative works, and to sublicense any or all of the foregoing rights to third parties.
- **Patent License**. You grant Winsider Seminars & Solutions, Inc., and those who receive the
Submission directly or indirectly from Winsider Seminars & Solutions, Inc., a perpetual,
worldwide, non-exclusive, royalty-free, irrevocable license under Your patent claims that are
necessarily infringed by the Submission or the combination of the Submission with the Project
to which it was Submitted to make, have made, use, offer to sell, sell and import or otherwise
dispose of the Submission alone or with the Project.
- **Other Rights Reserved**. Each party reserves all rights not expressly granted in this
Agreement. No additional licenses or rights whatsoever (including, without limitation, any
implied licenses) are granted by implication, exhaustion, estoppel or otherwise.
6. **Representations and Warranties**. You represent that You are legally entitled to grant the
above licenses. You represent that each of Your Submissions is entirely Your original work (except
as You may have disclosed under Section 3). You represent that You have secured permission from
Your employer to make the Submission in cases where Your Submission is made in the course of Your
work for Your employer or Your employer has intellectual property rights in Your Submission by
contract or applicable law. If You are signing this Agreement on behalf of Your employer, You
represent and warrant that You have the necessary authority to bind the listed employer to the
obligations contained in this Agreement. You are not expected to provide support for Your
Submission, unless You choose to do so. UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING,
AND EXCEPT FOR THE WARRANTIES EXPRESSLY STATED IN SECTIONS 3, 4, AND 6, THE SUBMISSION PROVIDED
UNDER THIS AGREEMENT IS PROVIDED WITHOUT WARRANTY OF ANY KIND, INCLUDING, BUT NOT LIMITED TO, ANY
WARRANTY OF NONINFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE.
7. **Notice to Winsider Seminars & Solutions, Inc.**. You agree to notify Winsider Seminars &
Solutions, Inc. in writing of any facts or circumstances of which You later become aware that would
make Your representations in this Agreement inaccurate in any respect.
8. **Information about Submissions**. You agree that contributions to Projects and information
about contributions may be maintained indefinitely and disclosed publicly, including Your name
and other information that You submit with Your Submission.
9. **Governing Law/Jurisdiction**. This Agreement and all disputes, claims, actions, suits or
other proceedings arising out of this agreement or relating in any way to it shall be governed by
the laws of Quebec, Canada excluding its private international law provisions.
10. **Entire Agreement/Assignment**. This Agreement is the entire agreement between the parties,
and supersedes any and all prior agreements, understandings or communications, written or oral,
between the parties relating to the subject matter hereof. This Agreement may be assigned by
Winsider Seminars & Solutions, Inc..
================================================
FILE: CMakeLists.txt
================================================
#
# Copyright (c) 2022 Winsider Seminars & Solutions, Inc. All rights reserved.
#
# This file is part of System Informer.
#
cmake_minimum_required(VERSION 3.30.0 FATAL_ERROR)
project(SystemInformer)
set(SI_ROOT ${CMAKE_CURRENT_SOURCE_DIR})
list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake)
include(commands)
set(CMAKE_CONFIGURATION_TYPES "Debug;Release" CACHE STRING "" FORCE)
# TODO change this to bin once we fully transition to cmake
# set up to allow user to disable with "" or "OFF"
set(SI_OUTPUT_DIR "${SI_ROOT}/bin-cmake" CACHE PATH "Output directory")
option(SI_WITH_CORE "Configure core projects" ON)
option(SI_WITH_PLUGINS "Configure plugin projects" OFF)
option(SI_WITH_KERNEL "Configure kernel projects" OFF)
option(SI_WITH_PREFAST "Enables prefast analysis" OFF)
option(SI_WITH_WPP_USER "Enables user mode WPP trace" OFF)
option(SI_WITH_WPP_KERNEL "Enables kernel mode WPP trace" ON)
if (MSVC_NOT_CLANG AND CMAKE_BUILD_TYPE STREQUAL "Release")
add_compile_options(/d1trimfile:${SI_ROOT})
endif()
if(SI_WITH_CORE)
add_subdirectory(SystemInformer)
add_subdirectory(phnt)
add_subdirectory(phlib)
add_subdirectory(tools)
endif()
if(SI_WITH_KERNEL)
message(FATAL_ERROR "Kernel mode projects are not supported")
add_subdirectory(KSystemInformer)
endif()
if(SI_WITH_PLUGINS)
add_subdirectory(plugins)
endif()
if(SI_WITH_CORE OR SI_WITH_KERNEL)
add_subdirectory(kphlib)
endif()
================================================
FILE: CODE_OF_CONDUCT.md
================================================
# Contributor Covenant Code of Conduct
## Our Pledge
In the interest of fostering an open and welcoming environment, we as
contributors and maintainers pledge to making participation in our project and
our community a harassment-free experience for everyone, regardless of age, body
size, disability, ethnicity, sex characteristics, gender identity and expression,
level of experience, education, socio-economic status, nationality, personal
appearance, race, religion, or sexual identity and orientation.
## Our Standards
Examples of behavior that contributes to creating a positive environment
include:
* Using welcoming and inclusive language
* Being respectful of differing viewpoints and experiences
* Gracefully accepting constructive criticism
* Focusing on what is best for the community
* Showing empathy towards other community members
Examples of unacceptable behavior by participants include:
* The use of sexualized language or imagery and unwelcome sexual attention or
advances
* Trolling, insulting/derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or electronic
address, without explicit permission
* Other conduct which could reasonably be considered inappropriate in a
professional setting
## Our Responsibilities
Project maintainers are responsible for clarifying the standards of acceptable
behavior and are expected to take appropriate and fair corrective action in
response to any instances of unacceptable behavior.
Project maintainers have the right and responsibility to remove, edit, or
reject comments, commits, code, wiki edits, issues, and other contributions
that are not aligned to this Code of Conduct, or to ban temporarily or
permanently any contributor for other behaviors that they deem inappropriate,
threatening, offensive, or harmful.
## Scope
This Code of Conduct applies both within project spaces and in public spaces
when an individual is representing the project or its community. Examples of
representing a project or community include using an official project e-mail
address, posting via an official social media account, or acting as an appointed
representative at an online or offline event. Representation of a project may be
further defined and clarified by project maintainers.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported by contacting the project team at dmex04@gmail.com and johnny.shaw@live.com. All
complaints will be reviewed and investigated and will result in a response that
is deemed necessary and appropriate to the circumstances. The project team is
obligated to maintain confidentiality with regard to the reporter of an incident.
Further details of specific enforcement policies may be posted separately.
Project maintainers who do not follow or enforce the Code of Conduct in good
faith may face temporary or permanent repercussions as determined by other
members of the project's leadership.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
[homepage]: https://www.contributor-covenant.org
For answers to common questions about this code of conduct, see
https://www.contributor-covenant.org/faq
================================================
FILE: CONTRIBUTING.md
================================================
# Contribution Guidelines
================================================
FILE: COPYRIGHT.txt
================================================
== System Informer ==
System Informer is licensed. A full copy of the license is provided in LICENSE.txt.
Copyright (c) 2022 Winsider Seminars & Solutions, Inc. All rights reserved.
== Mini-XML ==
System Informer uses Mini-XML licensed under the following terms:
The Mini-XML library and included programs are provided under the
terms of the GNU Library General Public License (LGPL) with the
following exceptions:
1. Static linking of applications to the Mini-XML library
does not constitute a derivative work and does not require
the author to provide source code for the application, use
the shared Mini-XML libraries, or link their applications
against a user-supplied version of Mini-XML.
If you link the application to a modified version of
Mini-XML, then the changes to Mini-XML must be provided
under the terms of the LGPL in sections 1, 2, and 4.
2. You do not have to provide a copy of the Mini-XML license
with programs that are linked to the Mini-XML library, nor
do you have to identify the Mini-XML license in your
program or documentation as required by section 6 of the
LGPL.
== PCRE ==
System Informer uses Perl-Compatible Regular Expressions licensed under the
following terms:
PCRE is a library of functions to support regular expressions whose syntax
and semantics are as close as possible to those of the Perl 5 language.
Release 8 of PCRE is distributed under the terms of the "BSD" licence, as
specified below.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of the University of Cambridge nor the name of Google
Inc. nor the names of their contributors may be used to endorse or
promote products derived from this software without specific prior
written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
== MD5 ==
System Informer uses a MD5 implementation licensed under the following terms:
MD5 hash implementation and interface functions
Copyright (c) 2003-2005, Jouni Malinen
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License version 2 as
published by the Free Software Foundation.
== SHA ==
System Informer uses a SHA implementation licensed under the following terms:
Copyright 2004 Filip Navara
Based on public domain SHA code by Steve Reid
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
== Natural order string comparison ==
System Informer uses "strnatcmp.c" licensed under the following terms:
strnatcmp.c -- Perform 'natural order' comparisons of strings in C.
Copyright (C) 2000, 2004 by Martin Pool
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
This code has been modified for System Informer.
================================================
FILE: Common.Kernel.props
================================================
true
WDM
$(ProjectDir)$(ProjectName)\obj\
WindowsKernelModeDriver10.0
false
Off
$(LatestTargetPlatformVersion)
Unicode
Spectre
Windows10
<_NT_TARGET_VERSION Condition="'$(Platform)'=='x64'">0x0A00
<_NT_TARGET_VERSION Condition="'$(Platform)'=='ARM64'">0xA000004
true
true
false
false
false
true
$(SystemInformerRoot)\Common.Kernel.ruleset
DbgengKernelDebugger
false
$(CRT_IncludePath);$(KM_IncludePath);$(KIT_SHARED_IncludePath)
%(AdditionalIncludeDirectories);$(SystemInformerRoot)phnt\include\
%(AdditionalIncludeDirectories);$(SystemInformerRoot)kphlib\include\
%(AdditionalIncludeDirectories);$(ProjectDir)include\
/kernel /utf-8 /INTEGRITYCHECK /d1nodatetime %(AdditionalOptions)
/d2guardretpoline %(AdditionalOptions)
StdCall
true
Guard
ProgramDatabase
4201
true
true
stdcpp20
stdc17
true
POOL_NX_OPTIN;POOL_ZERO_DOWN_LEVEL_SUPPORT;%(PreprocessorDefinitions)
_WIN64;%(PreprocessorDefinitions)
_AMD64_;AMD64;%(PreprocessorDefinitions)
_ARM64_;ARM64;%(PreprocessorDefinitions)
_DEBUG;DEBUG;%(PreprocessorDefinitions)
true
true
true
Level4
Speed
AnySuitable
true
MaxSpeed
%(AdditionalLibraryDirectories);$(OutDir)
/INTEGRITYCHECK /DEPENDENTLOADFLAG:0x800 /FILEALIGN:0x1000 %(AdditionalOptions)
/NATVIS:$(SystemInformerRoot)SystemInformer.natvis %(AdditionalOptions)
/guard:retpoline %(AdditionalOptions)
/BREPRO /NOVCFEATURE /NOCOFFGRPINFO %(AdditionalOptions)
true
UseLinkTimeCodeGeneration
true
true
UseLinkTimeCodeGeneration
true
true
/c 65001 %(AdditionalOptions)
true
tracewpp.exe
$(TraceWppCmd) -km
$(TraceWppCmd) -ext:.c.cpp.h.hpp
$(TraceWppCmd) -preserveext:.c.cpp.h.hpp
$(TraceWppCmd) -I"$(WindowsSdkDir)bin\$(TargetPlatformVersion)\WppConfig\Rev1"
$(TraceWppCmd) -odir:"$(IntDir)tmh"
$(TraceWppCmd) -scan:"$(SystemInformerRoot)KSystemInformer\include\trace.h"
%(AdditionalIncludeDirectories);$(IntDir)tmh\
TMH_FILE=%(FileName)%(Extension).tmh;%(PreprocessorDefinitions)
KSI_NO_WPP;%(PreprocessorDefinitions)
================================================
FILE: Common.Kernel.ruleset
================================================
================================================
FILE: Common.User.props
================================================
true
Level3
true
false
true
stdcpplatest
stdclatest
true
false
false
true
true
true
$(LatestTargetPlatformVersion)
true
Windows
true
true
true
true
0x0009
/c 65001 %(AdditionalOptions)
%(PreprocessorDefinitions);$(ExternalPreprocessorOptions)
true
tracewpp.exe
$(TraceWppCmd) -um
$(TraceWppCmd) -ext:.c.cpp.h.hpp
$(TraceWppCmd) -preserveext:.c.cpp.h.hpp
$(TraceWppCmd) -I"$(WindowsSdkDir)bin\$(TargetPlatformVersion)\WppConfig\Rev1"
$(TraceWppCmd) -odir:"$(IntDir)tmh"
$(TraceWppCmd) -scan:"$(SystemInformerRoot)phlib\include\trace.h"
%(AdditionalIncludeDirectories);$(IntDir)tmh\
TMH_FILE=%(FileName)%(Extension).tmh;%(PreprocessorDefinitions)
SI_NO_WPP;%(PreprocessorDefinitions)
Unicode
true
v143
false
true
false
Unicode
true
v143
false
true
false
Unicode
true
v143
false
true
false
Unicode
false
v143
Spectre
false
true
Unicode
false
v143
Spectre
false
true
Unicode
false
v143
Spectre
false
true
/utf-8 /d1nodatetime %(AdditionalOptions)
WIN32;DEBUG;_DEBUG;_WINDOWS;_USRDLL;%(PreprocessorDefinitions);$(ExternalPreprocessorOptions)
EditAndContinue
$(ExternalSimdOptions)
Disabled
MultiThreadedDebug
StdCall
Speed
Precise
true
AnySuitable
false
true
/BREPRO /DEBUGTYPE:CV,PDATA /DEPENDENTLOADFLAG:0x800 /FILEALIGN:0x1000 /NOOPTIDATA /BASERELOCCLUSTERING /NATVIS:$(MSBuildThisFileDirectory)\SystemInformer.natvis %(AdditionalOptions) $(ExternalLinkerOptions)
invalidcontinue.obj;noarg.obj;noenv.obj;nothrownew.obj;%(AdditionalDependencies)
6.01
MachineX86
true
true
true
true
Default
/utf-8 /d1nodatetime /d1import_no_registry /jumptablerdata %(AdditionalOptions) $(ExternalAdditionalOptions)
WIN64;DEBUG;_DEBUG;_WINDOWS;_USRDLL;%(PreprocessorDefinitions);$(ExternalPreprocessorOptions)
EditAndContinue
$(ExternalSimdOptions)
Disabled
MultiThreadedDebug
StdCall
Speed
Precise
true
AnySuitable
false
true
/BREPRO /DEBUGTYPE:CV,PDATA /DEPENDENTLOADFLAG:0x800 /FILEALIGN:0x1000 /NOOPTIDATA /BASERELOCCLUSTERING /NATVIS:$(MSBuildThisFileDirectory)\SystemInformer.natvis %(AdditionalOptions) $(ExternalLinkerOptions)
invalidcontinue.obj;noarg.obj;noenv.obj;nothrownew.obj;%(AdditionalDependencies)
6.01
MachineX64
true
true
true
true
Default
/utf-8 /d1nodatetime /d1import_no_registry %(AdditionalOptions)
WIN64;DEBUG;_DEBUG;_WINDOWS;_USRDLL;%(PreprocessorDefinitions);$(ExternalPreprocessorOptions)
EditAndContinue
$(ExternalSimdOptions)
Disabled
MultiThreadedDebug
StdCall
Speed
Precise
true
AnySuitable
false
true
true
/BREPRO /DEBUGTYPE:CV,PDATA /DEPENDENTLOADFLAG:0x800 /FILEALIGN:0x1000 /NOOPTIDATA /BASERELOCCLUSTERING /NATVIS:$(MSBuildThisFileDirectory)\SystemInformer.natvis %(AdditionalOptions) $(ExternalLinkerOptions)
invalidcontinue.obj;noarg.obj;noenv.obj;nothrownew.obj;%(AdditionalDependencies)
10
MachineARM64
true
true
true
true
Default
/utf-8 /d1nodatetime /d1import_no_registry /d1trimfile:"$(MSBuildThisFileDirectory)\" %(AdditionalOptions)
WIN32;NDEBUG;_WINDOWS;_USRDLL;%(PreprocessorDefinitions);$(ExternalPreprocessorOptions)
ProgramDatabase
MaxSpeed
MultiThreaded
StdCall
Precise
true
true
Spectre
true
AnySuitable
true
Speed
Guard
/BREPRO /DEBUGTYPE:CV,PDATA /DEPENDENTLOADFLAG:0x800 /PDBALTPATH:%_PDB% /FILEALIGN:0x1000 /NOOPTIDATA /NOVCFEATURE /NOCOFFGRPINFO /BASERELOCCLUSTERING %(AdditionalOptions) $(ExternalLinkerOptions)
invalidcontinue.obj;noarg.obj;noenv.obj;nothrownew.obj;%(AdditionalDependencies)
true
6.01
MachineX86
true
true
$(OutDir)$(TargetName)_stripped.pdb
true
true
true
true
UseLinkTimeCodeGeneration
/utf-8 /d1nodatetime /d1import_no_registry /guard:xfg /jumptablerdata /d1trimfile:"$(MSBuildThisFileDirectory)\" %(AdditionalOptions)
WIN64;NDEBUG;_WINDOWS;_USRDLL;%(PreprocessorDefinitions);$(ExternalPreprocessorOptions)
ProgramDatabase
$(ExternalSimdOptions)
MaxSpeed
MultiThreaded
StdCall
Precise
true
true
Spectre
true
true
AnySuitable
true
Speed
Guard
/BREPRO /DEBUGTYPE:CV,PDATA /DEPENDENTLOADFLAG:0x800 /PDBALTPATH:%_PDB% /FILEALIGN:0x1000 /LTCG /GUARD:XFG /NOOPTIDATA /NOVCFEATURE /NOCOFFGRPINFO /BASERELOCCLUSTERING %(AdditionalOptions) $(ExternalLinkerOptions)
invalidcontinue.obj;noarg.obj;noenv.obj;nothrownew.obj;%(AdditionalDependencies)
true
6.01
MachineX64
true
true
$(OutDir)$(TargetName)_stripped.pdb
true
true
true
true
UseLinkTimeCodeGeneration
/utf-8 /d1nodatetime /d1import_no_registry /d1trimfile:"$(MSBuildThisFileDirectory)\" %(AdditionalOptions)
WIN64;NDEBUG;_WINDOWS;_USRDLL;%(PreprocessorDefinitions);$(ExternalPreprocessorOptions)
ProgramDatabase
$(ExternalSimdOptions)
MaxSpeed
MultiThreaded
StdCall
Precise
true
true
true
true
AnySuitable
true
Speed
Guard
/BREPRO /DEBUGTYPE:CV,PDATA /DEPENDENTLOADFLAG:0x800 /PDBALTPATH:%_PDB% /FILEALIGN:0x1000 /LTCG /NOOPTIDATA /NOVCFEATURE /NOCOFFGRPINFO /BASERELOCCLUSTERING %(AdditionalOptions) $(ExternalLinkerOptions)
invalidcontinue.obj;noarg.obj;noenv.obj;nothrownew.obj;%(AdditionalDependencies)
10
MachineARM64
true
true
$(OutDir)$(TargetName)_stripped.pdb
true
true
true
true
UseLinkTimeCodeGeneration
================================================
FILE: Directory.Build.props
================================================
$(MSBuildThisFileDirectory)
================================================
FILE: HACKING.md
================================================
## Conventions
### Names
* Functions, function parameters, and global variables use CamelCase.
* Local variables use lowerCamelCase.
* Structs, enums and unions use CAPS_WITH_UNDERSCORES.
All names must have an appropriate prefix (with some exceptions):
* `Ph` or `PH_` (structures) for public names.
* `Php` or `PHP_` for private names.
* Some variants such as `Pha`.
* Private prefixes are created by appending `p` to the prefix. E.g. `Ph` -> `Php`, `Pha` -> `Phap`.
* Functions and global variables without a prefix must be declared "static".
* `static` names must not have a public prefix.
* Names with a private prefix do not have to be `static`.
* Structures without a prefix must be declared in a `.c/.cpp` file. Structures declared in a `.c/.cpp` file do not require a prefix.
### Types
Unless used for the Win32 API, the standard types are:
* `BOOLEAN` for a 1 byte boolean, or `LOGICAL` for a 4 byte boolean.
* `UCHAR` for 1 byte.
* `SHORT`/`USHORT` for 2 bytes.
* `LONG`/`ULONG` for 4 bytes.
* `LONG64`/`ULONG64` for 8 bytes.
* `CHAR` for a 1 byte character.
* `WCHAR` for a 2 byte character.
* `PSTR` for a string of 1 byte characters.
* `PWSTR` for a string of 2 byte characters.
#### Booleans
Always use:
```
if (booleanVariable) // not "if (booleanVariable == TRUE)"
{
...
}
```
to test a boolean value.
### Annotations, qualifiers
* All functions use SAL annotations, such as `_In_`, `_Inout_`, `_Out_`, etc.
* Do not use `volatile` in definitions. Instead, cast to a volatile pointer when necessary.
### Function success indicators
There are three main types of indicators used:
* A `BOOLEAN` value is returned. `TRUE` indicates success.
* A `NTSTATUS` value is returned. The `NT_SUCCESS` macro checks if a status value indicates success.
* A `HRESULT` value is returned. The `HR_SUCCESS` macro checks if a status value indicates success.
* Do not use the `SUCCEEDED` macro since third parties return `S_FALSE` for errors which `SUCCEEDED` considers a success code.
* The result of the function is returned (e.g. a pointer). A special value (e.g. `NULL`) indicates failure.
Unless indicated, a function which fails is guaranteed not to modify any of its output parameters (`_Out_`, `_Out_opt_`, etc.).
For functions which are passed a callback function, it is not guaranteed that a failed function has not executed the callback function.
### Threads
Every thread start routine must have the following signature:
```
_Function_class_(USER_THREAD_START_ROUTINE)
NTSTATUS NTAPI NameOfRoutine(
_In_ PVOID Parameter
);
```
Thread creation is done through the `PhCreateThread` and `PhCreateThreadEx` functions.
### Collections
The collections available are summarized below:
Name | Use | Type
--------------------- | ----------------------- | ---------------
`PH_ARRAY` | Array | Non-intrusive
`PH_LIST` | Array | Non-intrusive
`LIST_ENTRY` | Doubly linked list | Intrusive
`SINGLE_LIST_ENTRY` | Singly linked list | Intrusive
`PH_POINTER_LIST` | Array | Non-intrusive
`LIST_ENTRY` | Stack | Intrusive
`SINGLE_LIST_ENTRY` | Stack | Intrusive
`LIST_ENTRY` | Queue | Intrusive
`RTL_AVL_TABLE` | Binary tree (AVL) | Non-intrusive
`PH_AVL_LINKS` | Binary tree (AVL) | Intrusive
`RTL_GENERIC_TABLE` | Binary tree (splay) | Non-intrusive
`PH_HASHTABLE` | Hashtable | Non-intrusive
`PH_HASH_ENTRY` | Hashtable | Intrusive
`PH_CIRCULAR_BUFFER` | Circular buffer | Non-intrusive
### Synchronization
The queued lock should be used for all synchronization, due to its small size and good performance. Although the queued lock is a reader-writer lock, it can be used as a mutex simply by using the exclusive acquire/release functions.
- Events can be used through `PH_EVENT`. This object does not create a kernel event object until needed, and testing its state is very fast.
- Rundown protection is available through `PH_RUNDOWN_PROTECT`.
- Condition variables are available using the queued lock. Simply declare and initialize a queued lock variable, and use the `PhPulse(All)Condition` and `PhWaitForCondition` functions.
- Custom locking with low overhead can be built using the wake event, built on the queued lock. Test one or more conditions in a loop and use `PhQueueWakeEvent`/`PhWaitForWakeEvent` to block. When a condition is modified use `PhSetWakeEvent` to wake waiters. If after calling `PhQueueWakeEvent` it is determined that no blocking should occur, use `PhSetWakeEvent`.
### Exceptions (SEH)
The only method of error handling used in Process Hacker is the return value (`NTSTATUS`, `BOOLEAN`, etc.). Exceptions are used for exceptional situations which cannot easily be recovered from (e.g. a lock acquire function fails to block, or an object has a negative reference count.
Exceptions to this rule include:
* `PhAllocate`, which raises an exception if it fails to allocate. Checking the return value of each allocation to increase reliability is not worth the extra effort involved, as failed allocations are very rare.
* `PhProbeAddress`, which raises an exception if an address lies outside of a specified range. Raising an exception makes it possible to conduct multiple checks in one SEH block.
* `STATUS_NOT_IMPLEMENTED` exceptions triggered by code paths which should not be reached, purely due to programmer error. `assert(FALSE)` could also be used in this case.
### Memory management
Use `PhAllocate`/`PhFree` to allocate/free memory. For complex objects, use the reference counting system.
There is semi-automatic reference counting available in the form of auto-dereference pools (similar to Apple's `NSAutoreleasePool`s). Use the `PhAutoDereferenceObject` to add an object to the thread's pool, and the object will be dereferenced at an unspecified time in the future. However, the object is guaranteed to not be dereferenced while the current function is executing.
Referencing an object is necessary whenever a pointer to the object is stored in a globally visible location or passed to another thread. In most other cases, referencing is not necessary.
All objects passed to functions must have a guaranteed reference for the duration of that call. One mistake is to keep a reference which could be destroyed in a window procedure, and to use that reference implicitly inside the window procedure. Messages can still be pumped (e.g. dialog boxes) while the window procedure is executing, so the window procedure must reference the object as soon as possible.
### Names (2)
#### Object creation/deletion
* Allocate means allocate memory without initialization.
* Create means allocate memory for an object and initialize the object.
* Free can be used for objects created with Allocate or Create functions.
* Destroy can also be used for objects created with Create functions.
* Initialize means initialize an object with caller-supplied storage.
* Delete is paired with Initialize and does not free the object as it was allocated by the caller.
* Create is used when objects are being created through the reference counting system.
Examples:
* `PhAllocateFromFreeList`/`PhFreeToFreeList`
* `PhCreateFileDialog`/`PhFreeFileDialog`
* `PhInitializeWorkQueue`/`PhDeleteWorkQueue`
* `PhCreateString`/`PhDereferenceObject`
#### Element counts
* Length specifies the length in bytes. E.g. `PhCreateString` requires the length to be specified in bytes.
* Count specifies the length in elements. E.g. `PhSubstring` requires the length to be specified in characters.
* Index specifies the index in elements. E.g. `PhSubstring` requires the index to be specified in characters.
When null terminated strings are being written to output, the return count, if any, must be specified as the number of characters written including the null terminator.
### Strings
Strings use the `PH_STRING` type, managed by reference counting. To create a string object from a null-terminated string:
```
PPH_STRING myString = PhCreateString(L"My string");
wprintf(
L"My string is \"%s\", and uses %Iu bytes.\n",
myString->Buffer,
myString->Length
);
```
All string objects have an embedded length (always in bytes), and the string is additionally null-terminated for compatibility reasons.
String objects must be treated as immutable unless a string object is created and modified before the pointer is shared with any other functions or stored in any global variables. This exception applies only when creating a string using `PhCreateString` or `PhCreateStringEx`.
Strings can be concatenated with `PhConcatStrings`:
```
PPH_STRING newString;
newString = PhConcatStrings(
4,
L"My first string, ",
L"My second string, ",
aStringFromSomewhere,
L"My fourth string."
);
```
Another version concatenates two strings:
```
PPH_STRING newString;
newString = PhConcatStrings2(
L"My first string, ",
L"My second string."
);
```
Strings can be formatted:
```
PPH_STRING newString;
newString = PhFormatString(
L"%d: %s, %#x",
100,
L"test",
0xff
);
```
### Tips
* Use !! to "cast" to a boolean.
================================================
FILE: KSystemInformer/Directory.Build.props
================================================
================================================
FILE: KSystemInformer/KSystemInformer.inf
================================================
;
; Copyright (c) 2022 Winsider Seminars & Solutions, Inc. All rights reserved.
;
; This file is part of System Informer.
;
; Authors:
;
; jxy-s 2022-2025
;
[Version]
Signature = "$WINDOWS NT$"
Class = ActivityMonitor
ClassGuid = {b86dff51-a31e-4bac-b3cf-e8cfe75c9fc2}
Provider = SystemInformer
DriverVer = 09/01/2022,3.0.0000.0000
CatalogFile = KSystemInformer.cat
PnpLockdown = 1
[DestinationDirs]
DefaultDestDir = 13
[SystemInformer.Service]
DisplayName = KSystemInformer
ServiceType = 1 ; SERVICE_KERNEL_DRIVER
StartType = 3 ; SERVICE_DEMAND_START
ErrorControl = 1 ; SERVICE_ERROR_NORMAL
ServiceBinary = %13%\systeminformer.sys
[SystemInformer.CopyFiles]
systeminformer.sys
ksi.dll
[SourceDisksFiles]
systeminformer.sys = 1,,
ksi.dll = 1,,
[SourceDisksNames]
1 = %DiskId1%,,,""
[Strings]
DiskId1 = "KSystemInformer Disk #1"
;
; AMD64 Section
;
[DefaultInstall.NTamd64]
CopyFiles = SystemInformer.CopyFiles
[DefaultInstall.NTamd64.Services]
AddService = KSystemInformer,,SystemInformer.Service
;
; ARM64 Section
;
[DefaultInstall.NTarm64]
CopyFiles = SystemInformer.CopyFiles
[DefaultInstall.NTarm64.Services]
AddService = KSystemInformer,,SystemInformer.Service
================================================
FILE: KSystemInformer/KSystemInformer.sln
================================================
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.7.34024.191
MinimumVisualStudioVersion = 10.0.40219.1
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "KSystemInformer", "KSystemInformer.vcxproj", "{F4853009-C5D2-4A25-BE4D-BB0D9F84E2FF}"
ProjectSection(ProjectDependencies) = postProject
{B1863396-A667-42DB-97AC-C5E033CEE321} = {B1863396-A667-42DB-97AC-C5E033CEE321}
{B385D394-19CC-48BC-827E-AF9ADCE559E0} = {B385D394-19CC-48BC-827E-AF9ADCE559E0}
EndProjectSection
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "kphlib", "..\kphlib\kphlib_km.vcxproj", "{B1863396-A667-42DB-97AC-C5E033CEE321}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ksidll", "ksidll.vcxproj", "{B385D394-19CC-48BC-827E-AF9ADCE559E0}"
ProjectSection(ProjectDependencies) = postProject
{B1863396-A667-42DB-97AC-C5E033CEE321} = {B1863396-A667-42DB-97AC-C5E033CEE321}
EndProjectSection
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|ARM64 = Debug|ARM64
Debug|x64 = Debug|x64
Release|ARM64 = Release|ARM64
Release|x64 = Release|x64
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{F4853009-C5D2-4A25-BE4D-BB0D9F84E2FF}.Debug|ARM64.ActiveCfg = Debug|ARM64
{F4853009-C5D2-4A25-BE4D-BB0D9F84E2FF}.Debug|ARM64.Build.0 = Debug|ARM64
{F4853009-C5D2-4A25-BE4D-BB0D9F84E2FF}.Debug|ARM64.Deploy.0 = Debug|ARM64
{F4853009-C5D2-4A25-BE4D-BB0D9F84E2FF}.Debug|x64.ActiveCfg = Debug|x64
{F4853009-C5D2-4A25-BE4D-BB0D9F84E2FF}.Debug|x64.Build.0 = Debug|x64
{F4853009-C5D2-4A25-BE4D-BB0D9F84E2FF}.Debug|x64.Deploy.0 = Debug|x64
{F4853009-C5D2-4A25-BE4D-BB0D9F84E2FF}.Release|ARM64.ActiveCfg = Release|ARM64
{F4853009-C5D2-4A25-BE4D-BB0D9F84E2FF}.Release|ARM64.Build.0 = Release|ARM64
{F4853009-C5D2-4A25-BE4D-BB0D9F84E2FF}.Release|ARM64.Deploy.0 = Release|ARM64
{F4853009-C5D2-4A25-BE4D-BB0D9F84E2FF}.Release|x64.ActiveCfg = Release|x64
{F4853009-C5D2-4A25-BE4D-BB0D9F84E2FF}.Release|x64.Build.0 = Release|x64
{F4853009-C5D2-4A25-BE4D-BB0D9F84E2FF}.Release|x64.Deploy.0 = Release|x64
{B1863396-A667-42DB-97AC-C5E033CEE321}.Debug|ARM64.ActiveCfg = Debug|ARM64
{B1863396-A667-42DB-97AC-C5E033CEE321}.Debug|ARM64.Build.0 = Debug|ARM64
{B1863396-A667-42DB-97AC-C5E033CEE321}.Debug|ARM64.Deploy.0 = Debug|ARM64
{B1863396-A667-42DB-97AC-C5E033CEE321}.Debug|x64.ActiveCfg = Debug|x64
{B1863396-A667-42DB-97AC-C5E033CEE321}.Debug|x64.Build.0 = Debug|x64
{B1863396-A667-42DB-97AC-C5E033CEE321}.Debug|x64.Deploy.0 = Debug|x64
{B1863396-A667-42DB-97AC-C5E033CEE321}.Release|ARM64.ActiveCfg = Release|ARM64
{B1863396-A667-42DB-97AC-C5E033CEE321}.Release|ARM64.Build.0 = Release|ARM64
{B1863396-A667-42DB-97AC-C5E033CEE321}.Release|ARM64.Deploy.0 = Release|ARM64
{B1863396-A667-42DB-97AC-C5E033CEE321}.Release|x64.ActiveCfg = Release|x64
{B1863396-A667-42DB-97AC-C5E033CEE321}.Release|x64.Build.0 = Release|x64
{B1863396-A667-42DB-97AC-C5E033CEE321}.Release|x64.Deploy.0 = Release|x64
{B385D394-19CC-48BC-827E-AF9ADCE559E0}.Debug|ARM64.ActiveCfg = Debug|ARM64
{B385D394-19CC-48BC-827E-AF9ADCE559E0}.Debug|ARM64.Build.0 = Debug|ARM64
{B385D394-19CC-48BC-827E-AF9ADCE559E0}.Debug|ARM64.Deploy.0 = Debug|ARM64
{B385D394-19CC-48BC-827E-AF9ADCE559E0}.Debug|x64.ActiveCfg = Debug|x64
{B385D394-19CC-48BC-827E-AF9ADCE559E0}.Debug|x64.Build.0 = Debug|x64
{B385D394-19CC-48BC-827E-AF9ADCE559E0}.Debug|x64.Deploy.0 = Debug|x64
{B385D394-19CC-48BC-827E-AF9ADCE559E0}.Release|ARM64.ActiveCfg = Release|ARM64
{B385D394-19CC-48BC-827E-AF9ADCE559E0}.Release|ARM64.Build.0 = Release|ARM64
{B385D394-19CC-48BC-827E-AF9ADCE559E0}.Release|ARM64.Deploy.0 = Release|ARM64
{B385D394-19CC-48BC-827E-AF9ADCE559E0}.Release|x64.ActiveCfg = Release|x64
{B385D394-19CC-48BC-827E-AF9ADCE559E0}.Release|x64.Build.0 = Release|x64
{B385D394-19CC-48BC-827E-AF9ADCE559E0}.Release|x64.Deploy.0 = Release|x64
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {4989C8A8-DCF1-48A9-9012-23369AB01403}
EndGlobalSection
EndGlobal
================================================
FILE: KSystemInformer/KSystemInformer.slnx
================================================
================================================
FILE: KSystemInformer/KSystemInformer.vcxproj
================================================
Debug
ARM64
Release
ARM64
Debug
x64
Release
x64
{F4853009-C5D2-4A25-BE4D-BB0D9F84E2FF}
{dd38f7fc-d7bd-488b-9242-7d8754cde80d}
Driver
KSystemInformer
$(SystemInformerRoot)KSystemInformer\obj\$(Configuration)$(PlatformArchitecture)\$(ProjectName)\
$(SystemInformerRoot)KSystemInformer\bin\$(Configuration)$(PlatformArchitecture)\
SystemInformer
ksi.lib;FltMgr.lib;ksecdd.lib;kphlib_km.lib;volatileaccessk.lib;umaccess.lib%(AdditionalDependencies)
%(AdditionalIncludeDirectories);$(SystemInformerRoot)kphlib\include\
================================================
FILE: KSystemInformer/KSystemInformer.vcxproj.filters
================================================
{4FC737F1-C7A5-4376-A066-2A32D752A2FF}
cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx
{93995380-89BD-4b04-88EB-625FBE52EBFB}
h;hpp;hxx;hm;inl;inc;xsd
{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}
rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms
Source Files
Source Files
Source Files
Source Files
Source Files
Source Files
Source Files
Source Files
Source Files
Source Files
Source Files
Source Files
Source Files
Source Files
Source Files
Source Files
Source Files
Source Files
Source Files
Source Files
Source Files
Source Files
Source Files
Source Files
Source Files
Source Files
Source Files
Source Files
Source Files
Source Files
Source Files
Source Files
Source Files
Source Files
Source Files
Source Files
Source Files
Source Files
Source Files
Source Files
Resource Files
Header Files
Header Files
Header Files
Header Files
Header Files
Header Files
Header Files
================================================
FILE: KSystemInformer/README.md
================================================
# System Informer Kernel
- optimizes retrieval of information from the system
- enables broader inspection into the system
- informs of system activity in realtime
- assists in removal of malware
## Security
[Security Policies and Procedures](../SECURITY.md)
Because the information exposed through the driver enables, by design, broader
access into the system. Access is strictly limited to verified callers.
Access is restricted based on the state of the calling process. This involves
signing, privilege, and other state checks. If the client does not meet the
state requirements they are denied access.
Any binaries built with the intention of loading into System Informer must
have a `.sig` from the key pair integrated into the build process and driver.
Or be signed by Microsoft or an anti-malware vendor. Loading of unsigned
modules will restrict access to the driver. Third-party plugins are supported,
however when they are loaded access to the driver will be restricted as they
are not signed.
The driver tracks verified clients, restricts access by other actors on the
system, and denies access if the process is tampered with. The intention is to
discourage exploitation of the client when the driver is active. If tampering
or exploitation is detected the client is denied access.
## Development
Developers may suppress protections and state requirements by disabling secure
boot (if applicable) then enabling debug and test signing mode. Doing this will
permit the client process to be debugged and enables use of test signing keys.
```
bcdedit /debug on
bcdedit /set testsigning on
```
Developers may generate their own key pair for use in their environment.
1. execute `tools\CustomSignTool\bin\Release64\CustomSignTool.exe createkeypair kph.key public.key`
2. copy `kph.key` into `tools\CustomSignTool\resources`
3. copy the bytes for `public.key` into the `KphpPublicKeys` array in [verify.c](verify.c)
- `KphKeyTypeProd` does not require debug and test signing enablement
- `KphKeyTypeTest` requires debug and test signing enablement
4. regenerate dynamic data `tools\CustomBuildTool\bin\Release\amd64\CustomBuildTool.exe -dyndata`
5. rebuild the kernel _and_ user components
It's important to regenerate the dynamic data and build the kernel and user
components after creating and placing the new key pair. The driver uses the
public key to validate the dynamic data and user mode binaries. Neglecting to
do this will result in failures to load or connect to the driver.
Once these steps are completed builds of System Informer components will
generate a `.sig` file next to the output binaries. And the developer built
driver will use the specified key when doing verification checks. Any plugins
not built through the regular build process must also have their own `.sig`.
================================================
FILE: KSystemInformer/alloc.c
================================================
/*
* Copyright (c) 2022 Winsider Seminars & Solutions, Inc. All rights reserved.
*
* This file is part of System Informer.
*
* Authors:
*
* jxy-s 2022-2026
*
*/
#include
#include
typedef struct _KPH_LOOKASIDE_INIT
{
SIZE_T Size;
ULONG Tag;
} KPH_LOOKASIDE_INIT, *PKPH_LOOKASIDE_INIT;
KPH_PROTECTED_DATA_SECTION_RO_PUSH();
static const UNICODE_STRING KphpPagedLookasideObjectTypeName = RTL_CONSTANT_STRING(L"KphPagedLookaside");
static const UNICODE_STRING KphpNPagedLookasideObjectTypeName = RTL_CONSTANT_STRING(L"KphNPagedLookaside");
KPH_PROTECTED_DATA_SECTION_RO_POP();
KPH_PROTECTED_DATA_SECTION_PUSH();
static PKPH_OBJECT_TYPE KphpPagedLookasideObjectType = NULL;
static PKPH_OBJECT_TYPE KphpNPagedLookasideObjectType = NULL;
static ULONG KphpRandomPoolTag = 0;
KPH_PROTECTED_DATA_SECTION_POP();
/**
* \brief Allocates non-page-able memory.
*
* \param[in] NumberOfBytes The number of bytes to allocate.
* \param[in] Tag The pool tag to use.
*
* \return Allocated non-page-able memory. Null on failure.
*/
_IRQL_requires_max_(DISPATCH_LEVEL)
_Return_allocatesMem_size_(NumberOfBytes)
PVOID KphAllocateNPaged(
_In_ SIZE_T NumberOfBytes,
_In_ ULONG Tag
)
{
KPH_NPAGED_CODE_DISPATCH_MAX();
if (KphpRandomPoolTag)
{
Tag = KphpRandomPoolTag;
}
#pragma warning(suppress: 4995) // suppress deprecation warning
return ExAllocatePoolZero(NonPagedPoolNx, NumberOfBytes, Tag);
}
/**
* \brief Frees memory.
*
* \param[in] Memory The memory to free.
* \param[in] Tag The tag associated with the allocation.
*/
_IRQL_requires_max_(DISPATCH_LEVEL)
VOID KphFree(
_FreesMem_ PVOID Memory,
_In_ ULONG Tag
)
{
KPH_NPAGED_CODE_DISPATCH_MAX();
NT_ASSERT(Memory);
if (KphpRandomPoolTag)
{
Tag = KphpRandomPoolTag;
}
#pragma warning(suppress: 4995) // suppress deprecation warning
ExFreePoolWithTag(Memory, Tag);
}
/**
* \brief Initialized a non-page-able lookaside list.
*
* \param[out] Lookaside The lookaside list to initialize.
* \param[in] Size The size of entries in the lookaside list.
* \param[in] Tag The pool tag to use.
*/
_IRQL_requires_max_(DISPATCH_LEVEL)
VOID KphInitializeNPagedLookaside(
_Out_ PNPAGED_LOOKASIDE_LIST Lookaside,
_In_ SIZE_T Size,
_In_ ULONG Tag
)
{
KPH_NPAGED_CODE_DISPATCH_MAX();
if (KphpRandomPoolTag)
{
Tag = KphpRandomPoolTag;
}
#pragma warning(suppress: 4995) // suppress deprecation warning
ExInitializeNPagedLookasideList(Lookaside,
NULL,
NULL,
POOL_NX_ALLOCATION,
Size,
Tag,
0);
}
/**
* \brief Deletes a non-page-able lookaside list.
*
* \param[in,out] Lookaside The lookaside to delete.
*/
_IRQL_requires_max_(DISPATCH_LEVEL)
VOID KphDeleteNPagedLookaside(
_Inout_ PNPAGED_LOOKASIDE_LIST Lookaside
)
{
KPH_NPAGED_CODE_DISPATCH_MAX();
#pragma warning(suppress: 4995) // suppress deprecation warning
ExDeleteNPagedLookasideList(Lookaside);
}
/**
* \brief Allocates non-page-able memory from the lookaside list.
*
* \param[in,out] Lookaside The lookaside list to allocate from.
*
* \return Allocated non-page-able memory. Null on failure.
*/
_IRQL_requires_max_(DISPATCH_LEVEL)
_Return_allocatesMem_size_(Lookaside->L.Size)
PVOID KphAllocateFromNPagedLookaside(
_Inout_ PNPAGED_LOOKASIDE_LIST Lookaside
)
{
PVOID memory;
KPH_NPAGED_CODE_DISPATCH_MAX();
#pragma warning(suppress: 4995) // suppress deprecation warning
memory = ExAllocateFromNPagedLookasideList(Lookaside);
if (memory)
{
RtlZeroMemory(memory, Lookaside->L.Size);
}
return memory;
}
/**
* \brief Frees non-page-able memory back to the lookaside list.
*
* \param[in,out] Lookaside The lookaside list to free back to.
* \param[in] Memory The memory to free.
*/
_IRQL_requires_max_(DISPATCH_LEVEL)
VOID KphFreeToNPagedLookaside(
_Inout_ PNPAGED_LOOKASIDE_LIST Lookaside,
_In_freesMem_ PVOID Memory
)
{
KPH_NPAGED_CODE_DISPATCH_MAX();
NT_ASSERT(Memory);
#pragma warning(suppress: 4995) // suppress deprecation warning
ExFreeToNPagedLookasideList(Lookaside, Memory);
}
/**
* \brief Allocates a non-page-able lookaside object.
*
* \param[in] Size The size of the lookaside object.
*
* \return Allocated lookaside object, null on allocation failure.
*/
_Function_class_(KPH_TYPE_ALLOCATE_PROCEDURE)
_Return_allocatesMem_size_(Size)
PVOID KSIAPI KphpAllocateNPagedLookasideObject(
_In_ SIZE_T Size
)
{
KPH_NPAGED_CODE_DISPATCH_MAX();
return KphAllocateNPaged(Size, KPH_TAG_NPAGED_LOOKASIDE_OBJECT);
}
/**
* \brief Initializes a non-page-able lookaside object.
*
* \param[in,out] Object The lookaside object to initialize.
* \param[in] Parameter The lookaside initialization parameters.
*
* \return STATUS_SUCCESS
*/
_Function_class_(KPH_TYPE_INITIALIZE_PROCEDURE)
_Must_inspect_result_
NTSTATUS KSIAPI KphpInitializeNPagedLookasideObject(
_Inout_ PVOID Object,
_In_opt_ PVOID Parameter
)
{
PKPH_NPAGED_LOOKASIDE_OBJECT lookaside;
PKPH_LOOKASIDE_INIT init;
KPH_NPAGED_CODE_DISPATCH_MAX();
NT_ASSERT(Parameter);
lookaside = Object;
init = Parameter;
KphInitializeNPagedLookaside((PNPAGED_LOOKASIDE_LIST)lookaside,
init->Size,
init->Tag);
return STATUS_SUCCESS;
}
/**
* \brief Deletes a non-page-able lookaside object.
*
* \param[in,out] Object The lookaside object to delete.
*/
_Function_class_(KPH_TYPE_DELETE_PROCEDURE)
VOID KSIAPI KphpDeleteNPagedLookasideObject(
_Inout_ PVOID Object
)
{
PKPH_NPAGED_LOOKASIDE_OBJECT lookaside;
KPH_NPAGED_CODE_DISPATCH_MAX();
lookaside = Object;
KphDeleteNPagedLookaside((PNPAGED_LOOKASIDE_LIST)Object);
}
/**
* \brief Frees a non-page-page lookaside object.
*
* \param[in] Object The lookaside object to free.
*/
_Function_class_(KPH_TYPE_FREE_PROCEDURE)
VOID KSIAPI KphpFreeNPagedLookasideObject(
_In_freesMem_ PVOID Object
)
{
KPH_NPAGED_CODE_DISPATCH_MAX();
KphFree(Object, KPH_TAG_NPAGED_LOOKASIDE_OBJECT);
}
/**
* \brief Creates a non-page-able lookaside object.
*
* \param[out] Lookaside Set to the new non-page-able lookaside object on success.
* \param[in] Size The size of entries in the lookaside list.
* \param[in] Tag The pool tag to use.
*
* \return Successful or errant status.
*/
_IRQL_requires_max_(DISPATCH_LEVEL)
NTSTATUS KphCreateNPagedLookasideObject(
_Outptr_result_nullonfailure_ PKPH_NPAGED_LOOKASIDE_OBJECT* Lookaside,
_In_ SIZE_T Size,
_In_ ULONG Tag
)
{
NTSTATUS status;
KPH_LOOKASIDE_INIT init;
KPH_NPAGED_CODE_DISPATCH_MAX();
if (KphpRandomPoolTag)
{
Tag = KphpRandomPoolTag;
}
init.Size = Size;
init.Tag = Tag;
*Lookaside = NULL;
status = KphCreateObject(KphpNPagedLookasideObjectType,
sizeof(KPH_NPAGED_LOOKASIDE_OBJECT),
Lookaside,
&init);
if (!NT_SUCCESS(status))
{
*Lookaside = NULL;
}
return status;
}
/**
* \brief Allocates from a non-page-able lookaside object.
*
* \param[in,out] Lookaside The non-page-able lookaside object to allocate from.
*
* \return Allocated non-page-able memory. Null on failure.
*/
_IRQL_requires_max_(DISPATCH_LEVEL)
_Return_allocatesMem_size_(Lookaside->L.Size)
PVOID KphAllocateFromNPagedLookasideObject(
_Inout_ PKPH_NPAGED_LOOKASIDE_OBJECT Lookaside
)
{
KPH_NPAGED_CODE_DISPATCH_MAX();
return KphAllocateFromNPagedLookaside((PNPAGED_LOOKASIDE_LIST)Lookaside);
}
/**
* \brief Frees non-page-able memory back to the lookaside object.
*
* \param[in,out] Lookaside The lookaside object to free back to.
* \param[in] Memory The memory to free.
*/
_IRQL_requires_max_(DISPATCH_LEVEL)
VOID KphFreeToNPagedLookasideObject(
_Inout_ PKPH_NPAGED_LOOKASIDE_OBJECT Lookaside,
_In_freesMem_ PVOID Memory
)
{
KPH_NPAGED_CODE_DISPATCH_MAX();
KphFreeToNPagedLookaside((PNPAGED_LOOKASIDE_LIST)Lookaside, Memory);
}
KPH_PAGED_FILE();
/**
* \brief Allocates page-able memory.
*
* \param[in] NumberOfBytes The number of bytes to allocate.
* \param[in] Tag The pool tag to use.
*
* \return Allocated page-able memory. Null on failure.
*/
_IRQL_requires_max_(APC_LEVEL)
_Return_allocatesMem_size_(NumberOfBytes)
PVOID KphAllocatePaged(
_In_ SIZE_T NumberOfBytes,
_In_ ULONG Tag
)
{
KPH_PAGED_CODE();
if (KphpRandomPoolTag)
{
Tag = KphpRandomPoolTag;
}
#pragma warning(suppress: 4995) // suppress deprecation warning
return ExAllocatePoolZero(PagedPool, NumberOfBytes, Tag);
}
/**
* \brief Initialized a page-able lookaside list.
*
* \param[in] Lookaside The lookaside list to initialize.
* \param[in] Size The size of entries in the lookaside list.
* \param[in] Tag The pool tag to use.
*/
_IRQL_requires_max_(APC_LEVEL)
VOID KphInitializePagedLookaside(
_Out_ PPAGED_LOOKASIDE_LIST Lookaside,
_In_ SIZE_T Size,
_In_ ULONG Tag
)
{
KPH_PAGED_CODE();
if (KphpRandomPoolTag)
{
Tag = KphpRandomPoolTag;
}
#pragma warning(suppress: 4995) // suppress deprecation warning
ExInitializePagedLookasideList(Lookaside, NULL, NULL, 0, Size, Tag, 0);
}
/**
* \brief Deletes a page-able lookaside list.
*
* \param[in,out] Lookaside The lookaside to delete.
*/
_IRQL_requires_max_(APC_LEVEL)
VOID KphDeletePagedLookaside(
_Inout_ PPAGED_LOOKASIDE_LIST Lookaside
)
{
KPH_PAGED_CODE();
#pragma warning(suppress: 4995) // suppress deprecation warning
ExDeletePagedLookasideList(Lookaside);
}
/**
* \brief Allocates page-able memory from the lookaside list.
*
* \param[in,out] Lookaside The lookaside list to allocate from.
*
* \return Allocated page-able memory. Null on failure.
*/
_IRQL_requires_max_(APC_LEVEL)
_Return_allocatesMem_size_(Lookaside->L.Size)
PVOID KphAllocateFromPagedLookaside(
_Inout_ PPAGED_LOOKASIDE_LIST Lookaside
)
{
PVOID memory;
KPH_PAGED_CODE();
#pragma warning(suppress: 4995) // suppress deprecation warning
memory = ExAllocateFromPagedLookasideList(Lookaside);
if (memory)
{
RtlZeroMemory(memory, Lookaside->L.Size);
}
return memory;
}
/**
* \brief Frees page-able memory back to the lookaside list.
*
* \param[in,out] Lookaside The lookaside list to free back to.
* \param[in] Memory The memory to free.
*/
_IRQL_requires_max_(APC_LEVEL)
VOID KphFreeToPagedLookaside(
_Inout_ PPAGED_LOOKASIDE_LIST Lookaside,
_In_freesMem_ PVOID Memory
)
{
KPH_PAGED_CODE();
NT_ASSERT(Memory);
#pragma warning(suppress: 4995) // suppress deprecation warning
ExFreeToPagedLookasideList(Lookaside, Memory);
}
/**
* \brief Allocates a page-able lookaside object.
*
* \param[in] Size The size of the lookaside object.
*
* \return Allocated lookaside object, null on allocation failure.
*/
_Function_class_(KPH_TYPE_ALLOCATE_PROCEDURE)
_Return_allocatesMem_size_(Size)
PVOID KSIAPI KphpAllocatePagedLookasideObject(
_In_ SIZE_T Size
)
{
KPH_PAGED_CODE();
return KphAllocateNPaged(Size, KPH_TAG_PAGED_LOOKASIDE_OBJECT);
}
/**
* \brief Initializes a page-able lookaside object.
*
* \param[in,out] Object The lookaside object to initialize.
* \param[in] Parameter The lookaside initialization parameters.
*
* \return STATUS_SUCCESS
*/
_Function_class_(KPH_TYPE_INITIALIZE_PROCEDURE)
_Must_inspect_result_
NTSTATUS KSIAPI KphpInitializePagedLookasideObject(
_Inout_ PVOID Object,
_In_opt_ PVOID Parameter
)
{
PKPH_PAGED_LOOKASIDE_OBJECT lookaside;
PKPH_LOOKASIDE_INIT init;
KPH_PAGED_CODE();
NT_ASSERT(Parameter);
lookaside = Object;
init = Parameter;
KphInitializePagedLookaside((PPAGED_LOOKASIDE_LIST)lookaside,
init->Size,
init->Tag);
return STATUS_SUCCESS;
}
/**
* \brief Deletes a page-able lookaside object.
*
* \param[in,out] Object The lookaside object to delete.
*/
_Function_class_(KPH_TYPE_DELETE_PROCEDURE)
VOID KSIAPI KphpDeletePagedLookasideObject(
_Inout_ PVOID Object
)
{
PKPH_PAGED_LOOKASIDE_OBJECT lookaside;
KPH_PAGED_CODE();
lookaside = Object;
KphDeletePagedLookaside((PPAGED_LOOKASIDE_LIST)Object);
}
/**
* \brief Frees a page-page lookaside object.
*
* \param[in] Object The lookaside object to free.
*/
_Function_class_(KPH_TYPE_FREE_PROCEDURE)
VOID KSIAPI KphpFreePagedLookasideObject(
_In_freesMem_ PVOID Object
)
{
KPH_PAGED_CODE();
KphFree(Object, KPH_TAG_PAGED_LOOKASIDE_OBJECT);
}
/**
* \brief Creates a page-able lookaside object.
*
* \param[out] Lookaside Set to the new page-able lookaside object on success.
* \param[in] Size The size of entries in the lookaside list.
* \param[in] Tag The pool tag to use.
*
* \return Successful or errant status.
*/
_IRQL_requires_max_(APC_LEVEL)
NTSTATUS KphCreatePagedLookasideObject(
_Outptr_result_nullonfailure_ PKPH_PAGED_LOOKASIDE_OBJECT* Lookaside,
_In_ SIZE_T Size,
_In_ ULONG Tag
)
{
NTSTATUS status;
KPH_LOOKASIDE_INIT init;
KPH_PAGED_CODE();
if (KphpRandomPoolTag)
{
Tag = KphpRandomPoolTag;
}
init.Size = Size;
init.Tag = Tag;
*Lookaside = NULL;
status = KphCreateObject(KphpPagedLookasideObjectType,
sizeof(KPH_PAGED_LOOKASIDE_OBJECT),
Lookaside,
&init);
if (!NT_SUCCESS(status))
{
*Lookaside = NULL;
}
return status;
}
/**
* \brief Allocates from a page-able lookaside object.
*
* \param[in,out] Lookaside The page-able lookaside object to allocate from.
*
* \return Allocated page-able memory. Null on failure.
*/
_IRQL_requires_max_(APC_LEVEL)
_Return_allocatesMem_size_(Lookaside->L.Size)
PVOID KphAllocateFromPagedLookasideObject(
_Inout_ PKPH_PAGED_LOOKASIDE_OBJECT Lookaside
)
{
KPH_PAGED_CODE();
return KphAllocateFromPagedLookaside((PPAGED_LOOKASIDE_LIST)Lookaside);
}
/**
* \brief Frees page-able memory back to the lookaside object.
*
* \param[in,out] Lookaside The lookaside object to free back to.
* \param[in] Memory The memory to free.
*/
_IRQL_requires_max_(APC_LEVEL)
VOID KphFreeToPagedLookasideObject(
_Inout_ PKPH_PAGED_LOOKASIDE_OBJECT Lookaside,
_In_freesMem_ PVOID Memory
)
{
KPH_PAGED_CODE();
KphFreeToPagedLookaside((PPAGED_LOOKASIDE_LIST)Lookaside, Memory);
}
/**
* \brief Makes a byte suitable for a pool tag.
*
* \param[in] Byte A byte to make suitable for a pool tag.
*
* \return A byte suitable for a pool tag.
*/
_IRQL_requires_max_(PASSIVE_LEVEL)
BYTE KphpMakePoolTagByte(
_In_ BYTE Byte
)
{
BYTE outByte;
KPH_PAGED_CODE_PASSIVE();
outByte = Byte % '~';
if (outByte < ' ')
{
outByte += ' ';
}
return outByte;
}
/**
* \brief Generates a randomized pool tag.
*
* \return Randomized pool tag.
*/
_IRQL_requires_max_(PASSIVE_LEVEL)
ULONG KphpGenerateRandomPoolTag(
VOID
)
{
ULONG64 interruptTime;
ULONG seed;
ULONG randomValue;
BYTE byte;
ULONG poolTag;
KPH_PAGED_CODE_PASSIVE();
interruptTime = KeQueryInterruptTime();
seed = (ULONG)(interruptTime >> 32);
seed |= (ULONG)interruptTime;
seed ^= PtrToUlong(PsGetCurrentThread());
do
{
randomValue = RtlRandomEx(&seed);
} while (!randomValue);
poolTag = 0;
byte = KphpMakePoolTagByte(randomValue & 0xff);
poolTag = (ULONG)byte;
byte = KphpMakePoolTagByte((randomValue >> 8) & 0xff);
poolTag |= ((ULONG)byte << 8);
byte = KphpMakePoolTagByte((randomValue >> 16) & 0xff);
poolTag |= ((ULONG)byte << 16);
byte = KphpMakePoolTagByte((randomValue >> 24) & 0xff);
poolTag |= ((ULONG)byte << 24);
return poolTag;
}
/**
* \brief Initializes allocation infrastructure.
*/
_IRQL_requires_max_(PASSIVE_LEVEL)
VOID KphInitializeAlloc(
VOID
)
{
KPH_OBJECT_TYPE_INFO typeInfo;
KPH_PAGED_CODE_PASSIVE();
if (KphParameterFlags.RandomizedPoolTag)
{
KphpRandomPoolTag = KphpGenerateRandomPoolTag();
}
typeInfo.Allocate = KphpAllocatePagedLookasideObject;
typeInfo.Initialize = KphpInitializePagedLookasideObject;
typeInfo.Delete = KphpDeletePagedLookasideObject;
typeInfo.Free = KphpFreePagedLookasideObject;
typeInfo.Flags = 0;
KphCreateObjectType(&KphpPagedLookasideObjectTypeName,
&typeInfo,
&KphpPagedLookasideObjectType);
typeInfo.Allocate = KphpAllocateNPagedLookasideObject;
typeInfo.Initialize = KphpInitializeNPagedLookasideObject;
typeInfo.Delete = KphpDeleteNPagedLookasideObject;
typeInfo.Free = KphpFreeNPagedLookasideObject;
typeInfo.Flags = 0;
KphCreateObjectType(&KphpNPagedLookasideObjectTypeName,
&typeInfo,
&KphpNPagedLookasideObjectType);
}
================================================
FILE: KSystemInformer/alpc.c
================================================
/*
* Copyright (c) 2022 Winsider Seminars & Solutions, Inc. All rights reserved.
*
* This file is part of System Informer.
*
* Authors:
*
* jxy-s 2022-2026
*
*/
#include
#include
KPH_PAGED_FILE();
#define KPH_ALPC_NAME_BUFFER_SIZE ((MAX_PATH * 2) + sizeof(OBJECT_NAME_INFORMATION))
/**
* \brief References any communication ports associated with the input port.
* The caller must dereference any returned objects by calling ObDereferenceObject.
*
* \param[in] Dyn Dynamic configuration.
* \param[in] Port The ALPC port to references the associated ports of.
* \param[out] ConnectionPort Set to the connection port.
* \param[out] ServerPort Set to the server port.
* \param[out] ClientPort Set to the client port.
*
* \return Successful or errant status.
*/
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KphpReferenceAlpcCommunicationPorts(
_In_ PKPH_DYN Dyn,
_In_ PVOID Port,
_Outptr_result_maybenull_ PVOID* ConnectionPort,
_Outptr_result_maybenull_ PVOID* ServerPort,
_Outptr_result_maybenull_ PVOID* ClientPort
)
{
NTSTATUS status;
PVOID communicationInfo;
PVOID handleTable;
PVOID handleTableLock;
BOOLEAN typeMismatch;
PVOID connectionPort;
PVOID serverPort;
PVOID clientPort;
KPH_PAGED_CODE_PASSIVE();
*ConnectionPort = NULL;
*ServerPort = NULL;
*ClientPort = NULL;
connectionPort = NULL;
serverPort = NULL;
clientPort = NULL;
if ((Dyn->AlpcCommunicationInfo == ULONG_MAX) ||
(Dyn->AlpcHandleTable == ULONG_MAX) ||
(Dyn->AlpcHandleTableLock == ULONG_MAX))
{
status = STATUS_NOINTERFACE;
goto Exit;
}
communicationInfo = *(PVOID*)Add2Ptr(Port, Dyn->AlpcCommunicationInfo);
if (!communicationInfo)
{
status = STATUS_NOT_FOUND;
goto Exit;
}
handleTable = Add2Ptr(communicationInfo, Dyn->AlpcHandleTable);
handleTableLock = Add2Ptr(handleTable, Dyn->AlpcHandleTableLock);
typeMismatch = FALSE;
FltAcquirePushLockShared(handleTableLock);
if (Dyn->AlpcConnectionPort != ULONG_MAX)
{
connectionPort = *(PVOID*)Add2Ptr(communicationInfo,
Dyn->AlpcConnectionPort);
if (connectionPort)
{
if (ObGetObjectType(connectionPort) != *AlpcPortObjectType)
{
typeMismatch = TRUE;
connectionPort = NULL;
}
else
{
ObReferenceObject(connectionPort);
}
}
}
if (Dyn->AlpcServerCommunicationPort != ULONG_MAX)
{
serverPort = *(PVOID*)Add2Ptr(communicationInfo,
Dyn->AlpcServerCommunicationPort);
if (serverPort)
{
if (ObGetObjectType(serverPort) != *AlpcPortObjectType)
{
typeMismatch = TRUE;
serverPort = NULL;
}
else
{
ObReferenceObject(serverPort);
}
}
}
if (Dyn->AlpcClientCommunicationPort != ULONG_MAX)
{
clientPort = *(PVOID*)Add2Ptr(communicationInfo,
Dyn->AlpcClientCommunicationPort);
if (clientPort)
{
if (ObGetObjectType(clientPort) != *AlpcPortObjectType)
{
typeMismatch = TRUE;
clientPort = NULL;
}
else
{
ObReferenceObject(clientPort);
}
}
}
FltReleasePushLock(handleTableLock);
if (typeMismatch)
{
status = STATUS_OBJECT_TYPE_MISMATCH;
goto Exit;
}
*ConnectionPort = connectionPort;
connectionPort = NULL;
*ServerPort = serverPort;
serverPort = NULL;
*ClientPort = clientPort;
clientPort = NULL;
status = STATUS_SUCCESS;
Exit:
if (connectionPort)
{
ObDereferenceObject(connectionPort);
}
if (serverPort)
{
ObDereferenceObject(serverPort);
}
if (clientPort)
{
ObDereferenceObject(clientPort);
}
return status;
}
/**
* \brief Retrieves the basic information for an ALPC port.
*
* \param[in] Dyn Dynamic configuration.
* \param[in] Port The ALPC port to get the basic information of.
* \param[out] Info Populated with the basic information.
*
* \return Successful or errant status.
*/
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KphpAlpcBasicInfo(
_In_ PKPH_DYN Dyn,
_In_ PVOID Port,
_Out_ PKPH_ALPC_BASIC_INFORMATION Info
)
{
PEPROCESS process;
PVOID portObjectLock;
KPH_PAGED_CODE_PASSIVE();
RtlZeroMemory(Info, sizeof(*Info));
if ((Dyn->AlpcOwnerProcess == ULONG_MAX) ||
(Dyn->AlpcPortObjectLock == ULONG_MAX))
{
return STATUS_NOINTERFACE;
}
//
// The OS uses the first bit of the OwnerProcess to denote if it is valid,
// if the first bit of the OwnerProcess is set is it invalid. Checking the
// bit should be done under the PortObjectLock.
// See: ntoskrnl!AlpcpPortQueryServerSessionInfo
//
portObjectLock = Add2Ptr(Port, Dyn->AlpcPortObjectLock);
FltAcquirePushLockShared(portObjectLock);
process = *(PEPROCESS*)Add2Ptr(Port, Dyn->AlpcOwnerProcess);
if (process && (((ULONG_PTR)process & 1) == 0))
{
Info->OwnerProcessId = PsGetProcessId(process);
}
FltReleasePushLock(portObjectLock);
if ((Dyn->AlpcAttributes != ULONG_MAX) &&
(Dyn->AlpcAttributesFlags != ULONG_MAX))
{
Info->Flags = *(PULONG)Add2Ptr(Add2Ptr(Port, Dyn->AlpcAttributes),
Dyn->AlpcAttributesFlags);
}
if (Dyn->AlpcPortContext != ULONG_MAX)
{
Info->PortContext = *(PVOID*)Add2Ptr(Port, Dyn->AlpcPortContext);
}
if (Dyn->AlpcSequenceNo != ULONG_MAX)
{
Info->SequenceNo = *(PULONG)Add2Ptr(Port, Dyn->AlpcSequenceNo);
}
if (Dyn->AlpcState != ULONG_MAX)
{
Info->State = *(PULONG)Add2Ptr(Port, Dyn->AlpcState);
}
return STATUS_SUCCESS;
}
/**
* \brief Retrieves the communication information for an ALPC port.
*
* \param[in] Dyn Dynamic configuration.
* \param[in] Port The ALPC port to get the information of.
* \param[out] Info Populated with the communication information.
*
* \return Successful or errant status.
*/
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KphpAlpcCommunicationInfo(
_In_ PKPH_DYN Dyn,
_In_ PVOID Port,
_Out_ PKPH_ALPC_COMMUNICATION_INFORMATION Info
)
{
NTSTATUS status;
PVOID connectionPort;
PVOID serverPort;
PVOID clientPort;
KPH_PAGED_CODE_PASSIVE();
RtlZeroMemory(Info, sizeof(*Info));
status = KphpReferenceAlpcCommunicationPorts(Dyn,
Port,
&connectionPort,
&serverPort,
&clientPort);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
GENERAL,
"KphpReferenceAlpcCommunicationPorts failed: %!STATUS!",
status);
connectionPort = NULL;
serverPort = NULL;
clientPort = NULL;
goto Exit;
}
if (connectionPort)
{
status = KphpAlpcBasicInfo(Dyn,
connectionPort,
&Info->ConnectionPort);
if (!NT_SUCCESS(status))
{
goto Exit;
}
}
if (serverPort)
{
status = KphpAlpcBasicInfo(Dyn,
serverPort,
&Info->ServerCommunicationPort);
if (!NT_SUCCESS(status))
{
goto Exit;
}
}
if (clientPort)
{
status = KphpAlpcBasicInfo(Dyn,
clientPort,
&Info->ClientCommunicationPort);
if (!NT_SUCCESS(status))
{
goto Exit;
}
}
Exit:
if (connectionPort)
{
ObDereferenceObject(connectionPort);
}
if (serverPort)
{
ObDereferenceObject(serverPort);
}
if (clientPort)
{
ObDereferenceObject(clientPort);
}
return status;
}
/**
* \brief Utility function for copying the ALPC port name into an output string.
*
* \param[in] Port The ALPC port to copy the name of.
* \param[in] NameBuffer Preallocated name buffer to use to get the name.
* \param[in,out] Buffer The space to write the string.
* \param[in,out] RemainingLength The remaining space to write the string.
* \param[in,out] ReturnLength Updated by number of bytes written or required.
* \param[out] String The string to populate.
*
* \return Successful or errant status.
*/
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KphpAlpcCopyPortName(
_In_ PVOID Port,
_In_bytecount_(KPH_ALPC_NAME_BUFFER_SIZE) POBJECT_NAME_INFORMATION NameBuffer,
_Inout_ PVOID* Buffer,
_Inout_ PULONG RemainingLength,
_Inout_ PULONG ReturnLength,
_Out_opt_ PUNICODE_STRING String
)
{
NTSTATUS status;
ULONG returnLength;
KPH_PAGED_CODE_PASSIVE();
if (String)
{
RtlZeroMemory(String, sizeof(*String));
}
status = KphQueryNameObject(Port,
NameBuffer,
KPH_ALPC_NAME_BUFFER_SIZE,
&returnLength);
if (!NT_SUCCESS(status) || (NameBuffer->Name.Length == 0))
{
//
// We're best effort here.
//
return STATUS_SUCCESS;
}
status = RtlULongAdd(*ReturnLength, NameBuffer->Name.Length, ReturnLength);
if (!NT_SUCCESS(status))
{
return status;
}
if (!String || (NameBuffer->Name.Length > *RemainingLength))
{
//
// Success, return length is updated with the needed length.
//
return STATUS_SUCCESS;
}
String->Buffer = *Buffer;
String->MaximumLength = NameBuffer->Name.Length;
*Buffer = Add2Ptr(*Buffer, NameBuffer->Name.Length);
*RemainingLength -= NameBuffer->Name.Length;
RtlCopyUnicodeString(String, &NameBuffer->Name);
return STATUS_SUCCESS;
}
/**
* \brief Retrieves the names of the communication ports for a given ALPC port.
*
* \param[in] Dyn Dynamic configuration.
* \param[in] Port The port to retrieve the names of.
* \param[out] Info Populated with the name information.
* \param[in] InfoLength The length of the information buffer.
* \param[out] ReturnLength Receives the number of bytes written or required.
*
* \return Successful or errant status.
*/
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KphpAlpcCommunicationNamesInfo(
_In_ PKPH_DYN Dyn,
_In_ PVOID Port,
_Out_writes_bytes_opt_(InfoLength) PKPH_ALPC_COMMUNICATION_NAMES_INFORMATION Info,
_In_ ULONG InfoLength,
_Out_ PULONG ReturnLength
)
{
NTSTATUS status;
POBJECT_NAME_INFORMATION nameBuffer;
PVOID connectionPort;
PVOID serverPort;
PVOID clientPort;
PVOID buffer;
ULONG remainingLength;
KPH_PAGED_CODE_PASSIVE();
*ReturnLength = sizeof(KPH_ALPC_COMMUNICATION_NAMES_INFORMATION);
nameBuffer = NULL;
if (Info && (InfoLength >= sizeof(KPH_ALPC_COMMUNICATION_NAMES_INFORMATION)))
{
RtlZeroMemory(Info, sizeof(KPH_ALPC_COMMUNICATION_NAMES_INFORMATION));
remainingLength = (InfoLength - sizeof(KPH_ALPC_COMMUNICATION_NAMES_INFORMATION));
buffer = Add2Ptr(Info, sizeof(KPH_ALPC_COMMUNICATION_NAMES_INFORMATION));
}
else
{
remainingLength = 0;
buffer = NULL;
}
status = KphpReferenceAlpcCommunicationPorts(Dyn,
Port,
&connectionPort,
&serverPort,
&clientPort);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
GENERAL,
"KphpReferenceAlpcCommunicationPorts failed: %!STATUS!",
status);
connectionPort = NULL;
serverPort = NULL;
clientPort = NULL;
goto Exit;
}
nameBuffer = KphAllocatePaged(KPH_ALPC_NAME_BUFFER_SIZE,
KPH_TAG_ALPC_NAME_QUERY);
if (!nameBuffer)
{
status = STATUS_INSUFFICIENT_RESOURCES;
goto Exit;
}
if (connectionPort)
{
status = KphpAlpcCopyPortName(connectionPort,
nameBuffer,
&buffer,
&remainingLength,
ReturnLength,
(Info ? &Info->ConnectionPort : NULL));
if (!NT_SUCCESS(status))
{
goto Exit;
}
}
if (serverPort)
{
status = KphpAlpcCopyPortName(serverPort,
nameBuffer,
&buffer,
&remainingLength,
ReturnLength,
(Info ? &Info->ServerCommunicationPort : NULL));
if (!NT_SUCCESS(status))
{
goto Exit;
}
}
if (clientPort)
{
status = KphpAlpcCopyPortName(clientPort,
nameBuffer,
&buffer,
&remainingLength,
ReturnLength,
(Info ? &Info->ClientCommunicationPort : NULL));
if (!NT_SUCCESS(status))
{
goto Exit;
}
}
if (*ReturnLength > InfoLength)
{
status = STATUS_BUFFER_TOO_SMALL;
}
else
{
status = STATUS_SUCCESS;
}
Exit:
if (nameBuffer)
{
KphFree(nameBuffer, KPH_TAG_ALPC_NAME_QUERY);
}
if (connectionPort)
{
ObDereferenceObject(connectionPort);
}
if (serverPort)
{
ObDereferenceObject(serverPort);
}
if (clientPort)
{
ObDereferenceObject(clientPort);
}
return status;
}
/**
* \brief Queries information about an ALPC port.
*
* \param[in] ProceessHandle Handle to the process where the ALPC handle resides.
* \param[in] PortHandle Handle to the ALPC port to query.
* \param[in] AlpcInformationClass Information class to query.
* \param[out] AlpcInformation Populated with information by ALPC port.
* \param[in] AlpcInformationLength Length of the ALPC information buffer.
* \param[out] ReturnLength Receives the number of bytes written or required.
* \param[in] AccessMode The mode in which to perform access checks.
*
* \return Successful or errant status.
*/
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KphAlpcQueryInformation(
_In_ HANDLE ProcessHandle,
_In_ HANDLE PortHandle,
_In_ KPH_ALPC_INFORMATION_CLASS AlpcInformationClass,
_Out_writes_bytes_opt_(AlpcInformationLength) PVOID AlpcInformation,
_In_ ULONG AlpcInformationLength,
_Out_opt_ PULONG ReturnLength,
_In_ KPROCESSOR_MODE AccessMode
)
{
NTSTATUS status;
PKPH_DYN dyn;
PEPROCESS process;
KAPC_STATE apcState;
PVOID port;
ULONG returnLength;
PVOID buffer;
BYTE stackBuffer[64];
KPROCESSOR_MODE accessMode;
KPH_PAGED_CODE_PASSIVE();
dyn = NULL;
port = NULL;
returnLength = 0;
buffer = NULL;
status = ObReferenceObjectByHandle(ProcessHandle,
0,
*PsProcessType,
AccessMode,
&process,
NULL);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
GENERAL,
"ObReferenceObjectByHandle failed: %!STATUS!",
status);
process = NULL;
goto Exit;
}
if (process == PsInitialSystemProcess)
{
PortHandle = MakeKernelHandle(PortHandle);
accessMode = KernelMode;
}
else
{
if (IsKernelHandle(PortHandle))
{
status = STATUS_INVALID_HANDLE;
goto Exit;
}
accessMode = AccessMode;
}
KeStackAttachProcess(process, &apcState);
status = ObReferenceObjectByHandle(PortHandle,
0,
*AlpcPortObjectType,
accessMode,
&port,
NULL);
KeUnstackDetachProcess(&apcState);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
GENERAL,
"ObReferenceObjectByHandle failed: %!STATUS!",
status);
port = NULL;
goto Exit;
}
switch (AlpcInformationClass)
{
case KphAlpcBasicInformation:
{
KPH_ALPC_BASIC_INFORMATION info;
dyn = KphReferenceDynData();
if (!dyn)
{
status = STATUS_NOINTERFACE;
goto Exit;
}
if (!AlpcInformation ||
(AlpcInformationLength < sizeof(KPH_ALPC_BASIC_INFORMATION)))
{
status = STATUS_INFO_LENGTH_MISMATCH;
returnLength = sizeof(KPH_ALPC_BASIC_INFORMATION);
goto Exit;
}
status = KphpAlpcBasicInfo(dyn, port, &info);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
GENERAL,
"KphpAlpcBasicInfo failed: %!STATUS!",
status);
goto Exit;
}
status = KphCopyToMode(AlpcInformation,
&info,
sizeof(KPH_ALPC_BASIC_INFORMATION),
AccessMode);
if (NT_SUCCESS(status))
{
returnLength = sizeof(KPH_ALPC_BASIC_INFORMATION);
}
break;
}
case KphAlpcCommunicationInformation:
{
KPH_ALPC_COMMUNICATION_INFORMATION info;
dyn = KphReferenceDynData();
if (!dyn)
{
status = STATUS_NOINTERFACE;
goto Exit;
}
if (!AlpcInformation ||
(AlpcInformationLength < sizeof(KPH_ALPC_COMMUNICATION_INFORMATION)))
{
status = STATUS_INFO_LENGTH_MISMATCH;
returnLength = sizeof(KPH_ALPC_COMMUNICATION_INFORMATION);
goto Exit;
}
status = KphpAlpcCommunicationInfo(dyn, port, &info);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
GENERAL,
"KphpAlpcCommunicationInfo failed: %!STATUS!",
status);
goto Exit;
}
status = KphCopyToMode(AlpcInformation,
&info,
sizeof(KPH_ALPC_COMMUNICATION_INFORMATION),
AccessMode);
if (NT_SUCCESS(status))
{
returnLength = sizeof(KPH_ALPC_COMMUNICATION_INFORMATION);
}
break;
}
case KphAlpcCommunicationNamesInformation:
{
ULONG allocatedSize;
PKPH_ALPC_COMMUNICATION_NAMES_INFORMATION info;
dyn = KphReferenceDynData();
if (!dyn)
{
status = STATUS_NOINTERFACE;
goto Exit;
}
allocatedSize = AlpcInformationLength;
if (allocatedSize < sizeof(KPH_ALPC_COMMUNICATION_NAMES_INFORMATION))
{
allocatedSize = sizeof(KPH_ALPC_COMMUNICATION_NAMES_INFORMATION);
}
buffer = KphAllocatePagedA(allocatedSize,
KPH_TAG_ALPC_QUERY,
stackBuffer);
if (!buffer)
{
status = STATUS_INSUFFICIENT_RESOURCES;
goto Exit;
}
info = buffer;
status = KphpAlpcCommunicationNamesInfo(dyn,
port,
info,
AlpcInformationLength,
&returnLength);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
GENERAL,
"KphpAlpcCommunicationNamesInfo failed: %!STATUS!",
status);
goto Exit;
}
if (!AlpcInformation)
{
status = STATUS_BUFFER_TOO_SMALL;
goto Exit;
}
RebaseUnicodeString(&info->ConnectionPort,
buffer,
AlpcInformation);
RebaseUnicodeString(&info->ServerCommunicationPort,
buffer,
AlpcInformation);
RebaseUnicodeString(&info->ClientCommunicationPort,
buffer,
AlpcInformation);
status = KphCopyToMode(AlpcInformation,
info,
returnLength,
AccessMode);
break;
}
default:
{
status = STATUS_INVALID_INFO_CLASS;
break;
}
}
Exit:
if (buffer)
{
KphFreeA(buffer, KPH_TAG_ALPC_QUERY, stackBuffer);
}
if (port)
{
ObDereferenceObject(port);
}
if (process)
{
ObDereferenceObject(process);
}
if (dyn)
{
KphDereferenceObject(dyn);
}
if (ReturnLength)
{
KphWriteULongToMode(ReturnLength, returnLength, AccessMode);
}
return status;
}
================================================
FILE: KSystemInformer/back_trace.c
================================================
/*
* Copyright (c) 2022 Winsider Seminars & Solutions, Inc. All rights reserved.
*
* This file is part of System Informer.
*
* Authors:
*
* jxy-s 2023-2026
*
*/
#include
#include
typedef struct _KPH_STACK_BACK_TRACE_OBJECT
{
KSI_KAPC Apc;
KEVENT CompletedEvent;
ULONG FramesToSkip;
ULONG FramesToCapture;
ULONG CapturedFrames;
ULONG BackTraceHash;
ULONG Flags;
BOOLEAN DoHash;
PKPH_THREAD_CONTEXT Thread;
PVOID BackTrace[ANYSIZE_ARRAY];
} KPH_STACK_BACK_TRACE_OBJECT, *PKPH_STACK_BACK_TRACE_OBJECT;
KPH_PROTECTED_DATA_SECTION_RO_PUSH();
static const UNICODE_STRING KphpStackBackTraceTypeName = RTL_CONSTANT_STRING(L"KphStackBackTrace");
KPH_PROTECTED_DATA_SECTION_RO_POP();
KPH_PROTECTED_DATA_SECTION_PUSH();
static PVOID KphpSelfImageBase = NULL;
static PVOID KphpSelfImageEnd = NULL;
static PVOID KphpKsiImageBase = NULL;
static PVOID KphpKsiImageEnd = NULL;
static PKPH_OBJECT_TYPE KphpStackBackTraceType = NULL;
KPH_PROTECTED_DATA_SECTION_POP();
//
// Sentinel value inserted into the stack back trace when part of the stack
// couldn't be captured for some reason.
//
#define KPH_STACK_SENTINEL_FRAME ((PVOID)(ULONG_PTR)0xbad)
/**
* \brief Tries to add a sentinel frame to the stack back trace.
*
* \param[in] FramesToCapture The number of frames to capture.
* \param[out] BackTrace Buffer to store the back trace in.
* \param[in,out] Frames On input, the number of frames captured so far. If a
* sentinel frame is added, this is incremented.
* \param[in] Flags A combination of KPH_STACK_BACK_TRACE_* flags.
*/
_IRQL_requires_max_(DISPATCH_LEVEL)
VOID KphpTryAddSentinelFrame(
_In_ ULONG FramesToCapture,
_Out_writes_(FramesToCapture) PVOID* BackTrace,
_Inout_ PULONG Frames,
_In_ ULONG Flags
)
{
KPH_NPAGED_CODE_DISPATCH_MAX();
if (FlagOn(Flags, KPH_STACK_BACK_TRACE_NO_SENTINEL))
{
return;
}
if (*Frames < FramesToCapture)
{
BackTrace[*Frames] = KPH_STACK_SENTINEL_FRAME;
(*Frames)++;
}
}
/**
* \brief Captures a stack back trace.
*
* \param[in] FramesToSkip The number of kernel frames to skip.
* \param[in] FramesToCapture The number of frames to capture.
* \param[out] BackTrace Buffer to store the back trace in.
* \param[out] BackTraceHash Optionally receives a hash of the back trace.
* \param[in] Flags A combination of KPH_STACK_BACK_TRACE_* flags.
* \param[in] Thread Optional thread context of the target thread, used for
* internal tracking to prevent accidental recursion.
*
* \return The number of captured frames.
*/
_IRQL_requires_max_(DISPATCH_LEVEL)
_Success_(return != 0)
ULONG KphpCaptureStackBackTrace(
_In_ ULONG FramesToSkip,
_In_ ULONG FramesToCapture,
_Out_writes_(FramesToCapture) PVOID* BackTrace,
_Out_opt_ PULONG BackTraceHash,
_In_ ULONG Flags,
_In_opt_ PKPH_THREAD_CONTEXT Thread
)
{
ULONG frames;
ULONG skip;
KPH_NPAGED_CODE_DISPATCH_MAX();
FramesToSkip += 1;
skip = (FramesToSkip << RTL_STACK_WALKING_MODE_FRAMES_TO_SKIP_SHIFT);
frames = RtlWalkFrameChain(BackTrace, FramesToCapture, skip);
if (frames <= FramesToSkip)
{
frames = 0;
goto ContinueCapture;
}
frames -= FramesToSkip;
if (FlagOn(Flags, KPH_STACK_BACK_TRACE_SKIP_KPH))
{
for (skip = 0; skip < frames; skip++)
{
if (((BackTrace[skip] < KphpSelfImageBase) ||
(BackTrace[skip] >= KphpSelfImageEnd)) &&
((BackTrace[skip] < KphpKsiImageBase) ||
(BackTrace[skip] >= KphpKsiImageEnd)))
{
break;
}
}
if (frames <= skip)
{
frames = 0;
goto ContinueCapture;
}
frames -= skip;
RtlMoveMemory(BackTrace, &BackTrace[skip], frames * sizeof(PVOID));
}
ContinueCapture:
if (!FlagOn(Flags, KPH_STACK_BACK_TRACE_USER_MODE) ||
(KeGetCurrentIrql() == DISPATCH_LEVEL))
{
goto Exit;
}
if (!Thread)
{
KphpTryAddSentinelFrame(FramesToCapture, BackTrace, &frames, Flags);
goto Exit;
}
NT_ASSERT(Thread->EThread == PsGetCurrentThread());
//
// N.B. This path can become accidentally recursive if the user mode stack
// needs to be paged in and another kernel callback lands here again. In
// that case skip the capture.
//
if (Thread->CapturingUserModeStack)
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
GENERAL,
"Skipping user mode stack capture for "
"thread %lu in process %wZ (%lu)",
HandleToULong(Thread->ClientId.UniqueThread),
KphGetThreadImageName(Thread),
HandleToULong(Thread->ClientId.UniqueProcess));
KphpTryAddSentinelFrame(FramesToCapture, BackTrace, &frames, Flags);
goto Exit;
}
Thread->CapturingUserModeStack = TRUE;
__try
{
frames += RtlWalkFrameChain(&BackTrace[frames],
FramesToCapture - frames,
RTL_WALK_USER_MODE_STACK);
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
KphpTryAddSentinelFrame(FramesToCapture, BackTrace, &frames, Flags);
}
Thread->CapturingUserModeStack = FALSE;
Exit:
NT_ASSERT(frames <= FramesToCapture);
if (BackTraceHash)
{
*BackTraceHash = 0;
for (ULONG i = 0; i < frames; i++)
{
#pragma prefast(suppress: 6385)
*BackTraceHash += PtrToUlong(BackTrace[i]);
}
}
return frames;
}
/**
* \brief Captures a stack back trace.
*
* \param[in] FramesToSkip The number of kernel frames to skip.
* \param[in] FramesToCapture The number of frames to capture.
* \param[out] BackTrace Buffer to store the back trace in.
* \param[out] BackTraceHash Optionally receives a hash of the back trace.
* \param[in] Flags A combination of KPH_STACK_BACK_TRACE_* flags.
*
* \return The number of captured frames.
*/
_IRQL_requires_max_(DISPATCH_LEVEL)
_Success_(return != 0)
ULONG KphCaptureStackBackTrace(
_In_ ULONG FramesToSkip,
_In_ ULONG FramesToCapture,
_Out_writes_(FramesToCapture) PVOID* BackTrace,
_Out_opt_ PULONG BackTraceHash,
_In_ ULONG Flags
)
{
ULONG frames;
PKPH_THREAD_CONTEXT thread;
KPH_NPAGED_CODE_DISPATCH_MAX();
if (FlagOn(Flags, KPH_STACK_BACK_TRACE_USER_MODE) &&
(KeGetCurrentIrql() < DISPATCH_LEVEL))
{
thread = KphGetCurrentThreadContext();
}
else
{
thread = NULL;
}
frames = KphpCaptureStackBackTrace(FramesToSkip,
FramesToCapture,
BackTrace,
BackTraceHash,
Flags,
thread);
if (thread)
{
KphDereferenceObject(thread);
}
return frames;
}
KPH_PAGED_FILE();
/**
* \brief Captures the current stack back trace into a back trace object.
*
* \param[in,out] BackTrace The back trace object to populate.
*/
_IRQL_requires_(APC_LEVEL)
_IRQL_requires_same_
VOID KphpCaptureStackBackTraceIntoObject(
_Inout_ PKPH_STACK_BACK_TRACE_OBJECT BackTrace
)
{
PULONG backTraceHash;
ULONG capturedFrames;
KPH_PAGED_CODE_APC();
if (BackTrace->DoHash)
{
backTraceHash = &BackTrace->BackTraceHash;
}
else
{
backTraceHash = NULL;
}
capturedFrames = KphpCaptureStackBackTrace(BackTrace->FramesToSkip,
BackTrace->FramesToCapture,
BackTrace->BackTrace,
backTraceHash,
BackTrace->Flags,
BackTrace->Thread);
BackTrace->CapturedFrames = capturedFrames;
KeSetEvent(&BackTrace->CompletedEvent, EVENT_INCREMENT, FALSE);
}
/**
* \brief APC routine for capturing the stack back trace of a thread.
*
* \param[in] Apc The ACP executed, contained within the back trace object.
* \param[in] NormalRoutine Unused.
* \param[in] NormalContext Unused.
* \param[in] SystemArgument1 Unused.
* \param[in] SystemArgument2 Unused.
*/
_Function_class_(KSI_KKERNEL_ROUTINE)
_IRQL_requires_(APC_LEVEL)
_IRQL_requires_same_
VOID KSIAPI KphpCaptureStackBackTraceThreadSpecialApc(
_In_ PKSI_KAPC Apc,
_Inout_ _Deref_pre_maybenull_ PKNORMAL_ROUTINE *NormalRoutine,
_Inout_ _Deref_pre_maybenull_ PVOID *NormalContext,
_Inout_ _Deref_pre_maybenull_ PVOID *SystemArgument1,
_Inout_ _Deref_pre_maybenull_ PVOID *SystemArgument2
)
{
PKPH_STACK_BACK_TRACE_OBJECT backTrace;
KPH_PAGED_CODE_APC();
UNREFERENCED_PARAMETER(NormalRoutine);
UNREFERENCED_PARAMETER(NormalContext);
UNREFERENCED_PARAMETER(SystemArgument1);
UNREFERENCED_PARAMETER(SystemArgument2);
backTrace = CONTAINING_RECORD(Apc, KPH_STACK_BACK_TRACE_OBJECT, Apc);
KphpCaptureStackBackTraceIntoObject(backTrace);
}
/**
* \brief APC cleanup routine for stack back trace capture.
*
* \param[in] Apc The ACP to clean up.
* \param[in] Reason Unused.
*/
_Function_class_(KSI_KCLEANUP_ROUTINE)
_IRQL_requires_min_(PASSIVE_LEVEL)
_IRQL_requires_max_(APC_LEVEL)
_IRQL_requires_same_
VOID KSIAPI KphpCaptureStackBackTraceThreadSpecialApcCleanup(
_In_ PKSI_KAPC Apc,
_In_ KSI_KAPC_CLEANUP_REASON Reason
)
{
PKPH_STACK_BACK_TRACE_OBJECT backTrace;
KPH_PAGED_CODE();
UNREFERENCED_PARAMETER(Apc);
DBG_UNREFERENCED_PARAMETER(Reason);
backTrace = CONTAINING_RECORD(Apc, KPH_STACK_BACK_TRACE_OBJECT, Apc);
KphDereferenceObject(backTrace);
}
/**
* \brief Allocates a stack back trace object.
*
* \param[in] Size The size to allocate.
*
* \return Allocated object, null on allocation failure.
*/
_Function_class_(KPH_TYPE_ALLOCATE_PROCEDURE)
_Return_allocatesMem_size_(Size)
PVOID KSIAPI KphpStackBackTraceAllocate(
_In_ SIZE_T Size
)
{
KPH_PAGED_CODE();
return KphAllocateNPaged(Size, KPH_TAG_BACK_TRACE_OBJECT);
}
/**
* \brief Frees a stack back trace object.
*
* \param[in] Object The stack back trace object to free.
*/
_Function_class_(KPH_TYPE_FREE_PROCEDURE)
VOID KSIAPI KphpStackBackTraceFree(
_In_freesMem_ PVOID Object
)
{
KPH_PAGED_CODE();
KphFree(Object, KPH_TAG_BACK_TRACE_OBJECT);
}
/**
* \brief Initializes a stack back trace object.
*
* \param[in,out] Object The stack back trace object to initialize.
* \param[in] Parameter The thread to initialize the back trace object for.
*
* \return STATUS_SUCCESS
*/
_Function_class_(KPH_TYPE_INITIALIZE_PROCEDURE)
_Must_inspect_result_
NTSTATUS KSIAPI KphpStackBackTraceInitialize(
_Inout_ PVOID Object,
_In_opt_ PVOID Parameter
)
{
PKPH_STACK_BACK_TRACE_OBJECT backTrace;
PETHREAD thread;
KPH_PAGED_CODE();
NT_ASSERT(Parameter);
backTrace = Object;
thread = Parameter;
KsiInitializeApc(&backTrace->Apc,
KphDriverObject,
thread,
OriginalApcEnvironment,
KphpCaptureStackBackTraceThreadSpecialApc,
KphpCaptureStackBackTraceThreadSpecialApcCleanup,
NULL,
KernelMode,
NULL);
KeInitializeEvent(&backTrace->CompletedEvent, NotificationEvent, FALSE);
backTrace->Thread = KphGetEThreadContext(thread);
return STATUS_SUCCESS;
}
/**
* \brief Deletes a stack back trace object.
*
* \param[in] Object The stack back trace object to delete.
*/
_Function_class_(KPH_TYPE_DELETE_PROCEDURE)
VOID KSIAPI KphpStackBackTraceDelete(
_Inout_ PVOID Object
)
{
PKPH_STACK_BACK_TRACE_OBJECT backTrace;
KPH_PAGED_CODE();
backTrace = Object;
if (backTrace->Thread)
{
KphDereferenceObject(backTrace->Thread);
}
}
/**
* \brief Captures a stack back trace for a given thread.
*
* \param[in] Thread The thread to capture the stack back trace of.
* \param[in] FramesToSkip The number of kernel frames to skip.
* \param[in] FramesToCapture The number of frames to capture.
* \param[out] BackTrace Buffer to store the back trace in.
* \param[out] CapturedFrames Receives the number of captured frames.
* \param[out] BackTraceHash Optionally receives a hash of the back trace.
* \param[in] Flags A combination of KPH_STACK_BACK_TRACE_* flags.
* \param[in] Timeout Optionally specifies a timeout for the capture operation.
*
* \return STATUS_SUCCES, STATUS_TIMEOUT, or errant status.
*/
_IRQL_requires_max_(APC_LEVEL)
_Must_inspect_result_
NTSTATUS KphCaptureStackBackTraceThread(
_In_ PETHREAD Thread,
_In_ ULONG FramesToSkip,
_In_ ULONG FramesToCapture,
_Out_writes_(FramesToCapture) PVOID* BackTrace,
_Out_ PULONG CapturedFrames,
_Out_opt_ PULONG BackTraceHash,
_In_ ULONG Flags,
_In_opt_ PLARGE_INTEGER Timeout
)
{
NTSTATUS status;
ULONG backTraceSize;
PKPH_STACK_BACK_TRACE_OBJECT backTrace;
KPH_PAGED_CODE();
if (Thread == PsGetCurrentThread())
{
*CapturedFrames = KphCaptureStackBackTrace(FramesToSkip,
FramesToCapture,
BackTrace,
BackTraceHash,
Flags);
return (*CapturedFrames ? STATUS_SUCCESS : STATUS_UNSUCCESSFUL);
}
NT_ASSERT(KphpStackBackTraceType);
*CapturedFrames = 0;
if (BackTraceHash)
{
*BackTraceHash = 0;
}
backTrace = NULL;
status = RtlULongAdd(FramesToCapture,
sizeof(KPH_STACK_BACK_TRACE_OBJECT),
&backTraceSize);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
GENERAL,
"RtlULongAdd failed: %!STATUS!",
status);
goto Exit;
}
status = KphCreateObject(KphpStackBackTraceType,
backTraceSize,
&backTrace,
Thread);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
GENERAL,
"KphCreateObject failed: %!STATUS!",
status);
backTrace = NULL;
goto Exit;
}
backTrace->FramesToCapture = FramesToCapture;
backTrace->FramesToSkip = FramesToSkip;
backTrace->Flags = Flags;
backTrace->DoHash = (BackTraceHash != NULL);
//
// The APC could fire immediately we must reference before trying to queue.
//
KphReferenceObject(backTrace);
if (!KsiInsertQueueApc(&backTrace->Apc, NULL, NULL, IO_NO_INCREMENT))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
GENERAL,
"KsiInsertQueueApc failed");
KphDereferenceObject(backTrace);
status = STATUS_UNSUCCESSFUL;
goto Exit;
}
status = KeWaitForSingleObject(&backTrace->CompletedEvent,
Executive,
KernelMode,
FALSE,
Timeout);
if (status != STATUS_SUCCESS)
{
NTSTATUS removeStatus;
NT_ASSERT(status == STATUS_TIMEOUT);
removeStatus = KsiRemoveQueueApc(&backTrace->Apc);
KphTracePrint(TRACE_LEVEL_VERBOSE,
GENERAL,
"KsiRemoveQueueApc: %!STATUS!",
removeStatus);
goto Exit;
}
NT_ASSERT(backTrace->CapturedFrames <= FramesToCapture);
RtlCopyMemory(BackTrace,
backTrace->BackTrace,
backTrace->CapturedFrames * sizeof(PVOID));
*CapturedFrames = backTrace->CapturedFrames;
if (BackTraceHash)
{
*BackTraceHash = backTrace->BackTraceHash;
}
Exit:
if (backTrace)
{
KphDereferenceObject(backTrace);
}
return status;
}
/**
* \brief Initialized stack back trace infrastructure.
*
* \return Successful or errant status.
*/
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KphInitializeStackBackTrace(
VOID
)
{
NTSTATUS status;
KPH_OBJECT_TYPE_INFO typeInfo;
SYSTEM_SINGLE_MODULE_INFORMATION info;
KPH_PAGED_CODE_PASSIVE();
typeInfo.Allocate = KphpStackBackTraceAllocate;
typeInfo.Initialize = KphpStackBackTraceInitialize;
typeInfo.Delete = KphpStackBackTraceDelete;
typeInfo.Free = KphpStackBackTraceFree;
typeInfo.Flags = 0;
KphCreateObjectType(&KphpStackBackTraceTypeName,
&typeInfo,
&KphpStackBackTraceType);
RtlZeroMemory(&info, sizeof(SYSTEM_SINGLE_MODULE_INFORMATION));
info.TargetModuleAddress = (PVOID)KphInitializeStackBackTrace;
status = ZwQuerySystemInformation(SystemSingleModuleInformation,
&info,
sizeof(SYSTEM_SINGLE_MODULE_INFORMATION),
NULL);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
GENERAL,
"ZwQuerySystemInformation failed: %!STATUS!",
status);
return status;
}
KphpSelfImageBase = info.ExInfo.BaseInfo.ImageBase;
KphpSelfImageEnd = Add2Ptr(KphpSelfImageBase, info.ExInfo.BaseInfo.ImageSize);
RtlZeroMemory(&info, sizeof(SYSTEM_SINGLE_MODULE_INFORMATION));
info.TargetModuleAddress = (PVOID)KsiInitializeApc;
status = ZwQuerySystemInformation(SystemSingleModuleInformation,
&info,
sizeof(SYSTEM_SINGLE_MODULE_INFORMATION),
NULL);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
GENERAL,
"ZwQuerySystemInformation failed: %!STATUS!",
status);
return status;
}
KphpKsiImageBase = info.ExInfo.BaseInfo.ImageBase;
KphpKsiImageEnd = Add2Ptr(KphpKsiImageBase, info.ExInfo.BaseInfo.ImageSize);
return STATUS_SUCCESS;
}
================================================
FILE: KSystemInformer/cid_table.c
================================================
/*
* Copyright (c) 2022 Winsider Seminars & Solutions, Inc. All rights reserved.
*
* This file is part of System Informer.
*
* Authors:
*
* jxy-s 2022-2026
*
*/
#include
#include
#define KPH_CID_TABLE_LEVEL_MASK ((ULONG_PTR)3)
#define KPH_CID_TABLE_L0 ((ULONG_PTR)0)
#define KPH_CID_TABLE_L1 ((ULONG_PTR)1)
#define KPH_CID_TABLE_L2 ((ULONG_PTR)2)
#define KPH_CID_TABLE_POINTER_MASK ~KPH_CID_TABLE_LEVEL_MASK
#define KPH_CID_LIMIT (1 << 24)
#define KPH_CID_L0_COUNT (PAGE_SIZE / sizeof(KPH_CID_TABLE_ENTRY))
#define KPH_CID_L1_COUNT (PAGE_SIZE / sizeof(PKPH_CID_TABLE_ENTRY))
#define KPH_CID_L2_COUNT (KPH_CID_LIMIT / (KPH_CID_L0_COUNT * KPH_CID_L1_COUNT))
#define KPH_CID_MAX_L0 KPH_CID_L0_COUNT
#define KPH_CID_MAX_L1 (KPH_CID_L1_COUNT * KPH_CID_L0_COUNT)
#define KPH_CID_MAX_L2 (KPH_CID_L2_COUNT * KPH_CID_L1_COUNT * KPH_CID_L0_COUNT)
#define KPH_CID_MAX KPH_CID_MAX_L2
C_ASSERT(KPH_CID_MAX == KPH_CID_LIMIT);
#define KphpCidToId(x) ((ULONG_PTR)x / 4)
/**
* \brief Assigns an object to the table entry.
*
* \param[in,out] Entry The entry to assign to.
* \param[in] Object Optional object pointer to assign into the table entry.
*/
_IRQL_requires_max_(DISPATCH_LEVEL)
VOID KphCidAssignObject(
_Inout_ PKPH_CID_TABLE_ENTRY Entry,
_In_opt_ PVOID Object
)
{
KPH_NPAGED_CODE_DISPATCH_MAX();
KphAtomicAssignObjectReference(&Entry->ObjectRef, Object);
}
/**
* \brief References the object in the table entry.
*
* \param[in,out] Entry The entry to reference the object of.
*
* \return Referenced object pointer, NULL if no object is assigned.
*/
_IRQL_requires_max_(DISPATCH_LEVEL)
_Must_inspect_result_
PVOID KphCidReferenceObject(
_In_ PKPH_CID_TABLE_ENTRY Entry
)
{
KPH_NPAGED_CODE_DISPATCH_MAX();
return KphAtomicReferenceObject(&Entry->ObjectRef);
}
/**
* \brief Allocates a CID table for a given level.
*
* \param[in] Level The level to allocate for.
*
* \return Allocated table or null on allocation failure.
*/
_IRQL_requires_max_(DISPATCH_LEVEL)
_When_(Level == KPH_CID_TABLE_L0, _Return_allocatesMem_size_(KPH_CID_L0_COUNT * sizeof(KPH_CID_TABLE_ENTRY)))
_When_(Level == KPH_CID_TABLE_L1, _Return_allocatesMem_size_(KPH_CID_L1_COUNT * sizeof(PKPH_CID_TABLE_ENTRY)))
_When_(Level == KPH_CID_TABLE_L2, _Return_allocatesMem_size_(KPH_CID_L2_COUNT * sizeof(PKPH_CID_TABLE_ENTRY)))
PVOID KphpCidAllocateTable(
_In_ ULONG_PTR Level
)
{
KPH_NPAGED_CODE_DISPATCH_MAX();
switch (Level)
{
case KPH_CID_TABLE_L0:
{
return KphAllocateNPaged(KPH_CID_L0_COUNT * sizeof(KPH_CID_TABLE_ENTRY),
KPH_TAG_CID_TABLE);
}
case KPH_CID_TABLE_L1:
{
return KphAllocateNPaged(KPH_CID_L1_COUNT * sizeof(PKPH_CID_TABLE_ENTRY),
KPH_TAG_CID_TABLE);
}
case KPH_CID_TABLE_L2:
{
return KphAllocateNPaged(KPH_CID_L2_COUNT * sizeof(PKPH_CID_TABLE_ENTRY),
KPH_TAG_CID_TABLE);
}
default:
{
NT_ASSERT(FALSE);
break;
}
}
return NULL;
}
/**
* \brief Creates and initializes a CID table
*
* \param[out] Table The table to create an initialize.
*
* \return Successful or errant status.
*/
_IRQL_requires_max_(DISPATCH_LEVEL)
_Must_inspect_result_
NTSTATUS KphCidTableCreate(
_Out_ PKPH_CID_TABLE Table
)
{
KPH_NPAGED_CODE_DISPATCH_MAX();
Table->Table = (ULONG_PTR)KphpCidAllocateTable(KPH_CID_TABLE_L0);
if (!Table->Table)
{
return STATUS_INSUFFICIENT_RESOURCES;
}
KeInitializeSpinLock(&Table->Lock);
return STATUS_SUCCESS;
}
/**
* \brief Deletes a CID table.
*
* \param[in] Table The table to delete.
*/
_IRQL_requires_max_(DISPATCH_LEVEL)
VOID KphCidTableDelete(
_In_ PKPH_CID_TABLE Table
)
{
ULONG_PTR tableCode;
PKPH_CID_TABLE_ENTRY tableL0;
PKPH_CID_TABLE_ENTRY* tableL1;
PKPH_CID_TABLE_ENTRY** tableL2;
KPH_NPAGED_CODE_DISPATCH_MAX();
tableCode = ReadULongPtrAcquire(&Table->Table);
if (!tableCode)
{
return;
}
switch (tableCode & KPH_CID_TABLE_LEVEL_MASK)
{
case KPH_CID_TABLE_L0:
{
tableL0 = (PKPH_CID_TABLE_ENTRY)(tableCode & KPH_CID_TABLE_POINTER_MASK);
NT_ASSERT(tableL0);
KphFree(tableL0, KPH_TAG_CID_TABLE);
break;
}
case KPH_CID_TABLE_L1:
{
tableL1 = (PKPH_CID_TABLE_ENTRY*)(tableCode & KPH_CID_TABLE_POINTER_MASK);
NT_ASSERT(tableL1);
for (ULONG i = 0; i < KPH_CID_L1_COUNT; i++)
{
#pragma prefast(suppress : 6001) // memory is initialized
if (tableL1[i])
{
KphFree(tableL1[i], KPH_TAG_CID_TABLE);
}
}
KphFree(tableL1, KPH_TAG_CID_TABLE);
break;
}
case KPH_CID_TABLE_L2:
{
tableL2 = (PKPH_CID_TABLE_ENTRY**)(tableCode & KPH_CID_TABLE_POINTER_MASK);
NT_ASSERT(tableL2);
for (ULONG i = 0; i < KPH_CID_L2_COUNT; i++)
{
tableL1 = tableL2[i];
if (!tableL1)
{
continue;
}
for (ULONG j = 0; j < KPH_CID_L1_COUNT; j++)
{
#pragma prefast(suppress : 6001) // memory is initialized
if (tableL1[j])
{
KphFree(tableL1[j], KPH_TAG_CID_TABLE);
}
}
KphFree(tableL1, KPH_TAG_CID_TABLE);
}
KphFree(tableL2, KPH_TAG_CID_TABLE);
break;
}
default:
{
NT_ASSERT(FALSE);
break;
}
}
}
/**
* \brief Looks up an entry in the CID table.
*
* \param[in] Cid The CID to look up the entry of.
* \param[in] Table The table to look up the CID in.
*
* \return Pointer to the CID table entry, null if the table hasn't been
* expanded enough.
*/
_IRQL_requires_max_(DISPATCH_LEVEL)
PKPH_CID_TABLE_ENTRY KphpCidLookupEntry(
_In_ HANDLE Cid,
_In_ PKPH_CID_TABLE Table
)
{
ULONG_PTR table;
PKPH_CID_TABLE_ENTRY tableL0;
PKPH_CID_TABLE_ENTRY* tableL1;
PKPH_CID_TABLE_ENTRY** tableL2;
ULONG_PTR id;
KPH_NPAGED_CODE_DISPATCH_MAX();
id = KphpCidToId(Cid);
table = ReadULongPtrAcquire(&Table->Table);
switch (table & KPH_CID_TABLE_LEVEL_MASK)
{
case KPH_CID_TABLE_L0:
{
if (id >= KPH_CID_MAX_L0)
{
return NULL;
}
tableL0 = (PKPH_CID_TABLE_ENTRY)(table & KPH_CID_TABLE_POINTER_MASK);
return &tableL0[id];
}
case KPH_CID_TABLE_L1:
{
if (id >= KPH_CID_MAX_L1)
{
return NULL;
}
tableL1 = (PKPH_CID_TABLE_ENTRY*)(table & KPH_CID_TABLE_POINTER_MASK);
tableL0 = tableL1[id / KPH_CID_MAX_L0];
if (!tableL0)
{
return NULL;
}
return &tableL0[id % KPH_CID_MAX_L0];
}
case KPH_CID_TABLE_L2:
{
if (id >= KPH_CID_MAX_L2)
{
return NULL;
}
tableL2 = (PKPH_CID_TABLE_ENTRY**)(table & KPH_CID_TABLE_POINTER_MASK);
tableL1 = tableL2[id / KPH_CID_MAX_L1];
if (!tableL1)
{
return NULL;
}
tableL0 = tableL1[(id % KPH_CID_MAX_L1) / KPH_CID_MAX_L0];
if (!tableL0)
{
return NULL;
}
return &tableL0[(id % KPH_CID_MAX_L1) % KPH_CID_MAX_L0];
}
default:
{
NT_ASSERT(FALSE);
return NULL;
}
}
}
/**
* \brief Expands a CID table making it capable of holding an entry for a CID.
*
* \param[in] Cid The CID for which the table should be expanded for.
* \param[in,out] Table The table that should be expanded to hold the CID.
*
* \return Pointer to the table entry for the CID, null on allocation failure.
*/
_IRQL_requires_max_(DISPATCH_LEVEL)
PKPH_CID_TABLE_ENTRY KphpCidExpandTableFor(
_In_ HANDLE Cid,
_Inout_ PKPH_CID_TABLE Table
)
{
PKPH_CID_TABLE_ENTRY entry;
ULONG_PTR table;
ULONG_PTR level;
PKPH_CID_TABLE_ENTRY tableL0;
PKPH_CID_TABLE_ENTRY* tableL1;
PKPH_CID_TABLE_ENTRY** tableL2;
ULONG_PTR id;
KIRQL oldIrql;
KPH_NPAGED_CODE_DISPATCH_MAX();
id = KphpCidToId(Cid);
if (id >= KPH_CID_MAX)
{
//
// We can't, as of now, service CIDs over the maximum. It is extremely
// unlikely we'll ever get here. The system is likely to run out of
// memory before this happens. It also should be impossible given the
// limit enforced by handle tables for PspCidTable. Unless Microsoft
// changes it, the limit is 1 << 24.
//
NT_ASSERT(FALSE);
return NULL;
}
KeAcquireSpinLock(&Table->Lock, &oldIrql);
//
// See if another thread beat us.
//
entry = KphpCidLookupEntry(Cid, Table);
if (entry)
{
//
// Rock and roll!
//
goto Exit;
}
//
// We are the chosen one. Go expand the tree.
//
table = ReadULongPtrAcquire(&Table->Table);
level = (table & KPH_CID_TABLE_LEVEL_MASK);
if (level == KPH_CID_TABLE_L0)
{
//
// If we're here then we *must* be migrating to level 1.
//
NT_ASSERT(id >= KPH_CID_MAX_L0);
tableL1 = KphpCidAllocateTable(KPH_CID_TABLE_L1);
if (!tableL1)
{
entry = NULL;
goto Exit;
}
tableL1[0] = (PKPH_CID_TABLE_ENTRY)(table & KPH_CID_TABLE_POINTER_MASK);
table = ((ULONG_PTR)tableL1 | KPH_CID_TABLE_L1);
level = KPH_CID_TABLE_L1;
InterlockedExchangePointer((PVOID*)&Table->Table, (PVOID)table);
//
// Fall through for level 1 expansion.
//
}
if (level == KPH_CID_TABLE_L1)
{
if (id < KPH_CID_MAX_L1)
{
//
// Allocate a new block in the level 1 table.
//
tableL1 = (PKPH_CID_TABLE_ENTRY*)(table & KPH_CID_TABLE_POINTER_MASK);
NT_ASSERT(!tableL1[id / KPH_CID_MAX_L0]);
tableL1[id / KPH_CID_MAX_L0] = KphpCidAllocateTable(KPH_CID_TABLE_L0);
tableL0 = tableL1[id / KPH_CID_MAX_L0];
if (!tableL0)
{
entry = NULL;
goto Exit;
}
entry = &tableL0[id % KPH_CID_MAX_L0];
goto Exit;
}
//
// We have to migrate to a level 2 table.
//
tableL2 = KphpCidAllocateTable(KPH_CID_TABLE_L2);
if (!tableL2)
{
entry = NULL;
goto Exit;
}
tableL2[0] = (PKPH_CID_TABLE_ENTRY*)(table & KPH_CID_TABLE_POINTER_MASK);
table = ((ULONG_PTR)tableL2 | KPH_CID_TABLE_L2);
level = KPH_CID_TABLE_L2;
InterlockedExchangePointer((PVOID*)&Table->Table, (PVOID)table);
//
// Fall through for level 2 expansion
//
}
NT_ASSERT(level == KPH_CID_TABLE_L2);
NT_ASSERT(id < KPH_CID_MAX_L2);
//
// Allocate new block(s) in the level 2 table.
//
tableL2 = (PKPH_CID_TABLE_ENTRY**)(table & KPH_CID_TABLE_POINTER_MASK);
tableL1 = tableL2[id / KPH_CID_MAX_L1];
if (!tableL1)
{
tableL2[id / KPH_CID_MAX_L1] = KphpCidAllocateTable(KPH_CID_TABLE_L1);
tableL1 = tableL2[id / KPH_CID_MAX_L1];
if (!tableL1)
{
entry = NULL;
goto Exit;
}
}
NT_ASSERT(!tableL1[(id % KPH_CID_MAX_L1) / KPH_CID_MAX_L0]);
tableL1[(id % KPH_CID_MAX_L1) / KPH_CID_MAX_L0] = KphpCidAllocateTable(KPH_CID_TABLE_L0);
tableL0 = tableL1[(id % KPH_CID_MAX_L1) / KPH_CID_MAX_L0];
if (!tableL0)
{
entry = NULL;
goto Exit;
}
entry = &tableL0[(id % KPH_CID_MAX_L1) % KPH_CID_MAX_L0];
Exit:
KeReleaseSpinLock(&Table->Lock, oldIrql);
return entry;
}
/**
* \brief Retrieves the table entry for a given CID.
*
* \param[in] Cid The CID to retrieve the entry for.
* \param[in,out] Table The table to retrieve the entry from.
*
* \return Pointer to the table entry for the CID, null on allocation failure.
*/
_IRQL_requires_max_(DISPATCH_LEVEL)
_Must_inspect_result_
PKPH_CID_TABLE_ENTRY KphCidGetEntry(
_In_ HANDLE Cid,
_Inout_ PKPH_CID_TABLE Table
)
{
PKPH_CID_TABLE_ENTRY entry;
KPH_NPAGED_CODE_DISPATCH_MAX();
entry = KphpCidLookupEntry(Cid, Table);
if (entry)
{
return entry;
}
return KphpCidExpandTableFor(Cid, Table);
}
/**
* \brief Handles invoking the callback during enumeration.
*
* \param[in] Entry The CID table entry to invoke the callback for.
* \param[in] Callback The object callback to invoke.
* \param[in] Rundown The rundown callback to invoke.
* \param[in] Parameter Optional parameter passed to the callback.
*
* \return TRUE if enumeration should stop, FALSE otherwise.
*/
_IRQL_requires_max_(DISPATCH_LEVEL)
_Must_inspect_result_
BOOLEAN KphpCidEnumerateInvokeCallback(
_In_ PKPH_CID_TABLE_ENTRY Entry,
_When_(!Rundown, _In_) _When_(Rundown, _Pre_null_) PKPH_CID_ENUMERATE_CALLBACK Callback,
_When_(!Callback, _In_) _When_(Callback, _Pre_null_) PKPH_CID_RUNDOWN_CALLBACK Rundown,
_In_opt_ PVOID Parameter
)
{
PVOID object;
BOOLEAN res;
KPH_NPAGED_CODE_DISPATCH_MAX();
res = FALSE;
if (Callback)
{
object = KphAtomicReferenceObject(&Entry->ObjectRef);
if (object)
{
res = Callback(object, Parameter);
KphDereferenceObject(object);
}
}
else
{
NT_ASSERT(Rundown);
object = KphAtomicMoveObjectReference(&Entry->ObjectRef, NULL);
if (object)
{
Rundown(object, Parameter);
KphDereferenceObject(object);
}
}
return res;
}
/**
* \brief Enumerates a CID table.
*
* \param[in] Table The table to enumerate.
* \param[in] Callback The object callback to invoke.
* \param[in] Rundown The rundown callback to invoke.
* \param[in] Parameter Optional parameter passed to the callback.
*/
_IRQL_requires_max_(DISPATCH_LEVEL)
VOID KphpCidEnumerate(
_In_ PKPH_CID_TABLE Table,
_When_(!Rundown, _In_) _When_(Rundown, _Pre_null_) PKPH_CID_ENUMERATE_CALLBACK Callback,
_When_(!Callback, _In_) _When_(Callback, _Pre_null_) PKPH_CID_RUNDOWN_CALLBACK Rundown,
_In_opt_ PVOID Parameter
)
{
ULONG_PTR table;
BOOLEAN leaveCriticalRegion;
PKPH_CID_TABLE_ENTRY tableL0;
PKPH_CID_TABLE_ENTRY* tableL1;
PKPH_CID_TABLE_ENTRY** tableL2;
KPH_NPAGED_CODE_DISPATCH_MAX();
table = ReadULongPtrAcquire(&Table->Table);
if (!table)
{
return;
}
if (KeGetCurrentIrql() <= APC_LEVEL)
{
KeEnterCriticalRegion();
leaveCriticalRegion = TRUE;
}
else
{
leaveCriticalRegion = FALSE;
}
switch (table & KPH_CID_TABLE_LEVEL_MASK)
{
case KPH_CID_TABLE_L0:
{
tableL0 = (PKPH_CID_TABLE_ENTRY)(table & KPH_CID_TABLE_POINTER_MASK);
NT_ASSERT(tableL0);
for (ULONG i = 0; i < KPH_CID_L0_COUNT; i++)
{
if (KphpCidEnumerateInvokeCallback(&tableL0[i],
Callback,
Rundown,
Parameter))
{
goto Exit;
}
}
break;
}
case KPH_CID_TABLE_L1:
{
tableL1 = (PKPH_CID_TABLE_ENTRY*)(table & KPH_CID_TABLE_POINTER_MASK);
NT_ASSERT(tableL1);
for (ULONG i = 0; i < KPH_CID_L1_COUNT; i++)
{
tableL0 = tableL1[i];
if (!tableL0)
{
continue;
}
for (ULONG j = 0; j < KPH_CID_L0_COUNT; j++)
{
if (KphpCidEnumerateInvokeCallback(&tableL0[j],
Callback,
Rundown,
Parameter))
{
goto Exit;
}
}
}
break;
}
case KPH_CID_TABLE_L2:
{
tableL2 = (PKPH_CID_TABLE_ENTRY**)(table & KPH_CID_TABLE_POINTER_MASK);
NT_ASSERT(tableL2);
for (ULONG i = 0; i < KPH_CID_L2_COUNT; i++)
{
tableL1 = tableL2[i];
if (!tableL1)
{
continue;
}
for (ULONG j = 0; j < KPH_CID_L1_COUNT; j++)
{
tableL0 = tableL1[j];
if (!tableL0)
{
continue;
}
for (ULONG k = 0; k < KPH_CID_L0_COUNT; k++)
{
if (KphpCidEnumerateInvokeCallback(&tableL0[k],
Callback,
Rundown,
Parameter))
{
goto Exit;
}
}
}
}
break;
}
default:
{
NT_ASSERT(FALSE);
break;
}
}
Exit:
if (leaveCriticalRegion)
{
KeLeaveCriticalRegion();
}
}
/**
* \brief Enumerates objects in a CID table.
*
* \param[in] Table The table to enumerate.
* \param[in] Callback The object callback to invoke. The callback should
* return TRUE to stop enumerating and return FALSE to continue.
* \param[in] Parameter Optional parameter passed to the callback.
*/
_IRQL_requires_max_(DISPATCH_LEVEL)
VOID KphCidEnumerate(
_In_ PKPH_CID_TABLE Table,
_In_ PKPH_CID_ENUMERATE_CALLBACK Callback,
_In_opt_ PVOID Parameter
)
{
KPH_NPAGED_CODE_DISPATCH_MAX();
KphpCidEnumerate(Table, Callback, NULL, Parameter);
}
/**
* \brief Enumerates CID entries a CID table for rundown.
*
* \details This routine removes all items from the table. After the callback
* returns all object references in the table is dereferenced and removed from
* the table.
*
* \param[in] Table The table to enumerate.
* \param[in] Callback The rundown callback to invoke.
* \param[in] Parameter Optional parameter passed to the callback.
*/
_IRQL_requires_max_(DISPATCH_LEVEL)
VOID KphCidRundown(
_In_ PKPH_CID_TABLE Table,
_In_ PKPH_CID_RUNDOWN_CALLBACK Callback,
_In_opt_ PVOID Parameter
)
{
KPH_NPAGED_CODE_DISPATCH_MAX();
KphpCidEnumerate(Table, NULL, Callback, Parameter);
}
================================================
FILE: KSystemInformer/cid_tracking.c
================================================
/*
* Copyright (c) 2022 Winsider Seminars & Solutions, Inc. All rights reserved.
*
* This file is part of System Informer.
*
* Authors:
*
* jxy-s 2022-2026
*
*/
#include
#include
#include
typedef struct _KPH_CID_APC
{
KSI_KAPC Apc;
KEVENT CompletedEvent;
PKPH_THREAD_CONTEXT Thread;
} KPH_CID_APC, *PKPH_CID_APC;
KPH_PROTECTED_DATA_SECTION_PUSH();
static PKPH_OBJECT_TYPE KphpCidApcType = NULL;
PKPH_OBJECT_TYPE KphProcessContextType = NULL;
PKPH_OBJECT_TYPE KphThreadContextType = NULL;
static PKPH_NPAGED_LOOKASIDE_OBJECT KphpCidApcLookaside = NULL;
static PKPH_NPAGED_LOOKASIDE_OBJECT KphpProcessContextLookaside = NULL;
static PKPH_NPAGED_LOOKASIDE_OBJECT KphpThreadContextLookaside = NULL;
KPH_PROTECTED_DATA_SECTION_POP();
KPH_PROTECTED_DATA_SECTION_RO_PUSH();
static const UNICODE_STRING KphpCidApcTypeName = RTL_CONSTANT_STRING(L"KphCidApc");
static const UNICODE_STRING KphpProcessContextTypeName = RTL_CONSTANT_STRING(L"KphProcessContext");
static const UNICODE_STRING KphpThreadContextTypeName = RTL_CONSTANT_STRING(L"KphThreadContext");
static const LARGE_INTEGER KphpCidApcTimeout = KPH_TIMEOUT(3 * 1000);
KPH_PROTECTED_DATA_SECTION_RO_POP();
static BOOLEAN KphpCidTrackingInitialized = FALSE;
static KPH_CID_TABLE KphpCidTable;
static LONG KphpCidPopulated = 0;
static KEVENT KphpCidPopulatedEvent;
static PKPH_PROCESS_CONTEXT KphpSystemProcessContext = NULL;
static ULONG64 KphpProcessSequence = 0;
/**
* \brief Looks up a context object in the CID tracking.
*
* \param[in] Cid The CID of the object to look up.
* \param[in] ObjectType The expected object type if the CID.
*
* \return Pointer to the context object, null if not found or the object is
* not of the expected type. The caller *must* dereference the object when
* they are through with it.
*/
_IRQL_requires_max_(DISPATCH_LEVEL)
_Must_inspect_result_
PVOID KphpLookupContext(
_In_ HANDLE Cid,
_In_ PKPH_OBJECT_TYPE ObjectType
)
{
PVOID object;
PKPH_CID_TABLE_ENTRY entry;
KPH_NPAGED_CODE_DISPATCH_MAX();
entry = KphCidGetEntry(Cid, &KphpCidTable);
if (!entry)
{
return NULL;
}
object = KphCidReferenceObject(entry);
if (object && (KphGetObjectType(object) != ObjectType))
{
KphDereferenceObject(object);
object = NULL;
}
return object;
}
/**
* \brief Retrieves the system process context.
*
* \return Pointer to the system process context, null if not found. The caller
* *must* dereference the object when they are through with it.
*/
_IRQL_requires_max_(DISPATCH_LEVEL)
_Must_inspect_result_
PKPH_PROCESS_CONTEXT KphGetSystemProcessContext(
VOID
)
{
KPH_NPAGED_CODE_DISPATCH_MAX();
if (KphpSystemProcessContext)
{
KphReferenceObject(KphpSystemProcessContext);
}
return KphpSystemProcessContext;
}
/**
* \brief Retrieves a process context.
*
* \param[in] ProcessId The process ID of the process to get the context for.
*
* \return Pointer to the process context, null if not found. The caller
* *must* dereference the object when they are through with it.
*/
_IRQL_requires_max_(DISPATCH_LEVEL)
_Must_inspect_result_
PKPH_PROCESS_CONTEXT KphGetProcessContext(
_In_ HANDLE ProcessId
)
{
KPH_NPAGED_CODE_DISPATCH_MAX();
return KphpLookupContext(ProcessId, KphProcessContextType);
}
/**
* \brief Retrieves a process context by process object.
*
* \param[in] Process The process object of the process to get the context for.
*
* \return Pointer to the process context, null if not found. The caller
* *must* dereference the object when they are through with it.
*/
_IRQL_requires_max_(DISPATCH_LEVEL)
_Must_inspect_result_
PKPH_PROCESS_CONTEXT KphGetEProcessContext(
_In_ PEPROCESS Process
)
{
KPH_NPAGED_CODE_DISPATCH_MAX();
if (Process == PsInitialSystemProcess)
{
return KphGetSystemProcessContext();
}
return KphGetProcessContext(PsGetProcessId(Process));
}
/**
* \brief Retrieves a thread context.
*
* \param[in] ThreadId The thread ID of the thread to get the context for.
*
* \return Pointer to the thread context, null if not found. The caller
* *must* dereference the object when they are through with it.
*/
_IRQL_requires_max_(DISPATCH_LEVEL)
_Must_inspect_result_
PKPH_THREAD_CONTEXT KphGetThreadContext(
_In_ HANDLE ThreadId
)
{
KPH_NPAGED_CODE_DISPATCH_MAX();
return KphpLookupContext(ThreadId, KphThreadContextType);
}
KPH_PAGED_FILE();
/**
* \brief Marks the CID tracking populated, which unblocks public tracking APIs.
*/
_IRQL_requires_max_(PASSIVE_LEVEL)
VOID KphCidMarkPopulated(
VOID
)
{
KPH_PAGED_CODE_PASSIVE();
if (!KphpCidTrackingInitialized)
{
return;
}
WriteRelease(&KphpCidPopulated, 1);
KphTracePrint(TRACE_LEVEL_VERBOSE,
TRACKING,
"Marked CID tracking populated, unblocking routines.");
KeSetEvent(&KphpCidPopulatedEvent, EVENT_INCREMENT, FALSE);
}
/**
* \brief Waits for the CID tracking to be marked as populated.
*/
_IRQL_requires_max_(APC_LEVEL)
VOID KphpCidWaitForPopulate(
VOID
)
{
KPH_PAGED_CODE();
if (ReadAcquire(&KphpCidPopulated))
{
return;
}
KphTracePrint(TRACE_LEVEL_VERBOSE,
TRACKING,
"Waiting for CID tracking population...");
KeWaitForSingleObject(&KphpCidPopulatedEvent,
Executive,
KernelMode,
FALSE,
NULL);
}
/**
* \brief Allocates a CID APC object.
*
* \return Pointer to CID APC object, null on failure.
*/
_Function_class_(KPH_TYPE_ALLOCATE_PROCEDURE)
_IRQL_requires_max_(APC_LEVEL)
_Return_allocatesMem_size_(Size)
PVOID KSIAPI KphpAllocateCidApc(
_In_ SIZE_T Size
)
{
PVOID object;
KPH_PAGED_CODE();
NT_ASSERT(KphpCidApcLookaside);
NT_ASSERT(Size <= KphpCidApcLookaside->L.Size);
DBG_UNREFERENCED_PARAMETER(Size);
object = KphAllocateFromNPagedLookasideObject(KphpCidApcLookaside);
if (object)
{
KphReferenceObject(KphpCidApcLookaside);
}
return object;
}
/**
* \brief Initializes a CID APC object.
*
* \param[in] Object The CID APC object to initialize.
* \param[in] Parameter Unused.
*
* \return STATUS_SUCCESS
*/
_Function_class_(KPH_TYPE_INITIALIZE_PROCEDURE)
_IRQL_requires_max_(APC_LEVEL)
_Must_inspect_result_
NTSTATUS KSIAPI KphpInitializeCidApc(
_Inout_ PVOID Object,
_In_opt_ PVOID Parameter
)
{
PKPH_CID_APC apc;
KPH_PAGED_CODE();
UNREFERENCED_PARAMETER(Parameter);
apc = Object;
KeInitializeEvent(&apc->CompletedEvent, NotificationEvent, FALSE);
return STATUS_SUCCESS;
}
/**
* \brief Deletes a CID APC object.
*
* \param[in] Object The CID APC object to delete.
*/
_Function_class_(KPH_TYPE_DELETE_PROCEDURE)
_IRQL_requires_max_(APC_LEVEL)
VOID KSIAPI KphpDeleteCidApc(
_Inout_ PVOID Object
)
{
PKPH_CID_APC apc;
KPH_PAGED_CODE();
apc = Object;
if (apc->Thread)
{
KphDereferenceObject(apc->Thread);
}
}
/**
* \brief Frees a CID APC object.
*
* \param[in] Object The CID APC object to free.
*/
_Function_class_(KPH_TYPE_FREE_PROCEDURE)
_IRQL_requires_max_(APC_LEVEL)
VOID KSIAPI KphpFreeCidApc(
_In_freesMem_ PVOID Object
)
{
KPH_PAGED_CODE();
NT_ASSERT(KphpCidApcLookaside);
KphFreeToNPagedLookasideObject(KphpCidApcLookaside, Object);
KphDereferenceObject(KphpCidApcLookaside);
}
/**
* \brief APC cleanup routine for APC APCs.
*
* \param[in] Apc The ACP to clean up.
* \param[in] Reason Unused.
*/
_Function_class_(KSI_KCLEANUP_ROUTINE)
_IRQL_requires_min_(PASSIVE_LEVEL)
_IRQL_requires_max_(APC_LEVEL)
_IRQL_requires_same_
VOID KSIAPI KphpCidApcCleanup(
_In_ PKSI_KAPC Apc,
_In_ KSI_KAPC_CLEANUP_REASON Reason
)
{
PKPH_CID_APC apc;
KPH_PAGED_CODE();
UNREFERENCED_PARAMETER(Apc);
DBG_UNREFERENCED_PARAMETER(Reason);
apc = CONTAINING_RECORD(Apc, KPH_CID_APC, Apc);
KphDereferenceObject(apc);
}
/**
* \brief Allocates a process context object.
*
* \param[in] Size The size requested from the object infrastructure.
*
* \return Allocated process context object, null on allocation failure.
*/
_Function_class_(KPH_TYPE_ALLOCATE_PROCEDURE)
_IRQL_requires_max_(PASSIVE_LEVEL)
_Return_allocatesMem_size_(Size)
PVOID KSIAPI KphpAllocateProcessContext(
_In_ SIZE_T Size
)
{
PVOID object;
KPH_PAGED_CODE_PASSIVE();
DBG_UNREFERENCED_PARAMETER(Size);
NT_ASSERT(KphpProcessContextLookaside);
NT_ASSERT(Size <= KphpProcessContextLookaside->L.Size);
object = KphAllocateFromNPagedLookasideObject(KphpProcessContextLookaside);
if (object)
{
KphReferenceObject(KphpProcessContextLookaside);
}
return object;
}
/**
* \brief Initializes a process context.
*
* \param[in] Object The process context object to initialize.
* \param[in] Parameter The kernel process object associated with this context.
*
* \return STATUS_SUCCESS
*/
_Function_class_(KPH_TYPE_INITIALIZE_PROCEDURE)
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KSIAPI KphpInitializeProcessContext(
_Inout_ PVOID Object,
_In_opt_ PVOID Parameter
)
{
NTSTATUS status;
PKPH_PROCESS_CONTEXT process;
PEPROCESS processObject;
HANDLE processHandle;
PROCESS_EXTENDED_BASIC_INFORMATION basicInfo;
KPH_PAGED_CODE_PASSIVE();
process = Object;
processObject = Parameter;
process->SequenceNumber = InterlockedIncrementU64(&KphpProcessSequence);
status = ObOpenObjectByPointer(processObject,
OBJ_KERNEL_HANDLE,
NULL,
PROCESS_ALL_ACCESS,
*PsProcessType,
KernelMode,
&processHandle);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
TRACKING,
"ObOpenObjectByPointer failed: %!STATUS!",
status);
processHandle = NULL;
goto Exit;
}
status = ZwQueryInformationProcess(processHandle,
ProcessSubsystemInformation,
&process->SubsystemType,
sizeof(SUBSYSTEM_INFORMATION_TYPE),
NULL);
if (status == STATUS_INVALID_INFO_CLASS)
{
process->SubsystemType = SubsystemInformationTypeWin32;
}
else if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
TRACKING,
"ProcessSubsystemInformation failed: %!STATUS!",
status);
goto Exit;
}
basicInfo.Size = sizeof(PROCESS_EXTENDED_BASIC_INFORMATION);
status = ZwQueryInformationProcess(processHandle,
ProcessBasicInformation,
&basicInfo,
sizeof(PROCESS_EXTENDED_BASIC_INFORMATION),
NULL);
if (status == STATUS_INVALID_INFO_CLASS)
{
process->IsSubsystemProcess = FALSE;
}
else if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
TRACKING,
"ProcessBasicInformation failed: %!STATUS!",
status);
goto Exit;
}
else
{
process->IsSubsystemProcess = basicInfo.IsSubsystemProcess;
}
process->EProcess = processObject;
ObReferenceObject(process->EProcess);
process->ProcessId = PsGetProcessId(process->EProcess);
if (KphDynPsGetProcessStartKey)
{
process->ProcessStartKey = KphDynPsGetProcessStartKey(processObject);
}
else
{
PROCESS_TELEMETRY_ID_INFORMATION telemetryIdInfo;
telemetryIdInfo.HeaderSize = 0;
status = ZwQueryInformationProcess(processHandle,
ProcessTelemetryIdInformation,
&telemetryIdInfo,
sizeof(PROCESS_TELEMETRY_ID_INFORMATION),
NULL);
if ((status == STATUS_BUFFER_OVERFLOW) &&
RTL_CONTAINS_FIELD(&telemetryIdInfo,
telemetryIdInfo.HeaderSize,
ProcessStartKey))
{
status = STATUS_SUCCESS;
}
if (NT_SUCCESS(status))
{
process->ProcessStartKey = telemetryIdInfo.ProcessStartKey;
}
else
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
TRACKING,
"ProcessTelemetryIdInformation failed: %!STATUS!",
status);
NT_ASSERT(process->ProcessStartKey == 0);
}
}
#ifdef _WIN64
if (PsGetProcessWow64Process(process->EProcess))
{
process->IsWow64 = TRUE;
}
else
#endif
{
process->IsWow64 = FALSE;
}
KphInitializeRWLock(&process->ThreadListLock);
InitializeListHead(&process->ThreadListHead);
KphInitializeRWLock(&process->ProtectionLock);
status = PsReferenceProcessFilePointer(process->EProcess,
&process->FileObject);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
TRACKING,
"PsReferenceProcessFilePointer failed: %!STATUS!",
status);
process->FileObject = NULL;
}
if (process->FileObject)
{
ULONG returnLength;
status = KphQueryNameFileObject(process->FileObject,
NULL,
0,
&returnLength);
if (status == STATUS_BUFFER_TOO_SMALL)
{
POBJECT_NAME_INFORMATION nameInfo;
nameInfo = KphAllocateNPaged(returnLength,
KPH_TAG_PROCESS_IMAGE_FILE_NAME);
if (nameInfo)
{
status = KphQueryNameFileObject(process->FileObject,
nameInfo,
returnLength,
&returnLength);
if (NT_SUCCESS(status))
{
C_ASSERT(FIELD_OFFSET(OBJECT_NAME_INFORMATION, Name) == 0);
process->ImageFileName = (PUNICODE_STRING)nameInfo;
}
else
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
TRACKING,
"KphQueryNameFileObject failed: %!STATUS!",
status);
KphFree(nameInfo, KPH_TAG_PROCESS_IMAGE_FILE_NAME);
}
}
else
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
TRACKING,
"Failed to allocate process image file name.");
}
}
}
if (!process->ImageFileName)
{
status = SeLocateProcessImageName(process->EProcess,
&process->ImageFileName);
if (NT_SUCCESS(status))
{
process->SystemAllocatedImageFileName = TRUE;
}
else
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
TRACKING,
"SeLocateProcessImageName failed: %!STATUS!",
status);
process->ImageFileName = NULL;
}
}
if (process->ImageFileName)
{
status = KphGetFileNameFinalComponent(process->ImageFileName,
&process->ImageName);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
TRACKING,
"KphGetFileNameFinalComponent failed: %!STATUS!",
status);
NT_ASSERT(process->ImageName.Length == 0);
}
}
if (process->ImageName.Length == 0)
{
status = KphGetProcessImageName(process->EProcess,
&process->ImageName);
if (NT_SUCCESS(status))
{
process->AllocatedImageName = TRUE;
}
else
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
TRACKING,
"KphGetProcessImageName failed: %!STATUS!",
status);
NT_ASSERT(process->ImageName.Length == 0);
}
}
if (process->EProcess == PsInitialSystemProcess)
{
NT_ASSERT(!KphpSystemProcessContext);
KphReferenceObject(process);
KphpSystemProcessContext = process;
}
KphValidateLsass(process->EProcess);
status = STATUS_SUCCESS;
Exit:
if (processHandle)
{
ObCloseHandle(processHandle, KernelMode);
}
return status;
}
/**
* \brief Deletes a process context.
*
* \param[in] Object The process context object to delete.
*/
_Function_class_(KPH_TYPE_DELETE_PROCEDURE)
_IRQL_requires_max_(PASSIVE_LEVEL)
VOID KSIAPI KphpDeleteProcessContext(
_Inout_ PVOID Object
)
{
PKPH_PROCESS_CONTEXT process;
KPH_PAGED_CODE_PASSIVE();
process = Object;
KphAtomicAssignObjectReference(&process->InformerState.Atomic, NULL);
KphAtomicAssignObjectReference(&process->SessionToken.Atomic, NULL);
if (process->Protected)
{
KphStopProtectingProcess(process);
}
if (process->ImageFileName)
{
if (process->SystemAllocatedImageFileName)
{
KphFreePool(process->ImageFileName);
}
else
{
KphFree(process->ImageFileName, KPH_TAG_PROCESS_IMAGE_FILE_NAME);
}
}
if (process->AllocatedImageName)
{
KphFreeProcessImageName(&process->ImageName);
}
if (process->FileObject)
{
ObDereferenceObject(process->FileObject);
}
KphDeleteRWLock(&process->ProtectionLock);
NT_ASSERT(IsListEmpty(&process->ThreadListHead));
NT_ASSERT(process->NumberOfThreads == 0);
KphDeleteRWLock(&process->ThreadListLock);
KphInvalidateLsass(process->EProcess);
NT_ASSERT(process->EProcess);
ObDereferenceObject(process->EProcess);
}
/**
* \brief Frees a process context object.
*
* \param[in] Object The process context object to free.
*/
_Function_class_(KPH_TYPE_FREE_PROCEDURE)
_IRQL_requires_max_(PASSIVE_LEVEL)
VOID KSIAPI KphpFreeProcessContext(
_In_freesMem_ PVOID Object
)
{
KPH_PAGED_CODE_PASSIVE();
NT_ASSERT(KphpProcessContextLookaside);
KphFreeToNPagedLookasideObject(KphpProcessContextLookaside, Object);
KphDereferenceObject(KphpProcessContextLookaside);
}
/**
* \brief Allocates a thread context object.
*
* \param[in] Size The size requested from the object infrastructure.
*
* \return Allocated thread context object, null on allocation failure.
*/
_Function_class_(KPH_TYPE_ALLOCATE_PROCEDURE)
_IRQL_requires_max_(APC_LEVEL)
_Return_allocatesMem_size_(Size)
PVOID KSIAPI KphpAllocateThreadContext(
_In_ SIZE_T Size
)
{
PVOID object;
KPH_PAGED_CODE();
DBG_UNREFERENCED_PARAMETER(Size);
NT_ASSERT(KphpThreadContextLookaside);
NT_ASSERT(Size <= KphpThreadContextLookaside->L.Size);
object = KphAllocateFromNPagedLookasideObject(KphpThreadContextLookaside);
if (object)
{
KphReferenceObject(KphpThreadContextLookaside);
}
return object;
}
/**
* \brief Performs thread context initialization for a WSL thread.
*
* \param[in] Dyn Dynamic configuration.
* \param[in] ThreadContext The thread context to initialize.
*/
_IRQL_requires_(APC_LEVEL)
VOID KphpInitializeWSLThreadContext(
_In_ PKPH_DYN Dyn,
_In_ PKPH_THREAD_CONTEXT ThreadContext
)
{
PVOID picoContext;
PVOID value;
KPH_PAGED_CODE_APC();
//
// We use an APC here to reach into the thread pico context. We could
// reach directly into the pico context elsewhere, but reversing shows
// intent for possible other pico subsystem providers in the future.
// So, we use some "undocumented" APIs in the APC to ask "nicely" for
// the correct pico context.
//
if ((ThreadContext->SubsystemType != SubsystemInformationTypeWSL) ||
!KphDynLxpThreadGetCurrent ||
(Dyn->LxPicoThrdInfo == ULONG_MAX) ||
(Dyn->LxPicoThrdInfoTID == ULONG_MAX) ||
(Dyn->LxPicoProc == ULONG_MAX) ||
(Dyn->LxPicoProcInfo == ULONG_MAX) ||
(Dyn->LxPicoProcInfoPID == ULONG_MAX))
{
return;
}
if (!KphDynLxpThreadGetCurrent(&picoContext))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
TRACKING,
"LxpThreadGetCurrent failed");
return;
}
if (!ThreadContext->WSL.ValidThreadId)
{
value = *(PVOID*)Add2Ptr(picoContext, Dyn->LxPicoThrdInfo);
ThreadContext->WSL.ThreadId =
*(PULONG)Add2Ptr(value, Dyn->LxPicoThrdInfoTID);
ThreadContext->WSL.ValidThreadId = TRUE;
}
if (ThreadContext->ProcessContext &&
!ThreadContext->ProcessContext->WSL.ValidProcessId)
{
value = *(PVOID*)Add2Ptr(picoContext, Dyn->LxPicoProc);
value = *(PVOID*)Add2Ptr(value, Dyn->LxPicoProcInfo);
ThreadContext->ProcessContext->WSL.ProcessId =
*(PULONG)Add2Ptr(value, Dyn->LxPicoProcInfoPID);
ThreadContext->ProcessContext->WSL.ValidProcessId = TRUE;
}
}
/**
* \brief APC routine for thread tracking.
*
* \param[in] Apc The ACP executed, contained within the CID APC.
* \param[in] NormalRoutine Unused.
* \param[in] NormalContext Unused.
* \param[in] SystemArgument1 Unused.
* \param[in] SystemArgument2 Unused.
*/
_Function_class_(KSI_KKERNEL_ROUTINE)
_IRQL_requires_(APC_LEVEL)
_IRQL_requires_same_
VOID KSIAPI KphpInitializeThreadContextSpecialApc(
_In_ PKSI_KAPC Apc,
_Inout_ _Deref_pre_maybenull_ PKNORMAL_ROUTINE* NormalRoutine,
_Inout_ _Deref_pre_maybenull_ PVOID* NormalContext,
_Inout_ _Deref_pre_maybenull_ PVOID* SystemArgument1,
_Inout_ _Deref_pre_maybenull_ PVOID* SystemArgument2
)
{
PKPH_CID_APC apc;
PKPH_DYN dyn;
PTEB teb;
KPH_PAGED_CODE_APC();
UNREFERENCED_PARAMETER(NormalRoutine);
UNREFERENCED_PARAMETER(NormalContext);
UNREFERENCED_PARAMETER(SystemArgument1);
UNREFERENCED_PARAMETER(SystemArgument2);
apc = CONTAINING_RECORD(Apc, KPH_CID_APC, Apc);
NT_ASSERT(apc->Thread->EThread == KeGetCurrentThread());
#ifdef _WIN64
C_ASSERT(FIELD_OFFSET(TEB, SubProcessTag) == 0x1720);
#endif
teb = PsGetCurrentThreadTeb();
if (teb)
{
__try
{
apc->Thread->SubProcessTag = ReadPointerFromUser(&teb->SubProcessTag);
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
TRACKING,
"Failed to populate SubProcessTag: %!STATUS!",
GetExceptionCode());
}
}
dyn = KphReferenceDynData();
if (dyn)
{
KphpInitializeWSLThreadContext(dyn, apc->Thread);
KphDereferenceObject(dyn);
}
KeSetEvent(&apc->CompletedEvent, EVENT_INCREMENT, FALSE);
}
/**
* \brief Initializes thread context parts in the original thread environment.
*
* \param[in] ThreadContext The thread context to initialize.
* \param[in] Wait If TRUE this routine will try to wait for the APC to
* complete, otherwise this routine will not wait.
*/
_IRQL_requires_max_(APC_LEVEL)
VOID KphpInitThreadContextInOriginalEnvironment(
_In_ PKPH_THREAD_CONTEXT ThreadContext,
_In_ BOOLEAN Wait
)
{
NTSTATUS status;
PKPH_CID_APC apc;
KPH_PAGED_CODE();
status = KphCreateObject(KphpCidApcType, sizeof(KPH_CID_APC), &apc, NULL);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
TRACKING,
"KphCreateObject failed: %!STATUS!",
status);
goto Exit;
}
apc->Thread = ThreadContext;
KphReferenceObject(apc->Thread);
KsiInitializeApc(&apc->Apc,
KphDriverObject,
ThreadContext->EThread,
OriginalApcEnvironment,
KphpInitializeThreadContextSpecialApc,
KphpCidApcCleanup,
NULL,
KernelMode,
NULL);
//
// The APC could fire immediately we must reference before trying to queue.
//
KphReferenceObject(apc);
if (!KsiInsertQueueApc(&apc->Apc, NULL, NULL, IO_NO_INCREMENT))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
TRACKING,
"KsiInsertQueueApc failed");
KphDereferenceObject(apc);
status = STATUS_UNSUCCESSFUL;
goto Exit;
}
if (!Wait)
{
status = STATUS_SUCCESS;
goto Exit;
}
status = KeWaitForSingleObject(&apc->CompletedEvent,
Executive,
KernelMode,
FALSE,
(PLARGE_INTEGER)&KphpCidApcTimeout);
if (status != STATUS_SUCCESS)
{
NTSTATUS removeStatus;
NT_ASSERT(status == STATUS_TIMEOUT);
removeStatus = KsiRemoveQueueApc(&apc->Apc);
KphTracePrint(TRACE_LEVEL_VERBOSE,
TRACKING,
"KsiRemoveQueueApc: %!STATUS!",
removeStatus);
goto Exit;
}
Exit:
if (apc)
{
KphDereferenceObject(apc);
}
if (!NT_SUCCESS(status) || (status == STATUS_TIMEOUT))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
TRACKING,
"KphpInitializeThreadContextApc failed: %!STATUS!",
status);
}
}
/**
* \brief Initializes a thread context.
*
* \param[in] Object The thread context object to initialize.
* \param[in] Parameter Unused
*
* \return STATUS_SUCCESS
*/
_Function_class_(KPH_TYPE_INITIALIZE_PROCEDURE)
_IRQL_requires_max_(APC_LEVEL)
_Must_inspect_result_
NTSTATUS KSIAPI KphpInitializeThreadContext(
_Inout_ PVOID Object,
_In_opt_ PVOID Parameter
)
{
NTSTATUS status;
PKPH_THREAD_CONTEXT thread;
PETHREAD threadObject;
HANDLE threadHandle;
KPH_PAGED_CODE();
NT_ASSERT(Parameter);
thread = Object;
threadObject = Parameter;
status = ObOpenObjectByPointer(threadObject,
OBJ_KERNEL_HANDLE,
NULL,
THREAD_ALL_ACCESS,
*PsThreadType,
KernelMode,
&threadHandle);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
TRACKING,
"ObOpenObjectByPointer failed %!STATUS!",
status);
threadHandle = NULL;
goto Exit;
}
status = ZwQueryInformationThread(threadHandle,
ThreadSubsystemInformation,
&thread->SubsystemType,
sizeof(SUBSYSTEM_INFORMATION_TYPE),
NULL);
if (status == STATUS_INVALID_INFO_CLASS)
{
thread->SubsystemType = SubsystemInformationTypeWin32;
}
else if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
TRACKING,
"ThreadSubsystemInformation failed %!STATUS!",
status);
goto Exit;
}
thread->EThread = threadObject;
ObReferenceObject(thread->EThread);
thread->ClientId.UniqueThread = PsGetThreadId(thread->EThread);
thread->ClientId.UniqueProcess = PsGetThreadProcessId(thread->EThread);
NT_ASSERT(!thread->ProcessContext);
thread->ProcessContext = KphGetProcessContext(thread->ClientId.UniqueProcess);
if (thread->ProcessContext)
{
KphAcquireRWLockExclusive(&thread->ProcessContext->ThreadListLock);
if (!thread->ProcessContext->InitialThread)
{
thread->ProcessContext->InitialThread = thread;
KphReferenceObject(thread);
}
InsertTailList(&thread->ProcessContext->ThreadListHead,
&thread->ThreadListEntry);
thread->ProcessContext->NumberOfThreads++;
thread->InThreadList = TRUE;
KphReferenceObject(thread);
KphReleaseRWLock(&thread->ProcessContext->ThreadListLock);
}
if (!PsIsSystemThread(thread->EThread))
{
KphpInitThreadContextInOriginalEnvironment(thread, FALSE);
}
status = STATUS_SUCCESS;
Exit:
if (threadHandle)
{
ObCloseHandle(threadHandle, KernelMode);
}
return status;
}
/**
* \brief Deletes a thread context.
*
* \param[in] Object The thread context object to delete.
*/
_Function_class_(KPH_TYPE_DELETE_PROCEDURE)
_IRQL_requires_max_(PASSIVE_LEVEL)
VOID KSIAPI KphpDeleteThreadContext(
_Inout_ PVOID Object
)
{
PKPH_THREAD_CONTEXT thread;
KPH_PAGED_CODE_PASSIVE();
thread = Object;
NT_ASSERT(!thread->InThreadList);
KphAtomicAssignObjectReference(&thread->RequestSessionToken.Atomic, NULL);
KphAtomicAssignObjectReference(&thread->SessionToken.Atomic, NULL);
NT_ASSERT(thread->EThread);
ObDereferenceObject(thread->EThread);
if (thread->ProcessContext)
{
KphDereferenceObject(thread->ProcessContext);
}
}
/**
* \brief Frees a thread context object.
*
* \param[in] Object The thread context object to free.
*/
_Function_class_(KPH_TYPE_FREE_PROCEDURE)
_IRQL_requires_max_(PASSIVE_LEVEL)
VOID KSIAPI KphpFreeThreadContext(
_In_freesMem_ PVOID Object
)
{
KPH_PAGED_CODE_PASSIVE();
NT_ASSERT(KphpThreadContextLookaside);
KphFreeToNPagedLookasideObject(KphpThreadContextLookaside, Object);
KphDereferenceObject(KphpThreadContextLookaside);
}
/**
* \brief Initializes the CID tracking infrastructure.
*
* \return Successful or errant status.
*/
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KphCidInitialize(
VOID
)
{
NTSTATUS status;
KPH_OBJECT_TYPE_INFO typeInfo;
KPH_PAGED_CODE_PASSIVE();
status = KphCidTableCreate(&KphpCidTable);
if (!NT_SUCCESS(status))
{
return status;
}
KeInitializeEvent(&KphpCidPopulatedEvent, NotificationEvent, FALSE);
status = KphCreateNPagedLookasideObject(&KphpCidApcLookaside,
KphAddObjectHeaderSize(sizeof(KPH_CID_APC)),
KPH_TAG_CID_APC);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
TRACKING,
"KphCreateNPagedLookasideObject failed: %!STATUS!",
status);
KphpCidApcLookaside = NULL;
goto Exit;
}
status = KphCreateNPagedLookasideObject(&KphpProcessContextLookaside,
KphAddObjectHeaderSize(sizeof(KPH_PROCESS_CONTEXT)),
KPH_TAG_PROCESS_CONTEXT);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
TRACKING,
"KphCreatePagedLookasideObject failed: %!STATUS!",
status);
KphpProcessContextLookaside = NULL;
goto Exit;
}
status = KphCreateNPagedLookasideObject(&KphpThreadContextLookaside,
KphAddObjectHeaderSize(sizeof(KPH_THREAD_CONTEXT)),
KPH_TAG_THREAD_CONTEXT);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
TRACKING,
"KphCreatePagedLookasideObject failed: %!STATUS!",
status);
KphpThreadContextLookaside = NULL;
goto Exit;
}
typeInfo.Allocate = KphpAllocateProcessContext;
typeInfo.Initialize = KphpInitializeProcessContext;
typeInfo.Delete = KphpDeleteProcessContext;
typeInfo.Free = KphpFreeProcessContext;
typeInfo.Flags = 0;
typeInfo.DeferDelete = TRUE;
KphCreateObjectType(&KphpProcessContextTypeName,
&typeInfo,
&KphProcessContextType);
typeInfo.Allocate = KphpAllocateThreadContext;
typeInfo.Initialize = KphpInitializeThreadContext;
typeInfo.Delete = KphpDeleteThreadContext;
typeInfo.Free = KphpFreeThreadContext;
typeInfo.Flags = 0;
typeInfo.DeferDelete = TRUE;
KphCreateObjectType(&KphpThreadContextTypeName,
&typeInfo,
&KphThreadContextType);
typeInfo.Allocate = KphpAllocateCidApc;
typeInfo.Initialize = KphpInitializeCidApc;
typeInfo.Delete = KphpDeleteCidApc;
typeInfo.Free = KphpFreeCidApc;
typeInfo.Flags = 0;
KphCreateObjectType(&KphpCidApcTypeName,
&typeInfo,
&KphpCidApcType);
KphpCidTrackingInitialized = TRUE;
status = STATUS_SUCCESS;
Exit:
if (!NT_SUCCESS(status))
{
KphCidTableDelete(&KphpCidTable);
if (KphpProcessContextLookaside)
{
KphDereferenceObject(KphpProcessContextLookaside);
KphpProcessContextLookaside = NULL;
}
if (KphpThreadContextLookaside)
{
KphDereferenceObject(KphpThreadContextLookaside);
KphpThreadContextLookaside = NULL;
}
if (KphpCidApcLookaside)
{
KphDereferenceObject(KphpCidApcLookaside);
KphpCidApcLookaside = NULL;
}
}
return status;
}
/**
* \brief Unlinks thread contexts from a process context.
*
* \param[in] Process The process context to unlink thread contexts from.
*/
_IRQL_requires_max_(APC_LEVEL)
VOID KphpUnlinkProcessContextThreadContexts(
_In_ PKPH_PROCESS_CONTEXT Process
)
{
KPH_PAGED_CODE();
KphAcquireRWLockExclusive(&Process->ThreadListLock);
while (!IsListEmpty(&Process->ThreadListHead))
{
PKPH_THREAD_CONTEXT thread;
thread = CONTAINING_RECORD(RemoveHeadList(&Process->ThreadListHead),
KPH_THREAD_CONTEXT,
ThreadListEntry);
NT_ASSERT(thread->InThreadList);
Process->NumberOfThreads--;
Process->NumberOfUnlinkedThreads++;
thread->InThreadList = FALSE;
KphDereferenceObject(thread);
}
if (Process->InitialThread)
{
KphDereferenceObject(Process->InitialThread);
Process->InitialThread = NULL;
}
KphReleaseRWLock(&Process->ThreadListLock);
}
/**
* \brief Cleanup callback for enumerating the CID tracking table.
*
* \param[in] Object The CID table entry to clean up.
* \param[in] Parameter Unused
*
* \return FALSE to continue enumerating.
*/
_Function_class_(KPH_CID_RUNDOWN_CALLBACK)
_IRQL_requires_max_(PASSIVE_LEVEL)
VOID KSIAPI KphpCidCleanupCallback(
_In_ PVOID Object,
_In_opt_ PVOID Parameter
)
{
KPH_PAGED_CODE_PASSIVE();
UNREFERENCED_PARAMETER(Parameter);
if (KphGetObjectType(Object) == KphProcessContextType)
{
PKPH_PROCESS_CONTEXT process;
process = Object;
KphpUnlinkProcessContextThreadContexts(process);
}
}
/**
* \brief Cleans up the CID tracking infrastructure.
*/
_IRQL_requires_max_(PASSIVE_LEVEL)
VOID KphCidCleanup(
VOID
)
{
KPH_PAGED_CODE_PASSIVE();
if (!KphpCidTrackingInitialized)
{
return;
}
KphCidRundown(&KphpCidTable, KphpCidCleanupCallback, NULL);
if (KphpSystemProcessContext)
{
KphDereferenceObject(KphpSystemProcessContext);
}
KphCidTableDelete(&KphpCidTable);
KphDereferenceObject(KphpCidApcLookaside);
KphDereferenceObject(KphpThreadContextLookaside);
KphDereferenceObject(KphpProcessContextLookaside);
}
/**
* \brief Begins tracking a context object in the CID tracking. May return an
* existing object if it is already being tracked.
*
* \param[in] Cid The CID of the object to being tracking.
* \param[in] ObjectType The expected object type if the CID.
* \param[in] ObjectBodySize The size of the context body.
* \param[in] Parameter The parameter passed to the creation routine.
*
* \return Pointer to the context object, null on allocation failure or if the
* object is already being tracked and is not of the expected type. The caller
* *must* dereference the object when they are through with it.
*/
_IRQL_requires_max_(APC_LEVEL)
_Must_inspect_result_
PVOID KphpTrackContext(
_In_ HANDLE Cid,
_In_ PKPH_OBJECT_TYPE ObjectType,
_In_ ULONG ObjectBodySize,
_In_ PVOID Parameter
)
{
NTSTATUS status;
PKPH_CID_TABLE_ENTRY entry;
PVOID object;
KPH_PAGED_CODE();
entry = KphCidGetEntry(Cid, &KphpCidTable);
if (!entry)
{
return NULL;
}
object = KphCidReferenceObject(entry);
if (object)
{
if (KphGetObjectType(object) != ObjectType)
{
KphDereferenceObject(object);
object = NULL;
}
return object;
}
status = KphCreateObject(ObjectType, ObjectBodySize, &object, Parameter);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
TRACKING,
"KphCreateObject (%lu) failed: %!STATUS!",
HandleToULong(Cid),
status);
return NULL;
}
KphCidAssignObject(entry, object);
return object;
}
/**
* \brief Stops tracking a context object in the CID tracking.
*
* \param[in] Cid The CID of the object to being tracking.
* \param[in] ObjectType The expected object type if the CID.
* \param[in] ObjectBodySize The size of the context body.
*
* \return Pointer to the context object, null if not found or the object is
* not of the expected type. The caller *must* dereference the object when they
* are through with it.
*/
_IRQL_requires_max_(APC_LEVEL)
_Must_inspect_result_
PVOID KphpUntrackContext(
_In_ HANDLE Cid,
_In_ PKPH_OBJECT_TYPE ObjectType
)
{
PKPH_CID_TABLE_ENTRY entry;
PVOID object;
KPH_PAGED_CODE();
entry = KphCidGetEntry(Cid, &KphpCidTable);
if (!entry)
{
return NULL;
}
object = KphCidReferenceObject(entry);
if (object && (KphGetObjectType(object) != ObjectType))
{
KphDereferenceObject(object);
return NULL;
}
KphCidAssignObject(entry, NULL);
return object;
}
/**
* \brief Performs actions post population of CID table.
*
* \param[in] Process The context being enumerated.
* \param[in] Parameter Unused.
*
* \return FALSE
*/
_Function_class_(KPH_ENUM_CID_CONTEXTS_CALLBACK)
_Must_inspect_result_
BOOLEAN KSIAPI KphpCidEnumPostPopulate(
_In_ PVOID Context,
_In_opt_ PVOID Parameter
)
{
PKPH_OBJECT_TYPE objectType;
PKPH_PROCESS_CONTEXT process;
KPH_PAGED_CODE_PASSIVE();
UNREFERENCED_PARAMETER(Parameter);
objectType = KphGetObjectType(Context);
if (objectType == KphProcessContextType)
{
process = Context;
KphVerifyProcessAndProtectIfAppropriate(process);
}
return FALSE;
}
// from phnative.h
#define KPH_FIRST_PROCESS(Processes) ((PSYSTEM_PROCESS_INFORMATION)(Processes))
#define KPH_NEXT_PROCESS(Process) ( \
((PSYSTEM_PROCESS_INFORMATION)(Process))->NextEntryOffset ? \
(PSYSTEM_PROCESS_INFORMATION)Add2Ptr((Process), \
((PSYSTEM_PROCESS_INFORMATION)(Process))->NextEntryOffset) : \
NULL \
)
/**
* \brief Populates the CID tracking.
*
* \return Successful or errant status.
*/
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KphCidPopulate(
VOID
)
{
NTSTATUS status;
ULONG size;
PVOID buffer;
PSYSTEM_PROCESS_INFORMATION info;
KPH_PAGED_CODE_PASSIVE();
size = (PAGE_SIZE * 4);
buffer = KphAllocatePaged(size, KPH_TAG_CID_POPULATE);
if (!buffer)
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
TRACKING,
"Failed to allocate buffer for process information.");
status = STATUS_INSUFFICIENT_RESOURCES;
goto Exit;
}
for (;;)
{
status = ZwQuerySystemInformation(SystemProcessInformation,
buffer,
size,
&size);
if (NT_SUCCESS(status))
{
break;
}
if ((status != STATUS_BUFFER_TOO_SMALL) &&
(status != STATUS_INFO_LENGTH_MISMATCH))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
TRACKING,
"ZwQuerySystemInformation failed: %!STATUS!",
status);
goto Exit;
}
if (size == 0)
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
TRACKING,
"ZwQuerySystemInformation returned zero size!");
NT_ASSERT(!NT_SUCCESS(status));
goto Exit;
}
KphFree(buffer, KPH_TAG_CID_POPULATE);
buffer = KphAllocatePaged(size, KPH_TAG_CID_POPULATE);
if (!buffer)
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
TRACKING,
"Failed to allocate buffer for process information.");
status = STATUS_INSUFFICIENT_RESOURCES;
goto Exit;
}
KphTracePrint(TRACE_LEVEL_VERBOSE,
TRACKING,
"Trying again with larger buffer (%lu)...",
size);
}
//
// N.B. Because of the way we start up we guarantee the thread and process
// notifications routines are blocked until we complete this work. This
// gives us a guarantee that a new process/thread can't be fully created or
// tracked before we get here. However, this is still the possibility of
// a TOCTOU for an exiting process/thread. We need to take care here during
// initialization. If the process/thread is already exited we should not
// track it.
//
NT_ASSERT(NT_SUCCESS(status));
NT_ASSERT(size >= sizeof(SYSTEM_PROCESS_INFORMATION));
for (info = KPH_FIRST_PROCESS(buffer); info; info = KPH_NEXT_PROCESS(info))
{
PKPH_PROCESS_CONTEXT process;
PEPROCESS processObject;
if (!info->UniqueProcessId)
{
//
// Idle Process.
// For now we aren't going to track it. There are "valid" watchdog
// threads in the idle process, but we can't look up their thread
// objects in a documented way. For simplicity, for now, we opt
// not to track it.
//
continue;
}
if (info->UniqueProcessId == PsGetProcessId(PsInitialSystemProcess))
{
//
// System Process
//
processObject = PsInitialSystemProcess;
ObReferenceObject(processObject);
}
else
{
//
// Check if we should track this process during population.
// Ultimately here we're ensuring the process isn't already exited.
// Otherwise there is the possibility of an object leak from TOCTOU.
//
status = PsLookupProcessByProcessId(info->UniqueProcessId,
&processObject);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
TRACKING,
"PsLookupProcessByProcessId failed: %!STATUS!",
status);
continue;
}
if (PsGetProcessExitProcessCalled(processObject))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
TRACKING,
"PsGetProcessExitProcessCalled reported TRUE "
"(process %lu)",
HandleToULong(info->UniqueProcessId));
ObDereferenceObject(processObject);
continue;
}
}
process = KphpTrackContext(info->UniqueProcessId,
KphProcessContextType,
sizeof(KPH_PROCESS_CONTEXT),
processObject);
ObDereferenceObject(processObject);
processObject = NULL;
if (!process)
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
TRACKING,
"KphpTrackContext failed (process %lu)",
HandleToULong(info->UniqueProcessId));
continue;
}
process->CreatorClientId.UniqueProcess = NtCurrentProcess();
process->CreatorClientId.UniqueThread = NtCurrentThread();
KphTracePrint(TRACE_LEVEL_VERBOSE,
TRACKING,
"Tracking process %wZ (%lu)",
&process->ImageName,
HandleToULong(process->ProcessId));
for (ULONG i = 0; i < info->NumberOfThreads; i++)
{
PKPH_THREAD_CONTEXT thread;
PETHREAD threadObject;
PSYSTEM_THREAD_INFORMATION threadInfo;
threadInfo = &info->Threads[i];
status = PsLookupThreadByThreadId(threadInfo->ClientId.UniqueThread,
&threadObject);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
TRACKING,
"PsLookupThreadByThreadId failed "
"(thread %lu in process %wZ (%lu))): %!STATUS!",
HandleToULong(threadInfo->ClientId.UniqueThread),
&process->ImageName,
HandleToULong(process->ProcessId),
status);
continue;
}
if (PsIsThreadTerminating(threadObject))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
TRACKING,
"PsIsThreadTerminating reported TRUE "
"(thread %lu in process %wZ (%lu)))",
HandleToULong(threadInfo->ClientId.UniqueThread),
&process->ImageName,
HandleToULong(process->ProcessId));
ObDereferenceObject(threadObject);
continue;
}
thread = KphpTrackContext(threadInfo->ClientId.UniqueThread,
KphThreadContextType,
sizeof(KPH_THREAD_CONTEXT),
threadObject);
ObDereferenceObject(threadObject);
threadObject = NULL;
if (!thread)
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
TRACKING,
"KphpTrackContext failed "
"(thread %lu in process %wZ (%lu))",
HandleToULong(threadInfo->ClientId.UniqueThread),
&process->ImageName,
HandleToULong(process->ProcessId));
continue;
}
thread->CreatorClientId.UniqueProcess = NtCurrentProcess();
thread->CreatorClientId.UniqueThread = NtCurrentThread();
KphTracePrint(TRACE_LEVEL_VERBOSE,
TRACKING,
"Tracking thread %lu in process %wZ (%lu)",
HandleToULong(thread->ClientId.UniqueThread),
&process->ImageName,
HandleToULong(thread->ClientId.UniqueProcess));
KphDereferenceObject(thread);
}
KphDereferenceObject(process);
}
Exit:
KphCidMarkPopulated();
if (buffer)
{
KphFree(buffer, KPH_TAG_CID_POPULATE);
}
if (NT_SUCCESS(status))
{
KphEnumerateCidContexts(KphpCidEnumPostPopulate, NULL);
}
return status;
}
/**
* \brief Tracks a process context.
*
* \param[in] Process The process to start tracking.
*
* \return Pointer to the process context, null on allocation failure. May
* return an existing process context if the process is already tracked. The
* caller *must* dereference the object when they are through with it.
*/
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
PKPH_PROCESS_CONTEXT KphTrackProcessContext(
_In_ PEPROCESS Process
)
{
KPH_PAGED_CODE_PASSIVE();
KphpCidWaitForPopulate();
return KphpTrackContext(PsGetProcessId(Process),
KphProcessContextType,
sizeof(KPH_PROCESS_CONTEXT),
Process);
}
/**
* \brief Stops tracking a process context.
*
* \param[in] ProcessId The process ID of the process stop tracking.
*
* \return Pointer to the process context, null if not found. The caller *must*
* dereference the object when they are through with it.
*/
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
PKPH_PROCESS_CONTEXT KphUntrackProcessContext(
_In_ HANDLE ProcessId
)
{
PKPH_PROCESS_CONTEXT process;
KPH_PAGED_CODE_PASSIVE();
KphpCidWaitForPopulate();
process = KphpUntrackContext(ProcessId, KphProcessContextType);
if (!process)
{
return NULL;
}
KphpUnlinkProcessContextThreadContexts(process);
return process;
}
/**
* \brief Tracks a thread context.
*
* \param[in] Thread The thread to start tracking.
*
* \return Pointer to the thread context, null on allocation failure. May
* return an existing thread context if the thread is already tracked. The
* caller *must* dereference the object when they are through with it.
*/
_IRQL_requires_max_(APC_LEVEL)
_Must_inspect_result_
PKPH_THREAD_CONTEXT KphTrackThreadContext(
_In_ PETHREAD Thread
)
{
KPH_PAGED_CODE();
KphpCidWaitForPopulate();
return KphpTrackContext(PsGetThreadId(Thread),
KphThreadContextType,
sizeof(KPH_THREAD_CONTEXT),
Thread);
}
/**
* \brief Stops tracking a thread context.
*
* \param[in] ThreadId The thread ID of the thread stop tracking.
*
* \return Pointer to the thread context, null if not found. The caller *must*
* dereference the object when they are through with it.
*/
_IRQL_requires_max_(APC_LEVEL)
_Must_inspect_result_
PKPH_THREAD_CONTEXT KphUntrackThreadContext(
_In_ HANDLE ThreadId
)
{
PKPH_THREAD_CONTEXT thread;
KPH_PAGED_CODE();
KphpCidWaitForPopulate();
thread = KphpUntrackContext(ThreadId, KphThreadContextType);
if (!thread)
{
return NULL;
}
if (thread->ProcessContext)
{
KphAcquireRWLockExclusive(&thread->ProcessContext->ThreadListLock);
if (thread->InThreadList)
{
RemoveEntryList(&thread->ThreadListEntry);
thread->InThreadList = FALSE;
thread->ProcessContext->NumberOfThreads--;
KphDereferenceObject(thread);
}
if (thread->ProcessContext->InitialThread == thread)
{
thread->ProcessContext->InitialThread = NULL;
KphDereferenceObject(thread);
}
KphReleaseRWLock(&thread->ProcessContext->ThreadListLock);
}
return thread;
}
typedef struct _KPH_ENUM_CONTEXT
{
PKPH_ENUM_CID_CONTEXTS_CALLBACK CidCallback;
PKPH_ENUM_PROCESS_CONTEXTS_CALLBACK ProcessCallback;
PKPH_ENUM_THREAD_CONTEXTS_CALLBACK ThreadCallback;
PVOID Parameter;
} KPH_ENUM_CONTEXT, *PKPH_ENUM_CONTEXT;
/**
* \brief Enumeration callback.
*
* \param[in] Object The object from the enumeration.
* \param[in] Parameter The enumeration context from the caller.
*
* \return FALSE to keep enumerating if the object type is not what was asked
* for or the return value from callers callback.
*/
_Function_class_(KPH_CID_ENUMERATE_CALLBACK)
BOOLEAN KSIAPI KphpEnumerateContexts(
_In_ PVOID Object,
_In_opt_ PVOID Parameter
)
{
PKPH_ENUM_CONTEXT context;
PKPH_OBJECT_TYPE objectType;
KPH_PAGED_CODE();
NT_ASSERT(Parameter);
context = Parameter;
objectType = KphGetObjectType(Object);
if (context->CidCallback)
{
NT_ASSERT((objectType == KphProcessContextType) ||
(objectType == KphThreadContextType));
NT_ASSERT(!context->ProcessCallback);
NT_ASSERT(!context->ThreadCallback);
return context->CidCallback(Object, context->Parameter);
}
if (context->ProcessCallback)
{
NT_ASSERT(!context->CidCallback);
NT_ASSERT(!context->ThreadCallback);
if (objectType != KphProcessContextType)
{
return FALSE;
}
return context->ProcessCallback(Object, context->Parameter);
}
NT_ASSERT(!context->CidCallback);
NT_ASSERT(!context->ProcessCallback);
NT_ASSERT(context->ThreadCallback);
if (objectType != KphThreadContextType)
{
return FALSE;
}
return context->ThreadCallback(Object, context->Parameter);
}
/**
* \brief Enumerates process contexts.
*
* \param[in] Callback The callback to invoke for each process context. The
* callback should return TRUE to stop enumerating or FALSE to continue.
* \param[in] Parameter Optional parameter passed to the callback.
*/
_IRQL_requires_max_(APC_LEVEL)
VOID KphEnumerateProcessContexts(
_In_ PKPH_ENUM_PROCESS_CONTEXTS_CALLBACK Callback,
_In_opt_ PVOID Parameter
)
{
KPH_ENUM_CONTEXT context;
KPH_PAGED_CODE();
context.CidCallback = NULL;
context.ProcessCallback = Callback;
context.ThreadCallback = NULL;
context.Parameter = Parameter;
KphCidEnumerate(&KphpCidTable, KphpEnumerateContexts, &context);
}
/**
* \brief Enumerates thread contexts.
*
* \param[in] Callback The callback to invoke for each thread context. The
* callback should return TRUE to stop enumerating or FALSE to continue.
* \param[in] Parameter Optional parameter passed to the callback.
*/
_IRQL_requires_max_(APC_LEVEL)
VOID KphEnumerateThreadContexts(
_In_ PKPH_ENUM_THREAD_CONTEXTS_CALLBACK Callback,
_In_opt_ PVOID Parameter
)
{
KPH_ENUM_CONTEXT context;
KPH_PAGED_CODE();
context.CidCallback = NULL;
context.ProcessCallback = NULL;
context.ThreadCallback = Callback;
context.Parameter = Parameter;
KphCidEnumerate(&KphpCidTable, KphpEnumerateContexts, &context);
}
/**
* \brief Enumerates CID contexts.
*
* \param[in] Callback The callback to invoke for each CID context. The
* callback should return TRUE to stop enumerating or FALSE to continue.
* \param[in] Parameter Optional parameter passed to the callback.
*/
_IRQL_requires_max_(APC_LEVEL)
VOID KphEnumerateCidContexts(
_In_ KPH_ENUM_CID_CONTEXTS_CALLBACK Callback,
_In_opt_ PVOID Parameter
)
{
KPH_ENUM_CONTEXT context;
KPH_PAGED_CODE();
context.CidCallback = Callback;
context.ProcessCallback = NULL;
context.ThreadCallback = NULL;
context.Parameter = Parameter;
KphCidEnumerate(&KphpCidTable, KphpEnumerateContexts, &context);
}
/**
* \brief Checks the APC no-op routine for a given process.
*
* \param[in] Process The context of a process to check the routine of.
*
* \return Successful or errant status.
*/
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KphCheckProcessApcNoopRoutine(
_In_ PKPH_PROCESS_CONTEXT Process
)
{
NTSTATUS status;
HANDLE processHandle;
PROCESS_MITIGATION_POLICY_INFORMATION policyInfo;
KPH_PAGED_CODE_PASSIVE();
if (Process->ApcNoopRoutine)
{
return STATUS_SUCCESS;
}
status = ObOpenObjectByPointer(Process->EProcess,
OBJ_KERNEL_HANDLE,
NULL,
PROCESS_ALL_ACCESS,
*PsProcessType,
KernelMode,
&processHandle);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
TRACKING,
"ObOpenObjectByPointer failed: %!STATUS!",
status);
processHandle = NULL;
goto Exit;
}
policyInfo.Policy = ProcessControlFlowGuardPolicy;
policyInfo.ControlFlowGuardPolicy.Flags = 0;
status = ZwQueryInformationProcess(processHandle,
ProcessMitigationPolicy,
&policyInfo,
sizeof(PROCESS_MITIGATION_POLICY_INFORMATION),
NULL);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
TRACKING,
"ZwQueryInformationProcess failed: %!STATUS!",
status);
goto Exit;
}
if (policyInfo.ControlFlowGuardPolicy.EnableXfg)
{
status = KphDisableXfgOnTarget(processHandle, KphNtDllRtlSetBits);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
TRACKING,
"KphDisableXfgOnTarget failed (0x%08lx): %!STATUS!",
policyInfo.ControlFlowGuardPolicy.Flags,
status);
goto Exit;
}
}
if (policyInfo.ControlFlowGuardPolicy.EnableExportSuppression)
{
status = KphGuardGrantSuppressedCallAccess(processHandle,
KphNtDllRtlSetBits);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
TRACKING,
"KphGuardGrantSuppressedCallAccess failed "
"(0x%08lx): %!STATUS!",
policyInfo.ControlFlowGuardPolicy.Flags,
status);
goto Exit;
}
}
Process->ApcNoopRoutine = (PKNORMAL_ROUTINE)KphNtDllRtlSetBits;
status = STATUS_SUCCESS;
Exit:
if (processHandle)
{
ObCloseHandle(processHandle, KernelMode);
}
return status;
}
/**
* \brief Performs actions to verify a process and begin protecting it. Process
* protection is only started processes that meet the necessary requirements.
*
* \param[in] Process The context of a process verify and protect.
*/
_IRQL_requires_max_(PASSIVE_LEVEL)
VOID KphVerifyProcessAndProtectIfAppropriate(
_In_ PKPH_PROCESS_CONTEXT Process
)
{
NTSTATUS status;
KPH_PAGED_CODE_PASSIVE();
if (Process->ImageFileName &&
Process->FileObject &&
!Process->VerifiedProcess)
{
status = KphVerifyFile(Process->ImageFileName, Process->FileObject);
KphTracePrint(TRACE_LEVEL_VERBOSE,
VERIFY,
"KphVerifyFile: %lu \"%wZ\": %!STATUS!",
HandleToULong(Process->ProcessId),
Process->ImageFileName,
status);
if (NT_SUCCESS(status))
{
Process->VerifiedProcess = TRUE;
}
}
if (KphTestProcessContextState(Process, KPH_PROCESS_STATE_LOW))
{
ACCESS_MASK processAllowedMask;
ACCESS_MASK threadAllowedMask;
if (KphProtectionsSuppressed())
{
//
// Allow all access, but still exercise the code by registering.
//
processAllowedMask = ((ACCESS_MASK)-1);
threadAllowedMask = ((ACCESS_MASK)-1);
}
else
{
processAllowedMask = KPH_PROTECTED_PROCESS_MASK;
threadAllowedMask = KPH_PROTECTED_THREAD_MASK;
}
status = KphStartProtectingProcess(Process,
processAllowedMask,
threadAllowedMask);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
PROTECTION,
"KphStartProtectingProcess failed: %!STATUS!",
status);
NT_ASSERT(!Process->Protected);
}
}
}
/**
* \brief Gets the process image name for a given thread.
*
* \details The image name of a thread might not be available if there is no
* process context associated with the thread. This is very unlikely but
* possible if resources are constrained on the system. If there is no process
* context associated with the thread, this function will return NULL.
*
* \param[in] Thread The thread context to get the process image name of.
*
* \return Image name for the process of the given thread, otherwise NULL.
*/
_IRQL_requires_max_(APC_LEVEL)
_Must_inspect_result_
_Maybenull_
PUNICODE_STRING KphGetThreadImageName(
_In_ PKPH_THREAD_CONTEXT Thread
)
{
KPH_PAGED_CODE();
if (!Thread->ProcessContext)
{
return NULL;
}
return &Thread->ProcessContext->ImageName;
}
/**
* \brief Retrieves the process state mask from a process.
*
* \param[in] Process The process to get the state from.
*
* \return State mask describing what state the process is in.
*/
_IRQL_requires_max_(APC_LEVEL)
KPH_PROCESS_STATE KphGetProcessState(
_In_ PKPH_PROCESS_CONTEXT Process
)
{
KPH_PROCESS_STATE processState;
KPH_PAGED_CODE();
if (KphProtectionsSuppressed())
{
//
// This ultimately permits low state callers into the driver. But still
// check for verification. We still want to exercise the code below,
// regardless.
//
processState = ~KPH_PROCESS_VERIFIED_PROCESS;
}
else
{
processState = 0;
}
if (Process->SecurelyCreated)
{
processState |= KPH_PROCESS_SECURELY_CREATED;
}
if (Process->VerifiedProcess)
{
processState |= KPH_PROCESS_VERIFIED_PROCESS;
}
if (Process->Protected)
{
processState |= KPH_PROCESS_PROTECTED_PROCESS;
}
if (Process->CreateNotification)
{
processState |= KPH_PROCESS_CREATE_NOTIFICATION;
}
if (ReadSizeTAcquire(&Process->NumberOfUntrustedImageLoads) == 0)
{
processState |= KPH_PROCESS_NO_UNTRUSTED_IMAGES;
}
if (!Process->StateTracking.Debugged)
{
if (!PsIsProcessBeingDebugged(Process->EProcess))
{
processState |= KPH_PROCESS_NOT_BEING_DEBUGGED;
}
else
{
Process->StateTracking.Debugged = TRUE;
}
}
if (!Process->FileObject)
{
return processState;
}
processState |= KPH_PROCESS_HAS_FILE_OBJECT;
if (!Process->StateTracking.FileObjectWritable)
{
if (!Process->FileObject->WriteAccess || !Process->FileObject->SharedWrite)
{
processState |= KPH_PROCESS_NO_WRITABLE_FILE_OBJECT;
}
else
{
Process->StateTracking.FileObjectWritable = TRUE;
}
}
if (!Process->StateTracking.FileObjectTransaction)
{
if (!IoGetTransactionParameterBlock(Process->FileObject))
{
processState |= KPH_PROCESS_NO_FILE_TRANSACTION;
}
else
{
Process->StateTracking.FileObjectTransaction = TRUE;
}
}
if (!Process->FileObject->SectionObjectPointer)
{
return processState;
}
processState |= KPH_PROCESS_HAS_SECTION_OBJECT_POINTERS;
if (!Process->StateTracking.UserWritableReferences)
{
if (!MmDoesFileHaveUserWritableReferences(Process->FileObject->SectionObjectPointer))
{
processState |= KPH_PROCESS_NO_USER_WRITABLE_REFERENCES;
}
else
{
Process->StateTracking.UserWritableReferences = TRUE;
}
}
return processState;
}
/**
* \brief Queries information about a process context.
*
* \details Some information is not cached up front for one reason or another.
* Usually because dynamic data isn't available. This path enables a generic
* way to query information about a process context and try to populate and the
* cache the information if it is not already available.
*
* \param[in] Process The process context to query.
* \param[in] InformationClass The information class to query.
* \param[out] Information Optional buffer to receive the information.
* \param[in] InformationLength The size of the information buffer.
* \param[out] ReturnLength Receives the number of bytes written or required.
*
* \return Successful or errant status.
*/
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KphQueryInformationProcessContext(
_In_ PKPH_PROCESS_CONTEXT Process,
_In_ KPH_PROCESS_CONTEXT_INFORMATION_CLASS InformationClass,
_Out_writes_bytes_opt_(InformationLength) PVOID Information,
_In_ ULONG InformationLength,
_Out_opt_ PULONG ReturnLength
)
{
NTSTATUS status;
ULONG returnLength;
KPH_PAGED_CODE_PASSIVE();
returnLength = 0;
switch (InformationClass)
{
case KphProcessContextWSLProcessId:
{
if (Process->SubsystemType != SubsystemInformationTypeWSL)
{
status = STATUS_INVALID_PARAMETER;
goto Exit;
}
returnLength = sizeof(ULONG);
if (!Information || (InformationLength < sizeof(ULONG)))
{
status = STATUS_INFO_LENGTH_MISMATCH;
goto Exit;
}
if (!Process->WSL.ValidProcessId)
{
PKPH_THREAD_CONTEXT thread;
KphAcquireRWLockShared(&Process->ThreadListLock);
thread = Process->InitialThread;
if (thread)
{
KphReferenceObject(thread);
}
KphReleaseRWLock(&Process->ThreadListLock);
if (!thread)
{
status = STATUS_INSUFFICIENT_RESOURCES;
goto Exit;
}
KphpInitThreadContextInOriginalEnvironment(thread, TRUE);
KphDereferenceObject(thread);
}
if (!Process->WSL.ValidProcessId)
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
GENERAL,
"WSL process ID is not valid.");
status = STATUS_NOINTERFACE;
goto Exit;
}
*(PULONG)Information = Process->WSL.ProcessId;
status = STATUS_SUCCESS;
break;
}
default:
{
status = STATUS_INVALID_INFO_CLASS;
break;
}
}
Exit:
if (ReturnLength)
{
*ReturnLength = returnLength;
}
return status;
}
/**
* \brief Queries information about a thread context.
*
* \details Some information is not cached up front for one reason or another.
* Usually because dynamic data isn't available. This path enables a generic
* way to query information about a thread context and try to populate and the
* cache the information if it is not already available.
*
* \param[in] Thread The thread context to query.
* \param[in] InformationClass The information class to query.
* \param[out] Information Optional buffer to receive the information.
* \param[in] InformationLength The size of the information buffer.
* \param[out] ReturnLength Receives the number of bytes written or required.
*
* \return Successful or errant status.
*/
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KphQueryInformationThreadContext(
_In_ PKPH_THREAD_CONTEXT Thread,
_In_ KPH_THREAD_CONTEXT_INFORMATION_CLASS InformationClass,
_Out_writes_bytes_opt_(InformationLength) PVOID Information,
_In_ ULONG InformationLength,
_Out_opt_ PULONG ReturnLength
)
{
NTSTATUS status;
ULONG returnLength;
KPH_PAGED_CODE_PASSIVE();
returnLength = 0;
switch (InformationClass)
{
case KphThreadContextWSLThreadId:
{
if (Thread->SubsystemType != SubsystemInformationTypeWSL)
{
status = STATUS_INVALID_HANDLE;
goto Exit;
}
returnLength = sizeof(ULONG);
if (!Information || (InformationLength < sizeof(ULONG)))
{
status = STATUS_INFO_LENGTH_MISMATCH;
goto Exit;
}
if (!Thread->WSL.ValidThreadId)
{
KphpInitThreadContextInOriginalEnvironment(Thread, TRUE);
}
if (!Thread->WSL.ValidThreadId)
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
GENERAL,
"WSL thread ID is not valid.");
status = STATUS_NOINTERFACE;
goto Exit;
}
*(PULONG)Information = Thread->WSL.ThreadId;
status = STATUS_SUCCESS;
break;
}
default:
{
status = STATUS_INVALID_INFO_CLASS;
break;
}
}
Exit:
if (ReturnLength)
{
*ReturnLength = returnLength;
}
return status;
}
================================================
FILE: KSystemInformer/comms.c
================================================
/*
* Copyright (c) 2022 Winsider Seminars & Solutions, Inc. All rights reserved.
*
* This file is part of System Informer.
*
* Authors:
*
* jxy-s 2022-2026
*
*/
#include
#include
#include
#include
#include
#define KPH_COMMS_MIN_QUEUE_THREADS 2
#define KPH_COMMS_MAX_QUEUE_THREADS 64
#define KPH_COMMS_MAX_CLIENTS 32
typedef struct _KPHM_QUEUE_ITEM
{
LIST_ENTRY Entry;
BOOLEAN NonPaged;
PKPH_MESSAGE Message;
ULONG TargetClientCount;
PKPH_CLIENT TargetClients[KPH_COMMS_MAX_CLIENTS];
} KPHM_QUEUE_ITEM, *PKPHM_QUEUE_ITEM;
KPH_PROTECTED_DATA_SECTION_RO_PUSH();
static const UNICODE_STRING KphpClientTypeName = RTL_CONSTANT_STRING(L"KphClient");
static const UNICODE_STRING KphpClientInformerStateTypeName = RTL_CONSTANT_STRING(L"KphClientInformerState");
static const LARGE_INTEGER KphpMessageMinTimeout = KPH_TIMEOUT(300);
static const UNICODE_STRING KphpMessageQueueThreadName = RTL_CONSTANT_STRING(L"System Informer Message Queue");
KPH_PROTECTED_DATA_SECTION_RO_POP();
KPH_PROTECTED_DATA_SECTION_PUSH();
static PFLT_PORT KphpFltServerPort = NULL;
static PKPH_OBJECT_TYPE KphpClientType = NULL;
static PKPH_OBJECT_TYPE KphpClientInformerStateType = NULL;
KPH_PROTECTED_DATA_SECTION_POP();
static KPH_RUNDOWN KphpCommsRundown;
static PAGED_LOOKASIDE_LIST KphpMessageLookaside;
static NPAGED_LOOKASIDE_LIST KphpNPagedMessageLookaside;
static NPAGED_LOOKASIDE_LIST KphpMessageQueueItemLookaside;
static ALIGNED_EX_SPINLOCK KphpConnectedClientsLock = 0;
static ULONG KphpConnectedClientsCount = 0;
static PKPH_CLIENT KphpConnectedClients[KPH_COMMS_MAX_CLIENTS] = { NULL };
static KQUEUE KphpMessageQueue;
static PETHREAD* KphpMessageQueueThreads = NULL;
static ULONG KphpMessageQueueThreadsCount = 0;
/**
* \brief Gets the number of connected clients.
*
* \return Number of connected clients.
*/
_IRQL_requires_max_(DISPATCH_LEVEL)
ULONG KphGetConnectedClientCount(
VOID
)
{
ULONG count;
KIRQL oldIrql;
KPH_NPAGED_CODE_DISPATCH_MAX();
oldIrql = ExAcquireSpinLockShared(&KphpConnectedClientsLock);
count = KphpConnectedClientsCount;
ExReleaseSpinLockShared(&KphpConnectedClientsLock, oldIrql);
return count;
}
/**
* \brief Gets the number of active informer clients.
*
* \return Number of active informer clients.
*/
_IRQL_requires_max_(DISPATCH_LEVEL)
ULONG KphGetInformerClientCount(
VOID
)
{
ULONG count;
KIRQL oldIrql;
KPH_NPAGED_CODE_DISPATCH_MAX();
count = 0;
oldIrql = ExAcquireSpinLockShared(&KphpConnectedClientsLock);
for (ULONG i = 0; i < KphpConnectedClientsCount; i++)
{
PKPH_CLIENT client;
PKPH_CLIENT_INFORMER_STATE state;
client = KphpConnectedClients[i];
state = KphAtomicReferenceObject(&client->InformerState.Atomic);
if (state)
{
count++;
KphDereferenceObject(state);
}
}
ExReleaseSpinLockShared(&KphpConnectedClientsLock, oldIrql);
return count;
}
/**
* \brief Retrieves the connected clients. The caller is responsible for
* dereferencing the clients after use.
*
* \param[out] Clients Receives the connected clients.
*
* \return Number of connected clients.
*/
_IRQL_requires_max_(DISPATCH_LEVEL)
ULONG KphpGetInformerClients(
_Out_writes_to_(KPH_COMMS_MAX_CLIENTS, return) PKPH_CLIENT* Clients
)
{
ULONG count;
KIRQL oldIrql;
KPH_NPAGED_CODE_DISPATCH_MAX();
count = 0;
oldIrql = ExAcquireSpinLockShared(&KphpConnectedClientsLock);
for (ULONG i = 0; i < KphpConnectedClientsCount; i++)
{
PKPH_CLIENT client;
PKPH_CLIENT_INFORMER_STATE state;
client = KphpConnectedClients[i];
state = KphAtomicReferenceObject(&client->InformerState.Atomic);
if (state)
{
KphReferenceObject(client);
Clients[count++] = client;
KphDereferenceObject(state);
}
}
ExReleaseSpinLockShared(&KphpConnectedClientsLock, oldIrql);
return count;
}
/**
* \brief Adds a client to the connected clients list.
*
* \param[in] Client The client to add.
*
* \return TRUE if the client was added successfully, FALSE otherwise.
*/
_IRQL_requires_max_(DISPATCH_LEVEL)
_Must_inspect_result_
BOOLEAN KphpAddConnectedClient(
_In_ PKPH_CLIENT Client
)
{
BOOLEAN result;
KIRQL oldIrql;
KPH_NPAGED_CODE_DISPATCH_MAX();
result = FALSE;
oldIrql = ExAcquireSpinLockExclusive(&KphpConnectedClientsLock);
if (KphpConnectedClientsCount < KPH_COMMS_MAX_CLIENTS)
{
KphpConnectedClients[KphpConnectedClientsCount++] = Client;
KphReferenceObject(Client);
result = TRUE;
}
ExReleaseSpinLockExclusive(&KphpConnectedClientsLock, oldIrql);
return result;
}
/**
* \brief Removes a client from the connected clients list. The caller is
* responsible for dereferencing any return client.
*
* \param[in] Client The client to remove.
*
* \return The removed client if it was found, NULL otherwise.
*/
_IRQL_requires_max_(DISPATCH_LEVEL)
_Must_inspect_result_
PKPH_CLIENT KphpRemoveConnectedClient(
_In_ PKPH_CLIENT Client
)
{
PKPH_CLIENT client;
KIRQL oldIrql;
KPH_NPAGED_CODE_DISPATCH_MAX();
client = NULL;
oldIrql = ExAcquireSpinLockExclusive(&KphpConnectedClientsLock);
for (ULONG i = 0; i < KphpConnectedClientsCount; i++)
{
if (KphpConnectedClients[i] != Client)
{
continue;
}
client = KphpConnectedClients[i];
KphpConnectedClientsCount--;
RtlMoveMemory(&KphpConnectedClients[i],
&KphpConnectedClients[i + 1],
sizeof(PKPH_CLIENT) * ((KPH_COMMS_MAX_CLIENTS - i) - 1));
KphpConnectedClients[KPH_COMMS_MAX_CLIENTS - 1] = NULL;
break;
}
ExReleaseSpinLockExclusive(&KphpConnectedClientsLock, oldIrql);
return client;
}
/**
* \brief Allocates a message queue item.
*
* \return Message queue item, null on allocation failure.
*/
_IRQL_requires_max_(DISPATCH_LEVEL)
_Return_allocatesMem_
PKPHM_QUEUE_ITEM KphpAllocateMessageQueueItem()
{
KPH_NPAGED_CODE_DISPATCH_MAX();
return KphAllocateFromNPagedLookaside(&KphpMessageQueueItemLookaside);
}
/**
* \brief Allocates a non-paged message.
*
* \return Non-paged message, null on allocation failure.
*/
_IRQL_requires_max_(DISPATCH_LEVEL)
_Return_allocatesMem_
PKPH_MESSAGE KphAllocateNPagedMessage(
VOID
)
{
KPH_NPAGED_CODE_DISPATCH_MAX();
return KphAllocateFromNPagedLookaside(&KphpNPagedMessageLookaside);
}
/**
* \brief Frees a non-paged message.
*
* \param[in] Message The message to free.
*/
_IRQL_requires_max_(DISPATCH_LEVEL)
VOID KphFreeNPagedMessage(
_In_freesMem_ PKPH_MESSAGE Message
)
{
NT_ASSERT(Message);
KPH_NPAGED_CODE_DISPATCH_MAX();
KphFreeToNPagedLookaside(&KphpNPagedMessageLookaside, Message);
}
/**
* \brief Determines whether a message is rate limited for a client.
*
* \param[in] Client The client to check the rate limit for.
* \param[in] Message The message to check the rate limit for.
*
* \return TRUE if the message is rate limited, FALSE otherwise.
*/
_IRQL_requires_max_(DISPATCH_LEVEL)
BOOLEAN KphpCommsMessageIsRateLimited(
_In_ PKPH_CLIENT Client,
_In_ PKPH_MESSAGE Message
)
{
PKPH_CLIENT_INFORMER_STATE state;
BOOLEAN limited;
ULONG index;
KPH_NPAGED_CODE_DISPATCH_MAX();
limited = FALSE;
state = KphAtomicReferenceObject(&Client->InformerState.Atomic);
if (!state)
{
goto Exit;
}
index = (Message->Header.MessageId - (MaxKphMsgClientAllowed + 1));
if (!NT_VERIFY(index < KPH_INFORMER_COUNT))
{
goto Exit;
}
if (KphRateLimitConsumeToken(&state->InformerRateLimit[index],
&Message->Header.TimeStamp))
{
goto Exit;
}
limited = TRUE;
KphTracePrint(TRACE_LEVEL_VERBOSE,
COMMS,
"Message rate limited (%lu - %!TIME!) to client: "
"%wZ (%lu)",
(ULONG)Message->Header.MessageId,
Message->Header.TimeStamp.QuadPart,
&Client->Process->ImageName,
HandleToULong(Client->Process->ProcessId));
Exit:
if (state)
{
KphDereferenceObject(state);
}
return limited;
}
/**
* \brief Determines whether a queue is rate limited for a client.
*
* \param[in] Client The client to check the rate limit for.
* \param[in] Message The message to check the rate limit for.
*
* \return TRUE if the queue is rate limited, FALSE otherwise.
*/
_IRQL_requires_max_(DISPATCH_LEVEL)
BOOLEAN KphpCommsQueueIsRateLimited(
_In_ PKPH_CLIENT Client,
_In_ PKPH_MESSAGE Message
)
{
PKPH_CLIENT_INFORMER_STATE state;
BOOLEAN limited;
KPH_NPAGED_CODE_DISPATCH_MAX();
limited = FALSE;
state = KphAtomicReferenceObject(&Client->InformerState.Atomic);
if (!state)
{
goto Exit;
}
if (KphRateLimitConsumeToken(&state->AsyncQueueRateLimit,
&Message->Header.TimeStamp))
{
goto Exit;
}
limited = TRUE;
KphTracePrint(TRACE_LEVEL_VERBOSE,
COMMS,
"Message rate limited (%lu - %!TIME!) to client: "
"%wZ (%lu)",
(ULONG)Message->Header.MessageId,
Message->Header.TimeStamp.QuadPart,
&Client->Process->ImageName,
HandleToULong(Client->Process->ProcessId));
Exit:
if (state)
{
KphDereferenceObject(state);
}
return limited;
}
/**
* \brief Retrieves the non-rate-limited clients for a message. The caller is
* responsible for dereferencing the clients after use.
*
* \param[in] Message The message to check the rate limits for.
* \param[out] Clients Receives the non-rate-limited clients.
*
* \return Number of non-rate-limited clients.
*/
_IRQL_requires_max_(DISPATCH_LEVEL)
ULONG KphpGetNonRateLimitedClients(
_Out_writes_to_(KPH_COMMS_MAX_CLIENTS, return) PKPH_CLIENT* Clients,
_In_ PKPH_MESSAGE Message
)
{
ULONG count;
ULONG clientsCount;
PKPH_CLIENT clients[KPH_COMMS_MAX_CLIENTS];
count = 0;
clientsCount = KphpGetInformerClients(clients);
for (ULONG i = 0; i < clientsCount; i++)
{
if (KphpCommsMessageIsRateLimited(clients[i], Message))
{
KphDereferenceObjectDeferDelete(clients[i]);
continue;
}
Clients[count++] = clients[i];
}
return count;
}
/**
* \brief Copies a message to a client ring buffer.
*
* \param[in] Client The client to copy the message to.
* \param[in] Message The message to copy to the ring buffer.
*
* \return TRUE if the message was copied successfully, FALSE otherwise.
*/
_IRQL_requires_max_(DISPATCH_LEVEL)
BOOLEAN KphpCommsCopyMessageToRingBuffer(
_In_ PKPH_CLIENT Client,
_In_ PKPH_MESSAGE Message
)
{
PVOID buffer;
KPH_NPAGED_CODE_DISPATCH_MAX();
if (!Client->RingBuffer)
{
return FALSE;
}
KphTracePrint(TRACE_LEVEL_VERBOSE,
COMMS,
"Sending message (%lu - %!TIME!) to client: %wZ (%lu)",
(ULONG)Message->Header.MessageId,
Message->Header.TimeStamp.QuadPart,
&Client->Process->ImageName,
HandleToULong(Client->Process->ProcessId));
buffer = KphReserveRingBuffer(Client->RingBuffer,
Message->Header.Size);
if (!buffer)
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
COMMS,
"Ring buffer exhausted (%lu - %!TIME!) %wZ (%lu)",
(ULONG)Message->Header.MessageId,
Message->Header.TimeStamp.QuadPart,
&Client->Process->ImageName,
HandleToULong(Client->Process->ProcessId));
return FALSE;
}
RtlCopyMemory(buffer, Message, Message->Header.Size);
KphCommitRingBuffer(Client->RingBuffer, buffer);
return TRUE;
}
/**
* \brief Sends a message to all connected client ring buffers.
*
* \details If a client can not receive the message in its ring buffer, it is
* added to the output. The caller is responsible for dereferencing the clients
* that could not receive the message. The output clients are destined to have
* the message send to them using the asynchronous queue. Client rate limits
* are checked for the message by this routine. If the client is rate limited
* then the client is not added to the output.
*
* \param[out] Clients Receives the clients that could not receive the message.
* \param[in] Message The message to send to the clients.
*
* \return Number of clients that could not receive the message.
*/
_IRQL_requires_max_(DISPATCH_LEVEL)
ULONG KphpCommsSendMessageToRingBuffers(
_Out_writes_to_(KPH_COMMS_MAX_CLIENTS, return) PKPH_CLIENT* Clients,
_In_ PKPH_MESSAGE Message
)
{
ULONG count;
ULONG clientsCount;
PKPH_CLIENT clients[KPH_COMMS_MAX_CLIENTS];
KPH_NPAGED_CODE_DISPATCH_MAX();
count = 0;
clientsCount = KphpGetNonRateLimitedClients(clients, Message);
for (ULONG i = 0; i < clientsCount; i++)
{
if (KphpCommsCopyMessageToRingBuffer(clients[i], Message) ||
KphpCommsQueueIsRateLimited(clients[i], Message))
{
KphDereferenceObjectDeferDelete(clients[i]);
continue;
}
Clients[count++] = clients[i];
}
return count;
}
/**
* \brief Sends a non-paged message asynchronously.
*
* \param[in] Message The message to send asynchronously. The call assumes
* ownership over the message. The caller should *not* free the message after
* it is passed to this function.
*/
_IRQL_requires_max_(DISPATCH_LEVEL)
VOID KphCommsSendNPagedMessageAsync(
_In_aliasesMem_ PKPH_MESSAGE Message
)
{
PKPHM_QUEUE_ITEM item;
ULONG targetClientCount;
PKPH_CLIENT targetClients[KPH_COMMS_MAX_CLIENTS];
KPH_NPAGED_CODE_DISPATCH_MAX();
if (!KphAcquireRundown(&KphpCommsRundown))
{
KphTracePrint(TRACE_LEVEL_WARNING,
COMMS,
"Failed to acquire rundown, dropping message "
"(%lu - %!TIME!)",
(ULONG)Message->Header.MessageId,
Message->Header.TimeStamp.QuadPart);
KphFreeNPagedMessage(Message);
return;
}
targetClientCount = KphpCommsSendMessageToRingBuffers(targetClients,
Message);
if (!targetClientCount)
{
goto Exit;
}
item = KphpAllocateMessageQueueItem();
if (!item)
{
KphTracePrint(TRACE_LEVEL_WARNING,
COMMS,
"Failed to allocate queue item, dropping message "
"(%lu - %!TIME!)",
(ULONG)Message->Header.MessageId,
Message->Header.TimeStamp.QuadPart);
goto Exit;
}
item->Message = Message;
item->NonPaged = TRUE;
Message = NULL;
item->TargetClientCount = targetClientCount;
RtlCopyMemory(item->TargetClients,
targetClients,
sizeof(PKPH_CLIENT) * targetClientCount);
targetClientCount = 0;
KeInsertQueue(&KphpMessageQueue, &item->Entry);
Exit:
for (ULONG i = 0; i < targetClientCount; i++)
{
KphDereferenceObjectDeferDelete(targetClients[i]);
}
if (Message)
{
KphFreeNPagedMessage(Message);
}
KphReleaseRundown(&KphpCommsRundown);
}
/**
* \brief Captures the current stack and adds it to the message.
*
* \param[in,out] Message The message to populate with the current stack.
*/
_IRQL_requires_max_(DISPATCH_LEVEL)
VOID KphCaptureStackInMessage(
_Inout_ PKPH_MESSAGE Message
)
{
NTSTATUS status;
PVOID frames[150];
KPHM_STACK_TRACE stack;
ULONG flags;
KPH_NPAGED_CODE_DISPATCH_MAX();
flags = (KPH_STACK_BACK_TRACE_USER_MODE | KPH_STACK_BACK_TRACE_SKIP_KPH);
stack.Count = (USHORT)KphCaptureStackBackTrace(0,
ARRAYSIZE(frames),
frames,
NULL,
flags);
if (stack.Count == 0)
{
return;
}
stack.Frames = frames;
status = KphMsgDynAddStackTrace(Message, KphMsgFieldStackTrace, &stack);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
COMMS,
"KphMsgDynAddStackTrace failed: %!STATUS!",
status);
}
}
KPH_PAGED_FILE();
/**
* \brief Frees a message queue item.
*
* \param[in] Item Message queue item to free.
*/
_IRQL_requires_max_(APC_LEVEL)
VOID KphpFreeMessageQueueItem(_In_freesMem_ PKPHM_QUEUE_ITEM Item)
{
KPH_PAGED_CODE();
for (ULONG i = 0; i < Item->TargetClientCount; i++)
{
KphDereferenceObject(Item->TargetClients[i]);
}
if (Item->NonPaged)
{
KphFreeNPagedMessage(Item->Message);
}
else
{
KphFreeMessage(Item->Message);
}
KphFreeToNPagedLookaside(&KphpMessageQueueItemLookaside, Item);
}
/**
* \brief Allocates a client object.
*
* \param[in] Size The size requested from the object infrastructure.
*
* \return Allocated client object, null on allocation failure.
*/
_Function_class_(KPH_TYPE_ALLOCATE_PROCEDURE)
_IRQL_requires_max_(PASSIVE_LEVEL)
_Return_allocatesMem_size_(Size)
PVOID KSIAPI KphpAllocateClient(
_In_ SIZE_T Size
)
{
KPH_PAGED_CODE_PASSIVE();
return KphAllocateNPaged(Size, KPH_TAG_CLIENT);
}
/**
* \brief Initializes a client object.
*
* \param[in] Object The client object to initialize.
* \param[in] Parameter The process context associated with the client.
*
* \return STATUS_SUCCESS
*/
_Function_class_(KPH_TYPE_INITIALIZE_PROCEDURE)
_IRQL_requires_max_(APC_LEVEL)
_Must_inspect_result_
NTSTATUS KSIAPI KphpInitializeClient(
_Inout_ PVOID Object,
_In_opt_ PVOID Parameter
)
{
PKPH_CLIENT client;
KPH_PAGED_CODE();
NT_ASSERT(Parameter);
client = Object;
client->Process = Parameter;
KphReferenceObject(client->Process);
return STATUS_SUCCESS;
}
/**
* \brief Deletes a client object.
*
* \param[in] Object The client object to delete.
*/
_Function_class_(KPH_TYPE_DELETE_PROCEDURE)
_IRQL_requires_max_(PASSIVE_LEVEL)
VOID KSIAPI KphpDeleteClient(
_Inout_ PVOID Object
)
{
NTSTATUS status;
PKPH_CLIENT client;
KPH_PAGED_CODE_PASSIVE();
client = Object;
if (client->DriverUnloadProtectionRef.Count)
{
//
// The client is being destroyed while it has acquired driver unload
// protection. The communication handlers only acquire or release the
// protection once for each client, so we only need to call this once
// here.
//
status = KphReleaseDriverUnloadProtection(NULL);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
COMMS,
"KphReleaseDriverUnloadProtection failed: %!STATUS!",
status);
}
}
KphDereferenceObject(client->Process);
if (client->Port)
{
FltCloseClientPort(KphFltFilter, &client->Port);
}
if (client->RingBuffer)
{
KphDereferenceObject(client->RingBuffer);
}
KphAtomicAssignObjectReference(&client->InformerState.Atomic, NULL);
}
/**
* \brief Frees a client object.
*
* \param[in] Object The client object to free.
*/
_Function_class_(KPH_TYPE_FREE_PROCEDURE)
_IRQL_requires_max_(PASSIVE_LEVEL)
VOID KSIAPI KphpFreeClient(
_In_freesMem_ PVOID Object
)
{
KPH_PAGED_CODE_PASSIVE();
KphFree(Object, KPH_TAG_CLIENT);
}
/**
* \brief Allocates a client informer state object.
*
* \param[in] Size The size requested from the object infrastructure.
*
* \return Allocated client informer state object, null on allocation failure.
*/
_Function_class_(KPH_TYPE_ALLOCATE_PROCEDURE)
_IRQL_requires_max_(PASSIVE_LEVEL)
_Return_allocatesMem_size_(Size)
PVOID KSIAPI KphpAllocateClientInformerState(
_In_ SIZE_T Size
)
{
KPH_PAGED_CODE_PASSIVE();
return KphAllocateNPaged(Size, KPH_TAG_CLIENT_INFORMER_STATE);
}
/**
* \brief Initializes a client informer state object.
*
* \param[in] Object The client informer state object to initialize.
* \param[in] Parameter The client informer settings.
*
* \return STATUS_SUCCESS
*/
_Function_class_(KPH_TYPE_INITIALIZE_PROCEDURE)
_IRQL_requires_max_(APC_LEVEL)
_Must_inspect_result_
NTSTATUS KSIAPI KphpInitializeClientInformerState(
_Inout_ PVOID Object,
_In_opt_ PVOID Parameter
)
{
PKPH_CLIENT_INFORMER_STATE state;
PKPH_INFORMER_CLIENT_SETTINGS settings;
LARGE_INTEGER timeStamp;
KPH_PAGED_CODE();
NT_ASSERT(Parameter);
state = Object;
settings = Parameter;
KeQuerySystemTime(&timeStamp);
RtlCopyMemory(&state->MessageTimeouts,
&settings->MessageTimeouts,
sizeof(KPH_MESSAGE_TIMEOUTS));
KphInitializeRateLimit(&settings->AsyncQueuePolicy,
&timeStamp,
&state->AsyncQueueRateLimit);
for (ULONG i = 0; i < KPH_INFORMER_COUNT; i++)
{
KphInitializeRateLimit(&settings->InformerPolicy[i],
&timeStamp,
&state->InformerRateLimit[i]);
}
return STATUS_SUCCESS;
}
/**
* \brief Frees a client informer state object.
*
* \param[in] Object The client informer state object to free.
*/
_Function_class_(KPH_TYPE_FREE_PROCEDURE)
_IRQL_requires_max_(PASSIVE_LEVEL)
VOID KSIAPI KphpFreeClientInformerState(
_In_freesMem_ PVOID Object
)
{
KPH_PAGED_CODE_PASSIVE();
KphFree(Object, KPH_TAG_CLIENT_INFORMER_STATE);
}
/**
* \brief Initializes the client ring buffer.
*
* \param[in] Client The client to initialize the ring buffer for.
* \param[in,out] Connection Pointer to the ring buffer connection context.
*
* \return Successful status or an errant one.
*/
_IRQL_requires_max_(PASSIVE_LEVEL)
NTSTATUS KphpCommsInitializeRingBuffer(
_In_ PKPH_CLIENT Client,
_Inout_ PKPH_RING_BUFFER_CONNECT Connection
)
{
NTSTATUS status;
PKPH_RING_BUFFER ring;
KPH_RING_BUFFER_CONNECT connection;
PKEVENT event;
KPH_PAGED_CODE_PASSIVE();
ring = NULL;
event = NULL;
__try
{
CopyFromUser(&connection, Connection, sizeof(KPH_RING_BUFFER_CONNECT));
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
status = GetExceptionCode();
goto Exit;
}
if (connection.EventHandle)
{
status = ObReferenceObjectByHandle(connection.EventHandle,
EVENT_MODIFY_STATE,
*ExEventObjectType,
UserMode,
&event,
NULL);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
COMMS,
"ObReferenceObjectByHandle failed: %!STATUS!",
status);
event = NULL;
goto Exit;
}
}
status = KphCreateRingBuffer(&ring,
&Connection->Ring,
connection.Length,
event,
UserMode);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
COMMS,
"KphCreateRingBuffer failed: %!STATUS!",
status);
ring = NULL;
goto Exit;
}
Client->RingBuffer = ring;
KphReferenceObject(ring);
Exit:
if (ring)
{
KphDereferenceObject(ring);
}
if (event)
{
ObDereferenceObject(event);
}
return status;
}
/**
* \brief Communication port connect notify callback.
*
* \param[in] ClientPort Client port for this client.
* \param[in] ServerPortCookie Unused
* \param[in] ConnectionContext Context from the connecting client.
* \param[in] SizeOfContext Size of the connection context from the client.
* \param[out] ConnectionPortCookie Returns the client object on success.
*
* \return Successful status or an errant one.
*/
_Function_class_(PFLT_CONNECT_NOTIFY)
_IRQL_requires_max_(PASSIVE_LEVEL)
NTSTATUS FLTAPI KphpCommsConnectNotifyCallback(
_In_ PFLT_PORT ClientPort,
_In_ PVOID ServerPortCookie,
_In_ PVOID ConnectionContext,
_In_ ULONG SizeOfContext,
_Out_ PVOID* ConnectionPortCookie
)
{
NTSTATUS status;
PKPH_PROCESS_CONTEXT process;
KPH_PROCESS_STATE processState;
PKPH_CLIENT client;
KPH_PAGED_CODE_PASSIVE();
UNREFERENCED_PARAMETER(ServerPortCookie);
*ConnectionPortCookie = NULL;
process = NULL;
client = NULL;
if (!KphSinglePrivilegeCheck(SeDebugPrivilege, UserMode))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
COMMS,
"KphSinglePrivilegeCheck failed %lu",
HandleToULong(PsGetCurrentProcessId()));
status = STATUS_PRIVILEGE_NOT_HELD;
goto Exit;
}
process = KphGetCurrentProcessContext();
if (!process)
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
COMMS,
"Untracked process %lu",
HandleToULong(PsGetCurrentProcessId()));
status = STATUS_INSUFFICIENT_RESOURCES;
goto Exit;
}
processState = KphGetProcessState(process);
if (!KphTestProcessState(processState, KPH_PROCESS_STATE_LOW))
{
KphTracePrint(TRACE_LEVEL_CRITICAL,
COMMS,
"Untrusted client %wZ (%lu) (0x%08x)",
&process->ImageName,
HandleToULong(process->ProcessId),
processState);
status = STATUS_ACCESS_DENIED;
goto Exit;
}
status = KphCreateObject(KphpClientType,
sizeof(KPH_CLIENT),
&client,
process);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
COMMS,
"Failed to allocate client object");
client = NULL;
goto Exit;
}
if (ConnectionContext && (SizeOfContext >= sizeof(PVOID)))
{
PKPH_RING_BUFFER_CONNECT connection;
NT_ASSERT(ConnectionContext > MmHighestUserAddress);
connection = *(PVOID*)ConnectionContext;
status = KphpCommsInitializeRingBuffer(client, connection);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
COMMS,
"KphpCommsInitializeRingBuffer failed: %!STATUS!",
status);
goto Exit;
}
}
client->Port = ClientPort;
if (!KphpAddConnectedClient(client))
{
KphTracePrint(TRACE_LEVEL_ERROR,
COMMS,
"Clients at maximum: %wZ (%lu) (0x%08x)",
&client->Process->ImageName,
HandleToULong(client->Process->ProcessId),
processState);
status = STATUS_INSUFFICIENT_RESOURCES;
goto Exit;
}
KphTracePrint(TRACE_LEVEL_INFORMATION,
COMMS,
"Client connected: %wZ (%lu) (0x%08x)",
&client->Process->ImageName,
HandleToULong(client->Process->ProcessId),
processState);
*ConnectionPortCookie = client;
status = STATUS_SUCCESS;
Exit:
if (client)
{
KphDereferenceObject(client);
}
if (process)
{
KphDereferenceObject(process);
}
return status;
}
/**
* \brief Communication port disconnect notification callback.
*
* \param[in] ConnectionCookie Client object
*/
_Function_class_(PFLT_DISCONNECT_NOTIFY)
_IRQL_requires_max_(PASSIVE_LEVEL)
VOID FLTAPI KphpCommsDisconnectNotifyCallback(
_In_ PVOID ConnectionCookie
)
{
PKPH_CLIENT client;
KPH_PAGED_CODE_PASSIVE();
NT_ASSERT(ConnectionCookie);
client = KphpRemoveConnectedClient(ConnectionCookie);
if (client)
{
KphTracePrint(TRACE_LEVEL_INFORMATION,
COMMS,
"Client disconnected: %wZ (%lu)",
&client->Process->ImageName,
HandleToULong(client->Process->ProcessId));
KphDereferenceObject(client);
}
}
/**
* \brief Signals clients when a required state failure occurs on inbound requests.
*
* \param[in] MessageId The message ID that failed.
* \param[in] ClientState The client state that was checked.
* \param[in] RequiredState The state that was required.
*/
_IRQL_requires_max_(PASSIVE_LEVEL)
VOID KphpSendRequiredStateFailure(
_In_ KPH_MESSAGE_ID MessageId,
_In_ KPH_PROCESS_STATE ClientState,
_In_ KPH_PROCESS_STATE RequiredState
)
{
PKPH_MESSAGE msg;
KPH_PAGED_CODE_PASSIVE();
msg = KphAllocateMessage();
if (!msg)
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
COMMS,
"Failed to allocate message");
return;
}
KphMsgInit(msg, KphMsgRequiredStateFailure);
msg->Kernel.RequiredStateFailure.ClientId.UniqueProcess = PsGetCurrentProcessId();
msg->Kernel.RequiredStateFailure.ClientId.UniqueThread = PsGetCurrentThreadId();
msg->Kernel.RequiredStateFailure.MessageId = MessageId;
msg->Kernel.RequiredStateFailure.ClientState = ClientState;
msg->Kernel.RequiredStateFailure.RequiredState = RequiredState;
if (KphInformerOpts(NULL).EnableStackTraces)
{
KphCaptureStackInMessage(msg);
}
KphCommsSendMessageAsync(msg);
}
/**
* \brief Connection port message notification callback.
*
* \param[in] PortCookie Client object
* \param[in] InputBuffer Input buffer from client.
* \param[in] InputBufferLength Length of the input buffer.
* \param[out] OutputBuffer Output buffer from client.
* \param[in] OutputBufferLength Length of the output buffer.
* \param[out] ReturnOutputBufferLength Receives the number of bytes written.
*
* \return Successful or errant status.
*/
_Function_class_(PFLT_MESSAGE_NOTIFY)
_IRQL_requires_max_(PASSIVE_LEVEL)
NTSTATUS FLTAPI KphpCommsMessageNotifyCallback(
_In_ PVOID PortCookie,
_In_opt_ PVOID InputBuffer,
_In_ ULONG InputBufferLength,
_Out_opt_ PVOID OutputBuffer,
_In_ ULONG OutputBufferLength,
_Out_ PULONG ReturnOutputBufferLength
)
{
NTSTATUS status;
PKPH_CLIENT client;
PKPH_MESSAGE msg;
const KPH_MESSAGE_HANDLER* handler;
KPH_PROCESS_STATE processState;
KPH_PROCESS_STATE requiredState;
KPH_PAGED_CODE_PASSIVE();
client = (PKPH_CLIENT)PortCookie;
msg = NULL;
if ((PsGetCurrentProcess() != client->Process->EProcess) ||
(ExGetPreviousMode() != UserMode))
{
KphTracePrint(TRACE_LEVEL_CRITICAL,
COMMS,
"Unexpected caller process!");
status = STATUS_ACCESS_DENIED;
goto Exit;
}
*ReturnOutputBufferLength = 0;
if (!InputBuffer ||
(InputBufferLength < KPH_MESSAGE_MIN_SIZE) ||
(InputBufferLength > sizeof(KPH_MESSAGE)) ||
OutputBuffer ||
(OutputBufferLength > 0))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
COMMS,
"Client message input invalid");
status = STATUS_INVALID_PARAMETER;
goto Exit;
}
msg = KphAllocateMessage();
if (!msg)
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
COMMS,
"Failed to allocate message");
status = STATUS_INSUFFICIENT_RESOURCES;
goto Exit;
}
__try
{
CopyFromUser(msg, InputBuffer, KPH_MESSAGE_MIN_SIZE);
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
status = GetExceptionCode();
goto Exit;
}
status = KphMsgValidate(msg);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
COMMS,
"KphMsgValidate failed: %!STATUS!",
status);
goto Exit;
}
if (msg->Header.Size > KPH_MESSAGE_MIN_SIZE)
{
__try
{
CopyFromUser(msg->_Dyn.Buffer,
Add2Ptr(InputBuffer, KPH_MESSAGE_MIN_SIZE),
(msg->Header.Size - KPH_MESSAGE_MIN_SIZE));
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
status = GetExceptionCode();
goto Exit;
}
}
if ((msg->Header.MessageId <= InvalidKphMsg) ||
(msg->Header.MessageId >= MaxKphMsgClient) ||
((ULONG)msg->Header.MessageId >= KphCommsMessageHandlerCount))
{
KphTracePrint(TRACE_LEVEL_WARNING,
COMMS,
"Client message ID invalid: %lu",
(ULONG)msg->Header.MessageId);
status = STATUS_MESSAGE_NOT_FOUND;
goto Exit;
}
KphTracePrint(TRACE_LEVEL_VERBOSE,
COMMS,
"Received input (%lu - %!TIME!) from client: %wZ (%lu)",
(ULONG)msg->Header.MessageId,
msg->Header.TimeStamp.QuadPart,
&client->Process->ImageName,
HandleToULong(client->Process->ProcessId));
handler = &KphCommsMessageHandlers[msg->Header.MessageId];
NT_ASSERT(handler->MessageId == msg->Header.MessageId);
NT_ASSERT(handler->Handler);
NT_ASSERT(handler->RequiredState);
processState = KphGetProcessState(client->Process);
requiredState = handler->RequiredState(client, msg);
if (!KphTestProcessState(processState, requiredState))
{
KphTracePrint(TRACE_LEVEL_CRITICAL,
COMMS,
"Untrusted client %wZ (%lu) (0x%08x, 0x%08x)",
&client->Process->ImageName,
HandleToULong(client->Process->ProcessId),
processState,
requiredState);
KphpSendRequiredStateFailure(handler->MessageId,
processState,
requiredState);
status = STATUS_ACCESS_DENIED;
goto Exit;
}
status = handler->Handler(client, msg);
if (!NT_SUCCESS(status))
{
goto Exit;
}
NT_ASSERT(NT_SUCCESS(KphMsgValidate(msg)));
//
// We use the input buffer as the input and output. This is on purpose
// to minimize the allocations and copies we have to do.
//
if (InputBufferLength < msg->Header.Size)
{
status = STATUS_BUFFER_TOO_SMALL;
goto Exit;
}
__try
{
CopyToUser(InputBuffer, msg, msg->Header.Size);
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
status = GetExceptionCode();
goto Exit;
}
KphTracePrint(TRACE_LEVEL_VERBOSE,
COMMS,
"Sending output (%lu - %!TIME!) to client: %wZ (%lu)",
(ULONG)msg->Header.MessageId,
msg->Header.TimeStamp.QuadPart,
&client->Process->ImageName,
HandleToULong(client->Process->ProcessId));
status = STATUS_SUCCESS;
Exit:
if (msg)
{
KphFreeMessage(msg);
}
return status;
}
/**
* \brief Frees the communication port security descriptor.
*
* \param[in] SecurityDescriptor Security descriptor to free.
*/
_IRQL_always_function_max_(PASSIVE_LEVEL)
VOID KphpFreeCommsSecurityDescriptor(
_In_freesMem_ PSECURITY_DESCRIPTOR SecurityDescriptor
)
{
KPH_PAGED_CODE_PASSIVE();
FltFreeSecurityDescriptor(SecurityDescriptor);
}
/**
* \brief Builds the communication port security descriptor.
*
* \param[out] SecurityDescriptor On success set to the security descriptor for
* the communication port. Null on failure. The caller should free this pointer
* with KphpFreeCommsSecurityDescriptor.
*
* \return Successful or errant status.
*/
_IRQL_always_function_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KphpBuildCommsSecurityDescriptor(
_Outptr_allocatesMem_ PSECURITY_DESCRIPTOR* SecurityDescriptor
)
{
NTSTATUS status;
KPH_PAGED_CODE_PASSIVE();
status = FltBuildDefaultSecurityDescriptor(SecurityDescriptor,
FLT_PORT_ALL_ACCESS);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
COMMS,
"FltBuildDefaultSecurityDescriptor failed: %!STATUS!",
status);
*SecurityDescriptor = NULL;
goto Exit;
}
Exit:
return status;
}
/**
* \brief Gets the client informer settings.
*
* \param[in] Client The client to get the settings from.
* \param[out] Settings Receives the informer settings.
*
* \return Successful or errant status.
*/
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KphGetInformerClientSettings(
_In_ PKPH_CLIENT Client,
_Out_ PKPH_INFORMER_CLIENT_SETTINGS Settings
)
{
NTSTATUS status;
PKPH_CLIENT_INFORMER_STATE state;
KPH_PAGED_CODE_PASSIVE();
state = NULL;
__try
{
ZeroUserMemory(Settings, sizeof(KPH_INFORMER_CLIENT_SETTINGS));
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
status = GetExceptionCode();
goto Exit;
}
state = KphAtomicReferenceObject(&Client->InformerState.Atomic);
if (!state)
{
status = STATUS_NOT_FOUND;
goto Exit;
}
__try
{
CopyToUser(&Settings->MessageTimeouts,
&state->MessageTimeouts,
sizeof(KPH_MESSAGE_TIMEOUTS));
CopyToUser(&Settings->AsyncQueuePolicy,
&state->AsyncQueueRateLimit.Policy,
sizeof(KPH_RATE_LIMIT_POLICY));
for (ULONG i = 0; i < KPH_INFORMER_COUNT; i++)
{
CopyToUser(&Settings->InformerPolicy[i],
&state->InformerRateLimit[i].Policy,
sizeof(KPH_RATE_LIMIT_POLICY));
}
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
status = GetExceptionCode();
goto Exit;
}
status = STATUS_SUCCESS;
Exit:
if (state)
{
KphDereferenceObject(state);
}
return status;
}
/**
* \brief Sets the client informer settings.
*
* \param[in] Client The client to get the settings for.
* \param[in] Settings The settings to apply.
*
* \return Successful or errant status.
*/
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KphSetInformerClientSettings(
_In_ PKPH_CLIENT Client,
_In_ PKPH_INFORMER_CLIENT_SETTINGS Settings
)
{
NTSTATUS status;
PKPH_INFORMER_CLIENT_SETTINGS settings;
PKPH_CLIENT_INFORMER_STATE state;
KPH_PAGED_CODE_PASSIVE();
state = NULL;
settings = KphAllocatePaged(sizeof(KPH_INFORMER_CLIENT_SETTINGS),
KPH_TAG_CLIENT_INFORMER_SETTINGS);
if (!settings)
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
COMMS,
"Failed to allocate message settings");
status = STATUS_INSUFFICIENT_RESOURCES;
goto Exit;
}
__try
{
CopyFromUser(settings, Settings, sizeof(KPH_INFORMER_CLIENT_SETTINGS));
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
status = GetExceptionCode();
goto Exit;
}
status = KphCreateObject(KphpClientInformerStateType,
sizeof(KPH_CLIENT_INFORMER_STATE),
&state,
settings);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
COMMS,
"KphCreateObject failed: %!STATUS!",
status);
state = NULL;
goto Exit;
}
KphAtomicAssignObjectReference(&Client->InformerState.Atomic, state);
status = STATUS_SUCCESS;
Exit:
if (state)
{
KphDereferenceObject(state);
}
if (settings)
{
KphFree(settings, KPH_TAG_CLIENT_INFORMER_SETTINGS);
}
return status;
}
/**
* \brief Gets informer statistics for a client.
*
* \param[in] Client The client to get the informer statistics for.
* \param[out] Stats Receives the informer statistics.
*
* \return Successful or errant status.
*/
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KphGetInformerClientStats(
_In_ PKPH_CLIENT Client,
_Out_ PKPH_INFORMER_CLIENT_STATS Stats
)
{
NTSTATUS status;
PKPH_CLIENT_INFORMER_STATE state;
KPH_PAGED_CODE_PASSIVE();
state = NULL;
__try
{
ZeroUserMemory(Stats, sizeof(KPH_INFORMER_STATS));
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
status = GetExceptionCode();
goto Exit;
}
state = KphAtomicReferenceObject(&Client->InformerState.Atomic);
if (!state)
{
status = STATUS_NOT_FOUND;
goto Exit;
}
__try
{
CopyToUser(&Stats->AsyncQueueRateLimit.Policy,
&state->AsyncQueueRateLimit.Policy,
sizeof(KPH_RATE_LIMIT_POLICY));
WriteLong64ToUser(&Stats->AsyncQueueRateLimit.Allowed,
ReadNoFence64(&state->AsyncQueueRateLimit.Allowed));
WriteLong64ToUser(&Stats->AsyncQueueRateLimit.Dropped,
ReadNoFence64(&state->AsyncQueueRateLimit.Dropped));
WriteLong64ToUser(&Stats->AsyncQueueRateLimit.CasMiss,
ReadNoFence64(&state->AsyncQueueRateLimit.CasMiss));
for (ULONG i = 0; i < KPH_INFORMER_COUNT; i++)
{
CopyToUser(&Stats->InformerRateLimit[i].Policy,
&state->InformerRateLimit[i].Policy,
sizeof(KPH_RATE_LIMIT_POLICY));
WriteLong64ToUser(&Stats->InformerRateLimit[i].Allowed,
ReadNoFence64(&state->InformerRateLimit[i].Allowed));
WriteLong64ToUser(&Stats->InformerRateLimit[i].Dropped,
ReadNoFence64(&state->InformerRateLimit[i].Dropped));
WriteLong64ToUser(&Stats->InformerRateLimit[i].CasMiss,
ReadNoFence64(&state->InformerRateLimit[i].CasMiss));
}
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
status = GetExceptionCode();
goto Exit;
}
status = STATUS_SUCCESS;
Exit:
if (state)
{
KphDereferenceObject(state);
}
return status;
}
/**
* \brief Retrieves the timeout for a message.
*
* \param[in] Client The client to get the timeout for.
* \param[in] Message The message to retrieve the timeout for.
* \param[in] AsyncTimeout If TRUE the asynchronous timeout is returned, else
* the timeout appropriate for the message is returned.
* \param[out] Timeout Receives the timeout for the message.
*
* \return TRUE if a timeout was retrieved, FALSE otherwise.
*/
_IRQL_requires_max_(APC_LEVEL)
_Must_inspect_result_
BOOLEAN KphpGetTimeoutForMessage(
_In_ PKPH_CLIENT Client,
_In_ PKPH_MESSAGE Message,
_In_ BOOLEAN AsyncTimeout,
_Out_ PLARGE_INTEGER Timeout
)
{
PKPH_CLIENT_INFORMER_STATE state;
KPH_PAGED_CODE();
state = KphAtomicReferenceObject(&Client->InformerState.Atomic);
if (!state)
{
RtlZeroMemory(Timeout, sizeof(LARGE_INTEGER));
return FALSE;
}
if (AsyncTimeout)
{
*Timeout = state->MessageTimeouts.AsyncTimeout;
}
else
{
switch (Message->Header.MessageId)
{
case KphMsgProcessCreate:
{
*Timeout = state->MessageTimeouts.ProcessCreateTimeout;
break;
}
case KphMsgFilePreCreate:
{
*Timeout = state->MessageTimeouts.FilePreCreateTimeout;
break;
}
case KphMsgFilePostCreate:
{
*Timeout = state->MessageTimeouts.FilePostCreateTimeout;
break;
}
default:
{
*Timeout = state->MessageTimeouts.DefaultTimeout;
break;
}
}
}
KphDereferenceObject(state);
return TRUE;
}
/**
* \brief Wrapper for the communication port message send API.
*
* \param[in] ClientPort Client port to send message to.
* \param[in] SendBuffer Buffer to send.
* \param[in] SendBufferLength Length of send buffer.
* \param[out] ReplyBuffer Reply buffer.
* \param[in,out] ReplyBufferLength Length of reply buffer on input, set to
* number of bytes written to reply buffer on output.
* \param[in] Timeout Time allotted for message to be received. If a reply is
* expected this waits for a reply to be received too. If not provided the
* call waits indefinitely.
*
* \return Successful or errant status.
*/
_IRQL_requires_max_(APC_LEVEL)
_Must_inspect_result_
NTSTATUS KphpFltSendMessage(
_In_ PFLT_PORT* ClientPort,
_In_reads_bytes_(SendBufferLength) PVOID SendBuffer,
_In_ ULONG SendBufferLength,
_Out_writes_bytes_opt_(*ReplyBufferLength) PVOID ReplyBuffer,
_Inout_opt_ PULONG ReplyBufferLength,
_In_opt_ PLARGE_INTEGER Timeout
)
{
NTSTATUS status;
KPH_PAGED_CODE();
NT_ASSERT(KphFltFilter);
status = FltObjectReference(KphFltFilter);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
COMMS,
"FltObjectReference failed: %!STATUS!",
status);
return status;
}
status = FltSendMessage(KphFltFilter,
ClientPort,
SendBuffer,
SendBufferLength,
ReplyBuffer,
ReplyBufferLength,
Timeout);
if (status == STATUS_TIMEOUT)
{
//
// return an error status instead
//
status = STATUS_IO_TIMEOUT;
}
FltObjectDereference(KphFltFilter);
return status;
}
/**
* \brief Sends a message asynchronously to a single target client process.
*
* \param[in] Message The message to send asynchronously. This function assumes
* ownership over the message. The caller should *not* free the message after
* it is passed to this function.
* \param[in] TargetClient Optional target client to send the message to. If
* provided the message will only be sent to the target client from the queue
* processing. Otherwise, the message will be sent to all clients.
*/
_IRQL_requires_max_(APC_LEVEL)
VOID KphpCommsSendMessageAsync(
_In_aliasesMem_ PKPH_MESSAGE Message,
_In_opt_ PKPH_CLIENT TargetClient
)
{
PKPHM_QUEUE_ITEM item;
ULONG targetClientCount;
PKPH_CLIENT targetClients[KPH_COMMS_MAX_CLIENTS];
KPH_PAGED_CODE();
if (!KphAcquireRundown(&KphpCommsRundown))
{
KphTracePrint(TRACE_LEVEL_WARNING,
COMMS,
"Failed to acquire rundown, dropping message "
"(%lu - %!TIME!)",
(ULONG)Message->Header.MessageId,
Message->Header.TimeStamp.QuadPart);
KphFreeMessage(Message);
return;
}
if (TargetClient)
{
targetClientCount = 1;
targetClients[0] = TargetClient;
KphReferenceObject(TargetClient);
}
else
{
targetClientCount = KphpCommsSendMessageToRingBuffers(targetClients,
Message);
if (!targetClientCount)
{
goto Exit;
}
}
item = KphpAllocateMessageQueueItem();
if (!item)
{
KphTracePrint(TRACE_LEVEL_WARNING,
COMMS,
"Failed to allocate queue item, dropping message "
"(%lu - %!TIME!)",
(ULONG)Message->Header.MessageId,
Message->Header.TimeStamp.QuadPart);
goto Exit;
}
item->Message = Message;
item->NonPaged = FALSE;
Message = NULL;
item->TargetClientCount = targetClientCount;
RtlCopyMemory(item->TargetClients,
targetClients,
sizeof(PKPH_CLIENT) * targetClientCount);
targetClientCount = 0;
KeInsertQueue(&KphpMessageQueue, &item->Entry);
Exit:
for (ULONG i = 0; i < targetClientCount; i++)
{
KphDereferenceObject(targetClients[i]);
}
if (Message)
{
KphFreeMessage(Message);
}
KphReleaseRundown(&KphpCommsRundown);
}
/**
* \brief Sends a message to a specific client.
*
* \param[in] Client The client to send the message to.
* \param[in] Message The message to send. May contain multiple messages if
* the send is originating from the asynchronous message queue and the
* asynchronous message queue has coalesced multiple messages into one.
* \param[in] Length The length of the message to send. May constitute multiple
* messages if the send is originating from the asynchronous message queue and
* the asynchronous message queue has coalesced multiple messages into one.
* \param[out] Reply The reply from last client.
* \param[in] FromAsyncQueue If TRUE the message is being sent from the
* asynchronous message queue, otherwise FALSE.
*/
_IRQL_requires_max_(APC_LEVEL)
VOID KphpCommsSendMessage(
_In_ PKPH_CLIENT Client,
_In_ PKPH_MESSAGE Message,
_In_ ULONG Length,
_Inout_opt_ PKPH_MESSAGE Reply,
_In_ BOOLEAN FromAsyncQueue
)
{
NTSTATUS status;
PKPH_MESSAGE reply;
ULONG replyLength;
KPH_PROCESS_STATE processState;
LARGE_INTEGER timeout;
KPH_PAGED_CODE();
//
// Since we support multiple clients and only one client may be the
// authoritative reply. We choose to honor the first client to reply.
//
if (Reply && (Reply->Header.MessageId == KphMsgUnhandled))
{
reply = Reply;
replyLength = sizeof(KPH_MESSAGE);
}
else
{
reply = NULL;
replyLength = 0;
}
if (reply && (PsGetCurrentProcess() == Client->Process->EProcess))
{
PKPH_MESSAGE async;
NT_ASSERT(!FromAsyncQueue);
NT_ASSERT(Message->Header.Size == Length);
//
// This is a precaution to prevent a bottleneck. In this case the kernel
// will block for the client to fully reply. If the client generates
// more activity that too ends up fully synchronous it could exhaust
// the client thread pool. This may introduce a bottleneck into the
// system. To avoid this we force this one message to be sent to the
// target client asynchronously. The client will not be permitted to
// reply but will still have visibility.
//
KphTracePrint(TRACE_LEVEL_VERBOSE,
COMMS,
"Bottleneck protection, forcing asynchronous send "
"(%lu - %!TIME!) to client: %wZ (%lu)",
(ULONG)Message->Header.MessageId,
Message->Header.TimeStamp.QuadPart,
&Client->Process->ImageName,
HandleToULong(Client->Process->ProcessId));
//
// We must make a copy of this message to send asynchronously.
//
async = KphAllocateMessage();
if (!async)
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
COMMS,
"Failed to allocate message");
return;
}
RtlCopyMemory(async, Message, Message->Header.Size);
KphpCommsSendMessageAsync(async, Client);
status = STATUS_POSSIBLE_DEADLOCK;
goto Exit;
}
for (ULONG offset = 0; offset < Length; NOTHING)
{
PKPH_MESSAGE message;
NT_ASSERT(offset < sizeof(KPH_MESSAGE));
message = Add2Ptr(Message, offset);
KphTracePrint(TRACE_LEVEL_VERBOSE,
COMMS,
"Sending message (%lu - %!TIME!) to client: %wZ (%lu)",
(ULONG)message->Header.MessageId,
message->Header.TimeStamp.QuadPart,
&Client->Process->ImageName,
HandleToULong(Client->Process->ProcessId));
offset += message->Header.Size;
}
if (!KphpGetTimeoutForMessage(Client, Message, FromAsyncQueue, &timeout))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
COMMS,
"KphpGetTimeoutForMessage failed");
status = STATUS_NOT_FOUND;
goto Exit;
}
status = KphpFltSendMessage(&Client->Port,
Message,
Length,
reply,
(reply ? &replyLength : NULL),
&timeout);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
COMMS,
"KphpFltSendMessage failed: %!STATUS!",
status);
goto Exit;
}
if (!reply)
{
goto Exit;
}
processState = KphGetProcessState(Client->Process);
if (!KphTestProcessState(processState, KPH_PROCESS_STATE_MAXIMUM))
{
KphTracePrint(TRACE_LEVEL_CRITICAL,
COMMS,
"Untrusted client %wZ (%lu) (0x%08x)",
&Client->Process->ImageName,
HandleToULong(Client->Process->ProcessId),
processState);
status = STATUS_REPLY_MESSAGE_MISMATCH;
goto Exit;
}
status = KphMsgValidate(reply);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_WARNING,
COMMS,
"Received invalid reply from client: %wZ (%lu)",
&Client->Process->ImageName,
HandleToULong(Client->Process->ProcessId));
goto Exit;
}
KphTracePrint(TRACE_LEVEL_VERBOSE,
COMMS,
"Received reply (%lu - %!TIME!) from client: %wZ (%lu)",
(ULONG)reply->Header.MessageId,
reply->Header.TimeStamp.QuadPart,
&Client->Process->ImageName,
HandleToULong(Client->Process->ProcessId));
Exit:
if (!NT_SUCCESS(status) && reply)
{
KphMsgInit(reply, KphMsgUnhandled);
}
}
/**
* \brief Attempts to coalesce consecutive items in a message queue to minimize
* the number of individual message transactions.
*
* \details This function optimizes message transmission by combining compatible
* items from the queue into a single message. If coalescing is not possible,
* it simply returns the next item to be sent as-is.
*
* \param[in] Items The items to process.
* \param[in] Count The number of items to process.
* \param[in,out] Index The index of the first item to consider for coalescing.
* Incremented for each coalesced item.
* \param[out] Length Receives the length (in bytes) of the message to send,
* including any coalesced items.
* \param[out] Rundown Set to TRUE if rundown is active, FALSE otherwise.
*
* \return The next item to send, NULL otherwise.
*/
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
PKPHM_QUEUE_ITEM KphpMessageQueueCoalesceItems(
_In_reads_(Count) PLIST_ENTRY* Items,
_In_ ULONG Count,
_Inout_ PULONG Index,
_Out_ PULONG Length,
_Out_ PBOOLEAN Rundown
)
{
NTSTATUS status;
PKPHM_QUEUE_ITEM first;
ULONG coalescedCount;
ULONG remainingLength;
PVOID messagePointer;
KPH_PAGED_CODE_PASSIVE();
NT_ASSERT(*Index < Count);
*Rundown = FALSE;
first = NULL;
remainingLength = sizeof(KPH_MESSAGE);
coalescedCount = 0;
status = (NTSTATUS)(LONG_PTR)Items[*Index];
if ((status == STATUS_TIMEOUT) ||
(status == STATUS_USER_APC))
{
KphTracePrint(TRACE_LEVEL_ERROR,
COMMS,
"Unexpected queue status: %!STATUS!",
status);
goto Exit;
}
if (status == STATUS_ABANDONED)
{
//
// The rundown logic is responsible for draining/servicing the
// remaining queue items.
//
KphTracePrint(TRACE_LEVEL_INFORMATION,
COMMS,
"Message queue running down");
*Rundown = TRUE;
goto Exit;
}
first = CONTAINING_RECORD(Items[*Index], KPHM_QUEUE_ITEM, Entry);
NT_ASSERT(first);
messagePointer = Add2Ptr(first->Message, first->Message->Header.Size);
remainingLength = (sizeof(KPH_MESSAGE) - first->Message->Header.Size);
for (ULONG i = (*Index + 1); i < Count; i++)
{
PKPHM_QUEUE_ITEM item;
BOOLEAN clientsMatch;
status = (NTSTATUS)(LONG_PTR)Items[i];
if ((status == STATUS_TIMEOUT) ||
(status == STATUS_USER_APC))
{
KphTracePrint(TRACE_LEVEL_ERROR,
COMMS,
"Unexpected queue status: %!STATUS!",
status);
continue;
}
if (status == STATUS_ABANDONED)
{
//
// The rundown logic is responsible for draining/servicing the
// remaining queue items.
//
KphTracePrint(TRACE_LEVEL_INFORMATION,
COMMS,
"Message queue running down");
*Rundown = TRUE;
goto Exit;
}
item = CONTAINING_RECORD(Items[i], KPHM_QUEUE_ITEM, Entry);
NT_ASSERT(item);
//
// Verify that the client list matches and there is sufficient room to
// coalesce this message into the first.
//
if (first->TargetClientCount != item->TargetClientCount)
{
break;
}
clientsMatch = TRUE;
for (ULONG j = 0; j < first->TargetClientCount; j++)
{
if (first->TargetClients[j] != item->TargetClients[j])
{
clientsMatch = FALSE;
break;
}
}
if (!clientsMatch)
{
break;
}
if (remainingLength < item->Message->Header.Size)
{
break;
}
RtlCopyMemory(messagePointer,
item->Message,
item->Message->Header.Size);
messagePointer = Add2Ptr(messagePointer, item->Message->Header.Size);
remainingLength -= item->Message->Header.Size;
(*Index)++;
coalescedCount++;
KphpFreeMessageQueueItem(item);
}
Exit:
*Length = (sizeof(KPH_MESSAGE) - remainingLength);
if (coalescedCount)
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
COMMS,
"Coalesced %lu messages (%lu bytes)",
coalescedCount + 1,
*Length);
}
return first;
}
/**
* \brief Processes message queue items.
*
* \param[in] Items The items to process.
* \param[in] Count The number of items to process.
*
* \return TRUE if rundown is active, FALSE otherwise.
*/
_IRQL_requires_max_(PASSIVE_LEVEL)
BOOLEAN KphpMessageQueueProcessItems(
_In_reads_(Count) PLIST_ENTRY* Items,
_In_ ULONG Count
)
{
BOOLEAN rundown;
KPH_PAGED_CODE_PASSIVE();
rundown = FALSE;
for (ULONG i = 0; i < Count; i++)
{
PKPHM_QUEUE_ITEM item;
ULONG length;
item = KphpMessageQueueCoalesceItems(Items,
Count,
&i,
&length,
&rundown);
if (item)
{
for (ULONG j = 0; j < item->TargetClientCount; j++)
{
KphpCommsSendMessage(item->TargetClients[j],
item->Message,
length,
NULL,
TRUE);
}
KphpFreeMessageQueueItem(item);
}
if (rundown)
{
break;
}
}
return rundown;
}
/**
* \brief Asynchronous message queue thread.
*
* \param[in] Parameter Unused
*/
_Function_class_(KPH_THREAD_START_ROUTINE)
_IRQL_requires_max_(PASSIVE_LEVEL)
_IRQL_requires_same_
NTSTATUS KphpMessageQueueThread(
_In_opt_ PVOID Parameter
)
{
PLIST_ENTRY items[32];
ULONG count;
KPH_PAGED_CODE_PASSIVE();
UNREFERENCED_PARAMETER(Parameter);
count = ARRAYSIZE(items);
for (;;)
{
RtlZeroMemory(items, count * sizeof(PLIST_ENTRY));
count = KeRemoveQueueEx(&KphpMessageQueue,
KernelMode,
FALSE,
NULL,
items,
ARRAYSIZE(items));
if (KphpMessageQueueProcessItems(items, count))
{
break;
}
}
return STATUS_SUCCESS;
}
/**
* \brief Starts the communications infrastructure.
*
* \return Successful or errant status.
*/
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KphCommsStart(
VOID
)
{
NTSTATUS status;
KPH_OBJECT_TYPE_INFO typeInfo;
OBJECT_ATTRIBUTES objectAttributes;
PSECURITY_DESCRIPTOR securityDescriptor;
ULONG threadCount;
KPH_PAGED_CODE_PASSIVE();
NT_ASSERT(KphFltFilter);
NT_ASSERT(!KphpFltServerPort);
NT_ASSERT(KphPortName);
securityDescriptor = NULL;
KphInitializeRundown(&KphpCommsRundown);
threadCount = KeQueryActiveProcessorCountEx(ALL_PROCESSOR_GROUPS);
typeInfo.Allocate = KphpAllocateClient;
typeInfo.Initialize = KphpInitializeClient;
typeInfo.Delete = KphpDeleteClient;
typeInfo.Free = KphpFreeClient;
typeInfo.Flags = 0;
KphCreateObjectType(&KphpClientTypeName, &typeInfo, &KphpClientType);
typeInfo.Allocate = KphpAllocateClientInformerState;
typeInfo.Initialize = KphpInitializeClientInformerState;
typeInfo.Delete = NULL;
typeInfo.Free = KphpFreeClientInformerState;
typeInfo.Flags = 0;
KphCreateObjectType(&KphpClientInformerStateTypeName,
&typeInfo,
&KphpClientInformerStateType);
KphInitializePagedLookaside(&KphpMessageLookaside,
sizeof(KPH_MESSAGE),
KPH_TAG_MESSAGE);
KphInitializeNPagedLookaside(&KphpNPagedMessageLookaside,
sizeof(KPH_MESSAGE),
KPH_TAG_NPAGED_MESSAGE);
KphInitializeNPagedLookaside(&KphpMessageQueueItemLookaside,
sizeof(KPHM_QUEUE_ITEM),
KPH_TAG_QUEUE_ITEM);
if (threadCount < KPH_COMMS_MIN_QUEUE_THREADS)
{
threadCount = KPH_COMMS_MIN_QUEUE_THREADS;
}
else if (threadCount > KPH_COMMS_MAX_QUEUE_THREADS)
{
threadCount = KPH_COMMS_MAX_QUEUE_THREADS;
}
KeInitializeQueue(&KphpMessageQueue, threadCount);
KphpMessageQueueThreads = KphAllocatePaged((sizeof(PKTHREAD) * threadCount),
KPH_TAG_THREAD_POOL);
if (!KphpMessageQueueThreads)
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
COMMS,
"Failed to allocate queue threads array");
status = STATUS_INSUFFICIENT_RESOURCES;
goto Exit;
}
NT_ASSERT(KphpMessageQueueThreadsCount == 0);
for (ULONG i = 0; i < threadCount; i++)
{
status = KphCreateSystemThread(NULL,
&KphpMessageQueueThreads[i],
KphpMessageQueueThread,
NULL,
&KphpMessageQueueThreadName,
KPH_CREATE_SYSTEM_THREAD_IN_KSI_PROCESS);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
COMMS,
"KphCreateSystemThread failed: %!STATUS!",
status);
KphpMessageQueueThreads[i] = NULL;
goto Exit;
}
++KphpMessageQueueThreadsCount;
}
status = KphpBuildCommsSecurityDescriptor(&securityDescriptor);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
COMMS,
"KphpBuildCommsSecurityDescriptor failed: %!STATUS!",
status);
goto Exit;
}
InitializeObjectAttributes(&objectAttributes,
KphPortName,
OBJ_KERNEL_HANDLE,
NULL,
securityDescriptor);
status = FltCreateCommunicationPort(KphFltFilter,
&KphpFltServerPort,
&objectAttributes,
NULL,
KphpCommsConnectNotifyCallback,
KphpCommsDisconnectNotifyCallback,
KphpCommsMessageNotifyCallback,
KPH_COMMS_MAX_CLIENTS);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
COMMS,
"FltCreateCommunicationPort failed: %!STATUS!",
status);
KphpFltServerPort = NULL;
goto Exit;
}
status = STATUS_SUCCESS;
Exit:
if (!NT_SUCCESS(status))
{
NT_ASSERT(!KphpFltServerPort);
KphDeleteNPagedLookaside(&KphpMessageQueueItemLookaside);
KphDeleteNPagedLookaside(&KphpNPagedMessageLookaside);
KphDeletePagedLookaside(&KphpMessageLookaside);
NT_VERIFY(KeRundownQueue(&KphpMessageQueue) == NULL);
if (KphpMessageQueueThreads)
{
for (ULONG i = 0; i < KphpMessageQueueThreadsCount; i++)
{
KeWaitForSingleObject(KphpMessageQueueThreads[i],
Executive,
KernelMode,
FALSE,
NULL);
ObDereferenceObject(KphpMessageQueueThreads[i]);
}
KphFree(KphpMessageQueueThreads, KPH_TAG_THREAD_POOL);
KphpMessageQueueThreads = NULL;
KphpMessageQueueThreadsCount = 0;
}
}
if (securityDescriptor)
{
KphpFreeCommsSecurityDescriptor(securityDescriptor);
}
return status;
}
/**
* \brief Stops the communications infrastructure.
*/
_IRQL_requires_max_(PASSIVE_LEVEL)
VOID KphCommsStop(
VOID
)
{
PLIST_ENTRY entry;
KPH_PAGED_CODE_PASSIVE();
if (!KphpFltServerPort)
{
return;
}
KphWaitForRundown(&KphpCommsRundown);
entry = KeRundownQueue(&KphpMessageQueue);
for (ULONG i = 0; i < KphpMessageQueueThreadsCount; i++)
{
KeWaitForSingleObject(KphpMessageQueueThreads[i],
Executive,
KernelMode,
FALSE,
NULL);
ObDereferenceObject(KphpMessageQueueThreads[i]);
}
KphFree(KphpMessageQueueThreads, KPH_TAG_THREAD_POOL);
KphpMessageQueueThreads = NULL;
KphpMessageQueueThreadsCount = 0;
//
// Rundown any remaining items in the queue, if there are any.
//
if (entry != NULL)
{
PLIST_ENTRY first;
first = entry;
do
{
PLIST_ENTRY item;
item = entry;
entry = entry->Flink;
KphpMessageQueueProcessItems(&item, 1);
} while (entry != first);
}
FltCloseCommunicationPort(KphpFltServerPort);
KphDeleteNPagedLookaside(&KphpMessageQueueItemLookaside);
KphDeleteNPagedLookaside(&KphpNPagedMessageLookaside);
KphDeletePagedLookaside(&KphpMessageLookaside);
}
/**
* \brief Allocates a message.
*
* \return Allocated message, null on allocation failure.
*/
_IRQL_requires_max_(APC_LEVEL)
_Return_allocatesMem_
PKPH_MESSAGE KphAllocateMessage(
VOID
)
{
KPH_PAGED_CODE();
return KphAllocateFromPagedLookaside(&KphpMessageLookaside);
}
/**
* \brief Frees a message.
*
* \param[in] Message The message to free.
*/
_IRQL_requires_max_(APC_LEVEL)
VOID
KphFreeMessage(
_In_freesMem_ PKPH_MESSAGE Message
)
{
KPH_PAGED_CODE();
NT_ASSERT(Message);
KphFreeToPagedLookaside(&KphpMessageLookaside, Message);
}
/**
* \brief Sends a message asynchronously.
*
* \param[in] Message The message to send asynchronously. This function assumes
* ownership over the message. The caller should *not* free the message after
* it is passed to this function.
*/
_IRQL_requires_max_(APC_LEVEL)
VOID KphCommsSendMessageAsync(
_In_aliasesMem_ PKPH_MESSAGE Message
)
{
KPH_PAGED_CODE();
KphpCommsSendMessageAsync(Message, NULL);
}
/**
* \brief Sends a message to all connected clients. The first client to respond
* is given authority for any reply.
*
* \details Callers expecting a specific reply should check for the reply
* message identifier even on success. This function will return success with
* KphMsgUnhandled when no client handles the message.
*
* \param[in] Message The message to send.
* \param[out] Reply Optional reply from last client.
*
* \return Successful or errant status.
*/
_IRQL_requires_max_(APC_LEVEL)
_Must_inspect_result_
NTSTATUS KphCommsSendMessage(
_In_ PKPH_MESSAGE Message,
_Out_opt_ PKPH_MESSAGE Reply
)
{
NTSTATUS status;
ULONG clientCount;
PKPH_CLIENT clients[KPH_COMMS_MAX_CLIENTS];
KPH_PAGED_CODE();
NT_ASSERT(NT_SUCCESS(KphMsgValidate(Message)));
if (!KphAcquireRundown(&KphpCommsRundown))
{
return STATUS_TOO_LATE;
}
if (Reply)
{
KphMsgInit(Reply, KphMsgUnhandled);
}
status = STATUS_PORT_DISCONNECTED;
clientCount = KphpGetNonRateLimitedClients(clients, Message);
for (ULONG i = 0; i < clientCount; i++)
{
KphpCommsSendMessage(clients[i],
Message,
Message->Header.Size,
Reply,
FALSE);
KphDereferenceObject(clients[i]);
status = STATUS_SUCCESS;
}
KphReleaseRundown(&KphpCommsRundown);
return status;
}
================================================
FILE: KSystemInformer/comms_handlers.c
================================================
/*
* Copyright (c) 2022 Winsider Seminars & Solutions, Inc. All rights reserved.
*
* This file is part of System Informer.
*
* Authors:
*
* jxy-s 2022-2026
*
*/
#include
#include
#include
#include
KPHM_DEFINE_HANDLER(KphpCommsGetInformerSettings);
KPHM_DEFINE_HANDLER(KphpCommsSetInformerSettings);
KPHM_DEFINE_HANDLER(KphpCommsOpenProcess);
KPHM_DEFINE_HANDLER(KphpCommsOpenProcessToken);
KPHM_DEFINE_HANDLER(KphpCommsOpenProcessJob);
KPHM_DEFINE_HANDLER(KphpCommsTerminateProcess);
KPHM_DEFINE_HANDLER(KphpCommsReadVirtualMemory);
KPHM_DEFINE_HANDLER(KphpCommsOpenThread);
KPHM_DEFINE_HANDLER(KphpCommsOpenThreadProcess);
KPHM_DEFINE_HANDLER(KphpCommsCaptureStackBackTraceThread);
KPHM_DEFINE_HANDLER(KphpCommsEnumerateProcessHandles);
KPHM_DEFINE_HANDLER(KphpCommsQueryInformationObject);
KPHM_DEFINE_HANDLER(KphpCommsSetInformationObject);
KPHM_DEFINE_HANDLER(KphpCommsOpenDriver);
KPHM_DEFINE_HANDLER(KphpCommsQueryInformationDriver);
KPHM_DEFINE_HANDLER(KphpCommsQueryInformationProcess);
KPHM_DEFINE_HANDLER(KphpCommsSetInformationProcess);
KPHM_DEFINE_HANDLER(KphpCommsSetInformationThread);
KPHM_DEFINE_HANDLER(KphpCommsSystemControl);
KPHM_DEFINE_HANDLER(KphpCommsAlpcQueryInformation);
KPHM_DEFINE_HANDLER(KphpCommsQueryInformationFile);
KPHM_DEFINE_HANDLER(KphpCommsQueryVolumeInformationFile);
KPHM_DEFINE_HANDLER(KphpCommsDuplicateObject);
KPHM_DEFINE_HANDLER(KphpCommsQueryPerformanceCounter);
KPHM_DEFINE_HANDLER(KphpCommsCreateFile);
KPHM_DEFINE_HANDLER(KphpCommsQueryInformationThread);
KPHM_DEFINE_HANDLER(KphpCommsQuerySection);
KPHM_DEFINE_HANDLER(KphpCommsCompareObjects);
KPHM_DEFINE_HANDLER(KphpCommsGetInformerClientSettings);
KPHM_DEFINE_HANDLER(KphpCommsSetInformerClientSettings);
KPHM_DEFINE_HANDLER(KphpCommsAcquireDriverUnloadProtection);
KPHM_DEFINE_HANDLER(KphpCommsReleaseDriverUnloadProtection);
KPHM_DEFINE_HANDLER(KphpCommsGetConnectedClientCount);
KPHM_DEFINE_HANDLER(KphpCommsActivateDynData);
KPHM_DEFINE_HANDLER(KphpCommsIsDynDataActive);
KPHM_DEFINE_HANDLER(KphpCommsRequestSessionAccessToken);
KPHM_DEFINE_HANDLER(KphpCommsAssignProcessSessionToken);
KPHM_DEFINE_HANDLER(KphpCommsAssignThreadSessionToken);
KPHM_DEFINE_HANDLER(KphpCommsGetInformerProcessSettings);
KPHM_DEFINE_HANDLER(KphpCommsSetInformerProcessSettings);
KPHM_DEFINE_HANDLER(KphpCommsStripProtectedProcessMasks);
KPHM_DEFINE_HANDLER(KphpCommsQueryVirtualMemory);
KPHM_DEFINE_HANDLER(KphpCommsQueryHashInformationFile);
KPHM_DEFINE_HANDLER(KphpCommsOpenDevice);
KPHM_DEFINE_HANDLER(KphpCommsOpenDeviceDriver);
KPHM_DEFINE_HANDLER(KphpCommsOpenDeviceBaseDevice);
KPHM_DEFINE_HANDLER(KphpCommsGetInformerStats);
KPHM_DEFINE_HANDLER(KphpCommsGetInformerClientStats);
KPHM_DEFINE_REQUIRED_STATE(KphpCommsRequireMaximum);
KPHM_DEFINE_REQUIRED_STATE(KphpCommsRequireMedium);
KPHM_DEFINE_REQUIRED_STATE(KphpCommsRequireLow);
KPHM_DEFINE_REQUIRED_STATE(KphpCommsOpenProcessRequires);
KPHM_DEFINE_REQUIRED_STATE(KphpCommsOpenProcessTokenRequires);
KPHM_DEFINE_REQUIRED_STATE(KphpCommsOpenProcessJobRequires);
KPHM_DEFINE_REQUIRED_STATE(KphpCommsOpenThreadRequires);
KPHM_DEFINE_REQUIRED_STATE(KphpCommsOpenThreadProcessRequires);
KPHM_DEFINE_REQUIRED_STATE(KphpCommsQueryInformationProcessRequires);
KPHM_DEFINE_REQUIRED_STATE(KphpCommsCreateFileRequires);
KPHM_DEFINE_REQUIRED_STATE(KphpCommsQueryInformationThreadRequires);
KPHM_DEFINE_REQUIRED_STATE(KphpCommsQueryVirtualMemoryRequires);
KPH_PROTECTED_DATA_SECTION_RO_PUSH();
const KPH_MESSAGE_HANDLER KphCommsMessageHandlers[] =
{
{ InvalidKphMsg, NULL, NULL },
{ KphMsgGetInformerSettings, KphpCommsGetInformerSettings, KphpCommsRequireLow },
{ KphMsgSetInformerSettings, KphpCommsSetInformerSettings, KphpCommsRequireLow },
{ KphMsgOpenProcess, KphpCommsOpenProcess, KphpCommsOpenProcessRequires },
{ KphMsgOpenProcessToken, KphpCommsOpenProcessToken, KphpCommsOpenProcessTokenRequires },
{ KphMsgOpenProcessJob, KphpCommsOpenProcessJob, KphpCommsOpenProcessJobRequires },
{ KphMsgTerminateProcess, KphpCommsTerminateProcess, KphpCommsRequireMaximum },
{ KphMsgReadVirtualMemory, KphpCommsReadVirtualMemory, KphpCommsRequireMaximum },
{ KphMsgOpenThread, KphpCommsOpenThread, KphpCommsOpenThreadRequires },
{ KphMsgOpenThreadProcess, KphpCommsOpenThreadProcess, KphpCommsOpenThreadProcessRequires },
{ KphMsgCaptureStackBackTraceThread, KphpCommsCaptureStackBackTraceThread, KphpCommsRequireMedium },
{ KphMsgEnumerateProcessHandles, KphpCommsEnumerateProcessHandles, KphpCommsRequireMedium },
{ KphMsgQueryInformationObject, KphpCommsQueryInformationObject, KphpCommsRequireMedium },
{ KphMsgSetInformationObject, KphpCommsSetInformationObject, KphpCommsRequireMaximum },
{ KphMsgOpenDriver, KphpCommsOpenDriver, KphpCommsRequireMaximum },
{ KphMsgQueryInformationDriver, KphpCommsQueryInformationDriver, KphpCommsRequireMaximum },
{ KphMsgQueryInformationProcess, KphpCommsQueryInformationProcess, KphpCommsQueryInformationProcessRequires },
{ KphMsgSetInformationProcess, KphpCommsSetInformationProcess, KphpCommsRequireMaximum },
{ KphMsgSetInformationThread, KphpCommsSetInformationThread, KphpCommsRequireMaximum },
{ KphMsgSystemControl, KphpCommsSystemControl, KphpCommsRequireMaximum },
{ KphMsgAlpcQueryInformation, KphpCommsAlpcQueryInformation, KphpCommsRequireMedium },
{ KphMsgQueryInformationFile, KphpCommsQueryInformationFile, KphpCommsRequireMedium },
{ KphMsgQueryVolumeInformationFile, KphpCommsQueryVolumeInformationFile, KphpCommsRequireMedium },
{ KphMsgDuplicateObject, KphpCommsDuplicateObject, KphpCommsRequireMaximum },
{ KphMsgQueryPerformanceCounter, KphpCommsQueryPerformanceCounter, KphpCommsRequireLow },
{ KphMsgCreateFile, KphpCommsCreateFile, KphpCommsCreateFileRequires },
{ KphMsgQueryInformationThread, KphpCommsQueryInformationThread, KphpCommsQueryInformationThreadRequires },
{ KphMsgQuerySection, KphpCommsQuerySection, KphpCommsRequireMedium },
{ KphMsgCompareObjects, KphpCommsCompareObjects, KphpCommsRequireMedium },
{ KphMsgGetInformerClientSettings, KphpCommsGetInformerClientSettings, KphpCommsRequireLow },
{ KphMsgSetInformerClientSettings, KphpCommsSetInformerClientSettings, KphpCommsRequireLow },
{ KphMsgAcquireDriverUnloadProtection, KphpCommsAcquireDriverUnloadProtection, KphpCommsRequireMaximum },
{ KphMsgReleaseDriverUnloadProtection, KphpCommsReleaseDriverUnloadProtection, KphpCommsRequireMaximum },
{ KphMsgGetConnectedClientCount, KphpCommsGetConnectedClientCount, KphpCommsRequireLow },
{ KphMsgActivateDynData, KphpCommsActivateDynData, KphpCommsRequireLow },
{ KphMsgIsDynDataActive, KphpCommsIsDynDataActive, KphpCommsRequireLow },
{ KphMsgRequestSessionAccessToken, KphpCommsRequestSessionAccessToken, KphpCommsRequireMaximum },
{ KphMsgAssignProcessSessionToken, KphpCommsAssignProcessSessionToken, KphpCommsRequireMaximum },
{ KphMsgAssignThreadSessionToken, KphpCommsAssignThreadSessionToken, KphpCommsRequireMaximum },
{ KphMsgGetInformerProcessSettings, KphpCommsGetInformerProcessSettings, KphpCommsRequireLow },
{ KphMsgSetInformerProcessSettings, KphpCommsSetInformerProcessSettings, KphpCommsRequireLow },
{ KphMsgStripProtectedProcessMasks, KphpCommsStripProtectedProcessMasks, KphpCommsRequireMaximum },
{ KphMsgQueryVirtualMemory, KphpCommsQueryVirtualMemory, KphpCommsQueryVirtualMemoryRequires },
{ KphMsgQueryHashInformationFile, KphpCommsQueryHashInformationFile, KphpCommsRequireMaximum },
{ KphMsgOpenDevice, KphpCommsOpenDevice, KphpCommsRequireMaximum },
{ KphMsgOpenDeviceDriver, KphpCommsOpenDeviceDriver, KphpCommsRequireMaximum },
{ KphMsgOpenDeviceBaseDevice, KphpCommsOpenDeviceBaseDevice, KphpCommsRequireMaximum },
{ KphMsgGetInformerStats, KphpCommsGetInformerStats, KphpCommsRequireLow },
{ KphMsgGetInformerClientStats, KphpCommsGetInformerClientStats, KphpCommsRequireLow },
};
const ULONG KphCommsMessageHandlerCount = ARRAYSIZE(KphCommsMessageHandlers);
C_ASSERT(ARRAYSIZE(KphCommsMessageHandlers) == MaxKphMsgClient);
KPH_PROTECTED_DATA_SECTION_RO_POP();
KPH_PAGED_FILE();
_Function_class_(KPHM_REQUIRED_STATE)
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
KPH_PROCESS_STATE KSIAPI KphpCommsRequireMaximum(
_In_ PKPH_CLIENT Client,
_In_ PCKPH_MESSAGE Message
)
{
KPH_PAGED_CODE_PASSIVE();
NT_ASSERT(ExGetPreviousMode() == UserMode);
UNREFERENCED_PARAMETER(Client);
UNREFERENCED_PARAMETER(Message);
return KPH_PROCESS_STATE_MAXIMUM;
}
_Function_class_(KPHM_REQUIRED_STATE)
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
KPH_PROCESS_STATE KSIAPI KphpCommsRequireMedium(
_In_ PKPH_CLIENT Client,
_In_ PCKPH_MESSAGE Message
)
{
KPH_PAGED_CODE_PASSIVE();
NT_ASSERT(ExGetPreviousMode() == UserMode);
UNREFERENCED_PARAMETER(Client);
UNREFERENCED_PARAMETER(Message);
return KPH_PROCESS_STATE_MEDIUM;
}
_Function_class_(KPHM_REQUIRED_STATE)
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
KPH_PROCESS_STATE KSIAPI KphpCommsRequireLow(
_In_ PKPH_CLIENT Client,
_In_ PCKPH_MESSAGE Message
)
{
KPH_PAGED_CODE_PASSIVE();
NT_ASSERT(ExGetPreviousMode() == UserMode);
UNREFERENCED_PARAMETER(Client);
UNREFERENCED_PARAMETER(Message);
return KPH_PROCESS_STATE_LOW;
}
_Function_class_(KPHM_HANDLER)
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KSIAPI KphpCommsGetInformerSettings(
_In_ PKPH_CLIENT Client,
_Inout_ PKPH_MESSAGE Message
)
{
PKPHM_GET_INFORMER_SETTINGS msg;
KPH_PAGED_CODE_PASSIVE();
NT_ASSERT(ExGetPreviousMode() == UserMode);
NT_ASSERT(Message->Header.MessageId == KphMsgGetInformerSettings);
UNREFERENCED_PARAMETER(Client);
msg = &Message->User.GetInformerSettings;
msg->Status = KphGetInformerSettings(msg->Settings, UserMode);
return STATUS_SUCCESS;
}
_Function_class_(KPHM_HANDLER)
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KSIAPI KphpCommsSetInformerSettings(
_In_ PKPH_CLIENT Client,
_Inout_ PKPH_MESSAGE Message
)
{
PKPHM_SET_INFORMER_SETTINGS msg;
KPH_PAGED_CODE_PASSIVE();
NT_ASSERT(ExGetPreviousMode() == UserMode);
NT_ASSERT(Message->Header.MessageId == KphMsgSetInformerSettings);
UNREFERENCED_PARAMETER(Client);
msg = &Message->User.SetInformerSettings;
msg->Status = KphSetInformerSettings(msg->Settings, UserMode);
return STATUS_SUCCESS;
}
_Function_class_(KPHM_REQUIRED_STATE)
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
KPH_PROCESS_STATE KSIAPI KphpCommsOpenProcessRequires(
_In_ PKPH_CLIENT Client,
_In_ PCKPH_MESSAGE Message
)
{
ACCESS_MASK desiredAccess;
KPH_PAGED_CODE_PASSIVE();
NT_ASSERT(ExGetPreviousMode() == UserMode);
NT_ASSERT(Message->Header.MessageId == KphMsgOpenProcess);
UNREFERENCED_PARAMETER(Client);
desiredAccess = Message->User.OpenProcess.DesiredAccess;
if ((desiredAccess & KPH_PROCESS_READ_ACCESS) != desiredAccess)
{
return KPH_PROCESS_STATE_MAXIMUM;
}
return KPH_PROCESS_STATE_MEDIUM;
}
_Function_class_(KPHM_HANDLER)
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KSIAPI KphpCommsOpenProcess(
_In_ PKPH_CLIENT Client,
_Inout_ PKPH_MESSAGE Message
)
{
PKPHM_OPEN_PROCESS msg;
KPH_PAGED_CODE_PASSIVE();
NT_ASSERT(ExGetPreviousMode() == UserMode);
NT_ASSERT(Message->Header.MessageId == KphMsgOpenProcess);
UNREFERENCED_PARAMETER(Client);
msg = &Message->User.OpenProcess;
msg->Status = KphOpenProcess(msg->ProcessHandle,
msg->DesiredAccess,
msg->ClientId,
UserMode);
return STATUS_SUCCESS;
}
_Function_class_(KPHM_REQUIRED_STATE)
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
KPH_PROCESS_STATE KSIAPI KphpCommsOpenProcessTokenRequires(
_In_ PKPH_CLIENT Client,
_In_ PCKPH_MESSAGE Message
)
{
ACCESS_MASK desiredAccess;
KPH_PAGED_CODE_PASSIVE();
NT_ASSERT(ExGetPreviousMode() == UserMode);
NT_ASSERT(Message->Header.MessageId == KphMsgOpenProcessToken);
UNREFERENCED_PARAMETER(Client);
desiredAccess = Message->User.OpenProcessToken.DesiredAccess;
if ((desiredAccess & KPH_TOKEN_READ_ACCESS) != desiredAccess)
{
return KPH_PROCESS_STATE_MAXIMUM;
}
return KPH_PROCESS_STATE_MEDIUM;
}
_Function_class_(KPHM_HANDLER)
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KSIAPI KphpCommsOpenProcessToken(
_In_ PKPH_CLIENT Client,
_Inout_ PKPH_MESSAGE Message
)
{
PKPHM_OPEN_PROCESS_TOKEN msg;
KPH_PAGED_CODE_PASSIVE();
NT_ASSERT(ExGetPreviousMode() == UserMode);
NT_ASSERT(Message->Header.MessageId == KphMsgOpenProcessToken);
UNREFERENCED_PARAMETER(Client);
msg = &Message->User.OpenProcessToken;
msg->Status = KphOpenProcessToken(msg->ProcessHandle,
msg->DesiredAccess,
msg->TokenHandle,
UserMode);
return STATUS_SUCCESS;
}
_Function_class_(KPHM_REQUIRED_STATE)
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
KPH_PROCESS_STATE KSIAPI KphpCommsOpenProcessJobRequires(
_In_ PKPH_CLIENT Client,
_In_ PCKPH_MESSAGE Message
)
{
ACCESS_MASK desiredAccess;
KPH_PAGED_CODE_PASSIVE();
NT_ASSERT(ExGetPreviousMode() == UserMode);
NT_ASSERT(Message->Header.MessageId == KphMsgOpenProcessJob);
UNREFERENCED_PARAMETER(Client);
desiredAccess = Message->User.OpenProcessJob.DesiredAccess;
if ((desiredAccess & KPH_JOB_READ_ACCESS) != desiredAccess)
{
return KPH_PROCESS_STATE_MAXIMUM;
}
return KPH_PROCESS_STATE_MEDIUM;
}
_Function_class_(KPHM_HANDLER)
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KSIAPI KphpCommsOpenProcessJob(
_In_ PKPH_CLIENT Client,
_Inout_ PKPH_MESSAGE Message
)
{
PKPHM_OPEN_PROCESS_JOB msg;
KPH_PAGED_CODE_PASSIVE();
NT_ASSERT(ExGetPreviousMode() == UserMode);
NT_ASSERT(Message->Header.MessageId == KphMsgOpenProcessJob);
UNREFERENCED_PARAMETER(Client);
msg = &Message->User.OpenProcessJob;
msg->Status = KphOpenProcessJob(msg->ProcessHandle,
msg->DesiredAccess,
msg->JobHandle,
UserMode);
return STATUS_SUCCESS;
}
_Function_class_(KPHM_HANDLER)
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KSIAPI KphpCommsTerminateProcess(
_In_ PKPH_CLIENT Client,
_Inout_ PKPH_MESSAGE Message
)
{
PKPHM_TERMINATE_PROCESS msg;
KPH_PAGED_CODE_PASSIVE();
NT_ASSERT(ExGetPreviousMode() == UserMode);
NT_ASSERT(Message->Header.MessageId == KphMsgTerminateProcess);
UNREFERENCED_PARAMETER(Client);
msg = &Message->User.TerminateProcess;
msg->Status = KphTerminateProcess(msg->ProcessHandle,
msg->ExitStatus,
UserMode);
return STATUS_SUCCESS;
}
_Function_class_(KPHM_HANDLER)
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KSIAPI KphpCommsReadVirtualMemory(
_In_ PKPH_CLIENT Client,
_Inout_ PKPH_MESSAGE Message
)
{
PKPHM_READ_VIRTUAL_MEMORY msg;
KPH_PAGED_CODE_PASSIVE();
NT_ASSERT(ExGetPreviousMode() == UserMode);
NT_ASSERT(Message->Header.MessageId == KphMsgReadVirtualMemory);
UNREFERENCED_PARAMETER(Client);
msg = &Message->User.ReadVirtualMemory;
msg->Status = KphReadVirtualMemory(msg->ProcessHandle,
msg->BaseAddress,
msg->Buffer,
msg->BufferSize,
msg->NumberOfBytesRead,
UserMode);
return STATUS_SUCCESS;
}
_Function_class_(KPHM_REQUIRED_STATE)
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
KPH_PROCESS_STATE KSIAPI KphpCommsOpenThreadRequires(
_In_ PKPH_CLIENT Client,
_In_ PCKPH_MESSAGE Message
)
{
ACCESS_MASK desiredAccess;
KPH_PAGED_CODE_PASSIVE();
NT_ASSERT(ExGetPreviousMode() == UserMode);
NT_ASSERT(Message->Header.MessageId == KphMsgOpenThread);
UNREFERENCED_PARAMETER(Client);
desiredAccess = Message->User.OpenThread.DesiredAccess;
if ((desiredAccess & KPH_THREAD_READ_ACCESS) != desiredAccess)
{
return KPH_PROCESS_STATE_MAXIMUM;
}
return KPH_PROCESS_STATE_MEDIUM;
}
_Function_class_(KPHM_HANDLER)
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KSIAPI KphpCommsOpenThread(
_In_ PKPH_CLIENT Client,
_Inout_ PKPH_MESSAGE Message
)
{
PKPHM_OPEN_THREAD msg;
KPH_PAGED_CODE_PASSIVE();
NT_ASSERT(ExGetPreviousMode() == UserMode);
NT_ASSERT(Message->Header.MessageId == KphMsgOpenThread);
UNREFERENCED_PARAMETER(Client);
msg = &Message->User.OpenThread;
msg->Status = KphOpenThread(msg->ThreadHandle,
msg->DesiredAccess,
msg->ClientId,
UserMode);
return STATUS_SUCCESS;
}
_Function_class_(KPHM_REQUIRED_STATE)
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
KPH_PROCESS_STATE KSIAPI KphpCommsOpenThreadProcessRequires(
_In_ PKPH_CLIENT Client,
_In_ PCKPH_MESSAGE Message
)
{
ACCESS_MASK desiredAccess;
KPH_PAGED_CODE_PASSIVE();
NT_ASSERT(ExGetPreviousMode() == UserMode);
NT_ASSERT(Message->Header.MessageId == KphMsgOpenThreadProcess);
UNREFERENCED_PARAMETER(Client);
desiredAccess = Message->User.OpenThreadProcess.DesiredAccess;
if ((desiredAccess & KPH_PROCESS_READ_ACCESS) != desiredAccess)
{
return KPH_PROCESS_STATE_MAXIMUM;
}
return KPH_PROCESS_STATE_MEDIUM;
}
_Function_class_(KPHM_HANDLER)
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KSIAPI KphpCommsOpenThreadProcess(
_In_ PKPH_CLIENT Client,
_Inout_ PKPH_MESSAGE Message
)
{
PKPHM_OPEN_THREAD_PROCESS msg;
KPH_PAGED_CODE_PASSIVE();
NT_ASSERT(ExGetPreviousMode() == UserMode);
NT_ASSERT(Message->Header.MessageId == KphMsgOpenThreadProcess);
UNREFERENCED_PARAMETER(Client);
msg = &Message->User.OpenThreadProcess;
msg->Status = KphOpenThreadProcess(msg->ThreadHandle,
msg->DesiredAccess,
msg->ProcessHandle,
UserMode);
return STATUS_SUCCESS;
}
_Function_class_(KPHM_HANDLER)
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KSIAPI KphpCommsCaptureStackBackTraceThread(
_In_ PKPH_CLIENT Client,
_Inout_ PKPH_MESSAGE Message
)
{
PKPHM_CAPTURE_STACK_BACKTRACE_THREAD msg;
KPH_PAGED_CODE_PASSIVE();
NT_ASSERT(ExGetPreviousMode() == UserMode);
NT_ASSERT(Message->Header.MessageId == KphMsgCaptureStackBackTraceThread);
UNREFERENCED_PARAMETER(Client);
msg = &Message->User.CaptureStackBackTraceThread;
msg->Status = KphCaptureStackBackTraceThreadByHandle(msg->ThreadHandle,
msg->FramesToSkip,
msg->FramesToCapture,
msg->BackTrace,
msg->CapturedFrames,
msg->BackTraceHash,
msg->Flags,
msg->Timeout,
UserMode);
return STATUS_SUCCESS;
}
_Function_class_(KPHM_HANDLER)
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KSIAPI KphpCommsEnumerateProcessHandles(
_In_ PKPH_CLIENT Client,
_Inout_ PKPH_MESSAGE Message
)
{
PKPHM_ENUMERATE_PROCESS_HANDLES msg;
KPH_PAGED_CODE_PASSIVE();
NT_ASSERT(ExGetPreviousMode() == UserMode);
NT_ASSERT(Message->Header.MessageId == KphMsgEnumerateProcessHandles);
UNREFERENCED_PARAMETER(Client);
msg = &Message->User.EnumerateProcessHandles;
msg->Status = KphEnumerateProcessHandles(msg->ProcessHandle,
msg->Buffer,
msg->BufferLength,
msg->ReturnLength,
UserMode);
return STATUS_SUCCESS;
}
_Function_class_(KPHM_HANDLER)
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KSIAPI KphpCommsQueryInformationObject(
_In_ PKPH_CLIENT Client,
_Inout_ PKPH_MESSAGE Message
)
{
PKPHM_QUERY_INFORMATION_OBJECT msg;
KPH_PAGED_CODE_PASSIVE();
NT_ASSERT(ExGetPreviousMode() == UserMode);
NT_ASSERT(Message->Header.MessageId == KphMsgQueryInformationObject);
UNREFERENCED_PARAMETER(Client);
msg = &Message->User.QueryInformationObject;
msg->Status = KphQueryInformationObject(msg->ProcessHandle,
msg->Handle,
msg->ObjectInformationClass,
msg->ObjectInformation,
msg->ObjectInformationLength,
msg->ReturnLength,
UserMode);
return STATUS_SUCCESS;
}
_Function_class_(KPHM_HANDLER)
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KSIAPI KphpCommsSetInformationObject(
_In_ PKPH_CLIENT Client,
_Inout_ PKPH_MESSAGE Message
)
{
PKPHM_SET_INFORMATION_OBJECT msg;
KPH_PAGED_CODE_PASSIVE();
NT_ASSERT(ExGetPreviousMode() == UserMode);
NT_ASSERT(Message->Header.MessageId == KphMsgSetInformationObject);
UNREFERENCED_PARAMETER(Client);
msg = &Message->User.SetInformationObject;
msg->Status = KphSetInformationObject(msg->ProcessHandle,
msg->Handle,
msg->ObjectInformationClass,
msg->ObjectInformation,
msg->ObjectInformationLength,
UserMode);
return STATUS_SUCCESS;
}
_Function_class_(KPHM_HANDLER)
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KSIAPI KphpCommsOpenDriver(
_In_ PKPH_CLIENT Client,
_Inout_ PKPH_MESSAGE Message
)
{
PKPHM_OPEN_DRIVER msg;
KPH_PAGED_CODE_PASSIVE();
NT_ASSERT(ExGetPreviousMode() == UserMode);
NT_ASSERT(Message->Header.MessageId == KphMsgOpenDriver);
UNREFERENCED_PARAMETER(Client);
msg = &Message->User.OpenDriver;
msg->Status = KphOpenDriver(msg->DriverHandle,
msg->DesiredAccess,
msg->ObjectAttributes,
UserMode);
return STATUS_SUCCESS;
}
_Function_class_(KPHM_HANDLER)
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KSIAPI KphpCommsQueryInformationDriver(
_In_ PKPH_CLIENT Client,
_Inout_ PKPH_MESSAGE Message
)
{
PKPHM_QUERY_INFORMATION_DRIVER msg;
KPH_PAGED_CODE_PASSIVE();
NT_ASSERT(ExGetPreviousMode() == UserMode);
NT_ASSERT(Message->Header.MessageId == KphMsgQueryInformationDriver);
UNREFERENCED_PARAMETER(Client);
msg = &Message->User.QueryInformationDriver;
msg->Status = KphQueryInformationDriver(msg->DriverHandle,
msg->DriverInformationClass,
msg->DriverInformation,
msg->DriverInformationLength,
msg->ReturnLength,
UserMode);
return STATUS_SUCCESS;
}
_Function_class_(KPHM_REQUIRED_STATE)
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
KPH_PROCESS_STATE KSIAPI KphpCommsQueryInformationProcessRequires(
_In_ PKPH_CLIENT Client,
_In_ PCKPH_MESSAGE Message
)
{
KPH_PAGED_CODE_PASSIVE();
NT_ASSERT(ExGetPreviousMode() == UserMode);
NT_ASSERT(Message->Header.MessageId == KphMsgQueryInformationProcess);
UNREFERENCED_PARAMETER(Client);
switch (Message->User.QueryInformationProcess.ProcessInformationClass)
{
case KphProcessWSLProcessId:
case KphProcessStateInformation:
case KphProcessSequenceNumber:
case KphProcessStartKey:
{
return KPH_PROCESS_STATE_LOW;
}
case KphProcessBasicInformation:
case KphProcessImageSection:
{
return KPH_PROCESS_STATE_MEDIUM;
}
default:
{
return KPH_PROCESS_STATE_MAXIMUM;
}
}
}
_Function_class_(KPHM_HANDLER)
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KSIAPI KphpCommsQueryInformationProcess(
_In_ PKPH_CLIENT Client,
_Inout_ PKPH_MESSAGE Message
)
{
PKPHM_QUERY_INFORMATION_PROCESS msg;
KPH_PAGED_CODE_PASSIVE();
NT_ASSERT(ExGetPreviousMode() == UserMode);
NT_ASSERT(Message->Header.MessageId == KphMsgQueryInformationProcess);
UNREFERENCED_PARAMETER(Client);
msg = &Message->User.QueryInformationProcess;
msg->Status = KphQueryInformationProcess(msg->ProcessHandle,
msg->ProcessInformationClass,
msg->ProcessInformation,
msg->ProcessInformationLength,
msg->ReturnLength,
UserMode);
return STATUS_SUCCESS;
}
_Function_class_(KPHM_HANDLER)
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KSIAPI KphpCommsSetInformationProcess(
_In_ PKPH_CLIENT Client,
_Inout_ PKPH_MESSAGE Message
)
{
PKPHM_SET_INFORMATION_PROCESS msg;
KPH_PAGED_CODE_PASSIVE();
NT_ASSERT(ExGetPreviousMode() == UserMode);
NT_ASSERT(Message->Header.MessageId == KphMsgSetInformationProcess);
UNREFERENCED_PARAMETER(Client);
msg = &Message->User.SetInformationProcess;
msg->Status = KphSetInformationProcess(msg->ProcessHandle,
msg->ProcessInformationClass,
msg->ProcessInformation,
msg->ProcessInformationLength,
UserMode);
return STATUS_SUCCESS;
}
_Function_class_(KPHM_HANDLER)
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KSIAPI KphpCommsSetInformationThread(
_In_ PKPH_CLIENT Client,
_Inout_ PKPH_MESSAGE Message
)
{
PKPHM_SET_INFORMATION_THREAD msg;
KPH_PAGED_CODE_PASSIVE();
NT_ASSERT(ExGetPreviousMode() == UserMode);
NT_ASSERT(Message->Header.MessageId == KphMsgSetInformationThread);
UNREFERENCED_PARAMETER(Client);
msg = &Message->User.SetInformationThread;
msg->Status = KphSetInformationThread(msg->ThreadHandle,
msg->ThreadInformationClass,
msg->ThreadInformation,
msg->ThreadInformationLength,
UserMode);
return STATUS_SUCCESS;
}
_Function_class_(KPHM_HANDLER)
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KSIAPI KphpCommsSystemControl(
_In_ PKPH_CLIENT Client,
_Inout_ PKPH_MESSAGE Message
)
{
PKPHM_SYSTEM_CONTROL msg;
KPH_PAGED_CODE_PASSIVE();
NT_ASSERT(ExGetPreviousMode() == UserMode);
NT_ASSERT(Message->Header.MessageId == KphMsgSystemControl);
UNREFERENCED_PARAMETER(Client);
msg = &Message->User.SystemControl;
msg->Status = KphSystemControl(msg->SystemControlClass,
msg->SystemControlInfo,
msg->SystemControlInfoLength,
UserMode);
return STATUS_SUCCESS;
}
_Function_class_(KPHM_HANDLER)
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KSIAPI KphpCommsAlpcQueryInformation(
_In_ PKPH_CLIENT Client,
_Inout_ PKPH_MESSAGE Message
)
{
PKPHM_ALPC_QUERY_INFORMATION msg;
KPH_PAGED_CODE_PASSIVE();
NT_ASSERT(ExGetPreviousMode() == UserMode);
NT_ASSERT(Message->Header.MessageId == KphMsgAlpcQueryInformation);
UNREFERENCED_PARAMETER(Client);
msg = &Message->User.AlpcQueryInformation;
msg->Status = KphAlpcQueryInformation(msg->ProcessHandle,
msg->PortHandle,
msg->AlpcInformationClass,
msg->AlpcInformation,
msg->AlpcInformationLength,
msg->ReturnLength,
UserMode);
return STATUS_SUCCESS;
}
_Function_class_(KPHM_HANDLER)
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KSIAPI KphpCommsQueryInformationFile(
_In_ PKPH_CLIENT Client,
_Inout_ PKPH_MESSAGE Message
)
{
PKPHM_QUERY_INFORMATION_FILE msg;
KPH_PAGED_CODE_PASSIVE();
NT_ASSERT(ExGetPreviousMode() == UserMode);
NT_ASSERT(Message->Header.MessageId == KphMsgQueryInformationFile);
UNREFERENCED_PARAMETER(Client);
msg = &Message->User.QueryInformationFile;
msg->Status = KphQueryInformationFile(msg->ProcessHandle,
msg->FileHandle,
msg->FileInformationClass,
msg->FileInformation,
msg->FileInformationLength,
msg->IoStatusBlock,
UserMode);
return STATUS_SUCCESS;
}
_Function_class_(KPHM_HANDLER)
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KSIAPI KphpCommsQueryVolumeInformationFile(
_In_ PKPH_CLIENT Client,
_Inout_ PKPH_MESSAGE Message
)
{
PKPHM_QUERY_VOLUME_INFORMATION_FILE msg;
KPH_PAGED_CODE_PASSIVE();
NT_ASSERT(ExGetPreviousMode() == UserMode);
NT_ASSERT(Message->Header.MessageId == KphMsgQueryVolumeInformationFile);
UNREFERENCED_PARAMETER(Client);
msg = &Message->User.QueryVolumeInformationFile;
msg->Status = KphQueryVolumeInformationFile(msg->ProcessHandle,
msg->FileHandle,
msg->FsInformationClass,
msg->FsInformation,
msg->FsInformationLength,
msg->IoStatusBlock,
UserMode);
return STATUS_SUCCESS;
}
_Function_class_(KPHM_HANDLER)
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KSIAPI KphpCommsDuplicateObject(
_In_ PKPH_CLIENT Client,
_Inout_ PKPH_MESSAGE Message
)
{
PKPHM_DUPLICATE_OBJECT msg;
KPH_PAGED_CODE_PASSIVE();
NT_ASSERT(ExGetPreviousMode() == UserMode);
NT_ASSERT(Message->Header.MessageId == KphMsgDuplicateObject);
UNREFERENCED_PARAMETER(Client);
msg = &Message->User.DuplicateObject;
msg->Status = KphDuplicateObject(msg->ProcessHandle,
msg->SourceHandle,
msg->DesiredAccess,
msg->TargetHandle,
UserMode);
return STATUS_SUCCESS;
}
_Function_class_(KPHM_HANDLER)
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KSIAPI KphpCommsQueryPerformanceCounter(
_In_ PKPH_CLIENT Client,
_Inout_ PKPH_MESSAGE Message
)
{
PKPHM_QUERY_PERFORMANCE_COUNTER msg;
KPH_PAGED_CODE_PASSIVE();
NT_ASSERT(ExGetPreviousMode() == UserMode);
NT_ASSERT(Message->Header.MessageId == KphMsgQueryPerformanceCounter);
UNREFERENCED_PARAMETER(Client);
msg = &Message->User.QueryPerformanceCounter;
msg->PerformanceCounter = KeQueryPerformanceCounter(&msg->PerformanceFrequency);
return STATUS_SUCCESS;
}
_Function_class_(KPHM_REQUIRED_STATE)
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
KPH_PROCESS_STATE KSIAPI KphpCommsCreateFileRequires(
_In_ PKPH_CLIENT Client,
_In_ PCKPH_MESSAGE Message
)
{
ACCESS_MASK desiredAccess;
ULONG createDisposition;
KPH_PAGED_CODE_PASSIVE();
NT_ASSERT(ExGetPreviousMode() == UserMode);
NT_ASSERT(Message->Header.MessageId == KphMsgCreateFile);
UNREFERENCED_PARAMETER(Client);
desiredAccess = Message->User.CreateFile.DesiredAccess;
if ((desiredAccess & KPH_FILE_READ_ACCESS) != desiredAccess)
{
return KPH_PROCESS_STATE_MAXIMUM;
}
createDisposition = Message->User.CreateFile.CreateDisposition;
if (createDisposition != KPH_FILE_READ_DISPOSITION)
{
return KPH_PROCESS_STATE_MAXIMUM;
}
return KPH_PROCESS_STATE_MEDIUM;
}
_Function_class_(KPHM_HANDLER)
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KSIAPI KphpCommsCreateFile(
_In_ PKPH_CLIENT Client,
_Inout_ PKPH_MESSAGE Message
)
{
PKPHM_CREATE_FILE msg;
KPH_PAGED_CODE_PASSIVE();
NT_ASSERT(ExGetPreviousMode() == UserMode);
NT_ASSERT(Message->Header.MessageId == KphMsgCreateFile);
UNREFERENCED_PARAMETER(Client);
msg = &Message->User.CreateFile;
msg->Status = KphCreateFile(msg->FileHandle,
msg->DesiredAccess,
msg->ObjectAttributes,
msg->IoStatusBlock,
msg->AllocationSize,
msg->FileAttributes,
msg->ShareAccess,
msg->CreateDisposition,
msg->CreateOptions,
msg->EaBuffer,
msg->EaLength,
msg->Options,
UserMode);
return STATUS_SUCCESS;
}
_Function_class_(KPHM_REQUIRED_STATE)
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
KPH_PROCESS_STATE KSIAPI KphpCommsQueryInformationThreadRequires(
_In_ PKPH_CLIENT Client,
_In_ PCKPH_MESSAGE Message
)
{
KPH_PAGED_CODE_PASSIVE();
NT_ASSERT(ExGetPreviousMode() == UserMode);
NT_ASSERT(Message->Header.MessageId == KphMsgQueryInformationThread);
UNREFERENCED_PARAMETER(Client);
switch (Message->User.QueryInformationThread.ThreadInformationClass)
{
case KphThreadIoCounters:
case KphThreadWSLThreadId:
{
return KPH_PROCESS_STATE_LOW;
}
case KphThreadKernelStackInformation:
{
return KPH_PROCESS_STATE_MAXIMUM;
}
default:
{
return KPH_PROCESS_STATE_MAXIMUM;
}
}
}
_Function_class_(KPHM_HANDLER)
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KSIAPI KphpCommsQueryInformationThread(
_In_ PKPH_CLIENT Client,
_Inout_ PKPH_MESSAGE Message
)
{
PKPHM_QUERY_INFORMATION_THREAD msg;
KPH_PAGED_CODE_PASSIVE();
NT_ASSERT(ExGetPreviousMode() == UserMode);
NT_ASSERT(Message->Header.MessageId == KphMsgQueryInformationThread);
UNREFERENCED_PARAMETER(Client);
msg = &Message->User.QueryInformationThread;
msg->Status = KphQueryInformationThread(msg->ThreadHandle,
msg->ThreadInformationClass,
msg->ThreadInformation,
msg->ThreadInformationLength,
msg->ReturnLength,
UserMode);
return STATUS_SUCCESS;
}
_Function_class_(KPHM_HANDLER)
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KSIAPI KphpCommsQuerySection(
_In_ PKPH_CLIENT Client,
_Inout_ PKPH_MESSAGE Message
)
{
PKPHM_QUERY_SECTION msg;
KPH_PAGED_CODE_PASSIVE();
NT_ASSERT(ExGetPreviousMode() == UserMode);
NT_ASSERT(Message->Header.MessageId == KphMsgQuerySection);
UNREFERENCED_PARAMETER(Client);
msg = &Message->User.QuerySection;
msg->Status = KphQuerySection(msg->SectionHandle,
msg->SectionInformationClass,
msg->SectionInformation,
msg->SectionInformationLength,
msg->ReturnLength,
UserMode);
return STATUS_SUCCESS;
}
_Function_class_(KPHM_HANDLER)
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KSIAPI KphpCommsCompareObjects(
_In_ PKPH_CLIENT Client,
_Inout_ PKPH_MESSAGE Message
)
{
PKPHM_COMPARE_OBJECTS msg;
KPH_PAGED_CODE_PASSIVE();
NT_ASSERT(ExGetPreviousMode() == UserMode);
NT_ASSERT(Message->Header.MessageId == KphMsgCompareObjects);
UNREFERENCED_PARAMETER(Client);
msg = &Message->User.CompareObjects;
msg->Status = KphCompareObjects(msg->ProcessHandle,
msg->FirstObjectHandle,
msg->SecondObjectHandle,
UserMode);
return STATUS_SUCCESS;
}
_Function_class_(KPHM_HANDLER)
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KSIAPI KphpCommsGetInformerClientSettings(
_In_ PKPH_CLIENT Client,
_Inout_ PKPH_MESSAGE Message
)
{
PKPHM_GET_INFORMER_CLIENT_SETTINGS msg;
KPH_PAGED_CODE_PASSIVE();
NT_ASSERT(ExGetPreviousMode() == UserMode);
NT_ASSERT(Message->Header.MessageId == KphMsgGetInformerClientSettings);
msg = &Message->User.GetInformerClientSettings;
msg->Status = KphGetInformerClientSettings(Client, msg->Settings);
return STATUS_SUCCESS;
}
_Function_class_(KPHM_HANDLER)
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KSIAPI KphpCommsSetInformerClientSettings(
_In_ PKPH_CLIENT Client,
_Inout_ PKPH_MESSAGE Message
)
{
PKPHM_SET_INFORMER_CLIENT_SETTINGS msg;
KPH_PAGED_CODE_PASSIVE();
NT_ASSERT(ExGetPreviousMode() == UserMode);
NT_ASSERT(Message->Header.MessageId == KphMsgSetInformerClientSettings);
msg = &Message->User.SetInformerClientSettings;
msg->Status = KphSetInformerClientSettings(Client, msg->Settings);
return STATUS_SUCCESS;
}
_Function_class_(KPHM_HANDLER)
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KSIAPI KphpCommsAcquireDriverUnloadProtection(
_In_ PKPH_CLIENT Client,
_Inout_ PKPH_MESSAGE Message
)
{
PKPHM_ACQUIRE_DRIVER_UNLOAD_PROTECTION msg;
KPH_PAGED_CODE_PASSIVE();
NT_ASSERT(ExGetPreviousMode() == UserMode);
NT_ASSERT(Message->Header.MessageId == KphMsgAcquireDriverUnloadProtection);
msg = &Message->User.AcquireDriverUnloadProtection;
msg->Status = KphAcquireReference(&Client->DriverUnloadProtectionRef,
&msg->ClientPreviousCount);
if (NT_SUCCESS(msg->Status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
PROTECTION,
"Client %wZ (%lu) "
"acquired driver unload protection (%ld)",
&Client->Process->ImageName,
HandleToULong(Client->Process->ProcessId),
msg->ClientPreviousCount + 1);
if (msg->ClientPreviousCount == 0)
{
msg->Status = KphAcquireDriverUnloadProtection(&msg->PreviousCount);
}
else
{
msg->PreviousCount = KphGetDriverUnloadProtectionCount();
}
}
return STATUS_SUCCESS;
}
_Function_class_(KPHM_HANDLER)
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KSIAPI KphpCommsReleaseDriverUnloadProtection(
_In_ PKPH_CLIENT Client,
_Inout_ PKPH_MESSAGE Message
)
{
PKPHM_RELEASE_DRIVER_UNLOAD_PROTECTION msg;
KPH_PAGED_CODE_PASSIVE();
NT_ASSERT(ExGetPreviousMode() == UserMode);
NT_ASSERT(Message->Header.MessageId == KphMsgReleaseDriverUnloadProtection);
msg = &Message->User.ReleaseDriverUnloadProtection;
msg->Status = KphReleaseReference(&Client->DriverUnloadProtectionRef,
&msg->ClientPreviousCount);
if (NT_SUCCESS(msg->Status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
PROTECTION,
"Client %wZ (%lu) "
"released driver unload protection (%ld)",
&Client->Process->ImageName,
HandleToULong(Client->Process->ProcessId),
msg->ClientPreviousCount - 1);
if (msg->ClientPreviousCount == 1)
{
msg->Status = KphReleaseDriverUnloadProtection(&msg->PreviousCount);
}
else
{
msg->PreviousCount = KphGetDriverUnloadProtectionCount();
}
}
return STATUS_SUCCESS;
}
_Function_class_(KPHM_HANDLER)
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KSIAPI KphpCommsGetConnectedClientCount(
_In_ PKPH_CLIENT Client,
_Inout_ PKPH_MESSAGE Message
)
{
PKPHM_GET_CONNECTED_CLIENT_COUNT msg;
KPH_PAGED_CODE_PASSIVE();
NT_ASSERT(ExGetPreviousMode() == UserMode);
NT_ASSERT(Message->Header.MessageId == KphMsgGetConnectedClientCount);
UNREFERENCED_PARAMETER(Client);
msg = &Message->User.GetConnectedClientCount;
msg->Count = KphGetConnectedClientCount();
return STATUS_SUCCESS;
}
_Function_class_(KPHM_HANDLER)
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KSIAPI KphpCommsActivateDynData(
_In_ PKPH_CLIENT Client,
_Inout_ PKPH_MESSAGE Message
)
{
PKPHM_ACTIVATE_DYNDATA msg;
KPH_PAGED_CODE_PASSIVE();
NT_ASSERT(ExGetPreviousMode() == UserMode);
NT_ASSERT(Message->Header.MessageId == KphMsgActivateDynData);
UNREFERENCED_PARAMETER(Client);
msg = &Message->User.ActivateDynData;
msg->Status = KphActivateDynData(msg->DynData,
msg->DynDataLength,
msg->Signature,
msg->SignatureLength,
UserMode);
return STATUS_SUCCESS;
}
_Function_class_(KPHM_HANDLER)
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KSIAPI KphpCommsIsDynDataActive(
_In_ PKPH_CLIENT Client,
_Inout_ PKPH_MESSAGE Message
)
{
PKPHM_IS_DYNDATA_ACTIVE msg;
KPH_PAGED_CODE_PASSIVE();
NT_ASSERT(ExGetPreviousMode() == UserMode);
NT_ASSERT(Message->Header.MessageId == KphMsgIsDynDataActive);
UNREFERENCED_PARAMETER(Client);
msg = &Message->User.IsDynDataActive;
msg->Status = KphIsDynDataActive(msg->IsActive, UserMode);
return STATUS_SUCCESS;
}
_Function_class_(KPHM_HANDLER)
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KSIAPI KphpCommsRequestSessionAccessToken(
_In_ PKPH_CLIENT Client,
_Inout_ PKPH_MESSAGE Message
)
{
PKPHM_REQUEST_SESSION_ACCESS_TOKEN msg;
KPH_PAGED_CODE_PASSIVE();
NT_ASSERT(ExGetPreviousMode() == UserMode);
NT_ASSERT(Message->Header.MessageId == KphMsgRequestSessionAccessToken);
UNREFERENCED_PARAMETER(Client);
msg = &Message->User.RequestSessionAccessToken;
msg->Status = KphRequestSessionAccessToken(&msg->AccessToken,
&msg->Expiry,
msg->Privileges,
msg->Uses);
return STATUS_SUCCESS;
}
_Function_class_(KPHM_HANDLER)
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KSIAPI KphpCommsAssignProcessSessionToken(
_In_ PKPH_CLIENT Client,
_Inout_ PKPH_MESSAGE Message
)
{
PKPHM_ASSIGN_PROCESS_SESSION_TOKEN msg;
KPH_PAGED_CODE_PASSIVE();
NT_ASSERT(ExGetPreviousMode() == UserMode);
NT_ASSERT(Message->Header.MessageId == KphMsgAssignProcessSessionToken);
UNREFERENCED_PARAMETER(Client);
msg = &Message->User.AssignProcessSessionToken;
msg->Status = KphAssignProcessSessionToken(msg->ProcessHandle,
msg->Signature,
msg->SignatureLength,
UserMode);
return STATUS_SUCCESS;
}
_Function_class_(KPHM_HANDLER)
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KSIAPI KphpCommsAssignThreadSessionToken(
_In_ PKPH_CLIENT Client,
_Inout_ PKPH_MESSAGE Message
)
{
PKPHM_ASSIGN_THREAD_SESSION_TOKEN msg;
KPH_PAGED_CODE_PASSIVE();
NT_ASSERT(ExGetPreviousMode() == UserMode);
NT_ASSERT(Message->Header.MessageId == KphMsgAssignThreadSessionToken);
UNREFERENCED_PARAMETER(Client);
msg = &Message->User.AssignThreadSessionToken;
msg->Status = KphAssignThreadSessionToken(msg->ThreadHandle,
msg->Signature,
msg->SignatureLength,
UserMode);
return STATUS_SUCCESS;
}
_Function_class_(KPHM_HANDLER)
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KSIAPI KphpCommsGetInformerProcessSettings(
_In_ PKPH_CLIENT Client,
_Inout_ PKPH_MESSAGE Message
)
{
PKPHM_GET_INFORMER_PROCESS_SETTINGS msg;
KPH_PAGED_CODE_PASSIVE();
NT_ASSERT(ExGetPreviousMode() == UserMode);
NT_ASSERT(Message->Header.MessageId == KphMsgGetInformerProcessSettings);
UNREFERENCED_PARAMETER(Client);
msg = &Message->User.GetInformerProcessSettings;
msg->Status = KphGetInformerProcessSettings(msg->ProcessHandle,
msg->Settings,
UserMode);
return STATUS_SUCCESS;
}
_Function_class_(KPHM_HANDLER)
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KSIAPI KphpCommsSetInformerProcessSettings(
_In_ PKPH_CLIENT Client,
_Inout_ PKPH_MESSAGE Message
)
{
PKPHM_SET_INFORMER_PROCESS_SETTINGS msg;
KPH_PAGED_CODE_PASSIVE();
NT_ASSERT(ExGetPreviousMode() == UserMode);
NT_ASSERT(Message->Header.MessageId == KphMsgSetInformerProcessSettings);
UNREFERENCED_PARAMETER(Client);
msg = &Message->User.SetInformerProcessSettings;
msg->Status = KphSetInformerProcessSettings(msg->ProcessHandle,
msg->Settings,
UserMode);
return STATUS_SUCCESS;
}
_Function_class_(KPHM_HANDLER)
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KSIAPI KphpCommsStripProtectedProcessMasks(
_In_ PKPH_CLIENT Client,
_Inout_ PKPH_MESSAGE Message
)
{
PKPHM_STRIP_PROTECTED_PROCESS_MASKS msg;
KPH_PAGED_CODE_PASSIVE();
NT_ASSERT(ExGetPreviousMode() == UserMode);
NT_ASSERT(Message->Header.MessageId == KphMsgStripProtectedProcessMasks);
UNREFERENCED_PARAMETER(Client);
msg = &Message->User.StripProtectedProcessMasks;
msg->Status = KphStripProtectedProcessMasks(msg->ProcessHandle,
msg->ProcessAllowedMask,
msg->ThreadAllowedMask,
UserMode);
return STATUS_SUCCESS;
}
_Function_class_(KPHM_REQUIRED_STATE)
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
KPH_PROCESS_STATE KSIAPI KphpCommsQueryVirtualMemoryRequires(
_In_ PKPH_CLIENT Client,
_In_ PCKPH_MESSAGE Message
)
{
KPH_PAGED_CODE_PASSIVE();
NT_ASSERT(ExGetPreviousMode() == UserMode);
NT_ASSERT(Message->Header.MessageId == KphMsgQueryVirtualMemory);
UNREFERENCED_PARAMETER(Client);
switch (Message->User.QueryVirtualMemory.MemoryInformationClass)
{
case KphMemoryImageSection:
case KphMemoryDataSection:
{
return KPH_PROCESS_STATE_MEDIUM;
}
case KphMemoryMappedInformation:
default:
{
return KPH_PROCESS_STATE_MAXIMUM;
}
}
}
_Function_class_(KPHM_HANDLER)
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KSIAPI KphpCommsQueryVirtualMemory(
_In_ PKPH_CLIENT Client,
_Inout_ PKPH_MESSAGE Message
)
{
PKPHM_QUERY_VIRTUAL_MEMORY msg;
KPH_PAGED_CODE_PASSIVE();
NT_ASSERT(ExGetPreviousMode() == UserMode);
NT_ASSERT(Message->Header.MessageId == KphMsgQueryVirtualMemory);
UNREFERENCED_PARAMETER(Client);
msg = &Message->User.QueryVirtualMemory;
msg->Status = KphQueryVirtualMemory(msg->ProcessHandle,
msg->BaseAddress,
msg->MemoryInformationClass,
msg->MemoryInformation,
msg->MemoryInformationLength,
msg->ReturnLength,
UserMode);
return STATUS_SUCCESS;
}
_Function_class_(KPHM_HANDLER)
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KSIAPI KphpCommsQueryHashInformationFile(
_In_ PKPH_CLIENT Client,
_Inout_ PKPH_MESSAGE Message
)
{
PKPHM_QUERY_HASH_INFORMATION_FILE msg;
KPH_PAGED_CODE_PASSIVE();
NT_ASSERT(ExGetPreviousMode() == UserMode);
NT_ASSERT(Message->Header.MessageId == KphMsgQueryHashInformationFile);
UNREFERENCED_PARAMETER(Client);
msg = &Message->User.QueryHashInformationFile;
msg->Status = KphQueryHashInformationFile(msg->FileHandle,
msg->HashingInformation,
msg->HashingInformationLength,
UserMode);
return STATUS_SUCCESS;
}
_Function_class_(KPHM_HANDLER)
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KSIAPI KphpCommsOpenDevice(
_In_ PKPH_CLIENT Client,
_Inout_ PKPH_MESSAGE Message
)
{
PKPHM_OPEN_DEVICE msg;
KPH_PAGED_CODE_PASSIVE();
NT_ASSERT(ExGetPreviousMode() == UserMode);
NT_ASSERT(Message->Header.MessageId == KphMsgOpenDevice);
UNREFERENCED_PARAMETER(Client);
msg = &Message->User.OpenDevice;
msg->Status = KphOpenDevice(msg->DeviceHandle,
msg->DesiredAccess,
msg->ObjectAttributes,
UserMode);
return STATUS_SUCCESS;
}
_Function_class_(KPHM_HANDLER)
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KSIAPI KphpCommsOpenDeviceDriver(
_In_ PKPH_CLIENT Client,
_Inout_ PKPH_MESSAGE Message
)
{
PKPHM_OPEN_DEVICE_DRIVER msg;
KPH_PAGED_CODE_PASSIVE();
NT_ASSERT(ExGetPreviousMode() == UserMode);
NT_ASSERT(Message->Header.MessageId == KphMsgOpenDeviceDriver);
UNREFERENCED_PARAMETER(Client);
msg = &Message->User.OpenDeviceDriver;
msg->Status = KphOpenDeviceDriver(msg->DeviceHandle,
msg->DesiredAccess,
msg->DriverHandle,
UserMode);
return STATUS_SUCCESS;
}
_Function_class_(KPHM_HANDLER)
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KSIAPI KphpCommsOpenDeviceBaseDevice(
_In_ PKPH_CLIENT Client,
_Inout_ PKPH_MESSAGE Message
)
{
PKPHM_OPEN_DEVICE_BASE_DEVICE msg;
KPH_PAGED_CODE_PASSIVE();
NT_ASSERT(ExGetPreviousMode() == UserMode);
NT_ASSERT(Message->Header.MessageId == KphMsgOpenDeviceBaseDevice);
UNREFERENCED_PARAMETER(Client);
msg = &Message->User.OpenDeviceBaseDevice;
msg->Status = KphOpenDeviceBaseDevice(msg->DeviceHandle,
msg->DesiredAccess,
msg->BaseDeviceHandle,
UserMode);
return STATUS_SUCCESS;
}
_Function_class_(KPHM_HANDLER)
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KSIAPI KphpCommsGetInformerStats(
_In_ PKPH_CLIENT Client,
_Inout_ PKPH_MESSAGE Message
)
{
PKPHM_GET_INFORMER_STATS msg;
KPH_PAGED_CODE_PASSIVE();
NT_ASSERT(ExGetPreviousMode() == UserMode);
NT_ASSERT(Message->Header.MessageId == KphMsgGetInformerStats);
UNREFERENCED_PARAMETER(Client);
msg = &Message->User.GetInformerStats;
msg->Status = KphGetInformerStats(msg->ProcessHandle, msg->Stats, UserMode);
return STATUS_SUCCESS;
}
_Function_class_(KPHM_HANDLER)
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KSIAPI KphpCommsGetInformerClientStats(
_In_ PKPH_CLIENT Client,
_Inout_ PKPH_MESSAGE Message
)
{
PKPHM_GET_INFORMER_CLIENT_STATS msg;
KPH_PAGED_CODE_PASSIVE();
NT_ASSERT(ExGetPreviousMode() == UserMode);
NT_ASSERT(Message->Header.MessageId == KphMsgGetInformerClientStats);
msg = &Message->User.GetInformerClientStats;
msg->Status = KphGetInformerClientStats(Client, msg->Stats);
return STATUS_SUCCESS;
}
================================================
FILE: KSystemInformer/device.c
================================================
/*
* Copyright (c) 2022 Winsider Seminars & Solutions, Inc. All rights reserved.
*
* This file is part of System Informer.
*
* Authors:
*
* jxy-s 2024-2026
*
*/
#include
#include
KPH_PAGED_FILE();
/**
* \brief Open a device object.
*
* \param[out] DriverHandle Set to the opened handle to the device.
* \param[in] DesiredAccess Desired access to the device object.
* \param[in] ObjectAttributes Object attributes for opening the device object.
* \param[in] AccessMode The mode in which to perform access checks.
*/
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KphOpenDevice(
_Out_ PHANDLE DeviceHandle,
_In_ ACCESS_MASK DesiredAccess,
_In_ POBJECT_ATTRIBUTES ObjectAttributes,
_In_ KPROCESSOR_MODE AccessMode
)
{
NTSTATUS status;
HANDLE fileHandle;
PFILE_OBJECT fileObject;
PUNICODE_STRING capturedObjectName;
OBJECT_ATTRIBUTES objectAttributes;
POBJECT_ATTRIBUTES objectAttributesPtr;
ULONG options;
IO_STATUS_BLOCK ioStatusBlock;
HANDLE deviceHandle;
KPH_PAGED_CODE_PASSIVE();
fileHandle = NULL;
fileObject = NULL;
capturedObjectName = NULL;
if (AccessMode != KernelMode)
{
OBJECT_ATTRIBUTES capturedAttributes;
__try
{
ReadStructFromUser(&capturedAttributes, ObjectAttributes);
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
status = GetExceptionCode();
goto Exit;
}
if (capturedAttributes.ObjectName)
{
status = KphCaptureUnicodeString(capturedAttributes.ObjectName,
&capturedObjectName);
if (!NT_SUCCESS(status))
{
goto Exit;
}
}
SetFlag(capturedAttributes.Attributes, OBJ_KERNEL_HANDLE);
SetFlag(capturedAttributes.Attributes, OBJ_FORCE_ACCESS_CHECK);
InitializeObjectAttributes(&objectAttributes,
capturedObjectName,
capturedAttributes.Attributes,
capturedAttributes.RootDirectory,
NULL);
objectAttributesPtr = &objectAttributes;
options = IO_FORCE_ACCESS_CHECK;
}
else
{
objectAttributesPtr = ObjectAttributes;
options = 0;
}
status = KphCreateFile(&fileHandle,
DesiredAccess,
objectAttributesPtr,
&ioStatusBlock,
NULL,
0,
FILE_SHARE_READ | FILE_SHARE_WRITE,
FILE_OPEN,
FILE_NON_DIRECTORY_FILE,
NULL,
0,
options,
KernelMode);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
GENERAL,
"KphCreateFile failed: %!STATUS!",
status);
fileHandle = NULL;
goto Exit;
}
status = ObReferenceObjectByHandle(fileHandle,
0,
*IoFileObjectType,
KernelMode,
&fileObject,
NULL);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
GENERAL,
"ObReferenceObjectByHandle failed: %!STATUS!",
status);
fileObject = NULL;
goto Exit;
}
status = ObOpenObjectByPointer(IoGetRelatedDeviceObject(fileObject),
(AccessMode ? 0 : OBJ_KERNEL_HANDLE),
NULL,
DesiredAccess,
*IoDeviceObjectType,
AccessMode,
&deviceHandle);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
GENERAL,
"ObOpenObjectByPointer failed: %!STATUS!",
status);
deviceHandle = NULL;
goto Exit;
}
status = KphWriteHandleToMode(DeviceHandle, deviceHandle, AccessMode);
Exit:
if (fileObject)
{
ObDereferenceObject(fileObject);
}
if (fileHandle)
{
ObCloseHandle(fileHandle, KernelMode);
}
if (capturedObjectName)
{
KphReleaseUnicodeString(capturedObjectName);
}
return status;
}
/**
* \brief Opens a driver object associated with a device object.
*
* \param[in] DeviceHandle Handle to a device object.
* \param[in] DesiredAccess Desired access to the driver object.
* \param[out] DriverHandle Set to the opened handle to the driver object.
* \param[in] AccessMode The mode in which to perform access checks.
*/
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KphOpenDeviceDriver(
_In_ HANDLE DeviceHandle,
_In_ ACCESS_MASK DesiredAccess,
_Out_ PHANDLE DriverHandle,
_In_ KPROCESSOR_MODE AccessMode
)
{
NTSTATUS status;
PDEVICE_OBJECT deviceObject;
HANDLE driverHandle;
KPH_PAGED_CODE_PASSIVE();
status = ObReferenceObjectByHandle(DeviceHandle,
DesiredAccess,
*IoDeviceObjectType,
AccessMode,
&deviceObject,
NULL);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
GENERAL,
"ObReferenceObjectByHandle failed: %!STATUS!",
status);
deviceObject = NULL;
goto Exit;
}
status = ObOpenObjectByPointer(deviceObject->DriverObject,
(AccessMode ? 0 : OBJ_KERNEL_HANDLE),
NULL,
DesiredAccess,
*IoDriverObjectType,
AccessMode,
&driverHandle);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
GENERAL,
"ObOpenObjectByPointer failed: %!STATUS!",
status);
driverHandle = NULL;
goto Exit;
}
status = KphWriteHandleToMode(DriverHandle, driverHandle, AccessMode);
Exit:
if (deviceObject)
{
ObDereferenceObject(deviceObject);
}
return status;
}
/**
* \brief Opens the base device object associated with a device object.
*
* \details The base device object is the lowest-level device object in the file
* system or device driver stack.
*
* \param[in] DeviceHandle Handle to a device object.
* \param[in] DesiredAccess Desired access to the base device object.
* \param[out] BaseDeviceHandle Set to the opened handle to the device object.
* \param[in] AccessMode The mode in which to perform access checks.
*/
_IRQL_requires_max_(PASSIVE_LEVEL)
NTSTATUS KphOpenDeviceBaseDevice(
_In_ HANDLE DeviceHandle,
_In_ ACCESS_MASK DesiredAccess,
_Out_ PHANDLE BaseDeviceHandle,
_In_ KPROCESSOR_MODE AccessMode
)
{
NTSTATUS status;
PDEVICE_OBJECT deviceObject;
PDEVICE_OBJECT baseDeviceObject;
HANDLE baseDeviceHandle;
KPH_PAGED_CODE_PASSIVE();
baseDeviceObject = NULL;
status = ObReferenceObjectByHandle(DeviceHandle,
DesiredAccess,
*IoDeviceObjectType,
AccessMode,
&deviceObject,
NULL);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
GENERAL,
"ObReferenceObjectByHandle failed: %!STATUS!",
status);
deviceObject = NULL;
goto Exit;
}
baseDeviceObject = IoGetDeviceAttachmentBaseRef(deviceObject);
status = ObOpenObjectByPointer(baseDeviceObject,
(AccessMode ? 0 : OBJ_KERNEL_HANDLE),
NULL,
DesiredAccess,
*IoDeviceObjectType,
AccessMode,
&baseDeviceHandle);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
GENERAL,
"ObReferenceObjectByHandle failed: %!STATUS!",
status);
baseDeviceHandle = NULL;
goto Exit;
}
status = KphWriteHandleToMode(BaseDeviceHandle,
baseDeviceHandle,
AccessMode);
Exit:
if (baseDeviceObject)
{
ObDereferenceObject(baseDeviceObject);
}
if (deviceObject)
{
ObDereferenceObject(deviceObject);
}
return status;
}
================================================
FILE: KSystemInformer/driver.c
================================================
/*
* Copyright (c) 2022 Winsider Seminars & Solutions, Inc. All rights reserved.
*
* This file is part of System Informer.
*
* Authors:
*
* wj32 2010-2016
* jxy-s 2022-2026
*
*/
#include
#include
KPH_PAGED_FILE();
/**
* \brief Opens a driver object.
*
* \param[out] DriverHandle Set to the opened handle to the driver.
* \param[in] DesiredAccess Desired access to the driver object.
* \param[in] ObjectAttributes Object attributes for opening the driver object.
* \param[in] AccessMode The mode in which to perform access checks.
*
* \return Successful or errant status.
*/
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KphOpenDriver(
_Out_ PHANDLE DriverHandle,
_In_ ACCESS_MASK DesiredAccess,
_In_ POBJECT_ATTRIBUTES ObjectAttributes,
_In_ KPROCESSOR_MODE AccessMode
)
{
KPH_PAGED_CODE_PASSIVE();
return KphOpenNamedObject(DriverHandle,
DesiredAccess,
ObjectAttributes,
*IoDriverObjectType,
AccessMode);
}
/**
* \brief Queries information about a driver.
*
* \param[in] DriverHandle Handle to driver to query.
* \param[in] DriverInformationClass Information class to query.
* \param[out] DriverInformation Populated with driver information by class.
* \param[in] DriverInformationLength Length of the driver information buffer.
* \param[out] ReturnLength Receives the number of bytes written or required.
* \param[in] AccessMode The mode in which to perform access checks.
*
* \return Successful or errant status.
*/
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KphQueryInformationDriver(
_In_ HANDLE DriverHandle,
_In_ KPH_DRIVER_INFORMATION_CLASS DriverInformationClass,
_Out_writes_bytes_opt_(DriverInformationLength) PVOID DriverInformation,
_In_ ULONG DriverInformationLength,
_Out_opt_ PULONG ReturnLength,
_In_ KPROCESSOR_MODE AccessMode
)
{
NTSTATUS status;
PDRIVER_OBJECT driverObject;
ULONG returnLength;
KPH_PAGED_CODE_PASSIVE();
returnLength = 0;
status = ObReferenceObjectByHandle(DriverHandle,
0,
*IoDriverObjectType,
KernelMode,
&driverObject,
NULL);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
GENERAL,
"ObReferenceObjectByHandle failed: %!STATUS!",
status);
driverObject = NULL;
goto Exit;
}
//
// We reach into the driver object on purpose
//
#pragma prefast(push)
#pragma prefast(disable : 28175)
switch (DriverInformationClass)
{
case KphDriverBasicInformation:
{
//
// Basic information such as flags, driver base and driver size.
//
KPH_DRIVER_BASIC_INFORMATION basicInfo;
if (!DriverInformation ||
(DriverInformationLength < sizeof(KPH_DRIVER_BASIC_INFORMATION)))
{
status = STATUS_INFO_LENGTH_MISMATCH;
returnLength = sizeof(KPH_DRIVER_BASIC_INFORMATION);
goto Exit;
}
RtlZeroMemory(&basicInfo, sizeof(KPH_DRIVER_BASIC_INFORMATION));
basicInfo.Flags = driverObject->Flags;
basicInfo.DriverStart = driverObject->DriverStart;
basicInfo.DriverSize = driverObject->DriverSize;
status = KphCopyToMode(DriverInformation,
&basicInfo,
sizeof(KPH_DRIVER_BASIC_INFORMATION),
AccessMode);
if (NT_SUCCESS(status))
{
returnLength = sizeof(KPH_DRIVER_BASIC_INFORMATION);
}
break;
}
case KphDriverNameInformation:
{
//
// The name of the driver - e.g. \Driver\Null.
//
status = KphCopyUnicodeStringToMode(DriverInformation,
DriverInformationLength,
&driverObject->DriverName,
&returnLength,
AccessMode);
break;
}
case KphDriverServiceKeyNameInformation:
{
PCUNICODE_STRING serviceKeyName;
//
// The name of the driver's service key - e.g. \REGISTRY\...
//
if (driverObject->DriverExtension)
{
serviceKeyName = &driverObject->DriverExtension->ServiceKeyName;
}
else
{
serviceKeyName = NULL;
}
status = KphCopyUnicodeStringToMode(DriverInformation,
DriverInformationLength,
serviceKeyName,
&returnLength,
AccessMode);
break;
}
case KphDriverImageFileNameInformation:
{
UNICODE_STRING fullDriverPath;
if ((KphOsVersionInfo.dwMajorVersion < 10) ||
(KphOsVersionInfo.dwMajorVersion == 10) &&
(KphOsVersionInfo.dwBuildNumber < 16299))
{
//
// Per documentation it is not safe to call IoQueryFullDriverPath
// on anything but our own driver object.
//
status = STATUS_NOINTERFACE;
goto Exit;
}
RtlZeroMemory(&fullDriverPath, sizeof(UNICODE_STRING));
status = IoQueryFullDriverPath(driverObject, &fullDriverPath);
if (!NT_SUCCESS(status))
{
goto Exit;
}
status = KphCopyUnicodeStringToMode(DriverInformation,
DriverInformationLength,
&fullDriverPath,
&returnLength,
AccessMode);
KphFreePool(fullDriverPath.Buffer);
break;
}
default:
{
status = STATUS_INVALID_INFO_CLASS;
break;
}
}
#pragma prefast(pop)
Exit:
if (driverObject)
{
ObDereferenceObject(driverObject);
}
if (ReturnLength)
{
KphWriteULongToMode(ReturnLength, returnLength, AccessMode);
}
return status;
}
================================================
FILE: KSystemInformer/dyndata.c
================================================
/*
* Copyright (c) 2022 Winsider Seminars & Solutions, Inc. All rights reserved.
*
* This file is part of System Informer.
*
* Authors:
*
* wj32 2010-2016
* jxy-s 2022-2026
*
*/
#include
#include
#include
typedef struct _KPH_DYN_INIT
{
PKPH_DYN_CONFIG Config;
PKPH_DYN_KERNEL_FIELDS Kernel;
PKPH_DYN_LXCORE_FIELDS Lxcore;
} KPH_DYN_INIT, *PKPH_DYN_INIT;
typedef union _KPH_DYN_ATOMIC
{
PKPH_DYN Dyn;
KPH_ATOMIC_OBJECT_REF Atomic;
} KPH_DYN_ATOMIC, *PKPH_DYN_ATOMIC;
typedef struct _KPH_DYN_MODULE
{
USHORT Class;
UNICODE_STRING Name;
BOOLEAN Valid;
USHORT Machine;
ULONG TimeDateStamp;
ULONG SizeOfImage;
} KPH_DYN_MODULE, *PKPH_DYN_MODULE;
typedef enum _KPH_DYN_MODULE_CLASS
{
KphDynNtoskrnl,
KphDynNtkrla57,
KphDynLxcore,
} KPH_DYN_MODULE_CLASS, *PKPH_DYN_MODULE_CLASS;
KPH_PROTECTED_DATA_SECTION_RO_PUSH();
static const UNICODE_STRING KphpDynDataTypeName = RTL_CONSTANT_STRING(L"KphDynData");
KPH_PROTECTED_DATA_SECTION_RO_POP();
KPH_PROTECTED_DATA_SECTION_PUSH();
static PKPH_OBJECT_TYPE KphpDynDataType = NULL;
static KPH_DYN_MODULE KphpDynModules[] =
{
{ KPH_DYN_CLASS_NTOSKRNL, RTL_CONSTANT_STRING(L"ntoskrnl.exe"), FALSE, 0, 0, 0 },
{ KPH_DYN_CLASS_NTKRLA57, RTL_CONSTANT_STRING(L"ntkrla57.exe"), FALSE, 0, 0, 0 },
{ KPH_DYN_CLASS_LXCORE, RTL_CONSTANT_STRING(L"lxcore.sys"), FALSE, 0, 0, 0 },
};
KPH_PROTECTED_DATA_SECTION_POP();
static KPH_DYN_ATOMIC KphpDynData = { .Atomic = KPH_ATOMIC_OBJECT_REF_INIT };
/**
* \brief Reference the dynamic configuration.
*
* \return Pointer to the dynamic configuration, NULL if not activated. The
* caller must eventually dereference the object.
*/
_Must_inspect_result_
PKPH_DYN KphReferenceDynData(
VOID
)
{
return KphAtomicReferenceObject(&KphpDynData.Atomic);
}
KPH_PAGED_FILE();
/**
* \brief Allocates a dynamic configuration object.
*
* \param[in] Size The size of the object to allocate.
*
* \return Pointer to the allocated object, NULL on failure.
*/
_Function_class_(KPH_TYPE_ALLOCATE_PROCEDURE)
_IRQL_requires_max_(PASSIVE_LEVEL)
_Return_allocatesMem_size_(Size)
PVOID KSIAPI KphpAllocateDynData(
_In_ SIZE_T Size
)
{
KPH_PAGED_CODE_PASSIVE();
return KphAllocateNPaged(Size, KPH_TAG_DYNDATA);
}
/**
* \brief Initializes a dynamic configuration object.
*
* \param[in] Object Dynamic configuration object to initialize.
* \param[in] Parameter Initialization parameters for dynamic configuration.
*
* \return STATUS_SUCCESS
*/
_Function_class_(KPH_TYPE_INITIALIZE_PROCEDURE)
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KSIAPI KphpInitializeDynData(
_Inout_ PVOID Object,
_In_opt_ PVOID Parameter
)
{
NTSTATUS status;
PKPH_DYN dyn;
PKPH_DYN_INIT init;
KPH_PAGED_CODE_PASSIVE();
NT_ASSERT(Parameter);
dyn = Object;
init = Parameter;
#define KPH_LOAD_DYNITEM_KERNEL(x) dyn->x = C_2sTo4(init->Kernel->x)
#define KPH_LOAD_DYNITEM_LXCORE(x) dyn->x = init->Lxcore ? C_2sTo4(init->Lxcore->x) : ULONG_MAX;
KPH_LOAD_DYNITEM_KERNEL(EgeGuid);
KPH_LOAD_DYNITEM_KERNEL(EpObjectTable);
KPH_LOAD_DYNITEM_KERNEL(EreGuidEntry);
KPH_LOAD_DYNITEM_KERNEL(HtHandleContentionEvent);
KPH_LOAD_DYNITEM_KERNEL(OtName);
KPH_LOAD_DYNITEM_KERNEL(OtIndex);
KPH_LOAD_DYNITEM_KERNEL(ObDecodeShift);
KPH_LOAD_DYNITEM_KERNEL(ObAttributesShift);
KPH_LOAD_DYNITEM_KERNEL(AlpcCommunicationInfo);
KPH_LOAD_DYNITEM_KERNEL(AlpcOwnerProcess);
KPH_LOAD_DYNITEM_KERNEL(AlpcConnectionPort);
KPH_LOAD_DYNITEM_KERNEL(AlpcServerCommunicationPort);
KPH_LOAD_DYNITEM_KERNEL(AlpcClientCommunicationPort);
KPH_LOAD_DYNITEM_KERNEL(AlpcHandleTable);
KPH_LOAD_DYNITEM_KERNEL(AlpcHandleTableLock);
KPH_LOAD_DYNITEM_KERNEL(AlpcAttributes);
KPH_LOAD_DYNITEM_KERNEL(AlpcAttributesFlags);
KPH_LOAD_DYNITEM_KERNEL(AlpcPortContext);
KPH_LOAD_DYNITEM_KERNEL(AlpcPortObjectLock);
KPH_LOAD_DYNITEM_KERNEL(AlpcSequenceNo);
KPH_LOAD_DYNITEM_KERNEL(AlpcState);
KPH_LOAD_DYNITEM_KERNEL(KtInitialStack);
KPH_LOAD_DYNITEM_KERNEL(KtStackLimit);
KPH_LOAD_DYNITEM_KERNEL(KtStackBase);
KPH_LOAD_DYNITEM_KERNEL(KtKernelStack);
KPH_LOAD_DYNITEM_KERNEL(KtReadOperationCount);
KPH_LOAD_DYNITEM_KERNEL(KtWriteOperationCount);
KPH_LOAD_DYNITEM_KERNEL(KtOtherOperationCount);
KPH_LOAD_DYNITEM_KERNEL(KtReadTransferCount);
KPH_LOAD_DYNITEM_KERNEL(KtWriteTransferCount);
KPH_LOAD_DYNITEM_KERNEL(KtOtherTransferCount);
KPH_LOAD_DYNITEM_KERNEL(MmSectionControlArea);
KPH_LOAD_DYNITEM_KERNEL(MmControlAreaListHead);
KPH_LOAD_DYNITEM_KERNEL(MmControlAreaLock);
KPH_LOAD_DYNITEM_KERNEL(EpSectionObject);
KPH_LOAD_DYNITEM_LXCORE(LxPicoProc);
KPH_LOAD_DYNITEM_LXCORE(LxPicoProcInfo);
KPH_LOAD_DYNITEM_LXCORE(LxPicoProcInfoPID);
KPH_LOAD_DYNITEM_LXCORE(LxPicoThrdInfo);
KPH_LOAD_DYNITEM_LXCORE(LxPicoThrdInfoTID);
status = KphVerifyCreateKey(&dyn->SessionTokenPublicKeyHandle,
init->Config->SessionTokenPublicKey,
KPH_DYN_SESSION_TOKEN_PUBLIC_KEY_LENGTH);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
GENERAL,
"KphVerifyCreateKey failed: %!STATUS!",
status);
dyn->SessionTokenPublicKeyHandle = NULL;
}
return status;
}
/**
* \brief Deletes a dynamic configuration object.
*
* \param[in] Object Dynamic configuration object to delete.
*/
_Function_class_(KPH_TYPE_DELETE_PROCEDURE)
_IRQL_requires_max_(PASSIVE_LEVEL)
VOID KSIAPI KphpDeleteDynData(
_In_freesMem_ PVOID Object
)
{
PKPH_DYN dyn;
KPH_PAGED_CODE_PASSIVE();
dyn = Object;
if (dyn->SessionTokenPublicKeyHandle)
{
KphVerifyCloseKey(dyn->SessionTokenPublicKeyHandle);
}
}
/**
* \brief Frees a dynamic configuration object.
*
* \param[in] Object Dynamic configuration object to free.
*/
_Function_class_(KPH_TYPE_FREE_PROCEDURE)
_IRQL_requires_max_(PASSIVE_LEVEL)
VOID KSIAPI KphpFreeDynData(
_In_freesMem_ PVOID Object
)
{
KPH_PAGED_CODE_PASSIVE();
KphFree(Object, KPH_TAG_DYNDATA);
}
/**
* \brief Activates dynamic data.
*
* \param[in] DynConfig The dynamic configuration to activate.
* \param[in] DynConfigLength The length of the dynamic configuration.
* \param[in] Signature The signature of the dynamic data.
* \param[in] SignatureLength The length of the signature.
*
* \return Successful or errant status.
*/
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KphpActivateDynData(
_In_ PBYTE DynConfig,
_In_ ULONG DynConfigLength,
_In_opt_ PBYTE Signature,
_In_ ULONG SignatureLength
)
{
NTSTATUS status;
PKPH_DYN dyn;
KPH_DYN_INIT init;
PKPH_DYN_KERNEL_FIELDS kernel;
PKPH_DYN_LXCORE_FIELDS lxcore;
KPH_PAGED_CODE_PASSIVE();
dyn = NULL;
kernel = NULL;
lxcore = NULL;
if (Signature)
{
status = KphVerifyBuffer(DynConfig,
DynConfigLength,
Signature,
SignatureLength);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
GENERAL,
"KphVerifyBuffer failed: %!STATUS!",
status);
status = STATUS_SI_DYNDATA_INVALID_SIGNATURE;
goto Exit;
}
}
if (KphpDynModules[KphDynNtoskrnl].Valid)
{
status = KphDynDataLookup((PKPH_DYN_CONFIG)DynConfig,
DynConfigLength,
KphpDynModules[KphDynNtoskrnl].Class,
KphpDynModules[KphDynNtoskrnl].Machine,
KphpDynModules[KphDynNtoskrnl].TimeDateStamp,
KphpDynModules[KphDynNtoskrnl].SizeOfImage,
NULL,
&kernel);
}
else if (KphpDynModules[KphDynNtkrla57].Valid)
{
status = KphDynDataLookup((PKPH_DYN_CONFIG)DynConfig,
DynConfigLength,
KphpDynModules[KphDynNtkrla57].Class,
KphpDynModules[KphDynNtkrla57].Machine,
KphpDynModules[KphDynNtkrla57].TimeDateStamp,
KphpDynModules[KphDynNtkrla57].SizeOfImage,
NULL,
&kernel);
}
else
{
status = STATUS_NOT_FOUND;
}
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
GENERAL,
"KphDynDataLookup failed: %!STATUS!",
status);
//
// Activating dynamic data requires at least kernel data.
//
goto Exit;
}
if (KphpDynModules[KphDynLxcore].Valid)
{
status = KphDynDataLookup((PKPH_DYN_CONFIG)DynConfig,
DynConfigLength,
KphpDynModules[KphDynLxcore].Class,
KphpDynModules[KphDynLxcore].Machine,
KphpDynModules[KphDynLxcore].TimeDateStamp,
KphpDynModules[KphDynLxcore].SizeOfImage,
NULL,
&lxcore);
}
else
{
status = STATUS_NOT_FOUND;
}
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
GENERAL,
"KphDynDataLookup failed: %!STATUS!",
status);
//
// Activating dynamic data does not require lxcore data.
//
lxcore = NULL;
}
init.Config = (PKPH_DYN_CONFIG)DynConfig;
init.Kernel = kernel;
init.Lxcore = lxcore;
status = KphCreateObject(KphpDynDataType, sizeof(KPH_DYN), &dyn, &init);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
GENERAL,
"KphCreateObject failed: %!STATUS!",
status);
goto Exit;
}
KphAtomicAssignObjectReference(&KphpDynData.Atomic, dyn);
KphTracePrint(TRACE_LEVEL_INFORMATION,
GENERAL,
"Activated Dynamic Configuration "
"for Windows %lu.%lu.%lu Kernel %hu.%hu.%hu.%hu",
KphOsVersionInfo.dwMajorVersion,
KphOsVersionInfo.dwMinorVersion,
KphOsVersionInfo.dwBuildNumber,
KphKernelVersion.MajorVersion,
KphKernelVersion.MinorVersion,
KphKernelVersion.BuildNumber,
KphKernelVersion.Revision);
Exit:
if (dyn)
{
KphDereferenceObject(dyn);
}
return status;
}
/**
* \brief Activates dynamic data.
*
* \param[in] DynData The dynamic data to activate.
* \param[in] DynDataLength The length of the dynamic data.
* \param[in] Signature The signature of the dynamic data.
* \param[in] SignatureLength The length of the signature.
* \param[in] AccessMode The access mode of the caller.
*
* \return Successful or errant status.
*/
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KphActivateDynData(
_In_ PBYTE DynData,
_In_ ULONG DynDataLength,
_In_ PBYTE Signature,
_In_ ULONG SignatureLength,
_In_ KPROCESSOR_MODE AccessMode
)
{
NTSTATUS status;
PBYTE dynData;
PBYTE signature;
KPH_PAGED_CODE_PASSIVE();
dynData = NULL;
signature = NULL;
if (!DynData || !DynDataLength)
{
status = STATUS_INVALID_PARAMETER;
goto Exit;
}
if (!Signature || !SignatureLength)
{
status = STATUS_SI_DYNDATA_INVALID_SIGNATURE;
goto Exit;
}
if (AccessMode != KernelMode)
{
dynData = KphAllocatePaged(DynDataLength, KPH_TAG_DYNDATA);
if (!dynData)
{
status = STATUS_INSUFFICIENT_RESOURCES;
goto Exit;
}
signature = KphAllocatePaged(SignatureLength, KPH_TAG_DYNDATA);
if (!signature)
{
status = STATUS_INSUFFICIENT_RESOURCES;
goto Exit;
}
__try
{
CopyFromUser(dynData, DynData, DynDataLength);
CopyFromUser(signature, Signature, SignatureLength);
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
status = GetExceptionCode();
goto Exit;
}
}
else
{
dynData = DynData;
signature = Signature;
}
status = KphpActivateDynData(dynData,
DynDataLength,
signature,
SignatureLength);
Exit:
if (dynData && (dynData != DynData))
{
KphFree(dynData, KPH_TAG_DYNDATA);
}
if (signature && (signature != Signature))
{
KphFree(signature, KPH_TAG_DYNDATA);
}
return status;
}
/**
* \brief Checks if dynamic data is active.
*
* \param[out] IsActive Receives TRUE if active, FALSE otherwise.
* \param[in] AccessMode The access mode of the caller.
*
* \return Successful or errant status.
*/
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KphIsDynDataActive(
_Out_ PBOOLEAN IsActive,
_In_ KPROCESSOR_MODE AccessMode
)
{
PKPH_DYN dyn;
BOOLEAN isActive;
KPH_PAGED_CODE_PASSIVE();
dyn = KphReferenceDynData();
if (dyn)
{
isActive = TRUE;
KphDereferenceObject(dyn);
}
else
{
isActive = FALSE;
}
return KphWriteUCharToMode(IsActive, isActive, AccessMode);
}
/**
* \brief Initializes the dynamic modules.
*/
_IRQL_requires_max_(PASSIVE_LEVEL)
VOID KphpInitializeDynModules(
VOID
)
{
KPH_PAGED_CODE_PASSIVE();
KeEnterCriticalRegion();
if (!ExAcquireResourceSharedLite(PsLoadedModuleResource, TRUE))
{
KeLeaveCriticalRegion();
KphTracePrint(TRACE_LEVEL_VERBOSE,
GENERAL,
"Failed to acquire PsLoadedModuleResource");
return;
}
for (ULONG i = 0; i < ARRAYSIZE(KphpDynModules); i++)
{
PKPH_DYN_MODULE dynModule;
dynModule = &KphpDynModules[i];
for (PLIST_ENTRY link = PsLoadedModuleList->Flink;
link != PsLoadedModuleList;
link = link->Flink)
{
NTSTATUS status;
PKLDR_DATA_TABLE_ENTRY entry;
KPH_IMAGE_NT_HEADERS image;
entry = CONTAINING_RECORD(link, KLDR_DATA_TABLE_ENTRY, InLoadOrderLinks);
if (!RtlEqualUnicodeString(&entry->BaseDllName, &dynModule->Name, TRUE))
{
continue;
}
__try
{
status = KphImageNtHeader(entry->DllBase, entry->SizeOfImage, &image);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
GENERAL,
"KphImageNtHeader failed: %!STATUS!",
status);
break;
}
dynModule->Machine = image.Headers->FileHeader.Machine;
dynModule->TimeDateStamp = image.Headers->FileHeader.TimeDateStamp;
if (RTL_CONTAINS_FIELD(&image.Headers->OptionalHeader,
image.Headers->FileHeader.SizeOfOptionalHeader,
SizeOfImage))
{
dynModule->SizeOfImage = image.Headers->OptionalHeader.SizeOfImage;
dynModule->Valid = TRUE;
}
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
GENERAL,
"Failed to read module headers: %!STATUS!",
GetExceptionCode());
}
break;
}
}
ExReleaseResourceLite(PsLoadedModuleResource);
KeLeaveCriticalRegion();
}
/**
* \brief Initializes the dynamic data infrastructure.
*/
_IRQL_requires_max_(PASSIVE_LEVEL)
VOID KphInitializeDynData(
VOID
)
{
NTSTATUS status;
KPH_OBJECT_TYPE_INFO typeInfo;
KPH_PAGED_CODE_PASSIVE();
KphpInitializeDynModules();
typeInfo.Allocate = KphpAllocateDynData;
typeInfo.Initialize = KphpInitializeDynData;
typeInfo.Delete = KphpDeleteDynData;
typeInfo.Free = KphpFreeDynData;
typeInfo.Flags = 0;
typeInfo.DeferDelete = TRUE;
KphCreateObjectType(&KphpDynDataTypeName, &typeInfo, &KphpDynDataType);
if (KphParameterFlags.DynDataNoEmbedded)
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
GENERAL,
"Embedded Dynamic Configuration disabled");
}
else
{
status = KphpActivateDynData((PBYTE)KphDynConfig,
KphDynConfigLength,
NULL,
0);
NT_ANALYSIS_ASSUME(NT_SUCCESS(status));
KphTracePrint(TRACE_LEVEL_VERBOSE,
GENERAL,
"Embedded Dynamic Configuration: %!STATUS!",
status);
}
}
/**
* \brief Cleans up the dynamic data infrastructure.
*/
_IRQL_requires_max_(PASSIVE_LEVEL)
VOID KphCleanupDynData(
VOID
)
{
KPH_PAGED_CODE_PASSIVE();
KphAtomicAssignObjectReference(&KphpDynData.Atomic, NULL);
}
================================================
FILE: KSystemInformer/dynimp.c
================================================
/*
* Copyright (c) 2022 Winsider Seminars & Solutions, Inc. All rights reserved.
*
* This file is part of System Informer.
*
* Authors:
*
* wj32 2010-2016
* jxy-s 2020-2026
*
*/
#include
#include
KPH_PROTECTED_DATA_SECTION_PUSH();
PPS_SET_LOAD_IMAGE_NOTIFY_ROUTINE_EX KphDynPsSetLoadImageNotifyRoutineEx = NULL;
PPS_SET_CREATE_PROCESS_NOTIFY_ROUTINE_EX2 KphDynPsSetCreateProcessNotifyRoutineEx2 = NULL;
PMM_PROTECT_DRIVER_SECTION KphDynMmProtectDriverSection = NULL;
PPS_GET_PROCESS_SEQUENCE_NUMBER KphDynPsGetProcessSequenceNumber = NULL;
PPS_GET_PROCESS_START_KEY KphDynPsGetProcessStartKey = NULL;
PSE_REGISTER_IMAGE_VERIFICATION_CALLBACK KphSeRegisterImageVerificationCallback = NULL;
PSE_UNREGISTER_IMAGE_VERIFICATION_CALLBACK KphSeUnregisterImageVerificationCallback = NULL;
PCI_VALIDATE_FILE_OBJECT KphDynCiValidateFileObject = NULL;
PCI_FREE_POLICY_INFO KphDynCiFreePolicyInfo = NULL;
PLXP_THREAD_GET_CURRENT KphDynLxpThreadGetCurrent = NULL;
PIO_CHECK_FILE_OBJECT_OPENED_AS_COPY_SOURCE KphDynIoCheckFileObjectOpenedAsCopySource = NULL;
PIO_CHECK_FILE_OBJECT_OPENED_AS_COPY_DESTINATION KphDynIoCheckFileObjectOpenedAsCopyDestination = NULL;
PFLT_GET_COPY_INFORMATION_FROM_CALLBACK_DATA KphDynFltGetCopyInformationFromCallbackData = NULL;
KPH_PROTECTED_DATA_SECTION_POP();
KPH_PAGED_FILE();
/**
* \brief Dynamically imports routines.
*/
_IRQL_requires_max_(PASSIVE_LEVEL)
VOID KphDynamicImport(
VOID
)
{
KPH_PAGED_CODE_PASSIVE();
KphDynPsSetLoadImageNotifyRoutineEx = (PPS_SET_LOAD_IMAGE_NOTIFY_ROUTINE_EX)KphGetSystemRoutineAddress(L"PsSetLoadImageNotifyRoutineEx");
KphDynPsSetCreateProcessNotifyRoutineEx2 = (PPS_SET_CREATE_PROCESS_NOTIFY_ROUTINE_EX2)KphGetSystemRoutineAddress(L"PsSetCreateProcessNotifyRoutineEx2");
KphDynMmProtectDriverSection = (PMM_PROTECT_DRIVER_SECTION)KphGetSystemRoutineAddress(L"MmProtectDriverSection");
KphDynPsGetProcessSequenceNumber = (PPS_GET_PROCESS_SEQUENCE_NUMBER)KphGetSystemRoutineAddress(L"PsGetProcessSequenceNumber");
KphDynPsGetProcessStartKey = (PPS_GET_PROCESS_START_KEY)KphGetSystemRoutineAddress(L"PsGetProcessStartKey");
KphSeRegisterImageVerificationCallback = (PSE_REGISTER_IMAGE_VERIFICATION_CALLBACK)KphGetSystemRoutineAddress(L"SeRegisterImageVerificationCallback");
KphSeUnregisterImageVerificationCallback = (PSE_UNREGISTER_IMAGE_VERIFICATION_CALLBACK)KphGetSystemRoutineAddress(L"SeUnregisterImageVerificationCallback");
KphDynCiValidateFileObject = (PCI_VALIDATE_FILE_OBJECT)KphGetRoutineAddress(L"ci.dll", "CiValidateFileObject");
KphDynCiFreePolicyInfo = (PCI_FREE_POLICY_INFO)KphGetRoutineAddress(L"ci.dll", "CiFreePolicyInfo");
KphDynLxpThreadGetCurrent = (PLXP_THREAD_GET_CURRENT)KphGetRoutineAddress(L"lxcore.sys", "LxpThreadGetCurrent");
KphDynIoCheckFileObjectOpenedAsCopySource = (PIO_CHECK_FILE_OBJECT_OPENED_AS_COPY_SOURCE)KphGetSystemRoutineAddress(L"IoCheckFileObjectOpenedAsCopySource");
KphDynIoCheckFileObjectOpenedAsCopyDestination = (PIO_CHECK_FILE_OBJECT_OPENED_AS_COPY_DESTINATION)KphGetSystemRoutineAddress(L"IoCheckFileObjectOpenedAsCopyDestination");
KphDynFltGetCopyInformationFromCallbackData = (PFLT_GET_COPY_INFORMATION_FROM_CALLBACK_DATA)KphGetRoutineAddress(L"fltMgr.sys", "FltGetCopyInformationFromCallbackData");
}
/**
* \brief Retrieves the address of a function exported by NTOS or HAL.
*
* \param SystemRoutineName The name of the function.
*
* \return The address of the function, or NULL if the function could
* not be found.
*/
_IRQL_requires_max_(PASSIVE_LEVEL)
PVOID KphGetSystemRoutineAddress(
_In_z_ PCWSTR SystemRoutineName
)
{
UNICODE_STRING systemRoutineName;
KPH_PAGED_CODE_PASSIVE();
RtlInitUnicodeString(&systemRoutineName, SystemRoutineName);
return MmGetSystemRoutineAddress(&systemRoutineName);
}
/**
* \brief Retrieves the address of a function using the exported module list.
* Caller must guarantee that PsLoadedModuleList and PsLoadedModuleResource
* is available prior to this call.
*
* \param ModuleName The name of the module to retrieve the function from.
* \param RoutineName The name of the routine to retrieve.
*
* \return The address of the function, or NULL if the function could
* not be found.
*/
_IRQL_requires_max_(PASSIVE_LEVEL)
PVOID KphpGetRoutineAddressByModuleList(
_In_z_ PCWSTR ModuleName,
_In_z_ PCSTR RoutineName
)
{
PVOID routine;
UNICODE_STRING moduleName;
KPH_PAGED_CODE_PASSIVE();
routine = NULL;
RtlInitUnicodeString(&moduleName, ModuleName);
KeEnterCriticalRegion();
if (!ExAcquireResourceSharedLite(PsLoadedModuleResource, TRUE))
{
KeLeaveCriticalRegion();
KphTracePrint(TRACE_LEVEL_VERBOSE,
GENERAL,
"Failed to acquire PsLoadedModuleResource to "
"get routine %ls!%hs",
ModuleName,
RoutineName);
return routine;
}
for (PLIST_ENTRY link = PsLoadedModuleList->Flink;
link != PsLoadedModuleList;
link = link->Flink)
{
PKLDR_DATA_TABLE_ENTRY entry;
entry = CONTAINING_RECORD(link, KLDR_DATA_TABLE_ENTRY, InLoadOrderLinks);
if (!RtlEqualUnicodeString(&entry->BaseDllName, &moduleName, TRUE))
{
continue;
}
routine = RtlFindExportedRoutineByName(entry->DllBase, RoutineName);
if (routine)
{
break;
}
}
ExReleaseResourceLite(PsLoadedModuleResource);
KeLeaveCriticalRegion();
if (!routine)
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
GENERAL,
"Failed to find routine %ls!%hs",
ModuleName,
RoutineName);
}
return routine;
}
/**
* \brief Retrieves the address of a function.
*
* \param ModuleName The name of the module to retrieve the function from.
* \param RoutineName The name of the routine to retrieve.
*
* \return The address of the function, or NULL if the function could
* not be found.
*/
_IRQL_requires_max_(PASSIVE_LEVEL)
PVOID KphGetRoutineAddress(
_In_z_ PCWSTR ModuleName,
_In_z_ PCSTR RoutineName
)
{
KPH_PAGED_CODE_PASSIVE();
return KphpGetRoutineAddressByModuleList(ModuleName, RoutineName);
}
================================================
FILE: KSystemInformer/file.c
================================================
/*
* Copyright (c) 2022 Winsider Seminars & Solutions, Inc. All rights reserved.
*
* This file is part of System Informer.
*
* Authors:
*
* jxy-s 2022-2026
*
*/
#include
#include
KPH_PAGED_FILE();
/**
* \brief Checks if a file handle is safe to issue a query through.
*
* \details Expects to be stack attached to the process that owns the handle.
*
* \param[in] FileHandle The file handle to check.
*
* \return Successful or errant status.
*/
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KphpCheckFileHandleForQuery(
_In_ HANDLE FileHandle
)
{
NTSTATUS status;
PFILE_OBJECT fileObject;
KPH_PAGED_CODE_PASSIVE();
//
// We are stack attached and "invading" the process to perform the query.
// If the file object is "Busy" it means it's a synchronous file object
// that is currently servicing an I/O request. If we were to issue another
// request the call would block until the other completes. Since we don't
// control this handle it's possible the call will never complete.
//
// This check isn't perfect, there is still a race, but it's narrower and
// makes it safer and faster for the client. As this stands it's a pretty
// gross hack, but we get value out of these APIs, so the hack is better
// than nothing.
//
// TODO(jxy-s) investigate other options to close or eliminate the race
//
status = ObReferenceObjectByHandle(FileHandle,
0,
*IoFileObjectType,
KernelMode,
&fileObject,
NULL);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
GENERAL,
"ObReferenceObjectByHandle failed %!STATUS!",
status);
return status;
}
status = fileObject->Busy ? STATUS_POSSIBLE_DEADLOCK : STATUS_SUCCESS;
ObDereferenceObject(fileObject);
return status;
}
/**
* \brief Queries file information.
*
* \param[in] ProcessHandle A handle to a process.
* \param[in] FileHandle A file handle which is present in the process.
* \param[in] FileInformationClass The type of information to retrieve.
* \param[out] FileInformation The buffer to store the information.
* \param[in] FileInformationLength Length of the information buffer.
* \param[in] IoStatusBlock Receives completion status and information.
* \param[in] AccessMode The mode in which to perform access checks.
*
* \return Successful or errant status.
*/
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KphQueryInformationFile(
_In_ HANDLE ProcessHandle,
_In_ HANDLE FileHandle,
_In_ FILE_INFORMATION_CLASS FileInformationClass,
_Out_writes_bytes_(FileInformationLength) PVOID FileInformation,
_In_ ULONG FileInformationLength,
_Out_ PIO_STATUS_BLOCK IoStatusBlock,
_In_ KPROCESSOR_MODE AccessMode
)
{
NTSTATUS status;
PEPROCESS process;
KAPC_STATE apcState;
IO_STATUS_BLOCK ioStatusBlock;
PVOID buffer;
BYTE stackBuffer[64];
KPH_PAGED_CODE_PASSIVE();
process = NULL;
buffer = NULL;
RtlZeroMemory(&ioStatusBlock, sizeof(IO_STATUS_BLOCK));
if (!FileInformation || !IoStatusBlock)
{
status = STATUS_INVALID_PARAMETER;
goto Exit;
}
status = ObReferenceObjectByHandle(ProcessHandle,
0,
*PsProcessType,
AccessMode,
&process,
NULL);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
GENERAL,
"ObReferenceObjectByHandle failed %!STATUS!",
status);
goto Exit;
}
if (process == PsInitialSystemProcess)
{
FileHandle = MakeKernelHandle(FileHandle);
}
else
{
if (IsKernelHandle(FileHandle))
{
status = STATUS_INVALID_HANDLE;
goto Exit;
}
}
buffer = KphAllocatePagedA(FileInformationLength,
KPH_TAG_FILE_QUERY,
stackBuffer);
if (!buffer)
{
status = STATUS_INSUFFICIENT_RESOURCES;
goto Exit;
}
KeStackAttachProcess(process, &apcState);
status = KphpCheckFileHandleForQuery(FileHandle);
if (NT_SUCCESS(status))
{
status = ZwQueryInformationFile(FileHandle,
&ioStatusBlock,
buffer,
FileInformationLength,
FileInformationClass);
}
KeUnstackDetachProcess(&apcState);
if (NT_SUCCESS(status))
{
status = KphCopyToMode(FileInformation,
buffer,
FileInformationLength,
AccessMode);
}
KphCopyToMode(IoStatusBlock,
&ioStatusBlock,
sizeof(IO_STATUS_BLOCK),
AccessMode);
Exit:
if (buffer)
{
KphFreeA(buffer, KPH_TAG_FILE_QUERY, stackBuffer);
}
if (process)
{
ObDereferenceObject(process);
}
return status;
}
/**
* \brief Queries file volume information.
*
* \param[in] ProcessHandle A handle to a process.
* \param[in] FileHandle A file handle which is present in the process.
* \param[in] FsInformationClass The type of information to retrieve.
* \param[out] FsInformation The buffer to store the information.
* \param[in] FsInformationLength Length of the information buffer.
* \param[in] IoStatusBlock Receives completion status and information.
* \param[in] AccessMode The mode in which to perform access checks.
*
* \return Successful or errant status.
*/
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KphQueryVolumeInformationFile(
_In_ HANDLE ProcessHandle,
_In_ HANDLE FileHandle,
_In_ FS_INFORMATION_CLASS FsInformationClass,
_Out_writes_bytes_(FsInformationLength) PVOID FsInformation,
_In_ ULONG FsInformationLength,
_Out_ PIO_STATUS_BLOCK IoStatusBlock,
_In_ KPROCESSOR_MODE AccessMode
)
{
NTSTATUS status;
PEPROCESS process;
KAPC_STATE apcState;
IO_STATUS_BLOCK ioStatusBlock;
PVOID buffer;
BYTE stackBuffer[64];
KPH_PAGED_CODE_PASSIVE();
process = NULL;
buffer = NULL;
RtlZeroMemory(&ioStatusBlock, sizeof(IO_STATUS_BLOCK));
if (!FsInformation || !IoStatusBlock)
{
status = STATUS_INVALID_PARAMETER;
goto Exit;
}
status = ObReferenceObjectByHandle(ProcessHandle,
0,
*PsProcessType,
AccessMode,
&process,
NULL);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
GENERAL,
"ObReferenceObjectByHandle failed %!STATUS!",
status);
goto Exit;
}
if (process == PsInitialSystemProcess)
{
FileHandle = MakeKernelHandle(FileHandle);
}
else
{
if (IsKernelHandle(FileHandle))
{
status = STATUS_INVALID_HANDLE;
goto Exit;
}
}
buffer = KphAllocatePagedA(FsInformationLength,
KPH_TAG_VOL_FILE_QUERY,
stackBuffer);
if (!buffer)
{
status = STATUS_INSUFFICIENT_RESOURCES;
goto Exit;
}
KeStackAttachProcess(process, &apcState);
status = KphpCheckFileHandleForQuery(FileHandle);
if (NT_SUCCESS(status))
{
status = ZwQueryVolumeInformationFile(FileHandle,
&ioStatusBlock,
buffer,
FsInformationLength,
FsInformationClass);
}
KeUnstackDetachProcess(&apcState);
if (NT_SUCCESS(status))
{
status = KphCopyToMode(FsInformation,
buffer,
FsInformationLength,
AccessMode);
}
KphCopyToMode(IoStatusBlock,
&ioStatusBlock,
sizeof(IO_STATUS_BLOCK),
AccessMode);
Exit:
if (buffer)
{
KphFreeA(buffer, KPH_TAG_VOL_FILE_QUERY, stackBuffer);
}
if (process)
{
ObDereferenceObject(process);
}
return status;
}
/**
* \brief Creates a file.
*
* \param[out] FileHandle Populated with file handle on success.
* \param[in] DesiredAccess The desired access to the file.
* \param[in] ObjectAttributes Object attributes for the create.
* \param[out] IoStatusBlock Receives final completion status and information.
* \param[in] AllocationSize Optional allocation size, in bytes, for the file.
* \param[in] FileAttributes Explicitly specified attributes are applied only
* when the file is created, superseded, or, in some cases, overwritten.
* \param[in] ShareAccess Specifies the type of share access to the file that
* the caller would like.
* \param[in] CreateDisposition Value that determines how the file should be
* handled when the file already exists.
* \param[in] CreateOptions Specifies the options to be applied when creating
* or opening the file.
* \param[in] EaBuffer Optional pointer to an EA buffer.
* \param[in] EaLength Length of EA buffer.
* \param[in] Options Specifies options to be used during the generation of
* the create request.
* \param[in] AccessMode The mode in which to perform access checks.
*
* \return Successful or errant status.
*/
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KphCreateFile(
_Out_ PHANDLE FileHandle,
_In_ ACCESS_MASK DesiredAccess,
_In_ POBJECT_ATTRIBUTES ObjectAttributes,
_Out_ PIO_STATUS_BLOCK IoStatusBlock,
_In_opt_ PLARGE_INTEGER AllocationSize,
_In_ ULONG FileAttributes,
_In_ ULONG ShareAccess,
_In_ ULONG CreateDisposition,
_In_ ULONG CreateOptions,
_In_reads_bytes_opt_(EaLength) PVOID EaBuffer,
_In_ ULONG EaLength,
_In_ ULONG Options,
_In_ KPROCESSOR_MODE AccessMode
)
{
NTSTATUS status;
KPH_PAGED_CODE_PASSIVE();
if (AccessMode != KernelMode)
{
if (ExGetPreviousMode() == KernelMode)
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
GENERAL,
"Unexpected previous mode");
NT_ASSERT(ExGetPreviousMode() != KernelMode);
status = STATUS_INVALID_LEVEL;
goto Exit;
}
if (Options & ~(IO_OPEN_TARGET_DIRECTORY |
IO_STOP_ON_SYMLINK |
IO_IGNORE_SHARE_ACCESS_CHECK |
IO_OPEN_PAGING_FILE))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
GENERAL,
"Invalid options 0x%lx",
Options);
status = STATUS_INVALID_PARAMETER;
goto Exit;
}
}
else
{
Options |= (IO_NO_PARAMETER_CHECKING | IO_CHECK_CREATE_PARAMETERS);
}
status = IoCreateFile(FileHandle,
DesiredAccess,
ObjectAttributes,
IoStatusBlock,
AllocationSize,
FileAttributes,
ShareAccess,
CreateDisposition,
CreateOptions,
EaBuffer,
EaLength,
CreateFileTypeNone,
NULL,
Options);
Exit:
return status;
}
================================================
FILE: KSystemInformer/hash.c
================================================
/*
* Copyright (c) 2022 Winsider Seminars & Solutions, Inc. All rights reserved.
*
* This file is part of System Informer.
*
* Authors:
*
* jxy-s 2022-2026
*
*/
#include
#include
#define KPH_HASHING_BUFFER_SIZE (16 * 1024)
#define KPH_HASH_EACACHE_MD5 KPH_KERNEL_PURGE_EA "MD5"
#define KPH_HASH_EACACHE_SHA1 KPH_KERNEL_PURGE_EA "SHA1"
#define KPH_HASH_EACACHE_SHA1_AUTHENTICODE KPH_KERNEL_PURGE_EA "SHA1A"
#define KPH_HASH_EACACHE_SHA256 KPH_KERNEL_PURGE_EA "SHA256"
#define KPH_HASH_EACACHE_SHA256_AUTHENTICODE KPH_KERNEL_PURGE_EA "SHA256A"
#define KPH_HASH_EACACHE_SHA384 KPH_KERNEL_PURGE_EA "SHA384"
#define KPH_HASH_EACACHE_SHA512 KPH_KERNEL_PURGE_EA "SHA512"
C_ASSERT(sizeof(KPH_HASH_EACACHE_MD5) < MAXUCHAR);
C_ASSERT(sizeof(KPH_HASH_EACACHE_SHA1) < MAXUCHAR);
C_ASSERT(sizeof(KPH_HASH_EACACHE_SHA1_AUTHENTICODE) < MAXUCHAR);
C_ASSERT(sizeof(KPH_HASH_EACACHE_SHA256) < MAXUCHAR);
C_ASSERT(sizeof(KPH_HASH_EACACHE_SHA256_AUTHENTICODE) < MAXUCHAR);
C_ASSERT(sizeof(KPH_HASH_EACACHE_SHA384) < MAXUCHAR);
C_ASSERT(sizeof(KPH_HASH_EACACHE_SHA512) < MAXUCHAR);
#define KPH_HASH_EACACHE_LEN(x) \
ALIGN_UP_BY(FIELD_OFFSET(FILE_GET_EA_INFORMATION, EaName) + \
sizeof(x), \
sizeof(ULONG))
#define KPH_HASH_EACACHE_FULL_LEN(x) \
ALIGN_UP_BY(FIELD_OFFSET(FILE_FULL_EA_INFORMATION, EaName) + \
sizeof(x) + \
KPH_HASH_ALGORITHM_MAX_LENGTH, \
sizeof(ULONG))
#define KPH_HASH_EACACHE_MAX_LENGTH ( \
KPH_HASH_EACACHE_LEN(KPH_HASH_EACACHE_MD5) + \
KPH_HASH_EACACHE_LEN(KPH_HASH_EACACHE_SHA1) + \
KPH_HASH_EACACHE_LEN(KPH_HASH_EACACHE_SHA1_AUTHENTICODE) + \
KPH_HASH_EACACHE_LEN(KPH_HASH_EACACHE_SHA256) + \
KPH_HASH_EACACHE_LEN(KPH_HASH_EACACHE_SHA256_AUTHENTICODE) + \
KPH_HASH_EACACHE_LEN(KPH_HASH_EACACHE_SHA384) + \
KPH_HASH_EACACHE_LEN(KPH_HASH_EACACHE_SHA512))
#define KPH_HASH_EACACHE_FULL_MAX_LENGTH ( \
KPH_HASH_EACACHE_FULL_LEN(KPH_HASH_EACACHE_MD5) + \
KPH_HASH_EACACHE_FULL_LEN(KPH_HASH_EACACHE_SHA1) + \
KPH_HASH_EACACHE_FULL_LEN(KPH_HASH_EACACHE_SHA1_AUTHENTICODE) + \
KPH_HASH_EACACHE_FULL_LEN(KPH_HASH_EACACHE_SHA256) + \
KPH_HASH_EACACHE_FULL_LEN(KPH_HASH_EACACHE_SHA256_AUTHENTICODE) + \
KPH_HASH_EACACHE_FULL_LEN(KPH_HASH_EACACHE_SHA384) + \
KPH_HASH_EACACHE_FULL_LEN(KPH_HASH_EACACHE_SHA512))
C_ASSERT(KPH_HASH_EACACHE_FULL_MAX_LENGTH <= KPH_HASHING_BUFFER_SIZE);
typedef struct _KPH_HASHING_INFRASTRUCTURE
{
PAGED_LOOKASIDE_LIST HashingLookaside;
BYTE EaList[KPH_HASH_EACACHE_MAX_LENGTH];
} KPH_HASHING_INFRASTRUCTURE, *PKPH_HASHING_INFRASTRUCTURE;
typedef struct _KPH_HASHING_EACACHE_INFORMATION
{
ULONG HashSize;
ANSI_STRING EaName;
} KPH_HASHING_EACACHE_INFORMATION, *PKPH_HASHING_EACACHE_INFORMATION;
typedef const KPH_HASHING_EACACHE_INFORMATION *PCKPH_HASHING_EACACHE_INFORMATION;
typedef struct _KPH_HASHING_EACACHE_CONTEXT
{
HANDLE FileHandle;
PFILE_OBJECT FileObject;
HANDLE OplockEventHandle;
PKEVENT OplockEventObject;
IO_STATUS_BLOCK IoStatusBlock;
REQUEST_OPLOCK_OUTPUT_BUFFER OplockOutput;
} KPH_HASHING_EACACHE_CONTEXT, *PKPH_HASHING_EACACHE_CONTEXT;
typedef struct _KPH_HASHING_AUTHENTICODE_CONTEXT
{
KPH_MEMORY_REGION Regions[4];
PVOID SecurityBase;
ULONG SecuritySize;
} KPH_HASHING_AUTHENTICODE_CONTEXT, *PKPH_HASHING_AUTHENTICODE_CONTEXT;
typedef struct _KPH_HASHING_ENTRY
{
BCRYPT_HASH_HANDLE Handle;
BOOLEAN HashComplete;
ULONG Length;
BYTE Hash[KPH_HASH_ALGORITHM_MAX_LENGTH];
} KPH_HASHING_ENTRY, *PKPH_HASHING_ENTRY;
typedef struct _KPH_HASHING_CONTEXT
{
KPH_HASHING_ENTRY Hash[MaxKphHashAlgorithm];
KPH_HASHING_AUTHENTICODE_CONTEXT Authenticode;
BOOLEAN RequiresHashing;
KPH_HASHING_EACACHE_CONTEXT EaCache;
BYTE Buffer[KPH_HASHING_BUFFER_SIZE];
} KPH_HASHING_CONTEXT, *PKPH_HASHING_CONTEXT;
KPH_PROTECTED_DATA_SECTION_RO_PUSH();
static const UNICODE_STRING KphpHashingInfraName = RTL_CONSTANT_STRING(L"KphHashingInfrastructure");
static const KPH_HASHING_EACACHE_INFORMATION KphpHashEaCacheInfo[] =
{
{ (128 / 8), RTL_CONSTANT_STRING(KPH_HASH_EACACHE_MD5) },
{ (160 / 8), RTL_CONSTANT_STRING(KPH_HASH_EACACHE_SHA1) },
{ (160 / 8), RTL_CONSTANT_STRING(KPH_HASH_EACACHE_SHA1_AUTHENTICODE) },
{ (256 / 8), RTL_CONSTANT_STRING(KPH_HASH_EACACHE_SHA256) },
{ (256 / 8), RTL_CONSTANT_STRING(KPH_HASH_EACACHE_SHA256_AUTHENTICODE) },
{ (384 / 8), RTL_CONSTANT_STRING(KPH_HASH_EACACHE_SHA384) },
{ (512 / 8), RTL_CONSTANT_STRING(KPH_HASH_EACACHE_SHA512) },
};
C_ASSERT(ARRAYSIZE(KphpHashEaCacheInfo) == MaxKphHashAlgorithm);
KPH_PROTECTED_DATA_SECTION_RO_POP();
KPH_PROTECTED_DATA_SECTION_PUSH();
static PKPH_HASHING_INFRASTRUCTURE KphpHashingInfra = NULL;
static PKPH_OBJECT_TYPE KphpHashingInfraType = NULL;
KPH_PROTECTED_DATA_SECTION_POP();
KPH_PAGED_FILE();
/**
* \brief Allocates hashing infrastructure object.
*
* \param[in] Size The size to allocate.
*
* \return Allocated hashing infrastructure object, null on failure.
*/
_Function_class_(KPH_TYPE_ALLOCATE_PROCEDURE)
_Return_allocatesMem_size_(Size)
PVOID KSIAPI KphpAllocateHashingInfra(
_In_ SIZE_T Size
)
{
KPH_PAGED_CODE();
return KphAllocateNPaged(Size, KPH_TAG_HASHING_INFRA);
}
/**
* \brief Initializes hashing infrastructure.
*
* \param[in,out] Object The hashing infrastructure to initialize.
* \param[in] Parameter Unused
*
* \return Successful or errant status.
*/
_Function_class_(KPH_TYPE_INITIALIZE_PROCEDURE)
_Must_inspect_result_
NTSTATUS KSIAPI KphpInitHashingInfra(
_Inout_ PVOID Object,
_In_opt_ PVOID Parameter
)
{
PKPH_HASHING_INFRASTRUCTURE infra;
PFILE_GET_EA_INFORMATION eaInfo;
KPH_PAGED_CODE();
UNREFERENCED_PARAMETER(Parameter);
infra = Object;
//
// Pre-populate the EA cache items into the buffer to be used when querying
// for the cached EA values. We compile time assert that it will all fit in
// the buffer.
//
eaInfo = (PFILE_GET_EA_INFORMATION)infra->EaList;
eaInfo->NextEntryOffset = 0;
for (ULONG i = 0; i < ARRAYSIZE(KphpHashEaCacheInfo); i++)
{
PCKPH_HASHING_EACACHE_INFORMATION eaCacheInfo;
eaInfo = Add2Ptr(eaInfo, eaInfo->NextEntryOffset);
eaCacheInfo = &KphpHashEaCacheInfo[i];
RtlCopyMemory(eaInfo->EaName,
eaCacheInfo->EaName.Buffer,
eaCacheInfo->EaName.Length);
eaInfo->EaNameLength = (UCHAR)eaCacheInfo->EaName.Length;
eaInfo->EaName[eaInfo->EaNameLength] = ANSI_NULL;
eaInfo->NextEntryOffset = FIELD_OFFSET(FILE_GET_EA_INFORMATION, EaName);
eaInfo->NextEntryOffset += eaCacheInfo->EaName.Length;
eaInfo->NextEntryOffset += sizeof(ANSI_NULL);
eaInfo->NextEntryOffset = ALIGN_UP_BY(eaInfo->NextEntryOffset,
sizeof(ULONG));
}
eaInfo->NextEntryOffset = 0;
KphInitializePagedLookaside(&infra->HashingLookaside,
sizeof(KPH_HASHING_CONTEXT),
KPH_TAG_HASHING_CONTEXT);
return STATUS_SUCCESS;
}
/**
* \brief Deletes hashing infrastructure.
*
* \param[in,out] Object The hashing infrastructure to delete.
*/
_Function_class_(KPH_TYPE_DELETE_PROCEDURE)
VOID KSIAPI KphpDeleteHashingInfra(
_Inout_ PVOID Object
)
{
PKPH_HASHING_INFRASTRUCTURE infra;
KPH_PAGED_CODE();
infra = Object;
KphDeletePagedLookaside(&infra->HashingLookaside);
}
/**
* \brief Frees hashing infrastructure object.
*
* \param[in] Object The object to free.
*/
_Function_class_(KPH_TYPE_FREE_PROCEDURE)
VOID KSIAPI KphpFreeHashingInfra(
_In_freesMem_ PVOID Object
)
{
KPH_PAGED_CODE();
KphFree(Object, KPH_TAG_HASHING_INFRA);
}
/**
* \brief Allocates a hashing context from the hashing look-aside list.
*
* \return Hashing context, null on failure.
*/
_IRQL_requires_max_(PASSIVE_LEVEL)
_Return_allocatesMem_
PKPH_HASHING_CONTEXT KphpAllocateHashingContext(
VOID
)
{
KPH_PAGED_CODE_PASSIVE();
NT_ASSERT(KphpHashingInfra);
return KphAllocateFromPagedLookaside(&KphpHashingInfra->HashingLookaside);
}
_IRQL_requires_max_(PASSIVE_LEVEL)
VOID KphpCloseHashingEaCacheContext(
_Inout_ PKPH_HASHING_EACACHE_CONTEXT Context
)
{
KPH_PAGED_CODE_PASSIVE();
if (Context->FileObject)
{
ObDereferenceObject(Context->FileObject);
Context->FileObject = NULL;
}
if (Context->FileHandle)
{
NT_ASSERT(!KeAreAllApcsDisabled());
ObCloseHandle(Context->FileHandle, KernelMode);
Context->FileHandle = NULL;
}
if (Context->OplockEventObject)
{
KeWaitForSingleObject(Context->OplockEventObject,
Executive,
KernelMode,
FALSE,
NULL);
ObDereferenceObject(Context->OplockEventObject);
Context->OplockEventObject = NULL;
}
if (Context->OplockEventHandle)
{
ObCloseHandle(Context->OplockEventHandle, KernelMode);
Context->OplockEventHandle = NULL;
}
}
/**
* \brief Frees a hashing context back to the look-aside list.
*
* \param[in] Buffer The context to free back to the look-aside list.
*/
_IRQL_requires_max_(PASSIVE_LEVEL)
VOID KphpFreeHashingContext(
_In_freesMem_ PKPH_HASHING_CONTEXT Context
)
{
KPH_PAGED_CODE_PASSIVE();
NT_ASSERT(KphpHashingInfra);
for (ULONG i = 0; i < ARRAYSIZE(Context->Hash); i++)
{
if (Context->Hash[i].Handle)
{
BCryptDestroyHash(Context->Hash[i].Handle);
}
}
KphpCloseHashingEaCacheContext(&Context->EaCache);
KphFreeToPagedLookaside(&KphpHashingInfra->HashingLookaside, Context);
}
/**
* \brief Initializes hashing infrastructure.
*
* \return Successful or errant status.
*/
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KphInitializeHashing(
VOID
)
{
NTSTATUS status;
KPH_OBJECT_TYPE_INFO typeInfo;
KPH_PAGED_CODE_PASSIVE();
typeInfo.Allocate = KphpAllocateHashingInfra;
typeInfo.Initialize = KphpInitHashingInfra;
typeInfo.Delete = KphpDeleteHashingInfra;
typeInfo.Free = KphpFreeHashingInfra;
typeInfo.Flags = 0;
KphCreateObjectType(&KphpHashingInfraName,
&typeInfo,
&KphpHashingInfraType);
status = KphCreateObject(KphpHashingInfraType,
sizeof(KPH_HASHING_INFRASTRUCTURE),
&KphpHashingInfra,
NULL);
if (!NT_SUCCESS(status))
{
KphpHashingInfra = NULL;
}
return status;
}
/**
* \brief Cleans up hashing infrastructure.
*/
_IRQL_requires_max_(PASSIVE_LEVEL)
VOID KphCleanupHashing(
VOID
)
{
KPH_PAGED_CODE_PASSIVE();
if (KphpHashingInfra)
{
KphDereferenceObject(KphpHashingInfra);
}
}
/**
* \brief References the signing infrastructure.
*/
_IRQL_requires_max_(APC_LEVEL)
VOID KphReferenceHashingInfrastructure(
VOID
)
{
KPH_PAGED_CODE();
NT_ASSERT(KphpHashingInfra);
KphReferenceObject(KphpHashingInfra);
}
/**
* \brief Dereferences the signing infrastructure.
*/
_IRQL_requires_max_(APC_LEVEL)
VOID KphDereferenceHashingInfrastructure(
VOID
)
{
KPH_PAGED_CODE();
NT_ASSERT(KphpHashingInfra);
KphDereferenceObject(KphpHashingInfra);
}
/**
* \brief Hashes a buffer.
*
* \param[in] Buffer The buffer to hash.
* \param[in] BufferLength The length of the buffer.
* \param[in] HashAlgorithm The algorithm to use for hashing.
* \param[out] HashInformation Populated with the hash information.
*
* \return Successful or errant status.
*/
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KphHashBuffer(
_In_ PBYTE Buffer,
_In_ ULONG BufferLength,
_In_ KPH_HASH_ALGORITHM HashAlgorithm,
_Out_ PKPH_HASH_INFORMATION HashInformation
)
{
NTSTATUS status;
BCRYPT_ALG_HANDLE algHandle;
BCRYPT_HASH_HANDLE hashHandle;
KPH_PAGED_CODE_PASSIVE();
NT_ASSERT(KphpHashingInfra);
hashHandle = NULL;
RtlZeroMemory(HashInformation, sizeof(*HashInformation));
HashInformation->Algorithm = HashAlgorithm;
switch (HashAlgorithm)
{
case KphHashAlgorithmMd5:
{
HashInformation->Length = (128 / 8);
algHandle = BCRYPT_MD5_ALG_HANDLE;
break;
}
case KphHashAlgorithmSha1:
{
HashInformation->Length = (160 / 8);
algHandle = BCRYPT_SHA1_ALG_HANDLE;
break;
}
case KphHashAlgorithmSha256:
{
HashInformation->Length = (256 / 8);
algHandle = BCRYPT_SHA256_ALG_HANDLE;
break;
}
case KphHashAlgorithmSha384:
{
HashInformation->Length = (384 / 8);
algHandle = BCRYPT_SHA384_ALG_HANDLE;
break;
}
case KphHashAlgorithmSha512:
{
HashInformation->Length = (512 / 8);
algHandle = BCRYPT_SHA512_ALG_HANDLE;
break;
}
default:
{
status = STATUS_INVALID_PARAMETER;
goto Exit;
}
}
status = BCryptCreateHash(algHandle, &hashHandle, NULL, 0, NULL, 0, 0);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
HASH,
"BCryptCreateHash failed: %!STATUS!",
status);
hashHandle = NULL;
goto Exit;
}
status = BCryptHashData(hashHandle, Buffer, BufferLength, 0);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
HASH,
"BCryptHashData failed: %!STATUS!",
status);
goto Exit;
}
status = BCryptFinishHash(hashHandle,
HashInformation->Hash,
HashInformation->Length,
0);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
HASH,
"BCryptFinishHash failed: %!STATUS!",
status);
goto Exit;
}
Exit:
if (hashHandle)
{
BCryptDestroyHash(hashHandle);
}
return status;
}
/**
* \brief Updates a hash with a buffer from the context.
*
* \details The internal context buffer must already be updated with a copy of
* data pointed to by UnsafeBuffer.
*
* \param[in,out] Entry The hash entry to update.
* \param[in] Context The hashing context.
* \param[in] Authenticode Whether the hash is for authenticode or not.
* \param[in] UnsafeBuffer Address from which the information was copied.
* \param[in] Length The length of the buffer.
*
* \return Successful or errant status.
*/
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KphpUpdateHash(
_Inout_ PKPH_HASHING_ENTRY Entry,
_In_ PKPH_HASHING_CONTEXT Context,
_In_ BOOLEAN Authenticode,
_In_ PVOID UnsafeBuffer,
_In_ ULONG Length
)
{
PVOID unsafeEnd;
KPH_PAGED_CODE_PASSIVE();
NT_ASSERT(Entry->Handle && !Entry->HashComplete);
if (!Authenticode)
{
return BCryptHashData(Entry->Handle, Context->Buffer, Length, 0);
}
//
// Only hash within a region relevant for authenticode hashing.
//
unsafeEnd = Add2Ptr(UnsafeBuffer, Length);
for (ULONG i = 0; i < ARRAYSIZE(Context->Authenticode.Regions); i++)
{
NTSTATUS status;
PKPH_MEMORY_REGION region;
PVOID start;
PVOID end;
PVOID buffer;
ULONG length;
region = &Context->Authenticode.Regions[i];
if (!region->Start)
{
break;
}
if ((UnsafeBuffer >= region->Start) && (UnsafeBuffer < region->End))
{
start = UnsafeBuffer;
}
else if ((region->Start >= UnsafeBuffer) && (region->Start < unsafeEnd))
{
start = region->Start;
}
else
{
start = NULL;
}
if ((unsafeEnd >= region->Start) && (unsafeEnd < region->End))
{
end = unsafeEnd;
}
else if ((region->End >= UnsafeBuffer) && (region->End < unsafeEnd))
{
end = region->End;
}
else
{
end = NULL;
}
if (start && end)
{
NOTHING;
}
else if (start && !end)
{
end = unsafeEnd;
}
else if (!start && end)
{
start = UnsafeBuffer;
}
else
{
continue;
}
NT_ASSERT(start <= end);
buffer = Add2Ptr(Context->Buffer, PtrOffset(UnsafeBuffer, start));
length = PtrOffset(start, end);
status = BCryptHashData(Entry->Handle, buffer, length, 0);
if (!NT_SUCCESS(status))
{
return status;
}
}
return STATUS_SUCCESS;
}
/**
* \brief Updates all hashes in the context with the context buffer.
*
* \details The internal context buffer must already be updated with a copy of
* data pointed to by UnsafeBuffer.
*
* \param[in,out] Context The hashing context.
* \param[in] UnsafeBuffer Address from which the information was copied.
* \param[in] Length The length of the buffer.
*
* \return Successful or errant status.
*/
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KphpUpdateHashes(
_Inout_ PKPH_HASHING_CONTEXT Context,
_In_ PVOID UnsafeBuffer,
_In_ ULONG Length
)
{
KPH_PAGED_CODE_PASSIVE();
for (ULONG i = 0; i < ARRAYSIZE(Context->Hash); i++)
{
NTSTATUS status;
PKPH_HASHING_ENTRY entry;
BOOLEAN authenticode;
entry = &Context->Hash[i];
if (entry->HashComplete || !entry->Handle)
{
continue;
}
authenticode = ((i == KphHashAlgorithmSha1Authenticode) ||
(i == KphHashAlgorithmSha256Authenticode));
status = KphpUpdateHash(entry,
Context,
authenticode,
UnsafeBuffer,
Length);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
HASH,
"KphpUpdateHash failed: %!STATUS!",
status);
return status;
}
}
return STATUS_SUCCESS;
}
/**
* \brief Initializes a hashing context.
*
* \param[in,out] Context The hashing context to initialize.
* \param[in] HashInformation The hash information to use for initialization.
* \param[in] NumberOfHashes The number of hashing information items.
*
* \return Successful or errant status.
*/
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KphpInitializeHashingContext(
_Inout_ PKPH_HASHING_CONTEXT Context,
_In_ PKPH_HASH_INFORMATION HashInformation,
_In_ ULONG NumberOfHashes
)
{
NTSTATUS status;
KPH_PAGED_CODE_PASSIVE();
status = STATUS_SUCCESS;
for (ULONG i = 0; i < NumberOfHashes; i++)
{
PKPH_HASHING_ENTRY entry;
BCRYPT_ALG_HANDLE algHandle;
if ((HashInformation[i].Algorithm >= MaxKphHashAlgorithm) ||
(HashInformation[i].Algorithm < 0))
{
status = STATUS_INVALID_PARAMETER;
goto Exit;
}
entry = &Context->Hash[HashInformation[i].Algorithm];
if (entry->HashComplete || entry->Handle)
{
continue;
}
Context->RequiresHashing = TRUE;
switch (HashInformation[i].Algorithm)
{
case KphHashAlgorithmMd5:
{
entry->Length = (128 / 8);
algHandle = BCRYPT_MD5_ALG_HANDLE;
break;
}
case KphHashAlgorithmSha1:
case KphHashAlgorithmSha1Authenticode:
{
entry->Length = (160 / 8);
algHandle = BCRYPT_SHA1_ALG_HANDLE;
break;
}
case KphHashAlgorithmSha256:
case KphHashAlgorithmSha256Authenticode:
{
entry->Length = (256 / 8);
algHandle = BCRYPT_SHA256_ALG_HANDLE;
break;
}
case KphHashAlgorithmSha384:
{
entry->Length = (384 / 8);
algHandle = BCRYPT_SHA384_ALG_HANDLE;
break;
}
case KphHashAlgorithmSha512:
{
entry->Length = (512 / 8);
algHandle = BCRYPT_SHA512_ALG_HANDLE;
break;
}
DEFAULT_UNREACHABLE;
}
NT_ASSERT(entry->Length <= sizeof(Context->Hash));
status = BCryptCreateHash(algHandle,
&entry->Handle,
NULL,
0,
NULL,
0,
0);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
HASH,
"BCryptCreateHash failed: %!STATUS!",
status);
entry->Handle = NULL;
goto Exit;
}
}
Exit:
return status;
}
/**
* \brief Loads hashes from the EA cache into the hashing context.
*
* \param[in,out] Context The hashing context to load the hashes into.
* \param[in] FileHandle The file handle to load the hashes from.
* \param[in] AccessMode The mode in which to perform access checks.
*/
_IRQL_requires_max_(PASSIVE_LEVEL)
VOID KphpLoadHashesFromEaCache(
_Inout_ PKPH_HASHING_CONTEXT Context,
_In_ HANDLE FileHandle,
_In_ KPROCESSOR_MODE AccessMode
)
{
NTSTATUS status;
PFILE_OBJECT fileObject;
ULONG returnLength;
PFILE_FULL_EA_INFORMATION fullEaInfo;
KPH_PAGED_CODE_PASSIVE();
status = ObReferenceObjectByHandle(FileHandle,
0,
*IoFileObjectType,
AccessMode,
&fileObject,
NULL);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
HASH,
"ObReferenceObjectByHandle failed: %!STATUS!",
status);
return;
}
//
// N.B. We compile time assert that all the information we might store in
// our extended attributes will fit within the supplied buffer.
//
status = FsRtlQueryKernelEaFile(fileObject,
Context->Buffer,
sizeof(Context->Buffer),
FALSE,
KphpHashingInfra->EaList,
sizeof(KphpHashingInfra->EaList),
NULL,
TRUE,
&returnLength);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
HASH,
"FsRtlQueryKernelEaFile failed: %!STATUS!",
status);
goto Exit;
}
fullEaInfo = (PFILE_FULL_EA_INFORMATION)Context->Buffer;
for (;;)
{
ANSI_STRING eaName;
eaName.Buffer = fullEaInfo->EaName;
eaName.Length = fullEaInfo->EaNameLength;
eaName.MaximumLength = fullEaInfo->EaNameLength;
for (ULONG i = 0; i < ARRAYSIZE(KphpHashEaCacheInfo); i++)
{
PCKPH_HASHING_EACACHE_INFORMATION eaCacheInfo;
PKPH_HASHING_ENTRY entry;
PVOID buffer;
eaCacheInfo = &KphpHashEaCacheInfo[i];
//
// None of our EA names will reach this limit. We compile time
// assert this and runtime assert here for clarity. This is
// necessary when advancing the buffer pointer below.
//
NT_ASSERT(eaCacheInfo->EaName.Length < MAXUCHAR);
if (fullEaInfo->EaValueLength != eaCacheInfo->HashSize)
{
continue;
}
if (!RtlEqualString(&eaName, &eaCacheInfo->EaName, FALSE))
{
continue;
}
entry = &Context->Hash[i];
buffer = Add2Ptr(fullEaInfo->EaName,
fullEaInfo->EaNameLength + sizeof(ANSI_NULL));
RtlCopyMemory(entry->Hash, buffer, fullEaInfo->EaValueLength);
entry->Length = fullEaInfo->EaValueLength;
entry->HashComplete = TRUE;
break;
}
if (!fullEaInfo->NextEntryOffset)
{
break;
}
fullEaInfo = Add2Ptr(fullEaInfo, fullEaInfo->NextEntryOffset);
}
Exit:
ObDereferenceObject(fileObject);
}
/**
* \brief Initializes the EA cache context for hashing.
*
* \details The EA caching is completely opportunistic and will only be used if
* we can guarantee cache consistency. We do this using an opportunistic lock.
* The caller should call KphpProgressEaCacheContext to after each read from
* the file data to determine if there is another contender for writing to the
* file. If contention is detected, the EA cache will be closed and the cache
* will not be updated. This avoids contention on the system and ensures cache
* consistency.
*
* \param[in,out] Context The hashing context to initialize.
* \param[in] FileHandle The file handle to initialize the context with.
*/
_IRQL_requires_max_(PASSIVE_LEVEL)
VOID KphpInitializeEaCacheContext(
_Inout_ PKPH_HASHING_CONTEXT Context,
_In_ HANDLE FileHandle
)
{
NTSTATUS status;
UNICODE_STRING objectName;
OBJECT_ATTRIBUTES objectAttributes;
IO_STATUS_BLOCK ioStatusBlock;
REQUEST_OPLOCK_INPUT_BUFFER oplockInput;
ULONG64 usnValue;
ULONG returnLength;
KPH_PAGED_CODE_PASSIVE();
RtlInitUnicodeString(&objectName, NULL);
InitializeObjectAttributes(&objectAttributes,
&objectName,
OBJ_KERNEL_HANDLE,
FileHandle,
NULL);
status = KphCreateFile(&Context->EaCache.FileHandle,
FILE_WRITE_EA | FILE_READ_DATA | FILE_READ_ATTRIBUTES,
&objectAttributes,
&ioStatusBlock,
NULL,
FILE_ATTRIBUTE_NORMAL,
FILE_SHARE_READ,
FILE_OPEN,
FILE_NON_DIRECTORY_FILE | FILE_OPEN_REQUIRING_OPLOCK,
NULL,
0,
0,
KernelMode);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
HASH,
"KphCreateFile failed: %!STATUS!",
status);
Context->EaCache.FileHandle = NULL;
goto Exit;
}
status = ObReferenceObjectByHandle(Context->EaCache.FileHandle,
FILE_ALL_ACCESS,
*IoFileObjectType,
KernelMode,
&Context->EaCache.FileObject,
NULL);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
HASH,
"ObReferenceObjectByHandle failed: %!STATUS!",
status);
Context->EaCache.FileObject = NULL;
goto Exit;
}
status = ZwCreateEvent(&Context->EaCache.OplockEventHandle,
EVENT_ALL_ACCESS,
NULL,
NotificationEvent,
TRUE);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
HASH,
"ZwCreateEvent failed: %!STATUS!",
status);
Context->EaCache.OplockEventHandle = NULL;
goto Exit;
}
status = ObReferenceObjectByHandle(Context->EaCache.OplockEventHandle,
EVENT_ALL_ACCESS,
*ExEventObjectType,
KernelMode,
&Context->EaCache.OplockEventObject,
NULL);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
HASH,
"ObReferenceObjectByHandle failed: %!STATUS!",
status);
Context->EaCache.OplockEventObject = NULL;
goto Exit;
}
oplockInput.StructureVersion = REQUEST_OPLOCK_CURRENT_VERSION;
oplockInput.StructureLength = sizeof(REQUEST_OPLOCK_INPUT_BUFFER);
oplockInput.Flags = REQUEST_OPLOCK_INPUT_FLAG_REQUEST;
oplockInput.RequestedOplockLevel = (OPLOCK_LEVEL_CACHE_READ |
OPLOCK_LEVEL_CACHE_HANDLE);
KeResetEvent(Context->EaCache.OplockEventObject);
status = ZwFsControlFile(Context->EaCache.FileHandle,
Context->EaCache.OplockEventHandle,
NULL,
NULL,
&Context->EaCache.IoStatusBlock,
FSCTL_REQUEST_OPLOCK,
&oplockInput,
sizeof(REQUEST_OPLOCK_INPUT_BUFFER),
&Context->EaCache.OplockOutput,
sizeof(Context->EaCache.OplockOutput));
if (status != STATUS_PENDING)
{
KeSetEvent(Context->EaCache.OplockEventObject, IO_NO_INCREMENT, FALSE);
KphTracePrint(TRACE_LEVEL_VERBOSE,
HASH,
"ZwFsControlFile returned: %!STATUS!",
status);
if (NT_SUCCESS(status))
{
status = STATUS_UNEXPECTED_IO_ERROR;
}
goto Exit;
}
status = FsRtlKernelFsControlFile(Context->EaCache.FileObject,
FSCTL_WRITE_USN_CLOSE_RECORD,
NULL,
0,
&usnValue,
sizeof(ULONG64),
&returnLength);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
HASH,
"FsRtlKernelFsControlFile failed: %!STATUS!",
status);
goto Exit;
}
Exit:
if (!NT_SUCCESS(status))
{
KphpCloseHashingEaCacheContext(&Context->EaCache);
}
}
/**
* \brief Progresses the EA cache context.
*
* \details This function should be called after each read from the file data.
*
* \param[in,out] Context The hashing context to progress the EA cache of.
*
* \return TRUE if the EA cache is available and the opportunistic lock has not
* been broken, FALSE otherwise.
*/
_IRQL_requires_max_(PASSIVE_LEVEL)
BOOLEAN KphpProgressEaCacheContext(
_Inout_ PKPH_HASHING_CONTEXT Context
)
{
LARGE_INTEGER zeroTimeout = KPH_TIMEOUT(0);
KPH_PAGED_CODE_PASSIVE();
if (!Context->EaCache.OplockEventObject)
{
return FALSE;
}
if (KeWaitForSingleObject(Context->EaCache.OplockEventObject,
Executive,
KernelMode,
FALSE,
&zeroTimeout) != STATUS_SUCCESS)
{
return TRUE;
}
//
// The opportunistic lock has been broken. We could try to guarantee cache
// consistency by not closing the handle. Doing so would block the thread
// attempting to open a write handle until we acknowledge the break.
//
// An opportunistic lock break can also happen when a write occurs to the
// file, but this will not suspend the write operation for acknowledgment.
// Therefore, acknowledging the break here is important to avoid the
// possible cache inconsistency.
//
// We choose to completely get out of the way of the system and acknowledge
// the break of the opportunistic lock by closing the handle. We will
// continue to hash and return whatever that result is, but we will not
// update the EA cache since we can not guarantee cash consistency.
//
// N.B. It is important to close the handle instead of using the lock break
// acknowledgment control code, otherwise we could be the cause of a
// sharing violation.
//
KphTracePrint(TRACE_LEVEL_VERBOSE, HASH, "Opportunistic lock broken");
KphpCloseHashingEaCacheContext(&Context->EaCache);
return FALSE;
}
/**
* \brief Initializes the hashing context for file hashing.
*
* \param[in,out] Context The hashing context to initialize.
* \param[in] FileHandle The file handle to initialize the context with.
* \param[in] HashInformation The hash information to use for initialization.
* \param[in] NumberOfHashes The number of hashing information items.
* \param[in] AccessMode The mode in which to perform access checks.
*
* \return Successful or errant status.
*/
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KphpInitializeFileHashingContext(
_Inout_ PKPH_HASHING_CONTEXT Context,
_In_ HANDLE FileHandle,
_In_ PKPH_HASH_INFORMATION HashInformation,
_In_ ULONG NumberOfHashes,
_In_ KPROCESSOR_MODE AccessMode
)
{
NTSTATUS status;
KPH_PAGED_CODE_PASSIVE();
KphpLoadHashesFromEaCache(Context, FileHandle, AccessMode);
status = KphpInitializeHashingContext(Context,
HashInformation,
NumberOfHashes);
if (NT_SUCCESS(status) && Context->RequiresHashing)
{
KphpInitializeEaCacheContext(Context, FileHandle);
}
return status;
}
/**
* \brief Initializes the hashing context for authenticode hashing.
*
* \details This function can raise a structured exception due to an in-page
* error, which callers should be prepared to handle.
*
* \param[in,out] Context The hashing context to initialize.
* \param[in] MappedBase The base address of the mapped image.
* \param[in] ViewSize The size of the mapped image.
*
* \return Successful or errant status.
*/
_IRQL_requires_max_(PASSIVE_LEVEL)
NTSTATUS KphpInitializeAuthenticodeHashing(
_Inout_ PKPH_HASHING_CONTEXT Context,
_In_ PVOID MappedBase,
_In_ SIZE_T ViewSize
)
{
NTSTATUS status;
PVOID mappedEnd;
KPH_IMAGE_NT_HEADERS image;
PIMAGE_DATA_DIRECTORY securityDir;
PVOID securityBase;
ULONG securitySize;
PVOID securityEnd;
KPH_PAGED_CODE_PASSIVE();
RtlZeroMemory(&Context->Authenticode, sizeof(Context->Authenticode));
mappedEnd = Add2Ptr(MappedBase, ViewSize);
securityDir = NULL;
securityBase = NULL;
securityEnd = NULL;
NT_ASSERT(MappedBase <= mappedEnd);
status = KphImageNtHeader(MappedBase, ViewSize, &image);
if (!NT_SUCCESS(status))
{
goto Exit;
}
if (!RTL_CONTAINS_FIELD(&image.Headers->OptionalHeader,
image.Headers->FileHeader.SizeOfOptionalHeader,
Magic))
{
Context->Authenticode.Regions[0].Start = MappedBase;
Context->Authenticode.Regions[0].End = mappedEnd;
status = STATUS_SUCCESS;
goto VerifyRegions;
}
if (image.Headers->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR32_MAGIC)
{
if (!RTL_CONTAINS_FIELD(&image.Headers32->OptionalHeader,
image.Headers->FileHeader.SizeOfOptionalHeader,
CheckSum))
{
Context->Authenticode.Regions[0].Start = MappedBase;
Context->Authenticode.Regions[0].End = mappedEnd;
status = STATUS_SUCCESS;
goto VerifyRegions;
}
Context->Authenticode.Regions[0].Start = MappedBase;
Context->Authenticode.Regions[0].End = &image.Headers32->OptionalHeader.CheckSum;
Context->Authenticode.Regions[1].Start = &image.Headers32->OptionalHeader.Subsystem;
if (!RTL_CONTAINS_FIELD(&image.Headers32->OptionalHeader,
image.Headers->FileHeader.SizeOfOptionalHeader,
NumberOfRvaAndSizes) ||
(image.Headers32->OptionalHeader.NumberOfRvaAndSizes <= IMAGE_DIRECTORY_ENTRY_SECURITY))
{
Context->Authenticode.Regions[1].End = mappedEnd;
status = STATUS_SUCCESS;
goto VerifyRegions;
}
securityDir = &image.Headers32->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_SECURITY];
Context->Authenticode.Regions[1].End = securityDir;
}
else if (image.Headers->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC)
{
if (!RTL_CONTAINS_FIELD(&image.Headers64->OptionalHeader,
image.Headers->FileHeader.SizeOfOptionalHeader,
CheckSum))
{
Context->Authenticode.Regions[0].Start = MappedBase;
Context->Authenticode.Regions[0].End = mappedEnd;
status = STATUS_SUCCESS;
goto VerifyRegions;
}
Context->Authenticode.Regions[0].Start = MappedBase;
Context->Authenticode.Regions[0].End = &image.Headers64->OptionalHeader.CheckSum;
Context->Authenticode.Regions[1].Start = &image.Headers64->OptionalHeader.Subsystem;
if (!RTL_CONTAINS_FIELD(&image.Headers64->OptionalHeader,
image.Headers->FileHeader.SizeOfOptionalHeader,
NumberOfRvaAndSizes) ||
(image.Headers64->OptionalHeader.NumberOfRvaAndSizes <= IMAGE_DIRECTORY_ENTRY_SECURITY))
{
Context->Authenticode.Regions[1].End = mappedEnd;
status = STATUS_SUCCESS;
goto VerifyRegions;
}
securityDir = &image.Headers64->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_SECURITY];
Context->Authenticode.Regions[1].End = securityDir;
}
else
{
status = STATUS_INVALID_IMAGE_FORMAT;
goto Exit;
}
NT_ASSERT(securityDir);
Context->Authenticode.Regions[2].Start = &securityDir[1];
if (!securityDir->VirtualAddress || !securityDir->Size)
{
Context->Authenticode.Regions[2].End = mappedEnd;
status = STATUS_SUCCESS;
goto VerifyRegions;
}
securityBase = Add2Ptr(MappedBase, securityDir->VirtualAddress);
securitySize = securityDir->Size;
securityEnd = Add2Ptr(securityBase, securitySize);
Context->Authenticode.SecurityBase = securityBase;
Context->Authenticode.SecuritySize = securitySize;
Context->Authenticode.Regions[2].End = securityBase;
if (securityEnd < mappedEnd)
{
Context->Authenticode.Regions[3].Start = securityEnd;
Context->Authenticode.Regions[3].End = mappedEnd;
}
VerifyRegions:
for (ULONG i = 0; i < ARRAYSIZE(Context->Authenticode.Regions); i++)
{
PKPH_MEMORY_REGION region;
region = &Context->Authenticode.Regions[i];
if (!region->Start)
{
break;
}
if (!KphIsValidMemoryRegion(region, MappedBase, mappedEnd))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
HASH,
"Authenticode region overflows mapping.");
status = STATUS_BUFFER_OVERFLOW;
goto Exit;
}
}
if (securityBase)
{
KPH_MEMORY_REGION securityRegion;
securityRegion.Start = securityBase;
securityRegion.End = securityEnd;
if (!KphIsValidMemoryRegion(&securityRegion, MappedBase, mappedEnd))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
HASH,
"Security directory overflows mapping.");
status = STATUS_BUFFER_OVERFLOW;
goto Exit;
}
}
Exit:
if (!NT_SUCCESS(status))
{
RtlZeroMemory(&Context->Authenticode, sizeof(Context->Authenticode));
}
return status;
}
/**
* \brief Finishes hashes in the hashing context.
*
* \param[in,out] Context The hashing context to finish the hashes in.
*
* \return Successful or errant status.
*/
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KphpFinishHashes(
_In_ PKPH_HASHING_CONTEXT Context
)
{
NTSTATUS status;
PFILE_FULL_EA_INFORMATION fullEaInfo;
ULONG fullEaInfoLength;
KPH_PAGED_CODE_PASSIVE();
fullEaInfoLength = 0;
if (Context->EaCache.FileObject)
{
fullEaInfo = (PFILE_FULL_EA_INFORMATION)Context->Buffer;
fullEaInfo->NextEntryOffset = 0;
}
else
{
fullEaInfo = NULL;
}
for (ULONG i = 0; i < ARRAYSIZE(Context->Hash); i++)
{
PKPH_HASHING_ENTRY entry;
PCKPH_HASHING_EACACHE_INFORMATION eaInfo;
PVOID buffer;
entry = &Context->Hash[i];
if (!entry->Handle)
{
continue;
}
status = BCryptFinishHash(entry->Handle,
entry->Hash,
entry->Length,
0);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
HASH,
"BCryptFinishHash failed: %!STATUS!",
status);
return status;
}
entry->HashComplete = TRUE;
if (!fullEaInfo)
{
continue;
}
fullEaInfo = Add2Ptr(fullEaInfo, fullEaInfo->NextEntryOffset);
eaInfo = &KphpHashEaCacheInfo[i];
fullEaInfo->Flags = 0;
fullEaInfo->EaNameLength = (UCHAR)eaInfo->EaName.Length;
fullEaInfo->EaValueLength = (USHORT)entry->Length;
RtlCopyMemory(fullEaInfo->EaName,
eaInfo->EaName.Buffer,
eaInfo->EaName.Length);
fullEaInfo->EaName[eaInfo->EaName.Length] = ANSI_NULL;
buffer = Add2Ptr(fullEaInfo->EaName,
fullEaInfo->EaNameLength + sizeof(ANSI_NULL));
RtlCopyMemory(buffer, entry->Hash, entry->Length);
buffer = Add2Ptr(buffer, entry->Length);
fullEaInfo->NextEntryOffset = ALIGN_UP_BY(PtrOffset(fullEaInfo, buffer),
sizeof(ULONG));
fullEaInfoLength += fullEaInfo->NextEntryOffset;
}
if (fullEaInfoLength && KphpProgressEaCacheContext(Context))
{
NT_ASSERT(Context->EaCache.FileObject);
NT_ASSERT(fullEaInfo);
fullEaInfo->NextEntryOffset = 0;
status = FsRtlSetKernelEaFile(Context->EaCache.FileObject,
Context->Buffer,
fullEaInfoLength);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
HASH,
"FsRtlSetKernelEaFile failed: %!STATUS!",
status);
}
}
return STATUS_SUCCESS;
}
/**
* \brief Copies hashes from the hashing context to the hash information.
*
* \param[in] Context The hashing context to copy the hashes from.
* \param[in,out] HashInformation The hash information to copy the hashes to.
* \param[in] NumberOfHashes The number of hashing information items.
*/
_IRQL_requires_max_(PASSIVE_LEVEL)
VOID KphpCopyHashes(
_In_ PKPH_HASHING_CONTEXT Context,
_Inout_ PKPH_HASH_INFORMATION HashInformation,
_In_ ULONG NumberOfHashes
)
{
KPH_PAGED_CODE_PASSIVE();
for (ULONG i = 0; i < NumberOfHashes; i++)
{
PKPH_HASHING_ENTRY entry;
PKPH_HASH_INFORMATION info;
info = &HashInformation[i];
NT_ASSERT((info->Algorithm < MaxKphHashAlgorithm) &&
(info->Algorithm >= 0));
entry = &Context->Hash[info->Algorithm];
NT_ASSERT(entry->HashComplete);
info->Length = entry->Length;
RtlCopyMemory(info->Hash, entry->Hash, entry->Length);
}
}
/**
* \brief Hashes a file.
*
* \param[in] FileHandle Handle to a file to hash.
* \param[in,out] HashInformation The hash information to populate with the
* requested hash algorithms. On input this array contains the requested hash
* algorithms to use, and on output the hash information items are populated
* with the requested hash values.
* \param[in] NumberOfHashes The number of hash information items.
* \param[in] AccessMode The mode in which to perform access checks.
*
* \return Successful or errant status.
*/
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KphpHashFile(
_In_ HANDLE FileHandle,
_Inout_ PKPH_HASH_INFORMATION HashInformation,
_In_ ULONG NumberOfHashes,
_In_ KPROCESSOR_MODE AccessMode
)
{
NTSTATUS status;
PKPH_HASHING_CONTEXT context;
PVOID mappedBase;
SIZE_T viewSize;
ULONG readSize;
KPH_PAGED_CODE_PASSIVE();
mappedBase = NULL;
context = KphpAllocateHashingContext();
if (!context)
{
status = STATUS_INSUFFICIENT_RESOURCES;
goto Exit;
}
status = KphpInitializeFileHashingContext(context,
FileHandle,
HashInformation,
NumberOfHashes,
AccessMode);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
HASH,
"KphpInitializeFileHashingContext failed: %!STATUS!",
status);
goto Exit;
}
if (!context->RequiresHashing)
{
goto Finish;
}
viewSize = 0;
status = KphMapViewInSystem(FileHandle,
KPH_MAP_DATA,
&mappedBase,
&viewSize);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
HASH,
"KphMapViewInSystem failed: %!STATUS!",
status);
mappedBase = NULL;
goto Exit;
}
if (context->Hash[KphHashAlgorithmSha1Authenticode].Handle ||
context->Hash[KphHashAlgorithmSha256Authenticode].Handle)
{
__try
{
KphpInitializeAuthenticodeHashing(context, mappedBase, viewSize);
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
status = GetExceptionCode();
goto Exit;
}
}
readSize = 0;
for (SIZE_T offset = 0; offset < viewSize; offset += readSize)
{
PVOID buffer;
buffer = &((PBYTE)mappedBase)[offset];
readSize = (ULONG)min(viewSize - offset, sizeof(context->Buffer));
__try
{
RtlCopyMemory(context->Buffer, buffer, readSize);
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
status = GetExceptionCode();
goto Exit;
}
KphpProgressEaCacheContext(context);
status = KphpUpdateHashes(context, buffer, readSize);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
HASH,
"KphpUpdateHashes failed: %!STATUS!",
status);
goto Exit;
}
}
status = KphpFinishHashes(context);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
HASH,
"KphpFinishHashes failed: %!STATUS!",
status);
goto Exit;
}
Finish:
KphpCopyHashes(context, HashInformation, NumberOfHashes);
Exit:
if (mappedBase)
{
KphUnmapViewInSystem(mappedBase);
}
if (context)
{
KphpFreeHashingContext(context);
}
return status;
}
/**
* \brief Queries hash information for a file.
*
* \param[in] FileHandle Handle to a file to query.
* \param[in,out] HashInformation The hash information to populate with the
* requested hash algorithms. On input this array contains the requested hash
* algorithms to use, and on output the hash information items are populated
* with the requested hash values.
* \param[in] HashInformationLength The length of the hash information in bytes.
* \param[in] AccessMode The mode in which to perform access checks.
*
* \return Successful or errant status.
*/
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KphQueryHashInformationFile(
_In_ HANDLE FileHandle,
_Inout_ PKPH_HASH_INFORMATION HashInformation,
_In_ ULONG HashInformationLength,
_In_ KPROCESSOR_MODE AccessMode
)
{
NTSTATUS status;
PKPH_HASH_INFORMATION hashInfo;
ULONG numberOfHashes;
KPH_PAGED_CODE_PASSIVE();
hashInfo = NULL;
numberOfHashes = HashInformationLength / sizeof(KPH_HASH_INFORMATION);
if (!HashInformation || !numberOfHashes)
{
status = STATUS_INVALID_PARAMETER;
goto Exit;
}
if (AccessMode != KernelMode)
{
hashInfo = KphAllocatePaged(HashInformationLength,
KPH_TAG_CAPTURED_HASHES);
if (!hashInfo)
{
status = STATUS_INSUFFICIENT_RESOURCES;
goto Exit;
}
__try
{
CopyFromUser(hashInfo, HashInformation, HashInformationLength);
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
status = GetExceptionCode();
goto Exit;
}
}
else
{
hashInfo = HashInformation;
}
status = KphpHashFile(FileHandle, hashInfo, numberOfHashes, AccessMode);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
HASH,
"KphpHashFile failed: %!STATUS!",
status);
goto Exit;
}
if (AccessMode != KernelMode)
{
__try
{
CopyToUser(HashInformation, hashInfo, HashInformationLength);
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
status = GetExceptionCode();
goto Exit;
}
}
Exit:
if (hashInfo && (hashInfo != HashInformation))
{
KphFree(hashInfo, KPH_TAG_CAPTURED_HASHES);
}
return status;
}
/**
* \brief Queries hash information for a file object.
*
* \param[in] FileObject The file to query.
* \param[in,out] HashInformation The hash information to populate with the
* requested hash algorithms. On input this array contains the requested hash
* algorithms to use, and on output the hash information items are populated
* with the requested hash values.
* \param[in] HashInformationLength The length of the hash information in bytes.
* \param[in] AccessMode The mode in which to perform access checks.
*
* \return Successful or errant status.
*/
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KphQueryHashInformationFileObject(
_In_ PFILE_OBJECT FileObject,
_Inout_ PKPH_HASH_INFORMATION HashInformation,
_In_ ULONG HashInformationLength
)
{
NTSTATUS status;
HANDLE fileHandle;
KPH_PAGED_CODE_PASSIVE();
status = ObOpenObjectByPointer(FileObject,
OBJ_KERNEL_HANDLE,
NULL,
0,
*IoFileObjectType,
KernelMode,
&fileHandle);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
HASH,
"ObOpenObjectByPointer failed: %!STATUS!",
status);
return status;
}
status = KphQueryHashInformationFile(fileHandle,
HashInformation,
HashInformationLength,
KernelMode);
ObCloseHandle(fileHandle, KernelMode);
return status;
}
================================================
FILE: KSystemInformer/imgcoherency.c
================================================
/*
* Copyright (c) 2022 Winsider Seminars & Solutions, Inc. All rights reserved.
*
* This file is part of System Informer.
*
* Authors:
*
* jxy-s 2024-2026
*
*/
#include
#include
KPH_PAGED_FILE();
/**
* \brief Checks an image for coherency against the data backing the image.
*
* \details Always called from within structured exception handling.
*
* \param[in] ImageBase Base address of the image mapping.
* \param[in] ImageSize Size of the image mapping.
* \param[in] DataBase Base address of the data mapping.
* \param[in] DataSize Size of the data mapping.
*
* \return Successful or errant status.
*/
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KphpCheckImageCoherency(
_In_ PVOID ImageBase,
_In_ SIZE_T ImageSize,
_In_ PVOID DataBase,
_In_ SIZE_T DataSize
)
{
NTSTATUS status;
KPH_MEMORY_REGION regions[4];
KPH_IMAGE_NT_HEADERS image;
SIZE_T size;
PVOID dataEnd;
PVOID imageEnd;
KPH_PAGED_CODE_PASSIVE();
RtlZeroMemory(®ions, sizeof(regions));
regions[0].Start = DataBase;
regions[0].End = Add2Ptr(DataBase, sizeof(IMAGE_DOS_HEADER));
status = KphImageNtHeader(DataBase, DataSize, &image);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
GENERAL,
"KphImageNtHeader failed: %!STATUS!",
status);
return status;
}
size = (image.Headers->FileHeader.NumberOfSections * sizeof(IMAGE_SECTION_HEADER));
regions[1].Start = IMAGE_FIRST_SECTION(image.Headers);
regions[1].End = Add2Ptr(regions[1].Start, size);
regions[2].Start = &image.Headers->FileHeader.NumberOfSections;
if (!RTL_CONTAINS_FIELD(&image.Headers->OptionalHeader,
image.Headers->FileHeader.SizeOfOptionalHeader,
Magic))
{
regions[2].End = &image.Headers->OptionalHeader.Magic;
goto VerifyRegions;
}
if (image.Headers->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR32_MAGIC)
{
if (!RTL_CONTAINS_FIELD(&image.Headers32->OptionalHeader,
image.Headers->FileHeader.SizeOfOptionalHeader,
ImageBase))
{
regions[2].End = Add2Ptr(&image.Headers32->OptionalHeader,
image.Headers->FileHeader.SizeOfOptionalHeader);
goto VerifyRegions;
}
regions[2].End = &image.Headers32->OptionalHeader.ImageBase;
regions[3].Start = &image.Headers32->OptionalHeader.SectionAlignment;
regions[3].End = Add2Ptr(&image.Headers32->OptionalHeader,
image.Headers->FileHeader.SizeOfOptionalHeader);
}
else if (image.Headers->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC)
{
if (!RTL_CONTAINS_FIELD(&image.Headers64->OptionalHeader,
image.Headers->FileHeader.SizeOfOptionalHeader,
ImageBase))
{
regions[2].End = Add2Ptr(&image.Headers64->OptionalHeader,
image.Headers->FileHeader.SizeOfOptionalHeader);
goto VerifyRegions;
}
regions[2].End = &image.Headers64->OptionalHeader.ImageBase;
regions[3].Start = &image.Headers64->OptionalHeader.SectionAlignment;
regions[3].End = Add2Ptr(&image.Headers64->OptionalHeader,
image.Headers->FileHeader.SizeOfOptionalHeader);
}
else
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
GENERAL,
"Invalid image optional header magic: 0x%04x",
image.Headers->OptionalHeader.Magic);
return STATUS_INVALID_IMAGE_FORMAT;
}
VerifyRegions:
imageEnd = Add2Ptr(ImageBase, ImageSize);
dataEnd = Add2Ptr(DataBase, DataSize);
for (ULONG i = 0; i < ARRAYSIZE(regions); i++)
{
PKPH_MEMORY_REGION region;
KPH_MEMORY_REGION other;
region = ®ions[i];
if (!region->Start)
{
break;
}
if (!KphIsValidMemoryRegion(region, DataBase, dataEnd))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
GENERAL,
"Image region overflows mapping.");
return STATUS_BUFFER_OVERFLOW;
}
other.Start = RebasePtr(region->Start, DataBase, ImageBase);
other.End = RebasePtr(region->End, DataBase, ImageBase);
if (!KphIsValidMemoryRegion(&other, ImageBase, imageEnd))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
GENERAL,
"Data region overflows mapping.");
return STATUS_BUFFER_OVERFLOW;
}
size = PtrOffset(region->Start, region->End);
if (!KphEqualMemory(region->Start, other.Start, size))
{
return STATUS_IMAGE_CHECKSUM_MISMATCH;
}
}
return STATUS_SUCCESS;
}
/**
* \brief Checks an image for coherency against the data backing the image.
*
* \param[in] ImageBase Base address of the image mapping.
* \param[in] ImageSize Size of the image mapping.
* \param[in] DataBase Base address of the data mapping.
* \param[in] DataSize Size of the data mapping.
*
* \return Successful or errant status.
*/
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KphCheckImageCoherency(
_In_ PVOID ImageBase,
_In_ SIZE_T ImageSize,
_In_ PVOID DataBase,
_In_ SIZE_T DataSize
)
{
NTSTATUS status;
KPH_PAGED_CODE_PASSIVE();
__try
{
status = KphpCheckImageCoherency(ImageBase,
ImageSize,
DataBase,
DataSize);
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
status = GetExceptionCode();
}
return status;
}
================================================
FILE: KSystemInformer/include/comms.h
================================================
/*
* Copyright (c) 2022 Winsider Seminars & Solutions, Inc. All rights reserved.
*
* This file is part of System Informer.
*
* Authors:
*
* jxy-s 2022-2026
*
*/
#pragma once
#include
typedef struct _KPH_CLIENT_INFORMER_STATE
{
KPH_MESSAGE_TIMEOUTS MessageTimeouts;
KPH_RATE_LIMIT AsyncQueueRateLimit;
KPH_RATE_LIMIT InformerRateLimit[KPH_INFORMER_COUNT];
} KPH_CLIENT_INFORMER_STATE, *PKPH_CLIENT_INFORMER_STATE;
typedef union _KPH_CLIENT_INFORMER_STATE_ATOMIC
{
struct _KPH_CLIENT_RATE_LIMITS* Limits;
KPH_ATOMIC_OBJECT_REF Atomic;
} KPH_CLIENT_INFORMER_STATE_ATOMIC, *PKPH_CLIENT_INFORMER_STATE_ATOMIC;
typedef struct _KPH_CLIENT
{
PKPH_PROCESS_CONTEXT Process;
PFLT_PORT Port;
KPH_REFERENCE DriverUnloadProtectionRef;
KPH_CLIENT_INFORMER_STATE_ATOMIC InformerState;
PKPH_RING_BUFFER RingBuffer;
} KPH_CLIENT, *PKPH_CLIENT;
typedef
_Function_class_(KPHM_HANDLER)
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS
KSIAPI
KPHM_HANDLER (
_In_ PKPH_CLIENT Client,
_Inout_ PKPH_MESSAGE Message
);
typedef KPHM_HANDLER* PKPHM_HANDLER;
#define KPHM_DEFINE_HANDLER(name) \
_Function_class_(KPHM_HANDLER) \
_IRQL_requires_max_(PASSIVE_LEVEL) \
_Must_inspect_result_ \
NTSTATUS name( \
_In_ PKPH_CLIENT Client, \
_Inout_ PKPH_MESSAGE Message \
)
typedef
_Function_class_(KPHM_REQUIRED_STATE)
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
KPH_PROCESS_STATE
KSIAPI
KPHM_REQUIRED_STATE (
_In_ PKPH_CLIENT Client,
_In_ PCKPH_MESSAGE Message
);
typedef KPHM_REQUIRED_STATE* PKPHM_REQUIRED_STATE;
#define KPHM_DEFINE_REQUIRED_STATE(name) \
_Function_class_(KPHM_REQUIRED_STATE) \
_IRQL_requires_max_(PASSIVE_LEVEL) \
_Must_inspect_result_ \
KPH_PROCESS_STATE name( \
_In_ PKPH_CLIENT Client, \
_In_ PCKPH_MESSAGE Message \
)
typedef struct _KPH_MESSAGE_HANDLER
{
KPH_MESSAGE_ID MessageId;
PKPHM_HANDLER Handler;
PKPHM_REQUIRED_STATE RequiredState;
} KPH_MESSAGE_HANDLER, *PKPH_MESSAGE_HANDLER;
extern const KPH_MESSAGE_HANDLER KphCommsMessageHandlers[];
extern const ULONG KphCommsMessageHandlerCount;
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KphCommsStart(
VOID
);
_IRQL_requires_max_(PASSIVE_LEVEL)
VOID KphCommsStop(
VOID
);
_IRQL_requires_max_(DISPATCH_LEVEL)
ULONG KphGetConnectedClientCount(
VOID
);
_IRQL_requires_max_(DISPATCH_LEVEL)
ULONG KphGetInformerClientCount(
VOID
);
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KphGetInformerClientSettings(
_In_ PKPH_CLIENT Client,
_Out_ PKPH_INFORMER_CLIENT_SETTINGS Settings
);
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KphSetInformerClientSettings(
_In_ PKPH_CLIENT Client,
_In_ PKPH_INFORMER_CLIENT_SETTINGS Settings
);
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KphGetInformerClientStats(
_In_ PKPH_CLIENT Client,
_Out_ PKPH_INFORMER_CLIENT_STATS Stats
);
_IRQL_requires_max_(APC_LEVEL)
_Return_allocatesMem_
PKPH_MESSAGE KphAllocateMessage(
VOID
);
_IRQL_requires_max_(APC_LEVEL)
VOID KphFreeMessage(
_In_freesMem_ PKPH_MESSAGE Message
);
_IRQL_requires_max_(DISPATCH_LEVEL)
_Return_allocatesMem_
PKPH_MESSAGE KphAllocateNPagedMessage(
VOID
);
_IRQL_requires_max_(DISPATCH_LEVEL)
VOID KphFreeNPagedMessage(
_In_freesMem_ PKPH_MESSAGE Message
);
_IRQL_requires_max_(APC_LEVEL)
_Must_inspect_result_
NTSTATUS KphCommsSendMessage(
_In_ PKPH_MESSAGE Message,
_Out_opt_ PKPH_MESSAGE Reply
);
_IRQL_requires_max_(APC_LEVEL)
VOID KphCommsSendMessageAsync(
_In_aliasesMem_ PKPH_MESSAGE Message
);
_IRQL_requires_max_(DISPATCH_LEVEL)
VOID KphCommsSendNPagedMessageAsync(
_In_aliasesMem_ PKPH_MESSAGE Message
);
_IRQL_requires_max_(DISPATCH_LEVEL)
VOID KphCaptureStackInMessage(
_Inout_ PKPH_MESSAGE Message
);
================================================
FILE: KSystemInformer/include/informer.h
================================================
/*
* Copyright (c) 2022 Winsider Seminars & Solutions, Inc. All rights reserved.
*
* This file is part of System Informer.
*
* Authors:
*
* jxy-s 2022-2026
*
*/
#pragma once
#include
#include
_IRQL_requires_max_(PASSIVE_LEVEL)
VOID KphCleanupInformer(
VOID
);
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KphInitializeInformer(
VOID
);
_IRQL_requires_max_(DISPATCH_LEVEL)
BOOLEAN KphInformerAllowed(
_In_ ULONG Index,
_In_opt_ PKPH_PROCESS_CONTEXT ActorProcess,
_In_opt_ PKPH_PROCESS_CONTEXT TargetProcess
);
#define KphInformerEnabled(name, proc) \
KphInformerAllowed(KPH_INFORMER_INDEX(name), proc, NULL)
#define KphInformerEnabled2(name, actor, target) \
KphInformerAllowed(KPH_INFORMER_INDEX(name), actor, target)
_IRQL_requires_max_(DISPATCH_LEVEL)
KPH_INFORMER_OPTIONS KphInformerOptions(
_In_opt_ PKPH_PROCESS_CONTEXT ActorProcess,
_In_opt_ PKPH_PROCESS_CONTEXT TargetProcess
);
#define KphInformerOpts(proc) \
KphInformerOptions(proc, NULL)
#define KphInformerOpts2(proc, target) \
KphInformerOptions(proc, target)
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KphGetInformerSettings(
_Out_ PKPH_INFORMER_SETTINGS Settings,
_In_ KPROCESSOR_MODE AccessMode
);
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KphSetInformerSettings(
_In_ PKPH_INFORMER_SETTINGS Settings,
_In_ KPROCESSOR_MODE AccessMode
);
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KphGetInformerProcessSettings(
_In_ HANDLE ProcessHandle,
_Out_ PKPH_INFORMER_SETTINGS Settings,
_In_ KPROCESSOR_MODE AccessMode
);
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KphSetInformerProcessSettings(
_In_opt_ HANDLE ProcessHandle,
_In_ PKPH_INFORMER_SETTINGS Settings,
_In_ KPROCESSOR_MODE AccessMode
);
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KphGetInformerStats(
_In_opt_ HANDLE ProcessHandle,
_Out_ PKPH_INFORMER_STATS Stats,
_In_ KPROCESSOR_MODE AccessMode
);
// mini-filter informer
extern PFLT_FILTER KphFltFilter;
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KphFltRegister(
_In_ PDRIVER_OBJECT DriverObject,
_In_ PUNICODE_STRING RegistryPath
);
_IRQL_requires_max_(PASSIVE_LEVEL)
VOID KphFltUnregister(
VOID
);
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KphFltInformerStart(
VOID
);
// process informer
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KphProcessInformerStart(
VOID
);
_IRQL_requires_max_(PASSIVE_LEVEL)
VOID KphProcessInformerStop(
VOID
);
// thread informer
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KphThreadInformerStart(
VOID
);
_IRQL_requires_max_(PASSIVE_LEVEL)
VOID KphThreadInformerStop(
VOID
);
// image informer
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KphImageInformerStart(
VOID
);
_IRQL_requires_max_(PASSIVE_LEVEL)
VOID KphImageInformerStop(
VOID
);
// object informer
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KphObjectInformerStart(
VOID
);
_IRQL_requires_max_(PASSIVE_LEVEL)
VOID KphObjectInformerStop(
VOID
);
// registry informer
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KphRegistryInformerStart(
VOID
);
_IRQL_requires_max_(PASSIVE_LEVEL)
VOID KphRegistryInformerStop(
VOID
);
// debug informer
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KphDebugInformerStart(
VOID
);
_IRQL_requires_max_(PASSIVE_LEVEL)
VOID KphDebugInformerStop(
VOID
);
================================================
FILE: KSystemInformer/include/informer_filep.h
================================================
/*
* Copyright (c) 2022 Winsider Seminars & Solutions, Inc. All rights reserved.
*
* This file is part of System Informer.
*
* Authors:
*
* jxy-s 2023-2026
*
*/
#pragma once
// informer_fileop.c
_Function_class_(PFLT_POST_OPERATION_CALLBACK)
_IRQL_requires_max_(DISPATCH_LEVEL)
FLT_POSTOP_CALLBACK_STATUS FLTAPI KphpFltPostOp(
_Inout_ PFLT_CALLBACK_DATA Data,
_In_ PCFLT_RELATED_OBJECTS FltObjects,
_In_opt_ PVOID CompletionContext,
_In_ FLT_POST_OPERATION_FLAGS Flags
);
_Function_class_(PFLT_PRE_OPERATION_CALLBACK)
_IRQL_requires_max_(APC_LEVEL)
FLT_PREOP_CALLBACK_STATUS FLTAPI KphpFltPreOp(
_Inout_ PFLT_CALLBACK_DATA Data,
_In_ PCFLT_RELATED_OBJECTS FltObjects,
_Outptr_result_maybenull_ PVOID* CompletionContext
);
_IRQL_requires_max_(PASSIVE_LEVEL)
VOID KphpFltCleanupFileOp(
VOID
);
_IRQL_requires_max_(PASSIVE_LEVEL)
VOID KphpFltInitializeFileOp(
VOID
);
// informer_filenc.c
typedef enum _KPH_FLT_FILE_NAME_TYPE
{
KphFltFileNameTypeNone,
KphFltFileNameTypeNameInfo,
KphFltFileNameTypeContext,
KphFltFileNameTypeFileName,
KphFltFileNameTypeNameCache,
} KPH_FLT_FILE_NAME_TYPE, *PKPH_FLT_FILE_NAME_ITYPE;
typedef struct _KPH_FLT_FILE_NAME_CACHE_ENTRY
{
LIST_ENTRY ListEntry;
PFILE_OBJECT FileObject;
UNICODE_STRING FileName;
} KPH_FLT_FILE_NAME_CACHE_ENTRY, *PKPH_FLT_FILE_NAME_CACHE_ENTRY;
typedef struct _KPH_FLT_FILE_NAME
{
//
// N.B. PFLT_CONTEXT stores a PUNICODE_STRING
//
KPH_FLT_FILE_NAME_TYPE Type;
union
{
PFLT_FILE_NAME_INFORMATION NameInfo;
PFLT_CONTEXT Context;
PUNICODE_STRING FileName;
PKPH_FLT_FILE_NAME_CACHE_ENTRY NameCache;
};
} KPH_FLT_FILE_NAME, *PKPH_FLT_FILE_NAME;
#define KphpFltZeroFileName(info) \
(info)->Type = KphFltFileNameTypeNone; \
(info)->NameInfo = NULL;
_IRQL_requires_max_(APC_LEVEL)
_Must_inspect_result_
NTSTATUS KphpFltGetFileName(
_In_ PFLT_CALLBACK_DATA Data,
_In_ PCFLT_RELATED_OBJECTS FltObjects,
_Out_ PKPH_FLT_FILE_NAME FltFileName
);
_IRQL_requires_max_(APC_LEVEL)
_Must_inspect_result_
NTSTATUS KphpFltGetDestFileName(
_In_ PFLT_CALLBACK_DATA Data,
_In_ PCFLT_RELATED_OBJECTS FltObjects,
_Out_ PKPH_FLT_FILE_NAME FltDestFileName
);
_IRQL_requires_max_(APC_LEVEL)
VOID KphpFltReleaseFileName(
_In_ PKPH_FLT_FILE_NAME FltFileName
);
_IRQL_requires_max_(DISPATCH_LEVEL)
VOID KphpFltReapFileNameCache(
_In_ PFLT_CALLBACK_DATA Data,
_In_ PCFLT_RELATED_OBJECTS FltObjects
);
_IRQL_requires_max_(PASSIVE_LEVEL)
VOID KphpFltCleanupFileNameCache(
VOID
);
_IRQL_requires_max_(PASSIVE_LEVEL)
VOID KphpFltInitializeFileNameCache(
VOID
);
================================================
FILE: KSystemInformer/include/kph.h
================================================
/*
* Copyright (c) 2022 Winsider Seminars & Solutions, Inc. All rights reserved.
*
* This file is part of System Informer.
*
* Authors:
*
* jxy-s 2022-2026
*
*/
#pragma once
#pragma warning(push)
#pragma warning(disable: 5103) // invalid preprocessing token (/Zc:preprocessor)
#include
#include
#include
#include
#include
#include
#include
#include
#pragma warning(pop)
#include
#define PHNT_MODE PHNT_MODE_KERNEL
#include
#include
#include
#include
#include
#include
#include
#define KSIAPI NTAPI
#define KPH_PAGED_CODE() \
PAGED_CODE(); \
NT_ANALYSIS_ASSUME((KeGetCurrentIrql() == APC_LEVEL) || \
(KeGetCurrentIrql() == PASSIVE_LEVEL))
#define KPH_PAGED_CODE_PASSIVE() \
PAGED_CODE(); \
NT_ASSERT(KeGetCurrentIrql() == PASSIVE_LEVEL); \
NT_ANALYSIS_ASSUME(KeGetCurrentIrql() == PASSIVE_LEVEL)
#define KPH_PAGED_CODE_APC() \
PAGED_CODE(); \
NT_ASSERT(KeGetCurrentIrql() == APC_LEVEL); \
NT_ANALYSIS_ASSUME(KeGetCurrentIrql() == APC_LEVEL)
#define KPH_NPAGED_CODE_PASSIVE() \
NT_ASSERT(KeGetCurrentIrql() == PASSIVE_LEVEL); \
NT_ANALYSIS_ASSUME(KeGetCurrentIrql() == PASSIVE_LEVEL)
#define KPH_NPAGED_CODE_APC_MAX() \
NT_ASSERT(KeGetCurrentIrql() <= APC_LEVEL); \
NT_ANALYSIS_ASSUME((KeGetCurrentIrql() == APC_LEVEL) || \
(KeGetCurrentIrql() == PASSIVE_LEVEL))
#define KPH_NPAGED_CODE_DISPATCH_MAX() \
NT_ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL); \
NT_ANALYSIS_ASSUME((KeGetCurrentIrql() == DISPATCH_LEVEL) || \
(KeGetCurrentIrql() == APC_LEVEL) || \
(KeGetCurrentIrql() == PASSIVE_LEVEL))
#define KPH_NPAGED_CODE_DISPATCH_MIN() \
NT_ASSERT(KeGetCurrentIrql() >= DISPATCH_LEVEL); \
NT_ANALYSIS_ASSUME((KeGetCurrentIrql() == DISPATCH_LEVEL) || \
(KeGetCurrentIrql() == HIGH_LEVEL))
#define KPH_NPAGED_CODE_DISPATCH() \
NT_ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL); \
NT_ANALYSIS_ASSUME(KeGetCurrentIrql() == DISPATCH_LEVEL)
//
// N.B. This decorates code to indicate that the code supports up to APC_LEVEL
// but is in a non-paged segment since it can be called in a paging I/O path.
// Code in this path should also only allocate from non-paged pool to avoid
// deadlocks.
//
#define KPH_NPAGED_CODE_APC_MAX_FOR_PAGING_IO() KPH_NPAGED_CODE_APC_MAX()
#define KPH_PAGED_FILE() \
__pragma(bss_seg("PAGEBBS")) \
__pragma(code_seg("PAGE")) \
__pragma(data_seg("PAGEDATA")) \
__pragma(const_seg("PAGERO"))
#define KPH_PROTECTED_DATA_SECTION_PUSH() \
__pragma(data_seg(push)) \
__pragma(data_seg("KSIDATA"))
#define KPH_PROTECTED_DATA_SECTION_POP() \
__pragma(data_seg(pop))
#define KPH_PROTECTED_DATA_SECTION_RO_PUSH() \
__pragma(const_seg(push)) \
__pragma(const_seg("KSIRO"))
#define KPH_PROTECTED_DATA_SECTION_RO_POP() \
__pragma(const_seg(pop))
#define _Outptr_allocatesMem_ _Outptr_result_nullonfailure_ __drv_allocatesMem(Mem)
#define _Out_allocatesMem_ _Out_ __drv_allocatesMem(Mem)
#define _Out_allocatesMem_size_(size) _Out_allocatesMem_ _Post_writable_byte_size_(size)
#define _FreesMem_ _Pre_notnull_ _Post_ptr_invalid_ __drv_freesMem(Mem)
#define _In_freesMem_ _In_ _FreesMem_
#define _In_aliasesMem_ _In_ _Pre_notnull_ _Post_ptr_invalid_ __drv_aliasesMem
#define _Return_allocatesMem_ __drv_allocatesMem(Mem) _Check_return_ _Ret_maybenull_
#define _Return_allocatesMem_size_(size) _Return_allocatesMem_ _Post_writable_byte_size_(size)
#define _IRQL_requires_for_wait_(timeout) \
_When_(((timeout == NULL) || (timeout->QuadPart != 0)), \
_IRQL_requires_max_(APC_LEVEL)) \
_When_(((timeout != NULL) && (timeout->QuadPart == 0)), \
_IRQL_requires_max_(DISPATCH_LEVEL))
typedef const void* PCVOID;
#ifndef MAX_PATH
#define MAX_PATH 260
#endif
FORCEINLINE
ULONG64 InterlockedExchangeU64(
_Inout_ _Interlocked_operand_ volatile ULONG64* Target,
_In_ ULONG64 Value
)
{
return (ULONG64)InterlockedExchange64((LONG64*)Target, (LONG64)Value);
}
FORCEINLINE
ULONG64 InterlockedCompareExchangeU64(
_Inout_ _Interlocked_operand_ volatile ULONG64* Target,
_In_ ULONG64 Value,
_In_ ULONG64 Expected
)
{
return (ULONG64)InterlockedCompareExchange64((LONG64*)Target,
(LONG64)Value,
(LONG64)Expected);
}
FORCEINLINE
ULONG64 InterlockedIncrementU64(
_Inout_ _Interlocked_operand_ volatile ULONG64* Target
)
{
return (ULONG64)InterlockedIncrement64((LONG64*)Target);
}
FORCEINLINE
ULONG_PTR InterlockedExchangeULongPtr(
_Inout_ _Interlocked_operand_ volatile ULONG_PTR* Target,
_In_ ULONG_PTR Value
)
{
#ifdef _WIN64
return (ULONG_PTR)InterlockedExchange64((LONG64*)Target, (LONG64)Value);
#else
return (ULONG_PTR)InterlockedExchange((LONG*)Target, (LONG)Value);
#endif
}
FORCEINLINE
ULONG_PTR InterlockedExchangeAddULongPtr(
_Inout_ _Interlocked_operand_ volatile ULONG_PTR* Target,
_In_ ULONG_PTR Value
)
{
return (ULONG_PTR)InterlockedExchangeAddSizeT((SIZE_T*)Target, (SIZE_T)Value);
}
FORCEINLINE
BOOLEAN InterlockedBitTestAndResetULongPtr(
_Inout_ _Interlocked_operand_ volatile ULONG_PTR* Target,
_In_ ULONG_PTR Bit
)
{
#ifdef _WIN64
return InterlockedBitTestAndReset64((LONG64*)Target, (LONG64)Bit);
#else
return InterlockedBitTestAndReset((LONG*)Target, (LONG)Bit);
#endif
}
FORCEINLINE
ULONG_PTR InterlockedCompareExchangeULongPtr(
_Inout_ _Interlocked_operand_ volatile ULONG_PTR* Target,
_In_ ULONG_PTR Value,
_In_ ULONG_PTR Expected
)
{
#ifdef _WIN64
return (ULONG_PTR)InterlockedCompareExchange64((LONG64*)Target,
(LONG64)Value,
(LONG64)Expected);
#else
return (ULONG_PTR)InterlockedCompareExchange((LONG*)Target,
(LONG)Value,
(LONG)Expected);
#endif
}
FORCEINLINE
ULONG_PTR InterlockedDecrementULongPtr(
_Inout_ _Interlocked_operand_ volatile ULONG_PTR* Target
)
{
return (ULONG_PTR)InterlockedDecrementSizeT((SIZE_T*)Target);
}
FORCEINLINE
SSIZE_T InterlockedIncrementSSizeT(
_Inout_ _Interlocked_operand_ volatile SSIZE_T* Target
)
{
return (SSIZE_T)InterlockedIncrementSizeT((SIZE_T*)Target);
}
FORCEINLINE
VOID WriteSSizeTNoFence(
_Out_ _Interlocked_operand_ volatile SSIZE_T* Target,
_In_ SSIZE_T Value
)
{
WriteSizeTNoFence((SIZE_T*)Target, (SIZE_T)Value);
}
FORCEINLINE
SSIZE_T InterlockedIncrementSSizeTNoFence(
_Inout_ _Interlocked_operand_ volatile SSIZE_T* Target
)
{
return (SSIZE_T)InterlockedIncrementSizeTNoFence((SIZE_T*)Target);
}
FORCEINLINE
SSIZE_T InterlockedDecrementSSizeT(
_Inout_ _Interlocked_operand_ volatile SSIZE_T* Target
)
{
return (SSIZE_T)InterlockedDecrementSizeT((SIZE_T*)Target);
}
FORCEINLINE
SIZE_T InterlockedCompareExchangeSizeT(
_Inout_ _Interlocked_operand_ volatile SIZE_T* Target,
_In_ SIZE_T Value,
_In_ SIZE_T Expected
)
{
return (SIZE_T)InterlockedCompareExchangePointer((PVOID*)Target,
(PVOID)Value,
(PVOID)Expected);
}
FORCEINLINE
VOID InterlockedExchangeIfGreaterSizeT(
_Inout_ _Interlocked_operand_ volatile SIZE_T* Target,
_In_ SIZE_T Value
)
{
SIZE_T max;
max = ReadSizeTAcquire(Target);
while (Value > max)
{
SIZE_T expected;
expected = max;
max = InterlockedCompareExchangeSizeT(Target, Value, expected);
if (max == expected)
{
break;
}
}
}
//
// Use user-mode accessors (UMA) APIs instead.
//
#pragma deprecated("ProbeForRead")
#pragma deprecated("ProbeForWrite")
#define C_2sTo4(x) ((unsigned int)(signed short)(x))
#define RebasePtr(pointer, oldBase, newBase) \
Add2Ptr(newBase, PtrOffset(oldBase, pointer))
#define RebaseUnicodeString(string, oldBase, newBase) \
if ((string)->Buffer) \
{ \
(string)->Buffer = Add2Ptr(newBase, PtrOffset(oldBase, (string)->Buffer)); \
}
#define KPH_TIMEOUT(ms) { .QuadPart = (-10000ll * (ms)) }
#define KPH_KERNEL_PURGE_EA "$KERNEL.PURGE.SI."
typedef struct _KPH_FILE_VERSION
{
USHORT MajorVersion;
USHORT MinorVersion;
USHORT BuildNumber;
USHORT Revision;
} KPH_FILE_VERSION, *PKPH_FILE_VERSION;
typedef struct _KPH_MEMORY_REGION
{
PVOID Start;
PVOID End;
} KPH_MEMORY_REGION, *PKPH_MEMORY_REGION;
_Must_inspect_result_
FORCEINLINE
BOOLEAN KphIsValidMemoryRegion(
_In_ PKPH_MEMORY_REGION Region,
_In_ PVOID Start,
_In_ PVOID End
)
{
return ((Region->Start <= Region->End) &&
(Region->Start >= Start) &&
(Region->End <= End));
}
//
// C30030 - Probably allocating executable memory via specifying a
// MM_PAGE_PRIORITY type without a bitwise OR with MdlMappingNoExecute
//
#pragma deprecated("MmGetSystemAddressForMdl")
#pragma deprecated("MmGetSystemAddressForMdlSafe")
_IRQL_requires_max_(DISPATCH_LEVEL)
_Post_writable_byte_size_(Mdl->ByteCount)
_At_(Mdl->MappedSystemVa, _Post_writable_byte_size_(Mdl->ByteCount))
_Check_return_
_Success_(return != NULL)
FORCEINLINE
PVOID KphpGetSystemAddressForMdl(
_Inout_ PMDL Mdl,
_In_ MM_PAGE_PRIORITY Priority
)
{
#pragma warning(suppress: 4995) // suppress deprecation warning
return MmGetSystemAddressForMdlSafe(Mdl, Priority | MdlMappingNoExecute);
}
#define KphGetSystemAddressForMdl(mdl, priority) \
_Pragma("warning(push)") \
_Pragma("warning(disable : 30030)") \
KphpGetSystemAddressForMdl(mdl, priority) \
_Pragma("warning(pop)")
#pragma deprecated("ObReferenceObjectByHandle")
_IRQL_requires_max_(APC_LEVEL)
_Must_inspect_result_
FORCEINLINE
NTSTATUS KphpObReferenceObjectByHandle(
_In_ HANDLE Handle,
_In_ ACCESS_MASK DesiredAccess,
_In_opt_ POBJECT_TYPE ObjectType,
_In_ KPROCESSOR_MODE AccessMode,
_Out_ PVOID *Object,
_Out_opt_ POBJECT_HANDLE_INFORMATION HandleInformation
)
{
#pragma warning(disable: 4995) // suppress deprecation warning
#pragma prefast(suppress: 28118) // allow at APC_LEVEL
return ObReferenceObjectByHandle(Handle,
DesiredAccess,
ObjectType,
AccessMode,
Object,
HandleInformation);
}
#define ObReferenceObjectByHandle KphpObReferenceObjectByHandle
// main
extern PDRIVER_OBJECT KphDriverObject;
extern RTL_OSVERSIONINFOEXW KphOsVersionInfo;
extern KPH_FILE_VERSION KphKernelVersion;
extern BOOLEAN KphIgnoreProtectionsSuppressed;
extern BOOLEAN KphIgnoreTestSigningEnabled;
extern SYSTEM_SECUREBOOT_INFORMATION KphSecureBootInfo;
extern SYSTEM_CODEINTEGRITY_INFORMATION KphCodeIntegrityInfo;
FORCEINLINE
BOOLEAN KphInDeveloperMode(
VOID
)
{
if (KphSecureBootInfo.SecureBootCapable &&
KphSecureBootInfo.SecureBootEnabled)
{
return FALSE;
}
if (!KD_DEBUGGER_ENABLED)
{
return FALSE;
}
if (!FlagOn(KphCodeIntegrityInfo.CodeIntegrityOptions,
CODEINTEGRITY_OPTION_TESTSIGN))
{
return FALSE;
}
return TRUE;
}
FORCEINLINE
BOOLEAN KphProtectionsSuppressed(
VOID
)
{
if (KphIgnoreProtectionsSuppressed)
{
return FALSE;
}
return KphInDeveloperMode();
}
FORCEINLINE
BOOLEAN KphTestSigningEnabled(
VOID
)
{
if (KphIgnoreTestSigningEnabled)
{
return FALSE;
}
return KphInDeveloperMode();
}
// parameters
extern PUNICODE_STRING KphAltitude;
extern PUNICODE_STRING KphPortName;
extern KPH_PARAMETER_FLAGS KphParameterFlags;
extern PUNICODE_STRING KphSystemProcessName;
_IRQL_requires_max_(PASSIVE_LEVEL)
VOID KphCleanupParameters(
VOID
);
_IRQL_requires_max_(PASSIVE_LEVEL)
VOID KphInitializeParameters(
_In_ PCUNICODE_STRING RegistryPath
);
// alloc
//
// Always use wrapped allocators, unless explicitly necessary.
//
// This helps catch programmer error. Deprecate the original ones here, the
// allocation infrastructure will internally suppress the deprecated warnings.
//
#pragma deprecated("ExAllocateCacheAwareRundownProtection")
#pragma deprecated("ExAllocateFromLookasideListEx")
#pragma deprecated("ExAllocateFromNPagedLookasideList")
#pragma deprecated("ExAllocateFromPagedLookasideList")
#pragma deprecated("ExAllocatePool")
#pragma deprecated("ExAllocatePool2")
#pragma deprecated("ExAllocatePool3")
#pragma deprecated("ExAllocatePoolPriorityUninitialized")
#pragma deprecated("ExAllocatePoolPriorityZero")
#pragma deprecated("ExAllocatePoolQuotaUninitialized")
#pragma deprecated("ExAllocatePoolQuotaZero")
#pragma deprecated("ExAllocatePoolUninitialized")
#pragma deprecated("ExAllocatePoolWithQuota")
#pragma deprecated("ExAllocatePoolWithQuotaTag")
#pragma deprecated("ExAllocatePoolWithTag")
#pragma deprecated("ExAllocatePoolWithTagPriority")
#pragma deprecated("ExAllocatePoolZero")
#pragma deprecated("ExFreePool")
#pragma deprecated("ExFreePool2")
#pragma deprecated("ExFreePoolWithTag")
#pragma deprecated("ExFreeToLookasideListEx")
#pragma deprecated("ExFreeToNPagedLookasideList")
#pragma deprecated("ExFreeToPagedLookasideList")
#pragma deprecated("ExDeleteLookasideListEx")
#pragma deprecated("ExDeleteNPagedLookasideList")
#pragma deprecated("ExDeletePagedLookasideList")
#pragma deprecated("ExInitializeLookasideListEx")
#pragma deprecated("ExInitializeNPagedLookasideList")
#pragma deprecated("ExInitializePagedLookasideList")
FORCEINLINE
VOID KphFreePool(
_FreesMem_ PVOID Memory
)
{
#pragma warning(suppress: 4995) // intentional use of ExFreePool
ExFreePoolWithTag(Memory, 0);
}
_IRQL_requires_max_(PASSIVE_LEVEL)
VOID KphInitializeAlloc(
VOID
);
_IRQL_requires_max_(DISPATCH_LEVEL)
_Return_allocatesMem_size_(NumberOfBytes)
PVOID KphAllocateNPaged(
_In_ SIZE_T NumberOfBytes,
_In_ ULONG Tag
);
_IRQL_requires_max_(APC_LEVEL)
_Return_allocatesMem_size_(NumberOfBytes)
PVOID KphAllocatePaged(
_In_ SIZE_T NumberOfBytes,
_In_ ULONG Tag
);
_IRQL_requires_max_(DISPATCH_LEVEL)
VOID KphFree(
_FreesMem_ PVOID Memory,
_In_ ULONG Tag
);
#pragma prefast(push)
#pragma prefast(disable: 28195) // acquire memory without doing so
_IRQL_requires_max_(DISPATCH_LEVEL)
_Return_allocatesMem_size_(NumberOfBytes)
FORCEINLINE
PVOID KphpAllocateNPagedA(
_In_ SIZE_T NumberOfBytes,
_In_ ULONG Tag,
_Out_writes_bytes_(SizeOfStack) PBYTE Stack,
_In_ ULONG SizeOfStack
)
{
if (NumberOfBytes <= SizeOfStack)
{
RtlZeroMemory(Stack, SizeOfStack);
return Stack;
}
return KphAllocateNPaged(NumberOfBytes, Tag);
}
#pragma prefast(pop)
#define KphAllocateNPagedA(NumberOfBytes, Tag, Stack) \
KphpAllocateNPagedA(NumberOfBytes, Tag, Stack, sizeof(Stack))
#pragma prefast(push)
#pragma prefast(disable: 28195) // acquire memory without doing so
_IRQL_requires_max_(APC_LEVEL)
_Return_allocatesMem_size_(NumberOfBytes)
FORCEINLINE
PVOID KphpAllocatePagedA(
_In_ SIZE_T NumberOfBytes,
_In_ ULONG Tag,
_Out_writes_bytes_(SizeOfStack) PBYTE Stack,
_In_ ULONG SizeOfStack
)
{
if (NumberOfBytes <= SizeOfStack)
{
RtlZeroMemory(Stack, SizeOfStack);
return Stack;
}
return KphAllocatePaged(NumberOfBytes, Tag);
}
#pragma prefast(pop)
#define KphAllocatePagedA(NumberOfBytes, Tag, Stack) \
KphpAllocatePagedA(NumberOfBytes, Tag, Stack, sizeof(Stack))
#pragma prefast(push)
#pragma prefast(disable: 6014) // leaking memory
_IRQL_requires_max_(DISPATCH_LEVEL)
FORCEINLINE
VOID KphpFreeA(
_FreesMem_ PVOID Memory,
_In_ ULONG Tag,
_In_ PBYTE Stack
)
{
if (Memory != Stack)
{
KphFree(Memory, Tag);
}
}
#pragma prefast(pop)
#define KphFreeA(Memory, Tag, Stack) KphpFreeA(Memory, Tag, Stack)
_IRQL_requires_max_(DISPATCH_LEVEL)
VOID KphInitializeNPagedLookaside(
_Out_ PNPAGED_LOOKASIDE_LIST Lookaside,
_In_ SIZE_T Size,
_In_ ULONG Tag
);
_IRQL_requires_max_(DISPATCH_LEVEL)
VOID KphDeleteNPagedLookaside(
_Inout_ PNPAGED_LOOKASIDE_LIST Lookaside
);
_IRQL_requires_max_(DISPATCH_LEVEL)
_Return_allocatesMem_size_(Lookaside->L.Size)
PVOID KphAllocateFromNPagedLookaside(
_Inout_ PNPAGED_LOOKASIDE_LIST Lookaside
);
_IRQL_requires_max_(DISPATCH_LEVEL)
VOID KphFreeToNPagedLookaside(
_Inout_ PNPAGED_LOOKASIDE_LIST Lookaside,
_In_freesMem_ PVOID Memory
);
_IRQL_requires_max_(APC_LEVEL)
VOID KphInitializePagedLookaside(
_Out_ PPAGED_LOOKASIDE_LIST Lookaside,
_In_ SIZE_T Size,
_In_ ULONG Tag
);
_IRQL_requires_max_(APC_LEVEL)
VOID KphDeletePagedLookaside(
_Inout_ PPAGED_LOOKASIDE_LIST Lookaside
);
_IRQL_requires_max_(APC_LEVEL)
_Return_allocatesMem_size_(Lookaside->L.Size)
PVOID KphAllocateFromPagedLookaside(
_Inout_ PPAGED_LOOKASIDE_LIST Lookaside
);
_IRQL_requires_max_(APC_LEVEL)
VOID KphFreeToPagedLookaside(
_Inout_ PPAGED_LOOKASIDE_LIST Lookaside,
_In_freesMem_ PVOID Memory
);
typedef PAGED_LOOKASIDE_LIST KPH_PAGED_LOOKASIDE_OBJECT;
typedef KPH_PAGED_LOOKASIDE_OBJECT* PKPH_PAGED_LOOKASIDE_OBJECT;
_IRQL_requires_max_(APC_LEVEL)
NTSTATUS KphCreatePagedLookasideObject(
_Outptr_result_nullonfailure_ PKPH_PAGED_LOOKASIDE_OBJECT* Lookaside,
_In_ SIZE_T Size,
_In_ ULONG Tag
);
_IRQL_requires_max_(APC_LEVEL)
_Return_allocatesMem_size_(Lookaside->L.Size)
PVOID KphAllocateFromPagedLookasideObject(
_Inout_ PKPH_PAGED_LOOKASIDE_OBJECT Lookaside
);
_IRQL_requires_max_(APC_LEVEL)
VOID KphFreeToPagedLookasideObject(
_Inout_ PKPH_PAGED_LOOKASIDE_OBJECT Lookaside,
_In_freesMem_ PVOID Memory
);
typedef NPAGED_LOOKASIDE_LIST KPH_NPAGED_LOOKASIDE_OBJECT;
typedef KPH_NPAGED_LOOKASIDE_OBJECT* PKPH_NPAGED_LOOKASIDE_OBJECT;
_IRQL_requires_max_(DISPATCH_LEVEL)
NTSTATUS KphCreateNPagedLookasideObject(
_Outptr_result_nullonfailure_ PKPH_NPAGED_LOOKASIDE_OBJECT* Lookaside,
_In_ SIZE_T Size,
_In_ ULONG Tag
);
_IRQL_requires_max_(DISPATCH_LEVEL)
_Return_allocatesMem_size_(Lookaside->L.Size)
PVOID KphAllocateFromNPagedLookasideObject(
_Inout_ PKPH_NPAGED_LOOKASIDE_OBJECT Lookaside
);
_IRQL_requires_max_(DISPATCH_LEVEL)
VOID KphFreeToNPagedLookasideObject(
_Inout_ PKPH_NPAGED_LOOKASIDE_OBJECT Lookaside,
_In_freesMem_ PVOID Memory
);
// dynimp
extern PPS_SET_LOAD_IMAGE_NOTIFY_ROUTINE_EX KphDynPsSetLoadImageNotifyRoutineEx;
extern PPS_SET_CREATE_PROCESS_NOTIFY_ROUTINE_EX2 KphDynPsSetCreateProcessNotifyRoutineEx2;
extern PMM_PROTECT_DRIVER_SECTION KphDynMmProtectDriverSection;
extern PPS_GET_PROCESS_SEQUENCE_NUMBER KphDynPsGetProcessSequenceNumber;
extern PPS_GET_PROCESS_START_KEY KphDynPsGetProcessStartKey;
extern PSE_REGISTER_IMAGE_VERIFICATION_CALLBACK KphSeRegisterImageVerificationCallback;
extern PSE_UNREGISTER_IMAGE_VERIFICATION_CALLBACK KphSeUnregisterImageVerificationCallback;
extern PCI_VALIDATE_FILE_OBJECT KphDynCiValidateFileObject;
extern PCI_FREE_POLICY_INFO KphDynCiFreePolicyInfo;
extern PLXP_THREAD_GET_CURRENT KphDynLxpThreadGetCurrent;
extern PIO_CHECK_FILE_OBJECT_OPENED_AS_COPY_SOURCE KphDynIoCheckFileObjectOpenedAsCopySource;
extern PIO_CHECK_FILE_OBJECT_OPENED_AS_COPY_DESTINATION KphDynIoCheckFileObjectOpenedAsCopyDestination;
extern PFLT_GET_COPY_INFORMATION_FROM_CALLBACK_DATA KphDynFltGetCopyInformationFromCallbackData;
_IRQL_requires_max_(PASSIVE_LEVEL)
VOID KphDynamicImport(
VOID
);
_IRQL_requires_max_(PASSIVE_LEVEL)
PVOID KphGetSystemRoutineAddress(
_In_z_ PCWSTR SystemRoutineName
);
_IRQL_requires_max_(PASSIVE_LEVEL)
PVOID KphGetRoutineAddress(
_In_z_ PCWSTR ModuleName,
_In_z_ PCSTR RoutineName
);
// dyndata
typedef struct _KPH_DYN
{
ULONG EgeGuid;
ULONG EpObjectTable;
ULONG EreGuidEntry;
ULONG HtHandleContentionEvent;
ULONG OtName;
ULONG OtIndex;
ULONG ObDecodeShift;
ULONG ObAttributesShift;
ULONG AlpcCommunicationInfo;
ULONG AlpcOwnerProcess;
ULONG AlpcConnectionPort;
ULONG AlpcServerCommunicationPort;
ULONG AlpcClientCommunicationPort;
ULONG AlpcHandleTable;
ULONG AlpcHandleTableLock;
ULONG AlpcAttributes;
ULONG AlpcAttributesFlags;
ULONG AlpcPortContext;
ULONG AlpcPortObjectLock;
ULONG AlpcSequenceNo;
ULONG AlpcState;
ULONG KtInitialStack;
ULONG KtStackLimit;
ULONG KtStackBase;
ULONG KtKernelStack;
ULONG KtReadOperationCount;
ULONG KtWriteOperationCount;
ULONG KtOtherOperationCount;
ULONG KtReadTransferCount;
ULONG KtWriteTransferCount;
ULONG KtOtherTransferCount;
ULONG MmSectionControlArea;
ULONG MmControlAreaListHead;
ULONG MmControlAreaLock;
ULONG EpSectionObject;
ULONG LxPicoProc;
ULONG LxPicoProcInfo;
ULONG LxPicoProcInfoPID;
ULONG LxPicoThrdInfo;
ULONG LxPicoThrdInfoTID;
BCRYPT_KEY_HANDLE SessionTokenPublicKeyHandle;
} KPH_DYN, *PKPH_DYN;
_Must_inspect_result_
PKPH_DYN KphReferenceDynData(
VOID
);
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KphActivateDynData(
_In_ PBYTE DynData,
_In_ ULONG DynDataLength,
_In_ PBYTE Signature,
_In_ ULONG SignatureLength,
_In_ KPROCESSOR_MODE AccessMode
);
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KphIsDynDataActive(
_Out_ PBOOLEAN IsActive,
_In_ KPROCESSOR_MODE AccessMode
);
_IRQL_requires_max_(PASSIVE_LEVEL)
VOID KphInitializeDynData(
VOID
);
_IRQL_requires_max_(PASSIVE_LEVEL)
VOID KphCleanupDynData(
VOID
);
// object
#ifdef _X86_
#define KERNEL_HANDLE_BIT (0x80000000)
#else
#define KERNEL_HANDLE_BIT (0xffffffff80000000)
#endif
#define IsKernelHandle(Handle) ((LONG_PTR)(Handle) < 0)
#define MakeKernelHandle(Handle) ((HANDLE)((ULONG_PTR)(Handle) | KERNEL_HANDLE_BIT))
#define IsPseudoHandle(Handle) (((ULONG_PTR)(Handle) <= (ULONG_PTR)-1) && \
((ULONG_PTR)(Handle) >= (ULONG_PTR)-6))
_Must_inspect_result_
PVOID KphObpDecodeObject(
_In_ PKPH_DYN Dyn,
_In_ PHANDLE_TABLE_ENTRY HandleTableEntry
);
ULONG KphObpGetHandleAttributes(
_In_ PKPH_DYN Dyn,
_In_ PHANDLE_TABLE_ENTRY HandleTableEntry
);
_When_(NT_SUCCESS(return), _Acquires_lock_(Process))
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KphReferenceProcessHandleTable(
_In_ PKPH_DYN Dyn,
_In_ PEPROCESS Process,
_Outptr_result_nullonfailure_ PHANDLE_TABLE* HandleTable
);
_Releases_lock_(Process)
_IRQL_requires_max_(PASSIVE_LEVEL)
VOID KphDereferenceProcessHandleTable(
_In_ PEPROCESS Process
);
typedef
_Function_class_(KPH_ENUM_PROCESS_HANDLES_CALLBACK)
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
BOOLEAN
KSIAPI
KPH_ENUM_PROCESS_HANDLES_CALLBACK(
_Inout_ PHANDLE_TABLE_ENTRY HandleTableEntry,
_In_ HANDLE Handle,
_In_opt_ PVOID Parameter
);
typedef KPH_ENUM_PROCESS_HANDLES_CALLBACK* PKPH_ENUM_PROCESS_HANDLES_CALLBACK;
_IRQL_requires_max_(PASSIVE_LEVEL)
NTSTATUS KphEnumerateProcessHandlesEx(
_In_ PEPROCESS Process,
_In_ PKPH_ENUM_PROCESS_HANDLES_CALLBACK Callback,
_In_opt_ PVOID Parameter
);
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KphEnumerateProcessHandles(
_In_ HANDLE ProcessHandle,
_Out_writes_bytes_(BufferLength) PVOID Buffer,
_In_ ULONG BufferLength,
_Out_opt_ PULONG ReturnLength,
_In_ KPROCESSOR_MODE AccessMode
);
_IRQL_requires_max_(APC_LEVEL)
_Must_inspect_result_
NTSTATUS KphQueryNameObject(
_In_ PVOID Object,
_Out_writes_bytes_opt_(BufferLength) POBJECT_NAME_INFORMATION Buffer,
_In_ ULONG BufferLength,
_Out_ PULONG ReturnLength
);
_IRQL_requires_max_(APC_LEVEL)
_Must_inspect_result_
NTSTATUS KphQueryNameFileObject(
_In_ PFILE_OBJECT FileObject,
_Out_writes_bytes_opt_(BufferLength) POBJECT_NAME_INFORMATION Buffer,
_In_ ULONG BufferLength,
_Out_ PULONG ReturnLength
);
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KphQueryInformationObject(
_In_ HANDLE ProcessHandle,
_In_ HANDLE Handle,
_In_ KPH_OBJECT_INFORMATION_CLASS ObjectInformationClass,
_Out_writes_bytes_opt_(ObjectInformationLength) PVOID ObjectInformation,
_In_ ULONG ObjectInformationLength,
_Out_opt_ PULONG ReturnLength,
_In_ KPROCESSOR_MODE AccessMode
);
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KphSetInformationObject(
_In_ HANDLE ProcessHandle,
_In_ HANDLE Handle,
_In_ KPH_OBJECT_INFORMATION_CLASS ObjectInformationClass,
_In_reads_bytes_(ObjectInformationLength) PVOID ObjectInformation,
_In_ ULONG ObjectInformationLength,
_In_ KPROCESSOR_MODE AccessMode
);
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KphOpenNamedObject(
_Out_ PHANDLE ObjectHandle,
_In_ ACCESS_MASK DesiredAccess,
_In_ POBJECT_ATTRIBUTES ObjectAttributes,
_In_ POBJECT_TYPE ObjectType,
_In_ KPROCESSOR_MODE AccessMode
);
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KphDuplicateObject(
_In_ HANDLE ProcessHandle,
_In_ HANDLE SourceHandle,
_In_ ACCESS_MASK DesiredAccess,
_Out_ PHANDLE TargetHandle,
_In_ KPROCESSOR_MODE AccessMode
);
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KphCompareObjects(
_In_ HANDLE ProcessHandle,
_In_ HANDLE FirstObjectHandle,
_In_ HANDLE SecondObjectHandle,
_In_ KPROCESSOR_MODE AccessMode
);
// process
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KphOpenProcess(
_Out_ PHANDLE ProcessHandle,
_In_ ACCESS_MASK DesiredAccess,
_In_ PCLIENT_ID ClientId,
_In_ KPROCESSOR_MODE AccessMode
);
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KphOpenProcessToken(
_In_ HANDLE ProcessHandle,
_In_ ACCESS_MASK DesiredAccess,
_Out_ PHANDLE TokenHandle,
_In_ KPROCESSOR_MODE AccessMode
);
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KphOpenProcessJob(
_In_ HANDLE ProcessHandle,
_In_ ACCESS_MASK DesiredAccess,
_Out_ PHANDLE JobHandle,
_In_ KPROCESSOR_MODE AccessMode
);
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KphTerminateProcess(
_In_ HANDLE ProcessHandle,
_In_ NTSTATUS ExitStatus,
_In_ KPROCESSOR_MODE AccessMode
);
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KphQueryInformationProcess(
_In_ HANDLE ProcessHandle,
_In_ KPH_PROCESS_INFORMATION_CLASS ProcessInformationClass,
_Out_writes_bytes_opt_(ProcessInformationLength) PVOID ProcessInformation,
_In_ ULONG ProcessInformationLength,
_Out_opt_ PULONG ReturnLength,
_In_ KPROCESSOR_MODE AccessMode
);
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KphSetInformationProcess(
_In_ HANDLE ProcessHandle,
_In_ KPH_PROCESS_INFORMATION_CLASS ProcessInformationClass,
_In_reads_bytes_(ProcessInformationLength) PVOID ProcessInformation,
_In_ ULONG ProcessInformationLength,
_In_ KPROCESSOR_MODE AccessMode
);
// driver
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KphOpenDriver(
_Out_ PHANDLE DriverHandle,
_In_ ACCESS_MASK DesiredAccess,
_In_ POBJECT_ATTRIBUTES ObjectAttributes,
_In_ KPROCESSOR_MODE AccessMode
);
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KphQueryInformationDriver(
_In_ HANDLE DriverHandle,
_In_ KPH_DRIVER_INFORMATION_CLASS DriverInformationClass,
_Out_writes_bytes_opt_(DriverInformationLength) PVOID DriverInformation,
_In_ ULONG DriverInformationLength,
_Out_opt_ PULONG ReturnLength,
_In_ KPROCESSOR_MODE AccessMode
);
// device
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KphOpenDevice(
_Out_ PHANDLE DeviceHandle,
_In_ ACCESS_MASK DesiredAccess,
_In_ POBJECT_ATTRIBUTES ObjectAttributes,
_In_ KPROCESSOR_MODE AccessMode
);
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KphOpenDeviceDriver(
_In_ HANDLE DeviceHandle,
_In_ ACCESS_MASK DesiredAccess,
_Out_ PHANDLE DriverHandle,
_In_ KPROCESSOR_MODE AccessMode
);
_IRQL_requires_max_(PASSIVE_LEVEL)
NTSTATUS KphOpenDeviceBaseDevice(
_In_ HANDLE DeviceHandle,
_In_ ACCESS_MASK DesiredAccess,
_Out_ PHANDLE BaseDeviceHandle,
_In_ KPROCESSOR_MODE AccessMode
);
// thread
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KphOpenThread(
_Out_ PHANDLE ThreadHandle,
_In_ ACCESS_MASK DesiredAccess,
_In_ PCLIENT_ID ClientId,
_In_ KPROCESSOR_MODE AccessMode
);
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KphOpenThreadProcess(
_In_ HANDLE ThreadHandle,
_In_ ACCESS_MASK DesiredAccess,
_Out_ PHANDLE ProcessHandle,
_In_ KPROCESSOR_MODE AccessMode
);
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KphCaptureStackBackTraceThreadByHandle(
_In_ HANDLE ThreadHandle,
_In_ ULONG FramesToSkip,
_In_ ULONG FramesToCapture,
_Out_writes_(FramesToCapture) PVOID* BackTrace,
_Out_ PULONG CapturedFrames,
_Out_opt_ PULONG BackTraceHash,
_In_ ULONG Flags,
_In_opt_ PLARGE_INTEGER Timeout,
_In_ KPROCESSOR_MODE AccessMode
);
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KphSetInformationThread(
_In_ HANDLE ThreadHandle,
_In_ KPH_THREAD_INFORMATION_CLASS ThreadInformationClass,
_In_reads_bytes_(ThreadInformationLength) PVOID ThreadInformation,
_In_ ULONG ThreadInformationLength,
_In_ KPROCESSOR_MODE AccessMode
);
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KphQueryInformationThread(
_In_ HANDLE ThreadHandle,
_In_ KPH_THREAD_INFORMATION_CLASS ThreadInformationClass,
_Out_writes_bytes_opt_(ThreadInformationLength) PVOID ThreadInformation,
_In_ ULONG ThreadInformationLength,
_Out_opt_ PULONG ReturnLength,
_In_ KPROCESSOR_MODE AccessMode
);
// util
#pragma deprecated("memcmp")
#pragma deprecated("RtlCompareMemory")
#pragma deprecated("RtlEqualMemory")
_Must_inspect_result_
FORCEINLINE
INT KphCompareMemory(
_In_reads_bytes_(Length) PVOID Buffer1,
_In_reads_bytes_(Length) PVOID Buffer2,
_In_ SIZE_T Length
)
{
#pragma warning(suppress: 4995) // suppress deprecation warning
return memcmp(Buffer1, Buffer2, Length);
}
_Must_inspect_result_
FORCEINLINE
BOOLEAN KphEqualMemory(
_In_reads_bytes_(Length) PVOID Buffer1,
_In_reads_bytes_(Length) PVOID Buffer2,
_In_ SIZE_T Length
)
{
#pragma warning(suppress: 4995) // suppress deprecation warning
return (memcmp(Buffer1, Buffer2, Length) == 0);
}
typedef
_Function_class_(KPH_BINARY_SEARCH_CALLBACK)
INT
KSIAPI
KPH_BINARY_SEARCH_CALLBACK(
_In_opt_ PVOID Context,
_In_ PCVOID Key,
_In_ PCVOID Element
);
typedef KPH_BINARY_SEARCH_CALLBACK* PKPH_BINARY_SEARCH_CALLBACK;
_Must_inspect_result_
_Success_(return != NULL)
PVOID KphBinarySearch(
_In_ PCVOID Key,
_In_reads_bytes_(NumberOfElements * SizeOfElement) PCVOID Base,
_In_ ULONG NumberOfElements,
_In_ ULONG SizeOfElement,
_In_ PKPH_BINARY_SEARCH_CALLBACK Callback,
_In_opt_ PVOID Context
);
typedef
_Function_class_(KPH_QUICK_SORT_CALLBACK)
INT
KSIAPI
KPH_QUICK_SORT_CALLBACK(
_In_opt_ PVOID Context,
_In_ PCVOID LeftElement,
_In_ PCVOID RightElement
);
typedef KPH_QUICK_SORT_CALLBACK* PKPH_QUICK_SORT_CALLBACK;
VOID KphQuickSort(
_Inout_updates_bytes_(NumberOfElements * SizeOfElement) PVOID Base,
_In_ ULONG NumberOfElements,
_In_ ULONG SizeOfElement,
_In_ PKPH_QUICK_SORT_CALLBACK Callback,
_In_opt_ PVOID Context
);
_Must_inspect_result_
_Success_(return != NULL)
PVOID KphSearchMemory(
_In_reads_bytes_(BufferLength) PVOID Buffer,
_In_ ULONG BufferLength,
_In_reads_bytes_(PatternLength) PVOID Pattern,
_In_ ULONG PatternLength
);
typedef EX_RUNDOWN_REF KPH_RUNDOWN;
typedef KPH_RUNDOWN* PKPH_RUNDOWN;
_IRQL_requires_max_(DISPATCH_LEVEL)
_Must_inspect_result_
BOOLEAN KphAcquireRundown(
_Inout_ PKPH_RUNDOWN Rundown
);
_IRQL_requires_max_(DISPATCH_LEVEL)
VOID KphReleaseRundown(
_Inout_ PKPH_RUNDOWN Rundown
);
_IRQL_requires_max_(APC_LEVEL)
VOID KphInitializeRundown(
_Out_ PKPH_RUNDOWN Rundown
);
_IRQL_requires_max_(APC_LEVEL)
VOID KphWaitForRundown(
_Inout_ PKPH_RUNDOWN Rundown
);
typedef EX_PUSH_LOCK KPH_RWLOCK;
typedef KPH_RWLOCK* PKPH_RWLOCK;
_IRQL_requires_max_(APC_LEVEL)
VOID KphInitializeRWLock(
_Out_ PKPH_RWLOCK Lock
);
_IRQL_requires_max_(APC_LEVEL)
VOID KphDeleteRWLock(
_In_ PKPH_RWLOCK Lock
);
_IRQL_requires_max_(APC_LEVEL)
_Acquires_lock_(_Global_critical_region_)
VOID KphAcquireRWLockExclusive(
_Inout_ _Requires_lock_not_held_(*_Curr_) _Acquires_lock_(*_Curr_) PKPH_RWLOCK Lock
);
_IRQL_requires_max_(APC_LEVEL)
_Acquires_lock_(_Global_critical_region_)
VOID KphAcquireRWLockShared(
_Inout_ _Requires_lock_not_held_(*_Curr_) _Acquires_lock_(*_Curr_) PKPH_RWLOCK Lock
);
_IRQL_requires_max_(APC_LEVEL)
_Releases_lock_(_Global_critical_region_)
VOID KphReleaseRWLock(
_Inout_ _Requires_lock_held_(*_Curr_) _Releases_lock_(*_Curr_) PKPH_RWLOCK Lock
);
_IRQL_requires_max_(DISPATCH_LEVEL)
_Must_inspect_result_
BOOLEAN KphIsSameFile(
_In_ PFILE_OBJECT FirstFileObject,
_In_ PFILE_OBJECT SecondFileObject
);
typedef struct _KPH_REFERENCE
{
LONG Count;
} KPH_REFERENCE, *PKPH_REFERENCE;
_IRQL_requires_max_(APC_LEVEL)
_Must_inspect_result_
NTSTATUS KphAcquireReference(
_Inout_ PKPH_REFERENCE Reference,
_Out_opt_ PLONG PreviousCount
);
_IRQL_requires_max_(APC_LEVEL)
_Must_inspect_result_
NTSTATUS KphReleaseReference(
_Inout_ PKPH_REFERENCE Reference,
_Out_opt_ PLONG PreviousCount
);
_IRQL_requires_max_(APC_LEVEL)
_Must_inspect_result_
NTSTATUS KphValidateAddressForSystemModules(
_In_ PVOID Address,
_In_ SIZE_T Length
);
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KphQueryRegistryString(
_In_ HANDLE KeyHandle,
_In_ PCUNICODE_STRING ValueName,
_Outptr_allocatesMem_ PUNICODE_STRING* String
);
_IRQL_requires_max_(APC_LEVEL)
VOID KphFreeRegistryString(
_In_freesMem_ PUNICODE_STRING String
);
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KphQueryRegistryBinary(
_In_ HANDLE KeyHandle,
_In_ PCUNICODE_STRING ValueName,
_Outptr_allocatesMem_ PBYTE* Buffer,
_Out_ PULONG Length
);
_IRQL_requires_max_(APC_LEVEL)
VOID KphFreeRegistryBinary(
_In_freesMem_ PBYTE Buffer
);
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KphQueryRegistryULong(
_In_ HANDLE KeyHandle,
_In_ PCUNICODE_STRING ValueName,
_Out_ PULONG Value
);
#define KPH_MAP_DATA 0x00000000ul
#define KPH_MAP_IMAGE 0x00000001ul
_IRQL_always_function_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KphMapViewInSystem(
_In_ HANDLE FileHandle,
_In_ ULONG Flags,
_Outptr_result_bytebuffer_(*ViewSize) PVOID *MappedBase,
_Inout_ PSIZE_T ViewSize
);
_IRQL_always_function_max_(PASSIVE_LEVEL)
VOID KphUnmapViewInSystem(
_In_ PVOID MappedBase
);
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KphGetNameFileObject(
_In_ PFILE_OBJECT FileObject,
_Outptr_allocatesMem_ PUNICODE_STRING* FileName
);
_IRQL_requires_max_(APC_LEVEL)
VOID KphFreeNameFileObject(
_In_freesMem_ PUNICODE_STRING FileName
);
_IRQL_requires_max_(APC_LEVEL)
BOOLEAN KphSinglePrivilegeCheckEx(
_In_ LUID PrivilegeValue,
_In_ PSECURITY_SUBJECT_CONTEXT SubjectSecurityContext,
_In_ KPROCESSOR_MODE AccessMode
);
_IRQL_requires_max_(APC_LEVEL)
BOOLEAN KphSinglePrivilegeCheck(
_In_ LUID PrivilegeValue,
_In_ KPROCESSOR_MODE AccessMode
);
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KphGetFileVersion(
_In_ PCUNICODE_STRING FileName,
_Out_ PKPH_FILE_VERSION Version
);
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KphGetKernelVersion(
_Out_ PKPH_FILE_VERSION Version
);
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KphGuardGrantSuppressedCallAccess(
_In_ HANDLE ProcessHandle,
_In_ PVOID VirtualAddress
);
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KphDisableXfgOnTarget(
_In_ HANDLE ProcessHandle,
_In_ PVOID VirtualAddress
);
_IRQL_requires_max_(APC_LEVEL)
_Must_inspect_result_
NTSTATUS KphGetFileNameFinalComponent(
_In_ PCUNICODE_STRING FileName,
_Out_ PUNICODE_STRING FinalComponent
);
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KphGetProcessImageName(
_In_ PEPROCESS Process,
_Out_allocatesMem_ PUNICODE_STRING ImageName
);
_IRQL_requires_max_(APC_LEVEL)
VOID KphFreeProcessImageName(
_In_freesMem_ PUNICODE_STRING ImageName
);
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KphOpenParametersKey(
_In_ PCUNICODE_STRING RegistryPath,
_Out_ PHANDLE KeyHandle
);
_IRQL_requires_max_(APC_LEVEL)
_Must_inspect_result_
NTSTATUS KphDominationCheck(
_In_ PEPROCESS Process,
_In_ PEPROCESS ProcessTarget,
_In_ KPROCESSOR_MODE AccessMode
);
_IRQL_requires_max_(APC_LEVEL)
_Must_inspect_result_
NTSTATUS KphDominationAndPrivilegeCheck(
_In_ ULONG Privileges,
_In_ PETHREAD Thread,
_In_ PEPROCESS ProcessTarget,
_In_ KPROCESSOR_MODE AccessMode
);
_IRQL_requires_max_(DISPATCH_LEVEL)
ULONG64 KphGetProcessSequenceNumber(
_In_ PEPROCESS Process
);
_IRQL_requires_max_(DISPATCH_LEVEL)
ULONG64 KphGetProcessStartKey(
_In_ PEPROCESS Process
);
#define KphGetCurrentProcessStartKey() KphGetProcessStartKey(PsGetCurrentProcess())
#define KphGetThreadProcessStartKey(thread) KphGetProcessStartKey(PsGetThreadProcess(thread))
_IRQL_requires_max_(DISPATCH_LEVEL)
PVOID KphGetCurrentThreadSubProcessTag(
VOID
);
_IRQL_requires_max_(DISPATCH_LEVEL)
PVOID KphGetThreadSubProcessTagEx(
_In_ PETHREAD Thread,
_In_ BOOLEAN CacheOnly
);
_IRQL_requires_max_(DISPATCH_LEVEL)
PVOID KphGetThreadSubProcessTag(
_In_ PETHREAD Thread
);
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KphGetSigningLevel(
_In_ PFILE_OBJECT FileObject,
_Out_ PSE_SIGNING_LEVEL SigningLevel
);
typedef union _KPH_IMAGE_NT_HEADERS
{
PIMAGE_NT_HEADERS Headers;
PIMAGE_NT_HEADERS32 Headers32;
PIMAGE_NT_HEADERS64 Headers64;
} KPH_IMAGE_NT_HEADERS, *PKPH_IMAGE_NT_HEADERS;
_IRQL_requires_max_(APC_LEVEL)
_Must_inspect_result_
NTSTATUS KphImageNtHeader(
_In_ PVOID Base,
_In_ ULONG64 Size,
_Out_ PKPH_IMAGE_NT_HEADERS Headers
);
// lsa
_IRQL_requires_max_(APC_LEVEL)
_Must_inspect_result_
NTSTATUS KphProcessIsLsass(
_In_ PEPROCESS Process,
_Out_ PBOOLEAN IsLsass
);
_IRQL_requires_max_(APC_LEVEL)
VOID KphValidateLsass(
_In_ PEPROCESS Process
);
_IRQL_requires_max_(PASSIVE_LEVEL)
VOID KphInvalidateLsass(
_In_ PEPROCESS Process
);
_IRQL_requires_max_(APC_LEVEL)
_Must_inspect_result_
BOOLEAN KphCanIdentifyLsass(
VOID
);
// vm
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KphReadVirtualMemory(
_In_opt_ HANDLE ProcessHandle,
_In_ PVOID BaseAddress,
_Out_writes_bytes_(BufferSize) PVOID Buffer,
_In_ SIZE_T BufferSize,
_Out_opt_ PSIZE_T NumberOfBytesRead,
_In_ KPROCESSOR_MODE AccessMode
);
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KphQuerySection(
_In_ HANDLE SectionHandle,
_In_ KPH_SECTION_INFORMATION_CLASS SectionInformationClass,
_Out_writes_bytes_opt_(SectionInformationLength) PVOID SectionInformation,
_In_ ULONG SectionInformationLength,
_Out_opt_ PULONG ReturnLength,
_In_ KPROCESSOR_MODE AccessMode
);
typedef struct _KPH_VM_TLS_CREATE_DATA_SECTION
{
NTSTATUS Status;
HANDLE SectionHandle;
LARGE_INTEGER SectionFileSize;
KPROCESSOR_MODE AccessMode;
} KPH_VM_TLS_CREATE_DATA_SECTION, *PKPH_VM_TLS_CREATE_DATA_SECTION;
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KphQueryVirtualMemory(
_In_ HANDLE ProcessHandle,
_In_opt_ PVOID BaseAddress,
_In_ KPH_MEMORY_INFORMATION_CLASS MemoryInformationClass,
_Out_writes_bytes_opt_(MemoryInformationLength) PVOID MemoryInformation,
_In_ ULONG MemoryInformationLength,
_Out_opt_ PULONG ReturnLength,
_In_ KPROCESSOR_MODE AccessMode
);
// hash
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KphInitializeHashing(
VOID
);
_IRQL_requires_max_(PASSIVE_LEVEL)
VOID KphCleanupHashing(
VOID
);
_IRQL_requires_max_(APC_LEVEL)
VOID KphReferenceHashingInfrastructure(
VOID
);
_IRQL_requires_max_(APC_LEVEL)
VOID KphDereferenceHashingInfrastructure(
VOID
);
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KphHashBuffer(
_In_ PBYTE Buffer,
_In_ ULONG BufferLength,
_In_ KPH_HASH_ALGORITHM HashAlgorithm,
_Out_ PKPH_HASH_INFORMATION HashInformation
);
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KphQueryHashInformationFile(
_In_ HANDLE FileHandle,
_Inout_ PKPH_HASH_INFORMATION HashInformation,
_In_ ULONG HashInformationLength,
_In_ KPROCESSOR_MODE AccessMode
);
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KphQueryHashInformationFileObject(
_In_ PFILE_OBJECT FileObject,
_Inout_ PKPH_HASH_INFORMATION HashInformation,
_In_ ULONG HashInformationLength
);
// kphobject
typedef struct _KPH_OBJECT_HEADER
{
SSIZE_T PointerCount;
UCHAR TypeIndex;
SLIST_ENTRY ListEntry;
QUAD Body;
} KPH_OBJECT_HEADER, *PKPH_OBJECT_HEADER;
C_ASSERT((FIELD_OFFSET(KPH_OBJECT_HEADER, Body) % MEMORY_ALLOCATION_ALIGNMENT) == 0);
#define KphObjectToObjectHeader(x) ((PKPH_OBJECT_HEADER)CONTAINING_RECORD((PCHAR)x, KPH_OBJECT_HEADER, Body))
#define KphObjectHeaderToObject(x) ((PVOID)&((PKPH_OBJECT_HEADER)(x))->Body)
#define KphAddObjectHeaderSize(x) ((SIZE_T)(x) + FIELD_OFFSET(KPH_OBJECT_HEADER, Body))
typedef
_Function_class_(KPH_TYPE_ALLOCATE_PROCEDURE)
_Return_allocatesMem_size_(Size)
PVOID
KSIAPI
KPH_TYPE_ALLOCATE_PROCEDURE(
_In_ SIZE_T Size
);
typedef KPH_TYPE_ALLOCATE_PROCEDURE* PKPH_TYPE_ALLOCATE_PROCEDURE;
typedef
_Function_class_(KPH_TYPE_INITIALIZE_PROCEDURE)
_Must_inspect_result_
NTSTATUS
KSIAPI
KPH_TYPE_INITIALIZE_PROCEDURE(
_Inout_ PVOID Object,
_In_opt_ PVOID Parameter
);
typedef KPH_TYPE_INITIALIZE_PROCEDURE* PKPH_TYPE_INITIALIZE_PROCEDURE;
typedef
_Function_class_(KPH_TYPE_DELETE_PROCEDURE)
VOID
KSIAPI
KPH_TYPE_DELETE_PROCEDURE(
_Inout_ PVOID Object
);
typedef KPH_TYPE_DELETE_PROCEDURE* PKPH_TYPE_DELETE_PROCEDURE;
typedef
_Function_class_(KPH_TYPE_FREE_PROCEDURE)
VOID
KSIAPI
KPH_TYPE_FREE_PROCEDURE(
_In_freesMem_ PVOID Object
);
typedef KPH_TYPE_FREE_PROCEDURE* PKPH_TYPE_FREE_PROCEDURE;
typedef struct _KPH_OBJECT_TYPE_INFO
{
PKPH_TYPE_ALLOCATE_PROCEDURE Allocate;
PKPH_TYPE_INITIALIZE_PROCEDURE Initialize;
PKPH_TYPE_DELETE_PROCEDURE Delete;
PKPH_TYPE_FREE_PROCEDURE Free;
union
{
struct
{
ULONG DeferDelete : 1;
ULONG Spare : 31;
};
ULONG Flags;
};
} KPH_OBJECT_TYPE_INFO, *PKPH_OBJECT_TYPE_INFO;
typedef struct _KPH_OBJECT_TYPE
{
UNICODE_STRING Name;
UCHAR Index;
SIZE_T TotalNumberOfObjects;
SIZE_T HighWaterNumberOfObjects;
KPH_OBJECT_TYPE_INFO TypeInfo;
} KPH_OBJECT_TYPE, *PKPH_OBJECT_TYPE;
_IRQL_requires_max_(PASSIVE_LEVEL)
VOID KphObjectInitialize(
VOID
);
VOID KphCreateObjectType(
_In_ PCUNICODE_STRING TypeName,
_In_ PKPH_OBJECT_TYPE_INFO TypeInfo,
_Outptr_ PKPH_OBJECT_TYPE* ObjectType
);
_Must_inspect_result_
NTSTATUS KphCreateObject(
_In_ PKPH_OBJECT_TYPE ObjectType,
_In_ ULONG ObjectBodySize,
_Outptr_result_nullonfailure_ PVOID* Object,
_In_opt_ PVOID Parameter
);
VOID KphReferenceObject(
_In_ PVOID Object
);
VOID KphDereferenceObject(
_In_ PVOID Object
);
VOID KphDereferenceObjectDeferDelete(
_In_ PVOID Object
);
_Must_inspect_result_
PKPH_OBJECT_TYPE KphGetObjectType(
_In_ PVOID Object
);
typedef struct _KPH_ATOMIC_OBJECT_REF
{
ULONG_PTR Object;
} KPH_ATOMIC_OBJECT_REF, *PKPH_ATOMIC_OBJECT_REF;
#define KPH_ATOMIC_OBJECT_REF_INIT { .Object = 0 }
_Must_inspect_result_
PVOID KphAtomicReferenceObject(
_In_ PKPH_ATOMIC_OBJECT_REF ObjectRef
);
VOID KphAtomicAssignObjectReference(
_Inout_ PKPH_ATOMIC_OBJECT_REF ObjectRef,
_In_opt_ PVOID Object
);
_Must_inspect_result_
PVOID KphAtomicMoveObjectReference(
_Inout_ PKPH_ATOMIC_OBJECT_REF ObjectRef,
_In_opt_ PVOID Object
);
// cid_table
typedef struct _KPH_CID_TABLE_ENTRY
{
KPH_ATOMIC_OBJECT_REF ObjectRef;
} KPH_CID_TABLE_ENTRY, *PKPH_CID_TABLE_ENTRY;
typedef struct _KPH_CID_TABLE
{
KSPIN_LOCK Lock;
ULONG_PTR Table;
} KPH_CID_TABLE, *PKPH_CID_TABLE;
_IRQL_requires_max_(DISPATCH_LEVEL)
_Must_inspect_result_
NTSTATUS KphCidTableCreate(
_Out_ PKPH_CID_TABLE Table
);
_IRQL_requires_max_(DISPATCH_LEVEL)
VOID KphCidTableDelete(
_In_ PKPH_CID_TABLE Table
);
_IRQL_requires_max_(DISPATCH_LEVEL)
VOID KphCidAssignObject(
_Inout_ PKPH_CID_TABLE_ENTRY Entry,
_In_opt_ PVOID Object
);
_IRQL_requires_max_(DISPATCH_LEVEL)
_Must_inspect_result_
PVOID KphCidReferenceObject(
_In_ PKPH_CID_TABLE_ENTRY Entry
);
_IRQL_requires_max_(DISPATCH_LEVEL)
_Must_inspect_result_
PKPH_CID_TABLE_ENTRY KphCidGetEntry(
_In_ HANDLE Cid,
_Inout_ PKPH_CID_TABLE Table
);
typedef
_Function_class_(KPH_CID_ENUMERATE_CALLBACK)
BOOLEAN
KSIAPI
KPH_CID_ENUMERATE_CALLBACK(
_In_ PVOID Object,
_In_opt_ PVOID Parameter
);
typedef KPH_CID_ENUMERATE_CALLBACK* PKPH_CID_ENUMERATE_CALLBACK;
_IRQL_requires_max_(DISPATCH_LEVEL)
VOID KphCidEnumerate(
_In_ PKPH_CID_TABLE Table,
_In_ PKPH_CID_ENUMERATE_CALLBACK Callback,
_In_opt_ PVOID Parameter
);
typedef
_Function_class_(KPH_CID_RUNDOWN_CALLBACK)
VOID
KSIAPI
KPH_CID_RUNDOWN_CALLBACK(
_In_ PVOID Object,
_In_opt_ PVOID Parameter
);
typedef KPH_CID_RUNDOWN_CALLBACK* PKPH_CID_RUNDOWN_CALLBACK;
_IRQL_requires_max_(DISPATCH_LEVEL)
VOID KphCidRundown(
_In_ PKPH_CID_TABLE Table,
_In_ PKPH_CID_RUNDOWN_CALLBACK Callback,
_In_opt_ PVOID Parameter
);
// cid_tracking
#define KPH_PROTECTED_PROCESS_MASK (KPH_PROCESS_READ_ACCESS |\
PROCESS_TERMINATE |\
PROCESS_SUSPEND_RESUME)
#define KPH_PROTECTED_THREAD_MASK (KPH_THREAD_READ_ACCESS |\
THREAD_TERMINATE |\
THREAD_SUSPEND_RESUME |\
THREAD_RESUME)
typedef union _KPH_SESSION_TOKEN_ATOMIC
{
struct _KPH_SESSION_TOKEN* Token;
KPH_ATOMIC_OBJECT_REF Atomic;
} KPH_SESSION_TOKEN_ATOMIC, *PKPH_SESSION_TOKEN_ATOMIC;
typedef union _KPH_INFORMER_STATE_ATOMIC
{
struct _KPH_INFORMER_STATE* State;
KPH_ATOMIC_OBJECT_REF Atomic;
} KPH_INFORMER_STATE_ATOMIC, *PKPH_INFORMER_STATE_ATOMIC;
typedef struct _KPH_PROCESS_CONTEXT
{
PEPROCESS EProcess;
HANDLE ProcessId;
ULONG64 SequenceNumber;
ULONG64 ProcessStartKey;
CLIENT_ID CreatorClientId;
PUNICODE_STRING ImageFileName;
UNICODE_STRING ImageName;
PFILE_OBJECT FileObject;
SIZE_T NumberOfImageLoads;
KPH_SESSION_TOKEN_ATOMIC SessionToken;
KPH_INFORMER_STATE_ATOMIC InformerState;
union
{
ULONG Flags;
struct
{
ULONG CreateNotification : 1;
ULONG ExitNotification : 1;
ULONG VerifiedProcess : 1;
ULONG SecurelyCreated : 1;
ULONG Protected : 1;
ULONG IsWow64 : 1;
ULONG IsSubsystemProcess : 1;
ULONG AllocatedImageName : 1;
ULONG SystemAllocatedImageFileName : 1;
ULONG Reserved : 23;
};
};
KPH_RWLOCK ThreadListLock;
struct _KPH_THREAD_CONTEXT* InitialThread;
SIZE_T NumberOfThreads;
LIST_ENTRY ThreadListHead;
SIZE_T NumberOfUnlinkedThreads;
//
// Masks are only valid if Protected flag is set.
//
KPH_RWLOCK ProtectionLock;
ACCESS_MASK ProcessAllowedMask;
ACCESS_MASK ThreadAllowedMask;
union
{
ULONG Flags;
struct
{
ULONG Debugged : 1;
ULONG FileObjectWritable : 1;
ULONG FileObjectTransaction : 1;
ULONG UserWritableReferences : 1;
ULONG Reserved : 28;
};
} StateTracking;
//
// Only tracked for verified processes.
//
SIZE_T NumberOfMicrosoftImageLoads;
SIZE_T NumberOfAntimalwareImageLoads;
SIZE_T NumberOfVerifiedImageLoads;
SIZE_T NumberOfUntrustedImageLoads;
PKNORMAL_ROUTINE ApcNoopRoutine;
SUBSYSTEM_INFORMATION_TYPE SubsystemType;
//
// SubsystemInformationTypeWSL information
//
struct
{
BOOLEAN ValidProcessId;
ULONG ProcessId;
} WSL;
} KPH_PROCESS_CONTEXT, *PKPH_PROCESS_CONTEXT;
extern PKPH_OBJECT_TYPE KphProcessContextType;
typedef enum _KPH_PROCESS_CONTEXT_INFORMATION_CLASS
{
KphProcessContextWSLProcessId, // q: ULONG
} KPH_PROCESS_CONTEXT_INFORMATION_CLASS, *PKPH_PROCESS_CONTEXT_INFORMATION_CLASS;
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KphQueryInformationProcessContext(
_In_ PKPH_PROCESS_CONTEXT Process,
_In_ KPH_PROCESS_CONTEXT_INFORMATION_CLASS InformationClass,
_Out_writes_bytes_opt_(InformationLength) PVOID Information,
_In_ ULONG InformationLength,
_Out_opt_ PULONG ReturnLength
);
typedef struct _KPH_THREAD_CONTEXT
{
PETHREAD EThread;
PKPH_PROCESS_CONTEXT ProcessContext;
CLIENT_ID ClientId;
CLIENT_ID CreatorClientId;
PVOID SubProcessTag;
KPH_SESSION_TOKEN_ATOMIC SessionToken;
KPH_SESSION_TOKEN_ATOMIC RequestSessionToken;
union
{
volatile ULONG Flags;
struct
{
ULONG CreateNotification : 1;
ULONG ExecuteNotification : 1;
ULONG ExitNotification : 1;
ULONG InThreadList : 1;
ULONG IsCreatingProcess : 1;
ULONG CapturingUserModeStack : 1;
ULONG Reserved : 26;
};
};
LIST_ENTRY ThreadListEntry;
//
// Only valid if IsCreatingProcess flag is set.
//
HANDLE IsCreatingProcessId;
SUBSYSTEM_INFORMATION_TYPE SubsystemType;
//
// SubsystemInformationTypeWSL information
//
struct
{
BOOLEAN ValidThreadId;
ULONG ThreadId;
} WSL;
PVOID VmTlsCreateDataSection;
PVOID VmTlsMappedInformation;
} KPH_THREAD_CONTEXT, *PKPH_THREAD_CONTEXT;
extern PKPH_OBJECT_TYPE KphThreadContextType;
typedef enum _KPH_THREAD_CONTEXT_INFORMATION_CLASS
{
KphThreadContextWSLThreadId, // q: ULONG
} KPH_THREAD_CONTEXT_INFORMATION_CLASS, *PKPH_THREAD_CONTEXT_INFORMATION_CLASS;
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KphQueryInformationThreadContext(
_In_ PKPH_THREAD_CONTEXT Thread,
_In_ KPH_THREAD_CONTEXT_INFORMATION_CLASS InformationClass,
_Out_writes_bytes_opt_(InformationLength) PVOID Information,
_In_ ULONG InformationLength,
_Out_opt_ PULONG ReturnLength
);
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KphCidInitialize(
VOID
);
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KphCidPopulate(
VOID
);
_IRQL_requires_max_(PASSIVE_LEVEL)
VOID KphCidCleanup(
VOID
);
_IRQL_requires_max_(PASSIVE_LEVEL)
VOID KphCidMarkPopulated(
VOID
);
_IRQL_requires_max_(DISPATCH_LEVEL)
_Must_inspect_result_
PKPH_PROCESS_CONTEXT KphGetSystemProcessContext(
VOID
);
_IRQL_requires_max_(DISPATCH_LEVEL)
_Must_inspect_result_
PKPH_PROCESS_CONTEXT KphGetProcessContext(
_In_ HANDLE ProcessId
);
_IRQL_requires_max_(DISPATCH_LEVEL)
_Must_inspect_result_
PKPH_PROCESS_CONTEXT KphGetEProcessContext(
_In_ PEPROCESS Process
);
#define KphGetCurrentProcessContext() KphGetEProcessContext(PsGetCurrentProcess())
_IRQL_requires_max_(DISPATCH_LEVEL)
_Must_inspect_result_
PKPH_THREAD_CONTEXT KphGetThreadContext(
_In_ HANDLE ThreadId
);
#define KphGetEThreadContext(thread) KphGetThreadContext(PsGetThreadId(thread))
#define KphGetCurrentThreadContext() KphGetThreadContext(PsGetCurrentThreadId())
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
PKPH_PROCESS_CONTEXT KphTrackProcessContext(
_In_ PEPROCESS Process
);
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
PKPH_PROCESS_CONTEXT KphUntrackProcessContext(
_In_ HANDLE ProcessId
);
_IRQL_requires_max_(APC_LEVEL)
_Must_inspect_result_
PKPH_THREAD_CONTEXT KphTrackThreadContext(
_In_ PETHREAD Thread
);
_IRQL_requires_max_(APC_LEVEL)
_Must_inspect_result_
PKPH_THREAD_CONTEXT KphUntrackThreadContext(
_In_ HANDLE ThreadId
);
typedef
_Function_class_(KPH_ENUM_PROCESS_CONTEXTS_CALLBACK)
_Must_inspect_result_
BOOLEAN
KSIAPI
KPH_ENUM_PROCESS_CONTEXTS_CALLBACK(
_In_ PKPH_PROCESS_CONTEXT Process,
_In_opt_ PVOID Parameter
);
typedef KPH_ENUM_PROCESS_CONTEXTS_CALLBACK* PKPH_ENUM_PROCESS_CONTEXTS_CALLBACK;
_IRQL_requires_max_(APC_LEVEL)
VOID KphEnumerateProcessContexts(
_In_ PKPH_ENUM_PROCESS_CONTEXTS_CALLBACK Callback,
_In_opt_ PVOID Parameter
);
typedef
_Function_class_(KPH_ENUM_THREAD_CONTEXTS_CALLBACK)
_Must_inspect_result_
BOOLEAN
KSIAPI
KPH_ENUM_THREAD_CONTEXTS_CALLBACK(
_In_ PKPH_PROCESS_CONTEXT Thread,
_In_opt_ PVOID Parameter
);
typedef KPH_ENUM_THREAD_CONTEXTS_CALLBACK* PKPH_ENUM_THREAD_CONTEXTS_CALLBACK;
_IRQL_requires_max_(APC_LEVEL)
VOID KphEnumerateThreadContexts(
_In_ PKPH_ENUM_THREAD_CONTEXTS_CALLBACK Callback,
_In_opt_ PVOID Parameter
);
typedef
_Function_class_(KPH_ENUM_CID_CONTEXTS_CALLBACK)
_Must_inspect_result_
BOOLEAN
KSIAPI
KPH_ENUM_CID_CONTEXTS_CALLBACK(
_In_ PVOID Context,
_In_opt_ PVOID Parameter
);
typedef KPH_ENUM_CID_CONTEXTS_CALLBACK* PKPH_ENUM_CID_CONTEXTS_CALLBACK;
_IRQL_requires_max_(APC_LEVEL)
VOID KphEnumerateCidContexts(
_In_ KPH_ENUM_CID_CONTEXTS_CALLBACK Callback,
_In_opt_ PVOID Parameter
);
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KphCheckProcessApcNoopRoutine(
_In_ PKPH_PROCESS_CONTEXT ProcessContext
);
_IRQL_requires_max_(PASSIVE_LEVEL)
VOID KphVerifyProcessAndProtectIfAppropriate(
_In_ PKPH_PROCESS_CONTEXT Process
);
_IRQL_requires_max_(APC_LEVEL)
_Must_inspect_result_
_Maybenull_
PUNICODE_STRING KphGetThreadImageName(
_In_ PKPH_THREAD_CONTEXT Thread
);
_IRQL_requires_max_(APC_LEVEL)
KPH_PROCESS_STATE KphGetProcessState(
_In_ PKPH_PROCESS_CONTEXT Process
);
_IRQL_requires_max_(APC_LEVEL)
FORCEINLINE
BOOLEAN KphTestProcessState(
_In_ KPH_PROCESS_STATE ProcessState,
_In_ KPH_PROCESS_STATE State
)
{
return ((ProcessState & State) == State);
}
_IRQL_requires_max_(APC_LEVEL)
FORCEINLINE
BOOLEAN KphTestProcessContextState(
_In_ PKPH_PROCESS_CONTEXT Process,
_In_ KPH_PROCESS_STATE State
)
{
return KphTestProcessState(KphGetProcessState(Process), State);
}
// protection
_IRQL_requires_max_(PASSIVE_LEVEL)
VOID KphInitializeProtection(
VOID
);
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KphStartProtectingProcess(
_In_ PKPH_PROCESS_CONTEXT Process,
_In_ ACCESS_MASK ProcessAllowedMask,
_In_ ACCESS_MASK ThreadAllowedMask
);
_IRQL_requires_max_(APC_LEVEL)
VOID KphStopProtectingProcess(
_In_ PKPH_PROCESS_CONTEXT Process
);
_IRQL_requires_max_(PASSIVE_LEVEL)
BOOLEAN KphIsProtectedProcess(
_In_ PKPH_PROCESS_CONTEXT Process
);
_IRQL_requires_max_(APC_LEVEL)
VOID KphApplyObProtections(
_Inout_ POB_PRE_OPERATION_INFORMATION Info
);
_IRQL_requires_max_(PASSIVE_LEVEL)
VOID KphApplyImageProtections(
_Inout_ PKPH_PROCESS_CONTEXT Process,
_In_ PIMAGE_INFO_EX ImageInfo
);
_IRQL_requires_max_(APC_LEVEL)
_Must_inspect_result_
NTSTATUS KphAcquireDriverUnloadProtection(
_Out_opt_ PLONG PreviousCount
);
_IRQL_requires_max_(APC_LEVEL)
_Must_inspect_result_
NTSTATUS KphReleaseDriverUnloadProtection(
_Out_opt_ PLONG PreviousCount
);
_IRQL_requires_max_(APC_LEVEL)
LONG KphGetDriverUnloadProtectionCount(
VOID
);
FORCEINLINE
BOOLEAN KphIsDriverUnloadProtected(
VOID
)
{
return (KphGetDriverUnloadProtectionCount() > 0);
}
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KphStripProtectedProcessMasks(
_In_ HANDLE ProcessHandle,
_In_ ACCESS_MASK ProcessAllowedMask,
_In_ ACCESS_MASK ThreadAllowedMask,
_In_ KPROCESSOR_MODE AccessMode
);
// imgcoherency
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KphCheckImageCoherency(
_In_ PVOID ImageBase,
_In_ SIZE_T ImageSize,
_In_ PVOID DataBase,
_In_ SIZE_T DataSize
);
// verify
_IRQL_requires_max_(PASSIVE_LEVEL)
VOID KphVerifyCloseKey(
_In_ BCRYPT_KEY_HANDLE KeyHandle
);
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KphVerifyCreateKey(
_Out_ BCRYPT_KEY_HANDLE* KeyHandle,
_In_ PBYTE KeyMaterial,
_In_ ULONG KeyMaterialLength
);
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KphVerifyBufferEx(
_In_opt_ BCRYPT_KEY_HANDLE KeyHandle,
_In_ PBYTE Buffer,
_In_ ULONG BufferLength,
_In_ PBYTE Signature,
_In_ ULONG SignatureLength
);
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KphVerifyBuffer(
_In_ PBYTE Buffer,
_In_ ULONG BufferLength,
_In_ PBYTE Signature,
_In_ ULONG SignatureLength
);
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KphVerifyFileObject(
_In_ PFILE_OBJECT FileObject,
_In_opt_ PCUNICODE_STRING FileName
);
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KphVerifyFile(
_In_ PCUNICODE_STRING FileName,
_In_opt_ PFILE_OBJECT FileObject
);
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KphInitializeVerify(
VOID
);
_IRQL_requires_max_(PASSIVE_LEVEL)
VOID KphCleanupVerify(
VOID
);
// ksi.dll
#if !defined(_KSIDLL_)
#define KSISYSAPI DECLSPEC_IMPORT
#else
#define KSISYSAPI
#endif
#define KSIDLL_CURRENT_VERSION 1
KSISYSAPI
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS
KSIAPI
KsiInitialize(
_In_ ULONG Version,
_In_ PDRIVER_OBJECT DriverObject,
_In_opt_ PVOID Reserved
);
KSISYSAPI
_IRQL_requires_max_(PASSIVE_LEVEL)
VOID
KSIAPI
KsiUninitialize(
_In_ PDRIVER_OBJECT DriverObject,
_In_ ULONG Reserved
);
typedef struct _KSI_KAPC
{
KAPC Apc;
PDRIVER_OBJECT DriverObject;
PVOID InternalRoutine;
PVOID InternalCleanup;
PVOID InternalContext;
} KSI_KAPC, *PKSI_KAPC;
typedef enum _KSI_KAPC_CLEANUP_REASON
{
KsiApcCleanupKernel,
KsiApcCleanupNormal,
KsiApcCleanupRundown,
KsiApcCleanupRemoved
} KSI_KAPC_CLEANUP_REASON;
typedef
_Function_class_(KSI_KCLEANUP_ROUTINE)
_IRQL_requires_min_(PASSIVE_LEVEL)
_IRQL_requires_max_(APC_LEVEL)
_IRQL_requires_same_
VOID
KSIAPI
KSI_KCLEANUP_ROUTINE(
_In_ PKSI_KAPC Apc,
_In_ KSI_KAPC_CLEANUP_REASON Reason
);
typedef KSI_KCLEANUP_ROUTINE *PKSI_KCLEANUP_ROUTINE;
typedef
_Function_class_(KSI_KKERNEL_ROUTINE)
_IRQL_requires_(APC_LEVEL)
_IRQL_requires_same_
VOID
KSIAPI
KSI_KKERNEL_ROUTINE(
_In_ PKSI_KAPC Apc,
_Inout_ _Deref_pre_maybenull_ PKNORMAL_ROUTINE* NormalRoutine,
_Inout_ _Deref_pre_maybenull_ PVOID* NormalContext,
_Inout_ _Deref_pre_maybenull_ PVOID* SystemArgument1,
_Inout_ _Deref_pre_maybenull_ PVOID* SystemArgument2
);
typedef KSI_KKERNEL_ROUTINE *PKSI_KKERNEL_ROUTINE;
C_ASSERT(FIELD_OFFSET(KSI_KAPC, Apc) == 0);
KSISYSAPI
VOID
KSIAPI
KsiInitializeApc(
_Out_ PKSI_KAPC Apc,
_In_ PDRIVER_OBJECT DriverObject,
_In_ PRKTHREAD Thread,
_In_ KAPC_ENVIRONMENT Environment,
_In_ PKSI_KKERNEL_ROUTINE KernelRoutine,
_In_ PKSI_KCLEANUP_ROUTINE CleanupRoutine,
_In_opt_ PKNORMAL_ROUTINE NormalRoutine,
_In_ KPROCESSOR_MODE ApcMode,
_In_opt_ PVOID NormalContext
);
KSISYSAPI
BOOLEAN
KSIAPI
KsiInsertQueueApc(
_Inout_ PKSI_KAPC Apc,
_In_opt_ PVOID SystemArgument1,
_In_opt_ PVOID SystemArgument2,
_In_ KPRIORITY PriorityBoost
);
KSISYSAPI
NTSTATUS
KSIAPI
KsiRemoveQueueApc(
_Inout_ PKSI_KAPC Apc
);
typedef
_Function_class_(KSI_WORK_QUEUE_ROUTINE)
_IRQL_requires_max_(PASSIVE_LEVEL)
_IRQL_requires_same_
VOID
KSIAPI
KSI_WORK_QUEUE_ROUTINE(
_In_opt_ PVOID Parameter
);
typedef KSI_WORK_QUEUE_ROUTINE *PKSI_WORK_QUEUE_ROUTINE;
typedef struct _KSI_WORK_QUEUE_ITEM
{
WORK_QUEUE_ITEM WorkItem;
PDRIVER_OBJECT DriverObject;
PKSI_WORK_QUEUE_ROUTINE Routine;
PVOID Parameter;
} KSI_WORK_QUEUE_ITEM, *PKSI_WORK_QUEUE_ITEM;
_IRQL_requires_max_(DISPATCH_LEVEL)
KSISYSAPI
VOID
KSIAPI
KsiInitializeWorkItem(
_Out_ PKSI_WORK_QUEUE_ITEM WorkItem,
_In_ PDRIVER_OBJECT DriverObject,
_In_ PKSI_WORK_QUEUE_ROUTINE Routine,
_In_opt_ PVOID Parameter
);
_IRQL_requires_max_(DISPATCH_LEVEL)
KSISYSAPI
VOID
KSIAPI
KsiQueueWorkItem(
_Inout_ PKSI_WORK_QUEUE_ITEM WorkItem,
_In_ WORK_QUEUE_TYPE QueueType
);
extern KSISYSAPI PEPROCESS KsiSystemProcess;
_IRQL_requires_max_(PASSIVE_LEVEL)
KSISYSAPI
NTSTATUS
KSIAPI
KsiInitializeSystemProcess(
_In_ PCUNICODE_STRING ProcessName
);
// system
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KphSystemControl(
_In_ KPH_SYSTEM_CONTROL_CLASS SystemControlClass,
_In_reads_bytes_(SystemControlInfoLength) PVOID SystemControlInfo,
_In_ ULONG SystemControlInfoLength,
_In_ KPROCESSOR_MODE AccessMode
);
// alpc
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KphAlpcQueryInformation(
_In_ HANDLE ProcessHandle,
_In_ HANDLE PortHandle,
_In_ KPH_ALPC_INFORMATION_CLASS AlpcInformationClass,
_Out_writes_bytes_opt_(AlpcInformationLength) PVOID AlpcInformation,
_In_ ULONG AlpcInformationLength,
_Out_opt_ PULONG ReturnLength,
_In_ KPROCESSOR_MODE AccessMode
);
// file
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KphQueryInformationFile(
_In_ HANDLE ProcessHandle,
_In_ HANDLE FileHandle,
_In_ FILE_INFORMATION_CLASS FileInformationClass,
_Out_writes_bytes_(FileInformationLength) PVOID FileInformation,
_In_ ULONG FileInformationLength,
_Out_ PIO_STATUS_BLOCK IoStatusBlock,
_In_ KPROCESSOR_MODE AccessMode
);
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KphQueryVolumeInformationFile(
_In_ HANDLE ProcessHandle,
_In_ HANDLE FileHandle,
_In_ FS_INFORMATION_CLASS FsInformationClass,
_Out_writes_bytes_(FsInformationLength) PVOID FsInformation,
_In_ ULONG FsInformationLength,
_Out_ PIO_STATUS_BLOCK IoStatusBlock,
_In_ KPROCESSOR_MODE AccessMode
);
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KphCreateFile(
_Out_ PHANDLE FileHandle,
_In_ ACCESS_MASK DesiredAccess,
_In_ POBJECT_ATTRIBUTES ObjectAttributes,
_Out_ PIO_STATUS_BLOCK IoStatusBlock,
_In_opt_ PLARGE_INTEGER AllocationSize,
_In_ ULONG FileAttributes,
_In_ ULONG ShareAccess,
_In_ ULONG CreateDisposition,
_In_ ULONG CreateOptions,
_In_reads_bytes_opt_(EaLength) PVOID EaBuffer,
_In_ ULONG EaLength,
_In_ ULONG Options,
_In_ KPROCESSOR_MODE AccessMode
);
// knowndll
extern PVOID KphNtDllBaseAddress;
extern PVOID KphNtDllRtlSetBits;
_IRQL_requires_max_(PASSIVE_LEVEL)
NTSTATUS KphInitializeKnownDll(
VOID
);
// back_trace
#define KPH_STACK_BACK_TRACE_USER_MODE 0x00000001ul
#define KPH_STACK_BACK_TRACE_SKIP_KPH 0x00000002ul
#define KPH_STACK_BACK_TRACE_NO_SENTINEL 0x00000004ul
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KphInitializeStackBackTrace(
VOID
);
_IRQL_requires_max_(DISPATCH_LEVEL)
_Success_(return != 0)
ULONG KphCaptureStackBackTrace(
_In_ ULONG FramesToSkip,
_In_ ULONG FramesToCapture,
_Out_writes_(FramesToCapture) PVOID* BackTrace,
_Out_opt_ PULONG BackTraceHash,
_In_ ULONG Flags
);
_IRQL_requires_max_(APC_LEVEL)
_Must_inspect_result_
NTSTATUS KphCaptureStackBackTraceThread(
_In_ PETHREAD Thread,
_In_ ULONG FramesToSkip,
_In_ ULONG FramesToCapture,
_Out_writes_(FramesToCapture) PVOID* BackTrace,
_Out_ PULONG CapturedFrames,
_Out_opt_ PULONG BackTraceHash,
_In_ ULONG Flags,
_In_opt_ PLARGE_INTEGER Timeout
);
// session_token
typedef struct _KPH_SESSION_TOKEN
{
KPH_SESSION_ACCESS_TOKEN AccessToken;
LONG UseCount;
} KPH_SESSION_TOKEN, *PKPH_SESSION_TOKEN;
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KphRequestSessionAccessToken(
_Out_ PKPH_SESSION_ACCESS_TOKEN AccessToken,
_In_ PLARGE_INTEGER Expiry,
_In_ ULONG Privileges,
_In_ LONG Uses
);
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KphAssignProcessSessionToken(
_In_ HANDLE ProcessHandle,
_In_ PBYTE Signature,
_In_ ULONG SignatureLength,
_In_ KPROCESSOR_MODE AccessMode
);
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KphAssignThreadSessionToken(
_In_ HANDLE ThreadHandle,
_In_ PBYTE Signature,
_In_ ULONG SignatureLength,
_In_ KPROCESSOR_MODE AccessMode
);
_IRQL_requires_max_(APC_LEVEL)
_Must_inspect_result_
BOOLEAN KphSessionTokenPrivilegeCheck(
_In_ PKPH_THREAD_CONTEXT Thread,
_In_ ULONG Privileges
);
_IRQL_requires_max_(PASSIVE_LEVEL)
VOID KphInitializeSessionToken(
VOID
);
// ringbuff
typedef struct _KPH_RING_SECTION
{
PVOID SectionObject;
PMDL Mdl;
PVOID KernelBase;
} KPH_RING_SECTION, *PKPH_RING_SECTION;
typedef struct _KPH_RING_BUFFER
{
KSPIN_LOCK ProducerLock;
PULONG ProducerPos;
PULONG ConsumerPos;
PULONG ConsumerProcessing;
ULONG Length;
PVOID Buffer;
PKEVENT Event;
KPH_RING_SECTION ProducerSection;
KPH_RING_SECTION ConsumerSection;
PEPROCESS Process;
} KPH_RING_BUFFER, *PKPH_RING_BUFFER;
_Return_allocatesMem_size_(Length)
PVOID KphReserveRingBuffer(
_In_ PKPH_RING_BUFFER Ring,
_In_ ULONG Length
);
VOID KphCommitRingBuffer(
_In_ PKPH_RING_BUFFER Ring,
_In_freesMem_ PVOID Buffer
);
VOID KphDiscardRingBuffer(
_In_ PKPH_RING_BUFFER Ring,
_In_freesMem_ PVOID Buffer
);
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KphCreateRingBuffer(
_Out_ PKPH_RING_BUFFER* Ring,
_Out_ PKPH_RING_BUFFER_USER User,
_In_ ULONG Length,
_In_opt_ PKEVENT Event,
_In_ KPROCESSOR_MODE AccessMode
);
_IRQL_requires_max_(PASSIVE_LEVEL)
VOID KphInitializeRingBuffer(
VOID
);
// kphthread
typedef
_Function_class_(KPH_THREAD_START_ROUTINE)
_IRQL_requires_max_(PASSIVE_LEVEL)
_IRQL_requires_same_
NTSTATUS
KPH_THREAD_START_ROUTINE(
_In_opt_ PVOID Parameter
);
typedef KPH_THREAD_START_ROUTINE *PKPH_THREAD_START_ROUTINE;
#define KPH_CREATE_SYSTEM_THREAD_IN_KSI_PROCESS 0x00000001ul
_IRQL_requires_max_(PASSIVE_LEVEL)
NTSTATUS KphCreateSystemThread(
_Out_opt_ PHANDLE ThreadHandle,
_Out_opt_ PETHREAD* ThreadObject,
_In_ PKPH_THREAD_START_ROUTINE StartRoutine,
_In_opt_ _When_(return >= 0, __drv_aliasesMem) PVOID Parameter,
_In_opt_ PCUNICODE_STRING ThreadName,
_In_ ULONG Flags
);
_IRQL_requires_max_(PASSIVE_LEVEL)
VOID KphCleanupThreading(
VOID
);
_IRQL_requires_max_(PASSIVE_LEVEL)
VOID KphInitializeThreading(
VOID
);
// umaccess
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KphCaptureUnicodeString(
_In_ PUNICODE_STRING UnicodeString,
_Out_ PUNICODE_STRING* CapturedUnicodeString
);
_IRQL_requires_max_(PASSIVE_LEVEL)
VOID KphReleaseUnicodeString(
_In_ PUNICODE_STRING UnicodeString
);
_IRQL_requires_max_(APC_LEVEL)
_Must_inspect_result_
NTSTATUS KphZeroModeMemory(
_Out_writes_bytes_all_(Length) PVOID Destination,
_In_ SIZE_T Length,
_In_ KPROCESSOR_MODE AccessMode
);
_IRQL_requires_max_(APC_LEVEL)
NTSTATUS KphCopyToMode(
_Out_writes_bytes_all_(Length) PVOID Destination,
_In_reads_bytes_(Length) PVOID Source,
_In_ SIZE_T Length,
_In_ KPROCESSOR_MODE AccessMode
);
_IRQL_requires_max_(APC_LEVEL)
_Must_inspect_result_
NTSTATUS KphCopyFromMode(
_Out_writes_bytes_all_(Length) PVOID Destination,
_In_reads_bytes_(Length) PVOID Source,
_In_ SIZE_T Length,
_In_ KPROCESSOR_MODE AccessMode
);
_IRQL_requires_max_(APC_LEVEL)
NTSTATUS KphWriteUCharToMode(
_Out_ PUCHAR Destination,
_In_ UCHAR Source,
_In_ KPROCESSOR_MODE AccessMode
);
_IRQL_requires_max_(APC_LEVEL)
NTSTATUS KphWriteULongToMode(
_Out_ PULONG Destination,
_In_ ULONG Source,
_In_ KPROCESSOR_MODE AccessMode
);
_IRQL_requires_max_(APC_LEVEL)
NTSTATUS KphWriteULong64ToMode(
_Out_ PULONG64 Destination,
_In_ ULONG64 Source,
_In_ KPROCESSOR_MODE AccessMode
);
_IRQL_requires_max_(APC_LEVEL)
NTSTATUS KphWriteLong64ToMode(
_Out_ PLONG64 Destination,
_In_ LONG64 Source,
_In_ KPROCESSOR_MODE AccessMode
);
_IRQL_requires_max_(APC_LEVEL)
NTSTATUS KphWriteSizeTToMode(
_Out_ PSIZE_T Destination,
_In_ SIZE_T Source,
_In_ KPROCESSOR_MODE AccessMode
);
_IRQL_requires_max_(APC_LEVEL)
NTSTATUS KphWritePointerToMode(
_Out_ PVOID* Destination,
_In_ PVOID Source,
_In_ KPROCESSOR_MODE AccessMode
);
_IRQL_requires_max_(APC_LEVEL)
NTSTATUS KphWriteHandleToMode(
_Out_ PHANDLE Destination,
_In_ _Post_ptr_invalid_ HANDLE Source,
_In_ KPROCESSOR_MODE AccessMode
);
_IRQL_requires_max_(APC_LEVEL)
NTSTATUS KphCopyUnicodeStringToMode(
_Out_writes_bytes_opt_(Length) PVOID Destination,
_In_ SIZE_T Length,
_In_opt_ PCUNICODE_STRING String,
_Out_ PULONG ReturnLength,
_In_ KPROCESSOR_MODE AccessMode
);
_IRQL_requires_max_(APC_LEVEL)
_Must_inspect_result_
NTSTATUS KphReadLargeIntegerFromMode(
_Out_ PLARGE_INTEGER Destination,
_In_ PLARGE_INTEGER Source,
_In_ KPROCESSOR_MODE AccessMode
);
// ratelmt
typedef union _KPH_RATE_BUCKET
{
struct
{
ULONG CurrentTokens;
ULONG LastRefillTime;
};
LONG64 Quad;
} KPH_RATE_BUCKET, *PKPH_RATE_BUCKET;
typedef struct _KPH_RATE_LIMIT
{
KPH_RATE_BUCKET Bucket;
KPH_RATE_LIMIT_POLICY Policy;
LONG64 Allowed;
LONG64 Dropped;
LONG64 CasMiss;
} KPH_RATE_LIMIT, *PKPH_RATE_LIMIT;
_IRQL_requires_max_(DISPATCH_LEVEL)
VOID KphInitializeRateLimit(
_In_ PCKPH_RATE_LIMIT_POLICY Policy,
_In_ PLARGE_INTEGER TimeStamp,
_Out_ PKPH_RATE_LIMIT RateLimit
);
_IRQL_requires_max_(DISPATCH_LEVEL)
BOOLEAN KphRateLimitConsumeToken(
_Inout_ PKPH_RATE_LIMIT RateLimit,
_In_ PLARGE_INTEGER TimeStamp
);
================================================
FILE: KSystemInformer/include/ntfill.h
================================================
/*
* This file is part of System Informer.
*
* Authors:
*
* jxy-s 2022-2026
*
*/
#pragma once
typedef struct _CLIENT_ID32
{
ULONG UniqueProcess;
ULONG UniqueThread;
} CLIENT_ID32, *PCLIENT_ID32;
typedef struct _CLIENT_ID64
{
ULONGLONG UniqueProcess;
ULONGLONG UniqueThread;
} CLIENT_ID64, *PCLIENT_ID64;
typedef const STRING32 *PCUNICODE_STRING32;
// EX
typedef struct _EX_FAST_REF
{
union
{
PVOID Object;
ULONG_PTR RefCnt : 4;
ULONG_PTR Value;
};
} EX_FAST_REF, *PEX_FAST_REF;
typedef struct _EX_PUSH_LOCK_WAIT_BLOCK *PEX_PUSH_LOCK_WAIT_BLOCK;
NTKERNELAPI
VOID
FASTCALL
ExfUnblockPushLock(
_Inout_ PEX_PUSH_LOCK PushLock,
_Inout_opt_ PEX_PUSH_LOCK_WAIT_BLOCK WaitBlock
);
/*
0:000> dt ntoskrnl!_HANDLE_TABLE_ENTRY
+0x000 VolatileLowValue : Int8B
+0x000 LowValue : Int8B
+0x000 InfoTable : Ptr64 _HANDLE_TABLE_ENTRY_INFO
+0x008 HighValue : Int8B
+0x008 NextFreeHandleEntry : Ptr64 _HANDLE_TABLE_ENTRY
+0x008 LeafHandleValue : _EXHANDLE
+0x000 RefCountField : Int8B
+0x000 Unlocked : Pos 0, 1 Bit
+0x000 RefCnt : Pos 1, 16 Bits
+0x000 Attributes : Pos 17, 3 Bits
+0x000 ObjectPointerBits : Pos 20, 44 Bits
+0x008 GrantedAccessBits : Pos 0, 25 Bits
+0x008 NoRightsUpgrade : Pos 25, 1 Bit
+0x008 Spare1 : Pos 26, 6 Bits
+0x00c Spare2 : Uint4B
0:000> dt ntkrla57!_HANDLE_TABLE_ENTRY
+0x000 VolatileLowValue : Int8B
+0x000 LowValue : Int8B
+0x000 InfoTable : Ptr64 _HANDLE_TABLE_ENTRY_INFO
+0x008 HighValue : Int8B
+0x008 NextFreeHandleEntry : Ptr64 _HANDLE_TABLE_ENTRY
+0x008 LeafHandleValue : _EXHANDLE
+0x000 RefCountField : Int8B
+0x000 Unlocked : Pos 0, 1 Bit
+0x000 RefCnt : Pos 1, 7 Bits
+0x000 Attributes : Pos 8, 3 Bits
+0x000 ObjectPointerBits : Pos 11, 53 Bits
+0x008 GrantedAccessBits : Pos 0, 25 Bits
+0x008 NoRightsUpgrade : Pos 25, 1 Bit
+0x008 Spare1 : Pos 26, 6 Bits
+0x00c Spare2 : Uint4B
*/
//
// N.B. We define HANDLE_TABLE_ENTRY this way to allow for dynamic data to
// support different kernels (see above). The number of ObjectPointerBits are
// different for LA57.
//
typedef struct _HANDLE_TABLE_ENTRY
{
union
{
PVOID Object;
ULONG ObAttributes;
ULONG_PTR Value;
};
union
{
ACCESS_MASK GrantedAccess;
LONG NextFreeTableEntry;
};
} HANDLE_TABLE_ENTRY, *PHANDLE_TABLE_ENTRY;
typedef struct _HANDLE_TABLE HANDLE_TABLE, *PHANDLE_TABLE;
typedef
_Function_class_(EX_ENUM_HANDLE_CALLBACK)
_Must_inspect_result_
BOOLEAN
NTAPI
EX_ENUM_HANDLE_CALLBACK(
_In_ PHANDLE_TABLE HandleTable,
_Inout_ PHANDLE_TABLE_ENTRY HandleTableEntry,
_In_ HANDLE Handle,
_In_opt_ PVOID Context
);
typedef EX_ENUM_HANDLE_CALLBACK* PEX_ENUM_HANDLE_CALLBACK;
NTKERNELAPI
BOOLEAN
NTAPI
ExEnumHandleTable(
_In_ PHANDLE_TABLE HandleTable,
_In_ PEX_ENUM_HANDLE_CALLBACK EnumHandleProcedure,
_Inout_ PVOID Context,
_Out_opt_ PHANDLE Handle
);
NTSYSCALLAPI
NTSTATUS
NTAPI
ZwQuerySystemInformation(
_In_ SYSTEM_INFORMATION_CLASS SystemInformationClass,
_Out_writes_bytes_opt_(SystemInformationLength) PVOID SystemInformation,
_In_ ULONG SystemInformationLength,
_Out_opt_ PULONG ReturnLength
);
NTSYSCALLAPI
NTSTATUS
NTAPI
ZwQuerySection(
_In_ HANDLE SectionHandle,
_In_ SECTION_INFORMATION_CLASS SectionInformationClass,
_Out_writes_bytes_(SectionInformationLength) PVOID SectionInformation,
_In_ SIZE_T SectionInformationLength,
_Out_opt_ PSIZE_T ReturnLength
);
// IO
extern POBJECT_TYPE *IoDriverObjectType;
extern POBJECT_TYPE *IoDeviceObjectType;
typedef
_Function_class_(IO_CHECK_FILE_OBJECT_OPENED_AS_COPY_SOURCE)
BOOLEAN
NTAPI
IO_CHECK_FILE_OBJECT_OPENED_AS_COPY_SOURCE(
_In_ PFILE_OBJECT FileObject
);
typedef IO_CHECK_FILE_OBJECT_OPENED_AS_COPY_SOURCE* PIO_CHECK_FILE_OBJECT_OPENED_AS_COPY_SOURCE;
typedef
_Function_class_(IO_CHECK_FILE_OBJECT_OPENED_AS_COPY_DESTINATION)
BOOLEAN
IO_CHECK_FILE_OBJECT_OPENED_AS_COPY_DESTINATION(
_In_ PFILE_OBJECT FileObject
);
typedef IO_CHECK_FILE_OBJECT_OPENED_AS_COPY_DESTINATION* PIO_CHECK_FILE_OBJECT_OPENED_AS_COPY_DESTINATION;
typedef struct _COPY_INFORMATION
{
PFILE_OBJECT SourceFileObject;
LONGLONG SourceFileOffset;
} COPY_INFORMATION, *PCOPY_INFORMATION;
typedef
_Function_class_(IO_GET_COPY_INFORMATION_EXTENSION)
NTSTATUS
IO_GET_COPY_INFORMATION_EXTENSION(
_In_ PIRP Irp,
_Out_ PCOPY_INFORMATION CopyInformation
);
typedef IO_GET_COPY_INFORMATION_EXTENSION* PIO_GET_COPY_INFORMATION_EXTENSION;
// FLT
typedef
_Function_class_(FLT_GET_COPY_INFORMATION_FROM_CALLBACK_DATA)
_IRQL_requires_max_(DISPATCH_LEVEL)
NTSTATUS
FLTAPI
FLT_GET_COPY_INFORMATION_FROM_CALLBACK_DATA(
_In_ PFLT_CALLBACK_DATA Data,
_Out_ PCOPY_INFORMATION CopyInformation
);
typedef FLT_GET_COPY_INFORMATION_FROM_CALLBACK_DATA* PFLT_GET_COPY_INFORMATION_FROM_CALLBACK_DATA;
// KE
typedef enum _KAPC_ENVIRONMENT
{
OriginalApcEnvironment,
AttachedApcEnvironment,
CurrentApcEnvironment,
InsertApcEnvironment
} KAPC_ENVIRONMENT, *PKAPC_ENVIRONMENT;
typedef
_Function_class_(KNORMAL_ROUTINE)
_IRQL_requires_(PASSIVE_LEVEL)
_IRQL_requires_same_
VOID
NTAPI
KNORMAL_ROUTINE(
_In_opt_ PVOID NormalContext,
_In_opt_ PVOID SystemArgument1,
_In_opt_ PVOID SystemArgument2
);
typedef KNORMAL_ROUTINE *PKNORMAL_ROUTINE;
typedef
_Function_class_(KRUNDOWN_ROUTINE)
_IRQL_requires_(PASSIVE_LEVEL)
_IRQL_requires_same_
VOID
NTAPI
KRUNDOWN_ROUTINE(
_In_ PRKAPC Apc
);
typedef KRUNDOWN_ROUTINE *PKRUNDOWN_ROUTINE;
typedef
_Function_class_(KKERNEL_ROUTINE)
_IRQL_requires_(APC_LEVEL)
_IRQL_requires_same_
VOID
NTAPI
KKERNEL_ROUTINE(
_In_ PRKAPC Apc,
_Inout_ _Deref_pre_maybenull_ PKNORMAL_ROUTINE *NormalRoutine,
_Inout_ _Deref_pre_maybenull_ PVOID* NormalContext,
_Inout_ _Deref_pre_maybenull_ PVOID* SystemArgument1,
_Inout_ _Deref_pre_maybenull_ PVOID* SystemArgument2
);
typedef KKERNEL_ROUTINE *PKKERNEL_ROUTINE;
NTKERNELAPI
VOID
NTAPI
KeInitializeApc(
_Out_ PRKAPC Apc,
_In_ PRKTHREAD Thread,
_In_ KAPC_ENVIRONMENT Environment,
_In_ PKKERNEL_ROUTINE KernelRoutine,
_In_opt_ PKRUNDOWN_ROUTINE RundownRoutine,
_In_opt_ PKNORMAL_ROUTINE NormalRoutine,
_In_ KPROCESSOR_MODE Mode,
_In_opt_ PVOID NormalContext
);
NTKERNELAPI
BOOLEAN
NTAPI
KeInsertQueueApc(
_Inout_ PRKAPC Apc,
_In_opt_ PVOID SystemArgument1,
_In_opt_ PVOID SystemArgument2,
_In_ KPRIORITY Increment
);
NTKERNELAPI
BOOLEAN
NTAPI
KeTestAlertThread(
_In_ KPROCESSOR_MODE Mode
);
typedef
_Function_class_(KE_REMOVE_QUEUE_APC)
BOOLEAN
NTAPI
KE_REMOVE_QUEUE_APC(
_Inout_ PKAPC Apc
);
typedef KE_REMOVE_QUEUE_APC* PKE_REMOVE_QUEUE_APC;
// OB
// These definitions are no longer correct, but they produce correct results.
#define OBJ_PROTECT_CLOSE 0x00000001
#define OBJ_HANDLE_ATTRIBUTES (OBJ_PROTECT_CLOSE | OBJ_INHERIT | OBJ_AUDIT_OBJECT_CLOSE)
// This attribute is now stored in the GrantedAccess field.
#define ObpAccessProtectCloseBit 0x2000000
// GrantedAccess in the table entry is the low 25 bits
#define OBJ_GRANTED_ACCESS_MASK 0x01ffffff
#define ObpDecodeGrantedAccess(Access) ((Access) & OBJ_GRANTED_ACCESS_MASK)
FORCEINLINE
VOID
ObpSetGrantedAccess(
_Inout_ PACCESS_MASK GrantedAccess,
_In_ ACCESS_MASK Access
)
{
//
// Preserve the high bits and only set the low 25 bits.
//
*GrantedAccess = (Access | (*GrantedAccess & ~OBJ_GRANTED_ACCESS_MASK));
}
typedef struct _OBJECT_CREATE_INFORMATION OBJECT_CREATE_INFORMATION, *POBJECT_CREATE_INFORMATION;
typedef struct _OBJECT_HEADER
{
SSIZE_T PointerCount;
union
{
SSIZE_T HandleCount;
PVOID NextToFree;
};
EX_PUSH_LOCK Lock;
UCHAR TypeIndex;
union
{
UCHAR TraceFlags;
struct
{
UCHAR DbgRefTrace : 1;
UCHAR DbgTracePermanent : 1;
};
};
UCHAR InfoMask;
union
{
UCHAR Flags;
struct
{
UCHAR NewObject : 1;
UCHAR KernelObject : 1;
UCHAR KernelOnlyAccess : 1;
UCHAR ExclusiveObject : 1;
UCHAR PermanentObject : 1;
UCHAR DefaultSecurityQuota : 1;
UCHAR SingleHandleEntry : 1;
UCHAR DeletedInline : 1;
};
};
#ifdef _WIN64
ULONG Reserved;
#endif
union
{
POBJECT_CREATE_INFORMATION ObjectCreateInfo;
PVOID QuotaBlockCharged;
};
PVOID SecurityDescriptor;
QUAD Body;
} OBJECT_HEADER, *POBJECT_HEADER;
#if defined(_M_X64) || defined(_M_ARM64)
C_ASSERT(FIELD_OFFSET(OBJECT_HEADER, Body) == 0x030);
C_ASSERT(sizeof(OBJECT_HEADER) == 0x038);
#else
C_ASSERT(FIELD_OFFSET(OBJECT_HEADER, Body) == 0x018);
C_ASSERT(sizeof(OBJECT_HEADER) == 0x020);
#endif
#define OBJECT_TO_OBJECT_HEADER(Object) CONTAINING_RECORD((Object), OBJECT_HEADER, Body)
NTKERNELAPI
POBJECT_TYPE
NTAPI
ObGetObjectType(
_In_ PVOID Object
);
NTKERNELAPI
NTSTATUS
NTAPI
ObOpenObjectByName(
_In_ POBJECT_ATTRIBUTES ObjectAttributes,
_In_ POBJECT_TYPE ObjectType,
_In_ KPROCESSOR_MODE AccessMode,
_In_opt_ PACCESS_STATE AccessState,
_In_ ACCESS_MASK DesiredAccess,
_In_opt_ PVOID ParseContext,
_Out_ PHANDLE Handle
);
NTKERNELAPI
NTSTATUS
NTAPI
ObSetHandleAttributes(
_In_ HANDLE Handle,
_In_ POBJECT_HANDLE_FLAG_INFORMATION HandleFlags,
_In_ KPROCESSOR_MODE AccessMode
);
NTKERNELAPI
NTSTATUS
NTAPI
ObDuplicateObject(
_In_ PEPROCESS SourceProcess,
_In_ HANDLE SourceHandle,
_In_opt_ PEPROCESS TargetProcess,
_Out_opt_ PHANDLE TargetHandle,
_In_ ACCESS_MASK DesiredAccess,
_In_ ULONG HandleAttributes,
_In_ ULONG Options,
_In_ KPROCESSOR_MODE PreviousMode
);
// CM
#if (NTDDI_VERSION < NTDDI_WIN10_FE)
typedef struct _REG_SAVE_MERGED_KEY_INFORMATION
{
PVOID Object;
HANDLE FileHandle;
PVOID HighKeyObject;
PVOID LowKeyObject;
PVOID CallContext;
PVOID ObjectContext;
PVOID Reserved;
} REG_SAVE_MERGED_KEY_INFORMATION, *PREG_SAVE_MERGED_KEY_INFORMATION;
#endif
// LDR
#define IS_INTRESOURCE(_r) ((((ULONG_PTR)(_r)) >> 16) == 0)
#define MAKEINTRESOURCEA(i) ((LPSTR)((ULONG_PTR)((WORD)(i))))
#define MAKEINTRESOURCEW(i) ((LPWSTR)((ULONG_PTR)((WORD)(i))))
#ifdef UNICODE
#define MAKEINTRESOURCE MAKEINTRESOURCEW
#else
#define MAKEINTRESOURCE MAKEINTRESOURCEA
#endif
#define RT_VERSION MAKEINTRESOURCE(16)
#define VS_FILE_INFO RT_VERSION
#define VS_VERSION_INFO 1
#define VS_FFI_SIGNATURE 0xFEEF04BDL
typedef struct _IMAGE_RESOURCE_DATA_ENTRY IMAGE_RESOURCE_DATA_ENTRY, *PIMAGE_RESOURCE_DATA_ENTRY;
NTKERNELAPI
NTSTATUS
NTAPI
LdrAccessResource(
_In_ PVOID DllHandle,
_In_ PIMAGE_RESOURCE_DATA_ENTRY ResourceDataEntry,
_Out_opt_ PVOID *ResourceBuffer,
_Out_opt_ ULONG *ResourceLength
);
typedef struct _LDR_RESOURCE_INFO
{
ULONG_PTR Type;
ULONG_PTR Name;
ULONG_PTR Language;
} LDR_RESOURCE_INFO, *PLDR_RESOURCE_INFO;
#define RESOURCE_TYPE_LEVEL 0
#define RESOURCE_NAME_LEVEL 1
#define RESOURCE_LANGUAGE_LEVEL 2
#define RESOURCE_DATA_LEVEL 3
NTKERNELAPI
NTSTATUS
NTAPI
LdrFindResource_U(
_In_ PVOID DllHandle,
_In_ PLDR_RESOURCE_INFO ResourceInfo,
_In_ ULONG Level,
_Out_ PIMAGE_RESOURCE_DATA_ENTRY *ResourceDataEntry
);
typedef struct _VS_VERSION_INFO_STRUCT
{
USHORT Length;
USHORT ValueLength;
USHORT Type;
WCHAR Key[1];
} VS_VERSION_INFO_STRUCT, *PVS_VERSION_INFO_STRUCT;
typedef struct _FIXEDFILEINFO
{
DWORD dwSignature;
DWORD dwStrucVersion;
DWORD dwFileVersionMS;
DWORD dwFileVersionLS;
DWORD dwProductVersionMS;
DWORD dwProductVersionLS;
DWORD dwFileFlagsMask;
DWORD dwFileFlags;
DWORD dwFileOS;
DWORD dwFileType;
DWORD dwFileSubtype;
DWORD dwFileDateMS;
DWORD dwFileDateLS;
} VS_FIXEDFILEINFO, *PVS_FIXEDFILEINFO;
typedef struct _NON_PAGED_DEBUG_INFO NON_PAGED_DEBUG_INFO, *PNON_PAGED_DEBUG_INFO;
typedef struct _KLDR_DATA_TABLE_ENTRY
{
LIST_ENTRY InLoadOrderLinks;
PVOID ExceptionTable;
ULONG ExceptionTableSize;
PVOID GpValue;
PNON_PAGED_DEBUG_INFO NonPagedDebugInfo;
PVOID DllBase;
PVOID EntryPoint;
ULONG SizeOfImage;
UNICODE_STRING FullDllName;
UNICODE_STRING BaseDllName;
ULONG Flags;
USHORT LoadCount;
union
{
USHORT SignatureLevel : 4;
USHORT SignatureType : 3;
USHORT Frozen : 2;
USHORT HotPatch : 1;
USHORT Unused : 6;
USHORT EntireField;
} u1;
PVOID SectionPointer;
ULONG CheckSum;
ULONG CoverageSectionSize;
PVOID CoverageSection;
PVOID LoadedImports;
union
{
PVOID Spare;
struct _KLDR_DATA_TABLE_ENTRY* NtDataTableEntry; // win11
};
ULONG SizeOfImageNotRounded;
ULONG TimeDateStamp;
} KLDR_DATA_TABLE_ENTRY, *PKLDR_DATA_TABLE_ENTRY;
// PS
NTSYSCALLAPI
NTSTATUS
NTAPI
ZwQueryInformationProcess(
_In_ HANDLE ProcessHandle,
_In_ PROCESSINFOCLASS ProcessInformationClass,
_Out_writes_bytes_(ProcessInformationLength) PVOID ProcessInformation,
_In_ ULONG ProcessInformationLength,
_Out_opt_ PULONG ReturnLength
);
NTSYSCALLAPI
NTSTATUS
NTAPI
ZwQueryInformationThread(
_In_ HANDLE ThreadHandle,
_In_ THREADINFOCLASS ThreadInformationClass,
_Out_writes_bytes_(ThreadInformationLength) PVOID ThreadInformation,
_In_ ULONG ThreadInformationLength,
_Out_opt_ PULONG ReturnLength
);
NTKERNELAPI
NTSTATUS
NTAPI
PsLookupProcessThreadByCid(
_In_ PCLIENT_ID ClientId,
_Out_opt_ PEPROCESS *Process,
_Out_ PETHREAD *Thread
);
typedef struct _EJOB *PEJOB;
extern POBJECT_TYPE *PsJobType;
NTKERNELAPI
PEJOB
NTAPI
PsGetProcessJob(
_In_ PEPROCESS Process
);
_When_(NT_SUCCESS(return), _Acquires_lock_(Process))
NTKERNELAPI
NTSTATUS
NTAPI
PsAcquireProcessExitSynchronization(
_In_ PEPROCESS Process
);
_Releases_lock_(Process)
NTKERNELAPI
VOID
NTAPI
PsReleaseProcessExitSynchronization(
_In_ PEPROCESS Process
);
NTKERNELAPI
PVOID
NTAPI
PsGetProcessSectionBaseAddress(
_In_ PEPROCESS Process
);
NTKERNELAPI
BOOLEAN
NTAPI
PsGetProcessExitProcessCalled(
_In_ PEPROCESS Process
);
NTKERNELAPI
NTSTATUS
NTAPI
PsSuspendProcess(
_In_ PEPROCESS Process
);
NTKERNELAPI
NTSTATUS
NTAPI
PsResumeProcess(
_In_ PEPROCESS Process
);
typedef struct _PS_PROTECTION
{
union
{
UCHAR Level;
struct
{
UCHAR Type : 3;
UCHAR Audit : 1; // Reserved
UCHAR Signer : 4;
};
};
} PS_PROTECTION, *PPS_PROTECTION;
typedef enum _PS_PROTECTED_TYPE
{
PsProtectedTypeNone = 0,
PsProtectedTypeProtectedLight = 1,
PsProtectedTypeProtected = 2
} PS_PROTECTED_TYPE, *PPS_PROTECTED_TYPE;
typedef enum _PS_PROTECTED_SIGNER
{
PsProtectedSignerNone = 0,
PsProtectedSignerAuthenticode,
PsProtectedSignerCodeGen,
PsProtectedSignerAntimalware,
PsProtectedSignerLsa,
PsProtectedSignerWindows,
PsProtectedSignerWinTcb,
PsProtectedSignerWinSystem,
PsProtectedSignerApp,
PsProtectedSignerMax
} PS_PROTECTED_SIGNER, *PPS_PROTECTED_SIGNER;
#if (NTDDI_VERSION >= NTDDI_WIN10)
NTKERNELAPI
PS_PROTECTION
NTAPI
PsGetProcessProtection(
_In_ PEPROCESS Process
);
#endif
typedef
_Function_class_(PS_SET_LOAD_IMAGE_NOTIFY_ROUTINE_EX)
NTSTATUS
NTAPI
PS_SET_LOAD_IMAGE_NOTIFY_ROUTINE_EX(
_In_ PLOAD_IMAGE_NOTIFY_ROUTINE NotifyRoutine,
_In_ ULONG_PTR Flags
);
typedef PS_SET_LOAD_IMAGE_NOTIFY_ROUTINE_EX* PPS_SET_LOAD_IMAGE_NOTIFY_ROUTINE_EX;
#if (NTDDI_VERSION < NTDDI_WIN10_RS2)
typedef enum _PSCREATEPROCESSNOTIFYTYPE
{
PsCreateProcessNotifySubsystems = 0
} PSCREATEPROCESSNOTIFYTYPE;
#endif
typedef
_Function_class_(PS_SET_CREATE_PROCESS_NOTIFY_ROUTINE_EX2)
NTSTATUS
NTAPI
PS_SET_CREATE_PROCESS_NOTIFY_ROUTINE_EX2(
_In_ PSCREATEPROCESSNOTIFYTYPE NotifyType,
_In_ PVOID NotifyInformation,
_In_ BOOLEAN Remove
);
typedef PS_SET_CREATE_PROCESS_NOTIFY_ROUTINE_EX2* PPS_SET_CREATE_PROCESS_NOTIFY_ROUTINE_EX2;
#ifndef PS_IMAGE_NOTIFY_CONFLICTING_ARCHITECTURE
#define PS_IMAGE_NOTIFY_CONFLICTING_ARCHITECTURE 0x1
#endif
NTKERNELAPI
_Must_inspect_result_
NTSTATUS
NTAPI
PsReferenceProcessFilePointer(
_In_ PEPROCESS Process,
_Out_ PFILE_OBJECT* FileObject
);
NTKERNELAPI
HANDLE
NTAPI
PsGetProcessInheritedFromUniqueProcessId(
_In_ PEPROCESS Process
);
#ifdef _WIN64
NTKERNELAPI
PVOID
NTAPI
PsGetProcessWow64Process(
_In_ PEPROCESS Process
);
NTKERNELAPI
PVOID
NTAPI
PsGetCurrentProcessWow64Process(
VOID
);
#endif
NTKERNELAPI
PVOID
NTAPI
PsGetThreadTeb(
_In_ PETHREAD Thread
);
NTKERNELAPI
BOOLEAN
NTAPI
PsIsProcessBeingDebugged(
_In_ PEPROCESS Process
);
NTKERNELAPI
NTSTATUS
NTAPI
ZwSetInformationProcess(
_In_ HANDLE ProcessHandle,
_In_ PROCESSINFOCLASS ProcessInformationClass,
_In_reads_bytes_(ProcessInformationLength) PVOID ProcessInformation,
_In_ ULONG ProcessInformationLength
);
#define ProcessPowerThrottlingState 77
#define ProcessPriorityClassEx 108
#define ThreadPowerThrottlingState 49
#define ThreadNameInformation 38
#define ThreadExplicitCaseSensitivity 43
#if (NTDDI_VERSION >= NTDDI_WIN10)
extern PLIST_ENTRY PsLoadedModuleList;
extern PERESOURCE PsLoadedModuleResource;
#endif
typedef struct _PROCESS_MITIGATION_POLICY_INFORMATION
{
PROCESS_MITIGATION_POLICY Policy;
union
{
PROCESS_MITIGATION_ASLR_POLICY ASLRPolicy;
PROCESS_MITIGATION_STRICT_HANDLE_CHECK_POLICY StrictHandleCheckPolicy;
PROCESS_MITIGATION_SYSTEM_CALL_DISABLE_POLICY SystemCallDisablePolicy;
PROCESS_MITIGATION_EXTENSION_POINT_DISABLE_POLICY ExtensionPointDisablePolicy;
PROCESS_MITIGATION_DYNAMIC_CODE_POLICY DynamicCodePolicy;
PROCESS_MITIGATION_CONTROL_FLOW_GUARD_POLICY ControlFlowGuardPolicy;
PROCESS_MITIGATION_BINARY_SIGNATURE_POLICY SignaturePolicy;
PROCESS_MITIGATION_FONT_DISABLE_POLICY FontDisablePolicy;
PROCESS_MITIGATION_IMAGE_LOAD_POLICY ImageLoadPolicy;
PROCESS_MITIGATION_SYSTEM_CALL_FILTER_POLICY SystemCallFilterPolicy;
PROCESS_MITIGATION_PAYLOAD_RESTRICTION_POLICY PayloadRestrictionPolicy;
PROCESS_MITIGATION_CHILD_PROCESS_POLICY ChildProcessPolicy;
PROCESS_MITIGATION_SIDE_CHANNEL_ISOLATION_POLICY SideChannelIsolationPolicy;
PROCESS_MITIGATION_USER_SHADOW_STACK_POLICY UserShadowStackPolicy;
PROCESS_MITIGATION_REDIRECTION_TRUST_POLICY RedirectionTrustPolicy;
PROCESS_MITIGATION_USER_POINTER_AUTH_POLICY UserPointerAuthPolicy;
PROCESS_MITIGATION_SEHOP_POLICY SEHOPPolicy;
};
} PROCESS_MITIGATION_POLICY_INFORMATION, *PPROCESS_MITIGATION_POLICY_INFORMATION;
NTKERNELAPI
PUCHAR
NTAPI
PsGetProcessImageFileName(
_In_ PEPROCESS Process
);
typedef
_Function_class_(PS_GET_PROCESS_SEQUENCE_NUMBER)
_IRQL_requires_max_(DISPATCH_LEVEL)
ULONGLONG
PS_GET_PROCESS_SEQUENCE_NUMBER(
_In_ PEPROCESS Process
);
typedef PS_GET_PROCESS_SEQUENCE_NUMBER* PPS_GET_PROCESS_SEQUENCE_NUMBER;
typedef
_Function_class_(PS_GET_PROCESS_START_KEY)
_IRQL_requires_max_(DISPATCH_LEVEL)
ULONGLONG
PS_GET_PROCESS_START_KEY(
_In_ PEPROCESS Process
);
typedef PS_GET_PROCESS_START_KEY* PPS_GET_PROCESS_START_KEY;
typedef struct _PROCESS_TELEMETRY_ID_INFORMATION
{
ULONG HeaderSize;
ULONG ProcessId;
ULONGLONG ProcessStartKey;
ULONGLONG CreateTime;
ULONGLONG CreateInterruptTime;
ULONGLONG CreateUnbiasedInterruptTime;
ULONGLONG ProcessSequenceNumber;
ULONGLONG SessionCreateTime;
ULONG SessionId;
ULONG BootId;
ULONG ImageChecksum;
ULONG ImageTimeDateStamp;
ULONG UserSidOffset;
ULONG ImagePathOffset;
ULONG PackageNameOffset;
ULONG RelativeAppNameOffset;
ULONG CommandLineOffset;
} PROCESS_TELEMETRY_ID_INFORMATION, *PPROCESS_TELEMETRY_ID_INFORMATION;
#define PROCESS_CREATE_FLAGS_MINIMAL_PROCESS 0x00000800 // NtCreateProcessEx only
typedef
_Function_class_(ZW_CREATE_PROCESS_EX)
NTSYSCALLAPI
NTSTATUS
NTAPI
ZW_CREATE_PROCESS_EX(
_Out_ PHANDLE ProcessHandle,
_In_ ACCESS_MASK DesiredAccess,
_In_opt_ PCOBJECT_ATTRIBUTES ObjectAttributes,
_In_ HANDLE ParentProcess,
_In_ ULONG Flags, // PROCESS_CREATE_FLAGS_*
_In_opt_ HANDLE SectionHandle,
_In_opt_ HANDLE DebugPort,
_In_opt_ HANDLE TokenHandle,
_Reserved_ ULONG Reserved // JobMemberLevel
);
typedef ZW_CREATE_PROCESS_EX* PZW_CREATE_PROCESS_EX;
// RTL
#ifndef RTL_MAX_DRIVE_LETTERS
#define RTL_MAX_DRIVE_LETTERS 32
#endif
#define RTL_WALK_USER_MODE_STACK 0x00000001
#define RTL_WALK_VALID_FLAGS 0x00000001
#define RTL_STACK_WALKING_MODE_FRAMES_TO_SKIP_SHIFT 8
NTKERNELAPI
PIMAGE_NT_HEADERS
NTAPI
RtlImageNtHeader(
_In_ PVOID Base
);
#define RTL_IMAGE_NT_HEADER_EX_FLAG_NO_RANGE_CHECK 0x00000001
NTKERNELAPI
NTSTATUS
NTAPI
RtlImageNtHeaderEx(
_In_ ULONG Flags,
_In_ PVOID Base,
_In_ ULONG64 Size,
_Out_ PIMAGE_NT_HEADERS* OutHeaders
);
NTKERNELAPI
PVOID
NTAPI
RtlImageDirectoryEntryToData(
_In_ PVOID BaseAddress,
_In_ BOOLEAN MappedAsImage,
_In_ USHORT Directory,
_Out_ PULONG Size
);
#if (NTDDI_VERSION >= NTDDI_WIN10)
NTKERNELAPI
PVOID
NTAPI
RtlFindExportedRoutineByName(
_In_ PVOID BaseAddress,
_In_z_ PCSTR RoutineName
);
#endif
// MM
#define SEC_DRIVER_IMAGE 0x00100000
extern POBJECT_TYPE *MmSectionObjectType;
typedef
_Function_class_(MM_PROTECT_DRIVER_SECTION)
NTSTATUS
MM_PROTECT_DRIVER_SECTION(
_In_ PVOID AddressWithinSection,
_In_ SIZE_T Size,
_In_ ULONG Flags
);
typedef MM_PROTECT_DRIVER_SECTION* PMM_PROTECT_DRIVER_SECTION;
#define MM_PROTECT_DRIVER_SECTION_ALLOW_UNLOAD (0x1)
#define MM_PROTECT_DRIVER_SECTION_VALID_FLAGS \
(MM_PROTECT_DRIVER_SECTION_ALLOW_UNLOAD)
typedef struct _MMVAD_FLAGS
{
ULONG Lock : 1;
ULONG LockContended : 1;
ULONG DeleteInProgress : 1;
ULONG NoChange : 1;
ULONG VadType : 3;
ULONG Protection : 5;
ULONG PreferredNode : 7;
ULONG PageSize : 2;
ULONG PrivateMemory : 1;
} MMVAD_FLAGS, *PMMVAD_FLAGS;
typedef struct _MM_PRIVATE_VAD_FLAGS
{
ULONG Lock : 1;
ULONG LockContended : 1;
ULONG DeleteInProgress : 1;
ULONG NoChange : 1;
ULONG VadType : 3;
ULONG Protection : 5;
ULONG PreferredNode : 7;
ULONG PageSize : 2;
ULONG PrivateMemoryAlwaysSet : 1;
ULONG WriteWatch : 1;
ULONG FixedLargePageSize : 1;
ULONG ZeroFillPagesOptional : 1;
ULONG Graphics : 1;
ULONG Enclave : 1;
ULONG ShadowStack : 1;
ULONG PhysicalMemoryPfnsReferenced : 1;
} MM_PRIVATE_VAD_FLAGS, *PMM_PRIVATE_VAD_FLAGS;
typedef struct _MM_GRAPHICS_VAD_FLAGS
{
ULONG Lock : 1;
ULONG LockContended : 1;
ULONG DeleteInProgress : 1;
ULONG NoChange : 1;
ULONG VadType : 3;
ULONG Protection : 5;
ULONG PreferredNode : 7;
ULONG PageSize : 2;
ULONG PrivateMemoryAlwaysSet : 1;
ULONG WriteWatch : 1;
ULONG FixedLargePageSize : 1;
ULONG ZeroFillPagesOptional : 1;
ULONG GraphicsAlwaysSet : 1;
ULONG GraphicsUseCoherentBus : 1;
ULONG GraphicsNoCache : 1;
ULONG GraphicsPageProtection : 3;
} MM_GRAPHICS_VAD_FLAGS, *PMM_GRAPHICS_VAD_FLAGS;
typedef struct _MM_SHARED_VAD_FLAGS
{
ULONG Lock : 1;
ULONG LockContended : 1;
ULONG DeleteInProgress : 1;
ULONG NoChange : 1;
ULONG VadType : 3;
ULONG Protection : 5;
ULONG PreferredNode : 7;
ULONG PageSize : 2;
ULONG PrivateMemoryAlwaysClear : 1;
ULONG PrivateFixup : 1;
ULONG HotPatchState : 2;
} MM_SHARED_VAD_FLAGS, *PMM_SHARED_VAD_FLAGS;
typedef struct _MMVAD_FLAGS1
{
ULONG CommitCharge : 31;
ULONG MemCommit : 1;
}MMVAD_FLAGS1, *PMMVAD_FLAGS1;
typedef struct _MMVAD_SHORT
{
union
{
struct
{
struct _MMVAD_SHORT* NextVad;
PVOID ExtraCreateInfo;
};
RTL_BALANCED_NODE VadNode;
};
ULONG StartingVpn;
ULONG EndingVpn;
#ifdef _WIN64
UCHAR StartingVpnHigh;
UCHAR EndingVpnHigh;
UCHAR CommitChargeHigh;
union
{
UCHAR SpareNT64VadUChar;
struct // LA57
{
UCHAR EndingVpnHigher : 4;
UCHAR CommitChargeHigher : 4;
};
};
#endif
LONG ReferenceCount;
EX_PUSH_LOCK PushLock;
union
{
ULONG LongFlags;
MMVAD_FLAGS VadFlags;
MM_PRIVATE_VAD_FLAGS PrivateVadFlags;
MM_GRAPHICS_VAD_FLAGS GraphicsVadFlags;
MM_SHARED_VAD_FLAGS SharedVadFlags;
volatile ULONG VolatileVadLong;
} u;
union
{
ULONG LongFlags1;
MMVAD_FLAGS1 VadFlags1;
} u1;
#ifdef _WIN64
union
{
ULONG_PTR EventListULongPtr;
UCHAR StartingVpnHigher : 4; // LA57
} u5;
#else
PVOID EventList; // PMI_VAD_EVENT_BLOCK
#endif
} MMVAD_SHORT, *PMMVAD_SHORT;
FORCEINLINE
PVOID
MiGetVadShortStartAddress(
_In_ PMMVAD_SHORT Vad
)
{
#ifdef _WIN64
ULONG_PTR higher = Vad->u5.StartingVpnHigher;
ULONG_PTR high = Vad->StartingVpnHigh;
ULONG_PTR low = Vad->StartingVpn;
return (PVOID)((low | ((high | (higher << 8)) << 32)) << PAGE_SHIFT);
#else
return (PVOID)((ULONG_PTR)Vad->StartingVpn << PAGE_SHIFT);
#endif
}
FORCEINLINE
PVOID
MiGetVadShortEndAddress(
_In_ PMMVAD_SHORT Vad
)
{
#ifdef _WIN64
ULONG_PTR higher = Vad->EndingVpnHigher;
ULONG_PTR high = Vad->EndingVpnHigh;
ULONG_PTR low = Vad->EndingVpn;
return (PVOID)(((low + 1) | ((high | (higher << 8)) << 32)) << PAGE_SHIFT);
#else
return (PVOID)(((ULONG_PTR)Vad->StartingVpn + 1) << PAGE_SHIFT);
#endif
}
typedef struct _MMVAD_FLAGS2
{
ULONG FileOffset : 24;
ULONG Large : 1;
ULONG TrimBehind : 1;
ULONG Inherit : 1;
ULONG NoValidationNeeded : 1;
ULONG PrivateDemandZero : 1;
ULONG Spare : 3;
} MMVAD_FLAGS2, *PMMVAD_FLAGS2;
typedef struct _MI_VAD_SEQUENTIAL_INFO
{
ULONGLONG Length : 12;
ULONGLONG Vpn : 52;
} MI_VAD_SEQUENTIAL_INFO, *PMI_VAD_SEQUENTIAL_INFO;
typedef struct _MMEXTEND_INFO
{
ULONGLONG CommittedSize;
ULONG ReferenceCount;
} MMEXTEND_INFO, *PMMEXTEND_INFO;
typedef struct _MMPTE_HIGHLOW
{
ULONG LowPart;
ULONG HighPart;
} MMPTE_HIGHLOW, *PMMPTE_HIGHLOW;
typedef struct _MMPTE_HARDWARE
{
ULONGLONG Valid : 1;
ULONGLONG Dirty1 : 1;
ULONGLONG Owner : 1;
ULONGLONG WriteThrough : 1;
ULONGLONG CacheDisable : 1;
ULONGLONG Accessed : 1;
ULONGLONG Dirty : 1;
ULONGLONG LargePage : 1;
ULONGLONG Global : 1;
ULONGLONG CopyOnWrite : 1;
ULONGLONG Unused : 1;
ULONGLONG Write : 1;
ULONGLONG PageFrameNumber : 40;
ULONGLONG ReservedForSoftware : 4;
ULONGLONG WsleAge : 4;
ULONGLONG WsleProtection : 3;
ULONGLONG NoExecute : 1;
} MMPTE_HARDWARE, *PMMPTE_HARDWARE;
typedef struct _MMPTE_PROTOTYPE
{
ULONGLONG Valid : 1;
ULONGLONG DemandFillProto : 1;
ULONGLONG HiberVerifyConverted : 1;
ULONGLONG ReadOnly : 1;
ULONGLONG SwizzleBit : 1;
ULONGLONG Protection : 5;
ULONGLONG Prototype : 1;
ULONGLONG Combined : 1;
ULONGLONG Unused1 : 4;
LONGLONG ProtoAddress : 48;
} MMPTE_PROTOTYPE, * PMMPTE_PROTOTYPE;
typedef struct _MMPTE_SOFTWARE
{
ULONGLONG Valid : 1;
ULONGLONG PageFileReserved : 1;
ULONGLONG PageFileAllocated : 1;
ULONGLONG ColdPage : 1;
ULONGLONG SwizzleBit : 1;
ULONGLONG Protection : 5;
ULONGLONG Prototype : 1;
ULONGLONG Transition : 1;
ULONGLONG PageFileLow : 4;
ULONGLONG UsedPageTableEntries : 10;
ULONGLONG ShadowStack : 1;
ULONGLONG OnStandbyLookaside : 1;
ULONGLONG Unused : 4;
ULONGLONG PageFileHigh : 32;
} MMPTE_SOFTWARE, * PMMPTE_SOFTWARE;
typedef struct _MMPTE_TIMESTAMP
{
ULONGLONG MustBeZero : 1;
ULONGLONG Unused : 3;
ULONGLONG SwizzleBit : 1;
ULONGLONG Protection : 5;
ULONGLONG Prototype : 1;
ULONGLONG Transition : 1;
ULONGLONG PageFileLow : 4;
ULONGLONG Reserved : 16;
ULONGLONG GlobalTimeStamp : 32;
} MMPTE_TIMESTAMP, *PMMPTE_TIMESTAMP;
typedef struct _MMPTE_TRANSITION
{
ULONGLONG Valid : 1;
ULONGLONG Write : 1;
ULONGLONG OnStandbyLookaside : 1;
ULONGLONG IoTracker : 1;
ULONGLONG SwizzleBit : 1;
ULONGLONG Protection : 5;
ULONGLONG Prototype : 1;
ULONGLONG Transition : 1;
ULONGLONG PageFrameNumber : 40;
ULONGLONG Unused : 12;
} MMPTE_TRANSITION, *PMMPTE_TRANSITION;
typedef struct _MMPTE_SUBSECTION
{
ULONGLONG Valid : 1;
ULONGLONG Unused0 : 2;
ULONGLONG OnStandbyLookaside : 1;
ULONGLONG SwizzleBit : 1;
ULONGLONG Protection : 5;
ULONGLONG Prototype : 1;
ULONGLONG ColdPage : 1;
ULONGLONG Unused2 : 3;
ULONGLONG ExecutePrivilege : 1;
LONGLONG SubsectionAddress : 48;
} MMPTE_SUBSECTION, * PMMPTE_SUBSECTION;
typedef struct _MMPTE_LIST
{
ULONGLONG Valid : 1;
ULONGLONG OneEntry : 1;
ULONGLONG filler0 : 2;
ULONGLONG SwizzleBit : 1;
ULONGLONG Protection : 5;
ULONGLONG Prototype : 1;
ULONGLONG Transition : 1;
ULONGLONG filler1 : 16;
ULONGLONG NextEntry : 36;
} MMPTE_LIST, *PMMPTE_LIST;
typedef struct _MMPTE
{
union
{
ULONGLONG Long;
volatile ULONGLONG VolatileLong;
#ifndef _WIN64
MMPTE_HIGHLOW HighLow;
#endif
MMPTE_HARDWARE Hard;
MMPTE_PROTOTYPE Proto;
MMPTE_SOFTWARE Soft;
MMPTE_TIMESTAMP TimeStamp;
MMPTE_TRANSITION Trans;
MMPTE_SUBSECTION Subsect;
MMPTE_LIST List;
} u;
} MMPTE, *PMMPTE;
typedef struct _MMVAD
{
struct _MMVAD_SHORT Core;
union
{
ULONG LongFlags2;
MMVAD_FLAGS2 VadFlags2;
} u2;
PVOID Subsection; // PSUBSECTION
PMMPTE FirstPrototypePte;
PMMPTE LastContiguousPte;
LIST_ENTRY ViewLinks;
union
{
PEPROCESS VadsProcess;
UCHAR ViewMapType : 3; // VIEW_MAP_TYPE_*
};
union
{
MI_VAD_SEQUENTIAL_INFO SequentialVa;
PMMEXTEND_INFO ExtendedInfo;
} u4;
PFILE_OBJECT FileObject; // since WIN10
} MMVAD, *PMMVAD;
FORCEINLINE
PVOID
MiGetVadStartAddress(
_In_ PMMVAD Vad
)
{
return MiGetVadShortStartAddress(&Vad->Core);
}
FORCEINLINE
PVOID
MiGetVadEndAddress(
_In_ PMMVAD Vad
)
{
return MiGetVadShortEndAddress(&Vad->Core);
}
NTKERNELAPI
NTSTATUS
NTAPI
MmCreateSection(
_Out_ PVOID* SectionObject,
_In_ ACCESS_MASK DesiredAccess,
_In_opt_ POBJECT_ATTRIBUTES ObjectAttributes,
_In_ PLARGE_INTEGER MaximumSize,
_In_ ULONG SectionPageProtection,
_In_ ULONG AllocationAttributes,
_In_opt_ HANDLE FileHandle,
_In_opt_ PFILE_OBJECT FileObject
);
NTKERNELAPI
NTSTATUS
NTAPI
MmMapViewOfSection(
_In_ PVOID SectionObject,
_In_ PEPROCESS Process,
_Inout_ _At_(*BaseAddress, _Readable_bytes_(*ViewSize) _Writable_bytes_(*ViewSize) _Post_readable_byte_size_(*ViewSize)) PVOID *BaseAddress,
_In_ ULONG_PTR ZeroBits,
_In_ SIZE_T CommitSize,
_Inout_opt_ PLARGE_INTEGER SectionOffset,
_Inout_ PSIZE_T ViewSize,
_In_ SECTION_INHERIT InheritDisposition,
_In_ ULONG AllocationType,
_In_ ULONG PageProtection
);
NTKERNELAPI
NTSTATUS
NTAPI
MmUnmapViewOfSection(
_In_ PEPROCESS Process,
_In_ PVOID BaseAddress
);
NTKERNELAPI
NTSTATUS
NTAPI
MmCopyVirtualMemory(
_In_ PEPROCESS SourceProcess,
_In_reads_bytes_(BufferSize) PVOID SourceAddress,
_In_ PEPROCESS TargetProcess,
_Out_writes_bytes_(BufferSize) PVOID TargetAddress,
_In_ SIZE_T BufferSize,
_In_ KPROCESSOR_MODE PreviousMode,
_Out_ PSIZE_T ReturnSize
);
// CI
#ifndef ALGIDDEF
#define ALGIDDEF
typedef unsigned int ALG_ID;
#endif
#ifndef ALG_SID_MD5
#define ALG_SID_MD5 3
#endif ALG_SID_MD5
#ifndef ALG_SID_SHA1
#define ALG_SID_SHA1 4
#endif
#ifndef ALG_SID_SHA_256
#define ALG_SID_SHA_256 12
#endif
#ifndef ALG_CLASS_HASH
#define ALG_CLASS_HASH (4 << 13)
#endif
#ifndef ALG_TYPE_ANY
#define ALG_TYPE_ANY (0)
#endif
#ifndef CALG_MD5
#define CALG_MD5 (ALG_CLASS_HASH | ALG_TYPE_ANY | ALG_SID_MD5)
#endif
#ifndef CALG_SHA1
#define CALG_SHA1 (ALG_CLASS_HASH | ALG_TYPE_ANY | ALG_SID_SHA1)
#endif
#ifndef CALG_SHA_256
#define CALG_SHA_256 (ALG_CLASS_HASH | ALG_TYPE_ANY | ALG_SID_SHA_256)
#endif
#ifndef CRYPTO_BLOBS_DEFINED
#define CRYPTO_BLOBS_DEFINED
typedef struct _CRYPTOAPI_BLOB
{
ULONG cbData;
_Field_size_bytes_(cbData) UCHAR *pbData;
}
CRYPT_INTEGER_BLOB, *PCRYPT_INTEGER_BLOB,
CRYPT_UINT_BLOB, *PCRYPT_UINT_BLOB,
CRYPT_OBJID_BLOB, *PCRYPT_OBJID_BLOB,
CERT_NAME_BLOB, *PCERT_NAME_BLOB,
CERT_RDN_VALUE_BLOB, *PCERT_RDN_VALUE_BLOB,
CERT_BLOB, *PCERT_BLOB,
CRL_BLOB, *PCRL_BLOB,
DATA_BLOB, *PDATA_BLOB,
CRYPT_DATA_BLOB, *PCRYPT_DATA_BLOB,
CRYPT_HASH_BLOB, *PCRYPT_HASH_BLOB,
CRYPT_DIGEST_BLOB, *PCRYPT_DIGEST_BLOB,
CRYPT_DER_BLOB, *PCRYPT_DER_BLOB,
CRYPT_ATTR_BLOB, *PCRYPT_ATTR_BLOB;
#endif
#ifndef MINCRYPT_MAX_HASH_LEN
#define MINCRYPT_MAX_HASH_LEN 64
#endif
#ifndef MINCRYPT_SHA1_HASH_LEN
#define MINCRYPT_SHA1_HASH_LEN (160 / 8)
#endif
#ifndef MINCRYPT_SHA256_HASH_LEN
#define MINCRYPT_SHA256_HASH_LEN (256 / 8)
#endif
//
// Self-signed
//
#define MINCRYPT_POLICY_NO_ROOT 0x1
//
// Microsoft Authenticode Root Authority
// Microsoft Root Authority
// Microsoft Root Certificate Authority
//
#define MINCRYPT_POLICY_MICROSOFT_ROOT 0x2
//
// Microsoft Test Root Authority
//
#define MINCRYPT_POLICY_TEST_ROOT 0x4
//
// Microsoft Code Verification Root
//
#define MINCRYPT_POLICY_CODE_ROOT 0x8
//
// Win 10 RS5 started using Unknown Root
//
#define MINCRYPT_POLICY_UNKNOWN_ROOT 0x10
//
// Microsoft Digital Media Authority 2005
// Microsoft Digital Media Authority 2005 for preview releases
//
#define MINCRYPT_POLICY_DMD_ROOT 0x20
//
// MS Protected Media Test Root
//
#define MINCRYPT_POLICY_DMD_TEST_ROOT 0x40
//
// Win 8.1/10 flags
//
#define MINCRYPT_POLICY_3RD_PARTY_ROOT 0x80
#define MINCRYPT_POLICY_TRUSTED_BOOT_ROOT 0x100
#define MINCRYPT_POLICY_UEFI_ROOT 0x200
#define MINCRYPT_POLICY_FLIGHT_ROOT 0x400
#define MINCRYPT_POLICY_PRS_WIN81_ROOT 0x800
#define MINCRYPT_POLICY_TEST_WIN81_ROOT 0x1000
#define MINCRYPT_POLICY_OTHER_ROOT 0x2000
//
// Undefined bits
//
#define MINCRYPT_POLICY_INVALID_BITS 0xC000
//
// This flag masks out errors and only keeps policies
//
#define MINCRYPT_POLICY_ROOT_FLAG 0xFFFF
//
// All these are errors instead
//
#define MINCRYPT_POLICY_NO_SIGNATURE 0x00010000
#define MINCRYPT_POLICY_BAD_CHAIN 0x00020000
#define MINCRYPT_POLICY_BAD_SIGNATURE 0x00040000
#define MINCRYPT_POLICY_NO_CODE_EKU 0x00080000
#define MINCRYPT_POLICY_NO_PAGE_HASHES 0x00100000
#define MINCRYPT_POLICY_CERT_REVOKED 0x00200000
#define MINCRYPT_POLICY_CERT_EXPIRED 0x00400000
#define MINCRYPT_POLICY_UNKNOWN_ERROR 0x10000000
#define MINCRYPT_POLICY_ERROR_BIT_SHIFT 16
#define MINCRYPT_POLICY_ERROR_FLAGS 0xFFFF0000
//
// This set of flags ignores an invalid/unknown root authority and too long
// signing chain.
//
#define MINCRYPT_POLICY_BAD_ERROR_FLAGS (MINCRYPT_POLICY_ERROR_FLAGS & ~MINCRYPT_POLICY_BAD_CHAIN)
// https://docs.microsoft.com/en-us/windows/security/threat-protection/windows-defender-application-control/event-tag-explanations#microsoft-root-cas-trusted-by-windows
typedef enum _MINCRYPT_KNOWN_ROOT
{
MincryptRootNone,
MincryptRootUnknown,
MincryptRootSelfSigned,
MincryptRootAuthenticode,
MincryptRootMicrosoftProduct1997,
MincryptRootMicrosoftProduct2001,
MincryptRootMicrosoftProduct2010,
MincryptRootMicrosoftStandard2011,
MincryptRootMicrosoftCodeVerification2006,
MincryptRootMicrosoftTest1999,
MincryptRootMicrosoftTest2010,
MincryptRootMicrosoftDMDTest2005,
MincryptRootMicrosoftDMD2005,
MincryptRootMicrosoftDMDPreview2005,
MincryptRootMicrosoftFlight2014,
MincryptRootMicrosoftThirdPartyMarketplace,
MincryptRootMicrosoftECCTestingRoot2017,
MincryptRootMicrosoftECCDevelopmentRoot2018,
MincryptRootMicrosoftECCProduct2018,
MincryptRootMicrosoftECCProduct2017
} MINCRYPT_KNOWN_ROOT;
typedef struct _MINCRYPT_STRING
{
PCHAR Buffer;
USHORT Length;
UCHAR Asn1EncodingTag;
UCHAR Spare[1];
} MINCRYPT_STRING, *PMINCRYPT_STRING;
typedef struct _MINCRYPT_CHAIN_ELEMENT
{
ALG_ID AlgorithmId;
ULONG HashSize;
UCHAR Hash[MINCRYPT_MAX_HASH_LEN];
MINCRYPT_STRING Subject;
MINCRYPT_STRING Issuer;
CRYPT_DER_BLOB Certificate;
} MINCRYPT_CHAIN_ELEMENT, *PMINCRYPT_CHAIN_ELEMENT;
typedef struct _MINCRYPT_CHAIN_INFO
{
ULONG Size;
PCRYPT_DER_BLOB PublicKeys;
ULONG NumberOfPublicKeys;
PCRYPT_OBJID_BLOB ExtendedKeyUses;
ULONG NumberOfExtendedKeyUses;
// win10+
PMINCRYPT_CHAIN_ELEMENT ChainElements;
ULONG NumberOfChainElements;
MINCRYPT_KNOWN_ROOT KnownRoot;
CRYPT_ATTR_BLOB AuthenticodeAttributes;
UCHAR PlatformManifest[MINCRYPT_SHA256_HASH_LEN];
} MINCRYPT_CHAIN_INFO, *PMINCRYPT_CHAIN_INFO;
typedef struct _MINCRYPT_POLICY_INFO
{
ULONG Size;
NTSTATUS VerificationStatus;
ULONG PolicyBits;
PMINCRYPT_CHAIN_INFO ChainInfo;
// win8+
LARGE_INTEGER RevocationTime;
// win10+
LARGE_INTEGER ValidFromTime;
LARGE_INTEGER ValidToTime;
} MINCRYPT_POLICY_INFO, *PMINCRYPT_POLICY_INFO;
// rev
// CiFreePolicyInfo
typedef
_Function_class_(CI_FREE_POLICY_INFO)
_IRQL_requires_max_(PASSIVE_LEVEL)
VOID
NTAPI
CI_FREE_POLICY_INFO(
_Inout_ PMINCRYPT_POLICY_INFO PolicyInfo
);
typedef CI_FREE_POLICY_INFO* PCI_FREE_POLICY_INFO;
// rev
// CiCheckSignedFile (pre 6.1.7601.18519)
typedef
_Function_class_(CI_CHECK_SIGNED_FILE)
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS
NTAPI
CI_CHECK_SIGNED_FILE(
_In_bytecount_(MINCRYPT_SHA1_HASH_LEN) PBYTE Hash,
_In_bytecount_(SignatureSize) PBYTE Signature,
_In_ ULONG SignatureSize,
_Inout_opt_ PMINCRYPT_POLICY_INFO PolicyInfo,
_Out_opt_ PLARGE_INTEGER SigningTime,
_Inout_opt_ PMINCRYPT_POLICY_INFO TimeStampPolicyInfo
);
typedef CI_CHECK_SIGNED_FILE* PCI_CHECK_SIGNED_FILE;
// rev
// CiCheckSignedFile
typedef
_Function_class_(CI_CHECK_SIGNED_FILE_EX)
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS
NTAPI
CI_CHECK_SIGNED_FILE_EX(
_In_bytecount_(HashSize) PBYTE Hash,
_In_ ULONG HashSize,
_In_ ALG_ID AlgorithmId,
_In_bytecount_(SignatureSize) PBYTE Signature,
_In_ ULONG SignatureSize,
_Inout_opt_ PMINCRYPT_POLICY_INFO PolicyInfo,
_Out_opt_ PLARGE_INTEGER SigningTime,
_Inout_opt_ PMINCRYPT_POLICY_INFO TimeStampPolicyInfo
);
typedef CI_CHECK_SIGNED_FILE_EX* PCI_CHECK_SIGNED_FILE_EX;
// rev
// CiVerifyHashInCatalog (pre 6.1.7601.18519)
typedef
_Function_class_(CI_VERIFY_HASH_IN_CATALOG)
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS
NTAPI
CI_VERIFY_HASH_IN_CATALOG(
_In_bytecount_(MINCRYPT_SHA1_HASH_LEN) PBYTE Hash,
_In_ ULONG ReloadCatalogs,
_In_ ULONG SecureProcess,
_In_ ULONG AcceptRoots,
_Inout_opt_ PMINCRYPT_POLICY_INFO PolicyInfo,
_Out_opt_ PUNICODE_STRING CatalogName, // RtlFreeUnicodeString
_Out_opt_ PLARGE_INTEGER SigningTime,
_Inout_opt_ PMINCRYPT_POLICY_INFO TimeStampPolicyInfo
);
typedef CI_VERIFY_HASH_IN_CATALOG* PCI_VERIFY_HASH_IN_CATALOG;
// rev
// CiVerifyHashInCatalog
typedef
_Function_class_(CI_VERIFY_HASH_IN_CATALOG_EX)
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS
NTAPI
CI_VERIFY_HASH_IN_CATALOG_EX(
_In_bytecount_(HashSize) PBYTE Hash,
_In_ ULONG HashSize,
_In_ ALG_ID AlgorithmId,
_In_ ULONG ReloadCatalogs,
_In_ ULONG SecureProcess,
_In_ ULONG AcceptRoots,
_Inout_opt_ PMINCRYPT_POLICY_INFO PolicyInfo,
_Out_opt_ PUNICODE_STRING CatalogName, // RtlFreeUnicodeString
_Out_opt_ PLARGE_INTEGER SigningTime,
_Inout_opt_ PMINCRYPT_POLICY_INFO TimeStampPolicyInfo
);
typedef CI_VERIFY_HASH_IN_CATALOG_EX* PCI_VERIFY_HASH_IN_CATALOG_EX;
// rev
#define CI_POLICY_VALID_FLAGS 0x1BE00078ul
#define CI_POLICY_DEFAULT 0x00000000ul
#define CI_POLICY_REQUIRE_MICROSOFT 0x00000001ul // ? legacy
#define CI_POLICY_REQUIRE_SIGNED 0x00000002ul // ? legacy
#define CI_POLICY_ALLOW_UNSIGNED 0x00000004ul // ? legacy
#define CI_POLICY_REJECT_UNSIGNED 0x80000000ul // ? legacy
#define CI_POLICY_CHECK_PROTECTED_PROCESS_EKU 0x00000008ul
#define CI_POLICY_FORCE_PROTECTED_PROCESS_POLICY 0x00000010ul
#define CI_POLICY_ACCEPT_ANY_ROOT_CERTIFICATE 0x00000020ul
#define CI_POLICY_ALLOW_REVOKED_CERTIFICATE 0x00800000ul
#define CI_POLICY_ALLOW_EXPIRED_REVOKED_CERTIFICATE 0x08000000ul
// rev
// CiValidateFileObject
typedef
_Function_class_(CI_VALIDATE_FILE_OBJECT)
NTSTATUS
NTAPI
CI_VALIDATE_FILE_OBJECT(
_In_ PFILE_OBJECT FileObject,
_In_ ULONG PolicyFlags,
_In_ SE_SIGNING_LEVEL LevelCheck,
_Inout_ PMINCRYPT_POLICY_INFO PolicyInfo,
_Inout_ PMINCRYPT_POLICY_INFO TimeStampPolicyInfo,
_Out_ PLARGE_INTEGER SigningTime,
_Out_writes_bytes_to_(*ThumbprintSize, *ThumbprintSize) PUCHAR Thumbprint,
_Inout_ PULONG ThumbprintSize,
_Out_ PULONG ThumbprintAlgorithm
);
typedef CI_VALIDATE_FILE_OBJECT* PCI_VALIDATE_FILE_OBJECT;
// rev
typedef _Function_class_(CI_ALLOCATE_ROUTINE)
PVOID
NTAPI
CI_ALLOCATE_ROUTINE(
_In_ ULONG NumberOfBytes
);
typedef CI_ALLOCATE_ROUTINE* PCI_ALLOCATE_ROUTINE;
// rev
// CiGetCertPublisherName
typedef
_Function_class_(CI_GET_CERT_PUBLISHER_NAME)
NTSTATUS
NTAPI
CI_GET_CERT_PUBLISHER_NAME(
_In_ PCRYPT_DER_BLOB Certificate, // MINCRYPT_POLICY_INFO.ChainInfo.ChainElements.Certificate
_In_ PCI_ALLOCATE_ROUTINE AllocateRoutine,
_Out_ PUNICODE_STRING PublisherName
);
typedef CI_GET_CERT_PUBLISHER_NAME* PCI_GET_CERT_PUBLISHER_NAME;
// alpc
extern POBJECT_TYPE *LpcPortObjectType;
#define AlpcPortObjectType LpcPortObjectType
#define PORT_CONNECT 0x0001
#define PORT_ALL_ACCESS (STANDARD_RIGHTS_REQUIRED | SYNCHRONIZE | PORT_CONNECT)
typedef struct _PORT_MESSAGE
{
union
{
struct
{
CSHORT DataLength;
CSHORT TotalLength;
} s1;
ULONG Length;
} u1;
union
{
struct
{
CSHORT Type;
CSHORT DataInfoOffset;
} s2;
ULONG ZeroInit;
} u2;
union
{
CLIENT_ID ClientId;
double DoNotUseThisField;
};
ULONG MessageId;
union
{
SIZE_T ClientViewSize; // only valid for LPC_CONNECTION_REQUEST messages
ULONG CallbackId; // only valid for LPC_REQUEST messages
};
} PORT_MESSAGE, *PPORT_MESSAGE;
typedef HANDLE ALPC_HANDLE, *PALPC_HANDLE;
#define ALPC_PORFLG_ALLOW_LPC_REQUESTS 0x20000 // rev
#define ALPC_PORFLG_WAITABLE_PORT 0x40000 // dbg
#define ALPC_PORFLG_SYSTEM_PROCESS 0x100000 // dbg
typedef struct _ALPC_PORT_ATTRIBUTES
{
ULONG Flags;
SECURITY_QUALITY_OF_SERVICE SecurityQos;
SIZE_T MaxMessageLength;
SIZE_T MemoryBandwidth;
SIZE_T MaxPoolUsage;
SIZE_T MaxSectionSize;
SIZE_T MaxViewSize;
SIZE_T MaxTotalSectionSize;
ULONG DupObjectTypes;
#ifdef _WIN64
ULONG Reserved;
#endif
} ALPC_PORT_ATTRIBUTES, *PALPC_PORT_ATTRIBUTES;
#define ALPC_MESSAGE_SECURITY_ATTRIBUTE 0x80000000
#define ALPC_MESSAGE_VIEW_ATTRIBUTE 0x40000000
#define ALPC_MESSAGE_CONTEXT_ATTRIBUTE 0x20000000
#define ALPC_MESSAGE_HANDLE_ATTRIBUTE 0x10000000
typedef struct _ALPC_MESSAGE_ATTRIBUTES
{
ULONG AllocatedAttributes;
ULONG ValidAttributes;
} ALPC_MESSAGE_ATTRIBUTES, *PALPC_MESSAGE_ATTRIBUTES;
#define ALPC_MSGFLG_REPLY_MESSAGE 0x1
#define ALPC_MSGFLG_LPC_MODE 0x2 // ?
#define ALPC_MSGFLG_RELEASE_MESSAGE 0x10000 // dbg
#define ALPC_MSGFLG_SYNC_REQUEST 0x20000 // dbg
#define ALPC_MSGFLG_WAIT_USER_MODE 0x100000
#define ALPC_MSGFLG_WAIT_ALERTABLE 0x200000
#define ALPC_MSGFLG_WOW64_CALL 0x80000000 // dbg
NTSYSCALLAPI
NTSTATUS
NTAPI
ZwAlpcConnectPort(
_Out_ PHANDLE PortHandle,
_In_ PUNICODE_STRING PortName,
_In_opt_ POBJECT_ATTRIBUTES ObjectAttributes,
_In_opt_ PALPC_PORT_ATTRIBUTES PortAttributes,
_In_ ULONG Flags,
_In_opt_ PSID RequiredServerSid,
_Inout_updates_bytes_to_opt_(*BufferLength, *BufferLength) PPORT_MESSAGE ConnectionMessage,
_Inout_opt_ PSIZE_T BufferLength,
_Inout_opt_ PALPC_MESSAGE_ATTRIBUTES OutMessageAttributes,
_Inout_opt_ PALPC_MESSAGE_ATTRIBUTES InMessageAttributes,
_In_opt_ PLARGE_INTEGER Timeout
);
// LXCORE
typedef
_Function_class_(LXP_PROCESS_GET_CURRENT)
_Must_inspect_result_
BOOLEAN
NTAPI
LXP_PROCESS_GET_CURRENT(
_Out_ PVOID* Thread
);
typedef LXP_PROCESS_GET_CURRENT* PLXP_PROCESS_GET_CURRENT;
typedef
_Function_class_(LXP_THREAD_GET_CURRENT)
_Must_inspect_result_
BOOLEAN
NTAPI
LXP_THREAD_GET_CURRENT(
_Out_ PVOID* Thread
);
typedef LXP_THREAD_GET_CURRENT* PLXP_THREAD_GET_CURRENT;
// CFG
//
// Define flags for setting process CFG valid call target entries.
//
//
// Call target should be made valid. If not set, the call target is made
// invalid. Input flag.
//
#define CFG_CALL_TARGET_VALID (0x00000001)
//
// Call target has been successfully processed. Used to report to the caller
// how much progress has been made. Output flag.
//
#define CFG_CALL_TARGET_PROCESSED (0x00000002)
//
// Call target should be made valid only if it is suppressed export.
// What this flag means is that it can *only* be used on a cell which is
// currently in the CFG export suppressed state (only considered for export
// suppressed processes and not legacy CFG processes!), and it is also
// allowed to be used even if the process is a restricted (i.e. no ACG) process.
//
#define CFG_CALL_TARGET_CONVERT_EXPORT_SUPPRESSED_TO_VALID (0x00000004)
//
// Call target should be made into an XFG call target.
//
#define CFG_CALL_TARGET_VALID_XFG (0x00000008)
//
// Call target should be made valid only if it is already an XFG target
// in a process which has XFG audit mode enabled.
//
#define CFG_CALL_TARGET_CONVERT_XFG_TO_CFG (0x00000010)
typedef struct _CFG_CALL_TARGET_INFO {
ULONG_PTR Offset;
ULONG_PTR Flags;
} CFG_CALL_TARGET_INFO, *PCFG_CALL_TARGET_INFO;
typedef struct _CFG_CALL_TARGET_LIST_INFORMATION
{
ULONG NumberOfEntries;
ULONG Reserved;
PULONG NumberOfEntriesProcessed;
PCFG_CALL_TARGET_INFO CallTargetInfo;
PVOID Section; // since REDSTONE5
ULONGLONG FileOffset;
} CFG_CALL_TARGET_LIST_INFORMATION, *PCFG_CALL_TARGET_LIST_INFORMATION;
// SE
#define SeDebugPrivilege RtlConvertUlongToLuid(SE_DEBUG_PRIVILEGE)
#define SeCreateTokenPrivilege RtlConvertUlongToLuid(SE_CREATE_TOKEN_PRIVILEGE)
NTKERNELAPI
NTSTATUS
NTAPI
SeCaptureSecurityDescriptor(
_In_ PSECURITY_DESCRIPTOR OriginalSecurityDescriptor,
_In_ KPROCESSOR_MODE CurrentMode,
_In_ POOL_TYPE PoolType,
_In_ BOOLEAN CaptureIfKernel,
_Out_ PSECURITY_DESCRIPTOR *CapturedSecurityDescriptor
);
NTKERNELAPI
NTSTATUS
NTAPI
SeReleaseSecurityDescriptor(
_In_ PSECURITY_DESCRIPTOR CapturedSecurityDescriptor,
_In_ KPROCESSOR_MODE CurrentMode,
_In_ BOOLEAN CaptureIfKernelMode
);
#if (NTDDI_VERSION >= NTDDI_WINBLUE)
NTKERNELAPI
NTSTATUS
NTAPI
SeGetCachedSigningLevel(
_In_ PFILE_OBJECT FileObject,
_Out_ PULONG Flags,
_Out_ PSE_SIGNING_LEVEL SigningLevel,
_Out_writes_bytes_to_opt_(*ThumbprintSize, *ThumbprintSize) PUCHAR Thumbprint,
_Inout_opt_ PULONG ThumbprintSize,
_Out_opt_ PULONG ThumbprintAlgorithm
);
#endif
typedef
_IRQL_requires_max_(PASSIVE_LEVEL)
_Function_class_(SE_REGISTER_IMAGE_VERIFICATION_CALLBACK)
NTKERNELAPI
NTSTATUS
SE_REGISTER_IMAGE_VERIFICATION_CALLBACK(
_In_ SE_IMAGE_TYPE ImageType,
_In_ SE_IMAGE_VERIFICATION_CALLBACK_TYPE CallbackType,
_In_ PSE_IMAGE_VERIFICATION_CALLBACK_FUNCTION CallbackFunction,
_In_opt_ PVOID CallbackContext,
_Reserved_ SE_IMAGE_VERIFICATION_CALLBACK_TOKEN Token,
_Out_ PVOID* CallbackHandle
);
typedef SE_REGISTER_IMAGE_VERIFICATION_CALLBACK* PSE_REGISTER_IMAGE_VERIFICATION_CALLBACK;
typedef
_IRQL_requires_max_(PASSIVE_LEVEL)
_Function_class_(SE_UNREGISTER_IMAGE_VERIFICATION_CALLBACK)
NTKERNELAPI
VOID
SE_UNREGISTER_IMAGE_VERIFICATION_CALLBACK(
_In_ PVOID CallbackHandle
);
typedef SE_UNREGISTER_IMAGE_VERIFICATION_CALLBACK* PSE_UNREGISTER_IMAGE_VERIFICATION_CALLBACK;
// schannel.h
#define UNISP_NAME_A "Microsoft Unified Security Protocol Provider"
#define UNISP_NAME_W L"Microsoft Unified Security Protocol Provider"
#define SSL2SP_NAME_A "Microsoft SSL 2.0"
#define SSL2SP_NAME_W L"Microsoft SSL 2.0"
#define SSL3SP_NAME_A "Microsoft SSL 3.0"
#define SSL3SP_NAME_W L"Microsoft SSL 3.0"
#define TLS1SP_NAME_A "Microsoft TLS 1.0"
#define TLS1SP_NAME_W L"Microsoft TLS 1.0"
#define PCT1SP_NAME_A "Microsoft PCT 1.0"
#define PCT1SP_NAME_W L"Microsoft PCT 1.0"
#define SCHANNEL_NAME_A "Schannel"
#define SCHANNEL_NAME_W L"Schannel"
#define DEFAULT_TLS_SSP_NAME_A "Default TLS SSP"
#define DEFAULT_TLS_SSP_NAME_W L"Default TLS SSP"
#ifdef UNICODE
#define UNISP_NAME UNISP_NAME_W
#define PCT1SP_NAME PCT1SP_NAME_W
#define SSL2SP_NAME SSL2SP_NAME_W
#define SSL3SP_NAME SSL3SP_NAME_W
#define TLS1SP_NAME TLS1SP_NAME_W
#define SCHANNEL_NAME SCHANNEL_NAME_W
#define DEFAULT_TLS_SSP_NAME DEFAULT_TLS_SSP_NAME_W
#else
#define UNISP_NAME UNISP_NAME_A
#define PCT1SP_NAME PCT1SP_NAME_A
#define SSL2SP_NAME SSL2SP_NAME_A
#define SSL3SP_NAME SSL3SP_NAME_A
#define TLS1SP_NAME TLS1SP_NAME_A
#define SCHANNEL_NAME SCHANNEL_NAME_A
#define DEFAULT_TLS_SSP_NAME DEFAULT_TLS_SSP_NAME_A
#endif
#define SP_PROT_PCT1_SERVER 0x00000001
#define SP_PROT_PCT1_CLIENT 0x00000002
#define SP_PROT_PCT1 (SP_PROT_PCT1_SERVER | SP_PROT_PCT1_CLIENT)
#define SP_PROT_SSL2_SERVER 0x00000004
#define SP_PROT_SSL2_CLIENT 0x00000008
#define SP_PROT_SSL2 (SP_PROT_SSL2_SERVER | SP_PROT_SSL2_CLIENT)
#define SP_PROT_SSL3_SERVER 0x00000010
#define SP_PROT_SSL3_CLIENT 0x00000020
#define SP_PROT_SSL3 (SP_PROT_SSL3_SERVER | SP_PROT_SSL3_CLIENT)
#define SP_PROT_TLS1_SERVER 0x00000040
#define SP_PROT_TLS1_CLIENT 0x00000080
#define SP_PROT_TLS1 (SP_PROT_TLS1_SERVER | SP_PROT_TLS1_CLIENT)
#define SP_PROT_SSL3TLS1_CLIENTS (SP_PROT_TLS1_CLIENT | SP_PROT_SSL3_CLIENT)
#define SP_PROT_SSL3TLS1_SERVERS (SP_PROT_TLS1_SERVER | SP_PROT_SSL3_SERVER)
#define SP_PROT_SSL3TLS1 (SP_PROT_SSL3 | SP_PROT_TLS1)
#define SP_PROT_UNI_SERVER 0x40000000
#define SP_PROT_UNI_CLIENT 0x80000000
#define SP_PROT_UNI (SP_PROT_UNI_SERVER | SP_PROT_UNI_CLIENT)
#define SP_PROT_ALL 0xffffffff
#define SP_PROT_NONE 0
#define SP_PROT_CLIENTS (SP_PROT_PCT1_CLIENT | SP_PROT_SSL2_CLIENT | SP_PROT_SSL3_CLIENT | SP_PROT_UNI_CLIENT | SP_PROT_TLS1_CLIENT)
#define SP_PROT_SERVERS (SP_PROT_PCT1_SERVER | SP_PROT_SSL2_SERVER | SP_PROT_SSL3_SERVER | SP_PROT_UNI_SERVER | SP_PROT_TLS1_SERVER)
#define SP_PROT_TLS1_0_SERVER SP_PROT_TLS1_SERVER
#define SP_PROT_TLS1_0_CLIENT SP_PROT_TLS1_CLIENT
#define SP_PROT_TLS1_0 (SP_PROT_TLS1_0_SERVER | \
SP_PROT_TLS1_0_CLIENT)
#define SP_PROT_TLS1_1_SERVER 0x00000100
#define SP_PROT_TLS1_1_CLIENT 0x00000200
#define SP_PROT_TLS1_1 (SP_PROT_TLS1_1_SERVER | \
SP_PROT_TLS1_1_CLIENT)
#define SP_PROT_TLS1_2_SERVER 0x00000400
#define SP_PROT_TLS1_2_CLIENT 0x00000800
#define SP_PROT_TLS1_2 (SP_PROT_TLS1_2_SERVER | \
SP_PROT_TLS1_2_CLIENT)
#define SP_PROT_TLS1_3_SERVER 0x00001000
#define SP_PROT_TLS1_3_CLIENT 0x00002000
#define SP_PROT_TLS1_3 (SP_PROT_TLS1_3_SERVER | \
SP_PROT_TLS1_3_CLIENT)
#define SP_PROT_DTLS_SERVER 0x00010000
#define SP_PROT_DTLS_CLIENT 0x00020000
#define SP_PROT_DTLS (SP_PROT_DTLS_SERVER | \
SP_PROT_DTLS_CLIENT)
#define SP_PROT_DTLS1_0_SERVER SP_PROT_DTLS_SERVER
#define SP_PROT_DTLS1_0_CLIENT SP_PROT_DTLS_CLIENT
#define SP_PROT_DTLS1_0 (SP_PROT_DTLS1_0_SERVER | SP_PROT_DTLS1_0_CLIENT)
#define SP_PROT_DTLS1_2_SERVER 0x00040000
#define SP_PROT_DTLS1_2_CLIENT 0x00080000
#define SP_PROT_DTLS1_2 (SP_PROT_DTLS1_2_SERVER | SP_PROT_DTLS1_2_CLIENT)
#define SP_PROT_DTLS1_X_SERVER (SP_PROT_DTLS1_0_SERVER | \
SP_PROT_DTLS1_2_SERVER)
#define SP_PROT_DTLS1_X_CLIENT (SP_PROT_DTLS1_0_CLIENT | \
SP_PROT_DTLS1_2_CLIENT)
#define SP_PROT_DTLS1_X (SP_PROT_DTLS1_X_SERVER | \
SP_PROT_DTLS1_X_CLIENT)
#define SP_PROT_TLS1_1PLUS_SERVER (SP_PROT_TLS1_1_SERVER | \
SP_PROT_TLS1_2_SERVER | \
SP_PROT_TLS1_3_SERVER)
#define SP_PROT_TLS1_1PLUS_CLIENT (SP_PROT_TLS1_1_CLIENT | \
SP_PROT_TLS1_2_CLIENT | \
SP_PROT_TLS1_3_CLIENT)
#define SP_PROT_TLS1_1PLUS (SP_PROT_TLS1_1PLUS_SERVER | \
SP_PROT_TLS1_1PLUS_CLIENT)
#define SP_PROT_TLS1_3PLUS_SERVER SP_PROT_TLS1_3_SERVER
#define SP_PROT_TLS1_3PLUS_CLIENT SP_PROT_TLS1_3_CLIENT
#define SP_PROT_TLS1_3PLUS (SP_PROT_TLS1_3PLUS_SERVER | \
SP_PROT_TLS1_3PLUS_CLIENT)
#define SP_PROT_TLS1_X_SERVER (SP_PROT_TLS1_0_SERVER | \
SP_PROT_TLS1_1_SERVER | \
SP_PROT_TLS1_2_SERVER | \
SP_PROT_TLS1_3_SERVER)
#define SP_PROT_TLS1_X_CLIENT (SP_PROT_TLS1_0_CLIENT | \
SP_PROT_TLS1_1_CLIENT | \
SP_PROT_TLS1_2_CLIENT | \
SP_PROT_TLS1_3_CLIENT)
#define SP_PROT_TLS1_X (SP_PROT_TLS1_X_SERVER | \
SP_PROT_TLS1_X_CLIENT)
#define SP_PROT_SSL3TLS1_X_CLIENTS (SP_PROT_TLS1_X_CLIENT | \
SP_PROT_SSL3_CLIENT)
#define SP_PROT_SSL3TLS1_X_SERVERS (SP_PROT_TLS1_X_SERVER | \
SP_PROT_SSL3_SERVER)
#define SP_PROT_SSL3TLS1_X (SP_PROT_SSL3 | SP_PROT_TLS1_X)
#define SP_PROT_X_CLIENTS (SP_PROT_CLIENTS | \
SP_PROT_TLS1_X_CLIENT | \
SP_PROT_DTLS1_X_CLIENT)
#define SP_PROT_X_SERVERS (SP_PROT_SERVERS | \
SP_PROT_TLS1_X_SERVER | \
SP_PROT_DTLS1_X_SERVER)
#define SCH_CRED_NO_SYSTEM_MAPPER 0x00000002
#define SCH_CRED_NO_SERVERNAME_CHECK 0x00000004
#define SCH_CRED_MANUAL_CRED_VALIDATION 0x00000008
#define SCH_CRED_NO_DEFAULT_CREDS 0x00000010
#define SCH_CRED_AUTO_CRED_VALIDATION 0x00000020
#define SCH_CRED_USE_DEFAULT_CREDS 0x00000040
#define SCH_CRED_DISABLE_RECONNECTS 0x00000080
#define SCH_CRED_REVOCATION_CHECK_END_CERT 0x00000100
#define SCH_CRED_REVOCATION_CHECK_CHAIN 0x00000200
#define SCH_CRED_REVOCATION_CHECK_CHAIN_EXCLUDE_ROOT 0x00000400
#define SCH_CRED_IGNORE_NO_REVOCATION_CHECK 0x00000800
#define SCH_CRED_IGNORE_REVOCATION_OFFLINE 0x00001000
#define SCH_CRED_RESTRICTED_ROOTS 0x00002000
#define SCH_CRED_REVOCATION_CHECK_CACHE_ONLY 0x00004000
#define SCH_CRED_CACHE_ONLY_URL_RETRIEVAL 0x00008000
#define SCH_CRED_MEMORY_STORE_CERT 0x00010000
#define SCH_CRED_CACHE_ONLY_URL_RETRIEVAL_ON_CREATE 0x00020000
#define SCH_SEND_ROOT_CERT 0x00040000
#define SCH_CRED_SNI_CREDENTIAL 0x00080000
#define SCH_CRED_SNI_ENABLE_OCSP 0x00100000
#define SCH_SEND_AUX_RECORD 0x00200000
#define SCH_USE_STRONG_CRYPTO 0x00400000
#define SCH_USE_PRESHAREDKEY_ONLY 0x00800000
#define SCH_USE_DTLS_ONLY 0x01000000
#define SCH_ALLOW_NULL_ENCRYPTION 0x02000000
#define SCHANNEL_RENEGOTIATE 0 // renegotiate a connection
#define SCHANNEL_SHUTDOWN 1 // gracefully close down a connection
#define SCHANNEL_ALERT 2 // build an error message
#define SCHANNEL_SESSION 3 // session control
#define SCH_CRED_V1 0x00000001
#define SCH_CRED_V2 0x00000002 // for legacy code
#define SCH_CRED_VERSION 0x00000002 // for legacy code
#define SCH_CRED_V3 0x00000003 // for legacy code
#define SCHANNEL_CRED_VERSION 0x00000004 // for legacy code
#define SCH_CREDENTIALS_VERSION 0x00000005
typedef struct _SCHANNEL_CRED
{
DWORD dwVersion; // always SCHANNEL_CRED_VERSION
DWORD cCreds;
//PCCERT_CONTEXT *paCred;
PVOID paCred;
//HCERTSTORE hRootStore;
PVOID hRootStore;
DWORD cMappers;
struct _HMAPPER **aphMappers;
DWORD cSupportedAlgs;
ALG_ID * palgSupportedAlgs;
DWORD grbitEnabledProtocols;
DWORD dwMinimumCipherStrength;
DWORD dwMaximumCipherStrength;
DWORD dwSessionLifespan;
DWORD dwFlags;
DWORD dwCredFormat;
} SCHANNEL_CRED, *PSCHANNEL_CRED;
typedef enum _eTlsAlgorithmUsage
{
TlsParametersCngAlgUsageKeyExchange, // Key exchange algorithm. RSA, ECHDE, DHE, etc.
TlsParametersCngAlgUsageSignature, // Signature algorithm. RSA, DSA, ECDSA, etc.
TlsParametersCngAlgUsageCipher, // Encryption algorithm. AES, DES, RC4, etc.
TlsParametersCngAlgUsageDigest, // Digest of cipher suite. SHA1, SHA256, SHA384, etc.
TlsParametersCngAlgUsageCertSig // Signature and/or hash used to sign certificate. RSA, DSA, ECDSA, SHA1, SHA256, etc.
} eTlsAlgorithmUsage;
//
// SCH_CREDENTIALS structures
//
typedef struct _CRYPTO_SETTINGS
{
eTlsAlgorithmUsage eAlgorithmUsage; // How this algorithm is being used.
UNICODE_STRING strCngAlgId; // CNG algorithm identifier.
DWORD cChainingModes; // Set to 0 if CNG algorithm does not have a chaining mode.
PUNICODE_STRING rgstrChainingModes; // Set to NULL if CNG algorithm does not have a chaining mode.
DWORD dwMinBitLength; // Blacklist key sizes less than this. Set to 0 if not defined or CNG algorithm implies bit length.
DWORD dwMaxBitLength; // Blacklist key sizes greater than this. Set to 0 if not defined or CNG algorithm implies bit length.
} CRYPTO_SETTINGS, *PCRYPTO_SETTINGS;
typedef struct _TLS_PARAMETERS
{
DWORD cAlpnIds; // Valid for server applications only. Must be zero otherwise. Number of ALPN IDs in rgstrAlpnIds; set to 0 if applies to all.
PUNICODE_STRING rgstrAlpnIds; // Valid for server applications only. Must be NULL otherwise. Array of ALPN IDs that the following settings apply to; set to NULL if applies to all.
DWORD grbitDisabledProtocols; // List protocols you DO NOT want negotiated.
DWORD cDisabledCrypto; // Number of CRYPTO_SETTINGS structures; set to 0 if there are none.
PCRYPTO_SETTINGS pDisabledCrypto; // Array of CRYPTO_SETTINGS structures; set to NULL if there are none;
DWORD dwFlags; // Optional flags to pass; set to 0 if there are none.
} TLS_PARAMETERS, *PTLS_PARAMETERS;
#define TLS_PARAMS_OPTIONAL 0x00000001 // Valid for server applications only. Must be zero otherwise.
// TLS_PARAMETERS that will only be honored if they do not cause this server to terminate the handshake.
typedef struct _SCH_CREDENTIALS
{
DWORD dwVersion; // Always SCH_CREDENTIALS_VERSION.
DWORD dwCredFormat;
DWORD cCreds;
//PCCERT_CONTEXT *paCred;
PVOID *paCred;
//HCERTSTORE hRootStore;
PVOID hRootStore;
DWORD cMappers;
struct _HMAPPER **aphMappers;
DWORD dwSessionLifespan;
DWORD dwFlags;
DWORD cTlsParameters;
PTLS_PARAMETERS pTlsParameters;
} SCH_CREDENTIALS, *PSCH_CREDENTIALS;
#define SCH_CRED_MAX_SUPPORTED_PARAMETERS 16
#define SCH_CRED_MAX_SUPPORTED_ALPN_IDS 16
#define SCH_CRED_MAX_SUPPORTED_CRYPTO_SETTINGS 16
#define SCH_CRED_MAX_SUPPORTED_CHAINING_MODES 16
#define SECPKG_ATTR_ISSUER_LIST 0x50 // (OBSOLETE) returns SecPkgContext_IssuerListInfo
#define SECPKG_ATTR_REMOTE_CRED 0x51 // (OBSOLETE) returns SecPkgContext_RemoteCredentialInfo
#define SECPKG_ATTR_LOCAL_CRED 0x52 // (OBSOLETE) returns SecPkgContext_LocalCredentialInfo
#define SECPKG_ATTR_REMOTE_CERT_CONTEXT 0x53 // returns PCCERT_CONTEXT
#define SECPKG_ATTR_LOCAL_CERT_CONTEXT 0x54 // returns PCCERT_CONTEXT
#define SECPKG_ATTR_ROOT_STORE 0x55 // returns HCERTCONTEXT to the root store
#define SECPKG_ATTR_SUPPORTED_ALGS 0x56 // returns SecPkgCred_SupportedAlgs
#define SECPKG_ATTR_CIPHER_STRENGTHS 0x57 // returns SecPkgCred_CipherStrengths
#define SECPKG_ATTR_SUPPORTED_PROTOCOLS 0x58 // returns SecPkgCred_SupportedProtocols
#define SECPKG_ATTR_ISSUER_LIST_EX 0x59 // returns SecPkgContext_IssuerListInfoEx
#define SECPKG_ATTR_CONNECTION_INFO 0x5a // returns SecPkgContext_ConnectionInfo
#define SECPKG_ATTR_EAP_KEY_BLOCK 0x5b // returns SecPkgContext_EapKeyBlock
#define SECPKG_ATTR_MAPPED_CRED_ATTR 0x5c // returns SecPkgContext_MappedCredAttr
#define SECPKG_ATTR_SESSION_INFO 0x5d // returns SecPkgContext_SessionInfo
#define SECPKG_ATTR_APP_DATA 0x5e // sets/returns SecPkgContext_SessionAppData
#define SECPKG_ATTR_REMOTE_CERTIFICATES 0x5F // returns SecPkgContext_Certificates
#define SECPKG_ATTR_CLIENT_CERT_POLICY 0x60 // sets SecPkgCred_ClientCertCtlPolicy
#define SECPKG_ATTR_CC_POLICY_RESULT 0x61 // returns SecPkgContext_ClientCertPolicyResult
#define SECPKG_ATTR_USE_NCRYPT 0x62 // Sets the CRED_FLAG_USE_NCRYPT_PROVIDER FLAG on cred group
#define SECPKG_ATTR_LOCAL_CERT_INFO 0x63 // returns SecPkgContext_CertInfo
#define SECPKG_ATTR_CIPHER_INFO 0x64 // returns new CNG SecPkgContext_CipherInfo
#define SECPKG_ATTR_EAP_PRF_INFO 0x65 // sets SecPkgContext_EapPrfInfo
#define SECPKG_ATTR_SUPPORTED_SIGNATURES 0x66 // returns SecPkgContext_SupportedSignatures
#define SECPKG_ATTR_REMOTE_CERT_CHAIN 0x67 // returns PCCERT_CONTEXT
#define SECPKG_ATTR_UI_INFO 0x68 // sets SEcPkgContext_UiInfo
#define SECPKG_ATTR_EARLY_START 0x69 // sets SecPkgContext_EarlyStart
#define SECPKG_ATTR_KEYING_MATERIAL_INFO 0x6a // sets SecPkgContext_KeyingMaterialInfo
#define SECPKG_ATTR_KEYING_MATERIAL 0x6b // returns SecPkgContext_KeyingMaterial
#define SECPKG_ATTR_SRTP_PARAMETERS 0x6c // returns negotiated SRTP parameters
#define SECPKG_ATTR_TOKEN_BINDING 0x6d // returns SecPkgContext_TokenBinding
#define SECPKG_ATTR_CONNECTION_INFO_EX 0x6e // returns SecPkgContext_ConnectionInfoEx
#define SECPKG_ATTR_KEYING_MATERIAL_TOKEN_BINDING 0x6f // returns SecPkgContext_KeyingMaterial specific to Token Binding
#define SECPKG_ATTR_KEYING_MATERIAL_INPROC 0x70 // returns SecPkgContext_KeyingMaterial_Inproc
#define SECPKG_ATTR_CERT_CHECK_RESULT 0x71 // returns SecPkgContext_CertificateValidationResult, use during and after SSPI handshake loop
#define SECPKG_ATTR_CERT_CHECK_RESULT_INPROC 0x72 // returns SecPkgContext_CertificateValidationResult, use only after SSPI handshake loop
#define SECPKG_ATTR_SESSION_TICKET_KEYS 0x73 // sets SecPkgCred_SessionTicketKeys
#define SECPKG_ATTR_SERIALIZED_REMOTE_CERT_CONTEXT_INPROC 0x74 // returns CERT_BLOB, use only after SSPI handshake loop
#define SECPKG_ATTR_SERIALIZED_REMOTE_CERT_CONTEXT 0x75 // returns CERT_BLOB, use during and after SSPI handshake loop
typedef struct _SecPkgContext_ConnectionInfo
{
DWORD dwProtocol;
ALG_ID aiCipher;
DWORD dwCipherStrength;
ALG_ID aiHash;
DWORD dwHashStrength;
ALG_ID aiExch;
DWORD dwExchStrength;
} SecPkgContext_ConnectionInfo, *PSecPkgContext_ConnectionInfo;
#define SZ_ALG_MAX_SIZE 64
#define SECPKGCONTEXT_CONNECTION_INFO_EX_V1 1
typedef struct _SecPkgContext_ConnectionInfoEx
{
DWORD dwVersion;
DWORD dwProtocol;
WCHAR szCipher[SZ_ALG_MAX_SIZE];
DWORD dwCipherStrength;
WCHAR szHash[SZ_ALG_MAX_SIZE];
DWORD dwHashStrength;
WCHAR szExchange[SZ_ALG_MAX_SIZE];
DWORD dwExchStrength;
} SecPkgContext_ConnectionInfoEx, *PSecPkgContext_ConnectionInfoEx;
#define SECPKGCONTEXT_CIPHERINFO_V1 1
typedef struct _SecPkgContext_CipherInfo
{
DWORD dwVersion;
DWORD dwProtocol;
DWORD dwCipherSuite;
DWORD dwBaseCipherSuite;
WCHAR szCipherSuite[SZ_ALG_MAX_SIZE];
WCHAR szCipher[SZ_ALG_MAX_SIZE];
DWORD dwCipherLen;
DWORD dwCipherBlockLen; // in bytes
WCHAR szHash[SZ_ALG_MAX_SIZE];
DWORD dwHashLen;
WCHAR szExchange[SZ_ALG_MAX_SIZE];
DWORD dwMinExchangeLen;
DWORD dwMaxExchangeLen;
WCHAR szCertificate[SZ_ALG_MAX_SIZE];
DWORD dwKeyType;
} SecPkgContext_CipherInfo, *PSecPkgContext_CipherInfo;
================================================
FILE: KSystemInformer/include/pooltags.h
================================================
/*
* Copyright (c) 2022 Winsider Seminars & Solutions, Inc. All rights reserved.
*
* This file is part of System Informer.
*
* Authors:
*
* jxy-s 2022-2026
*
*/
#pragma once
// alloc
#define KPH_TAG_PAGED_LOOKASIDE_OBJECT '0ApK'
#define KPH_TAG_NPAGED_LOOKASIDE_OBJECT '1ApK'
// comms
#define KPH_TAG_CLIENT '0CpK'
#define KPH_TAG_MESSAGE '1CpK'
#define KPH_TAG_NPAGED_MESSAGE '2CpK'
#define KPH_TAG_QUEUE_ITEM '3CpK'
#define KPH_TAG_THREAD_POOL '4CpK'
#define KPH_TAG_CLIENT_INFORMER_STATE '5CpK'
#define KPH_TAG_CLIENT_INFORMER_SETTINGS '6CpK'
// dyndata
#define KPH_TAG_DYNDATA '0YpK'
// object
#define KPH_TAG_OBJECT_QUERY '0OpK'
#define KPH_TAG_OBJECT_INFO '1OpK'
// process
#define KPH_TAG_PROCESS_INFO '0PpK'
// thread
#define KPH_TAG_THREAD_BACK_TRACE '0TpK'
#define KPH_TAG_THREAD_INFO '1TpK'
// util
#define KPH_TAG_REG_STRING '0UpK'
#define KPH_TAG_REG_BINARY '1UpK'
#define KPH_TAG_FILE_OBJECT_NAME '2UpK'
#define KPH_TAG_CAPTURED_UNICODE_STRING '3UpK'
// vm
#define KPH_TAG_COPY_VM '0vpK'
#define KPH_TAG_SECTION_QUERY '1vpK'
#define KPH_TAG_VM_QUERY '2vpK'
// debug
#define KPH_TAG_DBG_SLOTS '0dpK'
// hash
#define KPH_TAG_HASHING_CONTEXT '0HpK'
#define KPH_TAG_HASHING_INFRA '1HpK'
#define KPH_TAG_CAPTURED_HASHES '2HpK'
// verify
#define KPH_TAG_VERIFY_SIGNATURE '0VpK'
// informer
#define KPH_TAG_OB_OBJECT_NAME '0IpK'
#define KPH_TAG_PROCESS_CREATE_APC '1IpK'
#define KPH_TAG_FLT_STREAMHANDLE_CONTEXT '2IpK'
#define KPH_TAG_FLT_FILE_NAME '3IpK'
#define KPH_TAG_FLT_CACHED_FILE_NAME '4IpK'
#define KPH_TAG_FLT_COMPLETION_CONTEXT '5IpK'
#define KPH_TAG_REG_CALL_CONTEXT '6IpK'
#define KPH_TAG_REG_OBJECT_NAME '7IpK'
#define KPH_TAG_REG_VALUE_NAMES '8IpK'
#define KPH_TAG_OB_CALL_CONTEXT '9IpK'
#define KPH_TAG_INFORMER_STATE 'SIpK'
#define KPH_TAG_INFORMER_SETTINGS 'sIpK'
// cid_tracking
#define KPH_TAG_CID_TABLE '0cpK'
#define KPH_TAG_CID_POPULATE '1cpK'
#define KPH_TAG_PROCESS_CONTEXT '2cpK'
#define KPH_TAG_THREAD_CONTEXT '3cpK'
#define KPH_TAG_CID_APC '4cpK'
#define KPH_TAG_PROCESS_IMAGE_FILE_NAME '5cpK'
// protection
#define KPH_TAG_IMAGE_LOAD_APC '0ppK'
// alpc
#define KPH_TAG_ALPC_NAME_QUERY '0apK'
#define KPH_TAG_ALPC_QUERY '1apK'
// file
#define KPH_TAG_FILE_QUERY '0FpK'
#define KPH_TAG_VOL_FILE_QUERY '1FpK'
// back_trace
#define KPH_TAG_BACK_TRACE_OBJECT '0BpK'
// session_token
#define KPH_TAG_SESSION_TOKEN_OBJECT '0tpK'
#define KPH_TAG_SESSION_TOKEN_SIGNATURE '1tpK'
// ringbuff
#define KPH_TAG_RING_BUFFER '0RpK'
// kphthread
#define KPH_TAG_THREAD_START_CONTEXT '0rpK'
================================================
FILE: KSystemInformer/include/trace.h
================================================
/*
* Copyright (c) 2022 Winsider Seminars & Solutions, Inc. All rights reserved.
*
* This file is part of System Informer.
*
* Authors:
*
* jxy-s 2022-2026
*
*/
#pragma once
#define WPP_CONTROL_GUIDS \
WPP_DEFINE_CONTROL_GUID( \
KSystemInformer, (f64b58a2, 8214, 4037, 8c7d, b96ce6098f3d), \
WPP_DEFINE_BIT(GENERAL) /* bit 0 = 0x00000001 */ \
WPP_DEFINE_BIT(UTIL) /* bit 1 = 0x00000002 */ \
WPP_DEFINE_BIT(COMMS) /* bit 2 = 0x00000004 */ \
WPP_DEFINE_BIT(INFORMER) /* bit 3 = 0x00000008 */ \
WPP_DEFINE_BIT(VERIFY) /* bit 4 = 0x00000010 */ \
WPP_DEFINE_BIT(HASH) /* bit 5 = 0x00000020 */ \
WPP_DEFINE_BIT(TRACKING) /* bit 5 = 0x00000040 */ \
WPP_DEFINE_BIT(PROTECTION) /* bit 5 = 0x00000080 */ \
)
#define WPP_LEVEL_EVENT_LOGGER(level,event) WPP_LEVEL_LOGGER(event)
#define WPP_LEVEL_EVENT_ENABLED(level,event) \
(WPP_LEVEL_ENABLED(event) && \
WPP_CONTROL(WPP_BIT_ ## event).Level >= level)
//
// begin_wpp config
// FUNC KphTracePrint(LEVEL,EVENT,MSG, ...);
// end_wpp
//
#ifdef KSI_NO_WPP
#define KphTracePrint(LEVEL,EVENT,MSG,...) ((void)(MSG, ## __VA_ARGS__))
#define WPP_INIT_TRACING(...) ((void)0)
#define WPP_CLEANUP(...) ((void)0)
#endif
#define TMH_STRINGIFYX(x) #x
#define TMH_STRINGIFY(x) TMH_STRINGIFYX(x)
#ifdef TMH_FILE
#include TMH_STRINGIFY(TMH_FILE)
#endif
================================================
FILE: KSystemInformer/informer.c
================================================
/*
* Copyright (c) 2022 Winsider Seminars & Solutions, Inc. All rights reserved.
*
* This file is part of System Informer.
*
* Authors:
*
* jxy-s 2023-2026
*
*/
#include
#include
#include
#include
typedef struct _KPH_INFORMER_STATE
{
KPH_INFORMER_OPTIONS Options;
KPH_RATE_LIMIT RateLimit[KPH_INFORMER_COUNT];
} KPH_INFORMER_STATE, *PKPH_INFORMER_STATE;
KPH_PROTECTED_DATA_SECTION_PUSH();
static PKPH_OBJECT_TYPE KphpInformerStateType = NULL;
static PKPH_NPAGED_LOOKASIDE_OBJECT KphpInformerStateLookaside = NULL;
KPH_PROTECTED_DATA_SECTION_POP();
KPH_PROTECTED_DATA_SECTION_RO_PUSH();
static const UNICODE_STRING KphpInformerStateTypeName = RTL_CONSTANT_STRING(L"KphInformerState");
KPH_PROTECTED_DATA_SECTION_RO_POP();
static KPH_INFORMER_STATE_ATOMIC KphpInformerState = { .Atomic = KPH_ATOMIC_OBJECT_REF_INIT };
/**
* \brief Allocates an informer state object.
*
* \return Pointer to informer state object, null on failure.
*/
_Function_class_(KPH_TYPE_ALLOCATE_PROCEDURE)
_IRQL_requires_max_(DISPATCH_LEVEL)
_Return_allocatesMem_size_(Size)
PVOID KSIAPI KphpAllocateInformerState(
_In_ SIZE_T Size
)
{
PVOID object;
KPH_NPAGED_CODE_DISPATCH_MAX();
NT_ASSERT(KphpInformerStateLookaside);
NT_ASSERT(Size <= KphpInformerStateLookaside->L.Size);
DBG_UNREFERENCED_PARAMETER(Size);
object = KphAllocateFromNPagedLookasideObject(KphpInformerStateLookaside);
if (object)
{
KphReferenceObject(KphpInformerStateLookaside);
}
return object;
}
/**
* \brief Initializes an informer state object.
*
* \param[in] Object The informer state object to initialize.
* \param[in] Parameter Optional settings to initialize with.
*
* \return STATUS_SUCCESS
*/
_Function_class_(KPH_TYPE_INITIALIZE_PROCEDURE)
_Must_inspect_result_
NTSTATUS
KSIAPI
KphpInitializeInformerState(
_Inout_ PVOID Object,
_In_opt_ PVOID Parameter
)
{
PKPH_INFORMER_STATE state;
PKPH_INFORMER_SETTINGS settings;
LARGE_INTEGER timeStamp;
state = Object;
settings = Parameter;
KeQuerySystemTime(&timeStamp);
if (settings)
{
state->Options.Flags = settings->Options.Flags;
for (ULONG i = 0; i < KPH_INFORMER_COUNT; i++)
{
KphInitializeRateLimit(&settings->Policy[i],
&timeStamp,
&state->RateLimit[i]);
}
}
else
{
KPH_RATE_LIMIT_POLICY policy = KPH_RATE_LIMIT_DENY_ALL;
state->Options.Flags = 0;
for (ULONG i = 0; i < KPH_INFORMER_COUNT; i++)
{
KphInitializeRateLimit(&policy, &timeStamp, &state->RateLimit[i]);
}
}
return STATUS_SUCCESS;
}
/**
* \brief Frees an informer state object.
*
* \param[in] Object The informer state object to free.
*/
_Function_class_(KPH_TYPE_FREE_PROCEDURE)
_IRQL_requires_max_(DISPATCH_LEVEL)
VOID KSIAPI KphpFreeInformerState(
_In_freesMem_ PVOID Object
)
{
KPH_NPAGED_CODE_DISPATCH_MAX();
NT_ASSERT(KphpInformerStateLookaside);
KphFreeToNPagedLookasideObject(KphpInformerStateLookaside, Object);
KphDereferenceObject(KphpInformerStateLookaside);
}
/**
* \brief Checks if an informer is allowed for a process.
*
* \param[in] Index The informer index to check.
* \param[in] TimeStamp The current time stamp.
* \param[in] Process The process context to check.
*
* \return TRUE if the informer is allowed, FALSE otherwise.
*/
_IRQL_requires_max_(DISPATCH_LEVEL)
BOOLEAN KphInformerProcessAllowed(
_In_ ULONG Index,
_In_ PLARGE_INTEGER TimeStamp,
_In_opt_ PKPH_PROCESS_CONTEXT Process
)
{
BOOLEAN allowed;
PKPH_INFORMER_STATE state;
KPH_NPAGED_CODE_DISPATCH_MAX();
if (!Process)
{
return TRUE;
}
if (!NT_VERIFY(Index < KPH_INFORMER_COUNT))
{
return FALSE;
}
state = KphAtomicReferenceObject(&Process->InformerState.Atomic);
if (!state)
{
return TRUE;
}
allowed = KphRateLimitConsumeToken(&state->RateLimit[Index], TimeStamp);
KphDereferenceObject(state);
return allowed;
}
/**
* \brief Checks if an informer is globally allowed.
*
* \param[in] Index The informer index to check.
* \param[in] TimeStamp The current time stamp.
*
* \return TRUE if the informer is allowed, FALSE otherwise.
*/
_IRQL_requires_max_(DISPATCH_LEVEL)
BOOLEAN KphInformerGlobalAllowed(
_In_ ULONG Index,
_In_ PLARGE_INTEGER TimeStamp
)
{
BOOLEAN allowed;
PKPH_INFORMER_STATE state;
KPH_NPAGED_CODE_DISPATCH_MAX();
if (!NT_VERIFY(Index < KPH_INFORMER_COUNT))
{
return FALSE;
}
state = KphAtomicReferenceObject(&KphpInformerState.Atomic);
NT_ASSERT(state);
allowed = KphRateLimitConsumeToken(&state->RateLimit[Index], TimeStamp);
KphDereferenceObject(state);
return allowed;
}
/**
* \brief Checks if an informer is allowed.
*
* \param[in] Index The informer index to check.
* \param[in] ActorProcess The actor process check.
* \param[in] TargetProcess The target process check.
*
* \return TRUE if the informer is allowed, FALSE otherwise.
*/
_IRQL_requires_max_(DISPATCH_LEVEL)
BOOLEAN KphInformerAllowed(
_In_ ULONG Index,
_In_opt_ PKPH_PROCESS_CONTEXT ActorProcess,
_In_opt_ PKPH_PROCESS_CONTEXT TargetProcess
)
{
LARGE_INTEGER timeStamp;
KPH_NPAGED_CODE_DISPATCH_MAX();
if (!KphGetInformerClientCount())
{
return FALSE;
}
KeQuerySystemTime(&timeStamp);
if (!KphInformerProcessAllowed(Index, &timeStamp, ActorProcess))
{
if (ActorProcess == TargetProcess)
{
return FALSE;
}
if (!KphInformerProcessAllowed(Index, &timeStamp, TargetProcess))
{
return FALSE;
}
}
if (!KphInformerGlobalAllowed(Index, &timeStamp))
{
return FALSE;
}
return TRUE;
}
/**
* \brief Retrieves the active informer options.
*
* \param[in] ActorProcess The actor process check.
* \param[in] TargetProcess The target process check.
*
* \return Active informer options.
*/
_IRQL_requires_max_(DISPATCH_LEVEL)
KPH_INFORMER_OPTIONS KphInformerOptions(
_In_opt_ PKPH_PROCESS_CONTEXT ActorProcess,
_In_opt_ PKPH_PROCESS_CONTEXT TargetProcess
)
{
PKPH_INFORMER_STATE state;
KPH_INFORMER_OPTIONS options;
KPH_NPAGED_CODE_DISPATCH_MAX();
options.Flags = 0;
state = KphAtomicReferenceObject(&KphpInformerState.Atomic);
NT_ASSERT(state);
SetFlag(options.Flags, state->Options.Flags);
KphDereferenceObject(state);
if (ActorProcess)
{
state = KphAtomicReferenceObject(&ActorProcess->InformerState.Atomic);
if (state)
{
SetFlag(options.Flags, state->Options.Flags);
KphDereferenceObject(state);
}
}
if (TargetProcess)
{
state = KphAtomicReferenceObject(&TargetProcess->InformerState.Atomic);
if (state)
{
SetFlag(options.Flags, state->Options.Flags);
KphDereferenceObject(state);
}
}
return options;
}
KPH_PAGED_FILE();
/**
* \brief Copies informer settings from state to mode.
*
* \param[out] Settings Receives the settings.
* \param[in] State The informer state to copy from.
* \param[in] AccessMode The mode in which to perform access checks.
*
* \return Successful or errant status.
*/
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KphpInformerCopySettingsToMode(
_Out_ PKPH_INFORMER_SETTINGS Settings,
_In_ PKPH_INFORMER_STATE State,
_In_ KPROCESSOR_MODE AccessMode
)
{
NTSTATUS status;
KPH_PAGED_CODE_PASSIVE();
status = KphCopyToMode(Settings,
&State->Options,
sizeof(KPH_INFORMER_OPTIONS),
AccessMode);
if (!NT_SUCCESS(status))
{
return status;
}
for (ULONG i = 0; i < KPH_INFORMER_COUNT; i++)
{
status = KphCopyToMode(&Settings->Policy[i],
&State->RateLimit[i].Policy,
sizeof(KPH_RATE_LIMIT_POLICY),
AccessMode);
if (!NT_SUCCESS(status))
{
return status;
}
}
return STATUS_SUCCESS;
}
/**
* \brief Gets informer settings.
*
* \param[out] Settings Receives the settings.
* \param[in] AccessMode The mode in which to perform access checks.
*
* \return Successful or errant status.
*/
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KphGetInformerSettings(
_Out_ PKPH_INFORMER_SETTINGS Settings,
_In_ KPROCESSOR_MODE AccessMode
)
{
NTSTATUS status;
PKPH_INFORMER_STATE state;
KPH_PAGED_CODE_PASSIVE();
state = KphAtomicReferenceObject(&KphpInformerState.Atomic);
NT_ASSERT(state);
status = KphZeroModeMemory(Settings,
sizeof(KPH_INFORMER_SETTINGS),
AccessMode);
if (!NT_SUCCESS(status))
{
goto Exit;
}
status = KphpInformerCopySettingsToMode(Settings, state, AccessMode);
Exit:
KphDereferenceObject(state);
return status;
}
/**
* \brief Sets informer settings.
*
* \param[in] Settings The settings to apply.
* \param[in] AccessMode The mode in which to perform access checks.
*
* \return Successful or errant status.
*/
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KphSetInformerSettings(
_In_ PKPH_INFORMER_SETTINGS Settings,
_In_ KPROCESSOR_MODE AccessMode
)
{
NTSTATUS status;
PKPH_INFORMER_SETTINGS settings;
PKPH_INFORMER_STATE state;
KPH_PAGED_CODE_PASSIVE();
state = NULL;
settings = KphAllocatePaged(sizeof(KPH_INFORMER_SETTINGS),
KPH_TAG_INFORMER_SETTINGS);
if (!settings)
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
GENERAL,
"Failed to allocate informer settings");
status = STATUS_INSUFFICIENT_RESOURCES;
goto Exit;
}
status = KphCopyFromMode(settings,
Settings,
sizeof(KPH_INFORMER_SETTINGS),
AccessMode);
if (!NT_SUCCESS(status))
{
goto Exit;
}
status = KphCreateObject(KphpInformerStateType,
sizeof(KPH_INFORMER_STATE),
&state,
settings);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
GENERAL,
"KphCreateObject failed: %!STATUS!",
status);
state = NULL;
goto Exit;
}
KphAtomicAssignObjectReference(&KphpInformerState.Atomic, state);
Exit:
if (state)
{
KphDereferenceObject(state);
}
if (settings)
{
KphFree(settings, KPH_TAG_INFORMER_SETTINGS);
}
return status;
}
/**
* \brief Gets informer settings for a process.
*
* \param[in] ProcessHandle Handle to the process to get settings of.
* \param[out] Settings Receives the settings.
* \param[in] AccessMode The mode in which to perform access checks.
*
* \return Successful or errant status.
*/
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KphGetInformerProcessSettings(
_In_ HANDLE ProcessHandle,
_Out_ PKPH_INFORMER_SETTINGS Settings,
_In_ KPROCESSOR_MODE AccessMode
)
{
NTSTATUS status;
PEPROCESS processObject;
PKPH_PROCESS_CONTEXT processContext;
PKPH_INFORMER_STATE state;
KPH_PAGED_CODE_PASSIVE();
processObject = NULL;
processContext = NULL;
state = NULL;
status = KphZeroModeMemory(Settings,
sizeof(KPH_INFORMER_SETTINGS),
AccessMode);
if (!NT_SUCCESS(status))
{
goto Exit;
}
status = ObReferenceObjectByHandle(ProcessHandle,
0,
*PsProcessType,
AccessMode,
&processObject,
NULL);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
GENERAL,
"ObReferenceObjectByHandle failed: %!STATUS!",
status);
processObject = NULL;
goto Exit;
}
processContext = KphGetEProcessContext(processObject);
if (!processContext)
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
GENERAL,
"KphGetEProcessContext return null.");
status = STATUS_INSUFFICIENT_RESOURCES;
goto Exit;
}
state = KphAtomicReferenceObject(&processContext->InformerState.Atomic);
if (!state)
{
status = STATUS_NOT_FOUND;
goto Exit;
}
status = KphpInformerCopySettingsToMode(Settings, state, AccessMode);
Exit:
if (processContext)
{
KphDereferenceObject(processContext);
}
if (processObject)
{
ObDereferenceObject(processObject);
}
if (state)
{
KphDereferenceObject(state);
}
return status;
}
typedef struct _KPH_SET_INFORMER_PROCESS_SETTINGS_CONTEXT
{
NTSTATUS Status;
PKPH_INFORMER_SETTINGS Settings;
} KPH_SET_INFORMER_PROCESS_SETTINGS_CONTEXT, *PKPH_SET_INFORMER_PROCESS_SETTINGS_CONTEXT;
/**
* \brief Callback for setting process informer settings.
*
* \param[in] Process The process to set the informer settings for.
* \param[in] Parameter The settings to apply.
*
* \return FALSE
*/
_Function_class_(KPH_ENUM_PROCESS_CONTEXTS_CALLBACK)
_Must_inspect_result_
BOOLEAN KSIAPI KphpSetInformerProcessSettings(
_In_ PKPH_PROCESS_CONTEXT Process,
_In_opt_ PVOID Parameter
)
{
NTSTATUS status;
PKPH_SET_INFORMER_PROCESS_SETTINGS_CONTEXT context;
PKPH_PROCESS_STATE state;
KPH_PAGED_CODE_PASSIVE();
NT_ASSERT(Parameter);
context = Parameter;
status = KphCreateObject(KphpInformerStateType,
sizeof(KPH_INFORMER_STATE),
&state,
context->Settings);
if (!NT_SUCCESS(status))
{
if (NT_SUCCESS(context->Status))
{
context->Status = status;
}
return FALSE;
}
KphAtomicAssignObjectReference(&Process->InformerState.Atomic, state);
KphDereferenceObject(state);
return FALSE;
}
/**
* \brief Sets informer settings for a process.
*
* \param[in] ProcessHandle Optional handle to the process to set settings of.
* If not provided the settings are applied to all processes.
* \param[in] Settings The settings to apply.
* \param[in] AccessMode The mode in which to perform access checks.
*
* \return Successful or errant status.
*/
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KphSetInformerProcessSettings(
_In_opt_ HANDLE ProcessHandle,
_In_ PKPH_INFORMER_SETTINGS Settings,
_In_ KPROCESSOR_MODE AccessMode
)
{
NTSTATUS status;
PEPROCESS processObject;
PKPH_PROCESS_CONTEXT processContext;
PKPH_INFORMER_SETTINGS settings;
KPH_SET_INFORMER_PROCESS_SETTINGS_CONTEXT context;
KPH_PAGED_CODE_PASSIVE();
processObject = NULL;
processContext = NULL;
settings = KphAllocatePaged(sizeof(KPH_INFORMER_SETTINGS),
KPH_TAG_INFORMER_SETTINGS);
if (!settings)
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
GENERAL,
"Failed to allocate informer settings");
status = STATUS_INSUFFICIENT_RESOURCES;
goto Exit;
}
status = KphCopyFromMode(settings,
Settings,
sizeof(KPH_INFORMER_SETTINGS),
AccessMode);
if (!NT_SUCCESS(status))
{
goto Exit;
}
if (ProcessHandle)
{
status = ObReferenceObjectByHandle(ProcessHandle,
0,
*PsProcessType,
AccessMode,
&processObject,
NULL);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
GENERAL,
"ObReferenceObjectByHandle failed: %!STATUS!",
status);
processObject = NULL;
goto Exit;
}
processContext = KphGetEProcessContext(processObject);
if (!processContext)
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
GENERAL,
"KphGetEProcessContext return null.");
status = STATUS_INSUFFICIENT_RESOURCES;
goto Exit;
}
context.Status = STATUS_SUCCESS;
context.Settings = settings;
(VOID)KphpSetInformerProcessSettings(processContext, &context);
status = context.Status;
}
else
{
context.Status = STATUS_SUCCESS;
context.Settings = settings;
KphEnumerateProcessContexts(KphpSetInformerProcessSettings, &context);
status = context.Status;
}
Exit:
if (processContext)
{
KphDereferenceObject(processContext);
}
if (processObject)
{
ObDereferenceObject(processObject);
}
if (settings)
{
KphFree(settings, KPH_TAG_INFORMER_SETTINGS);
}
return status;
}
/**
* \brief Gets informer statistics.
*
* \param[in] ProcessHandle Optional handle to the process to get stats of. If
* not provided the global stats are returned.
* \param[out] Stats Receives the informer statistics.
* \param[in] AccessMode The mode in which to perform access checks.
*
* \return Successful or errant status.
*/
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KphGetInformerStats(
_In_opt_ HANDLE ProcessHandle,
_Out_ PKPH_INFORMER_STATS Stats,
_In_ KPROCESSOR_MODE AccessMode
)
{
NTSTATUS status;
PKPH_INFORMER_STATE state;
PEPROCESS processObject;
PKPH_PROCESS_CONTEXT processContext;
KPH_PAGED_CODE_PASSIVE();
state = NULL;
processObject = NULL;
processContext = NULL;
status = KphZeroModeMemory(Stats, sizeof(KPH_INFORMER_STATS), AccessMode);
if (!NT_SUCCESS(status))
{
goto Exit;
}
if (ProcessHandle)
{
status = ObReferenceObjectByHandle(ProcessHandle,
0,
*PsProcessType,
AccessMode,
&processObject,
NULL);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
GENERAL,
"ObReferenceObjectByHandle failed: %!STATUS!",
status);
processObject = NULL;
goto Exit;
}
processContext = KphGetEProcessContext(processObject);
if (!processContext)
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
GENERAL,
"KphGetEProcessContext return null.");
status = STATUS_INSUFFICIENT_RESOURCES;
goto Exit;
}
state = KphAtomicReferenceObject(&processContext->InformerState.Atomic);
if (!state)
{
status = STATUS_NOT_FOUND;
goto Exit;
}
}
else
{
state = KphAtomicReferenceObject(&KphpInformerState.Atomic);
}
NT_ASSERT(state);
for (ULONG i = 0; i < KPH_INFORMER_COUNT; i++)
{
status = KphCopyToMode(&Stats->RateLimit[i].Policy,
&state->RateLimit[i].Policy,
sizeof(KPH_RATE_LIMIT_POLICY),
AccessMode);
if (!NT_SUCCESS(status))
{
goto Exit;
}
status = KphWriteLong64ToMode(&Stats->RateLimit[i].Allowed,
ReadNoFence64(&state->RateLimit[i].Allowed),
AccessMode);
if (!NT_SUCCESS(status))
{
goto Exit;
}
status = KphWriteLong64ToMode(&Stats->RateLimit[i].Dropped,
ReadNoFence64(&state->RateLimit[i].Dropped),
AccessMode);
if (!NT_SUCCESS(status))
{
goto Exit;
}
status = KphWriteLong64ToMode(&Stats->RateLimit[i].CasMiss,
ReadNoFence64(&state->RateLimit[i].CasMiss),
AccessMode);
if (!NT_SUCCESS(status))
{
goto Exit;
}
}
Exit:
if (processContext)
{
KphDereferenceObject(processContext);
}
if (processObject)
{
ObDereferenceObject(processObject);
}
if (state)
{
KphDereferenceObject(state);
}
return status;
}
/**
* \brief Cleans up informer infrastructure.
*/
_IRQL_requires_max_(PASSIVE_LEVEL)
VOID KphCleanupInformer(
VOID
)
{
KPH_PAGED_CODE_PASSIVE();
KphAtomicAssignObjectReference(&KphpInformerState.Atomic, NULL);
if (KphpInformerStateLookaside)
{
KphDereferenceObject(KphpInformerStateLookaside);
}
}
/**
* \brief Initialize the informer infrastructure.
*/
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KphInitializeInformer(
VOID
)
{
NTSTATUS status;
PKPH_INFORMER_STATE state;
KPH_OBJECT_TYPE_INFO typeInfo;
KPH_PAGED_CODE_PASSIVE();
state = NULL;
status = KphCreateNPagedLookasideObject(&KphpInformerStateLookaside,
KphAddObjectHeaderSize(sizeof(KPH_INFORMER_STATE)),
KPH_TAG_INFORMER_STATE);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
TRACKING,
"KphCreatePagedLookasideObject failed: %!STATUS!",
status);
KphpInformerStateLookaside = NULL;
goto Exit;
}
typeInfo.Allocate = KphpAllocateInformerState;
typeInfo.Initialize = KphpInitializeInformerState;
typeInfo.Delete = NULL;
typeInfo.Free = KphpFreeInformerState;
KphCreateObjectType(&KphpInformerStateTypeName,
&typeInfo,
&KphpInformerStateType);
status = KphCreateObject(KphpInformerStateType,
sizeof(KPH_INFORMER_STATE),
&state,
NULL);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
TRACKING,
"KphCreateObject failed: %!STATUS!",
status);
state = NULL;
goto Exit;
}
KphAtomicAssignObjectReference(&KphpInformerState.Atomic, state);
Exit:
if (state)
{
KphDereferenceObject(state);
}
return status;
}
================================================
FILE: KSystemInformer/informer_debug.c
================================================
/*
* Copyright (c) 2022 Winsider Seminars & Solutions, Inc. All rights reserved.
*
* This file is part of System Informer.
*
* Authors:
*
* jxy-s 2022-2026
*
*/
#include
#include
#include
#include
#include
typedef struct _KPH_DBG_PRINT_SLOT
{
KDPC Dpc;
LARGE_INTEGER TimeStamp;
CLIENT_ID ContextClientId;
ULONG ComponentId;
ULONG Level;
USHORT Length;
CHAR Buffer[FIELD_SIZE(KPH_MESSAGE, _Dyn.Buffer)];
} KPH_DBG_PRINT_SLOT, *PKPH_DBG_PRINT_SLOT;
static BOOLEAN KphpDbgPrintInitialized = FALSE;
static KSPIN_LOCK KphpDbgPrintLock;
static ULONG KphpDbgPrintSlotNext = 0;
static ULONG KphpDbgPrintSlotCount = 0;
static PKPH_DBG_PRINT_SLOT KphpDbgPrintSlots = NULL;
/**
* \brief Flushes the debug print slots to the communication port.
*/
_IRQL_requires_max_(DISPATCH_LEVEL)
_IRQL_requires_min_(DISPATCH_LEVEL)
_IRQL_requires_(DISPATCH_LEVEL)
_Requires_lock_held_(KphpDbgPrintLock)
VOID KphpDebugPrintFlush(
VOID
)
{
KPH_NPAGED_CODE_DISPATCH();
for (ULONG i = 0; i < KphpDbgPrintSlotNext; i++)
{
NTSTATUS status;
PKPH_PROCESS_CONTEXT process;
BOOLEAN enabled;
USHORT remaining;
ANSI_STRING output;
PKPH_MESSAGE msg;
PKPH_DBG_PRINT_SLOT slot;
slot = &KphpDbgPrintSlots[i];
process = KphGetProcessContext(slot->ContextClientId.UniqueProcess);
enabled = KphInformerEnabled(DebugPrint, process);
if (process)
{
KphDereferenceObjectDeferDelete(process);
}
if (!enabled)
{
continue;
}
msg = KphAllocateNPagedMessage();
if (!msg)
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
INFORMER,
"Failed to allocate message");
continue;
}
KphMsgInit(msg, KphMsgDebugPrint);
msg->Header.TimeStamp.QuadPart = slot->TimeStamp.QuadPart;
msg->Kernel.DebugPrint.ContextClientId.UniqueProcess = slot->ContextClientId.UniqueProcess;
msg->Kernel.DebugPrint.ContextClientId.UniqueThread = slot->ContextClientId.UniqueThread;
msg->Kernel.DebugPrint.ComponentId = slot->ComponentId;
msg->Kernel.DebugPrint.Level = slot->Level;
remaining = KphMsgDynRemaining(msg);
if (slot->Length > remaining)
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
INFORMER,
"Truncated debug print line");
}
output.Length = min(slot->Length, remaining);
output.MaximumLength = slot->Length;
output.Buffer = slot->Buffer;
status = KphMsgDynAddAnsiString(msg, KphMsgFieldOutput, &output);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
INFORMER,
"KphMsgDynAddAnsiString failed: %!STATUS!",
status);
}
KphCommsSendNPagedMessageAsync(msg);
}
KphpDbgPrintSlotNext = 0;
}
/**
* \brief DPC routine for debug informer.
*
* \param Dpc Unused
* \param DeferredContext Unused
* \param SystemArgument1 Unused
* \param SystemArgument2 Unused
*/
_Function_class_(KDEFERRED_ROUTINE)
_IRQL_requires_max_(DISPATCH_LEVEL)
_IRQL_requires_min_(DISPATCH_LEVEL)
_IRQL_requires_(DISPATCH_LEVEL)
_IRQL_requires_same_
VOID KphpDebugPrintDpc(
_In_ PKDPC Dpc,
_In_opt_ PVOID DeferredContext,
_In_opt_ PVOID SystemArgument1,
_In_opt_ PVOID SystemArgument2
)
{
KIRQL oldIrql;
UNREFERENCED_PARAMETER(Dpc);
UNREFERENCED_PARAMETER(DeferredContext);
UNREFERENCED_PARAMETER(SystemArgument1);
UNREFERENCED_PARAMETER(SystemArgument2);
//
// We use threaded DPCs so we could be invoked at passive or dispatch.
//
oldIrql = KeAcquireSpinLockForDpc(&KphpDbgPrintLock);
KphpDebugPrintFlush();
KeReleaseSpinLockForDpc(&KphpDbgPrintLock, oldIrql);
}
/**
* \brief Debug print callback.
*
* \param[in] Output Debug print string output.
* \param[in] ComponentId Component ID printing to the debug output.
* \param[in] Level Debug print level.
*/
_IRQL_always_function_min_(DISPATCH_LEVEL)
VOID KphpDebugPrintCallback(
_In_ PSTRING Output,
_In_ ULONG ComponentId,
_In_ ULONG Level
)
{
PKPH_DBG_PRINT_SLOT slot;
KPH_NPAGED_CODE_DISPATCH_MIN();
slot = NULL;
if (!Output->Buffer || (Output->Length == 0))
{
return;
}
KeAcquireSpinLockAtDpcLevel(&KphpDbgPrintLock);
if (KphpDbgPrintSlotNext >= KphpDbgPrintSlotCount)
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
INFORMER,
"Out of debug print slots!");
goto Exit;
}
slot = &KphpDbgPrintSlots[KphpDbgPrintSlotNext++];
KeQuerySystemTime(&slot->TimeStamp);
slot->ContextClientId.UniqueProcess = PsGetCurrentProcessId();
slot->ContextClientId.UniqueThread = PsGetCurrentThreadId();
slot->ComponentId = ComponentId;
slot->Level = Level;
slot->Length = min(Output->Length, ARRAYSIZE(slot->Buffer));
RtlCopyMemory(slot->Buffer, Output->Buffer, slot->Length);
if (Output->Length > ARRAYSIZE(slot->Buffer))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
INFORMER,
"Truncated debug print line");
}
Exit:
KeReleaseSpinLockFromDpcLevel(&KphpDbgPrintLock);
if (slot)
{
KeInsertQueueDpc(&slot->Dpc, NULL, NULL);
}
}
/**
* \brief Starts the debug informer.
*
* \return Successful or errant status.
*/
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KphDebugInformerStart(
VOID
)
{
NTSTATUS status;
ULONG count;
KPH_NPAGED_CODE_PASSIVE();
NT_ASSERT(KphpDbgPrintSlotNext == 0);
NT_ASSERT(!KphpDbgPrintInitialized);
KeInitializeSpinLock(&KphpDbgPrintLock);
count = (KeQueryActiveProcessorCountEx(ALL_PROCESSOR_GROUPS) * 2);
KphpDbgPrintSlots = KphAllocateNPaged((sizeof(KPH_DBG_PRINT_SLOT) * count),
KPH_TAG_DBG_SLOTS);
if (!KphpDbgPrintSlots)
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
INFORMER,
"Failed to allocate slots for debug print callback");
status = STATUS_INSUFFICIENT_RESOURCES;
goto Exit;
}
RtlZeroMemory(KphpDbgPrintSlots, sizeof(KPH_DBG_PRINT_SLOT) * count);
KphpDbgPrintSlotCount = count;
for (ULONG i = 0; i < KphpDbgPrintSlotCount; i++)
{
KeInitializeThreadedDpc(&KphpDbgPrintSlots[i].Dpc,
KphpDebugPrintDpc,
NULL);
}
status = DbgSetDebugPrintCallback(KphpDebugPrintCallback, TRUE);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
INFORMER,
"Failed to register debug print callback: %!STATUS!",
status);
goto Exit;
}
status = STATUS_SUCCESS;
KphpDbgPrintInitialized = TRUE;
Exit:
if (!NT_SUCCESS(status))
{
NT_ASSERT(!KphpDbgPrintInitialized);
KphpDbgPrintSlotCount = 0;
if (KphpDbgPrintSlots)
{
KphFree(KphpDbgPrintSlots, KPH_TAG_DBG_SLOTS);
KphpDbgPrintSlots = NULL;
}
}
return status;
}
/**
* \brief Stops the debug informer.
*/
_IRQL_requires_max_(PASSIVE_LEVEL)
VOID KphDebugInformerStop(
VOID
)
{
KIRQL oldIrql;
KPH_NPAGED_CODE_PASSIVE();
if (!KphpDbgPrintInitialized)
{
return;
}
DbgSetDebugPrintCallback(KphpDebugPrintCallback, FALSE);
for (ULONG i = 0; i < KphpDbgPrintSlotCount; i++)
{
KeRemoveQueueDpcEx(&KphpDbgPrintSlots[i].Dpc, TRUE);
}
KeAcquireSpinLock(&KphpDbgPrintLock, &oldIrql);
KphpDebugPrintFlush();
KphpDbgPrintSlotCount = 0;
KphFree(KphpDbgPrintSlots, KPH_TAG_DBG_SLOTS);
KphpDbgPrintSlots = NULL;
KeReleaseSpinLock(&KphpDbgPrintLock, oldIrql);
KphpDbgPrintInitialized = FALSE;
}
================================================
FILE: KSystemInformer/informer_file.c
================================================
/*
* Copyright (c) 2022 Winsider Seminars & Solutions, Inc. All rights reserved.
*
* This file is part of System Informer.
*
* Authors:
*
* jxy-s 2022-2026
*
*/
#include
#include
#include
#include
KPH_PROTECTED_DATA_SECTION_RO_PUSH();
static const UNICODE_STRING KphpDefaultInstaceName = RTL_CONSTANT_STRING(L"DefaultInstance");
static const UNICODE_STRING KphpAltitudeName = RTL_CONSTANT_STRING(L"Altitude");
static const UNICODE_STRING KphpFlagsName = RTL_CONSTANT_STRING(L"Flags");
static const DWORD KphpFlagsValue = 0;
KPH_PROTECTED_DATA_SECTION_RO_POP();
KPH_PROTECTED_DATA_SECTION_PUSH();
PFLT_FILTER KphFltFilter = NULL;
KPH_PROTECTED_DATA_SECTION_POP();
KPH_PAGED_FILE();
/**
* \brief Invoked when the mini-filter driver is asked to unload.
*
* \param[in] Flags Unused
*
* \return STATUS_SUCCESS
*/
_Function_class_(PFLT_FILTER_UNLOAD_CALLBACK)
_IRQL_requires_max_(PASSIVE_LEVEL)
NTSTATUS FLTAPI KphpFltFilterUnloadCallback(
_In_ FLT_FILTER_UNLOAD_FLAGS Flags
)
{
KPH_PAGED_CODE_PASSIVE();
UNREFERENCED_PARAMETER(Flags);
KphTracePrint(TRACE_LEVEL_VERBOSE, INFORMER, "Filter unload invoked");
if (KphIsDriverUnloadProtected())
{
return STATUS_FLT_DO_NOT_DETACH;
}
return STATUS_SUCCESS;
}
/**
* \brief Invoked when the mini-filter instance is asked to be removed.
*
* \param[in] FltObjects Unused
* \param[in] Flags Unused
*
* \return STATUS_SUCCESS
*/
_Function_class_(PFLT_INSTANCE_QUERY_TEARDOWN_CALLBACK)
_IRQL_requires_max_(PASSIVE_LEVEL)
NTSTATUS FLTAPI KphpFltInstanceQueryTeardownCallback(
_In_ PCFLT_RELATED_OBJECTS FltObjects,
_In_ FLT_INSTANCE_QUERY_TEARDOWN_FLAGS Flags
)
{
KPH_PAGED_CODE_PASSIVE();
UNREFERENCED_PARAMETER(FltObjects);
UNREFERENCED_PARAMETER(Flags);
KphTracePrint(TRACE_LEVEL_VERBOSE,
INFORMER,
"Filter query teardown invoked");
if (KphIsDriverUnloadProtected())
{
return STATUS_FLT_DO_NOT_DETACH;
}
return STATUS_SUCCESS;
}
KPH_PROTECTED_DATA_SECTION_RO_PUSH();
static const FLT_OPERATION_REGISTRATION KphpFltOperationRegistration[] =
{
{ IRP_MJ_CREATE, 0, KphpFltPreOp, KphpFltPostOp, 0 },
{ IRP_MJ_CREATE_NAMED_PIPE, 0, KphpFltPreOp, KphpFltPostOp, 0 },
{ IRP_MJ_CLOSE, 0, KphpFltPreOp, KphpFltPostOp, 0 },
{ IRP_MJ_READ, 0, KphpFltPreOp, KphpFltPostOp, 0 },
{ IRP_MJ_WRITE, 0, KphpFltPreOp, KphpFltPostOp, 0 },
{ IRP_MJ_QUERY_INFORMATION, 0, KphpFltPreOp, KphpFltPostOp, 0 },
{ IRP_MJ_SET_INFORMATION, 0, KphpFltPreOp, KphpFltPostOp, 0 },
{ IRP_MJ_QUERY_EA, 0, KphpFltPreOp, KphpFltPostOp, 0 },
{ IRP_MJ_SET_EA, 0, KphpFltPreOp, KphpFltPostOp, 0 },
{ IRP_MJ_FLUSH_BUFFERS, 0, KphpFltPreOp, KphpFltPostOp, 0 },
{ IRP_MJ_QUERY_VOLUME_INFORMATION, 0, KphpFltPreOp, KphpFltPostOp, 0 },
{ IRP_MJ_SET_VOLUME_INFORMATION, 0, KphpFltPreOp, KphpFltPostOp, 0 },
{ IRP_MJ_DIRECTORY_CONTROL, 0, KphpFltPreOp, KphpFltPostOp, 0 },
{ IRP_MJ_FILE_SYSTEM_CONTROL, 0, KphpFltPreOp, KphpFltPostOp, 0 },
{ IRP_MJ_DEVICE_CONTROL, 0, KphpFltPreOp, KphpFltPostOp, 0 },
{ IRP_MJ_INTERNAL_DEVICE_CONTROL, 0, KphpFltPreOp, KphpFltPostOp, 0 },
{ IRP_MJ_SHUTDOWN, 0, KphpFltPreOp, NULL, 0 },
{ IRP_MJ_LOCK_CONTROL, 0, KphpFltPreOp, KphpFltPostOp, 0 },
{ IRP_MJ_CLEANUP, 0, KphpFltPreOp, KphpFltPostOp, 0 },
{ IRP_MJ_CREATE_MAILSLOT, 0, KphpFltPreOp, KphpFltPostOp, 0 },
{ IRP_MJ_QUERY_SECURITY, 0, KphpFltPreOp, KphpFltPostOp, 0 },
{ IRP_MJ_SET_SECURITY, 0, KphpFltPreOp, KphpFltPostOp, 0 },
{ IRP_MJ_POWER, 0, KphpFltPreOp, KphpFltPostOp, 0 },
{ IRP_MJ_SYSTEM_CONTROL, 0, KphpFltPreOp, KphpFltPostOp, 0 },
{ IRP_MJ_DEVICE_CHANGE, 0, KphpFltPreOp, KphpFltPostOp, 0 },
{ IRP_MJ_QUERY_QUOTA, 0, KphpFltPreOp, KphpFltPostOp, 0 },
{ IRP_MJ_SET_QUOTA, 0, KphpFltPreOp, KphpFltPostOp, 0 },
{ IRP_MJ_PNP, 0, KphpFltPreOp, KphpFltPostOp, 0 },
{ IRP_MJ_ACQUIRE_FOR_SECTION_SYNCHRONIZATION, 0, KphpFltPreOp, KphpFltPostOp, 0 },
{ IRP_MJ_RELEASE_FOR_SECTION_SYNCHRONIZATION, 0, KphpFltPreOp, KphpFltPostOp, 0 },
{ IRP_MJ_ACQUIRE_FOR_MOD_WRITE, 0, KphpFltPreOp, KphpFltPostOp, 0 },
{ IRP_MJ_RELEASE_FOR_MOD_WRITE, 0, KphpFltPreOp, KphpFltPostOp, 0 },
{ IRP_MJ_ACQUIRE_FOR_CC_FLUSH, 0, KphpFltPreOp, KphpFltPostOp, 0 },
{ IRP_MJ_RELEASE_FOR_CC_FLUSH, 0, KphpFltPreOp, KphpFltPostOp, 0 },
{ IRP_MJ_QUERY_OPEN, 0, KphpFltPreOp, KphpFltPostOp, 0 },
{ IRP_MJ_FAST_IO_CHECK_IF_POSSIBLE, 0, KphpFltPreOp, KphpFltPostOp, 0 },
{ IRP_MJ_NETWORK_QUERY_OPEN, 0, KphpFltPreOp, KphpFltPostOp, 0 },
{ IRP_MJ_MDL_READ, 0, KphpFltPreOp, KphpFltPostOp, 0 },
{ IRP_MJ_MDL_READ_COMPLETE, 0, KphpFltPreOp, KphpFltPostOp, 0 },
{ IRP_MJ_PREPARE_MDL_WRITE, 0, KphpFltPreOp, KphpFltPostOp, 0 },
{ IRP_MJ_MDL_WRITE_COMPLETE, 0, KphpFltPreOp, KphpFltPostOp, 0 },
{ IRP_MJ_VOLUME_MOUNT, 0, KphpFltPreOp, KphpFltPostOp, 0 },
{ IRP_MJ_VOLUME_DISMOUNT, 0, KphpFltPreOp, KphpFltPostOp, 0 },
{ IRP_MJ_OPERATION_END }
};
static const FLT_CONTEXT_REGISTRATION KphpFltContextRegistration[] =
{
{
FLT_STREAMHANDLE_CONTEXT,
0,
NULL,
FLT_VARIABLE_SIZED_CONTEXTS,
KPH_TAG_FLT_STREAMHANDLE_CONTEXT
},
{ FLT_CONTEXT_END }
};
static const FLT_REGISTRATION KphpFltRegistration =
{
sizeof(FLT_REGISTRATION),
FLT_REGISTRATION_VERSION,
(FLTFL_REGISTRATION_SUPPORT_NPFS_MSFS |
FLTFL_REGISTRATION_SUPPORT_DAX_VOLUME |
FLTFL_REGISTRATION_SUPPORT_WCOS),
KphpFltContextRegistration,
KphpFltOperationRegistration,
KphpFltFilterUnloadCallback,
NULL,
KphpFltInstanceQueryTeardownCallback,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL
};
KPH_PROTECTED_DATA_SECTION_RO_POP();
/**
* \brief Registers the driver with the mini-filter.
*
* \param[in] DriverObject Driver object of this driver.
* \param[in] RegistryPath Registry path from the entry point.
*
* \return Successful or errant status.
*/
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KphFltRegister(
_In_ PDRIVER_OBJECT DriverObject,
_In_ PUNICODE_STRING RegistryPath
)
{
NTSTATUS status;
OBJECT_ATTRIBUTES objectAttributes;
HANDLE instancesKeyHandle;
HANDLE defaultInstanceKeyHandle;
WCHAR instancesBuffer[MAX_PATH];
UNICODE_STRING keyName;
BYTE objectInfoBuffer[MAX_PATH];
POBJECT_NAME_INFORMATION objectInfo;
ULONG returnLength;
KPH_PAGED_CODE_PASSIVE();
NT_ASSERT(!KphFltFilter);
instancesKeyHandle = NULL;
defaultInstanceKeyHandle = NULL;
objectInfo = (POBJECT_NAME_INFORMATION)objectInfoBuffer;
status = ObQueryNameString(DriverObject,
objectInfo,
sizeof(objectInfoBuffer),
&returnLength);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
INFORMER,
"ObQueryNameString failed: %!STATUS!",
status);
goto Exit;
}
for (USHORT i = (objectInfo->Name.Length / sizeof(WCHAR)); i > 0; i--)
{
if (i == 1)
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
INFORMER,
"Driver object name is invalid");
status = STATUS_OBJECT_NAME_INVALID;
goto Exit;
}
if (objectInfo->Name.Buffer[i - 1] == OBJ_NAME_PATH_SEPARATOR)
{
objectInfo->Name.Length -= (i * sizeof(WCHAR));
RtlMoveMemory(objectInfo->Name.Buffer,
&objectInfo->Name.Buffer[i],
objectInfo->Name.Length);
break;
}
}
//
// We null terminate this for the registry later.
//
NT_ASSERT(objectInfo->Name.MaximumLength >= (objectInfo->Name.Length + sizeof(WCHAR)));
objectInfo->Name.Buffer[objectInfo->Name.Length / sizeof(WCHAR)] = UNICODE_NULL;
keyName.Buffer = instancesBuffer;
keyName.Length = 0;
keyName.MaximumLength = sizeof(instancesBuffer);
status = RtlAppendUnicodeStringToString(&keyName, RegistryPath);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
INFORMER,
"RtlAppendUnicodeStringToString failed: %!STATUS!",
status);
goto Exit;
}
status = RtlAppendUnicodeToString(&keyName, L"\\Instances");
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
INFORMER,
"RtlAppendUnicodeToString failed: %!STATUS!",
status);
goto Exit;
}
InitializeObjectAttributes(&objectAttributes,
&keyName,
OBJ_KERNEL_HANDLE,
NULL,
NULL);
status = ZwCreateKey(&instancesKeyHandle,
KEY_ALL_ACCESS,
&objectAttributes,
0,
NULL,
0,
NULL);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
INFORMER,
"ZwCreateKey failed: %!STATUS!",
status);
instancesKeyHandle = NULL;
goto Exit;
}
//
// We guarantee this above.
//
NT_ASSERT(objectInfo->Name.MaximumLength >= (objectInfo->Name.Length + sizeof(WCHAR)));
NT_ASSERT(objectInfo->Name.Buffer[objectInfo->Name.Length / sizeof(WCHAR)] == UNICODE_NULL);
status = ZwSetValueKey(instancesKeyHandle,
(PUNICODE_STRING)&KphpDefaultInstaceName,
0,
REG_SZ,
objectInfo->Name.Buffer,
objectInfo->Name.Length + sizeof(WCHAR));
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
INFORMER,
"ZwSetValueKey failed: %!STATUS!",
status);
goto Exit;
}
status = RtlAppendUnicodeToString(&keyName, L"\\");
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
INFORMER,
"RtlAppendUnicodeToString failed: %!STATUS!",
status);
goto Exit;
}
status = RtlAppendUnicodeStringToString(&keyName, &objectInfo->Name);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
INFORMER,
"RtlAppendUnicodeStringToString failed: %!STATUS!",
status);
goto Exit;
}
InitializeObjectAttributes(&objectAttributes,
&keyName,
OBJ_KERNEL_HANDLE,
NULL,
NULL);
status = ZwCreateKey(&defaultInstanceKeyHandle,
KEY_ALL_ACCESS,
&objectAttributes,
0,
NULL,
0,
NULL);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
INFORMER,
"ZwCreateKey failed: %!STATUS!",
status);
defaultInstanceKeyHandle = NULL;
goto Exit;
}
NT_ASSERT(KphAltitude);
NT_ASSERT(KphAltitude->MaximumLength >= (KphAltitude->Length + sizeof(WCHAR)));
NT_ASSERT(KphAltitude->Buffer[KphAltitude->Length / sizeof(WCHAR)] == UNICODE_NULL);
status = ZwSetValueKey(defaultInstanceKeyHandle,
(PUNICODE_STRING)&KphpAltitudeName,
0,
REG_SZ,
KphAltitude->Buffer,
KphAltitude->Length + sizeof(WCHAR));
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
INFORMER,
"ZwSetValueKey failed: %!STATUS!",
status);
goto Exit;
}
status = ZwSetValueKey(defaultInstanceKeyHandle,
(PUNICODE_STRING)&KphpFlagsName,
0,
REG_DWORD,
(PVOID)&KphpFlagsValue,
sizeof(KphpFlagsValue));
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
INFORMER,
"ZwSetValueKey failed: %!STATUS!",
status);
goto Exit;
}
status = FltRegisterFilter(DriverObject,
&KphpFltRegistration,
&KphFltFilter);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
INFORMER,
"FltRegisterFilter failed: %!STATUS!",
status);
KphFltFilter = NULL;
goto Exit;
}
status = KphCommsStart();
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
INFORMER,
"KphCommsStart failed: %!STATUS!",
status);
goto Exit;
}
status = STATUS_SUCCESS;
Exit:
if (instancesKeyHandle)
{
ObCloseHandle(instancesKeyHandle, KernelMode);
}
if (defaultInstanceKeyHandle)
{
ObCloseHandle(defaultInstanceKeyHandle, KernelMode);
}
return status;
}
/**
* \brief Unregisters this driver with the mini-filter.
*/
_IRQL_requires_max_(PASSIVE_LEVEL)
VOID KphFltUnregister(
VOID
)
{
KPH_PAGED_CODE_PASSIVE();
KphCommsStop();
if (!KphFltFilter)
{
return;
}
FltUnregisterFilter(KphFltFilter);
KphpFltCleanupFileNameCache();
KphpFltCleanupFileOp();
}
/**
* \brief Begins informing on file activity from the mini-filter.
*
* \return Successful or errant status.
*/
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KphFltInformerStart(
VOID
)
{
KPH_PAGED_CODE_PASSIVE();
NT_ASSERT(KphFltFilter);
KphpFltInitializeFileOp();
KphpFltInitializeFileNameCache();
return FltStartFiltering(KphFltFilter);
}
================================================
FILE: KSystemInformer/informer_filenc.c
================================================
/*
* Copyright (c) 2022 Winsider Seminars & Solutions, Inc. All rights reserved.
*
* This file is part of System Informer.
*
* Authors:
*
* jxy-s 2023-2026
*
*/
#include
#include
#include
KPH_PROTECTED_DATA_SECTION_RO_PUSH();
static UNICODE_STRING KphpCachedFileNameTypeName = RTL_CONSTANT_STRING(L"KphCachedFileName");
KPH_PROTECTED_DATA_SECTION_RO_POP();
KPH_PROTECTED_DATA_SECTION_PUSH();
static PKPH_OBJECT_TYPE KphpCachedFileNameType = NULL;
static BOOLEAN KphpFileNameCacheInitialized = FALSE;
KPH_PROTECTED_DATA_SECTION_POP();
//
// We use a simple list and spin lock here since we are very selective about
// what we cache. The list is not expected to grow very large.
//
static ALIGNED_EX_SPINLOCK KphpFileNameCacheLock = 0;
static LIST_ENTRY KphpFileNameCacheList;
/**
* \brief Retrieves file name information using the filter name cache.
*
* \param[in] Data The callback data for the operation.
* \param[out] FltFileName Receives the file name.
*
* \return Successful or errant status.
*/
_IRQL_requires_max_(APC_LEVEL)
_Must_inspect_result_
NTSTATUS KphpFltGetFileNameInformation(
_In_ PFLT_CALLBACK_DATA Data,
_Out_ PKPH_FLT_FILE_NAME FltFileName
)
{
NTSTATUS status;
ULONG flags;
KPH_NPAGED_CODE_APC_MAX_FOR_PAGING_IO();
NT_ASSERT(Data->Iopb->TargetFileObject);
flags = (FLT_FILE_NAME_NORMALIZED |
FLT_FILE_NAME_QUERY_ALWAYS_ALLOW_CACHE_LOOKUP);
status = FltGetFileNameInformation(Data, flags, &FltFileName->NameInfo);
if (NT_SUCCESS(status))
{
FltFileName->Type = KphFltFileNameTypeNameInfo;
return status;
}
flags = (FLT_FILE_NAME_OPENED |
FLT_FILE_NAME_QUERY_ALWAYS_ALLOW_CACHE_LOOKUP);
status = FltGetFileNameInformation(Data, flags, &FltFileName->NameInfo);
if (NT_SUCCESS(status))
{
FltFileName->Type = KphFltFileNameTypeNameInfo;
}
return status;
}
/**
* \brief Retrieves the length of the file name from the related objects.
*
* \param[in] FltObjects The related objects for the operation.
*
* \return The length of the file name.
*/
_IRQL_requires_max_(APC_LEVEL)
ULONG KphpFltNameCacheFileNameLength(
_In_ PCFLT_RELATED_OBJECTS FltObjects
)
{
ULONG length;
KPH_NPAGED_CODE_APC_MAX_FOR_PAGING_IO();
NT_ASSERT(FltObjects->FileObject && FltObjects->Volume);
NT_VERIFY(FltGetVolumeName(FltObjects->Volume, NULL, &length)
== STATUS_BUFFER_TOO_SMALL);
length += FltObjects->FileObject->FileName.Length;
return length;
}
/**
* \brief Copies the file name into the related objects.
*
* \param[in] FltObjects The related objects for the operation.
* \param[in,out] FileName The file name to copy into.
*/
_IRQL_requires_max_(APC_LEVEL)
VOID KphpFltNameCacheCopyFileName(
_In_ PCFLT_RELATED_OBJECTS FltObjects,
_Inout_ PUNICODE_STRING FileName
)
{
PUNICODE_STRING fileName;
KPH_NPAGED_CODE_APC_MAX_FOR_PAGING_IO();
NT_ASSERT(FltObjects->FileObject && FltObjects->Volume);
NT_VERIFY(NT_SUCCESS(FltGetVolumeName(FltObjects->Volume, FileName, NULL)));
fileName = &FltObjects->FileObject->FileName;
NT_VERIFY(NT_SUCCESS(RtlAppendUnicodeStringToString(FileName, fileName)));
}
/**
* \brief Retrieves the file name, using the stream handle context as a cache.
*
* \param[in] FltObjects The related objects for the operation.
* \param[out] FltFileName Receives the file name.
*
* \return Successful or errant status.
*/
_IRQL_requires_max_(APC_LEVEL)
_Must_inspect_result_
NTSTATUS KphpFltGetFileNameUseContext(
_In_ PCFLT_RELATED_OBJECTS FltObjects,
_Out_ PKPH_FLT_FILE_NAME FltFileName
)
{
NTSTATUS status;
ULONG length;
PFLT_CONTEXT oldContext;
KPH_NPAGED_CODE_APC_MAX_FOR_PAGING_IO();
NT_ASSERT(FltObjects->FileObject);
//
// Check if the context already exists.
//
status = FltGetStreamHandleContext(FltObjects->Instance,
FltObjects->FileObject,
&FltFileName->Context);
if (NT_SUCCESS(status))
{
FltFileName->Type = KphFltFileNameTypeContext;
return status;
}
//
// Try to create a new context.
//
length = KphpFltNameCacheFileNameLength(FltObjects);
status = FltAllocateContext(FltObjects->Filter,
FLT_STREAMHANDLE_CONTEXT,
length + sizeof(UNICODE_STRING),
NonPagedPoolNx,
&FltFileName->Context);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
INFORMER,
"FltAllocateContext failed: %!STATUS!",
status);
FltFileName->Context = NULL;
return status;
}
NT_ASSERT(FltFileName->Context == FltFileName->FileName);
FltFileName->FileName->Length = 0;
FltFileName->FileName->MaximumLength = (USHORT)length;
FltFileName->FileName->Buffer = Add2Ptr(FltFileName->FileName,
sizeof(UNICODE_STRING));
KphpFltNameCacheCopyFileName(FltObjects, FltFileName->FileName);
//
// Set the new context.
//
status = FltSetStreamHandleContext(FltObjects->Instance,
FltObjects->FileObject,
FLT_SET_CONTEXT_KEEP_IF_EXISTS,
FltFileName->Context,
&oldContext);
if (NT_SUCCESS(status))
{
FltFileName->Type = KphFltFileNameTypeContext;
return status;
}
//
// We raced and lost, or failed for some other reason.
//
FltReleaseContext(FltFileName->Context);
FltFileName->Context = NULL;
if (status == STATUS_FLT_CONTEXT_ALREADY_DEFINED)
{
//
// Return the existing context.
//
FltFileName->Type = KphFltFileNameTypeContext;
FltFileName->Context = oldContext;
return STATUS_SUCCESS;
}
//
// This is unexpected, we could do more juggling, but instead choose to
// just give up.
//
KphTracePrint(TRACE_LEVEL_VERBOSE,
INFORMER,
"FltSetStreamHandleContext failed: %!STATUS!",
status);
return status;
}
/**
* \brief Retrieves the file name, using the global name cache.
*
* \param[in] FltObjects The related objects for the operation.
* \param[out] FltFileName Receives the file name.
*
* \return Successful or errant status.
*/
_IRQL_requires_max_(APC_LEVEL)
_Must_inspect_result_
NTSTATUS KphpFltGetFileNameUseNameCache(
_In_ PCFLT_RELATED_OBJECTS FltObjects,
_Out_ PKPH_FLT_FILE_NAME FltFileName
)
{
NTSTATUS status;
KIRQL previousIrql;
ULONG length;
PKPH_FLT_FILE_NAME_CACHE_ENTRY cacheEntry;
PKPH_FLT_FILE_NAME_CACHE_ENTRY existingCacheEntry;
KPH_NPAGED_CODE_APC_MAX_FOR_PAGING_IO();
NT_ASSERT(FltObjects->FileObject);
//
// Check if the file object is already in the cache.
//
cacheEntry = NULL;
previousIrql = ExAcquireSpinLockShared(&KphpFileNameCacheLock);
for (PLIST_ENTRY entry = KphpFileNameCacheList.Flink;
entry != &KphpFileNameCacheList;
entry = entry->Flink)
{
cacheEntry = CONTAINING_RECORD(entry,
KPH_FLT_FILE_NAME_CACHE_ENTRY,
ListEntry);
if (FltObjects->FileObject == cacheEntry->FileObject)
{
KphReferenceObject(cacheEntry);
break;
}
cacheEntry = NULL;
}
ExReleaseSpinLockShared(&KphpFileNameCacheLock, previousIrql);
if (cacheEntry)
{
//
// Return a reference (taken above) to the existing cached entry.
//
FltFileName->NameCache = cacheEntry;
FltFileName->Type = KphFltFileNameTypeNameCache;
return STATUS_SUCCESS;
}
//
// Try to create new entry and insert it into the cache.
//
length = KphpFltNameCacheFileNameLength(FltObjects);
status = KphCreateObject(KphpCachedFileNameType,
length + sizeof(KPH_FLT_FILE_NAME_CACHE_ENTRY),
&cacheEntry,
NULL);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
INFORMER,
"KphCreateObject failed: %!STATUS!",
status);
return status;
}
cacheEntry->FileObject = FltObjects->FileObject;
cacheEntry->FileName.Length = 0;
cacheEntry->FileName.MaximumLength = (USHORT)length;
cacheEntry->FileName.Buffer = Add2Ptr(cacheEntry,
sizeof(KPH_FLT_FILE_NAME_CACHE_ENTRY));
KphpFltNameCacheCopyFileName(FltObjects, &cacheEntry->FileName);
//
// Insert it into the cache, and check if we've raced.
//
existingCacheEntry = NULL;
previousIrql = ExAcquireSpinLockExclusive(&KphpFileNameCacheLock);
for (PLIST_ENTRY entry = KphpFileNameCacheList.Flink;
entry != &KphpFileNameCacheList;
entry = entry->Flink)
{
existingCacheEntry = CONTAINING_RECORD(entry,
KPH_FLT_FILE_NAME_CACHE_ENTRY,
ListEntry);
if (cacheEntry->FileObject == existingCacheEntry->FileObject)
{
KphReferenceObject(existingCacheEntry);
break;
}
existingCacheEntry = NULL;
}
if (!existingCacheEntry)
{
//
// Insert the new entry (with a reference) in the list.
//
KphReferenceObject(cacheEntry);
InsertHeadList(&KphpFileNameCacheList, &cacheEntry->ListEntry);
}
ExReleaseSpinLockExclusive(&KphpFileNameCacheLock, previousIrql);
if (existingCacheEntry)
{
//
// Return the existing cached entry reference (taken above) to the
// caller, and drop the one we just created.
//
FltFileName->NameCache = existingCacheEntry;
KphDereferenceObject(cacheEntry);
}
else
{
//
// New entry inserted into the cache
//
FltFileName->NameCache = cacheEntry;
}
FltFileName->Type = KphFltFileNameTypeNameCache;
return STATUS_SUCCESS;
}
/**
* \brief Retrieves the file name, by copying it from the related objects.
*
* \param[in] FltObjects The related objects for the operation.
* \param[out] FltFileName Receives the file name.
*
* \return Successful or errant status.
*/
_IRQL_requires_max_(APC_LEVEL)
_Must_inspect_result_
NTSTATUS KphpFltGetFileNameCopy(
_In_ PCFLT_RELATED_OBJECTS FltObjects,
_Out_ PKPH_FLT_FILE_NAME FltFileName
)
{
ULONG length;
KPH_NPAGED_CODE_APC_MAX_FOR_PAGING_IO();
NT_ASSERT(FltObjects->Volume);
NT_ASSERT(FltObjects->FileObject);
length = KphpFltNameCacheFileNameLength(FltObjects);
FltFileName->FileName = KphAllocateNPaged(length + sizeof(UNICODE_STRING),
KPH_TAG_FLT_FILE_NAME);
if (!FltFileName->FileName)
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
INFORMER,
"Failed to allocate file name.");
return STATUS_INSUFFICIENT_RESOURCES;
}
FltFileName->FileName->Length = 0;
FltFileName->FileName->MaximumLength = (USHORT)length;
FltFileName->FileName->Buffer = Add2Ptr(FltFileName->FileName,
sizeof(UNICODE_STRING));
KphpFltNameCacheCopyFileName(FltObjects, FltFileName->FileName);
FltFileName->Type = KphFltFileNameTypeFileName;
return STATUS_SUCCESS;
}
/**
* \brief Retrieves the volume name, by copying it from the related objects.
*
* \param[in] FltObjects The related objects for the operation.
* \param[out] FltFileName Receives the file name.
*
* \return Successful or errant status.
*/
_IRQL_requires_max_(APC_LEVEL)
_Must_inspect_result_
NTSTATUS KphpFltGetVolumeName(
_In_ PCFLT_RELATED_OBJECTS FltObjects,
_Out_ PKPH_FLT_FILE_NAME FltFileName
)
{
ULONG length;
KPH_NPAGED_CODE_APC_MAX_FOR_PAGING_IO();
if (!FltObjects->Volume)
{
KphTracePrint(TRACE_LEVEL_VERBOSE, INFORMER, "Volume is null");
return STATUS_OBJECT_NAME_NOT_FOUND;
}
(VOID)FltGetVolumeName(FltObjects->Volume, NULL, &length);
FltFileName->FileName = KphAllocateNPaged(length + sizeof(UNICODE_STRING),
KPH_TAG_FLT_FILE_NAME);
if (!FltFileName->FileName)
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
INFORMER,
"Failed to allocate file name.");
return STATUS_INSUFFICIENT_RESOURCES;
}
FltFileName->FileName->Length = 0;
FltFileName->FileName->MaximumLength = (USHORT)length;
FltFileName->FileName->Buffer = Add2Ptr(FltFileName->FileName,
sizeof(UNICODE_STRING));
(VOID)FltGetVolumeName(FltObjects->Volume, FltFileName->FileName, NULL);
FltFileName->Type = KphFltFileNameTypeFileName;
return STATUS_SUCCESS;
}
/**
* \brief Retrieves the file name for the given callback data.
*
* \param[in] Data The callback data for the operation.
* \param[in] FltObjects The related objects for the operation.
* \param[out] FltFileName Receives the file name, must be related using
* KphpFltReleaseFileName.
*
* \return Successful or errant status.
*/
_IRQL_requires_max_(APC_LEVEL)
_Must_inspect_result_
NTSTATUS KphpFltGetFileName(
_In_ PFLT_CALLBACK_DATA Data,
_In_ PCFLT_RELATED_OBJECTS FltObjects,
_Out_ PKPH_FLT_FILE_NAME FltFileName
)
{
NTSTATUS status;
KPH_NPAGED_CODE_APC_MAX_FOR_PAGING_IO();
NT_ASSERT(FltObjects->FileObject == Data->Iopb->TargetFileObject);
KphpFltZeroFileName(FltFileName);
if (!FltObjects->FileObject)
{
if ((Data->Iopb->MajorFunction == IRP_MJ_VOLUME_MOUNT) ||
(Data->Iopb->MajorFunction == IRP_MJ_VOLUME_DISMOUNT))
{
return KphpFltGetVolumeName(FltObjects, FltFileName);
}
KphTracePrint(TRACE_LEVEL_VERBOSE, INFORMER, "FileObject is null");
return STATUS_OBJECT_NAME_NOT_FOUND;
}
status = KphpFltGetFileNameInformation(Data, FltFileName);
if (NT_SUCCESS(status))
{
return status;
}
if (!FltObjects->Volume)
{
KphTracePrint(TRACE_LEVEL_VERBOSE, INFORMER, "Volume is null");
return STATUS_OBJECT_NAME_NOT_FOUND;
}
if (FltSupportsStreamHandleContexts(FltObjects->FileObject))
{
status = KphpFltGetFileNameUseContext(FltObjects, FltFileName);
}
else if (FsRtlIsPagingFile(FltObjects->FileObject))
{
status = KphpFltGetFileNameUseNameCache(FltObjects, FltFileName);
}
else
{
status = KphpFltGetFileNameCopy(FltObjects, FltFileName);
}
return status;
}
/**
* \brief Retrieves destination file name information using the filter name
* cache.
*
* \param[in] Data The callback data for the operation.
* \param[in] FltObjects The related objects for the operation.
* \param[out] FltFileName Receives the destination file name.
*
* \return Successful or errant status.
*/
_IRQL_requires_max_(APC_LEVEL)
_Must_inspect_result_
NTSTATUS KphpFltGetDestinationFileNameInformation(
_In_ PFLT_CALLBACK_DATA Data,
_In_ PCFLT_RELATED_OBJECTS FltObjects,
_Out_ PKPH_FLT_FILE_NAME FltDestFileName
)
{
NTSTATUS status;
HANDLE rootDirectory;
PWSTR fileName;
ULONG fileNameLength;
ULONG flags;
KPH_NPAGED_CODE_APC_MAX_FOR_PAGING_IO();
NT_ASSERT(FltObjects->FileObject);
if (Data->Iopb->MajorFunction != IRP_MJ_SET_INFORMATION)
{
NT_ASSERT(Data->Iopb->MinorFunction == IRP_MJ_SET_INFORMATION);
return STATUS_INVALID_PARAMETER;
}
switch (Data->Iopb->Parameters.SetFileInformation.FileInformationClass)
{
case FileRenameInformation:
case FileRenameInformationBypassAccessCheck:
case FileRenameInformationEx:
case FileRenameInformationExBypassAccessCheck:
{
PFILE_RENAME_INFORMATION renameInfo;
renameInfo = Data->Iopb->Parameters.SetFileInformation.InfoBuffer;
rootDirectory = renameInfo->RootDirectory;
fileName = renameInfo->FileName;
fileNameLength = renameInfo->FileNameLength;
break;
}
case FileLinkInformation:
case FileLinkInformationBypassAccessCheck:
case FileLinkInformationEx:
case FileLinkInformationExBypassAccessCheck:
{
PFILE_LINK_INFORMATION linkInfo;
linkInfo = Data->Iopb->Parameters.SetFileInformation.InfoBuffer;
rootDirectory = linkInfo->RootDirectory;
fileName = linkInfo->FileName;
fileNameLength = linkInfo->FileNameLength;
break;
}
default:
{
NT_ASSERT(FALSE);
return STATUS_INVALID_PARAMETER;
}
}
flags = (FLT_FILE_NAME_NORMALIZED |
FLT_FILE_NAME_QUERY_ALWAYS_ALLOW_CACHE_LOOKUP);
status = FltGetDestinationFileNameInformation(FltObjects->Instance,
FltObjects->FileObject,
rootDirectory,
fileName,
fileNameLength,
flags,
&FltDestFileName->NameInfo);
if (NT_SUCCESS(status))
{
FltDestFileName->Type = KphFltFileNameTypeNameInfo;
return status;
}
flags = (FLT_FILE_NAME_OPENED |
FLT_FILE_NAME_QUERY_ALWAYS_ALLOW_CACHE_LOOKUP);
status = FltGetDestinationFileNameInformation(FltObjects->Instance,
FltObjects->FileObject,
rootDirectory,
fileName,
fileNameLength,
flags,
&FltDestFileName->NameInfo);
if (NT_SUCCESS(status))
{
FltDestFileName->Type = KphFltFileNameTypeNameInfo;
}
return status;
}
/**
* \brief Retrieves the destination file name for the given callback data.
*
* \param[in] Data The callback data for the operation.
* \param[in] FltObjects The related objects for the operation.
* \param[out] FltFileName Receives the destination file name, must be related
* using KphpFltReleaseFileName.
*
* \return Successful or errant status.
*/
_IRQL_requires_max_(APC_LEVEL)
_Must_inspect_result_
NTSTATUS KphpFltGetDestFileName(
_In_ PFLT_CALLBACK_DATA Data,
_In_ PCFLT_RELATED_OBJECTS FltObjects,
_Out_ PKPH_FLT_FILE_NAME FltDestFileName
)
{
KPH_NPAGED_CODE_APC_MAX_FOR_PAGING_IO();
NT_ASSERT(FltObjects->FileObject == Data->Iopb->TargetFileObject);
KphpFltZeroFileName(FltDestFileName);
if (!FltObjects->FileObject)
{
KphTracePrint(TRACE_LEVEL_VERBOSE, INFORMER, "FileObject is null");
return STATUS_OBJECT_NAME_NOT_FOUND;
}
return KphpFltGetDestinationFileNameInformation(Data,
FltObjects,
FltDestFileName);
}
/**
* \brief Releases the file name.
*
* \param[in] FltFileName The file name to release.
*/
_IRQL_requires_max_(APC_LEVEL)
VOID KphpFltReleaseFileName(
_In_ PKPH_FLT_FILE_NAME FltFileName
)
{
KPH_NPAGED_CODE_APC_MAX_FOR_PAGING_IO();
switch (FltFileName->Type)
{
case KphFltFileNameTypeNameInfo:
{
NT_ASSERT(FltFileName->NameInfo);
FltReleaseFileNameInformation(FltFileName->NameInfo);
break;
}
case KphFltFileNameTypeContext:
{
NT_ASSERT(FltFileName->Context);
FltReleaseContext(FltFileName->Context);
break;
}
case KphFltFileNameTypeFileName:
{
NT_ASSERT(FltFileName->FileName);
KphFree(FltFileName->FileName, KPH_TAG_FLT_FILE_NAME);
break;
}
case KphFltFileNameTypeNameCache:
{
NT_ASSERT(FltFileName->NameCache);
KphDereferenceObject(FltFileName->NameCache);
break;
}
case KphFltFileNameTypeNone:
default:
{
//
// This is a union, assert is only needed once.
//
NT_ASSERT(!FltFileName->NameInfo);
break;
}
}
}
/**
* \brief Reaps the file name cache entry for the given callback data.
*
* \details A global name cache is used in limited cases to cache the file name
* for a given file object. In order to cleanly reap information from the cache
* this must be called in the post operation callback for IRP_MJ_CLOSE.
*
* \param[in] Data The callback data for the operation.
* \param[in] FltObjects The related objects for the operation.
*/
_IRQL_requires_max_(DISPATCH_LEVEL)
VOID KphpFltReapFileNameCache(
_In_ PFLT_CALLBACK_DATA Data,
_In_ PCFLT_RELATED_OBJECTS FltObjects
)
{
KIRQL previosIrql;
PKPH_FLT_FILE_NAME_CACHE_ENTRY cacheEntry;
KPH_NPAGED_CODE_DISPATCH_MAX();
NT_ASSERT(FlagOn(Data->Flags, FLTFL_CALLBACK_DATA_POST_OPERATION));
NT_ASSERT(Data->Iopb->MajorFunction == IRP_MJ_CLOSE);
DBG_UNREFERENCED_PARAMETER(Data);
cacheEntry = NULL;
previosIrql = ExAcquireSpinLockExclusive(&KphpFileNameCacheLock);
for (PLIST_ENTRY entry = KphpFileNameCacheList.Flink;
entry != &KphpFileNameCacheList;
entry = entry->Flink)
{
cacheEntry = CONTAINING_RECORD(entry,
KPH_FLT_FILE_NAME_CACHE_ENTRY,
ListEntry);
if (cacheEntry->FileObject == FltObjects->FileObject)
{
RemoveEntryList(&cacheEntry->ListEntry);
break;
}
cacheEntry = NULL;
}
ExReleaseSpinLockExclusive(&KphpFileNameCacheLock, previosIrql);
if (cacheEntry)
{
KphDereferenceObject(cacheEntry);
}
}
/**
* \brief Allocates a cached file name object.
*
* \param[in] Size The size of the object to allocate.
*
* \return The allocated object, or NULL on failure.
*/
_Function_class_(KPH_TYPE_ALLOCATE_PROCEDURE)
_IRQL_requires_max_(APC_LEVEL)
_Return_allocatesMem_size_(Size)
PVOID KSIAPI KphpFltAllocateCachedFileName(
_In_ SIZE_T Size
)
{
KPH_NPAGED_CODE_APC_MAX_FOR_PAGING_IO();
return KphAllocateNPaged(Size, KPH_TAG_FLT_CACHED_FILE_NAME);
}
/**
* \brief Frees a cached file name object.
*
* \param[in] Object The object to free.
*/
_Function_class_(KPH_TYPE_FREE_PROCEDURE)
_IRQL_requires_max_(APC_LEVEL)
VOID KSIAPI KphpFltFreeCachedFileName(
_In_freesMem_ PVOID Object
)
{
KPH_NPAGED_CODE_APC_MAX_FOR_PAGING_IO();
KphFree(Object, KPH_TAG_FLT_CACHED_FILE_NAME);
}
KPH_PAGED_FILE();
/**
* \brief Cleans up the file name cache.
*
* \details A global name cache is used in limited cases to cache the file name
* for a given file object. This must be called after the filter stops
* filtering.
*/
_IRQL_requires_max_(PASSIVE_LEVEL)
VOID KphpFltCleanupFileNameCache(
VOID
)
{
KPH_PAGED_CODE_PASSIVE();
if (!KphpFileNameCacheInitialized)
{
return;
}
while (!IsListEmpty(&KphpFileNameCacheList))
{
PKPH_FLT_FILE_NAME_CACHE_ENTRY cacheEntry;
cacheEntry = CONTAINING_RECORD(RemoveHeadList(&KphpFileNameCacheList),
KPH_FLT_FILE_NAME_CACHE_ENTRY,
ListEntry);
KphDereferenceObject(cacheEntry);
}
}
/**
* \brief Initializes the file name cache.
*
* \details A global name cache is used in limited cases to cache the file name
* for a given file object. This must be called before the filter is begins
* filtering.
*/
_IRQL_requires_max_(PASSIVE_LEVEL)
VOID KphpFltInitializeFileNameCache(
VOID
)
{
KPH_OBJECT_TYPE_INFO typeInfo;
KPH_PAGED_CODE_PASSIVE();
InitializeListHead(&KphpFileNameCacheList);
typeInfo.Allocate = KphpFltAllocateCachedFileName;
typeInfo.Initialize = NULL;
typeInfo.Delete = NULL;
typeInfo.Free = KphpFltFreeCachedFileName;
typeInfo.Flags = 0;
KphCreateObjectType(&KphpCachedFileNameTypeName,
&typeInfo,
&KphpCachedFileNameType);
KphpFileNameCacheInitialized = TRUE;
}
================================================
FILE: KSystemInformer/informer_fileop.c
================================================
/*
* Copyright (c) 2022 Winsider Seminars & Solutions, Inc. All rights reserved.
*
* This file is part of System Informer.
*
* Authors:
*
* jxy-s 2023-2026
*
*/
#include
#include
#include
#include
#include
#include
typedef union _KPH_FLT_OPTIONS
{
USHORT Flags;
struct
{
USHORT PreEnabled : 1;
USHORT PostEnabled : 1;
USHORT EnableStackTraces : 1;
USHORT EnablePostFileNames : 1;
USHORT EnablePagingIo : 1;
USHORT EnableSyncPagingIo : 1;
USHORT EnableIoControlBuffers : 1;
USHORT EnableFsControlBuffers : 1;
USHORT EnableDirControlBuffers : 1;
USHORT EnablePreCreateReply : 1;
USHORT EnablePostCreateReply : 1;
USHORT Spare : 5;
};
} KPH_FLT_OPTIONS, *PKPH_FLT_OPTIONS;
typedef struct _KPH_FLT_COMPLETION_CONTEXT
{
PKPH_MESSAGE Message;
KPH_FLT_OPTIONS Options;
PFLT_FILE_NAME_INFORMATION FileNameInfo;
PFLT_FILE_NAME_INFORMATION DestFileNameInfo;
} KPH_FLT_COMPLETION_CONTEXT, *PKPH_FLT_COMPLETION_CONTEXT;
static ULONG64 KphpFltSequence = 0;
static BOOLEAN KphpFltOpInitialized = FALSE;
static NPAGED_LOOKASIDE_LIST KphpFltCompletionContextLookaside = { 0 };
/**
* \brief Gets the options for the filter.
*
* \param[in] Data The callback data for the operation.
*
* \return Options for the filter.
*/
_IRQL_requires_max_(APC_LEVEL)
KPH_FLT_OPTIONS KphpFltGetOptions(
_In_ PFLT_CALLBACK_DATA Data
)
{
KPH_FLT_OPTIONS options;
PKPH_PROCESS_CONTEXT process;
KPH_NPAGED_CODE_APC_MAX_FOR_PAGING_IO();
options.Flags = 0;
if (Data->Thread)
{
process = KphGetEProcessContext(PsGetThreadProcess(Data->Thread));
}
else
{
process = KphGetSystemProcessContext();
}
#define KPH_FLT_SETTING(majorFunction, name) \
case majorFunction: \
{ \
if (KphInformerEnabled(FilePre##name, process)) \
{ \
options.PreEnabled = TRUE; \
} \
if (KphInformerEnabled(FilePost##name, process)) \
{ \
options.PostEnabled = TRUE; \
} \
break; \
}
switch (Data->Iopb->MajorFunction)
{
KPH_FLT_SETTING(IRP_MJ_CREATE, Create)
KPH_FLT_SETTING(IRP_MJ_CREATE_NAMED_PIPE, CreateNamedPipe)
KPH_FLT_SETTING(IRP_MJ_CLOSE, Close)
KPH_FLT_SETTING(IRP_MJ_READ, Read)
KPH_FLT_SETTING(IRP_MJ_WRITE, Write)
KPH_FLT_SETTING(IRP_MJ_QUERY_INFORMATION, QueryInformation)
KPH_FLT_SETTING(IRP_MJ_SET_INFORMATION, SetInformation)
KPH_FLT_SETTING(IRP_MJ_QUERY_EA, QueryEa)
KPH_FLT_SETTING(IRP_MJ_SET_EA, SetEa)
KPH_FLT_SETTING(IRP_MJ_FLUSH_BUFFERS, FlushBuffers)
KPH_FLT_SETTING(IRP_MJ_QUERY_VOLUME_INFORMATION, QueryVolumeInformation)
KPH_FLT_SETTING(IRP_MJ_SET_VOLUME_INFORMATION, SetVolumeInformation)
KPH_FLT_SETTING(IRP_MJ_DIRECTORY_CONTROL, DirectoryControl)
KPH_FLT_SETTING(IRP_MJ_FILE_SYSTEM_CONTROL, FileSystemControl)
KPH_FLT_SETTING(IRP_MJ_DEVICE_CONTROL, DeviceControl)
KPH_FLT_SETTING(IRP_MJ_INTERNAL_DEVICE_CONTROL, InternalDeviceControl)
KPH_FLT_SETTING(IRP_MJ_SHUTDOWN, Shutdown)
KPH_FLT_SETTING(IRP_MJ_LOCK_CONTROL, LockControl)
KPH_FLT_SETTING(IRP_MJ_CLEANUP, Cleanup)
KPH_FLT_SETTING(IRP_MJ_CREATE_MAILSLOT, CreateMailslot)
KPH_FLT_SETTING(IRP_MJ_QUERY_SECURITY, QuerySecurity)
KPH_FLT_SETTING(IRP_MJ_SET_SECURITY, SetSecurity)
KPH_FLT_SETTING(IRP_MJ_POWER, Power)
KPH_FLT_SETTING(IRP_MJ_SYSTEM_CONTROL, SystemControl)
KPH_FLT_SETTING(IRP_MJ_DEVICE_CHANGE, DeviceChange)
KPH_FLT_SETTING(IRP_MJ_QUERY_QUOTA, QueryQuota)
KPH_FLT_SETTING(IRP_MJ_SET_QUOTA, SetQuota)
KPH_FLT_SETTING(IRP_MJ_PNP, Pnp)
KPH_FLT_SETTING(IRP_MJ_ACQUIRE_FOR_SECTION_SYNCHRONIZATION, AcquireForSectionSync)
KPH_FLT_SETTING(IRP_MJ_RELEASE_FOR_SECTION_SYNCHRONIZATION, ReleaseForSectionSync)
KPH_FLT_SETTING(IRP_MJ_ACQUIRE_FOR_MOD_WRITE, AcquireForModWrite)
KPH_FLT_SETTING(IRP_MJ_RELEASE_FOR_MOD_WRITE, ReleaseForModWrite)
KPH_FLT_SETTING(IRP_MJ_ACQUIRE_FOR_CC_FLUSH, AcquireForCcFlush)
KPH_FLT_SETTING(IRP_MJ_RELEASE_FOR_CC_FLUSH, ReleaseForCcFlush)
KPH_FLT_SETTING(IRP_MJ_QUERY_OPEN, QueryOpen)
KPH_FLT_SETTING(IRP_MJ_FAST_IO_CHECK_IF_POSSIBLE, FastIoCheckIfPossible)
KPH_FLT_SETTING(IRP_MJ_NETWORK_QUERY_OPEN, NetworkQueryOpen)
KPH_FLT_SETTING(IRP_MJ_MDL_READ, MdlRead)
KPH_FLT_SETTING(IRP_MJ_MDL_READ_COMPLETE, MdlReadComplete)
KPH_FLT_SETTING(IRP_MJ_PREPARE_MDL_WRITE, PrepareMdlWrite)
KPH_FLT_SETTING(IRP_MJ_MDL_WRITE_COMPLETE, MdlWriteComplete)
KPH_FLT_SETTING(IRP_MJ_VOLUME_MOUNT, VolumeMount)
KPH_FLT_SETTING(IRP_MJ_VOLUME_DISMOUNT, VolumeDismount)
DEFAULT_UNREACHABLE;
}
if (options.PreEnabled || options.PostEnabled)
{
KPH_INFORMER_OPTIONS opts;
opts = KphInformerOpts(process);
options.EnableStackTraces = !!opts.EnableStackTraces;
options.EnablePostFileNames = !!opts.FileEnablePostFileNames;
options.EnablePagingIo = !!opts.FileEnablePagingIo;
options.EnableSyncPagingIo = !!opts.FileEnableSyncPagingIo;
options.EnableIoControlBuffers = !!opts.FileEnableIoControlBuffers;
options.EnableFsControlBuffers = !!opts.FileEnableFsControlBuffers;
options.EnableDirControlBuffers = !!opts.FileEnableDirControlBuffers;
options.EnablePreCreateReply = !!opts.FileEnablePreCreateReply;
options.EnablePostCreateReply = !!opts.FileEnablePostCreateReply;
}
if (process)
{
KphDereferenceObject(process);
}
return options;
}
/**
* \brief Retrieves the message ID for a given operation.
*
* \param[in] MajorFunction The major function of the operation.
* \param[in] PostOperation Denotes if this is a pre or post operation message.
*
* \return The message ID for the operation.
*/
_IRQL_requires_max_(DISPATCH_LEVEL)
KPH_MESSAGE_ID KphpFltGetMessageId(
_In_ UCHAR MajorFunction,
_In_ BOOLEAN PostOperation
)
{
KPH_MESSAGE_ID messageId;
KPH_NPAGED_CODE_DISPATCH_MAX();
#define KPH_FLT_MESSAGE_ID(majorFunction, name) \
case majorFunction: \
{ \
if (PostOperation) \
{ \
messageId = KphMsgFilePost##name; \
} \
else \
{ \
messageId = KphMsgFilePre##name; \
} \
break; \
}
switch (MajorFunction)
{
KPH_FLT_MESSAGE_ID(IRP_MJ_CREATE, Create)
KPH_FLT_MESSAGE_ID(IRP_MJ_CREATE_NAMED_PIPE, CreateNamedPipe)
KPH_FLT_MESSAGE_ID(IRP_MJ_CLOSE, Close)
KPH_FLT_MESSAGE_ID(IRP_MJ_READ, Read)
KPH_FLT_MESSAGE_ID(IRP_MJ_WRITE, Write)
KPH_FLT_MESSAGE_ID(IRP_MJ_QUERY_INFORMATION, QueryInformation)
KPH_FLT_MESSAGE_ID(IRP_MJ_SET_INFORMATION, SetInformation)
KPH_FLT_MESSAGE_ID(IRP_MJ_QUERY_EA, QueryEa)
KPH_FLT_MESSAGE_ID(IRP_MJ_SET_EA, SetEa)
KPH_FLT_MESSAGE_ID(IRP_MJ_FLUSH_BUFFERS, FlushBuffers)
KPH_FLT_MESSAGE_ID(IRP_MJ_QUERY_VOLUME_INFORMATION, QueryVolumeInformation)
KPH_FLT_MESSAGE_ID(IRP_MJ_SET_VOLUME_INFORMATION, SetVolumeInformation)
KPH_FLT_MESSAGE_ID(IRP_MJ_DIRECTORY_CONTROL, DirectoryControl)
KPH_FLT_MESSAGE_ID(IRP_MJ_FILE_SYSTEM_CONTROL, FileSystemControl)
KPH_FLT_MESSAGE_ID(IRP_MJ_DEVICE_CONTROL, DeviceControl)
KPH_FLT_MESSAGE_ID(IRP_MJ_INTERNAL_DEVICE_CONTROL, InternalDeviceControl)
KPH_FLT_MESSAGE_ID(IRP_MJ_SHUTDOWN, Shutdown)
KPH_FLT_MESSAGE_ID(IRP_MJ_LOCK_CONTROL, LockControl)
KPH_FLT_MESSAGE_ID(IRP_MJ_CLEANUP, Cleanup)
KPH_FLT_MESSAGE_ID(IRP_MJ_CREATE_MAILSLOT, CreateMailslot)
KPH_FLT_MESSAGE_ID(IRP_MJ_QUERY_SECURITY, QuerySecurity)
KPH_FLT_MESSAGE_ID(IRP_MJ_SET_SECURITY, SetSecurity)
KPH_FLT_MESSAGE_ID(IRP_MJ_POWER, Power)
KPH_FLT_MESSAGE_ID(IRP_MJ_SYSTEM_CONTROL, SystemControl)
KPH_FLT_MESSAGE_ID(IRP_MJ_DEVICE_CHANGE, DeviceChange)
KPH_FLT_MESSAGE_ID(IRP_MJ_QUERY_QUOTA, QueryQuota)
KPH_FLT_MESSAGE_ID(IRP_MJ_SET_QUOTA, SetQuota)
KPH_FLT_MESSAGE_ID(IRP_MJ_PNP, Pnp)
KPH_FLT_MESSAGE_ID(IRP_MJ_ACQUIRE_FOR_SECTION_SYNCHRONIZATION, AcquireForSectionSync)
KPH_FLT_MESSAGE_ID(IRP_MJ_RELEASE_FOR_SECTION_SYNCHRONIZATION, ReleaseForSectionSync)
KPH_FLT_MESSAGE_ID(IRP_MJ_ACQUIRE_FOR_MOD_WRITE, AcquireForModWrite)
KPH_FLT_MESSAGE_ID(IRP_MJ_RELEASE_FOR_MOD_WRITE, ReleaseForModWrite)
KPH_FLT_MESSAGE_ID(IRP_MJ_ACQUIRE_FOR_CC_FLUSH, AcquireForCcFlush)
KPH_FLT_MESSAGE_ID(IRP_MJ_RELEASE_FOR_CC_FLUSH, ReleaseForCcFlush)
KPH_FLT_MESSAGE_ID(IRP_MJ_QUERY_OPEN, QueryOpen)
KPH_FLT_MESSAGE_ID(IRP_MJ_FAST_IO_CHECK_IF_POSSIBLE, FastIoCheckIfPossible)
KPH_FLT_MESSAGE_ID(IRP_MJ_NETWORK_QUERY_OPEN, NetworkQueryOpen)
KPH_FLT_MESSAGE_ID(IRP_MJ_MDL_READ, MdlRead)
KPH_FLT_MESSAGE_ID(IRP_MJ_MDL_READ_COMPLETE, MdlReadComplete)
KPH_FLT_MESSAGE_ID(IRP_MJ_PREPARE_MDL_WRITE, PrepareMdlWrite)
KPH_FLT_MESSAGE_ID(IRP_MJ_MDL_WRITE_COMPLETE, MdlWriteComplete)
KPH_FLT_MESSAGE_ID(IRP_MJ_VOLUME_MOUNT, VolumeMount)
KPH_FLT_MESSAGE_ID(IRP_MJ_VOLUME_DISMOUNT, VolumeDismount)
DEFAULT_UNREACHABLE;
}
return messageId;
}
/**
* \brief Initializes a message for a file operation.
*
* \param[in,out] Message The message to initialize.
* \param[in] FltObjects The filter objects for the operation.
* \param[in] MajorFunction The major function of the operation.
* \param[in] PostOperation Denotes if this is a pre or post operation message.
*/
_IRQL_requires_max_(APC_LEVEL)
VOID KphpFltInitMessage(
_Inout_ PKPH_MESSAGE Message,
_In_ PCFLT_RELATED_OBJECTS FltObjects,
_In_ UCHAR MajorFunction,
_In_ BOOLEAN PostOperation
)
{
ULONG_PTR stackLowLimit;
ULONG_PTR stackHighLimit;
POPLOCK_KEY_CONTEXT oplockKeyContext;
KPH_NPAGED_CODE_APC_MAX_FOR_PAGING_IO();
KphMsgInit(Message, KphpFltGetMessageId(MajorFunction, PostOperation));
Message->Kernel.File.TransactionContext = FltObjects->TransactionContext;
Message->Kernel.File.Volume = FltObjects->Volume;
Message->Kernel.File.FileObject = FltObjects->FileObject;
Message->Kernel.File.Transaction = FltObjects->Transaction;
if (!FltObjects->FileObject)
{
return;
}
if (FltObjects->FileObject->SectionObjectPointer)
{
PSECTION_OBJECT_POINTERS sectionPointers;
sectionPointers = FltObjects->FileObject->SectionObjectPointer;
Message->Kernel.File.DataSectionObject = sectionPointers->DataSectionObject;
Message->Kernel.File.ImageSectionObject = sectionPointers->ImageSectionObject;
}
if (FsRtlIsPagingFile(FltObjects->FileObject))
{
Message->Kernel.File.IsPagingFile = TRUE;
}
if (FsRtlIsSystemPagingFile(FltObjects->FileObject))
{
Message->Kernel.File.IsSystemPagingFile = TRUE;
}
if (IoIsFileOriginRemote(FltObjects->FileObject))
{
Message->Kernel.File.OriginRemote = TRUE;
}
if (IoIsFileObjectIgnoringSharing(FltObjects->FileObject))
{
Message->Kernel.File.IgnoringSharing = TRUE;
}
IoGetStackLimits(&stackLowLimit, &stackHighLimit);
if (((ULONG_PTR)FltObjects->FileObject >= stackLowLimit) &&
((ULONG_PTR)FltObjects->FileObject <= stackHighLimit))
{
Message->Kernel.File.InStackFileObject = TRUE;
}
Message->Kernel.File.LockOperation = (FltObjects->FileObject->LockOperation != FALSE);
Message->Kernel.File.ReadAccess = (FltObjects->FileObject->ReadAccess != FALSE);
Message->Kernel.File.WriteAccess = (FltObjects->FileObject->WriteAccess != FALSE);
Message->Kernel.File.DeleteAccess = (FltObjects->FileObject->DeleteAccess != FALSE);
Message->Kernel.File.SharedRead = (FltObjects->FileObject->SharedRead != FALSE);
Message->Kernel.File.SharedWrite = (FltObjects->FileObject->SharedWrite != FALSE);
Message->Kernel.File.SharedDelete = (FltObjects->FileObject->SharedDelete != FALSE);
oplockKeyContext = IoGetOplockKeyContextEx(FltObjects->FileObject);
if (oplockKeyContext)
{
RtlCopyMemory(&Message->Kernel.File.OplockKeyContext,
oplockKeyContext,
sizeof(OPLOCK_KEY_CONTEXT));
}
if (KphDynIoCheckFileObjectOpenedAsCopySource &&
KphDynIoCheckFileObjectOpenedAsCopySource(FltObjects->FileObject))
{
Message->Kernel.File.OpenedAsCopySource = TRUE;
}
if (KphDynIoCheckFileObjectOpenedAsCopyDestination &&
KphDynIoCheckFileObjectOpenedAsCopyDestination(FltObjects->FileObject))
{
Message->Kernel.File.OpenedAsCopyDestination = TRUE;
}
}
/**
* \brief Copies a file name into a message.
*
* \param[in,out] Message The message to copy the file name into.
* \param[in] FieldId The field to copy the file name into.
* \param[in] FltFileName The file name information to copy.
*/
_IRQL_requires_max_(APC_LEVEL)
VOID KphpFltCopyFileName(
_Inout_ PKPH_MESSAGE Message,
_In_ KPH_MESSAGE_FIELD_ID FieldId,
_In_ PKPH_FLT_FILE_NAME FltFileName
)
{
NTSTATUS status;
PUNICODE_STRING string;
KPH_NPAGED_CODE_APC_MAX_FOR_PAGING_IO();
switch (FltFileName->Type)
{
case KphFltFileNameTypeNameInfo:
{
string = &FltFileName->NameInfo->Name;
break;
}
case KphFltFileNameTypeContext:
{
//
// N.B. PFLT_CONTEXT stores a PUNICODE_STRING, this is a union so
// fall through and use the FileName field.
//
NT_ASSERT(FltFileName->Context == FltFileName->FileName);
__fallthrough;
}
case KphFltFileNameTypeFileName:
{
string = FltFileName->FileName;
break;
}
case KphFltFileNameTypeNameCache:
{
string = &FltFileName->NameCache->FileName;
break;
}
default:
{
return;
}
}
status = KphMsgDynAddUnicodeString(Message, FieldId, string);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
INFORMER,
"KphMsgDynAddUnicodeString failed: %!STATUS!",
status);
}
}
/**
* \brief Copies a file system buffer into a message.
*
* \details This function handles various buffers from the file system filter
* to capture and copy information into the message. This function is overall
* best effort. It can truncate buffers to fit into the message.
*
* \param[in,out] Message The message to copy the buffer into.
* \param[in] Data The callback data for the operation.
* \param[in] FieldId The field to copy the buffer into. Optional if DestBuffer
* is non-NULL. Certain Unicode string fields are special cased to be copied as
* a Unicode string instead of a sized buffer.
* \param[in] SystemBuffer Denotes if the buffer is a system buffer. If TRUE
* this function assumes the buffer doesn't need to be probed and locked. In
* some cases the filter parameters are known to be a system buffer, such as
* METHOD_BUFFERED.
* \param[out] DestBuffer Optional destination buffer to copy into, if NULL the
* data will be copied into the dynamic message buffer for FieldId.
* \param[in] DestLength The length of the destination buffer.
* \param[in] Mdl Optional memory descriptor list which the buffer will be
* copied from instead, if non-NULL the MDL is used instead of any provided
* input buffer.
* \param[in] Buffer Optional buffer to copy from, if NULL and the Mdl is NULL
* this function will do nothing.
* \param[in] Length The length to copy.
* \param[in] Truncate If TRUE the buffer will be truncated to fit into the
* message, otherwise the buffer copy may fail.
*/
_IRQL_requires_max_(APC_LEVEL)
VOID KphpFltCopyBuffer(
_Inout_ PKPH_MESSAGE Message,
_In_ PFLT_CALLBACK_DATA Data,
_In_ KPH_MESSAGE_FIELD_ID FieldId,
_In_ BOOLEAN SystemBuffer,
_Out_writes_bytes_to_opt_(DestLength, Length) PVOID DestBuffer,
_In_ ULONG DestLength,
_In_opt_ PMDL Mdl,
_In_opt_ PVOID Buffer,
_In_ ULONG Length,
_In_ BOOLEAN Truncate
)
{
NTSTATUS status;
PVOID buffer;
PMDL mdl;
BOOLEAN unlockPages;
KPH_NPAGED_CODE_APC_MAX_FOR_PAGING_IO();
mdl = NULL;
unlockPages = FALSE;
if (DestBuffer)
{
NT_ASSERT(FieldId == InvalidKphMsgField);
NT_ASSERT(DestLength >= Length);
RtlZeroMemory(DestBuffer, DestLength);
}
else if (Truncate)
{
USHORT remaining;
NT_ASSERT(FieldId != InvalidKphMsgField);
remaining = KphMsgDynRemaining(Message);
if (Length > remaining)
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
INFORMER,
"Buffer too large for message, truncating %lu -> %lu",
Length,
remaining);
Length = remaining;
}
}
if (!Length)
{
goto Exit;
}
if (Mdl)
{
buffer = KphGetSystemAddressForMdl(Mdl, NormalPagePriority);
goto CopyBuffer;
}
if (SystemBuffer ||
FLT_IS_FASTIO_OPERATION(Data) ||
FLT_IS_SYSTEM_BUFFER(Data))
{
buffer = Buffer;
goto CopyBuffer;
}
if (!Buffer)
{
goto Exit;
}
if (!Data->Thread)
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
INFORMER,
"Missing thread for buffer capture");
goto Exit;
}
mdl = IoAllocateMdl(Buffer, Length, FALSE, FALSE, NULL);
if (!mdl)
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
INFORMER,
"Failed to allocate MDL");
goto Exit;
}
__try
{
MmProbeAndLockProcessPages(mdl,
PsGetThreadProcess(Data->Thread),
KernelMode,
IoReadAccess);
unlockPages = TRUE;
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
INFORMER,
"MmProbeAndLockProcessPages failed: %!STATUS!",
GetExceptionCode());
goto Exit;
}
buffer = KphGetSystemAddressForMdl(mdl, NormalPagePriority);
CopyBuffer:
if (!buffer)
{
goto Exit;
}
__try
{
if (DestBuffer)
{
NT_ASSERT(FieldId == InvalidKphMsgField);
NT_ASSERT(Length <= DestLength);
RtlCopyMemory(DestBuffer, buffer, Length);
}
else if (FieldId == KphMsgFieldDestinationFileName)
{
UNICODE_STRING fileName;
NT_ASSERT(Length <= USHORT_MAX);
fileName.Buffer = Buffer;
fileName.Length = (USHORT)Length;
fileName.MaximumLength = fileName.Length;
status = KphMsgDynAddUnicodeString(Message, FieldId, &fileName);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
INFORMER,
"KphMsgDynAddSizedBuffer failed: %!STATUS!",
status);
}
}
else
{
KPHM_SIZED_BUFFER sizedBuffer;
NT_ASSERT(FieldId != InvalidKphMsgField);
NT_ASSERT(Length <= USHORT_MAX);
sizedBuffer.Buffer = buffer;
sizedBuffer.Size = (USHORT)Length;
status = KphMsgDynAddSizedBuffer(Message, FieldId, &sizedBuffer);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
INFORMER,
"KphMsgDynAddSizedBuffer failed: %!STATUS!",
status);
}
}
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
INFORMER,
"Exception copying buffer: %!STATUS!",
GetExceptionCode());
if (FieldId != InvalidKphMsgField)
{
//
// N.B. The KphMsgDynAdd* routines will first reserve a table
// entry then copy data into the buffer. If we get here it means
// the control flow was arrested after the reservation. Since we
// can't guarantee it was all copied successfully clear the last
// entry that was reserved.
//
KphMsgDynClearLast(Message);
}
}
Exit:
if (mdl)
{
if (unlockPages)
{
MmUnlockPages(mdl);
}
IoFreeMdl(mdl);
}
}
/**
* \brief Copies file system control buffers into the message.
*
* \param[in,out] Message The message to copy the buffers into.
* \param[in] Data The callback data for the operation.
*/
_IRQL_requires_max_(APC_LEVEL)
VOID KphpFltCopyFsControl(
_Inout_ PKPH_MESSAGE Message,
_In_ PFLT_CALLBACK_DATA Data
)
{
UCHAR method;
PMDL mdl;
PVOID buffer;
ULONG length;
KPH_NPAGED_CODE_APC_MAX_FOR_PAGING_IO();
NT_ASSERT(Data->Iopb->MajorFunction == IRP_MJ_FILE_SYSTEM_CONTROL);
method = METHOD_FROM_CTL_CODE(Data->Iopb->Parameters.FileSystemControl.Common.FsControlCode);
switch (method)
{
case METHOD_NEITHER:
{
//
// Capture the input and output buffer always since it is possible
// to use them interchangeably.
//
buffer = Data->Iopb->Parameters.FileSystemControl.Neither.InputBuffer;
if (FlagOn(Data->Flags, FLTFL_CALLBACK_DATA_POST_OPERATION))
{
length = min((ULONG)Data->IoStatus.Information,
Data->Iopb->Parameters.FileSystemControl.Neither.InputBufferLength);
}
else
{
length = Data->Iopb->Parameters.FileSystemControl.Neither.InputBufferLength;
}
KphpFltCopyBuffer(Message,
Data,
KphMsgFieldInput,
FALSE,
NULL,
0,
NULL,
buffer,
length,
TRUE);
mdl = Data->Iopb->Parameters.FileSystemControl.Neither.OutputMdlAddress;
buffer = Data->Iopb->Parameters.FileSystemControl.Neither.OutputBuffer;
if (FlagOn(Data->Flags, FLTFL_CALLBACK_DATA_POST_OPERATION))
{
length = min((ULONG)Data->IoStatus.Information,
Data->Iopb->Parameters.FileSystemControl.Neither.OutputBufferLength);
}
else
{
length = Data->Iopb->Parameters.FileSystemControl.Neither.OutputBufferLength;
}
KphpFltCopyBuffer(Message,
Data,
KphMsgFieldOutput,
FALSE,
NULL,
0,
mdl,
buffer,
length,
TRUE);
break;
}
case METHOD_BUFFERED:
{
buffer = Data->Iopb->Parameters.FileSystemControl.Buffered.SystemBuffer;
if (FlagOn(Data->Flags, FLTFL_CALLBACK_DATA_POST_OPERATION))
{
length = min((ULONG)Data->IoStatus.Information,
Data->Iopb->Parameters.FileSystemControl.Buffered.OutputBufferLength);
KphpFltCopyBuffer(Message,
Data,
KphMsgFieldOutput,
TRUE,
NULL,
0,
NULL,
buffer,
length,
TRUE);
}
else
{
length = Data->Iopb->Parameters.FileSystemControl.Buffered.InputBufferLength;
KphpFltCopyBuffer(Message,
Data,
KphMsgFieldInput,
TRUE,
NULL,
0,
NULL,
buffer,
length,
TRUE);
}
break;
}
case METHOD_IN_DIRECT:
case METHOD_OUT_DIRECT:
{
if (FlagOn(Data->Flags, FLTFL_CALLBACK_DATA_POST_OPERATION))
{
mdl = Data->Iopb->Parameters.FileSystemControl.Direct.OutputMdlAddress;
buffer = Data->Iopb->Parameters.FileSystemControl.Direct.OutputBuffer;
length = min((ULONG)Data->IoStatus.Information,
Data->Iopb->Parameters.FileSystemControl.Direct.OutputBufferLength);
KphpFltCopyBuffer(Message,
Data,
KphMsgFieldOutput,
FALSE,
NULL,
0,
mdl,
buffer,
length,
TRUE);
}
else
{
buffer = Data->Iopb->Parameters.FileSystemControl.Direct.InputSystemBuffer;
length = Data->Iopb->Parameters.FileSystemControl.Direct.InputBufferLength;
KphpFltCopyBuffer(Message,
Data,
KphMsgFieldInput,
TRUE,
NULL,
0,
NULL,
buffer,
length,
TRUE);
}
break;
}
default:
{
return;
}
}
}
/**
* \brief Copies device control buffers into the message.
*
* \param[in,out] Message The message to copy the buffers into.
* \param[in] Data The callback data for the operation.
*/
_IRQL_requires_max_(APC_LEVEL)
VOID KphpFltCopyIoControl(
_Inout_ PKPH_MESSAGE Message,
_In_ PFLT_CALLBACK_DATA Data
)
{
UCHAR method;
PMDL mdl;
PVOID buffer;
ULONG length;
KPH_NPAGED_CODE_APC_MAX_FOR_PAGING_IO();
NT_ASSERT((Data->Iopb->MajorFunction == IRP_MJ_DEVICE_CONTROL) ||
(Data->Iopb->MajorFunction == IRP_MJ_INTERNAL_DEVICE_CONTROL));
if (FLT_IS_FASTIO_OPERATION(Data))
{
//
// Capture the input and output buffer always since it is possible
// to use them interchangeably.
//
buffer = Data->Iopb->Parameters.DeviceIoControl.FastIo.InputBuffer;
if (FlagOn(Data->Flags, FLTFL_CALLBACK_DATA_POST_OPERATION))
{
length = min((ULONG)Data->IoStatus.Information,
length = Data->Iopb->Parameters.DeviceIoControl.FastIo.InputBufferLength);
}
else
{
length = Data->Iopb->Parameters.DeviceIoControl.FastIo.InputBufferLength;
}
KphpFltCopyBuffer(Message,
Data,
KphMsgFieldInput,
FALSE,
NULL,
0,
NULL,
buffer,
length,
TRUE);
buffer = Data->Iopb->Parameters.DeviceIoControl.FastIo.OutputBuffer;
if (FlagOn(Data->Flags, FLTFL_CALLBACK_DATA_POST_OPERATION))
{
length = min((ULONG)Data->IoStatus.Information,
Data->Iopb->Parameters.DeviceIoControl.FastIo.OutputBufferLength);
}
else
{
length = Data->Iopb->Parameters.DeviceIoControl.FastIo.OutputBufferLength;
}
KphpFltCopyBuffer(Message,
Data,
KphMsgFieldOutput,
FALSE,
NULL,
0,
NULL,
buffer,
length,
TRUE);
return;
}
method = METHOD_FROM_CTL_CODE(Data->Iopb->Parameters.DeviceIoControl.Common.IoControlCode);
switch (method)
{
case METHOD_NEITHER:
{
//
// Capture the input and output buffer always since it is possible
// to use them interchangeably.
//
buffer = Data->Iopb->Parameters.DeviceIoControl.Neither.InputBuffer;
if (FlagOn(Data->Flags, FLTFL_CALLBACK_DATA_POST_OPERATION))
{
length = min((ULONG)Data->IoStatus.Information,
Data->Iopb->Parameters.DeviceIoControl.Neither.InputBufferLength);
}
else
{
length = Data->Iopb->Parameters.DeviceIoControl.Neither.InputBufferLength;
}
KphpFltCopyBuffer(Message,
Data,
KphMsgFieldInput,
FALSE,
NULL,
0,
NULL,
buffer,
length,
TRUE);
mdl = Data->Iopb->Parameters.DeviceIoControl.Neither.OutputMdlAddress;
buffer = Data->Iopb->Parameters.DeviceIoControl.Neither.OutputBuffer;
if (FlagOn(Data->Flags, FLTFL_CALLBACK_DATA_POST_OPERATION))
{
length = min((ULONG)Data->IoStatus.Information,
Data->Iopb->Parameters.DeviceIoControl.Neither.OutputBufferLength);
}
else
{
length = Data->Iopb->Parameters.DeviceIoControl.Neither.OutputBufferLength;
}
KphpFltCopyBuffer(Message,
Data,
KphMsgFieldOutput,
FALSE,
NULL,
0,
mdl,
buffer,
length,
TRUE);
break;
}
case METHOD_BUFFERED:
{
buffer = Data->Iopb->Parameters.DeviceIoControl.Buffered.SystemBuffer;
if (FlagOn(Data->Flags, FLTFL_CALLBACK_DATA_POST_OPERATION))
{
length = min((ULONG)Data->IoStatus.Information,
Data->Iopb->Parameters.DeviceIoControl.Buffered.OutputBufferLength);
KphpFltCopyBuffer(Message,
Data,
KphMsgFieldOutput,
TRUE,
NULL,
0,
NULL,
buffer,
length,
TRUE);
}
else
{
length = Data->Iopb->Parameters.DeviceIoControl.Buffered.InputBufferLength;
KphpFltCopyBuffer(Message,
Data,
KphMsgFieldInput,
TRUE,
NULL,
0,
NULL,
buffer,
length,
TRUE);
}
break;
}
case METHOD_IN_DIRECT:
case METHOD_OUT_DIRECT:
{
if (FlagOn(Data->Flags, FLTFL_CALLBACK_DATA_POST_OPERATION))
{
mdl = Data->Iopb->Parameters.DeviceIoControl.Direct.OutputMdlAddress;
buffer = Data->Iopb->Parameters.DeviceIoControl.Direct.OutputBuffer;
length = min((ULONG)Data->IoStatus.Information,
Data->Iopb->Parameters.DeviceIoControl.Direct.OutputBufferLength);
KphpFltCopyBuffer(Message,
Data,
KphMsgFieldOutput,
FALSE,
NULL,
0,
mdl,
buffer,
length,
TRUE);
}
else
{
buffer = Data->Iopb->Parameters.DeviceIoControl.Direct.InputSystemBuffer;
length = Data->Iopb->Parameters.DeviceIoControl.Direct.InputBufferLength;
KphpFltCopyBuffer(Message,
Data,
KphMsgFieldInput,
TRUE,
NULL,
0,
NULL,
buffer,
length,
TRUE);
}
break;
}
default:
{
return;
}
}
}
/**
* \brief Fills a message with common information.
*
* \param[in,out] Message The message to fill.
* \param[in] Data The callback data for the operation.
*/
_IRQL_requires_max_(DISPATCH_LEVEL)
VOID KphpFltFillCommonMessage(
_Inout_ PKPH_MESSAGE Message,
_In_ PFLT_CALLBACK_DATA Data
)
{
COPY_INFORMATION copyInfo;
KPH_NPAGED_CODE_DISPATCH_MAX();
if (Data->Thread)
{
PEPROCESS process;
BOOLEAN cacheOnly;
process = PsGetThreadProcess(Data->Thread);
Message->Kernel.File.ClientId.UniqueProcess = PsGetProcessId(process);
Message->Kernel.File.ClientId.UniqueThread = PsGetThreadId(Data->Thread);
Message->Kernel.File.ProcessStartKey = KphGetProcessStartKey(process);
cacheOnly = (FlagOn(Data->Iopb->IrpFlags, IRP_PAGING_IO) ||
FlagOn(Data->Iopb->IrpFlags, IRP_SYNCHRONOUS_PAGING_IO));
Message->Kernel.File.ThreadSubProcessTag = KphGetThreadSubProcessTagEx(Data->Thread, cacheOnly);
}
if (KphDynFltGetCopyInformationFromCallbackData &&
NT_SUCCESS(KphDynFltGetCopyInformationFromCallbackData(Data, ©Info)))
{
Message->Kernel.File.CopyInformation.SourceFileObject = copyInfo.SourceFileObject;
Message->Kernel.File.CopyInformation.SourceFileOffset = copyInfo.SourceFileOffset;
}
Message->Kernel.File.RequestorMode = (Data->RequestorMode != KernelMode);
Message->Kernel.File.MajorFunction = Data->Iopb->MajorFunction;
Message->Kernel.File.MinorFunction = Data->Iopb->MinorFunction;
Message->Kernel.File.Irql = KeGetCurrentIrql();
Message->Kernel.File.OperationFlags = Data->Iopb->OperationFlags;
Message->Kernel.File.FltFlags = Data->Flags;
Message->Kernel.File.IrpFlags = Data->Iopb->IrpFlags;
Message->Kernel.File.Parameters.Others.Argument1 = Data->Iopb->Parameters.Others.Argument1;
Message->Kernel.File.Parameters.Others.Argument2 = Data->Iopb->Parameters.Others.Argument2;
Message->Kernel.File.Parameters.Others.Argument3 = Data->Iopb->Parameters.Others.Argument3;
Message->Kernel.File.Parameters.Others.Argument4 = Data->Iopb->Parameters.Others.Argument4;
Message->Kernel.File.Parameters.Others.Argument5 = Data->Iopb->Parameters.Others.Argument5;
Message->Kernel.File.Parameters.Others.Argument6 = Data->Iopb->Parameters.Others.Argument6;
if (!Data->Iopb->TargetFileObject)
{
return;
}
Message->Kernel.File.DeletePending = (Data->Iopb->TargetFileObject->DeletePending != FALSE);
Message->Kernel.File.Busy = (Data->Iopb->TargetFileObject->Busy != 0);
Message->Kernel.File.Flags = Data->Iopb->TargetFileObject->Flags;
Message->Kernel.File.Waiters = Data->Iopb->TargetFileObject->Waiters;
Message->Kernel.File.CurrentByteOffset = Data->Iopb->TargetFileObject->CurrentByteOffset;
}
/**
* \brief Fills a message with pre operation information.
*
* \param[in,out] Message The message to fill.
* \param[in] Data The callback data for the operation.
* \param[in] Options The options for the filter.
*/
_IRQL_requires_max_(APC_LEVEL)
VOID KphpFltFillPreOpMessage(
_Inout_ PKPH_MESSAGE Message,
_In_ PFLT_CALLBACK_DATA Data,
_In_ PKPH_FLT_OPTIONS Options
)
{
PMDL mdl;
PVOID buffer;
ULONG length;
KPH_MESSAGE_FIELD_ID fieldId;
PVOID destBuffer;
ULONG destLength;
BOOLEAN truncate;
KPH_NPAGED_CODE_APC_MAX_FOR_PAGING_IO();
KphpFltFillCommonMessage(Message, Data);
mdl = NULL;
fieldId = InvalidKphMsgField;
destBuffer = NULL;
destLength = 0;
truncate = TRUE;
switch (Data->Iopb->MajorFunction)
{
case IRP_MJ_CREATE:
{
NT_ASSERT(KeGetCurrentIrql() == PASSIVE_LEVEL);
buffer = Data->Iopb->Parameters.Create.EaBuffer;
length = Data->Iopb->Parameters.Create.EaLength;
fieldId = KphMsgFieldEaBuffer;
break;
}
case IRP_MJ_CREATE_NAMED_PIPE:
{
buffer = Data->Iopb->Parameters.CreatePipe.Parameters;
length = sizeof(NAMED_PIPE_CREATE_PARAMETERS);
destBuffer = &Message->Kernel.File.Pre.CreateNamedPipe.Parameters;
destLength = sizeof(NAMED_PIPE_CREATE_PARAMETERS);
break;
}
case IRP_MJ_SET_INFORMATION:
{
buffer = Data->Iopb->Parameters.SetFileInformation.InfoBuffer;
length = Data->Iopb->Parameters.SetFileInformation.Length;
fieldId = KphMsgFieldInformationBuffer;
break;
}
case IRP_MJ_QUERY_EA:
{
//
// Pre operation captures the input query list. Post operation will
// capture any returned information.
//
buffer = Data->Iopb->Parameters.QueryEa.EaList;
length = Data->Iopb->Parameters.QueryEa.EaListLength;
fieldId = KphMsgFieldInformationBuffer; // FILE_GET_EA_INFORMATION
break;
}
case IRP_MJ_SET_EA:
{
mdl = Data->Iopb->Parameters.SetEa.MdlAddress;
buffer = Data->Iopb->Parameters.SetEa.EaBuffer;
length = Data->Iopb->Parameters.SetEa.Length;
fieldId = KphMsgFieldInformationBuffer; // FILE_FULL_EA_INFORMATION
break;
}
case IRP_MJ_SET_VOLUME_INFORMATION:
{
buffer = Data->Iopb->Parameters.SetVolumeInformation.VolumeBuffer;
length = Data->Iopb->Parameters.SetVolumeInformation.Length;
fieldId = KphMsgFieldInformationBuffer;
break;
}
case IRP_MJ_DIRECTORY_CONTROL:
{
PUNICODE_STRING fileName;
if (Data->Iopb->MinorFunction != IRP_MN_QUERY_DIRECTORY)
{
return;
}
fileName = Data->Iopb->Parameters.DirectoryControl.QueryDirectory.FileName;
if (!fileName)
{
return;
}
__try
{
buffer = fileName->Buffer;
length = fileName->Length;
fieldId = KphMsgFieldDestinationFileName;
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
INFORMER,
"Exception capturing file name: %!STATUS!",
GetExceptionCode());
return;
}
truncate = FALSE;
break;
}
case IRP_MJ_FILE_SYSTEM_CONTROL:
{
if (Options->EnableFsControlBuffers)
{
KphpFltCopyFsControl(Message, Data);
}
return;
}
case IRP_MJ_DEVICE_CONTROL:
case IRP_MJ_INTERNAL_DEVICE_CONTROL:
{
if (Options->EnableIoControlBuffers)
{
KphpFltCopyIoControl(Message, Data);
}
return;
}
case IRP_MJ_LOCK_CONTROL:
{
buffer = Data->Iopb->Parameters.LockControl.Length;
length = sizeof(LARGE_INTEGER);
destBuffer = &Message->Kernel.File.Pre.LockControl.Length;
destLength = sizeof(LARGE_INTEGER);
break;
}
case IRP_MJ_CREATE_MAILSLOT:
{
buffer = Data->Iopb->Parameters.CreateMailslot.Parameters;
length = sizeof(MAILSLOT_CREATE_PARAMETERS);
destBuffer = &Message->Kernel.File.Pre.CreateMailslot.Parameters;
destLength = sizeof(MAILSLOT_CREATE_PARAMETERS);
break;
}
case IRP_MJ_ACQUIRE_FOR_MOD_WRITE:
{
buffer = Data->Iopb->Parameters.AcquireForModifiedPageWriter.EndingOffset;
length = sizeof(LARGE_INTEGER);
destBuffer = &Message->Kernel.File.Pre.AcquireForModWrite.EndingOffset;
destLength = sizeof(LARGE_INTEGER);
break;
}
case IRP_MJ_QUERY_OPEN:
{
buffer = Data->Iopb->Parameters.QueryOpen.Length;
length = sizeof(ULONG);
destBuffer = &Message->Kernel.File.Pre.QueryOpen.Length;
destLength = sizeof(ULONG);
break;
}
default:
{
return;
}
}
KphpFltCopyBuffer(Message,
Data,
fieldId,
FALSE,
destBuffer,
destLength,
mdl,
buffer,
length,
truncate);
}
/**
* \brief Fills a message with post operation information.
*
* \param[in,out] Message The message to fill.
* \param[in] Data The callback data for the operation.
* \param[in] Options The options for the filter.
*/
_IRQL_requires_max_(DISPATCH_LEVEL)
VOID KphpFltFillPostOpMessage(
_Inout_ PKPH_MESSAGE Message,
_In_ PFLT_CALLBACK_DATA Data,
_In_ PKPH_FLT_OPTIONS Options
)
{
PMDL mdl;
PVOID buffer;
ULONG length;
KPH_MESSAGE_FIELD_ID fieldId;
KPH_NPAGED_CODE_DISPATCH_MAX();
KphpFltFillCommonMessage(Message, Data);
Message->Kernel.File.Post.IoStatus = Data->IoStatus;
if (KeGetCurrentIrql() > APC_LEVEL)
{
return;
}
mdl = NULL;
fieldId = InvalidKphMsgField;
switch (Data->Iopb->MajorFunction)
{
case IRP_MJ_QUERY_INFORMATION:
{
buffer = Data->Iopb->Parameters.QueryFileInformation.InfoBuffer;
length = min((ULONG)Data->IoStatus.Information,
Data->Iopb->Parameters.QueryFileInformation.Length);
fieldId = KphMsgFieldInformationBuffer;
break;
}
case IRP_MJ_QUERY_EA:
{
//
// Pre operation captured the input EA list.
//
mdl = Data->Iopb->Parameters.QueryEa.MdlAddress;
buffer = Data->Iopb->Parameters.QueryEa.EaBuffer;
length = min((ULONG)Data->IoStatus.Information,
Data->Iopb->Parameters.QueryEa.Length);
fieldId = KphMsgFieldInformationBuffer; // FILE_FULL_EA_INFORMATION
break;
}
case IRP_MJ_QUERY_VOLUME_INFORMATION:
{
buffer = Data->Iopb->Parameters.QueryVolumeInformation.VolumeBuffer;
length = min((ULONG)Data->IoStatus.Information,
Data->Iopb->Parameters.QueryVolumeInformation.Length);
fieldId = KphMsgFieldInformationBuffer;
break;
}
case IRP_MJ_DIRECTORY_CONTROL:
{
if (!Options->EnableDirControlBuffers)
{
return;
}
switch (Data->Iopb->MinorFunction)
{
case IRP_MN_QUERY_DIRECTORY:
{
//
// Pre operation captured the input file name.
//
mdl = Data->Iopb->Parameters.DirectoryControl.QueryDirectory.MdlAddress;
buffer = Data->Iopb->Parameters.DirectoryControl.QueryDirectory.DirectoryBuffer;
length = min((ULONG)Data->IoStatus.Information,
Data->Iopb->Parameters.DirectoryControl.QueryDirectory.Length);
fieldId = KphMsgFieldInformationBuffer;
break;
}
case IRP_MN_NOTIFY_CHANGE_DIRECTORY:
{
buffer = Data->Iopb->Parameters.DirectoryControl.NotifyDirectory.DirectoryBuffer;
length = min((ULONG)Data->IoStatus.Information,
Data->Iopb->Parameters.DirectoryControl.NotifyDirectory.Length);
fieldId = KphMsgFieldInformationBuffer;
break;
}
case IRP_MN_NOTIFY_CHANGE_DIRECTORY_EX:
{
mdl = Data->Iopb->Parameters.DirectoryControl.NotifyDirectoryEx.MdlAddress;
buffer = Data->Iopb->Parameters.DirectoryControl.NotifyDirectoryEx.DirectoryBuffer;
length = min((ULONG)Data->IoStatus.Information,
Data->Iopb->Parameters.DirectoryControl.NotifyDirectoryEx.Length);
fieldId = KphMsgFieldInformationBuffer;
break;
}
default:
{
return;
}
}
break;
}
case IRP_MJ_FILE_SYSTEM_CONTROL:
{
if (Options->EnableFsControlBuffers)
{
KphpFltCopyFsControl(Message, Data);
}
return;
}
case IRP_MJ_DEVICE_CONTROL:
case IRP_MJ_INTERNAL_DEVICE_CONTROL:
{
if (Options->EnableIoControlBuffers)
{
KphpFltCopyIoControl(Message, Data);
}
return;
}
case IRP_MJ_QUERY_SECURITY:
{
mdl = Data->Iopb->Parameters.QuerySecurity.MdlAddress;
buffer = Data->Iopb->Parameters.QuerySecurity.SecurityBuffer;
length = min((ULONG)Data->IoStatus.Information,
Data->Iopb->Parameters.QuerySecurity.Length);
fieldId = KphMsgFieldInformationBuffer;
break;
}
case IRP_MJ_QUERY_OPEN:
{
//
// The File System does not fill in the Information field in the
// IO_STATUS block. Filters shouldn't inspect this value in their
// post-calls. Use the length from the QueryOpen parameters.
//
if (!Data->Iopb->Parameters.QueryOpen.Length)
{
return;
}
__try
{
length = *Data->Iopb->Parameters.QueryOpen.Length;
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
INFORMER,
"Exception capturing length: %!STATUS!",
GetExceptionCode());
return;
}
buffer = Data->Iopb->Parameters.QueryOpen.FileInformation;
fieldId = KphMsgFieldInformationBuffer;
break;
}
case IRP_MJ_NETWORK_QUERY_OPEN:
{
buffer = Data->Iopb->Parameters.NetworkQueryOpen.NetworkInformation;
length = sizeof(FILE_NETWORK_OPEN_INFORMATION);
fieldId = KphMsgFieldInformationBuffer;
break;
}
default:
{
return;
}
}
KphpFltCopyBuffer(Message,
Data,
fieldId,
FALSE,
NULL,
0,
mdl,
buffer,
length,
TRUE);
}
/**
* \brief Handles name tunneling for a given file name information.
*
* \details If the file name was tunneled, the re-tunneled file name will be
* copied into the message, otherwise the original file name will be copied.
*
* \param[in,out] Message The message to copy the file name into.
* \param[in] Data The callback data for the operation.
* \param[in] FieldId The field to copy the file name into.
* \param[in] FileNameInfo The file name information to handle.
*
* \return TRUE if the file name was tunneled, FALSE otherwise.
*/
_IRQL_requires_max_(APC_LEVEL)
BOOLEAN KphpFltHandleNameTunneling(
_Inout_ PKPH_MESSAGE Message,
_In_ PFLT_CALLBACK_DATA Data,
_In_ KPH_MESSAGE_FIELD_ID FieldId,
_In_ PFLT_FILE_NAME_INFORMATION FileNameInfo
)
{
NTSTATUS status;
PFLT_FILE_NAME_INFORMATION reTunneledFileNameInfo;
KPH_NPAGED_CODE_APC_MAX_FOR_PAGING_IO();
NT_ASSERT(FileNameInfo->Format == FLT_FILE_NAME_NORMALIZED);
status = FltGetTunneledName(Data,
FileNameInfo,
&reTunneledFileNameInfo);
if (!NT_SUCCESS(status))
{
if (NT_SUCCESS(Data->IoStatus.Status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
INFORMER,
"FltGetTunneledName failed: %!STATUS!",
status);
}
reTunneledFileNameInfo = FALSE;
}
if (!reTunneledFileNameInfo)
{
//
// The file name was not tunneled.
//
status = KphMsgDynAddUnicodeString(Message,
FieldId,
&FileNameInfo->Name);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
INFORMER,
"KphMsgDynAddUnicodeString failed: %!STATUS!",
status);
}
return FALSE;
}
status = KphMsgDynAddUnicodeString(Message,
FieldId,
&reTunneledFileNameInfo->Name);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
INFORMER,
"KphMsgDynAddUnicodeString failed: %!STATUS!",
status);
}
FltReleaseFileNameInformation(reTunneledFileNameInfo);
return TRUE;
}
/**
* \brief Check if name tunneling is relevant for the operation.
*
* \param[in] Data The callback data for the operation.
*/
_IRQL_requires_max_(DISPATCH_LEVEL)
BOOLEAN KphpFltIsNameTunnelingPossible(
_In_ PFLT_CALLBACK_DATA Data
)
{
KPH_NPAGED_CODE_DISPATCH_MAX();
if (Data->Iopb->MajorFunction == IRP_MJ_CREATE)
{
return TRUE;
}
else if (Data->Iopb->MajorFunction == IRP_MJ_SET_INFORMATION)
{
switch (Data->Iopb->Parameters.SetFileInformation.FileInformationClass)
{
case FileRenameInformation:
case FileRenameInformationBypassAccessCheck:
case FileRenameInformationEx:
case FileRenameInformationExBypassAccessCheck:
case FileLinkInformation:
case FileLinkInformationBypassAccessCheck:
case FileLinkInformationEx:
case FileLinkInformationExBypassAccessCheck:
{
return TRUE;
}
default:
{
break;
}
}
}
return FALSE;
}
/**
* \brief Handles name tunneling in the post operation callback.
*
* \param[in] Data The callback data for the operation.
* \param[in,out] Context The completion context for the operation.
*/
_IRQL_requires_max_(DISPATCH_LEVEL)
VOID KphpFltPostOpHandleNameTunneling(
_In_ PFLT_CALLBACK_DATA Data,
_Inout_ PKPH_FLT_COMPLETION_CONTEXT Context
)
{
BOOLEAN tunneledFileName;
BOOLEAN tunneledDestFileName;
KPH_NPAGED_CODE_DISPATCH_MAX();
NT_ASSERT(KphpFltIsNameTunnelingPossible(Data));
tunneledFileName = FALSE;
tunneledDestFileName = FALSE;
if (Context->FileNameInfo)
{
NT_ASSERT(KeGetCurrentIrql() <= APC_LEVEL);
NT_ASSERT(Context->FileNameInfo->Format == FLT_FILE_NAME_NORMALIZED);
NT_ASSERT((Data->Iopb->MajorFunction == IRP_MJ_CREATE) ||
(Data->Iopb->MajorFunction == IRP_MJ_SET_INFORMATION));
if (KphpFltHandleNameTunneling(Context->Message,
Data,
KphMsgFieldFileName,
Context->FileNameInfo))
{
tunneledFileName = TRUE;
}
}
if (Context->DestFileNameInfo)
{
NT_ASSERT(KeGetCurrentIrql() <= APC_LEVEL);
NT_ASSERT(Context->DestFileNameInfo->Format == FLT_FILE_NAME_NORMALIZED);
NT_ASSERT(Data->Iopb->MajorFunction == IRP_MJ_SET_INFORMATION);
if (KphpFltHandleNameTunneling(Context->Message,
Data,
KphMsgFieldDestinationFileName,
Context->DestFileNameInfo))
{
tunneledDestFileName = TRUE;
}
}
if (Data->Iopb->MajorFunction == IRP_MJ_CREATE)
{
Context->Message->Kernel.File.Post.Create.TunneledFileName = tunneledFileName;
}
else if (Data->Iopb->MajorFunction == IRP_MJ_SET_INFORMATION)
{
Context->Message->Kernel.File.Post.SetInformation.TunneledFileName = tunneledFileName;
Context->Message->Kernel.File.Post.SetInformation.TunneledDestinationFileName = tunneledDestFileName;
}
}
/**
* \brief Frees a completion context.
*
* \param[in] CompletionContext The completion context to free.
*/
_IRQL_requires_max_(DISPATCH_LEVEL)
VOID KphpFltFreeCompletionContext(
_In_ PKPH_FLT_COMPLETION_CONTEXT CompletionContext
)
{
KPH_NPAGED_CODE_DISPATCH_MAX();
if (CompletionContext->Message)
{
KphFreeNPagedMessage(CompletionContext->Message);
}
//
// N.B. The file name information is released in the post operation should
// only occur from IRP_MJ_CREATE or IRP_MJ_SET_INFORMATION when the name
// needs to be possibly re-tunneled. This should only happen at or below
// APC_LEVEL.
//
if (CompletionContext->FileNameInfo)
{
NT_ASSERT(KeGetCurrentIrql() <= APC_LEVEL);
FltReleaseFileNameInformation(CompletionContext->FileNameInfo);
}
if (CompletionContext->DestFileNameInfo)
{
NT_ASSERT(KeGetCurrentIrql() <= APC_LEVEL);
FltReleaseFileNameInformation(CompletionContext->DestFileNameInfo);
}
KphFreeToNPagedLookaside(&KphpFltCompletionContextLookaside,
CompletionContext);
}
/**
* \brief Filter post operation callback for file operations.
*
* \param[in] Data The callback data for the operation.
* \param[in] FltObjects The related objects for the operation.
* \param[in] CompletionContext The completion context for the operation.
* \param[in] Flags Filter post operation flags.
*
* \return FLT_POSTOP_FINISHED_PROCESSING
*/
_Function_class_(PFLT_POST_OPERATION_CALLBACK)
_IRQL_requires_max_(DISPATCH_LEVEL)
FLT_POSTOP_CALLBACK_STATUS FLTAPI KphpFltPostOp(
_Inout_ PFLT_CALLBACK_DATA Data,
_In_ PCFLT_RELATED_OBJECTS FltObjects,
_In_opt_ PVOID CompletionContext,
_In_ FLT_POST_OPERATION_FLAGS Flags
)
{
NTSTATUS status;
ULONG64 sequence;
KPH_MESSAGE_ID messageId;
PKPH_FLT_COMPLETION_CONTEXT context;
PKPH_MESSAGE reply;
KPH_NPAGED_CODE_DISPATCH_MAX();
sequence = InterlockedIncrementU64(&KphpFltSequence);
context = CompletionContext;
reply = NULL;
if (!context)
{
goto Exit;
}
if (FlagOn(Flags, FLTFL_POST_OPERATION_DRAINING))
{
NT_ASSERT(KeGetCurrentIrql() <= APC_LEVEL);
goto Exit;
}
//
// Message was already initialized by the pre operation callback.
// But we need to update a few things for the post operation callback.
//
NT_ASSERT(NT_SUCCESS(KphMsgValidate(context->Message)));
NT_ASSERT(context->Message->Kernel.File.Sequence == 0);
NT_ASSERT(context->Message->Header.TimeStamp.QuadPart == 0);
messageId = KphpFltGetMessageId(Data->Iopb->MajorFunction, TRUE);
context->Message->Header.MessageId = messageId;
KeQuerySystemTime(&context->Message->Header.TimeStamp);
context->Message->Kernel.File.Sequence = sequence;
if (KphpFltIsNameTunnelingPossible(Data))
{
KphpFltPostOpHandleNameTunneling(Data, context);
}
KphpFltFillPostOpMessage(context->Message, Data, &context->Options);
if (context->Options.EnableStackTraces)
{
KphCaptureStackInMessage(context->Message);
}
if ((Data->Iopb->MajorFunction != IRP_MJ_CREATE) ||
!context->Options.EnablePostCreateReply)
{
KphCommsSendNPagedMessageAsync(context->Message);
context->Message = NULL;
goto Exit;
}
NT_ASSERT(KeGetCurrentIrql() == PASSIVE_LEVEL);
reply = KphAllocateNPagedMessage();
if (!reply)
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
INFORMER,
"KphAllocateNPagedMessage failed");
goto Exit;
}
status = KphCommsSendMessage(context->Message, reply);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
INFORMER,
"Failed to send message (%lu): %!STATUS!",
(ULONG)context->Message->Header.MessageId,
status);
goto Exit;
}
if ((reply->Header.MessageId == KphMsgFilePostCreate) &&
(reply->Reply.File.Post.Create.Status != STATUS_SUCCESS))
{
Data->IoStatus.Status = reply->Reply.File.Post.Create.Status;
Data->IoStatus.Information = 0;
FltCancelFileOpen(FltObjects->Instance, FltObjects->FileObject);
}
Exit:
if (reply)
{
KphFreeNPagedMessage(reply);
}
if (context)
{
KphpFltFreeCompletionContext(context);
}
if (Data->Iopb->MajorFunction == IRP_MJ_CLOSE)
{
KphpFltReapFileNameCache(Data, FltObjects);
}
return FLT_POSTOP_FINISHED_PROCESSING;
}
/**
* \brief Creates a message from pre the operation callback the post operation.
*
* \param[in] Data The callback data for the operation.
* \param[in] FltObjects The related objects for the operation.
* \param[in] Options The options for the filter.
* \param[in] FltFileName The file name information for the operation.
* \param[in] FltDestFileName The destination file name information, if any.
* \param[in] Sequence The sequence number for the pre operation.
* \param[in] TimeStamp The time stamp for the pre operation.
* \param[out] Context Set to the completion context for the operation.
*
* \return Successful or errant status.
*/
_IRQL_requires_max_(APC_LEVEL)
_Must_inspect_result_
NTSTATUS KphpFltPreOpCreateCompletionContext(
_In_ PFLT_CALLBACK_DATA Data,
_In_ PCFLT_RELATED_OBJECTS FltObjects,
_In_ PKPH_FLT_OPTIONS Options,
_In_ PKPH_FLT_FILE_NAME FltFileName,
_In_ PKPH_FLT_FILE_NAME FltDestFileName,
_In_ ULONG64 Sequence,
_In_ PLARGE_INTEGER TimeStamp,
_Out_ PKPH_FLT_COMPLETION_CONTEXT* CompletionContext
)
{
NTSTATUS status;
PKPH_FLT_COMPLETION_CONTEXT context;
KPH_NPAGED_CODE_APC_MAX_FOR_PAGING_IO();
*CompletionContext = NULL;
context = KphAllocateFromNPagedLookaside(&KphpFltCompletionContextLookaside);
if (!context)
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
INFORMER,
"KphAllocateFromNPagedLookaside failed");
status = STATUS_INSUFFICIENT_RESOURCES;
goto Exit;
}
context->Options.Flags = Options->Flags;
//
// Create the message for the post operation to finish filling in. We
// will populate what we can in the pre operation callback. In some cases
// this is necessary since post operation can complete at dispatch.
//
context->Message = KphAllocateNPagedMessage();
if (!context->Message)
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
INFORMER,
"KphAllocateNPagedMessage failed");
status = STATUS_INSUFFICIENT_RESOURCES;
goto Exit;
}
KphpFltInitMessage(context->Message,
FltObjects,
Data->Iopb->MajorFunction,
TRUE);
//
// Will be populated when the operation completes. Set a sentinel value
// for the time stamp to indicate that it is not yet valid.
//
NT_ASSERT(context->Message->Kernel.File.Sequence == 0);
context->Message->Header.TimeStamp.QuadPart = 0;
context->Message->Kernel.File.Post.PreSequence = Sequence;
context->Message->Kernel.File.Post.PreTimeStamp = *TimeStamp;
if (Options->EnablePostFileNames)
{
//
// Only if we have to will we fill in the file name information in the post
// operation (and resolve a possibly tunneled names there). In most cases
// we will copy the file name information into the post message here.
//
if (!KphpFltIsNameTunnelingPossible(Data))
{
KphpFltCopyFileName(context->Message, KphMsgFieldFileName, FltFileName);
KphpFltCopyFileName(context->Message, KphMsgFieldDestinationFileName, FltDestFileName);
}
else if (Data->Iopb->MajorFunction == IRP_MJ_CREATE)
{
if ((FltFileName->Type == KphFltFileNameTypeNameInfo) &&
(FltFileName->NameInfo->Format == FLT_FILE_NAME_NORMALIZED))
{
context->FileNameInfo = FltFileName->NameInfo;
FltReferenceFileNameInformation(context->FileNameInfo);
}
else
{
KphpFltCopyFileName(context->Message, KphMsgFieldFileName, FltFileName);
}
}
else
{
NT_ASSERT(Data->Iopb->MajorFunction == IRP_MJ_SET_INFORMATION);
if ((FltFileName->Type == KphFltFileNameTypeNameInfo) &&
(FltFileName->NameInfo->Format == FLT_FILE_NAME_NORMALIZED))
{
context->FileNameInfo = FltFileName->NameInfo;
FltReferenceFileNameInformation(context->FileNameInfo);
}
else
{
KphpFltCopyFileName(context->Message, KphMsgFieldFileName, FltFileName);
}
if ((FltDestFileName->Type == KphFltFileNameTypeNameInfo) &&
(FltDestFileName->NameInfo->Format == FLT_FILE_NAME_NORMALIZED))
{
context->DestFileNameInfo = FltDestFileName->NameInfo;
FltReferenceFileNameInformation(context->DestFileNameInfo);
}
else
{
KphpFltCopyFileName(context->Message, KphMsgFieldDestinationFileName, FltDestFileName);
}
}
}
*CompletionContext = context;
context = NULL;
status = STATUS_SUCCESS;
Exit:
if (context)
{
KphpFltFreeCompletionContext(context);
}
return status;
}
/**
* \brief Sends a pre operation message.
*
* \param[in,out] Data The callback data for the operation.
* \param[in] FltObjects The related objects for the operation.
* \param[in] Options The options for the filter.
* \param[in] FltFileName The file name information for the operation.
* \param[in] FltDestFileName The destination file name information, if any.
* \param[in] Sequence The sequence number for the operation.
* \param[out] TimeStamp Set to the time stamp for the pre operation.
*
* \return FLT_PREOP_SUCCESS_NO_CALLBACK or FLT_PREOP_COMPLETE
*/
_IRQL_requires_max_(APC_LEVEL)
FLT_PREOP_CALLBACK_STATUS KphpFltPreOpSend(
_Inout_ PFLT_CALLBACK_DATA Data,
_In_ PCFLT_RELATED_OBJECTS FltObjects,
_In_ PKPH_FLT_OPTIONS Options,
_In_ PKPH_FLT_FILE_NAME FltFileName,
_In_ PKPH_FLT_FILE_NAME FltDestFileName,
_In_ ULONG64 Sequence,
_Out_ PLARGE_INTEGER TimeStamp
)
{
FLT_PREOP_CALLBACK_STATUS callbackStatus;
NTSTATUS status;
PKPH_MESSAGE message;
PKPH_MESSAGE reply;
KPH_NPAGED_CODE_APC_MAX_FOR_PAGING_IO();
callbackStatus = FLT_PREOP_SUCCESS_NO_CALLBACK;
reply = NULL;
message = KphAllocateNPagedMessage();
if (!message)
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
INFORMER,
"KphAllocateNPagedMessage failed");
KeQuerySystemTime(TimeStamp);
goto Exit;
}
KphpFltInitMessage(message, FltObjects, Data->Iopb->MajorFunction, FALSE);
*TimeStamp = message->Header.TimeStamp;
message->Kernel.File.Sequence = Sequence;
KphpFltCopyFileName(message, KphMsgFieldFileName, FltFileName);
KphpFltCopyFileName(message, KphMsgFieldDestinationFileName, FltDestFileName);
//
// The post operation callback will resolve this name and note if it
// was a re-tunneled file name. For the pre operation callback we can
// only denote in the message that the file name is possibly tunneled.
//
if (!KphpFltIsNameTunnelingPossible(Data))
{
NOTHING;
}
else if (Data->Iopb->MajorFunction == IRP_MJ_CREATE)
{
if ((FltFileName->Type == KphFltFileNameTypeNameInfo) &&
(FltFileName->NameInfo->Format == FLT_FILE_NAME_NORMALIZED))
{
message->Kernel.File.Pre.Create.MaybeTunneledFileName = TRUE;
}
}
else
{
NT_ASSERT(Data->Iopb->MajorFunction == IRP_MJ_SET_INFORMATION);
if ((FltFileName->Type == KphFltFileNameTypeNameInfo) &&
(FltFileName->NameInfo->Format == FLT_FILE_NAME_NORMALIZED))
{
message->Kernel.File.Pre.SetInformation.MaybeTunneledFileName = TRUE;
}
if ((FltDestFileName->Type == KphFltFileNameTypeNameInfo) &&
(FltDestFileName->NameInfo->Format == FLT_FILE_NAME_NORMALIZED))
{
message->Kernel.File.Pre.SetInformation.MaybeTunneledDestinationFileName = TRUE;
}
}
KphpFltFillPreOpMessage(message, Data, Options);
if (Options->EnableStackTraces)
{
KphCaptureStackInMessage(message);
}
if ((Data->Iopb->MajorFunction != IRP_MJ_CREATE) ||
!Options->EnablePreCreateReply)
{
KphCommsSendNPagedMessageAsync(message);
message = NULL;
goto Exit;
}
reply = KphAllocateNPagedMessage();
if (!reply)
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
INFORMER,
"KphAllocateNPagedMessage failed");
goto Exit;
}
status = KphCommsSendMessage(message, reply);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
INFORMER,
"Failed to send message (%lu): %!STATUS!",
(ULONG)message->Header.MessageId,
status);
goto Exit;
}
if ((reply->Header.MessageId == KphMsgFilePreCreate) &&
(reply->Reply.File.Pre.Create.Status != STATUS_SUCCESS))
{
Data->IoStatus.Status = reply->Reply.File.Pre.Create.Status;
Data->IoStatus.Information = 0;
callbackStatus = FLT_PREOP_COMPLETE;
}
Exit:
if (reply)
{
KphFreeNPagedMessage(reply);
}
if (message)
{
KphFreeNPagedMessage(message);
}
return callbackStatus;
}
/**
* \brief Handles a request to perform an action inside of the filter.
*
* \param[in,out] Data The callback data for the operation.
* \param[in] FltObjects The related objects for the operation.
*/
VOID KphpFltRequestHandler(
_Inout_ PFLT_CALLBACK_DATA Data,
_In_ PCFLT_RELATED_OBJECTS FltObjects
)
{
PKPH_THREAD_CONTEXT thread;
//
// KphQueryVirtualMemory will use this to create a data section object.
// It will do this by issuing a MemoryMappedFilenameInformation. This
// results in an IRP_MJ_QUERY_INFORMATION with FileNameInformation. And will
// have previously set the one of the "TLS slots" to the address of the
// KphQueryVirtualMemory stack to pass information to and from this call.
//
if ((Data->Iopb->MajorFunction != IRP_MJ_QUERY_INFORMATION) ||
(Data->Iopb->Parameters.QueryFileInformation.FileInformationClass != FileNameInformation))
{
return;
}
thread = KphGetCurrentThreadContext();
if (!thread)
{
return;
}
if (thread->VmTlsCreateDataSection)
{
PKPH_VM_TLS_CREATE_DATA_SECTION tls;
NTSTATUS status;
OBJECT_ATTRIBUTES objectAttributes;
PVOID sectionObject;
tls = thread->VmTlsCreateDataSection;
thread->VmTlsCreateDataSection = NULL;
InitializeObjectAttributes(&objectAttributes,
NULL,
(tls->AccessMode ? 0 : OBJ_KERNEL_HANDLE),
NULL,
NULL);
status = FsRtlCreateSectionForDataScan(&tls->SectionHandle,
§ionObject,
&tls->SectionFileSize,
FltObjects->FileObject,
SECTION_QUERY | SECTION_MAP_READ,
&objectAttributes,
NULL,
PAGE_READONLY,
SEC_COMMIT,
0);
if (NT_SUCCESS(status))
{
ObDereferenceObject(sectionObject);
}
tls->Status = status;
}
else if (thread->VmTlsMappedInformation)
{
PKPH_MEMORY_MAPPED_INFORMATION tls;
tls = thread->VmTlsMappedInformation;
thread->VmTlsMappedInformation = NULL;
tls->FileObject = FltObjects->FileObject;
tls->SectionObjectPointers = FltObjects->FileObject->SectionObjectPointer;
if (FltObjects->FileObject->SectionObjectPointer)
{
tls->DataControlArea = FltObjects->FileObject->SectionObjectPointer->DataSectionObject;
tls->SharedCacheMap = FltObjects->FileObject->SectionObjectPointer->SharedCacheMap;
tls->ImageControlArea = FltObjects->FileObject->SectionObjectPointer->ImageSectionObject;
tls->UserWritableReferences = MmDoesFileHaveUserWritableReferences(FltObjects->FileObject->SectionObjectPointer);
}
}
KphDereferenceObject(thread);
}
/**
* \brief Filter pre operation callback for file operations.
*
* \param[in,out] Data The callback data for the operation.
* \param[in] FltObjects The related objects for the operation.
* \param[out] CompletionContext Set to the completion context if necessary.
*
* \return An appropriate callback status depending on the a post operation
* is needed or the pre operation should be completed.
*/
_Function_class_(PFLT_PRE_OPERATION_CALLBACK)
_IRQL_requires_max_(APC_LEVEL)
FLT_PREOP_CALLBACK_STATUS FLTAPI KphpFltPreOp(
_Inout_ PFLT_CALLBACK_DATA Data,
_In_ PCFLT_RELATED_OBJECTS FltObjects,
_Outptr_result_maybenull_ PVOID* CompletionContext
)
{
ULONG64 sequence;
FLT_PREOP_CALLBACK_STATUS callbackStatus;
KPH_FLT_OPTIONS options;
NTSTATUS status;
KPH_FLT_FILE_NAME fltFileName;
KPH_FLT_FILE_NAME fltDestFileName;
LARGE_INTEGER timeStamp;
KPH_NPAGED_CODE_APC_MAX_FOR_PAGING_IO();
*CompletionContext = NULL;
KphpFltRequestHandler(Data, FltObjects);
sequence = InterlockedIncrementU64(&KphpFltSequence);
callbackStatus = FLT_PREOP_SUCCESS_NO_CALLBACK;
KphpFltZeroFileName(&fltFileName);
KphpFltZeroFileName(&fltDestFileName);
options = KphpFltGetOptions(Data);
if (!options.PreEnabled && !options.PostEnabled)
{
goto Exit;
}
if (!options.EnablePagingIo &&
FlagOn(Data->Iopb->IrpFlags, IRP_PAGING_IO))
{
goto Exit;
}
if (!options.EnableSyncPagingIo &&
FlagOn(Data->Iopb->IrpFlags, IRP_SYNCHRONOUS_PAGING_IO))
{
goto Exit;
}
//
// Either pre or post is enabled, gather what is needed.
//
status = KphpFltGetFileName(Data, FltObjects, &fltFileName);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
INFORMER,
"KphpFltGetFileName failed: %!STATUS!",
status);
}
if (Data->Iopb->MajorFunction == IRP_MJ_SET_INFORMATION)
{
switch (Data->Iopb->Parameters.SetFileInformation.FileInformationClass)
{
case FileRenameInformation:
case FileRenameInformationBypassAccessCheck:
case FileRenameInformationEx:
case FileRenameInformationExBypassAccessCheck:
case FileLinkInformation:
case FileLinkInformationBypassAccessCheck:
case FileLinkInformationEx:
case FileLinkInformationExBypassAccessCheck:
{
status = KphpFltGetDestFileName(Data,
FltObjects,
&fltDestFileName);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
INFORMER,
"KphpFltGetDestFileName failed: %!STATUS!",
status);
}
break;
}
}
}
if (options.PreEnabled)
{
callbackStatus = KphpFltPreOpSend(Data,
FltObjects,
&options,
&fltFileName,
&fltDestFileName,
sequence,
&timeStamp);
NT_ASSERT((callbackStatus == FLT_PREOP_SUCCESS_NO_CALLBACK) ||
(callbackStatus == FLT_PREOP_COMPLETE));
if (callbackStatus == FLT_PREOP_COMPLETE)
{
goto Exit;
}
}
else
{
//
// Pre operations aren't enabled, but we need to grab the time stamp
// for the post operation to use.
//
KeQuerySystemTime(&timeStamp);
}
if (options.PostEnabled)
{
PKPH_FLT_COMPLETION_CONTEXT context;
status = KphpFltPreOpCreateCompletionContext(Data,
FltObjects,
&options,
&fltFileName,
&fltDestFileName,
sequence,
&timeStamp,
&context);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
INFORMER,
"KphpFltPreOpCreatePostOpMessage failed: %!STATUS!",
status);
goto Exit;
}
if (Data->Iopb->MajorFunction == IRP_MJ_SHUTDOWN)
{
//
// Post operation for IRP_MJ_SHUTDOWN is not supported, we do not
// register for one, fake it here by directly calling the post
// operation handler.
//
KphpFltPostOp(Data, FltObjects, context, 0);
}
else
{
*CompletionContext = context;
callbackStatus = FLT_PREOP_SUCCESS_WITH_CALLBACK;
}
}
Exit:
KphpFltReleaseFileName(&fltDestFileName);
KphpFltReleaseFileName(&fltFileName);
NT_ASSERT((callbackStatus == FLT_PREOP_SUCCESS_NO_CALLBACK) ||
(callbackStatus == FLT_PREOP_SUCCESS_WITH_CALLBACK) ||
(callbackStatus == FLT_PREOP_COMPLETE));
if ((callbackStatus != FLT_PREOP_SUCCESS_WITH_CALLBACK) &&
(Data->Iopb->MajorFunction == IRP_MJ_CLOSE))
{
NT_ASSERT(callbackStatus != FLT_PREOP_COMPLETE);
//
// N.B. We always require a post operation callback for IRP_MJ_CLOSE
// in order to do reaping (see: KphpFltReapFileNameCache).
//
callbackStatus = FLT_PREOP_SUCCESS_WITH_CALLBACK;
}
return callbackStatus;
}
KPH_PAGED_FILE();
/**
* \brief Cleans up the file operation filter.
*/
_IRQL_requires_max_(PASSIVE_LEVEL)
VOID KphpFltCleanupFileOp(
VOID
)
{
KPH_PAGED_CODE_PASSIVE();
if (!KphpFltOpInitialized)
{
return;
}
KphDeleteNPagedLookaside(&KphpFltCompletionContextLookaside);
}
/**
* \brief Initializes the file operation filter.
*/
_IRQL_requires_max_(PASSIVE_LEVEL)
VOID KphpFltInitializeFileOp(
VOID
)
{
KPH_PAGED_CODE_PASSIVE();
KphInitializeNPagedLookaside(&KphpFltCompletionContextLookaside,
sizeof(KPH_FLT_COMPLETION_CONTEXT),
KPH_TAG_FLT_COMPLETION_CONTEXT);
KphpFltOpInitialized = TRUE;
}
//
// These are some ugly compile time asserts intentionally hidden at the bottom
// of the file. Essentially, this is asserting that FLT_PARAMETERS has not
// changed enough to update KPHM_FILE_PARAMETERS. Realistically it should never
// change, but they're here for posterity.
//
C_ASSERT(sizeof(KPHM_FILE_PARAMETERS) == sizeof(FLT_PARAMETERS));
C_ASSERT(RTL_FIELD_SIZE(KPHM_FILE_PARAMETERS, Create) ==
RTL_FIELD_SIZE(FLT_PARAMETERS, Create));
C_ASSERT(RTL_FIELD_SIZE(KPHM_FILE_PARAMETERS, CreateNamedPipe) ==
RTL_FIELD_SIZE(FLT_PARAMETERS, CreatePipe));
C_ASSERT(RTL_FIELD_SIZE(KPHM_FILE_PARAMETERS, CreateMailslot) ==
RTL_FIELD_SIZE(FLT_PARAMETERS, CreateMailslot));
C_ASSERT(RTL_FIELD_SIZE(KPHM_FILE_PARAMETERS, Read) ==
RTL_FIELD_SIZE(FLT_PARAMETERS, Read));
C_ASSERT(RTL_FIELD_SIZE(KPHM_FILE_PARAMETERS, Write) ==
RTL_FIELD_SIZE(FLT_PARAMETERS, Write));
C_ASSERT(RTL_FIELD_SIZE(KPHM_FILE_PARAMETERS, QueryInformation) ==
RTL_FIELD_SIZE(FLT_PARAMETERS, QueryFileInformation));
C_ASSERT(RTL_FIELD_SIZE(KPHM_FILE_PARAMETERS, SetInformation) ==
RTL_FIELD_SIZE(FLT_PARAMETERS, SetFileInformation));
C_ASSERT(RTL_FIELD_SIZE(KPHM_FILE_PARAMETERS, QueryEa) ==
RTL_FIELD_SIZE(FLT_PARAMETERS, QueryEa));
C_ASSERT(RTL_FIELD_SIZE(KPHM_FILE_PARAMETERS, SetEa) ==
RTL_FIELD_SIZE(FLT_PARAMETERS, SetEa));
C_ASSERT(RTL_FIELD_SIZE(KPHM_FILE_PARAMETERS, QueryVolumeInformation) ==
RTL_FIELD_SIZE(FLT_PARAMETERS, QueryVolumeInformation));
C_ASSERT(RTL_FIELD_SIZE(KPHM_FILE_PARAMETERS, SetVolumeInformation) ==
RTL_FIELD_SIZE(FLT_PARAMETERS, SetVolumeInformation));
C_ASSERT(RTL_FIELD_SIZE(KPHM_FILE_PARAMETERS, DirectoryControl) ==
RTL_FIELD_SIZE(FLT_PARAMETERS, DirectoryControl));
C_ASSERT(RTL_FIELD_SIZE(KPHM_FILE_PARAMETERS, FileSystemControl) ==
RTL_FIELD_SIZE(FLT_PARAMETERS, FileSystemControl));
C_ASSERT(RTL_FIELD_SIZE(KPHM_FILE_PARAMETERS, DeviceControl) ==
RTL_FIELD_SIZE(FLT_PARAMETERS, DeviceIoControl));
C_ASSERT(RTL_FIELD_SIZE(KPHM_FILE_PARAMETERS, LockControl) ==
RTL_FIELD_SIZE(FLT_PARAMETERS, LockControl));
C_ASSERT(RTL_FIELD_SIZE(KPHM_FILE_PARAMETERS, QuerySecurity) ==
RTL_FIELD_SIZE(FLT_PARAMETERS, QuerySecurity));
C_ASSERT(RTL_FIELD_SIZE(KPHM_FILE_PARAMETERS, SetSecurity) ==
RTL_FIELD_SIZE(FLT_PARAMETERS, SetSecurity));
C_ASSERT(RTL_FIELD_SIZE(KPHM_FILE_PARAMETERS, SystemControl) ==
RTL_FIELD_SIZE(FLT_PARAMETERS, WMI));
C_ASSERT(RTL_FIELD_SIZE(KPHM_FILE_PARAMETERS, QueryQuota) ==
RTL_FIELD_SIZE(FLT_PARAMETERS, QueryQuota));
C_ASSERT(RTL_FIELD_SIZE(KPHM_FILE_PARAMETERS, SetQuota) ==
RTL_FIELD_SIZE(FLT_PARAMETERS, SetQuota));
C_ASSERT(RTL_FIELD_SIZE(KPHM_FILE_PARAMETERS, Pnp) ==
RTL_FIELD_SIZE(FLT_PARAMETERS, Pnp));
C_ASSERT(RTL_FIELD_SIZE(KPHM_FILE_PARAMETERS, AcquireForSectionSync) ==
RTL_FIELD_SIZE(FLT_PARAMETERS, AcquireForSectionSynchronization));
C_ASSERT(RTL_FIELD_SIZE(KPHM_FILE_PARAMETERS, AcquireForModWrite) ==
RTL_FIELD_SIZE(FLT_PARAMETERS, AcquireForModifiedPageWriter));
C_ASSERT(RTL_FIELD_SIZE(KPHM_FILE_PARAMETERS, ReleaseForModWrite) ==
RTL_FIELD_SIZE(FLT_PARAMETERS, ReleaseForModifiedPageWriter));
C_ASSERT(RTL_FIELD_SIZE(KPHM_FILE_PARAMETERS, QueryOpen) ==
RTL_FIELD_SIZE(FLT_PARAMETERS, QueryOpen));
C_ASSERT(RTL_FIELD_SIZE(KPHM_FILE_PARAMETERS, FastIoCheckIfPossible) ==
RTL_FIELD_SIZE(FLT_PARAMETERS, FastIoCheckIfPossible));
C_ASSERT(RTL_FIELD_SIZE(KPHM_FILE_PARAMETERS, NetworkQueryOpen) ==
RTL_FIELD_SIZE(FLT_PARAMETERS, NetworkQueryOpen));
C_ASSERT(RTL_FIELD_SIZE(KPHM_FILE_PARAMETERS, MdlRead) ==
RTL_FIELD_SIZE(FLT_PARAMETERS, MdlRead));
C_ASSERT(RTL_FIELD_SIZE(KPHM_FILE_PARAMETERS, MdlReadComplete) ==
RTL_FIELD_SIZE(FLT_PARAMETERS, MdlReadComplete));
C_ASSERT(RTL_FIELD_SIZE(KPHM_FILE_PARAMETERS, PrepareMdlWrite) ==
RTL_FIELD_SIZE(FLT_PARAMETERS, PrepareMdlWrite));
C_ASSERT(RTL_FIELD_SIZE(KPHM_FILE_PARAMETERS, MdlWriteComplete) ==
RTL_FIELD_SIZE(FLT_PARAMETERS, MdlWriteComplete));
C_ASSERT(RTL_FIELD_SIZE(KPHM_FILE_PARAMETERS, VolumeMount) ==
RTL_FIELD_SIZE(FLT_PARAMETERS, MountVolume));
C_ASSERT(RTL_FIELD_SIZE(KPHM_FILE_PARAMETERS, Others) ==
RTL_FIELD_SIZE(FLT_PARAMETERS, Others));
//
// N.B. We make an optimization that we only need to copy these 6 arguments
// when copying the parameter data into the message. Any of these asserts fire
// we must update KPHM_FILE_PARAMETERS.
//
C_ASSERT(RTL_SIZEOF_THROUGH_FIELD(FLT_PARAMETERS, Others.Argument1) == 0x08);
C_ASSERT(RTL_SIZEOF_THROUGH_FIELD(FLT_PARAMETERS, Others.Argument2) == 0x10);
C_ASSERT(RTL_SIZEOF_THROUGH_FIELD(FLT_PARAMETERS, Others.Argument3) == 0x18);
C_ASSERT(RTL_SIZEOF_THROUGH_FIELD(FLT_PARAMETERS, Others.Argument4) == 0x20);
C_ASSERT(RTL_SIZEOF_THROUGH_FIELD(FLT_PARAMETERS, Others.Argument5) == 0x28);
C_ASSERT(RTL_SIZEOF_THROUGH_FIELD(FLT_PARAMETERS, Others.Argument6) == 0x30);
C_ASSERT(RTL_FIELD_SIZE(KPHM_FILE_PARAMETERS, Create) <=
RTL_FIELD_SIZE(FLT_PARAMETERS, Others));
C_ASSERT(RTL_FIELD_SIZE(KPHM_FILE_PARAMETERS, CreateNamedPipe) <=
RTL_FIELD_SIZE(FLT_PARAMETERS, Others));
C_ASSERT(RTL_FIELD_SIZE(KPHM_FILE_PARAMETERS, CreateMailslot) <=
RTL_FIELD_SIZE(FLT_PARAMETERS, Others));
C_ASSERT(RTL_FIELD_SIZE(KPHM_FILE_PARAMETERS, Read) <=
RTL_FIELD_SIZE(FLT_PARAMETERS, Others));
C_ASSERT(RTL_FIELD_SIZE(KPHM_FILE_PARAMETERS, Write) <=
RTL_FIELD_SIZE(FLT_PARAMETERS, Others));
C_ASSERT(RTL_FIELD_SIZE(KPHM_FILE_PARAMETERS, QueryInformation) <=
RTL_FIELD_SIZE(FLT_PARAMETERS, Others));
C_ASSERT(RTL_FIELD_SIZE(KPHM_FILE_PARAMETERS, SetInformation) <=
RTL_FIELD_SIZE(FLT_PARAMETERS, Others));
C_ASSERT(RTL_FIELD_SIZE(KPHM_FILE_PARAMETERS, QueryEa) <=
RTL_FIELD_SIZE(FLT_PARAMETERS, Others));
C_ASSERT(RTL_FIELD_SIZE(KPHM_FILE_PARAMETERS, SetEa) <=
RTL_FIELD_SIZE(FLT_PARAMETERS, Others));
C_ASSERT(RTL_FIELD_SIZE(KPHM_FILE_PARAMETERS, QueryVolumeInformation) <=
RTL_FIELD_SIZE(FLT_PARAMETERS, Others));
C_ASSERT(RTL_FIELD_SIZE(KPHM_FILE_PARAMETERS, SetVolumeInformation) <=
RTL_FIELD_SIZE(FLT_PARAMETERS, Others));
C_ASSERT(RTL_FIELD_SIZE(KPHM_FILE_PARAMETERS, DirectoryControl) <=
RTL_FIELD_SIZE(FLT_PARAMETERS, Others));
C_ASSERT(RTL_FIELD_SIZE(KPHM_FILE_PARAMETERS, FileSystemControl) <=
RTL_FIELD_SIZE(FLT_PARAMETERS, Others));
C_ASSERT(RTL_FIELD_SIZE(KPHM_FILE_PARAMETERS, DeviceControl) <=
RTL_FIELD_SIZE(FLT_PARAMETERS, Others));
C_ASSERT(RTL_FIELD_SIZE(KPHM_FILE_PARAMETERS, LockControl) <=
RTL_FIELD_SIZE(FLT_PARAMETERS, Others));
C_ASSERT(RTL_FIELD_SIZE(KPHM_FILE_PARAMETERS, QuerySecurity) <=
RTL_FIELD_SIZE(FLT_PARAMETERS, Others));
C_ASSERT(RTL_FIELD_SIZE(KPHM_FILE_PARAMETERS, SetSecurity) <=
RTL_FIELD_SIZE(FLT_PARAMETERS, Others));
C_ASSERT(RTL_FIELD_SIZE(KPHM_FILE_PARAMETERS, SystemControl) <=
RTL_FIELD_SIZE(FLT_PARAMETERS, Others));
C_ASSERT(RTL_FIELD_SIZE(KPHM_FILE_PARAMETERS, QueryQuota) <=
RTL_FIELD_SIZE(FLT_PARAMETERS, Others));
C_ASSERT(RTL_FIELD_SIZE(KPHM_FILE_PARAMETERS, SetQuota) <=
RTL_FIELD_SIZE(FLT_PARAMETERS, Others));
C_ASSERT(RTL_FIELD_SIZE(KPHM_FILE_PARAMETERS, Pnp) <=
RTL_FIELD_SIZE(FLT_PARAMETERS, Others));
C_ASSERT(RTL_FIELD_SIZE(KPHM_FILE_PARAMETERS, AcquireForSectionSync) <=
RTL_FIELD_SIZE(FLT_PARAMETERS, Others));
C_ASSERT(RTL_FIELD_SIZE(KPHM_FILE_PARAMETERS, AcquireForModWrite) <=
RTL_FIELD_SIZE(FLT_PARAMETERS, Others));
C_ASSERT(RTL_FIELD_SIZE(KPHM_FILE_PARAMETERS, ReleaseForModWrite) <=
RTL_FIELD_SIZE(FLT_PARAMETERS, Others));
C_ASSERT(RTL_FIELD_SIZE(KPHM_FILE_PARAMETERS, QueryOpen) <=
RTL_FIELD_SIZE(FLT_PARAMETERS, Others));
C_ASSERT(RTL_FIELD_SIZE(KPHM_FILE_PARAMETERS, FastIoCheckIfPossible) <=
RTL_FIELD_SIZE(FLT_PARAMETERS, Others));
C_ASSERT(RTL_FIELD_SIZE(KPHM_FILE_PARAMETERS, NetworkQueryOpen) <=
RTL_FIELD_SIZE(FLT_PARAMETERS, Others));
C_ASSERT(RTL_FIELD_SIZE(KPHM_FILE_PARAMETERS, MdlRead) <=
RTL_FIELD_SIZE(FLT_PARAMETERS, Others));
C_ASSERT(RTL_FIELD_SIZE(KPHM_FILE_PARAMETERS, MdlReadComplete) <=
RTL_FIELD_SIZE(FLT_PARAMETERS, Others));
C_ASSERT(RTL_FIELD_SIZE(KPHM_FILE_PARAMETERS, PrepareMdlWrite) <=
RTL_FIELD_SIZE(FLT_PARAMETERS, Others));
C_ASSERT(RTL_FIELD_SIZE(KPHM_FILE_PARAMETERS, MdlWriteComplete) <=
RTL_FIELD_SIZE(FLT_PARAMETERS, Others));
C_ASSERT(RTL_FIELD_SIZE(KPHM_FILE_PARAMETERS, VolumeMount) <=
RTL_FIELD_SIZE(FLT_PARAMETERS, Others));
================================================
FILE: KSystemInformer/informer_image.c
================================================
/*
* Copyright (c) 2022 Winsider Seminars & Solutions, Inc. All rights reserved.
*
* This file is part of System Informer.
*
* Authors:
*
* jxy-s 2022-2026
*
*/
#include
#include
#include
#include
#include
KPH_PROTECTED_DATA_SECTION_PUSH();
static PVOID KphpImageVerificationCallbackHandle = NULL;
KPH_PROTECTED_DATA_SECTION_POP();
KPH_PAGED_FILE();
/**
* \brief Performs image tracking.
*
* \param[in,out] Process The process loading the image.
* \param[in] ImageInfo The image info from the notification routine.
*/
_IRQL_requires_max_(PASSIVE_LEVEL)
VOID KphpPerformImageTracking(
_Inout_ PKPH_PROCESS_CONTEXT Process,
_In_ PIMAGE_INFO_EX ImageInfo
)
{
KPH_PAGED_CODE_PASSIVE();
UNREFERENCED_PARAMETER(ImageInfo);
InterlockedIncrementSizeT(&Process->NumberOfImageLoads);
}
/**
* \brief Informs any clients of image notify routine invocations.
*
* \param[in] FullImageName File name of the loading image.
* \param[in] ProcessId Process ID where the image is being loaded.
* \param[in] ImageInfo Information about the image being loaded.
*/
_Function_class_(PLOAD_IMAGE_NOTIFY_ROUTINE)
_IRQL_requires_max_(PASSIVE_LEVEL)
VOID KphpLoadImageNotifyInformer(
_In_opt_ PUNICODE_STRING FullImageName,
_In_ HANDLE ProcessId,
_In_ PIMAGE_INFO_EX ImageInfo
)
{
NTSTATUS status;
PKPH_PROCESS_CONTEXT targetProcess;
PKPH_PROCESS_CONTEXT actorProcess;
PKPH_MESSAGE msg;
PUNICODE_STRING fileName;
BOOLEAN freeFileName;
KPH_PAGED_CODE_PASSIVE();
actorProcess = KphGetCurrentProcessContext();
targetProcess = KphGetProcessContext(ProcessId);
if (!KphInformerEnabled2(ImageLoad, actorProcess, targetProcess))
{
goto Exit;
}
msg = KphAllocateMessage();
if (!msg)
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
INFORMER,
"Failed to allocate message");
goto Exit;
}
freeFileName = TRUE;
status = KphGetNameFileObject(ImageInfo->FileObject, &fileName);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
INFORMER,
"KphGetNameFileObject failed: %!STATUS!",
status);
freeFileName = FALSE;
fileName = FullImageName;
}
KphMsgInit(msg, KphMsgImageLoad);
msg->Kernel.ImageLoad.LoadingClientId.UniqueProcess = PsGetCurrentProcessId();
msg->Kernel.ImageLoad.LoadingClientId.UniqueThread = PsGetCurrentThreadId();
msg->Kernel.ImageLoad.LoadingProcessStartKey = KphGetCurrentProcessStartKey();
msg->Kernel.ImageLoad.LoadingThreadSubProcessTag = KphGetCurrentThreadSubProcessTag();
msg->Kernel.ImageLoad.TargetProcessId = ProcessId;
msg->Kernel.ImageLoad.Properties = ImageInfo->ImageInfo.Properties;
msg->Kernel.ImageLoad.ImageBase = ImageInfo->ImageInfo.ImageBase;
msg->Kernel.ImageLoad.ImageSelector = ImageInfo->ImageInfo.ImageSelector;
msg->Kernel.ImageLoad.ImageSectionNumber = ImageInfo->ImageInfo.ImageSectionNumber;
msg->Kernel.ImageLoad.FileObject = ImageInfo->FileObject;
if (targetProcess)
{
ULONG64 startKey;
startKey = KphGetProcessStartKey(targetProcess->EProcess);
msg->Kernel.ImageLoad.TargetProcessStartKey = startKey;
}
if (fileName)
{
status = KphMsgDynAddUnicodeString(msg, KphMsgFieldFileName, fileName);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
INFORMER,
"KphMsgDynAddUnicodeString failed: %!STATUS!",
status);
}
if (freeFileName)
{
KphFreeNameFileObject(fileName);
}
}
if (KphInformerOpts2(actorProcess, targetProcess).EnableStackTraces)
{
KphCaptureStackInMessage(msg);
}
KphCommsSendMessageAsync(msg);
Exit:
if (targetProcess)
{
KphDereferenceObject(targetProcess);
}
if (actorProcess)
{
KphDereferenceObject(actorProcess);
}
}
/**
* \brief Image load notify routine.
*
* \param[in] FullImageName File name of the loading image.
* \param[in] ProcessId Process ID where the image is being loaded.
* \param[in] ImageInfo Information about the image being loaded.
*/
_Function_class_(PLOAD_IMAGE_NOTIFY_ROUTINE)
_IRQL_requires_max_(PASSIVE_LEVEL)
VOID KphpLoadImageNotifyRoutine(
_In_opt_ PUNICODE_STRING FullImageName,
_In_ HANDLE ProcessId,
_In_ PIMAGE_INFO ImageInfo
)
{
PKPH_PROCESS_CONTEXT process;
PIMAGE_INFO_EX imageInfo;
KPH_PAGED_CODE_PASSIVE();
UNREFERENCED_PARAMETER(FullImageName);
NT_ASSERT(ImageInfo->ExtendedInfoPresent);
imageInfo = CONTAINING_RECORD(ImageInfo, IMAGE_INFO_EX, ImageInfo);
process = KphGetProcessContext(ProcessId);
if (process)
{
KphpPerformImageTracking(process, imageInfo);
KphApplyImageProtections(process, imageInfo);
KphDereferenceObject(process);
}
KphpLoadImageNotifyInformer(FullImageName, ProcessId, imageInfo);
}
/**
* \brief Image verification callback.
*
* \param[in] CallbackContext Unused.
* \param[in] ImageType The type of image being verified.
* \param[in,out] ImageInformation Information about the image being verified.
*/
_IRQL_requires_same_
_Function_class_(SE_IMAGE_VERIFICATION_CALLBACK_FUNCTION)
VOID
KphpImageVerificationCallback(
_In_opt_ PVOID CallbackContext,
_In_ SE_IMAGE_TYPE ImageType,
_Inout_ PBDCB_IMAGE_INFORMATION ImageInformation
)
{
NTSTATUS status;
PKPH_PROCESS_CONTEXT actorProcess;
PKPH_MESSAGE msg;
KPHM_SIZED_BUFFER buffer;
KPH_PAGED_CODE_PASSIVE();
UNREFERENCED_PARAMETER(CallbackContext);
actorProcess = KphGetCurrentProcessContext();
if (!KphInformerEnabled(ImageVerify, actorProcess))
{
goto Exit;
}
msg = KphAllocateMessage();
if (!msg)
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
INFORMER,
"Failed to allocate message");
goto Exit;
}
KphMsgInit(msg, KphMsgImageVerify);
msg->Kernel.ImageVerify.ClientId.UniqueProcess = PsGetCurrentProcessId();
msg->Kernel.ImageVerify.ClientId.UniqueThread = PsGetCurrentThreadId();
msg->Kernel.ImageVerify.ProcessStartKey = KphGetCurrentProcessStartKey();
msg->Kernel.ImageVerify.ThreadSubProcessTag = KphGetCurrentThreadSubProcessTag();
msg->Kernel.ImageVerify.ImageType = ImageType;
msg->Kernel.ImageVerify.Classification = ImageInformation->Classification;
msg->Kernel.ImageVerify.ImageFlags = ImageInformation->ImageFlags;
msg->Kernel.ImageVerify.ImageHashAlgorithm = ImageInformation->ImageHashAlgorithm;
msg->Kernel.ImageVerify.ThumbprintHashAlgorithm = ImageInformation->ThumbprintHashAlgorithm;
status = KphMsgDynAddUnicodeString(msg,
KphMsgFieldFileName,
&ImageInformation->ImageName);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
INFORMER,
"KphMsgDynAddUnicodeString failed: %!STATUS!",
status);
}
buffer.Buffer = ImageInformation->ImageHash;
buffer.Size = (USHORT)ImageInformation->ImageHashLength;
status = KphMsgDynAddSizedBuffer(msg, KphMsgFieldHash, &buffer);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
INFORMER,
"KphMsgDynAddSizedBuffer failed: %!STATUS!",
status);
}
status = KphMsgDynAddUnicodeString(msg,
KphMsgFieldCertificatePublisher,
&ImageInformation->CertificatePublisher);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
INFORMER,
"KphMsgDynAddUnicodeString failed: %!STATUS!",
status);
}
status = KphMsgDynAddUnicodeString(msg,
KphMsgFieldCertificateIssuer,
&ImageInformation->CertificateIssuer);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
INFORMER,
"KphMsgDynAddUnicodeString failed: %!STATUS!",
status);
}
buffer.Buffer = ImageInformation->CertificateThumbprint;
buffer.Size = (USHORT)ImageInformation->CertificateThumbprintLength;
status = KphMsgDynAddSizedBuffer(msg,
KphMsgFieldCertificateThumbprint,
&buffer);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
INFORMER,
"KphMsgDynAddSizedBuffer failed: %!STATUS!",
status);
}
status = KphMsgDynAddUnicodeString(msg,
KphMsgFieldRegistryPath,
&ImageInformation->RegistryPath);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
INFORMER,
"KphMsgDynAddUnicodeString failed: %!STATUS!",
status);
}
if (KphInformerOpts(actorProcess).EnableStackTraces)
{
KphCaptureStackInMessage(msg);
}
KphCommsSendMessageAsync(msg);
Exit:
if (actorProcess)
{
KphDereferenceObject(actorProcess);
}
}
/**
* \brief Starts the image informer.
*
* \return Successful or errant status.
*/
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KphImageInformerStart(
VOID
)
{
NTSTATUS status;
KPH_PAGED_CODE_PASSIVE();
if (KphDynPsSetLoadImageNotifyRoutineEx)
{
status = KphDynPsSetLoadImageNotifyRoutineEx(
KphpLoadImageNotifyRoutine,
PS_IMAGE_NOTIFY_CONFLICTING_ARCHITECTURE);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
INFORMER,
"Failed to register image notify routine: %!STATUS!",
status);
return status;
}
}
else
{
status = PsSetLoadImageNotifyRoutine(KphpLoadImageNotifyRoutine);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
INFORMER,
"Failed to register image notify routine: %!STATUS!",
status);
return status;
}
}
if (!KphSeRegisterImageVerificationCallback ||
!KphSeUnregisterImageVerificationCallback)
{
return STATUS_SUCCESS;
}
status = KphSeRegisterImageVerificationCallback(
SeImageTypeDriver,
SeImageVerificationCallbackInformational,
KphpImageVerificationCallback,
NULL,
NULL,
&KphpImageVerificationCallbackHandle);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
INFORMER,
"Failed to register image verification callback: "
"%!STATUS!",
status);
KphpImageVerificationCallbackHandle = NULL;
return status;
}
return STATUS_SUCCESS;
}
/**
* \brief Stops the image informer.
*/
_IRQL_requires_max_(PASSIVE_LEVEL)
VOID KphImageInformerStop(
VOID
)
{
KPH_PAGED_CODE_PASSIVE();
if (KphpImageVerificationCallbackHandle)
{
NT_ASSERT(KphSeUnregisterImageVerificationCallback);
KphSeUnregisterImageVerificationCallback(KphpImageVerificationCallbackHandle);
}
PsRemoveLoadImageNotifyRoutine(KphpLoadImageNotifyRoutine);
}
================================================
FILE: KSystemInformer/informer_object.c
================================================
/*
* Copyright (c) 2022 Winsider Seminars & Solutions, Inc. All rights reserved.
*
* This file is part of System Informer.
*
* Authors:
*
* jxy-s 2022-2026
*
*/
#include
#include
#include
#include
#include
typedef union _KPH_OB_OPTIONS
{
UCHAR Flags;
struct
{
UCHAR PreEnabled : 1;
UCHAR PostEnabled : 1;
UCHAR EnableStackTraces : 1;
UCHAR Spare : 5;
};
} KPH_OB_OPTIONS, *PKPH_OB_OPTIONS;
typedef struct _KPH_OB_CALL_CONTEXT
{
ULONG64 PreSequence;
LARGE_INTEGER PreTimeStamp;
KPH_OB_OPTIONS Options;
ACCESS_MASK DesiredAccess;
ACCESS_MASK OriginalDesiredAccess;
HANDLE SourceProcessId;
HANDLE TargetProcessId;
} KPH_OB_CALL_CONTEXT, *PKPH_OB_CALL_CONTEXT;
KPH_PROTECTED_DATA_SECTION_PUSH();
static PVOID KphpObRegistrationHandle = NULL;
KPH_PROTECTED_DATA_SECTION_POP();
static PAGED_LOOKASIDE_LIST KphpObCallContextLookaside = { 0 };
KPH_PAGED_FILE();
static ULONG64 KphpObSequence = 0;
/**
* \brief Retrieves the object operation options.
*
* \param[in] Info Object manager pre-operation callback information.
*
* \return The object operation options.
*/
_IRQL_requires_max_(APC_LEVEL)
KPH_OB_OPTIONS KphpObGetOptions(
_In_ POB_PRE_OPERATION_INFORMATION Info
)
{
KPH_OB_OPTIONS options;
PKPH_PROCESS_CONTEXT process;
KPH_PAGED_CODE();
options.Flags = 0;
if (Info->KernelHandle)
{
process = KphGetSystemProcessContext();
}
else
{
process = KphGetCurrentProcessContext();
}
#define KPH_OB_SETTING(name) \
if (KphInformerEnabled(HandlePre##name, process)) \
{ \
options.PreEnabled = TRUE; \
} \
if (KphInformerEnabled(HandlePost##name, process)) \
{ \
options.PostEnabled = TRUE; \
}
if (Info->Operation == OB_OPERATION_HANDLE_CREATE)
{
if (Info->ObjectType == *PsProcessType)
{
KPH_OB_SETTING(CreateProcess);
}
else if (Info->ObjectType == *PsThreadType)
{
KPH_OB_SETTING(CreateThread);
}
else
{
NT_ASSERT(Info->ObjectType == *ExDesktopObjectType);
KPH_OB_SETTING(CreateDesktop);
}
}
else
{
if (Info->ObjectType == *PsProcessType)
{
KPH_OB_SETTING(DuplicateProcess);
}
else if (Info->ObjectType == *PsThreadType)
{
KPH_OB_SETTING(DuplicateThread);
}
else
{
NT_ASSERT(Info->ObjectType == *ExDesktopObjectType);
KPH_OB_SETTING(DuplicateDesktop);
}
}
if (options.PreEnabled || options.PostEnabled)
{
options.EnableStackTraces = !!KphInformerOpts(process).EnableStackTraces;
}
if (process)
{
KphDereferenceObject(process);
}
return options;
}
/**
* \brief Retrieves the message ID for the object pre-operation callback.
*
* \param[in] Info Object manager pre-operation callback information.
*
* \return The message ID for the callback.
*/
_IRQL_requires_max_(APC_LEVEL)
KPH_MESSAGE_ID KphpObPreGetMessageId(
_In_ POB_PRE_OPERATION_INFORMATION Info
)
{
KPH_MESSAGE_ID messageId;
KPH_PAGED_CODE();
if (Info->Operation == OB_OPERATION_HANDLE_CREATE)
{
if (Info->ObjectType == *PsProcessType)
{
messageId = KphMsgHandlePreCreateProcess;
}
else if (Info->ObjectType == *PsThreadType)
{
messageId = KphMsgHandlePreCreateThread;
}
else
{
NT_ASSERT(Info->ObjectType == *ExDesktopObjectType);
messageId = KphMsgHandlePreCreateDesktop;
}
}
else
{
NT_ASSERT(Info->Operation == OB_OPERATION_HANDLE_DUPLICATE);
if (Info->ObjectType == *PsProcessType)
{
messageId = KphMsgHandlePreDuplicateProcess;
}
else if (Info->ObjectType == *PsThreadType)
{
messageId = KphMsgHandlePreDuplicateThread;
}
else
{
NT_ASSERT(Info->ObjectType == *ExDesktopObjectType);
messageId = KphMsgHandlePreDuplicateDesktop;
}
}
return messageId;
}
/**
* \brief Retrieves the message ID for the object post-operation callback.
*
* \param[in] Info Object manager post-operation callback information.
*
* \return The message ID for the callback.
*/
_IRQL_requires_max_(APC_LEVEL)
KPH_MESSAGE_ID KphpObPostGetMessageId(
_In_ POB_POST_OPERATION_INFORMATION Info
)
{
KPH_MESSAGE_ID messageId;
KPH_PAGED_CODE();
if (Info->Operation == OB_OPERATION_HANDLE_CREATE)
{
if (Info->ObjectType == *PsProcessType)
{
messageId = KphMsgHandlePostCreateProcess;
}
else if (Info->ObjectType == *PsThreadType)
{
messageId = KphMsgHandlePostCreateThread;
}
else
{
NT_ASSERT(Info->ObjectType == *ExDesktopObjectType);
messageId = KphMsgHandlePostCreateDesktop;
}
}
else
{
NT_ASSERT(Info->Operation == OB_OPERATION_HANDLE_DUPLICATE);
if (Info->ObjectType == *PsProcessType)
{
messageId = KphMsgHandlePostDuplicateProcess;
}
else if (Info->ObjectType == *PsThreadType)
{
messageId = KphMsgHandlePostDuplicateThread;
}
else
{
NT_ASSERT(Info->ObjectType == *ExDesktopObjectType);
messageId = KphMsgHandlePostDuplicateDesktop;
}
}
return messageId;
}
/**
* \brief Copies the object name into a message for registered object callbacks.
*
* \details Used to populate the desktop name for desktop object callbacks.
*
* \param[in,out] Message The message to populate.
* \param[in] Object The object for which the name is populated in the message.
*/
_IRQL_requires_max_(APC_LEVEL)
VOID KphpObCopyObjectName(
_Inout_ PKPH_MESSAGE Message,
_In_ PVOID Object
)
{
NTSTATUS status;
POBJECT_NAME_INFORMATION info;
ULONG length;
KPH_PAGED_CODE();
info = NULL;
status = ObQueryNameString(Object, NULL, 0, &length);
if ((status != STATUS_INFO_LENGTH_MISMATCH) &&
(status != STATUS_BUFFER_TOO_SMALL))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
INFORMER,
"ObQueryNameString: %!STATUS!",
status);
goto Exit;
}
info = KphAllocatePaged(length, KPH_TAG_OB_OBJECT_NAME);
if (!info)
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
INFORMER,
"Failed to allocate buffer for object name");
goto Exit;
}
status = ObQueryNameString(Object, info, length, &length);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
INFORMER,
"ObQueryNameString failed: %!STATUS!",
status);
goto Exit;
}
status = KphMsgDynAddUnicodeString(Message,
KphMsgFieldObjectName,
&info->Name);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
INFORMER,
"KphMsgDynAddUnicodeString failed: %!STATUS!",
status);
}
Exit:
if (info)
{
KphFree(info, KPH_TAG_OB_OBJECT_NAME);
}
}
/**
* \brief Fills the object pre-operation callback message.
*
* \param[in,out] Message The message to populate.
* \param[in] Info Object manager pre-operation callback information.
*/
_IRQL_requires_max_(APC_LEVEL)
VOID KphpObPreFillMessage(
_Inout_ PKPH_MESSAGE Message,
_In_ POB_PRE_OPERATION_INFORMATION Info
)
{
KPH_PAGED_CODE();
Message->Kernel.Handle.ContextClientId.UniqueProcess = PsGetCurrentProcessId();
Message->Kernel.Handle.ContextClientId.UniqueThread = PsGetCurrentThreadId();
Message->Kernel.Handle.ContextProcessStartKey = KphGetCurrentProcessStartKey();
Message->Kernel.Handle.Flags = Info->Flags;
Message->Kernel.Handle.Object = Info->Object;
if (Info->Operation == OB_OPERATION_HANDLE_CREATE)
{
POB_PRE_CREATE_HANDLE_INFORMATION params;
params = &Info->Parameters->CreateHandleInformation;
Message->Kernel.Handle.Pre.DesiredAccess = params->DesiredAccess;
Message->Kernel.Handle.Pre.OriginalDesiredAccess = params->OriginalDesiredAccess;
if (Info->ObjectType == *PsProcessType)
{
Message->Kernel.Handle.Pre.Create.Process.ProcessId =
PsGetProcessId(Info->Object);
}
else if (Info->ObjectType == *PsThreadType)
{
Message->Kernel.Handle.Pre.Create.Thread.ClientId.UniqueProcess =
PsGetProcessId(PsGetThreadProcess(Info->Object));
Message->Kernel.Handle.Pre.Create.Thread.ClientId.UniqueThread =
PsGetThreadId(Info->Object);
Message->Kernel.Handle.Pre.Create.Thread.SubProcessTag =
KphGetThreadSubProcessTag(Info->Object);
}
else
{
NT_ASSERT(Info->ObjectType == *ExDesktopObjectType);
KphpObCopyObjectName(Message, Info->Object);
}
}
else
{
POB_PRE_DUPLICATE_HANDLE_INFORMATION params;
NT_ASSERT(Info->Operation == OB_OPERATION_HANDLE_DUPLICATE);
params = &Info->Parameters->DuplicateHandleInformation;
Message->Kernel.Handle.Duplicate = TRUE;
Message->Kernel.Handle.Pre.DesiredAccess = params->DesiredAccess;
Message->Kernel.Handle.Pre.OriginalDesiredAccess = params->OriginalDesiredAccess;
Message->Kernel.Handle.Pre.Duplicate.SourceProcessId = PsGetProcessId(params->SourceProcess);
Message->Kernel.Handle.Pre.Duplicate.TargetProcessId = PsGetProcessId(params->TargetProcess);
if (Info->ObjectType == *PsProcessType)
{
Message->Kernel.Handle.Pre.Duplicate.Process.ProcessId =
PsGetProcessId(Info->Object);
}
else if (Info->ObjectType == *PsThreadType)
{
Message->Kernel.Handle.Pre.Duplicate.Thread.ClientId.UniqueProcess =
PsGetProcessId(PsGetThreadProcess(Info->Object));
Message->Kernel.Handle.Pre.Duplicate.Thread.ClientId.UniqueThread =
PsGetThreadId(Info->Object);
Message->Kernel.Handle.Pre.Duplicate.Thread.SubProcessTag =
KphGetThreadSubProcessTag(Info->Object);
}
else
{
NT_ASSERT(Info->ObjectType == *ExDesktopObjectType);
KphpObCopyObjectName(Message, Info->Object);
}
}
}
/**
* \brief Fills the object post-operation callback message.
*
* \param[in,out] Message The message to populate.
* \param[in] Info Object manager post-operation callback information.
* \param[in] Context The object call context.
*/
_IRQL_requires_max_(APC_LEVEL)
VOID KphpObPostFillMessage(
_Inout_ PKPH_MESSAGE Message,
_In_ POB_POST_OPERATION_INFORMATION Info,
_In_ PKPH_OB_CALL_CONTEXT Context
)
{
KPH_PAGED_CODE();
Message->Kernel.Handle.ContextClientId.UniqueProcess = PsGetCurrentProcessId();
Message->Kernel.Handle.ContextClientId.UniqueThread = PsGetCurrentThreadId();
Message->Kernel.Handle.ContextProcessStartKey = KphGetCurrentProcessStartKey();
Message->Kernel.Handle.ContextThreadSubProcessTag = KphGetCurrentThreadSubProcessTag();
Message->Kernel.Handle.Flags = Info->Flags;
Message->Kernel.Handle.Object = Info->Object;
Message->Kernel.Handle.PostOperation = TRUE;
Message->Kernel.Handle.Post.PreSequence = Context->PreSequence;
Message->Kernel.Handle.Post.PreTimeStamp = Context->PreTimeStamp;
Message->Kernel.Handle.Post.ReturnStatus = Info->ReturnStatus;
Message->Kernel.Handle.Post.DesiredAccess = Context->DesiredAccess;
Message->Kernel.Handle.Post.OriginalDesiredAccess = Context->OriginalDesiredAccess;
if (Info->Operation == OB_OPERATION_HANDLE_CREATE)
{
POB_POST_CREATE_HANDLE_INFORMATION params;
params = &Info->Parameters->CreateHandleInformation;
Message->Kernel.Handle.Post.GrantedAccess = params->GrantedAccess;
if (Info->ObjectType == *PsProcessType)
{
Message->Kernel.Handle.Post.Create.Process.ProcessId =
PsGetProcessId(Info->Object);
}
else if (Info->ObjectType == *PsThreadType)
{
Message->Kernel.Handle.Post.Create.Thread.ClientId.UniqueProcess =
PsGetProcessId(PsGetThreadProcess(Info->Object));
Message->Kernel.Handle.Post.Create.Thread.ClientId.UniqueThread =
PsGetThreadId(Info->Object);
Message->Kernel.Handle.Post.Create.Thread.SubProcessTag =
KphGetThreadSubProcessTag(Info->Object);
}
else
{
NT_ASSERT(Info->ObjectType == *ExDesktopObjectType);
KphpObCopyObjectName(Message, Info->Object);
}
}
else
{
POB_POST_DUPLICATE_HANDLE_INFORMATION params;
NT_ASSERT(Info->Operation == OB_OPERATION_HANDLE_DUPLICATE);
params = &Info->Parameters->DuplicateHandleInformation;
Message->Kernel.Handle.Duplicate = TRUE;
Message->Kernel.Handle.Post.GrantedAccess = params->GrantedAccess;
Message->Kernel.Handle.Post.Duplicate.SourceProcessId = Context->SourceProcessId;
Message->Kernel.Handle.Post.Duplicate.TargetProcessId = Context->TargetProcessId;
if (Info->ObjectType == *PsProcessType)
{
Message->Kernel.Handle.Post.Duplicate.Process.ProcessId =
PsGetProcessId(Info->Object);
}
else if (Info->ObjectType == *PsThreadType)
{
Message->Kernel.Handle.Post.Duplicate.Thread.ClientId.UniqueProcess =
PsGetProcessId(PsGetThreadProcess(Info->Object));
Message->Kernel.Handle.Post.Duplicate.Thread.ClientId.UniqueThread =
PsGetThreadId(Info->Object);
Message->Kernel.Handle.Post.Duplicate.Thread.SubProcessTag =
KphGetThreadSubProcessTag(Info->Object);
}
else
{
NT_ASSERT(Info->ObjectType == *ExDesktopObjectType);
KphpObCopyObjectName(Message, Info->Object);
}
}
}
/**
* \brief Sends an object post-operation callback message.
*
* \param[in] Info Object manager post-operation callback information.
* \param[in] Sequence The sequence number for the message.
* \param[in] Context The object call context.
*/
_IRQL_requires_max_(APC_LEVEL)
VOID KphpObPostOpSend(
_In_ POB_POST_OPERATION_INFORMATION Info,
_In_ ULONG64 Sequence,
_In_ PKPH_OB_CALL_CONTEXT Context
)
{
PKPH_MESSAGE msg;
KPH_PAGED_CODE();
msg = KphAllocateMessage();
if (!msg)
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
INFORMER,
"Failed to allocate message");
return;
}
KphMsgInit(msg, KphpObPostGetMessageId(Info));
msg->Kernel.Handle.Sequence = Sequence;
KphpObPostFillMessage(msg, Info, Context);
if (Context->Options.EnableStackTraces)
{
KphCaptureStackInMessage(msg);
}
KphCommsSendMessageAsync(msg);
}
/**
* \brief Object manager post-operation callback.
*
* \param[in] Context Not used.
* \param[in] Info The object post-operation information.
*/
_Function_class_(POB_POST_OPERATION_CALLBACK)
_IRQL_requires_max_(APC_LEVEL)
VOID KphpObPostOp(
_In_ PVOID Context,
_In_ POB_POST_OPERATION_INFORMATION Info
)
{
PKPH_OB_CALL_CONTEXT context;
ULONG64 sequence;
KPH_PAGED_CODE();
UNREFERENCED_PARAMETER(Context);
sequence = InterlockedIncrementU64(&KphpObSequence);
context = Info->CallContext;
if (context)
{
KphpObPostOpSend(Info, sequence, context);
KphFreeToPagedLookaside(&KphpObCallContextLookaside, context);
}
}
/**
* \brief Sets the object callback call context for the post-operation to use.
*
* \param[in] Info Object manager pre-operation callback information.
* \param[in] Options Object operation options to use.
* \param[in] Sequence The pre-operation sequence number.
* \param[in] TimeStamp The pre-operation time stamp.
*/
_IRQL_requires_max_(APC_LEVEL)
VOID KphpObPreOpSetCallContext(
_In_ POB_PRE_OPERATION_INFORMATION Info,
_In_ PKPH_OB_OPTIONS Options,
_In_ ULONG64 Sequence,
_In_ PLARGE_INTEGER TimeStamp
)
{
PKPH_OB_CALL_CONTEXT context;
KPH_PAGED_CODE();
context = KphAllocateFromPagedLookaside(&KphpObCallContextLookaside);
if (!context)
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
INFORMER,
"Failed to allocate call context");
return;
}
context->PreSequence = Sequence;
context->PreTimeStamp = *TimeStamp;
context->Options.Flags = Options->Flags;
if (Info->Operation == OB_OPERATION_HANDLE_CREATE)
{
POB_PRE_CREATE_HANDLE_INFORMATION params;
params = &Info->Parameters->CreateHandleInformation;
context->DesiredAccess = params->DesiredAccess;
context->OriginalDesiredAccess = params->OriginalDesiredAccess;
}
else
{
POB_PRE_DUPLICATE_HANDLE_INFORMATION params;
NT_ASSERT(Info->Operation == OB_OPERATION_HANDLE_DUPLICATE);
params = &Info->Parameters->DuplicateHandleInformation;
context->DesiredAccess = params->DesiredAccess;
context->OriginalDesiredAccess = params->OriginalDesiredAccess;
context->SourceProcessId = PsGetProcessId(params->SourceProcess);
context->TargetProcessId = PsGetProcessId(params->TargetProcess);
}
Info->CallContext = context;
}
/**
* \brief Sends an object pre-operation callback message.
*
* \param[in] Info Object manager pre-operation callback information.
* \param[in] Options Object operation options to use.
* \param[in] Sequence The sequence number for the message.
* \param[out] TimeStamp Receives the time stamp of the pre-operation message.
*/
_IRQL_requires_max_(APC_LEVEL)
VOID KphpObPreOpSend(
_In_ POB_PRE_OPERATION_INFORMATION Info,
_In_ PKPH_OB_OPTIONS Options,
_In_ ULONG64 Sequence,
_Out_ PLARGE_INTEGER TimeStamp
)
{
PKPH_MESSAGE msg;
KPH_PAGED_CODE();
msg = KphAllocateMessage();
if (!msg)
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
INFORMER,
"Failed to allocate message");
KeQuerySystemTime(TimeStamp);
return;
}
KphMsgInit(msg, KphpObPreGetMessageId(Info));
*TimeStamp = msg->Header.TimeStamp;
msg->Kernel.Handle.Sequence = Sequence;
KphpObPreFillMessage(msg, Info);
if (Options->EnableStackTraces)
{
KphCaptureStackInMessage(msg);
}
KphCommsSendMessageAsync(msg);
}
/**
* \brief Performs process tracking in object manager callbacks.
*
* \param[in] ProcessObject The process object to track.
*/
_IRQL_requires_max_(PASSIVE_LEVEL)
VOID KphpObPerformProcessTracking(
_In_ PEPROCESS ProcessObject
)
{
PKPH_PROCESS_CONTEXT process;
KPH_PAGED_CODE_PASSIVE();
process = KphGetEProcessContext(ProcessObject);
if (process)
{
if (process->CreateNotification || process->NumberOfThreads)
{
//
// Let the process notification routine handle this process.
//
// N.B. We check CreateNotification and NumberOfThreads because we
// may have loaded too late to observe the original create
// notification. This avoids unnecessarily entering the logic below.
//
goto Exit;
}
//
// N.B. If a process is created and terminated without ever having a
// thread, the registered process notifications will not be called.
//
// In this case, indicators such as ExitTime, ExitStatus, and
// ProcessExiting will not be updated. However, the process exit
// synchronization (rundown) will be activated when the process
// terminates.
//
if (NT_SUCCESS(PsAcquireProcessExitSynchronization(ProcessObject)))
{
PsReleaseProcessExitSynchronization(ProcessObject);
goto Exit;
}
KphDereferenceObject(process);
process = KphUntrackProcessContext(PsGetProcessId(ProcessObject));
if (process)
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
TRACKING,
"Stopped tracking process %wZ (%lu)",
&process->ImageName,
HandleToULong(process->ProcessId));
}
goto Exit;
}
if (PsGetProcessExitProcessCalled(ProcessObject))
{
//
// Do not begin tracking the process if it has already exited.
//
goto Exit;
}
process = KphTrackProcessContext(ProcessObject);
if (process)
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
TRACKING,
"Tracking process %wZ (%lu)",
&process->ImageName,
HandleToULong(process->ProcessId));
KphVerifyProcessAndProtectIfAppropriate(process);
}
else
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
TRACKING,
"Failed to track process %lu",
HandleToULong(PsGetProcessId(ProcessObject)));
}
Exit:
if (process)
{
KphDereferenceObject(process);
}
}
/**
* \brief Performs thread tracking in object manager callbacks.
*
* \param[in] ThreadObject The thread object to track.
*/
_IRQL_requires_max_(APC_LEVEL)
VOID KphpObPerformThreadTracking(
_In_ PETHREAD ThreadObject
)
{
PKPH_THREAD_CONTEXT thread;
//
// N.B. Although Microsoft's documentation states that object callbacks
// are invoked at PASSIVE_LEVEL, they can and *do* get invoked at APC_LEVEL
// in practice.
//
// This is because thread creation can happen at APC_LEVEL and thread
// creation will open a handle to the thread object, which in turn invokes
// the object manager callbacks. This behavior has been observed and is
// triggered by Microsoft's own kernel components - no third-party drivers.
// As such, assuming callbacks always run at PASSIVE_LEVEL is unsafe. This
// contradicts the stated IRQL guarantees - use caution.
//
// For reference, see the documentation for PCREATE_THREAD_NOTIFY_ROUTINE.
// Note that although PsCreateSystemThread being documented as requiring
// PASSIVE_LEVEL, it can and *does* get invoked at APC_LEVEL in practice.
//
KPH_PAGED_CODE();
thread = KphGetEThreadContext(ThreadObject);
if (thread)
{
//
// Let the thread notification routine handle any exiting threads.
//
goto Exit;
}
if (PsIsThreadTerminating(ThreadObject))
{
//
// Do not begin tracking the thread if it is terminating.
//
goto Exit;
}
thread = KphTrackThreadContext(ThreadObject);
if (thread)
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
TRACKING,
"Tracking thread %lu in process %wZ (%lu)",
HandleToULong(thread->ClientId.UniqueThread),
KphGetThreadImageName(thread),
HandleToULong(thread->ClientId.UniqueProcess));
}
else
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
TRACKING,
"Failed to track thread %lu in process %lu",
HandleToULong(PsGetThreadId(ThreadObject)),
HandleToULong(PsGetThreadProcessId(ThreadObject)));
}
Exit:
if (thread)
{
KphDereferenceObject(thread);
}
}
/**
* \brief Performs process and thread tracking in object manager callbacks.
*
* \param[in] Info Object manager pre-operation callback information.
*/
_IRQL_requires_max_(APC_LEVEL)
VOID KphpObPerformProcessAndThreadTracking(
_In_ POB_PRE_OPERATION_INFORMATION Info
)
{
KIRQL currentIrql;
KPH_PAGED_CODE();
//
// During the object pre-operation callback, supplement process and thread
// tracking that is normally performed in the process and thread
// notification routines.
//
// The process notification routines are called when the initial thread is
// inserted into a process. However, a process can exist before those
// routines are invoked. If no thread is ever created in the process, it
// may exist without triggering any notification callbacks. When a handle is
// created for such a process (either during creation or by being opened
// from another process), we take this opportunity to perform tracking.
//
// We also use this point to sanity check that other contexts exist.
// If one doesn’t, it may be due to earlier resource constraints.
//
// N.B. If another driver has registered a process or thread notification
// routine before ours, it can cause us to land here before our own routines
// are called. Our tracking handles this scenario gracefully. This typically
// happens with security products that invoke user-mode code during process
// creation, and their user-mode component opens the process.
//
if (Info->KernelHandle)
{
//
// N.B. This prevents our own tracking routines from becoming reentrant.
// Our tracking routines always open a kernel handle.
//
return;
}
//
// N.B. Thread creation routines can be invoked at APC_LEVEL, which in turn
// causes the object manager's pre-operation callbacks to be invoked. Our
// thread tracking supports being called at APC_LEVEL, but process tracking
// requires PASSIVE_LEVEL. Therefore, we check the current IRQL below before
// attempting any process tracking.
//
currentIrql = KeGetCurrentIrql();
if (currentIrql == PASSIVE_LEVEL)
{
KphpObPerformProcessTracking(PsGetCurrentProcess());
}
KphpObPerformThreadTracking(PsGetCurrentThread());
if (Info->ObjectType == *PsProcessType)
{
if (currentIrql == PASSIVE_LEVEL)
{
KphpObPerformProcessTracking(Info->Object);
}
}
else if (Info->ObjectType == *PsThreadType)
{
if (currentIrql == PASSIVE_LEVEL)
{
KphpObPerformProcessTracking(PsGetThreadProcess(Info->Object));
}
KphpObPerformThreadTracking(Info->Object);
}
}
/**
* \brief Object manager pre-operation callback.
*
* \param[in] Context Not used.
* \param[in,out] Info The object pre-operation information.
*
* \return OB_PREOP_SUCCESS
*/
_Function_class_(POB_PRE_OPERATION_CALLBACK)
_IRQL_requires_max_(APC_LEVEL)
OB_PREOP_CALLBACK_STATUS KphpObPreOp(
_In_ PVOID Context,
_Inout_ POB_PRE_OPERATION_INFORMATION Info
)
{
KPH_OB_OPTIONS options;
ULONG64 sequence;
LARGE_INTEGER timeStamp;
//
// N.B. Although Microsoft's documentation states that object callbacks
// are invoked at PASSIVE_LEVEL, they can and *do* get invoked at APC_LEVEL
// in practice.
//
// This is because thread creation can happen at APC_LEVEL and thread
// creation will open a handle to the thread object, which in turn invokes
// the object manager callbacks. This behavior has been observed and is
// triggered by Microsoft's own kernel components - no third-party drivers.
// As such, assuming callbacks always run at PASSIVE_LEVEL is unsafe. This
// contradicts the stated IRQL guarantees - use caution.
//
// For reference, see the documentation for PCREATE_THREAD_NOTIFY_ROUTINE.
// Note that although PsCreateSystemThread being documented as requiring
// PASSIVE_LEVEL, it can and *does* get invoked at APC_LEVEL in practice.
//
KPH_PAGED_CODE();
UNREFERENCED_PARAMETER(Context);
KphpObPerformProcessAndThreadTracking(Info);
KphApplyObProtections(Info);
sequence = InterlockedIncrementU64(&KphpObSequence);
options = KphpObGetOptions(Info);
if (options.PreEnabled)
{
KphpObPreOpSend(Info, &options, sequence, &timeStamp);
}
else
{
//
// Pre operations aren't enabled, but we need the time stamp for the
// post operation to use.
//
KeQuerySystemTime(&timeStamp);
}
if (options.PostEnabled)
{
KphpObPreOpSetCallContext(Info, &options, sequence, &timeStamp);
}
return OB_PREOP_SUCCESS;
}
/**
* \brief Starts the object informer.
*
* \return Successful or errant status.
*/
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KphObjectInformerStart(
VOID
)
{
NTSTATUS status;
OB_CALLBACK_REGISTRATION callbackRegistration;
OB_OPERATION_REGISTRATION operationRegistration[3];
KPH_PAGED_CODE_PASSIVE();
NT_ASSERT(KphAltitude);
RtlZeroMemory(operationRegistration, sizeof(operationRegistration));
operationRegistration[0].ObjectType = PsProcessType;
operationRegistration[0].Operations = OB_OPERATION_HANDLE_CREATE | OB_OPERATION_HANDLE_DUPLICATE;
operationRegistration[0].PreOperation = KphpObPreOp;
operationRegistration[0].PostOperation = KphpObPostOp;
operationRegistration[1].ObjectType = PsThreadType;
operationRegistration[1].Operations = OB_OPERATION_HANDLE_CREATE | OB_OPERATION_HANDLE_DUPLICATE;
operationRegistration[1].PreOperation = KphpObPreOp;
operationRegistration[1].PostOperation = KphpObPostOp;
operationRegistration[2].ObjectType = ExDesktopObjectType;
operationRegistration[2].Operations = OB_OPERATION_HANDLE_CREATE | OB_OPERATION_HANDLE_DUPLICATE;
operationRegistration[2].PreOperation = KphpObPreOp;
operationRegistration[2].PostOperation = KphpObPostOp;
callbackRegistration.Version = OB_FLT_REGISTRATION_VERSION;
callbackRegistration.OperationRegistrationCount = ARRAYSIZE(operationRegistration);
callbackRegistration.Altitude.Buffer = KphAltitude->Buffer;
callbackRegistration.Altitude.Length = KphAltitude->Length;
callbackRegistration.Altitude.MaximumLength = KphAltitude->MaximumLength;
callbackRegistration.RegistrationContext = NULL;
callbackRegistration.OperationRegistration = operationRegistration;
KphInitializePagedLookaside(&KphpObCallContextLookaside,
sizeof(KPH_OB_CALL_CONTEXT),
KPH_TAG_OB_CALL_CONTEXT);
status = ObRegisterCallbacks(&callbackRegistration,
&KphpObRegistrationHandle);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
INFORMER,
"ObRegisterCallbacks failed: %!STATUS!",
status);
KphDeletePagedLookaside(&KphpObCallContextLookaside);
KphpObRegistrationHandle = NULL;
}
return status;
}
/**
* \brief Stops the object informer.
*/
_IRQL_requires_max_(PASSIVE_LEVEL)
VOID KphObjectInformerStop(
VOID
)
{
KPH_PAGED_CODE_PASSIVE();
if (KphpObRegistrationHandle)
{
ObUnRegisterCallbacks(KphpObRegistrationHandle);
KphDeletePagedLookaside(&KphpObCallContextLookaside);
}
}
================================================
FILE: KSystemInformer/informer_process.c
================================================
/*
* Copyright (c) 2022 Winsider Seminars & Solutions, Inc. All rights reserved.
*
* This file is part of System Informer.
*
* Authors:
*
* jxy-s 2022-2026
*
*/
#include
#include
#include
#include
#include
typedef struct _KPH_PROCESS_CREATE_APC
{
KSI_KAPC Apc;
PKPH_THREAD_CONTEXT Actor;
} KPH_PROCESS_CREATE_APC, *PKPH_PROCESS_CREATE_APC;
KPH_PROTECTED_DATA_SECTION_PUSH();
static PKPH_NPAGED_LOOKASIDE_OBJECT KphpProcesCreateApcLookaside = NULL;
KPH_PROTECTED_DATA_SECTION_POP();
KPH_PAGED_FILE();
/**
* \brief Allocates the process create APC which is used to track when a thread
* is actively in the kernel creating a process.
*
* \return Pointer to allocates process create APC, null on failure.
*/
_IRQL_requires_max_(PASSIVE_LEVEL)
_Return_allocatesMem_
PKPH_PROCESS_CREATE_APC KphpAllocateProcessCreateApc(
VOID
)
{
PKPH_PROCESS_CREATE_APC apc;
KPH_PAGED_CODE_PASSIVE();
NT_ASSERT(KphpProcesCreateApcLookaside);
apc = KphAllocateFromNPagedLookasideObject(KphpProcesCreateApcLookaside);
if (apc)
{
KphReferenceObject(KphpProcesCreateApcLookaside);
}
return apc;
}
/**
* \brief Frees a previously allocated process create APC.
*
* \param[in] Apc The process create APC to free.
*/
_IRQL_requires_max_(APC_LEVEL)
VOID KphpFreeProcessCreateApc(
_In_freesMem_ PKPH_PROCESS_CREATE_APC Apc
)
{
KPH_PAGED_CODE();
NT_ASSERT(KphpProcesCreateApcLookaside);
KphDereferenceObject(Apc->Actor);
KphFreeToNPagedLookasideObject(KphpProcesCreateApcLookaside, Apc);
KphDereferenceObject(KphpProcesCreateApcLookaside);
}
/**
* \brief Performing process tracking.
*
* \param[in] Process The process object from the notification callback.
* \param[in] ProcessId Process ID from the notification callback.
* \param[in] CreateInfo The create information from the callback.
*
* \return Pointer to the process context, may be null, the caller should
* dereference this object if it is non-null.
*/
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
PKPH_PROCESS_CONTEXT KphpPerformProcessTracking(
_Inout_ PEPROCESS Process,
_In_ HANDLE ProcessId,
_Inout_opt_ PPS_CREATE_NOTIFY_INFO CreateInfo
)
{
PKPH_PROCESS_CONTEXT process;
PKPH_PROCESS_CONTEXT creatorProcess;
KPH_PAGED_CODE_PASSIVE();
creatorProcess = NULL;
if (!CreateInfo)
{
process = KphUntrackProcessContext(ProcessId);
if (!process)
{
goto Exit;
}
KphTracePrint(TRACE_LEVEL_VERBOSE,
TRACKING,
"Stopped tracking process %wZ (%lu)",
&process->ImageName,
HandleToULong(process->ProcessId));
process->ExitNotification = TRUE;
NT_ASSERT(process->NumberOfThreads == 0);
NT_ASSERT(IsListEmpty(&process->ThreadListHead));
goto Exit;
}
NT_ASSERT(CreateInfo);
process = KphTrackProcessContext(Process);
if (!process)
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
TRACKING,
"Failed to track process %lu",
HandleToULong(ProcessId));
goto Exit;
}
process->CreateNotification = TRUE;
process->CreatorClientId.UniqueProcess = PsGetCurrentProcessId();
process->CreatorClientId.UniqueThread = PsGetCurrentThreadId();
KphTracePrint(TRACE_LEVEL_VERBOSE,
TRACKING,
"Tracking process %wZ (%lu)",
&process->ImageName,
HandleToULong(process->ProcessId));
KphVerifyProcessAndProtectIfAppropriate(process);
creatorProcess = KphGetCurrentProcessContext();
if (!creatorProcess)
{
goto Exit;
}
if (!KphTestProcessContextState(process, KPH_PROCESS_STATE_HIGH))
{
goto Exit;
}
if (KphTestProcessContextState(creatorProcess, KPH_PROCESS_STATE_MAXIMUM))
{
process->SecurelyCreated = TRUE;
}
else
{
PS_PROTECTION processProtection;
processProtection = PsGetProcessProtection(creatorProcess->EProcess);
if ((processProtection.Type != PsProtectedTypeNone) &&
((processProtection.Signer == PsProtectedSignerWinTcb) ||
(processProtection.Signer == PsProtectedSignerWinSystem)))
{
process->SecurelyCreated = TRUE;
}
}
Exit:
if (creatorProcess)
{
KphDereferenceObject(creatorProcess);
}
return process;
}
/**
* \brief Informs any clients of process notify routine invocations.
*
* \param[in,out] Process The process being created.
* \param[in,out] CreateInfo Information on the process being created, if the
* process is being destroyed this is NULL.
*
*/
_Function_class_(PCREATE_PROCESS_NOTIFY_ROUTINE_EX)
_IRQL_requires_max_(PASSIVE_LEVEL)
VOID KphpCreateProcessNotifyInformer(
_In_ PKPH_PROCESS_CONTEXT Process,
_Inout_opt_ PPS_CREATE_NOTIFY_INFO CreateInfo
)
{
NTSTATUS status;
PKPH_MESSAGE msg;
PKPH_MESSAGE reply;
PEPROCESS parentProcess;
PKPH_PROCESS_CONTEXT actorProcess;
KPH_INFORMER_OPTIONS opts;
KPH_PAGED_CODE_PASSIVE();
msg = NULL;
reply = NULL;
parentProcess = NULL;
actorProcess = KphGetCurrentProcessContext();
if (CreateInfo)
{
if (!KphInformerEnabled(ProcessCreate, actorProcess))
{
goto Exit;
}
msg = KphAllocateMessage();
if (!msg)
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
INFORMER,
"Failed to allocate message");
goto Exit;
}
status = PsLookupProcessByProcessId(CreateInfo->ParentProcessId,
&parentProcess);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
INFORMER,
"PsLookupProcessByProcessId failed: %!STATUS!",
status);
parentProcess = NULL;
}
KphMsgInit(msg, KphMsgProcessCreate);
msg->Kernel.ProcessCreate.CreatingClientId.UniqueProcess = PsGetCurrentProcessId();
msg->Kernel.ProcessCreate.CreatingClientId.UniqueThread = PsGetCurrentThreadId();
msg->Kernel.ProcessCreate.CreatingProcessStartKey = KphGetCurrentProcessStartKey();
msg->Kernel.ProcessCreate.CreatingThreadSubProcessTag = KphGetCurrentThreadSubProcessTag();
msg->Kernel.ProcessCreate.TargetProcessId = Process->ProcessId;
msg->Kernel.ProcessCreate.TargetProcessStartKey = KphGetProcessStartKey(Process->EProcess);
msg->Kernel.ProcessCreate.Flags = CreateInfo->Flags;
msg->Kernel.ProcessCreate.FileObject = CreateInfo->FileObject;
msg->Kernel.ProcessCreate.ParentProcessId = CreateInfo->ParentProcessId;
if (parentProcess)
{
msg->Kernel.ProcessCreate.ParentProcessStartKey = KphGetProcessStartKey(parentProcess);
}
if (Process->ImageFileName)
{
status = KphMsgDynAddUnicodeString(msg,
KphMsgFieldFileName,
Process->ImageFileName);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
INFORMER,
"KphMsgDynAddUnicodeString failed: %!STATUS!",
status);
}
}
if (CreateInfo->CommandLine)
{
status = KphMsgDynAddUnicodeString(msg,
KphMsgFieldCommandLine,
CreateInfo->CommandLine);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
INFORMER,
"KphMsgDynAddUnicodeString failed: %!STATUS!",
status);
}
}
opts = KphInformerOpts(actorProcess);
if (opts.EnableStackTraces)
{
KphCaptureStackInMessage(msg);
}
if (!opts.EnableProcessCreateReply)
{
KphCommsSendMessageAsync(msg);
msg = NULL;
goto Exit;
}
reply = KphAllocateMessage();
if (!reply)
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
INFORMER,
"Failed to allocate message");
goto Exit;
}
status = KphCommsSendMessage(msg, reply);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
INFORMER,
"Failed to send message (%lu): %!STATUS!",
(ULONG)msg->Header.MessageId,
status);
goto Exit;
}
if ((reply->Header.MessageId == KphMsgProcessCreate) &&
(reply->Reply.ProcessCreate.CreationStatus != STATUS_SUCCESS))
{
CreateInfo->CreationStatus = reply->Reply.ProcessCreate.CreationStatus;
}
}
else
{
if (!KphInformerEnabled(ProcessExit, actorProcess))
{
goto Exit;
}
msg = KphAllocateMessage();
if (!msg)
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
INFORMER,
"Failed to allocate message");
goto Exit;
}
KphMsgInit(msg, KphMsgProcessExit);
msg->Kernel.ProcessExit.ClientId.UniqueProcess = PsGetCurrentProcessId();
msg->Kernel.ProcessExit.ClientId.UniqueThread = PsGetCurrentThreadId();
msg->Kernel.ProcessExit.ProcessStartKey = KphGetProcessStartKey(Process->EProcess);
msg->Kernel.ProcessExit.ExitStatus = PsGetProcessExitStatus(Process->EProcess);
if (KphInformerOpts(actorProcess).EnableStackTraces)
{
KphCaptureStackInMessage(msg);
}
KphCommsSendMessageAsync(msg);
msg = NULL;
}
Exit:
if (reply)
{
KphFreeMessage(reply);
}
if (msg)
{
KphFreeMessage(msg);
}
if (parentProcess)
{
ObDereferenceObject(parentProcess);
}
if (actorProcess)
{
KphDereferenceObject(actorProcess);
}
}
/**
* \brief Cleanup routine for the create process APC.
*
* \param[in] Apc The APC to clean up.
* \param[in] Reason Unused.
*/
_Function_class_(KSI_KCLEANUP_ROUTINE)
_IRQL_requires_min_(PASSIVE_LEVEL)
_IRQL_requires_max_(APC_LEVEL)
_IRQL_requires_same_
VOID
KSIAPI
KphpProcessCreateCleanupRoutine(
_In_ PKSI_KAPC Apc,
_In_ KSI_KAPC_CLEANUP_REASON Reason
)
{
PKPH_PROCESS_CREATE_APC apc;
KPH_PAGED_CODE();
UNREFERENCED_PARAMETER(Reason);
apc = CONTAINING_RECORD(Apc, KPH_PROCESS_CREATE_APC, Apc);
KphpFreeProcessCreateApc(apc);
}
/**
* \brief Performs process creation actions at return from system.
*
* \details We use an APC to do state tracking in the current thread context.
* This enables us to identify when the thread is currently in the kernel
* creating a process. In this kernel routine we clear state from the acting
* thread before it returns from the system.
*
* \param[in] Apc The APC that is executing.
* \param[in] NormalRoutine Set to NULL to cancel the faked routine.
* \param[in,out] NormalContext Unused.
* \param[in,out] SystemArgument1 Unused.
* \param[in,out] SystemArgument2 Unused.
*/
_Function_class_(KSI_KKERNEL_ROUTINE)
_IRQL_requires_(APC_LEVEL)
_IRQL_requires_same_
VOID KSIAPI KphpProcessCreateKernelRoutine(
_In_ PKSI_KAPC Apc,
_Inout_ _Deref_pre_maybenull_ PKNORMAL_ROUTINE* NormalRoutine,
_Inout_ _Deref_pre_maybenull_ PVOID* NormalContext,
_Inout_ _Deref_pre_maybenull_ PVOID* SystemArgument1,
_Inout_ _Deref_pre_maybenull_ PVOID* SystemArgument2
)
{
PKPH_PROCESS_CREATE_APC apc;
KPH_PAGED_CODE_APC();
apc = CONTAINING_RECORD(Apc, KPH_PROCESS_CREATE_APC, Apc);
DBG_UNREFERENCED_PARAMETER(NormalRoutine);
NT_ASSERT(apc->Actor->ProcessContext->ApcNoopRoutine);
NT_ASSERT(*NormalRoutine == apc->Actor->ProcessContext->ApcNoopRoutine);
*NormalContext = NULL;
*SystemArgument1 = NULL;
*SystemArgument2 = NULL;
NT_ASSERT(apc->Actor->IsCreatingProcess);
apc->Actor->IsCreatingProcess = FALSE;
apc->Actor->IsCreatingProcessId = NULL;
}
/**
* \brief Performs tracking of the actor thread creating a process.
*
* \details We use an APC to do state tracking in the current thread context.
* This enables us to identify when the thread is currently in the kernel
* creating a process. This will be cleared when the kernel routine fires
* before returning from the system.
*
* \param[in] Process The process context of the process being created.
* \param[in,out] CreateInfo Information on the process being created, if the
* process is being destroyed this is NULL.
*/
_IRQL_requires_max_(PASSIVE_LEVEL)
VOID KphpPerformProcessCreationTracking(
_In_ PKPH_PROCESS_CONTEXT Process,
_Inout_opt_ PPS_CREATE_NOTIFY_INFO CreateInfo
)
{
NTSTATUS status;
PKPH_THREAD_CONTEXT actor;
PKPH_PROCESS_CREATE_APC apc;
BOOLEAN stopProtecting;
KPH_PAGED_CODE_PASSIVE();
if (!CreateInfo)
{
return;
}
NT_ASSERT(PsGetCurrentProcessId() == CreateInfo->CreatingThreadId.UniqueProcess);
NT_ASSERT(PsGetCurrentThreadId() == CreateInfo->CreatingThreadId.UniqueThread);
if (!KphIsProtectedProcess(Process))
{
return;
}
//
// If we fail here we will stop protecting the process. If protection is
// stopped the process will not be given full access to the driver APIs.
//
stopProtecting = TRUE;
apc = NULL;
actor = KphGetCurrentThreadContext();
if (!actor || !actor->ProcessContext)
{
KphTracePrint(TRACE_LEVEL_VERBOSE, TRACKING, "Insufficient tracking.");
goto Exit;
}
if (actor->ProcessContext->IsSubsystemProcess)
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
TRACKING,
"Skipping for subsystem process.");
goto Exit;
}
status = KphCheckProcessApcNoopRoutine(actor->ProcessContext);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
TRACKING,
"KphCheckProcessApcNoopRoutine failed: %!STATUS!",
status);
goto Exit;
}
apc = KphpAllocateProcessCreateApc();
if (!apc)
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
TRACKING,
"Failed to allocate create process APC");
goto Exit;
}
apc->Actor = actor;
actor = NULL;
apc->Actor->IsCreatingProcess = TRUE;
apc->Actor->IsCreatingProcessId = Process->ProcessId;
KsiInitializeApc(&apc->Apc,
KphDriverObject,
apc->Actor->EThread,
OriginalApcEnvironment,
KphpProcessCreateKernelRoutine,
KphpProcessCreateCleanupRoutine,
apc->Actor->ProcessContext->ApcNoopRoutine,
UserMode,
NULL);
if (!KsiInsertQueueApc(&apc->Apc, NULL, NULL, IO_NO_INCREMENT))
{
KphTracePrint(TRACE_LEVEL_VERBOSE, TRACKING, "KsiInsertQueueApc failed");
//
// No choice other than to reset the actor state immediately.
//
apc->Actor->IsCreatingProcess = FALSE;
apc->Actor->IsCreatingProcessId = NULL;
goto Exit;
}
//
// Stage the thread for user mode APC delivery.
//
KeTestAlertThread(UserMode);
stopProtecting = FALSE;
apc = NULL;
Exit:
if (apc)
{
KphpFreeProcessCreateApc(apc);
}
if (actor)
{
KphDereferenceObject(actor);
}
if (stopProtecting)
{
KphStopProtectingProcess(Process);
}
}
/**
* \brief Process create notify routine.
*
* \param[in,out] Process The process object being created.
* \param[in] ProcessId ProcessID of the process being created.
* \param[in,out] CreateInfo Information on the process being created, if the
* process is being destroyed this is NULL.
*/
_Function_class_(PCREATE_PROCESS_NOTIFY_ROUTINE_EX)
_IRQL_requires_max_(PASSIVE_LEVEL)
VOID KphpCreateProcessNotifyRoutine(
_Inout_ PEPROCESS Process,
_In_ HANDLE ProcessId,
_Inout_opt_ PPS_CREATE_NOTIFY_INFO CreateInfo
)
{
PKPH_PROCESS_CONTEXT process;
KPH_PAGED_CODE_PASSIVE();
process = KphpPerformProcessTracking(Process, ProcessId, CreateInfo);
if (process)
{
KphpPerformProcessCreationTracking(process, CreateInfo);
KphpCreateProcessNotifyInformer(process, CreateInfo);
KphDereferenceObject(process);
}
}
/**
* \brief Starts the process informer.
*
* \return Successful or errant status.
*/
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KphProcessInformerStart(
VOID
)
{
NTSTATUS status;
KPH_PAGED_CODE_PASSIVE();
status = KphCreateNPagedLookasideObject(&KphpProcesCreateApcLookaside,
sizeof(KPH_PROCESS_CREATE_APC),
KPH_TAG_PROCESS_CREATE_APC);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
INFORMER,
"KphCreateNPagedLookasideObject failed: %!STATUS!",
status);
KphpProcesCreateApcLookaside = NULL;
goto Exit;
}
if (KphDynPsSetCreateProcessNotifyRoutineEx2)
{
status = KphDynPsSetCreateProcessNotifyRoutineEx2(PsCreateProcessNotifySubsystems,
(PVOID)KphpCreateProcessNotifyRoutine,
FALSE);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
INFORMER,
"Failed to register process notify routine: %!STATUS!",
status);
goto Exit;
}
}
else
{
status = PsSetCreateProcessNotifyRoutineEx(KphpCreateProcessNotifyRoutine,
FALSE);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
INFORMER,
"Failed to register process notify routine: %!STATUS!",
status);
goto Exit;
}
}
Exit:
if (!NT_SUCCESS(status))
{
if (KphpProcesCreateApcLookaside)
{
KphDereferenceObject(KphpProcesCreateApcLookaside);
KphpProcesCreateApcLookaside = NULL;
}
}
return status;
}
/**
* \brief Stops the process informer.
*
* \return Successful or errant status.
*/
_IRQL_requires_max_(PASSIVE_LEVEL)
VOID KphProcessInformerStop(
VOID
)
{
KPH_PAGED_CODE_PASSIVE();
if (!KphpProcesCreateApcLookaside)
{
return;
}
if (KphDynPsSetCreateProcessNotifyRoutineEx2)
{
KphDynPsSetCreateProcessNotifyRoutineEx2(PsCreateProcessNotifySubsystems,
(PVOID)KphpCreateProcessNotifyRoutine,
TRUE);
}
else
{
PsSetCreateProcessNotifyRoutineEx(KphpCreateProcessNotifyRoutine, TRUE);
}
KphDereferenceObject(KphpProcesCreateApcLookaside);
}
================================================
FILE: KSystemInformer/informer_registry.c
================================================
/*
* Copyright (c) 2022 Winsider Seminars & Solutions, Inc. All rights reserved.
*
* This file is part of System Informer.
*
* Authors:
*
* jxy-s 2023-2026
*
*/
#include
#include
#include
#include
#include
typedef union _KPH_REG_OPTIONS
{
UCHAR Flags;
struct
{
UCHAR InPost : 1;
UCHAR PreEnabled : 1;
UCHAR PostEnabled : 1;
UCHAR EnableStackTraces : 1;
UCHAR EnablePostObjectNames : 1;
UCHAR EnablePostValueNames : 1;
UCHAR EnableValueBuffers : 1;
UCHAR Spare : 1;
};
} KPH_REG_OPTIONS, *PKPH_REG_OPTIONS;
typedef struct _KPH_REG_CALL_CONTEXT
{
ULONG64 PreSequence;
LARGE_INTEGER PreTimeStamp;
KPH_REG_OPTIONS Options;
ULONG_PTR ObjectId;
PUNICODE_STRING ObjectName;
PVOID Transaction;
} KPH_REG_CALL_CONTEXT, *PKPH_REG_CALL_CONTEXT;
typedef union _KPH_REG_PRE_INFORMATION
{
//
// Invalid for CreateKey and OpenKey. RootObject for LoadKey. Always NULL
// for SaveMergedKey. Otherwise valid.
//
PVOID Object;
REG_DELETE_KEY_INFORMATION DeleteKey;
REG_SET_VALUE_KEY_INFORMATION SetValueKey;
REG_DELETE_VALUE_KEY_INFORMATION DeleteValueKey;
REG_SET_INFORMATION_KEY_INFORMATION SetInformationKey;
REG_RENAME_KEY_INFORMATION RenameKey;
REG_ENUMERATE_KEY_INFORMATION EnumerateKey;
REG_ENUMERATE_VALUE_KEY_INFORMATION EnumerateValueKey;
REG_QUERY_KEY_INFORMATION QueryKey;
REG_QUERY_VALUE_KEY_INFORMATION QueryValueKey;
REG_QUERY_MULTIPLE_VALUE_KEY_INFORMATION QueryMultipleValueKey;
REG_KEY_HANDLE_CLOSE_INFORMATION KeyHandleClose;
REG_CREATE_KEY_INFORMATION_V1 CreateKey;
REG_OPEN_KEY_INFORMATION_V1 OpenKey;
REG_FLUSH_KEY_INFORMATION FlushKey;
REG_LOAD_KEY_INFORMATION_V2 LoadKey;
REG_UNLOAD_KEY_INFORMATION UnLoadKey;
REG_QUERY_KEY_SECURITY_INFORMATION QueryKeySecurity;
REG_SET_KEY_SECURITY_INFORMATION SetKeySecurity;
REG_RESTORE_KEY_INFORMATION RestoreKey;
REG_SAVE_KEY_INFORMATION SaveKey;
REG_REPLACE_KEY_INFORMATION ReplaceKey;
REG_QUERY_KEY_NAME QueryKeyName;
REG_SAVE_MERGED_KEY_INFORMATION SaveMergedKey;
} KPH_REG_PRE_INFORMATION, *PKPH_REG_PRE_INFORMATION;
typedef union _KPH_REG_POST_INFORMATION
{
//
// Must inspect Common.Status for STATUS_SUCCESS for CreateKey and OpenKey.
// Always NULL for LoadKey and SaveMergedKey. Unsafe to use with
// CmCallbackGetKeyObjectIDEx during post KeyHandleClose. Otherwise valid.
//
PVOID Object;
REG_POST_OPERATION_INFORMATION Common;
} KPH_REG_POST_INFORMATION, *PKPH_REG_POST_INFORMATION;
KPH_PROTECTED_DATA_SECTION_RO_PUSH();
static const UNICODE_STRING KphpCmDefaultValueName = RTL_CONSTANT_STRING(L"(Default)");
KPH_PROTECTED_DATA_SECTION_RO_POP();
static PAGED_LOOKASIDE_LIST KphpCmCallContextLookaside = { 0 };
KPH_PAGED_FILE();
static ULONG64 KphpCmSequence = 0;
static BOOLEAN KphpCmRegistered = FALSE;
static LARGE_INTEGER KphpCmCookie = { 0 };
/**
* \brief Retrieves the registry operation options for the specified class.
*
* \param[in] RegClass The registry operation class.
*
* \return Options for the specified class.
*/
_IRQL_requires_max_(APC_LEVEL)
KPH_REG_OPTIONS KphpRegGetOptions(
_In_ REG_NOTIFY_CLASS RegClass
)
{
KPH_REG_OPTIONS options;
PKPH_PROCESS_CONTEXT process;
KPH_PAGED_CODE();
options.Flags = 0;
if (ExGetPreviousMode() != KernelMode)
{
process = KphGetCurrentProcessContext();
}
else
{
process = KphGetSystemProcessContext();
}
#define KPH_REG_SETTING2(reg, name) \
case RegNtPre##reg: \
{ \
if (KphInformerEnabled(RegPre##name, process)) \
{ \
options.PreEnabled = TRUE; \
} \
if (KphInformerEnabled(RegPost##name, process)) \
{ \
options.PostEnabled = TRUE; \
} \
break; \
} \
case RegNtPost##reg: \
{ \
options.InPost = TRUE; \
break; \
}
#define KPH_REG_SETTING(name) KPH_REG_SETTING2(name, name)
switch (RegClass)
{
KPH_REG_SETTING(DeleteKey)
KPH_REG_SETTING(SetValueKey)
KPH_REG_SETTING(DeleteValueKey)
KPH_REG_SETTING(SetInformationKey)
KPH_REG_SETTING(RenameKey)
KPH_REG_SETTING(EnumerateKey)
KPH_REG_SETTING(EnumerateValueKey)
KPH_REG_SETTING(QueryKey)
KPH_REG_SETTING(QueryValueKey)
KPH_REG_SETTING(QueryMultipleValueKey)
KPH_REG_SETTING(KeyHandleClose)
KPH_REG_SETTING2(CreateKeyEx, CreateKey)
KPH_REG_SETTING2(OpenKeyEx, OpenKey)
KPH_REG_SETTING(FlushKey)
KPH_REG_SETTING(LoadKey)
KPH_REG_SETTING(UnLoadKey)
KPH_REG_SETTING(QueryKeySecurity)
KPH_REG_SETTING(SetKeySecurity)
KPH_REG_SETTING(RestoreKey)
KPH_REG_SETTING(SaveKey)
KPH_REG_SETTING(ReplaceKey)
KPH_REG_SETTING(QueryKeyName)
KPH_REG_SETTING(SaveMergedKey)
case RegNtPreCreateKey: // XP
case RegNtPostCreateKey: // XP
case RegNtPreOpenKey: // XP
case RegNtPostOpenKey: // XP
default:
{
NT_ASSERT(FALSE);
KphTracePrint(TRACE_LEVEL_VERBOSE,
INFORMER,
"Unsupported registry operation: %lu",
RegClass);
break;
}
}
if (!options.InPost && (options.PreEnabled || options.PostEnabled))
{
KPH_INFORMER_OPTIONS opts;
opts = KphInformerOpts(process);
options.EnableStackTraces = !!opts.EnableStackTraces;
options.EnablePostObjectNames = !!opts.RegEnablePostObjectNames;
options.EnablePostValueNames = !!opts.RegEnablePostValueNames;
options.EnableValueBuffers = !!opts.RegEnableValueBuffers;
}
if (process)
{
KphDereferenceObject(process);
}
return options;
}
/**
* \brief Retrieves the message ID for the specified registry operation class.
*
* \param[in] RegClass The registry operation class.
*
* \return The message ID for the specified class.
*/
_IRQL_requires_max_(APC_LEVEL)
KPH_MESSAGE_ID KphpRegGetMessageId(
_In_ REG_NOTIFY_CLASS RegClass
)
{
KPH_MESSAGE_ID messageId;
KPH_PAGED_CODE();
#define KPH_REG_MESSAGE_ID2(reg, name) \
case RegNtPre##reg: \
{ \
messageId = KphMsgRegPre##name; \
break; \
} \
case RegNtPost##reg: \
{ \
messageId = KphMsgRegPost##name; \
break; \
}
#define KPH_REG_MESSAGE_ID(name) KPH_REG_MESSAGE_ID2(name, name)
switch (RegClass)
{
KPH_REG_MESSAGE_ID(DeleteKey)
KPH_REG_MESSAGE_ID(SetValueKey)
KPH_REG_MESSAGE_ID(DeleteValueKey)
KPH_REG_MESSAGE_ID(SetInformationKey)
KPH_REG_MESSAGE_ID(RenameKey)
KPH_REG_MESSAGE_ID(EnumerateKey)
KPH_REG_MESSAGE_ID(EnumerateValueKey)
KPH_REG_MESSAGE_ID(QueryKey)
KPH_REG_MESSAGE_ID(QueryValueKey)
KPH_REG_MESSAGE_ID(QueryMultipleValueKey)
KPH_REG_MESSAGE_ID(KeyHandleClose)
KPH_REG_MESSAGE_ID2(CreateKeyEx, CreateKey)
KPH_REG_MESSAGE_ID2(OpenKeyEx, OpenKey)
KPH_REG_MESSAGE_ID(FlushKey)
KPH_REG_MESSAGE_ID(LoadKey)
KPH_REG_MESSAGE_ID(UnLoadKey)
KPH_REG_MESSAGE_ID(QueryKeySecurity)
KPH_REG_MESSAGE_ID(SetKeySecurity)
KPH_REG_MESSAGE_ID(RestoreKey)
KPH_REG_MESSAGE_ID(SaveKey)
KPH_REG_MESSAGE_ID(ReplaceKey)
KPH_REG_MESSAGE_ID(QueryKeyName)
KPH_REG_MESSAGE_ID(SaveMergedKey)
DEFAULT_UNREACHABLE;
}
return messageId;
}
/**
* \brief Populates a registry operation message with common information.
*
* \param[in,out] Message The message to populate.
* \param[in] RegClass The registry operation class.
* \param[in] PreInfo The pre-operation information.
*/
_IRQL_requires_max_(APC_LEVEL)
VOID KphpRegFillCommonMessage(
_Inout_ PKPH_MESSAGE Message,
_In_ REG_NOTIFY_CLASS RegClass,
_In_ PKPH_REG_PRE_INFORMATION PreInfo
)
{
KPH_PAGED_CODE();
Message->Kernel.Reg.ClientId.UniqueProcess = PsGetCurrentProcessId();
Message->Kernel.Reg.ClientId.UniqueThread = PsGetCurrentThreadId();
Message->Kernel.Reg.ProcessStartKey = KphGetCurrentProcessStartKey();
Message->Kernel.Reg.ThreadSubProcessTag = KphGetCurrentThreadSubProcessTag();
Message->Kernel.Reg.PreviousMode = (ExGetPreviousMode() != KernelMode);
#define KPH_REG_COPY_PARAMETER(name, value) \
Message->Kernel.Reg.Parameters.name.value = PreInfo->name.value
switch (RegClass)
{
case RegNtPreDeleteKey:
case RegNtPostDeleteKey:
{
break;
}
case RegNtPreSetValueKey:
case RegNtPostSetValueKey:
{
KPH_REG_COPY_PARAMETER(SetValueKey, TitleIndex);
KPH_REG_COPY_PARAMETER(SetValueKey, Type);
KPH_REG_COPY_PARAMETER(SetValueKey, Data);
KPH_REG_COPY_PARAMETER(SetValueKey, DataSize);
break;
}
case RegNtPreDeleteValueKey:
case RegNtPostDeleteValueKey:
{
KPH_REG_COPY_PARAMETER(DeleteValueKey, ValueName);
break;
}
case RegNtPreSetInformationKey:
case RegNtPostSetInformationKey:
{
KPH_REG_COPY_PARAMETER(SetInformationKey, KeySetInformationClass);
KPH_REG_COPY_PARAMETER(SetInformationKey, KeySetInformation);
KPH_REG_COPY_PARAMETER(SetInformationKey, KeySetInformationLength);
break;
}
case RegNtPreRenameKey:
case RegNtPostRenameKey:
{
KPH_REG_COPY_PARAMETER(RenameKey, NewName);
break;
}
case RegNtPreEnumerateKey:
case RegNtPostEnumerateKey:
{
KPH_REG_COPY_PARAMETER(EnumerateKey, Index);
KPH_REG_COPY_PARAMETER(EnumerateKey, KeyInformationClass);
KPH_REG_COPY_PARAMETER(EnumerateKey, KeyInformation);
KPH_REG_COPY_PARAMETER(EnumerateKey, Length);
KPH_REG_COPY_PARAMETER(EnumerateKey, ResultLength);
break;
}
case RegNtPreEnumerateValueKey:
case RegNtPostEnumerateValueKey:
{
KPH_REG_COPY_PARAMETER(EnumerateValueKey, Index);
KPH_REG_COPY_PARAMETER(EnumerateValueKey, KeyValueInformationClass);
KPH_REG_COPY_PARAMETER(EnumerateValueKey, KeyValueInformation);
KPH_REG_COPY_PARAMETER(EnumerateValueKey, Length);
KPH_REG_COPY_PARAMETER(EnumerateValueKey, ResultLength);
break;
}
case RegNtPreQueryKey:
case RegNtPostQueryKey:
{
KPH_REG_COPY_PARAMETER(QueryKey, KeyInformationClass);
KPH_REG_COPY_PARAMETER(QueryKey, KeyInformation);
KPH_REG_COPY_PARAMETER(QueryKey, Length);
KPH_REG_COPY_PARAMETER(QueryKey, ResultLength);
break;
}
case RegNtPreQueryValueKey:
case RegNtPostQueryValueKey:
{
KPH_REG_COPY_PARAMETER(QueryValueKey, ValueName);
KPH_REG_COPY_PARAMETER(QueryValueKey, KeyValueInformationClass);
KPH_REG_COPY_PARAMETER(QueryValueKey, KeyValueInformation);
KPH_REG_COPY_PARAMETER(QueryValueKey, Length);
KPH_REG_COPY_PARAMETER(QueryValueKey, ResultLength);
break;
}
case RegNtPreQueryMultipleValueKey:
case RegNtPostQueryMultipleValueKey:
{
KPH_REG_COPY_PARAMETER(QueryMultipleValueKey, ValueEntries);
KPH_REG_COPY_PARAMETER(QueryMultipleValueKey, EntryCount);
KPH_REG_COPY_PARAMETER(QueryMultipleValueKey, ValueBuffer);
KPH_REG_COPY_PARAMETER(QueryMultipleValueKey, BufferLength);
KPH_REG_COPY_PARAMETER(QueryMultipleValueKey, RequiredBufferLength);
break;
}
case RegNtPreKeyHandleClose:
case RegNtPostKeyHandleClose:
{
break;
}
case RegNtPreCreateKeyEx:
case RegNtPostCreateKeyEx:
{
KPH_REG_COPY_PARAMETER(CreateKey, CompleteName);
KPH_REG_COPY_PARAMETER(CreateKey, RootObject);
KPH_REG_COPY_PARAMETER(CreateKey, ObjectType);
KPH_REG_COPY_PARAMETER(CreateKey, Options);
KPH_REG_COPY_PARAMETER(CreateKey, Class);
KPH_REG_COPY_PARAMETER(CreateKey, SecurityDescriptor);
KPH_REG_COPY_PARAMETER(CreateKey, SecurityQualityOfService);
KPH_REG_COPY_PARAMETER(CreateKey, DesiredAccess);
KPH_REG_COPY_PARAMETER(CreateKey, GrantedAccess);
KPH_REG_COPY_PARAMETER(CreateKey, Disposition);
KPH_REG_COPY_PARAMETER(CreateKey, RemainingName);
KPH_REG_COPY_PARAMETER(CreateKey, Wow64Flags);
KPH_REG_COPY_PARAMETER(CreateKey, Attributes);
KPH_REG_COPY_PARAMETER(CreateKey, CheckAccessMode);
break;
}
case RegNtPreOpenKeyEx:
case RegNtPostOpenKeyEx:
{
KPH_REG_COPY_PARAMETER(OpenKey, CompleteName);
KPH_REG_COPY_PARAMETER(OpenKey, RootObject);
KPH_REG_COPY_PARAMETER(OpenKey, ObjectType);
KPH_REG_COPY_PARAMETER(OpenKey, Options);
KPH_REG_COPY_PARAMETER(OpenKey, Class);
KPH_REG_COPY_PARAMETER(OpenKey, SecurityDescriptor);
KPH_REG_COPY_PARAMETER(OpenKey, SecurityQualityOfService);
KPH_REG_COPY_PARAMETER(OpenKey, DesiredAccess);
KPH_REG_COPY_PARAMETER(OpenKey, GrantedAccess);
KPH_REG_COPY_PARAMETER(OpenKey, Disposition);
KPH_REG_COPY_PARAMETER(OpenKey, RemainingName);
KPH_REG_COPY_PARAMETER(OpenKey, Wow64Flags);
KPH_REG_COPY_PARAMETER(OpenKey, Attributes);
KPH_REG_COPY_PARAMETER(OpenKey, CheckAccessMode);
break;
}
case RegNtPreFlushKey:
case RegNtPostFlushKey:
{
break;
}
case RegNtPreLoadKey:
case RegNtPostLoadKey:
{
Message->Kernel.Reg.Parameters.LoadKey.RootObject = PreInfo->LoadKey.Object;
KPH_REG_COPY_PARAMETER(LoadKey, KeyName);
KPH_REG_COPY_PARAMETER(LoadKey, SourceFile);
KPH_REG_COPY_PARAMETER(LoadKey, Flags);
KPH_REG_COPY_PARAMETER(LoadKey, TrustClassObject);
KPH_REG_COPY_PARAMETER(LoadKey, UserEvent);
KPH_REG_COPY_PARAMETER(LoadKey, DesiredAccess);
KPH_REG_COPY_PARAMETER(LoadKey, RootHandle);
KPH_REG_COPY_PARAMETER(LoadKey, FileAccessToken);
break;
}
case RegNtPreUnLoadKey:
case RegNtPostUnLoadKey:
{
KPH_REG_COPY_PARAMETER(UnLoadKey, UserEvent);
break;
}
case RegNtPreQueryKeySecurity:
case RegNtPostQueryKeySecurity:
{
KPH_REG_COPY_PARAMETER(QueryKeySecurity, SecurityInformation);
KPH_REG_COPY_PARAMETER(QueryKeySecurity, SecurityDescriptor);
KPH_REG_COPY_PARAMETER(QueryKeySecurity, Length);
break;
}
case RegNtPreSetKeySecurity:
case RegNtPostSetKeySecurity:
{
KPH_REG_COPY_PARAMETER(SetKeySecurity, SecurityInformation);
KPH_REG_COPY_PARAMETER(SetKeySecurity, SecurityDescriptor);
break;
}
case RegNtPreRestoreKey:
case RegNtPostRestoreKey:
{
KPH_REG_COPY_PARAMETER(RestoreKey, FileHandle);
KPH_REG_COPY_PARAMETER(RestoreKey, Flags);
break;
}
case RegNtPreSaveKey:
case RegNtPostSaveKey:
{
KPH_REG_COPY_PARAMETER(SaveKey, FileHandle);
KPH_REG_COPY_PARAMETER(SaveKey, Format);
break;
}
case RegNtPreReplaceKey:
case RegNtPostReplaceKey:
{
KPH_REG_COPY_PARAMETER(ReplaceKey, OldFileName);
KPH_REG_COPY_PARAMETER(ReplaceKey, NewFileName);
break;
}
case RegNtPreQueryKeyName:
case RegNtPostQueryKeyName:
{
KPH_REG_COPY_PARAMETER(QueryKeyName, ObjectNameInfo);
KPH_REG_COPY_PARAMETER(QueryKeyName, Length);
KPH_REG_COPY_PARAMETER(QueryKeyName, ReturnLength);
break;
}
case RegNtPreSaveMergedKey:
case RegNtPostSaveMergedKey:
{
KPH_REG_COPY_PARAMETER(SaveMergedKey, FileHandle);
KPH_REG_COPY_PARAMETER(SaveMergedKey, HighKeyObject);
KPH_REG_COPY_PARAMETER(SaveMergedKey, LowKeyObject);
break;
}
DEFAULT_UNREACHABLE;
}
}
/**
* \brief Copies the object name information into a message. And returns other
* associated information for the object.
*
* \param[in,out] Message The message to populate.
* \param[in] FieldId The object name field ID to populate. Optional, when the
* identifier is InvalidKphMsgField the object name is not copied.
* \param[in] InputObject The object to copy the information from.
* \param[out] Object Receives a copy of the input object pointer.
* \param[out] ObjectId Receives the object ID for the object.
* \param[out] Transaction Receives the transaction for the object, if any.
*/
_IRQL_requires_max_(APC_LEVEL)
VOID KphpRegCopyObjectInfo(
_Inout_ PKPH_MESSAGE Message,
_In_ KPH_MESSAGE_FIELD_ID FieldId,
_In_ PVOID InputObject,
_Out_opt_ PVOID* Object,
_Out_opt_ PULONG_PTR ObjectId,
_Out_opt_ PVOID* Transaction
)
{
NTSTATUS status;
PUNICODE_STRING objectName;
PUNICODE_STRING* objectNamePointer;
KPH_PAGED_CODE();
objectName = NULL;
if (Object)
{
*Object = InputObject;
}
if (ObjectId)
{
*ObjectId = 0;
}
if (Transaction)
{
*Transaction = CmGetBoundTransaction(&KphpCmCookie, InputObject);
}
if (FieldId != InvalidKphMsgField)
{
objectNamePointer = &objectName;
}
else
{
objectNamePointer = NULL;
}
status = CmCallbackGetKeyObjectIDEx(&KphpCmCookie,
InputObject,
ObjectId,
objectNamePointer,
0);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
INFORMER,
"CmCallbackGetKeyObjectIDEx failed: %!STATUS!",
status);
if (ObjectId)
{
*ObjectId = 0;
}
objectName = NULL;
goto Exit;
}
if (FieldId != InvalidKphMsgField)
{
status = KphMsgDynAddUnicodeString(Message, FieldId, objectName);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
INFORMER,
"KphMsgDynAddUnicodeString failed: %!STATUS!",
status);
}
}
Exit:
if (objectName)
{
CmCallbackReleaseKeyObjectIDEx(objectName);
}
}
/**
* \brief Fills a message with information about the specified registry object.
*
* \param[in,out] Message The message to populate.
* \param[in] Object The object to copy the information from.
* \param[in] IncludeObjectName If TRUE the object name is included in the
* message, when FALSE the object name is not copied into the message.
*/
_IRQL_requires_max_(APC_LEVEL)
VOID KphpRegFillObjectInfo(
_Inout_ PKPH_MESSAGE Message,
_In_ PVOID Object,
_In_ BOOLEAN IncludeObjectName
)
{
KPH_MESSAGE_FIELD_ID fieldId;
KPH_PAGED_CODE();
if (IncludeObjectName)
{
fieldId = KphMsgFieldObjectName;
}
else
{
fieldId = InvalidKphMsgField;
}
KphpRegCopyObjectInfo(Message,
fieldId,
Object,
&Message->Kernel.Reg.Object,
&Message->Kernel.Reg.ObjectId,
&Message->Kernel.Reg.Transaction);
}
/**
* \brief Creates a registry object name from a root object and relative name.
*
* \param[in] RootObject The root registry object.
* \param[in] RelativeName The relative name from the root object.
* \param[out] ObjectName Receives a pointer to the allocated object name.
*
* \return Successful or errant status.
*/
_IRQL_requires_max_(APC_LEVEL)
_Must_inspect_result_
NTSTATUS KphpRegMakeObjectName(
_In_ PVOID RootObject,
_In_ PUNICODE_STRING RelativeName,
_Outptr_allocatesMem_ PUNICODE_STRING* ObjectName
)
{
NTSTATUS status;
PUNICODE_STRING rootName;
PUNICODE_STRING objectName;
BOOLEAN needsSeparator;
ULONG length;
KPH_PAGED_CODE();
*ObjectName = NULL;
objectName = NULL;
status = CmCallbackGetKeyObjectIDEx(&KphpCmCookie,
RootObject,
NULL,
&rootName,
0);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
INFORMER,
"CmCallbackGetKeyObjectIDEx failed: %!STATUS!",
status);
rootName = NULL;
goto Exit;
}
length = (rootName->Length + RelativeName->Length);
if ((rootName->Length >= sizeof(WCHAR)) &&
(rootName->Buffer[(rootName->Length / sizeof(WCHAR)) - 1] != OBJ_NAME_PATH_SEPARATOR) &&
(RelativeName->Length >= sizeof(WCHAR)) &&
(RelativeName->Buffer[0] != OBJ_NAME_PATH_SEPARATOR))
{
needsSeparator = TRUE;
length += sizeof(WCHAR);
}
else
{
needsSeparator = FALSE;
}
if (length > UNICODE_STRING_MAX_BYTES)
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
INFORMER,
"Object name too long: %lu",
length);
status = STATUS_INTEGER_OVERFLOW;
goto Exit;
}
objectName = KphAllocatePaged((length + sizeof(UNICODE_STRING)),
KPH_TAG_REG_OBJECT_NAME);
if (!objectName)
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
INFORMER,
"Failed to allocate object name.");
status = STATUS_INSUFFICIENT_RESOURCES;
goto Exit;
}
objectName->Length = 0;
objectName->MaximumLength = (USHORT)length;
objectName->Buffer = Add2Ptr(objectName, sizeof(UNICODE_STRING));
RtlAppendUnicodeStringToString(objectName, rootName);
if (needsSeparator)
{
RtlAppendUnicodeToString(objectName, L"\\");
}
RtlAppendUnicodeStringToString(objectName, RelativeName);
*ObjectName = objectName;
objectName = NULL;
Exit:
if (rootName)
{
CmCallbackReleaseKeyObjectIDEx(rootName);
}
if (objectName)
{
KphFree(objectName, KPH_TAG_REG_OBJECT_NAME);
}
return status;
}
/**
* \brief Fills a message with information about a creation or open operation.
*
* \param[in,out] Message The message to populate.
* \param[in] CreateInfo The creation or open information.
* \param[in] IncludeObjectName If TRUE the object name is included in the
* message, when FALSE the object name is not copied into the message.
*/
_IRQL_requires_max_(APC_LEVEL)
VOID KphpRegFillCreateKeyObjectInfo(
_Inout_ PKPH_MESSAGE Message,
_In_ PREG_CREATE_KEY_INFORMATION_V1 CreateInfo,
_In_ BOOLEAN IncludeObjectName
)
{
NTSTATUS status;
PUNICODE_STRING objectName;
KPH_PAGED_CODE();
Message->Kernel.Reg.Transaction = CreateInfo->Transaction;
if (!IncludeObjectName)
{
return;
}
if ((CreateInfo->CompleteName->Length >= sizeof(WCHAR)) &&
(CreateInfo->CompleteName->Buffer[0] == OBJ_NAME_PATH_SEPARATOR))
{
objectName = CreateInfo->CompleteName;
}
else
{
status = KphpRegMakeObjectName(CreateInfo->RootObject,
CreateInfo->CompleteName,
&objectName);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
INFORMER,
"KphpRegMakeObjectName failed: %!STATUS!",
status);
goto Exit;
}
}
status = KphMsgDynAddUnicodeString(Message,
KphMsgFieldObjectName,
objectName);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
INFORMER,
"KphMsgDynAddUnicodeString failed: %!STATUS!",
status);
}
Exit:
if (objectName && (objectName != CreateInfo->CompleteName))
{
KphFree(objectName, KPH_TAG_REG_OBJECT_NAME);
}
}
/**
* \brief Fills a message with information about a load key operation.
*
* \param[in,out] Message The message to populate.
* \param[in] LoadInfo The load key information.
* \param[in] IncludeObjectName If TRUE the object name is included in the
* message, when FALSE the object name is not copied into the message.
*/
_IRQL_requires_max_(APC_LEVEL)
VOID KphpRegFillLoadKeyObjectInfo(
_Inout_ PKPH_MESSAGE Message,
_In_ PREG_LOAD_KEY_INFORMATION_V2 LoadInfo,
_In_ BOOLEAN IncludeObjectName
)
{
NTSTATUS status;
PUNICODE_STRING objectName;
KPH_PAGED_CODE();
if (!IncludeObjectName)
{
return;
}
if (LoadInfo->Object)
{
status = KphpRegMakeObjectName(LoadInfo->Object,
LoadInfo->KeyName,
&objectName);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
INFORMER,
"KphpRegMakeObjectName failed: %!STATUS!",
status);
goto Exit;
}
}
else
{
objectName = LoadInfo->KeyName;
}
status = KphMsgDynAddUnicodeString(Message,
KphMsgFieldObjectName,
objectName);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
INFORMER,
"KphMsgDynAddUnicodeString failed: %!STATUS!",
status);
}
Exit:
if (objectName && (objectName != LoadInfo->KeyName))
{
KphFree(objectName, KPH_TAG_REG_OBJECT_NAME);
}
}
/**
* \brief Copies a Unicode string into a message
*
* \param[in] Message The message to populate.
* \param[in] FieldId The field ID for the Unicode string.
* \param[in] String The string to populate into the message.
* \param[in] Default Default string to use input string length is zero.
*/
_IRQL_requires_max_(APC_LEVEL)
VOID KphpRegCopyUnicodeStringWithDefault(
_Inout_ PKPH_MESSAGE Message,
_In_ KPH_MESSAGE_FIELD_ID FieldId,
_In_opt_ PUNICODE_STRING String,
_In_opt_ PCUNICODE_STRING Default
)
{
NTSTATUS status;
UNICODE_STRING string;
KPH_PAGED_CODE();
if (!String)
{
return;
}
__try
{
string.Buffer = String->Buffer;
string.Length = String->Length;
string.MaximumLength = string.Length;
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
INFORMER,
"Exception accessing string: %!STATUS!",
GetExceptionCode());
return;
}
if (!string.Length)
{
if (Default)
{
status = KphMsgDynAddUnicodeString(Message, FieldId, Default);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
INFORMER,
"KphMsgDynAddUnicodeString failed: %!STATUS!",
status);
}
}
return;
}
__try
{
status = KphMsgDynAddUnicodeString(Message, FieldId, &string);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
INFORMER,
"KphMsgDynAddUnicodeString failed: %!STATUS!",
status);
}
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
INFORMER,
"Exception copying string: %!STATUS!",
GetExceptionCode());
//
// N.B. The KphMsgDynAdd* routines will first reserve a table
// entry then copy data into the buffer. If we get here it means
// the control flow was arrested after the reservation. Since we
// can't guarantee it was all copied successfully clear the last
// entry that was reserved.
//
KphMsgDynClearLast(Message);
}
}
/**
* \brief Copies a Unicode string into a message.
*
* \param[in,out] Message The message to populate.
* \param[in] FieldId The field ID for the Unicode string.
* \param[in] String The string to populate into the message.
*/
_IRQL_requires_max_(APC_LEVEL)
VOID KphpRegCopyUnicodeString(
_Inout_ PKPH_MESSAGE Message,
_In_ KPH_MESSAGE_FIELD_ID FieldId,
_In_opt_ PUNICODE_STRING String
)
{
KPH_PAGED_CODE();
KphpRegCopyUnicodeStringWithDefault(Message, FieldId, String, NULL);
}
/**
* \brief Copies a registry value name into a message.
*
* \param[in,out] Message The message to populate.
* \param[in] ValueName The value name to populate into the message.
*/
_IRQL_requires_max_(APC_LEVEL)
VOID KphpRegCopyValueName(
_Inout_ PKPH_MESSAGE Message,
_In_opt_ PUNICODE_STRING ValueName
)
{
KPH_PAGED_CODE();
KphpRegCopyUnicodeStringWithDefault(Message,
KphMsgFieldValueName,
ValueName,
&KphpCmDefaultValueName);
}
/**
* \brief Copies multiple value names into a message.
*
* \param[in,out] Message The message to populate.
* \param[in] ValueEntries The value entries to populate into the message.
* \param[in] EntryCount The number of value entries.
*/
_IRQL_requires_max_(APC_LEVEL)
VOID KphpRegCopyMultipleValueNames(
_Inout_ PKPH_MESSAGE Message,
_In_opt_ PKEY_VALUE_ENTRY ValueEntries,
_In_ ULONG EntryCount
)
{
NTSTATUS status;
ULONG length;
PUNICODE_STRING valueNames;
ULONG remaining;
KPH_PAGED_CODE();
if (!ValueEntries || !EntryCount)
{
return;
}
valueNames = NULL;
//
// Try to capture the values names as a sequence of null terminated strings.
//
length = sizeof(WCHAR);
__try
{
for (ULONG i = 0; i < EntryCount; i++)
{
PCUNICODE_STRING entry;
ULONG valueLength;
entry = ValueEntries[i].ValueName;
valueLength = entry->Length;
if (valueLength)
{
valueLength += sizeof(WCHAR);
}
else
{
valueLength = (KphpCmDefaultValueName.Length + sizeof(WCHAR));
}
status = RtlULongAdd(length, valueLength, &length);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
INFORMER,
"RtlULongAdd failed: %!STATUS!",
status);
goto Exit;
}
}
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
INFORMER,
"Exception accessing value entries: %!STATUS!",
GetExceptionCode());
goto Exit;
}
if (length > UNICODE_STRING_MAX_BYTES)
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
INFORMER,
"Value names too long: %lu",
length);
goto Exit;
}
valueNames = KphAllocatePaged((length + sizeof(UNICODE_STRING)),
KPH_TAG_REG_VALUE_NAMES);
if (!valueNames)
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
INFORMER,
"Failed to allocate value names.");
goto Exit;
}
valueNames->Length = 0;
valueNames->MaximumLength = (USHORT)length;
valueNames->Buffer = Add2Ptr(valueNames, sizeof(UNICODE_STRING));
__try
{
for (ULONG i = 0; i < EntryCount; i++)
{
PCUNICODE_STRING entry;
entry = ValueEntries[i].ValueName;
if (!entry->Length)
{
entry = &KphpCmDefaultValueName;
}
status = RtlAppendUnicodeStringToString(valueNames, entry);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
INFORMER,
"RtlAppendUnicodeStringToString failed: %!STATUS!",
status);
goto Exit;
}
remaining = (valueNames->MaximumLength - valueNames->Length);
if (remaining < sizeof(WCHAR))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
INFORMER,
"Exhausted value names buffer.");
goto Exit;
}
valueNames->Buffer[valueNames->Length / sizeof(WCHAR)] = UNICODE_NULL;
valueNames->Length += sizeof(WCHAR);
}
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
INFORMER,
"Exception accessing value entries: %!STATUS!",
GetExceptionCode());
goto Exit;
}
remaining = (valueNames->MaximumLength - valueNames->Length);
if (remaining < sizeof(WCHAR))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
INFORMER,
"Exhausted value names buffer.");
goto Exit;
}
valueNames->Buffer[valueNames->Length / sizeof(WCHAR)] = UNICODE_NULL;
valueNames->Length += sizeof(WCHAR);
status = KphMsgDynAddUnicodeString(Message,
KphMsgFieldMultipleValueNames,
valueNames);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
INFORMER,
"KphMsgDynAddUnicodeString failed: %!STATUS!",
status);
}
Exit:
if (valueNames)
{
KphFree(valueNames, KPH_TAG_REG_VALUE_NAMES);
}
}
/**
* \brief Copies a buffer into a message.
*
* \param[in,out] Message The message to populate.
* \param[in] FieldId The field ID for the buffer.
* \param[in] Buffer The buffer to copy.
* \param[in] Length The length of the buffer.
*/
_IRQL_requires_max_(APC_LEVEL)
VOID KphpRegCopyBuffer(
_Inout_ PKPH_MESSAGE Message,
_In_ KPH_MESSAGE_FIELD_ID FieldId,
_In_opt_ PVOID Buffer,
_In_ ULONG Length
)
{
NTSTATUS status;
USHORT remaining;
KPHM_SIZED_BUFFER sizedBuffer;
KPH_PAGED_CODE();
if (!Buffer)
{
return;
}
remaining = KphMsgDynRemaining(Message);
if (remaining < Length)
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
INFORMER,
"Buffer too large for message, truncating %lu -> %lu",
Length,
remaining);
Length = remaining;
}
if (!Length)
{
return;
}
sizedBuffer.Buffer = Buffer;
sizedBuffer.Size = (USHORT)Length;
__try
{
status = KphMsgDynAddSizedBuffer(Message, FieldId, &sizedBuffer);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
INFORMER,
"KphMsgDynAddSizedBuffer failed: %!STATUS!",
status);
}
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
INFORMER,
"Exception copying buffer: %!STATUS!",
GetExceptionCode());
//
// N.B. The KphMsgDynAdd* routines will first reserve a table
// entry then copy data into the buffer. If we get here it means
// the control flow was arrested after the reservation. Since we
// can't guarantee it was all copied successfully clear the last
// entry that was reserved.
//
KphMsgDynClearLast(Message);
}
}
/**
* \brief Copies an object name into a message.
*
* \param[in,out] Message The message to populate.
* \param[in] FieldId The field ID for the object name.
* \param[in] Object The object to copy the name from.
*/
_IRQL_requires_max_(APC_LEVEL)
VOID KphpRegCopyObjectName(
_Inout_ PKPH_MESSAGE Message,
_In_ KPH_MESSAGE_FIELD_ID FieldId,
_In_opt_ PVOID Object
)
{
NTSTATUS status;
PUNICODE_STRING objectName;
PUNICODE_STRING keyName;
POBJECT_NAME_INFORMATION nameInfo;
ULONG length;
KPH_PAGED_CODE();
if (!Object)
{
return;
}
keyName = NULL;
nameInfo = NULL;
if (ObGetObjectType(Object) == *CmKeyObjectType)
{
status = CmCallbackGetKeyObjectIDEx(&KphpCmCookie,
Object,
NULL,
&keyName,
0);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
INFORMER,
"CmCallbackGetKeyObjectIDEx failed: %!STATUS!",
status);
keyName = NULL;
goto Exit;
}
objectName = keyName;
}
else
{
status = KphQueryNameObject(Object, NULL, 0, &length);
if (status != STATUS_BUFFER_TOO_SMALL)
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
INFORMER,
"KphQueryNameObject: %!STATUS!",
status);
goto Exit;
}
nameInfo = KphAllocatePaged(length, KPH_TAG_REG_OBJECT_NAME);
if (!nameInfo)
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
INFORMER,
"Failed to allocate object name.");
goto Exit;
}
status = KphQueryNameObject(Object, nameInfo, length, &length);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
INFORMER,
"KphQueryNameObject failed: %!STATUS!",
status);
goto Exit;
}
objectName = &nameInfo->Name;
}
status = KphMsgDynAddUnicodeString(Message, FieldId, objectName);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
INFORMER,
"KphMsgDynAddUnicodeString failed: %!STATUS!",
status);
}
Exit:
if (nameInfo)
{
KphFree(nameInfo, KPH_TAG_REG_OBJECT_NAME);
}
if (keyName)
{
CmCallbackReleaseKeyObjectIDEx(keyName);
}
}
/**
* \brief Copies a handle name into a message.
*
* \param[in,out] Message The message to populate.
* \param[in] FieldId The field ID for the handle name.
* \param[in] Handle The handle to copy the name from.
*/
_IRQL_requires_max_(APC_LEVEL)
VOID KphpRegCopyHandleName(
_Inout_ PKPH_MESSAGE Message,
_In_ KPH_MESSAGE_FIELD_ID FieldId,
_In_ HANDLE Handle
)
{
NTSTATUS status;
KAPC_STATE apcState;
PVOID object;
KPH_PAGED_CODE();
if (!Handle)
{
return;
}
if (IsKernelHandle(Handle) && !IsPseudoHandle(Handle))
{
KeStackAttachProcess(PsInitialSystemProcess, &apcState);
}
NT_ASSERT(Handle);
#pragma prefast(suppress : 6387)
status = ObReferenceObjectByHandle(Handle,
0,
NULL,
KernelMode,
&object,
NULL);
if (IsKernelHandle(Handle) && !IsPseudoHandle(Handle))
{
KeUnstackDetachProcess(&apcState);
}
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
INFORMER,
"ObReferenceObjectByHandle failed: %!STATUS!",
status);
return;
}
KphpRegCopyObjectName(Message, FieldId, object);
ObDereferenceObject(object);
}
/**
* \brief Fills a message with post operation information.
*
* \param[in,out] Message The message to populate.
* \param[in] RegClass The registry operation class.
* \param[in] PostInfo The post operation information.
* \param[in] Context The call context.
*/
_IRQL_requires_max_(APC_LEVEL)
VOID KphpRegFillPostOpMessage(
_Inout_ PKPH_MESSAGE Message,
_In_ REG_NOTIFY_CLASS RegClass,
_In_ PKPH_REG_POST_INFORMATION PostInfo,
_In_ PKPH_REG_CALL_CONTEXT Context
)
{
NTSTATUS status;
PKPH_REG_PRE_INFORMATION preInfo;
BOOLEAN enableObjectNames;
BOOLEAN enableValueNames;
KPH_PAGED_CODE();
enableObjectNames = Context->Options.EnablePostObjectNames;
enableValueNames = Context->Options.EnablePostValueNames;
Message->Kernel.Reg.PostOperation = TRUE;
Message->Kernel.Reg.Post.PreSequence = Context->PreSequence;
Message->Kernel.Reg.Post.PreTimeStamp = Context->PreTimeStamp;
preInfo = PostInfo->Common.PreInformation;
Message->Kernel.Reg.Post.Status = PostInfo->Common.Status;
KphpRegFillCommonMessage(Message, RegClass, preInfo);
#define KPH_REG_COPY_OUT_PARAM(n, v) \
if (preInfo->n.v) \
{ \
__try \
{ \
Message->Kernel.Reg.Post.n.v = *preInfo->n.v; \
} \
__except (EXCEPTION_EXECUTE_HANDLER) \
{ \
Message->Kernel.Reg.Post.n.v = 0; \
} \
}
switch (RegClass)
{
case RegNtPostSetValueKey:
{
KphpRegFillObjectInfo(Message, PostInfo->Object, enableObjectNames);
if (enableValueNames)
{
KphpRegCopyValueName(Message, preInfo->SetValueKey.ValueName);
}
break;
}
case RegNtPostDeleteValueKey:
{
KphpRegFillObjectInfo(Message, PostInfo->Object, enableObjectNames);
if (enableValueNames)
{
KphpRegCopyValueName(Message, preInfo->DeleteValueKey.ValueName);
}
break;
}
case RegNtPostEnumerateKey:
{
KphpRegFillObjectInfo(Message, PostInfo->Object, enableObjectNames);
if (!NT_SUCCESS(Message->Kernel.Reg.Post.Status))
{
break;
}
KPH_REG_COPY_OUT_PARAM(EnumerateKey, ResultLength);
KphpRegCopyBuffer(Message,
KphMsgFieldInformationBuffer,
preInfo->EnumerateKey.KeyInformation,
Message->Kernel.Reg.Post.EnumerateKey.ResultLength);
break;
}
case RegNtPostEnumerateValueKey:
{
KphpRegFillObjectInfo(Message, PostInfo->Object, enableObjectNames);
if (!NT_SUCCESS(PostInfo->Common.Status))
{
break;
}
KPH_REG_COPY_OUT_PARAM(EnumerateValueKey, ResultLength);
KphpRegCopyBuffer(Message,
KphMsgFieldInformationBuffer,
preInfo->EnumerateValueKey.KeyValueInformation,
Message->Kernel.Reg.Post.EnumerateValueKey.ResultLength);
break;
}
case RegNtPostQueryKey:
{
KphpRegFillObjectInfo(Message, PostInfo->Object, enableObjectNames);
if (!NT_SUCCESS(PostInfo->Common.Status))
{
break;
}
KPH_REG_COPY_OUT_PARAM(QueryKey, ResultLength);
KphpRegCopyBuffer(Message,
KphMsgFieldInformationBuffer,
preInfo->QueryKey.KeyInformation,
Message->Kernel.Reg.Post.QueryKey.ResultLength);
break;
}
case RegNtPostQueryValueKey:
{
KphpRegFillObjectInfo(Message, PostInfo->Object, enableObjectNames);
if (enableValueNames)
{
KphpRegCopyValueName(Message, preInfo->QueryValueKey.ValueName);
}
if (!NT_SUCCESS(PostInfo->Common.Status))
{
break;
}
KPH_REG_COPY_OUT_PARAM(QueryValueKey, ResultLength);
if (Context->Options.EnableValueBuffers)
{
KphpRegCopyBuffer(Message,
KphMsgFieldValueBuffer,
preInfo->QueryValueKey.KeyValueInformation,
Message->Kernel.Reg.Post.QueryValueKey.ResultLength);
}
break;
}
case RegNtPostQueryMultipleValueKey:
{
KphpRegFillObjectInfo(Message, PostInfo->Object, enableObjectNames);
if (enableValueNames)
{
KphpRegCopyMultipleValueNames(Message,
preInfo->QueryMultipleValueKey.ValueEntries,
preInfo->QueryMultipleValueKey.EntryCount);
}
KPH_REG_COPY_OUT_PARAM(QueryMultipleValueKey, RequiredBufferLength);
if (!NT_SUCCESS(PostInfo->Common.Status))
{
break;
}
KPH_REG_COPY_OUT_PARAM(QueryMultipleValueKey, BufferLength);
if (Context->Options.EnableValueBuffers)
{
ULONG entriesLength;
if (!NT_SUCCESS(RtlULongMult(preInfo->QueryMultipleValueKey.EntryCount,
sizeof(KEY_VALUE_ENTRY),
&entriesLength)))
{
break;
}
KphpRegCopyBuffer(Message,
KphMsgFieldMultipleValueEntries,
preInfo->QueryMultipleValueKey.ValueEntries,
entriesLength);
KphpRegCopyBuffer(Message,
KphMsgFieldValueBuffer,
preInfo->QueryMultipleValueKey.ValueBuffer,
Message->Kernel.Reg.Post.QueryMultipleValueKey.BufferLength);
}
break;
}
case RegNtPostKeyHandleClose:
{
Message->Kernel.Reg.Object = preInfo->Object;
Message->Kernel.Reg.ObjectId = Context->ObjectId;
Message->Kernel.Reg.Transaction = Context->Transaction;
if (Context->ObjectName)
{
status = KphMsgDynAddUnicodeString(Message,
KphMsgFieldObjectName,
Context->ObjectName);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
INFORMER,
"KphMsgDynAddUnicodeString failed: %!STATUS!",
status);
}
}
break;
}
case RegNtPostCreateKeyEx:
{
if (PostInfo->Common.Status == STATUS_SUCCESS)
{
KphpRegFillObjectInfo(Message, PostInfo->Common.Object, enableObjectNames);
}
else
{
KphpRegFillCreateKeyObjectInfo(Message, &preInfo->CreateKey, enableObjectNames);
}
if (NT_SUCCESS(PostInfo->Common.Status))
{
KPH_REG_COPY_OUT_PARAM(CreateKey, Disposition);
}
break;
}
case RegNtPostOpenKeyEx:
{
if (PostInfo->Common.Status == STATUS_SUCCESS)
{
KphpRegFillObjectInfo(Message, PostInfo->Common.Object, enableObjectNames);
}
else
{
KphpRegFillCreateKeyObjectInfo(Message, &preInfo->OpenKey, enableObjectNames);
}
if (NT_SUCCESS(PostInfo->Common.Status))
{
KPH_REG_COPY_OUT_PARAM(OpenKey, Disposition);
}
break;
}
case RegNtPostLoadKey:
{
KphpRegFillLoadKeyObjectInfo(Message, &preInfo->LoadKey, enableObjectNames);
if (enableObjectNames)
{
KphpRegCopyUnicodeString(Message,
KphMsgFieldFileName,
preInfo->LoadKey.SourceFile);
}
if (NT_SUCCESS(PostInfo->Common.Status))
{
KPH_REG_COPY_OUT_PARAM(LoadKey, RootHandle);
}
break;
}
case RegNtPostQueryKeySecurity:
{
KphpRegFillObjectInfo(Message, PostInfo->Object, enableObjectNames);
if (NT_SUCCESS(PostInfo->Common.Status))
{
KPH_REG_COPY_OUT_PARAM(QueryKeySecurity, Length);
}
break;
}
case RegNtPostRestoreKey:
{
KphpRegFillObjectInfo(Message, PostInfo->Object, TRUE);
if (enableObjectNames)
{
KphpRegCopyHandleName(Message,
KphMsgFieldFileName,
preInfo->RestoreKey.FileHandle);
}
break;
}
case RegNtPostSaveKey:
{
KphpRegFillObjectInfo(Message, PostInfo->Object, TRUE);
if (enableObjectNames)
{
KphpRegCopyHandleName(Message,
KphMsgFieldFileName,
preInfo->SaveKey.FileHandle);
}
break;
}
case RegNtPostReplaceKey:
{
KphpRegFillObjectInfo(Message, PostInfo->Object, enableObjectNames);
if (enableObjectNames)
{
KphpRegCopyUnicodeString(Message,
KphMsgFieldFileName,
preInfo->ReplaceKey.NewFileName);
KphpRegCopyUnicodeString(Message,
KphMsgFieldDestinationFileName,
preInfo->ReplaceKey.OldFileName);
}
break;
}
case RegNtPostQueryKeyName:
{
KphpRegFillObjectInfo(Message, PostInfo->Object, enableObjectNames);
if (NT_SUCCESS(PostInfo->Common.Status))
{
KPH_REG_COPY_OUT_PARAM(QueryKeyName, ReturnLength);
}
break;
}
case RegNtPostSaveMergedKey:
{
KPH_MESSAGE_FIELD_ID fieldId;
KphpRegFillObjectInfo(Message, preInfo->SaveMergedKey.HighKeyObject, enableObjectNames);
if (enableObjectNames)
{
fieldId = KphMsgFieldOtherObjectName;
}
else
{
fieldId = InvalidKphMsgField;
}
KphpRegCopyObjectInfo(Message,
fieldId,
preInfo->SaveMergedKey.LowKeyObject,
NULL,
&Message->Kernel.Reg.Post.SaveMergedKey.LowKeyObjectId,
&Message->Kernel.Reg.Post.SaveMergedKey.LowKeyTransaction);
if (enableObjectNames)
{
KphpRegCopyHandleName(Message,
KphMsgFieldFileName,
preInfo->SaveMergedKey.FileHandle);
}
break;
}
default:
{
KphpRegFillObjectInfo(Message, PostInfo->Object, enableObjectNames);
break;
}
}
}
/**
* \brief Fills a message with pre operation information.
*
* \param[in,out] Message The message to populate.
* \param[in] RegClass The registry operation class.
* \param[in] PreInfo The pre operation information.
* \param[in] Options Registry options to use.
*/
_IRQL_requires_max_(APC_LEVEL)
VOID KphpRegFillPreOpMessage(
_Inout_ PKPH_MESSAGE Message,
_In_ REG_NOTIFY_CLASS RegClass,
_In_ PKPH_REG_PRE_INFORMATION PreInfo,
_In_ PKPH_REG_OPTIONS Options
)
{
KPH_PAGED_CODE();
Message->Kernel.Reg.PostOperation = FALSE;
KphpRegFillCommonMessage(Message, RegClass, PreInfo);
#define KPH_REG_COPY_IN_PARAM(n, v) \
if (PreInfo->n.v) \
{ \
__try \
{ \
Message->Kernel.Reg.Pre.n.v = *PreInfo->n.v; \
} \
__except (EXCEPTION_EXECUTE_HANDLER) \
{ \
Message->Kernel.Reg.Pre.n.v = 0; \
} \
}
switch (RegClass)
{
case RegNtPreSetValueKey:
{
KphpRegFillObjectInfo(Message, PreInfo->Object, TRUE);
KphpRegCopyValueName(Message, PreInfo->SetValueKey.ValueName);
if (Options->EnableValueBuffers)
{
KphpRegCopyBuffer(Message,
KphMsgFieldValueBuffer,
PreInfo->SetValueKey.Data,
PreInfo->SetValueKey.DataSize);
}
break;
}
case RegNtPreDeleteValueKey:
{
KphpRegFillObjectInfo(Message, PreInfo->Object, TRUE);
KphpRegCopyValueName(Message, PreInfo->DeleteValueKey.ValueName);
break;
}
case RegNtPreSetInformationKey:
{
KphpRegFillObjectInfo(Message, PreInfo->Object, TRUE);
KphpRegCopyBuffer(Message,
KphMsgFieldInformationBuffer,
PreInfo->SetInformationKey.KeySetInformation,
PreInfo->SetInformationKey.KeySetInformationLength);
break;
}
case RegNtPreRenameKey:
{
KphpRegFillObjectInfo(Message, PreInfo->Object, TRUE);
KphpRegCopyUnicodeString(Message,
KphMsgFieldNewName,
PreInfo->RenameKey.NewName);
break;
}
case RegNtPreQueryValueKey:
{
KphpRegFillObjectInfo(Message, PreInfo->Object, TRUE);
KphpRegCopyValueName(Message, PreInfo->QueryValueKey.ValueName);
break;
}
case RegNtPreQueryMultipleValueKey:
{
KphpRegFillObjectInfo(Message, PreInfo->Object, TRUE);
KphpRegCopyMultipleValueNames(Message,
PreInfo->QueryMultipleValueKey.ValueEntries,
PreInfo->QueryMultipleValueKey.EntryCount);
KPH_REG_COPY_IN_PARAM(QueryMultipleValueKey, BufferLength);
break;
}
case RegNtPreCreateKeyEx:
{
KphpRegFillCreateKeyObjectInfo(Message, &PreInfo->CreateKey, TRUE);
KphpRegCopyUnicodeString(Message,
KphMsgFieldClass,
PreInfo->CreateKey.Class);
break;
}
case RegNtPreOpenKeyEx:
{
KphpRegFillCreateKeyObjectInfo(Message, &PreInfo->OpenKey, TRUE);
break;
}
case RegNtPreLoadKey:
{
KphpRegFillLoadKeyObjectInfo(Message, &PreInfo->LoadKey, TRUE);
KphpRegCopyUnicodeString(Message,
KphMsgFieldFileName,
PreInfo->LoadKey.SourceFile);
break;
}
case RegNtPreQueryKeySecurity:
{
KphpRegFillObjectInfo(Message, PreInfo->Object, TRUE);
KPH_REG_COPY_IN_PARAM(QueryKeySecurity, SecurityInformation);
KPH_REG_COPY_IN_PARAM(QueryKeySecurity, Length);
break;
}
case RegNtPreSetKeySecurity:
{
KphpRegFillObjectInfo(Message, PreInfo->Object, TRUE);
KPH_REG_COPY_IN_PARAM(SetKeySecurity, SecurityInformation);
break;
}
case RegNtPreRestoreKey:
{
KphpRegFillObjectInfo(Message, PreInfo->Object, TRUE);
KphpRegCopyHandleName(Message,
KphMsgFieldFileName,
PreInfo->RestoreKey.FileHandle);
break;
}
case RegNtPreSaveKey:
{
KphpRegFillObjectInfo(Message, PreInfo->Object, TRUE);
KphpRegCopyHandleName(Message,
KphMsgFieldFileName,
PreInfo->SaveKey.FileHandle);
break;
}
case RegNtPreReplaceKey:
{
KphpRegFillObjectInfo(Message, PreInfo->Object, TRUE);
KphpRegCopyUnicodeString(Message,
KphMsgFieldFileName,
PreInfo->ReplaceKey.NewFileName);
KphpRegCopyUnicodeString(Message,
KphMsgFieldDestinationFileName,
PreInfo->ReplaceKey.OldFileName);
break;
}
case RegNtPreSaveMergedKey:
{
KphpRegFillObjectInfo(Message, PreInfo->SaveMergedKey.HighKeyObject, TRUE);
KphpRegCopyObjectInfo(Message,
KphMsgFieldOtherObjectName,
PreInfo->SaveMergedKey.LowKeyObject,
NULL,
&Message->Kernel.Reg.Pre.SaveMergedKey.LowKeyObjectId,
&Message->Kernel.Reg.Pre.SaveMergedKey.LowKeyTransaction);
KphpRegCopyHandleName(Message,
KphMsgFieldFileName,
PreInfo->SaveMergedKey.FileHandle);
break;
}
default:
{
KphpRegFillObjectInfo(Message, PreInfo->Object, TRUE);
break;
}
}
}
/**
* \brief Sends a post operation message.
*
* \param[in] RegClass The registry operation class.
* \param[in] PostInfo The post operation information.
* \param[in] Sequence The registry sequence number.
* \param[in] Context The call context.
*/
_IRQL_requires_max_(APC_LEVEL)
VOID KphpRegPostOpSend(
_In_ REG_NOTIFY_CLASS RegClass,
_In_ PKPH_REG_POST_INFORMATION PostInfo,
_In_ ULONG64 Sequence,
_In_ PKPH_REG_CALL_CONTEXT Context
)
{
PKPH_MESSAGE msg;
KPH_PAGED_CODE();
msg = KphAllocateMessage();
if (!msg)
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
INFORMER,
"KphAllocateMessage failed");
return;
}
KphMsgInit(msg, KphpRegGetMessageId(RegClass));
msg->Kernel.Reg.Sequence = Sequence;
KphpRegFillPostOpMessage(msg, RegClass, PostInfo, Context);
if (Context->Options.EnableStackTraces)
{
KphCaptureStackInMessage(msg);
}
KphCommsSendMessageAsync(msg);
}
_IRQL_requires_max_(APC_LEVEL)
VOID KphpRegFreeCallContext(
_In_ PKPH_REG_CALL_CONTEXT Context
)
{
KPH_PAGED_CODE();
if (Context->ObjectName)
{
CmCallbackReleaseKeyObjectIDEx(Context->ObjectName);
}
KphFreeToPagedLookaside(&KphpCmCallContextLookaside, Context);
}
/**
* \brief Registry post operation callback.
*
* \param[in] RegClass The registry operation class.
* \param[in] PostInfo The post operation information.
* \param[in] Sequence The registry sequence number for the post operation.
*/
_IRQL_requires_max_(APC_LEVEL)
VOID KphpRegPostOp(
_In_ REG_NOTIFY_CLASS RegClass,
_In_ PKPH_REG_POST_INFORMATION PostInfo,
_In_ ULONG64 Sequence
)
{
PKPH_REG_CALL_CONTEXT context;
KPH_PAGED_CODE();
context = PostInfo->Common.CallContext;
if (context)
{
KphpRegPostOpSend(RegClass, PostInfo, Sequence, context);
KphpRegFreeCallContext(context);
}
}
/**
* \brief Sets the call context for a registry operation.
*
* \details This routine sets the call context in the pre operation information
* in preparation for the post operation to handle it.
*
* \param[in] RegClass The registry operation class.
* \param[in] PreInfo The pre operation information.
* \param[in] Options Registry options to use.
* \param[in] Sequence The registry sequence number for the pre operation.
* \param[in] TimeStamp The time stamp for the pre operation.
*/
_IRQL_requires_max_(APC_LEVEL)
_Must_inspect_result_
NTSTATUS KphpRegPreOpSetCallContext(
_In_ REG_NOTIFY_CLASS RegClass,
_In_ PKPH_REG_PRE_INFORMATION PreInfo,
_In_ PKPH_REG_OPTIONS Options,
_In_ ULONG64 Sequence,
_In_ PLARGE_INTEGER TimeStamp
)
{
NTSTATUS status;
PKPH_REG_CALL_CONTEXT context;
KPH_PAGED_CODE();
context = KphAllocateFromPagedLookaside(&KphpCmCallContextLookaside);
if (!context)
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
INFORMER,
"KphAllocateFromPagedLookaside failed");
status = STATUS_INSUFFICIENT_RESOURCES;
goto Exit;
}
context->PreSequence = Sequence;
context->PreTimeStamp = *TimeStamp;
context->Options.Flags = Options->Flags;
if (RegClass == RegNtPreKeyHandleClose)
{
PVOID object;
PUNICODE_STRING* objectNamePointer;
object = PreInfo->KeyHandleClose.Object;
context->Transaction = CmGetBoundTransaction(&KphpCmCookie, object);
if (Options->EnablePostObjectNames)
{
objectNamePointer = &context->ObjectName;
}
else
{
objectNamePointer = NULL;
}
status = CmCallbackGetKeyObjectIDEx(&KphpCmCookie,
object,
&context->ObjectId,
objectNamePointer,
0);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
INFORMER,
"CmCallbackGetKeyObjectIDEx failed: %!STATUS!",
status);
context->ObjectId = 0;
context->ObjectName = NULL;
}
}
status = STATUS_SUCCESS;
#define KPH_REG_SET_CALL_CONTEXT2(reg, name) \
case RegNtPre##reg: \
{ \
NT_ASSERT(!PreInfo->name.CallContext); \
PreInfo->name.CallContext = context; \
context = NULL; \
break; \
}
#define KPH_REG_SET_CALL_CONTEXT(name) KPH_REG_SET_CALL_CONTEXT2(name, name)
switch (RegClass)
{
KPH_REG_SET_CALL_CONTEXT(DeleteKey)
KPH_REG_SET_CALL_CONTEXT(SetValueKey)
KPH_REG_SET_CALL_CONTEXT(DeleteValueKey)
KPH_REG_SET_CALL_CONTEXT(SetInformationKey)
KPH_REG_SET_CALL_CONTEXT(RenameKey)
KPH_REG_SET_CALL_CONTEXT(EnumerateKey)
KPH_REG_SET_CALL_CONTEXT(EnumerateValueKey)
KPH_REG_SET_CALL_CONTEXT(QueryKey)
KPH_REG_SET_CALL_CONTEXT(QueryValueKey)
KPH_REG_SET_CALL_CONTEXT(QueryMultipleValueKey)
KPH_REG_SET_CALL_CONTEXT(KeyHandleClose)
KPH_REG_SET_CALL_CONTEXT2(CreateKeyEx, CreateKey)
KPH_REG_SET_CALL_CONTEXT2(OpenKeyEx, OpenKey)
KPH_REG_SET_CALL_CONTEXT(FlushKey)
KPH_REG_SET_CALL_CONTEXT(LoadKey)
KPH_REG_SET_CALL_CONTEXT(UnLoadKey)
KPH_REG_SET_CALL_CONTEXT(QueryKeySecurity)
KPH_REG_SET_CALL_CONTEXT(SetKeySecurity)
KPH_REG_SET_CALL_CONTEXT(RestoreKey)
KPH_REG_SET_CALL_CONTEXT(SaveKey)
KPH_REG_SET_CALL_CONTEXT(ReplaceKey)
KPH_REG_SET_CALL_CONTEXT(QueryKeyName)
KPH_REG_SET_CALL_CONTEXT(SaveMergedKey)
DEFAULT_UNREACHABLE;
}
Exit:
if (context)
{
KphFreeToPagedLookaside(&KphpCmCallContextLookaside, context);
}
return status;
}
/**
* \brief Sends a pre operation message.
*
* \param[in] RegClass The registry operation class.
* \param[in] PreInfo The pre operation information.
* \param[in] Options Registry options to use.
* \param[in] Sequence The registry sequence number for the pre operation.
* \param[out] TimeStamp Receives time stamp for the pre operation.
*/
_IRQL_requires_max_(APC_LEVEL)
VOID KphpRegPreOpSend(
_In_ REG_NOTIFY_CLASS RegClass,
_In_ PKPH_REG_PRE_INFORMATION PreInfo,
_In_ PKPH_REG_OPTIONS Options,
_In_ ULONG64 Sequence,
_Out_ PLARGE_INTEGER TimeStamp
)
{
PKPH_MESSAGE msg;
KPH_PAGED_CODE();
msg = KphAllocateMessage();
if (!msg)
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
INFORMER,
"KphAllocateMessage failed");
KeQuerySystemTime(TimeStamp);
return;
}
KphMsgInit(msg, KphpRegGetMessageId(RegClass));
*TimeStamp = msg->Header.TimeStamp;
msg->Kernel.Reg.Sequence = Sequence;
KphpRegFillPreOpMessage(msg, RegClass, PreInfo, Options);
if (Options->EnableStackTraces)
{
KphCaptureStackInMessage(msg);
}
KphCommsSendMessageAsync(msg);
}
/**
* \brief Registry pre operation callback.
*
* \param[in] RegClass The registry operation class.
* \param[in] PreInfo The pre operation information.
* \param[in] Options Registry options to use.
* \param[in] Sequence The registry sequence number for the pre operation.
*/
_IRQL_requires_max_(APC_LEVEL)
VOID KphpRegPreOp(
_In_ REG_NOTIFY_CLASS RegClass,
_In_ PKPH_REG_PRE_INFORMATION PreInfo,
_In_ PKPH_REG_OPTIONS Options,
_In_ ULONG64 Sequence
)
{
NTSTATUS status;
LARGE_INTEGER timeStamp;
KPH_PAGED_CODE();
NT_ASSERT(!Options->InPost);
if (Options->PreEnabled)
{
KphpRegPreOpSend(RegClass, PreInfo, Options, Sequence, &timeStamp);
}
else
{
//
// Pre operations aren't enabled, but we need the time stamp for the
// post operation to use.
//
KeQuerySystemTime(&timeStamp);
}
if (Options->PostEnabled)
{
status = KphpRegPreOpSetCallContext(RegClass,
PreInfo,
Options,
Sequence,
&timeStamp);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
INFORMER,
"KphpRegPreOpSetCallContext failed: %!STATUS!",
status);
}
}
}
/**
* \brief System registry callback, registered with the configuration manager.
*
* \param[in] CallbackContext Unused.
* \param[in] Argument1 The registry operation class.
* \param[in] Argument2 The registry operation information.
*
* \return STATUS_SUCCESS
*/
_IRQL_requires_max_(APC_LEVEL)
_IRQL_requires_same_
_Function_class_(EX_CALLBACK_FUNCTION)
NTSTATUS KphpRegistryCallback(
_In_ PVOID CallbackContext,
_In_opt_ PVOID Argument1,
_In_opt_ PVOID Argument2
)
{
REG_NOTIFY_CLASS regClass;
ULONG64 sequence;
KPH_REG_OPTIONS options;
//
// N.B. Although Microsoft’s documentation states that registry callbacks
// (e.g. CmRegisterCallbackEx) are invoked at PASSIVE_LEVEL, they can and
// *do* get invoked at APC_LEVEL in practice.
//
// This behavior has been observed and is triggered by code in Microsoft’s
// own kernel components - not third-party drivers. As such, assuming
// callbacks always run at PASSIVE_LEVEL is unsafe. This contradicts the
// stated IRQL guarantees - use caution.
//
KPH_PAGED_CODE();
UNREFERENCED_PARAMETER(CallbackContext);
NT_ASSERT(Argument2);
regClass = (REG_NOTIFY_CLASS)(ULONG_PTR)Argument1;
if (regClass == RegNtCallbackObjectContextCleanup)
{
return STATUS_SUCCESS;
}
sequence = InterlockedIncrementU64(&KphpCmSequence);
options = KphpRegGetOptions(regClass);
if (options.InPost)
{
KphpRegPostOp(regClass, Argument2, sequence);
}
else
{
KphpRegPreOp(regClass, Argument2, &options, sequence);
}
return STATUS_SUCCESS;
}
/**
* \brief Starts the registry informer.
*
* \return Successful or errant status.
*/
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KphRegistryInformerStart(
VOID
)
{
NTSTATUS status;
KPH_PAGED_CODE_PASSIVE();
KphInitializePagedLookaside(&KphpCmCallContextLookaside,
sizeof(KPH_REG_CALL_CONTEXT),
KPH_TAG_REG_CALL_CONTEXT);
status = CmRegisterCallbackEx(KphpRegistryCallback,
KphAltitude,
KphDriverObject,
NULL,
&KphpCmCookie,
NULL);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
INFORMER,
"CmRegisterCallbackEx failed: %!STATUS!",
status);
KphDeletePagedLookaside(&KphpCmCallContextLookaside);
return status;
}
KphpCmRegistered = TRUE;
return STATUS_SUCCESS;
}
/**
* \brief Stops the registry informer.
*/
_IRQL_requires_max_(PASSIVE_LEVEL)
VOID KphRegistryInformerStop(
VOID
)
{
NTSTATUS status;
KPH_PAGED_CODE_PASSIVE();
if (!KphpCmRegistered)
{
return;
}
status = CmUnRegisterCallback(KphpCmCookie);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
INFORMER,
"CmUnRegisterCallback failed: %!STATUS!",
status);
}
KphDeletePagedLookaside(&KphpCmCallContextLookaside);
}
================================================
FILE: KSystemInformer/informer_thread.c
================================================
/*
* Copyright (c) 2022 Winsider Seminars & Solutions, Inc. All rights reserved.
*
* This file is part of System Informer.
*
* Authors:
*
* jxy-s 2022-2026
*
*/
#include
#include
#include
#include
typedef enum _KPH_THREAD_NOTIFY_TYPE
{
KphThreadNotifyCreate,
KphThreadNotifyExecute,
KphThreadNotifyExit
} KPH_THREAD_NOTIFY_TYPE;
KPH_PAGED_FILE();
/**
* \brief Performing thread tracking.
*
* \param[in] ProcessId Process ID from the notification callback.
* \param[in] ThreadId Thread ID from the notification callback.
* \param[in] Create The create parameter from the notification callback.
* \param[in] Thread The thread object of the thread ID.
*
* \return Pointer to the thread context, may be null, the caller should
* dereference this object if it is non-null.
*/
_IRQL_requires_max_(APC_LEVEL)
_Must_inspect_result_
PKPH_THREAD_CONTEXT KphpPerformThreadTracking(
_In_ HANDLE ProcessId,
_In_ HANDLE ThreadId,
_In_ BOOLEAN Create,
_In_opt_ PETHREAD Thread
)
{
PKPH_THREAD_CONTEXT thread;
KPH_PAGED_CODE();
if (!Create)
{
thread = KphUntrackThreadContext(ThreadId);
if (!thread)
{
return NULL;
}
KphTracePrint(TRACE_LEVEL_VERBOSE,
TRACKING,
"Stopped tracking thread %lu in process %wZ (%lu)",
HandleToULong(thread->ClientId.UniqueThread),
KphGetThreadImageName(thread),
HandleToULong(thread->ClientId.UniqueProcess));
thread->ExitNotification = TRUE;
return thread;
}
if (!Thread)
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
TRACKING,
"Failed to track thread %lu in process %lu",
HandleToULong(ThreadId),
HandleToULong(ProcessId));
return NULL;
}
thread = KphTrackThreadContext(Thread);
if (!thread)
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
TRACKING,
"Failed to track thread %lu in process %lu",
HandleToULong(ThreadId),
HandleToULong(ProcessId));
return NULL;
}
thread->CreateNotification = TRUE;
thread->CreatorClientId.UniqueProcess = PsGetCurrentProcessId();
thread->CreatorClientId.UniqueThread = PsGetCurrentThreadId();
KphTracePrint(TRACE_LEVEL_VERBOSE,
TRACKING,
"Tracking thread %lu in process %wZ (%lu)",
HandleToULong(thread->ClientId.UniqueThread),
KphGetThreadImageName(thread),
HandleToULong(thread->ClientId.UniqueProcess));
return thread;
}
/**
* \brief Informs any clients of thread notify routine invocations.
*
* \param[in] Thread The thread being created.
* \param[in] ProcessId Process ID of the process where the thread is being
* created.
* \param[in] ThreadID Thread ID of the thread being created.
* \param[in] Create If true the thread is being created, if false the thread
* is being destroyed.
*/
_Function_class_(PCREATE_THREAD_NOTIFY_ROUTINE)
_IRQL_requires_max_(APC_LEVEL)
VOID KphpCreateThreadNotifyInformer(
_In_ PKPH_THREAD_CONTEXT Thread,
_In_ HANDLE ProcessId,
_In_ HANDLE ThreadId,
_In_ KPH_THREAD_NOTIFY_TYPE Type
)
{
PKPH_MESSAGE msg;
PKPH_PROCESS_CONTEXT actorProcess;
KPH_PAGED_CODE();
msg = NULL;
actorProcess = KphGetCurrentProcessContext();
if (Type == KphThreadNotifyCreate)
{
if (!KphInformerEnabled2(ThreadCreate, actorProcess, Thread->ProcessContext))
{
goto Exit;
}
msg = KphAllocateMessage();
if (!msg)
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
INFORMER,
"Failed to allocate message");
goto Exit;
}
KphMsgInit(msg, KphMsgThreadCreate);
msg->Kernel.ThreadCreate.CreatingClientId.UniqueProcess = PsGetCurrentProcessId();
msg->Kernel.ThreadCreate.CreatingClientId.UniqueThread = PsGetCurrentThreadId();
msg->Kernel.ThreadCreate.CreatingProcessStartKey = KphGetCurrentProcessStartKey();
msg->Kernel.ThreadCreate.CreatingThreadSubProcessTag = KphGetCurrentThreadSubProcessTag();
msg->Kernel.ThreadCreate.TargetClientId.UniqueProcess = ProcessId;
msg->Kernel.ThreadCreate.TargetClientId.UniqueThread = ThreadId;
msg->Kernel.ThreadCreate.TargetProcessStartKey = KphGetThreadProcessStartKey(Thread->EThread);
}
else if (Type == KphThreadNotifyExecute)
{
PVOID subProcessTag;
//
// Call this even if the informer is disabled since this is the best
// place to update the cached sub-process tag in the thread context.
//
subProcessTag = KphGetCurrentThreadSubProcessTag();
if (!KphInformerEnabled2(ThreadExecute, actorProcess, Thread->ProcessContext))
{
goto Exit;
}
msg = KphAllocateMessage();
if (!msg)
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
INFORMER,
"Failed to allocate message");
goto Exit;
}
KphMsgInit(msg, KphMsgThreadExecute);
msg->Kernel.ThreadExecute.ClientId.UniqueProcess = ProcessId;
msg->Kernel.ThreadExecute.ClientId.UniqueThread = ThreadId;
NT_ASSERT(ProcessId == PsGetCurrentProcessId());
msg->Kernel.ThreadExecute.ProcessStartKey = KphGetCurrentProcessStartKey();
msg->Kernel.ThreadExecute.ThreadSubProcessTag = subProcessTag;
}
else
{
NT_ASSERT(Type == KphThreadNotifyExit);
if (!KphInformerEnabled2(ThreadExit, actorProcess, Thread->ProcessContext))
{
goto Exit;
}
msg = KphAllocateMessage();
if (!msg)
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
INFORMER,
"Failed to allocate message");
goto Exit;
}
KphMsgInit(msg, KphMsgThreadExit);
msg->Kernel.ThreadExit.ClientId.UniqueProcess = ProcessId;
msg->Kernel.ThreadExit.ClientId.UniqueThread = ThreadId;
msg->Kernel.ThreadExit.ExitStatus = PsGetThreadExitStatus(Thread->EThread);
NT_ASSERT(ProcessId == PsGetCurrentProcessId());
msg->Kernel.ThreadExit.ProcessStartKey = KphGetCurrentProcessStartKey();
msg->Kernel.ThreadExit.ThreadSubProcessTag = KphGetCurrentThreadSubProcessTag();
}
if (KphInformerOpts2(actorProcess, Thread->ProcessContext).EnableStackTraces)
{
KphCaptureStackInMessage(msg);
}
KphCommsSendMessageAsync(msg);
msg = NULL;
Exit:
if (msg)
{
KphFreeMessage(msg);
}
if (actorProcess)
{
KphDereferenceObject(actorProcess);
}
}
/**
* \brief Thread execution notify routine.
*
* \param[in] ProcessId Process ID of the process where the thread is beginning
* execution.
* \param[in] ThreadId Thread ID of the thread beginning execution.
* \param[in] Create Unused
*/
_Function_class_(PCREATE_THREAD_NOTIFY_ROUTINE)
_IRQL_requires_max_(APC_LEVEL)
VOID KphpExecuteThreadNotifyRoutine(
_In_ HANDLE ProcessId,
_In_ HANDLE ThreadId,
_In_ BOOLEAN Create
)
{
PKPH_THREAD_CONTEXT thread;
KPH_PAGED_CODE();
UNREFERENCED_PARAMETER(Create);
thread = KphGetThreadContext(ThreadId);
if (thread)
{
thread->ExecuteNotification = TRUE;
KphpCreateThreadNotifyInformer(thread,
ProcessId,
ThreadId,
KphThreadNotifyExecute);
KphDereferenceObject(thread);
}
}
/**
* \brief Thread create notify routine.
*
* \param[in] ProcessId Process ID of the process where the thread is being
* created.
* \param[in] ThreadID Thread ID of the thread being created.
* \param[in] Create If true the thread is being created, if false the thread
* is being destroyed.
*/
_Function_class_(PCREATE_THREAD_NOTIFY_ROUTINE)
_IRQL_requires_max_(APC_LEVEL)
VOID KphpCreateThreadNotifyRoutine(
_In_ HANDLE ProcessId,
_In_ HANDLE ThreadId,
_In_ BOOLEAN Create
)
{
NTSTATUS status;
PKPH_THREAD_CONTEXT thread;
PETHREAD threadObject;
KPH_PAGED_CODE();
status = PsLookupThreadByThreadId(ThreadId, &threadObject);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
INFORMER,
"PsLookupThreadByThreadId failed: %!STATUS!",
status);
threadObject = NULL;
}
if (Create)
{
thread = KphpPerformThreadTracking(ProcessId,
ThreadId,
TRUE,
threadObject);
if (thread)
{
KphpCreateThreadNotifyInformer(thread,
ProcessId,
ThreadId,
KphThreadNotifyCreate);
}
}
else
{
thread = KphGetThreadContext(ThreadId);
if (thread)
{
KphpCreateThreadNotifyInformer(thread,
ProcessId,
ThreadId,
KphThreadNotifyExit);
KphDereferenceObject(thread);
}
thread = KphpPerformThreadTracking(ProcessId,
ThreadId,
FALSE,
threadObject);
}
if (thread)
{
KphDereferenceObject(thread);
}
if (threadObject)
{
ObDereferenceObject(threadObject);
}
}
/**
* \brief Starts the thread informer.
*
* \return Successful or errant status.
*/
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KphThreadInformerStart(
VOID
)
{
NTSTATUS status;
KPH_PAGED_CODE_PASSIVE();
status = PsSetCreateThreadNotifyRoutineEx(PsCreateThreadNotifySubsystems,
(PVOID)KphpCreateThreadNotifyRoutine);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
INFORMER,
"Failed to register thread notify routine: %!STATUS!",
status);
return status;
}
status = PsSetCreateThreadNotifyRoutineEx(PsCreateThreadNotifyNonSystem,
(PVOID)KphpExecuteThreadNotifyRoutine);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
INFORMER,
"Failed to register thread notify routine: %!STATUS!",
status);
return status;
}
return STATUS_SUCCESS;
}
/**
* \brief Stops the thread informer.
*/
_IRQL_requires_max_(PASSIVE_LEVEL)
VOID KphThreadInformerStop(
VOID
)
{
KPH_PAGED_CODE_PASSIVE();
PsRemoveCreateThreadNotifyRoutine(KphpExecuteThreadNotifyRoutine);
PsRemoveCreateThreadNotifyRoutine(KphpCreateThreadNotifyRoutine);
}
================================================
FILE: KSystemInformer/knowndll.c
================================================
/*
* Copyright (c) 2022 Winsider Seminars & Solutions, Inc. All rights reserved.
*
* This file is part of System Informer.
*
* Authors:
*
* jxy-s 2022-2026
*
*/
#include
#include
typedef struct _KPH_KNOWN_DLL_EXPORT
{
PCHAR Name;
PVOID* Storage;
} KPH_KNOWN_DLL_EXPORT, *PKPH_KNOWN_DLL_EXPORT;
typedef struct _KPH_KNOWN_DLL_INFORMATION
{
UNICODE_STRING SectionName;
PVOID* BaseAddressStorage;
PKPH_KNOWN_DLL_EXPORT Exports;
} KPH_KNOWN_DLL_INFORMATION, *PKPH_KNOWN_DLL_INFORMATION;
KPH_PROTECTED_DATA_SECTION_PUSH();
PVOID KphNtDllBaseAddress = NULL;
PVOID KphNtDllRtlSetBits = NULL;
static KPH_KNOWN_DLL_EXPORT KphpNtDllExports[] =
{
{ "RtlSetBits", &KphNtDllRtlSetBits },
{ NULL, NULL }
};
static KPH_KNOWN_DLL_INFORMATION KphpKnownDllInformation[] =
{
{
RTL_CONSTANT_STRING(L"\\KnownDlls\\ntdll.dll"),
&KphNtDllBaseAddress,
KphpNtDllExports
}
};
KPH_PROTECTED_DATA_SECTION_POP();
KPH_PAGED_FILE();
/**
* \brief Populates known DLL information.
*/
_IRQL_requires_max_(PASSIVE_LEVEL)
NTSTATUS KphInitializeKnownDll(
VOID
)
{
NTSTATUS status;
HANDLE sectionHandle;
PVOID sectionObject;
PVOID baseAddress;
SIZE_T viewSize;
KPH_PAGED_CODE_PASSIVE();
sectionHandle = NULL;
sectionObject = NULL;
baseAddress = NULL;
for (ULONG i = 0; i < ARRAYSIZE(KphpKnownDllInformation); i++)
{
PKPH_KNOWN_DLL_INFORMATION info;
OBJECT_ATTRIBUTES objectAttributes;
SECTION_IMAGE_INFORMATION sectionImageInfo;
if (baseAddress)
{
MmUnmapViewInSystemSpace(baseAddress);
baseAddress = NULL;
}
if (sectionObject)
{
ObDereferenceObject(sectionObject);
sectionObject = NULL;
}
if (sectionHandle)
{
ObCloseHandle(sectionHandle, KernelMode);
sectionHandle = NULL;
}
info = &KphpKnownDllInformation[i];
NT_ASSERT(info->BaseAddressStorage);
InitializeObjectAttributes(&objectAttributes,
&info->SectionName,
OBJ_KERNEL_HANDLE,
NULL,
NULL);
status = ZwOpenSection(§ionHandle,
SECTION_MAP_READ | SECTION_QUERY,
&objectAttributes);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
GENERAL,
"ZwOpenSection failed: %!STATUS!",
status);
sectionHandle = NULL;
goto Exit;
}
status = ZwQuerySection(sectionHandle,
SectionImageInformation,
§ionImageInfo,
sizeof(SECTION_IMAGE_INFORMATION),
NULL);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
GENERAL,
"ZwQuerySection failed: %!STATUS!",
status);
goto Exit;
}
*info->BaseAddressStorage = sectionImageInfo.TransferAddress;
if (!info->Exports)
{
continue;
}
status = ObReferenceObjectByHandle(sectionHandle,
SECTION_MAP_READ | SECTION_QUERY,
*MmSectionObjectType,
KernelMode,
§ionObject,
NULL);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
GENERAL,
"ObReferenceObjectByHandle failed: %!STATUS!",
status);
sectionObject = NULL;
goto Exit;
}
viewSize = 0;
status = MmMapViewInSystemSpace(sectionObject,
&baseAddress,
&viewSize);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
GENERAL,
"MmMapViewInSystemSpace failed: %!STATUS!",
status);
baseAddress = NULL;
goto Exit;
}
for (PKPH_KNOWN_DLL_EXPORT export = info->Exports;
export->Name != NULL;
export = export + 1)
{
PVOID exportAddress;
NT_ASSERT(export->Storage);
exportAddress = RtlFindExportedRoutineByName(baseAddress,
export->Name);
if (!exportAddress)
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
GENERAL,
"Failed to find %hs in %wZ",
export->Name,
&info->SectionName);
status = STATUS_NOT_FOUND;
goto Exit;
}
*export->Storage = Add2Ptr(sectionImageInfo.TransferAddress,
PtrOffset(baseAddress, exportAddress));
}
}
status = STATUS_SUCCESS;
Exit:
if (baseAddress)
{
MmUnmapViewInSystemSpace(baseAddress);
}
if (sectionObject)
{
ObDereferenceObject(sectionObject);
}
if (sectionHandle)
{
ObCloseHandle(sectionHandle, KernelMode);
}
return status;
}
================================================
FILE: KSystemInformer/kphobject.c
================================================
/*
* Copyright (c) 2022 Winsider Seminars & Solutions, Inc. All rights reserved.
*
* This file is part of System Informer.
*
* Authors:
*
* jxy-s 2022-2026
*
*/
#include
#include
#ifdef _WIN64
#define KPH_ATOMIC_OBJECT_REF_SHARED_MAX 7
#define KPH_ATOMIC_OBJECT_REF_EXCLUSIVE_BIT 3
#define KPH_ATOMIC_OBJECT_REF_LOCK_MASK 0x000000000000000f
#define KPH_ATOMIC_OBJECT_REF_OBJECT_MASK 0xfffffffffffffff0
#else
#define KPH_ATOMIC_OBJECT_REF_SHARED_MAX 3
#define KPH_ATOMIC_OBJECT_REF_EXCLUSIVE_BIT 2
#define KPH_ATOMIC_OBJECT_REF_LOCK_MASK 0x00000007
#define KPH_ATOMIC_OBJECT_REF_OBJECT_MASK 0xfffffff8
#endif
#define KPH_ATOMIC_OBJECT_REF_EXCLUSIVE_FLAG (1 << KPH_ATOMIC_OBJECT_REF_EXCLUSIVE_BIT)
//
// N.B. If more object types are added the array must be expanded.
//
static KPH_OBJECT_TYPE KphpObjectTypes[16] = { 0 };
C_ASSERT(ARRAYSIZE(KphpObjectTypes) < MAXUCHAR);
static LONG KphpObjectTypeCount = 0;
static KSI_WORK_QUEUE_ITEM KphpDeferDeleteObjectWorkItem;
static SLIST_HEADER KphpDeferDeleteObjectList;
/**
* \brief Carries out the deletion of an object.
*
* \param[in] Header The header of an object to delete.
*/
VOID KphpObjectDelete(
_In_freesMem_ PKPH_OBJECT_HEADER Header
)
{
PKPH_OBJECT_TYPE type;
type = &KphpObjectTypes[Header->TypeIndex];
if (type->TypeInfo.Delete)
{
type->TypeInfo.Delete(KphObjectHeaderToObject(Header));
}
type->TypeInfo.Free(Header);
InterlockedDecrementSizeTNoFence(&type->TotalNumberOfObjects);
}
/**
* \brief Defers the deletion of an object.
*
* \param[in] Header The header of an object to defer deletion of.
*/
VOID KphpObjectDeferDelete(
_In_ PKPH_OBJECT_HEADER Header
)
{
//
// Only queue the work item when the list was empty. The worker will flush
// the list, at that point the work item is already removed from the work
// queue and ready to be re-queued.
//
if (!InterlockedPushEntrySList(&KphpDeferDeleteObjectList,
&Header->ListEntry))
{
KsiQueueWorkItem(&KphpDeferDeleteObjectWorkItem, CriticalWorkQueue);
}
}
/**
* \brief Creates an object type.
*
* \param[in] TypeName The name of the type, this must always be resident.
* Preferably a string literal.
* \param[in] TypeInfo The information for the type being created.
* \param[out] ObjectType Set to a pointer to the object type on success.
*/
VOID KphCreateObjectType(
_In_ PCUNICODE_STRING TypeName,
_In_ PKPH_OBJECT_TYPE_INFO TypeInfo,
_Outptr_ PKPH_OBJECT_TYPE* ObjectType
)
{
PKPH_OBJECT_TYPE type;
LONG index;
index = (InterlockedIncrement(&KphpObjectTypeCount) - 1);
//
// We have failure free object type creation, to achieve this we have
// a pre-reserved sized array above. If this asserts the array wasn't
// expanded correctly to support a new type.
//
NT_ASSERT((index >= 0) && (index < ARRAYSIZE(KphpObjectTypes)));
NT_ASSERT(index < MAXUCHAR);
type = &KphpObjectTypes[index];
type->Name.Buffer = TypeName->Buffer;
type->Name.MaximumLength = TypeName->MaximumLength;
type->Name.Length = TypeName->Length;
type->Index = (UCHAR)index;
WriteSizeTNoFence(&type->TotalNumberOfObjects, 0);
WriteSizeTNoFence(&type->HighWaterNumberOfObjects, 0);
RtlCopyMemory(&type->TypeInfo, TypeInfo, sizeof(*TypeInfo));
*ObjectType = type;
}
/**
* \brief Creates an object of a given type.
*
* \param[in] ObjectType The type of object to create.
* \param[in] ObjectBodySize The size of the object body to create.
* \param[out] Object Set to the new object on success.
* \param[in] Parameter Optional parameter passed to initialization routine.
*
* \return Successful or errant status.
*/
_Must_inspect_result_
NTSTATUS KphCreateObject(
_In_ PKPH_OBJECT_TYPE ObjectType,
_In_ ULONG ObjectBodySize,
_Outptr_result_nullonfailure_ PVOID* Object,
_In_opt_ PVOID Parameter
)
{
NTSTATUS status;
PKPH_OBJECT_HEADER header;
PVOID object;
SIZE_T total;
*Object = NULL;
header = ObjectType->TypeInfo.Allocate(KphAddObjectHeaderSize(ObjectBodySize));
if (!header)
{
return STATUS_INSUFFICIENT_RESOURCES;
}
WriteSSizeTNoFence(&header->PointerCount, 1);
header->TypeIndex = ObjectType->Index;
object = KphObjectHeaderToObject(header);
if (ObjectType->TypeInfo.Initialize)
{
status = ObjectType->TypeInfo.Initialize(object, Parameter);
if (!NT_SUCCESS(status))
{
ObjectType->TypeInfo.Free(header);
return status;
}
}
total = InterlockedIncrementSizeTNoFence(&ObjectType->TotalNumberOfObjects);
InterlockedExchangeIfGreaterSizeT(&ObjectType->HighWaterNumberOfObjects,
total);
*Object = object;
return STATUS_SUCCESS;
}
/**
* \brief References an object.
*
* \param[in] Object The object to reference.
*/
VOID KphReferenceObject(
_In_ PVOID Object
)
{
PKPH_OBJECT_HEADER header;
header = KphObjectToObjectHeader(Object);
NT_VERIFY(InterlockedIncrementSSizeTNoFence(&header->PointerCount) > 0);
}
/**
* \brief Dereferences an object.
*
* \param[in] Object The object to dereference.
*/
VOID KphDereferenceObject(
_In_ PVOID Object
)
{
PKPH_OBJECT_HEADER header;
PKPH_OBJECT_TYPE type;
SSIZE_T refCount;
header = KphObjectToObjectHeader(Object);
refCount = InterlockedDecrementSSizeT(&header->PointerCount);
if (refCount > 0)
{
return;
}
NT_ASSERT(refCount == 0);
type = &KphpObjectTypes[header->TypeIndex];
if (type->TypeInfo.DeferDelete)
{
KphpObjectDeferDelete(header);
}
else
{
KphpObjectDelete(header);
}
}
/**
* \brief Dereferences an object and defers deletion of the object.
*
* \param[in] Object The object to dereference.
*/
VOID KphDereferenceObjectDeferDelete(
_In_ PVOID Object
)
{
PKPH_OBJECT_HEADER header;
SSIZE_T refCount;
header = KphObjectToObjectHeader(Object);
refCount = InterlockedDecrementSSizeT(&header->PointerCount);
if (refCount > 0)
{
return;
}
NT_ASSERT(refCount == 0);
KphpObjectDeferDelete(header);
}
/**
* \brief Retrieves the type from an object.
*
* \param[in] Object The object to get the type of.
*
* \return Pointer to the type of object.
*/
_Must_inspect_result_
PKPH_OBJECT_TYPE KphGetObjectType(
_In_ PVOID Object
)
{
PKPH_OBJECT_HEADER header;
UCHAR index;
LONG count;
header = KphObjectToObjectHeader(Object);
index = header->TypeIndex;
count = ReadAcquire(&KphpObjectTypeCount);
if (index >= count)
{
return NULL;
}
return &KphpObjectTypes[index];
}
//
// Custom locking routines follow, disable pedantic prefast locking checks.
//
#pragma prefast(push)
#pragma prefast(disable: 26165) // possibly failing to release lock
#pragma prefast(disable: 26166) // possibly failing to acquire lock
/**
* \brief Acquires the atomic object reference lock shared.
*
* \param[in,out] ObjectRef The object reference to acquire the lock for.
*/
_Requires_lock_not_held_(*ObjectRef)
_Acquires_lock_(*ObjectRef)
FORCEINLINE
VOID KphpAtomicAcquireObjectLockShared(
_Inout_ PKPH_ATOMIC_OBJECT_REF ObjectRef
)
{
ULONG_PTR object;
object = ReadULongPtrAcquire(&ObjectRef->Object);
for (;; YieldProcessor())
{
ULONG_PTR lock;
ULONG_PTR expected;
lock = object & KPH_ATOMIC_OBJECT_REF_LOCK_MASK;
if (lock >= KPH_ATOMIC_OBJECT_REF_SHARED_MAX)
{
object = ReadULongPtrAcquire(&ObjectRef->Object);
continue;
}
expected = object;
object = InterlockedCompareExchangeULongPtr(&ObjectRef->Object,
object + 1,
expected);
if (object == expected)
{
break;
}
}
}
/**
* \brief Releases the atomic object reference lock shared.
*
* \param[in,out] ObjectRef The object reference to release the lock of.
*/
_Requires_lock_held_(*ObjectRef)
_Releases_lock_(*ObjectRef)
FORCEINLINE
VOID KphpAtomicReleaseObjectLockShared(
_Inout_ PKPH_ATOMIC_OBJECT_REF ObjectRef
)
{
ULONG_PTR object;
object = InterlockedDecrementULongPtr(&ObjectRef->Object);
object = object & KPH_ATOMIC_OBJECT_REF_SHARED_MAX;
NT_ASSERT(object < KPH_ATOMIC_OBJECT_REF_SHARED_MAX);
}
/**
* \brief Acquires the atomic object reference lock exclusive.
*
* \param[in,out] ObjectRef The object reference to acquire the lock for.
*/
_Requires_lock_not_held_(*ObjectRef)
_Acquires_lock_(*ObjectRef)
FORCEINLINE
VOID KphpAtomicAcquireObjectLockExclusive(
_Inout_ PKPH_ATOMIC_OBJECT_REF ObjectRef
)
{
ULONG_PTR object;
object = ReadULongPtrAcquire(&ObjectRef->Object);
for (;; YieldProcessor())
{
ULONG_PTR locked;
ULONG_PTR expected;
if (object & KPH_ATOMIC_OBJECT_REF_EXCLUSIVE_FLAG)
{
object = ReadULongPtrAcquire(&ObjectRef->Object);
continue;
}
locked = object | KPH_ATOMIC_OBJECT_REF_EXCLUSIVE_FLAG;
expected = object;
object = InterlockedCompareExchangeULongPtr(&ObjectRef->Object,
locked,
expected);
if (object == expected)
{
break;
}
}
for (; object & KPH_ATOMIC_OBJECT_REF_SHARED_MAX; YieldProcessor())
{
object = ReadULongPtrAcquire(&ObjectRef->Object);
}
}
/**
* \brief Releases the atomic object reference lock shared.
*
* \param[in,out] ObjectRef The object reference to release the lock of.
*/
_Requires_lock_held_(*ObjectRef)
_Releases_lock_(*ObjectRef)
FORCEINLINE
VOID KphpAtomicReleaseObjectLockExclusive(
_Inout_ PKPH_ATOMIC_OBJECT_REF ObjectRef
)
{
BOOLEAN result;
result = InterlockedBitTestAndResetULongPtr(&ObjectRef->Object,
KPH_ATOMIC_OBJECT_REF_EXCLUSIVE_BIT);
NT_ASSERT(result);
}
#pragma prefast(pop)
//
// End of custom locking routines.
//
/**
* \brief Retrieves an addition object reference to an atomically managed
* object reference.
*
* \details This mechanism provides a light weight and fast way to atomically
* managed a reference to an object. If an object is currently managed an
* additional reference to that object is acquired and must be eventually
* released by calling KphDereferenceObject.
*
* \param[in] ObjectRef The object reference to retrieve the object from.
*
* \return A referenced object, NULL if no object is managed.
*/
_Must_inspect_result_
PVOID KphAtomicReferenceObject(
_In_ PKPH_ATOMIC_OBJECT_REF ObjectRef
)
{
ULONG_PTR value;
PVOID object;
KphpAtomicAcquireObjectLockShared(ObjectRef);
value = ReadULongPtrNoFence(&ObjectRef->Object);
object = (PVOID)(value & KPH_ATOMIC_OBJECT_REF_OBJECT_MASK);
if (object)
{
KphReferenceObject(object);
}
KphpAtomicReleaseObjectLockShared(ObjectRef);
return object;
}
/**
* \brief Stores an object to an atomically managed object reference.
*
* \param[in,out] ObjectRef The object reference to assign the object to.
* \param[in] Object Optional object to reference and assign.
*
* \return The previous object that was managed, NULL if no object was managed.
*/
_Must_inspect_result_
PVOID KphpAtomicStoreObjectReference(
_Inout_ PKPH_ATOMIC_OBJECT_REF ObjectRef,
_In_opt_ PVOID Object
)
{
PVOID previous;
ULONG_PTR value;
ULONG_PTR object;
NT_ASSERT(((ULONG_PTR)Object & KPH_ATOMIC_OBJECT_REF_LOCK_MASK) == 0);
KphpAtomicAcquireObjectLockExclusive(ObjectRef);
value = ReadULongPtrNoFence(&ObjectRef->Object);
previous = (PVOID)(value & KPH_ATOMIC_OBJECT_REF_OBJECT_MASK);
object = (ULONG_PTR)Object | KPH_ATOMIC_OBJECT_REF_EXCLUSIVE_FLAG;
InterlockedExchangeULongPtr(&ObjectRef->Object, object);
KphpAtomicReleaseObjectLockExclusive(ObjectRef);
return previous;
}
/**
* \brief Assigns an object to an atomically managed object reference.
*
* \details This mechanism provides a light weight and fast way to atomically
* managed a reference to an object. Any previously managed reference will be
* released. If an object is provided, this function will acquire an additional
* reference to the object, the caller should still release their reference.
*
* \param[in,out] ObjectRef The object reference to assign the object to.
* \param[in] Object Optional object to reference and assign, if NULL the
* managed reference will be cleared.
*/
VOID KphAtomicAssignObjectReference(
_Inout_ PKPH_ATOMIC_OBJECT_REF ObjectRef,
_In_opt_ PVOID Object
)
{
PVOID previous;
if (Object)
{
KphReferenceObject(Object);
}
previous = KphpAtomicStoreObjectReference(ObjectRef, Object);
if (previous)
{
KphDereferenceObject(previous);
}
}
/**
* \brief Moves an object to an atomically managed object reference.
*
* \details This mechanism provides a light weight and fast way to atomically
* managed a reference to an object. If any object is provided, this function
* will assume ownership over the reference, the caller should *not* release
* their reference. If an object is returned the ownership of it is transferred
* to the caller, the caller is responsible for eventually releasing it.
*
* \param[in,out] ObjectRef The object reference to move the object into.
* \param[in] Object Optional object move into the object reference.
*
* \return The previous object that was managed, NULL if no object was managed.
*/
_Must_inspect_result_
PVOID KphAtomicMoveObjectReference(
_Inout_ PKPH_ATOMIC_OBJECT_REF ObjectRef,
_In_opt_ PVOID Object
)
{
return KphpAtomicStoreObjectReference(ObjectRef, Object);
}
KPH_PAGED_FILE();
/**
* \brief Worker routine for deleting objects in a deferred manner.
*
* \param[in] Parameter Unused parameter.
*/
_Function_class_(KSI_WORK_QUEUE_ROUTINE)
_IRQL_requires_max_(PASSIVE_LEVEL)
_IRQL_requires_same_
VOID KSIAPI KphpDeferDeleteObjectWorker(
_In_opt_ PVOID Parameter
)
{
PSLIST_ENTRY entry;
KPH_PAGED_CODE_PASSIVE();
UNREFERENCED_PARAMETER(Parameter);
entry = InterlockedFlushSList(&KphpDeferDeleteObjectList);
while (entry)
{
PKPH_OBJECT_HEADER header;
header = CONTAINING_RECORD(entry, KPH_OBJECT_HEADER, ListEntry);
entry = entry->Next;
KphpObjectDelete(header);
}
}
/**
* \brief Initializes the object subsystem.
*/
_IRQL_requires_max_(PASSIVE_LEVEL)
VOID KphObjectInitialize(
VOID
)
{
KPH_PAGED_CODE_PASSIVE();
InitializeSListHead(&KphpDeferDeleteObjectList);
KsiInitializeWorkItem(&KphpDeferDeleteObjectWorkItem,
KphDriverObject,
&KphpDeferDeleteObjectWorker,
NULL);
}
================================================
FILE: KSystemInformer/kphthread.c
================================================
/*
* Copyright (c) 2022 Winsider Seminars & Solutions, Inc. All rights reserved.
*
* This file is part of System Informer.
*
* Authors:
*
* jxy-s 2025-2026
*
*/
#include
#include
typedef struct _KPH_THREAD_START_CONTEXT
{
PKPH_THREAD_START_ROUTINE StartRoutine;
PVOID Parameter;
UNICODE_STRING ThreadName;
} KPH_THREAD_START_CONTEXT, *PKPH_THREAD_START_CONTEXT;
KPH_PROTECTED_DATA_SECTION_PUSH();
static HANDLE KphpKsiSystemProcessHandle = NULL;
KPH_PROTECTED_DATA_SECTION_POP();
KPH_PAGED_FILE();
/**
* \brief Thread start routine for system threads.
*
* \param[in] Context Pointer to a KPH_THREAD_START_CONTEXT.
*/
_Function_class_(KSTART_ROUTINE)
_IRQL_requires_same_
VOID KphpThreadStartRoutine(
_In_ PVOID StartContext
)
{
NTSTATUS status;
PKPH_THREAD_START_CONTEXT context;
PKPH_THREAD_START_ROUTINE startRoutine;
PVOID parameter;
KPH_PAGED_CODE_PASSIVE();
context = StartContext;
startRoutine = context->StartRoutine;
parameter = context->Parameter;
if (context->ThreadName.Length && !KphParameterFlags.DisableThreadNames)
{
status = ZwSetInformationThread(ZwCurrentThread(),
ThreadNameInformation,
&context->ThreadName,
sizeof(UNICODE_STRING));
if (NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
GENERAL,
"Set thread (%lu) name \"%wZ\"",
HandleToULong(PsGetCurrentThreadId()),
&context->ThreadName);
}
else
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
GENERAL,
"Failed to set thread (%lu) name \"%wZ\": %!STATUS!",
HandleToULong(PsGetCurrentThreadId()),
&context->ThreadName,
status);
}
}
KphFree(context, KPH_TAG_THREAD_START_CONTEXT);
status = startRoutine(parameter);
PsTerminateSystemThread(status);
}
/**
* \brief Creates a system thread.
*
* \param[out] ThreadHandle Optionally receives a handle to the created thread.
* The caller must close the handle once the handle is no longer in use. The
* handle is a kernel handle with THREAD_ALL_ACCESS access rights.
* \param[out] ThreadObject Optionally receives a the created thread object.
* The caller must dereference the object once it is no longer in use.
* \param[in] StartRoutine The entry point for the newly created system thread.
* \param[in] Parameter Argument that is passed to the thread.
* \param[in] ThreadName Optionally specifies a name for the thread.
* \param[in] Flags Optional flags that control the thread creation.
* KPH_CREATE_SYSTEM_THREAD_IN_KSI_PROCESS if possible the thread will be
* created in a dedicated process that is used for system threads.
*
* \return Successful or errant status.
*/
_IRQL_requires_max_(PASSIVE_LEVEL)
NTSTATUS KphCreateSystemThread(
_Out_opt_ PHANDLE ThreadHandle,
_Out_opt_ PETHREAD* ThreadObject,
_In_ PKPH_THREAD_START_ROUTINE StartRoutine,
_In_opt_ _When_(return >= 0, __drv_aliasesMem) PVOID Parameter,
_In_opt_ PCUNICODE_STRING ThreadName,
_In_ ULONG Flags
)
{
NTSTATUS status;
HANDLE threadHandle;
ULONG contextLength;
PKPH_THREAD_START_CONTEXT context;
HANDLE processHandle;
KPH_PAGED_CODE_PASSIVE();
if (ThreadHandle)
{
*ThreadHandle = NULL;
}
if (ThreadObject)
{
*ThreadObject = NULL;
}
threadHandle = NULL;
contextLength = sizeof(KPH_THREAD_START_CONTEXT);
if (ThreadName)
{
contextLength += ThreadName->Length;
}
context = KphAllocatePaged(contextLength, KPH_TAG_THREAD_START_CONTEXT);
if (!context)
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
GENERAL,
"Failed to allocate thread start context.");
status = STATUS_INSUFFICIENT_RESOURCES;
goto Exit;
}
context->StartRoutine = StartRoutine;
context->Parameter = Parameter;
if (ThreadName)
{
context->ThreadName.MaximumLength = ThreadName->Length;
context->ThreadName.Buffer = Add2Ptr(context, sizeof(KPH_THREAD_START_CONTEXT));
RtlCopyUnicodeString(&context->ThreadName, ThreadName);
}
if (FlagOn(Flags, KPH_CREATE_SYSTEM_THREAD_IN_KSI_PROCESS))
{
processHandle = KphpKsiSystemProcessHandle;
}
else
{
processHandle = NULL;
}
status = PsCreateSystemThread(&threadHandle,
THREAD_ALL_ACCESS,
NULL,
processHandle,
NULL,
KphpThreadStartRoutine,
context);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
GENERAL,
"PsCreateSystemThread failed: %!STATUS!",
status);
threadHandle = NULL;
goto Exit;
}
context = NULL;
if (ThreadObject)
{
NT_VERIFY(NT_SUCCESS(ObReferenceObjectByHandle(threadHandle,
THREAD_ALL_ACCESS,
*PsThreadType,
KernelMode,
ThreadObject,
NULL)));
}
if (ThreadHandle)
{
*ThreadHandle = threadHandle;
threadHandle = NULL;
}
Exit:
if (threadHandle)
{
ObCloseHandle(threadHandle, KernelMode);
}
if (context)
{
KphFree(context, KPH_TAG_THREAD_START_CONTEXT);
}
return status;
}
/**
* \brief Cleans up the threading subsystem.
*/
_IRQL_requires_max_(PASSIVE_LEVEL)
VOID KphCleanupThreading(
VOID
)
{
KPH_PAGED_CODE_PASSIVE();
if (KphpKsiSystemProcessHandle)
{
ObCloseHandle(KphpKsiSystemProcessHandle, KernelMode);
}
}
/**
* \brief Initializes threading subsystem.
*/
_IRQL_requires_max_(PASSIVE_LEVEL)
VOID KphInitializeThreading(
VOID
)
{
NTSTATUS status;
KPH_PAGED_CODE_PASSIVE();
if (KphParameterFlags.DisableSystemProcess)
{
KphTracePrint(TRACE_LEVEL_INFORMATION,
GENERAL,
"System process disabled.");
return;
}
status = KsiInitializeSystemProcess(KphSystemProcessName);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
GENERAL,
"KsiInitializeSystemProcess failed: %!STATUS!",
status);
return;
}
status = ObOpenObjectByPointer(KsiSystemProcess,
OBJ_KERNEL_HANDLE,
NULL,
PROCESS_ALL_ACCESS,
*PsProcessType,
KernelMode,
&KphpKsiSystemProcessHandle);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_ERROR,
GENERAL,
"ObOpenObjectByPointer failed: %!STATUS!",
status);
KphpKsiSystemProcessHandle = NULL;
}
}
================================================
FILE: KSystemInformer/ksidll.c
================================================
/*
* Copyright (c) 2022 Winsider Seminars & Solutions, Inc. All rights reserved.
*
* This file is part of System Informer.
*
* Authors:
*
* jxy-s 2022-2026
*
*/
#include
#include
#include
//
// This library is intended to be an extension of core OS functionality which
// enables drivers to perform operations within the system that would otherwise
// be dangerous or impossible.
//
KPH_PROTECTED_DATA_SECTION_PUSH();
static BYTE KsipProtectedSection = 0;
static PMM_PROTECT_DRIVER_SECTION KsipMmProtectDriverSection = NULL;
static PKE_REMOVE_QUEUE_APC KsipKeRemoveQueueApc = NULL;
static PZW_CREATE_PROCESS_EX KsipZwCreateProcessEx = NULL;
KPH_PROTECTED_DATA_SECTION_POP();
static KEVENT KsipDummyThreadEvent;
static HANDLE KsipSystemProcessHandle = NULL;
PEPROCESS KsiSystemProcess = NULL;
_IRQL_requires_max_(PASSIVE_LEVEL)
PVOID KsipGetSystemRoutineAddress(
_In_z_ PCWSTR SystemRoutineName
)
{
UNICODE_STRING systemRoutineName;
RtlInitUnicodeString(&systemRoutineName, SystemRoutineName);
return MmGetSystemRoutineAddress(&systemRoutineName);
}
//
// This is an extension of the APC functionality in Windows to enable a driver
// to be unloaded while there are outstanding APCs on the system which
// reference it.
//
// N.B. While this library guarantees the driver will not be unmapped, it does
// not prevent DriverUnload from being invoked. Drivers using this library may
// check DIRVER_OBJECT.Flags for DRVO_UNLOAD_INVOKED to know if the system has
// or is about to invoke DriverUnload. If applicable the driver should act
// accordingly in their routines.
//
// This extension guarantees that the cleanup routine will always be invoked.
// Either due to normal APC rundown, removal, immediately after the kernel
// routine is executed, or (in the case of normal kernel APC execution) after
// the normal kernel routine executes. KSI_KAPC_CLEANUP_REASON indicates the
// context in which this is called. Users may choose to implement their own
// mechanism in the cleanup routine to synchronize with DriverUnload.
//
_Function_class_(KRUNDOWN_ROUTINE)
_IRQL_requires_(PASSIVE_LEVEL)
_IRQL_requires_same_
VOID NTAPI KsipApcRundownRoutine(
_In_ PKAPC Apc
)
{
PKSI_KAPC apc;
PDRIVER_OBJECT driverObject;
PKSI_KCLEANUP_ROUTINE cleanupRoutine;
apc = (PKSI_KAPC)Apc;
driverObject = apc->DriverObject;
cleanupRoutine = (PKSI_KCLEANUP_ROUTINE)apc->InternalCleanup;
cleanupRoutine(apc, KsiApcCleanupRundown);
ObDereferenceObjectDeferDelete(driverObject);
}
_Function_class_(KNORMAL_ROUTINE)
_IRQL_requires_(PASSIVE_LEVEL)
_IRQL_requires_same_
VOID NTAPI KsipApcNormalRoutine(
_In_opt_ PVOID NormalContext,
_In_opt_ PVOID SystemArgument1,
_In_opt_ PVOID SystemArgument2
)
{
PKSI_KAPC apc;
PDRIVER_OBJECT driverObject;
PKNORMAL_ROUTINE normalRoutine;
PKSI_KCLEANUP_ROUTINE cleanupRoutine;
NT_ASSERT(NormalContext);
apc = (PKSI_KAPC)NormalContext;
driverObject = apc->DriverObject;
normalRoutine = (PKNORMAL_ROUTINE)apc->InternalRoutine;
cleanupRoutine = (PKSI_KCLEANUP_ROUTINE)apc->InternalCleanup;
normalRoutine(apc->InternalContext, SystemArgument1, SystemArgument2);
cleanupRoutine(apc, KsiApcCleanupNormal);
ObDereferenceObjectDeferDelete(driverObject);
}
_Function_class_(KKERNEL_ROUTINE)
_IRQL_requires_(APC_LEVEL)
_IRQL_requires_same_
VOID NTAPI KsipApcKernelRoutine(
_In_ PKAPC Apc,
_Inout_ _Deref_pre_maybenull_ PKNORMAL_ROUTINE *NormalRoutine,
_Inout_ _Deref_pre_maybenull_ PVOID* NormalContext,
_Inout_ _Deref_pre_maybenull_ PVOID* SystemArgument1,
_Inout_ _Deref_pre_maybenull_ PVOID* SystemArgument2
)
{
PKSI_KAPC apc;
PDRIVER_OBJECT driverObject;
PKSI_KKERNEL_ROUTINE kernelRoutine;
PKSI_KCLEANUP_ROUTINE cleanupRoutine;
apc = (PKSI_KAPC)Apc;
driverObject = apc->DriverObject;
kernelRoutine = (PKSI_KKERNEL_ROUTINE)apc->InternalRoutine;
cleanupRoutine = (PKSI_KCLEANUP_ROUTINE)apc->InternalCleanup;
kernelRoutine(apc,
NormalRoutine,
NormalContext,
SystemArgument1,
SystemArgument2);
if ((apc->Apc.ApcMode == KernelMode) && *NormalRoutine)
{
apc->InternalRoutine = (PVOID)*NormalRoutine;
apc->InternalContext = *NormalContext;
*NormalRoutine = KsipApcNormalRoutine;
*NormalContext = apc;
return;
}
cleanupRoutine(apc, KsiApcCleanupKernel);
ObDereferenceObjectDeferDelete(driverObject);
}
VOID KSIAPI KsiInitializeApc(
_Out_ PKSI_KAPC Apc,
_In_ PDRIVER_OBJECT DriverObject,
_In_ PRKTHREAD Thread,
_In_ KAPC_ENVIRONMENT Environment,
_In_ PKSI_KKERNEL_ROUTINE KernelRoutine,
_In_ PKSI_KCLEANUP_ROUTINE CleanupRoutine,
_In_opt_ PKNORMAL_ROUTINE NormalRoutine,
_In_ KPROCESSOR_MODE Mode,
_In_opt_ PVOID NormalContext
)
{
KeInitializeApc(&Apc->Apc,
Thread,
Environment,
KsipApcKernelRoutine,
KsipApcRundownRoutine,
NormalRoutine,
Mode,
NormalContext);
Apc->DriverObject = DriverObject;
Apc->InternalRoutine = (PVOID)KernelRoutine;
Apc->InternalCleanup = (PVOID)CleanupRoutine;
Apc->InternalContext = NULL;
}
BOOLEAN KSIAPI KsiInsertQueueApc(
_Inout_ PKSI_KAPC Apc,
_In_opt_ PVOID SystemArgument1,
_In_opt_ PVOID SystemArgument2,
_In_ KPRIORITY PriorityBoost
)
{
BOOLEAN result;
ObReferenceObject(Apc->DriverObject);
result = KeInsertQueueApc(&Apc->Apc,
SystemArgument1,
SystemArgument2,
PriorityBoost);
if (!result)
{
ObDereferenceObject(Apc->DriverObject);
}
return result;
}
NTSTATUS KSIAPI KsiRemoveQueueApc(
_Inout_ PKSI_KAPC Apc
)
{
PDRIVER_OBJECT driverObject;
PKSI_KCLEANUP_ROUTINE cleanupRoutine;
if (!KsipKeRemoveQueueApc)
{
return STATUS_NOINTERFACE;
}
if (!KsipKeRemoveQueueApc(&Apc->Apc))
{
return STATUS_INVALID_STATE_TRANSITION;
}
driverObject = Apc->DriverObject;
cleanupRoutine = (PKSI_KCLEANUP_ROUTINE)Apc->InternalCleanup;
cleanupRoutine(Apc, KsiApcCleanupRemoved);
ObDereferenceObjectDeferDelete(driverObject);
return STATUS_SUCCESS;
}
//
// This is an extension of the work queue functionality in Windows to enable a
// driver to be unloaded while there are outstanding queued work items on the
// system with which reference it.
//
// N.B. WORK_QUEUE_ITEM and IO_WORKITEM are not equivalent. WORK_QUEUE_ITEM does
// not provide a mechanism to reference an object associated with the driver
// that queued the work item. IO_WORKITEM does, but its implementation for work
// scheduling and accounting differs. They are not interchangeable. Therefore,
// this implementation adds support for associating a driver object with a
// WORK_QUEUE_ITEM.
//
// N.B. While this library guarantees the driver will not be unmapped, it does
// not prevent DriverUnload from being invoked. Drivers using this library may
// check DIRVER_OBJECT.Flags for DRVO_UNLOAD_INVOKED to know if the system has
// or is about to invoke DriverUnload. If applicable the driver should act
// accordingly in their routines.
//
_Function_class_(WORKER_THREAD_ROUTINE)
_IRQL_requires_max_(PASSIVE_LEVEL)
_IRQL_requires_same_
VOID KsipWorkItemRoutine(
_In_ PVOID Parameter
)
{
PKSI_WORK_QUEUE_ITEM workItem;
PDRIVER_OBJECT driverObject;
PKSI_WORK_QUEUE_ROUTINE routine;
workItem = Parameter;
driverObject = workItem->DriverObject;
routine = workItem->Routine;
routine(workItem->Parameter);
ObDereferenceObjectDeferDelete(driverObject);
}
_IRQL_requires_max_(DISPATCH_LEVEL)
VOID KSIAPI KsiInitializeWorkItem(
_Out_ PKSI_WORK_QUEUE_ITEM WorkItem,
_In_ PDRIVER_OBJECT DriverObject,
_In_ PKSI_WORK_QUEUE_ROUTINE Routine,
_In_opt_ PVOID Parameter
)
{
WorkItem->DriverObject = DriverObject;
WorkItem->Routine = Routine;
WorkItem->Parameter = Parameter;
#pragma warning(suppress: 4996)
ExInitializeWorkItem(&WorkItem->WorkItem, KsipWorkItemRoutine, WorkItem);
}
_IRQL_requires_max_(DISPATCH_LEVEL)
VOID KSIAPI KsiQueueWorkItem(
_Inout_ PKSI_WORK_QUEUE_ITEM WorkItem,
_In_ WORK_QUEUE_TYPE QueueType
)
{
ObReferenceObject(WorkItem->DriverObject);
#pragma prefast(push)
#pragma prefast(disable: 28159) // obsolete function
#pragma warning(suppress: 4996)
ExQueueWorkItem(&WorkItem->WorkItem, QueueType);
#pragma prefast(pop)
}
//
// This is an extension that allows a minimal system process to be created.
// Minimal processes must be terminated using PsTerminateMinimalProcess, but
// this routine is not exported or accessible through normal means. If a minimal
// process is deleted without first calling PsTerminateMinimalProcess, the
// system will bug check with INVALID_MINIMAL_PROCESS_STATE.
//
// This implementation creates and exposes a minimal system process object that
// the System Informer driver can use. To work around the limitation, the
// lifetime of this process object is managed by this pinned module. This allows
// System Informer to reuse the existing minimal process object without needing
// to terminate it.
//
// As a result, the minimal process object is effectively "leaked" and requires
// a system reboot to be removed. This is currently the most practical approach
// until Microsoft provides official APIs for drivers to manage minimal
// processes more appropriately.
//
// N.B. This implementation makes some assumptions. It assumes that the current
// process is PsInitialSystemProcess. And the routine is not thread safe. It
// should be used only by System Informer during driver initialization.
//
_IRQL_requires_same_
_Function_class_(KSTART_ROUTINE)
VOID KsipDummyThreadRoutine(
_In_ PVOID StartContext
)
{
UNREFERENCED_PARAMETER(StartContext);
//
// This dummy thread ensures the process remains active. This is the same
// pattern that the Registry process uses to keep itself active (without a
// call to KeBugCheckEx if the wait returns).
//
KeWaitForSingleObject(&KsipDummyThreadEvent,
Executive,
KernelMode,
FALSE,
NULL);
PsTerminateSystemThread(STATUS_SUCCESS);
}
_IRQL_requires_max_(PASSIVE_LEVEL)
NTSTATUS KSIAPI KsiInitializeSystemProcess(
_In_ PCUNICODE_STRING ProcessName
)
{
NTSTATUS status;
HANDLE threadHandle;
OBJECT_ATTRIBUTES objectAttributes;
NT_ASSERT(PsGetCurrentProcess() == PsInitialSystemProcess);
if (!KsipZwCreateProcessEx)
{
return STATUS_NOINTERFACE;
}
if (KsiSystemProcess)
{
return STATUS_SUCCESS;
}
threadHandle = NULL;
if (!KsipSystemProcessHandle)
{
HANDLE processHandle;
InitializeObjectAttributes(&objectAttributes,
(PUNICODE_STRING)ProcessName,
OBJ_KERNEL_HANDLE,
NULL,
NULL);
status = KsipZwCreateProcessEx(&processHandle,
PROCESS_ALL_ACCESS,
&objectAttributes,
ZwCurrentProcess(),
PROCESS_CREATE_FLAGS_MINIMAL_PROCESS,
NULL,
NULL,
NULL,
0);
if (!NT_SUCCESS(status))
{
goto Exit;
}
//
// N.B. We cannot close the handle at this point, as there is no clean
// way to call PsTerminateMinimalProcess. Closing the handle risks
// triggering a bug check with INVALID_MINIMAL_PROCESS_STATE. If we fail
// below, the process object pointer (KsiSystemProcess) will remain
// NULL, allowing us to re-enter this path and try again - though it
// will likely continue to fail.
//
KsipSystemProcessHandle = processHandle;
}
InitializeObjectAttributes(&objectAttributes,
NULL,
OBJ_KERNEL_HANDLE,
NULL,
NULL);
status = PsCreateSystemThread(&threadHandle,
THREAD_ALL_ACCESS,
&objectAttributes,
KsipSystemProcessHandle,
NULL,
KsipDummyThreadRoutine,
NULL);
if (!NT_SUCCESS(status))
{
threadHandle = NULL;
goto Exit;
}
NT_VERIFY(NT_SUCCESS(ObReferenceObjectByHandle(KsipSystemProcessHandle,
PROCESS_ALL_ACCESS,
*PsProcessType,
KernelMode,
&KsiSystemProcess,
NULL)));
Exit:
if (threadHandle)
{
ObCloseHandle(threadHandle, KernelMode);
}
return status;
}
//
// General Library Functions
//
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KSIAPI KsiInitialize(
_In_ ULONG Version,
_In_ PDRIVER_OBJECT DriverObject,
_In_opt_ PVOID Reserved
)
{
UNREFERENCED_PARAMETER(DriverObject);
UNREFERENCED_PARAMETER(Reserved);
if (Version != KSIDLL_CURRENT_VERSION)
{
return STATUS_SI_KSIDLL_VERSION_MISMATCH;
}
return STATUS_SUCCESS;
}
_IRQL_requires_max_(PASSIVE_LEVEL)
VOID KSIAPI KsiUninitialize(
_In_ PDRIVER_OBJECT DriverObject,
_In_ ULONG Reserved
)
{
UNREFERENCED_PARAMETER(DriverObject);
UNREFERENCED_PARAMETER(Reserved);
}
NTSTATUS DllUnload(
VOID
)
{
//
// N.B. It used to be that not specifying a DllUnload routine would
// enforce that your export driver can not be unloaded by not activating
// the reference count mechanism. 10.0.25997.1010 changed this so if you
// do not specify DllUnload the driver can be unmapped. This is resolved
// by implementing DllUnload and returning an error.
//
return STATUS_NOT_SUPPORTED;
}
NTSTATUS DllInitialize(
_In_ PUNICODE_STRING RegistryPath
)
{
UNREFERENCED_PARAMETER(RegistryPath);
__security_init_cookie();
ExInitializeDriverRuntime(DrvRtPoolNxOptIn);
KeInitializeEvent(&KsipDummyThreadEvent, SynchronizationEvent, FALSE);
KsipMmProtectDriverSection = (PMM_PROTECT_DRIVER_SECTION)KsipGetSystemRoutineAddress(L"MmProtectDriverSection");
KsipKeRemoveQueueApc = (PKE_REMOVE_QUEUE_APC)KsipGetSystemRoutineAddress(L"KeRemoveQueueApc");
KsipZwCreateProcessEx = (PZW_CREATE_PROCESS_EX)KsipGetSystemRoutineAddress(L"ZwCreateProcessEx");
if (KsipMmProtectDriverSection)
{
KsipMmProtectDriverSection(&KsipProtectedSection, 0, 0);
}
return STATUS_SUCCESS;
}
================================================
FILE: KSystemInformer/ksidll.def
================================================
LIBRARY ksi.dll
EXPORTS
DllInitialize PRIVATE
DllUnload PRIVATE
KsiInitialize
KsiUninitialize
KsiInitializeApc
KsiInsertQueueApc
KsiRemoveQueueApc
KsiInitializeWorkItem
KsiQueueWorkItem
KsiInitializeSystemProcess
KsiSystemProcess DATA
================================================
FILE: KSystemInformer/ksidll.vcxproj
================================================
Debug
ARM64
Release
ARM64
Debug
x64
Release
x64
17.0
{B385D394-19CC-48BC-827E-AF9ADCE559E0}
{dd38f7fc-d7bd-488b-9242-7d8754cde80d}
DynamicLibrary
KSystemInformer
$(SystemInformerRoot)KSystemInformer\obj\$(Configuration)$(PlatformArchitecture)\$(ProjectName)\
$(SystemInformerRoot)KSystemInformer\bin\$(Configuration)$(PlatformArchitecture)\
ksi
_KSIDLL_;%(PreprocessorDefinitions)
true
ksidll.def
%(AdditionalIncludeDirectories);$(SystemInformerRoot)kphlib\include\;
================================================
FILE: KSystemInformer/lsa.c
================================================
/*
* Copyright (c) 2022 Winsider Seminars & Solutions, Inc. All rights reserved.
*
* This file is part of System Informer.
*
* Authors:
*
* jxy-s 2024-2026
*
*/
#include
#include
KPH_PROTECTED_DATA_SECTION_RO_PUSH();
static const UNICODE_STRING KphpLsaPortName = RTL_CONSTANT_STRING(L"\\SeLsaCommandPort");
KPH_PROTECTED_DATA_SECTION_RO_POP();
static HANDLE KphpLsassProcessId = NULL;
KPH_PAGED_FILE();
/**
* \brief Retrieves the process ID of lsass.
*
* \param[out] ProcessId Set to the process ID of lsass.
*
* \return Successful or errant status.
*/
_IRQL_requires_max_(APC_LEVEL)
NTSTATUS KphpGetLsassProcessId(
_Out_ PHANDLE ProcessId
)
{
NTSTATUS status;
KIRQL currentIrql;
PKPH_DYN dyn;
HANDLE portHandle;
KAPC_STATE apcState;
KPH_ALPC_COMMUNICATION_INFORMATION info;
KPH_PAGED_CODE();
//
// We cache the process ID of lsass since opening and closing the connection
// port frequently can cause significant performance degradation. This cache
// is invalidated when:
//
// - A process exits and the process ID is what was cached.
// - The process does not have SeCreateTokenPrivilege.
//
// N.B. The privilege check is always performed when checking if a process
// is lsass, even when the cached process ID is used.
//
*ProcessId = InterlockedCompareExchangePointer(&KphpLsassProcessId,
NULL,
NULL);
if (*ProcessId)
{
return STATUS_SUCCESS;
}
currentIrql = KeGetCurrentIrql();
if (currentIrql > PASSIVE_LEVEL)
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
GENERAL,
"KphpGetLsassProcessId called at %!irql!",
currentIrql);
return STATUS_INVALID_LEVEL;
}
//
// N.B. This is an optimization. In order to query the process ID of lsass
// through the LSA port, we need the dynamic data. Rather than doing the
// work to attach to the system process and open the port, if we know we
// do not have the dynamic data, exit early.
//
dyn = KphReferenceDynData();
if (!dyn)
{
return STATUS_NOINTERFACE;
}
//
// Attach to system to ensure we get a kernel handle from the following
// ZwAlpcConnectPort call. Opening the handle here does not ask for the
// object attributes. To keep our imports simple we choose to use this
// over the Ex version (might change in the future). Pattern is adopted
// from msrpc.sys.
//
KeStackAttachProcess(PsInitialSystemProcess, &apcState);
status = ZwAlpcConnectPort(&portHandle,
(PUNICODE_STRING)&KphpLsaPortName,
NULL,
NULL,
0,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
GENERAL,
"ZwAlpcConnectPort failed: %!STATUS!",
status);
portHandle = NULL;
goto Exit;
}
status = KphAlpcQueryInformation(ZwCurrentProcess(),
portHandle,
KphAlpcCommunicationInformation,
&info,
sizeof(KPH_ALPC_COMMUNICATION_INFORMATION),
NULL,
KernelMode);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
GENERAL,
"KphAlpcQueryInformation failed: %!STATUS!",
status);
goto Exit;
}
*ProcessId = info.ConnectionPort.OwnerProcessId;
Exit:
if (portHandle)
{
ObCloseHandle(portHandle, KernelMode);
}
KeUnstackDetachProcess(&apcState);
KphDereferenceObject(dyn);
return status;
}
/**
* \brief Caches the lsass process ID if appropriate.
*
* \param[in] Process Optional lsass process object.
* \param[in] ProcessId The lsass process ID to cache.
*/
_IRQL_requires_max_(APC_LEVEL)
VOID KphpCacheLsassProcessId(
_In_opt_ PEPROCESS Process,
_In_ HANDLE ProcessId
)
{
PEPROCESS process;
KPH_PAGED_CODE();
if (ReadPointerAcquire(&KphpLsassProcessId) == ProcessId)
{
return;
}
if (Process)
{
process = Process;
}
else if (!NT_SUCCESS(PsLookupProcessByProcessId(ProcessId, &process)))
{
return;
}
NT_ASSERT(PsGetProcessId(process) == ProcessId);
if (NT_SUCCESS(PsAcquireProcessExitSynchronization(process)))
{
InterlockedExchangePointer(&KphpLsassProcessId, ProcessId);
PsReleaseProcessExitSynchronization(process);
}
if (process != Process)
{
ObDereferenceObject(process);
}
}
/**
* \brief Checks if a given process is lsass.
*
* \param[in] Process The process to check.
* \param[out] IsLsass TRUE if the process is lsass, FALSE otherwise.
*
* \return Successful or errant status.
*/
_IRQL_requires_max_(APC_LEVEL)
_Must_inspect_result_
NTSTATUS KphProcessIsLsass(
_In_ PEPROCESS Process,
_Out_ PBOOLEAN IsLsass
)
{
NTSTATUS status;
PS_PROTECTION processProtection;
HANDLE processId;
SECURITY_SUBJECT_CONTEXT subjectContext;
BOOLEAN result;
KPH_PAGED_CODE();
*IsLsass = FALSE;
processProtection = PsGetProcessProtection(Process);
if ((processProtection.Type != PsProtectedTypeNone) &&
(processProtection.Signer == PsProtectedSignerLsa))
{
processId = PsGetProcessId(Process);
KphpCacheLsassProcessId(Process, processId);
}
else
{
status = KphpGetLsassProcessId(&processId);
if (!NT_SUCCESS(status))
{
return status;
}
KphpCacheLsassProcessId(NULL, processId);
if (processId != PsGetProcessId(Process))
{
return STATUS_SUCCESS;
}
}
SeCaptureSubjectContextEx(NULL, Process, &subjectContext);
result = KphSinglePrivilegeCheckEx(SeCreateTokenPrivilege,
&subjectContext,
UserMode);
SeReleaseSubjectContext(&subjectContext);
if (result)
{
*IsLsass = TRUE;
}
else
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
GENERAL,
"PID %lu does not have SeCreateTokenPrivilege",
HandleToULong(processId));
InterlockedExchangePointer(&KphpLsassProcessId, NULL);
}
return STATUS_SUCCESS;
}
/**
* \brief Validates if a process is lsass and caches it.
*
* \details This should be called whenever a new process is identified.
*
* \param[in] Process The process to validate.
*/
_IRQL_requires_max_(APC_LEVEL)
VOID KphValidateLsass(
_In_ PEPROCESS Process
)
{
NTSTATUS status;
BOOLEAN isLsass;
KPH_PAGED_CODE();
status = KphProcessIsLsass(Process, &isLsass);
if (NT_SUCCESS(status) && isLsass)
{
KphTracePrint(TRACE_LEVEL_INFORMATION,
GENERAL,
"Validated LSA process (%lu)",
HandleToULong(PsGetProcessId(Process)));
}
}
/**
* \brief Invalidates the cached lsass process ID if it matches.
*
* \details This should be called whenever a process ID could be recycled.
*
* \param[in] Process The process to invalidate.
*/
_IRQL_requires_max_(PASSIVE_LEVEL)
VOID KphInvalidateLsass(
_In_ PEPROCESS Process
)
{
HANDLE processId;
KPH_PAGED_CODE();
processId = PsGetProcessId(Process);
if (InterlockedCompareExchangePointer(&KphpLsassProcessId,
NULL,
processId) == processId)
{
KphTracePrint(TRACE_LEVEL_INFORMATION,
GENERAL,
"Invalidated LSA process (%lu)",
HandleToULong(processId));
}
}
/**
* \brief Checks if lsass can be identified.
*
* \return TRUE if lsass can be identified, FALSE otherwise.
*/
_IRQL_requires_max_(APC_LEVEL)
_Must_inspect_result_
BOOLEAN KphCanIdentifyLsass(
VOID
)
{
NTSTATUS status;
BOOLEAN isLsass;
KPH_PAGED_CODE();
//
// N.B. It does not matter what process we use here and we intentionally
// ignore the output boolean. What matters is the return succeeds which
// indicates that we can identify lsass.
//
status = KphProcessIsLsass(PsInitialSystemProcess, &isLsass);
return NT_SUCCESS(status);
}
================================================
FILE: KSystemInformer/main.c
================================================
/*
* Copyright (c) 2022 Winsider Seminars & Solutions, Inc. All rights reserved.
*
* This file is part of System Informer.
*
* Authors:
*
* wj32 2010-2016
* jxy-s 2021-2026
*
*/
#include
#include
#include
KPH_PROTECTED_DATA_SECTION_RO_PUSH();
static const BYTE KphpProtectedSectionReadOnly = 0;
KPH_PROTECTED_DATA_SECTION_RO_POP();
KPH_PROTECTED_DATA_SECTION_PUSH();
static BYTE KphpProtectedSection = 0;
PDRIVER_OBJECT KphDriverObject = NULL;
RTL_OSVERSIONINFOEXW KphOsVersionInfo = { 0 };
KPH_FILE_VERSION KphKernelVersion = { 0 };
BOOLEAN KphIgnoreProtectionsSuppressed = FALSE;
BOOLEAN KphIgnoreTestSigningEnabled = FALSE;
SYSTEM_SECUREBOOT_INFORMATION KphSecureBootInfo = { 0 };
SYSTEM_CODEINTEGRITY_INFORMATION KphCodeIntegrityInfo = { 0 };
KPH_PROTECTED_DATA_SECTION_POP();
KPH_PAGED_FILE();
/**
* \brief Protects select sections.
*/
_IRQL_requires_max_(PASSIVE_LEVEL)
VOID KphpProtectSections(
VOID
)
{
NTSTATUS status;
KPH_PAGED_CODE_PASSIVE();
if (!KphDynMmProtectDriverSection)
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
GENERAL,
"MmProtectDriverSection not found");
return;
}
status = KphDynMmProtectDriverSection(&KphpProtectedSection,
0,
MM_PROTECT_DRIVER_SECTION_ALLOW_UNLOAD);
KphTracePrint(TRACE_LEVEL_VERBOSE,
GENERAL,
"MmProtectDriverSection %!STATUS!",
status);
status = KphDynMmProtectDriverSection((PVOID)&KphpProtectedSectionReadOnly,
0,
MM_PROTECT_DRIVER_SECTION_ALLOW_UNLOAD);
KphTracePrint(TRACE_LEVEL_VERBOSE,
GENERAL,
"MmProtectDriverSection %!STATUS!",
status);
}
/**
* \brief Cleans up the driver state.
*
* \param[in] DriverObject Driver object of this driver.
*/
_IRQL_requires_max_(PASSIVE_LEVEL)
VOID KphpDriverCleanup(
_In_ PDRIVER_OBJECT DriverObject
)
{
KPH_PAGED_CODE_PASSIVE();
KphDebugInformerStop();
KphRegistryInformerStop();
KphObjectInformerStop();
KphImageInformerStop();
KphCidMarkPopulated();
KphThreadInformerStop();
KphProcessInformerStop();
KphFltUnregister();
KphCidCleanup();
KphCleanupInformer();
KphCleanupDynData();
KphCleanupVerify();
KphCleanupHashing();
KphCleanupThreading();
KphCleanupParameters();
KsiUninitialize(DriverObject, 0);
}
/**
* \brief Driver unload routine.
*
* \param[in] DriverObject Driver object of this driver.
*/
_Function_class_(DRIVER_UNLOAD)
_IRQL_requires_(PASSIVE_LEVEL)
_IRQL_requires_same_
VOID DriverUnload(
_In_ PDRIVER_OBJECT DriverObject
)
{
KPH_PAGED_CODE_PASSIVE();
KphTracePrint(TRACE_LEVEL_INFORMATION, GENERAL, "Driver Unloading...");
KphpDriverCleanup(DriverObject);
KphTracePrint(TRACE_LEVEL_INFORMATION, GENERAL, "Driver Unloaded");
WPP_CLEANUP(DriverObject);
}
/**
* \brief Driver entry point.
*
* \param[in] DriverObject Driver object for this driver.
* \param[in] RegistryPath Registry path for this driver.
*
* \return Successful or errant status.
*/
_Function_class_(DRIVER_INITIALIZE)
_IRQL_requires_(PASSIVE_LEVEL)
_IRQL_requires_same_
NTSTATUS DriverEntry(
_In_ PDRIVER_OBJECT DriverObject,
_In_ PUNICODE_STRING RegistryPath
)
{
NTSTATUS status;
KPH_PAGED_CODE_PASSIVE();
WPP_INIT_TRACING(DriverObject, RegistryPath);
KphTracePrint(TRACE_LEVEL_INFORMATION, GENERAL, "Driver Loading...");
KphDriverObject = DriverObject;
KphDriverObject->DriverUnload = DriverUnload;
ExInitializeDriverRuntime(DrvRtPoolNxOptIn);
status = KsiInitialize(KSIDLL_CURRENT_VERSION, DriverObject, NULL);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_ERROR,
GENERAL,
"KsiInitialize failed: %!STATUS!",
status);
goto Exit;
}
KphOsVersionInfo.dwOSVersionInfoSize = sizeof(RTL_OSVERSIONINFOEXW);
status = RtlGetVersion((PRTL_OSVERSIONINFOW)&KphOsVersionInfo);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_ERROR,
GENERAL,
"RtlGetVersion failed: %!STATUS!",
status);
goto Exit;
}
if (!NT_SUCCESS(ZwQuerySystemInformation(SystemSecureBootInformation,
&KphSecureBootInfo,
sizeof(KphSecureBootInfo),
NULL)))
{
RtlZeroMemory(&KphSecureBootInfo, sizeof(KphSecureBootInfo));
}
KphCodeIntegrityInfo.Length = sizeof(KphCodeIntegrityInfo);
if (!NT_SUCCESS(ZwQuerySystemInformation(SystemCodeIntegrityInformation,
&KphCodeIntegrityInfo,
sizeof(KphCodeIntegrityInfo),
NULL)))
{
RtlZeroMemory(&KphCodeIntegrityInfo, sizeof(KphCodeIntegrityInfo));
}
if (KphInDeveloperMode())
{
KphTracePrint(TRACE_LEVEL_INFORMATION, GENERAL, "Developer Mode");
}
KphDynamicImport();
KphObjectInitialize();
KphInitializeRingBuffer();
KphInitializeParameters(RegistryPath);
KphInitializeAlloc();
KphInitializeThreading();
status = KphInitializeKnownDll();
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_ERROR,
GENERAL,
"KphInitializeKnownDll failed: %!STATUS!",
status);
goto Exit;
}
status = KphGetKernelVersion(&KphKernelVersion);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_ERROR,
GENERAL,
"KphGetKernelVersion failed: %!STATUS!",
status);
goto Exit;
}
KphTracePrint(TRACE_LEVEL_INFORMATION,
GENERAL,
"Windows %lu.%lu.%lu Kernel %lu.%lu.%lu.%lu",
KphOsVersionInfo.dwMajorVersion,
KphOsVersionInfo.dwMinorVersion,
KphOsVersionInfo.dwBuildNumber,
KphKernelVersion.MajorVersion,
KphKernelVersion.MinorVersion,
KphKernelVersion.BuildNumber,
KphKernelVersion.Revision);
status = KphInitializeHashing();
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_ERROR,
GENERAL,
"Failed to initialize hashing: %!STATUS!",
status);
goto Exit;
}
status = KphInitializeVerify();
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_ERROR,
GENERAL,
"Failed to initialize verify: %!STATUS!",
status);
goto Exit;
}
KphInitializeDynData();
KphInitializeProtection();
KphInitializeSessionToken();
status = KphInitializeStackBackTrace();
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_ERROR,
GENERAL,
"Failed to initialize stack back trace: %!STATUS!",
status);
goto Exit;
}
status = KphInitializeInformer();
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_ERROR,
GENERAL,
"Failed to initialize informer: %!STATUS!",
status);
goto Exit;
}
status = KphFltRegister(DriverObject, RegistryPath);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_ERROR,
GENERAL,
"Failed to register mini-filter: %!STATUS!",
status);
goto Exit;
}
status = KphCidInitialize();
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_ERROR,
GENERAL,
"Failed to initialize CID tracking: %!STATUS!",
status);
goto Exit;
}
status = KphProcessInformerStart();
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_ERROR,
GENERAL,
"Failed to start process informer: %!STATUS!",
status);
goto Exit;
}
status = KphThreadInformerStart();
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_ERROR,
GENERAL,
"Failed to start thread informer: %!STATUS!",
status);
goto Exit;
}
status = KphCidPopulate();
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_ERROR,
GENERAL,
"Failed to populate CID tracking: %!STATUS!",
status);
goto Exit;
}
KphCidMarkPopulated();
status = KphFltInformerStart();
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_ERROR,
GENERAL,
"Failed to start mini-filter informer: %!STATUS!",
status);
goto Exit;
}
status = KphImageInformerStart();
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_ERROR,
GENERAL,
"Failed to start image informer: %!STATUS!",
status);
goto Exit;
}
status = KphObjectInformerStart();
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_ERROR,
GENERAL,
"Failed to start object informer: %!STATUS!",
status);
goto Exit;
}
status = KphRegistryInformerStart();
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_ERROR,
GENERAL,
"Failed to start registry informer: %!STATUS!",
status);
goto Exit;
}
status = KphDebugInformerStart();
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_ERROR,
GENERAL,
"Failed to start debug informer: %!STATUS!",
status);
goto Exit;
}
KphpProtectSections();
Exit:
if (NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_INFORMATION, GENERAL, "Driver Loaded");
}
else
{
KphTracePrint(TRACE_LEVEL_ERROR,
GENERAL,
"Driver Load Failed: %!STATUS!",
status);
KphpDriverCleanup(DriverObject);
WPP_CLEANUP(DriverObject);
}
return status;
}
================================================
FILE: KSystemInformer/object.c
================================================
/*
* Copyright (c) 2022 Winsider Seminars & Solutions, Inc. All rights reserved.
*
* This file is part of System Informer.
*
* Authors:
*
* wj32 2010-2016
* jxy-s 2021-2026
*
*/
#include
#include
typedef struct _KPH_ENUMERATE_PROCESS_HANDLES_CONTEXT
{
PKPH_DYN Dyn;
KPROCESSOR_MODE AccessMode;
PVOID Buffer;
PVOID BufferLimit;
PVOID CurrentEntry;
ULONG Count;
NTSTATUS Status;
} KPH_ENUMERATE_PROCESS_HANDLES_CONTEXT, *PKPH_ENUMERATE_PROCESS_HANDLES_CONTEXT;
KPH_PROTECTED_DATA_SECTION_RO_PUSH();
static const UNICODE_STRING KphpEtwRegistrationName = RTL_CONSTANT_STRING(L"EtwRegistration");
KPH_PROTECTED_DATA_SECTION_RO_POP();
_Must_inspect_result_
PVOID KphObpDecodeObject(
_In_ PKPH_DYN Dyn,
_In_ PHANDLE_TABLE_ENTRY HandleTableEntry
)
{
#if defined(_M_X64) || defined(_M_ARM64)
if (Dyn->ObDecodeShift != ULONG_MAX)
{
LONG_PTR object;
object = (LONG_PTR)(HandleTableEntry->Object);
//
// Decode the object pointer, shift back up the lower nibble to zeros.
// N.B. LA57 is special but the way we define HANDLE_TABLE_ENTRY and
// use dynamic data supports it.
//
object >>= Dyn->ObDecodeShift;
object <<= 4;
return (PVOID)object;
}
else
{
return NULL;
}
#else
return (PVOID)((ULONG_PTR)(HandleTableEntry->Object) & ~OBJ_HANDLE_ATTRIBUTES);
#endif
}
ULONG KphObpGetHandleAttributes(
_In_ PKPH_DYN Dyn,
_In_ PHANDLE_TABLE_ENTRY HandleTableEntry
)
{
#if defined(_M_X64) || defined(_M_ARM64)
if (Dyn->ObAttributesShift != ULONG_MAX)
{
return (ULONG)(HandleTableEntry->Value >> Dyn->ObAttributesShift) & 0x3;
}
else
{
return 0;
}
#else
return (HandleTableEntry->ObAttributes & (OBJ_INHERIT | OBJ_AUDIT_OBJECT_CLOSE)) |
((HandleTableEntry->GrantedAccess & ObpAccessProtectCloseBit) ? OBJ_PROTECT_CLOSE : 0);
#endif
}
/**
* \brief Retrieves information about a file object.
*
* \param[in] FileObject The file object to retrieve information for.
* \param[out] Information Receives the file object information.
*/
_IRQL_requires_max_(PASSIVE_LEVEL)
VOID KphpGetFileObjectInformation(
_In_ PFILE_OBJECT FileObject,
_Out_ PKPH_FILE_OBJECT_INFORMATION Information
)
{
PDEVICE_OBJECT attachedDevice;
PDEVICE_OBJECT relatedDevice;
KIRQL irql;
PVPB vpb;
//
// VPB lock will be acquired, code placed in non-paged segment on purpose.
//
KPH_NPAGED_CODE_PASSIVE();
attachedDevice = IoGetAttachedDevice(FileObject->DeviceObject);
relatedDevice = IoGetRelatedDeviceObject(FileObject);
RtlZeroMemory(Information, sizeof(KPH_FILE_OBJECT_INFORMATION));
Information->LockOperation = FileObject->LockOperation;
Information->DeletePending = FileObject->DeletePending;
Information->ReadAccess = FileObject->ReadAccess;
Information->WriteAccess = FileObject->WriteAccess;
Information->DeleteAccess = FileObject->DeleteAccess;
Information->SharedRead = FileObject->SharedRead;
Information->SharedWrite = FileObject->SharedWrite;
Information->SharedDelete = FileObject->SharedDelete;
Information->CurrentByteOffset = FileObject->CurrentByteOffset;
Information->Flags = FileObject->Flags;
if (FileObject->SectionObjectPointer)
{
Information->UserWritableReferences =
MmDoesFileHaveUserWritableReferences(FileObject->SectionObjectPointer);
}
Information->HasActiveTransaction =
(IoGetTransactionParameterBlock(FileObject) ? TRUE : FALSE);
Information->IsIgnoringSharing = IoIsFileObjectIgnoringSharing(FileObject);
Information->Waiters = FileObject->Waiters;
Information->Busy = FileObject->Busy;
Information->Device.Type = FileObject->DeviceObject->DeviceType;
Information->Device.Characteristics = FileObject->DeviceObject->Characteristics;
Information->Device.Flags = FileObject->DeviceObject->Flags;
Information->AttachedDevice.Type = attachedDevice->DeviceType;
Information->AttachedDevice.Characteristics = attachedDevice->Characteristics;
Information->AttachedDevice.Flags = attachedDevice->Flags;
Information->RelatedDevice.Type = relatedDevice->DeviceType;
Information->RelatedDevice.Characteristics = relatedDevice->Characteristics;
Information->RelatedDevice.Flags = relatedDevice->Flags;
C_ASSERT(ARRAYSIZE(Information->Vpb.VolumeLabel) == ARRAYSIZE(vpb->VolumeLabel));
IoAcquireVpbSpinLock(&irql);
//
// Accessing the VPB is reserved for "certain classes of drivers".
//
#pragma prefast(push)
#pragma prefast(disable : 28175)
vpb = FileObject->Vpb;
if (vpb)
{
Information->Vpb.Type = vpb->Type;
Information->Vpb.Size = vpb->Size;
Information->Vpb.Flags = vpb->Flags;
Information->Vpb.VolumeLabelLength = vpb->VolumeLabelLength;
Information->Vpb.SerialNumber = vpb->SerialNumber;
Information->Vpb.ReferenceCount = vpb->ReferenceCount;
RtlCopyMemory(Information->Vpb.VolumeLabel,
vpb->VolumeLabel,
ARRAYSIZE(vpb->VolumeLabel) * sizeof(WCHAR));
}
vpb = FileObject->DeviceObject->Vpb;
if (vpb)
{
Information->Device.Vpb.Type = vpb->Type;
Information->Device.Vpb.Size = vpb->Size;
Information->Device.Vpb.Flags = vpb->Flags;
Information->Device.Vpb.VolumeLabelLength = vpb->VolumeLabelLength;
Information->Device.Vpb.SerialNumber = vpb->SerialNumber;
Information->Device.Vpb.ReferenceCount = vpb->ReferenceCount;
RtlCopyMemory(Information->Device.Vpb.VolumeLabel,
vpb->VolumeLabel,
ARRAYSIZE(vpb->VolumeLabel) * sizeof(WCHAR));
}
vpb = attachedDevice->Vpb;
if (vpb)
{
Information->AttachedDevice.Vpb.Type = vpb->Type;
Information->AttachedDevice.Vpb.Size = vpb->Size;
Information->AttachedDevice.Vpb.Flags = vpb->Flags;
Information->AttachedDevice.Vpb.VolumeLabelLength = vpb->VolumeLabelLength;
Information->AttachedDevice.Vpb.SerialNumber = vpb->SerialNumber;
Information->AttachedDevice.Vpb.ReferenceCount = vpb->ReferenceCount;
RtlCopyMemory(Information->AttachedDevice.Vpb.VolumeLabel,
vpb->VolumeLabel,
ARRAYSIZE(vpb->VolumeLabel) * sizeof(WCHAR));
}
vpb = relatedDevice->Vpb;
if (vpb)
{
Information->RelatedDevice.Vpb.Type = vpb->Type;
Information->RelatedDevice.Vpb.Size = vpb->Size;
Information->RelatedDevice.Vpb.Flags = vpb->Flags;
Information->RelatedDevice.Vpb.VolumeLabelLength = vpb->VolumeLabelLength;
Information->RelatedDevice.Vpb.SerialNumber = vpb->SerialNumber;
Information->RelatedDevice.Vpb.ReferenceCount = vpb->ReferenceCount;
RtlCopyMemory(Information->RelatedDevice.Vpb.VolumeLabel,
vpb->VolumeLabel,
ARRAYSIZE(vpb->VolumeLabel) * sizeof(WCHAR));
}
#pragma prefast(pop)
IoReleaseVpbSpinLock(irql);
}
KPH_PAGED_FILE();
/**
* \brief Gets a pointer to the handle table of a process. On success, acquires
* process exit synchronization, the process should be released by calling
* KphDereferenceProcessHandleTable.
*
* \param[in] Dyn Dynamic configuration.
* \param[in] Process A process object.
* \param[out] HandleTable On success set to the process handle table.
*
* \return Successful or errant status.
*/
_When_(NT_SUCCESS(return), _Acquires_lock_(Process))
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KphReferenceProcessHandleTable(
_In_ PKPH_DYN Dyn,
_In_ PEPROCESS Process,
_Outptr_result_nullonfailure_ PHANDLE_TABLE* HandleTable
)
{
PHANDLE_TABLE handleTable;
NTSTATUS status;
KPH_PAGED_CODE_PASSIVE();
*HandleTable = NULL;
//
// Fail if we don't have an offset.
//
if (Dyn->EpObjectTable == ULONG_MAX)
{
return STATUS_NOINTERFACE;
}
//
// Prevent the process from terminating and get its handle table.
//
status = PsAcquireProcessExitSynchronization(Process);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
GENERAL,
"PsAcquireProcessExitSynchronization failed: %!STATUS!",
status);
return STATUS_TOO_LATE;
}
handleTable = *(PHANDLE_TABLE*)Add2Ptr(Process, Dyn->EpObjectTable);
if (!handleTable)
{
PsReleaseProcessExitSynchronization(Process);
KphTracePrint(TRACE_LEVEL_VERBOSE,
GENERAL,
"Process has no handle table.");
return STATUS_NOT_FOUND;
}
*HandleTable = handleTable;
return STATUS_SUCCESS;
}
/**
* \brief Releases process after acquiring the process handle table.
*
* \param[in] Process A process object.
*/
_Releases_lock_(Process)
_IRQL_requires_max_(PASSIVE_LEVEL)
VOID KphDereferenceProcessHandleTable(
_In_ PEPROCESS Process
)
{
KPH_PAGED_CODE_PASSIVE();
PsReleaseProcessExitSynchronization(Process);
}
/**
* \brief Unlocks a handle table entry.
*
* \param[in] Dyn Dynamic configuration.
* \param[in] HandleTable Handle table to unlock.
* \param[in] HandleTableEntry Handle table entry to unlock.
*/
_IRQL_requires_max_(PASSIVE_LEVEL)
VOID KphpUnlockHandleTableEntry(
_In_ PKPH_DYN Dyn,
_In_ PHANDLE_TABLE HandleTable,
_In_ PHANDLE_TABLE_ENTRY HandleTableEntry
)
{
PEX_PUSH_LOCK handleContentionEvent;
KPH_PAGED_CODE_PASSIVE();
NT_ASSERT(Dyn->HtHandleContentionEvent != ULONG_MAX);
//
// Set the unlocked bit.
//
InterlockedExchangeAddULongPtr(&HandleTableEntry->Value, 1);
//
// Allow waiters to wake up.
//
handleContentionEvent = (PEX_PUSH_LOCK)Add2Ptr(HandleTable,
Dyn->HtHandleContentionEvent);
if (*(PULONG_PTR)handleContentionEvent != 0)
{
ExfUnblockPushLock(handleContentionEvent, NULL);
}
}
typedef struct _KPH_ENUM_PROC_HANDLE_EX_CONTEXT
{
PKPH_DYN Dyn;
PKPH_ENUM_PROCESS_HANDLES_CALLBACK Callback;
PVOID Parameter;
} KPH_ENUM_PROC_HANDLE_EX_CONTEXT, *PKPH_ENUM_PROC_HANDLE_EX_CONTEXT;
/**
* \brief Pass-through callback for handle table enumeration.
*
* \param[in,out] HandleTableEntry Related handle table entry.
* \param[in] Handle The handle for this entry.
* \param[in] Context Enumeration context.
*
* \return Result from wrapped callback.
*/
_Function_class_(EX_ENUM_HANDLE_CALLBACK)
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
BOOLEAN NTAPI KphEnumerateProcessHandlesExCallback(
_In_ PHANDLE_TABLE HandleTable,
_Inout_ PHANDLE_TABLE_ENTRY HandleTableEntry,
_In_ HANDLE Handle,
_In_opt_ PVOID Context
)
{
PKPH_ENUM_PROC_HANDLE_EX_CONTEXT context;
BOOLEAN result;
KPH_PAGED_CODE_PASSIVE();
NT_ASSERT(Context);
context = Context;
result = context->Callback(HandleTableEntry, Handle, context->Parameter);
KphpUnlockHandleTableEntry(context->Dyn, HandleTable, HandleTableEntry);
return result;
}
/**
* \brief Enumerates the handles of a process.
*
* \param[in] Process The process to enumerate handles of.
* \param[in] Callback The callback to invoke for each handle table entry.
* \param[in] Parameter Optional parameter to pass to the callback.
*
* \return Successful or errant status.
*/
_IRQL_requires_max_(PASSIVE_LEVEL)
NTSTATUS KphEnumerateProcessHandlesEx(
_In_ PEPROCESS Process,
_In_ PKPH_ENUM_PROCESS_HANDLES_CALLBACK Callback,
_In_opt_ PVOID Parameter
)
{
NTSTATUS status;
PKPH_DYN dyn;
KPH_ENUM_PROC_HANDLE_EX_CONTEXT context;
PHANDLE_TABLE handleTable;
KPH_PAGED_CODE_PASSIVE();
dyn = KphReferenceDynData();
if (!dyn ||
(dyn->HtHandleContentionEvent == ULONG_MAX) ||
(dyn->EpObjectTable == ULONG_MAX))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
GENERAL,
"KphEnumerateProcessHandlesEx not supported");
status = STATUS_NOINTERFACE;
goto Exit;
}
status = KphReferenceProcessHandleTable(dyn, Process, &handleTable);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
GENERAL,
"KphReferenceProcessHandleTable failed: %!STATUS!",
status);
goto Exit;
}
context.Dyn = dyn;
context.Callback = Callback;
context.Parameter = Parameter;
ExEnumHandleTable(handleTable,
KphEnumerateProcessHandlesExCallback,
&context,
NULL);
KphDereferenceProcessHandleTable(Process);
Exit:
if (dyn)
{
KphDereferenceObject(dyn);
}
return status;
}
/**
* \brief Enumeration callback for handle enumeration.
*
* \param[in,out] HandleTableEntry Related handle table entry.
* \param[in] Handle The handle for this entry.
* \param[in] Context Enumeration context.
*
* \return FALSE
*/
_Function_class_(KPH_ENUM_PROCESS_HANDLES_CALLBACK)
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
BOOLEAN KphpEnumerateProcessHandlesCallback(
_Inout_ PHANDLE_TABLE_ENTRY HandleTableEntry,
_In_ HANDLE Handle,
_In_ PVOID Context
)
{
PKPH_ENUMERATE_PROCESS_HANDLES_CONTEXT context;
KPH_PROCESS_HANDLE handleInfo;
POBJECT_HEADER objectHeader;
POBJECT_TYPE objectType;
PKPH_PROCESS_HANDLE entryInBuffer;
KPH_MEMORY_REGION region;
KPH_PAGED_CODE_PASSIVE();
context = Context;
objectHeader = KphObpDecodeObject(context->Dyn, HandleTableEntry);
handleInfo.Handle = Handle;
handleInfo.Object = objectHeader ? &objectHeader->Body : NULL;
handleInfo.GrantedAccess = ObpDecodeGrantedAccess(HandleTableEntry->GrantedAccess);
handleInfo.ObjectTypeIndex = USHORT_MAX;
handleInfo.Reserved1 = 0;
handleInfo.HandleAttributes = KphObpGetHandleAttributes(context->Dyn,
HandleTableEntry);
handleInfo.Reserved2 = 0;
if (handleInfo.Object)
{
objectType = ObGetObjectType(handleInfo.Object);
if (objectType && (context->Dyn->OtIndex != ULONG_MAX))
{
UCHAR typeIndex;
typeIndex = *(PUCHAR)Add2Ptr(objectType, context->Dyn->OtIndex);
handleInfo.ObjectTypeIndex = (USHORT)typeIndex;
}
}
//
// Advance the current entry pointer regardless of whether the information
// will be written. This will allow the parent function to report the
// correct return length.
//
entryInBuffer = context->CurrentEntry;
context->CurrentEntry = Add2Ptr(context->CurrentEntry, sizeof(KPH_PROCESS_HANDLE));
context->Count++;
//
// Only write if we have not exceeded the buffer length. Also check for a
// potential overflow (if the process has an extremely large number of
// handles, the buffer pointer may wrap).
//
region.Start = entryInBuffer;
region.End = Add2Ptr(entryInBuffer, sizeof(KPH_PROCESS_HANDLE));
if (KphIsValidMemoryRegion(®ion, context->Buffer, context->BufferLimit))
{
context->Status = KphCopyToMode(entryInBuffer,
&handleInfo,
sizeof(KPH_PROCESS_HANDLE),
context->AccessMode);
if (!NT_SUCCESS(context->Status))
{
return TRUE;
}
}
else
{
if (context->Status == STATUS_SUCCESS)
{
context->Status = STATUS_BUFFER_TOO_SMALL;
}
}
return FALSE;
}
/**
* \brief Enumerates the handles of a process.
*
* \param[in] ProcessHandle A handle to a process.
* \param[out] Buffer The buffer in which the handle information will be stored.
* \param[in] BufferLength The number of bytes available in Buffer.
* \param[out] ReturnLength Receives the number of bytes written or required.
* \param[in] AccessMode The mode in which to perform access checks.
*
* \return Successful or errant status.
*/
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KphEnumerateProcessHandles(
_In_ HANDLE ProcessHandle,
_Out_writes_bytes_(BufferLength) PVOID Buffer,
_In_ ULONG BufferLength,
_Out_opt_ PULONG ReturnLength,
_In_ KPROCESSOR_MODE AccessMode
)
{
NTSTATUS status;
PKPH_DYN dyn;
PEPROCESS process;
KPH_ENUMERATE_PROCESS_HANDLES_CONTEXT context;
KPH_PAGED_CODE_PASSIVE();
dyn = NULL;
process = NULL;
if (!Buffer)
{
status = STATUS_INVALID_PARAMETER_2;
goto Exit;
}
dyn = KphReferenceDynData();
if (!dyn)
{
status = STATUS_NOINTERFACE;
goto Exit;
}
status = ObReferenceObjectByHandle(ProcessHandle,
0,
*PsProcessType,
AccessMode,
&process,
NULL);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
GENERAL,
"ObReferenceObjectByHandle failed: %!STATUS!",
status);
goto Exit;
}
context.Dyn = dyn;
context.AccessMode = AccessMode;
context.Buffer = Buffer;
context.BufferLimit = Add2Ptr(Buffer, BufferLength);
context.CurrentEntry = ((PKPH_PROCESS_HANDLE_INFORMATION)Buffer)->Handles;
context.Count = 0;
context.Status = STATUS_SUCCESS;
status = KphEnumerateProcessHandlesEx(process,
KphpEnumerateProcessHandlesCallback,
&context);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
GENERAL,
"KphEnumerateProcessHandlesEx failed: %!STATUS!",
status);
goto Exit;
}
if (BufferLength >= UFIELD_OFFSET(KPH_PROCESS_HANDLE_INFORMATION, Handles))
{
PKPH_PROCESS_HANDLE_INFORMATION info;
info = Buffer;
status = KphWriteULongToMode(&info->HandleCount,
context.Count,
AccessMode);
if (!NT_SUCCESS(status))
{
goto Exit;
}
}
if (ReturnLength)
{
ULONG_PTR returnLength;
status = RtlULongPtrSub((ULONG_PTR)context.CurrentEntry,
(ULONG_PTR)Buffer,
&returnLength);
if (!NT_SUCCESS(status) || (returnLength > ULONG_MAX))
{
status = STATUS_INTEGER_OVERFLOW;
goto Exit;
}
status = KphWriteULongToMode(ReturnLength,
(ULONG)returnLength,
AccessMode);
if (!NT_SUCCESS(status))
{
goto Exit;
}
}
status = context.Status;
Exit:
if (process)
{
ObDereferenceObject(process);
}
if (dyn)
{
KphDereferenceObject(dyn);
}
return status;
}
/**
* \brief Queries the name of an object.
*
* \param[in] Object A pointer to an object.
* \param[out] Buffer The buffer in which the object name will be stored.
* \param[in] BufferLength The number of bytes available in Buffer.
* \param[out] ReturnLength Receives the number of bytes written.
*
* \return Successful or errant status.
*/
_IRQL_requires_max_(APC_LEVEL)
_Must_inspect_result_
NTSTATUS KphQueryNameObject(
_In_ PVOID Object,
_Out_writes_bytes_opt_(BufferLength) POBJECT_NAME_INFORMATION Buffer,
_In_ ULONG BufferLength,
_Out_ PULONG ReturnLength
)
{
NTSTATUS status;
POBJECT_TYPE objectType;
KPH_PAGED_CODE();
objectType = ObGetObjectType(Object);
if (objectType == *IoFileObjectType)
{
status = KphQueryNameFileObject(Object,
Buffer,
BufferLength,
ReturnLength);
KphTracePrint(TRACE_LEVEL_VERBOSE,
GENERAL,
"KphQueryNameFileObject: %!STATUS!",
status);
}
else
{
status = ObQueryNameString(Object, Buffer, BufferLength, ReturnLength);
KphTracePrint(TRACE_LEVEL_VERBOSE,
GENERAL,
"ObQueryNameString: %!STATUS!",
status);
}
//
// Make the error returns consistent.
//
if ((status == STATUS_BUFFER_OVERFLOW) || // returned by I/O subsystem
(status == STATUS_INFO_LENGTH_MISMATCH)) // returned by ObQueryNameString
{
status = STATUS_BUFFER_TOO_SMALL;
}
if (NT_SUCCESS(status))
{
NT_ASSERT(Buffer);
KphTracePrint(TRACE_LEVEL_VERBOSE,
GENERAL,
"KphQueryNameObject: %wZ",
&Buffer->Name);
}
else
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
GENERAL,
"KphQueryNameObject: %!STATUS!",
status);
}
return status;
}
/**
* \brief Extracts the name of a file object by retrieving the device name,
* walking the related file objects, and the target file object.
*
* \param[in] FileObject A pointer to a file object.
* \param[out] Buffer The buffer in which the object name will be stored.
* \param[in] BufferLength The number of bytes available in Buffer.
* \param[out] ReturnLength Receives the number of bytes written or required.
*
* \return Successful or errant status.
*/
_IRQL_requires_max_(APC_LEVEL)
_Must_inspect_result_
NTSTATUS KphpExtractNameFileObject(
_In_ PFILE_OBJECT FileObject,
_Out_writes_bytes_opt_(BufferLength) POBJECT_NAME_INFORMATION Buffer,
_In_ ULONG BufferLength,
_Out_ PULONG ReturnLength
)
{
NTSTATUS status;
ULONG returnLength;
PFILE_OBJECT fileObject;
PUCHAR pos;
USHORT len;
KPH_PAGED_CODE();
*ReturnLength = 0;
if (!Buffer && BufferLength)
{
return STATUS_INVALID_PARAMETER;
}
if (FlagOn(FileObject->Flags, FO_CLEANUP_COMPLETE))
{
KphTracePrint(TRACE_LEVEL_VERBOSE, GENERAL, "File object closed");
return STATUS_FILE_CLOSED;
}
if (FileObject->DeviceObject)
{
returnLength = 0;
status = ObQueryNameString(FileObject->DeviceObject,
Buffer,
BufferLength,
&returnLength);
if (NT_SUCCESS(status))
{
NT_ASSERT(Buffer && BufferLength);
NT_ASSERT(returnLength >= sizeof(OBJECT_NAME_INFORMATION));
NT_ASSERT(Buffer->Name.Buffer == Add2Ptr(Buffer, sizeof(OBJECT_NAME_INFORMATION)));
}
else if (status == STATUS_INFO_LENGTH_MISMATCH)
{
NT_ASSERT(returnLength);
}
else
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
GENERAL,
"ObQueryNameString failed: %!STATUS!",
status);
return status;
}
}
else
{
if (Buffer && (BufferLength >= sizeof(OBJECT_NAME_INFORMATION)))
{
Buffer->Name.Length = 0;
Buffer->Name.Buffer = Add2Ptr(Buffer, sizeof(OBJECT_NAME_INFORMATION));
}
returnLength = sizeof(OBJECT_NAME_INFORMATION);
}
//
// Walk the file object and related objects. This is walking the name in
// reverse order. Place the name at the end of the buffer to be moved into
// place afterwards. If along the way we exhaust the buffer we'll continue
// to calculate the needed length but not write to the buffer.
//
fileObject = FileObject;
pos = Buffer ? Add2Ptr(Buffer, BufferLength) : NULL;
len = 0;
do
{
//
// N.B. There are some drivers that aren't as tidy with their related
// file objects (condrv.sys) and leave a dangling pointer. Admittedly
// we're doing a "non-blessed" thing by extracting the name like this.
// So we sanity check that the pointer is a valid file object to work
// around this, it's not perfect but we get value from this routine
// for cases where extracting the full name is otherwise impossible.
//
if ((ObGetObjectType(fileObject) != *IoFileObjectType) ||
FlagOn(fileObject->Flags, FO_CLEANUP_COMPLETE))
{
break;
}
status = RtlULongAdd(returnLength,
fileObject->FileName.Length,
&returnLength);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
GENERAL,
"RtlULongAdd failed: %!STATUS!",
status);
return status;
}
status = RtlUShortAdd(len, fileObject->FileName.Length, &len);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
GENERAL,
"RtlUShortAdd failed: %!STATUS!",
status);
return status;
}
if (Buffer && (returnLength <= BufferLength))
{
pos -= fileObject->FileName.Length;
RtlCopyMemory(pos,
fileObject->FileName.Buffer,
fileObject->FileName.Length);
}
fileObject = fileObject->RelatedFileObject;
} while (fileObject);
*ReturnLength = returnLength;
if (!Buffer || (returnLength > BufferLength))
{
return STATUS_BUFFER_TOO_SMALL;
}
RtlMoveMemory(Add2Ptr(Buffer->Name.Buffer, Buffer->Name.Length), pos, len);
status = RtlUShortAdd(Buffer->Name.Length, len, &Buffer->Name.Length);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
GENERAL,
"RtlUShortAdd failed: %!STATUS!",
status);
return status;
}
if ((BufferLength - returnLength) > sizeof(UNICODE_NULL))
{
Buffer->Name.Buffer[Buffer->Name.Length / sizeof(WCHAR)] = UNICODE_NULL;
returnLength += sizeof(UNICODE_NULL);
}
NT_ASSERT(returnLength >= sizeof(OBJECT_NAME_INFORMATION));
returnLength -= sizeof(OBJECT_NAME_INFORMATION);
NT_ASSERT(returnLength <= USHORT_MAX);
Buffer->Name.MaximumLength = (USHORT)returnLength;
return STATUS_SUCCESS;
}
/**
* \brief Queries the name of a file object.
*
* \param[in] FileObject A pointer to a file object.
* \param[out] Buffer The buffer in which the object name will be stored.
* \param[in] BufferLength The number of bytes available in Buffer.
* \param[out] ReturnLength Receives the number of bytes written or required.
*
* \return Successful or errant status.
*/
_IRQL_requires_max_(APC_LEVEL)
_Must_inspect_result_
NTSTATUS KphQueryNameFileObject(
_In_ PFILE_OBJECT FileObject,
_Out_writes_bytes_opt_(BufferLength) POBJECT_NAME_INFORMATION Buffer,
_In_ ULONG BufferLength,
_Out_ PULONG ReturnLength
)
{
NTSTATUS status;
PFLT_FILE_NAME_INFORMATION fileNameInfo;
FLT_FILE_NAME_OPTIONS nameOptions;
KPH_PAGED_CODE();
nameOptions = FLT_FILE_NAME_NORMALIZED;
if (IoGetTopLevelIrp() || KeAreAllApcsDisabled())
{
nameOptions |= FLT_FILE_NAME_QUERY_CACHE_ONLY;
}
else
{
nameOptions |= FLT_FILE_NAME_QUERY_ALWAYS_ALLOW_CACHE_LOOKUP;
}
status = FltGetFileNameInformationUnsafe(FileObject,
NULL,
nameOptions,
&fileNameInfo);
if (!NT_SUCCESS(status))
{
fileNameInfo = NULL;
status = KphpExtractNameFileObject(FileObject,
Buffer,
BufferLength,
ReturnLength);
goto Exit;
}
*ReturnLength = sizeof(OBJECT_NAME_INFORMATION);
*ReturnLength += fileNameInfo->Name.Length;
if (!Buffer || (BufferLength < *ReturnLength))
{
status = STATUS_BUFFER_TOO_SMALL;
goto Exit;
}
Buffer->Name.Length = 0;
Buffer->Name.MaximumLength = (USHORT)(BufferLength - sizeof(OBJECT_NAME_INFORMATION));
Buffer->Name.Buffer = (PWSTR)Add2Ptr(Buffer, sizeof(OBJECT_NAME_INFORMATION));
RtlCopyUnicodeString(&Buffer->Name, &fileNameInfo->Name);
Exit:
if (fileNameInfo)
{
FltReleaseFileNameInformation(fileNameInfo);
}
return status;
}
/**
* \brief Queries object information.
*
* \param[in] ProcessHandle A handle to a process.
* \param[in] Handle A handle which is present in the process.
* \param[in] ObjectInformationClass The type of information to retrieve.
* \param[out] ObjectInformation Information buffer to populate.
* \param[in] ObjectInformationLength Length of the information buffer in bytes.
* \param[out] ReturnLength Receives the number of bytes written or required.
* \param[in] AccessMode The mode in which to perform access checks.
*
* \return Successful or errant status.
*/
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KphQueryInformationObject(
_In_ HANDLE ProcessHandle,
_In_ HANDLE Handle,
_In_ KPH_OBJECT_INFORMATION_CLASS ObjectInformationClass,
_Out_writes_bytes_opt_(ObjectInformationLength) PVOID ObjectInformation,
_In_ ULONG ObjectInformationLength,
_Out_opt_ PULONG ReturnLength,
_In_ KPROCESSOR_MODE AccessMode
)
{
NTSTATUS status;
PKPH_DYN dyn;
PEPROCESS process;
KAPC_STATE apcState;
PVOID object;
ULONG returnLength;
PVOID buffer;
BYTE stackBuffer[64];
KPROCESSOR_MODE accessMode;
PKPH_PROCESS_CONTEXT processContext;
KPH_PAGED_CODE_PASSIVE();
dyn = NULL;
returnLength = 0;
object = NULL;
buffer = NULL;
processContext = NULL;
status = ObReferenceObjectByHandle(ProcessHandle,
0,
*PsProcessType,
AccessMode,
&process,
NULL);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
GENERAL,
"ObReferenceObjectByHandle failed %!STATUS!",
status);
goto Exit;
}
if (process == PsInitialSystemProcess)
{
Handle = MakeKernelHandle(Handle);
accessMode = KernelMode;
}
else
{
if (IsKernelHandle(Handle) && !IsPseudoHandle(Handle))
{
status = STATUS_INVALID_HANDLE;
goto Exit;
}
accessMode = AccessMode;
}
switch (ObjectInformationClass)
{
case KphObjectBasicInformation:
{
OBJECT_BASIC_INFORMATION basicInfo;
if (!ObjectInformation ||
(ObjectInformationLength < sizeof(OBJECT_BASIC_INFORMATION)))
{
status = STATUS_INFO_LENGTH_MISMATCH;
returnLength = sizeof(OBJECT_BASIC_INFORMATION);
goto Exit;
}
KeStackAttachProcess(process, &apcState);
status = ZwQueryObject(Handle,
ObjectBasicInformation,
&basicInfo,
sizeof(OBJECT_BASIC_INFORMATION),
NULL);
KeUnstackDetachProcess(&apcState);
if (NT_SUCCESS(status))
{
status = KphCopyToMode(ObjectInformation,
&basicInfo,
sizeof(OBJECT_BASIC_INFORMATION),
AccessMode);
if (NT_SUCCESS(status))
{
returnLength = sizeof(OBJECT_BASIC_INFORMATION);
}
}
break;
}
case KphObjectNameInformation:
{
ULONG allocateSize;
POBJECT_NAME_INFORMATION nameInfo;
KeStackAttachProcess(process, &apcState);
status = ObReferenceObjectByHandle(Handle,
0,
NULL,
accessMode,
&object,
NULL);
KeUnstackDetachProcess(&apcState);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
GENERAL,
"ObReferenceObjectByHandle failed: %!STATUS!",
status);
object = NULL;
goto Exit;
}
allocateSize = ObjectInformationLength;
if (allocateSize < sizeof(OBJECT_NAME_INFORMATION))
{
allocateSize = sizeof(OBJECT_NAME_INFORMATION);
}
buffer = KphAllocatePagedA(allocateSize,
KPH_TAG_OBJECT_QUERY,
stackBuffer);
if (!buffer)
{
status = STATUS_INSUFFICIENT_RESOURCES;
goto Exit;
}
nameInfo = (POBJECT_NAME_INFORMATION)buffer;
status = KphQueryNameObject(object,
nameInfo,
ObjectInformationLength,
&returnLength);
if (NT_SUCCESS(status))
{
if (!ObjectInformation)
{
status = STATUS_BUFFER_TOO_SMALL;
goto Exit;
}
RebaseUnicodeString(&nameInfo->Name,
nameInfo,
ObjectInformation);
status = KphCopyToMode(ObjectInformation,
nameInfo,
returnLength,
AccessMode);
}
break;
}
case KphObjectTypeInformation:
{
ULONG allocateSize;
POBJECT_TYPE_INFORMATION typeInfo;
returnLength = sizeof(OBJECT_TYPE_INFORMATION);
allocateSize = ObjectInformationLength;
if (allocateSize < sizeof(OBJECT_TYPE_INFORMATION))
{
allocateSize = sizeof(OBJECT_TYPE_INFORMATION);
}
//
// ObQueryTypeInfo uses ObjectType->Name.MaximumLength instead of
// ObjectType->Name.Length + sizeof(WCHAR) to calculate the
// required buffer size. In Windows 8, certain object types
// (e.g. TmTx) do NOT include the null terminator in MaximumLength,
// which causes ObQueryTypeInfo to overrun the given buffer. To
// work around this bug, we add some (generous) padding to our
// allocation.
//
allocateSize += sizeof(ULONG64);
buffer = KphAllocatePagedA(allocateSize,
KPH_TAG_OBJECT_QUERY,
stackBuffer);
if (!buffer)
{
status = STATUS_INSUFFICIENT_RESOURCES;
goto Exit;
}
typeInfo = (POBJECT_TYPE_INFORMATION)buffer;
KeStackAttachProcess(process, &apcState);
status = ZwQueryObject(Handle,
ObjectTypeInformation,
typeInfo,
ObjectInformationLength,
&returnLength);
KeUnstackDetachProcess(&apcState);
if (NT_SUCCESS(status))
{
if (!ObjectInformation)
{
status = STATUS_BUFFER_TOO_SMALL;
goto Exit;
}
RebaseUnicodeString(&typeInfo->TypeName,
typeInfo,
ObjectInformation);
status = KphCopyToMode(ObjectInformation,
typeInfo,
returnLength,
AccessMode);
}
break;
}
case KphObjectHandleFlagInformation:
{
OBJECT_HANDLE_FLAG_INFORMATION handleFlagInfo;
if (!ObjectInformation ||
(ObjectInformationLength < sizeof(OBJECT_HANDLE_FLAG_INFORMATION)))
{
status = STATUS_INFO_LENGTH_MISMATCH;
returnLength = sizeof(OBJECT_HANDLE_FLAG_INFORMATION);
goto Exit;
}
KeStackAttachProcess(process, &apcState);
status = ZwQueryObject(Handle,
ObjectHandleFlagInformation,
&handleFlagInfo,
sizeof(OBJECT_HANDLE_FLAG_INFORMATION),
NULL);
KeUnstackDetachProcess(&apcState);
if (NT_SUCCESS(status))
{
status = KphCopyToMode(ObjectInformation,
&handleFlagInfo,
sizeof(OBJECT_HANDLE_FLAG_INFORMATION),
AccessMode);
if (NT_SUCCESS(status))
{
returnLength = sizeof(OBJECT_HANDLE_FLAG_INFORMATION);
}
}
break;
}
case KphObjectProcessBasicInformation:
{
PROCESS_BASIC_INFORMATION basicInfo;
if (!ObjectInformation ||
(ObjectInformationLength < sizeof(PROCESS_BASIC_INFORMATION)))
{
status = STATUS_INFO_LENGTH_MISMATCH;
returnLength = sizeof(PROCESS_BASIC_INFORMATION);
goto Exit;
}
KeStackAttachProcess(process, &apcState);
status = ZwQueryInformationProcess(Handle,
ProcessBasicInformation,
&basicInfo,
sizeof(PROCESS_BASIC_INFORMATION),
NULL);
KeUnstackDetachProcess(&apcState);
if (NT_SUCCESS(status))
{
status = KphCopyToMode(ObjectInformation,
&basicInfo,
sizeof(PROCESS_BASIC_INFORMATION),
AccessMode);
if (NT_SUCCESS(status))
{
returnLength = sizeof(PROCESS_BASIC_INFORMATION);
}
}
break;
}
case KphObjectThreadBasicInformation:
{
THREAD_BASIC_INFORMATION basicInfo;
if (!ObjectInformation ||
(ObjectInformationLength < sizeof(THREAD_BASIC_INFORMATION)))
{
status = STATUS_INFO_LENGTH_MISMATCH;
returnLength = sizeof(THREAD_BASIC_INFORMATION);
goto Exit;
}
KeStackAttachProcess(process, &apcState);
status = ZwQueryInformationThread(Handle,
ThreadBasicInformation,
&basicInfo,
sizeof(THREAD_BASIC_INFORMATION),
NULL);
KeUnstackDetachProcess(&apcState);
if (NT_SUCCESS(status))
{
status = KphCopyToMode(ObjectInformation,
&basicInfo,
sizeof(THREAD_BASIC_INFORMATION),
AccessMode);
if (NT_SUCCESS(status))
{
returnLength = sizeof(THREAD_BASIC_INFORMATION);
}
}
break;
}
case KphObjectEtwRegBasicInformation:
{
PVOID objectType;
PUNICODE_STRING objectTypeName;
PVOID guidEntry;
KPH_ETWREG_BASIC_INFORMATION basicInfo;
dyn = KphReferenceDynData();
if (!dyn ||
(dyn->EgeGuid == ULONG_MAX) ||
(dyn->EreGuidEntry == ULONG_MAX) ||
(dyn->OtName == ULONG_MAX))
{
status = STATUS_NOINTERFACE;
goto Exit;
}
if (!ObjectInformation ||
(ObjectInformationLength < sizeof(KPH_ETWREG_BASIC_INFORMATION)))
{
status = STATUS_INFO_LENGTH_MISMATCH;
returnLength = sizeof(KPH_ETWREG_BASIC_INFORMATION);
goto Exit;
}
KeStackAttachProcess(process, &apcState);
status = ObReferenceObjectByHandle(Handle,
0,
NULL,
accessMode,
&object,
NULL);
KeUnstackDetachProcess(&apcState);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
GENERAL,
"ObReferenceObjectByHandle failed: %!STATUS!",
status);
object = NULL;
goto Exit;
}
objectType = ObGetObjectType(object);
if (!objectType)
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
GENERAL,
"ObGetObjectType returned null");
status = STATUS_NOT_SUPPORTED;
goto Exit;
}
objectTypeName = (PUNICODE_STRING)Add2Ptr(objectType, dyn->OtName);
if (!RtlEqualUnicodeString(objectTypeName,
&KphpEtwRegistrationName,
FALSE))
{
status = STATUS_OBJECT_TYPE_MISMATCH;
goto Exit;
}
guidEntry = *(PVOID*)Add2Ptr(object, dyn->EreGuidEntry);
if (guidEntry)
{
RtlCopyMemory(&basicInfo.Guid,
Add2Ptr(guidEntry, dyn->EgeGuid),
sizeof(GUID));
}
else
{
RtlZeroMemory(&basicInfo.Guid, sizeof(GUID));
}
//
// Not implemented.
//
basicInfo.SessionId = 0;
status = KphCopyToMode(ObjectInformation,
&basicInfo,
sizeof(KPH_ETWREG_BASIC_INFORMATION),
AccessMode);
if (NT_SUCCESS(status))
{
returnLength = sizeof(KPH_ETWREG_BASIC_INFORMATION);
}
break;
}
case KphObjectFileObjectInformation:
{
KPH_FILE_OBJECT_INFORMATION fileInfo;
if (!ObjectInformation ||
(ObjectInformationLength < sizeof(KPH_FILE_OBJECT_INFORMATION)))
{
status = STATUS_INFO_LENGTH_MISMATCH;
returnLength = sizeof(KPH_FILE_OBJECT_INFORMATION);
goto Exit;
}
KeStackAttachProcess(process, &apcState);
status = ObReferenceObjectByHandle(Handle,
0,
*IoFileObjectType,
accessMode,
&object,
NULL);
KeUnstackDetachProcess(&apcState);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
GENERAL,
"ObReferenceObjectByHandle failed: %!STATUS!",
status);
object = NULL;
goto Exit;
}
KphpGetFileObjectInformation(object, &fileInfo);
status = KphCopyToMode(ObjectInformation,
&fileInfo,
sizeof(KPH_FILE_OBJECT_INFORMATION),
AccessMode);
if (NT_SUCCESS(status))
{
returnLength = sizeof(KPH_FILE_OBJECT_INFORMATION);
}
break;
}
case KphObjectFileObjectDriver:
{
PFILE_OBJECT fileObject;
HANDLE driverHandle;
if (!ObjectInformation ||
(ObjectInformationLength < sizeof(HANDLE)))
{
status = STATUS_INFO_LENGTH_MISMATCH;
returnLength = sizeof(HANDLE);
goto Exit;
}
KeStackAttachProcess(process, &apcState);
status = ObReferenceObjectByHandle(Handle,
0,
*IoFileObjectType,
accessMode,
&object,
NULL);
KeUnstackDetachProcess(&apcState);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
GENERAL,
"ObReferenceObjectByHandle failed: %!STATUS!",
status);
object = NULL;
goto Exit;
}
fileObject = (PFILE_OBJECT)object;
if (!fileObject->DeviceObject ||
!fileObject->DeviceObject->DriverObject)
{
status = STATUS_INVALID_DEVICE_REQUEST;
goto Exit;
}
status = ObOpenObjectByPointer(fileObject->DeviceObject->DriverObject,
(AccessMode ? 0 : OBJ_KERNEL_HANDLE),
NULL,
SYNCHRONIZE,
*IoDriverObjectType,
AccessMode,
&driverHandle);
if (NT_SUCCESS(status))
{
status = KphWriteHandleToMode(ObjectInformation,
driverHandle,
AccessMode);
if (NT_SUCCESS(status))
{
returnLength = sizeof(HANDLE);
}
}
break;
}
case KphObjectProcessTimes:
{
KERNEL_USER_TIMES times;
if (!ObjectInformation ||
(ObjectInformationLength < sizeof(KERNEL_USER_TIMES)))
{
status = STATUS_INFO_LENGTH_MISMATCH;
returnLength = sizeof(KERNEL_USER_TIMES);
goto Exit;
}
KeStackAttachProcess(process, &apcState);
status = ZwQueryInformationProcess(Handle,
ProcessTimes,
×,
sizeof(KERNEL_USER_TIMES),
NULL);
KeUnstackDetachProcess(&apcState);
if (NT_SUCCESS(status))
{
status = KphCopyToMode(ObjectInformation,
×,
sizeof(KERNEL_USER_TIMES),
AccessMode);
if (NT_SUCCESS(status))
{
returnLength = sizeof(KERNEL_USER_TIMES);
}
}
break;
}
case KphObjectThreadTimes:
{
KERNEL_USER_TIMES times;
if (!ObjectInformation ||
(ObjectInformationLength < sizeof(KERNEL_USER_TIMES)))
{
status = STATUS_INFO_LENGTH_MISMATCH;
returnLength = sizeof(KERNEL_USER_TIMES);
goto Exit;
}
KeStackAttachProcess(process, &apcState);
status = ZwQueryInformationThread(Handle,
ThreadTimes,
×,
sizeof(KERNEL_USER_TIMES),
NULL);
KeUnstackDetachProcess(&apcState);
if (NT_SUCCESS(status))
{
status = KphCopyToMode(ObjectInformation,
×,
sizeof(KERNEL_USER_TIMES),
AccessMode);
if (NT_SUCCESS(status))
{
returnLength = sizeof(KERNEL_USER_TIMES);
}
}
break;
}
case KphObjectProcessImageFileName:
{
PEPROCESS targetProcess;
KeStackAttachProcess(process, &apcState);
status = ObReferenceObjectByHandle(Handle,
0,
*PsProcessType,
accessMode,
&targetProcess,
NULL);
KeUnstackDetachProcess(&apcState);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
GENERAL,
"ObReferenceObjectByHandle failed: %!STATUS!",
status);
goto Exit;
}
processContext = KphGetEProcessContext(targetProcess);
ObDereferenceObject(targetProcess);
if (!processContext || !processContext->ImageFileName)
{
status = STATUS_INSUFFICIENT_RESOURCES;
goto Exit;
}
status = KphCopyUnicodeStringToMode(ObjectInformation,
ObjectInformationLength,
processContext->ImageFileName,
&returnLength,
AccessMode);
break;
}
case KphObjectThreadNameInformation:
{
ULONG allocateSize;
PTHREAD_NAME_INFORMATION nameInfo;
returnLength = sizeof(THREAD_NAME_INFORMATION);
allocateSize = ObjectInformationLength;
if (allocateSize < sizeof(THREAD_NAME_INFORMATION))
{
allocateSize = sizeof(THREAD_NAME_INFORMATION);
}
allocateSize += sizeof(ULONG64);
buffer = KphAllocatePagedA(allocateSize,
KPH_TAG_OBJECT_QUERY,
stackBuffer);
if (!buffer)
{
status = STATUS_INSUFFICIENT_RESOURCES;
goto Exit;
}
nameInfo = (PTHREAD_NAME_INFORMATION)buffer;
KeStackAttachProcess(process, &apcState);
status = ZwQueryInformationThread(Handle,
ThreadNameInformation,
nameInfo,
ObjectInformationLength,
&returnLength);
KeUnstackDetachProcess(&apcState);
if (NT_SUCCESS(status))
{
if (!ObjectInformation)
{
status = STATUS_BUFFER_TOO_SMALL;
goto Exit;
}
RebaseUnicodeString(&nameInfo->ThreadName,
nameInfo,
ObjectInformation);
status = KphCopyToMode(ObjectInformation,
nameInfo,
returnLength,
AccessMode);
}
break;
}
case KphObjectThreadIsTerminated:
{
ULONG threadIsTerminated;
if (!ObjectInformation || (ObjectInformationLength < sizeof(ULONG)))
{
status = STATUS_INFO_LENGTH_MISMATCH;
returnLength = sizeof(ULONG);
goto Exit;
}
KeStackAttachProcess(process, &apcState);
status = ZwQueryInformationThread(Handle,
ThreadIsTerminated,
&threadIsTerminated,
sizeof(ULONG),
NULL);
KeUnstackDetachProcess(&apcState);
if (NT_SUCCESS(status))
{
status = KphWriteULongToMode(ObjectInformation,
threadIsTerminated,
AccessMode);
if (NT_SUCCESS(status))
{
returnLength = sizeof(ULONG);
}
}
break;
}
case KphObjectSectionBasicInformation:
case KphObjectSectionImageInformation:
case KphObjectSectionRelocationInformation:
case KphObjectSectionOriginalBaseInformation:
case KphObjectSectionInternalImageInformation:
case KphObjectSectionMappingsInformation:
{
if (ObjectInformation)
{
buffer = KphAllocatePagedA(ObjectInformationLength,
KPH_TAG_OBJECT_QUERY,
stackBuffer);
if (!buffer)
{
status = STATUS_INSUFFICIENT_RESOURCES;
goto Exit;
}
}
else
{
NT_ASSERT(!buffer);
ObjectInformationLength = 0;
}
if (ObjectInformationClass == KphObjectSectionMappingsInformation)
{
KeStackAttachProcess(process, &apcState);
status = KphQuerySection(Handle,
KphSectionMappingsInformation,
buffer,
ObjectInformationLength,
&returnLength,
KernelMode);
KeUnstackDetachProcess(&apcState);
if (NT_SUCCESS(status))
{
status = KphCopyToMode(ObjectInformation,
buffer,
returnLength,
AccessMode);
}
}
else
{
SIZE_T length;
SECTION_INFORMATION_CLASS sectionInfoClass;
if (ObjectInformationClass == KphObjectSectionBasicInformation)
{
sectionInfoClass = SectionBasicInformation;
}
else if (ObjectInformationClass == KphObjectSectionImageInformation)
{
sectionInfoClass = SectionImageInformation;
}
else if (ObjectInformationClass == KphObjectSectionRelocationInformation)
{
sectionInfoClass = SectionRelocationInformation;
}
else if (ObjectInformationClass == KphObjectSectionOriginalBaseInformation)
{
sectionInfoClass = SectionOriginalBaseInformation;
}
else
{
NT_ASSERT(ObjectInformationClass == KphObjectSectionInternalImageInformation);
sectionInfoClass = SectionInternalImageInformation;
}
length = 0;
KeStackAttachProcess(process, &apcState);
status = ZwQuerySection(Handle,
sectionInfoClass,
buffer,
ObjectInformationLength,
&length);
KeUnstackDetachProcess(&apcState);
if (NT_SUCCESS(status))
{
if (length > ULONG_MAX)
{
status = STATUS_INTEGER_OVERFLOW;
returnLength = 0;
goto Exit;
}
status = KphCopyToMode(ObjectInformation,
buffer,
length,
AccessMode);
if (NT_SUCCESS(status))
{
returnLength = (ULONG)length;
}
}
}
break;
}
case KphObjectSectionFileName:
{
PUNICODE_STRING sectionFileName;
ULONG allocateSize;
HANDLE sectionHandle;
PVOID baseAddress;
SIZE_T viewSize;
returnLength = sizeof(UNICODE_STRING);
allocateSize = ObjectInformationLength;
if (allocateSize < sizeof(UNICODE_STRING))
{
allocateSize = sizeof(UNICODE_STRING);
}
allocateSize += sizeof(ULONG64);
buffer = KphAllocatePagedA(allocateSize,
KPH_TAG_OBJECT_QUERY,
stackBuffer);
if (!buffer)
{
status = STATUS_INSUFFICIENT_RESOURCES;
goto Exit;
}
sectionFileName = (PUNICODE_STRING)buffer;
status = ObDuplicateObject(process,
Handle,
PsInitialSystemProcess,
§ionHandle,
SECTION_QUERY | SECTION_MAP_READ,
OBJ_KERNEL_HANDLE,
0,
KernelMode);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
GENERAL,
"ObDuplicateObject failed: %!STATUS!",
status);
goto Exit;
}
KeStackAttachProcess(PsInitialSystemProcess, &apcState);
baseAddress = NULL;
viewSize = PAGE_SIZE;
status = ZwMapViewOfSection(sectionHandle,
ZwCurrentProcess(),
&baseAddress,
0,
0,
NULL,
&viewSize,
ViewUnmap,
0,
PAGE_READONLY);
if (NT_SUCCESS(status))
{
SIZE_T length;
length = 0;
status = ZwQueryVirtualMemory(ZwCurrentProcess(),
baseAddress,
MemoryMappedFilenameInformation,
sectionFileName,
ObjectInformationLength,
&length);
if (length > ULONG_MAX)
{
status = STATUS_INTEGER_OVERFLOW;
returnLength = 0;
}
else
{
returnLength = (ULONG)length;
}
ZwUnmapViewOfSection(ZwCurrentProcess(), baseAddress);
}
KeUnstackDetachProcess(&apcState);
ObCloseHandle(sectionHandle, KernelMode);
if (NT_SUCCESS(status))
{
if (!ObjectInformation)
{
status = STATUS_BUFFER_TOO_SMALL;
goto Exit;
}
RebaseUnicodeString(sectionFileName,
sectionFileName,
ObjectInformation);
status = KphCopyToMode(ObjectInformation,
sectionFileName,
returnLength,
AccessMode);
}
break;
}
case KphObjectAttributesInformation:
{
PKPH_OBJECT_ATTRIBUTES_INFORMATION attributesInfo;
if (!ObjectInformation ||
(ObjectInformationLength < sizeof(KPH_OBJECT_ATTRIBUTES_INFORMATION)))
{
status = STATUS_INFO_LENGTH_MISMATCH;
returnLength = sizeof(KPH_OBJECT_ATTRIBUTES_INFORMATION);
goto Exit;
}
KeStackAttachProcess(process, &apcState);
status = ObReferenceObjectByHandle(Handle,
0,
NULL,
accessMode,
&object,
NULL);
KeUnstackDetachProcess(&apcState);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
GENERAL,
"ObReferenceObjectByHandle failed: %!STATUS!",
status);
object = NULL;
goto Exit;
}
attributesInfo = ObjectInformation;
C_ASSERT(sizeof(KPH_OBJECT_ATTRIBUTES_INFORMATION) == sizeof(UCHAR));
status = KphWriteUCharToMode(&attributesInfo->Flags,
OBJECT_TO_OBJECT_HEADER(object)->Flags,
AccessMode);
if (NT_SUCCESS(status))
{
returnLength = sizeof(KPH_OBJECT_ATTRIBUTES_INFORMATION);
}
break;
}
default:
{
status = STATUS_INVALID_INFO_CLASS;
break;
}
}
Exit:
if (processContext)
{
KphDereferenceObject(processContext);
}
if (buffer)
{
KphFreeA(buffer, KPH_TAG_OBJECT_QUERY, stackBuffer);
}
if (object)
{
ObDereferenceObject(object);
}
if (process)
{
ObDereferenceObject(process);
}
if (dyn)
{
KphDereferenceObject(dyn);
}
if (ReturnLength)
{
KphWriteULongToMode(ReturnLength, returnLength, AccessMode);
}
return status;
}
/**
* \brief Sets object information.
*
* \param[in] ProcessHandle A handle to a process.
* \param[in] Handle A handle which is present in the process.
* \param[in] ObjectInformationClass The type of information to set.
* \param[in] ObjectInformation A buffer which contains the information to set.
* \param[in] ObjectInformationLength Length of the information buffer in bytes.
* \param[in] AccessMode The mode in which to perform access checks.
*
* \return Successful or errant status.
*/
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KphSetInformationObject(
_In_ HANDLE ProcessHandle,
_In_ HANDLE Handle,
_In_ KPH_OBJECT_INFORMATION_CLASS ObjectInformationClass,
_In_reads_bytes_(ObjectInformationLength) PVOID ObjectInformation,
_In_ ULONG ObjectInformationLength,
_In_ KPROCESSOR_MODE AccessMode
)
{
NTSTATUS status;
PVOID objectInformation;
BYTE stackBuffer[64];
PEPROCESS process;
KAPC_STATE apcState;
KPH_PAGED_CODE_PASSIVE();
objectInformation = NULL;
process = NULL;
if (!ObjectInformation)
{
status = STATUS_INVALID_PARAMETER_4;
goto Exit;
}
if (AccessMode != KernelMode)
{
objectInformation = KphAllocatePagedA(ObjectInformationLength,
KPH_TAG_OBJECT_INFO,
stackBuffer);
if (!objectInformation)
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
GENERAL,
"Failed to allocate object info buffer.");
status = STATUS_INSUFFICIENT_RESOURCES;
goto Exit;
}
__try
{
CopyFromUser(objectInformation,
ObjectInformation,
ObjectInformationLength);
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
status = GetExceptionCode();
goto Exit;
}
}
else
{
objectInformation = ObjectInformation;
}
status = ObReferenceObjectByHandle(ProcessHandle,
PROCESS_SET_INFORMATION,
*PsProcessType,
AccessMode,
&process,
NULL);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
GENERAL,
"ObReferenceObjectByHandle failed: %!STATUS!",
status);
process = NULL;
goto Exit;
}
if (process == PsInitialSystemProcess)
{
Handle = MakeKernelHandle(Handle);
}
else
{
if (IsKernelHandle(Handle))
{
status = STATUS_INVALID_HANDLE;
goto Exit;
}
}
switch (ObjectInformationClass)
{
case KphObjectHandleFlagInformation:
{
if (ObjectInformationLength < sizeof(OBJECT_HANDLE_FLAG_INFORMATION))
{
status = STATUS_INFO_LENGTH_MISMATCH;
goto Exit;
}
KeStackAttachProcess(process, &apcState);
status = ObSetHandleAttributes(Handle, objectInformation, KernelMode);
KeUnstackDetachProcess(&apcState);
break;
}
default:
{
status = STATUS_INVALID_INFO_CLASS;
break;
}
}
Exit:
if (objectInformation && (objectInformation != ObjectInformation))
{
KphFreeA(objectInformation, KPH_TAG_OBJECT_INFO, stackBuffer);
}
if (process)
{
ObDereferenceObject(process);
}
return status;
}
/**
* \brief Opens a named object.
*
* \param[out] ObjectHandle Set to the opened handle.
* \param[in] DesiredAccess Desires access to the object.
* \param[in] ObjectAttributes Attributes to open the object.
* \param[in] ObjectType Type of object to open.
* \param[in] AccessMode The mode in which to perform access checks.
*
* \return Successful or errant status.
*/
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KphOpenNamedObject(
_Out_ PHANDLE ObjectHandle,
_In_ ACCESS_MASK DesiredAccess,
_In_ POBJECT_ATTRIBUTES ObjectAttributes,
_In_ POBJECT_TYPE ObjectType,
_In_ KPROCESSOR_MODE AccessMode
)
{
NTSTATUS status;
HANDLE objectHandle;
KPH_PAGED_CODE_PASSIVE();
status = ObOpenObjectByName(ObjectAttributes,
ObjectType,
AccessMode,
NULL,
DesiredAccess,
NULL,
&objectHandle);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
GENERAL,
"ObOpenObjectByName failed: %!STATUS!",
status);
objectHandle = NULL;
goto Exit;
}
status = KphWriteHandleToMode(ObjectHandle, objectHandle, AccessMode);
Exit:
return status;
}
/**
* \brief Duplicates an object.
*
* \param[in] ProcessHandle Handle to process where the source handle exists.
* \param[in] SourceHandle Source handle in the target process.
* \param[in] DesiredAccess Desired access for duplicated handle.
* \param[out] TargetHandle Populated with the duplicated handle.
* \param[in] AccessMode The mode in which to perform access checks.
*
* \return Successful or errant status.
*/
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KphDuplicateObject(
_In_ HANDLE ProcessHandle,
_In_ HANDLE SourceHandle,
_In_ ACCESS_MASK DesiredAccess,
_Out_ PHANDLE TargetHandle,
_In_ KPROCESSOR_MODE AccessMode
)
{
NTSTATUS status;
PEPROCESS sourceProcess;
PEPROCESS targetProcess;
HANDLE targetHandle;
KPH_PAGED_CODE_PASSIVE();
targetProcess = NULL;
sourceProcess = NULL;
if (DesiredAccess & ~(READ_CONTROL | ACCESS_SYSTEM_SECURITY | SYNCHRONIZE))
{
status = STATUS_ACCESS_DENIED;
goto Exit;
}
if (!TargetHandle)
{
status = STATUS_INVALID_PARAMETER;
goto Exit;
}
status = ObReferenceObjectByHandle(ProcessHandle,
0,
*PsProcessType,
AccessMode,
&sourceProcess,
NULL);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
GENERAL,
"ObReferenceObjectByHandle failed %!STATUS!",
status);
sourceProcess = NULL;
goto Exit;
}
if (sourceProcess == PsInitialSystemProcess)
{
SourceHandle = MakeKernelHandle(SourceHandle);
}
else
{
if (IsKernelHandle(SourceHandle))
{
status = STATUS_INVALID_HANDLE;
goto Exit;
}
}
status = ObReferenceObjectByHandle(ZwCurrentProcess(),
0,
*PsProcessType,
AccessMode,
&targetProcess,
NULL);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
GENERAL,
"ObReferenceObjectByHandle failed %!STATUS!",
status);
targetProcess = NULL;
goto Exit;
}
status = ObDuplicateObject(sourceProcess,
SourceHandle,
targetProcess,
&targetHandle,
DesiredAccess,
(AccessMode ? 0 : OBJ_KERNEL_HANDLE),
0,
KernelMode);
if (NT_SUCCESS(status))
{
status = KphWriteHandleToMode(TargetHandle, targetHandle, AccessMode);
}
Exit:
if (targetProcess)
{
ObDereferenceObject(targetProcess);
}
if (sourceProcess)
{
ObDereferenceObject(sourceProcess);
}
return status;
}
/**
* \brief Checks if two object handles refer to the same object.
*
* \param[in] FirstObjectHandle First handle to compare with the second.
* \param[in] SecondObjectHandle Second handle to compare with the first.
* \param[in] AccessMode The mode in which to perform access checks.
*
* \return STATUS_SUCCESS if the object handles refer to the same object,
* STATUS_NOT_SAME_OBJECT if the object handles refer to different objects,
* and STATUS_INVALID_HANDLE if one of the handles is invalid.
*/
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KphpCompareObjects(
_In_ HANDLE FirstObjectHandle,
_In_ HANDLE SecondObjectHandle,
_In_ KPROCESSOR_MODE AccessMode
)
{
NTSTATUS status;
PVOID firstObject;
PVOID secondObject;
KPH_PAGED_CODE_PASSIVE();
status = ObReferenceObjectByHandle(FirstObjectHandle,
0,
NULL,
AccessMode,
&firstObject,
NULL);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
GENERAL,
"ObReferenceObjectByHandle failed %!STATUS!",
status);
firstObject = NULL;
secondObject = NULL;
goto Exit;
}
status = ObReferenceObjectByHandle(SecondObjectHandle,
0,
NULL,
AccessMode,
&secondObject,
NULL);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
GENERAL,
"ObReferenceObjectByHandle failed %!STATUS!",
status);
secondObject = NULL;
goto Exit;
}
status = (firstObject == secondObject) ?
STATUS_SUCCESS : STATUS_NOT_SAME_OBJECT;
Exit:
if (secondObject)
{
ObDereferenceObject(secondObject);
}
if (firstObject)
{
ObDereferenceObject(firstObject);
}
return status;
}
/**
* \brief Checks if two object handles refer to the same object, for a process.
*
* \param[in] ProcessHandle A handle to a process.
* \param[in] FirstObjectHandle First handle to compare with the second.
* \param[in] SecondObjectHandle Second handle to compare with the first.
* \param[in] AccessMode The mode in which to perform access checks.
*
* \return STATUS_SUCCESS if the object handles refer to the same object,
* STATUS_NOT_SAME_OBJECT if the object handles refer to different objects,
* and STATUS_INVALID_HANDLE if one of the handles is invalid.
*/
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KphCompareObjects(
_In_ HANDLE ProcessHandle,
_In_ HANDLE FirstObjectHandle,
_In_ HANDLE SecondObjectHandle,
_In_ KPROCESSOR_MODE AccessMode
)
{
NTSTATUS status;
PEPROCESS process;
KPROCESSOR_MODE accessMode;
KAPC_STATE apcState;
KPH_PAGED_CODE_PASSIVE();
status = ObReferenceObjectByHandle(ProcessHandle,
0,
*PsProcessType,
AccessMode,
&process,
NULL);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
GENERAL,
"ObReferenceObjectByHandle failed %!STATUS!",
status);
process = NULL;
goto Exit;
}
if (process == PsInitialSystemProcess)
{
FirstObjectHandle = MakeKernelHandle(FirstObjectHandle);
SecondObjectHandle = MakeKernelHandle(SecondObjectHandle);
accessMode = KernelMode;
}
else
{
accessMode = AccessMode;
}
KeStackAttachProcess(process, &apcState);
status = KphpCompareObjects(FirstObjectHandle,
SecondObjectHandle,
accessMode);
KeUnstackDetachProcess(&apcState);
Exit:
if (process)
{
ObDereferenceObject(process);
}
return status;
}
================================================
FILE: KSystemInformer/parameters.c
================================================
/*
* Copyright (c) 2022 Winsider Seminars & Solutions, Inc. All rights reserved.
*
* This file is part of System Informer.
*
* Authors:
*
* jxy-s 2023-2026
*
*/
#include
typedef enum _KPH_PARAMETER_TYPE
{
KphParameterTypeULong,
KphParameterTypeString,
} KPH_PARAMETER_TYPE, *PKPH_PARAMETER_TYPE;
typedef struct _KPH_PARAMETER
{
UNICODE_STRING Name;
KPH_PARAMETER_TYPE Type;
PVOID Value;
PVOID Default;
} KPH_PARAMETER, *PKPH_PARAMETER;
KPH_PROTECTED_DATA_SECTION_RO_PUSH();
static const UNICODE_STRING KphpDefaultAltitude = RTL_CONSTANT_STRING(L"385210.5");
static const UNICODE_STRING KphpDefaultPortName = RTL_CONSTANT_STRING(L"\\KSystemInformer");
static const UNICODE_STRING KphpDefaultSystemProcessName = RTL_CONSTANT_STRING(L"System Informer Kernel");
KPH_PROTECTED_DATA_SECTION_RO_POP();
KPH_PROTECTED_DATA_SECTION_PUSH();
PUNICODE_STRING KphAltitude = NULL;
PUNICODE_STRING KphPortName = NULL;
KPH_PARAMETER_FLAGS KphParameterFlags = { .Flags = 0 };
PUNICODE_STRING KphSystemProcessName = NULL;
C_ASSERT(sizeof(KPH_PARAMETER_FLAGS) == sizeof(ULONG));
static KPH_PARAMETER KphpParameters[] =
{
{
RTL_CONSTANT_STRING(L"Altitude"),
KphParameterTypeString,
&KphAltitude,
(PVOID)&KphpDefaultAltitude
},
{
RTL_CONSTANT_STRING(L"PortName"),
KphParameterTypeString,
&KphPortName,
(PVOID)&KphpDefaultPortName
},
{
RTL_CONSTANT_STRING(L"Flags"),
KphParameterTypeULong,
&KphParameterFlags,
(PVOID)(ULONG_PTR)0
},
{
RTL_CONSTANT_STRING(L"SystemProcessName"),
KphParameterTypeString,
&KphSystemProcessName,
(PVOID)&KphpDefaultSystemProcessName
},
};
KPH_PROTECTED_DATA_SECTION_POP();
KPH_PAGED_FILE();
/**
* \brief Cleans up the driver parameters.
*/
_IRQL_requires_max_(PASSIVE_LEVEL)
VOID KphCleanupParameters(
VOID
)
{
KPH_PAGED_CODE_PASSIVE();
for (ULONG i = 0; i < ARRAYSIZE(KphpParameters); i++)
{
PKPH_PARAMETER parameter;
PUNICODE_STRING value;
parameter = &KphpParameters[i];
if (parameter->Type != KphParameterTypeString)
{
continue;
}
value = *(PUNICODE_STRING*)parameter->Value;
if (value && (value != parameter->Default))
{
KphFreeRegistryString(value);
}
}
}
/**
* \brief Initializes the driver parameters.
*
* \param[in] RegistryPath Registry path from the entry point.
*/
_IRQL_requires_max_(PASSIVE_LEVEL)
VOID KphInitializeParameters(
_In_ PCUNICODE_STRING RegistryPath
)
{
NTSTATUS status;
HANDLE keyHandle;
status = KphOpenParametersKey(RegistryPath, &keyHandle);
if (!NT_SUCCESS(status))
{
keyHandle = NULL;
}
KPH_PAGED_CODE_PASSIVE();
for (ULONG i = 0; i < ARRAYSIZE(KphpParameters); i++)
{
PKPH_PARAMETER parameter;
parameter = &KphpParameters[i];
switch (parameter->Type)
{
case KphParameterTypeULong:
{
ULONG value;
if (keyHandle)
{
status = KphQueryRegistryULong(keyHandle,
¶meter->Name,
&value);
if (!NT_SUCCESS(status))
{
value = (ULONG)(ULONG_PTR)parameter->Default;
}
}
else
{
value = (ULONG)(ULONG_PTR)parameter->Default;
}
*(PULONG)parameter->Value = value;
break;
}
case KphParameterTypeString:
{
PUNICODE_STRING value;
if (keyHandle)
{
status = KphQueryRegistryString(keyHandle,
¶meter->Name,
&value);
if (!NT_SUCCESS(status))
{
value = (PUNICODE_STRING)parameter->Default;
}
}
else
{
value = (PUNICODE_STRING)parameter->Default;
}
*(PUNICODE_STRING*)parameter->Value = value;
break;
}
DEFAULT_UNREACHABLE;
}
}
if (keyHandle)
{
ObCloseHandle(keyHandle, KernelMode);
}
}
================================================
FILE: KSystemInformer/process.c
================================================
/*
* Copyright (c) 2022 Winsider Seminars & Solutions, Inc. All rights reserved.
*
* This file is part of System Informer.
*
* Authors:
*
* wj32 2010-2016
* jxy-s 2020-2026
*
*/
#include
#include
KPH_PAGED_FILE();
/**
* \brief Opens a process.
*
* \param[out] ProcessHandle A variable which receives the process handle.
* \param[in] DesiredAccess The desired access to the process.
* \param[in] ClientId The identifier of a client. UniqueThread is optional.
* If UniqueThread is present, the thread of the referenced process will be
* checked against this identifier.
* \param[in] AccessMode The mode in which to perform access checks.
*
* \return Successful or errant status.
*/
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KphOpenProcess(
_Out_ PHANDLE ProcessHandle,
_In_ ACCESS_MASK DesiredAccess,
_In_ PCLIENT_ID ClientId,
_In_ KPROCESSOR_MODE AccessMode
)
{
NTSTATUS status;
CLIENT_ID clientId;
PEPROCESS process;
HANDLE processHandle;
KPH_PAGED_CODE_PASSIVE();
process = NULL;
status = KphCopyFromMode(&clientId,
ClientId,
sizeof(CLIENT_ID),
AccessMode);
if (!NT_SUCCESS(status))
{
goto Exit;
}
//
// Use the thread ID if it was specified.
//
if (clientId.UniqueThread)
{
PETHREAD thread;
status = PsLookupProcessThreadByCid(&clientId, &process, &thread);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
GENERAL,
"PsLookupProcessThreadByCid failed: %!STATUS!",
status);
process = NULL;
thread = NULL;
goto Exit;
}
ObDereferenceObject(thread);
}
else
{
status = PsLookupProcessByProcessId(clientId.UniqueProcess, &process);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
GENERAL,
"PsLookupProcessByProcessId failed: %!STATUS!",
status);
process = NULL;
goto Exit;
}
}
if ((DesiredAccess & KPH_PROCESS_READ_ACCESS) != DesiredAccess)
{
status = KphDominationCheck(PsGetCurrentProcess(),
process,
AccessMode);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
GENERAL,
"KphDominationCheck failed: %!STATUS!",
status);
goto Exit;
}
}
//
// Always open in KernelMode to skip ordinary access checks.
//
status = ObOpenObjectByPointer(process,
(AccessMode ? 0 : OBJ_KERNEL_HANDLE),
NULL,
DesiredAccess,
*PsProcessType,
KernelMode,
&processHandle);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
GENERAL,
"ObOpenObjectByPointer failed: %!STATUS!",
status);
processHandle = NULL;
goto Exit;
}
status = KphWriteHandleToMode(ProcessHandle, processHandle, AccessMode);
Exit:
if (process)
{
ObDereferenceObject(process);
}
return status;
}
/**
* \brief Opens the token of a process.
*
* \param[in] ProcessHandle A handle to a process.
* \param[in] DesiredAccess The desired access to the token.
* \param[out] TokenHandle A variable which receives the token handle.
* \param[in] AccessMode The mode in which to perform access checks.
*
* \return Successful or errant status.
*/
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KphOpenProcessToken(
_In_ HANDLE ProcessHandle,
_In_ ACCESS_MASK DesiredAccess,
_Out_ PHANDLE TokenHandle,
_In_ KPROCESSOR_MODE AccessMode
)
{
NTSTATUS status;
PEPROCESS process;
PACCESS_TOKEN primaryToken;
HANDLE tokenHandle;
KPH_PAGED_CODE_PASSIVE();
primaryToken = NULL;
status = ObReferenceObjectByHandle(ProcessHandle,
0,
*PsProcessType,
AccessMode,
&process,
NULL);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
GENERAL,
"ObReferenceObjectByHandle failed: %!STATUS!",
status);
process = NULL;
goto Exit;
}
primaryToken = PsReferencePrimaryToken(process);
if (!primaryToken)
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
GENERAL,
"PsReferencePrimaryToken returned null");
status = STATUS_NO_TOKEN;
goto Exit;
}
if ((DesiredAccess & KPH_TOKEN_READ_ACCESS) != DesiredAccess)
{
status = KphDominationCheck(PsGetCurrentProcess(),
process,
AccessMode);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
GENERAL,
"KphDominationCheck failed: %!STATUS!",
status);
goto Exit;
}
}
//
// Always open in KernelMode to skip ordinary access checks.
//
status = ObOpenObjectByPointer(primaryToken,
(AccessMode ? 0 : OBJ_KERNEL_HANDLE),
NULL,
DesiredAccess,
*SeTokenObjectType,
KernelMode,
&tokenHandle);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
GENERAL,
"ObOpenObjectByPointer failed: %!STATUS!",
status);
tokenHandle = NULL;
goto Exit;
}
status = KphWriteHandleToMode(TokenHandle, tokenHandle, AccessMode);
Exit:
if (primaryToken)
{
PsDereferencePrimaryToken(primaryToken);
}
if (process)
{
ObDereferenceObject(process);
}
return status;
}
/**
* \brief Opens the job object of a process.
*
* \param[in] ProcessHandle A handle to a process.
* \param[in] DesiredAccess The desired access to the job.
* \param[out] JobHandle A variable which receives the job object handle.
* \param[in] AccessMode The mode in which to perform access checks.
*
* \return Successful or errant status.
*/
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KphOpenProcessJob(
_In_ HANDLE ProcessHandle,
_In_ ACCESS_MASK DesiredAccess,
_Out_ PHANDLE JobHandle,
_In_ KPROCESSOR_MODE AccessMode
)
{
NTSTATUS status;
PEPROCESS process;
PEJOB job;
HANDLE jobHandle;
KPH_PAGED_CODE_PASSIVE();
status = ObReferenceObjectByHandle(ProcessHandle,
0,
*PsProcessType,
AccessMode,
&process,
NULL);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
GENERAL,
"ObReferenceObjectByHandle failed: %!STATUS!",
status);
process = NULL;
goto Exit;
}
job = PsGetProcessJob(process);
if (!job)
{
status = STATUS_NOT_FOUND;
goto Exit;
}
if ((DesiredAccess & KPH_JOB_READ_ACCESS) != DesiredAccess)
{
status = KphDominationCheck(PsGetCurrentProcess(),
process,
AccessMode);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
GENERAL,
"KphDominationCheck failed: %!STATUS!",
status);
goto Exit;
}
}
//
// Always open in KernelMode to skip ordinary access checks.
//
status = ObOpenObjectByPointer(job,
(AccessMode ? 0 : OBJ_KERNEL_HANDLE),
NULL,
DesiredAccess,
*PsJobType,
KernelMode,
&jobHandle);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
GENERAL,
"ObOpenObjectByPointer failed: %!STATUS!",
status);
jobHandle = NULL;
goto Exit;
}
status = KphWriteHandleToMode(JobHandle, jobHandle, AccessMode);
Exit:
if (process)
{
ObDereferenceObject(process);
}
return status;
}
/**
* Terminates a process.
*
* \param[in] ProcessHandle A handle to a process.
* \param[in] ExitStatus A status value initiating why the process terminated.
* \param[in] AccessMode The mode in which to perform access checks.
*
* \return Successful or errant status.
*/
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KphTerminateProcess(
_In_ HANDLE ProcessHandle,
_In_ NTSTATUS ExitStatus,
_In_ KPROCESSOR_MODE AccessMode
)
{
NTSTATUS status;
PEPROCESS process;
HANDLE processHandle;
KPH_PAGED_CODE_PASSIVE();
process = NULL;
processHandle = NULL;
status = ObReferenceObjectByHandle(ProcessHandle,
0,
*PsProcessType,
AccessMode,
&process,
NULL);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
GENERAL,
"ObReferenceObjectByHandle failed: %!STATUS!",
status);
process = NULL;
goto Exit;
}
status = KphDominationAndPrivilegeCheck(KPH_TOKEN_PRIVILEGE_TERMINATE,
PsGetCurrentThread(),
process,
AccessMode);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
GENERAL,
"KphDominationAndPrivilegeCheck failed: %!STATUS!",
status);
goto Exit;
}
if (process == PsGetCurrentProcess())
{
KphTracePrint(TRACE_LEVEL_VERBOSE, GENERAL, "Can not terminate self.");
status = STATUS_ACCESS_DENIED;
goto Exit;
}
//
// Re-open the process to get a kernel handle.
//
status = ObOpenObjectByPointer(process,
OBJ_KERNEL_HANDLE,
NULL,
PROCESS_TERMINATE,
*PsProcessType,
KernelMode,
&processHandle);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
GENERAL,
"ObOpenObjectByPointer failed: %!STATUS!",
status);
processHandle = NULL;
goto Exit;
}
status = ZwTerminateProcess(processHandle, ExitStatus);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
GENERAL,
"ZwTerminateProcess failed: %!STATUS!",
status);
}
Exit:
if (processHandle)
{
ObCloseHandle(processHandle, KernelMode);
}
if (process)
{
ObDereferenceObject(process);
}
return status;
}
/**
* \brief Queries information about a process.
*
* \param[in] ProcessHandle Handle to process to query.
* \param[in] ProcessInformationClass Information class to query.
* \param[out] ProcessInformation Populated with process information by class.
* \param[in] ProcessInformationLength Length of the process information buffer.
* \param[out] ReturnLength Receives the number of bytes written or required.
* \param[in] AccessMode The mode in which to perform access checks.
*
* \return Successful or errant status.
*/
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KphQueryInformationProcess(
_In_ HANDLE ProcessHandle,
_In_ KPH_PROCESS_INFORMATION_CLASS ProcessInformationClass,
_Out_writes_bytes_opt_(ProcessInformationLength) PVOID ProcessInformation,
_In_ ULONG ProcessInformationLength,
_Out_opt_ PULONG ReturnLength,
_In_ KPROCESSOR_MODE AccessMode
)
{
NTSTATUS status;
PKPH_DYN dyn;
PEPROCESS processObject;
PKPH_PROCESS_CONTEXT process;
ULONG returnLength;
KPH_PAGED_CODE_PASSIVE();
dyn = NULL;
process = NULL;
returnLength = 0;
status = ObReferenceObjectByHandle(ProcessHandle,
0,
*PsProcessType,
AccessMode,
&processObject,
NULL);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
GENERAL,
"ObReferenceObjectByHandle failed: %!STATUS!",
status);
processObject = NULL;
goto Exit;
}
process = KphGetEProcessContext(processObject);
if (!process)
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
GENERAL,
"KphGetEProcessContext return null.");
status = STATUS_OBJECTID_NOT_FOUND;
goto Exit;
}
switch (ProcessInformationClass)
{
case KphProcessBasicInformation:
{
KPH_PROCESS_BASIC_INFORMATION info;
if (!ProcessInformation ||
(ProcessInformationLength < sizeof(KPH_PROCESS_BASIC_INFORMATION)))
{
status = STATUS_INFO_LENGTH_MISMATCH;
returnLength = sizeof(KPH_PROCESS_BASIC_INFORMATION);
goto Exit;
}
KphAcquireRWLockShared(&process->ThreadListLock);
KphAcquireRWLockShared(&process->ProtectionLock);
info.ProcessState = KphGetProcessState(process);
info.ProcessStartKey = KphGetProcessStartKey(processObject);
info.CreatorClientId.UniqueProcess = process->CreatorClientId.UniqueProcess;
info.CreatorClientId.UniqueThread = process->CreatorClientId.UniqueThread;
info.NumberOfImageLoads = ReadSizeTNoFence(&process->NumberOfImageLoads);
info.Flags = process->Flags;
info.NumberOfThreads = process->NumberOfThreads;
info.ProcessAllowedMask = process->ProcessAllowedMask;
info.ThreadAllowedMask = process->ThreadAllowedMask;
info.NumberOfMicrosoftImageLoads = ReadSizeTNoFence(&process->NumberOfMicrosoftImageLoads);
info.NumberOfAntimalwareImageLoads = ReadSizeTNoFence(&process->NumberOfAntimalwareImageLoads);
info.NumberOfVerifiedImageLoads = ReadSizeTNoFence(&process->NumberOfVerifiedImageLoads);
info.NumberOfUntrustedImageLoads = ReadSizeTNoFence(&process->NumberOfUntrustedImageLoads);
info.UserWritableReferences = 0;
if (process->FileObject &&
process->FileObject->SectionObjectPointer)
{
info.UserWritableReferences =
MmDoesFileHaveUserWritableReferences(process->FileObject->SectionObjectPointer);
}
KphReleaseRWLock(&process->ProtectionLock);
KphReleaseRWLock(&process->ThreadListLock);
status = KphCopyToMode(ProcessInformation,
&info,
sizeof(KPH_PROCESS_BASIC_INFORMATION),
AccessMode);
if (NT_SUCCESS(status))
{
returnLength = sizeof(KPH_PROCESS_BASIC_INFORMATION);
}
break;
}
case KphProcessStateInformation:
{
KPH_PROCESS_STATE processState;
if (!ProcessInformation ||
(ProcessInformationLength < sizeof(KPH_PROCESS_STATE)))
{
status = STATUS_INFO_LENGTH_MISMATCH;
returnLength = sizeof(KPH_PROCESS_STATE);
goto Exit;
}
processState = KphGetProcessState(process);
status = KphWriteULongToMode(ProcessInformation,
processState,
AccessMode);
if (NT_SUCCESS(status))
{
returnLength = sizeof(KPH_PROCESS_STATE);
}
break;
}
case KphProcessWSLProcessId:
{
ULONG processId;
if (process->SubsystemType != SubsystemInformationTypeWSL)
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
GENERAL,
"Invalid subsystem for WSL process ID query.");
status = STATUS_INVALID_HANDLE;
goto Exit;
}
if (!ProcessInformation ||
(ProcessInformationLength < sizeof(ULONG)))
{
status = STATUS_INFO_LENGTH_MISMATCH;
returnLength = sizeof(ULONG);
goto Exit;
}
status = KphQueryInformationProcessContext(process,
KphProcessContextWSLProcessId,
&processId,
sizeof(ULONG),
NULL);
if (!NT_SUCCESS(status))
{
goto Exit;
}
status = KphWriteULongToMode(ProcessInformation,
processId,
AccessMode);
if (NT_SUCCESS(status))
{
returnLength = sizeof(ULONG);
}
break;
}
case KphProcessSequenceNumber:
{
ULONG64 sequenceNumber;
if (!ProcessInformation ||
(ProcessInformationLength < sizeof(ULONG64)))
{
status = STATUS_INFO_LENGTH_MISMATCH;
returnLength = sizeof(ULONG64);
goto Exit;
}
sequenceNumber = KphGetProcessSequenceNumber(processObject);
status = KphWriteULong64ToMode(ProcessInformation,
sequenceNumber,
AccessMode);
if (NT_SUCCESS(status))
{
returnLength = sizeof(ULONG64);
}
break;
}
case KphProcessStartKey:
{
ULONG64 startKey;
if (!ProcessInformation ||
(ProcessInformationLength < sizeof(ULONG64)))
{
status = STATUS_INFO_LENGTH_MISMATCH;
returnLength = sizeof(ULONG64);
goto Exit;
}
startKey = KphGetProcessStartKey(processObject);
status = KphWriteULong64ToMode(ProcessInformation,
startKey,
AccessMode);
if (NT_SUCCESS(status))
{
returnLength = sizeof(ULONG64);
}
break;
}
case KphProcessImageSection:
{
PVOID processSectionObject;
HANDLE processSectionHandle;
dyn = KphReferenceDynData();
if (!dyn || (dyn->EpSectionObject == ULONG_MAX))
{
status = STATUS_NOINTERFACE;
goto Exit;
}
if (!ProcessInformation ||
(ProcessInformationLength < sizeof(HANDLE)))
{
status = STATUS_INFO_LENGTH_MISMATCH;
returnLength = sizeof(HANDLE);
goto Exit;
}
status = PsAcquireProcessExitSynchronization(processObject);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
GENERAL,
"PsAcquireProcessExitSynchronization failed: %!STATUS!",
status);
goto Exit;
}
processSectionObject = *(PVOID*)Add2Ptr(processObject,
dyn->EpSectionObject);
if (processSectionObject)
{
ObReferenceObject(processSectionObject);
}
PsReleaseProcessExitSynchronization(processObject);
if (!processSectionObject)
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
GENERAL,
"Process section object pointer is null.");
status = STATUS_INVALID_PARAMETER;
goto Exit;
}
status = ObOpenObjectByPointer(processSectionObject,
(AccessMode ? 0 : OBJ_KERNEL_HANDLE),
NULL,
SECTION_QUERY | SECTION_MAP_READ,
*MmSectionObjectType,
KernelMode,
&processSectionHandle);
ObDereferenceObject(processSectionObject);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
GENERAL,
"ObOpenObjectByPointer failed: %!STATUS!",
status);
goto Exit;
}
status = KphWriteHandleToMode(ProcessInformation,
processSectionHandle,
AccessMode);
if (NT_SUCCESS(status))
{
returnLength = sizeof(HANDLE);
}
break;
}
case KphProcessImageFileName:
{
if (!process->ImageFileName)
{
status = STATUS_INSUFFICIENT_RESOURCES;
goto Exit;
}
status = KphCopyUnicodeStringToMode(ProcessInformation,
ProcessInformationLength,
process->ImageFileName,
&returnLength,
AccessMode);
break;
}
default:
{
status = STATUS_INVALID_INFO_CLASS;
break;
}
}
Exit:
if (ReturnLength)
{
KphWriteULongToMode(ReturnLength, returnLength, AccessMode);
}
if (process)
{
KphDereferenceObject(process);
}
if (processObject)
{
ObDereferenceObject(processObject);
}
if (dyn)
{
KphDereferenceObject(dyn);
}
return status;
}
/**
* \brief Sets information about a process.
*
* \param[in] ProcessHandle Handle to process to set information for.
* \param[in] ProcessInformationClass Information class to set.
* \param[in] ProcessInformation Information to set.
* \param[in] ProcessInformationLength Length of the process information buffer.
* \param[in] AccessMode The mode in which to perform access checks.
*
* \return Successful or errant status.
*/
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KphSetInformationProcess(
_In_ HANDLE ProcessHandle,
_In_ KPH_PROCESS_INFORMATION_CLASS ProcessInformationClass,
_In_reads_bytes_(ProcessInformationLength) PVOID ProcessInformation,
_In_ ULONG ProcessInformationLength,
_In_ KPROCESSOR_MODE AccessMode
)
{
NTSTATUS status;
PVOID processInformation;
BYTE stackBuffer[64];
PEPROCESS process;
HANDLE processHandle;
PROCESSINFOCLASS processInformationClass;
KPH_PAGED_CODE_PASSIVE();
processInformation = NULL;
process = NULL;
processHandle = NULL;
if (!ProcessInformation)
{
status = STATUS_INVALID_PARAMETER;
goto Exit;
}
if (AccessMode != KernelMode)
{
processInformation = KphAllocatePagedA(ProcessInformationLength,
KPH_TAG_PROCESS_INFO,
stackBuffer);
if (!processInformation)
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
GENERAL,
"Failed to allocate process info buffer.");
status = STATUS_INSUFFICIENT_RESOURCES;
goto Exit;
}
__try
{
CopyFromUser(processInformation,
ProcessInformation,
ProcessInformationLength);
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
status = GetExceptionCode();
goto Exit;
}
}
else
{
processInformation = ProcessInformation;
}
status = ObReferenceObjectByHandle(ProcessHandle,
0,
*PsProcessType,
AccessMode,
&process,
NULL);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
GENERAL,
"ObReferenceObjectByHandle failed: %!STATUS!",
status);
process = NULL;
goto Exit;
}
switch (ProcessInformationClass)
{
case KphProcessEmptyWorkingSet:
{
//
// Permitted across PPL boundaries.
//
break;
}
case KphProcessQuotaLimits:
case KphProcessBasePriority:
case KphProcessRaisePriority:
case KphProcessPriorityClass:
case KphProcessAffinityMask:
case KphProcessPriorityBoost:
case KphProcessIoPriority:
case KphProcessPagePriority:
case KphProcessPowerThrottlingState:
case KphProcessPriorityClassEx:
default:
{
status = KphDominationCheck(PsGetCurrentProcess(),
process,
AccessMode);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
GENERAL,
"KphDominationCheck failed: %!STATUS!",
status);
goto Exit;
}
break;
}
}
status = ObOpenObjectByPointer(process,
OBJ_KERNEL_HANDLE,
NULL,
PROCESS_SET_INFORMATION,
*PsProcessType,
KernelMode,
&processHandle);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
GENERAL,
"ObOpenObjectByPointer failed: %!STATUS!",
status);
processHandle = NULL;
goto Exit;
}
switch (ProcessInformationClass)
{
case KphProcessQuotaLimits:
{
processInformationClass = ProcessQuotaLimits;
break;
}
case KphProcessBasePriority:
{
processInformationClass = ProcessBasePriority;
break;
}
case KphProcessRaisePriority:
{
processInformationClass = ProcessRaisePriority;
break;
}
case KphProcessPriorityClass:
{
processInformationClass = ProcessPriorityClass;
break;
}
case KphProcessAffinityMask:
{
processInformationClass = ProcessAffinityMask;
break;
}
case KphProcessPriorityBoost:
{
processInformationClass = ProcessPriorityBoost;
break;
}
case KphProcessIoPriority:
{
processInformationClass = ProcessIoPriority;
break;
}
case KphProcessPagePriority:
{
processInformationClass = ProcessPagePriority;
break;
}
case KphProcessPowerThrottlingState:
{
processInformationClass = ProcessPowerThrottlingState;
break;
}
case KphProcessPriorityClassEx:
{
processInformationClass = ProcessPriorityClassEx;
break;
}
case KphProcessEmptyWorkingSet:
{
QUOTA_LIMITS_EX quotaLimits;
RtlZeroMemory("aLimits, sizeof(QUOTA_LIMITS_EX));
quotaLimits.MinimumWorkingSetSize = SIZE_T_MAX;
quotaLimits.MaximumWorkingSetSize = SIZE_T_MAX;
status = ZwSetInformationProcess(processHandle,
ProcessQuotaLimits,
"aLimits,
sizeof(QUOTA_LIMITS_EX));
//
// Bypass generic call to exit immediately.
//
goto Exit;
}
default:
{
status = STATUS_INVALID_INFO_CLASS;
goto Exit;
}
}
status = ZwSetInformationProcess(processHandle,
processInformationClass,
processInformation,
ProcessInformationLength);
Exit:
if (processHandle)
{
ObCloseHandle(processHandle, KernelMode);
}
if (process)
{
ObDereferenceObject(process);
}
if (processInformation && (processInformation != ProcessInformation))
{
KphFreeA(processInformation, KPH_TAG_PROCESS_INFO, stackBuffer);
}
return status;
}
================================================
FILE: KSystemInformer/protection.c
================================================
/*
* Copyright (c) 2022 Winsider Seminars & Solutions, Inc. All rights reserved.
*
* This file is part of System Informer.
*
* Authors:
*
* wj32 2010-2016
* jxy-s 2020-2026
*
*/
#include
#include
typedef struct _KPH_ENUM_FOR_PROTECTION
{
PKPH_DYN Dyn;
PKPH_PROCESS_CONTEXT ProcessEnum;
PKPH_PROCESS_CONTEXT Process;
NTSTATUS Status;
} KPH_ENUM_FOR_PROTECTION, *PKPH_ENUM_FOR_PROTECTION;
typedef struct _KPH_IMAGE_LOAD_APC
{
KSI_KAPC Apc;
PKPH_PROCESS_CONTEXT Process;
PVOID ImageBase;
PFILE_OBJECT FileObject;
} KPH_IMAGE_LOAD_APC, *PKPH_IMAGE_LOAD_APC;
typedef struct _KPH_IMAGE_LOAD_APC_INIT
{
PKPH_PROCESS_CONTEXT Process;
PVOID ImageBase;
PFILE_OBJECT FileObject;
} KPH_IMAGE_LOAD_APC_INIT, *PKPH_IMAGE_LOAD_APC_INIT;
KPH_PROTECTED_DATA_SECTION_RO_PUSH();
static const UNICODE_STRING KphpImageLoadApcTypeName = RTL_CONSTANT_STRING(L"KphImageLoadApc");
KPH_PROTECTED_DATA_SECTION_RO_POP();
KPH_PROTECTED_DATA_SECTION_PUSH();
static PKPH_OBJECT_TYPE KphpImageLoadApcType = NULL;
KPH_PROTECTED_DATA_SECTION_POP();
static KPH_REFERENCE KphpDriverUnloadProtectionRef = { 0 };
static PVOID KphpDriverUnloadPreviousRoutine = NULL;
KPH_PAGED_FILE();
/**
* \brief Allocates an image load APC object.
*
* \param[in] Size The size to allocate.
*
* \return Allocates image load APC object, null on failure.
*/
_Function_class_(KPH_TYPE_ALLOCATE_PROCEDURE)
_Return_allocatesMem_size_(Size)
PVOID KSIAPI KphpAllocateImageLoadApc(
_In_ SIZE_T Size
)
{
KPH_PAGED_CODE();
return KphAllocateNPaged(Size, KPH_TAG_IMAGE_LOAD_APC);
}
/**
* \brief Initializes image load APC object.
*
* \param[in,out] Object The image load APC object to initialize.
* \param[in] Parameter The image load ACP initialization structure.
*
* \return STATUS_SUCCESS
*/
_Function_class_(KPH_TYPE_INITIALIZE_PROCEDURE)
_Must_inspect_result_
NTSTATUS KSIAPI KphpInitializeImageLoadApc(
_Inout_ PVOID Object,
_In_opt_ PVOID Parameter
)
{
PKPH_IMAGE_LOAD_APC apc;
PKPH_IMAGE_LOAD_APC_INIT init;
KPH_PAGED_CODE();
NT_ASSERT(Parameter);
apc = Object;
init = Parameter;
apc->Process = init->Process;
KphReferenceObject(apc->Process);
apc->ImageBase = init->ImageBase;
apc->FileObject = init->FileObject;
if (apc->FileObject)
{
ObReferenceObject(apc->FileObject);
}
KphReferenceHashingInfrastructure();
return STATUS_SUCCESS;
}
/**
* \brief Deletes image load APC object.
*
* \param[in,out] Object The image load APC object to delete.
*/
_Function_class_(KPH_TYPE_DELETE_PROCEDURE)
VOID KSIAPI KphpDeleteImageLoadApc(
_Inout_ PVOID Object
)
{
PKPH_IMAGE_LOAD_APC apc;
KPH_PAGED_CODE();
apc = Object;
KphDereferenceObject(apc->Process);
if (apc->FileObject)
{
ObDereferenceObject(apc->FileObject);
}
KphDereferenceHashingInfrastructure();
}
/**
* \brief Frees an image load APC object.
*
* \param[in] Object Image load APC object to free.
*/
_Function_class_(KPH_TYPE_FREE_PROCEDURE)
VOID KSIAPI KphpFreeImageLoadApc(
_In_freesMem_ PVOID Object
)
{
KPH_PAGED_CODE();
KphFree(Object, KPH_TAG_IMAGE_LOAD_APC);
}
/**
* \brief Checks if object protections should be allowed.
*
* \param[in] Info Optional object pre operation information.
* \param[in] Actor The actor process requesting access.
* \param[in] Object The process context associated with the object.
* \param[out] Allow Receives TRUE if object protections should be allowed,
* FALSE otherwise.
*
* \return Successful or errant status.
*/
_IRQL_requires_max_(APC_LEVEL)
_Must_inspect_result_
NTSTATUS KphpShouldAllowObjectAccess(
_In_opt_ POB_PRE_OPERATION_INFORMATION Info,
_In_ PKPH_PROCESS_CONTEXT Actor,
_In_ PKPH_PROCESS_CONTEXT Object,
_Out_ PBOOLEAN Allow
)
{
NTSTATUS status;
PKPH_PROCESS_CONTEXT source;
PKPH_PROCESS_CONTEXT target;
BOOLEAN isLsass;
KPH_PAGED_CODE();
*Allow = FALSE;
source = NULL;
target = NULL;
NT_ASSERT(Object->Protected);
if (!Info || (Info->Operation == OB_OPERATION_HANDLE_CREATE))
{
//
// Allow access to itself.
//
if (Actor->EProcess == Object->EProcess)
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
PROTECTION,
"Process %wZ (%lu) allowed to access itself",
&Actor->ImageName,
HandleToULong(Actor->ProcessId));
*Allow = TRUE;
status = STATUS_SUCCESS;
goto Exit;
}
//
// Allow maximum state processes access to one another.
//
if (KphTestProcessContextState(Actor, KPH_PROCESS_STATE_MAXIMUM) &&
KphTestProcessContextState(Object, KPH_PROCESS_STATE_MAXIMUM))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
PROTECTION,
"Maximum state process %wZ (%lu) allowed to access "
"maximum state process %wZ (%lu)",
&Actor->ImageName,
HandleToULong(Actor->ProcessId),
&Object->ImageName,
HandleToULong(Object->ProcessId));
*Allow = TRUE;
status = STATUS_SUCCESS;
goto Exit;
}
KphTracePrint(TRACE_LEVEL_VERBOSE,
PROTECTION,
"Process %wZ (%lu) possibly subject to limited access "
"to process %wZ (%lu)",
&Actor->ImageName,
HandleToULong(Actor->ProcessId),
&Object->ImageName,
HandleToULong(Object->ProcessId));
}
else if (Info && (Info->Operation == OB_OPERATION_HANDLE_DUPLICATE))
{
PEPROCESS process;
//
// Allow duplication to itself into itself from itself.
//
if ((Object->EProcess == Actor->EProcess) &&
(Object->EProcess == Info->Parameters->DuplicateHandleInformation.SourceProcess) &&
(Object->EProcess == Info->Parameters->DuplicateHandleInformation.TargetProcess))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
PROTECTION,
"Process %wZ (%lu) allowed to access itself",
&Actor->ImageName,
HandleToULong(Actor->ProcessId));
*Allow = TRUE;
status = STATUS_SUCCESS;
goto Exit;
}
process = Info->Parameters->DuplicateHandleInformation.SourceProcess;
source = KphGetEProcessContext(process);
if (!source)
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
PROTECTION,
"KphGetEProcessContext(%lu) returned NULL",
HandleToULong(PsGetProcessId(process)));
status = STATUS_INVALID_CID;
goto Exit;
}
process = Info->Parameters->DuplicateHandleInformation.TargetProcess;
target = KphGetEProcessContext(process);
if (!target)
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
PROTECTION,
"KphGetEProcessContext(%lu) returned NULL",
HandleToULong(PsGetProcessId(process)));
status = STATUS_INVALID_CID;
goto Exit;
}
//
// Allow duplication between maximum state processes.
//
if (KphTestProcessContextState(Actor, KPH_PROCESS_STATE_MAXIMUM) &&
KphTestProcessContextState(source, KPH_PROCESS_STATE_MAXIMUM) &&
KphTestProcessContextState(target, KPH_PROCESS_STATE_MAXIMUM))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
PROTECTION,
"Maximum state process %wZ (%lu) allowed to duplicate "
"object for process %wZ (%lu) from maximum state "
"process %wZ (%lu) to maximum state process %wZ (%lu)",
&Actor->ImageName,
HandleToULong(Actor->ProcessId),
&Object->ImageName,
HandleToULong(Object->ProcessId),
&source->ImageName,
HandleToULong(source->ProcessId),
&target->ImageName,
HandleToULong(target->ProcessId));
*Allow = TRUE;
status = STATUS_SUCCESS;
goto Exit;
}
KphTracePrint(TRACE_LEVEL_VERBOSE,
PROTECTION,
"Process %wZ (%lu) possibly subject to limited access "
"to object for process %wZ (%lu) when duplicating object "
"from process %wZ (%lu) to process %wZ (%lu)",
&Actor->ImageName,
HandleToULong(Actor->ProcessId),
&Object->ImageName,
HandleToULong(Object->ProcessId),
&source->ImageName,
HandleToULong(source->ProcessId),
&target->ImageName,
HandleToULong(target->ProcessId));
}
status = KphDominationCheck(Object->EProcess, Actor->EProcess, UserMode);
if (!NT_SUCCESS(status))
{
//
// Allow access when the actor is a protected process and the target is
// not protected at a higher level.
//
KphTracePrint(TRACE_LEVEL_VERBOSE,
PROTECTION,
"PPL process %wZ (%lu) allowed to access process %wZ (%lu)",
&Actor->ImageName,
HandleToULong(Actor->ProcessId),
&Object->ImageName,
HandleToULong(Object->ProcessId));
*Allow = TRUE;
status = STATUS_SUCCESS;
goto Exit;
}
status = KphProcessIsLsass(Actor->EProcess, &isLsass);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
PROTECTION,
"KphProcessIsLsass failed: %!STATUS!",
status);
goto Exit;
}
if (isLsass)
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
PROTECTION,
"LSA process %wZ (%lu) allowed to access process %wZ (%lu)",
&Actor->ImageName,
HandleToULong(Actor->ProcessId),
&Object->ImageName,
HandleToULong(Object->ProcessId));
*Allow = TRUE;
goto Exit;
}
Exit:
if (target)
{
KphDereferenceObject(target);
}
if (source)
{
KphDereferenceObject(source);
}
return status;
}
/**
* \brief Handle enumeration callback for protecting a process.
*
* \param[in,out] HandleTableEntry The handle table entry being enumerated.
* \param[in] Handle The handle being enumerated.
* \param[in] Parameter The enumerate for protection context.
*
* \return FALSE to continue enumerating, TRUE to stop.
*/
_Function_class_(KPH_ENUM_PROCESS_HANDLES_CALLBACK)
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
BOOLEAN KSIAPI KphpEnumProcessHandlesForProtection(
_Inout_ PHANDLE_TABLE_ENTRY HandleTableEntry,
_In_ HANDLE Handle,
_In_opt_ PVOID Parameter
)
{
PKPH_ENUM_FOR_PROTECTION parameter;
POBJECT_HEADER objectHeader;
PVOID object;
POBJECT_TYPE objectType;
ACCESS_MASK grantedAccess;
ACCESS_MASK allowedAccessMask;
KPH_PAGED_CODE_PASSIVE();
NT_ASSERT(Parameter);
parameter = Parameter;
if (IsKernelHandle(Handle))
{
//
// Don't screw with kernel handles.
//
return FALSE;
}
objectHeader = KphObpDecodeObject(parameter->Dyn, HandleTableEntry);
if (!objectHeader)
{
//
// We don't have the dynamic data necessary to do work.
//
parameter->Status = STATUS_NOINTERFACE;
return TRUE;
}
object = (PVOID)&objectHeader->Body;
objectType = ObGetObjectType(object);
if (objectType == *PsProcessType)
{
if (parameter->Process->EProcess != object)
{
return FALSE;
}
grantedAccess = ObpDecodeGrantedAccess(HandleTableEntry->GrantedAccess);
allowedAccessMask = parameter->Process->ProcessAllowedMask;
if ((grantedAccess & allowedAccessMask) != grantedAccess)
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
PROTECTION,
"Modifying process handle (0x%04x, 0x%08x -> 0x%08x) "
"permissions in process %wZ (%lu) for process %wZ (%lu)",
HandleToULong(Handle),
grantedAccess,
(grantedAccess & allowedAccessMask),
¶meter->ProcessEnum->ImageName,
HandleToULong(parameter->ProcessEnum->ProcessId),
¶meter->Process->ImageName,
HandleToULong(parameter->Process->ProcessId));
ObpSetGrantedAccess(&HandleTableEntry->GrantedAccess,
(grantedAccess & allowedAccessMask));
}
return FALSE;
}
if (objectType == *PsThreadType)
{
PEPROCESS process;
process = PsGetThreadProcess(object);
if (parameter->Process->EProcess != process)
{
return FALSE;
}
grantedAccess = ObpDecodeGrantedAccess(HandleTableEntry->GrantedAccess);
allowedAccessMask = parameter->Process->ThreadAllowedMask;
if ((grantedAccess & allowedAccessMask) != grantedAccess)
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
PROTECTION,
"Modifying thread handle (0x%04x, 0x%08x -> 0x%08x) "
"permissions in process %wZ (%lu) for process %wZ (%lu)",
HandleToULong(Handle),
grantedAccess,
(grantedAccess & allowedAccessMask),
¶meter->ProcessEnum->ImageName,
HandleToULong(parameter->ProcessEnum->ProcessId),
¶meter->Process->ImageName,
HandleToULong(parameter->Process->ProcessId));
ObpSetGrantedAccess(&HandleTableEntry->GrantedAccess,
(grantedAccess & allowedAccessMask));
}
}
return FALSE;
}
/**
* \brief Process context enumeration callback for process protection.
*
* \param[in] Process The process context being enumerated.
* \param[in] Parameter The enumerate for protection context.
*
* \return FALSE to continue enumerating, TRUE to stop.
*/
_Function_class_(KPH_ENUM_PROCESS_CONTEXTS_CALLBACK)
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
BOOLEAN KSIAPI KphpEnumProcessContextsForProtection(
_In_ PKPH_PROCESS_CONTEXT Process,
_In_opt_ PVOID Parameter
)
{
NTSTATUS status;
PKPH_ENUM_FOR_PROTECTION parameter;
BOOLEAN allow;
KPH_PAGED_CODE_PASSIVE();
NT_ASSERT(Parameter);
parameter = Parameter;
allow = FALSE;
status = KphpShouldAllowObjectAccess(NULL,
Process,
parameter->Process,
&allow);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
PROTECTION,
"KphpShouldAllowObjectAccess failed: %!STATUS!",
status);
//
// This usually means we don't have the dynamic data to do work.
// We can't guarantee both protection and application compatibility.
//
parameter->Status = status;
return TRUE;
}
if (allow)
{
return FALSE;
}
parameter->ProcessEnum = Process;
status = KphEnumerateProcessHandlesEx(Process->EProcess,
KphpEnumProcessHandlesForProtection,
parameter);
if (status == STATUS_NOINTERFACE)
{
//
// This means we don't have the dynamic data necessary to do work.
// All other errors are failure to access process objects because
// the process is exiting or has no object table.
//
parameter->Status = status;
return TRUE;
}
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
PROTECTION,
"KphEnumerateProcessHandlesEx failed: %!STATUS!",
status);
}
return FALSE;
}
/**
* \brief Stops protecting a process.
*
* \param[in] Process The process to stop protecting.
*/
_IRQL_requires_max_(APC_LEVEL)
VOID KphStopProtectingProcess(
_In_ PKPH_PROCESS_CONTEXT Process
)
{
KPH_PAGED_CODE();
KphAcquireRWLockExclusive(&Process->ProtectionLock);
Process->Protected = FALSE;
Process->ProcessAllowedMask = 0;
Process->ThreadAllowedMask = 0;
KphReleaseRWLock(&Process->ProtectionLock);
KphTracePrint(TRACE_LEVEL_INFORMATION,
PROTECTION,
"Stopped protecting process %wZ (%lu)",
&Process->ImageName,
HandleToULong(Process->ProcessId));
}
/**
* \brief Determines if a process is protected.
*
* \param[in] Process The process to check.
*
* \return TRUE if the process is protected, FALSE otherwise.
*/
_IRQL_requires_max_(PASSIVE_LEVEL)
BOOLEAN KphIsProtectedProcess(
_In_ PKPH_PROCESS_CONTEXT Process
)
{
BOOLEAN isProtectedProcess;
KPH_PAGED_CODE_PASSIVE();
KphAcquireRWLockShared(&Process->ProtectionLock);
isProtectedProcess = Process->Protected ? TRUE : FALSE;
KphReleaseRWLock(&Process->ProtectionLock);
return isProtectedProcess;
}
/**
* \brief Starts protecting a process.
*
* \param[in] Process The process to start protecting.
* \param[in] ProcessAllowedMask Access mask containing the allowed access to
* the process.
* \param[in] ThreadAllowedMask Access mask containing the allowed access to
* the process threads.
*
* \return Successful or errant status.
*/
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KphStartProtectingProcess(
_In_ PKPH_PROCESS_CONTEXT Process,
_In_ ACCESS_MASK ProcessAllowedMask,
_In_ ACCESS_MASK ThreadAllowedMask
)
{
NTSTATUS status;
BOOLEAN releaseLock;
SECURITY_SUBJECT_CONTEXT subjectContext;
BOOLEAN accessGranted;
KPH_PAGED_CODE_PASSIVE();
releaseLock = FALSE;
//
// N.B. We must be able to identify lsass before applying protections.
// Without the ability to identify lsass, a process may fail to start.
// Normally, lsass runs as a protected process; however, if it is not,
// we rely on dynamic data to identify it. If a system configuration
// does not run lsass as a protected process and also lacks dynamic data
// support, we cannot guarantee application compatibility or protection.
// In that case, access to the driver will be restricted.
//
// This distinction is important later within the object callbacks.
// Note that the KphpShouldAllowObjectAccess call in KphApplyObProtections
// depends on KphProcessIsLsass.
//
if (!KphCanIdentifyLsass())
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
PROTECTION,
"KphCanIdentifyLsass failed");
status = STATUS_NOINTERFACE;
goto Exit;
}
SeCaptureSubjectContextEx(NULL, Process->EProcess, &subjectContext);
accessGranted = KphSinglePrivilegeCheckEx(SeDebugPrivilege,
&subjectContext,
UserMode);
SeReleaseSubjectContext(&subjectContext);
if (!accessGranted)
{
status = STATUS_PRIVILEGE_NOT_HELD;
goto Exit;
}
KphAcquireRWLockExclusive(&Process->ProtectionLock);
releaseLock = TRUE;
if (Process->Protected)
{
KphTracePrint(TRACE_LEVEL_INFORMATION,
PROTECTION,
"Already protecting process %wZ (%lu)",
&Process->ImageName,
HandleToULong(Process->ProcessId));
status = STATUS_ALREADY_COMPLETE;
goto Exit;
}
KphTracePrint(TRACE_LEVEL_INFORMATION,
PROTECTION,
"Started protecting process %wZ (%lu)",
&Process->ImageName,
HandleToULong(Process->ProcessId));
Process->Protected = TRUE;
Process->ProcessAllowedMask = ProcessAllowedMask;
Process->ThreadAllowedMask = ThreadAllowedMask;
status = STATUS_SUCCESS;
Exit:
if (releaseLock)
{
KphReleaseRWLock(&Process->ProtectionLock);
}
return status;
}
/**
* \brief Returns true if we should permit extending handle permissions for
* the creator process.
*
* \param[in] Info Object pre operation information.
* \param[in] Actor The actor thread.
* \param[in] ProcessTarget The process to check against.
*
* \return TRUE if we should permit, FALSE otherwise.
*/
_IRQL_requires_max_(APC_LEVEL)
BOOLEAN KphpShouldPermitCreatorProcess(
_In_ POB_PRE_OPERATION_INFORMATION Info,
_In_ PKPH_THREAD_CONTEXT Actor,
_In_ PKPH_PROCESS_CONTEXT Process
)
{
KPH_PAGED_CODE();
NT_ASSERT(Process->VerifiedProcess);
if (Info->Operation == OB_OPERATION_HANDLE_DUPLICATE)
{
return FALSE;
}
NT_ASSERT(Info->Operation == OB_OPERATION_HANDLE_CREATE);
if (!Actor->IsCreatingProcess ||
(Actor->IsCreatingProcessId != Process->ProcessId))
{
return FALSE;
}
return KphTestProcessContextState(Actor->ProcessContext,
KPH_PROCESS_STATE_MINIMUM);
}
/**
* \brief Applies object protections to protected processes. Invoked by the
* object manager callbacks.
*
* \param[in,out] Info Object pre operation information to apply protections.
*/
_IRQL_requires_max_(APC_LEVEL)
VOID KphApplyObProtections(
_Inout_ POB_PRE_OPERATION_INFORMATION Info
)
{
NTSTATUS status;
PKPH_PROCESS_CONTEXT process;
PKPH_THREAD_CONTEXT actor;
BOOLEAN releaseLock;
ACCESS_MASK allowedAccessMask;
ACCESS_MASK desiredAccess;
PACCESS_MASK access;
BOOLEAN allow;
KPH_PAGED_CODE();
process = NULL;
actor = NULL;
releaseLock = FALSE;
if ((Info->ObjectType != *PsProcessType) &&
(Info->ObjectType != *PsThreadType))
{
goto Exit;
}
if (Info->KernelHandle)
{
goto Exit;
}
actor = KphGetCurrentThreadContext();
if (!actor || !actor->ProcessContext)
{
goto Exit;
}
if (Info->ObjectType == *PsProcessType)
{
process = KphGetEProcessContext(Info->Object);
}
else
{
PKPH_THREAD_CONTEXT thread;
NT_ASSERT(Info->ObjectType == *PsThreadType);
thread = KphGetEThreadContext(Info->Object);
if (thread)
{
process = thread->ProcessContext;
if (process)
{
KphReferenceObject(process);
}
KphDereferenceObject(thread);
}
}
if (!process)
{
goto Exit;
}
if (Info->Operation == OB_OPERATION_HANDLE_CREATE)
{
access = &Info->Parameters->CreateHandleInformation.DesiredAccess;
desiredAccess = *access;
}
else
{
NT_ASSERT(Info->Operation == OB_OPERATION_HANDLE_DUPLICATE);
access = &Info->Parameters->DuplicateHandleInformation.DesiredAccess;
desiredAccess = *access;
}
KphAcquireRWLockShared(&process->ProtectionLock);
releaseLock = TRUE;
if (!process->Protected)
{
goto Exit;
}
allow = FALSE;
status = KphpShouldAllowObjectAccess(Info,
actor->ProcessContext,
process,
&allow);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_WARNING,
PROTECTION,
"KphpShouldAllowObjectAccess failed: %!STATUS!",
status);
//
// We shouldn't get here since we would have succeeded when starting to
// protect the process to begin with. So if we fail here we fail-safe
// and do not allow.
//
allow = FALSE;
}
if (allow)
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
PROTECTION,
"Allowing process %wZ (%lu) access (0x%08x) to "
"process %wZ (%lu)",
&actor->ProcessContext->ImageName,
HandleToULong(actor->ProcessContext->ProcessId),
desiredAccess,
&process->ImageName,
HandleToULong(process->ProcessId));
goto Exit;
}
if (Info->ObjectType == *PsProcessType)
{
allowedAccessMask = process->ProcessAllowedMask;
if (KphpShouldPermitCreatorProcess(Info, actor, process))
{
allowedAccessMask |= (KPH_PROCESS_READ_ACCESS |
PROCESS_TERMINATE |
PROCESS_VM_OPERATION |
PROCESS_VM_WRITE);
if ((allowedAccessMask & process->ProcessAllowedMask)
!= allowedAccessMask)
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
PROTECTION,
"Permitting extra process handle access "
"(0x%08x -> 0x%08x) in creator process %wZ (%lu) for "
"process %wZ (%lu)",
process->ProcessAllowedMask,
allowedAccessMask,
&actor->ProcessContext->ImageName,
HandleToULong(actor->ProcessContext->ProcessId),
&process->ImageName,
HandleToULong(process->ProcessId));
}
}
if ((desiredAccess & allowedAccessMask) != desiredAccess)
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
PROTECTION,
"Stripping process handle (0x%08x -> 0x%08x) "
"permissions in process %wZ (%lu) for process %wZ (%lu) (0x%08x)",
desiredAccess,
(desiredAccess & allowedAccessMask),
&actor->ProcessContext->ImageName,
HandleToULong(actor->ProcessContext->ProcessId),
&process->ImageName,
HandleToULong(process->ProcessId),
allowedAccessMask);
*access = (desiredAccess & allowedAccessMask);
}
}
else
{
NT_ASSERT(Info->ObjectType == *PsThreadType);
allowedAccessMask = process->ThreadAllowedMask;
if (KphpShouldPermitCreatorProcess(Info, actor, process))
{
allowedAccessMask |= (KPH_THREAD_READ_ACCESS |
THREAD_TERMINATE |
THREAD_RESUME);
if ((allowedAccessMask & process->ThreadAllowedMask)
!= allowedAccessMask)
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
PROTECTION,
"Permitting extra thread handle access "
"(0x%08x -> 0x%08x) in creator process %wZ (%lu) for "
"process %wZ (%lu)",
process->ThreadAllowedMask,
allowedAccessMask,
&actor->ProcessContext->ImageName,
HandleToULong(actor->ProcessContext->ProcessId),
&process->ImageName,
HandleToULong(process->ProcessId));
}
}
if ((desiredAccess & allowedAccessMask) != desiredAccess)
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
PROTECTION,
"Stripping thread handle (0x%08x -> 0x%08x) "
"permissions in process %wZ (%lu) for process %wZ (%lu) (0x%08x)",
desiredAccess,
(desiredAccess & allowedAccessMask),
&actor->ProcessContext->ImageName,
HandleToULong(actor->ProcessContext->ProcessId),
&process->ImageName,
HandleToULong(process->ProcessId),
allowedAccessMask);
*access = (desiredAccess & allowedAccessMask);
}
}
Exit:
if (process)
{
if (releaseLock)
{
KphReleaseRWLock(&process->ProtectionLock);
}
KphDereferenceObject(process);
}
if (actor)
{
KphDereferenceObject(actor);
}
}
/**
* \brief Cleanup routine for the image load APC.
*
* \param[in] Apc The APC to clean up.
* \param[in] Reason Unused.
*/
_Function_class_(KSI_KCLEANUP_ROUTINE)
_IRQL_requires_min_(PASSIVE_LEVEL)
_IRQL_requires_max_(APC_LEVEL)
_IRQL_requires_same_
VOID KSIAPI KphpImageLoadCleanupRoutine(
_In_ PKSI_KAPC Apc,
_In_ KSI_KAPC_CLEANUP_REASON Reason
)
{
PKPH_IMAGE_LOAD_APC apc;
KPH_PAGED_CODE();
UNREFERENCED_PARAMETER(Reason);
apc = CONTAINING_RECORD(Apc, KPH_IMAGE_LOAD_APC, Apc);
KphDereferenceObject(apc);
}
/**
* \brief Normal kernel APC routine where we carry out image load denial.
*
* \param[in] NormalContext Unused.
* \param[in] SystemArgument1 Image load APC object.
* \param[in] SystemArgument2 Unused.
*/
_Function_class_(KNORMAL_ROUTINE)
_IRQL_requires_(PASSIVE_LEVEL)
_IRQL_requires_same_
VOID NTAPI KphpImageLoadKernelNormalRoutine(
_In_opt_ PVOID NormalContext,
_In_opt_ PVOID SystemArgument1,
_In_opt_ PVOID SystemArgument2
)
{
NTSTATUS status;
PKPH_IMAGE_LOAD_APC apc;
KAPC_STATE apcState;
BOOLEAN attachToTarget;
KPH_PAGED_CODE_PASSIVE();
UNREFERENCED_PARAMETER(NormalContext);
UNREFERENCED_PARAMETER(SystemArgument2);
NT_ASSERT(SystemArgument1);
apc = SystemArgument1;
NT_ASSERT(apc->ImageBase <= MmHighestUserAddress);
attachToTarget = (apc->Process->EProcess != PsGetCurrentProcess());
if (attachToTarget)
{
KeStackAttachProcess(apc->Process->EProcess, &apcState);
}
status = ZwUnmapViewOfSection(ZwCurrentProcess(), apc->ImageBase);
if (attachToTarget)
{
KeUnstackDetachProcess(&apcState);
}
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
PROTECTION,
"ZwUnmapViewOfSection failed (%wZ (%lu), %p): %!STATUS!",
&apc->Process->ImageName,
HandleToULong(apc->Process->ProcessId),
apc->ImageBase,
status);
InterlockedIncrementSizeT(&apc->Process->NumberOfUntrustedImageLoads);
}
else
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
PROTECTION,
"Unmapped %p from process %wZ (%lu)",
apc->ImageBase,
&apc->Process->ImageName,
HandleToULong(apc->Process->ProcessId));
}
}
/**
* \param Second kernel APC routine, say hi on the way down to passive :).
*
* \param[in] Apc Unused.
* \param[in,out] NormalRoutine Pointer to the normal kernel APC routine.
* \param[in,out] NormalContext Unused.
* \param[in,out] SystemArgument1 Pointer to the image load APC object.
* \param[in,out] SystemArgument2 Unused.
*/
_Function_class_(KSI_KKERNEL_ROUTINE)
_IRQL_requires_(APC_LEVEL)
_IRQL_requires_same_
VOID KSIAPI KphpImageLoadKernelRoutineSecond(
_In_ PKSI_KAPC Apc,
_Inout_ _Deref_pre_maybenull_ PKNORMAL_ROUTINE* NormalRoutine,
_Inout_ _Deref_pre_maybenull_ PVOID* NormalContext,
_Inout_ _Deref_pre_maybenull_ PVOID* SystemArgument1,
_Inout_ _Deref_pre_maybenull_ PVOID* SystemArgument2
)
{
KPH_PAGED_CODE_APC();
UNREFERENCED_PARAMETER(Apc);
DBG_UNREFERENCED_PARAMETER(NormalRoutine);
UNREFERENCED_PARAMETER(NormalContext);
DBG_UNREFERENCED_PARAMETER(SystemArgument1);
UNREFERENCED_PARAMETER(SystemArgument2);
NT_ASSERT(*NormalRoutine == KphpImageLoadKernelNormalRoutine);
NT_ASSERT(*SystemArgument1);
NT_ASSERT(KphGetObjectType(*SystemArgument1) == KphpImageLoadApcType);
}
/**
* \brief First kernel APC routine, fired by the thread returning from the
* system. We will stage another normal kernel APC to fire at passive here.
*
* \param[in] Apc The APC object that is executing.
* \param[in,out] NormalRoutine Points to the faked routine, we'll cancel it.
* \param[in,out] NormalContext Unused.
* \param[in,out] SystemArgument1 Unused.
* \param[in,out] SystemArgument2 Unused.
*/
_Function_class_(KSI_KKERNEL_ROUTINE)
_IRQL_requires_(APC_LEVEL)
_IRQL_requires_same_
VOID KSIAPI KphpImageLoadKernelRoutineFirst(
_In_ PKSI_KAPC Apc,
_Inout_ _Deref_pre_maybenull_ PKNORMAL_ROUTINE* NormalRoutine,
_Inout_ _Deref_pre_maybenull_ PVOID* NormalContext,
_Inout_ _Deref_pre_maybenull_ PVOID* SystemArgument1,
_Inout_ _Deref_pre_maybenull_ PVOID* SystemArgument2
)
{
NTSTATUS status;
PKPH_IMAGE_LOAD_APC firstApc;
PKPH_IMAGE_LOAD_APC secondApc;
KPH_IMAGE_LOAD_APC_INIT init;
KPH_PAGED_CODE_APC();
secondApc = NULL;
firstApc = CONTAINING_RECORD(Apc, KPH_IMAGE_LOAD_APC, Apc);
UNREFERENCED_PARAMETER(NormalRoutine);
*NormalContext = NULL;
*SystemArgument1 = NULL;
*SystemArgument2 = NULL;
init.Process = firstApc->Process;
init.ImageBase = firstApc->ImageBase;
init.FileObject = NULL;
status = KphCreateObject(KphpImageLoadApcType,
sizeof(KPH_IMAGE_LOAD_APC),
&secondApc,
&init);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
PROTECTION,
"KphCreateObject failed: %!STATUS!",
status);
secondApc = NULL;
goto Exit;
}
//
// Initialize a normal kernel ACP to drop down to passive.
//
KsiInitializeApc(&secondApc->Apc,
KphDriverObject,
KeGetCurrentThread(),
OriginalApcEnvironment,
KphpImageLoadKernelRoutineSecond,
KphpImageLoadCleanupRoutine,
KphpImageLoadKernelNormalRoutine,
KernelMode,
NULL);
if (!KsiInsertQueueApc(&secondApc->Apc, secondApc, NULL, IO_NO_INCREMENT))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
PROTECTION,
"KsiInsertQueueApc failed");
status = STATUS_UNSUCCESSFUL;
goto Exit;
}
secondApc = NULL;
status = STATUS_SUCCESS;
Exit:
if (secondApc)
{
KphDereferenceObject(secondApc);
}
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
PROTECTION,
"Untrusted image load (%wZ (%lu), %p): %!STATUS!",
&firstApc->Process->ImageName,
HandleToULong(firstApc->Process->ProcessId),
firstApc->ImageBase,
status);
//
// No choice other than to just track that there is an untrusted image.
//
InterlockedIncrementSizeT(&firstApc->Process->NumberOfUntrustedImageLoads);
}
}
/**
* \brief Handles an untrusted image load in a verified process.
*
* \details If a failure is encountered in this path or if image load denial
* is disabled we will denote in the target process that an untrusted image
* was loaded. The approach here is to queue a user APC to execute when
* the thread returns from the system. Then, we issue another APC to drop
* down to passive level and remove the image mapping from the process.
*
* \param[in,out] Process The process where the image is being loaded.
* \param[in] ImageBase The base address of the untrusted image.
*/
_IRQL_requires_max_(PASSIVE_LEVEL)
VOID KphpHandleUntrustedImageLoad(
_Inout_ PKPH_PROCESS_CONTEXT Process,
_In_ PVOID ImageBase
)
{
NTSTATUS status;
PKPH_THREAD_CONTEXT actor;
KPH_IMAGE_LOAD_APC_INIT init;
PKPH_IMAGE_LOAD_APC apc;
KPH_PAGED_CODE_PASSIVE();
NT_ASSERT(Process->VerifiedProcess);
status = STATUS_UNSUCCESSFUL;
actor = NULL;
apc = NULL;
if (KphParameterFlags.DisableImageLoadProtection)
{
//
// Image load protections are disable. Do not deny the image load. We
// will still mark an untrusted image load. This will restrict access
// to the driver.
//
KphTracePrint(TRACE_LEVEL_VERBOSE,
PROTECTION,
"Image load protections are disabled.");
goto Exit;
}
if ((ReadSizeTAcquire(&Process->NumberOfImageLoads) == 1) &&
(PsGetProcessSectionBaseAddress(Process->EProcess) == ImageBase))
{
//
// The primary image is being loaded. Rather than deny the image load
// we will allow it to go through but will still mark an untrusted
// image load. This will restrict access to the driver. But will allow
// the process to continue starting.
//
// N.B. If another untrusted image is loaded other than the primary
// image that is critical to starting the process, it will still fail
// to start.
//
KphTracePrint(TRACE_LEVEL_VERBOSE,
PROTECTION,
"Image base is the process section base address.");
goto Exit;
}
actor = KphGetCurrentThreadContext();
if (!actor || !actor->ProcessContext)
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
PROTECTION,
"Insufficient tracking.");
status = STATUS_INSUFFICIENT_RESOURCES;
goto Exit;
}
status = KphCheckProcessApcNoopRoutine(actor->ProcessContext);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
PROTECTION,
"KphCheckProcessApcNoopRoutine failed: %!STATUS!",
status);
goto Exit;
}
if (ImageBase > MmHighestUserAddress)
{
KphTracePrint(TRACE_LEVEL_VERBOSE, PROTECTION, "Invalid image base!");
//
// This isn't an error, we just check for safety downstream.
//
status = STATUS_SUCCESS;
goto Exit;
}
init.Process = Process;
init.ImageBase = ImageBase;
init.FileObject = NULL;
status = KphCreateObject(KphpImageLoadApcType,
sizeof(KPH_IMAGE_LOAD_APC),
&apc,
&init);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
PROTECTION,
"KphCreateObject failed: %!STATUS!",
status);
apc = NULL;
goto Exit;
}
KsiInitializeApc(&apc->Apc,
KphDriverObject,
actor->EThread,
OriginalApcEnvironment,
KphpImageLoadKernelRoutineFirst,
KphpImageLoadCleanupRoutine,
actor->ProcessContext->ApcNoopRoutine,
UserMode,
NULL);
if (!KsiInsertQueueApc(&apc->Apc, NULL, NULL, IO_NO_INCREMENT))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
PROTECTION,
"KsiInsertQueueApc failed");
status = STATUS_UNSUCCESSFUL;
goto Exit;
}
//
// Stage the thread for user mode APC delivery.
//
KeTestAlertThread(UserMode);
apc = NULL;
status = STATUS_SUCCESS;
Exit:
if (apc)
{
KphDereferenceObject(apc);
}
if (actor)
{
KphDereferenceObject(actor);
}
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
PROTECTION,
"Untrusted image load (%wZ (%lu), %p): %!STATUS!",
&Process->ImageName,
HandleToULong(Process->ProcessId),
ImageBase,
status);
//
// No choice other than to just track that there is an untrusted image.
//
InterlockedIncrementSizeT(&Process->NumberOfUntrustedImageLoads);
}
}
/**
* \brief Re-opens the image file object for the image being loaded.
*
* \details This is unfortunately necessary since the file object from the
* image load callback is in a state that renders I/O inoperable, due to
* FO_CLEANUP_COMPLETE being set.
*
* \param[in] ImageFileObject The image file object.
* \param[out] FileHandle Receives a handle the to the file.
* \param[out] FileObject Receives a reference to the file object.
* \param[out] Filename Receives the file name of the image, must be freed
* using KphFreeNameFileObject.
* \param[out] ImageBase Receives the base address of the image mapping in the
* system address space. Must be unmapped with KphUnmapViewInSystem.
* \param[out] ImageSize Receives the size of the image mapping.
* \param[out] DataBase Receives the base address of the data mapping in the
* system address space. Must be unmapped with KphUnmapViewInSystem.
* \param[out] DataSize Receives the size of the data mapping.
*
* \return Successful or errant status.
*/
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KphpReOpenImageFile(
_In_ PFILE_OBJECT ImageFileObject,
_Out_ PHANDLE FileHandle,
_Out_ PFILE_OBJECT* FileObject,
_Out_ PUNICODE_STRING* FileName,
_Out_ PVOID* ImageBase,
_Out_ PSIZE_T ImageSize,
_Out_ PVOID* DataBase,
_Out_ PSIZE_T DataSize
)
{
NTSTATUS status;
PUNICODE_STRING fileName;
OBJECT_ATTRIBUTES objectAttributes;
IO_STATUS_BLOCK ioStatusBlock;
HANDLE fileHandle;
PFILE_OBJECT fileObject;
PVOID imageBase;
SIZE_T imageSize;
PVOID dataBase;
SIZE_T dataSize;
KPH_PAGED_CODE_PASSIVE();
fileHandle = NULL;
fileObject = NULL;
imageBase = NULL;
dataBase = NULL;
*FileObject = NULL;
*FileName = NULL;
*ImageBase = NULL;
*ImageSize = 0;
*DataBase = NULL;
*DataSize = 0;
status = KphGetNameFileObject(ImageFileObject, &fileName);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
PROTECTION,
"KphGetNameFileObject failed: %!STATUS!",
status);
goto Exit;
}
InitializeObjectAttributes(&objectAttributes,
fileName,
OBJ_KERNEL_HANDLE | OBJ_DONT_REPARSE,
NULL,
NULL);
status = KphCreateFile(&fileHandle,
FILE_READ_ACCESS | SYNCHRONIZE,
&objectAttributes,
&ioStatusBlock,
NULL,
FILE_ATTRIBUTE_NORMAL,
FILE_SHARE_READ,
FILE_OPEN,
(FILE_NON_DIRECTORY_FILE |
FILE_SYNCHRONOUS_IO_NONALERT |
FILE_COMPLETE_IF_OPLOCKED),
NULL,
0,
IO_IGNORE_SHARE_ACCESS_CHECK,
KernelMode);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
PROTECTION,
"KphCreateFile failed: %!STATUS!",
status);
fileHandle = NULL;
goto Exit;
}
else if (status == STATUS_OPLOCK_BREAK_IN_PROGRESS)
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
PROTECTION,
"KphCreateFile failed: %!STATUS!",
status);
status = STATUS_SHARING_VIOLATION;
goto Exit;
}
status = ObReferenceObjectByHandle(fileHandle,
0,
*IoFileObjectType,
KernelMode,
&fileObject,
NULL);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
PROTECTION,
"ObReferenceObjectByHandle failed: %!STATUS!",
status);
fileObject = NULL;
goto Exit;
}
imageSize = 0;
status = KphMapViewInSystem(fileHandle,
KPH_MAP_IMAGE,
&imageBase,
&imageSize);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
PROTECTION,
"KphMapViewInSystem failed: %!STATUS!",
status);
imageBase = NULL;
goto Exit;
}
dataSize = 0;
status = KphMapViewInSystem(fileHandle,
KPH_MAP_DATA,
&dataBase,
&dataSize);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
PROTECTION,
"KphMapViewInSystem failed: %!STATUS!",
status);
imageBase = NULL;
goto Exit;
}
*FileHandle = fileHandle;
fileHandle = NULL;
*FileObject = fileObject;
fileObject = NULL;
*FileName = fileName;
fileName = NULL;
*ImageBase = imageBase;
imageBase = NULL;
*ImageSize = imageSize;
*DataBase = dataBase;
dataBase = NULL;
*DataSize = dataSize;
Exit:
if (dataBase)
{
KphUnmapViewInSystem(dataBase);
}
if (imageBase)
{
KphUnmapViewInSystem(imageBase);
}
if (fileHandle)
{
ObCloseHandle(fileHandle, KernelMode);
}
if (fileObject)
{
ObDereferenceObject(fileObject);
}
if (fileName)
{
KphFreeNameFileObject(fileName);
}
return status;
}
/**
* \brief Applies image protections on verified processes.
*
* \param[in,out] Process The process where the image is being loaded.
* \param[in] ImageBase The base address of the image.
* \param[in] FileObject The file object for the image being loaded.
*/
_IRQL_requires_max_(PASSIVE_LEVEL)
VOID KphpApplyImageProtections(
_Inout_ PKPH_PROCESS_CONTEXT Process,
_In_ PVOID ImageBase,
_In_ PFILE_OBJECT FileObject
)
{
NTSTATUS status;
PSIZE_T imageLoadCounter;
HANDLE fileHandle;
PFILE_OBJECT fileObject;
PUNICODE_STRING fileName;
PVOID imageBase;
SIZE_T imageSize;
PVOID dataBase;
SIZE_T dataSize;
SE_SIGNING_LEVEL signingLevel;
KPH_PAGED_CODE_PASSIVE();
NT_ASSERT(!KeAreAllApcsDisabled());
imageLoadCounter = NULL;
fileHandle = NULL;
fileObject = NULL;
fileName = NULL;
imageBase = NULL;
dataBase = NULL;
if (FileObject->WriteAccess)
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
PROTECTION,
"%wZ (%lu) image \"%wZ\" is writable",
&Process->ImageName,
HandleToULong(Process->ProcessId),
&FileObject->FileName);
goto Exit;
}
if (IoGetTransactionParameterBlock(FileObject))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
PROTECTION,
"%wZ (%lu) image \"%wZ\" is in a transaction",
&Process->ImageName,
HandleToULong(Process->ProcessId),
&FileObject->FileName);
goto Exit;
}
if (!FileObject->SectionObjectPointer ||
MmDoesFileHaveUserWritableReferences(FileObject->SectionObjectPointer))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
PROTECTION,
"%wZ (%lu) image \"%wZ\" has user writable references",
&Process->ImageName,
HandleToULong(Process->ProcessId),
&FileObject->FileName);
goto Exit;
}
status = KphpReOpenImageFile(FileObject,
&fileHandle,
&fileObject,
&fileName,
&imageBase,
&imageSize,
&dataBase,
&dataSize);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
PROTECTION,
"KphpReOpenImageFile: %wZ (%lu) \"%wZ\": %!STATUS!",
&Process->ImageName,
HandleToULong(Process->ProcessId),
&FileObject->FileName,
status);
goto Exit;
}
if (!KphIsSameFile(FileObject, fileObject))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
PROTECTION,
"KphIsSameFile failed: %wZ (%lu) \"%wZ\" \"%wZ\"",
&Process->ImageName,
HandleToULong(Process->ProcessId),
&FileObject->FileName,
fileName);
goto Exit;
}
status = KphGetSigningLevel(fileObject, &signingLevel);
KphTracePrint(TRACE_LEVEL_VERBOSE,
PROTECTION,
"KphpGetSigningLevel: %wZ (%lu) \"%wZ\": 0x%02x %!STATUS!",
&Process->ImageName,
HandleToULong(Process->ProcessId),
fileName,
signingLevel,
status);
if (!NT_SUCCESS(status))
{
signingLevel = SE_SIGNING_LEVEL_UNCHECKED;
}
switch (signingLevel)
{
case SE_SIGNING_LEVEL_MICROSOFT:
case SE_SIGNING_LEVEL_WINDOWS:
case SE_SIGNING_LEVEL_WINDOWS_TCB:
{
imageLoadCounter = &Process->NumberOfMicrosoftImageLoads;
goto CheckCoherency;
}
case SE_SIGNING_LEVEL_ANTIMALWARE:
{
imageLoadCounter = &Process->NumberOfAntimalwareImageLoads;
goto CheckCoherency;
}
default:
{
break;
}
}
status = KphVerifyFileObject(fileObject, fileName);
KphTracePrint(TRACE_LEVEL_VERBOSE,
PROTECTION,
"KphVerifyFileObject: %wZ (%lu) \"%wZ\": %!STATUS!",
&Process->ImageName,
HandleToULong(Process->ProcessId),
fileName,
status);
if (NT_SUCCESS(status))
{
imageLoadCounter = &Process->NumberOfVerifiedImageLoads;
goto CheckCoherency;
}
CheckCoherency:
status = KphCheckImageCoherency(imageBase, imageSize, dataBase, dataSize);
KphTracePrint(TRACE_LEVEL_VERBOSE,
PROTECTION,
"KphCheckImageCoherency: %wZ (%lu) \"%wZ\": %!STATUS!",
&Process->ImageName,
HandleToULong(Process->ProcessId),
fileName,
status);
if (!NT_SUCCESS(status))
{
imageLoadCounter = NULL;
goto Exit;
}
Exit:
if (dataBase)
{
KphUnmapViewInSystem(dataBase);
}
if (imageBase)
{
KphUnmapViewInSystem(imageBase);
}
if (imageLoadCounter)
{
InterlockedIncrementSizeT(imageLoadCounter);
}
else
{
KphpHandleUntrustedImageLoad(Process, ImageBase);
}
if (fileName)
{
KphFreeNameFileObject(fileName);
}
if (fileObject)
{
ObDereferenceObject(fileObject);
}
if (fileHandle)
{
ObCloseHandle(fileHandle, KernelMode);
}
}
/**
* \brief Normal kernel APC routine for when we must get outside of APC
* disablement.
*
* \param[in] NormalContext Unused.
* \param[in] SystemArgument1 Image load APC object.
* \param[in] SystemArgument2 Unused.
*/
_Function_class_(KNORMAL_ROUTINE)
_IRQL_requires_(PASSIVE_LEVEL)
_IRQL_requires_same_
VOID NTAPI KphpImageLoadKernelNormalRoutineApcsDisabled(
_In_opt_ PVOID NormalContext,
_In_opt_ PVOID SystemArgument1,
_In_opt_ PVOID SystemArgument2
)
{
PKPH_IMAGE_LOAD_APC apc;
KPH_PAGED_CODE_PASSIVE();
UNREFERENCED_PARAMETER(NormalContext);
UNREFERENCED_PARAMETER(SystemArgument2);
NT_ASSERT(SystemArgument1);
apc = SystemArgument1;
NT_ASSERT(apc->FileObject);
KphpApplyImageProtections(apc->Process, apc->ImageBase, apc->FileObject);
}
/**
* \brief Kernel APC routine for when we must get outside of APC disablement.
*
* \param[in] Apc Unused.
* \param[in,out] NormalRoutine Pointer to the normal kernel APC routine.
* \param[in,out] NormalContext Unused.
* \param[in,out] SystemArgument1 Pointer to the image load APC object.
* \param[in,out] SystemArgument2 Unused.
*/
_Function_class_(KSI_KKERNEL_ROUTINE)
_IRQL_requires_(APC_LEVEL)
_IRQL_requires_same_
VOID KSIAPI KphpImageLoadKernelRoutineApcsDisabled(
_In_ PKSI_KAPC Apc,
_Inout_ _Deref_pre_maybenull_ PKNORMAL_ROUTINE* NormalRoutine,
_Inout_ _Deref_pre_maybenull_ PVOID* NormalContext,
_Inout_ _Deref_pre_maybenull_ PVOID* SystemArgument1,
_Inout_ _Deref_pre_maybenull_ PVOID* SystemArgument2
)
{
KPH_PAGED_CODE_APC();
UNREFERENCED_PARAMETER(Apc);
DBG_UNREFERENCED_PARAMETER(NormalRoutine);
UNREFERENCED_PARAMETER(NormalContext);
DBG_UNREFERENCED_PARAMETER(SystemArgument1);
UNREFERENCED_PARAMETER(SystemArgument2);
NT_ASSERT(*NormalRoutine == KphpImageLoadKernelNormalRoutineApcsDisabled);
NT_ASSERT(*SystemArgument1);
NT_ASSERT(KphGetObjectType(*SystemArgument1) == KphpImageLoadApcType);
}
/**
* \brief Applies image protections on verified processes.
*
* \param[in,out] Process The process where the image is being loaded.
* \param[in] ImageBase The base address of the image.
* \param[in] FileObject The file object for the image being loaded.
*/
_IRQL_requires_max_(PASSIVE_LEVEL)
VOID KphpApplyImageProtectionsApcsDisabled(
_Inout_ PKPH_PROCESS_CONTEXT Process,
_In_ PVOID ImageBase,
_In_ PFILE_OBJECT FileObject
)
{
NTSTATUS status;
PKPH_IMAGE_LOAD_APC apc;
KPH_IMAGE_LOAD_APC_INIT init;
KPH_PAGED_CODE_PASSIVE();
NT_ASSERT(KeAreAllApcsDisabled());
init.Process = Process;
init.ImageBase = ImageBase;
init.FileObject = FileObject;
status = KphCreateObject(KphpImageLoadApcType,
sizeof(KPH_IMAGE_LOAD_APC),
&apc,
&init);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
PROTECTION,
"KphCreateObject failed: %!STATUS!",
status);
apc = NULL;
goto Exit;
}
//
// Initialize a normal kernel ACP to handle this outside of the callback.
//
KsiInitializeApc(&apc->Apc,
KphDriverObject,
KeGetCurrentThread(),
OriginalApcEnvironment,
KphpImageLoadKernelRoutineApcsDisabled,
KphpImageLoadCleanupRoutine,
KphpImageLoadKernelNormalRoutineApcsDisabled,
KernelMode,
NULL);
if (!KsiInsertQueueApc(&apc->Apc, apc, NULL, IO_NO_INCREMENT))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
PROTECTION,
"KsiInsertQueueApc failed");
status = STATUS_UNSUCCESSFUL;
goto Exit;
}
apc = NULL;
status = STATUS_SUCCESS;
Exit:
if (apc)
{
KphDereferenceObject(apc);
}
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
PROTECTION,
"Untrusted image load (%wZ (%lu), %p): %!STATUS!",
&Process->ImageName,
HandleToULong(Process->ProcessId),
ImageBase,
status);
//
// No choice other than to just track that there is an untrusted image.
//
InterlockedIncrementSizeT(&Process->NumberOfUntrustedImageLoads);
}
}
/**
* \brief Applies image protections on verified processes.
*
* \param[in,out] Process The process where the image is being loaded.
* \param[in] ImageInfo The image info from the notification routine.
*/
_IRQL_requires_max_(PASSIVE_LEVEL)
VOID KphApplyImageProtections(
_Inout_ PKPH_PROCESS_CONTEXT Process,
_In_ PIMAGE_INFO_EX ImageInfo
)
{
KPH_PAGED_CODE_PASSIVE();
KphAcquireRWLockShared(&Process->ProtectionLock);
if (!Process->VerifiedProcess || !Process->Protected)
{
goto Exit;
}
if ((ImageInfo->ImageInfo.ImageSignatureLevel == SE_SIGNING_LEVEL_MICROSOFT) ||
(ImageInfo->ImageInfo.ImageSignatureLevel == SE_SIGNING_LEVEL_WINDOWS) ||
(ImageInfo->ImageInfo.ImageSignatureLevel == SE_SIGNING_LEVEL_WINDOWS_TCB))
{
InterlockedIncrementSizeT(&Process->NumberOfMicrosoftImageLoads);
goto Exit;
}
if (ImageInfo->ImageInfo.ImageSignatureLevel == SE_SIGNING_LEVEL_ANTIMALWARE)
{
InterlockedIncrementSizeT(&Process->NumberOfAntimalwareImageLoads);
goto Exit;
}
//
// We have to do a signing check ourselves which involves looking up the
// file name. If we can do it now, do so, otherwise handle it later.
//
if (KeAreAllApcsDisabled())
{
//
// We can't safely (or accurately) look up the file name. We have to
// special case it.
//
KphpApplyImageProtectionsApcsDisabled(Process,
ImageInfo->ImageInfo.ImageBase,
ImageInfo->FileObject);
}
else
{
KphpApplyImageProtections(Process,
ImageInfo->ImageInfo.ImageBase,
ImageInfo->FileObject);
}
Exit:
KphReleaseRWLock(&Process->ProtectionLock);
}
/**
* \brief Acquires a reference to the driver unload protection. Enables driver
* unload protection if this is the first reference.
*
* \param[out] PreviousCount Optionally set to the previous reference count.
*
* \return Successful or errant status.
*/
_IRQL_requires_max_(APC_LEVEL)
_Must_inspect_result_
NTSTATUS KphAcquireDriverUnloadProtection(
_Out_opt_ PLONG PreviousCount
)
{
NTSTATUS status;
LONG previousCount;
KPH_PAGED_CODE();
status = KphAcquireReference(&KphpDriverUnloadProtectionRef,
&previousCount);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
PROTECTION,
"KphAcquireReference failed: %!STATUS!",
status);
previousCount = 0;
goto Exit;
}
KphTracePrint(TRACE_LEVEL_VERBOSE,
PROTECTION,
"Acquired driver unload protection (%ld)",
previousCount + 1);
if (previousCount == 0)
{
#pragma prefast(push)
#pragma prefast(disable : 28175)
NT_ASSERT(KphDriverObject->DriverUnload);
NT_ASSERT(!KphpDriverUnloadPreviousRoutine);
KphpDriverUnloadPreviousRoutine = InterlockedExchangePointer(
(PVOID*)&KphDriverObject->DriverUnload,
NULL);
NT_ASSERT(!KphDriverObject->DriverUnload);
NT_ASSERT(KphpDriverUnloadPreviousRoutine);
#pragma prefast(pop)
KphTracePrint(TRACE_LEVEL_INFORMATION,
PROTECTION,
"Driver unload protection activated");
}
Exit:
if (PreviousCount)
{
*PreviousCount = previousCount;
}
return STATUS_SUCCESS;
}
/**
* \brief Releases a reference to the driver unload protection. Disables driver
* unload protection if this is the last reference.
*
* \param[out] PreviousCount Optionally set to the previous reference count.
*
* \return Successful or errant status.
*/
_IRQL_requires_max_(APC_LEVEL)
_Must_inspect_result_
NTSTATUS KphReleaseDriverUnloadProtection(
_Out_opt_ PLONG PreviousCount
)
{
NTSTATUS status;
LONG previousCount;
KPH_PAGED_CODE();
status = KphReleaseReference(&KphpDriverUnloadProtectionRef,
&previousCount);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
PROTECTION,
"KphReleaseReference failed: %!STATUS!",
status);
previousCount = 0;
goto Exit;
}
KphTracePrint(TRACE_LEVEL_VERBOSE,
PROTECTION,
"Released driver unload protection (%ld)",
previousCount - 1);
if (previousCount == 1)
{
#pragma prefast(push)
#pragma prefast(disable : 28175)
NT_ASSERT(!KphDriverObject->DriverUnload);
NT_ASSERT(KphpDriverUnloadPreviousRoutine);
KphpDriverUnloadPreviousRoutine = InterlockedExchangePointer(
(PVOID*)&KphDriverObject->DriverUnload,
KphpDriverUnloadPreviousRoutine);
NT_ASSERT(KphDriverObject->DriverUnload);
NT_ASSERT(!KphpDriverUnloadPreviousRoutine);
#pragma prefast(pop)
KphTracePrint(TRACE_LEVEL_INFORMATION,
PROTECTION,
"Driver unload protection deactivated");
}
Exit:
if (PreviousCount)
{
*PreviousCount = previousCount;
}
return status;
}
/**
* \brief Retrieves the current driver unload protection reference count.
*
* \details If the driver unload protection reference count is greater than
* zero, the driver unload protection is active.
*
* \return The current driver unload protection reference count.
*/
_IRQL_requires_max_(APC_LEVEL)
LONG KphGetDriverUnloadProtectionCount(
VOID
)
{
LONG count;
KPH_PAGED_CODE();
count = ReadAcquire(&KphpDriverUnloadProtectionRef.Count);
return count;
}
/**
* \brief Strips the process and thread allowed masks from a protected process.
*
* \details Callers may only strip allowed masks. In other words, callers may
* only make the protection more restrictive.
*
* \param[in] Process The protected process to strip the masks from.
* \param[in] ProcessAllowedMask The process allowed mask to strip.
* \param[in] ThreadAllowedMask The thread allowed mask to strip.
*
* \return Successful or errant status.
*/
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KphpStripProtectedProcessMasks(
_In_ PKPH_PROCESS_CONTEXT Process,
_In_ ACCESS_MASK ProcessAllowedMask,
_In_ ACCESS_MASK ThreadAllowedMask
)
{
NTSTATUS status;
PKPH_DYN dyn;
KPH_ENUM_FOR_PROTECTION context;
ACCESS_MASK prevProcessAllowedMask;
ACCESS_MASK prevThreadAllowedMask;
KPH_PAGED_CODE_PASSIVE();
dyn = KphReferenceDynData();
if (!dyn)
{
return STATUS_NOINTERFACE;
}
KphAcquireRWLockExclusive(&Process->ProtectionLock);
if (!Process->Protected)
{
status = STATUS_INVALID_PARAMETER;
goto Exit;
}
prevProcessAllowedMask = Process->ProcessAllowedMask;
prevThreadAllowedMask = Process->ThreadAllowedMask;
Process->ProcessAllowedMask &= ~ProcessAllowedMask;
Process->ThreadAllowedMask &= ~ThreadAllowedMask;
KphTracePrint(TRACE_LEVEL_VERBOSE,
PROTECTION,
"Modifying protected process %wZ (%lu) allowed masks, "
"process: 0x%08x -> 0x%08x, "
"thread: 0x%08x -> 0x%08x",
&Process->ImageName,
HandleToULong(Process->ProcessId),
prevProcessAllowedMask,
Process->ProcessAllowedMask,
prevThreadAllowedMask,
Process->ThreadAllowedMask);
if ((Process->ProcessAllowedMask == prevProcessAllowedMask) &&
(Process->ThreadAllowedMask == prevThreadAllowedMask))
{
status = STATUS_SUCCESS;
goto Exit;
}
context.Dyn = dyn;
context.Status = STATUS_SUCCESS;
context.Process = Process;
KphEnumerateProcessContexts(KphpEnumProcessContextsForProtection, &context);
status = context.Status;
if (!NT_SUCCESS(status))
{
Process->ProcessAllowedMask = prevProcessAllowedMask;
Process->ThreadAllowedMask = prevThreadAllowedMask;
}
Exit:
KphReleaseRWLock(&Process->ProtectionLock);
KphDereferenceObject(dyn);
return status;
}
/**
* \brief Strips the process and thread allowed masks from a protected process.
*
* \details Callers may only strip allowed masks. In other words, callers may
* only make the protection more restrictive.
*
* \param[in] ProcessHandle A handle to a process to strip the masks from.
* \param[in] ProcessAllowedMask The process allowed mask to strip.
* \param[in] ThreadAllowedMask The thread allowed mask to strip.
* \param[in] AccessMode The mode in which to perform access checks.
*
* \return Successful or errant status.
*/
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KphStripProtectedProcessMasks(
_In_ HANDLE ProcessHandle,
_In_ ACCESS_MASK ProcessAllowedMask,
_In_ ACCESS_MASK ThreadAllowedMask,
_In_ KPROCESSOR_MODE AccessMode
)
{
NTSTATUS status;
PEPROCESS processObject;
PKPH_PROCESS_CONTEXT processContext;
KPH_PAGED_CODE_PASSIVE();
processContext = NULL;
status = ObReferenceObjectByHandle(ProcessHandle,
PROCESS_SET_INFORMATION,
*PsProcessType,
AccessMode,
&processObject,
NULL);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
PROTECTION,
"ObReferenceObjectByHandle failed: %!STATUS!",
status);
processObject = NULL;
goto Exit;
}
processContext = KphGetEProcessContext(processObject);
if (!processContext)
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
PROTECTION,
"KphGetEProcessContext failed");
status = STATUS_INSUFFICIENT_RESOURCES;
goto Exit;
}
status = KphpStripProtectedProcessMasks(processContext,
ProcessAllowedMask,
ThreadAllowedMask);
Exit:
if (processContext)
{
KphDereferenceObject(processContext);
}
if (processObject)
{
ObDereferenceObject(processObject);
}
return status;
}
/**
* \brief Initializes the protection infrastructure.
*/
_IRQL_requires_max_(PASSIVE_LEVEL)
VOID KphInitializeProtection(
VOID
)
{
KPH_OBJECT_TYPE_INFO typeInfo;
KPH_PAGED_CODE_PASSIVE();
typeInfo.Allocate = KphpAllocateImageLoadApc;
typeInfo.Initialize = KphpInitializeImageLoadApc;
typeInfo.Delete = KphpDeleteImageLoadApc;
typeInfo.Free = KphpFreeImageLoadApc;
typeInfo.Flags = 0;
KphCreateObjectType(&KphpImageLoadApcTypeName,
&typeInfo,
&KphpImageLoadApcType);
}
================================================
FILE: KSystemInformer/ratelmt.c
================================================
/*
* Copyright (c) 2022 Winsider Seminars & Solutions, Inc. All rights reserved.
*
* This file is part of System Informer.
*
* Authors:
*
* jxy-s 2025-2026
*
*/
#include
#include
//
// Time granularity for rate limiter timestamps (100-nano units per tick).
// 10,000,000 / 100 = 100 ms granularity.
// 2^32 * 100 ms ~= 13.62 years before rollover.
// 100 ms granularity provides a good balance of fill frequency and rollover.
//
#define KPH_RATE_LIMIT_SEC_MULT (100)
#define KPH_RATE_LIMIT_TIME_UNIT (10000000 / KPH_RATE_LIMIT_SEC_MULT)
#define KPH_RATE_LIMIT_ROLL_CHECK (86400 * KPH_RATE_LIMIT_SEC_MULT)
/**
* \brief Initializes a rate limit.
*
* \param[in] Policy The rate limit policy to use.
* \param[in] TimeStamp The current time stamp.
* \param[in] RateLimit Receives the initialized rate limit.
*/
_IRQL_requires_max_(DISPATCH_LEVEL)
VOID KphInitializeRateLimit(
_In_ PCKPH_RATE_LIMIT_POLICY Policy,
_In_ PLARGE_INTEGER TimeStamp,
_Out_ PKPH_RATE_LIMIT RateLimit
)
{
KPH_RATE_BUCKET bucket;
KPH_NPAGED_CODE_DISPATCH_MAX();
bucket.CurrentTokens = Policy->MaxBucketSize;
bucket.LastRefillTime = (ULONG)(TimeStamp->QuadPart / KPH_RATE_LIMIT_TIME_UNIT);
RtlZeroMemory(RateLimit, sizeof(KPH_RATE_LIMIT));
WriteNoFence64(&RateLimit->Bucket.Quad, bucket.Quad);
RtlCopyMemory(&RateLimit->Policy, Policy, sizeof(KPH_RATE_LIMIT_POLICY));
}
/**
* \brief Consumes a token from the rate limit.
*
* \param[in] RateLimit The rate limit to consume a token from.
* \param[in] TimeStamp The current time stamp.
*
* \return TRUE if a token was consumed, FALSE otherwise.
*/
_IRQL_requires_max_(DISPATCH_LEVEL)
BOOLEAN KphRateLimitConsumeToken(
_Inout_ PKPH_RATE_LIMIT RateLimit,
_In_ PLARGE_INTEGER TimeStamp
)
{
KPH_RATE_BUCKET oldBucket;
KPH_RATE_BUCKET newBucket;
ULONG currentTime;
ULONG elapsedTime;
ULONG64 tokensToAdd;
BOOLEAN allowed;
LONG64 expected;
KPH_NPAGED_CODE_DISPATCH_MAX();
//
// KPH_RATE_LIMIT_UNLIMITED
//
if ((RateLimit->Policy.TokensPerPeriod == ULONG_MAX) ||
(RateLimit->Policy.MaxBucketSize == ULONG_MAX))
{
InterlockedIncrementNoFence64(&RateLimit->Allowed);
return TRUE;
}
//
// KPH_RATE_LIMIT_DENY_ALL
//
if ((RateLimit->Policy.TokensPerPeriod == 0) ||
(RateLimit->Policy.PeriodInSeconds == 0) ||
(RateLimit->Policy.MaxBucketSize == 0))
{
InterlockedIncrementNoFence64(&RateLimit->Dropped);
return FALSE;
}
currentTime = (ULONG)(TimeStamp->QuadPart / KPH_RATE_LIMIT_TIME_UNIT);
oldBucket.Quad = ReadNoFence64(&RateLimit->Bucket.Quad);
for (;;)
{
newBucket.Quad = oldBucket.Quad;
if (newBucket.LastRefillTime > currentTime)
{
if ((newBucket.LastRefillTime - currentTime) > KPH_RATE_LIMIT_ROLL_CHECK)
{
newBucket.CurrentTokens = RateLimit->Policy.MaxBucketSize;
newBucket.LastRefillTime = currentTime;
}
goto ConsumeToken;
}
elapsedTime = (currentTime - newBucket.LastRefillTime);
tokensToAdd = ((ULONG64)elapsedTime * RateLimit->Policy.TokensPerPeriod);
tokensToAdd /= ((ULONG64)RateLimit->Policy.PeriodInSeconds * KPH_RATE_LIMIT_SEC_MULT);
if (tokensToAdd == 0)
{
goto ConsumeToken;
}
if ((tokensToAdd >= RateLimit->Policy.MaxBucketSize) ||
(tokensToAdd > (RateLimit->Policy.MaxBucketSize - newBucket.CurrentTokens)))
{
newBucket.CurrentTokens = RateLimit->Policy.MaxBucketSize;
}
else
{
newBucket.CurrentTokens = (newBucket.CurrentTokens + (ULONG)tokensToAdd);
}
newBucket.LastRefillTime = currentTime;
ConsumeToken:
if (newBucket.CurrentTokens > 0)
{
newBucket.CurrentTokens--;
allowed = TRUE;
}
else
{
allowed = FALSE;
}
expected = oldBucket.Quad;
oldBucket.Quad = InterlockedCompareExchange64(&RateLimit->Bucket.Quad,
newBucket.Quad,
expected);
if (oldBucket.Quad != expected)
{
InterlockedIncrementNoFence64(&RateLimit->CasMiss);
continue;
}
if (allowed)
{
InterlockedIncrementNoFence64(&RateLimit->Allowed);
return TRUE;
}
else
{
InterlockedIncrementNoFence64(&RateLimit->Dropped);
return FALSE;
}
}
}
================================================
FILE: KSystemInformer/resource.rc
================================================
// Microsoft Visual C++ generated resource script.
//
#pragma code_page(65001)
#include
#include
#define VER_FILEVERSION 4,0,0,0
#define VER_FILEVERSION_STR "4.0.0\0"
#define VER_PRODUCTVERSION VER_FILEVERSION
#define VER_PRODUCTVERSION_STR VER_FILEVERSION_STR
#if DEBUG
#define VER_DEBUG VS_FF_DEBUG
#define VER_PRERELEASE VS_FF_PRERELEASE
#define VER_PRIVATE VS_FF_PRIVATEBUILD
#else
#define VER_DEBUG 0
#define VER_PRERELEASE 0
#define VER_PRIVATE 0
#endif
#define VER_FILEFLAGSMASK VS_FFI_FILEFLAGSMASK
#define VER_FILEFLAGS (VER_PRERELEASE | VER_DEBUG | VER_PRIVATE)
#define VER_FILEOS VOS_NT_WINDOWS32
#define VER_FILETYPE VFT_DRV
#define VER_FILESUBTYPE VFT2_DRV_SYSTEM
#define VER_COMPANYNAME_STR "System Informer\0"
#define VER_FILEDESCRIPTION_STR "System Informer\0"
#define VER_LEGALCOPYRIGHT_STR "Copyright (c) Winsider Seminars & Solutions, Inc. All rights reserved.\0"
#define VER_ORIGINALFILENAME_STR "SystemInformer.sys\0"
#define VER_INTERNALNAME_STR "SystemInformer.sys\0"
#define VER_PRODUCTNAME_STR "System Informer\0"
#if !defined(DEFAULT_VERSIONINFO)
VS_VERSION_INFO VERSIONINFO
FILEVERSION VER_FILEVERSION
PRODUCTVERSION VER_PRODUCTVERSION
FILEFLAGSMASK VER_FILEFLAGSMASK
FILEFLAGS VER_FILEFLAGS
FILEOS VER_FILEOS
FILETYPE VER_FILETYPE
FILESUBTYPE VER_FILESUBTYPE
BEGIN
BLOCK "StringFileInfo"
BEGIN
BLOCK "040904E4"
BEGIN
VALUE "CompanyName", VER_COMPANYNAME_STR
VALUE "FileDescription", VER_FILEDESCRIPTION_STR
VALUE "FileVersion", VER_FILEVERSION_STR
VALUE "InternalName", VER_INTERNALNAME_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
#else
#include
#include "common.ver"
#endif
================================================
FILE: KSystemInformer/ringbuff.c
================================================
/*
* Copyright (c) 2022 Winsider Seminars & Solutions, Inc. All rights reserved.
*
* This file is part of System Informer.
*
* Authors:
*
* jxy-s 2025-2026
*
*/
#include
#include
KPH_PROTECTED_DATA_SECTION_PUSH();
static PKPH_OBJECT_TYPE KphpRingBufferType = NULL;
KPH_PROTECTED_DATA_SECTION_POP();
KPH_PROTECTED_DATA_SECTION_RO_PUSH();
static const UNICODE_STRING KphpRingBufferTypeName = RTL_CONSTANT_STRING(L"KphRingBuffer");
KPH_PROTECTED_DATA_SECTION_RO_POP();
/**
* \brief Reserves a ring buffer entry.
*
* \details This routine reserves space in the ring buffer to be written to. On
* a successful call to this routine the returned buffer is marked busy and the
* caller must commit the buffer with KphCommitRingBuffer or discard it with
* KphDiscardRingBuffer.
*
* \param[in] Ring Pointer to a buffer object.
* \param[in] Length The length of the buffer to reserve.
*
* \return Reserved buffer, null on failure.
*/
_Return_allocatesMem_size_(Length)
PVOID KphReserveRingBuffer(
_In_ PKPH_RING_BUFFER Ring,
_In_ ULONG Length
)
{
PVOID buffer;
KIRQL previousIrql;
KLOCK_QUEUE_HANDLE lockHandle;
ULONG consumerPos;
ULONG producerPos;
ULONG remainingLength;
ULONG requiredLength;
ULONG alignment;
PKPH_RING_HEADER headerPointer;
KPH_RING_HEADER header;
//
// Maximum reserve accounts for the maximum encoded length bits, additional
// headers, and any necessary alignment.
//
if (Length > KPH_RING_BUFFER_RESERVE_MAX)
{
return NULL;
}
buffer = NULL;
previousIrql = KeGetCurrentIrql();
if (previousIrql >= DISPATCH_LEVEL)
{
KeAcquireInStackQueuedSpinLockAtDpcLevel(&Ring->ProducerLock,
&lockHandle);
}
else
{
KeAcquireInStackQueuedSpinLock(&Ring->ProducerLock, &lockHandle);
}
consumerPos = ReadULongAcquire(Ring->ConsumerPos);
producerPos = ReadULongNoFence(Ring->ProducerPos);
if (producerPos >= consumerPos)
{
remainingLength = (Ring->Length - producerPos);
}
else
{
//
// Producer position wrapped.
//
remainingLength = (consumerPos - producerPos);
}
if (!NT_VERIFY(remainingLength <= Ring->Length))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
GENERAL,
"Ring buffer position overflow: %lu %lu %lu",
producerPos,
consumerPos,
Ring->Length);
goto Exit;
}
requiredLength = (Length + KPH_RING_BUFFER_HEADER_SIZE);
requiredLength = ALIGN_UP_BY(requiredLength, MEMORY_ALLOCATION_ALIGNMENT);
alignment = (requiredLength - Length - KPH_RING_BUFFER_HEADER_SIZE);
//
// Always reserve an extra header for the reset marker.
//
requiredLength += KPH_RING_BUFFER_HEADER_SIZE;
if (requiredLength > remainingLength)
{
if ((producerPos < consumerPos) || (requiredLength > consumerPos))
{
//
// Ring buffer exhausted.
//
goto Exit;
}
//
// There is sufficient space to wrap, do so now.
//
if (!NT_VERIFY(remainingLength >= KPH_RING_BUFFER_HEADER_SIZE))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
GENERAL,
"Ring buffer accounting failure: %lu %lu %lu",
producerPos,
consumerPos,
Ring->Length);
goto Exit;
}
headerPointer = Add2Ptr(Ring->Buffer, producerPos);
header.Value = 0;
header.Reset = TRUE;
WriteULong64Release(&headerPointer->Value, header.Value);
producerPos = 0;
}
headerPointer = Add2Ptr(Ring->Buffer, producerPos);
buffer = Add2Ptr(headerPointer, KPH_RING_BUFFER_HEADER_SIZE);
header.Value = 0;
header.Length = Length;
header.Busy = TRUE;
header.Alignment = alignment;
producerPos += (requiredLength - KPH_RING_BUFFER_HEADER_SIZE);
//
// Sync with the consumer.
//
WriteULong64Release(&headerPointer->Value, header.Value);
WriteULongRelease(Ring->ProducerPos, producerPos);
Exit:
if (previousIrql >= DISPATCH_LEVEL)
{
KeReleaseInStackQueuedSpinLockFromDpcLevel(&lockHandle);
}
else
{
KeReleaseInStackQueuedSpinLock(&lockHandle);
}
return buffer;
}
/**
* \brief Helper routine to submits a previously reserved ring buffer entry.
*
* \param[in] Ring Pointer to a ring buffer object.
* \param[in] Buffer Pointer to the buffer to submit.
* \param[in] Discard If TRUE the buffer is discarded, if FALSE is it committed.
*/
VOID KphpSubmitRingBuffer(
_In_ PKPH_RING_BUFFER Ring,
_In_aliasesMem_ PVOID Buffer,
_In_ BOOLEAN Discard
)
{
PKPH_RING_HEADER headerPointer;
KPH_RING_HEADER header;
headerPointer = Add2Ptr(Buffer, -KPH_RING_BUFFER_HEADER_SIZE);
header.Value = ReadULong64NoFence(&headerPointer->Value);
header.Busy = FALSE;
header.Discard = !!Discard;
WriteULong64Release(&headerPointer->Value, header.Value);
if (Ring->Event && !ReadULongAcquire(Ring->ConsumerProcessing))
{
KeSetEvent(Ring->Event, EVENT_INCREMENT, FALSE);
}
}
/**
* \brief Commits a previously reserved ring buffer entry.
*
* \details After this call the busy ring buffer entry is made available for
* for the consumer to process.
*
* \param[in] Ring Pointer to a ring buffer object.
* \param[in] Buffer Pointer to the buffer to commit, previously returned from
* KphReserveRingBuffer.
*/
VOID KphCommitRingBuffer(
_In_ PKPH_RING_BUFFER Ring,
_In_freesMem_ PVOID Buffer
)
{
KphpSubmitRingBuffer(Ring, Buffer, FALSE);
}
/**
* \brief Discards a previously reserved ring buffer entry.
*
* \details After this call the busy ring buffer entry is marked as discarded
* the consumer should skip over it when processing the ring buffer.
*
* \param[in] Ring Pointer to a ring buffer object.
* \param[in] Buffer Pointer to the buffer to discard, previously returned from
* KphReserveRingBuffer.
*/
VOID KphDiscardRingBuffer(
_In_ PKPH_RING_BUFFER Ring,
_In_freesMem_ PVOID Buffer
)
{
KphpSubmitRingBuffer(Ring, Buffer, TRUE);
}
KPH_PAGED_FILE();
/**
* \brief Creates a ring buffer section.
*
* \details The section is created in a way that restricts read or write access
* to the ring buffer section by user mode. Kernel mode is always permitted to
* read or write to the section. The kernel mapping is locked in the system
* virtual address space. This routine does not map the section into user mode.
* It does provide the section object to be mapped to user mode by the caller.
* The method for creating the sections ensures appropriate memory protection is
* applied to the related section objects and that an eventual mapping into
* user mode can be paged out if necessary.
*
* \param[in] Section Pointer to a ring buffer section to populate.
* \param[in] Length The length of the ring buffer section.
* \param[in] PageProtection The page protection for the ring buffer section.
*
* \return Successful or errant status.
*/
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KphpCreateRingBufferSection(
_Out_ PKPH_RING_SECTION Section,
_In_ ULONG Length,
_In_ ULONG PageProtection
)
{
NTSTATUS status;
ULONG sectionAccess;
LARGE_INTEGER maximumSize;
SIZE_T viewSize;
PVOID kernelMappedBase;
KPH_PAGED_CODE_PASSIVE();
kernelMappedBase = NULL;
if (PageProtection == PAGE_READONLY)
{
sectionAccess = SECTION_MAP_READ;
}
else if (PageProtection == PAGE_READWRITE)
{
sectionAccess = SECTION_MAP_READ | SECTION_MAP_WRITE;
}
else
{
status = STATUS_INVALID_PARAMETER;
goto Exit;
}
maximumSize.QuadPart = Length;
status = MmCreateSection(&Section->SectionObject,
sectionAccess,
NULL,
&maximumSize,
PageProtection,
SEC_COMMIT,
NULL,
NULL);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
GENERAL,
"MmCreateSection failed: %!STATUS!",
status);
Section->SectionObject = NULL;
goto Exit;
}
kernelMappedBase = NULL;
viewSize = 0;
status = MmMapViewInSystemSpace(Section->SectionObject,
&kernelMappedBase,
&viewSize);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
GENERAL,
"MmMapViewInSystemSpace failed: %!STATUS!",
status);
kernelMappedBase = NULL;
goto Exit;
}
NT_ASSERT((viewSize >= Length) && (viewSize <= ULONG_MAX));
Section->Mdl = IoAllocateMdl(kernelMappedBase,
(ULONG)viewSize,
FALSE,
FALSE,
NULL);
if (!Section->Mdl)
{
KphTracePrint(TRACE_LEVEL_VERBOSE, GENERAL, "IoAllocateMdl failed");
status = STATUS_INSUFFICIENT_RESOURCES;
goto Exit;
}
__try
{
MmProbeAndLockPages(Section->Mdl, KernelMode, IoReadAccess);
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
IoFreeMdl(Section->Mdl);
Section->Mdl = NULL;
status = GetExceptionCode();
KphTracePrint(TRACE_LEVEL_VERBOSE,
GENERAL,
"MmProbeAndLockPages failed: %!STATUS!",
status);
goto Exit;
}
Section->KernelBase = KphGetSystemAddressForMdl(Section->Mdl,
NormalPagePriority);
if (!Section->KernelBase)
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
GENERAL,
"MmGetSystemAddressForMdlSafe failed");
status = STATUS_INSUFFICIENT_RESOURCES;
goto Exit;
}
RtlZeroMemory(Section->KernelBase, viewSize);
status = STATUS_SUCCESS;
Exit:
if (kernelMappedBase)
{
MmUnmapViewInSystemSpace(kernelMappedBase);
}
return status;
}
/**
* \brief Deletes a ring buffer section.
*
* \param[in] Section Pointer to a ring buffer section to delete.
*/
_IRQL_requires_max_(PASSIVE_LEVEL)
VOID KphpDeleteRingBufferSection(
_In_ PKPH_RING_SECTION Section
)
{
KPH_PAGED_CODE_PASSIVE();
if (Section->Mdl)
{
MmUnlockPages(Section->Mdl);
IoFreeMdl(Section->Mdl);
}
if (Section->SectionObject)
{
ObDereferenceObject(Section->SectionObject);
}
}
/**
* \brief Allocates a ring buffer object.
*
* \param[in] Size The size of the ring buffer object to allocate.
*
* \return Allocated ring buffer object, null on failure.
*/
_Function_class_(KPH_TYPE_ALLOCATE_PROCEDURE)
_IRQL_requires_max_(PASSIVE_LEVEL)
_Return_allocatesMem_size_(Size)
PVOID KSIAPI KphpAllocateRingBuffer(
_In_ SIZE_T Size
)
{
KPH_PAGED_CODE_PASSIVE();
return KphAllocateNPaged(Size, KPH_TAG_RING_BUFFER);
}
/**
* \brief Deletes a ring buffer object.
*
* \param[in,out] Object Pointer to a ring buffer object to delete.
*/
_Function_class_(KPH_TYPE_DELETE_PROCEDURE)
_IRQL_requires_max_(PASSIVE_LEVEL)
VOID KSIAPI KphpDeleteRingBuffer(
_Inout_ PVOID Object
)
{
PKPH_RING_BUFFER ring;
KPH_PAGED_CODE_PASSIVE();
ring = Object;
KphpDeleteRingBufferSection(&ring->ProducerSection);
KphpDeleteRingBufferSection(&ring->ConsumerSection);
if (ring->Event)
{
ObDereferenceObject(ring->Event);
}
ObDereferenceObject(ring->Process);
}
/**
* \brief Frees a ring buffer object.
*
* \param[in] Object Pointer to a ring buffer object to free.
*/
_Function_class_(KPH_TYPE_FREE_PROCEDURE)
_IRQL_requires_max_(PASSIVE_LEVEL)
VOID KSIAPI KphpFreeRingBuffer(
_In_freesMem_ PVOID Object
)
{
KPH_PAGED_CODE_PASSIVE();
KphFree(Object, KPH_TAG_RING_BUFFER);
}
/**
* \brief Creates a ring buffer object.
*
* \details This ring buffer is a multiple-producer single-consumer ring buffer.
* The producer (kernel mode) populates to the ring buffer and single consumer
* (in user mode) processes the data written to the buffer. The producer may
* reserve, write, and commit to the ring buffer from an arbitrary thread.
*
* \param[out] Ring Receives the ring buffer object.
* \param[out] User Receives the user mode ring buffer information.
* \param[in] Length Desired length of the ring buffer.
* \param[in] Event Optional event object to an event to signal when new data is
* committed and the consumer was previously caught up with processing. Must be
* an ExEventObjectType as a reference is taken to the object.
* \param[in] AccessMode The mode in which to perform access checks.
*
* \return Successful or errant status.
*/
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KphCreateRingBuffer(
_Out_ PKPH_RING_BUFFER* Ring,
_Out_ PKPH_RING_BUFFER_USER User,
_In_ ULONG Length,
_In_opt_ PKEVENT Event,
_In_ KPROCESSOR_MODE AccessMode
)
{
NTSTATUS status;
PKPH_RING_BUFFER ring;
PVOID userProducerBase;
PVOID userConsumerBase;
ULONG sectionLength;
ULONG roundedLength;
ULONG bufferLength;
PKPH_RING_PRODUCER_BLOCK producer;
PKPH_RING_CONSUMER_BLOCK consumer;
LARGE_INTEGER sectionOffset;
SIZE_T viewSize;
KPH_PAGED_CODE_PASSIVE();
*Ring = NULL;
ring = NULL;
userProducerBase = NULL;
userConsumerBase = NULL;
if (Event && (ObGetObjectType(Event) != *ExEventObjectType))
{
status = STATUS_INVALID_PARAMETER;
goto Exit;
}
status = KphZeroModeMemory(User, sizeof(KPH_RING_BUFFER_USER), AccessMode);
if (!NT_SUCCESS(status))
{
goto Exit;
}
status = RtlULongAdd(Length,
FIELD_OFFSET(KPH_RING_PRODUCER_BLOCK, Buffer),
§ionLength);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
GENERAL,
"RtlULongAdd failed: %!STATUS!",
status);
goto Exit;
}
roundedLength = ROUND_TO_PAGES(sectionLength);
if (roundedLength < sectionLength)
{
status = STATUS_INTEGER_OVERFLOW;
goto Exit;
}
sectionLength = roundedLength;
bufferLength = sectionLength;
bufferLength -= FIELD_OFFSET(KPH_RING_PRODUCER_BLOCK, Buffer);
status = KphCreateObject(KphpRingBufferType,
sizeof(KPH_RING_BUFFER),
&ring,
NULL);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
GENERAL,
"KphCreateObject failed: %!STATUS!",
status);
goto Exit;
}
ring->Process = PsGetCurrentProcess();
ObReferenceObject(ring->Process);
KeInitializeSpinLock(&ring->ProducerLock);
if (Event)
{
ring->Event = Event;
ObReferenceObject(ring->Event);
}
status = KphpCreateRingBufferSection(&ring->ProducerSection,
sectionLength,
PAGE_READONLY);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
GENERAL,
"KphpCreateRingBufferSection failed: %!STATUS!",
status);
goto Exit;
}
status = KphpCreateRingBufferSection(&ring->ConsumerSection,
sizeof(KPH_RING_CONSUMER_BLOCK),
PAGE_READWRITE);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
GENERAL,
"KphpCreateRingBufferSection failed: %!STATUS!",
status);
goto Exit;
}
producer = ring->ProducerSection.KernelBase;
consumer = ring->ConsumerSection.KernelBase;
producer->Length = bufferLength;
ring->ProducerPos = &producer->Position;
ring->ConsumerPos = &consumer->Position;
ring->ConsumerProcessing = &consumer->Processing;
ring->Length = bufferLength;
ring->Buffer = producer->Buffer;
//
// Set up the user mode portion of the ring buffer.
//
sectionOffset.QuadPart = 0;
viewSize = 0;
status = MmMapViewOfSection(ring->ProducerSection.SectionObject,
PsGetCurrentProcess(),
&userProducerBase,
0,
0,
§ionOffset,
&viewSize,
ViewUnmap,
0,
PAGE_READONLY);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
GENERAL,
"MmMapViewOfSection failed: %!STATUS!",
status);
userProducerBase = NULL;
goto Exit;
}
sectionOffset.QuadPart = 0;
viewSize = 0;
status = MmMapViewOfSection(ring->ConsumerSection.SectionObject,
PsGetCurrentProcess(),
&userConsumerBase,
0,
0,
§ionOffset,
&viewSize,
ViewUnmap,
0,
PAGE_READWRITE);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
GENERAL,
"MmMapViewOfSection failed: %!STATUS!",
status);
userConsumerBase = NULL;
goto Exit;
}
status = KphWritePointerToMode(&User->Consumer,
userConsumerBase,
AccessMode);
if (!NT_SUCCESS(status))
{
goto Exit;
}
status = KphWritePointerToMode(&User->Producer,
userProducerBase,
AccessMode);
if (!NT_SUCCESS(status))
{
goto Exit;
}
userProducerBase = NULL;
userConsumerBase = NULL;
*Ring = ring;
ring = NULL;
status = STATUS_SUCCESS;
Exit:
if (userConsumerBase)
{
MmUnmapViewOfSection(PsGetCurrentProcess(), userConsumerBase);
}
if (userProducerBase)
{
MmUnmapViewOfSection(PsGetCurrentProcess(), userProducerBase);
}
if (ring)
{
KphDereferenceObject(ring);
}
return status;
}
/**
* \brief Initializes the ring buffer infrastructure.
*/
_IRQL_requires_max_(PASSIVE_LEVEL)
VOID KphInitializeRingBuffer(
VOID
)
{
KPH_OBJECT_TYPE_INFO typeInfo;
KPH_PAGED_CODE_PASSIVE();
typeInfo.Allocate = KphpAllocateRingBuffer;
typeInfo.Initialize = NULL;
typeInfo.Delete = KphpDeleteRingBuffer;
typeInfo.Free = KphpFreeRingBuffer;
typeInfo.Flags = 0;
KphCreateObjectType(&KphpRingBufferTypeName,
&typeInfo,
&KphpRingBufferType);
}
================================================
FILE: KSystemInformer/session_token.c
================================================
/*
* Copyright (c) 2022 Winsider Seminars & Solutions, Inc. All rights reserved.
*
* This file is part of System Informer.
*
* Authors:
*
* jxy-s 2023-2026
*
*/
#include
#include
typedef struct _KPH_SESSION_TOKEN_INIT
{
LARGE_INTEGER Expiry;
ULONG Privileges;
LONG Uses;
} KPH_SESSION_TOKEN_INIT, *PKPH_SESSION_TOKEN_INIT;
KPH_PROTECTED_DATA_SECTION_RO_PUSH();
static const UNICODE_STRING KphpSessionTokenTypeName = RTL_CONSTANT_STRING(L"KphSessionToken");
KPH_PROTECTED_DATA_SECTION_RO_POP();
KPH_PROTECTED_DATA_SECTION_PUSH();
static PKPH_OBJECT_TYPE KphpSessionTokenType = NULL;
KPH_PROTECTED_DATA_SECTION_POP();
KPH_PAGED_FILE();
/**
* \brief Allocate a session token object.
*
* \param[in] Size The size of the object to allocate.
*
* \return A pointer to the allocated object, NULL on failure.
*/
_Function_class_(KPH_TYPE_ALLOCATE_PROCEDURE)
_IRQL_requires_max_(APC_LEVEL)
_Return_allocatesMem_size_(Size)
PVOID KSIAPI KphpAllocateSessionToken(
_In_ SIZE_T Size
)
{
KPH_PAGED_CODE();
//
// N.B. The session token object is allocated from non-paged pool because it
// is stored in an atomic object reference in process and thread contexts.
//
return KphAllocateNPaged(Size, KPH_TAG_SESSION_TOKEN_OBJECT);
}
/**
* \brief Free a session token object.
*
* \param[in] Object The session token object to free.
*/
_Function_class_(KPH_TYPE_FREE_PROCEDURE)
_IRQL_requires_max_(APC_LEVEL)
VOID KSIAPI KphpFreeSessionToken(
_In_freesMem_ PVOID Object
)
{
KPH_PAGED_CODE();
KphFree(Object, KPH_TAG_SESSION_TOKEN_OBJECT);
}
/**
* \brief Initialize a session token object.
*
* \param[in] Object The session token object to initialize.
* \param[in] Parameter The session token object initialization parameters.
*
* \return Successful or errant status.
*/
_Function_class_(KPH_TYPE_INITIALIZE_PROCEDURE)
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KSIAPI KphpInitializeSessionToken(
_Inout_ PVOID Object,
_In_opt_ PVOID Parameter
)
{
NTSTATUS status;
PKPH_SESSION_TOKEN token;
PKPH_SESSION_TOKEN_INIT init;
KPH_PAGED_CODE_PASSIVE();
NT_ASSERT(Parameter);
token = Object;
init = Parameter;
status = ExUuidCreate(&token->AccessToken.Identifier);
if (status != STATUS_SUCCESS)
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
GENERAL,
"ExUuidCreate failed: %!STATUS!",
status);
if (NT_SUCCESS(status))
{
status = STATUS_UNSUCCESSFUL;
}
return status;
}
status = BCryptGenRandom(NULL,
token->AccessToken.Material,
sizeof(token->AccessToken.Material),
BCRYPT_USE_SYSTEM_PREFERRED_RNG);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
GENERAL,
"BCryptGenRandom failed: %!STATUS!",
status);
return status;
}
if (init->Expiry.QuadPart < 0)
{
KeQuerySystemTime(&token->AccessToken.Expiry);
token->AccessToken.Expiry.QuadPart += -(init->Expiry.QuadPart);
}
else
{
token->AccessToken.Expiry = init->Expiry;
}
token->AccessToken.Privileges = init->Privileges;
token->AccessToken.Uses = init->Uses;
WriteNoFence(&token->UseCount, 0);
return STATUS_SUCCESS;
}
/**
* \brief Requests an access session token.
*
* \details After a request for an access session token has been made, the
* current thread is responsible for assigning the session token to a process
* or thread later by providing the signature for the access session token
* content.
*
* \param[out] AccessToken Populated with the access session token.
* \param[in] Expiry The expiry time of the session token.
* \param[in] Privileges The privileges of the session token.
* \param[in] Uses The number of uses of the session token.
* \param[in] AccessMode The mode in which to perform access checks.
*
* \return Successful or errant status.
*/
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KphRequestSessionAccessToken(
_Out_ PKPH_SESSION_ACCESS_TOKEN AccessToken,
_In_ PLARGE_INTEGER Expiry,
_In_ ULONG Privileges,
_In_ LONG Uses
)
{
NTSTATUS status;
PKPH_THREAD_CONTEXT thread;
KPH_SESSION_TOKEN_INIT tokenInit;
PKPH_SESSION_TOKEN token;
KPH_PAGED_CODE_PASSIVE();
RtlZeroMemory(AccessToken, sizeof(KPH_SESSION_ACCESS_TOKEN));
thread = NULL;
token = NULL;
if (!Privileges || (Privileges & ~KPH_TOKEN_VALID_PRIVILEGES))
{
status = STATUS_INVALID_PARAMETER;
goto Exit;
}
thread = KphGetCurrentThreadContext();
if (!thread)
{
status = STATUS_INSUFFICIENT_RESOURCES;
goto Exit;
}
tokenInit.Expiry = *Expiry;
tokenInit.Privileges = Privileges;
tokenInit.Uses = Uses;
status = KphCreateObject(KphpSessionTokenType,
sizeof(KPH_SESSION_TOKEN),
&token,
&tokenInit);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
GENERAL,
"KphCreateObject failed: %!STATUS!",
status);
goto Exit;
}
RtlCopyMemory(AccessToken,
&token->AccessToken,
sizeof(KPH_SESSION_ACCESS_TOKEN));
KphAtomicAssignObjectReference(&thread->RequestSessionToken.Atomic, token);
KphTracePrint(TRACE_LEVEL_VERBOSE,
GENERAL,
"[%!GUID!] %!TIME! %ld",
&token->AccessToken.Identifier,
token->AccessToken.Expiry.QuadPart,
token->AccessToken.Uses);
Exit:
if (token)
{
KphDereferenceObject(token);
}
if (thread)
{
KphDereferenceObject(thread);
}
return status;
}
/**
* \brief Verifies an access session token.
*
* \param[in] Actor The actor process setting the session token.
* \param[in] Target The target process receiving the session token.
* \param[in] Token The session token to verify.
* \param[in] Signature The signature of the session token.
* \param[in] SignatureLength The length of the signature.
* \param[in] AccessMode The mode in which to perform access checks.
*
* \return Successful or errant status.
*/
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KphpVerifySessionToken(
_In_ PKPH_PROCESS_CONTEXT Actor,
_In_ PKPH_PROCESS_CONTEXT Target,
_In_ PKPH_SESSION_TOKEN Token,
_In_ PBYTE Signature,
_In_ ULONG SignatureLength,
_In_ KPROCESSOR_MODE AccessMode
)
{
NTSTATUS status;
PKPH_DYN dyn;
PBYTE signature;
KPH_PAGED_CODE_PASSIVE();
signature = NULL;
dyn = NULL;
dyn = KphReferenceDynData();
if (!dyn || !dyn->SessionTokenPublicKeyHandle)
{
status = STATUS_NOINTERFACE;
goto Exit;
}
if (AccessMode != KernelMode)
{
signature = KphAllocatePaged(SignatureLength,
KPH_TAG_SESSION_TOKEN_SIGNATURE);
if (!signature)
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
GENERAL,
"Failed to allocate signature buffer.");
status = STATUS_INSUFFICIENT_RESOURCES;
goto Exit;
}
__try
{
CopyFromUser(signature, Signature, SignatureLength);
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
status = GetExceptionCode();
goto Exit;
}
}
else
{
signature = Signature;
}
status = KphVerifyBufferEx(dyn->SessionTokenPublicKeyHandle,
(PBYTE)&Token->AccessToken,
sizeof(Token->AccessToken),
signature,
SignatureLength);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
GENERAL,
"KphVerifyBufferEx failed: %!STATUS!",
status);
goto Exit;
}
if (!KphTestProcessContextState(Actor, KPH_PROCESS_STATE_MAXIMUM) ||
!KphTestProcessContextState(Target, KPH_PROCESS_STATE_MAXIMUM))
{
status = STATUS_ACCESS_DENIED;
goto Exit;
}
KphTracePrint(TRACE_LEVEL_VERBOSE,
GENERAL,
"[%!GUID!] %!TIME! %ld",
&Token->AccessToken.Identifier,
Token->AccessToken.Expiry.QuadPart,
Token->AccessToken.Uses);
status = STATUS_SUCCESS;
Exit:
if (dyn)
{
KphDereferenceObject(dyn);
}
if (signature && (signature != Signature))
{
KphFree(signature, KPH_TAG_SESSION_TOKEN_SIGNATURE);
}
return status;
}
/**
* \brief Assigns an access session token to a process.
*
* \param[in] Process The process to assign the session token to.
* \param[in] Signature The signature of the session token.
* \param[in] SignatureLength The length of the signature.
* \param[in] AccessMode The mode in which to perform access checks.
*
* \return Successful or errant status.
*/
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KphpAssignProcessSessionToken(
_Inout_ PKPH_PROCESS_CONTEXT Process,
_In_ PBYTE Signature,
_In_ ULONG SignatureLength,
_In_ KPROCESSOR_MODE AccessMode
)
{
NTSTATUS status;
PKPH_THREAD_CONTEXT actor;
PKPH_SESSION_TOKEN token;
KPH_PAGED_CODE_PASSIVE();
token = NULL;
actor = KphGetCurrentThreadContext();
if (!actor)
{
status = STATUS_INSUFFICIENT_RESOURCES;
goto Exit;
}
token = KphAtomicMoveObjectReference(&actor->RequestSessionToken.Atomic,
NULL);
if (!token)
{
status = STATUS_INVALID_STATE_TRANSITION;
goto Exit;
}
if (!actor->ProcessContext)
{
status = STATUS_INSUFFICIENT_RESOURCES;
goto Exit;
}
status = KphpVerifySessionToken(actor->ProcessContext,
Process,
token,
Signature,
SignatureLength,
AccessMode);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
GENERAL,
"KphpVerifyToken failed: %!STATUS!",
status);
goto Exit;
}
KphAtomicAssignObjectReference(&Process->SessionToken.Atomic, token);
status = STATUS_SUCCESS;
Exit:
if (token)
{
KphDereferenceObject(token);
}
if (actor)
{
KphDereferenceObject(actor);
}
return status;
}
/**
* \brief Assigns an access session token to a process.
*
* \param[in] ProcessHandle Handle to the process to assign the session token.
* \param[in] Signature The signature of the session token.
* \param[in] SignatureLength The length of the signature.
* \param[in] AccessMode The mode in which to perform access checks.
*
* \return Successful or errant status.
*/
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KphAssignProcessSessionToken(
_In_ HANDLE ProcessHandle,
_In_ PBYTE Signature,
_In_ ULONG SignatureLength,
_In_ KPROCESSOR_MODE AccessMode
)
{
NTSTATUS status;
PEPROCESS processObject;
PKPH_PROCESS_CONTEXT processContext;
KPH_PAGED_CODE_PASSIVE();
processObject = NULL;
processContext = NULL;
if (!Signature || !SignatureLength)
{
status = STATUS_INVALID_PARAMETER;
goto Exit;
}
status = ObReferenceObjectByHandle(ProcessHandle,
0,
*PsProcessType,
AccessMode,
&processObject,
NULL);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
GENERAL,
"ObReferenceObjectByHandle failed: %!STATUS!",
status);
processObject = NULL;
goto Exit;
}
processContext = KphGetEProcessContext(processObject);
if (!processContext)
{
status = STATUS_INSUFFICIENT_RESOURCES;
goto Exit;
}
status = KphpAssignProcessSessionToken(processContext,
Signature,
SignatureLength,
AccessMode);
Exit:
if (processContext)
{
KphDereferenceObject(processContext);
}
if (processObject)
{
ObDereferenceObject(processObject);
}
return status;
}
/**
* \brief Assigns an access session token to a thread.
*
* \param[in] Thread The thread to assign the session token to.
* \param[in] Signature The signature of the session token.
* \param[in] SignatureLength The length of the signature.
* \param[in] AccessMode The mode in which to perform access checks.
*
* \return Successful or errant status.
*/
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KphpAssignThreadSessionToken(
_Inout_ PKPH_THREAD_CONTEXT Thread,
_In_ PBYTE Signature,
_In_ ULONG SignatureLength,
_In_ KPROCESSOR_MODE AccessMode
)
{
NTSTATUS status;
PKPH_THREAD_CONTEXT actor;
PKPH_SESSION_TOKEN token;
KPH_PAGED_CODE_PASSIVE();
actor = NULL;
token = NULL;
actor = KphGetCurrentThreadContext();
if (!actor)
{
status = STATUS_INSUFFICIENT_RESOURCES;
goto Exit;
}
token = KphAtomicMoveObjectReference(&actor->RequestSessionToken.Atomic,
NULL);
if (!token)
{
status = STATUS_INVALID_STATE_TRANSITION;
goto Exit;
}
if (!actor->ProcessContext || !Thread->ProcessContext)
{
status = STATUS_INSUFFICIENT_RESOURCES;
goto Exit;
}
status = KphpVerifySessionToken(actor->ProcessContext,
Thread->ProcessContext,
token,
Signature,
SignatureLength,
AccessMode);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
GENERAL,
"KphpVerifyToken failed: %!STATUS!",
status);
goto Exit;
}
KphAtomicAssignObjectReference(&Thread->SessionToken.Atomic,
token);
status = STATUS_SUCCESS;
Exit:
if (token)
{
KphDereferenceObject(token);
}
if (actor)
{
KphDereferenceObject(actor);
}
return status;
}
/**
* \brief Assigns an access session token to a thread.
*
* \param[in] ThreadHandle Handle to the thread to assign the session token.
* \param[in] Signature The signature of the session token.
* \param[in] SignatureLength The length of the signature.
* \param[in] AccessMode The mode in which to perform access checks.
*
* \return Successful or errant status.
*/
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KphAssignThreadSessionToken(
_In_ HANDLE ThreadHandle,
_In_ PBYTE Signature,
_In_ ULONG SignatureLength,
_In_ KPROCESSOR_MODE AccessMode
)
{
NTSTATUS status;
PETHREAD threadObject;
PKPH_THREAD_CONTEXT threadContext;
KPH_PAGED_CODE_PASSIVE();
threadObject = NULL;
threadContext = NULL;
if (!Signature || !SignatureLength)
{
status = STATUS_INVALID_PARAMETER;
goto Exit;
}
status = ObReferenceObjectByHandle(ThreadHandle,
0,
*PsThreadType,
AccessMode,
&threadObject,
NULL);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
GENERAL,
"ObReferenceObjectByHandle failed: %!STATUS!",
status);
threadObject = NULL;
goto Exit;
}
threadContext = KphGetEThreadContext(threadObject);
if (!threadContext)
{
status = STATUS_INSUFFICIENT_RESOURCES;
goto Exit;
}
status = KphpAssignThreadSessionToken(threadContext,
Signature,
SignatureLength,
AccessMode);
Exit:
if (threadContext)
{
KphDereferenceObject(threadContext);
}
if (threadObject)
{
ObDereferenceObject(threadObject);
}
return status;
}
/**
* \brief Performs a session token privilege check.
*
* \param[in,out] Token The session token to perform the check on.
* \param[in] Privileges The privileges to check.
*
* \return TRUE if the privilege is granted, FALSE otherwise.
*/
_IRQL_requires_max_(APC_LEVEL)
_Must_inspect_result_
BOOLEAN KphpSessionTokenPrivilegeCheck(
_Inout_ PKPH_SESSION_TOKEN Token,
_In_ ULONG Privileges
)
{
NTSTATUS status;
LARGE_INTEGER systemTime;
LONG useCount;
KPH_PAGED_CODE();
KeQuerySystemTime(&systemTime);
useCount = ReadAcquire(&Token->UseCount);
if (Token->AccessToken.Expiry.QuadPart <= systemTime.QuadPart)
{
status = STATUS_TIMEOUT;
goto Exit;
}
if ((Token->AccessToken.Privileges & Privileges) != Privileges)
{
status = STATUS_PRIVILEGE_NOT_HELD;
goto Exit;
}
if (Token->AccessToken.Uses < 0)
{
status = STATUS_SUCCESS;
goto Exit;
}
for (;;)
{
LONG expected;
if (useCount >= Token->AccessToken.Uses)
{
status = STATUS_QUOTA_EXCEEDED;
goto Exit;
}
expected = useCount;
useCount = InterlockedCompareExchange(&Token->UseCount,
useCount + 1,
expected);
if (useCount == expected)
{
useCount++;
status = STATUS_SUCCESS;
break;
}
}
Exit:
KphTracePrint(TRACE_LEVEL_VERBOSE,
GENERAL,
"[%!GUID!] %!TIME! %ld/%ld %!STATUS!",
&Token->AccessToken.Identifier,
Token->AccessToken.Expiry.QuadPart,
useCount,
Token->AccessToken.Uses,
status);
return (status == STATUS_SUCCESS);
}
/**
* \brief Performs a session token privilege check on a thread.
*
* \param[in] Thread The thread to perform the session token check on.
* \param[in] Privileges The privileges to check.
*
* \return TRUE if the privilege is granted, FALSE otherwise.
*/
_IRQL_requires_max_(APC_LEVEL)
_Must_inspect_result_
BOOLEAN KphpThreadSessionTokenPrivilegeCheck(
_In_ PKPH_THREAD_CONTEXT Thread,
_In_ ULONG Privileges
)
{
BOOLEAN result;
PKPH_SESSION_TOKEN token;
KPH_PAGED_CODE();
result = FALSE;
token = KphAtomicReferenceObject(&Thread->SessionToken.Atomic);
if (token)
{
result = KphpSessionTokenPrivilegeCheck(token, Privileges);
KphDereferenceObject(token);
}
return result;
}
/**
* \brief Performs a session token privilege check on a process.
*
* \param[in] Process The process to perform the session token check on.
* \param[in] Privileges The privileges to check.
*
* \return TRUE if the privilege is granted, FALSE otherwise.
*/
_IRQL_requires_max_(APC_LEVEL)
_Must_inspect_result_
BOOLEAN KphpProcessSessionTokenPrivilegeCheck(
_In_ PKPH_PROCESS_CONTEXT Process,
_In_ ULONG Privileges
)
{
BOOLEAN result;
PKPH_SESSION_TOKEN token;
KPH_PAGED_CODE();
result = FALSE;
token = KphAtomicReferenceObject(&Process->SessionToken.Atomic);
if (token)
{
result = KphpSessionTokenPrivilegeCheck(token, Privileges);
KphDereferenceObject(token);
}
return result;
}
/**
* \brief Performs a session token privilege check.
*
* \param[in] Thread The thread to perform the session token check on.
* \param[in] Privileges The privileges to check.
*
* \return TRUE if the privilege is granted, FALSE otherwise.
*/
_IRQL_requires_max_(APC_LEVEL)
_Must_inspect_result_
BOOLEAN KphSessionTokenPrivilegeCheck(
_In_ PKPH_THREAD_CONTEXT Thread,
_In_ ULONG Privileges
)
{
KPH_PAGED_CODE();
if (KphpThreadSessionTokenPrivilegeCheck(Thread, Privileges))
{
return TRUE;
}
if (!Thread->ProcessContext)
{
return FALSE;
}
return KphpProcessSessionTokenPrivilegeCheck(Thread->ProcessContext,
Privileges);
}
/**
* \brief Initialize the session token infrastructure.
*/
_IRQL_requires_max_(PASSIVE_LEVEL)
VOID KphInitializeSessionToken(
VOID
)
{
KPH_OBJECT_TYPE_INFO typeInfo;
KPH_PAGED_CODE_PASSIVE();
typeInfo.Allocate = KphpAllocateSessionToken;
typeInfo.Initialize = KphpInitializeSessionToken;
typeInfo.Delete = NULL;
typeInfo.Free = KphpFreeSessionToken;
typeInfo.Flags = 0;
KphCreateObjectType(&KphpSessionTokenTypeName,
&typeInfo,
&KphpSessionTokenType);
}
================================================
FILE: KSystemInformer/system.c
================================================
/*
* Copyright (c) 2022 Winsider Seminars & Solutions, Inc. All rights reserved.
*
* This file is part of System Informer.
*
* Authors:
*
* jxy-s 2022-2026
*
*/
#include
#include
KPH_PAGED_FILE();
/**
* \brief Performs generic system control actions.
*
* \param[in] SystemControlClass System control classification.
* \param[in] SystemControlInfo Control input buffer.
* \param[in] SystemControlInfoLength Length of control input buffer.
* \param[in] AccessMode The mode in which to perform access checks.
*
* \return Successful or errant status.
*/
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KphSystemControl(
_In_ KPH_SYSTEM_CONTROL_CLASS SystemControlClass,
_In_reads_bytes_(SystemControlInfoLength) PVOID SystemControlInfo,
_In_ ULONG SystemControlInfoLength,
_In_ KPROCESSOR_MODE AccessMode
)
{
NTSTATUS status;
HANDLE processHandle;
KPH_PAGED_CODE_PASSIVE();
UNREFERENCED_PARAMETER(SystemControlInfo);
UNREFERENCED_PARAMETER(SystemControlInfoLength);
UNREFERENCED_PARAMETER(AccessMode);
processHandle = NULL;
switch (SystemControlClass)
{
case KphSystemControlEmptyCompressionStore:
{
SYSTEM_STORE_INFORMATION storeInfo;
SM_STORE_COMPRESSION_INFORMATION_REQUEST compressionInfo;
CLIENT_ID clientId;
OBJECT_ATTRIBUTES objectAttributes;
QUOTA_LIMITS_EX quotaLimits;
#ifdef _WIN64
C_ASSERT(sizeof(SYSTEM_STORE_INFORMATION) == 24);
C_ASSERT(SYSTEM_STORE_COMPRESSION_INFORMATION_VERSION_V1 == 3);
C_ASSERT(SYSTEM_STORE_COMPRESSION_INFORMATION_SIZE_V1 == 40);
#endif
RtlZeroMemory(&compressionInfo, sizeof(SM_STORE_COMPRESSION_INFORMATION_REQUEST));
compressionInfo.Version = SYSTEM_STORE_COMPRESSION_INFORMATION_VERSION_V1;
RtlZeroMemory(&storeInfo, sizeof(SYSTEM_STORE_INFORMATION));
storeInfo.Version = SYSTEM_STORE_INFORMATION_VERSION;
storeInfo.StoreInformationClass = MemCompressionInfoRequest;
storeInfo.Data = &compressionInfo;
storeInfo.Length = SYSTEM_STORE_COMPRESSION_INFORMATION_SIZE_V1;
status = ZwQuerySystemInformation(SystemStoreInformation,
&storeInfo,
sizeof(SYSTEM_STORE_INFORMATION),
NULL);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
GENERAL,
"ZwQuerySystemInformation failed: %!STATUS!",
status);
goto Exit;
}
if (compressionInfo.CompressionPid == 0)
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
GENERAL,
"Compression PID is zero");
status = STATUS_INVALID_CID;
goto Exit;
}
InitializeObjectAttributes(&objectAttributes,
NULL,
OBJ_KERNEL_HANDLE,
NULL,
NULL);
clientId.UniqueProcess = ULongToHandle(compressionInfo.CompressionPid);
clientId.UniqueThread = NULL;
status = ZwOpenProcess(&processHandle,
PROCESS_SET_QUOTA,
&objectAttributes,
&clientId);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
GENERAL,
"ZwOpenProcess failed: %!STATUS!",
status);
processHandle = NULL;
goto Exit;
}
RtlZeroMemory("aLimits, sizeof(QUOTA_LIMITS_EX));
quotaLimits.MinimumWorkingSetSize = SIZE_T_MAX;
quotaLimits.MaximumWorkingSetSize = SIZE_T_MAX;
status = ZwSetInformationProcess(processHandle,
ProcessQuotaLimits,
"aLimits,
sizeof(QUOTA_LIMITS_EX));
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
GENERAL,
"ZwSetInformationProcess failed: %!STATUS!",
status);
}
break;
}
default:
{
status = STATUS_INVALID_INFO_CLASS;
goto Exit;
}
}
Exit:
if (processHandle)
{
ObCloseHandle(processHandle, KernelMode);
}
return status;
}
================================================
FILE: KSystemInformer/thread.c
================================================
/*
* Copyright (c) 2022 Winsider Seminars & Solutions, Inc. All rights reserved.
*
* This file is part of System Informer.
*
* Authors:
*
* wj32 2010-2016
* jxy-s 2022-2026
*
*/
#include
#include
KPH_PAGED_FILE();
/**
* \brief Opens a thread.
*
* \param[out] ThreadHandle A variable which receives the thread handle.
* \param[in] DesiredAccess The desired access to the thread.
* \param[in] ClientId The identifier of a client. UniqueThread must be present.
* If UniqueProcess is present, the process of the referenced thread will be
* checked against this identifier.
* \param[in] AccessMode The mode in which to perform access checks.
*
* \return Successful or errant status.
*/
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KphOpenThread(
_Out_ PHANDLE ThreadHandle,
_In_ ACCESS_MASK DesiredAccess,
_In_ PCLIENT_ID ClientId,
_In_ KPROCESSOR_MODE AccessMode
)
{
NTSTATUS status;
CLIENT_ID clientId;
PETHREAD thread;
HANDLE threadHandle = NULL;
KPH_PAGED_CODE_PASSIVE();
thread = NULL;
status = KphCopyFromMode(&clientId,
ClientId,
sizeof(CLIENT_ID),
AccessMode);
if (!NT_SUCCESS(status))
{
goto Exit;
}
//
// Use the process ID if it was specified.
//
if (clientId.UniqueProcess)
{
status = PsLookupProcessThreadByCid(&clientId, NULL, &thread);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
GENERAL,
"PsLookupProcessThreadByCid failed: %!STATUS!",
status);
thread = NULL;
goto Exit;
}
}
else
{
status = PsLookupThreadByThreadId(clientId.UniqueThread, &thread);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
GENERAL,
"PsLookupThreadByThreadId failed: %!STATUS!",
status);
thread = NULL;
goto Exit;
}
}
if ((DesiredAccess & KPH_THREAD_READ_ACCESS) != DesiredAccess)
{
status = KphDominationCheck(PsGetCurrentProcess(),
PsGetThreadProcess(thread),
AccessMode);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
GENERAL,
"KphDominationCheck failed: %!STATUS!",
status);
goto Exit;
}
}
//
// Always open in KernelMode to skip access checks.
//
status = ObOpenObjectByPointer(thread,
(AccessMode ? 0 : OBJ_KERNEL_HANDLE),
NULL,
DesiredAccess,
*PsThreadType,
KernelMode,
&threadHandle);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
GENERAL,
"ObOpenObjectByPointer failed: %!STATUS!",
status);
threadHandle = NULL;
goto Exit;
}
status = KphWriteHandleToMode(ThreadHandle, threadHandle, AccessMode);
Exit:
if (thread)
{
ObDereferenceObject(thread);
}
return status;
}
/**
* \brief Opens the process of a thread.
*
* \param[in] ThreadHandle A handle to a thread.
* \param[in] DesiredAccess The desired access to the process.
* \param[out] ProcessHandle A variable which receives the process handle.
* \param[in] AccessMode The mode in which to perform access checks.
*
* \return Successful or errant status.
*/
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KphOpenThreadProcess(
_In_ HANDLE ThreadHandle,
_In_ ACCESS_MASK DesiredAccess,
_Out_ PHANDLE ProcessHandle,
_In_ KPROCESSOR_MODE AccessMode
)
{
NTSTATUS status;
PETHREAD thread;
HANDLE processHandle;
KPH_PAGED_CODE_PASSIVE();
status = ObReferenceObjectByHandle(ThreadHandle,
0,
*PsThreadType,
AccessMode,
&thread,
NULL);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
GENERAL,
"ObReferenceObjectByHandle failed: %!STATUS!",
status);
thread = NULL;
goto Exit;
}
if ((DesiredAccess & KPH_PROCESS_READ_ACCESS) != DesiredAccess)
{
status = KphDominationCheck(PsGetCurrentProcess(),
PsGetThreadProcess(thread),
AccessMode);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
GENERAL,
"KphDominationCheck failed: %!STATUS!",
status);
goto Exit;
}
}
//
// Always open in KernelMode to skip access checks.
//
status = ObOpenObjectByPointer(PsGetThreadProcess(thread),
(AccessMode ? 0 : OBJ_KERNEL_HANDLE),
NULL,
DesiredAccess,
*PsProcessType,
KernelMode,
&processHandle);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
GENERAL,
"ObOpenObjectByPointer failed: %!STATUS!",
status);
processHandle = NULL;
goto Exit;
}
status = KphWriteHandleToMode(ProcessHandle, processHandle, AccessMode);
Exit:
if (thread)
{
ObDereferenceObject(thread);
}
return status;
}
/**
* \brief Captures the stack trace of a thread.
*
* \param[in] ThreadHandle A handle to the thread to capture the stack trace of.
* \param[in] FramesToSkip The number of kernel frames to skip.
* \param[in] FramesToCapture The number of frames to capture.
* \param[out] BackTrace Buffer to store the back trace in.
* \param[out] CapturedFrames Receives the number of captured frames.
* \param[out] BackTraceHash Optionally receives a hash of the back trace.
* \param[in] Flags A combination of KPH_STACK_BACK_TRACE_* flags.
* \param[in] Timeout Optionally specifies a timeout for the capture operation.
*
* \return STATUS_SUCCES, STATUS_TIMEOUT, or errant status.
*/
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KphCaptureStackBackTraceThreadByHandle(
_In_ HANDLE ThreadHandle,
_In_ ULONG FramesToSkip,
_In_ ULONG FramesToCapture,
_Out_writes_(FramesToCapture) PVOID* BackTrace,
_Out_ PULONG CapturedFrames,
_Out_opt_ PULONG BackTraceHash,
_In_ ULONG Flags,
_In_opt_ PLARGE_INTEGER Timeout,
_In_ KPROCESSOR_MODE AccessMode
)
{
NTSTATUS status = STATUS_SUCCESS;
PETHREAD thread;
PVOID backTrace;
ULONG capturedFrames;
ULONG backTraceHash;
LARGE_INTEGER timeout;
KPH_PAGED_CODE_PASSIVE();
backTrace = NULL;
thread = NULL;
backTraceHash = 0;
if (!CapturedFrames)
{
status = STATUS_INVALID_PARAMETER;
goto Exit;
}
status = KphWriteULongToMode(CapturedFrames, 0, AccessMode);
if (!NT_SUCCESS(status))
{
goto Exit;
}
if (BackTraceHash)
{
status = KphWriteULongToMode(BackTraceHash, 0, AccessMode);
if (!NT_SUCCESS(status))
{
goto Exit;
}
}
if (Timeout)
{
status = KphReadLargeIntegerFromMode(&timeout, Timeout, AccessMode);
if (!NT_SUCCESS(status))
{
goto Exit;
}
}
if (AccessMode != KernelMode)
{
backTrace = KphAllocatePaged(FramesToCapture * sizeof(PVOID),
KPH_TAG_THREAD_BACK_TRACE);
if (!backTrace)
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
GENERAL,
"Failed to allocate back trace buffer.");
status = STATUS_INSUFFICIENT_RESOURCES;
goto Exit;
}
}
else
{
backTrace = BackTrace;
}
status = ObReferenceObjectByHandle(ThreadHandle,
0,
*PsThreadType,
AccessMode,
&thread,
NULL);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
GENERAL,
"ObReferenceObjectByHandle failed: %!STATUS!",
status);
thread = NULL;
goto Exit;
}
status = KphCaptureStackBackTraceThread(thread,
FramesToSkip,
FramesToCapture,
backTrace,
&capturedFrames,
(BackTraceHash ? &backTraceHash : NULL),
Flags,
(Timeout ? &timeout : NULL));
if (!NT_SUCCESS(status) || (status == STATUS_TIMEOUT))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
GENERAL,
"KphCaptureStackBackTraceThread failed: %!STATUS!",
status);
goto Exit;
}
if (AccessMode != KernelMode)
{
__try
{
CopyToUser(BackTrace, backTrace, capturedFrames * sizeof(PVOID));
WriteULongToUser(CapturedFrames, capturedFrames);
if (BackTraceHash)
{
WriteULongToUser(BackTraceHash, backTraceHash);
}
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
status = GetExceptionCode();
}
}
else
{
*CapturedFrames = capturedFrames;
if (BackTraceHash)
{
*BackTraceHash = backTraceHash;
}
}
Exit:
if (backTrace && (backTrace != BackTrace))
{
KphFree(backTrace, KPH_TAG_THREAD_BACK_TRACE);
}
if (thread)
{
ObDereferenceObject(thread);
}
return status;
}
/**
* \brief Sets information about a thread.
*
* \param[in] ThreadHandle Handle to thread to set information for.
* \param[in] ThreadInformationClass Information class to set.
* \param[in] ThreadInformation Information to set.
* \param[in] ThreadInformationLength Length of the thread information buffer.
* \param[in] AccessMode The mode in which to perform access checks.
*
* \return Successful or errant status.
*/
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KphSetInformationThread(
_In_ HANDLE ThreadHandle,
_In_ KPH_THREAD_INFORMATION_CLASS ThreadInformationClass,
_In_reads_bytes_(ThreadInformationLength) PVOID ThreadInformation,
_In_ ULONG ThreadInformationLength,
_In_ KPROCESSOR_MODE AccessMode
)
{
NTSTATUS status;
PVOID threadInformation;
BYTE stackBuffer[64];
PETHREAD thread;
HANDLE threadHandle;
THREADINFOCLASS threadInformationClass;
KPH_PAGED_CODE_PASSIVE();
threadInformation = NULL;
thread = NULL;
threadHandle = NULL;
if (!ThreadInformation)
{
status = STATUS_INVALID_PARAMETER;
goto Exit;
}
if (AccessMode != KernelMode)
{
threadInformation = KphAllocatePagedA(ThreadInformationLength,
KPH_TAG_THREAD_INFO,
stackBuffer);
if (!threadInformation)
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
GENERAL,
"Failed to allocate thread info buffer.");
status = STATUS_INSUFFICIENT_RESOURCES;
goto Exit;
}
__try
{
CopyFromUser(threadInformation,
ThreadInformation,
ThreadInformationLength);
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
status = GetExceptionCode();
goto Exit;
}
}
else
{
threadInformation = ThreadInformation;
}
status = ObReferenceObjectByHandle(ThreadHandle,
0,
*PsThreadType,
AccessMode,
&thread,
NULL);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
GENERAL,
"ObReferenceObjectByHandle failed: %!STATUS!",
status);
thread = NULL;
goto Exit;
}
status = KphDominationCheck(PsGetCurrentProcess(),
PsGetThreadProcess(thread),
AccessMode);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
GENERAL,
"KphDominationCheck failed: %!STATUS!",
status);
goto Exit;
}
status = ObOpenObjectByPointer(thread,
OBJ_KERNEL_HANDLE,
NULL,
THREAD_SET_INFORMATION,
*PsThreadType,
KernelMode,
&threadHandle);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
GENERAL,
"ObOpenObjectByPointer failed: %!STATUS!",
status);
threadHandle = NULL;
goto Exit;
}
switch (ThreadInformationClass)
{
case KphThreadPriority:
{
threadInformationClass = ThreadPriority;
break;
}
case KphThreadBasePriority:
{
threadInformationClass = ThreadBasePriority;
break;
}
case KphThreadAffinityMask:
{
threadInformationClass = ThreadAffinityMask;
break;
}
case KphThreadIdealProcessor:
{
threadInformationClass = ThreadIdealProcessor;
break;
}
case KphThreadPriorityBoost:
{
threadInformationClass = ThreadPriorityBoost;
break;
}
case KphThreadIoPriority:
{
threadInformationClass = ThreadIoPriority;
break;
}
case KphThreadPagePriority:
{
threadInformationClass = ThreadPagePriority;
break;
}
case KphThreadActualBasePriority:
{
threadInformationClass = ThreadActualBasePriority;
break;
}
case KphThreadGroupInformation:
{
threadInformationClass = ThreadGroupInformation;
break;
}
case KphThreadIdealProcessorEx:
{
threadInformationClass = ThreadIdealProcessorEx;
break;
}
case KphThreadActualGroupAffinity:
{
threadInformationClass = ThreadActualGroupAffinity;
break;
}
case KphThreadPowerThrottlingState:
{
threadInformationClass = ThreadPowerThrottlingState;
break;
}
case KphThreadExplicitCaseSensitivity:
{
threadInformationClass = ThreadExplicitCaseSensitivity;
break;
}
default:
{
status = STATUS_INVALID_INFO_CLASS;
goto Exit;
}
}
status = ZwSetInformationThread(threadHandle,
threadInformationClass,
threadInformation,
ThreadInformationLength);
Exit:
if (threadHandle)
{
ObCloseHandle(threadHandle, KernelMode);
}
if (thread)
{
ObDereferenceObject(thread);
}
if (threadInformation && (threadInformation != ThreadInformation))
{
KphFreeA(threadInformation, KPH_TAG_THREAD_INFO, stackBuffer);
}
return status;
}
/**
* \brief Queries thread information.
*
* \param[in] ThreadHandle Handle to thread to query information of.
* \param[in] ThreadInformationClass Information class to query.
* \param[out] ThreadInformation Populated with thread information by class.
* \param[in] ThreadInformationLength Length of the thread information buffer.
* \param[out] ReturnLength Received the number of bytes written or required.
* \param[in] AccessMode The mode in which to perform access checks.
*
* \return Successful or errant status.
*/
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KphQueryInformationThread(
_In_ HANDLE ThreadHandle,
_In_ KPH_THREAD_INFORMATION_CLASS ThreadInformationClass,
_Out_writes_bytes_opt_(ThreadInformationLength) PVOID ThreadInformation,
_In_ ULONG ThreadInformationLength,
_Out_opt_ PULONG ReturnLength,
_In_ KPROCESSOR_MODE AccessMode
)
{
NTSTATUS status;
PKPH_DYN dyn;
PETHREAD threadObject;
PKPH_THREAD_CONTEXT thread;
ULONG returnLength;
KPH_PAGED_CODE_PASSIVE();
dyn = NULL;
thread = NULL;
returnLength = 0;
status = ObReferenceObjectByHandle(ThreadHandle,
0,
*PsThreadType,
AccessMode,
&threadObject,
NULL);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
GENERAL,
"ObReferenceObjectByHandle failed: %!STATUS!",
status);
threadObject = NULL;
goto Exit;
}
thread = KphGetEThreadContext(threadObject);
if (!thread)
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
GENERAL,
"KphGetThreadContext returned null.");
status = STATUS_OBJECTID_NOT_FOUND;
goto Exit;
}
switch (ThreadInformationClass)
{
case KphThreadIoCounters:
{
IO_COUNTERS counters;
PULONG64 value;
dyn = KphReferenceDynData();
if (!dyn ||
(dyn->KtReadOperationCount == ULONG_MAX) ||
(dyn->KtWriteOperationCount == ULONG_MAX) ||
(dyn->KtOtherOperationCount == ULONG_MAX) ||
(dyn->KtReadTransferCount == ULONG_MAX) ||
(dyn->KtWriteTransferCount == ULONG_MAX) ||
(dyn->KtOtherTransferCount == ULONG_MAX))
{
status = STATUS_NOINTERFACE;
goto Exit;
}
if (!ThreadInformation ||
(ThreadInformationLength < sizeof(IO_COUNTERS)))
{
status = STATUS_INFO_LENGTH_MISMATCH;
returnLength = sizeof(IO_COUNTERS);
goto Exit;
}
RtlZeroMemory(&counters, sizeof(IO_COUNTERS));
value = Add2Ptr(threadObject, dyn->KtReadOperationCount);
counters.ReadOperationCount = *value;
value = Add2Ptr(threadObject, dyn->KtWriteOperationCount);
counters.WriteOperationCount = *value;
value = Add2Ptr(threadObject, dyn->KtOtherOperationCount);
counters.OtherOperationCount = *value;
value = Add2Ptr(threadObject, dyn->KtReadTransferCount);
counters.ReadTransferCount = *value;
value = Add2Ptr(threadObject, dyn->KtWriteTransferCount);
counters.WriteTransferCount = *value;
value = Add2Ptr(threadObject, dyn->KtOtherTransferCount);
counters.OtherTransferCount = *value;
status = KphCopyToMode(ThreadInformation,
&counters,
sizeof(IO_COUNTERS),
AccessMode);
if (NT_SUCCESS(status))
{
returnLength = sizeof(IO_COUNTERS);
}
break;
}
case KphThreadWSLThreadId:
{
ULONG threadId;
if (thread->SubsystemType != SubsystemInformationTypeWSL)
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
GENERAL,
"Invalid subsystem for WSL thread ID query.");
status = STATUS_INVALID_HANDLE;
goto Exit;
}
if (!ThreadInformation ||
(ThreadInformationLength < sizeof(ULONG)))
{
status = STATUS_INFO_LENGTH_MISMATCH;
returnLength = sizeof(ULONG);
goto Exit;
}
status = KphQueryInformationThreadContext(thread,
KphThreadContextWSLThreadId,
&threadId,
sizeof(ULONG),
NULL);
if (!NT_SUCCESS(status))
{
goto Exit;
}
status = KphWriteULongToMode(ThreadInformation,
threadId,
AccessMode);
if (NT_SUCCESS(status))
{
returnLength = sizeof(ULONG);
}
break;
}
case KphThreadKernelStackInformation:
{
KPH_KERNEL_STACK_INFORMATION info;
dyn = KphReferenceDynData();
if (!dyn ||
(dyn->KtInitialStack == ULONG_MAX) ||
(dyn->KtStackLimit == ULONG_MAX) ||
(dyn->KtStackBase == ULONG_MAX) ||
(dyn->KtKernelStack == ULONG_MAX))
{
status = STATUS_NOINTERFACE;
goto Exit;
}
if (!ThreadInformation ||
(ThreadInformationLength < sizeof(KPH_KERNEL_STACK_INFORMATION)))
{
status = STATUS_INFO_LENGTH_MISMATCH;
returnLength = sizeof(KPH_KERNEL_STACK_INFORMATION);
goto Exit;
}
RtlZeroMemory(&info, sizeof(KPH_KERNEL_STACK_INFORMATION));
info.InitialStack = *(PVOID*)Add2Ptr(threadObject, dyn->KtInitialStack);
info.StackLimit = *(PVOID*)Add2Ptr(threadObject, dyn->KtStackLimit);
info.StackBase = *(PVOID*)Add2Ptr(threadObject, dyn->KtStackBase);
info.KernelStack = *(PVOID*)Add2Ptr(threadObject, dyn->KtKernelStack);
status = KphCopyToMode(ThreadInformation,
&info,
sizeof(KPH_KERNEL_STACK_INFORMATION),
AccessMode);
if (NT_SUCCESS(status))
{
returnLength = sizeof(KPH_KERNEL_STACK_INFORMATION);
}
break;
}
default:
{
status = STATUS_INVALID_INFO_CLASS;
break;
}
}
Exit:
if (ReturnLength)
{
KphWriteULongToMode(ReturnLength, returnLength, AccessMode);
}
if (thread)
{
KphDereferenceObject(thread);
}
if (threadObject)
{
ObDereferenceObject(threadObject);
}
if (dyn)
{
KphDereferenceObject(dyn);
}
return status;
}
================================================
FILE: KSystemInformer/umaccess.c
================================================
/*
* Copyright (c) 2022 Winsider Seminars & Solutions, Inc. All rights reserved.
*
* This file is part of System Informer.
*
* Authors:
*
* jxy-s 2025-2026
*
*/
#include
#include
KPH_PAGED_FILE();
/**
* \brief Captures a Unicode string from user mode.
*
* \param[in] UnicodeString Unicode string to capture from user mode.
* \param[out] CapturedUnicodeString Receives the captured Unicode string, the
* captured buffer must be freed using KphReleaseUnicodeString.
*
* \return Successful or errant status.
*/
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KphCaptureUnicodeString(
_In_ PUNICODE_STRING UnicodeString,
_Out_ PUNICODE_STRING* CapturedUnicodeString
)
{
NTSTATUS status;
UNICODE_STRING inputString;
PUNICODE_STRING outputString;
KPH_PAGED_CODE_PASSIVE();
outputString = NULL;
__try
{
CopyFromUser(&inputString, UnicodeString, sizeof(UNICODE_STRING));
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
status = GetExceptionCode();
goto Exit;
}
outputString = KphAllocatePaged(sizeof(UNICODE_STRING) + inputString.Length,
KPH_TAG_CAPTURED_UNICODE_STRING);
if (!outputString)
{
status = STATUS_INSUFFICIENT_RESOURCES;
goto Exit;
}
outputString->Buffer = Add2Ptr(outputString, sizeof(UNICODE_STRING));
outputString->Length = 0;
outputString->MaximumLength = inputString.Length;
__try
{
CopyFromUser(outputString->Buffer,
inputString.Buffer,
inputString.Length);
outputString->Length = inputString.Length;
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
status = GetExceptionCode();
goto Exit;
}
*CapturedUnicodeString = outputString;
outputString = NULL;
status = STATUS_SUCCESS;
Exit:
if (outputString)
{
KphFree(outputString, KPH_TAG_CAPTURED_UNICODE_STRING);
}
return status;
}
/**
* \brief Releases a previously captured Unicode string.
*
* \param[in] UnicodeString Unicode string to release.
*/
_IRQL_requires_max_(PASSIVE_LEVEL)
VOID KphReleaseUnicodeString(
_In_ PUNICODE_STRING CaputredUnicodeString
)
{
KPH_PAGED_CODE_PASSIVE();
KphFree(CaputredUnicodeString, KPH_TAG_CAPTURED_UNICODE_STRING);
}
/**
* \brief Zeros memory in the specified mode.
*
* \param[out] Destination Address to zero memory at.
* \param[in] Length The length of the memory to zero, in bytes.
* \param[in] AccessMode The access mode to use for the zeroing.
*
* \return Successful or errant status.
*/
_IRQL_requires_max_(APC_LEVEL)
_Must_inspect_result_
NTSTATUS KphZeroModeMemory(
_Out_writes_bytes_all_(Length) PVOID Destination,
_In_ SIZE_T Length,
_In_ KPROCESSOR_MODE AccessMode
)
{
KPH_PAGED_CODE();
__try
{
ZeroModeMemory(Destination, Length, AccessMode);
}
__except (UmaExceptionFilter(AccessMode))
{
return GetExceptionCode();
}
return STATUS_SUCCESS;
}
/**
* \brief Copies memory to the specified mode.
*
* \param[out] Destination Address to copy memory to.
* \param[in] Source Address to copy memory from.
* \param[in] Length The length of the memory to copy, in bytes.
* \param[in] AccessMode The access mode to use for the copy.
*
* \return Successful or errant status.
*/
_IRQL_requires_max_(APC_LEVEL)
NTSTATUS KphCopyToMode(
_Out_writes_bytes_all_(Length) PVOID Destination,
_In_reads_bytes_(Length) PVOID Source,
_In_ SIZE_T Length,
_In_ KPROCESSOR_MODE AccessMode
)
{
KPH_PAGED_CODE();
__try
{
CopyToMode(Destination, Source, Length, AccessMode);
}
__except (UmaExceptionFilter(AccessMode))
{
return GetExceptionCode();
}
return STATUS_SUCCESS;
}
/**
* \brief Copies memory from the specified mode.
*
* \param[out] Destination Address to copy memory to.
* \param[in] Source Address to copy memory from.
* \param[in] Length The length of the memory to copy, in bytes.
* \param[in] AccessMode The access mode to use for the copy.
*
* \return Successful or errant status.
*/
_IRQL_requires_max_(APC_LEVEL)
_Must_inspect_result_
NTSTATUS KphCopyFromMode(
_Out_writes_bytes_all_(Length) PVOID Destination,
_In_reads_bytes_(Length) PVOID Source,
_In_ SIZE_T Length,
_In_ KPROCESSOR_MODE AccessMode
)
{
KPH_PAGED_CODE();
__try
{
CopyFromMode(Destination, Source, Length, AccessMode);
}
__except (UmaExceptionFilter(AccessMode))
{
return GetExceptionCode();
}
return STATUS_SUCCESS;
}
/**
* \brief Writes an unsigned 8-bit value to the specified mode.
*
* \param[out] Destination Address to write the unsigned 8-bit value to.
* \param[in] Source The unsigned 8-bit value to write.
* \param[in] AccessMode The access mode to use for the write.
*
* \return Successful or errant status.
*/
_IRQL_requires_max_(APC_LEVEL)
NTSTATUS KphWriteUCharToMode(
_Out_ PUCHAR Destination,
_In_ UCHAR Source,
_In_ KPROCESSOR_MODE AccessMode
)
{
KPH_PAGED_CODE();
__try
{
WriteUCharToMode(Destination, Source, AccessMode);
}
__except (UmaExceptionFilter(AccessMode))
{
return GetExceptionCode();
}
return STATUS_SUCCESS;
}
/**
* \brief Writes an unsigned 32-bit value to the specified mode.
*
* \param[out] Destination Address to write the unsigned 32-bit value to.
* \param[in] Source The unsigned 32-bit value to write.
* \param[in] AccessMode The access mode to use for the write.
*
* \return Successful or errant status.
*/
_IRQL_requires_max_(APC_LEVEL)
NTSTATUS KphWriteULongToMode(
_Out_ PULONG Destination,
_In_ ULONG Source,
_In_ KPROCESSOR_MODE AccessMode
)
{
KPH_PAGED_CODE();
__try
{
WriteULongToMode(Destination, Source, AccessMode);
}
__except (UmaExceptionFilter(AccessMode))
{
return GetExceptionCode();
}
return STATUS_SUCCESS;
}
/**
* \brief Writes an unsigned 64-bit value to the specified mode.
*
* \param[out] Destination Address to write the unsigned 64-bit value to.
* \param[in] Source The unsigned 64-bit value to write.
* \param[in] AccessMode The access mode to use for the write.
*
* \return Successful or errant status.
*/
_IRQL_requires_max_(APC_LEVEL)
NTSTATUS KphWriteULong64ToMode(
_Out_ PULONG64 Destination,
_In_ ULONG64 Source,
_In_ KPROCESSOR_MODE AccessMode
)
{
KPH_PAGED_CODE();
__try
{
WriteULong64ToMode(Destination, Source, AccessMode);
}
__except (UmaExceptionFilter(AccessMode))
{
return GetExceptionCode();
}
return STATUS_SUCCESS;
}
/**
* \brief Writes an signed 64-bit value to the specified mode.
*
* \param[out] Destination Address to write the signed 64-bit value to.
* \param[in] Source The signed 64-bit value to write.
* \param[in] AccessMode The access mode to use for the write.
*
* \return Successful or errant status.
*/
_IRQL_requires_max_(APC_LEVEL)
NTSTATUS KphWriteLong64ToMode(
_Out_ PLONG64 Destination,
_In_ LONG64 Source,
_In_ KPROCESSOR_MODE AccessMode
)
{
KPH_PAGED_CODE();
__try
{
WriteLong64ToMode(Destination, Source, AccessMode);
}
__except (UmaExceptionFilter(AccessMode))
{
return GetExceptionCode();
}
return STATUS_SUCCESS;
}
/**
* \brief Writes an unsigned size width value to the specified mode.
*
* \param[out] Destination Address to write the unsigned sized width value to.
* \param[in] Source The unsigned sized width value to write.
* \param[in] AccessMode The access mode to use for the write.
*
* \return Successful or errant status.
*/
_IRQL_requires_max_(APC_LEVEL)
NTSTATUS KphWriteSizeTToMode(
_Out_ PSIZE_T Destination,
_In_ SIZE_T Source,
_In_ KPROCESSOR_MODE AccessMode
)
{
KPH_PAGED_CODE();
__try
{
WriteSizeTToMode(Destination, Source, AccessMode);
}
__except (UmaExceptionFilter(AccessMode))
{
return GetExceptionCode();
}
return STATUS_SUCCESS;
}
/**
* \brief Writes a pointer to the specified mode.
*
* \param[out] Destination Address to write the pointer to.
* \param[in] Source The pointer to write.
* \param[in] AccessMode The access mode to use for the write.
*
* \return Successful or errant status.
*/
_IRQL_requires_max_(APC_LEVEL)
NTSTATUS KphWritePointerToMode(
_Out_ PVOID* Destination,
_In_ PVOID Source,
_In_ KPROCESSOR_MODE AccessMode
)
{
KPH_PAGED_CODE();
__try
{
WritePointerToMode(Destination, Source, AccessMode);
}
__except (UmaExceptionFilter(AccessMode))
{
return GetExceptionCode();
}
return STATUS_SUCCESS;
}
/**
* \brief Writes a handle to the specified mode.
*
* \details If the write fails then the handle is closed oh behalf of the caller
* to prevent a handle leak.
*
* \param[out] Destination Address to write the handle to.
* \param[in] Source The handle to write.
* \param[in] AccessMode The access mode to use for the write.
*
* \return Successful or errant status.
*/
_IRQL_requires_max_(APC_LEVEL)
NTSTATUS KphWriteHandleToMode(
_Out_ PHANDLE Destination,
_In_ _Post_ptr_invalid_ HANDLE Source,
_In_ KPROCESSOR_MODE AccessMode
)
{
KPH_PAGED_CODE();
__try
{
WriteHandleToMode(Destination, Source, AccessMode);
}
__except (UmaExceptionFilter(AccessMode))
{
ObCloseHandle(Source, AccessMode);
return GetExceptionCode();
}
return STATUS_SUCCESS;
}
/**
* \brief Copies a Unicode string to the specified mode.
*
* \details The Unicode string is constructed at the destination address and the
* string content immediately follows the string structure.
*
* \param[out] Destination Optional address to copy the Unicode string to.
* \param[in] Length The length of the destination buffer, in bytes.
* \param[in] String Optional Unicode string to copy.
* \param[out] ReturnLength Number of bytes copied to the destination buffer,
* including the string structure and following content.
* \param[in] AccessMode The access mode to use for the copy.
*
* \return Successful or errant status.
*/
_IRQL_requires_max_(APC_LEVEL)
NTSTATUS KphCopyUnicodeStringToMode(
_Out_writes_bytes_opt_(Length) PVOID Destination,
_In_ SIZE_T Length,
_In_opt_ PCUNICODE_STRING String,
_Out_ PULONG ReturnLength,
_In_ KPROCESSOR_MODE AccessMode
)
{
PUNICODE_STRING destination;
USHORT maximumLength;
KPH_PAGED_CODE();
if (!String)
{
*ReturnLength = sizeof(UNICODE_STRING);
if (!Destination || (Length < sizeof(UNICODE_STRING)))
{
return STATUS_BUFFER_TOO_SMALL;
}
destination = Destination;
return KphZeroModeMemory(destination,
sizeof(UNICODE_STRING),
AccessMode);
}
*ReturnLength = (sizeof(UNICODE_STRING) + String->Length);
if (!Destination || (Length < (sizeof(UNICODE_STRING) + String->Length)))
{
return STATUS_BUFFER_TOO_SMALL;
}
destination = Destination;
maximumLength = (USHORT)(Length - sizeof(UNICODE_STRING));
if (AccessMode != KernelMode)
{
__try
{
WriteUShortToUser(&destination->Length, String->Length);
WriteUShortToUser(&destination->MaximumLength, maximumLength);
WritePointerToUser(&destination->Buffer,
Add2Ptr(destination, sizeof(UNICODE_STRING)));
NT_ASSERT(destination->Buffer);
CopyToUser(destination->Buffer, String->Buffer, String->Length);
if ((String->Length + sizeof(UNICODE_NULL)) <= maximumLength)
{
PWCHAR end;
end = &destination->Buffer[String->Length / sizeof(WCHAR)];
WriteUShortToUser(end, UNICODE_NULL);
}
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
return GetExceptionCode();
}
}
else
{
destination->Length = 0;
destination->MaximumLength = maximumLength;
destination->Buffer = Add2Ptr(destination, sizeof(UNICODE_STRING));
RtlCopyUnicodeString(destination, String);
}
return STATUS_SUCCESS;
}
/**
* \brief Reads a LARGE_INTEGER from the specified mode.
*
* \param[out] Destination Address to read the LARGE_INTEGER to.
* \param[in] Source The LARGE_INTEGER to read.
* \param[in] AccessMode The access mode to use for the read.
*
* \return Successful or errant status.
*/
_IRQL_requires_max_(APC_LEVEL)
_Must_inspect_result_
NTSTATUS KphReadLargeIntegerFromMode(
_Out_ PLARGE_INTEGER Destination,
_In_ PLARGE_INTEGER Source,
_In_ KPROCESSOR_MODE AccessMode
)
{
KPH_PAGED_CODE();
__try
{
*Destination = ReadLargeIntegerFromMode(Source, AccessMode);
}
__except (UmaExceptionFilter(AccessMode))
{
return GetExceptionCode();
}
return STATUS_SUCCESS;
}
================================================
FILE: KSystemInformer/util.c
================================================
/*
* Copyright (c) 2022 Winsider Seminars & Solutions, Inc. All rights reserved.
*
* This file is part of System Informer.
*
* Authors:
*
* wj32 2016
* jxy-s 2022-2026
*
*/
#include
#include
#include
/**
* \brief Performs a binary search of a sorted array.
*
* \param[in] Key Pointer to the key to search for.
* \param[in] Base Pointer to the base of the search data.
* \param[in] NumberOfElements Number of elements in the array.
* \param[in] SizeOfElement Size of each element in the array.
* \param[in] Callback Comparison callback.
* \param[in] Context Optional context for the callback.
*
* \return Pointer to the found element, NULL if not found.
*/
_Must_inspect_result_
_Success_(return != NULL)
PVOID KphBinarySearch(
_In_ PCVOID Key,
_In_reads_bytes_(NumberOfElements * SizeOfElement) PCVOID Base,
_In_ ULONG NumberOfElements,
_In_ ULONG SizeOfElement,
_In_ PKPH_BINARY_SEARCH_CALLBACK Callback,
_In_opt_ PVOID Context
)
{
return bsearch_s(Key,
Base,
NumberOfElements,
SizeOfElement,
Callback,
Context);
}
/**
* \brief Performs a quick sort of an array.
*
* \param[in] Base Pointer to the base of the array to sort.
* \param[in] NumberOfElements Number of elements in the array.
* \param[in] SizeOfElement Size of each element in the array.
* \param[in] Callback Comparison callback.
* \param[in] Context Optional context for the callback.
*/
VOID KphQuickSort(
_Inout_updates_bytes_(NumberOfElements * SizeOfElement) PVOID Base,
_In_ ULONG NumberOfElements,
_In_ ULONG SizeOfElement,
_In_ PKPH_QUICK_SORT_CALLBACK Callback,
_In_opt_ PVOID Context
)
{
qsort_s(Base, NumberOfElements, SizeOfElement, Callback, Context);
}
/**
* \brief Searches memory for a given pattern.
*
* \param[in] Buffer The memory to search.
* \param[in] BufferLength The length of the memory to search.
* \param[in] Pattern The pattern to search for.
* \param[in] PatternLength The length of the pattern to search for.
*
* \return Pointer to the beginning of the first found pattern, NULL if the
* pattern is not found.
*/
_Must_inspect_result_
_Success_(return != NULL)
PVOID KphSearchMemory(
_In_reads_bytes_(BufferLength) PVOID Buffer,
_In_ ULONG BufferLength,
_In_reads_bytes_(PatternLength) PVOID Pattern,
_In_ ULONG PatternLength
)
{
PBYTE buffer;
PBYTE end;
if (!BufferLength || !PatternLength)
{
return NULL;
}
if (PatternLength > BufferLength)
{
return NULL;
}
buffer = Buffer;
end = Add2Ptr(Buffer, BufferLength - PatternLength);
//
// Move the loop into the switch to ensure optimal code generation.
//
#define KPH_SEARCH_MEMORY_FOR for (; buffer <= end; buffer++)
//
// Optimization for a pattern size that fits into a register.
//
#define KPH_SEARCH_MEMORY_SIZED(type) \
case sizeof(type): \
{ \
KPH_SEARCH_MEMORY_FOR \
{ \
if (*(type*)buffer == *(type*)Pattern) \
{ \
return buffer; \
} \
} \
break; \
}
switch (PatternLength)
{
KPH_SEARCH_MEMORY_SIZED(UCHAR)
KPH_SEARCH_MEMORY_SIZED(USHORT)
KPH_SEARCH_MEMORY_SIZED(ULONG)
KPH_SEARCH_MEMORY_SIZED(ULONG64)
default:
{
KPH_SEARCH_MEMORY_FOR
{
#pragma warning(suppress: 4995) // suppress deprecation warning
if (memcmp(buffer, Pattern, PatternLength) == 0)
{
return buffer;
}
}
break;
}
}
return NULL;
}
/**
* \brief Acquires rundown. On successful return the caller should release
* the rundown using KphReleaseRundown.
*
* \param[in,out] Rundown The rundown object to acquire.
*
* \return TRUE if rundown is acquired, FALSE if object is already ran down.
*/
_IRQL_requires_max_(DISPATCH_LEVEL)
_Must_inspect_result_
BOOLEAN KphAcquireRundown(
_Inout_ PKPH_RUNDOWN Rundown
)
{
KPH_NPAGED_CODE_DISPATCH_MAX();
return ExAcquireRundownProtection(Rundown);
}
/**
* \brief Releases rundown previously acquired by KphAcquireRundown.
*
* \param[in,out] Rundown The rundown object to release.
*/
_IRQL_requires_max_(DISPATCH_LEVEL)
VOID KphReleaseRundown(
_Inout_ PKPH_RUNDOWN Rundown
)
{
KPH_NPAGED_CODE_DISPATCH_MAX();
ExReleaseRundownProtection(Rundown);
}
/**
* \brief Retrieves the process sequence number for a given process.
*
* \param[in] Process The process to get the sequence number of.
*
* \return The sequence number key.
*/
_IRQL_requires_max_(DISPATCH_LEVEL)
ULONG64 KphGetProcessSequenceNumber(
_In_ PEPROCESS Process
)
{
ULONG64 sequence;
PKPH_PROCESS_CONTEXT process;
KPH_NPAGED_CODE_DISPATCH_MAX();
if (KphDynPsGetProcessSequenceNumber)
{
return KphDynPsGetProcessSequenceNumber(Process);
}
process = KphGetEProcessContext(Process);
if (!process)
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
UTIL,
"Failed to get process sequence number for PID %lu",
HandleToULong(PsGetProcessId(Process)));
return 0;
}
sequence = process->SequenceNumber;
KphDereferenceObject(process);
return sequence;
}
/**
* \brief Retrieves the process start key for a given process.
*
* \param[in] Process The process to get the start key of.
*
* \return The process start key.
*/
_IRQL_requires_max_(DISPATCH_LEVEL)
ULONG64 KphGetProcessStartKey(
_In_ PEPROCESS Process
)
{
ULONG64 key;
PKPH_PROCESS_CONTEXT process;
KPH_NPAGED_CODE_DISPATCH_MAX();
if (KphDynPsGetProcessStartKey)
{
return KphDynPsGetProcessStartKey(Process);
}
process = KphGetEProcessContext(Process);
if (!process)
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
UTIL,
"Failed to get process start key for PID %lu",
HandleToULong(PsGetProcessId(Process)));
return 0;
}
if (process->ProcessStartKey)
{
key = process->ProcessStartKey;
}
else
{
key = (process->SequenceNumber | ((ULONG64)SharedUserData->BootId << 48));
}
KphDereferenceObject(process);
return key;
}
/**
* \brief Retrieves the current thread's sub-process tag.
*
* \return The current thread's sub-process tag.
*/
_IRQL_requires_max_(DISPATCH_LEVEL)
PVOID KphGetCurrentThreadSubProcessTag(
VOID
)
{
PKPH_THREAD_CONTEXT thread;
PVOID subProcessTag;
PTEB teb;
KPH_NPAGED_CODE_DISPATCH_MAX();
if (PsIsSystemThread(PsGetCurrentThread()))
{
return NULL;
}
//
// We support lookups at dispatch. To achieve this we cache the last lookup
// in the thread context. If we're at dispatch use the cache. Otherwise go
// do the lookup and cache the result in the thread context.
//
if (KeGetCurrentIrql() > APC_LEVEL)
{
subProcessTag = NULL;
thread = KphGetCurrentThreadContext();
if (thread)
{
subProcessTag = thread->SubProcessTag;
KphDereferenceObject(thread);
}
return subProcessTag;
}
teb = PsGetCurrentThreadTeb();
if (!teb)
{
return NULL;
}
__try
{
subProcessTag = ReadPointerFromUser(&teb->SubProcessTag);
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
return NULL;
}
thread = KphGetCurrentThreadContext();
if (thread)
{
thread->SubProcessTag = subProcessTag;
KphDereferenceObject(thread);
}
return subProcessTag;
}
/**
* \brief Retrieves a thread's sub-process tag.
*
* \param[in] Thread The thread to get the sub-process tag of.
* \param[in] CacheOnly If TRUE, only the cached value is returned. This is
* useful when the caller knows that touching the TEB is not safe.
*
* \return The thread's sub-process tag.
*/
_IRQL_requires_max_(DISPATCH_LEVEL)
PVOID KphGetThreadSubProcessTagEx(
_In_ PETHREAD Thread,
_In_ BOOLEAN CacheOnly
)
{
PKPH_THREAD_CONTEXT thread;
PVOID subProcessTag;
PTEB teb;
KPH_NPAGED_CODE_DISPATCH_MAX();
if (PsIsSystemThread(Thread))
{
return NULL;
}
//
// We support lookups at dispatch and across process boundaries. To achieve
// this we cache the last lookup in the thread context. If we're at dispatch
// or across process boundaries use the cache. Otherwise go do the lookup
// and cache the result in the thread context. We choose not to attach to
// a process to retrieve the information to avoid performance penalties.
//
if (CacheOnly ||
(KeGetCurrentIrql() > APC_LEVEL) ||
(PsGetThreadProcess(Thread) != PsGetCurrentProcess()))
{
subProcessTag = NULL;
thread = KphGetEThreadContext(Thread);
if (thread)
{
subProcessTag = thread->SubProcessTag;
KphDereferenceObject(thread);
}
return subProcessTag;
}
teb = PsGetThreadTeb(Thread);
if (!teb)
{
return NULL;
}
__try
{
subProcessTag = ReadPointerFromUser(&teb->SubProcessTag);
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
return NULL;
}
thread = KphGetEThreadContext(Thread);
if (thread)
{
thread->SubProcessTag = subProcessTag;
KphDereferenceObject(thread);
}
return subProcessTag;
}
/**
* \brief Retrieves a thread's sub-process tag.
*
* \param[in] Thread The thread to get the sub-process tag of.
*
* \return The thread's sub-process tag.
*/
_IRQL_requires_max_(DISPATCH_LEVEL)
PVOID KphGetThreadSubProcessTag(
_In_ PETHREAD Thread
)
{
KPH_NPAGED_CODE_DISPATCH_MAX();
return KphGetThreadSubProcessTagEx(Thread, FALSE);
}
/**
* \brief Initializes rundown object.
*
* \param[out] Rundown The rundown object to initialize.
*/
_IRQL_requires_max_(APC_LEVEL)
VOID KphInitializeRundown(
_Out_ PKPH_RUNDOWN Rundown
)
{
KPH_NPAGED_CODE_APC_MAX_FOR_PAGING_IO();
ExInitializeRundownProtection(Rundown);
}
/**
* \brief Marks rundown active and waits for rundown release. Subsequent
* attempts to acquire rundown will fail.
*
* \param[in,out] Rundown The rundown object to activate and wait for.
*/
_IRQL_requires_max_(APC_LEVEL)
VOID KphWaitForRundown(
_Inout_ PKPH_RUNDOWN Rundown
)
{
KPH_NPAGED_CODE_APC_MAX_FOR_PAGING_IO();
ExWaitForRundownProtectionRelease(Rundown);
}
/**
* \brief Initializes a readers-writer lock.
*
* \param[in] Lock The readers-writer lock to initialize.
*/
_IRQL_requires_max_(APC_LEVEL)
VOID KphInitializeRWLock(
_Out_ PKPH_RWLOCK Lock
)
{
KPH_NPAGED_CODE_APC_MAX_FOR_PAGING_IO();
FltInitializePushLock(Lock);
}
/**
* \brief Deletes a readers-writer lock.
*
* \param[in] Lock The readers-writer lock to delete.
*/
_IRQL_requires_max_(APC_LEVEL)
VOID KphDeleteRWLock(
_In_ PKPH_RWLOCK Lock
)
{
KPH_NPAGED_CODE_APC_MAX_FOR_PAGING_IO();
FltDeletePushLock(Lock);
}
/**
* \brief Acquires a readers-writer lock exclusive.
*
* \param[in,out] Lock The readers-writer lock to acquire exclusively.
*/
_IRQL_requires_max_(APC_LEVEL)
_Acquires_lock_(_Global_critical_region_)
VOID KphAcquireRWLockExclusive(
_Inout_ _Requires_lock_not_held_(*_Curr_) _Acquires_lock_(*_Curr_) PKPH_RWLOCK Lock
)
{
KPH_NPAGED_CODE_APC_MAX_FOR_PAGING_IO();
FltAcquirePushLockExclusive(Lock);
}
/**
* \brief Acquires readers-writer lock shared.
*
* \param[in,out] Lock The readers-writer lock to acquire shared.
*/
_IRQL_requires_max_(APC_LEVEL)
_Acquires_lock_(_Global_critical_region_)
VOID KphAcquireRWLockShared(
_Inout_ _Requires_lock_not_held_(*_Curr_) _Acquires_lock_(*_Curr_) PKPH_RWLOCK Lock
)
{
KPH_NPAGED_CODE_APC_MAX_FOR_PAGING_IO();
FltAcquirePushLockShared(Lock);
}
/**
* \brief Releases a readers-writer lock.
*
* \param[in,out] Lock The readers-writer lock to release.
*/
_IRQL_requires_max_(APC_LEVEL)
_Releases_lock_(_Global_critical_region_)
VOID KphReleaseRWLock(
_Inout_ _Requires_lock_held_(*_Curr_) _Releases_lock_(*_Curr_) PKPH_RWLOCK Lock
)
{
KPH_NPAGED_CODE_APC_MAX_FOR_PAGING_IO();
FltReleasePushLock(Lock);
}
/**
* \brief Checks if two file objects are associated with the same data.
*
* \param[in] FirstFileObject The first file object to check.
* \param[in] SecondFileObject The second file object to check.
*
* \return TRUE if the files are the same, FALSE otherwise.
*/
_IRQL_requires_max_(DISPATCH_LEVEL)
_Must_inspect_result_
BOOLEAN KphIsSameFile(
_In_ PFILE_OBJECT FirstFileObject,
_In_ PFILE_OBJECT SecondFileObject
)
{
PSECTION_OBJECT_POINTERS first;
PSECTION_OBJECT_POINTERS second;
KPH_NPAGED_CODE_DISPATCH_MAX();
first = FirstFileObject->SectionObjectPointer;
second = SecondFileObject->SectionObjectPointer;
if (first != second)
{
return FALSE;
}
if (!first)
{
return TRUE;
}
if ((first->DataSectionObject != second->DataSectionObject) ||
(first->SharedCacheMap != second->SharedCacheMap) ||
(first->ImageSectionObject != second->ImageSectionObject))
{
return FALSE;
}
return TRUE;
}
KPH_PAGED_FILE();
/**
* \brief Acquires a reference to a reference object.
*
* \details KPH_REFERENCE provides underflow and overflow guarantees. For this
* reason, KPH_REFERENCE may not be suitable for all applications since it is
* more expensive than a simple reference count.
*
* \param[in,out] Reference The reference object to acquire.
* \param[out] PreviousCount Optionally set to the previous reference count.
*
* \return Successful or errant status.
*/
_IRQL_requires_max_(APC_LEVEL)
_Must_inspect_result_
NTSTATUS KphAcquireReference(
_Inout_ PKPH_REFERENCE Reference,
_Out_opt_ PLONG PreviousCount
)
{
LONG count;
KPH_PAGED_CODE();
count = ReadAcquire(&Reference->Count);
for (;;)
{
LONG expected;
if (count == LONG_MAX)
{
if (PreviousCount)
{
*PreviousCount = LONG_MAX;
}
return STATUS_INTEGER_OVERFLOW;
}
expected = count;
count = InterlockedCompareExchange(&Reference->Count,
count + 1,
expected);
if (count == expected)
{
if (PreviousCount)
{
*PreviousCount = count;
}
return STATUS_SUCCESS;
}
}
}
/**
* \brief Releases a reference to a reference object.
*
* \details KPH_REFERENCE provides underflow and overflow guarantees. For this
* reason, KPH_REFERENCE may not be suitable for all applications since it is
* more expensive than a simple reference count.
*
* \param[in,out] Reference The reference object to release.
* \param[out] PreviousCount Optionally set to the previous reference count.
*
* \return Successful or errant status.
*/
_IRQL_requires_max_(APC_LEVEL)
_Must_inspect_result_
NTSTATUS KphReleaseReference(
_Inout_ PKPH_REFERENCE Reference,
_Out_opt_ PLONG PreviousCount
)
{
LONG count;
KPH_PAGED_CODE();
count = ReadAcquire(&Reference->Count);
for (;;)
{
LONG expected;
if (count == 0)
{
if (PreviousCount)
{
*PreviousCount = 0;
}
return STATUS_INTEGER_OVERFLOW;
}
expected = count;
count = InterlockedCompareExchange(&Reference->Count,
count - 1,
count);
if (count == expected)
{
if (PreviousCount)
{
*PreviousCount = count;
}
return STATUS_SUCCESS;
}
}
}
/**
* \brief Checks if an address range lies within a kernel module.
*
* \param[in] Address The beginning of the address range.
* \param[in] Length The number of bytes in the address range.
*
* \return Successful or errant status.
*/
_IRQL_requires_max_(APC_LEVEL)
_Must_inspect_result_
NTSTATUS KphValidateAddressForSystemModules(
_In_ PVOID Address,
_In_ SIZE_T Length
)
{
BOOLEAN valid;
PVOID endAddress;
KPH_PAGED_CODE();
if (Add2Ptr(Address, Length) < Address)
{
return STATUS_BUFFER_OVERFLOW;
}
endAddress = Add2Ptr(Address, Length);
KeEnterCriticalRegion();
if (!ExAcquireResourceSharedLite(PsLoadedModuleResource, TRUE))
{
KeLeaveCriticalRegion();
KphTracePrint(TRACE_LEVEL_VERBOSE,
UTIL,
"Failed to acquire PsLoadedModuleResource");
return STATUS_RESOURCE_NOT_OWNED;
}
valid = FALSE;
for (PLIST_ENTRY link = PsLoadedModuleList->Flink;
link != PsLoadedModuleList;
link = link->Flink)
{
PKLDR_DATA_TABLE_ENTRY entry;
PVOID endOfImage;
entry = CONTAINING_RECORD(link, KLDR_DATA_TABLE_ENTRY, InLoadOrderLinks);
endOfImage = Add2Ptr(entry->DllBase, entry->SizeOfImage);
if ((Address >= entry->DllBase) && (endAddress <= endOfImage))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
UTIL,
"Validated address in %wZ",
&entry->FullDllName);
valid = TRUE;
break;
}
}
ExReleaseResourceLite(PsLoadedModuleResource);
KeLeaveCriticalRegion();
return (valid ? STATUS_SUCCESS : STATUS_ACCESS_VIOLATION);
}
/**
* \brief Queries the registry key for a string value.
*
* \param[in] KeyHandle Handle to key to query.
* \param[in] ValueName Name of value to query.
* \param[out] String Populated with the queried registry string. The string is
* guaranteed to be null terminated and the MaximumLength will reflect this
* when compared to the Length. This string should be freed using
* KphFreeRegistryString.
*
* \return Successful or errant status.
*/
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KphQueryRegistryString(
_In_ HANDLE KeyHandle,
_In_ PCUNICODE_STRING ValueName,
_Outptr_allocatesMem_ PUNICODE_STRING* String
)
{
NTSTATUS status;
ULONG resultLength;
PKEY_VALUE_PARTIAL_INFORMATION info;
PUNICODE_STRING string;
ULONG length;
KPH_PAGED_CODE_PASSIVE();
*String = NULL;
info = NULL;
status = ZwQueryValueKey(KeyHandle,
(PUNICODE_STRING)ValueName,
KeyValuePartialInformation,
NULL,
0,
&resultLength);
if ((status != STATUS_BUFFER_OVERFLOW) &&
(status != STATUS_BUFFER_TOO_SMALL))
{
if (NT_SUCCESS(status))
{
status = STATUS_UNSUCCESSFUL;
}
goto Exit;
}
info = KphAllocatePaged(resultLength, KPH_TAG_REG_STRING);
if (!info)
{
status = STATUS_INSUFFICIENT_RESOURCES;
goto Exit;
}
status = ZwQueryValueKey(KeyHandle,
(PUNICODE_STRING)ValueName,
KeyValuePartialInformation,
info,
resultLength,
&resultLength);
if (!NT_SUCCESS(status))
{
goto Exit;
}
if (resultLength < sizeof(KEY_VALUE_PARTIAL_INFORMATION))
{
status = STATUS_INFO_LENGTH_MISMATCH;
goto Exit;
}
status = RtlULongAdd(info->DataLength, sizeof(WCHAR), &length);
if (!NT_SUCCESS(status))
{
goto Exit;
}
if ((info->Type != REG_SZ) ||
(length > UNICODE_STRING_MAX_BYTES) ||
((info->DataLength % 2) != 0))
{
status = STATUS_OBJECT_TYPE_MISMATCH;
goto Exit;
}
string = KphAllocatePaged((sizeof(UNICODE_STRING) + length),
KPH_TAG_REG_STRING);
if (!string)
{
status = STATUS_INSUFFICIENT_RESOURCES;
goto Exit;
}
string->Buffer = Add2Ptr(string, sizeof(UNICODE_STRING));
RtlZeroMemory(string->Buffer, length);
NT_ASSERT(info->DataLength < UNICODE_STRING_MAX_BYTES);
NT_ASSERT(length < UNICODE_STRING_MAX_BYTES);
string->Length = (USHORT)info->DataLength;
string->MaximumLength = (USHORT)length;
if (string->Length)
{
PWCHAR sz;
sz = (PWCHAR)info->Data;
NT_ASSERT(info->DataLength >= sizeof(WCHAR));
if (sz[(info->DataLength / sizeof(WCHAR)) - 1] == UNICODE_NULL)
{
string->Length -= sizeof(WCHAR);
}
RtlCopyMemory(string->Buffer, info->Data, string->Length);
}
*String = string;
status = STATUS_SUCCESS;
Exit:
if (info)
{
KphFree(info, KPH_TAG_REG_STRING);
}
return status;
}
/**
* \brief Frees a string retrieved by KphQueryRegistryString.
*
* \param[in] String The string to free.
*/
_IRQL_requires_max_(APC_LEVEL)
VOID KphFreeRegistryString(
_In_freesMem_ PUNICODE_STRING String
)
{
KPH_PAGED_CODE();
KphFree(String, KPH_TAG_REG_STRING);
}
/**
* \brief Queries the registry key for a string value.
*
* \param[in] KeyHandle Handle to key to query.
* \param[in] ValueName Name of value to query.
* \param[out] Buffer Registry binary buffer on success, should be freed using
* KphFreeRegistryBinary.
* \param[out] Length Set to the length of the buffer.
*
* \return Successful or errant status.
*/
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KphQueryRegistryBinary(
_In_ HANDLE KeyHandle,
_In_ PCUNICODE_STRING ValueName,
_Outptr_allocatesMem_ PBYTE* Buffer,
_Out_ PULONG Length
)
{
NTSTATUS status;
PBYTE buffer;
ULONG resultLength;
PKEY_VALUE_PARTIAL_INFORMATION info;
KPH_PAGED_CODE_PASSIVE();
*Buffer = NULL;
*Length = 0;
buffer = NULL;
status = ZwQueryValueKey(KeyHandle,
(PUNICODE_STRING)ValueName,
KeyValuePartialInformation,
NULL,
0,
&resultLength);
if ((status != STATUS_BUFFER_OVERFLOW) &&
(status != STATUS_BUFFER_TOO_SMALL))
{
if (NT_SUCCESS(status))
{
status = STATUS_UNSUCCESSFUL;
}
goto Exit;
}
buffer = KphAllocatePaged(resultLength, KPH_TAG_REG_BINARY);
if (!buffer)
{
status = STATUS_INSUFFICIENT_RESOURCES;
goto Exit;
}
status = ZwQueryValueKey(KeyHandle,
(PUNICODE_STRING)ValueName,
KeyValuePartialInformation,
buffer,
resultLength,
&resultLength);
if (!NT_SUCCESS(status))
{
goto Exit;
}
if (resultLength < sizeof(KEY_VALUE_PARTIAL_INFORMATION))
{
status = STATUS_INFO_LENGTH_MISMATCH;
goto Exit;
}
info = (PKEY_VALUE_PARTIAL_INFORMATION)buffer;
if (info->Type != REG_BINARY)
{
status = STATUS_OBJECT_TYPE_MISMATCH;
goto Exit;
}
*Length = info->DataLength;
*Buffer = info->Data;
buffer = NULL;
status = STATUS_SUCCESS;
Exit:
if (buffer)
{
KphFree(buffer, KPH_TAG_REG_BINARY);
}
return status;
}
/**
* \brief Frees a buffer retrieved by KphQueryRegistryBinary.
*
* \param[in] String String to free.
*/
_IRQL_requires_max_(APC_LEVEL)
VOID KphFreeRegistryBinary(
_In_freesMem_ PBYTE Buffer
)
{
KPH_PAGED_CODE();
KphFree(CONTAINING_RECORD(Buffer, KEY_VALUE_PARTIAL_INFORMATION, Data),
KPH_TAG_REG_BINARY);
}
/**
* \brief Queries the registry key for a string value.
*
* \param[in] KeyHandle Handle to key to query.
* \param[in] ValueName Name of value to query.
* \param[out] Value Registry unsigned long.
*
* \return Successful or errant status.
*/
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KphQueryRegistryULong(
_In_ HANDLE KeyHandle,
_In_ PCUNICODE_STRING ValueName,
_Out_ PULONG Value
)
{
NTSTATUS status;
BYTE buffer[sizeof(KEY_VALUE_PARTIAL_INFORMATION) + sizeof(ULONG64)];
ULONG resultLength;
PKEY_VALUE_PARTIAL_INFORMATION info;
KPH_PAGED_CODE_PASSIVE();
*Value = 0;
status = ZwQueryValueKey(KeyHandle,
(PUNICODE_STRING)ValueName,
KeyValuePartialInformation,
buffer,
ARRAYSIZE(buffer),
&resultLength);
if (!NT_SUCCESS(status))
{
return status;
}
if (resultLength < sizeof(KEY_VALUE_PARTIAL_INFORMATION))
{
return STATUS_INFO_LENGTH_MISMATCH;
}
info = (PKEY_VALUE_PARTIAL_INFORMATION)buffer;
#pragma prefast(suppress: 28199) // possibly uninitialized
if (info->Type != REG_DWORD)
{
return STATUS_OBJECT_TYPE_MISMATCH;
}
if (info->DataLength != sizeof(ULONG))
{
return STATUS_INFO_LENGTH_MISMATCH;
}
*Value = *(PULONG)info->Data;
return STATUS_SUCCESS;
}
/**
* \brief Maps view into the system address space. The caller should unmap the
* view by calling KphUnmapViewInSystem.
*
* \param[in] FileHandle Handle to file to map.
* \param[in] Flags Options for the mapping (see: KPH_MAP..).
* \param[out] MappedBase Base address of the mapping.
* \param[out] ViewSize Size of the mapped view. If not an image mapping and
* if the view exceeds the file size, it is clamped to the file size.
*
* \return Successful or errant status.
*/
_IRQL_always_function_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KphMapViewInSystem(
_In_ HANDLE FileHandle,
_In_ ULONG Flags,
_Outptr_result_bytebuffer_(*ViewSize) PVOID *MappedBase,
_Inout_ PSIZE_T ViewSize
)
{
NTSTATUS status;
HANDLE sectionHandle;
PVOID sectionObject;
OBJECT_ATTRIBUTES objectAttributes;
ULONG allocationAttributes;
SIZE_T fileSize;
LARGE_INTEGER sectionOffset;
KPH_PAGED_CODE_PASSIVE();
sectionHandle = NULL;
sectionObject = NULL;
*MappedBase = NULL;
if (BooleanFlagOn(Flags, KPH_MAP_IMAGE))
{
allocationAttributes = SEC_IMAGE_NO_EXECUTE;
fileSize = SIZE_T_MAX;
}
else
{
IO_STATUS_BLOCK ioStatusBlock;
FILE_STANDARD_INFORMATION fileInfo;
allocationAttributes = SEC_COMMIT;
status = ZwQueryInformationFile(FileHandle,
&ioStatusBlock,
&fileInfo,
sizeof(FILE_STANDARD_INFORMATION),
FileStandardInformation);
if (!NT_SUCCESS(status))
{
goto Exit;
}
if (fileInfo.EndOfFile.QuadPart < 0)
{
status = STATUS_FILE_TOO_LARGE;
goto Exit;
}
#ifdef _WIN64
fileSize = (SIZE_T)fileInfo.EndOfFile.QuadPart;
#else
if (fileInfo.EndOfFile.HighPart != 0)
{
status = STATUS_FILE_TOO_LARGE;
goto Exit;
}
fileSize = fileInfo.EndOfFile.LowPart;
#endif
}
InitializeObjectAttributes(&objectAttributes,
NULL,
OBJ_KERNEL_HANDLE,
NULL,
NULL);
status = ZwCreateSection(§ionHandle,
SECTION_MAP_READ,
&objectAttributes,
NULL,
PAGE_READONLY,
allocationAttributes,
FileHandle);
if (!NT_SUCCESS(status))
{
sectionHandle = NULL;
goto Exit;
}
status = ObReferenceObjectByHandle(sectionHandle,
SECTION_MAP_READ,
*MmSectionObjectType,
KernelMode,
§ionObject,
NULL);
if (!NT_SUCCESS(status))
{
sectionObject = NULL;
goto Exit;
}
sectionOffset.QuadPart = 0;
status = MmMapViewInSystemSpaceEx(sectionObject,
MappedBase,
ViewSize,
§ionOffset,
MM_SYSTEM_VIEW_EXCEPTIONS_FOR_INPAGE_ERRORS);
if (!NT_SUCCESS(status))
{
*MappedBase = NULL;
*ViewSize = 0;
goto Exit;
}
if (*ViewSize > fileSize)
{
*ViewSize = fileSize;
}
status = STATUS_SUCCESS;
Exit:
if (sectionObject)
{
ObDereferenceObject(sectionObject);
}
if (sectionHandle)
{
ObCloseHandle(sectionHandle, KernelMode);
}
return status;
}
/**
* \brief Unmaps view from the system process address space.
*
* \param[in] MappedBase Base address of the mapping.
*/
_IRQL_always_function_max_(PASSIVE_LEVEL)
VOID KphUnmapViewInSystem(
_In_ PVOID MappedBase
)
{
KPH_PAGED_CODE_PASSIVE();
MmUnmapViewInSystemSpace(MappedBase);
}
/**
* \brief Gets the file name from a file object.
*
* \param[in] FileObject The file object to get the name from.
* \param[out] FileName Set to a point to the file name on success, caller
* should free this using KphFreeFileNameObject.
*
* \return Successful or errant status.
*/
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KphGetNameFileObject(
_In_ PFILE_OBJECT FileObject,
_Outptr_allocatesMem_ PUNICODE_STRING* FileName
)
{
NTSTATUS status;
ULONG returnLength;
POBJECT_NAME_INFORMATION nameInfo;
KPH_PAGED_CODE_PASSIVE();
*FileName = NULL;
returnLength = (sizeof(OBJECT_NAME_INFORMATION) + MAX_PATH);
nameInfo = KphAllocatePaged(returnLength, KPH_TAG_FILE_OBJECT_NAME);
if (!nameInfo)
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
UTIL,
"Failed to allocate for file object name.");
status = STATUS_INSUFFICIENT_RESOURCES;
goto Exit;
}
status = KphQueryNameFileObject(FileObject,
nameInfo,
returnLength,
&returnLength);
if (status == STATUS_BUFFER_TOO_SMALL)
{
KphFree(nameInfo, KPH_TAG_FILE_OBJECT_NAME);
nameInfo = KphAllocatePaged(returnLength, KPH_TAG_FILE_OBJECT_NAME);
if (!nameInfo)
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
UTIL,
"Failed to allocate for file object name.");
status = STATUS_INSUFFICIENT_RESOURCES;
goto Exit;
}
status = KphQueryNameFileObject(FileObject,
nameInfo,
returnLength,
&returnLength);
}
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
UTIL,
"KphQueryNameFileObject failed: %!STATUS!",
status);
goto Exit;
}
*FileName = &nameInfo->Name;
nameInfo = NULL;
status = STATUS_SUCCESS;
Exit:
if (nameInfo)
{
KphFree(nameInfo, KPH_TAG_FILE_OBJECT_NAME);
}
return status;
}
/**
* \brief Frees a file name previously retrieved by KphGetFileNameObject.
*
* \param[out] FileName The file name to free.
*/
_IRQL_requires_max_(APC_LEVEL)
VOID KphFreeNameFileObject(
_In_freesMem_ PUNICODE_STRING FileName
)
{
KPH_PAGED_CODE();
KphFree(CONTAINING_RECORD(FileName, OBJECT_NAME_INFORMATION, Name),
KPH_TAG_FILE_OBJECT_NAME);
}
/**
* \brief Perform a single privilege check on the supplied subject context.
*
* \param[in] PrivilegeValue The privilege value to check.
* \param[in] SubjectSecurityContext The subject context to check.
* \param[in] AccessMode The access mode used for the access check.
*
* \return TRUE if the subject has the desired privilege, FALSE otherwise.
*/
_IRQL_requires_max_(APC_LEVEL)
BOOLEAN KphSinglePrivilegeCheckEx(
_In_ LUID PrivilegeValue,
_In_ PSECURITY_SUBJECT_CONTEXT SubjectSecurityContext,
_In_ KPROCESSOR_MODE AccessMode
)
{
PRIVILEGE_SET requiredPrivileges;
KPH_PAGED_CODE();
requiredPrivileges.PrivilegeCount = 1;
requiredPrivileges.Control = PRIVILEGE_SET_ALL_NECESSARY;
requiredPrivileges.Privilege[0].Luid = PrivilegeValue;
requiredPrivileges.Privilege[0].Attributes = 0;
return SePrivilegeCheck(&requiredPrivileges,
SubjectSecurityContext,
AccessMode);
}
/**
* \brief Perform a single privilege check on the current subject context.
*
* \param[in] PrivilegeValue The privilege value to check.
* \param[in] AccessMode The access mode used for the access check.
*
* \return TRUE if the subject has the desired privilege, FALSE otherwise.
*/
_IRQL_requires_max_(APC_LEVEL)
BOOLEAN KphSinglePrivilegeCheck(
_In_ LUID PrivilegeValue,
_In_ KPROCESSOR_MODE AccessMode
)
{
BOOLEAN accessGranted;
SECURITY_SUBJECT_CONTEXT subjectContext;
KPH_PAGED_CODE();
SeCaptureSubjectContext(&subjectContext);
accessGranted = KphSinglePrivilegeCheckEx(PrivilegeValue,
&subjectContext,
AccessMode);
SeReleaseSubjectContext(&subjectContext);
return accessGranted;
}
/**
* \brief Retrieves the kernel file name.
*
* \param[out] FileName On success set to the file name for the kernel. Must be
* freed using RtlFreeUnicodeString.
*
* \return Successful or errant status.
*/
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KphpGetKernelFileName(
_Out_allocatesMem_ PUNICODE_STRING FileName
)
{
NTSTATUS status;
SYSTEM_SINGLE_MODULE_INFORMATION info;
ANSI_STRING fullPathName;
KPH_PAGED_CODE_PASSIVE();
RtlZeroMemory(FileName, sizeof(UNICODE_STRING));
RtlZeroMemory(&info, sizeof(SYSTEM_SINGLE_MODULE_INFORMATION));
info.TargetModuleAddress = (PVOID)ObCloseHandle;
status = ZwQuerySystemInformation(SystemSingleModuleInformation,
&info,
sizeof(SYSTEM_SINGLE_MODULE_INFORMATION),
NULL);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
UTIL,
"ZwQuerySystemInformation failed: %!STATUS!",
status);
return status;
}
status = RtlInitAnsiStringEx(&fullPathName,
(PCSZ)info.ExInfo.BaseInfo.FullPathName);
if (!NT_SUCCESS(status))
{
return status;
}
return RtlAnsiStringToUnicodeString(FileName, &fullPathName, TRUE);
}
/**
* \brief Retrieves the version of the kernel.
*
* \param[out] Version Set to the kernel build version.
*
* \return Successful or errant status.
*/
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KphGetKernelVersion(
_Out_ PKPH_FILE_VERSION Version
)
{
NTSTATUS status;
UNICODE_STRING kernelFileName;
KPH_PAGED_CODE_PASSIVE();
status = KphpGetKernelFileName(&kernelFileName);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
UTIL,
"KphGetKernelFileName failed: %!STATUS!",
status);
RtlZeroMemory(Version, sizeof(KPH_FILE_VERSION));
goto Exit;
}
KphTracePrint(TRACE_LEVEL_INFORMATION,
UTIL,
"Kernel file name: \"%wZ\"",
&kernelFileName);
status = KphGetFileVersion(&kernelFileName, Version);
Exit:
RtlFreeUnicodeString(&kernelFileName);
return status;
}
/**
* \brief Retrieves the file version from a mapped file.
*
* \param[in] ImageBase The base address of the mapped file.
* \param[in] ViewSize The size of the mapped file.
* \param[in] ResourceLanguage The language of the resource to locate.
* \param[out] Version Set to the file version.
*
* \return Successful or errant status.
*/
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KphpParseFileVersion(
_In_ PVOID ImageBase,
_In_ SIZE_T ViewSize,
_In_ ULONG_PTR ResourceLanguage,
_Out_ PKPH_FILE_VERSION Version
)
{
NTSTATUS status;
LDR_RESOURCE_INFO resourceInfo;
PIMAGE_RESOURCE_DATA_ENTRY resourceData;
PVOID resourceBuffer;
ULONG resourceLength;
PVOID imageEnd;
PVS_VERSION_INFO_STRUCT versionInfo;
UNICODE_STRING keyName;
PVS_FIXEDFILEINFO fileInfo;
KPH_PAGED_CODE_PASSIVE();
RtlZeroMemory(Version, sizeof(KPH_FILE_VERSION));
resourceInfo.Type = (ULONG_PTR)VS_FILE_INFO;
resourceInfo.Name = (ULONG_PTR)MAKEINTRESOURCEW(VS_VERSION_INFO);
resourceInfo.Language = ResourceLanguage;
__try
{
status = LdrFindResource_U(ImageBase,
&resourceInfo,
RESOURCE_DATA_LEVEL,
&resourceData);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
UTIL,
"LdrFindResource_U failed: %!STATUS!",
status);
goto Exit;
}
status = LdrAccessResource(ImageBase,
resourceData,
&resourceBuffer,
&resourceLength);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
UTIL,
"LdrAccessResource failed: %!STATUS!",
status);
goto Exit;
}
imageEnd = Add2Ptr(ImageBase, ViewSize);
if (Add2Ptr(resourceBuffer, resourceLength) >= imageEnd)
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
UTIL,
"Resource buffer overflows mapping");
status = STATUS_BUFFER_OVERFLOW;
goto Exit;
}
if (resourceLength < sizeof(VS_VERSION_INFO_STRUCT))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
UTIL,
"Resource length insufficient");
status = STATUS_BUFFER_TOO_SMALL;
goto Exit;
}
versionInfo = resourceBuffer;
if (Add2Ptr(resourceBuffer, versionInfo->Length) >= imageEnd)
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
UTIL,
"Version info overflows mapping");
status = STATUS_BUFFER_OVERFLOW;
goto Exit;
}
if (versionInfo->ValueLength < sizeof(VS_FIXEDFILEINFO))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
UTIL,
"Value length insufficient");
status = STATUS_BUFFER_TOO_SMALL;
goto Exit;
}
status = RtlInitUnicodeStringEx(&keyName, versionInfo->Key);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
UTIL,
"RtlInitUnicodeStringEx failed: %!STATUS!",
status);
goto Exit;
}
fileInfo = Add2Ptr(versionInfo, RTL_SIZEOF_THROUGH_FIELD(VS_VERSION_INFO_STRUCT, Type));
fileInfo = (PVS_FIXEDFILEINFO)ALIGN_UP(Add2Ptr(fileInfo, keyName.MaximumLength), ULONG);
if (Add2Ptr(fileInfo, sizeof(VS_FIXEDFILEINFO)) >= imageEnd)
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
UTIL,
"File version info overflows mapping");
status = STATUS_BUFFER_TOO_SMALL;
goto Exit;
}
if (fileInfo->dwSignature != VS_FFI_SIGNATURE)
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
UTIL,
"Invalid file version information signature (0x%08x)",
fileInfo->dwSignature);
status = STATUS_INVALID_SIGNATURE;
goto Exit;
}
if (fileInfo->dwStrucVersion != 0x10000)
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
UTIL,
"Unknown file version information structure (0x%08x)",
fileInfo->dwStrucVersion);
status = STATUS_REVISION_MISMATCH;
goto Exit;
}
Version->MajorVersion = HIWORD(fileInfo->dwFileVersionMS);
Version->MinorVersion = LOWORD(fileInfo->dwFileVersionMS);
Version->BuildNumber = HIWORD(fileInfo->dwFileVersionLS);
Version->Revision = LOWORD(fileInfo->dwFileVersionLS);
status = STATUS_SUCCESS;
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
RtlZeroMemory(Version, sizeof(KPH_FILE_VERSION));
status = GetExceptionCode();
}
Exit:
return status;
}
/**
* \brief Retrieves the file version from a file.
*
* \param[in] FileName The name of the file to get the version from.
* \param[out] Version Set to the file version.
*
* \return Successful or errant status.
*/
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KphGetFileVersion(
_In_ PCUNICODE_STRING FileName,
_Out_ PKPH_FILE_VERSION Version
)
{
NTSTATUS status;
OBJECT_ATTRIBUTES objectAttributes;
HANDLE fileHandle;
IO_STATUS_BLOCK ioStatusBlock;
PVOID imageBase;
SIZE_T imageSize;
KPH_PAGED_CODE_PASSIVE();
RtlZeroMemory(Version, sizeof(KPH_FILE_VERSION));
imageBase = NULL;
fileHandle = NULL;
InitializeObjectAttributes(&objectAttributes,
(PUNICODE_STRING)FileName,
OBJ_KERNEL_HANDLE,
NULL,
NULL);
status = KphCreateFile(&fileHandle,
FILE_READ_ACCESS | SYNCHRONIZE,
&objectAttributes,
&ioStatusBlock,
NULL,
FILE_ATTRIBUTE_NORMAL,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
FILE_OPEN,
FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT,
NULL,
0,
IO_IGNORE_SHARE_ACCESS_CHECK,
KernelMode);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
UTIL,
"KphCreateFile failed: %!STATUS!",
status);
fileHandle = NULL;
goto Exit;
}
imageSize = 0;
status = KphMapViewInSystem(fileHandle,
KPH_MAP_IMAGE,
&imageBase,
&imageSize);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
UTIL,
"KphMapViewInSystem failed: %!STATUS!",
status);
imageBase = NULL;
goto Exit;
}
status = KphpParseFileVersion(imageBase,
imageSize,
MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US),
Version);
if (NT_SUCCESS(status))
{
goto Exit;
}
KphTracePrint(TRACE_LEVEL_VERBOSE,
UTIL,
"KphpParseFileVersion failed: %!STATUS!",
status);
//
// Try again with a neutral sub language.
//
status = KphpParseFileVersion(imageBase,
imageSize,
MAKELANGID(LANG_ENGLISH, SUBLANG_NEUTRAL),
Version);
if (NT_SUCCESS(status))
{
goto Exit;
}
KphTracePrint(TRACE_LEVEL_VERBOSE,
UTIL,
"KphpParseFileVersion failed: %!STATUS!",
status);
//
// Try again with neutral language and sub language.
//
status = KphpParseFileVersion(imageBase,
imageSize,
MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL),
Version);
if (NT_SUCCESS(status))
{
goto Exit;
}
KphTracePrint(TRACE_LEVEL_VERBOSE,
UTIL,
"KphpParseFileVersion failed: %!STATUS!",
status);
Exit:
if (imageBase)
{
KphUnmapViewInSystem(imageBase);
}
if (fileHandle)
{
ObCloseHandle(fileHandle, KernelMode);
}
return status;
}
/**
* \brief Sets CFG call target information for a process.
*
* \param[in] ProcessHandle Handle to the process to configure CFG for.
* \param[in] VirtualAddress Virtual address to configure CFG for.
* \param[in] Flags CFG call target flags (CFG_CALL_TARGET_).
*
* \return Successful or errant status.
*/
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KphpSetCfgCallTargetInformation(
_In_ HANDLE ProcessHandle,
_In_ PVOID VirtualAddress,
_In_ ULONG Flags
)
{
MEMORY_RANGE_ENTRY memoryRange;
CFG_CALL_TARGET_INFO targetInfo;
CFG_CALL_TARGET_LIST_INFORMATION targetListInfo;
ULONG numberOfEntriesProcessed;
KPH_PAGED_CODE_PASSIVE();
#ifdef _WIN64
C_ASSERT(sizeof(CFG_CALL_TARGET_INFO) == 16);
C_ASSERT(sizeof(CFG_CALL_TARGET_LIST_INFORMATION) == 40);
#endif
memoryRange.VirtualAddress = PAGE_ALIGN(VirtualAddress);
memoryRange.NumberOfBytes = PAGE_SIZE;
targetInfo.Offset = BYTE_OFFSET(VirtualAddress);
targetInfo.Flags = Flags;
numberOfEntriesProcessed = 0;
RtlZeroMemory(&targetListInfo, sizeof(CFG_CALL_TARGET_LIST_INFORMATION));
targetListInfo.NumberOfEntries = 1;
targetListInfo.NumberOfEntriesProcessed = &numberOfEntriesProcessed;
targetListInfo.CallTargetInfo = &targetInfo;
return ZwSetInformationVirtualMemory(ProcessHandle,
VmCfgCallTargetInformation,
1,
&memoryRange,
&targetListInfo,
sizeof(CFG_CALL_TARGET_LIST_INFORMATION));
}
/**
* \brief Grants a suppressed call access to a target.
*
* \param[in] ProcessHandle Handle to the process to configure CFG for.
* \param[in] VirtualAddress Virtual address of the target.
*
* \return Successful or errant status.
*/
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KphGuardGrantSuppressedCallAccess(
_In_ HANDLE ProcessHandle,
_In_ PVOID VirtualAddress
)
{
ULONG flags;
KPH_PAGED_CODE_PASSIVE();
flags = CFG_CALL_TARGET_CONVERT_EXPORT_SUPPRESSED_TO_VALID;
return KphpSetCfgCallTargetInformation(ProcessHandle,
VirtualAddress,
flags);
}
/**
* \brief Disables XFG on a target.
*
* \param[in] ProcessHandle Handle to the process to configure CFG for.
* \param[in] VirtualAddress Virtual address of the target.
*
* \return Successful or errant status.
*/
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KphDisableXfgOnTarget(
_In_ HANDLE ProcessHandle,
_In_ PVOID VirtualAddress
)
{
ULONG flags;
KPH_PAGED_CODE_PASSIVE();
flags = CFG_CALL_TARGET_CONVERT_XFG_TO_CFG;
return KphpSetCfgCallTargetInformation(ProcessHandle,
VirtualAddress,
flags);
}
/**
* \brief Gets the final component of a file name.
*
* \details The final component is the last part of the file name, e.g. for
* "\SystemRoot\System32\ntoskrnl.exe" it would be "ntoskrnl.exe". Returns an
* error if the final component is not found.
*
* \param[in] FileName File name to get the final component of.
* \param[out] FinalComponent Final component of the file name, references
* input string, this string should not be freed and the input string must
* remain valid as long as this is in use.
*
* \return Successful or errant status.
*/
_IRQL_requires_max_(APC_LEVEL)
_Must_inspect_result_
NTSTATUS KphGetFileNameFinalComponent(
_In_ PCUNICODE_STRING FileName,
_Out_ PUNICODE_STRING FinalComponent
)
{
KPH_PAGED_CODE();
for (USHORT i = (FileName->Length / sizeof(WCHAR)); i > 0; i--)
{
if (FileName->Buffer[i - 1] == OBJ_NAME_PATH_SEPARATOR)
{
FinalComponent->Buffer = &FileName->Buffer[i];
FinalComponent->Length = FileName->Length - (i * sizeof(WCHAR));
FinalComponent->MaximumLength = FinalComponent->Length;
return STATUS_SUCCESS;
}
}
RtlZeroMemory(FinalComponent, sizeof(*FinalComponent));
return STATUS_NOT_FOUND;
}
/**
* \brief Gets the image name from a process.
*
* \param[in] Process The process to get the image name from.
* \param[out] ImageName Populated with the image name of the process, this
* string should be freed using KphFreeProcessImageName.
*
* \return Successful or errant status.
*/
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KphGetProcessImageName(
_In_ PEPROCESS Process,
_Out_allocatesMem_ PUNICODE_STRING ImageName
)
{
NTSTATUS status;
PUCHAR fileName;
SIZE_T len;
KPH_PAGED_CODE_PASSIVE();
fileName = PsGetProcessImageFileName(Process);
status = RtlStringCbLengthA((STRSAFE_PCNZCH)fileName, 15, &len);
if (NT_SUCCESS(status))
{
ANSI_STRING string;
string.Buffer = (PCHAR)fileName;
string.Length = (USHORT)len;
string.MaximumLength = string.Length;
status = RtlAnsiStringToUnicodeString(ImageName, &string, TRUE);
if (NT_SUCCESS(status))
{
return status;
}
}
RtlZeroMemory(ImageName, sizeof(*ImageName));
return status;
}
/**
* \brief Frees a process image file name from KphGetProcessImageName.
*
* \param[in] ImageName The image name to free.
*/
_IRQL_requires_max_(APC_LEVEL)
VOID KphFreeProcessImageName(
_In_freesMem_ PUNICODE_STRING ImageName
)
{
KPH_PAGED_CODE();
#pragma prefast(suppress : 28121) // SAL is incorrect in wdm.h
RtlFreeUnicodeString(ImageName);
}
/**
* \brief Opens the driver parameters key.
*
* \param[in] RegistryPath Registry path from the entry point.
* \param[out] KeyHandle Handle to parameters key on success, null on failure.
*
* \return Successful or errant status.
*/
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KphOpenParametersKey(
_In_ PCUNICODE_STRING RegistryPath,
_Out_ PHANDLE KeyHandle
)
{
NTSTATUS status;
WCHAR buffer[MAX_PATH];
UNICODE_STRING parametersKeyName;
OBJECT_ATTRIBUTES objectAttributes;
KPH_PAGED_CODE_PASSIVE();
*KeyHandle = NULL;
parametersKeyName.Buffer = buffer;
parametersKeyName.Length = 0;
parametersKeyName.MaximumLength = sizeof(buffer);
status = RtlAppendUnicodeStringToString(¶metersKeyName, RegistryPath);
if (!NT_SUCCESS(status))
{
return status;
}
status = RtlAppendUnicodeToString(¶metersKeyName, L"\\Parameters");
if (!NT_SUCCESS(status))
{
return status;
}
InitializeObjectAttributes(&objectAttributes,
¶metersKeyName,
OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
NULL,
NULL);
status = ZwOpenKey(KeyHandle, KEY_READ, &objectAttributes);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
UTIL,
"Unable to open Parameters key: %!STATUS!",
status);
*KeyHandle = NULL;
return status;
}
return STATUS_SUCCESS;
}
/**
* \brief Performs a domination check between a calling process and a target
* process.
*
* \details A process dominates the other when the protected level of the
* process exceeds the other. This domination check is not ideal, it is overly
* strict and lacks enough information from the kernel to fully understand the
* protected process state.
*
* \param[in] Process The calling process.
* \param[in] ProcessTarget Target process to check against the caller.
* \param[in] AccessMode Access mode of the request.
*
* \return Appropriate status:
* STATUS_SUCCESS The calling process dominates the target.
* STATUS_ACCESS_DENIED The calling process does not dominate the target.
*/
_IRQL_requires_max_(APC_LEVEL)
_Must_inspect_result_
NTSTATUS KphDominationCheck(
_In_ PEPROCESS Process,
_In_ PEPROCESS ProcessTarget,
_In_ KPROCESSOR_MODE AccessMode
)
{
PS_PROTECTION processProtection;
PS_PROTECTION targetProtection;
KPH_PAGED_CODE();
if (AccessMode == KernelMode)
{
//
// Give the kernel what it wants...
//
return STATUS_SUCCESS;
}
//
// Until Microsoft gives us more insight into protected process domination
// we'll do a very strict check here:
//
processProtection = PsGetProcessProtection(Process);
targetProtection = PsGetProcessProtection(ProcessTarget);
if ((targetProtection.Type != PsProtectedTypeNone) &&
(targetProtection.Type >= processProtection.Type))
{
//
// Calling process protection does not dominate the other, deny access.
// We could do our own domination check/mapping here with the signing
// level, but it won't be great and Microsoft might change it, so we'll
// do this strict check until Microsoft exports:
// PsTestProtectedProcessIncompatibility
// RtlProtectedAccess/RtlTestProtectedAccess
//
return STATUS_ACCESS_DENIED;
}
return STATUS_SUCCESS;
}
/**
* \brief Performs a domination and privilege check to verify that the calling
* thread has the required privilege to perform the action.
*
* \details This function replaces a call to KphDominationCheck in certain
* paths. It grants access to a protected process *only* if the calling thread
* or associated process has been granted permission to do so. Session tokens
* implement an expiring token validated using asymmetric keys. This involves
* verification coordinated between the driver, client, and server which
* requires end user authentication and may be audited or revoked at any time.
* This feature is a service similar to those provided by various security
* or system management focused products. System Informer provides this service
* to users in this same light. System Informer provides security and system
* management capabilities to users.
*
* \param[in] Privileges The specific privileges to be checked.
* \param[in] Thread The calling thread.
* \param[in] ProcessTarget Target process to check against the caller.
* \param[in] AccessMode Describes the access mode of the request.
*
* \return Appropriate status:
* STATUS_SUCCESS The calling thread or process is granted access.
* STATUS_ACCESS_DENIED The calling process does not dominate the target.
*/
_IRQL_requires_max_(APC_LEVEL)
_Must_inspect_result_
NTSTATUS KphDominationAndPrivilegeCheck(
_In_ ULONG Privileges,
_In_ PETHREAD Thread,
_In_ PEPROCESS ProcessTarget,
_In_ KPROCESSOR_MODE AccessMode
)
{
BOOLEAN granted;
PKPH_THREAD_CONTEXT thread;
KPH_PAGED_CODE();
if (AccessMode == KernelMode)
{
return STATUS_SUCCESS;
}
granted = FALSE;
thread = KphGetEThreadContext(Thread);
if (thread)
{
granted = KphSessionTokenPrivilegeCheck(thread, Privileges);
KphDereferenceObject(thread);
}
if (granted)
{
return STATUS_SUCCESS;
}
return KphDominationCheck(PsGetThreadProcess(Thread),
ProcessTarget,
AccessMode);
}
/**
* \brief Retrieves the signing level of a file object.
*
* \param[in] FileObject The file object to query.
* \param[out] SigningLevel Receives the signing level of the file object.
*
* \return Successful or errant status.
*/
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KphGetSigningLevel(
_In_ PFILE_OBJECT FileObject,
_Out_ PSE_SIGNING_LEVEL SigningLevel
)
{
NTSTATUS status;
ULONG flags;
MINCRYPT_POLICY_INFO policyInfo;
MINCRYPT_POLICY_INFO timeStampPolicyInfo;
LARGE_INTEGER signingTime;
UCHAR thumbprint[MINCRYPT_MAX_HASH_LEN];
ULONG thumbprintSize;
ULONG thumbprintAlgorithm;
ANSI_STRING issuer;
ANSI_STRING subject;
BOOLEAN microsoftSigned;
KPH_PAGED_CODE_PASSIVE();
status = SeGetCachedSigningLevel(FileObject,
&flags,
SigningLevel,
NULL,
NULL,
NULL);
if (NT_SUCCESS(status) && (*SigningLevel != SE_SIGNING_LEVEL_UNCHECKED))
{
return status;
}
//
// Invoke CI to update the cache.
//
if (!KphDynCiValidateFileObject || !KphDynCiFreePolicyInfo)
{
return STATUS_NOINTERFACE;
}
RtlZeroMemory(&policyInfo, sizeof(MINCRYPT_POLICY_INFO));
RtlZeroMemory(&timeStampPolicyInfo, sizeof(MINCRYPT_POLICY_INFO));
thumbprintSize = sizeof(thumbprint);
thumbprintAlgorithm = 0;
status = KphDynCiValidateFileObject(FileObject,
CI_POLICY_ACCEPT_ANY_ROOT_CERTIFICATE,
SE_SIGNING_LEVEL_UNCHECKED,
&policyInfo,
&timeStampPolicyInfo,
&signingTime,
thumbprint,
&thumbprintSize,
&thumbprintAlgorithm);
if (policyInfo.ChainInfo &&
RTL_CONTAINS_FIELD(policyInfo.ChainInfo, policyInfo.ChainInfo->Size, ChainElements) &&
policyInfo.ChainInfo->ChainElements &&
policyInfo.ChainInfo->NumberOfChainElements)
{
issuer.Buffer = policyInfo.ChainInfo->ChainElements[0].Issuer.Buffer;
issuer.Length = policyInfo.ChainInfo->ChainElements[0].Issuer.Length;
issuer.MaximumLength = issuer.Length;
subject.Buffer = policyInfo.ChainInfo->ChainElements[0].Subject.Buffer;
subject.Length = policyInfo.ChainInfo->ChainElements[0].Subject.Length;
subject.MaximumLength = subject.Length;
}
else
{
RtlZeroMemory(&issuer, sizeof(ANSI_STRING));
RtlZeroMemory(&subject, sizeof(ANSI_STRING));
}
if (!NT_SUCCESS(status) ||
!NT_SUCCESS(policyInfo.VerificationStatus) ||
BooleanFlagOn(policyInfo.PolicyBits,
(MINCRYPT_POLICY_ERROR_FLAGS |
MINCRYPT_POLICY_3RD_PARTY_ROOT |
MINCRYPT_POLICY_NO_ROOT |
MINCRYPT_POLICY_OTHER_ROOT)))
{
microsoftSigned = FALSE;
}
else
{
microsoftSigned = TRUE;
}
KphTracePrint(TRACE_LEVEL_VERBOSE,
UTIL,
"CiValidateFileObject: \"%wZ\" 0x%08lx \"%Z\" \"%Z\" "
"%!STATUS! %!STATUS!",
&FileObject->FileName,
policyInfo.PolicyBits,
&subject,
&issuer,
policyInfo.VerificationStatus,
status);
KphDynCiFreePolicyInfo(&policyInfo);
KphDynCiFreePolicyInfo(&timeStampPolicyInfo);
status = SeGetCachedSigningLevel(FileObject,
&flags,
SigningLevel,
NULL,
NULL,
NULL);
if (NT_SUCCESS(status))
{
return status;
}
//
// Caching of the signing level failed. If CI tells us this is Microsoft
// signed then we'll return that, otherwise return the errant status.
//
if (microsoftSigned)
{
*SigningLevel = SE_SIGNING_LEVEL_MICROSOFT;
status = STATUS_SUCCESS;
}
else
{
*SigningLevel = SE_SIGNING_LEVEL_UNCHECKED;
}
return status;
}
/**
* \brief Retrieves image headers from a mapped image.
*
* \details Extends RtlImageNtHeaderEx and verifies that the optional headers
* are within the region. The caller should still check that the optional
* headers contains a given field with RTL_CONTAINS_FIELD.
*
* \param[in] Base The base address of the image.
* \param[in] Size The size of the image.
* \param[out] Headers Receives the image headers.
*
* \return Successful or errant status.
*/
_IRQL_requires_max_(APC_LEVEL)
_Must_inspect_result_
NTSTATUS KphImageNtHeader(
_In_ PVOID Base,
_In_ ULONG64 Size,
_Out_ PKPH_IMAGE_NT_HEADERS Headers
)
{
NTSTATUS status;
ULONG64 size;
KPH_PAGED_CODE();
status = RtlImageNtHeaderEx(0, Base, Size, &Headers->Headers);
if (!NT_SUCCESS(status))
{
Headers->Headers = NULL;
return status;
}
size = PtrOffset(Base, Headers->Headers);
size += RTL_SIZEOF_THROUGH_FIELD(IMAGE_NT_HEADERS, FileHeader);
size += Headers->Headers->FileHeader.SizeOfOptionalHeader;
if (size > Size)
{
Headers->Headers = NULL;
return STATUS_INVALID_IMAGE_FORMAT;
}
return STATUS_SUCCESS;
}
================================================
FILE: KSystemInformer/verify.c
================================================
/*
* Copyright (c) 2022 Winsider Seminars & Solutions, Inc. All rights reserved.
*
* This file is part of System Informer.
*
* Authors:
*
* wj32 2016
* jxy-s 2020-2026
*
*/
#include
#include
#define KPH_KEY_MATERIAL_SIZE ((ULONG)0x21B)
#define KPH_KEY_ALG_HANDLE BCRYPT_RSA_ALG_HANDLE
#define KPH_KEY_ALGORITHM BCRYPT_RSA_ALGORITHM
#define KPH_KEY_HASH_ALGORITHM BCRYPT_SHA512_ALGORITHM
#define KPH_KEY_HASH_ALGORITHM_BYTES (512 / 8)
#define KPH_KEY_BLOB_PUBLIC BCRYPT_RSAPUBLIC_BLOB
#define KPH_KEY_PADDING_FLAGS BCRYPT_PAD_PSS
#define KPH_KEY_PADDING_INFO (PVOID)&KphpKeyPaddingInfo
#define KPH_VERIFY_SIGNATURE_MAX_LENGTH 1024
typedef enum _KPH_KEY_TYPE
{
KphKeyTypeTest,
KphKeyTypeProd,
} KPH_KEY_TYPE, *PKPH_KEY_TYPE;
typedef struct _KPH_KEY
{
KPH_KEY_TYPE Type;
BYTE Material[KPH_KEY_MATERIAL_SIZE];
} KPH_KEY, *PKPH_KEY;
typedef const KPH_KEY* PCKPH_KEY;
KPH_PROTECTED_DATA_SECTION_RO_PUSH();
static const KPH_KEY KphpPublicKeys[] =
{
{
KphKeyTypeProd, // kph
{
0x52, 0x53, 0x41, 0x31, 0x00, 0x10, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x01, 0x00, 0x01, 0xE5, 0xF2, 0xEF, 0xE0, 0xE0, 0x02, 0x05, 0xEA, 0x55,
0xF8, 0x4C, 0x26, 0xED, 0xF9, 0x55, 0xE3, 0xBD, 0x38, 0x06, 0x5D, 0xF6,
0x26, 0xA4, 0xFF, 0x69, 0xFC, 0x4D, 0x8C, 0x02, 0x7E, 0x82, 0x96, 0x3D,
0x90, 0xE8, 0xB7, 0xEB, 0x1A, 0xE3, 0x02, 0x57, 0x0F, 0x1C, 0xE9, 0x93,
0xD0, 0x61, 0xFC, 0x75, 0xF1, 0xF3, 0xC6, 0x49, 0xF9, 0xBC, 0x39, 0x6F,
0x80, 0x40, 0xC3, 0xF9, 0xB3, 0xDC, 0xFD, 0x21, 0x28, 0x25, 0x1B, 0x91,
0x42, 0xB0, 0x29, 0xA4, 0x76, 0x4D, 0x71, 0x4D, 0x38, 0x3C, 0x9F, 0x80,
0xD9, 0x11, 0xFD, 0xD5, 0x80, 0xB1, 0xFC, 0xB9, 0xC8, 0xCB, 0xE4, 0x6C,
0x1A, 0x11, 0x8E, 0xE4, 0x9D, 0x3C, 0x24, 0x58, 0x70, 0x64, 0x88, 0x08,
0x53, 0x7F, 0x8A, 0x30, 0x67, 0x59, 0x91, 0x2E, 0x77, 0xD5, 0x1F, 0x21,
0x9C, 0x8E, 0x7B, 0x5E, 0xC0, 0x3E, 0x52, 0x26, 0x69, 0x0F, 0x95, 0x14,
0x72, 0xE5, 0xE9, 0xA2, 0xF7, 0xFC, 0x22, 0x43, 0xB5, 0x1A, 0xB1, 0xDB,
0x01, 0xC4, 0x6A, 0x13, 0x99, 0xBE, 0x0F, 0x82, 0x59, 0xC8, 0x8F, 0xCA,
0xF2, 0x86, 0x54, 0x34, 0xF5, 0x4D, 0xD9, 0xD2, 0xA7, 0x4A, 0xDF, 0x94,
0x9E, 0x13, 0x53, 0xEA, 0xD0, 0x98, 0xB1, 0x15, 0x2D, 0x59, 0xD7, 0xFF,
0x9B, 0x9C, 0x78, 0x81, 0xDE, 0x90, 0xAF, 0x7E, 0xF4, 0x7D, 0x14, 0xAC,
0x40, 0x46, 0x13, 0x45, 0x16, 0x0A, 0x22, 0x51, 0xEC, 0x4F, 0x4F, 0xCF,
0x63, 0x0F, 0x6B, 0xF3, 0xFC, 0x7C, 0x85, 0x1C, 0x1E, 0xBB, 0xF1, 0x80,
0x34, 0x9F, 0x13, 0x57, 0xB5, 0x02, 0x37, 0xF6, 0xE5, 0x3A, 0x77, 0x90,
0x1B, 0xB7, 0x6E, 0xA9, 0xA3, 0xF1, 0x33, 0xE2, 0xC7, 0xD8, 0xFF, 0x82,
0x44, 0x0E, 0x28, 0x30, 0xD6, 0x25, 0x7D, 0x71, 0x4A, 0x68, 0x1C, 0xCD,
0x70, 0x6F, 0xB0, 0x64, 0x46, 0x0E, 0xE0, 0x1D, 0x30, 0x79, 0xE6, 0x69,
0x6D, 0x47, 0xF7, 0xEB, 0x09, 0x96, 0x30, 0x40, 0xEF, 0x5C, 0x62, 0xC0,
0x18, 0x36, 0xA4, 0x95, 0x85, 0x74, 0x91, 0x50, 0xFE, 0x6D, 0xE2, 0xD5,
0x52, 0xE4, 0x1F, 0x4A, 0x28, 0xB6, 0x9D, 0x5E, 0x34, 0xD5, 0x0C, 0x28,
0x4D, 0x49, 0xDD, 0x58, 0x40, 0x83, 0x84, 0xA4, 0x0E, 0x1D, 0xE6, 0xF5,
0xF0, 0x3B, 0x46, 0xAD, 0x2D, 0x64, 0xFA, 0xAC, 0x44, 0x95, 0x52, 0x31,
0x83, 0x43, 0x67, 0x65, 0x84, 0x74, 0xB3, 0xBD, 0x59, 0x2C, 0x13, 0x8A,
0x4B, 0xA2, 0xE2, 0x32, 0xF7, 0x42, 0xC6, 0xD6, 0x3D, 0x29, 0x39, 0x1D,
0x0F, 0xEC, 0x47, 0xA2, 0xBF, 0xED, 0x69, 0x6B, 0xEC, 0x45, 0x04, 0x32,
0x01, 0x8D, 0x50, 0x48, 0x8B, 0x9C, 0x8E, 0x1E, 0xFA, 0x11, 0x03, 0x87,
0x47, 0x23, 0x83, 0x98, 0x29, 0x2B, 0xB8, 0x76, 0xD3, 0x64, 0xE7, 0x8C,
0x22, 0xAE, 0x68, 0xA3, 0xC3, 0x58, 0xC0, 0xA0, 0xFE, 0x37, 0x90, 0x26,
0x27, 0x78, 0x4E, 0xC3, 0x63, 0x87, 0x3E, 0x20, 0xEF, 0x8A, 0xEA, 0x44,
0x1E, 0xCC, 0xAD, 0x1F, 0xFC, 0x05, 0x7B, 0x0A, 0x1B, 0x02, 0xFA, 0x1C,
0xEB, 0x5D, 0x81, 0x6B, 0x09, 0x2B, 0xDE, 0x49, 0x2A, 0xA6, 0xA1, 0x82,
0xE5, 0x08, 0xBF, 0x40, 0x6E, 0x67, 0x03, 0xAC, 0xB0, 0xDB, 0xB4, 0x9B,
0x66, 0x38, 0x78, 0x91, 0x79, 0x48, 0x54, 0xCE, 0xC5, 0x32, 0xAE, 0xAB,
0x35, 0x8B, 0x9C, 0xE9, 0x00, 0x42, 0xBE, 0x98, 0x2C, 0x00, 0x0F, 0x3C,
0xC7, 0x55, 0xA4, 0x45, 0x98, 0x7C, 0xE2, 0x5E, 0xFF, 0xC0, 0xEC, 0x97,
0x9C, 0x29, 0x5D, 0x65, 0x00, 0x68, 0x68, 0x97, 0x3B, 0x32, 0x4E, 0x39,
0x51, 0x95, 0x59, 0xA3, 0x81, 0xE5, 0xDC, 0xF0, 0xAF, 0x77, 0x34, 0x64,
0x6F, 0xD6, 0x88, 0x03, 0x23, 0xFB, 0x82, 0x62, 0x86, 0xFF, 0x59
}
},
{
KphKeyTypeTest, // kph-dev
{
0x52, 0x53, 0x41, 0x31, 0x00, 0x10, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x01, 0x00, 0x01, 0xAB, 0xDB, 0x39, 0x5E, 0xD1, 0xFA, 0x54, 0x35, 0xD7,
0x6B, 0x83, 0x62, 0x82, 0x06, 0xC7, 0x20, 0xDB, 0x05, 0x5E, 0x55, 0xF2,
0x3C, 0x6A, 0x52, 0x1F, 0x0B, 0xC6, 0xC2, 0xB4, 0x83, 0x13, 0xA9, 0xF2,
0xD6, 0xF7, 0x19, 0x99, 0xB5, 0xB3, 0x52, 0xE7, 0x54, 0x55, 0x17, 0x7F,
0xE3, 0xA6, 0x6B, 0xD6, 0xB7, 0xDA, 0x38, 0xC8, 0x44, 0xFF, 0x3F, 0x4A,
0x59, 0x0C, 0x3C, 0x45, 0xA3, 0x35, 0x8B, 0x34, 0x01, 0x82, 0xBD, 0x25,
0x9B, 0xAF, 0xFC, 0x56, 0x1F, 0x6E, 0xFE, 0xE2, 0xF5, 0xE1, 0x2D, 0xFB,
0x42, 0x41, 0x46, 0x48, 0x02, 0xEB, 0xEF, 0xEA, 0x41, 0x08, 0x8D, 0x58,
0xA5, 0x32, 0xFE, 0x8F, 0xE8, 0xBB, 0xF6, 0x36, 0xD9, 0x48, 0xC3, 0x2C,
0x30, 0x70, 0x57, 0xE0, 0x25, 0x1C, 0xA3, 0xE1, 0x72, 0x76, 0x48, 0xF9,
0x57, 0x8B, 0x50, 0x67, 0x27, 0x66, 0xD5, 0x70, 0x4F, 0x02, 0x94, 0x9F,
0xB3, 0xDE, 0x74, 0xF7, 0x7C, 0xD4, 0xEE, 0xFD, 0x95, 0x5D, 0x47, 0xF6,
0x0A, 0xDE, 0xA1, 0x76, 0x99, 0x52, 0x40, 0x0D, 0xB1, 0x63, 0x1C, 0x65,
0xA0, 0x04, 0xC8, 0x48, 0xE3, 0xC6, 0x3C, 0x6D, 0x26, 0x10, 0xB9, 0x22,
0x7D, 0x67, 0x5D, 0x81, 0x44, 0xAF, 0xE0, 0x85, 0xC3, 0xB3, 0x13, 0x27,
0xB8, 0xA3, 0xC2, 0x28, 0x2A, 0x57, 0xE2, 0xAE, 0x2D, 0xB7, 0x95, 0xC4,
0x13, 0x65, 0xB9, 0x5C, 0xF5, 0x29, 0x9D, 0x0E, 0x85, 0x37, 0x34, 0x34,
0xFF, 0x04, 0x17, 0x39, 0x0A, 0x5C, 0x31, 0xD8, 0x59, 0x11, 0xDA, 0x82,
0x1B, 0x18, 0x0C, 0x8E, 0x24, 0x24, 0x9B, 0x7F, 0x02, 0xB6, 0x5D, 0x31,
0x6D, 0xB5, 0x15, 0xB6, 0x54, 0x67, 0x1C, 0x6F, 0xF5, 0x2C, 0x42, 0x4B,
0x7F, 0x6F, 0xAC, 0xD7, 0x56, 0x8A, 0x18, 0xB7, 0x31, 0x02, 0x36, 0xF3,
0xB6, 0x8B, 0x47, 0x03, 0xFA, 0x84, 0xB5, 0x33, 0x90, 0xD7, 0x33, 0xF8,
0x32, 0x89, 0x7C, 0x0C, 0x31, 0xB8, 0xA3, 0xF3, 0xAF, 0x46, 0x3F, 0x77,
0x1A, 0x38, 0x8B, 0x82, 0xBD, 0x4A, 0xAD, 0x93, 0xC6, 0xFC, 0x24, 0x9F,
0xD4, 0xFF, 0xBB, 0x27, 0xE2, 0x6B, 0x5A, 0x99, 0x5F, 0x70, 0x08, 0xDB,
0xC9, 0x3A, 0xE8, 0x73, 0x87, 0x63, 0x09, 0x7F, 0x5D, 0x2A, 0x91, 0x24,
0xFA, 0xEE, 0x9D, 0xC5, 0x81, 0x97, 0x83, 0xC6, 0xD2, 0x6C, 0x5C, 0xFE,
0x45, 0x5A, 0xA4, 0xA9, 0x9C, 0xEA, 0xEF, 0x98, 0x52, 0xAE, 0x3A, 0xD0,
0x12, 0x54, 0x75, 0x9E, 0xD0, 0x1E, 0xF7, 0x9D, 0x03, 0x7E, 0xD5, 0x89,
0x3B, 0xE6, 0x87, 0x75, 0xB3, 0x7B, 0x7C, 0x45, 0x58, 0x1F, 0x8E, 0xE8,
0x59, 0x78, 0x54, 0xC3, 0xD5, 0x9B, 0x0F, 0xC7, 0x05, 0x34, 0xF2, 0x6C,
0xCF, 0x19, 0x40, 0x92, 0xDD, 0xF0, 0x1E, 0x5F, 0xA7, 0x04, 0xA2, 0x4D,
0x2E, 0x7C, 0x57, 0x43, 0x3C, 0xCC, 0xA0, 0x68, 0x0A, 0xEC, 0x47, 0x3D,
0x27, 0x16, 0x8C, 0x62, 0x33, 0x7F, 0x7D, 0x2D, 0xAA, 0x9D, 0x2F, 0x39,
0xF7, 0x44, 0xD7, 0x66, 0xE8, 0x1A, 0x38, 0x0C, 0x68, 0x9E, 0x37, 0x3E,
0x8F, 0xCF, 0x20, 0xBF, 0x9D, 0x3F, 0x4D, 0xA4, 0xFA, 0xFA, 0x67, 0xA8,
0xAD, 0xB5, 0x04, 0xFF, 0x29, 0x72, 0x4A, 0x87, 0xFD, 0x95, 0xC6, 0x1B,
0xE2, 0xA7, 0xB3, 0x7F, 0xC7, 0xCB, 0x4A, 0x5A, 0x7A, 0x02, 0x0B, 0x24,
0xBD, 0x27, 0xC1, 0xED, 0x94, 0x12, 0x5E, 0x7F, 0x45, 0x6E, 0xE5, 0x24,
0x38, 0x24, 0xA4, 0xF4, 0x93, 0xE6, 0xF6, 0x3F, 0x12, 0xD7, 0x9D, 0x2E,
0x15, 0xAC, 0x29, 0x2D, 0x83, 0xB2, 0x95, 0x5D, 0x7A, 0x1A, 0xCB, 0x9F,
0x23, 0xF3, 0x80, 0x3E, 0x8C, 0x72, 0x5F, 0x0F, 0xB2, 0x93, 0x1E, 0x15,
0xC5, 0xC0, 0x8B, 0xC3, 0xF8, 0x0A, 0xCA, 0x88, 0x07, 0x7F, 0x79
}
},
{
KphKeyTypeTest, // kph-test
{
0x52, 0x53, 0x41, 0x31, 0x00, 0x10, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x01, 0x00, 0x01, 0xC7, 0x95, 0x12, 0x92, 0x18, 0x6F, 0xA2, 0x01, 0x6C,
0x9A, 0x5F, 0xC1, 0x70, 0x7B, 0xF9, 0xE3, 0x57, 0x75, 0x28, 0x13, 0xC9,
0xBF, 0xBC, 0x93, 0xCD, 0x55, 0x5C, 0xA9, 0xC2, 0x80, 0x23, 0xA2, 0xEF,
0xF4, 0xB6, 0x3A, 0xBF, 0x6E, 0x39, 0xC6, 0x23, 0xC1, 0x3B, 0x04, 0xBE,
0xB5, 0x9B, 0x5C, 0x8A, 0x64, 0xAA, 0x7A, 0x1C, 0x20, 0x7E, 0x2D, 0x52,
0x55, 0xEC, 0x3E, 0x5C, 0x40, 0xD5, 0x76, 0xC8, 0x2F, 0xFF, 0x14, 0xD7,
0xA1, 0xE7, 0x1D, 0xF1, 0x3C, 0x73, 0x89, 0x6B, 0xF5, 0xA4, 0xC6, 0x01,
0x20, 0xB1, 0xD8, 0x21, 0x70, 0xF9, 0x61, 0x76, 0x87, 0x1D, 0x24, 0x0F,
0x79, 0xEF, 0x5A, 0xE9, 0x91, 0xB0, 0x97, 0x33, 0xB5, 0x6C, 0x9F, 0x91,
0xED, 0x56, 0x80, 0x6B, 0x40, 0x3C, 0xEF, 0x46, 0xB0, 0xC5, 0xD7, 0xA7,
0xB5, 0x9D, 0x45, 0x7F, 0x03, 0xEA, 0x70, 0xA1, 0x64, 0xE2, 0x29, 0xB4,
0x08, 0x69, 0x09, 0xC0, 0xD0, 0x57, 0x38, 0xF7, 0x4B, 0x33, 0x9D, 0xF4,
0x36, 0x28, 0x15, 0xBF, 0xB1, 0x62, 0x04, 0x10, 0x4D, 0xAB, 0x47, 0x03,
0x15, 0xAB, 0xC4, 0x78, 0x0E, 0x78, 0x9D, 0x8A, 0x28, 0x74, 0x54, 0xEB,
0x66, 0xE8, 0x89, 0x45, 0x45, 0x70, 0x2E, 0xB9, 0x03, 0x1C, 0xCB, 0x83,
0xA0, 0x7C, 0x0B, 0xC5, 0xB2, 0x93, 0x6A, 0x7F, 0x65, 0x4E, 0x35, 0xFB,
0xF1, 0x58, 0x21, 0xC7, 0x2D, 0x44, 0x53, 0x69, 0x9B, 0x46, 0x23, 0x10,
0x51, 0xAE, 0x4C, 0x36, 0x18, 0xC4, 0x62, 0xBE, 0x4E, 0x44, 0xFB, 0x98,
0x42, 0x46, 0x0B, 0x33, 0x48, 0x45, 0x86, 0xEA, 0x7F, 0x75, 0x32, 0xD2,
0x6B, 0x49, 0x5A, 0x0A, 0x18, 0x37, 0xB1, 0xC3, 0x3E, 0xB0, 0x94, 0xE8,
0x46, 0x01, 0x07, 0x59, 0x5F, 0xCD, 0xFF, 0x25, 0xE6, 0xB1, 0x23, 0x34,
0xD5, 0x3A, 0xBA, 0xB9, 0x96, 0xB8, 0xCA, 0xA5, 0x15, 0xD5, 0x07, 0xE3,
0xE1, 0x63, 0x6F, 0x97, 0x4F, 0x11, 0xA5, 0x49, 0x8E, 0x29, 0x3F, 0xC9,
0xAF, 0x59, 0x56, 0x4B, 0x11, 0x04, 0x6C, 0xBE, 0x3F, 0xAE, 0x3F, 0x72,
0xD1, 0x81, 0x15, 0xDE, 0xDB, 0x5B, 0x21, 0xA7, 0x02, 0xE6, 0x50, 0x6D,
0xB0, 0x9E, 0x98, 0x23, 0x44, 0x9D, 0x1D, 0x21, 0x12, 0x7C, 0x28, 0x1F,
0x37, 0x6D, 0xCB, 0xCB, 0x1B, 0x01, 0x63, 0xF3, 0xAC, 0xAF, 0x30, 0x41,
0xBD, 0xA8, 0x39, 0xD7, 0x37, 0x59, 0x56, 0xCC, 0x3B, 0x73, 0x5C, 0xF1,
0x21, 0x9C, 0xD2, 0x75, 0xEA, 0xE7, 0xBD, 0x82, 0xDF, 0x3D, 0x5A, 0x0E,
0x9D, 0x7A, 0x41, 0x16, 0x26, 0x72, 0x12, 0xA0, 0x89, 0xFF, 0xAC, 0x83,
0x87, 0xB5, 0x9F, 0xF0, 0x56, 0xAA, 0x36, 0x9A, 0x1B, 0x41, 0x3C, 0xDF,
0x75, 0x07, 0x0E, 0xC6, 0xF5, 0xB4, 0x41, 0xF6, 0xCA, 0xB6, 0xE8, 0xF0,
0x38, 0x98, 0x4C, 0xB1, 0x56, 0xBC, 0xFD, 0xD4, 0xD9, 0xB5, 0x7E, 0x80,
0x0D, 0xB2, 0xD0, 0x2F, 0x2E, 0x6F, 0x7D, 0xFF, 0xB0, 0xF7, 0x8C, 0xE2,
0x90, 0xDC, 0x3B, 0x15, 0xF6, 0x07, 0xBC, 0x36, 0x2C, 0x40, 0x93, 0x05,
0xEF, 0xF3, 0x02, 0xEB, 0x4D, 0xB0, 0xE8, 0xC9, 0x38, 0x4B, 0xBE, 0xA1,
0xBF, 0xEE, 0x9F, 0x8A, 0x31, 0xE0, 0x76, 0xE1, 0x79, 0x94, 0xB7, 0x4D,
0x7C, 0xF4, 0x65, 0xF6, 0x8C, 0x35, 0xF5, 0x78, 0x5E, 0xFE, 0x13, 0x72,
0xC6, 0x13, 0x4C, 0x3A, 0x5E, 0x2C, 0x2B, 0x68, 0x68, 0x59, 0x02, 0x06,
0xD5, 0x7F, 0x02, 0xA6, 0xF2, 0xB6, 0x16, 0xCA, 0xE8, 0x14, 0x75, 0xDA,
0x3D, 0xF5, 0x85, 0x3D, 0x6B, 0x2D, 0x03, 0xA4, 0xC1, 0x5F, 0x5D, 0x68,
0xCC, 0xB1, 0xDE, 0xC1, 0x69, 0x22, 0x99, 0x00, 0x4A, 0x5C, 0x61, 0x14,
0xAD, 0x50, 0x05, 0xCF, 0x6E, 0xEE, 0x4C, 0xC9, 0x58, 0xA4, 0xE5
}
}
};
static const BCRYPT_PSS_PADDING_INFO KphpKeyPaddingInfo =
{
KPH_KEY_HASH_ALGORITHM,
KPH_KEY_HASH_ALGORITHM_BYTES
};
static const UNICODE_STRING KphpSigExtension = RTL_CONSTANT_STRING(L".sig");
KPH_PROTECTED_DATA_SECTION_RO_POP();
KPH_PROTECTED_DATA_SECTION_PUSH();
static BCRYPT_KEY_HANDLE KphpPublicKeyHandles[ARRAYSIZE(KphpPublicKeys)] = { 0 };
static ULONG KphpPublicKeyHandlesCount = 0;
C_ASSERT(ARRAYSIZE(KphpPublicKeyHandles) == ARRAYSIZE(KphpPublicKeys));
KPH_PROTECTED_DATA_SECTION_POP();
KPH_PAGED_FILE();
/**
* \brief Verifies that the specified signature matches the specified hash by
* using one of the known public keys.
*
* \param[in] KeyHandle The key handle to use for verification, if NULL the
* pinned keys are used to verify the signature.
* \param[in] Hash The hash to verify.
* \param[in] Signature The signature to check.
* \param[in] SignatureLength The length of the signature.
*
* \return Successful or errant status.
*/
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KphpVerifyHash(
_In_opt_ BCRYPT_KEY_HANDLE KeyHandle,
_In_ PKPH_HASH_INFORMATION HashInformation,
_In_ PBYTE Signature,
_In_ ULONG SignatureLength
)
{
NTSTATUS status;
KPH_PAGED_CODE_PASSIVE();
status = STATUS_UNSUCCESSFUL;
if (KeyHandle)
{
status = BCryptVerifySignature(KeyHandle,
KPH_KEY_PADDING_INFO,
HashInformation->Hash,
HashInformation->Length,
Signature,
SignatureLength,
KPH_KEY_PADDING_FLAGS);
}
else
{
for (ULONG i = 0; i < KphpPublicKeyHandlesCount; i++)
{
status = BCryptVerifySignature(KphpPublicKeyHandles[i],
KPH_KEY_PADDING_INFO,
HashInformation->Hash,
HashInformation->Length,
Signature,
SignatureLength,
KPH_KEY_PADDING_FLAGS);
if (NT_SUCCESS(status))
{
return status;
}
}
}
return status;
}
/**
* \brief Closes a verification key handle.
*/
_IRQL_requires_max_(PASSIVE_LEVEL)
VOID KphVerifyCloseKey(
_In_ BCRYPT_KEY_HANDLE KeyHandle
)
{
KPH_PAGED_CODE_PASSIVE();
BCryptDestroyKey(KeyHandle);
}
/**
* \brief Creates a verification key handle.
*
* \param[out] KeyHandle The created key handle.
* \param[in] KeyMaterial The key material to use.
* \param[in] KeyMaterialLength The length of the key material.
*
* \return Successful or errant status.
*/
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KphVerifyCreateKey(
_Out_ BCRYPT_KEY_HANDLE* KeyHandle,
_In_ PBYTE KeyMaterial,
_In_ ULONG KeyMaterialLength
)
{
NTSTATUS status;
KPH_PAGED_CODE_PASSIVE();
status = BCryptImportKeyPair(KPH_KEY_ALG_HANDLE,
NULL,
KPH_KEY_BLOB_PUBLIC,
KeyHandle,
(PUCHAR)KeyMaterial,
KeyMaterialLength,
0);
if (!NT_SUCCESS(status))
{
*KeyHandle = NULL;
}
return status;
}
/**
* \brief Verifies a buffer matches the provided signature.
*
* \param[in] KeyHandle The key handle to use for verification, if NULL the
* pinned keys are used to verify the signature.
* \param[in] Buffer The buffer to verify.
* \param[in] BufferLength The length of the buffer.
* \param[in] Signature The signature to check.
* \param[in] SignatureLength The length of the signature.
*
* \return Successful or errant status.
*/
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KphVerifyBufferEx(
_In_opt_ BCRYPT_KEY_HANDLE KeyHandle,
_In_ PBYTE Buffer,
_In_ ULONG BufferLength,
_In_ PBYTE Signature,
_In_ ULONG SignatureLength
)
{
NTSTATUS status;
KPH_HASH_INFORMATION hashInfo;
KPH_PAGED_CODE_PASSIVE();
status = KphHashBuffer(Buffer,
BufferLength,
KphHashAlgorithmSha512,
&hashInfo);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
VERIFY,
"KphHashBuffer failed: %!STATUS!",
status);
return status;
}
status = KphpVerifyHash(KeyHandle, &hashInfo, Signature, SignatureLength);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
VERIFY,
"KphpVerifyHash failed: %!STATUS!",
status);
return status;
}
return STATUS_SUCCESS;
}
/**
* \brief Verifies a buffer matches the provided signature.
*
* \param[in] Buffer The buffer to verify.
* \param[in] BufferLength The length of the buffer.
* \param[in] Signature The signature to check.
* \param[in] SignatureLength The length of the signature.
*
* \return Successful or errant status.
*/
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KphVerifyBuffer(
_In_ PBYTE Buffer,
_In_ ULONG BufferLength,
_In_ PBYTE Signature,
_In_ ULONG SignatureLength
)
{
KPH_PAGED_CODE_PASSIVE();
return KphVerifyBufferEx(NULL,
Buffer,
BufferLength,
Signature,
SignatureLength);
}
#define KphpVerifyValidFileName(FileName) \
(((FileName)->Length > KphpSigExtension.Length) && \
((FileName)->Buffer[0] == OBJ_NAME_PATH_SEPARATOR))
/**
* \brief Verifies a file object.
*
* \param[in] FileObject File object to verify.
* \param[in] FileName Optional file name to use for verification, if not
* provided the file name is looked up from the file object.
*
* \return Successful or errant status.
*/
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KphVerifyFileObject(
_In_ PFILE_OBJECT FileObject,
_In_opt_ PCUNICODE_STRING FileName
)
{
NTSTATUS status;
PCUNICODE_STRING fileName;
PUNICODE_STRING localFileName;
UNICODE_STRING signatureFileName;
OBJECT_ATTRIBUTES objectAttributes;
HANDLE signatureFileHandle;
IO_STATUS_BLOCK ioStatusBlock;
PBYTE signature;
ULONG signatureLength;
KPH_HASH_INFORMATION hashInfo;
KPH_PAGED_CODE_PASSIVE();
localFileName = NULL;
RtlZeroMemory(&signatureFileName, sizeof(UNICODE_STRING));
signatureFileHandle = NULL;
signature = NULL;
if (FileName)
{
fileName = FileName;
}
else
{
status = KphGetNameFileObject(FileObject, &localFileName);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
VERIFY,
"KphGetNameFileObject failed: %!STATUS!",
status);
goto Exit;
}
fileName = localFileName;
}
if (!KphpVerifyValidFileName(fileName))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
VERIFY,
"File name is invalid \"%wZ\"",
fileName);
status = STATUS_OBJECT_NAME_INVALID;
goto Exit;
}
status = RtlDuplicateUnicodeString(0, fileName, &signatureFileName);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
VERIFY,
"RtlDuplicateUnicodeString failed: %!STATUS!",
status);
RtlZeroMemory(&signatureFileName, sizeof(UNICODE_STRING));
goto Exit;
}
//
// Replace the file extension with ".sig". Our verification requires that
// the signature file be placed alongside the file we're verifying.
//
if (signatureFileName.Length < KphpSigExtension.Length)
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
VERIFY,
"Signature file name length invalid \"%wZ\"",
&signatureFileName);
status = STATUS_OBJECT_NAME_INVALID;
goto Exit;
}
signatureFileName.Length -= KphpSigExtension.Length;
status = RtlAppendUnicodeStringToString(&signatureFileName,
&KphpSigExtension);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
VERIFY,
"RtlAppendUnicodeStringToString failed: %!STATUS!",
status);
goto Exit;
}
InitializeObjectAttributes(&objectAttributes,
&signatureFileName,
OBJ_KERNEL_HANDLE | OBJ_DONT_REPARSE,
NULL,
NULL);
status = KphCreateFile(&signatureFileHandle,
FILE_READ_ACCESS | SYNCHRONIZE,
&objectAttributes,
&ioStatusBlock,
NULL,
FILE_ATTRIBUTE_NORMAL,
FILE_SHARE_READ,
FILE_OPEN,
(FILE_NON_DIRECTORY_FILE |
FILE_SYNCHRONOUS_IO_NONALERT |
FILE_COMPLETE_IF_OPLOCKED),
NULL,
0,
IO_IGNORE_SHARE_ACCESS_CHECK,
KernelMode);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
VERIFY,
"Failed to open signature file \"%wZ\": %!STATUS!",
&signatureFileName,
status);
signatureFileHandle = NULL;
goto Exit;
}
else if (status == STATUS_OPLOCK_BREAK_IN_PROGRESS)
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
VERIFY,
"Failed to open signature file \"%wZ\": %!STATUS!",
&signatureFileName,
status);
status = STATUS_SHARING_VIOLATION;
goto Exit;
}
signature = KphAllocatePaged(KPH_VERIFY_SIGNATURE_MAX_LENGTH,
KPH_TAG_VERIFY_SIGNATURE);
if (!signature)
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
VERIFY,
"Failed to allocate signature buffer");
status = STATUS_INSUFFICIENT_RESOURCES;
goto Exit;
}
status = ZwReadFile(signatureFileHandle,
NULL,
NULL,
NULL,
&ioStatusBlock,
signature,
KPH_VERIFY_SIGNATURE_MAX_LENGTH,
NULL,
NULL);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
VERIFY,
"Failed to read signature file \"%wZ\": %!STATUS!",
&signatureFileName,
status);
goto Exit;
}
signatureLength = (ULONG)ioStatusBlock.Information;
hashInfo.Algorithm = KphHashAlgorithmSha512;
status = KphQueryHashInformationFileObject(FileObject,
&hashInfo,
sizeof(KPH_HASH_INFORMATION));
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
VERIFY,
"KphQueryHashInformationFileObject failed: %!STATUS!",
status);
goto Exit;
}
status = KphpVerifyHash(NULL, &hashInfo, signature, signatureLength);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
VERIFY,
"KphpVerifyHash failed: %!STATUS!",
status);
goto Exit;
}
status = STATUS_SUCCESS;
Exit:
if (signature)
{
KphFree(signature, KPH_TAG_VERIFY_SIGNATURE);
}
if (signatureFileHandle)
{
ObCloseHandle(signatureFileHandle, KernelMode);
}
RtlFreeUnicodeString(&signatureFileName);
if (localFileName)
{
KphFreeNameFileObject(localFileName);
}
return status;
}
/**
* \brief Verifies a file by name.
*
* \param[in] FileName File name to verify.
* \param[in] FileObject Optional file object to use for verification. If
* provided the opened file object is checked to match. This is useful when the
* file object is known but not in a state where you can use it directly.
*
* \return Successful or errant status.
*/
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KphVerifyFile(
_In_ PCUNICODE_STRING FileName,
_In_opt_ PFILE_OBJECT FileObject
)
{
NTSTATUS status;
OBJECT_ATTRIBUTES objectAttributes;
IO_STATUS_BLOCK ioStatusBlock;
HANDLE fileHandle;
PFILE_OBJECT fileObject;
KPH_PAGED_CODE_PASSIVE();
fileObject = NULL;
fileHandle = NULL;
if (!KphpVerifyValidFileName(FileName))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
VERIFY,
"File name is invalid \"%wZ\"",
FileName);
status = STATUS_OBJECT_NAME_INVALID;
goto Exit;
}
InitializeObjectAttributes(&objectAttributes,
(PUNICODE_STRING)FileName,
OBJ_KERNEL_HANDLE | OBJ_DONT_REPARSE,
NULL,
NULL);
status = KphCreateFile(&fileHandle,
FILE_READ_ACCESS | SYNCHRONIZE,
&objectAttributes,
&ioStatusBlock,
NULL,
FILE_ATTRIBUTE_NORMAL,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
FILE_OPEN,
(FILE_NON_DIRECTORY_FILE |
FILE_SYNCHRONOUS_IO_NONALERT |
FILE_COMPLETE_IF_OPLOCKED),
NULL,
0,
IO_IGNORE_SHARE_ACCESS_CHECK,
KernelMode);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
VERIFY,
"KphCreateFile failed: %!STATUS!",
status);
fileHandle = NULL;
goto Exit;
}
else if (status == STATUS_OPLOCK_BREAK_IN_PROGRESS)
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
VERIFY,
"KphCreateFile failed: %!STATUS!",
status);
status = STATUS_SHARING_VIOLATION;
goto Exit;
}
status = ObReferenceObjectByHandle(fileHandle,
0,
*IoFileObjectType,
KernelMode,
&fileObject,
NULL);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
VERIFY,
"ObReferenceObjectByHandle failed: %!STATUS!",
status);
fileObject = NULL;
goto Exit;
}
if (FileObject && !KphIsSameFile(FileObject, fileObject))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
VERIFY,
"File objects do not match!");
status = STATUS_INVALID_PARAMETER;
goto Exit;
}
status = KphVerifyFileObject(fileObject, FileName);
Exit:
if (fileObject)
{
ObDereferenceObject(fileObject);
}
if (fileHandle)
{
ObCloseHandle(fileHandle, KernelMode);
}
return status;
}
/**
* \brief Initializes verification infrastructure.
*
* \return Successful or errant status.
*/
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KphInitializeVerify(
VOID
)
{
BOOLEAN testSigning;
KPH_PAGED_CODE_PASSIVE();
testSigning = KphTestSigningEnabled();
for (ULONG i = 0; i < ARRAYSIZE(KphpPublicKeys); i++)
{
NTSTATUS status;
PCKPH_KEY key;
BCRYPT_KEY_HANDLE keyHandle;
key = &KphpPublicKeys[i];
if (!testSigning && (key->Type == KphKeyTypeTest))
{
continue;
}
status = KphVerifyCreateKey(&keyHandle,
(PBYTE)key->Material,
KPH_KEY_MATERIAL_SIZE);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
VERIFY,
"KphVerifyCreateKey failed: %!STATUS!",
status);
return status;
}
KphpPublicKeyHandles[KphpPublicKeyHandlesCount++] = keyHandle;
}
return STATUS_SUCCESS;
}
/**
* \brief Cleans up verification infrastructure.
*/
_IRQL_requires_max_(PASSIVE_LEVEL)
VOID KphCleanupVerify(
VOID
)
{
KPH_PAGED_CODE_PASSIVE();
for (ULONG i = 0; i < KphpPublicKeyHandlesCount; i++)
{
KphVerifyCloseKey(KphpPublicKeyHandles[i]);
}
}
================================================
FILE: KSystemInformer/vm.c
================================================
/*
* Copyright (c) 2022 Winsider Seminars & Solutions, Inc. All rights reserved.
*
* This file is part of System Informer.
*
* Authors:
*
* wj32 2010-2016
* jxy-s 2022-2026
*
*/
#include
#include
/**
* \brief Queries information on mappings for a given section object.
*
* \param[in] SectionObject The section object to query the info of.
* \param[out] SectionInformation Populated with the information. This storage
* must be located in non-paged system-space memory.
* \param[in] SectionInformationLength Length of the section information.
* \param[out] ReturnLength Receives the number of bytes written or required.
*
* \return Successful or errant status.
*/
_IRQL_requires_max_(DISPATCH_LEVEL)
_Must_inspect_result_
NTSTATUS KphpQuerySectionMappings(
_In_ PVOID SectionObject,
_Out_writes_bytes_opt_(SectionInformationLength) PVOID SectionInformation,
_In_ ULONG SectionInformationLength,
_Out_ PULONG ReturnLength
)
{
NTSTATUS status;
PKPH_DYN dyn;
ULONG returnLength;
PVOID controlArea;
PLIST_ENTRY listHead;
PKPH_SECTION_MAPPINGS_INFORMATION info;
PEX_SPIN_LOCK lock;
KIRQL oldIrql;
KPH_NPAGED_CODE_DISPATCH_MAX();
lock = NULL;
oldIrql = 0;
returnLength = 0;
dyn = KphReferenceDynData();
if (!dyn ||
(dyn->MmSectionControlArea == ULONG_MAX) ||
(dyn->MmControlAreaListHead == ULONG_MAX) ||
(dyn->MmControlAreaLock == ULONG_MAX))
{
status = STATUS_NOINTERFACE;
goto Exit;
}
controlArea = *(PVOID*)Add2Ptr(SectionObject, dyn->MmSectionControlArea);
if ((ULONG_PTR)controlArea & 3)
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
GENERAL,
"Section remote mappings not supported.");
status = STATUS_NOT_SUPPORTED;
goto Exit;
}
controlArea = (PVOID)((ULONG_PTR)controlArea & ~3);
if (!controlArea)
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
GENERAL,
"Section control area is null.");
status = STATUS_INVALID_PARAMETER;
goto Exit;
}
lock = Add2Ptr(controlArea, dyn->MmControlAreaLock);
oldIrql = ExAcquireSpinLockShared(lock);
listHead = Add2Ptr(controlArea, dyn->MmControlAreaListHead);
//
// Links are shared in a union with AweContext pointer. Ensure that both
// links look valid.
//
if (!listHead->Flink || !listHead->Blink ||
(listHead->Flink->Blink != listHead) ||
(listHead->Blink->Flink != listHead))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
GENERAL,
"Section unexpected control area links.");
status = STATUS_INVALID_PARAMETER;
goto Exit;
}
returnLength = UFIELD_OFFSET(KPH_SECTION_MAPPINGS_INFORMATION, Mappings);
for (PLIST_ENTRY link = listHead->Flink;
link != listHead;
link = link->Flink)
{
status = RtlULongAdd(returnLength,
sizeof(KPH_SECTION_MAP_ENTRY),
&returnLength);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
GENERAL,
"RtlULongAdd failed: %!STATUS!",
status);
goto Exit;
}
}
if (!SectionInformation || (SectionInformationLength < returnLength))
{
status = STATUS_BUFFER_TOO_SMALL;
goto Exit;
}
info = SectionInformation;
info->NumberOfMappings = 0;
for (PLIST_ENTRY link = listHead->Flink;
link != listHead;
link = link->Flink)
{
PMMVAD vad;
PKPH_SECTION_MAP_ENTRY entry;
vad = CONTAINING_RECORD(link, MMVAD, ViewLinks);
entry = &info->Mappings[info->NumberOfMappings++];
entry->ViewMapType = vad->ViewMapType;
entry->ProcessId = NULL;
if (vad->ViewMapType == VIEW_MAP_TYPE_PROCESS)
{
PEPROCESS process;
process = (PEPROCESS)((ULONG_PTR)vad->VadsProcess & ~VIEW_MAP_TYPE_PROCESS);
if (process)
{
entry->ProcessId = PsGetProcessId(process);
}
}
entry->StartVa = MiGetVadStartAddress(vad);
entry->EndVa = MiGetVadEndAddress(vad);
}
status = STATUS_SUCCESS;
Exit:
*ReturnLength = returnLength;
if (lock)
{
ExReleaseSpinLockShared(lock, oldIrql);
}
if (dyn)
{
KphDereferenceObject(dyn);
}
return status;
}
KPH_PAGED_FILE();
/**
* \brief Copies process or kernel memory into the current process.
*
* \param[in] ProcessHandle A handle to a process. The handle must have
* PROCESS_VM_READ access. This parameter may be NULL if BaseAddress is a kernel
* address.
* \param[in] BaseAddress The address from which memory is to be copied.
* \param[out] Buffer A buffer which receives the copied memory.
* \param[in] BufferSize The number of bytes to copy.
* \param[out] NumberOfBytesRead Receives the number of bytes written.
* \param[in] AccessMode The mode in which to perform access checks.
*
* \return Successful or errant status.
*/
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KphReadVirtualMemory(
_In_opt_ HANDLE ProcessHandle,
_In_ PVOID BaseAddress,
_Out_writes_bytes_(BufferSize) PVOID Buffer,
_In_ SIZE_T BufferSize,
_Out_opt_ PSIZE_T NumberOfBytesRead,
_In_ KPROCESSOR_MODE AccessMode
)
{
NTSTATUS status;
SIZE_T numberOfBytesRead;
BOOLEAN releaseModuleLock;
PVOID buffer;
BYTE stackBuffer[0x200];
KPH_PAGED_CODE_PASSIVE();
numberOfBytesRead = 0;
releaseModuleLock = FALSE;
buffer = NULL;
if (!Buffer)
{
status = STATUS_INVALID_PARAMETER_3;
goto Exit;
}
if (Add2Ptr(BaseAddress, BufferSize) < BaseAddress)
{
status = STATUS_ACCESS_VIOLATION;
goto Exit;
}
if (!BufferSize)
{
status = STATUS_SUCCESS;
goto Exit;
}
//
// Select the appropriate copy method.
//
if (Add2Ptr(BaseAddress, BufferSize) > MmHighestUserAddress)
{
MM_COPY_ADDRESS copyAddress;
//
// Only permit reading from the system module ranges. Prevent TOCTOU
// between checking the system module list and copying.
//
KeEnterCriticalRegion();
if (!ExAcquireResourceSharedLite(PsLoadedModuleResource, TRUE))
{
KeLeaveCriticalRegion();
KphTracePrint(TRACE_LEVEL_VERBOSE,
GENERAL,
"Failed to acquire PsLoadedModuleResource");
status = STATUS_RESOURCE_NOT_OWNED;
goto Exit;
}
releaseModuleLock = TRUE;
status = KphValidateAddressForSystemModules(BaseAddress, BufferSize);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
GENERAL,
"KphValidateAddressForSystemModules failed: %!STATUS!",
status);
goto Exit;
}
buffer = KphAllocateNPagedA(BufferSize, KPH_TAG_COPY_VM, stackBuffer);
if (!buffer)
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
GENERAL,
"Failed to allocate copy buffer.");
status = STATUS_INSUFFICIENT_RESOURCES;
goto Exit;
}
copyAddress.VirtualAddress = BaseAddress;
status = MmCopyMemory(buffer,
copyAddress,
BufferSize,
MM_COPY_MEMORY_VIRTUAL,
&numberOfBytesRead);
if (AccessMode != KernelMode)
{
__try
{
CopyToUser(Buffer, buffer, numberOfBytesRead);
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
status = GetExceptionCode();
}
}
else
{
RtlCopyMemory(Buffer, buffer, numberOfBytesRead);
}
}
else
{
PEPROCESS process;
if (!ProcessHandle)
{
status = STATUS_INVALID_PARAMETER_1;
goto Exit;
}
status = ObReferenceObjectByHandle(ProcessHandle,
0,
*PsProcessType,
AccessMode,
&process,
NULL);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
GENERAL,
"ObReferenceObjectByHandle failed: %!STATUS!",
status);
goto Exit;
}
status = MmCopyVirtualMemory(process,
BaseAddress,
PsGetCurrentProcess(),
Buffer,
BufferSize,
AccessMode,
&numberOfBytesRead);
ObDereferenceObject(process);
}
Exit:
if (buffer)
{
KphFreeA(buffer, KPH_TAG_COPY_VM, stackBuffer);
}
if (releaseModuleLock)
{
ExReleaseResourceLite(PsLoadedModuleResource);
KeLeaveCriticalRegion();
}
if (NumberOfBytesRead)
{
KphWriteSizeTToMode(NumberOfBytesRead, numberOfBytesRead, AccessMode);
}
return status;
}
/**
* \brief Queries information about a section.
*
* \param[in] SectionHandle Handle to the query to query information of.
* \param[in] SectionInformationClass Classification of information to query.
* \param[out] SectionInformation Populated with the requested information.
* \param[in] SectionInformationLength Length of the information buffer.
* \param[out] ReturnLength Receives the number of bytes written or required.
* \param[in] AccessMode The mode in which to perform access checks.
*
* \return Successful or errant status.
*/
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KphQuerySection(
_In_ HANDLE SectionHandle,
_In_ KPH_SECTION_INFORMATION_CLASS SectionInformationClass,
_Out_writes_bytes_opt_(SectionInformationLength) PVOID SectionInformation,
_In_ ULONG SectionInformationLength,
_Out_opt_ PULONG ReturnLength,
_In_ KPROCESSOR_MODE AccessMode
)
{
NTSTATUS status;
PVOID sectionObject;
ULONG returnLength;
PVOID buffer;
BYTE stackBuffer[64];
KPH_PAGED_CODE_PASSIVE();
returnLength = 0;
buffer = NULL;
status = ObReferenceObjectByHandle(SectionHandle,
0,
*MmSectionObjectType,
KernelMode,
§ionObject,
NULL);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
GENERAL,
"ObReferenceObjectByHandle failed: %!STATUS!",
status);
sectionObject = NULL;
goto Exit;
}
switch (SectionInformationClass)
{
case KphSectionMappingsInformation:
{
if (SectionInformation)
{
buffer = KphAllocateNPagedA(SectionInformationLength,
KPH_TAG_SECTION_QUERY,
stackBuffer);
if (!buffer)
{
status = STATUS_INSUFFICIENT_RESOURCES;
goto Exit;
}
}
else
{
NT_ASSERT(!buffer);
SectionInformationLength = 0;
}
status = KphpQuerySectionMappings(sectionObject,
buffer,
SectionInformationLength,
&returnLength);
if (!NT_SUCCESS(status))
{
goto Exit;
}
if (!SectionInformation)
{
status = STATUS_BUFFER_TOO_SMALL;
goto Exit;
}
status = KphCopyToMode(SectionInformation,
buffer,
returnLength,
AccessMode);
break;
}
default:
{
status = STATUS_INVALID_INFO_CLASS;
break;
}
}
Exit:
if (ReturnLength)
{
KphWriteULongToMode(ReturnLength, returnLength, AccessMode);
}
if (sectionObject)
{
ObDereferenceObject(sectionObject);
}
if (buffer)
{
KphFreeA(buffer, KPH_TAG_SECTION_QUERY, stackBuffer);
}
return status;
}
/**
* \brief Queries information about a region of pages within the virtual address
* space of the specified process.
*
* \param[in] ProcessHandle Handle for the process whose context the pages to be
* queried resides.
* \param[in] BaseAddress The base address of the region of pages to be queried.
* \param[in] MemoryInformationClass The memory information to retrieve.
* \param[out] MemoryInformation Populated with the requested information.
* \param[in] MemoryInformationLength Length of the information buffer.
* \param[out] ReturnLength Receives the number of bytes written or required.
* \param[in] AccessMode The mode in which to perform access checks.
*
* \return Successful or errant status.
*/
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSTATUS KphQueryVirtualMemory(
_In_ HANDLE ProcessHandle,
_In_opt_ PVOID BaseAddress,
_In_ KPH_MEMORY_INFORMATION_CLASS MemoryInformationClass,
_Out_writes_bytes_opt_(MemoryInformationLength) PVOID MemoryInformation,
_In_ ULONG MemoryInformationLength,
_Out_opt_ PULONG ReturnLength,
_In_ KPROCESSOR_MODE AccessMode
)
{
NTSTATUS status;
ULONG returnLength;
PKPH_THREAD_CONTEXT thread;
PUNICODE_STRING mappedFileName;
KPH_PAGED_CODE_PASSIVE();
returnLength = 0;
thread = NULL;
mappedFileName = NULL;
switch (MemoryInformationClass)
{
case KphMemoryImageSection:
{
SIZE_T length;
OBJECT_ATTRIBUTES objectAttributes;
IO_STATUS_BLOCK ioStatusBlock;
HANDLE fileHandle;
HANDLE sectionHandle;
if (!MemoryInformation ||
(MemoryInformationLength < sizeof(HANDLE)))
{
status = STATUS_INFO_LENGTH_MISMATCH;
returnLength = sizeof(HANDLE);
goto Exit;
}
//
// At this time there is no known way to open the image section
// for an address in a process without reopening the file.
//
// For image mappings the VAD does not retain a reference to the
// actual section object but does contain a pointer to the file
// object. There are a few exported APIs for creating sections
// using a file object directly. And, using the file object in the
// VAD, it is possible to map the data section but not the image
// section. Internally, MiCreateSection has checks for SEC_IMAGE
// when passing in a file object - this check will fail the
// operation.
//
// Unfortunately, to create the image section, we have to query the
// file name and open the file again. This introduces two points of
// failure that could be avoided if we could create the image
// section using the file object. The unfortunate failure mode is
// the file being "gone" or otherwise inaccessible. The other cases
// might be a resource constraint or a filter interfering with
// access to the file.
//
mappedFileName = KphAllocatePaged(MAX_PATH, KPH_TAG_VM_QUERY);
if (!mappedFileName)
{
status = STATUS_INSUFFICIENT_RESOURCES;
goto Exit;
}
status = ZwQueryVirtualMemory(ProcessHandle,
BaseAddress,
MemoryMappedFilenameInformation,
mappedFileName,
MAX_PATH,
&length);
if ((status == STATUS_BUFFER_OVERFLOW) && (length > 0))
{
KphFree(mappedFileName, KPH_TAG_VM_QUERY);
mappedFileName = KphAllocatePaged(length, KPH_TAG_VM_QUERY);
if (!mappedFileName)
{
status = STATUS_INSUFFICIENT_RESOURCES;
goto Exit;
}
status = ZwQueryVirtualMemory(ProcessHandle,
BaseAddress,
MemoryMappedFilenameInformation,
mappedFileName,
length,
&length);
}
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
GENERAL,
"ZwQueryVirtualMemory failed: %!STATUS!",
status);
goto Exit;
}
InitializeObjectAttributes(&objectAttributes,
mappedFileName,
OBJ_KERNEL_HANDLE,
NULL,
NULL);
status = KphCreateFile(&fileHandle,
FILE_READ_ACCESS | SYNCHRONIZE,
&objectAttributes,
&ioStatusBlock,
NULL,
FILE_ATTRIBUTE_NORMAL,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
FILE_OPEN,
FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT,
NULL,
0,
IO_IGNORE_SHARE_ACCESS_CHECK,
KernelMode);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
GENERAL,
"KphCreateFile failed: %!STATUS!",
status);
goto Exit;
}
InitializeObjectAttributes(&objectAttributes,
NULL,
(AccessMode ? 0 : OBJ_KERNEL_HANDLE),
NULL,
NULL);
status = ZwCreateSection(§ionHandle,
SECTION_QUERY | SECTION_MAP_READ,
&objectAttributes,
NULL,
PAGE_READONLY,
SEC_IMAGE_NO_EXECUTE,
fileHandle);
ObCloseHandle(fileHandle, KernelMode);
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
GENERAL,
"ZwCreateSection failed: %!STATUS!",
status);
goto Exit;
}
status = KphWriteHandleToMode(MemoryInformation,
sectionHandle,
AccessMode);
if (NT_SUCCESS(status))
{
returnLength = sizeof(HANDLE);
}
break;
}
case KphMemoryDataSection:
{
SIZE_T length;
KPH_VM_TLS_CREATE_DATA_SECTION tls;
PKPH_MEMORY_DATA_SECTION memoryInformation;
if (!MemoryInformation ||
(MemoryInformationLength) < sizeof(KPH_MEMORY_DATA_SECTION))
{
status = STATUS_INFO_LENGTH_MISMATCH;
returnLength = sizeof(KPH_MEMORY_DATA_SECTION);
goto Exit;
}
//
// The data section may be created using the file object in the VAD.
// First we have to access the file object from the VAD. We could do
// some dynamic data and walk the process VAD ourselves, but there
// is a "cleaner" option. Querying for the memory mapped file name
// will do the work to enumerate the VAD and will land us in our
// mini-filter instance with the file object. This still comes with
// possibility of filters interfering with the name query, but at
// least we don't have to go through an entire IRP_MJ_CREATE. The
// advantage here is we are able to create a section object for a
// file that might be "gone" or otherwise inaccessible.
//
thread = KphGetCurrentThreadContext();
if (!thread)
{
status = STATUS_INSUFFICIENT_RESOURCES;
goto Exit;
}
RtlZeroMemory(&tls, sizeof(KPH_VM_TLS_CREATE_DATA_SECTION));
tls.AccessMode = AccessMode;
thread->VmTlsCreateDataSection = &tls;
status = ZwQueryVirtualMemory(ProcessHandle,
BaseAddress,
MemoryMappedFilenameInformation,
NULL,
0,
&length);
NT_ANALYSIS_ASSUME(NT_SUCCESS(status));
if (thread->VmTlsCreateDataSection)
{
thread->VmTlsCreateDataSection = NULL;
KphTracePrint(TRACE_LEVEL_VERBOSE,
GENERAL,
"VmTlsCreateDataSection was not null! "
"ZwQueryVirtualMemory returned: %!STATUS!",
status);
status = STATUS_UNEXPECTED_IO_ERROR;
goto Exit;
}
status = tls.Status;
if (!NT_SUCCESS(status))
{
KphTracePrint(TRACE_LEVEL_VERBOSE,
GENERAL,
"KPH_VM_TLS_CREATE_DATA_SECTION status: %!STATUS!",
status);
goto Exit;
}
memoryInformation = MemoryInformation;
status = KphCopyToMode(&memoryInformation->SectionFileSize,
&tls.SectionFileSize,
sizeof(LARGE_INTEGER),
AccessMode);
if (!NT_SUCCESS(status))
{
ObCloseHandle(tls.SectionHandle, AccessMode);
goto Exit;
}
status = KphWriteHandleToMode(&memoryInformation->SectionHandle,
tls.SectionHandle,
AccessMode);
break;
}
case KphMemoryMappedInformation:
{
SIZE_T length;
KPH_MEMORY_MAPPED_INFORMATION tls;
KPH_MEMORY_MAPPED_INFORMATION memoryInformation;
if (!MemoryInformation ||
(MemoryInformationLength) < sizeof(KPH_MEMORY_MAPPED_INFORMATION))
{
status = STATUS_INFO_LENGTH_MISMATCH;
returnLength = sizeof(KPH_MEMORY_MAPPED_INFORMATION);
goto Exit;
}
thread = KphGetCurrentThreadContext();
if (!thread)
{
status = STATUS_INSUFFICIENT_RESOURCES;
goto Exit;
}
RtlZeroMemory(&tls, sizeof(KPH_MEMORY_MAPPED_INFORMATION));
thread->VmTlsMappedInformation = &tls;
status = ZwQueryVirtualMemory(ProcessHandle,
BaseAddress,
MemoryMappedFilenameInformation,
NULL,
0,
&length);
NT_ANALYSIS_ASSUME(NT_SUCCESS(status));
if (thread->VmTlsMappedInformation)
{
thread->VmTlsMappedInformation = NULL;
KphTracePrint(TRACE_LEVEL_VERBOSE,
GENERAL,
"VmTlsMappedInformation was not null! "
"ZwQueryVirtualMemory returned: %!STATUS!",
status);
status = STATUS_UNEXPECTED_IO_ERROR;
goto Exit;
}
RtlZeroMemory(&memoryInformation, sizeof(KPH_MEMORY_MAPPED_INFORMATION));
memoryInformation.FileObject = tls.FileObject;
memoryInformation.SectionObjectPointers = tls.SectionObjectPointers;
memoryInformation.DataControlArea = tls.DataControlArea;
memoryInformation.SharedCacheMap = tls.SharedCacheMap;
memoryInformation.ImageControlArea = tls.ImageControlArea;
memoryInformation.UserWritableReferences = tls.UserWritableReferences;
status = KphCopyToMode(MemoryInformation,
&memoryInformation,
sizeof(KPH_MEMORY_MAPPED_INFORMATION),
AccessMode);
if (NT_SUCCESS(status))
{
returnLength = sizeof(KPH_MEMORY_MAPPED_INFORMATION);
}
break;
}
default:
{
status = STATUS_INVALID_INFO_CLASS;
break;
}
}
Exit:
if (ReturnLength)
{
KphWriteULongToMode(ReturnLength, returnLength, AccessMode);
}
if (mappedFileName)
{
KphFree(mappedFileName, KPH_TAG_VM_QUERY);
}
if (thread)
{
KphDereferenceObject(thread);
}
return status;
}
================================================
FILE: LICENSE.txt
================================================
MIT License
Copyright (c) 2022 Winsider Seminars & Solutions, Inc.
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.
================================================
FILE: README.md
================================================
System Informer
A free, powerful, multi-purpose tool that helps you monitor system resources, debug software and detect malware.
Brought to you by Winsider Seminars & Solutions, Inc.
## System requirements
Windows 10 or higher, 32-bit or 64-bit.
## Features
* A detailed overview of system activity with highlighting.
* Graphs and statistics allow you quickly to track down resource hogs and
runaway processes.
* Can't edit or delete a file? Discover which processes are using that file.
* See what programs have active network connections, and close them if
necessary.
* Get real-time information on disk access.
* View detailed stack traces with kernel-mode, WOW64 and .NET support.
* Go beyond services.msc: create, edit and control services.
* Small, portable and no installation required.
* 100% [Free Software](https://www.gnu.org/philosophy/free-sw.en.html)
([MIT](https://opensource.org/licenses/MIT))
## Building the project
Requires Visual Studio (2022 or later).
After cloning the repo run `build_init.cmd` located in the `build` directory,
this doesn't not run again unless there are updates to the tools or third party
libraries.
Execute `build_release.cmd` located in the `build` directory to compile the
project or load the `SystemInformer.sln` and `Plugins.sln` solutions if you
prefer building the project using Visual Studio.
You can download the free
[Visual Studio Community Edition](https://www.visualstudio.com/vs/community/)
to build the System Informer source code.
See the [build readme](./build/README.md) for more information or if you're
having trouble building.
## Enhancements/Bugs
Please use the
[GitHub issue tracker](https://github.com/winsiderss/systeminformer/issues) for
reporting problems or suggesting new features.
## Settings
If you are running System Informer from a USB drive, you may want to
save System Informer's settings there as well. To do this, create a
blank file named "SystemInformer.exe.settings.xml" in the same
directory as SystemInformer.exe. You can do this using Windows Explorer:
1. Make sure "Hide extensions for known file types" is unticked in
Tools > Folder options > View.
2. Right-click in the folder and choose New > Text Document.
3. Rename the file to SystemInformer.exe.settings.xml (delete the ".txt"
extension).
================================================
FILE: README.txt
================================================
System Informer is a powerful free and open source process viewer.
## Getting started
Simply run SystemInformer.exe to start System Informer. There are two
versions, 32-bit (x86) and 64-bit (x64). If you are not sure which
version to use, open Control Panel > System and check the "System
type". You cannot run the 32-bit version of System Informer on a
64-bit system and expect it to work correctly, unlike other programs.
## System requirements
Windows 7 or higher, 32-bit or 64-bit.
## Settings
If you are running System Informer from a USB drive, you may want to
save System Informer's settings there as well. To do this, create a
blank file named "SystemInformer.exe.settings.xml" in the same
directory as SystemInformer.exe. You can do this using Windows Explorer:
1. Make sure "Hide extensions for known file types" is unticked in
Tools > Folder options > View.
2. Right-click in the folder and choose New > Text Document.
3. Rename the file to SystemInformer.exe.settings.xml (delete the ".txt"
extension).
## Plugins
Plugins can be configured from Hacker > Plugins.
If you experience any crashes involving plugins, make sure they
are up to date.
The ExtendedTools plugin is only available for Windows Vista and
above. Disk and Network information provided by this plugin is
only available when running Process Hacker with administrative
rights.
================================================
FILE: SECURITY.md
================================================
# Security Policies and Procedures
This document outlines security procedures and general policies for the System
Informer project.
* [Reporting a Bug](#reporting-a-bug)
* [Disclosure Policy](#disclosure-policy)
## Reporting a Bug
The System Informer team considers security our top priority and reviews all
reports of vulnerabilities or security issues very seriously.
We appreciate responsible disclosure efforts and are committed to acknowledging
your contributions.
If you discover a security issue, please send a report by emailing
security@systeminformer.com or security@systeminformer.io.
The System Informer team will acknowledge your email within 48 hours, and will
send a more detailed response indicating the next steps in handling your report.
After the initial reply to your report, the security team will endeavor to keep
you informed of the progress towards a fix and full announcement, and may ask
for additional information or guidance.
Report security bugs in third-party modules to the person or team maintaining
the module.
## Disclosure Policy
When the System Informer team receives a security bug report, they will assign
it to a primary developer. This person will coordinate the fix and release
process, involving the following steps:
* Confirm the problem and determine the affected versions.
* Audit code to find any potential similar problems.
* Seek review from respected third-party security experts.
* Prepare fixes for all releases still under maintenance. These fixes will be
released as fast as possible.
================================================
FILE: SystemInformer/CMakeLists.txt
================================================
#
# Copyright (c) 2022 Winsider Seminars & Solutions, Inc. All rights reserved.
#
# This file is part of System Informer.
#
set(HEADERS
"include/actions.h"
"include/appsup.h"
"include/colmgr.h"
"include/colsetmgr.h"
"include/devprv.h"
"include/extmgr.h"
"include/extmgri.h"
"include/heapstruct.h"
"include/hidnproc.h"
"include/hndllist.h"
"include/hndlmenu.h"
"include/hndlprv.h"
"include/informer.h"
"include/ksisup.h"
"include/mainwnd.h"
"include/mainwndp.h"
"include/memlist.h"
"include/memprv.h"
"include/memsrch.h"
"include/miniinfo.h"
"include/miniinfop.h"
"include/modlist.h"
"include/modprv.h"
"include/netlist.h"
"include/netprv.h"
"include/notifico.h"
"include/notificop.h"
"include/notiftoast.h"
"include/phapp.h"
"include/phappres.h"
"include/phfwddef.h"
"include/phplug.h"
"include/phsettings.h"
"include/phsvc.h"
"include/phsvcapi.h"
"include/phsvccl.h"
"include/phuisup.h"
"include/procgrp.h"
"include/procmtgn.h"
"include/procprp.h"
"include/procprpp.h"
"include/procprv.h"
"include/proctree.h"
"include/srvlist.h"
"include/srvprv.h"
"include/sysinfo.h"
"include/sysinfop.h"
"include/thrdlist.h"
"include/thrdprv.h"
"resource.h"
"sdk/phdk.h"
)
source_group("Header Files" FILES ${HEADERS})
set(RESOURCES
"version.rc"
"SystemInformer.def"
"SystemInformer.rc"
"SystemInformer.manifest"
"resources/application.ico"
"resources/capslist.txt"
"resources/case_sensitive_modern_dark.svg"
"resources/case_sensitive_modern_light.svg"
"resources/cog.ico"
"resources/etwguids.txt"
"resources/pooltag.txt"
"resources/regex_modern_dark.svg"
"resources/regex_modern_light.svg"
"resources/search_modern_dark.svg"
"resources/search_modern_light.svg"
"resources/search_stop_modern_dark.svg"
"resources/search_stop_modern_light.svg"
"resources/systeminformer.png"
"SystemInformer.ico"
)
source_group("Resource Files" FILES ${RESOURCES})
set(SOURCES
"about.c"
"actions.c"
"admintask.c"
"affinity.c"
"anawait.c"
"appsup.c"
"chcol.c"
"chdlg.c"
"chproc.c"
"colmgr.c"
"colsetmgr.c"
"dbgcon.c"
"delayhook.c"
"delayload.c"
"devprv.c"
"extmgr.c"
"findobj.c"
"gdihndl.c"
"heapinfo.c"
"hidnproc.c"
"hndllist.c"
"hndlmenu.c"
"hndlprp.c"
"hndlprv.c"
"hndlstat.c"
"infodlg.c"
"informer.c"
"itemtips.c"
"jobprp.c"
"kdump.c"
"ksidbg.c"
"ksisup.c"
"ksyscall.c"
"log.c"
"logwnd.c"
"main.c"
"mainwnd.c"
"mdump.c"
"memedit.c"
"memlist.c"
"memlists.c"
"memmod.c"
"memprot.c"
"memprv.c"
"memrslt.c"
"memsrch.c"
"memsrcht.c"
"miniinfo.c"
"modlist.c"
"modprv.c"
"mtgndlg.c"
"mwpgdev.c"
"mwpgnet.c"
"mwpgproc.c"
"mwpgsrv.c"
"netlist.c"
"netprv.c"
"netsup.c"
"notifico.c"
"notiftoast.cpp"
"ntobjprp.c"
"options.c"
"pagfiles.c"
"plugin.c"
"plugman.c"
"procgrp.c"
"procmtgn.c"
"procprp.c"
"procprv.c"
"procrec.c"
"proctree.c"
"prpgenv.c"
"prpggen.c"
"prpghndl.c"
"prpgjob.c"
"prpgmem.c"
"prpgmod.c"
"prpgperf.c"
"prpgsrv.c"
"prpgstat.c"
"prpgthrd.c"
"prpgtok.c"
"prpgvdm.c"
"prpgwmi.c"
"runas.c"
"searchbox.c"
"sessmsg.c"
"sessprp.c"
"sessshad.c"
"settings.c"
"srvcr.c"
"srvctl.c"
"srvlist.c"
"srvprp.c"
"srvprv.c"
"sysinfo.c"
"syssccpu.c"
"sysscio.c"
"sysscmem.c"
"thrdlist.c"
"thrdprv.c"
"thrdstk.c"
"thrdstks.c"
"tokprp.c"
"usrlist.c"
"phsvc/clapi.c"
"phsvc/svcapi.c"
"phsvc/svcapiport.c"
"phsvc/svcclient.c"
"phsvc/svcmain.c"
)
source_group("Source Files" FILES ${SOURCES})
set(ALL_FILES
${HEADERS}
${RESOURCES}
${SOURCES}
)
si_add_executable(SystemInformer WIN32 ${ALL_FILES})
set_target_properties(SystemInformer PROPERTIES ENABLE_EXPORTS TRUE)
target_include_directories(SystemInformer PRIVATE
"${CMAKE_CURRENT_SOURCE_DIR}/include"
)
target_include_directories(SystemInformer INTERFACE
"${SI_ROOT}/phnt/include"
"${SI_ROOT}/sdk/include"
)
target_compile_definitions(SystemInformer PRIVATE
_PHAPP_
)
target_link_libraries(SystemInformer PRIVATE
phnt
phlib
kphlib_um
delayimp
user32
gdi32
comdlg32
advapi32
ole32
oleaut32
setupapi
cfgmgr32
aclui
comctl32
dnsapi
ntdll
userenv
wbemuuid
windowscodecs
winhttp
winsta
)
target_link_options(SystemInformer PRIVATE
/DELAYLOAD:setupapi.dll
/DELAYLOAD:cfgmgr32.dll
/DELAYLOAD:aclui.dll
/DELAYLOAD:comdlg32.dll
/DELAYLOAD:gdiplus.dll
/DELAYLOAD:oleaut32.dll
/DELAYLOAD:winhttp.dll
/DELAYLOAD:winsta.dll
)
si_sdkbuild(SystemInformer)
================================================
FILE: SystemInformer/Directory.Build.props
================================================
================================================
FILE: SystemInformer/SystemInformer.def
================================================
;/*
; * Copyright (c) 2022 Winsider Seminars & Solutions, Inc. All rights reserved.
; *
; * This file is part of System Informer.
; *
; * Authors:
; *
; * wj32 2008-2016
; * dmex 2017-2024
; *
; */
;
; This file was automatically generated.
;
; Do not link at runtime. Use the SystemInformer.def.h header file instead.
;
EXPORTS
; main
PhAddProcessPropPage
PhAddProcessPropPage2
PhAddPropPageLayoutItem
PhAddTreeNewFilter
PhAggregateProcessFieldIfNeeded
PhApplyTreeNewFilters
PhApplyTreeNewFiltersToNode
PhChoiceDialog
PhCmLoadSettings
PhCmSaveSettings
PhCopyListView
PhCopyListViewInfoTip
PhCreateKsiSettingsBlob
PhCreateHandleItem
PhCreateProcessPropContext
PhCreateProcessPropPageContext
PhCreateProcessPropPageContextEx
PhCreateSearchControl
PhCreateServiceListControl
PhDeleteMemoryItemList
PhDeleteStaticWindowIcon
PhDeleteTreeNewColumnMenu
PhDeleteTreeNewFilterSupport
PhDereferenceProcessRecord
PhDeselectAllProcessNodes
PhDeselectAllServiceNodes
PhDeselectAllNetworkNodes
PhDestroyWindowIcon
PhDeviceProviderInitialization
PhDoPropPageLayout
PhDuplicateProcessInformation
PhDuplicateProcessNodeList
PhEnumDeviceResources
PhEnumNetworkItems
PhEnumNetworkItemsByProcessId
PhEnumProcessItems
PhExecuteRunAsCommand2
PhExecuteRunAsCommand3
PhExpandAllProcessNodes
PhFindNetworkNode
PhFindProcessNode
PhFindProcessRecord
PhFindServiceNode
PhFormatLocalSystemTimeISO
PhFormatLogEntry
PhFormatLogType
PhFreezeThread
PhGetApplicationIcon
PhGetApplicationIconEx
PhGetClientIdName
PhGetClientIdNameEx
PhGetDeviceIcon
PhGetDeviceProperty
PhGetFilterSupportNetworkTreeList
PhGetFilterSupportProcessTreeList
PhGetFilterSupportServiceTreeList
PhGetGeneralCallback
PhGetImageListIcon
PhGetListViewContextMenuPoint
PhGetPhReleaseChannel
PhGetPhReleaseChannelString
PhGetPhVersion
PhGetPhVersionHash
PhGetPhVersionNumbers
PhGetProcessIsSuspended
PhGetProcessKnownType
PhGetProcessKnownTypeEx
PhGetProcessPriorityClassString
PhGetProcessSmallImageList
PhGetProtocolTypeName
PhGetSelectedAndPropagateProcessItems
PhGetSelectedProcessItem
PhGetSelectedProcessItems
PhGetSelectedProcessNode
PhGetSelectedProcessNodes
PhGetSelectedServiceItem
PhGetSelectedServiceItems
PhGetServiceChange
PhGetShieldBitmap
PhGetStatisticsTime
PhGetStatisticsTimeString
PhGetTcpStateName
PhHandleCopyCellEMenuItem
PhHandleCopyListViewEMenuItem
PhHandleListViewNotifyBehaviors
PhHandleListViewNotifyForCopy
PhHandleTreeNewColumnMenu
PhImageListExtractIcon
PhImageListFlushCache
PhInitializeTreeNewColumnMenu
PhInitializeTreeNewColumnMenuEx
PhInitializeTreeNewFilterSupport
PhInsertCopyCellEMenuItem
PhInsertCopyListViewEMenuItem
PhInsertHandleObjectPropertiesEMenuItems
PhInvalidateAllProcessNodes
PhIsProcessBackground
PhIsProcessSuspended
PhLoadSymbolProviderOptions
PhLogMessageEntry
PhLookupDeviceItem
PhLookupDeviceItemByHash
PhLookupDevicePropertyClass
PhLookupMemoryItemList
PhProcessImageListInitialization
PhPropPageDlgProcHeader
PhQueryKphCounters
PhQueryMemoryItemList
PhReferenceDeviceItem
PhReferenceDeviceItem2
PhReferenceDeviceItemByHash
PhReferenceDeviceTree
PhReferenceDeviceTreeEx
PhReferenceNetworkItem
PhReferenceProcessItem
PhReferenceProcessItemForParent
PhReferenceProcessItemForRecord
PhReferenceProcessRecord
PhReferenceProcessRecordForStatistics
PhReferenceProcessRecordSafe
PhReferenceServiceItem
PhRegisterDialog
PhRegisterMessageLoopFilter
PhRemoveTreeNewFilter
PhSearchControlClear
PhSearchControlMatch
PhSearchControlMatchLongHintZ
PhSearchControlMatchPointer
PhSearchControlMatchPointerRange
PhSearchControlMatchZ
PhSearchOnlineString
PhSelectAndEnsureVisibleProcessNode
PhSelectAndEnsureVisibleServiceNode
PhSetApplicationWindowIcon
PhSetApplicationWindowIconEx
PhSetProcessItemAffinityMask
PhSetProcessItemIoPriority
PhSetProcessItemPagePriority
PhSetProcessItemPriority
PhSetProcessItemPriorityBoost
PhSetProcessItemThrottlingState
PhSetSelectThreadIdProcessPropContext
PhSetStaticWindowIcon
PhSetWindowIcon
PhShellExecuteUserString
PhShellOpenKey
PhShellOpenKey2
PhShellProcessHacker
PhShowChooseProcessDialog
PhShowHandleObjectProperties1
PhShowHandleObjectProperties2
PhShowIconNotification
PhShowIconNotificationEx
PhShowProcessAffinityDialog2
PhShowProcessProperties
PhShowProcessRecordDialog
PhShowSystemInformationDialog
PhShowThreadAffinityDialog
PhSiDoubleLabelYFunction
PhSiSetColorsGraphDrawInfo
PhSiSizeLabelYFunction
PhSiUInt64LabelYFunction
PhThawThread
PhUnregisterDialog
PhUnregisterMessageLoopFilter
PhUpdateProcessNode
PhUpdateServiceNode
PhWordMatchStringRef
PhWritePhTextHeader
PhaChoiceDialog
PhaGetProcessKnownCommandLine
; plugin
PhEnumeratePlugins
PhFindPlugin2
PhGetPluginCallback
PhGetPluginFileName
PhGetPluginInformation
PhGetPluginInterface
PhGetPluginName
PhPluginAddMenuHook
PhPluginAddTreeNewColumn
PhPluginCallPhSvc
PhPluginCreateEMenuItem
PhPluginCreateTabPage
PhPluginEnableTreeNewNotify
PhPluginGetObjectExtension
PhPluginGetSystemStatistics
PhPluginInvokeWindowCallback
PhPluginQueryPhSvc
PhPluginReserveIds
PhPluginSetObjectExtension
PhRegisterPluginByName
; phsvc
PhSvcCallChangeServiceConfig
PhSvcCallChangeServiceConfig2
PhSvcCallPostMessage
PhSvcCallSendMessage
; UI
PhUiCloseConnections
PhUiCloseHandles
PhUiConnectSession
PhUiConnectToPhSvc
PhUiConnectToPhSvcEx
PhUiContinueService
PhUiContinueServices
PhUiDebugProcess
PhUiDeleteService
PhUiDetachFromDebuggerProcess
PhUiDisconnectFromPhSvc
PhUiDisconnectSession
PhUiEmptyProcessMemoryWorkingSet
PhUiFlushHeapProcesses
PhUiFreeMemory
PhUiFreezeTreeProcess
PhUiHibernateComputer
PhUiLoadDllProcess
PhUiLockComputer
PhUiLogoffComputer
PhUiLogoffSession
PhUiPauseService
PhUiPauseServices
PhUiReduceWorkingSetProcesses
PhUiRestartComputer
PhUiRestartProcess
PhUiRestartServices
PhUiResumeProcesses
PhUiResumeThreads
PhUiResumeTreeProcess
PhUiSetActivityModeration
PhUiSetAttributesHandle
PhUiSetBoostPriorityProcess
PhUiSetBoostPriorityProcesses
PhUiSetBoostPriorityThread
PhUiSetBoostPriorityThreads
PhUiSetCriticalProcess
PhUiSetEcoModeProcess
PhUiSetEmptyWorkingSetProcesses
PhUiSetExecutionRequiredProcess
PhUiSetIoPriorityProcesses
PhUiSetIoPriorityThread
PhUiSetPagePriorityProcess
PhUiSetPagePriorityThread
PhUiSetPriorityClassProcesses
PhUiSetPriorityThread
PhUiSetPriorityThreads
PhUiSetVirtualizationProcess
PhUiShutdownComputer
PhUiSleepComputer
PhUiStartService
PhUiStartServices
PhUiStopService
PhUiStopServices
PhUiSuspendProcesses
PhUiSuspendThreads
PhUiSuspendTreeProcess
PhUiTerminateProcesses
PhUiTerminateThreads
PhUiTerminateTreeProcess
PhUiThawTreeProcess
PhUiUnloadModule
;
; phlib exports
;
; ref
PhAutoDereferenceObject
PhCreateAlloc
PhCreateObject
PhCreateObjectType
PhCreateObjectTypeEx
PhDeleteAutoPool
PhDereferenceObject
PhDereferenceObjectDeferDelete
PhDereferenceObjectEx
PhDrainAutoPool
PhGetObjectType
PhGetObjectTypeInformation
PhInitializeAutoPool
PhReferenceObject
PhReferenceObjectEx
PhReferenceObjectSafe
; queuedlock
PhfAcquireQueuedLockExclusive
PhfAcquireQueuedLockShared
PhfPulseAllCondition
PhfPulseCondition
PhfQueueWakeEvent
PhfReleaseQueuedLockExclusive
PhfReleaseQueuedLockShared
PhfSetWakeEvent
PhfWaitForCondition
PhfWaitForConditionEx
PhfWaitForWakeEvent
PhfWakeForReleaseQueuedLock
; phconfig
PhIsExecutingInWow64
PhSystemBasicInformation DATA
; phbasesup
PhAddElementAvlTree
PhAddEntryHashtable
PhAddEntryHashtableEx
PhAddItemArray
PhAddItemList
PhAddItemPointerList
PhAddItemSimpleHashtable
PhAddItemsArray
PhAddItemsList
PhAddPlusMaxMemorySingles
PhAllocate
PhAllocateExSafe
PhAllocateFromFreeList
PhAllocatePage
PhAllocateSafe
PhAppendBytesBuilder
PhAppendBytesBuilderEx
PhAppendCharStringBuilder
PhAppendCharStringBuilder2
PhAppendFormatStringBuilder
PhAppendFormatStringBuilder_V
PhAppendFormatBytesBuilder
PhAppendFormatBytesBuilder_V
PhAppendStringBuilderEx
PhBufferToHexString
PhBufferToHexStringEx
PhClearArray
PhClearHashtable
PhClearList
PhCompareStringRef
PhCompareStringZNatural
PhConcatStringRef2
PhConcatStringRef3
PhConcatStringRef4
PhConcatStrings
PhConcatStrings2
PhConcatStrings_V
PhConvertMultiByteToUtf16
PhConvertMultiByteToUtf16Ex
PhConvertUtf16ToAsciiEx
PhConvertUtf16ToMultiByte
PhConvertUtf16ToMultiByteEx
PhConvertUtf16ToUtf8
PhConvertUtf16ToUtf8Buffer
PhConvertUtf16ToUtf8Ex
PhConvertUtf16ToUtf8Size
PhConvertUtf8ToUtf16
PhConvertUtf8ToUtf16Buffer
PhConvertUtf8ToUtf16Ex
PhConvertUtf8ToUtf16Size
PhCopyBytesZ
PhCopyConvertCircularBufferULONG
PhCopyStringZ
PhCopyStringZFromBytes
PhCopyStringZFromMultiByte
PhCountStringZ
PhCreateBytesEx
PhCreateHashtable
PhCreateList
PhCreatePointerList
PhCreateSimpleHashtable
PhCreateString3
PhCreateStringEx
PhCreateThread
PhCreateThread2
PhCreateThreadEx
PhDecodeUnicodeDecoder
PhDelayExecution
PhDeleteArray
PhDeleteBytesBuilder
PhDeleteCallback
PhDeleteFreeList
PhDeleteStringBuilder
PhDivideSinglesBySingle
PhDoesNameContainWildCards
PhDosErrorToNtStatus
PhDuplicateBytesZ
PhDuplicateBytesZSafe
PhDuplicateStringZ
PhEncodeUnicode
PhEnumAvlTree
PhEnumHashtable
PhEnumPointerListEx
PhEqualStringRef
PhExponentiate
PhExponentiate64
PhExtractIcon
PhExtractIconEx
PhFillMemoryUlong
PhFinalBytesBuilderBytes
PhFinalStringBuilderString
PhFindCharInStringRef
PhFindElementAvlTree
PhFindEntryHashtable
PhFindItemList
PhFindItemPointerList
PhFindItemSimpleHashtable
PhFindLastCharInStringRef
PhFindStringInStringRef
PhFormat
PhFormatBytes
PhFormatBytes_V
PhFormatString
PhFormatString_V
PhFormatSystemTimeISO
PhFormatToBuffer
PhFree
PhFreePage
PhFreeToFreeList
PhGetLastError
PhGetPrimeNumber
PhHashBytes
PhHashStringRef
PhHashStringRefEx
PhHexStringToBuffer
PhHexStringToBufferEx
PhHungWindowFromGhostWindow
PhInitializeArray
PhInitializeAvlTree
PhInitializeBytesBuilder
PhInitializeCallback
PhInitializeFreeList
PhInitializeStringBuilder
PhInsertItemList
PhInsertItemsList
PhInsertStringBuilder
PhInsertStringBuilder2
PhInsertStringBuilderEx
PhIntegerToString64
PhInvokeCallback
PhIsNameInExpression
PhLoadIndirectString
PhLoadResource
PhLocalTimeToSystemTime
PhLowerBoundElementAvlTree
PhLowerDualBoundElementAvlTree
PhMaxMemorySingles
PhMaximumElementAvlTree
PhMinimumElementAvlTree
PhNtStatusFileNotFound
PhNtStatusToDosError
PhPredecessorElementAvlTree
PhPrintTimeSpan
PhQueryPerformanceCounter
PhQueryPerformanceFrequency
PhQuerySystemTime
PhQueryTimeZoneBias
PhReAllocate
PhReAllocateSafe
PhReferenceEmptyString
PhRegisterCallback
PhRegisterCallbackEx
PhRemoveElementAvlTree
PhRemoveEntryHashtable
PhRemoveItemArray
PhRemoveItemList
PhRemoveItemPointerList
PhRemoveItemSimpleHashtable
PhRemoveItemsArray
PhRemoveItemsList
PhRemoveStringBuilder
PhResizeArray
PhResizeList
PhRoundUpToPowerOfTwo
PhSecondsSince1970ToTime
PhSplitStringRefAtChar
PhSplitStringRefAtLastChar
PhSplitStringRefAtString
PhSplitStringRefEx
PhSplitStringRef
PhFreeStringArray
PhStringFuzzyMatch
PhStringToDouble
PhStringToInteger64
PhStringToUInt64
PhSuccessorElementAvlTree
PhSystemTimeToLocalTime
PhTimeToSecondsSince1970
PhTrimStringRef
PhUnregisterCallback
PhUpperBoundElementAvlTree
PhUpperDualBoundElementAvlTree
PhWriteUnicodeDecoder
PhZeroExtendToUtf16Buffer
PhZeroExtendToUtf16Ex
PhfAcquireRundownProtection
PhfBeginInitOnce
PhfEndInitOnce
PhfInitializeBarrier
PhfInitializeEvent
PhfInitializeInitOnce
PhfInitializeRundownProtection
PhfReleaseRundownProtection
PhfResetEvent
PhfSetEvent
PhfWaitForBarrier
PhfWaitForEvent
PhfWaitForRundownProtection
; phfirmware
PhEnumSMBIOS
PhGetSMBIOSString
; phnative
PhAdjustPrivilege
PhConnectPipe
PhCreateDirectoryFullPathWin32
PhCreateDirectoryWin32
PhCreateEvent
PhCreateFile
PhCreateFileWin32
PhCreateFileWin32Ex
PhCreateKey
PhCreateNamedPipe
PhCreatePipe
PhDeleteDirectoryWin32
PhDeleteFile
PhDeleteFileWin32
PhDeleteValueKey
PhDestroyWindowRemote
PhDetermineDosPathNameType
PhDeviceIoControlFile
PhDisconnectNamedPipe
PhDoesFileExist
PhDoesFileExistWin32
PhDosPathNameToNtPathName
PhEnumBigPoolInformation
PhEnumDirectoryFile
PhEnumDirectoryObjects
PhEnumFileStreams
PhEnumFirmwareEnvironmentValues
PhEnumGenericModules
PhEnumHandlesEx
PhEnumKernelModules
PhEnumNextProcess
PhEnumNextThread
PhEnumObjectIdInformation
PhEnumPagefiles
PhEnumPoolTagInformation
PhEnumProcessEnvironmentVariables
PhEnumProcesses
PhEnumProcessesEx
PhEnumReparsePointInformation
PhEnumerateKey
PhEnumerateValueKey
PhEnumerateValueKeyEx
PhFilterConnectCommunicationPort
PhFindProcessInformation
PhFindProcessInformationByImageName
PhGetActiveProcessorCount
PhGetContextThread
PhGetDeviceType
PhGetDllHandle
PhGetDriverImageFileName
PhGetDriverName
PhGetDriverServiceKeyName
PhGetFileFullAttributesInformation
PhGetFileName
PhGetFilePosition
PhGetFileSize
PhGetFirmwareEnvironmentVariable
PhGetJobProcessIdList
PhGetKernelFileName
PhGetKernelFileNameEx
PhGetModuleProcAddress
PhGetNamedPipeClientComputerName
PhGetNamedPipeClientProcessId
PhGetNamedPipeServerProcessId
PhGetObjectSecurity
PhGetObjectTypeIndexName
PhGetObjectTypeNumber
PhGetOwnTokenAttributes
PhGetPnPDeviceName
PhGetProcedureAddress
PhGetProcedureAddressRemote
PhGetProcessAffinityMask
PhGetProcessCommandLine
PhGetProcessDepStatus
PhGetProcessDeviceMap
PhGetProcessEnvironment
PhGetProcessExtendedBasicInformation
PhGetProcessGroupAffinity
PhGetProcessGroupInformation
PhGetProcessImageFileName
PhGetProcessImageFileNameById
PhGetProcessImageFileNameByProcessId
PhGetProcessImageFileNameWin32
PhGetProcessIoPriority
PhGetProcessIsDotNet
PhGetProcessIsDotNetEx
PhGetProcessIsTerminated
PhGetProcessIsWow64
PhGetProcessMappedFileName
PhGetProcessPagePriority
PhGetProcessPebString
PhGetProcessPowerThrottlingState
PhGetProcessPriorityBoost
PhGetProcessPriorityClass
PhGetProcessUnloadedDlls
PhGetProcessWindowTitle
PhGetProcessWorkingSetInformation
PhGetProcessWsCounters
PhGetSectionFileName
PhGetThreadBasePriority
PhGetThreadBasicInformation
PhGetThreadIsTerminated
PhGetThreadIoPriority
PhGetThreadName
PhGetTokenAppContainerSid
PhGetTokenGroups
PhGetTokenIntegrityLevel
PhGetTokenIntegrityLevelRID
PhGetTokenOwner
PhGetTokenPackageFullName
PhGetTokenPrimaryGroup
PhGetTokenPrivileges
PhGetTokenUser
PhImpersonateToken
PhIsDebuggerPresent
PhIsFirmwareSupported
PhListenNamedPipe
PhLoadAppKey
PhMoveFileWin32
PhOpenDirectoryObject
PhOpenDriver
PhOpenFile
PhOpenFileById
PhOpenKey
PhOpenProcess
PhOpenProcessClientId
PhOpenProcessToken
PhOpenThread
PhOpenThreadClientId
PhOpenThreadProcess
PhPeekNamedPipe
PhQueryEnvironmentVariable
PhQueryFullAttributesFileWin32
PhQueryKey
PhQueryKeyLastWriteTime
PhQuerySymbolicLinkObject
PhQueryTokenVariableSize
PhQueryValueKey
PhQueueUserWorkItem
PhReadFile
PhResolveDevicePrefix
PhRevertImpersonationToken
PhSetFileAllocationSize
PhSetFileIoPriorityHint
PhSetFilePosition
PhSetFileSize
PhSetFirmwareEnvironmentVariable
PhSetObjectSecurity
PhSetProcessPowerThrottlingState
PhSetProcessPriorityBoost
PhSetProcessPriorityClass
PhSetThreadBasePriority
PhSetThreadIoPriority
PhSetThreadName
PhSetTokenIsVirtualizationEnabled
PhSetTokenPrivilege
PhSetTokenPrivilege2
PhSetTokenSessionId
PhSetValueKey
PhTerminateProcess
PhTraceControl
PhTransceiveNamedPipe
PhUnloadDllProcess
PhUnloadDriver
PhUpdateDosDevicePrefixes
PhUpdateMupDevicePrefixes
PhWaitForNamedPipe
PhWriteFile
; phutil
PhAdjustRectangleToBounds
PhAdjustRectangleToWorkingArea
PhCenterRectangle
PhCenterWindow
PhCompareUnicodeStringZIgnoreMenuPrefix
PhConsoleSetForeground
PhConsoleSetWindow
PhCreateOpenFileDialog
PhCreateProcess
PhCreateProcessAsUser
PhCreateProcessRedirection
PhCreateProcessWin32
PhCreateProcessWin32Ex
PhCreateSaveFileDialog
PhDeleteImageVersionInfo
PhDevFreeObjectProperties
PhDevFreeObjects
PhDevGetObjectProperties
PhDevGetObjects
PhEllipsisString
PhEllipsisStringPath
PhEscapeCommandLinePart
PhEscapeStringForMenuPrefix
PhExpandEnvironmentStrings
PhFileReadAllTextWin32
PhFinalHash
PhFormatDate
PhFormatDateTime
PhFormatDecimal
PhFormatGuid
PhFormatImageVersionInfo
PhFormatSize
PhFormatTime
PhFormatTimeSpan
PhFormatTimeSpanRelative
PhFormatUInt64
PhFreeFileDialog
PhFreeLibrary
PhFreeLibraryAsImageResource
PhGenerateGuid
PhGenerateGuidFromName
PhGenerateRandomAlphaString
PhGenerateRandomNumber64
PhGetApplicationDataFileName
PhGetApplicationDirectory
PhGetApplicationDirectoryFileName
PhGetApplicationDirectoryWin32
PhGetApplicationFileNameWin32
PhGetApplicationFileName
PhGetBaseDirectory
PhGetBaseName
PhGetClassObject
PhGetDllFileName
PhGetDpiValue
PhGetFileDialogFileName
PhGetFileDialogFilterIndex
PhGetFileDialogOptions
PhGetFileVersionFixedInfo
PhGetFileVersionInfo
PhGetFileVersionInfoEx
PhGetFileVersionInfoLangCodePage
PhGetFileVersionInfoString
PhGetFileVersionInfoString2
PhGetFullPath
PhGetKnownFolderPath
PhGetKnownLocation
PhGetMessage
PhGetMonitorDpi
PhGetNtMessage
PhGetNtSystemRoot
PhGetRoamingAppDataDirectory
PhGetStatusMessage
PhGetSystemDirectory
PhGetSystemDpi
PhGetSystemMetrics
PhGetSystemParametersInfo
PhGetSystemRoot
PhGetTaskbarDpi
PhGetTemporaryDirectoryRandomAlphaFileName
PhGetUserLocaleInfoBool
PhGetWin32Message
PhGetWindowDpi
PhInitializeHash
PhInitializeImageVersionInfo
PhInitializeImageVersionInfoEx
PhInitializeProcThreadAttributeList
PhLargeIntegerToLocalSystemTime
PhLargeIntegerToSystemTime
PhLoadLibrary
PhLoadLibraryAsImageResource
PhMapFlags1
PhMapFlags2
PhMatchWildcards
PhParseCommandLine
PhParseCommandLineFuzzy
PhParseCommandLinePart
PhQueryRegistryString
PhQueryRegistryUlong
PhQueryRegistryUlong64
PhSetFileDialogFileName
PhSetFileDialogFilter
PhSetFileDialogOptions
PhShellExecute
PhShellExecuteEx
PhShellExploreFile
PhShellProperties
PhShowConfirmMessage
PhShowContinueStatus
PhShowFileDialog
PhShowMessage
PhShowMessage2
PhShowMessageOneTime
PhShowStatus
PhShowTaskDialog
PhStringToGuid
PhSystemTimeToLargeInteger
PhSystemTimeToTzSpecificLocalTime
PhTaskbarListCreate
PhTaskbarListDestroy
PhTaskbarListSetOverlayIcon
PhTaskbarListSetProgressState
PhTaskbarListSetProgressValue
PhUpdateHash
PhUpdateProcThreadAttribute
PhWaitForMultipleObjectsAndPump
; circbuf
PhClearCircularBuffer_FLOAT
PhClearCircularBuffer_PVOID
PhClearCircularBuffer_ULONG
PhClearCircularBuffer_ULONG64
PhCopyCircularBuffer_FLOAT
PhCopyCircularBuffer_PVOID
PhCopyCircularBuffer_ULONG
PhCopyCircularBuffer_ULONG64
PhDeleteCircularBuffer_FLOAT
PhDeleteCircularBuffer_PVOID
PhDeleteCircularBuffer_ULONG
PhDeleteCircularBuffer_ULONG64
PhInitializeCircularBuffer_FLOAT
PhInitializeCircularBuffer_PVOID
PhInitializeCircularBuffer_ULONG
PhInitializeCircularBuffer_ULONG64
PhResizeCircularBuffer_FLOAT
PhResizeCircularBuffer_PVOID
PhResizeCircularBuffer_ULONG
PhResizeCircularBuffer_ULONG64
; cpysave
PhGetGenericTreeNewLines
PhGetListViewItemText
PhGetListViewSelectedItemText
PhGetTreeNewText
; emenu
PhCreateEMenu
PhCreateEMenuItem
PhDestroyEMenu
PhDestroyEMenuItem
PhFindEMenuItem
PhIndexOfEMenuItem
PhInsertEMenuItem
PhLoadResourceEMenuItem
PhModifyEMenuItem
PhRemoveAllEMenuItems
PhRemoveEMenuItem
PhSetFlagsAllEMenuItems
PhSetFlagsEMenuItem
PhShowEMenu
; fastlock
PhDeleteFastLock
PhInitializeFastLock
PhfAcquireFastLockExclusive
PhfAcquireFastLockShared
PhfReleaseFastLockExclusive
PhfReleaseFastLockShared
PhfTryAcquireFastLockExclusive
PhfTryAcquireFastLockShared
; filestream
PhCreateFileStream
PhCreateFileStream2
PhFlushFileStream
PhGetPositionFileStream
PhLockFileStream
PhReadFileStream
PhSeekFileStream
PhUnlockFileStream
PhVerifyFileStream
PhWriteFileStream
PhWriteStringAsUtf8FileStream
PhWriteStringAsUtf8FileStreamEx
PhWriteStringFormatAsUtf8FileStream
PhWriteStringFormatAsUtf8FileStream_V
; graph
PhDeleteGraphState
PhDrawGraphDirect
PhDrawTrayIconText
PhGetDrawInfoGraphBuffers
PhGraphStateGetDrawInfo
PhInitializeGraphState
PhNfGetTrayIconFont
PhSetGraphText
; guisup
PhAddLayoutItem
PhAddLayoutItemEx
PhAddListViewColumn
PhAddListViewGroup
PhAddListViewGroupItem
PhAddListViewItem
PhAddTabControlTab
PhBitmapSetAlpha
PhCloseThemeData
PhCreateCommonFont
PhCreateDialog
PhCreateDIBSection
PhCreateIconTitleFont
PhCreateWindowEx
PhDeleteLayoutManager
PhDialogBox
PhDuplicateFont
PhDuplicateFontWithNewWeight
PhEnumChildWindows
PhEnumWindows
PhEnumWindowsEx
PhFindListViewItemByFlags
PhFindListViewItemByParam
PhGetClassName
PhGetComboBoxString
PhGetDialogItemValue
PhGetListBoxString
PhGetListViewItemImageIndex
PhGetListViewItemParam
PhGetSelectedListViewItemParam
PhGetSelectedListViewItemParams
PhGetStockApplicationIcon
PhGetStockObject
PhGetThemeMargins
PhGetThemePartSize
PhGetWindowClientId
PhGetWindowCompositionAttribute
PhGetWindowContext
PhGetWindowText
PhGetWindowTextEx
PhGetWindowTextToBuffer
PhIconToBitmap
PhImageListAddBitmap
PhImageListAddIcon
PhImageListCreate
PhImageListDestroy
PhImageListDrawEx
PhImageListDrawIcon
PhImageListGetIcon
PhImageListRemoveIcon
PhImageListReplace
PhImageListSetBkColor
PhImageListSetIconSize
PhImageListSetImageCount
PhInitializeLayoutManager
PhIsThemeActive
PhLayoutManagerUpdate
PhLayoutManagerLayout
PhLoadIcon
PhLoadImageFormatFromResource
PhModalPropertySheet
PhOpenThemeData
PhQueryDirectXExclusiveOwnership
PhInitializeWindowTheme
PhReInitializeWindowTheme
PhRegisterWindowCallback
PhRemoveListViewItem
PhRemoveWindowContext
PhSelectComboBoxString
PhSetClipboardString
PhSetControlTheme
PhSetDialogItemText
PhSetDialogItemValue
PhSetExtendedListView
PhSetGroupBoxText
PhSetHeaderSortIcon
PhSetImageListBitmap
PhSetListViewItemImageIndex
PhSetListViewItemParam
PhSetListViewSubItem
PhSetStateAllListViewItems
PhSetWindowContext
PhSetWindowText
PhTerminateWindow
PhThemeWindowDrawRebar
PhThemeWindowDrawToolbar
PhUnregisterWindowCallback
PhUserQueryWindow
PhWindowThemeControlColor
; hndlinfo
PhCallKphQueryFileInformationWithTimeout
PhCompareObjects
PhEnumObjectTypes
PhFormatNativeKeyName
PhGetEtwPublisherName
PhGetHandleInformation
PhGetHandleInformationEx
PhGetObjectTypeName
PhQueryObjectName
PhStdGetClientIdName
; lsasup
PhEnumeratePrivileges
PhGetSidFullName
PhLookupName
PhLookupPrivilegeDisplayName
PhLookupPrivilegeName
PhLookupPrivilegeValue
PhLookupSid
PhOpenLsaPolicy
PhSidToStringSid
; mapimg
PhLoadMappedImage
PhLoadMappedImageEx
PhLoadMappedImageHeaderPageSize
PhUnloadMappedImage
; provider
PhBoostProvider
PhDeleteProviderThread
PhGetEnabledProvider
PhInitializeProviderThread
PhRegisterProvider
PhSetEnabledProvider
PhSetIntervalProviderThread
PhStartProviderThread
PhStopProviderThread
PhUnregisterProvider
; settings
PhAddSetting
PhAddSettings
PhClearIgnoredSettings
PhConvertIgnoredSettings
PhGetIntegerPairStringRefSetting
PhGetIntegerStringRefSetting
PhGetScalableIntegerPairStringRefSetting
PhGetStringRefSetting
PhLoadCustomColorList
PhLoadListViewColumnSettings
PhLoadListViewColumnsFromSetting
PhLoadListViewGroupStatesFromSetting
PhLoadListViewSortColumnsFromSetting
PhLoadSettings
PhLoadWindowPlacementFromSetting
PhResetSettings
PhSaveCustomColorList
PhSaveListViewColumnSettings
PhSaveListViewColumnsToSetting
PhSaveListViewGroupStatesToSetting
PhSaveListViewSortColumnsToSetting
PhSaveSettings
PhSaveWindowPlacementToSetting
PhSetIntegerPairStringRefSetting
PhSetIntegerStringRefSetting
PhSetScalableIntegerPairStringRefSetting
PhSetScalableIntegerPairStringRefSetting2
PhSetStringRefSetting
PhUpdateCachedSettings
; secedit
PhCreateSecurityPage
PhEditSecurity
PhGetAccessEntries
PhGetAccessString
PhGetSeObjectSecurity
PhSetSeObjectSecurity
PhStdGetObjectSecurity
PhStdSetObjectSecurity
; svcsup
PhChangeServiceConfig2
PhCloseServiceHandle
PhEnumDependentServices
PhGetServiceConfig
PhGetServiceConfigFileName
PhGetServiceDescription
PhGetServiceDllParameter
PhGetServiceErrorControlInteger
PhGetServiceErrorControlString
PhGetServiceFileName
PhGetServiceNameFromTag
PhGetServiceStartTypeInteger
PhGetServiceStartTypeString
PhGetServiceStateString
PhGetServiceTypeInteger
PhGetServiceTypeString
PhGetThreadServiceTag
PhOpenService
PhOpenServiceKey
PhQueryServiceConfig
PhQueryServiceConfig2
PhQueryServiceStatus
PhQueryServiceVariableSize
PhStartService
PhStopService
; symprv
PhCreateSymbolProvider
PhGetLineFromAddress
PhGetModuleFromAddress
PhGetSymbolFromAddress
PhGetSymbolFromName
PhLoadSymbolProviderModules
PhLoadModuleSymbolProvider
PhLoadModulesForVirtualSymbolProvider
PhSetOptionsSymbolProvider
PhSetSearchPathSymbolProvider
PhStackWalk
PhWalkThreadStack
PhWriteMiniDumpProcess
; verify
PhVerifyFile
PhVerifyFileIsChainedToMicrosoft
; workqueue
PhDeleteWorkQueue
PhGetGlobalWorkQueue
PhInitializeWorkQueue
PhInitializeWorkQueueEnvironment
PhQueueItemWorkQueue
PhQueueItemWorkQueueEx
PhWaitForWorkQueue
; json
PhAddJsonArrayObject
PhAddJsonObject
PhAddJsonObject2
PhAddJsonObjectUtf8
PhAddJsonObjectInt64
PhAddJsonObjectUInt64
PhAddJsonObjectValue
PhCreateJsonArray
PhCreateJsonObject
PhCreateJsonParser
PhCreateJsonParserEx
PhFreeJsonObject
PhGetJsonArrayIndexObject
PhGetJsonArrayLength
PhGetJsonArrayLong64
PhGetJsonArrayString
PhGetJsonObject
PhGetJsonObjectAsArrayList
PhGetJsonObjectBool
PhGetJsonObjectLength
PhGetJsonObjectType
PhGetJsonValueAsInt64
PhGetJsonValueAsString
PhGetJsonValueAsUInt64
PhLoadJsonObjectFromFile
PhSaveJsonObjectToFile
; xml
PhCreateXmlNode
PhCreateXmlOpaqueNode
PhFindXmlObject
PhFreeXmlObject
PhGetXmlNodeAttributeByIndex
PhGetXmlNodeAttributeCount
PhGetXmlNodeAttributeText
PhGetXmlNodeElementText
PhGetXmlNodeFirstChild
PhGetXmlNodeNextChild
PhGetXmlNodeOpaqueText
PhGetXmlObject
PhLoadXmlObjectFromFile
PhLoadXmlObjectFromString
PhSaveXmlObjectToFile
PhSetXmlNodeAttributeText
; cache
PhClearCacheDirectory
PhCreateCacheFile
PhDeleteCacheFile
; appresolver
PhAppResolverGetAppIdForWindow
PhGetProcessPackageFullName
; http
PhDnsFree
PhDnsQuery
PhDnsQuery2
PhDnsReverseLookupNameFromAddress
PhHttpAddRequestHeaders
PhHttpBeginRequest
PhHttpClose
PhHttpConnect
PhHttpCrackUrl
PhHttpInitialize
PhHttpDestroy
PhHttpDownloadString
PhHttpReceiveResponse
PhHttpGetErrorMessage
PhHttpQueryHeaderString
PhHttpQueryHeaderUlong
PhHttpQueryHeaderUlong64
PhHttpQueryHeaders
PhHttpQueryOptionString
PhHttpReadData
PhHttpReadDataToBuffer
PhHttpSendRequest
PhHttpSetCredentials
PhHttpSetFeature
PhHttpSetSecurity
PhHttpSetProtocol
PhHttpWriteData
PhIpv4AddressToString
PhIpv6AddressToString
PhIpv4StringToAddress
PhIpv6StringToAddress
; ksiuser
KsiLevel
KphAlpcQueryInformation
KphDuplicateObject
KphOpenDevice
KphOpenDeviceBaseDevice
KphOpenDeviceDriver
KphQueryInformationDriver
KphQueryInformationObject
KsiEnumerateProcessHandles
KsiQueryHashInformationFile
================================================
FILE: SystemInformer/SystemInformer.def.h
================================================
/*
* Copyright (c) 2022 Winsider Seminars & Solutions, Inc. All rights reserved.
*
* This file is part of System Informer.
*
* Authors:
*
* wj32 2008-2016
* dmex 2017-2024
*
*
* This file was automatically generated.
*
* Do not link at runtime. Use the SystemInformer.def.h header file instead.
*
*/
#pragma once
#ifndef _PH_EXPORT_DEF_H
#define _PH_EXPORT_DEF_H
#define EXPORT_PHADDPROCESSPROPPAGE 1001
#define EXPORT_PHADDPROCESSPROPPAGE2 1002
#define EXPORT_PHADDPROPPAGELAYOUTITEM 1003
#define EXPORT_PHADDTREENEWFILTER 1004
#define EXPORT_PHAPPLYTREENEWFILTERS 1005
#define EXPORT_PHAPPLYTREENEWFILTERSTONODE 1006
#define EXPORT_PHCHOICEDIALOG 1007
#define EXPORT_PHCMLOADSETTINGS 1008
#define EXPORT_PHCMSAVESETTINGS 1009
#define EXPORT_PHCOPYLISTVIEW 1010
#define EXPORT_PHCOPYLISTVIEWINFOTIP 1011
#define EXPORT_PHCREATEKSISETTINGSBLOB 1012
#define EXPORT_PHCREATEPROCESSPROPCONTEXT 1013
#define EXPORT_PHCREATEPROCESSPROPPAGECONTEXT 1014
#define EXPORT_PHCREATEPROCESSPROPPAGECONTEXTEX 1015
#define EXPORT_PHCREATESEARCHCONTROL 1016
#define EXPORT_PHCREATESERVICELISTCONTROL 1017
#define EXPORT_PHDELETEMEMORYITEMLIST 1018
#define EXPORT_PHDELETESTATICWINDOWICON 1019
#define EXPORT_PHDELETETREENEWCOLUMNMENU 1020
#define EXPORT_PHDELETETREENEWFILTERSUPPORT 1021
#define EXPORT_PHDEREFERENCEPROCESSRECORD 1022
#define EXPORT_PHDESELECTALLPROCESSNODES 1023
#define EXPORT_PHDESELECTALLSERVICENODES 1024
#define EXPORT_PHDESTROYWINDOWICON 1025
#define EXPORT_PHDEVICEPROVIDERINITIALIZATION 1026
#define EXPORT_PHDOPROPPAGELAYOUT 1027
#define EXPORT_PHDUPLICATEPROCESSINFORMATION 1028
#define EXPORT_PHDUPLICATEPROCESSNODELIST 1029
#define EXPORT_PHENUMNETWORKITEMS 1030
#define EXPORT_PHENUMNETWORKITEMSBYPROCESSID 1031
#define EXPORT_PHENUMPROCESSITEMS 1032
#define EXPORT_PHEXECUTERUNASCOMMAND2 1033
#define EXPORT_PHEXECUTERUNASCOMMAND3 1034
#define EXPORT_PHEXPANDALLPROCESSNODES 1035
#define EXPORT_PHFINDNETWORKNODE 1036
#define EXPORT_PHFINDPROCESSNODE 1037
#define EXPORT_PHFINDPROCESSRECORD 1038
#define EXPORT_PHFINDSERVICENODE 1039
#define EXPORT_PHFORMATLOGENTRY 1040
#define EXPORT_PHGETAPPLICATIONICON 1041
#define EXPORT_PHGETAPPLICATIONICONEX 1042
#define EXPORT_PHGETCLIENTIDNAME 1043
#define EXPORT_PHGETCLIENTIDNAMEEX 1044
#define EXPORT_PHGETDEVICEICON 1045
#define EXPORT_PHGETDEVICEPROPERTY 1046
#define EXPORT_PHGETFILTERSUPPORTNETWORKTREELIST 1047
#define EXPORT_PHGETFILTERSUPPORTPROCESSTREELIST 1048
#define EXPORT_PHGETFILTERSUPPORTSERVICETREELIST 1049
#define EXPORT_PHGETGENERALCALLBACK 1050
#define EXPORT_PHGETIMAGELISTICON 1051
#define EXPORT_PHGETLISTVIEWCONTEXTMENUPOINT 1052
#define EXPORT_PHGETPHRELEASECHANNEL 1053
#define EXPORT_PHGETPHRELEASECHANNELSTRING 1054
#define EXPORT_PHGETPHVERSION 1055
#define EXPORT_PHGETPHVERSIONHASH 1056
#define EXPORT_PHGETPHVERSIONNUMBERS 1057
#define EXPORT_PHGETPROCESSISSUSPENDED 1058
#define EXPORT_PHGETPROCESSKNOWNTYPE 1059
#define EXPORT_PHGETPROCESSKNOWNTYPEEX 1060
#define EXPORT_PHGETPROCESSPRIORITYCLASSSTRING 1061
#define EXPORT_PHGETPROCESSSMALLIMAGELIST 1062
#define EXPORT_PHGETPROTOCOLTYPENAME 1063
#define EXPORT_PHGETSELECTEDANDPROPAGATEPROCESSITEMS 1064
#define EXPORT_PHGETSELECTEDPROCESSITEM 1065
#define EXPORT_PHGETSELECTEDPROCESSITEMS 1066
#define EXPORT_PHGETSELECTEDPROCESSNODES 1067
#define EXPORT_PHGETSELECTEDSERVICEITEM 1068
#define EXPORT_PHGETSELECTEDSERVICEITEMS 1069
#define EXPORT_PHGETSERVICECHANGE 1070
#define EXPORT_PHGETSHIELDBITMAP 1071
#define EXPORT_PHGETSTATISTICSTIME 1072
#define EXPORT_PHGETSTATISTICSTIMESTRING 1073
#define EXPORT_PHGETTCPSTATENAME 1074
#define EXPORT_PHHANDLECOPYCELLEMENUITEM 1075
#define EXPORT_PHHANDLECOPYLISTVIEWEMENUITEM 1076
#define EXPORT_PHHANDLELISTVIEWNOTIFYBEHAVIORS 1077
#define EXPORT_PHHANDLELISTVIEWNOTIFYFORCOPY 1078
#define EXPORT_PHHANDLETREENEWCOLUMNMENU 1079
#define EXPORT_PHIMAGELISTEXTRACTICON 1080
#define EXPORT_PHIMAGELISTFLUSHCACHE 1081
#define EXPORT_PHINITIALIZETREENEWCOLUMNMENU 1082
#define EXPORT_PHINITIALIZETREENEWCOLUMNMENUEX 1083
#define EXPORT_PHINITIALIZETREENEWFILTERSUPPORT 1084
#define EXPORT_PHINSERTCOPYCELLEMENUITEM 1085
#define EXPORT_PHINSERTCOPYLISTVIEWEMENUITEM 1086
#define EXPORT_PHINSERTHANDLEOBJECTPROPERTIESEMENUITEMS 1087
#define EXPORT_PHINVALIDATEALLPROCESSNODES 1088
#define EXPORT_PHISPROCESSSUSPENDED 1089
#define EXPORT_PHLOADSYMBOLPROVIDEROPTIONS 1090
#define EXPORT_PHLOGMESSAGEENTRY 1091
#define EXPORT_PHLOOKUPDEVICEITEM 1092
#define EXPORT_PHLOOKUPDEVICEITEMBYHASH 1093
#define EXPORT_PHLOOKUPDEVICEPROPERTYCLASS 1094
#define EXPORT_PHLOOKUPMEMORYITEMLIST 1095
#define EXPORT_PHPROCESSIMAGELISTINITIALIZATION 1096
#define EXPORT_PHPROPPAGEDLGPROCHEADER 1097
#define EXPORT_PHQUERYMEMORYITEMLIST 1098
#define EXPORT_PHREFERENCEDEVICEITEM 1099
#define EXPORT_PHREFERENCEDEVICEITEM2 1100
#define EXPORT_PHREFERENCEDEVICEITEMBYHASH 1101
#define EXPORT_PHREFERENCEDEVICETREE 1102
#define EXPORT_PHREFERENCEDEVICETREEEX 1103
#define EXPORT_PHREFERENCENETWORKITEM 1104
#define EXPORT_PHREFERENCEPROCESSITEM 1105
#define EXPORT_PHREFERENCEPROCESSITEMFORPARENT 1106
#define EXPORT_PHREFERENCEPROCESSITEMFORRECORD 1107
#define EXPORT_PHREFERENCEPROCESSRECORD 1108
#define EXPORT_PHREFERENCEPROCESSRECORDFORSTATISTICS 1109
#define EXPORT_PHREFERENCEPROCESSRECORDSAFE 1110
#define EXPORT_PHREFERENCESERVICEITEM 1111
#define EXPORT_PHREGISTERDIALOG 1112
#define EXPORT_PHREGISTERMESSAGELOOPFILTER 1113
#define EXPORT_PHREMOVETREENEWFILTER 1114
#define EXPORT_PHSEARCHCONTROLMATCH 1115
#define EXPORT_PHSEARCHCONTROLMATCHLONGHINTZ 1116
#define EXPORT_PHSEARCHCONTROLMATCHPOINTER 1117
#define EXPORT_PHSEARCHCONTROLMATCHPOINTERRANGE 1118
#define EXPORT_PHSEARCHCONTROLMATCHZ 1119
#define EXPORT_PHSEARCHONLINESTRING 1120
#define EXPORT_PHSELECTANDENSUREVISIBLEPROCESSNODE 1121
#define EXPORT_PHSELECTANDENSUREVISIBLESERVICENODE 1122
#define EXPORT_PHSETAPPLICATIONWINDOWICON 1123
#define EXPORT_PHSETAPPLICATIONWINDOWICONEX 1124
#define EXPORT_PHSETPROCESSITEMAFFINITYMASK 1125
#define EXPORT_PHSETPROCESSITEMIOPRIORITY 1126
#define EXPORT_PHSETPROCESSITEMPAGEPRIORITY 1127
#define EXPORT_PHSETPROCESSITEMPRIORITY 1128
#define EXPORT_PHSETPROCESSITEMPRIORITYBOOST 1129
#define EXPORT_PHSETPROCESSITEMTHROTTLINGSTATE 1130
#define EXPORT_PHSETSELECTTHREADIDPROCESSPROPCONTEXT 1131
#define EXPORT_PHSETSTATICWINDOWICON 1132
#define EXPORT_PHSETWINDOWICON 1133
#define EXPORT_PHSHELLEXECUTEUSERSTRING 1134
#define EXPORT_PHSHELLOPENKEY 1135
#define EXPORT_PHSHELLOPENKEY2 1136
#define EXPORT_PHSHELLPROCESSHACKER 1137
#define EXPORT_PHSHOWCHOOSEPROCESSDIALOG 1138
#define EXPORT_PHSHOWHANDLEOBJECTPROPERTIES1 1139
#define EXPORT_PHSHOWHANDLEOBJECTPROPERTIES2 1140
#define EXPORT_PHSHOWICONNOTIFICATION 1141
#define EXPORT_PHSHOWPROCESSAFFINITYDIALOG2 1142
#define EXPORT_PHSHOWPROCESSPROPERTIES 1143
#define EXPORT_PHSHOWPROCESSRECORDDIALOG 1144
#define EXPORT_PHSHOWSYSTEMINFORMATIONDIALOG 1145
#define EXPORT_PHSHOWTHREADAFFINITYDIALOG 1146
#define EXPORT_PHSIDOUBLELABELYFUNCTION 1147
#define EXPORT_PHSISETCOLORSGRAPHDRAWINFO 1148
#define EXPORT_PHSISIZELABELYFUNCTION 1149
#define EXPORT_PHSIUINT64LABELYFUNCTION 1150
#define EXPORT_PHUNREGISTERDIALOG 1151
#define EXPORT_PHUNREGISTERMESSAGELOOPFILTER 1152
#define EXPORT_PHUPDATEPROCESSNODE 1153
#define EXPORT_PHUPDATESERVICENODE 1154
#define EXPORT_PHWORDMATCHSTRINGREF 1155
#define EXPORT_PHWRITEPHTEXTHEADER 1156
#define EXPORT_PHACHOICEDIALOG 1157
#define EXPORT_PHAGETPROCESSKNOWNCOMMANDLINE 1158
#define EXPORT_PHENUMERATEPLUGINS 1159
#define EXPORT_PHFINDPLUGIN2 1160
#define EXPORT_PHGETPLUGINCALLBACK 1161
#define EXPORT_PHGETPLUGINFILENAME 1162
#define EXPORT_PHGETPLUGININFORMATION 1163
#define EXPORT_PHGETPLUGININTERFACE 1164
#define EXPORT_PHGETPLUGINNAME 1165
#define EXPORT_PHPLUGINADDMENUHOOK 1166
#define EXPORT_PHPLUGINADDTREENEWCOLUMN 1167
#define EXPORT_PHPLUGINCALLPHSVC 1168
#define EXPORT_PHPLUGINCREATEEMENUITEM 1169
#define EXPORT_PHPLUGINCREATETABPAGE 1170
#define EXPORT_PHPLUGINENABLETREENEWNOTIFY 1171
#define EXPORT_PHPLUGINGETOBJECTEXTENSION 1172
#define EXPORT_PHPLUGINGETSYSTEMSTATISTICS 1173
#define EXPORT_PHPLUGININVOKEWINDOWCALLBACK 1174
#define EXPORT_PHPLUGINQUERYPHSVC 1175
#define EXPORT_PHPLUGINRESERVEIDS 1176
#define EXPORT_PHPLUGINSETOBJECTEXTENSION 1177
#define EXPORT_PHREGISTERPLUGIN 1178
#define EXPORT_PHSVCCALLCHANGESERVICECONFIG 1179
#define EXPORT_PHSVCCALLCHANGESERVICECONFIG2 1180
#define EXPORT_PHSVCCALLPOSTMESSAGE 1181
#define EXPORT_PHSVCCALLSENDMESSAGE 1182
#define EXPORT_PHUICLOSECONNECTIONS 1183
#define EXPORT_PHUICLOSEHANDLES 1184
#define EXPORT_PHUICONNECTSESSION 1185
#define EXPORT_PHUICONNECTTOPHSVC 1186
#define EXPORT_PHUICONNECTTOPHSVCEX 1187
#define EXPORT_PHUICONTINUESERVICE 1188
#define EXPORT_PHUICONTINUESERVICES 1189
#define EXPORT_PHUIDEBUGPROCESS 1190
#define EXPORT_PHUIDELETESERVICE 1191
#define EXPORT_PHUIDETACHFROMDEBUGGERPROCESS 1192
#define EXPORT_PHUIDISCONNECTFROMPHSVC 1193
#define EXPORT_PHUIDISCONNECTSESSION 1194
#define EXPORT_PHUIFLUSHHEAPPROCESSES 1195
#define EXPORT_PHUIFREEMEMORY 1196
#define EXPORT_PHUIFREEZETREEPROCESS 1197
#define EXPORT_PHUIHIBERNATECOMPUTER 1198
#define EXPORT_PHUILOADDLLPROCESS 1199
#define EXPORT_PHUILOCKCOMPUTER 1200
#define EXPORT_PHUILOGOFFCOMPUTER 1201
#define EXPORT_PHUILOGOFFSESSION 1202
#define EXPORT_PHUIPAUSESERVICE 1203
#define EXPORT_PHUIPAUSESERVICES 1204
#define EXPORT_PHUIREDUCEWORKINGSETPROCESSES 1205
#define EXPORT_PHUIRESTARTCOMPUTER 1206
#define EXPORT_PHUIRESTARTPROCESS 1207
#define EXPORT_PHUIRESUMEPROCESSES 1208
#define EXPORT_PHUIRESUMETHREADS 1209
#define EXPORT_PHUIRESUMETREEPROCESS 1210
#define EXPORT_PHUISETATTRIBUTESHANDLE 1211
#define EXPORT_PHUISETBOOSTPRIORITYPROCESS 1212
#define EXPORT_PHUISETBOOSTPRIORITYPROCESSES 1213
#define EXPORT_PHUISETBOOSTPRIORITYTHREAD 1214
#define EXPORT_PHUISETBOOSTPRIORITYTHREADS 1215
#define EXPORT_PHUISETCRITICALPROCESS 1216
#define EXPORT_PHUISETECOMODEPROCESS 1217
#define EXPORT_PHUISETEXECUTIONREQUIREDPROCESS 1218
#define EXPORT_PHUISETIOPRIORITYPROCESSES 1219
#define EXPORT_PHUISETIOPRIORITYTHREAD 1220
#define EXPORT_PHUISETPAGEPRIORITYPROCESS 1221
#define EXPORT_PHUISETPAGEPRIORITYTHREAD 1222
#define EXPORT_PHUISETPRIORITYPROCESSES 1223
#define EXPORT_PHUISETPRIORITYTHREAD 1224
#define EXPORT_PHUISETPRIORITYTHREADS 1225
#define EXPORT_PHUISETVIRTUALIZATIONPROCESS 1226
#define EXPORT_PHUISHUTDOWNCOMPUTER 1227
#define EXPORT_PHUISLEEPCOMPUTER 1228
#define EXPORT_PHUISTARTSERVICE 1229
#define EXPORT_PHUISTARTSERVICES 1230
#define EXPORT_PHUISTOPSERVICE 1231
#define EXPORT_PHUISTOPSERVICES 1232
#define EXPORT_PHUISUSPENDPROCESSES 1233
#define EXPORT_PHUISUSPENDTHREADS 1234
#define EXPORT_PHUISUSPENDTREEPROCESS 1235
#define EXPORT_PHUITERMINATEPROCESSES 1236
#define EXPORT_PHUITERMINATETHREADS 1237
#define EXPORT_PHUITERMINATETREEPROCESS 1238
#define EXPORT_PHUITHAWTREEPROCESS 1239
#define EXPORT_PHUIUNLOADMODULE 1240
#define EXPORT_PHAUTODEREFERENCEOBJECT 1241
#define EXPORT_PHCREATEALLOC 1242
#define EXPORT_PHCREATEOBJECT 1243
#define EXPORT_PHCREATEOBJECTTYPE 1244
#define EXPORT_PHCREATEOBJECTTYPEEX 1245
#define EXPORT_PHDELETEAUTOPOOL 1246
#define EXPORT_PHDEREFERENCEOBJECT 1247
#define EXPORT_PHDEREFERENCEOBJECTDEFERDELETE 1248
#define EXPORT_PHDEREFERENCEOBJECTEX 1249
#define EXPORT_PHDRAINAUTOPOOL 1250
#define EXPORT_PHGETOBJECTTYPE 1251
#define EXPORT_PHGETOBJECTTYPEINFORMATION 1252
#define EXPORT_PHINITIALIZEAUTOPOOL 1253
#define EXPORT_PHREFERENCEOBJECT 1254
#define EXPORT_PHREFERENCEOBJECTEX 1255
#define EXPORT_PHREFERENCEOBJECTSAFE 1256
#define EXPORT_PHFACQUIREQUEUEDLOCKEXCLUSIVE 1257
#define EXPORT_PHFACQUIREQUEUEDLOCKSHARED 1258
#define EXPORT_PHFPULSEALLCONDITION 1259
#define EXPORT_PHFPULSECONDITION 1260
#define EXPORT_PHFQUEUEWAKEEVENT 1261
#define EXPORT_PHFRELEASEQUEUEDLOCKEXCLUSIVE 1262
#define EXPORT_PHFRELEASEQUEUEDLOCKSHARED 1263
#define EXPORT_PHFSETWAKEEVENT 1264
#define EXPORT_PHFWAITFORCONDITION 1265
#define EXPORT_PHFWAITFORCONDITIONEX 1266
#define EXPORT_PHFWAITFORWAKEEVENT 1267
#define EXPORT_PHFWAKEFORRELEASEQUEUEDLOCK 1268
#define EXPORT_PHISEXECUTINGINWOW64 1269
#define EXPORT_PHSYSTEMBASICINFORMATION 1270
#define EXPORT_PHADDELEMENTAVLTREE 1271
#define EXPORT_PHADDENTRYHASHTABLE 1272
#define EXPORT_PHADDENTRYHASHTABLEEX 1273
#define EXPORT_PHADDITEMARRAY 1274
#define EXPORT_PHADDITEMLIST 1275
#define EXPORT_PHADDITEMPOINTERLIST 1276
#define EXPORT_PHADDITEMSIMPLEHASHTABLE 1277
#define EXPORT_PHADDITEMSARRAY 1278
#define EXPORT_PHADDITEMSLIST 1279
#define EXPORT_PHADDPLUSMAXMEMORYSINGLES 1280
#define EXPORT_PHALLOCATE 1281
#define EXPORT_PHALLOCATEEXSAFE 1282
#define EXPORT_PHALLOCATEFROMFREELIST 1283
#define EXPORT_PHALLOCATEPAGE 1284
#define EXPORT_PHALLOCATESAFE 1285
#define EXPORT_PHAPPENDBYTESBUILDER 1286
#define EXPORT_PHAPPENDBYTESBUILDER2 1287
#define EXPORT_PHAPPENDBYTESBUILDEREX 1288
#define EXPORT_PHAPPENDCHARSTRINGBUILDER 1289
#define EXPORT_PHAPPENDCHARSTRINGBUILDER2 1290
#define EXPORT_PHAPPENDFORMATSTRINGBUILDER 1291
#define EXPORT_PHAPPENDFORMATSTRINGBUILDER_V 1292
#define EXPORT_PHAPPENDSTRINGBUILDEREX 1293
#define EXPORT_PHBUFFERTOHEXSTRING 1294
#define EXPORT_PHBUFFERTOHEXSTRINGEX 1295
#define EXPORT_PHCLEARARRAY 1296
#define EXPORT_PHCLEARHASHTABLE 1297
#define EXPORT_PHCLEARLIST 1298
#define EXPORT_PHCOMPARESTRINGREF 1299
#define EXPORT_PHCOMPARESTRINGZNATURAL 1300
#define EXPORT_PHCONCATSTRINGREF2 1301
#define EXPORT_PHCONCATSTRINGREF3 1302
#define EXPORT_PHCONCATSTRINGREF4 1303
#define EXPORT_PHCONCATSTRINGS 1304
#define EXPORT_PHCONCATSTRINGS2 1305
#define EXPORT_PHCONCATSTRINGS_V 1306
#define EXPORT_PHCONVERTMULTIBYTETOUTF16 1307
#define EXPORT_PHCONVERTMULTIBYTETOUTF16EX 1308
#define EXPORT_PHCONVERTUTF16TOASCIIEX 1309
#define EXPORT_PHCONVERTUTF16TOMULTIBYTE 1310
#define EXPORT_PHCONVERTUTF16TOMULTIBYTEEX 1311
#define EXPORT_PHCONVERTUTF16TOUTF8 1312
#define EXPORT_PHCONVERTUTF16TOUTF8BUFFER 1313
#define EXPORT_PHCONVERTUTF16TOUTF8EX 1314
#define EXPORT_PHCONVERTUTF16TOUTF8SIZE 1315
#define EXPORT_PHCONVERTUTF8TOUTF16 1316
#define EXPORT_PHCONVERTUTF8TOUTF16BUFFER 1317
#define EXPORT_PHCONVERTUTF8TOUTF16EX 1318
#define EXPORT_PHCONVERTUTF8TOUTF16SIZE 1319
#define EXPORT_PHCOPYBYTESZ 1320
#define EXPORT_PHCOPYCONVERTCIRCULARBUFFERULONG 1321
#define EXPORT_PHCOPYSTRINGZ 1322
#define EXPORT_PHCOPYSTRINGZFROMBYTES 1323
#define EXPORT_PHCOPYSTRINGZFROMMULTIBYTE 1324
#define EXPORT_PHCOUNTSTRINGZ 1325
#define EXPORT_PHCREATEBYTES 1326
#define EXPORT_PHCREATEBYTESEX 1327
#define EXPORT_PHCREATEHASHTABLE 1328
#define EXPORT_PHCREATELIST 1329
#define EXPORT_PHCREATEPOINTERLIST 1330
#define EXPORT_PHCREATESIMPLEHASHTABLE 1331
#define EXPORT_PHCREATESTRING3 1332
#define EXPORT_PHCREATESTRINGEX 1333
#define EXPORT_PHCREATETHREAD 1334
#define EXPORT_PHCREATETHREAD2 1335
#define EXPORT_PHCREATETHREADEX 1336
#define EXPORT_PHDECODEUNICODEDECODER 1337
#define EXPORT_PHDELAYEXECUTION 1338
#define EXPORT_PHDELETEARRAY 1339
#define EXPORT_PHDELETEBYTESBUILDER 1340
#define EXPORT_PHDELETECALLBACK 1341
#define EXPORT_PHDELETEFREELIST 1342
#define EXPORT_PHDELETESTRINGBUILDER 1343
#define EXPORT_PHDIVIDESINGLESBYSINGLE 1344
#define EXPORT_PHDOSERRORTONTSTATUS 1345
#define EXPORT_PHDUPLICATEBYTESZ 1346
#define EXPORT_PHDUPLICATEBYTESZSAFE 1347
#define EXPORT_PHDUPLICATESTRINGZ 1348
#define EXPORT_PHENCODEUNICODE 1349
#define EXPORT_PHENUMAVLTREE 1350
#define EXPORT_PHENUMHASHTABLE 1351
#define EXPORT_PHENUMPOINTERLISTEX 1352
#define EXPORT_PHEQUALSTRINGREF 1353
#define EXPORT_PHEXPONENTIATE 1354
#define EXPORT_PHEXPONENTIATE64 1355
#define EXPORT_PHEXTRACTICON 1356
#define EXPORT_PHEXTRACTICONEX 1357
#define EXPORT_PHFILLMEMORYULONG 1358
#define EXPORT_PHFINALBYTESBUILDERBYTES 1359
#define EXPORT_PHFINALSTRINGBUILDERSTRING 1360
#define EXPORT_PHFINDCHARINSTRINGREF 1361
#define EXPORT_PHFINDELEMENTAVLTREE 1362
#define EXPORT_PHFINDENTRYHASHTABLE 1363
#define EXPORT_PHFINDITEMLIST 1364
#define EXPORT_PHFINDITEMPOINTERLIST 1365
#define EXPORT_PHFINDITEMSIMPLEHASHTABLE 1366
#define EXPORT_PHFINDLASTCHARINSTRINGREF 1367
#define EXPORT_PHFINDSTRINGINSTRINGREF 1368
#define EXPORT_PHFORMAT 1369
#define EXPORT_PHFORMATBYTES 1370
#define EXPORT_PHFORMATBYTES_V 1371
#define EXPORT_PHFORMATSTRING 1372
#define EXPORT_PHFORMATSTRING_V 1373
#define EXPORT_PHFORMATTOBUFFER 1374
#define EXPORT_PHFREE 1375
#define EXPORT_PHFREEPAGE 1376
#define EXPORT_PHFREETOFREELIST 1377
#define EXPORT_PHGETLASTERROR 1378
#define EXPORT_PHGETPRIMENUMBER 1379
#define EXPORT_PHHASHBYTES 1380
#define EXPORT_PHHASHSTRINGREF 1381
#define EXPORT_PHHASHSTRINGREFEX 1382
#define EXPORT_PHHEXSTRINGTOBUFFER 1383
#define EXPORT_PHHEXSTRINGTOBUFFEREX 1384
#define EXPORT_PHHUNGWINDOWFROMGHOSTWINDOW 1385
#define EXPORT_PHINITIALIZEARRAY 1386
#define EXPORT_PHINITIALIZEAVLTREE 1387
#define EXPORT_PHINITIALIZEBYTESBUILDER 1388
#define EXPORT_PHINITIALIZECALLBACK 1389
#define EXPORT_PHINITIALIZEFREELIST 1390
#define EXPORT_PHINITIALIZESTRINGBUILDER 1391
#define EXPORT_PHINSERTITEMLIST 1392
#define EXPORT_PHINSERTITEMSLIST 1393
#define EXPORT_PHINSERTSTRINGBUILDER 1394
#define EXPORT_PHINSERTSTRINGBUILDER2 1395
#define EXPORT_PHINSERTSTRINGBUILDEREX 1396
#define EXPORT_PHINTEGERTOSTRING64 1397
#define EXPORT_PHINVOKECALLBACK 1398
#define EXPORT_PHLOADINDIRECTSTRING 1399
#define EXPORT_PHLOADRESOURCE 1400
#define EXPORT_PHLOCALTIMETOSYSTEMTIME 1401
#define EXPORT_PHLOWERBOUNDELEMENTAVLTREE 1402
#define EXPORT_PHLOWERDUALBOUNDELEMENTAVLTREE 1403
#define EXPORT_PHMAXMEMORYSINGLES 1404
#define EXPORT_PHMAXIMUMELEMENTAVLTREE 1405
#define EXPORT_PHMINIMUMELEMENTAVLTREE 1406
#define EXPORT_PHNTSTATUSFILENOTFOUND 1407
#define EXPORT_PHNTSTATUSTODOSERROR 1408
#define EXPORT_PHPREDECESSORELEMENTAVLTREE 1409
#define EXPORT_PHPRINTTIMESPAN 1410
#define EXPORT_PHQUERYPERFORMANCECOUNTER 1411
#define EXPORT_PHQUERYPERFORMANCEFREQUENCY 1412
#define EXPORT_PHQUERYSYSTEMTIME 1413
#define EXPORT_PHQUERYTIMEZONEBIAS 1414
#define EXPORT_PHREALLOCATE 1415
#define EXPORT_PHREALLOCATESAFE 1416
#define EXPORT_PHREADTIMESTAMPCOUNTER 1417
#define EXPORT_PHREFERENCEEMPTYSTRING 1418
#define EXPORT_PHREGISTERCALLBACK 1419
#define EXPORT_PHREGISTERCALLBACKEX 1420
#define EXPORT_PHREMOVEELEMENTAVLTREE 1421
#define EXPORT_PHREMOVEENTRYHASHTABLE 1422
#define EXPORT_PHREMOVEITEMARRAY 1423
#define EXPORT_PHREMOVEITEMLIST 1424
#define EXPORT_PHREMOVEITEMPOINTERLIST 1425
#define EXPORT_PHREMOVEITEMSIMPLEHASHTABLE 1426
#define EXPORT_PHREMOVEITEMSARRAY 1427
#define EXPORT_PHREMOVEITEMSLIST 1428
#define EXPORT_PHREMOVESTRINGBUILDER 1429
#define EXPORT_PHRESIZEARRAY 1430
#define EXPORT_PHRESIZELIST 1431
#define EXPORT_PHROUNDUPTOPOWEROFTWO 1432
#define EXPORT_PHSECONDSSINCE1970TOTIME 1433
#define EXPORT_PHSPLITSTRINGREFATCHAR 1434
#define EXPORT_PHSPLITSTRINGREFATLASTCHAR 1435
#define EXPORT_PHSPLITSTRINGREFATSTRING 1436
#define EXPORT_PHSPLITSTRINGREFEX 1437
#define EXPORT_PHSTRINGTODOUBLE 1438
#define EXPORT_PHSTRINGTOINTEGER64 1439
#define EXPORT_PHSTRINGTOUINT64 1440
#define EXPORT_PHSUCCESSORELEMENTAVLTREE 1441
#define EXPORT_PHSYSTEMTIMETOLOCALTIME 1442
#define EXPORT_PHTIMETOSECONDSSINCE1970 1443
#define EXPORT_PHTRIMSTRINGREF 1444
#define EXPORT_PHUNREGISTERCALLBACK 1445
#define EXPORT_PHUPPERBOUNDELEMENTAVLTREE 1446
#define EXPORT_PHUPPERDUALBOUNDELEMENTAVLTREE 1447
#define EXPORT_PHWRITEUNICODEDECODER 1448
#define EXPORT_PHZEROEXTENDTOUTF16BUFFER 1449
#define EXPORT_PHZEROEXTENDTOUTF16EX 1450
#define EXPORT_PHFACQUIRERUNDOWNPROTECTION 1451
#define EXPORT_PHFBEGININITONCE 1452
#define EXPORT_PHFENDINITONCE 1453
#define EXPORT_PHFINITIALIZEBARRIER 1454
#define EXPORT_PHFINITIALIZEEVENT 1455
#define EXPORT_PHFINITIALIZEINITONCE 1456
#define EXPORT_PHFINITIALIZERUNDOWNPROTECTION 1457
#define EXPORT_PHFRELEASERUNDOWNPROTECTION 1458
#define EXPORT_PHFRESETEVENT 1459
#define EXPORT_PHFSETEVENT 1460
#define EXPORT_PHFWAITFORBARRIER 1461
#define EXPORT_PHFWAITFOREVENT 1462
#define EXPORT_PHFWAITFORRUNDOWNPROTECTION 1463
#define EXPORT_PHENUMSMBIOS 1464
#define EXPORT_PHGETSMBIOSSTRING 1465
#define EXPORT_PHADJUSTPRIVILEGE 1466
#define EXPORT_PHCONNECTPIPE 1467
#define EXPORT_PHCREATEDIRECTORYFULLPATHWIN32 1468
#define EXPORT_PHCREATEDIRECTORYWIN32 1469
#define EXPORT_PHCREATEEVENT 1470
#define EXPORT_PHCREATEFILE 1471
#define EXPORT_PHCREATEFILEWIN32 1472
#define EXPORT_PHCREATEFILEWIN32EX 1473
#define EXPORT_PHCREATEKEY 1474
#define EXPORT_PHCREATENAMEDPIPE 1475
#define EXPORT_PHCREATEPIPE 1476
#define EXPORT_PHDELETEDIRECTORYWIN32 1477
#define EXPORT_PHDELETEFILE 1478
#define EXPORT_PHDELETEFILEWIN32 1479
#define EXPORT_PHDELETEVALUEKEY 1480
#define EXPORT_PHDESTROYWINDOWREMOTE 1481
#define EXPORT_PHDETERMINEDOSPATHNAMETYPE 1482
#define EXPORT_PHDEVICEIOCONTROLFILE 1483
#define EXPORT_PHDISCONNECTNAMEDPIPE 1484
#define EXPORT_PHDOESFILEEXIST 1485
#define EXPORT_PHDOESFILEEXISTWIN32 1486
#define EXPORT_PHDOSPATHNAMETONTPATHNAME 1487
#define EXPORT_PHENUMBIGPOOLINFORMATION 1488
#define EXPORT_PHENUMDIRECTORYFILE 1489
#define EXPORT_PHENUMDIRECTORYOBJECTS 1490
#define EXPORT_PHENUMFILESTREAMS 1491
#define EXPORT_PHENUMFIRMWAREENVIRONMENTVALUES 1492
#define EXPORT_PHENUMGENERICMODULES 1493
#define EXPORT_PHENUMHANDLESEX 1494
#define EXPORT_PHENUMKERNELMODULES 1495
#define EXPORT_PHENUMNEXTPROCESS 1496
#define EXPORT_PHENUMNEXTTHREAD 1497
#define EXPORT_PHENUMOBJECTIDINFORMATION 1498
#define EXPORT_PHENUMPAGEFILES 1499
#define EXPORT_PHENUMPOOLTAGINFORMATION 1500
#define EXPORT_PHENUMPROCESSENVIRONMENTVARIABLES 1501
#define EXPORT_PHENUMPROCESSES 1502
#define EXPORT_PHENUMPROCESSESEX 1503
#define EXPORT_PHENUMPROCESSHANDLES 1504
#define EXPORT_PHENUMREPARSEPOINTINFORMATION 1505
#define EXPORT_PHENUMERATEKEY 1506
#define EXPORT_PHENUMERATEVALUEKEY 1507
#define EXPORT_PHFILTERCONNECTCOMMUNICATIONPORT 1508
#define EXPORT_PHFINDPROCESSINFORMATION 1509
#define EXPORT_PHFINDPROCESSINFORMATIONBYIMAGENAME 1510
#define EXPORT_PHGETACTIVEPROCESSORCOUNT 1511
#define EXPORT_PHGETCONTEXTTHREAD 1512
#define EXPORT_PHGETDEVICETYPE 1513
#define EXPORT_PHGETDLLHANDLE 1514
#define EXPORT_PHGETDRIVERIMAGEFILENAME 1515
#define EXPORT_PHGETDRIVERNAME 1516
#define EXPORT_PHGETDRIVERSERVICEKEYNAME 1517
#define EXPORT_PHGETFILEFULLATTRIBUTESINFORMATION 1518
#define EXPORT_PHGETFILENAME 1519
#define EXPORT_PHGETFILEPOSITION 1520
#define EXPORT_PHGETFILESIZE 1521
#define EXPORT_PHGETFIRMWAREENVIRONMENTVARIABLE 1522
#define EXPORT_PHGETJOBPROCESSIDLIST 1523
#define EXPORT_PHGETKERNELFILENAME 1524
#define EXPORT_PHGETKERNELFILENAME2 1525
#define EXPORT_PHGETKERNELFILENAMEEX 1526
#define EXPORT_PHGETMODULEPROCADDRESS 1527
#define EXPORT_PHGETNAMEDPIPECLIENTCOMPUTERNAME 1528
#define EXPORT_PHGETNAMEDPIPECLIENTPROCESSID 1529
#define EXPORT_PHGETNAMEDPIPESERVERPROCESSID 1530
#define EXPORT_PHGETOBJECTSECURITY 1531
#define EXPORT_PHGETOBJECTTYPEINDEXNAME 1532
#define EXPORT_PHGETOBJECTTYPENUMBER 1533
#define EXPORT_PHGETOWNTOKENATTRIBUTES 1534
#define EXPORT_PHGETPNPDEVICENAME 1535
#define EXPORT_PHGETPROCEDUREADDRESS 1536
#define EXPORT_PHGETPROCEDUREADDRESSREMOTE 1537
#define EXPORT_PHGETPROCESSCOMMANDLINE 1538
#define EXPORT_PHGETPROCESSDEPSTATUS 1539
#define EXPORT_PHGETPROCESSDEVICEMAP 1540
#define EXPORT_PHGETPROCESSENVIRONMENT 1541
#define EXPORT_PHGETPROCESSIMAGEFILENAME 1542
#define EXPORT_PHGETPROCESSIMAGEFILENAMEBYPROCESSID 1543
#define EXPORT_PHGETPROCESSIMAGEFILENAMEWIN32 1544
#define EXPORT_PHGETPROCESSISDOTNET 1545
#define EXPORT_PHGETPROCESSISDOTNETEX 1546
#define EXPORT_PHGETPROCESSMAPPEDFILENAME 1547
#define EXPORT_PHGETPROCESSPEBSTRING 1548
#define EXPORT_PHGETPROCESSPRIORITYCLASS 1549
#define EXPORT_PHGETPROCESSUNLOADEDDLLS 1550
#define EXPORT_PHGETPROCESSWINDOWTITLE 1551
#define EXPORT_PHGETPROCESSWORKINGSETINFORMATION 1552
#define EXPORT_PHGETPROCESSWSCOUNTERS 1553
#define EXPORT_PHGETSECTIONFILENAME 1554
#define EXPORT_PHGETTOKENGROUPS 1555
#define EXPORT_PHGETTOKENINTEGRITYLEVEL 1556
#define EXPORT_PHGETTOKENINTEGRITYLEVELRID 1557
#define EXPORT_PHGETTOKENOWNER 1558
#define EXPORT_PHGETTOKENPRIMARYGROUP 1559
#define EXPORT_PHGETTOKENPRIVILEGES 1560
#define EXPORT_PHGETTOKENUSER 1561
#define EXPORT_PHIMPERSONATETOKEN 1562
#define EXPORT_PHISDEBUGGERPRESENT 1563
#define EXPORT_PHISFIRMWARESUPPORTED 1564
#define EXPORT_PHLISTENNAMEDPIPE 1565
#define EXPORT_PHLOADAPPKEY 1566
#define EXPORT_PHMOVEFILEWIN32 1567
#define EXPORT_PHOPENDIRECTORYOBJECT 1568
#define EXPORT_PHOPENDRIVER 1569
#define EXPORT_PHOPENFILE 1570
#define EXPORT_PHOPENFILEBYID 1571
#define EXPORT_PHOPENKEY 1572
#define EXPORT_PHOPENPROCESS 1573
#define EXPORT_PHOPENPROCESSCLIENTID 1574
#define EXPORT_PHOPENPROCESSTOKEN 1575
#define EXPORT_PHOPENTHREAD 1576
#define EXPORT_PHOPENTHREADPROCESS 1577
#define EXPORT_PHPEEKNAMEDPIPE 1578
#define EXPORT_PHQUERYENVIRONMENTVARIABLE 1579
#define EXPORT_PHQUERYFULLATTRIBUTESFILEWIN32 1580
#define EXPORT_PHQUERYKEY 1581
#define EXPORT_PHQUERYSYMBOLICLINKOBJECT 1582
#define EXPORT_PHQUERYTOKENVARIABLESIZE 1583
#define EXPORT_PHQUERYVALUEKEY 1584
#define EXPORT_PHQUEUEUSERWORKITEM 1585
#define EXPORT_PHREADFILE 1586
#define EXPORT_PHRESOLVEDEVICEPREFIX 1587
#define EXPORT_PHREVERTIMPERSONATIONTOKEN 1588
#define EXPORT_PHSETFILEALLOCATIONSIZE 1589
#define EXPORT_PHSETFILEPOSITION 1590
#define EXPORT_PHSETFILESIZE 1591
#define EXPORT_PHSETFIRMWAREENVIRONMENTVARIABLE 1592
#define EXPORT_PHSETOBJECTSECURITY 1593
#define EXPORT_PHSETPROCESSPOWERTHROTTLINGSTATE 1594
#define EXPORT_PHSETPROCESSPRIORITYBOOST 1595
#define EXPORT_PHSETPROCESSPRIORITYCLASS 1596
#define EXPORT_PHSETTHREADBASEPRIORITY 1597
#define EXPORT_PHSETTHREADIOPRIORITY 1598
#define EXPORT_PHSETTHREADNAME 1599
#define EXPORT_PHSETTOKENISVIRTUALIZATIONENABLED 1600
#define EXPORT_PHSETTOKENPRIVILEGE 1601
#define EXPORT_PHSETTOKENPRIVILEGE2 1602
#define EXPORT_PHSETTOKENSESSIONID 1603
#define EXPORT_PHSETVALUEKEY 1604
#define EXPORT_PHTERMINATEPROCESS 1605
#define EXPORT_PHTRACECONTROL 1606
#define EXPORT_PHTRANSCEIVENAMEDPIPE 1607
#define EXPORT_PHUNLOADDLLPROCESS 1608
#define EXPORT_PHUNLOADDRIVER 1609
#define EXPORT_PHUPDATEDOSDEVICEPREFIXES 1610
#define EXPORT_PHUPDATEMUPDEVICEPREFIXES 1611
#define EXPORT_PHWAITFORNAMEDPIPE 1612
#define EXPORT_PHWRITEFILE 1613
#define EXPORT_PHADJUSTRECTANGLETOBOUNDS 1614
#define EXPORT_PHADJUSTRECTANGLETOWORKINGAREA 1615
#define EXPORT_PHCENTERRECTANGLE 1616
#define EXPORT_PHCENTERWINDOW 1617
#define EXPORT_PHCOMPAREUNICODESTRINGZIGNOREMENUPREFIX 1618
#define EXPORT_PHCONSOLESETFOREGROUND 1619
#define EXPORT_PHCONSOLESETWINDOW 1620
#define EXPORT_PHCREATEOPENFILEDIALOG 1621
#define EXPORT_PHCREATEPROCESS 1622
#define EXPORT_PHCREATEPROCESSASUSER 1623
#define EXPORT_PHCREATEPROCESSREDIRECTION 1624
#define EXPORT_PHCREATEPROCESSWIN32 1625
#define EXPORT_PHCREATEPROCESSWIN32EX 1626
#define EXPORT_PHCREATESAVEFILEDIALOG 1627
#define EXPORT_PHDELETEIMAGEVERSIONINFO 1628
#define EXPORT_PHDEVFREEOBJECTPROPERTIES 1629
#define EXPORT_PHDEVFREEOBJECTS 1630
#define EXPORT_PHDEVGETOBJECTPROPERTIES 1631
#define EXPORT_PHDEVGETOBJECTS 1632
#define EXPORT_PHELLIPSISSTRING 1633
#define EXPORT_PHELLIPSISSTRINGPATH 1634
#define EXPORT_PHESCAPECOMMANDLINEPART 1635
#define EXPORT_PHESCAPESTRINGFORMENUPREFIX 1636
#define EXPORT_PHEXPANDENVIRONMENTSTRINGS 1637
#define EXPORT_PHFILEREADALLTEXTWIN32 1638
#define EXPORT_PHFINALHASH 1639
#define EXPORT_PHFORMATDATE 1640
#define EXPORT_PHFORMATDATETIME 1641
#define EXPORT_PHFORMATDECIMAL 1642
#define EXPORT_PHFORMATGUID 1643
#define EXPORT_PHFORMATIMAGEVERSIONINFO 1644
#define EXPORT_PHFORMATSIZE 1645
#define EXPORT_PHFORMATTIME 1646
#define EXPORT_PHFORMATTIMESPAN 1647
#define EXPORT_PHFORMATTIMESPANRELATIVE 1648
#define EXPORT_PHFORMATUINT64 1649
#define EXPORT_PHFREEFILEDIALOG 1650
#define EXPORT_PHFREELIBRARY 1651
#define EXPORT_PHFREELIBRARYASIMAGERESOURCE 1652
#define EXPORT_PHGENERATEGUID 1653
#define EXPORT_PHGENERATEGUIDFROMNAME 1654
#define EXPORT_PHGENERATERANDOMALPHASTRING 1655
#define EXPORT_PHGENERATERANDOMNUMBER64 1656
#define EXPORT_PHGETAPPLICATIONDATAFILENAME 1657
#define EXPORT_PHGETAPPLICATIONDIRECTORY 1658
#define EXPORT_PHGETAPPLICATIONDIRECTORYFILENAME 1659
#define EXPORT_PHGETAPPLICATIONDIRECTORYWIN32 1660
#define EXPORT_PHGETAPPLICATIONFILENAMEWIN32 1661
#define EXPORT_PHGETBASEDIRECTORY 1662
#define EXPORT_PHGETBASENAME 1663
#define EXPORT_PHGETCLASSOBJECT 1664
#define EXPORT_PHGETDLLFILENAME 1665
#define EXPORT_PHGETCLIENTRECT 1666
#define EXPORT_PHGETDPI 1667
#define EXPORT_PHGETDPIVALUE 1668
#define EXPORT_PHGETFILEDIALOGFILENAME 1669
#define EXPORT_PHGETFILEDIALOGFILTERINDEX 1670
#define EXPORT_PHGETFILEDIALOGOPTIONS 1671
#define EXPORT_PHGETFILEVERSIONFIXEDINFO 1672
#define EXPORT_PHGETFILEVERSIONINFO 1673
#define EXPORT_PHGETFILEVERSIONINFOEX 1674
#define EXPORT_PHGETFILEVERSIONINFOLANGCODEPAGE 1675
#define EXPORT_PHGETFILEVERSIONINFOSTRING 1676
#define EXPORT_PHGETFILEVERSIONINFOSTRING2 1677
#define EXPORT_PHGETFULLPATH 1678
#define EXPORT_PHGETKNOWNFOLDERPATH 1679
#define EXPORT_PHGETKNOWNLOCATION 1680
#define EXPORT_PHGETMESSAGE 1681
#define EXPORT_PHGETMONITORDPI 1682
#define EXPORT_PHGETNTMESSAGE 1683
#define EXPORT_PHGETNTSYSTEMROOT 1684
#define EXPORT_PHGETSIZEDPIVALUE 1685
#define EXPORT_PHGETSTATUSMESSAGE 1686
#define EXPORT_PHGETSYSTEMDIRECTORY 1687
#define EXPORT_PHGETSYSTEMDPI 1688
#define EXPORT_PHGETSYSTEMMETRICS 1689
#define EXPORT_PHGETSYSTEMPARAMETERSINFO 1690
#define EXPORT_PHGETSYSTEMROOT 1691
#define EXPORT_PHGETTASKBARDPI 1692
#define EXPORT_PHGETTEMPORARYDIRECTORYRANDOMALPHAFILENAME 1693
#define EXPORT_PHGETWIN32MESSAGE 1694
#define EXPORT_PHGETWINDOWDPI 1695
#define EXPORT_PHINITIALIZEHASH 1696
#define EXPORT_PHINITIALIZEIMAGEVERSIONINFO 1697
#define EXPORT_PHINITIALIZEIMAGEVERSIONINFOEX 1698
#define EXPORT_PHINITIALIZEPROCTHREADATTRIBUTELIST 1699
#define EXPORT_PHLARGEINTEGERTOLOCALSYSTEMTIME 1700
#define EXPORT_PHLARGEINTEGERTOSYSTEMTIME 1701
#define EXPORT_PHLOADLIBRARY 1702
#define EXPORT_PHLOADLIBRARYASIMAGERESOURCE 1703
#define EXPORT_PHMAPFLAGS1 1704
#define EXPORT_PHMAPFLAGS2 1705
#define EXPORT_PHMATCHWILDCARDS 1706
#define EXPORT_PHPARSECOMMANDLINE 1707
#define EXPORT_PHPARSECOMMANDLINEFUZZY 1708
#define EXPORT_PHPARSECOMMANDLINEPART 1709
#define EXPORT_PHQUERYREGISTRYSTRING 1710
#define EXPORT_PHQUERYREGISTRYULONG 1711
#define EXPORT_PHQUERYREGISTRYULONG64 1712
#define EXPORT_PHSETFILEDIALOGFILENAME 1713
#define EXPORT_PHSETFILEDIALOGFILTER 1714
#define EXPORT_PHSETFILEDIALOGOPTIONS 1715
#define EXPORT_PHSHELLEXECUTE 1716
#define EXPORT_PHSHELLEXECUTEEX 1717
#define EXPORT_PHSHELLEXPLOREFILE 1718
#define EXPORT_PHSHELLPROPERTIES 1719
#define EXPORT_PHSHOWCONFIRMMESSAGE 1720
#define EXPORT_PHSHOWCONTINUESTATUS 1721
#define EXPORT_PHSHOWFILEDIALOG 1722
#define EXPORT_PHSHOWMESSAGE 1723
#define EXPORT_PHSHOWMESSAGE2 1724
#define EXPORT_PHSHOWMESSAGEONETIME 1725
#define EXPORT_PHSHOWSTATUS 1726
#define EXPORT_PHSHOWTASKDIALOG 1727
#define EXPORT_PHSTRINGTOGUID 1728
#define EXPORT_PHSYSTEMTIMETOLARGEINTEGER 1729
#define EXPORT_PHSYSTEMTIMETOTZSPECIFICLOCALTIME 1730
#define EXPORT_PHTASKBARLISTCREATE 1731
#define EXPORT_PHTASKBARLISTDESTROY 1732
#define EXPORT_PHTASKBARLISTSETOVERLAYICON 1733
#define EXPORT_PHTASKBARLISTSETPROGRESSSTATE 1734
#define EXPORT_PHTASKBARLISTSETPROGRESSVALUE 1735
#define EXPORT_PHUPDATEHASH 1736
#define EXPORT_PHUPDATEPROCTHREADATTRIBUTE 1737
#define EXPORT_PHWAITFORMULTIPLEOBJECTSANDPUMP 1738
#define EXPORT_PHCLEARCIRCULARBUFFER_FLOAT 1739
#define EXPORT_PHCLEARCIRCULARBUFFER_PVOID 1740
#define EXPORT_PHCLEARCIRCULARBUFFER_ULONG 1741
#define EXPORT_PHCLEARCIRCULARBUFFER_ULONG64 1742
#define EXPORT_PHCOPYCIRCULARBUFFER_FLOAT 1743
#define EXPORT_PHCOPYCIRCULARBUFFER_PVOID 1744
#define EXPORT_PHCOPYCIRCULARBUFFER_ULONG 1745
#define EXPORT_PHCOPYCIRCULARBUFFER_ULONG64 1746
#define EXPORT_PHDELETECIRCULARBUFFER_FLOAT 1747
#define EXPORT_PHDELETECIRCULARBUFFER_PVOID 1748
#define EXPORT_PHDELETECIRCULARBUFFER_ULONG 1749
#define EXPORT_PHDELETECIRCULARBUFFER_ULONG64 1750
#define EXPORT_PHINITIALIZECIRCULARBUFFER_FLOAT 1751
#define EXPORT_PHINITIALIZECIRCULARBUFFER_PVOID 1752
#define EXPORT_PHINITIALIZECIRCULARBUFFER_ULONG 1753
#define EXPORT_PHINITIALIZECIRCULARBUFFER_ULONG64 1754
#define EXPORT_PHRESIZECIRCULARBUFFER_FLOAT 1755
#define EXPORT_PHRESIZECIRCULARBUFFER_PVOID 1756
#define EXPORT_PHRESIZECIRCULARBUFFER_ULONG 1757
#define EXPORT_PHRESIZECIRCULARBUFFER_ULONG64 1758
#define EXPORT_PHGETGENERICTREENEWLINES 1759
#define EXPORT_PHGETLISTVIEWITEMTEXT 1760
#define EXPORT_PHGETLISTVIEWSELECTEDITEMTEXT 1761
#define EXPORT_PHGETTREENEWTEXT 1762
#define EXPORT_PHCREATEEMENU 1763
#define EXPORT_PHCREATEEMENUITEM 1764
#define EXPORT_PHDESTROYEMENU 1765
#define EXPORT_PHDESTROYEMENUITEM 1766
#define EXPORT_PHFINDEMENUITEM 1767
#define EXPORT_PHINDEXOFEMENUITEM 1768
#define EXPORT_PHINSERTEMENUITEM 1769
#define EXPORT_PHLOADRESOURCEEMENUITEM 1770
#define EXPORT_PHMODIFYEMENUITEM 1771
#define EXPORT_PHREMOVEALLEMENUITEMS 1772
#define EXPORT_PHREMOVEEMENUITEM 1773
#define EXPORT_PHSETFLAGSALLEMENUITEMS 1774
#define EXPORT_PHSETFLAGSEMENUITEM 1775
#define EXPORT_PHSHOWEMENU 1776
#define EXPORT_PHDELETEFASTLOCK 1777
#define EXPORT_PHINITIALIZEFASTLOCK 1778
#define EXPORT_PHFACQUIREFASTLOCKEXCLUSIVE 1779
#define EXPORT_PHFACQUIREFASTLOCKSHARED 1780
#define EXPORT_PHFRELEASEFASTLOCKEXCLUSIVE 1781
#define EXPORT_PHFRELEASEFASTLOCKSHARED 1782
#define EXPORT_PHFTRYACQUIREFASTLOCKEXCLUSIVE 1783
#define EXPORT_PHFTRYACQUIREFASTLOCKSHARED 1784
#define EXPORT_PHCREATEFILESTREAM 1785
#define EXPORT_PHCREATEFILESTREAM2 1786
#define EXPORT_PHFLUSHFILESTREAM 1787
#define EXPORT_PHGETPOSITIONFILESTREAM 1788
#define EXPORT_PHLOCKFILESTREAM 1789
#define EXPORT_PHREADFILESTREAM 1790
#define EXPORT_PHSEEKFILESTREAM 1791
#define EXPORT_PHUNLOCKFILESTREAM 1792
#define EXPORT_PHVERIFYFILESTREAM 1793
#define EXPORT_PHWRITEFILESTREAM 1794
#define EXPORT_PHWRITESTRINGASUTF8FILESTREAM 1795
#define EXPORT_PHWRITESTRINGASUTF8FILESTREAMEX 1796
#define EXPORT_PHWRITESTRINGFORMATASUTF8FILESTREAM 1797
#define EXPORT_PHWRITESTRINGFORMATASUTF8FILESTREAM_V 1798
#define EXPORT_PHDELETEGRAPHSTATE 1799
#define EXPORT_PHDRAWGRAPHDIRECT 1800
#define EXPORT_PHDRAWTRAYICONTEXT 1801
#define EXPORT_PHGETDRAWINFOGRAPHBUFFERS 1802
#define EXPORT_PHGRAPHSTATEGETDRAWINFO 1803
#define EXPORT_PHINITIALIZEGRAPHSTATE 1804
#define EXPORT_PHNFGETTRAYICONFONT 1805
#define EXPORT_PHSETGRAPHTEXT 1806
#define EXPORT_PHADDLAYOUTITEM 1807
#define EXPORT_PHADDLAYOUTITEMEX 1808
#define EXPORT_PHADDLISTVIEWCOLUMN 1809
#define EXPORT_PHADDILISTVIEWCOLUMN 1810
#define EXPORT_PHADDILISTVIEWGROUP 1811
#define EXPORT_PHADDILISTVIEWGROUPITEM 1812
#define EXPORT_PHADDLISTVIEWGROUP 1813
#define EXPORT_PHADDLISTVIEWGROUPITEM 1814
#define EXPORT_PHADDLISTVIEWITEM 1815
#define EXPORT_PHADDILISTVIEWITEM 1816
#define EXPORT_PHADDTABCONTROLTAB 1817
#define EXPORT_PHBITMAPSETALPHA 1818
#define EXPORT_PHCREATEDIALOG 1819
#define EXPORT_PHCREATEWINDOWEX 1820
#define EXPORT_PHDELETELAYOUTMANAGER 1821
#define EXPORT_PHDIALOGBOX 1822
#define EXPORT_PHENUMCHILDWINDOWS 1823
#define EXPORT_PHENUMWINDOWS 1824
#define EXPORT_PHFINDILISTVIEWITEMBYFLAGS 1825
#define EXPORT_PHFINDLISTVIEWITEMBYFLAGS 1826
#define EXPORT_PHFINDLISTVIEWITEMBYPARAM 1827
#define EXPORT_PHGETCOMBOBOXSTRING 1828
#define EXPORT_PHGETDIALOGITEMVALUE 1829
#define EXPORT_PHGETILISTVIEWITEMPARAM 1830
#define EXPORT_PHGETLISTBOXSTRING 1831
#define EXPORT_PHGETLISTVIEWITEMIMAGEINDEX 1832
#define EXPORT_PHGETLISTVIEWITEMPARAM 1833
#define EXPORT_PHGETSELECTEDLISTVIEWITEMPARAM 1834
#define EXPORT_PHGETSELECTEDLISTVIEWITEMPARAMS 1835
#define EXPORT_PHGETSELECTEDILISTVIEWITEMPARAMS 1836
#define EXPORT_PHGETSTOCKAPPLICATIONICON 1837
#define EXPORT_PHGETSTOCKOBJECT 1838
#define EXPORT_PHGETWINDOWCLIENTID 1839
#define EXPORT_PHGETWINDOWCONTEXT 1840
#define EXPORT_PHGETWINDOWTEXT 1841
#define EXPORT_PHGETWINDOWTEXTEX 1842
#define EXPORT_PHICONTOBITMAP 1843
#define EXPORT_PHIMAGELISTADDBITMAP 1844
#define EXPORT_PHIMAGELISTADDICON 1845
#define EXPORT_PHIMAGELISTCREATE 1846
#define EXPORT_PHIMAGELISTDESTROY 1847
#define EXPORT_PHIMAGELISTDRAWEX 1848
#define EXPORT_PHIMAGELISTDRAWICON 1849
#define EXPORT_PHIMAGELISTGETICON 1850
#define EXPORT_PHIMAGELISTREMOVEICON 1851
#define EXPORT_PHIMAGELISTREPLACE 1852
#define EXPORT_PHIMAGELISTSETBKCOLOR 1853
#define EXPORT_PHIMAGELISTSETICONSIZE 1854
#define EXPORT_PHIMAGELISTSETIMAGECOUNT 1855
#define EXPORT_PHINITIALIZELAYOUTMANAGER 1856
#define EXPORT_PHLAYOUTMANAGERLAYOUT 1857
#define EXPORT_PHLOADICON 1858
#define EXPORT_PHLOADIMAGEFORMATFROMRESOURCE 1859
#define EXPORT_PHMODALPROPERTYSHEET 1860
#define EXPORT_PHQUERYDIRECTXEXCLUSIVEOWNERSHIP 1861
#define EXPORT_PHINITIALIZEWINDOWTHEME 1862
#define EXPORT_PHREINITIALIZEWINDOWTHEME 1863
#define EXPORT_PHREGISTERWINDOWCALLBACK 1864
#define EXPORT_PHREMOVELISTVIEWITEM 1865
#define EXPORT_PHREMOVEWINDOWCONTEXT 1866
#define EXPORT_PHSELECTCOMBOBOXSTRING 1867
#define EXPORT_PHSETCLIPBOARDSTRING 1868
#define EXPORT_PHSETCONTROLTHEME 1869
#define EXPORT_PHSETDIALOGITEMTEXT 1870
#define EXPORT_PHSETDIALOGITEMVALUE 1871
#define EXPORT_PHSETEXTENDEDLISTVIEW 1872
#define EXPORT_PHSETGROUPBOXTEXT 1873
#define EXPORT_PHSETHEADERSORTICON 1874
#define EXPORT_PHSETIMAGELISTBITMAP 1875
#define EXPORT_PHSETLISTVIEWITEMIMAGEINDEX 1876
#define EXPORT_PHSETLISTVIEWITEMPARAM 1877
#define EXPORT_PHSETLISTVIEWSUBITEM 1878
#define EXPORT_PHSETILISTVIEWSUBITEM 1879
#define EXPORT_PHSETSTATEALLLISTVIEWITEMS 1880
#define EXPORT_PHSETWINDOWCONTEXT 1881
#define EXPORT_PHSETWINDOWTEXT 1882
#define EXPORT_PHTHEMEWINDOWDRAWREBAR 1883
#define EXPORT_PHTHEMEWINDOWDRAWTOOLBAR 1884
#define EXPORT_PHUNREGISTERWINDOWCALLBACK 1885
#define EXPORT_PHWINDOWTHEMECONTROLCOLOR 1886
#define EXPORT_PHCALLKPHQUERYFILEINFORMATIONWITHTIMEOUT 1887
#define EXPORT_PHCOMPAREOBJECTS 1888
#define EXPORT_PHENUMOBJECTTYPES 1889
#define EXPORT_PHFORMATNATIVEKEYNAME 1890
#define EXPORT_PHGETETWPUBLISHERNAME 1891
#define EXPORT_PHGETHANDLEINFORMATION 1892
#define EXPORT_PHGETHANDLEINFORMATIONEX 1893
#define EXPORT_PHGETOBJECTTYPENAME 1894
#define EXPORT_PHQUERYOBJECTNAME 1895
#define EXPORT_PHSTDGETCLIENTIDNAME 1896
#define EXPORT_PHENUMERATEPRIVILEGES 1897
#define EXPORT_PHGETSIDFULLNAME 1898
#define EXPORT_PHLOOKUPNAME 1899
#define EXPORT_PHLOOKUPPRIVILEGEDISPLAYNAME 1900
#define EXPORT_PHLOOKUPPRIVILEGENAME 1901
#define EXPORT_PHLOOKUPPRIVILEGEVALUE 1902
#define EXPORT_PHLOOKUPSID 1903
#define EXPORT_PHOPENLSAPOLICY 1904
#define EXPORT_PHSIDTOSTRINGSID 1905
#define EXPORT_PHLOADMAPPEDIMAGE 1906
#define EXPORT_PHLOADMAPPEDIMAGEEX 1907
#define EXPORT_PHLOADMAPPEDIMAGEHEADERPAGESIZE 1908
#define EXPORT_PHUNLOADMAPPEDIMAGE 1909
#define EXPORT_PHBOOSTPROVIDER 1910
#define EXPORT_PHDELETEPROVIDERTHREAD 1911
#define EXPORT_PHGETENABLEDPROVIDER 1912
#define EXPORT_PHINITIALIZEPROVIDERTHREAD 1913
#define EXPORT_PHREGISTERPROVIDER 1914
#define EXPORT_PHSETENABLEDPROVIDER 1915
#define EXPORT_PHSETINTERVALPROVIDERTHREAD 1916
#define EXPORT_PHSTARTPROVIDERTHREAD 1917
#define EXPORT_PHSTOPPROVIDERTHREAD 1918
#define EXPORT_PHUNREGISTERPROVIDER 1919
#define EXPORT_PHADDSETTING 1920
#define EXPORT_PHADDSETTINGS 1921
#define EXPORT_PHCLEARIGNOREDSETTINGS 1922
#define EXPORT_PHCONVERTIGNOREDSETTINGS 1923
#define EXPORT_PHGETINTEGERPAIRSTRINGREFSETTING 1924
#define EXPORT_PHGETINTEGERSTRINGREFSETTING 1925
#define EXPORT_PHGETSCALABLEINTEGERPAIRSTRINGREFSETTING 1926
#define EXPORT_PHGETSTRINGREFSETTING 1927
#define EXPORT_PHLOADCUSTOMCOLORLIST 1928
#define EXPORT_PHLOADLISTVIEWCOLUMNSETTINGS 1929
#define EXPORT_PHLOADLISTVIEWCOLUMNSFROMSETTING 1930
#define EXPORT_PHLOADLISTVIEWGROUPSTATESFROMSETTING 1931
#define EXPORT_PHLOADLISTVIEWSORTCOLUMNSFROMSETTING 1932
#define EXPORT_PHLOADSETTINGS 1933
#define EXPORT_PHLOADWINDOWPLACEMENTFROMSETTING 1934
#define EXPORT_PHRESETSETTINGS 1935
#define EXPORT_PHSAVECUSTOMCOLORLIST 1936
#define EXPORT_PHSAVELISTVIEWCOLUMNSETTINGS 1937
#define EXPORT_PHSAVELISTVIEWCOLUMNSTOSETTING 1938
#define EXPORT_PHSAVELISTVIEWGROUPSTATESTOSETTING 1939
#define EXPORT_PHSAVELISTVIEWSORTCOLUMNSTOSETTING 1940
#define EXPORT_PHSAVESETTINGS 1941
#define EXPORT_PHSAVEWINDOWPLACEMENTTOSETTING 1942
#define EXPORT_PHSETINTEGERPAIRSTRINGREFSETTING 1943
#define EXPORT_PHSETINTEGERSTRINGREFSETTING 1944
#define EXPORT_PHSETSCALABLEINTEGERPAIRSTRINGREFSETTING 1945
#define EXPORT_PHSETSCALABLEINTEGERPAIRSTRINGREFSETTING2 1946
#define EXPORT_PHSETSTRINGREFSETTING 1947
#define EXPORT_PHUPDATECACHEDSETTINGS 1948
#define EXPORT_PHCREATESECURITYPAGE 1949
#define EXPORT_PHEDITSECURITY 1950
#define EXPORT_PHGETACCESSENTRIES 1951
#define EXPORT_PHGETACCESSSTRING 1952
#define EXPORT_PHGETSEOBJECTSECURITY 1953
#define EXPORT_PHSETSEOBJECTSECURITY 1954
#define EXPORT_PHSTDGETOBJECTSECURITY 1955
#define EXPORT_PHSTDSETOBJECTSECURITY 1956
#define EXPORT_PHCHANGESERVICECONFIG2 1957
#define EXPORT_PHCLOSESERVICEHANDLE 1958
#define EXPORT_PHENUMDEPENDENTSERVICES 1959
#define EXPORT_PHGETSERVICECONFIG 1960
#define EXPORT_PHGETSERVICECONFIGFILENAME 1961
#define EXPORT_PHGETSERVICEDESCRIPTION 1962
#define EXPORT_PHGETSERVICEDLLPARAMETER 1963
#define EXPORT_PHGETSERVICEERRORCONTROLINTEGER 1964
#define EXPORT_PHGETSERVICEERRORCONTROLSTRING 1965
#define EXPORT_PHGETSERVICEFILENAME 1966
#define EXPORT_PHGETSERVICENAMEFROMTAG 1967
#define EXPORT_PHGETSERVICESTARTTYPEINTEGER 1968
#define EXPORT_PHGETSERVICESTARTTYPESTRING 1969
#define EXPORT_PHGETSERVICESTATESTRING 1970
#define EXPORT_PHGETSERVICETYPEINTEGER 1971
#define EXPORT_PHGETSERVICETYPESTRING 1972
#define EXPORT_PHGETTHREADSERVICETAG 1973
#define EXPORT_PHOPENSERVICE 1974
#define EXPORT_PHOPENSERVICEKEY 1975
#define EXPORT_PHQUERYSERVICECONFIG 1976
#define EXPORT_PHQUERYSERVICECONFIG2 1977
#define EXPORT_PHQUERYSERVICESTATUS 1978
#define EXPORT_PHQUERYSERVICEVARIABLESIZE 1979
#define EXPORT_PHSTARTSERVICE 1980
#define EXPORT_PHSTOPSERVICE 1981
#define EXPORT_PHCREATESYMBOLPROVIDER 1982
#define EXPORT_PHGETLINEFROMADDRESS 1983
#define EXPORT_PHGETMODULEFROMADDRESS 1984
#define EXPORT_PHGETSYMBOLFROMADDRESS 1985
#define EXPORT_PHGETSYMBOLFROMNAME 1986
#define EXPORT_PHLOADSYMBOLPROVIDERMODULES 1987
#define EXPORT_PHLOADMODULESYMBOLPROVIDER 1988
#define EXPORT_PHLOADMODULESFORVIRTUALSYMBOLPROVIDER 1989
#define EXPORT_PHSETOPTIONSSYMBOLPROVIDER 1990
#define EXPORT_PHSETSEARCHPATHSYMBOLPROVIDER 1991
#define EXPORT_PHSTACKWALK 1992
#define EXPORT_PHWALKTHREADSTACK 1993
#define EXPORT_PHWRITEMINIDUMPPROCESS 1994
#define EXPORT_PHVERIFYFILE 1995
#define EXPORT_PHVERIFYFILEISCHAINEDTOMICROSOFT 1996
#define EXPORT_PHDELETEWORKQUEUE 1997
#define EXPORT_PHGETGLOBALWORKQUEUE 1998
#define EXPORT_PHINITIALIZEWORKQUEUE 1999
#define EXPORT_PHINITIALIZEWORKQUEUEENVIRONMENT 2000
#define EXPORT_PHQUEUEITEMWORKQUEUE 2001
#define EXPORT_PHQUEUEITEMWORKQUEUEEX 2002
#define EXPORT_PHWAITFORWORKQUEUE 2003
#define EXPORT_PHADDJSONARRAYOBJECT 2004
#define EXPORT_PHADDJSONOBJECT 2005
#define EXPORT_PHADDJSONOBJECT2 2006
#define EXPORT_PHADDJSONOBJECTINT64 2007
#define EXPORT_PHADDJSONOBJECTUINT64 2008
#define EXPORT_PHADDJSONOBJECTVALUE 2009
#define EXPORT_PHCREATEJSONARRAY 2010
#define EXPORT_PHCREATEJSONOBJECT 2011
#define EXPORT_PHCREATEJSONPARSER 2012
#define EXPORT_PHCREATEJSONPARSEREX 2013
#define EXPORT_PHFREEJSONOBJECT 2014
#define EXPORT_PHGETJSONARRAYINDEXOBJECT 2015
#define EXPORT_PHGETJSONARRAYLENGTH 2016
#define EXPORT_PHGETJSONARRAYLONG64 2017
#define EXPORT_PHGETJSONARRAYSTRING 2018
#define EXPORT_PHGETJSONOBJECT 2019
#define EXPORT_PHGETJSONOBJECTASARRAYLIST 2020
#define EXPORT_PHGETJSONOBJECTBOOL 2021
#define EXPORT_PHGETJSONOBJECTLENGTH 2022
#define EXPORT_PHGETJSONOBJECTTYPE 2023
#define EXPORT_PHGETJSONVALUEASINT64 2024
#define EXPORT_PHGETJSONVALUEASSTRING 2025
#define EXPORT_PHGETJSONVALUEASUINT64 2026
#define EXPORT_PHCREATEXMLNODE 2027
#define EXPORT_PHCREATEXMLOPAQUENODE 2028
#define EXPORT_PHFINDXMLOBJECT 2029
#define EXPORT_PHFREEXMLOBJECT 2030
#define EXPORT_PHGETXMLINTERFACE 2031
#define EXPORT_PHGETXMLNODEATTRIBUTEBYINDEX 2032
#define EXPORT_PHGETXMLNODEATTRIBUTECOUNT 2033
#define EXPORT_PHGETXMLNODEATTRIBUTETEXT 2034
#define EXPORT_PHGETXMLNODEELEMENTTEXT 2035
#define EXPORT_PHGETXMLNODEFIRSTCHILD 2036
#define EXPORT_PHGETXMLNODENEXTCHILD 2037
#define EXPORT_PHGETXMLNODEOPAQUETEXT 2038
#define EXPORT_PHGETXMLOBJECT 2039
#define EXPORT_PHLOADXMLOBJECTFROMFILE 2040
#define EXPORT_PHLOADXMLOBJECTFROMSTRING 2041
#define EXPORT_PHSAVEXMLOBJECTTOFILE 2042
#define EXPORT_PHSETXMLNODEATTRIBUTETEXT 2043
#define EXPORT_PHCLEARCACHEDIRECTORY 2044
#define EXPORT_PHCREATECACHEFILE 2045
#define EXPORT_PHDELETECACHEFILE 2046
#define EXPORT_PHAPPRESOLVERGETAPPIDFORWINDOW 2047
#define EXPORT_PHGETPROCESSPACKAGEFULLNAME 2048
#define EXPORT_PHDNSFREE 2049
#define EXPORT_PHDNSQUERY 2050
#define EXPORT_PHDNSQUERY2 2051
#define EXPORT_PHHTTPDNSQUERY 2052
#define EXPORT_PHHTTPSOCKETADDREQUESTHEADERS 2053
#define EXPORT_PHHTTPSOCKETBEGINREQUEST 2054
#define EXPORT_PHHTTPSOCKETCLOSE 2055
#define EXPORT_PHHTTPSOCKETCONNECT 2056
#define EXPORT_PHHTTPSOCKETCREATE 2057
#define EXPORT_PHHTTPSOCKETDESTROY 2058
#define EXPORT_PHHTTPSOCKETDOWNLOADSTRING 2059
#define EXPORT_PHHTTPSOCKETENDREQUEST 2060
#define EXPORT_PHHTTPSOCKETGETERRORMESSAGE 2061
#define EXPORT_PHHTTPSOCKETPARSEURL 2062
#define EXPORT_PHHTTPSOCKETQUERYHEADERSTRING 2063
#define EXPORT_PHHTTPSOCKETQUERYHEADERULONG 2064
#define EXPORT_PHHTTPSOCKETQUERYHEADERULONG64 2065
#define EXPORT_PHHTTPSOCKETQUERYHEADERS 2066
#define EXPORT_PHHTTPSOCKETQUERYOPTIONSTRING 2067
#define EXPORT_PHHTTPSOCKETREADDATA 2068
#define EXPORT_PHHTTPSOCKETREADDATATOBUFFER 2069
#define EXPORT_PHHTTPSOCKETSENDREQUEST 2070
#define EXPORT_PHHTTPSOCKETSETCREDENTIALS 2071
#define EXPORT_PHHTTPSOCKETSETFEATURE 2072
#define EXPORT_PHHTTPSOCKETSETSECURITY 2073
#define EXPORT_PHHTTPSOCKETWRITEDATA 2074
#define EXPORT_KSILEVEL 2075
#define EXPORT_KPHALPCQUERYINFORMATION 2076
#define EXPORT_KPHDUPLICATEOBJECT 2077
#define EXPORT_KPHOPENDEVICE 2078
#define EXPORT_KPHOPENDEVICEBASEDEVICE 2079
#define EXPORT_KPHOPENDEVICEDRIVER 2080
#define EXPORT_KPHQUERYINFORMATIONDRIVER 2081
#define EXPORT_KPHQUERYINFORMATIONOBJECT 2082
#define EXPORT_KSIENUMERATEPROCESSHANDLES 2083
#define EXPORT_KSIQUERYHASHINFORMATIONFILE 2084
#endif _PH_EXPORT_DEF_H
================================================
FILE: SystemInformer/SystemInformer.manifest
================================================
System Informer
true/pm
true
true
PerMonitorV2, PerMonitor
true
UTF-8
SegmentHeap
================================================
FILE: SystemInformer/SystemInformer.rc
================================================
// Microsoft Visual C++ generated resource script.
//
#pragma code_page(65001)
#include "resource.h"
#define APSTUDIO_READONLY_SYMBOLS
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 2 resource.
//
#include "winres.h"
#include "include/phappres.h"
/////////////////////////////////////////////////////////////////////////////
#undef APSTUDIO_READONLY_SYMBOLS
/////////////////////////////////////////////////////////////////////////////
// English (United States) resources
#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
#ifdef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// TEXTINCLUDE
//
1 TEXTINCLUDE
BEGIN
"resource.h\0"
END
2 TEXTINCLUDE
BEGIN
"#include ""winres.h""\r\n"
"#include ""include/phappres.h""\0"
END
3 TEXTINCLUDE
BEGIN
"\0"
END
#endif // APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// PNG
//
IDB_SEARCH_ACTIVE_MODERN_LIGHT PNG "resources\\search_stop_modern_light.png"
IDB_SEARCH_ACTIVE PNG "resources\\active_search.png"
IDB_SEARCH_INACTIVE PNG "resources\\inactive_search.png"
IDB_SEARCH_ACTIVE_SMALL PNG "resources\\active_search_small.png"
IDB_SEARCH_INACTIVE_SMALL PNG "resources\\inactive_search_small.png"
IDB_SEARCH_REGEX_MODERN_DARK PNG "resources\\regex_modern_dark.png"
IDB_SEARCH_REGEX_MODERN_LIGHT PNG "resources\\regex_modern_light.png"
IDB_SEARCH_CASE_MODERN_DARK PNG "resources\\case_sensitive_modern_dark.png"
IDB_SEARCH_CASE_MODERN_LIGHT PNG "resources\\case_sensitive_modern_light.png"
IDB_SEARCH_ACTIVE_MODERN_DARK PNG "resources\\search_stop_modern_dark.png"
IDB_SEARCH_INACTIVE_MODERN_LIGHT PNG "resources\\search_modern_light.png"
IDB_SEARCH_INACTIVE_MODERN_DARK PNG "resources\\search_modern_dark.png"
/////////////////////////////////////////////////////////////////////////////
//
// Icon
//
// Icon with lowest ID value placed first to ensure application icon
// remains consistent on all systems.
IDI_PROCESSHACKER ICON "SystemInformer.ico"
IDI_PHAPPLICATION ICON "resources\\application.ico"
IDI_COG ICON "resources\\cog.ico"
IDI_PIN ICON "resources\\pin.ico"
IDI_FOLDER ICON "resources\\folder.ico"
IDI_MAGNIFIER ICON "resources\\magnifier.ico"
IDI_UACSHIELD ICON "resources\\shield.ico"
/////////////////////////////////////////////////////////////////////////////
//
// Dialog
//
IDD_PROCGENERAL DIALOGEX 0, 0, 260, 260
STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU
CAPTION "General"
FONT 8, "MS Shell Dlg", 400, 0, 0x1
BEGIN
PUSHBUTTON "Permissions",IDC_PERMISSIONS,143,218,50,14
PUSHBUTTON "Terminate",IDC_TERMINATE,197,218,50,14
GROUPBOX "File",IDC_FILE,7,7,246,103
ICON "",IDC_FILEICON,14,18,20,20
EDITTEXT IDC_NAME,44,17,182,12,ES_AUTOHSCROLL | ES_READONLY | NOT WS_BORDER
EDITTEXT IDC_COMPANYNAME,44,29,183,12,ES_AUTOHSCROLL | ES_READONLY | NOT WS_BORDER
CONTROL "Company name link",IDC_COMPANYNAME_LINK,"SysLink",LWS_NOPREFIX | NOT WS_VISIBLE | WS_TABSTOP,46,29,183,9
LTEXT "Version:",IDC_STATIC,15,41,27,8
EDITTEXT IDC_VERSION,44,41,113,12,ES_AUTOHSCROLL | ES_READONLY | NOT WS_BORDER
LTEXT "Image file name (Win32):",IDC_STATIC,15,55,86,8
EDITTEXT IDC_FILENAME,15,65,191,12,ES_AUTOHSCROLL | ES_READONLY
PUSHBUTTON "Inspect",IDC_INSPECT,210,64,17,15,BS_ICON
PUSHBUTTON "Open",IDC_OPENFILENAME,230,64,17,15,BS_ICON
GROUPBOX "Process",IDC_PROCESS,7,112,246,142
LTEXT "Command line:",IDC_STATIC,13,124,50,8
EDITTEXT IDC_CMDLINE,79,122,147,12,ES_AUTOHSCROLL | ES_READONLY
LTEXT "Current directory:",IDC_STATIC,13,140,60,8
EDITTEXT IDC_CURDIR,79,138,168,12,ES_AUTOHSCROLL | ES_READONLY
LTEXT "Started:",IDC_STATIC,13,156,28,8
EDITTEXT IDC_STARTED,79,154,168,12,ES_AUTOHSCROLL | ES_READONLY
LTEXT "Parent console:",IDC_STATIC,13,172,52,8
EDITTEXT IDC_PARENTCONSOLE,79,170,168,12,ES_AUTOHSCROLL | ES_READONLY
LTEXT "Parent process:",IDC_STATIC,13,188,52,8
EDITTEXT IDC_PARENTPROCESS,79,186,147,12,ES_AUTOHSCROLL | ES_READONLY
PUSHBUTTON "View",IDC_VIEWPARENTPROCESS,230,185,17,15,BS_ICON
LTEXT "Mitigation policies:",IDC_STATIC,13,204,59,8
EDITTEXT IDC_MITIGATION,79,202,126,12,ES_AUTOHSCROLL | ES_READONLY
PUSHBUTTON "Details",IDC_VIEWMITIGATION,209,201,38,14
LTEXT "Protection:",IDC_STATIC,13,222,40,8
EDITTEXT IDC_PROTECTION,53,222,80,12,ES_AUTOHSCROLL | ES_READONLY | NOT WS_BORDER
LTEXT "Image type:",IDC_STATIC,13,233,40,8
EDITTEXT IDC_PROCESSTYPETEXT,53,233,160,12,ES_AUTOHSCROLL | ES_READONLY | NOT WS_BORDER
PUSHBUTTON "View",IDC_VIEWCOMMANDLINE,230,121,17,15,BS_ICON
LTEXT "Image file name:",IDC_STATIC,15,80,86,8
EDITTEXT IDC_FILENAMEWIN32,15,91,191,12,ES_AUTOHSCROLL | ES_READONLY
PUSHBUTTON "Inspect",IDC_INSPECT2,210,90,17,15,BS_ICON
PUSHBUTTON "Open",IDC_OPENFILENAME2,230,90,17,15,BS_ICON
PUSHBUTTON "Policy",IDC_INTEGRITY,90,218,50,14
END
IDD_PROCMODULES DIALOGEX 0, 0, 260, 260
STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU
CAPTION "Modules"
FONT 8, "MS Shell Dlg", 400, 0, 0x1
BEGIN
CONTROL "",IDC_LIST,"PhTreeNew",WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_TABSTOP | 0xa,2,19,256,239,WS_EX_CLIENTEDGE
EDITTEXT IDC_SEARCH,114,3,144,14,ES_AUTOHSCROLL
PUSHBUTTON "Options",IDC_FILTEROPTIONS,2,2,50,14
END
IDD_PROCTHREADS DIALOGEX 0, 0, 260, 260
STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU
CAPTION "Threads"
FONT 8, "MS Shell Dlg", 400, 0, 0x1
BEGIN
CONTROL "",IDC_LIST,"PhTreeNew",WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_TABSTOP | 0xa,2,19,256,239,WS_EX_CLIENTEDGE
EDITTEXT IDC_SEARCH,114,3,144,14,ES_AUTOHSCROLL
PUSHBUTTON "Options",IDC_OPTIONS,2,2,50,14
LTEXT "",IDC_STATE,56,5,8,8
END
IDD_PROCHANDLES DIALOGEX 0, 0, 260, 260
STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU
CAPTION "Handles"
FONT 8, "MS Shell Dlg", 400, 0, 0x1
BEGIN
PUSHBUTTON "Options",IDC_OPTIONS,2,2,50,14
EDITTEXT IDC_HANDLESEARCH,114,3,144,14,ES_AUTOHSCROLL
CONTROL "",IDC_LIST,"PhTreeNew",WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_TABSTOP | 0x2,2,19,256,239,WS_EX_CLIENTEDGE
END
IDD_PROCENVIRONMENT DIALOGEX 0, 0, 260, 260
STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU
CAPTION "Environment"
FONT 8, "MS Shell Dlg", 400, 0, 0x1
BEGIN
CONTROL "",IDC_LIST,"PhTreeNew",WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_TABSTOP | 0xa,2,19,256,239,WS_EX_CLIENTEDGE
EDITTEXT IDC_SEARCH,114,3,144,14,ES_AUTOHSCROLL
PUSHBUTTON "Refresh",IDC_REFRESH,53,2,50,14
PUSHBUTTON "Options",IDC_OPTIONS,2,2,50,14
END
IDD_THRDSTACK DIALOGEX 0, 0, 273, 228
STYLE DS_SETFONT | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME
CAPTION "Thread Stack"
FONT 8, "MS Shell Dlg", 400, 0, 0x1
BEGIN
PUSHBUTTON "Copy",IDC_COPY,113,212,50,14
PUSHBUTTON "Refresh",IDC_REFRESH,167,212,50,14
PUSHBUTTON "Close",IDOK,221,212,50,14
CONTROL "",IDC_TREELIST,"PhTreeNew",WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_TABSTOP | 0x2,0,0,273,211,WS_EX_CLIENTEDGE
PUSHBUTTON "Options",IDC_OPTIONS,2,212,50,14
END
IDD_ABOUT DIALOGEX 0, 0, 270, 215
STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | DS_CENTER | WS_POPUP | WS_CAPTION | WS_SYSMENU
CAPTION "About"
FONT 8, "MS Shell Dlg", 400, 0, 0x1
BEGIN
ICON IDI_PROCESSHACKER,IDC_FILEICON,16,11,20,20
CONTROL "System Informer",IDC_ABOUT_NAME,"SysLink",WS_TABSTOP,45,12,192,8
LTEXT "Copyright (c) Winsider Seminars && Solutions, Inc.",IDC_STATIC,45,24,193,8
CONTROL "Credits.",IDC_CREDITS,"SysLink",WS_TABSTOP,15,49,248,141
CONTROL "System Informer on SourceForge.net",IDC_LINK_SF,
"SysLink",WS_TABSTOP,7,197,130,11
PUSHBUTTON "Diagnostics",IDC_DIAGNOSTICS,160,194,50,14
DEFPUSHBUTTON "OK",IDOK,213,194,50,14
END
IDD_SRVLIST DIALOGEX 0, 0, 237, 201
STYLE DS_SETFONT | DS_FIXEDSYS | DS_CONTROL | WS_CHILD | WS_SYSMENU
FONT 8, "MS Shell Dlg", 400, 0, 0x1
BEGIN
CONTROL "",IDC_LIST,"SysListView32",LVS_REPORT | LVS_SHOWSELALWAYS | LVS_ALIGNLEFT | WS_BORDER | WS_TABSTOP,0,0,237,141
EDITTEXT IDC_DESCRIPTION,0,144,237,40,ES_MULTILINE | ES_READONLY | NOT WS_BORDER
PUSHBUTTON "&Start",IDC_START,134,187,50,14
PUSHBUTTON "&Pause",IDC_PAUSE,187,187,50,14
END
IDD_SRVGENERAL DIALOGEX 0, 0, 282, 183
STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU
CAPTION "General"
FONT 8, "MS Shell Dlg", 400, 0, 0x1
BEGIN
EDITTEXT IDC_DESCRIPTION,7,7,268,45,ES_MULTILINE | ES_READONLY | WS_VSCROLL
LTEXT "Type:",IDC_STATIC,7,57,20,8
COMBOBOX IDC_TYPE,30,56,110,65,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
LTEXT "Start type:",IDC_STATIC,147,57,38,8
COMBOBOX IDC_STARTTYPE,188,56,87,30,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
LTEXT "Error control:",IDC_STATIC,7,75,47,8
COMBOBOX IDC_ERRORCONTROL,58,73,82,30,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
LTEXT "Group:",IDC_STATIC,147,75,23,8
EDITTEXT IDC_GROUP,173,74,102,12,ES_AUTOHSCROLL
LTEXT "Binary path:",IDC_STATIC,7,92,40,8
EDITTEXT IDC_BINARYPATH,58,91,163,12,ES_AUTOHSCROLL
PUSHBUTTON "Browse...",IDC_BROWSE,225,90,50,14
LTEXT "User account:",IDC_STATIC,7,110,46,8
EDITTEXT IDC_USERACCOUNT,58,108,217,12,ES_AUTOHSCROLL
LTEXT "Password:",IDC_STATIC,7,127,34,8
EDITTEXT IDC_PASSWORD,58,125,204,12,ES_PASSWORD | ES_AUTOHSCROLL
CONTROL "",IDC_PASSWORDCHECK,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,266,126,9,10
LTEXT "Service DLL:",IDC_STATIC,7,144,48,8
EDITTEXT IDC_SERVICEDLL,58,142,217,12,ES_AUTOHSCROLL | ES_READONLY
CONTROL "Delayed start",IDC_DELAYEDSTART,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,158,59,10
PUSHBUTTON "Permissions",IDC_PERMISSIONS,225,162,50,14
END
IDD_HNDLGENERAL DIALOGEX 0, 0, 260, 181
STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU
CAPTION "General"
FONT 8, "MS Shell Dlg", 400, 0, 0x1
BEGIN
CONTROL "",IDC_LIST,"SysListView32",LVS_REPORT | LVS_ALIGNLEFT | LVS_NOCOLUMNHEADER | WS_TABSTOP,0,2,260,179
END
IDD_INFORMATION DIALOGEX 0, 0, 317, 184
STYLE DS_SETFONT | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME
CAPTION "Information"
FONT 8, "MS Shell Dlg", 400, 0, 0x1
BEGIN
EDITTEXT IDC_TEXT,7,7,303,152,ES_MULTILINE | ES_READONLY | WS_VSCROLL
PUSHBUTTON "Save...",IDC_SAVE,154,163,50,14
PUSHBUTTON "Copy",IDC_COPY,207,163,50,14
DEFPUSHBUTTON "Close",IDOK,260,163,50,14
END
IDD_FINDOBJECTS DIALOGEX 0, 0, 357, 233
STYLE DS_SETFONT | DS_FIXEDSYS | WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_POPUP | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME
CAPTION "Find Handles or DLLs"
FONT 8, "MS Shell Dlg", 400, 0, 0x1
BEGIN
EDITTEXT IDC_FILTER,97,4,207,14,ES_AUTOHSCROLL
PUSHBUTTON "Find",IDOK,306,4,50,14
COMBOBOX IDC_FILTERTYPE,2,5,93,30,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
CONTROL "",IDC_TREELIST,"PhTreeNew",WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_TABSTOP | 0x2,2,20,353,212,WS_EX_CLIENTEDGE
END
IDD_THRDSTACKS DIALOGEX 0, 0, 443, 254
STYLE DS_SETFONT | DS_FIXEDSYS | WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_POPUP | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME
CAPTION "Thread Stacks"
FONT 8, "MS Shell Dlg", 400, 0, 0x1
BEGIN
EDITTEXT IDC_FILTER,2,3,388,14,ES_AUTOHSCROLL
PUSHBUTTON "Refresh",IDC_REFRESH,391,3,50,14
CONTROL "",IDC_TREELIST,"PhTreeNew",WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_TABSTOP | 0x2,2,19,439,219,WS_EX_CLIENTEDGE
LTEXT "",IDC_MESSAGE,8,242,427,10
END
IDD_USRLIST DIALOGEX 0, 0, 443, 254
STYLE DS_SETFONT | DS_FIXEDSYS | WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_POPUP | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME
CAPTION "Users List"
FONT 8, "MS Shell Dlg", 400, 0, 0x1
BEGIN
EDITTEXT IDC_FILTER,2,3,388,14,ES_AUTOHSCROLL
PUSHBUTTON "Refresh",IDC_REFRESH,391,3,50,14
CONTROL "",IDC_TREELIST,"PhTreeNew",WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_TABSTOP | 0x2,2,19,439,219,WS_EX_CLIENTEDGE
LTEXT "",IDC_MESSAGE,8,242,427,10
END
IDD_OBJTOKEN DIALOGEX 0, 0, 260, 260
STYLE DS_SETFONT | DS_FIXEDSYS | WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_POPUP | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME
CAPTION "Token"
FONT 8, "MS Shell Dlg", 400, 0, 0x1
BEGIN
LTEXT "User:",IDC_TOKENUSER,7,7,18,8
EDITTEXT IDC_USER,48,7,205,12,ES_AUTOHSCROLL | ES_READONLY | NOT WS_BORDER
LTEXT "User SID:",IDC_TOKENSID,7,18,32,8
EDITTEXT IDC_USERSID,48,18,205,12,ES_AUTOHSCROLL | ES_READONLY | NOT WS_BORDER
LTEXT "Session:",IDC_STATIC,7,29,28,8
LTEXT "Unknown",IDC_SESSIONID,38,29,30,8
LTEXT "Elevated:",IDC_STATIC,78,29,32,8
LTEXT "Unknown",IDC_ELEVATED,113,29,45,8
LTEXT "Virtualized:",IDC_STATIC,169,29,36,8
LTEXT "Unknown",IDC_VIRTUALIZED,209,29,44,8
CONTROL "",IDC_GROUPS,"SysListView32",LVS_REPORT | LVS_SHOWSELALWAYS | LVS_SHAREIMAGELISTS | LVS_ALIGNLEFT | WS_BORDER | WS_TABSTOP,7,42,246,194
PUSHBUTTON "Integrity",IDC_INTEGRITY,149,239,50,14
PUSHBUTTON "Advanced",IDC_ADVANCED,203,239,50,14
PUSHBUTTON "Permissions",IDC_PERMISSIONS,95,239,50,14
PUSHBUTTON "Default token",IDC_DEFAULTPERM,41,239,50,14
END
IDD_ZOMBIEPROCESSES DIALOGEX 0, 0, 337, 221
STYLE DS_SETFONT | DS_FIXEDSYS | WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_POPUP | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME
CAPTION "Zombie Processes"
FONT 8, "MS Shell Dlg", 400, 0, 0x1
BEGIN
LTEXT "Processes highlighted red are hidden while those highlighted grey have terminated.",IDC_INTRO,7,7,323,12
CONTROL "",IDC_PROCESSES,"SysListView32",LVS_REPORT | LVS_SHOWSELALWAYS | LVS_ALIGNLEFT | WS_BORDER | WS_TABSTOP,7,23,323,162
RTEXT "",IDC_DESCRIPTION,7,187,323,11
COMBOBOX IDC_METHOD,7,201,73,30,CBS_DROPDOWNLIST | CBS_SORT | WS_VSCROLL | WS_TABSTOP
PUSHBUTTON "Terminate",IDC_TERMINATE,115,200,50,14
PUSHBUTTON "Save...",IDC_SAVE,170,200,50,14
PUSHBUTTON "&Scan",IDC_SCAN,225,200,50,14
DEFPUSHBUTTON "Close",IDOK,280,200,50,14
END
IDD_RUNAS DIALOGEX 0, 0, 293, 127
STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU
CAPTION "Run As"
FONT 8, "MS Shell Dlg", 400, 0, 0x1
BEGIN
LTEXT "Enter the command to start as the specified user.",IDC_TITLE,7,7,160,8
LTEXT "Program:",IDC_STATIC,7,23,30,8
LTEXT "User name:",IDC_STATIC,7,40,38,8
COMBOBOX IDC_USERNAME,53,38,123,12,CBS_DROPDOWN | CBS_AUTOHSCROLL | WS_VSCROLL | WS_TABSTOP
LTEXT "Type:",IDC_STATIC,184,40,20,8
COMBOBOX IDC_TYPE,208,38,78,12,CBS_DROPDOWNLIST | CBS_SORT | WS_VSCROLL | WS_TABSTOP
LTEXT "Password:",IDC_STATIC,7,56,34,8
EDITTEXT IDC_PASSWORD,53,55,123,12,ES_PASSWORD | ES_AUTOHSCROLL
CONTROL "Toggle elevation",IDC_TOGGLEELEVATION,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,183,56,69,10
LTEXT "Session ID:",IDC_STATIC,7,73,37,8
LTEXT "Desktop:",IDC_STATIC,7,90,30,8
DEFPUSHBUTTON "OK",IDOK,127,106,50,14
PUSHBUTTON "Cancel",IDCANCEL,182,106,50,14
COMBOBOX IDC_SESSIONCOMBO,53,72,123,12,CBS_DROPDOWNLIST | CBS_AUTOHSCROLL | CBS_SORT | WS_VSCROLL | WS_TABSTOP
COMBOBOX IDC_DESKTOPCOMBO,53,88,123,12,CBS_DROPDOWNLIST | CBS_AUTOHSCROLL | CBS_SORT | WS_VSCROLL | WS_TABSTOP
PUSHBUTTON "Browse",IDC_BROWSE,237,106,50,14
CONTROL "Create suspended",IDC_TOGGLESUSPENDED,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,183,70,75,10
COMBOBOX IDC_PROGRAMCOMBO,53,21,233,12,CBS_DROPDOWN | CBS_AUTOHSCROLL | WS_VSCROLL | WS_TABSTOP
CONTROL "Create UIAccess",IDC_TOGGLEUIACCESS,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,183,84,69,10
END
IDD_PROGRESS DIALOGEX 0, 0, 216, 62
STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU
CAPTION "Progress"
FONT 8, "MS Shell Dlg", 400, 0, 0x1
BEGIN
PUSHBUTTON "Cancel",IDCANCEL,159,41,50,14
CONTROL "",IDC_PROGRESS,"msctls_progress32",0x0,7,23,202,14
LTEXT "Please wait...",IDC_PROGRESSTEXT,7,7,202,12
END
IDD_PAGEFILES DIALOGEX 0, 0, 322, 162
STYLE DS_SETFONT | DS_FIXEDSYS | WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_POPUP | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME
CAPTION "Pagefiles"
FONT 8, "MS Shell Dlg", 400, 0, 0x1
BEGIN
CONTROL "",IDC_LIST,"SysListView32",LVS_REPORT | LVS_SINGLESEL | LVS_SHOWSELALWAYS | LVS_ALIGNLEFT | LVS_NOSORTHEADER | WS_BORDER | WS_TABSTOP,2,2,318,142
PUSHBUTTON "Refresh",IDC_REFRESH,215,146,50,14
DEFPUSHBUTTON "Close",IDOK,269,146,50,14
END
IDD_TOKGENERAL DIALOGEX 0, 0, 270, 228
STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU
CAPTION "General"
FONT 8, "MS Shell Dlg", 400, 0, 0x1
BEGIN
GROUPBOX "Token",IDC_STATIC,7,7,256,149
LTEXT "User:",IDC_STATIC,15,19,18,8
EDITTEXT IDC_USER,71,17,185,12,ES_AUTOHSCROLL | ES_READONLY
LTEXT "User SID:",IDC_STATIC,15,36,32,8
EDITTEXT IDC_USERSID,71,34,185,12,ES_AUTOHSCROLL | ES_READONLY
LTEXT "Owner:",IDC_STATIC,15,53,25,8
EDITTEXT IDC_OWNER,71,51,185,12,ES_AUTOHSCROLL | ES_READONLY
LTEXT "Primary group:",IDC_STATIC,15,70,49,8
EDITTEXT IDC_PRIMARYGROUP,71,68,185,12,ES_AUTOHSCROLL | ES_READONLY
LTEXT "Session ID:",IDC_STATIC,15,87,37,8
EDITTEXT IDC_SESSIONID,71,85,88,12,ES_AUTOHSCROLL | ES_READONLY
LTEXT "Elevated:",IDC_STATIC,15,104,32,8
EDITTEXT IDC_ELEVATED,71,102,117,12,ES_AUTOHSCROLL | ES_READONLY
LTEXT "Virtualization:",IDC_STATIC,15,121,44,8
EDITTEXT IDC_VIRTUALIZATION,71,119,185,12,ES_AUTOHSCROLL | ES_READONLY
LTEXT "UIAccess:",IDC_STATIC,15,138,32,8
EDITTEXT IDC_UIACCESS,71,136,185,12,ES_AUTOHSCROLL | ES_READONLY
PUSHBUTTON "Linked token",IDC_LINKEDTOKEN,193,101,63,14
GROUPBOX "Source",IDC_STATIC,7,160,256,47
LTEXT "Name:",IDC_STATIC,15,172,22,8
EDITTEXT IDC_SOURCENAME,71,170,185,12,ES_AUTOHSCROLL | ES_READONLY
LTEXT "LUID:",IDC_STATIC,15,190,19,8
EDITTEXT IDC_SOURCELUID,71,188,185,12,ES_AUTOHSCROLL | ES_READONLY
END
IDD_TOKADVANCED DIALOGEX 0, 0, 236, 186
STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU
CAPTION "Advanced"
FONT 8, "MS Shell Dlg", 400, 0, 0x1
BEGIN
CONTROL "",IDC_LIST,"SysListView32",LVS_REPORT | LVS_ALIGNLEFT | WS_TABSTOP,0,0,236,186
END
IDD_OBJJOB DIALOGEX 0, 0, 260, 260
STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU
CAPTION "Job"
FONT 8, "MS Shell Dlg", 400, 0, 0x1
BEGIN
LTEXT "Name:",IDC_STATIC,7,9,22,8
EDITTEXT IDC_NAME,35,8,164,12,ES_AUTOHSCROLL
PUSHBUTTON "Terminate",IDC_TERMINATE,203,7,50,14
LTEXT "Processes in job:",IDC_STATIC,7,25,55,8
CONTROL "",IDC_PROCESSES,"SysListView32",LVS_REPORT | LVS_SHOWSELALWAYS | LVS_SORTASCENDING | LVS_ALIGNLEFT | LVS_NOCOLUMNHEADER | LVS_NOSORTHEADER | WS_BORDER | WS_TABSTOP,7,38,246,62
PUSHBUTTON "Add...",IDC_ADD,203,103,50,14
LTEXT "Limits:",IDC_STATIC,7,117,21,8
CONTROL "",IDC_LIMITS,"SysListView32",LVS_REPORT | LVS_SHOWSELALWAYS | LVS_SORTASCENDING | LVS_ALIGNLEFT | LVS_NOSORTHEADER | WS_BORDER | WS_TABSTOP,7,129,246,107
PUSHBUTTON "Advanced",IDC_ADVANCED,203,239,50,14
END
IDD_OBJEVENT DIALOGEX 0, 0, 186, 76
STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU
CAPTION "Event"
FONT 8, "MS Shell Dlg", 400, 0, 0x1
BEGIN
LTEXT "Type:",IDC_STATIC,7,7,20,8
LTEXT "Unknown",IDC_TYPE,42,7,137,8
LTEXT "Signaled:",IDC_STATIC,7,18,30,8
LTEXT "Unknown",IDC_SIGNALED,42,18,137,8
PUSHBUTTON "Set",IDC_SET,7,34,50,14
PUSHBUTTON "Reset",IDC_RESET,61,34,50,14
PUSHBUTTON "Pulse",IDC_PULSE,115,34,50,14
END
IDD_OBJMUTANT DIALOGEX 0, 0, 186, 76
STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU
CAPTION "Mutant"
FONT 8, "MS Shell Dlg", 400, 0, 0x1
BEGIN
LTEXT "Count:",IDC_STATIC,7,7,23,8
LTEXT "Unknown",IDC_COUNT,55,7,124,8
LTEXT "Abandoned:",IDC_STATIC,7,18,40,8
LTEXT "Unknown",IDC_ABANDONED,55,18,124,8
LTEXT "Owner:",IDC_OWNERLABEL,7,29,25,8
LTEXT "Unknown",IDC_OWNER,55,29,124,8
END
IDD_OBJSEMAPHORE DIALOGEX 0, 0, 186, 76
STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU
CAPTION "Semaphore"
FONT 8, "MS Shell Dlg", 400, 0, 0x1
BEGIN
LTEXT "Current count:",IDC_STATIC,7,7,50,8
LTEXT "Unknown",IDC_CURRENTCOUNT,66,7,113,8
LTEXT "Maximum count:",IDC_STATIC,7,18,54,8
LTEXT "Unknown",IDC_MAXIMUMCOUNT,66,18,113,8
PUSHBUTTON "Acquire",IDC_ACQUIRE,7,34,50,14
PUSHBUTTON "Release",IDC_RELEASE,61,34,50,14
END
IDD_OBJTIMER DIALOGEX 0, 0, 186, 76
STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU
CAPTION "Timer"
FONT 8, "MS Shell Dlg", 400, 0, 0x1
BEGIN
LTEXT "Signaled:",-1,7,7,30,8
LTEXT "Unknown",IDC_SIGNALED,42,7,137,8
PUSHBUTTON "Cancel",IDC_CANCEL,7,19,50,14
END
IDD_JOBSTATISTICS DIALOGEX 0, 0, 259, 186
STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU
CAPTION "Statistics"
FONT 8, "MS Shell Dlg", 400, 0, 0x1
BEGIN
GROUPBOX "General",IDC_STATIC,7,7,133,47
LTEXT "Active processes",IDC_STATIC,15,18,55,8
RTEXT "Static",IDC_ZACTIVEPROCESSES_V,99,18,37,8,SS_ENDELLIPSIS
LTEXT "Total processes",IDC_STATIC,15,28,51,8
RTEXT "Static",IDC_ZTOTALPROCESSES_V,95,29,41,8,SS_ENDELLIPSIS
LTEXT "Terminated due to job limits",IDC_STATIC,15,39,98,8
RTEXT "Static",IDC_ZTERMINATEDPROCESSES_V,107,39,29,8,SS_ENDELLIPSIS
GROUPBOX "Time",IDC_STATIC,7,57,133,57
LTEXT "User time",IDC_STATIC,15,68,32,8
LTEXT "Kernel time",IDC_STATIC,15,79,38,8
LTEXT "User time (period)",IDC_STATIC,15,89,60,8
LTEXT "Kernel time (period)",IDC_STATIC,15,99,65,8
RTEXT "Static",IDC_ZUSERTIME_V,83,68,53,8,SS_ENDELLIPSIS
RTEXT "Static",IDC_ZKERNELTIME_V,85,79,51,8,SS_ENDELLIPSIS
RTEXT "Static",IDC_ZUSERTIMEPERIOD_V,87,89,49,8,SS_ENDELLIPSIS
RTEXT "Static",IDC_ZKERNELTIMEPERIOD_V,87,99,49,8,SS_ENDELLIPSIS
GROUPBOX "Memory",IDC_STATIC,7,118,133,48
LTEXT "Page faults",IDC_STATIC,15,129,38,8
LTEXT "Peak process usage",IDC_STATIC,15,140,65,8
LTEXT "Peak job usage",IDC_STATIC,15,151,52,8
RTEXT "Static",IDC_ZPAGEFAULTS_V,86,129,50,8,SS_ENDELLIPSIS
RTEXT "Static",IDC_ZPEAKPROCESSUSAGE_V,85,140,51,8,SS_ENDELLIPSIS
RTEXT "Static",IDC_ZPEAKJOBUSAGE_V,86,151,50,8,SS_ENDELLIPSIS
GROUPBOX "I/O",IDC_STATIC,146,7,106,75
LTEXT "Reads",IDC_STATIC,152,17,21,8
LTEXT "Read bytes",IDC_STATIC,152,27,38,8
LTEXT "Writes",IDC_STATIC,152,37,22,8
LTEXT "Write bytes",IDC_STATIC,152,47,38,8
LTEXT "Other",IDC_STATIC,152,57,20,8
LTEXT "Other bytes",IDC_STATIC,152,67,40,8
RTEXT "Static",IDC_ZIOREADS_V,200,17,47,8,SS_ENDELLIPSIS
RTEXT "Static",IDC_ZIOREADBYTES_V,200,27,47,8,SS_ENDELLIPSIS
RTEXT "Static",IDC_ZIOWRITES_V,200,37,47,8,SS_ENDELLIPSIS
RTEXT "Static",IDC_ZIOWRITEBYTES_V,200,47,47,8,SS_ENDELLIPSIS
RTEXT "Static",IDC_ZIOOTHER_V,200,57,47,8,SS_ENDELLIPSIS
RTEXT "Static",IDC_ZIOOTHERBYTES_V,200,67,47,8,SS_ENDELLIPSIS
END
IDD_OBJEVENTPAIR DIALOGEX 0, 0, 186, 76
STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU
CAPTION "Event Pair"
FONT 8, "MS Shell Dlg", 400, 0, 0x1
BEGIN
PUSHBUTTON "Set low",IDC_SETLOW,7,7,50,14
PUSHBUTTON "Set high",IDC_SETHIGH,61,7,50,14
END
IDD_AFFINITY DIALOGEX 0, 0, 279, 227
STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU
CAPTION "Affinity"
FONT 8, "MS Shell Dlg", 400, 0, 0x1
BEGIN
DEFPUSHBUTTON "OK",IDOK,169,206,50,14
PUSHBUTTON "Cancel",IDCANCEL,222,206,50,14
LTEXT "Affinity controls which CPUs threads are allowed to execute on.",IDC_STATIC,7,7,212,11
CONTROL "CPU 0",IDC_CPU0,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,21,35,10
CONTROL "CPU 1",IDC_CPU1,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,33,35,10
CONTROL "CPU 2",IDC_CPU2,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,44,35,10
CONTROL "CPU 3",IDC_CPU3,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,56,35,10
CONTROL "CPU 4",IDC_CPU4,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,67,35,10
CONTROL "CPU 5",IDC_CPU5,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,78,35,10
CONTROL "CPU 6",IDC_CPU6,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,89,35,10
CONTROL "CPU 7",IDC_CPU7,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,100,35,10
CONTROL "CPU 8",IDC_CPU8,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,111,35,10
CONTROL "CPU 9",IDC_CPU9,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,122,35,10
CONTROL "CPU 10",IDC_CPU10,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,134,39,10
CONTROL "CPU 11",IDC_CPU11,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,145,39,10
CONTROL "CPU 12",IDC_CPU12,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,156,39,10
CONTROL "CPU 13",IDC_CPU13,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,167,39,10
CONTROL "CPU 14",IDC_CPU14,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,178,39,10
CONTROL "CPU 15",IDC_CPU15,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,190,39,10
CONTROL "CPU 16",IDC_CPU16,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,79,21,39,10
CONTROL "CPU 17",IDC_CPU17,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,79,33,39,10
CONTROL "CPU 18",IDC_CPU18,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,79,44,39,10
CONTROL "CPU 19",IDC_CPU19,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,79,56,39,10
CONTROL "CPU 20",IDC_CPU20,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,79,67,39,10
CONTROL "CPU 21",IDC_CPU21,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,79,78,39,10
CONTROL "CPU 22",IDC_CPU22,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,79,89,39,10
CONTROL "CPU 23",IDC_CPU23,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,79,100,39,10
CONTROL "CPU 24",IDC_CPU24,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,79,111,39,10
CONTROL "CPU 25",IDC_CPU25,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,79,122,39,10
CONTROL "CPU 26",IDC_CPU26,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,79,134,39,10
CONTROL "CPU 27",IDC_CPU27,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,79,145,39,10
CONTROL "CPU 28",IDC_CPU28,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,79,156,39,10
CONTROL "CPU 29",IDC_CPU29,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,79,167,39,10
CONTROL "CPU 30",IDC_CPU30,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,79,178,39,10
CONTROL "CPU 31",IDC_CPU31,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,79,190,39,10
CONTROL "CPU 32",IDC_CPU32,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,150,21,39,10
CONTROL "CPU 33",IDC_CPU33,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,150,33,39,10
CONTROL "CPU 34",IDC_CPU34,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,150,44,39,10
CONTROL "CPU 35",IDC_CPU35,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,150,56,39,10
CONTROL "CPU 36",IDC_CPU36,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,150,67,39,10
CONTROL "CPU 37",IDC_CPU37,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,150,78,39,10
CONTROL "CPU 38",IDC_CPU38,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,150,89,39,10
CONTROL "CPU 39",IDC_CPU39,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,150,100,39,10
CONTROL "CPU 40",IDC_CPU40,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,150,111,39,10
CONTROL "CPU 41",IDC_CPU41,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,150,122,39,10
CONTROL "CPU 42",IDC_CPU42,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,150,134,39,10
CONTROL "CPU 43",IDC_CPU43,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,150,145,39,10
CONTROL "CPU 44",IDC_CPU44,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,150,156,39,10
CONTROL "CPU 45",IDC_CPU45,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,150,167,39,10
CONTROL "CPU 46",IDC_CPU46,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,150,178,39,10
CONTROL "CPU 47",IDC_CPU47,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,150,190,39,10
CONTROL "CPU 48",IDC_CPU48,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,224,21,39,10
CONTROL "CPU 49",IDC_CPU49,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,224,33,39,10
CONTROL "CPU 50",IDC_CPU50,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,224,44,39,10
CONTROL "CPU 51",IDC_CPU51,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,224,56,39,10
CONTROL "CPU 52",IDC_CPU52,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,224,67,39,10
CONTROL "CPU 53",IDC_CPU53,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,224,78,39,10
CONTROL "CPU 54",IDC_CPU54,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,224,89,39,10
CONTROL "CPU 55",IDC_CPU55,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,224,100,39,10
CONTROL "CPU 56",IDC_CPU56,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,224,111,39,10
CONTROL "CPU 57",IDC_CPU57,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,224,122,39,10
CONTROL "CPU 58",IDC_CPU58,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,224,134,39,10
CONTROL "CPU 59",IDC_CPU59,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,224,145,39,10
CONTROL "CPU 60",IDC_CPU60,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,224,156,39,10
CONTROL "CPU 61",IDC_CPU61,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,224,167,39,10
CONTROL "CPU 62",IDC_CPU62,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,224,178,39,10
CONTROL "CPU 63",IDC_CPU63,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,224,190,39,10
PUSHBUTTON "Select all",IDC_SELECTALL,7,206,50,14
PUSHBUTTON "Deselect all",IDC_DESELECTALL,60,206,50,14
COMBOBOX IDC_GROUPCPU,220,4,52,30,CBS_DROPDOWNLIST | CBS_SORT | NOT WS_VISIBLE | WS_VSCROLL | WS_TABSTOP
END
IDD_SYSINFO DIALOGEX 0, 0, 423, 247
STYLE DS_SETFONT | DS_FIXEDSYS | WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_POPUP | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME
EXSTYLE WS_EX_APPWINDOW
CAPTION "System Information"
FONT 8, "MS Shell Dlg", 400, 0, 0x1
BEGIN
END
IDD_EDITMESSAGE DIALOGEX 0, 0, 282, 163
STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU
CAPTION "Message"
FONT 8, "MS Shell Dlg", 400, 0, 0x1
BEGIN
LTEXT "Title:",IDC_STATIC,7,8,17,8
EDITTEXT IDC_TITLE,52,7,223,12,ES_AUTOHSCROLL
LTEXT "Text:",IDC_STATIC,7,24,18,8
EDITTEXT IDC_TEXT,52,23,223,79,ES_MULTILINE | ES_AUTOVSCROLL | ES_WANTRETURN | WS_VSCROLL
LTEXT "Icon:",IDC_STATIC,7,107,18,8
COMBOBOX IDC_TYPE,52,105,80,30,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
LTEXT "Timeout (s):",IDC_STATIC,7,123,40,8
EDITTEXT IDC_TIMEOUT,52,122,43,12,ES_AUTOHSCROLL
DEFPUSHBUTTON "OK",IDOK,172,142,50,14
PUSHBUTTON "Cancel",IDCANCEL,225,142,50,14
END
IDD_SESSION DIALOGEX 0, 0, 317, 267
STYLE DS_SETFONT | DS_FIXEDSYS | WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_POPUP | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME
CAPTION "Session Properties"
FONT 8, "MS Shell Dlg", 400, 0, 0x1
BEGIN
DEFPUSHBUTTON "Close",IDOK,265,251,50,14
CONTROL "",IDC_LIST,"SysListView32",LVS_REPORT | LVS_SHOWSELALWAYS | LVS_ALIGNLEFT | WS_BORDER | WS_TABSTOP,2,2,313,247
END
IDD_PROCMEMORY DIALOGEX 0, 0, 260, 260
STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU
CAPTION "Memory"
FONT 8, "MS Shell Dlg", 400, 0, 0x1
BEGIN
PUSHBUTTON "Refresh",IDC_REFRESH,53,2,50,14
CONTROL "",IDC_LIST,"PhTreeNew",WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_TABSTOP | 0xa,2,19,256,239,WS_EX_CLIENTEDGE
PUSHBUTTON "Options",IDC_FILTEROPTIONS,2,2,50,14
EDITTEXT IDC_SEARCH,114,3,144,14,ES_AUTOHSCROLL
END
IDD_CHOOSE DIALOGEX 0, 0, 199, 73
STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU
CAPTION "Dialog"
FONT 8, "MS Shell Dlg", 400, 0, 0x1
BEGIN
LTEXT "Text:",IDC_MESSAGE,7,7,185,8,SS_ENDELLIPSIS
COMBOBOX IDC_CHOICE,7,20,185,30,CBS_DROPDOWNLIST | CBS_AUTOHSCROLL | NOT WS_VISIBLE | WS_VSCROLL | WS_TABSTOP
CONTROL "Option",IDC_OPTION,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,39,185,10
DEFPUSHBUTTON "OK",IDOK,89,52,50,14
PUSHBUTTON "Cancel",IDCANCEL,142,52,50,14
COMBOBOX IDC_CHOICEUSER,7,20,185,30,CBS_DROPDOWN | CBS_AUTOHSCROLL | CBS_SORT | NOT WS_VISIBLE | WS_VSCROLL | WS_TABSTOP
EDITTEXT IDC_CHOICESIMPLE,7,20,185,13,ES_PASSWORD | ES_AUTOHSCROLL | NOT WS_VISIBLE
END
IDD_OPTGENERAL DIALOGEX 0, 0, 315, 220
STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU
CAPTION "General"
FONT 8, "MS Shell Dlg", 400, 0, 0x1
BEGIN
LTEXT "Search engine:",IDC_STATIC,7,8,49,8
EDITTEXT IDC_SEARCHENGINE,61,7,247,12,ES_AUTOHSCROLL
LTEXT "PE viewer:",IDC_STATIC,7,23,35,8
EDITTEXT IDC_PEVIEWER,61,22,247,12,ES_AUTOHSCROLL
LTEXT "Max. size unit:",IDC_STATIC,7,55,49,8
COMBOBOX IDC_MAXSIZEUNIT,61,54,39,30,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
LTEXT "Icon processes:",IDC_STATIC,7,72,52,8
EDITTEXT IDC_ICONPROCESSES,61,70,40,12,ES_AUTOHSCROLL | ES_NUMBER
PUSHBUTTON "Font...",IDC_FONT,179,70,49,14
PUSHBUTTON "Monospace...",IDC_FONTMONOSPACE,230,70,70,14
PUSHBUTTON "Make default...",IDC_REPLACETASKMANAGER,179,86,72,14
LTEXT "Graph history length:",IDC_STATIC,106,56,69,8
EDITTEXT IDC_SAMPLECOUNT,180,55,48,12,ES_AUTOHSCROLL | ES_NUMBER
CONTROL "Automatic",IDC_SAMPLECOUNTAUTOMATIC,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,233,56,48,10
CONTROL "",IDC_SETTINGS,"SysListView32",LVS_LIST | LVS_SINGLESEL | LVS_ALIGNLEFT | WS_TABSTOP,7,103,301,110
LTEXT "Application font:",IDC_STATIC,121,72,54,8
RTEXT "System Informer is the default Task Manager:",IDC_DEFSTATE,7,88,166,8
LTEXT "Symbol path:",IDC_STATIC,7,39,43,8
EDITTEXT IDC_DBGHELPSEARCHPATH,61,38,247,12,ES_AUTOHSCROLL
END
IDD_OPTHIGHLIGHTING DIALOGEX 0, 0, 250, 174
STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU
CAPTION "Highlighting"
FONT 8, "MS Shell Dlg", 400, 0, 0x1
BEGIN
LTEXT "Highlighting duration:",IDC_STATIC,7,8,70,8
EDITTEXT IDC_HIGHLIGHTINGDURATION,79,7,40,12,ES_AUTOHSCROLL | ES_NUMBER
LTEXT "New objects:",IDC_STATIC,7,25,44,8
CONTROL "New objects",IDC_NEWOBJECTS,"PhColorBox",WS_CLIPSIBLINGS | WS_TABSTOP,57,23,33,13
LTEXT "Removed objects:",IDC_STATIC,127,25,60,8
CONTROL "Removed objects",IDC_REMOVEDOBJECTS,"PhColorBox",WS_CLIPSIBLINGS | WS_TABSTOP,193,23,33,13
CONTROL "",IDC_LIST,"SysListView32",LVS_REPORT | LVS_SINGLESEL | LVS_SHOWSELALWAYS | LVS_ALIGNLEFT | LVS_NOCOLUMNHEADER | WS_BORDER | WS_TABSTOP,7,41,236,107
LTEXT "Double-click an item to change it.",IDC_INFO,7,156,106,8
PUSHBUTTON "Enable all",IDC_ENABLEALL,140,153,50,14
PUSHBUTTON "Disable all",IDC_DISABLEALL,193,153,50,14
END
IDD_CHOOSECOLUMNS DIALOGEX 0, 0, 381, 226
STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU
CAPTION "Choose Columns"
FONT 8, "MS Shell Dlg", 400, 0, 0x1
BEGIN
LTEXT "Inactive columns:",IDC_STATIC,7,21,57,8
LISTBOX IDC_INACTIVE,7,49,129,153,LBS_SORT | LBS_OWNERDRAWFIXED | LBS_HASSTRINGS | LBS_NOINTEGRALHEIGHT | WS_VSCROLL | WS_TABSTOP
PUSHBUTTON "Show >",IDC_SHOW,139,105,50,14
PUSHBUTTON "< Hide",IDC_HIDE,139,121,50,14
LISTBOX IDC_ACTIVE,192,49,129,153,LBS_OWNERDRAWFIXED | LBS_HASSTRINGS | LBS_NOINTEGRALHEIGHT | WS_VSCROLL | WS_TABSTOP
PUSHBUTTON "Move up",IDC_MOVEUP,326,49,50,14
PUSHBUTTON "Move down",IDC_MOVEDOWN,326,66,50,14
PUSHBUTTON "OK",IDOK,272,206,50,14
DEFPUSHBUTTON "Cancel",IDCANCEL,326,206,50,14
LTEXT "Active columns:",IDC_STATIC,192,21,51,8
LTEXT "Select the columns that will appear in the list.",IDC_MESSAGE,7,7,369,10
EDITTEXT IDC_SEARCH,7,33,129,14,ES_AUTOHSCROLL
EDITTEXT IDC_FILTER,192,33,129,14,ES_AUTOHSCROLL
END
IDD_NETSTACK DIALOGEX 0, 0, 261, 228
STYLE DS_SETFONT | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME
CAPTION "Network Stack"
FONT 8, "MS Shell Dlg", 400, 0, 0x1
BEGIN
CONTROL "",IDC_LIST,"SysListView32",LVS_REPORT | LVS_SHOWSELALWAYS | LVS_ALIGNLEFT | LVS_NOSORTHEADER | WS_BORDER | WS_TABSTOP,7,7,247,196
PUSHBUTTON "Close",IDOK,204,207,50,14
END
IDD_CREATESERVICE DIALOGEX 0, 0, 287, 133
STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU
CAPTION "Create Service"
FONT 8, "MS Shell Dlg", 400, 0, 0x1
BEGIN
LTEXT "Name:",IDC_STATIC,7,8,22,8
EDITTEXT IDC_NAME,61,7,219,12,ES_AUTOHSCROLL
LTEXT "Display name:",IDC_STATIC,7,24,46,8
EDITTEXT IDC_DISPLAYNAME,61,23,219,12,ES_AUTOHSCROLL
LTEXT "Type:",IDC_STATIC,7,41,20,8
COMBOBOX IDC_TYPE,61,39,97,30,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
LTEXT "Start type:",IDC_STATIC,7,58,38,8
COMBOBOX IDC_STARTTYPE,61,56,97,30,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
LTEXT "Error control:",IDC_STATIC,7,75,45,8
COMBOBOX IDC_ERRORCONTROL,61,73,97,30,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
LTEXT "Binary path:",IDC_STATIC,7,91,40,8
EDITTEXT IDC_BINARYPATH,61,90,165,12,ES_AUTOHSCROLL
PUSHBUTTON "Browse...",IDC_BROWSE,230,89,50,14
DEFPUSHBUTTON "OK",IDOK,176,112,50,14
PUSHBUTTON "Cancel",IDCANCEL,230,112,50,14
END
IDD_PROCPERFORMANCE DIALOGEX 0, 0, 260, 260
STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU
CAPTION "Performance"
FONT 8, "MS Shell Dlg", 400, 0, 0x1
BEGIN
GROUPBOX "CPU",IDC_GROUPCPU,7,7,246,74,0,WS_EX_TRANSPARENT
GROUPBOX "Private bytes",IDC_GROUPPRIVATEBYTES,7,86,246,77,0,WS_EX_TRANSPARENT
GROUPBOX "I/O",IDC_GROUPIO,7,164,246,89,0,WS_EX_TRANSPARENT
CONTROL "",IDC_CPU,"PhGraph",WS_CLIPSIBLINGS,105,42,50,14
CONTROL "",IDC_PRIVATEBYTES,"PhGraph",WS_CLIPSIBLINGS,105,122,50,14
CONTROL "",IDC_IO,"PhGraph",WS_CLIPSIBLINGS,105,204,50,14
END
IDD_PROCSTATISTICS DIALOGEX 0, 0, 260, 260
STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU
CAPTION "Statistics"
FONT 8, "MS Shell Dlg", 400, 0, 0x1
BEGIN
CONTROL "",IDC_STATISTICS_LIST,"SysListView32",LVS_REPORT | LVS_ALIGNLEFT | WS_BORDER | WS_TABSTOP,2,3,255,255
END
IDD_OPTADVANCED DIALOGEX 0, 0, 317, 225
STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU
CAPTION "Advanced"
FONT 8, "MS Shell Dlg", 400, 0, 0x1
BEGIN
EDITTEXT IDC_SEARCH,171,3,144,14,ES_AUTOHSCROLL
PUSHBUTTON "Options",IDC_FILTEROPTIONS,2,3,50,14
CONTROL "",IDC_SETTINGS,"PhTreeNew",WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_TABSTOP | 0xa,2,19,313,204,WS_EX_CLIENTEDGE
PUSHBUTTON "Refresh",IDC_REFRESH,53,3,50,14
END
IDD_GDIHANDLES DIALOGEX 0, 0, 351, 307
STYLE DS_SETFONT | DS_FIXEDSYS | WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_POPUP | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME
CAPTION "GDI Handles"
FONT 8, "MS Shell Dlg", 400, 0, 0x1
BEGIN
CONTROL "",IDC_LIST,"SysListView32",LVS_REPORT | LVS_SHOWSELALWAYS | LVS_ALIGNLEFT | WS_BORDER | WS_TABSTOP,7,7,337,275
PUSHBUTTON "Refresh",IDC_REFRESH,240,286,50,14
DEFPUSHBUTTON "Close",IDOK,294,286,50,14
END
IDD_LOG DIALOGEX 0, 0, 313, 303
STYLE DS_SETFONT | DS_FIXEDSYS | WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_POPUP | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME
EXSTYLE WS_EX_APPWINDOW
CAPTION "Log"
FONT 8, "MS Shell Dlg", 400, 0, 0x1
BEGIN
CONTROL "",IDC_LIST,"SysListView32",LVS_REPORT | LVS_SHOWSELALWAYS | LVS_ALIGNLEFT | LVS_OWNERDATA | LVS_NOSORTHEADER | WS_BORDER | WS_TABSTOP,7,7,299,272
PUSHBUTTON "Clear",IDC_CLEAR,7,282,50,14
CONTROL "Auto-scroll",IDC_AUTOSCROLL,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,61,284,50,10
PUSHBUTTON "Save...",IDC_SAVE,148,282,50,14
PUSHBUTTON "Copy",IDC_COPY,202,282,50,14
DEFPUSHBUTTON "Close",IDOK,256,282,50,14
END
IDD_MEMEDIT DIALOGEX 0, 0, 441, 269
STYLE DS_SETFONT | DS_FIXEDSYS | WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_POPUP | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME
EXSTYLE WS_EX_APPWINDOW
CAPTION "Memory"
FONT 8, "MS Shell Dlg", 400, 0, 0x1
BEGIN
CONTROL "",IDC_MEMORY,"PhHexEdit",WS_CLIPSIBLINGS | WS_VSCROLL | WS_TABSTOP,7,7,427,239
PUSHBUTTON "Re-read",IDC_REREAD,7,248,50,14
PUSHBUTTON "Write",IDC_WRITE,60,248,50,14
PUSHBUTTON "Go to...",IDC_GOTO,112,248,50,14
PUSHBUTTON "Save...",IDC_SAVE,331,248,50,14
PUSHBUTTON "Close",IDOK,384,248,50,14
COMBOBOX IDC_BYTESPERROW,164,249,86,30,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
END
IDD_MEMPROTECT DIALOGEX 0, 0, 270, 171
STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU
CAPTION "Memory Protection"
FONT 8, "MS Shell Dlg", 400, 0, 0x1
BEGIN
EDITTEXT IDC_INTRO,7,7,256,122,ES_MULTILINE | ES_AUTOHSCROLL | ES_READONLY | NOT WS_BORDER
LTEXT "New value:",IDC_STATIC,120,136,37,8
EDITTEXT IDC_VALUE,161,135,102,12,ES_AUTOHSCROLL
DEFPUSHBUTTON "OK",IDOK,161,150,50,14
PUSHBUTTON "Cancel",IDCANCEL,213,150,50,14
END
IDD_MEMSTRINGS DIALOGEX 0, 0, 443, 254
STYLE DS_SETFONT | DS_FIXEDSYS | WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_POPUP | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME
CAPTION "Strings"
FONT 8, "MS Shell Dlg", 400, 0, 0x1
BEGIN
PUSHBUTTON "Options",IDC_SETTINGS,2,3,50,14
PUSHBUTTON "Filter",IDC_FILTER,53,3,50,14
EDITTEXT IDC_SEARCH,105,3,336,14,ES_AUTOHSCROLL
CONTROL "",IDC_TREELIST,"PhTreeNew",WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_TABSTOP | 0x2,2,19,439,219,WS_EX_CLIENTEDGE
LTEXT "",IDC_MESSAGE,8,242,427,10
END
IDD_MEMSTRINGSMINLEN DIALOGEX 0, 0, 123, 46
STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU
CAPTION "String Search"
FONT 8, "MS Shell Dlg", 400, 0, 0x1
BEGIN
LTEXT "Minimum length:",IDC_STATIC,7,8,54,8
EDITTEXT IDC_MINIMUMLENGTH,67,7,51,12,ES_AUTOHSCROLL
DEFPUSHBUTTON "OK",IDOK,13,25,50,14
PUSHBUTTON "Cancel",IDCANCEL,67,25,50,14
END
IDD_MEMRESULTS DIALOGEX 0, 0, 313, 266
STYLE DS_SETFONT | DS_FIXEDSYS | WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_POPUP | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME
CAPTION "Results"
FONT 8, "MS Shell Dlg", 400, 0, 0x1
BEGIN
LTEXT "Results.",IDC_INTRO,7,9,299,8
CONTROL "",IDC_LIST,"SysListView32",LVS_REPORT | LVS_SHOWSELALWAYS | LVS_ALIGNLEFT | LVS_OWNERDATA | LVS_NOSORTHEADER | WS_BORDER | WS_TABSTOP,7,21,299,221
PUSHBUTTON "Filter",IDC_FILTER,7,245,50,14
PUSHBUTTON "Save...",IDC_SAVE,149,245,50,14
PUSHBUTTON "Copy",IDC_COPY,203,245,50,14
DEFPUSHBUTTON "Close",IDOK,256,245,50,14
END
IDD_MEMSTRING DIALOGEX 0, 0, 241, 86
STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU
CAPTION "String Search"
FONT 8, "MS Shell Dlg", 400, 0, 0x1
BEGIN
LTEXT "Minimum length:",IDC_STATIC,7,8,54,8
EDITTEXT IDC_MINIMUMLENGTH,67,7,51,12,ES_AUTOHSCROLL
CONTROL "Detect Unicode",IDC_DETECTUNICODE,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,22,65,10
LTEXT "Search in the following types of memory regions:",IDC_STATIC,7,36,157,8
CONTROL "Private",IDC_PRIVATE,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,49,39,10
CONTROL "Image",IDC_IMAGE,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,55,49,36,10
CONTROL "Mapped",IDC_MAPPED,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,101,49,41,10
DEFPUSHBUTTON "OK",IDOK,131,65,50,14
PUSHBUTTON "Cancel",IDCANCEL,184,65,50,14
CONTROL "Extended Unicode",IDC_EXTENDEDUNICODE,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,80,22,74,10
END
IDD_OPTGRAPHS DIALOGEX 0, 0, 250, 156
STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU
CAPTION "Graphs"
FONT 8, "MS Shell Dlg", 400, 0, 0x1
BEGIN
CONTROL "Show text",IDC_SHOWTEXT,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,7,49,10
CONTROL "",IDC_LIST,"SysListView32",LVS_REPORT | LVS_SINGLESEL | LVS_SHOWSELALWAYS | LVS_ALIGNLEFT | LVS_NOCOLUMNHEADER | WS_DISABLED | WS_TABSTOP,7,44,236,105
CONTROL "Use old colors (black background)",IDC_USEOLDCOLORS,
"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,19,123,10
CONTROL "Show commit charge instead of physical memory in summary view",IDC_SHOWCOMMITINSUMMARY,
"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,31,227,10
END
IDD_PLUGINMANAGER DIALOGEX 0, 0, 501, 272
STYLE DS_SETFONT | DS_FIXEDSYS | WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_POPUP | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME
CAPTION "Plugins"
FONT 8, "MS Shell Dlg", 400, 0, 0x1
BEGIN
LTEXT "Changes may require a restart to take effect.",IDC_INSTRUCTION,2,262,148,8
CONTROL "",IDC_PLUGINTREE,"PhTreeNew",WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_TABSTOP | 0xa,2,2,497,253,WS_EX_CLIENTEDGE
PUSHBUTTON "Disabled plugins (0)",IDC_DISABLED,427,256,72,14
END
IDD_HANDLESTATS DIALOGEX 0, 0, 219, 175
STYLE DS_SETFONT | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME
CAPTION "Handle Statistics"
FONT 8, "MS Shell Dlg", 400, 0, 0x1
BEGIN
CONTROL "",IDC_LIST,"SysListView32",LVS_REPORT | LVS_SHOWSELALWAYS | LVS_ALIGNLEFT | WS_BORDER | WS_TABSTOP,7,7,205,143
DEFPUSHBUTTON "Close",IDOK,162,154,50,14
END
IDD_PROCRECORD DIALOGEX 0, 0, 260, 202
STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU
CAPTION "Process Record"
FONT 8, "MS Shell Dlg", 400, 0, 0x1
BEGIN
LTEXT "Name:",IDC_STATIC,7,9,22,8
EDITTEXT IDC_PROCESSNAME,39,9,214,12,ES_AUTOHSCROLL | ES_READONLY | NOT WS_BORDER
LTEXT "Parent:",IDC_STATIC,7,21,25,8
EDITTEXT IDC_PARENT,39,21,214,12,ES_AUTOHSCROLL | ES_READONLY | NOT WS_BORDER
GROUPBOX "File",IDC_FILE,7,38,246,79
ICON "",IDC_FILEICON,14,49,20,20
EDITTEXT IDC_NAME,44,48,182,12,ES_AUTOHSCROLL | ES_READONLY | NOT WS_BORDER
EDITTEXT IDC_COMPANYNAME,44,60,183,12,ES_AUTOHSCROLL | ES_READONLY | NOT WS_BORDER
LTEXT "Version:",IDC_STATIC,15,72,27,8
EDITTEXT IDC_VERSION,44,72,183,12,ES_AUTOHSCROLL | ES_READONLY | NOT WS_BORDER
LTEXT "Image file name:",IDC_STATIC,15,86,56,8
EDITTEXT IDC_FILENAME,15,96,211,12,ES_AUTOHSCROLL | ES_READONLY
PUSHBUTTON "Open",IDC_OPENFILENAME,230,95,17,15,BS_ICON
LTEXT "Command line:",IDC_STATIC,7,123,50,8
EDITTEXT IDC_CMDLINE,65,121,188,12,ES_AUTOHSCROLL | ES_READONLY
LTEXT "Started:",IDC_STATIC,7,139,28,8
EDITTEXT IDC_STARTED,65,137,188,12,ES_AUTOHSCROLL | ES_READONLY
LTEXT "Terminated:",IDC_STATIC,7,155,40,8
EDITTEXT IDC_TERMINATED,65,153,188,12,ES_AUTOHSCROLL | ES_READONLY
LTEXT "Session ID:",IDC_STATIC,7,170,37,8
LTEXT "Static",IDC_SESSIONID,50,170,19,8
PUSHBUTTON "Properties",IDC_PROPERTIES,150,181,50,14
DEFPUSHBUTTON "Close",IDOK,203,181,50,14
END
IDD_CHOOSEPROCESS DIALOGEX 0, 0, 317, 239
STYLE DS_SETFONT | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME
CAPTION "Select a Process"
FONT 8, "MS Shell Dlg", 400, 0, 0x1
BEGIN
LTEXT "Select a process from the list below.",IDC_MESSAGE,7,7,303,8,SS_ENDELLIPSIS
CONTROL "",IDC_LIST,"SysListView32",LVS_REPORT | LVS_SINGLESEL | LVS_SHOWSELALWAYS | LVS_SHAREIMAGELISTS | LVS_ALIGNLEFT | WS_BORDER | WS_TABSTOP,7,21,303,193
PUSHBUTTON "Refresh",IDC_REFRESH,7,218,50,14
DEFPUSHBUTTON "OK",IDOK,205,218,50,14
PUSHBUTTON "Cancel",IDCANCEL,260,218,50,14
END
IDD_PROCSERVICES DIALOGEX 0, 0, 260, 261
STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU
CAPTION "Services"
FONT 8, "MS Shell Dlg", 400, 0, 0x1
BEGIN
LTEXT "Services",IDC_SERVICES_LAYOUT,7,7,246,247,NOT WS_VISIBLE | WS_BORDER
END
IDD_SHADOWSESSION DIALOGEX 0, 0, 228, 84
STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU
CAPTION "Remote Control"
FONT 8, "MS Shell Dlg", 400, 0, 0x1
BEGIN
LTEXT "To end the remote control session, press this key and the modifiers selected below:",IDC_STATIC,7,7,214,19
COMBOBOX IDC_VIRTUALKEY,7,27,74,30,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
CONTROL "Shift",IDC_SHIFT,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,47,31,10
CONTROL "Ctrl",IDC_CTRL,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,49,47,27,10
CONTROL "Alt",IDC_ALT,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,89,47,25,10
DEFPUSHBUTTON "OK",IDOK,117,63,50,14
PUSHBUTTON "Cancel",IDCANCEL,171,63,50,14
END
IDD_TOKCAPABILITIES DIALOGEX 0, 0, 270, 228
STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU
CAPTION "Capabilities"
FONT 8, "MS Shell Dlg", 400, 0, 0x1
BEGIN
CONTROL "",IDC_LIST,"PhTreeNew",WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_TABSTOP | 0x32,0,0,270,228
END
IDD_TOKATTRIBUTES DIALOGEX 0, 0, 270, 228
STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU
CAPTION "Attributes"
FONT 8, "MS Shell Dlg", 400, 0, 0x1
BEGIN
CONTROL "",IDC_LIST,"PhTreeNew",WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_TABSTOP | 0x32,0,0,270,228
END
IDD_TOKAPPPOLICY DIALOGEX 0, 0, 270, 228
STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU
CAPTION "Policy"
FONT 8, "MS Shell Dlg", 400, 0, 0x1
BEGIN
CONTROL "",IDC_LIST,"PhTreeNew",WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_TABSTOP | 0xa,0,0,270,228
END
IDD_SYSINFO_CPU DIALOGEX 0, 0, 316, 195
STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | DS_CONTROL | WS_CHILD | WS_CAPTION | WS_SYSMENU
EXSTYLE WS_EX_CONTROLPARENT
CAPTION "CPU"
FONT 8, "MS Shell Dlg", 400, 0, 0x1
BEGIN
RTEXT "CPU name",IDC_CPUNAME,41,4,274,16,SS_WORDELLIPSIS
LTEXT "CPU",IDC_TITLE,0,0,37,21
LTEXT "Panel layout",IDC_LAYOUT,0,105,315,88,NOT WS_VISIBLE
LTEXT "Graph layout",IDC_GRAPH_LAYOUT,0,22,315,82,NOT WS_VISIBLE
END
IDD_SYSINFO_CPUPANEL DIALOGEX 0, 0, 506, 86
STYLE DS_SETFONT | DS_FIXEDSYS | WS_CHILD | WS_SYSMENU
EXSTYLE WS_EX_CONTROLPARENT
FONT 8, "MS Shell Dlg", 400, 0, 0x1
BEGIN
LTEXT "Utilization:",IDC_STATIC,6,3,34,8
LTEXT "Speed:",IDC_STATIC,108,3,24,8
LTEXT "Virtualization:",IDC_STATIC,247,3,60,8
LTEXT "Static",IDC_UTILIZATION,46,0,60,15
LTEXT "Static",IDC_SPEED,137,0,100,15
LTEXT "Static",IDC_VIRTUALIZATION,296,0,500,15
GROUPBOX "System",IDC_STATIC,0,17,97,55
LTEXT "Processes",IDC_STATIC,7,28,33,8
LTEXT "Threads",IDC_STATIC,7,38,27,8
LTEXT "Handles",IDC_STATIC,7,48,26,8
LTEXT "Uptime",IDC_STATIC,7,58,23,8
RTEXT "Static",IDC_ZPROCESSES_V,45,28,46,8,SS_ENDELLIPSIS
RTEXT "Static",IDC_ZTHREADS_V,45,38,46,8,SS_ENDELLIPSIS
RTEXT "Static",IDC_ZHANDLES_V,45,48,46,8,SS_ENDELLIPSIS
RTEXT "Static",IDC_ZUPTIME_V,46,58,45,8,SS_ENDELLIPSIS
CONTROL "&Show one graph per CPU",IDC_ONEGRAPHPERCPU,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,0,76,96,10
GROUPBOX "CPU",IDC_STATIC,101,17,136,55
LTEXT "Context switches delta",IDC_STATIC,109,28,76,8
LTEXT "Interrupts delta",IDC_STATIC,109,38,52,8
LTEXT "DPCs delta",IDC_STATIC,109,48,36,8
LTEXT "System calls delta",IDC_STATIC,109,58,60,8
RTEXT "Static",IDC_ZCONTEXTSWITCHESDELTA_V,191,28,40,8,SS_ENDELLIPSIS
RTEXT "Static",IDC_ZINTERRUPTSDELTA_V,185,39,46,8,SS_ENDELLIPSIS
RTEXT "Static",IDC_ZDPCSDELTA_V,181,48,50,8,SS_ENDELLIPSIS
RTEXT "Static",IDC_ZSYSTEMCALLSDELTA_V,179,58,52,8,SS_ENDELLIPSIS
GROUPBOX "Topology",IDC_STATIC,241,17,109,55
LTEXT "Cores",IDC_STATIC,249,28,76,8
LTEXT "Sockets",IDC_STATIC,249,38,26,8
LTEXT "Logical processors",IDC_STATIC,249,48,58,8
LTEXT "Latency",IDC_STATIC,249,58,60,8
RTEXT "Static",IDC_ZCORES,305,28,40,8,SS_ENDELLIPSIS
RTEXT "Static",IDC_ZSOCKETS,299,38,46,8,SS_ENDELLIPSIS
RTEXT "Static",IDC_ZLOGICAL,314,48,31,8,SS_ENDELLIPSIS
RTEXT "Static",IDC_ZLATENCY,293,58,52,8,SS_ENDELLIPSIS
GROUPBOX "Hardware",IDC_STATIC,354,17,97,55
LTEXT "L1 cache",IDC_STATIC,362,28,52,8
RTEXT "Static",IDC_ZL1CACHE_V,394,28,52,8,SS_ENDELLIPSIS
LTEXT "L2 cache",IDC_STATIC,362,38,53,8
RTEXT "Static",IDC_ZL2CACHE_V,394,38,52,8,SS_ENDELLIPSIS
LTEXT "L3 cache",IDC_STATIC,362,48,53,8
RTEXT "Static",IDC_ZL3CACHE_V,394,48,52,8,SS_ENDELLIPSIS
END
IDD_SYSINFO_MEMPANEL DIALOGEX 0, 0, 488, 171
STYLE DS_SETFONT | DS_FIXEDSYS | WS_CHILD | WS_SYSMENU
EXSTYLE WS_EX_CONTROLPARENT
FONT 8, "MS Shell Dlg", 400, 0, 0x1
BEGIN
GROUPBOX "Commit charge",IDC_STATIC,0,0,108,44
LTEXT "Current",IDC_STATIC,7,11,26,8
LTEXT "Peak",IDC_STATIC,7,21,16,8
LTEXT "Limit",IDC_STATIC,7,31,15,8
RTEXT "Static",IDC_ZCOMMITCURRENT_V,45,11,56,8,SS_ENDELLIPSIS
RTEXT "Static",IDC_ZCOMMITPEAK_V,41,21,60,8,SS_ENDELLIPSIS
RTEXT "Static",IDC_ZCOMMITLIMIT_V,40,31,61,8,SS_ENDELLIPSIS
GROUPBOX "Physical memory",IDC_STATIC,112,0,118,73
LTEXT "Current",IDC_STATIC,120,11,26,8
LTEXT "Cache WS",IDC_STATIC,120,41,34,8
RTEXT "Static",IDC_ZPHYSICALCURRENT_V,151,11,71,8,SS_ENDELLIPSIS
RTEXT "Static",IDC_ZPHYSICALTOTAL_V,145,21,77,8,SS_ENDELLIPSIS
RTEXT "Static",IDC_ZPHYSICALCACHEWS_V,159,41,63,8,SS_ENDELLIPSIS
LTEXT "Total",IDC_STATIC,120,21,17,8
LTEXT "Kernel WS",IDC_STATIC,120,51,34,8
RTEXT "Static",IDC_ZPHYSICALKERNELWS_V,159,51,63,8,SS_ENDELLIPSIS
LTEXT "Driver WS",IDC_STATIC,120,61,33,8
RTEXT "Static",IDC_ZPHYSICALDRIVERWS_V,159,61,63,8,SS_ENDELLIPSIS
GROUPBOX "Paged pool",IDC_STATIC,0,47,108,64
LTEXT "Working set",IDC_STATIC,7,58,40,8
LTEXT "Limit",IDC_STATIC,7,78,15,8
RTEXT "Static",IDC_ZPAGEDWORKINGSET_V,54,58,47,8,SS_ENDELLIPSIS
RTEXT "Static",IDC_ZPAGEDVIRTUALSIZE_V,52,68,49,8,SS_ENDELLIPSIS
RTEXT "Static",IDC_ZPAGEDLIMIT_V,36,78,65,8,SS_ENDELLIPSIS
LTEXT "Virtual size",IDC_STATIC,7,68,36,8
LTEXT "Allocs delta",IDC_STATIC,7,88,38,8
RTEXT "Static",IDC_ZPAGEDALLOCSDELTA_V,53,88,48,8,SS_ENDELLIPSIS
LTEXT "Frees delta",IDC_STATIC,7,98,38,8
RTEXT "Static",IDC_ZPAGEDFREESDELTA_V,51,98,50,8,SS_ENDELLIPSIS
GROUPBOX "Non-paged pool",IDC_STATIC,0,114,108,55
LTEXT "Usage",IDC_STATIC,7,125,21,8
LTEXT "Allocs delta",IDC_STATIC,7,145,38,8
RTEXT "Static",IDC_ZNONPAGEDUSAGE_V,36,125,65,8,SS_ENDELLIPSIS
RTEXT "Static",IDC_ZNONPAGEDLIMIT_V,29,135,72,8,SS_ENDELLIPSIS
RTEXT "Static",IDC_ZNONPAGEDALLOCSDELTA_V,50,145,51,8,SS_ENDELLIPSIS
LTEXT "Limit",IDC_STATIC,7,135,15,8
LTEXT "Frees delta",IDC_STATIC,7,155,38,8
RTEXT "Static",IDC_ZNONPAGEDFREESDELTA_V,48,155,53,8,SS_ENDELLIPSIS
GROUPBOX "Memory lists",IDC_STATIC,234,0,123,169
LTEXT "Zeroed",IDC_STATIC,242,11,24,8
LTEXT "Modified",IDC_STATIC,242,31,28,8
RTEXT "Static",IDC_ZLISTZEROED_V,304,11,47,8,SS_ENDELLIPSIS
RTEXT "Static",IDC_ZLISTFREE_V,302,21,49,8,SS_ENDELLIPSIS
RTEXT "Static",IDC_ZLISTMODIFIED_V,298,31,53,8,SS_ENDELLIPSIS
LTEXT "Free",IDC_STATIC,242,21,16,8
LTEXT "Modified no-write",IDC_STATIC,242,41,60,8
RTEXT "Static",IDC_ZLISTMODIFIEDNOWRITE_V,303,41,48,8,SS_ENDELLIPSIS
LTEXT "Standby",IDC_STATIC,242,61,28,8
LTEXT "Priority 0",IDC_STATIC,251,71,30,8
LTEXT "Priority 1",IDC_STATIC,251,80,30,8
LTEXT "Priority 2",IDC_STATIC,251,90,30,8
LTEXT "Priority 3",IDC_STATIC,251,100,30,8
LTEXT "Priority 4",IDC_STATIC,251,110,30,8
LTEXT "Priority 5",IDC_STATIC,251,120,30,8
LTEXT "Priority 6",IDC_STATIC,251,130,30,8
LTEXT "Priority 7",IDC_STATIC,251,140,30,8
RTEXT "Static",IDC_ZLISTSTANDBY_V,303,61,48,8,SS_ENDELLIPSIS
RTEXT "Static",IDC_ZLISTSTANDBY0_V,303,71,48,8,SS_ENDELLIPSIS
RTEXT "Static",IDC_ZLISTSTANDBY1_V,303,80,48,8,SS_ENDELLIPSIS
RTEXT "Static",IDC_ZLISTSTANDBY2_V,303,90,48,8,SS_ENDELLIPSIS
RTEXT "Static",IDC_ZLISTSTANDBY3_V,303,100,48,8,SS_ENDELLIPSIS
RTEXT "Static",IDC_ZLISTSTANDBY4_V,303,110,48,8,SS_ENDELLIPSIS
RTEXT "Static",IDC_ZLISTSTANDBY5_V,303,120,48,8,SS_ENDELLIPSIS
RTEXT "Static",IDC_ZLISTSTANDBY6_V,303,130,48,8,SS_ENDELLIPSIS
RTEXT "Static",IDC_ZLISTSTANDBY7_V,303,140,48,8,SS_ENDELLIPSIS
GROUPBOX "Paging",IDC_STATIC,112,75,118,55
LTEXT "Page faults delta",IDC_STATIC,120,86,57,8
LTEXT "Pagefile writes delta",IDC_STATIC,120,106,68,8
RTEXT "Static",IDC_ZPAGINGPAGEFAULTSDELTA_V,183,86,39,8,SS_ENDELLIPSIS
RTEXT "Static",IDC_ZPAGINGPAGEREADSDELTA_V,179,96,43,8,SS_ENDELLIPSIS
RTEXT "Static",IDC_ZPAGINGPAGEFILEWRITESDELTA_V,189,106,33,8,SS_ENDELLIPSIS
LTEXT "Page reads delta",IDC_STATIC,120,96,58,8
LTEXT "Mapped writes delta",IDC_STATIC,120,116,68,8
RTEXT "Static",IDC_ZPAGINGMAPPEDWRITESDELTA_V,191,116,31,8,SS_ENDELLIPSIS
LTEXT "Modified pagefile",IDC_STATIC,242,51,60,8
RTEXT "Static",IDC_ZLISTMODIFIEDPAGEFILE_V,303,51,48,8,SS_ENDELLIPSIS
PUSHBUTTON "&More",IDC_MORE,242,152,50,14
RTEXT "Static",IDC_ZPHYSICALRESERVED_V,157,31,65,8,SS_ENDELLIPSIS
LTEXT "Reserved",IDC_STATIC,120,31,32,8
GROUPBOX "Mapped IO",IDC_STATIC,112,132,118,36
LTEXT "Mapped reads",IDC_STATIC,119,145,68,8
RTEXT "Static",IDC_ZMAPPEDREADIO,180,145,42,8,SS_ENDELLIPSIS
LTEXT "Mapped writes",IDC_STATIC,119,156,68,8
RTEXT "Static",IDC_ZMAPPEDWRITEIO,180,156,42,8,SS_ENDELLIPSIS
PUSHBUTTON "&Empty",IDC_EMPTY,299,152,50,14
GROUPBOX "Hardware",IDC_STATIC,361,0,114,63
LTEXT "Slots used",IDC_STATIC,369,11,40,8
RTEXT "Static",IDC_ZMEMSLOTS_V,415,11,54,8,SS_ENDELLIPSIS
LTEXT "Form factor",IDC_STATIC,369,21,48,8
RTEXT "Static",IDC_ZMEMFORMFACTOR_V,419,21,50,8,SS_ENDELLIPSIS
LTEXT "Technology",IDC_STATIC,369,31,48,8
RTEXT "Static",IDC_ZMEMTECHNOLOGY_V,419,31,50,8,SS_ENDELLIPSIS
LTEXT "Memory type",IDC_STATIC,369,41,48,8
RTEXT "Static",IDC_ZMEMTYPE_V,419,41,50,8,SS_ENDELLIPSIS
LTEXT "Speed",IDC_STATIC,369,51,26,8
RTEXT "Static",IDC_ZMEMSPEED_V,397,51,72,8,SS_ENDELLIPSIS
END
IDD_SYSINFO_MEM DIALOGEX 0, 0, 316, 250
STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | DS_CONTROL | WS_CHILD | WS_CAPTION | WS_SYSMENU
EXSTYLE WS_EX_CONTROLPARENT
CAPTION "Memory"
FONT 8, "MS Shell Dlg", 400, 0, 0x1
BEGIN
LTEXT "Memory",IDC_TITLE,0,0,110,21
LTEXT "Panel layout",IDC_LAYOUT,0,74,315,175,NOT WS_VISIBLE
LTEXT "Graph layout",IDC_GRAPH_LAYOUT,0,22,315,51,NOT WS_VISIBLE
RTEXT "Total physical",IDC_TOTALPHYSICAL,127,4,188,16,SS_WORDELLIPSIS
LTEXT "Commit charge:",IDC_COMMIT_L,111,1,52,8
LTEXT "Physical memory:",IDC_PHYSICAL_L,111,7,56,8
END
IDD_SYSINFO_IO DIALOGEX 0, 0, 316, 187
STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | DS_CONTROL | WS_CHILD | WS_CAPTION | WS_SYSMENU
CAPTION "I/O"
FONT 8, "MS Shell Dlg", 400, 0, 0x1
BEGIN
LTEXT "I/O",IDC_TITLE,0,0,110,21
LTEXT "Panel layout",IDC_LAYOUT,0,109,315,78,NOT WS_VISIBLE
LTEXT "Graph layout",IDC_GRAPH_LAYOUT,0,22,315,84,NOT WS_VISIBLE
LTEXT "Read bytes:",IDC_IOREAD_L,111,1,52,8
LTEXT "Write bytes:",IDC_IOWRITE_L,111,7,56,8
LTEXT "Other bytes:",IDC_IOOTHER_L,111,14,60,8
END
IDD_SYSINFO_IOPANEL DIALOGEX 0, 0, 276, 75
STYLE DS_SETFONT | DS_FIXEDSYS | WS_CHILD | WS_SYSMENU
FONT 8, "MS Shell Dlg", 400, 0, 0x1
BEGIN
GROUPBOX "I/O deltas",IDC_STATIC,0,0,133,74
LTEXT "Reads delta",IDC_STATIC,7,11,40,8
LTEXT "Read bytes delta",IDC_STATIC,7,21,56,8
LTEXT "Writes delta",IDC_STATIC,7,31,40,8
LTEXT "Write bytes delta",IDC_STATIC,7,41,57,8
RTEXT "Static",IDC_ZREADSDELTA_V,59,11,67,8,SS_ENDELLIPSIS
RTEXT "Static",IDC_ZREADBYTESDELTA_V,75,21,51,8,SS_ENDELLIPSIS
RTEXT "Static",IDC_ZWRITESDELTA_V,57,31,69,8,SS_ENDELLIPSIS
RTEXT "Static",IDC_ZWRITEBYTESDELTA_V,73,41,53,8,SS_ENDELLIPSIS
LTEXT "Other delta",IDC_STATIC,7,51,38,8
LTEXT "Other bytes delta",IDC_STATIC,7,61,58,8
RTEXT "Static",IDC_ZOTHERDELTA_V,67,51,59,8,SS_ENDELLIPSIS
RTEXT "Static",IDC_ZOTHERBYTESDELTA_V,81,61,45,8,SS_ENDELLIPSIS
GROUPBOX "I/O totals",IDC_STATIC,137,0,133,74
LTEXT "Reads",IDC_STATIC,145,11,21,8
LTEXT "Read bytes",IDC_STATIC,145,21,38,8
LTEXT "Writes",IDC_STATIC,145,31,22,8
LTEXT "Write bytes",IDC_STATIC,145,41,38,8
RTEXT "Static",IDC_ZREADS_V,197,11,67,8,SS_ENDELLIPSIS
RTEXT "Static",IDC_ZREADBYTES_V,192,21,72,8,SS_ENDELLIPSIS
RTEXT "Static",IDC_ZWRITES_V,195,31,69,8,SS_ENDELLIPSIS
RTEXT "Static",IDC_ZWRITEBYTES_V,190,41,74,8,SS_ENDELLIPSIS
LTEXT "Other",IDC_STATIC,145,51,20,8
LTEXT "Other bytes",IDC_STATIC,145,61,40,8
RTEXT "Static",IDC_ZOTHER_V,191,51,73,8,SS_ENDELLIPSIS
RTEXT "Static",IDC_ZOTHERBYTES_V,192,61,72,8,SS_ENDELLIPSIS
END
IDD_MEMLISTS DIALOGEX 0, 0, 228, 195
STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_MINIMIZEBOX | WS_POPUP | WS_CAPTION | WS_SYSMENU
EXSTYLE WS_EX_TOPMOST
CAPTION "Memory Lists"
FONT 8, "MS Shell Dlg", 400, 0, 0x1
BEGIN
DEFPUSHBUTTON "Close",IDOK,171,174,50,14
LTEXT "Zeroed",IDC_STATIC,7,7,24,8
LTEXT "Free",IDC_STATIC,7,17,16,8
LTEXT "Modified",IDC_STATIC,7,28,28,8
LTEXT "Modified no-write",IDC_STATIC,7,39,56,8
LTEXT "Bad",IDC_STATIC,7,60,13,8
LTEXT "Standby",IDC_STATIC,7,72,28,8
LTEXT "Priority 0",IDC_STATIC,23,83,30,8
LTEXT "Priority 1",IDC_STATIC,23,94,30,8
LTEXT "Priority 2",IDC_STATIC,23,105,30,8
LTEXT "Priority 3",IDC_STATIC,23,116,30,8
LTEXT "Priority 4",IDC_STATIC,23,128,30,8
LTEXT "Priority 5",IDC_STATIC,23,139,30,8
LTEXT "Priority 6",IDC_STATIC,23,150,30,8
LTEXT "Priority 7",IDC_STATIC,23,161,30,8
RTEXT "Static",IDC_ZLISTZEROED_V,73,7,59,8,SS_ENDELLIPSIS
RTEXT "Static",IDC_ZLISTFREE_V,73,17,59,8,SS_ENDELLIPSIS
RTEXT "Static",IDC_ZLISTMODIFIED_V,73,28,59,8,SS_ENDELLIPSIS
RTEXT "Static",IDC_ZLISTMODIFIEDNOWRITE_V,73,39,59,8,SS_ENDELLIPSIS
RTEXT "Static",IDC_ZLISTBAD_V,73,60,59,8,SS_ENDELLIPSIS
RTEXT "Static",IDC_ZLISTSTANDBY_V,73,72,59,8,SS_ENDELLIPSIS
RTEXT "Static",IDC_ZLISTSTANDBY0_V,73,83,59,8,SS_ENDELLIPSIS
RTEXT "Static",IDC_ZLISTSTANDBY1_V,73,94,59,8,SS_ENDELLIPSIS
RTEXT "Static",IDC_ZLISTSTANDBY2_V,73,105,59,8,SS_ENDELLIPSIS
RTEXT "Static",IDC_ZLISTSTANDBY3_V,73,116,59,8,SS_ENDELLIPSIS
RTEXT "Static",IDC_ZLISTSTANDBY4_V,73,128,59,8,SS_ENDELLIPSIS
RTEXT "Static",IDC_ZLISTSTANDBY5_V,73,139,59,8,SS_ENDELLIPSIS
RTEXT "Static",IDC_ZLISTSTANDBY6_V,73,150,59,8,SS_ENDELLIPSIS
RTEXT "Static",IDC_ZLISTSTANDBY7_V,73,161,59,8,SS_ENDELLIPSIS
RTEXT "Static",IDC_ZLISTREPURPOSED_V,162,72,59,8,SS_ENDELLIPSIS
RTEXT "Static",IDC_ZLISTREPURPOSED0_V,162,84,59,8,SS_ENDELLIPSIS
RTEXT "Static",IDC_ZLISTREPURPOSED1_V,162,95,59,8,SS_ENDELLIPSIS
RTEXT "Static",IDC_ZLISTREPURPOSED2_V,162,106,59,8,SS_ENDELLIPSIS
RTEXT "Static",IDC_ZLISTREPURPOSED3_V,162,117,59,8,SS_ENDELLIPSIS
RTEXT "Static",IDC_ZLISTREPURPOSED4_V,162,128,59,8,SS_ENDELLIPSIS
RTEXT "Static",IDC_ZLISTREPURPOSED5_V,162,139,59,8,SS_ENDELLIPSIS
RTEXT "Static",IDC_ZLISTREPURPOSED6_V,162,150,59,8,SS_ENDELLIPSIS
RTEXT "Static",IDC_ZLISTREPURPOSED7_V,162,161,59,8,SS_ENDELLIPSIS
RTEXT "(Repurposed)",IDC_STATIC,162,60,59,8
PUSHBUTTON "&Empty",IDC_EMPTY,117,174,50,14
LTEXT "Modified pagefile",IDC_STATIC,7,49,55,8
RTEXT "Static",IDC_ZLISTMODIFIEDPAGEFILE_V,73,49,59,8,SS_ENDELLIPSIS
END
IDD_CONTAINER DIALOGEX 0, 0, 316, 182
STYLE DS_SETFONT | DS_FIXEDSYS | DS_CONTROL | WS_CHILD | WS_SYSMENU
EXSTYLE WS_EX_CONTROLPARENT
FONT 8, "MS Shell Dlg", 400, 0, 0x1
BEGIN
END
IDD_MINIINFO DIALOGEX 0, 0, 217, 150
STYLE DS_SETFONT | DS_FIXEDSYS | DS_CONTROL | WS_CHILD
FONT 8, "MS Shell Dlg", 400, 0, 0x1
BEGIN
LTEXT "Content layout",IDC_LAYOUT,4,3,210,128,NOT WS_VISIBLE
CONTROL "Section",IDC_SECTION,"Static",SS_LEFTNOWORDWRAP | SS_NOTIFY | SS_ENDELLIPSIS | WS_GROUP,4,134,177,13
PUSHBUTTON "Options",IDC_OPTIONS,183,133,15,14,BS_ICON
CONTROL "Pin",IDC_PINWINDOW,"Button",BS_AUTOCHECKBOX | BS_ICON | BS_PUSHLIKE | WS_TABSTOP,199,133,15,14
END
IDD_MINIINFO_LIST DIALOGEX 0, 0, 217, 141
STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU
CAPTION "CPU"
FONT 8, "MS Shell Dlg", 400, 0, 0x1
BEGIN
CONTROL "",IDC_LIST,"PhTreeNew",WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_TABSTOP | 0x82,0,0,217,140,WS_EX_CLIENTEDGE
END
IDD_MITIGATION DIALOGEX 0, 0, 277, 217
STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU
CAPTION "Mitigation Policies"
FONT 8, "MS Shell Dlg", 400, 0, 0x1
BEGIN
CONTROL "",IDC_LIST,"SysListView32",LVS_REPORT | LVS_SHOWSELALWAYS | LVS_ALIGNLEFT | LVS_NOCOLUMNHEADER | WS_BORDER | WS_TABSTOP,7,7,263,108
LTEXT "Description:",IDC_DESCRIPTIONLABEL,7,118,38,8
EDITTEXT IDC_DESCRIPTION,7,129,263,62,ES_MULTILINE | ES_READONLY | WS_VSCROLL
DEFPUSHBUTTON "OK",IDOK,220,196,50,14
END
IDD_EDITENV DIALOGEX 0, 0, 311, 177
STYLE DS_SETFONT | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME
CAPTION "Edit Environment Variable"
FONT 8, "MS Shell Dlg", 400, 0, 0x1
BEGIN
LTEXT "Name:",IDC_STATIC,7,8,21,8
EDITTEXT IDC_NAME,38,7,266,12,ES_AUTOHSCROLL
LTEXT "Value:",IDC_STATIC,7,25,21,8
EDITTEXT IDC_VALUE,38,24,266,128,ES_MULTILINE | WS_VSCROLL
DEFPUSHBUTTON "OK",IDOK,200,156,50,14
PUSHBUTTON "Cancel",IDCANCEL,254,156,50,14
END
IDD_OPTIONS DIALOGEX 0, 0, 423, 247
STYLE DS_SETFONT | DS_FIXEDSYS | WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_POPUP | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME
CAPTION "Options"
FONT 8, "MS Shell Dlg", 400, 0, 0x1
BEGIN
DEFPUSHBUTTON "Close",IDOK,371,231,50,14
CONTROL "",IDC_SECTIONTREE,"SysTreeView32",TVS_SHOWSELALWAYS | TVS_TRACKSELECT | TVS_FULLROWSELECT | WS_HSCROLL | WS_TABSTOP,2,1,103,244
CONTROL "",IDD_CONTAINER,"#32770",0x44c,107,0,316,229,WS_EX_TOPMOST | WS_EX_TOOLWINDOW | WS_EX_CONTROLPARENT | 0xc0000800L
PUSHBUTTON "Reset",IDC_RESET,108,231,50,14
PUSHBUTTON "Apply",IDC_APPLY,319,231,50,14,NOT WS_VISIBLE
PUSHBUTTON "Cleanup",IDC_CLEANUP,160,231,50,14
END
IDD_PLUGINPROPERTIES DIALOGEX 0, 0, 291, 152
STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU
CAPTION "Plugin Properties"
FONT 8, "MS Shell Dlg", 400, 0, 0x1
BEGIN
GROUPBOX "Plugin",IDC_STATIC,7,7,277,122
LTEXT "Name:",IDC_STATIC,15,18,22,8
EDITTEXT IDC_NAME,71,18,104,12,ES_AUTOHSCROLL | ES_READONLY | NOT WS_BORDER
LTEXT "Internal name:",IDC_STATIC,15,30,49,8
EDITTEXT IDC_INTERNALNAME,71,30,203,12,ES_AUTOHSCROLL | ES_READONLY | NOT WS_BORDER
LTEXT "Author:",IDC_STATIC,15,41,26,8
EDITTEXT IDC_AUTHOR,71,41,203,12,ES_AUTOHSCROLL | ES_READONLY | NOT WS_BORDER
LTEXT "URL:",IDC_STATIC,15,52,16,8
EDITTEXT IDC_URL,71,52,174,12,ES_AUTOHSCROLL | ES_READONLY | NOT WS_BORDER
CONTROL "Open",IDC_OPENURL,"SysLink",WS_TABSTOP,250,52,26,10
LTEXT "File name:",IDC_STATIC,15,63,34,8
EDITTEXT IDC_FILENAME,71,63,203,12,ES_AUTOHSCROLL | ES_READONLY | NOT WS_BORDER
LTEXT "Version:",IDC_STATIC,183,18,27,8
EDITTEXT IDC_VERSION,215,18,59,12,ES_AUTOHSCROLL | ES_READONLY | NOT WS_BORDER
LTEXT "Description:",IDC_STATIC,15,74,39,8
EDITTEXT IDC_DESCRIPTION,14,86,263,37,ES_MULTILINE | ES_READONLY
PUSHBUTTON "Options...",IDC_OPTIONS,7,131,50,14
DEFPUSHBUTTON "Close",IDOK,234,131,50,14
END
IDD_PLUGINSDISABLED DIALOGEX 0, 0, 309, 176
STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU
CAPTION "Disabled Plugins"
FONT 8, "MS Shell Dlg", 400, 0, 0x1
BEGIN
DEFPUSHBUTTON "OK",IDOK,257,160,50,14
CONTROL "",IDC_LIST_DISABLED,"SysListView32",LVS_REPORT | LVS_SINGLESEL | LVS_ALIGNLEFT | LVS_NOCOLUMNHEADER | WS_BORDER | WS_TABSTOP,2,2,305,157
LTEXT "Changes may require a restart to take effect.",IDC_INSTRUCTION,2,163,148,8,NOT WS_VISIBLE
END
IDD_PROCWMIPROVIDERS DIALOGEX 0, 0, 260, 260
STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU
CAPTION "WMI"
FONT 8, "MS Shell Dlg", 400, 0, 0x1
BEGIN
PUSHBUTTON "Refresh",IDC_REFRESH,53,2,50,14
PUSHBUTTON "Options",IDC_OPTIONS,2,2,50,14
EDITTEXT IDC_SEARCH,114,3,144,14,ES_AUTOHSCROLL
CONTROL "",IDC_LIST,"PhTreeNew",WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_TABSTOP | 0xa,2,19,256,239,WS_EX_CLIENTEDGE
END
IDD_COLUMNSETS DIALOGEX 0, 0, 231, 188
STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU
CAPTION "Organize Column Sets"
FONT 8, "MS Shell Dlg", 400, 0, 0x1
BEGIN
PUSHBUTTON "Move up",IDC_MOVEUP,177,42,50,14
PUSHBUTTON "Move down",IDC_MOVEDOWN,177,59,50,14
PUSHBUTTON "OK",IDOK,177,171,50,14
PUSHBUTTON "Rename",IDC_RENAME,177,4,50,14
PUSHBUTTON "Delete",IDC_REMOVE,177,94,50,14
CONTROL "",IDC_COLUMNSETLIST,"SysListView32",LVS_REPORT | LVS_SINGLESEL | LVS_EDITLABELS | LVS_ALIGNLEFT | LVS_NOCOLUMNHEADER | WS_BORDER | WS_TABSTOP,4,4,170,180
END
IDD_RUNFILEDLG DIALOGEX 0, 0, 235, 105
STYLE DS_SETFONT | DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
CAPTION "Run"
FONT 9, "Segoe UI", 0, 0, 0x1
BEGIN
ICON 100,IDC_FILEICON,7,11,20,20
LTEXT "Type the name of a program, folder, document, or Internet resource, and Windows will open it for you.",IDC_STATIC,36,11,182,22
LTEXT "&Open:",IDC_STATIC,7,39,28,10
COMBOBOX IDC_PROGRAMCOMBO,36,37,183,200,CBS_DROPDOWN | CBS_AUTOHSCROLL | CBS_DISABLENOSCROLL | WS_VSCROLL | WS_TABSTOP
DEFPUSHBUTTON "OK",IDOK,62,82,50,14
PUSHBUTTON "Cancel",IDCANCEL,116,82,50,14
PUSHBUTTON "&Browse...",IDC_BROWSE,170,82,50,14
CONTROL "Create this task with TrustedInstaller privileges.",IDC_TRUSTEDINSTALLER,
"Button",BS_AUTOCHECKBOX | WS_TABSTOP,40,62,180,10
CONTROL "Create this task with Administrative privileges.",IDC_TOGGLEELEVATION,
"Button",BS_AUTOCHECKBOX | WS_TABSTOP,40,51,180,10
END
IDD_PROCDUMP DIALOGEX 0, 0, 191, 296
STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU
CAPTION "Process dump"
FONT 8, "MS Shell Dlg", 400, 0, 0x1
BEGIN
CONTROL "Normal",IDC_MINIDUMP_NORMAL,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,7,177,10
CONTROL "With data segments",IDC_MINIDUMP_WITH_DATA_SEGS,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,17,177,10
CONTROL "With full memory",IDC_MINIDUMP_WITH_FULL_MEM,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,27,177,10
CONTROL "With handle data",IDC_MINIDUMP_WITH_HANDLE_DATA,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,37,177,10
CONTROL "Filter memory",IDC_MINIDUMP_FILTER_MEMORY,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,47,177,10
CONTROL "Scan memory",IDC_MINIDUMP_SCAN_MEMORY,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,57,177,10
CONTROL "With unloaded modules",IDC_MINIDUMP_WITH_UNLOADED_MODULES,
"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,67,177,10
CONTROL "With indirectly referenced memory",IDC_MINIDUMP_WITH_INDIRECT_REFERENCED_MEM,
"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,77,177,10
CONTROL "Filter module paths",IDC_MINIDUMP_FILTER_MODULE_PATHS,
"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,87,179,10
CONTROL "With process and thread data",IDC_MINIDUMP_WITH_PROC_THRD_DATA,
"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,97,177,10
CONTROL "With private read-write memory",IDC_MINIDUMP_WITH_PRIVATE_RW_MEM,
"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,107,177,10
CONTROL "Without optional data",IDC_MINIDUMP_WITHOUT_OPTIONAL_DATA,
"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,117,177,10
CONTROL "With full memory info",IDC_MINIDUMP_WITH_FULL_MEM_INFO,
"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,127,177,10
CONTROL "With thread info",IDC_MINIDUMP_WITH_THRD_INFO,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,137,177,10
CONTROL "With code segments",IDC_MINIDUMP_WITH_CODE_SEGS,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,147,177,10
CONTROL "Without auxiliary state",IDC_MINIDUMP_WITHOUT_AUXILIARY_STATE,
"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,157,177,10
CONTROL "With full auxiliary state",IDC_MINIDUMP_WITH_FULL_AUXILIARY_STATE,
"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,167,177,10
CONTROL "With private write-copy memory",IDC_MINIDUMP_WITH_PRIVATE_WRITE_COPY_MEM,
"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,177,177,10
CONTROL "Ignore inaccessible memory",IDC_MINIDUMP_IGNORE_INACCESSIBLE_MEM,
"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,187,177,10
CONTROL "With token information",IDC_MINIDUMP_WITH_TOKEN_INFO,
"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,197,177,10
CONTROL "With module headers",IDC_MINIDUMP_WITH_MODULE_HEADERS,
"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,207,177,10
CONTROL "With filter triage data",IDC_MINIDUMP_FILTER_TRIAGE,
"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,217,177,10
CONTROL "With AVX state context registers",IDC_MINIDUMP_WITH_AVXX_STATE_CONTEXT,
"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,227,177,10
CONTROL "With Intel processor trace data",IDC_MINIDUMP_WITH_IPT_TRACE,
"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,237,177,10
CONTROL "Scan inaccessible partial pages",IDC_MINIDUMP_SCAN_INACCESSIBLE_PARTIAL_PAGES,
"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,247,177,10
CONTROL "Filter write-combined memory",IDC_MINIDUMP_FILTER_WRITE_COMBINE_MEM,
"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,257,177,10
DEFPUSHBUTTON "Save",IDOK,80,275,50,14
PUSHBUTTON "Cancel",IDCANCEL,134,275,50,14
END
IDD_LIVEDUMP DIALOGEX 0, 0, 191, 97
STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU
CAPTION "Live kernel dump"
FONT 8, "MS Shell Dlg", 400, 0, 0x1
BEGIN
CONTROL "Use dump storage stack",IDC_DUMPSTACK,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,7,177,10
CONTROL "Compress memory pages",IDC_COMPRESS,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,17,177,10
CONTROL "Capture user pages",IDC_USERMODE,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,27,177,10
CONTROL "Capture Hypervisor pages",IDC_HYPERVISOR,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,37,177,10
CONTROL "Include nonessential Hypervisor pages",IDC_HYPERVISORNONESSENTIAL,
"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,47,177,10
CONTROL "Only kernel thread stacks",IDC_ONLYKERNELTHREADSTACKS,
"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,57,177,10
DEFPUSHBUTTON "Save",IDOK,80,76,50,14
PUSHBUTTON "Cancel",IDCANCEL,134,76,50,14
END
IDD_HEAPS DIALOGEX 0, 0, 343, 268
STYLE DS_SETFONT | DS_FIXEDSYS | WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_POPUP | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME
CAPTION "Heaps"
FONT 8, "MS Shell Dlg", 400, 0, 0x1
BEGIN
DEFPUSHBUTTON "Close",IDCANCEL,289,251,50,14
CONTROL "",IDC_LIST,"SysListView32",LVS_REPORT | LVS_SHOWSELALWAYS | LVS_ALIGNLEFT | WS_TABSTOP,0,0,343,248
CONTROL "Sizes in bytes",IDC_SIZESINBYTES,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,6,254,59,10
PUSHBUTTON "Refresh",IDC_REFRESH,235,251,50,14
END
IDD_PROCVDMHOST DIALOGEX 0, 0, 260, 260
STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU
CAPTION "NTVDM"
FONT 8, "MS Shell Dlg", 400, 0, 0x1
BEGIN
CONTROL "",IDC_LIST,"SysListView32",LVS_REPORT | LVS_SINGLESEL | LVS_SHOWSELALWAYS | LVS_ALIGNLEFT | WS_BORDER | WS_TABSTOP,2,2,256,256
END
IDD_RUNPACKAGE DIALOGEX 0, 0, 317, 239
STYLE DS_SETFONT | DS_FIXEDSYS | WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_POPUP | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME
CAPTION "Select a package"
FONT 8, "MS Shell Dlg", 400, 0, 0x1
BEGIN
LTEXT "Select the identity for access to the package file system and registry.",IDC_MESSAGE,7,36,303,8,SS_ENDELLIPSIS
PUSHBUTTON "Refresh",IDC_REFRESH,144,218,50,14
DEFPUSHBUTTON "OK",IDOK,205,218,50,14
PUSHBUTTON "Cancel",IDCANCEL,260,218,50,14
COMBOBOX IDC_PROGRAMCOMBO,7,20,250,200,CBS_DROPDOWN | CBS_AUTOHSCROLL | CBS_DISABLENOSCROLL | WS_VSCROLL | WS_TABSTOP
LTEXT "Enter the command to start as the specified package.",IDC_TITLE,7,7,172,8
PUSHBUTTON "Browse",IDC_BROWSE,260,19,50,14
CONTROL "",IDC_LIST,"PhTreeNew",WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_TABSTOP | 0xa,7,49,303,165,WS_EX_CLIENTEDGE
EDITTEXT IDC_SEARCH,7,218,135,14,ES_AUTOHSCROLL
END
IDD_OBJMAPPINGS DIALOGEX 0, 0, 300, 260
STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU
CAPTION "Mappings"
FONT 8, "MS Shell Dlg", 400, 0, 0x1
BEGIN
CONTROL "",IDC_LIST,"SysListView32",LVS_REPORT | LVS_SHOWSELALWAYS | LVS_SHAREIMAGELISTS | LVS_ALIGNLEFT | WS_BORDER | WS_TABSTOP,7,7,286,246
LTEXT "Static",IDC_TEXT,7,7,286,246,NOT WS_VISIBLE
END
IDD_MODIFIEDPAGES DIALOGEX 0, 0, 351, 307
STYLE DS_SETFONT | DS_FIXEDSYS | WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_POPUP | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME
CAPTION "Modified pages"
FONT 8, "MS Shell Dlg", 400, 0, 0x1
BEGIN
CONTROL "",IDC_LIST,"SysListView32",LVS_REPORT | LVS_SHOWSELALWAYS | LVS_ALIGNLEFT | WS_BORDER | WS_TABSTOP,2,2,347,286
PUSHBUTTON "Refresh",IDC_REFRESH,240,291,50,14
DEFPUSHBUTTON "Close",IDOK,294,291,50,14
LTEXT "Status",IDC_STATE,4,294,20,8
END
IDD_CHOOSENEW DIALOGEX 0, 0, 270, 120
STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU
CAPTION "Input Prompt"
FONT 8, "MS Shell Dlg", 400, 0, 0x0
BEGIN
LTEXT "Input Prompt",IDC_TITLE,7,7,262,22
CONTROL "Input Prompt",IDC_TEXT,"SysLink",WS_TABSTOP,7,33,259,23
CONTROL "",IDC_SIZE_,"Static",SS_ETCHEDHORZ,0,90,271,1
DEFPUSHBUTTON "&Ok",IDOK,158,98,50,14
PUSHBUTTON "Cancel",IDCANCEL,212,98,50,14
COMBOBOX IDC_INPUT,7,64,255,30,CBS_DROPDOWN | CBS_SORT | WS_VSCROLL | WS_TABSTOP
END
IDD_OBJAFDSOCKET DIALOGEX 0, 0, 260, 234
STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU
CAPTION "Socket"
FONT 8, "MS Shell Dlg", 400, 0, 0x1
BEGIN
CONTROL "",IDC_LIST,"SysListView32",LVS_REPORT | LVS_ALIGNLEFT | LVS_NOCOLUMNHEADER | WS_TABSTOP,0,2,260,232
END
IDD_HNDLSECURITY DIALOGEX 0, 0, 260, 181
STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU
CAPTION "Permissions"
FONT 8, "MS Shell Dlg", 400, 0, 0x1
BEGIN
CONTROL "",IDC_LIST,"SysListView32",LVS_REPORT | LVS_ALIGNLEFT | WS_BORDER | WS_TABSTOP,0,60,260,94
PUSHBUTTON "Add",IDC_ADD,1,160,47,14
PUSHBUTTON "Advanced",IDC_ADVANCED,212,160,47,14
PUSHBUTTON "Remove",IDC_REMOVE,51,160,47,14
PUSHBUTTON "View",IDC_SHOW,103,160,47,14
CONTROL "",IDC_SETTINGS,"SysListView32",LVS_REPORT | LVS_ALIGNLEFT | LVS_NOCOLUMNHEADER | WS_TABSTOP,0,2,260,46
END
IDD_HNDLSECAUDIT DIALOGEX 0, 0, 260, 181
STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU
CAPTION "Auditing"
FONT 8, "MS Shell Dlg", 400, 0, 0x1
BEGIN
CONTROL "",IDC_LIST,"SysListView32",LVS_REPORT | LVS_ALIGNLEFT | WS_BORDER | WS_TABSTOP,0,60,260,94
PUSHBUTTON "Add",IDC_ADD,1,160,47,14,NOT WS_VISIBLE
PUSHBUTTON "Advanced",IDC_ADVANCED,212,160,47,14
PUSHBUTTON "Remove",IDC_REMOVE,51,160,47,14,NOT WS_VISIBLE
PUSHBUTTON "View",IDC_SHOW,103,160,47,14,NOT WS_VISIBLE
CONTROL "",IDC_SETTINGS,"SysListView32",LVS_REPORT | LVS_ALIGNLEFT | LVS_NOCOLUMNHEADER | WS_TABSTOP,0,2,260,46
END
/////////////////////////////////////////////////////////////////////////////
//
// DESIGNINFO
//
#ifdef APSTUDIO_INVOKED
GUIDELINES DESIGNINFO
BEGIN
IDD_PROCGENERAL, DIALOG
BEGIN
LEFTMARGIN, 7
RIGHTMARGIN, 253
TOPMARGIN, 7
BOTTOMMARGIN, 254
END
IDD_PROCMODULES, DIALOG
BEGIN
LEFTMARGIN, 2
RIGHTMARGIN, 258
TOPMARGIN, 2
BOTTOMMARGIN, 258
END
IDD_PROCTHREADS, DIALOG
BEGIN
LEFTMARGIN, 7
RIGHTMARGIN, 253
TOPMARGIN, 7
BOTTOMMARGIN, 253
END
IDD_PROCHANDLES, DIALOG
BEGIN
LEFTMARGIN, 2
RIGHTMARGIN, 258
TOPMARGIN, 2
BOTTOMMARGIN, 258
END
IDD_PROCENVIRONMENT, DIALOG
BEGIN
LEFTMARGIN, 2
RIGHTMARGIN, 258
TOPMARGIN, 2
BOTTOMMARGIN, 258
END
IDD_THRDSTACK, DIALOG
BEGIN
BOTTOMMARGIN, 226
END
IDD_USRLIST, DIALOG
BEGIN
BOTTOMMARGIN, 226
END
IDD_ABOUT, DIALOG
BEGIN
LEFTMARGIN, 7
RIGHTMARGIN, 263
TOPMARGIN, 7
BOTTOMMARGIN, 188
END
IDD_SRVLIST, DIALOG
BEGIN
END
IDD_SRVGENERAL, DIALOG
BEGIN
LEFTMARGIN, 7
RIGHTMARGIN, 275
TOPMARGIN, 7
BOTTOMMARGIN, 176
END
IDD_HNDLGENERAL, DIALOG
BEGIN
LEFTMARGIN, 7
RIGHTMARGIN, 253
TOPMARGIN, 7
BOTTOMMARGIN, 174
END
IDD_INFORMATION, DIALOG
BEGIN
LEFTMARGIN, 7
RIGHTMARGIN, 310
TOPMARGIN, 7
BOTTOMMARGIN, 177
END
IDD_FINDOBJECTS, DIALOG
BEGIN
LEFTMARGIN, 2
RIGHTMARGIN, 355
TOPMARGIN, 4
BOTTOMMARGIN, 231
END
IDD_THRDSTACKS, DIALOG
BEGIN
LEFTMARGIN, 2
RIGHTMARGIN, 441
TOPMARGIN, 4
BOTTOMMARGIN, 252
END
IDD_USRLIST, DIALOG
BEGIN
LEFTMARGIN, 2
RIGHTMARGIN, 441
TOPMARGIN, 4
BOTTOMMARGIN, 252
END
IDD_OBJTOKEN, DIALOG
BEGIN
LEFTMARGIN, 7
RIGHTMARGIN, 253
TOPMARGIN, 7
BOTTOMMARGIN, 253
END
IDD_ZOMBIEPROCESSES, DIALOG
BEGIN
LEFTMARGIN, 7
RIGHTMARGIN, 330
TOPMARGIN, 7
BOTTOMMARGIN, 214
END
IDD_RUNAS, DIALOG
BEGIN
LEFTMARGIN, 7
RIGHTMARGIN, 286
TOPMARGIN, 7
BOTTOMMARGIN, 120
END
IDD_PROGRESS, DIALOG
BEGIN
LEFTMARGIN, 7
RIGHTMARGIN, 209
TOPMARGIN, 7
BOTTOMMARGIN, 55
END
IDD_PAGEFILES, DIALOG
BEGIN
LEFTMARGIN, 2
RIGHTMARGIN, 320
TOPMARGIN, 2
BOTTOMMARGIN, 160
END
IDD_TOKGENERAL, DIALOG
BEGIN
LEFTMARGIN, 7
RIGHTMARGIN, 263
TOPMARGIN, 7
BOTTOMMARGIN, 221
END
IDD_TOKADVANCED, DIALOG
BEGIN
END
IDD_OBJJOB, DIALOG
BEGIN
LEFTMARGIN, 7
RIGHTMARGIN, 253
TOPMARGIN, 7
BOTTOMMARGIN, 253
END
IDD_OBJEVENT, DIALOG
BEGIN
LEFTMARGIN, 7
RIGHTMARGIN, 179
TOPMARGIN, 7
BOTTOMMARGIN, 69
END
IDD_OBJMUTANT, DIALOG
BEGIN
LEFTMARGIN, 7
RIGHTMARGIN, 179
TOPMARGIN, 7
BOTTOMMARGIN, 69
END
IDD_OBJSEMAPHORE, DIALOG
BEGIN
LEFTMARGIN, 7
RIGHTMARGIN, 179
TOPMARGIN, 7
BOTTOMMARGIN, 69
END
IDD_OBJTIMER, DIALOG
BEGIN
LEFTMARGIN, 7
RIGHTMARGIN, 179
TOPMARGIN, 7
BOTTOMMARGIN, 69
END
IDD_JOBSTATISTICS, DIALOG
BEGIN
LEFTMARGIN, 7
RIGHTMARGIN, 252
TOPMARGIN, 7
BOTTOMMARGIN, 179
END
IDD_OBJEVENTPAIR, DIALOG
BEGIN
LEFTMARGIN, 7
RIGHTMARGIN, 179
TOPMARGIN, 7
BOTTOMMARGIN, 69
END
IDD_AFFINITY, DIALOG
BEGIN
LEFTMARGIN, 7
RIGHTMARGIN, 272
TOPMARGIN, 7
BOTTOMMARGIN, 220
END
IDD_SYSINFO, DIALOG
BEGIN
END
IDD_EDITMESSAGE, DIALOG
BEGIN
LEFTMARGIN, 7
RIGHTMARGIN, 275
TOPMARGIN, 7
BOTTOMMARGIN, 156
END
IDD_SESSION, DIALOG
BEGIN
LEFTMARGIN, 2
RIGHTMARGIN, 315
VERTGUIDE, 164
TOPMARGIN, 2
BOTTOMMARGIN, 265
END
IDD_PROCMEMORY, DIALOG
BEGIN
LEFTMARGIN, 2
RIGHTMARGIN, 258
TOPMARGIN, 2
BOTTOMMARGIN, 258
END
IDD_CHOOSE, DIALOG
BEGIN
LEFTMARGIN, 7
RIGHTMARGIN, 192
TOPMARGIN, 7
BOTTOMMARGIN, 66
END
IDD_OPTGENERAL, DIALOG
BEGIN
LEFTMARGIN, 7
RIGHTMARGIN, 308
TOPMARGIN, 7
BOTTOMMARGIN, 213
END
IDD_OPTHIGHLIGHTING, DIALOG
BEGIN
LEFTMARGIN, 7
RIGHTMARGIN, 243
TOPMARGIN, 7
BOTTOMMARGIN, 167
END
IDD_CHOOSECOLUMNS, DIALOG
BEGIN
LEFTMARGIN, 7
RIGHTMARGIN, 376
TOPMARGIN, 7
BOTTOMMARGIN, 220
END
IDD_NETSTACK, DIALOG
BEGIN
LEFTMARGIN, 7
RIGHTMARGIN, 254
TOPMARGIN, 7
BOTTOMMARGIN, 221
END
IDD_CREATESERVICE, DIALOG
BEGIN
LEFTMARGIN, 7
RIGHTMARGIN, 280
TOPMARGIN, 7
BOTTOMMARGIN, 126
END
IDD_PROCPERFORMANCE, DIALOG
BEGIN
LEFTMARGIN, 7
RIGHTMARGIN, 253
TOPMARGIN, 7
BOTTOMMARGIN, 253
END
IDD_PROCSTATISTICS, DIALOG
BEGIN
LEFTMARGIN, 2
RIGHTMARGIN, 257
TOPMARGIN, 3
BOTTOMMARGIN, 258
END
IDD_OPTADVANCED, DIALOG
BEGIN
LEFTMARGIN, 7
RIGHTMARGIN, 310
TOPMARGIN, 7
BOTTOMMARGIN, 218
END
IDD_GDIHANDLES, DIALOG
BEGIN
LEFTMARGIN, 7
RIGHTMARGIN, 344
TOPMARGIN, 7
BOTTOMMARGIN, 300
END
IDD_LOG, DIALOG
BEGIN
LEFTMARGIN, 7
RIGHTMARGIN, 306
TOPMARGIN, 7
BOTTOMMARGIN, 296
END
IDD_MEMEDIT, DIALOG
BEGIN
LEFTMARGIN, 7
RIGHTMARGIN, 434
TOPMARGIN, 7
BOTTOMMARGIN, 262
END
IDD_MEMPROTECT, DIALOG
BEGIN
LEFTMARGIN, 7
RIGHTMARGIN, 263
TOPMARGIN, 7
BOTTOMMARGIN, 164
END
IDD_MEMSTRINGS, DIALOG
BEGIN
LEFTMARGIN, 3
RIGHTMARGIN, 298
TOPMARGIN, 3
END
IDD_MEMSTRINGSMINLEN, DIALOG
BEGIN
LEFTMARGIN, 3
TOPMARGIN, 3
END
IDD_OPTGRAPHS, DIALOG
BEGIN
LEFTMARGIN, 7
RIGHTMARGIN, 243
TOPMARGIN, 7
BOTTOMMARGIN, 149
END
IDD_PLUGINMANAGER, DIALOG
BEGIN
LEFTMARGIN, 2
RIGHTMARGIN, 499
TOPMARGIN, 2
BOTTOMMARGIN, 270
END
IDD_HANDLESTATS, DIALOG
BEGIN
LEFTMARGIN, 7
RIGHTMARGIN, 212
TOPMARGIN, 7
BOTTOMMARGIN, 168
END
IDD_PROCRECORD, DIALOG
BEGIN
LEFTMARGIN, 7
RIGHTMARGIN, 253
TOPMARGIN, 7
BOTTOMMARGIN, 195
END
IDD_CHOOSEPROCESS, DIALOG
BEGIN
LEFTMARGIN, 7
RIGHTMARGIN, 310
TOPMARGIN, 7
BOTTOMMARGIN, 232
END
IDD_PROCSERVICES, DIALOG
BEGIN
LEFTMARGIN, 7
RIGHTMARGIN, 253
TOPMARGIN, 7
BOTTOMMARGIN, 254
END
IDD_SHADOWSESSION, DIALOG
BEGIN
LEFTMARGIN, 7
RIGHTMARGIN, 221
TOPMARGIN, 7
BOTTOMMARGIN, 77
END
IDD_TOKCAPABILITIES, DIALOG
BEGIN
LEFTMARGIN, 7
RIGHTMARGIN, 263
TOPMARGIN, 7
BOTTOMMARGIN, 221
END
IDD_TOKATTRIBUTES, DIALOG
BEGIN
LEFTMARGIN, 7
RIGHTMARGIN, 263
TOPMARGIN, 7
BOTTOMMARGIN, 221
END
IDD_SYSINFO_CPU, DIALOG
BEGIN
BOTTOMMARGIN, 193
END
IDD_SYSINFO_CPUPANEL, DIALOG
BEGIN
BOTTOMMARGIN, 85
END
IDD_SYSINFO_MEMPANEL, DIALOG
BEGIN
END
IDD_SYSINFO_MEM, DIALOG
BEGIN
BOTTOMMARGIN, 247
END
IDD_SYSINFO_IO, DIALOG
BEGIN
END
IDD_SYSINFO_IOPANEL, DIALOG
BEGIN
BOTTOMMARGIN, 74
END
IDD_MEMLISTS, DIALOG
BEGIN
LEFTMARGIN, 7
RIGHTMARGIN, 221
TOPMARGIN, 7
BOTTOMMARGIN, 188
END
IDD_CONTAINER, DIALOG
BEGIN
LEFTMARGIN, 7
RIGHTMARGIN, 309
TOPMARGIN, 7
BOTTOMMARGIN, 175
END
IDD_MINIINFO, DIALOG
BEGIN
LEFTMARGIN, 4
RIGHTMARGIN, 214
TOPMARGIN, 3
BOTTOMMARGIN, 147
END
IDD_MINIINFO_LIST, DIALOG
BEGIN
END
IDD_MITIGATION, DIALOG
BEGIN
LEFTMARGIN, 7
RIGHTMARGIN, 270
TOPMARGIN, 7
BOTTOMMARGIN, 210
END
IDD_EDITENV, DIALOG
BEGIN
LEFTMARGIN, 7
RIGHTMARGIN, 304
TOPMARGIN, 7
BOTTOMMARGIN, 170
END
IDD_OPTIONS, DIALOG
BEGIN
LEFTMARGIN, 2
BOTTOMMARGIN, 245
END
IDD_PLUGINPROPERTIES, DIALOG
BEGIN
LEFTMARGIN, 7
RIGHTMARGIN, 284
TOPMARGIN, 7
BOTTOMMARGIN, 145
END
IDD_PLUGINSDISABLED, DIALOG
BEGIN
LEFTMARGIN, 2
RIGHTMARGIN, 307
TOPMARGIN, 2
BOTTOMMARGIN, 174
END
IDD_PROCWMIPROVIDERS, DIALOG
BEGIN
LEFTMARGIN, 2
RIGHTMARGIN, 258
TOPMARGIN, 2
BOTTOMMARGIN, 258
END
IDD_COLUMNSETS, DIALOG
BEGIN
LEFTMARGIN, 4
RIGHTMARGIN, 227
TOPMARGIN, 4
BOTTOMMARGIN, 184
END
IDD_RUNFILEDLG, DIALOG
BEGIN
END
IDD_PROCDUMP, DIALOG
BEGIN
BOTTOMMARGIN, 289
END
IDD_LIVEDUMP, DIALOG
BEGIN
LEFTMARGIN, 7
RIGHTMARGIN, 184
TOPMARGIN, 7
BOTTOMMARGIN, 90
END
IDD_HEAPS, DIALOG
BEGIN
END
IDD_PROCVDMHOST, DIALOG
BEGIN
LEFTMARGIN, 2
RIGHTMARGIN, 258
TOPMARGIN, 2
BOTTOMMARGIN, 258
END
IDD_RUNPACKAGE, DIALOG
BEGIN
LEFTMARGIN, 7
RIGHTMARGIN, 310
TOPMARGIN, 7
BOTTOMMARGIN, 232
END
IDD_OBJMAPPINGS, DIALOG
BEGIN
LEFTMARGIN, 7
RIGHTMARGIN, 253
TOPMARGIN, 7
BOTTOMMARGIN, 253
END
IDD_MODIFIEDPAGES, DIALOG
BEGIN
END
IDD_CHOOSENEW, DIALOG
BEGIN
LEFTMARGIN, 7
TOPMARGIN, 7
END
IDD_OBJAFDSOCKET, DIALOG
BEGIN
LEFTMARGIN, 7
RIGHTMARGIN, 253
TOPMARGIN, 7
END
IDD_HNDLSECURITY, DIALOG
BEGIN
END
IDD_HNDLSECAUDIT, DIALOG
BEGIN
END
END
#endif // APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// Accelerator
//
IDR_MAINWND_ACCEL ACCELERATORS
BEGIN
VK_ESCAPE, ID_ESC_EXIT, VIRTKEY, NOINVERT
"F", ID_HACKER_FINDHANDLESORDLLS, VIRTKEY, CONTROL, NOINVERT
"R", ID_HACKER_RUN, VIRTKEY, CONTROL, NOINVERT
"R", ID_HACKER_RUNAS, VIRTKEY, SHIFT, CONTROL, NOINVERT
"S", ID_HACKER_SAVE, VIRTKEY, CONTROL, NOINVERT
"L", ID_HELP_LOG, VIRTKEY, CONTROL, NOINVERT
"M", ID_PROCESS_SEARCHONLINE, VIRTKEY, CONTROL, NOINVERT
VK_TAB, ID_TAB_NEXT, VIRTKEY, CONTROL, NOINVERT
VK_TAB, ID_TAB_PREV, VIRTKEY, SHIFT, CONTROL, NOINVERT
VK_F5, ID_VIEW_REFRESH, VIRTKEY, NOINVERT
"I", ID_VIEW_SYSTEMINFORMATION, VIRTKEY, CONTROL, NOINVERT
VK_PAUSE, ID_VIEW_UPDATEAUTOMATICALLY, VIRTKEY, NOINVERT
VK_F6, ID_VIEW_UPDATEAUTOMATICALLY, VIRTKEY, NOINVERT
END
IDR_SYSINFO_ACCEL ACCELERATORS
BEGIN
"1", ID_DIGIT1, VIRTKEY, NOINVERT
"2", ID_DIGIT2, VIRTKEY, NOINVERT
"3", ID_DIGIT3, VIRTKEY, NOINVERT
"4", ID_DIGIT4, VIRTKEY, NOINVERT
"5", ID_DIGIT5, VIRTKEY, NOINVERT
"6", ID_DIGIT6, VIRTKEY, NOINVERT
"7", ID_DIGIT7, VIRTKEY, NOINVERT
"8", ID_DIGIT8, VIRTKEY, NOINVERT
"9", ID_DIGIT9, VIRTKEY, NOINVERT
"B", IDC_BACK, VIRTKEY, ALT, NOINVERT
VK_BACK, IDC_BACK, VIRTKEY, NOINVERT
VK_F6, IDC_PAUSE, VIRTKEY, NOINVERT
VK_PAUSE, IDC_PAUSE, VIRTKEY, NOINVERT
VK_F5, IDC_REFRESH, VIRTKEY, NOINVERT
VK_F11, IDC_MAXSCREEN, VIRTKEY, NOINVERT
END
/////////////////////////////////////////////////////////////////////////////
//
// Bitmap
//
IDB_SEARCH_ACTIVE_BMP BITMAP "resources\\active_search.bmp"
IDB_SEARCH_INACTIVE_BMP BITMAP "resources\\inactive_search.bmp"
#endif // English (United States) resources
/////////////////////////////////////////////////////////////////////////////
================================================
FILE: SystemInformer/SystemInformer.vcxproj
================================================
Debug
Win32
Debug
x64
Debug
ARM64
Release
Win32
Release
x64
Release
ARM64
17.0
{0271DD27-6707-4290-8DFE-285702B7115D}
Win32Proj
SystemInformer
SystemInformer
$(LatestTargetPlatformVersion)
Application
WindowsLocalDebugger
Application
WindowsLocalDebugger
Application
WindowsLocalDebugger
Application
WindowsLocalDebugger
Application
WindowsLocalDebugger
Application
WindowsLocalDebugger
$(SolutionDir)bin\$(Configuration)$(PlatformArchitecture)\
$(ProjectDir)obj\$(Configuration)$(PlatformArchitecture)\
$(SolutionDir)bin\$(Configuration)$(PlatformArchitecture)\
$(ProjectDir)obj\$(Configuration)$(PlatformArchitecture)\
$(SolutionDir)bin\$(Configuration)$(PlatformArchitecture)\
$(ProjectDir)obj\$(Configuration)$(PlatformArchitecture)\
$(SolutionDir)bin\$(Configuration)$(PlatformArchitecture)\
$(ProjectDir)obj\$(Configuration)$(PlatformArchitecture)\
$(SolutionDir)bin\$(Configuration)$(PlatformArchitecture)\
$(ProjectDir)obj\$(Configuration)$(PlatformArchitecture)\
$(SolutionDir)bin\$(Configuration)$(PlatformArchitecture)\
$(ProjectDir)obj\$(Configuration)$(PlatformArchitecture)\
$(SolutionDir)phnt\include;$(SolutionDir)phlib\include;$(SolutionDir)kphlib\include;include;%(AdditionalIncludeDirectories)
_PHLIB_;_PHAPP_;_WINDOWS;WIN32;_DEBUG;DEBUG;%(PreprocessorDefinitions);$(ExternalPreprocessorOptions)
setupapi.lib;cfgmgr32.lib;aclui.lib;comctl32.lib;dnsapi.lib;gdiplus.lib;ntdll.lib;phlib.lib;userenv.lib;wbemuuid.lib;windowscodecs.lib;winhttp.lib;winsta.lib;kphlib_um.lib;%(AdditionalDependencies)
$(SolutionDir)kphlib\bin\$(Configuration)$(PlatformArchitecture);$(SolutionDir)phlib\bin\$(Configuration)$(PlatformArchitecture);%(AdditionalLibraryDirectories)
SystemInformer.def
setupapi.dll;cfgmgr32.dll;aclui.dll;comdlg32.dll;gdiplus.dll;oleaut32.dll;winhttp.dll;winsta.dll;%(DelayLoadDLLs)
$(SolutionDir)kphlib\include;$(SolutionDir)phnt\include;$(SolutionDir)phlib\include;include;%(AdditionalIncludeDirectories)
"$(SolutionDir)build\build_zdriver_sdk.cmd" "$(OutDir)$(TargetName)$(TargetExt)" "-$(Configuration)" "-$(PlatformName)"
systeminformer.manifest
$(SolutionDir)phnt\include;$(SolutionDir)phlib\include;$(SolutionDir)kphlib\include;include;%(AdditionalIncludeDirectories)
_PHLIB_;_PHAPP_;_WINDOWS;WIN64;_DEBUG;DEBUG;%(PreprocessorDefinitions);$(ExternalPreprocessorOptions)
setupapi.lib;cfgmgr32.lib;aclui.lib;comctl32.lib;dnsapi.lib;gdiplus.lib;ntdll.lib;phlib.lib;userenv.lib;wbemuuid.lib;windowscodecs.lib;winhttp.lib;winsta.lib;kphlib_um.lib;%(AdditionalDependencies)
$(SolutionDir)kphlib\bin\$(Configuration)$(PlatformArchitecture);$(SolutionDir)phlib\bin\$(Configuration)$(PlatformArchitecture);%(AdditionalLibraryDirectories)
SystemInformer.def
setupapi.dll;cfgmgr32.dll;aclui.dll;comdlg32.dll;gdiplus.dll;oleaut32.dll;winhttp.dll;winsta.dll;%(DelayLoadDLLs)
$(SolutionDir)kphlib\include;$(SolutionDir)phnt\include;$(SolutionDir)phlib\include;include;%(AdditionalIncludeDirectories)
"$(SolutionDir)build\build_zdriver_sdk.cmd" "$(OutDir)$(TargetName)$(TargetExt)" "-$(Configuration)" "-$(PlatformName)"
systeminformer.manifest
$(SolutionDir)phnt\include;$(SolutionDir)phlib\include;$(SolutionDir)kphlib\include;include;%(AdditionalIncludeDirectories)
_PHLIB_;_PHAPP_;_WINDOWS;WIN64;_DEBUG;DEBUG;%(PreprocessorDefinitions);$(ExternalPreprocessorOptions)
setupapi.lib;cfgmgr32.lib;aclui.lib;comctl32.lib;dnsapi.lib;gdiplus.lib;ntdll.lib;phlib.lib;userenv.lib;wbemuuid.lib;windowscodecs.lib;winhttp.lib;winsta.lib;kphlib_um.lib;%(AdditionalDependencies)
$(SolutionDir)kphlib\bin\$(Configuration)$(PlatformArchitecture);$(SolutionDir)phlib\bin\$(Configuration)$(PlatformArchitecture);%(AdditionalLibraryDirectories)
SystemInformer.def
setupapi.dll;cfgmgr32.dll;aclui.dll;comdlg32.dll;gdiplus.dll;oleaut32.dll;winhttp.dll;winsta.dll;%(DelayLoadDLLs)
$(SolutionDir)kphlib\include;$(SolutionDir)phnt\include;$(SolutionDir)phlib\include;include;%(AdditionalIncludeDirectories)
"$(SolutionDir)build\build_zdriver_sdk.cmd" "$(OutDir)$(TargetName)$(TargetExt)" "-$(Configuration)" "-$(PlatformName)"
systeminformer.manifest
$(SolutionDir)phnt\include;$(SolutionDir)phlib\include;$(SolutionDir)kphlib\include;include;%(AdditionalIncludeDirectories)
_PHLIB_;_PHAPP_;_WINDOWS;WIN32;NDEBUG;%(PreprocessorDefinitions);$(ExternalPreprocessorOptions)
setupapi.lib;cfgmgr32.lib;aclui.lib;comctl32.lib;dnsapi.lib;gdiplus.lib;ntdll.lib;phlib.lib;userenv.lib;wbemuuid.lib;windowscodecs.lib;winhttp.lib;winsta.lib;kphlib_um.lib;%(AdditionalDependencies)
$(SolutionDir)kphlib\bin\$(Configuration)$(PlatformArchitecture);$(SolutionDir)phlib\bin\$(Configuration)$(PlatformArchitecture);%(AdditionalLibraryDirectories)
SystemInformer.def
setupapi.dll;cfgmgr32.dll;aclui.dll;comdlg32.dll;gdiplus.dll;oleaut32.dll;winhttp.dll;winsta.dll;%(DelayLoadDLLs)
$(SolutionDir)kphlib\include;$(SolutionDir)phnt\include;$(SolutionDir)phlib\include;include;%(AdditionalIncludeDirectories)
"$(SolutionDir)build\build_zdriver_sdk.cmd" "$(OutDir)$(TargetName)$(TargetExt)" "-$(Configuration)" "-$(PlatformName)"
systeminformer.manifest
$(SolutionDir)phnt\include;$(SolutionDir)phlib\include;$(SolutionDir)kphlib\include;include;%(AdditionalIncludeDirectories)
_PHLIB_;_PHAPP_;_WINDOWS;WIN64;NDEBUG;%(PreprocessorDefinitions);$(ExternalPreprocessorOptions)
setupapi.lib;cfgmgr32.lib;aclui.lib;comctl32.lib;dnsapi.lib;gdiplus.lib;ntdll.lib;phlib.lib;userenv.lib;wbemuuid.lib;windowscodecs.lib;winhttp.lib;winsta.lib;kphlib_um.lib;%(AdditionalDependencies)
$(SolutionDir)kphlib\bin\$(Configuration)$(PlatformArchitecture);$(SolutionDir)phlib\bin\$(Configuration)$(PlatformArchitecture);%(AdditionalLibraryDirectories)
SystemInformer.def
setupapi.dll;cfgmgr32.dll;aclui.dll;comdlg32.dll;gdiplus.dll;oleaut32.dll;winhttp.dll;winsta.dll;%(DelayLoadDLLs)
$(SolutionDir)kphlib\include;$(SolutionDir)phnt\include;$(SolutionDir)phlib\include;include;%(AdditionalIncludeDirectories)
"$(SolutionDir)build\build_zdriver_sdk.cmd" "$(OutDir)$(TargetName)$(TargetExt)" "-$(Configuration)" "-$(PlatformName)"
systeminformer.manifest
$(SolutionDir)phnt\include;$(SolutionDir)phlib\include;$(SolutionDir)kphlib\include;include;%(AdditionalIncludeDirectories)
_PHLIB_;_PHAPP_;_WINDOWS;WIN64;NDEBUG;%(PreprocessorDefinitions);$(ExternalPreprocessorOptions)
setupapi.lib;cfgmgr32.lib;aclui.lib;comctl32.lib;dnsapi.lib;gdiplus.lib;ntdll.lib;phlib.lib;userenv.lib;wbemuuid.lib;windowscodecs.lib;winhttp.lib;winsta.lib;kphlib_um.lib;%(AdditionalDependencies)
$(SolutionDir)kphlib\bin\$(Configuration)$(PlatformArchitecture);$(SolutionDir)phlib\bin\$(Configuration)$(PlatformArchitecture);%(AdditionalLibraryDirectories)
SystemInformer.def
setupapi.dll;cfgmgr32.dll;aclui.dll;comdlg32.dll;gdiplus.dll;oleaut32.dll;winhttp.dll;winsta.dll;%(DelayLoadDLLs)
$(SolutionDir)kphlib\include;$(SolutionDir)phnt\include;$(SolutionDir)phlib\include;include;%(AdditionalIncludeDirectories)
"$(SolutionDir)build\build_zdriver_sdk.cmd" "$(OutDir)$(TargetName)$(TargetExt)" "-$(Configuration)" "-$(PlatformName)"
systeminformer.manifest
================================================
FILE: SystemInformer/SystemInformer.vcxproj.filters
================================================
{93995380-89BD-4b04-88EB-625FBE52EBFB}
h;hpp;hxx;hm;inl;inc;xsd
{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}
rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav
{4FC737F1-C7A5-4376-A066-2A32D752A2FF}
cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx
{84a3be0b-42cf-42ae-bcd3-16f165caa0db}
{67c40321-0b28-4c16-8c48-dc0b64c148d7}
System Informer
System Informer
System Informer
System Informer
System Informer
System Informer
System Informer
System Informer
System Informer
System Informer
System Informer
System Informer
System Informer
System Informer
System Informer
System Informer
System Informer
System Informer
System Informer
System Informer
System Informer
System Informer
System Informer
System Informer
System Informer
System Informer
System Informer
System Informer
System Informer
System Informer
System Informer
System Informer
System Informer
System Informer
System Informer
System Informer
System Informer
System Informer
System Informer
System Informer
System Informer
System Informer
System Informer
System Informer
System Informer
System Informer
System Informer
System Informer
System Informer
System Informer
System Informer
System Informer
phsvc
phsvc
phsvc
phsvc
phsvc
System Informer
System Informer
System Informer
System Informer
System Informer
System Informer
System Informer
System Informer
System Informer
System Informer
System Informer
System Informer
System Informer
System Informer
System Informer
System Informer
System Informer
System Informer
System Informer
System Informer
System Informer
System Informer
System Informer
System Informer
System Informer
System Informer
System Informer
System Informer
System Informer
System Informer
System Informer
System Informer
System Informer
System Informer
System Informer
System Informer
System Informer
System Informer
System Informer
System Informer
System Informer
System Informer
System Informer
System Informer
System Informer
System Informer
System Informer
System Informer
System Informer
System Informer
System Informer
System Informer
System Informer
Headers
Headers
Headers
Headers
Headers
Headers
Headers
Headers
Headers
Headers
Headers
Headers
Headers
Headers
Headers
Headers
Headers
Headers
Headers
Headers
Headers
Headers
Headers
Headers
Headers
Headers
Headers
Headers
Headers
Headers
Headers
Headers
Headers
Headers
Headers
Headers
Headers
Headers
Headers
Headers
Headers
Headers
Headers
Headers
Headers
Headers
Headers
Headers
Headers
Headers
Headers
Resources
Resources
Resources
Resources
Module
Resources
Resources
Resources
Resources
Resources
Resources
Resources
Resources
Resources
Resources
Resources
Resources
Resources
Resources
Resources
Resources
Resources
Resources
Resources
Resources
Resources
Resources
Resources
Resources
Resources
Resources
Resources
Resources
Resources
Resources
Resources
Resources
Resources
Resources
================================================
FILE: SystemInformer/about.c
================================================
/*
* Copyright (c) 2022 Winsider Seminars & Solutions, Inc. All rights reserved.
*
* This file is part of System Informer.
*
* Authors:
*
* wj32 2010-2016
* dmex 2017-2024
*
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
static HWND PhAboutWindowHandle = NULL;
static INT_PTR CALLBACK PhpAboutDlgProc(
_In_ HWND hwndDlg,
_In_ UINT uMsg,
_In_ WPARAM wParam,
_In_ LPARAM lParam
)
{
switch (uMsg)
{
case WM_INITDIALOG:
{
PPH_STRING versionString;
PhSetApplicationWindowIcon(hwndDlg);
PhCenterWindow(hwndDlg, PhMainWndHandle);
versionString = PhGetApplicationVersionString(TRUE);
PhMoveReference(&versionString, PhConcatStringRefZ(&versionString->sr, L"\r\n"));
PhSetDialogItemText(hwndDlg, IDC_ABOUT_NAME, versionString->Buffer);
PhDereferenceObject(versionString);
PhSetDialogItemText(hwndDlg, IDC_CREDITS,
L"Thanks to:\n"
L" wj32 - Wen Jia Liu\n"
L" dmex - Steven G\n"
L" jxy-s - Johnny Shaw\n"
L" ionescu007 - Alex Ionescu\n"
L" yardenshafir - Yarden Shafir\n"
L" Contributors - thank you for your additions!\n"
L" Donors - thank you for your support!\n\n"
L"System Informer uses the following components:\n"
L" PresentMon by Intel Corporation\n"
L" Mini-XML by Michael Sweet\n"
L" PCRE2 by Philip Hazel\n"
L" json-c by Michael Clark\n"
L" MD5 code by Jouni Malinen\n"
L" SHA1 code by Filip Navara, based on code by Steve Reid\n"
);
PhSetDialogFocus(hwndDlg, GetDlgItem(hwndDlg, IDOK));
PhRegisterWindowCallback(hwndDlg, PH_PLUGIN_WINDOW_EVENT_TYPE_TOPMOST, NULL);
PhInitializeWindowTheme(hwndDlg, PhEnableThemeSupport);
}
break;
case WM_DESTROY:
{
PhUnregisterWindowCallback(hwndDlg);
PhUnregisterDialog(PhAboutWindowHandle);
PhAboutWindowHandle = NULL;
}
break;
case WM_COMMAND:
{
switch (GET_WM_COMMAND_ID(wParam, lParam))
{
case IDCANCEL:
case IDOK:
DestroyWindow(hwndDlg);
break;
case IDC_DIAGNOSTICS:
{
PhShowInformationDialog(hwndDlg, PH_AUTO_T(PH_STRING, PhGetDiagnosticsString())->Buffer, 0);
}
break;
}
}
break;
case WM_NOTIFY:
{
LPNMHDR header = (LPNMHDR)lParam;
switch (header->code)
{
case NM_CLICK:
{
switch (header->idFrom)
{
case IDC_ABOUT_NAME:
case IDC_CREDITS:
case IDC_LINK_SF:
PhShellExecute(hwndDlg, ((PNMLINK)header)->item.szUrl, NULL);
break;
}
}
break;
}
}
break;
case WM_CTLCOLORBTN:
return HANDLE_WM_CTLCOLORBTN(hwndDlg, wParam, lParam, PhWindowThemeControlColor);
case WM_CTLCOLORDLG:
return HANDLE_WM_CTLCOLORDLG(hwndDlg, wParam, lParam, PhWindowThemeControlColor);
case WM_CTLCOLORSTATIC:
return HANDLE_WM_CTLCOLORSTATIC(hwndDlg, wParam, lParam, PhWindowThemeControlColor);
}
return FALSE;
}
VOID PhShowAboutDialog(
_In_ HWND ParentWindowHandle
)
{
if (!PhAboutWindowHandle)
{
PhAboutWindowHandle = PhCreateDialog(
PhInstanceHandle,
MAKEINTRESOURCE(IDD_ABOUT),
PhCsForceNoParent ? NULL : ParentWindowHandle,
PhpAboutDlgProc,
NULL
);
PhRegisterDialog(PhAboutWindowHandle);
ShowWindow(PhAboutWindowHandle, SW_SHOW);
}
if (IsMinimized(PhAboutWindowHandle))
ShowWindow(PhAboutWindowHandle, SW_RESTORE);
else
SetForegroundWindow(PhAboutWindowHandle);
}
FORCEINLINE ULONG PhpGetObjectTypeObjectCount(
_In_ PPH_OBJECT_TYPE ObjectType
)
{
PH_OBJECT_TYPE_INFORMATION info;
memset(&info, 0, sizeof(PH_OBJECT_TYPE_INFORMATION));
if (ObjectType) PhGetObjectTypeInformation(ObjectType, &info);
return info.NumberOfObjects;
}
PPH_STRING PhGetDiagnosticsString(
VOID
)
{
PPH_STRING versionString;
PH_STRING_BUILDER stringBuilder;
PhInitializeStringBuilder(&stringBuilder, 50);
versionString = PhGetApplicationVersionString(FALSE);
PhAppendStringBuilder(&stringBuilder, &versionString->sr);
PhAppendStringBuilder2(&stringBuilder, L"\r\n");
PhDereferenceObject(versionString);
PhAppendStringBuilder2(&stringBuilder, L"OBJECT INFORMATION\r\n");
#define OBJECT_TYPE_COUNT(Type) PhAppendFormatStringBuilder(&stringBuilder, \
TEXT(#Type) L": %lu objects\r\n", PhpGetObjectTypeObjectCount(Type))
// ref
OBJECT_TYPE_COUNT(PhObjectTypeObject);
// basesup
OBJECT_TYPE_COUNT(PhStringType);
OBJECT_TYPE_COUNT(PhBytesType);
OBJECT_TYPE_COUNT(PhListType);
OBJECT_TYPE_COUNT(PhPointerListType);
OBJECT_TYPE_COUNT(PhHashtableType);
OBJECT_TYPE_COUNT(PhFileStreamType);
// ph
OBJECT_TYPE_COUNT(PhSymbolProviderType);
OBJECT_TYPE_COUNT(PhProcessItemType);
OBJECT_TYPE_COUNT(PhServiceItemType);
OBJECT_TYPE_COUNT(PhNetworkItemType);
OBJECT_TYPE_COUNT(PhModuleProviderType);
OBJECT_TYPE_COUNT(PhModuleItemType);
OBJECT_TYPE_COUNT(PhThreadProviderType);
OBJECT_TYPE_COUNT(PhThreadItemType);
OBJECT_TYPE_COUNT(PhHandleProviderType);
OBJECT_TYPE_COUNT(PhHandleItemType);
OBJECT_TYPE_COUNT(PhMemoryItemType);
OBJECT_TYPE_COUNT(PhImageListItemType);
#ifdef DEBUG
PhAppendStringBuilder2(&stringBuilder, L"STATISTIC INFORMATION\r\n");
#define PRINT_STATISTIC(Name) PhAppendFormatStringBuilder(&stringBuilder, \
TEXT(#Name) L": %u\r\n", PhLibStatisticsBlock.Name)
PRINT_STATISTIC(BaseThreadsCreated);
PRINT_STATISTIC(BaseThreadsCreateFailed);
PRINT_STATISTIC(BaseStringBuildersCreated);
PRINT_STATISTIC(BaseStringBuildersResized);
PRINT_STATISTIC(RefObjectsCreated);
PRINT_STATISTIC(RefObjectsDestroyed);
PRINT_STATISTIC(RefObjectsAllocated);
PRINT_STATISTIC(RefObjectsFreed);
PRINT_STATISTIC(RefObjectsAllocatedFromSmallFreeList);
PRINT_STATISTIC(RefObjectsFreedToSmallFreeList);
PRINT_STATISTIC(RefObjectsAllocatedFromTypeFreeList);
PRINT_STATISTIC(RefObjectsFreedToTypeFreeList);
PRINT_STATISTIC(RefObjectsDeleteDeferred);
PRINT_STATISTIC(RefAutoPoolsCreated);
PRINT_STATISTIC(RefAutoPoolsDestroyed);
PRINT_STATISTIC(RefAutoPoolsDynamicAllocated);
PRINT_STATISTIC(RefAutoPoolsDynamicResized);
PRINT_STATISTIC(QlBlockSpins);
PRINT_STATISTIC(QlBlockWaits);
PRINT_STATISTIC(QlAcquireExclusiveBlocks);
PRINT_STATISTIC(QlAcquireSharedBlocks);
PRINT_STATISTIC(WqWorkQueueThreadsCreated);
PRINT_STATISTIC(WqWorkQueueThreadsCreateFailed);
PRINT_STATISTIC(WqWorkItemsQueued);
#endif
return PhFinalStringBuilderString(&stringBuilder);
}
PPH_STRING PhGetApplicationVersionString(
_In_ BOOLEAN LinkToCommit
)
{
PPH_STRING versionString;
PCWSTR channelName = PhGetPhReleaseChannelString();
PPH_STRING commitversionString = PhGetPhVersion();
PPH_STRING commitHashString = PhGetPhVersionHash();
#if (PHAPP_VERSION_REVISION != 0)
if (LinkToCommit)
{
PH_FORMAT format[8];
// "System Informer %lu.%lu.%lu (%hs) %ls"
PhInitFormatS(&format[0], L"System Informer ");
PhInitFormatSR(&format[1], commitversionString->sr);
PhInitFormatS(&format[2], L" (sr);
PhInitFormatS(&format[4], L"\">");
PhInitFormatSR(&format[5], commitHashString->sr);
PhInitFormatS(&format[6], L") ");
PhInitFormatS(&format[7], channelName);
versionString = PhFormat(format, RTL_NUMBER_OF(format), 0);
}
else
{
PH_FORMAT format[6];
// "System Informer %lu.%lu.%lu (%hs) %ls"
PhInitFormatS(&format[0], L"System Informer ");
PhInitFormatSR(&format[1], commitversionString->sr);
PhInitFormatS(&format[2], L" (");
PhInitFormatSR(&format[3], commitHashString->sr);
PhInitFormatS(&format[4], L") ");
PhInitFormatS(&format[5], channelName);
versionString = PhFormat(format, RTL_NUMBER_OF(format), 0);
}
#else
PH_FORMAT format[4];
// "System Informer %lu.%lu %ls"
PhInitFormatS(&format[0], L"System Informer ");
PhInitFormatSR(&format[1], commitversionString->sr);
PhInitFormatC(&format[2], L' ');
PhInitFormatS(&format[3], channelName);
versionString = PhFormat(format, RTL_NUMBER_OF(format), 0);
#endif
PhClearReference(&commitHashString);
PhClearReference(&commitversionString);
return versionString;
}
================================================
FILE: SystemInformer/actions.c
================================================
/*
* Copyright (c) 2022 Winsider Seminars & Solutions, Inc. All rights reserved.
*
* This file is part of System Informer.
*
* Authors:
*
* wj32 2010-2016
* dmex 2017-2024
*
*/
/*
* These are a set of consistent functions which will perform actions on objects such as processes,
* threads and services, while displaying any necessary prompts and error messages. Automatic
* elevation can also easily be added if necessary.
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
static volatile LONG PhSvcReferenceCount = 0;
static PH_PHSVC_MODE PhSvcCurrentMode;
static PH_QUEUED_LOCK PhSvcStartLock = PH_QUEUED_LOCK_INIT;
/**
* Callback used by elevation Task Dialogs to mark the primary action button
* as requiring elevation when the dialog is constructed.
*
* \param WindowHandle The handle to the task dialog window.
* \param Notification The Task Dialog notification code (e.g. TDN_DIALOG_CONSTRUCTED).
* \param wParam Notification-specific word parameter.
* \param lParam Notification-specific long parameter.
* \param Context Callback context (passed through lpCallbackData).
* \return HRESULT S_OK.
*/
HRESULT CALLBACK PhpElevateActionCallbackProc(
_In_ HWND WindowHandle,
_In_ UINT Notification,
_In_ WPARAM wParam,
_In_ LPARAM lParam,
_In_ LONG_PTR Context
)
{
switch (Notification)
{
case TDN_DIALOG_CONSTRUCTED:
SendMessage(WindowHandle, TDM_SET_BUTTON_ELEVATION_REQUIRED_STATE, IDYES, TRUE);
break;
}
return S_OK;
}
/**
* Display a Task Dialog asking the user to continue with an elevated action.
*
* \param WindowHandle Parent window for the dialog.
* \param Message Main instruction text describing the operation requiring elevation.
* \param Context Optional callback context passed to the dialog callback.
* \param Button Receives the ID of the button pressed by the user when the dialog returns.
* \return BOOLEAN TRUE if the dialog was shown and a button value was returned, FALSE otherwise.
*/
_Success_(return)
BOOLEAN PhpShowElevatePrompt(
_In_ HWND WindowHandle,
_In_ PCWSTR Message,
_In_opt_ PVOID Context,
_Out_ PLONG Button
)
{
TASKDIALOGCONFIG config;
CONST TASKDIALOG_BUTTON buttons[1] =
{
{ IDYES, L"Continue"}
};
LONG button;
// Currently the error dialog box is similar to the one displayed
// when you try to label a drive in Windows Explorer. It's much better
// than the clunky dialog in PH 1.x.
memset(&config, 0, sizeof(TASKDIALOGCONFIG));
config.cbSize = sizeof(TASKDIALOGCONFIG);
config.hwndParent = WindowHandle;
config.hInstance = NtCurrentImageBase();
config.dwFlags = IsWindowVisible(WindowHandle) ? TDF_POSITION_RELATIVE_TO_WINDOW : 0;
config.pszWindowTitle = PhApplicationName;
config.pszMainIcon = TD_ERROR_ICON;
config.pszMainInstruction = PhaConcatStrings2(Message, L".")->Buffer;
config.pszContent = L"You will need to provide administrator permission. "
L"Click Continue to complete this operation.";
config.dwCommonButtons = TDCBF_CANCEL_BUTTON;
config.cButtons = 1;
config.pButtons = buttons;
config.nDefaultButton = IDYES;
config.pfCallback = PhpElevateActionCallbackProc;
config.lpCallbackData = (LONG_PTR)Context;
if (PhShowTaskDialog(
&config,
&button,
NULL,
NULL
))
{
*Button = button;
return TRUE;
}
else
{
return FALSE;
}
}
/**
* Shows an error, prompts for elevation, and executes a command.
*
* \param WindowHandle The window to display user interface components on.
* \param Connected A variable which receives TRUE if the elevated
* action succeeded or FALSE if the action failed.
* \return TRUE if the user was prompted for elevation, otherwise
* FALSE, in which case you need to show your own error message.
*/
_Success_(return)
BOOLEAN PhpElevationLevelAndConnectToPhSvc(
_In_ HWND WindowHandle,
_Out_ PBOOLEAN Connected
)
{
PH_ACTION_ELEVATION_LEVEL elevationLevel;
*Connected = FALSE;
if (PhGetOwnTokenAttributes().Elevated)
return FALSE;
elevationLevel = PhGetIntegerSetting(SETTING_ELEVATION_LEVEL);
if (elevationLevel == NeverElevateAction)
return FALSE;
// Try to connect now so we can avoid prompting the user.
if (PhUiConnectToPhSvc(WindowHandle, TRUE))
{
*Connected = TRUE;
return TRUE;
}
if (
elevationLevel == PromptElevateAction ||
elevationLevel == AlwaysElevateAction
)
{
*Connected = PhUiConnectToPhSvc(WindowHandle, FALSE);
return TRUE;
}
return FALSE;
}
/**
* Shows an error, prompts for elevation, and connects to phsvc.
*
* \param WindowHandle The window to display user interface components on.
* \param Message A message describing the operation that failed.
* \param Status A NTSTATUS value.
* \param Connected A variable which receives TRUE if the user
* elevated the action and phsvc was started, or FALSE if the user
* cancelled elevation. If the value is TRUE, you need to
* perform any necessary phsvc calls and use PhUiDisconnectFromPhSvc()
* to disconnect from phsvc.
* \param Cancelled A variable which receives TRUE if the user cancelled
* the action and phsvc was started.
*
* \return TRUE if the user was prompted for elevation, otherwise
* FALSE, in which case you need to show your own error message.
*/
BOOLEAN PhpShowErrorAndConnectToPhSvc(
_In_ HWND WindowHandle,
_In_ PCWSTR Message,
_In_ NTSTATUS Status,
_Out_ PBOOLEAN Connected,
_Out_ PBOOLEAN Cancelled
)
{
PH_ACTION_ELEVATION_LEVEL elevationLevel;
LONG button = IDNO;
*Connected = FALSE;
*Cancelled = FALSE;
if (!(Status == STATUS_ACCESS_DENIED || Status == STATUS_PRIVILEGE_NOT_HELD))
return FALSE;
if (PhGetOwnTokenAttributes().Elevated)
return FALSE;
elevationLevel = PhGetIntegerSetting(SETTING_ELEVATION_LEVEL);
if (elevationLevel == NeverElevateAction)
return FALSE;
// Try to connect now so we can avoid prompting the user.
if (PhUiConnectToPhSvc(WindowHandle, TRUE))
{
*Connected = TRUE;
return TRUE;
}
if (elevationLevel == PromptElevateAction)
{
if (!PhpShowElevatePrompt(WindowHandle, Message, NULL, &button))
return FALSE;
}
if (elevationLevel == AlwaysElevateAction || button == IDYES)
{
*Connected = PhUiConnectToPhSvc(WindowHandle, FALSE);
return TRUE;
}
if (button == IDCANCEL)
{
*Cancelled = TRUE;
}
return FALSE;
}
/**
* Connects to phsvc.
*
* \param WindowHandle The window to display user interface components on.
* \param ConnectOnly TRUE to only try to connect to phsvc, otherwise
* FALSE to try to elevate and start phsvc if the initial connection
* attempt failed.
*/
BOOLEAN PhUiConnectToPhSvc(
_In_opt_ HWND WindowHandle,
_In_ BOOLEAN ConnectOnly
)
{
return PhUiConnectToPhSvcEx(WindowHandle, ElevatedPhSvcMode, ConnectOnly);
}
/**
* Get the LPC/ALPC port name for the phsvc instance corresponding to the requested mode.
*
* \param Mode The phsvc mode for which the port name is required.
* \param PortName Receives the UNICODE_STRING for the port name.
* \note Raises STATUS_INVALID_PARAMETER for unknown modes.
*/
VOID PhpGetPhSvcPortName(
_In_ PH_PHSVC_MODE Mode,
_Out_ PUNICODE_STRING PortName
)
{
switch (Mode)
{
case ElevatedPhSvcMode:
if (!PhIsExecutingInWow64())
RtlInitUnicodeString(PortName, PHSVC_PORT_NAME);
else
RtlInitUnicodeString(PortName, PHSVC_WOW64_PORT_NAME);
break;
case Wow64PhSvcMode:
RtlInitUnicodeString(PortName, PHSVC_WOW64_PORT_NAME);
break;
default:
PhRaiseStatus(STATUS_INVALID_PARAMETER);
break;
}
}
/**
* Attempt to start the phsvc helper process for the specified mode.
*
* \param WindowHandle Optional parent window for elevation UI created by ShellExecute.
* \param Mode The phsvc mode to start (ElevatedPhSvcMode or Wow64PhSvcMode).
* \return BOOLEAN TRUE on success (an attempt to start phsvc was made and succeeded), FALSE otherwise.
*/
BOOLEAN PhpStartPhSvcProcess(
_In_opt_ HWND WindowHandle,
_In_ PH_PHSVC_MODE Mode
)
{
switch (Mode)
{
case ElevatedPhSvcMode:
if (NT_SUCCESS(PhShellProcessHacker(
WindowHandle,
L"-phsvc",
SW_HIDE,
PH_SHELL_EXECUTE_ADMIN,
0,
0,
NULL
)))
{
return TRUE;
}
break;
case Wow64PhSvcMode:
{
static CONST PH_STRINGREF relativeFileNames[] =
{
PH_STRINGREF_INIT(L"\\x86\\"),
#ifdef DEBUG
PH_STRINGREF_INIT(L"\\..\\Debug32\\"),
PH_STRINGREF_INIT(L"\\..\\Release32\\")
#endif
};
ULONG i;
PPH_STRING applicationDirectory;
PPH_STRING applicationFileName;
if (!(applicationDirectory = PhGetApplicationDirectoryWin32()))
return FALSE;
if (!(applicationFileName = PhGetApplicationFileNameWin32()))
return FALSE;
PhMoveReference(&applicationFileName, PhGetBaseName(applicationFileName));
for (i = 0; i < RTL_NUMBER_OF(relativeFileNames); i++)
{
PPH_STRING fileName;
PPH_STRING fileFullPath;
fileName = PhConcatStringRef3(
&applicationDirectory->sr,
&relativeFileNames[i],
&applicationFileName->sr
);
if (NT_SUCCESS(PhGetFullPath(PhGetString(fileName), &fileFullPath, NULL)))
{
PhMoveReference(&fileName, fileFullPath);
}
if (PhDoesFileExistWin32(PhGetString(fileName)))
{
if (NT_SUCCESS(PhShellProcessHackerEx(
WindowHandle,
PhGetString(fileName),
L"-phsvc",
SW_HIDE,
PH_SHELL_EXECUTE_DEFAULT,
0,
0,
NULL
)))
{
PhDereferenceObject(fileName);
PhDereferenceObject(applicationFileName);
PhDereferenceObject(applicationDirectory);
return TRUE;
}
}
PhDereferenceObject(fileName);
}
PhDereferenceObject(applicationFileName);
PhDereferenceObject(applicationDirectory);
}
break;
}
return FALSE;
}
/**
* Connects to phsvc.
*
* \param WindowHandle The window to display user interface components on.
* \param Mode The type of phsvc instance to connect to.
* \param ConnectOnly TRUE to only try to connect to phsvc, otherwise
* FALSE to try to elevate and start phsvc if the initial connection
* attempt failed.
*/
BOOLEAN PhUiConnectToPhSvcEx(
_In_opt_ HWND WindowHandle,
_In_ PH_PHSVC_MODE Mode,
_In_ BOOLEAN ConnectOnly
)
{
NTSTATUS status;
BOOLEAN started;
UNICODE_STRING portName;
if (_InterlockedIncrementNoZero(&PhSvcReferenceCount))
{
if (PhSvcCurrentMode == Mode)
{
started = TRUE;
}
else
{
_InterlockedDecrement(&PhSvcReferenceCount);
started = FALSE;
}
}
else
{
PhAcquireQueuedLockExclusive(&PhSvcStartLock);
if (_InterlockedExchange(&PhSvcReferenceCount, 0) == 0)
{
started = FALSE;
PhpGetPhSvcPortName(Mode, &portName);
// Try to connect first, then start the server if we failed.
status = PhSvcConnectToServer(&portName, 0);
if (NT_SUCCESS(status))
{
started = TRUE;
PhSvcCurrentMode = Mode;
_InterlockedIncrement(&PhSvcReferenceCount);
}
else if (!ConnectOnly)
{
// Prompt for elevation, and then try to connect to the server.
if (PhpStartPhSvcProcess(WindowHandle, Mode))
started = TRUE;
if (started)
{
ULONG attempts = 10;
// Try to connect several times because the server may take
// a while to initialize.
do
{
status = PhSvcConnectToServer(&portName, 0);
if (NT_SUCCESS(status))
break;
PhDelayExecution(1000);
} while (--attempts != 0);
// Increment the reference count even if we failed.
// We don't want to prompt the user again.
PhSvcCurrentMode = Mode;
_InterlockedIncrement(&PhSvcReferenceCount);
}
}
}
else
{
if (PhSvcCurrentMode == Mode)
{
started = TRUE;
_InterlockedIncrement(&PhSvcReferenceCount);
}
else
{
started = FALSE;
}
}
PhReleaseQueuedLockExclusive(&PhSvcStartLock);
}
return started;
}
/**
* Disconnects from phsvc.
*/
VOID PhUiDisconnectFromPhSvc(
VOID
)
{
PhAcquireQueuedLockExclusive(&PhSvcStartLock);
if (_InterlockedDecrement(&PhSvcReferenceCount) == 0)
{
PhSvcDisconnectFromServer();
}
PhReleaseQueuedLockExclusive(&PhSvcStartLock);
}
/**
* Locks the current workstation.
*
* \param WindowHandle Parent window used for error reporting.
* \return BOOLEAN TRUE on success, FALSE on failure (and an error UI is shown).
*/
BOOLEAN PhUiLockComputer(
_In_ HWND WindowHandle
)
{
if (LockWorkStation())
return TRUE;
else
PhShowStatus(WindowHandle, L"Unable to lock the computer.", 0, PhGetLastError());
return FALSE;
}
/**
* Log the current user off.
*
* \param WindowHandle Parent window used for error reporting.
* \return BOOLEAN TRUE on success, FALSE on failure (and an error UI is shown).
*/
BOOLEAN PhUiLogoffComputer(
_In_ HWND WindowHandle
)
{
if (ExitWindowsEx(EWX_LOGOFF, 0))
return TRUE;
else
PhShowStatus(WindowHandle, L"Unable to log off the computer.", 0, PhGetLastError());
return FALSE;
}
/**
* Put the system into sleep (standby) state.
*
* \param WindowHandle Parent window used for error reporting.
* \return BOOLEAN TRUE on success, FALSE on failure (and an error UI is shown).
*/
BOOLEAN PhUiSleepComputer(
_In_ HWND WindowHandle
)
{
NTSTATUS status;
if (NT_SUCCESS(status = NtInitiatePowerAction(
PowerActionSleep,
PowerSystemSleeping1,
0,
FALSE
)))
return TRUE;
else
PhShowStatus(WindowHandle, L"Unable to sleep the computer.", status, 0);
return FALSE;
}
/**
* Put the system into hibernate state.
*
* \param WindowHandle Parent window used for error reporting.
* \return BOOLEAN TRUE on success, FALSE on failure (and an error UI is shown).
*/
BOOLEAN PhUiHibernateComputer(
_In_ HWND WindowHandle
)
{
NTSTATUS status;
if (NT_SUCCESS(status = NtInitiatePowerAction(
PowerActionHibernate,
PowerSystemSleeping1,
0,
FALSE
)))
return TRUE;
else
PhShowStatus(WindowHandle, L"Unable to hibernate the computer.", status, 0);
return FALSE;
}
/**
* Restart the computer using the specified power action type.
*
* \param WindowHandle Parent window used for confirmation and error UI.
* \param Action The type of restart to perform (PH_POWERACTION_TYPE_*).
* \param Flags Additional flags passed to PhInitiateShutdown for Win32 restart.
* \return BOOLEAN TRUE if the restart action was initiated, FALSE otherwise.
*/
BOOLEAN PhUiRestartComputer(
_In_ HWND WindowHandle,
_In_ PH_POWERACTION_TYPE Action,
_In_ ULONG Flags
)
{
switch (Action)
{
case PH_POWERACTION_TYPE_WIN32:
{
if (!PhGetIntegerSetting(SETTING_ENABLE_WARNINGS) || PhShowConfirmMessage(
WindowHandle,
L"restart",
L"the computer",
NULL,
FALSE
))
{
ULONG status = PhInitiateShutdown(PH_SHUTDOWN_RESTART | Flags);
if (status == ERROR_SUCCESS)
return TRUE;
PhShowStatus(WindowHandle, L"Unable to restart the computer.", 0, status);
//if (ExitWindowsEx(EWX_REBOOT | EWX_BOOTOPTIONS, 0))
// return TRUE;
//else
// PhShowStatus(WindowHandle, L"Unable to restart the computer.", 0, GetLastError());
}
}
break;
case PH_POWERACTION_TYPE_NATIVE:
{
PPH_STRING messageText;
messageText = PhaFormatString(
L"This option %s %s in a disorderly manner and may cause file corruption or system instability.",
L"performs a hard",
L"restart");
// Ignore the EnableWarnings preference and always show the warning prompt. (dmex)
if (PhShowConfirmMessage(
WindowHandle,
L"restart",
L"the computer",
messageText->Buffer,
TRUE
))
{
NTSTATUS status;
status = NtShutdownSystem(ShutdownReboot);
if (NT_SUCCESS(status))
return TRUE;
PhShowStatus(WindowHandle, L"Unable to restart the computer.", status, 0);
}
}
break;
case PH_POWERACTION_TYPE_CRITICAL:
{
PPH_STRING messageText;
messageText = PhaFormatString(
L"This option %s %s in an disorderly manner and may cause corrupted files or instability in the system.",
L"forces a critical",
L"restart");
// Ignore the EnableWarnings preference and always show the warning prompt. (dmex)
if (PhShowConfirmMessage(
WindowHandle,
L"restart",
L"the computer",
messageText->Buffer,
TRUE
))
{
NTSTATUS status;
status = NtSetSystemPowerState(
PowerActionShutdownReset,
PowerSystemShutdown,
POWER_ACTION_CRITICAL
);
//status = NtInitiatePowerAction(
// PowerActionShutdownReset,
// PowerSystemShutdown,
// POWER_ACTION_CRITICAL,
// FALSE
// );
if (NT_SUCCESS(status))
return TRUE;
PhShowStatus(WindowHandle, L"Unable to restart the computer.", status, 0);
}
}
break;
case PH_POWERACTION_TYPE_ADVANCEDBOOT:
{
if (!PhGetIntegerSetting(SETTING_ENABLE_WARNINGS) || PhShowConfirmMessage(
WindowHandle,
L"restart",
L"the computer",
NULL,
FALSE
))
{
NTSTATUS status;
status = PhBcdSetAdvancedOptionsOneTime(
TRUE
);
if (NT_SUCCESS(status))
{
status = PhInitiateShutdown(PH_SHUTDOWN_RESTART);
if (status == ERROR_SUCCESS)
return TRUE;
PhShowStatus(WindowHandle, L"Unable to configure the advanced boot options.", 0, status);
}
else
{
PhShowStatus(WindowHandle, L"Unable to configure the advanced boot options.", status, 0);
}
}
}
break;
case PH_POWERACTION_TYPE_FIRMWAREBOOT:
{
if (!PhGetOwnTokenAttributes().Elevated)
{
PhShowMessage2(
WindowHandle,
TD_OK_BUTTON,
TD_ERROR_ICON,
L"Unable to restart to firmware options.",
L"Make sure System Informer is running with administrative privileges."
);
break;
}
if (!NT_SUCCESS(PhAdjustPrivilege(NULL, SE_SYSTEM_ENVIRONMENT_PRIVILEGE, TRUE)))
{
PhShowMessage2(
WindowHandle,
TD_OK_BUTTON,
TD_ERROR_ICON,
L"Unable to restart to firmware options.",
L"Make sure System Informer is running with administrative privileges."
);
break;
}
if (!PhIsFirmwareSupported())
{
PhShowMessage2(
WindowHandle,
TD_OK_BUTTON,
TD_ERROR_ICON,
L"Unable to restart to firmware options.",
L"This machine does not have UEFI support."
);
break;
}
if (!PhGetIntegerSetting(SETTING_ENABLE_WARNINGS) || PhShowConfirmMessage(
WindowHandle,
L"restart",
L"the computer",
NULL,
FALSE
))
{
NTSTATUS status;
status = PhSetSystemEnvironmentBootToFirmware();
if (NT_SUCCESS(status))
{
status = PhInitiateShutdown(PH_SHUTDOWN_RESTART);
if (status == ERROR_SUCCESS)
return TRUE;
PhShowStatus(WindowHandle, L"Unable to restart the computer.", 0, status);
}
else
{
PhShowStatus(WindowHandle, L"Unable to restart the computer.", status, 0);
}
}
}
break;
case PH_POWERACTION_TYPE_UPDATE:
{
if (!PhGetIntegerSetting(SETTING_ENABLE_WARNINGS) || PhShowConfirmMessage(
WindowHandle,
L"update and restart",
L"the computer",
NULL,
FALSE
))
{
ULONG status = PhInitiateShutdown(PH_SHUTDOWN_RESTART | PH_SHUTDOWN_INSTALL_UPDATES);
if (status == ERROR_SUCCESS)
return TRUE;
PhShowStatus(WindowHandle, L"Unable to restart the computer.", 0, status);
}
}
break;
case PH_POWERACTION_TYPE_WDOSCAN:
{
if (!PhGetIntegerSetting(SETTING_ENABLE_WARNINGS) || PhShowConfirmMessage(
WindowHandle,
L"restart",
L"the computer for Windows Defender Offline Scan",
NULL,
FALSE
))
{
HRESULT status = PhRestartDefenderOfflineScan();
if (status == S_OK)
return TRUE;
if ((status & 0xFFFF0000) == MAKE_HRESULT(SEVERITY_ERROR, FACILITY_WIN32, 0))
{
PhShowStatus(WindowHandle, L"Unable to restart the computer.", 0, HRESULT_CODE(status));
}
else
{
PhShowStatus(WindowHandle, L"Unable to restart the computer.", STATUS_UNSUCCESSFUL, 0);
}
}
}
break;
}
return FALSE;
}
/**
* Shutdown the computer using the specified power action type.
*
* \param WindowHandle Parent window used for confirmation and error UI.
* \param Action The type of shutdown to perform (PH_POWERACTION_TYPE_*).
* \param Flags Additional flags passed to PhInitiateShutdown for Win32 shutdown.
* \return BOOLEAN TRUE if the shutdown action was initiated, FALSE otherwise.
*/
BOOLEAN PhUiShutdownComputer(
_In_ HWND WindowHandle,
_In_ PH_POWERACTION_TYPE Action,
_In_ ULONG Flags
)
{
switch (Action)
{
case PH_POWERACTION_TYPE_WIN32:
{
if (!PhGetIntegerSetting(SETTING_ENABLE_WARNINGS) || PhShowConfirmMessage(
WindowHandle,
L"shut down",
L"the computer",
NULL,
FALSE
))
{
ULONG status = PhInitiateShutdown(PH_SHUTDOWN_POWEROFF | Flags);
if (status == ERROR_SUCCESS)
return TRUE;
PhShowStatus(WindowHandle, L"Unable to shut down the computer.", 0, status);
//if (ExitWindowsEx(EWX_POWEROFF | EWX_HYBRID_SHUTDOWN, 0))
// return TRUE;
//else if (ExitWindowsEx(EWX_SHUTDOWN | EWX_HYBRID_SHUTDOWN, 0))
// return TRUE;
//else
// PhShowStatus(WindowHandle, L"Unable to shut down the computer.", 0, GetLastError());
}
}
break;
case PH_POWERACTION_TYPE_NATIVE:
{
PPH_STRING messageText;
messageText = PhaFormatString(
L"This option %s %s in an disorderly manner and may cause corrupted files or instability in the system.",
L"performs a hard",
L"shut down");
// Ignore the EnableWarnings preference and always show the warning prompt. (dmex)
if (PhShowConfirmMessage(
WindowHandle,
L"shut down",
L"the computer",
messageText->Buffer,
TRUE
))
{
NTSTATUS status;
status = NtShutdownSystem(ShutdownPowerOff);
if (NT_SUCCESS(status))
return TRUE;
PhShowStatus(WindowHandle, L"Unable to shut down the computer.", status, 0);
}
}
break;
case PH_POWERACTION_TYPE_CRITICAL:
{
PPH_STRING messageText;
messageText = PhaFormatString(
L"This option %s %s in an disorderly manner and may cause corrupted files or instability in the system.",
L"forces a critical",
L"shut down");
// Ignore the EnableWarnings preference and always show the warning prompt. (dmex)
if (PhShowConfirmMessage(
WindowHandle,
L"shut down",
L"the computer",
messageText->Buffer,
TRUE
))
{
NTSTATUS status;
status = NtSetSystemPowerState(
PowerActionShutdownOff,
PowerSystemShutdown,
POWER_ACTION_CRITICAL
);
//status = NtInitiatePowerAction(
// PowerActionShutdownReset,
// PowerSystemShutdown,
// POWER_ACTION_CRITICAL,
// FALSE
// );
if (NT_SUCCESS(status))
return TRUE;
PhShowStatus(WindowHandle, L"Unable to shut down the computer.", status, 0);
}
}
break;
case PH_POWERACTION_TYPE_UPDATE:
{
if (!PhGetIntegerSetting(SETTING_ENABLE_WARNINGS) || PhShowConfirmMessage(
WindowHandle,
L"update and shutdown",
L"the computer",
NULL,
FALSE
))
{
ULONG status = PhInitiateShutdown(PH_SHUTDOWN_POWEROFF | PH_SHUTDOWN_INSTALL_UPDATES);
if (status == ERROR_SUCCESS)
return TRUE;
PhShowStatus(WindowHandle, L"Unable to shut down the computer.", 0, status);
}
}
break;
}
return FALSE;
}
/**
* Build a dynamic menu listing boot applications (one-time boot entries).
*
* \param DelayLoadMenu If TRUE, the function will create a placeholder menu item
* and delay enumerating boot applications until the menu is opened.
* \return PVOID A menu item object (owner-managed); may be disabled if the caller lacks privileges.
*/
PVOID PhUiCreateComputerBootDeviceMenu(
_In_ BOOLEAN DelayLoadMenu
)
{
PPH_EMENU_ITEM menuItem;
PPH_LIST bootApplicationList;
menuItem = PhCreateEMenuItem(PH_EMENU_DISABLED, ID_COMPUTER_RESTARTBOOTDEVICE, L"Restart to boot application", NULL, NULL);
if (!PhGetOwnTokenAttributes().Elevated)
return menuItem;
if (!DelayLoadMenu)
{
BOOLEAN bootEnumerateAllObjects = !!PhGetIntegerSetting(SETTING_ENABLE_BOOT_OBJECTS_ENUMERATE);
if (bootApplicationList = PhBcdQueryBootApplicationList(bootEnumerateAllObjects))
{
for (ULONG i = 0; i < bootApplicationList->Count; i++)
{
PPH_BCD_OBJECT_LIST entry = bootApplicationList->Items[i];
PPH_EMENU_ITEM menuItemNew;
menuItemNew = PhCreateEMenuItem(
PH_EMENU_TEXT_OWNED,
ID_COMPUTER_RESTARTBOOTDEVICE,
PhAllocateCopy(entry->ObjectName->Buffer, entry->ObjectName->Length + sizeof(UNICODE_NULL)),
NULL,
UlongToPtr(i)
);
PhInsertEMenuItem(menuItem, menuItemNew, ULONG_MAX);
}
if (bootApplicationList->Count)
PhSetEnabledEMenuItem(menuItem, TRUE);
PhBcdDestroyBootApplicationList(bootApplicationList);
}
}
return menuItem;
}
/**
* Build a dynamic menu listing firmware boot applications (UEFI).
*
* \param DelayLoadMenu If TRUE, the function will create a placeholder menu item
* and delay enumerating firmware applications until the menu is opened.
* \return PVOID A menu item object (owner-managed); may be disabled if the caller lacks privileges.
*/
PVOID PhUiCreateComputerFirmwareDeviceMenu(
_In_ BOOLEAN DelayLoadMenu
)
{
PPH_EMENU_ITEM menuItem;
PPH_LIST firmwareApplicationList;
menuItem = PhCreateEMenuItem(PH_EMENU_DISABLED, ID_COMPUTER_RESTARTFWDEVICE, L"Restart to firmware application", NULL, NULL);
if (!PhGetOwnTokenAttributes().Elevated)
return menuItem;
if (!DelayLoadMenu)
{
if (firmwareApplicationList = PhBcdQueryFirmwareBootApplicationList())
{
for (ULONG i = 0; i < firmwareApplicationList->Count; i++)
{
PPH_BCD_OBJECT_LIST entry = firmwareApplicationList->Items[i];
PPH_EMENU_ITEM menuItemNew;
menuItemNew = PhCreateEMenuItem(
PH_EMENU_TEXT_OWNED,
ID_COMPUTER_RESTARTFWDEVICE,
PhAllocateCopy(entry->ObjectName->Buffer, entry->ObjectName->Length + sizeof(UNICODE_NULL)),
NULL,
UlongToPtr(i)
);
PhInsertEMenuItem(menuItem, menuItemNew, ULONG_MAX);
}
if (firmwareApplicationList->Count)
PhSetEnabledEMenuItem(menuItem, TRUE);
PhBcdDestroyBootApplicationList(firmwareApplicationList);
}
}
return menuItem;
}
/**
* Handle selection of a boot application menu entry by configuring a one-time boot entry
* and initiating a restart if the operation succeeded.
*
* \param WindowHandle Parent window for confirmation and error dialogs.
* \param MenuIndex Index of the selected boot application as returned from the created menu.
*/
VOID PhUiHandleComputerBootApplicationMenu(
_In_ HWND WindowHandle,
_In_ ULONG MenuIndex
)
{
NTSTATUS status = STATUS_UNSUCCESSFUL;
BOOLEAN bootEnumerateAllObjects;
BOOLEAN bootUpdateFwBootObjects;
PPH_LIST bootApplicationList;
if (PhGetIntegerSetting(SETTING_ENABLE_WARNINGS) && !PhShowConfirmMessage(
WindowHandle,
L"restart",
L"the computer",
NULL,
FALSE
))
{
return;
}
bootEnumerateAllObjects = !!PhGetIntegerSetting(SETTING_ENABLE_BOOT_OBJECTS_ENUMERATE);
bootUpdateFwBootObjects = !!PhGetIntegerSetting(SETTING_ENABLE_UPDATE_DEFAULT_FIRMWARE_BOOT_ENTRY);
if (bootApplicationList = PhBcdQueryBootApplicationList(bootEnumerateAllObjects))
{
if (MenuIndex < bootApplicationList->Count)
{
PPH_BCD_OBJECT_LIST entry = bootApplicationList->Items[MenuIndex];
status = PhBcdSetBootApplicationOneTime(&entry->ObjectGuid, bootUpdateFwBootObjects);
}
PhBcdDestroyBootApplicationList(bootApplicationList);
}
if (NT_SUCCESS(status))
{
status = PhInitiateShutdown(PH_SHUTDOWN_RESTART);
if (status != ERROR_SUCCESS)
{
PhShowStatus(WindowHandle, L"Unable to configure the boot application.", 0, status);
}
}
else
{
PhShowStatus(WindowHandle, L"Unable to configure the boot application.", status, 0);
}
}
/**
* Handle selection of a firmware boot application menu entry by configuring a one-time firmware boot
* entry and initiating a restart if the operation succeeded.
*
* \param WindowHandle Parent window for confirmation and error dialogs.
* \param MenuIndex Index of the selected firmware application as returned from the created menu.
*/
VOID PhUiHandleComputerFirmwareApplicationMenu(
_In_ HWND WindowHandle,
_In_ ULONG MenuIndex
)
{
NTSTATUS status = STATUS_UNSUCCESSFUL;
PPH_LIST firmwareApplicationList;
if (PhGetIntegerSetting(SETTING_ENABLE_WARNINGS) && !PhShowConfirmMessage(
WindowHandle,
L"restart",
L"the computer",
NULL,
FALSE
))
{
return;
}
if (firmwareApplicationList = PhBcdQueryFirmwareBootApplicationList())
{
if (MenuIndex < firmwareApplicationList->Count)
{
PPH_BCD_OBJECT_LIST entry = firmwareApplicationList->Items[MenuIndex];
status = PhBcdSetFirmwareBootApplicationOneTime(&entry->ObjectGuid);
}
PhBcdDestroyBootApplicationList(firmwareApplicationList);
}
if (NT_SUCCESS(status))
{
status = PhInitiateShutdown(PH_SHUTDOWN_RESTART);
if (status != ERROR_SUCCESS)
{
PhShowStatus(WindowHandle, L"Unable to configure the boot application.", 0, status);
}
}
else
{
PhShowStatus(WindowHandle, L"Unable to configure the boot application.", status, 0);
}
}
typedef struct _PHP_USERSMENU_ENTRY
{
ULONG SessionId;
PPH_STRING UserName;
} PHP_USERSMENU_ENTRY, *PPHP_USERSMENU_ENTRY;
/**
* Comparison callback used to sort user session menu entries.
*
* \param Context Unused callback context.
* \param elem1 Pointer to the first element to compare.
* \param elem2 Pointer to the second element to compare.
* \return int <0 if elem1 < elem2, 0 if equal, >0 if elem1 > elem2.
*/
static int __cdecl PhpUsersMainMenuNameCompare(
_In_ void* Context,
_In_ void const* elem1,
_In_ void const* elem2
)
{
PPHP_USERSMENU_ENTRY item1 = *(PPHP_USERSMENU_ENTRY*)elem1;
PPHP_USERSMENU_ENTRY item2 = *(PPHP_USERSMENU_ENTRY*)elem2;
return PhCompareString(item1->UserName, item2->UserName, TRUE);
}
/**
* Populate the provided users menu item with entries for each active WinStation session.
*
* \param UsersMenuItem Menu object to populate with per-session submenus.
*/
VOID PhUiCreateSessionMenu(
_In_ PVOID UsersMenuItem
)
{
PPH_LIST userSessionList;
PSESSIONIDW sessions;
ULONG numberOfSessions;
ULONG i;
userSessionList = PhCreateList(1);
if (WinStationEnumerateW(WINSTATION_CURRENT_SERVER, &sessions, &numberOfSessions))
{
for (i = 0; i < numberOfSessions; i++)
{
WINSTATIONINFORMATION winStationInfo;
ULONG returnLength;
SIZE_T formatLength;
PH_FORMAT format[5];
PH_STRINGREF menuTextSr;
WCHAR formatBuffer[0x100];
if (!WinStationQueryInformationW(
WINSTATION_CURRENT_SERVER,
sessions[i].SessionId,
WinStationInformation,
&winStationInfo,
sizeof(WINSTATIONINFORMATION),
&returnLength
))
{
winStationInfo.Domain[0] = UNICODE_NULL;
winStationInfo.UserName[0] = UNICODE_NULL;
}
if (winStationInfo.Domain[0] == UNICODE_NULL || winStationInfo.UserName[0] == UNICODE_NULL)
{
// Probably the Services or RDP-Tcp session.
continue;
}
PhInitFormatU(&format[0], sessions[i].SessionId);
PhInitFormatS(&format[1], L": ");
PhInitFormatS(&format[2], winStationInfo.Domain);
PhInitFormatC(&format[3], OBJ_NAME_PATH_SEPARATOR);
PhInitFormatS(&format[4], winStationInfo.UserName);
if (!PhFormatToBuffer(
format,
RTL_NUMBER_OF(format),
formatBuffer,
sizeof(formatBuffer),
&formatLength
))
{
continue;
}
menuTextSr.Length = formatLength - sizeof(UNICODE_NULL);
menuTextSr.Buffer = formatBuffer;
{
PPHP_USERSMENU_ENTRY entry;
entry = PhCreateAlloc(sizeof(PHP_USERSMENU_ENTRY));
entry->SessionId = sessions[i].SessionId;
entry->UserName = PhCreateString2(&menuTextSr);
PhAddItemList(userSessionList, entry);
}
}
WinStationFreeMemory(sessions);
}
// Sort the users. (dmex)
qsort_s(userSessionList->Items, userSessionList->Count, sizeof(PVOID), PhpUsersMainMenuNameCompare, NULL);
// Update the users menu. (dmex)
for (i = 0; i < userSessionList->Count; i++)
{
PPHP_USERSMENU_ENTRY entry;
PPH_STRING escapedMenuText;
PPH_EMENU_ITEM userMenu;
entry = userSessionList->Items[i];
escapedMenuText = PhEscapeStringForMenuPrefix(&entry->UserName->sr);
userMenu = PhCreateEMenuItem(
PH_EMENU_TEXT_OWNED,
0,
PhAllocateCopy(escapedMenuText->Buffer, escapedMenuText->Length + sizeof(UNICODE_NULL)),
NULL,
UlongToPtr(entry->SessionId)
);
PhDereferenceObject(escapedMenuText);
PhDereferenceObject(entry->UserName);
PhInsertEMenuItem(userMenu, PhCreateEMenuItem(0, ID_USER_CONNECT, L"&Connect", NULL, NULL), ULONG_MAX);
PhInsertEMenuItem(userMenu, PhCreateEMenuItem(0, ID_USER_DISCONNECT, L"&Disconnect", NULL, NULL), ULONG_MAX);
PhInsertEMenuItem(userMenu, PhCreateEMenuItem(0, ID_USER_LOGOFF, L"&Logoff", NULL, NULL), ULONG_MAX);
PhInsertEMenuItem(userMenu, PhCreateEMenuItem(0, ID_USER_REMOTECONTROL, L"Rem&ote control", NULL, NULL), ULONG_MAX);
PhInsertEMenuItem(userMenu, PhCreateEMenuItem(0, ID_USER_SENDMESSAGE, L"Send &message...", NULL, NULL), ULONG_MAX);
PhInsertEMenuItem(userMenu, PhCreateEMenuSeparator(), ULONG_MAX);
PhInsertEMenuItem(userMenu, PhCreateEMenuItem(0, ID_USER_PROPERTIES, L"P&roperties", NULL, NULL), ULONG_MAX);
PhInsertEMenuItem(UsersMenuItem, userMenu, ULONG_MAX);
}
PhDereferenceObjects(userSessionList->Items, userSessionList->Count);
PhDereferenceObject(userSessionList);
}
/**
* Connect the current console to a remote/non-current WinStation session. Prompts for a password
* if an initial attempt without credentials fails.
*
* \param WindowHandle Parent window for password prompts and error UI.
* \param SessionId The WinStation session id to connect to.
* \return BOOLEAN TRUE on success, FALSE on failure or user cancel.
*/
BOOLEAN PhUiConnectSession(
_In_ HWND WindowHandle,
_In_ ULONG SessionId
)
{
BOOLEAN success = FALSE;
PPH_STRING selectedChoice = NULL;
PPH_STRING oldSelectedChoice = NULL;
// Try once with no password.
if (WinStationConnectW(WINSTATION_CURRENT_SERVER, SessionId, LOGONID_CURRENT, L"", TRUE))
return TRUE;
while (PhaChoiceDialog(
WindowHandle,
L"Connect to session",
L"Password:",
NULL,
0,
NULL,
PH_CHOICE_DIALOG_PASSWORD,
&selectedChoice,
NULL,
NULL
))
{
if (oldSelectedChoice)
{
RtlSecureZeroMemory(oldSelectedChoice->Buffer, oldSelectedChoice->Length);
PhDereferenceObject(oldSelectedChoice);
}
oldSelectedChoice = selectedChoice;
if (WinStationConnectW(WINSTATION_CURRENT_SERVER, SessionId, LOGONID_CURRENT, selectedChoice->Buffer, TRUE))
{
success = TRUE;
break;
}
else
{
if (!PhShowContinueStatus(WindowHandle, L"Unable to connect to the session", 0, GetLastError()))
break;
}
}
if (oldSelectedChoice)
{
RtlSecureZeroMemory(oldSelectedChoice->Buffer, oldSelectedChoice->Length);
PhDereferenceObject(oldSelectedChoice);
}
return success;
}
/**
* Disconnect a WinStation session.
*
* \param WindowHandle Parent window for error reporting.
* \param SessionId The WinStation session id to disconnect.
* \return BOOLEAN TRUE on success, FALSE on failure (and an error UI is shown).
*/
BOOLEAN PhUiDisconnectSession(
_In_ HWND WindowHandle,
_In_ ULONG SessionId
)
{
if (WinStationDisconnect(WINSTATION_CURRENT_SERVER, SessionId, FALSE))
return TRUE;
else
PhShowStatus(WindowHandle, L"Unable to disconnect the session", 0, GetLastError());
return FALSE;
}
/**
* Log off a specific WinStation session after optional confirmation.
*
* \param WindowHandle Parent window for confirmation and error UI.
* \param SessionId The WinStation session id to log off.
* \return BOOLEAN TRUE on success, FALSE on failure or if the user cancelled.
*/
BOOLEAN PhUiLogoffSession(
_In_ HWND WindowHandle,
_In_ ULONG SessionId
)
{
if (!PhGetIntegerSetting(SETTING_ENABLE_WARNINGS) || PhShowConfirmMessage(
WindowHandle,
L"logoff",
L"the user",
NULL,
FALSE
))
{
if (WinStationReset(WINSTATION_CURRENT_SERVER, SessionId, FALSE))
return TRUE;
else
PhShowStatus(WindowHandle, L"Unable to logoff the session", 0, GetLastError());
}
return FALSE;
}
/**
* Determines if a process is a system process.
*
* \param ProcessId The PID of the process to check.
*/
BOOLEAN PhIsDangerousProcess(
_In_ HANDLE ProcessId
)
{
static CONST ULONG DangerousProcesses[] =
{
0x6ccbdb46, // csrss.exe
0x5920bffe, // dwm.exe
0x8880527b, // logonui.exe
0x9fd9b2be, // lsass.exe
0xb1c6af0a, // lsm.exe
0xaafce8c2, // services.exe
0xfe38787e, // smss.exe
0x9d662730, // wininit.exe
0x2aa5caab, // winlogon.exe
};
PPH_STRING fileName;
ULONG hash;
if (ProcessId == SYSTEM_PROCESS_ID)
return TRUE;
if (!NT_SUCCESS(PhGetProcessImageFileNameByProcessId(ProcessId, &fileName)))
return FALSE;
PhMoveReference(&fileName, PhGetBaseName(fileName));
hash = PhHashStringRefEx(&fileName->sr, TRUE, PH_STRING_HASH_X65599);
PhDereferenceObject(fileName);
for (ULONG i = 0; i < RTL_NUMBER_OF(DangerousProcesses); i++)
{
if (hash == DangerousProcesses[i])
return TRUE;
}
if (PhPluginsEnabled)
{
PH_PLUGIN_IS_DANGEROUS_PROCESS processInfo;
processInfo.ProcessId = ProcessId;
processInfo.DangerousProcess = FALSE;
PhInvokeCallback(PhGetGeneralCallback(GeneralCallbackDangerousProcess), &processInfo);
if (processInfo.DangerousProcess)
return TRUE;
}
return FALSE;
}
#if defined(PH_TS_IS_SYSTEM_PROCESS)
typedef struct _PH_IS_SYSTEM_PROCESS_CONTEXT
{
PPH_STRING BaseName;
BOOLEAN Found;
} PH_IS_SYSTEM_PROCESS_CONTEXT, *PPH_IS_SYSTEM_PROCESS_CONTEXT;
_Function_class_(PH_ENUM_KEY_CALLBACK)
static BOOLEAN NTAPI PhIsSystemProcessCallback(
_In_ HANDLE RootDirectory,
_In_ PKEY_VALUE_FULL_INFORMATION Information,
_In_ PPH_IS_SYSTEM_PROCESS_CONTEXT Context
)
{
if (Information->Type == REG_DWORD)
{
PH_STRINGREF string;
string.Buffer = PTR_ADD_OFFSET(Information, Information->DataOffset);
string.Length = Information->DataLength;
if (PhEqualStringRef(&string, &Context->BaseName->sr, TRUE))
{
Context->Found = TRUE;
return FALSE;
}
}
return TRUE;
}
/**
* Determines if a process is a system process.
*
* \param ProcessId The PID of the process to check.
*/
BOOLEAN PhIsTerminalServerSystemProcess(
_In_ HANDLE ProcessId
)
{
static CONST PH_STRINGREF keyName = PH_STRINGREF_INIT(L"System\\CurrentControlSet\\Control\\Terminal Server\\SysProcs");
PPH_STRING fileName;
HANDLE keyHandle;
if (ProcessId == SYSTEM_PROCESS_ID)
return TRUE;
if (!NT_SUCCESS(PhGetProcessImageFileNameByProcessId(ProcessId, &fileName)))
return FALSE;
PhMoveReference(&fileName, PhGetBaseName(fileName));
if (NT_SUCCESS(PhOpenKey(
&keyHandle,
KEY_READ,
PH_KEY_LOCAL_MACHINE,
&keyName,
0
)))
{
PH_IS_SYSTEM_PROCESS_CONTEXT context;
memset(&context, 0, sizeof(PH_IS_SYSTEM_PROCESS_CONTEXT));
context.BaseName = fileName;
context.Found = FALSE;
PhEnumerateValueKey(
keyHandle,
KeyValueFullInformation,
PhIsSystemProcessCallback,
&context
);
NtClose(keyHandle);
if (context.Found)
{
PhDereferenceObject(fileName);
return TRUE;
}
}
PhDereferenceObject(fileName);
return FALSE;
}
#endif
/**
* Checks if the user wants to proceed with an operation.
*
* \param WindowHandle A handle to the parent window.
* \param Verb A verb describing the action.
* \param Message A message containing additional information
* about the action.
* \param WarnOnlyIfDangerous TRUE to skip the confirmation
* dialog if none of the processes are system processes,
* FALSE to always show the confirmation dialog.
* \param Processes An array of pointers to process items.
* \param NumberOfProcesses The number of process items.
* \return TRUE if the user wants to proceed with the operation,
* otherwise FALSE.
*/
static BOOLEAN PhpShowContinueMessageProcesses(
_In_ HWND WindowHandle,
_In_ PCWSTR Verb,
_In_opt_ PCWSTR Message,
_In_ BOOLEAN WarnOnlyIfDangerous,
_In_ PPH_PROCESS_ITEM *Processes,
_In_ ULONG NumberOfProcesses
)
{
PWSTR object;
ULONG i;
BOOLEAN critical = FALSE;
BOOLEAN dangerous = FALSE;
BOOLEAN cont = FALSE;
if (NumberOfProcesses == 0)
return FALSE;
for (i = 0; i < NumberOfProcesses; i++)
{
HANDLE processHandle;
BOOLEAN breakOnTermination = FALSE;
if (PhIsDangerousProcess(Processes[i]->ProcessId))
{
critical = TRUE;
dangerous = TRUE;
break;
}
if (NT_SUCCESS(PhOpenProcess(&processHandle, PROCESS_QUERY_INFORMATION, Processes[i]->ProcessId)))
{
PhGetProcessBreakOnTermination(processHandle, &breakOnTermination);
NtClose(processHandle);
}
if (breakOnTermination)
{
critical = TRUE;
dangerous = TRUE;
break;
}
}
if (WarnOnlyIfDangerous && !dangerous)
return TRUE;
if (PhGetIntegerSetting(SETTING_ENABLE_WARNINGS))
{
if (NumberOfProcesses == 1)
{
object = Processes[0]->ProcessName->Buffer;
}
else if (NumberOfProcesses == 2)
{
object = PhaConcatStrings(
3,
Processes[0]->ProcessName->Buffer,
L" and ",
Processes[1]->ProcessName->Buffer
)->Buffer;
}
else
{
object = L"the selected processes";
}
if (!dangerous)
{
cont = PhShowConfirmMessage(
WindowHandle,
Verb,
object,
Message,
FALSE
);
}
else if (!critical)
{
cont = PhShowConfirmMessage(
WindowHandle,
Verb,
object,
PhaConcatStrings(
3,
L"You are about to ",
Verb,
L" one or more system processes."
)->Buffer,
TRUE
);
}
else
{
PPH_STRING message;
if (PhEqualStringZ(Verb, L"terminate", FALSE))
{
message = PhaConcatStrings(
3,
L"You are about to ",
Verb,
L" one or more critical processes. This will shut down the operating system immediately."
);
}
else
{
message = PhaConcatStrings(
3,
L"You are about to ",
Verb,
L" one or more critical processes."
);
}
cont = PhShowConfirmMessage(
WindowHandle,
Verb,
object,
message->Buffer,
TRUE
);
}
}
else
{
cont = TRUE;
}
return cont;
}
/**
* Shows an error message to the user and checks
* if the user wants to continue.
*
* \param WindowHandle A handle to the parent window.
* \param Verb A verb describing the action which
* resulted in an error.
* \param Process The process item which the action
* was performed on.
* \param Status A NT status value representing the
* error.
* \param Win32Result A Win32 error code representing
* the error.
*
* \return TRUE if the user wants to continue, otherwise
* FALSE. The result is typically only useful when
* executing an action on multiple processes.
*/
static BOOLEAN PhpShowErrorProcess(
_In_ HWND WindowHandle,
_In_ PCWSTR Verb,
_In_ PPH_PROCESS_ITEM Process,
_In_ NTSTATUS Status,
_In_opt_ ULONG Win32Result
)
{
if (!PH_IS_FAKE_PROCESS_ID(Process->ProcessId))
{
return PhShowContinueStatus(
WindowHandle,
PhaFormatString(
L"Unable to %s %s (PID %lu)",
Verb,
Process->ProcessName->Buffer,
HandleToUlong(Process->ProcessId)
)->Buffer,
Status,
Win32Result
);
}
else
{
return PhShowContinueStatus(
WindowHandle,
PhaFormatString(
L"Unable to %s %s",
Verb,
Process->ProcessName->Buffer
)->Buffer,
Status,
Win32Result
);
}
}
BOOLEAN PhUiTerminateProcesses(
_In_ HWND WindowHandle,
_In_ PPH_PROCESS_ITEM *Processes,
_In_ ULONG NumberOfProcesses
)
{
BOOLEAN success = TRUE;
BOOLEAN cancelled = FALSE;
ULONG i;
if (!PhpShowContinueMessageProcesses(
WindowHandle,
L"terminate",
L"Terminating a process will cause unsaved data to be lost.",
FALSE,
Processes,
NumberOfProcesses
))
return FALSE;
for (i = 0; i < NumberOfProcesses; i++)
{
NTSTATUS status;
HANDLE processHandle;
// Note: The current process is a special case (see GH#1770) (dmex)
if (Processes[i]->ProcessId == NtCurrentProcessId())
{
RtlExitUserProcess(STATUS_SUCCESS);
}
if (NT_SUCCESS(status = PhOpenProcess(
&processHandle,
PROCESS_TERMINATE,
Processes[i]->ProcessId
)))
{
// An exit status of 1 is used here for compatibility reasons:
// 1. Both Task Manager and Process Explorer use 1.
// 2. winlogon tries to restart explorer.exe if the exit status is not 1.
status = PhTerminateProcess(processHandle, 1);
if (status == STATUS_SUCCESS || status == STATUS_PROCESS_IS_TERMINATING)
PhTerminateProcess(processHandle, DBG_TERMINATE_PROCESS); // debug terminate (dmex)
NtClose(processHandle);
}
if (!NT_SUCCESS(status))
{
BOOLEAN connected;
success = FALSE;
if (!cancelled && PhpShowErrorAndConnectToPhSvc(
WindowHandle,
PhaConcatStrings2(L"Unable to terminate ", Processes[i]->ProcessName->Buffer)->Buffer,
status,
&connected,
&cancelled
))
{
if (connected)
{
if (NT_SUCCESS(status = PhSvcCallControlProcess(Processes[i]->ProcessId, PhSvcControlProcessTerminate, 0)))
success = TRUE;
else
PhpShowErrorProcess(WindowHandle, L"terminate", Processes[i], status, 0);
PhUiDisconnectFromPhSvc();
}
else
{
cancelled = TRUE;
}
}
else
{
if (cancelled)
break;
if (!PhpShowErrorProcess(WindowHandle, L"terminate", Processes[i], status, 0))
break;
}
}
}
return success;
}
BOOLEAN PhpUiTerminateTreeProcess(
_In_ HWND WindowHandle,
_In_ PPH_PROCESS_ITEM Process,
_In_ PVOID Processes,
_Inout_ PBOOLEAN Success
)
{
NTSTATUS status;
PSYSTEM_PROCESS_INFORMATION process;
HANDLE processHandle;
PPH_PROCESS_ITEM processItem;
// Note:
// FALSE should be written to Success if any part of the operation failed.
// The return value of this function indicates whether to continue with
// the operation (FALSE if user cancelled).
// Terminate the process.
if (NT_SUCCESS(status = PhOpenProcess(
&processHandle,
PROCESS_TERMINATE,
Process->ProcessId
)))
{
status = PhTerminateProcess(processHandle, 1);
if (status == STATUS_SUCCESS || status == STATUS_PROCESS_IS_TERMINATING)
PhTerminateProcess(processHandle, DBG_TERMINATE_PROCESS); // debug terminate (dmex)
NtClose(processHandle);
}
if (!NT_SUCCESS(status))
{
*Success = FALSE;
if (!PhpShowErrorProcess(WindowHandle, L"terminate", Process, status, 0))
return FALSE;
}
// Terminate the process' children.
process = PH_FIRST_PROCESS(Processes);
do
{
if (process->UniqueProcessId != Process->ProcessId &&
process->InheritedFromUniqueProcessId == Process->ProcessId)
{
if (processItem = PhReferenceProcessItem(process->UniqueProcessId))
{
if (WindowsVersion >= WINDOWS_10_RS3)
{
// Check the sequence number to make sure it is a descendant.
if (processItem->ProcessSequenceNumber >= Process->ProcessSequenceNumber)
{
if (!PhpUiTerminateTreeProcess(WindowHandle, processItem, Processes, Success))
{
PhDereferenceObject(processItem);
return FALSE;
}
}
}
else
{
// Check the creation time to make sure it is a descendant.
if (processItem->CreateTime.QuadPart >= Process->CreateTime.QuadPart)
{
if (!PhpUiTerminateTreeProcess(WindowHandle, processItem, Processes, Success))
{
PhDereferenceObject(processItem);
return FALSE;
}
}
}
PhDereferenceObject(processItem);
}
}
} while (process = PH_NEXT_PROCESS(process));
return TRUE;
}
BOOLEAN PhUiTerminateTreeProcess(
_In_ HWND WindowHandle,
_In_ PPH_PROCESS_ITEM Process
)
{
NTSTATUS status;
BOOLEAN success = TRUE;
BOOLEAN cont = FALSE;
PVOID processes;
if (PhGetIntegerSetting(SETTING_ENABLE_WARNINGS))
{
cont = PhShowConfirmMessage(
WindowHandle,
L"terminate",
PhaConcatStrings2(Process->ProcessName->Buffer, L" and its descendants")->Buffer,
L"Terminating a process tree will cause the process and its descendants to be terminated.",
FALSE
);
}
else
{
cont = TRUE;
}
if (!cont)
return FALSE;
if (!NT_SUCCESS(status = PhEnumProcesses(&processes)))
{
PhShowStatus(WindowHandle, L"Unable to enumerate processes", status, 0);
return FALSE;
}
PhpUiTerminateTreeProcess(WindowHandle, Process, processes, &success);
PhFree(processes);
return success;
}
BOOLEAN PhUiSuspendProcesses(
_In_ HWND WindowHandle,
_In_ PPH_PROCESS_ITEM *Processes,
_In_ ULONG NumberOfProcesses
)
{
BOOLEAN success = TRUE;
BOOLEAN cancelled = FALSE;
ULONG i;
if (!PhpShowContinueMessageProcesses(
WindowHandle,
L"suspend",
NULL,
TRUE,
Processes,
NumberOfProcesses
))
return FALSE;
for (i = 0; i < NumberOfProcesses; i++)
{
NTSTATUS status;
HANDLE processHandle;
if (NT_SUCCESS(status = PhOpenProcess(
&processHandle,
PROCESS_SUSPEND_RESUME,
Processes[i]->ProcessId
)))
{
status = NtSuspendProcess(processHandle);
NtClose(processHandle);
}
if (!NT_SUCCESS(status))
{
BOOLEAN connected;
success = FALSE;
if (!cancelled && PhpShowErrorAndConnectToPhSvc(
WindowHandle,
PhaConcatStrings2(L"Unable to suspend ", Processes[i]->ProcessName->Buffer)->Buffer,
status,
&connected,
&cancelled
))
{
if (connected)
{
if (NT_SUCCESS(status = PhSvcCallControlProcess(Processes[i]->ProcessId, PhSvcControlProcessSuspend, 0)))
success = TRUE;
else
PhpShowErrorProcess(WindowHandle, L"suspend", Processes[i], status, 0);
PhUiDisconnectFromPhSvc();
}
else
{
cancelled = TRUE;
}
}
else
{
if (cancelled)
break;
if (!PhpShowErrorProcess(WindowHandle, L"suspend", Processes[i], status, 0))
break;
}
}
}
return success;
}
BOOLEAN PhpUiSuspendTreeProcess(
_In_ HWND WindowHandle,
_In_ PPH_PROCESS_ITEM Process,
_In_ PVOID Processes,
_Inout_ PBOOLEAN Success
)
{
NTSTATUS status;
PSYSTEM_PROCESS_INFORMATION process;
HANDLE processHandle;
PPH_PROCESS_ITEM processItem;
// Note:
// FALSE should be written to Success if any part of the operation failed.
// The return value of this function indicates whether to continue with
// the operation (FALSE if user cancelled).
// Suspend the process.
if (NT_SUCCESS(status = PhOpenProcess(
&processHandle,
PROCESS_SUSPEND_RESUME,
Process->ProcessId
)))
{
status = NtSuspendProcess(processHandle);
NtClose(processHandle);
}
if (!NT_SUCCESS(status))
{
*Success = FALSE;
if (!PhpShowErrorProcess(WindowHandle, L"suspend", Process, status, 0))
return FALSE;
}
// Suspend the process' children.
process = PH_FIRST_PROCESS(Processes);
do
{
if (process->UniqueProcessId != Process->ProcessId &&
process->InheritedFromUniqueProcessId == Process->ProcessId)
{
if (processItem = PhReferenceProcessItem(process->UniqueProcessId))
{
if (WindowsVersion >= WINDOWS_10_RS3)
{
// Check the sequence number to make sure it is a descendant.
if (processItem->ProcessSequenceNumber >= Process->ProcessSequenceNumber)
{
if (!PhpUiSuspendTreeProcess(WindowHandle, processItem, Processes, Success))
{
PhDereferenceObject(processItem);
return FALSE;
}
}
}
else
{
// Check the creation time to make sure it is a descendant.
if (processItem->CreateTime.QuadPart >= Process->CreateTime.QuadPart)
{
if (!PhpUiSuspendTreeProcess(WindowHandle, processItem, Processes, Success))
{
PhDereferenceObject(processItem);
return FALSE;
}
}
}
PhDereferenceObject(processItem);
}
}
} while (process = PH_NEXT_PROCESS(process));
return TRUE;
}
BOOLEAN PhUiSuspendTreeProcess(
_In_ HWND WindowHandle,
_In_ PPH_PROCESS_ITEM Process
)
{
NTSTATUS status;
BOOLEAN result;
PVOID processes;
if (PhGetIntegerSetting(SETTING_ENABLE_WARNINGS))
{
result = PhShowConfirmMessage(
WindowHandle,
L"suspend",
PhaConcatStrings2(Process->ProcessName->Buffer, L" and its descendants")->Buffer,
L"Suspending a process tree will cause the process and its descendants to be suspend.",
FALSE
);
}
else
{
result = TRUE;
}
if (!result)
return FALSE;
if (!NT_SUCCESS(status = PhEnumProcesses(&processes)))
{
PhShowStatus(WindowHandle, L"Unable to enumerate processes", status, 0);
return FALSE;
}
PhpUiSuspendTreeProcess(WindowHandle, Process, processes, &result);
PhFree(processes);
return result;
}
BOOLEAN PhUiResumeProcesses(
_In_ HWND WindowHandle,
_In_ PPH_PROCESS_ITEM *Processes,
_In_ ULONG NumberOfProcesses
)
{
BOOLEAN success = TRUE;
BOOLEAN cancelled = FALSE;
ULONG i;
if (!PhpShowContinueMessageProcesses(
WindowHandle,
L"resume",
NULL,
TRUE,
Processes,
NumberOfProcesses
))
return FALSE;
for (i = 0; i < NumberOfProcesses; i++)
{
NTSTATUS status;
HANDLE processHandle;
if (NT_SUCCESS(status = PhOpenProcess(
&processHandle,
PROCESS_SUSPEND_RESUME,
Processes[i]->ProcessId
)))
{
status = NtResumeProcess(processHandle);
NtClose(processHandle);
}
if (!NT_SUCCESS(status))
{
BOOLEAN connected;
success = FALSE;
if (!cancelled && PhpShowErrorAndConnectToPhSvc(
WindowHandle,
PhaConcatStrings2(L"Unable to resume ", Processes[i]->ProcessName->Buffer)->Buffer,
status,
&connected,
&cancelled
))
{
if (connected)
{
if (NT_SUCCESS(status = PhSvcCallControlProcess(Processes[i]->ProcessId, PhSvcControlProcessResume, 0)))
success = TRUE;
else
PhpShowErrorProcess(WindowHandle, L"resume", Processes[i], status, 0);
PhUiDisconnectFromPhSvc();
}
else
{
cancelled = TRUE;
}
}
else
{
if (cancelled)
break;
if (!PhpShowErrorProcess(WindowHandle, L"resume", Processes[i], status, 0))
break;
}
}
}
return success;
}
BOOLEAN PhpUiResumeTreeProcess(
_In_ HWND WindowHandle,
_In_ PPH_PROCESS_ITEM Process,
_In_ PVOID Processes,
_Inout_ PBOOLEAN Success
)
{
NTSTATUS status;
PSYSTEM_PROCESS_INFORMATION process;
HANDLE processHandle;
PPH_PROCESS_ITEM processItem;
// Note:
// FALSE should be written to Success if any part of the operation failed.
// The return value of this function indicates whether to continue with
// the operation (FALSE if user cancelled).
// Resume the process.
if (NT_SUCCESS(status = PhOpenProcess(
&processHandle,
PROCESS_SUSPEND_RESUME,
Process->ProcessId
)))
{
status = NtResumeProcess(processHandle);
NtClose(processHandle);
}
if (!NT_SUCCESS(status))
{
*Success = FALSE;
if (!PhpShowErrorProcess(WindowHandle, L"resume", Process, status, 0))
return FALSE;
}
// Resume the process' children.
process = PH_FIRST_PROCESS(Processes);
do
{
if (process->UniqueProcessId != Process->ProcessId &&
process->InheritedFromUniqueProcessId == Process->ProcessId)
{
if (processItem = PhReferenceProcessItem(process->UniqueProcessId))
{
if (WindowsVersion >= WINDOWS_10_RS3)
{
// Check the sequence number to make sure it is a descendant.
if (processItem->ProcessSequenceNumber >= Process->ProcessSequenceNumber)
{
if (!PhpUiResumeTreeProcess(WindowHandle, processItem, Processes, Success))
{
PhDereferenceObject(processItem);
return FALSE;
}
}
}
else
{
// Check the creation time to make sure it is a descendant.
if (processItem->CreateTime.QuadPart >= Process->CreateTime.QuadPart)
{
if (!PhpUiResumeTreeProcess(WindowHandle, processItem, Processes, Success))
{
PhDereferenceObject(processItem);
return FALSE;
}
}
}
PhDereferenceObject(processItem);
}
}
} while (process = PH_NEXT_PROCESS(process));
return TRUE;
}
BOOLEAN PhUiResumeTreeProcess(
_In_ HWND WindowHandle,
_In_ PPH_PROCESS_ITEM Process
)
{
NTSTATUS status;
BOOLEAN result;
PVOID processes;
if (PhGetIntegerSetting(SETTING_ENABLE_WARNINGS))
{
result = PhShowConfirmMessage(
WindowHandle,
L"resume",
PhConcatStringRefZ(&Process->ProcessName->sr, L" and its descendants")->Buffer,
L"Resuming a process tree will cause the process and its descendants to be resumed.",
FALSE
);
}
else
{
result = TRUE;
}
if (!result)
return FALSE;
if (!NT_SUCCESS(status = PhEnumProcesses(&processes)))
{
PhShowStatus(WindowHandle, L"Unable to enumerate processes", status, 0);
return FALSE;
}
PhpUiResumeTreeProcess(WindowHandle, Process, processes, &result);
PhFree(processes);
return result;
}
BOOLEAN PhUiFreezeTreeProcess(
_In_ HWND WindowHandle,
_In_ PPH_PROCESS_ITEM Process
)
{
NTSTATUS status;
BOOLEAN result = FALSE;
HANDLE freezeHandle;
if (ReadPointerAcquire(&Process->FreezeHandle))
return FALSE;
if (PhGetIntegerSetting(SETTING_ENABLE_WARNINGS))
{
result = PhShowConfirmMessage(
WindowHandle,
L"freeze",
Process->ProcessName->Buffer,
L"Freezing does not persist after exiting System Informer.",
FALSE
);
}
else
{
result = TRUE;
}
if (!result)
return FALSE;
status = PhFreezeProcessById(
&freezeHandle,
Process->ProcessId
);
if (!NT_SUCCESS(status))
{
PhpShowErrorProcess(WindowHandle, L"freeze", Process, status, 0);
return FALSE;
}
if (freezeHandle = InterlockedExchangePointer(&Process->FreezeHandle, freezeHandle))
{
NtClose(freezeHandle);
}
return TRUE;
}
BOOLEAN PhUiThawTreeProcess(
_In_ HWND WindowHandle,
_In_ PPH_PROCESS_ITEM Process
)
{
NTSTATUS status;
HANDLE freezeHandle;
if (!ReadPointerAcquire(&Process->FreezeHandle))
return FALSE;
status = PhThawProcessById(
Process->FreezeHandle,
Process->ProcessId
);
if (!NT_SUCCESS(status))
{
PhpShowErrorProcess(WindowHandle, L"thaw", Process, status, 0);
return FALSE;
}
if (freezeHandle = InterlockedExchangePointer(&Process->FreezeHandle, NULL))
{
NtClose(freezeHandle);
}
return TRUE;
}
BOOLEAN PhUiRestartProcess(
_In_ HWND WindowHandle,
_In_ PPH_PROCESS_ITEM Process
)
{
NTSTATUS status;
BOOLEAN result = FALSE;
BOOLEAN elevated = !!PhGetOwnTokenAttributes().Elevated;
BOOLEAN tokenIsStronglyNamed = FALSE;
BOOLEAN tokenIsUIAccessEnabled = FALSE;
BOOLEAN tokenRevertImpersonation = FALSE;
HANDLE processHandle = NULL;
HANDLE newProcessHandle = NULL;
HANDLE tokenHandle = NULL;
PPH_STRING fileNameWin32 = NULL;
PPH_STRING commandLine = NULL;
PPH_STRING currentDirectory = NULL;
STARTUPINFOEX startupInfo = { 0 };
PSECURITY_DESCRIPTOR processSecurityDescriptor = NULL;
PSECURITY_DESCRIPTOR tokenSecurityDescriptor = NULL;
PPROC_THREAD_ATTRIBUTE_LIST attributeList = NULL;
BOOLEAN environmentAllocated = FALSE;
PVOID environmentBuffer = NULL;
ULONG environmentLength;
if (PhGetIntegerSetting(SETTING_ENABLE_WARNINGS))
{
result = PhShowConfirmMessage(
WindowHandle,
L"restart",
Process->ProcessName->Buffer,
L"The process will be restarted with the same command line, "
L"working directory and privileges.",
FALSE
);
}
else
{
result = TRUE;
}
if (!result)
return FALSE;
// Fail when restarting the current process otherwise
// we get terminated before creating the new process. (dmex)
if (Process->ProcessId == NtCurrentProcessId())
return FALSE;
// Special handling for the current shell process. (dmex)
{
CLIENT_ID shellClientId;
if (NT_SUCCESS(PhGetWindowClientId(PhGetShellWindow(), &shellClientId)))
{
if (Process->ProcessId == shellClientId.UniqueProcess)
{
if (NT_SUCCESS(PhOpenProcess(
&processHandle,
PROCESS_TERMINATE,
Process->ProcessId
)))
{
status = PhTerminateProcess(
processHandle,
STATUS_SUCCESS
);
NtClose(processHandle);
if (NT_SUCCESS(status))
goto CleanupExit;
}
}
}
}
fileNameWin32 = Process->FileName ? PhGetFileName(Process->FileName) : NULL;
if (PhIsNullOrEmptyString(fileNameWin32) || !PhDoesFileExistWin32(PhGetString(fileNameWin32)))
{
status = STATUS_NO_SUCH_FILE;
goto CleanupExit;
}
// Open the process and get the command line and current directory.
if (!NT_SUCCESS(status = PhOpenProcess(
&processHandle,
PROCESS_QUERY_LIMITED_INFORMATION | PROCESS_VM_READ,
Process->ProcessId
)))
goto CleanupExit;
if (!NT_SUCCESS(status = PhGetProcessCurrentDirectory(
processHandle,
!!Process->IsWow64Process,
¤tDirectory
)))
goto CleanupExit;
if (!NT_SUCCESS(status = PhGetProcessCommandLine(
processHandle,
&commandLine
)))
goto CleanupExit;
if (!NT_SUCCESS(status = PhGetProcessEnvironment(
processHandle,
!!Process->IsWow64Process,
&environmentBuffer,
&environmentLength
)))
goto CleanupExit;
NtClose(processHandle);
processHandle = NULL;
// Start the process.
//
// Use the existing process as the parent, and restarting the process will inherit most of the process configuration from itself (dmex)
status = PhOpenProcess(
&processHandle,
PROCESS_CREATE_PROCESS | PROCESS_QUERY_LIMITED_INFORMATION | PROCESS_TERMINATE | (elevated ? READ_CONTROL : 0),
Process->ProcessId
);
if (!NT_SUCCESS(status))
goto CleanupExit;
status = PhInitializeProcThreadAttributeList(&attributeList, 1);
if (!NT_SUCCESS(status))
goto CleanupExit;
status = PhUpdateProcThreadAttribute(
attributeList,
PROC_THREAD_ATTRIBUTE_PARENT_PROCESS,
&processHandle,
sizeof(HANDLE)
);
if (!NT_SUCCESS(status))
goto CleanupExit;
status = PhOpenProcessToken(
processHandle,
TOKEN_ASSIGN_PRIMARY | TOKEN_DUPLICATE | TOKEN_IMPERSONATE | TOKEN_QUERY | (elevated ? READ_CONTROL : 0),
&tokenHandle
);
if (NT_SUCCESS(status))
{
status = PhGetTokenUIAccess(tokenHandle, &tokenIsUIAccessEnabled);
if (!NT_SUCCESS(status))
goto CleanupExit;
}
else
{
status = PhOpenProcessToken(
processHandle,
TOKEN_QUERY | (elevated ? READ_CONTROL : 0),
&tokenHandle
);
if (!NT_SUCCESS(status))
goto CleanupExit;
}
if (elevated)
{
PhGetObjectSecurity(
processHandle,
OWNER_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION | LABEL_SECURITY_INFORMATION,
&processSecurityDescriptor
);
PhGetObjectSecurity(
tokenHandle,
OWNER_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION | LABEL_SECURITY_INFORMATION,
&tokenSecurityDescriptor
);
}
if (!environmentBuffer)
{
if (NT_SUCCESS(PhCreateEnvironmentBlock(&environmentBuffer, tokenHandle, FALSE)))
{
environmentAllocated = TRUE;
}
}
if (NT_SUCCESS(PhGetProcessIsStronglyNamed(processHandle, &tokenIsStronglyNamed)) && tokenIsStronglyNamed)
{
tokenRevertImpersonation = NT_SUCCESS(PhImpersonateToken(NtCurrentThread(), tokenHandle));
}
memset(&startupInfo, 0, sizeof(STARTUPINFOEX));
startupInfo.StartupInfo.cb = sizeof(STARTUPINFOEX);
startupInfo.StartupInfo.dwFlags = STARTF_USESHOWWINDOW;
startupInfo.StartupInfo.wShowWindow = SW_SHOWNORMAL;
startupInfo.lpAttributeList = attributeList;
status = PhCreateProcessWin32Ex(
PhGetString(fileNameWin32),
PhGetString(commandLine),
environmentBuffer,
PhGetString(currentDirectory),
&startupInfo,
PH_CREATE_PROCESS_SUSPENDED | PH_CREATE_PROCESS_NEW_CONSOLE | PH_CREATE_PROCESS_EXTENDED_STARTUPINFO |
PH_CREATE_PROCESS_DEFAULT_ERROR_MODE | PH_CREATE_PROCESS_UNICODE_ENVIRONMENT,
tokenHandle,
NULL,
&newProcessHandle,
NULL
);
if (!NT_SUCCESS(status)) // Try without the token (dmex)
{
status = PhCreateProcessWin32Ex(
PhGetString(fileNameWin32),
PhGetString(commandLine),
environmentBuffer,
PhGetString(currentDirectory),
&startupInfo,
PH_CREATE_PROCESS_SUSPENDED | PH_CREATE_PROCESS_NEW_CONSOLE | PH_CREATE_PROCESS_EXTENDED_STARTUPINFO |
PH_CREATE_PROCESS_DEFAULT_ERROR_MODE | PH_CREATE_PROCESS_UNICODE_ENVIRONMENT,
NULL,
NULL,
&newProcessHandle,
NULL
);
}
if (!NT_SUCCESS(status) && tokenIsUIAccessEnabled) // Try UIAccess (dmex)
{
status = PhShellExecuteEx(
WindowHandle,
PhGetString(fileNameWin32),
PhGetString(commandLine),
PhGetString(currentDirectory),
SW_SHOW,
PH_SHELL_EXECUTE_DEFAULT,
0,
&newProcessHandle
);
}
if (tokenRevertImpersonation)
{
PhRevertImpersonationToken(NtCurrentThread());
}
if (NT_SUCCESS(status))
{
// See runas.c for a description of the Windows issue with PROC_THREAD_ATTRIBUTE_PARENT_PROCESS
// requiring the reset of the security descriptor. (dmex)
if (elevated && !tokenIsUIAccessEnabled) // Skip processes with UIAccess (dmex)
{
HANDLE tokenWriteHandle = NULL;
if (processSecurityDescriptor)
{
PhSetObjectSecurity(
newProcessHandle,
OWNER_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION | LABEL_SECURITY_INFORMATION,
processSecurityDescriptor
);
}
if (tokenSecurityDescriptor && NT_SUCCESS(PhOpenProcessToken(
newProcessHandle,
WRITE_DAC | WRITE_OWNER,
&tokenWriteHandle
)))
{
PhSetObjectSecurity(
tokenWriteHandle,
OWNER_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION | LABEL_SECURITY_INFORMATION,
tokenSecurityDescriptor
);
NtClose(tokenWriteHandle);
}
}
// Terminate the existing process.
PhTerminateProcess(processHandle, STATUS_SUCCESS);
// Update the console foreground.
PhConsoleSetForeground(newProcessHandle, TRUE);
// Resume the new process.
NtResumeProcess(newProcessHandle);
}
CleanupExit:
if (tokenHandle)
{
NtClose(tokenHandle);
}
if (newProcessHandle)
{
NtClose(newProcessHandle);
}
if (processHandle)
{
NtClose(processHandle);
}
if (attributeList)
{
PhDeleteProcThreadAttributeList(attributeList);
}
if (environmentBuffer && environmentAllocated)
{
PhDestroyEnvironmentBlock(environmentBuffer);
}
if (tokenSecurityDescriptor)
{
PhFree(tokenSecurityDescriptor);
}
if (processSecurityDescriptor)
{
PhFree(processSecurityDescriptor);
}
if (currentDirectory)
{
PhDereferenceObject(currentDirectory);
}
if (commandLine)
{
PhDereferenceObject(commandLine);
}
if (fileNameWin32)
{
PhDereferenceObject(fileNameWin32);
}
if (!NT_SUCCESS(status))
{
PhpShowErrorProcess(WindowHandle, L"restart", Process, status, 0);
return FALSE;
}
return TRUE;
}
static PPH_STRING PhFindDebuggerPath(
_In_ PCWSTR DebuggerName
)
{
static PH_STRINGREF windowsKitsKeyName = PH_STRINGREF_INIT(L"Software\\Microsoft\\Windows Kits\\Installed Roots");
PPH_STRING debuggerPath = NULL;
HANDLE keyHandle;
PPH_STRING kitsRoot;
if (NT_SUCCESS(PhOpenKey(
&keyHandle,
KEY_READ,
PH_KEY_LOCAL_MACHINE,
&windowsKitsKeyName,
0
)))
{
if (kitsRoot = PhQueryRegistryStringZ(keyHandle, L"KitsRoot10"))
{
PPH_STRING testPath;
#ifdef _WIN64
testPath = PhConcatStringRefZ(&kitsRoot->sr, L"Debuggers\\x64\\");
#else
testPath = PhConcatStringRefZ(&kitsRoot->sr, L"Debuggers\\x86\\");
#endif
PhMoveReference(&testPath, PhConcatStringRefZ(&testPath->sr, DebuggerName));
if (PhDoesFileExistWin32(PhGetString(testPath)))
{
debuggerPath = testPath;
}
else
{
PhDereferenceObject(testPath);
}
PhDereferenceObject(kitsRoot);
}
NtClose(keyHandle);
}
if (PhIsNullOrEmptyString(debuggerPath))
{
PhMoveReference(&debuggerPath, PhSearchFilePath(DebuggerName, L".exe"));
}
return debuggerPath;
}
//static PPH_STRING PhFindVisualStudioDebugger(
// _In_ PCPH_STRINGREF VersionRange
// )
//{
// static const PH_STRINGREF vswhere = PH_STRINGREF_INIT(L"%ProgramFiles(x86)%\\Microsoft Visual Studio\\Installer\\vswhere.exe");
// static const PH_STRINGREF trimSet = PH_STRINGREF_INIT(L"\r\n");
// static const PH_STRINGREF args01 = PH_STRINGREF_INIT(L" -prerelease -version ");
// static const PH_STRINGREF args02 = PH_STRINGREF_INIT(L" -property installationPath ");
// NTSTATUS status;
// PPH_STRING expandedfileName = NULL;
// PPH_STRING expandedCommand = NULL;
// PPH_STRING devenvPath = NULL;
// PPH_STRING output = NULL;
//
// if (!(expandedfileName = PhExpandEnvironmentStrings(&vswhere)))
// return NULL;
//
// if (!PhDoesFileExistWin32(PhGetString(expandedfileName)))
// {
// PhDereferenceObject(expandedfileName);
// return NULL;
// }
//
// // vswhere.exe -prerelease -version %s -property installationPath
// expandedCommand = PhConcatStringRef3(
// &args01,
// VersionRange,
// &args02
// );
//
// status = PhCreateProcessRedirection(
// &expandedfileName->sr,
// &expandedCommand->sr,
// NULL,
// &output
// );
//
// if (!NT_SUCCESS(status) || PhIsNullOrEmptyString(output))
// goto CleanupExit;
//
// PhTrimStringRef(
// &output->sr,
// &trimSet,
// PH_TRIM_END_ONLY
// );
//
// devenvPath = PhConcatStringRefZ(&output->sr, L"\\Common7\\IDE\\devenv.exe");
//
// if (PhDoesFileExistWin32(devenvPath->Buffer))
// {
// PhClearReference(&output);
// PhClearReference(&expandedCommand);
// PhClearReference(&expandedfileName);
//
// return devenvPath;
// }
//
//CleanupExit:
// PhClearReference(&output);
// PhClearReference(&devenvPath);
// PhClearReference(&expandedCommand);
// PhClearReference(&expandedfileName);
// return NULL;
//}
/**
* Launch a system debugger attached to the specified process.
*
* \param WindowHandle: The parent HWND for dialogs (Task Dialogs, errors, etc.).
* \param Process A pointer to a `PPH_PROCESS_ITEM` describing the target process.
* \return BOOLEAN: TRUE on success (debugger launched), FALSE on cancel or failure.
*/
BOOLEAN PhUiDebugProcess(
_In_ HWND WindowHandle,
_In_ PPH_PROCESS_ITEM Process
)
{
static CONST PH_STRINGREF aeDebugKeyName = PH_STRINGREF_INIT(L"Software\\Microsoft\\Windows NT\\CurrentVersion\\AeDebug");
#ifdef _WIN64
static CONST PH_STRINGREF aeDebugWow64KeyName = PH_STRINGREF_INIT(L"Software\\Wow6432Node\\Microsoft\\Windows NT\\CurrentVersion\\AeDebug");
#endif
NTSTATUS status;
PPH_STRING commandLine = NULL;
PPH_STRING debuggerCommand = NULL;
PH_STRING_BUILDER commandLineBuilder;
HANDLE keyHandle;
PPH_STRING debugger;
PH_STRINGREF commandPart;
PH_STRINGREF dummy;
LONG debuggerChoice = 0;
PPH_STRING registryDebuggerPath = NULL;
PPH_STRING registryDebuggerName = NULL;
//if (PhGetIntegerSetting(SETTING_ENABLE_WARNINGS))
//{
// result = PhShowConfirmMessage(
// WindowHandle,
// L"debug",
// Process->ProcessName->Buffer,
// L"Debugging a process may result in loss of data.",
// FALSE
// );
//}
//else
//{
// result = TRUE;
//}
//
//if (!result)
// return FALSE;
status = PhOpenKey(
&keyHandle,
KEY_READ,
PH_KEY_LOCAL_MACHINE,
#ifdef _WIN64
Process->IsWow64Process ? &aeDebugWow64KeyName : &aeDebugKeyName,
#else
&aeDebugKeyName,
#endif
0
);
if (NT_SUCCESS(status))
{
if (debugger = PhQueryRegistryStringZ(keyHandle, L"Debugger"))
{
if (PhSplitStringRefAtChar(&debugger->sr, L'"', &dummy, &commandPart) &&
PhSplitStringRefAtChar(&commandPart, L'"', &commandPart, &dummy))
{
registryDebuggerPath = PhCreateString2(&commandPart);
registryDebuggerName = PhGetFileName(registryDebuggerPath);
}
PhDereferenceObject(debugger);
}
NtClose(keyHandle);
}
// Check for available debuggers and show selection dialog
{
PPH_STRING windbgPath = NULL;
PPH_STRING windbgPreviewPath = NULL;
//PPH_STRING vs2026Path = NULL;
//PPH_STRING vs2022Path = NULL;
PPH_STRING cdbPath = NULL;
PPH_STRING kdPath = NULL;
PPH_STRING ntsdPath = NULL;
TASKDIALOGCONFIG config;
TASKDIALOG_BUTTON buttons[11];
ULONG buttonCount = 0;
PPH_STRING registryButtonText = NULL;
if (windbgPath = PhFindDebuggerPath(L"windbg.exe"))
buttons[buttonCount++] = (TASKDIALOG_BUTTON){ 101, L"\U0001FA9F WinDbg\nGraphical debugger for both user-mode and kernel-mode debugging." };
if (windbgPreviewPath = PhFindDebuggerPath(L"windbgx.exe"))
buttons[buttonCount++] = (TASKDIALOG_BUTTON){ 102, L"\U0001FA9F WinDbg (Preview)\nModern graphical debugger for both user-mode and kernel-mode debugging." };
//if (vs2026Path = PhFindVisualStudioDebugger(SREF(L"[18.0,19.0)")))
// buttons[buttonCount++] = (TASKDIALOG_BUTTON){ 107, L"\U0001F4D8 Visual Studio 2026\nFull-featured IDE with integrated debugging." };
//if (vs2022Path = PhFindVisualStudioDebugger(SREF(L"[16.0,17.0)")))
// buttons[buttonCount++] = (TASKDIALOG_BUTTON){ 108, L"\U0001F4D8 Visual Studio 2022\nFull-featured IDE with integrated debugging." };
if (cdbPath = PhFindDebuggerPath(L"cdb.exe"))
buttons[buttonCount++] = (TASKDIALOG_BUTTON){ 103, L"\U0001F4FA CDB\nCommand-line debugger for user-mode applications." };
if (kdPath = PhFindDebuggerPath(L"kd.exe"))
buttons[buttonCount++] = (TASKDIALOG_BUTTON){ 104, L"\U0001F4FA KD\nKernel debugger for low-level system debugging." };
if (ntsdPath = PhFindDebuggerPath(L"ntsd.exe"))
buttons[buttonCount++] = (TASKDIALOG_BUTTON){ 105, L"\U0001F4FA NTSD\nLegacy command-line debugger similar to CDB." };
// Always add registry debugger option
if (registryDebuggerPath && registryDebuggerName)
{
registryButtonText = PhFormatString(
L"\U00002699 (System Default)\n%s",
registryDebuggerName->Buffer,
registryDebuggerPath->Buffer
);
buttons[buttonCount++] = (TASKDIALOG_BUTTON){ 106, registryButtonText->Buffer };
}
else
{
buttons[buttonCount++] = (TASKDIALOG_BUTTON){ 106, L"\U00002699 System Default\nNo debugger configured in AeDebug registry key." };
}
memset(&config, 0, sizeof(TASKDIALOGCONFIG));
config.cbSize = sizeof(TASKDIALOGCONFIG);
config.hwndParent = WindowHandle;
config.dwFlags = TDF_USE_HICON_MAIN | TDF_ALLOW_DIALOG_CANCELLATION | TDF_USE_COMMAND_LINKS | TDF_POSITION_RELATIVE_TO_WINDOW | TDF_CAN_BE_MINIMIZED;
config.hMainIcon = PhGetApplicationIcon(FALSE);
config.dwCommonButtons = TDCBF_CANCEL_BUTTON;
config.pszWindowTitle = PhApplicationName;
config.pszMainInstruction = L"Select a system debugger to use for this process:";
config.pszContent = L"You can choose from the installed debugging tools below.";
config.cButtons = buttonCount;
config.pButtons = buttons;
if (PhShowTaskDialog(&config, &debuggerChoice, NULL, NULL) && debuggerChoice != 0)
{
switch (debuggerChoice)
{
case 101:
debuggerCommand = windbgPath;
windbgPath = NULL;
break;
case 102:
debuggerCommand = windbgPreviewPath;
windbgPreviewPath = NULL;
break;
case 103:
debuggerCommand = cdbPath;
cdbPath = NULL;
break;
case 104:
debuggerCommand = kdPath;
kdPath = NULL;
break;
case 105:
debuggerCommand = ntsdPath;
ntsdPath = NULL;
break;
case 106:
debuggerCommand = registryDebuggerPath;
registryDebuggerPath = NULL;
break;
//case 107:
// debuggerCommand = vs2026Path;
// vs2026Path = NULL;
// break;
//case 108:
// debuggerCommand = vs2022Path;
// vs2022Path = NULL;
// break;
}
}
PhClearReference(&windbgPath);
PhClearReference(&windbgPreviewPath);
//PhClearReference(&vs2026Path);
//PhClearReference(&vs2022Path);
PhClearReference(&cdbPath);
PhClearReference(&kdPath);
PhClearReference(&ntsdPath);
PhClearReference(®istryButtonText);
}
PhClearReference(®istryDebuggerName);
PhClearReference(®istryDebuggerPath);
if (!debuggerCommand || debuggerChoice == 0)
{
// User cancelled
return FALSE;
}
PhInitializeStringBuilder(&commandLineBuilder, debuggerCommand->Length + 30);
PhAppendCharStringBuilder(&commandLineBuilder, L'"');
PhAppendStringBuilder(&commandLineBuilder, &debuggerCommand->sr);
PhAppendCharStringBuilder(&commandLineBuilder, L'"');
switch (debuggerChoice)
{
case 101:
case 102:
case 103:
case 104:
case 105:
case 106:
PhAppendFormatStringBuilder(&commandLineBuilder, L" -p %lu", HandleToUlong(Process->ProcessId));
break;
case 107:
case 108:
PhAppendFormatStringBuilder(&commandLineBuilder, L" /JITDebug /JITDebugParam %lx", HandleToUlong(Process->ProcessId));
break;
}
commandLine = PhFinalStringBuilderString(&commandLineBuilder);
status = PhCreateProcessWin32(
NULL,
PhGetString(commandLine),
NULL,
NULL,
0,
NULL,
NULL,
NULL
);
PhDeleteStringBuilder(&commandLineBuilder);
PhDereferenceObject(debuggerCommand);
if (!NT_SUCCESS(status))
{
PhpShowErrorProcess(WindowHandle, L"debug", Process, status, 0);
return FALSE;
}
return TRUE;
}
/**
* Reduce the working set of a list of processes.
*
* \param WindowHandle Parent window used for error reporting.
* \param Processes Array of process items to operate on.
* \param NumberOfProcesses Number of processes in the array.
* \return BOOLEAN TRUE if the operation succeeded for all processes, FALSE if one or more failed.
*/
BOOLEAN PhUiReduceWorkingSetProcesses(
_In_ HWND WindowHandle,
_In_ CONST PPH_PROCESS_ITEM *Processes,
_In_ ULONG NumberOfProcesses
)
{
BOOLEAN success = TRUE;
ULONG i;
for (i = 0; i < NumberOfProcesses; i++)
{
NTSTATUS status;
HANDLE processHandle;
status = PhOpenProcess(
&processHandle,
PROCESS_SET_QUOTA,
Processes[i]->ProcessId
);
if ((status == STATUS_ACCESS_DENIED) && (KsiLevel() == KphLevelMax))
{
status = PhOpenProcess(
&processHandle,
PROCESS_QUERY_LIMITED_INFORMATION, // HACK for KphProcessEmptyWorkingSet (dmex)
Processes[i]->ProcessId
);
}
if (NT_SUCCESS(status))
{
status = PhSetProcessEmptyWorkingSet(processHandle);
NtClose(processHandle);
}
if (!NT_SUCCESS(status))
{
success = FALSE;
if (!PhpShowErrorProcess(WindowHandle, L"reduce the working set of", Processes[i], status, 0))
break;
}
}
return success;
}
/**
* Empty the working set of a list of processes.
*
* \param WindowHandle Parent window used for error reporting.
* \param Processes Array of process items to operate on.
* \param NumberOfProcesses Number of processes in the array.
* \return BOOLEAN TRUE if the operation succeeded for all processes, FALSE if one or more failed.
*/
BOOLEAN PhUiSetEmptyWorkingSetProcesses(
_In_ HWND WindowHandle,
_In_ CONST PPH_PROCESS_ITEM* Processes,
_In_ ULONG NumberOfProcesses
)
{
BOOLEAN success = TRUE;
ULONG i;
for (i = 0; i < NumberOfProcesses; i++)
{
NTSTATUS status;
HANDLE processHandle;
status = PhOpenProcess(
&processHandle,
PROCESS_VM_OPERATION,
Processes[i]->ProcessId
);
if (NT_SUCCESS(status))
{
status = PhSetProcessWorkingSetEmpty(processHandle);
NtClose(processHandle);
}
if (!NT_SUCCESS(status))
{
success = FALSE;
if (!PhpShowErrorProcess(WindowHandle, L"empty the working set of", Processes[i], status, 0))
break;
}
}
return success;
}
/**
* Configure activity moderation (Eco/foreground throttling) for a process.
*
* \param WindowHandle Parent window for dialogs.
* \param Process The process item to configure.
* \return BOOLEAN TRUE on success, FALSE on failure.
*/
BOOLEAN PhUiSetActivityModeration(
_In_ HWND WindowHandle,
_In_ PPH_PROCESS_ITEM Process
)
{
static CONST TASKDIALOG_BUTTON TaskDialogRadioButtonArray[] =
{
{ SystemActivityModerationStateSystemManaged, L"System managed" },
{ SystemActivityModerationStateUserManagedAllowThrottling, L"Allow activity moderation throttling" },
{ SystemActivityModerationStateUserManagedDisableThrottling, L"Disable activity moderation throttling" },
};
static CONST TASKDIALOG_BUTTON TaskDialogButtonArray[] =
{
{ IDYES, L"Save" },
{ IDCANCEL, L"Cancel" },
};
NTSTATUS status;
SYSTEM_ACTIVITY_MODERATION_APP_SETTINGS activityModerationInfo = { 0 };
TASKDIALOGCONFIG config;
ULONG buttonId;
ULONG moderationState;
LARGE_INTEGER startTime;
LARGE_INTEGER currentTime;
SYSTEMTIME startTimeFields;
PPH_STRING startTimeRelativeString = NULL;
PPH_STRING startTimeString = NULL;
memset(&config, 0, sizeof(TASKDIALOGCONFIG));
config.cbSize = sizeof(TASKDIALOGCONFIG);
config.dwFlags = TDF_USE_HICON_MAIN | TDF_ALLOW_DIALOG_CANCELLATION | TDF_CAN_BE_MINIMIZED | TDF_POSITION_RELATIVE_TO_WINDOW;
config.hMainIcon = PhGetApplicationIcon(FALSE);
config.pszWindowTitle = PhApplicationName;
config.pszMainInstruction = L"Select the process activity moderation throttling state.";
config.nDefaultButton = IDCANCEL;
config.pRadioButtons = TaskDialogRadioButtonArray;
config.cRadioButtons = RTL_NUMBER_OF(TaskDialogRadioButtonArray);
config.pButtons = TaskDialogButtonArray;
config.cButtons = RTL_NUMBER_OF(TaskDialogButtonArray);
config.hwndParent = WindowHandle;
config.cxWidth = 220;
if (PhIsNullOrEmptyString(Process->FileName))
return TRUE;
status = PhGetProcessActivityModerationState(
&Process->FileName->sr,
&activityModerationInfo
);
if (NT_SUCCESS(status))
{
config.nDefaultRadioButton = activityModerationInfo.ModerationState;
PhQuerySystemTime(¤tTime);
if (activityModerationInfo.LastUpdatedTime.QuadPart < currentTime.QuadPart)
{
startTime = activityModerationInfo.LastUpdatedTime;
startTimeRelativeString = PH_AUTO(PhFormatTimeSpanRelative(currentTime.QuadPart - startTime.QuadPart));
PhLargeIntegerToLocalSystemTime(&startTimeFields, &startTime);
startTimeString = PhaFormatDateTime(&startTimeFields);
}
}
else
{
config.nDefaultRadioButton = SystemActivityModerationStateSystemManaged;
}
config.pszContent = PhaFormatString(
L"System-managed activity moderation settings are automatically removed by Windows when the executable is deleted or was last executed more than 7 days ago.\r\n\r\n"
L"Image: %s\r\nUpdated: %s",
PH_AUTO_T(PH_STRING, PhGetBaseName(Process->FileName))->Buffer,
(startTimeRelativeString && startTimeString) ? PhaFormatString(L"%s ago (%s)", PhGetString(startTimeRelativeString), PhGetString(startTimeString))->Buffer : L"N/A"
)->Buffer;
if (PhShowTaskDialog(
&config,
&buttonId,
&moderationState,
NULL
) && buttonId == IDYES)
{
if (Process->IsPackagedProcess)
{
status = PhSetProcessActivityModerationState(
&Process->FileName->sr,
SystemActivityModerationAppTypePackaged,
moderationState
);
}
else
{
status = PhSetProcessActivityModerationState(
&Process->FileName->sr,
SystemActivityModerationAppTypeClassic,
moderationState
);
}
}
else
{
status = STATUS_SUCCESS;
}
if (!NT_SUCCESS(status))
{
PhpShowErrorProcess(WindowHandle, L"set background activity moderation for", Process, status, 0);
return FALSE;
}
return TRUE;
}
/**
* Enable or disable token virtualization for the specified process.
*
* \param WindowHandle Parent window used for confirmation and error UI.
* \param Process The process item to operate on.
* \param Enable TRUE to enable virtualization, FALSE to disable.
* \return BOOLEAN TRUE on success, FALSE on failure.
*/
BOOLEAN PhUiSetVirtualizationProcess(
_In_ HWND WindowHandle,
_In_ PPH_PROCESS_ITEM Process,
_In_ BOOLEAN Enable
)
{
NTSTATUS status;
BOOLEAN cont = FALSE;
HANDLE processHandle;
HANDLE tokenHandle;
if (PhGetIntegerSetting(SETTING_ENABLE_WARNINGS))
{
cont = PhShowConfirmMessage(
WindowHandle,
L"set",
L"virtualization for the process",
L"Enabling or disabling virtualization for a process may "
L"alter its functionality and produce undesirable effects.",
FALSE
);
}
else
{
cont = TRUE;
}
if (!cont)
return FALSE;
if (NT_SUCCESS(status = PhOpenProcess(
&processHandle,
PROCESS_QUERY_LIMITED_INFORMATION,
Process->ProcessId
)))
{
if (NT_SUCCESS(status = PhOpenProcessToken(
processHandle,
TOKEN_WRITE,
&tokenHandle
)))
{
status = PhSetTokenIsVirtualizationEnabled(tokenHandle, Enable);
NtClose(tokenHandle);
}
NtClose(processHandle);
}
if (!NT_SUCCESS(status))
{
PhpShowErrorProcess(WindowHandle, L"set virtualization for", Process, status, 0);
return FALSE;
}
return TRUE;
}
/**
* Toggle break-on-termination (critical process) for a process.
*
* \param WindowHandle Parent window used for confirmation and error UI.
* \param Process The process item to operate on.
* \return BOOLEAN TRUE on success, FALSE on failure.
*/
BOOLEAN PhUiSetCriticalProcess(
_In_ HWND WindowHandle,
_In_ PPH_PROCESS_ITEM Process
)
{
NTSTATUS status;
HANDLE processHandle;
BOOLEAN breakOnTermination;
status = PhOpenProcess(
&processHandle,
PROCESS_QUERY_INFORMATION | PROCESS_SET_INFORMATION,
Process->ProcessId
);
if (NT_SUCCESS(status))
{
status = PhGetProcessBreakOnTermination(
processHandle,
&breakOnTermination
);
if (NT_SUCCESS(status))
{
if (!breakOnTermination && (!PhGetIntegerSetting(SETTING_ENABLE_WARNINGS) || PhShowConfirmMessage(
WindowHandle,
L"enable",
L"critical status on the process",
L"If the process ends, the operating system will shut down immediately.",
TRUE
)))
{
status = PhSetProcessBreakOnTermination(processHandle, TRUE);
}
else if (breakOnTermination && (!PhGetIntegerSetting(SETTING_ENABLE_WARNINGS) || PhShowConfirmMessage(
WindowHandle,
L"disable",
L"critical status on the process",
NULL,
FALSE
)))
{
status = PhSetProcessBreakOnTermination(processHandle, FALSE);
}
}
NtClose(processHandle);
}
if (!NT_SUCCESS(status))
{
PhpShowErrorProcess(WindowHandle, L"set critical status for", Process, status, 0);
return FALSE;
}
return TRUE;
}
/**
* Enable or disable Eco mode for a process (power throttling).
*
* \param WindowHandle Parent window used for confirmation and error UI.
* \param Process The process item to operate on.
* \return BOOLEAN TRUE on success, FALSE on failure.
*/
BOOLEAN PhUiSetEcoModeProcess(
_In_ HWND WindowHandle,
_In_ PPH_PROCESS_ITEM Process
)
{
NTSTATUS status;
HANDLE processHandle;
POWER_THROTTLING_PROCESS_STATE powerThrottlingState;
status = PhOpenProcess(
&processHandle,
PROCESS_QUERY_LIMITED_INFORMATION | PROCESS_SET_INFORMATION,
Process->ProcessId
);
if (NT_SUCCESS(status))
{
status = PhGetProcessPowerThrottlingState(
processHandle,
&powerThrottlingState
);
if (NT_SUCCESS(status))
{
if (!(
FlagOn(powerThrottlingState.ControlMask, POWER_THROTTLING_PROCESS_EXECUTION_SPEED) &&
FlagOn(powerThrottlingState.StateMask, POWER_THROTTLING_PROCESS_EXECUTION_SPEED)
))
{
if (!PhGetIntegerSetting(SETTING_ENABLE_WARNINGS) || PhShowConfirmMessage(
WindowHandle,
L"enable",
L"Eco mode for this process",
L"Eco mode will lower process priority and improve power efficiency but may cause instability in some processes.",
FALSE
))
{
// Taskmgr sets the process priority to idle before enabling 'Eco mode'. (dmex)
PhSetProcessPriorityClass(processHandle, PROCESS_PRIORITY_CLASS_IDLE);
//
// Turn PROCESS_EXECUTION_SPEED throttling on.
//
status = PhSetProcessPowerThrottlingState(
processHandle,
POWER_THROTTLING_PROCESS_EXECUTION_SPEED,
POWER_THROTTLING_PROCESS_EXECUTION_SPEED
);
}
}
else
{
//if (!PhGetIntegerSetting(SETTING_ENABLE_WARNINGS) || PhShowConfirmMessage(
// WindowHandle,
// L"disable",
// L"Eco mode for this process",
// L"Eco mode will lower process priority and improve power efficiency but may cause instability in some processes.",
// FALSE
// ))
{
// Taskmgr does not properly restore the original priority after it has exited
// and you later decide to disable 'Eco mode', so we'll restore normal priority
// which isn't quite correct but still way better than what taskmgr does. (dmex)
PhSetProcessPriorityClass(processHandle, PROCESS_PRIORITY_CLASS_NORMAL);
//
// Let system manage all power throttling.
//
status = PhSetProcessPowerThrottlingState(processHandle, 0, 0);
}
}
}
NtClose(processHandle);
}
if (!NT_SUCCESS(status))
{
PhpShowErrorProcess(WindowHandle, L"set Eco mode for", Process, status, 0);
return FALSE;
}
return TRUE;
}
/**
* Toggle the "execution required" state for a process (prevent PLM suspension/termination).
*
* \param WindowHandle Parent window used for confirmation and error UI.
* \param Process The process item to operate on.
* \return BOOLEAN TRUE on success, FALSE on failure.
*/
BOOLEAN PhUiSetExecutionRequiredProcess(
_In_ HWND WindowHandle,
_In_ PPH_PROCESS_ITEM Process
)
{
NTSTATUS status;
if (PhGetIntegerSetting(SETTING_ENABLE_WARNINGS))
{
if (!PhShowConfirmMessage(
WindowHandle,
L"change the execution required state",
PhaConcatStrings2(L"of ", Process->ProcessName->Buffer)->Buffer,
L"The process continues to run instead of being suspended or terminated by process lifetime management (PLM).",
FALSE
))
{
return FALSE;
}
}
if (PhIsProcessExecutionRequired(Process->ProcessId))
{
status = PhProcessExecutionRequiredDisable(Process->ProcessId);
}
else
{
status = PhProcessExecutionRequiredEnable(Process->ProcessId);
}
if (!NT_SUCCESS(status))
{
PhpShowErrorProcess(WindowHandle, L"create execution required state for", Process, status, 0);
return FALSE;
}
return TRUE;
}
/**
* Detach the debugger from a process.
*
* \param WindowHandle Parent window used for confirmation and error UI.
* \param Process The process item to operate on.
* \return BOOLEAN TRUE on success, FALSE on failure.
*/
BOOLEAN PhUiDetachFromDebuggerProcess(
_In_ HWND WindowHandle,
_In_ PPH_PROCESS_ITEM Process
)
{
NTSTATUS status;
HANDLE processHandle;
HANDLE debugObjectHandle;
if (NT_SUCCESS(status = PhOpenProcess(
&processHandle,
PROCESS_QUERY_INFORMATION | PROCESS_SUSPEND_RESUME,
Process->ProcessId
)))
{
if (NT_SUCCESS(status = PhGetProcessDebugObject(
processHandle,
&debugObjectHandle
)))
{
// Disable kill-on-close.
if (NT_SUCCESS(status = PhSetDebugKillProcessOnExit(
debugObjectHandle,
FALSE
)))
{
status = NtRemoveProcessDebug(processHandle, debugObjectHandle);
}
NtClose(debugObjectHandle);
}
NtClose(processHandle);
}
if (status == STATUS_PORT_NOT_SET)
{
PhShowInformation2(WindowHandle, L"Unable to detach the debugger.", L"%s", L"The process is not being debugged.");
return FALSE;
}
if (!NT_SUCCESS(status))
{
PhpShowErrorProcess(WindowHandle, L"detach debugger from", Process, status, 0);
return FALSE;
}
return TRUE;
}
/**
* Load a DLL into the target process.
*
* \param WindowHandle Parent window for dialogs.
* \param Process The process item to load the DLL into.
* \return BOOLEAN TRUE on success, FALSE on failure or cancel.
*/
BOOLEAN PhUiLoadDllProcess(
_In_ HWND WindowHandle,
_In_ PPH_PROCESS_ITEM Process
)
{
static PH_FILETYPE_FILTER filters[] =
{
{ L"DLL files (*.dll)", L"*.dll" },
{ L"All files (*.*)", L"*.*" }
};
NTSTATUS status = STATUS_UNSUCCESSFUL;
HANDLE processHandle = NULL;
PVOID fileDialog;
PPH_STRING fileName;
fileDialog = PhCreateOpenFileDialog();
PhSetFileDialogOptions(fileDialog, PH_FILEDIALOG_DONTADDTORECENT);
PhSetFileDialogFilter(fileDialog, filters, RTL_NUMBER_OF(filters));
if (!PhShowFileDialog(WindowHandle, fileDialog))
{
PhFreeFileDialog(fileDialog);
return FALSE;
}
fileName = PH_AUTO(PhGetFileDialogFileName(fileDialog));
PhFreeFileDialog(fileDialog);
// Windows 8 requires ALL_ACCESS for PLM execution requests. (dmex)
if (WindowsVersion >= WINDOWS_8 && WindowsVersion <= WINDOWS_8_1)
{
status = PhOpenProcess(
&processHandle,
PROCESS_ALL_ACCESS,
Process->ProcessId
);
}
// Windows 10 and above require SET_LIMITED for PLM execution requests. (dmex)
if (!NT_SUCCESS(status))
{
status = PhOpenProcess(
&processHandle,
PROCESS_QUERY_LIMITED_INFORMATION | PROCESS_SET_LIMITED_INFORMATION |
PROCESS_QUERY_INFORMATION | PROCESS_CREATE_THREAD | PROCESS_VM_OPERATION |
PROCESS_VM_READ | PROCESS_VM_WRITE | SYNCHRONIZE,
Process->ProcessId
);
}
if (NT_SUCCESS(status))
{
status = PhLoadDllProcess(
processHandle,
&fileName->sr,
5000
);
NtClose(processHandle);
}
if (!NT_SUCCESS(status))
{
PhpShowErrorProcess(WindowHandle, L"load the DLL into", Process, status, 0);
return FALSE;
}
return TRUE;
}
/**
* Set I/O priority for a list of processes.
*
* \param WindowHandle Parent window used for error reporting.
* \param Processes Array of process items to operate on.
* \param NumberOfProcesses Number of processes in the array.
* \param IoPriority The IO priority hint to set.
* \return BOOLEAN TRUE if the operation succeeded for all processes, FALSE otherwise.
*/
BOOLEAN PhUiSetIoPriorityProcesses(
_In_ HWND WindowHandle,
_In_ PPH_PROCESS_ITEM *Processes,
_In_ ULONG NumberOfProcesses,
_In_ IO_PRIORITY_HINT IoPriority
)
{
BOOLEAN success = TRUE;
BOOLEAN cancelled = FALSE;
ULONG i;
for (i = 0; i < NumberOfProcesses; i++)
{
NTSTATUS status;
HANDLE processHandle;
if (NT_SUCCESS(status = PhOpenProcess(
&processHandle,
PROCESS_SET_INFORMATION,
Processes[i]->ProcessId
)))
{
if (Processes[i]->ProcessId != SYSTEM_PROCESS_ID)
{
status = PhSetProcessIoPriority(processHandle, IoPriority);
}
else
{
// See comment in PhUiSetPriorityClassProcesses.
status = STATUS_UNSUCCESSFUL;
}
NtClose(processHandle);
}
if (!NT_SUCCESS(status))
{
BOOLEAN connected;
success = FALSE;
// The operation may have failed due to the lack of SeIncreaseBasePriorityPrivilege.
if (!cancelled && PhpShowErrorAndConnectToPhSvc(
WindowHandle,
PhaConcatStrings2(L"Unable to set the I/O priority of ", Processes[i]->ProcessName->Buffer)->Buffer,
status,
&connected,
&cancelled
))
{
if (connected)
{
if (NT_SUCCESS(status = PhSvcCallControlProcess(Processes[i]->ProcessId, PhSvcControlProcessIoPriority, IoPriority)))
success = TRUE;
else
PhpShowErrorProcess(WindowHandle, L"set the I/O priority of", Processes[i], status, 0);
PhUiDisconnectFromPhSvc();
}
else
{
cancelled = TRUE;
}
}
else
{
if (cancelled)
break;
if (!PhpShowErrorProcess(WindowHandle, L"set the I/O priority of", Processes[i], status, 0))
break;
}
}
}
return success;
}
/**
* Set page priority for a process.
*
* \param WindowHandle Parent window used for error reporting.
* \param Process The process item to operate on.
* \param PagePriority Page priority value to set.
* \return BOOLEAN TRUE on success, FALSE on failure.
*/
BOOLEAN PhUiSetPagePriorityProcess(
_In_ HWND WindowHandle,
_In_ PPH_PROCESS_ITEM Process,
_In_ ULONG PagePriority
)
{
NTSTATUS status;
HANDLE processHandle;
if (NT_SUCCESS(status = PhOpenProcess(
&processHandle,
PROCESS_SET_INFORMATION,
Process->ProcessId
)))
{
if (Process->ProcessId != SYSTEM_PROCESS_ID)
{
status = PhSetProcessPagePriority(processHandle, PagePriority);
}
else
{
// See comment in PhUiSetPriorityClassProcesses.
status = STATUS_UNSUCCESSFUL;
}
NtClose(processHandle);
}
if (!NT_SUCCESS(status))
{
PhpShowErrorProcess(WindowHandle, L"set the page priority of", Process, status, 0);
return FALSE;
}
return TRUE;
}
/**
* Set the priority class for a list of processes.
*
* \param WindowHandle Parent window used for error reporting.
* \param Processes Array of process items to operate on.
* \param NumberOfProcesses Number of processes in the array.
* \param PriorityClass Priority class value to set.
* \return BOOLEAN TRUE if the operation succeeded for all processes, FALSE otherwise.
*/
BOOLEAN PhUiSetPriorityClassProcesses(
_In_ HWND WindowHandle,
_In_ PPH_PROCESS_ITEM *Processes,
_In_ ULONG NumberOfProcesses,
_In_ ULONG PriorityClass
)
{
BOOLEAN success = TRUE;
BOOLEAN cancelled = FALSE;
ULONG i;
for (i = 0; i < NumberOfProcesses; i++)
{
NTSTATUS status;
HANDLE processHandle;
if (NT_SUCCESS(status = PhOpenProcess(
&processHandle,
PROCESS_SET_INFORMATION,
Processes[i]->ProcessId
)))
{
if (Processes[i]->ProcessId != SYSTEM_PROCESS_ID)
{
status = PhSetProcessPriorityClass(processHandle, (UCHAR)PriorityClass);
}
else
{
// Changing the priority of System can lead to a BSOD on some versions of Windows,
// so disallow this.
status = STATUS_UNSUCCESSFUL;
}
NtClose(processHandle);
}
if (!NT_SUCCESS(status))
{
BOOLEAN connected;
success = FALSE;
// The operation may have failed due to the lack of SeIncreaseBasePriorityPrivilege.
if (!cancelled && PhpShowErrorAndConnectToPhSvc(
WindowHandle,
PhaConcatStrings2(L"Unable to set the priority class of ", Processes[i]->ProcessName->Buffer)->Buffer,
status,
&connected,
&cancelled
))
{
if (connected)
{
if (NT_SUCCESS(status = PhSvcCallControlProcess(Processes[i]->ProcessId, PhSvcControlProcessPriority, PriorityClass)))
success = TRUE;
else
PhpShowErrorProcess(WindowHandle, L"set the priority class of", Processes[i], status, 0);
PhUiDisconnectFromPhSvc();
}
else
{
cancelled = TRUE;
}
}
else
{
if (cancelled)
break;
if (!PhpShowErrorProcess(WindowHandle, L"set the priority class of", Processes[i], status, 0))
break;
}
}
}
return success;
}
/**
* Set or clear priority boost for a list of processes.
*
* \param WindowHandle Parent window used for error reporting.
* \param Processes Array of process items to operate on.
* \param NumberOfProcesses Number of processes in the array.
* \param PriorityBoost TRUE to enable boost, FALSE to disable.
* \return BOOLEAN TRUE if the operation succeeded for all processes, FALSE otherwise.
*/
BOOLEAN PhUiSetBoostPriorityProcesses(
_In_ HWND WindowHandle,
_In_ PPH_PROCESS_ITEM* Processes,
_In_ ULONG NumberOfProcesses,
_In_ BOOLEAN PriorityBoost
)
{
BOOLEAN success = TRUE;
ULONG i;
for (i = 0; i < NumberOfProcesses; i++)
{
NTSTATUS status;
HANDLE processHandle;
if (NT_SUCCESS(status = PhOpenProcess(
&processHandle,
PROCESS_SET_INFORMATION,
Processes[i]->ProcessId
)))
{
status = PhSetProcessPriorityBoost(processHandle, PriorityBoost);
NtClose(processHandle);
if (!NT_SUCCESS(status))
{
success = FALSE;
if (!PhpShowErrorProcess(WindowHandle, L"change boost priority of", Processes[i], status, 0))
break;
}
}
}
return success;
}
/**
* Set or clear priority boost for a single process.
*
* \param WindowHandle Parent window used for error reporting.
* \param Process The process item to operate on.
* \param PriorityBoost TRUE to enable boost, FALSE to disable.
* \return BOOLEAN TRUE on success, FALSE on failure.
*/
BOOLEAN PhUiSetBoostPriorityProcess(
_In_ HWND WindowHandle,
_In_ PPH_PROCESS_ITEM Process,
_In_ BOOLEAN PriorityBoost
)
{
NTSTATUS status;
HANDLE processHandle;
if (NT_SUCCESS(status = PhOpenProcess(
&processHandle,
PROCESS_SET_INFORMATION,
Process->ProcessId
)))
{
status = PhSetProcessPriorityBoost(processHandle, PriorityBoost);
NtClose(processHandle);
}
if (!NT_SUCCESS(status))
{
PhpShowErrorProcess(WindowHandle, L"set the boost priority of", Process, status, 0);
return FALSE;
}
return TRUE;
}
#pragma region Service Progress Dialog
typedef struct _PH_UI_SERVICE_PROGRESS_DIALOG
{
HWND WindowHandle;
HWND ParentWindowHandle;
PCWSTR Verb;
PCWSTR Message;
PPH_STRING StatusMessage;
PPH_STRING StatusContent;
PPH_LIST ServiceItemList;
volatile LONG RequireElevation;
struct
{
BOOLEAN Flags;
union
{
BOOLEAN Warning : 1;
BOOLEAN Spare : 7;
};
};
WNDPROC OldWndProc;
PUSER_THREAD_START_ROUTINE ActionCallback;
PHSVC_API_CONTROLSERVICE_COMMAND ActionCommand;
} PH_UI_SERVICE_PROGRESS_DIALOG, *PPH_UI_SERVICE_PROGRESS_DIALOG;
typedef struct _PH_UI_SERVICE_ITEM
{
NTSTATUS Status;
PPH_SERVICE_ITEM Service;
} PH_UI_SERVICE_ITEM, *PPH_UI_SERVICE_ITEM;
#define WM_PHSVC_ERROR (WM_APP + 1)
#define WM_PHSVC_EXIT (WM_APP + 2)
VOID PhShowServiceProgressDialogStatusPage(
_In_ PPH_UI_SERVICE_PROGRESS_DIALOG Context
);
#pragma endregion
VOID PhpShowServiceProgressInitializeText(
_In_ PPH_UI_SERVICE_PROGRESS_DIALOG Context,
_Out_ PPH_STRING* Verb,
_Out_ PPH_STRING* VerbCaps,
_Out_ PPH_STRING* Action,
_Out_ PCWSTR* Object
)
{
if (Context->ServiceItemList->Count == 1)
*Object = L"the selected service";
else
*Object = L"the selected services";
// Make sure the verb is all lowercase.
*Verb = PhaLowerString(PhaCreateString(Context->Verb));
// "terminate" -> "Terminate"
*VerbCaps = PhaDuplicateString(*Verb);
if (!PhIsNullOrEmptyString(*VerbCaps)) (*VerbCaps)->Buffer[0] = PhUpcaseUnicodeChar((*VerbCaps)->Buffer[0]);
// "terminate", "the process" -> "terminate the process"
*Action = PhaConcatStrings(3, (*Verb)->Buffer, L" ", *Object);
}
HRESULT CALLBACK PhpUiServiceErrorDialogCallbackProc(
_In_ HWND WindowHandle,
_In_ UINT WindowMessage,
_In_ WPARAM wParam,
_In_ LPARAM lParam,
_In_ LONG_PTR Context
)
{
PPH_UI_SERVICE_PROGRESS_DIALOG context = (PPH_UI_SERVICE_PROGRESS_DIALOG)Context;
switch (WindowMessage)
{
case TDN_NAVIGATED:
{
if (InterlockedCompareExchange(&context->RequireElevation, FALSE, FALSE))
{
SendMessage(WindowHandle, TDM_SET_BUTTON_ELEVATION_REQUIRED_STATE, IDYES, TRUE);
}
}
break;
case TDN_BUTTON_CLICKED:
{
ULONG buttonId = (ULONG)wParam;
if (buttonId == IDYES)
{
PhShowServiceProgressDialogStatusPage(context);
return S_FALSE;
}
}
break;
}
return S_OK;
}
VOID PhUiNavigateServiceErrorDialogPage(
_In_ PPH_UI_SERVICE_PROGRESS_DIALOG Context,
_In_ PPH_STRING MainInstruction,
_In_opt_ PPH_STRING MainContent
)
{
static CONST TASKDIALOG_BUTTON buttons[1] =
{
{ IDNO, L"Close" }
};
static CONST TASKDIALOG_BUTTON buttonsElevation[2] =
{
{ IDYES, L"Continue" },
{ IDNO, L"Cancel" },
};
TASKDIALOGCONFIG config;
memset(&config, 0, sizeof(TASKDIALOGCONFIG));
config.cbSize = sizeof(TASKDIALOGCONFIG);
config.dwFlags = TDF_ALLOW_DIALOG_CANCELLATION | TDF_CAN_BE_MINIMIZED;
config.pszWindowTitle = PhApplicationName;
config.pszMainIcon = TD_ERROR_ICON;
config.lpCallbackData = (LONG_PTR)Context;
config.pfCallback = PhpUiServiceErrorDialogCallbackProc;
config.pszMainInstruction = PhGetString(MainInstruction);
if (MainContent) config.pszContent = PhGetString(MainContent);
config.cxWidth = 200;
if (InterlockedCompareExchange(&Context->RequireElevation, FALSE, FALSE))
{
config.cButtons = RTL_NUMBER_OF(buttonsElevation);
config.pButtons = buttonsElevation;
config.nDefaultButton = IDYES;
}
else
{
config.cButtons = RTL_NUMBER_OF(buttons);
config.pButtons = buttons;
config.nDefaultButton = IDNO;
}
PhTaskDialogNavigatePage(Context->WindowHandle, &config);
}
VOID PhUiNavigateServiceCompleteDialogPage(
_In_ PPH_UI_SERVICE_PROGRESS_DIALOG Context
)
{
PostMessage(Context->WindowHandle, WM_PHSVC_EXIT, 0, 0);
}
VOID PhUiNavigateServiceErrorDialogPageFromThread(
_In_ PPH_UI_SERVICE_PROGRESS_DIALOG Context
)
{
PostMessage(Context->WindowHandle, WM_PHSVC_ERROR, 0, 0);
}
_Function_class_(USER_THREAD_START_ROUTINE)
NTSTATUS PhpUiServicePendingStartCallback(
_In_ PPH_UI_SERVICE_PROGRESS_DIALOG Context
)
{
PPH_LIST serviceErrorList = PhCreateList(1);
if (InterlockedCompareExchange(&Context->RequireElevation, FALSE, FALSE))
{
NTSTATUS status;
BOOLEAN connected;
if (PhpElevationLevelAndConnectToPhSvc(Context->WindowHandle, &connected) && connected)
{
for (ULONG i = 0; i < Context->ServiceItemList->Count; i++)
{
PPH_SERVICE_ITEM serviceItem = Context->ServiceItemList->Items[i];
status = PhSvcCallControlService(
PhGetString(serviceItem->Name),
Context->ActionCommand
);
if (NT_SUCCESS(status))
{
ULONG index;
index = PhFindItemList(Context->ServiceItemList, serviceItem);
if (index != ULONG_MAX)
{
PhRemoveItemList(Context->ServiceItemList, index);
PhDereferenceObject(serviceItem);
}
}
else
{
PhAddItemList(serviceErrorList, LongToPtr(status));
}
}
PhUiDisconnectFromPhSvc();
}
else
{
PhUiNavigateServiceCompleteDialogPage(Context);
goto CleanupExit;
}
}
else
{
for (ULONG i = 0; i < Context->ServiceItemList->Count; i++)
{
PPH_SERVICE_ITEM serviceItem = Context->ServiceItemList->Items[i];
NTSTATUS status;
status = Context->ActionCallback(serviceItem);
if (NT_SUCCESS(status))
{
ULONG index;
index = PhFindItemList(Context->ServiceItemList, serviceItem);
if (index != ULONG_MAX)
{
PhRemoveItemList(Context->ServiceItemList, index);
PhDereferenceObject(serviceItem);
}
}
else
{
PhAddItemList(serviceErrorList, LongToPtr(status));
}
}
}
InterlockedExchange(&Context->RequireElevation, FALSE);
if (serviceErrorList->Count && !PhGetOwnTokenAttributes().Elevated)
{
for (ULONG i = 0; i < serviceErrorList->Count; i++)
{
NTSTATUS status = PtrToLong(serviceErrorList->Items[i]);
if (status == STATUS_ACCESS_DENIED || status == STATUS_PRIVILEGE_NOT_HELD)
{
InterlockedExchange(&Context->RequireElevation, TRUE);
break;
}
}
}
if (Context->ServiceItemList->Count)
{
PH_STRING_BUILDER stringBuilder;
PhInitializeStringBuilder(&stringBuilder, 0x50);
for (ULONG i = 0; i < Context->ServiceItemList->Count; i++)
{
PPH_STRING serviceName = NULL;
PPH_STRING statusMessage = NULL;
if (!PhIsNullOrEmptyString(((PPH_SERVICE_ITEM)Context->ServiceItemList->Items[i])->Name))
{
serviceName = ((PPH_SERVICE_ITEM)Context->ServiceItemList->Items[i])->Name;
}
if (i < serviceErrorList->Count)
{
statusMessage = PhGetStatusMessage(PtrToLong(serviceErrorList->Items[i]), 0);
}
if (!PhIsNullOrEmptyString(serviceName))
PhAppendStringBuilder(&stringBuilder, &serviceName->sr);
PhAppendStringBuilder2(&stringBuilder, L": ");
PhAppendFormatStringBuilder(&stringBuilder, L"(0x%lx) ", PtrToLong(serviceErrorList->Items[i]));
if (!PhIsNullOrEmptyString(statusMessage))
{
PhAppendStringBuilder(&stringBuilder, &statusMessage->sr);
PhClearReference(&statusMessage);
}
PhAppendStringBuilder2(&stringBuilder, L"\r\n");
}
if (stringBuilder.String->Length != 0)
{
PhRemoveEndStringBuilder(&stringBuilder, 2);
}
if (InterlockedCompareExchange(&Context->RequireElevation, FALSE, FALSE))
{
PhAppendStringBuilder2(&stringBuilder, L"\r\n\r\n");
PhAppendStringBuilder2(&stringBuilder, L"You will need to provide administrator permission. "
L"Click Continue to complete this operation.");
}
{
PPH_STRING message;
PPH_STRING content;
message = PhFormatString(L"Unable to %s services:", Context->Verb);
content = PhFinalStringBuilderString(&stringBuilder);
InterlockedExchangePointer(&Context->StatusMessage, message);
InterlockedExchangePointer(&Context->StatusContent, content);
PhUiNavigateServiceErrorDialogPageFromThread(Context);
}
}
else
{
PhUiNavigateServiceCompleteDialogPage(Context);
}
CleanupExit:
PhClearReference(&serviceErrorList);
PhDereferenceObject(Context);
return STATUS_SUCCESS;
}
HRESULT CALLBACK PhpUiServiceProgressDialogCallbackProc(
_In_ HWND WindowHandle,
_In_ UINT WindowMessage,
_In_ WPARAM wParam,
_In_ LPARAM lParam,
_In_ LONG_PTR Context
)
{
PPH_UI_SERVICE_PROGRESS_DIALOG context = (PPH_UI_SERVICE_PROGRESS_DIALOG)Context;
switch (WindowMessage)
{
case TDN_NAVIGATED:
{
SendMessage(WindowHandle, TDM_SET_MARQUEE_PROGRESS_BAR, TRUE, 0);
SendMessage(WindowHandle, TDM_SET_PROGRESS_BAR_MARQUEE, TRUE, 1);
PhReferenceObject(context);
PhCreateThread2(PhpUiServicePendingStartCallback, context);
}
break;
case TDN_BUTTON_CLICKED:
{
ULONG buttonId = (ULONG)wParam;
if (buttonId != IDCANCEL)
{
return S_FALSE;
}
}
break;
}
return S_OK;
}
VOID PhShowServiceProgressDialogStatusPage(
_In_ PPH_UI_SERVICE_PROGRESS_DIALOG Context
)
{
TASKDIALOGCONFIG config;
PPH_STRING verb;
PPH_STRING verbCaps;
PPH_STRING action;
PCWSTR object;
PhpShowServiceProgressInitializeText(Context, &verb, &verbCaps, &action, &object);
memset(&config, 0, sizeof(TASKDIALOGCONFIG));
config.cbSize = sizeof(TASKDIALOGCONFIG);
config.dwFlags = TDF_ALLOW_DIALOG_CANCELLATION | TDF_SHOW_MARQUEE_PROGRESS_BAR | TDF_CAN_BE_MINIMIZED;
config.pszWindowTitle = PhApplicationName;
config.pszMainIcon = TD_INFORMATION_ICON;
config.dwCommonButtons = TDCBF_CANCEL_BUTTON;
config.lpCallbackData = (LONG_PTR)Context;
config.pfCallback = PhpUiServiceProgressDialogCallbackProc;
config.pszMainInstruction = PhaConcatStrings(5, L"Attempting to ", PhGetString(verb), L" ", object, L"...")->Buffer;
config.cxWidth = 200;
PhTaskDialogNavigatePage(Context->WindowHandle, &config);
}
HRESULT CALLBACK PhpUiServiceConfirmDialogCallbackProc(
_In_ HWND WindowHandle,
_In_ UINT WindowMessage,
_In_ WPARAM wParam,
_In_ LPARAM lParam,
_In_ LONG_PTR Context
)
{
PPH_UI_SERVICE_PROGRESS_DIALOG context = (PPH_UI_SERVICE_PROGRESS_DIALOG)Context;
switch (WindowMessage)
{
case TDN_BUTTON_CLICKED:
{
ULONG buttonId = (ULONG)wParam;
if (buttonId == IDYES)
{
PhShowServiceProgressDialogStatusPage(context);
return S_FALSE;
}
}
break;
}
return S_OK;
}
VOID PhShowServiceProgressDialogConfirmMessage(
_In_ PPH_UI_SERVICE_PROGRESS_DIALOG Context
)
{
TASKDIALOGCONFIG config;
TASKDIALOG_BUTTON buttons[2];
PPH_STRING verb;
PPH_STRING verbCaps;
PPH_STRING action;
PCWSTR object;
PhpShowServiceProgressInitializeText(Context, &verb, &verbCaps, &action, &object);
memset(&config, 0, sizeof(TASKDIALOGCONFIG));
config.cbSize = sizeof(TASKDIALOGCONFIG);
config.dwFlags = TDF_ALLOW_DIALOG_CANCELLATION | TDF_CAN_BE_MINIMIZED;
config.pszWindowTitle = PhApplicationName;
config.lpCallbackData = (LONG_PTR)Context;
config.pfCallback = PhpUiServiceConfirmDialogCallbackProc;
config.pszMainIcon = Context->Warning ? TD_WARNING_ICON : TD_INFORMATION_ICON;
config.pszMainInstruction = PhaConcatStrings(3, L"Do you want to ", action->Buffer, L"?")->Buffer;
if (Context->Message) config.pszContent = PhaConcatStrings2(Context->Message, L" Are you sure you want to continue?")->Buffer;
buttons[0].nButtonID = IDYES;
buttons[0].pszButtonText = verbCaps->Buffer;
buttons[1].nButtonID = IDNO;
buttons[1].pszButtonText = L"Cancel";
config.cButtons = 2;
config.pButtons = buttons;
config.nDefaultButton = IDYES;
config.cxWidth = 200;
PhTaskDialogNavigatePage(Context->WindowHandle, &config);
}
static LRESULT CALLBACK PhpUiServiceProgressDialogWndProc(
_In_ HWND WindowHandle,
_In_ UINT WindowMessage,
_In_ WPARAM wParam,
_In_ LPARAM lParam
)
{
PPH_UI_SERVICE_PROGRESS_DIALOG context;
WNDPROC oldWndProc;
context = PhGetWindowContext(WindowHandle, MAXCHAR);
if (!context)
goto DefaultWndProc;
oldWndProc = context->OldWndProc;
switch (WindowMessage)
{
case WM_DESTROY:
{
PhSetWindowProcedure(WindowHandle, oldWndProc);
PhRemoveWindowContext(WindowHandle, MAXCHAR);
}
break;
case WM_DPICHANGED:
{
PhSetApplicationWindowIconEx(WindowHandle, HIWORD(wParam));
}
break;
case WM_PHSVC_ERROR:
{
PPH_STRING message;
PPH_STRING content;
message = InterlockedExchangePointer(&context->StatusMessage, NULL);
content = InterlockedExchangePointer(&context->StatusContent, NULL);
PhUiNavigateServiceErrorDialogPage(
context,
message,
content
);
PhClearReference(&message);
PhClearReference(&content);
}
goto DefaultWndProc;
case WM_PHSVC_EXIT:
{
CallWindowProc(oldWndProc, WindowHandle, TDM_CLICK_BUTTON, IDCANCEL, 0);
}
goto DefaultWndProc;
}
return CallWindowProc(oldWndProc, WindowHandle, WindowMessage, wParam, lParam);
DefaultWndProc:
return DefWindowProc(WindowHandle, WindowMessage, wParam, lParam);
}
HRESULT CALLBACK PhpUiServiceInitializeDialogCallbackProc(
_In_ HWND WindowHandle,
_In_ UINT WindowMessage,
_In_ WPARAM wParam,
_In_ LPARAM lParam,
_In_ LONG_PTR Context
)
{
PPH_UI_SERVICE_PROGRESS_DIALOG context = (PPH_UI_SERVICE_PROGRESS_DIALOG)Context;
switch (WindowMessage)
{
case TDN_DIALOG_CONSTRUCTED:
{
context->WindowHandle = WindowHandle;
PhSetApplicationWindowIconEx(WindowHandle, PhGetWindowDpi(WindowHandle));
PhCenterWindow(WindowHandle, context->ParentWindowHandle);
PhRegisterWindowCallback(WindowHandle, PH_PLUGIN_WINDOW_EVENT_TYPE_TOPMOST, NULL);
context->OldWndProc = PhGetWindowProcedure(WindowHandle);
PhSetWindowContext(WindowHandle, MAXCHAR, context);
PhSetWindowProcedure(WindowHandle, PhpUiServiceProgressDialogWndProc);
if (
PhGetIntegerSetting(SETTING_ENABLE_WARNINGS) &&
context->ActionCommand != PhSvcControlServiceStart
)
{
PhShowServiceProgressDialogConfirmMessage(context);
}
else
{
PhShowServiceProgressDialogStatusPage(context);
}
PhInitializeWindowTheme(WindowHandle, !!PhGetIntegerSetting(SETTING_ENABLE_THEME_SUPPORT));
}
break;
}
return S_OK;
}
_Function_class_(USER_THREAD_START_ROUTINE)
NTSTATUS PhShowServiceProgressDialogThread(
_In_ PPH_UI_SERVICE_PROGRESS_DIALOG Context
)
{
PH_AUTO_POOL autoPool;
TASKDIALOGCONFIG config;
PhInitializeAutoPool(&autoPool);
memset(&config, 0, sizeof(TASKDIALOGCONFIG));
config.cbSize = sizeof(TASKDIALOGCONFIG);
config.dwFlags = TDF_ALLOW_DIALOG_CANCELLATION | TDF_CAN_BE_MINIMIZED;
config.pfCallback = PhpUiServiceInitializeDialogCallbackProc;
config.lpCallbackData = (LONG_PTR)Context;
config.pszContent = L"Initializing...";
config.cxWidth = 200;
PhShowTaskDialog(&config, NULL, NULL, NULL);
PhDeleteAutoPool(&autoPool);
PhDereferenceObject(Context);
return STATUS_SUCCESS;
}
_Function_class_(PH_TYPE_DELETE_PROCEDURE)
static VOID PhServiceProgressContextDeleteProcedure(
_In_ PVOID Object,
_In_ ULONG Flags
)
{
PPH_UI_SERVICE_PROGRESS_DIALOG context = Object;
PhDereferenceObjects(context->ServiceItemList->Items, context->ServiceItemList->Count);
PhDereferenceObject(context->ServiceItemList);
}
PPH_UI_SERVICE_PROGRESS_DIALOG PhCreateServiceProgressContext(
VOID
)
{
static PPH_OBJECT_TYPE PhServiceProgressObjectType = NULL;
static PH_INITONCE PhServiceProgressTypeInitOnce = PH_INITONCE_INIT;
PPH_UI_SERVICE_PROGRESS_DIALOG context;
if (PhBeginInitOnce(&PhServiceProgressTypeInitOnce))
{
PhServiceProgressObjectType = PhCreateObjectType(L"ServiceProgressObjectType", 0, PhServiceProgressContextDeleteProcedure);
PhEndInitOnce(&PhServiceProgressTypeInitOnce);
}
context = PhCreateObject(sizeof(PH_UI_SERVICE_PROGRESS_DIALOG), PhServiceProgressObjectType);
memset(context, 0, sizeof(PH_UI_SERVICE_PROGRESS_DIALOG));
return context;
}
VOID PhShowServiceProgressDialog(
_In_ HWND WindowHandle,
_In_ PCWSTR Verb,
_In_ PCWSTR Message,
_In_ BOOLEAN Warning,
_In_ PPH_SERVICE_ITEM* Services,
_In_ ULONG NumberOfServices,
_In_ PUSER_THREAD_START_ROUTINE ActionCallback,
_In_ PHSVC_API_CONTROLSERVICE_COMMAND ActionCommand
)
{
PPH_UI_SERVICE_PROGRESS_DIALOG context;
if (NumberOfServices == 0)
return;
PhReferenceObjects(Services, NumberOfServices);
context = PhCreateServiceProgressContext();
context->ParentWindowHandle = WindowHandle;
context->Verb = Verb;
context->Message = Message;
context->Warning = Warning;
context->ActionCallback = ActionCallback;
context->ActionCommand = ActionCommand;
context->ServiceItemList = PhCreateList(NumberOfServices);
PhAddItemsList(context->ServiceItemList, Services, NumberOfServices);
PhCreateThread2(PhShowServiceProgressDialogThread, context);
}
static BOOLEAN PhpShowContinueMessageServices(
_In_ HWND WindowHandle,
_In_ PCWSTR Verb,
_In_ PCWSTR Message,
_In_ BOOLEAN Warning,
_In_ PPH_SERVICE_ITEM* Services,
_In_ ULONG NumberOfServices
)
{
if (NumberOfServices == 0)
return FALSE;
if (PhGetIntegerSetting(SETTING_ENABLE_WARNINGS))
{
PCWSTR object;
if (NumberOfServices == 1)
{
object = L"the selected service";
}
else
{
object = L"the selected services";
}
return PhShowConfirmMessage(
WindowHandle,
Verb,
object,
Message,
Warning
);
}
else
{
return TRUE;
}
}
static BOOLEAN PhpShowErrorService(
_In_ HWND WindowHandle,
_In_ PWSTR Verb,
_In_ PPH_SERVICE_ITEM Service,
_In_ NTSTATUS Status,
_In_opt_ ULONG Win32Result
)
{
return PhShowContinueStatus(
WindowHandle,
PhaFormatString(
L"Unable to %s %s.",
Verb,
Service->Name->Buffer
)->Buffer,
Status,
Win32Result
);
}
_Function_class_(USER_THREAD_START_ROUTINE)
static NTSTATUS PhUiServiceStartCallback(
_In_ PPH_SERVICE_ITEM ServiceItem
)
{
NTSTATUS status;
SC_HANDLE serviceHandle;
status = PhOpenService(
&serviceHandle,
SERVICE_START,
PhGetString(ServiceItem->Name)
);
if (NT_SUCCESS(status))
{
status = PhStartService(serviceHandle, 0, NULL);
PhCloseServiceHandle(serviceHandle);
}
return status;
}
BOOLEAN PhUiStartServices(
_In_ HWND WindowHandle,
_In_ PPH_SERVICE_ITEM* Services,
_In_ ULONG NumberOfServices
)
{
BOOLEAN success = TRUE;
BOOLEAN cancelled = FALSE;
ULONG i;
if (PhGetIntegerSetting(SETTING_ENABLE_SERVICE_PROGRESS_DIALOG))
{
PhShowServiceProgressDialog(
WindowHandle,
L"start",
L"Starting a service might prevent the system from functioning properly.",
FALSE,
Services,
NumberOfServices,
PhUiServiceStartCallback,
PhSvcControlServiceStart
);
return FALSE;
}
if (!PhpShowContinueMessageServices(
WindowHandle,
L"start",
L"Starting a service might prevent the system from functioning properly.",
FALSE,
Services,
NumberOfServices
))
return FALSE;
for (i = 0; i < NumberOfServices; i++)
{
NTSTATUS status;
SC_HANDLE serviceHandle;
success = FALSE;
status = PhOpenService(&serviceHandle, SERVICE_START, PhGetString(Services[i]->Name));
if (NT_SUCCESS(status))
{
status = PhStartService(serviceHandle, 0, NULL);
if (NT_SUCCESS(status))
success = TRUE;
PhCloseServiceHandle(serviceHandle);
}
if (!success)
{
BOOLEAN connected;
success = FALSE;
if (!cancelled && PhpShowErrorAndConnectToPhSvc(
WindowHandle,
PhaConcatStrings2(L"Unable to start ", PhGetString(Services[i]->Name))->Buffer,
status,
&connected,
&cancelled
))
{
if (connected)
{
if (NT_SUCCESS(status = PhSvcCallControlService(PhGetString(Services[i]->Name), PhSvcControlServiceStart)))
success = TRUE;
else
PhpShowErrorService(WindowHandle, L"start", Services[i], status, 0);
PhUiDisconnectFromPhSvc();
}
else
{
cancelled = TRUE;
}
}
else
{
if (cancelled)
break;
if (!PhpShowErrorService(WindowHandle, L"start", Services[i], status, 0))
break;
}
}
}
return success;
//BOOLEAN result = TRUE;
//ULONG i;
//
//for (i = 0; i < NumberOfServices; i++)
//{
// SC_HANDLE serviceHandle;
// BOOLEAN success = FALSE;
//
// serviceHandle = PhOpenService(PhGetString(Services[i]->Name), SERVICE_START);
//
// if (serviceHandle)
// {
// if (StartService(serviceHandle, 0, NULL))
// success = TRUE;
//
// PhCloseServiceHandle(serviceHandle);
// }
//
// if (!success)
// {
// NTSTATUS status;
//
// status = PhGetLastWin32ErrorAsNtStatus();
// result = FALSE;
//
// PhpShowErrorService(WindowHandle, L"start", Services[i], status, 0);
// }
//}
//
//return result;
}
BOOLEAN PhUiStartService(
_In_ HWND WindowHandle,
_In_ PPH_SERVICE_ITEM Service
)
{
SC_HANDLE serviceHandle;
NTSTATUS status;
BOOLEAN success = FALSE;
status = PhOpenService(&serviceHandle, SERVICE_START, PhGetString(Service->Name));
if (NT_SUCCESS(status))
{
status = PhStartService(serviceHandle, 0, NULL);
if (NT_SUCCESS(status))
success = TRUE;
PhCloseServiceHandle(serviceHandle);
}
if (!success)
{
BOOLEAN connected;
BOOLEAN cancelled;
if (PhpShowErrorAndConnectToPhSvc(
WindowHandle,
PhaConcatStrings2(L"Unable to start ", PhGetString(Service->Name))->Buffer,
status,
&connected,
&cancelled
))
{
if (connected)
{
if (NT_SUCCESS(status = PhSvcCallControlService(PhGetString(Service->Name), PhSvcControlServiceStart)))
success = TRUE;
else
PhpShowErrorService(WindowHandle, L"start", Service, status, 0);
PhUiDisconnectFromPhSvc();
}
}
else
{
if (!cancelled)
{
PhpShowErrorService(WindowHandle, L"start", Service, status, 0);
}
}
}
return success;
}
_Function_class_(USER_THREAD_START_ROUTINE)
static NTSTATUS PhUiServiceContinueCallback(
_In_ PPH_SERVICE_ITEM ServiceItem
)
{
NTSTATUS status;
SC_HANDLE serviceHandle;
status = PhOpenService(
&serviceHandle,
SERVICE_PAUSE_CONTINUE,
PhGetString(ServiceItem->Name)
);
if (NT_SUCCESS(status))
{
status = PhContinueService(serviceHandle);
PhCloseServiceHandle(serviceHandle);
}
return status;
}
BOOLEAN PhUiContinueServices(
_In_ HWND WindowHandle,
_In_ PPH_SERVICE_ITEM* Services,
_In_ ULONG NumberOfServices
)
{
BOOLEAN success = TRUE;
BOOLEAN cancelled = FALSE;
ULONG i;
if (PhGetIntegerSetting(SETTING_ENABLE_SERVICE_PROGRESS_DIALOG))
{
PhShowServiceProgressDialog(
WindowHandle,
L"continue",
L"Continuing a service might prevent the system from functioning properly.",
FALSE,
Services,
NumberOfServices,
PhUiServiceContinueCallback,
PhSvcControlServiceContinue
);
return FALSE;
}
if (!PhpShowContinueMessageServices(
WindowHandle,
L"continue",
L"Continuing a service might prevent the system from functioning properly.",
FALSE,
Services,
NumberOfServices
))
return FALSE;
for (i = 0; i < NumberOfServices; i++)
{
NTSTATUS status;
SC_HANDLE serviceHandle;
success = FALSE;
status = PhOpenService(&serviceHandle, SERVICE_PAUSE_CONTINUE, PhGetString(Services[i]->Name));
if (NT_SUCCESS(status))
{
status = PhContinueService(serviceHandle);
if (NT_SUCCESS(status))
success = TRUE;
PhCloseServiceHandle(serviceHandle);
}
if (!success)
{
BOOLEAN connected;
success = FALSE;
if (!cancelled && PhpShowErrorAndConnectToPhSvc(
WindowHandle,
PhaConcatStrings2(L"Unable to continue ", PhGetString(Services[i]->Name))->Buffer,
status,
&connected,
&cancelled
))
{
if (connected)
{
if (NT_SUCCESS(status = PhSvcCallControlService(PhGetString(Services[i]->Name), PhSvcControlServiceContinue)))
success = TRUE;
else
PhpShowErrorService(WindowHandle, L"continue", Services[i], status, 0);
PhUiDisconnectFromPhSvc();
}
else
{
cancelled = TRUE;
}
}
else
{
if (cancelled)
break;
if (!PhpShowErrorService(WindowHandle, L"continue", Services[i], status, 0))
break;
}
}
}
return success;
//BOOLEAN result = TRUE;
//ULONG i;
//
//for (i = 0; i < NumberOfServices; i++)
//{
// SC_HANDLE serviceHandle;
// BOOLEAN success = FALSE;
//
// serviceHandle = PhOpenService(PhGetString(Services[i]->Name), SERVICE_PAUSE_CONTINUE);
//
// if (serviceHandle)
// {
// SERVICE_STATUS serviceStatus;
//
// if (ControlService(serviceHandle, SERVICE_CONTROL_CONTINUE, &serviceStatus))
// success = TRUE;
//
// PhCloseServiceHandle(serviceHandle);
// }
//
// if (!success)
// {
// NTSTATUS status;
//
// status = PhGetLastWin32ErrorAsNtStatus();
// result = FALSE;
//
// PhpShowErrorService(WindowHandle, L"continue", Services[i], status, 0);
// }
//}
//
//return result;
}
BOOLEAN PhUiContinueService(
_In_ HWND WindowHandle,
_In_ PPH_SERVICE_ITEM Service
)
{
SC_HANDLE serviceHandle;
NTSTATUS status;
BOOLEAN success = FALSE;
status = PhOpenService(&serviceHandle, SERVICE_PAUSE_CONTINUE, PhGetString(Service->Name));
if (NT_SUCCESS(status))
{
status = PhContinueService(serviceHandle);
if (NT_SUCCESS(status))
success = TRUE;
PhCloseServiceHandle(serviceHandle);
}
if (!success)
{
BOOLEAN connected;
BOOLEAN cancelled;
if (PhpShowErrorAndConnectToPhSvc(
WindowHandle,
PhaConcatStrings2(L"Unable to continue ", PhGetString(Service->Name))->Buffer,
status,
&connected,
&cancelled
))
{
if (connected)
{
if (NT_SUCCESS(status = PhSvcCallControlService(PhGetString(Service->Name), PhSvcControlServiceContinue)))
success = TRUE;
else
PhpShowErrorService(WindowHandle, L"continue", Service, status, 0);
PhUiDisconnectFromPhSvc();
}
}
else
{
if (!cancelled)
{
PhpShowErrorService(WindowHandle, L"continue", Service, status, 0);
}
}
}
return success;
}
_Function_class_(USER_THREAD_START_ROUTINE)
static NTSTATUS PhUiServicePauseCallback(
_In_ PPH_SERVICE_ITEM ServiceItem
)
{
NTSTATUS status;
SC_HANDLE serviceHandle;
status = PhOpenService(
&serviceHandle,
SERVICE_PAUSE_CONTINUE,
PhGetString(ServiceItem->Name)
);
if (NT_SUCCESS(status))
{
status = PhPauseService(serviceHandle);
PhCloseServiceHandle(serviceHandle);
}
return status;
}
BOOLEAN PhUiPauseServices(
_In_ HWND WindowHandle,
_In_ PPH_SERVICE_ITEM* Services,
_In_ ULONG NumberOfServices
)
{
BOOLEAN success = TRUE;
BOOLEAN cancelled = FALSE;
ULONG i;
if (PhGetIntegerSetting(SETTING_ENABLE_SERVICE_PROGRESS_DIALOG))
{
PhShowServiceProgressDialog(
WindowHandle,
L"pause",
L"Pausing a service might prevent the system from functioning properly.",
FALSE,
Services,
NumberOfServices,
PhUiServicePauseCallback,
PhSvcControlServicePause
);
return FALSE;
}
if (!PhpShowContinueMessageServices(
WindowHandle,
L"pause",
L"Pausing a service might prevent the system from functioning properly.",
FALSE,
Services,
NumberOfServices
))
return FALSE;
for (i = 0; i < NumberOfServices; i++)
{
NTSTATUS status;
SC_HANDLE serviceHandle;
success = FALSE;
status = PhOpenService(&serviceHandle, SERVICE_PAUSE_CONTINUE, PhGetString(Services[i]->Name));
if (NT_SUCCESS(status))
{
status = PhPauseService(serviceHandle);
if (NT_SUCCESS(status))
success = TRUE;
PhCloseServiceHandle(serviceHandle);
}
if (!success)
{
BOOLEAN connected;
success = FALSE;
if (!cancelled && PhpShowErrorAndConnectToPhSvc(
WindowHandle,
PhaConcatStrings2(L"Unable to pause ", PhGetString(Services[i]->Name))->Buffer,
status,
&connected,
&cancelled
))
{
if (connected)
{
if (NT_SUCCESS(status = PhSvcCallControlService(PhGetString(Services[i]->Name), PhSvcControlServicePause)))
success = TRUE;
else
PhpShowErrorService(WindowHandle, L"pause", Services[i], status, 0);
PhUiDisconnectFromPhSvc();
}
else
{
cancelled = TRUE;
}
}
else
{
if (cancelled)
break;
if (!PhpShowErrorService(WindowHandle, L"pause", Services[i], status, 0))
break;
}
}
}
return success;
//BOOLEAN result = TRUE;
//ULONG i;
//
//for (i = 0; i < NumberOfServices; i++)
//{
// SC_HANDLE serviceHandle;
// BOOLEAN success = FALSE;
//
// serviceHandle = PhOpenService(PhGetString(Services[i]->Name), SERVICE_PAUSE_CONTINUE);
//
// if (serviceHandle)
// {
// SERVICE_STATUS serviceStatus;
//
// if (ControlService(serviceHandle, SERVICE_CONTROL_PAUSE, &serviceStatus))
// success = TRUE;
//
// PhCloseServiceHandle(serviceHandle);
// }
//
// if (!success)
// {
// NTSTATUS status;
//
// status = PhGetLastWin32ErrorAsNtStatus();
// result = FALSE;
//
// PhpShowErrorService(WindowHandle, L"pause", Services[i], status, 0);
// }
//}
//
//return result;
}
BOOLEAN PhUiPauseService(
_In_ HWND WindowHandle,
_In_ PPH_SERVICE_ITEM Service
)
{
SC_HANDLE serviceHandle;
NTSTATUS status;
BOOLEAN success = FALSE;
status = PhOpenService(&serviceHandle, SERVICE_PAUSE_CONTINUE, PhGetString(Service->Name));
if (NT_SUCCESS(status))
{
status = PhPauseService(serviceHandle);
if (NT_SUCCESS(status))
success = TRUE;
PhCloseServiceHandle(serviceHandle);
}
if (!success)
{
BOOLEAN connected;
BOOLEAN cancelled;
if (PhpShowErrorAndConnectToPhSvc(
WindowHandle,
PhaConcatStrings2(L"Unable to pause ", Service->Name->Buffer)->Buffer,
status,
&connected,
&cancelled
))
{
if (connected)
{
if (NT_SUCCESS(status = PhSvcCallControlService(PhGetString(Service->Name), PhSvcControlServicePause)))
success = TRUE;
else
PhpShowErrorService(WindowHandle, L"pause", Service, status, 0);
PhUiDisconnectFromPhSvc();
}
}
else
{
if (!cancelled)
{
PhpShowErrorService(WindowHandle, L"pause", Service, status, 0);
}
}
}
return success;
}
_Function_class_(USER_THREAD_START_ROUTINE)
static NTSTATUS PhUiServiceStopCallback(
_In_ PPH_SERVICE_ITEM ServiceItem
)
{
NTSTATUS status;
SC_HANDLE serviceHandle;
status = PhOpenService(
&serviceHandle,
SERVICE_STOP,
PhGetString(ServiceItem->Name)
);
if (NT_SUCCESS(status))
{
status = PhStopService(serviceHandle);
PhCloseServiceHandle(serviceHandle);
}
return status;
}
BOOLEAN PhUiStopServices(
_In_ HWND WindowHandle,
_In_ PPH_SERVICE_ITEM* Services,
_In_ ULONG NumberOfServices
)
{
BOOLEAN success = TRUE;
BOOLEAN cancelled = FALSE;
ULONG i;
if (PhGetIntegerSetting(SETTING_ENABLE_SERVICE_PROGRESS_DIALOG))
{
PhShowServiceProgressDialog(
WindowHandle,
L"stop",
L"Stopping a service might prevent the system from functioning properly.",
FALSE,
Services,
NumberOfServices,
PhUiServiceStopCallback,
PhSvcControlServiceStop
);
return FALSE;
}
if (!PhpShowContinueMessageServices(
WindowHandle,
L"stop",
L"Stopping a service might prevent the system from functioning properly.",
FALSE,
Services,
NumberOfServices
))
return FALSE;
for (i = 0; i < NumberOfServices; i++)
{
NTSTATUS status;
SC_HANDLE serviceHandle;
success = FALSE;
status = PhOpenService(&serviceHandle, SERVICE_STOP, PhGetString(Services[i]->Name));
if (NT_SUCCESS(status))
{
status = PhStopService(serviceHandle);
if (NT_SUCCESS(status))
success = TRUE;
PhCloseServiceHandle(serviceHandle);
}
if (!success)
{
BOOLEAN connected;
success = FALSE;
if (!cancelled && PhpShowErrorAndConnectToPhSvc(
WindowHandle,
PhaConcatStrings2(L"Unable to stop ", PhGetString(Services[i]->Name))->Buffer,
status,
&connected,
&cancelled
))
{
if (connected)
{
if (NT_SUCCESS(status = PhSvcCallControlService(PhGetString(Services[i]->Name), PhSvcControlServiceStop)))
success = TRUE;
else
PhpShowErrorService(WindowHandle, L"stop", Services[i], status, 0);
PhUiDisconnectFromPhSvc();
}
else
{
cancelled = TRUE;
}
}
else
{
if (cancelled)
break;
if (!PhpShowErrorService(WindowHandle, L"stop", Services[i], status, 0))
break;
}
}
}
return success;
//BOOLEAN result = TRUE;
//ULONG i;
//
//for (i = 0; i < NumberOfServices; i++)
//{
// SC_HANDLE serviceHandle;
// BOOLEAN success = FALSE;
//
// serviceHandle = PhOpenService(PhGetString(Services[i]->Name), SERVICE_STOP);
//
// if (serviceHandle)
// {
// SERVICE_STATUS serviceStatus;
//
// if (ControlService(serviceHandle, SERVICE_CONTROL_STOP, &serviceStatus))
// success = TRUE;
//
// PhCloseServiceHandle(serviceHandle);
// }
//
// if (!success)
// {
// NTSTATUS status;
//
// status = PhGetLastWin32ErrorAsNtStatus();
// result = FALSE;
//
// PhpShowErrorService(WindowHandle, L"stop", Services[i], status, 0);
// }
//}
//
//return result;
}
BOOLEAN PhUiStopService(
_In_ HWND WindowHandle,
_In_ PPH_SERVICE_ITEM Service
)
{
SC_HANDLE serviceHandle;
NTSTATUS status;
BOOLEAN success = FALSE;
status = PhOpenService(&serviceHandle, SERVICE_STOP, PhGetString(Service->Name));
if (NT_SUCCESS(status))
{
status = PhStopService(serviceHandle);
if (NT_SUCCESS(status))
success = TRUE;
PhCloseServiceHandle(serviceHandle);
}
if (!success)
{
BOOLEAN connected;
BOOLEAN cancelled;
if (PhpShowErrorAndConnectToPhSvc(
WindowHandle,
PhaConcatStrings2(L"Unable to stop ", PhGetString(Service->Name))->Buffer,
status,
&connected,
&cancelled
))
{
if (connected)
{
if (NT_SUCCESS(status = PhSvcCallControlService(PhGetString(Service->Name), PhSvcControlServiceStop)))
success = TRUE;
else
PhpShowErrorService(WindowHandle, L"stop", Service, status, 0);
PhUiDisconnectFromPhSvc();
}
}
else
{
PhpShowErrorService(WindowHandle, L"stop", Service, status, 0);
}
}
return success;
}
BOOLEAN PhUiDeleteService(
_In_ HWND WindowHandle,
_In_ PPH_SERVICE_ITEM Service
)
{
SC_HANDLE serviceHandle;
NTSTATUS status;
BOOLEAN success = FALSE;
// Warnings cannot be disabled for service deletion.
if (!PhShowConfirmMessage(
WindowHandle,
L"delete",
Service->Name->Buffer,
L"Deleting a service can prevent the system from starting "
L"or functioning properly.",
TRUE
))
return FALSE;
status = PhOpenService(&serviceHandle, DELETE, PhGetString(Service->Name));
if (NT_SUCCESS(status))
{
status = PhDeleteService(serviceHandle);
if (NT_SUCCESS(status))
success = TRUE;
PhCloseServiceHandle(serviceHandle);
}
if (!success)
{
BOOLEAN connected;
BOOLEAN cancelled;
if (PhpShowErrorAndConnectToPhSvc(
WindowHandle,
PhaConcatStrings2(L"Unable to delete ", PhGetString(Service->Name))->Buffer,
status,
&connected,
&cancelled
))
{
if (connected)
{
if (NT_SUCCESS(status = PhSvcCallControlService(PhGetString(Service->Name), PhSvcControlServiceDelete)))
success = TRUE;
else
PhpShowErrorService(WindowHandle, L"delete", Service, status, 0);
PhUiDisconnectFromPhSvc();
}
}
else
{
PhpShowErrorService(WindowHandle, L"delete", Service, status, 0);
}
}
return success;
}
_Function_class_(USER_THREAD_START_ROUTINE)
static NTSTATUS PhUiServiceRestartCallback(
_In_ PPH_SERVICE_ITEM ServiceItem
)
{
NTSTATUS status;
SC_HANDLE serviceHandle;
status = PhOpenService(
&serviceHandle,
SERVICE_QUERY_STATUS | SERVICE_START | SERVICE_STOP,
PhGetString(ServiceItem->Name)
);
if (NT_SUCCESS(status))
{
status = PhStopService(serviceHandle);
if (NT_SUCCESS(status))
{
status = PhWaitForServiceStatus(
serviceHandle,
SERVICE_STOPPED,
60 * 1000
);
if (NT_SUCCESS(status))
{
status = PhStartService(serviceHandle, 0, NULL);
if (NT_SUCCESS(status))
{
status = PhWaitForServiceStatus(
serviceHandle,
SERVICE_RUNNING,
60 * 1000
);
}
}
}
PhCloseServiceHandle(serviceHandle);
}
return status;
}
BOOLEAN PhUiRestartServices(
_In_ HWND WindowHandle,
_In_ PPH_SERVICE_ITEM* Services,
_In_ ULONG NumberOfServices
)
{
BOOLEAN success = TRUE;
BOOLEAN cancelled = FALSE;
ULONG i;
if (PhGetIntegerSetting(SETTING_ENABLE_SERVICE_PROGRESS_DIALOG))
{
PhShowServiceProgressDialog(
WindowHandle,
L"restart",
L"Restarting a service might prevent the system from functioning properly.",
FALSE,
Services,
NumberOfServices,
PhUiServiceRestartCallback,
PhSvcControlServiceRestart
);
return FALSE;
}
if (!PhpShowContinueMessageServices(
WindowHandle,
L"restart",
L"Restarting a service might prevent the system from functioning properly.",
FALSE,
Services,
NumberOfServices
))
return FALSE;
for (i = 0; i < NumberOfServices; i++)
{
NTSTATUS status;
SC_HANDLE serviceHandle;
success = FALSE;
status = PhOpenService(
&serviceHandle,
SERVICE_QUERY_STATUS | SERVICE_START | SERVICE_STOP,
PhGetString(Services[i]->Name)
);
if (NT_SUCCESS(status))
{
status = PhStopService(serviceHandle);
if (NT_SUCCESS(status))
{
status = PhWaitForServiceStatus(
serviceHandle,
SERVICE_STOPPED,
60 * 1000
);
if (NT_SUCCESS(status))
{
status = PhStartService(serviceHandle, 0, NULL);
if (NT_SUCCESS(status))
{
status = PhWaitForServiceStatus(
serviceHandle,
SERVICE_RUNNING,
60 * 1000
);
if (NT_SUCCESS(status))
{
success = TRUE;
}
}
}
}
PhCloseServiceHandle(serviceHandle);
}
if (!success)
{
BOOLEAN connected;
success = FALSE;
if (!cancelled && PhpShowErrorAndConnectToPhSvc(
WindowHandle,
PhaConcatStrings2(L"Unable to restart ", PhGetString(Services[i]->Name))->Buffer,
status,
&connected,
&cancelled
))
{
if (connected)
{
if (NT_SUCCESS(status = PhSvcCallControlService(PhGetString(Services[i]->Name), PhSvcControlServiceRestart)))
success = TRUE;
else
PhpShowErrorService(WindowHandle, L"restart", Services[i], status, 0);
PhUiDisconnectFromPhSvc();
}
else
{
cancelled = TRUE;
}
}
else
{
if (cancelled)
break;
if (!PhpShowErrorService(WindowHandle, L"restart", Services[i], status, 0))
break;
}
}
}
return success;
}
BOOLEAN PhUiCloseConnections(
_In_ HWND WindowHandle,
_In_ PPH_NETWORK_ITEM *Connections,
_In_ ULONG NumberOfConnections
)
{
static ULONG (WINAPI* SetTcpEntry_I)(_In_ PMIB_TCPROW pTcpRow) = NULL;
BOOLEAN success = TRUE;
BOOLEAN cancelled = FALSE;
ULONG result;
ULONG i;
MIB_TCPROW tcpRow;
if (!SetTcpEntry_I)
{
SetTcpEntry_I = PhGetDllProcedureAddressZ(L"iphlpapi.dll", "SetTcpEntry", 0);
}
if (!SetTcpEntry_I)
{
PhShowStatus(WindowHandle, L"Unable to close the TCP connection", STATUS_NOT_SUPPORTED, 0);
return FALSE;
}
for (i = 0; i < NumberOfConnections; i++)
{
if (Connections[i]->State != MIB_TCP_STATE_ESTAB)
continue;
result = PhSetTcpEntry(Connections[i]);
if (NT_SUCCESS(result))
continue;
if (Connections[i]->ProtocolType != PH_NETWORK_PROTOCOL_TCP4)
continue;
tcpRow.dwState = MIB_TCP_STATE_DELETE_TCB;
tcpRow.dwLocalAddr = Connections[i]->LocalEndpoint.Address.InAddr.s_addr;
tcpRow.dwLocalPort = _byteswap_ushort((USHORT)Connections[i]->LocalEndpoint.Port);
tcpRow.dwRemoteAddr = Connections[i]->RemoteEndpoint.Address.InAddr.s_addr;
tcpRow.dwRemotePort = _byteswap_ushort((USHORT)Connections[i]->RemoteEndpoint.Port);
if ((result = SetTcpEntry_I(&tcpRow)) != NO_ERROR)
{
NTSTATUS status;
BOOLEAN connected;
success = FALSE;
// SetTcpEntry returns ERROR_MR_MID_NOT_FOUND for access denied errors for some reason.
if (result == ERROR_MR_MID_NOT_FOUND)
result = ERROR_ACCESS_DENIED;
if (!cancelled && PhpShowErrorAndConnectToPhSvc(
WindowHandle,
L"Unable to close the TCP connection",
PhDosErrorToNtStatus(result),
&connected,
&cancelled
))
{
if (connected)
{
if (NT_SUCCESS(status = PhSvcCallSetTcpEntry(&tcpRow)))
success = TRUE;
else
PhShowStatus(WindowHandle, L"Unable to close the TCP connection", status, 0);
PhUiDisconnectFromPhSvc();
}
else
{
cancelled = TRUE;
}
}
else
{
if (cancelled)
break;
if (PhShowMessage2(
WindowHandle,
TD_OK_BUTTON,
TD_ERROR_ICON,
L"Unable to close the TCP connection.",
L"Make sure System Informer is running with administrative privileges."
) != IDOK)
break;
}
}
}
return success;
}
static BOOLEAN PhpShowContinueMessageThreads(
_In_ HWND WindowHandle,
_In_ PWSTR Verb,
_In_ PWSTR Message,
_In_ BOOLEAN Warning,
_In_ PPH_THREAD_ITEM *Threads,
_In_ ULONG NumberOfThreads
)
{
PWSTR object;
BOOLEAN cont = FALSE;
if (NumberOfThreads == 0)
return FALSE;
if (PhGetIntegerSetting(SETTING_ENABLE_WARNINGS))
{
if (NumberOfThreads == 1)
{
object = L"the selected thread";
}
else
{
object = L"the selected threads";
}
cont = PhShowConfirmMessage(
WindowHandle,
Verb,
object,
Message,
Warning
);
}
else
{
cont = TRUE;
}
return cont;
}
static BOOLEAN PhpShowErrorThread(
_In_ HWND WindowHandle,
_In_ PWSTR Verb,
_In_ PPH_THREAD_ITEM Thread,
_In_ NTSTATUS Status,
_In_opt_ ULONG Win32Result
)
{
return PhShowContinueStatus(
WindowHandle,
PhaFormatString(
L"Unable to %s thread %lu",
Verb,
HandleToUlong(Thread->ThreadId)
)->Buffer,
Status,
Win32Result
);
}
BOOLEAN PhUiTerminateThreads(
_In_ HWND WindowHandle,
_In_ PPH_THREAD_ITEM *Threads,
_In_ ULONG NumberOfThreads
)
{
BOOLEAN success = TRUE;
BOOLEAN cancelled = FALSE;
ULONG i;
if (!PhpShowContinueMessageThreads(
WindowHandle,
L"terminate",
L"Terminating a thread may cause the process to stop working.",
FALSE,
Threads,
NumberOfThreads
))
return FALSE;
for (i = 0; i < NumberOfThreads; i++)
{
NTSTATUS status;
HANDLE threadHandle;
if (NT_SUCCESS(status = PhOpenThread(
&threadHandle,
THREAD_TERMINATE,
Threads[i]->ThreadId
)))
{
status = PhTerminateThread(threadHandle, STATUS_SUCCESS);
if (status == STATUS_SUCCESS || status == STATUS_THREAD_IS_TERMINATING)
PhTerminateThread(threadHandle, DBG_TERMINATE_THREAD); // debug terminate (dmex)
NtClose(threadHandle);
}
if (!NT_SUCCESS(status))
{
BOOLEAN connected;
success = FALSE;
if (!cancelled && PhpShowErrorAndConnectToPhSvc(
WindowHandle,
PhaFormatString(L"Unable to terminate thread %lu", HandleToUlong(Threads[i]->ThreadId))->Buffer,
status,
&connected,
&cancelled
))
{
if (connected)
{
if (NT_SUCCESS(status = PhSvcCallControlThread(Threads[i]->ThreadId, PhSvcControlThreadTerminate, 0)))
success = TRUE;
else
PhpShowErrorThread(WindowHandle, L"terminate", Threads[i], status, 0);
PhUiDisconnectFromPhSvc();
}
else
{
cancelled = TRUE;
}
}
else
{
if (cancelled)
break;
if (!PhpShowErrorThread(WindowHandle, L"terminate", Threads[i], status, 0))
break;
}
}
}
return success;
}
BOOLEAN PhUiSuspendThreads(
_In_ HWND WindowHandle,
_In_ PPH_THREAD_ITEM *Threads,
_In_ ULONG NumberOfThreads
)
{
BOOLEAN success = TRUE;
BOOLEAN cancelled = FALSE;
ULONG i;
for (i = 0; i < NumberOfThreads; i++)
{
NTSTATUS status;
HANDLE threadHandle;
if (NT_SUCCESS(status = PhOpenThread(
&threadHandle,
THREAD_SUSPEND_RESUME,
Threads[i]->ThreadId
)))
{
status = NtSuspendThread(threadHandle, NULL);
NtClose(threadHandle);
}
if (!NT_SUCCESS(status))
{
BOOLEAN connected;
success = FALSE;
if (!cancelled && PhpShowErrorAndConnectToPhSvc(
WindowHandle,
PhaFormatString(L"Unable to suspend thread %lu", HandleToUlong(Threads[i]->ThreadId))->Buffer,
status,
&connected,
&cancelled
))
{
if (connected)
{
if (NT_SUCCESS(status = PhSvcCallControlThread(Threads[i]->ThreadId, PhSvcControlThreadSuspend, 0)))
success = TRUE;
else
PhpShowErrorThread(WindowHandle, L"suspend", Threads[i], status, 0);
PhUiDisconnectFromPhSvc();
}
else
{
cancelled = TRUE;
}
}
else
{
if (cancelled)
break;
if (!PhpShowErrorThread(WindowHandle, L"suspend", Threads[i], status, 0))
break;
}
}
}
return success;
}
BOOLEAN PhUiResumeThreads(
_In_ HWND WindowHandle,
_In_ PPH_THREAD_ITEM *Threads,
_In_ ULONG NumberOfThreads
)
{
BOOLEAN success = TRUE;
BOOLEAN cancelled = FALSE;
ULONG i;
for (i = 0; i < NumberOfThreads; i++)
{
NTSTATUS status;
HANDLE threadHandle;
if (NT_SUCCESS(status = PhOpenThread(
&threadHandle,
THREAD_SUSPEND_RESUME,
Threads[i]->ThreadId
)))
{
status = NtResumeThread(threadHandle, NULL);
NtClose(threadHandle);
}
if (!NT_SUCCESS(status))
{
BOOLEAN connected;
success = FALSE;
if (!cancelled && PhpShowErrorAndConnectToPhSvc(
WindowHandle,
PhaFormatString(L"Unable to resume thread %lu", HandleToUlong(Threads[i]->ThreadId))->Buffer,
status,
&connected,
&cancelled
))
{
if (connected)
{
if (NT_SUCCESS(status = PhSvcCallControlThread(Threads[i]->ThreadId, PhSvcControlThreadResume, 0)))
success = TRUE;
else
PhpShowErrorThread(WindowHandle, L"resume", Threads[i], status, 0);
PhUiDisconnectFromPhSvc();
}
else
{
cancelled = TRUE;
}
}
else
{
if (cancelled)
break;
if (!PhpShowErrorThread(WindowHandle, L"resume", Threads[i], status, 0))
break;
}
}
}
return success;
}
BOOLEAN PhUiSetBoostPriorityThreads(
_In_ HWND WindowHandle,
_In_ PPH_THREAD_ITEM *Threads,
_In_ ULONG NumberOfThreads,
_In_ BOOLEAN PriorityBoost
)
{
BOOLEAN success = TRUE;
ULONG i;
for (i = 0; i < NumberOfThreads; i++)
{
NTSTATUS status;
HANDLE threadHandle;
status = PhOpenThread(
&threadHandle,
THREAD_SET_LIMITED_INFORMATION,
Threads[i]->ThreadId
);
if (NT_SUCCESS(status))
{
status = PhSetThreadPriorityBoost(threadHandle, PriorityBoost);
NtClose(threadHandle);
}
if (!NT_SUCCESS(status))
{
success = FALSE;
if (!PhpShowErrorThread(WindowHandle, L"change boost priority of", Threads[i], status, 0))
break;
}
}
return success;
}
BOOLEAN PhUiSetBoostPriorityThread(
_In_ HWND WindowHandle,
_In_ PPH_THREAD_ITEM Thread,
_In_ BOOLEAN PriorityBoost
)
{
NTSTATUS status;
HANDLE threadHandle;
if (NT_SUCCESS(status = PhOpenThread(
&threadHandle,
THREAD_SET_LIMITED_INFORMATION,
Thread->ThreadId
)))
{
status = PhSetThreadPriorityBoost(threadHandle, PriorityBoost);
NtClose(threadHandle);
}
if (!NT_SUCCESS(status))
{
PhpShowErrorThread(WindowHandle, L"set the boost priority of", Thread, status, 0);
return FALSE;
}
return TRUE;
}
BOOLEAN PhUiSetPriorityThreads(
_In_ HWND WindowHandle,
_In_ PPH_THREAD_ITEM *Threads,
_In_ ULONG NumberOfThreads,
_In_ LONG Increment
)
{
BOOLEAN success = TRUE;
ULONG i;
// Special saturation values
if (Increment == THREAD_PRIORITY_TIME_CRITICAL)
Increment = THREAD_BASE_PRIORITY_LOWRT + 1;
else if (Increment == THREAD_PRIORITY_IDLE)
Increment = THREAD_BASE_PRIORITY_IDLE - 1;
for (i = 0; i < NumberOfThreads; i++)
{
NTSTATUS status;
HANDLE threadHandle;
status = PhOpenThread(
&threadHandle,
THREAD_SET_LIMITED_INFORMATION,
Threads[i]->ThreadId
);
if (NT_SUCCESS(status))
{
status = PhSetThreadBasePriority(threadHandle, Increment);
NtClose(threadHandle);
}
if (!NT_SUCCESS(status))
{
success = FALSE;
if (!PhpShowErrorThread(WindowHandle, L"change priority of", Threads[i], status, 0))
break;
}
}
return success;
}
BOOLEAN PhUiSetPriorityThread(
_In_ HWND WindowHandle,
_In_ PPH_THREAD_ITEM Thread,
_In_ LONG Increment
)
{
NTSTATUS status;
HANDLE threadHandle;
if (NT_SUCCESS(status = PhOpenThread(
&threadHandle,
THREAD_SET_LIMITED_INFORMATION,
Thread->ThreadId
)))
{
status = PhSetThreadBasePriority(threadHandle, Increment);
NtClose(threadHandle);
}
if (!NT_SUCCESS(status))
{
PhpShowErrorThread(WindowHandle, L"set the priority of", Thread, status, 0);
return FALSE;
}
return TRUE;
}
BOOLEAN PhUiSetIoPriorityThread(
_In_ HWND WindowHandle,
_In_ PPH_THREAD_ITEM Thread,
_In_ IO_PRIORITY_HINT IoPriority
)
{
NTSTATUS status;
BOOLEAN success = TRUE;
HANDLE threadHandle;
if (NT_SUCCESS(status = PhOpenThread(
&threadHandle,
THREAD_SET_INFORMATION,
Thread->ThreadId
)))
{
status = PhSetThreadIoPriority(threadHandle, IoPriority);
NtClose(threadHandle);
}
if (!NT_SUCCESS(status))
{
BOOLEAN connected;
BOOLEAN cancelled;
success = FALSE;
// The operation may have failed due to the lack of SeIncreaseBasePriorityPrivilege.
if (PhpShowErrorAndConnectToPhSvc(
WindowHandle,
PhaFormatString(L"Unable to set the I/O priority of thread %lu", HandleToUlong(Thread->ThreadId))->Buffer,
status,
&connected,
&cancelled
))
{
if (connected)
{
if (NT_SUCCESS(status = PhSvcCallControlThread(Thread->ThreadId, PhSvcControlThreadIoPriority, IoPriority)))
success = TRUE;
else
PhpShowErrorThread(WindowHandle, L"set the I/O priority of", Thread, status, 0);
PhUiDisconnectFromPhSvc();
}
}
else
{
PhpShowErrorThread(WindowHandle, L"set the I/O priority of", Thread, status, 0);
}
}
return success;
}
BOOLEAN PhUiSetPagePriorityThread(
_In_ HWND WindowHandle,
_In_ PPH_THREAD_ITEM Thread,
_In_ ULONG PagePriority
)
{
NTSTATUS status;
HANDLE threadHandle;
if (NT_SUCCESS(status = PhOpenThread(
&threadHandle,
THREAD_SET_INFORMATION,
Thread->ThreadId
)))
{
status = PhSetThreadPagePriority(threadHandle, PagePriority);
NtClose(threadHandle);
}
if (!NT_SUCCESS(status))
{
PhpShowErrorThread(WindowHandle, L"set the page priority of", Thread, status, 0);
return FALSE;
}
return TRUE;
}
BOOLEAN PhUiUnloadModule(
_In_ HWND WindowHandle,
_In_ HANDLE ProcessId,
_In_ PPH_MODULE_ITEM Module
)
{
NTSTATUS status = STATUS_UNSUCCESSFUL;
HANDLE processHandle = NULL;
BOOLEAN cont = FALSE;
if (PhGetIntegerSetting(SETTING_ENABLE_WARNINGS))
{
PWSTR verb;
PWSTR message;
switch (Module->Type)
{
case PH_MODULE_TYPE_MODULE:
case PH_MODULE_TYPE_WOW64_MODULE:
verb = L"unload";
message = L"Unloading a module may cause the process to crash.";
if (WindowsVersion >= WINDOWS_8)
message = L"Unloading a module may cause the process to crash. NOTE: This feature may not work correctly on your version of Windows and some programs may restrict access or ban your account.";
break;
case PH_MODULE_TYPE_KERNEL_MODULE:
verb = L"unload";
message = L"Unloading a driver may cause system instability.";
break;
case PH_MODULE_TYPE_MAPPED_FILE:
case PH_MODULE_TYPE_MAPPED_IMAGE:
verb = L"unmap";
message = L"Unmapping a section view may cause the process to crash.";
break;
default:
return FALSE;
}
cont = PhShowConfirmMessage(
WindowHandle,
verb,
Module->Name->Buffer,
message,
TRUE
);
}
else
{
cont = TRUE;
}
if (!cont)
return FALSE;
switch (Module->Type)
{
case PH_MODULE_TYPE_MODULE:
case PH_MODULE_TYPE_WOW64_MODULE:
{
if (WindowsVersion < WINDOWS_8)
{
// Windows 7 requires QUERY_INFORMATION for MemoryMappedFileName. (dmex)
status = PhOpenProcess(
&processHandle,
PROCESS_QUERY_INFORMATION | PROCESS_SET_LIMITED_INFORMATION |
PROCESS_CREATE_THREAD | PROCESS_VM_OPERATION |
PROCESS_VM_READ | PROCESS_VM_WRITE,
ProcessId
);
}
else if (WindowsVersion < WINDOWS_10)
{
// Windows 8 requires ALL_ACCESS for PLM execution requests. (dmex)
status = PhOpenProcess(
&processHandle,
PROCESS_ALL_ACCESS,
ProcessId
);
}
if (!NT_SUCCESS(status))
{
// Windows 10 and above require SET_LIMITED for PLM execution requests. (dmex)
status = PhOpenProcess(
&processHandle,
PROCESS_QUERY_LIMITED_INFORMATION | PROCESS_SET_LIMITED_INFORMATION |
PROCESS_CREATE_THREAD | PROCESS_VM_OPERATION |
PROCESS_VM_READ | PROCESS_VM_WRITE,
ProcessId
);
}
if (NT_SUCCESS(status))
{
status = PhUnloadDllProcess(
processHandle,
Module->BaseAddress,
5000
);
NtClose(processHandle);
}
if (status == STATUS_DLL_NOT_FOUND)
{
PhShowStatus(WindowHandle, L"Unable to unload the module", 0, ERROR_MOD_NOT_FOUND);
return FALSE;
}
if (!NT_SUCCESS(status))
{
PhShowStatus(
WindowHandle,
PhaConcatStrings2(L"Unable to unload ", Module->Name->Buffer)->Buffer,
status,
0
);
return FALSE;
}
}
break;
case PH_MODULE_TYPE_KERNEL_MODULE:
status = PhUnloadDriver(Module->BaseAddress, &Module->Name->sr, &Module->FileName->sr);
if (!NT_SUCCESS(status))
{
BOOLEAN success = FALSE;
BOOLEAN connected;
BOOLEAN cancelled;
if (PhpShowErrorAndConnectToPhSvc(
WindowHandle,
PhaConcatStrings2(L"Unable to unload ", Module->Name->Buffer)->Buffer,
status,
&connected,
&cancelled
))
{
if (connected)
{
if (NT_SUCCESS(status = PhSvcCallUnloadDriver(Module->BaseAddress, Module->Name->Buffer, Module->FileName->Buffer)))
success = TRUE;
else
PhShowStatus(WindowHandle, PhaConcatStrings2(L"Unable to unload ", Module->Name->Buffer)->Buffer, status, 0);
PhUiDisconnectFromPhSvc();
}
}
else
{
if (cancelled)
return FALSE;
PhShowStatus(
WindowHandle,
PhaConcatStrings(
3,
L"Unable to unload ",
Module->Name->Buffer,
L". Make sure System Informer is running with "
L"administrative privileges."
)->Buffer,
status,
0
);
return FALSE;
}
return success;
}
break;
case PH_MODULE_TYPE_MAPPED_FILE:
case PH_MODULE_TYPE_MAPPED_IMAGE:
if (NT_SUCCESS(status = PhOpenProcess(
&processHandle,
PROCESS_VM_OPERATION,
ProcessId
)))
{
status = PhUnmapViewOfSection(processHandle, Module->BaseAddress);
NtClose(processHandle);
}
if (!NT_SUCCESS(status))
{
PhShowStatus(
WindowHandle,
PhaFormatString(L"Unable to unmap the section view at 0x%p", Module->BaseAddress)->Buffer,
status,
0
);
return FALSE;
}
break;
default:
return FALSE;
}
return TRUE;
}
BOOLEAN PhUiFreeMemory(
_In_ HWND WindowHandle,
_In_ HANDLE ProcessId,
_In_ PPH_MEMORY_ITEM MemoryItem,
_In_ BOOLEAN Free
)
{
NTSTATUS status;
BOOLEAN cont = FALSE;
HANDLE processHandle;
if (PhGetIntegerSetting(SETTING_ENABLE_WARNINGS))
{
PWSTR verb;
PWSTR message;
if (!(MemoryItem->Type & (MEM_MAPPED | MEM_IMAGE)))
{
if (Free)
{
verb = L"free";
message = L"Freeing memory regions may cause the process to crash.\r\n\r\nSome programs may also restrict access or ban your account when freeing the memory of the process.";
}
else
{
verb = L"decommit";
message = L"Decommitting memory regions may cause the process to crash.\r\n\r\nSome programs may also restrict access or ban your account when decommitting the memory of the process.";
}
}
else
{
verb = L"unmap";
message = L"Unmapping a section view may cause the process to crash.\r\n\r\nSome programs may also restrict access or ban your account when unmapping the memory of the process.";
}
cont = PhShowConfirmMessage(
WindowHandle,
verb,
L"the memory region",
message,
TRUE
);
}
else
{
cont = TRUE;
}
if (!cont)
return FALSE;
if (NT_SUCCESS(status = PhOpenProcess(
&processHandle,
PROCESS_VM_OPERATION,
ProcessId
)))
{
PVOID baseAddress;
SIZE_T regionSize;
baseAddress = MemoryItem->BaseAddress;
if (!(MemoryItem->Type & (MEM_MAPPED | MEM_IMAGE)))
{
// The size needs to be 0 if we're freeing.
if (Free)
regionSize = 0;
else
regionSize = MemoryItem->RegionSize;
status = NtFreeVirtualMemory(
processHandle,
&baseAddress,
®ionSize,
Free ? MEM_RELEASE : MEM_DECOMMIT
);
}
else
{
status = PhUnmapViewOfSection(processHandle, baseAddress);
}
NtClose(processHandle);
}
if (!NT_SUCCESS(status))
{
PWSTR message;
if (!(MemoryItem->Type & (MEM_MAPPED | MEM_IMAGE)))
{
if (Free)
message = L"Unable to free the memory region";
else
message = L"Unable to decommit the memory region";
}
else
{
message = L"Unable to unmap the section view";
}
PhShowStatus(
WindowHandle,
message,
status,
0
);
return FALSE;
}
return TRUE;
}
BOOLEAN PhUiEmptyProcessMemoryWorkingSet(
_In_ HWND WindowHandle,
_In_ HANDLE ProcessId,
_In_ PPH_MEMORY_ITEM MemoryItem
)
{
NTSTATUS status;
HANDLE processHandle;
if (NT_SUCCESS(status = PhOpenProcess(
&processHandle,
PROCESS_VM_OPERATION,
ProcessId
)))
{
status = PhSetProcessEmptyPageWorkingSet(
processHandle,
MemoryItem->BaseAddress,
MemoryItem->RegionSize
);
NtClose(processHandle);
}
if (!NT_SUCCESS(status))
{
PhShowStatus(WindowHandle, L"Unable to empty the region working set.", status, 0);
return FALSE;
}
return TRUE;
}
static BOOLEAN PhpShowErrorHandle(
_In_ HWND WindowHandle,
_In_ PCWSTR Verb,
_In_ PCWSTR Verb2,
_In_ PPH_HANDLE_ITEM Handle,
_In_ NTSTATUS Status,
_In_opt_ ULONG Win32Result
)
{
WCHAR value[PH_PTR_STR_LEN_1];
PhPrintPointer(value, (PVOID)Handle->Handle);
if (!PhIsNullOrEmptyString(Handle->BestObjectName))
{
return PhShowContinueStatus(
WindowHandle,
PhaFormatString(
L"Unable to %s handle \"%s\" (%s)%s",
Verb,
Handle->BestObjectName->Buffer,
value,
Verb2
)->Buffer,
Status,
Win32Result
);
}
else
{
return PhShowContinueStatus(
WindowHandle,
PhaFormatString(
L"Unable to %s handle %s%s",
Verb,
value,
Verb2
)->Buffer,
Status,
Win32Result
);
}
}
BOOLEAN PhUiCloseHandles(
_In_ HWND WindowHandle,
_In_ HANDLE ProcessId,
_In_ PPH_HANDLE_ITEM *Handles,
_In_ ULONG NumberOfHandles,
_In_ BOOLEAN Warn
)
{
NTSTATUS status;
BOOLEAN result = FALSE;
BOOLEAN success = TRUE;
HANDLE processHandle;
if (NumberOfHandles == 0)
return FALSE;
if (Warn && PhGetIntegerSetting(SETTING_ENABLE_WARNINGS))
{
result = PhShowConfirmMessage(
WindowHandle,
L"close",
NumberOfHandles == 1 ? L"the selected handle" : L"the selected handles",
L"Closing handles may cause system instability and data corruption.",
FALSE
);
}
else
{
result = TRUE;
}
if (!result)
return FALSE;
if (NT_SUCCESS(status = PhOpenProcess(
&processHandle,
PROCESS_QUERY_INFORMATION | PROCESS_DUP_HANDLE,
ProcessId
)))
{
BOOLEAN critical = FALSE;
BOOLEAN strict = FALSE;
if (WindowsVersion >= WINDOWS_10)
{
BOOLEAN breakOnTermination;
PROCESS_MITIGATION_POLICY_INFORMATION policyInfo;
if (NT_SUCCESS(PhGetProcessBreakOnTermination(
processHandle,
&breakOnTermination
)))
{
if (breakOnTermination)
{
critical = TRUE;
}
}
if (NT_SUCCESS(PhGetProcessMitigationPolicy(
processHandle,
ProcessStrictHandleCheckPolicy,
&policyInfo
)))
{
if (policyInfo.StrictHandleCheckPolicy.Flags != 0)
{
strict = TRUE;
}
}
}
if (critical && strict)
{
result = PhShowConfirmMessage(
WindowHandle,
L"close",
L"critical process handle(s)",
L"You are about to close one or more handles for a critical process with strict handle checks enabled. This will shut down the operating system immediately.\r\n\r\n",
TRUE
);
}
if (!result)
return FALSE;
for (ULONG i = 0; i < NumberOfHandles; i++)
{
if (FlagOn(Handles[i]->Attributes, OBJ_PROTECT_CLOSE))
{
if (!PhpShowErrorHandle(
WindowHandle,
L"close",
NULL,
Handles[i],
STATUS_HANDLE_NOT_CLOSABLE,
0
))
{
break;
}
}
status = NtDuplicateObject(
processHandle,
Handles[i]->Handle,
NULL,
NULL,
0,
0,
DUPLICATE_CLOSE_SOURCE
);
if (!NT_SUCCESS(status))
{
success = FALSE;
if (!PhpShowErrorHandle(
WindowHandle,
L"close",
NULL,
Handles[i],
status,
0
))
break;
}
}
NtClose(processHandle);
}
else
{
PhShowStatus(WindowHandle, L"Unable to open the process", status, 0);
return FALSE;
}
return success;
}
BOOLEAN PhUiSetAttributesHandle(
_In_ HWND WindowHandle,
_In_ HANDLE ProcessId,
_In_ PPH_HANDLE_ITEM Handle,
_In_ ULONG Attributes
)
{
NTSTATUS status;
HANDLE processHandle;
if (KsiLevel() < KphLevelMax)
{
PhShowKsiNotConnected(
WindowHandle,
L"Setting handle attributes requires a connection to the kernel driver."
);
return FALSE;
}
if (NT_SUCCESS(status = PhOpenProcess(
&processHandle,
PROCESS_SET_INFORMATION,
ProcessId
)))
{
OBJECT_HANDLE_FLAG_INFORMATION handleFlagInfo;
handleFlagInfo.Inherit = !!(Attributes & OBJ_INHERIT);
handleFlagInfo.ProtectFromClose = !!(Attributes & OBJ_PROTECT_CLOSE);
status = KphSetInformationObject(
processHandle,
Handle->Handle,
KphObjectHandleFlagInformation,
&handleFlagInfo,
sizeof(OBJECT_HANDLE_FLAG_INFORMATION)
);
NtClose(processHandle);
}
if (!NT_SUCCESS(status))
{
PhpShowErrorHandle(WindowHandle, L"set attributes of", NULL, Handle, status, 0);
return FALSE;
}
return TRUE;
}
/**
* Flush process heap(s) remotely for a list of processes.
*
* \param WindowHandle Parent window used for error reporting.
* \param Processes Array of process items to operate on.
* \param NumberOfProcesses Number of processes in the array.
* \return BOOLEAN TRUE if all flush operations succeeded, FALSE if any failed.
*/
BOOLEAN PhUiFlushHeapProcesses(
_In_ HWND WindowHandle,
_In_ PPH_PROCESS_ITEM *Processes,
_In_ ULONG NumberOfProcesses
)
{
BOOLEAN success = TRUE;
ULONG i;
LARGE_INTEGER timeout;
for (i = 0; i < NumberOfProcesses; i++)
{
NTSTATUS status;
HANDLE processHandle;
status = PhOpenProcess(
&processHandle,
PROCESS_CREATE_THREAD | PROCESS_QUERY_LIMITED_INFORMATION |
PROCESS_SET_LIMITED_INFORMATION | PROCESS_VM_READ,
Processes[i]->ProcessId
);
if (NT_SUCCESS(status))
{
status = PhFlushProcessHeapsRemote(processHandle, PhTimeoutFromMilliseconds(&timeout, 4000));
NtClose(processHandle);
}
if (!NT_SUCCESS(status))
{
success = FALSE;
if (!PhpShowErrorProcess(WindowHandle, L"flush the process heap(s) of", Processes[i], status, 0))
break;
}
}
return success;
}
================================================
FILE: SystemInformer/admintask.c
================================================
/*
* Copyright (c) 2022 Winsider Seminars & Solutions, Inc. All rights reserved.
*
* This file is part of System Informer.
*
* Authors:
*
* dmex 2017-2023
*
*/
#include
#include
#include
#include
#include
#include
DEFINE_GUID(CLSID_TaskScheduler, 0x0f87369f, 0xa4e5, 0x4cfc, 0xbd, 0x3e, 0x73, 0xe6, 0x15, 0x45, 0x72, 0xdd);
DEFINE_GUID(IID_ITaskService, 0x2FABA4C7, 0x4DA9, 0x4013, 0x96, 0x97, 0x20, 0xCC, 0x3F, 0xD4, 0x0F, 0x85);
DEFINE_GUID(IID_ITaskSettings2, 0x2C05C3F0, 0x6EED, 0x4C05, 0xA1, 0x5F, 0xED, 0x7D, 0x7A, 0x98, 0xA3, 0x69);
DEFINE_GUID(IID_ILogonTrigger, 0x72DADE38, 0xFAE4, 0x4b3e, 0xBA, 0xF4, 0x5D, 0x00, 0x9A, 0xF0, 0x2B, 0x1C);
DEFINE_GUID(IID_IExecAction, 0x4C3D624D, 0xfd6b, 0x49A3, 0xB9, 0xB7, 0x09, 0xCB, 0x3C, 0xD3, 0xF0, 0x47);
HRESULT PhCreateAdminTask(
_In_ PPH_STRINGREF TaskName,
_In_ PPH_STRINGREF FileName
)
{
static CONST PH_STRINGREF taskTimeLimit = PH_STRINGREF_INIT(L"PT0S");
#if (PH_ADMIN_TASK_FORWARD_COMMANDLINE_UNPRIVILEGED)
static CONST PH_STRINGREF taskArguments = PH_STRINGREF_INIT(L"$(Arg0)");
#endif
HRESULT status;
BSTR taskNameString = NULL;
BSTR taskFileNameString = NULL;
#if (PH_ADMIN_TASK_FORWARD_COMMANDLINE_UNPRIVILEGED)
BSTR taskArgumentsString = NULL;
#endif
BSTR taskFolderString = NULL;
BSTR taskTimeLimitString = NULL;
VARIANT empty = { VT_EMPTY };
ITaskService* taskService = NULL;
ITaskFolder* taskFolder = NULL;
ITaskDefinition* taskDefinition = NULL;
ITaskSettings* taskSettings = NULL;
ITaskSettings2* taskSettings2 = NULL;
ITriggerCollection* taskTriggerCollection = NULL;
ITrigger* taskTrigger = NULL;
ILogonTrigger* taskLogonTrigger = NULL;
IRegisteredTask* taskRegisteredTask = NULL;
IPrincipal* taskPrincipal = NULL;
IActionCollection* taskActionCollection = NULL;
IAction* taskAction = NULL;
IExecAction* taskExecAction = NULL;
status = PhGetClassObject(
L"taskschd.dll",
&CLSID_TaskScheduler,
&IID_ITaskService,
&taskService
);
if (FAILED(status))
goto CleanupExit;
taskNameString = PhStringRefToBSTR(TaskName);
taskFileNameString = PhStringRefToBSTR(FileName);
taskFolderString = PhStringRefToBSTR(&PhNtPathSeparatorString);
taskTimeLimitString = PhStringRefToBSTR(&taskTimeLimit);
#if (PH_ADMIN_TASK_FORWARD_COMMANDLINE_UNPRIVILEGED)
taskArgumentsString = PhStringRefToBSTR(&taskArguments);
#endif
status = ITaskService_Connect(
taskService,
empty,
empty,
empty,
empty
);
if (FAILED(status))
goto CleanupExit;
status = ITaskService_GetFolder(
taskService,
taskFolderString,
&taskFolder
);
if (FAILED(status))
goto CleanupExit;
status = ITaskService_NewTask(
taskService,
0,
&taskDefinition
);
if (FAILED(status))
goto CleanupExit;
status = ITaskDefinition_get_Settings(
taskDefinition,
&taskSettings
);
if (FAILED(status))
goto CleanupExit;
ITaskSettings_put_Compatibility(taskSettings, TASK_COMPATIBILITY_V2_1);
ITaskSettings_put_StartWhenAvailable(taskSettings, VARIANT_TRUE);
ITaskSettings_put_DisallowStartIfOnBatteries(taskSettings, VARIANT_FALSE);
ITaskSettings_put_StopIfGoingOnBatteries(taskSettings, VARIANT_FALSE);
ITaskSettings_put_ExecutionTimeLimit(taskSettings, taskTimeLimitString);
ITaskSettings_put_Priority(taskSettings, 1);
if (SUCCEEDED(ITaskSettings_QueryInterface(
taskSettings,
&IID_ITaskSettings2,
&taskSettings2
)))
{
ITaskSettings2_put_UseUnifiedSchedulingEngine(taskSettings2, VARIANT_TRUE);
ITaskSettings2_put_DisallowStartOnRemoteAppSession(taskSettings2, VARIANT_TRUE);
ITaskSettings2_Release(taskSettings2);
}
//status = ITaskDefinition_get_Triggers(
// taskDefinition,
// &taskTriggerCollection
// );
//
//if (FAILED(status))
// goto CleanupExit;
//
//status = ITriggerCollection_Create(
// taskTriggerCollection,
// TASK_TRIGGER_LOGON,
// &taskTrigger
// );
//
//if (FAILED(status))
// goto CleanupExit;
//
//status = ITrigger_QueryInterface(
// taskTrigger,
// &IID_ILogonTrigger,
// &taskLogonTrigger
// );
//
//if (FAILED(status))
// goto CleanupExit;
//
//ILogonTrigger_put_Id(taskLogonTrigger, L"LogonTriggerId");
//ILogonTrigger_put_UserId(taskLogonTrigger, PhGetString(PhGetTokenUserString(PhGetOwnTokenAttributes().TokenHandle, TRUE)));
status = ITaskDefinition_get_Principal(
taskDefinition,
&taskPrincipal
);
if (FAILED(status))
goto CleanupExit;
#define TASK_RUNLEVEL_LUA 0
#define TASK_RUNLEVEL_HIGHEST 1
#define RUNLEVEL_ADMIN 2
#define RUNLEVEL_MAX_NON_UIA 3
#define RUNLEVEL_LUA_UIA 4
#define RUNLEVEL_HIGHEST_UIA 5
#define RUNLEVEL_ADMIN_UIA 6
#define RUNLEVEL_MAX 7
IPrincipal_put_RunLevel(taskPrincipal, TASK_RUNLEVEL_HIGHEST);
IPrincipal_put_LogonType(taskPrincipal, TASK_LOGON_INTERACTIVE_TOKEN);
status = ITaskDefinition_get_Actions(
taskDefinition,
&taskActionCollection
);
if (FAILED(status))
goto CleanupExit;
status = IActionCollection_Create(
taskActionCollection,
TASK_ACTION_EXEC,
&taskAction
);
if (FAILED(status))
goto CleanupExit;
status = IAction_QueryInterface(
taskAction,
&IID_IExecAction,
&taskExecAction
);
if (FAILED(status))
goto CleanupExit;
status = IExecAction_put_Path(
taskExecAction,
taskFileNameString
);
if (FAILED(status))
goto CleanupExit;
#if (PH_ADMIN_TASK_FORWARD_COMMANDLINE_UNPRIVILEGED)
status = IExecAction_put_Arguments(
taskExecAction,
taskArgumentsString
);
if (FAILED(status))
goto CleanupExit;
#endif
ITaskFolder_DeleteTask(
taskFolder,
taskNameString,
0
);
status = ITaskFolder_RegisterTaskDefinition(
taskFolder,
taskNameString,
taskDefinition,
TASK_CREATE_OR_UPDATE,
empty,
empty,
TASK_LOGON_INTERACTIVE_TOKEN,
empty,
&taskRegisteredTask
);
CleanupExit:
if (taskRegisteredTask)
IRegisteredTask_Release(taskRegisteredTask);
if (taskActionCollection)
IActionCollection_Release(taskActionCollection);
if (taskPrincipal)
IPrincipal_Release(taskPrincipal);
if (taskLogonTrigger)
ILogonTrigger_Release(taskLogonTrigger);
if (taskTrigger)
ITrigger_Release(taskTrigger);
if (taskTriggerCollection)
ITriggerCollection_Release(taskTriggerCollection);
if (taskSettings)
ITaskSettings_Release(taskSettings);
if (taskDefinition)
ITaskDefinition_Release(taskDefinition);
if (taskFolder)
ITaskFolder_Release(taskFolder);
if (taskService)
ITaskService_Release(taskService);
if (taskTimeLimitString)
SysFreeString(taskTimeLimitString);
if (taskNameString)
SysFreeString(taskNameString);
if (taskFileNameString)
SysFreeString(taskFileNameString);
#if (PH_ADMIN_TASK_FORWARD_COMMANDLINE_UNPRIVILEGED)
if (taskArgumentsString)
SysFreeString(taskArgumentsString);
#endif
if (taskFolderString)
SysFreeString(taskFolderString);
return status;
}
HRESULT PhDeleteAdminTask(
_In_ PPH_STRINGREF TaskName
)
{
HRESULT status;
BSTR taskNameString = NULL;
BSTR taskFolderString = NULL;
VARIANT empty = { VT_EMPTY };
ITaskService* taskService = NULL;
ITaskFolder* taskFolder = NULL;
status = PhGetClassObject(
L"taskschd.dll",
&CLSID_TaskScheduler,
&IID_ITaskService,
&taskService
);
if (FAILED(status))
goto CleanupExit;
taskNameString = PhStringRefToBSTR(TaskName);
taskFolderString = PhStringRefToBSTR(&PhNtPathSeparatorString);
status = ITaskService_Connect(
taskService,
empty,
empty,
empty,
empty
);
if (FAILED(status))
goto CleanupExit;
status = ITaskService_GetFolder(
taskService,
taskFolderString,
&taskFolder
);
if (FAILED(status))
goto CleanupExit;
status = ITaskFolder_DeleteTask(
taskFolder,
taskNameString,
0
);
CleanupExit:
if (taskFolder)
ITaskFolder_Release(taskFolder);
if (taskService)
ITaskService_Release(taskService);
if (taskFolderString)
SysFreeString(taskFolderString);
if (taskNameString)
SysFreeString(taskNameString);
return status;
}
HRESULT PhRunAsAdminTask(
_In_ PPH_STRINGREF TaskName
)
{
HRESULT status;
ULONG sessionId = ULONG_MAX;
BSTR taskNameString = NULL;
BSTR taskFolderString = NULL;
VARIANT empty = { VT_EMPTY };
VARIANT params = { VT_EMPTY };
ITaskService* taskService = NULL;
ITaskFolder* taskFolder = NULL;
IRegisteredTask* taskRegisteredTask = NULL;
IRunningTask* taskRunningTask = NULL;
PhTraceFuncEnter("Run as admin task");
status = PhGetClassObject(
L"taskschd.dll",
&CLSID_TaskScheduler,
&IID_ITaskService,
&taskService
);
if (FAILED(status))
goto CleanupExit;
taskNameString = PhStringRefToBSTR(TaskName);
taskFolderString = PhStringRefToBSTR(&PhNtPathSeparatorString);
status = ITaskService_Connect(
taskService,
empty,
empty,
empty,
empty
);
if (FAILED(status))
goto CleanupExit;
status = ITaskService_GetFolder(
taskService,
taskFolderString,
&taskFolder
);
if (FAILED(status))
goto CleanupExit;
status = ITaskFolder_GetTask(
taskFolder,
taskNameString,
&taskRegisteredTask
);
if (FAILED(status))
goto CleanupExit;
#if (PH_ADMIN_TASK_FORWARD_COMMANDLINE_UNPRIVILEGED)
if (CommandLine)
{
PH_STRINGREF commandline;
if (NT_SUCCESS(PhGetProcessCommandLineStringRef(&commandline)))
{
V_VT(¶ms) = VT_BSTR;
V_BSTR(¶ms) = PhStringRefToBSTR(&commandline);
}
}
#endif
status = IRegisteredTask_RunEx(
taskRegisteredTask,
params,
TASK_RUN_AS_SELF | TASK_RUN_USE_SESSION_ID,
USER_SHARED_DATA->ActiveConsoleId,
NULL,
&taskRunningTask
);
CleanupExit:
if (taskRunningTask)
IRunningTask_Release(taskRunningTask);
if (taskRegisteredTask)
IRegisteredTask_Release(taskRegisteredTask);
if (taskFolder)
ITaskFolder_Release(taskFolder);
if (taskService)
ITaskService_Release(taskService);
#if (PH_ADMIN_TASK_FORWARD_COMMANDLINE_UNPRIVILEGED)
if (V_BSTR(¶ms))
SysFreeString(V_BSTR(¶ms));
#endif
if (taskFolderString)
SysFreeString(taskFolderString);
if (taskNameString)
SysFreeString(taskNameString);
PhTraceFuncExit("Run as admin task: %!STATUS!", status);
return status;
}
NTSTATUS PhRunAsAdminTaskUIAccess(
VOID
)
{
NTSTATUS status;
BOOLEAN tokenIsUIAccess;
ULONG sessionId;
CLIENT_ID desktopId;
PPH_STRING fileName = NULL;
PhTraceFuncEnter("Run as admin task UI access");
if (!PhGetOwnTokenAttributes().Elevated)
{
status = STATUS_UNSUCCESSFUL;
goto CleanupExit;
}
if (!NT_SUCCESS(PhGetTokenUIAccess(
PhGetOwnTokenAttributes().TokenHandle,
&tokenIsUIAccess
)) || tokenIsUIAccess)
{
status = STATUS_UNSUCCESSFUL;
goto CleanupExit;
}
fileName = PhGetApplicationFileNameWin32();
if (PhIsNullOrEmptyString(fileName))
{
status = STATUS_UNSUCCESSFUL;
goto CleanupExit;
}
// Query the process from the current desktop.
status = PhGetWindowClientId(PhGetShellWindow(), &desktopId);
if (!NT_SUCCESS(status))
goto CleanupExit;
// Query the session from the current process.
status = PhGetProcessSessionId(NtCurrentProcess(), &sessionId);
if (!NT_SUCCESS(status))
goto CleanupExit;
status = PhExecuteRunAsCommand3(
NULL,
PhGetString(fileName),
NULL,
NULL,
LOGON32_LOGON_INTERACTIVE,
desktopId.UniqueProcess,
sessionId,
NULL,
TRUE,
FALSE,
TRUE
);
CleanupExit:
PhClearReference(&fileName);
PhTraceFuncExit("Run as admin task UI access: %!STATUS!", status);
return status;
}
================================================
FILE: SystemInformer/affinity.c
================================================
/*
* Copyright (c) 2022 Winsider Seminars & Solutions, Inc. All rights reserved.
*
* This file is part of System Informer.
*
* Authors:
*
* wj32 2010-2015
* dmex 2020-2023
*
*/
/*
* The affinity dialog was originally created to support the modification
* of process affinity masks, but now supports modifying thread affinity
* and generic masks.
*/
#include
#include
#include
typedef struct _PH_AFFINITY_DIALOG_CONTEXT
{
HWND WindowHandle;
HWND GroupComboHandle;
PPH_PROCESS_ITEM ProcessItem;
PPH_THREAD_ITEM ThreadItem;
KAFFINITY NewAffinityMask;
PPH_LIST CpuControlList;
USHORT AffinityGroup;
KAFFINITY AffinityMask;
KAFFINITY SystemAffinityMask;
// Multiple selected items (dmex)
PPH_THREAD_ITEM* Threads;
ULONG NumberOfThreads;
PHANDLE ThreadHandles;
} PH_AFFINITY_DIALOG_CONTEXT, *PPH_AFFINITY_DIALOG_CONTEXT;
INT_PTR CALLBACK PhpProcessAffinityDlgProc(
_In_ HWND hwndDlg,
_In_ UINT uMsg,
_In_ WPARAM wParam,
_In_ LPARAM lParam
);
VOID PhShowProcessAffinityDialog(
_In_ HWND ParentWindowHandle,
_In_opt_ PPH_PROCESS_ITEM ProcessItem,
_In_opt_ PPH_THREAD_ITEM ThreadItem
)
{
PH_AFFINITY_DIALOG_CONTEXT context;
assert(!!ProcessItem != !!ThreadItem); // make sure we have one and not the other (wj32)
memset(&context, 0, sizeof(PH_AFFINITY_DIALOG_CONTEXT));
context.ProcessItem = ProcessItem;
context.ThreadItem = ThreadItem;
PhDialogBox(
PhInstanceHandle,
MAKEINTRESOURCE(IDD_AFFINITY),
ParentWindowHandle,
PhpProcessAffinityDlgProc,
&context
);
}
_Success_(return)
BOOLEAN PhShowProcessAffinityDialog2(
_In_ HWND ParentWindowHandle,
_In_ PPH_PROCESS_ITEM ProcessItem,
_Out_ PKAFFINITY NewAffinityMask
)
{
PH_AFFINITY_DIALOG_CONTEXT context;
memset(&context, 0, sizeof(PH_AFFINITY_DIALOG_CONTEXT));
context.ProcessItem = ProcessItem;
context.ThreadItem = NULL;
if (PhDialogBox(
PhInstanceHandle,
MAKEINTRESOURCE(IDD_AFFINITY),
ParentWindowHandle,
PhpProcessAffinityDlgProc,
&context
) == IDOK)
{
*NewAffinityMask = context.NewAffinityMask;
return TRUE;
}
else
{
return FALSE;
}
}
VOID PhShowThreadAffinityDialog(
_In_ HWND ParentWindowHandle,
_In_ PPH_THREAD_ITEM* Threads,
_In_ ULONG NumberOfThreads
)
{
PH_AFFINITY_DIALOG_CONTEXT context;
memset(&context, 0, sizeof(PH_AFFINITY_DIALOG_CONTEXT));
context.Threads = Threads;
context.NumberOfThreads = NumberOfThreads;
context.ThreadHandles = PhAllocateZero(NumberOfThreads * sizeof(HANDLE));
// Cache handles to each thread since the ThreadId gets
// reassigned to a different process after the thread exits. (dmex)
for (ULONG i = 0; i < NumberOfThreads; i++)
{
if (!NT_SUCCESS(PhOpenThread(
&context.ThreadHandles[i],
THREAD_QUERY_LIMITED_INFORMATION | THREAD_SET_LIMITED_INFORMATION | THREAD_SET_INFORMATION,
Threads[i]->ThreadId
)))
{
if (!NT_SUCCESS(PhOpenThread(
&context.ThreadHandles[i],
THREAD_QUERY_LIMITED_INFORMATION | THREAD_SET_LIMITED_INFORMATION,
Threads[i]->ThreadId
)))
{
context.ThreadHandles[i] = INVALID_HANDLE_VALUE;
}
}
}
PhDialogBox(
PhInstanceHandle,
MAKEINTRESOURCE(IDD_AFFINITY),
ParentWindowHandle,
PhpProcessAffinityDlgProc,
&context
);
}
static BOOLEAN PhpShowProcessErrorAffinity(
_In_ HWND hWnd,
_In_ PPH_PROCESS_ITEM Process,
_In_ NTSTATUS Status,
_In_opt_ ULONG Win32Result
)
{
return PhShowContinueStatus(
hWnd,
PhaFormatString(
L"Unable to change affinity of process %lu",
HandleToUlong(Process->ProcessId)
)->Buffer,
Status,
Win32Result
);
}
static BOOLEAN PhpShowThreadErrorAffinity(
_In_ HWND hWnd,
_In_ PPH_THREAD_ITEM Thread,
_In_ NTSTATUS Status,
_In_opt_ ULONG Win32Result
)
{
return PhShowContinueStatus(
hWnd,
PhaFormatString(
L"Unable to change affinity of thread %lu",
HandleToUlong(Thread->ThreadId)
)->Buffer,
Status,
Win32Result
);
}
VOID PhpShowThreadErrorAffinityList(
_In_ PPH_AFFINITY_DIALOG_CONTEXT Context,
_Inout_ PPH_LIST AffinityErrorsList
)
{
PH_STRING_BUILDER stringBuilder;
PhInitializeStringBuilder(&stringBuilder, 100);
for (ULONG i = 0; i < AffinityErrorsList->Count; i++)
{
PhAppendFormatStringBuilder(
&stringBuilder,
L"%s\n",
PhGetStringOrDefault(AffinityErrorsList->Items[i], L"An unknown error occurred.")
);
}
if (PhEndsWithStringRef2(&stringBuilder.String->sr, L"\n", FALSE))
PhRemoveEndStringBuilder(&stringBuilder, 2);
PhShowInformation2(
Context->WindowHandle,
L"Unable to update affinity for thread(s)",
L"Unable to update affinity for thread(s):\r\n%s",
PhGetString(PhFinalStringBuilderString(&stringBuilder))
);
PhDeleteStringBuilder(&stringBuilder);
PhDereferenceObjects(AffinityErrorsList->Items, AffinityErrorsList->Count);
PhDereferenceObject(AffinityErrorsList);
}
BOOLEAN PhpCheckThreadsHaveSameAffinity(
_In_ PPH_AFFINITY_DIALOG_CONTEXT Context
)
{
BOOLEAN result = TRUE;
GROUP_AFFINITY groupAffinity;
KAFFINITY lastAffinityMask = 0;
KAFFINITY affinityMask = 0;
if (Context->ThreadHandles[0] != INVALID_HANDLE_VALUE)
{
if (NT_SUCCESS(PhGetThreadGroupAffinity(Context->ThreadHandles[0], &groupAffinity)))
{
lastAffinityMask = groupAffinity.Mask;
}
}
for (ULONG i = 0; i < Context->NumberOfThreads; i++)
{
if (Context->ThreadHandles[i] == INVALID_HANDLE_VALUE)
continue;
if (NT_SUCCESS(PhGetThreadGroupAffinity(Context->ThreadHandles[i], &groupAffinity)))
{
affinityMask = groupAffinity.Mask;
}
if (lastAffinityMask != affinityMask)
{
result = FALSE;
break;
}
}
return result;
}
INT_PTR CALLBACK PhpProcessAffinityDlgProc(
_In_ HWND hwndDlg,
_In_ UINT uMsg,
_In_ WPARAM wParam,
_In_ LPARAM lParam
)
{
PPH_AFFINITY_DIALOG_CONTEXT context = NULL;
if (uMsg == WM_INITDIALOG)
{
context = (PPH_AFFINITY_DIALOG_CONTEXT)lParam;
PhSetWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT, context);
}
else
{
context = PhGetWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT);
}
if (!context)
return FALSE;
switch (uMsg)
{
case WM_INITDIALOG:
{
NTSTATUS status = STATUS_UNSUCCESSFUL;
BOOLEAN differentAffinity = FALSE;
ULONG i;
context->WindowHandle = hwndDlg;
PhCenterWindow(hwndDlg, GetParent(hwndDlg));
PhSetApplicationWindowIcon(hwndDlg);
{
context->CpuControlList = PhCreateList(MAXIMUM_PROC_PER_GROUP);
for (i = 0; i < MAXIMUM_PROC_PER_GROUP; i++)
{
PhAddItemList(context->CpuControlList, GetDlgItem(hwndDlg, IDC_CPU0 + i));
}
}
if (!PhSystemProcessorInformation.SingleProcessorGroup)
{
context->GroupComboHandle = GetDlgItem(hwndDlg, IDC_GROUPCPU);
for (USHORT processorGroup = 0; processorGroup < PhSystemProcessorInformation.NumberOfProcessorGroups; processorGroup++)
{
ComboBox_AddString(context->GroupComboHandle, PhaFormatString(L"Group %hu", processorGroup)->Buffer);
}
ShowWindow(context->GroupComboHandle, SW_SHOW);
}
if (context->ProcessItem)
{
HANDLE processHandle;
GROUP_AFFINITY processGroupAffinity = { 0 };
if (NT_SUCCESS(status = PhOpenProcess(
&processHandle,
PROCESS_QUERY_LIMITED_INFORMATION,
context->ProcessItem->ProcessId
)))
{
status = PhGetProcessGroupAffinity(processHandle, &processGroupAffinity);
if (NT_SUCCESS(status))
{
context->AffinityMask = processGroupAffinity.Mask;
context->AffinityGroup = processGroupAffinity.Group;
if (context->GroupComboHandle)
{
ComboBox_SetCurSel(context->GroupComboHandle, processGroupAffinity.Group);
}
}
NtClose(processHandle);
}
}
else if (context->ThreadItem)
{
HANDLE threadHandle;
THREAD_BASIC_INFORMATION basicInfo;
GROUP_AFFINITY groupAffinity = { 0 };
HANDLE processHandle;
if (NT_SUCCESS(status = PhOpenThread(
&threadHandle,
THREAD_QUERY_LIMITED_INFORMATION,
context->ThreadItem->ThreadId
)))
{
status = PhGetThreadGroupAffinity(threadHandle, &groupAffinity);
if (NT_SUCCESS(status))
{
context->AffinityMask = groupAffinity.Mask;
context->AffinityGroup = groupAffinity.Group;
if (context->GroupComboHandle)
{
ComboBox_SetCurSel(context->GroupComboHandle, groupAffinity.Group);
}
if (NT_SUCCESS(PhGetThreadBasicInformation(
threadHandle,
&basicInfo
)))
{
// A thread's affinity mask is restricted by the process affinity mask,
// so use that as the system affinity mask. (wj32)
if (NT_SUCCESS(PhOpenProcess(
&processHandle,
PROCESS_QUERY_LIMITED_INFORMATION,
basicInfo.ClientId.UniqueProcess
)))
{
GROUP_AFFINITY processGroupAffinity = { 0 };
if (NT_SUCCESS(PhGetProcessGroupAffinity(processHandle, &processGroupAffinity)))
{
context->SystemAffinityMask = processGroupAffinity.Mask;
}
NtClose(processHandle);
}
}
}
NtClose(threadHandle);
}
}
else if (context->Threads)
{
HANDLE processHandle;
THREAD_BASIC_INFORMATION basicInfo;
GROUP_AFFINITY groupAffinity = { 0 };
PPH_STRING windowText;
windowText = PH_AUTO(PhGetWindowText(hwndDlg));
PhSetWindowText(hwndDlg, PhaFormatString(
L"%s (%lu threads)",
windowText->Buffer,
context->NumberOfThreads
)->Buffer);
differentAffinity = !PhpCheckThreadsHaveSameAffinity(context);
if (context->ThreadHandles[0] != INVALID_HANDLE_VALUE)
{
// Use affinity from the first thread when all threads are identical (dmex)
status = PhGetThreadGroupAffinity(
context->ThreadHandles[0],
&groupAffinity
);
}
else
{
status = STATUS_UNSUCCESSFUL;
}
if (NT_SUCCESS(status))
{
context->AffinityMask = groupAffinity.Mask;
context->AffinityGroup = groupAffinity.Group;
if (context->GroupComboHandle)
{
ComboBox_SetCurSel(context->GroupComboHandle, groupAffinity.Group);
}
if (NT_SUCCESS(PhGetThreadBasicInformation(
context->ThreadHandles[0],
&basicInfo
)))
{
if (NT_SUCCESS(PhOpenProcess(
&processHandle,
PROCESS_QUERY_LIMITED_INFORMATION,
basicInfo.ClientId.UniqueProcess
)))
{
GROUP_AFFINITY processGroupAffinity = { 0 };
if (NT_SUCCESS(PhGetProcessGroupAffinity(processHandle, &processGroupAffinity)))
{
context->SystemAffinityMask = processGroupAffinity.Mask;
}
NtClose(processHandle);
}
}
}
}
if (NT_SUCCESS(status) && context->SystemAffinityMask == 0)
{
KAFFINITY systemAffinityMask;
if (PhSystemProcessorInformation.SingleProcessorGroup)
{
status = PhGetProcessorSystemAffinityMask(&systemAffinityMask);
}
else
{
status = PhGetProcessorGroupActiveAffinityMask(context->AffinityGroup, &systemAffinityMask);
}
if (NT_SUCCESS(status))
{
context->SystemAffinityMask = systemAffinityMask;
}
}
if (!NT_SUCCESS(status))
{
PhShowStatus(hwndDlg, L"Unable to query the current affinity.", status, 0);
EndDialog(hwndDlg, IDCANCEL);
break;
}
// Disable the CPU checkboxes which aren't part of the system affinity mask,
// and check the CPU checkboxes which are part of the affinity mask. (wj32)
for (i = 0; i < MAXIMUM_PROC_PER_GROUP; i++)
{
if ((context->SystemAffinityMask >> i) & 0x1)
{
if (differentAffinity) // Skip for multiple selection (dmex)
continue;
if ((context->AffinityMask >> i) & 0x1)
{
Button_SetCheck(context->CpuControlList->Items[i], BST_CHECKED);
}
}
else
{
EnableWindow(context->CpuControlList->Items[i], FALSE);
}
}
PhInitializeWindowTheme(hwndDlg, PhEnableThemeSupport);
}
break;
case WM_DESTROY:
{
PhRemoveWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT);
if (context->ThreadHandles)
{
for (ULONG i = 0; i < context->NumberOfThreads; i++)
{
if (context->ThreadHandles[i] != INVALID_HANDLE_VALUE)
{
NtClose(context->ThreadHandles[i]);
context->ThreadHandles[i] = NULL;
}
}
PhFree(context->ThreadHandles);
}
}
break;
case WM_COMMAND:
{
switch (GET_WM_COMMAND_ID(wParam, lParam))
{
case IDCANCEL:
EndDialog(hwndDlg, IDCANCEL);
break;
case IDOK:
{
NTSTATUS status = STATUS_SUCCESS;
KAFFINITY affinityMask = 0;
USHORT affinityGroup = 0;
// Work out the affinity mask.
for (ULONG i = 0; i < MAXIMUM_PROC_PER_GROUP; i++)
{
if (Button_GetCheck(context->CpuControlList->Items[i]) == BST_CHECKED)
affinityMask |= AFFINITY_MASK(i);
}
if (context->GroupComboHandle)
{
LONG affinityGroupSelection = ComboBox_GetCurSel(context->GroupComboHandle);
if (affinityGroupSelection == CB_ERR)
affinityGroup = context->AffinityGroup;
else
affinityGroup = (USHORT)affinityGroupSelection;
}
if (affinityMask == 0)
{
PhShowError2(hwndDlg, L"Unable to change affinity settings.", L"%s", L"You must select at least one CPU.");
break;
}
if (context->ProcessItem)
{
HANDLE processHandle;
if (NT_SUCCESS(status = PhOpenProcess(
&processHandle,
PROCESS_SET_INFORMATION,
context->ProcessItem->ProcessId
)))
{
if (PhSystemProcessorInformation.SingleProcessorGroup)
{
status = PhSetProcessAffinityMask(processHandle, affinityMask);
}
else
{
GROUP_AFFINITY groupAffinity;
memset(&groupAffinity, 0, sizeof(GROUP_AFFINITY));
groupAffinity.Group = affinityGroup;
groupAffinity.Mask = affinityMask;
status = PhSetProcessGroupAffinity(processHandle, groupAffinity);
}
NtClose(processHandle);
}
if (NT_SUCCESS(status))
{
context->NewAffinityMask = affinityMask;
}
else
{
PhpShowProcessErrorAffinity(hwndDlg, context->ProcessItem, status, 0);
}
}
else if (context->ThreadItem)
{
if (PhSystemProcessorInformation.SingleProcessorGroup)
{
HANDLE threadHandle;
status = PhOpenThread(
&threadHandle,
THREAD_SET_LIMITED_INFORMATION,
context->ThreadItem->ThreadId
);
if (NT_SUCCESS(status))
{
status = PhSetThreadAffinityMask(threadHandle, affinityMask);
NtClose(threadHandle);
}
if (!NT_SUCCESS(status))
{
PhpShowThreadErrorAffinity(hwndDlg, context->ThreadItem, status, 0);
}
}
else
{
HANDLE threadHandle;
status = PhOpenThread(
&threadHandle,
THREAD_SET_INFORMATION,
context->ThreadItem->ThreadId
);
if (NT_SUCCESS(status))
{
GROUP_AFFINITY groupAffinity;
memset(&groupAffinity, 0, sizeof(GROUP_AFFINITY));
groupAffinity.Group = affinityGroup;
groupAffinity.Mask = affinityMask;
status = PhSetThreadGroupAffinity(threadHandle, groupAffinity);
NtClose(threadHandle);
}
if (!NT_SUCCESS(status))
{
PhpShowThreadErrorAffinity(hwndDlg, context->ThreadItem, status, 0);
}
}
}
else if (context->Threads)
{
PPH_LIST threadAffinityErrors = PhCreateList(1);
for (ULONG i = 0; i < context->NumberOfThreads; i++)
{
if (context->ThreadHandles[i] == INVALID_HANDLE_VALUE)
continue;
if (PhSystemProcessorInformation.SingleProcessorGroup)
{
status = PhSetThreadAffinityMask(context->ThreadHandles[i], affinityMask);
}
else
{
GROUP_AFFINITY groupAffinity;
memset(&groupAffinity, 0, sizeof(GROUP_AFFINITY));
groupAffinity.Group = affinityGroup;
groupAffinity.Mask = affinityMask;
status = PhSetThreadGroupAffinity(context->ThreadHandles[i], groupAffinity);
}
if (!NT_SUCCESS(status))
{
PPH_STRING errorMessage;
if (errorMessage = PhGetNtMessage(status))
{
PhAddItemList(threadAffinityErrors, errorMessage);
}
}
}
if (threadAffinityErrors->Count > 0)
PhpShowThreadErrorAffinityList(context, threadAffinityErrors);
else
PhDereferenceObject(threadAffinityErrors);
}
if (NT_SUCCESS(status))
{
EndDialog(hwndDlg, IDOK);
}
}
break;
case IDC_SELECTALL:
case IDC_DESELECTALL:
{
for (ULONG i = 0; i < MAXIMUM_PROC_PER_GROUP; i++)
{
HWND checkBox = context->CpuControlList->Items[i];
if (IsWindowEnabled(checkBox))
Button_SetCheck(checkBox, GET_WM_COMMAND_ID(wParam, lParam) == IDC_SELECTALL ? BST_CHECKED : BST_UNCHECKED);
}
}
break;
case IDC_GROUPCPU:
{
LONG index;
if (!context->GroupComboHandle)
break;
index = ComboBox_GetCurSel(context->GroupComboHandle);
if (index != CB_ERR)
{
if (index != context->AffinityGroup)
{
for (ULONG i = 0; i < MAXIMUM_PROC_PER_GROUP; i++)
{
Button_SetCheck(context->CpuControlList->Items[i], BST_UNCHECKED);
}
}
else
{
BOOLEAN differentAffinity = FALSE;
if (context->Threads)
{
differentAffinity = !PhpCheckThreadsHaveSameAffinity(context);
}
for (ULONG i = 0; i < MAXIMUM_PROC_PER_GROUP; i++)
{
if ((context->SystemAffinityMask >> i) & 0x1)
{
if (differentAffinity) // Skip for multiple selection (dmex)
continue;
if ((context->AffinityMask >> i) & 0x1)
{
Button_SetCheck(context->CpuControlList->Items[i], BST_CHECKED);
}
}
}
}
}
}
break;
}
}
break;
case WM_CTLCOLORBTN:
return HANDLE_WM_CTLCOLORBTN(hwndDlg, wParam, lParam, PhWindowThemeControlColor);
case WM_CTLCOLORDLG:
return HANDLE_WM_CTLCOLORDLG(hwndDlg, wParam, lParam, PhWindowThemeControlColor);
case WM_CTLCOLORSTATIC:
return HANDLE_WM_CTLCOLORSTATIC(hwndDlg, wParam, lParam, PhWindowThemeControlColor);
}
return FALSE;
}
// Note: Workaround for UserNotes plugin dialog overrides (dmex)
NTSTATUS PhSetProcessItemAffinityMask(
_In_ PPH_PROCESS_ITEM ProcessItem,
_In_ KAFFINITY AffinityMask
)
{
NTSTATUS status;
HANDLE processHandle;
status = PhOpenProcess(
&processHandle,
PROCESS_SET_INFORMATION,
ProcessItem->ProcessId
);
if (NT_SUCCESS(status))
{
status = PhSetProcessAffinityMask(processHandle, AffinityMask);
NtClose(processHandle);
}
return status;
}
// Note: Workaround for UserNotes plugin dialog overrides (dmex)
NTSTATUS PhSetProcessItemPagePriority(
_In_ PPH_PROCESS_ITEM ProcessItem,
_In_ ULONG PagePriority
)
{
NTSTATUS status;
HANDLE processHandle;
status = PhOpenProcess(
&processHandle,
PROCESS_SET_INFORMATION,
ProcessItem->ProcessId
);
if (NT_SUCCESS(status))
{
status = PhSetProcessPagePriority(processHandle, PagePriority);
NtClose(processHandle);
}
return status;
}
// Note: Workaround for UserNotes plugin dialog overrides (dmex)
NTSTATUS PhSetProcessItemIoPriority(
_In_ PPH_PROCESS_ITEM ProcessItem,
_In_ IO_PRIORITY_HINT IoPriority
)
{
NTSTATUS status;
HANDLE processHandle;
status = PhOpenProcess(
&processHandle,
PROCESS_SET_INFORMATION,
ProcessItem->ProcessId
);
if (NT_SUCCESS(status))
{
status = PhSetProcessIoPriority(processHandle, IoPriority);
NtClose(processHandle);
}
return status;
}
// Note: Workaround for UserNotes plugin dialog overrides (dmex)
NTSTATUS PhSetProcessItemPriority(
_In_ PPH_PROCESS_ITEM ProcessItem,
_In_ UCHAR PriorityClass
)
{
NTSTATUS status;
HANDLE processHandle;
status = PhOpenProcess(
&processHandle,
PROCESS_SET_INFORMATION,
ProcessItem->ProcessId
);
if (NT_SUCCESS(status))
{
status = PhSetProcessPriorityClass(processHandle, PriorityClass);
NtClose(processHandle);
}
return status;
}
// Note: Workaround for UserNotes plugin dialog overrides (dmex)
NTSTATUS PhSetProcessItemPriorityBoost(
_In_ PPH_PROCESS_ITEM ProcessItem,
_In_ BOOLEAN PriorityBoost
)
{
NTSTATUS status;
HANDLE processHandle;
status = PhOpenProcess(
&processHandle,
PROCESS_SET_INFORMATION,
ProcessItem->ProcessId
);
if (NT_SUCCESS(status))
{
status = PhSetProcessPriorityBoost(processHandle, PriorityBoost);
NtClose(processHandle);
}
return status;
}
// Note: Workaround for UserNotes plugin dialog overrides (dmex)
NTSTATUS PhSetProcessItemThrottlingState(
_In_ PPH_PROCESS_ITEM ProcessItem,
_In_ BOOLEAN ClearThrottlingState
)
{
NTSTATUS status;
HANDLE processHandle;
status = PhOpenProcess(
&processHandle,
PROCESS_SET_INFORMATION,
ProcessItem->ProcessId
);
if (NT_SUCCESS(status))
{
if (ClearThrottlingState)
{
PhSetProcessPriorityClass(processHandle, PROCESS_PRIORITY_CLASS_NORMAL);
status = PhSetProcessPowerThrottlingState(processHandle, 0, 0);
}
else
{
// Taskmgr sets the process priority to idle before enabling 'Eco mode'. (dmex)
PhSetProcessPriorityClass(processHandle, PROCESS_PRIORITY_CLASS_IDLE);
status = PhSetProcessPowerThrottlingState(
processHandle,
POWER_THROTTLING_PROCESS_EXECUTION_SPEED,
POWER_THROTTLING_PROCESS_EXECUTION_SPEED
);
}
NtClose(processHandle);
}
return status;
}
================================================
FILE: SystemInformer/anawait.c
================================================
/*
* Copyright (c) 2022 Winsider Seminars & Solutions, Inc. All rights reserved.
*
* This file is part of System Informer.
*
* Authors:
*
* wj32 2010-2011
*
*/
/*
* There are two ways of seeing what a thread is waiting on. The first method
* is to walk the stack of a thread and read the arguments to whatever system
* call it is blocking on; this only works on x86 because on x64 the arguments
* are passed in registers (at least the first four are). The second method
* involves using the ThreadLastSystemCall info class for NtQueryInformationThread
* to retrieve the first argument to the system call the thread is blocking on.
* This is obviously only useful for NtWaitForSingleObject.
*
* There are other methods for specific scenarios, like USER messages and ALPC
* calls.
*/
#include
#include
#include
#include
typedef struct _ANALYZE_WAIT_CONTEXT
{
BOOLEAN Found;
BOOLEAN IsWow64Process;
HANDLE ProcessId;
HANDLE ThreadId;
HANDLE ProcessHandle;
PPH_SYMBOL_PROVIDER SymbolProvider;
PH_STRING_BUILDER StringBuilder;
PVOID PrevParams[4];
} ANALYZE_WAIT_CONTEXT, *PANALYZE_WAIT_CONTEXT;
VOID PhpAnalyzeWaitPassive(
_In_ HWND WindowHandle,
_In_ HANDLE ProcessId,
_In_ HANDLE ThreadId
);
_Function_class_(PH_WALK_THREAD_STACK_CALLBACK)
BOOLEAN NTAPI PhpWalkThreadStackAnalyzeCallback(
_In_ PPH_THREAD_STACK_FRAME StackFrame,
_In_ PVOID Context
);
VOID PhpAnalyzeWaitFallbacks(
_In_ PANALYZE_WAIT_CONTEXT Context
);
VOID PhpInitializeServiceNumbers(
VOID
);
PPH_STRING PhpaGetHandleString(
_In_ HANDLE ProcessHandle,
_In_ HANDLE Handle
);
VOID PhpGetWfmoInformation(
_In_ HANDLE ProcessHandle,
_In_ BOOLEAN IsWow64Process,
_In_ ULONG NumberOfHandles,
_In_ PHANDLE AddressOfHandles,
_In_ WAIT_TYPE WaitType,
_In_ BOOLEAN Alertable,
_Inout_ PPH_STRING_BUILDER StringBuilder
);
PPH_STRING PhpaGetSendMessageReceiver(
_In_ HANDLE ThreadId
);
PPH_STRING PhpaGetAlpcInformation(
_In_ HANDLE ThreadId
);
static PH_INITONCE ServiceNumbersInitOnce = PH_INITONCE_INIT;
static USHORT NumberForWfso = USHRT_MAX;
static USHORT NumberForWfmo = USHRT_MAX;
static USHORT NumberForRf = USHRT_MAX;
VOID PhUiAnalyzeWaitThread(
_In_ HWND WindowHandle,
_In_ HANDLE ProcessId,
_In_ HANDLE ThreadId,
_In_ PPH_SYMBOL_PROVIDER SymbolProvider
)
{
NTSTATUS status;
HANDLE threadHandle;
HANDLE processHandle;
#ifdef _WIN64
BOOLEAN isWow64;
#endif
CLIENT_ID clientId;
ANALYZE_WAIT_CONTEXT context;
if (!NT_SUCCESS(status = PhOpenProcess(&processHandle, PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, ProcessId)))
{
PhShowStatus(WindowHandle, L"Unable to open the process.", status, 0);
return;
}
#ifdef _WIN64
// Determine if the process is WOW64. If not, we use the passive method.
if (!NT_SUCCESS(status = PhGetProcessIsWow64(processHandle, &isWow64)) || !isWow64)
{
PhpAnalyzeWaitPassive(WindowHandle, ProcessId, ThreadId);
NtClose(processHandle);
return;
}
#endif
if (!NT_SUCCESS(status = PhOpenThread(
&threadHandle,
THREAD_QUERY_LIMITED_INFORMATION | THREAD_GET_CONTEXT | THREAD_SUSPEND_RESUME,
ThreadId
)))
{
PhShowStatus(WindowHandle, L"Unable to open the thread.", status, 0);
NtClose(processHandle);
return;
}
memset(&context, 0, sizeof(ANALYZE_WAIT_CONTEXT));
context.ProcessId = ProcessId;
context.ThreadId = ThreadId;
context.ProcessHandle = processHandle;
context.SymbolProvider = SymbolProvider;
PhInitializeStringBuilder(&context.StringBuilder, 100);
clientId.UniqueProcess = ProcessId;
clientId.UniqueThread = ThreadId;
PhWalkThreadStack(
threadHandle,
processHandle,
&clientId,
SymbolProvider,
PH_WALK_USER_WOW64_STACK,
PhpWalkThreadStackAnalyzeCallback,
&context
);
NtClose(threadHandle);
NtClose(processHandle);
PhpAnalyzeWaitFallbacks(&context);
if (context.Found)
{
PhShowInformationDialog(WindowHandle, PhFinalStringBuilderString(&context.StringBuilder)->Buffer, 0);
}
else
{
PhShowInformation2(WindowHandle, L"Unable to analyze the thread.", L"%s", L"The thread does not appear to be waiting.");
}
PhDeleteStringBuilder(&context.StringBuilder);
}
VOID PhpAnalyzeWaitPassive(
_In_ HWND WindowHandle,
_In_ HANDLE ProcessId,
_In_ HANDLE ThreadId
)
{
NTSTATUS status;
HANDLE processHandle = NULL;
HANDLE threadHandle = NULL;
PPH_STRING lastSystemCallName;
THREAD_LAST_SYSCALL_INFORMATION lastSystemCall;
PH_STRING_BUILDER stringBuilder;
PPH_STRING string;
if (!NT_SUCCESS(status = PhOpenProcess(&processHandle, PROCESS_DUP_HANDLE, ProcessId)))
{
PhShowStatus(WindowHandle, L"Unable to open the process.", status, 0);
goto CleanupExit;
}
if (!NT_SUCCESS(status = PhOpenThread(&threadHandle, THREAD_GET_CONTEXT, ThreadId)))
{
PhShowStatus(WindowHandle, L"Unable to open the thread.", status, 0);
return;
}
if (!NT_SUCCESS(status = PhGetThreadLastSystemCall(threadHandle, &lastSystemCall)))
{
PhShowStatus(WindowHandle, L"Unable to determine whether the thread is waiting.", status, 0);
goto CleanupExit;
}
PhInitializeStringBuilder(&stringBuilder, 100);
lastSystemCallName = PhGetSystemCallNumberName(lastSystemCall.SystemCallNumber);
if (!PhIsNullOrEmptyString(lastSystemCallName))
{
PhAppendStringBuilder2(&stringBuilder, L"Thread is waiting on system call: ");
PhAppendStringBuilder(&stringBuilder, &lastSystemCallName->sr);
PhAppendStringBuilder2(&stringBuilder, L"\r\n");
if (
PhEqualString2(lastSystemCallName, L"NtWaitForSingleObject", TRUE)
)
{
string = PhpaGetHandleString(processHandle, lastSystemCall.FirstArgument);
PhAppendFormatStringBuilder(&stringBuilder, L"Thread is waiting for:\r\n");
PhAppendStringBuilder(&stringBuilder, &string->sr);
}
else if (
PhEqualString2(lastSystemCallName, L"NtWaitForMultipleObjects", TRUE) ||
PhEqualString2(lastSystemCallName, L"NtUserMsgWaitForMultipleObjects", TRUE) ||
PhEqualString2(lastSystemCallName, L"NtUserMsgWaitForMultipleObjectsEx", TRUE)
)
{
PhAppendFormatStringBuilder(&stringBuilder, L"Thread is waiting for multiple (%lu) objects.", PtrToUlong(lastSystemCall.FirstArgument));
}
else if (
PhEqualString2(lastSystemCallName, L"NtReadFile", TRUE) ||
PhEqualString2(lastSystemCallName, L"NtWriteFile", TRUE)
)
{
string = PhpaGetHandleString(processHandle, lastSystemCall.FirstArgument);
PhAppendFormatStringBuilder(&stringBuilder, L"Thread is waiting for file I/O:\r\n");
PhAppendStringBuilder(&stringBuilder, &string->sr);
}
else
{
string = PhpaGetSendMessageReceiver(ThreadId);
if (string)
{
PhAppendStringBuilder2(&stringBuilder, L"Thread is sending a USER message:\r\n");
PhAppendStringBuilder(&stringBuilder, &string->sr);
}
else
{
string = PhpaGetAlpcInformation(ThreadId);
if (string)
{
PhAppendStringBuilder2(&stringBuilder, L"Thread is waiting for an ALPC port:\r\n");
PhAppendStringBuilder(&stringBuilder, &string->sr);
}
}
}
}
else
{
PhpInitializeServiceNumbers();
if (lastSystemCall.SystemCallNumber == NumberForWfso)
{
string = PhpaGetHandleString(processHandle, lastSystemCall.FirstArgument);
PhAppendFormatStringBuilder(&stringBuilder, L"Thread is waiting for:\r\n");
PhAppendStringBuilder(&stringBuilder, &string->sr);
}
else if (lastSystemCall.SystemCallNumber == NumberForWfmo)
{
PhAppendFormatStringBuilder(&stringBuilder, L"Thread is waiting for multiple (%lu) objects.", PtrToUlong(lastSystemCall.FirstArgument));
}
else if (lastSystemCall.SystemCallNumber == NumberForRf)
{
string = PhpaGetHandleString(processHandle, lastSystemCall.FirstArgument);
PhAppendFormatStringBuilder(&stringBuilder, L"Thread is waiting for file I/O:\r\n");
PhAppendStringBuilder(&stringBuilder, &string->sr);
}
else
{
string = PhpaGetSendMessageReceiver(ThreadId);
if (string)
{
PhAppendStringBuilder2(&stringBuilder, L"Thread is sending a USER message:\r\n");
PhAppendStringBuilder(&stringBuilder, &string->sr);
}
else
{
string = PhpaGetAlpcInformation(ThreadId);
if (string)
{
PhAppendStringBuilder2(&stringBuilder, L"Thread is waiting for an ALPC port:\r\n");
PhAppendStringBuilder(&stringBuilder, &string->sr);
}
}
}
}
if (stringBuilder.String->Length == 0)
PhAppendStringBuilder2(&stringBuilder, L"Unable to determine why the thread is waiting.");
PhShowInformationDialog(WindowHandle, stringBuilder.String->Buffer, 0);
PhDeleteStringBuilder(&stringBuilder);
CleanupExit:
if (threadHandle)
{
NtClose(threadHandle);
}
if (processHandle)
{
NtClose(processHandle);
}
}
_Function_class_(PH_WALK_THREAD_STACK_CALLBACK)
BOOLEAN NTAPI PhpWalkThreadStackAnalyzeCallback(
_In_ PPH_THREAD_STACK_FRAME StackFrame,
_In_ PVOID Context
)
{
PANALYZE_WAIT_CONTEXT context = (PANALYZE_WAIT_CONTEXT)Context;
PPH_STRING name;
name = PhGetSymbolFromAddress(
context->SymbolProvider,
StackFrame->PcAddress,
NULL,
NULL,
NULL,
NULL
);
if (PhIsNullOrEmptyString(name))
return TRUE;
context->Found = TRUE;
#define FUNC_MATCH(Name) PhStartsWithString2(name, L##Name, TRUE)
#define NT_FUNC_MATCH(Name) ( \
PhStartsWithString2(name, L"ntdll.dll!Nt" L##Name, TRUE) || \
PhStartsWithString2(name, L"ntdll.dll!Zw" L##Name, TRUE) \
)
if (!name)
{
// Dummy
}
else if (FUNC_MATCH("kernel32.dll!Sleep"))
{
PhAppendFormatStringBuilder(
&context->StringBuilder,
L"Thread is sleeping. Timeout: %lu milliseconds.",
PtrToUlong(StackFrame->Params[0])
);
}
else if (NT_FUNC_MATCH("DelayExecution"))
{
BOOLEAN alertable = !!StackFrame->Params[0];
PVOID timeoutAddress = StackFrame->Params[1];
LONGLONG timeout;
if (NT_SUCCESS(PhReadVirtualMemory(
context->ProcessHandle,
timeoutAddress,
&timeout,
sizeof(LONGLONG),
NULL
)))
{
if (timeout < 0)
{
PhAppendFormatStringBuilder(
&context->StringBuilder,
L"Thread is sleeping. Timeout: %llu milliseconds.",
-timeout / PH_TIMEOUT_MS
);
}
else
{
// TODO
}
}
else
{
PhAppendStringBuilder2(
&context->StringBuilder,
L"Thread is sleeping."
);
}
}
else if (NT_FUNC_MATCH("DeviceIoControlFile"))
{
HANDLE handle = (HANDLE)StackFrame->Params[0];
PhAppendStringBuilder2(
&context->StringBuilder,
L"Thread is waiting for an I/O control request:\r\n"
);
PhAppendStringBuilder(
&context->StringBuilder,
&PhpaGetHandleString(context->ProcessHandle, handle)->sr
);
}
else if (NT_FUNC_MATCH("FsControlFile"))
{
HANDLE handle = StackFrame->Params[0];
PhAppendStringBuilder2(
&context->StringBuilder,
L"Thread is waiting for a FS control request:\r\n"
);
PhAppendStringBuilder(
&context->StringBuilder,
&PhpaGetHandleString(context->ProcessHandle, handle)->sr
);
}
else if (NT_FUNC_MATCH("QueryObject"))
{
HANDLE handle = StackFrame->Params[0];
// Use the KiFastSystemCall args if the handle we have is wrong.
if ((ULONG_PTR)handle % 4 != 0 || !handle)
handle = context->PrevParams[1];
PhAppendStringBuilder2(
&context->StringBuilder,
L"Thread is querying an object:\r\n"
);
PhAppendStringBuilder(
&context->StringBuilder,
&PhpaGetHandleString(context->ProcessHandle, handle)->sr
);
}
else if (NT_FUNC_MATCH("ReadFile") || NT_FUNC_MATCH("WriteFile"))
{
HANDLE handle = StackFrame->Params[0];
PhAppendStringBuilder2(
&context->StringBuilder,
L"Thread is waiting for file I/O:\r\n"
);
PhAppendStringBuilder(
&context->StringBuilder,
&PhpaGetHandleString(context->ProcessHandle, handle)->sr
);
}
else if (NT_FUNC_MATCH("RemoveIoCompletion") || NT_FUNC_MATCH("RemoveIoCompletionEx"))
{
HANDLE handle = StackFrame->Params[0];
PhAppendStringBuilder2(
&context->StringBuilder,
L"Thread is waiting for an I/O completion port:\r\n"
);
PhAppendStringBuilder(
&context->StringBuilder,
&PhpaGetHandleString(context->ProcessHandle, handle)->sr
);
}
else if (
NT_FUNC_MATCH("ReplyWaitReceivePort") ||
NT_FUNC_MATCH("RequestWaitReplyPort") ||
NT_FUNC_MATCH("AlpcSendWaitReceivePort")
)
{
HANDLE handle = StackFrame->Params[0];
PPH_STRING alpcInfo;
PhAppendStringBuilder2(
&context->StringBuilder,
L"Thread is waiting for an ALPC port:\r\n"
);
PhAppendStringBuilder(
&context->StringBuilder,
&PhpaGetHandleString(context->ProcessHandle, handle)->sr
);
if (alpcInfo = PhpaGetAlpcInformation(context->ThreadId))
{
PhAppendStringBuilder2(
&context->StringBuilder,
L"\r\n"
);
PhAppendStringBuilder(
&context->StringBuilder,
&alpcInfo->sr
);
}
}
else if (
NT_FUNC_MATCH("SetHighWaitLowEventPair") ||
NT_FUNC_MATCH("SetLowWaitHighEventPair") ||
NT_FUNC_MATCH("WaitHighEventPair") ||
NT_FUNC_MATCH("WaitLowEventPair")
)
{
HANDLE handle = StackFrame->Params[0];
if ((ULONG_PTR)handle % 4 != 0 || !handle)
handle = context->PrevParams[1];
PhAppendFormatStringBuilder(
&context->StringBuilder,
L"Thread is waiting (%s) for an event pair:\r\n",
name->Buffer
);
PhAppendStringBuilder(
&context->StringBuilder,
&PhpaGetHandleString(context->ProcessHandle, handle)->sr
);
}
else if (
FUNC_MATCH("user32.dll!NtUserGetMessage") ||
FUNC_MATCH("user32.dll!NtUserWaitMessage")
)
{
PhAppendStringBuilder2(
&context->StringBuilder,
L"Thread is waiting for a USER message.\r\n"
);
}
else if (FUNC_MATCH("user32.dll!NtUserMessageCall"))
{
PPH_STRING receiverString;
PhAppendStringBuilder2(
&context->StringBuilder,
L"Thread is sending a USER message:\r\n"
);
receiverString = PhpaGetSendMessageReceiver(context->ThreadId);
if (receiverString)
{
PhAppendStringBuilder(&context->StringBuilder, &receiverString->sr);
PhAppendStringBuilder2(&context->StringBuilder, L"\r\n");
}
else
{
PhAppendStringBuilder2(&context->StringBuilder, L"Unknown.\r\n");
}
}
else if (NT_FUNC_MATCH("WaitForDebugEvent"))
{
HANDLE handle = StackFrame->Params[0];
PhAppendStringBuilder2(
&context->StringBuilder,
L"Thread is waiting for a debug event:\r\n"
);
PhAppendStringBuilder(
&context->StringBuilder,
&PhpaGetHandleString(context->ProcessHandle, handle)->sr
);
}
else if (
NT_FUNC_MATCH("WaitForKeyedEvent") ||
NT_FUNC_MATCH("ReleaseKeyedEvent")
)
{
HANDLE handle = StackFrame->Params[0];
ULONG_PTR key = (ULONG_PTR)StackFrame->Params[1];
PhAppendFormatStringBuilder(
&context->StringBuilder,
L"Thread is waiting (%s) for a keyed event (key 0x%Ix):\r\n",
name->Buffer,
key
);
PhAppendStringBuilder(
&context->StringBuilder,
&PhpaGetHandleString(context->ProcessHandle, handle)->sr
);
}
else if (
NT_FUNC_MATCH("WaitForMultipleObjects") ||
FUNC_MATCH("kernel32.dll!WaitForMultipleObjects")
)
{
ULONG numberOfHandles = PtrToUlong(StackFrame->Params[0]);
PVOID addressOfHandles = StackFrame->Params[1];
WAIT_TYPE waitType = (WAIT_TYPE)PtrToUlong(StackFrame->Params[2]);
BOOLEAN alertable = !!StackFrame->Params[3];
if (numberOfHandles > MAXIMUM_WAIT_OBJECTS)
{
numberOfHandles = PtrToUlong(context->PrevParams[1]);
addressOfHandles = context->PrevParams[2];
waitType = (WAIT_TYPE)PtrToUlong(context->PrevParams[3]);
alertable = FALSE;
}
PhpGetWfmoInformation(
context->ProcessHandle,
TRUE, // on x64 this function is only called for WOW64 processes
numberOfHandles,
addressOfHandles,
waitType,
alertable,
&context->StringBuilder
);
}
else if (
NT_FUNC_MATCH("WaitForSingleObject") ||
FUNC_MATCH("kernel32.dll!WaitForSingleObject")
)
{
HANDLE handle = StackFrame->Params[0];
BOOLEAN alertable = !!StackFrame->Params[1];
if ((ULONG_PTR)handle % 4 != 0 || !handle)
{
handle = context->PrevParams[1];
alertable = !!context->PrevParams[2];
}
PhAppendFormatStringBuilder(
&context->StringBuilder,
L"Thread is waiting (%s) for:\r\n",
alertable ? L"alertable" : L"non-alertable"
);
PhAppendStringBuilder(
&context->StringBuilder,
&PhpaGetHandleString(context->ProcessHandle, handle)->sr
);
}
else if (NT_FUNC_MATCH("WaitForWorkViaWorkerFactory"))
{
HANDLE handle = StackFrame->Params[0];
PhAppendStringBuilder2(
&context->StringBuilder,
L"Thread is waiting for work from a worker factory:\r\n"
);
PhAppendStringBuilder(
&context->StringBuilder,
&PhpaGetHandleString(context->ProcessHandle, handle)->sr
);
}
else if (
FUNC_MATCH("win32u.dll!NtUserMsgWaitForMultipleObjects") ||
FUNC_MATCH("win32u.dll!NtUserMsgWaitForMultipleObjectsEx")
)
{
ULONG numberOfHandles = 0;
ULONG milliseconds = 0;
PVOID addressOfHandles = NULL;
BOOLEAN waitAllHandles = FALSE;
BOOLEAN alertable = FALSE;
if (FUNC_MATCH("win32u.dll!NtUserMsgWaitForMultipleObjects"))
{
numberOfHandles = PtrToUlong(StackFrame->Params[0]);
addressOfHandles = StackFrame->Params[1];
waitAllHandles = !!StackFrame->Params[2];
milliseconds = PtrToUlong(StackFrame->Params[3]);
}
else
{
numberOfHandles = PtrToUlong(StackFrame->Params[0]);
addressOfHandles = StackFrame->Params[1];
milliseconds = PtrToUlong(StackFrame->Params[2]);
ULONG flags = PtrToUlong(StackFrame->Params[3]);
if (FlagOn(flags, MWMO_ALERTABLE))
{
alertable = TRUE;
}
if (FlagOn(flags, MWMO_WAITALL))
{
waitAllHandles = TRUE;
}
}
// if (numberOfHandles > MAXIMUM_WAIT_OBJECTS)
PhpGetWfmoInformation(
context->ProcessHandle,
TRUE,
numberOfHandles,
addressOfHandles,
waitAllHandles ? WaitAll : WaitAny,
alertable,
&context->StringBuilder
);
}
else
{
context->Found = FALSE;
}
PhDereferenceObject(name);
memcpy(&context->PrevParams, StackFrame->Params, sizeof(StackFrame->Params));
return !context->Found;
}
VOID PhpAnalyzeWaitFallbacks(
_In_ PANALYZE_WAIT_CONTEXT Context
)
{
PPH_STRING info;
// We didn't detect NtUserMessageCall, but this may still apply due to another
// win32k system call (e.g. from EnableWindow).
if (!Context->Found && (info = PhpaGetSendMessageReceiver(Context->ThreadId)))
{
PhAppendStringBuilder2(
&Context->StringBuilder,
L"Thread is sending a USER message:\r\n"
);
PhAppendStringBuilder(&Context->StringBuilder, &info->sr);
PhAppendStringBuilder2(&Context->StringBuilder, L"\r\n");
Context->Found = TRUE;
}
// Nt(Alpc)ConnectPort doesn't get detected anywhere else.
if (!Context->Found && (info = PhpaGetAlpcInformation(Context->ThreadId)))
{
PhAppendStringBuilder2(
&Context->StringBuilder,
L"Thread is waiting for an ALPC port:\r\n"
);
PhAppendStringBuilder(&Context->StringBuilder, &info->sr);
PhAppendStringBuilder2(&Context->StringBuilder, L"\r\n");
Context->Found = TRUE;
}
}
static BOOLEAN PhpWaitUntilThreadIsWaiting(
_In_ HANDLE ThreadHandle
)
{
ULONG attempts;
BOOLEAN isWaiting = FALSE;
THREAD_BASIC_INFORMATION basicInfo;
if (!NT_SUCCESS(PhGetThreadBasicInformation(ThreadHandle, &basicInfo)))
return FALSE;
for (attempts = 0; attempts < 20; attempts++)
{
PVOID processes;
PSYSTEM_PROCESS_INFORMATION processInfo;
ULONG i;
PhDelayExecution(100);
if (!NT_SUCCESS(PhEnumProcesses(&processes)))
break;
processInfo = PhFindProcessInformation(processes, basicInfo.ClientId.UniqueProcess);
if (processInfo)
{
for (i = 0; i < processInfo->NumberOfThreads; i++)
{
if (
processInfo->Threads[i].ClientId.UniqueThread == basicInfo.ClientId.UniqueThread &&
processInfo->Threads[i].ThreadState == Waiting &&
(processInfo->Threads[i].WaitReason == UserRequest ||
processInfo->Threads[i].WaitReason == Executive)
)
{
isWaiting = TRUE;
break;
}
}
}
PhFree(processes);
if (isWaiting)
break;
PhDelayExecution(500);
}
return isWaiting;
}
_Success_(return)
static BOOLEAN PhpGetThreadLastSystemCallNumber(
_In_ HANDLE ThreadHandle,
_Out_ PUSHORT LastSystemCallNumber
)
{
THREAD_LAST_SYSCALL_INFORMATION lastSystemCall;
if (NT_SUCCESS(PhGetThreadLastSystemCall(ThreadHandle, &lastSystemCall)))
{
*LastSystemCallNumber = lastSystemCall.SystemCallNumber;
return TRUE;
}
return FALSE;
}
_Function_class_(USER_THREAD_START_ROUTINE)
static NTSTATUS PhpWfsoThreadStart(
_In_ PVOID Parameter
)
{
HANDLE eventHandle;
LARGE_INTEGER timeout;
eventHandle = Parameter;
timeout.QuadPart = -(LONGLONG)UInt32x32To64(5, PH_TIMEOUT_SEC);
NtWaitForSingleObject(eventHandle, FALSE, &timeout);
return STATUS_SUCCESS;
}
_Function_class_(USER_THREAD_START_ROUTINE)
static NTSTATUS PhpWfmoThreadStart(
_In_ PVOID Parameter
)
{
HANDLE eventHandle;
LARGE_INTEGER timeout;
eventHandle = Parameter;
timeout.QuadPart = -(LONGLONG)UInt32x32To64(5, PH_TIMEOUT_SEC);
NtWaitForMultipleObjects(1, &eventHandle, WaitAll, FALSE, &timeout);
return STATUS_SUCCESS;
}
_Function_class_(USER_THREAD_START_ROUTINE)
static NTSTATUS PhpRfThreadStart(
_In_ PVOID Parameter
)
{
HANDLE fileHandle;
IO_STATUS_BLOCK isb;
ULONG data;
fileHandle = Parameter;
NtReadFile(fileHandle, NULL, NULL, NULL, &isb, &data, sizeof(ULONG), NULL, NULL);
return STATUS_SUCCESS;
}
VOID PhpInitializeServiceNumbers(
VOID
)
{
if (PhBeginInitOnce(&ServiceNumbersInitOnce))
{
NTSTATUS status;
HANDLE eventHandle;
HANDLE threadHandle;
HANDLE pipeReadHandle;
HANDLE pipeWriteHandle;
// The ThreadLastSystemCall info class only works when the thread is in the Waiting
// state. We'll create a thread which blocks on an event object we create, then wait
// until it is in the Waiting state. Only then can we query the thread using
// ThreadLastSystemCall.
// NtWaitForSingleObject
status = PhCreateEvent(&eventHandle, EVENT_ALL_ACCESS, NotificationEvent, FALSE);
if (NT_SUCCESS(status))
{
if (NT_SUCCESS(PhCreateThreadEx(&threadHandle, PhpWfsoThreadStart, eventHandle)))
{
if (PhpWaitUntilThreadIsWaiting(threadHandle))
{
PhpGetThreadLastSystemCallNumber(threadHandle, &NumberForWfso);
}
// Allow the thread to exit.
NtSetEvent(eventHandle, NULL);
NtClose(threadHandle);
}
NtClose(eventHandle);
}
// NtWaitForMultipleObjects
status = PhCreateEvent(&eventHandle, EVENT_ALL_ACCESS, NotificationEvent, FALSE);
if (NT_SUCCESS(status))
{
if (NT_SUCCESS(PhCreateThreadEx(&threadHandle, PhpWfmoThreadStart, eventHandle)))
{
if (PhpWaitUntilThreadIsWaiting(threadHandle))
{
PhpGetThreadLastSystemCallNumber(threadHandle, &NumberForWfmo);
}
NtSetEvent(eventHandle, NULL);
NtClose(threadHandle);
}
NtClose(eventHandle);
}
// NtReadFile
status = PhCreatePipe(&pipeReadHandle, &pipeWriteHandle);
if (NT_SUCCESS(status))
{
if (NT_SUCCESS(PhCreateThreadEx(&threadHandle, PhpRfThreadStart, pipeReadHandle)))
{
ULONG data = 0;
IO_STATUS_BLOCK isb;
if (PhpWaitUntilThreadIsWaiting(threadHandle))
{
PhpGetThreadLastSystemCallNumber(threadHandle, &NumberForRf);
}
NtWriteFile(pipeWriteHandle, NULL, NULL, NULL, &isb, &data, sizeof(data), NULL, NULL);
NtClose(threadHandle);
}
NtClose(pipeReadHandle);
NtClose(pipeWriteHandle);
}
PhEndInitOnce(&ServiceNumbersInitOnce);
}
}
PPH_STRING PhpaGetHandleString(
_In_ HANDLE ProcessHandle,
_In_ HANDLE Handle
)
{
PPH_STRING typeName = NULL;
PPH_STRING name = NULL;
PPH_STRING result;
PhGetHandleInformation(
ProcessHandle,
Handle,
ULONG_MAX,
NULL,
&typeName,
NULL,
&name
);
if (typeName && name)
{
result = PhaFormatString(
L"Handle 0x%lx (%s): %s",
HandleToUlong(Handle),
typeName->Buffer,
!PhIsNullOrEmptyString(name) ? name->Buffer : L"(unnamed object)"
);
}
else
{
result = PhaFormatString(
L"Handle 0x%lx: (error querying handle)",
HandleToUlong(Handle)
);
}
if (typeName)
PhDereferenceObject(typeName);
if (name)
PhDereferenceObject(name);
return result;
}
VOID PhpGetWfmoInformation(
_In_ HANDLE ProcessHandle,
_In_ BOOLEAN IsWow64Process,
_In_ ULONG NumberOfHandles,
_In_ PHANDLE AddressOfHandles,
_In_ WAIT_TYPE WaitType,
_In_ BOOLEAN Alertable,
_Inout_ PPH_STRING_BUILDER StringBuilder
)
{
NTSTATUS status;
HANDLE handles[MAXIMUM_WAIT_OBJECTS];
ULONG i;
status = STATUS_SUCCESS;
if (NumberOfHandles <= MAXIMUM_WAIT_OBJECTS)
{
#ifdef _WIN64
if (IsWow64Process)
{
ULONG handles32[MAXIMUM_WAIT_OBJECTS];
if (NT_SUCCESS(status = PhReadVirtualMemory(
ProcessHandle,
AddressOfHandles,
handles32,
NumberOfHandles * sizeof(ULONG),
NULL
)))
{
for (i = 0; i < NumberOfHandles; i++)
handles[i] = UlongToHandle(handles32[i]);
}
}
else
{
#endif
status = PhReadVirtualMemory(
ProcessHandle,
AddressOfHandles,
handles,
NumberOfHandles * sizeof(HANDLE),
NULL
);
#ifdef _WIN64
}
#endif
if (NT_SUCCESS(status))
{
PhAppendFormatStringBuilder(
StringBuilder,
L"Thread is waiting (%s, %s) for:\r\n",
Alertable ? L"alertable" : L"non-alertable",
WaitType == WaitAll ? L"wait all" : L"wait any"
);
for (i = 0; i < NumberOfHandles; i++)
{
PhAppendStringBuilder(
StringBuilder,
&PhpaGetHandleString(ProcessHandle, handles[i])->sr
);
PhAppendStringBuilder2(
StringBuilder,
L"\r\n"
);
}
}
}
if (!NT_SUCCESS(status) || NumberOfHandles > MAXIMUM_WAIT_OBJECTS)
{
PhAppendStringBuilder2(
StringBuilder,
L"Thread is waiting for multiple objects."
);
}
}
PPH_STRING PhpaGetSendMessageReceiver(
_In_ HANDLE ThreadId
)
{
HWND windowHandle;
CLIENT_ID clientId;
PPH_STRING clientIdName;
WCHAR windowClass[64];
PPH_STRING windowText;
if (!NT_SUCCESS(PhGetSendMessageReceiver(ThreadId, &windowHandle)))
return NULL;
if (!NT_SUCCESS(PhGetWindowClientId(windowHandle, &clientId)))
return NULL;
clientIdName = PH_AUTO(PhGetClientIdName(&clientId));
if (!GetClassName(windowHandle, windowClass, sizeof(windowClass) / sizeof(WCHAR)))
windowClass[0] = UNICODE_NULL;
windowText = PH_AUTO(PhGetWindowText(windowHandle));
return PhaFormatString(L"Window 0x%Ix (%s): %s \"%s\"", (ULONG_PTR)windowHandle, clientIdName->Buffer, windowClass, PhGetStringOrEmpty(windowText));
}
PPH_STRING PhpaGetAlpcInformation(
_In_ HANDLE ThreadId
)
{
NTSTATUS status;
PPH_STRING string = NULL;
HANDLE threadHandle;
PALPC_SERVER_INFORMATION serverInfo;
ULONG bufferLength;
if (!NT_SUCCESS(PhOpenThread(&threadHandle, THREAD_QUERY_INFORMATION, ThreadId)))
return NULL;
bufferLength = 0x110;
serverInfo = PhAllocate(bufferLength);
serverInfo->In.ThreadHandle = threadHandle;
status = NtAlpcQueryInformation(NULL, AlpcServerInformation, serverInfo, bufferLength, &bufferLength);
if (status == STATUS_INFO_LENGTH_MISMATCH)
{
PhFree(serverInfo);
serverInfo = PhAllocate(bufferLength);
serverInfo->In.ThreadHandle = threadHandle;
status = NtAlpcQueryInformation(NULL, AlpcServerInformation, serverInfo, bufferLength, &bufferLength);
}
if (NT_SUCCESS(status) && serverInfo->Out.ThreadBlocked)
{
CLIENT_ID clientId;
PPH_STRING clientIdName;
clientId.UniqueProcess = serverInfo->Out.ConnectedProcessId;
clientId.UniqueThread = NULL;
clientIdName = PH_AUTO(PhGetClientIdName(&clientId));
string = PhaFormatString(L"ALPC Port: %.*s (%s)", serverInfo->Out.ConnectionPortName.Length / sizeof(WCHAR), serverInfo->Out.ConnectionPortName.Buffer, clientIdName->Buffer);
}
PhFree(serverInfo);
NtClose(threadHandle);
return string;
}
================================================
FILE: SystemInformer/appsup.c
================================================
/*
* Copyright (c) 2022 Winsider Seminars & Solutions, Inc. All rights reserved.
*
* This file is part of System Informer.
*
* Authors:
*
* wj32 2010-2016
* dmex 2017-2023
*
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
/**
* Determines whether a process is suspended.
*
* \param Process The SYSTEM_PROCESS_INFORMATION structure
* of the process.
*/
BOOLEAN PhGetProcessIsSuspended(
_In_ PSYSTEM_PROCESS_INFORMATION Process
)
{
ULONG i;
for (i = 0; i < Process->NumberOfThreads; i++)
{
if (
Process->Threads[i].ThreadState != Waiting ||
Process->Threads[i].WaitReason != Suspended
)
return FALSE;
}
return Process->UserTime.QuadPart + Process->KernelTime.QuadPart != 0 && Process->NumberOfThreads != 0;
}
BOOLEAN PhIsProcessSuspended(
_In_ HANDLE ProcessId
)
{
BOOLEAN suspended = FALSE;
PVOID processes;
PSYSTEM_PROCESS_INFORMATION process;
if (NT_SUCCESS(PhEnumProcesses(&processes)))
{
if (process = PhFindProcessInformation(processes, ProcessId))
{
suspended = PhGetProcessIsSuspended(process);
}
PhFree(processes);
}
return suspended;
}
BOOLEAN PhIsProcessBackground(
_In_ ULONG PriorityClass
)
{
// This is similar to PROCESS_MODE_BACKGROUND_BEGIN (dmex)
if (
PriorityClass == PROCESS_PRIORITY_CLASS_BELOW_NORMAL ||
PriorityClass == PROCESS_PRIORITY_CLASS_IDLE
)
{
return TRUE;
}
return FALSE;
}
static CONST PH_KEY_VALUE_PAIR ProcessPriorityClassTypePairs[] =
{
SIP(SREF(L"Unknown"), PROCESS_PRIORITY_CLASS_UNKNOWN),
SIP(SREF(L"Idle"), PROCESS_PRIORITY_CLASS_IDLE),
SIP(SREF(L"Normal"), PROCESS_PRIORITY_CLASS_NORMAL),
SIP(SREF(L"High"), PROCESS_PRIORITY_CLASS_HIGH),
SIP(SREF(L"Real time"), PROCESS_PRIORITY_CLASS_REALTIME),
SIP(SREF(L"Below normal"), PROCESS_PRIORITY_CLASS_BELOW_NORMAL),
SIP(SREF(L"Above normal"), PROCESS_PRIORITY_CLASS_ABOVE_NORMAL),
};
PCPH_STRINGREF PhGetProcessPriorityClassString(
_In_ ULONG PriorityClass
)
{
PCPH_STRINGREF string;
if (PhIndexStringRefSiKeyValuePairs(
ProcessPriorityClassTypePairs,
sizeof(ProcessPriorityClassTypePairs),
PriorityClass,
&string
))
{
return string;
}
static_assert(ARRAYSIZE(ProcessPriorityClassTypePairs) == PROCESS_PRIORITY_CLASS_ABOVE_NORMAL + 1, "PriorityClassString must equal PROCESS_PRIORITY_CLASS_MAX");
return (PCPH_STRINGREF)ProcessPriorityClassTypePairs[PROCESS_PRIORITY_CLASS_UNKNOWN].Key;
//switch (PriorityClass)
//{
//case PROCESS_PRIORITY_CLASS_REALTIME:
// return L"Real time";
//case PROCESS_PRIORITY_CLASS_HIGH:
// return L"High";
//case PROCESS_PRIORITY_CLASS_ABOVE_NORMAL:
// return L"Above normal";
//case PROCESS_PRIORITY_CLASS_NORMAL:
// return L"Normal";
//case PROCESS_PRIORITY_CLASS_BELOW_NORMAL:
// return L"Below normal";
//case PROCESS_PRIORITY_CLASS_IDLE:
// return L"Idle";
//case PROCESS_PRIORITY_CLASS_UNKNOWN:
//default:
// return L"Unknown";
//}
}
static CONST PH_KEY_VALUE_PAIR PhProtectedTypeStrings[] =
{
SIP(L"None", NULL), // PsProtectedTypeNone
SIP(L"Light", PsProtectedTypeProtectedLight),
SIP(L"Full", PsProtectedTypeProtected),
};
static CONST PH_KEY_VALUE_PAIR PhProtectedSignerStrings[] =
{
SIP(L"None", NULL), // PsProtectedSignerNone
SIP(L"Authenticode", PsProtectedSignerAuthenticode),
SIP(L"CodeGen", PsProtectedSignerCodeGen),
SIP(L"Antimalware", PsProtectedSignerAntimalware),
SIP(L"Lsa", PsProtectedSignerLsa),
SIP(L"Windows", PsProtectedSignerWindows),
SIP(L"WinTcb", PsProtectedSignerWinTcb),
SIP(L"WinSystem", PsProtectedSignerWinSystem),
SIP(L"StoreApp", PsProtectedSignerApp),
};
static_assert(RTL_NUMBER_OF(PhProtectedTypeStrings) == PsProtectedTypeMax, "PsProtectedTypeStrings must equal PsProtectedTypeMax (value offsets)");
static_assert(RTL_NUMBER_OF(PhProtectedSignerStrings) == PsProtectedSignerMax, "PhProtectedSignerStrings must equal PsProtectedSignerMax (value offsets)");
PPH_STRING PhGetProcessProtectionString(
_In_ PS_PROTECTION Protection,
_In_ BOOLEAN IsSecureProcess
)
{
if (Protection.Level)
{
static PPH_STRING PhpProtectionNoneString = NULL;
PH_FORMAT format[6];
ULONG count = 0;
PCWSTR type;
PCWSTR signer;
if (!PhpProtectionNoneString)
PhpProtectionNoneString = PhCreateString(L"None");
if (IsSecureProcess)
{
PhInitFormatS(&format[count++], L"Secure ");
}
if (PhIndexStringSiKeyValuePairs(
PhProtectedTypeStrings,
sizeof(PhProtectedTypeStrings),
Protection.Type,
&type
))
{
PhInitFormatS(&format[count++], type);
}
else
{
PhInitFormatS(&format[count++], L"Unknown");
}
if (PhIndexStringSiKeyValuePairs(
PhProtectedSignerStrings,
sizeof(PhProtectedSignerStrings),
Protection.Signer,
&signer
))
{
PhInitFormatS(&format[count++], L" (");
PhInitFormatS(&format[count++], signer);
PhInitFormatS(&format[count++], L")");
}
else
{
PhInitFormatS(&format[count++], L" (");
PhInitFormatS(&format[count++], L"Unknown");
PhInitFormatS(&format[count++], L")");
}
if (Protection.Audit)
{
PhInitFormatS(&format[count++], L" (Audit)");
}
return PhFormat(format, count, 10);
}
else
{
static PPH_STRING PhpProtectionSecureIUMString = NULL;
if (!PhpProtectionSecureIUMString)
PhpProtectionSecureIUMString = PhCreateString(L"Secure (IUM)");
if (IsSecureProcess)
{
return PhReferenceObject(PhpProtectionSecureIUMString);
}
}
return NULL;
}
/**
* Determines the OS compatibility context of a process.
*
* \param ProcessHandle A handle to a process.
* \param Guid A variable which receives a GUID identifying an
* operating system version.
*/
NTSTATUS PhGetProcessSwitchContext(
_In_ HANDLE ProcessHandle,
_Out_ PGUID Guid
)
{
NTSTATUS status;
PROCESS_BASIC_INFORMATION basicInfo;
#ifdef _WIN64
PVOID peb32;
ULONG data32;
#endif
PVOID data;
// Reverse-engineered from WdcGetProcessSwitchContext (wdc.dll).
// On Windows 8, the function is now SdbGetAppCompatData (apphelp.dll).
// On Windows 10, the function is again WdcGetProcessSwitchContext.
#ifdef _WIN64
if (NT_SUCCESS(PhGetProcessPeb32(ProcessHandle, &peb32)) && peb32)
{
if (WindowsVersion >= WINDOWS_8)
{
if (!NT_SUCCESS(status = PhReadVirtualMemory(
ProcessHandle,
PTR_ADD_OFFSET(peb32, FIELD_OFFSET(PEB32, pShimData)),
&data32,
sizeof(ULONG),
NULL
)))
return status;
}
else
{
if (!NT_SUCCESS(status = PhReadVirtualMemory(
ProcessHandle,
PTR_ADD_OFFSET(peb32, FIELD_OFFSET(PEB32, pContextData)),
&data32,
sizeof(ULONG),
NULL
)))
return status;
}
data = UlongToPtr(data32);
}
else
{
#endif
if (!NT_SUCCESS(status = PhGetProcessBasicInformation(ProcessHandle, &basicInfo)))
return status;
if (WindowsVersion >= WINDOWS_8)
{
if (!NT_SUCCESS(status = PhReadVirtualMemory(
ProcessHandle,
PTR_ADD_OFFSET(basicInfo.PebBaseAddress, FIELD_OFFSET(PEB, pShimData)),
&data,
sizeof(PVOID),
NULL
)))
return status;
}
else
{
if (!NT_SUCCESS(status = PhReadVirtualMemory(
ProcessHandle,
PTR_ADD_OFFSET(basicInfo.PebBaseAddress, FIELD_OFFSET(PEB, pContextData)),
&data,
sizeof(PVOID),
NULL
)))
return status;
}
#ifdef _WIN64
}
#endif
if (!data)
return STATUS_UNSUCCESSFUL; // no compatibility context data
if (WindowsVersion >= WINDOWS_10_RS5)
{
if (!NT_SUCCESS(status = PhReadVirtualMemory(
ProcessHandle,
PTR_ADD_OFFSET(data, 2040 + 24), // Magic value from SbReadProcContextByHandle
Guid,
sizeof(GUID),
NULL
)))
return status;
}
else if (WindowsVersion >= WINDOWS_10_RS2)
{
if (!NT_SUCCESS(status = PhReadVirtualMemory(
ProcessHandle,
PTR_ADD_OFFSET(data, 1544),
Guid,
sizeof(GUID),
NULL
)))
return status;
}
else if (WindowsVersion >= WINDOWS_10)
{
if (!NT_SUCCESS(status = PhReadVirtualMemory(
ProcessHandle,
PTR_ADD_OFFSET(data, 2040 + 24), // Magic value from SbReadProcContextByHandle
Guid,
sizeof(GUID),
NULL
)))
return status;
}
else if (WindowsVersion >= WINDOWS_8_1)
{
if (!NT_SUCCESS(status = PhReadVirtualMemory(
ProcessHandle,
PTR_ADD_OFFSET(data, 2040 + 16), // Magic value from SbReadProcContextByHandle
Guid,
sizeof(GUID),
NULL
)))
return status;
}
else if (WindowsVersion >= WINDOWS_8)
{
if (!NT_SUCCESS(status = PhReadVirtualMemory(
ProcessHandle,
PTR_ADD_OFFSET(data, 2040), // Magic value from SbReadProcContextByHandle
Guid,
sizeof(GUID),
NULL
)))
return status;
}
else
{
if (!NT_SUCCESS(status = PhReadVirtualMemory(
ProcessHandle,
PTR_ADD_OFFSET(data, 32), // Magic value from WdcGetProcessSwitchContext
Guid,
sizeof(GUID),
NULL
)))
return status;
}
//static ULONG (WINAPI *BaseReadAppCompatDataForProcess_I)(
// _In_ HANDLE ProcessHandle,
// _Out_ PULONG_PTR ShimData,
// _Out_opt_ PULONG_PTR ShimDataBaseAddress
// ) = NULL;
//static ULONG (WINAPI *BaseFreeAppCompatDataForProcess_I)(
// _In_ ULONG_PTR ShimData
// ) = NULL;
//
//if (!BaseReadAppCompatDataForProcess_I)
// BaseReadAppCompatDataForProcess_I = PhGetDllProcedureAddressZ(L"kernelbase.dll", "BaseReadAppCompatDataForProcess", 0);
//if (!BaseFreeAppCompatDataForProcess_I)
// BaseFreeAppCompatDataForProcess_I = PhGetDllProcedureAddressZ(L"kernelbase.dll", "BaseFreeAppCompatDataForProcess", 0);
//
//if (BaseReadAppCompatDataForProcess_I && BaseFreeAppCompatDataForProcess_I)
//{
// ULONG_PTR pShimData;
//
// if (BaseReadAppCompatDataForProcess_I(ProcessHandle, &pShimData, NULL) == ERROR_SUCCESS)
// {
// if (WindowsVersion >= WINDOWS_10_RS5)
// {
// *Guid = *(PGUID)PTR_ADD_OFFSET(pShimData, 2040 + 24);
// BaseFreeAppCompatDataForProcess_I(pShimData);
// return STATUS_SUCCESS;
// }
//
// BaseFreeAppCompatDataForProcess_I(pShimData);
// }
//
// return STATUS_UNSUCCESSFUL;
//}
return STATUS_SUCCESS;
}
NTSTATUS PhGetProcessDefaultHeap(
_In_ HANDLE ProcessHandle,
_Out_ PVOID *Heap
)
{
NTSTATUS status;
#ifdef _WIN64
BOOLEAN IsWow64;
status = PhGetProcessIsWow64(ProcessHandle, &IsWow64);
if (!NT_SUCCESS(status))
return status;
if (IsWow64)
{
PVOID peb32;
ULONG processHeapsPtr32;
status = PhGetProcessPeb32(ProcessHandle, &peb32);
if (!NT_SUCCESS(status))
return status;
status = PhReadVirtualMemory(
ProcessHandle,
PTR_ADD_OFFSET(peb32, UFIELD_OFFSET(PEB32, ProcessHeap)),
&processHeapsPtr32,
sizeof(ULONG),
NULL
);
if (!NT_SUCCESS(status))
return status;
if (Heap)
{
*Heap = UlongToPtr(processHeapsPtr32);
}
return status;
}
else
{
#endif
PROCESS_BASIC_INFORMATION basicInfo;
PVOID processHeapsPtr;
status = PhGetProcessBasicInformation(
ProcessHandle,
&basicInfo
);
if (!NT_SUCCESS(status))
return status;
status = PhReadVirtualMemory(
ProcessHandle,
PTR_ADD_OFFSET(basicInfo.PebBaseAddress, UFIELD_OFFSET(PEB, ProcessHeap)),
&processHeapsPtr,
sizeof(PVOID),
NULL
);
if (!NT_SUCCESS(status))
return status;
if (Heap)
{
*Heap = processHeapsPtr;
}
return status;
#ifdef _WIN64
}
#endif
}
/**
* Determines the type of a process based on its image file name.
*
* \param ProcessHandle A handle to a process.
* \param KnownProcessType A variable which receives the process
* type.
*/
NTSTATUS PhGetProcessKnownType(
_In_ HANDLE ProcessHandle,
_Out_ PH_KNOWN_PROCESS_TYPE *KnownProcessType
)
{
NTSTATUS status;
PROCESS_BASIC_INFORMATION basicInfo;
PPH_STRING fileName;
if (!NT_SUCCESS(status = PhGetProcessBasicInformation(
ProcessHandle,
&basicInfo
)))
return status;
if (basicInfo.UniqueProcessId == SYSTEM_PROCESS_ID)
{
*KnownProcessType = SystemProcessType;
return STATUS_SUCCESS;
}
if (!NT_SUCCESS(status = PhGetProcessImageFileName(
ProcessHandle,
&fileName
)))
{
return status;
}
*KnownProcessType = PhGetProcessKnownTypeEx(
basicInfo.UniqueProcessId,
fileName
);
PhDereferenceObject(fileName);
return status;
}
PH_KNOWN_PROCESS_TYPE PhGetProcessKnownTypeEx(
_In_opt_ HANDLE ProcessId,
_In_ PPH_STRING FileName
)
{
PH_KNOWN_PROCESS_TYPE knownProcessType;
PH_STRINGREF systemRootPrefix;
PPH_STRING fileName;
PH_STRINGREF name;
#ifdef _WIN64
BOOLEAN isWow64 = FALSE;
#endif
if (ProcessId == SYSTEM_PROCESS_ID || ProcessId == SYSTEM_IDLE_PROCESS_ID)
return SystemProcessType;
if (PhIsNullOrEmptyString(FileName))
return UnknownProcessType;
PhGetNtSystemRoot(&systemRootPrefix);
fileName = PhReferenceObject(FileName);
name = fileName->sr;
knownProcessType = UnknownProcessType;
if (PhStartsWithStringRef(&name, &systemRootPrefix, TRUE))
{
// Skip the system root, and we now have three cases:
// 1. \\xyz.exe - Windows executable.
// 2. \\System32\\xyz.exe - system32 executable.
// 3. \\SysWow64\\xyz.exe - system32 executable + WOW64.
PhSkipStringRef(&name, systemRootPrefix.Length);
if (PhEqualStringRef2(&name, L"\\explorer.exe", TRUE))
{
knownProcessType = ExplorerProcessType;
}
else if (
PhStartsWithStringRef2(&name, L"\\System32", TRUE)
#ifdef _WIN64
|| (PhStartsWithStringRef2(&name, L"\\SysWOW64", TRUE) && (isWow64 = TRUE, TRUE)) // ugly but necessary
#ifdef _M_ARM64
|| (PhStartsWithStringRef2(&name, L"\\SysArm32", TRUE) && (isWow64 = TRUE, TRUE)) // ugly but necessary
|| (PhStartsWithStringRef2(&name, L"\\SyChpe32", TRUE) && (isWow64 = TRUE, TRUE)) // ugly but necessary
#endif
#endif
)
{
// System32, SysWow64, SysArm32, and SyChpe32 are all 8 characters long.
PhSkipStringRef(&name, 9 * sizeof(WCHAR));
if (FALSE)
; // Dummy
else if (PhEqualStringRef2(&name, L"\\smss.exe", TRUE))
knownProcessType = SessionManagerProcessType;
else if (PhEqualStringRef2(&name, L"\\csrss.exe", TRUE))
knownProcessType = WindowsSubsystemProcessType;
else if (PhEqualStringRef2(&name, L"\\wininit.exe", TRUE))
knownProcessType = WindowsStartupProcessType;
else if (PhEqualStringRef2(&name, L"\\services.exe", TRUE))
knownProcessType = ServiceControlManagerProcessType;
else if (PhEqualStringRef2(&name, L"\\lsass.exe", TRUE))
knownProcessType = LocalSecurityAuthorityProcessType;
else if (PhEqualStringRef2(&name, L"\\lsm.exe", TRUE))
knownProcessType = LocalSessionManagerProcessType;
else if (PhEqualStringRef2(&name, L"\\winlogon.exe", TRUE))
knownProcessType = WindowsLogonProcessType;
else if (PhEqualStringRef2(&name, L"\\svchost.exe", TRUE))
knownProcessType = ServiceHostProcessType;
else if (PhEqualStringRef2(&name, L"\\rundll32.exe", TRUE))
knownProcessType = RunDllAsAppProcessType;
else if (PhEqualStringRef2(&name, L"\\dllhost.exe", TRUE))
knownProcessType = ComSurrogateProcessType;
else if (PhEqualStringRef2(&name, L"\\taskeng.exe", TRUE))
knownProcessType = TaskHostProcessType;
else if (PhEqualStringRef2(&name, L"\\taskhost.exe", TRUE))
knownProcessType = TaskHostProcessType;
else if (PhEqualStringRef2(&name, L"\\taskhostex.exe", TRUE))
knownProcessType = TaskHostProcessType;
else if (PhEqualStringRef2(&name, L"\\taskhostw.exe", TRUE))
knownProcessType = TaskHostProcessType;
else if (PhEqualStringRef2(&name, L"\\wudfhost.exe", TRUE))
knownProcessType = UmdfHostProcessType;
else if (PhEqualStringRef2(&name, L"\\wbem\\WmiPrvSE.exe", TRUE))
knownProcessType = WmiProviderHostType;
//else if (PhEqualStringRef2(&name, L"\\MicrosoftEdgeCP.exe", TRUE)) // RS5
// knownProcessType = EdgeProcessType;
//else if (PhEqualStringRef2(&name, L"\\MicrosoftEdgeSH.exe", TRUE)) // RS5
// knownProcessType = EdgeProcessType;
#ifdef _M_IX86
else if (PhEqualStringRef2(&name, L"\\ntvdm.exe", TRUE))
knownProcessType = NtVdmHostProcessType;
#endif
}
//else
//{
// if (PhEndsWithStringRef2(&name, L"\\MicrosoftEdgeCP.exe", TRUE)) // RS4
// knownProcessType = EdgeProcessType;
// else if (PhEndsWithStringRef2(&name, L"\\MicrosoftEdge.exe", TRUE))
// knownProcessType = EdgeProcessType;
// else if (PhEndsWithStringRef2(&name, L"\\ServiceWorkerHost.exe", TRUE))
// knownProcessType = EdgeProcessType;
// else if (PhEndsWithStringRef2(&name, L"\\Windows.WARP.JITService.exe", TRUE))
// knownProcessType = EdgeProcessType;
//}
}
PhDereferenceObject(fileName);
#ifdef _WIN64
if (isWow64)
knownProcessType |= KnownProcessWow64;
#endif
return knownProcessType;
}
_Function_class_(PH_COMMAND_LINE_CALLBACK)
static BOOLEAN NTAPI PhpSvchostCommandLineCallback(
_In_opt_ PCPH_COMMAND_LINE_OPTION Option,
_In_opt_ PPH_STRING Value,
_In_opt_ PVOID Context
)
{
PPH_KNOWN_PROCESS_COMMAND_LINE knownCommandLine = Context;
if (knownCommandLine && Option && Option->Id == 1)
{
PhSwapReference(&knownCommandLine->ServiceHost.GroupName, Value);
}
return TRUE;
}
_Success_(return)
BOOLEAN PhaGetProcessKnownCommandLine(
_In_ PPH_STRING CommandLine,
_In_ PH_KNOWN_PROCESS_TYPE KnownProcessType,
_Out_ PPH_KNOWN_PROCESS_COMMAND_LINE KnownCommandLine
)
{
switch (KnownProcessType & KnownProcessTypeMask)
{
case ServiceHostProcessType:
{
// svchost.exe -k
static CONST PH_COMMAND_LINE_OPTION options[] =
{
{ 1, L"k", MandatoryArgumentType }
};
KnownCommandLine->ServiceHost.GroupName = NULL;
PhParseCommandLine(
&CommandLine->sr,
options,
sizeof(options) / sizeof(PH_COMMAND_LINE_OPTION),
PH_COMMAND_LINE_IGNORE_UNKNOWN_OPTIONS,
PhpSvchostCommandLineCallback,
KnownCommandLine
);
if (KnownCommandLine->ServiceHost.GroupName)
{
PH_AUTO(KnownCommandLine->ServiceHost.GroupName);
return TRUE;
}
else
{
return FALSE;
}
}
break;
case RunDllAsAppProcessType:
{
// rundll32.exe , ...
SIZE_T i;
PH_STRINGREF dllNamePart;
PH_STRINGREF procedureNamePart;
PPH_STRING dllName;
PPH_STRING procedureName;
i = 0;
// Get the rundll32.exe part.
dllName = PhParseCommandLinePart(&CommandLine->sr, &i);
if (!dllName)
return FALSE;
PhDereferenceObject(dllName);
// Get the DLL name part.
while (i < CommandLine->Length / sizeof(WCHAR) && CommandLine->Buffer[i] == L' ')
i++;
dllName = PhParseCommandLinePart(&CommandLine->sr, &i);
if (!dllName)
return FALSE;
PH_AUTO(dllName);
// The procedure name begins after the last comma.
if (!PhSplitStringRefAtLastChar(&dllName->sr, L',', &dllNamePart, &procedureNamePart))
return FALSE;
dllName = PH_AUTO(PhCreateString2(&dllNamePart));
procedureName = PH_AUTO(PhCreateString2(&procedureNamePart));
// If the DLL name isn't an absolute path, assume it's in system32.
// TODO: Use a proper search function.
if (PhDetermineDosPathNameType(&dllName->sr) == RtlPathTypeRelative)
{
dllName = PhGetSystemDirectoryWin32(&dllName->sr);
}
KnownCommandLine->RunDllAsApp.FileName = dllName;
KnownCommandLine->RunDllAsApp.ProcedureName = procedureName;
}
break;
case ComSurrogateProcessType:
{
// dllhost.exe /processid:
static CONST PH_STRINGREF inprocServer32Name = PH_STRINGREF_INIT(L"InprocServer32");
SIZE_T i;
ULONG_PTR indexOfProcessId;
PPH_STRING argPart;
PPH_STRING guidString;
GUID guid;
HANDLE rootKeyHandle;
HANDLE inprocServer32KeyHandle;
PPH_STRING fileName;
i = 0;
// Get the dllhost.exe part.
argPart = PhParseCommandLinePart(&CommandLine->sr, &i);
if (!argPart)
return FALSE;
PhDereferenceObject(argPart);
// Get the argument part.
while (i < (ULONG)CommandLine->Length / sizeof(WCHAR) && CommandLine->Buffer[i] == L' ')
i++;
argPart = PhParseCommandLinePart(&CommandLine->sr, &i);
if (!argPart)
return FALSE;
PH_AUTO(argPart);
// Find "/processid:"; the GUID is just after that.
PhUpperStringRef(&argPart->sr);
indexOfProcessId = PhFindStringInString(argPart, 0, L"/PROCESSID:");
if (indexOfProcessId == SIZE_MAX)
return FALSE;
guidString = PhaSubstring(
argPart,
indexOfProcessId + 11,
argPart->Length / sizeof(WCHAR) - indexOfProcessId - 11
);
if (!NT_SUCCESS(PhStringToGuid(&guidString->sr, &guid)))
return FALSE;
KnownCommandLine->ComSurrogate.Guid = guid;
KnownCommandLine->ComSurrogate.Name = NULL;
KnownCommandLine->ComSurrogate.FileName = NULL;
// Lookup the GUID in the registry to determine the name and file name.
if (NT_SUCCESS(PhOpenKey(
&rootKeyHandle,
KEY_READ,
PH_KEY_CLASSES_ROOT,
&PhaConcatStrings2(L"CLSID\\", guidString->Buffer)->sr,
0
)))
{
KnownCommandLine->ComSurrogate.Name = PH_AUTO(PhQueryRegistryString(rootKeyHandle, NULL));
if (NT_SUCCESS(PhOpenKey(
&inprocServer32KeyHandle,
KEY_READ,
rootKeyHandle,
&inprocServer32Name,
0
)))
{
KnownCommandLine->ComSurrogate.FileName = PH_AUTO(PhQueryRegistryString(inprocServer32KeyHandle, NULL));
if (fileName = PH_AUTO(PhExpandEnvironmentStrings(&KnownCommandLine->ComSurrogate.FileName->sr)))
{
KnownCommandLine->ComSurrogate.FileName = fileName;
}
NtClose(inprocServer32KeyHandle);
}
NtClose(rootKeyHandle);
}
else if (NT_SUCCESS(PhOpenKey(
&rootKeyHandle,
KEY_READ,
PH_KEY_CLASSES_ROOT,
&PhaConcatStrings2(L"AppID\\", guidString->Buffer)->sr,
0
)))
{
KnownCommandLine->ComSurrogate.Name = PH_AUTO(PhQueryRegistryString(rootKeyHandle, NULL));
NtClose(rootKeyHandle);
}
}
break;
default:
return FALSE;
}
return TRUE;
}
PPH_STRING PhEscapeStringForDelimiter(
_In_ PPH_STRING String,
_In_ WCHAR Delimiter
)
{
PH_STRING_BUILDER stringBuilder;
SIZE_T length;
SIZE_T i;
WCHAR temp[2];
length = String->Length / sizeof(WCHAR);
PhInitializeStringBuilder(&stringBuilder, String->Length / sizeof(WCHAR) * 3);
temp[0] = L'\\';
for (i = 0; i < length; i++)
{
if (String->Buffer[i] == L'\\' || String->Buffer[i] == Delimiter)
{
temp[1] = String->Buffer[i];
PhAppendStringBuilderEx(&stringBuilder, temp, 4);
}
else
{
PhAppendCharStringBuilder(&stringBuilder, String->Buffer[i]);
}
}
return PhFinalStringBuilderString(&stringBuilder);
}
PPH_STRING PhUnescapeStringForDelimiter(
_In_ PPH_STRING String,
_In_ WCHAR Delimiter
)
{
PH_STRING_BUILDER stringBuilder;
SIZE_T length;
SIZE_T i;
length = String->Length / sizeof(WCHAR);
PhInitializeStringBuilder(&stringBuilder, String->Length / sizeof(WCHAR) * 3);
for (i = 0; i < length; i++)
{
if (String->Buffer[i] == L'\\')
{
if (i != length - 1)
{
PhAppendCharStringBuilder(&stringBuilder, String->Buffer[i + 1]);
i++;
}
else
{
// Trailing backslash. Just ignore it.
break;
}
}
else
{
PhAppendCharStringBuilder(&stringBuilder, String->Buffer[i]);
}
}
return PhFinalStringBuilderString(&stringBuilder);
}
VOID PhSearchOnlineString(
_In_ HWND WindowHandle,
_In_ PCWSTR String
)
{
PhShellExecuteUserString(WindowHandle, SETTING_SEARCH_ENGINE, String, TRUE, NULL);
}
VOID PhShellExecuteUserString(
_In_ HWND WindowHandle,
_In_ PCWSTR Setting,
_In_ PCWSTR String,
_In_ BOOLEAN UseShellExecute,
_In_opt_ PCWSTR ErrorMessage
)
{
static CONST PH_STRINGREF replacementToken = PH_STRINGREF_INIT(L"%s");
PPH_STRING applicationDirectory;
PPH_STRING executeString;
PH_STRINGREF stringBefore;
PH_STRINGREF stringAfter;
PPH_STRING ntMessage;
// Get the execute command. (dmex)
executeString = PhGetStringSetting(Setting);
if (PhEqualString2(executeString, L"%SystemRoot%\\explorer.exe /select,\"%s\"", TRUE))
{
// Special case: Use PhShowFileInExplorer for this specific setting. (dmex)
if (String[0] == OBJ_NAME_PATH_SEPARATOR)
{
PPH_STRING stringTemp;
PPH_STRING stringMiddle;
stringTemp = PhCreateString(String);
stringMiddle = PhGetFileName(stringTemp);
PhShellExploreFile(WindowHandle, PhGetString(stringMiddle));
PhDereferenceObject(stringMiddle);
PhDereferenceObject(stringTemp);
}
else
{
PhShellExploreFile(WindowHandle, String);
}
PhDereferenceObject(executeString);
return;
}
// Expand environment strings. (dmex)
PhMoveReference(&executeString, PhExpandEnvironmentStrings(&executeString->sr));
if (!(applicationDirectory = PhGetApplicationDirectoryWin32()))
{
PhShowStatus(WindowHandle, L"Unable to locate the application directory.", STATUS_NOT_FOUND, 0);
return;
}
// Make sure the user executable string is absolute. We can't use PhDetermineDosPathNameType
// here because the string may be a URL. (dmex)
if (PhFindCharInString(executeString, 0, L':') == SIZE_MAX)
{
static CONST PH_STRINGREF separator = PH_STRINGREF_INIT(L"\"");
static CONST PH_STRINGREF space = PH_STRINGREF_INIT(L" ");
PPH_LIST stringArgList;
PPH_STRING fileName = NULL;
PPH_STRING fileArgs = NULL;
// HACK: Escape the individual executeString components. (dmex)
if (stringArgList = PhCommandLineToList(executeString->Buffer))
{
fileName = PhReferenceObject(stringArgList->Items[0]);
if (stringArgList->Count == 2)
{
fileArgs = PhReferenceObject(stringArgList->Items[1]);
}
PhDereferenceObjects(stringArgList->Items, stringArgList->Count);
PhDereferenceObject(stringArgList);
}
if (fileName && fileArgs)
{
// Make sure the string is absolute and escape the filename.
if (PhDetermineDosPathNameType(&fileName->sr) == RtlPathTypeRelative)
{
PhMoveReference(&fileName, PhConcatStringRef3(&separator, &applicationDirectory->sr, &fileName->sr));
PhMoveReference(&fileName, PhConcatStringRef2(&fileName->sr, &separator));
}
else
{
PhMoveReference(&fileName, PhConcatStringRef3(&separator, &fileName->sr, &separator));
}
// Escape the parameters.
PhMoveReference(&fileArgs, PhConcatStringRef3(&separator, &fileArgs->sr, &separator));
// Create the escaped execute string.
PhMoveReference(&executeString, PhConcatStringRef3(&fileName->sr, &space, &fileArgs->sr));
}
else
{
if (PhDetermineDosPathNameType(&executeString->sr) == RtlPathTypeRelative)
{
PhMoveReference(&executeString, PhConcatStringRef3(&separator, &applicationDirectory->sr, &executeString->sr));
PhMoveReference(&executeString, PhConcatStringRef2(&executeString->sr, &separator));
}
else
{
PhMoveReference(&executeString, PhConcatStringRef3(&separator, &executeString->sr, &separator));
}
}
PhClearReference(&fileArgs);
PhClearReference(&fileName);
}
// Replace the token with the string, or use the original string if the token is not present.
if (PhSplitStringRefAtString(&executeString->sr, &replacementToken, FALSE, &stringBefore, &stringAfter))
{
// Note: See PhGetProcessImageFileNameWin32 for a description of
// the issue with some filenames and faulty RamDisk software (dmex)
if (String[0] == OBJ_NAME_PATH_SEPARATOR) // Workaround faulty software (dmex)
{
PPH_STRING stringTemp;
PPH_STRING stringMiddle;
stringTemp = PhCreateString(String);
stringMiddle = PhGetFileName(stringTemp);
PhMoveReference(&executeString, PhConcatStringRef3(&stringBefore, &stringMiddle->sr, &stringAfter));
PhDereferenceObject(stringMiddle);
PhDereferenceObject(stringTemp);
}
else
{
PH_STRINGREF stringMiddle;
PhInitializeStringRefLongHint(&stringMiddle, String);
PhMoveReference(&executeString, PhConcatStringRef3(&stringBefore, &stringMiddle, &stringAfter));
}
}
if (UseShellExecute)
{
PhShellExecute(WindowHandle, executeString->Buffer, NULL);
}
else
{
NTSTATUS status;
HANDLE processHandle;
status = PhCreateProcessWin32(
NULL,
executeString->Buffer,
NULL,
NULL,
0,
NULL,
&processHandle,
NULL
);
if (NT_SUCCESS(status))
{
PhConsoleSetForeground(processHandle, TRUE);
NtClose(processHandle);
}
else
{
if (ErrorMessage)
{
ntMessage = PhGetNtMessage(status);
PhShowError2(
WindowHandle,
L"Unable to execute the command.",
L"%s\n%s",
PhGetStringOrDefault(ntMessage, L"An unknown error occurred."),
ErrorMessage
);
PhDereferenceObject(ntMessage);
}
else
{
PhShowStatus(WindowHandle, L"Unable to execute the command.", status, 0);
}
}
}
PhDereferenceObject(executeString);
PhDereferenceObject(applicationDirectory);
}
VOID PhLoadSymbolProviderOptions(
_Inout_ PPH_SYMBOL_PROVIDER SymbolProvider
)
{
static CONST PH_STRINGREF symbolPath = PH_STRINGREF_INIT(L"_NT_SYMBOL_PATH");
PPH_STRING searchPath = NULL;
PhSetOptionsSymbolProvider(
PH_SYMOPT_UNDNAME | PH_SYMOPT_VERIFY_MICROSOFT_CHAIN,
(PhGetIntegerSetting(SETTING_DBGHELP_UNDECORATE) ? PH_SYMOPT_UNDNAME : 0) |
(PhGetIntegerSetting(SETTING_DBGHELP_VERIFY_MICROSOFT_CHAIN) ? PH_SYMOPT_VERIFY_MICROSOFT_CHAIN : 0)
);
PhQueryEnvironmentVariable(NULL, &symbolPath, &searchPath);
if (PhIsNullOrEmptyString(searchPath))
searchPath = PhGetStringSetting(SETTING_DBGHELP_SEARCH_PATH);
if (!PhIsNullOrEmptyString(searchPath))
PhSetSearchPathSymbolProvider(SymbolProvider, searchPath->Buffer);
if (searchPath)
PhDereferenceObject(searchPath);
}
/**
* Copies a string into a NMLVGETINFOTIP structure.
*
* \param GetInfoTip The NMLVGETINFOTIP structure.
* \param Tip The string to copy.
*
* \remarks The text is truncated if it is too long.
*/
VOID PhCopyListViewInfoTip(
_Inout_ LPNMLVGETINFOTIP GetInfoTip,
_In_ PPH_STRINGREF Tip
)
{
ULONG copyIndex;
ULONG bufferRemaining;
ULONG copyLength;
if (GetInfoTip->dwFlags == 0)
{
copyIndex = (ULONG)PhCountStringZ(GetInfoTip->pszText) + 1; // plus one for newline
if (GetInfoTip->cchTextMax - copyIndex < 2) // need at least two bytes
return;
bufferRemaining = GetInfoTip->cchTextMax - copyIndex - 1;
GetInfoTip->pszText[copyIndex - 1] = L'\n';
}
else
{
copyIndex = 0;
bufferRemaining = GetInfoTip->cchTextMax;
}
copyLength = min((ULONG)Tip->Length / sizeof(WCHAR), bufferRemaining - 1);
memcpy(
&GetInfoTip->pszText[copyIndex],
Tip->Buffer,
copyLength * sizeof(WCHAR)
);
GetInfoTip->pszText[copyIndex + copyLength] = UNICODE_NULL;
}
VOID PhCopyListView(
_In_ HWND ListViewHandle
)
{
PPH_STRING text;
text = PhGetListViewText(ListViewHandle);
PhSetClipboardString(ListViewHandle, &text->sr);
PhDereferenceObject(text);
}
VOID PhHandleListViewNotifyForCopy(
_In_ LPARAM lParam,
_In_ HWND ListViewHandle
)
{
PhHandleListViewNotifyBehaviors(lParam, ListViewHandle, PH_LIST_VIEW_CTRL_C_BEHAVIOR);
}
VOID PhHandleListViewNotifyBehaviors(
_In_ LPARAM lParam,
_In_ HWND ListViewHandle,
_In_ ULONG Behaviors
)
{
#pragma warning(push)
#pragma warning(disable:26454) // disable Windows SDK warning (dmex)
if (((LPNMHDR)lParam)->hwndFrom == ListViewHandle && ((LPNMHDR)lParam)->code == LVN_KEYDOWN)
{
LPNMLVKEYDOWN keyDown = (LPNMLVKEYDOWN)lParam;
switch (keyDown->wVKey)
{
case 'C':
if (Behaviors & PH_LIST_VIEW_CTRL_C_BEHAVIOR)
{
if (GetKeyState(VK_CONTROL) < 0)
PhCopyListView(ListViewHandle);
}
break;
case 'A':
if (Behaviors & PH_LIST_VIEW_CTRL_A_BEHAVIOR)
{
if (GetKeyState(VK_CONTROL) < 0)
PhSetStateAllListViewItems(ListViewHandle, LVIS_SELECTED, LVIS_SELECTED);
}
break;
}
}
#pragma warning(pop)
}
BOOLEAN PhGetListViewContextMenuPoint(
_In_ HWND ListViewHandle,
_Out_ PPOINT Point
)
{
LONG selectedIndex;
RECT bounds;
RECT clientRect;
// The user pressed a key to display the context menu.
// Suggest where the context menu should display.
if ((selectedIndex = PhFindListViewItemByFlags(ListViewHandle, INT_ERROR, LVNI_SELECTED)) != INT_ERROR)
{
if (ListView_GetItemRect(ListViewHandle, selectedIndex, &bounds, LVIR_BOUNDS))
{
LONG dpiValue = PhGetWindowDpi(ListViewHandle);
Point->x = bounds.left + PhGetSystemMetrics(SM_CXSMICON, dpiValue) / 2;
Point->y = bounds.top + PhGetSystemMetrics(SM_CYSMICON, dpiValue) / 2;
if (!PhGetClientRect(ListViewHandle, &clientRect))
return FALSE;
if (Point->x < 0 || Point->y < 0 || Point->x >= clientRect.right || Point->y >= clientRect.bottom)
{
// The menu is going to be outside of the control. Just put it at the top-left.
Point->x = 0;
Point->y = 0;
}
ClientToScreen(ListViewHandle, Point);
return TRUE;
}
}
Point->x = 0;
Point->y = 0;
ClientToScreen(ListViewHandle, Point);
return FALSE;
}
VOID PhSetWindowOpacity(
_In_ HWND WindowHandle,
_In_ ULONG OpacityPercent
)
{
if (OpacityPercent == 0)
{
// Make things a bit faster by removing the WS_EX_LAYERED bit.
PhSetWindowExStyle(WindowHandle, WS_EX_LAYERED, 0);
RedrawWindow(WindowHandle, NULL, NULL, RDW_ERASE | RDW_INVALIDATE | RDW_FRAME | RDW_ALLCHILDREN);
return;
}
PhSetWindowExStyle(WindowHandle, WS_EX_LAYERED, WS_EX_LAYERED);
// Disallow opacity values of less than 10%.
OpacityPercent = min(OpacityPercent, 90);
// The opacity value is backwards - 0 means opaque, 100 means transparent.
SetLayeredWindowAttributes(
WindowHandle,
0,
(BYTE)(255 * (100 - OpacityPercent) / 100),
LWA_ALPHA
);
}
PPH_STRING PhGetPhVersion(
VOID
)
{
PH_FORMAT format[7];
PhInitFormatU(&format[0], PHAPP_VERSION_MAJOR);
PhInitFormatC(&format[1], L'.');
PhInitFormatU(&format[2], PHAPP_VERSION_MINOR);
PhInitFormatC(&format[3], L'.');
PhInitFormatU(&format[4], PHAPP_VERSION_BUILD);
PhInitFormatC(&format[5], L'.');
PhInitFormatU(&format[6], PHAPP_VERSION_REVISION);
return PhFormat(format, RTL_NUMBER_OF(format), 0);
}
VOID PhGetPhVersionNumbers(
_Out_opt_ PULONG MajorVersion,
_Out_opt_ PULONG MinorVersion,
_Out_opt_ PULONG BuildNumber,
_Out_opt_ PULONG RevisionNumber
)
{
if (MajorVersion)
*MajorVersion = PHAPP_VERSION_MAJOR;
if (MinorVersion)
*MinorVersion = PHAPP_VERSION_MINOR;
if (BuildNumber)
*BuildNumber = PHAPP_VERSION_BUILD;
if (RevisionNumber)
*RevisionNumber = PHAPP_VERSION_REVISION;
}
PPH_STRING PhGetPhVersionHash(
VOID
)
{
return PhConvertUtf8ToUtf16(PHAPP_VERSION_COMMIT);
}
PH_RELEASE_CHANNEL PhGetPhReleaseChannel(
VOID
)
{
return PhGetIntegerSetting(SETTING_RELEASE_CHANNEL);
}
PCWSTR PhGetPhReleaseChannelString(
VOID
)
{
switch (PhGetIntegerSetting(SETTING_RELEASE_CHANNEL))
{
case PhReleaseChannel:
return L"Release";
case PhPreviewChannel:
return L"Preview";
case PhCanaryChannel:
return L"Canary";
case PhDeveloperChannel:
return L"Developer";
}
return L"Unknown";
}
VOID PhWritePhTextHeader(
_Inout_ PPH_FILE_STREAM FileStream
)
{
PPH_STRING version;
LARGE_INTEGER time;
SYSTEMTIME systemTime;
PPH_STRING timeString;
PhWriteStringAsUtf8FileStream2(FileStream, L"System Informer ");
if (version = PhGetPhVersion())
{
PhWriteStringAsUtf8FileStream(FileStream, &version->sr);
PhDereferenceObject(version);
}
PhWriteStringFormatAsUtf8FileStream(FileStream, L"\r\nWindows NT %lu.%lu", PhOsVersion.MajorVersion, PhOsVersion.MinorVersion);
if (PhOsVersion.CSDVersion[0] != UNICODE_NULL)
PhWriteStringFormatAsUtf8FileStream(FileStream, L" %s", PhOsVersion.CSDVersion);
#ifdef _WIN64
PhWriteStringAsUtf8FileStream2(FileStream, L" (64-bit)");
#else
PhWriteStringAsUtf8FileStream2(FileStream, L" (32-bit)");
#endif
PhQuerySystemTime(&time);
PhLargeIntegerToLocalSystemTime(&systemTime, &time);
timeString = PhFormatDateTime(&systemTime);
PhWriteStringFormatAsUtf8FileStream(FileStream, L"\r\n%s\r\n\r\n", timeString->Buffer);
PhDereferenceObject(timeString);
}
NTSTATUS PhShellProcessHacker(
_In_opt_ HWND WindowHandle,
_In_opt_ PCWSTR Parameters,
_In_ ULONG ShowWindowType,
_In_ ULONG Flags,
_In_ ULONG AppFlags,
_In_opt_ ULONG Timeout,
_Out_opt_ PHANDLE ProcessHandle
)
{
return PhShellProcessHackerEx(
WindowHandle,
NULL,
Parameters,
ShowWindowType,
Flags,
AppFlags,
Timeout,
ProcessHandle
);
}
VOID PhpAppendCommandLineArgument(
_Inout_ PPH_STRING_BUILDER StringBuilder,
_In_ PCWSTR Name,
_In_ PPH_STRINGREF Value
)
{
PPH_STRING temp;
PhAppendStringBuilder2(StringBuilder, L" -");
PhAppendStringBuilder2(StringBuilder, Name);
PhAppendStringBuilder2(StringBuilder, L" \"");
temp = PhEscapeCommandLinePart(Value);
PhAppendStringBuilder(StringBuilder, &temp->sr);
PhDereferenceObject(temp);
PhAppendCharStringBuilder(StringBuilder, L'\"');
}
NTSTATUS PhShellProcessHackerEx(
_In_opt_ HWND WindowHandle,
_In_opt_ PCWSTR FileName,
_In_opt_ PCWSTR Parameters,
_In_ ULONG ShowWindowType,
_In_ ULONG Flags,
_In_ ULONG AppFlags,
_In_opt_ ULONG Timeout,
_Out_opt_ PHANDLE ProcessHandle
)
{
NTSTATUS status;
PPH_STRING applicationFileName;
PH_STRING_BUILDER sb;
PCWSTR parameters;
if (!(applicationFileName = PhGetApplicationFileNameWin32()))
return FALSE;
if (AppFlags & PH_SHELL_APP_PROPAGATE_PARAMETERS)
{
PhInitializeStringBuilder(&sb, 128);
// Propagate parameters.
if (PhStartupParameters.NoSettings)
{
PhAppendStringBuilder2(&sb, L" -nosettings");
}
else
{
if (!PhIsNullOrEmptyString(PhSettingsFileName))
{
PhAppendStringBuilder2(&sb, L" -settings \"");
PhAppendStringBuilder(&sb, &PhSettingsFileName->sr);
PhAppendStringBuilder2(&sb, L"\"");
}
}
if (PhStartupParameters.NoKph)
PhAppendStringBuilder2(&sb, L" -nokph");
if (PhStartupParameters.Debug)
PhAppendStringBuilder2(&sb, L" -debug");
if (PhStartupParameters.NoPlugins)
PhAppendStringBuilder2(&sb, L" -noplugins");
if (PhStartupParameters.NewInstance)
PhAppendStringBuilder2(&sb, L" -newinstance");
if (PhStartupParameters.SelectPid != 0)
PhAppendFormatStringBuilder(&sb, L" -selectpid %lu", PhStartupParameters.SelectPid);
if (PhStartupParameters.PriorityClass != 0)
{
CHAR value = 0;
switch (PhStartupParameters.PriorityClass)
{
case PROCESS_PRIORITY_CLASS_REALTIME:
value = L'r';
break;
case PROCESS_PRIORITY_CLASS_HIGH:
value = L'h';
break;
case PROCESS_PRIORITY_CLASS_NORMAL:
value = L'n';
break;
case PROCESS_PRIORITY_CLASS_IDLE:
value = L'l';
break;
}
if (value != 0)
{
PhAppendStringBuilder2(&sb, L" -priority ");
PhAppendCharStringBuilder(&sb, value);
}
}
if (PhStartupParameters.PluginParameters)
{
ULONG i;
for (i = 0; i < PhStartupParameters.PluginParameters->Count; i++)
{
PPH_STRING value = PhStartupParameters.PluginParameters->Items[i];
PhpAppendCommandLineArgument(&sb, L"plugin", &value->sr);
}
}
if (PhStartupParameters.SelectTab)
PhpAppendCommandLineArgument(&sb, L"selecttab", &PhStartupParameters.SelectTab->sr);
if (!(AppFlags & PH_SHELL_APP_PROPAGATE_PARAMETERS_IGNORE_VISIBILITY))
{
if (PhStartupParameters.ShowVisible)
PhAppendStringBuilder2(&sb, L" -v");
if (PhStartupParameters.ShowHidden)
PhAppendStringBuilder2(&sb, L" -hide");
}
// Add user-specified parameters last so they can override the propagated parameters.
if (Parameters)
{
PhAppendCharStringBuilder(&sb, L' ');
PhAppendStringBuilder2(&sb, Parameters);
}
if (sb.String->Length != 0 && sb.String->Buffer[0] == L' ')
parameters = sb.String->Buffer + 1;
else
parameters = sb.String->Buffer;
}
else
{
parameters = Parameters;
}
status = PhShellExecuteEx(
WindowHandle,
FileName ? FileName : PhGetString(applicationFileName),
parameters,
NULL,
ShowWindowType,
Flags,
Timeout,
ProcessHandle
);
if (AppFlags & PH_SHELL_APP_PROPAGATE_PARAMETERS)
PhDeleteStringBuilder(&sb);
PhDereferenceObject(applicationFileName);
return status;
}
BOOLEAN PhCreateProcessIgnoreIfeoDebugger(
_In_ PCWSTR FileName,
_In_opt_ PCWSTR CommandLine
)
{
BOOLEAN result;
BOOLEAN originalValue;
HANDLE processHandle;
result = FALSE;
RtlAcquirePebLock();
originalValue = NtCurrentPeb()->ReadImageFileExecOptions;
NtCurrentPeb()->ReadImageFileExecOptions = FALSE;
RtlReleasePebLock();
// The combination of ReadImageFileExecOptions = FALSE and the DEBUG_PROCESS flag
// allows us to skip the Debugger IFEO value. (wj32)
if (NT_SUCCESS(PhCreateProcessWin32(
FileName,
CommandLine,
NULL,
NULL,
PH_CREATE_PROCESS_DEBUG | PH_CREATE_PROCESS_DEBUG_ONLY_THIS_PROCESS,
NULL,
&processHandle,
NULL
)))
{
HANDLE debugObjectHandle;
if (NT_SUCCESS(PhGetProcessDebugObject(
processHandle,
&debugObjectHandle
)))
{
// Disable kill-on-close.
if (NT_SUCCESS(PhSetDebugKillProcessOnExit(
debugObjectHandle,
FALSE
)))
{
// Stop debugging the process now.
NtRemoveProcessDebug(processHandle, debugObjectHandle);
}
NtClose(debugObjectHandle);
}
// Ignore the debug object status.
result = TRUE;
PhConsoleSetForeground(processHandle, TRUE);
NtClose(processHandle);
}
if (originalValue)
{
RtlAcquirePebLock();
NtCurrentPeb()->ReadImageFileExecOptions = originalValue;
RtlReleasePebLock();
}
return result;
}
VOID PhInitializeTreeNewColumnMenu(
_Inout_ PPH_TN_COLUMN_MENU_DATA Data
)
{
PhInitializeTreeNewColumnMenuEx(Data, 0);
}
VOID PhInitializeTreeNewColumnMenuEx(
_Inout_ PPH_TN_COLUMN_MENU_DATA Data,
_In_ ULONG Flags
)
{
PPH_EMENU_ITEM resetSortMenuItem = NULL;
PPH_EMENU_ITEM sizeColumnToFitMenuItem;
PPH_EMENU_ITEM sizeAllColumnsToFitMenuItem;
PPH_EMENU_ITEM hideColumnMenuItem = NULL;
PPH_EMENU_ITEM chooseColumnsMenuItem = NULL;
ULONG minimumNumberOfColumns;
Data->Menu = PhCreateEMenu();
Data->Selection = NULL;
Data->ProcessedId = 0;
sizeColumnToFitMenuItem = PhCreateEMenuItem(0, PH_TN_COLUMN_MENU_SIZE_COLUMN_TO_FIT_ID, L"Size column to fit", NULL, NULL);
sizeAllColumnsToFitMenuItem = PhCreateEMenuItem(0, PH_TN_COLUMN_MENU_SIZE_ALL_COLUMNS_TO_FIT_ID, L"Size all columns to fit", NULL, NULL);
if (!(Flags & PH_TN_COLUMN_MENU_NO_VISIBILITY))
{
hideColumnMenuItem = PhCreateEMenuItem(0, PH_TN_COLUMN_MENU_HIDE_COLUMN_ID, L"Hide column", NULL, NULL);
chooseColumnsMenuItem = PhCreateEMenuItem(0, PH_TN_COLUMN_MENU_CHOOSE_COLUMNS_ID, L"Choose columns...", NULL, NULL);
}
if (Flags & PH_TN_COLUMN_MENU_SHOW_RESET_SORT)
{
ULONG sortColumn = 0;
PH_SORT_ORDER sortOrder = NoSortOrder;
TreeNew_GetSort(Data->TreeNewHandle, &sortColumn, &sortOrder);
if (sortOrder != Data->DefaultSortOrder || (Data->DefaultSortOrder != NoSortOrder && sortColumn != Data->DefaultSortColumn))
resetSortMenuItem = PhCreateEMenuItem(0, PH_TN_COLUMN_MENU_RESET_SORT_ID, L"Reset sort", NULL, NULL);
}
PhInsertEMenuItem(Data->Menu, sizeColumnToFitMenuItem, ULONG_MAX);
PhInsertEMenuItem(Data->Menu, sizeAllColumnsToFitMenuItem, ULONG_MAX);
if (!(Flags & PH_TN_COLUMN_MENU_NO_VISIBILITY))
{
if (hideColumnMenuItem) PhInsertEMenuItem(Data->Menu, hideColumnMenuItem, ULONG_MAX);
if (resetSortMenuItem) PhInsertEMenuItem(Data->Menu, resetSortMenuItem, ULONG_MAX);
PhInsertEMenuItem(Data->Menu, PhCreateEMenuSeparator(), ULONG_MAX);
if (chooseColumnsMenuItem) PhInsertEMenuItem(Data->Menu, chooseColumnsMenuItem, ULONG_MAX);
if (TreeNew_GetFixedColumn(Data->TreeNewHandle))
minimumNumberOfColumns = 2; // don't allow user to remove all normal columns (the fixed column can never be removed)
else
minimumNumberOfColumns = 1;
if (!Data->MouseEvent || !Data->MouseEvent->Column ||
Data->MouseEvent->Column->Fixed || // don't allow the fixed column to be hidden
TreeNew_GetVisibleColumnCount(Data->TreeNewHandle) < minimumNumberOfColumns + 1
)
{
if (hideColumnMenuItem)
PhSetDisabledEMenuItem(hideColumnMenuItem);
}
}
else
{
if (resetSortMenuItem)
PhInsertEMenuItem(Data->Menu, resetSortMenuItem, ULONG_MAX);
}
if (!Data->MouseEvent || !Data->MouseEvent->Column)
{
PhSetDisabledEMenuItem(sizeColumnToFitMenuItem);
}
}
VOID PhpEnsureValidSortColumnTreeNew(
_Inout_ HWND TreeNewHandle,
_In_ ULONG DefaultSortColumn,
_In_ PH_SORT_ORDER DefaultSortOrder
)
{
ULONG sortColumn = 0;
PH_SORT_ORDER sortOrder = NoSortOrder;
// Make sure the column we're sorting by is actually visible, and if not, don't sort anymore.
TreeNew_GetSort(TreeNewHandle, &sortColumn, &sortOrder);
if (sortOrder != NoSortOrder)
{
PH_TREENEW_COLUMN column;
TreeNew_GetColumn(TreeNewHandle, sortColumn, &column);
if (!column.Visible)
{
if (DefaultSortOrder != NoSortOrder)
{
// Make sure the default sort column is visible.
TreeNew_GetColumn(TreeNewHandle, DefaultSortColumn, &column);
if (!column.Visible)
{
ULONG maxId;
ULONG id;
BOOLEAN found;
// Use the first visible column.
maxId = TreeNew_GetMaxId(TreeNewHandle);
id = 0;
found = FALSE;
while (id <= maxId)
{
if (TreeNew_GetColumn(TreeNewHandle, id, &column))
{
if (column.Visible)
{
DefaultSortColumn = id;
found = TRUE;
break;
}
}
id++;
}
if (!found)
{
DefaultSortColumn = 0;
DefaultSortOrder = NoSortOrder;
}
}
}
TreeNew_SetSort(TreeNewHandle, DefaultSortColumn, DefaultSortOrder);
}
}
}
BOOLEAN PhHandleTreeNewColumnMenu(
_Inout_ PPH_TN_COLUMN_MENU_DATA Data
)
{
if (!Data->Selection)
return FALSE;
switch (Data->Selection->Id)
{
case PH_TN_COLUMN_MENU_RESET_SORT_ID:
{
TreeNew_SetSort(Data->TreeNewHandle, Data->DefaultSortColumn, Data->DefaultSortOrder);
}
break;
case PH_TN_COLUMN_MENU_SIZE_COLUMN_TO_FIT_ID:
{
if (Data->MouseEvent && Data->MouseEvent->Column)
{
TreeNew_AutoSizeColumn(Data->TreeNewHandle, Data->MouseEvent->Column->Id, 0);
}
}
break;
case PH_TN_COLUMN_MENU_SIZE_ALL_COLUMNS_TO_FIT_ID:
{
ULONG maxId;
ULONG id;
maxId = TreeNew_GetMaxId(Data->TreeNewHandle);
id = 0;
while (id <= maxId)
{
TreeNew_AutoSizeColumn(Data->TreeNewHandle, id, 0);
id++;
}
}
break;
case PH_TN_COLUMN_MENU_HIDE_COLUMN_ID:
{
PH_TREENEW_COLUMN column;
if (Data->MouseEvent && Data->MouseEvent->Column && !Data->MouseEvent->Column->Fixed)
{
column.Id = Data->MouseEvent->Column->Id;
column.Visible = FALSE;
TreeNew_SetColumn(Data->TreeNewHandle, TN_COLUMN_FLAG_VISIBLE, &column);
PhpEnsureValidSortColumnTreeNew(Data->TreeNewHandle, Data->DefaultSortColumn, Data->DefaultSortOrder);
InvalidateRect(Data->TreeNewHandle, NULL, FALSE);
}
}
break;
case PH_TN_COLUMN_MENU_CHOOSE_COLUMNS_ID:
{
PhShowChooseColumnsDialog(Data->TreeNewHandle, Data->TreeNewHandle, PH_CONTROL_TYPE_TREE_NEW);
PhpEnsureValidSortColumnTreeNew(Data->TreeNewHandle, Data->DefaultSortColumn, Data->DefaultSortOrder);
}
break;
default:
return FALSE;
}
Data->ProcessedId = Data->Selection->Id;
return TRUE;
}
VOID PhDeleteTreeNewColumnMenu(
_In_ PPH_TN_COLUMN_MENU_DATA Data
)
{
if (Data->Menu)
{
PhDestroyEMenu(Data->Menu);
Data->Menu = NULL;
}
}
VOID PhInitializeTreeNewFilterSupport(
_Out_ PPH_TN_FILTER_SUPPORT Support,
_In_ HWND TreeNewHandle,
_In_ PPH_LIST NodeList
)
{
Support->FilterList = NULL;
Support->TreeNewHandle = TreeNewHandle;
Support->NodeList = NodeList;
}
VOID PhDeleteTreeNewFilterSupport(
_In_ PPH_TN_FILTER_SUPPORT Support
)
{
if (!Support->FilterList)
return;
PhDereferenceObject(Support->FilterList);
Support->FilterList = NULL;
}
PPH_TN_FILTER_ENTRY PhAddTreeNewFilter(
_In_ PPH_TN_FILTER_SUPPORT Support,
_In_ PPH_TN_FILTER_FUNCTION Filter,
_In_opt_ PVOID Context
)
{
PPH_TN_FILTER_ENTRY entry;
entry = PhAllocate(sizeof(PH_TN_FILTER_ENTRY));
entry->Filter = Filter;
entry->Context = Context;
if (!Support->FilterList)
Support->FilterList = PhCreateList(2);
PhAddItemList(Support->FilterList, entry);
return entry;
}
VOID PhRemoveTreeNewFilter(
_In_ PPH_TN_FILTER_SUPPORT Support,
_In_ PPH_TN_FILTER_ENTRY Entry
)
{
ULONG index;
if (!Support->FilterList)
return;
index = PhFindItemList(Support->FilterList, Entry);
if (index != ULONG_MAX)
{
PhRemoveItemList(Support->FilterList, index);
PhFree(Entry);
}
}
BOOLEAN PhApplyTreeNewFiltersToNode(
_In_ PPH_TN_FILTER_SUPPORT Support,
_In_ PPH_TREENEW_NODE Node
)
{
BOOLEAN show;
ULONG i;
show = TRUE;
if (Support->FilterList)
{
for (i = 0; i < Support->FilterList->Count; i++)
{
PPH_TN_FILTER_ENTRY entry;
entry = Support->FilterList->Items[i];
if (!entry->Filter(Node, entry->Context))
{
show = FALSE;
break;
}
}
}
return show;
}
VOID PhApplyTreeNewFilters(
_In_ PPH_TN_FILTER_SUPPORT Support
)
{
ULONG i;
for (i = 0; i < Support->NodeList->Count; i++)
{
PPH_TREENEW_NODE node;
node = Support->NodeList->Items[i];
node->Visible = PhApplyTreeNewFiltersToNode(Support, node);
if (!node->Visible && node->Selected)
{
node->Selected = FALSE;
}
}
if (Support->NodeList->Count)
{
TreeNew_NodesStructured(Support->TreeNewHandle);
}
}
_Function_class_(PH_EMENU_ITEM_DELETE_FUNCTION)
VOID NTAPI PhpCopyCellEMenuItemDeleteFunction(
_In_ PPH_EMENU_ITEM Item
)
{
PPH_COPY_CELL_CONTEXT context;
context = Item->Context;
PhDereferenceObject(context->MenuItemText);
PhFree(context);
}
BOOLEAN PhInsertCopyCellEMenuItem(
_In_ PPH_EMENU_ITEM Menu,
_In_ ULONG InsertAfterId,
_In_ HWND TreeNewHandle,
_In_ PPH_TREENEW_COLUMN Column
)
{
PPH_EMENU_ITEM parentItem = NULL;
ULONG indexInParent = 0;
PPH_COPY_CELL_CONTEXT context;
PH_STRINGREF columnText;
PPH_STRING escapedText;
PPH_STRING menuItemText;
PPH_EMENU_ITEM copyCellItem;
PH_FORMAT format[3];
if (!Column)
return FALSE;
if (!PhFindEMenuItemEx(Menu, 0, NULL, InsertAfterId, &parentItem, &indexInParent))
return FALSE;
indexInParent++;
PhInitializeStringRefLongHint(&columnText, Column->Text);
escapedText = PhEscapeStringForMenuPrefix(&columnText);
PhInitFormatS(&format[0], L"Copy \""); // Copy \"%s\"
PhInitFormatSR(&format[1], escapedText->sr);
PhInitFormatS(&format[2], L"\"");
menuItemText = PhFormat(format, RTL_NUMBER_OF(format), 0);
PhDereferenceObject(escapedText);
context = PhAllocate(sizeof(PH_COPY_CELL_CONTEXT));
context->TreeNewHandle = TreeNewHandle;
context->Id = Column->Id;
context->MenuItemText = menuItemText;
copyCellItem = PhCreateEMenuItem(0, ID_COPY_CELL, menuItemText->Buffer, NULL, context);
copyCellItem->DeleteFunction = PhpCopyCellEMenuItemDeleteFunction;
if (Column->CustomDraw)
copyCellItem->Flags |= PH_EMENU_DISABLED;
PhInsertEMenuItem(parentItem, copyCellItem, indexInParent);
return TRUE;
}
BOOLEAN PhHandleCopyCellEMenuItem(
_In_ PPH_EMENU_ITEM SelectedItem
)
{
PPH_COPY_CELL_CONTEXT context;
PH_STRING_BUILDER stringBuilder;
ULONG count;
ULONG selectedCount;
ULONG i;
PPH_TREENEW_NODE node;
PH_TREENEW_GET_CELL_TEXT getCellText;
if (!SelectedItem)
return FALSE;
if (SelectedItem->Id != ID_COPY_CELL)
return FALSE;
context = SelectedItem->Context;
PhInitializeStringBuilder(&stringBuilder, 0x100);
count = TreeNew_GetFlatNodeCount(context->TreeNewHandle);
selectedCount = 0;
for (i = 0; i < count; i++)
{
node = TreeNew_GetFlatNode(context->TreeNewHandle, i);
if (node && node->Selected)
{
selectedCount++;
getCellText.Flags = 0;
getCellText.Node = node;
getCellText.Id = context->Id;
PhInitializeEmptyStringRef(&getCellText.Text);
TreeNew_GetCellText(context->TreeNewHandle, &getCellText);
PhAppendStringBuilder(&stringBuilder, &getCellText.Text);
PhAppendStringBuilder2(&stringBuilder, L"\r\n");
}
}
if (stringBuilder.String->Length != 0 && selectedCount == 1)
PhRemoveEndStringBuilder(&stringBuilder, 2);
PhSetClipboardString(context->TreeNewHandle, &stringBuilder.String->sr);
PhDeleteStringBuilder(&stringBuilder);
return TRUE;
}
_Function_class_(PH_EMENU_ITEM_DELETE_FUNCTION)
VOID NTAPI PhpCopyListViewEMenuItemDeleteFunction(
_In_ PPH_EMENU_ITEM Item
)
{
PPH_COPY_ITEM_CONTEXT context;
context = Item->Context;
PhDereferenceObject(context->MenuItemText);
PhFree(context);
}
BOOLEAN PhInsertCopyListViewEMenuItem(
_In_ PPH_EMENU_ITEM Menu,
_In_ ULONG InsertAfterId,
_In_ HWND ListViewHandle
)
{
PPH_EMENU_ITEM parentItem = NULL;
ULONG indexInParent = 0;
PPH_COPY_ITEM_CONTEXT context;
PH_STRINGREF columnText;
PPH_STRING escapedText;
PPH_STRING menuItemText;
PPH_EMENU_ITEM copyMenuItem;
POINT location;
LVHITTESTINFO lvHitInfo;
HDITEM headerItem;
HWND headerHandle;
PH_FORMAT format[3];
WCHAR headerText[MAX_PATH] = L"";
if (!PhGetClientPos(ListViewHandle, &location))
return FALSE;
memset(&lvHitInfo, 0, sizeof(LVHITTESTINFO));
lvHitInfo.pt = location;
if (ListView_SubItemHitTest(ListViewHandle, &lvHitInfo) == INT_ERROR)
return FALSE;
memset(&headerItem, 0, sizeof(HDITEM));
headerItem.mask = HDI_TEXT;
headerItem.cchTextMax = RTL_NUMBER_OF(headerText);
headerItem.pszText = headerText;
headerHandle = ListView_GetHeader(ListViewHandle);
if (!Header_GetItem(headerHandle, lvHitInfo.iSubItem, &headerItem))
return FALSE;
PhInitializeStringRefLongHint(&columnText, headerText);
if (PhIsNullOrEmptyStringRef(&columnText))
return FALSE;
if (!PhFindEMenuItemEx(Menu, 0, NULL, InsertAfterId, &parentItem, &indexInParent))
return FALSE;
indexInParent++;
escapedText = PhEscapeStringForMenuPrefix(&columnText);
PhInitFormatS(&format[0], L"Copy \""); // Copy \"%s\"
PhInitFormatSR(&format[1], escapedText->sr);
PhInitFormatS(&format[2], L"\"");
menuItemText = PhFormat(format, RTL_NUMBER_OF(format), 0);
PhDereferenceObject(escapedText);
context = PhAllocate(sizeof(PH_COPY_ITEM_CONTEXT));
context->ListViewHandle = ListViewHandle;
context->Id = lvHitInfo.iItem;
context->SubId = lvHitInfo.iSubItem;
context->MenuItemText = menuItemText;
copyMenuItem = PhCreateEMenuItem(0, ID_COPY_CELL, menuItemText->Buffer, NULL, context);
copyMenuItem->DeleteFunction = PhpCopyListViewEMenuItemDeleteFunction;
PhInsertEMenuItem(parentItem, copyMenuItem, indexInParent);
return TRUE;
}
BOOLEAN PhHandleCopyListViewEMenuItem(
_In_ PPH_EMENU_ITEM SelectedItem
)
{
PPH_COPY_ITEM_CONTEXT context;
PH_STRING_BUILDER stringBuilder;
ULONG state = 0;
ULONG count = 0;
ULONG selectedCount;
ULONG i;
PPH_STRING getItemText;
if (!SelectedItem)
return FALSE;
if (SelectedItem->Id != ID_COPY_CELL)
return FALSE;
context = SelectedItem->Context;
PhInitializeStringBuilder(&stringBuilder, 0x100);
count = ListView_GetItemCount(context->ListViewHandle);
selectedCount = 0;
for (i = 0; i < count; i++)
{
state = ListView_GetItemState(context->ListViewHandle, i, LVIS_SELECTED);
if (!FlagOn(state, LVIS_SELECTED))
continue;
if (getItemText = PhaGetListViewItemText(context->ListViewHandle, i, context->SubId))
PhAppendStringBuilder(&stringBuilder, &getItemText->sr);
PhAppendStringBuilder2(&stringBuilder, L"\r\n");
selectedCount++;
}
if (stringBuilder.String->Length != 0 && selectedCount == 1)
PhRemoveEndStringBuilder(&stringBuilder, 2);
PhSetClipboardString(context->ListViewHandle, &stringBuilder.String->sr);
PhDeleteStringBuilder(&stringBuilder);
return TRUE;
}
BOOLEAN PhpSelectFavoriteInRegedit(
_In_ HWND RegeditWindow,
_In_ PPH_STRINGREF FavoriteName,
_In_ BOOLEAN UsePhSvc
)
{
HMENU menu;
HMENU favoritesMenu;
ULONG count;
ULONG i;
ULONG id = ULONG_MAX;
if (!(menu = GetMenu(RegeditWindow)))
return FALSE;
// Cause the Registry Editor to refresh the Favorites menu.
if (UsePhSvc)
PhSvcCallSendMessage(RegeditWindow, WM_MENUSELECT, MAKEWPARAM(3, MF_POPUP), (LPARAM)menu);
else
PhSendMessageTimeout(RegeditWindow, WM_MENUSELECT, MAKEWPARAM(3, MF_POPUP), (LPARAM)menu, 5000, NULL);
if (!(favoritesMenu = GetSubMenu(menu, 3)))
return FALSE;
// Find our entry.
count = GetMenuItemCount(favoritesMenu);
if (count == ULONG_MAX)
return FALSE;
if (count > 1000)
count = 1000;
for (i = 0; i < count; i++)
{
MENUITEMINFO info;
WCHAR buffer[MAX_PATH];
memset(&info, 0, sizeof(MENUITEMINFO));
info.cbSize = sizeof(MENUITEMINFO);
info.fMask = MIIM_ID | MIIM_STRING;
info.dwTypeData = buffer;
info.cch = RTL_NUMBER_OF(buffer);
if (!GetMenuItemInfo(favoritesMenu, i, TRUE, &info))
continue;
if (info.cch == FavoriteName->Length / sizeof(WCHAR))
{
PH_STRINGREF text;
text.Buffer = buffer;
text.Length = info.cch * sizeof(WCHAR);
if (PhEqualStringRef(&text, FavoriteName, TRUE))
{
id = info.wID;
break;
}
}
}
if (id == ULONG_MAX)
return FALSE;
// Activate our entry.
if (UsePhSvc)
PhSvcCallSendMessage(RegeditWindow, WM_COMMAND, MAKEWPARAM(id, 0), 0);
else
PhSendMessageTimeout(RegeditWindow, WM_COMMAND, MAKEWPARAM(id, 0), 0, 5000, NULL);
// "Close" the Favorites menu and restore normal status bar text.
if (UsePhSvc)
PhSvcCallPostMessage(RegeditWindow, WM_MENUSELECT, MAKEWPARAM(0, 0xffff), 0);
else
PostMessage(RegeditWindow, WM_MENUSELECT, MAKEWPARAM(0, 0xffff), 0);
// Bring regedit to the top.
if (IsMinimized(RegeditWindow))
{
ShowWindow(RegeditWindow, SW_RESTORE);
}
SetForegroundWindow(RegeditWindow);
return TRUE;
}
/**
* Opens a key in the Registry Editor.
*
* \param WindowHandle A handle to the parent window.
* \param KeyName The key name to open.
*/
VOID PhShellOpenKey(
_In_ HWND WindowHandle,
_In_ PPH_STRING KeyName
)
{
static CONST PH_STRINGREF regeditKeyName = PH_STRINGREF_INIT(L"Software\\Microsoft\\Windows\\CurrentVersion\\Applets\\Regedit");
NTSTATUS status;
HANDLE regeditKeyHandle;
PPH_STRING lastKey;
PPH_STRING regeditFileName;
PH_STRINGREF systemRootString;
status = PhCreateKey(
®editKeyHandle,
KEY_WRITE,
PH_KEY_CURRENT_USER,
®editKeyName,
0,
0,
NULL
);
if (!NT_SUCCESS(status))
{
PhShowStatus(WindowHandle, L"Unable to execute the program.", status, 0);
return;
}
lastKey = PhExpandKeyName(KeyName, FALSE);
PhSetValueKeyZ(regeditKeyHandle, L"LastKey", REG_SZ, lastKey->Buffer, (ULONG)lastKey->Length + sizeof(UNICODE_NULL));
NtClose(regeditKeyHandle);
PhDereferenceObject(lastKey);
// Start regedit. If we aren't elevated, request that regedit be elevated. This is so we can get
// the consent dialog in the center of the specified window. (wj32)
PhGetSystemRoot(&systemRootString);
regeditFileName = PhConcatStringRefZ(&systemRootString, L"\\regedit.exe");
if (PhGetOwnTokenAttributes().Elevated)
{
status = PhShellExecuteEx(
WindowHandle,
regeditFileName->Buffer,
NULL,
NULL,
SW_SHOW,
0,
0,
NULL
);
if (!NT_SUCCESS(status))
{
PhShowStatus(WindowHandle, L"Unable to execute the program.", status, 0);
}
}
else
{
status = PhShellExecuteEx(
WindowHandle,
regeditFileName->Buffer,
NULL,
NULL,
SW_SHOW,
PH_SHELL_EXECUTE_ADMIN,
0,
NULL
);
if (!NT_SUCCESS(status))
{
PhShowStatus(WindowHandle, L"Unable to execute the program.", status, 0);
}
}
PhDereferenceObject(regeditFileName);
}
NTSTATUS PhRegeditOpenUserFavoritesKey(
_In_ HWND RegeditWindow,
_Out_ PHANDLE KeyHandle
)
{
static CONST PH_STRINGREF favoritesKeyName = PH_STRINGREF_INIT(L"Software\\Microsoft\\Windows\\CurrentVersion\\Applets\\Regedit\\Favorites");
NTSTATUS status;
CLIENT_ID clientId;
HANDLE processHandle;
HANDLE tokenHandle;
HANDLE keyUserHandle;
HANDLE keyFavoritesHandle;
PH_TOKEN_USER tokenUser;
PPH_STRING tokenUserSid;
status = PhGetWindowClientId(RegeditWindow, &clientId);
if (!NT_SUCCESS(status))
goto CleanupExit;
status = PhOpenProcessClientId(
&processHandle,
PROCESS_QUERY_LIMITED_INFORMATION,
&clientId
);
if (!NT_SUCCESS(status))
goto CleanupExit;
status = PhOpenProcessToken(
processHandle,
TOKEN_QUERY,
&tokenHandle
);
NtClose(processHandle);
if (!NT_SUCCESS(status))
goto CleanupExit;
status = PhGetTokenUser(
tokenHandle,
&tokenUser
);
NtClose(tokenHandle);
if (!NT_SUCCESS(status))
goto CleanupExit;
if (!(tokenUserSid = PhSidToStringSid(tokenUser.User.Sid)))
{
status = STATUS_NO_MEMORY;
goto CleanupExit;
}
status = PhOpenKey(
&keyUserHandle,
KEY_READ,
PH_KEY_USERS,
&tokenUserSid->sr,
0
);
PhDereferenceObject(tokenUserSid);
if (!NT_SUCCESS(status))
goto CleanupExit;
status = PhCreateKey(
&keyFavoritesHandle,
KEY_WRITE,
keyUserHandle,
&favoritesKeyName,
0,
0,
NULL
);
NtClose(keyUserHandle);
if (NT_SUCCESS(status))
{
*KeyHandle = keyFavoritesHandle;
return STATUS_SUCCESS;
}
CleanupExit:
status = PhCreateKey(
&keyFavoritesHandle,
KEY_WRITE,
PH_KEY_CURRENT_USER,
&favoritesKeyName,
0,
0,
NULL
);
if (NT_SUCCESS(status))
{
*KeyHandle = keyFavoritesHandle;
return STATUS_SUCCESS;
}
return status;
}
/**
* Opens a key in the Registry Editor. If the Registry Editor is already open,
* the specified key is selected in the Registry Editor.
*
* \param WindowHandle A handle to the parent window.
* \param KeyName The key name to open.
*/
BOOLEAN PhShellOpenKey2(
_In_ HWND WindowHandle,
_In_ PPH_STRING KeyName
)
{
BOOLEAN result = FALSE;
HWND regeditWindow;
HANDLE favoritesKeyHandle;
WCHAR favoriteName[33];
PH_STRINGREF valueName;
PPH_STRING expandedKeyName;
regeditWindow = FindWindow(L"RegEdit_RegEdit", NULL);
if (!regeditWindow)
{
PhShellOpenKey(WindowHandle, KeyName);
return TRUE;
}
if (!PhGetOwnTokenAttributes().Elevated)
{
if (!PhUiConnectToPhSvc(WindowHandle, FALSE))
return FALSE;
}
// Create our entry in Favorites.
if (!NT_SUCCESS(PhRegeditOpenUserFavoritesKey(regeditWindow, &favoritesKeyHandle)))
goto CleanupExit;
memcpy(favoriteName, L"A_SystemInformer", 16 * sizeof(WCHAR));
PhGenerateRandomAlphaString(&favoriteName[16], ARRAYSIZE(favoriteName) - 16);
valueName.Buffer = favoriteName;
valueName.Length = sizeof(favoriteName) - sizeof(UNICODE_NULL);
expandedKeyName = PhExpandKeyName(KeyName, FALSE);
PhSetValueKey(favoritesKeyHandle, &valueName, REG_SZ, expandedKeyName->Buffer, (ULONG)expandedKeyName->Length + sizeof(UNICODE_NULL));
PhDereferenceObject(expandedKeyName);
// Select our entry in regedit.
result = PhpSelectFavoriteInRegedit(regeditWindow, &valueName, !PhGetOwnTokenAttributes().Elevated);
PhDeleteValueKey(favoritesKeyHandle, &valueName);
NtClose(favoritesKeyHandle);
CleanupExit:
if (!PhGetOwnTokenAttributes().Elevated)
PhUiDisconnectFromPhSvc();
return result;
}
PPH_STRING PhPcre2GetErrorMessage(
_In_ LONG ErrorCode
)
{
PPH_STRING buffer;
SIZE_T bufferLength;
INT_PTR returnLength;
bufferLength = 128 * sizeof(WCHAR);
buffer = PhCreateStringEx(NULL, bufferLength);
while (TRUE)
{
if ((returnLength = pcre2_get_error_message(ErrorCode, buffer->Buffer, bufferLength / sizeof(WCHAR) + 1)) >= 0)
break;
PhDereferenceObject(buffer);
bufferLength *= 2;
if (bufferLength > 0x1000 * sizeof(WCHAR))
break;
buffer = PhCreateStringEx(NULL, bufferLength);
}
if (returnLength < 0)
return NULL;
buffer->Length = returnLength * sizeof(WCHAR);
return buffer;
}
HBITMAP PhGetShieldBitmap(
_In_ LONG WindowDpi,
_In_opt_ LONG Width,
_In_opt_ LONG Height
)
{
HICON shieldIcon;
HBITMAP shieldBitmap = NULL;
shieldIcon = PhLoadIcon(
PhInstanceHandle,
MAKEINTRESOURCE(IDI_UACSHIELD),
0,
Width,
Height,
WindowDpi
);
if (!shieldIcon)
{
shieldIcon = PhLoadIcon(
NULL,
IDI_SHIELD,
0,
Width,
Height,
WindowDpi
);
}
if (shieldIcon)
{
shieldBitmap = PhIconToBitmap(
shieldIcon,
Width,
Height
);
DestroyIcon(shieldIcon);
}
return shieldBitmap;
}
HICON PhGetApplicationIcon(
_In_ BOOLEAN SmallIcon
)
{
static HICON smallIcon = NULL;
static HICON largeIcon = NULL;
static LONG systemDpi = 0;
if (systemDpi != PhSystemDpi)
{
if (smallIcon)
{
DestroyIcon(smallIcon);
smallIcon = NULL;
}
if (largeIcon)
{
DestroyIcon(largeIcon);
largeIcon = NULL;
}
systemDpi = PhSystemDpi;
}
if (!smallIcon || !largeIcon)
{
if (!smallIcon)
smallIcon = PhLoadIcon(NtCurrentImageBase(), MAKEINTRESOURCE(IDI_PROCESSHACKER), PH_LOAD_ICON_SIZE_SMALL, 0, 0, systemDpi);
if (!largeIcon)
largeIcon = PhLoadIcon(NtCurrentImageBase(), MAKEINTRESOURCE(IDI_PROCESSHACKER), PH_LOAD_ICON_SIZE_LARGE, 0, 0, systemDpi);
}
return SmallIcon ? smallIcon : largeIcon;
}
HICON PhGetApplicationIconEx(
_In_ BOOLEAN SmallIcon,
_In_opt_ LONG WindowDpi
)
{
if (SmallIcon)
return PhLoadIcon(NtCurrentImageBase(), MAKEINTRESOURCE(IDI_PROCESSHACKER), PH_LOAD_ICON_SIZE_SMALL, 0, 0, WindowDpi);
return PhLoadIcon(NtCurrentImageBase(), MAKEINTRESOURCE(IDI_PROCESSHACKER), PH_LOAD_ICON_SIZE_LARGE, 0, 0, WindowDpi);
}
VOID PhSetWindowIcon(
_In_ HWND WindowHandle,
_In_opt_ HICON SmallIcon,
_In_opt_ HICON LargeIcon,
_In_ BOOLEAN CleanupIcon
)
{
if (SmallIcon)
{
HICON iconHandle = (HICON)SendMessage(WindowHandle, WM_SETICON, ICON_SMALL, (LPARAM)SmallIcon);
if (iconHandle && CleanupIcon)
{
DestroyIcon(iconHandle);
}
}
if (LargeIcon)
{
HICON iconHandle = (HICON)SendMessage(WindowHandle, WM_SETICON, ICON_BIG, (LPARAM)LargeIcon);
if (iconHandle && CleanupIcon)
{
DestroyIcon(iconHandle);
}
}
}
VOID PhDestroyWindowIcon(
_In_ HWND WindowHandle
)
{
HICON iconHandle;
if (iconHandle = (HICON)SendMessage(WindowHandle, WM_SETICON, ICON_SMALL, 0))
{
DestroyIcon(iconHandle);
}
if (iconHandle = (HICON)SendMessage(WindowHandle, WM_SETICON, ICON_BIG, 0))
{
DestroyIcon(iconHandle);
}
}
VOID PhSetApplicationWindowIcon(
_In_ HWND WindowHandle
)
{
PhSetWindowIcon(
WindowHandle,
PhGetApplicationIcon(TRUE),
PhGetApplicationIcon(FALSE),
TRUE
);
}
VOID PhSetApplicationWindowIconEx(
_In_ HWND WindowHandle,
_In_opt_ LONG WindowDpi
)
{
PhSetWindowIcon(
WindowHandle,
PhGetApplicationIconEx(TRUE, WindowDpi),
PhGetApplicationIconEx(FALSE, WindowDpi),
TRUE
);
}
VOID PhSetStaticWindowIcon(
_In_ HWND WindowHandle,
_In_opt_ LONG WindowDpi
)
{
HICON largeIcon;
HICON destroyIcon;
if (largeIcon = PhGetApplicationIconEx(FALSE, WindowDpi))
{
if (destroyIcon = Static_SetIcon(WindowHandle, largeIcon))
{
DestroyIcon(destroyIcon);
}
}
}
VOID PhDeleteStaticWindowIcon(
_In_ HWND WindowHandle
)
{
HICON destroyIcon;
if (destroyIcon = Static_GetIcon(WindowHandle, 0))
{
DestroyIcon(destroyIcon);
}
}
BOOLEAN PhWordMatchStringRef(
_In_ PPH_STRINGREF SearchText,
_In_ PPH_STRINGREF Text
)
{
PH_STRINGREF part;
PH_STRINGREF remainingPart;
remainingPart = *SearchText;
while (remainingPart.Length)
{
PhSplitStringRefAtChar(&remainingPart, L'|', &part, &remainingPart);
if (part.Length)
{
if (PhFindStringInStringRef(Text, &part, TRUE) != SIZE_MAX)
return TRUE;
}
}
return FALSE;
}
//BOOLEAN PhIsSystemRebootRequired(
// VOID
// )
//{
// static PH_STRINGREF keyRootPath = PH_STRINGREF_INIT(L"Software\\Microsoft\\Windows\\CurrentVersion\\WindowsUpdate");
// static PH_STRINGREF keyAuPath = PH_STRINGREF_INIT(L"Auto Update\\RebootRequired");
// static PH_STRINGREF keyOrcPath = PH_STRINGREF_INIT(L"Orchestrator\\RebootRequired");
// BOOLEAN keyRebootRequired = FALSE;
// HANDLE keyRootHandle;
// HANDLE keyHandle;
//
// if (NT_SUCCESS(PhOpenKey(
// &keyRootHandle,
// KEY_READ,
// PH_KEY_LOCAL_MACHINE,
// &keyRootPath,
// 0
// )))
// {
// if (NT_SUCCESS(PhOpenKey(
// &keyHandle,
// KEY_READ,
// keyRootHandle,
// &keyAuPath,
// 0
// )))
// {
// keyRebootRequired = TRUE;
// NtClose(keyHandle);
// }
//
// if (NT_SUCCESS(PhOpenKey(
// &keyHandle,
// KEY_READ,
// keyRootHandle,
// &keyOrcPath,
// 0
// )))
// {
// keyRebootRequired = TRUE;
// NtClose(keyHandle);
// }
//
// NtClose(keyRootHandle);
// }
//
// //#include
// //ISystemInformation* systemInformation = NULL;
// //
// //if (SUCCEEDED(PhGetClassObject(
// // L"wuapi.dll",
// // &CLSID_SystemInformation,
// // &IID_ISystemInformation,
// // &systemInformation
// // )))
// //{
// // VARIANT_BOOL rebootRequiredVariant = VARIANT_FALSE;
// //
// // ISystemInformation_get_RebootRequired(systemInformation, &rebootRequiredVariant);
// // ISystemInformation_Release(systemInformation);
// //
// // keyRebootRequired = rebootRequiredVariant == VARIANT_TRUE;
// //}
//
// return keyRebootRequired;
//}
================================================
FILE: SystemInformer/chcol.c
================================================
/*
* Copyright (c) 2022 Winsider Seminars & Solutions, Inc. All rights reserved.
*
* This file is part of System Informer.
*
* Authors:
*
* wj32 2010
* dmex 2017-2023
*
*/
#include
#include
typedef struct _COLUMNS_DIALOG_CONTEXT
{
HWND ControlHandle;
HFONT ControlFont;
ULONG Type;
PPH_LIST Columns;
HBRUSH BrushNormal;
HBRUSH BrushPushed;
HBRUSH BrushHot;
COLORREF TextColor;
HWND InactiveWindowHandle;
HWND ActiveWindowHandle;
HWND SearchInactiveHandle;
HWND SearchActiveHandle;
HWND HideWindowHandle;
HWND ShowWindowHandle;
HWND MoveUpHandle;
HWND MoveDownHandle;
PPH_LIST InactiveListArray;
PPH_LIST ActiveListArray;
} COLUMNS_DIALOG_CONTEXT, *PCOLUMNS_DIALOG_CONTEXT;
INT_PTR CALLBACK PhpColumnsDlgProc(
_In_ HWND hwndDlg,
_In_ UINT uMsg,
_In_ WPARAM wParam,
_In_ LPARAM lParam
);
VOID PhShowChooseColumnsDialog(
_In_ HWND ParentWindowHandle,
_In_ HWND ControlHandle,
_In_ ULONG Type
)
{
COLUMNS_DIALOG_CONTEXT context;
memset(&context, 0, sizeof(COLUMNS_DIALOG_CONTEXT));
context.ControlHandle = ControlHandle;
context.Type = Type;
if (Type == PH_CONTROL_TYPE_TREE_NEW)
context.Columns = PhCreateList(TreeNew_GetColumnCount(ControlHandle));
else
return;
PhDialogBox(
PhInstanceHandle,
MAKEINTRESOURCE(IDD_CHOOSECOLUMNS),
ParentWindowHandle,
PhpColumnsDlgProc,
&context
);
PhDereferenceObject(context.Columns);
}
static int __cdecl PhpColumnsCompareDisplayIndexTn(
_In_ void* Context,
_In_ void const* elem1,
_In_ void const* elem2
)
{
PPH_TREENEW_COLUMN column1 = *(PPH_TREENEW_COLUMN *)elem1;
PPH_TREENEW_COLUMN column2 = *(PPH_TREENEW_COLUMN *)elem2;
return uintcmp(column1->DisplayIndex, column2->DisplayIndex);
}
static long __cdecl PhpInactiveColumnsCompareNameTn(
_In_ const void* Context,
_In_ const void *elem1,
_In_ const void *elem2
)
{
PCWSTR column1 = *(PCWSTR *)elem1;
PCWSTR column2 = *(PCWSTR *)elem2;
return PhCompareStringZ(column1, column2, FALSE);
}
_Success_(return != ULONG_MAX)
static ULONG IndexOfStringInList(
_In_ PPH_LIST List,
_In_ PCWSTR String
)
{
for (ULONG i = 0; i < List->Count; i++)
{
if (PhEqualStringZ(List->Items[i], String, FALSE))
return i;
}
return ULONG_MAX;
}
VOID PhpColumnsResetListBox(
_In_ HWND ListBoxHandle,
_In_ ULONG_PTR MatchHandle,
_In_ PPH_LIST Array,
_In_opt_ PVOID CompareFunction
)
{
SendMessage(ListBoxHandle, WM_SETREDRAW, FALSE, 0);
ListBox_ResetContent(ListBoxHandle);
if (CompareFunction)
qsort_s(Array->Items, Array->Count, sizeof(ULONG_PTR), CompareFunction, NULL);
if (!MatchHandle)
{
for (ULONG i = 0; i < Array->Count; i++)
{
ListBox_InsertString(ListBoxHandle, i, Array->Items[i]);
}
}
else
{
ULONG index = 0;
for (ULONG i = 0; i < Array->Count; i++)
{
PH_STRINGREF text;
PhInitializeStringRefLongHint(&text, Array->Items[i]);
if (PhSearchControlMatch(MatchHandle, &text))
{
ListBox_InsertString(ListBoxHandle, index, Array->Items[i]);
index++;
}
}
}
SendMessage(ListBoxHandle, WM_SETREDRAW, TRUE, 0);
}
_Function_class_(PH_SEARCHCONTROL_CALLBACK)
VOID NTAPI PhpInactiveColumnsSearchControlCallback(
_In_ ULONG_PTR MatchHandle,
_In_opt_ PVOID Context
)
{
PCOLUMNS_DIALOG_CONTEXT context = Context;
PhpColumnsResetListBox(
context->InactiveWindowHandle,
MatchHandle,
context->InactiveListArray,
PhpInactiveColumnsCompareNameTn
);
}
_Function_class_(PH_SEARCHCONTROL_CALLBACK)
VOID NTAPI PhpActiveColumnsSearchControlCallback(
_In_ ULONG_PTR MatchHandle,
_In_opt_ PVOID Context
)
{
PCOLUMNS_DIALOG_CONTEXT context = Context;
PhpColumnsResetListBox(
context->ActiveWindowHandle,
MatchHandle,
context->ActiveListArray,
NULL
);
}
INT_PTR CALLBACK PhpColumnsDlgProc(
_In_ HWND hwndDlg,
_In_ UINT uMsg,
_In_ WPARAM wParam,
_In_ LPARAM lParam
)
{
PCOLUMNS_DIALOG_CONTEXT context = NULL;
if (uMsg == WM_INITDIALOG)
{
context = (PCOLUMNS_DIALOG_CONTEXT)lParam;
PhSetWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT, context);
}
else
{
context = PhGetWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT);
}
if (!context)
return FALSE;
switch (uMsg)
{
case WM_INITDIALOG:
{
ULONG count;
ULONG total;
ULONG i;
PPH_LIST displayOrderList = NULL;
LONG dpiValue;
PhCenterWindow(hwndDlg, GetParent(hwndDlg));
PhSetApplicationWindowIcon(hwndDlg);
dpiValue = PhGetWindowDpi(hwndDlg);
context->InactiveWindowHandle = GetDlgItem(hwndDlg, IDC_INACTIVE);
context->ActiveWindowHandle = GetDlgItem(hwndDlg, IDC_ACTIVE);
context->SearchInactiveHandle = GetDlgItem(hwndDlg, IDC_SEARCH);
context->SearchActiveHandle = GetDlgItem(hwndDlg, IDC_FILTER);
context->HideWindowHandle = GetDlgItem(hwndDlg, IDC_HIDE);
context->ShowWindowHandle = GetDlgItem(hwndDlg, IDC_SHOW);
context->MoveUpHandle = GetDlgItem(hwndDlg, IDC_MOVEUP);
context->MoveDownHandle = GetDlgItem(hwndDlg, IDC_MOVEDOWN);
context->InactiveListArray = PhCreateList(1);
context->ActiveListArray = PhCreateList(1);
context->ControlFont = PhCreateMessageFont(dpiValue); // PhDuplicateFont(PhTreeWindowFont)
PhCreateSearchControl(
hwndDlg,
context->SearchInactiveHandle,
L"Inactive columns...",
PhpInactiveColumnsSearchControlCallback,
context
);
PhCreateSearchControl(
hwndDlg,
context->SearchActiveHandle,
L"Active columns...",
PhpActiveColumnsSearchControlCallback,
context
);
ListBox_SetItemHeight(context->InactiveWindowHandle, 0, PhGetDpi(16, dpiValue));
ListBox_SetItemHeight(context->ActiveWindowHandle, 0, PhGetDpi(16, dpiValue));
Button_Enable(context->HideWindowHandle, FALSE);
Button_Enable(context->ShowWindowHandle, FALSE);
Button_Enable(context->MoveUpHandle, FALSE);
Button_Enable(context->MoveDownHandle, FALSE);
if (PhEnableThemeSupport)
{
context->BrushNormal = CreateSolidBrush(PhThemeWindowBackgroundColor);
context->BrushHot = CreateSolidBrush(PhThemeWindowHighlightColor);
context->BrushPushed = CreateSolidBrush(PhThemeWindowHighlight2Color);
context->TextColor = PhThemeWindowTextColor;
}
else
{
context->BrushNormal = GetSysColorBrush(COLOR_WINDOW);
context->BrushHot = CreateSolidBrush(RGB(145, 201, 247));
context->BrushPushed = CreateSolidBrush(RGB(153, 209, 255));
context->TextColor = GetSysColor(COLOR_WINDOWTEXT);
}
if (context->Type == PH_CONTROL_TYPE_TREE_NEW)
{
PH_TREENEW_COLUMN column;
count = 0;
total = TreeNew_GetColumnCount(context->ControlHandle);
i = 0;
displayOrderList = PhCreateList(total);
while (count < total)
{
if (TreeNew_GetColumn(context->ControlHandle, i, &column))
{
PPH_TREENEW_COLUMN copy;
if (column.Fixed)
{
i++;
total--;
continue;
}
copy = PhAllocateCopy(&column, sizeof(PH_TREENEW_COLUMN));
PhAddItemList(context->Columns, copy);
count++;
if (column.Visible)
{
PhAddItemList(displayOrderList, copy);
}
else
{
PhAddItemList(context->InactiveListArray, (PWSTR)column.Text);
}
}
i++;
}
qsort_s(displayOrderList->Items, displayOrderList->Count, sizeof(PVOID), PhpColumnsCompareDisplayIndexTn, NULL);
}
PhpColumnsResetListBox(
context->InactiveWindowHandle,
0,
context->InactiveListArray,
PhpInactiveColumnsCompareNameTn
);
if (displayOrderList)
{
for (i = 0; i < displayOrderList->Count; i++)
{
if (context->Type == PH_CONTROL_TYPE_TREE_NEW)
{
PPH_TREENEW_COLUMN copy = displayOrderList->Items[i];
PhAddItemList(context->ActiveListArray, (PWSTR)copy->Text);
ListBox_InsertString(context->ActiveWindowHandle, i, copy->Text);
}
}
PhDereferenceObject(displayOrderList);
}
SendMessage(hwndDlg, WM_COMMAND, MAKEWPARAM(IDC_INACTIVE, LBN_SELCHANGE), (LPARAM)context->InactiveWindowHandle);
SendMessage(hwndDlg, WM_COMMAND, MAKEWPARAM(IDC_ACTIVE, LBN_SELCHANGE), (LPARAM)context->ActiveWindowHandle);
PhInitializeWindowTheme(hwndDlg, PhEnableThemeSupport);
PhSetDialogFocus(hwndDlg, GetDlgItem(hwndDlg, IDCANCEL));
}
break;
case WM_DESTROY:
{
for (ULONG i = 0; i < context->Columns->Count; i++)
PhFree(context->Columns->Items[i]);
if (context->BrushNormal)
DeleteBrush(context->BrushNormal);
if (context->BrushHot)
DeleteBrush(context->BrushHot);
if (context->BrushPushed)
DeleteBrush(context->BrushPushed);
if (context->ControlFont)
DeleteFont(context->ControlFont);
if (context->InactiveListArray)
PhDereferenceObject(context->InactiveListArray);
if (context->ActiveListArray)
PhDereferenceObject(context->ActiveListArray);
PhRemoveWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT);
}
break;
case WM_DPICHANGED:
{
if (context->ControlFont) DeleteFont(context->ControlFont);
context->ControlFont = PhCreateMessageFont(LOWORD(wParam));
ListBox_SetItemHeight(context->InactiveWindowHandle, 0, PhGetDpi(16, LOWORD(wParam)));
ListBox_SetItemHeight(context->ActiveWindowHandle, 0, PhGetDpi(16, LOWORD(wParam)));
}
break;
case WM_COMMAND:
{
switch (GET_WM_COMMAND_ID(wParam, lParam))
{
case IDCANCEL:
EndDialog(hwndDlg, IDCANCEL);
break;
case IDOK:
{
ULONG i;
ULONG orderArraySize;
PULONG orderArray;
ULONG maxOrder;
if (context->Type == PH_CONTROL_TYPE_TREE_NEW)
{
orderArraySize = (TreeNew_GetColumnCount(context->ControlHandle) + 1) * sizeof(ULONG);
orderArray = _malloca(orderArraySize);
memset(orderArray, 0, orderArraySize);
maxOrder = 0;
// Apply visibility settings and build the order array.
TreeNew_SetRedraw(context->ControlHandle, FALSE);
for (i = 0; i < context->Columns->Count; i++)
{
PPH_TREENEW_COLUMN column = context->Columns->Items[i];
ULONG index;
index = IndexOfStringInList(context->ActiveListArray, column->Text);
column->Visible = index != ULONG_MAX;
TreeNew_SetColumn(context->ControlHandle, TN_COLUMN_FLAG_VISIBLE, column);
if (column->Visible)
{
orderArray[index] = column->Id;
if (maxOrder < index + 1)
maxOrder = index + 1;
}
}
// Apply display order.
TreeNew_SetColumnOrderArray(context->ControlHandle, maxOrder, orderArray);
TreeNew_SetRedraw(context->ControlHandle, TRUE);
InvalidateRect(context->ControlHandle, NULL, FALSE);
_freea(orderArray);
}
EndDialog(hwndDlg, IDOK);
}
break;
case IDC_INACTIVE:
{
switch (GET_WM_COMMAND_CMD(wParam, lParam))
{
case LBN_DBLCLK:
{
SendMessage(hwndDlg, WM_COMMAND, IDC_SHOW, 0);
}
break;
case LBN_SELCHANGE:
{
LONG sel = ListBox_GetCurSel(context->InactiveWindowHandle);
EnableWindow(context->ShowWindowHandle, sel != LB_ERR);
}
break;
}
}
break;
case IDC_ACTIVE:
{
switch (GET_WM_COMMAND_CMD(wParam, lParam))
{
case LBN_DBLCLK:
{
SendMessage(hwndDlg, WM_COMMAND, IDC_HIDE, 0);
}
break;
case LBN_SELCHANGE:
{
LONG sel = ListBox_GetCurSel(context->ActiveWindowHandle);
LONG count = ListBox_GetCount(context->ActiveWindowHandle);
if (sel != LB_ERR)
{
EnableWindow(context->HideWindowHandle, sel != 0);
EnableWindow(context->MoveUpHandle, sel != 0);
EnableWindow(context->MoveDownHandle, sel != count - 1);
}
}
break;
}
}
break;
case IDC_SHOW:
{
LONG sel;
LONG count;
PPH_STRING string;
sel = ListBox_GetCurSel(context->InactiveWindowHandle);
count = ListBox_GetCount(context->InactiveWindowHandle);
if (sel != LB_ERR)
{
string = PhGetListBoxString(context->InactiveWindowHandle, sel);
if (!PhIsNullOrEmptyString(string))
{
ULONG index = IndexOfStringInList(context->InactiveListArray, string->Buffer);
if (index != ULONG_MAX)
{
PVOID item = context->InactiveListArray->Items[index];
PhRemoveItemsList(context->InactiveListArray, index, 1);
PhAddItemList(context->ActiveListArray, item);
ListBox_DeleteString(context->InactiveWindowHandle, sel);
ListBox_AddString(context->ActiveWindowHandle, item);
}
count--;
if (sel >= count - 1)
sel = count - 1;
if (sel != LB_ERR)
{
ListBox_SetCurSel(context->InactiveWindowHandle, sel);
}
}
}
SendMessage(hwndDlg, WM_COMMAND, MAKEWPARAM(IDC_INACTIVE, LBN_SELCHANGE), (LPARAM)context->InactiveWindowHandle);
SendMessage(hwndDlg, WM_COMMAND, MAKEWPARAM(IDC_ACTIVE, LBN_SELCHANGE), (LPARAM)context->ActiveWindowHandle);
}
break;
case IDC_HIDE:
{
LONG sel;
LONG count;
PPH_STRING string;
sel = ListBox_GetCurSel(context->ActiveWindowHandle);
count = ListBox_GetCount(context->ActiveWindowHandle);
if (sel != LB_ERR)
{
string = PhGetListBoxString(context->ActiveWindowHandle, sel);
if (!PhIsNullOrEmptyString(string))
{
ULONG index = IndexOfStringInList(context->ActiveListArray, string->Buffer);
if (index != ULONG_MAX)
{
PVOID item = context->ActiveListArray->Items[index];
// Remove from active array, insert into inactive
PhRemoveItemsList(context->ActiveListArray, index, 1);
PhAddItemList(context->InactiveListArray, item);
// Sort inactive list with new entry
qsort_s(context->InactiveListArray->Items, context->InactiveListArray->Count, sizeof(ULONG_PTR), PhpInactiveColumnsCompareNameTn, NULL);
// Find index of new entry in inactive list
ULONG lb_index = IndexOfStringInList(context->InactiveListArray, item);
// Delete from active list
ListBox_DeleteString(context->ActiveWindowHandle, sel);
// Add to list in the same position as the inactive list
ListBox_InsertString(context->InactiveWindowHandle, lb_index, item);
PhpColumnsResetListBox(context->InactiveWindowHandle, 0, context->InactiveListArray, PhpInactiveColumnsCompareNameTn);
}
count--;
if (sel >= count - 1)
sel = count - 1;
if (sel != LB_ERR)
{
ListBox_SetCurSel(context->ActiveWindowHandle, sel);
}
}
PhClearReference(&string);
}
SendMessage(hwndDlg, WM_COMMAND, MAKEWPARAM(IDC_INACTIVE, LBN_SELCHANGE), (LPARAM)context->InactiveWindowHandle);
SendMessage(hwndDlg, WM_COMMAND, MAKEWPARAM(IDC_ACTIVE, LBN_SELCHANGE), (LPARAM)context->ActiveWindowHandle);
}
break;
case IDC_MOVEUP:
{
LONG sel;
LONG count;
PPH_STRING string;
sel = ListBox_GetCurSel(context->ActiveWindowHandle);
count = ListBox_GetCount(context->ActiveWindowHandle);
if (sel != LB_ERR)
{
string = PhGetListBoxString(context->ActiveWindowHandle, sel);
if (!PhIsNullOrEmptyString(string))
{
ULONG index = IndexOfStringInList(context->ActiveListArray, string->Buffer);
if (index != ULONG_MAX)
{
PVOID item = context->ActiveListArray->Items[index];
PhRemoveItemsList(context->ActiveListArray, index, 1);
PhInsertItemList(context->ActiveListArray, index - 1, item);
ListBox_DeleteString(context->ActiveWindowHandle, sel);
sel -= 1;
ListBox_InsertString(context->ActiveWindowHandle, sel, item);
ListBox_SetCurSel(context->ActiveWindowHandle, sel);
EnableWindow(context->MoveUpHandle, sel != 0);
EnableWindow(context->MoveDownHandle, sel != count - 1);
}
}
PhClearReference(&string);
}
}
break;
case IDC_MOVEDOWN:
{
LONG sel;
LONG count;
PPH_STRING string;
sel = ListBox_GetCurSel(context->ActiveWindowHandle);
count = ListBox_GetCount(context->ActiveWindowHandle);
if (sel != LB_ERR && sel != count - 1)
{
string = PhGetListBoxString(context->ActiveWindowHandle, sel);
if (!PhIsNullOrEmptyString(string))
{
ULONG index = IndexOfStringInList(context->ActiveListArray, string->Buffer);
if (index != ULONG_MAX)
{
PVOID item = context->ActiveListArray->Items[index];
PhRemoveItemsList(context->ActiveListArray, index, 1);
PhInsertItemList(context->ActiveListArray, index + 1, item);
ListBox_DeleteString(context->ActiveWindowHandle, sel);
sel += 1;
ListBox_InsertString(context->ActiveWindowHandle, sel, item);
ListBox_SetCurSel(context->ActiveWindowHandle, sel);
EnableWindow(context->MoveUpHandle, sel != 0);
EnableWindow(context->MoveDownHandle, sel != count - 1);
}
}
PhClearReference(&string);
}
}
break;
}
}
break;
case WM_DRAWITEM:
{
LPDRAWITEMSTRUCT drawInfo = (LPDRAWITEMSTRUCT)lParam;
if (
drawInfo->hwndItem == context->InactiveWindowHandle ||
drawInfo->hwndItem == context->ActiveWindowHandle
)
{
HDC bufferDc;
HBITMAP bufferBitmap;
HBITMAP oldBufferBitmap;
PPH_STRING string;
RECT bufferRect =
{
0, 0,
drawInfo->rcItem.right - drawInfo->rcItem.left,
drawInfo->rcItem.bottom - drawInfo->rcItem.top
};
BOOLEAN isSelected = (drawInfo->itemState & ODS_SELECTED) == ODS_SELECTED;
BOOLEAN isFocused = (drawInfo->itemState & ODS_FOCUS) == ODS_FOCUS;
if (drawInfo->itemID == LB_ERR)
break;
string = PhGetListBoxString(drawInfo->hwndItem, drawInfo->itemID);
if (PhIsNullOrEmptyString(string))
{
PhClearReference(&string);
break;
}
bufferDc = CreateCompatibleDC(drawInfo->hDC);
bufferBitmap = CreateCompatibleBitmap(drawInfo->hDC, bufferRect.right, bufferRect.bottom);
oldBufferBitmap = SelectBitmap(bufferDc, bufferBitmap);
SelectFont(bufferDc, context->ControlFont);
SetBkMode(bufferDc, TRANSPARENT);
if (isSelected || isFocused)
{
FillRect(bufferDc, &bufferRect, context->BrushHot);
//FrameRect(bufferDc, &bufferRect, PhGetStockBrush(BLACK_BRUSH));
SetTextColor(bufferDc, context->TextColor);
}
else
{
FillRect(bufferDc, &bufferRect, context->BrushNormal);
//FrameRect(bufferDc, &bufferRect, GetSysColorBrush(COLOR_HIGHLIGHTTEXT));
SetTextColor(bufferDc, context->TextColor);
}
bufferRect.left += 5;
DrawText(
bufferDc,
string->Buffer,
(UINT)string->Length / sizeof(WCHAR),
&bufferRect,
DT_LEFT | DT_VCENTER | DT_SINGLELINE | DT_END_ELLIPSIS | DT_NOCLIP
);
bufferRect.left -= 5;
BitBlt(
drawInfo->hDC,
drawInfo->rcItem.left,
drawInfo->rcItem.top,
drawInfo->rcItem.right,
drawInfo->rcItem.bottom,
bufferDc,
0,
0,
SRCCOPY
);
SelectBitmap(bufferDc, oldBufferBitmap);
DeleteBitmap(bufferBitmap);
DeleteDC(bufferDc);
PhClearReference(&string);
return TRUE;
}
}
break;
case WM_CTLCOLORBTN:
return HANDLE_WM_CTLCOLORBTN(hwndDlg, wParam, lParam, PhWindowThemeControlColor);
case WM_CTLCOLORDLG:
return HANDLE_WM_CTLCOLORDLG(hwndDlg, wParam, lParam, PhWindowThemeControlColor);
case WM_CTLCOLORSTATIC:
return HANDLE_WM_CTLCOLORSTATIC(hwndDlg, wParam, lParam, PhWindowThemeControlColor);
}
return FALSE;
}
================================================
FILE: SystemInformer/chdlg.c
================================================
/*
* Copyright (c) 2022 Winsider Seminars & Solutions, Inc. All rights reserved.
*
* This file is part of System Informer.
*
* Authors:
*
* wj32 2010-2013
* dmex 2015-2024
*
*/
#include
#include
#include
#include
#include
typedef struct _PH_CHOICE_DIALOG_CONTEXT
{
PCWSTR Title;
PCWSTR Message;
PCWSTR *Choices;
ULONG NumberOfChoices;
PCWSTR Option;
ULONG Flags;
PPH_STRING *SelectedChoice;
PBOOLEAN SelectedOption;
PCWSTR SavedChoicesSettingName;
HWND ComboBoxHandle;
HFONT FontHandle;
} PH_CHOICE_DIALOG_CONTEXT, *PPH_CHOICE_DIALOG_CONTEXT;
INT_PTR CALLBACK PhChoiceDlgProc(
_In_ HWND WindowHandle,
_In_ UINT WindowMessage,
_In_ WPARAM wParam,
_In_ LPARAM lParam
)
{
PPH_CHOICE_DIALOG_CONTEXT context;
if (WindowMessage == WM_INITDIALOG)
{
context = (PPH_CHOICE_DIALOG_CONTEXT)lParam;
PhSetWindowContext(WindowHandle, PH_WINDOW_CONTEXT_DEFAULT, context);
}
else
{
context = PhGetWindowContext(WindowHandle, PH_WINDOW_CONTEXT_DEFAULT);
}
if (!context)
return FALSE;
switch (WindowMessage)
{
case WM_INITDIALOG:
{
ULONG type;
SIZE_T i;
HWND comboBoxHandle;
HWND checkBoxHandle;
RECT checkBoxRect;
RECT rect;
ULONG diff;
checkBoxHandle = GetDlgItem(WindowHandle, IDC_OPTION);
PhSetWindowText(WindowHandle, context->Title);
PhSetWindowText(GetDlgItem(WindowHandle, IDC_MESSAGE), context->Message);
PhCenterWindow(WindowHandle, GetParent(WindowHandle));
type = context->Flags & PH_CHOICE_DIALOG_TYPE_MASK;
// Select the control to show, depending on the type. This is
// because it is impossible to change the style of the combo box
// after it is created.
switch (type)
{
case PH_CHOICE_DIALOG_USER_CHOICE:
comboBoxHandle = GetDlgItem(WindowHandle, IDC_CHOICEUSER);
break;
case PH_CHOICE_DIALOG_PASSWORD:
comboBoxHandle = GetDlgItem(WindowHandle, IDC_CHOICESIMPLE);
// Disable combo box features since it isn't a combo box.
context->SavedChoicesSettingName = NULL;
break;
case PH_CHOICE_DIALOG_CHOICE:
default:
comboBoxHandle = GetDlgItem(WindowHandle, IDC_CHOICE);
break;
}
context->ComboBoxHandle = comboBoxHandle;
ShowWindow(context->ComboBoxHandle, SW_SHOW);
if (type == PH_CHOICE_DIALOG_PASSWORD)
{
// Nothing
}
else if (type == PH_CHOICE_DIALOG_USER_CHOICE && context->SavedChoicesSettingName)
{
PPH_STRING savedChoices = PhGetStringSetting(context->SavedChoicesSettingName);
ULONG_PTR indexOfDelim;
PPH_STRING savedChoice;
i = 0;
// Split the saved choices using the delimiter.
while (i < savedChoices->Length / sizeof(WCHAR))
{
// BUG BUG BUG - what if the user saves "\s"?
indexOfDelim = PhFindStringInString(savedChoices, i, L"\\s");
if (indexOfDelim == SIZE_MAX)
indexOfDelim = savedChoices->Length / sizeof(WCHAR);
savedChoice = PhSubstring(savedChoices, i, indexOfDelim - i);
if (savedChoice->Length != 0)
{
PPH_STRING unescaped;
unescaped = PhUnescapeStringForDelimiter(savedChoice, L'\\');
ComboBox_InsertString(comboBoxHandle, -1, unescaped->Buffer);
PhDereferenceObject(unescaped);
}
PhDereferenceObject(savedChoice);
i = indexOfDelim + 2;
}
PhDereferenceObject(savedChoices);
}
else
{
for (i = 0; i < context->NumberOfChoices; i++)
{
ComboBox_AddString(comboBoxHandle, context->Choices[i]);
}
context->SavedChoicesSettingName = NULL; // make sure we don't try to save the choices
}
if (type == PH_CHOICE_DIALOG_PASSWORD)
{
if (!PhIsNullOrEmptyString(*context->SelectedChoice))
{
PhSetWindowText(comboBoxHandle, PhGetString(*context->SelectedChoice));
}
Edit_SetSel(comboBoxHandle, 0, -1);
}
else if (type == PH_CHOICE_DIALOG_USER_CHOICE || type == PH_CHOICE_DIALOG_CHOICE)
{
// If we failed to choose a default choice based on what was specified,
// select the first one if possible, or set the text directly.
if (
!PhIsNullOrEmptyString(*context->SelectedChoice) ||
PhSelectComboBoxString(comboBoxHandle, PhGetString(*context->SelectedChoice), FALSE) == CB_ERR
)
{
if (type == PH_CHOICE_DIALOG_USER_CHOICE && !PhIsNullOrEmptyString(*context->SelectedChoice))
{
PhSetWindowText(comboBoxHandle, PhGetString(*context->SelectedChoice));
}
else if (type == PH_CHOICE_DIALOG_CHOICE && context->NumberOfChoices != 0)
{
ComboBox_SetCurSel(comboBoxHandle, 0);
}
}
if (type == PH_CHOICE_DIALOG_USER_CHOICE)
ComboBox_SetEditSel(comboBoxHandle, 0, -1);
}
if (context->Option)
{
PhSetWindowText(checkBoxHandle, context->Option);
if (context->SelectedOption)
Button_SetCheck(checkBoxHandle, *context->SelectedOption ? BST_CHECKED : BST_UNCHECKED);
}
else
{
// Hide the check box and move the buttons up.
ShowWindow(checkBoxHandle, SW_HIDE);
PhGetWindowRect(checkBoxHandle, &checkBoxRect);
MapWindowRect(NULL, WindowHandle, &checkBoxRect);
PhGetWindowRect(GetDlgItem(WindowHandle, IDOK), &rect);
MapWindowRect(NULL, WindowHandle, &rect);
diff = rect.top - checkBoxRect.top;
// OK
rect.top -= diff;
rect.bottom -= diff;
SetWindowPos(GetDlgItem(WindowHandle, IDOK), NULL, rect.left, rect.top,
rect.right - rect.left, rect.bottom - rect.top,
SWP_NOACTIVATE | SWP_NOZORDER);
// Cancel
PhGetWindowRect(GetDlgItem(WindowHandle, IDCANCEL), &rect);
MapWindowRect(NULL, WindowHandle, &rect);
rect.top -= diff;
rect.bottom -= diff;
SetWindowPos(GetDlgItem(WindowHandle, IDCANCEL), NULL, rect.left, rect.top,
rect.right - rect.left, rect.bottom - rect.top,
SWP_NOACTIVATE | SWP_NOZORDER);
// Window
PhGetWindowRect(WindowHandle, &rect);
rect.bottom -= diff;
SetWindowPos(WindowHandle, NULL, rect.left, rect.top,
rect.right - rect.left, rect.bottom - rect.top,
SWP_NOACTIVATE | SWP_NOZORDER);
}
PhInitializeWindowTheme(WindowHandle, PhEnableThemeSupport);
PhSetDialogFocus(WindowHandle, comboBoxHandle);
}
break;
case WM_DESTROY:
{
PhRemoveWindowContext(WindowHandle, PH_WINDOW_CONTEXT_DEFAULT);
}
break;
case WM_COMMAND:
{
switch (GET_WM_COMMAND_ID(wParam, lParam))
{
case IDCANCEL:
EndDialog(WindowHandle, IDCANCEL);
break;
case IDOK:
{
PPH_STRING selectedChoice;
if (FlagOn(context->Flags, PH_CHOICE_DIALOG_TYPE_MASK) != PH_CHOICE_DIALOG_PASSWORD)
{
selectedChoice = PH_AUTO(PhGetWindowText(context->ComboBoxHandle));
*context->SelectedChoice = selectedChoice;
}
else
{
// Password values are never auto-dereferenced.
selectedChoice = PhGetWindowText(context->ComboBoxHandle);
*context->SelectedChoice = selectedChoice;
}
if (context->Option && context->SelectedOption)
{
*context->SelectedOption = Button_GetCheck(GetDlgItem(WindowHandle, IDC_OPTION)) == BST_CHECKED;
}
if (context->SavedChoicesSettingName)
{
PH_STRING_BUILDER savedChoices;
ULONG i;
ULONG choicesToSave = PH_CHOICE_DIALOG_SAVED_CHOICES;
PPH_STRING choice;
PPH_STRING escaped;
PhInitializeStringBuilder(&savedChoices, 100);
// Push the selected choice to the top, then save the others.
if (selectedChoice->Length != 0)
{
escaped = PH_AUTO(PhEscapeStringForDelimiter(selectedChoice, L'\\'));
PhAppendStringBuilder(&savedChoices, &escaped->sr);
PhAppendStringBuilder2(&savedChoices, L"\\s");
}
for (i = 1; i < choicesToSave; i++)
{
choice = PH_AUTO(PhGetComboBoxString(context->ComboBoxHandle, i - 1));
if (PhIsNullOrEmptyString(choice))
break;
// Don't save the choice if it's the same as the one
// entered by the user (since we already saved it above).
if (PhEqualString(choice, selectedChoice, FALSE))
{
choicesToSave++; // useless for now, but may be needed in the future
continue;
}
escaped = PH_AUTO(PhEscapeStringForDelimiter(choice, L'\\'));
PhAppendStringBuilder(&savedChoices, &escaped->sr);
PhAppendStringBuilder2(&savedChoices, L"\\s");
}
if (PhEndsWithString2(savedChoices.String, L"\\s", FALSE))
PhRemoveEndStringBuilder(&savedChoices, 2);
PhSetStringSetting2(context->SavedChoicesSettingName, &savedChoices.String->sr);
PhDeleteStringBuilder(&savedChoices);
}
EndDialog(WindowHandle, IDOK);
}
break;
}
}
break;
case WM_CTLCOLORBTN:
return HANDLE_WM_CTLCOLORBTN(WindowHandle, wParam, lParam, PhWindowThemeControlColor);
case WM_CTLCOLORDLG:
return HANDLE_WM_CTLCOLORDLG(WindowHandle, wParam, lParam, PhWindowThemeControlColor);
case WM_CTLCOLORSTATIC:
return HANDLE_WM_CTLCOLORSTATIC(WindowHandle, wParam, lParam, PhWindowThemeControlColor);
}
return FALSE;
}
INT_PTR CALLBACK PhChooseNewPageDlgProc(
_In_ HWND WindowHandle,
_In_ UINT WindowMessage,
_In_ WPARAM wParam,
_In_ LPARAM lParam
)
{
PPH_CHOICE_DIALOG_CONTEXT context;
if (WindowMessage == WM_INITDIALOG)
{
context = (PPH_CHOICE_DIALOG_CONTEXT)lParam;
PhSetWindowContext(WindowHandle, PH_WINDOW_CONTEXT_DEFAULT, context);
}
else
{
context = PhGetWindowContext(WindowHandle, PH_WINDOW_CONTEXT_DEFAULT);
}
if (!context)
return FALSE;
switch (WindowMessage)
{
case WM_INITDIALOG:
{
context->ComboBoxHandle = GetDlgItem(WindowHandle, IDC_INPUT);
PhCenterWindow(WindowHandle, GetParent(WindowHandle));
PhSetApplicationWindowIcon(WindowHandle);
PhSetWindowText(WindowHandle, PhApplicationName);
PhSetWindowText(GetDlgItem(WindowHandle, IDC_TITLE), context->Title);
PhSetWindowText(GetDlgItem(WindowHandle, IDC_TEXT), context->Message);
{
NONCLIENTMETRICS metrics = { sizeof(NONCLIENTMETRICS) };
LONG dpi = PhGetWindowDpi(WindowHandle);
if (PhGetSystemParametersInfo(SPI_GETNONCLIENTMETRICS, sizeof(metrics), &metrics, dpi))
{
metrics.lfMessageFont.lfHeight = 4 * metrics.lfMessageFont.lfHeight / 3;
if (context->FontHandle = CreateFontIndirect(&metrics.lfMessageFont))
{
SetWindowFont(GetDlgItem(WindowHandle, IDC_TITLE), context->FontHandle, FALSE);
}
metrics.lfMessageFont.lfHeight = -14;
if (context->FontHandle = CreateFontIndirect(&metrics.lfMessageFont))
{
SetWindowFont(context->ComboBoxHandle, context->FontHandle, FALSE);
}
}
}
{
if (FlagOn(context->Flags, PH_CHOICE_DIALOG_TYPE_MASK) == PH_CHOICE_DIALOG_PASSWORD)
{
NOTHING;
}
else if (FlagOn(context->Flags, PH_CHOICE_DIALOG_TYPE_MASK) == PH_CHOICE_DIALOG_USER_CHOICE && context->SavedChoicesSettingName)
{
PPH_STRING savedChoices = PhGetStringSetting(context->SavedChoicesSettingName);
PPH_STRING savedChoice;
ULONG_PTR indexOfDelim;
ULONG_PTR i = 0;
// Split the saved choices using the delimiter.
while (i < savedChoices->Length / sizeof(WCHAR))
{
// BUG BUG BUG - what if the user saves "\s"?
indexOfDelim = PhFindStringInString(savedChoices, i, L"\\s");
if (indexOfDelim == SIZE_MAX)
indexOfDelim = savedChoices->Length / sizeof(WCHAR);
savedChoice = PhSubstring(savedChoices, i, indexOfDelim - i);
if (savedChoice->Length)
{
PPH_STRING unescaped;
unescaped = PhUnescapeStringForDelimiter(savedChoice, L'\\');
ComboBox_InsertString(context->ComboBoxHandle, UINT_MAX, unescaped->Buffer);
PhDereferenceObject(unescaped);
}
PhDereferenceObject(savedChoice);
i = indexOfDelim + 2;
}
PhDereferenceObject(savedChoices);
}
else
{
for (ULONG i = 0; i < context->NumberOfChoices; i++)
{
ComboBox_AddString(context->ComboBoxHandle, context->Choices[i]);
}
context->SavedChoicesSettingName = NULL; // make sure we don't try to save the choices
}
}
PhSetDialogFocus(WindowHandle, context->ComboBoxHandle);
if (PhEnableThemeSupport)
ShowWindow(GetDlgItem(WindowHandle, IDC_SIZE_), SW_HIDE);
PhInitializeWindowTheme(WindowHandle, PhEnableThemeSupport);
}
break;
case WM_DESTROY:
{
PhRemoveWindowContext(WindowHandle, PH_WINDOW_CONTEXT_DEFAULT);
if (context->FontHandle)
{
DeleteFont(context->FontHandle);
}
}
break;
case WM_COMMAND:
{
switch (GET_WM_COMMAND_ID(wParam, lParam))
{
case IDCANCEL:
{
EndDialog(WindowHandle, IDCANCEL);
}
break;
case IDOK:
{
PPH_STRING selectedChoice;
if (FlagOn(context->Flags, PH_CHOICE_DIALOG_TYPE_MASK) != PH_CHOICE_DIALOG_PASSWORD)
{
selectedChoice = PH_AUTO(PhGetWindowText(context->ComboBoxHandle));
*context->SelectedChoice = selectedChoice;
}
else
{
// Password values are never auto-dereferenced.
selectedChoice = PhGetWindowText(context->ComboBoxHandle);
*context->SelectedChoice = selectedChoice;
}
if (context->SavedChoicesSettingName)
{
PH_STRING_BUILDER savedChoices;
ULONG i;
ULONG choicesToSave = PH_CHOICE_DIALOG_SAVED_CHOICES;
PPH_STRING choice;
PPH_STRING escaped;
PhInitializeStringBuilder(&savedChoices, 100);
// Push the selected choice to the top, then save the others.
if (selectedChoice->Length != 0)
{
escaped = PH_AUTO(PhEscapeStringForDelimiter(selectedChoice, L'\\'));
PhAppendStringBuilder(&savedChoices, &escaped->sr);
PhAppendStringBuilder2(&savedChoices, L"\\s");
}
for (i = 1; i < choicesToSave; i++)
{
choice = PH_AUTO(PhGetComboBoxString(context->ComboBoxHandle, i - 1));
if (PhIsNullOrEmptyString(choice))
break;
// Don't save the choice if it's the same as the one
// entered by the user (since we already saved it above).
if (PhEqualString(choice, selectedChoice, FALSE))
{
choicesToSave++; // useless for now, but may be needed in the future
continue;
}
escaped = PH_AUTO(PhEscapeStringForDelimiter(choice, L'\\'));
PhAppendStringBuilder(&savedChoices, &escaped->sr);
PhAppendStringBuilder2(&savedChoices, L"\\s");
}
if (PhEndsWithString2(savedChoices.String, L"\\s", FALSE))
PhRemoveEndStringBuilder(&savedChoices, 2);
PhSetStringSetting2(context->SavedChoicesSettingName, &savedChoices.String->sr);
PhDeleteStringBuilder(&savedChoices);
}
EndDialog(WindowHandle, IDOK);
}
break;
}
}
break;
case WM_ERASEBKGND:
{
LONG dpi = PhGetWindowDpi(WindowHandle);
HDC hdc = (HDC)wParam;
RECT clientRect;
if (!PhGetClientRect(WindowHandle, &clientRect))
break;
SetBkMode(hdc, TRANSPARENT);
clientRect.bottom -= PhGetDpi(50, dpi);
FillRect(hdc, &clientRect, PhEnableThemeSupport ? PhThemeWindowBackgroundBrush : GetSysColorBrush(COLOR_WINDOW));
clientRect.top = clientRect.bottom;
clientRect.bottom = clientRect.top + PhGetDpi(50, dpi);
if (PhEnableThemeSupport)
{
SetDCBrushColor(hdc, RGB(50, 50, 50));
FillRect(hdc, &clientRect, PhGetStockBrush(DC_BRUSH));
clientRect.bottom = clientRect.top + 1;
SetDCBrushColor(hdc, PhThemeWindowForegroundColor);
FillRect(hdc, &clientRect, PhGetStockBrush(DC_BRUSH));
PhOffsetRect(&clientRect, 0, 1);
SetDCBrushColor(hdc, PhThemeWindowBackground2Color);
FillRect(hdc, &clientRect, PhGetStockBrush(DC_BRUSH));
}
else
{
FillRect(hdc, &clientRect, GetSysColorBrush(COLOR_3DFACE));
}
SetWindowLongPtr(WindowHandle, DWLP_MSGRESULT, TRUE);
}
return TRUE;
case WM_CTLCOLORSTATIC:
{
HDC hdc = (HDC)wParam;
if (PhEnableThemeSupport)
break;
SetBkMode(hdc, TRANSPARENT);
if ((HWND)lParam == GetDlgItem(WindowHandle, IDC_TITLE))
{
static COLORREF color = 0;
if (color == 0)
{
HTHEME theme;
if (theme = PhOpenThemeData(NULL, VSCLASS_TASKDIALOG, 0))
{
PhGetThemeColor(theme, TDLG_MAININSTRUCTIONPANE, 0, TMT_TEXTCOLOR, &color);
PhCloseThemeData(theme);
}
}
SetTextColor(hdc, color);
}
return (INT_PTR)PhGetStockBrush(WHITE_BRUSH);
}
break;
}
return FALSE;
}
/**
* Prompts the user for input.
*
* \remarks If \c PH_CHOICE_DIALOG_PASSWORD is specified, the string
* returned in \a SelectedChoice is NOT auto-dereferenced.
*/
BOOLEAN PhaChoiceDialog(
_In_ HWND ParentWindowHandle,
_In_ PCWSTR Title,
_In_ PCWSTR Message,
_In_opt_ PCWSTR*Choices,
_In_opt_ ULONG NumberOfChoices,
_In_opt_ PCWSTR Option,
_In_ ULONG Flags,
_Inout_ PPH_STRING *SelectedChoice,
_Inout_opt_ PBOOLEAN SelectedOption,
_In_opt_ PCWSTR SavedChoicesSettingName
)
{
PH_CHOICE_DIALOG_CONTEXT context;
memset(&context, 0, sizeof(PH_CHOICE_DIALOG_CONTEXT));
context.Title = Title;
context.Message = Message;
context.Choices = Choices;
context.NumberOfChoices = NumberOfChoices;
context.Option = Option;
context.Flags = Flags;
context.SelectedChoice = SelectedChoice;
context.SelectedOption = SelectedOption;
context.SavedChoicesSettingName = SavedChoicesSettingName;
return PhDialogBox(
PhInstanceHandle,
MAKEINTRESOURCE(IDD_CHOOSE),
ParentWindowHandle,
PhChoiceDlgProc,
&context
) == IDOK;
}
BOOLEAN PhChoiceDialog(
_In_ HWND ParentWindowHandle,
_In_ PCWSTR Title,
_In_ PCWSTR Message,
_In_opt_ PCWSTR*Choices,
_In_opt_ ULONG NumberOfChoices,
_In_opt_ PCWSTR Option,
_In_ ULONG Flags,
_Inout_ PPH_STRING *SelectedChoice,
_Inout_opt_ PBOOLEAN SelectedOption,
_In_opt_ PCWSTR SavedChoicesSettingName
)
{
PH_CHOICE_DIALOG_CONTEXT context;
memset(&context, 0, sizeof(PH_CHOICE_DIALOG_CONTEXT));
context.Title = Title;
context.Message = Message;
context.Choices = Choices;
context.NumberOfChoices = NumberOfChoices;
context.Option = Option;
context.Flags = Flags;
context.SelectedChoice = SelectedChoice;
context.SelectedOption = SelectedOption;
context.SavedChoicesSettingName = SavedChoicesSettingName;
return PhDialogBox(
PhInstanceHandle,
MAKEINTRESOURCE(IDD_CHOOSENEW),
PhCsForceNoParent ? NULL : ParentWindowHandle,
PhChooseNewPageDlgProc,
&context
) == IDOK;
}
================================================
FILE: SystemInformer/chproc.c
================================================
/*
* Copyright (c) 2022 Winsider Seminars & Solutions, Inc. All rights reserved.
*
* This file is part of System Informer.
*
* Authors:
*
* wj32 2010
* dmex 2016-2023
*
*/
#include
#include
#include
typedef struct _PH_CHOOSE_PROCESS_DIALOG_CONTEXT
{
PCWSTR Message;
HANDLE ProcessId;
PH_LAYOUT_MANAGER LayoutManager;
RECT MinimumSize;
HIMAGELIST ImageList;
HWND ListViewHandle;
} PH_CHOOSE_PROCESS_DIALOG_CONTEXT, *PPH_CHOOSE_PROCESS_DIALOG_CONTEXT;
INT_PTR CALLBACK PhpChooseProcessDlgProc(
_In_ HWND hwndDlg,
_In_ UINT uMsg,
_In_ WPARAM wParam,
_In_ LPARAM lParam
);
_Success_(return)
BOOLEAN PhShowChooseProcessDialog(
_In_ HWND ParentWindowHandle,
_In_ PCWSTR Message,
_Out_ PHANDLE ProcessId
)
{
PH_CHOOSE_PROCESS_DIALOG_CONTEXT context;
memset(&context, 0, sizeof(PH_CHOOSE_PROCESS_DIALOG_CONTEXT));
context.Message = Message;
context.ProcessId = NULL;
if (PhDialogBox(
PhInstanceHandle,
MAKEINTRESOURCE(IDD_CHOOSEPROCESS),
ParentWindowHandle,
PhpChooseProcessDlgProc,
&context
) == IDOK)
{
*ProcessId = context.ProcessId;
return TRUE;
}
else
{
return FALSE;
}
}
static VOID PhpRefreshProcessList(
_In_ HWND hwndDlg,
_In_ PPH_CHOOSE_PROCESS_DIALOG_CONTEXT Context
)
{
NTSTATUS status;
PVOID processes;
PSYSTEM_PROCESS_INFORMATION process;
if (!NT_SUCCESS(status = PhEnumProcesses(&processes)))
{
PhShowStatus(hwndDlg, L"Unable to enumerate processes", status, 0);
return;
}
ExtendedListView_SetRedraw(Context->ListViewHandle, FALSE);
ListView_DeleteAllItems(Context->ListViewHandle);
PhImageListRemoveAll(Context->ImageList);
process = PH_FIRST_PROCESS(processes);
do
{
LONG lvItemIndex;
PPH_STRING name;
HANDLE processHandle;
PPH_STRING fileName = NULL;
HICON icon = NULL;
WCHAR processIdString[PH_INT32_STR_LEN_1];
PPH_STRING userName = NULL;
LONG imageIndex = INT_MAX;
if (process->UniqueProcessId != SYSTEM_IDLE_PROCESS_ID)
name = PhCreateStringFromUnicodeString(&process->ImageName);
else
name = PhCreateStringFromUnicodeString(&SYSTEM_IDLE_PROCESS_NAME);
lvItemIndex = PhAddListViewItem(Context->ListViewHandle, MAXINT, name->Buffer, process->UniqueProcessId);
PhDereferenceObject(name);
if (NT_SUCCESS(PhOpenProcess(&processHandle, PROCESS_QUERY_LIMITED_INFORMATION, process->UniqueProcessId)))
{
HANDLE tokenHandle;
PH_TOKEN_USER tokenUser;
if (NT_SUCCESS(PhOpenProcessToken(processHandle, TOKEN_QUERY, &tokenHandle)))
{
if (NT_SUCCESS(PhGetTokenUser(tokenHandle, &tokenUser)))
{
userName = PhGetSidFullName(tokenUser.User.Sid, TRUE, NULL);
}
NtClose(tokenHandle);
}
NtClose(processHandle);
}
if (process->UniqueProcessId == SYSTEM_IDLE_PROCESS_ID && !userName)
{
PhSetReference(&userName, PhGetSidFullName((PSID)&PhSeLocalSystemSid, TRUE, NULL));
}
if (process->UniqueProcessId == SYSTEM_PROCESS_ID)
fileName = PhGetKernelFileName();
else if (PH_IS_REAL_PROCESS_ID(process->UniqueProcessId))
PhGetProcessImageFileNameByProcessId(process->UniqueProcessId, &fileName);
if (fileName)
PhMoveReference(&fileName, PhGetFileName(fileName));
// Icon
if (!PhIsNullOrEmptyString(fileName))
{
PhExtractIcon(PhGetString(fileName), &icon, NULL);
}
if (icon)
{
imageIndex = PhImageListAddIcon(Context->ImageList, icon);
PhSetListViewItemImageIndex(Context->ListViewHandle, lvItemIndex, imageIndex);
DestroyIcon(icon);
}
else
{
PhGetStockApplicationIcon(NULL, &icon);
imageIndex = PhImageListAddIcon(Context->ImageList, icon);
PhSetListViewItemImageIndex(Context->ListViewHandle, lvItemIndex, imageIndex);
}
// PID
PhPrintUInt32(processIdString, HandleToUlong(process->UniqueProcessId));
PhSetListViewSubItem(Context->ListViewHandle, lvItemIndex, 1, processIdString);
// User Name
PhSetListViewSubItem(Context->ListViewHandle, lvItemIndex, 2, PhGetString(userName));
if (userName) PhDereferenceObject(userName);
if (fileName) PhDereferenceObject(fileName);
} while (process = PH_NEXT_PROCESS(process));
PhFree(processes);
ExtendedListView_SortItems(Context->ListViewHandle);
ExtendedListView_SetRedraw(Context->ListViewHandle, TRUE);
}
static VOID PhpChooseProcessSetImagelist(
_Inout_ PPH_CHOOSE_PROCESS_DIALOG_CONTEXT context
)
{
LONG dpiValue;
dpiValue = PhGetWindowDpi(context->ListViewHandle);
if (context->ImageList)
{
PhImageListSetIconSize(
context->ImageList,
PhGetSystemMetrics(SM_CXSMICON, dpiValue),
PhGetSystemMetrics(SM_CYSMICON, dpiValue)
);
}
else
{
context->ImageList = PhImageListCreate(
PhGetSystemMetrics(SM_CXSMICON, dpiValue),
PhGetSystemMetrics(SM_CYSMICON, dpiValue),
ILC_MASK | ILC_COLOR32,
0, 40
);
}
ListView_SetImageList(context->ListViewHandle, context->ImageList, LVSIL_SMALL);
}
INT_PTR CALLBACK PhpChooseProcessDlgProc(
_In_ HWND hwndDlg,
_In_ UINT uMsg,
_In_ WPARAM wParam,
_In_ LPARAM lParam
)
{
PPH_CHOOSE_PROCESS_DIALOG_CONTEXT context = NULL;
if (uMsg == WM_INITDIALOG)
{
context = (PPH_CHOOSE_PROCESS_DIALOG_CONTEXT)lParam;
PhSetWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT, context);
}
else
{
context = PhGetWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT);
}
if (!context)
return FALSE;
switch (uMsg)
{
case WM_INITDIALOG:
{
HWND lvHandle;
PhSetApplicationWindowIcon(hwndDlg);
PhCenterWindow(hwndDlg, GetParent(hwndDlg));
PhSetDialogItemText(hwndDlg, IDC_MESSAGE, context->Message);
PhInitializeLayoutManager(&context->LayoutManager, hwndDlg);
PhAddLayoutItem(&context->LayoutManager, GetDlgItem(hwndDlg, IDC_MESSAGE), NULL, PH_ANCHOR_LEFT | PH_ANCHOR_TOP | PH_ANCHOR_RIGHT | PH_LAYOUT_FORCE_INVALIDATE);
PhAddLayoutItem(&context->LayoutManager, GetDlgItem(hwndDlg, IDC_LIST), NULL, PH_ANCHOR_ALL);
PhAddLayoutItem(&context->LayoutManager, GetDlgItem(hwndDlg, IDOK), NULL, PH_ANCHOR_RIGHT | PH_ANCHOR_BOTTOM);
PhAddLayoutItem(&context->LayoutManager, GetDlgItem(hwndDlg, IDCANCEL), NULL, PH_ANCHOR_RIGHT | PH_ANCHOR_BOTTOM);
PhAddLayoutItem(&context->LayoutManager, GetDlgItem(hwndDlg, IDC_REFRESH), NULL, PH_ANCHOR_BOTTOM | PH_ANCHOR_LEFT);
PhLayoutManagerLayout(&context->LayoutManager);
context->MinimumSize.left = 0;
context->MinimumSize.top = 0;
context->MinimumSize.right = 280;
context->MinimumSize.bottom = 170;
MapDialogRect(hwndDlg, &context->MinimumSize);
context->ListViewHandle = lvHandle = GetDlgItem(hwndDlg, IDC_LIST);
PhSetListViewStyle(lvHandle, FALSE, TRUE);
PhSetControlTheme(lvHandle, L"explorer");
PhAddListViewColumn(lvHandle, 0, 0, 0, LVCFMT_LEFT, 180, L"Name");
PhAddListViewColumn(lvHandle, 1, 1, 1, LVCFMT_LEFT, 60, L"PID");
PhAddListViewColumn(lvHandle, 2, 2, 2, LVCFMT_LEFT, 160, L"User name");
PhSetExtendedListView(lvHandle);
PhpChooseProcessSetImagelist(context);
PhpRefreshProcessList(hwndDlg, context);
EnableWindow(GetDlgItem(hwndDlg, IDOK), FALSE);
PhInitializeWindowTheme(hwndDlg, PhEnableThemeSupport);
}
break;
case WM_DESTROY:
{
PhDeleteLayoutManager(&context->LayoutManager);
if (context->ImageList)
{
PhImageListDestroy(context->ImageList);
context->ImageList = NULL;
}
PhRemoveWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT);
}
break;
case WM_DPICHANGED:
{
PhLayoutManagerUpdate(&context->LayoutManager, LOWORD(wParam));
PhLayoutManagerLayout(&context->LayoutManager);
PhpChooseProcessSetImagelist(context);
PhpRefreshProcessList(hwndDlg, context);
}
break;
case WM_COMMAND:
{
switch (GET_WM_COMMAND_ID(wParam, lParam))
{
case IDCANCEL:
{
EndDialog(hwndDlg, IDCANCEL);
}
break;
case IDOK:
{
if (ListView_GetSelectedCount(context->ListViewHandle) == 1)
{
context->ProcessId = (HANDLE)PhGetSelectedListViewItemParam(context->ListViewHandle);
EndDialog(hwndDlg, IDOK);
}
}
break;
case IDC_REFRESH:
{
PhpRefreshProcessList(hwndDlg, context);
}
break;
}
}
break;
case WM_NOTIFY:
{
LPNMHDR header = (LPNMHDR)lParam;
switch (header->code)
{
case LVN_ITEMCHANGED:
{
EnableWindow(GetDlgItem(hwndDlg, IDOK), ListView_GetSelectedCount(context->ListViewHandle) == 1);
}
break;
case NM_DBLCLK:
{
SendMessage(hwndDlg, WM_COMMAND, IDOK, 0);
}
break;
}
}
break;
case WM_SIZE:
{
PhLayoutManagerLayout(&context->LayoutManager);
}
break;
case WM_SIZING:
{
PhResizingMinimumSize((PRECT)lParam, wParam, context->MinimumSize.right, context->MinimumSize.bottom);
}
break;
case WM_CTLCOLORBTN:
return HANDLE_WM_CTLCOLORBTN(hwndDlg, wParam, lParam, PhWindowThemeControlColor);
case WM_CTLCOLORDLG:
return HANDLE_WM_CTLCOLORDLG(hwndDlg, wParam, lParam, PhWindowThemeControlColor);
case WM_CTLCOLORSTATIC:
return HANDLE_WM_CTLCOLORSTATIC(hwndDlg, wParam, lParam, PhWindowThemeControlColor);
}
return FALSE;
}
================================================
FILE: SystemInformer/colmgr.c
================================================
/*
* Copyright (c) 2022 Winsider Seminars & Solutions, Inc. All rights reserved.
*
* This file is part of System Informer.
*
* Authors:
*
* wj32 2011-2016
* dmex 2017-2023
*
*/
#include
#include
#include
#include
typedef struct _PH_CM_SORT_CONTEXT
{
PPH_PLUGIN_TREENEW_SORT_FUNCTION SortFunction;
ULONG SubId;
PVOID Context;
PPH_CM_POST_SORT_FUNCTION PostSortFunction;
PH_SORT_ORDER SortOrder;
} PH_CM_SORT_CONTEXT, *PPH_CM_SORT_CONTEXT;
VOID PhCmInitializeManager(
_Out_ PPH_CM_MANAGER Manager,
_In_ HWND Handle,
_In_ ULONG MinId,
_In_ PPH_CM_POST_SORT_FUNCTION PostSortFunction
)
{
Manager->Handle = Handle;
Manager->MinId = MinId;
Manager->NextId = MinId;
Manager->PostSortFunction = PostSortFunction;
InitializeListHead(&Manager->ColumnListHead);
Manager->NotifyList = NULL;
}
VOID PhCmDeleteManager(
_In_ PPH_CM_MANAGER Manager
)
{
PLIST_ENTRY listEntry;
PPH_CM_COLUMN column;
listEntry = Manager->ColumnListHead.Flink;
while (listEntry != &Manager->ColumnListHead)
{
column = CONTAINING_RECORD(listEntry, PH_CM_COLUMN, ListEntry);
listEntry = listEntry->Flink;
PhFree(column);
}
if (Manager->NotifyList)
PhDereferenceObject(Manager->NotifyList);
}
PPH_CM_COLUMN PhCmCreateColumn(
_Inout_ PPH_CM_MANAGER Manager,
_In_ PPH_TREENEW_COLUMN Column,
_In_ PPH_PLUGIN Plugin,
_In_ ULONG SubId,
_In_opt_ PVOID Context,
_In_opt_ PVOID SortFunction
)
{
PPH_CM_COLUMN column;
PH_TREENEW_COLUMN tnColumn;
column = PhAllocateZero(sizeof(PH_CM_COLUMN));
column->Id = Manager->NextId++;
column->Plugin = Plugin;
column->SubId = SubId;
column->Context = Context;
column->SortFunction = SortFunction;
InsertTailList(&Manager->ColumnListHead, &column->ListEntry);
memset(&tnColumn, 0, sizeof(PH_TREENEW_COLUMN));
tnColumn.Id = column->Id;
tnColumn.Context = column;
tnColumn.Visible = Column->Visible;
tnColumn.CustomDraw = Column->CustomDraw;
tnColumn.SortDescending = Column->SortDescending;
tnColumn.Text = Column->Text;
tnColumn.Width = Column->Width;
tnColumn.Alignment = Column->Alignment;
tnColumn.DisplayIndex = Column->Visible ? Column->DisplayIndex : ULONG_MAX;
tnColumn.TextFlags = Column->TextFlags;
TreeNew_AddColumn(Manager->Handle, &tnColumn);
return column;
}
PPH_CM_COLUMN PhCmFindColumn(
_In_ PPH_CM_MANAGER Manager,
_In_ PCPH_STRINGREF PluginName,
_In_ ULONG SubId
)
{
PLIST_ENTRY listEntry;
PPH_CM_COLUMN column;
listEntry = Manager->ColumnListHead.Flink;
while (listEntry != &Manager->ColumnListHead)
{
column = CONTAINING_RECORD(listEntry, PH_CM_COLUMN, ListEntry);
if (column->SubId == SubId && PhEqualStringRef(PluginName, &column->Plugin->AppContext.AppName, FALSE))
return column;
listEntry = listEntry->Flink;
}
return NULL;
}
VOID PhCmSetNotifyPlugin(
_In_ PPH_CM_MANAGER Manager,
_In_ PPH_PLUGIN Plugin
)
{
if (!Manager->NotifyList)
{
Manager->NotifyList = PhCreateList(8);
}
else
{
if (PhFindItemList(Manager->NotifyList, Plugin) != ULONG_MAX)
return;
}
PhAddItemList(Manager->NotifyList, Plugin);
}
BOOLEAN PhCmForwardMessage(
_In_ HWND WindowHandle,
_In_ PH_TREENEW_MESSAGE Message,
_In_ PVOID Parameter1,
_In_ PVOID Parameter2,
_In_ PPH_CM_MANAGER Manager
)
{
PH_PLUGIN_TREENEW_MESSAGE pluginMessage;
PPH_PLUGIN plugin;
switch (Message)
{
case TreeNewDestroying:
{
return FALSE;
}
break;
case TreeNewGetCellText:
{
PPH_TREENEW_GET_CELL_TEXT getCellText = Parameter1;
PH_TREENEW_COLUMN tnColumn;
PPH_CM_COLUMN column;
if (getCellText->Id < Manager->MinId)
return FALSE;
if (!TreeNew_GetColumn(WindowHandle, getCellText->Id, &tnColumn))
return FALSE;
column = tnColumn.Context;
pluginMessage.SubId = column->SubId;
pluginMessage.Context = column->Context;
plugin = column->Plugin;
}
break;
case TreeNewCustomDraw:
{
PPH_TREENEW_CUSTOM_DRAW customDraw = Parameter1;
PPH_CM_COLUMN column;
if (customDraw->Column->Id < Manager->MinId)
return FALSE;
column = customDraw->Column->Context;
pluginMessage.SubId = column->SubId;
pluginMessage.Context = column->Context;
plugin = column->Plugin;
}
break;
case TreeNewColumnResized:
{
PPH_TREENEW_COLUMN tlColumn = Parameter1;
PPH_CM_COLUMN column;
if (tlColumn->Id < Manager->MinId)
return FALSE;
column = tlColumn->Context;
pluginMessage.SubId = column->SubId;
pluginMessage.Context = column->Context;
plugin = column->Plugin;
}
break;
case TreeNewGetHeaderText:
{
PPH_TREENEW_GET_HEADER_TEXT getHeaderText = Parameter1;
PPH_TREENEW_COLUMN tlColumn = getHeaderText->Column;
PPH_CM_COLUMN column;
if (tlColumn->Id < Manager->MinId)
return FALSE;
column = tlColumn->Context;
pluginMessage.SubId = column->SubId;
pluginMessage.Context = column->Context;
plugin = column->Plugin;
}
break;
default:
{
// Some plugins want to be notified about all messages.
if (Manager->NotifyList)
{
for (ULONG i = 0; i < Manager->NotifyList->Count; i++)
{
plugin = Manager->NotifyList->Items[i];
pluginMessage.TreeNewHandle = WindowHandle;
pluginMessage.Message = Message;
pluginMessage.Parameter1 = Parameter1;
pluginMessage.Parameter2 = Parameter2;
pluginMessage.SubId = 0;
pluginMessage.Context = NULL;
PhInvokeCallback(PhGetPluginCallback(plugin, PluginCallbackTreeNewMessage), &pluginMessage);
}
}
}
return FALSE;
}
pluginMessage.TreeNewHandle = WindowHandle;
pluginMessage.Message = Message;
pluginMessage.Parameter1 = Parameter1;
pluginMessage.Parameter2 = Parameter2;
PhInvokeCallback(PhGetPluginCallback(plugin, PluginCallbackTreeNewMessage), &pluginMessage);
return TRUE;
}
static int __cdecl PhCmpSortFunction(
_In_ void *context,
_In_ const void *elem1,
_In_ const void *elem2
)
{
PPH_CM_SORT_CONTEXT sortContext = context;
PVOID node1 = *(PVOID *)elem1;
PVOID node2 = *(PVOID *)elem2;
LONG result;
result = sortContext->SortFunction(node1, node2, sortContext->SubId, sortContext->SortOrder, sortContext->Context);
return sortContext->PostSortFunction(result, node1, node2, sortContext->SortOrder);
}
BOOLEAN PhCmForwardSort(
_In_ PPH_TREENEW_NODE *Nodes,
_In_ ULONG NumberOfNodes,
_In_ ULONG SortColumn,
_In_ PH_SORT_ORDER SortOrder,
_In_ PPH_CM_MANAGER Manager
)
{
PH_TREENEW_COLUMN tnColumn;
PPH_CM_COLUMN column;
PH_CM_SORT_CONTEXT sortContext;
if (SortColumn < Manager->MinId)
return FALSE;
if (!Manager->Handle)
return TRUE;
if (!TreeNew_GetColumn(Manager->Handle, SortColumn, &tnColumn))
return TRUE;
column = tnColumn.Context;
if (!column->SortFunction)
return TRUE;
sortContext.SortFunction = column->SortFunction;
sortContext.SubId = column->SubId;
sortContext.Context = column->Context;
sortContext.PostSortFunction = Manager->PostSortFunction;
sortContext.SortOrder = SortOrder;
qsort_s(Nodes, NumberOfNodes, sizeof(PVOID), PhCmpSortFunction, &sortContext);
return TRUE;
}
BOOLEAN PhCmLoadSettings(
_In_ HWND TreeNewHandle,
_In_ PCPH_STRINGREF Settings
)
{
return PhCmLoadSettingsEx(TreeNewHandle, NULL, 0, Settings, NULL);
}
BOOLEAN PhCmLoadSettingsEx(
_In_ HWND TreeNewHandle,
_In_opt_ PPH_CM_MANAGER Manager,
_In_ ULONG Flags,
_In_ PCPH_STRINGREF Settings,
_In_opt_ PCPH_STRINGREF SortSettings
)
{
BOOLEAN result = FALSE;
PH_STRINGREF scalePart;
PH_STRINGREF columnPart;
PH_STRINGREF remainingColumnPart;
PH_STRINGREF valuePart;
PH_STRINGREF subPart;
ULONG64 integer;
ULONG scale;
ULONG total;
BOOLEAN hasFixedColumn;
ULONG count;
ULONG i;
PPH_HASHTABLE columnHashtable;
PH_HASHTABLE_ENUM_CONTEXT enumContext;
PPH_KEY_VALUE_PAIR pair;
ULONG orderArray[PH_CM_ORDER_LIMIT];
ULONG maxOrder;
LONG dpiValue;
dpiValue = PhGetWindowDpi(TreeNewHandle);
if (Settings->Length != 0)
{
columnHashtable = PhCreateSimpleHashtable(20);
remainingColumnPart = *Settings;
if (remainingColumnPart.Length != 0 && remainingColumnPart.Buffer[0] == L'@')
{
PhSkipStringRef(&remainingColumnPart, sizeof(WCHAR));
PhSplitStringRefAtChar(&remainingColumnPart, L'|', &scalePart, &remainingColumnPart);
if (scalePart.Length == 0 || !PhStringToUInt64(&scalePart, 10, &integer))
goto CleanupExit;
scale = (ULONG)integer;
}
else
{
scale = USER_DEFAULT_SCREEN_DPI;
}
while (remainingColumnPart.Length != 0)
{
PPH_TREENEW_COLUMN column;
ULONG id;
ULONG displayIndex;
LONG width;
PhSplitStringRefAtChar(&remainingColumnPart, L'|', &columnPart, &remainingColumnPart);
if (columnPart.Length != 0)
{
// Id
PhSplitStringRefAtChar(&columnPart, L',', &valuePart, &columnPart);
if (valuePart.Length == 0)
goto CleanupExit;
if (valuePart.Buffer[0] == L'+')
{
PH_STRINGREF pluginName;
ULONG subId;
PPH_CM_COLUMN cmColumn;
// This is a plugin-owned column.
if (!Manager)
continue;
if (!PhEmParseCompoundId(&valuePart, &pluginName, &subId))
continue;
cmColumn = PhCmFindColumn(Manager, &pluginName, subId);
if (!cmColumn)
continue; // can't find the column, skip this part
id = cmColumn->Id;
}
else
{
if (!PhStringToUInt64(&valuePart, 10, &integer))
goto CleanupExit;
id = (ULONG)integer;
}
// Display Index
PhSplitStringRefAtChar(&columnPart, L',', &valuePart, &columnPart);
if (!(Flags & PH_CM_COLUMN_WIDTHS_ONLY))
{
if (valuePart.Length == 0 || !PhStringToUInt64(&valuePart, 10, &integer))
goto CleanupExit;
displayIndex = (ULONG)integer;
}
else
{
if (valuePart.Length != 0)
goto CleanupExit;
displayIndex = ULONG_MAX;
}
// Width
if (columnPart.Length == 0 || !PhStringToUInt64(&columnPart, 10, &integer))
goto CleanupExit;
width = (LONG)integer;
if (scale != dpiValue && scale != 0)
width = PhMultiplyDivideSigned(width, dpiValue, scale);
column = PhAllocateZero(sizeof(PH_TREENEW_COLUMN));
column->Id = id;
column->DisplayIndex = displayIndex;
column->Width = width;
PhAddItemSimpleHashtable(columnHashtable, UlongToPtr(column->Id), column);
}
}
TreeNew_SetRedraw(TreeNewHandle, FALSE);
// Set visibility and width.
i = 0;
count = 0;
total = TreeNew_GetColumnCount(TreeNewHandle);
hasFixedColumn = !!TreeNew_GetFixedColumn(TreeNewHandle);
memset(orderArray, 0, sizeof(orderArray));
maxOrder = 0;
while (count < total)
{
PH_TREENEW_COLUMN setColumn;
PPH_TREENEW_COLUMN *columnPtr;
if (TreeNew_GetColumn(TreeNewHandle, i, &setColumn))
{
columnPtr = (PPH_TREENEW_COLUMN *)PhFindItemSimpleHashtable(columnHashtable, UlongToPtr(i));
if (!(Flags & PH_CM_COLUMN_WIDTHS_ONLY))
{
if (columnPtr)
{
setColumn.Visible = TRUE;
setColumn.Width = (*columnPtr)->Width;
TreeNew_SetColumn(TreeNewHandle, TN_COLUMN_FLAG_VISIBLE | TN_COLUMN_WIDTH, &setColumn);
if (!setColumn.Fixed)
{
// For compatibility reasons, normal columns have their display indices stored
// one higher than usual (so they start from 1, not 0). Fix that here.
if (hasFixedColumn && (*columnPtr)->DisplayIndex != 0)
(*columnPtr)->DisplayIndex--;
if ((*columnPtr)->DisplayIndex < PH_CM_ORDER_LIMIT)
{
orderArray[(*columnPtr)->DisplayIndex] = i;
if (maxOrder < (*columnPtr)->DisplayIndex + 1)
maxOrder = (*columnPtr)->DisplayIndex + 1;
}
}
}
else if (!setColumn.Fixed) // never hide the fixed column
{
setColumn.Visible = FALSE;
TreeNew_SetColumn(TreeNewHandle, TN_COLUMN_FLAG_VISIBLE, &setColumn);
}
}
else
{
if (columnPtr)
{
setColumn.Width = (*columnPtr)->Width;
TreeNew_SetColumn(TreeNewHandle, TN_COLUMN_WIDTH, &setColumn);
}
}
count++;
}
i++;
}
if (!(Flags & PH_CM_COLUMN_WIDTHS_ONLY))
{
// Set the order array.
TreeNew_SetColumnOrderArray(TreeNewHandle, maxOrder, orderArray);
}
TreeNew_SetRedraw(TreeNewHandle, TRUE);
result = TRUE;
CleanupExit:
PhBeginEnumHashtable(columnHashtable, &enumContext);
while (pair = PhNextEnumHashtable(&enumContext))
PhFree(pair->Value);
PhDereferenceObject(columnHashtable);
}
// Load sort settings.
if (SortSettings && SortSettings->Length != 0)
{
PhSplitStringRefAtChar(SortSettings, L',', &valuePart, &subPart);
if (valuePart.Length != 0 && subPart.Length != 0)
{
ULONG sortColumn;
PH_SORT_ORDER sortOrder;
sortColumn = ULONG_MAX;
if (valuePart.Buffer[0] == L'+')
{
PH_STRINGREF pluginName;
ULONG subId;
PPH_CM_COLUMN cmColumn;
if (
Manager &&
PhEmParseCompoundId(&valuePart, &pluginName, &subId) &&
(cmColumn = PhCmFindColumn(Manager, &pluginName, subId))
)
{
sortColumn = cmColumn->Id;
}
}
else
{
PhStringToUInt64(&valuePart, 10, &integer);
sortColumn = (ULONG)integer;
}
PhStringToUInt64(&subPart, 10, &integer);
sortOrder = (PH_SORT_ORDER)integer;
if (sortColumn != ULONG_MAX && sortOrder <= DescendingSortOrder)
{
TreeNew_SetSort(TreeNewHandle, sortColumn, sortOrder);
}
}
}
return result;
}
PPH_STRING PhCmSaveSettings(
_In_ HWND TreeNewHandle
)
{
return PhCmSaveSettingsEx(TreeNewHandle, NULL, 0, NULL);
}
PPH_STRING PhCmSaveSettingsEx(
_In_ HWND TreeNewHandle,
_In_opt_ PPH_CM_MANAGER Manager,
_In_ ULONG Flags,
_Out_opt_ PPH_STRING *SortSettings
)
{
PH_STRING_BUILDER stringBuilder;
ULONG i = 0;
ULONG count = 0;
ULONG total;
ULONG increment;
PH_TREENEW_COLUMN column;
LONG dpiValue;
dpiValue = PhGetWindowDpi(TreeNewHandle);
total = TreeNew_GetColumnCount(TreeNewHandle);
if (TreeNew_GetFixedColumn(TreeNewHandle))
increment = 1; // the first normal column should have a display index that starts with 1, for compatibility
else
increment = 0;
PhInitializeStringBuilder(&stringBuilder, 100);
{
PH_FORMAT format[3];
SIZE_T returnLength;
WCHAR buffer[PH_INT64_STR_LEN_1];
// @%lu|
PhInitFormatC(&format[0], L'@');
PhInitFormatU(&format[1], dpiValue);
PhInitFormatC(&format[2], L'|');
if (PhFormatToBuffer(format, RTL_NUMBER_OF(format), buffer, sizeof(buffer), &returnLength))
{
PhAppendStringBuilderEx(&stringBuilder, buffer, returnLength - sizeof(UNICODE_NULL));
}
else
{
PhAppendFormatStringBuilder(&stringBuilder, L"@%lu|", dpiValue);
}
}
while (count < total)
{
if (TreeNew_GetColumn(TreeNewHandle, i, &column))
{
if (!(Flags & PH_CM_COLUMN_WIDTHS_ONLY))
{
if (column.Visible)
{
if (!Manager || i < Manager->MinId)
{
PH_FORMAT format[6];
SIZE_T returnLength;
WCHAR buffer[PH_INT64_STR_LEN_1];
// %lu,%lu,%ld|
PhInitFormatU(&format[0], i);
PhInitFormatC(&format[1], L',');
PhInitFormatU(&format[2], column.Fixed ? 0 : column.DisplayIndex + increment);
PhInitFormatC(&format[3], L',');
PhInitFormatU(&format[4], column.Width);
PhInitFormatC(&format[5], L'|');
if (PhFormatToBuffer(format, RTL_NUMBER_OF(format), buffer, sizeof(buffer), &returnLength))
{
PhAppendStringBuilderEx(&stringBuilder, buffer, returnLength - sizeof(UNICODE_NULL));
}
else
{
PhAppendFormatStringBuilder(
&stringBuilder,
L"%lu,%lu,%ld|",
i,
column.Fixed ? 0 : column.DisplayIndex + increment,
column.Width
);
}
}
else
{
PPH_CM_COLUMN cmColumn = column.Context;
PH_FORMAT format[9];
SIZE_T returnLength;
WCHAR buffer[PH_INT64_STR_LEN_1];
// +%s+%lu,%lu,%ld|
PhInitFormatC(&format[0], L'+');
PhInitFormatSR(&format[1], cmColumn->Plugin->Name);
PhInitFormatC(&format[2], L'+');
PhInitFormatU(&format[3], cmColumn->SubId);
PhInitFormatC(&format[4], L',');
PhInitFormatU(&format[5], column.DisplayIndex + increment);
PhInitFormatC(&format[6], L',');
PhInitFormatD(&format[7], column.Width);
PhInitFormatC(&format[8], L'|');
if (PhFormatToBuffer(format, RTL_NUMBER_OF(format), buffer, sizeof(buffer), &returnLength))
{
PhAppendStringBuilderEx(&stringBuilder, buffer, returnLength - sizeof(UNICODE_NULL));
}
else
{
PhAppendFormatStringBuilder(
&stringBuilder,
L"+%s+%lu,%lu,%ld|",
cmColumn->Plugin->Name.Buffer,
cmColumn->SubId,
column.DisplayIndex + increment,
column.Width
);
}
}
}
}
else
{
if (!Manager || i < Manager->MinId)
{
PhAppendFormatStringBuilder(
&stringBuilder,
L"%lu,,%ld|",
i,
column.Width
);
}
else
{
PPH_CM_COLUMN cmColumn;
cmColumn = column.Context;
PhAppendFormatStringBuilder(
&stringBuilder,
L"+%s+%lu,,%ld|",
cmColumn->Plugin->Name.Buffer,
cmColumn->SubId,
column.Width
);
}
}
count++;
}
i++;
}
if (stringBuilder.String->Length != 0)
PhRemoveEndStringBuilder(&stringBuilder, 1);
if (SortSettings)
{
ULONG sortColumn;
PH_SORT_ORDER sortOrder;
if (TreeNew_GetSort(TreeNewHandle, &sortColumn, &sortOrder))
{
if (sortOrder != NoSortOrder)
{
if (!Manager || sortColumn < Manager->MinId)
{
PH_FORMAT format[3];
// %lu,%lu
PhInitFormatU(&format[0], sortColumn);
PhInitFormatC(&format[1], L',');
PhInitFormatU(&format[2], sortOrder);
*SortSettings = PhFormat(format, RTL_NUMBER_OF(format), 32);
}
else
{
if (TreeNew_GetColumn(TreeNewHandle, sortColumn, &column))
{
PPH_CM_COLUMN cmColumn;
PH_FORMAT format[6];
cmColumn = column.Context;
// +%s+%lu,%lu
PhInitFormatC(&format[0], L'+');
PhInitFormatSR(&format[1], cmColumn->Plugin->Name);
PhInitFormatC(&format[2], L'+');
PhInitFormatU(&format[3], cmColumn->SubId);
PhInitFormatC(&format[4], L',');
PhInitFormatU(&format[5], sortOrder);
*SortSettings = PhFormat(format, RTL_NUMBER_OF(format), 64);
}
else
{
*SortSettings = PhReferenceEmptyString();
}
}
}
else
{
*SortSettings = PhCreateString(L"0,0");
}
}
else
{
*SortSettings = PhReferenceEmptyString();
}
}
return PhFinalStringBuilderString(&stringBuilder);
}
================================================
FILE: SystemInformer/colsetmgr.c
================================================
/*
* Copyright (c) 2022 Winsider Seminars & Solutions, Inc. All rights reserved.
*
* This file is part of System Informer.
*
* Authors:
*
* dmex 2017-2022
*
*/
#include
#include
#include
PPH_LIST PhInitializeColumnSetList(
_In_ PCWSTR SettingName
)
{
PPH_LIST columnSetList;
PPH_STRING settingsString;
ULONG64 count;
ULONG64 index;
PH_STRINGREF remaining;
PH_STRINGREF part;
columnSetList = PhCreateList(10);
settingsString = PhaGetStringSetting(SettingName);
remaining = settingsString->sr;
if (remaining.Length == 0)
goto CleanupExit;
if (!PhSplitStringRefAtChar(&remaining, L'-', &part, &remaining))
goto CleanupExit;
if (!PhStringToInteger64(&part, 10, &count))
goto CleanupExit;
for (index = 0; index < count; index++)
{
PH_STRINGREF columnSetNamePart;
PH_STRINGREF columnSetSettingPart;
PH_STRINGREF columnSetSortPart;
if (remaining.Length == 0)
break;
PhSplitStringRefAtChar(&remaining, L'-', &columnSetNamePart, &remaining);
PhSplitStringRefAtChar(&remaining, L'-', &columnSetSettingPart, &remaining);
PhSplitStringRefAtChar(&remaining, L'-', &columnSetSortPart, &remaining);
{
PPH_COLUMN_SET_ENTRY entry;
entry = PhAllocate(sizeof(PH_COLUMN_SET_ENTRY));
entry->Name = PhCreateString2(&columnSetNamePart);
entry->Setting = PhCreateString2(&columnSetSettingPart);
entry->Sorting = PhCreateString2(&columnSetSortPart);
PhAddItemList(columnSetList, entry);
}
}
CleanupExit:
return columnSetList;
}
VOID PhSaveSettingsColumnList(
_In_ PCWSTR SettingName,
_In_ PPH_LIST ColumnSetList
)
{
ULONG index;
PPH_STRING settingsString;
PH_STRING_BUILDER stringBuilder;
PhInitializeStringBuilder(&stringBuilder, 100);
PhAppendFormatStringBuilder(
&stringBuilder,
L"%lu-",
ColumnSetList->Count
);
for (index = 0; index < ColumnSetList->Count; index++)
{
PPH_COLUMN_SET_ENTRY entry = ColumnSetList->Items[index];
if (PhIsNullOrEmptyString(entry->Name))
continue;
PhAppendFormatStringBuilder(
&stringBuilder,
L"%s-%s-%s-",
PhGetStringOrEmpty(entry->Name),
PhGetStringOrEmpty(entry->Setting),
PhGetStringOrEmpty(entry->Sorting)
);
}
if (stringBuilder.String->Length != 0)
PhRemoveEndStringBuilder(&stringBuilder, 1);
settingsString = PH_AUTO(PhFinalStringBuilderString(&stringBuilder));
PhSetStringSetting2(SettingName, &settingsString->sr);
}
VOID PhDeleteColumnSetList(
_In_ PPH_LIST ColumnSetList
)
{
for (ULONG i = 0; i < ColumnSetList->Count; i++)
{
PPH_COLUMN_SET_ENTRY entry = ColumnSetList->Items[i];
//PhRemoveItemList(ColumnSetList, PhFindItemList(ColumnSetList, entry));
PhClearReference(&entry->Name);
PhClearReference(&entry->Setting);
PhClearReference(&entry->Sorting);
PhFree(entry);
}
PhDereferenceObject(ColumnSetList);
}
_Success_(return)
BOOLEAN PhLoadSettingsColumnSet(
_In_ PCWSTR SettingName,
_In_ PPH_STRING ColumnSetName,
_Out_ PPH_STRING *TreeListSettings,
_Out_ PPH_STRING *TreeSortSettings
)
{
PPH_STRING treeSettings = NULL;
PPH_STRING sortSettings = NULL;
PPH_STRING settingsString;
ULONG64 count;
ULONG64 index;
PH_STRINGREF remaining;
PH_STRINGREF part;
settingsString = PhaGetStringSetting(SettingName);
remaining = settingsString->sr;
if (remaining.Length == 0)
return FALSE;
if (!PhSplitStringRefAtChar(&remaining, L'-', &part, &remaining))
return FALSE;
if (!PhStringToInteger64(&part, 10, &count))
return FALSE;
for (index = 0; index < count; index++)
{
PH_STRINGREF columnSetNamePart;
PH_STRINGREF columnSetSettingPart;
PH_STRINGREF columnSetSortPart;
if (remaining.Length == 0)
break;
PhSplitStringRefAtChar(&remaining, L'-', &columnSetNamePart, &remaining);
PhSplitStringRefAtChar(&remaining, L'-', &columnSetSettingPart, &remaining);
PhSplitStringRefAtChar(&remaining, L'-', &columnSetSortPart, &remaining);
if (PhEqualStringRef(&columnSetNamePart, &ColumnSetName->sr, FALSE))
{
treeSettings = PhCreateString2(&columnSetSettingPart);
sortSettings = PhCreateString2(&columnSetSortPart);
break;
}
}
if (!PhIsNullOrEmptyString(treeSettings) && !PhIsNullOrEmptyString(sortSettings))
{
*TreeListSettings = treeSettings;
*TreeSortSettings = sortSettings;
return TRUE;
}
else
{
if (treeSettings)
PhDereferenceObject(treeSettings);
if (sortSettings)
PhDereferenceObject(sortSettings);
return FALSE;
}
}
VOID PhSaveSettingsColumnSet(
_In_ PCWSTR SettingName,
_In_ PPH_STRING ColumnSetName,
_In_ PPH_STRING TreeListSettings,
_In_ PPH_STRING TreeSortSettings
)
{
ULONG index;
BOOLEAN found = FALSE;
PPH_LIST columnSetList;
columnSetList = PhInitializeColumnSetList(SettingName);
for (index = 0; index < columnSetList->Count; index++)
{
PPH_COLUMN_SET_ENTRY entry = columnSetList->Items[index];
if (PhEqualString(entry->Name, ColumnSetName, FALSE))
{
PhReferenceObject(TreeListSettings);
PhReferenceObject(TreeSortSettings);
PhMoveReference(&entry->Setting, TreeListSettings);
PhMoveReference(&entry->Sorting, TreeSortSettings);
found = TRUE;
break;
}
}
if (!found)
{
PPH_COLUMN_SET_ENTRY entry;
PhReferenceObject(ColumnSetName);
PhReferenceObject(TreeListSettings);
PhReferenceObject(TreeSortSettings);
entry = PhAllocate(sizeof(PH_COLUMN_SET_ENTRY));
entry->Name = ColumnSetName;
entry->Setting = TreeListSettings;
entry->Sorting = TreeSortSettings;
PhAddItemList(columnSetList, entry);
}
PhSaveSettingsColumnList(SettingName, columnSetList);
PhDeleteColumnSetList(columnSetList);
}
// Column Set Editor Dialog
typedef struct _PH_COLUMNSET_DIALOG_CONTEXT
{
HWND DialogHandle;
HWND ListViewHandle;
HWND RenameButtonHandle;
HWND MoveUpButtonHandle;
HWND MoveDownButtonHandle;
HWND RemoveButtonHandle;
PPH_STRING SettingName;
PPH_LIST ColumnSetList;
BOOLEAN LabelEditActive;
} PH_COLUMNSET_DIALOG_CONTEXT, *PPH_COLUMNSET_DIALOG_CONTEXT;
INT_PTR CALLBACK PhpColumnSetEditorDlgProc(
_In_ HWND hwndDlg,
_In_ UINT uMsg,
_In_ WPARAM wParam,
_In_ LPARAM lParam
);
VOID PhShowColumnSetEditorDialog(
_In_ HWND ParentWindowHandle,
_In_ PCWSTR SettingName
)
{
PhDialogBox(
PhInstanceHandle,
MAKEINTRESOURCE(IDD_COLUMNSETS),
ParentWindowHandle,
PhpColumnSetEditorDlgProc,
(PVOID)SettingName
);
}
VOID PhpMoveListViewItem(
_In_ HWND ListViewHandle,
_In_ LONG ItemIndex1,
_In_ LONG ItemIndex2
)
{
LVITEM item1;
LVITEM item2;
WCHAR buffer1[MAX_PATH];
WCHAR buffer2[MAX_PATH];
item1.mask = LVIF_TEXT | LVIF_PARAM | LVIF_STATE;
item1.stateMask = (UINT)-1;
item1.iItem = ItemIndex1;
item1.iSubItem = 0;
item1.cchTextMax = sizeof(buffer1);
item1.pszText = buffer1;
item2.mask = LVIF_TEXT | LVIF_PARAM | LVIF_STATE;
item2.stateMask = (UINT)-1;
item2.iItem = ItemIndex2;
item2.iSubItem = 0;
item2.cchTextMax = sizeof(buffer2);
item2.pszText = buffer2;
if (
ListView_GetItem(ListViewHandle, &item1) &&
ListView_GetItem(ListViewHandle, &item2)
)
{
item1.iItem = ItemIndex2;
item2.iItem = ItemIndex1;
ListView_SetItem(ListViewHandle, &item1);
ListView_SetItem(ListViewHandle, &item2);
}
}
VOID PhpMoveSelectedListViewItemUp(
_In_ HWND ListViewHandle
)
{
LONG lvItemIndex = PhFindListViewItemByFlags(ListViewHandle, INT_ERROR, LVNI_SELECTED);
if (lvItemIndex != INT_ERROR)
{
PhpMoveListViewItem(ListViewHandle, lvItemIndex, lvItemIndex - 1);
}
SetFocus(ListViewHandle);
ListView_SetItemState(ListViewHandle, lvItemIndex - 1, LVNI_SELECTED, LVNI_SELECTED);
//ListView_EnsureVisible(ListViewHandle, lvItemIndex - 1, FALSE);
}
VOID PhpMoveSelectedListViewItemDown(
_In_ HWND ListViewHandle
)
{
LONG lvItemIndex = PhFindListViewItemByFlags(ListViewHandle, INT_ERROR, LVNI_SELECTED);
if (lvItemIndex != INT_ERROR)
{
PhpMoveListViewItem(ListViewHandle, lvItemIndex, lvItemIndex + 1);
}
SetFocus(ListViewHandle);
ListView_SetItemState(ListViewHandle, lvItemIndex + 1, LVNI_SELECTED, LVNI_SELECTED);
//ListView_EnsureVisible(ListViewHandle, lvItemIndex + 1, FALSE);
}
INT_PTR CALLBACK PhpColumnSetEditorDlgProc(
_In_ HWND hwndDlg,
_In_ UINT uMsg,
_In_ WPARAM wParam,
_In_ LPARAM lParam
)
{
PPH_COLUMNSET_DIALOG_CONTEXT context;
if (uMsg == WM_INITDIALOG)
{
context = PhAllocateZero(sizeof(PH_COLUMNSET_DIALOG_CONTEXT));
context->SettingName = PhCreateString((PCWSTR)lParam);
PhSetWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT, context);
}
else
{
context = PhGetWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT);
}
if (!context)
return FALSE;
switch (uMsg)
{
case WM_INITDIALOG:
{
context->DialogHandle = hwndDlg;
context->ListViewHandle = GetDlgItem(hwndDlg, IDC_COLUMNSETLIST);
context->RenameButtonHandle = GetDlgItem(hwndDlg, IDC_RENAME);
context->MoveUpButtonHandle = GetDlgItem(hwndDlg, IDC_MOVEUP);
context->MoveDownButtonHandle = GetDlgItem(hwndDlg, IDC_MOVEDOWN);
context->RemoveButtonHandle = GetDlgItem(hwndDlg, IDC_REMOVE);
PhCenterWindow(hwndDlg, GetParent(hwndDlg));
PhSetListViewStyle(context->ListViewHandle, FALSE, TRUE);
PhSetControlTheme(context->ListViewHandle, L"explorer");
PhAddListViewColumn(context->ListViewHandle, 0, 0, 0, LVCFMT_LEFT, 250, L"Name");
PhSetExtendedListView(context->ListViewHandle);
context->ColumnSetList = PhInitializeColumnSetList(PhGetString(context->SettingName));
for (ULONG i = 0; i < context->ColumnSetList->Count; i++)
{
PPH_COLUMN_SET_ENTRY entry = context->ColumnSetList->Items[i];
PhAddListViewItem(context->ListViewHandle, MAXINT, entry->Name->Buffer, entry);
}
Button_Enable(context->RenameButtonHandle, FALSE);
Button_Enable(context->MoveUpButtonHandle, FALSE);
Button_Enable(context->MoveDownButtonHandle, FALSE);
Button_Enable(context->RemoveButtonHandle, FALSE);
PhInitializeWindowTheme(hwndDlg, PhEnableThemeSupport);
}
break;
case WM_DESTROY:
{
PhRemoveWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT);
PhDeleteColumnSetList(context->ColumnSetList);
PhFree(context);
}
break;
case WM_COMMAND:
{
switch (GET_WM_COMMAND_ID(wParam, lParam))
{
case IDCANCEL:
EndDialog(hwndDlg, IDCANCEL);
break;
case IDOK:
{
if (context->LabelEditActive)
break;
PhSaveSettingsColumnList(PhGetString(context->SettingName), context->ColumnSetList);
EndDialog(hwndDlg, IDOK);
}
break;
case IDC_RENAME:
{
LONG lvItemIndex = PhFindListViewItemByFlags(context->ListViewHandle, INT_ERROR, LVNI_SELECTED);
if (lvItemIndex != INT_ERROR)
{
SetFocus(context->ListViewHandle);
ListView_EditLabel(context->ListViewHandle, lvItemIndex);
}
}
break;
case IDC_MOVEUP:
{
LONG lvItemIndex;
PPH_COLUMN_SET_ENTRY entry;
ULONG index;
PhpMoveSelectedListViewItemUp(context->ListViewHandle);
lvItemIndex = PhFindListViewItemByFlags(context->ListViewHandle, INT_ERROR, LVNI_SELECTED);
if (lvItemIndex != INT_ERROR && PhGetListViewItemParam(context->ListViewHandle, lvItemIndex, (PVOID *)&entry))
{
index = PhFindItemList(context->ColumnSetList, entry);
if (index != ULONG_MAX)
{
PhRemoveItemList(context->ColumnSetList, index);
PhInsertItemList(context->ColumnSetList, lvItemIndex, entry);
}
}
}
break;
case IDC_MOVEDOWN:
{
LONG lvItemIndex;
PPH_COLUMN_SET_ENTRY entry;
ULONG index;
PhpMoveSelectedListViewItemDown(context->ListViewHandle);
lvItemIndex = PhFindListViewItemByFlags(context->ListViewHandle, INT_ERROR, LVNI_SELECTED);
if (lvItemIndex != INT_ERROR && PhGetListViewItemParam(context->ListViewHandle, lvItemIndex, (PVOID *)&entry))
{
index = PhFindItemList(context->ColumnSetList, entry);
if (index != ULONG_MAX)
{
PhRemoveItemList(context->ColumnSetList, index);
PhInsertItemList(context->ColumnSetList, lvItemIndex, entry);
}
}
}
break;
case IDC_REMOVE:
{
LONG lvItemIndex;
PPH_COLUMN_SET_ENTRY entry;
ULONG index;
lvItemIndex = PhFindListViewItemByFlags(context->ListViewHandle, INT_ERROR, LVNI_SELECTED);
if (lvItemIndex != INT_ERROR && PhGetListViewItemParam(context->ListViewHandle, lvItemIndex, (PVOID *)&entry))
{
index = PhFindItemList(context->ColumnSetList, entry);
if (index != ULONG_MAX)
{
PhRemoveItemList(context->ColumnSetList, index);
PhRemoveListViewItem(context->ListViewHandle, lvItemIndex);
PhClearReference(&entry->Name);
PhClearReference(&entry->Setting);
PhClearReference(&entry->Sorting);
PhFree(entry);
}
SetFocus(context->ListViewHandle);
ListView_SetItemState(context->ListViewHandle, 0, LVNI_SELECTED, LVNI_SELECTED);
//ListView_EnsureVisible(context->ListViewHandle, 0, FALSE);
}
}
break;
}
}
break;
case WM_NOTIFY:
{
LPNMHDR header = (LPNMHDR)lParam;
switch (header->code)
{
case NM_DBLCLK:
{
LONG lvItemIndex = PhFindListViewItemByFlags(context->ListViewHandle, INT_ERROR, LVNI_SELECTED);
if (lvItemIndex != INT_ERROR)
{
SetFocus(context->ListViewHandle);
ListView_EditLabel(context->ListViewHandle, lvItemIndex);
}
}
break;
case LVN_ITEMCHANGED:
{
LPNMLISTVIEW listview = (LPNMLISTVIEW)lParam;
LONG index;
LONG lvItemIndex;
LONG count;
index = listview->iItem;
lvItemIndex = PhFindListViewItemByFlags(context->ListViewHandle, INT_ERROR, LVNI_SELECTED);
count = ListView_GetItemCount(context->ListViewHandle);
if (count == 0 || index == -1 || lvItemIndex == -1)
{
Button_Enable(context->RenameButtonHandle, FALSE);
Button_Enable(context->MoveUpButtonHandle, FALSE);
Button_Enable(context->MoveDownButtonHandle, FALSE);
Button_Enable(context->RemoveButtonHandle, FALSE);
break;
}
if (index != lvItemIndex)
break;
if (index == 0 && count == 1)
{
// First and last item
Button_Enable(context->MoveUpButtonHandle, FALSE);
Button_Enable(context->MoveDownButtonHandle, FALSE);
}
else if (index == (count - 1))
{
// Last item
Button_Enable(context->MoveUpButtonHandle, TRUE);
Button_Enable(context->MoveDownButtonHandle, FALSE);
}
else if (index == 0)
{
// First item
Button_Enable(context->MoveUpButtonHandle, FALSE);
Button_Enable(context->MoveDownButtonHandle, TRUE);
}
else
{
Button_Enable(context->MoveUpButtonHandle, TRUE);
Button_Enable(context->MoveDownButtonHandle, TRUE);
}
Button_Enable(context->RenameButtonHandle, TRUE);
Button_Enable(context->RemoveButtonHandle, TRUE);
}
break;
case LVN_BEGINLABELEDIT:
context->LabelEditActive = TRUE;
break;
case LVN_ENDLABELEDIT:
{
LV_DISPINFO* lvinfo = (LV_DISPINFO*)lParam;
if (lvinfo->item.iItem != -1 && lvinfo->item.pszText)
{
BOOLEAN found = FALSE;
PPH_COLUMN_SET_ENTRY entry;
ULONG index;
for (ULONG i = 0; i < context->ColumnSetList->Count; i++)
{
entry = context->ColumnSetList->Items[i];
if (PhEqualStringRef2(&entry->Name->sr, lvinfo->item.pszText, FALSE))
{
found = TRUE;
break;
}
}
if (!found && PhGetListViewItemParam(context->ListViewHandle, lvinfo->item.iItem, (PVOID *)&entry))
{
index = PhFindItemList(context->ColumnSetList, entry);
if (index != -1)
{
PhMoveReference(&entry->Name, PhCreateString(lvinfo->item.pszText));
ListView_SetItemText(context->ListViewHandle, lvinfo->item.iItem, 0, lvinfo->item.pszText);
}
}
}
context->LabelEditActive = FALSE;
}
break;
}
}
break;
case WM_CTLCOLORBTN:
return HANDLE_WM_CTLCOLORBTN(hwndDlg, wParam, lParam, PhWindowThemeControlColor);
case WM_CTLCOLORDLG:
return HANDLE_WM_CTLCOLORDLG(hwndDlg, wParam, lParam, PhWindowThemeControlColor);
case WM_CTLCOLORSTATIC:
return HANDLE_WM_CTLCOLORSTATIC(hwndDlg, wParam, lParam, PhWindowThemeControlColor);
}
return FALSE;
}
================================================
FILE: SystemInformer/dbgcon.c
================================================
/*
* Copyright (c) 2022 Winsider Seminars & Solutions, Inc. All rights reserved.
*
* This file is part of System Informer.
*
* Authors:
*
* wj32 2010-2011
*
*/
/*
* This is a simple debugging console which is able to explore phlib's
* systems easily. Commands are provided to debug reference counting
* problems and memory usage, as well as to do general performance testing.
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
typedef struct _STRING_TABLE_ENTRY
{
PPH_STRING String;
ULONG_PTR Count;
} STRING_TABLE_ENTRY, *PSTRING_TABLE_ENTRY;
BOOL ConsoleHandlerRoutine(
_In_ ULONG dwCtrlType
);
VOID PhpPrintHashtableStatistics(
_In_ PPH_HASHTABLE Hashtable
);
_Function_class_(USER_THREAD_START_ROUTINE)
NTSTATUS PhpDebugConsoleThreadStart(
_In_ PVOID Parameter
);
extern PH_FREE_LIST PhObjectSmallFreeList;
static HANDLE DebugConsoleThreadHandle;
static PPH_SYMBOL_PROVIDER DebugConsoleSymbolProvider;
static PPH_HASHTABLE ObjectListSnapshot = NULL;
#ifdef DEBUG
static PPH_LIST NewObjectList = NULL;
static PH_QUEUED_LOCK NewObjectListLock;
#endif
static BOOLEAN ShowAllLeaks = FALSE;
static BOOLEAN InLeakDetection = FALSE;
static ULONG NumberOfLeaks;
static ULONG NumberOfLeaksShown;
VOID PhShowDebugConsole(
VOID
)
{
if (AllocConsole())
{
HMENU menu;
// Disable the close button because it's impossible to handle
// those events.
menu = GetSystemMenu(GetConsoleWindow(), FALSE);
EnableMenuItem(menu, SC_CLOSE, MF_GRAYED | MF_DISABLED);
DeleteMenu(menu, SC_CLOSE, 0);
// Set a handler so we can catch Ctrl+C and Ctrl+Break.
SetConsoleCtrlHandler(ConsoleHandlerRoutine, TRUE);
_wfreopen(L"CONOUT$", L"w", stdout);
_wfreopen(L"CONOUT$", L"w", stderr);
_wfreopen(L"CONIN$", L"r", stdin);
SetConsoleCP(CP_UTF8);
SetConsoleOutputCP(CP_UTF8);
PhCreateThreadEx(&DebugConsoleThreadHandle, PhpDebugConsoleThreadStart, NULL);
}
else
{
HWND consoleWindow;
consoleWindow = GetConsoleWindow();
// Console window already exists, so bring it to the top.
if (IsMinimized(consoleWindow))
ShowWindow(consoleWindow, SW_RESTORE);
else
BringWindowToTop(consoleWindow);
return;
}
}
VOID PhCloseDebugConsole(
VOID
)
{
_wfreopen(L"NUL", L"w", stdout);
_wfreopen(L"NUL", L"w", stderr);
_wfreopen(L"NUL", L"r", stdin);
PhFreeConsole();
}
BOOL ConsoleHandlerRoutine(
_In_ ULONG dwCtrlType
)
{
switch (dwCtrlType)
{
case CTRL_C_EVENT:
case CTRL_BREAK_EVENT:
case CTRL_CLOSE_EVENT:
PhCloseDebugConsole();
return TRUE;
}
return FALSE;
}
static PWSTR PhpGetSymbolForAddress(
_In_ PVOID Address
)
{
return PH_AUTO_T(PH_STRING, PhGetSymbolFromAddress(
DebugConsoleSymbolProvider, Address, NULL, NULL, NULL, NULL
))->Buffer;
}
static VOID PhpPrintObjectInfo(
_In_ PPH_OBJECT_HEADER ObjectHeader,
_In_ LONG RefToSubtract
)
{
PVOID object;
PPH_OBJECT_TYPE objectType;
WCHAR c = L' ';
object = PhObjectHeaderToObject(ObjectHeader);
wprintf(L"%Ix", (ULONG_PTR)object);
objectType = PhGetObjectType(object);
wprintf(L"\t%20s", objectType->Name);
if (ObjectHeader->Flags & PH_OBJECT_FROM_SMALL_FREE_LIST)
c = L'f';
else if (ObjectHeader->Flags & PH_OBJECT_FROM_TYPE_FREE_LIST)
c = L'F';
wprintf(L"\t%4d %c", ObjectHeader->RefCount - RefToSubtract, c);
if (!objectType)
{
// Dummy
}
else if (objectType == PhObjectTypeObject)
{
wprintf(L"\t%.32s", ((PPH_OBJECT_TYPE)object)->Name);
}
else if (objectType == PhStringType)
{
wprintf(L"\t%.32s", ((PPH_STRING)object)->Buffer);
}
else if (objectType == PhBytesType)
{
wprintf(L"\t%.32S", ((PPH_BYTES)object)->Buffer);
}
else if (objectType == PhListType)
{
wprintf(L"\tCount: %u", ((PPH_LIST)object)->Count);
}
else if (objectType == PhPointerListType)
{
wprintf(L"\tCount: %u", ((PPH_POINTER_LIST)object)->Count);
}
else if (objectType == PhHashtableType)
{
wprintf(L"\tCount: %u", ((PPH_HASHTABLE)object)->Count);
}
else if (objectType == PhProcessItemType)
{
wprintf(
L"\t%.28s (%lu)",
((PPH_PROCESS_ITEM)object)->ProcessName->Buffer,
HandleToUlong(((PPH_PROCESS_ITEM)object)->ProcessId)
);
}
else if (objectType == PhServiceItemType)
{
wprintf(L"\t%s", ((PPH_SERVICE_ITEM)object)->Name->Buffer);
}
else if (objectType == PhThreadItemType)
{
wprintf(L"\tTID: %lu", HandleToUlong(((PPH_THREAD_ITEM)object)->ThreadId));
}
wprintf(L"\n");
}
static VOID PhpDumpObjectInfo(
_In_ PPH_OBJECT_HEADER ObjectHeader
)
{
PVOID object;
PPH_OBJECT_TYPE objectType;
object = PhObjectHeaderToObject(ObjectHeader);
objectType = PhGetObjectType(object);
__try
{
wprintf(L"Type: %s\n", objectType->Name);
wprintf(L"Reference count: %ld\n", ObjectHeader->RefCount);
wprintf(L"Flags: %x\n", ObjectHeader->Flags);
if (objectType == PhObjectTypeObject)
{
wprintf(L"Name: %s\n", ((PPH_OBJECT_TYPE)object)->Name);
wprintf(L"Number of objects: %lu\n", ((PPH_OBJECT_TYPE)object)->NumberOfObjects);
wprintf(L"Flags: %u\n", ((PPH_OBJECT_TYPE)object)->Flags);
wprintf(L"Type index: %u\n", ((PPH_OBJECT_TYPE)object)->TypeIndex);
wprintf(L"Free list count: %lu\n", ((PPH_OBJECT_TYPE)object)->FreeList.Count);
}
else if (objectType == PhStringType)
{
wprintf(L"%s\n", ((PPH_STRING)object)->Buffer);
}
else if (objectType == PhBytesType)
{
wprintf(L"%S\n", ((PPH_BYTES)object)->Buffer);
}
else if (objectType == PhHashtableType)
{
PhpPrintHashtableStatistics((PPH_HASHTABLE)object);
}
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
wprintf(L"Error.\n");
}
}
VOID PhpPrintHashtableStatistics(
_In_ PPH_HASHTABLE Hashtable
)
{
ULONG i;
ULONG expectedLookupMisses = 0;
wprintf(L"Count: %u\n", Hashtable->Count);
wprintf(L"Allocated buckets: %u\n", Hashtable->AllocatedBuckets);
wprintf(L"Allocated entries: %u\n", Hashtable->AllocatedEntries);
wprintf(L"Next free entry: %d\n", Hashtable->FreeEntry);
wprintf(L"Next usable entry: %d\n", Hashtable->NextEntry);
wprintf(L"Equal function: %s\n", PhpGetSymbolForAddress(Hashtable->EqualFunction));
wprintf(L"Hash function: %s\n", PhpGetSymbolForAddress(Hashtable->HashFunction));
wprintf(L"\nBuckets:\n");
for (i = 0; i < Hashtable->AllocatedBuckets; i++)
{
ULONG index;
ULONG count = 0;
// Count the number of entries in this bucket.
index = Hashtable->Buckets[i];
while (index != ULONG_MAX)
{
index = PH_HASHTABLE_GET_ENTRY(Hashtable, index)->Next;
count++;
}
if (count != 0)
{
expectedLookupMisses += count - 1;
}
if (count != 0)
{
wprintf(L"%lu: ", i);
// Print out the entry indicies.
index = Hashtable->Buckets[i];
while (index != ULONG_MAX)
{
wprintf(L"%lu", index);
index = PH_HASHTABLE_GET_ENTRY(Hashtable, index)->Next;
count--;
if (count != 0)
wprintf(L", ");
}
wprintf(L"\n");
}
else
{
//wprintf(L"%u: (empty)\n");
}
}
wprintf(L"\nExpected lookup misses: %lu\n", expectedLookupMisses);
}
#ifdef DEBUG
static VOID PhpDebugCreateObjectHook(
_In_ PVOID Object,
_In_ SIZE_T Size,
_In_ ULONG Flags,
_In_ PPH_OBJECT_TYPE ObjectType
)
{
PhAcquireQueuedLockExclusive(&NewObjectListLock);
if (NewObjectList)
{
PhReferenceObject(Object);
PhAddItemList(NewObjectList, Object);
}
PhReleaseQueuedLockExclusive(&NewObjectListLock);
}
#endif
#ifdef DEBUG
static VOID PhpDeleteNewObjectList(
VOID
)
{
if (NewObjectList)
{
PhDereferenceObjects(NewObjectList->Items, NewObjectList->Count);
PhDereferenceObject(NewObjectList);
NewObjectList = NULL;
}
}
#endif
_Function_class_(PH_HASHTABLE_EQUAL_FUNCTION)
static BOOLEAN PhpStringHashtableEqualFunction(
_In_ PVOID Entry1,
_In_ PVOID Entry2
)
{
PSTRING_TABLE_ENTRY entry1 = Entry1;
PSTRING_TABLE_ENTRY entry2 = Entry2;
return PhEqualString(entry1->String, entry2->String, FALSE);
}
_Function_class_(PH_HASHTABLE_HASH_FUNCTION)
static ULONG PhpStringHashtableHashFunction(
_In_ PVOID Entry
)
{
PSTRING_TABLE_ENTRY entry = Entry;
return PhHashBytes((PUCHAR)entry->String->Buffer, entry->String->Length);
}
static int __cdecl PhpStringEntryCompareByCount(
_In_ const void *elem1,
_In_ const void *elem2
)
{
PSTRING_TABLE_ENTRY entry1 = *(PSTRING_TABLE_ENTRY *)elem1;
PSTRING_TABLE_ENTRY entry2 = *(PSTRING_TABLE_ENTRY *)elem2;
return uintptrcmp(entry2->Count, entry1->Count);
}
static NTSTATUS PhpLeakEnumerationRoutine(
_In_ LONG Reserved,
_In_ PVOID HeapHandle,
_In_ PVOID BaseAddress,
_In_ SIZE_T BlockSize,
_In_ ULONG StackTraceDepth,
_In_ PVOID *StackTrace
)
{
ULONG i;
if (!InLeakDetection)
return 0;
if (!HeapHandle) // means no more entries
return 0;
if (ShowAllLeaks || HeapHandle == PhHeapHandle)
{
wprintf(L"Leak at 0x%Ix (%Iu bytes). Stack trace:\n", (ULONG_PTR)BaseAddress, BlockSize);
for (i = 0; i < StackTraceDepth; i++)
{
PPH_STRING symbol;
symbol = PhGetSymbolFromAddress(DebugConsoleSymbolProvider, StackTrace[i], NULL, NULL, NULL, NULL);
if (symbol)
{
wprintf(L"\t%s\n", symbol->Buffer);
PhDereferenceObject(symbol);
}
else
{
wprintf(L"\t?\n");
}
}
NumberOfLeaksShown++;
}
NumberOfLeaks++;
return 0;
}
typedef VOID (FASTCALL *PPHF_RW_LOCK_FUNCTION)(
_In_ PVOID Parameter
);
typedef struct _RW_TEST_CONTEXT
{
PWSTR Name;
PPHF_RW_LOCK_FUNCTION AcquireExclusive;
PPHF_RW_LOCK_FUNCTION AcquireShared;
PPHF_RW_LOCK_FUNCTION ReleaseExclusive;
PPHF_RW_LOCK_FUNCTION ReleaseShared;
PVOID Parameter;
} RW_TEST_CONTEXT, *PRW_TEST_CONTEXT;
static PH_BARRIER RwStartBarrier;
static LONG RwReadersActive;
static LONG RwWritersActive;
_Function_class_(USER_THREAD_START_ROUTINE)
static NTSTATUS PhpRwLockTestThreadStart(
_In_ PVOID Parameter
)
{
#define RW_ITERS 10000
#define RW_READ_ITERS 100
#define RW_WRITE_ITERS 10
#define RW_READ_SPIN_ITERS 60
#define RW_WRITE_SPIN_ITERS 200
RW_TEST_CONTEXT context = *(PRW_TEST_CONTEXT)Parameter;
ULONG i;
ULONG j;
ULONG k;
ULONG m;
PhWaitForBarrier(&RwStartBarrier, FALSE);
for (i = 0; i < RW_ITERS; i++)
{
for (j = 0; j < RW_READ_ITERS; j++)
{
// Read zone
context.AcquireShared(context.Parameter);
_InterlockedIncrement(&RwReadersActive);
for (m = 0; m < RW_READ_SPIN_ITERS; m++)
YieldProcessor();
if (ReadAcquire(&RwWritersActive) != 0)
{
wprintf(L"[fail]: writers active in read zone!\n");
NtWaitForSingleObject(NtCurrentProcess(), FALSE, NULL);
}
_InterlockedDecrement(&RwReadersActive);
context.ReleaseShared(context.Parameter);
// Spin for a while
for (m = 0; m < 10; m++)
YieldProcessor();
if (j == RW_READ_ITERS / 2)
{
// Write zone
for (k = 0; k < RW_WRITE_ITERS; k++)
{
context.AcquireExclusive(context.Parameter);
_InterlockedIncrement(&RwWritersActive);
for (m = 0; m < RW_WRITE_SPIN_ITERS; m++)
YieldProcessor();
if (ReadAcquire(&RwReadersActive) != 0)
{
wprintf(L"[fail]: readers active in write zone!\n");
NtWaitForSingleObject(NtCurrentProcess(), FALSE, NULL);
}
_InterlockedDecrement(&RwWritersActive);
context.ReleaseExclusive(context.Parameter);
}
}
}
}
return STATUS_SUCCESS;
}
static VOID PhpTestRwLock(
_In_ PRW_TEST_CONTEXT Context
)
{
#define RW_PROCESSORS 4
PH_STOPWATCH stopwatch;
ULONG i;
HANDLE threadHandles[RW_PROCESSORS];
// Dummy
Context->AcquireExclusive(Context->Parameter);
Context->ReleaseExclusive(Context->Parameter);
Context->AcquireShared(Context->Parameter);
Context->ReleaseShared(Context->Parameter);
// Null test
PhInitializeStopwatch(&stopwatch);
PhStartStopwatch(&stopwatch);
for (i = 0; i < 2000000; i++)
{
Context->AcquireExclusive(Context->Parameter);
Context->ReleaseExclusive(Context->Parameter);
Context->AcquireShared(Context->Parameter);
Context->ReleaseShared(Context->Parameter);
}
PhStopStopwatch(&stopwatch);
wprintf(L"[null] %s: %ums\n", Context->Name, PhGetMillisecondsStopwatch(&stopwatch));
// Stress test
PhInitializeBarrier(&RwStartBarrier, RW_PROCESSORS + 1);
RwReadersActive = 0;
RwWritersActive = 0;
for (i = 0; i < RW_PROCESSORS; i++)
{
PhCreateThreadEx(&threadHandles[i], PhpRwLockTestThreadStart, Context);
}
PhWaitForBarrier(&RwStartBarrier, FALSE);
PhInitializeStopwatch(&stopwatch);
PhStartStopwatch(&stopwatch);
NtWaitForMultipleObjects(RW_PROCESSORS, threadHandles, WaitAll, FALSE, NULL);
PhStopStopwatch(&stopwatch);
for (i = 0; i < RW_PROCESSORS; i++)
NtClose(threadHandles[i]);
wprintf(L"[strs] %s: %ums\n", Context->Name, PhGetMillisecondsStopwatch(&stopwatch));
}
_Acquires_exclusive_lock_(*CriticalSection)
VOID FASTCALL PhfAcquireCriticalSection(
_In_ PRTL_CRITICAL_SECTION CriticalSection
)
{
RtlEnterCriticalSection(CriticalSection);
}
_Releases_exclusive_lock_(*CriticalSection)
VOID FASTCALL PhfReleaseCriticalSection(
_In_ PRTL_CRITICAL_SECTION CriticalSection
)
{
RtlLeaveCriticalSection(CriticalSection);
}
_Function_class_(USER_THREAD_START_ROUTINE)
NTSTATUS PhpDebugConsoleThreadStart(
_In_ PVOID Parameter
)
{
PH_AUTO_POOL autoPool;
BOOLEAN exit = FALSE;
PhInitializeAutoPool(&autoPool);
DebugConsoleSymbolProvider = PhCreateSymbolProvider(NtCurrentProcessId());
PhLoadSymbolProviderOptions(DebugConsoleSymbolProvider);
{
static CONST PH_STRINGREF variableNameSr = PH_STRINGREF_INIT(L"_NT_SYMBOL_PATH");
PPH_STRING variableValue;
PPH_STRING newSearchPath;
if (NT_SUCCESS(PhQueryEnvironmentVariable(NULL, &variableNameSr, &variableValue)))
{
PPH_STRING currentDirectory = PhGetApplicationDirectoryWin32();
PPH_STRING currentSearchPath = PhGetStringSetting(SETTING_DBGHELP_SEARCH_PATH);
if (currentSearchPath->Length != 0)
{
newSearchPath = PhFormatString(
L"%s;%s;%s",
variableValue->Buffer,
PhGetStringOrEmpty(currentSearchPath),
PhGetStringOrEmpty(currentDirectory)
);
}
else
{
newSearchPath = PhFormatString(
L"%s;%s",
variableValue->Buffer,
PhGetStringOrEmpty(currentDirectory)
);
}
PhSetSearchPathSymbolProvider(DebugConsoleSymbolProvider, PhGetString(newSearchPath));
PhDereferenceObject(variableValue);
PhDereferenceObject(newSearchPath);
PhDereferenceObject(currentDirectory);
}
}
PhLoadSymbolProviderModules(
DebugConsoleSymbolProvider,
NtCurrentProcessId()
);
#ifdef DEBUG
PhInitializeQueuedLock(&NewObjectListLock);
PhDbgCreateObjectHook = PhpDebugCreateObjectHook;
#endif
wprintf(L"Press Ctrl+C or type \"exit\" to close the debug console. Type \"help\" for a list of commands.\n");
while (!exit)
{
static PCWSTR delims = L" \t";
static PCWSTR commandDebugOnly = L"This command is not available on non-debug builds.\n";
WCHAR line[201];
ULONG inputLength;
PWSTR context;
PWSTR command;
wprintf(L"dbg>");
if (!fgetws(line, sizeof(line) / sizeof(WCHAR) - 1, stdin))
break;
// Remove the terminating new line character.
inputLength = (ULONG)PhCountStringZ(line);
if (inputLength != 0)
line[inputLength - 1] = UNICODE_NULL;
context = NULL;
command = wcstok_s(line, delims, &context);
if (!command)
{
continue;
}
else if (PhEqualStringZ(command, L"help", TRUE))
{
wprintf(
L"Commands:\n"
L"exit\n"
L"testperf\n"
L"testlocks\n"
L"stats\n"
L"objects [type-name-filter]\n"
L"objtrace object-address\n"
L"objmksnap\n"
L"objcmpsnap\n"
L"objmknew\n"
L"objdelnew\n"
L"objviewnew\n"
L"dumpobj\n"
L"dumpautopool\n"
L"threads\n"
L"provthreads\n"
L"workqueues\n"
L"procrecords\n"
L"procitem\n"
L"uniquestr\n"
L"enableleakdetect\n"
L"leakdetect\n"
L"mem\n"
);
}
else if (PhEqualStringZ(command, L"exit", TRUE))
{
PhCloseDebugConsole();
exit = TRUE;
}
else if (PhEqualStringZ(command, L"testperf", TRUE))
{
PH_STOPWATCH stopwatch;
ULONG i;
PPH_STRING testString;
RTL_CRITICAL_SECTION testCriticalSection;
PH_FAST_LOCK testFastLock;
PH_QUEUED_LOCK testQueuedLock;
// Control (string reference counting)
testString = PhCreateString(L"");
PhReferenceObject(testString);
PhDereferenceObject(testString);
PhStartStopwatch(&stopwatch);
for (i = 0; i < 10000000; i++)
{
PhReferenceObject(testString);
PhDereferenceObject(testString);
}
PhStopStopwatch(&stopwatch);
PhDereferenceObject(testString);
wprintf(L"Referencing: %ums\n", PhGetMillisecondsStopwatch(&stopwatch));
// Critical section
RtlInitializeCriticalSection(&testCriticalSection);
RtlEnterCriticalSection(&testCriticalSection);
RtlLeaveCriticalSection(&testCriticalSection);
PhStartStopwatch(&stopwatch);
for (i = 0; i < 10000000; i++)
{
RtlEnterCriticalSection(&testCriticalSection);
RtlLeaveCriticalSection(&testCriticalSection);
}
PhStopStopwatch(&stopwatch);
RtlDeleteCriticalSection(&testCriticalSection);
wprintf(L"Critical section: %ums\n", PhGetMillisecondsStopwatch(&stopwatch));
// Fast lock
PhInitializeFastLock(&testFastLock);
PhAcquireFastLockExclusive(&testFastLock);
PhReleaseFastLockExclusive(&testFastLock);
PhStartStopwatch(&stopwatch);
for (i = 0; i < 10000000; i++)
{
PhAcquireFastLockExclusive(&testFastLock);
PhReleaseFastLockExclusive(&testFastLock);
}
PhStopStopwatch(&stopwatch);
PhDeleteFastLock(&testFastLock);
wprintf(L"Fast lock: %ums\n", PhGetMillisecondsStopwatch(&stopwatch));
// Queued lock
PhInitializeQueuedLock(&testQueuedLock);
PhAcquireQueuedLockExclusive(&testQueuedLock);
PhReleaseQueuedLockExclusive(&testQueuedLock);
PhStartStopwatch(&stopwatch);
for (i = 0; i < 10000000; i++)
{
PhAcquireQueuedLockExclusive(&testQueuedLock);
PhReleaseQueuedLockExclusive(&testQueuedLock);
}
PhStopStopwatch(&stopwatch);
wprintf(L"Queued lock: %ums\n", PhGetMillisecondsStopwatch(&stopwatch));
}
else if (PhEqualStringZ(command, L"testlocks", TRUE))
{
RW_TEST_CONTEXT testContext;
PH_FAST_LOCK fastLock;
PH_QUEUED_LOCK queuedLock;
RTL_CRITICAL_SECTION criticalSection;
testContext.Name = L"FastLock";
testContext.AcquireExclusive = PhfAcquireFastLockExclusive;
testContext.AcquireShared = PhfAcquireFastLockShared;
testContext.ReleaseExclusive = PhfReleaseFastLockExclusive;
testContext.ReleaseShared = PhfReleaseFastLockShared;
testContext.Parameter = &fastLock;
PhInitializeFastLock(&fastLock);
PhpTestRwLock(&testContext);
PhDeleteFastLock(&fastLock);
testContext.Name = L"QueuedLock";
testContext.AcquireExclusive = PhfAcquireQueuedLockExclusive;
testContext.AcquireShared = PhfAcquireQueuedLockShared;
testContext.ReleaseExclusive = PhfReleaseQueuedLockExclusive;
testContext.ReleaseShared = PhfReleaseQueuedLockShared;
testContext.Parameter = &queuedLock;
PhInitializeQueuedLock(&queuedLock);
PhpTestRwLock(&testContext);
testContext.Name = L"CriticalSection";
testContext.AcquireExclusive = PhfAcquireCriticalSection;
testContext.AcquireShared = PhfAcquireCriticalSection;
testContext.ReleaseExclusive = PhfReleaseCriticalSection;
testContext.ReleaseShared = PhfReleaseCriticalSection;
testContext.Parameter = &criticalSection;
RtlInitializeCriticalSection(&criticalSection);
PhpTestRwLock(&testContext);
RtlDeleteCriticalSection(&criticalSection);
testContext.Name = L"QueuedLockMutex";
testContext.AcquireExclusive = PhfAcquireQueuedLockExclusive;
testContext.AcquireShared = PhfAcquireQueuedLockExclusive;
testContext.ReleaseExclusive = PhfReleaseQueuedLockExclusive;
testContext.ReleaseShared = PhfReleaseQueuedLockExclusive;
testContext.Parameter = &queuedLock;
PhInitializeQueuedLock(&queuedLock);
PhpTestRwLock(&testContext);
}
else if (PhEqualStringZ(command, L"stats", TRUE))
{
#ifdef DEBUG
wprintf(L"Object small free list count: %u\n", PhObjectSmallFreeList.Count);
wprintf(L"Statistics:\n");
#define PRINT_STATISTIC(Name) wprintf(TEXT(#Name) L": %u\n", PhLibStatisticsBlock.Name);
PRINT_STATISTIC(BaseThreadsCreated);
PRINT_STATISTIC(BaseThreadsCreateFailed);
PRINT_STATISTIC(BaseStringBuildersCreated);
PRINT_STATISTIC(BaseStringBuildersResized);
PRINT_STATISTIC(RefObjectsCreated);
PRINT_STATISTIC(RefObjectsDestroyed);
PRINT_STATISTIC(RefObjectsAllocated);
PRINT_STATISTIC(RefObjectsFreed);
PRINT_STATISTIC(RefObjectsAllocatedFromSmallFreeList);
PRINT_STATISTIC(RefObjectsFreedToSmallFreeList);
PRINT_STATISTIC(RefObjectsAllocatedFromTypeFreeList);
PRINT_STATISTIC(RefObjectsFreedToTypeFreeList);
PRINT_STATISTIC(RefObjectsDeleteDeferred);
PRINT_STATISTIC(RefAutoPoolsCreated);
PRINT_STATISTIC(RefAutoPoolsDestroyed);
PRINT_STATISTIC(RefAutoPoolsDynamicAllocated);
PRINT_STATISTIC(RefAutoPoolsDynamicResized);
PRINT_STATISTIC(QlBlockSpins);
PRINT_STATISTIC(QlBlockWaits);
PRINT_STATISTIC(QlAcquireExclusiveBlocks);
PRINT_STATISTIC(QlAcquireSharedBlocks);
PRINT_STATISTIC(WqWorkQueueThreadsCreated);
PRINT_STATISTIC(WqWorkQueueThreadsCreateFailed);
PRINT_STATISTIC(WqWorkItemsQueued);
#else
wprintf(commandDebugOnly);
#endif
}
else if (PhEqualStringZ(command, L"objects", TRUE))
{
#ifdef DEBUG
PWSTR typeFilter = wcstok_s(NULL, delims, &context);
PLIST_ENTRY currentEntry;
ULONG totalNumberOfObjects = 0;
//SIZE_T totalNumberOfBytes = 0;
if (typeFilter)
_wcslwr(typeFilter);
PhAcquireQueuedLockShared(&PhDbgObjectListLock);
currentEntry = PhDbgObjectListHead.Flink;
while (currentEntry != &PhDbgObjectListHead)
{
PPH_OBJECT_HEADER objectHeader;
WCHAR typeName[32];
objectHeader = CONTAINING_RECORD(currentEntry, PH_OBJECT_HEADER, ObjectListEntry);
// Make sure the object isn't being destroyed.
if (!PhReferenceObjectSafe(PhObjectHeaderToObject(objectHeader)))
{
currentEntry = currentEntry->Flink;
continue;
}
totalNumberOfObjects++;
//totalNumberOfBytes += objectHeader->Size;
if (typeFilter)
{
wcscpy_s(typeName, sizeof(typeName) / sizeof(WCHAR), PhGetObjectType(PhObjectHeaderToObject(objectHeader))->Name);
_wcslwr(typeName);
}
if (
!typeFilter ||
(typeFilter && wcsstr(typeName, typeFilter))
)
{
PhpPrintObjectInfo(objectHeader, 1);
}
currentEntry = currentEntry->Flink;
PhDereferenceObjectDeferDelete(PhObjectHeaderToObject(objectHeader));
}
PhReleaseQueuedLockShared(&PhDbgObjectListLock);
wprintf(L"\n");
wprintf(L"Total number: %lu\n", totalNumberOfObjects);
/*wprintf(L"Total size (excl. header): %s\n",
((PPH_STRING)PH_AUTO(PhFormatSize(totalNumberOfBytes, 1)))->Buffer);*/
wprintf(L"Total overhead (header): %s\n",
((PPH_STRING)PH_AUTO(
PhFormatSize(PhAddObjectHeaderSize(0) * totalNumberOfObjects, 1)
))->Buffer);
#else
wprintf(commandDebugOnly);
#endif
}
else if (PhEqualStringZ(command, L"objtrace", TRUE))
{
#ifdef DEBUG
PWSTR objectAddress = wcstok_s(NULL, delims, &context);
PH_STRINGREF objectAddressString;
ULONG64 address;
if (!objectAddress)
{
wprintf(L"Missing object address.\n");
goto EndCommand;
}
PhInitializeStringRef(&objectAddressString, objectAddress);
if (PhStringToUInt64(&objectAddressString, 16, &address))
{
PPH_OBJECT_HEADER objectHeader = (PPH_OBJECT_HEADER)PhObjectToObjectHeader((PVOID)address);
PVOID stackBackTrace[16];
ULONG i;
// The address may not be valid.
__try
{
memcpy(stackBackTrace, objectHeader->StackBackTrace, 16 * sizeof(PVOID));
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
PPH_STRING message;
message = PH_AUTO(PhGetNtMessage(GetExceptionCode()));
wprintf(L"Error: %s\n", PhGetString(message));
goto EndCommand;
}
for (i = 0; i < 16; i++)
{
if (!stackBackTrace[i])
break;
wprintf(L"%s\n", PhpGetSymbolForAddress(stackBackTrace[i]));
}
}
else
{
wprintf(L"Invalid object address.\n");
}
#else
wprintf(commandDebugOnly);
#endif
}
else if (PhEqualStringZ(command, L"objmksnap", TRUE))
{
#ifdef DEBUG
PLIST_ENTRY currentEntry;
if (ObjectListSnapshot)
{
PhDereferenceObject(ObjectListSnapshot);
ObjectListSnapshot = NULL;
}
ObjectListSnapshot = PhCreateSimpleHashtable(100);
PhAcquireQueuedLockShared(&PhDbgObjectListLock);
currentEntry = PhDbgObjectListHead.Flink;
while (currentEntry != &PhDbgObjectListHead)
{
PPH_OBJECT_HEADER objectHeader;
objectHeader = CONTAINING_RECORD(currentEntry, PH_OBJECT_HEADER, ObjectListEntry);
currentEntry = currentEntry->Flink;
if (PhObjectHeaderToObject(objectHeader) != ObjectListSnapshot)
PhAddItemSimpleHashtable(ObjectListSnapshot, objectHeader, NULL);
}
PhReleaseQueuedLockShared(&PhDbgObjectListLock);
#else
wprintf(commandDebugOnly);
#endif
}
else if (PhEqualStringZ(command, L"objcmpsnap", TRUE))
{
#ifdef DEBUG
PLIST_ENTRY currentEntry;
PPH_LIST newObjects;
ULONG i;
if (!ObjectListSnapshot)
{
wprintf(L"No snapshot.\n");
goto EndCommand;
}
newObjects = PhCreateList(10);
PhAcquireQueuedLockShared(&PhDbgObjectListLock);
currentEntry = PhDbgObjectListHead.Flink;
while (currentEntry != &PhDbgObjectListHead)
{
PPH_OBJECT_HEADER objectHeader;
objectHeader = CONTAINING_RECORD(currentEntry, PH_OBJECT_HEADER, ObjectListEntry);
currentEntry = currentEntry->Flink;
if (
PhObjectHeaderToObject(objectHeader) != ObjectListSnapshot &&
PhObjectHeaderToObject(objectHeader) != newObjects
)
{
if (!PhFindItemSimpleHashtable(ObjectListSnapshot, objectHeader))
{
if (PhReferenceObjectSafe(PhObjectHeaderToObject(objectHeader)))
PhAddItemList(newObjects, objectHeader);
}
}
}
PhReleaseQueuedLockShared(&PhDbgObjectListLock);
for (i = 0; i < newObjects->Count; i++)
{
PPH_OBJECT_HEADER objectHeader = newObjects->Items[i];
PhpPrintObjectInfo(objectHeader, 1);
PhDereferenceObject(PhObjectHeaderToObject(objectHeader));
}
PhDereferenceObject(newObjects);
#else
wprintf(commandDebugOnly);
#endif
}
else if (PhEqualStringZ(command, L"objmknew", TRUE))
{
#ifdef DEBUG
PhAcquireQueuedLockExclusive(&NewObjectListLock);
PhpDeleteNewObjectList();
PhReleaseQueuedLockExclusive(&NewObjectListLock);
// Creation needs to be done outside of the lock,
// otherwise a deadlock will occur.
NewObjectList = PhCreateList(100);
#else
wprintf(commandDebugOnly);
#endif
}
else if (PhEqualStringZ(command, L"objdelnew", TRUE))
{
#ifdef DEBUG
PhAcquireQueuedLockExclusive(&NewObjectListLock);
PhpDeleteNewObjectList();
PhReleaseQueuedLockExclusive(&NewObjectListLock);
#else
wprintf(commandDebugOnly);
#endif
}
else if (PhEqualStringZ(command, L"objviewnew", TRUE))
{
#ifdef DEBUG
ULONG i;
PhAcquireQueuedLockExclusive(&NewObjectListLock);
if (!NewObjectList)
{
wprintf(L"Object creation hooking not active.\n");
PhReleaseQueuedLockExclusive(&NewObjectListLock);
goto EndCommand;
}
for (i = 0; i < NewObjectList->Count; i++)
{
PhpPrintObjectInfo(PhObjectToObjectHeader(NewObjectList->Items[i]), 1);
}
PhReleaseQueuedLockExclusive(&NewObjectListLock);
#else
wprintf(commandDebugOnly);
#endif
}
else if (PhEqualStringZ(command, L"dumpobj", TRUE))
{
PWSTR addressString = wcstok_s(NULL, delims, &context);
PH_STRINGREF addressStringRef;
ULONG64 address;
if (!addressString)
goto EndCommand;
PhInitializeStringRef(&addressStringRef, addressString);
if (PhStringToUInt64(&addressStringRef, 16, &address))
{
PhpDumpObjectInfo((PPH_OBJECT_HEADER)PhObjectToObjectHeader((PVOID)address));
}
}
else if (PhEqualStringZ(command, L"dumpautopool", TRUE))
{
PWSTR addressString = wcstok_s(NULL, delims, &context);
PH_STRINGREF addressStringRef;
ULONG64 address;
if (!addressString)
goto EndCommand;
PhInitializeStringRef(&addressStringRef, addressString);
if (PhStringToUInt64(&addressStringRef, 16, &address))
{
PPH_AUTO_POOL userAutoPool = (PPH_AUTO_POOL)address;
ULONG i;
__try
{
wprintf(L"Static count: %u\n", userAutoPool->StaticCount);
wprintf(L"Dynamic count: %u\n", userAutoPool->DynamicCount);
wprintf(L"Dynamic allocated: %u\n", userAutoPool->DynamicAllocated);
wprintf(L"Static objects:\n");
for (i = 0; i < userAutoPool->StaticCount; i++)
PhpPrintObjectInfo(PhObjectToObjectHeader(userAutoPool->StaticObjects[i]), 0);
wprintf(L"Dynamic objects:\n");
for (i = 0; i < userAutoPool->DynamicCount; i++)
PhpPrintObjectInfo(PhObjectToObjectHeader(userAutoPool->DynamicObjects[i]), 0);
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
goto EndCommand;
}
}
}
else if (PhEqualStringZ(command, L"threads", TRUE))
{
#ifdef DEBUG
PLIST_ENTRY currentEntry;
PhAcquireQueuedLockShared(&PhDbgThreadListLock);
currentEntry = PhDbgThreadListHead.Flink;
while (currentEntry != &PhDbgThreadListHead)
{
PPHP_BASE_THREAD_DBG dbg;
dbg = CONTAINING_RECORD(currentEntry, PHP_BASE_THREAD_DBG, ListEntry);
wprintf(L"Thread %u\n", HandleToUlong(dbg->ClientId.UniqueThread));
wprintf(L"\tStart Address: %s\n", PhpGetSymbolForAddress(dbg->StartAddress));
wprintf(L"\tParameter: %Ix\n", (ULONG_PTR)dbg->Parameter);
wprintf(L"\tCurrent auto-pool: %Ix\n", (ULONG_PTR)dbg->CurrentAutoPool);
currentEntry = currentEntry->Flink;
}
PhReleaseQueuedLockShared(&PhDbgThreadListLock);
#else
wprintf(commandDebugOnly);
#endif
}
else if (PhEqualStringZ(command, L"provthreads", TRUE))
{
#ifdef DEBUG
ULONG i;
if (PhDbgProviderList)
{
PhAcquireQueuedLockShared(&PhDbgProviderListLock);
for (i = 0; i < PhDbgProviderList->Count; i++)
{
PPH_PROVIDER_THREAD providerThread = PhDbgProviderList->Items[i];
THREAD_BASIC_INFORMATION basicInfo;
PLIST_ENTRY providerEntry;
if (providerThread->ThreadHandle)
{
PhGetThreadBasicInformation(providerThread->ThreadHandle, &basicInfo);
wprintf(L"Thread %u\n", HandleToUlong(basicInfo.ClientId.UniqueThread));
}
else
{
wprintf(L"Thread not running\n");
}
PhAcquireQueuedLockExclusive(&providerThread->Lock);
providerEntry = providerThread->ListHead.Flink;
while (providerEntry != &providerThread->ListHead)
{
PPH_PROVIDER_REGISTRATION registration;
registration = CONTAINING_RECORD(providerEntry, PH_PROVIDER_REGISTRATION, ListEntry);
wprintf(L"\tProvider registration at %Ix\n", (ULONG_PTR)registration);
wprintf(L"\t\tEnabled: %s\n", registration->Enabled ? L"Yes" : L"No");
wprintf(L"\t\tFunction: %s\n", PhpGetSymbolForAddress(registration->Function));
if (registration->Object)
{
wprintf(L"\t\tObject:\n");
PhpPrintObjectInfo(PhObjectToObjectHeader(registration->Object), 0);
}
providerEntry = providerEntry->Flink;
}
PhReleaseQueuedLockExclusive(&providerThread->Lock);
wprintf(L"\n");
}
PhReleaseQueuedLockShared(&PhDbgProviderListLock);
}
#else
wprintf(commandDebugOnly);
#endif
}
else if (PhEqualStringZ(command, L"workqueues", TRUE))
{
#ifdef DEBUG
ULONG i;
if (PhDbgWorkQueueList)
{
PhAcquireQueuedLockShared(&PhDbgWorkQueueListLock);
for (i = 0; i < PhDbgWorkQueueList->Count; i++)
{
PPH_WORK_QUEUE workQueue = PhDbgWorkQueueList->Items[i];
PLIST_ENTRY workQueueItemEntry;
wprintf(L"Work queue at %s\n", PhpGetSymbolForAddress(workQueue));
wprintf(L"Maximum threads: %lu\n", workQueue->MaximumThreads);
wprintf(L"Minimum threads: %lu\n", workQueue->MinimumThreads);
wprintf(L"No work timeout: %lu\n", workQueue->NoWorkTimeout);
wprintf(L"Current threads: %lu\n", workQueue->CurrentThreads);
wprintf(L"Busy count: %lu\n", workQueue->BusyCount);
PhAcquireQueuedLockExclusive(&workQueue->QueueLock);
// List the items backwards.
workQueueItemEntry = workQueue->QueueListHead.Blink;
while (workQueueItemEntry != &workQueue->QueueListHead)
{
PPH_WORK_QUEUE_ITEM workQueueItem;
workQueueItem = CONTAINING_RECORD(workQueueItemEntry, PH_WORK_QUEUE_ITEM, ListEntry);
wprintf(L"\tWork queue item at %Ix\n", (ULONG_PTR)workQueueItem);
wprintf(L"\t\tFunction: %s\n", PhpGetSymbolForAddress(workQueueItem->Function));
wprintf(L"\t\tContext: %Ix\n", (ULONG_PTR)workQueueItem->Context);
workQueueItemEntry = workQueueItemEntry->Blink;
}
PhReleaseQueuedLockExclusive(&workQueue->QueueLock);
wprintf(L"\n");
}
PhReleaseQueuedLockShared(&PhDbgWorkQueueListLock);
}
#else
wprintf(commandDebugOnly);
#endif
}
else if (PhEqualStringZ(command, L"procrecords", TRUE))
{
PPH_PROCESS_RECORD record;
ULONG i;
SYSTEMTIME systemTime;
PPH_PROCESS_RECORD startRecord;
PhAcquireQueuedLockShared(&PhProcessRecordListLock);
for (i = 0; i < PhProcessRecordList->Count; i++)
{
record = (PPH_PROCESS_RECORD)PhProcessRecordList->Items[i];
PhLargeIntegerToLocalSystemTime(&systemTime, &record->CreateTime);
wprintf(L"Records for %s %s:\n",
((PPH_STRING)PH_AUTO(PhFormatDate(&systemTime, NULL)))->Buffer,
((PPH_STRING)PH_AUTO(PhFormatTime(&systemTime, NULL)))->Buffer
);
startRecord = record;
do
{
wprintf(L"\tRecord at %Ix: %s (%lu) (refs: %ld)\n", (ULONG_PTR)record, record->ProcessName->Buffer, HandleToUlong(record->ProcessId), record->RefCount);
if (record->FileName)
wprintf(L"\t\t%s\n", record->FileName->Buffer);
record = CONTAINING_RECORD(record->ListEntry.Flink, PH_PROCESS_RECORD, ListEntry);
} while (record != startRecord);
}
PhReleaseQueuedLockShared(&PhProcessRecordListLock);
}
else if (PhEqualStringZ(command, L"procitem", TRUE))
{
PWSTR filterString;
PH_STRINGREF filterRef;
ULONG64 filter64;
LONG_PTR processIdFilter = -9; // can't use -2, -1 or 0 because they're all used for process IDs
ULONG_PTR processAddressFilter = 0;
PWSTR imageNameFilter = NULL;
BOOLEAN showAll = FALSE;
PPH_PROCESS_ITEM *processes;
ULONG numberOfProcesses;
ULONG i;
filterString = wcstok_s(NULL, delims, &context);
if (filterString)
{
PhInitializeStringRef(&filterRef, filterString);
if (PhStringToInteger64(&filterRef, 10, &filter64))
processIdFilter = (LONG_PTR)filter64;
if (PhStringToInteger64(&filterRef, 16, &filter64))
processAddressFilter = (ULONG_PTR)filter64;
imageNameFilter = filterString;
}
else
{
showAll = TRUE;
}
PhEnumProcessItems(&processes, &numberOfProcesses);
for (i = 0; i < numberOfProcesses; i++)
{
PPH_PROCESS_ITEM process = processes[i];
if (
showAll ||
(processIdFilter != -9 && (LONG_PTR)process->ProcessId == processIdFilter) ||
(processAddressFilter != 0 && (ULONG_PTR)process == processAddressFilter) ||
(imageNameFilter && PhMatchWildcards(imageNameFilter, process->ProcessName->Buffer, TRUE))
)
{
wprintf(L"Process item at %Ix: %s (%u)\n", (ULONG_PTR)process, process->ProcessName->Buffer, HandleToUlong(process->ProcessId));
wprintf(L"\tRecord at %Ix\n", (ULONG_PTR)process->Record);
wprintf(L"\tQuery handle %Ix\n", (ULONG_PTR)process->QueryHandle);
wprintf(L"\tFile name at %Ix: %s\n", (ULONG_PTR)process->FileName, PhGetStringOrDefault(process->FileName, L"(null)"));
wprintf(L"\tCommand line at %Ix: %s\n", (ULONG_PTR)process->CommandLine, PhGetStringOrDefault(process->CommandLine, L"(null)"));
wprintf(L"\tFlags: %u\n", process->Flags);
wprintf(L"\n");
}
}
PhDereferenceObjects(processes, numberOfProcesses);
}
else if (PhEqualStringZ(command, L"uniquestr", TRUE))
{
#ifdef DEBUG
PLIST_ENTRY currentEntry;
PPH_HASHTABLE hashtable;
PPH_LIST list;
PSTRING_TABLE_ENTRY stringEntry;
ULONG enumerationKey;
ULONG i;
hashtable = PhCreateHashtable(
sizeof(STRING_TABLE_ENTRY),
PhpStringHashtableEqualFunction,
PhpStringHashtableHashFunction,
1024
);
PhAcquireQueuedLockShared(&PhDbgObjectListLock);
currentEntry = PhDbgObjectListHead.Flink;
while (currentEntry != &PhDbgObjectListHead)
{
PPH_OBJECT_HEADER objectHeader;
PPH_STRING string;
STRING_TABLE_ENTRY localStringEntry;
BOOLEAN added;
objectHeader = CONTAINING_RECORD(currentEntry, PH_OBJECT_HEADER, ObjectListEntry);
currentEntry = currentEntry->Flink;
string = PhObjectHeaderToObject(objectHeader);
// Make sure this is a string.
if (PhGetObjectType(string) != PhStringType)
continue;
// Make sure the object isn't being destroyed.
if (!PhReferenceObjectSafe(string))
continue;
localStringEntry.String = string;
stringEntry = PhAddEntryHashtableEx(hashtable, &localStringEntry, &added);
if (added)
{
stringEntry->Count = 1;
PhReferenceObject(string);
}
else
{
stringEntry->Count++;
}
PhDereferenceObjectDeferDelete(string);
}
PhReleaseQueuedLockShared(&PhDbgObjectListLock);
// Sort the string entries by count.
list = PhCreateList(hashtable->Count);
enumerationKey = 0;
while (PhEnumHashtable(hashtable, &stringEntry, &enumerationKey))
{
PhAddItemList(list, stringEntry);
}
qsort(list->Items, list->Count, sizeof(PSTRING_TABLE_ENTRY), PhpStringEntryCompareByCount);
// Display...
for (i = 0; i < 40 && i < list->Count; i++)
{
stringEntry = list->Items[i];
wprintf(L"%Iu\t%.64s\n", stringEntry->Count, stringEntry->String->Buffer);
}
wprintf(L"\nTotal unique strings: %u\n", list->Count);
// Cleanup
for (i = 0; i < list->Count; i++)
{
stringEntry = list->Items[i];
PhDereferenceObject(stringEntry->String);
}
PhDereferenceObject(list);
PhDereferenceObject(hashtable);
#else
wprintf(commandDebugOnly);
#endif
}
else if (PhEqualStringZ(command, L"enableleakdetect", TRUE))
{
HEAP_DEBUGGING_INFORMATION debuggingInfo;
memset(&debuggingInfo, 0, sizeof(HEAP_DEBUGGING_INFORMATION));
debuggingInfo.StackTraceDepth = 32;
debuggingInfo.HeapLeakEnumerationRoutine = PhpLeakEnumerationRoutine;
if (!NT_SUCCESS(RtlSetHeapInformation(NULL, HeapSetDebuggingInformation, &debuggingInfo, sizeof(HEAP_DEBUGGING_INFORMATION))))
{
wprintf(L"Unable to initialize heap debugging. Make sure that you are using Windows 7 or above.");
}
}
else if (PhEqualStringZ(command, L"leakdetect", TRUE))
{
VOID (NTAPI *rtlDetectHeapLeaks)(VOID);
PWSTR options = wcstok_s(NULL, delims, &context);
rtlDetectHeapLeaks = PhGetDllProcedureAddressZ(L"ntdll.dll", "RtlDetectHeapLeaks", 0);
if (!(NtCurrentPeb()->NtGlobalFlag & FLG_USER_STACK_TRACE_DB))
{
wprintf(L"Warning: user-mode stack trace database is not enabled. Stack traces will not be displayed.\n");
}
ShowAllLeaks = FALSE;
if (options)
{
if (PhEqualStringZ(options, L"all", TRUE))
ShowAllLeaks = TRUE;
}
if (rtlDetectHeapLeaks)
{
InLeakDetection = TRUE;
NumberOfLeaks = 0;
NumberOfLeaksShown = 0;
rtlDetectHeapLeaks();
InLeakDetection = FALSE;
wprintf(L"\nNumber of leaks: %lu (%lu displayed)\n", NumberOfLeaks, NumberOfLeaksShown);
}
}
else if (PhEqualStringZ(command, L"mem", TRUE))
{
PWSTR addressString;
PWSTR bytesString;
PH_STRINGREF addressStringRef;
PH_STRINGREF bytesStringRef;
ULONG64 address64;
ULONG64 numberOfBytes64;
PUCHAR address;
ULONG numberOfBytes;
ULONG blockSize;
UCHAR buffer[16];
ULONG i;
addressString = wcstok_s(NULL, delims, &context);
if (!addressString)
goto PrintMemUsage;
bytesString = wcstok_s(NULL, delims, &context);
if (!bytesString)
{
bytesString = L"16";
}
PhInitializeStringRef(&addressStringRef, addressString);
PhInitializeStringRef(&bytesStringRef, bytesString);
if (PhStringToUInt64(&addressStringRef, 16, &address64) && PhStringToUInt64(&bytesStringRef, 10, &numberOfBytes64))
{
address = (PUCHAR)address64;
numberOfBytes = (ULONG)numberOfBytes64;
if (numberOfBytes > 256)
{
wprintf(L"Number of bytes must be 256 or smaller.\n");
goto EndCommand;
}
blockSize = sizeof(buffer);
while (numberOfBytes != 0)
{
if (blockSize > numberOfBytes)
blockSize = numberOfBytes;
__try
{
memcpy(buffer, address, blockSize);
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
wprintf(L"Error reading address near %Ix.\n", (ULONG_PTR)address);
goto EndCommand;
}
// Print hex dump
for (i = 0; i < blockSize; i++)
wprintf(L"%02x ", buffer[i]);
// Fill remaining space (for last, possibly partial block)
for (; i < sizeof(buffer); i++)
wprintf(L" ");
wprintf(L" ");
// Print ASCII dump
for (i = 0; i < blockSize; i++)
putwchar((ULONG)(buffer[i] - ' ') <= (ULONG)('~' - ' ') ? buffer[i] : '.');
wprintf(L"\n");
address += blockSize;
numberOfBytes -= blockSize;
}
}
goto EndCommand;
PrintMemUsage:
wprintf(L"Usage: mem address [numberOfBytes]\n");
wprintf(L"Example: mem 12345678 16\n");
}
else
{
wprintf(L"Unrecognized command.\n");
goto EndCommand; // get rid of the compiler warning about the label being unreferenced
}
EndCommand:
PhDrainAutoPool(&autoPool);
}
PhDereferenceObject(DebugConsoleSymbolProvider);
PhDeleteAutoPool(&autoPool);
return STATUS_SUCCESS;
}
================================================
FILE: SystemInformer/delayhook.c
================================================
/*
* Copyright (c) 2022 Winsider Seminars & Solutions, Inc. All rights reserved.
*
* This file is part of System Informer.
*
* Authors:
*
* dmex 2022-2023
*
*/
#include
#include
#include
#include
#include
#include
#include
#include "settings.h"
#include
// https://learn.microsoft.com/en-us/windows/win32/winmsg/about-window-procedures#window-procedure-superclassing
static WNDPROC PhDefaultMenuWindowProcedure = NULL;
static WNDPROC PhDefaultDialogWindowProcedure = NULL;
static WNDPROC PhDefaultRebarWindowProcedure = NULL;
static WNDPROC PhDefaultComboBoxWindowProcedure = NULL;
static WNDPROC PhDefaultStaticWindowProcedure = NULL;
static WNDPROC PhDefaultStatusbarWindowProcedure = NULL;
static WNDPROC PhDefaultEditWindowProcedure = NULL;
static WNDPROC PhDefaultHeaderWindowProcedure = NULL;
static BOOLEAN PhDefaultEnableStreamerMode = FALSE;
static BOOLEAN PhDefaultEnableThemeAcrylicWindowSupport = FALSE;
static BOOLEAN PhDefaultEnableThemeAnimation = FALSE;
LRESULT CALLBACK PhMenuWindowHookProcedure(
_In_ HWND WindowHandle,
_In_ UINT WindowMessage,
_In_ WPARAM wParam,
_In_ LPARAM lParam
)
{
switch (WindowMessage)
{
case WM_NCCREATE:
{
//CREATESTRUCT* createStruct = (CREATESTRUCT*)lParam;
if (PhEnableThemeSupport)
{
HFONT fontHandle;
LONG windowDpi = PhGetWindowDpi(WindowHandle);
if (fontHandle = PhCreateMessageFont(windowDpi))
{
PhSetWindowContext(WindowHandle, (ULONG)'font', fontHandle);
SetWindowFont(WindowHandle, fontHandle, TRUE);
}
}
}
break;
case WM_CREATE:
{
//CREATESTRUCT* createStruct = (CREATESTRUCT*)lParam;
if (PhDefaultEnableStreamerMode)
{
SetWindowDisplayAffinity(WindowHandle, WDA_EXCLUDEFROMCAPTURE);
}
if (PhEnableThemeSupport)
{
if (PhEnableThemeAcrylicSupport)
{
// Note: DWM crashes if called from WM_NCCREATE (dmex)
PhSetWindowAcrylicCompositionColor(WindowHandle, MakeABGRFromCOLORREF(0, RGB(10, 10, 10)));
}
}
}
break;
case WM_NCDESTROY:
{
if (PhEnableThemeSupport)
{
HFONT fontHandle;
fontHandle = PhGetWindowContext(WindowHandle, (ULONG)'font');
PhRemoveWindowContext(WindowHandle, (ULONG)'font');
if (fontHandle)
{
DeleteFont(fontHandle);
}
}
}
break;
}
return CallWindowProc(PhDefaultMenuWindowProcedure, WindowHandle, WindowMessage, wParam, lParam);
}
LRESULT CALLBACK PhDialogWindowHookProcedure(
_In_ HWND WindowHandle,
_In_ UINT WindowMessage,
_In_ WPARAM wParam,
_In_ LPARAM lParam
)
{
switch (WindowMessage)
{
case WM_CREATE:
{
//CREATESTRUCT* createStruct = (CREATESTRUCT*)lParam;
//IsTopLevelWindow(createStruct->hwndParent)
if (WindowHandle == GetAncestor(WindowHandle, GA_ROOT))
{
if (PhDefaultEnableStreamerMode)
{
SetWindowDisplayAffinity(WindowHandle, WDA_EXCLUDEFROMCAPTURE);
}
if (PhEnableThemeSupport && PhDefaultEnableThemeAcrylicWindowSupport)
{
// Note: DWM crashes if called from WM_NCCREATE (dmex)
PhSetWindowAcrylicCompositionColor(WindowHandle, MakeABGRFromCOLORREF(0, RGB(10, 10, 10)));
}
}
}
break;
}
return CallWindowProc(PhDefaultDialogWindowProcedure, WindowHandle, WindowMessage, wParam, lParam);
}
LRESULT CALLBACK PhRebarWindowHookProcedure(
_In_ HWND WindowHandle,
_In_ UINT WindowMessage,
_In_ WPARAM wParam,
_In_ LPARAM lParam
)
{
switch (WindowMessage)
{
case WM_CTLCOLOREDIT:
{
HDC hdc = (HDC)wParam;
SetBkMode(hdc, TRANSPARENT);
SetTextColor(hdc, PhThemeWindowTextColor);
SetDCBrushColor(hdc, PhThemeWindowBackground2Color);
return (INT_PTR)PhGetStockBrush(DC_BRUSH);
}
break;
}
return CallWindowProc(PhDefaultRebarWindowProcedure, WindowHandle, WindowMessage, wParam, lParam);
}
LRESULT CALLBACK PhComboBoxWindowHookProcedure(
_In_ HWND WindowHandle,
_In_ UINT WindowMessage,
_In_ WPARAM wParam,
_In_ LPARAM lParam
)
{
LRESULT result = CallWindowProc(PhDefaultComboBoxWindowProcedure, WindowHandle, WindowMessage, wParam, lParam);
switch (WindowMessage)
{
case WM_NCCREATE:
{
//CREATESTRUCT* createStruct = (CREATESTRUCT*)lParam;
COMBOBOXINFO info = { sizeof(COMBOBOXINFO) };
if (SendMessage(WindowHandle, CB_GETCOMBOBOXINFO, 0, (LPARAM)&info))
{
if (PhDefaultEnableStreamerMode)
{
SetWindowDisplayAffinity(info.hwndList, WDA_EXCLUDEFROMCAPTURE);
}
}
}
break;
}
return result;
}
LRESULT CALLBACK PhStaticWindowHookProcedure(
_In_ HWND WindowHandle,
_In_ UINT WindowMessage,
_In_ WPARAM wParam,
_In_ LPARAM lParam
)
{
if (WindowMessage == WM_NCCREATE)
{
ULONG style = PhGetWindowStyle(WindowHandle);
if ((style & SS_ICON) == SS_ICON)
{
PhSetWindowContext(WindowHandle, SCHAR_MAX, UlongToPtr(TRUE));
}
}
if (WindowMessage != WM_KILLFOCUS && !PhGetWindowContext(WindowHandle, SCHAR_MAX))
return CallWindowProc(PhDefaultStaticWindowProcedure, WindowHandle, WindowMessage, wParam, lParam);
switch (WindowMessage)
{
case WM_NCDESTROY:
PhRemoveWindowContext(WindowHandle, SCHAR_MAX);
break;
case WM_ERASEBKGND:
return TRUE;
case WM_KILLFOCUS:
{
WCHAR windowClassName[MAX_PATH];
HWND ParentHandle = GetParent(WindowHandle);
if (!GetClassName(ParentHandle, windowClassName, RTL_NUMBER_OF(windowClassName)))
windowClassName[0] = UNICODE_NULL;
if (PhEqualStringZ(windowClassName, L"CHECKLIST_ACLUI", FALSE))
{
RECT rectClient;
if (PhGetClientRect(WindowHandle, &rectClient))
{
PhInflateRect(&rectClient, 2, 2);
MapWindowRect(WindowHandle, ParentHandle, &rectClient);
InvalidateRect(ParentHandle, &rectClient, TRUE); // fix the annoying white border left by the previous active control
}
}
}
break;
case WM_PAINT:
{
HICON iconHandle;
PAINTSTRUCT ps;
RECT clientRect;
WCHAR windowClassName[MAX_PATH];
if (!GetClassName(GetParent(WindowHandle), windowClassName, RTL_NUMBER_OF(windowClassName)))
windowClassName[0] = UNICODE_NULL;
if (PhEqualStringZ(windowClassName, L"CHECKLIST_ACLUI", FALSE))
{
if (iconHandle = (HICON)(UINT_PTR)CallWindowProc(PhDefaultStaticWindowProcedure, WindowHandle, STM_GETICON, 0, 0))
{
static PH_INITONCE initOnce = PH_INITONCE_INIT;
static HFONT hCheckFont = NULL;
HDC hdc = BeginPaint(WindowHandle, &ps);
clientRect = ps.rcPaint;
HDC bufferDc = CreateCompatibleDC(hdc);
HBITMAP bufferBitmap = CreateCompatibleBitmap(hdc, clientRect.right, clientRect.bottom);
HBITMAP oldBufferBitmap = SelectBitmap(bufferDc, bufferBitmap);
enum { nocheck, check, graycheck } checkType = nocheck;
INT startX = clientRect.left + (clientRect.right - clientRect.bottom) / 2 + 1;
INT startY = clientRect.top + (clientRect.bottom - clientRect.top) / 2;
DrawIconEx(bufferDc, clientRect.left, clientRect.top, iconHandle, clientRect.right - clientRect.left, clientRect.bottom - clientRect.top,
0, NULL, DI_NORMAL);
for (INT x = startX, y = startY; x < clientRect.right; x++)
{
COLORREF pixel = GetPixel(bufferDc, x, y);
if (pixel == RGB(0xB4, 0xB4, 0xB4))
{
checkType = graycheck;
goto draw_acl_check;
}
}
for (INT x = startX, y = startY; x < clientRect.right; x++)
{
COLORREF pixel = GetPixel(bufferDc, x, y);
if (pixel == RGB(0, 0, 0) || pixel == PhThemeWindowTextColor)
{
checkType = check;
goto draw_acl_check;
}
}
draw_acl_check:
if (checkType == check || checkType == graycheck) // right is checked or special permission checked
{
if (PhBeginInitOnce(&initOnce)) // cache font
{
hCheckFont = CreateFont(
clientRect.bottom - clientRect.top - 1,
clientRect.right - clientRect.left - 3,
0, 0,
FW_BOLD, FALSE, FALSE, FALSE, DEFAULT_CHARSET, OUT_OUTLINE_PRECIS,
CLIP_DEFAULT_PRECIS, CLEARTYPE_QUALITY,
VARIABLE_PITCH, L"Segoe UI");
PhEndInitOnce(&initOnce);
}
SetBkMode(hdc, TRANSPARENT);
SetTextColor(hdc, checkType == check ? PhThemeWindowTextColor : RGB(0xB4, 0xB4, 0xB4));
SelectFont(hdc, hCheckFont);
//HFONT hFontOriginal = SelectFont(hdc, hCheckFont);
FillRect(hdc, &clientRect, PhThemeWindowBackgroundBrush);
DrawText(hdc, L"✓", 1, &clientRect, DT_CENTER | DT_VCENTER);
//SelectFont(hdc, hFontOriginal);
}
SelectBitmap(bufferDc, oldBufferBitmap);
DeleteBitmap(bufferBitmap);
DeleteDC(bufferDc);
EndPaint(WindowHandle, &ps);
return 0;
}
}
//else if (iconHandle = (HICON)(UINT_PTR)CallWindowProc(PhDefaultStaticWindowProcedure, WindowHandle, STM_GETICON, 0, 0)) // Static_GetIcon(WindowHandle, 0)
//{
// PAINTSTRUCT ps;
// if (PhGetWindowContext(GetParent(WindowHandle), LONG_MAX) &&
// BeginPaint(WindowHandle, &ps))
// {
// // Fix artefacts when window moving back from off-screen (Dart Vanya)
// hdc = GetDC(WindowHandle);
// GetClientRect(WindowHandle, &clientRect);
// FillRect(hdc, &clientRect, PhThemeWindowBackgroundBrush);
// DrawIconEx(
// hdc,
// clientRect.left,
// clientRect.top,
// iconHandle,
// clientRect.right - clientRect.left,
// clientRect.bottom - clientRect.top,
// 0,
// NULL,
// DI_NORMAL
// );
// ReleaseDC(WindowHandle, hdc);
// EndPaint(WindowHandle, &ps);
// }
//}
}
}
return CallWindowProc(PhDefaultStaticWindowProcedure, WindowHandle, WindowMessage, wParam, lParam);
}
typedef struct _PHP_THEME_WINDOW_STATUSBAR_CONTEXT
{
struct
{
BOOLEAN Flags;
union
{
BOOLEAN NonMouseActive : 1;
BOOLEAN MouseActive : 1;
BOOLEAN HotTrack : 1;
BOOLEAN Hot : 1;
BOOLEAN Spare : 4;
};
};
HTHEME ThemeHandle;
POINT CursorPos;
HDC BufferedDc;
HBITMAP BufferedOldBitmap;
HBITMAP BufferedBitmap;
RECT BufferedContextRect;
} PHP_THEME_WINDOW_STATUSBAR_CONTEXT, *PPHP_THEME_WINDOW_STATUSBAR_CONTEXT;
VOID ThemeWindowStatusBarCreateBufferedContext(
_In_ PPHP_THEME_WINDOW_STATUSBAR_CONTEXT Context,
_In_ HDC Hdc,
_In_ PRECT BufferRect
)
{
Context->BufferedDc = CreateCompatibleDC(Hdc);
if (!Context->BufferedDc)
return;
Context->BufferedContextRect = *BufferRect;
Context->BufferedBitmap = CreateCompatibleBitmap(
Hdc,
BufferRect->right,
BufferRect->bottom
);
Context->BufferedOldBitmap = SelectBitmap(Context->BufferedDc, Context->BufferedBitmap);
}
VOID ThemeWindowStatusBarDestroyBufferedContext(
_In_ PPHP_THEME_WINDOW_STATUSBAR_CONTEXT Context
)
{
if (Context->BufferedDc && Context->BufferedOldBitmap)
{
SelectBitmap(Context->BufferedDc, Context->BufferedOldBitmap);
}
if (Context->BufferedBitmap)
{
DeleteBitmap(Context->BufferedBitmap);
Context->BufferedBitmap = NULL;
}
if (Context->BufferedDc)
{
DeleteDC(Context->BufferedDc);
Context->BufferedDc = NULL;
}
}
LONG ThemeWindowStatusBarUpdateRectToIndex(
_In_ HWND WindowHandle,
_In_ WNDPROC WindowProcedure,
_In_ PRECT UpdateRect,
_In_ LONG Count
)
{
for (LONG i = 0; i < Count; i++)
{
RECT blockRect = { 0 };
if (!CallWindowProc(WindowProcedure, WindowHandle, SB_GETRECT, (WPARAM)i, (WPARAM)&blockRect))
continue;
if (
UpdateRect->bottom == blockRect.bottom &&
//UpdateRect->left == blockRect.left &&
UpdateRect->right == blockRect.right
//UpdateRect->top == blockRect.top
)
{
return i;
}
}
return INT_ERROR;
}
VOID ThemeWindowStatusBarDrawPart(
_In_ PPHP_THEME_WINDOW_STATUSBAR_CONTEXT Context,
_In_ HWND WindowHandle,
_In_ HDC bufferDc,
_In_ PRECT clientRect,
_In_ LONG Index
)
{
RECT blockRect = { 0 };
WCHAR text[0x80] = { 0 };
if (!CallWindowProc(PhDefaultStatusbarWindowProcedure, WindowHandle, SB_GETRECT, (WPARAM)Index, (WPARAM)&blockRect))
return;
if (!RectVisible(bufferDc, &blockRect))
return;
if (CallWindowProc(PhDefaultStatusbarWindowProcedure, WindowHandle, SB_GETTEXTLENGTH, (WPARAM)Index, 0) >= RTL_NUMBER_OF(text))
return;
if (!CallWindowProc(PhDefaultStatusbarWindowProcedure, WindowHandle, SB_GETTEXT, (WPARAM)Index, (LPARAM)text))
return;
if (PhPtInRect(&blockRect, &Context->CursorPos))
{
SetTextColor(bufferDc, PhThemeWindowTextColor);
SetDCBrushColor(bufferDc, PhThemeWindowHighlightColor);
blockRect.left -= 3, blockRect.top -= 1;
FillRect(bufferDc, &blockRect, PhGetStockBrush(DC_BRUSH));
blockRect.left += 3, blockRect.top += 1;
}
else
{
RECT separator;
SetTextColor(bufferDc, PhThemeWindowTextColor);
FillRect(bufferDc, &blockRect, PhThemeWindowBackgroundBrush);
separator = blockRect;
separator.left = separator.right - 1;
PhInflateRect(&separator, 0, -1);
SetDCBrushColor(bufferDc, PhThemeWindowHighlightColor);
FillRect(bufferDc, &separator, PhGetStockBrush(DC_BRUSH));
}
blockRect.left += 2, blockRect.bottom -= 1;
DrawText(
bufferDc,
text,
(UINT)PhCountStringZ(text),
&blockRect,
DT_LEFT | DT_VCENTER | DT_SINGLELINE | DT_HIDEPREFIX
);
blockRect.left -= 2, blockRect.bottom += 1;
}
VOID ThemeWindowRenderStatusBar(
_In_ PPHP_THEME_WINDOW_STATUSBAR_CONTEXT Context,
_In_ HWND WindowHandle,
_In_ HDC bufferDc,
_In_ PRECT clientRect
)
{
SetBkMode(bufferDc, TRANSPARENT);
SelectFont(bufferDc, GetWindowFont(WindowHandle));
FillRect(bufferDc, clientRect, PhThemeWindowBackgroundBrush);
LONG blockCount = (LONG)CallWindowProc(
PhDefaultStatusbarWindowProcedure,
WindowHandle,
SB_GETPARTS,
0, 0
);
//INT index = ThemeWindowStatusBarUpdateRectToIndex( // used with BeginBufferedPaint (dmex)
// WindowHandle,
// WindowProcedure,
// clientRect,
// blockCount
// );
//
//if (index == UINT_MAX)
{
RECT sizeGripRect;
LONG dpi;
dpi = PhGetWindowDpi(WindowHandle);
sizeGripRect.left = clientRect->right - PhGetSystemMetrics(SM_CXHSCROLL, dpi);
sizeGripRect.top = clientRect->bottom - PhGetSystemMetrics(SM_CYVSCROLL, dpi);
sizeGripRect.right = clientRect->right;
sizeGripRect.bottom = clientRect->bottom;
if (Context->ThemeHandle)
{
//if (IsThemeBackgroundPartiallyTransparent(Context->ThemeHandle, SP_GRIPPER, 0))
// DrawThemeParentBackground(WindowHandle, bufferDc, NULL);
PhDrawThemeBackground(Context->ThemeHandle, bufferDc, SP_GRIPPER, 0, &sizeGripRect, &sizeGripRect);
}
else
{
DrawFrameControl(bufferDc, &sizeGripRect, DFC_SCROLL, DFCS_SCROLLSIZEGRIP);
}
// Top statusbar border will be drawn by bottom tabcontrol border
for (LONG i = 0; i < blockCount; i++)
{
ThemeWindowStatusBarDrawPart(Context, WindowHandle, bufferDc, clientRect, i);
}
}
//else
//{
// ThemeWindowStatusBarDrawPart(Context, WindowHandle, bufferDc, clientRect, WindowProcedure, index);
//}
}
LRESULT CALLBACK PhStatusBarWindowHookProcedure(
_In_ HWND WindowHandle,
_In_ UINT WindowMessage,
_In_ WPARAM wParam,
_In_ LPARAM lParam
)
{
PPHP_THEME_WINDOW_STATUSBAR_CONTEXT context = NULL;
if (WindowMessage == WM_NCCREATE)
{
context = PhAllocateZero(sizeof(PHP_THEME_WINDOW_STATUSBAR_CONTEXT));
context->ThemeHandle = PhOpenThemeData(WindowHandle, VSCLASS_STATUS, PhGetWindowDpi(WindowHandle));
context->CursorPos.x = LONG_MIN;
context->CursorPos.y = LONG_MIN;
PhSetWindowContext(WindowHandle, LONG_MAX, context);
}
else
{
context = PhGetWindowContext(WindowHandle, LONG_MAX);
}
if (context)
{
switch (WindowMessage)
{
case WM_NCDESTROY:
{
PhRemoveWindowContext(WindowHandle, LONG_MAX);
ThemeWindowStatusBarDestroyBufferedContext(context);
if (context->ThemeHandle)
{
PhCloseThemeData(context->ThemeHandle);
}
PhFree(context);
}
break;
case WM_THEMECHANGED:
{
if (context->ThemeHandle)
{
PhCloseThemeData(context->ThemeHandle);
context->ThemeHandle = NULL;
}
context->ThemeHandle = PhOpenThemeData(WindowHandle, VSCLASS_STATUS, PhGetWindowDpi(WindowHandle));
}
break;
case WM_ERASEBKGND:
return TRUE;
case WM_MOUSEMOVE:
{
if (!context->MouseActive)
{
TRACKMOUSEEVENT trackEvent =
{
sizeof(TRACKMOUSEEVENT),
TME_LEAVE,
WindowHandle,
0
};
TrackMouseEvent(&trackEvent);
context->MouseActive = TRUE;
}
context->CursorPos.x = GET_X_LPARAM(lParam);
context->CursorPos.y = GET_Y_LPARAM(lParam);
InvalidateRect(WindowHandle, NULL, FALSE);
}
break;
case WM_MOUSELEAVE:
{
context->MouseActive = FALSE;
context->CursorPos.x = LONG_MIN;
context->CursorPos.y = LONG_MIN;
InvalidateRect(WindowHandle, NULL, FALSE);
}
break;
case WM_PAINT:
{
//PAINTSTRUCT ps;
//HDC BufferedHDC;
//HPAINTBUFFER BufferedPaint;
//
//if (!BeginPaint(WindowHandle, &ps))
// break;
//
//if (BufferedPaint = BeginBufferedPaint(ps.hdc, &ps.rcPaint, BPBF_COMPATIBLEBITMAP, NULL, &BufferedHDC))
//{
// ThemeWindowRenderStatusBar(context, WindowHandle, BufferedHDC, &ps.rcPaint, oldWndProc);
// EndBufferedPaint(BufferedPaint, TRUE);
//}
//else
{
RECT clientRect;
RECT bufferRect;
HDC hdc;
if (!PhGetClientRect(WindowHandle, &clientRect))
break;
bufferRect.left = 0;
bufferRect.top = 0;
bufferRect.right = clientRect.right - clientRect.left;
bufferRect.bottom = clientRect.bottom - clientRect.top;
hdc = GetDC(WindowHandle);
if (context->BufferedDc && (
context->BufferedContextRect.right < bufferRect.right ||
context->BufferedContextRect.bottom < bufferRect.bottom))
{
ThemeWindowStatusBarDestroyBufferedContext(context);
}
if (!context->BufferedDc)
{
ThemeWindowStatusBarCreateBufferedContext(context, hdc, &bufferRect);
}
if (context->BufferedDc)
{
ThemeWindowRenderStatusBar(
context,
WindowHandle,
context->BufferedDc,
&clientRect
);
BitBlt(hdc, clientRect.left, clientRect.top, clientRect.right, clientRect.bottom, context->BufferedDc, 0, 0, SRCCOPY);
}
ReleaseDC(WindowHandle, hdc);
}
//EndPaint(WindowHandle, &ps);
}
goto DefaultWndProc;
}
}
return CallWindowProc(PhDefaultStatusbarWindowProcedure, WindowHandle, WindowMessage, wParam, lParam);
DefaultWndProc:
return DefWindowProc(WindowHandle, WindowMessage, wParam, lParam);
}
LRESULT CALLBACK PhEditWindowHookProcedure(
_In_ HWND WindowHandle,
_In_ UINT WindowMessage,
_In_ WPARAM wParam,
_In_ LPARAM lParam
)
{
switch (WindowMessage)
{
case WM_NCPAINT:
{
HDC hdc;
ULONG flags;
RECT windowRect;
HRGN updateRegion;
// The searchbox control does its own theme drawing.
if (PhGetWindowContext(WindowHandle, SHRT_MAX))
break;
if (!PhGetWindowRect(WindowHandle, &windowRect))
break;
updateRegion = (HRGN)wParam;
if (updateRegion == HRGN_FULL)
updateRegion = NULL;
flags = DCX_WINDOW | DCX_CACHE | DCX_USESTYLE;
if (updateRegion)
flags |= DCX_INTERSECTRGN | DCX_NODELETERGN;
if (hdc = GetDCEx(WindowHandle, updateRegion, flags))
{
PhOffsetRect(&windowRect, -windowRect.left, -windowRect.top);
if (GetFocus() == WindowHandle)
{
// A little bit nicer and contrast color (Dart Vanya)
SetDCBrushColor(hdc, PhMakeColorBrighter(GetSysColor(COLOR_HOTLIGHT), 85)); // PhThemeWindowHighlightColor
FrameRect(hdc, &windowRect, PhGetStockBrush(DC_BRUSH));
}
else
{
SetDCBrushColor(hdc, PhThemeWindowBackground2Color);
FrameRect(hdc, &windowRect, PhGetStockBrush(DC_BRUSH));
}
ReleaseDC(WindowHandle, hdc);
return 0;
}
}
break;
}
return CallWindowProc(PhDefaultEditWindowProcedure, WindowHandle, WindowMessage, wParam, lParam);
}
typedef struct _PHP_THEME_WINDOW_HEADER_CONTEXT
{
HTHEME ThemeHandle;
BOOLEAN MouseActive;
POINT CursorPos;
} PHP_THEME_WINDOW_HEADER_CONTEXT, *PPHP_THEME_WINDOW_HEADER_CONTEXT;
VOID ThemeWindowRenderHeaderControl(
_In_ PPHP_THEME_WINDOW_HEADER_CONTEXT Context,
_In_ HWND WindowHandle,
_In_ HDC bufferDc,
_In_ PRECT clientRect
)
{
SetBkMode(bufferDc, TRANSPARENT);
SelectFont(bufferDc, GetWindowFont(WindowHandle));
FillRect(bufferDc, clientRect, PhThemeWindowBackgroundBrush);
//INT headerItemCount = Header_GetItemCount(WindowHandle);
INT headerItemCount = (INT)CallWindowProc(
PhDefaultHeaderWindowProcedure,
WindowHandle,
HDM_GETITEMCOUNT,
0, 0
);
for (INT i = 0; i < headerItemCount; i++)
{
RECT headerRect = { 0 };
//Header_GetItemRect(WindowHandle, i, &headerRect);
if (!(BOOL)CallWindowProc(
PhDefaultHeaderWindowProcedure,
WindowHandle,
HDM_GETITEMRECT,
(WPARAM)i,
(LPARAM)&headerRect
))
{
continue;
}
if (PhPtInRect(&headerRect, &Context->CursorPos))
{
SetTextColor(bufferDc, PhThemeWindowTextColor);
SetDCBrushColor(bufferDc, PhThemeWindowBackground2Color); // PhThemeWindowHighlightColor);
FillRect(bufferDc, &headerRect, PhGetStockBrush(DC_BRUSH));
//FrameRect(bufferDc, &headerRect, GetSysColorBrush(COLOR_HIGHLIGHT));
}
else
{
SetTextColor(bufferDc, PhThemeWindowTextColor);
FillRect(bufferDc, &headerRect, PhThemeWindowBackgroundBrush);
//FrameRect(hdc, &headerRect, GetSysColorBrush(COLOR_HIGHLIGHT));
//SetDCPenColor(hdc, RGB(0, 255, 0));
//SetDCBrushColor(hdc, RGB(0, 255, 0));
//DrawEdge(hdc, &headerRect, BDR_RAISEDOUTER | BF_FLAT, BF_RIGHT);
//RECT frameRect;
//frameRect.bottom = headerRect.bottom - 2;
//frameRect.left = headerRect.right - 1;
//frameRect.right = headerRect.right;
//frameRect.top = headerRect.top;
//SetDCBrushColor(hdc, RGB(68, 68, 68)); // RGB(0x77, 0x77, 0x77));
//FrameRect(hdc, &headerRect, PhGetStockBrush(DC_BRUSH));
//PatBlt(DrawInfo->hDC, DrawInfo->rcItem.right - 1, DrawInfo->rcItem.top, 1, DrawInfo->rcItem.bottom - DrawInfo->rcItem.top, PATCOPY);
//PatBlt(DrawInfo->hDC, DrawInfo->rcItem.left, DrawInfo->rcItem.bottom - 1, DrawInfo->rcItem.right - DrawInfo->rcItem.left, 1, PATCOPY);
DrawEdge(bufferDc, &headerRect, BDR_RAISEDOUTER, BF_RIGHT);
}
INT drawTextFlags = DT_SINGLELINE | DT_HIDEPREFIX | DT_WORD_ELLIPSIS;
WCHAR headerText[0x80] = { 0 };
HDITEM headerItem;
ZeroMemory(&headerItem, sizeof(HDITEM));
headerItem.mask = HDI_TEXT | HDI_FORMAT;
headerItem.cchTextMax = MAX_PATH;
headerItem.pszText = headerText;
//Header_GetItem(WindowHandle, i, &headerItem);
if (!(BOOL)CallWindowProc(
PhDefaultHeaderWindowProcedure,
WindowHandle,
HDM_GETITEM,
(WPARAM)i,
(LPARAM)&headerItem
))
{
break;
}
if (headerItem.fmt & HDF_SORTUP)
{
if (Context->ThemeHandle)
{
RECT sortArrowRect = headerRect;
SIZE sortArrowSize;
if (PhGetThemePartSize(
Context->ThemeHandle,
bufferDc,
HP_HEADERSORTARROW,
HSAS_SORTEDUP,
NULL,
THEMEPARTSIZE_TRUE,
&sortArrowSize
))
{
sortArrowRect.bottom = sortArrowSize.cy;
}
PhDrawThemeBackground(
Context->ThemeHandle,
bufferDc,
HP_HEADERSORTARROW,
HSAS_SORTEDUP,
&sortArrowRect,
NULL
);
}
}
else if (headerItem.fmt & HDF_SORTDOWN)
{
if (Context->ThemeHandle)
{
RECT sortArrowRect = headerRect;
SIZE sortArrowSize;
if (PhGetThemePartSize(
Context->ThemeHandle,
bufferDc,
HP_HEADERSORTARROW,
HSAS_SORTEDDOWN,
NULL,
THEMEPARTSIZE_TRUE,
&sortArrowSize
))
{
sortArrowRect.bottom = sortArrowSize.cy;
}
PhDrawThemeBackground(
Context->ThemeHandle,
bufferDc,
HP_HEADERSORTARROW,
HSAS_SORTEDDOWN,
&sortArrowRect,
NULL
);
}
}
if (headerItem.fmt & HDF_RIGHT)
drawTextFlags |= DT_VCENTER | DT_RIGHT;
else
drawTextFlags |= DT_VCENTER | DT_LEFT;
headerRect.left += 4;
headerRect.right -= 8;
DrawText(
bufferDc,
headerText,
(UINT)PhCountStringZ(headerText),
&headerRect,
drawTextFlags
);
}
}
LRESULT CALLBACK PhHeaderWindowHookProcedure(
_In_ HWND WindowHandle,
_In_ UINT WindowMessage,
_In_ WPARAM wParam,
_In_ LPARAM lParam
)
{
PPHP_THEME_WINDOW_HEADER_CONTEXT context = NULL;
if (WindowMessage == WM_NCCREATE)
{
CREATESTRUCT* createStruct = (CREATESTRUCT*)lParam;
if (createStruct->hwndParent)
{
WCHAR windowClassName[MAX_PATH];
if (!GetClassName(createStruct->hwndParent, windowClassName, RTL_NUMBER_OF(windowClassName)))
windowClassName[0] = UNICODE_NULL;
if (PhEqualStringZ(windowClassName, L"PhTreeNew", FALSE))
{
ULONG windowStyle = PhGetWindowStyle(createStruct->hwndParent);
if (BooleanFlagOn(windowStyle, TN_STYLE_CUSTOM_HEADERDRAW))
{
PhSetControlTheme(WindowHandle, L"DarkMode_ItemsView");
return CallWindowProc(PhDefaultHeaderWindowProcedure, WindowHandle, WindowMessage, wParam, lParam);
}
}
}
context = PhAllocateZero(sizeof(PHP_THEME_WINDOW_HEADER_CONTEXT));
context->ThemeHandle = PhOpenThemeData(WindowHandle, VSCLASS_HEADER, PhGetWindowDpi(WindowHandle));
context->CursorPos.x = LONG_MIN;
context->CursorPos.y = LONG_MIN;
PhSetWindowContext(WindowHandle, LONG_MAX, context);
PhSetControlTheme(WindowHandle, L"DarkMode_ItemsView");
InvalidateRect(WindowHandle, NULL, FALSE);
}
else
{
context = PhGetWindowContext(WindowHandle, LONG_MAX);
}
if (context)
{
switch (WindowMessage)
{
case WM_NCDESTROY:
{
PhRemoveWindowContext(WindowHandle, LONG_MAX);
if (context->ThemeHandle)
{
PhCloseThemeData(context->ThemeHandle);
}
PhFree(context);
}
break;
case WM_THEMECHANGED:
{
if (context->ThemeHandle)
{
PhCloseThemeData(context->ThemeHandle);
context->ThemeHandle = NULL;
}
context->ThemeHandle = PhOpenThemeData(WindowHandle, VSCLASS_HEADER, PhGetWindowDpi(WindowHandle));
}
break;
case WM_ERASEBKGND:
return TRUE;
case WM_MOUSEMOVE:
{
if (GetCapture() == WindowHandle)
break;
if (!context->MouseActive)
{
TRACKMOUSEEVENT trackEvent =
{
sizeof(TRACKMOUSEEVENT),
TME_LEAVE,
WindowHandle,
0
};
TrackMouseEvent(&trackEvent);
context->MouseActive = TRUE;
}
context->CursorPos.x = GET_X_LPARAM(lParam);
context->CursorPos.y = GET_Y_LPARAM(lParam);
InvalidateRect(WindowHandle, NULL, FALSE);
}
break;
case WM_CONTEXTMENU:
{
LRESULT result = CallWindowProc(PhDefaultHeaderWindowProcedure, WindowHandle, WindowMessage, wParam, lParam);
InvalidateRect(WindowHandle, NULL, TRUE);
return result;
}
break;
case WM_MOUSELEAVE:
{
LRESULT result = CallWindowProc(PhDefaultHeaderWindowProcedure, WindowHandle, WindowMessage, wParam, lParam);
context->MouseActive = FALSE;
context->CursorPos.x = LONG_MIN;
context->CursorPos.y = LONG_MIN;
InvalidateRect(WindowHandle, NULL, TRUE);
return result;
}
break;
case WM_PAINT:
{
// Don't apply header theme for unsupported dialogs: Advanced Security, Digital Signature Details, etc. (Dart Vanya)
if (!PhIsDarkModeAllowedForWindow(GetParent(WindowHandle)))
{
PhRemoveWindowContext(WindowHandle, LONG_MAX);
if (context->ThemeHandle)
PhCloseThemeData(context->ThemeHandle);
PhFree(context);
PhSetControlTheme(WindowHandle, L"Explorer");
break;
}
//PAINTSTRUCT ps;
//HDC BufferedHDC;
//HPAINTBUFFER BufferedPaint;
//
//if (!BeginPaint(WindowHandle, &ps))
// break;
//
//DEBUG_BEGINPAINT_RECT(WindowHandle, ps.rcPaint);
//
//if (BufferedPaint = BeginBufferedPaint(ps.hdc, &ps.rcPaint, BPBF_COMPATIBLEBITMAP, NULL, &BufferedHDC))
//{
// ThemeWindowRenderHeaderControl(context, WindowHandle, BufferedHDC, &ps.rcPaint, oldWndProc);
// EndBufferedPaint(BufferedPaint, TRUE);
//}
//else
{
RECT clientRect;
HDC hdc;
HDC bufferDc;
HBITMAP bufferBitmap;
HBITMAP oldBufferBitmap;
if (!PhGetClientRect(WindowHandle, &clientRect))
break;
hdc = GetDC(WindowHandle);
bufferDc = CreateCompatibleDC(hdc);
bufferBitmap = CreateCompatibleBitmap(hdc, clientRect.right, clientRect.bottom);
oldBufferBitmap = SelectBitmap(bufferDc, bufferBitmap);
ThemeWindowRenderHeaderControl(context, WindowHandle, bufferDc, &clientRect);
BitBlt(hdc, clientRect.left, clientRect.top, clientRect.right, clientRect.bottom, bufferDc, 0, 0, SRCCOPY);
SelectBitmap(bufferDc, oldBufferBitmap);
DeleteBitmap(bufferBitmap);
DeleteDC(bufferDc);
ReleaseDC(WindowHandle, hdc);
}
//EndPaint(WindowHandle, &ps);
}
goto DefaultWndProc;
}
}
return CallWindowProc(PhDefaultHeaderWindowProcedure, WindowHandle, WindowMessage, wParam, lParam);
DefaultWndProc:
return DefWindowProc(WindowHandle, WindowMessage, wParam, lParam);
}
VOID PhRegisterDialogSuperClass(
VOID
)
{
WNDCLASSEX wcex = { sizeof(WNDCLASSEX) };
if (!GetClassInfoEx(NULL, L"#32770", &wcex))
return;
PhDefaultDialogWindowProcedure = wcex.lpfnWndProc;
wcex.lpfnWndProc = PhDialogWindowHookProcedure;
wcex.style = wcex.style | CS_GLOBALCLASS;
UnregisterClass(L"#32770", NULL);
if (RegisterClassEx(&wcex) == INVALID_ATOM)
{
PhShowStatus(NULL, L"Unable to register window class.", 0, GetLastError());
}
}
VOID PhRegisterMenuSuperClass(
VOID
)
{
WNDCLASSEX wcex = { sizeof(WNDCLASSEX) };
if (!GetClassInfoEx(NULL, L"#32768", &wcex))
return;
PhDefaultMenuWindowProcedure = wcex.lpfnWndProc;
wcex.lpfnWndProc = PhMenuWindowHookProcedure;
wcex.style = wcex.style | CS_GLOBALCLASS;
UnregisterClass(L"#32768", NULL);
if (RegisterClassEx(&wcex) == INVALID_ATOM)
{
PhShowStatus(NULL, L"Unable to register window class.", 0, GetLastError());
}
}
VOID PhRegisterRebarSuperClass(
VOID
)
{
WNDCLASSEX wcex = { sizeof(WNDCLASSEX) };
if (!GetClassInfoEx(NULL, REBARCLASSNAME, &wcex))
return;
PhDefaultRebarWindowProcedure = wcex.lpfnWndProc;
wcex.lpfnWndProc = PhRebarWindowHookProcedure;
wcex.style = wcex.style | CS_GLOBALCLASS;
UnregisterClass(REBARCLASSNAME, NULL);
if (RegisterClassEx(&wcex) == INVALID_ATOM)
{
PhShowStatus(NULL, L"Unable to register window class.", 0, GetLastError());
}
}
VOID PhRegisterComboBoxSuperClass(
VOID
)
{
WNDCLASSEX wcex = { sizeof(WNDCLASSEX) };
if (!GetClassInfoEx(NULL, WC_COMBOBOX, &wcex))
return;
PhDefaultComboBoxWindowProcedure = wcex.lpfnWndProc;
wcex.lpfnWndProc = PhComboBoxWindowHookProcedure;
wcex.style = wcex.style | CS_GLOBALCLASS;
UnregisterClass(WC_COMBOBOX, NULL);
if (RegisterClassEx(&wcex) == INVALID_ATOM)
{
PhShowStatus(NULL, L"Unable to register window class.", 0, GetLastError());
}
}
VOID PhRegisterStaticSuperClass(
VOID
)
{
WNDCLASSEX wcex = { sizeof(WNDCLASSEX) };
if (!GetClassInfoEx(NULL, WC_STATIC, &wcex))
return;
PhDefaultStaticWindowProcedure = wcex.lpfnWndProc;
wcex.lpfnWndProc = PhStaticWindowHookProcedure;
wcex.style = wcex.style | CS_GLOBALCLASS;
UnregisterClass(WC_STATIC, NULL);
if (RegisterClassEx(&wcex) == INVALID_ATOM)
{
PhShowStatus(NULL, L"Unable to register window class.", 0, GetLastError());
}
}
VOID PhRegisterStatusBarSuperClass(
VOID
)
{
WNDCLASSEX wcex = { sizeof(WNDCLASSEX) };
if (!GetClassInfoEx(NULL, STATUSCLASSNAME, &wcex))
return;
PhDefaultStatusbarWindowProcedure = wcex.lpfnWndProc;
wcex.lpfnWndProc = PhStatusBarWindowHookProcedure;
wcex.style = wcex.style | CS_GLOBALCLASS;
UnregisterClass(STATUSCLASSNAME, NULL);
if (RegisterClassEx(&wcex) == INVALID_ATOM)
{
PhShowStatus(NULL, L"Unable to register window class.", 0, GetLastError());
}
}
VOID PhRegisterEditSuperClass(
VOID
)
{
WNDCLASSEX wcex = { sizeof(WNDCLASSEX) };
if (!GetClassInfoEx(NULL, WC_EDIT, &wcex))
return;
PhDefaultEditWindowProcedure = wcex.lpfnWndProc;
wcex.lpfnWndProc = PhEditWindowHookProcedure;
wcex.style = wcex.style | CS_GLOBALCLASS;
UnregisterClass(WC_EDIT, NULL);
if (RegisterClassEx(&wcex) == INVALID_ATOM)
{
PhShowStatus(NULL, L"Unable to register window class.", 0, GetLastError());
}
}
VOID PhRegisterHeaderSuperClass(
VOID
)
{
WNDCLASSEX wcex = { sizeof(WNDCLASSEX) };
if (!GetClassInfoEx(NULL, WC_HEADER, &wcex))
return;
PhDefaultHeaderWindowProcedure = wcex.lpfnWndProc;
wcex.lpfnWndProc = PhHeaderWindowHookProcedure;
wcex.style = wcex.style | CS_GLOBALCLASS;
UnregisterClass(WC_HEADER, NULL);
if (RegisterClassEx(&wcex) == INVALID_ATOM)
{
PhShowStatus(NULL, L"Unable to register window class.", 0, GetLastError());
}
}
// Detours export procedure hooks
static typeof(&DrawThemeBackground) DefaultDrawThemeBackground = NULL;
static typeof(&DrawThemeBackgroundEx) DefaultDrawThemeBackgroundEx = NULL;
static typeof(&DrawThemeText) DefaultDrawThemeText = NULL;
static typeof(&DrawThemeTextEx) DefaultDrawThemeTextEx = NULL;
static typeof(&DrawTextW) DefaultComCtl32DrawTextW = NULL;
static typeof(&TaskDialogIndirect) DefaultTaskDialogIndirect = NULL;
static typeof(&GetThemeColor) DefaultGetThemeColor = NULL;
static typeof(&SystemParametersInfoW) DefaultSystemParametersInfo = NULL;
static typeof(&CreateWindowExW) DefaultCreateWindowEx = NULL;
// uxtheme.dll ordinal 49
static HTHEME(WINAPI* DefaultOpenNcThemeData)(
_In_ HWND hwnd,
_In_ LPCWSTR pszClassList
) = NULL;
typedef struct _TASKDIALOG_CALLBACK_WRAP
{
PFTASKDIALOGCALLBACK pfCallback;
LONG_PTR lpCallbackData;
} TASKDIALOG_CALLBACK_WRAP, *PTASKDIALOG_CALLBACK_WRAP;
typedef struct _TASKDIALOG_COMMON_CONTEXT
{
WNDPROC DefaultWindowProc;
ULONG Painting;
} TASKDIALOG_COMMON_CONTEXT, *PTASKDIALOG_COMMON_CONTEXT;
typedef struct _TASKDIALOG_WINDOW_CONTEXT
{
WNDPROC DefaultWindowProc;
ULONG Painting;
PTASKDIALOG_CALLBACK_WRAP CallbackData;
} TASKDIALOG_WINDOW_CONTEXT, *PTASKDIALOG_WINDOW_CONTEXT;
#define TASKDIALOG_CONTEXT_TAG (ULONG)'TDLG'
#define GETCLASSNAME_OR_NULL(WindowHandle, ClassName) if (!GetClassName(WindowHandle, ClassName, RTL_NUMBER_OF(ClassName))) ClassName[0] = UNICODE_NULL
HRESULT CALLBACK ThemeTaskDialogCallbackHook(
_In_ HWND hwndDlg,
_In_ UINT uMsg,
_In_ WPARAM wParam,
_In_ LPARAM lParam,
_In_ LONG_PTR dwRefData
);
LRESULT CALLBACK ThemeTaskDialogMasterSubclass(
_In_ HWND hwnd,
_In_ UINT uMsg,
_In_ WPARAM wParam,
_In_ LPARAM lParam
);
_Function_class_(PH_WINDOW_ENUM_CALLBACK)
BOOLEAN CALLBACK PhInitializeTaskDialogTheme(
_In_ HWND hwndDlg,
_In_opt_ PVOID Context
);
HRESULT PhDrawThemeBackgroundHook(
_In_ HTHEME Theme,
_In_ HDC Hdc,
_In_ LONG PartId,
_In_ LONG StateId,
_In_ LPCRECT Rect,
_In_ LPCRECT ClipRect
)
{
WCHAR className[MAX_PATH];
BOOLEAN hasThemeClass = PhGetThemeClass(Theme, className, RTL_NUMBER_OF(className));
if (WindowsVersion >= WINDOWS_11 && hasThemeClass)
{
if (PhEqualStringZ(className, VSCLASS_MENU, TRUE))
{
if (PartId == MENU_POPUPGUTTER || PartId == MENU_POPUPBORDERS)
{
FillRect(Hdc, Rect, PhThemeWindowBackgroundBrush);
return S_OK;
}
}
}
if (hasThemeClass && PhEqualStringZ(className, VSCLASS_PROGRESS, TRUE)
/*|| WindowsVersion < WINDOWS_11 && WindowFromDC(Hdc) == NULL*/)
{
if (PartId == PP_TRANSPARENTBAR || PartId == PP_TRANSPARENTBARVERT) // Progress bar background
{
FillRect(Hdc, Rect, PhThemeWindowBackgroundBrush);
SetDCBrushColor(Hdc, RGB(0x60, 0x60, 0x60));
FrameRect(Hdc, Rect, PhGetStockBrush(DC_BRUSH));
return S_OK;
}
}
return DefaultDrawThemeBackground(Theme, Hdc, PartId, StateId, Rect, ClipRect);
}
HRESULT WINAPI PhDrawThemeBackgroundExHook(
_In_ HTHEME hTheme,
_In_ HDC hdc,
_In_ int iPartId,
_In_ int iStateId,
_In_ LPCRECT pRect,
_In_ const DTBGOPTS* pOptions
)
{
WCHAR className[MAX_PATH];
// Apply theme to ListView checkboxes
if (iPartId == BP_CHECKBOX /*|| iPartId == BP_RADIOBUTTON*/)
{
if (PhGetThemeClass(hTheme, className, RTL_NUMBER_OF(className)) &&
PhEqualStringZ(className, VSCLASS_BUTTON, TRUE))
{
HTHEME darkButtonTheme = PhOpenThemeData(NULL, L"DarkMode_Explorer::Button", 0);
HRESULT retVal = DefaultDrawThemeBackgroundEx(darkButtonTheme ? darkButtonTheme : hTheme, hdc, iPartId, iStateId, pRect, pOptions);
if (darkButtonTheme)
PhCloseThemeData(darkButtonTheme);
return retVal;
}
}
// Micro optimization
if ((iPartId == TDLG_PRIMARYPANEL || iPartId == TDLG_FOOTNOTEPANE || iPartId == TDLG_SECONDARYPANEL || iPartId == TDLG_FOOTNOTESEPARATOR || iPartId == TDLG_EXPANDOBUTTON) &&
PhGetThemeClass(hTheme, className, RTL_NUMBER_OF(className)) && PhEqualStringZ(className, VSCLASS_TASKDIALOG, TRUE)
/*|| WindowsVersion < WINDOWS_11 && WindowFromDC(hdc) == NULL*/)
{
switch (iPartId)
{
case TDLG_PRIMARYPANEL:
SetDCBrushColor(hdc, PhThemeWindowBackground2Color);
FillRect(hdc, pRect, PhGetStockBrush(DC_BRUSH));
return S_OK;
case TDLG_FOOTNOTEPANE:
FillRect(hdc, pRect, PhThemeWindowBackgroundBrush);
return S_OK;
case TDLG_SECONDARYPANEL:
{
FillRect(hdc, pRect, PhThemeWindowBackgroundBrush);
RECT rect = *pRect;
rect.bottom = rect.top + 1;
SetDCBrushColor(hdc, PhThemeWindowForegroundColor);
FillRect(hdc, &rect, PhGetStockBrush(DC_BRUSH));
PhOffsetRect(&rect, 0, 1);
SetDCBrushColor(hdc, PhThemeWindowBackground2Color);
FillRect(hdc, &rect, PhGetStockBrush(DC_BRUSH));
return S_OK;
}
case TDLG_FOOTNOTESEPARATOR:
{
SetDCBrushColor(hdc, PhThemeWindowForegroundColor);
FillRect(hdc, pRect, PhGetStockBrush(DC_BRUSH));
RECT rect = *pRect;
rect.top += 1;
SetDCBrushColor(hdc, PhThemeWindowBackground2Color);
FillRect(hdc, &rect, PhGetStockBrush(DC_BRUSH));
return S_OK;
}
case TDLG_EXPANDOBUTTON:
if (WindowsVersion >= WINDOWS_11)
{
// In Windows 11, buttons lack background, making them indistinguishable on dark backgrounds.
// To address this, we invert the button. This technique isn't applicable to Windows 10 as it causes the button's border to appear chipped.
static enum { yes, no, unknown } mustInvertButton = unknown;
if (mustInvertButton == unknown)
{
DefaultDrawThemeBackgroundEx(hTheme, hdc, iPartId, iStateId, pRect, pOptions);
int buttonCenterX = pOptions->rcClip.left + (pOptions->rcClip.right - pOptions->rcClip.left) / 2;
int buttonCenterY = pOptions->rcClip.top + (pOptions->rcClip.bottom - pOptions->rcClip.top) / 2;
COLORREF centerPixel = GetPixel(hdc, buttonCenterX, buttonCenterY);
mustInvertButton = centerPixel == PhThemeWindowTextColor ? no : yes;
}
FillRect(hdc, pRect, PhThemeWindowBackgroundBrush);
if (mustInvertButton == yes) InvertRect(hdc, pRect);
HRESULT retVal = DefaultDrawThemeBackgroundEx(hTheme, hdc, iPartId, iStateId, pRect, pOptions);
if (mustInvertButton == yes) InvertRect(hdc, pRect);
return retVal;
}
}
}
return DefaultDrawThemeBackgroundEx(hTheme, hdc, iPartId, iStateId, pRect, pOptions);
}
HWND PhCreateWindowExHook(
_In_ ULONG ExStyle,
_In_opt_ PCWSTR ClassName,
_In_opt_ PCWSTR WindowName,
_In_ ULONG Style,
_In_ LONG X,
_In_ LONG Y,
_In_ LONG Width,
_In_ LONG Height,
_In_opt_ HWND Parent,
_In_opt_ HMENU Menu,
_In_opt_ PVOID Instance,
_In_opt_ PVOID Param
)
{
HWND windowHandle = DefaultCreateWindowEx(
ExStyle,
ClassName,
WindowName,
Style,
X,
Y,
Width,
Height,
Parent,
Menu,
Instance,
Param
);
if (Parent == NULL)
{
if (PhDefaultEnableStreamerMode)
{
SetWindowDisplayAffinity(windowHandle, WDA_EXCLUDEFROMCAPTURE);
}
if (PhEnableThemeSupport && PhDefaultEnableThemeAcrylicWindowSupport)
{
PhSetWindowAcrylicCompositionColor(windowHandle, MakeABGRFromCOLORREF(0, RGB(10, 10, 10)));
}
}
else if (PhEnableThemeSupport)
{
// Early subclassing of the SysLink control to eliminate blinking during page switches.
if (!IS_INTRESOURCE(ClassName) && PhEqualStringZ(ClassName, WC_LINK, TRUE))
{
PhInitializeTaskDialogTheme(windowHandle, 0);
}
else if (!IS_INTRESOURCE(ClassName) && PhEqualStringZ(ClassName, WC_BUTTON, TRUE) &&
PhGetWindowContext(GetAncestor(Parent, GA_ROOT), LONG_MAX))
{
PhSetControlTheme(windowHandle, L"DarkMode_Explorer");
}
}
return windowHandle;
}
BOOL WINAPI PhSystemParametersInfoHook(
_In_ UINT uiAction,
_In_ UINT uiParam,
_Pre_maybenull_ _Post_valid_ PVOID pvParam,
_In_ UINT fWinIni
)
{
if (uiAction == SPI_GETMENUFADE && pvParam)
{
*((PBOOL)pvParam) = FALSE;
return TRUE;
}
if (uiAction == SPI_GETCLIENTAREAANIMATION && pvParam)
{
*((PBOOL)pvParam) = FALSE;
return TRUE;
}
if (uiAction == SPI_GETCOMBOBOXANIMATION && pvParam)
{
*((PBOOL)pvParam) = FALSE;
return TRUE;
}
if (uiAction == SPI_GETTOOLTIPANIMATION && pvParam)
{
*((PBOOL)pvParam) = FALSE;
return TRUE;
}
if (uiAction == SPI_GETMENUANIMATION && pvParam)
{
*((PBOOL)pvParam) = FALSE;
return TRUE;
}
if (uiAction == SPI_GETTOOLTIPFADE && pvParam)
{
*((PBOOL)pvParam) = FALSE;
return TRUE;
}
if (uiAction == SPI_GETMOUSEVANISH && pvParam)
{
*((PBOOL)pvParam) = FALSE;
return TRUE;
}
return DefaultSystemParametersInfo(uiAction, uiParam, pvParam, fWinIni);
}
//ULONG WINAPI GetSysColorHook(int nIndex)
//{
// if (nIndex == COLOR_WINDOW)
// return PhThemeWindowTextColor;
// if (nIndex == COLOR_MENUTEXT)
// return PhThemeWindowTextColor;
// if (nIndex == COLOR_WINDOWTEXT)
// return PhThemeWindowTextColor;
// if (nIndex == COLOR_CAPTIONTEXT)
// return PhThemeWindowTextColor;
// if (nIndex == COLOR_HIGHLIGHTTEXT)
// return PhThemeWindowTextColor;
// if (nIndex == COLOR_GRAYTEXT)
// return PhThemeWindowTextColor;
// if (nIndex == COLOR_BTNTEXT)
// return PhThemeWindowTextColor;
// if (nIndex == COLOR_INACTIVECAPTIONTEXT)
// return PhThemeWindowTextColor;
// if (nIndex == COLOR_BTNFACE)
// return PhThemeWindowBackgroundColor;
// if (nIndex == COLOR_BTNTEXT)
// return PhThemeWindowTextColor;
// if (nIndex == COLOR_BTNHIGHLIGHT)
// return PhThemeWindowBackgroundColor;
// if (nIndex == COLOR_INFOTEXT)
// return PhThemeWindowTextColor;
// if (nIndex == COLOR_INFOBK)
// return PhThemeWindowBackgroundColor;
// if (nIndex == COLOR_MENU)
// return PhThemeWindowBackgroundColor;
// if (nIndex == COLOR_HIGHLIGHT)
// return PhThemeWindowForegroundColor;
// return originalGetSysColor(nIndex);
//}
//
//HBRUSH WINAPI GetSysColorBrushHook(_In_ int nIndex)
//{
// //if (nIndex == COLOR_WINDOW)
// // return originalCreateSolidBrush(PhThemeWindowBackgroundColor);
// if (nIndex == COLOR_BTNFACE)
// return originalCreateSolidBrush(PhThemeWindowBackgroundColor);
// return originalHook(nIndex);
//}
//
// RGB(GetBValue(color), GetGValue(color), GetRValue(color));
//#define RGB_FROM_COLOREF(cref) \
// ((((cref) & 0x000000FF) << 16) | (((cref) & 0x0000FF00)) | (((cref) & 0x00FF0000) >> 16))
HRESULT WINAPI PhDrawThemeTextHook(
_In_ HTHEME hTheme,
_In_ HDC hdc,
_In_ int iPartId,
_In_ int iStateId,
_In_ LPCWSTR pszText,
_In_ int cchText,
_In_ DWORD dwTextFlags,
_In_ DWORD dwTextFlags2,
_In_ LPCRECT pRect
)
{
if ((iPartId == BP_COMMANDLINK /*|| iPartId == BP_RADIOBUTTON*/) && iStateId != PBS_DISABLED)
{
WCHAR className[MAX_PATH];
if (PhGetThemeClass(hTheme, className, RTL_NUMBER_OF(className)) && PhEqualStringZ(className, VSCLASS_BUTTON, TRUE)
/*|| WindowsVersion < WINDOWS_11 && WindowFromDC(hdc) == NULL*/)
{
DTTOPTS options = { sizeof(DTTOPTS), DTT_TEXTCOLOR, PhThemeWindowTextColor };
return DefaultDrawThemeTextEx(hTheme, hdc, iPartId, iStateId, pszText, cchText, dwTextFlags, (LPRECT)pRect, &options);
}
}
return DefaultDrawThemeText(hTheme, hdc, iPartId, iStateId, pszText, cchText, dwTextFlags, dwTextFlags2, pRect);
}
HRESULT WINAPI PhDrawThemeTextExHook(
_In_ HTHEME hTheme,
_In_ HDC hdc,
_In_ int iPartId,
_In_ int iStateId,
_In_ LPCWSTR pszText,
_In_ int cchText,
_In_ DWORD dwTextFlags,
_Inout_ LPRECT pRect,
_In_ const DTTOPTS* pOptions
)
{
if (iPartId == BP_COMMANDLINK)
{
WCHAR className[MAX_PATH];
if (PhGetThemeClass(hTheme, className, RTL_NUMBER_OF(className)) && PhEqualStringZ(className, VSCLASS_BUTTON, TRUE)
/*|| WindowsVersion < WINDOWS_11 && WindowFromDC(hdc) == NULL*/)
{
DTTOPTS options = { sizeof(DTTOPTS) };
if (pOptions)
options = *pOptions;
options.dwFlags |= DTT_TEXTCOLOR;
DefaultGetThemeColor(hTheme, iPartId, iStateId, TMT_TEXTCOLOR, &options.crText);
options.crText = PhMakeColorBrighter(options.crText, 90);
return DefaultDrawThemeTextEx(hTheme, hdc, iPartId, iStateId, pszText, cchText, dwTextFlags, pRect, &options);
}
}
return DefaultDrawThemeTextEx(hTheme, hdc, iPartId, iStateId, pszText, cchText, dwTextFlags, pRect, pOptions);
}
int PhDetoursComCtl32DrawTextW(
_In_ HDC hdc,
_Inout_ LPCWSTR lpchText,
_In_ int cchText,
_Inout_ LPRECT lprc,
_In_ UINT format
)
{
static PH_INITONCE initOnce = PH_INITONCE_INIT;
static COLORREF colLinkNormal = RGB(0, 0 ,0);
static COLORREF colLinkHot = RGB(0, 0, 0);
static COLORREF colLinkPressed = RGB(0, 0, 0);
HWND WindowHandle;
if ((WindowHandle = WindowFromDC(hdc)) &&
(PhIsDarkModeAllowedForWindow(WindowHandle) || PhGetWindowContext(WindowHandle, TASKDIALOG_CONTEXT_TAG))) // HACK
{
WCHAR windowClassName[MAX_PATH];
GETCLASSNAME_OR_NULL(WindowHandle, windowClassName);
if (PhEqualStringZ(windowClassName, WC_LINK, FALSE))
{
if (PhBeginInitOnce(&initOnce))
{
HTHEME hTextTheme = PhOpenThemeData(WindowHandle, VSCLASS_TEXTSTYLE, PhGetWindowDpi(WindowHandle));
if (hTextTheme)
{
PhGetThemeColor(hTextTheme, TEXT_HYPERLINKTEXT, TS_HYPERLINK_NORMAL, TMT_TEXTCOLOR, &colLinkNormal);
PhGetThemeColor(hTextTheme, TEXT_HYPERLINKTEXT, TS_HYPERLINK_HOT, TMT_TEXTCOLOR, &colLinkHot);
PhGetThemeColor(hTextTheme, TEXT_HYPERLINKTEXT, TS_HYPERLINK_PRESSED, TMT_TEXTCOLOR, &colLinkPressed);
PhCloseThemeData(hTextTheme);
}
PhEndInitOnce(&initOnce);
}
COLORREF color = GetTextColor(hdc);
if (color == colLinkNormal || color == colLinkHot || color == colLinkPressed ||
WindowsVersion < WINDOWS_11 && color == RGB(0x00, 0x66, 0xCC)) // on Windows 10 PhGetThemeColor returns 0xFFFFFF for any StateId
{
SetTextColor(hdc, PhMakeColorBrighter(color, 95));
}
}
}
return DefaultComCtl32DrawTextW(hdc, lpchText, cchText, lprc, format);
}
HRESULT PhGetThemeColorHook(
_In_ HTHEME hTheme,
_In_ int iPartId,
_In_ int iStateId,
_In_ int iPropId,
_Out_ COLORREF* pColor
)
{
WCHAR className[MAX_PATH];
HRESULT retVal = DefaultGetThemeColor(hTheme, iPartId, iStateId, iPropId, pColor);
if (iPropId == TMT_TEXTCOLOR && iPartId == TDLG_MAININSTRUCTIONPANE)
{
if (PhGetThemeClass(hTheme, className, RTL_NUMBER_OF(className)) && PhEqualStringZ(className, VSCLASS_TASKDIALOGSTYLE, TRUE)
/*|| WindowsVersion < WINDOWS_11*/)
{
*pColor = PhMakeColorBrighter(*pColor, 150); // Main header.
}
}
else if (iPropId == TMT_TEXTCOLOR)
{
if (PhGetThemeClass(hTheme, className, RTL_NUMBER_OF(className)) && PhEqualStringZ(className, VSCLASS_TASKDIALOGSTYLE, TRUE)
/*|| WindowsVersion < WINDOWS_11*/)
{
*pColor = PhThemeWindowTextColor; // Text color for check boxes, expanded text, and expander button text.
}
}
return retVal;
}
HTHEME PhOpenNcThemeDataHook(
_In_ HWND hwnd,
_In_ LPCWSTR pszClassList
)
{
if (PhEqualStringZ((PWSTR)pszClassList, VSCLASS_SCROLLBAR, TRUE) &&
PhIsDarkModeAllowedForWindow(hwnd))
{
return DefaultOpenNcThemeData(NULL, L"Explorer::ScrollBar");
}
return DefaultOpenNcThemeData(hwnd, pszClassList);
}
_Function_class_(PH_WINDOW_ENUM_CALLBACK)
BOOLEAN CALLBACK PhInitializeTaskDialogTheme(
_In_ HWND WindowHandle,
_In_opt_ PVOID CallbackData
)
{
WCHAR windowClassName[MAX_PATH];
PTASKDIALOG_COMMON_CONTEXT context;
BOOLEAN windowHasContext = !!PhGetWindowContext(WindowHandle, TASKDIALOG_CONTEXT_TAG);
if (CallbackData && !windowHasContext)
{
if (PhDefaultEnableStreamerMode)
{
SetWindowDisplayAffinity(WindowHandle, WDA_EXCLUDEFROMCAPTURE);
}
PhInitializeThemeWindowFrame(WindowHandle);
PTASKDIALOG_WINDOW_CONTEXT context = PhAllocateZero(sizeof(TASKDIALOG_WINDOW_CONTEXT));
context->DefaultWindowProc = PhSetWindowProcedure(WindowHandle, ThemeTaskDialogMasterSubclass);
context->CallbackData = CallbackData;
PhSetWindowContext(WindowHandle, TASKDIALOG_CONTEXT_TAG, context);
windowHasContext = TRUE;
}
PhEnumChildWindows(
WindowHandle,
PhInitializeTaskDialogTheme,
NULL
);
if (windowHasContext) // HACK
return TRUE;
GETCLASSNAME_OR_NULL(WindowHandle, windowClassName);
context = PhAllocateZero(sizeof(TASKDIALOG_COMMON_CONTEXT));
context->DefaultWindowProc = PhSetWindowProcedure(WindowHandle, ThemeTaskDialogMasterSubclass);
PhSetWindowContext(WindowHandle, TASKDIALOG_CONTEXT_TAG, context);
if (PhEqualStringZ(windowClassName, WC_BUTTON, FALSE) ||
PhEqualStringZ(windowClassName, WC_SCROLLBAR, FALSE))
{
PhSetControlTheme(WindowHandle, L"DarkMode_Explorer");
}
//else if (PhEqualStringZ(windowClassName, WC_LINK, FALSE))
//{
// PhAllowDarkModeForWindow(WindowHandle); // this doesn't work, idk why
//}
else if (PhEqualStringZ(windowClassName, L"DirectUIHWND", FALSE))
{
//WINDOWPLACEMENT pos = { 0 };
//GetWindowPlacement(GetParent(WindowHandle), &pos);
PhSetControlTheme(WindowHandle, L"DarkMode_Explorer");
//SetWindowPlacement(GetParent(WindowHandle), &pos);
}
return TRUE;
}
LRESULT CALLBACK ThemeTaskDialogMasterSubclass(
_In_ HWND hwnd,
_In_ UINT uMsg,
_In_ WPARAM wParam,
_In_ LPARAM lParam
)
{
LRESULT result;
PTASKDIALOG_COMMON_CONTEXT context;
WNDPROC OldWndProc;
if (!(context = PhGetWindowContext(hwnd, TASKDIALOG_CONTEXT_TAG)))
return 0;
OldWndProc = context->DefaultWindowProc;
switch (uMsg)
{
case WM_ERASEBKGND:
{
HDC hdc = (HDC)wParam;
RECT rect;
WCHAR windowClassName[MAX_PATH];
SetTextColor(hdc, PhThemeWindowTextColor); // Color for SysLink, which must be set in its parent.
if (!context->Painting)
{
GETCLASSNAME_OR_NULL(hwnd, windowClassName);
// Avoid erasing the background for links, as they will blink white on the extender and during page switches.
if (!PhEqualStringZ(windowClassName, WC_LINK, FALSE))
{
GetClipBox(hdc, &rect);
SetDCBrushColor(hdc, PhThemeWindowBackground2Color);
FillRect(hdc, &rect, PhGetStockBrush(DC_BRUSH));
}
}
}
return TRUE;
case WM_NOTIFY:
{
LPNMHDR data = (LPNMHDR)lParam;
if (data->code == NM_CUSTOMDRAW)
{
LPNMCUSTOMDRAW customDraw = (LPNMCUSTOMDRAW)lParam;
WCHAR className[MAX_PATH];
if (!GetClassName(customDraw->hdr.hwndFrom, className, RTL_NUMBER_OF(className)))
className[0] = UNICODE_NULL;
if (PhEqualStringZ(className, WC_BUTTON, FALSE))
{
return PhThemeWindowDrawButton(customDraw);
}
}
}
break;
case TDM_NAVIGATE_PAGE:
{
PTASKDIALOG_WINDOW_CONTEXT WindowContext = (PTASKDIALOG_WINDOW_CONTEXT)context;
PTASKDIALOGCONFIG trueConfig = (PTASKDIALOGCONFIG)lParam;
PTASKDIALOGCONFIG myConfig;
TASKDIALOGCONFIG config = { sizeof(TASKDIALOGCONFIG) };
WindowContext->CallbackData->pfCallback = trueConfig ? trueConfig->pfCallback : NULL;
WindowContext->CallbackData->lpCallbackData = trueConfig ? trueConfig->lpCallbackData : 0;
myConfig = trueConfig ? trueConfig : &config;
myConfig->pfCallback = ThemeTaskDialogCallbackHook;
myConfig->lpCallbackData = (LONG_PTR)WindowContext->CallbackData;
return CallWindowProc(OldWndProc, hwnd, uMsg, wParam, (LPARAM)myConfig);
}
case WM_DESTROY:
{
PhSetWindowProcedure(hwnd, OldWndProc);
PhRemoveWindowContext(hwnd, TASKDIALOG_CONTEXT_TAG);
PhFree(context);
}
return CallWindowProc(OldWndProc, hwnd, uMsg, wParam, lParam);
case WM_CTLCOLORDLG:
return (LRESULT)PhThemeWindowBackgroundBrush; // Window background color when the extender resizes upward (Windows 10 only).
}
context->Painting++;
result = CallWindowProc(OldWndProc, hwnd, uMsg, wParam, lParam);
context->Painting--;
return result;
}
HRESULT CALLBACK ThemeTaskDialogCallbackHook(
_In_ HWND hwndDlg,
_In_ UINT uMsg,
_In_ WPARAM wParam,
_In_ LPARAM lParam,
_In_ LONG_PTR dwRefData
)
{
HRESULT result = S_OK;
PTASKDIALOG_CALLBACK_WRAP CallbackData = (PTASKDIALOG_CALLBACK_WRAP)dwRefData;
if (uMsg == TDN_DIALOG_CONSTRUCTED) // Called on each new page, including the first one.
{
PhInitializeTaskDialogTheme(hwndDlg, CallbackData);
}
if (CallbackData->pfCallback)
result = CallbackData->pfCallback(hwndDlg, uMsg, wParam, lParam, CallbackData->lpCallbackData);
return result;
}
// https://github.com/SFTRS/DarkTaskDialog
HRESULT PhTaskDialogIndirectHook(
_In_ const TASKDIALOGCONFIG* pTaskConfig,
_Out_opt_ int* pnButton,
_Out_opt_ int* pnRadioButton,
_Out_opt_ BOOL* pfVerificationFlagChecked
)
{
TASKDIALOG_CALLBACK_WRAP CallbackData;
CallbackData.pfCallback = pTaskConfig->pfCallback;
CallbackData.lpCallbackData = pTaskConfig->lpCallbackData;
TASKDIALOGCONFIG myConfig = *pTaskConfig;
myConfig.pfCallback = ThemeTaskDialogCallbackHook;
myConfig.lpCallbackData = (LONG_PTR)&CallbackData;
return DefaultTaskDialogIndirect(&myConfig, pnButton, pnRadioButton, pfVerificationFlagChecked);
}
VOID PhRegisterDetoursHooks(
VOID
)
{
NTSTATUS status;
PVOID baseAddress;
// For early TaskDialog with PhStartupParameters.ShowOptions
if (!PhThemeWindowBackgroundBrush)
{
PhThemeWindowBackgroundBrush = CreateSolidBrush(PhThemeWindowBackgroundColor);
}
if (baseAddress = PhGetLoaderEntryDllBaseZ(L"user32.dll"))
{
DefaultCreateWindowEx = PhGetDllBaseProcedureAddress(baseAddress, "CreateWindowExW", 0);
DefaultSystemParametersInfo = PhGetDllBaseProcedureAddress(baseAddress, "SystemParametersInfoW", 0);
}
if (baseAddress = PhGetLoaderEntryDllBaseZ(L"uxtheme.dll"))
{
DefaultDrawThemeBackground = PhGetDllBaseProcedureAddress(baseAddress, "DrawThemeBackground", 0);
DefaultDrawThemeBackgroundEx = PhGetDllBaseProcedureAddress(baseAddress, "DrawThemeBackgroundEx", 0);
DefaultDrawThemeText = PhGetDllBaseProcedureAddress(baseAddress, "DrawThemeText", 0);
DefaultDrawThemeTextEx = PhGetDllBaseProcedureAddress(baseAddress, "DrawThemeTextEx", 0);
DefaultGetThemeColor = PhGetDllBaseProcedureAddress(baseAddress, "GetThemeColor", 0);
DefaultOpenNcThemeData = PhGetDllBaseProcedureAddress(baseAddress, NULL, 49);
}
if (baseAddress = PhGetLoaderEntryDllBaseZ(L"Comctl32.dll"))
{
if (WindowsVersion >= WINDOWS_11) // TaskDialog theme on Windows 10 currently unsupported...
DefaultTaskDialogIndirect = PhGetDllBaseProcedureAddress(baseAddress, "TaskDialogIndirect", 0);
PhLoaderEntryDetourImportProcedure(baseAddress, "User32.dll", "DrawTextW", PhDetoursComCtl32DrawTextW, (PVOID*)&DefaultComCtl32DrawTextW);
}
if (!NT_SUCCESS(status = DetourTransactionBegin()))
goto CleanupExit;
if (PhEnableThemeSupport || PhEnableThemeAcrylicSupport)
{
if (!NT_SUCCESS(status = DetourAttach((PVOID)&DefaultDrawThemeBackground, (PVOID)PhDrawThemeBackgroundHook)))
goto CleanupExit;
if (!NT_SUCCESS(status = DetourAttach((PVOID)&DefaultDrawThemeBackgroundEx, (PVOID)PhDrawThemeBackgroundExHook)))
goto CleanupExit;
if (!PhDefaultEnableThemeAnimation)
{
if (!NT_SUCCESS(status = DetourAttach((PVOID)&DefaultSystemParametersInfo, (PVOID)PhSystemParametersInfoHook)))
goto CleanupExit;
}
if (!NT_SUCCESS(status = DetourAttach((PVOID)&DefaultDrawThemeText, (PVOID)PhDrawThemeTextHook)))
goto CleanupExit;
if (!NT_SUCCESS(status = DetourAttach((PVOID)&DefaultDrawThemeTextEx, (PVOID)PhDrawThemeTextExHook)))
goto CleanupExit;
if (!NT_SUCCESS(status = DetourAttach((PVOID)&DefaultGetThemeColor, (PVOID)PhGetThemeColorHook)))
goto CleanupExit;
if (!NT_SUCCESS(status = DetourAttach((PVOID)&DefaultOpenNcThemeData, (PVOID)PhOpenNcThemeDataHook)))
goto CleanupExit;
if (WindowsVersion >= WINDOWS_11)
if (!NT_SUCCESS(status = DetourAttach((PVOID)&DefaultTaskDialogIndirect, (PVOID)PhTaskDialogIndirectHook)))
goto CleanupExit;
}
if (!NT_SUCCESS(status = DetourAttach((PVOID)&DefaultCreateWindowEx, (PVOID)PhCreateWindowExHook)))
goto CleanupExit;
if (!NT_SUCCESS(status = DetourTransactionCommit()))
goto CleanupExit;
CleanupExit:
if (!NT_SUCCESS(status))
{
PhShowStatus(NULL, L"Unable to commit detours transaction.", status, 0);
}
}
BOOLEAN PhIsThemeTransparencyEnabled(
VOID
)
{
static CONST PH_STRINGREF themesKeyName = PH_STRINGREF_INIT(L"Software\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize");
BOOLEAN themesEnableTransparency = FALSE;
HANDLE keyHandle;
if (NT_SUCCESS(PhOpenKey(
&keyHandle,
KEY_QUERY_VALUE,
PH_KEY_CURRENT_USER,
&themesKeyName,
0
)))
{
themesEnableTransparency = !!PhQueryRegistryUlongZ(keyHandle, L"EnableTransparency");
NtClose(keyHandle);
}
return themesEnableTransparency;
}
VOID PhInitializeSuperclassControls(
VOID
)
{
PhDefaultEnableStreamerMode = !!PhGetIntegerSetting(SETTING_ENABLE_STREAMER_MODE);
if (PhEnableThemeAcrylicSupport && !PhEnableThemeSupport)
PhEnableThemeAcrylicSupport = FALSE;
if (PhEnableThemeAcrylicSupport)
PhEnableThemeAcrylicSupport = PhIsThemeTransparencyEnabled();
if (PhEnableThemeSupport || PhDefaultEnableStreamerMode)
{
if (WindowsVersion >= WINDOWS_11)
{
PhDefaultEnableThemeAcrylicWindowSupport = !!PhGetIntegerSetting(SETTING_ENABLE_THEME_ACRYLIC_WINDOW_SUPPORT);
}
PhDefaultEnableThemeAnimation = !!PhGetIntegerSetting(SETTING_ENABLE_THEME_ANIMATION);
PhRegisterDialogSuperClass();
PhRegisterMenuSuperClass();
PhRegisterRebarSuperClass();
PhRegisterComboBoxSuperClass();
PhRegisterStaticSuperClass();
PhRegisterStatusBarSuperClass();
PhRegisterEditSuperClass();
PhRegisterHeaderSuperClass();
PhRegisterDetoursHooks();
}
}
================================================
FILE: SystemInformer/delayload.c
================================================
/*
* Copyright (c) 2022 Winsider Seminars & Solutions, Inc. All rights reserved.
*
* This file is part of System Informer.
*
* Authors:
*
* dmex 2021-2023
*
*/
#include
#include
// CRT delayload support
// The msvc delayload handler throws exceptions when
// imports are unavailable instead of returning NULL (dmex)
#ifndef PH_NATIVE_DELAYLOAD
PH_QUEUED_LOCK PhDelayLoadImportLock = PH_QUEUED_LOCK_INIT;
ULONG PhDelayLoadOldProtection = PAGE_WRITECOPY;
ULONG PhDelayLoadLockCount = 0;
// based on \MSVC\14.31.31103\include\dloadsup.h (dmex)
VOID PhDelayLoadImportAcquire(
_In_ PVOID ImportDirectorySectionAddress,
_In_ SIZE_T ImportDirectorySectionSize
)
{
PhAcquireQueuedLockExclusive(&PhDelayLoadImportLock);
PhDelayLoadLockCount += 1;
if (PhDelayLoadLockCount == 1)
{
NTSTATUS status;
if (!NT_SUCCESS(status = PhProtectVirtualMemory(
NtCurrentProcess(),
ImportDirectorySectionAddress,
ImportDirectorySectionSize,
PAGE_READWRITE,
&PhDelayLoadOldProtection
)))
{
PhRaiseStatus(status);
}
}
PhReleaseQueuedLockExclusive(&PhDelayLoadImportLock);
}
VOID PhDelayLoadImportRelease(
_In_ PVOID ImportDirectorySectionAddress,
_In_ SIZE_T ImportDirectorySectionSize
)
{
PhAcquireQueuedLockExclusive(&PhDelayLoadImportLock);
PhDelayLoadLockCount -= 1;
if (PhDelayLoadLockCount == 0)
{
ULONG importSectionOldProtection;
PhProtectVirtualMemory(
NtCurrentProcess(),
ImportDirectorySectionAddress,
ImportDirectorySectionSize,
PhDelayLoadOldProtection,
&importSectionOldProtection
);
#ifdef _M_ARM64
NtFlushInstructionCache(
NtCurrentProcess(),
ImportDirectorySectionAddress,
ImportDirectorySectionSize
);
#endif
}
PhReleaseQueuedLockExclusive(&PhDelayLoadImportLock);
}
#endif
PVOID WINAPI __delayLoadHelper2(
_In_ PIMAGE_DELAYLOAD_DESCRIPTOR DelayloadDescriptor,
_In_ PIMAGE_THUNK_DATA ThunkAddress
)
{
#if !defined(PH_NATIVE_DELAYLOAD)
BOOLEAN importNeedsFree = FALSE;
PCSTR importDllName;
PVOID procedureAddress;
PVOID moduleHandle;
PVOID* importHandle;
PIMAGE_THUNK_DATA importEntry;
PIMAGE_THUNK_DATA importTable;
PIMAGE_THUNK_DATA importNameTable;
PIMAGE_NT_HEADERS imageNtHeaders;
SIZE_T importDirectorySectionSize;
PVOID importDirectorySectionAddress;
importDllName = PTR_ADD_OFFSET(NtCurrentImageBase(), DelayloadDescriptor->DllNameRVA);
importHandle = PTR_ADD_OFFSET(NtCurrentImageBase(), DelayloadDescriptor->ModuleHandleRVA);
importTable = PTR_ADD_OFFSET(NtCurrentImageBase(), DelayloadDescriptor->ImportAddressTableRVA);
importNameTable = PTR_ADD_OFFSET(NtCurrentImageBase(), DelayloadDescriptor->ImportNameTableRVA);
if (!(moduleHandle = InterlockedCompareExchangePointer(importHandle, NULL, NULL)))
{
WCHAR importDllNameBuffer[DOS_MAX_PATH_LENGTH] = L"";
PhZeroExtendToUtf16Buffer(importDllName, strlen(importDllName), importDllNameBuffer);
if (!(moduleHandle = PhLoadLibrary(importDllNameBuffer)))
{
return NULL;
}
importNeedsFree = TRUE;
}
importEntry = PTR_ADD_OFFSET(importNameTable, PTR_SUB_OFFSET(ThunkAddress, importTable));
if (IMAGE_SNAP_BY_ORDINAL(importEntry->u1.Ordinal))
{
USHORT procedureOrdinal = IMAGE_ORDINAL(importEntry->u1.Ordinal);
procedureAddress = PhGetDllBaseProcedureAddress(moduleHandle, NULL, procedureOrdinal);
}
else
{
PIMAGE_IMPORT_BY_NAME importByName = PTR_ADD_OFFSET(NtCurrentImageBase(), importEntry->u1.AddressOfData);
procedureAddress = PhGetDllBaseProcedureAddressWithHint(moduleHandle, importByName->Name, importByName->Hint);
}
if (!procedureAddress)
return NULL;
if (!NT_SUCCESS(PhGetLoaderEntryImageNtHeaders(
NtCurrentImageBase(),
&imageNtHeaders
)))
{
return NULL;
}
if (!NT_SUCCESS(PhGetLoaderEntryImageVaToSection(
NtCurrentImageBase(),
imageNtHeaders,
importTable,
&importDirectorySectionAddress,
&importDirectorySectionSize
)))
{
return NULL;
}
PhDelayLoadImportAcquire(importDirectorySectionAddress, importDirectorySectionSize);
InterlockedExchangePointer((PVOID)ThunkAddress, procedureAddress);
PhDelayLoadImportRelease(importDirectorySectionAddress, importDirectorySectionSize);
if ((InterlockedExchangePointer(importHandle, moduleHandle) == moduleHandle) && importNeedsFree)
{
PhFreeLibrary(moduleHandle); // already updated the cache (dmex)
}
return procedureAddress;
#else
static PH_INITONCE initOnce = PH_INITONCE_INIT;
static PVOID (WINAPI* DelayLoadFailureHook)(
_In_ PCSTR DllName,
_In_ PCSTR ProcName
) = NULL;
if (PhBeginInitOnce(&initOnce))
{
DelayLoadFailureHook = PhGetModuleProcAddress(L"kernel32.dll", "DelayLoadFailureHook"); // kernelbase.dll
PhEndInitOnce(&initOnce);
}
return LdrResolveDelayLoadedAPI(
NtCurrentImageBase(),
DelayloadDescriptor,
NULL,
DelayLoadFailureHook,
ThunkAddress,
0
);
#endif
}
================================================
FILE: SystemInformer/devprv.c
================================================
/*
* Copyright (c) 2022 Winsider Seminars & Solutions, Inc. All rights reserved.
*
* This file is part of System Informer.
*
* Authors:
*
* jxy-s 2022-2023
*
*/
#include
#include
#include
#include
#include
#include
DEFINE_GUID(GUID_DEVINTERFACE_A2DP_SIDEBAND_AUDIO, 0xf3b1362f, 0xc9f4, 0x4dd1, 0x9d, 0x55, 0xe0, 0x20, 0x38, 0xa1, 0x29, 0xfb);
DEFINE_GUID(GUID_DEVINTERFACE_BLUETOOTH_HFP_SCO_HCIBYPASS, 0xbe446647, 0xf655, 0x4919, 0x8b, 0xd0, 0x12, 0x5b, 0xa5, 0xd4, 0xce, 0x65);
DEFINE_GUID(GUID_DEVINTERFACE_CHARGING_ARBITRATION, 0xec0a1cc9, 0x4294, 0x43fb, 0xbf, 0x37, 0xb8, 0x50, 0xce, 0x95, 0xf3, 0x37);
DEFINE_GUID(GUID_DEVINTERFACE_CONFIGURABLE_USBFN_CHARGER, 0x7158c35c, 0xc1bc, 0x4d90, 0xac, 0xb1, 0x80, 0x20, 0xbd, 0xe, 0x19, 0xca);
DEFINE_GUID(GUID_DEVINTERFACE_CONFIGURABLE_WIRELESS_CHARGER, 0x3612b1c8, 0x3633, 0x47d3, 0x8a, 0xf5, 0x0, 0xa4, 0xdf, 0xa0, 0x47, 0x93);
DEFINE_GUID(GUID_DEVINTERFACE_I2C, 0x2564AA4F, 0xDDDB, 0x4495, 0xB4, 0x97, 0x6A, 0xD4, 0xA8, 0x41, 0x63, 0xD7);
DEFINE_GUID(GUID_DEVINTERFACE_OPM, 0xBF4672DE, 0x6B4E, 0x4BE4, 0xA3, 0x25, 0x68, 0xA9, 0x1E, 0xA4, 0x9C, 0x09);
DEFINE_GUID(GUID_DEVINTERFACE_OPM_2_JTP, 0xE929EEA4, 0xB9F1, 0x407B, 0xAA, 0xB9, 0xAB, 0x08, 0xBB, 0x44, 0xFB, 0xF4);
DEFINE_GUID(GUID_DEVINTERFACE_OPM_2, 0x7F098726, 0x2EBB, 0x4FF3, 0xA2, 0x7F, 0x10, 0x46, 0xB9, 0x5D, 0xC5, 0x17);
DEFINE_GUID(GUID_DEVINTERFACE_OPM_3, 0x693a2cb1, 0x8c8d, 0x4ab6, 0x95, 0x55, 0x4b, 0x85, 0xef, 0x2c, 0x7c, 0x6b);
DEFINE_GUID(GUID_DEVINTERFACE_BRIGHTNESS, 0xFDE5BBA4, 0xB3F9, 0x46FB, 0xBD, 0xAA, 0x07, 0x28, 0xCE, 0x31, 0x00, 0xB4);
DEFINE_GUID(GUID_DEVINTERFACE_BRIGHTNESS_2, 0x148A3C98, 0x0ECD, 0x465A, 0xB6, 0x34, 0xB0, 0x5F, 0x19, 0x5F, 0x77, 0x39);
DEFINE_GUID(GUID_DEVINTERFACE_MIRACAST_DISPLAY, 0xaf03f190, 0x22af, 0x48cb, 0x94, 0xbb, 0xb7, 0x8e, 0x76, 0xa2, 0x51, 0x7);
DEFINE_GUID(GUID_DEVINTERFACE_BRIGHTNESS_3, 0x197a4a6e, 0x391, 0x4322, 0x96, 0xea, 0xc2, 0x76, 0xf, 0x88, 0x1d, 0x3a);
DEFINE_GUID(GUID_DEVINTERFACE_HPMI, 0xdedae202, 0x1d20, 0x4c40, 0xa6, 0xf3, 0x18, 0x97, 0xe3, 0x19, 0xd5, 0x4f);
DEFINE_GUID(GUID_DEVINTERFACE_VIRTUALIZABLE_DEVICE, 0xa13a7a93, 0x11f0, 0x4bd2, 0xa9, 0xf5, 0x6b, 0x5c, 0x5b, 0x88, 0x52, 0x7d);
DEFINE_GUID(GUID_DEVINTERFACE_EMMC_PARTITION_ACCESS_RPMB, 0x27447c21L, 0xbcc3, 0x4d07, 0xa0, 0x5b, 0xa3, 0x39, 0x5b, 0xb4, 0xee, 0xe7);
DEFINE_GUID(GUID_DEVINTERFACE_EMMC_PARTITION_ACCESS_GPP, 0x2e0e2e39L, 0x1f19, 0x4595, 0xa9, 0x06, 0x88, 0x78, 0x82, 0xe7, 0x39, 0x03);
DEFINE_GUID(GUID_DEVINTERFACE_USB_SIDEBAND_AUDIO_HS_HCIBYPASS, 0x2baa4b5, 0x33b5, 0x4d97, 0xae, 0x4f, 0xe8, 0x6d, 0xde, 0x17, 0x53, 0x6f);
DEFINE_GUID(GUID_BUS_VPCI, 0xc066f39a, 0xde00, 0x4667, 0x89, 0x41, 0x33, 0x68, 0xed, 0x5d, 0x83, 0xb5);
DEFINE_GUID(GUID_VPCI_INTERFACE_STANDARD, 0x12e65e71, 0xb651, 0x4067, 0x83, 0x1a, 0x13, 0x83, 0x20, 0x3c, 0xb0, 0xcb);
DEFINE_GUID(GUID_DEVINTERFACE_VPCI, 0x57863182, 0xc948, 0x4692, 0x97, 0xe3, 0x34, 0xb5, 0x76, 0x62, 0xa3, 0xe0);
DEFINE_GUID(GUID_DEVINTERFACE_WWAN_CONTROLLER, 0x669159fd, 0xe3c0, 0x45cb, 0xbc, 0x5f, 0x95, 0x99, 0x5b, 0xcd, 0x6, 0xcd);
DEFINE_GUID(GUID_DEVINTERFACE_WDDM3_ON_VB, 0xe922004d, 0xeb9c, 0x4de1, 0x92, 0x24, 0xa9, 0xce, 0xaa, 0x95, 0x9b, 0xce);
DEFINE_GUID(GUID_DEVINTERFACE_GRAPHICSPOWER, 0xea5c6870, 0xe93c, 0x4588, 0xbe, 0xf1, 0xfe, 0xc4, 0x2f, 0xc9, 0x42, 0x9a);
DEFINE_GUID(GUID_DEVINTERFACE_GNSS, 0x3336e5e4, 0x18a, 0x4669, 0x84, 0xc5, 0xbd, 0x5, 0xf3, 0xbd, 0x36, 0x8b);
DEFINE_GUID(GUID_DEVINTERFACE_HID, 0x4D1E55B2L, 0xF16F, 0x11CF, 0x88, 0xCB, 0x00, 0x11, 0x11, 0x00, 0x00, 0x30);
DEFINE_GUID(GUID_DEVINTERFACE_LAMP, 0x6c11e9e3, 0x8238, 0x4f0a, 0x0a, 0x19, 0xaa, 0xec, 0x26, 0xca, 0x5e, 0x98);
DEFINE_GUID(GUID_DEVINTERFACE_NFCDTA, 0x7fd3f30b, 0x5e49, 0x4be1, 0xb3, 0xaa, 0xaf, 0x06, 0x26, 0x0d, 0x23, 0x6a);
DEFINE_GUID(GUID_DEVINTERFACE_NFCSE, 0x8dc7c854, 0xf5e5, 0x4bed, 0x81, 0x5d, 0xc, 0x85, 0xad, 0x4, 0x77, 0x25);
DEFINE_GUID(GUID_DEVINTERFACE_NFP, 0xFB3842CD, 0x9E2A, 0x4F83, 0x8F, 0xCC, 0x4B, 0x07, 0x61, 0x13, 0x9A, 0xE9);
DEFINE_GUID(GUID_DEVINTERFACE_KEYBOARD, 0x884b96c3, 0x56ef, 0x11d1, 0xbc, 0x8c, 0x00, 0xa0, 0xc9, 0x14, 0x05, 0xdd);
DEFINE_GUID(GUID_DEVINTERFACE_MODEM, 0x2c7089aa, 0x2e0e, 0x11d1, 0xb1, 0x14, 0x00, 0xc0, 0x4f, 0xc2, 0xaa, 0xe4);
DEFINE_GUID(GUID_DEVINTERFACE_MOUSE, 0x378de44c, 0x56ef, 0x11d1, 0xbc, 0x8c, 0x00, 0xa0, 0xc9, 0x14, 0x05, 0xdd );
DEFINE_GUID(GUID_DEVINTERFACE_PARALLEL, 0x97F76EF0, 0xF883, 0x11D0, 0xAF, 0x1F, 0x00, 0x00, 0xF8, 0x00, 0x84, 0x5C);
DEFINE_GUID(GUID_DEVINTERFACE_PARCLASS, 0x811FC6A5, 0xF728, 0x11D0, 0xA5, 0x37, 0x00, 0x00, 0xF8, 0x75, 0x3E, 0xD1);
DEFINE_GUID(GUID_PARALLEL_DEVICE, 0x97F76EF0, 0xF883, 0x11D0, 0xAF, 0x1F, 0x00, 0x00, 0xF8, 0x00, 0x84, 0x5C);
DEFINE_GUID(GUID_PARCLASS_DEVICE, 0x811FC6A5, 0xF728, 0x11D0, 0xA5, 0x37, 0x00, 0x00, 0xF8, 0x75, 0x3E, 0xD1);
DEFINE_GUID(GUID_DEVINTERFACE_DISPLAY_ADAPTER, 0x5b45201d, 0xf2f2, 0x4f3b, 0x85, 0xbb, 0x30, 0xff, 0x1f, 0x95, 0x35, 0x99);
DEFINE_GUID(GUID_DEVINTERFACE_MONITOR, 0xe6f07b5f, 0xee97, 0x4a90, 0xb0, 0x76, 0x33, 0xf5, 0x7b, 0xf4, 0xea, 0xa7);
DEFINE_GUID(GUID_DEVICE_BATTERY, 0x72631e54L, 0x78A4, 0x11d0, 0xbc, 0xf7, 0x00, 0xaa, 0x00, 0xb7, 0xb3, 0x2a);
DEFINE_GUID(GUID_DEVICE_APPLICATIONLAUNCH_BUTTON, 0x629758eel, 0x986e, 0x4d9e, 0x8e, 0x47, 0xde, 0x27, 0xf8, 0xab, 0x05, 0x4d);
DEFINE_GUID(GUID_DEVICE_SYS_BUTTON, 0x4AFA3D53L, 0x74A7, 0x11d0, 0xbe, 0x5e, 0x00, 0xA0, 0xC9, 0x06, 0x28, 0x57);
DEFINE_GUID(GUID_DEVICE_LID, 0x4AFA3D52L, 0x74A7, 0x11d0, 0xbe, 0x5e, 0x00, 0xA0, 0xC9, 0x06, 0x28, 0x57);
DEFINE_GUID(GUID_DEVICE_THERMAL_ZONE, 0x4AFA3D51L, 0x74A7, 0x11d0, 0xbe, 0x5e, 0x00, 0xA0, 0xC9, 0x06, 0x28, 0x57);
DEFINE_GUID(GUID_DEVICE_FAN, 0x05ecd13dL, 0x81da, 0x4a2a, 0x8a, 0x4c, 0x52, 0x4f, 0x23, 0xdd, 0x4d, 0xc9);
DEFINE_GUID(GUID_DEVICE_PROCESSOR, 0x97fadb10L, 0x4e33, 0x40ae, 0x35, 0x9c, 0x8b, 0xef, 0x02, 0x9d, 0xbd, 0xd0);
DEFINE_GUID(GUID_DEVICE_MEMORY, 0x3fd0f03dL, 0x92e0, 0x45fb, 0xb7, 0x5c, 0x5e, 0xd8, 0xff, 0xb0, 0x10, 0x21);
DEFINE_GUID(GUID_DEVICE_ACPI_TIME, 0x97f99bf6L, 0x4497, 0x4f18, 0xbb, 0x22, 0x4b, 0x9f, 0xb2, 0xfb, 0xef, 0x9c);
DEFINE_GUID(GUID_DEVICE_MESSAGE_INDICATOR, 0XCD48A365L, 0xfa94, 0x4ce2, 0xa2, 0x32, 0xa1, 0xb7, 0x64, 0xe5, 0xd8, 0xb4);
DEFINE_GUID(GUID_CLASS_INPUT, 0x4D1E55B2L, 0xF16F, 0x11CF, 0x88, 0xCB, 0x00, 0x11, 0x11, 0x00, 0x00, 0x30);
DEFINE_GUID(GUID_DEVINTERFACE_THERMAL_COOLING, 0xdbe4373d, 0x3c81, 0x40cb, 0xac, 0xe4, 0xe0, 0xe5, 0xd0, 0x5f, 0xc, 0x9f);
DEFINE_GUID(GUID_DEVINTERFACE_THERMAL_MANAGER, 0x927ec093, 0x69a4, 0x4bc0, 0xbd, 0x2, 0x71, 0x16, 0x64, 0x71, 0x44, 0x63);
DEFINE_GUID(GUID_DEVINTERFACE_PWM_CONTROLLER, 0x60824b4c, 0xeed1, 0x4c9c, 0xb4, 0x9c, 0x1b, 0x96, 0x14, 0x61, 0xa8, 0x19);
DEFINE_GUID(GUID_DEVINTERFACE_USBPRINT, 0x28d78fad, 0x5a12, 0x11d1, 0xae, 0x5b, 0x0, 0x0, 0xf8, 0x3, 0xa8, 0xc2);
DEFINE_GUID(GUID_DEVINTERFACE_IPPUSB_PRINT, 0xf2f40381, 0xf46d, 0x4e51, 0xbc, 0xe7, 0x62, 0xde, 0x6c, 0xf2, 0xd0, 0x98);
DEFINE_GUID(GUID_DEVINTERFACE_VM_GENCOUNTER, 0x3ff2c92b, 0x6598, 0x4e60, 0x8e, 0x1c, 0x0c, 0xcf, 0x49, 0x27, 0xe3, 0x19);
DEFINE_GUID(GUID_DEVINTERFACE_BIOMETRIC_READER, 0xe2b5183a, 0x99ea, 0x4cc3, 0xad, 0x6b, 0x80, 0xca, 0x8d, 0x71, 0x5b, 0x80);
DEFINE_GUID(GUID_DEVINTERFACE_SMARTCARD_READER, 0x50DD5230, 0xBA8A, 0x11D1, 0xBF, 0x5D, 0x00, 0x00, 0xF8, 0x05, 0xF5, 0x30);
DEFINE_GUID(GUID_DEVINTERFACE_DMR, 0xD0875FB4, 0x2196, 0x4c7a, 0xA6, 0x3D, 0xE4, 0x16, 0xAD, 0xDD, 0x60, 0xA1);
DEFINE_GUID(GUID_DEVINTERFACE_DMP, 0x25B4E268, 0x2A05, 0x496e, 0x80, 0x3B, 0x26, 0x68, 0x37, 0xFB, 0xDA, 0x4B);
DEFINE_GUID(GUID_DEVINTERFACE_DMS, 0xC96037AE, 0xA558, 0x4470, 0xB4, 0x32, 0x11, 0x5A, 0x31, 0xB8, 0x55, 0x53);
DEFINE_GUID(GUID_DEVINTERFACE_ENHANCED_STORAGE_SILO, 0x3897f6a4, 0xfd35, 0x4bc8, 0xa0, 0xb7, 0x5d, 0xbb, 0xa3, 0x6a, 0xda, 0xfa);
DEFINE_GUID(GUID_DEVINTERFACE_WPD, 0x6AC27878, 0xA6FA, 0x4155, 0xBA, 0x85, 0xF9, 0x8F, 0x49, 0x1D, 0x4F, 0x33);
DEFINE_GUID(GUID_DEVINTERFACE_WPD_PRIVATE, 0xBA0C718F, 0x4DED, 0x49B7, 0xBD, 0xD3, 0xFA, 0xBE, 0x28, 0x66, 0x12, 0x11);
DEFINE_GUID(GUID_DEVINTERFACE_WPD_SERVICE, 0x9EF44F80, 0x3D64, 0x4246, 0xA6, 0xAA, 0x20, 0x6F, 0x32, 0x8D, 0x1E, 0xDC);
DEFINE_GUID(GUID_DEVINTERFACE_SENSOR, 0XBA1BB692, 0X9B7A, 0X4833, 0X9A, 0X1E, 0X52, 0X5E, 0XD1, 0X34, 0XE7, 0XE2);
DEFINE_GUID(GUID_DEVINTERFACE_IMAGE, 0x6bdd1fc6L, 0x810f, 0x11d0, 0xbe, 0xc7, 0x08, 0x00, 0x2b, 0xe2, 0x09, 0x2f);
DEFINE_GUID(GUID_DEVINTERFACE_SIDESHOW, 0x152e5811, 0xfeb9, 0x4b00, 0x90, 0xf4, 0xd3, 0x29, 0x47, 0xae, 0x16, 0x81);
DEFINE_GUID(GUID_DEVINTERFACE_WIFIDIRECT_DEVICE, 0x439b20af, 0x8955, 0x405b, 0x99, 0xf0, 0xa6, 0x2a, 0xf0, 0xc6, 0x8d, 0x43);
DEFINE_GUID(GUID_AEPSERVICE_WIFIDIRECT_DEVICE, 0xcc29827c, 0x9caf, 0x4928, 0x99, 0xa9, 0x18, 0xf7, 0xc2, 0x38, 0x13, 0x89);
DEFINE_GUID(GUID_DEVINTERFACE_ASP_INFRA_DEVICE, 0xff823995, 0x7a72, 0x4c80, 0x87, 0x57, 0xc6, 0x7e, 0xe1, 0x3d, 0x1a, 0x49);
DEFINE_GUID(GUID_DXGKDDI_MITIGABLE_DEVICE_INTERFACE, 0x1387f270, 0x121a, 0x4a4a, 0xb2, 0x5e, 0x3b, 0x15, 0x89, 0x97, 0x6c, 0x61);
DEFINE_GUID(GUID_DXGKDDI_FLEXIOV_DEVICE_INTERFACE, 0x7b73a997, 0x48e8, 0x4cab, 0x9f, 0xab, 0xe0, 0x77, 0x4b, 0x44, 0xf5, 0x99);
DEFINE_GUID(GUID_SRIOV_DEVICE_INTERFACE_STANDARD, 0x937ee9b6, 0xed3, 0x411c, 0x98, 0x2b, 0x1f, 0x56, 0x4a, 0xfb, 0xab, 0xd3);
DEFINE_GUID(GUID_MITIGABLE_DEVICE_INTERFACE, 0xadfd9567, 0x4245, 0x497e, 0x85, 0x72, 0x78, 0xd4, 0x5c, 0x16, 0x22, 0xb8);
DEFINE_GUID(GUID_IO_VOLUME_DEVICE_INTERFACE, 0x53f5630d, 0xb6bf, 0x11d0, 0x94, 0xf2, 0x00, 0xa0, 0xc9, 0x1e, 0xfb, 0x8b);
DEFINE_GUID(GUID_NFC_RADIO_MEDIA_DEVICE_INTERFACE, 0x4d51e930, 0x750d, 0x4a36, 0xa9, 0xf7, 0x91, 0xdc, 0x54, 0x0f, 0xcd, 0x30);
DEFINE_GUID(GUID_NFCSE_RADIO_MEDIA_DEVICE_INTERFACE, 0xef8ba08f, 0x148d, 0x4116, 0x83, 0xef, 0xa2, 0x67, 0x9d, 0xfc, 0x3f, 0xa5);
DEFINE_GUID(GUID_BLUETOOTHLE_DEVICE_INTERFACE, 0x781aee18, 0x7733, 0x4ce4, 0xad, 0xd0, 0x91, 0xf4, 0x1c, 0x67, 0xb5, 0x92);
DEFINE_GUID(GUID_BLUETOOTH_GATT_SERVICE_DEVICE_INTERFACE, 0x6e3bb679, 0x4372, 0x40c8, 0x9e, 0xaa, 0x45, 0x09, 0xdf, 0x26, 0x0c, 0xd8);
DEFINE_GUID(GUID_BUS_TYPE_TREE, 0x4E815EE1, 0x20F8, 0x41EF, 0x8C, 0xFF, 0x3C, 0x28, 0x3F, 0x02, 0xD7, 0x22);
DEFINE_GUID(GUID_TREE_TPM_SERVICE_FDTPM, 0x36deaa79, 0xc5dd, 0x447c, 0x95, 0xe6, 0xb3, 0x85, 0x95, 0x89, 0x29, 0x1a);
DEFINE_GUID(GUID_TREE_TPM_SERVICE_STPM, 0x1F75CF6D, 0xF709, 0x4C0C, 0x8B, 0xCB, 0x0C, 0xDC, 0xA1, 0x28, 0x9D, 0xDD);
DEFINE_GUID(GUID_DISPLAY_DEVICE_ARRIVAL, 0x1CA05180, 0xA699, 0x450A, 0x9A, 0x0C, 0xDE, 0x4F, 0xBE, 0x3D, 0xDD, 0x89);
DEFINE_GUID(GUID_COMPUTE_DEVICE_ARRIVAL, 0x1024e4c9, 0x47c9, 0x48d3, 0xb4, 0xa8, 0xf9, 0xdf, 0x78, 0x52, 0x3b, 0x53);
DEFINE_DEVPROPKEY(DEVPKEY_Gpu_Luid, 0x60b193cb, 0x5276, 0x4d0f, 0x96, 0xfc, 0xf1, 0x73, 0xab, 0xad, 0x3e, 0xc6, 2); // DEVPROP_TYPE_UINT64
DEFINE_DEVPROPKEY(DEVPKEY_Gpu_PhysicalAdapterIndex, 0x60b193cb, 0x5276, 0x4d0f, 0x96, 0xfc, 0xf1, 0x73, 0xab, 0xad, 0x3e, 0xc6, 3); // DEVPROP_TYPE_UINT32
#include
#include
#include
#include
#include
#include
#include
#pragma push_macro("DEFINE_GUID")
#undef DEFINE_GUID
#include
#include
#pragma pop_macro("DEFINE_GUID")
static CONST PH_STRINGREF RootInstanceId = PH_STRINGREF_INIT(L"HTREE\\ROOT\\0");
static ULONG RootInstanceIdHash = 2387464428; // PhHashStringRefEx(TRUE, PH_STRING_HASH_X65599);
PPH_OBJECT_TYPE PhDeviceTreeType = NULL;
PPH_OBJECT_TYPE PhDeviceItemType = NULL;
PPH_OBJECT_TYPE PhDeviceNotifyType = NULL;
static PPH_OBJECT_TYPE PhpDeviceInfoType = NULL;
static PH_FAST_LOCK PhpDeviceTreeLock = PH_FAST_LOCK_INIT;
static PPH_DEVICE_TREE PhpDeviceTree = NULL;
static HCMNOTIFICATION PhpDeviceNotification = NULL;
static HCMNOTIFICATION PhpDeviceInterfaceNotification = NULL;
static PH_CALLBACK_REGISTRATION ServiceProviderUpdatedRegistration;
static SLIST_HEADER PhDeviceNotifyListHead;
static PH_FREE_LIST PhDeviceNotifyFreeList;
#if !defined(NTDDI_WIN10_NI) || (NTDDI_VERSION < NTDDI_WIN10_NI)
// Note: This propkey is required for building with 22H1 and older Windows SDK (dmex)
DEFINE_DEVPROPKEY(DEVPKEY_Device_FirmwareVendor, 0x540b947e, 0x8b40, 0x45bc, 0xa8, 0xa2, 0x6a, 0x0b, 0x89, 0x4c, 0xbd, 0xa2, 26); // DEVPROP_TYPE_STRING
#endif
#define DEVPROP_FILL_FLAG_CLASS 0x00000001
_Function_class_(PH_DEVICE_PROPERTY_FILL_CALLBACK)
typedef
VOID
NTAPI
PH_DEVICE_PROPERTY_FILL_CALLBACK(
_In_ HDEVINFO DeviceInfoSet,
_In_ PPH_DEVINFO_DATA DeviceInfoData,
_In_ const DEVPROPKEY* PropertyKey,
_Out_ PPH_DEVICE_PROPERTY Property,
_In_ ULONG Flags
);
typedef PH_DEVICE_PROPERTY_FILL_CALLBACK* PPH_DEVICE_PROPERTY_FILL_CALLBACK;
typedef struct _PH_DEVICE_PROPERTY_TABLE_ENTRY
{
PH_DEVICE_PROPERTY_CLASS PropClass;
const DEVPROPKEY* PropKey;
PPH_DEVICE_PROPERTY_FILL_CALLBACK Callback;
ULONG CallbackFlags;
} PH_DEVICE_PROPERTY_TABLE_ENTRY, *PPH_DEVICE_PROPERTY_TABLE_ENTRY;
BOOLEAN PhpGetDevicePropertyGuid(
_In_ HDEVINFO DeviceInfoSet,
_In_ PSP_DEVINFO_DATA DeviceInfoData,
_In_ const DEVPROPKEY* DeviceProperty,
_Out_ PGUID Guid
)
{
BOOL result;
DEVPROPTYPE devicePropertyType = DEVPROP_TYPE_EMPTY;
ULONG requiredLength = sizeof(GUID);
result = SetupDiGetDevicePropertyW(
DeviceInfoSet,
DeviceInfoData,
DeviceProperty,
&devicePropertyType,
(PBYTE)Guid,
sizeof(GUID),
&requiredLength,
0
);
if (result && (devicePropertyType == DEVPROP_TYPE_GUID))
{
return TRUE;
}
RtlZeroMemory(Guid, sizeof(GUID));
return FALSE;
}
BOOLEAN PhpGetDeviceInterfacePropertyGuid(
_In_ HDEVINFO DeviceInfoSet,
_In_ PSP_DEVICE_INTERFACE_DATA DeviceInterfaceData,
_In_ const DEVPROPKEY* DeviceProperty,
_Out_ PGUID Guid
)
{
BOOL result;
DEVPROPTYPE devicePropertyType = DEVPROP_TYPE_EMPTY;
ULONG requiredLength = sizeof(GUID);
result = SetupDiGetDeviceInterfacePropertyW(
DeviceInfoSet,
DeviceInterfaceData,
DeviceProperty,
&devicePropertyType,
(PBYTE)Guid,
sizeof(GUID),
&requiredLength,
0
);
if (result && (devicePropertyType == DEVPROP_TYPE_GUID))
{
return TRUE;
}
RtlZeroMemory(Guid, sizeof(GUID));
return FALSE;
}
BOOLEAN PhpGetClassPropertyGuid(
_In_ const GUID* ClassGuid,
_In_ const DEVPROPKEY* DeviceProperty,
_In_ ULONG Flags,
_Out_ PGUID Guid
)
{
BOOL result;
DEVPROPTYPE devicePropertyType = DEVPROP_TYPE_EMPTY;
ULONG requiredLength = sizeof(GUID);
result = SetupDiGetClassPropertyW(
ClassGuid,
DeviceProperty,
&devicePropertyType,
(PBYTE)Guid,
sizeof(GUID),
&requiredLength,
Flags
);
if (result && (devicePropertyType == DEVPROP_TYPE_GUID))
{
return TRUE;
}
RtlZeroMemory(Guid, sizeof(GUID));
return FALSE;
}
BOOLEAN PhpGetDevicePropertyUInt64(
_In_ HDEVINFO DeviceInfoSet,
_In_ PSP_DEVINFO_DATA DeviceInfoData,
_In_ const DEVPROPKEY* DeviceProperty,
_Out_ PULONG64 Value
)
{
BOOL result;
DEVPROPTYPE devicePropertyType = DEVPROP_TYPE_EMPTY;
ULONG requiredLength = sizeof(ULONG64);
result = SetupDiGetDevicePropertyW(
DeviceInfoSet,
DeviceInfoData,
DeviceProperty,
&devicePropertyType,
(PBYTE)Value,
sizeof(ULONG64),
&requiredLength,
0
);
if (result && (devicePropertyType == DEVPROP_TYPE_UINT64))
{
return TRUE;
}
*Value = 0;
return FALSE;
}
BOOLEAN PhpGetDeviceInterfacePropertyUInt64(
_In_ HDEVINFO DeviceInfoSet,
_In_ PSP_DEVICE_INTERFACE_DATA DeviceInterfaceData,
_In_ const DEVPROPKEY* DeviceProperty,
_Out_ PULONG64 Value
)
{
BOOL result;
DEVPROPTYPE devicePropertyType = DEVPROP_TYPE_EMPTY;
ULONG requiredLength = sizeof(ULONG64);
result = SetupDiGetDeviceInterfacePropertyW(
DeviceInfoSet,
DeviceInterfaceData,
DeviceProperty,
&devicePropertyType,
(PBYTE)Value,
sizeof(ULONG64),
&requiredLength,
0
);
if (result && (devicePropertyType == DEVPROP_TYPE_UINT64))
{
return TRUE;
}
*Value = 0;
return FALSE;
}
BOOLEAN PhpGetClassPropertyUInt64(
_In_ const GUID* ClassGuid,
_In_ const DEVPROPKEY* DeviceProperty,
_In_ ULONG Flags,
_Out_ PULONG64 Value
)
{
BOOL result;
DEVPROPTYPE devicePropertyType = DEVPROP_TYPE_EMPTY;
ULONG requiredLength = sizeof(ULONG64);
result = SetupDiGetClassPropertyW(
ClassGuid,
DeviceProperty,
&devicePropertyType,
(PBYTE)Value,
sizeof(ULONG64),
&requiredLength,
Flags
);
if (result && (devicePropertyType == DEVPROP_TYPE_UINT64))
{
return TRUE;
}
*Value = 0;
return FALSE;
}
BOOLEAN PhpGetDevicePropertyInt64(
_In_ HDEVINFO DeviceInfoSet,
_In_ PSP_DEVINFO_DATA DeviceInfoData,
_In_ const DEVPROPKEY* DeviceProperty,
_Out_ PLONG64 Value
)
{
BOOL result;
DEVPROPTYPE devicePropertyType = DEVPROP_TYPE_EMPTY;
ULONG requiredLength = sizeof(LONG64);
result = SetupDiGetDevicePropertyW(
DeviceInfoSet,
DeviceInfoData,
DeviceProperty,
&devicePropertyType,
(PBYTE)Value,
sizeof(LONG64),
&requiredLength,
0
);
if (result && (devicePropertyType == DEVPROP_TYPE_INT64))
{
return TRUE;
}
*Value = 0;
return FALSE;
}
BOOLEAN PhpGetDeviceInterfacePropertyInt64(
_In_ HDEVINFO DeviceInfoSet,
_In_ PSP_DEVICE_INTERFACE_DATA DeviceInterfaceData,
_In_ const DEVPROPKEY* DeviceProperty,
_Out_ PLONG64 Value
)
{
BOOL result;
DEVPROPTYPE devicePropertyType = DEVPROP_TYPE_EMPTY;
ULONG requiredLength = sizeof(LONG64);
result = SetupDiGetDeviceInterfacePropertyW(
DeviceInfoSet,
DeviceInterfaceData,
DeviceProperty,
&devicePropertyType,
(PBYTE)Value,
sizeof(LONG64),
&requiredLength,
0
);
if (result && (devicePropertyType == DEVPROP_TYPE_INT64))
{
return TRUE;
}
*Value = 0;
return FALSE;
}
BOOLEAN PhpGetClassPropertyInt64(
_In_ const GUID* ClassGuid,
_In_ const DEVPROPKEY* DeviceProperty,
_In_ ULONG Flags,
_Out_ PLONG64 Value
)
{
BOOL result;
DEVPROPTYPE devicePropertyType = DEVPROP_TYPE_EMPTY;
ULONG requiredLength = sizeof(LONG64);
result = SetupDiGetClassPropertyW(
ClassGuid,
DeviceProperty,
&devicePropertyType,
(PBYTE)Value,
sizeof(LONG64),
&requiredLength,
Flags
);
if (result && (devicePropertyType == DEVPROP_TYPE_INT64))
{
return TRUE;
}
*Value = 0;
return FALSE;
}
BOOLEAN PhpGetDevicePropertyUInt32(
_In_ HDEVINFO DeviceInfoSet,
_In_ PSP_DEVINFO_DATA DeviceInfoData,
_In_ const DEVPROPKEY* DeviceProperty,
_Out_ PULONG Value
)
{
BOOL result;
DEVPROPTYPE devicePropertyType = DEVPROP_TYPE_EMPTY;
ULONG requiredLength = sizeof(ULONG);
result = SetupDiGetDevicePropertyW(
DeviceInfoSet,
DeviceInfoData,
DeviceProperty,
&devicePropertyType,
(PBYTE)Value,
sizeof(ULONG),
&requiredLength,
0
);
if (result && (devicePropertyType == DEVPROP_TYPE_UINT32))
{
return TRUE;
}
*Value = 0;
return FALSE;
}
BOOLEAN PhpGetDeviceInterfacePropertyUInt32(
_In_ HDEVINFO DeviceInfoSet,
_In_ PSP_DEVICE_INTERFACE_DATA DeviceInterfaceData,
_In_ const DEVPROPKEY* DeviceProperty,
_Out_ PULONG Value
)
{
BOOL result;
DEVPROPTYPE devicePropertyType = DEVPROP_TYPE_EMPTY;
ULONG requiredLength = sizeof(ULONG);
result = SetupDiGetDeviceInterfacePropertyW(
DeviceInfoSet,
DeviceInterfaceData,
DeviceProperty,
&devicePropertyType,
(PBYTE)Value,
sizeof(ULONG),
&requiredLength,
0
);
if (result && (devicePropertyType == DEVPROP_TYPE_UINT32))
{
return TRUE;
}
*Value = 0;
return FALSE;
}
BOOLEAN PhpGetClassPropertyUInt32(
_In_ const GUID* ClassGuid,
_In_ const DEVPROPKEY* DeviceProperty,
_In_ ULONG Flags,
_Out_ PULONG Value
)
{
BOOL result;
DEVPROPTYPE devicePropertyType = DEVPROP_TYPE_EMPTY;
ULONG requiredLength = sizeof(ULONG);
result = SetupDiGetClassPropertyW(
ClassGuid,
DeviceProperty,
&devicePropertyType,
(PBYTE)Value,
sizeof(ULONG),
&requiredLength,
Flags
);
if (result && (devicePropertyType == DEVPROP_TYPE_UINT32))
{
return TRUE;
}
*Value = 0;
return FALSE;
}
BOOLEAN PhpGetDevicePropertyInt32(
_In_ HDEVINFO DeviceInfoSet,
_In_ PSP_DEVINFO_DATA DeviceInfoData,
_In_ const DEVPROPKEY* DeviceProperty,
_Out_ PLONG Value
)
{
BOOL result;
DEVPROPTYPE devicePropertyType = DEVPROP_TYPE_EMPTY;
ULONG requiredLength = sizeof(LONG);
result = SetupDiGetDevicePropertyW(
DeviceInfoSet,
DeviceInfoData,
DeviceProperty,
&devicePropertyType,
(PBYTE)Value,
sizeof(LONG),
&requiredLength,
0
);
if (result && (devicePropertyType == DEVPROP_TYPE_INT32))
{
return TRUE;
}
*Value = 0;
return FALSE;
}
BOOLEAN PhpGetDeviceInterfacePropertyInt32(
_In_ HDEVINFO DeviceInfoSet,
_In_ PSP_DEVICE_INTERFACE_DATA DeviceInterfaceData,
_In_ const DEVPROPKEY* DeviceProperty,
_Out_ PLONG Value
)
{
BOOL result;
DEVPROPTYPE devicePropertyType = DEVPROP_TYPE_EMPTY;
ULONG requiredLength = sizeof(LONG);
result = SetupDiGetDeviceInterfacePropertyW(
DeviceInfoSet,
DeviceInterfaceData,
DeviceProperty,
&devicePropertyType,
(PBYTE)Value,
sizeof(LONG),
&requiredLength,
0
);
if (result && (devicePropertyType == DEVPROP_TYPE_INT32))
{
return TRUE;
}
*Value = 0;
return FALSE;
}
BOOLEAN PhpGetClassPropertyInt32(
_In_ const GUID* ClassGuid,
_In_ const DEVPROPKEY* DeviceProperty,
_In_ ULONG Flags,
_Out_ PLONG Value
)
{
BOOL result;
DEVPROPTYPE devicePropertyType = DEVPROP_TYPE_EMPTY;
ULONG requiredLength = sizeof(LONG);
result = SetupDiGetClassPropertyW(
ClassGuid,
DeviceProperty,
&devicePropertyType,
(PBYTE)Value,
sizeof(LONG),
&requiredLength,
Flags
);
if (result && (devicePropertyType == DEVPROP_TYPE_INT32))
{
return TRUE;
}
*Value = 0;
return FALSE;
}
BOOLEAN PhpGetDevicePropertyNTSTATUS(
_In_ HDEVINFO DeviceInfoSet,
_In_ PSP_DEVINFO_DATA DeviceInfoData,
_In_ const DEVPROPKEY* DeviceProperty,
_Out_ PNTSTATUS Status
)
{
BOOL result;
DEVPROPTYPE devicePropertyType = DEVPROP_TYPE_EMPTY;
ULONG requiredLength = sizeof(NTSTATUS);
result = SetupDiGetDevicePropertyW(
DeviceInfoSet,
DeviceInfoData,
DeviceProperty,
&devicePropertyType,
(PBYTE)Status,
sizeof(NTSTATUS),
&requiredLength,
0
);
if (result && (devicePropertyType == DEVPROP_TYPE_NTSTATUS))
{
return TRUE;
}
*Status = 0;
return FALSE;
}
BOOLEAN PhpGetDeviceInterfacePropertyNTSTATUS(
_In_ HDEVINFO DeviceInfoSet,
_In_ PSP_DEVICE_INTERFACE_DATA DeviceInterfaceData,
_In_ const DEVPROPKEY* DeviceProperty,
_Out_ PNTSTATUS Status
)
{
BOOL result;
DEVPROPTYPE devicePropertyType = DEVPROP_TYPE_EMPTY;
ULONG requiredLength = sizeof(NTSTATUS);
result = SetupDiGetDeviceInterfacePropertyW(
DeviceInfoSet,
DeviceInterfaceData,
DeviceProperty,
&devicePropertyType,
(PBYTE)Status,
sizeof(NTSTATUS),
&requiredLength,
0
);
if (result && (devicePropertyType == DEVPROP_TYPE_NTSTATUS))
{
return TRUE;
}
*Status = 0;
return FALSE;
}
BOOLEAN PhpGetClassPropertyNTSTATUS(
_In_ const GUID* ClassGuid,
_In_ const DEVPROPKEY* DeviceProperty,
_In_ ULONG Flags,
_Out_ PNTSTATUS Status
)
{
BOOL result;
DEVPROPTYPE devicePropertyType = DEVPROP_TYPE_EMPTY;
ULONG requiredLength = sizeof(NTSTATUS);
result = SetupDiGetClassPropertyW(
ClassGuid,
DeviceProperty,
&devicePropertyType,
(PBYTE)Status,
sizeof(NTSTATUS),
&requiredLength,
Flags
);
if (result && (devicePropertyType == DEVPROP_TYPE_NTSTATUS))
{
return TRUE;
}
*Status = 0;
return FALSE;
}
BOOLEAN PhpGetDevicePropertyBoolean(
_In_ HDEVINFO DeviceInfoSet,
_In_ PSP_DEVINFO_DATA DeviceInfoData,
_In_ const DEVPROPKEY* DeviceProperty,
_Out_ PBOOLEAN Boolean
)
{
BOOL result;
DEVPROPTYPE devicePropertyType = DEVPROP_TYPE_EMPTY;
DEVPROP_BOOLEAN boolean;
ULONG requiredLength = sizeof(DEVPROP_BOOLEAN);
result = SetupDiGetDevicePropertyW(
DeviceInfoSet,
DeviceInfoData,
DeviceProperty,
&devicePropertyType,
(PBYTE)&boolean,
sizeof(DEVPROP_BOOLEAN),
&requiredLength,
0
);
if (result && (devicePropertyType == DEVPROP_TYPE_BOOLEAN))
{
*Boolean = boolean == DEVPROP_TRUE;
return TRUE;
}
*Boolean = FALSE;
return FALSE;
}
BOOLEAN PhpGetDeviceInterfacePropertyBoolean(
_In_ HDEVINFO DeviceInfoSet,
_In_ PSP_DEVICE_INTERFACE_DATA DeviceInterfaceData,
_In_ const DEVPROPKEY* DeviceProperty,
_Out_ PBOOLEAN Boolean
)
{
BOOL result;
DEVPROPTYPE devicePropertyType = DEVPROP_TYPE_EMPTY;
DEVPROP_BOOLEAN boolean;
ULONG requiredLength = sizeof(DEVPROP_BOOLEAN);
result = SetupDiGetDeviceInterfacePropertyW(
DeviceInfoSet,
DeviceInterfaceData,
DeviceProperty,
&devicePropertyType,
(PBYTE)&boolean,
sizeof(DEVPROP_BOOLEAN),
&requiredLength,
0
);
if (result && (devicePropertyType == DEVPROP_TYPE_BOOLEAN))
{
*Boolean = boolean == DEVPROP_TRUE;
return TRUE;
}
*Boolean = FALSE;
return FALSE;
}
BOOLEAN PhpGetClassPropertyBoolean(
_In_ const GUID* ClassGuid,
_In_ const DEVPROPKEY* DeviceProperty,
_In_ ULONG Flags,
_Out_ PBOOLEAN Boolean
)
{
BOOL result;
DEVPROPTYPE devicePropertyType = DEVPROP_TYPE_EMPTY;
DEVPROP_BOOLEAN boolean;
ULONG requiredLength = sizeof(DEVPROP_BOOLEAN);
result = SetupDiGetClassPropertyW(
ClassGuid,
DeviceProperty,
&devicePropertyType,
(PBYTE)&boolean,
sizeof(DEVPROP_BOOLEAN),
&requiredLength,
Flags
);
if (result && (devicePropertyType == DEVPROP_TYPE_BOOLEAN))
{
*Boolean = boolean == DEVPROP_TRUE;
return TRUE;
}
*Boolean = FALSE;
return FALSE;
}
BOOLEAN PhpGetDevicePropertyTimeStamp(
_In_ HDEVINFO DeviceInfoSet,
_In_ PSP_DEVINFO_DATA DeviceInfoData,
_In_ const DEVPROPKEY* DeviceProperty,
_Out_ PLARGE_INTEGER TimeStamp
)
{
BOOL result;
DEVPROPTYPE devicePropertyType = DEVPROP_TYPE_EMPTY;
FILETIME fileTime;
ULONG requiredLength = sizeof(FILETIME);
result = SetupDiGetDevicePropertyW(
DeviceInfoSet,
DeviceInfoData,
DeviceProperty,
&devicePropertyType,
(PBYTE)&fileTime,
sizeof(FILETIME),
&requiredLength,
0
);
if (result && (devicePropertyType == DEVPROP_TYPE_FILETIME))
{
TimeStamp->HighPart = fileTime.dwHighDateTime;
TimeStamp->LowPart = fileTime.dwLowDateTime;
return TRUE;
}
TimeStamp->QuadPart = 0;
return FALSE;
}
BOOLEAN PhpGetDeviceInterfacePropertyTimeStamp(
_In_ HDEVINFO DeviceInfoSet,
_In_ PSP_DEVICE_INTERFACE_DATA DeviceInterfaceData,
_In_ const DEVPROPKEY* DeviceProperty,
_Out_ PLARGE_INTEGER TimeStamp
)
{
BOOL result;
DEVPROPTYPE devicePropertyType = DEVPROP_TYPE_EMPTY;
FILETIME fileTime;
ULONG requiredLength = sizeof(FILETIME);
result = SetupDiGetDeviceInterfacePropertyW(
DeviceInfoSet,
DeviceInterfaceData,
DeviceProperty,
&devicePropertyType,
(PBYTE)&fileTime,
sizeof(FILETIME),
&requiredLength,
0
);
if (result && (devicePropertyType == DEVPROP_TYPE_FILETIME))
{
TimeStamp->HighPart = fileTime.dwHighDateTime;
TimeStamp->LowPart = fileTime.dwLowDateTime;
return TRUE;
}
TimeStamp->QuadPart = 0;
return FALSE;
}
BOOLEAN PhpGetClassPropertyTimeStamp(
_In_ const GUID* ClassGuid,
_In_ const DEVPROPKEY* DeviceProperty,
_In_ ULONG Flags,
_Out_ PLARGE_INTEGER TimeStamp
)
{
BOOL result;
DEVPROPTYPE devicePropertyType = DEVPROP_TYPE_EMPTY;
FILETIME fileTime;
ULONG requiredLength = sizeof(FILETIME);
result = SetupDiGetClassPropertyW(
ClassGuid,
DeviceProperty,
&devicePropertyType,
(PBYTE)&fileTime,
sizeof(FILETIME),
&requiredLength,
Flags
);
if (result && (devicePropertyType == DEVPROP_TYPE_FILETIME))
{
TimeStamp->HighPart = fileTime.dwHighDateTime;
TimeStamp->LowPart = fileTime.dwLowDateTime;
return TRUE;
}
TimeStamp->QuadPart = 0;
return FALSE;
}
BOOLEAN PhpGetDevicePropertyString(
_In_ HDEVINFO DeviceInfoSet,
_In_ PSP_DEVINFO_DATA DeviceInfoData,
_In_ const DEVPROPKEY* DeviceProperty,
_Out_ PPH_STRING* String
)
{
BOOL result;
DEVPROPTYPE devicePropertyType = DEVPROP_TYPE_EMPTY;
ULONG requiredLength = 0;
PPH_STRING string;
*String = NULL;
result = SetupDiGetDevicePropertyW(
DeviceInfoSet,
DeviceInfoData,
DeviceProperty,
&devicePropertyType,
NULL,
0,
&requiredLength,
0
);
if (result ||
(requiredLength == 0) ||
(GetLastError() != ERROR_INSUFFICIENT_BUFFER) ||
((devicePropertyType != DEVPROP_TYPE_STRING) &&
(devicePropertyType != DEVPROP_TYPE_SECURITY_DESCRIPTOR_STRING)))
{
return FALSE;
}
if (requiredLength < sizeof(UNICODE_NULL))
{
*String = PhReferenceEmptyString();
return TRUE;
}
string = PhCreateStringEx(NULL, requiredLength - sizeof(UNICODE_NULL));
if (SetupDiGetDevicePropertyW(
DeviceInfoSet,
DeviceInfoData,
DeviceProperty,
&devicePropertyType,
(PBYTE)string->Buffer,
requiredLength,
&requiredLength,
0
))
{
*String = string;
return TRUE;
}
PhClearReference(&string);
return FALSE;
}
BOOLEAN PhpGetDeviceInterfacePropertyString(
_In_ HDEVINFO DeviceInfoSet,
_In_ PSP_DEVICE_INTERFACE_DATA DeviceInterfaceData,
_In_ const DEVPROPKEY* DeviceProperty,
_Out_ PPH_STRING* String
)
{
BOOL result;
DEVPROPTYPE devicePropertyType = DEVPROP_TYPE_EMPTY;
ULONG requiredLength = 0;
PPH_STRING string;
*String = NULL;
result = SetupDiGetDeviceInterfacePropertyW(
DeviceInfoSet,
DeviceInterfaceData,
DeviceProperty,
&devicePropertyType,
NULL,
0,
&requiredLength,
0
);
if (result ||
(requiredLength == 0) ||
(GetLastError() != ERROR_INSUFFICIENT_BUFFER) ||
((devicePropertyType != DEVPROP_TYPE_STRING) &&
(devicePropertyType != DEVPROP_TYPE_SECURITY_DESCRIPTOR_STRING)))
{
return FALSE;
}
if (requiredLength < sizeof(UNICODE_NULL))
{
*String = PhReferenceEmptyString();
return TRUE;
}
string = PhCreateStringEx(NULL, requiredLength - sizeof(UNICODE_NULL));
if (SetupDiGetDeviceInterfacePropertyW(
DeviceInfoSet,
DeviceInterfaceData,
DeviceProperty,
&devicePropertyType,
(PBYTE)string->Buffer,
requiredLength,
&requiredLength,
0
))
{
*String = string;
return TRUE;
}
PhClearReference(&string);
return FALSE;
}
BOOLEAN PhpGetClassPropertyString(
_In_ const GUID* ClassGuid,
_In_ const DEVPROPKEY* DeviceProperty,
_In_ ULONG Flags,
_Out_ PPH_STRING* String
)
{
BOOL result;
DEVPROPTYPE devicePropertyType = DEVPROP_TYPE_EMPTY;
ULONG requiredLength = 0;
PPH_STRING string;
*String = NULL;
result = SetupDiGetClassPropertyW(
ClassGuid,
DeviceProperty,
&devicePropertyType,
NULL,
0,
&requiredLength,
Flags
);
if (result ||
(requiredLength == 0) ||
(GetLastError() != ERROR_INSUFFICIENT_BUFFER) ||
((devicePropertyType != DEVPROP_TYPE_STRING) &&
(devicePropertyType != DEVPROP_TYPE_SECURITY_DESCRIPTOR_STRING)))
{
return FALSE;
}
if (requiredLength < sizeof(UNICODE_NULL))
{
*String = PhReferenceEmptyString();
return TRUE;
}
string = PhCreateStringEx(NULL, requiredLength - sizeof(UNICODE_NULL));
if (SetupDiGetClassPropertyW(
ClassGuid,
DeviceProperty,
&devicePropertyType,
(PBYTE)string->Buffer,
requiredLength,
&requiredLength,
Flags
))
{
*String = string;
return TRUE;
}
PhClearReference(&string);
return FALSE;
}
BOOLEAN PhpGetDevicePropertyStringList(
_In_ HDEVINFO DeviceInfoSet,
_In_ PSP_DEVINFO_DATA DeviceInfoData,
_In_ const DEVPROPKEY* DeviceProperty,
_Out_ PPH_LIST* StringList
)
{
BOOL result;
DEVPROPTYPE devicePropertyType = DEVPROP_TYPE_EMPTY;
ULONG requiredLength = 0;
PVOID buffer = NULL;
PPH_LIST stringList;
*StringList = NULL;
result = SetupDiGetDevicePropertyW(
DeviceInfoSet,
DeviceInfoData,
DeviceProperty,
&devicePropertyType,
NULL,
0,
&requiredLength,
0
);
if (result ||
(requiredLength == 0) ||
(GetLastError() != ERROR_INSUFFICIENT_BUFFER) ||
(devicePropertyType != DEVPROP_TYPE_STRING_LIST))
{
goto Exit;
}
buffer = PhAllocate(requiredLength);
result = SetupDiGetDevicePropertyW(
DeviceInfoSet,
DeviceInfoData,
DeviceProperty,
&devicePropertyType,
buffer,
requiredLength,
&requiredLength,
0
);
if (!result)
{
goto Exit;
}
stringList = PhCreateList(1);
for (PZZWSTR item = buffer;;)
{
PH_STRINGREF string;
PhInitializeStringRefLongHint(&string, item);
if (string.Length == 0)
{
break;
}
PhAddItemList(stringList, PhCreateString2(&string));
item = PTR_ADD_OFFSET(item, string.Length + sizeof(UNICODE_NULL));
}
*StringList = stringList;
Exit:
if (buffer)
PhFree(buffer);
return !!result;
}
BOOLEAN PhpGetDeviceInterfacePropertyStringList(
_In_ HDEVINFO DeviceInfoSet,
_In_ PSP_DEVICE_INTERFACE_DATA DeviceInterfaceData,
_In_ const DEVPROPKEY* DeviceProperty,
_Out_ PPH_LIST* StringList
)
{
BOOL result;
DEVPROPTYPE devicePropertyType = DEVPROP_TYPE_EMPTY;
ULONG requiredLength = 0;
PVOID buffer = NULL;
PPH_LIST stringList;
*StringList = NULL;
result = SetupDiGetDeviceInterfacePropertyW(
DeviceInfoSet,
DeviceInterfaceData,
DeviceProperty,
&devicePropertyType,
NULL,
0,
&requiredLength,
0
);
if (result ||
(requiredLength == 0) ||
(GetLastError() != ERROR_INSUFFICIENT_BUFFER) ||
(devicePropertyType != DEVPROP_TYPE_STRING_LIST))
{
goto Exit;
}
buffer = PhAllocate(requiredLength);
result = SetupDiGetDeviceInterfacePropertyW(
DeviceInfoSet,
DeviceInterfaceData,
DeviceProperty,
&devicePropertyType,
buffer,
requiredLength,
&requiredLength,
0
);
if (!result)
{
goto Exit;
}
stringList = PhCreateList(1);
for (PZZWSTR item = buffer;;)
{
PH_STRINGREF string;
PhInitializeStringRefLongHint(&string, item);
if (string.Length == 0)
{
break;
}
PhAddItemList(stringList, PhCreateString2(&string));
item = PTR_ADD_OFFSET(item, string.Length + sizeof(UNICODE_NULL));
}
*StringList = stringList;
Exit:
if (buffer)
PhFree(buffer);
return !!result;
}
BOOLEAN PhpGetClassPropertyStringList(
_In_ const GUID* ClassGuid,
_In_ const DEVPROPKEY* DeviceProperty,
_In_ ULONG Flags,
_Out_ PPH_LIST* StringList
)
{
BOOL result;
DEVPROPTYPE devicePropertyType = DEVPROP_TYPE_EMPTY;
ULONG requiredLength = 0;
PVOID buffer = NULL;
PPH_LIST stringList;
*StringList = NULL;
result = SetupDiGetClassPropertyW(
ClassGuid,
DeviceProperty,
&devicePropertyType,
NULL,
0,
&requiredLength,
Flags
);
if (result ||
(requiredLength == 0) ||
(GetLastError() != ERROR_INSUFFICIENT_BUFFER) ||
(devicePropertyType != DEVPROP_TYPE_STRING_LIST))
{
goto Exit;
}
buffer = PhAllocate(requiredLength);
result = SetupDiGetClassPropertyW(
ClassGuid,
DeviceProperty,
&devicePropertyType,
buffer,
requiredLength,
&requiredLength,
Flags
);
if (!result)
{
goto Exit;
}
stringList = PhCreateList(1);
for (PZZWSTR item = buffer;;)
{
PH_STRINGREF string;
PhInitializeStringRefLongHint(&string, item);
if (string.Length == 0)
{
break;
}
PhAddItemList(stringList, PhCreateString2(&string));
item = PTR_ADD_OFFSET(item, string.Length + sizeof(UNICODE_NULL));
}
*StringList = stringList;
Exit:
if (buffer)
PhFree(buffer);
return !!result;
}
BOOLEAN PhpGetDevicePropertyBinary(
_In_ HDEVINFO DeviceInfoSet,
_In_ PSP_DEVINFO_DATA DeviceInfoData,
_In_ const DEVPROPKEY* DeviceProperty,
_Out_ PBYTE* Buffer,
_Out_ PULONG Size
)
{
BOOL result;
DEVPROPTYPE devicePropertyType = DEVPROP_TYPE_EMPTY;
ULONG requiredLength = 0;
PVOID buffer = NULL;
*Buffer = NULL;
*Size = 0;
result = SetupDiGetDevicePropertyW(
DeviceInfoSet,
DeviceInfoData,
DeviceProperty,
&devicePropertyType,
NULL,
0,
&requiredLength,
0
);
if (result ||
(requiredLength == 0) ||
(GetLastError() != ERROR_INSUFFICIENT_BUFFER) ||
((devicePropertyType != DEVPROP_TYPE_BINARY) &&
(devicePropertyType != DEVPROP_TYPE_SECURITY_DESCRIPTOR)))
{
goto Exit;
}
buffer = PhAllocate(requiredLength);
result = SetupDiGetDevicePropertyW(
DeviceInfoSet,
DeviceInfoData,
DeviceProperty,
&devicePropertyType,
buffer,
requiredLength,
&requiredLength,
0
);
if (!result)
{
goto Exit;
}
*Size = requiredLength;
*Buffer = buffer;
buffer = NULL;
Exit:
if (buffer)
PhFree(buffer);
return !!result;
}
BOOLEAN PhpGetDeviceInterfacePropertyBinary(
_In_ HDEVINFO DeviceInfoSet,
_In_ PSP_DEVICE_INTERFACE_DATA DeviceInterfaceData,
_In_ const DEVPROPKEY* DeviceProperty,
_Out_ PBYTE* Buffer,
_Out_ PULONG Size
)
{
BOOL result;
DEVPROPTYPE devicePropertyType = DEVPROP_TYPE_EMPTY;
ULONG requiredLength = 0;
PVOID buffer = NULL;
*Buffer = NULL;
*Size = 0;
result = SetupDiGetDeviceInterfacePropertyW(
DeviceInfoSet,
DeviceInterfaceData,
DeviceProperty,
&devicePropertyType,
NULL,
0,
&requiredLength,
0
);
if (result ||
(requiredLength == 0) ||
(GetLastError() != ERROR_INSUFFICIENT_BUFFER) ||
((devicePropertyType != DEVPROP_TYPE_BINARY) &&
(devicePropertyType != DEVPROP_TYPE_SECURITY_DESCRIPTOR)))
{
goto Exit;
}
buffer = PhAllocate(requiredLength);
result = SetupDiGetDeviceInterfacePropertyW(
DeviceInfoSet,
DeviceInterfaceData,
DeviceProperty,
&devicePropertyType,
buffer,
requiredLength,
&requiredLength,
0
);
if (!result)
{
goto Exit;
}
*Size = requiredLength;
*Buffer = buffer;
buffer = NULL;
Exit:
if (buffer)
PhFree(buffer);
return !!result;
}
BOOLEAN PhpGetClassPropertyBinary(
_In_ const GUID* ClassGuid,
_In_ const DEVPROPKEY* DeviceProperty,
_In_ ULONG Flags,
_Out_ PBYTE* Buffer,
_Out_ PULONG Size
)
{
BOOL result;
DEVPROPTYPE devicePropertyType = DEVPROP_TYPE_EMPTY;
ULONG requiredLength = 0;
PVOID buffer = NULL;
*Buffer = NULL;
*Size = 0;
result = SetupDiGetClassPropertyW(
ClassGuid,
DeviceProperty,
&devicePropertyType,
NULL,
0,
&requiredLength,
Flags
);
if (result ||
(requiredLength == 0) ||
(GetLastError() != ERROR_INSUFFICIENT_BUFFER) ||
((devicePropertyType != DEVPROP_TYPE_BINARY) &&
(devicePropertyType != DEVPROP_TYPE_SECURITY_DESCRIPTOR)))
{
goto Exit;
}
buffer = PhAllocate(requiredLength);
result = SetupDiGetClassPropertyW(
ClassGuid,
DeviceProperty,
&devicePropertyType,
buffer,
requiredLength,
&requiredLength,
Flags
);
if (!result)
{
goto Exit;
}
*Size = requiredLength;
*Buffer = buffer;
buffer = NULL;
Exit:
if (buffer)
PhFree(buffer);
return !!result;
}
_Function_class_(PH_DEVICE_PROPERTY_FILL_CALLBACK)
VOID NTAPI PhpDevPropFillString(
_In_ HDEVINFO DeviceInfoSet,
_In_ PPH_DEVINFO_DATA DeviceInfoData,
_In_ const DEVPROPKEY* PropertyKey,
_Out_ PPH_DEVICE_PROPERTY Property,
_In_ ULONG Flags
)
{
Property->Type = PhDevicePropertyTypeString;
if (DeviceInfoData->Interface)
{
if (Flags & DEVPROP_FILL_FLAG_CLASS)
{
Property->Valid = PhpGetClassPropertyString(
&DeviceInfoData->InterfaceData.InterfaceClassGuid,
PropertyKey,
DICLASSPROP_INTERFACE,
&Property->String
);
}
else
{
Property->Valid = PhpGetDeviceInterfacePropertyString(
DeviceInfoSet,
&DeviceInfoData->InterfaceData,
PropertyKey,
&Property->String
);
}
}
else
{
if (Flags & DEVPROP_FILL_FLAG_CLASS)
{
Property->Valid = PhpGetClassPropertyString(
&DeviceInfoData->DeviceData.ClassGuid,
PropertyKey,
DICLASSPROP_INSTALLER,
&Property->String
);
}
else
{
Property->Valid = PhpGetDevicePropertyString(
DeviceInfoSet,
&DeviceInfoData->DeviceData,
PropertyKey,
&Property->String
);
}
}
if (Property->Valid)
{
Property->AsString = Property->String;
PhReferenceObject(Property->AsString);
}
}
VOID PhpDevPropFillUInt64Common(
_In_ HDEVINFO DeviceInfoSet,
_In_ PPH_DEVINFO_DATA DeviceInfoData,
_In_ const DEVPROPKEY* PropertyKey,
_Out_ PPH_DEVICE_PROPERTY Property,
_In_ ULONG Flags
)
{
Property->Type = PhDevicePropertyTypeUInt64;
if (DeviceInfoData->Interface)
{
if (Flags & DEVPROP_FILL_FLAG_CLASS)
{
Property->Valid = PhpGetClassPropertyUInt64(
&DeviceInfoData->InterfaceData.InterfaceClassGuid,
PropertyKey,
DICLASSPROP_INTERFACE,
&Property->UInt64
);
}
else
{
Property->Valid = PhpGetDeviceInterfacePropertyUInt64(
DeviceInfoSet,
&DeviceInfoData->InterfaceData,
PropertyKey,
&Property->UInt64
);
}
}
else
{
if (Flags & DEVPROP_FILL_FLAG_CLASS)
{
Property->Valid = PhpGetClassPropertyUInt64(
&DeviceInfoData->DeviceData.ClassGuid,
PropertyKey,
DICLASSPROP_INSTALLER,
&Property->UInt64
);
}
else
{
Property->Valid = PhpGetDevicePropertyUInt64(
DeviceInfoSet,
&DeviceInfoData->DeviceData,
PropertyKey,
&Property->UInt64
);
}
}
}
_Function_class_(PH_DEVICE_PROPERTY_FILL_CALLBACK)
VOID NTAPI PhpDevPropFillUInt64(
_In_ HDEVINFO DeviceInfoSet,
_In_ PPH_DEVINFO_DATA DeviceInfoData,
_In_ const DEVPROPKEY* PropertyKey,
_Out_ PPH_DEVICE_PROPERTY Property,
_In_ ULONG Flags
)
{
PhpDevPropFillUInt64Common(
DeviceInfoSet,
DeviceInfoData,
PropertyKey,
Property,
Flags
);
if (Property->Valid)
{
PH_FORMAT format[1];
PhInitFormatI64U(&format[0], Property->UInt64);
Property->AsString = PhFormat(format, ARRAYSIZE(format), 1);
}
}
_Function_class_(PH_DEVICE_PROPERTY_FILL_CALLBACK)
VOID NTAPI PhpDevPropFillUInt64Hex(
_In_ HDEVINFO DeviceInfoSet,
_In_ PPH_DEVINFO_DATA DeviceInfoData,
_In_ const DEVPROPKEY* PropertyKey,
_Out_ PPH_DEVICE_PROPERTY Property,
_In_ ULONG Flags
)
{
PhpDevPropFillUInt64Common(
DeviceInfoSet,
DeviceInfoData,
PropertyKey,
Property,
Flags
);
if (Property->Valid)
{
PH_FORMAT format[2];
PhInitFormatI64X(&format[1], Property->UInt64);
Property->AsString = PhFormat(format, ARRAYSIZE(format), 10);
}
}
VOID PhpDevPropFillInt64Common(
_In_ HDEVINFO DeviceInfoSet,
_In_ PPH_DEVINFO_DATA DeviceInfoData,
_In_ const DEVPROPKEY* PropertyKey,
_Out_ PPH_DEVICE_PROPERTY Property,
_In_ ULONG Flags
)
{
Property->Type = PhDevicePropertyTypeInt64;
if (DeviceInfoData->Interface)
{
if (Flags & DEVPROP_FILL_FLAG_CLASS)
{
Property->Valid = PhpGetClassPropertyInt64(
&DeviceInfoData->InterfaceData.InterfaceClassGuid,
PropertyKey,
DICLASSPROP_INTERFACE,
&Property->Int64
);
}
else
{
Property->Valid = PhpGetDeviceInterfacePropertyInt64(
DeviceInfoSet,
&DeviceInfoData->InterfaceData,
PropertyKey,
&Property->Int64
);
}
}
else
{
if (Flags & DEVPROP_FILL_FLAG_CLASS)
{
Property->Valid = PhpGetClassPropertyInt64(
&DeviceInfoData->DeviceData.ClassGuid,
PropertyKey,
DICLASSPROP_INSTALLER,
&Property->Int64
);
}
else
{
Property->Valid = PhpGetDevicePropertyInt64(
DeviceInfoSet,
&DeviceInfoData->DeviceData,
PropertyKey,
&Property->Int64
);
}
}
}
_Function_class_(PH_DEVICE_PROPERTY_FILL_CALLBACK)
VOID NTAPI PhpDevPropFillInt64(
_In_ HDEVINFO DeviceInfoSet,
_In_ PPH_DEVINFO_DATA DeviceInfoData,
_In_ const DEVPROPKEY* PropertyKey,
_Out_ PPH_DEVICE_PROPERTY Property,
_In_ ULONG Flags
)
{
PhpDevPropFillInt64Common(
DeviceInfoSet,
DeviceInfoData,
PropertyKey,
Property,
Flags
);
if (Property->Valid)
{
PH_FORMAT format[1];
PhInitFormatI64D(&format[0], Property->Int64);
Property->AsString = PhFormat(format, ARRAYSIZE(format), 1);
}
}
VOID PhpDevPropFillUInt32Common(
_In_ HDEVINFO DeviceInfoSet,
_In_ PPH_DEVINFO_DATA DeviceInfoData,
_In_ const DEVPROPKEY* PropertyKey,
_Out_ PPH_DEVICE_PROPERTY Property,
_In_ ULONG Flags
)
{
Property->Type = PhDevicePropertyTypeUInt32;
if (DeviceInfoData->Interface)
{
if (Flags & DEVPROP_FILL_FLAG_CLASS)
{
Property->Valid = PhpGetClassPropertyUInt32(
&DeviceInfoData->InterfaceData.InterfaceClassGuid,
PropertyKey,
DICLASSPROP_INTERFACE,
&Property->UInt32
);
}
else
{
Property->Valid = PhpGetDeviceInterfacePropertyUInt32(
DeviceInfoSet,
&DeviceInfoData->InterfaceData,
PropertyKey,
&Property->UInt32
);
}
}
else
{
if (Flags & DEVPROP_FILL_FLAG_CLASS)
{
Property->Valid = PhpGetClassPropertyUInt32(
&DeviceInfoData->DeviceData.ClassGuid,
PropertyKey,
DICLASSPROP_INSTALLER,
&Property->UInt32
);
}
else
{
Property->Valid = PhpGetDevicePropertyUInt32(
DeviceInfoSet,
&DeviceInfoData->DeviceData,
PropertyKey,
&Property->UInt32
);
}
}
}
_Function_class_(PH_DEVICE_PROPERTY_FILL_CALLBACK)
VOID NTAPI PhpDevPropFillUInt32(
_In_ HDEVINFO DeviceInfoSet,
_In_ PPH_DEVINFO_DATA DeviceInfoData,
_In_ const DEVPROPKEY* PropertyKey,
_Out_ PPH_DEVICE_PROPERTY Property,
_In_ ULONG Flags
)
{
PhpDevPropFillUInt32Common(
DeviceInfoSet,
DeviceInfoData,
PropertyKey,
Property,
Flags
);
if (Property->Valid)
{
PH_FORMAT format[1];
PhInitFormatU(&format[0], Property->UInt32);
Property->AsString = PhFormat(format, ARRAYSIZE(format), 1);
}
}
_Function_class_(PH_DEVICE_PROPERTY_FILL_CALLBACK)
VOID NTAPI PhpDevPropFillUInt32Hex(
_In_ HDEVINFO DeviceInfoSet,
_In_ PPH_DEVINFO_DATA DeviceInfoData,
_In_ const DEVPROPKEY* PropertyKey,
_Out_ PPH_DEVICE_PROPERTY Property,
_In_ ULONG Flags
)
{
PhpDevPropFillUInt32Common(
DeviceInfoSet,
DeviceInfoData,
PropertyKey,
Property,
Flags
);
if (Property->Valid)
{
PH_FORMAT format[2];
PhInitFormatS(&format[0], L"0x");
PhInitFormatIX(&format[1], Property->UInt32);
Property->AsString = PhFormat(format, ARRAYSIZE(format), 10);
}
}
_Function_class_(PH_DEVICE_PROPERTY_FILL_CALLBACK)
VOID NTAPI PhpDevPropFillInt32(
_In_ HDEVINFO DeviceInfoSet,
_In_ PPH_DEVINFO_DATA DeviceInfoData,
_In_ const DEVPROPKEY* PropertyKey,
_Out_ PPH_DEVICE_PROPERTY Property,
_In_ ULONG Flags
)
{
Property->Type = PhDevicePropertyTypeUInt32;
if (DeviceInfoData->Interface)
{
if (Flags & DEVPROP_FILL_FLAG_CLASS)
{
Property->Valid = PhpGetClassPropertyInt32(
&DeviceInfoData->InterfaceData.InterfaceClassGuid,
PropertyKey,
DICLASSPROP_INTERFACE,
&Property->Int32
);
}
else
{
Property->Valid = PhpGetDeviceInterfacePropertyInt32(
DeviceInfoSet,
&DeviceInfoData->InterfaceData,
PropertyKey,
&Property->Int32
);
}
}
else
{
if (Flags & DEVPROP_FILL_FLAG_CLASS)
{
Property->Valid = PhpGetClassPropertyInt32(
&DeviceInfoData->DeviceData.ClassGuid,
PropertyKey,
DICLASSPROP_INSTALLER,
&Property->Int32
);
}
else
{
Property->Valid = PhpGetDevicePropertyInt32(
DeviceInfoSet,
&DeviceInfoData->DeviceData,
PropertyKey,
&Property->Int32
);
}
}
if (Property->Valid)
{
PH_FORMAT format[1];
PhInitFormatD(&format[0], Property->Int32);
Property->AsString = PhFormat(format, ARRAYSIZE(format), 1);
}
}
_Function_class_(PH_DEVICE_PROPERTY_FILL_CALLBACK)
VOID NTAPI PhpDevPropFillNTSTATUS(
_In_ HDEVINFO DeviceInfoSet,
_In_ PPH_DEVINFO_DATA DeviceInfoData,
_In_ const DEVPROPKEY* PropertyKey,
_Out_ PPH_DEVICE_PROPERTY Property,
_In_ ULONG Flags
)
{
Property->Type = PhDevicePropertyTypeNTSTATUS;
if (DeviceInfoData->Interface)
{
if (Flags & DEVPROP_FILL_FLAG_CLASS)
{
Property->Valid = PhpGetClassPropertyNTSTATUS(
&DeviceInfoData->InterfaceData.InterfaceClassGuid,
PropertyKey,
DICLASSPROP_INTERFACE,
&Property->Status
);
}
else
{
Property->Valid = PhpGetDeviceInterfacePropertyNTSTATUS(
DeviceInfoSet,
&DeviceInfoData->InterfaceData,
PropertyKey,
&Property->Status
);
}
}
else
{
if (Flags & DEVPROP_FILL_FLAG_CLASS)
{
Property->Valid = PhpGetClassPropertyNTSTATUS(
&DeviceInfoData->DeviceData.ClassGuid,
PropertyKey,
DICLASSPROP_INSTALLER,
&Property->Status
);
}
else
{
Property->Valid = PhpGetDevicePropertyNTSTATUS(
DeviceInfoSet,
&DeviceInfoData->DeviceData,
PropertyKey,
&Property->Status
);
}
}
if (Property->Valid && Property->Status != STATUS_SUCCESS)
{
Property->AsString = PhGetStatusMessage(Property->Status, 0);
}
}
typedef struct _PH_WELL_KNOWN_GUID
{
const GUID* Guid;
PH_STRINGREF Symbol;
} PH_WELL_KNOWN_GUID, *PPH_WELL_KNOWN_GUID;
#define PH_DEFINE_WELL_KNOWN_GUID(guid) const PH_WELL_KNOWN_GUID PH_WELL_KNOWN_##guid = { &(guid), PH_STRINGREF_INIT(TEXT(#guid)) }
PH_DEFINE_WELL_KNOWN_GUID(GUID_HWPROFILE_QUERY_CHANGE);
PH_DEFINE_WELL_KNOWN_GUID(GUID_HWPROFILE_CHANGE_CANCELLED);
PH_DEFINE_WELL_KNOWN_GUID(GUID_HWPROFILE_CHANGE_COMPLETE);
PH_DEFINE_WELL_KNOWN_GUID(GUID_DEVICE_INTERFACE_ARRIVAL);
PH_DEFINE_WELL_KNOWN_GUID(GUID_DEVICE_INTERFACE_REMOVAL);
PH_DEFINE_WELL_KNOWN_GUID(GUID_TARGET_DEVICE_QUERY_REMOVE);
PH_DEFINE_WELL_KNOWN_GUID(GUID_TARGET_DEVICE_REMOVE_CANCELLED);
PH_DEFINE_WELL_KNOWN_GUID(GUID_TARGET_DEVICE_REMOVE_COMPLETE);
PH_DEFINE_WELL_KNOWN_GUID(GUID_PNP_CUSTOM_NOTIFICATION);
PH_DEFINE_WELL_KNOWN_GUID(GUID_PNP_POWER_NOTIFICATION);
PH_DEFINE_WELL_KNOWN_GUID(GUID_PNP_POWER_SETTING_CHANGE);
PH_DEFINE_WELL_KNOWN_GUID(GUID_TARGET_DEVICE_TRANSPORT_RELATIONS_CHANGED);
PH_DEFINE_WELL_KNOWN_GUID(GUID_KERNEL_SOFT_RESTART_PREPARE);
PH_DEFINE_WELL_KNOWN_GUID(GUID_KERNEL_SOFT_RESTART_CANCEL);
PH_DEFINE_WELL_KNOWN_GUID(GUID_RECOVERY_PCI_PREPARE_SHUTDOWN);
PH_DEFINE_WELL_KNOWN_GUID(GUID_RECOVERY_NVMED_PREPARE_SHUTDOWN);
PH_DEFINE_WELL_KNOWN_GUID(GUID_KERNEL_SOFT_RESTART_FINALIZE);
PH_DEFINE_WELL_KNOWN_GUID(GUID_BUS_INTERFACE_STANDARD);
PH_DEFINE_WELL_KNOWN_GUID(GUID_PCI_BUS_INTERFACE_STANDARD);
PH_DEFINE_WELL_KNOWN_GUID(GUID_PCI_BUS_INTERFACE_STANDARD2);
PH_DEFINE_WELL_KNOWN_GUID(GUID_ARBITER_INTERFACE_STANDARD);
PH_DEFINE_WELL_KNOWN_GUID(GUID_TRANSLATOR_INTERFACE_STANDARD);
PH_DEFINE_WELL_KNOWN_GUID(GUID_ACPI_INTERFACE_STANDARD);
PH_DEFINE_WELL_KNOWN_GUID(GUID_INT_ROUTE_INTERFACE_STANDARD);
PH_DEFINE_WELL_KNOWN_GUID(GUID_PCMCIA_BUS_INTERFACE_STANDARD);
PH_DEFINE_WELL_KNOWN_GUID(GUID_ACPI_REGS_INTERFACE_STANDARD);
PH_DEFINE_WELL_KNOWN_GUID(GUID_LEGACY_DEVICE_DETECTION_STANDARD);
PH_DEFINE_WELL_KNOWN_GUID(GUID_PCI_DEVICE_PRESENT_INTERFACE);
PH_DEFINE_WELL_KNOWN_GUID(GUID_MF_ENUMERATION_INTERFACE);
PH_DEFINE_WELL_KNOWN_GUID(GUID_REENUMERATE_SELF_INTERFACE_STANDARD);
PH_DEFINE_WELL_KNOWN_GUID(GUID_AGP_TARGET_BUS_INTERFACE_STANDARD);
PH_DEFINE_WELL_KNOWN_GUID(GUID_ACPI_CMOS_INTERFACE_STANDARD);
PH_DEFINE_WELL_KNOWN_GUID(GUID_ACPI_PORT_RANGES_INTERFACE_STANDARD);
PH_DEFINE_WELL_KNOWN_GUID(GUID_ACPI_INTERFACE_STANDARD2);
PH_DEFINE_WELL_KNOWN_GUID(GUID_PNP_LOCATION_INTERFACE);
PH_DEFINE_WELL_KNOWN_GUID(GUID_PCI_EXPRESS_LINK_QUIESCENT_INTERFACE);
PH_DEFINE_WELL_KNOWN_GUID(GUID_PCI_EXPRESS_ROOT_PORT_INTERFACE);
PH_DEFINE_WELL_KNOWN_GUID(GUID_MSIX_TABLE_CONFIG_INTERFACE);
PH_DEFINE_WELL_KNOWN_GUID(GUID_D3COLD_SUPPORT_INTERFACE);
PH_DEFINE_WELL_KNOWN_GUID(GUID_PROCESSOR_PCC_INTERFACE_STANDARD);
PH_DEFINE_WELL_KNOWN_GUID(GUID_PCI_VIRTUALIZATION_INTERFACE);
PH_DEFINE_WELL_KNOWN_GUID(GUID_PCC_INTERFACE_STANDARD);
PH_DEFINE_WELL_KNOWN_GUID(GUID_PCC_INTERFACE_INTERNAL);
PH_DEFINE_WELL_KNOWN_GUID(GUID_THERMAL_COOLING_INTERFACE);
PH_DEFINE_WELL_KNOWN_GUID(GUID_DMA_CACHE_COHERENCY_INTERFACE);
PH_DEFINE_WELL_KNOWN_GUID(GUID_DEVICE_RESET_INTERFACE_STANDARD);
PH_DEFINE_WELL_KNOWN_GUID(GUID_IOMMU_BUS_INTERFACE);
PH_DEFINE_WELL_KNOWN_GUID(GUID_PCI_SECURITY_INTERFACE);
PH_DEFINE_WELL_KNOWN_GUID(GUID_SCM_BUS_INTERFACE);
PH_DEFINE_WELL_KNOWN_GUID(GUID_SECURE_DRIVER_INTERFACE);
PH_DEFINE_WELL_KNOWN_GUID(GUID_SDEV_IDENTIFIER_INTERFACE);
PH_DEFINE_WELL_KNOWN_GUID(GUID_SCM_BUS_NVD_INTERFACE);
PH_DEFINE_WELL_KNOWN_GUID(GUID_SCM_BUS_LD_INTERFACE);
PH_DEFINE_WELL_KNOWN_GUID(GUID_SCM_PHYSICAL_NVDIMM_INTERFACE);
PH_DEFINE_WELL_KNOWN_GUID(GUID_PNP_EXTENDED_ADDRESS_INTERFACE);
PH_DEFINE_WELL_KNOWN_GUID(GUID_D3COLD_AUX_POWER_AND_TIMING_INTERFACE);
PH_DEFINE_WELL_KNOWN_GUID(GUID_PCI_FPGA_CONTROL_INTERFACE);
PH_DEFINE_WELL_KNOWN_GUID(GUID_PCI_PTM_CONTROL_INTERFACE);
PH_DEFINE_WELL_KNOWN_GUID(GUID_BUS_RESOURCE_UPDATE_INTERFACE);
PH_DEFINE_WELL_KNOWN_GUID(GUID_NPEM_CONTROL_INTERFACE);
PH_DEFINE_WELL_KNOWN_GUID(GUID_PCI_ATS_INTERFACE);
PH_DEFINE_WELL_KNOWN_GUID(GUID_BUS_TYPE_INTERNAL);
PH_DEFINE_WELL_KNOWN_GUID(GUID_BUS_TYPE_PCMCIA);
PH_DEFINE_WELL_KNOWN_GUID(GUID_BUS_TYPE_PCI);
PH_DEFINE_WELL_KNOWN_GUID(GUID_BUS_TYPE_ISAPNP);
PH_DEFINE_WELL_KNOWN_GUID(GUID_BUS_TYPE_EISA);
PH_DEFINE_WELL_KNOWN_GUID(GUID_BUS_TYPE_MCA);
PH_DEFINE_WELL_KNOWN_GUID(GUID_BUS_TYPE_SERENUM);
PH_DEFINE_WELL_KNOWN_GUID(GUID_BUS_TYPE_USB);
PH_DEFINE_WELL_KNOWN_GUID(GUID_BUS_TYPE_LPTENUM);
PH_DEFINE_WELL_KNOWN_GUID(GUID_BUS_TYPE_USBPRINT);
PH_DEFINE_WELL_KNOWN_GUID(GUID_BUS_TYPE_DOT4PRT);
PH_DEFINE_WELL_KNOWN_GUID(GUID_BUS_TYPE_1394);
PH_DEFINE_WELL_KNOWN_GUID(GUID_BUS_TYPE_HID);
PH_DEFINE_WELL_KNOWN_GUID(GUID_BUS_TYPE_AVC);
PH_DEFINE_WELL_KNOWN_GUID(GUID_BUS_TYPE_IRDA);
PH_DEFINE_WELL_KNOWN_GUID(GUID_BUS_TYPE_SD);
PH_DEFINE_WELL_KNOWN_GUID(GUID_BUS_TYPE_ACPI);
PH_DEFINE_WELL_KNOWN_GUID(GUID_BUS_TYPE_SW_DEVICE);
PH_DEFINE_WELL_KNOWN_GUID(GUID_BUS_TYPE_SCM);
PH_DEFINE_WELL_KNOWN_GUID(GUID_POWER_DEVICE_ENABLE);
PH_DEFINE_WELL_KNOWN_GUID(GUID_POWER_DEVICE_TIMEOUTS);
PH_DEFINE_WELL_KNOWN_GUID(GUID_POWER_DEVICE_WAKE_ENABLE);
PH_DEFINE_WELL_KNOWN_GUID(GUID_WUDF_DEVICE_HOST_PROBLEM);
PH_DEFINE_WELL_KNOWN_GUID(GUID_PARTITION_UNIT_INTERFACE_STANDARD);
PH_DEFINE_WELL_KNOWN_GUID(GUID_QUERY_CRASHDUMP_FUNCTIONS);
PH_DEFINE_WELL_KNOWN_GUID(GUID_DEVINTERFACE_DISK);
PH_DEFINE_WELL_KNOWN_GUID(GUID_DEVINTERFACE_CDROM);
PH_DEFINE_WELL_KNOWN_GUID(GUID_DEVINTERFACE_PARTITION);
PH_DEFINE_WELL_KNOWN_GUID(GUID_DEVINTERFACE_TAPE);
PH_DEFINE_WELL_KNOWN_GUID(GUID_DEVINTERFACE_WRITEONCEDISK);
PH_DEFINE_WELL_KNOWN_GUID(GUID_DEVINTERFACE_VOLUME);
PH_DEFINE_WELL_KNOWN_GUID(GUID_DEVINTERFACE_MEDIUMCHANGER);
PH_DEFINE_WELL_KNOWN_GUID(GUID_DEVINTERFACE_FLOPPY);
PH_DEFINE_WELL_KNOWN_GUID(GUID_DEVINTERFACE_CDCHANGER);
PH_DEFINE_WELL_KNOWN_GUID(GUID_DEVINTERFACE_STORAGEPORT);
PH_DEFINE_WELL_KNOWN_GUID(GUID_DEVINTERFACE_VMLUN);
PH_DEFINE_WELL_KNOWN_GUID(GUID_DEVINTERFACE_SES);
PH_DEFINE_WELL_KNOWN_GUID(GUID_DEVINTERFACE_ZNSDISK);
PH_DEFINE_WELL_KNOWN_GUID(GUID_DEVINTERFACE_SERVICE_VOLUME);
PH_DEFINE_WELL_KNOWN_GUID(GUID_DEVINTERFACE_HIDDEN_VOLUME);
PH_DEFINE_WELL_KNOWN_GUID(GUID_DEVINTERFACE_UNIFIED_ACCESS_RPMB);
PH_DEFINE_WELL_KNOWN_GUID(GUID_DEVINTERFACE_SCM_PHYSICAL_DEVICE);
PH_DEFINE_WELL_KNOWN_GUID(GUID_SCM_PD_HEALTH_NOTIFICATION);
PH_DEFINE_WELL_KNOWN_GUID(GUID_SCM_PD_PASSTHROUGH_INVDIMM);
PH_DEFINE_WELL_KNOWN_GUID(GUID_DEVINTERFACE_COMPORT);
PH_DEFINE_WELL_KNOWN_GUID(GUID_DEVINTERFACE_SERENUM_BUS_ENUMERATOR);
PH_DEFINE_WELL_KNOWN_GUID(GUID_BTHPORT_DEVICE_INTERFACE);
PH_DEFINE_WELL_KNOWN_GUID(GUID_BTH_RFCOMM_SERVICE_DEVICE_INTERFACE);
PH_DEFINE_WELL_KNOWN_GUID(GUID_DEVCLASS_1394);
PH_DEFINE_WELL_KNOWN_GUID(GUID_DEVCLASS_1394DEBUG);
PH_DEFINE_WELL_KNOWN_GUID(GUID_DEVCLASS_61883);
PH_DEFINE_WELL_KNOWN_GUID(GUID_DEVCLASS_ADAPTER);
PH_DEFINE_WELL_KNOWN_GUID(GUID_DEVCLASS_APMSUPPORT);
PH_DEFINE_WELL_KNOWN_GUID(GUID_DEVCLASS_AVC);
PH_DEFINE_WELL_KNOWN_GUID(GUID_DEVCLASS_BATTERY);
PH_DEFINE_WELL_KNOWN_GUID(GUID_DEVCLASS_BIOMETRIC);
PH_DEFINE_WELL_KNOWN_GUID(GUID_DEVCLASS_BLUETOOTH);
PH_DEFINE_WELL_KNOWN_GUID(GUID_DEVCLASS_CAMERA);
PH_DEFINE_WELL_KNOWN_GUID(GUID_DEVCLASS_CDROM);
PH_DEFINE_WELL_KNOWN_GUID(GUID_DEVCLASS_COMPUTEACCELERATOR);
PH_DEFINE_WELL_KNOWN_GUID(GUID_DEVCLASS_COMPUTER);
PH_DEFINE_WELL_KNOWN_GUID(GUID_DEVCLASS_DECODER);
PH_DEFINE_WELL_KNOWN_GUID(GUID_DEVCLASS_DISKDRIVE);
PH_DEFINE_WELL_KNOWN_GUID(GUID_DEVCLASS_DISPLAY);
PH_DEFINE_WELL_KNOWN_GUID(GUID_DEVCLASS_DOT4);
PH_DEFINE_WELL_KNOWN_GUID(GUID_DEVCLASS_DOT4PRINT);
PH_DEFINE_WELL_KNOWN_GUID(GUID_DEVCLASS_EHSTORAGESILO);
PH_DEFINE_WELL_KNOWN_GUID(GUID_DEVCLASS_ENUM1394);
PH_DEFINE_WELL_KNOWN_GUID(GUID_DEVCLASS_EXTENSION);
PH_DEFINE_WELL_KNOWN_GUID(GUID_DEVCLASS_FDC);
PH_DEFINE_WELL_KNOWN_GUID(GUID_DEVCLASS_FIRMWARE);
PH_DEFINE_WELL_KNOWN_GUID(GUID_DEVCLASS_FLOPPYDISK);
PH_DEFINE_WELL_KNOWN_GUID(GUID_DEVCLASS_GENERIC);
PH_DEFINE_WELL_KNOWN_GUID(GUID_DEVCLASS_GPS);
PH_DEFINE_WELL_KNOWN_GUID(GUID_DEVCLASS_HDC);
PH_DEFINE_WELL_KNOWN_GUID(GUID_DEVCLASS_HIDCLASS);
PH_DEFINE_WELL_KNOWN_GUID(GUID_DEVCLASS_HOLOGRAPHIC);
PH_DEFINE_WELL_KNOWN_GUID(GUID_DEVCLASS_IMAGE);
PH_DEFINE_WELL_KNOWN_GUID(GUID_DEVCLASS_INFINIBAND);
PH_DEFINE_WELL_KNOWN_GUID(GUID_DEVCLASS_INFRARED);
PH_DEFINE_WELL_KNOWN_GUID(GUID_DEVCLASS_KEYBOARD);
PH_DEFINE_WELL_KNOWN_GUID(GUID_DEVCLASS_LEGACYDRIVER);
PH_DEFINE_WELL_KNOWN_GUID(GUID_DEVCLASS_MEDIA);
PH_DEFINE_WELL_KNOWN_GUID(GUID_DEVCLASS_MEDIUM_CHANGER);
PH_DEFINE_WELL_KNOWN_GUID(GUID_DEVCLASS_MEMORY);
PH_DEFINE_WELL_KNOWN_GUID(GUID_DEVCLASS_MODEM);
PH_DEFINE_WELL_KNOWN_GUID(GUID_DEVCLASS_MONITOR);
PH_DEFINE_WELL_KNOWN_GUID(GUID_DEVCLASS_MOUSE);
PH_DEFINE_WELL_KNOWN_GUID(GUID_DEVCLASS_MTD);
PH_DEFINE_WELL_KNOWN_GUID(GUID_DEVCLASS_MULTIFUNCTION);
PH_DEFINE_WELL_KNOWN_GUID(GUID_DEVCLASS_MULTIPORTSERIAL);
PH_DEFINE_WELL_KNOWN_GUID(GUID_DEVCLASS_NET);
PH_DEFINE_WELL_KNOWN_GUID(GUID_DEVCLASS_NETCLIENT);
PH_DEFINE_WELL_KNOWN_GUID(GUID_DEVCLASS_NETDRIVER);
PH_DEFINE_WELL_KNOWN_GUID(GUID_DEVCLASS_NETSERVICE);
PH_DEFINE_WELL_KNOWN_GUID(GUID_DEVCLASS_NETTRANS);
PH_DEFINE_WELL_KNOWN_GUID(GUID_DEVCLASS_NETUIO);
PH_DEFINE_WELL_KNOWN_GUID(GUID_DEVCLASS_NODRIVER);
PH_DEFINE_WELL_KNOWN_GUID(GUID_DEVCLASS_PCMCIA);
PH_DEFINE_WELL_KNOWN_GUID(GUID_DEVCLASS_PNPPRINTERS);
PH_DEFINE_WELL_KNOWN_GUID(GUID_DEVCLASS_PORTS);
PH_DEFINE_WELL_KNOWN_GUID(GUID_DEVCLASS_PRIMITIVE);
PH_DEFINE_WELL_KNOWN_GUID(GUID_DEVCLASS_PRINTER);
PH_DEFINE_WELL_KNOWN_GUID(GUID_DEVCLASS_PRINTERUPGRADE);
PH_DEFINE_WELL_KNOWN_GUID(GUID_DEVCLASS_PRINTQUEUE);
PH_DEFINE_WELL_KNOWN_GUID(GUID_DEVCLASS_PROCESSOR);
PH_DEFINE_WELL_KNOWN_GUID(GUID_DEVCLASS_SBP2);
PH_DEFINE_WELL_KNOWN_GUID(GUID_DEVCLASS_SCMDISK);
PH_DEFINE_WELL_KNOWN_GUID(GUID_DEVCLASS_SCMVOLUME);
PH_DEFINE_WELL_KNOWN_GUID(GUID_DEVCLASS_SCSIADAPTER);
PH_DEFINE_WELL_KNOWN_GUID(GUID_DEVCLASS_SECURITYACCELERATOR);
PH_DEFINE_WELL_KNOWN_GUID(GUID_DEVCLASS_SENSOR);
PH_DEFINE_WELL_KNOWN_GUID(GUID_DEVCLASS_SIDESHOW);
PH_DEFINE_WELL_KNOWN_GUID(GUID_DEVCLASS_SMARTCARDREADER);
PH_DEFINE_WELL_KNOWN_GUID(GUID_DEVCLASS_SMRDISK);
PH_DEFINE_WELL_KNOWN_GUID(GUID_DEVCLASS_SMRVOLUME);
PH_DEFINE_WELL_KNOWN_GUID(GUID_DEVCLASS_SOFTWARECOMPONENT);
PH_DEFINE_WELL_KNOWN_GUID(GUID_DEVCLASS_SOUND);
PH_DEFINE_WELL_KNOWN_GUID(GUID_DEVCLASS_SYSTEM);
PH_DEFINE_WELL_KNOWN_GUID(GUID_DEVCLASS_TAPEDRIVE);
PH_DEFINE_WELL_KNOWN_GUID(GUID_DEVCLASS_UNKNOWN);
PH_DEFINE_WELL_KNOWN_GUID(GUID_DEVCLASS_UCM);
PH_DEFINE_WELL_KNOWN_GUID(GUID_DEVCLASS_USB);
PH_DEFINE_WELL_KNOWN_GUID(GUID_DEVCLASS_VOLUME);
PH_DEFINE_WELL_KNOWN_GUID(GUID_DEVCLASS_VOLUMESNAPSHOT);
PH_DEFINE_WELL_KNOWN_GUID(GUID_DEVCLASS_WCEUSBS);
PH_DEFINE_WELL_KNOWN_GUID(GUID_DEVCLASS_WPD);
PH_DEFINE_WELL_KNOWN_GUID(GUID_DEVCLASS_FSFILTER_TOP);
PH_DEFINE_WELL_KNOWN_GUID(GUID_DEVCLASS_FSFILTER_ACTIVITYMONITOR);
PH_DEFINE_WELL_KNOWN_GUID(GUID_DEVCLASS_FSFILTER_UNDELETE);
PH_DEFINE_WELL_KNOWN_GUID(GUID_DEVCLASS_FSFILTER_ANTIVIRUS);
PH_DEFINE_WELL_KNOWN_GUID(GUID_DEVCLASS_FSFILTER_REPLICATION);
PH_DEFINE_WELL_KNOWN_GUID(GUID_DEVCLASS_FSFILTER_CONTINUOUSBACKUP);
PH_DEFINE_WELL_KNOWN_GUID(GUID_DEVCLASS_FSFILTER_CONTENTSCREENER);
PH_DEFINE_WELL_KNOWN_GUID(GUID_DEVCLASS_FSFILTER_QUOTAMANAGEMENT);
PH_DEFINE_WELL_KNOWN_GUID(GUID_DEVCLASS_FSFILTER_SYSTEMRECOVERY);
PH_DEFINE_WELL_KNOWN_GUID(GUID_DEVCLASS_FSFILTER_CFSMETADATASERVER);
PH_DEFINE_WELL_KNOWN_GUID(GUID_DEVCLASS_FSFILTER_HSM);
PH_DEFINE_WELL_KNOWN_GUID(GUID_DEVCLASS_FSFILTER_COMPRESSION);
PH_DEFINE_WELL_KNOWN_GUID(GUID_DEVCLASS_FSFILTER_ENCRYPTION);
PH_DEFINE_WELL_KNOWN_GUID(GUID_DEVCLASS_FSFILTER_VIRTUALIZATION);
PH_DEFINE_WELL_KNOWN_GUID(GUID_DEVCLASS_FSFILTER_PHYSICALQUOTAMANAGEMENT);
PH_DEFINE_WELL_KNOWN_GUID(GUID_DEVCLASS_FSFILTER_OPENFILEBACKUP);
PH_DEFINE_WELL_KNOWN_GUID(GUID_DEVCLASS_FSFILTER_SECURITYENHANCER);
PH_DEFINE_WELL_KNOWN_GUID(GUID_DEVCLASS_FSFILTER_COPYPROTECTION);
PH_DEFINE_WELL_KNOWN_GUID(GUID_DEVCLASS_FSFILTER_BOTTOM);
PH_DEFINE_WELL_KNOWN_GUID(GUID_DEVCLASS_FSFILTER_SYSTEM);
PH_DEFINE_WELL_KNOWN_GUID(GUID_DEVCLASS_FSFILTER_INFRASTRUCTURE);
PH_DEFINE_WELL_KNOWN_GUID(GUID_DEVINTERFACE_USB_HUB);
PH_DEFINE_WELL_KNOWN_GUID(GUID_DEVINTERFACE_USB_BILLBOARD);
PH_DEFINE_WELL_KNOWN_GUID(GUID_DEVINTERFACE_USB_DEVICE);
PH_DEFINE_WELL_KNOWN_GUID(GUID_DEVINTERFACE_USB_HOST_CONTROLLER);
PH_DEFINE_WELL_KNOWN_GUID(GUID_DEVINTERFACE_A2DP_SIDEBAND_AUDIO);
PH_DEFINE_WELL_KNOWN_GUID(GUID_DEVINTERFACE_BLUETOOTH_HFP_SCO_HCIBYPASS);
PH_DEFINE_WELL_KNOWN_GUID(GUID_DEVINTERFACE_CHARGING_ARBITRATION);
PH_DEFINE_WELL_KNOWN_GUID(GUID_DEVINTERFACE_CONFIGURABLE_USBFN_CHARGER);
PH_DEFINE_WELL_KNOWN_GUID(GUID_DEVINTERFACE_CONFIGURABLE_WIRELESS_CHARGER);
PH_DEFINE_WELL_KNOWN_GUID(GUID_DEVINTERFACE_I2C);
PH_DEFINE_WELL_KNOWN_GUID(GUID_DEVINTERFACE_OPM);
PH_DEFINE_WELL_KNOWN_GUID(GUID_DEVINTERFACE_OPM_2_JTP);
PH_DEFINE_WELL_KNOWN_GUID(GUID_DEVINTERFACE_OPM_2);
PH_DEFINE_WELL_KNOWN_GUID(GUID_DEVINTERFACE_OPM_3);
PH_DEFINE_WELL_KNOWN_GUID(GUID_DEVINTERFACE_BRIGHTNESS);
PH_DEFINE_WELL_KNOWN_GUID(GUID_DEVINTERFACE_BRIGHTNESS_2);
PH_DEFINE_WELL_KNOWN_GUID(GUID_DEVINTERFACE_MIRACAST_DISPLAY);
PH_DEFINE_WELL_KNOWN_GUID(GUID_DEVINTERFACE_BRIGHTNESS_3);
PH_DEFINE_WELL_KNOWN_GUID(GUID_DEVINTERFACE_HPMI);
PH_DEFINE_WELL_KNOWN_GUID(GUID_DEVINTERFACE_VIRTUALIZABLE_DEVICE);
PH_DEFINE_WELL_KNOWN_GUID(GUID_DEVINTERFACE_EMMC_PARTITION_ACCESS_RPMB);
PH_DEFINE_WELL_KNOWN_GUID(GUID_DEVINTERFACE_EMMC_PARTITION_ACCESS_GPP);
PH_DEFINE_WELL_KNOWN_GUID(GUID_DEVINTERFACE_USB_SIDEBAND_AUDIO_HS_HCIBYPASS);
PH_DEFINE_WELL_KNOWN_GUID(GUID_BUS_VPCI);
PH_DEFINE_WELL_KNOWN_GUID(GUID_VPCI_INTERFACE_STANDARD);
PH_DEFINE_WELL_KNOWN_GUID(GUID_DEVINTERFACE_VPCI);
PH_DEFINE_WELL_KNOWN_GUID(GUID_DEVINTERFACE_WWAN_CONTROLLER);
PH_DEFINE_WELL_KNOWN_GUID(GUID_DEVINTERFACE_WDDM3_ON_VB);
PH_DEFINE_WELL_KNOWN_GUID(GUID_DEVINTERFACE_GRAPHICSPOWER);
PH_DEFINE_WELL_KNOWN_GUID(GUID_DEVINTERFACE_GNSS);
PH_DEFINE_WELL_KNOWN_GUID(GUID_DEVINTERFACE_HID);
PH_DEFINE_WELL_KNOWN_GUID(GUID_DEVINTERFACE_LAMP);
PH_DEFINE_WELL_KNOWN_GUID(GUID_DEVINTERFACE_NET);
PH_DEFINE_WELL_KNOWN_GUID(GUID_DEVINTERFACE_NETUIO);
PH_DEFINE_WELL_KNOWN_GUID(GUID_DEVINTERFACE_NFCDTA);
PH_DEFINE_WELL_KNOWN_GUID(GUID_DEVINTERFACE_NFCSE);
PH_DEFINE_WELL_KNOWN_GUID(GUID_DEVINTERFACE_NFP);
PH_DEFINE_WELL_KNOWN_GUID(GUID_DEVINTERFACE_KEYBOARD);
PH_DEFINE_WELL_KNOWN_GUID(GUID_DEVINTERFACE_MODEM);
PH_DEFINE_WELL_KNOWN_GUID(GUID_DEVINTERFACE_MOUSE);
PH_DEFINE_WELL_KNOWN_GUID(GUID_DEVINTERFACE_PARALLEL);
PH_DEFINE_WELL_KNOWN_GUID(GUID_DEVINTERFACE_PARCLASS);
PH_DEFINE_WELL_KNOWN_GUID(GUID_PARALLEL_DEVICE);
PH_DEFINE_WELL_KNOWN_GUID(GUID_PARCLASS_DEVICE);
PH_DEFINE_WELL_KNOWN_GUID(GUID_DEVINTERFACE_DISPLAY_ADAPTER);
PH_DEFINE_WELL_KNOWN_GUID(GUID_DEVINTERFACE_MONITOR);
PH_DEFINE_WELL_KNOWN_GUID(GUID_DEVICE_BATTERY);
PH_DEFINE_WELL_KNOWN_GUID(GUID_DEVICE_APPLICATIONLAUNCH_BUTTON);
PH_DEFINE_WELL_KNOWN_GUID(GUID_DEVICE_SYS_BUTTON);
PH_DEFINE_WELL_KNOWN_GUID(GUID_DEVICE_LID);
PH_DEFINE_WELL_KNOWN_GUID(GUID_DEVICE_THERMAL_ZONE);
PH_DEFINE_WELL_KNOWN_GUID(GUID_DEVICE_FAN);
PH_DEFINE_WELL_KNOWN_GUID(GUID_DEVICE_PROCESSOR);
PH_DEFINE_WELL_KNOWN_GUID(GUID_DEVICE_MEMORY);
PH_DEFINE_WELL_KNOWN_GUID(GUID_DEVICE_ACPI_TIME);
PH_DEFINE_WELL_KNOWN_GUID(GUID_DEVICE_MESSAGE_INDICATOR);
PH_DEFINE_WELL_KNOWN_GUID(GUID_CLASS_INPUT);
PH_DEFINE_WELL_KNOWN_GUID(GUID_DEVINTERFACE_THERMAL_COOLING);
PH_DEFINE_WELL_KNOWN_GUID(GUID_DEVINTERFACE_THERMAL_MANAGER);
PH_DEFINE_WELL_KNOWN_GUID(GUID_DEVINTERFACE_PWM_CONTROLLER);
PH_DEFINE_WELL_KNOWN_GUID(GUID_DEVINTERFACE_USBPRINT);
PH_DEFINE_WELL_KNOWN_GUID(GUID_DEVINTERFACE_IPPUSB_PRINT);
PH_DEFINE_WELL_KNOWN_GUID(GUID_DEVINTERFACE_VM_GENCOUNTER);
PH_DEFINE_WELL_KNOWN_GUID(GUID_DEVINTERFACE_BIOMETRIC_READER);
PH_DEFINE_WELL_KNOWN_GUID(GUID_DEVINTERFACE_SMARTCARD_READER);
PH_DEFINE_WELL_KNOWN_GUID(GUID_DEVINTERFACE_DMR);
PH_DEFINE_WELL_KNOWN_GUID(GUID_DEVINTERFACE_DMP);
PH_DEFINE_WELL_KNOWN_GUID(GUID_DEVINTERFACE_DMS);
PH_DEFINE_WELL_KNOWN_GUID(GUID_DEVINTERFACE_ENHANCED_STORAGE_SILO);
PH_DEFINE_WELL_KNOWN_GUID(GUID_DEVINTERFACE_WPD);
PH_DEFINE_WELL_KNOWN_GUID(GUID_DEVINTERFACE_WPD_PRIVATE);
PH_DEFINE_WELL_KNOWN_GUID(GUID_DEVINTERFACE_WPD_SERVICE);
PH_DEFINE_WELL_KNOWN_GUID(GUID_DEVINTERFACE_SENSOR);
PH_DEFINE_WELL_KNOWN_GUID(GUID_DEVINTERFACE_IMAGE);
PH_DEFINE_WELL_KNOWN_GUID(GUID_DEVINTERFACE_SIDESHOW);
PH_DEFINE_WELL_KNOWN_GUID(GUID_DEVINTERFACE_WIFIDIRECT_DEVICE);
PH_DEFINE_WELL_KNOWN_GUID(GUID_AEPSERVICE_WIFIDIRECT_DEVICE);
PH_DEFINE_WELL_KNOWN_GUID(GUID_DEVINTERFACE_ASP_INFRA_DEVICE);
PH_DEFINE_WELL_KNOWN_GUID(GUID_DXGKDDI_MITIGABLE_DEVICE_INTERFACE);
PH_DEFINE_WELL_KNOWN_GUID(GUID_DXGKDDI_FLEXIOV_DEVICE_INTERFACE);
PH_DEFINE_WELL_KNOWN_GUID(GUID_SRIOV_DEVICE_INTERFACE_STANDARD);
PH_DEFINE_WELL_KNOWN_GUID(GUID_MITIGABLE_DEVICE_INTERFACE);
PH_DEFINE_WELL_KNOWN_GUID(GUID_IO_VOLUME_DEVICE_INTERFACE);
PH_DEFINE_WELL_KNOWN_GUID(GUID_NDIS_LAN_CLASS);
PH_DEFINE_WELL_KNOWN_GUID(GUID_NFC_RADIO_MEDIA_DEVICE_INTERFACE);
PH_DEFINE_WELL_KNOWN_GUID(GUID_NFCSE_RADIO_MEDIA_DEVICE_INTERFACE);
PH_DEFINE_WELL_KNOWN_GUID(GUID_BLUETOOTHLE_DEVICE_INTERFACE);
PH_DEFINE_WELL_KNOWN_GUID(GUID_BLUETOOTH_GATT_SERVICE_DEVICE_INTERFACE);
PH_DEFINE_WELL_KNOWN_GUID(GUID_BUS_TYPE_TREE);
PH_DEFINE_WELL_KNOWN_GUID(GUID_TREE_TPM_SERVICE_FDTPM);
PH_DEFINE_WELL_KNOWN_GUID(GUID_TREE_TPM_SERVICE_STPM);
PH_DEFINE_WELL_KNOWN_GUID(GUID_DISPLAY_DEVICE_ARRIVAL);
PH_DEFINE_WELL_KNOWN_GUID(GUID_COMPUTE_DEVICE_ARRIVAL);
static PH_INITONCE PhpWellKnownGuidsInitOnce = PH_INITONCE_INIT;
static const PH_WELL_KNOWN_GUID* PhpWellKnownGuids[] =
{
&PH_WELL_KNOWN_GUID_HWPROFILE_QUERY_CHANGE,
&PH_WELL_KNOWN_GUID_HWPROFILE_CHANGE_CANCELLED,
&PH_WELL_KNOWN_GUID_HWPROFILE_CHANGE_COMPLETE,
&PH_WELL_KNOWN_GUID_DEVICE_INTERFACE_ARRIVAL,
&PH_WELL_KNOWN_GUID_DEVICE_INTERFACE_REMOVAL,
&PH_WELL_KNOWN_GUID_TARGET_DEVICE_QUERY_REMOVE,
&PH_WELL_KNOWN_GUID_TARGET_DEVICE_REMOVE_CANCELLED,
&PH_WELL_KNOWN_GUID_TARGET_DEVICE_REMOVE_COMPLETE,
&PH_WELL_KNOWN_GUID_PNP_CUSTOM_NOTIFICATION,
&PH_WELL_KNOWN_GUID_PNP_POWER_NOTIFICATION,
&PH_WELL_KNOWN_GUID_PNP_POWER_SETTING_CHANGE,
&PH_WELL_KNOWN_GUID_TARGET_DEVICE_TRANSPORT_RELATIONS_CHANGED,
&PH_WELL_KNOWN_GUID_KERNEL_SOFT_RESTART_PREPARE,
&PH_WELL_KNOWN_GUID_KERNEL_SOFT_RESTART_CANCEL,
&PH_WELL_KNOWN_GUID_RECOVERY_PCI_PREPARE_SHUTDOWN,
&PH_WELL_KNOWN_GUID_RECOVERY_NVMED_PREPARE_SHUTDOWN,
&PH_WELL_KNOWN_GUID_KERNEL_SOFT_RESTART_FINALIZE,
&PH_WELL_KNOWN_GUID_BUS_INTERFACE_STANDARD,
&PH_WELL_KNOWN_GUID_PCI_BUS_INTERFACE_STANDARD,
&PH_WELL_KNOWN_GUID_PCI_BUS_INTERFACE_STANDARD2,
&PH_WELL_KNOWN_GUID_ARBITER_INTERFACE_STANDARD,
&PH_WELL_KNOWN_GUID_TRANSLATOR_INTERFACE_STANDARD,
&PH_WELL_KNOWN_GUID_ACPI_INTERFACE_STANDARD,
&PH_WELL_KNOWN_GUID_INT_ROUTE_INTERFACE_STANDARD,
&PH_WELL_KNOWN_GUID_PCMCIA_BUS_INTERFACE_STANDARD,
&PH_WELL_KNOWN_GUID_ACPI_REGS_INTERFACE_STANDARD,
&PH_WELL_KNOWN_GUID_LEGACY_DEVICE_DETECTION_STANDARD,
&PH_WELL_KNOWN_GUID_PCI_DEVICE_PRESENT_INTERFACE,
&PH_WELL_KNOWN_GUID_MF_ENUMERATION_INTERFACE,
&PH_WELL_KNOWN_GUID_REENUMERATE_SELF_INTERFACE_STANDARD,
&PH_WELL_KNOWN_GUID_AGP_TARGET_BUS_INTERFACE_STANDARD,
&PH_WELL_KNOWN_GUID_ACPI_CMOS_INTERFACE_STANDARD,
&PH_WELL_KNOWN_GUID_ACPI_PORT_RANGES_INTERFACE_STANDARD,
&PH_WELL_KNOWN_GUID_ACPI_INTERFACE_STANDARD2,
&PH_WELL_KNOWN_GUID_PNP_LOCATION_INTERFACE,
&PH_WELL_KNOWN_GUID_PCI_EXPRESS_LINK_QUIESCENT_INTERFACE,
&PH_WELL_KNOWN_GUID_PCI_EXPRESS_ROOT_PORT_INTERFACE,
&PH_WELL_KNOWN_GUID_MSIX_TABLE_CONFIG_INTERFACE,
&PH_WELL_KNOWN_GUID_D3COLD_SUPPORT_INTERFACE,
&PH_WELL_KNOWN_GUID_PROCESSOR_PCC_INTERFACE_STANDARD,
&PH_WELL_KNOWN_GUID_PCI_VIRTUALIZATION_INTERFACE,
&PH_WELL_KNOWN_GUID_PCC_INTERFACE_STANDARD,
&PH_WELL_KNOWN_GUID_PCC_INTERFACE_INTERNAL,
&PH_WELL_KNOWN_GUID_THERMAL_COOLING_INTERFACE,
&PH_WELL_KNOWN_GUID_DMA_CACHE_COHERENCY_INTERFACE,
&PH_WELL_KNOWN_GUID_DEVICE_RESET_INTERFACE_STANDARD,
&PH_WELL_KNOWN_GUID_IOMMU_BUS_INTERFACE,
&PH_WELL_KNOWN_GUID_PCI_SECURITY_INTERFACE,
&PH_WELL_KNOWN_GUID_SCM_BUS_INTERFACE,
&PH_WELL_KNOWN_GUID_SECURE_DRIVER_INTERFACE,
&PH_WELL_KNOWN_GUID_SDEV_IDENTIFIER_INTERFACE,
&PH_WELL_KNOWN_GUID_SCM_BUS_NVD_INTERFACE,
&PH_WELL_KNOWN_GUID_SCM_BUS_LD_INTERFACE,
&PH_WELL_KNOWN_GUID_SCM_PHYSICAL_NVDIMM_INTERFACE,
&PH_WELL_KNOWN_GUID_PNP_EXTENDED_ADDRESS_INTERFACE,
&PH_WELL_KNOWN_GUID_D3COLD_AUX_POWER_AND_TIMING_INTERFACE,
&PH_WELL_KNOWN_GUID_PCI_FPGA_CONTROL_INTERFACE,
&PH_WELL_KNOWN_GUID_PCI_PTM_CONTROL_INTERFACE,
&PH_WELL_KNOWN_GUID_BUS_RESOURCE_UPDATE_INTERFACE,
&PH_WELL_KNOWN_GUID_NPEM_CONTROL_INTERFACE,
&PH_WELL_KNOWN_GUID_PCI_ATS_INTERFACE,
&PH_WELL_KNOWN_GUID_BUS_TYPE_INTERNAL,
&PH_WELL_KNOWN_GUID_BUS_TYPE_PCMCIA,
&PH_WELL_KNOWN_GUID_BUS_TYPE_PCI,
&PH_WELL_KNOWN_GUID_BUS_TYPE_ISAPNP,
&PH_WELL_KNOWN_GUID_BUS_TYPE_EISA,
&PH_WELL_KNOWN_GUID_BUS_TYPE_MCA,
&PH_WELL_KNOWN_GUID_BUS_TYPE_SERENUM,
&PH_WELL_KNOWN_GUID_BUS_TYPE_USB,
&PH_WELL_KNOWN_GUID_BUS_TYPE_LPTENUM,
&PH_WELL_KNOWN_GUID_BUS_TYPE_USBPRINT,
&PH_WELL_KNOWN_GUID_BUS_TYPE_DOT4PRT,
&PH_WELL_KNOWN_GUID_BUS_TYPE_1394,
&PH_WELL_KNOWN_GUID_BUS_TYPE_HID,
&PH_WELL_KNOWN_GUID_BUS_TYPE_AVC,
&PH_WELL_KNOWN_GUID_BUS_TYPE_IRDA,
&PH_WELL_KNOWN_GUID_BUS_TYPE_SD,
&PH_WELL_KNOWN_GUID_BUS_TYPE_ACPI,
&PH_WELL_KNOWN_GUID_BUS_TYPE_SW_DEVICE,
&PH_WELL_KNOWN_GUID_BUS_TYPE_SCM,
&PH_WELL_KNOWN_GUID_POWER_DEVICE_ENABLE,
&PH_WELL_KNOWN_GUID_POWER_DEVICE_TIMEOUTS,
&PH_WELL_KNOWN_GUID_POWER_DEVICE_WAKE_ENABLE,
&PH_WELL_KNOWN_GUID_WUDF_DEVICE_HOST_PROBLEM,
&PH_WELL_KNOWN_GUID_PARTITION_UNIT_INTERFACE_STANDARD,
&PH_WELL_KNOWN_GUID_QUERY_CRASHDUMP_FUNCTIONS,
&PH_WELL_KNOWN_GUID_DEVINTERFACE_DISK,
&PH_WELL_KNOWN_GUID_DEVINTERFACE_CDROM,
&PH_WELL_KNOWN_GUID_DEVINTERFACE_PARTITION,
&PH_WELL_KNOWN_GUID_DEVINTERFACE_TAPE,
&PH_WELL_KNOWN_GUID_DEVINTERFACE_WRITEONCEDISK,
&PH_WELL_KNOWN_GUID_DEVINTERFACE_VOLUME,
&PH_WELL_KNOWN_GUID_DEVINTERFACE_MEDIUMCHANGER,
&PH_WELL_KNOWN_GUID_DEVINTERFACE_FLOPPY,
&PH_WELL_KNOWN_GUID_DEVINTERFACE_CDCHANGER,
&PH_WELL_KNOWN_GUID_DEVINTERFACE_STORAGEPORT,
&PH_WELL_KNOWN_GUID_DEVINTERFACE_VMLUN,
&PH_WELL_KNOWN_GUID_DEVINTERFACE_SES,
&PH_WELL_KNOWN_GUID_DEVINTERFACE_ZNSDISK,
&PH_WELL_KNOWN_GUID_DEVINTERFACE_SERVICE_VOLUME,
&PH_WELL_KNOWN_GUID_DEVINTERFACE_HIDDEN_VOLUME,
&PH_WELL_KNOWN_GUID_DEVINTERFACE_UNIFIED_ACCESS_RPMB,
&PH_WELL_KNOWN_GUID_DEVINTERFACE_SCM_PHYSICAL_DEVICE,
&PH_WELL_KNOWN_GUID_SCM_PD_HEALTH_NOTIFICATION,
&PH_WELL_KNOWN_GUID_SCM_PD_PASSTHROUGH_INVDIMM,
&PH_WELL_KNOWN_GUID_DEVINTERFACE_COMPORT,
&PH_WELL_KNOWN_GUID_DEVINTERFACE_SERENUM_BUS_ENUMERATOR,
&PH_WELL_KNOWN_GUID_BTHPORT_DEVICE_INTERFACE,
&PH_WELL_KNOWN_GUID_BTH_RFCOMM_SERVICE_DEVICE_INTERFACE,
&PH_WELL_KNOWN_GUID_DEVCLASS_1394,
&PH_WELL_KNOWN_GUID_DEVCLASS_1394DEBUG,
&PH_WELL_KNOWN_GUID_DEVCLASS_61883,
&PH_WELL_KNOWN_GUID_DEVCLASS_ADAPTER,
&PH_WELL_KNOWN_GUID_DEVCLASS_APMSUPPORT,
&PH_WELL_KNOWN_GUID_DEVCLASS_AVC,
&PH_WELL_KNOWN_GUID_DEVCLASS_BATTERY,
&PH_WELL_KNOWN_GUID_DEVCLASS_BIOMETRIC,
&PH_WELL_KNOWN_GUID_DEVCLASS_BLUETOOTH,
&PH_WELL_KNOWN_GUID_DEVCLASS_CAMERA,
&PH_WELL_KNOWN_GUID_DEVCLASS_CDROM,
&PH_WELL_KNOWN_GUID_DEVCLASS_COMPUTEACCELERATOR,
&PH_WELL_KNOWN_GUID_DEVCLASS_COMPUTER,
&PH_WELL_KNOWN_GUID_DEVCLASS_DECODER,
&PH_WELL_KNOWN_GUID_DEVCLASS_DISKDRIVE,
&PH_WELL_KNOWN_GUID_DEVCLASS_DISPLAY,
&PH_WELL_KNOWN_GUID_DEVCLASS_DOT4,
&PH_WELL_KNOWN_GUID_DEVCLASS_DOT4PRINT,
&PH_WELL_KNOWN_GUID_DEVCLASS_EHSTORAGESILO,
&PH_WELL_KNOWN_GUID_DEVCLASS_ENUM1394,
&PH_WELL_KNOWN_GUID_DEVCLASS_EXTENSION,
&PH_WELL_KNOWN_GUID_DEVCLASS_FDC,
&PH_WELL_KNOWN_GUID_DEVCLASS_FIRMWARE,
&PH_WELL_KNOWN_GUID_DEVCLASS_FLOPPYDISK,
&PH_WELL_KNOWN_GUID_DEVCLASS_GENERIC,
&PH_WELL_KNOWN_GUID_DEVCLASS_GPS,
&PH_WELL_KNOWN_GUID_DEVCLASS_HDC,
&PH_WELL_KNOWN_GUID_DEVCLASS_HIDCLASS,
&PH_WELL_KNOWN_GUID_DEVCLASS_HOLOGRAPHIC,
&PH_WELL_KNOWN_GUID_DEVCLASS_IMAGE,
&PH_WELL_KNOWN_GUID_DEVCLASS_INFINIBAND,
&PH_WELL_KNOWN_GUID_DEVCLASS_INFRARED,
&PH_WELL_KNOWN_GUID_DEVCLASS_KEYBOARD,
&PH_WELL_KNOWN_GUID_DEVCLASS_LEGACYDRIVER,
&PH_WELL_KNOWN_GUID_DEVCLASS_MEDIA,
&PH_WELL_KNOWN_GUID_DEVCLASS_MEDIUM_CHANGER,
&PH_WELL_KNOWN_GUID_DEVCLASS_MEMORY,
&PH_WELL_KNOWN_GUID_DEVCLASS_MODEM,
&PH_WELL_KNOWN_GUID_DEVCLASS_MONITOR,
&PH_WELL_KNOWN_GUID_DEVCLASS_MOUSE,
&PH_WELL_KNOWN_GUID_DEVCLASS_MTD,
&PH_WELL_KNOWN_GUID_DEVCLASS_MULTIFUNCTION,
&PH_WELL_KNOWN_GUID_DEVCLASS_MULTIPORTSERIAL,
&PH_WELL_KNOWN_GUID_DEVCLASS_NET,
&PH_WELL_KNOWN_GUID_DEVCLASS_NETCLIENT,
&PH_WELL_KNOWN_GUID_DEVCLASS_NETDRIVER,
&PH_WELL_KNOWN_GUID_DEVCLASS_NETSERVICE,
&PH_WELL_KNOWN_GUID_DEVCLASS_NETTRANS,
&PH_WELL_KNOWN_GUID_DEVCLASS_NETUIO,
&PH_WELL_KNOWN_GUID_DEVCLASS_NODRIVER,
&PH_WELL_KNOWN_GUID_DEVCLASS_PCMCIA,
&PH_WELL_KNOWN_GUID_DEVCLASS_PNPPRINTERS,
&PH_WELL_KNOWN_GUID_DEVCLASS_PORTS,
&PH_WELL_KNOWN_GUID_DEVCLASS_PRIMITIVE,
&PH_WELL_KNOWN_GUID_DEVCLASS_PRINTER,
&PH_WELL_KNOWN_GUID_DEVCLASS_PRINTERUPGRADE,
&PH_WELL_KNOWN_GUID_DEVCLASS_PRINTQUEUE,
&PH_WELL_KNOWN_GUID_DEVCLASS_PROCESSOR,
&PH_WELL_KNOWN_GUID_DEVCLASS_SBP2,
&PH_WELL_KNOWN_GUID_DEVCLASS_SCMDISK,
&PH_WELL_KNOWN_GUID_DEVCLASS_SCMVOLUME,
&PH_WELL_KNOWN_GUID_DEVCLASS_SCSIADAPTER,
&PH_WELL_KNOWN_GUID_DEVCLASS_SECURITYACCELERATOR,
&PH_WELL_KNOWN_GUID_DEVCLASS_SENSOR,
&PH_WELL_KNOWN_GUID_DEVCLASS_SIDESHOW,
&PH_WELL_KNOWN_GUID_DEVCLASS_SMARTCARDREADER,
&PH_WELL_KNOWN_GUID_DEVCLASS_SMRDISK,
&PH_WELL_KNOWN_GUID_DEVCLASS_SMRVOLUME,
&PH_WELL_KNOWN_GUID_DEVCLASS_SOFTWARECOMPONENT,
&PH_WELL_KNOWN_GUID_DEVCLASS_SOUND,
&PH_WELL_KNOWN_GUID_DEVCLASS_SYSTEM,
&PH_WELL_KNOWN_GUID_DEVCLASS_TAPEDRIVE,
&PH_WELL_KNOWN_GUID_DEVCLASS_UNKNOWN,
&PH_WELL_KNOWN_GUID_DEVCLASS_UCM,
&PH_WELL_KNOWN_GUID_DEVCLASS_USB,
&PH_WELL_KNOWN_GUID_DEVCLASS_VOLUME,
&PH_WELL_KNOWN_GUID_DEVCLASS_VOLUMESNAPSHOT,
&PH_WELL_KNOWN_GUID_DEVCLASS_WCEUSBS,
&PH_WELL_KNOWN_GUID_DEVCLASS_WPD,
&PH_WELL_KNOWN_GUID_DEVCLASS_FSFILTER_TOP,
&PH_WELL_KNOWN_GUID_DEVCLASS_FSFILTER_ACTIVITYMONITOR,
&PH_WELL_KNOWN_GUID_DEVCLASS_FSFILTER_UNDELETE,
&PH_WELL_KNOWN_GUID_DEVCLASS_FSFILTER_ANTIVIRUS,
&PH_WELL_KNOWN_GUID_DEVCLASS_FSFILTER_REPLICATION,
&PH_WELL_KNOWN_GUID_DEVCLASS_FSFILTER_CONTINUOUSBACKUP,
&PH_WELL_KNOWN_GUID_DEVCLASS_FSFILTER_CONTENTSCREENER,
&PH_WELL_KNOWN_GUID_DEVCLASS_FSFILTER_QUOTAMANAGEMENT,
&PH_WELL_KNOWN_GUID_DEVCLASS_FSFILTER_SYSTEMRECOVERY,
&PH_WELL_KNOWN_GUID_DEVCLASS_FSFILTER_CFSMETADATASERVER,
&PH_WELL_KNOWN_GUID_DEVCLASS_FSFILTER_HSM,
&PH_WELL_KNOWN_GUID_DEVCLASS_FSFILTER_COMPRESSION,
&PH_WELL_KNOWN_GUID_DEVCLASS_FSFILTER_ENCRYPTION,
&PH_WELL_KNOWN_GUID_DEVCLASS_FSFILTER_VIRTUALIZATION,
&PH_WELL_KNOWN_GUID_DEVCLASS_FSFILTER_PHYSICALQUOTAMANAGEMENT,
&PH_WELL_KNOWN_GUID_DEVCLASS_FSFILTER_OPENFILEBACKUP,
&PH_WELL_KNOWN_GUID_DEVCLASS_FSFILTER_SECURITYENHANCER,
&PH_WELL_KNOWN_GUID_DEVCLASS_FSFILTER_COPYPROTECTION,
&PH_WELL_KNOWN_GUID_DEVCLASS_FSFILTER_BOTTOM,
&PH_WELL_KNOWN_GUID_DEVCLASS_FSFILTER_SYSTEM,
&PH_WELL_KNOWN_GUID_DEVCLASS_FSFILTER_INFRASTRUCTURE,
&PH_WELL_KNOWN_GUID_DEVINTERFACE_USB_HUB,
&PH_WELL_KNOWN_GUID_DEVINTERFACE_USB_BILLBOARD,
&PH_WELL_KNOWN_GUID_DEVINTERFACE_USB_DEVICE,
&PH_WELL_KNOWN_GUID_DEVINTERFACE_USB_HOST_CONTROLLER,
&PH_WELL_KNOWN_GUID_DEVINTERFACE_A2DP_SIDEBAND_AUDIO,
&PH_WELL_KNOWN_GUID_DEVINTERFACE_BLUETOOTH_HFP_SCO_HCIBYPASS,
&PH_WELL_KNOWN_GUID_DEVINTERFACE_CHARGING_ARBITRATION,
&PH_WELL_KNOWN_GUID_DEVINTERFACE_CONFIGURABLE_USBFN_CHARGER,
&PH_WELL_KNOWN_GUID_DEVINTERFACE_CONFIGURABLE_WIRELESS_CHARGER,
&PH_WELL_KNOWN_GUID_DEVINTERFACE_I2C,
&PH_WELL_KNOWN_GUID_DEVINTERFACE_OPM,
&PH_WELL_KNOWN_GUID_DEVINTERFACE_OPM_2_JTP,
&PH_WELL_KNOWN_GUID_DEVINTERFACE_OPM_2,
&PH_WELL_KNOWN_GUID_DEVINTERFACE_OPM_3,
&PH_WELL_KNOWN_GUID_DEVINTERFACE_BRIGHTNESS,
&PH_WELL_KNOWN_GUID_DEVINTERFACE_BRIGHTNESS_2,
&PH_WELL_KNOWN_GUID_DEVINTERFACE_MIRACAST_DISPLAY,
&PH_WELL_KNOWN_GUID_DEVINTERFACE_BRIGHTNESS_3,
&PH_WELL_KNOWN_GUID_DEVINTERFACE_HPMI,
&PH_WELL_KNOWN_GUID_DEVINTERFACE_VIRTUALIZABLE_DEVICE,
&PH_WELL_KNOWN_GUID_DEVINTERFACE_EMMC_PARTITION_ACCESS_RPMB,
&PH_WELL_KNOWN_GUID_DEVINTERFACE_EMMC_PARTITION_ACCESS_GPP,
&PH_WELL_KNOWN_GUID_DEVINTERFACE_USB_SIDEBAND_AUDIO_HS_HCIBYPASS,
&PH_WELL_KNOWN_GUID_BUS_VPCI,
&PH_WELL_KNOWN_GUID_VPCI_INTERFACE_STANDARD,
&PH_WELL_KNOWN_GUID_DEVINTERFACE_VPCI,
&PH_WELL_KNOWN_GUID_DEVINTERFACE_WWAN_CONTROLLER,
&PH_WELL_KNOWN_GUID_DEVINTERFACE_WDDM3_ON_VB,
&PH_WELL_KNOWN_GUID_DEVINTERFACE_GRAPHICSPOWER,
&PH_WELL_KNOWN_GUID_DEVINTERFACE_GNSS,
&PH_WELL_KNOWN_GUID_DEVINTERFACE_HID,
&PH_WELL_KNOWN_GUID_DEVINTERFACE_LAMP,
&PH_WELL_KNOWN_GUID_DEVINTERFACE_NET,
&PH_WELL_KNOWN_GUID_DEVINTERFACE_NETUIO,
&PH_WELL_KNOWN_GUID_DEVINTERFACE_NFCDTA,
&PH_WELL_KNOWN_GUID_DEVINTERFACE_NFCSE,
&PH_WELL_KNOWN_GUID_DEVINTERFACE_NFP,
&PH_WELL_KNOWN_GUID_DEVINTERFACE_KEYBOARD,
&PH_WELL_KNOWN_GUID_DEVINTERFACE_MODEM,
&PH_WELL_KNOWN_GUID_DEVINTERFACE_MOUSE,
&PH_WELL_KNOWN_GUID_DEVINTERFACE_PARALLEL,
&PH_WELL_KNOWN_GUID_DEVINTERFACE_PARCLASS,
&PH_WELL_KNOWN_GUID_PARALLEL_DEVICE,
&PH_WELL_KNOWN_GUID_PARCLASS_DEVICE,
&PH_WELL_KNOWN_GUID_DEVINTERFACE_DISPLAY_ADAPTER,
&PH_WELL_KNOWN_GUID_DEVINTERFACE_MONITOR,
&PH_WELL_KNOWN_GUID_DEVICE_BATTERY,
&PH_WELL_KNOWN_GUID_DEVICE_APPLICATIONLAUNCH_BUTTON,
&PH_WELL_KNOWN_GUID_DEVICE_SYS_BUTTON,
&PH_WELL_KNOWN_GUID_DEVICE_LID,
&PH_WELL_KNOWN_GUID_DEVICE_THERMAL_ZONE,
&PH_WELL_KNOWN_GUID_DEVICE_FAN,
&PH_WELL_KNOWN_GUID_DEVICE_PROCESSOR,
&PH_WELL_KNOWN_GUID_DEVICE_MEMORY,
&PH_WELL_KNOWN_GUID_DEVICE_ACPI_TIME,
&PH_WELL_KNOWN_GUID_DEVICE_MESSAGE_INDICATOR,
&PH_WELL_KNOWN_GUID_CLASS_INPUT,
&PH_WELL_KNOWN_GUID_DEVINTERFACE_THERMAL_COOLING,
&PH_WELL_KNOWN_GUID_DEVINTERFACE_THERMAL_MANAGER,
&PH_WELL_KNOWN_GUID_DEVINTERFACE_PWM_CONTROLLER,
&PH_WELL_KNOWN_GUID_DEVINTERFACE_USBPRINT,
&PH_WELL_KNOWN_GUID_DEVINTERFACE_IPPUSB_PRINT,
&PH_WELL_KNOWN_GUID_DEVINTERFACE_VM_GENCOUNTER,
&PH_WELL_KNOWN_GUID_DEVINTERFACE_BIOMETRIC_READER,
&PH_WELL_KNOWN_GUID_DEVINTERFACE_SMARTCARD_READER,
&PH_WELL_KNOWN_GUID_DEVINTERFACE_DMR,
&PH_WELL_KNOWN_GUID_DEVINTERFACE_DMP,
&PH_WELL_KNOWN_GUID_DEVINTERFACE_DMS,
&PH_WELL_KNOWN_GUID_DEVINTERFACE_ENHANCED_STORAGE_SILO,
&PH_WELL_KNOWN_GUID_DEVINTERFACE_WPD,
&PH_WELL_KNOWN_GUID_DEVINTERFACE_WPD_PRIVATE,
&PH_WELL_KNOWN_GUID_DEVINTERFACE_WPD_SERVICE,
&PH_WELL_KNOWN_GUID_DEVINTERFACE_SENSOR,
&PH_WELL_KNOWN_GUID_DEVINTERFACE_IMAGE,
&PH_WELL_KNOWN_GUID_DEVINTERFACE_SIDESHOW,
&PH_WELL_KNOWN_GUID_DEVINTERFACE_WIFIDIRECT_DEVICE,
&PH_WELL_KNOWN_GUID_AEPSERVICE_WIFIDIRECT_DEVICE,
&PH_WELL_KNOWN_GUID_DEVINTERFACE_ASP_INFRA_DEVICE,
&PH_WELL_KNOWN_GUID_DXGKDDI_MITIGABLE_DEVICE_INTERFACE,
&PH_WELL_KNOWN_GUID_DXGKDDI_FLEXIOV_DEVICE_INTERFACE,
&PH_WELL_KNOWN_GUID_SRIOV_DEVICE_INTERFACE_STANDARD,
&PH_WELL_KNOWN_GUID_MITIGABLE_DEVICE_INTERFACE,
&PH_WELL_KNOWN_GUID_IO_VOLUME_DEVICE_INTERFACE,
&PH_WELL_KNOWN_GUID_NDIS_LAN_CLASS,
&PH_WELL_KNOWN_GUID_NFC_RADIO_MEDIA_DEVICE_INTERFACE,
&PH_WELL_KNOWN_GUID_NFCSE_RADIO_MEDIA_DEVICE_INTERFACE,
&PH_WELL_KNOWN_GUID_BLUETOOTHLE_DEVICE_INTERFACE,
&PH_WELL_KNOWN_GUID_BLUETOOTH_GATT_SERVICE_DEVICE_INTERFACE,
&PH_WELL_KNOWN_GUID_BUS_TYPE_TREE,
&PH_WELL_KNOWN_GUID_TREE_TPM_SERVICE_FDTPM,
&PH_WELL_KNOWN_GUID_TREE_TPM_SERVICE_STPM,
&PH_WELL_KNOWN_GUID_DISPLAY_DEVICE_ARRIVAL,
&PH_WELL_KNOWN_GUID_COMPUTE_DEVICE_ARRIVAL,
};
static int __cdecl PhpWellKnownGuidSortFunction(
const void* Left,
const void* Right
)
{
PPH_WELL_KNOWN_GUID lhsItem;
PPH_WELL_KNOWN_GUID rhsItem;
lhsItem = *(PPH_WELL_KNOWN_GUID*)Left;
rhsItem = *(PPH_WELL_KNOWN_GUID*)Right;
return memcmp(lhsItem->Guid, rhsItem->Guid, sizeof(GUID));
}
static int __cdecl PhpWellKnownGuidSearchFunction(
const GUID* Guid,
const void* Item
)
{
PPH_WELL_KNOWN_GUID item;
item = *(PPH_WELL_KNOWN_GUID*)Item;
return memcmp(Guid, item->Guid, sizeof(GUID));
}
PPH_STRING PhpDevPropWellKnownGuidToString(
_In_ PGUID Guid
)
{
const PH_WELL_KNOWN_GUID** entry;
if (PhBeginInitOnce(&PhpWellKnownGuidsInitOnce))
{
qsort(
(void*)PhpWellKnownGuids,
RTL_NUMBER_OF(PhpWellKnownGuids),
sizeof(PPH_WELL_KNOWN_GUID),
PhpWellKnownGuidSortFunction
);
#ifdef DEBUG
// check for collisions
for (ULONG i = 0; i < RTL_NUMBER_OF(PhpWellKnownGuids) - 1; i++)
{
const PH_WELL_KNOWN_GUID* lhs = PhpWellKnownGuids[i];
const PH_WELL_KNOWN_GUID* rhs = PhpWellKnownGuids[i + 1];
assert(!IsEqualGUID(&lhs->Guid, &rhs->Guid));
}
#endif
PhEndInitOnce(&PhpWellKnownGuidsInitOnce);
}
entry = bsearch(
Guid,
PhpWellKnownGuids,
RTL_NUMBER_OF(PhpWellKnownGuids),
sizeof(PPH_WELL_KNOWN_GUID),
PhpWellKnownGuidSearchFunction
);
if (!entry)
return NULL;
return PhCreateString2((PPH_STRINGREF)&(*entry)->Symbol);
}
_Function_class_(PH_DEVICE_PROPERTY_FILL_CALLBACK)
VOID NTAPI PhpDevPropFillGuid(
_In_ HDEVINFO DeviceInfoSet,
_In_ PPH_DEVINFO_DATA DeviceInfoData,
_In_ const DEVPROPKEY* PropertyKey,
_Out_ PPH_DEVICE_PROPERTY Property,
_In_ ULONG Flags
)
{
Property->Type = PhDevicePropertyTypeGUID;
if (DeviceInfoData->Interface)
{
if (Flags & DEVPROP_FILL_FLAG_CLASS)
{
Property->Valid = PhpGetClassPropertyGuid(
&DeviceInfoData->InterfaceData.InterfaceClassGuid,
PropertyKey,
DICLASSPROP_INTERFACE,
&Property->Guid
);
}
else
{
Property->Valid = PhpGetDeviceInterfacePropertyGuid(
DeviceInfoSet,
&DeviceInfoData->InterfaceData,
PropertyKey,
&Property->Guid
);
}
}
else
{
if (Flags & DEVPROP_FILL_FLAG_CLASS)
{
Property->Valid = PhpGetClassPropertyGuid(
&DeviceInfoData->DeviceData.ClassGuid,
PropertyKey,
DICLASSPROP_INSTALLER,
&Property->Guid
);
}
else
{
Property->Valid = PhpGetDevicePropertyGuid(
DeviceInfoSet,
&DeviceInfoData->DeviceData,
PropertyKey,
&Property->Guid
);
}
}
if (Property->Valid)
{
Property->AsString = PhpDevPropWellKnownGuidToString(&Property->Guid);
if (!Property->AsString)
Property->AsString = PhFormatGuid(&Property->Guid);
}
}
PPH_STRING PhpDevPropPciDeviceTypeToString(
_In_ ULONG Flags
)
{
PH_STRING_BUILDER stringBuilder;
WCHAR pointer[PH_PTR_STR_LEN_1];
PhInitializeStringBuilder(&stringBuilder, 10);
if (BooleanFlagOn(Flags, DevProp_PciDevice_DeviceType_PciConventional))
PhAppendStringBuilder2(&stringBuilder, L"PciConventional, ");
else if (BooleanFlagOn(Flags, DevProp_PciDevice_DeviceType_PciX))
PhAppendStringBuilder2(&stringBuilder, L"PciX, ");
else if (BooleanFlagOn(Flags, DevProp_PciDevice_DeviceType_PciExpressEndpoint))
PhAppendStringBuilder2(&stringBuilder, L"PciExpressEndpoint, ");
else if (BooleanFlagOn(Flags, DevProp_PciDevice_DeviceType_PciExpressLegacyEndpoint))
PhAppendStringBuilder2(&stringBuilder, L"PciExpressLegacyEndpoint, ");
else if (BooleanFlagOn(Flags, DevProp_PciDevice_DeviceType_PciExpressRootComplexIntegratedEndpoint))
PhAppendStringBuilder2(&stringBuilder, L"PciExpressRootComplexIntegratedEndpoint, ");
else if (BooleanFlagOn(Flags, DevProp_PciDevice_DeviceType_PciExpressTreatedAsPci))
PhAppendStringBuilder2(&stringBuilder, L"PciExpressTreatedAsPci, ");
else if (BooleanFlagOn(Flags, DevProp_PciDevice_BridgeType_PciConventional))
PhAppendStringBuilder2(&stringBuilder, L"PciConventional, ");
else if (BooleanFlagOn(Flags, DevProp_PciDevice_BridgeType_PciX))
PhAppendStringBuilder2(&stringBuilder, L"PciX, ");
else if (BooleanFlagOn(Flags, DevProp_PciDevice_BridgeType_PciExpressRootPort))
PhAppendStringBuilder2(&stringBuilder, L"PciExpressRootPort, ");
else if (BooleanFlagOn(Flags, DevProp_PciDevice_BridgeType_PciExpressUpstreamSwitchPort))
PhAppendStringBuilder2(&stringBuilder, L"PciExpressUpstreamSwitchPort, ");
else if (BooleanFlagOn(Flags, DevProp_PciDevice_BridgeType_PciExpressDownstreamSwitchPort))
PhAppendStringBuilder2(&stringBuilder, L"PciExpressDownstreamSwitchPort, ");
else if (BooleanFlagOn(Flags, DevProp_PciDevice_BridgeType_PciExpressToPciXBridge))
PhAppendStringBuilder2(&stringBuilder, L"PciExpressToPciXBridge, ");
else if (BooleanFlagOn(Flags, DevProp_PciDevice_BridgeType_PciXToExpressBridge))
PhAppendStringBuilder2(&stringBuilder, L"PciXToExpressBridge, ");
else if (BooleanFlagOn(Flags, DevProp_PciDevice_BridgeType_PciExpressTreatedAsPci))
PhAppendStringBuilder2(&stringBuilder, L"PciExpressTreatedAsPci, ");
else if (BooleanFlagOn(Flags, DevProp_PciDevice_BridgeType_PciExpressEventCollector))
PhAppendStringBuilder2(&stringBuilder, L"PciExpressEventCollector, ");
if (PhEndsWithString2(stringBuilder.String, L", ", FALSE))
PhRemoveEndStringBuilder(&stringBuilder, 2);
if (Flags)
{
PhPrintPointer(pointer, UlongToPtr(Flags));
PhAppendFormatStringBuilder(&stringBuilder, L" (%s)", pointer);
}
return PhFinalStringBuilderString(&stringBuilder);
}
_Function_class_(PH_DEVICE_PROPERTY_FILL_CALLBACK)
VOID NTAPI PhpDevPropFillPciDeviceType(
_In_ HDEVINFO DeviceInfoSet,
_In_ PPH_DEVINFO_DATA DeviceInfoData,
_In_ const DEVPROPKEY* PropertyKey,
_Out_ PPH_DEVICE_PROPERTY Property,
_In_ ULONG Flags
)
{
PhpDevPropFillUInt32Common(
DeviceInfoSet,
DeviceInfoData,
PropertyKey,
Property,
Flags
);
if (Property->Valid)
{
Property->AsString = PhpDevPropPciDeviceTypeToString(Property->UInt32);
}
}
PPH_STRING PhpDevPropPciDeviceInterruptSupportToString(
_In_ ULONG Flags
)
{
PH_STRING_BUILDER stringBuilder;
WCHAR pointer[PH_PTR_STR_LEN_1];
PhInitializeStringBuilder(&stringBuilder, 10);
if (BooleanFlagOn(Flags, DevProp_PciDevice_InterruptType_LineBased))
PhAppendStringBuilder2(&stringBuilder, L"Line based, ");
if (BooleanFlagOn(Flags, DevProp_PciDevice_InterruptType_Msi))
PhAppendStringBuilder2(&stringBuilder, L"Msi, ");
if (BooleanFlagOn(Flags, DevProp_PciDevice_InterruptType_MsiX))
PhAppendStringBuilder2(&stringBuilder, L"MsiX, ");
if (PhEndsWithString2(stringBuilder.String, L", ", FALSE))
PhRemoveEndStringBuilder(&stringBuilder, 2);
PhPrintPointer(pointer, UlongToPtr(Flags));
PhAppendFormatStringBuilder(&stringBuilder, L" (%s)", pointer);
return PhFinalStringBuilderString(&stringBuilder);
}
_Function_class_(PH_DEVICE_PROPERTY_FILL_CALLBACK)
VOID NTAPI PhpDevPropFillPciDeviceInterruptSupport(
_In_ HDEVINFO DeviceInfoSet,
_In_ PPH_DEVINFO_DATA DeviceInfoData,
_In_ const DEVPROPKEY* PropertyKey,
_Out_ PPH_DEVICE_PROPERTY Property,
_In_ ULONG Flags
)
{
PhpDevPropFillUInt32Common(
DeviceInfoSet,
DeviceInfoData,
PropertyKey,
Property,
Flags
);
if (Property->Valid)
{
Property->AsString = PhpDevPropPciDeviceInterruptSupportToString(Property->UInt32);
}
}
PPH_STRING PhpDevPropPciDeviceRequestSizeToString(
_In_ ULONG Flags
)
{
PH_STRING_BUILDER stringBuilder;
WCHAR pointer[PH_PTR_STR_LEN_1];
PhInitializeStringBuilder(&stringBuilder, 10);
if (BooleanFlagOn(Flags, DevProp_PciExpressDevice_PayloadOrRequestSize_128Bytes))
PhAppendStringBuilder2(&stringBuilder, L"128B, ");
else if (BooleanFlagOn(Flags, DevProp_PciExpressDevice_PayloadOrRequestSize_256Bytes))
PhAppendStringBuilder2(&stringBuilder, L"256B, ");
else if (BooleanFlagOn(Flags, DevProp_PciExpressDevice_PayloadOrRequestSize_512Bytes))
PhAppendStringBuilder2(&stringBuilder, L"512B, ");
else if (BooleanFlagOn(Flags, DevProp_PciExpressDevice_PayloadOrRequestSize_1024Bytes))
PhAppendStringBuilder2(&stringBuilder, L"1024B, ");
else if (BooleanFlagOn(Flags, DevProp_PciExpressDevice_PayloadOrRequestSize_2048Bytes))
PhAppendStringBuilder2(&stringBuilder, L"2048B, ");
else if (BooleanFlagOn(Flags, DevProp_PciExpressDevice_PayloadOrRequestSize_4096Bytes))
PhAppendStringBuilder2(&stringBuilder, L"4096B, ");
if (PhEndsWithString2(stringBuilder.String, L", ", FALSE))
PhRemoveEndStringBuilder(&stringBuilder, 2);
PhPrintPointer(pointer, UlongToPtr(Flags));
PhAppendFormatStringBuilder(&stringBuilder, L" (%s)", pointer);
return PhFinalStringBuilderString(&stringBuilder);
}
_Function_class_(PH_DEVICE_PROPERTY_FILL_CALLBACK)
VOID NTAPI PhpDevPropFillPciDeviceRequestSize(
_In_ HDEVINFO DeviceInfoSet,
_In_ PPH_DEVINFO_DATA DeviceInfoData,
_In_ const DEVPROPKEY* PropertyKey,
_Out_ PPH_DEVICE_PROPERTY Property,
_In_ ULONG Flags
)
{
PhpDevPropFillUInt32Common(
DeviceInfoSet,
DeviceInfoData,
PropertyKey,
Property,
Flags
);
if (Property->Valid)
{
Property->AsString = PhpDevPropPciDeviceRequestSizeToString(Property->UInt32);
}
}
PPH_STRING PhpDevPropPciDeviceSriovSupportToString(
_In_ ULONG Flags
)
{
PH_STRING_BUILDER stringBuilder;
WCHAR pointer[PH_PTR_STR_LEN_1];
PhInitializeStringBuilder(&stringBuilder, 10);
if (BooleanFlagOn(Flags, DevProp_PciDevice_SriovSupport_Ok))
PhAppendStringBuilder2(&stringBuilder, L"Ok, ");
if (BooleanFlagOn(Flags, DevProp_PciDevice_SriovSupport_MissingAcs))
PhAppendStringBuilder2(&stringBuilder, L"MissingAcs, ");
if (BooleanFlagOn(Flags, DevProp_PciDevice_SriovSupport_MissingPfDriver))
PhAppendStringBuilder2(&stringBuilder, L"MissingPfDriver, ");
if (BooleanFlagOn(Flags, DevProp_PciDevice_SriovSupport_NoBusResource))
PhAppendStringBuilder2(&stringBuilder, L"NoBusResource, ");
if (BooleanFlagOn(Flags, DevProp_PciDevice_SriovSupport_DidntGetVfBarSpace))
PhAppendStringBuilder2(&stringBuilder, L"DidntGetVfBarSpace, ");
if (PhEndsWithString2(stringBuilder.String, L", ", FALSE))
PhRemoveEndStringBuilder(&stringBuilder, 2);
PhPrintPointer(pointer, UlongToPtr(Flags));
PhAppendFormatStringBuilder(&stringBuilder, L" (%s)", pointer);
return PhFinalStringBuilderString(&stringBuilder);
}
_Function_class_(PH_DEVICE_PROPERTY_FILL_CALLBACK)
VOID NTAPI PhpDevPropFillPciDeviceSriovSupport(
_In_ HDEVINFO DeviceInfoSet,
_In_ PPH_DEVINFO_DATA DeviceInfoData,
_In_ const DEVPROPKEY* PropertyKey,
_Out_ PPH_DEVICE_PROPERTY Property,
_In_ ULONG Flags
)
{
PhpDevPropFillUInt32Common(
DeviceInfoSet,
DeviceInfoData,
PropertyKey,
Property,
Flags
);
if (Property->Valid)
{
Property->AsString = PhpDevPropPciDeviceSriovSupportToString(Property->UInt32);
}
}
_Function_class_(PH_DEVICE_PROPERTY_FILL_CALLBACK)
VOID NTAPI PhpDevPropFillBoolean(
_In_ HDEVINFO DeviceInfoSet,
_In_ PPH_DEVINFO_DATA DeviceInfoData,
_In_ const DEVPROPKEY* PropertyKey,
_Out_ PPH_DEVICE_PROPERTY Property,
_In_ ULONG Flags
)
{
Property->Type = PhDevicePropertyTypeBoolean;
if (DeviceInfoData->Interface)
{
if (Flags & DEVPROP_FILL_FLAG_CLASS)
{
Property->Valid = PhpGetClassPropertyBoolean(
&DeviceInfoData->InterfaceData.InterfaceClassGuid,
PropertyKey,
DICLASSPROP_INTERFACE,
&Property->Boolean
);
}
else
{
Property->Valid = PhpGetDeviceInterfacePropertyBoolean(
DeviceInfoSet,
&DeviceInfoData->InterfaceData,
PropertyKey,
&Property->Boolean
);
}
}
else
{
if (Flags & DEVPROP_FILL_FLAG_CLASS)
{
Property->Valid = PhpGetClassPropertyBoolean(
&DeviceInfoData->DeviceData.ClassGuid,
PropertyKey,
DICLASSPROP_INSTALLER,
&Property->Boolean
);
}
else
{
Property->Valid = PhpGetDevicePropertyBoolean(
DeviceInfoSet,
&DeviceInfoData->DeviceData,
PropertyKey,
&Property->Boolean
);
}
}
if (Property->Valid)
{
if (Property->Boolean)
{
static CONST PH_STRINGREF string = PH_STRINGREF_INIT(L"true");
Property->AsString = PhCreateString2(&string);
}
else
{
static CONST PH_STRINGREF string = PH_STRINGREF_INIT(L"false");
Property->AsString = PhCreateString2(&string);
}
}
}
_Function_class_(PH_DEVICE_PROPERTY_FILL_CALLBACK)
VOID NTAPI PhpDevPropFillTimeStamp(
_In_ HDEVINFO DeviceInfoSet,
_In_ PPH_DEVINFO_DATA DeviceInfoData,
_In_ const DEVPROPKEY* PropertyKey,
_Out_ PPH_DEVICE_PROPERTY Property,
_In_ ULONG Flags
)
{
Property->Type = PhDevicePropertyTypeTimeStamp;
if (DeviceInfoData->Interface)
{
if (Flags & DEVPROP_FILL_FLAG_CLASS)
{
Property->Valid = PhpGetClassPropertyTimeStamp(
&DeviceInfoData->InterfaceData.InterfaceClassGuid,
PropertyKey,
DICLASSPROP_INTERFACE,
&Property->TimeStamp
);
}
else
{
Property->Valid = PhpGetDeviceInterfacePropertyTimeStamp(
DeviceInfoSet,
&DeviceInfoData->InterfaceData,
PropertyKey,
&Property->TimeStamp
);
}
}
else
{
if (Flags & DEVPROP_FILL_FLAG_CLASS)
{
Property->Valid = PhpGetClassPropertyTimeStamp(
&DeviceInfoData->DeviceData.ClassGuid,
PropertyKey,
DICLASSPROP_INSTALLER,
&Property->TimeStamp
);
}
else
{
Property->Valid = PhpGetDevicePropertyTimeStamp(
DeviceInfoSet,
&DeviceInfoData->DeviceData,
PropertyKey,
&Property->TimeStamp
);
}
}
if (Property->Valid)
{
SYSTEMTIME systemTime;
PhLargeIntegerToLocalSystemTime(&systemTime, &Property->TimeStamp);
Property->AsString = PhFormatDateTime(&systemTime);
}
}
_Function_class_(PH_DEVICE_PROPERTY_FILL_CALLBACK)
VOID NTAPI PhpDevPropFillStringList(
_In_ HDEVINFO DeviceInfoSet,
_In_ PPH_DEVINFO_DATA DeviceInfoData,
_In_ const DEVPROPKEY* PropertyKey,
_Out_ PPH_DEVICE_PROPERTY Property,
_In_ ULONG Flags
)
{
Property->Type = PhDevicePropertyTypeStringList;
if (DeviceInfoData->Interface)
{
if (Flags & DEVPROP_FILL_FLAG_CLASS)
{
Property->Valid = PhpGetClassPropertyStringList(
&DeviceInfoData->InterfaceData.InterfaceClassGuid,
PropertyKey,
DICLASSPROP_INTERFACE,
&Property->StringList
);
}
else
{
Property->Valid = PhpGetDeviceInterfacePropertyStringList(
DeviceInfoSet,
&DeviceInfoData->InterfaceData,
PropertyKey,
&Property->StringList
);
}
}
else
{
if (Flags & DEVPROP_FILL_FLAG_CLASS)
{
Property->Valid = PhpGetClassPropertyStringList(
&DeviceInfoData->DeviceData.ClassGuid,
PropertyKey,
DICLASSPROP_INSTALLER,
&Property->StringList
);
}
else
{
Property->Valid = PhpGetDevicePropertyStringList(
DeviceInfoSet,
&DeviceInfoData->DeviceData,
PropertyKey,
&Property->StringList
);
}
}
if (Property->Valid && Property->StringList->Count > 0)
{
PH_STRING_BUILDER stringBuilder;
PPH_STRING string;
PhInitializeStringBuilder(&stringBuilder, Property->StringList->Count);
for (ULONG i = 0; i < Property->StringList->Count - 1; i++)
{
string = Property->StringList->Items[i];
PhAppendStringBuilder(&stringBuilder, &string->sr);
PhAppendStringBuilder2(&stringBuilder, L", ");
}
string = Property->StringList->Items[Property->StringList->Count - 1];
PhAppendStringBuilder(&stringBuilder, &string->sr);
Property->AsString = PhFinalStringBuilderString(&stringBuilder);
PhReferenceObject(Property->AsString);
PhDeleteStringBuilder(&stringBuilder);
}
}
_Function_class_(PH_DEVICE_PROPERTY_FILL_CALLBACK)
VOID NTAPI PhpDevPropFillStringOrStringList(
_In_ HDEVINFO DeviceInfoSet,
_In_ PPH_DEVINFO_DATA DeviceInfoData,
_In_ const DEVPROPKEY* PropertyKey,
_Out_ PPH_DEVICE_PROPERTY Property,
_In_ ULONG Flags
)
{
PhpDevPropFillString(
DeviceInfoSet,
DeviceInfoData,
PropertyKey,
Property,
Flags
);
if (!Property->Valid)
{
PhpDevPropFillStringList(
DeviceInfoSet,
DeviceInfoData,
PropertyKey,
Property,
Flags
);
}
}
VOID PhpDevPropFillBinaryCommon(
_In_ HDEVINFO DeviceInfoSet,
_In_ PPH_DEVINFO_DATA DeviceInfoData,
_In_ const DEVPROPKEY* PropertyKey,
_Out_ PPH_DEVICE_PROPERTY Property,
_In_ ULONG Flags
)
{
Property->Type = PhDevicePropertyTypeBinary;
if (DeviceInfoData->Interface)
{
if (Flags & DEVPROP_FILL_FLAG_CLASS)
{
Property->Valid = PhpGetClassPropertyBinary(
&DeviceInfoData->InterfaceData.InterfaceClassGuid,
PropertyKey,
DICLASSPROP_INTERFACE,
&Property->Binary.Buffer,
&Property->Binary.Size
);
}
else
{
Property->Valid = PhpGetDeviceInterfacePropertyBinary(
DeviceInfoSet,
&DeviceInfoData->InterfaceData,
PropertyKey,
&Property->Binary.Buffer,
&Property->Binary.Size
);
}
}
else
{
if (Flags & DEVPROP_FILL_FLAG_CLASS)
{
Property->Valid = PhpGetClassPropertyBinary(
&DeviceInfoData->DeviceData.ClassGuid,
PropertyKey,
DICLASSPROP_INSTALLER,
&Property->Binary.Buffer,
&Property->Binary.Size
);
}
else
{
Property->Valid = PhpGetDevicePropertyBinary(
DeviceInfoSet,
&DeviceInfoData->DeviceData,
PropertyKey,
&Property->Binary.Buffer,
&Property->Binary.Size
);
}
}
}
_Function_class_(PH_DEVICE_PROPERTY_FILL_CALLBACK)
VOID NTAPI PhpDevPropFillBinary(
_In_ HDEVINFO DeviceInfoSet,
_In_ PPH_DEVINFO_DATA DeviceInfoData,
_In_ const DEVPROPKEY* PropertyKey,
_Out_ PPH_DEVICE_PROPERTY Property,
_In_ ULONG Flags
)
{
PhpDevPropFillBinaryCommon(
DeviceInfoSet,
DeviceInfoData,
PropertyKey,
Property,
Flags
);
if (Property->Valid)
{
Property->AsString = PhBufferToHexString(Property->Binary.Buffer, Property->Binary.Size);
}
}
static const PH_STRINGREF UnknownString = PH_STRINGREF_INIT(L"Unk");
PCPH_STRINGREF PhpDevPowerStateString(
_In_ DEVICE_POWER_STATE PowerState
)
{
static const PH_STRINGREF states[] =
{
PH_STRINGREF_INIT(L"Uns"), // PowerDeviceUnspecified
PH_STRINGREF_INIT(L"D0"), // PowerDeviceD0
PH_STRINGREF_INIT(L"D1"), // PowerDeviceD1
PH_STRINGREF_INIT(L"D2"), // PowerDeviceD2
PH_STRINGREF_INIT(L"D3"), // PowerDeviceD3
PH_STRINGREF_INIT(L"D4"), // PowerDeviceD4
PH_STRINGREF_INIT(L"Max"), // PowerDeviceMaximum
};
if (PowerState >= 0 && PowerState < RTL_NUMBER_OF(states))
return &states[PowerState];
return &UnknownString;
}
PCPH_STRINGREF PhpDevSysPowerStateString(
_In_ SYSTEM_POWER_STATE PowerState
)
{
static const PH_STRINGREF states[] =
{
PH_STRINGREF_INIT(L"Uns"), // PowerSystemUnspecified
PH_STRINGREF_INIT(L"S0"), // PowerSystemWorking
PH_STRINGREF_INIT(L"S1"), // PowerSystemSleep1
PH_STRINGREF_INIT(L"S2"), // PowerSystemSleep2
PH_STRINGREF_INIT(L"S3"), // PowerSystemSleep3
PH_STRINGREF_INIT(L"S4"), // PowerSystemHybernate
PH_STRINGREF_INIT(L"S5"), // PowerSystemShutdown
PH_STRINGREF_INIT(L"Max"), // PowerSystemMaximum
};
if (PowerState >= 0 && PowerState < RTL_NUMBER_OF(states))
return &states[PowerState];
return &UnknownString;
}
PPH_STRING PhpDevSysPowerPowerDataString(
_In_ PCM_POWER_DATA PowerData
)
{
static const PH_ACCESS_ENTRY pdCap[] =
{
{ L"PDCAP_D0_SUPPORTED", PDCAP_D0_SUPPORTED, FALSE, FALSE, L"D0" },
{ L"PDCAP_D1_SUPPORTED", PDCAP_D1_SUPPORTED, FALSE, FALSE, L"D1" },
{ L"PDCAP_D2_SUPPORTED", PDCAP_D2_SUPPORTED, FALSE, FALSE, L"D2" },
{ L"PDCAP_D3_SUPPORTED", PDCAP_D3_SUPPORTED, FALSE, FALSE, L"D3" },
{ L"PDCAP_WAKE_FROM_D0_SUPPORTED", PDCAP_WAKE_FROM_D0_SUPPORTED, FALSE, FALSE, L"Wake from D0" },
{ L"PDCAP_WAKE_FROM_D1_SUPPORTED", PDCAP_WAKE_FROM_D1_SUPPORTED, FALSE, FALSE, L"Wake from D1" },
{ L"PDCAP_WAKE_FROM_D2_SUPPORTED", PDCAP_WAKE_FROM_D2_SUPPORTED, FALSE, FALSE, L"Wake from D2" },
{ L"PDCAP_WAKE_FROM_D3_SUPPORTED", PDCAP_WAKE_FROM_D3_SUPPORTED, FALSE, FALSE, L"Wake from D3" },
{ L"PDCAP_WARM_EJECT_SUPPORTED", PDCAP_WARM_EJECT_SUPPORTED, FALSE, FALSE, L"Warm eject" },
};
PH_FORMAT format[29];
ULONG count = 0;
PPH_STRING capabilities;
PPH_STRING string;
PhInitFormatSR(&format[count++], *PhpDevPowerStateString(PowerData->PD_MostRecentPowerState));
if (PowerData->PD_D1Latency)
{
PhInitFormatS(&format[count++], L", ");
PhInitFormatU(&format[count++], PowerData->PD_D1Latency);
PhInitFormatS(&format[count++], L" D1 latency");
}
if (PowerData->PD_D2Latency)
{
PhInitFormatS(&format[count++], L", ");
PhInitFormatU(&format[count++], PowerData->PD_D2Latency);
PhInitFormatS(&format[count++], L" D2 latency");
}
if (PowerData->PD_D2Latency)
{
PhInitFormatS(&format[count++], L", ");
PhInitFormatU(&format[count++], PowerData->PD_D3Latency);
PhInitFormatS(&format[count++], L" D3 latency");
}
PhInitFormatS(&format[count++], L", S0->");
PhInitFormatSR(&format[count++], *PhpDevPowerStateString(PowerData->PD_PowerStateMapping[PowerSystemWorking]));
PhInitFormatS(&format[count++], L", S1->");
PhInitFormatSR(&format[count++], *PhpDevPowerStateString(PowerData->PD_PowerStateMapping[PowerSystemSleeping1]));
PhInitFormatS(&format[count++], L", S2->");
PhInitFormatSR(&format[count++], *PhpDevPowerStateString(PowerData->PD_PowerStateMapping[PowerSystemSleeping2]));
PhInitFormatS(&format[count++], L", S3->");
PhInitFormatSR(&format[count++], *PhpDevPowerStateString(PowerData->PD_PowerStateMapping[PowerSystemSleeping3]));
PhInitFormatS(&format[count++], L", S4->");
PhInitFormatSR(&format[count++], *PhpDevPowerStateString(PowerData->PD_PowerStateMapping[PowerSystemHibernate]));
PhInitFormatS(&format[count++], L", S5->");
PhInitFormatSR(&format[count++], *PhpDevPowerStateString(PowerData->PD_PowerStateMapping[PowerSystemShutdown]));
capabilities = PhGetAccessString(PowerData->PD_Capabilities, (PVOID)pdCap, RTL_NUMBER_OF(pdCap));
PhInitFormatS(&format[count++], L", (");
PhInitFormatSR(&format[count++], capabilities->sr);
PhInitFormatS(&format[count++], L" (0x");
PhInitFormatX(&format[count++], PowerData->PD_Capabilities);
PhInitFormatS(&format[count++], L"))");
if (PowerData->PD_DeepestSystemWake != PowerSystemUnspecified)
{
PhInitFormatS(&format[count++], L", Deepest wake ");
PhInitFormatSR(&format[count++], *PhpDevSysPowerStateString(PowerData->PD_DeepestSystemWake));
}
string = PhFormat(format, count, 20);
PhClearReference(&capabilities);
return string;
}
_Function_class_(PH_DEVICE_PROPERTY_FILL_CALLBACK)
VOID NTAPI PhpDevPropFillPowerData(
_In_ HDEVINFO DeviceInfoSet,
_In_ PPH_DEVINFO_DATA DeviceInfoData,
_In_ const DEVPROPKEY* PropertyKey,
_Out_ PPH_DEVICE_PROPERTY Property,
_In_ ULONG Flags
)
{
PhpDevPropFillBinaryCommon(
DeviceInfoSet,
DeviceInfoData,
PropertyKey,
Property,
Flags
);
if (Property->Valid)
{
if (Property->Binary.Size >= sizeof(CM_POWER_DATA))
Property->AsString = PhpDevSysPowerPowerDataString((PCM_POWER_DATA)Property->Binary.Buffer);
else
Property->AsString = PhBufferToHexString(Property->Binary.Buffer, Property->Binary.Size);
}
}
_Function_class_(PH_DEVICE_PROPERTY_FILL_CALLBACK)
VOID NTAPI PhpDevPropFillUInt32Flags(
_In_ HDEVINFO DeviceInfoSet,
_In_ PPH_DEVINFO_DATA DeviceInfoData,
_In_ const DEVPROPKEY* PropertyKey,
_Out_ PPH_DEVICE_PROPERTY Property,
_In_ ULONG Flags
)
{
#define PH_DEVICE_FLAG(x, n) { TEXT(#x), x, FALSE, FALSE, n }
static const PH_ACCESS_ENTRY deviceCapabilities[] =
{
PH_DEVICE_FLAG(CM_DEVCAP_LOCKSUPPORTED, L"Lock supported"),
PH_DEVICE_FLAG(CM_DEVCAP_EJECTSUPPORTED, L"Eject supported"),
PH_DEVICE_FLAG(CM_DEVCAP_REMOVABLE, L"Removable"),
PH_DEVICE_FLAG(CM_DEVCAP_DOCKDEVICE, L"Dock device"),
PH_DEVICE_FLAG(CM_DEVCAP_UNIQUEID, L"Unique ID"),
PH_DEVICE_FLAG(CM_DEVCAP_SILENTINSTALL, L"Silent install"),
PH_DEVICE_FLAG(CM_DEVCAP_RAWDEVICEOK, L"Raw device ok"),
PH_DEVICE_FLAG(CM_DEVCAP_SURPRISEREMOVALOK, L"Surprise removal ok"),
PH_DEVICE_FLAG(CM_DEVCAP_HARDWAREDISABLED, L"Hardware disabled"),
PH_DEVICE_FLAG(CM_DEVCAP_NONDYNAMIC, L"No dynamic"),
PH_DEVICE_FLAG(CM_DEVCAP_SECUREDEVICE, L"Secure device"),
};
static const PH_ACCESS_ENTRY devNodeStatus[] =
{
PH_DEVICE_FLAG(DN_ROOT_ENUMERATED, L"Enumerated"),
PH_DEVICE_FLAG(DN_DRIVER_LOADED, L"Driver loaded"),
PH_DEVICE_FLAG(DN_ENUM_LOADED, L"Enumerator loaded"),
PH_DEVICE_FLAG(DN_STARTED, L"Started"),
PH_DEVICE_FLAG(DN_MANUAL, L"Manually installed"),
PH_DEVICE_FLAG(DN_NEED_TO_ENUM, L"Needs enumerated"),
PH_DEVICE_FLAG(DN_DRIVER_BLOCKED, L"Driver blocked"),
PH_DEVICE_FLAG(DN_HARDWARE_ENUM, L"Hardware enum"),
PH_DEVICE_FLAG(DN_NEED_RESTART, L"Needs reboot"),
PH_DEVICE_FLAG(DN_CHILD_WITH_INVALID_ID, L"Child with invalid ID"),
PH_DEVICE_FLAG(DN_HAS_PROBLEM, L"Has problem"),
PH_DEVICE_FLAG(DN_FILTERED, L"Filtered"),
PH_DEVICE_FLAG(DN_LEGACY_DRIVER, L"Legacy driver"),
PH_DEVICE_FLAG(DN_DISABLEABLE, L"Disabled"),
PH_DEVICE_FLAG(DN_REMOVABLE, L"Removable"),
PH_DEVICE_FLAG(DN_PRIVATE_PROBLEM, L"Has problem"),
PH_DEVICE_FLAG(DN_QUERY_REMOVE_PENDING, L"Query remove pending"),
PH_DEVICE_FLAG(DN_QUERY_REMOVE_ACTIVE, L"Query remove active"),
PH_DEVICE_FLAG(DN_WILL_BE_REMOVED, L"Being removed"),
PH_DEVICE_FLAG(DN_NOT_FIRST_TIMEE, L"Received config"),
PH_DEVICE_FLAG(DN_STOP_FREE_RES, L"To be freed"),
PH_DEVICE_FLAG(DN_REBAL_CANDIDATE, L"Rebalance candidate"),
PH_DEVICE_FLAG(DN_BAD_PARTIAL, L"Bad partial"),
PH_DEVICE_FLAG(DN_NT_ENUMERATOR, L"NT enumerator"),
PH_DEVICE_FLAG(DN_NT_DRIVER, L"NT driver"),
PH_DEVICE_FLAG(DN_DEVICE_DISCONNECTED, L"Disconnected"),
PH_DEVICE_FLAG(DN_ARM_WAKEUP, L"Wakeup device"),
PH_DEVICE_FLAG(DN_APM_ENUMERATOR, L"Advanced power enumerator"),
PH_DEVICE_FLAG(DN_APM_DRIVER, L"Advanced power aware"),
PH_DEVICE_FLAG(DN_SILENT_INSTALL, L"Silent install"),
PH_DEVICE_FLAG(DN_NO_SHOW_IN_DM, L"Hidden from device manager"),
PH_DEVICE_FLAG(DN_BOOT_LOG_PROB, L"Boot log problem"),
};
static const PH_ACCESS_ENTRY characteristics[] =
{
PH_DEVICE_FLAG(FILE_REMOVABLE_MEDIA, L"Removable media"),
PH_DEVICE_FLAG(FILE_READ_ONLY_DEVICE, L"Read only device"),
PH_DEVICE_FLAG(FILE_FLOPPY_DISKETTE, L"Floppy disk"),
PH_DEVICE_FLAG(FILE_WRITE_ONCE_MEDIA, L"Write once media"),
PH_DEVICE_FLAG(FILE_REMOTE_DEVICE, L"Remote device"),
PH_DEVICE_FLAG(FILE_DEVICE_IS_MOUNTED, L"Mounted"),
PH_DEVICE_FLAG(FILE_VIRTUAL_VOLUME, L"Virtual volume"),
PH_DEVICE_FLAG(FILE_AUTOGENERATED_DEVICE_NAME, L"Autogenerated device name"),
PH_DEVICE_FLAG(FILE_DEVICE_SECURE_OPEN, L"Secure open"),
PH_DEVICE_FLAG(FILE_CHARACTERISTIC_PNP_DEVICE, L"PnP device"),
PH_DEVICE_FLAG(FILE_CHARACTERISTIC_TS_DEVICE, L"Terminal services device"),
PH_DEVICE_FLAG(FILE_CHARACTERISTIC_WEBDAV_DEVICE, L"WebDAV device"),
PH_DEVICE_FLAG(FILE_CHARACTERISTIC_CSV, L"Cluster shared volume"),
PH_DEVICE_FLAG(FILE_DEVICE_ALLOW_APPCONTAINER_TRAVERSAL, L"Allow app container traversal"),
PH_DEVICE_FLAG(FILE_PORTABLE_DEVICE, L"Portable device"),
PH_DEVICE_FLAG(FILE_REMOTE_DEVICE_VSMB, L"Virtual SCSI device"),
PH_DEVICE_FLAG(FILE_DEVICE_REQUIRE_SECURITY_CHECK, L"Require security check"),
};
static const PH_ACCESS_ENTRY nameAttributes[] =
{
PH_DEVICE_FLAG(CM_NAME_ATTRIBUTE_NAME_RETRIEVED_FROM_DEVICE, L"Retrieved from device"),
PH_DEVICE_FLAG(CM_NAME_ATTRIBUTE_USER_ASSIGNED_NAME, L"User assigned name"),
};
PPH_ACCESS_ENTRY entries = NULL;
ULONG count = 0;
PhpDevPropFillUInt32Common(
DeviceInfoSet,
DeviceInfoData,
PropertyKey,
Property,
Flags
);
if (PropertyKey == &DEVPKEY_Device_DevNodeStatus)
{
entries = (PPH_ACCESS_ENTRY)devNodeStatus;
count = RTL_NUMBER_OF(devNodeStatus);
}
else if (PropertyKey == &DEVPKEY_Device_Capabilities)
{
entries = (PPH_ACCESS_ENTRY)deviceCapabilities;
count = RTL_NUMBER_OF(deviceCapabilities);
}
else if (PropertyKey == &DEVPKEY_Device_Characteristics ||
PropertyKey == &DEVPKEY_DeviceClass_Characteristics)
{
entries = (PPH_ACCESS_ENTRY)characteristics;
count = RTL_NUMBER_OF(characteristics);
}
else if (PropertyKey == &DEVPKEY_Device_FriendlyNameAttributes)
{
entries = (PPH_ACCESS_ENTRY)nameAttributes;
count = RTL_NUMBER_OF(nameAttributes);
}
if (entries)
{
if (Property->UInt32)
{
PH_FORMAT format[4];
PPH_STRING string;
string = PhGetAccessString(Property->UInt32, entries, count);
PhInitFormatSR(&format[0], string->sr);
PhInitFormatS(&format[1], L" (0x");
PhInitFormatX(&format[2], Property->UInt32);
PhInitFormatS(&format[3], L")");
Property->AsString = PhFormat(format, ARRAYSIZE(format), 10);
PhDereferenceObject(string);
}
else
{
Property->AsString = PhReferenceEmptyString();
}
}
else
{
PH_FORMAT format[2];
PhInitFormatS(&format[0], L"0x");
PhInitFormatIX(&format[1], Property->UInt32);
Property->AsString = PhFormat(format, ARRAYSIZE(format), 10);
}
}
static const PH_DEVICE_PROPERTY_TABLE_ENTRY PhpDeviceItemPropertyTable[] =
{
{ PhDevicePropertyName, &DEVPKEY_NAME, PhpDevPropFillString, 0 },
{ PhDevicePropertyManufacturer, &DEVPKEY_Device_Manufacturer, PhpDevPropFillString, 0 },
{ PhDevicePropertyService, &DEVPKEY_Device_Service, PhpDevPropFillString, 0 },
{ PhDevicePropertyClass, &DEVPKEY_Device_Class, PhpDevPropFillString, 0 },
{ PhDevicePropertyEnumeratorName, &DEVPKEY_Device_EnumeratorName, PhpDevPropFillString, 0 },
{ PhDevicePropertyInstallDate, &DEVPKEY_Device_InstallDate, PhpDevPropFillTimeStamp, 0 },
{ PhDevicePropertyFirstInstallDate, &DEVPKEY_Device_FirstInstallDate, PhpDevPropFillTimeStamp, 0 },
{ PhDevicePropertyLastArrivalDate, &DEVPKEY_Device_LastArrivalDate, PhpDevPropFillTimeStamp, 0 },
{ PhDevicePropertyLastRemovalDate, &DEVPKEY_Device_LastRemovalDate, PhpDevPropFillTimeStamp, 0 },
{ PhDevicePropertyDeviceDesc, &DEVPKEY_Device_DeviceDesc, PhpDevPropFillString, 0 },
{ PhDevicePropertyFriendlyName, &DEVPKEY_Device_FriendlyName, PhpDevPropFillString, 0 },
{ PhDevicePropertyInstanceId, &DEVPKEY_Device_InstanceId, PhpDevPropFillString, 0 },
{ PhDevicePropertyParentInstanceId, &DEVPKEY_Device_Parent, PhpDevPropFillString, 0 },
{ PhDevicePropertyPDOName, &DEVPKEY_Device_PDOName, PhpDevPropFillString, 0 },
{ PhDevicePropertyLocationInfo, &DEVPKEY_Device_LocationInfo, PhpDevPropFillString, 0 },
{ PhDevicePropertyClassGuid, &DEVPKEY_Device_ClassGuid, PhpDevPropFillGuid, 0 },
{ PhDevicePropertyDriver, &DEVPKEY_Device_Driver, PhpDevPropFillString, 0 },
{ PhDevicePropertyDriverVersion, &DEVPKEY_Device_DriverVersion, PhpDevPropFillString, 0 },
{ PhDevicePropertyDriverDate, &DEVPKEY_Device_DriverDate, PhpDevPropFillTimeStamp, 0 },
{ PhDevicePropertyFirmwareDate, &DEVPKEY_Device_FirmwareDate, PhpDevPropFillTimeStamp, 0 },
{ PhDevicePropertyFirmwareVersion, &DEVPKEY_Device_FirmwareVersion, PhpDevPropFillString, 0 },
{ PhDevicePropertyFirmwareRevision, &DEVPKEY_Device_FirmwareRevision, PhpDevPropFillString, 0 },
{ PhDevicePropertyHasProblem, &DEVPKEY_Device_HasProblem, PhpDevPropFillBoolean, 0 },
{ PhDevicePropertyProblemCode, &DEVPKEY_Device_ProblemCode, PhpDevPropFillUInt32, 0 },
{ PhDevicePropertyProblemStatus, &DEVPKEY_Device_ProblemStatus, PhpDevPropFillNTSTATUS, 0 },
{ PhDevicePropertyDevNodeStatus, &DEVPKEY_Device_DevNodeStatus, PhpDevPropFillUInt32Flags, 0 },
{ PhDevicePropertyDevCapabilities, &DEVPKEY_Device_Capabilities, PhpDevPropFillUInt32Flags, 0 },
{ PhDevicePropertyUpperFilters, &DEVPKEY_Device_UpperFilters, PhpDevPropFillStringList, 0 },
{ PhDevicePropertyLowerFilters, &DEVPKEY_Device_LowerFilters, PhpDevPropFillStringList, 0 },
{ PhDevicePropertyHardwareIds, &DEVPKEY_Device_HardwareIds, PhpDevPropFillStringList, 0 },
{ PhDevicePropertyCompatibleIds, &DEVPKEY_Device_CompatibleIds, PhpDevPropFillStringList, 0 },
{ PhDevicePropertyConfigFlags, &DEVPKEY_Device_ConfigFlags, PhpDevPropFillUInt32Hex, 0 },
{ PhDevicePropertyUINumber, &DEVPKEY_Device_UINumber, PhpDevPropFillUInt32, 0 },
{ PhDevicePropertyBusTypeGuid, &DEVPKEY_Device_BusTypeGuid, PhpDevPropFillGuid, 0 },
{ PhDevicePropertyLegacyBusType, &DEVPKEY_Device_LegacyBusType, PhpDevPropFillUInt32, 0 },
{ PhDevicePropertyBusNumber, &DEVPKEY_Device_BusNumber, PhpDevPropFillUInt32, 0 },
{ PhDevicePropertySecurity, &DEVPKEY_Device_Security, PhpDevPropFillBinary, 0 }, // DEVPROP_TYPE_SECURITY_DESCRIPTOR as binary, PhDevicePropertySecuritySDS for string
{ PhDevicePropertySecuritySDS, &DEVPKEY_Device_SecuritySDS, PhpDevPropFillString, 0 },
{ PhDevicePropertyDevType, &DEVPKEY_Device_DevType, PhpDevPropFillUInt32, 0 },
{ PhDevicePropertyExclusive, &DEVPKEY_Device_Exclusive, PhpDevPropFillBoolean, 0 },
{ PhDevicePropertyCharacteristics, &DEVPKEY_Device_Characteristics, PhpDevPropFillUInt32Flags, 0 },
{ PhDevicePropertyAddress, &DEVPKEY_Device_Address, PhpDevPropFillUInt32Hex, 0 },
{ PhDevicePropertyPowerData, &DEVPKEY_Device_PowerData, PhpDevPropFillPowerData, 0 },
{ PhDevicePropertyRemovalPolicy, &DEVPKEY_Device_RemovalPolicy, PhpDevPropFillUInt32, 0 },
{ PhDevicePropertyRemovalPolicyDefault, &DEVPKEY_Device_RemovalPolicyDefault, PhpDevPropFillUInt32, 0 },
{ PhDevicePropertyRemovalPolicyOverride, &DEVPKEY_Device_RemovalPolicyOverride, PhpDevPropFillUInt32, 0 },
{ PhDevicePropertyInstallState, &DEVPKEY_Device_InstallState, PhpDevPropFillUInt32, 0 },
{ PhDevicePropertyLocationPaths, &DEVPKEY_Device_LocationPaths, PhpDevPropFillStringList, 0 },
{ PhDevicePropertyBaseContainerId, &DEVPKEY_Device_BaseContainerId, PhpDevPropFillGuid, 0 },
{ PhDevicePropertyEjectionRelations, &DEVPKEY_Device_EjectionRelations, PhpDevPropFillStringList, 0 },
{ PhDevicePropertyRemovalRelations, &DEVPKEY_Device_RemovalRelations, PhpDevPropFillStringList, 0 },
{ PhDevicePropertyPowerRelations, &DEVPKEY_Device_PowerRelations, PhpDevPropFillStringList, 0 },
{ PhDevicePropertyBusRelations, &DEVPKEY_Device_BusRelations, PhpDevPropFillStringList, 0 },
{ PhDevicePropertyChildren, &DEVPKEY_Device_Children, PhpDevPropFillStringList, 0 },
{ PhDevicePropertySiblings, &DEVPKEY_Device_Siblings, PhpDevPropFillStringList, 0 },
{ PhDevicePropertyTransportRelations, &DEVPKEY_Device_TransportRelations, PhpDevPropFillStringList, 0 },
{ PhDevicePropertyReported, &DEVPKEY_Device_Reported, PhpDevPropFillBoolean, 0 },
{ PhDevicePropertyLegacy, &DEVPKEY_Device_Legacy, PhpDevPropFillBoolean, 0 },
{ PhDevicePropertyContainerId, &DEVPKEY_Device_ContainerId, PhpDevPropFillGuid, 0 },
{ PhDevicePropertyInLocalMachineContainer, &DEVPKEY_Device_InLocalMachineContainer, PhpDevPropFillBoolean, 0 },
{ PhDevicePropertyModel, &DEVPKEY_Device_Model, PhpDevPropFillString, 0 },
{ PhDevicePropertyModelId, &DEVPKEY_Device_ModelId, PhpDevPropFillGuid, 0 },
{ PhDevicePropertyFriendlyNameAttributes, &DEVPKEY_Device_FriendlyNameAttributes, PhpDevPropFillUInt32Flags, 0 },
{ PhDevicePropertyManufacturerAttributes, &DEVPKEY_Device_ManufacturerAttributes, PhpDevPropFillUInt32Hex, 0 },
{ PhDevicePropertyPresenceNotForDevice, &DEVPKEY_Device_PresenceNotForDevice, PhpDevPropFillBoolean, 0 },
{ PhDevicePropertySignalStrength, &DEVPKEY_Device_SignalStrength, PhpDevPropFillInt32, 0 },
{ PhDevicePropertyIsAssociateableByUserAction, &DEVPKEY_Device_IsAssociateableByUserAction, PhpDevPropFillBoolean, 0 },
{ PhDevicePropertyShowInUninstallUI, &DEVPKEY_Device_ShowInUninstallUI, PhpDevPropFillBoolean, 0 },
{ PhDevicePropertyNumaProximityDomain, &DEVPKEY_Device_Numa_Proximity_Domain, PhpDevPropFillUInt32, 0 },
{ PhDevicePropertyDHPRebalancePolicy, &DEVPKEY_Device_DHP_Rebalance_Policy, PhpDevPropFillUInt32, 0 },
{ PhDevicePropertyNumaNode, &DEVPKEY_Device_Numa_Node, PhpDevPropFillUInt32, 0 },
{ PhDevicePropertyBusReportedDeviceDesc, &DEVPKEY_Device_BusReportedDeviceDesc, PhpDevPropFillString, 0 },
{ PhDevicePropertyIsPresent, &DEVPKEY_Device_IsPresent, PhpDevPropFillBoolean, 0 },
{ PhDevicePropertyConfigurationId, &DEVPKEY_Device_ConfigurationId, PhpDevPropFillString, 0 },
{ PhDevicePropertyReportedDeviceIdsHash, &DEVPKEY_Device_ReportedDeviceIdsHash, PhpDevPropFillUInt32, 0 },
{ PhDevicePropertyPhysicalDeviceLocation, &DEVPKEY_Device_PhysicalDeviceLocation, PhpDevPropFillBinary, 0 }, // TODO(jxy-s) look into ACPI 4.0a Specification, section 6.1.6 for AsString formatting
{ PhDevicePropertyBiosDeviceName, &DEVPKEY_Device_BiosDeviceName, PhpDevPropFillString, 0 },
{ PhDevicePropertyDriverProblemDesc, &DEVPKEY_Device_DriverProblemDesc, PhpDevPropFillString, 0 },
{ PhDevicePropertyDebuggerSafe, &DEVPKEY_Device_DebuggerSafe, PhpDevPropFillUInt32, 0 },
{ PhDevicePropertyPostInstallInProgress, &DEVPKEY_Device_PostInstallInProgress, PhpDevPropFillBoolean, 0 },
{ PhDevicePropertyStack, &DEVPKEY_Device_Stack, PhpDevPropFillStringList, 0 },
{ PhDevicePropertyExtendedConfigurationIds, &DEVPKEY_Device_ExtendedConfigurationIds, PhpDevPropFillStringList, 0 },
{ PhDevicePropertyIsRebootRequired, &DEVPKEY_Device_IsRebootRequired, PhpDevPropFillBoolean, 0 },
{ PhDevicePropertyDependencyProviders, &DEVPKEY_Device_DependencyProviders, PhpDevPropFillStringList, 0 },
{ PhDevicePropertyDependencyDependents, &DEVPKEY_Device_DependencyDependents, PhpDevPropFillStringList, 0 },
{ PhDevicePropertySoftRestartSupported, &DEVPKEY_Device_SoftRestartSupported, PhpDevPropFillBoolean, 0 },
{ PhDevicePropertyExtendedAddress, &DEVPKEY_Device_ExtendedAddress, PhpDevPropFillUInt64Hex, 0 },
{ PhDevicePropertyAssignedToGuest, &DEVPKEY_Device_AssignedToGuest, PhpDevPropFillBoolean, 0 },
{ PhDevicePropertyCreatorProcessId, &DEVPKEY_Device_CreatorProcessId, PhpDevPropFillUInt32, 0 },
{ PhDevicePropertyFirmwareVendor, &DEVPKEY_Device_FirmwareVendor, PhpDevPropFillString, 0 },
{ PhDevicePropertySessionId, &DEVPKEY_Device_SessionId, PhpDevPropFillUInt32, 0 },
{ PhDevicePropertyDriverDesc, &DEVPKEY_Device_DriverDesc, PhpDevPropFillString, 0 },
{ PhDevicePropertyDriverInfPath, &DEVPKEY_Device_DriverInfPath, PhpDevPropFillString, 0 },
{ PhDevicePropertyDriverInfSection, &DEVPKEY_Device_DriverInfSection, PhpDevPropFillString, 0 },
{ PhDevicePropertyDriverInfSectionExt, &DEVPKEY_Device_DriverInfSectionExt, PhpDevPropFillString, 0 },
{ PhDevicePropertyMatchingDeviceId, &DEVPKEY_Device_MatchingDeviceId, PhpDevPropFillString, 0 },
{ PhDevicePropertyDriverProvider, &DEVPKEY_Device_DriverProvider, PhpDevPropFillString, 0 },
{ PhDevicePropertyDriverPropPageProvider, &DEVPKEY_Device_DriverPropPageProvider, PhpDevPropFillString, 0 },
{ PhDevicePropertyDriverCoInstallers, &DEVPKEY_Device_DriverCoInstallers, PhpDevPropFillStringList, 0 },
{ PhDevicePropertyResourcePickerTags, &DEVPKEY_Device_ResourcePickerTags, PhpDevPropFillString, 0 },
{ PhDevicePropertyResourcePickerExceptions, &DEVPKEY_Device_ResourcePickerExceptions, PhpDevPropFillString, 0 },
{ PhDevicePropertyDriverRank, &DEVPKEY_Device_DriverRank, PhpDevPropFillUInt32, 0 },
{ PhDevicePropertyDriverLogoLevel, &DEVPKEY_Device_DriverLogoLevel, PhpDevPropFillUInt32, 0 },
{ PhDevicePropertyNoConnectSound, &DEVPKEY_Device_NoConnectSound, PhpDevPropFillBoolean, 0 },
{ PhDevicePropertyGenericDriverInstalled, &DEVPKEY_Device_GenericDriverInstalled, PhpDevPropFillBoolean, 0 },
{ PhDevicePropertyAdditionalSoftwareRequested, &DEVPKEY_Device_AdditionalSoftwareRequested, PhpDevPropFillBoolean, 0 },
{ PhDevicePropertySafeRemovalRequired, &DEVPKEY_Device_SafeRemovalRequired, PhpDevPropFillBoolean, 0 },
{ PhDevicePropertySafeRemovalRequiredOverride, &DEVPKEY_Device_SafeRemovalRequiredOverride, PhpDevPropFillBoolean, 0 },
{ PhDevicePropertyPkgModel, &DEVPKEY_DrvPkg_Model, PhpDevPropFillString, 0 },
{ PhDevicePropertyPkgVendorWebSite, &DEVPKEY_DrvPkg_VendorWebSite, PhpDevPropFillString, 0 },
{ PhDevicePropertyPkgDetailedDescription, &DEVPKEY_DrvPkg_DetailedDescription, PhpDevPropFillString, 0 },
{ PhDevicePropertyPkgDocumentationLink, &DEVPKEY_DrvPkg_DocumentationLink, PhpDevPropFillString, 0 },
{ PhDevicePropertyPkgIcon, &DEVPKEY_DrvPkg_Icon, PhpDevPropFillStringList, 0 },
{ PhDevicePropertyPkgBrandingIcon, &DEVPKEY_DrvPkg_BrandingIcon, PhpDevPropFillStringList, 0 },
{ PhDevicePropertyClassUpperFilters, &DEVPKEY_DeviceClass_UpperFilters, PhpDevPropFillStringList, DEVPROP_FILL_FLAG_CLASS },
{ PhDevicePropertyClassLowerFilters, &DEVPKEY_DeviceClass_LowerFilters, PhpDevPropFillStringList, DEVPROP_FILL_FLAG_CLASS },
{ PhDevicePropertyClassSecurity, &DEVPKEY_DeviceClass_Security, PhpDevPropFillBinary, DEVPROP_FILL_FLAG_CLASS }, // DEVPROP_TYPE_SECURITY_DESCRIPTOR as binary, PhDevicePropertyClassSecuritySDS for string
{ PhDevicePropertyClassSecuritySDS, &DEVPKEY_DeviceClass_SecuritySDS, PhpDevPropFillString, DEVPROP_FILL_FLAG_CLASS },
{ PhDevicePropertyClassDevType, &DEVPKEY_DeviceClass_DevType, PhpDevPropFillUInt32, DEVPROP_FILL_FLAG_CLASS },
{ PhDevicePropertyClassExclusive, &DEVPKEY_DeviceClass_Exclusive, PhpDevPropFillBoolean, DEVPROP_FILL_FLAG_CLASS },
{ PhDevicePropertyClassCharacteristics, &DEVPKEY_DeviceClass_Characteristics, PhpDevPropFillUInt32Flags, DEVPROP_FILL_FLAG_CLASS },
{ PhDevicePropertyClassName, &DEVPKEY_DeviceClass_Name, PhpDevPropFillString, DEVPROP_FILL_FLAG_CLASS },
{ PhDevicePropertyClassClassName, &DEVPKEY_DeviceClass_ClassName, PhpDevPropFillString, DEVPROP_FILL_FLAG_CLASS },
{ PhDevicePropertyClassIcon, &DEVPKEY_DeviceClass_Icon, PhpDevPropFillString, DEVPROP_FILL_FLAG_CLASS },
{ PhDevicePropertyClassClassInstaller, &DEVPKEY_DeviceClass_ClassInstaller, PhpDevPropFillString, DEVPROP_FILL_FLAG_CLASS },
{ PhDevicePropertyClassPropPageProvider, &DEVPKEY_DeviceClass_PropPageProvider, PhpDevPropFillString, DEVPROP_FILL_FLAG_CLASS },
{ PhDevicePropertyClassNoInstallClass, &DEVPKEY_DeviceClass_NoInstallClass, PhpDevPropFillBoolean, DEVPROP_FILL_FLAG_CLASS },
{ PhDevicePropertyClassNoDisplayClass, &DEVPKEY_DeviceClass_NoDisplayClass, PhpDevPropFillBoolean, DEVPROP_FILL_FLAG_CLASS },
{ PhDevicePropertyClassSilentInstall, &DEVPKEY_DeviceClass_SilentInstall, PhpDevPropFillBoolean, DEVPROP_FILL_FLAG_CLASS },
{ PhDevicePropertyClassNoUseClass, &DEVPKEY_DeviceClass_NoUseClass, PhpDevPropFillBoolean, DEVPROP_FILL_FLAG_CLASS },
{ PhDevicePropertyClassDefaultService, &DEVPKEY_DeviceClass_DefaultService, PhpDevPropFillString, DEVPROP_FILL_FLAG_CLASS },
{ PhDevicePropertyClassIconPath, &DEVPKEY_DeviceClass_IconPath, PhpDevPropFillStringList, DEVPROP_FILL_FLAG_CLASS },
{ PhDevicePropertyClassDHPRebalanceOptOut, &DEVPKEY_DeviceClass_DHPRebalanceOptOut, PhpDevPropFillBoolean, DEVPROP_FILL_FLAG_CLASS },
{ PhDevicePropertyClassClassCoInstallers, &DEVPKEY_DeviceClass_ClassCoInstallers, PhpDevPropFillStringList, DEVPROP_FILL_FLAG_CLASS },
{ PhDevicePropertyInterfaceFriendlyName, &DEVPKEY_DeviceInterface_FriendlyName, PhpDevPropFillString, 0 },
{ PhDevicePropertyInterfaceEnabled, &DEVPKEY_DeviceInterface_Enabled, PhpDevPropFillBoolean, 0 },
{ PhDevicePropertyInterfaceClassGuid, &DEVPKEY_DeviceInterface_ClassGuid, PhpDevPropFillGuid, 0 },
{ PhDevicePropertyInterfaceReferenceString, &DEVPKEY_DeviceInterface_ReferenceString, PhpDevPropFillString, 0 },
{ PhDevicePropertyInterfaceRestricted, &DEVPKEY_DeviceInterface_Restricted, PhpDevPropFillBoolean, 0 },
{ PhDevicePropertyInterfaceUnrestrictedAppCapabilities, &DEVPKEY_DeviceInterface_UnrestrictedAppCapabilities, PhpDevPropFillStringList, 0 },
{ PhDevicePropertyInterfaceSchematicName, &DEVPKEY_DeviceInterface_SchematicName, PhpDevPropFillString, 0 },
{ PhDevicePropertyInterfaceClassDefaultInterface, &DEVPKEY_DeviceInterfaceClass_DefaultInterface, PhpDevPropFillString, DEVPROP_FILL_FLAG_CLASS },
{ PhDevicePropertyInterfaceClassName, &DEVPKEY_DeviceInterfaceClass_Name, PhpDevPropFillString, DEVPROP_FILL_FLAG_CLASS },
{ PhDevicePropertyContainerAddress, &DEVPKEY_DeviceContainer_Address, PhpDevPropFillStringOrStringList, 0 },
{ PhDevicePropertyContainerDiscoveryMethod, &DEVPKEY_DeviceContainer_DiscoveryMethod, PhpDevPropFillStringList, 0 },
{ PhDevicePropertyContainerIsEncrypted, &DEVPKEY_DeviceContainer_IsEncrypted, PhpDevPropFillBoolean, 0 },
{ PhDevicePropertyContainerIsAuthenticated, &DEVPKEY_DeviceContainer_IsAuthenticated, PhpDevPropFillBoolean, 0 },
{ PhDevicePropertyContainerIsConnected, &DEVPKEY_DeviceContainer_IsConnected, PhpDevPropFillBoolean, 0 },
{ PhDevicePropertyContainerIsPaired, &DEVPKEY_DeviceContainer_IsPaired, PhpDevPropFillBoolean, 0 },
{ PhDevicePropertyContainerIcon, &DEVPKEY_DeviceContainer_Icon, PhpDevPropFillString, 0 },
{ PhDevicePropertyContainerVersion, &DEVPKEY_DeviceContainer_Version, PhpDevPropFillString, 0 },
{ PhDevicePropertyContainerLastSeen, &DEVPKEY_DeviceContainer_Last_Seen, PhpDevPropFillTimeStamp, 0 },
{ PhDevicePropertyContainerLastConnected, &DEVPKEY_DeviceContainer_Last_Connected, PhpDevPropFillTimeStamp, 0 },
{ PhDevicePropertyContainerIsShowInDisconnectedState, &DEVPKEY_DeviceContainer_IsShowInDisconnectedState, PhpDevPropFillBoolean, 0 },
{ PhDevicePropertyContainerIsLocalMachine, &DEVPKEY_DeviceContainer_IsLocalMachine, PhpDevPropFillBoolean, 0 },
{ PhDevicePropertyContainerMetadataPath, &DEVPKEY_DeviceContainer_MetadataPath, PhpDevPropFillString, 0 },
{ PhDevicePropertyContainerIsMetadataSearchInProgress, &DEVPKEY_DeviceContainer_IsMetadataSearchInProgress, PhpDevPropFillBoolean, 0 },
{ PhDevicePropertyContainerIsMetadataChecksum, &DEVPKEY_DeviceContainer_MetadataChecksum, PhpDevPropFillBinary, 0 },
{ PhDevicePropertyContainerIsNotInterestingForDisplay, &DEVPKEY_DeviceContainer_IsNotInterestingForDisplay, PhpDevPropFillBoolean, 0 },
{ PhDevicePropertyContainerLaunchDeviceStageOnDeviceConnect, &DEVPKEY_DeviceContainer_LaunchDeviceStageOnDeviceConnect, PhpDevPropFillBoolean, 0 },
{ PhDevicePropertyContainerLaunchDeviceStageFromExplorer, &DEVPKEY_DeviceContainer_LaunchDeviceStageFromExplorer, PhpDevPropFillBoolean, 0 },
{ PhDevicePropertyContainerBaselineExperienceId, &DEVPKEY_DeviceContainer_BaselineExperienceId, PhpDevPropFillGuid, 0 },
{ PhDevicePropertyContainerIsDeviceUniquelyIdentifiable, &DEVPKEY_DeviceContainer_IsDeviceUniquelyIdentifiable, PhpDevPropFillBoolean, 0 },
{ PhDevicePropertyContainerAssociationArray, &DEVPKEY_DeviceContainer_AssociationArray, PhpDevPropFillStringList, 0 },
{ PhDevicePropertyContainerDeviceDescription1, &DEVPKEY_DeviceContainer_DeviceDescription1, PhpDevPropFillString, 0 },
{ PhDevicePropertyContainerDeviceDescription2, &DEVPKEY_DeviceContainer_DeviceDescription2, PhpDevPropFillString, 0 },
{ PhDevicePropertyContainerHasProblem, &DEVPKEY_DeviceContainer_HasProblem, PhpDevPropFillBoolean, 0 },
{ PhDevicePropertyContainerIsSharedDevice, &DEVPKEY_DeviceContainer_IsSharedDevice, PhpDevPropFillBoolean, 0 },
{ PhDevicePropertyContainerIsNetworkDevice, &DEVPKEY_DeviceContainer_IsNetworkDevice, PhpDevPropFillBoolean, 0 },
{ PhDevicePropertyContainerIsDefaultDevice, &DEVPKEY_DeviceContainer_IsDefaultDevice, PhpDevPropFillBoolean, 0 },
{ PhDevicePropertyContainerMetadataCabinet, &DEVPKEY_DeviceContainer_MetadataCabinet, PhpDevPropFillString, 0 },
{ PhDevicePropertyContainerRequiresPairingElevation, &DEVPKEY_DeviceContainer_RequiresPairingElevation, PhpDevPropFillBoolean, 0 },
{ PhDevicePropertyContainerExperienceId, &DEVPKEY_DeviceContainer_ExperienceId, PhpDevPropFillGuid, 0 },
{ PhDevicePropertyContainerCategory, &DEVPKEY_DeviceContainer_Category, PhpDevPropFillStringList, 0 },
{ PhDevicePropertyContainerCategoryDescSingular, &DEVPKEY_DeviceContainer_Category_Desc_Singular, PhpDevPropFillStringList, 0 },
{ PhDevicePropertyContainerCategoryDescPlural, &DEVPKEY_DeviceContainer_Category_Desc_Plural, PhpDevPropFillStringList, 0 },
{ PhDevicePropertyContainerCategoryIcon, &DEVPKEY_DeviceContainer_Category_Icon, PhpDevPropFillString, 0 },
{ PhDevicePropertyContainerCategoryGroupDesc, &DEVPKEY_DeviceContainer_CategoryGroup_Desc, PhpDevPropFillStringList, 0 },
{ PhDevicePropertyContainerCategoryGroupIcon, &DEVPKEY_DeviceContainer_CategoryGroup_Icon, PhpDevPropFillString, 0 },
{ PhDevicePropertyContainerPrimaryCategory, &DEVPKEY_DeviceContainer_PrimaryCategory, PhpDevPropFillString, 0 },
{ PhDevicePropertyContainerUnpairUninstall, &DEVPKEY_DeviceContainer_UnpairUninstall, PhpDevPropFillBoolean, 0 },
{ PhDevicePropertyContainerRequiresUninstallElevation, &DEVPKEY_DeviceContainer_RequiresUninstallElevation, PhpDevPropFillBoolean, 0 },
{ PhDevicePropertyContainerDeviceFunctionSubRank, &DEVPKEY_DeviceContainer_DeviceFunctionSubRank, PhpDevPropFillUInt32, 0 },
{ PhDevicePropertyContainerAlwaysShowDeviceAsConnected, &DEVPKEY_DeviceContainer_AlwaysShowDeviceAsConnected, PhpDevPropFillBoolean, 0 },
{ PhDevicePropertyContainerConfigFlags, &DEVPKEY_DeviceContainer_ConfigFlags, PhpDevPropFillUInt32Hex, 0 },
{ PhDevicePropertyContainerPrivilegedPackageFamilyNames, &DEVPKEY_DeviceContainer_PrivilegedPackageFamilyNames, PhpDevPropFillStringList, 0 },
{ PhDevicePropertyContainerCustomPrivilegedPackageFamilyNames, &DEVPKEY_DeviceContainer_CustomPrivilegedPackageFamilyNames, PhpDevPropFillStringList, 0 },
{ PhDevicePropertyContainerIsRebootRequired, &DEVPKEY_DeviceContainer_IsRebootRequired, PhpDevPropFillBoolean, 0 },
{ PhDevicePropertyContainerFriendlyName, &DEVPKEY_DeviceContainer_FriendlyName, PhpDevPropFillString, 0 },
{ PhDevicePropertyContainerManufacturer, &DEVPKEY_DeviceContainer_Manufacturer, PhpDevPropFillString, 0 },
{ PhDevicePropertyContainerModelName, &DEVPKEY_DeviceContainer_ModelName, PhpDevPropFillString, 0 },
{ PhDevicePropertyContainerModelNumber, &DEVPKEY_DeviceContainer_ModelNumber, PhpDevPropFillString, 0 },
{ PhDevicePropertyContainerInstallInProgress, &DEVPKEY_DeviceContainer_InstallInProgress, PhpDevPropFillBoolean, 0 },
{ PhDevicePropertyObjectType, &DEVPKEY_DevQuery_ObjectType, PhpDevPropFillUInt32, 0 },
{ PhDevicePropertyPciDeviceType, &DEVPKEY_PciDevice_DeviceType, PhpDevPropFillPciDeviceType, 0 },
{ PhDevicePropertyPciCurrentSpeedAndMode, &DEVPKEY_PciDevice_CurrentSpeedAndMode, PhpDevPropFillUInt32, 0 },
{ PhDevicePropertyPciBaseClass, &DEVPKEY_PciDevice_BaseClass, PhpDevPropFillUInt32, 0 },
{ PhDevicePropertyPciSubClass, &DEVPKEY_PciDevice_SubClass, PhpDevPropFillUInt32, 0 },
{ PhDevicePropertyPciProgIf, &DEVPKEY_PciDevice_ProgIf, PhpDevPropFillUInt32, 0 },
{ PhDevicePropertyPciCurrentPayloadSize, &DEVPKEY_PciDevice_CurrentPayloadSize, PhpDevPropFillPciDeviceRequestSize, 0 },
{ PhDevicePropertyPciMaxPayloadSize, &DEVPKEY_PciDevice_MaxPayloadSize, PhpDevPropFillPciDeviceRequestSize, 0 },
{ PhDevicePropertyPciMaxReadRequestSize, &DEVPKEY_PciDevice_MaxReadRequestSize, PhpDevPropFillPciDeviceRequestSize, 0 },
{ PhDevicePropertyPciCurrentLinkSpeed, &DEVPKEY_PciDevice_CurrentLinkSpeed, PhpDevPropFillUInt32, 0 },
{ PhDevicePropertyPciCurrentLinkWidth, &DEVPKEY_PciDevice_CurrentLinkWidth, PhpDevPropFillUInt32, 0 },
{ PhDevicePropertyPciMaxLinkSpeed, &DEVPKEY_PciDevice_MaxLinkSpeed, PhpDevPropFillUInt32, 0 },
{ PhDevicePropertyPciMaxLinkWidth, &DEVPKEY_PciDevice_MaxLinkWidth, PhpDevPropFillUInt32, 0 },
{ PhDevicePropertyPciExpressSpecVersion, &DEVPKEY_PciDevice_ExpressSpecVersion, PhpDevPropFillUInt32, 0 },
{ PhDevicePropertyPciInterruptSupport, &DEVPKEY_PciDevice_InterruptSupport, PhpDevPropFillPciDeviceInterruptSupport, 0 },
{ PhDevicePropertyPciInterruptMessageMaximum, &DEVPKEY_PciDevice_InterruptMessageMaximum, PhpDevPropFillUInt32, 0 },
{ PhDevicePropertyPciBarTypes, &DEVPKEY_PciDevice_BarTypes, PhpDevPropFillUInt32, 0 },
{ PhDevicePropertyPciSriovSupport, &DEVPKEY_PciDevice_SriovSupport, PhpDevPropFillPciDeviceSriovSupport, 0 },
{ PhDevicePropertyPciLabel_Id, &DEVPKEY_PciDevice_Label_Id, PhpDevPropFillUInt32, 0 },
{ PhDevicePropertyPciLabel_String, &DEVPKEY_PciDevice_Label_String, PhpDevPropFillUInt32, 0 },
{ PhDevicePropertyPciSerialNumber, &DEVPKEY_PciDevice_SerialNumber, PhpDevPropFillUInt32, 0 },
{ PhDevicePropertyPciExpressCapabilityControl, &DEVPKEY_PciRootBus_PCIExpressCapabilityControl, PhpDevPropFillBoolean, 0 },
{ PhDevicePropertyPciNativeExpressControl, &DEVPKEY_PciRootBus_NativePciExpressControl, PhpDevPropFillBoolean, 0 },
{ PhDevicePropertyPciSystemMsiSupport, &DEVPKEY_PciRootBus_SystemMsiSupport, PhpDevPropFillBoolean, 0 },
{ PhDevicePropertyStoragePortable, &DEVPKEY_Storage_Portable, PhpDevPropFillBoolean, 0 },
{ PhDevicePropertyStorageRemovableMedia, &DEVPKEY_Storage_Removable_Media, PhpDevPropFillBoolean, 0 },
{ PhDevicePropertyStorageSystemCritical, &DEVPKEY_Storage_System_Critical, PhpDevPropFillBoolean, 0 },
{ PhDevicePropertyStorageDiskNumber, &DEVPKEY_Storage_Disk_Number, PhpDevPropFillUInt32, 0 },
{ PhDevicePropertyStoragePartitionNumber, &DEVPKEY_Storage_Partition_Number, PhpDevPropFillUInt32, 0 },
{ PhDevicePropertyGpuLuid, &DEVPKEY_Gpu_Luid, PhpDevPropFillUInt64, 0 },
{ PhDevicePropertyGpuPhysicalAdapterIndex, &DEVPKEY_Gpu_PhysicalAdapterIndex, PhpDevPropFillUInt32, 0 },
};
C_ASSERT(RTL_NUMBER_OF(PhpDeviceItemPropertyTable) == PhMaxDeviceProperty);
#ifdef DEBUG
static BOOLEAN PhpBreakOnDeviceProperty = FALSE;
static PH_DEVICE_PROPERTY_CLASS PhpBreakOnDevicePropertyClass = 0;
#endif
PPH_DEVICE_PROPERTY PhGetDeviceProperty(
_In_ PPH_DEVICE_ITEM Item,
_In_ PH_DEVICE_PROPERTY_CLASS Class
)
{
PPH_DEVICE_PROPERTY prop;
prop = &Item->Properties[Class];
if (!prop->Initialized)
{
const PH_DEVICE_PROPERTY_TABLE_ENTRY* entry;
entry = &PhpDeviceItemPropertyTable[Class];
#ifdef DEBUG
if (PhpBreakOnDeviceProperty && (PhpBreakOnDevicePropertyClass == Class))
__debugbreak();
#endif
entry->Callback(
Item->DeviceInfo->Handle,
&Item->DeviceInfoData,
entry->PropKey,
prop,
entry->CallbackFlags
);
prop->Initialized = TRUE;
}
return prop;
}
BOOLEAN PhLookupDevicePropertyClass(
_In_ const DEVPROPKEY* Key,
_Out_ PPH_DEVICE_PROPERTY_CLASS Class
)
{
for (ULONG i = 0; i < RTL_NUMBER_OF(PhpDeviceItemPropertyTable); i++)
{
const PH_DEVICE_PROPERTY_TABLE_ENTRY* entry = &PhpDeviceItemPropertyTable[i];
if (IsEqualDevPropKey(*Key, *entry->PropKey))
{
*Class = entry->PropClass;
return TRUE;
}
}
*Class = PhMaxDeviceProperty;
return FALSE;
}
HICON PhGetDeviceIcon(
_In_ PPH_DEVICE_ITEM Item,
_In_ PPH_INTEGER_PAIR IconSize
)
{
HICON iconHandle;
if (Item->DeviceInfoData.Interface)
{
// try to give the icon for the parent device
if (Item->Parent && !Item->Parent->DeviceInfoData.Interface)
{
if (!SetupDiLoadDeviceIcon(
Item->Parent->DeviceInfo->Handle,
&Item->Parent->DeviceInfoData.DeviceData,
IconSize->X,
IconSize->Y,
0,
&iconHandle
))
{
iconHandle = NULL;
}
}
else
{
iconHandle = NULL;
}
}
else
{
if (!SetupDiLoadDeviceIcon(
Item->DeviceInfo->Handle,
&Item->DeviceInfoData.DeviceData,
IconSize->X,
IconSize->Y,
0,
&iconHandle
))
{
iconHandle = NULL;
}
}
return iconHandle;
}
ULONG PhpGenerateInstanceIdHash(
_In_ PPH_STRINGREF InstanceId
)
{
return PhHashStringRefEx(InstanceId, TRUE, PH_STRING_HASH_X65599);
}
static int __cdecl PhpDeviceItemSortFunction(
const void* Left,
const void* Right
)
{
PPH_DEVICE_ITEM lhsItem;
PPH_DEVICE_ITEM rhsItem;
lhsItem = *(PPH_DEVICE_ITEM*)Left;
rhsItem = *(PPH_DEVICE_ITEM*)Right;
return uintcmp(lhsItem->InstanceIdHash, rhsItem->InstanceIdHash);
}
static int __cdecl PhpDeviceItemSearchFunction(
const void* Hash,
const void* Item
)
{
PPH_DEVICE_ITEM item;
item = *(PPH_DEVICE_ITEM*)Item;
return uintcmp(PtrToUlong(Hash), item->InstanceIdHash);
}
_Success_(return != NULL)
_Must_inspect_result_
PPH_DEVICE_ITEM PhLookupDeviceItemByHash(
_In_ PPH_DEVICE_TREE Tree,
_In_ ULONG InstanceIdHash
)
{
PPH_DEVICE_ITEM* deviceItem;
deviceItem = bsearch(
UlongToPtr(InstanceIdHash),
Tree->DeviceList->Items,
Tree->DeviceList->Count,
sizeof(PVOID),
PhpDeviceItemSearchFunction
);
return deviceItem ? *deviceItem : NULL;
}
_Success_(return != NULL)
_Must_inspect_result_
PPH_DEVICE_ITEM PhLookupDeviceItem(
_In_ PPH_DEVICE_TREE Tree,
_In_ PPH_STRINGREF InstanceId
)
{
return PhLookupDeviceItemByHash(Tree, PhpGenerateInstanceIdHash(InstanceId));
}
_Success_(return != NULL)
_Must_inspect_result_
PPH_DEVICE_ITEM PhReferenceDeviceItemByHash(
_In_ PPH_DEVICE_TREE Tree,
_In_ ULONG InstanceIdHash
)
{
PPH_DEVICE_ITEM deviceItem;
PhSetReference(&deviceItem, PhLookupDeviceItemByHash(Tree, InstanceIdHash));
return deviceItem;
}
_Success_(return != NULL)
_Must_inspect_result_
PPH_DEVICE_ITEM PhReferenceDeviceItem(
_In_ PPH_DEVICE_TREE Tree,
_In_ PPH_STRINGREF InstanceId
)
{
PPH_DEVICE_ITEM deviceItem;
PhSetReference(&deviceItem, PhLookupDeviceItem(Tree, InstanceId));
return deviceItem;
}
_Success_(return != NULL)
_Must_inspect_result_
PPH_DEVICE_ITEM PhReferenceDeviceItem2(
_In_ PPH_STRINGREF InstanceId
)
{
PPH_DEVICE_TREE tree;
PPH_DEVICE_ITEM deviceItem;
tree = PhReferenceDeviceTree();
PhSetReference(&deviceItem, PhLookupDeviceItem(tree, InstanceId));
PhClearReference(&tree);
return deviceItem;
}
_Function_class_(PH_TYPE_DELETE_PROCEDURE)
VOID PhpDeviceInfoDeleteProcedure(
_In_ PVOID Object,
_In_ ULONG Flags
)
{
PPH_DEVINFO info = Object;
if (info->Handle != INVALID_HANDLE_VALUE)
{
SetupDiDestroyDeviceInfoList(info->Handle);
info->Handle = INVALID_HANDLE_VALUE;
}
}
_Function_class_(PH_TYPE_DELETE_PROCEDURE)
VOID PhpDeviceItemDeleteProcedure(
_In_ PVOID Object,
_In_ ULONG Flags
)
{
PPH_DEVICE_ITEM item = Object;
for (ULONG i = 0; i < ARRAYSIZE(item->Properties); i++)
{
PPH_DEVICE_PROPERTY prop;
prop = &item->Properties[i];
if (prop->Valid)
{
if (prop->Type == PhDevicePropertyTypeString)
{
PhClearReference(&prop->String);
}
else if (prop->Type == PhDevicePropertyTypeStringList)
{
for (ULONG j = 0; j < prop->StringList->Count; j++)
{
PhClearReference(&prop->StringList->Items[j]);
}
PhClearReference(&prop->StringList);
}
else if (prop->Type == PhDevicePropertyTypeBinary)
{
#pragma warning(suppress : 6001) // buffer is valid and initialized
PhFree(prop->Binary.Buffer);
}
}
PhClearReference(&prop->AsString);
}
PhClearReference(&item->InstanceId);
PhClearReference(&item->ParentInstanceId);
PhClearReference(&item->DeviceInfo);
}
_Function_class_(PH_TYPE_DELETE_PROCEDURE)
VOID PhpDeviceTreeDeleteProcedure(
_In_ PVOID Object,
_In_ ULONG Flags
)
{
PPH_DEVICE_TREE tree = Object;
for (ULONG i = 0; i < tree->DeviceList->Count; i++)
{
PPH_DEVICE_ITEM item;
item = tree->DeviceList->Items[i];
PhClearReference(&item);
}
for (ULONG i = 0; i < tree->DeviceInterfaceList->Count; i++)
{
PPH_DEVICE_ITEM item;
item = tree->DeviceInterfaceList->Items[i];
PhClearReference(&item);
}
PhClearReference(&tree->DeviceList);
PhClearReference(&tree->DeviceInterfaceList);
PhClearReference(&tree->DeviceInfo);
}
_Function_class_(PH_TYPE_DELETE_PROCEDURE)
VOID PhpDeviceNotifyDeleteProcedure(
_In_ PVOID Object,
_In_ ULONG Flags
)
{
PPH_DEVICE_NOTIFY notify = Object;
if (notify->Action == PhDeviceNotifyInstanceEnumerated ||
notify->Action == PhDeviceNotifyInstanceStarted ||
notify->Action == PhDeviceNotifyInstanceRemoved)
{
PhClearReference(¬ify->DeviceInstance.InstanceId);
}
}
PPH_DEVICE_ITEM NTAPI PhpAddDeviceItem(
_In_ PPH_DEVICE_TREE Tree,
_In_ PSP_DEVINFO_DATA DeviceInfoData
)
{
PPH_DEVICE_ITEM item;
item = PhCreateObjectZero(sizeof(PH_DEVICE_ITEM), PhDeviceItemType);
item->Tree = Tree;
item->DeviceInfo = PhReferenceObject(Tree->DeviceInfo);
RtlCopyMemory(&item->DeviceInfoData.DeviceData, DeviceInfoData, sizeof(SP_DEVINFO_DATA));
RtlCopyMemory(&item->ClassGuid, &DeviceInfoData->ClassGuid, sizeof(GUID));
//
// Only get the minimum here, the rest will be retrieved later if necessary.
// For convenience later, other frequently referenced items are gotten here too.
//
item->InstanceId = PhGetDeviceProperty(item, PhDevicePropertyInstanceId)->AsString;
if (item->InstanceId)
{
item->InstanceIdHash = PhpGenerateInstanceIdHash(&item->InstanceId->sr);
//
// If this is the root enumerator override some properties.
//
//if (PhEqualStringRef(&item->InstanceId->sr, &RootInstanceId, TRUE))
if (item->InstanceIdHash == RootInstanceIdHash)
{
PPH_DEVICE_PROPERTY prop;
prop = &item->Properties[PhDevicePropertyName];
assert(!prop->Initialized);
prop->AsString = PhGetActiveComputerName();
prop->Initialized = TRUE;
}
PhReferenceObject(item->InstanceId);
}
item->ParentInstanceId = PhGetDeviceProperty(item, PhDevicePropertyParentInstanceId)->AsString;
if (item->ParentInstanceId)
PhReferenceObject(item->ParentInstanceId);
PhGetDeviceProperty(item, PhDevicePropertyProblemCode);
if (item->Properties[PhDevicePropertyProblemCode].Valid)
item->ProblemCode = item->Properties[PhDevicePropertyProblemCode].UInt32;
else
item->ProblemCode = CM_PROB_PHANTOM;
PhGetDeviceProperty(item, PhDevicePropertyDevNodeStatus);
if (item->Properties[PhDevicePropertyDevNodeStatus].Valid)
item->DevNodeStatus = item->Properties[PhDevicePropertyDevNodeStatus].UInt32;
else
item->DevNodeStatus = 0;
PhGetDeviceProperty(item, PhDevicePropertyDevCapabilities);
if (item->Properties[PhDevicePropertyDevCapabilities].Valid)
item->Capabilities = item->Properties[PhDevicePropertyDevCapabilities].UInt32;
else
item->Capabilities = 0;
PhGetDeviceProperty(item, PhDevicePropertyUpperFilters);
PhGetDeviceProperty(item, PhDevicePropertyClassUpperFilters);
if ((item->Properties[PhDevicePropertyUpperFilters].Valid &&
(item->Properties[PhDevicePropertyUpperFilters].StringList->Count > 0)) ||
(item->Properties[PhDevicePropertyClassUpperFilters].Valid &&
(item->Properties[PhDevicePropertyClassUpperFilters].StringList->Count > 0)))
{
item->HasUpperFilters = TRUE;
}
PhGetDeviceProperty(item, PhDevicePropertyLowerFilters);
PhGetDeviceProperty(item, PhDevicePropertyClassLowerFilters);
if ((item->Properties[PhDevicePropertyLowerFilters].Valid &&
(item->Properties[PhDevicePropertyLowerFilters].StringList->Count > 0)) ||
(item->Properties[PhDevicePropertyClassLowerFilters].Valid &&
(item->Properties[PhDevicePropertyClassLowerFilters].StringList->Count > 0)))
{
item->HasLowerFilters = TRUE;
}
PhAddItemList(Tree->DeviceList, item);
return item;
}
PPH_DEVICE_ITEM NTAPI PhpAddDeviceInterfaceItem(
_In_ PPH_DEVICE_TREE Tree,
_In_ PPH_DEVICE_ITEM DeviceItem,
_In_ PSP_DEVICE_INTERFACE_DATA DeviceInterfaceData
)
{
PPH_DEVICE_ITEM item;
PPH_DEVICE_PROPERTY prop;
item = PhCreateObjectZero(sizeof(PH_DEVICE_ITEM), PhDeviceItemType);
item->Tree = Tree;
item->DeviceInfo = PhReferenceObject(Tree->DeviceInfo);
item->DeviceInfoData.Interface = TRUE;
RtlCopyMemory(&item->DeviceInfoData.InterfaceData, DeviceInterfaceData, sizeof(SP_DEVICE_INTERFACE_DATA));
RtlCopyMemory(&item->ClassGuid, &DeviceInterfaceData->InterfaceClassGuid, sizeof(GUID));
item->Parent = DeviceItem;
item->InstanceId = PhGetDeviceProperty(item, PhDevicePropertyInstanceId)->AsString;
if (item->InstanceId)
PhReferenceObject(item->InstanceId);
item->ParentInstanceId = PhReferenceObject(DeviceItem->InstanceId);
item->DeviceInterface = TRUE;
// link the interface as a "child" of the device
DeviceItem->InterfaceCount++;
if (DeviceItem->Child)
{
PPH_DEVICE_ITEM sibling = DeviceItem->Child;
while (sibling->Sibling)
sibling = sibling->Sibling;
sibling->Sibling = item;
}
else
{
DeviceItem->Child = item;
}
// if the interface has no name, override it to be the best possible name
prop = PhGetDeviceProperty(item, PhDevicePropertyName);
if (!prop->AsString)
{
PPH_STRING string;
if (string = PhGetDeviceProperty(item, PhDevicePropertyInterfaceFriendlyName)->AsString)
prop->AsString = PhReferenceObject(string);
else if (string = PhGetDeviceProperty(item, PhDevicePropertyInterfaceClassName)->AsString)
prop->AsString = PhReferenceObject(string);
else if (string = PhGetDeviceProperty(item, PhDevicePropertyInstanceId)->AsString)
prop->AsString = PhReferenceObject(string);
else
prop->AsString = PhFormatGuid(&DeviceInterfaceData->InterfaceClassGuid);
}
PhAddItemList(Tree->DeviceInterfaceList, item);
return item;
}
VOID PhpGetInterfaceClassList(
_Out_ PGUID* InterfaceClassList,
_Out_ PSIZE_T InterfaceClassListCount
)
{
const DEV_OBJECT* objects = NULL;
ULONG objectCount;
PH_ARRAY objectArray;
if (HR_FAILED(PhDevGetObjects(
DevObjectTypeDeviceInterfaceClass,
DevQueryFlagNone,
0,
NULL,
0,
NULL,
&objectCount,
&objects
)) || !objects)
{
*InterfaceClassList = NULL;
*InterfaceClassListCount = 0;
return;
}
PhInitializeArray(&objectArray, sizeof(GUID), objectCount);
for (ULONG i = 0; i < objectCount; i++)
{
GUID interfaceClassGuid;
PH_STRINGREF interfaceClassGuidString;
PhInitializeStringRefLongHint(&interfaceClassGuidString, objects[i].pszObjectId);
if (!NT_SUCCESS(PhStringToGuid(&interfaceClassGuidString, &interfaceClassGuid)))
continue;
PhAddItemArray(&objectArray, &interfaceClassGuid);
}
*InterfaceClassList = PhFinalArrayItems(&objectArray);
*InterfaceClassListCount = PhFinalArrayCount(&objectArray);
PhDevFreeObjects(objectCount, objects);
}
VOID PhpAssociateDeviceInterfaces(
_Inout_ PPH_DEVICE_TREE Tree,
_In_ PPH_DEVICE_ITEM DeviceItem,
_In_ PGUID InterfaceClassList,
_In_ SIZE_T InterfaceClassListCount
)
{
assert(!DeviceItem->DeviceInfoData.Interface);
for (SIZE_T i = 0; i < InterfaceClassListCount; i++)
{
for (ULONG j = 0;; j++)
{
SP_DEVICE_INTERFACE_DATA deviceInterfaceData;
RtlZeroMemory(&deviceInterfaceData, sizeof(SP_DEVICE_INTERFACE_DATA));
deviceInterfaceData.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
if (!SetupDiEnumDeviceInterfaces(
Tree->DeviceInfo->Handle,
&DeviceItem->DeviceInfoData.DeviceData,
&InterfaceClassList[i],
j,
&deviceInterfaceData
))
{
break;
}
PhpAddDeviceInterfaceItem(Tree, DeviceItem, &deviceInterfaceData);
}
}
}
#ifdef DEBUG
VOID PhpCheckDeviceTree(
_In_ PPH_DEVICE_TREE Tree
)
{
static ULONG runawayLimit = 5000;
ULONG count;
assert(!Tree->Root->Sibling);
assert(!Tree->Root->Parent);
for (ULONG i = 0; i < Tree->DeviceList->Count; i++)
{
PPH_DEVICE_ITEM item;
PPH_DEVICE_ITEM other;
item = Tree->DeviceList->Items[i];
// ensure children terminate
other = item->Child;
count = 0;
while (other)
{
other = other->Child;
assert(count < runawayLimit);
}
// ensure siblings terminate
other = item->Sibling;
count = 0;
while (other)
{
other = other->Sibling;
assert(count < runawayLimit);
}
// ensure parents terminate
other = item->Parent;
count = 0;
while (other)
{
other = other->Parent;
assert(count < runawayLimit);
}
}
for (ULONG i = 0; i < Tree->DeviceInterfaceList->Count; i++)
{
PPH_DEVICE_ITEM item;
PPH_DEVICE_ITEM other;
item = Tree->DeviceInterfaceList->Items[i];
// ensure children terminate
other = item->Child;
count = 0;
while (other)
{
other = other->Child;
assert(count < runawayLimit);
}
// ensure siblings terminate
other = item->Sibling;
count = 0;
while (other)
{
other = other->Sibling;
assert(count < runawayLimit);
}
// ensure parents terminate
other = item->Parent;
count = 0;
while (other)
{
other = other->Parent;
assert(count < runawayLimit);
}
}
}
#endif
PPH_DEVICE_TREE PhpCreateDeviceTree(
VOID
)
{
PPH_DEVICE_TREE tree;
PPH_DEVICE_ITEM root;
PGUID interfaceClassList;
SIZE_T interfaceClassListCount;
tree = PhCreateObjectZero(sizeof(PH_DEVICE_TREE), PhDeviceTreeType);
tree->DeviceList = PhCreateList(250);
tree->DeviceInterfaceList = PhCreateList(1024);
tree->DeviceInfo = PhCreateObject(sizeof(PH_DEVINFO), PhpDeviceInfoType);
tree->DeviceInfo->Handle = SetupDiGetClassDevsW(
NULL,
NULL,
NULL,
DIGCF_ALLCLASSES
);
if (tree->DeviceInfo->Handle == INVALID_HANDLE_VALUE)
{
return tree;
}
for (ULONG i = 0;; i++)
{
SP_DEVINFO_DATA deviceInfoData;
RtlZeroMemory(&deviceInfoData, sizeof(SP_DEVINFO_DATA));
deviceInfoData.cbSize = sizeof(SP_DEVINFO_DATA);
if (!SetupDiEnumDeviceInfo(tree->DeviceInfo->Handle, i, &deviceInfoData))
break;
PhpAddDeviceItem(tree, &deviceInfoData);
}
// now add interfaces to the device info set, the return value will be the
// same pointer as the existing handle
(VOID)SetupDiGetClassDevsExW(
NULL,
NULL,
NULL,
DIGCF_ALLCLASSES | DIGCF_DEVICEINTERFACE,
tree->DeviceInfo->Handle,
NULL,
NULL
);
PhpGetInterfaceClassList(&interfaceClassList, &interfaceClassListCount);
// Link the device relations.
root = NULL;
for (ULONG i = 0; i < tree->DeviceList->Count; i++)
{
BOOLEAN found;
PPH_DEVICE_ITEM item;
PPH_DEVICE_ITEM other;
found = FALSE;
item = tree->DeviceList->Items[i];
// for this device item associate any device interfaces
PhpAssociateDeviceInterfaces(tree, item, interfaceClassList, interfaceClassListCount);
for (ULONG j = 0; j < tree->DeviceList->Count; j++)
{
other = tree->DeviceList->Items[j];
if (item == other)
{
continue;
}
if (!other->InstanceId || !item->ParentInstanceId)
{
continue;
}
if (!PhEqualString(other->InstanceId, item->ParentInstanceId, TRUE))
{
continue;
}
found = TRUE;
item->Parent = other;
item->Parent->ChildrenCount++;
if (!other->Child)
{
other->Child = item;
break;
}
other = other->Child;
while (other->Sibling)
{
other = other->Sibling;
}
other->Sibling = item;
break;
}
if (!found)
{
assert(!root);
root = item;
}
}
tree->Root = root;
// sort the list for faster lookups later
qsort(
tree->DeviceList->Items,
tree->DeviceList->Count,
sizeof(PVOID),
PhpDeviceItemSortFunction
);
if (interfaceClassList)
PhFree(interfaceClassList);
#ifdef DEBUG
PhpCheckDeviceTree(tree);
#endif
return tree;
}
PPH_DEVICE_TREE PhpPublishDeviceTree(
VOID
)
{
PPH_DEVICE_TREE newTree;
PPH_DEVICE_TREE oldTree;
newTree = PhpCreateDeviceTree();
PhReferenceObject(newTree);
PhAcquireFastLockExclusive(&PhpDeviceTreeLock);
oldTree = PhpDeviceTree;
PhpDeviceTree = newTree;
PhReleaseFastLockExclusive(&PhpDeviceTreeLock);
PhClearReference(&oldTree);
return newTree;
}
VOID PhpDeviceNotify(
_In_ PSLIST_ENTRY List
)
{
PPH_DEVICE_TREE deviceTree;
PPH_DEVICE_NOTIFY entry;
// We process device notifications in blocks so that bursts of device changes
// don't each trigger a new tree each time. THe internal is determined by the
// interval update and the Service Provider Updated Event. (500ms/1000ms)
if (deviceTree = PhpPublishDeviceTree())
{
while (List)
{
entry = CONTAINING_RECORD(List, PH_DEVICE_NOTIFY, ListEntry);
List = List->Next;
// Restore TypeIndex and Flags.
PhInvokeCallback(PhGetGeneralCallback(GeneralCallbackDeviceNotificationEvent), entry);
PhFreeToFreeList(&PhDeviceNotifyFreeList, entry);
}
}
PhClearReference(&deviceTree);
}
_Function_class_(PCM_NOTIFY_CALLBACK)
ULONG CALLBACK PhpCmNotifyCallback(
_In_ HCMNOTIFICATION hNotify,
_In_opt_ PVOID Context,
_In_ CM_NOTIFY_ACTION Action,
_In_reads_bytes_(EventDataSize) PCM_NOTIFY_EVENT_DATA EventData,
_In_ ULONG EventDataSize
)
{
PPH_DEVICE_NOTIFY entry;
switch (Action)
{
case CM_NOTIFY_ACTION_DEVICEINTERFACEARRIVAL:
{
entry = PhAllocateFromFreeList(&PhDeviceNotifyFreeList);
memset(entry, 0, sizeof(PH_DEVICE_NOTIFY));
entry->Action = PhDeviceNotifyInterfaceArrival;
RtlCopyMemory(&entry->DeviceInterface.ClassGuid, &EventData->u.DeviceInterface.ClassGuid, sizeof(GUID));
InterlockedPushEntrySList(&PhDeviceNotifyListHead, &entry->ListEntry);
}
break;
case CM_NOTIFY_ACTION_DEVICEINTERFACEREMOVAL:
{
entry = PhAllocateFromFreeList(&PhDeviceNotifyFreeList);
memset(entry, 0, sizeof(PH_DEVICE_NOTIFY));
entry->Action = PhDeviceNotifyInterfaceRemoval;
RtlCopyMemory(&entry->DeviceInterface.ClassGuid, &EventData->u.DeviceInterface.ClassGuid, sizeof(GUID));
InterlockedPushEntrySList(&PhDeviceNotifyListHead, &entry->ListEntry);
}
break;
case CM_NOTIFY_ACTION_DEVICEINSTANCEENUMERATED:
{
entry = PhAllocateFromFreeList(&PhDeviceNotifyFreeList);
memset(entry, 0, sizeof(PH_DEVICE_NOTIFY));
entry->Action = PhDeviceNotifyInstanceEnumerated;
entry->DeviceInstance.InstanceId = PhCreateString(EventData->u.DeviceInstance.InstanceId);
InterlockedPushEntrySList(&PhDeviceNotifyListHead, &entry->ListEntry);
}
break;
case CM_NOTIFY_ACTION_DEVICEINSTANCESTARTED:
{
entry = PhAllocateFromFreeList(&PhDeviceNotifyFreeList);
memset(entry, 0, sizeof(PH_DEVICE_NOTIFY));
entry->Action = PhDeviceNotifyInstanceStarted;
entry->DeviceInstance.InstanceId = PhCreateString(EventData->u.DeviceInstance.InstanceId);
InterlockedPushEntrySList(&PhDeviceNotifyListHead, &entry->ListEntry);
}
break;
case CM_NOTIFY_ACTION_DEVICEINSTANCEREMOVED:
{
entry = PhAllocateFromFreeList(&PhDeviceNotifyFreeList);
memset(entry, 0, sizeof(PH_DEVICE_NOTIFY));
entry->Action = PhDeviceNotifyInstanceRemoved;
entry->DeviceInstance.InstanceId = PhCreateString(EventData->u.DeviceInstance.InstanceId);
InterlockedPushEntrySList(&PhDeviceNotifyListHead, &entry->ListEntry);
}
break;
}
return ERROR_SUCCESS;
}
_Function_class_(PH_CALLBACK_FUNCTION)
static VOID NTAPI ServiceProviderUpdatedCallback(
_In_opt_ PVOID Parameter,
_In_opt_ PVOID Context
)
{
PSLIST_ENTRY list;
// drain the list
if (list = RtlInterlockedFlushSList(&PhDeviceNotifyListHead))
{
PhpDeviceNotify(list);
}
}
BOOLEAN PhpDeviceProviderInitialization(
VOID
)
{
CM_NOTIFY_FILTER cmFilter;
if (WindowsVersion < WINDOWS_10 || !PhGetIntegerSetting(L"EnableDeviceSupport"))
return FALSE;
PhDeviceItemType = PhCreateObjectType(L"DeviceItem", 0, PhpDeviceItemDeleteProcedure);
PhDeviceTreeType = PhCreateObjectType(L"DeviceTree", 0, PhpDeviceTreeDeleteProcedure);
PhDeviceNotifyType = PhCreateObjectType(L"DeviceNotify", 0, PhpDeviceNotifyDeleteProcedure);
PhpDeviceInfoType = PhCreateObjectType(L"DeviceInfo", 0, PhpDeviceInfoDeleteProcedure);
PhInitializeSListHead(&PhDeviceNotifyListHead);
PhInitializeFreeList(&PhDeviceNotifyFreeList, sizeof(PH_DEVICE_NOTIFY), 16);
RtlZeroMemory(&cmFilter, sizeof(CM_NOTIFY_FILTER));
cmFilter.cbSize = sizeof(CM_NOTIFY_FILTER);
cmFilter.FilterType = CM_NOTIFY_FILTER_TYPE_DEVICEINSTANCE;
cmFilter.Flags = CM_NOTIFY_FILTER_FLAG_ALL_DEVICE_INSTANCES;
if (CM_Register_Notification(
&cmFilter,
NULL,
PhpCmNotifyCallback,
&PhpDeviceNotification
) != CR_SUCCESS)
{
return FALSE;
}
RtlZeroMemory(&cmFilter, sizeof(CM_NOTIFY_FILTER));
cmFilter.cbSize = sizeof(CM_NOTIFY_FILTER);
cmFilter.FilterType = CM_NOTIFY_FILTER_TYPE_DEVICEINTERFACE;
cmFilter.Flags = CM_NOTIFY_FILTER_FLAG_ALL_INTERFACE_CLASSES;
if (CM_Register_Notification(
&cmFilter,
NULL,
PhpCmNotifyCallback,
&PhpDeviceInterfaceNotification
) != CR_SUCCESS)
{
return FALSE;
}
PhRegisterCallback(
PhGetGeneralCallback(GeneralCallbackServiceProviderUpdatedEvent),
ServiceProviderUpdatedCallback,
NULL,
&ServiceProviderUpdatedRegistration
);
return TRUE;
}
BOOLEAN PhDeviceProviderInitialization(
VOID
)
{
static PH_INITONCE initOnce = PH_INITONCE_INIT;
static BOOLEAN initialized = FALSE;
if (PhBeginInitOnce(&initOnce))
{
initialized = PhpDeviceProviderInitialization();
PhEndInitOnce(&initOnce);
}
return initialized;
}
PPH_DEVICE_TREE PhReferenceDeviceTree(
VOID
)
{
return PhReferenceDeviceTreeEx(FALSE);
}
PPH_DEVICE_TREE PhReferenceDeviceTreeEx(
_In_ BOOLEAN ForceRefresh
)
{
PPH_DEVICE_TREE deviceTree;
PhAcquireFastLockShared(&PhpDeviceTreeLock);
PhSetReference(&deviceTree, PhpDeviceTree);
PhReleaseFastLockShared(&PhpDeviceTreeLock);
if (ForceRefresh || !deviceTree)
{
PhMoveReference(&deviceTree, PhpPublishDeviceTree());
}
return deviceTree;
}
BOOLEAN PhEnumDeviceResources(
_In_ PPH_DEVICE_ITEM Item,
_In_ ULONG LogicalConfig,
_In_ PPH_DEVICE_ENUM_RESOURCES_CALLBACK Callback,
_In_opt_ PVOID Context
)
{
BOOLEAN done;
LOG_CONF logicalConfig;
PVOID buffer;
ULONG length;
if (CM_Get_First_Log_Conf(
&logicalConfig,
Item->DeviceInfoData.DeviceData.DevInst,
LogicalConfig
) != CR_SUCCESS)
return FALSE;
done = FALSE;
buffer = PhAllocate(64);
length = 64;
while (!done)
{
LOG_CONF nextConfig;
ULONG size;
RES_DES deviceResource;
RESOURCEID resourceId;
if (CM_Get_Next_Res_Des(
&deviceResource,
logicalConfig,
ResType_All,
&resourceId,
0
) == CR_SUCCESS)
{
while (!done)
{
RES_DES nextResource;
if (CM_Get_Res_Des_Data_Size(&size, deviceResource, 0) == CR_SUCCESS)
{
if (size > length)
{
buffer = PhReAllocate(buffer, size);
length = size;
}
assert(buffer);
if (CM_Get_Res_Des_Data(deviceResource, buffer, size, 0) == CR_SUCCESS)
{
done = Callback(LogicalConfig, resourceId, buffer, size, Context);
if (done)
break;
}
}
if (CM_Get_Next_Res_Des(
&nextResource,
deviceResource,
ResType_All,
&resourceId,
0
) != CR_SUCCESS)
break;
CM_Free_Res_Des_Handle(deviceResource);
deviceResource = nextResource;
}
}
CM_Free_Res_Des_Handle(deviceResource);
if (done)
break;
if (CM_Get_Next_Log_Conf(&nextConfig, logicalConfig, 0) != CR_SUCCESS)
break;
CM_Free_Log_Conf_Handle(logicalConfig);
logicalConfig = nextConfig;
}
CM_Free_Log_Conf_Handle(logicalConfig);
PhFree(buffer);
return TRUE;
}
================================================
FILE: SystemInformer/extmgr.c
================================================
/*
* Copyright (c) 2022 Winsider Seminars & Solutions, Inc. All rights reserved.
*
* This file is part of System Informer.
*
* Authors:
*
* wj32 2011
*
*/
/*
* The extension manager provides support for generic extensions. It sits directly
* underneath the plugin manager, and has no knowledge of plugin details (how they are
* loaded, plugin information, etc.).
*/
#include
#include
LIST_ENTRY PhEmAppContextListHead = { &PhEmAppContextListHead, &PhEmAppContextListHead };
ULONG PhEmAppContextCount = 0;
PH_EM_OBJECT_TYPE_STATE PhEmObjectTypeState[EmMaximumObjectType] = { 0 };
/**
* Initializes the extension manager module.
*/
VOID PhEmInitialization(
VOID
)
{
ULONG i;
for (i = 0; i < EmMaximumObjectType; i++)
{
InitializeListHead(&PhEmObjectTypeState[i].ExtensionListHead);
}
}
/**
* Initializes an extension application context.
*
* \param AppContext The application context.
* \param AppName The application name.
*/
VOID PhEmInitializeAppContext(
_Out_ PPH_EM_APP_CONTEXT AppContext,
_In_ PCPH_STRINGREF AppName
)
{
AppContext->AppName = *AppName;
memset(AppContext->Extensions, 0, sizeof(AppContext->Extensions));
InsertTailList(&PhEmAppContextListHead, &AppContext->ListEntry);
PhEmAppContextCount++;
}
/**
* Sets the object extension size and callbacks for an object type.
*
* \param AppContext The application context.
* \param ObjectType The type of object for which the extension is being registered.
* \param ExtensionSize The size of the extension, in bytes.
* \param CreateCallback The object creation callback.
* \param DeleteCallback The object deletion callback.
*/
VOID PhEmSetObjectExtension(
_Inout_ PPH_EM_APP_CONTEXT AppContext,
_In_ PH_EM_OBJECT_TYPE ObjectType,
_In_ SIZE_T ExtensionSize,
_In_opt_ PPH_EM_OBJECT_CALLBACK CreateCallback,
_In_opt_ PPH_EM_OBJECT_CALLBACK DeleteCallback
)
{
PPH_EM_OBJECT_TYPE_STATE objectTypeState;
PPH_EM_OBJECT_EXTENSION objectExtension;
objectTypeState = &PhEmObjectTypeState[ObjectType];
objectExtension = AppContext->Extensions[ObjectType];
if (!objectExtension)
{
objectExtension = PhAllocate(sizeof(PH_EM_OBJECT_EXTENSION));
memset(objectExtension, 0, sizeof(PH_EM_OBJECT_EXTENSION));
InsertTailList(&objectTypeState->ExtensionListHead, &objectExtension->ListEntry);
AppContext->Extensions[ObjectType] = objectExtension;
objectExtension->ExtensionSize = ExtensionSize;
objectExtension->ExtensionOffset = objectTypeState->ExtensionOffset;
objectTypeState->ExtensionOffset += ExtensionSize;
}
objectExtension->Callbacks[EmObjectCreate] = CreateCallback;
objectExtension->Callbacks[EmObjectDelete] = DeleteCallback;
}
/**
* Gets the object for an extension.
*
* \param AppContext The application context.
* \param ObjectType The type of object for which an extension has been registered.
* \param Extension The object extension.
*/
PVOID PhEmGetObject(
_In_ PPH_EM_APP_CONTEXT AppContext,
_In_ PH_EM_OBJECT_TYPE ObjectType,
_In_ PVOID Extension
)
{
PPH_EM_OBJECT_EXTENSION objectExtension;
objectExtension = AppContext->Extensions[ObjectType];
if (!objectExtension)
return NULL;
return PTR_SUB_OFFSET(Extension, PhEmObjectTypeState[ObjectType].InitialSize + objectExtension->ExtensionOffset);
}
/**
* Gets the object extension for an object.
*
* \param AppContext The application context.
* \param ObjectType The type of object for which an extension has been registered.
* \param Object The object.
*/
PVOID PhEmGetObjectExtension(
_In_ PPH_EM_APP_CONTEXT AppContext,
_In_ PH_EM_OBJECT_TYPE ObjectType,
_In_ PVOID Object
)
{
PPH_EM_OBJECT_EXTENSION objectExtension;
objectExtension = AppContext->Extensions[ObjectType];
if (!objectExtension)
return NULL;
return PTR_ADD_OFFSET(Object, PhEmObjectTypeState[ObjectType].InitialSize + objectExtension->ExtensionOffset);
}
/**
* Determines the size of an object, including extensions.
*
* \param ObjectType The object type.
* \param InitialSize The initial size of the object.
*/
SIZE_T PhEmGetObjectSize(
_In_ PH_EM_OBJECT_TYPE ObjectType,
_In_ SIZE_T InitialSize
)
{
PhEmObjectTypeState[ObjectType].InitialSize = InitialSize;
return InitialSize + PhEmObjectTypeState[ObjectType].ExtensionOffset;
}
/**
* Invokes callbacks for an object operation.
*
* \param ObjectType The object type.
* \param Object The object.
* \param Operation The operation being performed.
*/
VOID PhEmCallObjectOperation(
_In_ PH_EM_OBJECT_TYPE ObjectType,
_In_ PVOID Object,
_In_ PH_EM_OBJECT_OPERATION Operation
)
{
PPH_EM_OBJECT_TYPE_STATE objectTypeState;
PLIST_ENTRY listEntry;
PPH_EM_OBJECT_EXTENSION objectExtension;
if (PhEmAppContextCount == 0)
return;
objectTypeState = &PhEmObjectTypeState[ObjectType];
listEntry = objectTypeState->ExtensionListHead.Flink;
while (listEntry != &objectTypeState->ExtensionListHead)
{
objectExtension = CONTAINING_RECORD(listEntry, PH_EM_OBJECT_EXTENSION, ListEntry);
if (objectExtension->Callbacks[Operation])
{
objectExtension->Callbacks[Operation](
Object,
ObjectType,
PTR_ADD_OFFSET(Object, objectTypeState->InitialSize + objectExtension->ExtensionOffset)
);
}
listEntry = listEntry->Flink;
}
}
/**
* Parses an application name and sub-ID pair.
*
* \param CompoundId The compound identifier.
* \param AppName A variable which receives the application name.
* \param SubId A variable which receives the sub-ID.
*/
_Success_(return)
BOOLEAN PhEmParseCompoundId(
_In_ PCPH_STRINGREF CompoundId,
_Out_ PPH_STRINGREF AppName,
_Out_ PULONG SubId
)
{
PH_STRINGREF firstPart;
PH_STRINGREF secondPart;
ULONG64 integer;
firstPart = *CompoundId;
if (firstPart.Length == 0)
return FALSE;
if (firstPart.Buffer[0] != L'+')
return FALSE;
PhSkipStringRef(&firstPart, sizeof(WCHAR));
PhSplitStringRefAtChar(&firstPart, L'+', &firstPart, &secondPart);
if (firstPart.Length == 0 || secondPart.Length == 0)
return FALSE;
if (!PhStringToUInt64(&secondPart, 10, &integer))
return FALSE;
if (PhIsLegacyPrefix(&firstPart))
{
PhSkipStringRef(&firstPart, 14 * sizeof(WCHAR));
}
*AppName = firstPart;
*SubId = (ULONG)integer;
return TRUE;
}
================================================
FILE: SystemInformer/findobj.c
================================================
/*
* Copyright (c) 2022 Winsider Seminars & Solutions, Inc. All rights reserved.
*
* This file is part of System Informer.
*
* Authors:
*
* wj32 2010-2016
* dmex 2017-2023
*
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define WM_PH_SEARCH_SHOWDIALOG (WM_APP + 801)
#define WM_PH_SEARCH_FINISHED (WM_APP + 802)
#define WM_PH_SEARCH_SHOWMENU (WM_APP + 803)
static PPH_OBJECT_TYPE PhFindObjectsItemType = NULL;
static HANDLE PhFindObjectsThreadHandle = NULL;
static HWND PhFindObjectsWindowHandle = NULL;
static PH_EVENT PhFindObjectsInitializedEvent = PH_EVENT_INIT;
typedef struct _PH_HANDLE_SEARCH_CONTEXT
{
PH_LAYOUT_MANAGER LayoutManager;
RECT MinimumSize;
HWND WindowHandle;
HWND TreeNewHandle;
HWND TypeWindowHandle;
HWND SearchWindowHandle;
PPH_STRING WindowText;
HFONT TypeWindowFont;
ULONG TreeNewSortColumn;
PH_SORT_ORDER TreeNewSortOrder;
//PPH_HASHTABLE NodeHashtable;
PPH_LIST NodeList;
HANDLE SearchThreadHandle;
BOOLEAN SearchAll;
BOOLEAN SearchStop;
PPH_STRING SearchTypeString;
ULONG_PTR SearchMatchHandle;
PPH_LIST SearchResults;
ULONG SearchResultsAddIndex;
PH_QUEUED_LOCK SearchResultsLock;
} PH_HANDLE_SEARCH_CONTEXT, *PPH_HANDLE_SEARCH_CONTEXT;
typedef enum _PHP_OBJECT_RESULT_TYPE
{
HandleSearchResult,
ModuleSearchResult,
MappedFileSearchResult
} PHP_OBJECT_RESULT_TYPE;
typedef struct _PHP_OBJECT_SEARCH_RESULT
{
HANDLE ProcessId;
PHP_OBJECT_RESULT_TYPE ResultType;
HANDLE Handle;
PVOID Object;
PPH_STRING TypeName;
PPH_STRING ObjectName;
PPH_STRING BestObjectName;
SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX Info;
} PHP_OBJECT_SEARCH_RESULT, *PPHP_OBJECT_SEARCH_RESULT;
typedef enum _PH_HANDLE_OBJECT_TREE_COLUMN_ITEM_NAME
{
PH_OBJECT_SEARCH_TREE_COLUMN_PROCESS,
PH_OBJECT_SEARCH_TREE_COLUMN_TYPE,
PH_OBJECT_SEARCH_TREE_COLUMN_NAME,
PH_OBJECT_SEARCH_TREE_COLUMN_HANDLE,
PH_OBJECT_SEARCH_TREE_COLUMN_OBJECTADDRESS,
PH_OBJECT_SEARCH_TREE_COLUMN_ORIGINALNAME,
PH_OBJECT_SEARCH_TREE_COLUMN_GRANTEDACCESS,
PH_OBJECT_SEARCH_TREE_COLUMN_MAXIMUM
} PH_HANDLE_OBJECT_TREE_COLUMN_ITEM_NAME;
typedef struct _PH_HANDLE_OBJECT_TREE_ROOT_NODE
{
PH_TREENEW_NODE Node;
ULONG64 UniqueId; // used to stabilize sorting
PHP_OBJECT_RESULT_TYPE ResultType;
PVOID HandleObject;
HANDLE Handle;
HANDLE ProcessId;
PPH_STRING ClientIdName;
PPH_STRING TypeNameString;
PPH_STRING ObjectNameString;
PPH_STRING BestObjectName;
PPH_STRING GrantedAccessSymbolicText;
WCHAR HandleString[PH_PTR_STR_LEN_1];
WCHAR ObjectString[PH_PTR_STR_LEN_1];
SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX HandleInfo;
PH_STRINGREF TextCache[PH_OBJECT_SEARCH_TREE_COLUMN_MAXIMUM];
} PH_HANDLE_OBJECT_TREE_ROOT_NODE, *PPH_HANDLE_OBJECT_TREE_ROOT_NODE;
#define SORT_FUNCTION(Column) PhpHandleObjectTreeNewCompare##Column
#define BEGIN_SORT_FUNCTION(Column) static int __cdecl PhpHandleObjectTreeNewCompare##Column( \
_In_ void *_context, \
_In_ const void *_elem1, \
_In_ const void *_elem2 \
) \
{ \
PPH_HANDLE_SEARCH_CONTEXT context = ((PPH_HANDLE_SEARCH_CONTEXT)_context); \
PPH_HANDLE_OBJECT_TREE_ROOT_NODE node1 = *(PPH_HANDLE_OBJECT_TREE_ROOT_NODE*)_elem1; \
PPH_HANDLE_OBJECT_TREE_ROOT_NODE node2 = *(PPH_HANDLE_OBJECT_TREE_ROOT_NODE*)_elem2; \
int sortResult = 0;
#define END_SORT_FUNCTION \
if (sortResult == 0) \
sortResult = uintptrcmp((ULONG_PTR)node1->UniqueId, (ULONG_PTR)node2->UniqueId); \
\
return PhModifySort(sortResult, context->TreeNewSortOrder); \
}
BEGIN_SORT_FUNCTION(Process)
{
sortResult = PhCompareStringWithNullSortOrder(node1->ClientIdName, node2->ClientIdName, context->TreeNewSortOrder, TRUE);
}
END_SORT_FUNCTION
BEGIN_SORT_FUNCTION(Type)
{
sortResult = PhCompareStringWithNullSortOrder(node1->TypeNameString, node2->TypeNameString, context->TreeNewSortOrder, TRUE);
}
END_SORT_FUNCTION
BEGIN_SORT_FUNCTION(Name)
{
sortResult = PhCompareStringWithNullSortOrder(node1->BestObjectName, node2->BestObjectName, context->TreeNewSortOrder, FALSE);
}
END_SORT_FUNCTION
BEGIN_SORT_FUNCTION(Handle)
{
sortResult = uintptrcmp((ULONG_PTR)node1->Handle, (ULONG_PTR)node2->Handle);
}
END_SORT_FUNCTION
BEGIN_SORT_FUNCTION(ObjectAddress)
{
sortResult = uintptrcmp((ULONG_PTR)node1->HandleObject, (ULONG_PTR)node2->HandleObject);
}
END_SORT_FUNCTION
BEGIN_SORT_FUNCTION(OriginalName)
{
sortResult = PhCompareStringWithNullSortOrder(node1->ObjectNameString, node2->ObjectNameString, context->TreeNewSortOrder, FALSE);
}
END_SORT_FUNCTION
BEGIN_SORT_FUNCTION(GrantedAccess)
{
sortResult = PhCompareStringWithNullSortOrder(node1->GrantedAccessSymbolicText, node2->GrantedAccessSymbolicText, context->TreeNewSortOrder, TRUE);
}
END_SORT_FUNCTION
VOID PhpHandleObjectLoadSettingsTreeList(
_Inout_ PPH_HANDLE_SEARCH_CONTEXT Context
)
{
PPH_STRING settings;
settings = PhGetStringSetting(SETTING_FIND_OBJ_TREE_LIST_COLUMNS);
PhCmLoadSettings(Context->TreeNewHandle, &settings->sr);
PhDereferenceObject(settings);
}
VOID PhpHandleObjectSaveSettingsTreeList(
_Inout_ PPH_HANDLE_SEARCH_CONTEXT Context
)
{
PPH_STRING settings;
settings = PhCmSaveSettings(Context->TreeNewHandle);
PhSetStringSetting2(SETTING_FIND_OBJ_TREE_LIST_COLUMNS, &settings->sr);
PhDereferenceObject(settings);
}
//BOOLEAN PhpHandleObjectNodeHashtableEqualFunction(
// _In_ PVOID Entry1,
// _In_ PVOID Entry2
// )
//{
// PPH_HANDLE_OBJECT_TREE_ROOT_NODE node1 = *(PPH_HANDLE_OBJECT_TREE_ROOT_NODE *)Entry1;
// PPH_HANDLE_OBJECT_TREE_ROOT_NODE node2 = *(PPH_HANDLE_OBJECT_TREE_ROOT_NODE *)Entry2;
//
// return node1->HandleObject == node2->HandleObject;
//}
//
//ULONG PhpHandleObjectNodeHashtableHashFunction(
// _In_ PVOID Entry
// )
//{
// return PhHashIntPtr((ULONG_PTR)(*(PPH_HANDLE_OBJECT_TREE_ROOT_NODE*)Entry)->Handle);
//}
VOID PhpDestroyHandleObjectNode(
_In_ PPH_HANDLE_OBJECT_TREE_ROOT_NODE Node
)
{
PhClearReference(&Node->ClientIdName);
PhClearReference(&Node->TypeNameString);
PhClearReference(&Node->ObjectNameString);
PhClearReference(&Node->BestObjectName);
PhClearReference(&Node->GrantedAccessSymbolicText);
PhFree(Node);
}
PPH_HANDLE_OBJECT_TREE_ROOT_NODE PhpAddHandleObjectNode(
_Inout_ PPH_HANDLE_SEARCH_CONTEXT Context,
_In_ HANDLE Handle
)
{
static ULONG64 NextUniqueId = 0;
PPH_HANDLE_OBJECT_TREE_ROOT_NODE handleObjectNode;
handleObjectNode = PhAllocate(sizeof(PH_HANDLE_OBJECT_TREE_ROOT_NODE));
memset(handleObjectNode, 0, sizeof(PH_HANDLE_OBJECT_TREE_ROOT_NODE));
PhInitializeTreeNewNode(&handleObjectNode->Node);
memset(handleObjectNode->TextCache, 0, sizeof(PH_STRINGREF) * PH_OBJECT_SEARCH_TREE_COLUMN_MAXIMUM);
handleObjectNode->Node.TextCache = handleObjectNode->TextCache;
handleObjectNode->Node.TextCacheSize = PH_OBJECT_SEARCH_TREE_COLUMN_MAXIMUM;
handleObjectNode->Handle = Handle;
handleObjectNode->UniqueId = ++NextUniqueId;
//PhAddEntryHashtable(Context->NodeHashtable, &handleObjectNode);
PhAddItemList(Context->NodeList, handleObjectNode);
//TreeNew_NodesStructured(Context->TreeNewHandle);
return handleObjectNode;
}
//PPH_HANDLE_OBJECT_TREE_ROOT_NODE PhpFindHandleObjectNode(
// _In_ PPH_HANDLE_SEARCH_CONTEXT Context,
// _In_ HANDLE Handle
// )
//{
// PH_HANDLE_OBJECT_TREE_ROOT_NODE lookupHandleObjectNode;
// PPH_HANDLE_OBJECT_TREE_ROOT_NODE lookupHandleObjectNodePtr = &lookupHandleObjectNode;
// PPH_HANDLE_OBJECT_TREE_ROOT_NODE *handleObjectNode;
//
// lookupHandleObjectNode.Handle = Handle;
//
// handleObjectNode = (PPH_HANDLE_OBJECT_TREE_ROOT_NODE*)PhFindEntryHashtable(
// Context->NodeHashtable,
// &lookupHandleObjectNodePtr
// );
//
// if (handleObjectNode)
// return *handleObjectNode;
// else
// return NULL;
//}
VOID PhpRemoveHandleObjectNode(
_In_ PPH_HANDLE_SEARCH_CONTEXT Context,
_In_ PPH_HANDLE_OBJECT_TREE_ROOT_NODE Node
)
{
ULONG index = 0;
//PhRemoveEntryHashtable(Context->NodeHashtable, &Node);
if ((index = PhFindItemList(Context->NodeList, Node)) != ULONG_MAX)
{
PhRemoveItemList(Context->NodeList, index);
}
PhpDestroyHandleObjectNode(Node);
TreeNew_NodesStructured(Context->TreeNewHandle);
}
VOID PhpUpdateHandleObjectNode(
_In_ PPH_HANDLE_SEARCH_CONTEXT Context,
_In_ PPH_HANDLE_OBJECT_TREE_ROOT_NODE Node
)
{
memset(Node->TextCache, 0, sizeof(PH_STRINGREF) * PH_OBJECT_SEARCH_TREE_COLUMN_MAXIMUM);
PhInvalidateTreeNewNode(&Node->Node, TN_CACHE_COLOR);
TreeNew_NodesStructured(Context->TreeNewHandle);
}
BOOLEAN NTAPI PhpHandleObjectTreeNewCallback(
_In_ HWND WindowHandle,
_In_ PH_TREENEW_MESSAGE Message,
_In_ PVOID Parameter1,
_In_ PVOID Parameter2,
_In_ PVOID Context
)
{
PPH_HANDLE_SEARCH_CONTEXT context = Context;
PPH_HANDLE_OBJECT_TREE_ROOT_NODE node;
switch (Message)
{
case TreeNewGetChildren:
{
PPH_TREENEW_GET_CHILDREN getChildren = Parameter1;
node = (PPH_HANDLE_OBJECT_TREE_ROOT_NODE)getChildren->Node;
if (!getChildren->Node)
{
static _CoreCrtSecureSearchSortCompareFunction sortFunctions[] =
{
SORT_FUNCTION(Process),
SORT_FUNCTION(Type),
SORT_FUNCTION(Name),
SORT_FUNCTION(Handle),
SORT_FUNCTION(ObjectAddress),
SORT_FUNCTION(OriginalName),
SORT_FUNCTION(GrantedAccess),
};
_CoreCrtSecureSearchSortCompareFunction sortFunction;
static_assert(RTL_NUMBER_OF(sortFunctions) == PH_OBJECT_SEARCH_TREE_COLUMN_MAXIMUM, "SortFunctions must equal maximum.");
if (context->TreeNewSortColumn < PH_OBJECT_SEARCH_TREE_COLUMN_MAXIMUM)
sortFunction = sortFunctions[context->TreeNewSortColumn];
else
sortFunction = NULL;
if (sortFunction)
{
qsort_s(context->NodeList->Items, context->NodeList->Count, sizeof(PVOID), sortFunction, context);
}
getChildren->Children = (PPH_TREENEW_NODE *)context->NodeList->Items;
getChildren->NumberOfChildren = context->NodeList->Count;
}
}
return TRUE;
case TreeNewIsLeaf:
{
PPH_TREENEW_IS_LEAF isLeaf = (PPH_TREENEW_IS_LEAF)Parameter1;
node = (PPH_HANDLE_OBJECT_TREE_ROOT_NODE)isLeaf->Node;
isLeaf->IsLeaf = TRUE;
}
return TRUE;
case TreeNewGetCellText:
{
PPH_TREENEW_GET_CELL_TEXT getCellText = (PPH_TREENEW_GET_CELL_TEXT)Parameter1;
node = (PPH_HANDLE_OBJECT_TREE_ROOT_NODE)getCellText->Node;
switch (getCellText->Id)
{
case PH_OBJECT_SEARCH_TREE_COLUMN_PROCESS:
getCellText->Text = PhGetStringRef(node->ClientIdName);
break;
case PH_OBJECT_SEARCH_TREE_COLUMN_TYPE:
getCellText->Text = PhGetStringRef(node->TypeNameString);
break;
case PH_OBJECT_SEARCH_TREE_COLUMN_NAME:
getCellText->Text = PhGetStringRef(node->BestObjectName);
break;
case PH_OBJECT_SEARCH_TREE_COLUMN_HANDLE:
PhInitializeStringRefLongHint(&getCellText->Text, node->HandleString);
break;
case PH_OBJECT_SEARCH_TREE_COLUMN_OBJECTADDRESS:
PhInitializeStringRefLongHint(&getCellText->Text, node->ObjectString);
break;
case PH_OBJECT_SEARCH_TREE_COLUMN_ORIGINALNAME:
getCellText->Text = PhGetStringRef(node->ObjectNameString);
break;
case PH_OBJECT_SEARCH_TREE_COLUMN_GRANTEDACCESS:
getCellText->Text = PhGetStringRef(node->GrantedAccessSymbolicText);
break;
default:
return FALSE;
}
getCellText->Flags = TN_CACHE;
}
return TRUE;
case TreeNewGetNodeColor:
{
PPH_TREENEW_GET_NODE_COLOR getNodeColor = Parameter1;
node = (PPH_HANDLE_OBJECT_TREE_ROOT_NODE)getNodeColor->Node;
if (!node)
; // Dummy
else if (PhCsUseColorProtectedInheritHandles && FlagOn(node->HandleInfo.HandleAttributes, OBJ_PROTECT_CLOSE) && FlagOn(node->HandleInfo.HandleAttributes, OBJ_INHERIT))
getNodeColor->BackColor = PhCsColorProtectedInheritHandles;
else if (PhCsUseColorProtectedHandles && FlagOn(node->HandleInfo.HandleAttributes, OBJ_PROTECT_CLOSE))
getNodeColor->BackColor = PhCsColorProtectedHandles;
else if (PhCsUseColorInheritHandles && FlagOn(node->HandleInfo.HandleAttributes, OBJ_INHERIT))
getNodeColor->BackColor = PhCsColorInheritHandles;
getNodeColor->Flags = TN_AUTO_FORECOLOR;
}
return TRUE;
case TreeNewSortChanged:
{
PPH_TREENEW_SORT_CHANGED_EVENT sorting = Parameter1;
context->TreeNewSortColumn = sorting->SortColumn;
context->TreeNewSortOrder = sorting->SortOrder;
// Force a rebuild to sort the items.
TreeNew_NodesStructured(WindowHandle);
}
return TRUE;
case TreeNewKeyDown:
{
PPH_TREENEW_KEY_EVENT keyEvent = Parameter1;
switch (keyEvent->VirtualKey)
{
case 'C':
if (GetKeyState(VK_CONTROL) < 0)
SendMessage(context->WindowHandle, WM_COMMAND, ID_OBJECT_COPY, 0);
break;
case VK_DELETE:
SendMessage(context->WindowHandle, WM_COMMAND, ID_OBJECT_CLOSE, 0);
break;
}
}
return TRUE;
case TreeNewLeftDoubleClick:
{
SendMessage(context->WindowHandle, WM_COMMAND, ID_OBJECT_PROPERTIES, 0);
}
return TRUE;
case TreeNewContextMenu:
{
PPH_TREENEW_CONTEXT_MENU contextMenuEvent = Parameter1;
SendMessage(
context->WindowHandle,
WM_COMMAND,
WM_PH_SEARCH_SHOWMENU,
(LPARAM)contextMenuEvent
);
}
return TRUE;
case TreeNewHeaderRightClick:
{
PH_TN_COLUMN_MENU_DATA data;
memset(&data, 0, sizeof(PH_TN_COLUMN_MENU_DATA));
data.TreeNewHandle = WindowHandle;
data.MouseEvent = Parameter1;
data.DefaultSortColumn = PH_OBJECT_SEARCH_TREE_COLUMN_PROCESS;
data.DefaultSortOrder = NoSortOrder;
PhInitializeTreeNewColumnMenuEx(&data, PH_TN_COLUMN_MENU_SHOW_RESET_SORT);
data.Selection = PhShowEMenu(data.Menu, WindowHandle, PH_EMENU_SHOW_LEFTRIGHT,
PH_ALIGN_LEFT | PH_ALIGN_TOP, data.MouseEvent->ScreenLocation.x, data.MouseEvent->ScreenLocation.y);
PhHandleTreeNewColumnMenu(&data);
PhDeleteTreeNewColumnMenu(&data);
}
return TRUE;
}
return FALSE;
}
VOID PhpClearHandleObjectTree(
_In_ PPH_HANDLE_SEARCH_CONTEXT Context
)
{
for (ULONG i = 0; i < Context->NodeList->Count; i++)
PhpDestroyHandleObjectNode(Context->NodeList->Items[i]);
//PhClearHashtable(Context->NodeHashtable);
PhClearList(Context->NodeList);
TreeNew_NodesStructured(Context->TreeNewHandle);
}
PPH_HANDLE_OBJECT_TREE_ROOT_NODE PhpGetSelectedHandleObjectNode(
_In_ PPH_HANDLE_SEARCH_CONTEXT Context
)
{
PPH_HANDLE_OBJECT_TREE_ROOT_NODE handleNode = NULL;
for (ULONG i = 0; i < Context->NodeList->Count; i++)
{
handleNode = Context->NodeList->Items[i];
if (handleNode->Node.Selected)
return handleNode;
}
return NULL;
}
_Success_(return)
BOOLEAN PhpGetSelectedHandleObjectNodes(
_In_ PPH_HANDLE_SEARCH_CONTEXT Context,
_Out_ PPH_HANDLE_OBJECT_TREE_ROOT_NODE **HandleObjectNodes,
_Out_ PULONG NumberOfHandleObjectNodes
)
{
PPH_LIST list = PhCreateList(2);
for (ULONG i = 0; i < Context->NodeList->Count; i++)
{
PPH_HANDLE_OBJECT_TREE_ROOT_NODE node = (PPH_HANDLE_OBJECT_TREE_ROOT_NODE)Context->NodeList->Items[i];
if (node->Node.Selected)
{
PhAddItemList(list, node);
}
}
if (list->Count)
{
*HandleObjectNodes = PhAllocateCopy(list->Items, sizeof(PVOID) * list->Count);
*NumberOfHandleObjectNodes = list->Count;
PhDereferenceObject(list);
return TRUE;
}
PhDereferenceObject(list);
return FALSE;
}
VOID PhpInitializeHandleObjectTree(
_Inout_ PPH_HANDLE_SEARCH_CONTEXT Context
)
{
Context->NodeList = PhCreateList(100);
//Context->NodeHashtable = PhCreateHashtable(
// sizeof(PPH_HANDLE_OBJECT_TREE_ROOT_NODE),
// PhpHandleObjectNodeHashtableEqualFunction,
// PhpHandleObjectNodeHashtableHashFunction,
// 100
// );
PhSetControlTheme(Context->TreeNewHandle, L"explorer");
TreeNew_SetRedraw(Context->TreeNewHandle, FALSE);
TreeNew_SetCallback(Context->TreeNewHandle, PhpHandleObjectTreeNewCallback, Context);
// Default columns
PhAddTreeNewColumn(Context->TreeNewHandle, PH_OBJECT_SEARCH_TREE_COLUMN_PROCESS, TRUE, L"Process", 100, PH_ALIGN_LEFT, 0, 0);
PhAddTreeNewColumn(Context->TreeNewHandle, PH_OBJECT_SEARCH_TREE_COLUMN_TYPE, TRUE, L"Type", 100, PH_ALIGN_LEFT, 1, 0);
PhAddTreeNewColumn(Context->TreeNewHandle, PH_OBJECT_SEARCH_TREE_COLUMN_NAME, TRUE, L"Name", 200, PH_ALIGN_LEFT, 2, 0);
PhAddTreeNewColumn(Context->TreeNewHandle, PH_OBJECT_SEARCH_TREE_COLUMN_HANDLE, TRUE, L"Handle", 80, PH_ALIGN_LEFT, 3, 0);
// Customizable columns
PhAddTreeNewColumn(Context->TreeNewHandle, PH_OBJECT_SEARCH_TREE_COLUMN_OBJECTADDRESS, FALSE, L"Object address", 80, PH_ALIGN_LEFT, ULONG_MAX, 0);
PhAddTreeNewColumn(Context->TreeNewHandle, PH_OBJECT_SEARCH_TREE_COLUMN_ORIGINALNAME, FALSE, L"Original name", 200, PH_ALIGN_LEFT, ULONG_MAX, 0);
PhAddTreeNewColumn(Context->TreeNewHandle, PH_OBJECT_SEARCH_TREE_COLUMN_GRANTEDACCESS, FALSE, L"Granted access", 200, PH_ALIGN_LEFT, ULONG_MAX, 0);
TreeNew_SetTriState(Context->TreeNewHandle, TRUE);
TreeNew_SetRedraw(Context->TreeNewHandle, TRUE);
PhpHandleObjectLoadSettingsTreeList(Context);
}
VOID PhpDeleteHandleObjectTree(
_In_ PPH_HANDLE_SEARCH_CONTEXT Context
)
{
PhpHandleObjectSaveSettingsTreeList(Context);
for (ULONG i = 0; i < Context->NodeList->Count; i++)
{
PhpDestroyHandleObjectNode(Context->NodeList->Items[i]);
}
//PhDereferenceObject(Context->NodeHashtable);
PhDereferenceObject(Context->NodeList);
}
VOID PhpInitializeFindObjMenu(
_In_ PPH_EMENU Menu,
_In_ PPH_HANDLE_OBJECT_TREE_ROOT_NODE *Results,
_In_ ULONG NumberOfResults
)
{
BOOLEAN allCanBeClosed = TRUE;
ULONG i;
if (NumberOfResults == 1)
{
PH_HANDLE_ITEM_INFO info;
info.ProcessId = Results[0]->ProcessId;
info.Handle = Results[0]->Handle;
info.TypeName = Results[0]->TypeNameString;
info.BestObjectName = Results[0]->BestObjectName;
PhInsertHandleObjectPropertiesEMenuItems(Menu, ID_OBJECT_PROPERTIES, FALSE, &info);
}
else
{
PhSetFlagsAllEMenuItems(Menu, PH_EMENU_DISABLED, PH_EMENU_DISABLED);
PhEnableEMenuItem(Menu, ID_OBJECT_COPY, TRUE);
}
for (i = 0; i < NumberOfResults; i++)
{
if (Results[i]->ResultType != HandleSearchResult)
{
allCanBeClosed = FALSE;
break;
}
}
PhEnableEMenuItem(Menu, ID_OBJECT_CLOSE, allCanBeClosed);
}
static int __cdecl PhpStringObjectTypeCompare(
_In_ const void *elem1,
_In_ const void *elem2
)
{
PPH_STRING entry1 = *(PPH_STRING *)elem1;
PPH_STRING entry2 = *(PPH_STRING *)elem2;
return PhCompareString(entry1, entry2, TRUE);
}
VOID PhpUpdateDropdownThemeMetrics(
_In_ PPH_HANDLE_SEARCH_CONTEXT Context,
_In_ LONG WindowDpi
)
{
LONG maxLength;
HDC comboDc;
HFONT oldFont;
INT count;
maxLength = 0;
comboDc = GetDC(Context->TypeWindowHandle);
oldFont = SelectFont(comboDc, Context->TypeWindowFont);
count = ComboBox_GetCount(Context->TypeWindowHandle);
for (INT i = 0; i < count; i++)
{
PPH_STRING entry = PhGetComboBoxString(Context->TypeWindowHandle, i);
SIZE textSize;
if (GetTextExtentPoint32(comboDc, entry->Buffer, (ULONG)entry->Length / sizeof(WCHAR), &textSize))
{
if (textSize.cx > maxLength)
maxLength = textSize.cx;
}
PhDereferenceObject(entry);
}
if (oldFont)
SelectFont(comboDc, oldFont);
ReleaseDC(Context->TypeWindowHandle, comboDc);
// Add some padding for the vertical scroll bar and margins.
maxLength += PhGetSystemMetrics(SM_CXVSCROLL, WindowDpi) * 2;
if (maxLength)
{
SendMessage(Context->TypeWindowHandle, CB_SETDROPPEDWIDTH, maxLength, 0);
}
}
VOID PhpPopulateObjectTypes(
_In_ PPH_HANDLE_SEARCH_CONTEXT Context
)
{
POBJECT_TYPES_INFORMATION objectTypes;
POBJECT_TYPE_INFORMATION objectType;
PPH_LIST objectTypeList;
objectTypeList = PhCreateList(100);
// Add a custom object type for searching all objects.
ComboBox_AddString(Context->TypeWindowHandle, L"Everything");
ComboBox_SetCurSel(Context->TypeWindowHandle, 0);
// Enumerate the available object types.
if (NT_SUCCESS(PhEnumObjectTypes(&objectTypes)))
{
objectType = PH_FIRST_OBJECT_TYPE(objectTypes);
for (ULONG i = 0; i < objectTypes->NumberOfTypes; i++)
{
PhAddItemList(objectTypeList, PhCreateStringFromUnicodeString(&objectType->TypeName));
objectType = PH_NEXT_OBJECT_TYPE(objectType);
}
PhFree(objectTypes);
}
// Sort the object types.
qsort(objectTypeList->Items, objectTypeList->Count, sizeof(PVOID), PhpStringObjectTypeCompare);
// Set the font, add the dropdown entries and adjust the dropdown width.
{
LONG maxLength;
HDC comboDc;
HFONT oldFont;
maxLength = 0;
comboDc = GetDC(Context->TypeWindowHandle);
oldFont = SelectFont(comboDc, Context->TypeWindowFont);
SetWindowFont(Context->TypeWindowHandle, Context->TypeWindowFont, TRUE);
for (ULONG i = 0; i < objectTypeList->Count; i++)
{
PPH_STRING entry = objectTypeList->Items[i];
SIZE textSize;
if (GetTextExtentPoint32(comboDc, entry->Buffer, (ULONG)entry->Length / sizeof(WCHAR), &textSize))
{
if (textSize.cx > maxLength)
maxLength = textSize.cx;
}
ComboBox_AddString(Context->TypeWindowHandle, PhGetString(objectTypeList->Items[i]));
PhDereferenceObject(objectTypeList->Items[i]);
}
if (oldFont)
SelectFont(comboDc, oldFont);
ReleaseDC(Context->TypeWindowHandle, comboDc);
maxLength += PhGetSystemMetrics(SM_CXVSCROLL, PhGetWindowDpi(Context->TypeWindowHandle)) * 2;
if (maxLength)
{
SendMessage(Context->TypeWindowHandle, CB_SETDROPPEDWIDTH, maxLength, 0);
}
}
PhDereferenceObject(objectTypeList);
}
VOID PhpFindObjectAddResultEntries(
_In_ PPH_HANDLE_SEARCH_CONTEXT Context
)
{
ULONG i;
PhAcquireQueuedLockExclusive(&Context->SearchResultsLock);
if (Context->SearchResults->Count == 0 || Context->SearchResultsAddIndex == Context->SearchResults->Count)
{
PhReleaseQueuedLockExclusive(&Context->SearchResultsLock);
return;
}
TreeNew_SetRedraw(Context->TreeNewHandle, FALSE);
for (i = Context->SearchResultsAddIndex; i < Context->SearchResults->Count; i++)
{
PPHP_OBJECT_SEARCH_RESULT searchResult = Context->SearchResults->Items[i];
CLIENT_ID clientId;
PPH_PROCESS_ITEM processItem;
PPH_HANDLE_OBJECT_TREE_ROOT_NODE objectNode;
clientId.UniqueProcess = searchResult->ProcessId;
clientId.UniqueThread = NULL;
processItem = PhReferenceProcessItem(clientId.UniqueProcess);
objectNode = PhpAddHandleObjectNode(Context, searchResult->Handle);
objectNode->ProcessId = searchResult->ProcessId;
objectNode->ResultType = searchResult->ResultType;
objectNode->ClientIdName = PhGetClientIdNameEx(&clientId, processItem ? processItem->ProcessName : NULL);
objectNode->TypeNameString = searchResult->TypeName;
objectNode->ObjectNameString = searchResult->ObjectName;
objectNode->BestObjectName = searchResult->BestObjectName;
objectNode->HandleInfo = searchResult->Info;
PhPrintPointer(objectNode->HandleString, searchResult->Handle);
if (searchResult->Object)
{
objectNode->HandleObject = searchResult->Object;
PhPrintPointer(objectNode->ObjectString, searchResult->Object);
}
if (searchResult->Info.GrantedAccess != 0)
{
PPH_ACCESS_ENTRY accessEntries;
ULONG numberOfAccessEntries;
if (PhGetAccessEntries(PhGetStringOrEmpty(searchResult->TypeName), &accessEntries, &numberOfAccessEntries))
{
objectNode->GrantedAccessSymbolicText = PhGetAccessString(searchResult->Info.GrantedAccess, accessEntries, numberOfAccessEntries);
PhFree(accessEntries);
}
if (objectNode->GrantedAccessSymbolicText)
{
WCHAR grantedAccessString[PH_PTR_STR_LEN_1];
PhPrintPointer(grantedAccessString, UlongToPtr(searchResult->Info.GrantedAccess));
PhMoveReference(&objectNode->GrantedAccessSymbolicText, PhFormatString(
L"%s (%s)",
PhGetString(objectNode->GrantedAccessSymbolicText),
grantedAccessString
));
}
}
if (processItem)
{
PhDereferenceObject(processItem);
}
}
TreeNew_NodesStructured(Context->TreeNewHandle);
TreeNew_SetRedraw(Context->TreeNewHandle, TRUE);
Context->SearchResultsAddIndex = i;
PhReleaseQueuedLockExclusive(&Context->SearchResultsLock);
}
VOID PhpFindObjectClearResultEntries(
_In_ PPH_HANDLE_SEARCH_CONTEXT Context
)
{
PhpClearHandleObjectTree(Context);
Context->SearchResultsAddIndex = 0;
for (ULONG i = 0; i < Context->SearchResults->Count; i++)
PhFree(Context->SearchResults->Items[i]);
PhClearList(Context->SearchResults);
}
static BOOLEAN MatchSearchString(
_In_ PPH_HANDLE_SEARCH_CONTEXT Context,
_In_ PPH_STRINGREF Input
)
{
return PhSearchControlMatch(Context->SearchMatchHandle, Input);
}
static BOOLEAN MatchTypeString(
_In_ PPH_HANDLE_SEARCH_CONTEXT Context,
_In_ PPH_STRINGREF Input
)
{
if (PhEqualString2(Context->SearchTypeString, L"Everything", FALSE))
return TRUE;
return PhEqualStringRef(Input, &Context->SearchTypeString->sr, TRUE);
}
typedef struct _SEARCH_HANDLE_CONTEXT
{
PPH_HANDLE_SEARCH_CONTEXT WindowContext;
BOOLEAN NeedToFree;
PSYSTEM_HANDLE_TABLE_ENTRY_INFO_EX HandleInfo;
HANDLE ProcessHandle;
} SEARCH_HANDLE_CONTEXT, *PSEARCH_HANDLE_CONTEXT;
_Function_class_(USER_THREAD_START_ROUTINE)
static NTSTATUS NTAPI SearchHandleFunction(
_In_ PVOID Parameter
)
{
PSEARCH_HANDLE_CONTEXT handleContext = Parameter;
PPH_HANDLE_SEARCH_CONTEXT context = handleContext->WindowContext;
PPH_STRING typeName;
PPH_STRING objectName;
PPH_STRING bestObjectName;
if (NT_SUCCESS(PhGetHandleInformation(
handleContext->ProcessHandle,
handleContext->HandleInfo->HandleValue,
handleContext->HandleInfo->ObjectTypeIndex,
NULL,
&typeName,
&objectName,
&bestObjectName
)))
{
PPH_STRING upperObjectName;
PPH_STRING upperBestObjectName;
PPH_STRING upperTypeName;
upperObjectName = PhUpperString(objectName);
upperBestObjectName = PhUpperString(bestObjectName);
upperTypeName = PhUpperString(typeName);
if (((context->SearchAll || MatchSearchString(context, &upperObjectName->sr) || MatchSearchString(context, &upperBestObjectName->sr)) &&
MatchTypeString(context, &upperTypeName->sr)) ||
PhSearchControlMatchPointer(context->SearchMatchHandle, handleContext->HandleInfo->Object) ||
PhSearchControlMatchPointer(context->SearchMatchHandle, handleContext->HandleInfo->HandleValue))
{
PPHP_OBJECT_SEARCH_RESULT searchResult;
searchResult = PhAllocateZero(sizeof(PHP_OBJECT_SEARCH_RESULT));
searchResult->ProcessId = handleContext->HandleInfo->UniqueProcessId;
searchResult->ResultType = HandleSearchResult;
searchResult->Object = handleContext->HandleInfo->Object;
searchResult->Handle = handleContext->HandleInfo->HandleValue;
searchResult->TypeName = typeName;
searchResult->ObjectName = objectName;
searchResult->BestObjectName = bestObjectName;
searchResult->Info = *handleContext->HandleInfo;
PhAcquireQueuedLockExclusive(&context->SearchResultsLock);
PhAddItemList(context->SearchResults, searchResult);
PhReleaseQueuedLockExclusive(&context->SearchResultsLock);
}
else
{
PhDereferenceObject(typeName);
PhDereferenceObject(objectName);
PhDereferenceObject(bestObjectName);
}
PhDereferenceObject(upperTypeName);
PhDereferenceObject(upperBestObjectName);
PhDereferenceObject(upperObjectName);
}
if (handleContext->NeedToFree)
PhFree(handleContext);
return STATUS_SUCCESS;
}
typedef struct _SEARCH_MODULE_CONTEXT
{
PPH_HANDLE_SEARCH_CONTEXT WindowContext;
HANDLE ProcessId;
} SEARCH_MODULE_CONTEXT, *PSEARCH_MODULE_CONTEXT;
_Function_class_(PH_ENUM_GENERIC_MODULES_CALLBACK)
static BOOLEAN NTAPI EnumModulesCallback(
_In_ PPH_MODULE_INFO Module,
_In_ PSEARCH_MODULE_CONTEXT Context
)
{
PPH_HANDLE_SEARCH_CONTEXT context = Context->WindowContext;
PPH_STRING filenameWin32;
PPH_STRING upperFileName;
PPH_STRING upperOriginalFileName;
filenameWin32 = PhGetFileName(Module->FileName);
upperFileName = PhUpperString(filenameWin32);
upperOriginalFileName = PhUpperString(Module->FileName);
if ((
context->SearchAll ||
MatchSearchString(context, &upperFileName->sr) ||
MatchSearchString(context, &upperOriginalFileName->sr)) ||
PhSearchControlMatchPointer(context->SearchMatchHandle, Module->BaseAddress))
{
PPHP_OBJECT_SEARCH_RESULT searchResult;
PCWSTR typeName;
switch (Module->Type)
{
case PH_MODULE_TYPE_MAPPED_FILE:
typeName = L"Mapped file";
break;
case PH_MODULE_TYPE_MAPPED_IMAGE:
typeName = L"Mapped image";
break;
default:
typeName = L"DLL";
break;
}
searchResult = PhAllocateZero(sizeof(PHP_OBJECT_SEARCH_RESULT));
searchResult->ProcessId = Context->ProcessId;
searchResult->ResultType = (Module->Type == PH_MODULE_TYPE_MAPPED_FILE || Module->Type == PH_MODULE_TYPE_MAPPED_IMAGE) ? MappedFileSearchResult : ModuleSearchResult;
searchResult->Handle = Module->BaseAddress;
searchResult->TypeName = PhCreateString(typeName);
PhSetReference(&searchResult->BestObjectName, filenameWin32);
PhSetReference(&searchResult->ObjectName, Module->FileName);
PhAcquireQueuedLockExclusive(&context->SearchResultsLock);
PhAddItemList(context->SearchResults, searchResult);
PhReleaseQueuedLockExclusive(&context->SearchResultsLock);
}
PhDereferenceObject(upperOriginalFileName);
PhDereferenceObject(upperFileName);
PhDereferenceObject(filenameWin32);
return TRUE;
}
_Function_class_(USER_THREAD_START_ROUTINE)
NTSTATUS PhpFindObjectsThreadStart(
_In_ PVOID Parameter
)
{
PPH_HANDLE_SEARCH_CONTEXT context = Parameter;
NTSTATUS status = STATUS_SUCCESS;
PSYSTEM_HANDLE_INFORMATION_EX handles;
PPH_HASHTABLE processHandleHashtable;
PVOID processes;
PSYSTEM_PROCESS_INFORMATION process;
ULONG i;
// Refuse to search with no filter.
if (!context->SearchMatchHandle && !context->SearchAll)
{
goto CleanupExit;
}
status = PhEnumHandlesEx(&handles);
if (NT_SUCCESS(status))
{
static PH_INITONCE initOnce = PH_INITONCE_INIT;
static USHORT fileObjectTypeIndex = USHRT_MAX;
BOOLEAN useWorkQueue = FALSE;
PH_WORK_QUEUE workQueue;
processHandleHashtable = PhCreateSimpleHashtable(8);
if (KsiLevel() < KphLevelMed)
{
useWorkQueue = TRUE;
PhInitializeWorkQueue(&workQueue, 1, 20, 1000);
if (PhBeginInitOnce(&initOnce))
{
fileObjectTypeIndex = (USHORT)PhGetObjectTypeNumberZ(L"File");
PhEndInitOnce(&initOnce);
}
}
for (i = 0; i < handles->NumberOfHandles; i++)
{
PSYSTEM_HANDLE_TABLE_ENTRY_INFO_EX handleInfo = &handles->Handles[i];
HANDLE processHandle;
// Don't continue if the user requested cancellation.
if (context->SearchStop)
break;
// Open a handle to the process if we don't already have one.
if (!(processHandle = PhFindItemSimpleHashtable2(processHandleHashtable, handleInfo->UniqueProcessId)))
{
if (NT_SUCCESS(PhOpenProcess(&processHandle, PROCESS_DUP_HANDLE | PROCESS_QUERY_INFORMATION, handleInfo->UniqueProcessId)))
{
PhAddItemSimpleHashtable(processHandleHashtable, handleInfo->UniqueProcessId, processHandle);
}
else
{
if (NT_SUCCESS(PhOpenProcess(&processHandle, PROCESS_QUERY_INFORMATION, handleInfo->UniqueProcessId)))
{
PhAddItemSimpleHashtable(processHandleHashtable, handleInfo->UniqueProcessId, processHandle);
}
else
{
continue;
}
}
}
if (useWorkQueue && handleInfo->ObjectTypeIndex == fileObjectTypeIndex)
{
PSEARCH_HANDLE_CONTEXT searchHandleContext;
searchHandleContext = PhAllocateZero(sizeof(SEARCH_HANDLE_CONTEXT));
searchHandleContext->WindowContext = context;
searchHandleContext->NeedToFree = TRUE;
searchHandleContext->HandleInfo = handleInfo;
searchHandleContext->ProcessHandle = processHandle;
PhQueueItemWorkQueue(&workQueue, SearchHandleFunction, searchHandleContext);
}
else
{
SEARCH_HANDLE_CONTEXT searchHandleContext;
searchHandleContext.WindowContext = context;
searchHandleContext.NeedToFree = FALSE;
searchHandleContext.HandleInfo = handleInfo;
searchHandleContext.ProcessHandle = processHandle;
SearchHandleFunction(&searchHandleContext);
}
}
if (useWorkQueue)
{
PhWaitForWorkQueue(&workQueue);
PhDeleteWorkQueue(&workQueue);
}
{
PPH_KEY_VALUE_PAIR entry;
i = 0;
while (PhEnumHashtable(processHandleHashtable, &entry, &i))
{
if (entry->Value)
{
status = NtClose(entry->Value);
if (!NT_SUCCESS(status))
{
PhShowStatus(NULL, L"Unidentified third party object.", status, 0);
}
}
}
}
PhDereferenceObject(processHandleHashtable);
PhFree(handles);
}
if (context->SearchStop)
goto CleanupExit;
if (PhEqualString2(context->SearchTypeString, L"File", TRUE) ||
PhEqualString2(context->SearchTypeString, L"Everything", FALSE))
{
if (NT_SUCCESS(PhEnumProcesses(&processes)))
{
process = PH_FIRST_PROCESS(processes);
do
{
SEARCH_MODULE_CONTEXT searchModuleContext;
if (context->SearchStop)
break;
searchModuleContext.WindowContext = context;
searchModuleContext.ProcessId = process->UniqueProcessId;
PhEnumGenericModules(
process->UniqueProcessId,
NULL,
PH_ENUM_GENERIC_MAPPED_FILES | PH_ENUM_GENERIC_MAPPED_IMAGES,
EnumModulesCallback,
&searchModuleContext
);
} while (process = PH_NEXT_PROCESS(process));
PhFree(processes);
}
}
CleanupExit:
PostMessage(context->WindowHandle, WM_PH_SEARCH_FINISHED, status, 0);
PhDereferenceObject(context);
return STATUS_SUCCESS;
}
_Function_class_(PH_TYPE_DELETE_PROCEDURE)
VOID PhpFindObjectsDeleteProcedure(
_In_ PVOID Object,
_In_ ULONG Flags
)
{
PPH_HANDLE_SEARCH_CONTEXT context = Object;
if (context->SearchResults)
{
for (ULONG i = 0; i < context->SearchResults->Count; i++)
PhFree(context->SearchResults->Items[i]);
PhClearList(context->SearchResults);
PhClearReference(&context->SearchResults);
}
PhClearReference(&context->SearchTypeString);
}
PPH_HANDLE_SEARCH_CONTEXT PhCreateFindObjectContext(
VOID
)
{
static PH_INITONCE initOnce = PH_INITONCE_INIT;
PPH_HANDLE_SEARCH_CONTEXT context;
if (PhBeginInitOnce(&initOnce))
{
PhFindObjectsItemType = PhCreateObjectType(L"FindObjectsItem", 0, PhpFindObjectsDeleteProcedure);
PhEndInitOnce(&initOnce);
}
context = PhCreateObject(sizeof(PH_HANDLE_SEARCH_CONTEXT), PhFindObjectsItemType);
memset(context, 0, sizeof(PH_HANDLE_SEARCH_CONTEXT));
return context;
}
_Function_class_(PH_SEARCHCONTROL_CALLBACK)
VOID NTAPI PhFindObjectsSearchControlCallback(
_In_ ULONG_PTR MatchHandle,
_In_opt_ PVOID Context
)
{
PPH_HANDLE_SEARCH_CONTEXT context = Context;
assert(context);
context->SearchMatchHandle = MatchHandle;
}
INT_PTR CALLBACK PhFindObjectsDlgProc(
_In_ HWND hwndDlg,
_In_ UINT uMsg,
_In_ WPARAM wParam,
_In_ LPARAM lParam
)
{
PPH_HANDLE_SEARCH_CONTEXT context;
if (uMsg == WM_INITDIALOG)
{
context = PhCreateFindObjectContext();
PhSetDialogContext(hwndDlg, context);
}
else
{
context = PhGetDialogContext(hwndDlg);
}
if (!context)
return FALSE;
switch (uMsg)
{
case WM_INITDIALOG:
{
PhSetApplicationWindowIcon(hwndDlg);
context->WindowHandle = hwndDlg;
context->TreeNewHandle = GetDlgItem(hwndDlg, IDC_TREELIST);
context->TypeWindowHandle = GetDlgItem(hwndDlg, IDC_FILTERTYPE);
context->SearchWindowHandle = GetDlgItem(hwndDlg, IDC_FILTER);
context->TypeWindowFont = PhDuplicateFont(PhApplicationFont);
context->WindowText = PhGetWindowText(hwndDlg);
PhInitializeLayoutManager(&context->LayoutManager, hwndDlg);
PhAddLayoutItem(&context->LayoutManager, context->TypeWindowHandle, NULL, PH_ANCHOR_LEFT | PH_ANCHOR_TOP);
PhAddLayoutItem(&context->LayoutManager, context->SearchWindowHandle, NULL, PH_ANCHOR_LEFT | PH_ANCHOR_TOP | PH_ANCHOR_RIGHT);
PhAddLayoutItem(&context->LayoutManager, GetDlgItem(hwndDlg, IDOK), NULL, PH_ANCHOR_TOP | PH_ANCHOR_RIGHT);
PhAddLayoutItem(&context->LayoutManager, context->TreeNewHandle, NULL, PH_ANCHOR_ALL);
PhRegisterDialog(hwndDlg);
PhCreateSearchControl(
hwndDlg,
context->SearchWindowHandle,
L"Find Handles or DLLs",
PhFindObjectsSearchControlCallback,
context
);
PhpPopulateObjectTypes(context);
PhpInitializeHandleObjectTree(context);
context->MinimumSize.left = 0;
context->MinimumSize.top = 0;
context->MinimumSize.right = 300;
context->MinimumSize.bottom = 100;
MapDialogRect(hwndDlg, &context->MinimumSize);
if (PhValidWindowPlacementFromSetting(SETTING_FIND_OBJ_WINDOW_POSITION))
PhLoadWindowPlacementFromSetting(SETTING_FIND_OBJ_WINDOW_POSITION, SETTING_FIND_OBJ_WINDOW_SIZE, hwndDlg);
else
PhCenterWindow(hwndDlg, (HWND)lParam);
PhRegisterWindowCallback(hwndDlg, PH_PLUGIN_WINDOW_EVENT_TYPE_TOPMOST, NULL);
context->SearchResults = PhCreateList(128);
context->SearchResultsAddIndex = 0;
PhSetTimer(hwndDlg, PH_WINDOW_TIMER_DEFAULT, 1000, NULL);
Edit_SetSel(context->SearchWindowHandle, 0, -1);
PhInitializeWindowTheme(hwndDlg, PhEnableThemeSupport);
}
break;
case WM_DESTROY:
{
PhRemoveDialogContext(hwndDlg);
context->SearchStop = TRUE;
PhKillTimer(hwndDlg, PH_WINDOW_TIMER_DEFAULT);
if (context->SearchThreadHandle)
{
NtWaitForSingleObject(context->SearchThreadHandle, FALSE, NULL);
NtClose(context->SearchThreadHandle);
context->SearchThreadHandle = NULL;
}
PhSaveWindowPlacementToSetting(SETTING_FIND_OBJ_WINDOW_POSITION, SETTING_FIND_OBJ_WINDOW_SIZE, hwndDlg);
PhUnregisterWindowCallback(hwndDlg);
PhDeleteLayoutManager(&context->LayoutManager);
PhpDeleteHandleObjectTree(context);
if (context->WindowText)
PhDereferenceObject(context->WindowText);
if (context->TypeWindowFont)
DeleteFont(context->TypeWindowFont);
PhDereferenceObject(context);
PostQuitMessage(0);
}
break;
case WM_PH_SEARCH_SHOWDIALOG:
{
if (IsMinimized(hwndDlg))
ShowWindow(hwndDlg, SW_RESTORE);
else
ShowWindow(hwndDlg, SW_SHOW);
SetForegroundWindow(hwndDlg);
}
break;
case WM_SETCURSOR:
{
if (context->SearchThreadHandle)
{
PhSetCursor(PhLoadCursor(NULL, IDC_APPSTARTING));
SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, TRUE);
return TRUE;
}
}
break;
case WM_COMMAND:
{
if (GET_WM_COMMAND_HWND(wParam, lParam) == context->TypeWindowHandle)
{
switch (GET_WM_COMMAND_CMD(wParam, lParam))
{
case CBN_SELCHANGE:
{
// Change focus from the dropdown list to the searchbox.
SetFocus(context->SearchWindowHandle);
}
break;
}
}
switch (GET_WM_COMMAND_ID(wParam, lParam))
{
case IDOK:
{
// Don't continue if the user requested cancellation.
if (context->SearchStop)
break;
// Restore the original window title. (dmex)
PhSetWindowText(hwndDlg, PhGetStringOrEmpty(context->WindowText));
if (!context->SearchThreadHandle)
{
// Setup search parameters.
context->SearchAll = !!PhIsNullOrEmptyString(PhaGetWindowText(context->SearchWindowHandle));
PhMoveReference(&context->SearchTypeString, PhGetWindowText(context->TypeWindowHandle));
// Clean up previous results.
PhpFindObjectClearResultEntries(context);
// Start the search.
PhReferenceObject(context);
if (!NT_SUCCESS(PhCreateThreadEx(&context->SearchThreadHandle, PhpFindObjectsThreadStart, context)))
{
PhDereferenceObject(context);
break;
}
PhSetDialogItemText(hwndDlg, IDOK, L"Cancel");
PhSetCursor(PhLoadCursor(NULL, IDC_APPSTARTING));
}
else
{
context->SearchStop = TRUE;
EnableWindow(GetDlgItem(hwndDlg, IDOK), FALSE);
}
}
break;
case IDCANCEL:
{
DestroyWindow(hwndDlg);
}
break;
case WM_PH_SEARCH_SHOWMENU:
{
PPH_TREENEW_CONTEXT_MENU contextMenuEvent = (PPH_TREENEW_CONTEXT_MENU)lParam;
PPH_EMENU menu;
PPH_EMENU_ITEM selectedItem;
PPH_HANDLE_OBJECT_TREE_ROOT_NODE *handleObjectNodes = NULL;
ULONG numberOfHandleObjectNodes = 0;
if (!PhpGetSelectedHandleObjectNodes(context, &handleObjectNodes, &numberOfHandleObjectNodes))
break;
if (numberOfHandleObjectNodes != 0)
{
menu = PhCreateEMenu();
PhInsertEMenuItem(menu, PhCreateEMenuItem(0, ID_OBJECT_CLOSE, L"C&lose\bDel", NULL, NULL), ULONG_MAX);
PhInsertEMenuItem(menu, PhCreateEMenuSeparator(), ULONG_MAX);
PhInsertEMenuItem(menu, PhCreateEMenuItem(0, ID_OBJECT_GOTOOWNINGPROCESS, L"Go to &process...", NULL, NULL), ULONG_MAX);
PhInsertEMenuItem(menu, PhCreateEMenuSeparator(), ULONG_MAX);
PhInsertEMenuItem(menu, PhCreateEMenuItem(0, ID_OBJECT_PROPERTIES, L"Prope&rties", NULL, NULL), ULONG_MAX);
PhInsertEMenuItem(menu, PhCreateEMenuSeparator(), ULONG_MAX);
PhInsertEMenuItem(menu, PhCreateEMenuItem(0, ID_OBJECT_COPY, L"&Copy\bCtrl+C", NULL, NULL), ULONG_MAX);
PhInsertCopyCellEMenuItem(menu, ID_OBJECT_COPY, context->TreeNewHandle, contextMenuEvent->Column);
PhSetFlagsEMenuItem(menu, ID_OBJECT_PROPERTIES, PH_EMENU_DEFAULT, PH_EMENU_DEFAULT);
PhpInitializeFindObjMenu(menu, handleObjectNodes, numberOfHandleObjectNodes);
selectedItem = PhShowEMenu(
menu,
hwndDlg,
PH_EMENU_SHOW_SEND_COMMAND | PH_EMENU_SHOW_LEFTRIGHT,
PH_ALIGN_LEFT | PH_ALIGN_TOP,
contextMenuEvent->Location.x,
contextMenuEvent->Location.y
);
if (selectedItem && selectedItem->Id != ULONG_MAX)
{
PhHandleCopyCellEMenuItem(selectedItem);
}
PhDestroyEMenu(menu);
}
PhFree(handleObjectNodes);
}
break;
case ID_OBJECT_CLOSE:
{
PPH_HANDLE_OBJECT_TREE_ROOT_NODE *handleObjectNodes = NULL;
ULONG numberOfHandleObjectNodes = 0;
BOOLEAN allCanBeClosed = TRUE;
if (!PhpGetSelectedHandleObjectNodes(context, &handleObjectNodes, &numberOfHandleObjectNodes))
break;
// Check the item called by TreeNewKeyDown is valid (dmex)
for (ULONG i = 0; i < numberOfHandleObjectNodes; i++)
{
if (handleObjectNodes[i]->ResultType != HandleSearchResult)
{
allCanBeClosed = FALSE;
break;
}
}
if (!allCanBeClosed)
break;
if (numberOfHandleObjectNodes != 0 && PhShowConfirmMessage(
hwndDlg,
L"close",
numberOfHandleObjectNodes == 1 ? L"the selected handle" : L"the selected handles",
L"Closing handles may cause system instability and data corruption.",
FALSE
))
{
for (ULONG i = 0; i < numberOfHandleObjectNodes; i++)
{
NTSTATUS status;
HANDLE processHandle;
if (handleObjectNodes[i]->ResultType != HandleSearchResult)
continue;
if (WindowsVersion >= WINDOWS_10)
{
if (NT_SUCCESS(PhOpenProcess(
&processHandle,
PROCESS_QUERY_INFORMATION,
handleObjectNodes[i]->ProcessId
)))
{
BOOLEAN critical = FALSE;
BOOLEAN strict = FALSE;
BOOLEAN breakOnTermination;
PROCESS_MITIGATION_POLICY_INFORMATION policyInfo;
if (NT_SUCCESS(PhGetProcessBreakOnTermination(
processHandle,
&breakOnTermination
)))
{
if (breakOnTermination)
{
critical = TRUE;
}
}
policyInfo.Policy = ProcessStrictHandleCheckPolicy;
policyInfo.StrictHandleCheckPolicy.Flags = 0;
if (NT_SUCCESS(NtQueryInformationProcess(
processHandle,
ProcessMitigationPolicy,
&policyInfo,
sizeof(PROCESS_MITIGATION_POLICY_INFORMATION),
NULL
)))
{
if (policyInfo.StrictHandleCheckPolicy.Flags != 0)
{
strict = TRUE;
}
}
NtClose(processHandle);
if (critical && strict)
{
if (!PhShowConfirmMessage(
hwndDlg,
L"close",
L"critical handle(s)",
L"You are about to close one or more handles for a critical process with strict handle checks enabled. This will shut down the operating system immediately.\r\n\r\n",
TRUE
))
{
continue;
}
}
}
}
if (NT_SUCCESS(status = PhOpenProcess(
&processHandle,
PROCESS_DUP_HANDLE,
handleObjectNodes[i]->ProcessId
)))
{
if (NT_SUCCESS(status = NtDuplicateObject(
processHandle,
handleObjectNodes[i]->Handle,
NULL,
NULL,
0,
0,
DUPLICATE_CLOSE_SOURCE
)))
{
if (handleObjectNodes[i]->HandleInfo.HandleAttributes & OBJ_PROTECT_CLOSE)
status = STATUS_HANDLE_NOT_CLOSABLE;
else
PhpRemoveHandleObjectNode(context, handleObjectNodes[i]);
}
NtClose(processHandle);
}
if (!NT_SUCCESS(status))
{
if (!PhShowContinueStatus(hwndDlg,
PhaFormatString(L"Unable to close \"%s\"", PhGetStringOrDefault(handleObjectNodes[i]->BestObjectName, L"??"))->Buffer,
status,
0
))
break;
}
}
}
PhFree(handleObjectNodes);
}
break;
case ID_HANDLE_OBJECTPROPERTIES1:
case ID_HANDLE_OBJECTPROPERTIES2:
{
PPH_HANDLE_OBJECT_TREE_ROOT_NODE handleObjectNode;
if (handleObjectNode = PhpGetSelectedHandleObjectNode(context))
{
PH_HANDLE_ITEM_INFO info;
info.ProcessId = handleObjectNode->ProcessId;
info.Handle = handleObjectNode->Handle;
info.TypeName = handleObjectNode->TypeNameString;
info.BestObjectName = handleObjectNode->BestObjectName;
if (GET_WM_COMMAND_ID(wParam, lParam) == ID_HANDLE_OBJECTPROPERTIES1)
PhShowHandleObjectProperties1(hwndDlg, &info);
else
PhShowHandleObjectProperties2(hwndDlg, &info);
}
}
break;
case ID_OBJECT_GOTOOWNINGPROCESS:
{
PPH_HANDLE_OBJECT_TREE_ROOT_NODE handleObjectNode;
if (handleObjectNode = PhpGetSelectedHandleObjectNode(context))
{
PPH_PROCESS_NODE processNode;
if (processNode = PhFindProcessNode(handleObjectNode->ProcessId))
{
SystemInformer_SelectTabPage(0);
SystemInformer_SelectProcessNode(processNode);
SystemInformer_ToggleVisible(TRUE);
}
else
{
PhShowStatus(hwndDlg, L"The process does not exist.", STATUS_INVALID_CID, 0);
}
}
}
break;
case ID_OBJECT_PROPERTIES:
{
PPH_HANDLE_OBJECT_TREE_ROOT_NODE handleObjectNode;
if (handleObjectNode = PhpGetSelectedHandleObjectNode(context))
{
if (handleObjectNode->ResultType == HandleSearchResult)
{
PPH_HANDLE_ITEM handleItem;
handleItem = PhCreateHandleItem(&handleObjectNode->HandleInfo);
if (!PhIsNullOrEmptyString(handleObjectNode->TypeNameString))
handleItem->TypeName = PhReferenceObject(handleObjectNode->TypeNameString);
if (!PhIsNullOrEmptyString(handleObjectNode->ObjectNameString))
handleItem->ObjectName = PhReferenceObject(handleObjectNode->ObjectNameString);
if (!PhIsNullOrEmptyString(handleObjectNode->BestObjectName))
handleItem->BestObjectName = PhReferenceObject(handleObjectNode->BestObjectName);
PhShowHandleProperties(
hwndDlg,
handleObjectNode->ProcessId,
handleItem
);
PhDereferenceObject(handleItem);
}
else
{
// DLL or Mapped File. Just show file properties.
PhShellProperties(hwndDlg, handleObjectNode->BestObjectName->Buffer);
}
}
}
break;
case ID_OBJECT_COPY:
{
PPH_STRING text;
text = PhGetTreeNewText(context->TreeNewHandle, 0);
PhSetClipboardString(context->TreeNewHandle, &text->sr);
PhDereferenceObject(text);
}
break;
}
}
break;
case WM_DPICHANGED:
{
PhLayoutManagerUpdate(&context->LayoutManager, LOWORD(wParam));
PhLayoutManagerLayout(&context->LayoutManager);
PhpUpdateDropdownThemeMetrics(context, LOWORD(wParam));
}
break;
case WM_SIZE:
{
PhLayoutManagerLayout(&context->LayoutManager);
}
break;
case WM_SIZING:
{
PhResizingMinimumSize((PRECT)lParam, wParam, context->MinimumSize.right, context->MinimumSize.bottom);
}
break;
case WM_TIMER:
{
switch (wParam)
{
case PH_WINDOW_TIMER_DEFAULT:
{
if (!context->SearchThreadHandle)
break;
// Update the search results.
PhpFindObjectAddResultEntries(context);
}
break;
}
}
break;
case WM_PH_SEARCH_FINISHED:
{
// Add any un-added items.
PhpFindObjectAddResultEntries(context);
// Add the result count to the window title. (dmex)
PhSetWindowText(hwndDlg, PhaFormatString(
L"%s (%lu results)",
PhGetStringOrEmpty(context->WindowText),
context->SearchResultsAddIndex
)->Buffer);
NtWaitForSingleObject(context->SearchThreadHandle, FALSE, NULL);
NtClose(context->SearchThreadHandle);
context->SearchThreadHandle = NULL;
context->SearchStop = FALSE;
PhSetDialogItemText(hwndDlg, IDOK, L"Find");
EnableWindow(GetDlgItem(hwndDlg, IDOK), TRUE);
PhSetCursor(PhLoadCursor(NULL, IDC_ARROW));
if ((NTSTATUS)wParam == STATUS_INSUFFICIENT_RESOURCES)
{
PhShowWarning2(
hwndDlg,
L"Unable to search for handles because the total number of handles on the system is too large.",
L"%s",
L"Please check if there are any processes with an extremely large number of handles open."
);
}
}
break;
case WM_CTLCOLORBTN:
return HANDLE_WM_CTLCOLORBTN(hwndDlg, wParam, lParam, PhWindowThemeControlColor);
case WM_CTLCOLORDLG:
return HANDLE_WM_CTLCOLORDLG(hwndDlg, wParam, lParam, PhWindowThemeControlColor);
case WM_CTLCOLORSTATIC:
return HANDLE_WM_CTLCOLORSTATIC(hwndDlg, wParam, lParam, PhWindowThemeControlColor);
}
return FALSE;
}
_Function_class_(USER_THREAD_START_ROUTINE)
NTSTATUS PhpFindObjectsDialogThreadStart(
_In_ PVOID Parameter
)
{
BOOL result;
MSG message;
PH_AUTO_POOL autoPool;
PhInitializeAutoPool(&autoPool);
PhFindObjectsWindowHandle = PhCreateDialog(
PhInstanceHandle,
MAKEINTRESOURCE(IDD_FINDOBJECTS),
NULL,
PhFindObjectsDlgProc,
Parameter
);
PhSetEvent(&PhFindObjectsInitializedEvent);
while (result = GetMessage(&message, NULL, 0, 0))
{
if (result == INT_ERROR)
break;
if (!IsDialogMessage(PhFindObjectsWindowHandle, &message))
{
TranslateMessage(&message);
DispatchMessage(&message);
}
PhDrainAutoPool(&autoPool);
}
PhDeleteAutoPool(&autoPool);
PhResetEvent(&PhFindObjectsInitializedEvent);
if (PhFindObjectsThreadHandle)
{
NtClose(PhFindObjectsThreadHandle);
PhFindObjectsThreadHandle = NULL;
}
return STATUS_SUCCESS;
}
VOID PhShowFindObjectsDialog(
_In_ HWND ParentWindowHandle
)
{
if (!PhFindObjectsThreadHandle)
{
if (!NT_SUCCESS(PhCreateThreadEx(&PhFindObjectsThreadHandle, PhpFindObjectsDialogThreadStart, ParentWindowHandle)))
{
PhShowStatus(ParentWindowHandle, L"Unable to create the window.", 0, ERROR_OUTOFMEMORY);
return;
}
PhWaitForEvent(&PhFindObjectsInitializedEvent, NULL);
}
PostMessage(PhFindObjectsWindowHandle, WM_PH_SEARCH_SHOWDIALOG, 0, 0);
}
================================================
FILE: SystemInformer/gdihndl.c
================================================
/*
* Copyright (c) 2022 Winsider Seminars & Solutions, Inc. All rights reserved.
*
* This file is part of System Informer.
*
* Authors:
*
* wj32 2010
* dmex 2021-2023
*
*/
#include
#include
#include
#include
#include
typedef struct _PH_GDI_HANDLES_CONTEXT
{
HWND ListViewHandle;
HWND ParentWindowHandle;
PPH_PROCESS_ITEM ProcessItem;
PPH_LIST List;
PH_LAYOUT_MANAGER LayoutManager;
} PH_GDI_HANDLES_CONTEXT, *PPH_GDI_HANDLES_CONTEXT;
typedef struct _PH_GDI_HANDLE_ITEM
{
PGDI_HANDLE_ENTRY Entry;
ULONG Handle;
PVOID Object;
PCWSTR TypeName;
PPH_STRING Information;
} PH_GDI_HANDLE_ITEM, *PPH_GDI_HANDLE_ITEM;
INT_PTR CALLBACK PhpGdiHandlesDlgProc(
_In_ HWND hwndDlg,
_In_ UINT uMsg,
_In_ WPARAM wParam,
_In_ LPARAM lParam
);
VOID PhShowGdiHandlesDialog(
_In_ HWND ParentWindowHandle,
_In_ PPH_PROCESS_ITEM ProcessItem
)
{
PPH_GDI_HANDLES_CONTEXT context;
HWND windowHandle;
context = PhAllocateZero(sizeof(PH_GDI_HANDLES_CONTEXT));
context->ProcessItem = PhReferenceObject(ProcessItem);
context->List = PhCreateList(20);
context->ParentWindowHandle = ParentWindowHandle;
windowHandle = PhCreateDialog(
PhInstanceHandle,
MAKEINTRESOURCE(IDD_GDIHANDLES),
PhCsForceNoParent ? NULL : ParentWindowHandle,
PhpGdiHandlesDlgProc,
context
);
ShowWindow(windowHandle, SW_SHOW);
SetForegroundWindow(windowHandle);
}
PCWSTR PhpGetGdiHandleTypeName(
_In_ ULONG Unique
)
{
switch (GDI_CLIENT_TYPE_FROM_UNIQUE(Unique))
{
case GDI_CLIENT_ALTDC_TYPE:
return L"Alt. DC";
case GDI_CLIENT_BITMAP_TYPE:
return L"Bitmap";
case GDI_CLIENT_BRUSH_TYPE:
return L"Brush";
case GDI_CLIENT_CLIENTOBJ_TYPE:
return L"Client Object";
case GDI_CLIENT_DIBSECTION_TYPE:
return L"DIB Section";
case GDI_CLIENT_DC_TYPE:
return L"DC";
case GDI_CLIENT_EXTPEN_TYPE:
return L"ExtPen";
case GDI_CLIENT_FONT_TYPE:
return L"Font";
case GDI_CLIENT_METADC16_TYPE:
return L"Metafile DC";
case GDI_CLIENT_METAFILE_TYPE:
return L"Enhanced Metafile";
case GDI_CLIENT_METAFILE16_TYPE:
return L"Metafile";
case GDI_CLIENT_PALETTE_TYPE:
return L"Palette";
case GDI_CLIENT_PEN_TYPE:
return L"Pen";
case GDI_CLIENT_REGION_TYPE:
return L"Region";
default:
return NULL;
}
}
PPH_STRING PhpGetGdiHandleInformation(
_In_ ULONG Handle
)
{
HGDIOBJ handle;
handle = (HGDIOBJ)UlongToPtr(Handle);
switch (GDI_CLIENT_TYPE_FROM_HANDLE(Handle))
{
case GDI_CLIENT_BITMAP_TYPE:
case GDI_CLIENT_DIBSECTION_TYPE:
{
BITMAP bitmap;
if (GetObject(handle, sizeof(BITMAP), &bitmap))
{
return PhFormatString(
L"Width: %u, Height: %u, Depth: %u",
bitmap.bmWidth,
bitmap.bmHeight,
bitmap.bmBitsPixel
);
}
}
break;
case GDI_CLIENT_BRUSH_TYPE:
{
LOGBRUSH brush;
if (GetObject(handle, sizeof(LOGBRUSH), &brush))
{
return PhFormatString(
L"Style: %u, Color: 0x%08x, Hatch: 0x%Ix",
brush.lbStyle,
_byteswap_ulong(brush.lbColor),
brush.lbHatch
);
}
}
break;
case GDI_CLIENT_EXTPEN_TYPE:
{
EXTLOGPEN pen;
if (GetObject(handle, sizeof(EXTLOGPEN), &pen))
{
return PhFormatString(
L"Style: 0x%x, Width: %u, Color: 0x%08x",
pen.elpPenStyle,
pen.elpWidth,
_byteswap_ulong(pen.elpColor)
);
}
}
break;
case GDI_CLIENT_FONT_TYPE:
{
LOGFONT font;
if (GetObject(handle, sizeof(LOGFONT), &font))
{
return PhFormatString(
L"Face: %s, Height: %d",
font.lfFaceName,
font.lfHeight
);
}
}
break;
case GDI_CLIENT_PALETTE_TYPE:
{
USHORT count;
if (GetObject(handle, sizeof(USHORT), &count))
{
return PhFormatString(
L"Entries: %u",
(ULONG)count
);
}
}
break;
case GDI_CLIENT_PEN_TYPE:
{
LOGPEN pen;
if (GetObject(handle, sizeof(LOGPEN), &pen))
{
return PhFormatString(
L"Style: %u, Width: %u, Color: 0x%08x",
pen.lopnStyle,
pen.lopnWidth.x,
_byteswap_ulong(pen.lopnColor)
);
}
}
break;
}
return NULL;
}
VOID PhpRefreshGdiHandles(
_In_ PPH_GDI_HANDLES_CONTEXT Context
)
{
ULONG i;
PGDI_SHARED_MEMORY gdiShared;
USHORT processId;
ULONG handleCount;
PGDI_HANDLE_ENTRY handle;
PPH_GDI_HANDLE_ITEM gdiHandleItem;
MEMORY_BASIC_INFORMATION basicInfo;
memset(&basicInfo, 0, sizeof(MEMORY_BASIC_INFORMATION));
ExtendedListView_SetRedraw(Context->ListViewHandle, FALSE);
ListView_DeleteAllItems(Context->ListViewHandle);
for (i = 0; i < Context->List->Count; i++)
{
gdiHandleItem = Context->List->Items[i];
if (gdiHandleItem->Information)
PhDereferenceObject(gdiHandleItem->Information);
PhFree(Context->List->Items[i]);
Context->List->Items[i] = NULL;
}
PhClearList(Context->List);
gdiShared = (PGDI_SHARED_MEMORY)NtCurrentPeb()->GdiSharedHandleTable;
processId = (USHORT)HandleToUlong(Context->ProcessItem->ProcessId);
handleCount = GDI_MAX_HANDLE_COUNT;
if (NT_SUCCESS(NtQueryVirtualMemory(
NtCurrentProcess(),
gdiShared,
MemoryBasicInformation,
&basicInfo,
sizeof(MEMORY_BASIC_INFORMATION),
NULL
)))
{
handleCount = (ULONG)(basicInfo.RegionSize / sizeof(GDI_HANDLE_ENTRY));
handleCount = __min(GDI_MAX_HANDLE_COUNT, handleCount);
}
for (i = 0; i < handleCount; i++)
{
PCWSTR typeName;
LONG lvItemIndex;
WCHAR pointer[PH_PTR_STR_LEN_1];
handle = &gdiShared->Handles[i];
if (handle->Owner.ProcessId != processId)
continue;
typeName = PhpGetGdiHandleTypeName(handle->Unique);
if (!typeName)
continue;
gdiHandleItem = PhAllocateZero(sizeof(PH_GDI_HANDLE_ITEM));
gdiHandleItem->Entry = handle;
gdiHandleItem->Handle = GDI_MAKE_HANDLE(i, handle->Unique);
gdiHandleItem->Object = handle->Object;
gdiHandleItem->TypeName = typeName;
gdiHandleItem->Information = PhpGetGdiHandleInformation(gdiHandleItem->Handle);
PhAddItemList(Context->List, gdiHandleItem);
lvItemIndex = PhAddListViewItem(Context->ListViewHandle, MAXINT, gdiHandleItem->TypeName, gdiHandleItem);
PhPrintPointer(pointer, UlongToPtr(gdiHandleItem->Handle));
PhSetListViewSubItem(Context->ListViewHandle, lvItemIndex, 1, pointer);
PhPrintPointer(pointer, gdiHandleItem->Object);
PhSetListViewSubItem(Context->ListViewHandle, lvItemIndex, 2, pointer);
PhSetListViewSubItem(Context->ListViewHandle, lvItemIndex, 3, PhGetString(gdiHandleItem->Information));
}
ExtendedListView_SortItems(Context->ListViewHandle);
ExtendedListView_SetRedraw(Context->ListViewHandle, TRUE);
}
LONG NTAPI PhpGdiHandleHandleCompareFunction(
_In_ PVOID Item1,
_In_ PVOID Item2,
_In_opt_ PVOID Context
)
{
PPH_GDI_HANDLE_ITEM item1 = Item1;
PPH_GDI_HANDLE_ITEM item2 = Item2;
return uintcmp(item1->Handle, item2->Handle);
}
LONG NTAPI PhpGdiHandleObjectCompareFunction(
_In_ PVOID Item1,
_In_ PVOID Item2,
_In_opt_ PVOID Context
)
{
PPH_GDI_HANDLE_ITEM item1 = Item1;
PPH_GDI_HANDLE_ITEM item2 = Item2;
return uintptrcmp((ULONG_PTR)item1->Object, (ULONG_PTR)item2->Object);
}
INT_PTR CALLBACK PhpGdiHandlesDlgProc(
_In_ HWND hwndDlg,
_In_ UINT uMsg,
_In_ WPARAM wParam,
_In_ LPARAM lParam
)
{
PPH_GDI_HANDLES_CONTEXT context = NULL;
if (uMsg == WM_INITDIALOG)
{
context = (PPH_GDI_HANDLES_CONTEXT)lParam;
PhSetWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT, context);
}
else
{
context = PhGetWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT);
}
if (!context)
return FALSE;
switch (uMsg)
{
case WM_INITDIALOG:
{
context->ListViewHandle = GetDlgItem(hwndDlg, IDC_LIST);
PhSetApplicationWindowIcon(hwndDlg);
PhCenterWindow(hwndDlg, context->ParentWindowHandle);
PhRegisterDialog(hwndDlg);
PhInitializeLayoutManager(&context->LayoutManager, hwndDlg);
PhAddLayoutItem(&context->LayoutManager, context->ListViewHandle, NULL, PH_ANCHOR_ALL);
PhAddLayoutItem(&context->LayoutManager, GetDlgItem(hwndDlg, IDC_REFRESH), NULL, PH_ANCHOR_BOTTOM | PH_ANCHOR_RIGHT);
PhAddLayoutItem(&context->LayoutManager, GetDlgItem(hwndDlg, IDOK), NULL, PH_ANCHOR_BOTTOM | PH_ANCHOR_RIGHT);
PhSetListViewStyle(context->ListViewHandle, TRUE, TRUE);
PhSetControlTheme(context->ListViewHandle, L"explorer");
PhAddListViewColumn(context->ListViewHandle, 0, 0, 0, LVCFMT_LEFT, 100, L"Type");
PhAddListViewColumn(context->ListViewHandle, 1, 1, 1, LVCFMT_LEFT, 80, L"Handle");
PhAddListViewColumn(context->ListViewHandle, 2, 2, 2, LVCFMT_LEFT, 102, L"Object");
PhAddListViewColumn(context->ListViewHandle, 3, 3, 3, LVCFMT_LEFT, 200, L"Information");
PhSetExtendedListView(context->ListViewHandle);
ExtendedListView_SetCompareFunction(context->ListViewHandle, 1, PhpGdiHandleHandleCompareFunction);
ExtendedListView_SetCompareFunction(context->ListViewHandle, 2, PhpGdiHandleObjectCompareFunction);
ExtendedListView_AddFallbackColumn(context->ListViewHandle, 0);
ExtendedListView_AddFallbackColumn(context->ListViewHandle, 1);
{
PPH_STRING windowTitle;
windowTitle = PhGetWindowText(hwndDlg);
PhMoveReference(&windowTitle, PhFormatString(
L"%s: %s (%lu)",
PhGetStringOrEmpty(windowTitle),
PhGetStringOrEmpty(context->ProcessItem->ProcessName),
HandleToUlong(context->ProcessItem->ProcessId)
));
PhSetWindowText(hwndDlg, PhGetStringOrEmpty(windowTitle));
PhDereferenceObject(windowTitle);
}
PhpRefreshGdiHandles(context);
PhInitializeWindowTheme(hwndDlg, PhEnableThemeSupport);
}
break;
case WM_DESTROY:
{
PhUnregisterDialog(hwndDlg);
PhDeleteLayoutManager(&context->LayoutManager);
if (context->List)
{
for (ULONG i = 0; i < context->List->Count; i++)
{
PPH_GDI_HANDLE_ITEM gdiHandleItem = context->List->Items[i];
if (gdiHandleItem->Information)
PhDereferenceObject(gdiHandleItem->Information);
PhFree(context->List->Items[i]);
context->List->Items[i] = NULL;
}
PhDereferenceObject(context->List);
}
if (context->ProcessItem)
{
PhDereferenceObject(context->ProcessItem);
}
PhRemoveWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT);
PhFree(context);
}
break;
case WM_COMMAND:
{
switch (GET_WM_COMMAND_ID(wParam, lParam))
{
case IDCANCEL:
case IDOK:
DestroyWindow(hwndDlg);
break;
case IDC_REFRESH:
PhpRefreshGdiHandles(context);
break;
}
}
break;
case WM_DPICHANGED:
{
PhLayoutManagerUpdate(&context->LayoutManager, LOWORD(wParam));
PhLayoutManagerLayout(&context->LayoutManager);
}
break;
case WM_SIZE:
{
PhLayoutManagerLayout(&context->LayoutManager);
}
break;
case WM_NOTIFY:
{
PhHandleListViewNotifyForCopy(lParam, context->ListViewHandle);
}
break;
case WM_CONTEXTMENU:
{
if ((HWND)wParam == context->ListViewHandle)
{
POINT point;
PPH_EMENU menu;
PPH_EMENU item;
PVOID* listviewItems;
ULONG numberOfItems;
point.x = GET_X_LPARAM(lParam);
point.y = GET_Y_LPARAM(lParam);
if (point.x == -1 && point.y == -1)
PhGetListViewContextMenuPoint(context->ListViewHandle, &point);
PhGetSelectedListViewItemParams(context->ListViewHandle, &listviewItems, &numberOfItems);
if (numberOfItems != 0)
{
menu = PhCreateEMenu();
PhInsertEMenuItem(menu, PhCreateEMenuItem(0, IDC_COPY, L"&Copy", NULL, NULL), ULONG_MAX);
PhInsertCopyListViewEMenuItem(menu, IDC_COPY, context->ListViewHandle);
item = PhShowEMenu(
menu,
hwndDlg,
PH_EMENU_SHOW_SEND_COMMAND | PH_EMENU_SHOW_LEFTRIGHT,
PH_ALIGN_LEFT | PH_ALIGN_TOP,
point.x,
point.y
);
if (item)
{
if (!PhHandleCopyListViewEMenuItem(item))
{
switch (item->Id)
{
case IDC_COPY:
PhCopyListView(context->ListViewHandle);
break;
}
}
}
PhDestroyEMenu(menu);
}
PhFree(listviewItems);
}
}
break;
case WM_CTLCOLORBTN:
return HANDLE_WM_CTLCOLORBTN(hwndDlg, wParam, lParam, PhWindowThemeControlColor);
case WM_CTLCOLORDLG:
return HANDLE_WM_CTLCOLORDLG(hwndDlg, wParam, lParam, PhWindowThemeControlColor);
case WM_CTLCOLORSTATIC:
return HANDLE_WM_CTLCOLORSTATIC(hwndDlg, wParam, lParam, PhWindowThemeControlColor);
}
return FALSE;
}
================================================
FILE: SystemInformer/heapinfo.c
================================================
/*
* Copyright (c) 2022 Winsider Seminars & Solutions, Inc. All rights reserved.
*
* This file is part of System Informer.
*
* Authors:
*
* wj32 2010-2011
* dmex 2020-2023
*
*/
#include
#include
#include
#include
#include
#include
#include
typedef struct _PH_PROCESS_HEAPS_CONTEXT
{
HWND WindowHandle;
HWND ParentWindowHandle;
HWND ListViewHandle;
HFONT BoldFont;
union
{
BOOLEAN Flags;
struct
{
BOOLEAN Initialized : 1;
BOOLEAN IsWow64Process : 1;
BOOLEAN Spare : 6;
};
};
PPH_PROCESS_ITEM ProcessItem;
PVOID ProcessHeap;
PVOID DebugBuffer;
PH_LAYOUT_MANAGER LayoutManager;
} PH_PROCESS_HEAPS_CONTEXT, *PPH_PROCESS_HEAPS_CONTEXT;
typedef struct _HEAP_COUNTERS
{
ULONG_PTR TotalMemoryReserved;
ULONG_PTR TotalMemoryCommitted;
ULONG_PTR TotalMemoryLargeUCR;
ULONG_PTR TotalSizeInVirtualBlocks;
ULONG TotalSegments;
ULONG TotalUCRs;
ULONG CommittOps;
ULONG DeCommitOps;
ULONG LockAcquires;
ULONG LockCollisions;
ULONG CommitRate;
ULONG DecommittRate;
ULONG CommitFailures;
ULONG InBlockCommitFailures;
ULONG PollIntervalCounter;
ULONG DecommitsSinceLastCheck;
ULONG HeapPollInterval;
ULONG AllocAndFreeOps;
ULONG AllocationIndicesActive;
ULONG InBlockDeccommits;
ULONG_PTR InBlockDeccomitSize;
ULONG_PTR HighWatermarkSize;
ULONG_PTR LastPolledSize;
} HEAP_COUNTERS, *PHEAP_COUNTERS;
typedef struct _HEAP_COUNTERS32
{
ULONG TotalMemoryReserved;
ULONG TotalMemoryCommitted;
ULONG TotalMemoryLargeUCR;
ULONG TotalSizeInVirtualBlocks;
ULONG TotalSegments;
ULONG TotalUCRs;
ULONG CommittOps;
ULONG DeCommitOps;
ULONG LockAcquires;
ULONG LockCollisions;
ULONG CommitRate;
ULONG DecommittRate;
ULONG CommitFailures;
ULONG InBlockCommitFailures;
ULONG PollIntervalCounter;
ULONG DecommitsSinceLastCheck;
ULONG HeapPollInterval;
ULONG AllocAndFreeOps;
ULONG AllocationIndicesActive;
ULONG InBlockDeccommits;
ULONG InBlockDeccomitSize;
ULONG HighWatermarkSize;
ULONG LastPolledSize;
} HEAP_COUNTERS32, *PHEAP_COUNTERS32;
typedef struct _HEAP_OPPORTUNISTIC_LARGE_PAGE_STATS
{
ULONG_PTR SmallPagesInUseWithinLarge;
ULONG_PTR OpportunisticLargePageCount;
} HEAP_OPPORTUNISTIC_LARGE_PAGE_STATS, *PHEAP_OPPORTUNISTIC_LARGE_PAGE_STATS;
typedef struct _HEAP_OPPORTUNISTIC_LARGE_PAGE_STATS32
{
ULONG SmallPagesInUseWithinLarge;
ULONG OpportunisticLargePageCount;
} HEAP_OPPORTUNISTIC_LARGE_PAGE_STATS32, *PHEAP_OPPORTUNISTIC_LARGE_PAGE_STATS32;
typedef struct _HEAP_RUNTIME_MEMORY_STATS
{
ULONG_PTR TotalReservedPages;
ULONG_PTR TotalCommittedPages;
ULONG_PTR FreeCommittedPages;
ULONG_PTR LfhFreeCommittedPages;
HEAP_OPPORTUNISTIC_LARGE_PAGE_STATS LargePageStats[2];
//RTL_HP_SEG_ALLOC_POLICY LargePageUtilizationPolicy;
} HEAP_RUNTIME_MEMORY_STATS, *PHEAP_RUNTIME_MEMORY_STATS;
typedef struct _HEAP_RUNTIME_MEMORY_STATS32
{
ULONG TotalReservedPages;
ULONG TotalCommittedPages;
ULONG FreeCommittedPages;
ULONG LfhFreeCommittedPages;
HEAP_OPPORTUNISTIC_LARGE_PAGE_STATS32 LargePageStats[2];
//RTL_HP_SEG_ALLOC_POLICY32 LargePageUtilizationPolicy;
} HEAP_RUNTIME_MEMORY_STATS32, *PHEAP_RUNTIME_MEMORY_STATS32;
NTSTATUS PhGetProcessDefaultHeap(
_In_ HANDLE ProcessHandle,
_Out_ PVOID *Heap
);
NTSTATUS PhGetProcessHeapSignature(
_In_ HANDLE ProcessHandle,
_In_ PVOID HeapAddress,
_In_ ULONG IsWow64Process,
_Out_ ULONG* HeapSignature
);
INT_PTR CALLBACK PhpProcessHeapsDlgProc(
_In_ HWND hwndDlg,
_In_ UINT uMsg,
_In_ WPARAM wParam,
_In_ LPARAM lParam
);
VOID PhShowProcessHeapsDialog(
_In_ HWND ParentWindowHandle,
_In_ PPH_PROCESS_ITEM ProcessItem
)
{
PPH_PROCESS_HEAPS_CONTEXT context;
context = PhAllocateZero(sizeof(PH_PROCESS_HEAPS_CONTEXT));
context->ParentWindowHandle = ParentWindowHandle;
context->ProcessItem = PhReferenceObject(ProcessItem);
context->IsWow64Process = !!ProcessItem->IsWow64Process;
PhDialogBox(
PhInstanceHandle,
MAKEINTRESOURCE(IDD_HEAPS),
NULL,
PhpProcessHeapsDlgProc,
context
);
}
static INT NTAPI PhpHeapAddressCompareFunction(
_In_ PVOID Item1,
_In_ PVOID Item2,
_In_ PVOID Context
)
{
PPH_PROCESS_HEAPS_CONTEXT context = Context;
if (context->IsWow64Process)
{
PPH_PROCESS_DEBUG_HEAP_ENTRY32 heapInfo1 = Item1;
PPH_PROCESS_DEBUG_HEAP_ENTRY32 heapInfo2 = Item2;
return uintptrcmp((ULONG_PTR)heapInfo1->BaseAddress, (ULONG_PTR)heapInfo2->BaseAddress);
}
else
{
PPH_PROCESS_DEBUG_HEAP_ENTRY heapInfo1 = Item1;
PPH_PROCESS_DEBUG_HEAP_ENTRY heapInfo2 = Item2;
return uintptrcmp((ULONG_PTR)heapInfo1->BaseAddress, (ULONG_PTR)heapInfo2->BaseAddress);
}
}
static INT NTAPI PhpHeapUsedCompareFunction(
_In_ PVOID Item1,
_In_ PVOID Item2,
_In_ PVOID Context
)
{
PPH_PROCESS_HEAPS_CONTEXT context = Context;
if (context->IsWow64Process)
{
PPH_PROCESS_DEBUG_HEAP_ENTRY32 heapInfo1 = Item1;
PPH_PROCESS_DEBUG_HEAP_ENTRY32 heapInfo2 = Item2;
return uintptrcmp(heapInfo1->BytesAllocated, heapInfo2->BytesAllocated);
}
else
{
PPH_PROCESS_DEBUG_HEAP_ENTRY heapInfo1 = Item1;
PPH_PROCESS_DEBUG_HEAP_ENTRY heapInfo2 = Item2;
return uintptrcmp(heapInfo1->BytesAllocated, heapInfo2->BytesAllocated);
}
}
static INT NTAPI PhpHeapCommittedCompareFunction(
_In_ PVOID Item1,
_In_ PVOID Item2,
_In_ PVOID Context
)
{
PPH_PROCESS_HEAPS_CONTEXT context = Context;
if (context->IsWow64Process)
{
PPH_PROCESS_DEBUG_HEAP_ENTRY32 heapInfo1 = Item1;
PPH_PROCESS_DEBUG_HEAP_ENTRY32 heapInfo2 = Item2;
return uintptrcmp(heapInfo1->BytesCommitted, heapInfo2->BytesCommitted);
}
else
{
PPH_PROCESS_DEBUG_HEAP_ENTRY heapInfo1 = Item1;
PPH_PROCESS_DEBUG_HEAP_ENTRY heapInfo2 = Item2;
return uintptrcmp(heapInfo1->BytesCommitted, heapInfo2->BytesCommitted);
}
}
static INT NTAPI PhpHeapEntriesCompareFunction(
_In_ PVOID Item1,
_In_ PVOID Item2,
_In_ PVOID Context
)
{
PPH_PROCESS_HEAPS_CONTEXT context = Context;
if (context->IsWow64Process)
{
PPH_PROCESS_DEBUG_HEAP_ENTRY32 heapInfo1 = Item1;
PPH_PROCESS_DEBUG_HEAP_ENTRY32 heapInfo2 = Item2;
return uintcmp(heapInfo1->NumberOfEntries, heapInfo2->NumberOfEntries);
}
else
{
PPH_PROCESS_DEBUG_HEAP_ENTRY heapInfo1 = Item1;
PPH_PROCESS_DEBUG_HEAP_ENTRY heapInfo2 = Item2;
return uintcmp(heapInfo1->NumberOfEntries, heapInfo2->NumberOfEntries);
}
}
static HFONT NTAPI PhpHeapFontFunction(
_In_ INT Index,
_In_ PVOID Param,
_In_ PVOID Context
)
{
PVOID heapBaseAddress = Param;
PPH_PROCESS_HEAPS_CONTEXT context = Context;
if (context->IsWow64Process)
{
PPH_PROCESS_DEBUG_HEAP_ENTRY32 heapInfo = Param;
heapBaseAddress = UlongToPtr(heapInfo->BaseAddress);
}
else
{
PPH_PROCESS_DEBUG_HEAP_ENTRY heapInfo = Param;
heapBaseAddress = heapInfo->BaseAddress;
}
if (!context->ProcessHeap)
{
HANDLE processHandle;
if (NT_SUCCESS(PhOpenProcess(
&processHandle,
PROCESS_QUERY_LIMITED_INFORMATION | PROCESS_VM_READ,
context->ProcessItem->ProcessId
)))
{
PhGetProcessDefaultHeap(processHandle, &context->ProcessHeap);
NtClose(processHandle);
}
}
if (heapBaseAddress == context->ProcessHeap)
{
if (!context->BoldFont)
context->BoldFont = PhDuplicateFontWithNewWeight((HFONT)SendMessage(context->ListViewHandle, WM_GETFONT, 0, 0), FW_BOLD);
return context->BoldFont;
}
return NULL;
}
PPH_STRING PhGetProcessHeapFlagsText(
_In_ ULONG Flags
)
{
PH_STRING_BUILDER stringBuilder;
PhInitializeStringBuilder(&stringBuilder, 10);
if (Flags & HEAP_NO_SERIALIZE)
PhAppendStringBuilder2(&stringBuilder, L"No serialize, ");
if (Flags & HEAP_GROWABLE)
PhAppendStringBuilder2(&stringBuilder, L"Growable, ");
if (Flags & HEAP_GENERATE_EXCEPTIONS)
PhAppendStringBuilder2(&stringBuilder, L"Generate exceptions, ");
if (Flags & HEAP_ZERO_MEMORY)
PhAppendStringBuilder2(&stringBuilder, L"Zero memory, ");
if (Flags & HEAP_REALLOC_IN_PLACE_ONLY)
PhAppendStringBuilder2(&stringBuilder, L"Realloc in-place, ");
if (Flags & HEAP_TAIL_CHECKING_ENABLED)
PhAppendStringBuilder2(&stringBuilder, L"Tail checking, ");
if (Flags & HEAP_FREE_CHECKING_ENABLED)
PhAppendStringBuilder2(&stringBuilder, L"Free checking, ");
if (Flags & HEAP_DISABLE_COALESCE_ON_FREE)
PhAppendStringBuilder2(&stringBuilder, L"Coalesce on free, ");
if (Flags & HEAP_CREATE_ALIGN_16)
PhAppendStringBuilder2(&stringBuilder, L"Align 16, ");
if (Flags & HEAP_CREATE_ENABLE_TRACING)
PhAppendStringBuilder2(&stringBuilder, L"Traceable, ");
if (Flags & HEAP_CREATE_ENABLE_EXECUTE)
PhAppendStringBuilder2(&stringBuilder, L"Executable, ");
if (Flags & HEAP_CREATE_SEGMENT_HEAP)
PhAppendStringBuilder2(&stringBuilder, L"Segment heap, ");
if (Flags & HEAP_CREATE_HARDENED)
PhAppendStringBuilder2(&stringBuilder, L"Segment hardened, ");
if (PhEndsWithString2(stringBuilder.String, L", ", FALSE))
PhRemoveEndStringBuilder(&stringBuilder, 2);
if (Flags)
{
WCHAR pointer[PH_PTR_STR_LEN_1];
PhPrintPointer(pointer, UlongToPtr(Flags));
if (PhIsNullOrEmptyString(stringBuilder.String))
PhAppendFormatStringBuilder(&stringBuilder, L"%s", pointer);
else
PhAppendFormatStringBuilder(&stringBuilder, L" (%s)", pointer);
}
return PhFinalStringBuilderString(&stringBuilder);
}
PCWSTR PhGetProcessHeapClassText(
_In_ ULONG HeapClass
)
{
switch (HeapClass)
{
case HEAP_CLASS_0:
return L"Process Heap";
case HEAP_CLASS_1:
return L"Private Heap";
case HEAP_CLASS_2:
return L"Kernel Heap";
case HEAP_CLASS_3:
return L"GDI Heap";
case HEAP_CLASS_4:
return L"User Heap";
case HEAP_CLASS_5:
return L"Console Heap";
case HEAP_CLASS_6:
return L"Desktop Heap";
case HEAP_CLASS_7:
return L"CSRSS Shared Heap";
case HEAP_CLASS_8:
return L"CSRSS Port Heap";
}
return L"Unknown Heap";
}
VOID PhpEnumerateProcessHeaps(
_In_ PPH_PROCESS_HEAPS_CONTEXT Context
)
{
NTSTATUS status = STATUS_UNSUCCESSFUL;
HANDLE processHandle = NULL;
HANDLE powerRequestHandle = NULL;
PROCESS_REFLECTION_INFORMATION reflectionInfo = { 0 };
HANDLE clientProcessId = Context->ProcessItem->ProcessId;
BOOLEAN sizesInBytes;
sizesInBytes = Button_GetCheck(GetDlgItem(Context->WindowHandle, IDC_SIZESINBYTES)) == BST_CHECKED;
ExtendedListView_SetRedraw(Context->ListViewHandle, FALSE);
ListView_DeleteAllItems(Context->ListViewHandle);
if (Context->DebugBuffer)
{
PhFree(Context->DebugBuffer);
Context->DebugBuffer = NULL;
}
if (WindowsVersion >= WINDOWS_8 && WindowsVersion <= WINDOWS_8_1)
{
// Windows 8 requires ALL_ACCESS for PLM execution requests. (dmex)
status = PhOpenProcess(
&processHandle,
PROCESS_ALL_ACCESS,
clientProcessId
);
}
else
{
// Windows 10 and above require SET_LIMITED for PLM execution requests. (dmex)
status = PhOpenProcess(
&processHandle,
PROCESS_QUERY_LIMITED_INFORMATION | PROCESS_SET_LIMITED_INFORMATION | // PLM
PROCESS_CREATE_THREAD | PROCESS_VM_OPERATION | PROCESS_DUP_HANDLE, // Reflection
clientProcessId
);
}
if (processHandle)
{
PhCreateExecutionRequiredRequest(processHandle, &powerRequestHandle);
if (PhGetIntegerSetting(SETTING_ENABLE_HEAP_REFLECTION))
{
// NOTE: RtlQueryProcessDebugInformation injects a thread into the process causing deadlocks and other issues in rare cases.
// We mitigate these problems by reflecting the process and querying heap information from the clone. (dmex)
status = PhCreateProcessReflection(
&reflectionInfo,
processHandle
);
if (NT_SUCCESS(status))
{
clientProcessId = reflectionInfo.ReflectionClientId.UniqueProcess;
}
}
}
#ifdef _WIN64
if (Context->ProcessItem->IsWow64Process)
{
if (PhUiConnectToPhSvcEx(Context->WindowHandle, Wow64PhSvcMode, FALSE))
{
PPH_STRING capturedHeapInfoString;
PPH_PROCESS_DEBUG_HEAP_INFORMATION32 capturedHeapInfo;
ULONG capturedHeapInfoLength;
status = PhSvcCallQueryProcessHeapInformation(
clientProcessId,
&capturedHeapInfoString
);
if (!NT_SUCCESS(status))
{
PhUiDisconnectFromPhSvc();
PhShowStatus(Context->WindowHandle, L"Unable to query heap information.", status, 0);
goto CleanupExit;
}
PhUiDisconnectFromPhSvc();
capturedHeapInfoLength = sizeof(PH_PROCESS_DEBUG_HEAP_INFORMATION32) + 100 * sizeof(PH_PROCESS_DEBUG_HEAP_ENTRY32);
capturedHeapInfo = PhAllocateZero(capturedHeapInfoLength);
if (!PhHexStringToBufferEx(
&capturedHeapInfoString->sr,
capturedHeapInfoLength,
capturedHeapInfo
))
{
PhFree(capturedHeapInfo);
goto CleanupExit;
}
Context->DebugBuffer = capturedHeapInfo;
Context->ProcessHeap = UlongToPtr(capturedHeapInfo->DefaultHeap);
for (ULONG i = 0; i < capturedHeapInfo->NumberOfHeaps; i++)
{
PPH_PROCESS_DEBUG_HEAP_ENTRY32 entry = &capturedHeapInfo->Heaps[i];
INT lvItemIndex;
WCHAR value[PH_INT64_STR_LEN_1];
PhPrintUInt32(value, i + 1);
lvItemIndex = PhAddListViewItem(Context->ListViewHandle, MAXINT, value, entry);
PhPrintPointer(value, UlongToPtr(entry->BaseAddress));
PhSetListViewSubItem(Context->ListViewHandle, lvItemIndex, 1, value);
PhSetListViewSubItem(Context->ListViewHandle, lvItemIndex, 2, PhaFormatSize(entry->BytesAllocated, sizesInBytes ? 0 : ULONG_MAX)->Buffer);
PhSetListViewSubItem(Context->ListViewHandle, lvItemIndex, 3, PhaFormatSize(entry->BytesCommitted, sizesInBytes ? 0 : ULONG_MAX)->Buffer);
PhSetListViewSubItem(Context->ListViewHandle, lvItemIndex, 4, PhaFormatUInt64(entry->NumberOfEntries, TRUE)->Buffer);
PhSetListViewSubItem(Context->ListViewHandle, lvItemIndex, 5, PH_AUTO_T(PH_STRING, PhGetProcessHeapFlagsText(entry->Flags & ~HEAP_CLASS_MASK))->Buffer);
PhSetListViewSubItem(Context->ListViewHandle, lvItemIndex, 6, PhGetProcessHeapClassText(entry->Flags & HEAP_CLASS_MASK));
switch (entry->Signature)
{
case RTL_HEAP_SIGNATURE:
{
switch (entry->HeapFrontEndType)
{
case 1:
PhSetListViewSubItem(Context->ListViewHandle, lvItemIndex, 7, L"NT Heap (Lookaside)");
break;
case 2:
PhSetListViewSubItem(Context->ListViewHandle, lvItemIndex, 7, L"NT Heap (LFH)");
break;
default:
PhSetListViewSubItem(Context->ListViewHandle, lvItemIndex, 7, L"NT Heap");
break;
}
}
break;
case RTL_HEAP_SEGMENT_SIGNATURE:
{
switch (entry->HeapFrontEndType)
{
case 1:
PhSetListViewSubItem(Context->ListViewHandle, lvItemIndex, 7, L"Segment Heap (Lookaside)");
break;
case 2:
PhSetListViewSubItem(Context->ListViewHandle, lvItemIndex, 7, L"Segment Heap (LFH)");
break;
default:
PhSetListViewSubItem(Context->ListViewHandle, lvItemIndex, 7, L"Segment Heap");
break;
}
}
break;
}
}
PhDereferenceObject(capturedHeapInfoString);
}
else
{
PhShowError2(
Context->WindowHandle,
L"Unable to query 32bit heap information.",
L"%s",
L"The 32-bit version of System Informer could not be located."
);
goto CleanupExit;
}
}
else
{
#endif
PPH_PROCESS_DEBUG_HEAP_INFORMATION heapDebugInfo;
status = PhQueryProcessHeapInformation(
clientProcessId,
&heapDebugInfo
);
if (!NT_SUCCESS(status))
{
PhShowStatus(Context->WindowHandle, L"Unable to query heap information.", status, 0);
goto CleanupExit;
}
Context->DebugBuffer = heapDebugInfo;
Context->ProcessHeap = heapDebugInfo->DefaultHeap;
for (ULONG i = 0; i < heapDebugInfo->NumberOfHeaps; i++)
{
PPH_PROCESS_DEBUG_HEAP_ENTRY entry = &heapDebugInfo->Heaps[i];
INT lvItemIndex;
WCHAR value[PH_INT64_STR_LEN_1];
PhPrintUInt32(value, i + 1);
lvItemIndex = PhAddListViewItem(Context->ListViewHandle, MAXINT, value, entry);
PhPrintPointer(value, entry->BaseAddress);
PhSetListViewSubItem(Context->ListViewHandle, lvItemIndex, 1, value);
PhSetListViewSubItem(Context->ListViewHandle, lvItemIndex, 2, PhaFormatSize(entry->BytesAllocated, sizesInBytes ? 0 : ULONG_MAX)->Buffer);
PhSetListViewSubItem(Context->ListViewHandle, lvItemIndex, 3, PhaFormatSize(entry->BytesCommitted, sizesInBytes ? 0 : ULONG_MAX)->Buffer);
PhSetListViewSubItem(Context->ListViewHandle, lvItemIndex, 4, PhaFormatUInt64(entry->NumberOfEntries, TRUE)->Buffer);
PhSetListViewSubItem(Context->ListViewHandle, lvItemIndex, 5, PH_AUTO_T(PH_STRING, PhGetProcessHeapFlagsText(entry->Flags & ~HEAP_CLASS_MASK))->Buffer);
PhSetListViewSubItem(Context->ListViewHandle, lvItemIndex, 6, PhGetProcessHeapClassText(entry->Flags & HEAP_CLASS_MASK));
switch (entry->Signature)
{
case RTL_HEAP_SIGNATURE:
{
switch (entry->HeapFrontEndType)
{
case 1:
PhSetListViewSubItem(Context->ListViewHandle, lvItemIndex, 7, L"NT Heap (Lookaside)");
break;
case 2:
PhSetListViewSubItem(Context->ListViewHandle, lvItemIndex, 7, L"NT Heap (LFH)");
break;
default:
PhSetListViewSubItem(Context->ListViewHandle, lvItemIndex, 7, L"NT Heap");
break;
}
}
break;
case RTL_HEAP_SEGMENT_SIGNATURE:
{
switch (entry->HeapFrontEndType)
{
case 1:
PhSetListViewSubItem(Context->ListViewHandle, lvItemIndex, 7, L"Segment Heap (Lookaside)");
break;
case 2:
PhSetListViewSubItem(Context->ListViewHandle, lvItemIndex, 7, L"Segment Heap (LFH)");
break;
default:
PhSetListViewSubItem(Context->ListViewHandle, lvItemIndex, 7, L"Segment Heap");
break;
}
}
break;
}
}
#ifdef _WIN64
}
#endif
CleanupExit:
PhFreeProcessReflection(&reflectionInfo);
if (processHandle)
NtClose(processHandle);
if (powerRequestHandle)
PhDestroyExecutionRequiredRequest(powerRequestHandle);
ExtendedListView_SortItems(Context->ListViewHandle);
ExtendedListView_SetRedraw(Context->ListViewHandle, TRUE);
}
VOID PhpSetProcessHeapsWindowText(
_In_ HWND WindowHandle,
_In_ PCWSTR Title,
_In_ PPH_PROCESS_ITEM ProcessItem
)
{
PH_FORMAT format[5];
WCHAR formatBuffer[260];
PhInitFormatS(&format[0], Title);
PhInitFormatSR(&format[1], ProcessItem->ProcessName->sr);
PhInitFormatS(&format[2], L" (");
PhInitFormatU(&format[3], HandleToUlong(ProcessItem->ProcessId));
PhInitFormatC(&format[4], L')');
if (PhFormatToBuffer(
format,
RTL_NUMBER_OF(format),
formatBuffer,
sizeof(formatBuffer),
NULL
))
{
PhSetWindowText(WindowHandle, formatBuffer);
}
}
INT_PTR CALLBACK PhpProcessHeapsDlgProc(
_In_ HWND hwndDlg,
_In_ UINT uMsg,
_In_ WPARAM wParam,
_In_ LPARAM lParam
)
{
PPH_PROCESS_HEAPS_CONTEXT context = NULL;
if (uMsg == WM_INITDIALOG)
{
context = (PPH_PROCESS_HEAPS_CONTEXT)lParam;
PhSetWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT, context);
}
else
{
context = PhGetWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT);
}
if (!context)
return FALSE;
switch (uMsg)
{
case WM_INITDIALOG:
{
context->WindowHandle = hwndDlg;
context->ListViewHandle = GetDlgItem(hwndDlg, IDC_LIST);
PhSetApplicationWindowIcon(hwndDlg);
PhpSetProcessHeapsWindowText(hwndDlg, L"Heaps - ", context->ProcessItem);
PhSetListViewStyle(context->ListViewHandle, TRUE, TRUE);
PhSetControlTheme(context->ListViewHandle, L"explorer");
PhAddListViewColumn(context->ListViewHandle, 0, 0, 0, LVCFMT_LEFT, 40, L"#");
PhAddListViewColumn(context->ListViewHandle, 1, 1, 1, LVCFMT_LEFT, 100, L"Address");
PhAddListViewColumn(context->ListViewHandle, 2, 2, 2, LVCFMT_LEFT, 120, L"Used");
PhAddListViewColumn(context->ListViewHandle, 3, 3, 3, LVCFMT_LEFT, 120, L"Committed");
PhAddListViewColumn(context->ListViewHandle, 4, 4, 4, LVCFMT_LEFT, 80, L"Entries");
PhAddListViewColumn(context->ListViewHandle, 5, 5, 5, LVCFMT_LEFT, 80, L"Flags");
PhAddListViewColumn(context->ListViewHandle, 6, 6, 6, LVCFMT_LEFT, 80, L"Class");
PhAddListViewColumn(context->ListViewHandle, 7, 7, 7, LVCFMT_LEFT, 80, L"Type");
PhSetExtendedListView(context->ListViewHandle);
ExtendedListView_SetContext(context->ListViewHandle, context);
ExtendedListView_SetCompareFunction(context->ListViewHandle, 1, PhpHeapAddressCompareFunction);
ExtendedListView_SetCompareFunction(context->ListViewHandle, 2, PhpHeapUsedCompareFunction);
ExtendedListView_SetCompareFunction(context->ListViewHandle, 3, PhpHeapCommittedCompareFunction);
ExtendedListView_SetCompareFunction(context->ListViewHandle, 4, PhpHeapEntriesCompareFunction);
ExtendedListView_SetItemFontFunction(context->ListViewHandle, PhpHeapFontFunction);
PhLoadListViewColumnsFromSetting(SETTING_SEGMENT_HEAP_LIST_VIEW_COLUMNS, context->ListViewHandle);
PhLoadListViewSortColumnsFromSetting(SETTING_SEGMENT_HEAP_LIST_VIEW_SORT, context->ListViewHandle);
PhInitializeLayoutManager(&context->LayoutManager, hwndDlg);
PhAddLayoutItem(&context->LayoutManager, context->ListViewHandle, NULL, PH_ANCHOR_ALL);
PhAddLayoutItem(&context->LayoutManager, GetDlgItem(hwndDlg, IDC_SIZESINBYTES), NULL, PH_ANCHOR_LEFT | PH_ANCHOR_BOTTOM);
PhAddLayoutItem(&context->LayoutManager, GetDlgItem(hwndDlg, IDC_REFRESH), NULL, PH_ANCHOR_RIGHT | PH_ANCHOR_BOTTOM);
PhAddLayoutItem(&context->LayoutManager, GetDlgItem(hwndDlg, IDCANCEL), NULL, PH_ANCHOR_RIGHT | PH_ANCHOR_BOTTOM);
if (PhValidWindowPlacementFromSetting(SETTING_SEGMENT_HEAP_WINDOW_POSITION))
PhLoadWindowPlacementFromSetting(SETTING_SEGMENT_HEAP_WINDOW_POSITION, SETTING_SEGMENT_HEAP_WINDOW_SIZE, hwndDlg);
else
PhCenterWindow(hwndDlg, context->ParentWindowHandle);
PhInitializeWindowTheme(hwndDlg, PhEnableThemeSupport);
}
break;
case WM_DESTROY:
{
PhRemoveWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT);
PhSaveListViewSortColumnsToSetting(SETTING_SEGMENT_HEAP_LIST_VIEW_SORT, context->ListViewHandle);
PhSaveListViewColumnsToSetting(SETTING_SEGMENT_HEAP_LIST_VIEW_COLUMNS, context->ListViewHandle);
PhSaveWindowPlacementToSetting(SETTING_SEGMENT_HEAP_WINDOW_POSITION, SETTING_SEGMENT_HEAP_WINDOW_SIZE, hwndDlg);
PhDeleteLayoutManager(&context->LayoutManager);
if (context->BoldFont)
{
DeleteFont(context->BoldFont);
context->BoldFont = NULL;
}
if (context->DebugBuffer)
{
PhFree(context->DebugBuffer);
context->DebugBuffer = NULL;
}
if (context->ProcessItem)
{
PhDereferenceObject(context->ProcessItem);
context->ProcessItem = NULL;
}
PhFree(context);
}
break;
case WM_SHOWWINDOW:
{
if (!context->Initialized)
{
PostMessage(context->WindowHandle, WM_COMMAND, MAKEWPARAM(IDC_REFRESH, 0), 0);
context->Initialized = TRUE;
}
}
break;
case WM_COMMAND:
{
switch (GET_WM_COMMAND_ID(wParam, lParam))
{
case IDCANCEL:
EndDialog(hwndDlg, IDOK);
break;
case IDC_SIZESINBYTES:
{
BOOLEAN sizesInBytes = Button_GetCheck(GET_WM_COMMAND_HWND(wParam, lParam)) == BST_CHECKED;
INT index = -1;
ExtendedListView_SetRedraw(context->ListViewHandle, FALSE);
while ((index = ListView_GetNextItem(context->ListViewHandle, index, LVNI_ALL)) != -1)
{
PPH_STRING usedString = NULL;
PPH_STRING committedString = NULL;
if (context->IsWow64Process)
{
PPH_PROCESS_DEBUG_HEAP_ENTRY32 heapInfo;
if (PhGetListViewItemParam(context->ListViewHandle, index, &heapInfo))
{
usedString = PhFormatSize(heapInfo->BytesAllocated, sizesInBytes ? 0 : ULONG_MAX);
committedString = PhFormatSize(heapInfo->BytesCommitted, sizesInBytes ? 0 : ULONG_MAX);
}
}
else
{
PPH_PROCESS_DEBUG_HEAP_ENTRY heapInfo;
if (PhGetListViewItemParam(context->ListViewHandle, index, &heapInfo))
{
usedString = PhFormatSize(heapInfo->BytesAllocated, sizesInBytes ? 0 : ULONG_MAX);
committedString = PhFormatSize(heapInfo->BytesCommitted, sizesInBytes ? 0 : ULONG_MAX);
}
}
if (usedString)
{
PhSetListViewSubItem(context->ListViewHandle, index, 2, usedString->Buffer);
PhDereferenceObject(usedString);
}
if (committedString)
{
PhSetListViewSubItem(context->ListViewHandle, index, 3, committedString->Buffer);
PhDereferenceObject(committedString);
}
}
ExtendedListView_SetRedraw(context->ListViewHandle, TRUE);
}
break;
case IDC_REFRESH:
{
PhpEnumerateProcessHeaps(context);
}
break;
}
}
break;
case WM_NOTIFY:
{
PhHandleListViewNotifyForCopy(lParam, context->ListViewHandle);
REFLECT_MESSAGE_DLG(hwndDlg, context->ListViewHandle, uMsg, wParam, lParam);
}
break;
case WM_DPICHANGED:
{
PhLayoutManagerUpdate(&context->LayoutManager, LOWORD(wParam));
PhLayoutManagerLayout(&context->LayoutManager);
}
break;
case WM_SIZE:
{
PhLayoutManagerLayout(&context->LayoutManager);
}
break;
case WM_CONTEXTMENU:
{
if ((HWND)wParam == context->ListViewHandle)
{
POINT point;
PPH_PROCESS_DEBUG_HEAP_ENTRY heapInfo;
PPH_EMENU menu;
INT selectedCount;
PPH_EMENU_ITEM menuItem;
point.x = GET_X_LPARAM(lParam);
point.y = GET_Y_LPARAM(lParam);
if (point.x == -1 && point.y == -1)
PhGetListViewContextMenuPoint(context->ListViewHandle, &point);
selectedCount = ListView_GetSelectedCount(context->ListViewHandle);
heapInfo = PhGetSelectedListViewItemParam(context->ListViewHandle);
if (selectedCount != 0)
{
menu = PhCreateEMenu();
//PhInsertEMenuItem(menu, PhCreateEMenuItem(selectedCount != 1 ? PH_EMENU_DISABLED : 0, 1, L"Destroy", NULL, NULL), ULONG_MAX);
//PhInsertEMenuItem(menu, PhCreateEMenuSeparator(), ULONG_MAX);
PhInsertEMenuItem(menu, PhCreateEMenuItem(0, USHRT_MAX, L"Copy\bCtrl+C", NULL, NULL), ULONG_MAX);
PhInsertCopyListViewEMenuItem(menu, USHRT_MAX, context->ListViewHandle);
menuItem = PhShowEMenu(
menu,
context->ListViewHandle,
PH_EMENU_SHOW_LEFTRIGHT,
PH_ALIGN_LEFT | PH_ALIGN_TOP,
point.x,
point.y
);
if (menuItem)
{
if (PhHandleCopyListViewEMenuItem(menuItem))
break;
switch (menuItem->Id)
{
case 1:
//if (PhpDestroyHeap(hwndDlg, context->ProcessItem->ProcessId, heapInfo->BaseAddress))
// ListView_DeleteItem(context->ListViewHandle, PhFindListViewItemByParam(context->ListViewHandle, -1, heapInfo));
//break;
case USHRT_MAX:
PhCopyListView(context->ListViewHandle);
break;
}
}
PhDestroyEMenu(menu);
}
}
}
break;
case WM_CTLCOLORBTN:
return HANDLE_WM_CTLCOLORBTN(hwndDlg, wParam, lParam, PhWindowThemeControlColor);
case WM_CTLCOLORDLG:
return HANDLE_WM_CTLCOLORDLG(hwndDlg, wParam, lParam, PhWindowThemeControlColor);
case WM_CTLCOLORSTATIC:
return HANDLE_WM_CTLCOLORSTATIC(hwndDlg, wParam, lParam, PhWindowThemeControlColor);
}
return FALSE;
}
//NTSTATUS PhGetProcessHeapCompatibilityInformation(
// _In_ HANDLE ProcessHandle,
// _In_ PVOID HeapAddress,
// _In_ ULONG IsWow64Process,
// _Out_ ULONG* HeapCompatibility
// )
//{
// NTSTATUS status = STATUS_UNSUCCESSFUL;
// ULONG frontEndHeapType = ULONG_MAX;
//
// if (WindowsVersion >= WINDOWS_10_20H1)
// {
// status = PhReadVirtualMemory(
// ProcessHandle,
// PTR_ADD_OFFSET(HeapAddress, IsWow64Process ? 0xEA : 0x1A2),
// &frontEndHeapType,
// sizeof(ULONG),
// NULL
// );
// }
//
// if (NT_SUCCESS(status))
// {
// if (HeapCompatibility)
// *HeapCompatibility = frontEndHeapType;
// }
//
// return status;
//}
//
//NTSTATUS PhGetProcessHeapCounters(
// _In_ HANDLE ProcessHandle,
// _In_ PVOID HeapAddress,
// _In_ ULONG IsWow64Process,
// _Out_ PVOID *HeapCounters
// )
//{
// NTSTATUS status = STATUS_UNSUCCESSFUL;
// PVOID heapCounters;
// ULONG heapCountersLength;
//
// if (IsWow64Process)
// heapCountersLength = sizeof(HEAP_COUNTERS32);
// else
// heapCountersLength = sizeof(HEAP_COUNTERS);
//
// heapCounters = PhAllocate(heapCountersLength);
// memset(heapCounters, 0, heapCountersLength);
//
// if (WindowsVersion >= WINDOWS_10_20H1)
// {
// status = PhReadVirtualMemory(
// ProcessHandle,
// PTR_ADD_OFFSET(HeapAddress, IsWow64Process ? 0x1F4 : 0x238),
// heapCounters,
// heapCountersLength,
// NULL
// );
// }
//
// if (NT_SUCCESS(status))
// {
// if (HeapCounters)
// *HeapCounters = heapCounters;
// return status;
// }
//
// PhFree(heapCounters);
// return status;
//}
//
//NTSTATUS PhGetProcessSegmentHeapCounters(
// _In_ HANDLE ProcessHandle,
// _In_ PVOID HeapAddress,
// _In_ ULONG IsWow64Process,
// _Out_ PVOID *HeapCounters
// )
//{
// NTSTATUS status = STATUS_UNSUCCESSFUL;
// PVOID heapCounters;
// ULONG heapCountersLength;
//
// if (IsWow64Process)
// heapCountersLength = sizeof(HEAP_RUNTIME_MEMORY_STATS32);
// else
// heapCountersLength = sizeof(HEAP_RUNTIME_MEMORY_STATS);
//
// heapCounters = PhAllocate(heapCountersLength);
// memset(heapCounters, 0, heapCountersLength);
//
// if (WindowsVersion >= WINDOWS_10_20H1)
// {
// status = PhReadVirtualMemory(
// ProcessHandle,
// PTR_ADD_OFFSET(HeapAddress, IsWow64Process ? 0x80 : 0x80),
// heapCounters,
// heapCountersLength,
// NULL
// );
// }
//
// if (NT_SUCCESS(status))
// {
// if (HeapCounters)
// *HeapCounters = heapCounters;
// return status;
// }
//
// PhFree(heapCounters);
// return status;
//}
//
//NTSTATUS PhGetProcessHeaps(
// _In_ HANDLE ProcessHandle
// )
//{
// PROCESS_BASIC_INFORMATION basicInfo;
// ULONG numberOfHeaps;
// PVOID processHeapsPtr;
// PVOID* processHeaps;
//#ifdef _WIN64
// PVOID peb32;
// ULONG processHeapsPtr32;
// ULONG* processHeaps32;
//#endif
//
// if (NT_SUCCESS(PhGetProcessBasicInformation(ProcessHandle, &basicInfo)) && basicInfo.PebBaseAddress != 0)
// {
// if (NT_SUCCESS(PhReadVirtualMemory(
// ProcessHandle,
// PTR_ADD_OFFSET(basicInfo.PebBaseAddress, UFIELD_OFFSET(PEB, NumberOfHeaps)),
// &numberOfHeaps,
// sizeof(ULONG),
// NULL
// )) && numberOfHeaps < 1000)
// {
// processHeaps = PhAllocateZero(numberOfHeaps * sizeof(PVOID));
//
// if (
// NT_SUCCESS(PhReadVirtualMemory(ProcessHandle, PTR_ADD_OFFSET(basicInfo.PebBaseAddress, UFIELD_OFFSET(PEB, ProcessHeaps)), &processHeapsPtr, sizeof(PVOID), NULL)) &&
// NT_SUCCESS(PhReadVirtualMemory(ProcessHandle, processHeapsPtr, processHeaps, numberOfHeaps * sizeof(PVOID), NULL))
// )
// {
// for (ULONG i = 0; i < numberOfHeaps; i++)
// {
// ULONG signature = ULONG_MAX;
//
// if (!NT_SUCCESS(PhGetProcessHeapSignature(ProcessHandle, processHeaps[i], FALSE, &signature)))
// continue;
//
// if (signature == RTL_HEAP_SIGNATURE)
// {
// PHEAP_COUNTERS counters;
//
// if (NT_SUCCESS(PhGetProcessHeapCounters(ProcessHandle, processHeaps[i], FALSE, &counters)))
// {
// dprintf(
// "NT-%lu - 0x%I64x - committed: %lu - allocated: %lu - count: %lu\n",
// i + 1,
// processHeaps[i],
// counters->TotalMemoryCommitted,
// counters->LastPolledSize,
// counters->TotalSegments
// );
//
// PhFree(counters);
// }
// }
// else if (signature == RTL_HEAP_SEGMENT_SIGNATURE)
// {
// dprintf("Segment Heap");
// }
// }
// }
//
// PhFree(processHeaps);
// }
// }
//#ifdef _WIN64
//
// if (NT_SUCCESS(PhGetProcessPeb32(ProcessHandle, &peb32)) && peb32 != 0)
// {
// if (NT_SUCCESS(PhReadVirtualMemory(
// ProcessHandle,
// PTR_ADD_OFFSET(peb32, UFIELD_OFFSET(PEB32, NumberOfHeaps)),
// &numberOfHeaps,
// sizeof(ULONG),
// NULL
// )) && numberOfHeaps < 1000)
// {
// processHeaps32 = PhAllocateZero(numberOfHeaps * sizeof(ULONG));
//
// if (
// NT_SUCCESS(PhReadVirtualMemory(ProcessHandle, PTR_ADD_OFFSET(peb32, UFIELD_OFFSET(PEB32, ProcessHeaps)), &processHeapsPtr32, sizeof(ULONG), NULL)) &&
// NT_SUCCESS(PhReadVirtualMemory(ProcessHandle, UlongToPtr(processHeapsPtr32), processHeaps32, numberOfHeaps * sizeof(ULONG), NULL))
// )
// {
// for (ULONG i = 0; i < numberOfHeaps; i++)
// {
// ULONG signature = ULONG_MAX;
//
// if (!NT_SUCCESS(PhGetProcessHeapSignature(ProcessHandle, UlongToPtr(processHeaps32[i]), TRUE, &signature)))
// continue;
//
// if (signature == RTL_HEAP_SIGNATURE)
// {
// PHEAP_COUNTERS32 counters;
//
// if (NT_SUCCESS(PhGetProcessHeapCounters(ProcessHandle, UlongToPtr(processHeaps32[i]), TRUE, &counters)))
// {
// PhFree(counters);
// }
// }
// else if (signature == RTL_HEAP_SEGMENT_SIGNATURE)
// {
// dprintf("Segment Heap\n");
// }
// }
// }
//
// PhFree(processHeaps32);
// }
// }
//#endif
//
// return STATUS_SUCCESS;
//}
//
//BOOLEAN PhpDestroyHeap(
// _In_ HWND hWnd,
// _In_ HANDLE ProcessId,
// _In_ PVOID HeapHandle
// )
//{
// NTSTATUS status;
// BOOLEAN cont = FALSE;
// HANDLE processHandle;
// HANDLE threadHandle;
//
// if (PhGetIntegerSetting(SETTING_ENABLE_WARNINGS))
// {
// cont = PhShowConfirmMessage(
// hWnd,
// L"destroy",
// L"the selected heap",
// L"Destroying heaps may cause the process to crash.",
// FALSE
// );
// }
// else
// {
// cont = TRUE;
// }
//
// if (!cont)
// return FALSE;
//
// if (NT_SUCCESS(status = PhOpenProcess(
// &processHandle,
// PROCESS_CREATE_THREAD,
// ProcessId
// )))
// {
// if (WindowsVersion >= WINDOWS_VISTA)
// {
// status = RtlCreateUserThread(
// processHandle,
// NULL,
// FALSE,
// 0,
// 0,
// 0,
// PhGetModuleProcAddress(L"ntdll.dll", "RtlDestroyHeap"),
// HeapHandle,
// &threadHandle,
// NULL
// );
// }
// else
// {
// if (!(threadHandle = CreateRemoteThread(
// processHandle,
// NULL,
// 0,
// PhGetModuleProcAddress(L"ntdll.dll", "RtlDestroyHeap"),
// HeapHandle,
// 0,
// NULL
// )))
// {
// status = PhGetLastWin32ErrorAsNtStatus();
// }
// }
//
// if (NT_SUCCESS(status))
// NtClose(threadHandle);
//
// NtClose(processHandle);
// }
//
// if (!NT_SUCCESS(status))
// {
// PhShowStatus(hWnd, L"Unable to destroy the heap", status, 0);
// return FALSE;
// }
//
// return TRUE;
//}
//
//#include
//
//BOOLEAN PhpEnumerateHeapsWin32(
// _In_ PPROCESS_HEAPS_CONTEXT Context
// )
//{
// HANDLE snapshotHandle;
// HEAPLIST32 heapList32;
//
// snapshotHandle = CreateToolhelp32Snapshot(
// TH32CS_SNAPHEAPLIST,
// Context->ProcessItem->ProcessId
// );
//
// if (snapshotHandle == INVALID_HANDLE_VALUE)
// return FALSE;
//
// memset(&heapList32, 0, sizeof(HEAPLIST32));
// heapList32.dwSize = sizeof(HEAPLIST32);
//
// if (Heap32ListFirst(snapshotHandle, &heapList32))
// {
// do
// {
// HEAPENTRY32 entry;
//
// memset(&entry, 0, sizeof(HEAPENTRY32));
// entry.dwSize = sizeof(HEAPENTRY32);
//
// dprintf("\nHeap ID: %lu\n", heapList32.th32HeapID);
//
// if (Heap32First(&entry, Context->ProcessItem->ProcessId, heapList32.th32HeapID))
// {
// do
// {
// dprintf("Block size: %lu\n", entry.dwBlockSize);
//
// memset(&entry, 0, sizeof(HEAPENTRY32));
// entry.dwSize = sizeof(HEAPENTRY32);
// } while (Heap32Next(&entry));
// }
//
// memset(&heapList32, 0, sizeof(HEAPLIST32));
// heapList32.dwSize = sizeof(HEAPLIST32);
// } while (Heap32ListNext(snapshotHandle, &heapList32));
// }
//
// NtClose(snapshotHandle);
//}
typedef struct _PH_PROCESS_LOCKS_CONTEXT
{
HWND WindowHandle;
HWND ParentWindowHandle;
HWND ListViewHandle;
HFONT BoldFont;
union
{
BOOLEAN Flags;
struct
{
BOOLEAN Initialized : 1;
BOOLEAN IsWow64Process : 1;
BOOLEAN Spare : 6;
};
};
PPH_PROCESS_ITEM ProcessItem;
PVOID ProcessHeap;
PVOID DebugBuffer;
PH_LAYOUT_MANAGER LayoutManager;
} PH_PROCESS_LOCKS_CONTEXT, *PPH_PROCESS_LOCKS_CONTEXT;
INT_PTR CALLBACK PhProcessLocksDlgProc(
_In_ HWND WindowHandle,
_In_ UINT WindowMessage,
_In_ WPARAM wParam,
_In_ LPARAM lParam
);
VOID PhShowProcessLocksDialog(
_In_ HWND ParentWindowHandle,
_In_ PPH_PROCESS_ITEM ProcessItem
)
{
PPH_PROCESS_LOCKS_CONTEXT context;
context = PhAllocateZero(sizeof(PH_PROCESS_LOCKS_CONTEXT));
context->ParentWindowHandle = ParentWindowHandle;
context->ProcessItem = PhReferenceObject(ProcessItem);
context->IsWow64Process = !!ProcessItem->IsWow64Process;
PhDialogBox(
PhInstanceHandle,
MAKEINTRESOURCE(IDD_HEAPS),
NULL,
PhProcessLocksDlgProc,
context
);
}
_Function_class_(PH_ENUM_PROCESS_LOCKS)
NTSTATUS NTAPI PhEnumProcessLocksCallback(
_In_ ULONG NumberOfLocks,
_In_ PRTL_PROCESS_LOCK_INFORMATION Locks,
_In_opt_ PVOID Context
)
{
if (!Context) return STATUS_INVALID_PARAMETER;
PPH_PROCESS_LOCKS_CONTEXT context = Context;
NTSTATUS status;
HANDLE processHandle = NULL;
status = PhOpenProcess(
&processHandle,
PROCESS_QUERY_LIMITED_INFORMATION,
context->ProcessItem->ProcessId
);
if (!NT_SUCCESS(status))
return status;
for (ULONG i = 0; i < NumberOfLocks; i++)
{
PRTL_PROCESS_LOCK_INFORMATION entry = &Locks[i];
INT lvItemIndex;
WCHAR value[PH_INT64_STR_LEN_1];
CLIENT_ID clientId;
PPH_STRING fileName = NULL;
clientId.UniqueProcess = NULL;
clientId.UniqueThread = entry->OwningThread;
if (processHandle && entry->Address)
{
if (NT_SUCCESS(PhGetProcessMappedFileName(processHandle, entry->Address, &fileName)))
{
PhMoveReference(&fileName, PhGetFileName(fileName));
}
}
PhPrintUInt32(value, i + 1);
lvItemIndex = PhAddListViewItem(context->ListViewHandle, MAXINT, value, PhAllocateCopy(entry, sizeof(*entry)));
PhPrintPointer(value, entry->Address);
PhSetListViewSubItem(context->ListViewHandle, lvItemIndex, 2, value);
PhSetListViewSubItem(context->ListViewHandle, lvItemIndex, 3, PhGetStringOrEmpty(fileName));
PhSetListViewSubItem(context->ListViewHandle, lvItemIndex, 4, entry->OwningThread ? PH_AUTO_T(PH_STRING, PhGetClientIdName(&clientId))->Buffer : L"");
PhSetListViewSubItem(context->ListViewHandle, lvItemIndex, 5, entry->LockCount != ULONG_MAX ? PhaFormatUInt64(entry->LockCount, TRUE)->Buffer : L"");
PhSetListViewSubItem(context->ListViewHandle, lvItemIndex, 6, PhaFormatUInt64(entry->ContentionCount, TRUE)->Buffer);
PhSetListViewSubItem(context->ListViewHandle, lvItemIndex, 7, PhaFormatUInt64(entry->EntryCount, TRUE)->Buffer);
PhSetListViewSubItem(context->ListViewHandle, lvItemIndex, 8, PhaFormatUInt64(entry->RecursionCount, TRUE)->Buffer);
PhSetListViewSubItem(context->ListViewHandle, lvItemIndex, 9, PhaFormatUInt64(entry->NumberOfWaitingShared, TRUE)->Buffer);
PhSetListViewSubItem(context->ListViewHandle, lvItemIndex, 10, PhaFormatUInt64(entry->NumberOfWaitingExclusive, TRUE)->Buffer);
switch (entry->Type)
{
case RTL_CRITSECT_TYPE:
PhSetListViewSubItem(context->ListViewHandle, lvItemIndex, 1, L"Critical section");
break;
case RTL_RESOURCE_TYPE:
PhSetListViewSubItem(context->ListViewHandle, lvItemIndex, 1, L"Resource");
break;
}
}
NtClose(processHandle);
return STATUS_SUCCESS;
}
VOID PhEnumerateProcessLocks(
_In_ PPH_PROCESS_LOCKS_CONTEXT Context
)
{
NTSTATUS status;
ExtendedListView_SetRedraw(Context->ListViewHandle, FALSE);
ListView_DeleteAllItems(Context->ListViewHandle);
status = PhQueryProcessLockInformation(
Context->ProcessItem->ProcessId,
PhEnumProcessLocksCallback,
Context
);
if (!NT_SUCCESS(status))
{
PhShowStatus(Context->WindowHandle, L"Unable to query lock information.", status, 0);
return;
}
ExtendedListView_SortItems(Context->ListViewHandle);
ExtendedListView_SetRedraw(Context->ListViewHandle, TRUE);
}
INT_PTR CALLBACK PhProcessLocksDlgProc(
_In_ HWND WindowHandle,
_In_ UINT WindowMessage,
_In_ WPARAM wParam,
_In_ LPARAM lParam
)
{
PPH_PROCESS_LOCKS_CONTEXT context = NULL;
if (WindowMessage == WM_INITDIALOG)
{
context = (PPH_PROCESS_LOCKS_CONTEXT)lParam;
PhSetWindowContext(WindowHandle, PH_WINDOW_CONTEXT_DEFAULT, context);
}
else
{
context = PhGetWindowContext(WindowHandle, PH_WINDOW_CONTEXT_DEFAULT);
}
if (!context)
return FALSE;
switch (WindowMessage)
{
case WM_INITDIALOG:
{
context->WindowHandle = WindowHandle;
context->ListViewHandle = GetDlgItem(WindowHandle, IDC_LIST);
PhSetApplicationWindowIcon(WindowHandle);
PhpSetProcessHeapsWindowText(WindowHandle, L"Locks - ", context->ProcessItem);
PhSetListViewStyle(context->ListViewHandle, TRUE, TRUE);
PhSetControlTheme(context->ListViewHandle, L"explorer");
PhAddListViewColumn(context->ListViewHandle, 0, 0, 0, LVCFMT_LEFT, 40, L"#");
PhAddListViewColumn(context->ListViewHandle, 1, 1, 1, LVCFMT_LEFT, 50, L"Type");
PhAddListViewColumn(context->ListViewHandle, 2, 2, 2, LVCFMT_LEFT, 100, L"Address");
PhAddListViewColumn(context->ListViewHandle, 3, 3, 3, LVCFMT_LEFT, 120, L"Filename");
PhAddListViewColumn(context->ListViewHandle, 4, 4, 4, LVCFMT_LEFT, 120, L"Thread");
PhAddListViewColumn(context->ListViewHandle, 5, 5, 5, LVCFMT_LEFT, 80, L"Lock count");
PhAddListViewColumn(context->ListViewHandle, 6, 6, 6, LVCFMT_LEFT, 80, L"Entry count");
PhAddListViewColumn(context->ListViewHandle, 7, 7, 7, LVCFMT_LEFT, 80, L"Recursion count");
PhAddListViewColumn(context->ListViewHandle, 8, 8, 8, LVCFMT_LEFT, 80, L"Waiting shared count");
PhAddListViewColumn(context->ListViewHandle, 9, 9, 9, LVCFMT_LEFT, 80, L"Waiting exclusive count");
PhSetExtendedListView(context->ListViewHandle);
ExtendedListView_SetContext(context->ListViewHandle, context);
ExtendedListView_SetItemFontFunction(context->ListViewHandle, PhpHeapFontFunction);
PhLoadListViewColumnsFromSetting(SETTING_SEGMENT_LOCKS_LIST_VIEW_COLUMNS, context->ListViewHandle);
PhLoadListViewSortColumnsFromSetting(SETTING_SEGMENT_LOCKS_LIST_VIEW_SORT, context->ListViewHandle);
PhInitializeLayoutManager(&context->LayoutManager, WindowHandle);
PhAddLayoutItem(&context->LayoutManager, context->ListViewHandle, NULL, PH_ANCHOR_ALL);
PhAddLayoutItem(&context->LayoutManager, GetDlgItem(WindowHandle, IDC_SIZESINBYTES), NULL, PH_ANCHOR_LEFT | PH_ANCHOR_BOTTOM);
PhAddLayoutItem(&context->LayoutManager, GetDlgItem(WindowHandle, IDC_REFRESH), NULL, PH_ANCHOR_RIGHT | PH_ANCHOR_BOTTOM);
PhAddLayoutItem(&context->LayoutManager, GetDlgItem(WindowHandle, IDCANCEL), NULL, PH_ANCHOR_RIGHT | PH_ANCHOR_BOTTOM);
if (PhValidWindowPlacementFromSetting(SETTING_SEGMENT_LOCKS_WINDOW_POSITION))
PhLoadWindowPlacementFromSetting(SETTING_SEGMENT_LOCKS_WINDOW_POSITION, SETTING_SEGMENT_LOCKS_WINDOW_SIZE, WindowHandle);
else
PhCenterWindow(WindowHandle, context->ParentWindowHandle);
PhInitializeWindowTheme(WindowHandle, PhEnableThemeSupport);
ShowWindow(GetDlgItem(WindowHandle, IDC_SIZESINBYTES), SW_HIDE);
}
break;
case WM_DESTROY:
{
PhRemoveWindowContext(WindowHandle, PH_WINDOW_CONTEXT_DEFAULT);
PhSaveListViewSortColumnsToSetting(SETTING_SEGMENT_LOCKS_LIST_VIEW_SORT, context->ListViewHandle);
PhSaveListViewColumnsToSetting(SETTING_SEGMENT_LOCKS_LIST_VIEW_COLUMNS, context->ListViewHandle);
PhSaveWindowPlacementToSetting(SETTING_SEGMENT_LOCKS_WINDOW_POSITION, SETTING_SEGMENT_LOCKS_WINDOW_SIZE, WindowHandle);
PhDeleteLayoutManager(&context->LayoutManager);
if (context->BoldFont)
{
DeleteFont(context->BoldFont);
context->BoldFont = NULL;
}
if (context->DebugBuffer)
{
PhFree(context->DebugBuffer);
context->DebugBuffer = NULL;
}
if (context->ProcessItem)
{
PhDereferenceObject(context->ProcessItem);
context->ProcessItem = NULL;
}
PhFree(context);
}
break;
case WM_SHOWWINDOW:
{
if (!context->Initialized)
{
PostMessage(context->WindowHandle, WM_COMMAND, MAKEWPARAM(IDC_REFRESH, 0), 0);
context->Initialized = TRUE;
}
}
break;
case WM_COMMAND:
{
switch (GET_WM_COMMAND_ID(wParam, lParam))
{
case IDCANCEL:
EndDialog(WindowHandle, IDOK);
break;
case IDC_SIZESINBYTES:
{
BOOLEAN sizesInBytes = Button_GetCheck(GET_WM_COMMAND_HWND(wParam, lParam)) == BST_CHECKED;
INT index = -1;
ExtendedListView_SetRedraw(context->ListViewHandle, FALSE);
while ((index = ListView_GetNextItem(context->ListViewHandle, index, LVNI_ALL)) != -1)
{
PPH_STRING usedString = NULL;
PPH_STRING committedString = NULL;
if (context->IsWow64Process)
{
PPH_PROCESS_DEBUG_HEAP_ENTRY32 heapInfo;
if (PhGetListViewItemParam(context->ListViewHandle, index, &heapInfo))
{
usedString = PhFormatSize(heapInfo->BytesAllocated, sizesInBytes ? 0 : ULONG_MAX);
committedString = PhFormatSize(heapInfo->BytesCommitted, sizesInBytes ? 0 : ULONG_MAX);
}
}
else
{
PPH_PROCESS_DEBUG_HEAP_ENTRY heapInfo;
if (PhGetListViewItemParam(context->ListViewHandle, index, &heapInfo))
{
usedString = PhFormatSize(heapInfo->BytesAllocated, sizesInBytes ? 0 : ULONG_MAX);
committedString = PhFormatSize(heapInfo->BytesCommitted, sizesInBytes ? 0 : ULONG_MAX);
}
}
if (usedString)
{
PhSetListViewSubItem(context->ListViewHandle, index, 2, usedString->Buffer);
PhDereferenceObject(usedString);
}
if (committedString)
{
PhSetListViewSubItem(context->ListViewHandle, index, 3, committedString->Buffer);
PhDereferenceObject(committedString);
}
}
ExtendedListView_SetRedraw(context->ListViewHandle, TRUE);
}
break;
case IDC_REFRESH:
{
PhEnumerateProcessLocks(context);
}
break;
}
}
break;
case WM_NOTIFY:
{
PhHandleListViewNotifyForCopy(lParam, context->ListViewHandle);
REFLECT_MESSAGE_DLG(WindowHandle, context->ListViewHandle, WindowMessage, wParam, lParam);
}
break;
case WM_DPICHANGED:
{
PhLayoutManagerUpdate(&context->LayoutManager, LOWORD(wParam));
PhLayoutManagerLayout(&context->LayoutManager);
}
break;
case WM_SIZE:
{
PhLayoutManagerLayout(&context->LayoutManager);
}
break;
case WM_CONTEXTMENU:
{
if ((HWND)wParam == context->ListViewHandle)
{
POINT point;
PPH_PROCESS_DEBUG_HEAP_ENTRY heapInfo;
PPH_EMENU menu;
INT selectedCount;
PPH_EMENU_ITEM menuItem;
point.x = GET_X_LPARAM(lParam);
point.y = GET_Y_LPARAM(lParam);
if (point.x == -1 && point.y == -1)
PhGetListViewContextMenuPoint(context->ListViewHandle, &point);
selectedCount = ListView_GetSelectedCount(context->ListViewHandle);
heapInfo = PhGetSelectedListViewItemParam(context->ListViewHandle);
if (selectedCount != 0)
{
menu = PhCreateEMenu();
PhInsertEMenuItem(menu, PhCreateEMenuItem(0, USHRT_MAX, L"Copy\bCtrl+C", NULL, NULL), ULONG_MAX);
PhInsertCopyListViewEMenuItem(menu, USHRT_MAX, context->ListViewHandle);
menuItem = PhShowEMenu(
menu,
context->ListViewHandle,
PH_EMENU_SHOW_LEFTRIGHT,
PH_ALIGN_LEFT | PH_ALIGN_TOP,
point.x,
point.y
);
if (menuItem)
{
if (PhHandleCopyListViewEMenuItem(menuItem))
break;
switch (menuItem->Id)
{
case 1:
case USHRT_MAX:
PhCopyListView(context->ListViewHandle);
break;
}
}
PhDestroyEMenu(menu);
}
}
}
break;
case WM_CTLCOLORBTN:
return HANDLE_WM_CTLCOLORBTN(WindowHandle, wParam, lParam, PhWindowThemeControlColor);
case WM_CTLCOLORDLG:
return HANDLE_WM_CTLCOLORDLG(WindowHandle, wParam, lParam, PhWindowThemeControlColor);
case WM_CTLCOLORSTATIC:
return HANDLE_WM_CTLCOLORSTATIC(WindowHandle, wParam, lParam, PhWindowThemeControlColor);
}
return FALSE;
}
================================================
FILE: SystemInformer/hidnproc.c
================================================
/*
* Copyright (c) 2022 Winsider Seminars & Solutions, Inc. All rights reserved.
*
* This file is part of System Informer.
*
* Authors:
*
* wj32 2010-2013
* dmex 2019-2023
*
*/
/*
* There are two methods of zombie process detection implemented in this module.
*
* Brute Force. This attempts to open all possible PIDs within a certain range
* in order to find processes which have been unlinked from the active process
* list (EPROCESS.ActiveProcessLinks). This method is not effective when
* either NtOpenProcess is hooked or PsLookupProcessByProcessId is hooked
* (KSystemInformer cannot bypass this).
*
* CSR Handles. This enumerates handles in all running CSR processes, and works
* even when a process has been unlinked from the active process list and
* has been removed from the client ID table (PspCidTable). However, the method
* does not detect native executables since CSR is not notified about them.
* Some rootkits hook NtQuerySystemInformation in order to modify the returned
* handle information; System Informer bypasses this by using KSystemInformer,
* which calls ExEnumHandleTable directly. Note that both process and thread
* handles are examined.
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
INT_PTR CALLBACK PhpZombieProcessesDlgProc(
_In_ HWND hwndDlg,
_In_ UINT uMsg,
_In_ WPARAM wParam,
_In_ LPARAM lParam
);
COLORREF NTAPI PhpZombieProcessesColorFunction(
_In_ INT Index,
_In_ PVOID Param,
_In_opt_ PVOID Context
);
BOOLEAN NTAPI PhpZombieProcessesCallback(
_In_ PPH_ZOMBIE_PROCESS_ENTRY Process,
_In_opt_ PVOID Context
);
VOID PhZombieProcessesUpdateListView(
_In_ PPH_LIST UpdateList
);
NTSTATUS PhpCreateProcessItemForZombieProcess(
_In_ HWND WindowHandle,
_In_ PPH_ZOMBIE_PROCESS_ENTRY Entry,
_Out_ PPH_PROCESS_ITEM* ProcessItem
);
static HWND PhZombieProcessesWindowHandle = NULL;
static HWND PhZombieProcessesListViewHandle = NULL;
static PH_LAYOUT_MANAGER WindowLayoutManager;
static RECT MinimumSize;
static PH_ZOMBIE_PROCESS_METHOD ProcessesMethod;
static PPH_LIST ProcessesList = NULL;
static ULONG NumberOfZombieProcesses;
static ULONG NumberOfTerminatedProcesses;
VOID PhShowZombieProcessesDialog(
VOID
)
{
if (!PhZombieProcessesWindowHandle)
{
PhZombieProcessesWindowHandle = PhCreateDialog(
PhInstanceHandle,
MAKEINTRESOURCE(IDD_ZOMBIEPROCESSES),
NULL,
PhpZombieProcessesDlgProc,
NULL
);
}
if (!IsWindowVisible(PhZombieProcessesWindowHandle))
ShowWindow(PhZombieProcessesWindowHandle, SW_SHOW);
else
SetForegroundWindow(PhZombieProcessesWindowHandle);
}
VOID PhZombieProcessesCleanupList(
_In_opt_ PPH_LIST UpdateList
)
{
if (UpdateList)
{
for (ULONG i = 0; i < UpdateList->Count; i++)
{
PPH_ZOMBIE_PROCESS_ENTRY entry = UpdateList->Items[i];
PhClearReference(&entry->FileName);
PhFree(entry);
}
PhDereferenceObject(UpdateList);
}
{
PPH_STRING string = PhFormatString(L"%u zombie process(es), %u terminated process(es).",
NumberOfZombieProcesses, NumberOfTerminatedProcesses);
PhSetDialogItemText(PhZombieProcessesWindowHandle, IDC_DESCRIPTION, string->Buffer);
InvalidateRect(GetDlgItem(PhZombieProcessesWindowHandle, IDC_DESCRIPTION), NULL, TRUE);
PhDereferenceObject(string);
}
}
_Function_class_(USER_THREAD_START_ROUTINE)
NTSTATUS PhZombieProcessesThread(
_In_ PVOID Context
)
{
NTSTATUS status;
PPH_LIST processList;
ExtendedListView_SetRedraw(PhZombieProcessesListViewHandle, FALSE);
ListView_DeleteAllItems(PhZombieProcessesListViewHandle);
ExtendedListView_SetRedraw(PhZombieProcessesListViewHandle, TRUE);
processList = PhCreateList(100);
status = PhEnumZombieProcesses(
ProcessesMethod,
PhpZombieProcessesCallback,
processList
);
if (PhZombieProcessesWindowHandle)
PostMessage(PhZombieProcessesWindowHandle, WM_PH_UPDATE_DIALOG, status, (LPARAM)processList);
else
PhZombieProcessesCleanupList(processList);
return STATUS_SUCCESS;
}
INT_PTR CALLBACK PhpZombieProcessesDlgProc(
_In_ HWND hwndDlg,
_In_ UINT uMsg,
_In_ WPARAM wParam,
_In_ LPARAM lParam
)
{
switch (uMsg)
{
case WM_INITDIALOG:
{
HWND lvHandle;
HWND methodHandle;
PhSetApplicationWindowIcon(hwndDlg);
PhZombieProcessesListViewHandle = lvHandle = GetDlgItem(hwndDlg, IDC_PROCESSES);
methodHandle = GetDlgItem(hwndDlg, IDC_METHOD);
PhInitializeLayoutManager(&WindowLayoutManager, hwndDlg);
PhAddLayoutItem(&WindowLayoutManager, GetDlgItem(hwndDlg, IDC_INTRO), NULL, PH_ANCHOR_LEFT | PH_ANCHOR_TOP | PH_ANCHOR_RIGHT | PH_LAYOUT_FORCE_INVALIDATE);
PhAddLayoutItem(&WindowLayoutManager, lvHandle, NULL, PH_ANCHOR_ALL);
PhAddLayoutItem(&WindowLayoutManager, GetDlgItem(hwndDlg, IDC_DESCRIPTION), NULL, PH_ANCHOR_LEFT | PH_ANCHOR_RIGHT | PH_ANCHOR_BOTTOM | PH_LAYOUT_FORCE_INVALIDATE);
PhAddLayoutItem(&WindowLayoutManager, methodHandle, NULL, PH_ANCHOR_LEFT | PH_ANCHOR_BOTTOM);
PhAddLayoutItem(&WindowLayoutManager, GetDlgItem(hwndDlg, IDC_TERMINATE), NULL, PH_ANCHOR_RIGHT | PH_ANCHOR_BOTTOM);
PhAddLayoutItem(&WindowLayoutManager, GetDlgItem(hwndDlg, IDC_SAVE), NULL, PH_ANCHOR_RIGHT | PH_ANCHOR_BOTTOM);
PhAddLayoutItem(&WindowLayoutManager, GetDlgItem(hwndDlg, IDC_SCAN), NULL, PH_ANCHOR_RIGHT | PH_ANCHOR_BOTTOM);
PhAddLayoutItem(&WindowLayoutManager, GetDlgItem(hwndDlg, IDOK), NULL, PH_ANCHOR_RIGHT | PH_ANCHOR_BOTTOM);
PhSetListViewStyle(lvHandle, TRUE, TRUE);
PhSetControlTheme(lvHandle, L"explorer");
PhAddListViewColumn(lvHandle, 0, 0, 0, LVCFMT_LEFT, 320, L"Process");
PhAddListViewColumn(lvHandle, 1, 1, 1, LVCFMT_LEFT, 60, L"PID");
PhSetExtendedListView(lvHandle);
PhLoadListViewColumnsFromSetting(SETTING_ZOMBIE_PROCESSES_LIST_VIEW_COLUMNS, lvHandle);
ExtendedListView_AddFallbackColumn(lvHandle, 0);
ExtendedListView_AddFallbackColumn(lvHandle, 1);
ExtendedListView_SetItemColorFunction(lvHandle, PhpZombieProcessesColorFunction);
ComboBox_AddString(methodHandle, L"Brute force");
ComboBox_AddString(methodHandle, L"CSR handles");
ComboBox_AddString(methodHandle, L"ETW handles");
ComboBox_AddString(methodHandle, L"Process handles");
ComboBox_AddString(methodHandle, L"Registry handles");
ComboBox_AddString(methodHandle, L"Ntdll handles");
PhSelectComboBoxString(methodHandle, L"Process handles", FALSE);
MinimumSize.left = 0;
MinimumSize.top = 0;
MinimumSize.right = 330;
MinimumSize.bottom = 140;
MapDialogRect(hwndDlg, &MinimumSize);
if (PhValidWindowPlacementFromSetting(SETTING_ZOMBIE_PROCESSES_WINDOW_POSITION))
PhLoadWindowPlacementFromSetting(SETTING_ZOMBIE_PROCESSES_WINDOW_POSITION, SETTING_ZOMBIE_PROCESSES_WINDOW_SIZE, hwndDlg);
else
PhCenterWindow(hwndDlg, GetParent(hwndDlg));
EnableWindow(GetDlgItem(hwndDlg, IDC_TERMINATE), FALSE);
PhInitializeWindowTheme(hwndDlg, PhEnableThemeSupport);
}
break;
case WM_DESTROY:
{
PhSaveWindowPlacementToSetting(SETTING_ZOMBIE_PROCESSES_WINDOW_POSITION, SETTING_ZOMBIE_PROCESSES_WINDOW_SIZE, hwndDlg);
PhSaveListViewColumnsToSetting(SETTING_ZOMBIE_PROCESSES_LIST_VIEW_COLUMNS, PhZombieProcessesListViewHandle);
PhZombieProcessesCleanupList(ProcessesList);
PhZombieProcessesWindowHandle = NULL;
}
break;
case WM_COMMAND:
{
switch (GET_WM_COMMAND_ID(wParam, lParam))
{
case IDCANCEL:
case IDOK:
{
DestroyWindow(hwndDlg);
}
break;
case IDC_SCAN:
{
PPH_STRING method;
method = PH_AUTO(PhGetWindowText(GetDlgItem(hwndDlg, IDC_METHOD)));
PhZombieProcessesCleanupList(NULL);
ProcessesList = PhCreateList(40);
if (PhEqualString2(method, L"Brute force", TRUE))
ProcessesMethod = BruteForceScanMethod;
else if (PhEqualString2(method, L"CSR handles", TRUE))
ProcessesMethod = CsrHandlesScanMethod;
else if (PhEqualString2(method, L"Process handles", TRUE))
ProcessesMethod = ProcessHandleScanMethod;
else if (PhEqualString2(method, L"Registry handles", TRUE))
ProcessesMethod = RegistryScanMethod;
else if (PhEqualString2(method, L"ETW handles", TRUE))
ProcessesMethod = EtwGuidScanMethod;
else if (PhEqualString2(method, L"Ntdll handles", TRUE))
ProcessesMethod = NtdllScanMethod;
NumberOfZombieProcesses = 0;
NumberOfTerminatedProcesses = 0;
EnableWindow(GetDlgItem(hwndDlg, IDC_SCAN), FALSE);
PhCreateThread2(PhZombieProcessesThread, NULL);
}
break;
case IDC_TERMINATE:
{
PPH_ZOMBIE_PROCESS_ENTRY *entries;
ULONG numberOfEntries;
ULONG i;
PhGetSelectedListViewItemParams(PhZombieProcessesListViewHandle, &entries, &numberOfEntries);
if (numberOfEntries != 0)
{
if (!PhGetIntegerSetting(SETTING_ENABLE_WARNINGS) ||
PhShowConfirmMessage(
hwndDlg,
L"terminate",
L"the selected process(es)",
L"Terminating a Zombie process may cause the system to become unstable "
L"or crash.",
TRUE
))
{
NTSTATUS status;
HANDLE processHandle;
BOOLEAN refresh;
refresh = FALSE;
for (i = 0; i < numberOfEntries; i++)
{
if (ProcessesMethod == BruteForceScanMethod || ProcessesMethod == ProcessHandleScanMethod)
{
status = PhOpenProcess(
&processHandle,
PROCESS_TERMINATE,
entries[i]->ProcessId
);
}
else
{
status = PhOpenProcessByCsrHandles(
&processHandle,
PROCESS_TERMINATE,
entries[i]->ProcessId
);
}
if (NT_SUCCESS(status))
{
status = PhTerminateProcess(processHandle, STATUS_SUCCESS);
NtClose(processHandle);
if (NT_SUCCESS(status))
refresh = TRUE;
}
else
{
PhShowStatus(hwndDlg, L"Unable to terminate the process", status, 0);
}
}
if (refresh)
{
// Sleep for a bit before continuing. It seems to help avoid BSODs.
PhDelayExecution(250);
SendMessage(hwndDlg, WM_COMMAND, IDC_SCAN, 0);
}
}
}
PhFree(entries);
}
break;
case IDC_SAVE:
{
static PH_FILETYPE_FILTER filters[] =
{
{ L"Text files (*.txt)", L"*.txt" },
{ L"All files (*.*)", L"*.*" }
};
PVOID fileDialog;
fileDialog = PhCreateSaveFileDialog();
PhSetFileDialogFilter(fileDialog, filters, sizeof(filters) / sizeof(PH_FILETYPE_FILTER));
PhSetFileDialogFileName(fileDialog, L"Zombie Processes.txt");
if (PhShowFileDialog(hwndDlg, fileDialog))
{
NTSTATUS status;
PPH_STRING fileName;
PPH_FILE_STREAM fileStream;
fileName = PH_AUTO(PhGetFileDialogFileName(fileDialog));
if (NT_SUCCESS(status = PhCreateFileStream(
&fileStream,
fileName->Buffer,
FILE_GENERIC_WRITE,
FILE_SHARE_READ,
FILE_OVERWRITE_IF,
0
)))
{
PhWriteStringAsUtf8FileStream(fileStream, (PPH_STRINGREF)&PhUnicodeByteOrderMark);
PhWritePhTextHeader(fileStream);
PhWriteStringAsUtf8FileStream2(fileStream, L"Method: ");
PhWriteStringAsUtf8FileStream2(fileStream,
ProcessesMethod == BruteForceScanMethod ? L"Brute Force\r\n" : L"CSR Handles\r\n");
PhWriteStringFormatAsUtf8FileStream(
fileStream,
L"Zombie: %u\r\nTerminated: %u\r\n\r\n",
NumberOfZombieProcesses,
NumberOfTerminatedProcesses
);
if (ProcessesList)
{
ULONG i;
for (i = 0; i < ProcessesList->Count; i++)
{
PPH_ZOMBIE_PROCESS_ENTRY entry = ProcessesList->Items[i];
if (entry->Type == ZombieProcess)
PhWriteStringAsUtf8FileStream2(fileStream, L"[Zombie] ");
else if (entry->Type == TerminatedProcess)
PhWriteStringAsUtf8FileStream2(fileStream, L"[Terminated] ");
else if (entry->Type != NormalProcess)
continue;
PhWriteStringFormatAsUtf8FileStream(
fileStream,
L"%s (%u)\r\n",
entry->FileName->Buffer,
HandleToUlong(entry->ProcessId)
);
}
}
PhDereferenceObject(fileStream);
}
if (!NT_SUCCESS(status))
PhShowStatus(hwndDlg, L"Unable to create the file", status, 0);
}
PhFreeFileDialog(fileDialog);
}
break;
}
}
break;
case WM_NOTIFY:
{
LPNMHDR header = (LPNMHDR)lParam;
PhHandleListViewNotifyBehaviors(lParam, PhZombieProcessesListViewHandle, PH_LIST_VIEW_DEFAULT_1_BEHAVIORS);
switch (header->code)
{
case LVN_ITEMCHANGED:
{
if (header->hwndFrom == PhZombieProcessesListViewHandle)
{
EnableWindow(
GetDlgItem(hwndDlg, IDC_TERMINATE),
ListView_GetSelectedCount(PhZombieProcessesListViewHandle) > 0
);
}
}
break;
case NM_DBLCLK:
{
if (header->hwndFrom == PhZombieProcessesListViewHandle)
{
PPH_ZOMBIE_PROCESS_ENTRY entry;
entry = PhGetSelectedListViewItemParam(PhZombieProcessesListViewHandle);
if (entry)
{
NTSTATUS status;
PPH_PROCESS_ITEM processItem;
status = PhpCreateProcessItemForZombieProcess(hwndDlg, entry, &processItem);
if (NT_SUCCESS(status))
{
SystemInformer_ShowProcessProperties(processItem);
PhDereferenceObject(processItem);
}
else
{
PhShowStatus(hwndDlg, L"Unable to create a process structure for the selected process.", status, 0);
}
}
}
}
break;
}
REFLECT_MESSAGE_DLG(hwndDlg, PhZombieProcessesListViewHandle, uMsg, wParam, lParam);
}
break;
case WM_DPICHANGED:
{
PhLayoutManagerUpdate(&WindowLayoutManager, LOWORD(wParam));
PhLayoutManagerLayout(&WindowLayoutManager);
}
break;
case WM_SIZE:
{
PhLayoutManagerLayout(&WindowLayoutManager);
}
break;
case WM_SIZING:
{
PhResizingMinimumSize((PRECT)lParam, wParam, MinimumSize.right, MinimumSize.bottom);
}
break;
case WM_CONTEXTMENU:
{
if ((HWND)wParam == PhZombieProcessesListViewHandle)
{
POINT point;
PPH_EMENU menu;
PPH_EMENU item;
PVOID* listviewItems;
ULONG numberOfItems;
point.x = GET_X_LPARAM(lParam);
point.y = GET_Y_LPARAM(lParam);
if (point.x == -1 && point.y == -1)
PhGetListViewContextMenuPoint(PhZombieProcessesListViewHandle, &point);
PhGetSelectedListViewItemParams(PhZombieProcessesListViewHandle, &listviewItems, &numberOfItems);
if (numberOfItems != 0)
{
menu = PhCreateEMenu();
PhInsertEMenuItem(menu, PhCreateEMenuItem(0, IDC_COPY, L"&Copy", NULL, NULL), ULONG_MAX);
PhInsertCopyListViewEMenuItem(menu, IDC_COPY, PhZombieProcessesListViewHandle);
item = PhShowEMenu(
menu,
hwndDlg,
PH_EMENU_SHOW_SEND_COMMAND | PH_EMENU_SHOW_LEFTRIGHT,
PH_ALIGN_LEFT | PH_ALIGN_TOP,
point.x,
point.y
);
if (item)
{
BOOLEAN handled = FALSE;
handled = PhHandleCopyListViewEMenuItem(item);
//if (!handled && PhPluginsEnabled)
// handled = PhPluginTriggerEMenuItem(&menuInfo, item);
if (!handled)
{
switch (item->Id)
{
case IDC_COPY:
PhCopyListView(PhZombieProcessesListViewHandle);
break;
}
}
}
PhDestroyEMenu(menu);
}
PhFree(listviewItems);
}
}
break;
case WM_PH_UPDATE_DIALOG:
{
NTSTATUS status = (NTSTATUS)wParam;
PPH_LIST list = (PPH_LIST)lParam;
ExtendedListView_SetRedraw(PhZombieProcessesListViewHandle, FALSE);
ListView_DeleteAllItems(PhZombieProcessesListViewHandle);
PhZombieProcessesUpdateListView(list);
ExtendedListView_SortItems(PhZombieProcessesListViewHandle);
ExtendedListView_SetRedraw(PhZombieProcessesListViewHandle, TRUE);
if (NT_SUCCESS(status))
{
PhSetDialogItemText(hwndDlg, IDC_DESCRIPTION, PhaFormatString(
L"%u zombie process(es), %u terminated process(es).",
NumberOfZombieProcesses,
NumberOfTerminatedProcesses
)->Buffer);
InvalidateRect(GetDlgItem(hwndDlg, IDC_DESCRIPTION), NULL, TRUE);
}
else
{
PhShowStatus(hwndDlg, L"Unable to perform the scan", status, 0);
}
EnableWindow(GetDlgItem(hwndDlg, IDC_SCAN), TRUE);
}
break;
case WM_CTLCOLORBTN:
return HANDLE_WM_CTLCOLORBTN(hwndDlg, wParam, lParam, PhWindowThemeControlColor);
case WM_CTLCOLORDLG:
return HANDLE_WM_CTLCOLORDLG(hwndDlg, wParam, lParam, PhWindowThemeControlColor);
case WM_CTLCOLORSTATIC:
{
if ((HWND)lParam == GetDlgItem(hwndDlg, IDC_DESCRIPTION))
{
if (NumberOfZombieProcesses != 0)
{
SetTextColor((HDC)wParam, RGB(0xff, 0x00, 0x00));
}
SetBkColor((HDC)wParam, GetSysColor(COLOR_WINDOW));
return (INT_PTR)GetSysColorBrush(COLOR_WINDOW);
}
return HANDLE_WM_CTLCOLORSTATIC(hwndDlg, wParam, lParam, PhWindowThemeControlColor);
}
}
return FALSE;
}
COLORREF NTAPI PhpZombieProcessesColorFunction(
_In_ INT Index,
_In_ PVOID Param,
_In_opt_ PVOID Context
)
{
PPH_ZOMBIE_PROCESS_ENTRY entry = Param;
switch (entry->Type)
{
case UnknownProcess:
case ZombieProcess:
return RGB(229, 186, 208);
case TerminatedProcess:
return RGB(0x77, 0x77, 0x77);
}
return PhEnableThemeSupport ? PhThemeWindowBackgroundColor : GetSysColor(COLOR_WINDOW);
}
BOOLEAN NTAPI PhpZombieProcessesCallback(
_In_ PPH_ZOMBIE_PROCESS_ENTRY Process,
_In_ PVOID Context
)
{
PPH_ZOMBIE_PROCESS_ENTRY entry;
ULONG count = ((PPH_LIST)Context)->Count;
for (ULONG i = 0; i < count; i++)
{
PPH_ZOMBIE_PROCESS_ENTRY item = ((PPH_LIST)Context)->Items[i];
if (item->ProcessId == Process->ProcessId)
{
return TRUE; // duplicate
}
}
entry = PhAllocateCopy(Process, sizeof(PH_ZOMBIE_PROCESS_ENTRY));
if (entry->FileName)
PhReferenceObject(entry->FileName);
PhAddItemList(Context, entry);
if (entry->Type == ZombieProcess)
InterlockedIncrement(&NumberOfZombieProcesses);
else if (entry->Type == TerminatedProcess)
InterlockedIncrement(&NumberOfTerminatedProcesses);
return TRUE;
}
VOID PhZombieProcessesUpdateListView(
_In_ PPH_LIST UpdateList
)
{
for (ULONG i = 0; i < UpdateList->Count; i++)
{
PPH_ZOMBIE_PROCESS_ENTRY entry = UpdateList->Items[i];
INT lvItemIndex;
WCHAR pidString[PH_INT32_STR_LEN_1];
if (entry->FileName)
{
PhMoveReference(&entry->FileName, PhGetFileName(entry->FileName));
}
lvItemIndex = PhAddListViewItem(
PhZombieProcessesListViewHandle,
MAXINT,
PhGetStringOrDefault(entry->FileName, L"(unknown)"),
entry
);
PhPrintUInt32(pidString, HandleToUlong(entry->ProcessId));
PhSetListViewSubItem(PhZombieProcessesListViewHandle, lvItemIndex, 1, pidString);
PhAddItemList(ProcessesList, entry);
}
}
NTSTATUS PhpCreateProcessItemForZombieProcess(
_In_ HWND WindowHandle,
_In_ PPH_ZOMBIE_PROCESS_ENTRY Entry,
_Out_ PPH_PROCESS_ITEM* ProcessItem
)
{
NTSTATUS status;
PPH_PROCESS_ITEM processItem;
HANDLE processHandle;
if (Entry->Type == NormalProcess)
{
if (processItem = PhReferenceProcessItem(Entry->ProcessId))
{
*ProcessItem = processItem;
return STATUS_SUCCESS;
}
}
if (ProcessesMethod != CsrHandlesScanMethod)
{
status = PhOpenProcess(
&processHandle,
PROCESS_QUERY_LIMITED_INFORMATION,
Entry->ProcessId
);
}
else
{
status = PhOpenProcessByCsrHandles(
&processHandle,
PROCESS_QUERY_LIMITED_INFORMATION,
Entry->ProcessId
);
}
if (NT_SUCCESS(status))
{
if (processItem = PhCreateProcessItemFromHandle(
Entry->ProcessId,
processHandle,
Entry->Type == TerminatedProcess
))
{
*ProcessItem = processItem;
return STATUS_SUCCESS;
}
NtClose(processHandle);
}
return status;
}
NTSTATUS PhpEnumZombieProcessesBruteForce(
_In_ PPH_ENUM_ZOMBIE_PROCESSES_CALLBACK Callback,
_In_opt_ PVOID Context
)
{
NTSTATUS status;
PVOID processes;
PSYSTEM_PROCESS_INFORMATION process;
PPH_LIST pids;
ULONG pid;
BOOLEAN stop = FALSE;
if (!NT_SUCCESS(status = PhEnumProcesses(&processes)))
return status;
pids = PhCreateList(40);
process = PH_FIRST_PROCESS(processes);
do
{
PhAddItemList(pids, process->UniqueProcessId);
} while (process = PH_NEXT_PROCESS(process));
PhFree(processes);
for (pid = 8; pid <= 65536; pid += 4)
{
NTSTATUS status2;
HANDLE processHandle;
PH_ZOMBIE_PROCESS_ENTRY entry;
KERNEL_USER_TIMES times;
PPH_STRING fileName;
status2 = PhOpenProcess(
&processHandle,
PROCESS_QUERY_LIMITED_INFORMATION,
UlongToHandle(pid)
);
if (NT_SUCCESS(status2))
{
entry.ProcessId = UlongToHandle(pid);
if (NT_SUCCESS(status2 = PhGetProcessTimes(
processHandle,
×
)) &&
NT_SUCCESS(status2 = PhGetProcessImageFileName(
processHandle,
&fileName
)))
{
entry.FileName = fileName;
if (times.ExitTime.QuadPart != 0)
entry.Type = TerminatedProcess;
else if (PhFindItemList(pids, UlongToHandle(pid)) != ULONG_MAX)
entry.Type = NormalProcess;
else
entry.Type = ZombieProcess;
if (!Callback(&entry, Context))
stop = TRUE;
PhDereferenceObject(fileName);
}
NtClose(processHandle);
}
// Use an alternative method if we don't have sufficient access.
if (status2 == STATUS_ACCESS_DENIED)
{
if (NT_SUCCESS(status2 = PhGetProcessImageFileNameByProcessId(UlongToHandle(pid), &fileName)))
{
entry.ProcessId = UlongToHandle(pid);
entry.FileName = fileName;
if (PhFindItemList(pids, UlongToHandle(pid)) != ULONG_MAX)
entry.Type = NormalProcess;
else
entry.Type = ZombieProcess;
if (!Callback(&entry, Context))
stop = TRUE;
PhDereferenceObject(fileName);
}
}
if (status2 == STATUS_INVALID_CID || status2 == STATUS_INVALID_PARAMETER)
status2 = STATUS_SUCCESS;
if (!NT_SUCCESS(status2))
{
entry.ProcessId = UlongToHandle(pid);
entry.FileName = NULL;
entry.Type = UnknownProcess;
if (!Callback(&entry, Context))
stop = TRUE;
}
if (stop)
break;
}
PhDereferenceObject(pids);
return status;
}
typedef struct _CSR_HANDLES_CONTEXT
{
PPH_ENUM_ZOMBIE_PROCESSES_CALLBACK Callback;
PVOID Context;
PPH_LIST Pids;
} CSR_HANDLES_CONTEXT, *PCSR_HANDLES_CONTEXT;
static BOOLEAN NTAPI PhpCsrProcessHandlesCallback(
_In_ PPH_CSR_HANDLE_INFO Handle,
_In_opt_ PVOID Context
)
{
NTSTATUS status;
BOOLEAN cont = TRUE;
PCSR_HANDLES_CONTEXT context = Context;
HANDLE processHandle;
KERNEL_USER_TIMES times;
PPH_STRING fileName;
PH_ZOMBIE_PROCESS_ENTRY entry;
entry.ProcessId = Handle->ProcessId;
if (NT_SUCCESS(status = PhOpenProcessByCsrHandle(
&processHandle,
PROCESS_QUERY_LIMITED_INFORMATION,
Handle
)))
{
if (NT_SUCCESS(status = PhGetProcessTimes(
processHandle,
×
)) &&
NT_SUCCESS(status = PhGetProcessImageFileName(
processHandle,
&fileName
)))
{
entry.FileName = fileName;
if (times.ExitTime.QuadPart != 0)
entry.Type = TerminatedProcess;
else if (context && PhFindItemList(context->Pids, Handle->ProcessId) != ULONG_MAX)
entry.Type = NormalProcess;
else
entry.Type = ZombieProcess;
if (context && !context->Callback(&entry, context->Context))
cont = FALSE;
PhDereferenceObject(fileName);
}
NtClose(processHandle);
}
if (!NT_SUCCESS(status))
{
entry.FileName = NULL;
entry.Type = UnknownProcess;
if (context && !context->Callback(&entry, context->Context))
cont = FALSE;
}
return cont;
}
NTSTATUS PhpEnumZombieProcessesCsrHandles(
_In_ PPH_ENUM_ZOMBIE_PROCESSES_CALLBACK Callback,
_In_opt_ PVOID Context
)
{
NTSTATUS status;
PVOID processes;
PSYSTEM_PROCESS_INFORMATION process;
PPH_LIST pids;
CSR_HANDLES_CONTEXT context;
if (!NT_SUCCESS(status = PhEnumProcesses(&processes)))
return status;
pids = PhCreateList(40);
process = PH_FIRST_PROCESS(processes);
do
{
PhAddItemList(pids, process->UniqueProcessId);
} while (process = PH_NEXT_PROCESS(process));
PhFree(processes);
context.Callback = Callback;
context.Context = Context;
context.Pids = pids;
status = PhEnumCsrProcessHandles(PhpCsrProcessHandlesCallback, &context);
PhDereferenceObject(pids);
return status;
}
typedef struct _PH_ENUM_NEXT_PROCESS_CONTEXT
{
PPH_ENUM_ZOMBIE_PROCESSES_CALLBACK Callback;
PVOID Context;
} PH_ENUM_NEXT_PROCESS_CONTEXT, *PPH_ENUM_NEXT_PROCESS_CONTEXT;
NTSTATUS NTAPI PhpEnumNextProcessHandles(
_In_ HANDLE ProcessHandle,
_In_ PVOID Context
)
{
PPH_ENUM_NEXT_PROCESS_CONTEXT context = Context;
PROCESS_EXTENDED_BASIC_INFORMATION basicInfo;
NTSTATUS status;
PVOID processes = NULL;
status = PhGetProcessExtendedBasicInformation(ProcessHandle, &basicInfo);
if (NT_SUCCESS(status))
{
status = PhEnumProcesses(&processes);
if (NT_SUCCESS(status))
{
if (!PhFindProcessInformation(processes, basicInfo.BasicInfo.UniqueProcessId))
{
PH_ZOMBIE_PROCESS_ENTRY entry;
PPH_STRING fileName;
entry.ProcessId = basicInfo.BasicInfo.UniqueProcessId;
if (NT_SUCCESS(PhGetProcessImageFileName(ProcessHandle, &fileName)))
{
entry.FileName = fileName;
entry.Type = ZombieProcess;
//if (basicInfo.IsProcessDeleting)
// entry.Type = TerminatedProcess;
if (!context->Callback(&entry, context->Context))
goto CleanupExit;
PhDereferenceObject(fileName);
}
else
{
entry.FileName = NULL;
entry.Type = UnknownProcess;
if (!context->Callback(&entry, context->Context))
goto CleanupExit;
}
}
}
}
CleanupExit:
if (processes)
{
PhFree(processes);
}
return STATUS_SUCCESS;
}
NTSTATUS PhpEnumZombieProcessHandles(
_In_ PPH_ENUM_ZOMBIE_PROCESSES_CALLBACK Callback,
_In_opt_ PVOID Context
)
{
NTSTATUS status;
PH_ENUM_NEXT_PROCESS_CONTEXT context;
context.Callback = Callback;
context.Context = Context;
status = PhEnumNextProcess(
NULL,
PROCESS_QUERY_LIMITED_INFORMATION,
PhpEnumNextProcessHandles,
&context
);
return status;
}
NTSTATUS PhpEnumZombieSubKeyHandles(
_In_ PPH_ENUM_ZOMBIE_PROCESSES_CALLBACK Callback,
_In_opt_ PVOID Context
)
{
NTSTATUS status;
ULONG bufferSize;
PKEY_OPEN_SUBKEYS_INFORMATION buffer;
UNICODE_STRING subkeyName;
OBJECT_ATTRIBUTES objectAttributes;
bufferSize = 0x200;
buffer = PhAllocate(bufferSize);
RtlInitUnicodeString(&subkeyName, L"\\REGISTRY");
InitializeObjectAttributes(
&objectAttributes,
&subkeyName,
OBJ_CASE_INSENSITIVE,
NULL,
NULL
);
while (TRUE)
{
status = NtQueryOpenSubKeysEx(
&objectAttributes,
bufferSize,
buffer,
&bufferSize
);
if (status == STATUS_BUFFER_OVERFLOW || status == STATUS_BUFFER_TOO_SMALL || status == STATUS_INFO_LENGTH_MISMATCH)
{
PhFree(buffer);
bufferSize *= 2;
buffer = PhAllocate(bufferSize);
}
else
{
break;
}
}
if (NT_SUCCESS(status))
{
for (ULONG i = 0; i < buffer->Count; i++)
{
KEY_PID_ARRAY entry = buffer->KeyArray[i];
HANDLE processHandle;
if (NT_SUCCESS(PhOpenProcess(&processHandle, PROCESS_QUERY_LIMITED_INFORMATION, entry.ProcessId)))
{
PVOID processes;
if (NT_SUCCESS(PhEnumProcesses(&processes)))
{
if (!PhFindProcessInformation(processes, entry.ProcessId))
{
PH_ZOMBIE_PROCESS_ENTRY process;
PPH_STRING fileName;
process.ProcessId = entry.ProcessId;
if (NT_SUCCESS(PhGetProcessImageFileName(processHandle, &fileName)))
{
PROCESS_EXTENDED_BASIC_INFORMATION basicInfo;
process.FileName = fileName;
process.Type = ZombieProcess;
if (NT_SUCCESS(PhGetProcessExtendedBasicInformation(processHandle, &basicInfo)))
{
if (basicInfo.IsProcessDeleting)
process.Type = TerminatedProcess;
}
if (!Callback(&process, Context))
break;
PhDereferenceObject(fileName);
}
else
{
process.FileName = NULL;
process.Type = UnknownProcess;
if (!Callback(&process, Context))
break;
}
}
PhFree(processes);
}
NtClose(processHandle);
}
else
{
PH_ZOMBIE_PROCESS_ENTRY process;
PPH_STRING fileName;
process.ProcessId = entry.ProcessId;
if (NT_SUCCESS(PhGetProcessImageFileNameByProcessId(process.ProcessId, &fileName)))
{
process.FileName = fileName;
process.Type = ZombieProcess;
if (!Callback(&process, Context))
break;
PhDereferenceObject(fileName);
}
else
{
process.FileName = NULL;
process.Type = UnknownProcess;
if (!Callback(&process, Context))
break;
}
}
}
}
else
{
PhFree(buffer);
}
return status;
}
#define PH_FIRST_ETW_GUID(TraceGuid) \
(((PETW_TRACE_GUID_INFO)(TraceGuid))->InstanceCount ? \
((PETW_TRACE_PROVIDER_INSTANCE_INFO)PTR_ADD_OFFSET(TraceGuid, \
sizeof(ETW_TRACE_GUID_INFO))) : NULL)
#define PH_NEXT_ETW_GUID(TraceGuid) \
(((PETW_TRACE_PROVIDER_INSTANCE_INFO)(TraceGuid))->NextOffset ? \
(PETW_TRACE_PROVIDER_INSTANCE_INFO)PTR_ADD_OFFSET((TraceGuid), \
((PETW_TRACE_PROVIDER_INSTANCE_INFO)(TraceGuid))->NextOffset) : NULL)
NTSTATUS PhpEnumEtwGuidHandles(
_In_ PPH_ENUM_ZOMBIE_PROCESSES_CALLBACK Callback,
_In_opt_ PVOID Context
)
{
NTSTATUS status;
PGUID traceGuidList = NULL;
ULONG traceGuidListLength = 0;
status = PhTraceControlVariableSize(
EtwEnumTraceGuidList,
NULL,
0,
&traceGuidList,
&traceGuidListLength
);
if (NT_SUCCESS(status))
{
for (ULONG i = 0; i < traceGuidListLength / sizeof(GUID); i++)
{
GUID providerGuid = traceGuidList[i];
PETW_TRACE_GUID_INFO traceGuidInfo;
status = PhTraceControlVariableSize(
EtwGetTraceGuidInfo,
&providerGuid,
sizeof(GUID),
&traceGuidInfo,
NULL
);
if (NT_SUCCESS(status))
{
PETW_TRACE_PROVIDER_INSTANCE_INFO instance;
HANDLE processHandle;
//PVOID processes;
for (instance = PH_FIRST_ETW_GUID(traceGuidInfo);
instance;
instance = PH_NEXT_ETW_GUID(instance))
{
if (instance->Pid == 0)
continue;
if (NT_SUCCESS(PhOpenProcess(&processHandle, PROCESS_QUERY_LIMITED_INFORMATION, UlongToHandle(instance->Pid))))
{
PPH_PROCESS_ITEM processItem;
processItem = PhReferenceProcessItem(UlongToHandle(instance->Pid));
//if (NT_SUCCESS(PhEnumProcesses(&processes)))
{
//if (!PhFindProcessInformation(processes, UlongToHandle(instance->Pid)))
if (!processItem)
{
PH_ZOMBIE_PROCESS_ENTRY process;
PPH_STRING fileName;
process.ProcessId = UlongToHandle(instance->Pid);
if (NT_SUCCESS(PhGetProcessImageFileName(processHandle, &fileName)))
{
PROCESS_EXTENDED_BASIC_INFORMATION basicInfo;
process.FileName = fileName;
process.Type = ZombieProcess;
if (NT_SUCCESS(PhGetProcessExtendedBasicInformation(processHandle, &basicInfo)))
{
if (basicInfo.IsProcessDeleting)
process.Type = TerminatedProcess;
}
if (!Callback(&process, Context))
break;
PhDereferenceObject(fileName);
}
else
{
process.FileName = NULL;
process.Type = UnknownProcess;
if (!Callback(&process, Context))
break;
}
}
//PhFree(processes);
}
PhClearReference(&processItem);
NtClose(processHandle);
}
else
{
PH_ZOMBIE_PROCESS_ENTRY process;
PPH_STRING fileName;
process.ProcessId = UlongToHandle(instance->Pid);
if (NT_SUCCESS(PhGetProcessImageFileNameByProcessId(process.ProcessId, &fileName)))
{
process.FileName = fileName;
process.Type = ZombieProcess;
if (!Callback(&process, Context))
break;
PhDereferenceObject(process.FileName);
}
else
{
process.FileName = NULL;
process.Type = UnknownProcess;
if (!Callback(&process, Context))
break;
}
}
}
PhFree(traceGuidInfo);
}
}
PhFree(traceGuidList);
}
return status;
}
NTSTATUS PhpEnumNtdllHandles(
_In_ PPH_ENUM_ZOMBIE_PROCESSES_CALLBACK Callback,
_In_opt_ PVOID Context
)
{
static CONST PH_STRINGREF ntdllPath = PH_STRINGREF_INIT(L"\\SystemRoot\\System32\\ntdll.dll");
static CONST PH_STRINGREF ntfsPath = PH_STRINGREF_INIT(L"\\ntfs");
NTSTATUS status;
HANDLE fileHandle;
status = PhCreateFile(
&fileHandle,
&ntfsPath,
FILE_READ_ATTRIBUTES | SYNCHRONIZE,
FILE_ATTRIBUTE_NORMAL,
FILE_SHARE_READ,
FILE_OPEN,
FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT
);
if (!NT_SUCCESS(status))
{
status = PhCreateFile(
&fileHandle,
&ntdllPath,
FILE_READ_ATTRIBUTES | SYNCHRONIZE,
FILE_ATTRIBUTE_NORMAL,
FILE_SHARE_READ,
FILE_OPEN,
FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT
);
}
if (NT_SUCCESS(status))
{
PFILE_PROCESS_IDS_USING_FILE_INFORMATION processIds;
status = PhGetProcessIdsUsingFile(
fileHandle,
&processIds
);
if (NT_SUCCESS(status))
{
for (ULONG i = 0; i < processIds->NumberOfProcessIdsInList; i++)
{
HANDLE processId;
HANDLE processHandle;
processId = (HANDLE)processIds->ProcessIdList[i];
if (NT_SUCCESS(PhOpenProcess(&processHandle, PROCESS_QUERY_LIMITED_INFORMATION, processId)))
{
PVOID processes;
if (NT_SUCCESS(PhEnumProcesses(&processes)))
{
if (!PhFindProcessInformation(processes, processId))
{
PH_ZOMBIE_PROCESS_ENTRY process;
PPH_STRING fileName;
process.ProcessId = processId;
if (NT_SUCCESS(PhGetProcessImageFileName(processHandle, &fileName)))
{
PROCESS_EXTENDED_BASIC_INFORMATION basicInfo;
process.FileName = fileName;
process.Type = ZombieProcess;
if (NT_SUCCESS(PhGetProcessExtendedBasicInformation(processHandle, &basicInfo)))
{
//if (basicInfo.IsProcessDeleting)
// process.Type = TerminatedProcess;
}
if (!Callback(&process, Context))
break;
PhDereferenceObject(fileName);
}
else
{
process.FileName = NULL;
process.Type = UnknownProcess;
if (!Callback(&process, Context))
break;
}
}
PhFree(processes);
}
NtClose(processHandle);
}
else
{
PH_ZOMBIE_PROCESS_ENTRY process;
PPH_STRING fileName;
process.ProcessId = processId;
if (NT_SUCCESS(PhGetProcessImageFileNameByProcessId(process.ProcessId, &fileName)))
{
process.FileName = fileName;
process.Type = ZombieProcess;
if (!Callback(&process, Context))
break;
PhDereferenceObject(fileName);
}
else
{
process.FileName = NULL;
process.Type = UnknownProcess;
if (!Callback(&process, Context))
break;
}
}
}
PhFree(processIds);
}
NtClose(fileHandle);
}
return status;
}
NTSTATUS PhEnumZombieProcesses(
_In_ PH_ZOMBIE_PROCESS_METHOD Method,
_In_ PPH_ENUM_ZOMBIE_PROCESSES_CALLBACK Callback,
_In_opt_ PVOID Context
)
{
switch (Method)
{
case BruteForceScanMethod:
return PhpEnumZombieProcessesBruteForce(Callback, Context);
case CsrHandlesScanMethod:
return PhpEnumZombieProcessesCsrHandles(Callback, Context);
case ProcessHandleScanMethod:
return PhpEnumZombieProcessHandles(Callback, Context);
case RegistryScanMethod:
return PhpEnumZombieSubKeyHandles(Callback, Context);
case EtwGuidScanMethod:
return PhpEnumEtwGuidHandles(Callback, Context);
case NtdllScanMethod:
return PhpEnumNtdllHandles(Callback, Context);
}
return STATUS_FAIL_CHECK;
}
NTSTATUS PhpOpenCsrProcesses(
_Out_ PHANDLE *ProcessHandles,
_Out_ PULONG NumberOfProcessHandles
)
{
NTSTATUS status;
PVOID processes;
PSYSTEM_PROCESS_INFORMATION process;
PPH_LIST processHandleList;
if (!NT_SUCCESS(status = PhEnumProcesses(&processes)))
return status;
processHandleList = PhCreateList(8);
process = PH_FIRST_PROCESS(processes);
do
{
HANDLE processHandle;
PH_KNOWN_PROCESS_TYPE knownProcessType;
if (NT_SUCCESS(PhOpenProcess(
&processHandle,
PROCESS_QUERY_LIMITED_INFORMATION | PROCESS_DUP_HANDLE,
process->UniqueProcessId
)))
{
if (NT_SUCCESS(PhGetProcessKnownType(
processHandle,
&knownProcessType
)) &&
(knownProcessType & KnownProcessTypeMask) == WindowsSubsystemProcessType)
{
PhAddItemList(processHandleList, processHandle);
}
else
{
NtClose(processHandle);
}
}
} while (process = PH_NEXT_PROCESS(process));
PhFree(processes);
if (processHandleList->Count)
{
*ProcessHandles = PhAllocateCopy(processHandleList->Items, processHandleList->Count * sizeof(HANDLE));
*NumberOfProcessHandles = processHandleList->Count;
PhDereferenceObject(processHandleList);
return status;
}
PhDereferenceObject(processHandleList);
return STATUS_UNSUCCESSFUL;
}
NTSTATUS PhpGetCsrHandleProcessId(
_Inout_ PPH_CSR_HANDLE_INFO Handle
)
{
NTSTATUS status;
PROCESS_BASIC_INFORMATION processBasicInfo;
THREAD_BASIC_INFORMATION threadBasicInfo;
Handle->IsThreadHandle = FALSE;
Handle->ProcessId = NULL;
// Assume the handle is a process handle, and get the
// process ID.
status = KphQueryInformationObject(
Handle->CsrProcessHandle,
Handle->Handle,
KphObjectProcessBasicInformation,
&processBasicInfo,
sizeof(PROCESS_BASIC_INFORMATION),
NULL
);
if (NT_SUCCESS(status))
{
Handle->ProcessId = processBasicInfo.UniqueProcessId;
}
else
{
// We failed to get the process ID. Assume the handle
// is a thread handle, and get the process ID.
status = KphQueryInformationObject(
Handle->CsrProcessHandle,
Handle->Handle,
KphObjectThreadBasicInformation,
&threadBasicInfo,
sizeof(THREAD_BASIC_INFORMATION),
NULL
);
if (NT_SUCCESS(status))
{
Handle->ProcessId = threadBasicInfo.ClientId.UniqueProcess;
Handle->IsThreadHandle = TRUE;
}
}
return status;
}
NTSTATUS PhEnumCsrProcessHandles(
_In_ PPH_ENUM_CSR_PROCESS_HANDLES_CALLBACK Callback,
_In_opt_ PVOID Context
)
{
NTSTATUS status;
PHANDLE csrProcessHandles;
ULONG numberOfCsrProcessHandles;
ULONG i;
BOOLEAN stop = FALSE;
PPH_LIST pids;
if (!NT_SUCCESS(status = PhpOpenCsrProcesses(
&csrProcessHandles,
&numberOfCsrProcessHandles
)))
return status;
pids = PhCreateList(40);
for (i = 0; i < numberOfCsrProcessHandles; i++)
{
PKPH_PROCESS_HANDLE_INFORMATION handles;
ULONG j;
if (stop)
break;
if (NT_SUCCESS(KsiEnumerateProcessHandles(csrProcessHandles[i], &handles)))
{
for (j = 0; j < handles->HandleCount; j++)
{
PH_CSR_HANDLE_INFO handle;
handle.CsrProcessHandle = csrProcessHandles[i];
handle.Handle = handles->Handles[j].Handle;
// Get the process ID associated with the handle.
// This call will fail if the handle is not a
// process or thread handle.
if (!NT_SUCCESS(PhpGetCsrHandleProcessId(&handle)))
continue;
// Avoid duplicate PIDs.
if (PhFindItemList(pids, handle.ProcessId) != ULONG_MAX)
continue;
PhAddItemList(pids, handle.ProcessId);
if (!Callback(&handle, Context))
{
stop = TRUE;
break;
}
}
PhFree(handles);
}
}
PhDereferenceObject(pids);
for (i = 0; i < numberOfCsrProcessHandles; i++)
NtClose(csrProcessHandles[i]);
PhFree(csrProcessHandles);
return status;
}
NTSTATUS PhOpenProcessByCsrHandle(
_Out_ PHANDLE ProcessHandle,
_In_ ACCESS_MASK DesiredAccess,
_In_ PPH_CSR_HANDLE_INFO Handle
)
{
NTSTATUS status;
if (!Handle->IsThreadHandle)
{
status = NtDuplicateObject(
Handle->CsrProcessHandle,
Handle->Handle,
NtCurrentProcess(),
ProcessHandle,
DesiredAccess,
0,
0
);
}
else
{
HANDLE threadHandle;
if (!NT_SUCCESS(status = NtDuplicateObject(
Handle->CsrProcessHandle,
Handle->Handle,
NtCurrentProcess(),
&threadHandle,
THREAD_QUERY_LIMITED_INFORMATION,
0,
0
)))
return status;
status = KphOpenThreadProcess(
threadHandle,
DesiredAccess,
ProcessHandle
);
NtClose(threadHandle);
}
return status;
}
typedef struct _OPEN_PROCESS_BY_CSR_CONTEXT
{
NTSTATUS Status;
PHANDLE ProcessHandle;
ACCESS_MASK DesiredAccess;
HANDLE ProcessId;
} OPEN_PROCESS_BY_CSR_CONTEXT, *POPEN_PROCESS_BY_CSR_CONTEXT;
static BOOLEAN NTAPI PhpOpenProcessByCsrHandlesCallback(
_In_ PPH_CSR_HANDLE_INFO Handle,
_In_opt_ PVOID Context
)
{
POPEN_PROCESS_BY_CSR_CONTEXT context = Context;
if (context && Handle->ProcessId == context->ProcessId)
{
context->Status = PhOpenProcessByCsrHandle(
context->ProcessHandle,
context->DesiredAccess,
Handle
);
return FALSE;
}
return TRUE;
}
NTSTATUS PhOpenProcessByCsrHandles(
_Out_ PHANDLE ProcessHandle,
_In_ ACCESS_MASK DesiredAccess,
_In_ HANDLE ProcessId
)
{
NTSTATUS status;
OPEN_PROCESS_BY_CSR_CONTEXT context;
context.Status = STATUS_INVALID_CID;
context.ProcessHandle = ProcessHandle;
context.DesiredAccess = DesiredAccess;
context.ProcessId = ProcessId;
if (!NT_SUCCESS(status = PhEnumCsrProcessHandles(
PhpOpenProcessByCsrHandlesCallback,
&context
)))
return status;
return context.Status;
}
================================================
FILE: SystemInformer/hndllist.c
================================================
/*
* Copyright (c) 2022 Winsider Seminars & Solutions, Inc. All rights reserved.
*
* This file is part of System Informer.
*
* Authors:
*
* wj32 2011-2013
* dmex 2017-2023
*
*/
#include
#include
#include
#include
#include
#include
#include
#include
_Function_class_(PH_HASHTABLE_EQUAL_FUNCTION)
BOOLEAN PhpHandleNodeHashtableEqualFunction(
_In_ PVOID Entry1,
_In_ PVOID Entry2
);
_Function_class_(PH_HASHTABLE_HASH_FUNCTION)
ULONG PhpHandleNodeHashtableHashFunction(
_In_ PVOID Entry
);
VOID PhpDestroyHandleNode(
_In_ PPH_HANDLE_NODE HandleNode
);
VOID PhpRemoveHandleNode(
_In_ PPH_HANDLE_NODE HandleNode,
_In_ PPH_HANDLE_LIST_CONTEXT Context
);
_Function_class_(PH_CM_POST_SORT_FUNCTION)
LONG PhpHandleTreeNewPostSortFunction(
_In_ LONG Result,
_In_ PVOID Node1,
_In_ PVOID Node2,
_In_ PH_SORT_ORDER SortOrder
);
BOOLEAN NTAPI PhpHandleTreeNewCallback(
_In_ HWND hwnd,
_In_ PH_TREENEW_MESSAGE Message,
_In_ PVOID Parameter1,
_In_ PVOID Parameter2,
_In_ PVOID Context
);
VOID PhInitializeHandleList(
_In_ HWND ParentWindowHandle,
_In_ HWND TreeNewHandle,
_Out_ PPH_HANDLE_LIST_CONTEXT Context
)
{
HWND hwnd;
memset(Context, 0, sizeof(PH_HANDLE_LIST_CONTEXT));
Context->EnableStateHighlighting = TRUE;
Context->NodeHashtable = PhCreateHashtable(
sizeof(PPH_HANDLE_NODE),
PhpHandleNodeHashtableEqualFunction,
PhpHandleNodeHashtableHashFunction,
100
);
Context->NodeList = PhCreateList(100);
Context->ParentWindowHandle = ParentWindowHandle;
Context->TreeNewHandle = TreeNewHandle;
hwnd = TreeNewHandle;
PhSetControlTheme(hwnd, L"explorer");
TreeNew_SetCallback(hwnd, PhpHandleTreeNewCallback, Context);
TreeNew_SetRedraw(hwnd, FALSE);
// Default columns
PhAddTreeNewColumn(hwnd, PHHNTLC_TYPE, TRUE, L"Type", 100, PH_ALIGN_LEFT, 0, 0);
PhAddTreeNewColumn(hwnd, PHHNTLC_NAME, TRUE, L"Name", 200, PH_ALIGN_LEFT, 1, 0);
PhAddTreeNewColumn(hwnd, PHHNTLC_GRANTEDACCESSSYMBOLIC, TRUE, L"Granted access (symbolic)", 140, PH_ALIGN_LEFT, 2, 0);
PhAddTreeNewColumn(hwnd, PHHNTLC_HANDLE, FALSE, L"Handle", 80, PH_ALIGN_LEFT, ULONG_MAX, 0);
PhAddTreeNewColumn(hwnd, PHHNTLC_OBJECTADDRESS, FALSE, L"Object address", 80, PH_ALIGN_LEFT, ULONG_MAX, 0);
PhAddTreeNewColumnEx(hwnd, PHHNTLC_ATTRIBUTES, FALSE, L"Attributes", 120, PH_ALIGN_LEFT, ULONG_MAX, 0, TRUE);
PhAddTreeNewColumn(hwnd, PHHNTLC_GRANTEDACCESS, FALSE, L"Granted access", 80, PH_ALIGN_LEFT, ULONG_MAX, 0);
PhAddTreeNewColumn(hwnd, PHHNTLC_ORIGINALNAME, FALSE, L"Original name", 200, PH_ALIGN_LEFT, ULONG_MAX, 0);
PhAddTreeNewColumnEx(hwnd, PHHNTLC_FILESHAREACCESS, FALSE, L"File share access", 50, PH_ALIGN_LEFT, ULONG_MAX, 0, TRUE);
PhAddTreeNewColumn(hwnd, PHHNTLC_HANDLEVALUE, FALSE, L"Handle value", 80, PH_ALIGN_LEFT, ULONG_MAX, 0);
PhAddTreeNewColumn(hwnd, PHHNTLC_HANDLECOUNT, FALSE, L"Handle count", 80, PH_ALIGN_LEFT, ULONG_MAX, 0);
PhAddTreeNewColumn(hwnd, PHHNTLC_POINTERCOUNT, FALSE, L"Reference count", 80, PH_ALIGN_LEFT, ULONG_MAX, 0);
PhAddTreeNewColumn(hwnd, PHHNTLC_PAGEDSIZE, FALSE, L"Paged size", 80, PH_ALIGN_LEFT, ULONG_MAX, 0);
PhAddTreeNewColumn(hwnd, PHHNTLC_NONPAGEDSIZE, FALSE, L"Nonpaged size", 80, PH_ALIGN_LEFT, ULONG_MAX, 0);
TreeNew_SetRedraw(hwnd, TRUE);
TreeNew_SetSort(hwnd, 0, AscendingSortOrder);
PhCmInitializeManager(&Context->Cm, hwnd, PHHNTLC_MAXIMUM, PhpHandleTreeNewPostSortFunction);
PhInitializeTreeNewFilterSupport(&Context->TreeFilterSupport, hwnd, Context->NodeList);
}
VOID PhDeleteHandleList(
_In_ PPH_HANDLE_LIST_CONTEXT Context
)
{
ULONG i;
PhDeleteTreeNewFilterSupport(&Context->TreeFilterSupport);
PhCmDeleteManager(&Context->Cm);
for (i = 0; i < Context->NodeList->Count; i++)
PhpDestroyHandleNode(Context->NodeList->Items[i]);
PhDereferenceObject(Context->NodeHashtable);
PhDereferenceObject(Context->NodeList);
}
_Function_class_(PH_HASHTABLE_EQUAL_FUNCTION)
BOOLEAN PhpHandleNodeHashtableEqualFunction(
_In_ PVOID Entry1,
_In_ PVOID Entry2
)
{
PPH_HANDLE_NODE handleNode1 = *(PPH_HANDLE_NODE *)Entry1;
PPH_HANDLE_NODE handleNode2 = *(PPH_HANDLE_NODE *)Entry2;
return handleNode1->Handle == handleNode2->Handle;
}
_Function_class_(PH_HASHTABLE_HASH_FUNCTION)
ULONG PhpHandleNodeHashtableHashFunction(
_In_ PVOID Entry
)
{
return HandleToUlong((*(PPH_HANDLE_NODE *)Entry)->Handle) / 4;
}
VOID PhLoadSettingsHandleList(
_Inout_ PPH_HANDLE_LIST_CONTEXT Context
)
{
PPH_STRING settings;
PPH_STRING sortSettings;
settings = PhGetStringSetting(SETTING_HANDLE_TREE_LIST_COLUMNS);
sortSettings = PhGetStringSetting(SETTING_HANDLE_TREE_LIST_SORT);
Context->Flags = PhGetIntegerSetting(SETTING_HANDLE_TREE_LIST_FLAGS);
PhCmLoadSettingsEx(Context->TreeNewHandle, &Context->Cm, 0, &settings->sr, &sortSettings->sr);
PhDereferenceObject(settings);
PhDereferenceObject(sortSettings);
}
VOID PhSaveSettingsHandleList(
_Inout_ PPH_HANDLE_LIST_CONTEXT Context
)
{
PPH_STRING settings;
PPH_STRING sortSettings;
settings = PhCmSaveSettingsEx(Context->TreeNewHandle, &Context->Cm, 0, &sortSettings);
PhSetIntegerSetting(SETTING_HANDLE_TREE_LIST_FLAGS, Context->Flags);
PhSetStringSetting2(SETTING_HANDLE_TREE_LIST_COLUMNS, &settings->sr);
PhSetStringSetting2(SETTING_HANDLE_TREE_LIST_SORT, &sortSettings->sr);
PhDereferenceObject(settings);
PhDereferenceObject(sortSettings);
}
VOID PhSetOptionsHandleList(
_Inout_ PPH_HANDLE_LIST_CONTEXT Context,
_In_ ULONG Options
)
{
switch (Options)
{
case PH_HANDLE_TREE_MENUITEM_HIDE_PROTECTED_HANDLES:
Context->HideProtectedHandles = !Context->HideProtectedHandles;
break;
case PH_HANDLE_TREE_MENUITEM_HIDE_INHERIT_HANDLES:
Context->HideInheritHandles = !Context->HideInheritHandles;
break;
case PH_HANDLE_TREE_MENUITEM_HIDE_UNNAMED_HANDLES:
Context->HideUnnamedHandles = !Context->HideUnnamedHandles;
break;
case PH_HANDLE_TREE_MENUITEM_HIDE_ETW_HANDLES:
Context->HideEtwHandles = !Context->HideEtwHandles;
break;
}
}
PPH_HANDLE_NODE PhAddHandleNode(
_Inout_ PPH_HANDLE_LIST_CONTEXT Context,
_In_ PPH_HANDLE_ITEM HandleItem,
_In_ ULONG RunId
)
{
PPH_HANDLE_NODE handleNode;
handleNode = PhAllocate(PhEmGetObjectSize(EmHandleNodeType, sizeof(PH_HANDLE_NODE)));
memset(handleNode, 0, sizeof(PH_HANDLE_NODE));
PhInitializeTreeNewNode(&handleNode->Node);
if (Context->EnableStateHighlighting && RunId != 1)
{
PhChangeShStateTn(
&handleNode->Node,
&handleNode->ShState,
&Context->NodeStateList,
NewItemState,
PhCsColorNew,
NULL
);
}
handleNode->Handle = HandleItem->Handle;
handleNode->HandleItem = HandleItem;
PhReferenceObject(HandleItem);
memset(handleNode->TextCache, 0, sizeof(PH_STRINGREF) * PHHNTLC_MAXIMUM);
handleNode->Node.TextCache = handleNode->TextCache;
handleNode->Node.TextCacheSize = PHHNTLC_MAXIMUM;
PhAddEntryHashtable(Context->NodeHashtable, &handleNode);
PhAddItemList(Context->NodeList, handleNode);
if (Context->TreeFilterSupport.FilterList)
handleNode->Node.Visible = PhApplyTreeNewFiltersToNode(&Context->TreeFilterSupport, &handleNode->Node);
PhEmCallObjectOperation(EmHandleNodeType, handleNode, EmObjectCreate);
TreeNew_NodesStructured(Context->TreeNewHandle);
return handleNode;
}
PPH_HANDLE_NODE PhFindHandleNode(
_In_ PPH_HANDLE_LIST_CONTEXT Context,
_In_ HANDLE Handle
)
{
PH_HANDLE_NODE lookupHandleNode;
PPH_HANDLE_NODE lookupHandleNodePtr = &lookupHandleNode;
PPH_HANDLE_NODE *handleNode;
lookupHandleNode.Handle = Handle;
handleNode = (PPH_HANDLE_NODE *)PhFindEntryHashtable(
Context->NodeHashtable,
&lookupHandleNodePtr
);
if (handleNode)
return *handleNode;
else
return NULL;
}
VOID PhRemoveHandleNode(
_In_ PPH_HANDLE_LIST_CONTEXT Context,
_In_ PPH_HANDLE_NODE HandleNode
)
{
// Remove from the hashtable here to avoid problems in case the key is re-used.
PhRemoveEntryHashtable(Context->NodeHashtable, &HandleNode);
if (Context->EnableStateHighlighting)
{
PhChangeShStateTn(
&HandleNode->Node,
&HandleNode->ShState,
&Context->NodeStateList,
RemovingItemState,
PhCsColorRemoved,
Context->TreeNewHandle
);
}
else
{
PhpRemoveHandleNode(HandleNode, Context);
}
}
VOID PhpDestroyHandleNode(
_In_ PPH_HANDLE_NODE HandleNode
)
{
PhEmCallObjectOperation(EmHandleNodeType, HandleNode, EmObjectDelete);
if (HandleNode->GrantedAccessSymbolicText) PhDereferenceObject(HandleNode->GrantedAccessSymbolicText);
if (HandleNode->HandleValue) PhDereferenceObject(HandleNode->HandleValue);
if (HandleNode->HandleCountText) PhDereferenceObject(HandleNode->HandleCountText);
if (HandleNode->PointerCountText) PhDereferenceObject(HandleNode->PointerCountText);
if (HandleNode->PageSizeText) PhDereferenceObject(HandleNode->PageSizeText);
if (HandleNode->NonPageSizeText) PhDereferenceObject(HandleNode->NonPageSizeText);
PhDereferenceObject(HandleNode->HandleItem);
PhFree(HandleNode);
}
VOID PhpRemoveHandleNode(
_In_ PPH_HANDLE_NODE HandleNode,
_In_ PPH_HANDLE_LIST_CONTEXT Context // PH_TICK_SH_STATE requires this parameter to be after HandleNode
)
{
ULONG index;
// Remove from list and cleanup.
if ((index = PhFindItemList(Context->NodeList, HandleNode)) != ULONG_MAX)
PhRemoveItemList(Context->NodeList, index);
PhpDestroyHandleNode(HandleNode);
TreeNew_NodesStructured(Context->TreeNewHandle);
}
VOID PhUpdateHandleNode(
_In_ PPH_HANDLE_LIST_CONTEXT Context,
_In_ PPH_HANDLE_NODE HandleNode
)
{
memset(HandleNode->TextCache, 0, sizeof(PH_STRINGREF) * PHHNTLC_MAXIMUM);
PhInvalidateTreeNewNode(&HandleNode->Node, TN_CACHE_COLOR);
TreeNew_NodesStructured(Context->TreeNewHandle);
}
VOID PhExpandAllHandleNodes(
_In_ PPH_HANDLE_LIST_CONTEXT Context,
_In_ BOOLEAN Expand
)
{
ULONG i;
BOOLEAN needsRestructure = FALSE;
for (i = 0; i < Context->NodeList->Count; i++)
{
PPH_HANDLE_NODE node = Context->NodeList->Items[i];
if (node->Node.Expanded != Expand)
{
node->Node.Expanded = Expand;
needsRestructure = TRUE;
}
}
if (needsRestructure)
TreeNew_NodesStructured(Context->TreeNewHandle);
}
VOID PhTickHandleNodes(
_In_ PPH_HANDLE_LIST_CONTEXT Context
)
{
PH_TICK_SH_STATE_TN(PH_HANDLE_NODE, ShState, Context->NodeStateList, PhpRemoveHandleNode, PhCsHighlightingDuration, Context->TreeNewHandle, TRUE, NULL, Context);
}
#define SORT_FUNCTION(Column) PhpHandleTreeNewCompare##Column
#define BEGIN_SORT_FUNCTION(Column) static int __cdecl PhpHandleTreeNewCompare##Column( \
_In_ void *_context, \
_In_ const void *_elem1, \
_In_ const void *_elem2 \
) \
{ \
PPH_HANDLE_LIST_CONTEXT context = (PPH_HANDLE_LIST_CONTEXT)_context; \
PPH_HANDLE_NODE node1 = *(PPH_HANDLE_NODE *)_elem1; \
PPH_HANDLE_NODE node2 = *(PPH_HANDLE_NODE *)_elem2; \
PPH_HANDLE_ITEM handleItem1 = node1->HandleItem; \
PPH_HANDLE_ITEM handleItem2 = node2->HandleItem; \
int sortResult = 0;
#define END_SORT_FUNCTION \
if (sortResult == 0) \
sortResult = uintptrcmp((ULONG_PTR)node1->Handle, (ULONG_PTR)node2->Handle); \
\
return PhModifySort(sortResult, context->TreeNewSortOrder); \
}
_Function_class_(PH_CM_POST_SORT_FUNCTION)
LONG PhpHandleTreeNewPostSortFunction(
_In_ LONG Result,
_In_ PVOID Node1,
_In_ PVOID Node2,
_In_ PH_SORT_ORDER SortOrder
)
{
if (Result == 0)
Result = uintptrcmp((ULONG_PTR)((PPH_HANDLE_NODE)Node1)->Handle, (ULONG_PTR)((PPH_HANDLE_NODE)Node2)->Handle);
return PhModifySort(Result, SortOrder);
}
BEGIN_SORT_FUNCTION(Type)
{
sortResult = PhCompareStringWithNullSortOrder(handleItem1->TypeName, handleItem2->TypeName, context->TreeNewSortOrder, TRUE);
}
END_SORT_FUNCTION
BEGIN_SORT_FUNCTION(Name)
{
sortResult = PhCompareStringWithNullSortOrder(handleItem1->BestObjectName, handleItem2->BestObjectName, context->TreeNewSortOrder, TRUE);
}
END_SORT_FUNCTION
BEGIN_SORT_FUNCTION(Handle)
{
sortResult = uintptrcmp((ULONG_PTR)node1->Handle, (ULONG_PTR)node2->Handle);
}
END_SORT_FUNCTION
BEGIN_SORT_FUNCTION(ObjectAddress)
{
sortResult = uintptrcmp((ULONG_PTR)handleItem1->Object, (ULONG_PTR)handleItem2->Object);
}
END_SORT_FUNCTION
BEGIN_SORT_FUNCTION(Attributes)
{
sortResult = uintcmp(handleItem1->Attributes, handleItem2->Attributes);
}
END_SORT_FUNCTION
BEGIN_SORT_FUNCTION(GrantedAccess)
{
sortResult = uintcmp(handleItem1->GrantedAccess, handleItem2->GrantedAccess);
}
END_SORT_FUNCTION
BEGIN_SORT_FUNCTION(OriginalName)
{
sortResult = PhCompareStringWithNullSortOrder(handleItem1->ObjectName, handleItem2->ObjectName, context->TreeNewSortOrder, TRUE);
}
END_SORT_FUNCTION
BEGIN_SORT_FUNCTION(FileShareAccess)
{
sortResult = uintcmp(handleItem1->FileFlags & (PH_HANDLE_FILE_SHARED_MASK), handleItem2->FileFlags & (PH_HANDLE_FILE_SHARED_MASK));
// Make sure all file handles get grouped together regardless of share access.
if (sortResult == 0 && !PhIsNullOrEmptyString(handleItem1->TypeName))
sortResult = intcmp(PhEqualString2(handleItem1->TypeName, L"File", TRUE), PhEqualString2(handleItem2->TypeName, L"File", TRUE));
}
END_SORT_FUNCTION
BEGIN_SORT_FUNCTION(HandleCount)
{
sortResult = uintcmp(handleItem1->HandleCount, handleItem2->HandleCount);
}
END_SORT_FUNCTION
BEGIN_SORT_FUNCTION(PointerCount)
{
sortResult = uintcmp(handleItem1->PointerCount, handleItem2->PointerCount);
}
END_SORT_FUNCTION
BEGIN_SORT_FUNCTION(PagedPoolCharge)
{
sortResult = uintcmp(handleItem1->PagedPoolCharge, handleItem2->PagedPoolCharge);
}
END_SORT_FUNCTION
BEGIN_SORT_FUNCTION(NonPagedPoolCharge)
{
sortResult = uintcmp(handleItem1->NonPagedPoolCharge, handleItem2->NonPagedPoolCharge);
}
END_SORT_FUNCTION
BOOLEAN NTAPI PhpHandleTreeNewCallback(
_In_ HWND hwnd,
_In_ PH_TREENEW_MESSAGE Message,
_In_ PVOID Parameter1,
_In_ PVOID Parameter2,
_In_ PVOID Context
)
{
PPH_HANDLE_LIST_CONTEXT context = Context;
PPH_HANDLE_NODE node;
if (PhCmForwardMessage(hwnd, Message, Parameter1, Parameter2, &context->Cm))
return TRUE;
switch (Message)
{
case TreeNewGetChildren:
{
PPH_TREENEW_GET_CHILDREN getChildren = Parameter1;
if (!getChildren->Node)
{
static PVOID sortFunctions[] =
{
SORT_FUNCTION(Type),
SORT_FUNCTION(Name),
SORT_FUNCTION(Handle),
SORT_FUNCTION(ObjectAddress),
SORT_FUNCTION(Attributes),
SORT_FUNCTION(GrantedAccess),
SORT_FUNCTION(GrantedAccess), // Granted Access (Symbolic)
SORT_FUNCTION(OriginalName),
SORT_FUNCTION(FileShareAccess),
SORT_FUNCTION(Handle),
SORT_FUNCTION(HandleCount),
SORT_FUNCTION(PointerCount),
SORT_FUNCTION(PagedPoolCharge),
SORT_FUNCTION(NonPagedPoolCharge),
};
int (__cdecl *sortFunction)(void *, const void *, const void *);
static_assert(RTL_NUMBER_OF(sortFunctions) == PHHNTLC_MAXIMUM, "SortFunctions must equal maximum.");
if (!PhCmForwardSort(
(PPH_TREENEW_NODE *)context->NodeList->Items,
context->NodeList->Count,
context->TreeNewSortColumn,
context->TreeNewSortOrder,
&context->Cm
))
{
if (context->TreeNewSortColumn < PHHNTLC_MAXIMUM)
sortFunction = sortFunctions[context->TreeNewSortColumn];
else
sortFunction = NULL;
}
else
{
sortFunction = NULL;
}
if (sortFunction)
{
qsort_s(context->NodeList->Items, context->NodeList->Count, sizeof(PVOID), sortFunction, context);
}
getChildren->Children = (PPH_TREENEW_NODE *)context->NodeList->Items;
getChildren->NumberOfChildren = context->NodeList->Count;
}
}
return TRUE;
case TreeNewIsLeaf:
{
PPH_TREENEW_IS_LEAF isLeaf = Parameter1;
isLeaf->IsLeaf = TRUE;
}
return TRUE;
case TreeNewGetCellText:
{
PPH_TREENEW_GET_CELL_TEXT getCellText = Parameter1;
PPH_HANDLE_ITEM handleItem;
node = (PPH_HANDLE_NODE)getCellText->Node;
handleItem = node->HandleItem;
switch (getCellText->Id)
{
case PHHNTLC_TYPE:
getCellText->Text = PhGetStringRef(handleItem->TypeName);
break;
case PHHNTLC_NAME:
getCellText->Text = PhGetStringRef(handleItem->BestObjectName);
break;
case PHHNTLC_HANDLE:
{
PhInitializeStringRefLongHint(&getCellText->Text, handleItem->HandleString);
}
break;
case PHHNTLC_OBJECTADDRESS:
{
if (handleItem->Object)
PhInitializeStringRefLongHint(&getCellText->Text, handleItem->ObjectString);
}
break;
case PHHNTLC_ATTRIBUTES:
{
switch (handleItem->Attributes & (OBJ_PROTECT_CLOSE | OBJ_INHERIT))
{
case OBJ_PROTECT_CLOSE:
PhInitializeStringRef(&getCellText->Text, L"Protected");
break;
case OBJ_INHERIT:
PhInitializeStringRef(&getCellText->Text, L"Inherit");
break;
case OBJ_PROTECT_CLOSE | OBJ_INHERIT:
PhInitializeStringRef(&getCellText->Text, L"Protected, Inherit");
break;
}
}
break;
case PHHNTLC_GRANTEDACCESS:
{
PhInitializeStringRefLongHint(&getCellText->Text, handleItem->GrantedAccessString);
}
break;
case PHHNTLC_GRANTEDACCESSSYMBOLIC:
{
if (handleItem->GrantedAccess != 0)
{
if (!node->GrantedAccessSymbolicText)
{
PPH_ACCESS_ENTRY accessEntries;
ULONG numberOfAccessEntries;
if (PhGetAccessEntries(PhGetStringOrEmpty(handleItem->TypeName), &accessEntries, &numberOfAccessEntries))
{
node->GrantedAccessSymbolicText = PhGetAccessString(handleItem->GrantedAccess, accessEntries, numberOfAccessEntries);
PhFree(accessEntries);
}
}
getCellText->Text = PhGetStringRef(node->GrantedAccessSymbolicText);
}
}
break;
case PHHNTLC_ORIGINALNAME:
{
getCellText->Text = PhGetStringRef(handleItem->ObjectName);
}
break;
case PHHNTLC_FILESHAREACCESS:
{
if (handleItem->FileFlags & PH_HANDLE_FILE_SHARED_MASK)
{
node->FileShareAccessText[0] = L'-';
node->FileShareAccessText[1] = L'-';
node->FileShareAccessText[2] = L'-';
node->FileShareAccessText[3] = UNICODE_NULL;
if (handleItem->FileFlags & PH_HANDLE_FILE_SHARED_READ)
node->FileShareAccessText[0] = L'R';
if (handleItem->FileFlags & PH_HANDLE_FILE_SHARED_WRITE)
node->FileShareAccessText[1] = L'W';
if (handleItem->FileFlags & PH_HANDLE_FILE_SHARED_DELETE)
node->FileShareAccessText[2] = L'D';
PhInitializeStringRefLongHint(&getCellText->Text, node->FileShareAccessText);
}
}
break;
case PHHNTLC_HANDLEVALUE:
{
PhMoveReference(&node->HandleValue, PhFormatUInt64((HANDLE_PTR)handleItem->Handle, FALSE));
getCellText->Text = PhGetStringRef(node->HandleValue);
}
break;
case PHHNTLC_HANDLECOUNT:
{
if (handleItem->HandleCount != 0)
{
PhMoveReference(&node->HandleCountText, PhFormatUInt64(handleItem->HandleCount, FALSE));
getCellText->Text = PhGetStringRef(node->HandleCountText);
}
}
break;
case PHHNTLC_POINTERCOUNT:
{
if (handleItem->PointerCount != 0)
{
PhMoveReference(&node->PointerCountText, PhFormatUInt64(handleItem->PointerCount, FALSE));
getCellText->Text = PhGetStringRef(node->PointerCountText);
}
}
break;
case PHHNTLC_PAGEDSIZE:
{
if (handleItem->PagedPoolCharge != 0)
{
PhMoveReference(&node->PageSizeText, PhFormatSize(handleItem->PagedPoolCharge, ULONG_MAX));
getCellText->Text = PhGetStringRef(node->PageSizeText);
}
}
break;
case PHHNTLC_NONPAGEDSIZE:
{
if (handleItem->NonPagedPoolCharge != 0)
{
PhMoveReference(&node->NonPageSizeText, PhFormatSize(handleItem->NonPagedPoolCharge, ULONG_MAX));
getCellText->Text = PhGetStringRef(node->NonPageSizeText);
}
}
break;
default:
return FALSE;
}
getCellText->Flags = TN_CACHE;
}
return TRUE;
case TreeNewGetNodeColor:
{
PPH_TREENEW_GET_NODE_COLOR getNodeColor = Parameter1;
PPH_HANDLE_ITEM handleItem;
node = (PPH_HANDLE_NODE)getNodeColor->Node;
handleItem = node->HandleItem;
if (!handleItem)
; // Dummy
else if (PhCsUseColorProtectedInheritHandles && FlagOn(handleItem->Attributes, OBJ_PROTECT_CLOSE) && FlagOn(handleItem->Attributes, OBJ_INHERIT))
getNodeColor->BackColor = PhCsColorProtectedInheritHandles;
else if (PhCsUseColorProtectedHandles && FlagOn(handleItem->Attributes, OBJ_PROTECT_CLOSE))
getNodeColor->BackColor = PhCsColorProtectedHandles;
else if (PhCsUseColorInheritHandles && FlagOn(handleItem->Attributes, OBJ_INHERIT))
getNodeColor->BackColor = PhCsColorInheritHandles;
getNodeColor->Flags = TN_AUTO_FORECOLOR;
}
return TRUE;
case TreeNewSortChanged:
{
PPH_TREENEW_SORT_CHANGED_EVENT sorting = Parameter1;
context->TreeNewSortColumn = sorting->SortColumn;
context->TreeNewSortOrder = sorting->SortOrder;
// Force a rebuild to sort the items.
TreeNew_NodesStructured(hwnd);
}
return TRUE;
case TreeNewKeyDown:
{
PPH_TREENEW_KEY_EVENT keyEvent = Parameter1;
switch (keyEvent->VirtualKey)
{
case 'C':
if (GetKeyState(VK_CONTROL) < 0)
SendMessage(context->ParentWindowHandle, WM_COMMAND, ID_HANDLE_COPY, 0);
break;
case VK_DELETE:
// Pass a 1 in lParam to indicate that warnings should be enabled.
SendMessage(context->ParentWindowHandle, WM_COMMAND, ID_HANDLE_CLOSE, 1);
break;
case VK_RETURN:
if (GetKeyState(VK_CONTROL) >= 0)
SendMessage(context->ParentWindowHandle, WM_COMMAND, ID_HANDLE_PROPERTIES, 0);
else
SendMessage(context->ParentWindowHandle, WM_COMMAND, ID_HANDLE_OBJECTPROPERTIES1, 0);
break;
}
}
return TRUE;
case TreeNewHeaderRightClick:
{
PH_TN_COLUMN_MENU_DATA data;
memset(&data, 0, sizeof(PH_TN_COLUMN_MENU_DATA));
data.TreeNewHandle = hwnd;
data.MouseEvent = Parameter1;
data.DefaultSortColumn = PHHNTLC_TYPE;
data.DefaultSortOrder = AscendingSortOrder;
PhInitializeTreeNewColumnMenuEx(&data, PH_TN_COLUMN_MENU_SHOW_RESET_SORT);
data.Selection = PhShowEMenu(data.Menu, hwnd, PH_EMENU_SHOW_LEFTRIGHT,
PH_ALIGN_LEFT | PH_ALIGN_TOP, data.MouseEvent->ScreenLocation.x, data.MouseEvent->ScreenLocation.y);
PhHandleTreeNewColumnMenu(&data);
PhDeleteTreeNewColumnMenu(&data);
}
return TRUE;
case TreeNewLeftDoubleClick:
{
SendMessage(context->ParentWindowHandle, WM_COMMAND, ID_HANDLE_PROPERTIES, 0);
}
return TRUE;
case TreeNewContextMenu:
{
PPH_TREENEW_CONTEXT_MENU contextMenu = Parameter1;
SendMessage(context->ParentWindowHandle, WM_COMMAND, ID_SHOWCONTEXTMENU, (LPARAM)contextMenu);
}
return TRUE;
case TreeNewGetDialogCode:
{
PULONG code = Parameter2;
if (PtrToUlong(Parameter1) == VK_RETURN)
{
*code = DLGC_WANTMESSAGE;
return TRUE;
}
}
return FALSE;
}
return FALSE;
}
PPH_HANDLE_ITEM PhGetSelectedHandleItem(
_In_ PPH_HANDLE_LIST_CONTEXT Context
)
{
PPH_HANDLE_ITEM handleItem = NULL;
ULONG i;
for (i = 0; i < Context->NodeList->Count; i++)
{
PPH_HANDLE_NODE node = Context->NodeList->Items[i];
if (node->Node.Selected)
{
handleItem = node->HandleItem;
break;
}
}
return handleItem;
}
VOID PhGetSelectedHandleItems(
_In_ PPH_HANDLE_LIST_CONTEXT Context,
_Out_ PPH_HANDLE_ITEM **Handles,
_Out_ PULONG NumberOfHandles
)
{
PH_ARRAY array;
ULONG i;
PhInitializeArray(&array, sizeof(PVOID), 2);
for (i = 0; i < Context->NodeList->Count; i++)
{
PPH_HANDLE_NODE node = Context->NodeList->Items[i];
if (node->Node.Selected)
PhAddItemArray(&array, &node->HandleItem);
}
*NumberOfHandles = (ULONG)PhFinalArrayCount(&array);
*Handles = PhFinalArrayItems(&array);
}
VOID PhDeselectAllHandleNodes(
_In_ PPH_HANDLE_LIST_CONTEXT Context
)
{
TreeNew_DeselectRange(Context->TreeNewHandle, 0, -1);
}
================================================
FILE: SystemInformer/hndlmenu.c
================================================
/*
* Copyright (c) 2022 Winsider Seminars & Solutions, Inc. All rights reserved.
*
* This file is part of System Informer.
*
* Authors:
*
* wj32 2016
*
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
VOID PhInsertHandleObjectPropertiesEMenuItems(
_In_ PPH_EMENU_ITEM Menu,
_In_ ULONG InsertBeforeId,
_In_ BOOLEAN EnableShortcut,
_In_ PPH_HANDLE_ITEM_INFO Info
)
{
PPH_EMENU_ITEM parentItem;
ULONG indexInParent;
if (!PhFindEMenuItemEx(Menu, 0, NULL, InsertBeforeId, &parentItem, &indexInParent))
return;
if (PhIsNullOrEmptyString(Info->TypeName))
return;
if (PhEqualString2(Info->TypeName, L"File", TRUE) || PhEqualString2(Info->TypeName, L"DLL", TRUE) ||
PhEqualString2(Info->TypeName, L"Mapped file", TRUE) || PhEqualString2(Info->TypeName, L"Mapped image", TRUE))
{
if (PhEqualString2(Info->TypeName, L"File", TRUE))
{
PhInsertEMenuItem(parentItem, PhCreateEMenuItem(0, ID_HANDLE_OBJECTPROPERTIES2, L"File propert&ies", NULL, NULL), indexInParent);
PhInsertEMenuItem(parentItem, PhCreateEMenuItem(0, ID_HANDLE_OBJECTPROPERTIES1, PhaAppendCtrlEnter(L"Open &file location", EnableShortcut), NULL, NULL), indexInParent + 1);
PhInsertEMenuItem(parentItem, PhCreateEMenuSeparator(), indexInParent + 2);
}
else
{
PhInsertEMenuItem(parentItem, PhCreateEMenuItem(0, ID_HANDLE_OBJECTPROPERTIES1, PhaAppendCtrlEnter(L"Open &file location", EnableShortcut), NULL, NULL), indexInParent);
PhInsertEMenuItem(parentItem, PhCreateEMenuSeparator(), indexInParent + 1);
}
}
else if (PhEqualString2(Info->TypeName, L"Key", TRUE))
{
PhInsertEMenuItem(parentItem, PhCreateEMenuItem(0, ID_HANDLE_OBJECTPROPERTIES1, PhaAppendCtrlEnter(L"Open &key", EnableShortcut), NULL, NULL), indexInParent);
PhInsertEMenuItem(parentItem, PhCreateEMenuSeparator(), indexInParent + 1);
}
else if (PhEqualString2(Info->TypeName, L"Process", TRUE))
{
PhInsertEMenuItem(parentItem, PhCreateEMenuItem(0, ID_HANDLE_GOTOOWNINGPROCESS, L"Go to process...", NULL, NULL), indexInParent);
PhInsertEMenuItem(parentItem, PhCreateEMenuItem(0, ID_HANDLE_OBJECTPROPERTIES1, PhaAppendCtrlEnter(L"Process propert&ies", EnableShortcut), NULL, NULL), indexInParent + 1);
PhInsertEMenuItem(parentItem, PhCreateEMenuSeparator(), indexInParent + 2);
}
else if (PhEqualString2(Info->TypeName, L"Section", TRUE))
{
PhInsertEMenuItem(parentItem, PhCreateEMenuItem(0, ID_HANDLE_OBJECTPROPERTIES1, PhaAppendCtrlEnter(L"Read/Write &memory", EnableShortcut), NULL, NULL), indexInParent);
PhInsertEMenuItem(parentItem, PhCreateEMenuSeparator(), indexInParent + 1);
}
else if (PhEqualString2(Info->TypeName, L"Thread", TRUE))
{
PhInsertEMenuItem(parentItem, PhCreateEMenuItem(0, ID_HANDLE_OBJECTPROPERTIES1, PhaAppendCtrlEnter(L"Go to t&hread...", EnableShortcut), NULL, NULL), indexInParent);
PhInsertEMenuItem(parentItem, PhCreateEMenuSeparator(), indexInParent + 1);
}
}
static NTSTATUS PhpDuplicateHandleFromProcessItem(
_Out_ PHANDLE NewHandle,
_In_ ACCESS_MASK DesiredAccess,
_In_ HANDLE ProcessId,
_In_ HANDLE Handle
)
{
NTSTATUS status;
HANDLE processHandle;
if (!NT_SUCCESS(status = PhOpenProcess(
&processHandle,
PROCESS_DUP_HANDLE,
ProcessId
)))
return status;
status = NtDuplicateObject(
processHandle,
Handle,
NtCurrentProcess(),
NewHandle,
DesiredAccess,
0,
0
);
NtClose(processHandle);
return status;
}
static VOID PhpShowProcessPropContext(
_In_ PVOID Parameter
)
{
PhShowProcessProperties(Parameter);
PhDereferenceObject(Parameter);
}
VOID PhShowHandleObjectProperties1(
_In_ HWND hWnd,
_In_ PPH_HANDLE_ITEM_INFO Info
)
{
if (PhIsNullOrEmptyString(Info->TypeName))
return;
if (PhEqualString2(Info->TypeName, L"File", TRUE) || PhEqualString2(Info->TypeName, L"DLL", TRUE) ||
PhEqualString2(Info->TypeName, L"Mapped file", TRUE) || PhEqualString2(Info->TypeName, L"Mapped image", TRUE))
{
if (Info->BestObjectName)
{
PhShellExecuteUserString(
hWnd,
SETTING_FILE_BROWSE_EXECUTABLE,
Info->BestObjectName->Buffer,
FALSE,
L"Make sure the Explorer executable file is present."
);
}
else
PhShowError2(hWnd, L"Unable to open the file location.", L"%s", L"The object is unnamed.");
}
else if (PhEqualString2(Info->TypeName, L"Key", TRUE))
{
if (Info->BestObjectName)
PhShellOpenKey2(hWnd, Info->BestObjectName);
else
PhShowError2(hWnd, L"Unable to open key.", L"%s", L"The object is unnamed.");
}
else if (PhEqualString2(Info->TypeName, L"Process", TRUE))
{
HANDLE processHandle;
HANDLE processId;
PPH_PROCESS_ITEM targetProcessItem;
processId = NULL;
if (KsiLevel() >= KphLevelMed)
{
if (NT_SUCCESS(PhOpenProcess(
&processHandle,
PROCESS_QUERY_LIMITED_INFORMATION,
Info->ProcessId
)))
{
PROCESS_BASIC_INFORMATION basicInfo;
if (NT_SUCCESS(KphQueryInformationObject(
processHandle,
Info->Handle,
KphObjectProcessBasicInformation,
&basicInfo,
sizeof(PROCESS_BASIC_INFORMATION),
NULL
)))
{
processId = basicInfo.UniqueProcessId;
}
NtClose(processHandle);
}
}
else
{
HANDLE handle;
PROCESS_BASIC_INFORMATION basicInfo;
if (NT_SUCCESS(PhpDuplicateHandleFromProcessItem(
&handle,
PROCESS_QUERY_LIMITED_INFORMATION,
Info->ProcessId,
Info->Handle
)))
{
if (NT_SUCCESS(PhGetProcessBasicInformation(handle, &basicInfo)))
processId = basicInfo.UniqueProcessId;
NtClose(handle);
}
}
if (processId)
{
targetProcessItem = PhReferenceProcessItem(processId);
if (!targetProcessItem)
{
NTSTATUS status;
status = PhOpenProcess(
&processHandle,
PROCESS_QUERY_LIMITED_INFORMATION,
processId
);
if (NT_SUCCESS(status))
{
targetProcessItem = PhCreateProcessItemFromHandle(
processId,
processHandle,
TRUE
);
}
}
if (targetProcessItem)
{
SystemInformer_ShowProcessProperties(targetProcessItem);
PhDereferenceObject(targetProcessItem);
}
else
{
PhShowError2(hWnd, L"Unable to show the process properties.", L"%s", L"The process does not exist.");
}
}
}
else if (PhEqualString2(Info->TypeName, L"Section", TRUE))
{
NTSTATUS status;
HANDLE handle = NULL;
BOOLEAN readOnly = FALSE;
if (!NT_SUCCESS(status = PhpDuplicateHandleFromProcessItem(
&handle,
SECTION_QUERY | SECTION_MAP_READ | SECTION_MAP_WRITE,
Info->ProcessId,
Info->Handle
)))
{
status = PhpDuplicateHandleFromProcessItem(
&handle,
SECTION_QUERY | SECTION_MAP_READ,
Info->ProcessId,
Info->Handle
);
readOnly = TRUE;
}
if (handle)
{
PPH_STRING sectionName = NULL;
SECTION_BASIC_INFORMATION basicInfo;
SIZE_T viewSize = PH_MAX_SECTION_EDIT_SIZE;
PVOID viewBase = NULL;
BOOLEAN tooBig = FALSE;
PhGetHandleInformation(NtCurrentProcess(), handle, ULONG_MAX, NULL, NULL, NULL, §ionName);
if (NT_SUCCESS(status = PhGetSectionBasicInformation(handle, &basicInfo)))
{
if (basicInfo.MaximumSize.QuadPart <= PH_MAX_SECTION_EDIT_SIZE)
viewSize = (SIZE_T)basicInfo.MaximumSize.QuadPart;
else
tooBig = TRUE;
status = PhMapViewOfSection(
handle,
NtCurrentProcess(),
&viewBase,
0,
NULL,
&viewSize,
ViewUnmap,
0,
readOnly ? PAGE_READONLY : PAGE_READWRITE
);
if (status == STATUS_SECTION_PROTECTION && !readOnly)
{
status = PhMapViewOfSection(
handle,
NtCurrentProcess(),
&viewBase,
0,
NULL,
&viewSize,
ViewUnmap,
0,
PAGE_READONLY
);
}
if (NT_SUCCESS(status))
{
PPH_SHOW_MEMORY_EDITOR showMemoryEditor = PhAllocate(sizeof(PH_SHOW_MEMORY_EDITOR));
if (tooBig)
{
PhShowWarning2(hWnd, L"Unable to map a view of the section.", L"%s", L"The section size is greater than 32 MB. Only the first 32 MB will be available.");
}
memset(showMemoryEditor, 0, sizeof(PH_SHOW_MEMORY_EDITOR));
showMemoryEditor->ProcessId = NtCurrentProcessId();
showMemoryEditor->BaseAddress = viewBase;
showMemoryEditor->RegionSize = viewSize;
showMemoryEditor->SelectOffset = ULONG_MAX;
showMemoryEditor->SelectLength = 0;
showMemoryEditor->Title = sectionName ? PhConcatStrings2(L"Section - ", sectionName->Buffer) : PhCreateString(L"Section");
showMemoryEditor->Flags = PH_MEMORY_EDITOR_UNMAP_VIEW_OF_SECTION;
SystemInformer_ShowMemoryEditor(showMemoryEditor);
}
else
{
PhShowStatus(hWnd, L"Unable to map a view of the section.", status, 0);
}
}
else
{
PhShowStatus(hWnd, L"Unable to query the section.", status, 0);
}
PhClearReference(§ionName);
NtClose(handle);
}
}
else if (PhEqualString2(Info->TypeName, L"Thread", TRUE))
{
HANDLE processHandle;
CLIENT_ID clientId;
PPH_PROCESS_ITEM targetProcessItem;
PPH_PROCESS_PROPCONTEXT propContext;
clientId.UniqueProcess = NULL;
clientId.UniqueThread = NULL;
if (KsiLevel() >= KphLevelMed)
{
if (NT_SUCCESS(PhOpenProcess(
&processHandle,
PROCESS_QUERY_LIMITED_INFORMATION,
Info->ProcessId
)))
{
THREAD_BASIC_INFORMATION basicInfo;
if (NT_SUCCESS(KphQueryInformationObject(
processHandle,
Info->Handle,
KphObjectThreadBasicInformation,
&basicInfo,
sizeof(THREAD_BASIC_INFORMATION),
NULL
)))
{
clientId = basicInfo.ClientId;
}
NtClose(processHandle);
}
}
else
{
HANDLE handle;
THREAD_BASIC_INFORMATION basicInfo;
if (NT_SUCCESS(PhpDuplicateHandleFromProcessItem(
&handle,
THREAD_QUERY_LIMITED_INFORMATION,
Info->ProcessId,
Info->Handle
)))
{
if (NT_SUCCESS(PhGetThreadBasicInformation(handle, &basicInfo)))
clientId = basicInfo.ClientId;
NtClose(handle);
}
}
if (clientId.UniqueProcess)
{
targetProcessItem = PhReferenceProcessItem(clientId.UniqueProcess);
if (!targetProcessItem)
{
NTSTATUS status;
status = PhOpenProcessClientId(
&processHandle,
PROCESS_QUERY_LIMITED_INFORMATION,
&clientId
);
if (NT_SUCCESS(status))
{
targetProcessItem = PhCreateProcessItemFromHandle(
clientId.UniqueProcess,
processHandle,
TRUE
);
}
}
if (targetProcessItem)
{
propContext = PhCreateProcessPropContext(NULL, targetProcessItem);
PhDereferenceObject(targetProcessItem);
PhSetSelectThreadIdProcessPropContext(propContext, clientId.UniqueThread);
SystemInformer_Invoke(PhpShowProcessPropContext, propContext);
}
else
{
PhShowError2(hWnd, L"Unable to show the process properties.", L"%s", L"The process does not exist.");
}
}
}
}
VOID PhShowHandleObjectProperties2(
_In_ HWND hWnd,
_In_ PPH_HANDLE_ITEM_INFO Info
)
{
if (PhIsNullOrEmptyString(Info->TypeName))
return;
if (PhEqualString2(Info->TypeName, L"File", TRUE) || PhEqualString2(Info->TypeName, L"DLL", TRUE) ||
PhEqualString2(Info->TypeName, L"Mapped file", TRUE) || PhEqualString2(Info->TypeName, L"Mapped image", TRUE))
{
if (Info->BestObjectName)
PhShellProperties(hWnd, Info->BestObjectName->Buffer);
else
PhShowError2(hWnd, L"Unable to open the file properties.", L"%s", L"The object is unnamed.");
}
}
================================================
FILE: SystemInformer/hndlprp.c
================================================
/*
* Copyright (c) 2022 Winsider Seminars & Solutions, Inc. All rights reserved.
*
* This file is part of System Informer.
*
* Authors:
*
* wj32 2010-2013
* dmex 2018-2024
*
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
typedef enum _PHP_HANDLE_GENERAL_CATEGORY
{
// common
PH_HANDLE_GENERAL_CATEGORY_BASICINFO,
PH_HANDLE_GENERAL_CATEGORY_SECURITY,
PH_HANDLE_GENERAL_CATEGORY_REFERENCES,
PH_HANDLE_GENERAL_CATEGORY_QUOTA,
// extra
PH_HANDLE_GENERAL_CATEGORY_ALPC,
PH_HANDLE_GENERAL_CATEGORY_FILE,
PH_HANDLE_GENERAL_CATEGORY_SECTION,
PH_HANDLE_GENERAL_CATEGORY_MUTANT,
PH_HANDLE_GENERAL_CATEGORY_PROCESSTHREAD,
PH_HANDLE_GENERAL_CATEGORY_ETW,
PH_HANDLE_GENERAL_CATEGORY_SYMBOLICLINK,
PH_HANDLE_GENERAL_CATEGORY_MAXIMUM
} PHP_HANDLE_GENERAL_CATEGORY;
typedef enum _PHP_HANDLE_GENERAL_INDEX
{
PH_HANDLE_GENERAL_INDEX_NAME,
PH_HANDLE_GENERAL_INDEX_TYPE,
PH_HANDLE_GENERAL_INDEX_OBJECT,
PH_HANDLE_GENERAL_INDEX_FULLNAME,
PH_HANDLE_GENERAL_INDEX_ACCESSS,
PH_HANDLE_GENERAL_INDEX_ACCESSGENERIC,
PH_HANDLE_GENERAL_INDEX_ACCESSMASK,
PH_HANDLE_GENERAL_INDEX_SDDL,
PH_HANDLE_GENERAL_INDEX_REFERENCES,
PH_HANDLE_GENERAL_INDEX_HANDLES,
PH_HANDLE_GENERAL_INDEX_PAGED,
PH_HANDLE_GENERAL_INDEX_NONPAGED,
PH_HANDLE_GENERAL_INDEX_FLAGS,
PH_HANDLE_GENERAL_INDEX_SEQUENCENUMBER,
PH_HANDLE_GENERAL_INDEX_PORTCONTEXT,
PH_HANDLE_GENERAL_INDEX_FILETYPE,
PH_HANDLE_GENERAL_INDEX_FILEMODE,
PH_HANDLE_GENERAL_INDEX_FILEPOSITION,
PH_HANDLE_GENERAL_INDEX_FILESIZE,
PH_HANDLE_GENERAL_INDEX_FILEPRIORITY,
PH_HANDLE_GENERAL_INDEX_FILEDRIVER,
PH_HANDLE_GENERAL_INDEX_FILEDRIVERIMAGE,
PH_HANDLE_GENERAL_INDEX_SECTIONTYPE,
PH_HANDLE_GENERAL_INDEX_SECTIONFILE,
PH_HANDLE_GENERAL_INDEX_SECTIONSIZE,
PH_HANDLE_GENERAL_INDEX_MUTANTCOUNT,
PH_HANDLE_GENERAL_INDEX_MUTANTABANDONED,
PH_HANDLE_GENERAL_INDEX_MUTANTOWNER,
PH_HANDLE_GENERAL_INDEX_ALPCCONNECTION,
PH_HANDLE_GENERAL_INDEX_ALPCSERVER,
PH_HANDLE_GENERAL_INDEX_ALPCCLIENT,
PH_HANDLE_GENERAL_INDEX_ALPCOWNER,
PH_HANDLE_GENERAL_INDEX_PROCESSTHREADNAME,
PH_HANDLE_GENERAL_INDEX_PROCESSTHREADCREATETIME,
PH_HANDLE_GENERAL_INDEX_PROCESSTHREADEXITTIME,
PH_HANDLE_GENERAL_INDEX_PROCESSTHREADEXITCODE,
PH_HANDLE_GENERAL_INDEX_ETWORIGINALNAME,
PH_HANDLE_GENERAL_INDEX_ETWGROUPNAME,
PH_HANDLE_GENERAL_INDEX_SYMBOLICLINKLINK,
PH_HANDLE_GENERAL_INDEX_MAXIMUM
} PHP_HANDLE_GENERAL_INDEX;
typedef struct _HANDLE_PROPERTIES_CONTEXT
{
HWND ListViewHandle;
HWND ParentWindow;
HANDLE ProcessId;
PPH_LISTVIEW_CONTEXT ListViewClass;
PPH_HANDLE_ITEM HandleItem;
PH_LAYOUT_MANAGER LayoutManager;
} HANDLE_PROPERTIES_CONTEXT, *PHANDLE_PROPERTIES_CONTEXT;
typedef struct _HANDLE_PERMISSIONS_CONTEXT
{
PHANDLE_PROPERTIES_CONTEXT HandleProperties;
HWND ListViewHeader;
HWND ListViewHandle;
HWND ParentWindow;
HANDLE ProcessId;
PPH_LISTVIEW_CONTEXT ListViewClass;
PPH_HANDLE_ITEM HandleItem;
PH_LAYOUT_MANAGER LayoutManager;
} HANDLE_PERMISSIONS_CONTEXT, *PHANDLE_PERMISSIONS_CONTEXT;
#define PH_FILEMODE_ASYNC 0x01000000
#define PhFileModeUpdAsyncFlag(mode) \
((mode) & (FILE_SYNCHRONOUS_IO_ALERT | FILE_SYNCHRONOUS_IO_NONALERT) ? (mode) &~ PH_FILEMODE_ASYNC: (mode) | PH_FILEMODE_ASYNC)
CONST PH_ACCESS_ENTRY FileModeAccessEntries[] =
{
{ L"FILE_FLAG_OVERLAPPED", PH_FILEMODE_ASYNC, FALSE, FALSE, L"Asynchronous" },
{ L"FILE_FLAG_WRITE_THROUGH", FILE_WRITE_THROUGH, FALSE, FALSE, L"Write through" },
{ L"FILE_FLAG_SEQUENTIAL_SCAN", FILE_SEQUENTIAL_ONLY, FALSE, FALSE, L"Sequential" },
{ L"FILE_FLAG_NO_BUFFERING", FILE_NO_INTERMEDIATE_BUFFERING, FALSE, FALSE, L"No buffering" },
{ L"FILE_SYNCHRONOUS_IO_ALERT", FILE_SYNCHRONOUS_IO_ALERT, FALSE, FALSE, L"Synchronous alert" },
{ L"FILE_SYNCHRONOUS_IO_NONALERT", FILE_SYNCHRONOUS_IO_NONALERT, FALSE, FALSE, L"Synchronous non-alert" },
};
CONST PH_ACCESS_ENTRY AlpcFlags[] =
{
{ L"ALPC_PORFLG_LPC_MODE", ALPC_PORFLG_LPC_MODE, FALSE, FALSE, L"LPC mode"},
{ L"ALPC_PORFLG_ALLOW_IMPERSONATION", ALPC_PORFLG_ALLOW_IMPERSONATION, FALSE, FALSE, L"Allow impersonation"},
{ L"ALPC_PORFLG_ALLOW_LPC_REQUESTS", ALPC_PORFLG_ALLOW_LPC_REQUESTS, FALSE, FALSE, L"Allow LPC requests"},
{ L"ALPC_PORFLG_WAITABLE_PORT", ALPC_PORFLG_WAITABLE_PORT, FALSE, FALSE, L"Waitable"},
{ L"ALPC_PORFLG_ALLOW_DUP_OBJECT", ALPC_PORFLG_ALLOW_DUP_OBJECT, FALSE, FALSE, L"Allow object duplication"},
{ L"ALPC_PORFLG_SYSTEM_PROCESS", ALPC_PORFLG_SYSTEM_PROCESS, FALSE, FALSE, L"System process only"},
{ L"ALPC_PORFLG_WAKE_POLICY1", ALPC_PORFLG_WAKE_POLICY1, FALSE, FALSE, L"Wake policy (1)"},
{ L"ALPC_PORFLG_WAKE_POLICY2", ALPC_PORFLG_WAKE_POLICY2, FALSE, FALSE, L"Wake policy (2)"},
{ L"ALPC_PORFLG_WAKE_POLICY3", ALPC_PORFLG_WAKE_POLICY3, FALSE, FALSE, L"Wake policy (3)"},
{ L"ALPC_PORFLG_DIRECT_MESSAGE", ALPC_PORFLG_DIRECT_MESSAGE, FALSE, FALSE, L"No shared section (direct)"},
{ L"ALPC_PORFLG_ALLOW_MULTIHANDLE_ATTRIBUTE", ALPC_PORFLG_ALLOW_MULTIHANDLE_ATTRIBUTE, FALSE, FALSE, L"Allow multi-handle attributes"},
};
INT_PTR CALLBACK PhpHandleGeneralDlgProc(
_In_ HWND hwndDlg,
_In_ UINT uMsg,
_In_ WPARAM wParam,
_In_ LPARAM lParam
);
INT_PTR CALLBACK PhpHandlePermissionsDlgProc(
_In_ HWND hwndDlg,
_In_ UINT uMsg,
_In_ WPARAM wParam,
_In_ LPARAM lParam
);
INT_PTR CALLBACK PhpHandleAuditingDlgProc(
_In_ HWND hwndDlg,
_In_ UINT uMsg,
_In_ WPARAM wParam,
_In_ LPARAM lParam
);
_Function_class_(PH_OPEN_OBJECT)
static NTSTATUS PhpDuplicateHandleFromProcess(
_Out_ PHANDLE Handle,
_In_ ACCESS_MASK DesiredAccess,
_In_ PVOID Context
)
{
PHANDLE_PROPERTIES_CONTEXT context = Context;
NTSTATUS status;
HANDLE processHandle;
*Handle = NULL;
if (NT_SUCCESS(status = PhOpenProcess(
&processHandle,
PROCESS_DUP_HANDLE,
context->ProcessId
)))
{
status = NtDuplicateObject(
processHandle,
context->HandleItem->Handle,
NtCurrentProcess(),
Handle,
DesiredAccess,
0,
0
);
NtClose(processHandle);
}
if (!NT_SUCCESS(status) && KsiLevel() >= KphLevelMax)
{
if (NT_SUCCESS(status = PhOpenProcess(
&processHandle,
PROCESS_QUERY_LIMITED_INFORMATION,
context->ProcessId
)))
{
status = KphDuplicateObject(
processHandle,
context->HandleItem->Handle,
DesiredAccess,
Handle
);
NtClose(processHandle);
}
}
return status;
}
_Function_class_(PH_CLOSE_OBJECT)
static NTSTATUS PhpDuplicateHandleCloseProcess(
_In_opt_ HANDLE Handle,
_In_opt_ BOOLEAN Release,
_In_opt_ PVOID Context
)
{
if (Handle)
NtClose(Handle);
return STATUS_SUCCESS;
}
typedef struct _HANDLE_PROPERTIES_THREAD_CONTEXT
{
HWND ParentWindowHandle;
HANDLE ProcessId;
PPH_HANDLE_ITEM HandleItem;
} HANDLE_PROPERTIES_THREAD_CONTEXT, *PHANDLE_PROPERTIES_THREAD_CONTEXT;
BOOLEAN PhpIsVerboseBestObjectName(
_In_ PPH_HANDLE_ITEM HandleItem
)
{
// Some handles use a verbose BestObjectName for the sake of searching. And will display
// extended information in the property window consisting of the information contained in the
// BestObjectName. This routine is used to fall back to the normal ObjectName in the property
// window for these cases. (jxy-s)
if (PhIsNullOrEmptyString(HandleItem->TypeName))
return FALSE;
if (PhEqualString2(HandleItem->TypeName, L"ALPC Port", TRUE))
return TRUE;
if (PhEqualString2(HandleItem->TypeName, L"File", TRUE) &&
PhAfdIsSocketObjectName(HandleItem->ObjectName))
return TRUE;
return FALSE;
}
_Function_class_(PH_TYPE_DELETE_PROCEDURE)
static VOID PhHandlePropertiesContextDeleteProcedure(
_In_ PVOID Object,
_In_ ULONG Flags
)
{
PHANDLE_PROPERTIES_CONTEXT context = Object;
PhDereferenceObject(context->HandleItem);
}
PHANDLE_PROPERTIES_CONTEXT PhCreateHandlePropertiesContext(
VOID
)
{
static PH_INITONCE initOnce = PH_INITONCE_INIT;
static PPH_OBJECT_TYPE PhHandlePropertiesContextType = NULL;
PHANDLE_PROPERTIES_CONTEXT context;
if (PhBeginInitOnce(&initOnce))
{
PhHandlePropertiesContextType = PhCreateObjectType(L"HandlePropertiesItem", 0, PhHandlePropertiesContextDeleteProcedure);
PhEndInitOnce(&initOnce);
}
context = PhCreateObject(sizeof(HANDLE_PROPERTIES_CONTEXT), PhHandlePropertiesContextType);
memset(context, 0, sizeof(HANDLE_PROPERTIES_CONTEXT));
return context;
}
_Function_class_(USER_THREAD_START_ROUTINE)
NTSTATUS PhpShowHandlePropertiesThread(
_In_ PVOID Parameter
)
{
PHANDLE_PROPERTIES_THREAD_CONTEXT handleContext = Parameter;
PROPSHEETHEADER propSheetHeader = { sizeof(PROPSHEETHEADER) };
PROPSHEETPAGE propSheetPage;
HPROPSHEETPAGE pages[16];
PHANDLE_PROPERTIES_CONTEXT context;
PH_AUTO_POOL autoPool;
context = PhCreateHandlePropertiesContext();
context->ProcessId = handleContext->ProcessId;
context->HandleItem = handleContext->HandleItem;
context->ParentWindow = handleContext->ParentWindowHandle;
PhInitializeAutoPool(&autoPool);
propSheetHeader.dwFlags =
PSH_MODELESS |
PSH_NOAPPLYNOW |
PSH_NOCONTEXTHELP |
PSH_PROPTITLE;
propSheetHeader.hInstance = NtCurrentImageBase();
propSheetHeader.hwndParent = PhCsForceNoParent ? NULL : handleContext->ParentWindowHandle;
propSheetHeader.pszCaption = L"Handle";
propSheetHeader.nPages = 0;
propSheetHeader.nStartPage = 0;
propSheetHeader.phpage = pages;
// General page
memset(&propSheetPage, 0, sizeof(PROPSHEETPAGE));
propSheetPage.dwSize = sizeof(PROPSHEETPAGE);
propSheetPage.pszTemplate = MAKEINTRESOURCE(IDD_HNDLGENERAL);
propSheetPage.hInstance = NtCurrentImageBase();
propSheetPage.pfnDlgProc = PhpHandleGeneralDlgProc;
propSheetPage.lParam = (LPARAM)context;
pages[propSheetHeader.nPages++] = CreatePropertySheetPage(&propSheetPage);
// Permissions page
memset(&propSheetPage, 0, sizeof(PROPSHEETPAGE));
propSheetPage.dwSize = sizeof(PROPSHEETPAGE);
propSheetPage.pszTemplate = MAKEINTRESOURCE(IDD_HNDLSECURITY);
propSheetPage.hInstance = NtCurrentImageBase();
propSheetPage.pfnDlgProc = PhpHandlePermissionsDlgProc;
propSheetPage.lParam = (LPARAM)context;
pages[propSheetHeader.nPages++] = CreatePropertySheetPage(&propSheetPage);
// Audiing page
memset(&propSheetPage, 0, sizeof(PROPSHEETPAGE));
propSheetPage.dwSize = sizeof(PROPSHEETPAGE);
propSheetPage.pszTemplate = MAKEINTRESOURCE(IDD_HNDLSECAUDIT);
propSheetPage.hInstance = NtCurrentImageBase();
propSheetPage.pfnDlgProc = PhpHandleAuditingDlgProc;
propSheetPage.lParam = (LPARAM)context;
pages[propSheetHeader.nPages++] = CreatePropertySheetPage(&propSheetPage);
// Object-specific page
if (PhIsNullOrEmptyString(handleContext->HandleItem->TypeName))
{
NOTHING;
}
else if (PhEqualString2(handleContext->HandleItem->TypeName, L"Event", TRUE))
{
pages[propSheetHeader.nPages++] = PhCreateEventPage(
PhpDuplicateHandleFromProcess,
PhpDuplicateHandleCloseProcess,
context
);
}
else if (PhEqualString2(handleContext->HandleItem->TypeName, L"EventPair", TRUE))
{
pages[propSheetHeader.nPages++] = PhCreateEventPairPage(
PhpDuplicateHandleFromProcess,
PhpDuplicateHandleCloseProcess,
context
);
}
else if (PhEqualString2(handleContext->HandleItem->TypeName, L"Job", TRUE))
{
pages[propSheetHeader.nPages++] = PhCreateJobPage(
PhpDuplicateHandleFromProcess,
PhpDuplicateHandleCloseProcess,
context,
NULL
);
}
else if (PhEqualString2(handleContext->HandleItem->TypeName, L"Semaphore", TRUE))
{
pages[propSheetHeader.nPages++] = PhCreateSemaphorePage(
PhpDuplicateHandleFromProcess,
PhpDuplicateHandleCloseProcess,
context
);
}
else if (PhEqualString2(handleContext->HandleItem->TypeName, L"Timer", TRUE))
{
pages[propSheetHeader.nPages++] = PhCreateTimerPage(
PhpDuplicateHandleFromProcess,
PhpDuplicateHandleCloseProcess,
context
);
}
else if (PhEqualString2(handleContext->HandleItem->TypeName, L"Token", TRUE))
{
pages[propSheetHeader.nPages++] = PhCreateTokenPage(
PhpDuplicateHandleFromProcess,
PhpDuplicateHandleCloseProcess,
context->ProcessId,
context,
NULL
);
}
else if (PhEqualString2(handleContext->HandleItem->TypeName, L"Section", TRUE))
{
pages[propSheetHeader.nPages++] = PhCreateMappingsPage(
handleContext->ProcessId,
handleContext->HandleItem->Handle
);
}
else if (PhEqualString2(handleContext->HandleItem->TypeName, L"File", TRUE) &&
PhAfdIsSocketObjectName(handleContext->HandleItem->ObjectName))
{
pages[propSheetHeader.nPages++] = PhCreateAfdSocketPage(
context->ProcessId,
context->HandleItem->Handle
);
}
// Security page
{
PCWSTR objectName;
if (PhpIsVerboseBestObjectName(handleContext->HandleItem))
objectName = PhGetStringOrEmpty(handleContext->HandleItem->ObjectName);
else
objectName = PhGetStringOrEmpty(handleContext->HandleItem->BestObjectName);
pages[propSheetHeader.nPages++] = PhCreateSecurityPage(
objectName,
PhGetStringOrEmpty(handleContext->HandleItem->TypeName),
PhpDuplicateHandleFromProcess,
PhpDuplicateHandleCloseProcess,
context
);
}
if (PhPluginsEnabled)
{
PH_PLUGIN_OBJECT_PROPERTIES objectProperties;
PH_PLUGIN_HANDLE_PROPERTIES_CONTEXT propertiesContext;
memset(&propertiesContext, 0, sizeof(PH_PLUGIN_HANDLE_PROPERTIES_CONTEXT));
propertiesContext.ParentWindowHandle = handleContext->ParentWindowHandle;
propertiesContext.ProcessId = handleContext->ProcessId;
propertiesContext.HandleItem = handleContext->HandleItem;
memset(&objectProperties, 0, sizeof(PH_PLUGIN_OBJECT_PROPERTIES));
objectProperties.Parameter = &propertiesContext;
objectProperties.NumberOfPages = propSheetHeader.nPages;
objectProperties.MaximumNumberOfPages = RTL_NUMBER_OF(pages);
objectProperties.Pages = pages;
PhInvokeCallback(PhGetGeneralCallback(GeneralCallbackHandlePropertiesInitializing), &objectProperties);
propSheetHeader.nPages = objectProperties.NumberOfPages;
}
PhModalPropertySheet(&propSheetHeader);
PhDeleteAutoPool(&autoPool);
PhDereferenceObject(context);
PhFree(handleContext);
return STATUS_SUCCESS;
}
VOID PhShowHandleProperties(
_In_ HWND ParentWindowHandle,
_In_ HANDLE ProcessId,
_In_ PPH_HANDLE_ITEM HandleItem
)
{
PHANDLE_PROPERTIES_THREAD_CONTEXT context;
context = PhAllocateZero(sizeof(HANDLE_PROPERTIES_THREAD_CONTEXT));
context->ParentWindowHandle = ParentWindowHandle;
context->ProcessId = ProcessId;
context->HandleItem = HandleItem;
PhReferenceObject(HandleItem);
PhCreateThread2(PhpShowHandlePropertiesThread, context);
}
VOID PhAddHandleListViewItem(
_In_ PPH_LISTVIEW_CONTEXT ListViewClass,
_In_ LONG GroupId,
_In_ LONG Index,
_In_ PCWSTR Text
)
{
PhListView_AddGroupItem(ListViewClass, GroupId, Index, Text, UlongToPtr(Index));
}
VOID PhSetHandleListViewItem(
_In_ PHANDLE_PROPERTIES_CONTEXT Context,
_In_ LONG Index,
_In_ LONG SubItemIndex,
_In_ PCWSTR Text
)
{
LONG index = PhFindListViewItemByParam(Context->ListViewHandle, INT_ERROR, UlongToPtr(Index));
if (index != INT_ERROR)
{
PhListView_SetSubItem(Context->ListViewClass, index, SubItemIndex, Text);
}
}
VOID PhpUpdateHandleGeneralListViewGroups(
_In_ PHANDLE_PROPERTIES_CONTEXT Context
)
{
PhListView_EnableGroupView(Context->ListViewClass, TRUE);
PhListView_AddGroup(Context->ListViewClass, PH_HANDLE_GENERAL_CATEGORY_BASICINFO, L"Basic information");
PhListView_AddGroup(Context->ListViewClass, PH_HANDLE_GENERAL_CATEGORY_SECURITY, L"Security information");
PhListView_AddGroup(Context->ListViewClass, PH_HANDLE_GENERAL_CATEGORY_REFERENCES, L"References");
PhListView_AddGroup(Context->ListViewClass, PH_HANDLE_GENERAL_CATEGORY_QUOTA, L"Quota charges");
PhAddHandleListViewItem(Context->ListViewClass, PH_HANDLE_GENERAL_CATEGORY_BASICINFO, PH_HANDLE_GENERAL_INDEX_NAME, L"Name");
PhAddHandleListViewItem(Context->ListViewClass, PH_HANDLE_GENERAL_CATEGORY_BASICINFO, PH_HANDLE_GENERAL_INDEX_TYPE, L"Type");
PhAddHandleListViewItem(Context->ListViewClass, PH_HANDLE_GENERAL_CATEGORY_BASICINFO, PH_HANDLE_GENERAL_INDEX_OBJECT, L"Object address");
PhAddHandleListViewItem(Context->ListViewClass, PH_HANDLE_GENERAL_CATEGORY_BASICINFO, PH_HANDLE_GENERAL_INDEX_FULLNAME, L"FullPath");
PhAddHandleListViewItem(Context->ListViewClass, PH_HANDLE_GENERAL_CATEGORY_SECURITY, PH_HANDLE_GENERAL_INDEX_ACCESSS, L"Granted access");
PhAddHandleListViewItem(Context->ListViewClass, PH_HANDLE_GENERAL_CATEGORY_SECURITY, PH_HANDLE_GENERAL_INDEX_ACCESSGENERIC, L"Granted access (generic)");
PhAddHandleListViewItem(Context->ListViewClass, PH_HANDLE_GENERAL_CATEGORY_SECURITY, PH_HANDLE_GENERAL_INDEX_ACCESSMASK, L"Granted access (mask)");
PhAddHandleListViewItem(Context->ListViewClass, PH_HANDLE_GENERAL_CATEGORY_SECURITY, PH_HANDLE_GENERAL_INDEX_SDDL, L"SDDL");
PhAddHandleListViewItem(Context->ListViewClass, PH_HANDLE_GENERAL_CATEGORY_REFERENCES, PH_HANDLE_GENERAL_INDEX_REFERENCES, L"References");
PhAddHandleListViewItem(Context->ListViewClass, PH_HANDLE_GENERAL_CATEGORY_REFERENCES, PH_HANDLE_GENERAL_INDEX_HANDLES, L"Handles");
PhAddHandleListViewItem(Context->ListViewClass, PH_HANDLE_GENERAL_CATEGORY_QUOTA, PH_HANDLE_GENERAL_INDEX_PAGED, L"Paged");
PhAddHandleListViewItem(Context->ListViewClass, PH_HANDLE_GENERAL_CATEGORY_QUOTA, PH_HANDLE_GENERAL_INDEX_NONPAGED, L"Virtual size");
if (PhIsNullOrEmptyString(Context->HandleItem->TypeName))
{
NOTHING;
}
else if (PhEqualString2(Context->HandleItem->TypeName, L"ALPC Port", TRUE))
{
PhListView_AddGroup(Context->ListViewClass, PH_HANDLE_GENERAL_CATEGORY_ALPC, L"ALPC Port");
PhAddHandleListViewItem(Context->ListViewClass, PH_HANDLE_GENERAL_CATEGORY_ALPC, PH_HANDLE_GENERAL_INDEX_FLAGS, L"Flags");
PhAddHandleListViewItem(Context->ListViewClass, PH_HANDLE_GENERAL_CATEGORY_ALPC, PH_HANDLE_GENERAL_INDEX_SEQUENCENUMBER, L"Sequence Number");
PhAddHandleListViewItem(Context->ListViewClass, PH_HANDLE_GENERAL_CATEGORY_ALPC, PH_HANDLE_GENERAL_INDEX_PORTCONTEXT, L"Port Context");
if (KsiLevel() >= KphLevelMed)
{
PhAddHandleListViewItem(Context->ListViewClass, PH_HANDLE_GENERAL_CATEGORY_ALPC, PH_HANDLE_GENERAL_INDEX_ALPCCONNECTION, L"Connection");
PhAddHandleListViewItem(Context->ListViewClass, PH_HANDLE_GENERAL_CATEGORY_ALPC, PH_HANDLE_GENERAL_INDEX_ALPCSERVER, L"Server");
PhAddHandleListViewItem(Context->ListViewClass, PH_HANDLE_GENERAL_CATEGORY_ALPC, PH_HANDLE_GENERAL_INDEX_ALPCCLIENT, L"Client");
}
if (WindowsVersion >= WINDOWS_10_19H2)
{
PhAddHandleListViewItem(Context->ListViewClass, PH_HANDLE_GENERAL_CATEGORY_ALPC, PH_HANDLE_GENERAL_INDEX_ALPCOWNER, L"Owner");
}
}
else if (PhEqualString2(Context->HandleItem->TypeName, L"EtwRegistration", TRUE))
{
PhListView_AddGroup(Context->ListViewClass, PH_HANDLE_GENERAL_CATEGORY_ETW, L"Event trace information");
PhAddHandleListViewItem(Context->ListViewClass, PH_HANDLE_GENERAL_CATEGORY_ETW, PH_HANDLE_GENERAL_INDEX_ETWORIGINALNAME, L"GUID");
//PhAddHandleListViewItem(Context->ListViewClass, PH_HANDLE_GENERAL_CATEGORY_ETW, PH_HANDLE_GENERAL_INDEX_ETWGROUPNAME, L"Group GUID");
}
else if (PhEqualStringRef2(&Context->HandleItem->TypeName->sr, L"File", TRUE))
{
PhListView_AddGroup(Context->ListViewClass, PH_HANDLE_GENERAL_CATEGORY_FILE, L"File information");
PhAddHandleListViewItem(Context->ListViewClass, PH_HANDLE_GENERAL_CATEGORY_FILE, PH_HANDLE_GENERAL_INDEX_FILETYPE, L"Type");
PhAddHandleListViewItem(Context->ListViewClass, PH_HANDLE_GENERAL_CATEGORY_FILE, PH_HANDLE_GENERAL_INDEX_FILEMODE, L"Mode");
PhAddHandleListViewItem(Context->ListViewClass, PH_HANDLE_GENERAL_CATEGORY_FILE, PH_HANDLE_GENERAL_INDEX_FILEPOSITION, L"Position");
PhAddHandleListViewItem(Context->ListViewClass, PH_HANDLE_GENERAL_CATEGORY_FILE, PH_HANDLE_GENERAL_INDEX_FILESIZE, L"Size");
PhAddHandleListViewItem(Context->ListViewClass, PH_HANDLE_GENERAL_CATEGORY_FILE, PH_HANDLE_GENERAL_INDEX_FILEPRIORITY, L"Priority");
if (KsiLevel() >= KphLevelMed)
{
PhAddHandleListViewItem(Context->ListViewClass, PH_HANDLE_GENERAL_CATEGORY_FILE, PH_HANDLE_GENERAL_INDEX_FILEDRIVER, L"Driver");
PhAddHandleListViewItem(Context->ListViewClass, PH_HANDLE_GENERAL_CATEGORY_FILE, PH_HANDLE_GENERAL_INDEX_FILEDRIVERIMAGE, L"Driver Image");
}
}
else if (PhEqualStringRef2(&Context->HandleItem->TypeName->sr, L"Section", TRUE))
{
PhListView_AddGroup(Context->ListViewClass, PH_HANDLE_GENERAL_CATEGORY_SECTION, L"Section information");
PhAddHandleListViewItem(Context->ListViewClass, PH_HANDLE_GENERAL_CATEGORY_SECTION, PH_HANDLE_GENERAL_INDEX_SECTIONTYPE, L"Type");
PhAddHandleListViewItem(Context->ListViewClass, PH_HANDLE_GENERAL_CATEGORY_SECTION, PH_HANDLE_GENERAL_INDEX_SECTIONFILE, L"File");
PhAddHandleListViewItem(Context->ListViewClass, PH_HANDLE_GENERAL_CATEGORY_SECTION, PH_HANDLE_GENERAL_INDEX_SECTIONSIZE, L"Size");
}
else if (PhEqualStringRef2(&Context->HandleItem->TypeName->sr, L"Mutant", TRUE))
{
PhListView_AddGroup(Context->ListViewClass, PH_HANDLE_GENERAL_CATEGORY_MUTANT, L"Mutant information");
PhAddHandleListViewItem(Context->ListViewClass, PH_HANDLE_GENERAL_CATEGORY_MUTANT, PH_HANDLE_GENERAL_INDEX_MUTANTCOUNT, L"Count");
PhAddHandleListViewItem(Context->ListViewClass, PH_HANDLE_GENERAL_CATEGORY_MUTANT, PH_HANDLE_GENERAL_INDEX_MUTANTABANDONED, L"Abandoned");
PhAddHandleListViewItem(Context->ListViewClass, PH_HANDLE_GENERAL_CATEGORY_MUTANT, PH_HANDLE_GENERAL_INDEX_MUTANTOWNER, L"Owner");
}
else if (PhEqualStringRef2(&Context->HandleItem->TypeName->sr, L"Process", TRUE))
{
PhListView_AddGroup(Context->ListViewClass, PH_HANDLE_GENERAL_CATEGORY_PROCESSTHREAD, L"Process information");
PhAddHandleListViewItem(Context->ListViewClass, PH_HANDLE_GENERAL_CATEGORY_PROCESSTHREAD, PH_HANDLE_GENERAL_INDEX_PROCESSTHREADNAME, L"Name");
PhAddHandleListViewItem(Context->ListViewClass, PH_HANDLE_GENERAL_CATEGORY_PROCESSTHREAD, PH_HANDLE_GENERAL_INDEX_PROCESSTHREADCREATETIME, L"Created");
PhAddHandleListViewItem(Context->ListViewClass, PH_HANDLE_GENERAL_CATEGORY_PROCESSTHREAD, PH_HANDLE_GENERAL_INDEX_PROCESSTHREADEXITTIME, L"Exited");
PhAddHandleListViewItem(Context->ListViewClass, PH_HANDLE_GENERAL_CATEGORY_PROCESSTHREAD, PH_HANDLE_GENERAL_INDEX_PROCESSTHREADEXITCODE, L"Exit status");
}
else if (PhEqualStringRef2(&Context->HandleItem->TypeName->sr, L"Thread", TRUE))
{
PhListView_AddGroup(Context->ListViewClass, PH_HANDLE_GENERAL_CATEGORY_PROCESSTHREAD, L"Thread information");
PhAddHandleListViewItem(Context->ListViewClass, PH_HANDLE_GENERAL_CATEGORY_PROCESSTHREAD, PH_HANDLE_GENERAL_INDEX_PROCESSTHREADNAME, L"Name");
PhAddHandleListViewItem(Context->ListViewClass, PH_HANDLE_GENERAL_CATEGORY_PROCESSTHREAD, PH_HANDLE_GENERAL_INDEX_PROCESSTHREADCREATETIME, L"Created");
PhAddHandleListViewItem(Context->ListViewClass, PH_HANDLE_GENERAL_CATEGORY_PROCESSTHREAD, PH_HANDLE_GENERAL_INDEX_PROCESSTHREADEXITTIME, L"Exited");
PhAddHandleListViewItem(Context->ListViewClass, PH_HANDLE_GENERAL_CATEGORY_PROCESSTHREAD, PH_HANDLE_GENERAL_INDEX_PROCESSTHREADEXITCODE, L"Exit status");
}
else if (PhEqualStringRef2(&Context->HandleItem->TypeName->sr, L"SymbolicLink", TRUE))
{
PhListView_AddGroup(Context->ListViewClass, PH_HANDLE_GENERAL_CATEGORY_SYMBOLICLINK, L"Symbolic Link information");
PhAddHandleListViewItem(Context->ListViewClass, PH_HANDLE_GENERAL_CATEGORY_SYMBOLICLINK, PH_HANDLE_GENERAL_INDEX_SYMBOLICLINKLINK, L"Link target");
}
}
VOID PhpUpdateHandleGeneral(
_In_ PHANDLE_PROPERTIES_CONTEXT Context
)
{
HANDLE processHandle;
// Name, FullName
if (PhpIsVerboseBestObjectName(Context->HandleItem) && !PhIsNullOrEmptyString(Context->HandleItem->ObjectName))
{
PPH_STRING objectName;
objectName = PhGetBaseName(Context->HandleItem->ObjectName);
PhSetHandleListViewItem(Context, PH_HANDLE_GENERAL_INDEX_NAME, 1, PhGetStringOrEmpty(objectName));
PhClearReference(&objectName);
objectName = PhReferenceObject(Context->HandleItem->ObjectName);
PhSetHandleListViewItem(Context, PH_HANDLE_GENERAL_INDEX_FULLNAME, 1, PhGetStringOrEmpty(objectName));
PhClearReference(&objectName);
}
else if (!PhIsNullOrEmptyString(Context->HandleItem->BestObjectName))
{
PPH_STRING objectName;
objectName = PhGetBaseName(Context->HandleItem->BestObjectName);
PhSetHandleListViewItem(Context, PH_HANDLE_GENERAL_INDEX_NAME, 1, PhGetStringOrEmpty(objectName));
PhClearReference(&objectName);
objectName = PhReferenceObject(Context->HandleItem->BestObjectName);
PhSetHandleListViewItem(Context, PH_HANDLE_GENERAL_INDEX_FULLNAME, 1, PhGetStringOrEmpty(objectName));
PhClearReference(&objectName);
}
// Type
PhSetHandleListViewItem(Context, PH_HANDLE_GENERAL_INDEX_TYPE, 1, PhGetStringOrEmpty(Context->HandleItem->TypeName));
// Address
if (Context->HandleItem->Object)
{
WCHAR string[PH_INT64_STR_LEN_1];
PhPrintPointer(string, Context->HandleItem->Object);
PhSetHandleListViewItem(Context, PH_HANDLE_GENERAL_INDEX_OBJECT, 1, string);
}
else
{
if (PhGetIntegerSetting(SETTING_ENABLE_HANDLE_SNAPSHOT))
PhSetHandleListViewItem(Context, PH_HANDLE_GENERAL_INDEX_OBJECT, 1, L"N/A (snapshot)");
else
PhSetHandleListViewItem(Context, PH_HANDLE_GENERAL_INDEX_OBJECT, 1, L"N/A");
}
// AccessMask (Symbolic)
{
PPH_ACCESS_ENTRY accessEntries;
ULONG numberOfAccessEntries;
if (PhGetAccessEntries(
PhGetStringOrEmpty(Context->HandleItem->TypeName),
&accessEntries,
&numberOfAccessEntries
))
{
PPH_STRING accessString;
if (accessString = PH_AUTO(PhGetAccessString(
Context->HandleItem->GrantedAccess,
accessEntries,
numberOfAccessEntries
)))
{
PhSetHandleListViewItem(Context, PH_HANDLE_GENERAL_INDEX_ACCESSS, 1, PhGetString(accessString));
}
else
{
PhSetHandleListViewItem(Context, PH_HANDLE_GENERAL_INDEX_ACCESSS, 1, L"N/A");
}
PhFree(accessEntries);
}
else
{
PhSetHandleListViewItem(Context, PH_HANDLE_GENERAL_INDEX_ACCESSS, 1, L"N/A");
}
}
// AccessMask (Generic)
{
PPH_STRING genericString = NULL;
GENERIC_MAPPING genericMapping;
if (Context->HandleItem->TypeName && NT_SUCCESS(PhGetObjectTypeMask(
&Context->HandleItem->TypeName->sr,
&genericMapping
)))
{
PH_STRING_BUILDER stringBuilder;
PhInitializeStringBuilder(&stringBuilder, 64);
if (FlagOn(Context->HandleItem->GrantedAccess, genericMapping.GenericRead))
PhAppendStringBuilder2(&stringBuilder, L"Read, ");
if (FlagOn(Context->HandleItem->GrantedAccess, genericMapping.GenericWrite))
PhAppendStringBuilder2(&stringBuilder, L"Write, ");
if (FlagOn(Context->HandleItem->GrantedAccess, genericMapping.GenericExecute))
PhAppendStringBuilder2(&stringBuilder, L"Execute, ");
if (FlagOn(Context->HandleItem->GrantedAccess, genericMapping.GenericAll))
PhAppendStringBuilder2(&stringBuilder, L"All, ");
if (PhEndsWithStringRef2(&stringBuilder.String->sr, L", ", FALSE))
PhRemoveEndStringBuilder(&stringBuilder, 2);
genericString = PH_AUTO(PhFinalStringBuilderString(&stringBuilder));
}
if (!PhIsNullOrEmptyString(genericString))
PhSetHandleListViewItem(Context, PH_HANDLE_GENERAL_INDEX_ACCESSGENERIC, 1, PhGetString(genericString));
else
PhSetHandleListViewItem(Context, PH_HANDLE_GENERAL_INDEX_ACCESSGENERIC, 1, L"N/A");
}
// AccessMask (Hex)
{
WCHAR string[PH_INT64_STR_LEN_1];
PhPrintPointer(string, UlongToPtr(Context->HandleItem->GrantedAccess));
PhSetHandleListViewItem(Context, PH_HANDLE_GENERAL_INDEX_ACCESSMASK, 1, string);
}
// AccessMask (SDDL)
{
NTSTATUS status;
PPH_STRING handleSddlString;
status = PhGetObjectSecurityDescriptorAsString(
Context->HandleItem->Handle,
&handleSddlString
);
if (NT_SUCCESS(status))
{
PhSetHandleListViewItem(Context, PH_HANDLE_GENERAL_INDEX_SDDL, 1, PhGetString(handleSddlString));
PhDereferenceObject(handleSddlString);
}
else
{
WCHAR string[PH_INT64_STR_LEN_1];
PhPrintPointer(string, UlongToPtr(status));
PhSetHandleListViewItem(Context, PH_HANDLE_GENERAL_INDEX_SDDL, 1, string);
}
}
if (NT_SUCCESS(PhOpenProcess(
&processHandle,
(KsiLevel() >= KphLevelMed ? PROCESS_QUERY_LIMITED_INFORMATION : PROCESS_DUP_HANDLE),
Context->ProcessId
)))
{
OBJECT_BASIC_INFORMATION basicInfo;
if (NT_SUCCESS(PhGetHandleInformation(
processHandle,
Context->HandleItem->Handle,
ULONG_MAX,
&basicInfo,
NULL,
NULL,
NULL
)))
{
WCHAR string[PH_INT64_STR_LEN_1];
PhPrintUInt32(string, basicInfo.PointerCount);
PhSetHandleListViewItem(Context, PH_HANDLE_GENERAL_INDEX_REFERENCES, 1, string);
PhPrintUInt32(string, basicInfo.HandleCount);
PhSetHandleListViewItem(Context, PH_HANDLE_GENERAL_INDEX_HANDLES, 1, string);
PhPrintUInt32(string, basicInfo.PagedPoolCharge);
PhSetHandleListViewItem(Context, PH_HANDLE_GENERAL_INDEX_PAGED, 1, string);
PhPrintUInt32(string, basicInfo.NonPagedPoolCharge);
PhSetHandleListViewItem(Context, PH_HANDLE_GENERAL_INDEX_NONPAGED, 1, string);
}
NtClose(processHandle);
}
if (PhIsNullOrEmptyString(Context->HandleItem->TypeName))
{
NOTHING;
}
else if (PhEqualString2(Context->HandleItem->TypeName, L"ALPC Port", TRUE))
{
NTSTATUS status;
if (KsiLevel() >= KphLevelMed && NT_SUCCESS(PhOpenProcess(
&processHandle,
PROCESS_QUERY_LIMITED_INFORMATION,
Context->ProcessId
)))
{
//
// TODO this path doesn't use all the ALPC info returned yet
// see: KPH_ALPC_BASIC_INFORMATION.State
//
KPH_ALPC_BASIC_INFORMATION basicInfo;
PKPH_ALPC_COMMUNICATION_NAMES_INFORMATION connectionNames;
KPH_ALPC_COMMUNICATION_INFORMATION connectionInfo;
if (NT_SUCCESS(KphAlpcQueryInformation(
processHandle,
Context->HandleItem->Handle,
KphAlpcBasicInformation,
&basicInfo,
sizeof(basicInfo),
NULL
)))
{
PH_FORMAT format[5];
PPH_STRING alpcFlagsString;
WCHAR string[PH_INT64_STR_LEN_1];
alpcFlagsString = PhGetAccessString(
basicInfo.Flags,
(PPH_ACCESS_ENTRY)AlpcFlags,
RTL_NUMBER_OF(AlpcFlags)
);
PhInitFormatS(&format[0], L"0x");
PhInitFormatX(&format[1], basicInfo.Flags);
PhInitFormatS(&format[2], L" (");
PhInitFormatSR(&format[3], alpcFlagsString->sr);
PhInitFormatS(&format[4], L")");
PhMoveReference(&alpcFlagsString, PhFormat(format, RTL_NUMBER_OF(format), 40));
PhSetHandleListViewItem(Context, PH_HANDLE_GENERAL_INDEX_FLAGS, 1, PhGetString(alpcFlagsString));
PhDereferenceObject(alpcFlagsString);
PhPrintUInt32(string, basicInfo.SequenceNo);
PhSetHandleListViewItem(Context, PH_HANDLE_GENERAL_INDEX_SEQUENCENUMBER, 1, string);
PhPrintPointer(string, basicInfo.PortContext);
PhSetHandleListViewItem(Context, PH_HANDLE_GENERAL_INDEX_PORTCONTEXT, 1, string);
}
if (!NT_SUCCESS(KphAlpcQueryCommunicationsNamesInfo(
processHandle,
Context->HandleItem->Handle,
&connectionNames)))
{
connectionNames = NULL;
}
if (NT_SUCCESS(KphAlpcQueryInformation(
processHandle,
Context->HandleItem->Handle,
KphAlpcCommunicationInformation,
&connectionInfo,
sizeof(connectionInfo),
NULL
)))
{
CLIENT_ID clientId;
PPH_STRING name;
if (connectionInfo.ConnectionPort.OwnerProcessId)
{
clientId.UniqueProcess = connectionInfo.ConnectionPort.OwnerProcessId;
clientId.UniqueThread = 0;
name = PhStdGetClientIdName(&clientId);
if (connectionNames && connectionNames->ConnectionPort.Length > 0)
{
PPH_STRING newName;
PH_FORMAT format[3];
PhInitFormatSR(&format[0], name->sr);
PhInitFormatS(&format[1], L" - ");
PhInitFormatUCS(&format[2], &connectionNames->ConnectionPort);
newName = PhFormat(format, 3, MAX_PATH);
PhDereferenceObject(name);
name = newName;
}
PhSetHandleListViewItem(Context, PH_HANDLE_GENERAL_INDEX_ALPCCONNECTION, 1, name->Buffer);
PhDereferenceObject(name);
}
if (connectionInfo.ServerCommunicationPort.OwnerProcessId)
{
clientId.UniqueProcess = connectionInfo.ServerCommunicationPort.OwnerProcessId;
clientId.UniqueThread = 0;
name = PhStdGetClientIdName(&clientId);
if (connectionNames && connectionNames->ServerCommunicationPort.Length > 0)
{
PPH_STRING newName;
PH_FORMAT format[3];
PhInitFormatSR(&format[0], name->sr);
PhInitFormatS(&format[1], L" - ");
PhInitFormatUCS(&format[2], &connectionNames->ServerCommunicationPort);
newName = PhFormat(format, 3, MAX_PATH);
PhDereferenceObject(name);
name = newName;
}
PhSetHandleListViewItem(Context, PH_HANDLE_GENERAL_INDEX_ALPCSERVER, 1, name->Buffer);
PhDereferenceObject(name);
}
if (connectionInfo.ClientCommunicationPort.OwnerProcessId)
{
clientId.UniqueProcess = connectionInfo.ClientCommunicationPort.OwnerProcessId;
clientId.UniqueThread = 0;
name = PhStdGetClientIdName(&clientId);
if (connectionNames && connectionNames->ClientCommunicationPort.Length > 0)
{
PPH_STRING newName;
PH_FORMAT format[3];
PhInitFormatSR(&format[0], name->sr);
PhInitFormatS(&format[1], L" - ");
PhInitFormatUCS(&format[2], &connectionNames->ClientCommunicationPort);
newName = PhFormat(format, 3, MAX_PATH);
PhDereferenceObject(name);
name = newName;
}
PhSetHandleListViewItem(Context, PH_HANDLE_GENERAL_INDEX_ALPCCLIENT, 1, name->Buffer);
PhDereferenceObject(name);
}
if (connectionNames)
PhFree(connectionNames);
NtClose(processHandle);
}
else
{
HANDLE alpcPortHandle = NULL;
if (NT_SUCCESS(status = PhOpenProcess(
&processHandle,
PROCESS_DUP_HANDLE,
Context->ProcessId
)))
{
status = NtDuplicateObject(
processHandle,
Context->HandleItem->Handle,
NtCurrentProcess(),
&alpcPortHandle,
READ_CONTROL,
0,
0
);
NtClose(processHandle);
}
if (NT_SUCCESS(status) && alpcPortHandle)
{
ALPC_BASIC_INFORMATION alpcBasicInfo;
if (NT_SUCCESS(NtAlpcQueryInformation(
alpcPortHandle,
AlpcBasicInformation,
&alpcBasicInfo,
sizeof(ALPC_BASIC_INFORMATION),
NULL
)))
{
PH_FORMAT format[5];
PPH_STRING alpcFlagsString;
WCHAR string[PH_INT64_STR_LEN_1];
alpcFlagsString = PhGetAccessString(
alpcBasicInfo.Flags,
(PPH_ACCESS_ENTRY)AlpcFlags,
RTL_NUMBER_OF(AlpcFlags)
);
PhInitFormatS(&format[0], L"0x");
PhInitFormatX(&format[1], alpcBasicInfo.Flags);
PhInitFormatS(&format[2], L" (");
PhInitFormatSR(&format[3], alpcFlagsString->sr);
PhInitFormatS(&format[4], L")");
PhMoveReference(&alpcFlagsString, PhFormat(format, RTL_NUMBER_OF(format), 40));
PhSetHandleListViewItem(Context, PH_HANDLE_GENERAL_INDEX_FLAGS, 1, PhGetString(alpcFlagsString));
PhDereferenceObject(alpcFlagsString);
PhPrintUInt32(string, basicInfo.SequenceNo);
PhSetHandleListViewItem(Context, PH_HANDLE_GENERAL_INDEX_SEQUENCENUMBER, 1, string);
PhPrintPointer(string, basicInfo.PortContext);
PhSetHandleListViewItem(Context, PH_HANDLE_GENERAL_INDEX_PORTCONTEXT, 1, string);
}
if (WindowsVersion >= WINDOWS_10_19H2)
{
ALPC_SERVER_SESSION_INFORMATION serverInfo;
if (NT_SUCCESS(NtAlpcQueryInformation(
alpcPortHandle,
AlpcServerSessionInformation,
&serverInfo,
sizeof(ALPC_SERVER_SESSION_INFORMATION),
NULL
)))
{
CLIENT_ID clientId;
PPH_STRING name;
clientId.UniqueProcess = UlongToHandle(serverInfo.ProcessId);
clientId.UniqueThread = NULL;
name = PhGetClientIdName(&clientId);
PhSetHandleListViewItem(Context, PH_HANDLE_GENERAL_INDEX_ALPCOWNER, 1, name->Buffer);
PhDereferenceObject(name);
}
}
NtClose(alpcPortHandle);
}
}
}
}
else if (PhEqualString2(Context->HandleItem->TypeName, L"EtwRegistration", TRUE))
{
PhSetHandleListViewItem(Context, PH_HANDLE_GENERAL_INDEX_ETWORIGINALNAME, 1, PhGetString(Context->HandleItem->ObjectName));
}
else if (PhEqualString2(Context->HandleItem->TypeName, L"File", TRUE))
{
NTSTATUS status;
if (KsiLevel() >= KphLevelMed && NT_SUCCESS(PhOpenProcess(
&processHandle,
PROCESS_QUERY_LIMITED_INFORMATION,
Context->ProcessId
)))
{
BOOLEAN isFileOrDirectory = FALSE;
BOOLEAN isConsoleHandle = FALSE;
FILE_FS_DEVICE_INFORMATION fileDeviceInfo;
FILE_MODE_INFORMATION fileModeInfo;
FILE_STANDARD_INFORMATION fileStandardInfo;
FILE_POSITION_INFORMATION filePositionInfo;
FILE_IO_PRIORITY_HINT_INFORMATION_EX priorityInfo;
IO_STATUS_BLOCK isb;
HANDLE fileObjectDriver;
if (NT_SUCCESS(KphQueryVolumeInformationFile(
processHandle,
Context->HandleItem->Handle,
FileFsDeviceInformation,
&fileDeviceInfo,
sizeof(FILE_FS_DEVICE_INFORMATION),
&isb
)))
{
switch (fileDeviceInfo.DeviceType)
{
case FILE_DEVICE_NAMED_PIPE:
//isPipeHandle = TRUE;
PhSetHandleListViewItem(Context, PH_HANDLE_GENERAL_INDEX_FILETYPE, 1, L"Pipe");
break;
case FILE_DEVICE_NETWORK:
//isNetworkHandle = TRUE;
PhSetHandleListViewItem(Context, PH_HANDLE_GENERAL_INDEX_FILETYPE, 1, L"Network");
break;
case FILE_DEVICE_CD_ROM:
case FILE_DEVICE_CD_ROM_FILE_SYSTEM:
case FILE_DEVICE_CONTROLLER:
case FILE_DEVICE_DATALINK:
case FILE_DEVICE_DFS:
case FILE_DEVICE_DISK:
case FILE_DEVICE_DISK_FILE_SYSTEM:
case FILE_DEVICE_VIRTUAL_DISK:
isFileOrDirectory = TRUE;
PhSetHandleListViewItem(Context, PH_HANDLE_GENERAL_INDEX_FILETYPE, 1, L"File or directory");
break;
case FILE_DEVICE_CONSOLE:
isConsoleHandle = TRUE;
PhSetHandleListViewItem(Context, PH_HANDLE_GENERAL_INDEX_FILETYPE, 1, L"Console");
break;
default:
PhSetHandleListViewItem(Context, PH_HANDLE_GENERAL_INDEX_FILETYPE, 1, L"Other");
break;
}
}
// Note: These devices deadlock without a timeout (dmex)
// 1) Named pipes
// 2) \Device\ConDrv\CurrentIn
// 3) \Device\VolMgrControl
if (NT_SUCCESS(status = PhCallKphQueryFileInformationWithTimeout(
processHandle,
Context->HandleItem->Handle,
FileModeInformation,
&fileModeInfo,
sizeof(FILE_MODE_INFORMATION)
)))
{
PH_FORMAT format[5];
PPH_STRING fileModeAccessStr;
WCHAR fileModeString[MAX_PATH];
// Since FILE_MODE_INFORMATION has no flag for asynchronous I/O we should use our own flag and set
// it only if none of synchronous flags are present. That's why we need PhFileModeUpdAsyncFlag.
fileModeAccessStr = PhGetAccessString(
PhFileModeUpdAsyncFlag(fileModeInfo.Mode),
(PPH_ACCESS_ENTRY)FileModeAccessEntries,
RTL_NUMBER_OF(FileModeAccessEntries)
);
PhInitFormatS(&format[0], L"0x");
PhInitFormatX(&format[1], fileModeInfo.Mode);
PhInitFormatS(&format[2], L" (");
PhInitFormatSR(&format[3], fileModeAccessStr->sr);
PhInitFormatS(&format[4], L")");
if (PhFormatToBuffer(format, RTL_NUMBER_OF(format), fileModeString, sizeof(fileModeString), NULL))
{
PhSetHandleListViewItem(Context, PH_HANDLE_GENERAL_INDEX_FILEMODE, 1, fileModeString);
}
PhDereferenceObject(fileModeAccessStr);
}
if (!isConsoleHandle)
{
if (NT_SUCCESS(status = PhCallKphQueryFileInformationWithTimeout(
processHandle,
Context->HandleItem->Handle,
FileStandardInformation,
&fileStandardInfo,
sizeof(FILE_STANDARD_INFORMATION)
)))
{
PH_FORMAT format[1];
WCHAR fileSizeString[PH_INT64_STR_LEN];
PhInitFormatSize(&format[0], fileStandardInfo.EndOfFile.QuadPart);
if (PhFormatToBuffer(format, RTL_NUMBER_OF(format), fileSizeString, sizeof(fileSizeString), NULL))
{
PhSetHandleListViewItem(Context, PH_HANDLE_GENERAL_INDEX_FILESIZE, 1, fileSizeString);
}
if (isFileOrDirectory)
{
PhSetHandleListViewItem(Context, PH_HANDLE_GENERAL_INDEX_FILETYPE, 1, fileStandardInfo.Directory ? L"Directory" : L"File");
}
//disableFlushButton |= fileStandardInfo.Directory;
}
if (NT_SUCCESS(status = PhCallKphQueryFileInformationWithTimeout(
processHandle,
Context->HandleItem->Handle,
FilePositionInformation,
&filePositionInfo,
sizeof(FILE_POSITION_INFORMATION)
)))
{
if (filePositionInfo.CurrentByteOffset.QuadPart != 0 && fileStandardInfo.EndOfFile.QuadPart != 0)
{
PH_FORMAT format[4];
WCHAR filePositionString[PH_INT64_STR_LEN];
PhInitFormatI64UGroupDigits(&format[0], filePositionInfo.CurrentByteOffset.QuadPart);
PhInitFormatS(&format[1], L" (");
PhInitFormatF(&format[2], (FLOAT)filePositionInfo.CurrentByteOffset.QuadPart / fileStandardInfo.EndOfFile.QuadPart * 100.f, 1);
PhInitFormatS(&format[3], L"%)");
if (PhFormatToBuffer(format, RTL_NUMBER_OF(format), filePositionString, sizeof(filePositionString), NULL))
{
PhSetHandleListViewItem(Context, PH_HANDLE_GENERAL_INDEX_FILEPOSITION, 1, filePositionString);
}
}
else if (filePositionInfo.CurrentByteOffset.QuadPart != 0)
{
PH_FORMAT format[1];
WCHAR filePositionString[PH_INT64_STR_LEN];
PhInitFormatI64UGroupDigits(&format[0], filePositionInfo.CurrentByteOffset.QuadPart);
if (PhFormatToBuffer(format, RTL_NUMBER_OF(format), filePositionString, sizeof(filePositionString), NULL))
{
PhSetHandleListViewItem(Context, PH_HANDLE_GENERAL_INDEX_FILEPOSITION, 1, filePositionString);
}
}
}
}
if (NT_SUCCESS(status = PhCallKphQueryFileInformationWithTimeout(
processHandle,
Context->HandleItem->Handle,
FileIoPriorityHintInformation,
&priorityInfo,
sizeof(priorityInfo)
)))
{
switch (priorityInfo.PriorityHint)
{
case IoPriorityVeryLow:
PhSetHandleListViewItem(Context, PH_HANDLE_GENERAL_INDEX_FILEPRIORITY, 1, L"Very Low");
break;
case IoPriorityLow:
PhSetHandleListViewItem(Context, PH_HANDLE_GENERAL_INDEX_FILEPRIORITY, 1, L"Low");
break;
case IoPriorityNormal:
PhSetHandleListViewItem(Context, PH_HANDLE_GENERAL_INDEX_FILEPRIORITY, 1, L"Normal");
break;
case IoPriorityHigh:
PhSetHandleListViewItem(Context, PH_HANDLE_GENERAL_INDEX_FILEPRIORITY, 1, L"High");
break;
case IoPriorityCritical:
PhSetHandleListViewItem(Context, PH_HANDLE_GENERAL_INDEX_FILEPRIORITY, 1, L"Critical");
break;
}
}
if (NT_SUCCESS(KphQueryInformationObject(
processHandle,
Context->HandleItem->Handle,
KphObjectFileObjectDriver,
&fileObjectDriver,
sizeof(HANDLE),
NULL
)))
{
PPH_STRING driverName;
if (NT_SUCCESS(PhGetDriverName(fileObjectDriver, &driverName)))
{
PhSetHandleListViewItem(Context, PH_HANDLE_GENERAL_INDEX_FILEDRIVER, 1, PhGetString(driverName));
PhDereferenceObject(driverName);
}
if (NT_SUCCESS(PhGetDriverImageFileName(fileObjectDriver, &driverName)))
{
PhSetHandleListViewItem(Context, PH_HANDLE_GENERAL_INDEX_FILEDRIVERIMAGE, 1, PhGetString(driverName));
PhDereferenceObject(driverName);
}
NtClose(fileObjectDriver);
}
NtClose(processHandle);
}
else
{
HANDLE fileHandle = NULL;
if (NT_SUCCESS(status = PhOpenProcess(
&processHandle,
PROCESS_DUP_HANDLE,
Context->ProcessId
)))
{
status = NtDuplicateObject(
processHandle,
Context->HandleItem->Handle,
NtCurrentProcess(),
&fileHandle,
FILE_READ_ACCESS | SYNCHRONIZE,
0,
0
);
if (!NT_SUCCESS(status))
{
status = NtDuplicateObject(
processHandle,
Context->HandleItem->Handle,
NtCurrentProcess(),
&fileHandle,
FILE_READ_ATTRIBUTES | SYNCHRONIZE,
0,
0
);
}
//if (!NT_SUCCESS(status))
//{
// status = NtDuplicateObject(
// processHandle,
// Context->HandleItem->Handle,
// NtCurrentProcess(),
// &fileHandle,
// MAXIMUM_ALLOWED | SYNCHRONIZE,
// 0,
// 0
// );
//}
if (!NT_SUCCESS(status))
{
status = NtDuplicateObject(
processHandle,
Context->HandleItem->Handle,
NtCurrentProcess(),
&fileHandle,
0,
0,
DUPLICATE_SAME_ACCESS
);
if (NT_SUCCESS(status))
{
HANDLE newhandle;
if (NT_SUCCESS(PhReOpenFile(
&newhandle,
fileHandle,
FILE_READ_ATTRIBUTES | SYNCHRONIZE,
FILE_SHARE_READ,
0
)))
{
NtClose(fileHandle);
fileHandle = newhandle;
}
}
}
NtClose(processHandle);
}
if (NT_SUCCESS(status) && fileHandle)
{
//BOOLEAN disableFlushButton = FALSE;
BOOLEAN isFileOrDirectory = FALSE;
BOOLEAN isConsoleHandle = FALSE;
//BOOLEAN isPipeHandle = FALSE;
//BOOLEAN isNetworkHandle = FALSE;
FILE_FS_DEVICE_INFORMATION fileDeviceInfo;
FILE_MODE_INFORMATION fileModeInfo;
FILE_STANDARD_INFORMATION fileStandardInfo;
FILE_POSITION_INFORMATION filePositionInfo;
FILE_IO_PRIORITY_HINT_INFORMATION_EX priorityInfo;
IO_STATUS_BLOCK isb;
if (NT_SUCCESS(NtQueryVolumeInformationFile(
fileHandle,
&isb,
&fileDeviceInfo,
sizeof(FILE_FS_DEVICE_INFORMATION),
FileFsDeviceInformation
)))
{
switch (fileDeviceInfo.DeviceType)
{
case FILE_DEVICE_NAMED_PIPE:
//isPipeHandle = TRUE;
PhSetHandleListViewItem(Context, PH_HANDLE_GENERAL_INDEX_FILETYPE, 1, L"Pipe");
break;
case FILE_DEVICE_NETWORK:
//isNetworkHandle = TRUE;
PhSetHandleListViewItem(Context, PH_HANDLE_GENERAL_INDEX_FILETYPE, 1, L"Network");
break;
case FILE_DEVICE_CD_ROM:
case FILE_DEVICE_CD_ROM_FILE_SYSTEM:
case FILE_DEVICE_CONTROLLER:
case FILE_DEVICE_DATALINK:
case FILE_DEVICE_DFS:
case FILE_DEVICE_DISK:
case FILE_DEVICE_DISK_FILE_SYSTEM:
case FILE_DEVICE_VIRTUAL_DISK:
isFileOrDirectory = TRUE;
PhSetHandleListViewItem(Context, PH_HANDLE_GENERAL_INDEX_FILETYPE, 1, L"File or directory");
break;
case FILE_DEVICE_CONSOLE:
isConsoleHandle = TRUE;
PhSetHandleListViewItem(Context, PH_HANDLE_GENERAL_INDEX_FILETYPE, 1, L"Console");
break;
default:
PhSetHandleListViewItem(Context, PH_HANDLE_GENERAL_INDEX_FILETYPE, 1, L"Other");
break;
}
}
// Note: These devices deadlock without a timeout (dmex)
// 1) Named pipes
// 2) \Device\ConDrv\CurrentIn
// 3) \Device\VolMgrControl
if (NT_SUCCESS(status = PhCallNtQueryFileInformationWithTimeout(
fileHandle,
FileModeInformation,
&fileModeInfo,
sizeof(FILE_MODE_INFORMATION)
)))
{
PH_FORMAT format[5];
PPH_STRING fileModeAccessStr;
WCHAR fileModeString[MAX_PATH];
// Since FILE_MODE_INFORMATION has no flag for asynchronous I/O we should use our own flag and set
// it only if none of synchronous flags are present. That's why we need PhFileModeUpdAsyncFlag.
fileModeAccessStr = PhGetAccessString(
PhFileModeUpdAsyncFlag(fileModeInfo.Mode),
(PPH_ACCESS_ENTRY)FileModeAccessEntries,
RTL_NUMBER_OF(FileModeAccessEntries)
);
PhInitFormatS(&format[0], L"0x");
PhInitFormatX(&format[1], fileModeInfo.Mode);
PhInitFormatS(&format[2], L" (");
PhInitFormatSR(&format[3], fileModeAccessStr->sr);
PhInitFormatS(&format[4], L")");
if (PhFormatToBuffer(format, RTL_NUMBER_OF(format), fileModeString, sizeof(fileModeString), NULL))
{
PhSetHandleListViewItem(Context, PH_HANDLE_GENERAL_INDEX_FILEMODE, 1, fileModeString);
}
PhDereferenceObject(fileModeAccessStr);
}
if (!isConsoleHandle)
{
if (NT_SUCCESS(status = PhCallNtQueryFileInformationWithTimeout(
fileHandle,
FileStandardInformation,
&fileStandardInfo,
sizeof(FILE_STANDARD_INFORMATION)
)))
{
PH_FORMAT format[1];
WCHAR fileSizeString[PH_INT64_STR_LEN];
PhInitFormatSize(&format[0], fileStandardInfo.EndOfFile.QuadPart);
if (PhFormatToBuffer(format, RTL_NUMBER_OF(format), fileSizeString, sizeof(fileSizeString), NULL))
{
PhSetHandleListViewItem(Context, PH_HANDLE_GENERAL_INDEX_FILESIZE, 1, fileSizeString);
}
if (isFileOrDirectory)
{
PhSetHandleListViewItem(Context, PH_HANDLE_GENERAL_INDEX_FILETYPE, 1, fileStandardInfo.Directory ? L"Directory" : L"File");
}
//disableFlushButton |= fileStandardInfo.Directory;
}
if (NT_SUCCESS(status = PhCallNtQueryFileInformationWithTimeout(
fileHandle,
FilePositionInformation,
&filePositionInfo,
sizeof(FILE_POSITION_INFORMATION)
)))
{
if (fileStandardInfo.EndOfFile.QuadPart != 0)
{
PH_FORMAT format[4];
WCHAR filePositionString[PH_INT64_STR_LEN];
PhInitFormatI64UGroupDigits(&format[0], filePositionInfo.CurrentByteOffset.QuadPart);
PhInitFormatS(&format[1], L" (");
PhInitFormatF(&format[2], (FLOAT)filePositionInfo.CurrentByteOffset.QuadPart / fileStandardInfo.EndOfFile.QuadPart * 100.f, 1);
PhInitFormatS(&format[3], L"%)");
if (PhFormatToBuffer(format, RTL_NUMBER_OF(format), filePositionString, sizeof(filePositionString), NULL))
{
PhSetHandleListViewItem(Context, PH_HANDLE_GENERAL_INDEX_FILEPOSITION, 1, filePositionString);
}
}
else
{
PH_FORMAT format[1];
WCHAR filePositionString[PH_INT64_STR_LEN];
PhInitFormatI64UGroupDigits(&format[0], filePositionInfo.CurrentByteOffset.QuadPart);
if (PhFormatToBuffer(format, RTL_NUMBER_OF(format), filePositionString, sizeof(filePositionString), NULL))
{
PhSetHandleListViewItem(Context, PH_HANDLE_GENERAL_INDEX_FILEPOSITION, 1, filePositionString);
}
}
}
}
if (NT_SUCCESS(status = PhCallNtQueryFileInformationWithTimeout(
fileHandle,
FileIoPriorityHintInformation,
&priorityInfo,
sizeof(priorityInfo)
)))
{
switch (priorityInfo.PriorityHint)
{
case IoPriorityVeryLow:
PhSetHandleListViewItem(Context, PH_HANDLE_GENERAL_INDEX_FILEPRIORITY, 1, L"Very Low");
break;
case IoPriorityLow:
PhSetHandleListViewItem(Context, PH_HANDLE_GENERAL_INDEX_FILEPRIORITY, 1, L"Low");
break;
case IoPriorityNormal:
PhSetHandleListViewItem(Context, PH_HANDLE_GENERAL_INDEX_FILEPRIORITY, 1, L"Normal");
break;
case IoPriorityHigh:
PhSetHandleListViewItem(Context, PH_HANDLE_GENERAL_INDEX_FILEPRIORITY, 1, L"High");
break;
case IoPriorityCritical:
PhSetHandleListViewItem(Context, PH_HANDLE_GENERAL_INDEX_FILEPRIORITY, 1, L"Critical");
break;
}
}
NtClose(fileHandle);
}
}
}
else if (PhEqualString2(Context->HandleItem->TypeName, L"Section", TRUE))
{
NTSTATUS status;
SECTION_BASIC_INFORMATION basicInfo = { NULL };
PPH_STRING fileName = NULL;
if (KsiLevel() >= KphLevelMed && NT_SUCCESS(PhOpenProcess(
&processHandle,
PROCESS_QUERY_LIMITED_INFORMATION,
Context->ProcessId
)))
{
ULONG bufferSize;
ULONG returnLength;
PUNICODE_STRING buffer;
NTSTATUS status2;
returnLength = 0;
bufferSize = 0x100;
buffer = PhAllocate(bufferSize);
status = KphQueryInformationObject(
processHandle,
Context->HandleItem->Handle,
KphObjectSectionBasicInformation,
&basicInfo,
sizeof(basicInfo),
NULL
);
status2 = KphQueryInformationObject(
processHandle,
Context->HandleItem->Handle,
KphObjectSectionFileName,
buffer,
bufferSize,
&returnLength
);
if (status2 == STATUS_BUFFER_OVERFLOW && returnLength > 0)
{
PhFree(buffer);
bufferSize = returnLength;
buffer = PhAllocate(returnLength);
status2 = KphQueryInformationObject(
processHandle,
Context->HandleItem->Handle,
KphObjectSectionFileName,
buffer,
bufferSize,
&returnLength
);
}
if (NT_SUCCESS(status2))
{
fileName = PhCreateStringFromUnicodeString(buffer);
}
PhFree(buffer);
NtClose(processHandle);
}
else
{
HANDLE sectionHandle = NULL;
if (NT_SUCCESS(status = PhOpenProcess(
&processHandle,
PROCESS_DUP_HANDLE,
Context->ProcessId
)))
{
status = NtDuplicateObject(
processHandle,
Context->HandleItem->Handle,
NtCurrentProcess(),
§ionHandle,
SECTION_QUERY | SECTION_MAP_READ,
0,
0
);
if (!NT_SUCCESS(status))
{
status = NtDuplicateObject(
processHandle,
Context->HandleItem->Handle,
NtCurrentProcess(),
§ionHandle,
SECTION_QUERY,
0,
0
);
}
NtClose(processHandle);
}
if (NT_SUCCESS(status) && sectionHandle)
{
status = PhGetSectionBasicInformation(sectionHandle, &basicInfo);
if (!NT_SUCCESS(PhGetSectionFileName(sectionHandle, &fileName)))
{
fileName = NULL;
}
NtClose(sectionHandle);
}
}
if (NT_SUCCESS(status))
{
PCWSTR sectionType = L"Unknown";
PPH_STRING sectionSize = NULL;
if (FlagOn(basicInfo.AllocationAttributes, SEC_COMMIT))
sectionType = L"Commit";
else if (FlagOn(basicInfo.AllocationAttributes, SEC_FILE))
sectionType = L"File";
else if (FlagOn(basicInfo.AllocationAttributes, SEC_IMAGE))
sectionType = L"Image";
else if (FlagOn(basicInfo.AllocationAttributes, SEC_RESERVE))
sectionType = L"Reserve";
sectionSize = PhaFormatSize(basicInfo.MaximumSize.QuadPart, ULONG_MAX);
if (fileName)
{
PhMoveReference(&fileName, PhGetFileName(fileName));
}
PhSetHandleListViewItem(Context, PH_HANDLE_GENERAL_INDEX_SECTIONFILE, 1, PhGetStringOrDefault(fileName, L"N/A"));
PhSetHandleListViewItem(Context, PH_HANDLE_GENERAL_INDEX_SECTIONTYPE, 1, sectionType);
PhSetHandleListViewItem(Context, PH_HANDLE_GENERAL_INDEX_SECTIONSIZE, 1, PhGetStringOrDefault(sectionSize, L"Unknown"));
}
}
else if (PhEqualString2(Context->HandleItem->TypeName, L"Mutant", TRUE))
{
NTSTATUS status;
HANDLE mutantHandle = NULL;
if (NT_SUCCESS(status = PhOpenProcess(
&processHandle,
PROCESS_DUP_HANDLE,
Context->ProcessId
)))
{
status = NtDuplicateObject(
processHandle,
Context->HandleItem->Handle,
NtCurrentProcess(),
&mutantHandle,
SEMAPHORE_QUERY_STATE,
0,
0
);
NtClose(processHandle);
}
if (NT_SUCCESS(status) && mutantHandle)
{
MUTANT_BASIC_INFORMATION basicInfo;
MUTANT_OWNER_INFORMATION ownerInfo;
if (NT_SUCCESS(PhGetMutantBasicInformation(mutantHandle, &basicInfo)))
{
PhSetHandleListViewItem(Context, PH_HANDLE_GENERAL_INDEX_MUTANTCOUNT, 1, PhaFormatUInt64(basicInfo.CurrentCount, TRUE)->Buffer);
PhSetHandleListViewItem(Context, PH_HANDLE_GENERAL_INDEX_MUTANTABANDONED, 1, basicInfo.AbandonedState ? L"True" : L"False");
}
if (NT_SUCCESS(PhGetMutantOwnerInformation(mutantHandle, &ownerInfo)))
{
PPH_STRING name;
if (ownerInfo.ClientId.UniqueProcess)
{
name = PhGetClientIdName(&ownerInfo.ClientId);
PhSetHandleListViewItem(Context, PH_HANDLE_GENERAL_INDEX_MUTANTOWNER, 1, name->Buffer);
PhDereferenceObject(name);
}
}
NtClose(mutantHandle);
}
}
else if (PhEqualString2(Context->HandleItem->TypeName, L"Process", TRUE))
{
NTSTATUS status;
NTSTATUS exitStatus = STATUS_PENDING;
PPH_STRING fileName = NULL;
PROCESS_BASIC_INFORMATION basicInfo;
KERNEL_USER_TIMES times;
BOOLEAN haveTimes = FALSE;
// TODO(jxy-s): Uncomment following code after next driver release (commented out as workaround for KphObjectProcessImageFileName memory leak) (dmex)
//if (KsiLevel() >= KphLevelMed && NT_SUCCESS(PhOpenProcess(
// &processHandle,
// PROCESS_QUERY_LIMITED_INFORMATION,
// Context->ProcessId
// )))
//{
// ULONG bufferSize;
// ULONG returnLength;
// PUNICODE_STRING buffer;
// NTSTATUS status2;
// returnLength = 0;
// bufferSize = 0x100;
// buffer = PhAllocate(bufferSize);
// if (NT_SUCCESS(KphQueryInformationObject(
// processHandle,
// Context->HandleItem->Handle,
// KphObjectProcessBasicInformation,
// &basicInfo,
// sizeof(basicInfo),
// NULL
// )))
// {
// exitStatus = basicInfo.ExitStatus;
// }
// haveTimes = NT_SUCCESS(KphQueryInformationObject(
// processHandle,
// Context->HandleItem->Handle,
// KphObjectProcessTimes,
// ×,
// sizeof(times),
// NULL
// ));
// status2 = KphQueryInformationObject(
// processHandle,
// Context->HandleItem->Handle,
// KphObjectProcessImageFileName,
// buffer,
// bufferSize,
// &returnLength
// );
// if (status2 == STATUS_BUFFER_TOO_SMALL && returnLength > 0)
// {
// PhFree(buffer);
// bufferSize = returnLength;
// buffer = PhAllocate(returnLength);
// status2 = KphQueryInformationObject(
// processHandle,
// Context->HandleItem->Handle,
// KphObjectProcessImageFileName,
// buffer,
// bufferSize,
// &returnLength
// );
// }
// if (NT_SUCCESS(status2))
// {
// fileName = PhCreateStringFromUnicodeString(buffer);
// PhMoveReference(&fileName, PhGetFileName(fileName));
// }
// NtClose(processHandle);
//}
//else
{
HANDLE dupHandle = NULL;
if (NT_SUCCESS(status = PhOpenProcess(
&processHandle,
PROCESS_DUP_HANDLE,
Context->ProcessId
)))
{
status = NtDuplicateObject(
processHandle,
Context->HandleItem->Handle,
NtCurrentProcess(),
&dupHandle,
PROCESS_QUERY_LIMITED_INFORMATION,
0,
0
);
NtClose(processHandle);
}
if (NT_SUCCESS(status) && dupHandle)
{
if (NT_SUCCESS(PhGetProcessImageFileName(dupHandle, &fileName)))
{
PhMoveReference(&fileName, PhGetFileName(fileName));
}
if (NT_SUCCESS(PhGetProcessBasicInformation(dupHandle, &basicInfo)))
{
exitStatus = basicInfo.ExitStatus;
}
haveTimes = NT_SUCCESS(PhGetProcessTimes(dupHandle, ×));
NtClose(dupHandle);
}
}
if (fileName)
{
PhSetHandleListViewItem(Context, PH_HANDLE_GENERAL_INDEX_PROCESSTHREADNAME, 1, PhGetStringOrEmpty(fileName));
PhDereferenceObject(fileName);
}
if (haveTimes)
{
SYSTEMTIME time;
PhLargeIntegerToLocalSystemTime(&time, ×.CreateTime);
PhSetHandleListViewItem(Context, PH_HANDLE_GENERAL_INDEX_PROCESSTHREADCREATETIME, 1, PhaFormatDateTime(&time)->Buffer);
if (exitStatus != STATUS_PENDING)
{
PhLargeIntegerToLocalSystemTime(&time, ×.ExitTime);
PhSetHandleListViewItem(Context, PH_HANDLE_GENERAL_INDEX_PROCESSTHREADEXITTIME, 1, PhaFormatDateTime(&time)->Buffer);
}
}
if (exitStatus != STATUS_PENDING)
{
PPH_STRING message;
PPH_STRING exitcode;
message = PhGetStatusMessage(exitStatus, 0);
exitcode = PhFormatString(L"0x%x (%s)", exitStatus, PhGetStringOrDefault(message, L"Unknown"));
PhSetHandleListViewItem(Context, PH_HANDLE_GENERAL_INDEX_PROCESSTHREADEXITCODE, 1, PhGetStringOrEmpty(exitcode));
PhClearReference(&exitcode);
PhClearReference(&message);
}
}
else if (PhEqualString2(Context->HandleItem->TypeName, L"Thread", TRUE))
{
NTSTATUS status;
BOOLEAN isTerminated = FALSE;
PPH_STRING name = NULL;
KERNEL_USER_TIMES times;
NTSTATUS exitStatus = STATUS_PENDING;
if (KsiLevel() >= KphLevelMed && NT_SUCCESS(PhOpenProcess(
&processHandle,
PROCESS_QUERY_LIMITED_INFORMATION,
Context->ProcessId
)))
{
PPH_STRING threadName;
ULONG threadIsTerminated;
THREAD_BASIC_INFORMATION basicInfo;
if (NT_SUCCESS(KphQueryObjectThreadName(
processHandle,
Context->HandleItem->Handle,
&threadName
)))
{
name = threadName;
}
if (NT_SUCCESS(KphQueryInformationObject(
processHandle,
Context->HandleItem->Handle,
KphObjectThreadIsTerminated,
&threadIsTerminated,
sizeof(threadIsTerminated),
NULL
)))
{
isTerminated = !!threadIsTerminated;
}
if (isTerminated && NT_SUCCESS(KphQueryInformationObject(
processHandle,
Context->HandleItem->Handle,
KphObjectThreadBasicInformation,
&basicInfo,
sizeof(basicInfo),
NULL
)))
{
exitStatus = basicInfo.ExitStatus;
}
status = KphQueryInformationObject(
processHandle,
Context->HandleItem->Handle,
KphObjectThreadTimes,
×,
sizeof(times),
NULL
);
NtClose(processHandle);
}
else
{
HANDLE dupHandle = NULL;
PPH_STRING threadName;
THREAD_BASIC_INFORMATION basicInfo;
if (NT_SUCCESS(status = PhOpenProcess(
&processHandle,
PROCESS_DUP_HANDLE,
Context->ProcessId
)))
{
status = NtDuplicateObject(
processHandle,
Context->HandleItem->Handle,
NtCurrentProcess(),
&dupHandle,
THREAD_QUERY_LIMITED_INFORMATION,
0,
0
);
NtClose(processHandle);
}
if (NT_SUCCESS(status) && dupHandle)
{
if (NT_SUCCESS(PhGetThreadName(dupHandle, &threadName)))
{
name = threadName;
}
PhGetThreadIsTerminated(dupHandle, &isTerminated);
if (isTerminated && NT_SUCCESS(PhGetThreadBasicInformation(dupHandle, &basicInfo)))
{
exitStatus = basicInfo.ExitStatus;
}
status = PhGetThreadTimes(dupHandle, ×);
NtClose(dupHandle);
}
}
if (name)
{
PhSetHandleListViewItem(Context, PH_HANDLE_GENERAL_INDEX_PROCESSTHREADNAME, 1, PhGetStringOrEmpty(name));
PhDereferenceObject(name);
}
if (isTerminated)
{
PPH_STRING message;
PPH_STRING exitcode;
message = PhGetStatusMessage(exitStatus, 0);
exitcode = PhFormatString(L"0x%x (%s)", exitStatus, PhGetStringOrDefault(message, L"Unknown"));
PhSetHandleListViewItem(Context, PH_HANDLE_GENERAL_INDEX_PROCESSTHREADEXITCODE, 1, PhGetStringOrEmpty(exitcode));
PhClearReference(&exitcode);
PhClearReference(&message);
}
if (NT_SUCCESS(status))
{
SYSTEMTIME time;
PhLargeIntegerToLocalSystemTime(&time, ×.CreateTime);
PhSetHandleListViewItem(Context, PH_HANDLE_GENERAL_INDEX_PROCESSTHREADCREATETIME, 1, PhaFormatDateTime(&time)->Buffer);
if (isTerminated)
{
PhLargeIntegerToLocalSystemTime(&time, ×.ExitTime);
PhSetHandleListViewItem(Context, PH_HANDLE_GENERAL_INDEX_PROCESSTHREADEXITTIME, 1, PhaFormatDateTime(&time)->Buffer);
}
}
}
else if (PhEqualString2(Context->HandleItem->TypeName, L"SymbolicLink", TRUE))
{
PPH_STRING linkTarget;
if (!PhIsNullOrEmptyString(Context->HandleItem->ObjectName) &&
NT_SUCCESS(PhQuerySymbolicLinkObject(&linkTarget, NULL, &Context->HandleItem->ObjectName->sr)))
{
PhSetHandleListViewItem(Context, PH_HANDLE_GENERAL_INDEX_SYMBOLICLINKLINK, 1, PhGetStringOrEmpty(linkTarget));
PhDereferenceObject(linkTarget);
}
}
}
INT_PTR CALLBACK PhpHandleGeneralDlgProc(
_In_ HWND hwndDlg,
_In_ UINT uMsg,
_In_ WPARAM wParam,
_In_ LPARAM lParam
)
{
PHANDLE_PROPERTIES_CONTEXT context;
if (uMsg == WM_INITDIALOG)
{
LPPROPSHEETPAGE propSheetPage = (LPPROPSHEETPAGE)lParam;
context = (PHANDLE_PROPERTIES_CONTEXT)propSheetPage->lParam;
PhSetDialogContext(hwndDlg, context);
}
else
{
context = PhGetDialogContext(hwndDlg);
}
if (!context)
return FALSE;
switch (uMsg)
{
case WM_INITDIALOG:
{
PhSetApplicationWindowIcon(context->ParentWindow);
context->ListViewHandle = GetDlgItem(hwndDlg, IDC_LIST);
context->ParentWindow = GetParent(hwndDlg);
context->ListViewClass = PhListView_Initialize(context->ListViewHandle);
PhSetListViewStyle(context->ListViewHandle, FALSE, TRUE);
PhSetControlTheme(context->ListViewHandle, L"explorer");
PhAddListViewColumn(context->ListViewHandle, 0, 0, 0, LVCFMT_LEFT, 120, L"Name");
PhAddListViewColumn(context->ListViewHandle, 1, 1, 1, LVCFMT_LEFT, 250, L"Value");
PhSetExtendedListView(context->ListViewHandle);
PhInitializeLayoutManager(&context->LayoutManager, hwndDlg);
PhAddLayoutItem(&context->LayoutManager, context->ListViewHandle, NULL, PH_ANCHOR_ALL);
if (PhValidWindowPlacementFromSetting(SETTING_HANDLE_PROPERTIES_WINDOW_POSITION))
PhLoadWindowPlacementFromSetting(SETTING_HANDLE_PROPERTIES_WINDOW_POSITION, NULL, context->ParentWindow);
else
PhCenterWindow(context->ParentWindow, GetParent(context->ParentWindow)); // HACK
PhpUpdateHandleGeneralListViewGroups(context);
PhpUpdateHandleGeneral(context);
PhRegisterWindowCallback(context->ParentWindow, PH_PLUGIN_WINDOW_EVENT_TYPE_TOPMOST, NULL);
if (PhPluginsEnabled)
{
PPH_PLUGIN_HANDLE_PROPERTIES_WINDOW_CONTEXT Context;
Context = (PPH_PLUGIN_HANDLE_PROPERTIES_WINDOW_CONTEXT)context;
PhInvokeCallback(PhGetGeneralCallback(GeneralCallbackHandlePropertiesWindowInitialized), Context);
}
if (PhEnableThemeSupport) // TODO: Required for compat (dmex)
PhInitializeWindowTheme(context->ParentWindow, PhEnableThemeSupport);
else
PhInitializeWindowTheme(hwndDlg, FALSE);
}
break;
case WM_DESTROY:
{
PhUnregisterWindowCallback(context->ParentWindow);
PhSaveWindowPlacementToSetting(SETTING_HANDLE_PROPERTIES_WINDOW_POSITION, NULL, context->ParentWindow);
PhDeleteLayoutManager(&context->LayoutManager);
PhListView_Destroy(context->ListViewClass);
PhRemoveDialogContext(hwndDlg);
}
break;
case WM_DPICHANGED:
{
PhLayoutManagerUpdate(&context->LayoutManager, LOWORD(wParam));
PhLayoutManagerLayout(&context->LayoutManager);
}
break;
case WM_SIZE:
{
PhLayoutManagerLayout(&context->LayoutManager);
ExtendedListView_SetColumnWidth(context->ListViewHandle, 0, ELVSCW_AUTOSIZE_REMAININGSPACE);
}
break;
case WM_NOTIFY:
{
LPNMHDR header = (LPNMHDR)lParam;
PhHandleListViewNotifyBehaviors(lParam, context->ListViewHandle, PH_LIST_VIEW_DEFAULT_1_BEHAVIORS);
switch (header->code)
{
case PSN_QUERYINITIALFOCUS:
SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, (LONG_PTR)context->ListViewHandle);
return TRUE;
}
REFLECT_MESSAGE_DLG(hwndDlg, context->ListViewHandle, uMsg, wParam, lParam);
}
break;
case WM_CONTEXTMENU:
{
if ((HWND)wParam == context->ListViewHandle)
{
POINT point;
PPH_EMENU menu;
PPH_EMENU item;
PVOID *listviewItems;
ULONG numberOfItems;
point.x = GET_X_LPARAM(lParam);
point.y = GET_Y_LPARAM(lParam);
if (point.x == -1 && point.y == -1)
PhGetListViewContextMenuPoint(context->ListViewHandle, &point);
PhGetSelectedListViewItemParams(context->ListViewHandle, &listviewItems, &numberOfItems);
if (numberOfItems != 0)
{
menu = PhCreateEMenu();
PhInsertEMenuItem(menu, PhCreateEMenuItem(0, IDC_COPY, L"&Copy", NULL, NULL), ULONG_MAX);
PhInsertCopyListViewEMenuItem(menu, IDC_COPY, context->ListViewHandle);
item = PhShowEMenu(
menu,
hwndDlg,
PH_EMENU_SHOW_SEND_COMMAND | PH_EMENU_SHOW_LEFTRIGHT,
PH_ALIGN_LEFT | PH_ALIGN_TOP,
point.x,
point.y
);
if (item)
{
BOOLEAN handled = FALSE;
handled = PhHandleCopyListViewEMenuItem(item);
//if (!handled && PhPluginsEnabled)
// handled = PhPluginTriggerEMenuItem(&menuInfo, item);
if (!handled)
{
switch (item->Id)
{
case IDC_COPY:
{
PhCopyListView(context->ListViewHandle);
}
break;
}
}
}
PhDestroyEMenu(menu);
}
PhFree(listviewItems);
}
}
break;
HANDLE_MSG(hwndDlg, WM_CTLCOLORBTN, PhWindowThemeControlColor);
HANDLE_MSG(hwndDlg, WM_CTLCOLORDLG, PhWindowThemeControlColor);
HANDLE_MSG(hwndDlg, WM_CTLCOLORSTATIC, PhWindowThemeControlColor);
}
return FALSE;
}
VOID PhAddStatusPermissionsTrustee(
_In_ PHANDLE_PERMISSIONS_CONTEXT Context,
_In_ HWND ListViewHandle,
_In_ NTSTATUS status,
_In_ ULONG Index
)
{
//LONG index = PhAddListViewItem(ListViewHandle, MAXINT, Message, NULL);
//PhSetListViewSubItem(ListViewHandle, index, 1, L"N/A");
//PhSetListViewSubItem(ListViewHandle, index, 2, L"N/A");
//if (!NT_SUCCESS(status))
{
PPH_STRING string;
string = PhGetNtMessage(status);
PhMoveReference(&string, PhFormatString(L"0x%x: %s",
status,
PhGetStringOrDefault(string, L"N/A")
));
PhSetListViewSubItem(ListViewHandle, 1, 1, PhGetString(string));
//PhSetListViewSubItem(ListViewHandle, 2, 1, PhGetString(string));
//PhSetListViewSubItem(ListViewHandle, 3, 1, PhGetString(string));
PhClearReference(&string);
}
}
VOID PhAddHandlePermissionsTrustee(
_In_ PHANDLE_PERMISSIONS_CONTEXT Context,
_In_ HWND ListViewHandle,
_In_ PSID TrusteeSid,
_In_ ACCESS_MASK TrusteeMask,
_In_ ULONG Index
)
{
LONG index;
PPH_ACCESS_ENTRY accessEntries;
ULONG numberOfAccessEntries;
PPH_STRING string;
SID_NAME_USE sidNameUse;
if (string = PhGetSidFullName(TrusteeSid, FALSE, &sidNameUse))
{
switch (sidNameUse)
{
case SidTypeUser:
case SidTypeLogonSession:
case SidTypeDeletedAccount:
PhMoveReference(&string, PhConcatStringRefZ(&string->sr, L" (User)"));
break;
case SidTypeAlias:
case SidTypeGroup:
case SidTypeWellKnownGroup:
PhMoveReference(&string, PhConcatStringRefZ(&string->sr, L" (Group)"));
break;
case SidTypeDomain:
case SidTypeComputer:
PhMoveReference(&string, PhConcatStringRefZ(&string->sr, L" (Computer)"));
break;
}
}
else if (string = PhGetAppContainerPackageName(TrusteeSid))
{
PhMoveReference(&string, PhConcatStringRefZ(&string->sr, L" (APP_PACKAGE)"));
}
else if (string = PhGetAppContainerName(TrusteeSid))
{
PhMoveReference(&string, PhConcatStringRefZ(&string->sr, L" (APP_CONTAINER)"));
}
else if (string = PhGetCapabilitySidName(TrusteeSid))
{
PhMoveReference(&string, PhConcatStringRefZ(&string->sr, L" (APP_CAPABILITY)"));
}
if (Index)
{
index = Index;
PhSetListViewSubItem(ListViewHandle, Index, 1, PhGetStringOrDefault(string, L"N/A"));
}
else
{
index = PhAddListViewItem(
ListViewHandle,
MAXINT,
PhGetStringOrDefault(string, L"N/A"),
NULL
);
PhSetListViewSubItem(ListViewHandle, index, 1, L"Allow");
}
if (PhGetAccessEntries(
PhGetStringOrEmpty(Context->HandleProperties->HandleItem->TypeName),
&accessEntries,
&numberOfAccessEntries
))
{
PPH_STRING accessString;
if (accessString = PH_AUTO(PhGetAccessString(
TrusteeMask,
accessEntries,
numberOfAccessEntries
)))
{
PhSetListViewSubItem(Context->ListViewHandle, index, 2, PhGetStringOrEmpty(accessString));
}
else
{
PhSetListViewSubItem(Context->ListViewHandle, index, 2, L"N/A");
}
PhFree(accessEntries);
}
}
VOID PhUpdateHandlePermissionsOwnerSecurity(
_In_ PHANDLE_PERMISSIONS_CONTEXT Context,
_In_ HANDLE QueryHandle
)
{
NTSTATUS status;
PSECURITY_DESCRIPTOR currentSecurityDescriptor;
BOOLEAN currentOwnerDefaulted;
PACL currentOwner;
status = PhGetObjectSecurity(
QueryHandle,
OWNER_SECURITY_INFORMATION,
¤tSecurityDescriptor
);
if (NT_SUCCESS(status))
{
status = PhGetOwnerSecurityDescriptor(
currentSecurityDescriptor,
¤tOwner,
¤tOwnerDefaulted
);
if (NT_SUCCESS(status))
{
if (currentOwner)
{
PPH_STRING string;
SID_NAME_USE sidNameUse;
if (string = PhGetSidFullName(currentOwner, FALSE, &sidNameUse))
{
switch (sidNameUse)
{
case SidTypeUser:
case SidTypeLogonSession:
case SidTypeDeletedAccount:
PhMoveReference(&string, PhConcatStringRefZ(&string->sr, L" (User)"));
break;
case SidTypeAlias:
case SidTypeGroup:
case SidTypeWellKnownGroup:
PhMoveReference(&string, PhConcatStringRefZ(&string->sr, L" (Group)"));
break;
case SidTypeDomain:
case SidTypeComputer:
PhMoveReference(&string, PhConcatStringRefZ(&string->sr, L" (Computer)"));
break;
}
}
else if (string = PhGetAppContainerPackageName(currentOwner))
{
PhMoveReference(&string, PhConcatStringRefZ(&string->sr, L" (APP_PACKAGE)"));
}
else if (string = PhGetAppContainerName(currentOwner))
{
PhMoveReference(&string, PhConcatStringRefZ(&string->sr, L" (APP_CONTAINER)"));
}
else if (string = PhGetCapabilitySidName(currentOwner))
{
PhMoveReference(&string, PhConcatStringRefZ(&string->sr, L" (APP_CAPABILITY)"));
}
PhSetListViewSubItem(Context->ListViewHeader, 0, 1, PhGetStringOrDefault(string, L"N/A"));
PhClearReference(&string);
}
else
{
PhSetListViewSubItem(Context->ListViewHeader, 0, 1, L"NULL");
}
}
else
{
PPH_STRING string = PhGetNtMessage(status);;
PhSetListViewSubItem(Context->ListViewHeader, 0, 1, PH_AUTO_T(PH_STRING, PhFormatString(
L"0x%x: %s",
status,
PhGetStringOrEmpty(string)))->Buffer);
PhClearReference(&string);
}
PhFree(currentSecurityDescriptor);
}
else
{
PPH_STRING string = PhGetNtMessage(status);;
PhSetListViewSubItem(Context->ListViewHeader, 0, 1, PH_AUTO_T(PH_STRING, PhFormatString(
L"0x%x: %s",
status,
PhGetStringOrEmpty(string)))->Buffer);
PhClearReference(&string);
}
}
VOID PhUpdateHandlePermissionsGroupSecurity(
_In_ PHANDLE_PERMISSIONS_CONTEXT Context,
_In_ HANDLE QueryHandle
)
{
NTSTATUS status;
PSECURITY_DESCRIPTOR currentSecurityDescriptor;
BOOLEAN currentGroupDefaulted;
PACL currentGroupSid;
status = PhGetObjectSecurity(
QueryHandle,
GROUP_SECURITY_INFORMATION,
¤tSecurityDescriptor
);
if (NT_SUCCESS(status))
{
status = PhGetGroupSecurityDescriptor(
currentSecurityDescriptor,
¤tGroupSid,
¤tGroupDefaulted
);
if (NT_SUCCESS(status))
{
if (currentGroupSid)
{
PPH_STRING string;
SID_NAME_USE sidNameUse;
if (string = PhGetSidFullName(currentGroupSid, FALSE, &sidNameUse))
{
switch (sidNameUse)
{
case SidTypeUser:
case SidTypeLogonSession:
case SidTypeDeletedAccount:
PhMoveReference(&string, PhConcatStringRefZ(&string->sr, L" (User)"));
break;
case SidTypeAlias:
case SidTypeGroup:
case SidTypeWellKnownGroup:
PhMoveReference(&string, PhConcatStringRefZ(&string->sr, L" (Group)"));
break;
case SidTypeDomain:
case SidTypeComputer:
PhMoveReference(&string, PhConcatStringRefZ(&string->sr, L" (Computer)"));
break;
}
}
else if (string = PhGetAppContainerPackageName(currentGroupSid))
{
PhMoveReference(&string, PhConcatStringRefZ(&string->sr, L" (APP_PACKAGE)"));
}
else if (string = PhGetAppContainerName(currentGroupSid))
{
PhMoveReference(&string, PhConcatStringRefZ(&string->sr, L" (APP_CONTAINER)"));
}
else if (string = PhGetCapabilitySidName(currentGroupSid))
{
PhMoveReference(&string, PhConcatStringRefZ(&string->sr, L" (APP_CAPABILITY)"));
}
PhSetListViewSubItem(Context->ListViewHeader, 1, 1, PhGetStringOrDefault(string, L"N/A"));
PhClearReference(&string);
}
else
{
PhSetListViewSubItem(Context->ListViewHeader, 1, 1, L"NULL");
}
}
else
{
PPH_STRING string = PhGetNtMessage(status);;
PhSetListViewSubItem(Context->ListViewHeader, 1, 1, PH_AUTO_T(PH_STRING, PhFormatString(
L"0x%x: %s",
status,
PhGetStringOrEmpty(string)))->Buffer);
PhClearReference(&string);
}
PhFree(currentSecurityDescriptor);
}
else
{
PPH_STRING string = PhGetNtMessage(status);;
PhSetListViewSubItem(Context->ListViewHeader, 1, 1, PH_AUTO_T(PH_STRING, PhFormatString(
L"0x%x: %s",
status,
PhGetStringOrEmpty(string)))->Buffer);
PhClearReference(&string);
}
}
VOID PhUpdateHandlePermissionsSaclSecurity(
_In_ PHANDLE_PERMISSIONS_CONTEXT Context,
_In_ HANDLE QueryHandle
)
{
NTSTATUS status;
PSECURITY_DESCRIPTOR currentSecurityDescriptor;
BOOLEAN currentSaclPresent;
BOOLEAN currentSaclDefaulted;
PACL currentSacl;
status = PhGetObjectSecurity(
QueryHandle,
SACL_SECURITY_INFORMATION,
¤tSecurityDescriptor
);
if (NT_SUCCESS(status))
{
status = PhGetSaclSecurityDescriptor(
currentSecurityDescriptor,
¤tSaclPresent,
¤tSacl,
¤tSaclDefaulted
);
if (NT_SUCCESS(status) && currentSaclPresent && currentSacl)
{
PSYSTEM_MANDATORY_LABEL_ACE currentAce;
for (USHORT i = 0; i < currentSacl->AceCount; i++)
{
status = PhGetAce(currentSacl, i, ¤tAce);
if (NT_SUCCESS(status))
{
switch (currentAce->Header.AceType)
{
case SYSTEM_MANDATORY_LABEL_ACE_TYPE:
{
PSID trusteeSidBuffer = ¤tAce->SidStart;
ULONG trusteeSidlength = PhLengthSid(trusteeSidBuffer);
ULONG opaqueDataLength = currentAce->Header.AceSize - trusteeSidlength - FIELD_OFFSET(SYSTEM_MANDATORY_LABEL_ACE, SidStart);
// The remaining bytes of the SID are stored in contiguous memory after the SidStart member. This SID can be appended with application data.
PhAddHandlePermissionsTrustee(Context, Context->ListViewHandle, trusteeSidBuffer, currentAce->Mask, 0);
}
break;
}
}
else
{
PhAddStatusPermissionsTrustee(Context, Context->ListViewHeader, status, 0);
}
}
}
PhFree(currentSecurityDescriptor);
}
if (!NT_SUCCESS(status))
{
PhAddStatusPermissionsTrustee(Context, Context->ListViewHeader, status, 0);
}
}
VOID PhUpdateHandlePermissionsLabelSecurity(
_In_ PHANDLE_PERMISSIONS_CONTEXT Context,
_In_ HANDLE QueryHandle
)
{
NTSTATUS status;
PSECURITY_DESCRIPTOR currentSecurityDescriptor;
BOOLEAN currentSaclPresent;
BOOLEAN currentSaclDefaulted;
PACL currentSacl;
status = PhGetObjectSecurity(
QueryHandle,
LABEL_SECURITY_INFORMATION,
¤tSecurityDescriptor
);
if (NT_SUCCESS(status))
{
status = PhGetSaclSecurityDescriptor(
currentSecurityDescriptor,
¤tSaclPresent,
¤tSacl,
¤tSaclDefaulted
);
if (NT_SUCCESS(status) && currentSaclPresent && currentSacl)
{
PSYSTEM_MANDATORY_LABEL_ACE currentAce;
for (USHORT i = 0; i < currentSacl->AceCount; i++)
{
status = PhGetAce(currentSacl, i, ¤tAce);
if (NT_SUCCESS(status))
{
switch (currentAce->Header.AceType)
{
case SYSTEM_MANDATORY_LABEL_ACE_TYPE:
{
PSID trusteeSidBuffer = ¤tAce->SidStart;
ULONG trusteeSidlength = PhLengthSid(trusteeSidBuffer);
ULONG opaqueDataLength = currentAce->Header.AceSize - trusteeSidlength - FIELD_OFFSET(SYSTEM_MANDATORY_LABEL_ACE, SidStart);
// The remaining bytes of the SID are stored in contiguous memory after the SidStart member. This SID can be appended with application data.
PhAddHandlePermissionsTrustee(Context, Context->ListViewHeader, trusteeSidBuffer, currentAce->Mask, 2);
}
break;
}
}
else
{
PhAddStatusPermissionsTrustee(Context, Context->ListViewHeader, status, 2);
}
}
}
PhFree(currentSecurityDescriptor);
}
if (!NT_SUCCESS(status))
{
PhAddStatusPermissionsTrustee(Context, Context->ListViewHeader, status, 2);
}
}
VOID PhUpdateHandlePermissionsDaclSecurity(
_In_ PHANDLE_PERMISSIONS_CONTEXT Context,
_In_ HANDLE QueryHandle
)
{
NTSTATUS status;
PSECURITY_DESCRIPTOR currentSecurityDescriptor;
BOOLEAN currentSaclPresent;
BOOLEAN currentSaclDefaulted;
PACL currentSacl;
status = PhGetObjectSecurity(
QueryHandle,
DACL_SECURITY_INFORMATION,
¤tSecurityDescriptor
);
if (NT_SUCCESS(status))
{
status = PhGetDaclSecurityDescriptor(
currentSecurityDescriptor,
¤tSaclPresent,
¤tSacl,
¤tSaclDefaulted
);
if (NT_SUCCESS(status) && currentSaclPresent && currentSacl)
{
PACCESS_ALLOWED_ACE currentAce;
for (USHORT i = 0; i < currentSacl->AceCount; i++)
{
status = PhGetAce(currentSacl, i, ¤tAce);
if (NT_SUCCESS(status))
{
switch (currentAce->Header.AceType)
{
case ACCESS_ALLOWED_ACE_TYPE:
{
PSID trusteeSidBuffer = ¤tAce->SidStart;
ULONG trusteeSidlength = PhLengthSid(trusteeSidBuffer);
ULONG opaqueDataLength = currentAce->Header.AceSize - trusteeSidlength - FIELD_OFFSET(ACCESS_ALLOWED_ACE, SidStart);
// The remaining bytes of the SID are stored in contiguous memory after the SidStart member. This SID can be appended with application data.
PhAddHandlePermissionsTrustee(Context, Context->ListViewHandle, trusteeSidBuffer, currentAce->Mask, 0);
if (FlagOn(currentAce->Header.AceFlags, INHERITED_ACE))
{
// This ACE is inherited
}
if (FlagOn(currentAce->Header.AceFlags, OBJECT_INHERIT_ACE))
{
// This ACE can be inherited by child objects
}
if (FlagOn(currentAce->Header.AceFlags, CONTAINER_INHERIT_ACE))
{
// This ACE can be inherited by container child objects
}
}
break;
}
}
}
}
PhFree(currentSecurityDescriptor);
}
if (!NT_SUCCESS(status))
{
PhAddStatusPermissionsTrustee(Context, Context->ListViewHeader, status, 2);
}
}
VOID PhUpdateHandlePermissionSecurity(
_In_ PHANDLE_PERMISSIONS_CONTEXT Context
)
{
NTSTATUS status;
HANDLE processHandle;
HANDLE dupHandle = NULL;
PhSetListViewStyle(Context->ListViewHeader, FALSE, TRUE);
PhSetControlTheme(Context->ListViewHeader, L"explorer");
PhAddListViewColumn(Context->ListViewHeader, 0, 0, 0, LVCFMT_LEFT, 120, L"Name");
PhAddListViewColumn(Context->ListViewHeader, 1, 1, 1, LVCFMT_LEFT, 250, L"Value");
PhSetExtendedListView(Context->ListViewHeader);
ListView_EnableGroupView(Context->ListViewHeader, TRUE);
PhAddListViewGroup(Context->ListViewHeader, PH_HANDLE_GENERAL_CATEGORY_SECURITY, L"Security information");
PhAddListViewGroupItem(Context->ListViewHeader, PH_HANDLE_GENERAL_CATEGORY_SECURITY, 0, L"Owner", NULL);
PhAddListViewGroupItem(Context->ListViewHeader, PH_HANDLE_GENERAL_CATEGORY_SECURITY, 1, L"Group", NULL);
PhAddListViewGroupItem(Context->ListViewHeader, PH_HANDLE_GENERAL_CATEGORY_SECURITY, 2, L"Integrity", NULL);
PhSetListViewSubItem(Context->ListViewHeader, 0, 1, L"N/A");
PhSetListViewSubItem(Context->ListViewHeader, 1, 1, L"N/A");
PhSetListViewSubItem(Context->ListViewHeader, 2, 1, L"N/A");
if (NT_SUCCESS(status = PhOpenProcess(
&processHandle,
PROCESS_DUP_HANDLE,
Context->HandleProperties->ProcessId
)))
{
if (!NT_SUCCESS(status = NtDuplicateObject(
processHandle,
Context->HandleProperties->HandleItem->Handle,
NtCurrentProcess(),
&dupHandle,
READ_CONTROL | ACCESS_SYSTEM_SECURITY,
0,
0
)))
{
status = NtDuplicateObject(
processHandle,
Context->HandleProperties->HandleItem->Handle,
NtCurrentProcess(),
&dupHandle,
READ_CONTROL,
0,
0
);
}
NtClose(processHandle);
}
if (NT_SUCCESS(status))
{
PhUpdateHandlePermissionsDaclSecurity(Context, dupHandle);
PhUpdateHandlePermissionsOwnerSecurity(Context, dupHandle);
PhUpdateHandlePermissionsGroupSecurity(Context, dupHandle);
PhUpdateHandlePermissionsLabelSecurity(Context, dupHandle);
NtClose(dupHandle);
}
else
{
PPH_STRING string;
if (string = PhGetWin32Message(PhNtStatusToDosError(status)))
{
PhMoveReference(&string, PhFormatString(L"0x%x: %s",
status,
PhGetStringOrDefault(string, L"N/A")
));
}
else
{
string = PhGetNtMessage(status);
PhMoveReference(&string, PhFormatString(L"0x%x: %s",
status,
PhGetStringOrDefault(string, L"N/A")
));
}
PhSetListViewSubItem(Context->ListViewHeader, 0, 1, PhGetString(string));
PhSetListViewSubItem(Context->ListViewHeader, 1, 1, PhGetString(string));
PhSetListViewSubItem(Context->ListViewHeader, 2, 1, PhGetString(string));
PhClearReference(&string);
}
}
VOID PhUpdateHandleAuditingSecurity(
_In_ PHANDLE_PERMISSIONS_CONTEXT Context
)
{
NTSTATUS status;
HANDLE processHandle;
HANDLE dupHandle = NULL;
PhSetListViewStyle(Context->ListViewHeader, FALSE, TRUE);
PhSetControlTheme(Context->ListViewHeader, L"explorer");
PhAddListViewColumn(Context->ListViewHeader, 0, 0, 0, LVCFMT_LEFT, 120, L"Name");
PhAddListViewColumn(Context->ListViewHeader, 1, 1, 1, LVCFMT_LEFT, 250, L"Value");
PhSetExtendedListView(Context->ListViewHeader);
ListView_EnableGroupView(Context->ListViewHeader, TRUE);
PhAddListViewGroup(Context->ListViewHeader, PH_HANDLE_GENERAL_CATEGORY_SECURITY, L"Auditing information");
PhAddListViewGroupItem(Context->ListViewHeader, PH_HANDLE_GENERAL_CATEGORY_SECURITY, 0, L"Owner", NULL);
PhAddListViewGroupItem(Context->ListViewHeader, PH_HANDLE_GENERAL_CATEGORY_SECURITY, 1, L"Group", NULL);
PhAddListViewGroupItem(Context->ListViewHeader, PH_HANDLE_GENERAL_CATEGORY_SECURITY, 2, L"Integrity", NULL);
PhSetListViewSubItem(Context->ListViewHeader, 0, 1, L"N/A");
PhSetListViewSubItem(Context->ListViewHeader, 1, 1, L"N/A");
PhSetListViewSubItem(Context->ListViewHeader, 2, 1, L"N/A");
if (NT_SUCCESS(status = PhOpenProcess(
&processHandle,
PROCESS_DUP_HANDLE,
Context->HandleProperties->ProcessId
)))
{
if (!NT_SUCCESS(status = NtDuplicateObject(
processHandle,
Context->HandleProperties->HandleItem->Handle,
NtCurrentProcess(),
&dupHandle,
READ_CONTROL | ACCESS_SYSTEM_SECURITY,
0,
0
)))
{
status = NtDuplicateObject(
processHandle,
Context->HandleProperties->HandleItem->Handle,
NtCurrentProcess(),
&dupHandle,
READ_CONTROL,
0,
0
);
}
NtClose(processHandle);
}
if (NT_SUCCESS(status))
{
PhUpdateHandlePermissionsOwnerSecurity(Context, dupHandle);
PhUpdateHandlePermissionsGroupSecurity(Context, dupHandle);
PhUpdateHandlePermissionsLabelSecurity(Context, dupHandle);
NtClose(dupHandle);
}
else
{
PPH_STRING string;
if (string = PhGetWin32Message(PhNtStatusToDosError(status)))
{
PhMoveReference(&string, PhFormatString(L"0x%x: %s",
status,
PhGetStringOrDefault(string, L"N/A")
));
}
else
{
string = PhGetNtMessage(status);
PhMoveReference(&string, PhFormatString(L"0x%x: %s",
status,
PhGetStringOrDefault(string, L"N/A")
));
}
PhSetListViewSubItem(Context->ListViewHeader, 0, 1, PhGetString(string));
PhSetListViewSubItem(Context->ListViewHeader, 1, 1, PhGetString(string));
PhSetListViewSubItem(Context->ListViewHeader, 2, 1, PhGetString(string));
PhClearReference(&string);
}
}
INT_PTR CALLBACK PhpHandlePermissionsDlgProc(
_In_ HWND hwndDlg,
_In_ UINT uMsg,
_In_ WPARAM wParam,
_In_ LPARAM lParam
)
{
PHANDLE_PERMISSIONS_CONTEXT context;
if (uMsg == WM_INITDIALOG)
{
LPPROPSHEETPAGE propSheetPage = (LPPROPSHEETPAGE)lParam;
context = PhAllocateZero(sizeof(HANDLE_PERMISSIONS_CONTEXT));
context->HandleProperties = (PHANDLE_PROPERTIES_CONTEXT)propSheetPage->lParam;
PhSetDialogContext(hwndDlg, context);
}
else
{
context = PhGetDialogContext(hwndDlg);
}
if (!context)
return FALSE;
switch (uMsg)
{
case WM_INITDIALOG:
{
PhSetApplicationWindowIcon(context->ParentWindow);
context->ListViewHandle = GetDlgItem(hwndDlg, IDC_LIST);
context->ListViewHeader = GetDlgItem(hwndDlg, IDC_SETTINGS);
context->ParentWindow = GetParent(hwndDlg);
//context->ListViewClass = PhGetListViewInterface(context->ListViewHandle);
PhSetListViewStyle(context->ListViewHandle, FALSE, TRUE);
PhSetControlTheme(context->ListViewHandle, L"explorer");
PhAddListViewColumn(context->ListViewHandle, 0, 0, 0, LVCFMT_LEFT, 140, L"Principal");
PhAddListViewColumn(context->ListViewHandle, 1, 1, 1, LVCFMT_LEFT, 50, L"Type");
PhAddListViewColumn(context->ListViewHandle, 2, 2, 2, LVCFMT_LEFT, 80, L"Access");
//PhAddListViewColumn(context->ListViewHandle, 3, 3, 3, LVCFMT_LEFT, 80, L"Inherited from");
PhSetExtendedListView(context->ListViewHandle);
PhInitializeLayoutManager(&context->LayoutManager, hwndDlg);
PhAddLayoutItem(&context->LayoutManager, context->ListViewHeader, NULL, PH_ANCHOR_LEFT | PH_ANCHOR_TOP | PH_ANCHOR_RIGHT);
PhAddLayoutItem(&context->LayoutManager, context->ListViewHandle, NULL, PH_ANCHOR_ALL);
PhAddLayoutItem(&context->LayoutManager, GetDlgItem(hwndDlg, IDC_ADD), NULL, PH_ANCHOR_LEFT | PH_ANCHOR_BOTTOM);
PhAddLayoutItem(&context->LayoutManager, GetDlgItem(hwndDlg, IDC_REMOVE), NULL, PH_ANCHOR_LEFT | PH_ANCHOR_BOTTOM);
PhAddLayoutItem(&context->LayoutManager, GetDlgItem(hwndDlg, IDC_SHOW), NULL, PH_ANCHOR_LEFT | PH_ANCHOR_BOTTOM);
PhAddLayoutItem(&context->LayoutManager, GetDlgItem(hwndDlg, IDC_ADVANCED), NULL, PH_ANCHOR_LEFT | PH_ANCHOR_BOTTOM);
ShowWindow(GetDlgItem(hwndDlg, IDC_ADD), SW_HIDE);
ShowWindow(GetDlgItem(hwndDlg, IDC_REMOVE), SW_HIDE);
ShowWindow(GetDlgItem(hwndDlg, IDC_SHOW), SW_HIDE);
EnableWindow(GetDlgItem(hwndDlg, IDC_ADVANCED), FALSE);
PhUpdateHandlePermissionSecurity(context);
//if (PhPluginsEnabled)
//{
// PPH_PLUGIN_HANDLE_PROPERTIES_WINDOW_CONTEXT Context;
// Context = (PPH_PLUGIN_HANDLE_PROPERTIES_WINDOW_CONTEXT)context;
//
// PhInvokeCallback(PhGetGeneralCallback(GeneralCallbackHandlePropertiesWindowInitialized), Context);
//}
if (PhEnableThemeSupport) // TODO: Required for compat (dmex)
PhInitializeWindowTheme(context->ParentWindow, PhEnableThemeSupport);
else
PhInitializeWindowTheme(hwndDlg, FALSE);
}
break;
case WM_DESTROY:
{
//PhUnregisterWindowCallback(context->ParentWindow);
//PhSaveWindowPlacementToSetting(SETTING_HANDLE_PROPERTIES_WINDOW_POSITION, NULL, context->ParentWindow);
PhDeleteLayoutManager(&context->LayoutManager);
//PhDestroyListViewInterface(context->ListViewClass);
PhRemoveDialogContext(hwndDlg);
PhFree(context);
}
break;
case WM_DPICHANGED:
{
PhLayoutManagerUpdate(&context->LayoutManager, LOWORD(wParam));
PhLayoutManagerLayout(&context->LayoutManager);
}
break;
case WM_SIZE:
{
PhLayoutManagerLayout(&context->LayoutManager);
ExtendedListView_SetColumnWidth(context->ListViewHandle, 0, ELVSCW_AUTOSIZE_REMAININGSPACE);
}
break;
case WM_NOTIFY:
{
LPNMHDR header = (LPNMHDR)lParam;
PhHandleListViewNotifyBehaviors(lParam, context->ListViewHandle, PH_LIST_VIEW_DEFAULT_1_BEHAVIORS);
switch (header->code)
{
case PSN_QUERYINITIALFOCUS:
SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, (LONG_PTR)context->ListViewHandle);
return TRUE;
}
REFLECT_MESSAGE_DLG(hwndDlg, context->ListViewHandle, uMsg, wParam, lParam);
}
break;
case WM_CONTEXTMENU:
{
if ((HWND)wParam == context->ListViewHandle)
{
POINT point;
PPH_EMENU menu;
PPH_EMENU item;
PVOID *listviewItems;
ULONG numberOfItems;
point.x = GET_X_LPARAM(lParam);
point.y = GET_Y_LPARAM(lParam);
if (point.x == -1 && point.y == -1)
PhGetListViewContextMenuPoint(context->ListViewHandle, &point);
PhGetSelectedListViewItemParams(context->ListViewHandle, &listviewItems, &numberOfItems);
if (numberOfItems != 0)
{
menu = PhCreateEMenu();
PhInsertEMenuItem(menu, PhCreateEMenuItem(0, IDC_COPY, L"&Copy", NULL, NULL), ULONG_MAX);
PhInsertCopyListViewEMenuItem(menu, IDC_COPY, context->ListViewHandle);
item = PhShowEMenu(
menu,
hwndDlg,
PH_EMENU_SHOW_SEND_COMMAND | PH_EMENU_SHOW_LEFTRIGHT,
PH_ALIGN_LEFT | PH_ALIGN_TOP,
point.x,
point.y
);
if (item)
{
BOOLEAN handled = FALSE;
handled = PhHandleCopyListViewEMenuItem(item);
//if (!handled && PhPluginsEnabled)
// handled = PhPluginTriggerEMenuItem(&menuInfo, item);
if (!handled)
{
switch (item->Id)
{
case IDC_COPY:
{
PhCopyListView(context->ListViewHandle);
}
break;
}
}
}
PhDestroyEMenu(menu);
}
PhFree(listviewItems);
}
}
break;
HANDLE_MSG(hwndDlg, WM_CTLCOLORBTN, PhWindowThemeControlColor);
HANDLE_MSG(hwndDlg, WM_CTLCOLORDLG, PhWindowThemeControlColor);
HANDLE_MSG(hwndDlg, WM_CTLCOLORSTATIC, PhWindowThemeControlColor);
}
return FALSE;
}
INT_PTR CALLBACK PhpHandleAuditingDlgProc(
_In_ HWND hwndDlg,
_In_ UINT uMsg,
_In_ WPARAM wParam,
_In_ LPARAM lParam
)
{
PHANDLE_PERMISSIONS_CONTEXT context;
if (uMsg == WM_INITDIALOG)
{
LPPROPSHEETPAGE propSheetPage = (LPPROPSHEETPAGE)lParam;
context = PhAllocateZero(sizeof(HANDLE_PERMISSIONS_CONTEXT));
context->HandleProperties = (PHANDLE_PROPERTIES_CONTEXT)propSheetPage->lParam;
PhSetDialogContext(hwndDlg, context);
}
else
{
context = PhGetDialogContext(hwndDlg);
}
if (!context)
return FALSE;
switch (uMsg)
{
case WM_INITDIALOG:
{
PhSetApplicationWindowIcon(context->ParentWindow);
context->ListViewHandle = GetDlgItem(hwndDlg, IDC_LIST);
context->ListViewHeader = GetDlgItem(hwndDlg, IDC_SETTINGS);
context->ParentWindow = GetParent(hwndDlg);
//context->ListViewClass = PhGetListViewInterface(context->ListViewHandle);
PhSetListViewStyle(context->ListViewHandle, FALSE, TRUE);
PhSetControlTheme(context->ListViewHandle, L"explorer");
PhAddListViewColumn(context->ListViewHandle, 0, 0, 0, LVCFMT_LEFT, 140, L"Principal");
PhAddListViewColumn(context->ListViewHandle, 1, 1, 1, LVCFMT_LEFT, 50, L"Type");
PhAddListViewColumn(context->ListViewHandle, 2, 2, 2, LVCFMT_LEFT, 80, L"Access");
//PhAddListViewColumn(context->ListViewHandle, 3, 3, 3, LVCFMT_LEFT, 80, L"Inherited from");
//PhAddListViewColumn(context->ListViewHandle, 4, 4, 4, LVCFMT_LEFT, 80, L"Source");
PhSetExtendedListView(context->ListViewHandle);
PhInitializeLayoutManager(&context->LayoutManager, hwndDlg);
PhAddLayoutItem(&context->LayoutManager, context->ListViewHeader, NULL, PH_ANCHOR_LEFT | PH_ANCHOR_TOP | PH_ANCHOR_RIGHT);
PhAddLayoutItem(&context->LayoutManager, context->ListViewHandle, NULL, PH_ANCHOR_ALL);
PhAddLayoutItem(&context->LayoutManager, GetDlgItem(hwndDlg, IDC_ADD), NULL, PH_ANCHOR_LEFT | PH_ANCHOR_BOTTOM);
PhAddLayoutItem(&context->LayoutManager, GetDlgItem(hwndDlg, IDC_REMOVE), NULL, PH_ANCHOR_LEFT | PH_ANCHOR_BOTTOM);
PhAddLayoutItem(&context->LayoutManager, GetDlgItem(hwndDlg, IDC_SHOW), NULL, PH_ANCHOR_LEFT | PH_ANCHOR_BOTTOM);
PhAddLayoutItem(&context->LayoutManager, GetDlgItem(hwndDlg, IDC_ADVANCED), NULL, PH_ANCHOR_LEFT | PH_ANCHOR_BOTTOM);
ShowWindow(GetDlgItem(hwndDlg, IDC_ADD), SW_HIDE);
ShowWindow(GetDlgItem(hwndDlg, IDC_REMOVE), SW_HIDE);
ShowWindow(GetDlgItem(hwndDlg, IDC_SHOW), SW_HIDE);
EnableWindow(GetDlgItem(hwndDlg, IDC_ADVANCED), FALSE);
PhUpdateHandleAuditingSecurity(context);
}
break;
case WM_DESTROY:
{
//PhSaveWindowPlacementToSetting(SETTING_HANDLE_PROPERTIES_WINDOW_POSITION, NULL, context->ParentWindow);
PhDeleteLayoutManager(&context->LayoutManager);
//PhDestroyListViewInterface(context->ListViewClass);
PhRemoveDialogContext(hwndDlg);
PhFree(context);
}
break;
case WM_DPICHANGED:
{
PhLayoutManagerUpdate(&context->LayoutManager, LOWORD(wParam));
PhLayoutManagerLayout(&context->LayoutManager);
}
break;
case WM_SIZE:
{
PhLayoutManagerLayout(&context->LayoutManager);
ExtendedListView_SetColumnWidth(context->ListViewHandle, 0, ELVSCW_AUTOSIZE_REMAININGSPACE);
}
break;
case WM_NOTIFY:
{
LPNMHDR header = (LPNMHDR)lParam;
PhHandleListViewNotifyBehaviors(lParam, context->ListViewHandle, PH_LIST_VIEW_DEFAULT_1_BEHAVIORS);
switch (header->code)
{
case PSN_QUERYINITIALFOCUS:
SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, (LONG_PTR)context->ListViewHandle);
return TRUE;
}
REFLECT_MESSAGE_DLG(hwndDlg, context->ListViewHandle, uMsg, wParam, lParam);
}
break;
case WM_CONTEXTMENU:
{
if ((HWND)wParam == context->ListViewHandle)
{
POINT point;
PPH_EMENU menu;
PPH_EMENU item;
PVOID *listviewItems;
ULONG numberOfItems;
point.x = GET_X_LPARAM(lParam);
point.y = GET_Y_LPARAM(lParam);
if (point.x == -1 && point.y == -1)
PhGetListViewContextMenuPoint(context->ListViewHandle, &point);
PhGetSelectedListViewItemParams(context->ListViewHandle, &listviewItems, &numberOfItems);
if (numberOfItems != 0)
{
menu = PhCreateEMenu();
PhInsertEMenuItem(menu, PhCreateEMenuItem(0, IDC_COPY, L"&Copy", NULL, NULL), ULONG_MAX);
PhInsertCopyListViewEMenuItem(menu, IDC_COPY, context->ListViewHandle);
item = PhShowEMenu(
menu,
hwndDlg,
PH_EMENU_SHOW_SEND_COMMAND | PH_EMENU_SHOW_LEFTRIGHT,
PH_ALIGN_LEFT | PH_ALIGN_TOP,
point.x,
point.y
);
if (item)
{
BOOLEAN handled = FALSE;
handled = PhHandleCopyListViewEMenuItem(item);
//if (!handled && PhPluginsEnabled)
// handled = PhPluginTriggerEMenuItem(&menuInfo, item);
if (!handled)
{
switch (item->Id)
{
case IDC_COPY:
{
PhCopyListView(context->ListViewHandle);
}
break;
}
}
}
PhDestroyEMenu(menu);
}
PhFree(listviewItems);
}
}
break;
HANDLE_MSG(hwndDlg, WM_CTLCOLORBTN, PhWindowThemeControlColor);
HANDLE_MSG(hwndDlg, WM_CTLCOLORDLG, PhWindowThemeControlColor);
HANDLE_MSG(hwndDlg, WM_CTLCOLORSTATIC, PhWindowThemeControlColor);
}
return FALSE;
}
================================================
FILE: SystemInformer/hndlprv.c
================================================
/*
* Copyright (c) 2022 Winsider Seminars & Solutions, Inc. All rights reserved.
*
* This file is part of System Informer.
*
* Authors:
*
* wj32 2010-2015
* dmex 2017-2023
*
*/
#include
#include
#include
#include
#include
#include
#include
#include
typedef struct _PHP_CREATE_HANDLE_ITEM_CONTEXT
{
PPH_HANDLE_PROVIDER Provider;
PSYSTEM_HANDLE_TABLE_ENTRY_INFO_EX Handle;
} PHP_CREATE_HANDLE_ITEM_CONTEXT, *PPHP_CREATE_HANDLE_ITEM_CONTEXT;
_Function_class_(PH_TYPE_DELETE_PROCEDURE)
VOID NTAPI PhpHandleProviderDeleteProcedure(
_In_ PVOID Object,
_In_ ULONG Flags
);
_Function_class_(PH_TYPE_DELETE_PROCEDURE)
VOID NTAPI PhpHandleItemDeleteProcedure(
_In_ PVOID Object,
_In_ ULONG Flags
);
PPH_OBJECT_TYPE PhHandleProviderType = NULL;
PPH_OBJECT_TYPE PhHandleItemType = NULL;
PPH_HANDLE_PROVIDER PhCreateHandleProvider(
_In_ HANDLE ProcessId
)
{
static PH_INITONCE initOnce = PH_INITONCE_INIT;
PPH_HANDLE_PROVIDER handleProvider;
if (PhBeginInitOnce(&initOnce))
{
PhHandleProviderType = PhCreateObjectType(L"HandleProvider", 0, PhpHandleProviderDeleteProcedure);
PhEndInitOnce(&initOnce);
}
handleProvider = PhCreateObject(
PhEmGetObjectSize(EmHandleProviderType, sizeof(PH_HANDLE_PROVIDER)),
PhHandleProviderType
);
memset(handleProvider, 0, sizeof(PH_HANDLE_PROVIDER));
handleProvider->HandleHashSetSize = 128;
handleProvider->HandleHashSet = PhCreateHashSet(handleProvider->HandleHashSetSize);
handleProvider->HandleHashSetCount = 0;
PhInitializeQueuedLock(&handleProvider->HandleHashSetLock);
PhInitializeCallback(&handleProvider->HandleAddedEvent);
PhInitializeCallback(&handleProvider->HandleModifiedEvent);
PhInitializeCallback(&handleProvider->HandleRemovedEvent);
PhInitializeCallback(&handleProvider->HandleUpdatedEvent);
handleProvider->ProcessId = ProcessId;
handleProvider->ProcessHandle = NULL;
handleProvider->RunStatus = PhOpenProcess(
&handleProvider->ProcessHandle,
PROCESS_QUERY_INFORMATION | PROCESS_DUP_HANDLE,
ProcessId
);
if (!NT_SUCCESS(handleProvider->RunStatus))
{
handleProvider->RunStatus = PhOpenProcess(
&handleProvider->ProcessHandle,
PROCESS_QUERY_INFORMATION,
ProcessId
);
}
handleProvider->TempListHashtable = PhCreateSimpleHashtable(512);
PhEmCallObjectOperation(EmHandleProviderType, handleProvider, EmObjectCreate);
return handleProvider;
}
_Function_class_(PH_TYPE_DELETE_PROCEDURE)
VOID PhpHandleProviderDeleteProcedure(
_In_ PVOID Object,
_In_ ULONG Flags
)
{
PPH_HANDLE_PROVIDER handleProvider = (PPH_HANDLE_PROVIDER)Object;
PhEmCallObjectOperation(EmHandleProviderType, handleProvider, EmObjectDelete);
// Dereference all handle items (we referenced them
// when we added them to the hashtable).
PhDereferenceAllHandleItems(handleProvider);
PhFree(handleProvider->HandleHashSet);
PhDeleteCallback(&handleProvider->HandleAddedEvent);
PhDeleteCallback(&handleProvider->HandleModifiedEvent);
PhDeleteCallback(&handleProvider->HandleRemovedEvent);
if (handleProvider->ProcessHandle) NtClose(handleProvider->ProcessHandle);
PhDereferenceObject(handleProvider->TempListHashtable);
}
PPH_HANDLE_ITEM PhCreateHandleItem(
_In_opt_ PSYSTEM_HANDLE_TABLE_ENTRY_INFO_EX Handle
)
{
static PH_INITONCE initOnce = PH_INITONCE_INIT;
PPH_HANDLE_ITEM handleItem;
if (PhBeginInitOnce(&initOnce))
{
PhHandleItemType = PhCreateObjectType(L"HandleItem", 0, PhpHandleItemDeleteProcedure);
PhEndInitOnce(&initOnce);
}
handleItem = PhCreateObject(
PhEmGetObjectSize(EmHandleItemType, sizeof(PH_HANDLE_ITEM)),
PhHandleItemType
);
memset(handleItem, 0, sizeof(PH_HANDLE_ITEM));
if (Handle)
{
handleItem->Object = Handle->Object;
handleItem->ProcessId = Handle->UniqueProcessId;
handleItem->Handle = Handle->HandleValue;
handleItem->Attributes = Handle->HandleAttributes;
handleItem->GrantedAccess = Handle->GrantedAccess;
handleItem->TypeIndex = Handle->ObjectTypeIndex;
PhPrintPointer(handleItem->HandleString, (PVOID)handleItem->Handle);
PhPrintPointer(handleItem->GrantedAccessString, UlongToPtr(handleItem->GrantedAccess));
if (handleItem->Object)
PhPrintPointer(handleItem->ObjectString, handleItem->Object);
}
PhEmCallObjectOperation(EmHandleItemType, handleItem, EmObjectCreate);
return handleItem;
}
_Function_class_(PH_TYPE_DELETE_PROCEDURE)
VOID PhpHandleItemDeleteProcedure(
_In_ PVOID Object,
_In_ ULONG Flags
)
{
PPH_HANDLE_ITEM handleItem = (PPH_HANDLE_ITEM)Object;
PhEmCallObjectOperation(EmHandleItemType, handleItem, EmObjectDelete);
if (handleItem->TypeName) PhDereferenceObject(handleItem->TypeName);
if (handleItem->ObjectName) PhDereferenceObject(handleItem->ObjectName);
if (handleItem->BestObjectName) PhDereferenceObject(handleItem->BestObjectName);
}
FORCEINLINE BOOLEAN PhCompareHandleItem(
_In_ PPH_HANDLE_ITEM Value1,
_In_ PPH_HANDLE_ITEM Value2
)
{
return Value1->Handle == Value2->Handle;
}
FORCEINLINE ULONG PhHashHandleItem(
_In_ PPH_HANDLE_ITEM Value
)
{
return HandleToUlong(Value->Handle) / 4;
}
PPH_HANDLE_ITEM PhpLookupHandleItem(
_In_ PPH_HANDLE_PROVIDER HandleProvider,
_In_ HANDLE Handle
)
{
PH_HANDLE_ITEM lookupHandleItem;
PPH_HASH_ENTRY entry;
PPH_HANDLE_ITEM handleItem;
lookupHandleItem.Handle = Handle;
entry = PhFindEntryHashSet(
HandleProvider->HandleHashSet,
HandleProvider->HandleHashSetSize,
PhHashHandleItem(&lookupHandleItem)
);
for (; entry; entry = entry->Next)
{
handleItem = CONTAINING_RECORD(entry, PH_HANDLE_ITEM, HashEntry);
if (PhCompareHandleItem(&lookupHandleItem, handleItem))
return handleItem;
}
return NULL;
}
PPH_HANDLE_ITEM PhReferenceHandleItem(
_In_ PPH_HANDLE_PROVIDER HandleProvider,
_In_ HANDLE Handle
)
{
PPH_HANDLE_ITEM handleItem;
PhAcquireQueuedLockShared(&HandleProvider->HandleHashSetLock);
handleItem = PhpLookupHandleItem(HandleProvider, Handle);
if (handleItem)
PhReferenceObject(handleItem);
PhReleaseQueuedLockShared(&HandleProvider->HandleHashSetLock);
return handleItem;
}
VOID PhDereferenceAllHandleItems(
_In_ PPH_HANDLE_PROVIDER HandleProvider
)
{
ULONG i;
PPH_HASH_ENTRY entry;
PPH_HANDLE_ITEM handleItem;
PhAcquireQueuedLockExclusive(&HandleProvider->HandleHashSetLock);
for (i = 0; i < HandleProvider->HandleHashSetSize; i++)
{
entry = HandleProvider->HandleHashSet[i];
while (entry)
{
handleItem = CONTAINING_RECORD(entry, PH_HANDLE_ITEM, HashEntry);
entry = entry->Next;
PhDereferenceObject(handleItem);
}
}
PhReleaseQueuedLockExclusive(&HandleProvider->HandleHashSetLock);
}
VOID PhpAddHandleItem(
_In_ PPH_HANDLE_PROVIDER HandleProvider,
_In_ _Assume_refs_(1) PPH_HANDLE_ITEM HandleItem
)
{
if (HandleProvider->HandleHashSetSize < HandleProvider->HandleHashSetCount + 1)
{
PhResizeHashSet(
&HandleProvider->HandleHashSet,
&HandleProvider->HandleHashSetSize,
HandleProvider->HandleHashSetSize * 2
);
}
PhAddEntryHashSet(
HandleProvider->HandleHashSet,
HandleProvider->HandleHashSetSize,
&HandleItem->HashEntry,
PhHashHandleItem(HandleItem)
);
HandleProvider->HandleHashSetCount++;
}
VOID PhpRemoveHandleItem(
_In_ PPH_HANDLE_PROVIDER HandleProvider,
_In_ PPH_HANDLE_ITEM HandleItem
)
{
PhRemoveEntryHashSet(HandleProvider->HandleHashSet, HandleProvider->HandleHashSetSize, &HandleItem->HashEntry);
HandleProvider->HandleHashSetCount--;
PhDereferenceObject(HandleItem);
}
_Function_class_(USER_THREAD_START_ROUTINE)
NTSTATUS PhpCreateHandleItemFunction(
_In_ PVOID Parameter
)
{
PPHP_CREATE_HANDLE_ITEM_CONTEXT context = Parameter;
PPH_HANDLE_ITEM handleItem;
OBJECT_BASIC_INFORMATION handleBasicInformation = { 0 };
handleItem = PhCreateHandleItem(context->Handle);
PhGetHandleInformationEx(
context->Provider->ProcessHandle,
handleItem->Handle,
context->Handle->ObjectTypeIndex,
0,
NULL,
&handleBasicInformation,
&handleItem->TypeName,
&handleItem->ObjectName,
&handleItem->BestObjectName,
NULL
);
if (PhIsNullOrEmptyString(handleItem->TypeName))
{
PhMoveReference(&handleItem->TypeName, PhGetObjectTypeIndexName(handleItem->TypeIndex));
}
handleItem->HandleCount = handleBasicInformation.HandleCount;
handleItem->PointerCount = handleBasicInformation.PointerCount;
handleItem->PagedPoolCharge = handleBasicInformation.PagedPoolCharge;
handleItem->NonPagedPoolCharge = handleBasicInformation.NonPagedPoolCharge;
if (handleItem->TypeName)
{
// Add the handle item to the hashtable.
PhAcquireQueuedLockExclusive(&context->Provider->HandleHashSetLock);
PhpAddHandleItem(context->Provider, handleItem);
PhReleaseQueuedLockExclusive(&context->Provider->HandleHashSetLock);
// Raise the handle added event.
PhInvokeCallback(&context->Provider->HandleAddedEvent, handleItem);
}
else
{
PhDereferenceObject(handleItem);
}
PhFree(context);
return STATUS_SUCCESS;
}
_Function_class_(PH_PROVIDER_FUNCTION)
VOID PhHandleProviderUpdate(
_In_ PVOID Object
)
{
static PH_INITONCE initOnce = PH_INITONCE_INIT;
static ULONG fileObjectTypeIndex = ULONG_MAX;
PPH_HANDLE_PROVIDER handleProvider = (PPH_HANDLE_PROVIDER)Object;
PSYSTEM_HANDLE_INFORMATION_EX handleInfo;
PSYSTEM_HANDLE_TABLE_ENTRY_INFO_EX handles;
ULONG_PTR numberOfHandles;
ULONG i;
PH_HASHTABLE_ENUM_CONTEXT enumContext;
PPH_KEY_VALUE_PAIR handlePair;
BOOLEAN useWorkQueue = FALSE;
PH_WORK_QUEUE workQueue;
KPH_LEVEL level;
handleProvider->RunStatus = PhEnumHandlesGeneric(
handleProvider->ProcessId,
handleProvider->ProcessHandle,
!!PhCsEnableHandleSnapshot,
&handleInfo
);
level = KsiLevel();
if (level < KphLevelMed)
{
useWorkQueue = TRUE;
PhInitializeWorkQueue(&workQueue, 1, 20, 1000);
if (PhBeginInitOnce(&initOnce))
{
fileObjectTypeIndex = PhGetObjectTypeNumberZ(L"File");
PhEndInitOnce(&initOnce);
}
}
if (NT_SUCCESS(handleProvider->RunStatus))
{
handles = handleInfo->Handles;
numberOfHandles = handleInfo->NumberOfHandles;
for (i = 0; i < numberOfHandles; i++)
{
PSYSTEM_HANDLE_TABLE_ENTRY_INFO_EX handle = &handles[i];
PhAddItemSimpleHashtable(
handleProvider->TempListHashtable,
handle->HandleValue,
handle
);
}
}
// Look for closed handles.
{
PPH_LIST handlesToRemove = NULL;
PPH_HASH_ENTRY entry;
PPH_HANDLE_ITEM handleItem;
PSYSTEM_HANDLE_TABLE_ENTRY_INFO_EX *tempHashtableValue;
for (i = 0; i < handleProvider->HandleHashSetSize; i++)
{
for (entry = handleProvider->HandleHashSet[i]; entry; entry = entry->Next)
{
BOOLEAN found = FALSE;
handleItem = CONTAINING_RECORD(entry, PH_HANDLE_ITEM, HashEntry);
// Check if the handle still exists.
tempHashtableValue = (PSYSTEM_HANDLE_TABLE_ENTRY_INFO_EX *)PhFindItemSimpleHashtable(
handleProvider->TempListHashtable,
handleItem->Handle
);
if (tempHashtableValue)
{
// Also compare the object pointers to make sure a different object wasn't
// re-opened with the same handle value.
if (level >= KphLevelMed && handleProvider->ProcessHandle)
{
found = NT_SUCCESS(KphCompareObjects(
handleProvider->ProcessHandle,
handleItem->Handle,
(*tempHashtableValue)->HandleValue
));
}
// This isn't 100% accurate as pool addresses may be re-used, but it works well.
else if (handleItem->Object && handleItem->Object == (*tempHashtableValue)->Object)
{
found = TRUE;
}
else
{
if (
handleItem->Handle == (*tempHashtableValue)->HandleValue &&
handleItem->GrantedAccess == (*tempHashtableValue)->GrantedAccess &&
handleItem->TypeIndex == (*tempHashtableValue)->ObjectTypeIndex &&
handleItem->Attributes == (*tempHashtableValue)->HandleAttributes &&
handleItem->ProcessId == (*tempHashtableValue)->UniqueProcessId
)
{
found = TRUE;
}
}
}
if (!found)
{
// Raise the handle removed event.
PhInvokeCallback(&handleProvider->HandleRemovedEvent, handleItem);
if (!handlesToRemove)
handlesToRemove = PhCreateList(2);
PhAddItemList(handlesToRemove, handleItem);
}
}
}
if (handlesToRemove)
{
PhAcquireQueuedLockExclusive(&handleProvider->HandleHashSetLock);
for (i = 0; i < handlesToRemove->Count; i++)
{
PhpRemoveHandleItem(handleProvider, handlesToRemove->Items[i]);
}
PhReleaseQueuedLockExclusive(&handleProvider->HandleHashSetLock);
PhDereferenceObject(handlesToRemove);
}
}
// Look for new handles and update existing ones.
PhBeginEnumHashtable(handleProvider->TempListHashtable, &enumContext);
while (handlePair = PhNextEnumHashtable(&enumContext))
{
PSYSTEM_HANDLE_TABLE_ENTRY_INFO_EX handle = handlePair->Value;
PPH_HANDLE_ITEM handleItem;
handleItem = PhpLookupHandleItem(handleProvider, handle->HandleValue);
if (!handleItem)
{
OBJECT_BASIC_INFORMATION handleBasicInformation = { 0 };
// When we don't have KPH, query handle information in parallel to take full advantage of the
// PhCallWithTimeout functionality.
if (useWorkQueue && handle->ObjectTypeIndex == fileObjectTypeIndex)
{
PPHP_CREATE_HANDLE_ITEM_CONTEXT context;
context = PhAllocate(sizeof(PHP_CREATE_HANDLE_ITEM_CONTEXT));
context->Provider = handleProvider;
context->Handle = handle;
PhQueueItemWorkQueue(&workQueue, PhpCreateHandleItemFunction, context);
continue;
}
handleItem = PhCreateHandleItem(handle);
PhGetHandleInformationEx(
handleProvider->ProcessHandle,
handleItem->Handle,
handle->ObjectTypeIndex,
0,
NULL,
&handleBasicInformation,
&handleItem->TypeName,
&handleItem->ObjectName,
&handleItem->BestObjectName,
NULL
);
handleItem->HandleCount = handleBasicInformation.HandleCount;
handleItem->PointerCount = handleBasicInformation.PointerCount;
handleItem->PagedPoolCharge = handleBasicInformation.PagedPoolCharge;
handleItem->NonPagedPoolCharge = handleBasicInformation.NonPagedPoolCharge;
if (PhIsNullOrEmptyString(handleItem->TypeName))
{
PPH_STRING typeName;
if (typeName = PhGetObjectTypeIndexName(handleItem->TypeIndex))
{
PhMoveReference(&handleItem->TypeName, typeName);
}
}
if (handle->ObjectTypeIndex == fileObjectTypeIndex)
{
if (level >= KphLevelMed)
{
KPH_FILE_OBJECT_INFORMATION objectInfo;
if (NT_SUCCESS(KphQueryInformationObject(
handleProvider->ProcessHandle,
handleItem->Handle,
KphObjectFileObjectInformation,
&objectInfo,
sizeof(KPH_FILE_OBJECT_INFORMATION),
NULL
)))
{
if (objectInfo.SharedRead)
handleItem->FileFlags |= PH_HANDLE_FILE_SHARED_READ;
if (objectInfo.SharedWrite)
handleItem->FileFlags |= PH_HANDLE_FILE_SHARED_WRITE;
if (objectInfo.SharedDelete)
handleItem->FileFlags |= PH_HANDLE_FILE_SHARED_DELETE;
// TODO add extra info from file objects here (jxy-s)
// objectInfo.HasActiveTransaction;
// objectInfo.UserWritableReferences;
// objectInfo.IsIgnoringSharing;
// ... more
}
}
}
// Add the handle item to the hashtable.
PhAcquireQueuedLockExclusive(&handleProvider->HandleHashSetLock);
PhpAddHandleItem(handleProvider, handleItem);
PhReleaseQueuedLockExclusive(&handleProvider->HandleHashSetLock);
// Raise the handle added event.
PhInvokeCallback(&handleProvider->HandleAddedEvent, handleItem);
}
else
{
BOOLEAN modified = FALSE;
OBJECT_BASIC_INFORMATION handleBasicInformation = { 0 };
if (NT_SUCCESS(PhGetHandleInformationEx(
handleProvider->ProcessHandle,
handleItem->Handle,
handle->ObjectTypeIndex,
0,
NULL,
&handleBasicInformation,
NULL,
NULL,
NULL,
NULL
)))
{
if (handleItem->HandleCount != handleBasicInformation.HandleCount)
{
handleItem->HandleCount = handleBasicInformation.HandleCount;
modified = TRUE;
}
if (handleItem->PointerCount != handleBasicInformation.PointerCount)
{
handleItem->PointerCount = handleBasicInformation.PointerCount;
modified = TRUE;
}
if (handleItem->PagedPoolCharge != handleBasicInformation.PagedPoolCharge)
{
handleItem->PagedPoolCharge = handleBasicInformation.PagedPoolCharge;
modified = TRUE;
}
if (handleItem->NonPagedPoolCharge != handleBasicInformation.NonPagedPoolCharge)
{
handleItem->NonPagedPoolCharge = handleBasicInformation.NonPagedPoolCharge;
modified = TRUE;
}
}
if (handleItem->Attributes != handle->HandleAttributes)
{
handleItem->Attributes = handle->HandleAttributes;
modified = TRUE;
}
if (modified)
{
// Raise the handle modified event.
PhInvokeCallback(&handleProvider->HandleModifiedEvent, handleItem);
}
}
}
if (useWorkQueue)
{
PhWaitForWorkQueue(&workQueue);
PhDeleteWorkQueue(&workQueue);
}
if (NT_SUCCESS(handleProvider->RunStatus))
{
PhFree(handleInfo);
}
// Re-create the temporary hashtable if it got too big.
if (handleProvider->TempListHashtable->AllocatedEntries > 8192)
{
PhDereferenceObject(handleProvider->TempListHashtable);
handleProvider->TempListHashtable = PhCreateSimpleHashtable(512);
}
else
{
PhClearHashtable(handleProvider->TempListHashtable);
}
PhInvokeCallback(&handleProvider->HandleUpdatedEvent, NULL);
}
================================================
FILE: SystemInformer/hndlstat.c
================================================
/*
* Copyright (c) 2022 Winsider Seminars & Solutions, Inc. All rights reserved.
*
* This file is part of System Informer.
*
* Authors:
*
* wj32 2010
* dmex 2022-2023
*
*/
#include
#include
#include
#include
typedef struct _HANDLE_STATISTICS_ENTRY
{
PPH_STRING Name;
ULONG Count;
} HANDLE_STATISTICS_ENTRY, *PHANDLE_STATISTICS_ENTRY;
typedef struct _HANDLE_STATISTICS_CONTEXT
{
HANDLE ProcessId;
HANDLE ProcessHandle;
HWND ListViewHandle;
PH_LAYOUT_MANAGER LayoutManager;
PSYSTEM_HANDLE_INFORMATION_EX Handles;
HANDLE_STATISTICS_ENTRY Entries[MAX_OBJECT_TYPE_NUMBER];
} HANDLE_STATISTICS_CONTEXT, *PHANDLE_STATISTICS_CONTEXT;
INT_PTR CALLBACK PhpHandleStatisticsDlgProc(
_In_ HWND hwndDlg,
_In_ UINT uMsg,
_In_ WPARAM wParam,
_In_ LPARAM lParam
);
VOID PhShowHandleStatisticsDialog(
_In_ HWND ParentWindowHandle,
_In_ HANDLE ProcessId
)
{
NTSTATUS status;
PHANDLE_STATISTICS_CONTEXT context;
PSYSTEM_HANDLE_INFORMATION_EX handleInfo;
HANDLE processHandle;
status = PhOpenProcess(
&processHandle,
PROCESS_DUP_HANDLE,
ProcessId
);
if (!NT_SUCCESS(status))
{
PhShowStatus(ParentWindowHandle, L"Unable to open the process", status, 0);
return;
}
status = PhEnumHandlesGeneric(
ProcessId,
processHandle,
!!PhCsEnableHandleSnapshot,
&handleInfo
);
if (!NT_SUCCESS(status))
{
NtClose(processHandle);
PhShowStatus(ParentWindowHandle, L"Unable to enumerate process handles", status, 0);
return;
}
context = PhAllocateZero(sizeof(HANDLE_STATISTICS_CONTEXT));
context->ProcessId = ProcessId;
context->ProcessHandle = processHandle;
context->Handles = handleInfo;
PhDialogBox(
PhInstanceHandle,
MAKEINTRESOURCE(IDD_HANDLESTATS),
ParentWindowHandle,
PhpHandleStatisticsDlgProc,
context
);
}
void PhDestroyHandleStatistics(
_In_ PHANDLE_STATISTICS_CONTEXT Context)
{
for (ULONG i = 0; i < MAX_OBJECT_TYPE_NUMBER; i++)
{
if (Context->Entries[i].Name)
{
PhDereferenceObject(Context->Entries[i].Name);
}
}
if (Context->Handles)
{
PhFree(Context->Handles);
}
if (Context->ProcessHandle)
{
NtClose(Context->ProcessHandle);
}
}
static LONG NTAPI PhpTypeCountCompareFunction(
_In_ PVOID Item1,
_In_ PVOID Item2,
_In_opt_ PVOID Context
)
{
PHANDLE_STATISTICS_ENTRY entry1 = Item1;
PHANDLE_STATISTICS_ENTRY entry2 = Item2;
return uintcmp(entry1->Count, entry2->Count);
}
INT_PTR CALLBACK PhpHandleStatisticsDlgProc(
_In_ HWND hwndDlg,
_In_ UINT uMsg,
_In_ WPARAM wParam,
_In_ LPARAM lParam
)
{
PHANDLE_STATISTICS_CONTEXT context;
if (uMsg == WM_INITDIALOG)
{
context = (PHANDLE_STATISTICS_CONTEXT)lParam;
PhSetWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT, context);
}
else
{
context = PhGetWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT);
}
if (!context)
return FALSE;
switch (uMsg)
{
case WM_INITDIALOG:
{
HANDLE processId;
ULONG_PTR i;
PhSetApplicationWindowIcon(hwndDlg);
processId = context->ProcessId;
context->ListViewHandle = GetDlgItem(hwndDlg, IDC_LIST);
PhSetListViewStyle(context->ListViewHandle, TRUE, TRUE);
PhSetControlTheme(context->ListViewHandle, L"explorer");
PhAddListViewColumn(context->ListViewHandle, 0, 0, 0, LVCFMT_LEFT, 140, L"Type");
PhAddListViewColumn(context->ListViewHandle, 1, 1, 1, LVCFMT_LEFT, 100, L"Count");
PhSetExtendedListView(context->ListViewHandle);
ExtendedListView_SetCompareFunction(context->ListViewHandle, 1, PhpTypeCountCompareFunction);
PhLoadListViewColumnsFromSetting(SETTING_HANDLE_STATISTICS_LIST_VIEW_COLUMNS, context->ListViewHandle);
PhLoadListViewSortColumnsFromSetting(SETTING_HANDLE_STATISTICS_LIST_VIEW_SORT, context->ListViewHandle);
PhInitializeLayoutManager(&context->LayoutManager, hwndDlg);
PhAddLayoutItem(&context->LayoutManager, context->ListViewHandle, NULL, PH_ANCHOR_ALL);
PhAddLayoutItem(&context->LayoutManager, GetDlgItem(hwndDlg, IDOK), NULL, PH_ANCHOR_BOTTOM | PH_ANCHOR_RIGHT);
if (PhValidWindowPlacementFromSetting(SETTING_HANDLE_STATISTICS_WINDOW_POSITION))
PhLoadWindowPlacementFromSetting(SETTING_HANDLE_STATISTICS_WINDOW_POSITION, SETTING_HANDLE_STATISTICS_WINDOW_SIZE, hwndDlg);
else
PhCenterWindow(hwndDlg, GetParent(hwndDlg));
for (i = 0; i < context->Handles->NumberOfHandles; i++)
{
PSYSTEM_HANDLE_TABLE_ENTRY_INFO_EX handleInfo;
PHANDLE_STATISTICS_ENTRY entry;
handleInfo = &context->Handles->Handles[i];
if (handleInfo->UniqueProcessId != processId)
continue;
if (handleInfo->ObjectTypeIndex >= MAX_OBJECT_TYPE_NUMBER)
continue;
entry = &context->Entries[handleInfo->ObjectTypeIndex];
if (PhIsNullOrEmptyString(entry->Name))
{
entry->Name = PhGetObjectTypeIndexName(handleInfo->ObjectTypeIndex);
if (PhIsNullOrEmptyString(entry->Name))
{
PPH_STRING typeName = NULL;
PhGetHandleInformation(
context->ProcessHandle,
handleInfo->HandleValue,
handleInfo->ObjectTypeIndex,
NULL,
&typeName,
NULL,
NULL
);
PhMoveReference(&entry->Name, typeName);
}
}
entry->Count++;
}
for (i = 0; i < MAX_OBJECT_TYPE_NUMBER; i++)
{
PHANDLE_STATISTICS_ENTRY entry;
PPH_STRING unknownType;
PPH_STRING countString;
LONG lvItemIndex;
entry = &context->Entries[i];
if (entry->Count == 0)
continue;
if (PhIsNullOrEmptyString(entry->Name))
{
unknownType = PhFormatString(L"(unknown: %lu)", (ULONG)i);
lvItemIndex = PhAddListViewItem(
context->ListViewHandle,
MAXINT,
PhGetString(unknownType),
entry
);
PhDereferenceObject(unknownType);
}
else
{
lvItemIndex = PhAddListViewItem(
context->ListViewHandle,
MAXINT,
PhGetString(entry->Name),
entry
);
}
countString = PhFormatUInt64(entry->Count, TRUE);
PhSetListViewSubItem(context->ListViewHandle, lvItemIndex, 1, PhGetString(countString));
PhDereferenceObject(countString);
}
ExtendedListView_SortItems(context->ListViewHandle);
PhInitializeWindowTheme(hwndDlg, PhEnableThemeSupport);
}
break;
case WM_DESTROY:
{
PhDestroyHandleStatistics(context);
PhSaveListViewSortColumnsToSetting(SETTING_HANDLE_STATISTICS_LIST_VIEW_SORT, context->ListViewHandle);
PhSaveListViewColumnsToSetting(SETTING_HANDLE_STATISTICS_LIST_VIEW_COLUMNS, context->ListViewHandle);
PhSaveWindowPlacementToSetting(SETTING_HANDLE_STATISTICS_WINDOW_POSITION, SETTING_HANDLE_STATISTICS_WINDOW_SIZE, hwndDlg);
PhDeleteLayoutManager(&context->LayoutManager);
PhRemoveWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT);
}
break;
case WM_COMMAND:
{
switch (GET_WM_COMMAND_ID(wParam, lParam))
{
case IDCANCEL:
case IDOK:
EndDialog(hwndDlg, IDOK);
break;
}
}
break;
case WM_NOTIFY:
{
PhHandleListViewNotifyForCopy(lParam, context->ListViewHandle);
}
break;
case WM_DPICHANGED:
{
PhLayoutManagerUpdate(&context->LayoutManager, LOWORD(wParam));
PhLayoutManagerLayout(&context->LayoutManager);
}
break;
case WM_SIZE:
{
PhLayoutManagerLayout(&context->LayoutManager);
}
break;
case WM_CTLCOLORBTN:
return HANDLE_WM_CTLCOLORBTN(hwndDlg, wParam, lParam, PhWindowThemeControlColor);
case WM_CTLCOLORDLG:
return HANDLE_WM_CTLCOLORDLG(hwndDlg, wParam, lParam, PhWindowThemeControlColor);
case WM_CTLCOLORSTATIC:
return HANDLE_WM_CTLCOLORSTATIC(hwndDlg, wParam, lParam, PhWindowThemeControlColor);
}
return FALSE;
}
================================================
FILE: SystemInformer/include/actions.h
================================================
/*
* Copyright (c) 2022 Winsider Seminars & Solutions, Inc. All rights reserved.
*
* This file is part of System Informer.
*
* Authors:
*
* wj32 2016
* dmex 2018-2023
*
*/
#ifndef PH_ACTIONS_H
#define PH_ACTIONS_H
EXTERN_C_START
typedef enum _PH_ACTION_ELEVATION_LEVEL
{
NeverElevateAction = 0,
PromptElevateAction = 1,
AlwaysElevateAction = 2
} PH_ACTION_ELEVATION_LEVEL;
// begin_phapppub
typedef enum _PH_PHSVC_MODE
{
ElevatedPhSvcMode,
Wow64PhSvcMode
} PH_PHSVC_MODE;
PHAPPAPI
BOOLEAN
NTAPI
PhUiConnectToPhSvc(
_In_opt_ HWND WindowHandle,
_In_ BOOLEAN ConnectOnly
);
PHAPPAPI
BOOLEAN
NTAPI
PhUiConnectToPhSvcEx(
_In_opt_ HWND WindowHandle,
_In_ PH_PHSVC_MODE Mode,
_In_ BOOLEAN ConnectOnly
);
PHAPPAPI
VOID
NTAPI
PhUiDisconnectFromPhSvc(
VOID
);
PHAPPAPI
BOOLEAN
NTAPI
PhUiLockComputer(
_In_ HWND WindowHandle
);
PHAPPAPI
BOOLEAN
NTAPI
PhUiLogoffComputer(
_In_ HWND WindowHandle
);
PHAPPAPI
BOOLEAN
NTAPI
PhUiSleepComputer(
_In_ HWND WindowHandle
);
PHAPPAPI
BOOLEAN
NTAPI
PhUiHibernateComputer(
_In_ HWND WindowHandle
);
typedef enum _PH_POWERACTION_TYPE
{
PH_POWERACTION_TYPE_NONE,
PH_POWERACTION_TYPE_WIN32,
PH_POWERACTION_TYPE_NATIVE,
PH_POWERACTION_TYPE_CRITICAL,
PH_POWERACTION_TYPE_ADVANCEDBOOT,
PH_POWERACTION_TYPE_FIRMWAREBOOT,
PH_POWERACTION_TYPE_UPDATE,
PH_POWERACTION_TYPE_WDOSCAN,
PH_POWERACTION_TYPE_MAXIMUM
} PH_POWERACTION_TYPE;
PHAPPAPI
BOOLEAN
NTAPI
PhUiRestartComputer(
_In_ HWND WindowHandle,
_In_ PH_POWERACTION_TYPE Action,
_In_ ULONG Flags
);
PHAPPAPI
BOOLEAN
NTAPI
PhUiShutdownComputer(
_In_ HWND WindowHandle,
_In_ PH_POWERACTION_TYPE Action,
_In_ ULONG Flags
);
PVOID PhUiCreateComputerBootDeviceMenu(
_In_ BOOLEAN DelayLoadMenu
);
PVOID PhUiCreateComputerFirmwareDeviceMenu(
_In_ BOOLEAN DelayLoadMenu
);
VOID PhUiHandleComputerBootApplicationMenu(
_In_ HWND WindowHandle,
_In_ ULONG MenuIndex
);
VOID PhUiHandleComputerFirmwareApplicationMenu(
_In_ HWND WindowHandle,
_In_ ULONG MenuIndex
);
VOID PhUiCreateSessionMenu(
_In_ PVOID UsersMenuItem
);
PHAPPAPI
BOOLEAN
NTAPI
PhUiConnectSession(
_In_ HWND WindowHandle,
_In_ ULONG SessionId
);
PHAPPAPI
BOOLEAN
NTAPI
PhUiDisconnectSession(
_In_ HWND WindowHandle,
_In_ ULONG SessionId
);
PHAPPAPI
BOOLEAN
NTAPI
PhUiLogoffSession(
_In_ HWND WindowHandle,
_In_ ULONG SessionId
);
PHAPPAPI
BOOLEAN
NTAPI
PhUiTerminateProcesses(
_In_ HWND WindowHandle,
_In_ PPH_PROCESS_ITEM *Processes,
_In_ ULONG NumberOfProcesses
);
PHAPPAPI
BOOLEAN
NTAPI
PhUiTerminateTreeProcess(
_In_ HWND WindowHandle,
_In_ PPH_PROCESS_ITEM Process
);
PHAPPAPI
BOOLEAN
NTAPI
PhUiSuspendProcesses(
_In_ HWND WindowHandle,
_In_ PPH_PROCESS_ITEM *Processes,
_In_ ULONG NumberOfProcesses
);
PHAPPAPI
BOOLEAN
NTAPI
PhUiSuspendTreeProcess(
_In_ HWND WindowHandle,
_In_ PPH_PROCESS_ITEM Process
);
PHAPPAPI
BOOLEAN
NTAPI
PhUiResumeProcesses(
_In_ HWND WindowHandle,
_In_ PPH_PROCESS_ITEM *Processes,
_In_ ULONG NumberOfProcesses
);
PHAPPAPI
BOOLEAN
NTAPI
PhUiResumeTreeProcess(
_In_ HWND WindowHandle,
_In_ PPH_PROCESS_ITEM Process
);
PHAPPAPI
BOOLEAN
NTAPI
PhUiFreezeTreeProcess(
_In_ HWND WindowHandle,
_In_ PPH_PROCESS_ITEM Process
);
PHAPPAPI
BOOLEAN
NTAPI
PhUiThawTreeProcess(
_In_ HWND WindowHandle,
_In_ PPH_PROCESS_ITEM Process
);
PHAPPAPI
BOOLEAN
NTAPI
PhUiRestartProcess(
_In_ HWND WindowHandle,
_In_ PPH_PROCESS_ITEM Process
);
PHAPPAPI
BOOLEAN
NTAPI
PhUiDebugProcess(
_In_ HWND WindowHandle,
_In_ PPH_PROCESS_ITEM Process
);
PHAPPAPI
BOOLEAN
NTAPI
PhUiReduceWorkingSetProcesses(
_In_ HWND WindowHandle,
_In_ CONST PPH_PROCESS_ITEM *Processes,
_In_ ULONG NumberOfProcesses
);
PHAPPAPI
BOOLEAN
NTAPI
PhUiSetEmptyWorkingSetProcesses(
_In_ HWND WindowHandle,
_In_ CONST PPH_PROCESS_ITEM* Processes,
_In_ ULONG NumberOfProcesses
);
PHAPPAPI
BOOLEAN
NTAPI
PhUiSetActivityModeration(
_In_ HWND WindowHandle,
_In_ PPH_PROCESS_ITEM Process
);
PHAPPAPI
BOOLEAN
NTAPI
PhUiSetVirtualizationProcess(
_In_ HWND WindowHandle,
_In_ PPH_PROCESS_ITEM Process,
_In_ BOOLEAN Enable
);
PHAPPAPI
BOOLEAN
NTAPI
PhUiSetCriticalProcess(
_In_ HWND WindowHandle,
_In_ PPH_PROCESS_ITEM Process
);
PHAPPAPI
BOOLEAN
NTAPI
PhUiSetEcoModeProcess(
_In_ HWND WindowHandle,
_In_ PPH_PROCESS_ITEM Process
);
PHAPPAPI
BOOLEAN
NTAPI
PhUiDetachFromDebuggerProcess(
_In_ HWND WindowHandle,
_In_ PPH_PROCESS_ITEM Process
);
PHAPPAPI
BOOLEAN
NTAPI
PhUiSetExecutionRequiredProcess(
_In_ HWND WindowHandle,
_In_ PPH_PROCESS_ITEM Process
);
PHAPPAPI
BOOLEAN
NTAPI
PhUiLoadDllProcess(
_In_ HWND WindowHandle,
_In_ PPH_PROCESS_ITEM Process
);
PHAPPAPI
BOOLEAN
NTAPI
PhUiSetIoPriorityProcesses(
_In_ HWND WindowHandle,
_In_ PPH_PROCESS_ITEM *Processes,
_In_ ULONG NumberOfProcesses,
_In_ IO_PRIORITY_HINT IoPriority
);
PHAPPAPI
BOOLEAN
NTAPI
PhUiSetPagePriorityProcess(
_In_ HWND WindowHandle,
_In_ PPH_PROCESS_ITEM Process,
_In_ ULONG PagePriority
);
PHAPPAPI
BOOLEAN
NTAPI
PhUiSetPriorityClassProcesses(
_In_ HWND WindowHandle,
_In_ PPH_PROCESS_ITEM *Processes,
_In_ ULONG NumberOfProcesses,
_In_ ULONG PriorityClass
);
PHAPPAPI
BOOLEAN
NTAPI
PhUiSetBoostPriorityProcesses(
_In_ HWND WindowHandle,
_In_ PPH_PROCESS_ITEM* Processes,
_In_ ULONG NumberOfProcesses,
_In_ BOOLEAN PriorityBoost
);
PHAPPAPI
BOOLEAN
NTAPI
PhUiSetBoostPriorityProcess(
_In_ HWND WindowHandle,
_In_ PPH_PROCESS_ITEM Process,
_In_ BOOLEAN PriorityBoost
);
PHAPPAPI
BOOLEAN
NTAPI
PhUiStartServices(
_In_ HWND WindowHandle,
_In_ PPH_SERVICE_ITEM* Services,
_In_ ULONG NumberOfServices
);
PHAPPAPI
BOOLEAN
NTAPI
PhUiStartService(
_In_ HWND WindowHandle,
_In_ PPH_SERVICE_ITEM Service
);
PHAPPAPI
BOOLEAN
NTAPI
PhUiContinueServices(
_In_ HWND WindowHandle,
_In_ PPH_SERVICE_ITEM* Services,
_In_ ULONG NumberOfServices
);
PHAPPAPI
BOOLEAN
NTAPI
PhUiContinueService(
_In_ HWND WindowHandle,
_In_ PPH_SERVICE_ITEM Service
);
PHAPPAPI
BOOLEAN
NTAPI
PhUiPauseServices(
_In_ HWND WindowHandle,
_In_ PPH_SERVICE_ITEM* Services,
_In_ ULONG NumberOfServices
);
PHAPPAPI
BOOLEAN
NTAPI
PhUiPauseService(
_In_ HWND WindowHandle,
_In_ PPH_SERVICE_ITEM Service
);
PHAPPAPI
BOOLEAN
NTAPI
PhUiStopServices(
_In_ HWND WindowHandle,
_In_ PPH_SERVICE_ITEM* Services,
_In_ ULONG NumberOfServices
);
PHAPPAPI
BOOLEAN
NTAPI
PhUiStopService(
_In_ HWND WindowHandle,
_In_ PPH_SERVICE_ITEM Service
);
PHAPPAPI
BOOLEAN
NTAPI
PhUiDeleteService(
_In_ HWND WindowHandle,
_In_ PPH_SERVICE_ITEM Service
);
PHAPPAPI
BOOLEAN
NTAPI
PhUiRestartServices(
_In_ HWND WindowHandle,
_In_ PPH_SERVICE_ITEM* Services,
_In_ ULONG NumberOfServices
);
PHAPPAPI
BOOLEAN
NTAPI
PhUiCloseConnections(
_In_ HWND WindowHandle,
_In_ PPH_NETWORK_ITEM *Connections,
_In_ ULONG NumberOfConnections
);
PHAPPAPI
BOOLEAN
NTAPI
PhUiTerminateThreads(
_In_ HWND WindowHandle,
_In_ PPH_THREAD_ITEM *Threads,
_In_ ULONG NumberOfThreads
);
PHAPPAPI
BOOLEAN
NTAPI
PhUiSuspendThreads(
_In_ HWND WindowHandle,
_In_ PPH_THREAD_ITEM *Threads,
_In_ ULONG NumberOfThreads
);
PHAPPAPI
BOOLEAN
NTAPI
PhUiResumeThreads(
_In_ HWND WindowHandle,
_In_ PPH_THREAD_ITEM *Threads,
_In_ ULONG NumberOfThreads
);
PHAPPAPI
BOOLEAN
NTAPI
PhUiSetBoostPriorityThreads(
_In_ HWND WindowHandle,
_In_ PPH_THREAD_ITEM* Threads,
_In_ ULONG NumberOfThreads,
_In_ BOOLEAN PriorityBoost
);
PHAPPAPI
BOOLEAN
NTAPI
PhUiSetBoostPriorityThread(
_In_ HWND WindowHandle,
_In_ PPH_THREAD_ITEM Thread,
_In_ BOOLEAN PriorityBoost
);
PHAPPAPI
BOOLEAN
NTAPI
PhUiSetPriorityThreads(
_In_ HWND WindowHandle,
_In_ PPH_THREAD_ITEM* Threads,
_In_ ULONG NumberOfThreads,
_In_ LONG Increment
);
PHAPPAPI
BOOLEAN
NTAPI
PhUiSetPriorityThread(
_In_ HWND WindowHandle,
_In_ PPH_THREAD_ITEM Thread,
_In_ LONG Increment
);
PHAPPAPI
BOOLEAN
NTAPI
PhUiSetIoPriorityThread(
_In_ HWND WindowHandle,
_In_ PPH_THREAD_ITEM Thread,
_In_ IO_PRIORITY_HINT IoPriority
);
PHAPPAPI
BOOLEAN
NTAPI
PhUiSetPagePriorityThread(
_In_ HWND WindowHandle,
_In_ PPH_THREAD_ITEM Thread,
_In_ ULONG PagePriority
);
PHAPPAPI
BOOLEAN
NTAPI
PhUiUnloadModule(
_In_ HWND WindowHandle,
_In_ HANDLE ProcessId,
_In_ PPH_MODULE_ITEM Module
);
PHAPPAPI
BOOLEAN
NTAPI
PhUiFreeMemory(
_In_ HWND WindowHandle,
_In_ HANDLE ProcessId,
_In_ PPH_MEMORY_ITEM MemoryItem,
_In_ BOOLEAN Free
);
PHAPPAPI
BOOLEAN
NTAPI
PhUiEmptyProcessMemoryWorkingSet(
_In_ HWND WindowHandle,
_In_ HANDLE ProcessId,
_In_ PPH_MEMORY_ITEM MemoryItem
);
PHAPPAPI
BOOLEAN
NTAPI
PhUiCloseHandles(
_In_ HWND WindowHandle,
_In_ HANDLE ProcessId,
_In_ PPH_HANDLE_ITEM *Handles,
_In_ ULONG NumberOfHandles,
_In_ BOOLEAN Warn
);
PHAPPAPI
BOOLEAN
NTAPI
PhUiSetAttributesHandle(
_In_ HWND WindowHandle,
_In_ HANDLE ProcessId,
_In_ PPH_HANDLE_ITEM Handle,
_In_ ULONG Attributes
);
PHAPPAPI
BOOLEAN
NTAPI
PhUiFlushHeapProcesses(
_In_ HWND WindowHandle,
_In_ PPH_PROCESS_ITEM* Processes,
_In_ ULONG NumberOfProcesses
);
// end_phapppub
EXTERN_C_END
#endif
================================================
FILE: SystemInformer/include/appsup.h
================================================
/*
* Copyright (c) 2022 Winsider Seminars & Solutions, Inc. All rights reserved.
*
* This file is part of System Informer.
*
* Authors:
*
* wj32 2016-2017
* dmex 2017-2023
*
*/
#ifndef PH_APPSUP_H
#define PH_APPSUP_H
DEFINE_GUID(XP_CONTEXT_GUID, 0xbeb1b341, 0x6837, 0x4c83, 0x83, 0x66, 0x2b, 0x45, 0x1e, 0x7c, 0xe6, 0x9b);
DEFINE_GUID(VISTA_CONTEXT_GUID, 0xe2011457, 0x1546, 0x43c5, 0xa5, 0xfe, 0x00, 0x8d, 0xee, 0xe3, 0xd3, 0xf0);
DEFINE_GUID(WIN7_CONTEXT_GUID, 0x35138b9a, 0x5d96, 0x4fbd, 0x8e, 0x2d, 0xa2, 0x44, 0x02, 0x25, 0xf9, 0x3a);
DEFINE_GUID(WIN8_CONTEXT_GUID, 0x4a2f28e3, 0x53b9, 0x4441, 0xba, 0x9c, 0xd6, 0x9d, 0x4a, 0x4a, 0x6e, 0x38);
DEFINE_GUID(WINBLUE_CONTEXT_GUID, 0x1f676c76, 0x80e1, 0x4239, 0x95, 0xbb, 0x83, 0xd0, 0xf6, 0xd0, 0xda, 0x78);
DEFINE_GUID(WIN10_CONTEXT_GUID, 0x8e0f7a12, 0xbfb3, 0x4fe8, 0xb9, 0xa5, 0x48, 0xfd, 0x50, 0xa1, 0x5a, 0x9a);
// begin_phapppub
PHAPPAPI
BOOLEAN
NTAPI
PhGetProcessIsSuspended(
_In_ PSYSTEM_PROCESS_INFORMATION Process
);
PHAPPAPI
BOOLEAN
NTAPI
PhIsProcessSuspended(
_In_ HANDLE ProcessId
);
PHAPPAPI
BOOLEAN
NTAPI
PhIsProcessBackground(
_In_ ULONG PriorityClass
);
PHAPPAPI
PCPH_STRINGREF
NTAPI
PhGetProcessPriorityClassString(
_In_ ULONG PriorityClass
);
// end_phapppub
PPH_STRING PhGetProcessProtectionString(
_In_ PS_PROTECTION Protection,
_In_ BOOLEAN IsSecureProcess
);
NTSTATUS PhGetProcessSwitchContext(
_In_ HANDLE ProcessHandle,
_Out_ PGUID Guid
);
NTSTATUS PhGetProcessDefaultHeap(
_In_ HANDLE ProcessHandle,
_Out_ PVOID* Heap
);
// begin_phapppub
typedef enum _PH_KNOWN_PROCESS_TYPE
{
UnknownProcessType,
SystemProcessType, // ntoskrnl/ntkrnlpa/...
SessionManagerProcessType, // smss
WindowsSubsystemProcessType, // csrss
WindowsStartupProcessType, // wininit
ServiceControlManagerProcessType, // services
LocalSecurityAuthorityProcessType, // lsass
LocalSessionManagerProcessType, // lsm
WindowsLogonProcessType, // winlogon
ServiceHostProcessType, // svchost
RunDllAsAppProcessType, // rundll32
ComSurrogateProcessType, // dllhost
TaskHostProcessType, // taskeng, taskhost, taskhostex
ExplorerProcessType, // explorer
UmdfHostProcessType, // wudfhost
NtVdmHostProcessType, // ntvdm
//EdgeProcessType, // Microsoft Edge
WmiProviderHostType,
MaximumProcessType,
KnownProcessTypeMask = 0xffff,
KnownProcessWow64 = 0x20000
} PH_KNOWN_PROCESS_TYPE;
PHAPPAPI
NTSTATUS
NTAPI
PhGetProcessKnownType(
_In_ HANDLE ProcessHandle,
_Out_ PH_KNOWN_PROCESS_TYPE *KnownProcessType
);
PHAPPAPI
PH_KNOWN_PROCESS_TYPE
NTAPI
PhGetProcessKnownTypeEx(
_In_opt_ HANDLE ProcessId,
_In_ PPH_STRING FileName
);
typedef union _PH_KNOWN_PROCESS_COMMAND_LINE
{
struct
{
PPH_STRING GroupName;
} ServiceHost;
struct
{
PPH_STRING FileName;
PPH_STRING ProcedureName;
} RunDllAsApp;
struct
{
GUID Guid;
PPH_STRING Name; // optional
PPH_STRING FileName; // optional
} ComSurrogate;
} PH_KNOWN_PROCESS_COMMAND_LINE, *PPH_KNOWN_PROCESS_COMMAND_LINE;
_Success_(return)
PHAPPAPI
BOOLEAN
NTAPI
PhaGetProcessKnownCommandLine(
_In_ PPH_STRING CommandLine,
_In_ PH_KNOWN_PROCESS_TYPE KnownProcessType,
_Out_ PPH_KNOWN_PROCESS_COMMAND_LINE KnownCommandLine
);
// end_phapppub
PPH_STRING PhEscapeStringForDelimiter(
_In_ PPH_STRING String,
_In_ WCHAR Delimiter
);
PPH_STRING PhUnescapeStringForDelimiter(
_In_ PPH_STRING String,
_In_ WCHAR Delimiter
);
// begin_phapppub
PHAPPAPI
VOID
NTAPI
PhSearchOnlineString(
_In_ HWND WindowHandle,
_In_ PCWSTR String
);
PHAPPAPI
VOID
NTAPI
PhShellExecuteUserString(
_In_ HWND WindowHandle,
_In_ PCWSTR Setting,
_In_ PCWSTR String,
_In_ BOOLEAN UseShellExecute,
_In_opt_ PCWSTR ErrorMessage
);
PHAPPAPI
VOID
NTAPI
PhLoadSymbolProviderOptions(
_Inout_ PPH_SYMBOL_PROVIDER SymbolProvider
);
PHAPPAPI
VOID
NTAPI
PhCopyListViewInfoTip(
_Inout_ LPNMLVGETINFOTIP GetInfoTip,
_In_ PPH_STRINGREF Tip
);
PHAPPAPI
VOID
NTAPI
PhCopyListView(
_In_ HWND ListViewHandle
);
PHAPPAPI
VOID
NTAPI
PhHandleListViewNotifyForCopy(
_In_ LPARAM lParam,
_In_ HWND ListViewHandle
);
#define PH_LIST_VIEW_CTRL_C_BEHAVIOR 0x1
#define PH_LIST_VIEW_CTRL_A_BEHAVIOR 0x2
#define PH_LIST_VIEW_DEFAULT_1_BEHAVIORS (PH_LIST_VIEW_CTRL_C_BEHAVIOR | PH_LIST_VIEW_CTRL_A_BEHAVIOR)
PHAPPAPI
VOID
NTAPI
PhHandleListViewNotifyBehaviors(
_In_ LPARAM lParam,
_In_ HWND ListViewHandle,
_In_ ULONG Behaviors
);
PHAPPAPI
BOOLEAN
NTAPI
PhGetListViewContextMenuPoint(
_In_ HWND ListViewHandle,
_Out_ PPOINT Point
);
// end_phapppub
VOID PhSetWindowOpacity(
_In_ HWND WindowHandle,
_In_ ULONG OpacityPercent
);
#define PH_OPACITY_TO_ID(Opacity) (ID_OPACITY_10 + (10 - (Opacity) / 10) - 1)
#define PH_ID_TO_OPACITY(Id) (100 - (((Id) - ID_OPACITY_10) + 1) * 10)
// begin_phapppub
PHAPPAPI
PPH_STRING
NTAPI
PhGetPhVersion(
VOID
);
PHAPPAPI
VOID
NTAPI
PhGetPhVersionNumbers(
_Out_opt_ PULONG MajorVersion,
_Out_opt_ PULONG MinorVersion,
_Out_opt_ PULONG BuildNumber,
_Out_opt_ PULONG RevisionNumber
);
PHAPPAPI
PPH_STRING
NTAPI
PhGetPhVersionHash(
VOID
);
typedef enum _PH_RELEASE_CHANNEL
{
PhReleaseChannel = 0,
PhPreviewChannel = 1, // unused, reserved
PhCanaryChannel = 2,
PhDeveloperChannel = 3,
PhInvalidChannel = ULONG_MAX,
} PH_RELEASE_CHANNEL, *PPH_RELEASE_CHANNEL;
PHAPPAPI
PH_RELEASE_CHANNEL
NTAPI
PhGetPhReleaseChannel(
VOID
);
PHAPPAPI
PCWSTR
NTAPI
PhGetPhReleaseChannelString(
VOID
);
PHAPPAPI
VOID
NTAPI
PhWritePhTextHeader(
_Inout_ PPH_FILE_STREAM FileStream
);
#define PH_SHELL_APP_PROPAGATE_PARAMETERS 0x1
#define PH_SHELL_APP_PROPAGATE_PARAMETERS_IGNORE_VISIBILITY 0x2
PHAPPAPI
NTSTATUS
NTAPI
PhShellProcessHacker(
_In_opt_ HWND WindowHandle,
_In_opt_ PCWSTR Parameters,
_In_ ULONG ShowWindowType,
_In_ ULONG Flags,
_In_ ULONG AppFlags,
_In_opt_ ULONG Timeout,
_Out_opt_ PHANDLE ProcessHandle
);
// end_phapppub
NTSTATUS PhShellProcessHackerEx(
_In_opt_ HWND WindowHandle,
_In_opt_ PCWSTR FileName,
_In_opt_ PCWSTR Parameters,
_In_ ULONG ShowWindowType,
_In_ ULONG Flags,
_In_ ULONG AppFlags,
_In_opt_ ULONG Timeout,
_Out_opt_ PHANDLE ProcessHandle
);
BOOLEAN PhCreateProcessIgnoreIfeoDebugger(
_In_ PCWSTR FileName,
_In_opt_ PCWSTR CommandLine
);
// begin_phapppub
typedef struct _PH_EMENU_ITEM* PPH_EMENU_ITEM;
typedef struct _PH_TN_COLUMN_MENU_DATA
{
HWND TreeNewHandle;
PPH_TREENEW_HEADER_MOUSE_EVENT MouseEvent;
ULONG DefaultSortColumn;
PH_SORT_ORDER DefaultSortOrder;
PPH_EMENU_ITEM Menu;
PPH_EMENU_ITEM Selection;
ULONG ProcessedId;
} PH_TN_COLUMN_MENU_DATA, *PPH_TN_COLUMN_MENU_DATA;
#define PH_TN_COLUMN_MENU_HIDE_COLUMN_ID ((ULONG)-1)
#define PH_TN_COLUMN_MENU_CHOOSE_COLUMNS_ID ((ULONG)-2)
#define PH_TN_COLUMN_MENU_SIZE_COLUMN_TO_FIT_ID ((ULONG)-3)
#define PH_TN_COLUMN_MENU_SIZE_ALL_COLUMNS_TO_FIT_ID ((ULONG)-4)
#define PH_TN_COLUMN_MENU_RESET_SORT_ID ((ULONG)-5)
PHAPPAPI
VOID
NTAPI
PhInitializeTreeNewColumnMenu(
_Inout_ PPH_TN_COLUMN_MENU_DATA Data
);
#define PH_TN_COLUMN_MENU_NO_VISIBILITY 0x1
#define PH_TN_COLUMN_MENU_SHOW_RESET_SORT 0x2
PHAPPAPI
VOID
NTAPI
PhInitializeTreeNewColumnMenuEx(
_Inout_ PPH_TN_COLUMN_MENU_DATA Data,
_In_ ULONG Flags
);
PHAPPAPI
BOOLEAN
NTAPI
PhHandleTreeNewColumnMenu(
_Inout_ PPH_TN_COLUMN_MENU_DATA Data
);
PHAPPAPI
VOID
NTAPI
PhDeleteTreeNewColumnMenu(
_In_ PPH_TN_COLUMN_MENU_DATA Data
);
typedef struct _PH_TN_FILTER_SUPPORT
{
PPH_LIST FilterList;
HWND TreeNewHandle;
PPH_LIST NodeList;
} PH_TN_FILTER_SUPPORT, *PPH_TN_FILTER_SUPPORT;
typedef _Function_class_(PH_TN_FILTER_FUNCTION)
BOOLEAN NTAPI PH_TN_FILTER_FUNCTION(
_In_ PPH_TREENEW_NODE Node,
_In_opt_ PVOID Context
);
typedef PH_TN_FILTER_FUNCTION* PPH_TN_FILTER_FUNCTION;
typedef struct _PH_TN_FILTER_ENTRY
{
PPH_TN_FILTER_FUNCTION Filter;
PVOID Context;
} PH_TN_FILTER_ENTRY, *PPH_TN_FILTER_ENTRY;
PHAPPAPI
VOID
NTAPI
PhInitializeTreeNewFilterSupport(
_Out_ PPH_TN_FILTER_SUPPORT Support,
_In_ HWND TreeNewHandle,
_In_ PPH_LIST NodeList
);
PHAPPAPI
VOID
NTAPI
PhDeleteTreeNewFilterSupport(
_In_ PPH_TN_FILTER_SUPPORT Support
);
PHAPPAPI
PPH_TN_FILTER_ENTRY
NTAPI
PhAddTreeNewFilter(
_In_ PPH_TN_FILTER_SUPPORT Support,
_In_ PPH_TN_FILTER_FUNCTION Filter,
_In_opt_ PVOID Context
);
PHAPPAPI
VOID
NTAPI
PhRemoveTreeNewFilter(
_In_ PPH_TN_FILTER_SUPPORT Support,
_In_ PPH_TN_FILTER_ENTRY Entry
);
PHAPPAPI
BOOLEAN
NTAPI
PhApplyTreeNewFiltersToNode(
_In_ PPH_TN_FILTER_SUPPORT Support,
_In_ PPH_TREENEW_NODE Node
);
PHAPPAPI
VOID
NTAPI
PhApplyTreeNewFilters(
_In_ PPH_TN_FILTER_SUPPORT Support
);
typedef struct _PH_COPY_CELL_CONTEXT
{
HWND TreeNewHandle;
ULONG Id; // column ID
PPH_STRING MenuItemText;
} PH_COPY_CELL_CONTEXT, *PPH_COPY_CELL_CONTEXT;
PHAPPAPI
BOOLEAN
NTAPI
PhInsertCopyCellEMenuItem(
_In_ PPH_EMENU_ITEM Menu,
_In_ ULONG InsertAfterId,
_In_ HWND TreeNewHandle,
_In_ PPH_TREENEW_COLUMN Column
);
PHAPPAPI
BOOLEAN
NTAPI
PhHandleCopyCellEMenuItem(
_In_ PPH_EMENU_ITEM SelectedItem
);
typedef struct _PH_COPY_ITEM_CONTEXT
{
HWND ListViewHandle;
ULONG Id;
ULONG SubId;
PPH_STRING MenuItemText;
} PH_COPY_ITEM_CONTEXT, *PPH_COPY_ITEM_CONTEXT;
PHAPPAPI
BOOLEAN
NTAPI
PhInsertCopyListViewEMenuItem(
_In_ PPH_EMENU_ITEM Menu,
_In_ ULONG InsertAfterId,
_In_ HWND ListViewHandle
);
PHAPPAPI
BOOLEAN
NTAPI
PhHandleCopyListViewEMenuItem(
_In_ PPH_EMENU_ITEM SelectedItem
);
PHAPPAPI
VOID
NTAPI
PhShellOpenKey(
_In_ HWND WindowHandle,
_In_ PPH_STRING KeyName
);
PHAPPAPI
BOOLEAN
NTAPI
PhShellOpenKey2(
_In_ HWND WindowHandle,
_In_ PPH_STRING KeyName
);
// end_phapppub
PPH_STRING PhPcre2GetErrorMessage(
_In_ LONG ErrorCode
);
// begin_phapppub
PHAPPAPI
HBITMAP
NTAPI
PhGetShieldBitmap(
_In_ LONG WindowDpi,
_In_opt_ LONG Width,
_In_opt_ LONG Height
);
PHAPPAPI
HICON
NTAPI
PhGetApplicationIcon(
_In_ BOOLEAN SmallIcon
);
PHAPPAPI
HICON
NTAPI
PhGetApplicationIconEx(
_In_ BOOLEAN SmallIcon,
_In_opt_ LONG WindowDpi
);
PHAPPAPI
VOID
NTAPI
PhSetApplicationWindowIcon(
_In_ HWND WindowHandle
);
PHAPPAPI
VOID
NTAPI
PhSetApplicationWindowIconEx(
_In_ HWND WindowHandle,
_In_opt_ LONG WindowDpi
);
PHAPPAPI
VOID
NTAPI
PhSetWindowIcon(
_In_ HWND WindowHandle,
_In_opt_ HICON SmallIcon,
_In_opt_ HICON LargeIcon,
_In_ BOOLEAN CleanupIcon
);
PHAPPAPI
VOID
NTAPI
PhDestroyWindowIcon(
_In_ HWND WindowHandle
);
PHAPPAPI
VOID
NTAPI
PhSetStaticWindowIcon(
_In_ HWND WindowHandle,
_In_opt_ LONG WindowDpi
);
PHAPPAPI
VOID
NTAPI
PhDeleteStaticWindowIcon(
_In_ HWND WindowHandle
);
// end_phapppub
#define SI_RUNAS_ADMIN_TASK_NAME ((PH_STRINGREF)PH_STRINGREF_INIT(L"SystemInformerTaskAdmin"))
HRESULT PhRunAsAdminTask(
_In_ PPH_STRINGREF TaskName
);
HRESULT PhDeleteAdminTask(
_In_ PPH_STRINGREF TaskName
);
HRESULT PhCreateAdminTask(
_In_ PPH_STRINGREF TaskName,
_In_ PPH_STRINGREF FileName
);
NTSTATUS PhRunAsAdminTaskUIAccess(
VOID
);
// begin_phapppub
PHAPPAPI
BOOLEAN
NTAPI
PhWordMatchStringRef(
_In_ PPH_STRINGREF SearchText,
_In_ PPH_STRINGREF Text
);
FORCEINLINE
BOOLEAN
NTAPI
PhWordMatchStringZ(
_In_ PPH_STRING SearchText,
_In_ PCWSTR Text
)
{
PH_STRINGREF text;
PhInitializeStringRef(&text, Text);
return PhWordMatchStringRef(&SearchText->sr, &text);
}
FORCEINLINE
BOOLEAN
NTAPI
PhWordMatchStringLongHintZ(
_In_ PPH_STRING SearchText,
_In_ PCWSTR Text
)
{
PH_STRINGREF text;
PhInitializeStringRefLongHint(&text, Text);
return PhWordMatchStringRef(&SearchText->sr, &text);
}
PHAPPAPI
PVOID
NTAPI
PhCreateKsiSettingsBlob( // ksisup.c
VOID
);
PHAPPAPI
NTSTATUS
NTAPI
PhQueryKphCounters( // ksisup.c
_Out_ PULONG64 Duration,
_Out_ PULONG64 DurationDown,
_Out_ PULONG64 DurationUp
);
// end_phapppub
#define PH_LOAD_SHARED_ICON_SMALL(BaseAddress, Name, dpiValue) PhLoadIcon(BaseAddress, (Name), PH_LOAD_ICON_SHARED | PH_LOAD_ICON_SIZE_SMALL, 0, 0, dpiValue) // phapppub
#define PH_LOAD_SHARED_ICON_LARGE(BaseAddress, Name, dpiValue) PhLoadIcon(BaseAddress, (Name), PH_LOAD_ICON_SHARED | PH_LOAD_ICON_SIZE_LARGE, 0, 0, dpiValue) // phapppub
FORCEINLINE PVOID PhpGenericPropertyPageHeader(
_In_ HWND hwndDlg,
_In_ UINT uMsg,
_In_ WPARAM wParam,
_In_ LPARAM lParam,
_In_ ULONG ContextHash
)
{
PVOID context;
switch (uMsg)
{
case WM_INITDIALOG:
{
LPPROPSHEETPAGE propSheetPage = (LPPROPSHEETPAGE)lParam;
context = (PVOID)propSheetPage->lParam;
PhSetWindowContext(hwndDlg, ContextHash, context);
}
break;
case WM_NCDESTROY:
{
context = PhGetWindowContext(hwndDlg, ContextHash);
PhRemoveWindowContext(hwndDlg, ContextHash);
}
break;
default:
{
context = PhGetWindowContext(hwndDlg, ContextHash);
}
break;
}
return context;
}
#define SWP_NO_ACTIVATE_MOVE_SIZE_ZORDER (SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER)
#define SWP_SHOWWINDOW_ONLY (SWP_NO_ACTIVATE_MOVE_SIZE_ZORDER | SWP_SHOWWINDOW)
#define SWP_HIDEWINDOW_ONLY (SWP_NO_ACTIVATE_MOVE_SIZE_ZORDER | SWP_HIDEWINDOW)
#endif
================================================
FILE: SystemInformer/include/colmgr.h
================================================
/*
* Copyright (c) 2022 Winsider Seminars & Solutions, Inc. All rights reserved.
*
* This file is part of System Informer.
*
* Authors:
*
* wj32 2011-2015
* dmex 2023-2024
*
*/
#ifndef PH_COLMGR_H
#define PH_COLMGR_H
#define PH_CM_ORDER_LIMIT 160
// begin_phapppub
typedef _Function_class_(PH_CM_POST_SORT_FUNCTION)
LONG NTAPI PH_CM_POST_SORT_FUNCTION(
_In_ LONG Result,
_In_ PVOID Node1,
_In_ PVOID Node2,
_In_ PH_SORT_ORDER SortOrder
);
typedef PH_CM_POST_SORT_FUNCTION* PPH_CM_POST_SORT_FUNCTION;
// end_phapppub
typedef struct _PH_CM_MANAGER
{
HWND Handle;
ULONG MinId;
ULONG NextId;
PPH_CM_POST_SORT_FUNCTION PostSortFunction;
LIST_ENTRY ColumnListHead;
PPH_LIST NotifyList;
} PH_CM_MANAGER, *PPH_CM_MANAGER;
typedef struct _PH_PLUGIN PH_PLUGIN, *PPH_PLUGIN;
typedef struct _PH_CM_COLUMN
{
LIST_ENTRY ListEntry;
ULONG Id;
PPH_PLUGIN Plugin;
ULONG SubId;
PVOID Context;
PVOID SortFunction;
} PH_CM_COLUMN, *PPH_CM_COLUMN;
VOID PhCmInitializeManager(
_Out_ PPH_CM_MANAGER Manager,
_In_ HWND Handle,
_In_ ULONG MinId,
_In_ PPH_CM_POST_SORT_FUNCTION PostSortFunction
);
VOID PhCmDeleteManager(
_In_ PPH_CM_MANAGER Manager
);
PPH_CM_COLUMN PhCmCreateColumn(
_Inout_ PPH_CM_MANAGER Manager,
_In_ PPH_TREENEW_COLUMN Column,
_In_ PPH_PLUGIN Plugin,
_In_ ULONG SubId,
_In_opt_ PVOID Context,
_In_opt_ PVOID SortFunction
);
PPH_CM_COLUMN PhCmFindColumn(
_In_ PPH_CM_MANAGER Manager,
_In_ PCPH_STRINGREF PluginName,
_In_ ULONG SubId
);
VOID PhCmSetNotifyPlugin(
_In_ PPH_CM_MANAGER Manager,
_In_ PPH_PLUGIN Plugin
);
BOOLEAN PhCmForwardMessage(
_In_ HWND WindowHandle,
_In_ PH_TREENEW_MESSAGE Message,
_In_ PVOID Parameter1,
_In_ PVOID Parameter2,
_In_ PPH_CM_MANAGER Manager
);
BOOLEAN PhCmForwardSort(
_In_ PPH_TREENEW_NODE *Nodes,
_In_ ULONG NumberOfNodes,
_In_ ULONG SortColumn,
_In_ PH_SORT_ORDER SortOrder,
_In_ PPH_CM_MANAGER Manager
);
// begin_phapppub
PHAPPAPI
BOOLEAN
NTAPI
PhCmLoadSettings(
_In_ HWND TreeNewHandle,
_In_ PCPH_STRINGREF Settings
);
// end_phapppub
#define PH_CM_COLUMN_WIDTHS_ONLY 0x1
BOOLEAN PhCmLoadSettingsEx(
_In_ HWND TreeNewHandle,
_In_opt_ PPH_CM_MANAGER Manager,
_In_ ULONG Flags,
_In_ PCPH_STRINGREF Settings,
_In_opt_ PCPH_STRINGREF SortSettings
);
// begin_phapppub
PHAPPAPI
PPH_STRING
NTAPI
PhCmSaveSettings(
_In_ HWND TreeNewHandle
);
// end_phapppub
PPH_STRING PhCmSaveSettingsEx(
_In_ HWND TreeNewHandle,
_In_opt_ PPH_CM_MANAGER Manager,
_In_ ULONG Flags,
_Out_opt_ PPH_STRING *SortSettings
);
#endif
================================================
FILE: SystemInformer/include/colsetmgr.h
================================================
#ifndef PH_COLSETMGR_H
#define PH_COLSETMGR_H
typedef struct _PH_COLUMN_SET_ENTRY
{
PPH_STRING Name;
PPH_STRING Setting;
PPH_STRING Sorting;
} PH_COLUMN_SET_ENTRY, *PPH_COLUMN_SET_ENTRY;
PPH_LIST PhInitializeColumnSetList(
_In_ PCWSTR SettingName
);
VOID PhDeleteColumnSetList(
_In_ PPH_LIST ColumnSetList
);
_Success_(return)
BOOLEAN PhLoadSettingsColumnSet(
_In_ PCWSTR SettingName,
_In_ PPH_STRING ColumnSetName,
_Out_ PPH_STRING *TreeListSettings,
_Out_ PPH_STRING *TreeSortSettings
);
VOID PhSaveSettingsColumnSet(
_In_ PCWSTR SettingName,
_In_ PPH_STRING ColumnSetName,
_In_ PPH_STRING TreeListSettings,
_In_ PPH_STRING TreeSortSettings
);
// Column Set Editor Dialog
VOID PhShowColumnSetEditorDialog(
_In_ HWND ParentWindowHandle,
_In_ PCWSTR SettingName
);
#endif
================================================
FILE: SystemInformer/include/devprv.h
================================================
/*
* Copyright (c) 2022 Winsider Seminars & Solutions, Inc. All rights reserved.
*
* This file is part of System Informer.
*
* Authors:
*
* jxy-s 2022-2023
*
*/
#ifndef _DEVICEPRV_H_
#define _DEVICEPRV_H_
#include
// begin_phapppub
extern PPH_OBJECT_TYPE PhDeviceTreeType;
extern PPH_OBJECT_TYPE PhDeviceItemType;
extern PPH_OBJECT_TYPE PhDeviceNotifyType;
typedef enum _PH_DEVICE_PROPERTY_CLASS
{
PhDevicePropertyName,
PhDevicePropertyManufacturer,
PhDevicePropertyService,
PhDevicePropertyClass,
PhDevicePropertyEnumeratorName,
PhDevicePropertyInstallDate,
PhDevicePropertyFirstInstallDate,
PhDevicePropertyLastArrivalDate,
PhDevicePropertyLastRemovalDate,
PhDevicePropertyDeviceDesc,
PhDevicePropertyFriendlyName,
PhDevicePropertyInstanceId,
PhDevicePropertyParentInstanceId,
PhDevicePropertyPDOName,
PhDevicePropertyLocationInfo,
PhDevicePropertyClassGuid,
PhDevicePropertyDriver,
PhDevicePropertyDriverVersion,
PhDevicePropertyDriverDate,
PhDevicePropertyFirmwareDate,
PhDevicePropertyFirmwareVersion,
PhDevicePropertyFirmwareRevision,
PhDevicePropertyHasProblem,
PhDevicePropertyProblemCode,
PhDevicePropertyProblemStatus,
PhDevicePropertyDevNodeStatus,
PhDevicePropertyDevCapabilities,
PhDevicePropertyUpperFilters,
PhDevicePropertyLowerFilters,
PhDevicePropertyHardwareIds,
PhDevicePropertyCompatibleIds,
PhDevicePropertyConfigFlags,
PhDevicePropertyUINumber,
PhDevicePropertyBusTypeGuid,
PhDevicePropertyLegacyBusType,
PhDevicePropertyBusNumber,
PhDevicePropertySecurity,
PhDevicePropertySecuritySDS,
PhDevicePropertyDevType,
PhDevicePropertyExclusive,
PhDevicePropertyCharacteristics,
PhDevicePropertyAddress,
PhDevicePropertyPowerData,
PhDevicePropertyRemovalPolicy,
PhDevicePropertyRemovalPolicyDefault,
PhDevicePropertyRemovalPolicyOverride,
PhDevicePropertyInstallState,
PhDevicePropertyLocationPaths,
PhDevicePropertyBaseContainerId,
PhDevicePropertyEjectionRelations,
PhDevicePropertyRemovalRelations,
PhDevicePropertyPowerRelations,
PhDevicePropertyBusRelations,
PhDevicePropertyChildren,
PhDevicePropertySiblings,
PhDevicePropertyTransportRelations,
PhDevicePropertyReported,
PhDevicePropertyLegacy,
PhDevicePropertyContainerId,
PhDevicePropertyInLocalMachineContainer,
PhDevicePropertyModel,
PhDevicePropertyModelId,
PhDevicePropertyFriendlyNameAttributes,
PhDevicePropertyManufacturerAttributes,
PhDevicePropertyPresenceNotForDevice,
PhDevicePropertySignalStrength,
PhDevicePropertyIsAssociateableByUserAction,
PhDevicePropertyShowInUninstallUI,
PhDevicePropertyNumaProximityDomain,
PhDevicePropertyDHPRebalancePolicy,
PhDevicePropertyNumaNode,
PhDevicePropertyBusReportedDeviceDesc,
PhDevicePropertyIsPresent,
PhDevicePropertyConfigurationId,
PhDevicePropertyReportedDeviceIdsHash,
PhDevicePropertyPhysicalDeviceLocation,
PhDevicePropertyBiosDeviceName,
PhDevicePropertyDriverProblemDesc,
PhDevicePropertyDebuggerSafe,
PhDevicePropertyPostInstallInProgress,
PhDevicePropertyStack,
PhDevicePropertyExtendedConfigurationIds,
PhDevicePropertyIsRebootRequired,
PhDevicePropertyDependencyProviders,
PhDevicePropertyDependencyDependents,
PhDevicePropertySoftRestartSupported,
PhDevicePropertyExtendedAddress,
PhDevicePropertyAssignedToGuest,
PhDevicePropertyCreatorProcessId,
PhDevicePropertyFirmwareVendor,
PhDevicePropertySessionId,
PhDevicePropertyDriverDesc,
PhDevicePropertyDriverInfPath,
PhDevicePropertyDriverInfSection,
PhDevicePropertyDriverInfSectionExt,
PhDevicePropertyMatchingDeviceId,
PhDevicePropertyDriverProvider,
PhDevicePropertyDriverPropPageProvider,
PhDevicePropertyDriverCoInstallers,
PhDevicePropertyResourcePickerTags,
PhDevicePropertyResourcePickerExceptions,
PhDevicePropertyDriverRank,
PhDevicePropertyDriverLogoLevel,
PhDevicePropertyNoConnectSound,
PhDevicePropertyGenericDriverInstalled,
PhDevicePropertyAdditionalSoftwareRequested,
PhDevicePropertySafeRemovalRequired,
PhDevicePropertySafeRemovalRequiredOverride,
PhDevicePropertyPkgModel,
PhDevicePropertyPkgVendorWebSite,
PhDevicePropertyPkgDetailedDescription,
PhDevicePropertyPkgDocumentationLink,
PhDevicePropertyPkgIcon,
PhDevicePropertyPkgBrandingIcon,
PhDevicePropertyClassUpperFilters,
PhDevicePropertyClassLowerFilters,
PhDevicePropertyClassSecurity,
PhDevicePropertyClassSecuritySDS,
PhDevicePropertyClassDevType,
PhDevicePropertyClassExclusive,
PhDevicePropertyClassCharacteristics,
PhDevicePropertyClassName,
PhDevicePropertyClassClassName,
PhDevicePropertyClassIcon,
PhDevicePropertyClassClassInstaller,
PhDevicePropertyClassPropPageProvider,
PhDevicePropertyClassNoInstallClass,
PhDevicePropertyClassNoDisplayClass,
PhDevicePropertyClassSilentInstall,
PhDevicePropertyClassNoUseClass,
PhDevicePropertyClassDefaultService,
PhDevicePropertyClassIconPath,
PhDevicePropertyClassDHPRebalanceOptOut,
PhDevicePropertyClassClassCoInstallers,
PhDevicePropertyInterfaceFriendlyName,
PhDevicePropertyInterfaceEnabled,
PhDevicePropertyInterfaceClassGuid,
PhDevicePropertyInterfaceReferenceString,
PhDevicePropertyInterfaceRestricted,
PhDevicePropertyInterfaceUnrestrictedAppCapabilities,
PhDevicePropertyInterfaceSchematicName,
PhDevicePropertyInterfaceClassDefaultInterface,
PhDevicePropertyInterfaceClassName,
PhDevicePropertyContainerAddress,
PhDevicePropertyContainerDiscoveryMethod,
PhDevicePropertyContainerIsEncrypted,
PhDevicePropertyContainerIsAuthenticated,
PhDevicePropertyContainerIsConnected,
PhDevicePropertyContainerIsPaired,
PhDevicePropertyContainerIcon,
PhDevicePropertyContainerVersion,
PhDevicePropertyContainerLastSeen,
PhDevicePropertyContainerLastConnected,
PhDevicePropertyContainerIsShowInDisconnectedState,
PhDevicePropertyContainerIsLocalMachine,
PhDevicePropertyContainerMetadataPath,
PhDevicePropertyContainerIsMetadataSearchInProgress,
PhDevicePropertyContainerIsMetadataChecksum,
PhDevicePropertyContainerIsNotInterestingForDisplay,
PhDevicePropertyContainerLaunchDeviceStageOnDeviceConnect,
PhDevicePropertyContainerLaunchDeviceStageFromExplorer,
PhDevicePropertyContainerBaselineExperienceId,
PhDevicePropertyContainerIsDeviceUniquelyIdentifiable,
PhDevicePropertyContainerAssociationArray,
PhDevicePropertyContainerDeviceDescription1,
PhDevicePropertyContainerDeviceDescription2,
PhDevicePropertyContainerHasProblem,
PhDevicePropertyContainerIsSharedDevice,
PhDevicePropertyContainerIsNetworkDevice,
PhDevicePropertyContainerIsDefaultDevice,
PhDevicePropertyContainerMetadataCabinet,
PhDevicePropertyContainerRequiresPairingElevation,
PhDevicePropertyContainerExperienceId,
PhDevicePropertyContainerCategory,
PhDevicePropertyContainerCategoryDescSingular,
PhDevicePropertyContainerCategoryDescPlural,
PhDevicePropertyContainerCategoryIcon,
PhDevicePropertyContainerCategoryGroupDesc,
PhDevicePropertyContainerCategoryGroupIcon,
PhDevicePropertyContainerPrimaryCategory,
PhDevicePropertyContainerUnpairUninstall,
PhDevicePropertyContainerRequiresUninstallElevation,
PhDevicePropertyContainerDeviceFunctionSubRank,
PhDevicePropertyContainerAlwaysShowDeviceAsConnected,
PhDevicePropertyContainerConfigFlags,
PhDevicePropertyContainerPrivilegedPackageFamilyNames,
PhDevicePropertyContainerCustomPrivilegedPackageFamilyNames,
PhDevicePropertyContainerIsRebootRequired,
PhDevicePropertyContainerFriendlyName,
PhDevicePropertyContainerManufacturer,
PhDevicePropertyContainerModelName,
PhDevicePropertyContainerModelNumber,
PhDevicePropertyContainerInstallInProgress,
PhDevicePropertyObjectType,
PhDevicePropertyPciDeviceType,
PhDevicePropertyPciCurrentSpeedAndMode,
PhDevicePropertyPciBaseClass,
PhDevicePropertyPciSubClass,
PhDevicePropertyPciProgIf,
PhDevicePropertyPciCurrentPayloadSize,
PhDevicePropertyPciMaxPayloadSize,
PhDevicePropertyPciMaxReadRequestSize,
PhDevicePropertyPciCurrentLinkSpeed,
PhDevicePropertyPciCurrentLinkWidth,
PhDevicePropertyPciMaxLinkSpeed,
PhDevicePropertyPciMaxLinkWidth,
PhDevicePropertyPciExpressSpecVersion,
PhDevicePropertyPciInterruptSupport,
PhDevicePropertyPciInterruptMessageMaximum,
PhDevicePropertyPciBarTypes,
PhDevicePropertyPciSriovSupport,
PhDevicePropertyPciLabel_Id,
PhDevicePropertyPciLabel_String,
PhDevicePropertyPciSerialNumber,
PhDevicePropertyPciExpressCapabilityControl,
PhDevicePropertyPciNativeExpressControl,
PhDevicePropertyPciSystemMsiSupport,
PhDevicePropertyStoragePortable,
PhDevicePropertyStorageRemovableMedia,
PhDevicePropertyStorageSystemCritical,
PhDevicePropertyStorageDiskNumber,
PhDevicePropertyStoragePartitionNumber,
PhDevicePropertyGpuLuid,
PhDevicePropertyGpuPhysicalAdapterIndex,
PhMaxDeviceProperty
} PH_DEVICE_PROPERTY_CLASS, *PPH_DEVICE_PROPERTY_CLASS;
typedef enum _PH_DEVICE_PROPERTY_TYPE
{
PhDevicePropertyTypeString,
PhDevicePropertyTypeUInt64,
PhDevicePropertyTypeInt64,
PhDevicePropertyTypeUInt32,
PhDevicePropertyTypeInt32,
PhDevicePropertyTypeNTSTATUS,
PhDevicePropertyTypeGUID,
PhDevicePropertyTypeBoolean,
PhDevicePropertyTypeTimeStamp,
PhDevicePropertyTypeStringList,
PhDevicePropertyTypeBinary,
PhMaxDevicePropertyType
} PH_DEVICE_PROPERTY_TYPE, PPH_DEVICE_PROPERTY_TYPE;
C_ASSERT(PhMaxDevicePropertyType <= MAXSHORT);
typedef struct _PH_DEVICE_PROPERTY
{
union
{
struct
{
ULONG Type : 16; // PH_DEVICE_PROPERTY_TYPE
ULONG Spare : 14;
ULONG Initialized : 1;
ULONG Valid : 1;
};
ULONG State;
};
union
{
PPH_STRING String;
ULONG64 UInt64;
LONG64 Int64;
ULONG UInt32;
LONG Int32;
NTSTATUS Status;
GUID Guid;
BOOLEAN Boolean;
LARGE_INTEGER TimeStamp;
PPH_LIST StringList;
struct
{
ULONG Size;
PBYTE Buffer;
} Binary;
};
PPH_STRING AsString;
} PH_DEVICE_PROPERTY, *PPH_DEVICE_PROPERTY;
// end_phapppub
typedef struct _PH_DEVINFO
{
HDEVINFO Handle;
} PH_DEVINFO, *PPH_DEVINFO;
typedef struct _PH_DEVINFO_DATA
{
BOOLEAN Interface;
union
{
SP_DEVINFO_DATA DeviceData;
SP_DEVICE_INTERFACE_DATA InterfaceData;
};
} PH_DEVINFO_DATA, *PPH_DEVINFO_DATA;
// begin_phapppub
typedef struct _PH_DEVICE_ITEM
{
struct _PH_DEVICE_TREE* Tree;
struct _PH_DEVICE_ITEM* Parent;
struct _PH_DEVICE_ITEM* Sibling;
struct _PH_DEVICE_ITEM* Child;
GUID ClassGuid;
ULONG InstanceIdHash;
PPH_STRING InstanceId;
PPH_STRING ParentInstanceId;
ULONG ProblemCode;
ULONG DevNodeStatus;
ULONG Capabilities;
ULONG ChildrenCount;
ULONG InterfaceCount;
union
{
struct
{
ULONG HasUpperFilters : 1;
ULONG HasLowerFilters : 1;
ULONG DeviceInterface : 1;
ULONG Spare : 29;
};
ULONG Flags;
};
PH_DEVICE_PROPERTY Properties[PhMaxDeviceProperty];
// end_phapppub
PPH_DEVINFO DeviceInfo;
PH_DEVINFO_DATA DeviceInfoData;
// begin_phapppub
} PH_DEVICE_ITEM, *PPH_DEVICE_ITEM;
typedef struct _PH_DEVICE_TREE
{
PPH_DEVICE_ITEM Root;
PPH_LIST DeviceList;
PPH_LIST DeviceInterfaceList;
// end_phapppub
PPH_DEVINFO DeviceInfo;
// begin_phapppub
} PH_DEVICE_TREE, *PPH_DEVICE_TREE;
_Function_class_(PH_DEVICE_ENUM_RESOURCES_CALLBACK)
typedef
BOOLEAN
NTAPI
PH_DEVICE_ENUM_RESOURCES_CALLBACK(
_In_ ULONG LogicalConfig,
_In_ ULONG ResourceId,
_In_ PVOID Buffer,
_In_ ULONG Length,
_In_opt_ PVOID Context
);
typedef PH_DEVICE_ENUM_RESOURCES_CALLBACK* PPH_DEVICE_ENUM_RESOURCES_CALLBACK;
PHAPPAPI
BOOLEAN
NTAPI
PhEnumDeviceResources(
_In_ PPH_DEVICE_ITEM Item,
_In_ ULONG LogicalConfig,
_In_ PPH_DEVICE_ENUM_RESOURCES_CALLBACK Callback,
_In_opt_ PVOID Context
);
PHAPPAPI
PPH_DEVICE_PROPERTY
NTAPI
PhGetDeviceProperty(
_In_ PPH_DEVICE_ITEM Item,
_In_ PH_DEVICE_PROPERTY_CLASS Class
);
PHAPPAPI
BOOLEAN
NTAPI
PhLookupDevicePropertyClass(
_In_ const DEVPROPKEY* Key,
_Out_ PPH_DEVICE_PROPERTY_CLASS Class
);
PHAPPAPI
HICON
NTAPI
PhGetDeviceIcon(
_In_ PPH_DEVICE_ITEM Item,
_In_ PPH_INTEGER_PAIR IconSize
);
PHAPPAPI
PPH_DEVICE_TREE
NTAPI
PhReferenceDeviceTree(
VOID
);
PHAPPAPI
PPH_DEVICE_TREE
NTAPI
PhReferenceDeviceTreeEx(
_In_ BOOLEAN ForceRefresh
);
PHAPPAPI
_Success_(return != NULL)
_Must_inspect_result_
PPH_DEVICE_ITEM
NTAPI
PhLookupDeviceItemByHash(
_In_ PPH_DEVICE_TREE Tree,
_In_ ULONG InstanceIdHash
);
PHAPPAPI
_Success_(return != NULL)
_Must_inspect_result_
PPH_DEVICE_ITEM
NTAPI
PhLookupDeviceItem(
_In_ PPH_DEVICE_TREE Tree,
_In_ PPH_STRINGREF InstanceId
);
PHAPPAPI
_Success_(return != NULL)
_Must_inspect_result_
PPH_DEVICE_ITEM
NTAPI
PhReferenceDeviceItemByHash(
_In_ PPH_DEVICE_TREE Tree,
_In_ ULONG InstanceIdHash
);
PHAPPAPI
_Success_(return != NULL)
_Must_inspect_result_
PPH_DEVICE_ITEM
NTAPI
PhReferenceDeviceItem(
_In_ PPH_DEVICE_TREE Tree,
_In_ PPH_STRINGREF InstanceId
);
PHAPPAPI
_Success_(return != NULL)
_Must_inspect_result_
PPH_DEVICE_ITEM
NTAPI
PhReferenceDeviceItem2(
_In_ PPH_STRINGREF InstanceId
);
typedef enum _PH_DEVICE_NOTIFY_ACTION
{
PhDeviceNotifyInterfaceArrival,
PhDeviceNotifyInterfaceRemoval,
PhDeviceNotifyInstanceEnumerated,
PhDeviceNotifyInstanceStarted,
PhDeviceNotifyInstanceRemoved,
} PH_DEVICE_NOTIFY_ACTION, *PPH_DEVICE_NOTIFY_ACTION;
typedef struct _PH_DEVICE_NOTIFY
{
PH_DEVICE_NOTIFY_ACTION Action;
union
{
struct
{
GUID ClassGuid;
} DeviceInterface; // PhDeviceNotifyInterface...
struct
{
PPH_STRING InstanceId;
} DeviceInstance; // PhDeviceNotifyInstance...
};
// end_phapppub
SLIST_ENTRY ListEntry;
// begin_phapppub
} PH_DEVICE_NOTIFY, *PPH_DEVICE_NOTIFY;
PHAPPAPI
BOOLEAN
NTAPI
PhDeviceProviderInitialization(
VOID
);
// end_phapppub
#endif
================================================
FILE: SystemInformer/include/extmgr.h
================================================
/*
* Copyright (c) 2022 Winsider Seminars & Solutions, Inc. All rights reserved.
*
* This file is part of System Informer.
*
* Authors:
*
* wj32 2011-2016
* dmex 2017-2022
*
*/
#ifndef PH_EXTMGR_H
#define PH_EXTMGR_H
// begin_phapppub
typedef enum _PH_EM_OBJECT_TYPE
{
EmProcessItemType,
EmProcessNodeType,
EmServiceItemType,
EmServiceNodeType,
EmNetworkItemType,
EmNetworkNodeType,
EmThreadItemType,
EmThreadNodeType,
EmModuleItemType,
EmModuleNodeType,
EmHandleItemType,
EmHandleNodeType,
EmThreadsContextType,
EmModulesContextType,
EmHandlesContextType,
EmThreadProviderType,
EmModuleProviderType,
EmHandleProviderType,
EmMemoryNodeType,
EmMemoryContextType,
EmMaximumObjectType
} PH_EM_OBJECT_TYPE;
typedef enum _PH_EM_OBJECT_OPERATION
{
EmObjectCreate,
EmObjectDelete,
EmMaximumObjectOperation
} PH_EM_OBJECT_OPERATION;
typedef VOID (NTAPI *PPH_EM_OBJECT_CALLBACK)(
_In_ PVOID Object,
_In_ PH_EM_OBJECT_TYPE ObjectType,
_In_ PVOID Extension
);
// end_phapppub
typedef struct _PH_EM_APP_CONTEXT
{
LIST_ENTRY ListEntry;
PH_STRINGREF AppName;
struct _PH_EM_OBJECT_EXTENSION *Extensions[EmMaximumObjectType];
} PH_EM_APP_CONTEXT, *PPH_EM_APP_CONTEXT;
#endif
================================================
FILE: SystemInformer/include/extmgri.h
================================================
/*
* Copyright (c) 2022 Winsider Seminars & Solutions, Inc. All rights reserved.
*
* This file is part of System Informer.
*
* Authors:
*
* wj32 2011-2013
* dmex 2017-2022
*
*/
#ifndef PH_EXTMGRI_H
#define PH_EXTMGRI_H
#include
typedef struct _PH_EM_OBJECT_TYPE_STATE
{
SIZE_T InitialSize;
SIZE_T ExtensionOffset;
LIST_ENTRY ExtensionListHead;
} PH_EM_OBJECT_TYPE_STATE, *PPH_EM_OBJECT_TYPE_STATE;
typedef struct _PH_EM_OBJECT_EXTENSION
{
LIST_ENTRY ListEntry;
SIZE_T ExtensionSize;
SIZE_T ExtensionOffset;
PPH_EM_OBJECT_CALLBACK Callbacks[EmMaximumObjectOperation];
} PH_EM_OBJECT_EXTENSION, *PPH_EM_OBJECT_EXTENSION;
VOID PhEmInitialization(
VOID
);
VOID PhEmInitializeAppContext(
_Out_ PPH_EM_APP_CONTEXT AppContext,
_In_ PCPH_STRINGREF AppName
);
VOID PhEmSetObjectExtension(
_Inout_ PPH_EM_APP_CONTEXT AppContext,
_In_ PH_EM_OBJECT_TYPE ObjectType,
_In_ SIZE_T ExtensionSize,
_In_opt_ PPH_EM_OBJECT_CALLBACK CreateCallback,
_In_opt_ PPH_EM_OBJECT_CALLBACK DeleteCallback
);
PVOID PhEmGetObject(
_In_ PPH_EM_APP_CONTEXT AppContext,
_In_ PH_EM_OBJECT_TYPE ObjectType,
_In_ PVOID Extension
);
PVOID PhEmGetObjectExtension(
_In_ PPH_EM_APP_CONTEXT AppContext,
_In_ PH_EM_OBJECT_TYPE ObjectType,
_In_ PVOID Object
);
SIZE_T PhEmGetObjectSize(
_In_ PH_EM_OBJECT_TYPE ObjectType,
_In_ SIZE_T InitialSize
);
VOID PhEmCallObjectOperation(
_In_ PH_EM_OBJECT_TYPE ObjectType,
_In_ PVOID Object,
_In_ PH_EM_OBJECT_OPERATION Operation
);
_Success_(return)
BOOLEAN PhEmParseCompoundId(
_In_ PCPH_STRINGREF CompoundId,
_Out_ PPH_STRINGREF AppName,
_Out_ PULONG SubId
);
#endif
================================================
FILE: SystemInformer/include/heapstruct.h
================================================
/*
* Copyright (c) 2022 Winsider Seminars & Solutions, Inc. All rights reserved.
*
* This file is part of System Informer.
*
* Authors:
*
* wj32 2015-2016
* dmex 2017-2022
*
*/
#ifndef PH_HEAPSTRUCT_H
#define PH_HEAPSTRUCT_H
// Not the actual structure, but has the same size.
typedef struct _HEAP_ENTRY
{
PVOID Data1;
PVOID Data2;
} HEAP_ENTRY, *PHEAP_ENTRY;
#define HEAP_SEGMENT_SIGNATURE 0xffeeffee
// Windows 7 and above
typedef struct _HEAP_SEGMENT
{
HEAP_ENTRY Entry;
ULONG SegmentSignature;
ULONG SegmentFlags;
LIST_ENTRY SegmentListEntry;
struct _HEAP *Heap;
PVOID BaseAddress;
ULONG NumberOfPages;
PHEAP_ENTRY FirstEntry;
PHEAP_ENTRY LastValidEntry;
ULONG NumberOfUnCommittedPages;
ULONG NumberOfUnCommittedRanges;
USHORT SegmentAllocatorBackTraceIndex;
USHORT Reserved;
LIST_ENTRY UCRSegmentList;
} HEAP_SEGMENT, *PHEAP_SEGMENT;
#define HEAP_SIGNATURE 0xeeffeeff
// Windows 7
typedef struct _HEAP_OLD
{
HEAP_SEGMENT Segment;
ULONG Flags;
ULONG ForceFlags;
ULONG CompatibilityFlags;
ULONG EncodeFlagMask;
HEAP_ENTRY Encoding;
ULONG_PTR PointerKey; // Windows 7 only
ULONG Interceptor;
ULONG VirtualMemoryThreshold;
ULONG Signature;
// ...
} HEAP_OLD, *PHEAP_OLD;
// Windows 8 and above
typedef struct _HEAP_WIN8
{
HEAP_SEGMENT Segment;
ULONG Flags;
ULONG ForceFlags;
ULONG CompatibilityFlags;
ULONG EncodeFlagMask;
HEAP_ENTRY Encoding;
ULONG Interceptor;
ULONG VirtualMemoryThreshold;
ULONG Signature;
// ...
} HEAP_WIN8, *PHEAP_WIN8;
//
// Windows 10 and above
//
typedef struct _RTLP_HEAP_COMMIT_LIMIT_DATA
{
SIZE_T CommitLimitBytes;
SIZE_T CommitLimitFailureCode;
} RTLP_HEAP_COMMIT_LIMIT_DATA, *PRTLP_HEAP_COMMIT_LIMIT_DATA;
typedef struct _HEAP_PSEUDO_TAG_ENTRY
{
SIZE_T CommitLimitBytes;
SIZE_T CommitLimitFailureCode;
} HEAP_PSEUDO_TAG_ENTRY, *PHEAP_PSEUDO_TAG_ENTRY;
typedef struct _HEAP_TUNING_PARAMETERS
{
ULONG CommittThresholdShift;
SIZE_T MaxPreCommittThreshold;
} HEAP_TUNING_PARAMETERS, *PHEAP_TUNING_PARAMETERS;
typedef struct _HEAP_COUNTERS
{
SIZE_T TotalMemoryReserved;
SIZE_T TotalMemoryCommitted;
SIZE_T TotalMemoryLargeUCR;
SIZE_T TotalSizeInVirtualBlocks;
ULONG TotalSegments;
ULONG TotalUCRs;
ULONG CommittOps;
ULONG DeCommitOps;
ULONG LockAcquires;
ULONG LockCollisions;
ULONG CommitRate;
ULONG DecommittRate;
ULONG CommitFailures;
ULONG InBlockCommitFailures;
ULONG PollIntervalCounter;
ULONG DecommitsSinceLastCheck;
ULONG HeapPollInterval;
ULONG AllocAndFreeOps;
ULONG AllocationIndicesActive;
ULONG InBlockDeccommits;
SIZE_T InBlockDeccomitSize;
SIZE_T HighWatermarkSize;
SIZE_T LastPolledSize;
} HEAP_COUNTERS, *PHEAP_COUNTERS;
typedef struct _HEAP
{
HEAP_SEGMENT Segment;
HEAP_ENTRY Entry;
ULONG SegmentSignature;
ULONG SegmentFlags;
LIST_ENTRY SegmentListEntry;
struct _HEAP* Heap;
PVOID BaseAddress;
ULONG NumberOfPages;
PHEAP_ENTRY FirstEntry;
PHEAP_ENTRY LastValidEntry;
ULONG NumberOfUnCommittedPages;
ULONG NumberOfUnCommittedRanges;
USHORT SegmentAllocatorBackTraceIndex;
USHORT Reserved;
LIST_ENTRY UCRSegmentList;
ULONG Flags;
ULONG ForceFlags;
ULONG CompatibilityFlags;
ULONG EncodeFlagMask;
HEAP_ENTRY Encoding;
ULONG Interceptor;
ULONG VirtualMemoryThreshold;
ULONG Signature;
SIZE_T SegmentReserve;
SIZE_T SegmentCommit;
SIZE_T DeCommitFreeBlockThreshold;
SIZE_T DeCommitTotalFreeThreshold;
SIZE_T TotalFreeSize;
SIZE_T MaximumAllocationSize;
USHORT ProcessHeapsListIndex;
USHORT HeaderValidateLength;
PVOID HeaderValidateCopy;
USHORT NextAvailableTagIndex;
USHORT MaximumTagIndex;
struct _HEAP_TAG_ENTRY* TagEntries;
LIST_ENTRY UCRList;
SIZE_T AlignRound;
SIZE_T AlignMask;
LIST_ENTRY VirtualAllocdBlocks;
LIST_ENTRY SegmentList;
USHORT AllocatorBackTraceIndex;
ULONG NonDedicatedListLength;
PVOID BlocksIndex;
PVOID UCRIndex;
PHEAP_PSEUDO_TAG_ENTRY PseudoTagEntries;
LIST_ENTRY FreeLists;
struct _HEAP_LOCK* LockVariable;
long (*CommitRoutine)(void);
RTL_RUN_ONCE StackTraceInitVar;
RTLP_HEAP_COMMIT_LIMIT_DATA CommitLimitData;
PVOID UserContext;
ULONG_PTR Spare;
PVOID FrontEndHeap;
USHORT FrontHeapLockCount;
UCHAR FrontEndHeapType;
UCHAR RequestedFrontEndHeapType;
PUSHORT FrontEndHeapUsageData;
USHORT FrontEndHeapMaximumIndex;
UCHAR FrontEndHeapStatusBitmap[129];
UCHAR ReadOnly : 1;
UCHAR InternalFlags;
HEAP_COUNTERS Counters;
HEAP_TUNING_PARAMETERS TuningParameters;
} HEAP;
#define SEGMENT_HEAP_SIGNATURE 0xddeeddee
// Windows 8.1 and above
typedef struct _SEGMENT_HEAP_WIN8
{
ULONG_PTR Padding[2];
ULONG Signature;
ULONG GlobalFlags;
// ...
} SEGMENT_HEAP_WIN8, PSEGMENT_HEAP_WIN8;
//
// Windows 10 and above
//
typedef struct _RTL_HP_ENV_HANDLE
{
ULONG_PTR h[2];
} RTL_HP_ENV_HANDLE, PRTL_HP_ENV_HANDLE;
typedef struct _HEAP_OPPORTUNISTIC_LARGE_PAGE_STATS
{
SIZE_T SmallPagesInUseWithinLarge;
SIZE_T OpportunisticLargePageCount;
} HEAP_OPPORTUNISTIC_LARGE_PAGE_STATS, PHEAP_OPPORTUNISTIC_LARGE_PAGE_STATS;
typedef struct _RTL_HP_SEG_ALLOC_POLICY
{
SIZE_T SmallPagesInUseWithinLarge;
SIZE_T OpportunisticLargePageCount;
} RTL_HP_SEG_ALLOC_POLICY, PRTL_HP_SEG_ALLOC_POLICY;
typedef struct _HEAP_RUNTIME_MEMORY_STATS
{
SIZE_T TotalReservedPages;
SIZE_T TotalCommittedPages;
SIZE_T FreeCommittedPages;
SIZE_T LfhFreeCommittedPages;
SIZE_T VsFreeCommittedPages;
HEAP_OPPORTUNISTIC_LARGE_PAGE_STATS LargePageStats[2];
RTL_HP_SEG_ALLOC_POLICY LargePageUtilizationPolicy;
} HEAP_RUNTIME_MEMORY_STATS, PHEAP_RUNTIME_MEMORY_STATS;
typedef struct _SEGMENT_HEAP
{
RTL_HP_ENV_HANDLE EnvHandle;
ULONG Signature;
ULONG GlobalFlags;
ULONG Interceptor;
USHORT ProcessHeapListIndex;
struct
{
USHORT AllocatedFromMetadata : 1;
USHORT ReadOnly : 1;
USHORT InternalFlags : 14;
};
RTLP_HEAP_COMMIT_LIMIT_DATA CommitLimitData;
SIZE_T ReservedMustBeZero;
PVOID UserContext;
SIZE_T LargeMetadataLock;
RTL_RB_TREE LargeAllocMetadata;
SIZE_T LargeReservedPages;
SIZE_T LargeCommittedPages;
SIZE_T Tag;
RTL_RUN_ONCE StackTraceInitVar;
HEAP_RUNTIME_MEMORY_STATS MemStats;
ULONG GlobalLockOwner;
SIZE_T ContextExtendLock;
PBYTE AllocatedBase;
PBYTE UncommittedBase;
PBYTE ReservedLimit;
PBYTE ReservedRegionEnd;
//RTL_HP_HEAP_VA_CALLBACKS_ENCODED CallbacksEncoded;
//HEAP_SEG_CONTEXT SegContexts[2];
//HEAP_VS_CONTEXT VsContext;
//HEAP_LFH_CONTEXT LfhContext;
} SEGMENT_HEAP, PSEGMENT_HEAP;
//
// 32-bit versions
//
typedef struct _HEAP_ENTRY32
{
WOW64_POINTER(PVOID) Data1;
WOW64_POINTER(PVOID) Data2;
} HEAP_ENTRY32, *PHEAP_ENTRY32;
typedef struct _HEAP_SEGMENT32
{
HEAP_ENTRY32 HeapEntry;
ULONG SegmentSignature;
ULONG SegmentFlags;
LIST_ENTRY32 SegmentListEntry;
WOW64_POINTER(struct _HEAP32 *) Heap;
WOW64_POINTER(PVOID) BaseAddress;
ULONG NumberOfPages;
WOW64_POINTER(PHEAP_ENTRY32) FirstEntry;
WOW64_POINTER(PHEAP_ENTRY32) LastValidEntry;
ULONG NumberOfUnCommittedPages;
ULONG NumberOfUnCommittedRanges;
USHORT SegmentAllocatorBackTraceIndex;
USHORT Reserved;
LIST_ENTRY32 UCRSegmentList;
} HEAP_SEGMENT32, *PHEAP_SEGMENT32;
typedef struct _HEAP_OLD32
{
HEAP_SEGMENT32 Segment;
ULONG Flags;
ULONG ForceFlags;
ULONG CompatibilityFlags;
ULONG EncodeFlagMask;
HEAP_ENTRY32 Encoding;
WOW64_POINTER(ULONG_PTR) PointerKey;
ULONG Interceptor;
ULONG VirtualMemoryThreshold;
ULONG Signature;
// ...
} HEAP_OLD32, *PHEAP_OLD32;
typedef struct _HEAP32
{
HEAP_SEGMENT32 Segment;
ULONG Flags;
ULONG ForceFlags;
ULONG CompatibilityFlags;
ULONG EncodeFlagMask;
HEAP_ENTRY32 Encoding;
ULONG Interceptor;
ULONG VirtualMemoryThreshold;
ULONG Signature;
// ...
} HEAP32, *PHEAP32;
typedef struct _SEGMENT_HEAP32
{
WOW64_POINTER(ULONG_PTR) Padding[2];
ULONG Signature;
ULONG GlobalFlags;
// ...
} SEGMENT_HEAP32, PSEGMENT_HEAP32;
typedef union _PH_ANY_HEAP
{
HEAP_OLD HeapOld;
HEAP_OLD32 HeapOld32;
HEAP Heap;
HEAP32 Heap32;
SEGMENT_HEAP SegmentHeap;
SEGMENT_HEAP32 SegmentHeap32;
} PH_ANY_HEAP;
#define HEAP_SEGMENT_MAX_SIZE \
(max(sizeof(HEAP_SEGMENT), sizeof(HEAP_SEGMENT32)))
#endif
================================================
FILE: SystemInformer/include/hidnproc.h
================================================
/*
* Copyright (c) 2022 Winsider Seminars & Solutions, Inc. All rights reserved.
*
* This file is part of System Informer.
*
* Authors:
*
* wj32 2009-2015
* dmex 2017-2024
*
*/
#ifndef PH_HIDNPROC_H
#define PH_HIDNPROC_H
typedef enum _PH_ZOMBIE_PROCESS_METHOD
{
BruteForceScanMethod,
CsrHandlesScanMethod,
ProcessHandleScanMethod,
RegistryScanMethod,
EtwGuidScanMethod,
NtdllScanMethod,
} PH_ZOMBIE_PROCESS_METHOD;
typedef enum _PH_ZOMBIE_PROCESS_TYPE
{
UnknownProcess,
NormalProcess,
ZombieProcess,
TerminatedProcess
} PH_ZOMBIE_PROCESS_TYPE;
typedef struct _PH_ZOMBIE_PROCESS_ENTRY
{
HANDLE ProcessId;
PPH_STRING FileName;
PH_ZOMBIE_PROCESS_TYPE Type;
} PH_ZOMBIE_PROCESS_ENTRY, *PPH_ZOMBIE_PROCESS_ENTRY;
typedef struct _PH_CSR_HANDLE_INFO
{
HANDLE CsrProcessHandle;
HANDLE Handle;
BOOLEAN IsThreadHandle;
HANDLE ProcessId;
} PH_CSR_HANDLE_INFO, *PPH_CSR_HANDLE_INFO;
typedef BOOLEAN (NTAPI *PPH_ENUM_ZOMBIE_PROCESSES_CALLBACK)(
_In_ PPH_ZOMBIE_PROCESS_ENTRY Process,
_In_opt_ PVOID Context
);
NTSTATUS
NTAPI
PhEnumZombieProcesses(
_In_ PH_ZOMBIE_PROCESS_METHOD Method,
_In_ PPH_ENUM_ZOMBIE_PROCESSES_CALLBACK Callback,
_In_opt_ PVOID Context
);
typedef BOOLEAN (NTAPI *PPH_ENUM_CSR_PROCESS_HANDLES_CALLBACK)(
_In_ PPH_CSR_HANDLE_INFO Handle,
_In_opt_ PVOID Context
);
NTSTATUS
NTAPI
PhEnumCsrProcessHandles(
_In_ PPH_ENUM_CSR_PROCESS_HANDLES_CALLBACK Callback,
_In_opt_ PVOID Context
);
NTSTATUS
NTAPI
PhOpenProcessByCsrHandle(
_Out_ PHANDLE ProcessHandle,
_In_ ACCESS_MASK DesiredAccess,
_In_ PPH_CSR_HANDLE_INFO Handle
);
NTSTATUS
NTAPI
PhOpenProcessByCsrHandles(
_Out_ PHANDLE ProcessHandle,
_In_ ACCESS_MASK DesiredAccess,
_In_ HANDLE ProcessId
);
#endif
================================================
FILE: SystemInformer/include/hndllist.h
================================================
/*
* Copyright (c) 2022 Winsider Seminars & Solutions, Inc. All rights reserved.
*
* This file is part of System Informer.
*
* Authors:
*
* wj32 2009-2016
* dmex 2016-2023
*
*/
#ifndef PH_HNDLLIST_H
#define PH_HNDLLIST_H
#include
#include
// Columns
#define PHHNTLC_TYPE 0
#define PHHNTLC_NAME 1
#define PHHNTLC_HANDLE 2
#define PHHNTLC_OBJECTADDRESS 3
#define PHHNTLC_ATTRIBUTES 4
#define PHHNTLC_GRANTEDACCESS 5
#define PHHNTLC_GRANTEDACCESSSYMBOLIC 6
#define PHHNTLC_ORIGINALNAME 7
#define PHHNTLC_FILESHAREACCESS 8
#define PHHNTLC_HANDLEVALUE 9
#define PHHNTLC_HANDLECOUNT 10
#define PHHNTLC_POINTERCOUNT 11
#define PHHNTLC_PAGEDSIZE 12
#define PHHNTLC_NONPAGEDSIZE 13
#define PHHNTLC_MAXIMUM 14
// begin_phapppub
typedef enum _PH_HANDLE_TREE_MENUITEM
{
PH_HANDLE_TREE_MENUITEM_NONE,
PH_HANDLE_TREE_MENUITEM_HIDE_PROTECTED_HANDLES,
PH_HANDLE_TREE_MENUITEM_HIDE_INHERIT_HANDLES,
PH_HANDLE_TREE_MENUITEM_HIDE_UNNAMED_HANDLES,
PH_HANDLE_TREE_MENUITEM_HIDE_ETW_HANDLES,
PH_HANDLE_TREE_MENUITEM_HIGHLIGHT_PROTECTED_HANDLES,
PH_HANDLE_TREE_MENUITEM_HIGHLIGHT_INHERIT_HANDLES,
PH_HANDLE_TREE_MENUITEM_HANDLESTATS,
PH_HANDLE_TREE_MENUITEM_MAXIMUM
} PH_HANDLE_TREE_MENUITEM;
// end_phapppub
// begin_phapppub
typedef struct _PH_HANDLE_NODE
{
PH_TREENEW_NODE Node;
PH_SH_STATE ShState;
HANDLE Handle;
PPH_HANDLE_ITEM HandleItem;
// end_phapppub
PH_STRINGREF TextCache[PHHNTLC_MAXIMUM];
PPH_STRING GrantedAccessSymbolicText;
WCHAR FileShareAccessText[4];
PPH_STRING HandleValue;
PPH_STRING HandleCountText;
PPH_STRING PointerCountText;
PPH_STRING PageSizeText;
PPH_STRING NonPageSizeText;
// begin_phapppub
} PH_HANDLE_NODE, *PPH_HANDLE_NODE;
// end_phapppub
typedef struct _PH_HANDLE_LIST_CONTEXT
{
HWND ParentWindowHandle;
HWND TreeNewHandle;
ULONG TreeNewSortColumn;
PH_TN_FILTER_SUPPORT TreeFilterSupport;
PH_SORT_ORDER TreeNewSortOrder;
PH_CM_MANAGER Cm;
BOOLEAN EnableStateHighlighting;
union
{
ULONG Flags;
struct
{
ULONG Reserved : 1;
ULONG HideUnnamedHandles : 1;
ULONG HideEtwHandles : 1;
ULONG HideProtectedHandles : 1;
ULONG HideInheritHandles : 1;
ULONG Spare : 27;
};
};
PPH_HASHTABLE NodeHashtable;
PPH_LIST NodeList;
PPH_POINTER_LIST NodeStateList;
} PH_HANDLE_LIST_CONTEXT, *PPH_HANDLE_LIST_CONTEXT;
VOID PhInitializeHandleList(
_In_ HWND ParentWindowHandle,
_In_ HWND TreeNewHandle,
_Out_ PPH_HANDLE_LIST_CONTEXT Context
);
VOID PhDeleteHandleList(
_In_ PPH_HANDLE_LIST_CONTEXT Context
);
VOID PhLoadSettingsHandleList(
_Inout_ PPH_HANDLE_LIST_CONTEXT Context
);
VOID PhSaveSettingsHandleList(
_Inout_ PPH_HANDLE_LIST_CONTEXT Context
);
VOID PhSetOptionsHandleList(
_Inout_ PPH_HANDLE_LIST_CONTEXT Context,
_In_ ULONG Options
);
PPH_HANDLE_NODE PhAddHandleNode(
_Inout_ PPH_HANDLE_LIST_CONTEXT Context,
_In_ PPH_HANDLE_ITEM HandleItem,
_In_ ULONG RunId
);
PPH_HANDLE_NODE PhFindHandleNode(
_In_ PPH_HANDLE_LIST_CONTEXT Context,
_In_ HANDLE Handle
);
VOID PhRemoveHandleNode(
_In_ PPH_HANDLE_LIST_CONTEXT Context,
_In_ PPH_HANDLE_NODE HandleNode
);
VOID PhUpdateHandleNode(
_In_ PPH_HANDLE_LIST_CONTEXT Context,
_In_ PPH_HANDLE_NODE HandleNode
);
VOID PhExpandAllHandleNodes(
_In_ PPH_HANDLE_LIST_CONTEXT Context,
_In_ BOOLEAN Expand
);
VOID PhTickHandleNodes(
_In_ PPH_HANDLE_LIST_CONTEXT Context
);
PPH_HANDLE_ITEM PhGetSelectedHandleItem(
_In_ PPH_HANDLE_LIST_CONTEXT Context
);
VOID PhGetSelectedHandleItems(
_In_ PPH_HANDLE_LIST_CONTEXT Context,
_Out_ PPH_HANDLE_ITEM **Handles,
_Out_ PULONG NumberOfHandles
);
VOID PhDeselectAllHandleNodes(
_In_ PPH_HANDLE_LIST_CONTEXT Context
);
#endif
================================================
FILE: SystemInformer/include/hndlmenu.h
================================================
/*
* Copyright (c) 2022 Winsider Seminars & Solutions, Inc. All rights reserved.
*
* This file is part of System Informer.
*
* Authors:
*
* wj32 2009-2016
*
*/
#ifndef PH_HNDLMENU_H
#define PH_HNDLMENU_H
#define PhaAppendCtrlEnter(Text, Enable) ((Enable) ? PhaConcatStrings2((Text), L"\tCtrl+Enter")->Buffer : (Text))
#define PH_MAX_SECTION_EDIT_SIZE (32 * 1024 * 1024) // 32 MB
// begin_phapppub
typedef struct _PH_HANDLE_ITEM_INFO
{
HANDLE ProcessId;
HANDLE Handle;
PPH_STRING TypeName;
PPH_STRING BestObjectName;
} PH_HANDLE_ITEM_INFO, *PPH_HANDLE_ITEM_INFO;
VOID PhInsertHandleObjectPropertiesEMenuItems(
_In_ struct _PH_EMENU_ITEM *Menu,
_In_ ULONG InsertBeforeId,
_In_ BOOLEAN EnableShortcut,
_In_ PPH_HANDLE_ITEM_INFO Info
);
VOID PhShowHandleObjectProperties1(
_In_ HWND hWnd,
_In_ PPH_HANDLE_ITEM_INFO Info
);
VOID PhShowHandleObjectProperties2(
_In_ HWND hWnd,
_In_ PPH_HANDLE_ITEM_INFO Info
);
// end_phapppub
#endif
================================================
FILE: SystemInformer/include/hndlprv.h
================================================
/*
* Copyright (c) 2022 Winsider Seminars & Solutions, Inc. All rights reserved.
*
* This file is part of System Informer.
*
* Authors:
*
* wj32 2010-2016
* dmex 2017-2023
*
*/
#ifndef PH_HNDLPRV_H
#define PH_HNDLPRV_H
extern PPH_OBJECT_TYPE PhHandleProviderType;
extern PPH_OBJECT_TYPE PhHandleItemType;
// begin_phapppub
#define PH_HANDLE_FILE_SHARED_READ 0x1
#define PH_HANDLE_FILE_SHARED_WRITE 0x2
#define PH_HANDLE_FILE_SHARED_DELETE 0x4
#define PH_HANDLE_FILE_SHARED_MASK 0x7
typedef struct _PH_HANDLE_ITEM
{
PH_HASH_ENTRY HashEntry;
PVOID Object;
HANDLE Handle;
HANDLE ProcessId;
ULONG Attributes;
ACCESS_MASK GrantedAccess;
ULONG TypeIndex;
ULONG FileFlags;
ULONG HandleCount;
ULONG PointerCount;
ULONG PagedPoolCharge;
ULONG NonPagedPoolCharge;
PPH_STRING TypeName;
PPH_STRING ObjectName;
PPH_STRING BestObjectName;
WCHAR HandleString[PH_PTR_STR_LEN_1];
WCHAR GrantedAccessString[PH_PTR_STR_LEN_1];
WCHAR ObjectString[PH_PTR_STR_LEN_1];
} PH_HANDLE_ITEM, *PPH_HANDLE_ITEM;
typedef struct _PH_HANDLE_PROVIDER
{
PPH_HASH_ENTRY *HandleHashSet;
ULONG HandleHashSetSize;
ULONG HandleHashSetCount;
PH_QUEUED_LOCK HandleHashSetLock;
PH_CALLBACK HandleAddedEvent;
PH_CALLBACK HandleModifiedEvent;
PH_CALLBACK HandleRemovedEvent;
PH_CALLBACK HandleUpdatedEvent;
HANDLE ProcessId;
HANDLE ProcessHandle;
PPH_HASHTABLE TempListHashtable;
NTSTATUS RunStatus;
} PH_HANDLE_PROVIDER, *PPH_HANDLE_PROVIDER;
PPH_HANDLE_ITEM PhCreateHandleItem(
_In_opt_ PSYSTEM_HANDLE_TABLE_ENTRY_INFO_EX Handle
);
// end_phapppub
PPH_HANDLE_PROVIDER PhCreateHandleProvider(
_In_ HANDLE ProcessId
);
PPH_HANDLE_ITEM PhReferenceHandleItem(
_In_ PPH_HANDLE_PROVIDER HandleProvider,
_In_ HANDLE Handle
);
VOID PhDereferenceAllHandleItems(
_In_ PPH_HANDLE_PROVIDER HandleProvider
);
_Function_class_(PH_PROVIDER_FUNCTION)
VOID PhHandleProviderUpdate(
_In_ PVOID Object
);
#endif
================================================
FILE: SystemInformer/include/informer.h
================================================
/*
* Copyright (c) 2022 Winsider Seminars & Solutions, Inc. All rights reserved.
*
* This file is part of System Informer.
*
* Authors:
*
* jxy-s 2024
*
*/
#ifndef PH_KSIMSG_H
#define PH_KSIMSG_H
typedef struct _PH_INFORMER_CONTEXT
{
PKPH_MESSAGE Message;
ULONG_PTR ReplyToken;
BOOLEAN Handled;
} PH_INFORMER_CONTEXT, *PPH_INFORMER_CONTEXT;
/**
* Callback registration for informer messages that can be replied to. Any
* processing done by these callbacks **must** be brief as it is blocking
* informer message handling on the system.
*
* Receives PPH_INFORMER_CONTEXT as the callback parameter. The callback
* may use PhInformerReply to reply to a message. After a successful call to
* PhInformerReply the context is updated with Handled set to TRUE. Callbacks
* later in the chain will still be called, but the message should not be
* replied to. Callbacks should generally check if the message has already
* been handled prior to doing work.
*/
extern PH_CALLBACK PhInformerCallback;
NTSTATUS PhInformerReply(
_Inout_ PPH_INFORMER_CONTEXT Context,
_In_ PKPH_MESSAGE ReplyMessage
);
BOOLEAN PhInformerDispatch(
_In_ ULONG_PTR ReplyToken,
_In_ PCKPH_MESSAGE Message
);
VOID PhInformerInitialize(
VOID
);
VOID PhInformerActivate(
VOID
);
PPH_LIST PhInformerDatabaseQuery(
_In_ ULONG64 ProcessStartKey,
_In_opt_ PLARGE_INTEGER TimeStamp
);
#endif
================================================
FILE: SystemInformer/include/ksisup.h
================================================
/*
* Copyright (c) 2022 Winsider Seminars & Solutions, Inc. All rights reserved.
*
* This file is part of System Informer.
*
* Authors:
*
* jxy-s 2022
* dmex 2022-2023
*
*/
#ifndef PH_KSISUP_H
#define PH_KSISUP_H
VOID
PhShowKsiStatus(
VOID
);
VOID
PhInitializeKsi(
VOID
);
NTSTATUS
PhCleanupKsi(
VOID
);
PPH_STRING
PhGetKsiMessage(
_In_opt_ NTSTATUS Status,
_In_ BOOLEAN Force,
_In_ PCWSTR Format,
...
);
LONG
PhShowKsiMessage2(
_In_opt_ HWND WindowHandle,
_In_ ULONG Buttons,
_In_opt_ PCWSTR Icon,
_In_opt_ NTSTATUS Status,
_In_ BOOLEAN Force,
_In_ PCWSTR Title,
_In_ PCWSTR Format,
...
);
VOID
PhShowKsiMessageEx(
_In_opt_ HWND WindowHandle,
_In_opt_ PCWSTR Icon,
_In_opt_ NTSTATUS Status,
_In_ BOOLEAN Force,
_In_ PCWSTR Title,
_In_ PCWSTR Format,
...
);
VOID
PhShowKsiMessage(
_In_opt_ HWND WindowHandle,
_In_opt_ PCWSTR Icon,
_In_ PCWSTR Title,
_In_ PCWSTR Format,
...
);
PWSTR PhKsiStandardHelpText(
VOID
);
FORCEINLINE
VOID
PhShowKsiNotConnected(
_In_opt_ HWND WindowHandle,
_In_ PWSTR Message
)
{
PhShowKsiMessage(
WindowHandle,
TD_INFORMATION_ICON,
L"Kernel driver not connected",
L"%s\r\n\r\n"
L"System Informer is not connected to the kernel driver or lacks the required state "
L"necessary for this feature. Make sure that the \"Enable kernel-mode driver\" option is "
L"enabled and that System Informer is running with administrator privileges.",
Message
);
}
FORCEINLINE
PPH_STRING
PhGetKsiNotConnectedString(
_In_ PWSTR Message
)
{
return PhGetKsiMessage(
0,
FALSE,
L"%s\r\n\r\n%s\r\n\r\n%s",
L"Kernel driver not connected",
L"System Informer is not connected to the kernel driver or lacks the required state "
L"necessary for this feature. Make sure that the \"Enable kernel-mode driver\" option is "
L"enabled and that System Informer is running with administrator privileges.",
Message
);
}
#endif
================================================
FILE: SystemInformer/include/mainwnd.h
================================================
/*
* Copyright (c) 2022 Winsider Seminars & Solutions, Inc. All rights reserved.
*
* This file is part of System Informer.
*
* Authors:
*
* wj32 2016
* dmex 2017-2023
*
*/
#ifndef PH_MAINWND_H
#define PH_MAINWND_H
extern HWND PhMainWndHandle;
extern BOOLEAN PhMainWndExiting;
// begin_phapppub
#define WM_PH_FIRST (WM_APP + 99)
#define WM_PH_ACTIVATE (WM_APP + 99)
#define WM_PH_SHOW_DIALOG (WM_APP + 100) // unused (plugins only)
#define WM_PH_UPDATE_DIALOG (WM_APP + 101) // unused (plugins only)
#define PH_ACTIVATE_REPLY 0x1119
#define WM_PH_NOTIFY_ICON_MESSAGE (WM_APP + 126)
#define WM_PH_UPDATE_FONT (WM_APP + 136)
// end_phapppub
#define WM_PH_DESTROY (WM_APP + 145)
#define WM_PH_INVOKE (WM_APP + 146)
#define WM_PH_PREPARE_FOR_EARLY_SHUTDOWN (WM_APP + 147)
#define WM_PH_SAVE_ALL_SETTINGS (WM_APP + 148)
#define WM_PH_SHOW_PROPERTIES (WM_APP + 149)
#define WM_PH_ACTIVATE_WINDOW (WM_APP + 150)
#define WM_PH_SELECT_NODE (WM_APP + 151)
#define WM_PH_SHOW_EDITOR (WM_APP + 152)
#define WM_PH_SHOW_RESULT (WM_APP + 153)
#define WM_PH_LAST (WM_APP + 154)
// begin_phapppub
typedef enum _PH_MAINWINDOW_CALLBACK_TYPE
{
PH_MAINWINDOW_CALLBACK_TYPE_DESTROY,
PH_MAINWINDOW_CALLBACK_TYPE_SHOW_PROPERTIES,
PH_MAINWINDOW_CALLBACK_TYPE_SAVE_ALL_SETTINGS,
PH_MAINWINDOW_CALLBACK_TYPE_PREPARE_FOR_EARLY_SHUTDOWN,
PH_MAINWINDOW_CALLBACK_TYPE_CANCEL_EARLY_SHUTDOWN,
PH_MAINWINDOW_CALLBACK_TYPE_TOGGLE_VISIBLE,
PH_MAINWINDOW_CALLBACK_TYPE_SHOW_MEMORY_EDITOR,
PH_MAINWINDOW_CALLBACK_TYPE_SHOW_MEMORY_RESULTS,
PH_MAINWINDOW_CALLBACK_TYPE_SELECT_TAB_PAGE,
PH_MAINWINDOW_CALLBACK_TYPE_GET_CALLBACK_LAYOUT_PADDING,
PH_MAINWINDOW_CALLBACK_TYPE_INVALIDATE_LAYOUT_PADDING,
PH_MAINWINDOW_CALLBACK_TYPE_SELECT_PROCESS_NODE,
PH_MAINWINDOW_CALLBACK_TYPE_SELECT_SERVICE_ITEM,
PH_MAINWINDOW_CALLBACK_TYPE_SELECT_NETWORK_ITEM,
PH_MAINWINDOW_CALLBACK_TYPE_UPDATE_FONT,
PH_MAINWINDOW_CALLBACK_TYPE_GET_FONT,
PH_MAINWINDOW_CALLBACK_TYPE_INVOKE,
PH_MAINWINDOW_CALLBACK_TYPE_POST,
PH_MAINWINDOW_CALLBACK_TYPE_REFRESH,
PH_MAINWINDOW_CALLBACK_TYPE_CREATE_TAB_PAGE,
PH_MAINWINDOW_CALLBACK_TYPE_GET_UPDATE_AUTOMATICALLY,
PH_MAINWINDOW_CALLBACK_TYPE_SET_UPDATE_AUTOMATICALLY,
PH_MAINWINDOW_CALLBACK_TYPE_ICON_CLICK,
PH_MAINWINDOW_CALLBACK_TYPE_WINDOW_BASE,
PH_MAINWINDOW_CALLBACK_TYPE_GETWINDOW_PROCEDURE,
PH_MAINWINDOW_CALLBACK_TYPE_SETWINDOW_PROCEDURE,
PH_MAINWINDOW_CALLBACK_TYPE_WINDOW_HANDLE,
PH_MAINWINDOW_CALLBACK_TYPE_VERSION,
PH_MAINWINDOW_CALLBACK_TYPE_PORTABLE,
PH_MAINWINDOW_CALLBACK_TYPE_PAGEINDEX,
PH_MAINWINDOW_CALLBACK_TYPE_WINDOWDPI,
PH_MAINWINDOW_CALLBACK_TYPE_WINDOWNAME,
PH_MAINWINDOW_CALLBACK_TYPE_GET_MAIN_MENU,
PH_MAINWINDOW_CALLBACK_TYPE_GET_MAIN_SUBMENU,
PH_MAINWINDOW_CALLBACK_TYPE_SET_MAIN_SUBCMD,
PH_MAINWINDOW_CALLBACK_TYPE_MAXIMUM
} PH_MAINWINDOW_CALLBACK_TYPE;
PHAPPAPI
PVOID
NTAPI
PhPluginInvokeWindowCallback(
_In_ PH_MAINWINDOW_CALLBACK_TYPE Event,
_In_opt_ PVOID wparam,
_In_opt_ PVOID lparam
);
#define SystemInformer_Destroy() \
PhPluginInvokeWindowCallback(PH_MAINWINDOW_CALLBACK_TYPE_DESTROY, 0, 0)
#define SystemInformer_ShowProcessProperties(ProcessItem) \
PhPluginInvokeWindowCallback(PH_MAINWINDOW_CALLBACK_TYPE_SHOW_PROPERTIES, 0, (PVOID)(ULONG_PTR)(ProcessItem))
#define SystemInformer_SaveAllSettings() \
PhPluginInvokeWindowCallback(PH_MAINWINDOW_CALLBACK_TYPE_SAVE_ALL_SETTINGS, 0, 0)
#define SystemInformer_PrepareForEarlyShutdown() \
PhPluginInvokeWindowCallback(PH_MAINWINDOW_CALLBACK_TYPE_PREPARE_FOR_EARLY_SHUTDOWN, 0, 0)
#define SystemInformer_CancelEarlyShutdown() \
PhPluginInvokeWindowCallback(PH_MAINWINDOW_CALLBACK_TYPE_CANCEL_EARLY_SHUTDOWN, 0, 0)
#define SystemInformer_ToggleVisible(AlwaysShow) \
PhPluginInvokeWindowCallback(PH_MAINWINDOW_CALLBACK_TYPE_TOGGLE_VISIBLE, (PVOID)(ULONG_PTR)(AlwaysShow), 0)
#define SystemInformer_ShowMemoryEditor(ShowMemoryEditor) \
PhPluginInvokeWindowCallback(PH_MAINWINDOW_CALLBACK_TYPE_SHOW_MEMORY_EDITOR, 0, (PVOID)(ULONG_PTR)(ShowMemoryEditor))
#define SystemInformer_ShowMemoryResults(ShowMemoryResults) \
PhPluginInvokeWindowCallback(PH_MAINWINDOW_CALLBACK_TYPE_SHOW_MEMORY_RESULTS, 0, (PVOID)(ULONG_PTR)(ShowMemoryResults))
#define SystemInformer_SelectTabPage(Index) \
PhPluginInvokeWindowCallback(PH_MAINWINDOW_CALLBACK_TYPE_SELECT_TAB_PAGE, 0, (PVOID)(ULONG_PTR)(Index))
#define SystemInformer_GetCallbackLayoutPadding() \
((PPH_CALLBACK)PhPluginInvokeWindowCallback(PH_MAINWINDOW_CALLBACK_TYPE_GET_CALLBACK_LAYOUT_PADDING, 0, 0))
#define SystemInformer_InvalidateLayoutPadding() \
PhPluginInvokeWindowCallback(PH_MAINWINDOW_CALLBACK_TYPE_INVALIDATE_LAYOUT_PADDING, 0, 0)
#define SystemInformer_SelectProcessNode(ProcessNode) \
PhPluginInvokeWindowCallback(PH_MAINWINDOW_CALLBACK_TYPE_SELECT_PROCESS_NODE, 0, (PVOID)(ULONG_PTR)(ProcessNode))
#define SystemInformer_SelectServiceItem(ServiceItem) \
PhPluginInvokeWindowCallback(PH_MAINWINDOW_CALLBACK_TYPE_SELECT_SERVICE_ITEM, 0, (PVOID)(ULONG_PTR)(ServiceItem))
#define SystemInformer_SelectNetworkItem(NetworkItem) \
PhPluginInvokeWindowCallback(PH_MAINWINDOW_CALLBACK_TYPE_SELECT_NETWORK_ITEM, 0, (PVOID)(ULONG_PTR)(NetworkItem))
#define SystemInformer_UpdateFont() \
PhPluginInvokeWindowCallback(PH_MAINWINDOW_CALLBACK_TYPE_UPDATE_FONT, 0, 0)
#define SystemInformer_GetFont() \
((HFONT)PhPluginInvokeWindowCallback(PH_MAINWINDOW_CALLBACK_TYPE_GET_FONT, 0, 0))
#define SystemInformer_Invoke(Function, Parameter) \
PhPluginInvokeWindowCallback(PH_MAINWINDOW_CALLBACK_TYPE_INVOKE, (PVOID)(ULONG_PTR)(Parameter), (PVOID)(ULONG_PTR)(Function))
//#define SystemInformer_Post(Function, Parameter) \
// PhPluginInvokeWindowCallback(PH_MAINWINDOW_CALLBACK_TYPE_POST, (PVOID)(ULONG_PTR)(Parameter), (PVOID)(ULONG_PTR)(Function))
//#define SystemInformer_CreateTabPage(Template) \
// PhPluginInvokeWindowCallback(PH_MAINWINDOW_CALLBACK_TYPE_CREATE_TAB_PAGE, 0, (PVOID)(ULONG_PTR)(Template))
#define SystemInformer_Refresh() \
PhPluginInvokeWindowCallback(PH_MAINWINDOW_CALLBACK_TYPE_REFRESH, 0, 0)
#define SystemInformer_GetUpdateAutomatically() \
((BOOLEAN)PtrToUlong(PhPluginInvokeWindowCallback(PH_MAINWINDOW_CALLBACK_TYPE_GET_UPDATE_AUTOMATICALLY, 0, 0)))
#define SystemInformer_SetUpdateAutomatically(Value) \
PhPluginInvokeWindowCallback(PH_MAINWINDOW_CALLBACK_TYPE_SET_UPDATE_AUTOMATICALLY, (PVOID)(ULONG_PTR)(Value), 0)
#define SystemInformer_IconClick() \
PhPluginInvokeWindowCallback(PH_MAINWINDOW_CALLBACK_TYPE_ICON_CLICK, 0, 0)
#define SystemInformer_GetInstanceHandle() \
((PVOID)PhPluginInvokeWindowCallback(PH_MAINWINDOW_CALLBACK_TYPE_WINDOW_BASE, 0, 0))
#define SystemInformer_GetWindowProcedure() \
((WNDPROC)PhPluginInvokeWindowCallback(PH_MAINWINDOW_CALLBACK_TYPE_GETWINDOW_PROCEDURE, 0, 0))
#define SystemInformer_SetWindowProcedure(Value) \
PhPluginInvokeWindowCallback(PH_MAINWINDOW_CALLBACK_TYPE_SETWINDOW_PROCEDURE, (PVOID)(ULONG_PTR)(Value), 0)
#define SystemInformer_GetWindowHandle() \
((HWND)PhPluginInvokeWindowCallback(PH_MAINWINDOW_CALLBACK_TYPE_WINDOW_HANDLE, 0, 0))
#define SystemInformer_GetWindowsVersion() \
(PtrToUlong(PhPluginInvokeWindowCallback(PH_MAINWINDOW_CALLBACK_TYPE_VERSION, 0, 0)))
#define SystemInformer_IsPortableMode() \
((BOOLEAN)PtrToUlong(PhPluginInvokeWindowCallback(PH_MAINWINDOW_CALLBACK_TYPE_PORTABLE, 0, 0)))
#define SystemInformer_GetTabIndex(Value) \
(PtrToUlong(PhPluginInvokeWindowCallback(PH_MAINWINDOW_CALLBACK_TYPE_PAGEINDEX, (PVOID)(ULONG_PTR)(Value), 0)))
#define SystemInformer_GetWindowDpi() \
(PtrToUlong(PhPluginInvokeWindowCallback(PH_MAINWINDOW_CALLBACK_TYPE_WINDOWDPI, 0, 0)))
#define SystemInformer_GetWindowName() \
(PWSTR)(PhPluginInvokeWindowCallback(PH_MAINWINDOW_CALLBACK_TYPE_WINDOWNAME, 0, 0))
#define SystemInformer_GetMainMenu() \
((PPH_EMENU)PhPluginInvokeWindowCallback(PH_MAINWINDOW_CALLBACK_TYPE_GET_MAIN_MENU, 0, 0))
#define SystemInformer_GetMainSubMenu(Index) \
((PPH_EMENU)PhPluginInvokeWindowCallback(PH_MAINWINDOW_CALLBACK_TYPE_GET_MAIN_SUBMENU, (PVOID)(ULONG_PTR)(Index), 0))
#define SystemInformer_SetMainSubCmd(Index, Context) \
PhPluginInvokeWindowCallback(PH_MAINWINDOW_CALLBACK_TYPE_SET_MAIN_SUBCMD, (PVOID)(ULONG_PTR)(Index), (PVOID)Context)
#define PhWindowsVersion SystemInformer_GetWindowsVersion() // Temporary backwards compat (dmex)
// end_phapppub
// begin_phapppub
PHAPPAPI
PVOID
NTAPI
PhPluginCreateTabPage(
_In_ PVOID Page
);
// end_phapppub
typedef struct _PH_SHOW_MEMORY_EDITOR
{
HWND OwnerWindow;
HANDLE ProcessId;
PVOID BaseAddress;
SIZE_T RegionSize;
ULONG SelectOffset;
ULONG SelectLength;
PPH_STRING Title;
ULONG Flags;
} PH_SHOW_MEMORY_EDITOR, *PPH_SHOW_MEMORY_EDITOR;
typedef struct _PH_SHOW_MEMORY_RESULTS
{
HANDLE ProcessId;
PPH_LIST Results;
} PH_SHOW_MEMORY_RESULTS, *PPH_SHOW_MEMORY_RESULTS;
// begin_phapppub
typedef struct _PH_LAYOUT_PADDING_DATA
{
RECT Padding;
} PH_LAYOUT_PADDING_DATA, *PPH_LAYOUT_PADDING_DATA;
// end_phapppub
// begin_phapppub
typedef enum _PH_MAIN_TAB_PAGE_MESSAGE
{
MainTabPageCreate,
MainTabPageDestroy,
MainTabPageCreateWindow, // HWND *Parameter1 (WindowHandle), HWND Parameter2 (ParentWindow)
MainTabPageSelected, // BOOLEAN Parameter1 (Selected)
MainTabPageInitializeSectionMenuItems, // PPH_MAIN_TAB_PAGE_MENU_INFORMATION Parameter1
MainTabPageLoadSettings,
MainTabPageSaveSettings,
MainTabPageExportContent, // PPH_MAIN_TAB_PAGE_EXPORT_CONTENT Parameter1
MainTabPageFontChanged, // HFONT Parameter1 (Font)
MainTabPageUpdateAutomaticallyChanged, // BOOLEAN Parameter1 (UpdateAutomatically)
MainTabPageDpiChanged,
MaxMainTabPageMessage
} PH_MAIN_TAB_PAGE_MESSAGE;
typedef struct _PH_MAIN_TAB_PAGE *PPH_MAIN_TAB_PAGE;
typedef _Function_class_(PH_MAIN_TAB_PAGE_CALLBACK)
BOOLEAN NTAPI PH_MAIN_TAB_PAGE_CALLBACK(
_In_ PPH_MAIN_TAB_PAGE Page,
_In_ PH_MAIN_TAB_PAGE_MESSAGE Message,
_In_opt_ PVOID Parameter1,
_In_opt_ PVOID Parameter2
);
typedef PH_MAIN_TAB_PAGE_CALLBACK* PPH_MAIN_TAB_PAGE_CALLBACK;
typedef struct _PH_MAIN_TAB_PAGE_EXPORT_CONTENT
{
PPH_FILE_STREAM FileStream;
ULONG Mode;
} PH_MAIN_TAB_PAGE_EXPORT_CONTENT, *PPH_MAIN_TAB_PAGE_EXPORT_CONTENT;
typedef struct _PH_MAIN_TAB_PAGE_MENU_INFORMATION
{
PPH_EMENU_ITEM Menu;
ULONG StartIndex;
} PH_MAIN_TAB_PAGE_MENU_INFORMATION, *PPH_MAIN_TAB_PAGE_MENU_INFORMATION;
typedef struct _PH_MAIN_TAB_PAGE
{
// Public
PH_STRINGREF Name;
ULONG Flags;
PPH_MAIN_TAB_PAGE_CALLBACK Callback;
PVOID Context;
LONG Index;
union
{
ULONG StateFlags;
struct
{
ULONG Selected : 1;
ULONG CreateWindowCalled : 1;
ULONG SpareStateFlags : 30;
};
};
PVOID Reserved[2];
// end_phapppub
// Private
HWND WindowHandle;
// begin_phapppub
} PH_MAIN_TAB_PAGE, *PPH_MAIN_TAB_PAGE;
// end_phapppub
// begin_phapppub
#define PH_NOTIFY_MINIMUM 0x1
#define PH_NOTIFY_PROCESS_CREATE 0x1
#define PH_NOTIFY_PROCESS_DELETE 0x2
#define PH_NOTIFY_SERVICE_CREATE 0x4
#define PH_NOTIFY_SERVICE_DELETE 0x8
#define PH_NOTIFY_SERVICE_START 0x10
#define PH_NOTIFY_SERVICE_STOP 0x20
#define PH_NOTIFY_SERVICE_MODIFIED 0x40
#define PH_NOTIFY_DEVICE_ARRIVED 0x80
#define PH_NOTIFY_DEVICE_REMOVED 0x100
#define PH_NOTIFY_MAXIMUM 0x200
#define PH_NOTIFY_VALID_MASK 0x1ff
// end_phapppub
BOOLEAN PhMainWndInitialization(
_In_ LONG ShowCommand
);
VOID PhAddMiniProcessMenuItems(
_Inout_ PPH_EMENU_ITEM Menu,
_In_ HANDLE ProcessId
);
BOOLEAN PhHandleMiniProcessMenuItem(
_Inout_ PPH_EMENU_ITEM MenuItem
);
VOID PhShowIconContextMenu(
_In_ HWND WindowHandle,
_In_ PPOINT Location
);
// begin_phapppub
PHAPPAPI
VOID
NTAPI
PhShowIconNotification(
_In_ PCWSTR Title,
_In_ PCWSTR Text
);
typedef enum _PH_TOAST_REASON
{
PhToastReasonUserCanceled,
PhToastReasonApplicationHidden,
PhToastReasonTimedOut,
PhToastReasonActivated,
PhToastReasonError,
PhToastReasonUnknown
} PH_TOAST_REASON;
typedef _Function_class_(PH_TOAST_CALLBACK)
VOID NTAPI PH_TOAST_CALLBACK(
_In_ HRESULT Result,
_In_ PH_TOAST_REASON Reason,
_In_ PVOID Context
);
typedef PH_TOAST_CALLBACK* PPH_TOAST_CALLBACK;
PHAPPAPI
HRESULT
NTAPI
PhShowIconNotificationEx(
_In_ PCWSTR Title,
_In_ PCWSTR Text,
_In_ ULONG Timeout,
_In_opt_ PPH_TOAST_CALLBACK Callback,
_In_opt_ PVOID Context
);
// end_phapppub
VOID PhProcessInvokeQueue(
VOID
);
VOID PhShowDetailsForIconNotification(
VOID
);
VOID PhShowOptionsRestartRequired(
_In_ HWND WindowHandle
);
BOOLEAN PhShowOptionsDefaultInstallLocation(
_In_ HWND ParentWindowHandle,
_In_ PCWSTR Message
);
VOID PhShowProcessContextMenu(
_In_ HWND WindowHandle,
_In_ PPH_TREENEW_CONTEXT_MENU ContextMenu
);
VOID PhShowServiceContextMenu(
_In_ PPH_TREENEW_CONTEXT_MENU ContextMenu
);
VOID PhShowNetworkContextMenu(
_In_ PPH_TREENEW_CONTEXT_MENU ContextMenu
);
typedef struct _PH_EMENU_ITEM PH_EMENU, *PPH_EMENU;
VOID PhServiceListInsertContextMenu(
_In_ HWND ParentWindow,
_In_ PPH_EMENU Menu,
_In_ PPH_SERVICE_ITEM* Services,
_In_ ULONG NumberOfServices
);
#endif
================================================
FILE: SystemInformer/include/mainwndp.h
================================================
/*
* Copyright (c) 2022 Winsider Seminars & Solutions, Inc. All rights reserved.
*
* This file is part of System Informer.
*
* Authors:
*
* wj32 2011-2016
* dmex 2017-2023
*
*/
#ifndef PH_MAINWNDP_H
#define PH_MAINWNDP_H
#define PH_FLUSH_PROCESS_QUERY_DATA_INTERVAL_1 250
#define PH_FLUSH_PROCESS_QUERY_DATA_INTERVAL_2 750
#define PH_FLUSH_PROCESS_QUERY_DATA_INTERVAL_LONG_TERM 1000
#define TIMER_FLUSH_PROCESS_QUERY_DATA 1
#define TIMER_ICON_CLICK_ACTIVATE 2
#define TIMER_ICON_RESTORE_HOVER 3
#define TIMER_ICON_POPUPOPEN 4
typedef union _PH_MWP_NOTIFICATION_DETAILS
{
HANDLE ProcessId;
PPH_STRING ServiceName;
} PH_MWP_NOTIFICATION_DETAILS, *PPH_MWP_NOTIFICATION_DETAILS;
extern PH_PROVIDER_REGISTRATION PhMwpProcessProviderRegistration;
extern PH_PROVIDER_REGISTRATION PhMwpServiceProviderRegistration;
extern PH_PROVIDER_REGISTRATION PhMwpNetworkProviderRegistration;
extern BOOLEAN PhMwpUpdateAutomatically;
extern ULONG PhMwpNotifyIconNotifyMask;
extern ULONG PhMwpLastNotificationType;
extern PH_MWP_NOTIFICATION_DETAILS PhMwpLastNotificationDetails;
LRESULT CALLBACK PhMwpWndProc(
_In_ HWND WindowHandle,
_In_ UINT WindowMessage,
_In_ WPARAM wParam,
_In_ LPARAM lParam
);
//
// Initialization
//
RTL_ATOM PhMwpInitializeWindowClass(
VOID
);
PPH_STRING PhMwpInitializeWindowTitle(
VOID
);
VOID PhMwpInitializeProviders(
VOID
);
VOID PhMwpShowWindow(
_In_ LONG ShowCommand
);
VOID PhMwpApplyUpdateInterval(
_In_ ULONG Interval
);
VOID PhMwpInitializeMetrics(
_In_ HWND WindowHandle,
_In_ LONG WindowDpi
);
VOID PhMwpInitializeControls(
_In_ HWND WindowHandle
);
_Function_class_(USER_THREAD_START_ROUTINE)
NTSTATUS PhMwpLoadStage1Worker(
_In_ PVOID Parameter
);
VOID PhMwpInvokePrepareEarlyExit(
_In_ HWND WindowHandle
);
VOID PhMwpInvokeActivateWindow(
_In_ BOOLEAN Toggle
);
VOID PhMwpInvokeSelectTabPage(
_In_ PVOID Parameter
);
VOID PhMwpInvokeSelectServiceItem(
_In_ PPH_SERVICE_ITEM ServiceItem
);
VOID PhMwpInvokeSelectNetworkItem(
_In_ PPH_NETWORK_ITEM NetworkItem
);
VOID PhMwpInvokeUpdateWindowFont(
_In_opt_ PVOID Parameter
);
VOID PhMwpInvokeUpdateWindowFontMonospace(
_In_ HWND WindowHandle,
_In_opt_ PVOID Parameter
);
//
// Main
//
LONG PhMainMessageLoop(
VOID
);
VOID PhInitializePreviousInstance(
VOID
);
VOID PhActivatePreviousInstance(
VOID
);
VOID PhInitializeCommonControls(
VOID
);
VOID PhInitializeSuperclassControls( // delayhook.c
VOID
);
NTSTATUS PhInitializeAppSystem(
VOID
);
VOID PhInitializeAppSettings(
VOID
);
VOID PhpProcessStartupParameters(
VOID
);
VOID PhpEnablePrivileges(
VOID
);
VOID PhEnableTerminationPolicy(
_In_ BOOLEAN Enabled
);
NTSTATUS PhInitializeDirectoryPolicy(
VOID
);
NTSTATUS PhInitializeExceptionPolicy(
VOID
);
NTSTATUS PhInitializeExecutionPolicy(
VOID
);
NTSTATUS PhInitializeNamespacePolicy(
VOID
);
NTSTATUS PhInitializeMitigationPolicy(
VOID
);
NTSTATUS PhInitializeComPolicy(
VOID
);
NTSTATUS PhInitializeTimerPolicy(
VOID
);
//
// Event handlers
//
VOID PhMwpOnDestroy(
_In_ HWND WindowHandle
);
VOID PhMwpOnEndSession(
_In_ HWND WindowHandle,
_In_ BOOLEAN SessionEnding,
_In_ ULONG Reason
);
VOID PhMwpOnSettingChange(
_In_ HWND WindowHandle,
_In_opt_ ULONG Action,
_In_opt_ PCWSTR Metric
);
VOID PhMwpOnCommand(
_In_ HWND WindowHandle,
_In_ ULONG Id
);
VOID PhMwpOnShowWindow(
_In_ HWND WindowHandle,
_In_ BOOLEAN Showing,
_In_ ULONG State
);
BOOLEAN PhMwpOnSysCommand(
_In_ HWND WindowHandle,
_In_ ULONG Type,
_In_ LONG CursorScreenX,
_In_ LONG CursorScreenY
);
VOID PhMwpOnMenuCommand(
_In_ HWND WindowHandle,
_In_ ULONG Index,
_In_ HMENU Menu
);
VOID PhMwpOnInitMenuPopup(
_In_ HWND WindowHandle,
_In_ HMENU Menu,
_In_ ULONG Index,
_In_ BOOLEAN IsWindowMenu
);
VOID PhMwpOnSize(
_In_ HWND WindowHandle,
_In_ UINT State,
_In_ LONG Width,
_In_ LONG Height
);
VOID PhMwpOnSizing(
_In_ ULONG Edge,
_In_ PRECT DragRectangle
);
VOID PhMwpOnSetFocus(
_In_ HWND WindowHandle
);
VOID PhMwpOnTimer(
_In_ HWND WindowHandle,
_In_ WPARAM wParam,
_In_ LPARAM lParam
);
_Success_(return)
BOOLEAN PhMwpOnNotify(
_In_ NMHDR *Header,
_Out_ LRESULT *Result
);
VOID PhMwpOnDeviceChanged(
_In_ HWND WindowHandle,
_In_ WPARAM wParam,
_In_ LPARAM lParam
);
VOID PhMwpOnDpiChanged(
_In_ HWND WindowHandle,
_In_ LONG WindowDpi
);
LRESULT PhMwpOnUserMessage(
_In_ HWND WindowHandle,
_In_ ULONG Message,
_In_ ULONG_PTR WParam,
_In_ ULONG_PTR LParam
);
//
// Settings
//
VOID PhMwpLoadSettings(
_In_ HWND WindowHandle
);
VOID PhMwpSaveSettings(
_In_ HWND WindowHandle
);
VOID PhMwpSaveWindowState(
_In_ HWND WindowHandle
);
//
// Misc.
//
VOID PhMwpUpdateLayoutPadding(
VOID
);
VOID PhMwpApplyLayoutPadding(
_Inout_ PRECT Rect,
_In_ PRECT Padding
);
VOID PhMwpLayout(
_Inout_ HDWP *DeferHandle
);
VOID PhMwpSetupComputerMenu(
_In_ PPH_EMENU_ITEM Root
);
BOOLEAN PhMwpExecuteComputerCommand(
_In_ HWND WindowHandle,
_In_ ULONG Id
);
VOID PhMwpActivateWindow(
_In_ HWND WindowHandle,
_In_ BOOLEAN Toggle
);
//
// Main menu
//
PPH_EMENU PhpCreateMainMenu(
_In_ ULONG SubMenuIndex
);
VOID PhMwpInitializeMainMenu(
_In_ HWND WindowHandle
);
VOID PhMwpDispatchMenuCommand(
_In_ HWND WindowHandle,
_In_ ULONG ItemId,
_In_ ULONG_PTR ItemData
);
VOID PhMwpInitializeSubMenu(
_In_ HWND WindowHandle,
_In_ PPH_EMENU Menu,
_In_ ULONG Index
);
VOID PhMwpInitializeSectionMenuItems(
_In_ PPH_EMENU Menu,
_In_ ULONG StartIndex
);
BOOLEAN PhMwpExecuteNotificationMenuCommand(
_In_ HWND WindowHandle,
_In_ ULONG Id
);
BOOLEAN PhMwpExecuteNotificationSettingsMenuCommand(
_In_ HWND WindowHandle,
_In_ ULONG Id
);
PPH_EMENU PhMwpCreateProcessMenu(
_In_ BOOLEAN SystemProcess
);
//
// Tab control
//
VOID PhMwpLayoutTabControl(
_Inout_ HDWP *DeferHandle
);
VOID PhMwpNotifyTabControl(
_In_ NMHDR *Header
);
VOID PhMwpSelectionChangedTabControl(
_In_ LONG OldIndex
);
PPH_MAIN_TAB_PAGE PhMwpCreatePage(
_In_ PPH_MAIN_TAB_PAGE Template
);
VOID PhMwpSelectPage(
_In_ ULONG Index
);
PPH_MAIN_TAB_PAGE PhMwpFindPage(
_In_ PPH_STRINGREF Name
);
PPH_MAIN_TAB_PAGE PhMwpCreateInternalPage(
_In_ PCWSTR Name,
_In_ ULONG Flags,
_In_ PPH_MAIN_TAB_PAGE_CALLBACK Callback
);
VOID PhMwpNotifyAllPages(
_In_ PH_MAIN_TAB_PAGE_MESSAGE Message,
_In_opt_ PVOID Parameter1,
_In_opt_ PVOID Parameter2
);
//
// Notifications
//
VOID PhMwpAddIconProcesses(
_In_ PPH_EMENU_ITEM Menu,
_In_ ULONG NumberOfProcesses
);
VOID PhMwpClearLastNotificationDetails(
VOID
);
BOOLEAN PhMwpPluginNotifyEvent(
_In_ ULONG Type,
_In_ PVOID Parameter
);
typedef struct _PH_MAIN_TAB_PAGE *PPH_MAIN_TAB_PAGE;
typedef struct _PH_PROVIDER_EVENT_QUEUE PH_PROVIDER_EVENT_QUEUE, *PPH_PROVIDER_EVENT_QUEUE;
typedef struct DECLSPEC_ALIGN(MEMORY_ALLOCATION_ALIGNMENT) _PH_INVOKE_ENTRY
{
SLIST_ENTRY ListEntry;
PVOID Command;
PVOID Parameter;
//HANDLE ThreadId;
//ULONG64 SubmitTime;
} PH_INVOKE_ENTRY, * PPH_INVOKE_ENTRY;
extern SLIST_HEADER PhMainThreadInvokeQueue;
extern PH_FREE_LIST PhMainThreadInvokeQueueFreeList;
extern volatile LONG PhMainThreadInvokePending;
//
// Processes
//
extern PPH_MAIN_TAB_PAGE PhMwpProcessesPage;
extern HWND PhMwpProcessTreeNewHandle;
extern HWND PhMwpSelectedProcessWindowHandle;
extern BOOLEAN PhMwpSelectedProcessVirtualizationEnabled;
extern PH_PROVIDER_EVENT_QUEUE PhMwpProcessEventQueue;
_Function_class_(PH_MAIN_TAB_PAGE_CALLBACK)
BOOLEAN PhMwpProcessesPageCallback(
_In_ PPH_MAIN_TAB_PAGE Page,
_In_ PH_MAIN_TAB_PAGE_MESSAGE Message,
_In_opt_ PVOID Parameter1,
_In_opt_ PVOID Parameter2
);
VOID PhMwpShowProcessProperties(
_In_ PPH_PROCESS_ITEM ProcessItem
);
VOID PhMwpToggleCurrentUserProcessTreeFilter(
VOID
);
_Function_class_(PH_TN_FILTER_FUNCTION)
BOOLEAN PhMwpCurrentUserProcessTreeFilter(
_In_ PPH_TREENEW_NODE Node,
_In_opt_ PVOID Context
);
VOID PhMwpToggleSignedProcessTreeFilter(
_In_ HWND WindowHandle
);
VOID PhMwpToggleMicrosoftProcessTreeFilter(
VOID
);
_Function_class_(PH_TN_FILTER_FUNCTION)
BOOLEAN PhMwpSignedProcessTreeFilter(
_In_ PPH_TREENEW_NODE Node,
_In_opt_ PVOID Context
);
_Function_class_(PH_TN_FILTER_FUNCTION)
BOOLEAN PhMwpMicrosoftProcessTreeFilter(
_In_ PPH_TREENEW_NODE Node,
_In_opt_ PVOID Context
);
BOOLEAN PhMwpExecuteProcessPriorityClassCommand(
_In_ HWND WindowHandle,
_In_ ULONG Id,
_In_ PPH_PROCESS_ITEM *Processes,
_In_ ULONG NumberOfProcesses
);
BOOLEAN PhMwpExecuteProcessIoPriorityCommand(
_In_ HWND WindowHandle,
_In_ ULONG Id,
_In_ PPH_PROCESS_ITEM *Processes,
_In_ ULONG NumberOfProcesses
);
VOID PhMwpSetProcessMenuPriorityChecks(
_In_ PPH_EMENU Menu,
_In_opt_ HANDLE ProcessId,
_In_ BOOLEAN SetPriority,
_In_ BOOLEAN SetIoPriority,
_In_ BOOLEAN SetPagePriority
);
VOID PhMwpInitializeProcessMenu(
_In_ PPH_EMENU Menu,
_In_ PPH_PROCESS_ITEM *Processes,
_In_ ULONG NumberOfProcesses
);
_Function_class_(PH_CALLBACK_FUNCTION)
VOID NTAPI PhMwpProcessAddedHandler(
_In_ PVOID Parameter,
_In_ PVOID Context
);
_Function_class_(PH_CALLBACK_FUNCTION)
VOID NTAPI PhMwpProcessModifiedHandler(
_In_ PVOID Parameter,
_In_ PVOID Context
);
_Function_class_(PH_CALLBACK_FUNCTION)
VOID NTAPI PhMwpProcessRemovedHandler(
_In_ PVOID Parameter,
_In_ PVOID Context
);
_Function_class_(PH_CALLBACK_FUNCTION)
VOID NTAPI PhMwpProcessesUpdatedHandler(
_In_ PVOID Parameter,
_In_ PVOID Context
);
VOID PhMwpOnProcessesUpdated(
_In_ ULONG RunId
);
//
// Services
//
extern PPH_MAIN_TAB_PAGE PhMwpServicesPage;
extern HWND PhMwpServiceTreeNewHandle;
extern PH_PROVIDER_EVENT_QUEUE PhMwpServiceEventQueue;
_Function_class_(PH_MAIN_TAB_PAGE_CALLBACK)
BOOLEAN PhMwpServicesPageCallback(
_In_ PPH_MAIN_TAB_PAGE Page,
_In_ PH_MAIN_TAB_PAGE_MESSAGE Message,
_In_ PVOID Parameter1,
_In_ PVOID Parameter2
);
VOID PhMwpNeedServiceTreeList(
VOID
);
VOID PhMwpToggleDriverServiceTreeFilter(
VOID
);
VOID PhMwpToggleMicrosoftServiceTreeFilter(
VOID
);
_Function_class_(PH_TN_FILTER_FUNCTION)
BOOLEAN PhMwpDriverServiceTreeFilter(
_In_ PPH_TREENEW_NODE Node,
_In_opt_ PVOID Context
);
_Function_class_(PH_TN_FILTER_FUNCTION)
BOOLEAN PhMwpMicrosoftServiceTreeFilter(
_In_ PPH_TREENEW_NODE Node,
_In_opt_ PVOID Context
);
VOID PhMwpInitializeServiceMenu(
_In_ PPH_EMENU Menu,
_In_ PPH_SERVICE_ITEM *Services,
_In_ ULONG NumberOfServices
);
_Function_class_(PH_CALLBACK_FUNCTION)
VOID NTAPI PhMwpServiceAddedHandler(
_In_ PVOID Parameter,
_In_ PVOID Context
);
_Function_class_(PH_CALLBACK_FUNCTION)
VOID NTAPI PhMwpServiceModifiedHandler(
_In_ PVOID Parameter,
_In_ PVOID Context
);
_Function_class_(PH_CALLBACK_FUNCTION)
VOID NTAPI PhMwpServiceRemovedHandler(
_In_ PVOID Parameter,
_In_ PVOID Context
);
_Function_class_(PH_CALLBACK_FUNCTION)
VOID NTAPI PhMwpServicesUpdatedHandler(
_In_ PVOID Parameter,
_In_ PVOID Context
);
VOID PhMwpOnServicesUpdated(
_In_ ULONG RunId
);
//
// Network
//
extern PPH_MAIN_TAB_PAGE PhMwpNetworkPage;
extern HWND PhMwpNetworkTreeNewHandle;
extern PH_PROVIDER_EVENT_QUEUE PhMwpNetworkEventQueue;
_Function_class_(PH_MAIN_TAB_PAGE_CALLBACK)
BOOLEAN PhMwpNetworkPageCallback(
_In_ PPH_MAIN_TAB_PAGE Page,
_In_ PH_MAIN_TAB_PAGE_MESSAGE Message,
_In_ PVOID Parameter1,
_In_ PVOID Parameter2
);
VOID PhMwpNeedNetworkTreeList(
VOID
);
VOID PhMwpToggleNetworkWaitingConnectionTreeFilter(
VOID
);
_Function_class_(PH_TN_FILTER_FUNCTION)
BOOLEAN PhMwpNetworkTreeFilter(
_In_ PPH_TREENEW_NODE Node,
_In_opt_ PVOID Context
);
VOID PhMwpInitializeNetworkMenu(
_In_ PPH_EMENU Menu,
_In_ PPH_NETWORK_ITEM *NetworkItems,
_In_ ULONG NumberOfNetworkItems
);
_Function_class_(PH_CALLBACK_FUNCTION)
VOID PhMwpNetworkItemAddedHandler(
_In_ PVOID Parameter,
_In_ PVOID Context
);
_Function_class_(PH_CALLBACK_FUNCTION)
VOID PhMwpNetworkItemModifiedHandler(
_In_ PVOID Parameter,
_In_ PVOID Context
);
_Function_class_(PH_CALLBACK_FUNCTION)
VOID PhMwpNetworkItemRemovedHandler(
_In_ PVOID Parameter,
_In_ PVOID Context
);
_Function_class_(PH_CALLBACK_FUNCTION)
VOID PhMwpNetworkItemsUpdatedHandler(
_In_ PVOID Parameter,
_In_ PVOID Context
);
VOID PhMwpOnNetworkItemsUpdated(
_In_ ULONG RunId
);
//
// Devices
//
VOID PhMwpInitializeDeviceNotifications(
VOID
);
#endif
================================================
FILE: SystemInformer/include/memlist.h
================================================
/*
* Copyright (c) 2022 Winsider Seminars & Solutions, Inc. All rights reserved.
*
* This file is part of System Informer.
*
* Authors:
*
* wj32 2016
* dmex 2017-2023
*
*/
#ifndef PH_MEMLIST_H
#define PH_MEMLIST_H
#include
// Columns
#define PHMMTLC_BASEADDRESS 0
#define PHMMTLC_TYPE 1
#define PHMMTLC_SIZE 2
#define PHMMTLC_PROTECTION 3
#define PHMMTLC_USE 4
#define PHMMTLC_TOTALWS 5
#define PHMMTLC_PRIVATEWS 6
#define PHMMTLC_SHAREABLEWS 7
#define PHMMTLC_SHAREDWS 8
#define PHMMTLC_LOCKEDWS 9
#define PHMMTLC_COMMITTED 10
#define PHMMTLC_PRIVATE 11
#define PHMMTLC_SIGNING_LEVEL 12
#define PHMMTLC_ORIGINAL_PROTECTION 13
#define PHMMTLC_ORIGINAL_PAGES 14
#define PHMMTLC_REGIONTYPE 15
#define PHMMTLC_PRIORITY 16
#define PHMMTLC_MAXIMUM 17
// begin_phapppub
typedef struct _PH_MEMORY_NODE
{
PH_TREENEW_NODE Node;
BOOLEAN IsAllocationBase;
BOOLEAN Reserved1;
USHORT Reserved2;
PPH_MEMORY_ITEM MemoryItem;
struct _PH_MEMORY_NODE *Parent;
PPH_LIST Children;
// end_phapppub
PH_STRINGREF TextCache[PHMMTLC_MAXIMUM];
WCHAR TypeText[30];
PPH_STRING SizeText;
WCHAR ProtectionText[17];
WCHAR OriginalProtectionText[17];
PPH_STRING UseText;
PPH_STRING TotalWsText;
PPH_STRING PrivateWsText;
PPH_STRING ShareableWsText;
PPH_STRING SharedWsText;
PPH_STRING LockedWsText;
PPH_STRING CommittedText;
PPH_STRING PrivateText;
PPH_STRING OriginalPagesText;
PPH_STRING RegionTypeText;
PPH_STRING PriorityText;
// begin_phapppub
} PH_MEMORY_NODE, *PPH_MEMORY_NODE;
// end_phapppub
#define PH_MEMORY_FLAGS_FREE_OPTION 1
#define PH_MEMORY_FLAGS_RESERVED_OPTION 2
#define PH_MEMORY_FLAGS_PRIVATE_OPTION 3
#define PH_MEMORY_FLAGS_SYSTEM_OPTION 4
#define PH_MEMORY_FLAGS_CFG_OPTION 5
#define PH_MEMORY_FLAGS_EXECUTE_OPTION 6
#define PH_MEMORY_FLAGS_GUARD_OPTION 7
#define PH_MEMORY_FLAGS_ZERO_PAD_ADDRESSES 8
typedef struct _PH_MEMORY_LIST_CONTEXT
{
HWND ParentWindowHandle;
HWND TreeNewHandle;
ULONG TreeNewSortColumn;
PH_TN_FILTER_SUPPORT AllocationTreeFilterSupport;
PH_TN_FILTER_SUPPORT TreeFilterSupport;
PH_SORT_ORDER TreeNewSortOrder;
PH_CM_MANAGER Cm;
union
{
ULONG Flags;
struct
{
ULONG HideFreeRegions : 1;
ULONG HideReservedRegions : 1;
ULONG HighlightPrivatePages : 1;
ULONG HighlightSystemPages : 1;
ULONG HighlightCfgPages : 1;
ULONG HighlightExecutePages : 1;
ULONG HideGuardRegions : 1;
ULONG ZeroPadAddresses : 1;
ULONG Spare : 24;
};
};
PPH_LIST AllocationBaseNodeList; // Allocation base nodes (list should always be sorted by base address)
PPH_LIST RegionNodeList; // Memory region nodes
} PH_MEMORY_LIST_CONTEXT, *PPH_MEMORY_LIST_CONTEXT;
VOID PhInitializeMemoryList(
_In_ HWND ParentWindowHandle,
_In_ HWND TreeNewHandle,
_Out_ PPH_MEMORY_LIST_CONTEXT Context
);
VOID PhDeleteMemoryList(
_In_ PPH_MEMORY_LIST_CONTEXT Context
);
VOID PhLoadSettingsMemoryList(
_Inout_ PPH_MEMORY_LIST_CONTEXT Context
);
VOID PhSaveSettingsMemoryList(
_Inout_ PPH_MEMORY_LIST_CONTEXT Context
);
VOID PhSetOptionsMemoryList(
_Inout_ PPH_MEMORY_LIST_CONTEXT Context,
_In_ ULONG Options
);
VOID PhReplaceMemoryList(
_Inout_ PPH_MEMORY_LIST_CONTEXT Context,
_In_opt_ PPH_MEMORY_ITEM_LIST List
);
VOID PhRemoveMemoryNode(
_Inout_ PPH_MEMORY_LIST_CONTEXT Context,
_In_ PPH_MEMORY_ITEM_LIST List,
_In_ PPH_MEMORY_NODE MemoryNode
);
VOID PhUpdateMemoryNode(
_In_ PPH_MEMORY_LIST_CONTEXT Context,
_In_ PPH_MEMORY_NODE MemoryNode
);
VOID PhInvalidateAllMemoryNodes(
_In_ PPH_MEMORY_LIST_CONTEXT Context
);
VOID PhInvalidateAllMemoryBaseAddressNodes(
_In_ PPH_MEMORY_LIST_CONTEXT Context
);
VOID PhExpandAllMemoryNodes(
_In_ PPH_MEMORY_LIST_CONTEXT Context,
_In_ BOOLEAN Expand
);
PPH_STRING PhGetMemoryRegionUseText(
_In_ PPH_MEMORY_ITEM MemoryItem
);
PPH_MEMORY_NODE PhGetSelectedMemoryNode(
_In_ PPH_MEMORY_LIST_CONTEXT Context
);
VOID PhGetSelectedMemoryNodes(
_In_ PPH_MEMORY_LIST_CONTEXT Context,
_Out_ PPH_MEMORY_NODE **MemoryNodes,
_Out_ PULONG NumberOfMemoryNodes
);
VOID PhDeselectAllMemoryNodes(
_In_ PPH_MEMORY_LIST_CONTEXT Context
);
#endif
================================================
FILE: SystemInformer/include/memprv.h
================================================
/*
* Copyright (c) 2022 Winsider Seminars & Solutions, Inc. All rights reserved.
*
* This file is part of System Informer.
*
* Authors:
*
* wj32 2016
* dmex 2018-2023
*
*/
#ifndef PH_MEMPRV_H
#define PH_MEMPRV_H
extern PPH_OBJECT_TYPE PhMemoryItemType;
// begin_phapppub
typedef enum _PH_MEMORY_REGION_TYPE
{
UnknownRegion,
CustomRegion,
UnusableRegion,
MappedFileRegion,
UserSharedDataRegion,
PebRegion,
Peb32Region,
TebRegion,
Teb32Region, // Not used
StackRegion,
Stack32Region,
HeapRegion,
Heap32Region,
HeapSegmentRegion,
HeapSegment32Region,
CfgBitmapRegion,
CfgBitmap32Region,
ApiSetMapRegion,
HypervisorSharedDataRegion,
ReadOnlySharedMemoryRegion,
CodePageDataRegion,
GdiSharedHandleTableRegion,
ShimDataRegion,
ActivationContextDataRegion,
WerRegistrationDataRegion,
SiloSharedDataRegion,
TelemetryCoverageRegion,
ProcessParametersRegion,
LeapSecondDataRegion,
DesktopHeapRegion,
} PH_MEMORY_REGION_TYPE;
typedef enum _PH_ACTIVATION_CONTEXT_DATA_TYPE
{
CustomActivationContext,
ProcessActivationContext,
SystemActivationContext
} PH_ACTIVATION_CONTEXT_DATA_TYPE;
typedef struct _PH_MEMORY_ITEM
{
LIST_ENTRY ListEntry;
PH_AVL_LINKS Links;
union
{
MEMORY_BASIC_INFORMATION BasicInfo;
struct
{
PVOID BaseAddress;
PVOID AllocationBase;
ULONG AllocationProtect;
SIZE_T RegionSize;
ULONG State;
ULONG Protect;
ULONG Type;
};
};
union
{
BOOLEAN Attributes;
struct
{
BOOLEAN Valid : 1;
BOOLEAN Bad : 1;
BOOLEAN Spare : 6;
};
};
union
{
ULONG RegionTypeEx;
struct
{
ULONG Private : 1;
ULONG MappedDataFile : 1;
ULONG MappedImage : 1;
ULONG MappedPageFile : 1;
ULONG MappedPhysical : 1;
ULONG DirectMapped : 1;
ULONG SoftwareEnclave : 1; // REDSTONE3
ULONG PageSize64K : 1;
ULONG PlaceholderReservation : 1; // REDSTONE4
ULONG MappedAwe : 1; // 21H1
ULONG MappedWriteWatch : 1;
ULONG PageSizeLarge : 1;
ULONG PageSizeHuge : 1;
ULONG Reserved : 19; // Sync with MemoryRegionInformationEx (dmex)
};
};
WCHAR BaseAddressString[PH_PTR_STR_LEN_1];
struct _PH_MEMORY_ITEM *AllocationBaseItem;
SIZE_T CommittedSize;
SIZE_T PrivateSize;
SIZE_T TotalWorkingSetPages;
SIZE_T PrivateWorkingSetPages;
SIZE_T SharedWorkingSetPages;
SIZE_T ShareableWorkingSetPages;
SIZE_T LockedWorkingSetPages;
SIZE_T SharedOriginalPages;
ULONG_PTR Priority;
PH_MEMORY_REGION_TYPE RegionType;
union
{
struct
{
PPH_STRING Text;
BOOLEAN PropertyOfAllocationBase;
} Custom;
struct
{
PPH_STRING FileName;
BOOLEAN SigningLevelValid;
SE_SIGNING_LEVEL SigningLevel;
} MappedFile;
struct
{
HANDLE ThreadId;
} Teb;
struct
{
HANDLE ThreadId;
} Stack;
struct
{
ULONG Index;
BOOLEAN ClassValid;
ULONG Class;
} Heap;
struct
{
struct _PH_MEMORY_ITEM *HeapItem;
} HeapSegment;
struct
{
PH_ACTIVATION_CONTEXT_DATA_TYPE Type;
} ActivationContextData;
} u;
} PH_MEMORY_ITEM, *PPH_MEMORY_ITEM;
typedef struct _PH_MEMORY_ITEM_LIST
{
HANDLE ProcessId;
PH_AVL_TREE Set;
LIST_ENTRY ListHead;
} PH_MEMORY_ITEM_LIST, *PPH_MEMORY_ITEM_LIST;
// end_phapppub
VOID PhGetMemoryProtectionString(
_In_ ULONG Protection,
_Inout_z_ PWSTR String
);
PCPH_STRINGREF PhGetMemoryStateString(
_In_ ULONG State
);
PCPH_STRINGREF PhGetMemoryTypeString(
_In_ ULONG Type
);
PCPH_STRINGREF PhGetSigningLevelString(
_In_ SE_SIGNING_LEVEL SigningLevel
);
PCPH_STRINGREF PhGetMemoryPagePriorityString(
_In_ ULONG PagePriority
);
PPH_STRING PhGetMemoryRegionTypeExString(
_In_ PPH_MEMORY_ITEM MemoryItem
);
PPH_MEMORY_ITEM PhCreateMemoryItem(
VOID
);
// begin_phapppub
PHAPPAPI
VOID
NTAPI
PhDeleteMemoryItemList(
_In_ PPH_MEMORY_ITEM_LIST List
);
PHAPPAPI
PPH_MEMORY_ITEM
NTAPI
PhLookupMemoryItemList(
_In_ PPH_MEMORY_ITEM_LIST List,
_In_ PVOID Address
);
#define PH_QUERY_MEMORY_IGNORE_FREE 0x1
#define PH_QUERY_MEMORY_REGION_TYPE 0x2
#define PH_QUERY_MEMORY_WS_COUNTERS 0x4
#define PH_QUERY_MEMORY_ZERO_PAD_ADDRESSES 0x8
PHAPPAPI
NTSTATUS
NTAPI
PhQueryMemoryItemList(
_In_ HANDLE ProcessId,
_In_ ULONG Flags,
_Out_ PPH_MEMORY_ITEM_LIST List
);
// end_phapppub
#endif
================================================
FILE: SystemInformer/include/memsrch.h
================================================
#ifndef PH_MEMSRCH_H
#define PH_MEMSRCH_H
typedef struct _PH_MEMORY_RESULT
{
LONG RefCount;
PVOID Address;
PVOID BaseAddress;
SIZE_T Length;
PH_STRINGREF Display;
} PH_MEMORY_RESULT, *PPH_MEMORY_RESULT;
typedef VOID (NTAPI *PPH_MEMORY_RESULT_CALLBACK)(
_In_ _Assume_refs_(1) PPH_MEMORY_RESULT Result,
_In_opt_ PVOID Context
);
#define PH_DISPLAY_BUFFER_COUNT (PAGE_SIZE * 2 - 1)
typedef struct _PH_MEMORY_SEARCH_OPTIONS
{
BOOLEAN Cancel;
PPH_MEMORY_RESULT_CALLBACK Callback;
PVOID Context;
} PH_MEMORY_SEARCH_OPTIONS, *PPH_MEMORY_SEARCH_OPTIONS;
typedef struct _PH_MEMORY_STRING_OPTIONS
{
PH_MEMORY_SEARCH_OPTIONS Header;
ULONG MinimumLength;
ULONG MemoryTypeMask;
union
{
BOOLEAN Flags;
struct
{
BOOLEAN DetectUnicode : 1;
BOOLEAN ExtendedUnicode : 1;
BOOLEAN Spare : 6;
};
};
} PH_MEMORY_STRING_OPTIONS, *PPH_MEMORY_STRING_OPTIONS;
PVOID PhAllocateForMemorySearch(
_In_ SIZE_T Size
);
VOID PhFreeForMemorySearch(
_In_ _Post_invalid_ PVOID Memory
);
PVOID PhCreateMemoryResult(
_In_ PVOID Address,
_In_ PVOID BaseAddress,
_In_ SIZE_T Length
);
VOID PhReferenceMemoryResult(
_In_ PPH_MEMORY_RESULT Result
);
VOID PhDereferenceMemoryResult(
_In_ PPH_MEMORY_RESULT Result
);
VOID PhDereferenceMemoryResults(
_In_reads_(NumberOfResults) PPH_MEMORY_RESULT *Results,
_In_ ULONG NumberOfResults
);
#endif
================================================
FILE: SystemInformer/include/miniinfo.h
================================================
/*
* Copyright (c) 2022 Winsider Seminars & Solutions, Inc. All rights reserved.
*
* This file is part of System Informer.
*
* Authors:
*
* wj32 2015-2016
* dmex 2017-2023
*
*/
#ifndef PH_MINIINFO_H
#define PH_MINIINFO_H
#include
// begin_phapppub
// Section
typedef struct _PH_MINIINFO_SECTION PH_MINIINFO_SECTION, *PPH_MINIINFO_SECTION;
typedef VOID (NTAPI *PPH_MINIINFO_SET_SECTION_TEXT)(
_In_ PPH_MINIINFO_SECTION Section,
_In_opt_ PPH_STRING Text
);
typedef struct _PH_MINIINFO_PARAMETERS
{
HWND ContainerWindowHandle;
HWND MiniInfoWindowHandle;
HFONT Font;
HFONT MediumFont;
ULONG FontHeight;
ULONG FontAverageWidth;
ULONG MediumFontHeight;
ULONG MediumFontAverageWidth;
PPH_MINIINFO_SET_SECTION_TEXT SetSectionText;
} PH_MINIINFO_PARAMETERS, *PPH_MINIINFO_PARAMETERS;
typedef enum _PH_MINIINFO_SECTION_MESSAGE
{
MiniInfoCreate,
MiniInfoDestroy,
MiniInfoTick,
MiniInfoSectionChanging, // PPH_MINIINFO_SECTION Parameter1
MiniInfoShowing, // BOOLEAN Parameter1 (Showing)
MiniInfoCreateDialog, // PPH_MINIINFO_CREATE_DIALOG Parameter1
MaxMiniInfoMessage
} PH_MINIINFO_SECTION_MESSAGE;
typedef BOOLEAN (NTAPI *PPH_MINIINFO_SECTION_CALLBACK)(
_In_ PPH_MINIINFO_SECTION Section,
_In_ PH_MINIINFO_SECTION_MESSAGE Message,
_In_opt_ PVOID Parameter1,
_In_opt_ PVOID Parameter2
);
typedef struct _PH_MINIINFO_CREATE_DIALOG
{
BOOLEAN CustomCreate;
// Parameters for default create
PVOID Instance;
PWSTR Template;
DLGPROC DialogProc;
PVOID Parameter;
} PH_MINIINFO_CREATE_DIALOG, *PPH_MINIINFO_CREATE_DIALOG;
#define PH_MINIINFO_SECTION_NO_UPPER_MARGINS 0x1
// end_phapppub
// begin_phapppub
typedef struct _PH_MINIINFO_SECTION
{
// Public
// Initialization
PH_STRINGREF Name;
ULONG Flags;
PPH_MINIINFO_SECTION_CALLBACK Callback;
PVOID Context;
PVOID Reserved1[3];
PPH_MINIINFO_PARAMETERS Parameters;
PVOID Reserved2[3];
// end_phapppub
// Private
ULONG SpareFlag;
HWND DialogHandle;
PPH_STRING Text;
// begin_phapppub
} PH_MINIINFO_SECTION, *PPH_MINIINFO_SECTION;
// end_phapppub
typedef enum _PH_MINIINFO_PIN_TYPE
{
MiniInfoManualPinType, // User pin
MiniInfoIconPinType, // Notification icon
MiniInfoActivePinType, // Window is active
MiniInfoHoverPinType, // Cursor is over mini info window
MiniInfoChildControlPinType, // Interacting with child control
MaxMiniInfoPinType
} PH_MINIINFO_PIN_TYPE;
#define PH_MINIINFO_ACTIVATE_WINDOW 0x1
#define PH_MINIINFO_LOAD_POSITION 0x2
#define PH_MINIINFO_DONT_CHANGE_SECTION_IF_PINNED 0x4
VOID PhPinMiniInformation(
_In_ PH_MINIINFO_PIN_TYPE PinType,
_In_ LONG PinCount,
_In_opt_ ULONG PinDelayMs,
_In_ ULONG Flags,
_In_opt_ PCWSTR SectionName,
_In_opt_ PPOINT SourcePoint
);
// begin_phapppub
// List section
typedef enum _PH_MINIINFO_LIST_SECTION_MESSAGE
{
MiListSectionCreate,
MiListSectionDestroy,
MiListSectionTick,
MiListSectionShowing, // BOOLEAN Parameter1 (Showing)
MiListSectionDialogCreated, // HWND Parameter1 (DialogHandle)
MiListSectionSortProcessList, // PPH_MINIINFO_LIST_SECTION_SORT_LIST Parameter1
MiListSectionAssignSortData, // PPH_MINIINFO_LIST_SECTION_ASSIGN_SORT_DATA Parameter1
MiListSectionSortGroupList, // PPH_MINIINFO_LIST_SECTION_SORT_LIST Parameter1
MiListSectionGetTitleText, // PPH_MINIINFO_LIST_SECTION_GET_TITLE_TEXT Parameter1
MiListSectionGetUsageText, // PPH_MINIINFO_LIST_SECTION_GET_USAGE_TEXT Parameter1
MiListSectionInitializeContextMenu, // PPH_MINIINFO_LIST_SECTION_MENU_INFORMATION Parameter1
MiListSectionHandleContextMenu, // PPH_MINIINFO_LIST_SECTION_MENU_INFORMATION Parameter1
MaxMiListSectionMessage
} PH_MINIINFO_LIST_SECTION_MESSAGE;
typedef struct _PH_MINIINFO_LIST_SECTION PH_MINIINFO_LIST_SECTION, *PPH_MINIINFO_LIST_SECTION;
typedef BOOLEAN (NTAPI *PPH_MINIINFO_LIST_SECTION_CALLBACK)(
_In_ PPH_MINIINFO_LIST_SECTION ListSection,
_In_ PH_MINIINFO_LIST_SECTION_MESSAGE Message,
_In_opt_ PVOID Parameter1,
_In_opt_ PVOID Parameter2
);
// The list section performs the following steps when constructing the list of process groups:
// 1. MiListSectionSortProcessList is sent in order to sort the process list.
// 2. A small number of process groups is created from the first few processes in the sorted list (typically high
// resource consumers).
// 3. MiListSectionAssignSortData is sent for each process group so that the user can assign custom sort data to
// each process group.
// 4. MiListSectionSortGroupList is sent in order to ensure that the process groups are correctly sorted by resource
// usage.
// The user also has access to the sort data when handling MiListSectionGetTitleText and MiListSectionGetUsageText.
typedef struct _PH_MINIINFO_LIST_SECTION_SORT_DATA
{
PH_TREENEW_NODE DoNotModify;
ULONGLONG UserData[4];
} PH_MINIINFO_LIST_SECTION_SORT_DATA, *PPH_MINIINFO_LIST_SECTION_SORT_DATA;
typedef struct _PH_MINIINFO_LIST_SECTION_ASSIGN_SORT_DATA
{
PPH_PROCESS_GROUP ProcessGroup;
PPH_MINIINFO_LIST_SECTION_SORT_DATA SortData;
} PH_MINIINFO_LIST_SECTION_ASSIGN_SORT_DATA, *PPH_MINIINFO_LIST_SECTION_ASSIGN_SORT_DATA;
typedef struct _PH_MINIINFO_LIST_SECTION_SORT_LIST
{
// MiListSectionSortProcessList: List of PPH_PROCESS_NODE
// MiListSectionSortGroupList: List of PPH_MINIINFO_LIST_SECTION_SORT_DATA
PPH_LIST List;
} PH_MINIINFO_LIST_SECTION_SORT_LIST, *PPH_MINIINFO_LIST_SECTION_SORT_LIST;
typedef struct _PH_MINIINFO_LIST_SECTION_GET_TITLE_TEXT
{
PPH_PROCESS_GROUP ProcessGroup;
PPH_MINIINFO_LIST_SECTION_SORT_DATA SortData;
PPH_STRING Title; // Top line (may already contain a string)
PPH_STRING Subtitle; // Bottom line (may already contain a string)
COLORREF TitleColor;
COLORREF SubtitleColor;
} PH_MINIINFO_LIST_SECTION_GET_TITLE_TEXT, *PPH_MINIINFO_LIST_SECTION_GET_TITLE_TEXT;
typedef struct _PH_MINIINFO_LIST_SECTION_GET_USAGE_TEXT
{
PPH_PROCESS_GROUP ProcessGroup;
PPH_MINIINFO_LIST_SECTION_SORT_DATA SortData;
PPH_STRING Line1; // Top line
PPH_STRING Line2; // Bottom line
COLORREF Line1Color;
COLORREF Line2Color;
} PH_MINIINFO_LIST_SECTION_GET_USAGE_TEXT, *PPH_MINIINFO_LIST_SECTION_GET_USAGE_TEXT;
typedef struct _PH_MINIINFO_LIST_SECTION_MENU_INFORMATION
{
PPH_PROCESS_GROUP ProcessGroup;
PPH_MINIINFO_LIST_SECTION_SORT_DATA SortData;
PPH_TREENEW_CONTEXT_MENU ContextMenu;
PPH_EMENU_ITEM SelectedItem;
} PH_MINIINFO_LIST_SECTION_MENU_INFORMATION, *PPH_MINIINFO_LIST_SECTION_MENU_INFORMATION;
// end_phapppub
// begin_phapppub
typedef struct _PH_MINIINFO_LIST_SECTION
{
// Public
PPH_MINIINFO_SECTION Section; // State
HWND DialogHandle; // State
HWND TreeNewHandle; // State
PVOID Context; // Initialization
PPH_MINIINFO_LIST_SECTION_CALLBACK Callback; // Initialization
// end_phapppub
// Private
PH_LAYOUT_MANAGER LayoutManager;
ULONG RunCount;
LONG SuspendUpdate;
PPH_LIST ProcessGroupList;
PPH_LIST NodeList;
HANDLE SelectedRepresentativeProcessId;
LARGE_INTEGER SelectedRepresentativeCreateTime;
// begin_phapppub
} PH_MINIINFO_LIST_SECTION, *PPH_MINIINFO_LIST_SECTION;
// end_phapppub
#endif
================================================
FILE: SystemInformer/include/miniinfop.h
================================================
/*
* Copyright (c) 2022 Winsider Seminars & Solutions, Inc. All rights reserved.
*
* This file is part of System Informer.
*
* Authors:
*
* wj32 2015-2016
* dmex 2017-2023
*
*/
#ifndef PH_MINIINFOP_H
#define PH_MINIINFOP_H
// Constants
#define MIP_TIMER_PIN_FIRST 1
#define MIP_TIMER_PIN_LAST (MIP_TIMER_PIN_FIRST + MaxMiniInfoPinType - 1)
#define MIP_MSG_FIRST (WM_APP + 150)
#define MIP_MSG_UPDATE (WM_APP + 150)
#define MIP_MSG_LAST (WM_APP + 151)
#define MIP_UNPIN_CHILD_CONTROL_DELAY 1000
#define MIP_UNPIN_HOVER_DELAY 250
#define MIP_REFRESH_AUTOMATICALLY_PINNED 0x1
#define MIP_REFRESH_AUTOMATICALLY_UNPINNED 0x2
#define MIP_REFRESH_AUTOMATICALLY_FLAG(Pinned) \
((Pinned) ? MIP_REFRESH_AUTOMATICALLY_PINNED : MIP_REFRESH_AUTOMATICALLY_UNPINNED)
// Misc.
#define SET_BUTTON_ICON(hwndDlg, Id, Icon) \
SendMessage(GetDlgItem(hwndDlg, (Id)), BM_SETIMAGE, IMAGE_ICON, (LPARAM)(Icon))
// Dialog procedure
RTL_ATOM PhMipContainerInitializeWindowClass(
VOID
);
LRESULT CALLBACK PhMipContainerWndProc(
_In_ HWND hWnd,
_In_ UINT uMsg,
_In_ WPARAM wParam,
_In_ LPARAM lParam
);
INT_PTR CALLBACK PhMipMiniInfoDialogProc(
_In_ HWND hwndDlg,
_In_ UINT uMsg,
_In_ WPARAM wParam,
_In_ LPARAM lParam
);
// Container event handlers
VOID PhMipContainerOnShowWindow(
_In_ BOOLEAN Showing,
_In_ ULONG State
);
VOID PhMipContainerOnActivate(
_In_ ULONG Type,
_In_ BOOLEAN Minimized
);
VOID PhMipContainerOnSize(
VOID
);
VOID PhMipContainerOnSizing(
_In_ ULONG Edge,
_In_ PRECT DragRectangle
);
VOID PhMipContainerOnExitSizeMove(
VOID
);
BOOLEAN PhMipContainerOnEraseBkgnd(
_In_ HDC hdc
);
VOID PhMipContainerOnTimer(
_In_ ULONG Id
);
// Child dialog event handlers
VOID PhMipOnInitDialog(
VOID
);
VOID PhMipOnShowWindow(
_In_ BOOLEAN Showing,
_In_ ULONG State
);
VOID PhMipOnCommand(
_In_ ULONG Id,
_In_ ULONG Code
);
_Success_(return)
BOOLEAN
PhMipOnNotify(
_In_ NMHDR *Header,
_Out_ LRESULT *Result
);
_Success_(return)
BOOLEAN
PhMipOnCtlColorXxx(
_In_ ULONG Message,
_In_ HWND WindowHandle,
_In_ HDC hdc,
_Out_ HBRUSH *Brush
);
BOOLEAN PhMipOnDrawItem(
_In_ ULONG_PTR Id,
_In_ DRAWITEMSTRUCT *DrawItemStruct
);
VOID PhMipOnUserMessage(
_In_ ULONG Message,
_In_ ULONG_PTR WParam,
_In_ ULONG_PTR LParam
);
// Framework
typedef enum _PH_MIP_ADJUST_PIN_RESULT
{
NoAdjustPinResult,
ShowAdjustPinResult,
HideAdjustPinResult
} PH_MIP_ADJUST_PIN_RESULT;
BOOLEAN NTAPI PhMipMessageLoopFilter(
_In_ PMSG Message,
_In_ PVOID Context
);
_Function_class_(PH_CALLBACK_FUNCTION)
VOID NTAPI PhMipUpdateHandler(
_In_opt_ PVOID Parameter,
_In_opt_ PVOID Context
);
PH_MIP_ADJUST_PIN_RESULT PhMipAdjustPin(
_In_ PH_MINIINFO_PIN_TYPE PinType,
_In_ LONG PinCount
);
VOID PhMipCalculateWindowRectangle(
_In_ PPOINT SourcePoint,
_Out_ PPH_RECTANGLE WindowRectangle
);
VOID PhMipInitializeParameters(
VOID
);
_Function_class_(PH_MINIINFO_CREATE_SECTION)
PPH_MINIINFO_SECTION PhMipCreateSection(
_In_ PPH_MINIINFO_SECTION Template
);
VOID PhMipDestroySection(
_In_ PPH_MINIINFO_SECTION Section
);
_Function_class_(PH_MINIINFO_FIND_SECTION)
PPH_MINIINFO_SECTION PhMipFindSection(
_In_ PPH_STRINGREF Name
);
PPH_MINIINFO_SECTION PhMipCreateInternalSection(
_In_ PCWSTR Name,
_In_ ULONG Flags,
_In_ PPH_MINIINFO_SECTION_CALLBACK Callback
);
VOID PhMipCreateSectionDialog(
_In_ PPH_MINIINFO_SECTION Section
);
VOID PhMipChangeSection(
_In_ PPH_MINIINFO_SECTION NewSection
);
VOID PhMipSetSectionText(
_In_ struct _PH_MINIINFO_SECTION *Section,
_In_opt_ PPH_STRING Text
);
VOID PhMipUpdateSectionText(
_In_ PPH_MINIINFO_SECTION Section
);
VOID PhMipLayout(
VOID
);
VOID PhMipBeginChildControlPin(
VOID
);
VOID PhMipEndChildControlPin(
VOID
);
VOID PhMipRefresh(
VOID
);
VOID PhMipToggleRefreshAutomatically(
VOID
);
VOID PhMipSetPinned(
_In_ BOOLEAN Pinned,
_In_ BOOLEAN Update
);
VOID PhMipShowSectionMenu(
VOID
);
VOID PhMipShowOptionsMenu(
VOID
);
LRESULT CALLBACK PhMipSectionControlHookWndProc(
_In_ HWND WindowHandle,
_In_ UINT uMsg,
_In_ WPARAM wParam,
_In_ LPARAM lParam
);
// List-based section
#define MIP_MAX_PROCESS_GROUPS 15
#define MIP_SINGLE_COLUMN_ID 0
#define MIP_CELL_PADDING 5
#define MIP_ICON_PADDING 3
#define MIP_INNER_PADDING 3
typedef struct _PH_MIP_GROUP_NODE
{
union
{
PH_TREENEW_NODE Node;
PH_MINIINFO_LIST_SECTION_SORT_DATA SortData;
};
PPH_PROCESS_GROUP ProcessGroup;
HANDLE RepresentativeProcessId;
LARGE_INTEGER RepresentativeCreateTime;
BOOLEAN RepresentativeIsHung;
BOOLEAN RepresentativeIsTerminated;
PPH_STRING TooltipText;
} PH_MIP_GROUP_NODE, *PPH_MIP_GROUP_NODE;
PPH_MINIINFO_LIST_SECTION PhMipCreateListSection(
_In_ PCWSTR Name,
_In_ ULONG Flags,
_In_ PPH_MINIINFO_LIST_SECTION Template
);
PPH_MINIINFO_LIST_SECTION PhMipCreateInternalListSection(
_In_ PCWSTR Name,
_In_ ULONG Flags,
_In_ PPH_MINIINFO_LIST_SECTION_CALLBACK Callback
);
BOOLEAN PhMipListSectionCallback(
_In_ PPH_MINIINFO_SECTION Section,
_In_ PH_MINIINFO_SECTION_MESSAGE Message,
_In_ PVOID Parameter1,
_In_ PVOID Parameter2
);
INT_PTR CALLBACK PhMipListSectionDialogProc(
_In_ HWND hwndDlg,
_In_ UINT uMsg,
_In_ WPARAM wParam,
_In_ LPARAM lParam
);
VOID PhMipListSectionSortFunction(
_In_ PPH_LIST List,
_In_opt_ PVOID Context
);
VOID PhMipTickListSection(
_In_ PPH_MINIINFO_LIST_SECTION ListSection
);
VOID PhMipClearListSection(
_In_ PPH_MINIINFO_LIST_SECTION ListSection
);
LONG PhMipCalculateRowHeight(
_In_ HWND WindowHandle
);
PPH_MIP_GROUP_NODE PhMipAddGroupNode(
_In_ PPH_MINIINFO_LIST_SECTION ListSection,
_In_ PPH_PROCESS_GROUP ProcessGroup
);
VOID PhMipDestroyGroupNode(
_In_ PPH_MIP_GROUP_NODE Node
);
BOOLEAN PhMipListSectionTreeNewCallback(
_In_ HWND WindowHandle,
_In_ PH_TREENEW_MESSAGE Message,
_In_opt_ PVOID Parameter1,
_In_opt_ PVOID Parameter2,
_In_opt_ PVOID Context
);
PPH_STRING PhMipGetGroupNodeTooltip(
_In_ PPH_MINIINFO_LIST_SECTION ListSection,
_In_ PPH_MIP_GROUP_NODE Node
);
PPH_MIP_GROUP_NODE PhMipGetSelectedGroupNode(
_In_ PPH_MINIINFO_LIST_SECTION ListSection
);
VOID PhMipShowListSectionContextMenu(
_In_ PPH_MINIINFO_LIST_SECTION ListSection,
_In_ PPH_TREENEW_CONTEXT_MENU ContextMenu
);
VOID PhMipHandleListSectionCommand(
_In_ PPH_MINIINFO_LIST_SECTION ListSection,
_In_ PPH_PROCESS_GROUP ProcessGroup,
_In_ ULONG Id
);
// CPU section
BOOLEAN PhMipCpuListSectionCallback(
_In_ PPH_MINIINFO_LIST_SECTION ListSection,
_In_ PH_MINIINFO_LIST_SECTION_MESSAGE Message,
_In_opt_ PVOID Parameter1,
_In_opt_ PVOID Parameter2
);
int __cdecl PhMipCpuListSectionProcessCompareFunction(
_In_ const void *elem1,
_In_ const void *elem2
);
int __cdecl PhMipCpuListSectionNodeCompareFunction(
_In_ const void *elem1,
_In_ const void *elem2
);
// Commit charge section
BOOLEAN PhMipCommitListSectionCallback(
_In_ PPH_MINIINFO_LIST_SECTION ListSection,
_In_ PH_MINIINFO_LIST_SECTION_MESSAGE Message,
_In_opt_ PVOID Parameter1,
_In_opt_ PVOID Parameter2
);
int __cdecl PhMipCommitListSectionProcessCompareFunction(
_In_ const void *elem1,
_In_ const void *elem2
);
int __cdecl PhMipCommitListSectionNodeCompareFunction(
_In_ const void *elem1,
_In_ const void *elem2
);
// Physical memory section
BOOLEAN PhMipPhysicalListSectionCallback(
_In_ PPH_MINIINFO_LIST_SECTION ListSection,
_In_ PH_MINIINFO_LIST_SECTION_MESSAGE Message,
_In_opt_ PVOID Parameter1,
_In_opt_ PVOID Parameter2
);
int __cdecl PhMipPhysicalListSectionProcessCompareFunction(
_In_ const void *elem1,
_In_ const void *elem2
);
int __cdecl PhMipPhysicalListSectionNodeCompareFunction(
_In_ const void *elem1,
_In_ const void *elem2
);
// I/O section
BOOLEAN PhMipIoListSectionCallback(
_In_ PPH_MINIINFO_LIST_SECTION ListSection,
_In_ PH_MINIINFO_LIST_SECTION_MESSAGE Message,
_In_opt_ PVOID Parameter1,
_In_opt_ PVOID Parameter2
);
int __cdecl PhMipIoListSectionProcessCompareFunction(
_In_ const void *elem1,
_In_ const void *elem2
);
int __cdecl PhMipIoListSectionNodeCompareFunction(
_In_ const void *elem1,
_In_ const void *elem2
);
#endif
================================================
FILE: SystemInformer/include/modlist.h
================================================
/*
* Copyright (c) 2022 Winsider Seminars & Solutions, Inc. All rights reserved.
*
* This file is part of System Informer.
*
* Authors:
*
* wj32 2016
* dmex 2017-2023
*
*/
#ifndef PH_MODLIST_H
#define PH_MODLIST_H
#include
#include
// Columns
#define PHMOTLC_NAME 0
#define PHMOTLC_BASEADDRESS 1
#define PHMOTLC_SIZE 2
#define PHMOTLC_DESCRIPTION 3
#define PHMOTLC_COMPANYNAME 4
#define PHMOTLC_VERSION 5
#define PHMOTLC_FILENAME 6
#define PHMOTLC_TYPE 7
#define PHMOTLC_LOADCOUNT 8
#define PHMOTLC_VERIFICATIONSTATUS 9
#define PHMOTLC_VERIFIEDSIGNER 10
#define PHMOTLC_ASLR 11
#define PHMOTLC_TIMESTAMP 12
#define PHMOTLC_CFGUARD 13
#define PHMOTLC_LOADTIME 14
#define PHMOTLC_LOADREASON 15
#define PHMOTLC_FILEMODIFIEDTIME 16
#define PHMOTLC_FILESIZE 17
#define PHMOTLC_ENTRYPOINT 18
#define PHMOTLC_PARENTBASEADDRESS 19
#define PHMOTLC_CET 20
#define PHMOTLC_COHERENCY 21
#define PHMOTLC_TIMELINE 22
#define PHMOTLC_ORIGINALNAME 23
#define PHMOTLC_SERVICE 24
#define PHMOTLC_ENCLAVE_TYPE 25
#define PHMOTLC_ENCLAVE_BASE_ADDRESS 26
#define PHMOTLC_ENCLAVE_SIZE 27
#define PHMOTLC_ARCHITECTURE 28
#define PHMOTLC_MAXIMUM 29
// begin_phapppub
typedef struct _PH_MODULE_NODE
{
PH_TREENEW_NODE Node;
PH_SH_STATE ShState;
PPH_MODULE_ITEM ModuleItem;
// end_phapppub
PH_STRINGREF TextCache[PHMOTLC_MAXIMUM];
ULONG ValidMask;
PPH_STRING TooltipText;
PPH_STRING FileNameWin32;
PPH_STRING SizeText;
WCHAR LoadCountText[PH_INT32_STR_LEN_1];
PPH_STRING TimeStampText;
PPH_STRING LoadTimeText;
PPH_STRING FileModifiedTimeText;
PPH_STRING FileSizeText;
PPH_STRING ImageCoherencyText;
PPH_STRING ServiceText;
PPH_STRING EnclaveSizeText;
struct _PH_MODULE_NODE *Parent;
PPH_LIST Children;
// begin_phapppub
} PH_MODULE_NODE, *PPH_MODULE_NODE;
// end_phapppub
#define PH_MODULE_FLAGS_DYNAMIC_OPTION 1
#define PH_MODULE_FLAGS_MAPPED_OPTION 2
#define PH_MODULE_FLAGS_STATIC_OPTION 3
#define PH_MODULE_FLAGS_SIGNED_OPTION 4
#define PH_MODULE_FLAGS_HIGHLIGHT_UNSIGNED_OPTION 5
#define PH_MODULE_FLAGS_HIGHLIGHT_DOTNET_OPTION 6
#define PH_MODULE_FLAGS_HIGHLIGHT_IMMERSIVE_OPTION 7
#define PH_MODULE_FLAGS_HIGHLIGHT_RELOCATED_OPTION 8
#define PH_MODULE_FLAGS_LOAD_MODULE_OPTION 9
#define PH_MODULE_FLAGS_MODULE_STRINGS_OPTION 10
#define PH_MODULE_FLAGS_SYSTEM_OPTION 11
#define PH_MODULE_FLAGS_HIGHLIGHT_SYSTEM_OPTION 12
#define PH_MODULE_FLAGS_LOWIMAGECOHERENCY_OPTION 13
#define PH_MODULE_FLAGS_HIGHLIGHT_LOWIMAGECOHERENCY_OPTION 14
#define PH_MODULE_FLAGS_IMAGEKNOWNDLL_OPTION 15
#define PH_MODULE_FLAGS_HIGHLIGHT_IMAGEKNOWNDLL 16
#define PH_MODULE_FLAGS_ZERO_PAD_ADDRESSES 17
#define PH_MODULE_FLAGS_SAVE_OPTION 40 // Always last (dmex)
typedef struct _PH_MODULE_LIST_CONTEXT
{
HWND ParentWindowHandle;
HWND TreeNewHandle;
ULONG TreeNewSortColumn;
PH_TN_FILTER_SUPPORT TreeFilterSupport;
PH_SORT_ORDER TreeNewSortOrder;
PH_CM_MANAGER Cm;
HANDLE ProcessId;
LARGE_INTEGER ProcessCreateTime;
BOOLEAN HasServices;
BOOLEAN EnableStateHighlighting;
union
{
ULONG Flags;
struct
{
ULONG Reserved : 1;
ULONG HideDynamicModules : 1;
ULONG HideMappedModules : 1;
ULONG HideSignedModules : 1;
ULONG HideStaticModules : 1;
ULONG HighlightUntrustedModules : 1;
ULONG HighlightDotNetModules : 1;
ULONG HighlightImmersiveModules : 1;
ULONG HighlightRelocatedModules : 1;
ULONG HideSystemModules : 1;
ULONG HighlightSystemModules : 1;
ULONG HideLowImageCoherency : 1;
ULONG HighlightLowImageCoherency : 1;
ULONG HideImageKnownDll : 1;
ULONG HighlightImageKnownDll : 1;
ULONG ZeroPadAddresses : 1;
ULONG Spare : 16;
};
};
PPH_HASHTABLE NodeHashtable;
PPH_LIST NodeList;
PPH_LIST NodeRootList;
PPH_POINTER_LIST NodeStateList;
HFONT BoldFont;
} PH_MODULE_LIST_CONTEXT, *PPH_MODULE_LIST_CONTEXT;
VOID PhInitializeModuleList(
_In_ HWND ParentWindowHandle,
_In_ HWND TreeNewHandle,
_Out_ PPH_MODULE_LIST_CONTEXT Context
);
VOID PhDeleteModuleList(
_In_ PPH_MODULE_LIST_CONTEXT Context
);
VOID PhLoadSettingsModuleList(
_Inout_ PPH_MODULE_LIST_CONTEXT Context
);
VOID PhSaveSettingsModuleList(
_Inout_ PPH_MODULE_LIST_CONTEXT Context
);
VOID PhSetOptionsModuleList(
_Inout_ PPH_MODULE_LIST_CONTEXT Context,
_In_ ULONG Options
);
PPH_MODULE_NODE PhAddModuleNode(
_Inout_ PPH_MODULE_LIST_CONTEXT Context,
_In_ PPH_MODULE_ITEM ModuleItem,
_In_ ULONG RunId
);
PPH_MODULE_NODE PhFindModuleNode(
_In_ PPH_MODULE_LIST_CONTEXT Context,
_In_ PPH_MODULE_ITEM ModuleItem
);
VOID PhRemoveModuleNode(
_In_ PPH_MODULE_LIST_CONTEXT Context,
_In_ PPH_MODULE_NODE ModuleNode
);
VOID PhUpdateModuleNode(
_In_ PPH_MODULE_LIST_CONTEXT Context,
_In_ PPH_MODULE_NODE ModuleNode
);
VOID PhInvalidateAllModuleNodes(
_In_ PPH_MODULE_LIST_CONTEXT Context
);
VOID PhInvalidateAllModuleBaseAddressNodes(
_In_ PPH_MODULE_LIST_CONTEXT Context
);
VOID PhExpandAllModuleNodes(
_In_ PPH_MODULE_LIST_CONTEXT Context,
_In_ BOOLEAN Expand
);
VOID PhTickModuleNodes(
_In_ PPH_MODULE_LIST_CONTEXT Context
);
PPH_MODULE_ITEM PhGetSelectedModuleItem(
_In_ PPH_MODULE_LIST_CONTEXT Context
);
VOID PhGetSelectedModuleItems(
_In_ PPH_MODULE_LIST_CONTEXT Context,
_Out_ PPH_MODULE_ITEM **Modules,
_Out_ PULONG NumberOfModules
);
VOID PhDeselectAllModuleNodes(
_In_ PPH_MODULE_LIST_CONTEXT Context
);
BOOLEAN PhShouldShowModuleCoherency(
_In_ PPH_MODULE_ITEM ModuleItem,
_In_ BOOLEAN CheckThreshold
);
#endif
================================================
FILE: SystemInformer/include/modprv.h
================================================
/*
* Copyright (c) 2022 Winsider Seminars & Solutions, Inc. All rights reserved.
*
* This file is part of System Informer.
*
* Authors:
*
* wj32 2016
* dmex 2017-2023
*
*/
#ifndef PH_MODPRV_H
#define PH_MODPRV_H
extern PPH_OBJECT_TYPE PhModuleProviderType;
extern PPH_OBJECT_TYPE PhModuleItemType;
// begin_phapppub
typedef struct _PH_MODULE_ITEM
{
PVOID BaseAddress;
PVOID ParentBaseAddress;
PVOID EntryPoint;
ULONG Size;
ULONG Flags;
ULONG Type;
USHORT LoadReason;
USHORT LoadCount;
PPH_STRING Name;
PPH_STRING FileName;
PH_IMAGE_VERSION_INFO VersionInfo;
ULONG EnclaveType;
PVOID EnclaveBaseAddress;
SIZE_T EnclaveSize;
union
{
BOOLEAN StateFlags;
struct
{
BOOLEAN JustProcessed : 1;
BOOLEAN IsFirst : 1;
BOOLEAN ImageNotAtBase : 1;
BOOLEAN ImageKnownDll : 1;
BOOLEAN Spare : 4;
};
};
ULONG VerifyResult;
PPH_STRING VerifySignerName;
USHORT ImageMachine;
ULONG ImageCHPEVersion;
ULONG ImageTimeDateStamp;
USHORT ImageCharacteristics;
USHORT ImageDllCharacteristics;
ULONG ImageDllCharacteristicsEx;
ULONG GuardFlags;
LARGE_INTEGER LoadTime;
LARGE_INTEGER FileLastWriteTime;
LARGE_INTEGER FileEndOfFile;
NTSTATUS ImageCoherencyStatus;
FLOAT ImageCoherency;
WCHAR BaseAddressString[PH_PTR_STR_LEN_1];
WCHAR ParentBaseAddressString[PH_PTR_STR_LEN_1];
WCHAR EntryPointAddressString[PH_PTR_STR_LEN_1];
WCHAR EnclaveBaseAddressString[PH_PTR_STR_LEN_1];
} PH_MODULE_ITEM, *PPH_MODULE_ITEM;
typedef struct _PH_MODULE_PROVIDER
{
PPH_HASHTABLE ModuleHashtable;
PH_FAST_LOCK ModuleHashtableLock;
PH_CALLBACK ModuleAddedEvent;
PH_CALLBACK ModuleModifiedEvent;
PH_CALLBACK ModuleRemovedEvent;
PH_CALLBACK UpdatedEvent;
HANDLE ProcessId;
HANDLE ProcessHandle;
PPH_STRING ProcessFileName;
PPH_STRING PackageFullName;
SLIST_HEADER QueryListHead;
NTSTATUS RunStatus;
union
{
BOOLEAN Flags;
struct
{
BOOLEAN HaveFirst : 1;
BOOLEAN IsHandleValid : 1;
BOOLEAN IsSubsystemProcess : 1;
BOOLEAN ControlFlowGuardEnabled : 1;
BOOLEAN CetEnabled : 1;
BOOLEAN CetStrictModeEnabled : 1;
BOOLEAN ZeroPadAddresses : 1;
BOOLEAN Spare : 1;
};
};
UCHAR ImageCoherencyScanLevel;
} PH_MODULE_PROVIDER, *PPH_MODULE_PROVIDER;
// end_phapppub
PPH_MODULE_PROVIDER PhCreateModuleProvider(
_In_ HANDLE ProcessId
);
PPH_MODULE_ITEM PhCreateModuleItem(
VOID
);
PPH_MODULE_ITEM PhReferenceModuleItemEx(
_In_ PPH_MODULE_PROVIDER ModuleProvider,
_In_ PVOID BaseAddress,
_In_opt_ PVOID EnclaveBaseAddress,
_In_opt_ PPH_STRING FileName
);
PPH_MODULE_ITEM PhReferenceModuleItem(
_In_ PPH_MODULE_PROVIDER ModuleProvider,
_In_ PVOID BaseAddress
);
VOID PhDereferenceAllModuleItems(
_In_ PPH_MODULE_PROVIDER ModuleProvider
);
_Function_class_(PH_PROVIDER_FUNCTION)
VOID PhModuleProviderUpdate(
_In_ PVOID Object
);
PCPH_STRINGREF PhGetModuleTypeName(
_In_ ULONG ModuleType
);
PCPH_STRINGREF PhGetModuleLoadReasonTypeName(
_In_ USHORT LoadReason
);
PCPH_STRINGREF PhGetModuleEnclaveTypeName(
_In_ ULONG EnclaveType
);
#endif
================================================
FILE: SystemInformer/include/netlist.h
================================================
/*
* Copyright (c) 2022 Winsider Seminars & Solutions, Inc. All rights reserved.
*
* This file is part of System Informer.
*
* Authors:
*
* wj32 2010-2016
* dmex 2017-2023
*
*/
#ifndef PH_NETLIST_H
#define PH_NETLIST_H
#include
// Columns
#define PHNETLC_PROCESS 0
#define PHNETLC_PID 1
#define PHNETLC_LOCALADDRESS 2
#define PHNETLC_LOCALPORT 3
#define PHNETLC_REMOTEADDRESS 4
#define PHNETLC_REMOTEPORT 5
#define PHNETLC_PROTOCOL 6
#define PHNETLC_STATE 7
#define PHNETLC_OWNER 8
#define PHNETLC_TIMESTAMP 9
#define PHNETLC_LOCALHOSTNAME 10
#define PHNETLC_REMOTEHOSTNAME 11
#define PHNETLC_TIMELINE 12
#define PHNETLC_HV_SERVICE 13
#define PHNETLC_MAXIMUM 14
// begin_phapppub
typedef struct _PH_NETWORK_NODE
{
PH_TREENEW_NODE Node;
PH_SH_STATE ShState;
PPH_NETWORK_ITEM NetworkItem;
PPH_STRING ProcessNameText;
PPH_STRING TimeStampText;
WCHAR ProcessIdString[PH_INT32_STR_LEN_1];
// end_phapppub
PPH_STRING TooltipText;
ULONG64 UniqueId;
PH_STRINGREF TextCache[PHNETLC_MAXIMUM];
// begin_phapppub
} PH_NETWORK_NODE, *PPH_NETWORK_NODE;
// end_phapppub
VOID PhNetworkTreeListInitialization(
VOID
);
VOID PhInitializeNetworkTreeList(
_In_ HWND TreeNewHandle
);
VOID PhLoadSettingsNetworkTreeList(
VOID
);
VOID PhSaveSettingsNetworkTreeList(
VOID
);
// begin_phapppub
PHAPPAPI
PPH_TN_FILTER_SUPPORT
NTAPI
PhGetFilterSupportNetworkTreeList(
VOID
);
// end_phapppub
PPH_NETWORK_NODE PhAddNetworkNode(
_In_ PPH_NETWORK_ITEM NetworkItem,
_In_ ULONG RunId
);
// begin_phapppub
PHAPPAPI
PPH_NETWORK_NODE
NTAPI
PhFindNetworkNode(
_In_ PPH_NETWORK_ITEM NetworkItem
);
// end_phapppub
VOID PhRemoveNetworkNode(
_In_ PPH_NETWORK_NODE NetworkNode
);
VOID PhUpdateNetworkNode(
_In_ PPH_NETWORK_NODE NetworkNode
);
VOID PhTickNetworkNodes(
VOID
);
PPH_NETWORK_ITEM PhGetSelectedNetworkItem(
VOID
);
VOID PhGetSelectedNetworkItems(
_Out_ PPH_NETWORK_ITEM **NetworkItems,
_Out_ PULONG NumberOfNetworkItems
);
// begin_phapppub
VOID PhDeselectAllNetworkNodes(
VOID
);
// end_phapppub
BOOLEAN PhSelectAndEnsureVisibleNetworkNode(
_In_ PPH_NETWORK_NODE NetworkNode
);
VOID PhInvalidateAllNetworkNodes(
VOID
);
VOID PhInvalidateAllNetworkNodesHostnames(
VOID
);
VOID PhCopyNetworkList(
VOID
);
VOID PhWriteNetworkList(
_Inout_ PPH_FILE_STREAM FileStream,
_In_ ULONG Mode
);
PPH_LIST PhDuplicateNetworkNodeList(
VOID
);
#endif
================================================
FILE: SystemInformer/include/netprv.h
================================================
/*
* Copyright (c) 2022 Winsider Seminars & Solutions, Inc. All rights reserved.
*
* This file is part of System Informer.
*
* Authors:
*
* wj32 2016
* dmex 2017-2023
*
*/
#ifndef PH_NETPRV_H
#define PH_NETPRV_H
extern PPH_OBJECT_TYPE PhNetworkItemType;
extern BOOLEAN PhEnableNetworkProviderResolve;
extern BOOLEAN PhEnableNetworkBoundConnections;
typedef enum _PH_NETWORK_PROVIDER_FLAG
{
PH_NETWORK_PROVIDER_FLAG_HOSTNAME = 0x1,
} PH_NETWORK_PROVIDER_FLAG;
extern ULONG PhNetworkProviderFlagsMask;
// begin_phapppub
#define PH_NETWORK_OWNER_INFO_SIZE 16
typedef struct _PH_NETWORK_ITEM
{
ULONG ProtocolType;
PH_IP_ENDPOINT LocalEndpoint;
PH_IP_ENDPOINT RemoteEndpoint;
MIB_TCP_STATE State;
HANDLE ProcessId;
PPH_STRING ProcessName;
ULONG_PTR ProcessIconIndex;
BOOLEAN ProcessIconValid;
PPH_STRING OwnerName;
volatile LONG JustResolved;
PPH_STRING LocalAddressString;
WCHAR LocalPortString[PH_INT32_STR_LEN_1];
PPH_STRING RemoteAddressString;
WCHAR RemotePortString[PH_INT32_STR_LEN_1];
PPH_STRING LocalHostString;
PPH_STRING RemoteHostString;
PPH_STRING HvService;
LARGE_INTEGER CreateTime;
ULONG LocalScopeId;
ULONG RemoteScopeId;
union
{
ULONG Flags;
struct
{
ULONG UnknownProcess : 1;
ULONG SubsystemProcess : 1;
ULONG Spare : 27;
ULONG InvalidateHostname : 1;
ULONG LocalHostnameResolved : 1;
ULONG RemoteHostnameResolved : 1;
};
};
PPH_PROCESS_ITEM ProcessItem;
} PH_NETWORK_ITEM, *PPH_NETWORK_ITEM;
// end_phapppub
BOOLEAN PhNetworkProviderInitialization(
VOID
);
PPH_NETWORK_ITEM PhCreateNetworkItem(
VOID
);
// begin_phapppub
PHAPPAPI
PPH_NETWORK_ITEM
NTAPI
PhReferenceNetworkItem(
_In_ ULONG ProtocolType,
_In_ PPH_IP_ENDPOINT LocalEndpoint,
_In_ PPH_IP_ENDPOINT RemoteEndpoint,
_In_ HANDLE ProcessId
);
PHAPPAPI
VOID
NTAPI
PhEnumNetworkItems(
_Out_opt_ PPH_NETWORK_ITEM** NetworkItems,
_Out_ PULONG NumberOfNetworkItems
);
PHAPPAPI
VOID
NTAPI
PhEnumNetworkItemsByProcessId(
_In_opt_ HANDLE ProcessId,
_Out_opt_ PPH_NETWORK_ITEM** NetworkItems,
_Out_ PULONG NumberOfNetworkItems
);
// end_phapppub
VOID PhFlushNetworkItemResolveCache(
VOID
);
//PPH_STRING PhGetHostNameFromAddress(
// _In_ PPH_IP_ADDRESS Address
// );
_Function_class_(PH_PROVIDER_FUNCTION)
VOID PhNetworkProviderUpdate(
_In_ PVOID Object
);
// begin_phapppub
PHAPPAPI
PCPH_STRINGREF
NTAPI
PhGetProtocolTypeName(
_In_ ULONG ProtocolType
);
PHAPPAPI
PCPH_STRINGREF
NTAPI
PhGetTcpStateName(
_In_ ULONG State
);
// end_phapppub
// iphlpapi imports
typedef ULONG (WINAPI *_GetExtendedTcpTable)(
_Out_writes_bytes_opt_(*pdwSize) PVOID pTcpTable,
_Inout_ PULONG pdwSize,
_In_ BOOL bOrder,
_In_ ULONG ulAf,
_In_ TCP_TABLE_CLASS TableClass,
_In_ ULONG Reserved
);
typedef ULONG (WINAPI *_GetExtendedUdpTable)(
_Out_writes_bytes_opt_(*pdwSize) PVOID pUdpTable,
_Inout_ PULONG pdwSize,
_In_ BOOL bOrder,
_In_ ULONG ulAf,
_In_ UDP_TABLE_CLASS TableClass,
_In_ ULONG Reserved
);
//DECLSPEC_IMPORT ULONG WINAPI InternalGetTcpTableWithOwnerModule(
// _Out_ PVOID* Tcp4Table, // PMIB_TCPTABLE_OWNER_MODULE
// _In_ PVOID HeapHandle,
// _In_opt_ ULONG HeapFlags
// );
//DECLSPEC_IMPORT ULONG WINAPI InternalGetTcp6TableWithOwnerModule(
// _Out_ PVOID* Tcp6Table, // PMIB_TCP6TABLE_OWNER_MODULE
// _In_ PVOID HeapHandle,
// _In_opt_ ULONG HeapFlags
// );
//DECLSPEC_IMPORT ULONG WINAPI InternalGetUdpTableWithOwnerModule(
// _Out_ PVOID* Udp4Table, // PMIB_UDPTABLE_OWNER_MODULE
// _In_ PVOID HeapHandle,
// _In_opt_ ULONG HeapFlags
// );
//DECLSPEC_IMPORT ULONG WINAPI InternalGetUdp6TableWithOwnerModule(
// _Out_ PVOID* Udp6Table, // PMIB_UDP6TABLE_OWNER_MODULE
// _In_ PVOID HeapHandle,
// _In_opt_ ULONG HeapFlags
// );
typedef ULONG (WINAPI *_InternalGetBoundTcpEndpointTable)(
_Out_ _When_(return!=0, _Notnull_) PVOID* BoundTcpTable, // PMIB_TCPTABLE2
_In_ PVOID HeapHandle,
_In_opt_ ULONG HeapFlags
);
typedef ULONG (WINAPI *_InternalGetBoundTcp6EndpointTable)(
_Out_ _When_(return!=0, _Notnull_) PVOID* BoundTcpTable, // PMIB_TCP6TABLE2
_In_ PVOID HeapHandle,
_In_opt_ ULONG HeapFlags
);
// netsup
NTSTATUS
NTAPI
PhSetTcpEntry(
_In_ PPH_NETWORK_ITEM NetworkItem
);
#endif
================================================
FILE: SystemInformer/include/notifico.h
================================================
/*
* Copyright (c) 2022 Winsider Seminars & Solutions, Inc. All rights reserved.
*
* This file is part of System Informer.
*
* Authors:
*
* wj32 2011-2016
* dmex 2017-2023
*
*/
#ifndef PH_NOTIFICO_H
#define PH_NOTIFICO_H
extern PPH_LIST PhTrayIconItemList;
typedef enum _PH_TRAY_ICON_ID
{
PH_TRAY_ICON_ID_NONE,
PH_TRAY_ICON_ID_CPU_USAGE,
PH_TRAY_ICON_ID_CPU_HISTORY,
PH_TRAY_ICON_ID_IO_HISTORY,
PH_TRAY_ICON_ID_COMMIT_HISTORY,
PH_TRAY_ICON_ID_PHYSICAL_HISTORY,
PH_TRAY_ICON_ID_CPU_TEXT,
PH_TRAY_ICON_ID_IO_TEXT,
PH_TRAY_ICON_ID_COMMIT_TEXT,
PH_TRAY_ICON_ID_PHYSICAL_TEXT,
PH_TRAY_ICON_ID_PLAIN_ICON,
PH_TRAY_ICON_ID_MAXIMUM
} PH_TRAY_ICON_ID;
typedef enum _PH_TRAY_ICON_GUID
{
PH_TRAY_ICON_GUID_CPU_USAGE,
PH_TRAY_ICON_GUID_CPU_HISTORY,
PH_TRAY_ICON_GUID_IO_HISTORY,
PH_TRAY_ICON_GUID_COMMIT_HISTORY,
PH_TRAY_ICON_GUID_PHYSICAL_HISTORY,
PH_TRAY_ICON_GUID_CPU_TEXT,
PH_TRAY_ICON_GUID_IO_TEXT,
PH_TRAY_ICON_GUID_COMMIT_TEXT,
PH_TRAY_ICON_GUID_PHYSICAL_TEXT,
PH_TRAY_ICON_GUID_PLAIN_ICON,
PH_TRAY_ICON_GUID_MAXIMUM
} PH_TRAY_ICON_GUID;
#define PH_TRAY_ICON_ID_PLUGIN 0x80
#define PH_ICON_LIMIT 0x80000000
#define PH_ICON_ALL 0xffffffff
// begin_phapppub
typedef struct _PH_NF_ICON PH_NF_ICON, *PPH_NF_ICON;
typedef _Function_class_(PH_NF_UPDATE_REGISTERED_ICON)
VOID NTAPI PH_NF_UPDATE_REGISTERED_ICON(
_In_ PPH_NF_ICON Icon
);
typedef PH_NF_UPDATE_REGISTERED_ICON* PPH_NF_UPDATE_REGISTERED_ICON;
typedef _Function_class_(PH_NF_BEGIN_BITMAP)
VOID NTAPI PH_NF_BEGIN_BITMAP(
_Out_ PULONG Width,
_Out_ PULONG Height,
_Out_ HBITMAP *Bitmap,
_Out_opt_ PVOID *Bits,
_Out_ HDC *Hdc,
_Out_ HBITMAP *OldBitmap
);
typedef PH_NF_BEGIN_BITMAP* PPH_NF_BEGIN_BITMAP;
typedef struct _PH_NF_POINTERS
{
PPH_NF_BEGIN_BITMAP BeginBitmap;
} PH_NF_POINTERS, *PPH_NF_POINTERS;
#define PH_NF_UPDATE_IS_BITMAP 0x1
#define PH_NF_UPDATE_DESTROY_RESOURCE 0x2
typedef _Function_class_(PH_NF_ICON_UPDATE_CALLBACK)
VOID NTAPI PH_NF_ICON_UPDATE_CALLBACK(
_In_ PPH_NF_ICON Icon,
_Out_ PVOID *NewIconOrBitmap,
_Out_ PULONG Flags,
_Out_ PPH_STRING *NewText,
_In_opt_ PVOID Context
);
typedef PH_NF_ICON_UPDATE_CALLBACK* PPH_NF_ICON_UPDATE_CALLBACK;
typedef _Function_class_(PH_NF_ICON_MESSAGE_CALLBACK)
BOOLEAN NTAPI PH_NF_ICON_MESSAGE_CALLBACK(
_In_ PPH_NF_ICON Icon,
_In_ ULONG_PTR WParam,
_In_ ULONG_PTR LParam,
_In_opt_ PVOID Context
);
typedef PH_NF_ICON_MESSAGE_CALLBACK* PPH_NF_ICON_MESSAGE_CALLBACK;
// Special messages
// The message type is stored in LOWORD(LParam), and the message data is in WParam.
#define PH_NF_MSG_SHOWMINIINFOSECTION (WM_APP + 1)
typedef struct _PH_NF_MSG_SHOWMINIINFOSECTION_DATA
{
PWSTR SectionName; // NULL to leave unchanged
} PH_NF_MSG_SHOWMINIINFOSECTION_DATA, *PPH_NF_MSG_SHOWMINIINFOSECTION_DATA;
// Structures and internal functions
#define PH_NF_ICON_ENABLED 0x1
#define PH_NF_ICON_UNAVAILABLE 0x2
#define PH_NF_ICON_NOSHOW_MINIINFO 0x4
// end_phapppub
// begin_phapppub
typedef struct _PH_PLUGIN PH_PLUGIN, *PPH_PLUGIN;
typedef struct _PH_NF_ICON
{
// Public
PPH_PLUGIN Plugin;
ULONG SubId;
PVOID Context;
PPH_NF_POINTERS Pointers;
// end_phapppub
// Private
PCWSTR Text;
ULONG Flags;
ULONG IconId;
GUID IconGuid;
PPH_NF_ICON_UPDATE_CALLBACK UpdateCallback;
PPH_NF_ICON_MESSAGE_CALLBACK MessageCallback;
PPH_STRING TextCache;
// begin_phapppub
} PH_NF_ICON, *PPH_NF_ICON;
// end_phapppub
VOID PhNfLoadStage1(
VOID
);
VOID PhNfLoadStage2(
VOID
);
VOID PhNfSaveSettings(
VOID
);
VOID PhNfUninitialization(
VOID
);
VOID PhNfForwardMessage(
_In_ HWND WindowHandle,
_In_ ULONG_PTR WParam,
_In_ ULONG_PTR LParam
);
VOID PhNfSetVisibleIcon(
_In_ PPH_NF_ICON Icon,
_In_ BOOLEAN Visible
);
BOOLEAN PhNfShowBalloonTip(
_In_ PCWSTR Title,
_In_ PCWSTR Text,
_In_ ULONG Timeout
);
HRESULT PhNfShowBalloonTipEx(
_In_ PCWSTR Title,
_In_ PCWSTR Text,
_In_ ULONG Timeout,
_In_opt_ PPH_TOAST_CALLBACK ToastCallback,
_In_opt_ PVOID Context
);
HICON PhNfBitmapToIcon(
_In_ HBITMAP Bitmap
);
PPH_NF_ICON PhNfRegisterIcon(
_In_opt_ PPH_PLUGIN Plugin,
_In_ ULONG Id,
_In_ GUID Guid,
_In_opt_ PVOID Context,
_In_ PCWSTR Text,
_In_ ULONG Flags,
_In_opt_ PPH_NF_ICON_UPDATE_CALLBACK UpdateCallback,
_In_opt_ PPH_NF_ICON_MESSAGE_CALLBACK MessageCallback
);
PPH_NF_ICON PhNfGetIconById(
_In_ ULONG Id
);
PPH_NF_ICON PhNfFindIcon(
_In_ PPH_STRINGREF PluginName,
_In_ ULONG SubId
);
BOOLEAN PhNfIconsEnabled(
VOID
);
VOID PhNfNotifyMiniInfoPinned(
_In_ BOOLEAN Pinned
);
// begin_phapppub
// Public registration data
typedef struct _PH_NF_ICON_REGISTRATION_DATA
{
PPH_NF_ICON_UPDATE_CALLBACK UpdateCallback;
PPH_NF_ICON_MESSAGE_CALLBACK MessageCallback;
} PH_NF_ICON_REGISTRATION_DATA, *PPH_NF_ICON_REGISTRATION_DATA;
VOID PhDrawTrayIconText(
_In_ HDC hdc,
_In_ PVOID Bits,
_Inout_ PPH_GRAPH_DRAW_INFO DrawInfo,
_In_ PPH_STRINGREF Text
);
HFONT PhNfGetTrayIconFont(
_In_opt_ LONG DpiValue
);
// end_phapppub
#endif
================================================
FILE: SystemInformer/include/notificop.h
================================================
/*
* Copyright (c) 2022 Winsider Seminars & Solutions, Inc. All rights reserved.
*
* This file is part of System Informer.
*
* Authors:
*
* wj32 2011-2016
* dmex 2017-2023
*
*/
#ifndef PH_NOTIFICOP_H
#define PH_NOTIFICOP_H
#define PH_NF_ENABLE_WORKQUEUE 1
typedef struct _PH_NF_WORKQUEUE_DATA
{
SLIST_ENTRY ListEntry;
PPH_NF_ICON Icon;
union
{
ULONG Flags;
struct
{
ULONG Add : 1;
ULONG Delete : 1;
ULONG Update : 1;
ULONG ShowBalloon : 1;
ULONG Spare : 28;
};
};
PPH_STRING BalloonTitle;
PPH_STRING BalloonText;
ULONG BalloonTimeout;
} PH_NF_WORKQUEUE_DATA, *PPH_NF_WORKQUEUE_DATA;
typedef struct _PH_NF_BITMAP
{
BOOLEAN Initialized;
HDC Hdc;
HBITMAP Bitmap;
LPRGBQUAD Bits;
LONG Width;
LONG Height;
LONG TaskbarDpi;
} PH_NF_BITMAP, *PPH_NF_BITMAP;
HICON PhNfGetApplicationIcon(
_In_opt_ LONG DpiValue
);
HICON PhNfpGetBlackIcon(
VOID
);
BOOLEAN PhNfpAddNotifyIcon(
_In_ PPH_NF_ICON Icon
);
BOOLEAN PhNfpRemoveNotifyIcon(
_In_ PPH_NF_ICON Icon
);
BOOLEAN PhNfpModifyNotifyIcon(
_In_ PPH_NF_ICON Icon,
_In_ ULONG Flags,
_In_opt_ PPH_STRING Text,
_In_opt_ HICON IconHandle
);
_Function_class_(USER_THREAD_START_ROUTINE)
NTSTATUS PhNfpTrayIconUpdateThread(
_In_opt_ PVOID Context
);
_Function_class_(PH_CALLBACK_FUNCTION)
VOID PhNfpProcessesUpdatedHandler(
_In_opt_ PVOID Parameter,
_In_opt_ PVOID Context
);
VOID PhNfpUpdateRegisteredIcon(
_In_ PPH_NF_ICON Icon
);
_Function_class_(PH_NF_BEGIN_BITMAP)
VOID PhNfpBeginBitmap(
_Out_ PULONG Width,
_Out_ PULONG Height,
_Out_ HBITMAP *Bitmap,
_Out_opt_ PVOID *Bits,
_Out_ HDC *Hdc,
_Out_ HBITMAP *OldBitmap
);
_Function_class_(PH_NF_ICON_UPDATE_CALLBACK)
VOID PhNfpBeginBitmap2(
_Inout_ PPH_NF_BITMAP Context,
_Out_ PULONG Width,
_Out_ PULONG Height,
_Out_ HBITMAP *Bitmap,
_Out_opt_ PVOID *Bits,
_Out_ HDC *Hdc,
_Out_ HBITMAP *OldBitmap
);
_Function_class_(PH_NF_ICON_UPDATE_CALLBACK)
VOID PhNfpCpuHistoryIconUpdateCallback(
_In_ PPH_NF_ICON Icon,
_Out_ PVOID *NewIconOrBitmap,
_Out_ PULONG Flags,
_Out_ PPH_STRING *NewText,
_In_opt_ PVOID Context
);
_Function_class_(PH_NF_ICON_UPDATE_CALLBACK)
VOID PhNfpIoHistoryIconUpdateCallback(
_In_ PPH_NF_ICON Icon,
_Out_ PVOID *NewIconOrBitmap,
_Out_ PULONG Flags,
_Out_ PPH_STRING *NewText,
_In_opt_ PVOID Context
);
_Function_class_(PH_NF_ICON_UPDATE_CALLBACK)
VOID PhNfpCommitHistoryIconUpdateCallback(
_In_ PPH_NF_ICON Icon,
_Out_ PVOID *NewIconOrBitmap,
_Out_ PULONG Flags,
_Out_ PPH_STRING *NewText,
_In_opt_ PVOID Context
);
_Function_class_(PH_NF_ICON_UPDATE_CALLBACK)
VOID PhNfpPhysicalHistoryIconUpdateCallback(
_In_ PPH_NF_ICON Icon,
_Out_ PVOID *NewIconOrBitmap,
_Out_ PULONG Flags,
_Out_ PPH_STRING *NewText,
_In_opt_ PVOID Context
);
_Function_class_(PH_NF_ICON_UPDATE_CALLBACK)
VOID PhNfpCpuUsageIconUpdateCallback(
_In_ PPH_NF_ICON Icon,
_Out_ PVOID *NewIconOrBitmap,
_Out_ PULONG Flags,
_Out_ PPH_STRING *NewText,
_In_opt_ PVOID Context
);
// Text icons
_Function_class_(PH_NF_ICON_UPDATE_CALLBACK)
VOID PhNfpCpuUsageTextIconUpdateCallback(
_In_ PPH_NF_ICON Icon,
_Out_ PVOID *NewIconOrBitmap,
_Out_ PULONG Flags,
_Out_ PPH_STRING *NewText,
_In_opt_ PVOID Context
);
_Function_class_(PH_NF_ICON_UPDATE_CALLBACK)
VOID PhNfpIoUsageTextIconUpdateCallback(
_In_ PPH_NF_ICON Icon,
_Out_ PVOID *NewIconOrBitmap,
_Out_ PULONG Flags,
_Out_ PPH_STRING *NewText,
_In_opt_ PVOID Context
);
_Function_class_(PH_NF_ICON_UPDATE_CALLBACK)
VOID PhNfpCommitTextIconUpdateCallback(
_In_ PPH_NF_ICON Icon,
_Out_ PVOID *NewIconOrBitmap,
_Out_ PULONG Flags,
_Out_ PPH_STRING *NewText,
_In_opt_ PVOID Context
);
_Function_class_(PH_NF_ICON_UPDATE_CALLBACK)
VOID PhNfpPhysicalUsageTextIconUpdateCallback(
_In_ PPH_NF_ICON Icon,
_Out_ PVOID *NewIconOrBitmap,
_Out_ PULONG Flags,
_Out_ PPH_STRING *NewText,
_In_opt_ PVOID Context
);
// plain icon
_Function_class_(PH_NF_ICON_UPDATE_CALLBACK)
VOID PhNfpPlainIconUpdateCallback(
_In_ PPH_NF_ICON Icon,
_Out_ PVOID *NewIconOrBitmap,
_Out_ PULONG Flags,
_Out_ PPH_STRING *NewText,
_In_opt_ PVOID Context
);
_Success_(return)
BOOLEAN PhNfpGetShowMiniInfoSectionData(
_In_ ULONG IconIndex,
_In_ PPH_NF_ICON RegisteredIcon,
_Out_ PPH_NF_MSG_SHOWMINIINFOSECTION_DATA Data
);
#define NFP_ICON_CLICK_ACTIVATE_DELAY 140
#define NFP_ICON_RESTORE_HOVER_DELAY 1000
VOID PhNfpIconClickActivateTimerProc(
_In_ HWND WindowHandle,
_In_ UINT uMsg,
_In_ UINT_PTR idEvent,
_In_ ULONG dwTime
);
VOID PhNfpDisableHover(
VOID
);
VOID PhNfpIconRestoreHoverTimerProc(
_In_ HWND WindowHandle,
_In_ UINT uMsg,
_In_ UINT_PTR idEvent,
_In_ ULONG dwTime
);
VOID PhNfpIconDisablePopupHoverWin11Workaround(
VOID
);
VOID PhNfpIconShowPopupHoverTimerProc(
_In_ HWND WindowHandle,
_In_ UINT uMsg,
_In_ UINT_PTR idEvent,
_In_ ULONG dwTime
);
#endif
================================================
FILE: SystemInformer/include/notiftoast.h
================================================
/*
* Copyright (c) 2022 Winsider Seminars & Solutions, Inc. All rights reserved.
*
* This file is part of System Informer.
*
* Authors:
*
* jxy-s 2021
*
*/
#pragma once
#ifndef PH_NOTIFTOAST_H
#define PH_NOTIFTOAST_H
EXTERN_C_START
_Must_inspect_result_
HRESULT
NTAPI
PhInitializeToastRuntime();
VOID
NTAPI
PhUninitializeToastRuntime();
_Must_inspect_result_
HRESULT
NTAPI
PhShowToast(
_In_ PCWSTR ApplicationId,
_In_ PCWSTR ToastXml,
_In_opt_ ULONG TimeoutMilliseconds,
_In_opt_ PPH_TOAST_CALLBACK ToastCallback,
_In_opt_ PVOID Context
);
_Must_inspect_result_
HRESULT
NTAPI
PhShowToastStringRef(
_In_ PPH_STRINGREF ApplicationId,
_In_ PPH_STRINGREF ToastXml,
_In_opt_ ULONG TimeoutMilliseconds,
_In_opt_ PPH_TOAST_CALLBACK ToastCallback,
_In_opt_ PVOID Context
);
EXTERN_C_END
#endif
================================================
FILE: SystemInformer/include/phapp.h
================================================
/*
* Copyright (c) 2022 Winsider Seminars & Solutions, Inc. All rights reserved.
*
* This file is part of System Informer.
*
* Authors:
*
* wj32 2010-2016
* dmex 2017-2024
*
*/
#ifndef PHAPP_H
#define PHAPP_H
#if !defined(_PHAPP_)
#define PHAPPAPI __declspec(dllimport)
#else
#define PHAPPAPI __declspec(dllexport)
#endif
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "../resource.h"
#include
#include
#include
// main
typedef struct _PH_STARTUP_PARAMETERS
{
union
{
struct
{
ULONG NoSettings : 1;
ULONG ShowVisible : 1;
ULONG ShowHidden : 1;
ULONG NoKph : 1;
ULONG Debug : 1;
ULONG ShowOptions : 1;
ULONG PhSvc : 1;
ULONG NoPlugins : 1;
ULONG NewInstance : 1;
ULONG Elevate : 1;
ULONG Silent : 1;
ULONG Help : 1;
ULONG KphStartupHigh : 1;
ULONG KphStartupMax : 1;
ULONG Spare : 18;
};
ULONG Flags;
};
PPH_STRING SettingsFileName;
PPH_STRING RunAsServiceMode;
HWND WindowHandle;
POINT Point;
ULONG SelectPid;
ULONG PriorityClass;
PPH_LIST PluginParameters;
PPH_STRING SelectTab;
PPH_STRING SysInfo;
PH_RELEASE_CHANNEL UpdateChannel;
} PH_STARTUP_PARAMETERS, *PPH_STARTUP_PARAMETERS;
extern BOOLEAN PhPluginsEnabled;
extern BOOLEAN PhPortableEnabled;
extern PPH_STRING PhSettingsFileName;
extern PH_STARTUP_PARAMETERS PhStartupParameters;
extern PH_PROVIDER_THREAD PhPrimaryProviderThread;
extern PH_PROVIDER_THREAD PhSecondaryProviderThread;
extern PH_PROVIDER_THREAD PhTertiaryProviderThread;
extern RTL_ATOM PhTreeWindowAtom;
extern RTL_ATOM PhGraphWindowAtom;
extern RTL_ATOM PhHexEditWindowAtom;
extern RTL_ATOM PhColorBoxWindowAtom;
// begin_phapppub
PHAPPAPI
VOID
NTAPI
PhRegisterDialog(
_In_ HWND DialogWindowHandle
);
PHAPPAPI
VOID
NTAPI
PhUnregisterDialog(
_In_ HWND DialogWindowHandle
);
typedef BOOLEAN (NTAPI *PPH_MESSAGE_LOOP_FILTER)(
_In_ PMSG Message,
_In_ PVOID Context
);
typedef struct _PH_MESSAGE_LOOP_FILTER_ENTRY
{
PPH_MESSAGE_LOOP_FILTER Filter;
PVOID Context;
} PH_MESSAGE_LOOP_FILTER_ENTRY, *PPH_MESSAGE_LOOP_FILTER_ENTRY;
PHAPPAPI
PPH_MESSAGE_LOOP_FILTER_ENTRY
NTAPI
PhRegisterMessageLoopFilter(
_In_ PPH_MESSAGE_LOOP_FILTER Filter,
_In_opt_ PVOID Context
);
PHAPPAPI
VOID
NTAPI
PhUnregisterMessageLoopFilter(
_In_ PPH_MESSAGE_LOOP_FILTER_ENTRY FilterEntry
);
// end_phapppub
// plugin
extern PH_AVL_TREE PhPluginsByName;
VOID PhInitializeCallbacks(
VOID
);
BOOLEAN PhIsPluginDisabled(
_In_ PCPH_STRINGREF BaseName
);
VOID PhSetPluginDisabled(
_In_ PCPH_STRINGREF BaseName,
_In_ BOOLEAN Disable
);
VOID PhLoadPlugins(
VOID
);
VOID PhUnloadPlugins(
_In_ BOOLEAN SessionEnding
);
// log
#define PH_LOG_ENTRY_PROCESS_FIRST 1
#define PH_LOG_ENTRY_PROCESS_CREATE 1
#define PH_LOG_ENTRY_PROCESS_DELETE 2
#define PH_LOG_ENTRY_PROCESS_LAST 2
#define PH_LOG_ENTRY_SERVICE_FIRST 3
#define PH_LOG_ENTRY_SERVICE_CREATE 3
#define PH_LOG_ENTRY_SERVICE_DELETE 4
#define PH_LOG_ENTRY_SERVICE_START 5
#define PH_LOG_ENTRY_SERVICE_STOP 6
#define PH_LOG_ENTRY_SERVICE_CONTINUE 7
#define PH_LOG_ENTRY_SERVICE_PAUSE 8
#define PH_LOG_ENTRY_SERVICE_MODIFIED 9
#define PH_LOG_ENTRY_SERVICE_LAST 10
#define PH_LOG_ENTRY_DEVICE_REMOVED 11
#define PH_LOG_ENTRY_DEVICE_ARRIVED 12
#define PH_LOG_ENTRY_MESSAGE 100 // phapppub
typedef struct _PH_LOG_ENTRY *PPH_LOG_ENTRY; // phapppub
typedef struct _PH_LOG_ENTRY
{
UCHAR Type;
UCHAR Reserved1;
USHORT Flags;
LARGE_INTEGER Time;
union
{
struct
{
HANDLE ProcessId;
PPH_STRING Name;
HANDLE ParentProcessId;
PPH_STRING ParentName;
NTSTATUS ExitStatus;
} Process;
struct
{
PPH_STRING Name;
PPH_STRING DisplayName;
} Service;
struct
{
PPH_STRING Classification;
PPH_STRING Name;
} Device;
PPH_STRING Message;
};
UCHAR Buffer[1];
} PH_LOG_ENTRY, *PPH_LOG_ENTRY;
extern PH_CIRCULAR_BUFFER_PVOID PhLogBuffer;
VOID PhLogInitialization(
VOID
);
VOID PhClearLogEntries(
VOID
);
VOID PhLogProcessEntry(
_In_ UCHAR Type,
_In_ HANDLE ProcessId,
_In_ PPH_STRING Name,
_In_opt_ HANDLE ParentProcessId,
_In_opt_ PPH_STRING ParentName,
_In_opt_ ULONG Status
);
VOID PhLogServiceEntry(
_In_ UCHAR Type,
_In_ PPH_STRING Name,
_In_ PPH_STRING DisplayName
);
VOID PhLogDeviceEntry(
_In_ UCHAR Type,
_In_ PPH_STRING Classification,
_In_ PPH_STRING Name
);
// begin_phapppub
PHAPPAPI
VOID
NTAPI
PhLogMessageEntry(
_In_ UCHAR Type,
_In_ PPH_STRING Message
);
PHAPPAPI
PPH_STRING
NTAPI
PhFormatLogEntry(
_In_ PPH_LOG_ENTRY Entry
);
PHAPPAPI
PCPH_STRINGREF
NTAPI
PhFormatLogType(
_In_ PPH_LOG_ENTRY Entry
);
// end_phapppub
// dbgcon
VOID PhShowDebugConsole(
VOID
);
// itemtips
PPH_STRING PhGetProcessTooltipText(
_In_ PPH_PROCESS_ITEM Process,
_Out_opt_ PULONG64 ValidToTickCount
);
PPH_STRING PhGetServiceTooltipText(
_In_ PPH_SERVICE_ITEM Service
);
// cmdmode
NTSTATUS PhCommandModeStart(
VOID
);
// anawait
VOID PhUiAnalyzeWaitThread(
_In_ HWND WindowHandle,
_In_ HANDLE ProcessId,
_In_ HANDLE ThreadId,
_In_ PPH_SYMBOL_PROVIDER SymbolProvider
);
// mdump
VOID PhUiCreateDumpFileProcess(
_In_ HWND WindowHandle,
_In_ PPH_PROCESS_ITEM ProcessItem,
_In_ MINIDUMP_TYPE DumpType
);
VOID PhShowCreateDumpFileProcessDialog(
_In_ HWND WindowHandle,
_In_ PPH_PROCESS_ITEM ProcessItem
);
// about
VOID PhShowAboutDialog(
_In_ HWND ParentWindowHandle
);
PPH_STRING PhGetDiagnosticsString(
VOID
);
PPH_STRING PhGetApplicationVersionString(
_In_ BOOLEAN LinkToCommit
);
// affinity
VOID PhShowProcessAffinityDialog(
_In_ HWND ParentWindowHandle,
_In_opt_ PPH_PROCESS_ITEM ProcessItem,
_In_opt_ PPH_THREAD_ITEM ThreadItem
);
// begin_phapppub
_Success_(return)
PHAPPAPI
BOOLEAN
NTAPI
PhShowProcessAffinityDialog2(
_In_ HWND ParentWindowHandle,
_In_ PPH_PROCESS_ITEM ProcessItem,
_Out_ PKAFFINITY NewAffinityMask
);
PHAPPAPI
NTSTATUS
NTAPI
PhSetProcessItemAffinityMask(
_In_ PPH_PROCESS_ITEM ProcessItem,
_In_ KAFFINITY AffinityMask
);
PHAPPAPI
NTSTATUS
NTAPI
PhSetProcessItemPagePriority(
_In_ PPH_PROCESS_ITEM ProcessItem,
_In_ ULONG PagePriority
);
PHAPPAPI
NTSTATUS
NTAPI
PhSetProcessItemIoPriority(
_In_ PPH_PROCESS_ITEM ProcessItem,
_In_ IO_PRIORITY_HINT IoPriority
);
PHAPPAPI
NTSTATUS
NTAPI
PhSetProcessItemPriority(
_In_ PPH_PROCESS_ITEM ProcessItem,
_In_ UCHAR PriorityClass
);
PHAPPAPI
NTSTATUS
NTAPI
PhSetProcessItemPriorityBoost(
_In_ PPH_PROCESS_ITEM ProcessItem,
_In_ BOOLEAN PriorityBoost
);
PHAPPAPI
NTSTATUS
NTAPI
PhSetProcessItemThrottlingState(
_In_ PPH_PROCESS_ITEM ProcessItem,
_In_ BOOLEAN ClearThrottlingState
);
// end_phapppub
PHAPPAPI
VOID
NTAPI
PhShowThreadAffinityDialog(
_In_ HWND ParentWindowHandle,
_In_ PPH_THREAD_ITEM *Threads,
_In_ ULONG NumberOfThreads
);
// chcol
#define PH_CONTROL_TYPE_TREE_NEW 1
VOID PhShowChooseColumnsDialog(
_In_ HWND ParentWindowHandle,
_In_ HWND ControlHandle,
_In_ ULONG Type
);
// chdlg
// begin_phapppub
#define PH_CHOICE_DIALOG_SAVED_CHOICES 10
#define PH_CHOICE_DIALOG_CHOICE 0x0
#define PH_CHOICE_DIALOG_USER_CHOICE 0x1
#define PH_CHOICE_DIALOG_PASSWORD 0x2
#define PH_CHOICE_DIALOG_TYPE_MASK 0x3
PHAPPAPI
BOOLEAN
NTAPI
PhaChoiceDialog(
_In_ HWND ParentWindowHandle,
_In_ PCWSTR Title,
_In_ PCWSTR Message,
_In_opt_ PCWSTR *Choices,
_In_opt_ ULONG NumberOfChoices,
_In_opt_ PCWSTR Option,
_In_ ULONG Flags,
_Inout_ PPH_STRING *SelectedChoice,
_Inout_opt_ PBOOLEAN SelectedOption,
_In_opt_ PCWSTR SavedChoicesSettingName
);
PHAPPAPI
BOOLEAN
NTAPI
PhChoiceDialog(
_In_ HWND ParentWindowHandle,
_In_ PCWSTR Title,
_In_ PCWSTR Message,
_In_opt_ PCWSTR* Choices,
_In_opt_ ULONG NumberOfChoices,
_In_opt_ PCWSTR Option,
_In_ ULONG Flags,
_Inout_ PPH_STRING* SelectedChoice,
_Inout_opt_ PBOOLEAN SelectedOption,
_In_opt_ PCWSTR SavedChoicesSettingName
);
// end_phapppub
// chproc
// begin_phapppub
_Success_(return)
PHAPPAPI
BOOLEAN
NTAPI
PhShowChooseProcessDialog(
_In_ HWND ParentWindowHandle,
_In_ PCWSTR Message,
_Out_ PHANDLE ProcessId
);
// end_phapppub
// findobj
VOID PhShowFindObjectsDialog(
_In_ HWND ParentWindowHandle
);
// gdihndl
VOID PhShowGdiHandlesDialog(
_In_ HWND ParentWindowHandle,
_In_ PPH_PROCESS_ITEM ProcessItem
);
// heapinfo
VOID PhShowProcessHeapsDialog(
_In_ HWND ParentWindowHandle,
_In_ PPH_PROCESS_ITEM ProcessItem
);
VOID PhShowProcessLocksDialog(
_In_ HWND ParentWindowHandle,
_In_ PPH_PROCESS_ITEM ProcessItem
);
// hidnproc
VOID PhShowZombieProcessesDialog(
VOID
);
// hndlprp
VOID PhShowHandleProperties(
_In_ HWND ParentWindowHandle,
_In_ HANDLE ProcessId,
_In_ PPH_HANDLE_ITEM HandleItem
);
// hndlstat
VOID PhShowHandleStatisticsDialog(
_In_ HWND ParentWindowHandle,
_In_ HANDLE ProcessId
);
// infodlg
VOID PhShowInformationDialog(
_In_ HWND ParentWindowHandle,
_In_ PCWSTR String,
_Reserved_ ULONG Flags
);
// jobprp
VOID PhShowJobProperties(
_In_ HWND ParentWindowHandle,
_In_ PPH_OPEN_OBJECT OpenObject,
_In_ PPH_CLOSE_OBJECT CloseObject,
_In_opt_ PVOID Context,
_In_opt_ PCWSTR Title
);
HPROPSHEETPAGE PhCreateJobPage(
_In_ PPH_OPEN_OBJECT OpenObject,
_In_ PPH_CLOSE_OBJECT CloseObject,
_In_opt_ PVOID Context,
_In_opt_ DLGPROC HookProc
);
// kdump
typedef union _PH_LIVE_DUMP_OPTIONS
{
BOOLEAN Flags;
struct
{
BOOLEAN CompressMemoryPages : 1;
BOOLEAN IncludeUserSpaceMemory : 1;
BOOLEAN IncludeHypervisorPages : 1;
BOOLEAN OnlyKernelThreadStacks : 1;
BOOLEAN UseDumpStorageStack : 1;
BOOLEAN IncludeNonEssentialHypervisorPages : 1;
BOOLEAN Spare : 2;
};
} PH_LIVE_DUMP_OPTIONS, *PPH_LIVE_DUMP_OPTIONS;
VOID PhUiCreateLiveDump(
_In_ HWND ParentWindowHandle,
_In_ PPH_LIVE_DUMP_OPTIONS Options
);
VOID PhShowLiveDumpDialog(
_In_ HWND ParentWindowHandle
);
// ksyscall
PPH_STRING PhGetSystemCallNumberName(
_In_ USHORT SystemCallNumber
);
// logwnd
VOID PhShowLogDialog(
VOID
);
// memedit
#define PH_MEMORY_EDITOR_UNMAP_VIEW_OF_SECTION 0x1
VOID PhShowMemoryEditorDialog(
_In_ HWND OwnerWindow,
_In_ HANDLE ProcessId,
_In_ PVOID BaseAddress,
_In_ SIZE_T RegionSize,
_In_ ULONG SelectOffset,
_In_ ULONG SelectLength,
_In_opt_ PPH_STRING Title,
_In_ ULONG Flags
);
// memlists
VOID PhShowMemoryListsDialog(
_In_ HWND ParentWindowHandle,
_In_opt_ VOID (NTAPI *RegisterDialog)(HWND),
_In_opt_ VOID (NTAPI *UnregisterDialog)(HWND)
);
// memprot
VOID PhShowMemoryProtectDialog(
_In_ HWND ParentWindowHandle,
_In_ PPH_PROCESS_ITEM ProcessItem,
_In_ PPH_MEMORY_ITEM MemoryItem
);
// memrslt
VOID PhShowMemoryResultsDialog(
_In_ HANDLE ProcessId,
_In_ PPH_LIST Results
);
// memsrch
VOID PhShowMemoryStringDialog(
_In_ HWND ParentWindowHandle,
_In_ PPH_PROCESS_ITEM ProcessItem
);
// msmsrcht
VOID PhShowMemoryStringTreeDialog(
_In_ HWND ParentWindowHandle,
_In_ PPH_PROCESS_ITEM ProcessItem
);
// memmod
VOID PhShowImagePageModifiedDialog(
_In_ HWND ParentWindowHandle,
_In_ PPH_PROCESS_ITEM ProcessItem
);
// mtgndlg
VOID PhShowProcessMitigationPolicyDialog(
_In_ HWND ParentWindowHandle,
_In_ HANDLE ProcessId
);
// ntobjprp
HPROPSHEETPAGE PhCreateEventPage(
_In_ PPH_OPEN_OBJECT OpenObject,
_In_ PPH_CLOSE_OBJECT CloseObject,
_In_opt_ PVOID Context
);
HPROPSHEETPAGE PhCreateEventPairPage(
_In_ PPH_OPEN_OBJECT OpenObject,
_In_ PPH_CLOSE_OBJECT CloseObject,
_In_opt_ PVOID Context
);
HPROPSHEETPAGE PhCreateSemaphorePage(
_In_ PPH_OPEN_OBJECT OpenObject,
_In_ PPH_CLOSE_OBJECT CloseObject,
_In_opt_ PVOID Context
);
HPROPSHEETPAGE PhCreateTimerPage(
_In_ PPH_OPEN_OBJECT OpenObject,
_In_ PPH_CLOSE_OBJECT CloseObject,
_In_opt_ PVOID Context
);
HPROPSHEETPAGE PhCreateAfdSocketPage(
_In_ HANDLE ProcessId,
_In_ HANDLE HandleValue
);
HPROPSHEETPAGE PhCreateMappingsPage(
_In_ HANDLE ProcessId,
_In_ HANDLE SectionHandle
);
// options
VOID PhShowOptionsDialog(
_In_ HWND ParentWindowHandle
);
// pagfiles
VOID PhShowPagefilesDialog(
_In_ HWND ParentWindowHandle
);
// plugman
INT_PTR CALLBACK PhPluginsDlgProc(
_In_ HWND hwndDlg,
_In_ UINT uMsg,
_In_ WPARAM wParam,
_In_ LPARAM lParam
);
// procrec
// begin_phapppub
PHAPPAPI
VOID
NTAPI
PhShowProcessRecordDialog(
_In_ HWND ParentWindowHandle,
_In_ PPH_PROCESS_RECORD Record
);
// end_phapppub
// runas
typedef struct _PH_RUNAS_SERVICE_PARAMETERS
{
ULONG ProcessId;
PCWSTR UserName;
PCWSTR Password;
ULONG LogonType;
ULONG SessionId;
PCWSTR CurrentDirectory;
PCWSTR CommandLine;
PCWSTR FileName;
PCWSTR DesktopName;
BOOLEAN UseLinkedToken;
PCWSTR ServiceName;
BOOLEAN CreateSuspendedProcess;
HWND WindowHandle;
BOOLEAN CreateUIAccessProcess;
} PH_RUNAS_SERVICE_PARAMETERS, *PPH_RUNAS_SERVICE_PARAMETERS;
VOID PhShowRunAsDialog(
_In_ HWND ParentWindowHandle,
_In_opt_ HANDLE ProcessId
);
VOID PhShowRunAsPackageDialog(
_In_ HWND ParentWindowHandle
);
// begin_phapppub
PHLIBAPI
BOOLEAN
NTAPI
PhShowRunFileDialog(
_In_ HWND ParentWindowHandle
);
// end_phapppub
NTSTATUS PhExecuteRunAsCommand(
_In_ PPH_RUNAS_SERVICE_PARAMETERS Parameters
);
// begin_phapppub
PHAPPAPI
NTSTATUS
NTAPI
PhExecuteRunAsCommand2(
_In_ HWND hWnd,
_In_ PCWSTR Program,
_In_opt_ PCWSTR UserName,
_In_opt_ PCWSTR Password,
_In_opt_ ULONG LogonType,
_In_opt_ HANDLE ProcessIdWithToken,
_In_opt_ ULONG SessionId,
_In_opt_ PCWSTR DesktopName,
_In_ BOOLEAN UseLinkedToken
);
// end_phapppub
PHAPPAPI
NTSTATUS
NTAPI
PhExecuteRunAsCommand3(
_In_ HWND hWnd,
_In_ PCWSTR Program,
_In_opt_ PCWSTR UserName,
_In_opt_ PCWSTR Password,
_In_opt_ ULONG LogonType,
_In_opt_ HANDLE ProcessIdWithToken,
_In_opt_ ULONG SessionId,
_In_opt_ PCWSTR DesktopName,
_In_ BOOLEAN UseLinkedToken,
_In_ BOOLEAN CreateSuspendedProcess,
_In_ BOOLEAN CreateUIAccessProcess
);
NTSTATUS PhRunAsServiceStart(
_In_ PPH_STRING ServiceName
);
NTSTATUS PhInvokeRunAsService(
_In_ PPH_RUNAS_SERVICE_PARAMETERS Parameters
);
// searchbox
// begin_phapppub
typedef _Function_class_(PH_SEARCHCONTROL_CALLBACK)
VOID NTAPI PH_SEARCHCONTROL_CALLBACK(
_In_ ULONG_PTR MatchHandle,
_In_opt_ PVOID Context
);
typedef PH_SEARCHCONTROL_CALLBACK* PPH_SEARCHCONTROL_CALLBACK;
PHAPPAPI
VOID
NTAPI
PhCreateSearchControl(
_In_ HWND ParentWindowHandle,
_In_ HWND SearchWindowHandle,
_In_opt_ PCWSTR BannerText,
_In_ PPH_SEARCHCONTROL_CALLBACK Callback,
_In_opt_ PVOID Context
);
// end_phapppub
// sessmsg
VOID PhShowSessionSendMessageDialog(
_In_ HWND ParentWindowHandle,
_In_ ULONG SessionId
);
// sessprp
VOID PhShowSessionProperties(
_In_ HWND ParentWindowHandle,
_In_ ULONG SessionId
);
// sessshad
VOID PhShowSessionShadowDialog(
_In_ HWND ParentWindowHandle,
_In_ ULONG SessionId
);
// srvcr
VOID PhShowCreateServiceDialog(
_In_ HWND ParentWindowHandle
);
// srvctl
// begin_phapppub
#define WM_PH_SET_LIST_VIEW_SETTINGS (WM_APP + 701)
PHAPPAPI
HWND
NTAPI
PhCreateServiceListControl(
_In_ HWND ParentWindowHandle,
_In_ PPH_SERVICE_ITEM *Services,
_In_ ULONG NumberOfServices
);
// end_phapppub
// srvprp
VOID PhShowServiceProperties(
_In_ HWND ParentWindowHandle,
_In_ PPH_SERVICE_ITEM ServiceItem
);
// thrdstk
VOID PhShowThreadStackDialog(
_In_ HWND ParentWindowHandle,
_In_ HANDLE ProcessId,
_In_ HANDLE ThreadId,
_In_ PPH_THREAD_PROVIDER ThreadProvider
);
// thrdstks
VOID PhShowThreadStacksDialog(
_In_ HWND ParentWindowHandle
);
// userslist
VOID PhShowUserListDialog(
_In_ HWND ParentWindowHandle
);
// tokprp
PPH_STRING PhGetGroupAttributesString(
_In_ ULONG Attributes,
_In_ BOOLEAN Restricted
);
PCWSTR PhGetPrivilegeAttributesString(
_In_ ULONG Attributes
);
_Success_(return)
BOOLEAN PhGetElevationTypeString(
_In_ BOOLEAN IsElevated,
_In_ TOKEN_ELEVATION_TYPE ElevationType,
_Out_ PCPH_STRINGREF* ElevationTypeString
);
VOID PhShowTokenProperties(
_In_ HWND ParentWindowHandle,
_In_ PPH_OPEN_OBJECT OpenObject,
_In_ PPH_CLOSE_OBJECT CloseObject,
_In_ HANDLE ProcessId,
_In_ PVOID Context,
_In_opt_ PCWSTR Title
);
HPROPSHEETPAGE PhCreateTokenPage(
_In_ PPH_OPEN_OBJECT OpenObject,
_In_ PPH_CLOSE_OBJECT CloseObject,
_In_ HANDLE ProcessId,
_In_opt_ PVOID Context,
_In_opt_ DLGPROC HookProc
);
#endif
================================================
FILE: SystemInformer/include/phappres.h
================================================
/*
* Copyright (c) 2022 Winsider Seminars & Solutions, Inc. All rights reserved.
*
* This file is part of System Informer.
*
* Authors:
*
* wj32 2010-2016
* dmex 2016-2024
*
*/
#ifndef PH_PHAPPRES_H
#define PH_PHAPPRES_H
#ifndef PHAPP_VERSION_MAJOR
#define PHAPP_VERSION_MAJOR 0
#endif
#ifndef PHAPP_VERSION_MINOR
#define PHAPP_VERSION_MINOR 0
#endif
#ifndef PHAPP_VERSION_BUILD
#define PHAPP_VERSION_BUILD 0
#endif
#ifndef PHAPP_VERSION_REVISION
#define PHAPP_VERSION_REVISION 0
#endif
#ifndef PHAPP_VERSION_COMMITHASH
#define PHAPP_VERSION_COMMITHASH "0000000"
#endif
#define DO_MAKE_STR(x) #x
#define MAKE_STR(x) DO_MAKE_STR(x)
#define PHAPP_VERSION_STRING MAKE_STR(PHAPP_VERSION_MAJOR) "." MAKE_STR(PHAPP_VERSION_MINOR) "." MAKE_STR(PHAPP_VERSION_BUILD) "." MAKE_STR(PHAPP_VERSION_REVISION)
#define PHAPP_VERSION_NUMBER PHAPP_VERSION_MAJOR,PHAPP_VERSION_MINOR,PHAPP_VERSION_BUILD,PHAPP_VERSION_REVISION
#define PHAPP_VERSION_COMMIT MAKE_STR(PHAPP_VERSION_COMMITHASH)
#endif // PHAPPRES_H
================================================
FILE: SystemInformer/include/phfwddef.h
================================================
/*
* Copyright (c) 2022 Winsider Seminars & Solutions, Inc. All rights reserved.
*
* This file is part of System Informer.
*
* Authors:
*
* wj32 2016
* dmex 2022
*
*/
#ifndef PH_PHFWDDEF_H
#define PH_PHFWDDEF_H
// begin_phapppub
// phlib
typedef struct _PH_SYMBOL_PROVIDER *PPH_SYMBOL_PROVIDER;
// Providers
typedef struct _PH_PROCESS_ITEM *PPH_PROCESS_ITEM;
typedef struct _PH_PROCESS_RECORD *PPH_PROCESS_RECORD;
typedef struct _PH_SERVICE_ITEM *PPH_SERVICE_ITEM;
typedef struct _PH_NETWORK_ITEM *PPH_NETWORK_ITEM;
typedef struct _PH_MODULE_ITEM *PPH_MODULE_ITEM;
typedef struct _PH_MODULE_PROVIDER *PPH_MODULE_PROVIDER;
typedef struct _PH_THREAD_ITEM *PPH_THREAD_ITEM;
typedef struct _PH_THREAD_PROVIDER *PPH_THREAD_PROVIDER;
typedef struct _PH_HANDLE_ITEM *PPH_HANDLE_ITEM;
typedef struct _PH_HANDLE_PROVIDER *PPH_HANDLE_PROVIDER;
typedef struct _PH_MEMORY_ITEM *PPH_MEMORY_ITEM;
typedef struct _PH_MEMORY_ITEM_LIST *PPH_MEMORY_ITEM_LIST;
// uimodels
typedef struct _PH_PROCESS_NODE *PPH_PROCESS_NODE;
typedef struct _PH_SERVICE_NODE *PPH_SERVICE_NODE;
typedef struct _PH_NETWORK_NODE *PPH_NETWORK_NODE;
typedef struct _PH_MODULE_NODE *PPH_MODULE_NODE;
typedef struct _PH_THREAD_NODE *PPH_THREAD_NODE;
typedef struct _PH_HANDLE_NODE *PPH_HANDLE_NODE;
typedef struct _PH_MEMORY_NODE *PPH_MEMORY_NODE;
// procprv
typedef struct _PH_PROCESS_PROPCONTEXT *PPH_PROCESS_PROPCONTEXT;
// end_phapppub
#endif
================================================
FILE: SystemInformer/include/phplug.h
================================================
/*
* Copyright (c) 2022 Winsider Seminars & Solutions, Inc. All rights reserved.
*
* This file is part of System Informer.
*
* Authors:
*
* wj32 2010-2015
* dmex 2017-2024
*
*/
#ifndef PH_PHPLUG_H
#define PH_PHPLUG_H
#include
#include
#include
// begin_phapppub
// Callbacks
typedef enum _PH_GENERAL_CALLBACK
{
GeneralCallbackMainWindowShowing = 0, // INT ShowCommand [main thread]
GeneralCallbackProcessesUpdated = 1, // ULONG RunId [main thread]
GeneralCallbackGetProcessHighlightingColor = 2, // PPH_PLUGIN_GET_HIGHLIGHTING_COLOR Data [main thread]
GeneralCallbackGetProcessTooltipText = 3, // PPH_PLUGIN_GET_TOOLTIP_TEXT Data [main thread]
GeneralCallbackProcessPropertiesInitializing = 4, // PPH_PLUGIN_PROCESS_PROPCONTEXT Data [properties thread]
GeneralCallbackMainMenuInitializing = 5, // PPH_PLUGIN_MENU_INFORMATION Data [main thread]
GeneralCallbackNotifyEvent = 6, // PPH_PLUGIN_NOTIFY_EVENT Data [main thread]
GeneralCallbackServicePropertiesInitializing = 7, // PPH_PLUGIN_OBJECT_PROPERTIES Data [properties thread]
GeneralCallbackHandlePropertiesInitializing = 8, // PPH_PLUGIN_OBJECT_PROPERTIES Data [properties thread]
GeneralCallbackProcessMenuInitializing = 9, // PPH_PLUGIN_MENU_INFORMATION Data [main thread]
GeneralCallbackServiceMenuInitializing = 10, // PPH_PLUGIN_MENU_INFORMATION Data [main thread]
GeneralCallbackNetworkMenuInitializing = 11, // PPH_PLUGIN_MENU_INFORMATION Data [main thread]
GeneralCallbackIconMenuInitializing = 12, // PPH_PLUGIN_MENU_INFORMATION Data [main thread]
GeneralCallbackThreadMenuInitializing = 13, // PPH_PLUGIN_MENU_INFORMATION Data [properties thread]
GeneralCallbackModuleMenuInitializing = 14, // PPH_PLUGIN_MENU_INFORMATION Data [properties thread]
GeneralCallbackMemoryMenuInitializing = 15, // PPH_PLUGIN_MENU_INFORMATION Data [properties thread]
GeneralCallbackHandleMenuInitializing = 16, // PPH_PLUGIN_MENU_INFORMATION Data [properties thread]
GeneralCallbackProcessTreeNewInitializing = 17, // PPH_PLUGIN_TREENEW_INFORMATION Data [main thread]
GeneralCallbackServiceTreeNewInitializing = 18, // PPH_PLUGIN_TREENEW_INFORMATION Data [main thread]
GeneralCallbackNetworkTreeNewInitializing = 19, // PPH_PLUGIN_TREENEW_INFORMATION Data [main thread]
GeneralCallbackModuleTreeNewInitializing = 20, // PPH_PLUGIN_TREENEW_INFORMATION Data [properties thread]
GeneralCallbackModuleTreeNewUninitializing = 21, // PPH_PLUGIN_TREENEW_INFORMATION Data [properties thread]
GeneralCallbackThreadTreeNewInitializing = 22, // PPH_PLUGIN_TREENEW_INFORMATION Data [properties thread]
GeneralCallbackThreadTreeNewUninitializing = 23, // PPH_PLUGIN_TREENEW_INFORMATION Data [properties thread]
GeneralCallbackHandleTreeNewInitializing = 24, // PPH_PLUGIN_TREENEW_INFORMATION Data [properties thread]
GeneralCallbackHandleTreeNewUninitializing = 25, // PPH_PLUGIN_TREENEW_INFORMATION Data [properties thread]
GeneralCallbackThreadStackControl = 26, // PPH_PLUGIN_THREAD_STACK_CONTROL Data [properties thread]
GeneralCallbackSystemInformationInitializing = 27, // PPH_PLUGIN_SYSINFO_POINTERS Data [system information thread]
GeneralCallbackMainWindowTabChanged = 28, // INT NewIndex [main thread]
GeneralCallbackMemoryTreeNewInitializing = 29, // PPH_PLUGIN_TREENEW_INFORMATION Data [properties thread]
GeneralCallbackMemoryTreeNewUninitializing = 30, // PPH_PLUGIN_TREENEW_INFORMATION Data [properties thread]
GeneralCallbackMemoryItemListControl = 31, // PPH_PLUGIN_MEMORY_ITEM_LIST_CONTROL Data [properties thread]
GeneralCallbackMiniInformationInitializing = 32, // PPH_PLUGIN_MINIINFO_POINTERS Data [main thread]
GeneralCallbackMiListSectionMenuInitializing = 33, // PPH_PLUGIN_MENU_INFORMATION Data [main thread]
GeneralCallbackOptionsWindowInitializing = 34, // PH_PLUGIN_OPTIONS_POINTERS Data [main thread]
GeneralCallbackHandlePropertiesWindowInitialized = 35, // PPH_PLUGIN_HANDLE_PROPERTIES_WINDOW_CONTEXT Data [properties thread]
GeneralCallbackHandlePropertiesWindowUninitializing = 36, // PPH_PLUGIN_HANDLE_PROPERTIES_WINDOW_CONTEXT Data [properties thread]
GeneralCallbackProcessProviderAddedEvent, // [process provider thread]
GeneralCallbackProcessProviderModifiedEvent, // [process provider thread]
GeneralCallbackProcessProviderRemovedEvent, // [process provider thread]
GeneralCallbackProcessProviderUpdatedEvent, // PPH_PROCESS_PROVIDER_UPDATED_EVENT [process provider thread]
GeneralCallbackServiceProviderAddedEvent, // [service provider thread]
GeneralCallbackServiceProviderModifiedEvent, // [service provider thread]
GeneralCallbackServiceProviderRemovedEvent, // [service provider thread]
GeneralCallbackServiceProviderUpdatedEvent, // [service provider thread]
GeneralCallbackNetworkProviderAddedEvent, // [network provider thread]
GeneralCallbackNetworkProviderModifiedEvent, // [network provider thread]
GeneralCallbackNetworkProviderRemovedEvent, // [network provider thread]
GeneralCallbackNetworkProviderUpdatedEvent, // [network provider thread]
GeneralCallbackLoggedEvent, // [multiple provider threads]
GeneralCallbackDeviceNotificationEvent, // [device provider thread]
GeneralCallbackTrayIconsInitializing, // [work queue thread]
GeneralCallbackTrayIconsUpdatedEvent,
GeneralCallbackWindowNotifyEvent,
GeneralCallbackProcessStatsNotifyEvent,
GeneralCallbackSettingsUpdated,
GeneralCallbackDangerousProcess,
GeneralCallbackUpdateAutomatically,
GeneralCallbackMaximum
} PH_GENERAL_CALLBACK, *PPH_GENERAL_CALLBACK;
typedef enum _PH_PLUGIN_CALLBACK
{
PluginCallbackLoad = 0, // PPH_LIST Parameters [main thread] // list of strings, might be NULL
PluginCallbackUnload = 1, // BOOLEAN SessionEnding [main thread]
PluginCallbackShowOptions = 2, // HWND ParentWindowHandle [main thread]
PluginCallbackMenuItem = 3, // PPH_PLUGIN_MENU_ITEM MenuItem [main/properties thread]
PluginCallbackTreeNewMessage = 4, // PPH_PLUGIN_TREENEW_MESSAGE Message [main/properties thread]
PluginCallbackPhSvcRequest = 5, // PPH_PLUGIN_PHSVC_REQUEST Message [phsvc thread]
PluginCallbackMenuHook = 6, // PH_PLUGIN_MENU_HOOK_INFORMATION MenuHookInfo [menu thread]
PluginCallbackMaximum
} PH_PLUGIN_CALLBACK, *PPH_PLUGIN_CALLBACK;
// Provider events
typedef struct _PH_PROCESS_PROVIDER_UPDATED_EVENT
{
ULONG RunCount;
} PH_PROCESS_PROVIDER_UPDATED_EVENT, *PPH_PROCESS_PROVIDER_UPDATED_EVENT;
// Plugin events
typedef struct _PH_PLUGIN_GET_HIGHLIGHTING_COLOR
{
// Parameter is:
// PPH_PROCESS_ITEM for GeneralCallbackGetProcessHighlightingColor
PVOID Parameter;
COLORREF BackColor;
COLORREF ForeColor;
BOOLEAN Handled;
BOOLEAN Cache;
} PH_PLUGIN_GET_HIGHLIGHTING_COLOR, *PPH_PLUGIN_GET_HIGHLIGHTING_COLOR;
typedef struct _PH_PLUGIN_GET_TOOLTIP_TEXT
{
// Parameter is:
// PPH_PROCESS_ITEM for GeneralCallbackGetProcessTooltipText
PVOID Parameter;
PPH_STRING_BUILDER StringBuilder;
ULONG ValidForMs;
} PH_PLUGIN_GET_TOOLTIP_TEXT, *PPH_PLUGIN_GET_TOOLTIP_TEXT;
typedef struct _PH_PLUGIN_PROCESS_PROPCONTEXT
{
PPH_PROCESS_PROPCONTEXT PropContext;
PPH_PROCESS_ITEM ProcessItem;
} PH_PLUGIN_PROCESS_PROPCONTEXT, *PPH_PLUGIN_PROCESS_PROPCONTEXT;
typedef struct _PH_PLUGIN_NOTIFY_EVENT
{
// Parameter is:
// PPH_PROCESS_ITEM for Type = PH_NOTIFY_PROCESS_*
// PPH_SERVICE_ITEM for Type = PH_NOTIFY_SERVICE_*
// PPH_DEVICE_ITEM for type = PH_NOTIFY_DEVICE_*
ULONG Type;
BOOLEAN Handled;
PVOID Parameter;
} PH_PLUGIN_NOTIFY_EVENT, *PPH_PLUGIN_NOTIFY_EVENT;
typedef struct _PH_PLUGIN_OBJECT_PROPERTIES
{
// Parameter is:
// PPH_SERVICE_ITEM for GeneralCallbackServicePropertiesInitializing
// PPH_PLUGIN_HANDLE_PROPERTIES_CONTEXT for GeneralCallbackHandlePropertiesInitializing
PVOID Parameter;
ULONG NumberOfPages;
ULONG MaximumNumberOfPages;
HPROPSHEETPAGE *Pages;
} PH_PLUGIN_OBJECT_PROPERTIES, *PPH_PLUGIN_OBJECT_PROPERTIES;
typedef struct _PH_PLUGIN_IS_DANGEROUS_PROCESS
{
HANDLE ProcessId;
BOOLEAN DangerousProcess;
} PH_PLUGIN_IS_DANGEROUS_PROCESS, *PPH_PLUGIN_IS_DANGEROUS_PROCESS;
typedef enum _PH_PLUGIN_HANDLE_GENERAL_CATEGORY
{
// common
PH_PLUGIN_HANDLE_GENERAL_CATEGORY_BASICINFO,
PH_PLUGIN_HANDLE_GENERAL_CATEGORY_REFERENCES,
PH_PLUGIN_HANDLE_GENERAL_CATEGORY_QUOTA,
// extra
PH_PLUGIN_HANDLE_GENERAL_CATEGORY_ALPC,
PH_PLUGIN_HANDLE_GENERAL_CATEGORY_FILE,
PH_PLUGIN_HANDLE_GENERAL_CATEGORY_SECTION,
PH_PLUGIN_HANDLE_GENERAL_CATEGORY_MUTANT,
PH_PLUGIN_HANDLE_GENERAL_CATEGORY_PROCESSTHREAD,
PH_PLUGIN_HANDLE_GENERAL_CATEGORY_ETW,
PH_PLUGIN_HANDLE_GENERAL_CATEGORY_SYMBOLICLINK,
PH_PLUGIN_HANDLE_GENERAL_CATEGORY_MAXIMUM
} PH_PLUGIN_HANDLE_GENERAL_CATEGORY;
typedef enum _PH_PLUGIN_HANDLE_GENERAL_INDEX
{
PH_PLUGIN_HANDLE_GENERAL_INDEX_NAME,
PH_PLUGIN_HANDLE_GENERAL_INDEX_TYPE,
PH_PLUGIN_HANDLE_GENERAL_INDEX_OBJECT,
PH_PLUGIN_HANDLE_GENERAL_INDEX_ACCESSMASK,
PH_PLUGIN_HANDLE_GENERAL_INDEX_REFERENCES,
PH_PLUGIN_HANDLE_GENERAL_INDEX_HANDLES,
PH_PLUGIN_HANDLE_GENERAL_INDEX_PAGED,
PH_PLUGIN_HANDLE_GENERAL_INDEX_NONPAGED,
PH_PLUGIN_HANDLE_GENERAL_INDEX_FLAGS,
PH_PLUGIN_HANDLE_GENERAL_INDEX_SEQUENCENUMBER,
PH_PLUGIN_HANDLE_GENERAL_INDEX_PORTCONTEXT,
PH_PLUGIN_HANDLE_GENERAL_INDEX_FILETYPE,
PH_PLUGIN_HANDLE_GENERAL_INDEX_FILEMODE,
PH_PLUGIN_HANDLE_GENERAL_INDEX_FILEPOSITION,
PH_PLUGIN_HANDLE_GENERAL_INDEX_FILESIZE,
PH_PLUGIN_HANDLE_GENERAL_INDEX_FILEPRIORITY,
PH_PLUGIN_HANDLE_GENERAL_INDEX_FILEDRIVER,
PH_PLUGIN_HANDLE_GENERAL_INDEX_FILEDRIVERIMAGE,
PH_PLUGIN_HANDLE_GENERAL_INDEX_SECTIONTYPE,
PH_PLUGIN_HANDLE_GENERAL_INDEX_SECTIONFILE,
PH_PLUGIN_HANDLE_GENERAL_INDEX_SECTIONSIZE,
PH_PLUGIN_HANDLE_GENERAL_INDEX_MUTANTCOUNT,
PH_PLUGIN_HANDLE_GENERAL_INDEX_MUTANTABANDONED,
PH_PLUGIN_HANDLE_GENERAL_INDEX_MUTANTOWNER,
PH_PLUGIN_HANDLE_GENERAL_INDEX_ALPCCONNECTION,
PH_PLUGIN_HANDLE_GENERAL_INDEX_ALPCSERVER,
PH_PLUGIN_HANDLE_GENERAL_INDEX_ALPCCLIENT,
PH_PLUGIN_HANDLE_GENERAL_INDEX_PROCESSTHREADNAME,
PH_PLUGIN_HANDLE_GENERAL_INDEX_PROCESSTHREADCREATETIME,
PH_PLUGIN_HANDLE_GENERAL_INDEX_PROCESSTHREADEXITTIME,
PH_PLUGIN_HANDLE_GENERAL_INDEX_PROCESSTHREADEXITCODE,
PH_PLUGIN_HANDLE_GENERAL_INDEX_ETWORIGINALNAME,
PH_PLUGIN_HANDLE_GENERAL_INDEX_ETWGROUPNAME,
PH_PLUGIN_HANDLE_GENERAL_INDEX_SYMBOLICLINKLINK,
PH_PLUGIN_HANDLE_GENERAL_INDEX_MAXIMUM
} PH_PLUGIN_HANDLE_GENERAL_INDEX;
typedef struct _PH_PLUGIN PH_PLUGIN, *PPH_PLUGIN;
typedef struct _PH_PLUGIN_HANDLE_PROPERTIES_WINDOW_CONTEXT
{
HWND ListViewHandle;
HWND ParentWindow;
HANDLE ProcessId;
PVOID ListViewClass;
PPH_HANDLE_ITEM HandleItem;
PH_LAYOUT_MANAGER LayoutManager;
} PH_PLUGIN_HANDLE_PROPERTIES_WINDOW_CONTEXT, *PPH_PLUGIN_HANDLE_PROPERTIES_WINDOW_CONTEXT;
typedef struct _PH_PLUGIN_PROCESS_STATS_EVENT
{
ULONG Version;
ULONG Type;
PPH_PROCESS_ITEM ProcessItem;
PVOID Parameter;
} PH_PLUGIN_PROCESS_STATS_EVENT, *PPH_PLUGIN_PROCESS_STATS_EVENT;
typedef struct _PH_PLUGIN_HANDLE_PROPERTIES_CONTEXT
{
HWND ParentWindowHandle;
HANDLE ProcessId;
PPH_HANDLE_ITEM HandleItem;
} PH_PLUGIN_HANDLE_PROPERTIES_CONTEXT, *PPH_PLUGIN_HANDLE_PROPERTIES_CONTEXT;
typedef struct _PH_EMENU_ITEM *PPH_EMENU_ITEM, *PPH_EMENU;
#define PH_PLUGIN_MENU_DISALLOW_HOOKS 0x1
typedef struct _PH_PLUGIN_MENU_INFORMATION
{
PPH_EMENU Menu;
HWND OwnerWindow;
union
{
struct
{
PVOID Reserved[8]; // Reserve space for future expansion of this union
} DoNotUse;
struct
{
ULONG SubMenuIndex;
} MainMenu;
struct
{
PPH_PROCESS_ITEM *Processes;
ULONG NumberOfProcesses;
} Process;
struct
{
PPH_SERVICE_ITEM *Services;
ULONG NumberOfServices;
} Service;
struct
{
PPH_NETWORK_ITEM *NetworkItems;
ULONG NumberOfNetworkItems;
} Network;
struct
{
HANDLE ProcessId;
PPH_THREAD_ITEM *Threads;
ULONG NumberOfThreads;
} Thread;
struct
{
HANDLE ProcessId;
PPH_MODULE_ITEM *Modules;
ULONG NumberOfModules;
} Module;
struct
{
HANDLE ProcessId;
PPH_MEMORY_NODE *MemoryNodes;
ULONG NumberOfMemoryNodes;
} Memory;
struct
{
HANDLE ProcessId;
PPH_HANDLE_ITEM *Handles;
ULONG NumberOfHandles;
} Handle;
struct
{
PPH_STRINGREF SectionName;
PPH_PROCESS_GROUP ProcessGroup;
} MiListSection;
} u;
ULONG Flags;
PPH_LIST PluginHookList;
} PH_PLUGIN_MENU_INFORMATION, *PPH_PLUGIN_MENU_INFORMATION;
C_ASSERT(RTL_FIELD_SIZE(PH_PLUGIN_MENU_INFORMATION, u) == RTL_FIELD_SIZE(PH_PLUGIN_MENU_INFORMATION, u.DoNotUse));
typedef struct _PH_PLUGIN_MENU_HOOK_INFORMATION
{
PPH_PLUGIN_MENU_INFORMATION MenuInfo;
PPH_EMENU SelectedItem;
PVOID Context;
BOOLEAN Handled;
} PH_PLUGIN_MENU_HOOK_INFORMATION, *PPH_PLUGIN_MENU_HOOK_INFORMATION;
typedef struct _PH_PLUGIN_TREENEW_INFORMATION
{
HWND TreeNewHandle;
PVOID CmData;
PVOID SystemContext; // e.g. PPH_THREADS_CONTEXT
} PH_PLUGIN_TREENEW_INFORMATION, *PPH_PLUGIN_TREENEW_INFORMATION;
typedef enum _PH_PLUGIN_THREAD_STACK_CONTROL_TYPE
{
PluginThreadStackInitializing,
PluginThreadStackUninitializing,
PluginThreadStackResolveSymbol,
PluginThreadStackGetTooltip,
PluginThreadStackWalkStack,
PluginThreadStackBeginDefaultWalkStack,
PluginThreadStackEndDefaultWalkStack,
PluginThreadStackMaximum
} PH_PLUGIN_THREAD_STACK_CONTROL_TYPE;
typedef struct _PH_SYMBOL_PROVIDER *PPH_SYMBOL_PROVIDER;
typedef struct _PH_THREAD_STACK_FRAME *PPH_THREAD_STACK_FRAME;
typedef _Function_class_(PH_PLUGIN_WALK_THREAD_STACK_CALLBACK)
BOOLEAN NTAPI PH_PLUGIN_WALK_THREAD_STACK_CALLBACK(
_In_ PPH_THREAD_STACK_FRAME StackFrame,
_In_opt_ PVOID Context
);
typedef PH_PLUGIN_WALK_THREAD_STACK_CALLBACK *PPH_PLUGIN_WALK_THREAD_STACK_CALLBACK;
typedef struct _PH_PLUGIN_THREAD_STACK_CONTROL
{
PH_PLUGIN_THREAD_STACK_CONTROL_TYPE Type;
PVOID UniqueKey;
union
{
struct
{
HANDLE ProcessId;
HANDLE ThreadId;
HANDLE ThreadHandle;
HANDLE ProcessHandle;
PPH_SYMBOL_PROVIDER SymbolProvider;
BOOLEAN CustomWalk;
} Initializing;
struct
{
PPH_THREAD_STACK_FRAME StackFrame;
PPH_STRING Symbol;
PPH_STRING FileName;
} ResolveSymbol;
struct
{
PPH_THREAD_STACK_FRAME StackFrame;
PPH_STRING_BUILDER StringBuilder;
} GetTooltip;
struct
{
NTSTATUS Status;
HANDLE ThreadHandle;
HANDLE ProcessHandle;
PCLIENT_ID ClientId;
ULONG Flags;
PPH_PLUGIN_WALK_THREAD_STACK_CALLBACK Callback;
PVOID CallbackContext;
} WalkStack;
} u;
} PH_PLUGIN_THREAD_STACK_CONTROL, *PPH_PLUGIN_THREAD_STACK_CONTROL;
typedef enum _PH_PLUGIN_MEMORY_ITEM_LIST_CONTROL_TYPE
{
PluginMemoryItemListInitialized,
PluginMemoryItemListMaximum
} PH_PLUGIN_MEMORY_ITEM_LIST_CONTROL_TYPE;
typedef struct _PH_PLUGIN_MEMORY_ITEM_LIST_CONTROL
{
PH_PLUGIN_MEMORY_ITEM_LIST_CONTROL_TYPE Type;
union
{
struct
{
PPH_MEMORY_ITEM_LIST List;
} Initialized;
} u;
} PH_PLUGIN_MEMORY_ITEM_LIST_CONTROL, *PPH_PLUGIN_MEMORY_ITEM_LIST_CONTROL;
typedef _Function_class_(PH_SYSINFO_CREATE_SECTION)
PPH_SYSINFO_SECTION NTAPI PH_SYSINFO_CREATE_SECTION(
_In_ PPH_SYSINFO_SECTION Template
);
typedef PH_SYSINFO_CREATE_SECTION *PPH_SYSINFO_CREATE_SECTION;
typedef _Function_class_(PH_SYSINFO_FIND_SECTION)
PPH_SYSINFO_SECTION NTAPI PH_SYSINFO_FIND_SECTION(
_In_ PPH_STRINGREF Name
);
typedef PH_SYSINFO_FIND_SECTION *PPH_SYSINFO_FIND_SECTION;
typedef _Function_class_(PH_SYSINFO_ENTER_SECTION_VIEW)
VOID NTAPI PH_SYSINFO_ENTER_SECTION_VIEW(
_In_ PPH_SYSINFO_SECTION NewSection
);
typedef PH_SYSINFO_ENTER_SECTION_VIEW *PPH_SYSINFO_ENTER_SECTION_VIEW;
typedef _Function_class_(PH_SYSINFO_RESTORE_SUMMARY_VIEW)
VOID NTAPI PH_SYSINFO_RESTORE_SUMMARY_VIEW(
VOID
);
typedef PH_SYSINFO_RESTORE_SUMMARY_VIEW *PPH_SYSINFO_RESTORE_SUMMARY_VIEW;
typedef struct _PH_PLUGIN_SYSINFO_POINTERS
{
HWND WindowHandle;
PPH_SYSINFO_CREATE_SECTION CreateSection;
PPH_SYSINFO_FIND_SECTION FindSection;
PPH_SYSINFO_ENTER_SECTION_VIEW EnterSectionView;
PPH_SYSINFO_RESTORE_SUMMARY_VIEW RestoreSummaryView;
} PH_PLUGIN_SYSINFO_POINTERS, *PPH_PLUGIN_SYSINFO_POINTERS;
typedef _Function_class_(PH_MINIINFO_CREATE_SECTION)
PPH_MINIINFO_SECTION NTAPI PH_MINIINFO_CREATE_SECTION(
_In_ PPH_MINIINFO_SECTION Template
);
typedef PH_MINIINFO_CREATE_SECTION *PPH_MINIINFO_CREATE_SECTION;
typedef _Function_class_(PH_MINIINFO_FIND_SECTION)
PPH_MINIINFO_SECTION NTAPI PH_MINIINFO_FIND_SECTION(
_In_ PPH_STRINGREF Name
);
typedef PH_MINIINFO_FIND_SECTION *PPH_MINIINFO_FIND_SECTION;
typedef struct _PH_MINIINFO_LIST_SECTION PH_MINIINFO_LIST_SECTION, *PPH_MINIINFO_LIST_SECTION;
typedef PPH_MINIINFO_LIST_SECTION (NTAPI *PPH_MINIINFO_CREATE_LIST_SECTION)(
_In_ PCWSTR Name,
_In_ ULONG Flags,
_In_ PPH_MINIINFO_LIST_SECTION Template
);
typedef struct _PH_PLUGIN_MINIINFO_POINTERS
{
HWND WindowHandle;
PPH_MINIINFO_CREATE_SECTION CreateSection;
PPH_MINIINFO_FIND_SECTION FindSection;
PPH_MINIINFO_CREATE_LIST_SECTION CreateListSection;
} PH_PLUGIN_MINIINFO_POINTERS, *PPH_PLUGIN_MINIINFO_POINTERS;
// end_phapppub
// begin_phapppub
typedef struct _PH_NF_ICON_REGISTRATION_DATA *PPH_NF_ICON_REGISTRATION_DATA;
/**
* Creates a notification icon.
*
* \param Plugin A plugin instance structure.
* \param SubId An identifier for the column. This should be unique within the
* plugin.
* \param Guid A unique guid for this icon.
* \param Context A user-defined value.
* \param Text A string describing the notification icon.
* \param Flags A combination of flags.
* \li \c PH_NF_ICON_UNAVAILABLE The notification icon is currently unavailable.
* \param RegistrationData A \ref PH_NF_ICON_REGISTRATION_DATA structure that
* contains registration information.
*/
typedef _Function_class_(PH_REGISTER_TRAY_ICON)
PPH_PLUGIN NTAPI PH_REGISTER_TRAY_ICON(
_In_ PPH_PLUGIN Plugin,
_In_ ULONG SubId,
_In_ GUID Guid,
_In_opt_ PVOID Context,
_In_ PCWSTR Text,
_In_ ULONG Flags,
_In_ PPH_NF_ICON_REGISTRATION_DATA RegistrationData
);
typedef PH_REGISTER_TRAY_ICON *PPH_REGISTER_TRAY_ICON;
typedef struct _PH_TRAY_ICON_POINTERS
{
PPH_REGISTER_TRAY_ICON RegisterTrayIcon;
} PH_TRAY_ICON_POINTERS, *PPH_TRAY_ICON_POINTERS;
// end_phapppub
// begin_phapppub
typedef struct _PH_OPTIONS_SECTION
{
PH_STRINGREF Name;
// end_phapppub
PVOID Instance;
PCWSTR Template;
DLGPROC DialogProc;
PVOID Parameter;
HWND DialogHandle;
HTREEITEM TreeItemHandle;
// begin_phapppub
} PH_OPTIONS_SECTION, *PPH_OPTIONS_SECTION;
// end_phapppub
// begin_phapppub
typedef _Function_class_(PH_OPTIONS_CREATE_SECTION)
PPH_OPTIONS_SECTION NTAPI PH_OPTIONS_CREATE_SECTION(
_In_ PCWSTR Name,
_In_ PVOID Instance,
_In_ PCWSTR Template,
_In_ DLGPROC DialogProc,
_In_opt_ PVOID Parameter
);
typedef PH_OPTIONS_CREATE_SECTION *PPH_OPTIONS_CREATE_SECTION;
typedef _Function_class_(PH_OPTIONS_FIND_SECTION)
PPH_OPTIONS_SECTION NTAPI PH_OPTIONS_FIND_SECTION(
_In_ PPH_STRINGREF Name
);
typedef PH_OPTIONS_FIND_SECTION *PPH_OPTIONS_FIND_SECTION;
typedef _Function_class_(PH_OPTIONS_ENTER_SECTION_VIEW)
VOID NTAPI PH_OPTIONS_ENTER_SECTION_VIEW(
_In_ PPH_OPTIONS_SECTION NewSection
);
typedef PH_OPTIONS_ENTER_SECTION_VIEW *PPH_OPTIONS_ENTER_SECTION_VIEW;
typedef struct _PH_PLUGIN_OPTIONS_POINTERS
{
HWND WindowHandle;
PPH_OPTIONS_CREATE_SECTION CreateSection;
PPH_OPTIONS_FIND_SECTION FindSection;
PPH_OPTIONS_ENTER_SECTION_VIEW EnterSectionView;
} PH_PLUGIN_OPTIONS_POINTERS, *PPH_PLUGIN_OPTIONS_POINTERS;
// end_phapppub
// begin_phapppub
typedef struct _PH_PLUGIN_TREENEW_MESSAGE
{
HWND TreeNewHandle;
PH_TREENEW_MESSAGE Message;
PVOID Parameter1;
PVOID Parameter2;
ULONG SubId;
PVOID Context;
} PH_PLUGIN_TREENEW_MESSAGE, *PPH_PLUGIN_TREENEW_MESSAGE;
typedef _Function_class_(PH_PLUGIN_TREENEW_SORT_FUNCTION)
LONG NTAPI PH_PLUGIN_TREENEW_SORT_FUNCTION(
_In_ PVOID Node1,
_In_ PVOID Node2,
_In_ ULONG SubId,
_In_ PH_SORT_ORDER SortOrder,
_In_ PVOID Context
);
typedef PH_PLUGIN_TREENEW_SORT_FUNCTION *PPH_PLUGIN_TREENEW_SORT_FUNCTION;
typedef _Function_class_(PHSVC_SERVER_PROBE_BUFFER)
NTSTATUS NTAPI PHSVC_SERVER_PROBE_BUFFER(
_In_ PPH_RELATIVE_STRINGREF String,
_In_ ULONG Alignment,
_In_ BOOLEAN AllowNull,
_Out_ PVOID *Pointer
);
typedef PHSVC_SERVER_PROBE_BUFFER *PPHSVC_SERVER_PROBE_BUFFER;
typedef _Function_class_(PHSVC_SERVER_CAPTURE_BUFFER)
NTSTATUS NTAPI PHSVC_SERVER_CAPTURE_BUFFER(
_In_ PPH_RELATIVE_STRINGREF String,
_In_ BOOLEAN AllowNull,
_Out_ PVOID *CapturedBuffer
);
typedef PHSVC_SERVER_CAPTURE_BUFFER *PPHSVC_SERVER_CAPTURE_BUFFER;
typedef _Function_class_(PHSVC_SERVER_CAPTURE_STRING)
NTSTATUS NTAPI PHSVC_SERVER_CAPTURE_STRING(
_In_ PPH_RELATIVE_STRINGREF String,
_In_ BOOLEAN AllowNull,
_Out_ PPH_STRING *CapturedString
);
typedef PHSVC_SERVER_CAPTURE_STRING *PPHSVC_SERVER_CAPTURE_STRING;
typedef struct _PH_PLUGIN_PHSVC_REQUEST
{
ULONG SubId;
NTSTATUS ReturnStatus;
PVOID InBuffer;
ULONG InLength;
PVOID OutBuffer;
ULONG OutLength;
PPHSVC_SERVER_PROBE_BUFFER ProbeBuffer;
PPHSVC_SERVER_CAPTURE_BUFFER CaptureBuffer;
PPHSVC_SERVER_CAPTURE_STRING CaptureString;
} PH_PLUGIN_PHSVC_REQUEST, *PPH_PLUGIN_PHSVC_REQUEST;
typedef _Function_class_(PHSVC_CLIENT_FREE_HEAP)
VOID NTAPI PHSVC_CLIENT_FREE_HEAP(
_In_ PVOID Memory
);
typedef PHSVC_CLIENT_FREE_HEAP *PPHSVC_CLIENT_FREE_HEAP;
typedef _Function_class_(PHSVC_CLIENT_CREATE_STRING)
PVOID NTAPI PHSVC_CLIENT_CREATE_STRING(
_In_opt_ PVOID String,
_In_ SIZE_T Length,
_Out_ PPH_RELATIVE_STRINGREF StringRef
);
typedef PHSVC_CLIENT_CREATE_STRING *PPHSVC_CLIENT_CREATE_STRING;
typedef struct _PH_PLUGIN_PHSVC_CLIENT
{
HANDLE ServerProcessId;
PPHSVC_CLIENT_FREE_HEAP FreeHeap;
PPHSVC_CLIENT_CREATE_STRING CreateString;
} PH_PLUGIN_PHSVC_CLIENT, *PPH_PLUGIN_PHSVC_CLIENT;
// Plugin structures
typedef struct _PH_PLUGIN_INFORMATION
{
PCWSTR DisplayName;
PCWSTR Author;
PCWSTR Description;
PCWSTR Url;
BOOLEAN HasOptions;
BOOLEAN Reserved1[3];
PVOID Interface;
} PH_PLUGIN_INFORMATION, *PPH_PLUGIN_INFORMATION;
#define PH_PLUGIN_FLAG_RESERVED 0x1
// end_phapppub
// begin_phapppub
typedef struct _PH_PLUGIN
{
// Public
PH_AVL_LINKS Links;
PVOID DllBase;
// end_phapppub
// Private
PH_STRINGREF Name;
ULONG Flags;
PH_PLUGIN_INFORMATION Information;
PH_CALLBACK Callbacks[PluginCallbackMaximum];
PH_EM_APP_CONTEXT AppContext;
// begin_phapppub
} PH_PLUGIN, *PPH_PLUGIN;
// end_phapppub
// begin_phapppub
// Plugin API
PHAPPAPI
PPH_PLUGIN
NTAPI
PhRegisterPluginByName(
_In_ PCPH_STRINGREF Name,
_In_ PVOID DllBase,
_Out_opt_ PPH_PLUGIN_INFORMATION *Information
);
FORCEINLINE
PPH_PLUGIN
NTAPI
PhRegisterPlugin(
_In_ PCWSTR Name,
_In_ PVOID DllBase,
_Out_opt_ PPH_PLUGIN_INFORMATION* Information
)
{
PH_STRINGREF name;
PhInitializeStringRef(&name, Name);
return PhRegisterPluginByName(&name, DllBase, Information);
}
PHAPPAPI
PPH_PLUGIN
NTAPI
PhFindPlugin2(
_In_ PCPH_STRINGREF Name
);
/**
* Locates a plugin instance structure.
*
* \param Name The name of the plugin.
*
* \return A plugin instance structure, or NULL if the plugin was not found.
*/
FORCEINLINE
PPH_PLUGIN
NTAPI
PhFindPlugin(
_In_ PCWSTR Name
)
{
PH_STRINGREF name;
PhInitializeStringRef(&name, Name);
return PhFindPlugin2(&name);
}
PHAPPAPI
PVOID
NTAPI
PhGetPluginInterface(
_In_ PCPH_STRINGREF Name,
_In_opt_ ULONG Version
);
FORCEINLINE
PVOID
NTAPI
PhGetPluginInterfaceZ(
_In_ PCWSTR Name,
_In_opt_ ULONG Version
)
{
PH_STRINGREF name;
PhInitializeStringRef(&name, Name);
return PhGetPluginInterface(&name, Version);
}
PHAPPAPI
PPH_PLUGIN_INFORMATION
NTAPI
PhGetPluginInformation(
_In_ PPH_PLUGIN Plugin
);
PHAPPAPI
PPH_CALLBACK
NTAPI
PhGetPluginCallback(
_In_ PPH_PLUGIN Plugin,
_In_ PH_PLUGIN_CALLBACK Callback
);
PHAPPAPI
PPH_CALLBACK
NTAPI
PhGetGeneralCallback(
_In_ PH_GENERAL_CALLBACK Callback
);
PHAPPAPI
ULONG
NTAPI
PhPluginReserveIds(
_In_ ULONG Count
);
typedef struct _PH_PLUGIN_MENU_ITEM *PPH_PLUGIN_MENU_ITEM;
_Function_class_(PH_PLUGIN_MENU_ITEM_DELETE_FUNCTION)
typedef VOID (NTAPI PH_PLUGIN_MENU_ITEM_DELETE_FUNCTION)(
_In_ PPH_PLUGIN_MENU_ITEM MenuItem
);
typedef PH_PLUGIN_MENU_ITEM_DELETE_FUNCTION *PPH_PLUGIN_MENU_ITEM_DELETE_FUNCTION;
typedef struct _PH_PLUGIN_MENU_ITEM
{
PPH_PLUGIN Plugin;
ULONG Id;
ULONG Reserved1;
PVOID Context;
HWND OwnerWindow; // valid only when the menu item is chosen
PVOID Reserved2;
PVOID Reserved3;
PPH_PLUGIN_MENU_ITEM_DELETE_FUNCTION DeleteFunction; // valid only for EMENU-based menu items
} PH_PLUGIN_MENU_ITEM, *PPH_PLUGIN_MENU_ITEM;
// Location
#define PH_MENU_ITEM_LOCATION_SYSTEM 0
#define PH_MENU_ITEM_LOCATION_VIEW 1
#define PH_MENU_ITEM_LOCATION_TOOLS 2
#define PH_MENU_ITEM_LOCATION_USERS 3
#define PH_MENU_ITEM_LOCATION_HELP 4
typedef struct _PH_PLUGIN_SYSTEM_STATISTICS
{
PSYSTEM_PERFORMANCE_INFORMATION Performance;
ULONG NumberOfProcesses;
ULONG NumberOfThreads;
ULONG NumberOfHandles;
FLOAT CpuKernelUsage;
FLOAT CpuUserUsage;
PH_UINT64_DELTA IoReadDelta;
PH_UINT64_DELTA IoWriteDelta;
PH_UINT64_DELTA IoOtherDelta;
ULONG CommitPages;
ULONG PhysicalPages;
HANDLE MaxCpuProcessId;
HANDLE MaxIoProcessId;
PPH_CIRCULAR_BUFFER_FLOAT CpuKernelHistory;
PPH_CIRCULAR_BUFFER_FLOAT CpuUserHistory;
PPH_CIRCULAR_BUFFER_FLOAT *CpusKernelHistory;
PPH_CIRCULAR_BUFFER_FLOAT *CpusUserHistory;
PPH_CIRCULAR_BUFFER_ULONG64 IoReadHistory;
PPH_CIRCULAR_BUFFER_ULONG64 IoWriteHistory;
PPH_CIRCULAR_BUFFER_ULONG64 IoOtherHistory;
PPH_CIRCULAR_BUFFER_ULONG CommitHistory;
PPH_CIRCULAR_BUFFER_ULONG PhysicalHistory;
PPH_CIRCULAR_BUFFER_ULONG MaxCpuHistory; // ID of max. CPU process
PPH_CIRCULAR_BUFFER_ULONG MaxIoHistory; // ID of max. I/O process
PPH_CIRCULAR_BUFFER_FLOAT MaxCpuUsageHistory;
PPH_CIRCULAR_BUFFER_ULONG64 MaxIoReadOtherHistory;
PPH_CIRCULAR_BUFFER_ULONG64 MaxIoWriteHistory;
} PH_PLUGIN_SYSTEM_STATISTICS, *PPH_PLUGIN_SYSTEM_STATISTICS;
PHAPPAPI
VOID
NTAPI
PhPluginGetSystemStatistics(
_Out_ PPH_PLUGIN_SYSTEM_STATISTICS Statistics
);
PHAPPAPI
PPH_EMENU_ITEM
NTAPI
PhPluginCreateEMenuItem(
_In_ PPH_PLUGIN Plugin,
_In_ ULONG Flags,
_In_ ULONG Id,
_In_ PCWSTR Text,
_In_opt_ PVOID Context
);
PHAPPAPI
BOOLEAN
NTAPI
PhPluginAddMenuHook(
_Inout_ PPH_PLUGIN_MENU_INFORMATION MenuInfo,
_In_ PPH_PLUGIN Plugin,
_In_opt_ PVOID Context
);
// end_phapppub
VOID
NTAPI
PhPluginInitializeMenuInfo(
_Out_ PPH_PLUGIN_MENU_INFORMATION MenuInfo,
_In_opt_ PPH_EMENU Menu,
_In_ HWND OwnerWindow,
_In_ ULONG Flags
);
BOOLEAN
NTAPI
PhPluginTriggerEMenuItem(
_In_ PPH_PLUGIN_MENU_INFORMATION MenuInfo,
_In_ PPH_EMENU_ITEM Item
);
// begin_phapppub
PHAPPAPI
BOOLEAN
NTAPI
PhPluginAddTreeNewColumn(
_In_ PPH_PLUGIN Plugin,
_In_ PVOID CmData,
_In_ PPH_TREENEW_COLUMN Column,
_In_ ULONG SubId,
_In_opt_ PVOID Context,
_In_opt_ PPH_PLUGIN_TREENEW_SORT_FUNCTION SortFunction
);
PHAPPAPI
VOID
NTAPI
PhPluginSetObjectExtension(
_In_ PPH_PLUGIN Plugin,
_In_ PH_EM_OBJECT_TYPE ObjectType,
_In_ ULONG ExtensionSize,
_In_opt_ PPH_EM_OBJECT_CALLBACK CreateCallback,
_In_opt_ PPH_EM_OBJECT_CALLBACK DeleteCallback
);
PHAPPAPI
PVOID
NTAPI
PhPluginGetObjectExtension(
_In_ PPH_PLUGIN Plugin,
_In_ PVOID Object,
_In_ PH_EM_OBJECT_TYPE ObjectType
);
PHAPPAPI
VOID
NTAPI
PhPluginEnableTreeNewNotify(
_In_ PPH_PLUGIN Plugin,
_In_ PVOID CmData
);
PHAPPAPI
NTSTATUS
NTAPI
PhPluginQueryPhSvc(
_Out_ PPH_PLUGIN_PHSVC_CLIENT Client
);
PHAPPAPI
NTSTATUS
NTAPI
PhPluginCallPhSvc(
_In_ PPH_PLUGIN Plugin,
_In_ ULONG SubId,
_In_reads_bytes_opt_(InLength) PVOID InBuffer,
_In_ ULONG InLength,
_Out_writes_bytes_opt_(OutLength) PVOID OutBuffer,
_In_ ULONG OutLength
);
PHAPPAPI
PPH_STRING
NTAPI
PhGetPluginName(
_In_ PPH_PLUGIN Plugin
);
PHAPPAPI
PPH_STRING
NTAPI
PhGetPluginFileName(
_In_ PPH_PLUGIN Plugin
);
_Function_class_(PH_PLUGIN_ENUMERATE)
typedef NTSTATUS (NTAPI PH_PLUGIN_ENUMERATE)(
_In_ PPH_PLUGIN Information,
_In_opt_ PVOID Context
);
typedef PH_PLUGIN_ENUMERATE *PPH_PLUGIN_ENUMERATE;
PHAPPAPI
VOID
NTAPI
PhEnumeratePlugins(
_In_ PPH_PLUGIN_ENUMERATE Callback,
_In_opt_ PVOID Context
);
// end_phapppub
#endif
================================================
FILE: SystemInformer/include/phsettings.h
================================================
/*
* Copyright (c) 2022 Winsider Seminars & Solutions, Inc. All rights reserved.
*
* This file is part of System Informer.
*
* Authors:
*
* wj32 2010-2016
* dmex 2017-2023
*
*/
#ifndef PH_SETTINGS_H
#define PH_SETTINGS_H
//
// Application Typedefs
//
EXTERN_C
VOID
NTAPI
PhAddDefaultSettings(
VOID
);
EXTERN_C
VOID
NTAPI
PhUpdateCachedSettings(
VOID
);
//
// Cached settings
//
#pragma push_macro("EXT")
#undef EXT
#ifdef PH_SETTINGS_PRIVATE
#define EXT
#else
#define EXT extern
#endif
EXT BOOLEAN PhEnableProcessQueryStage2;
EXT BOOLEAN PhEnableServiceQueryStage2;
EXT BOOLEAN PhEnableWindowText;
EXT BOOLEAN PhEnableTooltipSupport;
EXT BOOLEAN PhEnableLinuxSubsystemSupport;
EXT ULONG PhCsEnableNetworkResolveDoH;
EXT ULONG PhCsEnableVersionSupport;
EXT BOOLEAN PhEnableDeferredLayout;
EXT BOOLEAN PhEnableKsiWarnings;
EXT BOOLEAN PhEnableKsiSupport;
EXT ULONG PhCsForceNoParent;
EXT ULONG PhCsHighlightingDuration;
EXT ULONG PhCsPropagateCpuUsage;
EXT ULONG PhCsScrollToNewProcesses;
EXT ULONG PhCsScrollToRemovedProcesses;
EXT ULONG PhCsSortChildProcesses;
EXT ULONG PhCsSortRootProcesses;
EXT ULONG PhCsShowCpuBelow001;
EXT ULONG PhCsUpdateInterval;
EXT ULONG PhCsColorNew;
EXT ULONG PhCsColorRemoved;
EXT ULONG PhCsUseColorOwnProcesses;
EXT ULONG PhCsColorOwnProcesses;
EXT ULONG PhCsUseColorSystemProcesses;
EXT ULONG PhCsColorSystemProcesses;
EXT ULONG PhCsUseColorServiceProcesses;
EXT ULONG PhCsColorServiceProcesses;
EXT ULONG PhCsUseColorBackgroundProcesses;
EXT ULONG PhCsColorBackgroundProcesses;
EXT ULONG PhCsUseColorJobProcesses;
EXT ULONG PhCsColorJobProcesses;
EXT ULONG PhCsUseColorWow64Processes;
EXT ULONG PhCsColorWow64Processes;
EXT ULONG PhCsUseColorDebuggedProcesses;
EXT ULONG PhCsColorDebuggedProcesses;
EXT ULONG PhCsUseColorElevatedProcesses;
EXT ULONG PhCsColorElevatedProcesses;
EXT ULONG PhCsUseColorHandleFiltered;
EXT ULONG PhCsColorHandleFiltered;
EXT ULONG PhCsUseColorImmersiveProcesses;
EXT ULONG PhCsColorImmersiveProcesses;
EXT ULONG PhCsUseColorUIAccessProcesses;
EXT ULONG PhCsColorUIAccessProcesses;
EXT ULONG PhCsUseColorPicoProcesses;
EXT ULONG PhCsColorPicoProcesses;
EXT ULONG PhCsUseColorSuspended;
EXT ULONG PhCsColorSuspended;
EXT ULONG PhCsUseColorDotNet;
EXT ULONG PhCsColorDotNet;
EXT ULONG PhCsUseColorPacked;
EXT ULONG PhCsColorPacked;
EXT ULONG PhCsUseColorLowImageCoherency;
EXT ULONG PhCsColorLowImageCoherency;
EXT ULONG PhCsUseColorPartiallySuspended;
EXT ULONG PhCsColorPartiallySuspended;
EXT ULONG PhCsUseColorGuiThreads;
EXT ULONG PhCsColorGuiThreads;
EXT ULONG PhCsUseColorRelocatedModules;
EXT ULONG PhCsColorRelocatedModules;
EXT ULONG PhCsUseColorProtectedHandles;
EXT ULONG PhCsColorProtectedHandles;
EXT ULONG PhCsUseColorProtectedInheritHandles;
EXT ULONG PhCsColorProtectedInheritHandles;
EXT ULONG PhCsUseColorProtectedProcess;
EXT ULONG PhCsColorProtectedProcess;
EXT ULONG PhCsUseColorInheritHandles;
EXT ULONG PhCsColorInheritHandles;
EXT ULONG PhCsUseColorEfficiencyMode;
EXT ULONG PhCsColorEfficiencyMode;
EXT ULONG PhCsGraphShowText;
EXT ULONG PhCsGraphColorMode;
EXT ULONG PhCsColorCpuKernel;
EXT ULONG PhCsColorCpuUser;
EXT ULONG PhCsColorIoReadOther;
EXT ULONG PhCsColorIoWrite;
EXT ULONG PhCsColorPrivate;
EXT ULONG PhCsColorPhysical;
EXT ULONG PhCsColorPowerUsage;
EXT ULONG PhCsColorTemperature;
EXT ULONG PhCsColorFanRpm;
EXT ULONG PhCsUseColorUnknown;
EXT ULONG PhCsColorUnknown;
EXT ULONG PhCsUseColorServiceDisabled;
EXT ULONG PhCsColorServiceDisabled;
EXT ULONG PhCsUseColorServiceStop;
EXT ULONG PhCsColorServiceStop;
EXT BOOLEAN PhEnableImageCoherencySupport;
EXT ULONG PhCsImageCoherencyScanLevel;
EXT ULONG PhCsEnableGraphMaxScale;
EXT ULONG PhCsEnableGraphMaxText;
EXT ULONG PhCsEnableAvxSupport;
EXT ULONG PhCsEnableHandleSnapshot;
EXT BOOLEAN PhEnableProcessMonitor;
EXT ULONG PhProcessMonitorLookback;
EXT ULONG PhProcessMonitorCacheLimit;
#pragma pop_macro("EXT")
#define PH_GET_INTEGER_CACHED_SETTING(Name) ((PhCs##Name) = PhGetIntegerSetting(TEXT(#Name)))
#define PH_SET_INTEGER_CACHED_SETTING(Name, Value) (PhSetIntegerSetting(TEXT(#Name), (PhCs##Name) = (Value)))
// begin_phapppub
#define SETTING_SCHEMA_FILE L"$schema"
#define SETTING_ALLOW_ONLY_ONE_INSTANCE L"AllowOnlyOneInstance"
#define SETTING_CLOSE_ON_ESCAPE L"CloseOnEscape"
#define SETTING_DBGHELP_SEARCH_PATH L"DbgHelpSearchPath"
#define SETTING_DBGHELP_UNDECORATE L"DbgHelpUndecorate"
#define SETTING_DBGHELP_VERIFY_MICROSOFT_CHAIN L"DbgHelpVerifyMicrosoftChain"
#define SETTING_DISABLED_PLUGINS L"DisabledPlugins"
#define SETTING_ELEVATION_LEVEL L"ElevationLevel"
#define SETTING_ENABLE_ADVANCED_OPTIONS L"EnableAdvancedOptions"
#define SETTING_ENABLE_AVX_SUPPORT L"EnableAvxSupport"
#define SETTING_ENABLE_BITMAP_SUPPORT L"EnableBitmapSupport"
#define SETTING_ENABLE_BREAK_ON_TERMINATION L"EnableBreakOnTermination"
#define SETTING_ENABLE_BOOT_OBJECTS_ENUMERATE L"EnableBootObjectsEnumerate"
#define SETTING_ENABLE_COMMAND_LINE_TOOLTIPS L"EnableCommandLineTooltips"
#define SETTING_ENABLE_CYCLE_CPU_USAGE L"EnableCycleCpuUsage"
#define SETTING_ENABLE_DEFERRED_LAYOUT L"EnableDeferredLayout"
#define SETTING_ENABLE_DEVICE_SUPPORT L"EnableDeviceSupport"
#define SETTING_ENABLE_DEVICE_NOTIFY_SUPPORT L"EnableDeviceNotifySupport"
#define SETTING_ENABLE_IMAGE_COHERENCY_SUPPORT L"EnableImageCoherencySupport"
#define SETTING_ENABLE_INSTANT_TOOLTIPS L"EnableInstantTooltips"
#define SETTING_ENABLE_HEAP_REFLECTION L"EnableHeapReflection"
#define SETTING_ENABLE_HEAP_MEMORY_TAGGING L"EnableHeapMemoryTagging"
#define SETTING_ENABLE_LAST_PROCESS_SHUTDOWN L"EnableLastProcessShutdown"
#define SETTING_ENABLE_LINUX_SUBSYSTEM_SUPPORT L"EnableLinuxSubsystemSupport"
#define SETTING_ENABLE_HANDLE_SNAPSHOT L"EnableHandleSnapshot"
#define SETTING_ENABLE_MINIDUMP_KERNEL_MINIDUMP L"EnableMinidumpKernelMinidump"
#define SETTING_ENABLE_MINIDUMP_SNAPSHOT L"EnableMinidumpSnapshot"
#define SETTING_ENABLE_MONOSPACE_FONT L"EnableMonospaceFont"
#define SETTING_ENABLE_NETWORK_BOUND_CONNECTIONS L"EnableNetworkBoundConnections"
#define SETTING_ENABLE_NETWORK_RESOLVE L"EnableNetworkResolve"
#define SETTING_ENABLE_NETWORK_RESOLVE_DOH L"EnableNetworkResolveDoH"
#define SETTING_ENABLE_MEM_STRINGS_TREE_DIALOG L"EnableMemStringsTreeDialog"
#define SETTING_ENABLE_PACKAGE_ICON_SUPPORT L"EnablePackageIconSupport"
#define SETTING_ENABLE_PROCESS_HANDLE_PNP_DEVICE_NAME_SUPPORT L"EnableProcessHandlePnPDeviceNameSupport"
#define SETTING_ENABLE_PLUGINS L"EnablePlugins"
#define SETTING_ENABLE_PLUGINS_NATIVE L"EnablePluginsNative"
#define SETTING_ENABLE_GRAPH_MAX_SCALE L"EnableGraphMaxScale"
#define SETTING_ENABLE_GRAPH_MAX_TEXT L"EnableGraphMaxText"
#define SETTING_ENABLE_HIGH_RESOLUTION_PROVIDER_TIMER L"EnableHighResolutionProviderTimer"
#define SETTING_ENABLE_SERVICE_NON_POLL L"EnableServiceNonPoll"
#define SETTING_ENABLE_SERVICE_NON_POLL_NOTIFY L"EnableServiceNonPollNotify"
#define SETTING_ENABLE_SERVICE_STAGE2 L"EnableServiceStage2"
#define SETTING_ENABLE_SERVICE_PROGRESS_DIALOG L"EnableServiceProgressDialog"
#define SETTING_ENABLE_SHELL_EXECUTE_SKIP_IFEO_DEBUGGER L"EnableShellExecuteSkipIfeoDebugger"
#define SETTING_ENABLE_STAGE2 L"EnableStage2"
#define SETTING_ENABLE_STREAMER_MODE L"EnableStreamerMode"
#define SETTING_ENABLE_START_AS_ADMIN L"EnableStartAsAdmin"
#define SETTING_ENABLE_START_AS_ADMIN_ALWAYS_ON_TOP L"EnableStartAsAdminAlwaysOnTop"
#define SETTING_ENABLE_DEFAULT_SAFE_PLUGINS L"EnableDefaultSafePlugins"
#define SETTING_ENABLE_SECURITY_ADVANCED_DIALOG L"EnableSecurityAdvancedDialog"
#define SETTING_ENABLE_SHORT_RELATIVE_START_TIME L"EnableShortRelativeStartTime"
#define SETTING_ENABLE_SHUTDOWN_CRITICAL_MENU L"EnableShutdownCriticalMenu"
#define SETTING_ENABLE_SHUTDOWN_BOOT_MENU L"EnableShutdownBootMenu"
#define SETTING_ENABLE_SILENT_CRASH_NOTIFY L"EnableSilentCrashNotify"
#define SETTING_ENABLE_THEME_SUPPORT L"EnableThemeSupport"
#define SETTING_ENABLE_THEME_ACRYLIC_SUPPORT L"EnableThemeAcrylicSupport"
#define SETTING_ENABLE_THEME_ACRYLIC_WINDOW_SUPPORT L"EnableThemeAcrylicWindowSupport"
#define SETTING_ENABLE_THEME_ANIMATION L"EnableThemeAnimation"
#define SETTING_ENABLE_THEME_NATIVE_BUTTONS L"EnableThemeNativeButtons"
#define SETTING_ENABLE_THREAD_STACK_INLINE_SYMBOLS L"EnableThreadStackInlineSymbols"
#define SETTING_ENABLE_THREAD_STACK_LINE_INFORMATION L"EnableThreadStackLineInformation"
#define SETTING_ENABLE_TOKEN_REMOVED_PRIVILEGES L"EnableTokenRemovedPrivileges"
#define SETTING_ENABLE_TOOLTIP_SUPPORT L"EnableTooltipSupport"
#define SETTING_ENABLE_UPDATE_DEFAULT_FIRMWARE_BOOT_ENTRY L"EnableUpdateDefaultFirmwareBootEntry"
#define SETTING_ENABLE_VERSION_SUPPORT L"EnableVersionSupport"
#define SETTING_ENABLE_WARNINGS L"EnableWarnings"
#define SETTING_ENABLE_WARNINGS_RUNAS L"EnableWarningsRunas"
#define SETTING_ENABLE_WINDOW_TEXT L"EnableWindowText"
#define SETTING_ENVIRONMENT_TREE_LIST_COLUMNS L"EnvironmentTreeListColumns"
#define SETTING_ENVIRONMENT_TREE_LIST_SORT L"EnvironmentTreeListSort"
#define SETTING_ENVIRONMENT_TREE_LIST_FLAGS L"EnvironmentTreeListFlags"
#define SETTING_SEARCH_CONTROL_REGEX L"SearchControlRegex"
#define SETTING_SEARCH_CONTROL_CASE_SENSITIVE L"SearchControlCaseSensitive"
#define SETTING_FIND_OBJ_TREE_LIST_COLUMNS L"FindObjTreeListColumns"
#define SETTING_FIND_OBJ_WINDOW_POSITION L"FindObjWindowPosition"
#define SETTING_FIND_OBJ_WINDOW_SIZE L"FindObjWindowSize"
#define SETTING_THREAD_STACKS_TREE_LIST_COLUMNS L"ThreadStacksTreeListColumns"
#define SETTING_THREAD_STACKS_WINDOW_POSITION L"ThreadStacksWindowPosition"
#define SETTING_THREAD_STACKS_WINDOW_SIZE L"ThreadStacksWindowSize"
#define SETTING_FILE_BROWSE_EXECUTABLE L"FileBrowseExecutable"
#define SETTING_FIRST_RUN L"FirstRun"
#define SETTING_FONT L"Font"
#define SETTING_FONT_MONOSPACE L"FontMonospace"
#define SETTING_FONT_QUALITY L"FontQuality"
#define SETTING_FORCE_NO_PARENT L"ForceNoParent"
#define SETTING_HANDLE_TREE_LIST_COLUMNS L"HandleTreeListColumns"
#define SETTING_HANDLE_TREE_LIST_SORT L"HandleTreeListSort"
#define SETTING_HANDLE_TREE_LIST_FLAGS L"HandleTreeListFlags"
#define SETTING_HANDLE_PROPERTIES_WINDOW_POSITION L"HandlePropertiesWindowPosition"
#define SETTING_HANDLE_PROPERTIES_WINDOW_SIZE L"HandlePropertiesWindowSize"
#define SETTING_HANDLE_STATISTICS_LIST_VIEW_COLUMNS L"HandleStatisticsListViewColumns"
#define SETTING_HANDLE_STATISTICS_LIST_VIEW_SORT L"HandleStatisticsListViewSort"
#define SETTING_HANDLE_STATISTICS_WINDOW_POSITION L"HandleStatisticsWindowPosition"
#define SETTING_HANDLE_STATISTICS_WINDOW_SIZE L"HandleStatisticsWindowSize"
#define SETTING_HIDE_DEFAULT_SERVICES L"HideDefaultServices"
#define SETTING_HIDE_DRIVER_SERVICES L"HideDriverServices"
#define SETTING_HIDE_FREE_REGIONS L"HideFreeRegions"
#define SETTING_HIDE_ON_CLOSE L"HideOnClose"
#define SETTING_HIDE_ON_MINIMIZE L"HideOnMinimize"
#define SETTING_HIDE_OTHER_USER_PROCESSES L"HideOtherUserProcesses"
#define SETTING_HIDE_SIGNED_PROCESSES L"HideSignedProcesses"
#define SETTING_HIDE_MICROSOFT_PROCESSES L"HideMicrosoftProcesses"
#define SETTING_HIDE_WAITING_CONNECTIONS L"HideWaitingConnections"
#define SETTING_HIGHLIGHTING_DURATION L"HighlightingDuration"
#define SETTING_ICON_BALLOON_SHOW_ICON L"IconBalloonShowIcon"
#define SETTING_ICON_BALLOON_MUTE_SOUND L"IconBalloonMuteSound"
#define SETTING_TOAST_NOTIFY_ENABLED L"ToastNotifyEnabled"
#define SETTING_ICON_TRAY_GUIDS L"IconTrayGuids"
#define SETTING_ICON_TRAY_PERSIST_GUID_ENABLED L"IconTrayPersistGuidEnabled"
#define SETTING_ICON_TRAY_LAZY_START_DELAY L"IconTrayLazyStartDelay"
#define SETTING_ICON_IGNORE_BALLOON_CLICK L"IconIgnoreBalloonClick"
#define SETTING_ICON_SETTINGS L"IconSettings"
#define SETTING_ICON_NOTIFY_MASK L"IconNotifyMask"
#define SETTING_ICON_PROCESSES L"IconProcesses"
#define SETTING_ICON_SINGLE_CLICK L"IconSingleClick"
#define SETTING_ICON_TOGGLES_VISIBILITY L"IconTogglesVisibility"
#define SETTING_ICON_TRANSPARENCY_ENABLED L"IconTransparencyEnabled"
#define SETTING_INFORMATION_WINDOW_POSITION L"InformationWindowPosition"
#define SETTING_INFORMATION_WINDOW_SIZE L"InformationWindowSize"
#define SETTING_IMAGE_COHERENCY_SCAN_LEVEL L"ImageCoherencyScanLevel"
#define SETTING_JOB_LIST_VIEW_COLUMNS L"JobListViewColumns"
#define SETTING_LOG_ENTRIES L"LogEntries"
#define SETTING_LOG_LIST_VIEW_COLUMNS L"LogListViewColumns"
#define SETTING_LOG_WINDOW_POSITION L"LogWindowPosition"
#define SETTING_LOG_WINDOW_SIZE L"LogWindowSize"
#define SETTING_MAIN_WINDOW_ALWAYS_ON_TOP L"MainWindowAlwaysOnTop"
#define SETTING_MAIN_WINDOW_CLASS_NAME L"MainWindowClassName"
#define SETTING_MAIN_WINDOW_OPACITY L"MainWindowOpacity"
#define SETTING_MAIN_WINDOW_POSITION L"MainWindowPosition"
#define SETTING_MAIN_WINDOW_SIZE L"MainWindowSize"
#define SETTING_MAIN_WINDOW_STATE L"MainWindowState"
#define SETTING_MAIN_WINDOW_TAB_RESTORE_ENABLED L"MainWindowTabRestoreEnabled"
#define SETTING_MAIN_WINDOW_TAB_RESTORE_INDEX L"MainWindowTabRestoreIndex"
#define SETTING_MAX_SIZE_UNIT L"MaxSizeUnit"
#define SETTING_MAX_PRECISION_UNIT L"MaxPrecisionUnit"
#define SETTING_MEM_EDIT_BYTES_PER_ROW L"MemEditBytesPerRow"
#define SETTING_MEM_EDIT_GOTO_CHOICES L"MemEditGotoChoices"
#define SETTING_MEM_EDIT_POSITION L"MemEditPosition"
#define SETTING_MEM_EDIT_SIZE L"MemEditSize"
#define SETTING_MEM_FILTER_CHOICES L"MemFilterChoices"
#define SETTING_MEM_RESULTS_LIST_VIEW_COLUMNS L"MemResultsListViewColumns"
#define SETTING_MEM_RESULTS_POSITION L"MemResultsPosition"
#define SETTING_MEM_RESULTS_SIZE L"MemResultsSize"
#define SETTING_MEMORY_LIST_FLAGS L"MemoryListFlags"
#define SETTING_MEMORY_TREE_LIST_COLUMNS L"MemoryTreeListColumns"
#define SETTING_MEMORY_TREE_LIST_SORT L"MemoryTreeListSort"
#define SETTING_MEMORY_LISTS_WINDOW_POSITION L"MemoryListsWindowPosition"
#define SETTING_MEMORY_READ_WRITE_ADDRESS_CHOICES L"MemoryReadWriteAddressChoices"
#define SETTING_MEMORY_MODIFIED_WINDOW_POSITION L"MemoryModifiedWindowPosition"
#define SETTING_MEMORY_MODIFIED_WINDOW_SIZE L"MemoryModifiedWindowSize"
#define SETTING_MEMORY_MODIFIED_LIST_VIEW_COLUMNS L"MemoryModifiedListViewColumns"
#define SETTING_MEMORY_MODIFIED_LIST_VIEW_SORT L"MemoryModifiedListViewSort"
#define SETTING_MEM_STRINGS_TREE_LIST_COLUMNS L"MemStringsTreeListColumns"
#define SETTING_MEM_STRINGS_TREE_LIST_SORT L"MemStringsTreeListSort"
#define SETTING_MEM_STRINGS_TREE_LIST_FLAGS L"MemStringsTreeListFlags"
#define SETTING_MEM_STRINGS_MINIMUM_LENGTH L"MemStringsMinimumLength"
#define SETTING_MEM_STRINGS_WINDOW_POSITION L"MemStringsWindowPosition"
#define SETTING_MEM_STRINGS_WINDOW_SIZE L"MemStringsWindowSize"
#define SETTING_MINI_INFO_CONTAINER_CLASS_NAME L"MiniInfoContainerClassName"
#define SETTING_MINI_INFO_WINDOW_CLASS_NAME L"MiniInfoWindowClassName"
#define SETTING_MINI_INFO_WINDOW_ENABLED L"MiniInfoWindowEnabled"
#define SETTING_MINI_INFO_WINDOW_OPACITY L"MiniInfoWindowOpacity"
#define SETTING_MINI_INFO_WINDOW_PINNED L"MiniInfoWindowPinned"
#define SETTING_MINI_INFO_WINDOW_POSITION L"MiniInfoWindowPosition"
#define SETTING_MINI_INFO_WINDOW_REFRESH_AUTOMATICALLY L"MiniInfoWindowRefreshAutomatically"
#define SETTING_MINI_INFO_WINDOW_SIZE L"MiniInfoWindowSize"
#define SETTING_MODULE_TREE_LIST_FLAGS L"ModuleTreeListFlags"
#define SETTING_MODULE_TREE_LIST_COLUMNS L"ModuleTreeListColumns"
#define SETTING_MODULE_TREE_LIST_SORT L"ModuleTreeListSort"
#define SETTING_NETWORK_TREE_LIST_COLUMNS L"NetworkTreeListColumns"
#define SETTING_NETWORK_TREE_LIST_SORT L"NetworkTreeListSort"
#define SETTING_NON_POLL_FLUSH_INTERVAL L"NonPollFlushInterval"
#define SETTING_NO_PURGE_PROCESS_RECORDS L"NoPurgeProcessRecords"
#define SETTING_OPTIONS_CUSTOM_COLOR_LIST L"OptionsCustomColorList"
#define SETTING_OPTIONS_WINDOW_ADVANCED_COLUMNS L"OptionsWindowAdvancedColumns"
#define SETTING_OPTIONS_WINDOW_ADVANCED_FLAGS L"OptionsWindowAdvancedFlags"
#define SETTING_OPTIONS_WINDOW_POSITION L"OptionsWindowPosition"
#define SETTING_OPTIONS_WINDOW_SIZE L"OptionsWindowSize"
#define SETTING_PAGE_FILE_WINDOW_POSITION L"PageFileWindowPosition"
#define SETTING_PAGE_FILE_WINDOW_SIZE L"PageFileWindowSize"
#define SETTING_PAGE_FILE_LIST_VIEW_COLUMNS L"PageFileListViewColumns"
#define SETTING_PLUGIN_MANAGER_TREE_LIST_COLUMNS L"PluginManagerTreeListColumns"
#define SETTING_PROCESS_SERVICE_LIST_VIEW_COLUMNS L"ProcessServiceListViewColumns"
#define SETTING_PROCESS_TREE_COLUMN_SET_CONFIG L"ProcessTreeColumnSetConfig"
#define SETTING_PROCESS_TREE_LIST_COLUMNS L"ProcessTreeListColumns"
#define SETTING_PROCESS_TREE_LIST_SORT L"ProcessTreeListSort"
#define SETTING_PROCESS_TREE_LIST_NAME_DEFAULT L"ProcessTreeListNameDefault"
#define SETTING_PROC_PROP_PAGE L"ProcPropPage"
#define SETTING_PROC_PROP_POSITION L"ProcPropPosition"
#define SETTING_PROC_PROP_SIZE L"ProcPropSize"
#define SETTING_PROC_STAT_PROP_PAGE_GROUP_LIST_VIEW_COLUMNS L"ProcStatPropPageGroupListViewColumns"
#define SETTING_PROC_STAT_PROP_PAGE_GROUP_LIST_VIEW_SORT L"ProcStatPropPageGroupListViewSort"
#define SETTING_PROC_STAT_PROP_PAGE_GROUP_STATES L"ProcStatPropPageGroupStates"
#define SETTING_PROGRAM_INSPECT_EXECUTABLES L"ProgramInspectExecutables"
#define SETTING_PROPAGATE_CPU_USAGE L"PropagateCpuUsage"
#define SETTING_RELEASE_CHANNEL L"ReleaseChannel"
#define SETTING_RUN_AS_ENABLE_AUTO_COMPLETE L"RunAsEnableAutoComplete"
#define SETTING_RUN_AS_PROGRAM L"RunAsProgram"
#define SETTING_RUN_AS_USER_NAME L"RunAsUserName"
#define SETTING_RUN_AS_WINDOW_POSITION L"RunAsWindowPosition"
#define SETTING_RUN_AS_PACKAGE_WINDOW_POSITION L"RunAsPackageWindowPosition"
#define SETTING_RUN_AS_PACKAGE_WINDOW_SIZE L"RunAsPackageWindowSize"
#define SETTING_RUN_FILE_DLG_STATE L"RunFileDlgState"
#define SETTING_SAMPLE_COUNT L"SampleCount"
#define SETTING_SAMPLE_COUNT_AUTOMATIC L"SampleCountAutomatic"
#define SETTING_SCROLL_TO_NEW_PROCESSES L"ScrollToNewProcesses"
#define SETTING_SCROLL_TO_REMOVED_PROCESSES L"ScrollToRemovedProcesses"
#define SETTING_SEARCH_ENGINE L"SearchEngine"
#define SETTING_SEGMENT_HEAP_LIST_VIEW_COLUMNS L"SegmentHeapListViewColumns"
#define SETTING_SEGMENT_HEAP_LIST_VIEW_SORT L"SegmentHeapListViewSort"
#define SETTING_SEGMENT_HEAP_WINDOW_POSITION L"SegmentHeapWindowPosition"
#define SETTING_SEGMENT_HEAP_WINDOW_SIZE L"SegmentHeapWindowSize"
#define SETTING_SEGMENT_LOCKS_LIST_VIEW_COLUMNS L"SegmentLocksListViewColumns"
#define SETTING_SEGMENT_LOCKS_LIST_VIEW_SORT L"SegmentLocksListViewSort"
#define SETTING_SEGMENT_LOCKS_WINDOW_POSITION L"SegmentLocksWindowPosition"
#define SETTING_SEGMENT_LOCKS_WINDOW_SIZE L"SegmentLocksWindowSize"
#define SETTING_SERVICE_WINDOW_POSITION L"ServiceWindowPosition"
#define SETTING_SERVICE_LIST_VIEW_COLUMNS L"ServiceListViewColumns"
#define SETTING_SERVICE_TREE_LIST_COLUMNS L"ServiceTreeListColumns"
#define SETTING_SERVICE_TREE_LIST_SORT L"ServiceTreeListSort"
#define SETTING_SESSION_SHADOW_HOTKEY L"SessionShadowHotkey"
#define SETTING_SHOW_PLUGIN_LOAD_ERRORS L"ShowPluginLoadErrors"
#define SETTING_SHOW_COMMIT_IN_SUMMARY L"ShowCommitInSummary"
#define SETTING_SHOW_CPU_BELOW_001 L"ShowCpuBelow001"
#define SETTING_SHOW_HEX_ID L"ShowHexId"
#define SETTING_SORT_CHILD_PROCESSES L"SortChildProcesses"
#define SETTING_SORT_ROOT_PROCESSES L"SortRootProcesses"
#define SETTING_START_HIDDEN L"StartHidden"
#define SETTING_SYSINFO_SHOW_CPU_SPEED_MHZ L"SysInfoShowCpuSpeedMhz"
#define SETTING_SYSINFO_SHOW_CPU_SPEED_PER_CPU L"SysInfoShowCpuSpeedPerCpu"
#define SETTING_SYSINFO_WINDOW_ALWAYS_ON_TOP L"SysInfoWindowAlwaysOnTop"
#define SETTING_SYSINFO_WINDOW_ONE_GRAPH_PER_CPU L"SysInfoWindowOneGraphPerCpu"
#define SETTING_SYSINFO_WINDOW_POSITION L"SysInfoWindowPosition"
#define SETTING_SYSINFO_WINDOW_SECTION L"SysInfoWindowSection"
#define SETTING_SYSINFO_WINDOW_SIZE L"SysInfoWindowSize"
#define SETTING_SYSINFO_WINDOW_STATE L"SysInfoWindowState"
#define SETTING_TASKMGR_WINDOW_STATE L"TaskmgrWindowState"
#define SETTING_THEME_WINDOW_FOREGROUND_COLOR L"ThemeWindowForegroundColor"
#define SETTING_THEME_WINDOW_BACKGROUND_COLOR L"ThemeWindowBackgroundColor"
#define SETTING_THEME_WINDOW_BACKGROUND2_COLOR L"ThemeWindowBackground2Color"
#define SETTING_THEME_WINDOW_HIGHLIGHT_COLOR L"ThemeWindowHighlightColor"
#define SETTING_THEME_WINDOW_HIGHLIGHT2_COLOR L"ThemeWindowHighlight2Color"
#define SETTING_THEME_WINDOW_TEXT_COLOR L"ThemeWindowTextColor"
#define SETTING_THIN_ROWS L"ThinRows"
#define SETTING_THREAD_TREE_LIST_COLUMNS L"ThreadTreeListColumns"
#define SETTING_THREAD_TREE_LIST_SORT L"ThreadTreeListSort"
#define SETTING_THREAD_TREE_LIST_FLAGS L"ThreadTreeListFlags"
#define SETTING_THREAD_STACK_TREE_LIST_COLUMNS L"ThreadStackTreeListColumns"
#define SETTING_THREAD_STACK_WINDOW_SIZE L"ThreadStackWindowSize"
#define SETTING_TOKEN_WINDOW_POSITION L"TokenWindowPosition"
#define SETTING_TOKEN_WINDOW_SIZE L"TokenWindowSize"
#define SETTING_TOKEN_GROUPS_LIST_VIEW_COLUMNS L"TokenGroupsListViewColumns"
#define SETTING_TOKEN_GROUPS_LIST_VIEW_STATES L"TokenGroupsListViewStates"
#define SETTING_TOKEN_GROUPS_LIST_VIEW_SORT L"TokenGroupsListViewSort"
#define SETTING_TOKEN_PRIVILEGES_LIST_VIEW_COLUMNS L"TokenPrivilegesListViewColumns"
#define SETTING_TREE_LIST_BORDER_ENABLE L"TreeListBorderEnable"
#define SETTING_TREE_LIST_CUSTOM_COLORS_ENABLE L"TreeListCustomColorsEnable"
#define SETTING_TREE_LIST_CUSTOM_COLOR_TEXT L"TreeListCustomColorText"
#define SETTING_TREE_LIST_CUSTOM_COLOR_FOCUS L"TreeListCustomColorFocus"
#define SETTING_TREE_LIST_CUSTOM_COLOR_SELECTION L"TreeListCustomColorSelection"
#define SETTING_TREE_LIST_CUSTOM_ROW_SIZE L"TreeListCustomRowSize"
#define SETTING_TREE_LIST_ENABLE_HEADER_TOTALS L"TreeListEnableHeaderTotals"
#define SETTING_TREE_LIST_ENABLE_DRAG_REORDER L"TreeListEnableDragReorder"
#define SETTING_UPDATE_INTERVAL L"UpdateInterval"
#define SETTING_USER_LIST_TREE_LIST_COLUMNS L"UserListTreeListColumns"
#define SETTING_USER_LIST_WINDOW_POSITION L"UserListWindowPosition"
#define SETTING_USER_LIST_WINDOW_SIZE L"UserListWindowSize"
#define SETTING_WMI_PROVIDER_ENABLE_HIDDEN_MENU L"WmiProviderEnableHiddenMenu"
#define SETTING_WMI_PROVIDER_ENABLE_TOOLTIP_SUPPORT L"WmiProviderEnableTooltipSupport"
#define SETTING_WMI_PROVIDER_TREE_LIST_COLUMNS L"WmiProviderTreeListColumns"
#define SETTING_WMI_PROVIDER_TREE_LIST_SORT L"WmiProviderTreeListSort"
#define SETTING_WMI_PROVIDER_TREE_LIST_FLAGS L"WmiProviderTreeListFlags"
#define SETTING_VDM_HOST_LIST_VIEW_COLUMNS L"VdmHostListViewColumns"
#define SETTING_ZOMBIE_PROCESSES_LIST_VIEW_COLUMNS L"ZombieProcessesListViewColumns"
#define SETTING_ZOMBIE_PROCESSES_WINDOW_POSITION L"ZombieProcessesWindowPosition"
#define SETTING_ZOMBIE_PROCESSES_WINDOW_SIZE L"ZombieProcessesWindowSize"
#define SETTING_COLOR_NEW L"ColorNew"
#define SETTING_COLOR_REMOVED L"ColorRemoved"
#define SETTING_USE_COLOR_OWN_PROCESSES L"UseColorOwnProcesses"
#define SETTING_COLOR_OWN_PROCESSES L"ColorOwnProcesses"
#define SETTING_USE_COLOR_SYSTEM_PROCESSES L"UseColorSystemProcesses"
#define SETTING_COLOR_SYSTEM_PROCESSES L"ColorSystemProcesses"
#define SETTING_USE_COLOR_SERVICE_PROCESSES L"UseColorServiceProcesses"
#define SETTING_COLOR_SERVICE_PROCESSES L"ColorServiceProcesses"
#define SETTING_USE_COLOR_BACKGROUND_PROCESSES L"UseColorBackgroundProcesses"
#define SETTING_COLOR_BACKGROUND_PROCESSES L"ColorBackgroundProcesses"
#define SETTING_USE_COLOR_JOB_PROCESSES L"UseColorJobProcesses"
#define SETTING_COLOR_JOB_PROCESSES L"ColorJobProcesses"
#define SETTING_USE_COLOR_WOW64_PROCESSES L"UseColorWow64Processes"
#define SETTING_COLOR_WOW64_PROCESSES L"ColorWow64Processes"
#define SETTING_USE_COLOR_DEBUGGED_PROCESSES L"UseColorDebuggedProcesses"
#define SETTING_COLOR_DEBUGGED_PROCESSES L"ColorDebuggedProcesses"
#define SETTING_USE_COLOR_ELEVATED_PROCESSES L"UseColorElevatedProcesses"
#define SETTING_COLOR_ELEVATED_PROCESSES L"ColorElevatedProcesses"
#define SETTING_USE_COLOR_HANDLE_FILTERED L"UseColorHandleFiltered"
#define SETTING_COLOR_HANDLE_FILTERED L"ColorHandleFiltered"
#define SETTING_USE_COLOR_IMMERSIVE_PROCESSES L"UseColorImmersiveProcesses"
#define SETTING_COLOR_IMMERSIVE_PROCESSES L"ColorImmersiveProcesses"
#define SETTING_USE_COLOR_UI_ACCESS_PROCESSES L"UseColorUIAccessProcesses"
#define SETTING_COLOR_UI_ACCESS_PROCESSES L"ColorUIAccessProcesses"
#define SETTING_USE_COLOR_PICO_PROCESSES L"UseColorPicoProcesses"
#define SETTING_COLOR_PICO_PROCESSES L"ColorPicoProcesses"
#define SETTING_USE_COLOR_SUSPENDED L"UseColorSuspended"
#define SETTING_COLOR_SUSPENDED L"ColorSuspended"
#define SETTING_USE_COLOR_DOT_NET L"UseColorDotNet"
#define SETTING_COLOR_DOT_NET L"ColorDotNet"
#define SETTING_USE_COLOR_PACKED L"UseColorPacked"
#define SETTING_COLOR_PACKED L"ColorPacked"
#define SETTING_USE_COLOR_LOW_IMAGE_COHERENCY L"UseColorLowImageCoherency"
#define SETTING_COLOR_LOW_IMAGE_COHERENCY L"ColorLowImageCoherency"
#define SETTING_USE_COLOR_PARTIALLY_SUSPENDED L"UseColorPartiallySuspended"
#define SETTING_COLOR_PARTIALLY_SUSPENDED L"ColorPartiallySuspended"
#define SETTING_USE_COLOR_GUI_THREADS L"UseColorGuiThreads"
#define SETTING_COLOR_GUI_THREADS L"ColorGuiThreads"
#define SETTING_USE_COLOR_RELOCATED_MODULES L"UseColorRelocatedModules"
#define SETTING_COLOR_RELOCATED_MODULES L"ColorRelocatedModules"
#define SETTING_USE_COLOR_PROTECTED_HANDLES L"UseColorProtectedHandles"
#define SETTING_COLOR_PROTECTED_HANDLES L"ColorProtectedHandles"
#define SETTING_USE_COLOR_PROTECTED_INHERIT_HANDLES L"UseColorProtectedInheritHandles"
#define SETTING_COLOR_PROTECTED_INHERIT_HANDLES L"ColorProtectedInheritHandles"
#define SETTING_USE_COLOR_PROTECTED_PROCESS L"UseColorProtectedProcess"
#define SETTING_COLOR_PROTECTED_PROCESS L"ColorProtectedProcess"
#define SETTING_USE_COLOR_INHERIT_HANDLES L"UseColorInheritHandles"
#define SETTING_COLOR_INHERIT_HANDLES L"ColorInheritHandles"
#define SETTING_USE_COLOR_EFFICIENCY_MODE L"UseColorEfficiencyMode"
#define SETTING_COLOR_EFFICIENCY_MODE L"ColorEfficiencyMode"
#define SETTING_USE_COLOR_SERVICE_DISABLED L"UseColorServiceDisabled"
#define SETTING_COLOR_SERVICE_DISABLED L"ColorServiceDisabled"
#define SETTING_USE_COLOR_SERVICE_STOP L"UseColorServiceStop"
#define SETTING_COLOR_SERVICE_STOP L"ColorServiceStop"
#define SETTING_USE_COLOR_UNKNOWN L"UseColorUnknown"
#define SETTING_COLOR_UNKNOWN L"ColorUnknown"
#define SETTING_USE_COLOR_SYSTEM_THREAD_STACK L"UseColorSystemThreadStack"
#define SETTING_COLOR_SYSTEM_THREAD_STACK L"ColorSystemThreadStack"
#define SETTING_USE_COLOR_USER_THREAD_STACK L"UseColorUserThreadStack"
#define SETTING_COLOR_USER_THREAD_STACK L"ColorUserThreadStack"
#define SETTING_USE_COLOR_INLINE_THREAD_STACK L"UseColorInlineThreadStack"
#define SETTING_COLOR_INLINE_THREAD_STACK L"ColorInlineThreadStack"
#define SETTING_GRAPH_SHOW_TEXT L"GraphShowText"
#define SETTING_GRAPH_COLOR_MODE L"GraphColorMode"
#define SETTING_COLOR_CPU_KERNEL L"ColorCpuKernel"
#define SETTING_COLOR_CPU_USER L"ColorCpuUser"
#define SETTING_COLOR_IO_READ_OTHER L"ColorIoReadOther"
#define SETTING_COLOR_IO_WRITE L"ColorIoWrite"
#define SETTING_COLOR_PRIVATE L"ColorPrivate"
#define SETTING_COLOR_PHYSICAL L"ColorPhysical"
#define SETTING_COLOR_POWER_USAGE L"ColorPowerUsage"
#define SETTING_COLOR_TEMPERATURE L"ColorTemperature"
#define SETTING_COLOR_FAN_RPM L"ColorFanRpm"
#define SETTING_KSI_ENABLE L"KsiEnable"
#define SETTING_KSI_ENABLE_WARNINGS L"KsiEnableWarnings"
#define SETTING_KSI_SERVICE_NAME L"KsiServiceName"
#define SETTING_KSI_OBJECT_NAME L"KsiObjectName"
#define SETTING_KSI_PORT_NAME L"KsiPortName"
#define SETTING_KSI_ALTITUDE L"KsiAltitude"
#define SETTING_KSI_SYSTEM_PROCESS_NAME L"KsiSystemProcessName"
#define SETTING_KSI_DISABLE_IMAGE_LOAD_PROTECTION L"KsiDisableImageLoadProtection"
#define SETTING_KSI_ENABLE_SPLASH_SCREEN L"KsiEnableSplashScreen"
#define SETTING_KSI_ENABLE_LOAD_NATIVE L"KsiEnableLoadNative"
#define SETTING_KSI_ENABLE_LOAD_FILTER L"KsiEnableLoadFilter"
#define SETTING_KSI_UNLOAD_ON_EXIT L"KsiUnloadOnExit"
#define SETTING_KSI_RANDOMIZED_POOL_TAG L"KsiRandomizedPoolTag"
#define SETTING_KSI_ENABLE_UNLOAD_PROTECTION L"KsiEnableUnloadProtection"
#define SETTING_KSI_DYN_DATA_NO_EMBEDDED L"KsiDynDataNoEmbedded"
#define SETTING_KSI_DISABLE_SYSTEM_PROCESS L"KsiDisableSystemProcess"
#define SETTING_KSI_DISABLE_THREAD_NAMES L"KsiDisableThreadNames"
#define SETTING_KSI_CLIENT_PROCESS_PROTECTION_LEVEL L"KsiClientProcessProtectionLevel"
#define SETTING_KSI_PREVIOUS_TEMPORARY_DRIVER_FILE L"KsiPreviousTemporaryDriverFile"
#define SETTING_KSI_ENABLE_FS_FEATURE_OFFLOAD_READ L"KsiEnableFsFeatureOffloadRead"
#define SETTING_KSI_ENABLE_FS_FEATURE_OFFLOAD_WRITE L"KsiEnableFsFeatureOffloadWrite"
#define SETTING_KSI_ENABLE_FS_FEATURE_QUERY_OPEN L"KsiEnableFsFeatureQueryOpen"
#define SETTING_KSI_ENABLE_FS_FEATURE_BYPASS_IO L"KsiEnableFsFeatureBypassIO"
#define SETTING_KSI_RING_BUFFER_LENGTH L"KsiRingBufferLength"
#define SETTING_ENABLE_PROCESS_MONITOR L"EnableProcessMonitor"
#define SETTING_PROCESS_MONITOR_LOOKBACK L"ProcessMonitorLookback"
#define SETTING_PROCESS_MONITOR_CACHE_LIMIT L"ProcessMonitorCacheLimit"
// end_phapppub
#endif
================================================
FILE: SystemInformer/include/phsvc.h
================================================
/*
* Copyright (c) 2022 Winsider Seminars & Solutions, Inc. All rights reserved.
*
* This file is part of System Informer.
*
* Authors:
*
* wj32 2010-2016
* dmex 2017-2022
*
*/
#ifndef PH_PHSVC_H
#define PH_PHSVC_H
#include
#define PHSVC_SHARED_SECTION_SIZE (512 * 1024)
// svcmain
typedef struct _PHSVC_STOP
{
BOOLEAN Stop;
HANDLE Event1;
HANDLE Event2;
} PHSVC_STOP, *PPHSVC_STOP;
NTSTATUS PhSvcMain(
_In_opt_ PPH_STRING PortName,
_Inout_opt_ PPHSVC_STOP Stop
);
VOID PhSvcStop(
_Inout_ PPHSVC_STOP Stop
);
// svcclient
typedef struct _PHSVC_CLIENT
{
LIST_ENTRY ListEntry;
PH_EVENT ReadyEvent;
CLIENT_ID ClientId;
HANDLE PortHandle;
PVOID ClientViewBase;
PVOID ClientViewLimit;
} PHSVC_CLIENT, *PPHSVC_CLIENT;
PPHSVC_CLIENT PhSvcCreateClient(
_In_opt_ PCLIENT_ID ClientId
);
PPHSVC_CLIENT PhSvcReferenceClientByClientId(
_In_ PCLIENT_ID ClientId
);
PPHSVC_CLIENT PhSvcGetCurrentClient(
VOID
);
BOOLEAN PhSvcAttachClient(
_In_ PPHSVC_CLIENT Client
);
VOID PhSvcDetachClient(
_In_ PPHSVC_CLIENT Client
);
// svcapiport
typedef struct _PHSVC_THREAD_CONTEXT
{
PPHSVC_CLIENT CurrentClient;
PPHSVC_CLIENT OldClient;
} PHSVC_THREAD_CONTEXT, *PPHSVC_THREAD_CONTEXT;
NTSTATUS PhSvcApiPortInitialization(
_In_ PUNICODE_STRING PortName
);
PPHSVC_THREAD_CONTEXT PhSvcGetCurrentThreadContext(
VOID
);
VOID PhSvcHandleConnectionRequest(
_In_ PPORT_MESSAGE PortMessage
);
// svcapi
typedef _Function_class_(PHSVC_API_PROCEDURE)
NTSTATUS NTAPI PHSVC_API_PROCEDURE(
_In_ PPHSVC_CLIENT Client,
_Inout_ PPHSVC_API_PAYLOAD Payload
);
typedef PHSVC_API_PROCEDURE* PPHSVC_API_PROCEDURE;
typedef CONST PPHSVC_API_PROCEDURE PCPHSVC_API_PROCEDURE;
VOID PhSvcDispatchApiCall(
_In_ PPHSVC_CLIENT Client,
_Inout_ PPHSVC_API_PAYLOAD Payload,
_Out_ PHANDLE ReplyPortHandle
);
PVOID PhSvcValidateString(
_In_ PPH_RELATIVE_STRINGREF String,
_In_ ULONG Alignment
);
_Function_class_(PHSVC_SERVER_PROBE_BUFFER)
NTSTATUS PhSvcProbeBuffer(
_In_ PPH_RELATIVE_STRINGREF String,
_In_ ULONG Alignment,
_In_ BOOLEAN AllowNull,
_Out_ PVOID *Pointer
);
_Function_class_(PHSVC_SERVER_CAPTURE_BUFFER)
NTSTATUS PhSvcCaptureBuffer(
_In_ PPH_RELATIVE_STRINGREF String,
_In_ BOOLEAN AllowNull,
_Out_ PVOID *CapturedBuffer
);
NTSTATUS PhSvcCaptureString(
_In_ PPH_RELATIVE_STRINGREF String,
_In_ BOOLEAN AllowNull,
_Out_ PPH_STRING *CapturedString
);
NTSTATUS PhSvcCaptureSid(
_In_ PPH_RELATIVE_STRINGREF String,
_In_ BOOLEAN AllowNull,
_Out_ PSID *CapturedSid
);
NTSTATUS PhSvcCaptureSecurityDescriptor(
_In_ PPH_RELATIVE_STRINGREF String,
_In_ BOOLEAN AllowNull,
_In_ SECURITY_INFORMATION RequiredInformation,
_Out_ PSECURITY_DESCRIPTOR *CapturedSecurityDescriptor
);
NTSTATUS PhSvcApiDefault(
_In_ PPHSVC_CLIENT Client,
_Inout_ PPHSVC_API_PAYLOAD Payload
);
_Function_class_(PHSVC_API_PROCEDURE)
NTSTATUS PhSvcApiPlugin(
_In_ PPHSVC_CLIENT Client,
_Inout_ PPHSVC_API_PAYLOAD Payload
);
NTSTATUS PhSvcApiExecuteRunAsCommand(
_In_ PPHSVC_CLIENT Client,
_Inout_ PPHSVC_API_PAYLOAD Payload
);
NTSTATUS PhSvcApiUnloadDriver(
_In_ PPHSVC_CLIENT Client,
_Inout_ PPHSVC_API_PAYLOAD Payload
);
NTSTATUS PhSvcApiControlProcess(
_In_ PPHSVC_CLIENT Client,
_Inout_ PPHSVC_API_PAYLOAD Payload
);
NTSTATUS PhSvcApiControlService(
_In_ PPHSVC_CLIENT Client,
_Inout_ PPHSVC_API_PAYLOAD Payload
);
NTSTATUS PhSvcApiCreateService(
_In_ PPHSVC_CLIENT Client,
_Inout_ PPHSVC_API_PAYLOAD Payload
);
NTSTATUS PhSvcApiChangeServiceConfig(
_In_ PPHSVC_CLIENT Client,
_Inout_ PPHSVC_API_PAYLOAD Payload
);
NTSTATUS PhSvcApiChangeServiceConfig2(
_In_ PPHSVC_CLIENT Client,
_Inout_ PPHSVC_API_PAYLOAD Payload
);
NTSTATUS PhSvcApiSetTcpEntry(
_In_ PPHSVC_CLIENT Client,
_Inout_ PPHSVC_API_PAYLOAD Payload
);
NTSTATUS PhSvcApiControlThread(
_In_ PPHSVC_CLIENT Client,
_Inout_ PPHSVC_API_PAYLOAD Payload
);
NTSTATUS PhSvcApiAddAccountRight(
_In_ PPHSVC_CLIENT Client,
_Inout_ PPHSVC_API_PAYLOAD Payload
);
NTSTATUS PhSvcApiInvokeRunAsService(
_In_ PPHSVC_CLIENT Client,
_Inout_ PPHSVC_API_PAYLOAD Payload
);
NTSTATUS PhSvcApiIssueMemoryListCommand(
_In_ PPHSVC_CLIENT Client,
_Inout_ PPHSVC_API_PAYLOAD Payload
);
NTSTATUS PhSvcApiPostMessage(
_In_ PPHSVC_CLIENT Client,
_Inout_ PPHSVC_API_PAYLOAD Payload
);
NTSTATUS PhSvcApiSendMessage(
_In_ PPHSVC_CLIENT Client,
_Inout_ PPHSVC_API_PAYLOAD Payload
);
NTSTATUS PhSvcApiCreateProcessIgnoreIfeoDebugger(
_In_ PPHSVC_CLIENT Client,
_Inout_ PPHSVC_API_PAYLOAD Payload
);
NTSTATUS PhSvcApiSetServiceSecurity(
_In_ PPHSVC_CLIENT Client,
_Inout_ PPHSVC_API_PAYLOAD Payload
);
NTSTATUS PhSvcApiWriteMiniDumpProcess(
_In_ PPHSVC_CLIENT Client,
_Inout_ PPHSVC_API_PAYLOAD Payload
);
NTSTATUS PhSvcApiQueryProcessHeapInformation(
_In_ PPHSVC_CLIENT Client,
_Inout_ PPHSVC_API_PAYLOAD Payload
);
NTSTATUS PhSvcApiCreateProcessForKsi(
_In_ PPHSVC_CLIENT Client,
_Inout_ PPHSVC_API_PAYLOAD Payload
);
#endif
================================================
FILE: SystemInformer/include/phsvcapi.h
================================================
/*
* Copyright (c) 2022 Winsider Seminars & Solutions, Inc. All rights reserved.
*
* This file is part of System Informer.
*
* Authors:
*
* wj32 2011-2015
* dmex 2017-2023
*
*/
#ifndef PH_PHSVCAPI_H
#define PH_PHSVCAPI_H
#define PHSVC_PORT_NAME (L"\\BaseNamedObjects\\SiSvcApiPort")
#define PHSVC_WOW64_PORT_NAME (L"\\BaseNamedObjects\\SiSvcWow64ApiPort")
typedef enum _PHSVC_API_NUMBER
{
PhSvcInvalidApiNumber = 0,
PhSvcPluginApiNumber = 1,
PhSvcExecuteRunAsCommandApiNumber = 2,
PhSvcUnloadDriverApiNumber = 3,
PhSvcControlProcessApiNumber = 4,
PhSvcControlServiceApiNumber = 5,
PhSvcCreateServiceApiNumber = 6,
PhSvcChangeServiceConfigApiNumber = 7,
PhSvcChangeServiceConfig2ApiNumber = 8,
PhSvcSetTcpEntryApiNumber = 9,
PhSvcControlThreadApiNumber = 10,
PhSvcAddAccountRightApiNumber = 11,
PhSvcInvokeRunAsServiceApiNumber = 12,
PhSvcIssueMemoryListCommandApiNumber = 13,
PhSvcPostMessageApiNumber = 14,
PhSvcSendMessageApiNumber = 15,
PhSvcCreateProcessIgnoreIfeoDebuggerApiNumber = 16,
PhSvcSetServiceSecurityApiNumber = 17,
PhSvcWriteMiniDumpProcessApiNumber = 18, // WOW64 compatible
PhSvcQueryProcessDebugInformationApiNumber = 19, // WOW64 compatible
PhSvcCreateProcessForKsi = 20,
PhSvcMaximumApiNumber
} PHSVC_API_NUMBER, *PPHSVC_API_NUMBER;
typedef struct _PHSVC_API_CONNECTINFO
{
ULONG ServerProcessId;
} PHSVC_API_CONNECTINFO, *PPHSVC_API_CONNECTINFO;
typedef union _PHSVC_API_PLUGIN
{
struct
{
PH_RELATIVE_STRINGREF ApiId;
ULONG Data[30];
} i;
struct
{
ULONG Data[32];
} o;
} PHSVC_API_PLUGIN, *PPHSVC_API_PLUGIN;
typedef union _PHSVC_API_EXECUTERUNASCOMMAND
{
struct
{
ULONG ProcessId;
PH_RELATIVE_STRINGREF UserName;
PH_RELATIVE_STRINGREF Password;
ULONG LogonType;
ULONG SessionId;
PH_RELATIVE_STRINGREF CurrentDirectory;
PH_RELATIVE_STRINGREF CommandLine;
PH_RELATIVE_STRINGREF FileName;
PH_RELATIVE_STRINGREF DesktopName;
BOOLEAN UseLinkedToken;
PH_RELATIVE_STRINGREF ServiceName;
BOOLEAN CreateSuspendedProcess;
BOOLEAN CreateUIAccessProcess;
HWND WindowHandle;
} i;
} PHSVC_API_EXECUTERUNASCOMMAND, *PPHSVC_API_EXECUTERUNASCOMMAND;
typedef union _PHSVC_API_UNLOADDRIVER
{
struct
{
PVOID BaseAddress;
PH_RELATIVE_STRINGREF Name;
PH_RELATIVE_STRINGREF FileName;
} i;
} PHSVC_API_UNLOADDRIVER, *PPHSVC_API_UNLOADDRIVER;
typedef enum _PHSVC_API_CONTROLPROCESS_COMMAND
{
PhSvcControlProcessTerminate = 1,
PhSvcControlProcessSuspend,
PhSvcControlProcessResume,
PhSvcControlProcessPriority,
PhSvcControlProcessIoPriority
} PHSVC_API_CONTROLPROCESS_COMMAND;
typedef union _PHSVC_API_CONTROLPROCESS
{
struct
{
HANDLE ProcessId;
PHSVC_API_CONTROLPROCESS_COMMAND Command;
ULONG Argument;
} i;
} PHSVC_API_CONTROLPROCESS, *PPHSVC_API_CONTROLPROCESS;
typedef enum _PHSVC_API_CONTROLSERVICE_COMMAND
{
PhSvcControlServiceStart = 1,
PhSvcControlServiceContinue,
PhSvcControlServicePause,
PhSvcControlServiceStop,
PhSvcControlServiceDelete,
PhSvcControlServiceRestart
} PHSVC_API_CONTROLSERVICE_COMMAND;
typedef union _PHSVC_API_CONTROLSERVICE
{
struct
{
PH_RELATIVE_STRINGREF ServiceName;
PHSVC_API_CONTROLSERVICE_COMMAND Command;
} i;
} PHSVC_API_CONTROLSERVICE, *PPHSVC_API_CONTROLSERVICE;
typedef union _PHSVC_API_CREATESERVICE
{
struct
{
// ServiceName is the only required string.
PH_RELATIVE_STRINGREF ServiceName;
PH_RELATIVE_STRINGREF DisplayName;
ULONG ServiceType;
ULONG StartType;
ULONG ErrorControl;
PH_RELATIVE_STRINGREF BinaryPathName;
PH_RELATIVE_STRINGREF LoadOrderGroup;
PH_RELATIVE_STRINGREF Dependencies;
PH_RELATIVE_STRINGREF ServiceStartName;
PH_RELATIVE_STRINGREF Password;
BOOLEAN TagIdSpecified;
} i;
struct
{
ULONG TagId;
} o;
} PHSVC_API_CREATESERVICE, *PPHSVC_API_CREATESERVICE;
typedef union _PHSVC_API_CHANGESERVICECONFIG
{
struct
{
PH_RELATIVE_STRINGREF ServiceName;
ULONG ServiceType;
ULONG StartType;
ULONG ErrorControl;
PH_RELATIVE_STRINGREF BinaryPathName;
PH_RELATIVE_STRINGREF LoadOrderGroup;
PH_RELATIVE_STRINGREF Dependencies;
PH_RELATIVE_STRINGREF ServiceStartName;
PH_RELATIVE_STRINGREF Password;
PH_RELATIVE_STRINGREF DisplayName;
BOOLEAN TagIdSpecified;
} i;
struct
{
ULONG TagId;
} o;
} PHSVC_API_CHANGESERVICECONFIG, *PPHSVC_API_CHANGESERVICECONFIG;
typedef union _PHSVC_API_CHANGESERVICECONFIG2
{
struct
{
PH_RELATIVE_STRINGREF ServiceName;
ULONG InfoLevel;
PH_RELATIVE_STRINGREF Info;
} i;
} PHSVC_API_CHANGESERVICECONFIG2, *PPHSVC_API_CHANGESERVICECONFIG2;
typedef union _PHSVC_API_SETTCPENTRY
{
struct
{
ULONG State;
ULONG LocalAddress;
ULONG LocalPort;
ULONG RemoteAddress;
ULONG RemotePort;
} i;
} PHSVC_API_SETTCPENTRY, *PPHSVC_API_SETTCPENTRY;
typedef enum _PHSVC_API_CONTROLTHREAD_COMMAND
{
PhSvcControlThreadTerminate = 1,
PhSvcControlThreadSuspend,
PhSvcControlThreadResume,
PhSvcControlThreadIoPriority
} PHSVC_API_CONTROLTHREAD_COMMAND;
typedef union _PHSVC_API_CONTROLTHREAD
{
struct
{
HANDLE ThreadId;
PHSVC_API_CONTROLTHREAD_COMMAND Command;
ULONG Argument;
} i;
} PHSVC_API_CONTROLTHREAD, *PPHSVC_API_CONTROLTHREAD;
typedef union _PHSVC_API_ADDACCOUNTRIGHT
{
struct
{
PH_RELATIVE_STRINGREF AccountSid;
PH_RELATIVE_STRINGREF UserRight;
} i;
} PHSVC_API_ADDACCOUNTRIGHT, *PPHSVC_API_ADDACCOUNTRIGHT;
typedef union _PHSVC_API_ISSUEMEMORYLISTCOMMAND
{
struct
{
SYSTEM_MEMORY_LIST_COMMAND Command;
} i;
} PHSVC_API_ISSUEMEMORYLISTCOMMAND, *PPHSVC_API_ISSUEMEMORYLISTCOMMAND;
typedef union _PHSVC_API_POSTMESSAGE
{
struct
{
HWND hWnd;
UINT Msg;
WPARAM wParam;
LPARAM lParam;
} i;
} PHSVC_API_POSTMESSAGE, *PPHSVC_API_POSTMESSAGE;
typedef union _PHSVC_API_CREATEPROCESSIGNOREIFEODEBUGGER
{
struct
{
PH_RELATIVE_STRINGREF FileName;
PH_RELATIVE_STRINGREF CommandLine;
} i;
} PHSVC_API_CREATEPROCESSIGNOREIFEODEBUGGER, *PPHSVC_API_CREATEPROCESSIGNOREIFEODEBUGGER;
typedef union _PHSVC_API_SETSERVICESECURITY
{
struct
{
PH_RELATIVE_STRINGREF ServiceName;
SECURITY_INFORMATION SecurityInformation;
PH_RELATIVE_STRINGREF SecurityDescriptor;
} i;
} PHSVC_API_SETSERVICESECURITY, *PPHSVC_API_SETSERVICESECURITY;
typedef union _PHSVC_API_WRITEMINIDUMPPROCESS
{
struct
{
ULONG LocalProcessHandle;
ULONG ProcessId;
ULONG LocalFileHandle;
ULONG DumpType;
} i;
} PHSVC_API_WRITEMINIDUMPPROCESS, *PPHSVC_API_WRITEMINIDUMPPROCESS;
typedef union _PHSVC_API_PROCESSHEAPINFORMATION
{
struct
{
ULONG ProcessId;
PH_RELATIVE_STRINGREF Data; // out
} i;
struct
{
ULONG DataLength;
} o;
} PHSVC_API_PROCESSHEAPINFORMATION, *PPHSVC_API_PROCESSHEAPINFORMATION;
typedef union _PHSVC_API_CREATEPROCESSFORKSI
{
struct
{
PH_RELATIVE_STRINGREF CommandLine;
ULONG64 MitigationFlags[2];
} i;
} PHSVC_API_CREATEPROCESSFORKSI, *PPHSVC_API_CREATEPROCESSFORKSI;
typedef union _PHSVC_API_PAYLOAD
{
PHSVC_API_CONNECTINFO ConnectInfo;
struct
{
PHSVC_API_NUMBER ApiNumber;
NTSTATUS ReturnStatus;
union
{
PHSVC_API_PLUGIN Plugin;
PHSVC_API_EXECUTERUNASCOMMAND ExecuteRunAsCommand;
PHSVC_API_UNLOADDRIVER UnloadDriver;
PHSVC_API_CONTROLPROCESS ControlProcess;
PHSVC_API_CONTROLSERVICE ControlService;
PHSVC_API_CREATESERVICE CreateService;
PHSVC_API_CHANGESERVICECONFIG ChangeServiceConfig;
PHSVC_API_CHANGESERVICECONFIG2 ChangeServiceConfig2;
PHSVC_API_SETTCPENTRY SetTcpEntry;
PHSVC_API_CONTROLTHREAD ControlThread;
PHSVC_API_ADDACCOUNTRIGHT AddAccountRight;
PHSVC_API_ISSUEMEMORYLISTCOMMAND IssueMemoryListCommand;
PHSVC_API_POSTMESSAGE PostMessage;
PHSVC_API_CREATEPROCESSIGNOREIFEODEBUGGER CreateProcessIgnoreIfeoDebugger;
PHSVC_API_SETSERVICESECURITY SetServiceSecurity;
PHSVC_API_WRITEMINIDUMPPROCESS WriteMiniDumpProcess;
PHSVC_API_PROCESSHEAPINFORMATION QueryProcessHeap;
PHSVC_API_CREATEPROCESSFORKSI CreateProcessForKsi;
} u;
};
} PHSVC_API_PAYLOAD, *PPHSVC_API_PAYLOAD;
typedef struct _PHSVC_API_MSG
{
PORT_MESSAGE h;
PHSVC_API_PAYLOAD p;
} PHSVC_API_MSG, *PPHSVC_API_MSG;
typedef struct _PHSVC_API_MSG64
{
PORT_MESSAGE64 h;
PHSVC_API_PAYLOAD p;
} PHSVC_API_MSG64, *PPHSVC_API_MSG64;
C_ASSERT(FIELD_OFFSET(PHSVC_API_PAYLOAD, u) == 8);
C_ASSERT(sizeof(PHSVC_API_MSG) <= PORT_TOTAL_MAXIMUM_MESSAGE_LENGTH);
C_ASSERT(sizeof(PHSVC_API_MSG64) <= PORT_TOTAL_MAXIMUM_MESSAGE_LENGTH);
#endif
================================================
FILE: SystemInformer/include/phsvccl.h
================================================
/*
* Copyright (c) 2022 Winsider Seminars & Solutions, Inc. All rights reserved.
*
* This file is part of System Informer.
*
* Authors:
*
* wj32 2010-2016
* dmex 2017-2023
*
*/
#ifndef PH_PHSVCCL_H
#define PH_PHSVCCL_H
#include
extern HANDLE PhSvcClServerProcessId;
NTSTATUS PhSvcConnectToServer(
_In_ PUNICODE_STRING PortName,
_In_opt_ SIZE_T PortSectionSize
);
VOID PhSvcDisconnectFromServer(
VOID
);
VOID PhSvcpFreeHeap(
_In_ PVOID Memory
);
_Success_(return != NULL)
PVOID PhSvcpCreateString(
_In_opt_ PCWSTR String,
_In_ SIZE_T Length,
_Out_ PPH_RELATIVE_STRINGREF StringRef
);
NTSTATUS PhSvcCallPlugin(
_In_ PPH_STRINGREF ApiId,
_In_reads_bytes_opt_(InLength) PVOID InBuffer,
_In_ ULONG InLength,
_Out_writes_bytes_opt_(OutLength) PVOID OutBuffer,
_In_ ULONG OutLength
);
NTSTATUS PhSvcCallExecuteRunAsCommand(
_In_ PPH_RUNAS_SERVICE_PARAMETERS Parameters
);
NTSTATUS PhSvcCallUnloadDriver(
_In_opt_ PVOID BaseAddress,
_In_opt_ PCWSTR Name,
_In_opt_ PCWSTR FileName
);
NTSTATUS PhSvcCallControlProcess(
_In_opt_ HANDLE ProcessId,
_In_ PHSVC_API_CONTROLPROCESS_COMMAND Command,
_In_ ULONG Argument
);
NTSTATUS PhSvcCallControlService(
_In_ PCWSTR ServiceName,
_In_ PHSVC_API_CONTROLSERVICE_COMMAND Command
);
NTSTATUS PhSvcCallCreateService(
_In_ PCWSTR ServiceName,
_In_opt_ PCWSTR DisplayName,
_In_ ULONG ServiceType,
_In_ ULONG StartType,
_In_ ULONG ErrorControl,
_In_opt_ PCWSTR BinaryPathName,
_In_opt_ PCWSTR LoadOrderGroup,
_Out_opt_ PULONG TagId,
_In_opt_ PCWSTR Dependencies,
_In_opt_ PCWSTR ServiceStartName,
_In_opt_ PCWSTR Password
);
// begin_phapppub
PHAPPAPI
NTSTATUS PhSvcCallChangeServiceConfig(
_In_ PCWSTR ServiceName,
_In_ ULONG ServiceType,
_In_ ULONG StartType,
_In_ ULONG ErrorControl,
_In_opt_ PCWSTR BinaryPathName,
_In_opt_ PCWSTR LoadOrderGroup,
_Out_opt_ PULONG TagId,
_In_opt_ PCWSTR Dependencies,
_In_opt_ PCWSTR ServiceStartName,
_In_opt_ PCWSTR Password,
_In_opt_ PCWSTR DisplayName
);
PHAPPAPI
NTSTATUS PhSvcCallChangeServiceConfig2(
_In_ PCWSTR ServiceName,
_In_ ULONG InfoLevel,
_In_ PVOID Info
);
// end_phapppub
NTSTATUS PhSvcCallSetTcpEntry(
_In_ PVOID TcpRow
);
NTSTATUS PhSvcCallControlThread(
_In_ HANDLE ThreadId,
_In_ PHSVC_API_CONTROLTHREAD_COMMAND Command,
_In_ ULONG Argument
);
NTSTATUS PhSvcCallAddAccountRight(
_In_ PSID AccountSid,
_In_ PUNICODE_STRING UserRight
);
NTSTATUS PhSvcCallInvokeRunAsService(
_In_ PPH_RUNAS_SERVICE_PARAMETERS Parameters
);
NTSTATUS PhSvcCallIssueMemoryListCommand(
_In_ SYSTEM_MEMORY_LIST_COMMAND Command
);
// begin_phapppub
PHAPPAPI
NTSTATUS PhSvcCallPostMessage(
_In_opt_ HWND hWnd,
_In_ UINT Msg,
_In_ WPARAM wParam,
_In_ LPARAM lParam
);
PHAPPAPI
NTSTATUS PhSvcCallSendMessage(
_In_opt_ HWND hWnd,
_In_ UINT Msg,
_In_ WPARAM wParam,
_In_ LPARAM lParam
);
// end_phapppub
NTSTATUS PhSvcCallCreateProcessIgnoreIfeoDebugger(
_In_ PCWSTR FileName,
_In_opt_ PCWSTR CommandLine
);
NTSTATUS PhSvcCallSetServiceSecurity(
_In_ PCWSTR ServiceName,
_In_ SECURITY_INFORMATION SecurityInformation,
_In_ PSECURITY_DESCRIPTOR SecurityDescriptor
);
NTSTATUS PhSvcCallWriteMiniDumpProcess(
_In_ HANDLE ProcessHandle,
_In_ HANDLE ProcessId,
_In_ HANDLE FileHandle,
_In_ ULONG DumpType
);
NTSTATUS PhSvcCallQueryProcessHeapInformation(
_In_ HANDLE ProcessId,
_Out_ PPH_STRING* HeapInformation
);
NTSTATUS PhSvcCallCreateProcessForKsi(
_In_ PCWSTR CommandLine,
_In_ ULONG64 MitigationFlags[2]
);
#endif
================================================
FILE: SystemInformer/include/phuisup.h
================================================
/*
* Copyright (c) 2022 Winsider Seminars & Solutions, Inc. All rights reserved.
*
* This file is part of System Informer.
*
* Authors:
*
* wj32 2009-2016
*
*/
#ifndef PH_PHUISUP_H
#define PH_PHUISUP_H
// begin_phapppub
// Common state highlighting support
typedef struct _PH_SH_STATE
{
PH_ITEM_STATE State;
HANDLE StateListHandle;
ULONG64 TickCount;
} PH_SH_STATE, *PPH_SH_STATE;
FORCEINLINE VOID PhChangeShStateTn(
_Inout_ PPH_TREENEW_NODE Node,
_Inout_ PPH_SH_STATE ShState,
_Inout_ PPH_POINTER_LIST *StateList,
_In_ PH_ITEM_STATE NewState,
_In_ COLORREF NewTempBackColor,
_In_opt_ HWND TreeNewHandleForUpdate
)
{
if (!*StateList)
*StateList = PhCreatePointerList(4);
if (ShState->State == NormalItemState)
ShState->StateListHandle = PhAddItemPointerList(*StateList, Node);
ShState->TickCount = NtGetTickCount64();
ShState->State = NewState;
Node->UseTempBackColor = TRUE;
Node->TempBackColor = NewTempBackColor;
if (TreeNewHandleForUpdate)
TreeNew_InvalidateNode(TreeNewHandleForUpdate, Node);
}
#define PH_TICK_SH_STATE_TN(NodeType, ShStateFieldName, StateList, RemoveFunction, HighlightingDuration, TreeNewHandleForUpdate, Invalidate, FullyInvalidated, Context) \
do { \
NodeType *node; \
ULONG enumerationKey = 0; \
ULONG64 tickCount; \
BOOLEAN preferFullInvalidate; \
HANDLE stateListHandle; \
BOOLEAN needsFullInvalidate = FALSE; \
\
if (!StateList || StateList->Count == 0) \
break; \
\
tickCount = NtGetTickCount64(); \
preferFullInvalidate = StateList->Count > 8; \
\
while (PhEnumPointerList(StateList, &enumerationKey, &node)) \
{ \
if (PhRoundNumber(tickCount - node->ShStateFieldName.TickCount, 100) < (HighlightingDuration)) \
continue; \
\
stateListHandle = node->ShStateFieldName.StateListHandle; \
\
if (node->ShStateFieldName.State == NewItemState) \
{ \
node->ShStateFieldName.State = NormalItemState; \
((PPH_TREENEW_NODE)node)->UseTempBackColor = FALSE; \
if (Invalidate) \
{ \
if (preferFullInvalidate) \
{ \
needsFullInvalidate = TRUE; \
} \
else \
{ \
if (TreeNewHandleForUpdate) \
TreeNew_InvalidateNode((TreeNewHandleForUpdate), node); \
} \
} \
} \
else if (node->ShStateFieldName.State == RemovingItemState) \
{ \
RemoveFunction(node, Context); \
needsFullInvalidate = TRUE; \
} \
\
PhRemoveItemPointerList(StateList, stateListHandle); \
} \
\
if (TreeNewHandleForUpdate) \
{ \
if (needsFullInvalidate && (FullyInvalidated && !(*(PBOOLEAN)FullyInvalidated))) \
{ \
PhInvalidateRect((TreeNewHandleForUpdate), NULL, FALSE); \
if (FullyInvalidated) \
*((PBOOLEAN)FullyInvalidated) = TRUE; \
} \
} \
} while (0)
// Provider event queues
typedef enum _PH_PROVIDER_EVENT_TYPE
{
ProviderAddedEvent = 1,
ProviderModifiedEvent = 2,
ProviderRemovedEvent = 3
} PH_PROVIDER_EVENT_TYPE;
typedef struct _PH_PROVIDER_EVENT
{
ULONG_PTR TypeAndObject;
ULONG RunId;
} PH_PROVIDER_EVENT, *PPH_PROVIDER_EVENT;
#define PH_PROVIDER_EVENT_TYPE_MASK 0x3
#define PH_PROVIDER_EVENT_OBJECT_MASK (~(ULONG_PTR)0x3)
#define PH_PROVIDER_EVENT_TYPE(Event) ((ULONG)(Event).TypeAndObject & PH_PROVIDER_EVENT_TYPE_MASK)
#define PH_PROVIDER_EVENT_OBJECT(Event) ((PVOID)((Event).TypeAndObject & PH_PROVIDER_EVENT_OBJECT_MASK))
typedef struct _PH_PROVIDER_EVENT_QUEUE
{
PH_ARRAY Array;
PH_QUEUED_LOCK Lock;
} PH_PROVIDER_EVENT_QUEUE, *PPH_PROVIDER_EVENT_QUEUE;
FORCEINLINE VOID PhInitializeProviderEventQueue(
_Out_ PPH_PROVIDER_EVENT_QUEUE EventQueue,
_In_ SIZE_T InitialCapacity
)
{
PhInitializeArray(&EventQueue->Array, sizeof(PH_PROVIDER_EVENT), InitialCapacity);
PhInitializeQueuedLock(&EventQueue->Lock);
}
FORCEINLINE VOID PhDeleteProviderEventQueue(
_Inout_ PPH_PROVIDER_EVENT_QUEUE EventQueue
)
{
PPH_PROVIDER_EVENT events;
SIZE_T i;
events = (PPH_PROVIDER_EVENT)EventQueue->Array.Items;
for (i = 0; i < EventQueue->Array.Count; i++)
{
if (PH_PROVIDER_EVENT_TYPE(events[i]) == ProviderAddedEvent)
PhDereferenceObject(PH_PROVIDER_EVENT_OBJECT(events[i]));
}
PhDeleteArray(&EventQueue->Array);
}
FORCEINLINE VOID PhPushProviderEventQueue(
_Inout_ PPH_PROVIDER_EVENT_QUEUE EventQueue,
_In_ PH_PROVIDER_EVENT_TYPE Type,
_In_opt_ PVOID Object,
_In_ ULONG RunId
)
{
PH_PROVIDER_EVENT event;
assert(!(PtrToUlong(Object) & PH_PROVIDER_EVENT_TYPE_MASK));
event.TypeAndObject = (ULONG_PTR)Object | Type;
event.RunId = RunId;
PhAcquireQueuedLockExclusive(&EventQueue->Lock);
PhAddItemArray(&EventQueue->Array, &event);
PhReleaseQueuedLockExclusive(&EventQueue->Lock);
}
FORCEINLINE PPH_PROVIDER_EVENT PhFlushProviderEventQueue(
_Inout_ PPH_PROVIDER_EVENT_QUEUE EventQueue,
_In_ ULONG UpToRunId,
_Out_ PULONG Count
)
{
PPH_PROVIDER_EVENT availableEvents;
PPH_PROVIDER_EVENT events = NULL;
SIZE_T count;
PhAcquireQueuedLockExclusive(&EventQueue->Lock);
availableEvents = (PPH_PROVIDER_EVENT)EventQueue->Array.Items;
for (count = 0; count < EventQueue->Array.Count; count++)
{
if ((LONG)(UpToRunId - availableEvents[count].RunId) < 0)
break;
}
if (count != 0)
{
events = (PPH_PROVIDER_EVENT)PhAllocateCopy(availableEvents, count * sizeof(PH_PROVIDER_EVENT));
PhRemoveItemsArray(&EventQueue->Array, 0, count);
}
PhReleaseQueuedLockExclusive(&EventQueue->Lock);
*Count = (ULONG)count;
return events;
}
// end_phapppub
#endif
================================================
FILE: SystemInformer/include/procgrp.h
================================================
/*
* Copyright (c) 2022 Winsider Seminars & Solutions, Inc. All rights reserved.
*
* This file is part of System Informer.
*
* Authors:
*
* wj32 2010-2016
* dmex 2017-2022
*
*/
#ifndef PH_PROCGRP_H
#define PH_PROCGRP_H
// begin_phapppub
typedef struct _PH_PROCESS_GROUP
{
PPH_PROCESS_ITEM Representative; // An element of Processes (no extra reference added)
PPH_LIST Processes; // List of PPH_PROCESS_ITEM
HWND WindowHandle; // Window handle of representative
} PH_PROCESS_GROUP, *PPH_PROCESS_GROUP;
// end_phapppub
typedef VOID (NTAPI *PPH_SORT_LIST_FUNCTION)(
_In_ PPH_LIST List,
_In_opt_ PVOID Context
);
#define PH_GROUP_PROCESSES_DONT_GROUP 0x1
#define PH_GROUP_PROCESSES_FILE_PATH 0x2
PPH_LIST PhCreateProcessGroupList(
_In_opt_ PPH_SORT_LIST_FUNCTION SortListFunction, // Sort a list of PPH_PROCESS_NODE
_In_opt_ PVOID Context,
_In_ ULONG MaximumGroups,
_In_ ULONG Flags
);
VOID PhFreeProcessGroupList(
_In_ PPH_LIST List
);
#endif
================================================
FILE: SystemInformer/include/procmtgn.h
================================================
/*
* Copyright (c) 2022 Winsider Seminars & Solutions, Inc. All rights reserved.
*
* This file is part of System Informer.
*
* Authors:
*
* wj32 2016
* dmex 2017-2023
*
*/
#ifndef PH_PROCMITIGATION_H
#define PH_PROCMITIGATION_H
typedef struct _PH_PROCESS_MITIGATION_POLICY_ALL_INFORMATION
{
PVOID Pointers[MaxProcessMitigationPolicy];
PROCESS_MITIGATION_DEP_POLICY DEPPolicy; // ProcessDEPPolicy
PROCESS_MITIGATION_ASLR_POLICY ASLRPolicy; // ProcessASLRPolicy
PROCESS_MITIGATION_DYNAMIC_CODE_POLICY DynamicCodePolicy; // ProcessDynamicCodePolicy
PROCESS_MITIGATION_STRICT_HANDLE_CHECK_POLICY StrictHandleCheckPolicy; // ProcessStrictHandleCheckPolicy
PROCESS_MITIGATION_SYSTEM_CALL_DISABLE_POLICY SystemCallDisablePolicy; // ProcessSystemCallDisablePolicy
// ProcessMitigationOptionsMask
PROCESS_MITIGATION_EXTENSION_POINT_DISABLE_POLICY ExtensionPointDisablePolicy; // ProcessExtensionPointDisablePolicy
PROCESS_MITIGATION_CONTROL_FLOW_GUARD_POLICY ControlFlowGuardPolicy; // ProcessControlFlowGuardPolicy
PROCESS_MITIGATION_BINARY_SIGNATURE_POLICY SignaturePolicy; // ProcessSignaturePolicy
PROCESS_MITIGATION_FONT_DISABLE_POLICY FontDisablePolicy; // ProcessFontDisablePolicy
PROCESS_MITIGATION_IMAGE_LOAD_POLICY ImageLoadPolicy; // ProcessImageLoadPolicy
PROCESS_MITIGATION_SYSTEM_CALL_FILTER_POLICY SystemCallFilterPolicy; // ProcessSystemCallFilterPolicy
PROCESS_MITIGATION_PAYLOAD_RESTRICTION_POLICY PayloadRestrictionPolicy; // ProcessPayloadRestrictionPolicy
PROCESS_MITIGATION_CHILD_PROCESS_POLICY ChildProcessPolicy; // ProcessChildProcessPolicy
PROCESS_MITIGATION_SIDE_CHANNEL_ISOLATION_POLICY SideChannelIsolationPolicy; // ProcessSideChannelIsolationPolicy
PROCESS_MITIGATION_USER_SHADOW_STACK_POLICY UserShadowStackPolicy; // ProcessUserShadowStackPolicy
PROCESS_MITIGATION_REDIRECTION_TRUST_POLICY RedirectionTrustPolicy; // ProcessRedirectionTrustPolicy
PROCESS_MITIGATION_USER_POINTER_AUTH_POLICY UserPointerAuthPolicy; // ProcessUserPointerAuthPolicy
PROCESS_MITIGATION_SEHOP_POLICY SEHOPPolicy; // ProcessSEHOPPolicy
PROCESS_MITIGATION_ACTIVATION_CONTEXT_TRUST_POLICY2 ActivationContextTrustPolicy; // ProcessActivationContextTrustPolicy2
} PH_PROCESS_MITIGATION_POLICY_ALL_INFORMATION, *PPH_PROCESS_MITIGATION_POLICY_ALL_INFORMATION;
NTSTATUS PhGetProcessMitigationPolicyAllInformation(
_In_ HANDLE ProcessHandle,
_Out_ PPH_PROCESS_MITIGATION_POLICY_ALL_INFORMATION Information
);
_Success_(return)
BOOLEAN PhDescribeProcessMitigationPolicy(
_In_ PROCESS_MITIGATION_POLICY Policy,
_In_ PVOID Data,
_Out_opt_ PPH_STRING *ShortDescription,
_Out_opt_ PPH_STRING *LongDescription
);
#endif
================================================
FILE: SystemInformer/include/procprp.h
================================================
/*
* Copyright (c) 2022 Winsider Seminars & Solutions, Inc. All rights reserved.
*
* This file is part of System Informer.
*
* Authors:
*
* wj32 2008-2016
* dmex 2017-2022
*
*/
#ifndef PH_PROCPRP_H
#define PH_PROCPRP_H
typedef struct _PH_PROCESS_WAITPROPCONTEXT
{
SLIST_ENTRY ListEntry;
HWND PropSheetWindowHandle;
HANDLE ProcessWaitHandle;
HANDLE ProcessHandle;
PPH_PROCESS_ITEM ProcessItem;
} PH_PROCESS_WAITPROPCONTEXT, *PPH_PROCESS_WAITPROPCONTEXT;
#define PH_PROCESS_PROPCONTEXT_MAXPAGES 20
typedef struct _PH_PROCESS_PROPCONTEXT
{
PPH_PROCESS_ITEM ProcessItem;
PPH_STRING Title;
PROPSHEETHEADER PropSheetHeader;
HPROPSHEETPAGE* PropSheetPages;
HANDLE SelectThreadId;
PPH_PROCESS_WAITPROPCONTEXT ProcessWaitContext;
BOOLEAN WaitInitialized;
} PH_PROCESS_PROPCONTEXT, *PPH_PROCESS_PROPCONTEXT;
// begin_phapppub
typedef struct _PH_PROCESS_PROPPAGECONTEXT
{
PPH_PROCESS_PROPCONTEXT PropContext;
PVOID Context;
PROPSHEETPAGE PropSheetPage;
BOOLEAN LayoutInitialized;
} PH_PROCESS_PROPPAGECONTEXT, *PPH_PROCESS_PROPPAGECONTEXT;
// end_phapppub
// begin_phapppub
PHAPPAPI
PPH_PROCESS_PROPCONTEXT
NTAPI
PhCreateProcessPropContext(
_In_opt_ HWND ParentWindowHandle,
_In_ PPH_PROCESS_ITEM ProcessItem
);
// end_phapppub
VOID PhRefreshProcessPropContext(
_Inout_ PPH_PROCESS_PROPCONTEXT PropContext
);
// begin_phapppub
PHAPPAPI
VOID
NTAPI
PhSetSelectThreadIdProcessPropContext(
_Inout_ PPH_PROCESS_PROPCONTEXT PropContext,
_In_ HANDLE ThreadId
);
PHAPPAPI
BOOLEAN
NTAPI
PhAddProcessPropPage(
_Inout_ PPH_PROCESS_PROPCONTEXT PropContext,
_In_ _Assume_refs_(1) PPH_PROCESS_PROPPAGECONTEXT PropPageContext
);
PHAPPAPI
BOOLEAN
NTAPI
PhAddProcessPropPage2(
_Inout_ PPH_PROCESS_PROPCONTEXT PropContext,
_In_ HPROPSHEETPAGE PropSheetPageHandle
);
PHAPPAPI
PPH_PROCESS_PROPPAGECONTEXT
NTAPI
PhCreateProcessPropPageContext(
_In_ LPCWSTR Template,
_In_ DLGPROC DlgProc,
_In_opt_ PVOID Context
);
PHAPPAPI
PPH_PROCESS_PROPPAGECONTEXT
NTAPI
PhCreateProcessPropPageContextEx(
_In_opt_ PVOID InstanceHandle,
_In_ LPCWSTR Template,
_In_ DLGPROC DlgProc,
_In_opt_ PVOID Context
);
_Success_(return)
PHAPPAPI
BOOLEAN
NTAPI
PhPropPageDlgProcHeader(
_In_ HWND hwndDlg,
_In_ UINT uMsg,
_In_ LPARAM lParam,
_Out_opt_ LPPROPSHEETPAGE *PropSheetPage,
_Out_opt_ PPH_PROCESS_PROPPAGECONTEXT *PropPageContext,
_Out_opt_ PPH_PROCESS_ITEM *ProcessItem
);
#define PH_PROP_PAGE_TAB_CONTROL_PARENT ((PPH_LAYOUT_ITEM)0x1)
PHAPPAPI
PPH_LAYOUT_ITEM
NTAPI
PhAddPropPageLayoutItem(
_In_ HWND WindowHandle,
_In_ HWND Handle,
_In_ PPH_LAYOUT_ITEM ParentItem,
_In_ ULONG Anchor
);
PHAPPAPI
VOID
NTAPI
PhDoPropPageLayout(
_In_ HWND WindowHandle
);
FORCEINLINE
PPH_LAYOUT_ITEM
PhBeginPropPageLayout(
_In_ HWND hwndDlg,
_In_ PPH_PROCESS_PROPPAGECONTEXT PropPageContext
)
{
if (!PropPageContext->LayoutInitialized)
{
return PhAddPropPageLayoutItem(hwndDlg, hwndDlg,
PH_PROP_PAGE_TAB_CONTROL_PARENT, PH_ANCHOR_ALL);
}
else
{
return NULL;
}
}
FORCEINLINE
VOID
PhEndPropPageLayout(
_In_ HWND hwndDlg,
_In_ PPH_PROCESS_PROPPAGECONTEXT PropPageContext
)
{
PhDoPropPageLayout(hwndDlg);
PropPageContext->LayoutInitialized = TRUE;
}
PHAPPAPI
VOID
NTAPI
PhShowProcessProperties(
_In_ PPH_PROCESS_PROPCONTEXT Context
);
// end_phapppub
#endif
================================================
FILE: SystemInformer/include/procprpp.h
================================================
/*
* Copyright (c) 2022 Winsider Seminars & Solutions, Inc. All rights reserved.
*
* This file is part of System Informer.
*
* Authors:
*
* wj32 2010-2016
* dmex 2017-2023
*
*/
#ifndef PH_PROCPRPP_H
#define PH_PROCPRPP_H
#include
#include
#include
#include
#include
#include
#include
typedef struct _PH_PROCESS_PROPSHEETCONTEXT
{
WNDPROC PropSheetWindowHookProc;
PH_LAYOUT_MANAGER LayoutManager;
PPH_LAYOUT_ITEM TabPageItem;
BOOLEAN LayoutInitialized;
HFONT PropSheetWindowFont;
PH_CALLBACK_REGISTRATION ProcessesUpdatedRegistration;
HWND OptionsButtonWindowHandle;
WNDPROC OldOptionsButtonWndProc;
//HWND PermissionsButtonWindowHandle;
HWND ButtonsLabelWindowHandle;
} PH_PROCESS_PROPSHEETCONTEXT, *PPH_PROCESS_PROPSHEETCONTEXT;
_Function_class_(PH_TYPE_DELETE_PROCEDURE)
VOID NTAPI PhpProcessPropContextDeleteProcedure(
_In_ PVOID Object,
_In_ ULONG Flags
);
INT CALLBACK PhpPropSheetProc(
_In_ HWND hwndDlg,
_In_ UINT uMsg,
_In_ LPARAM lParam
);
PPH_PROCESS_PROPSHEETCONTEXT PhpGetPropSheetContext(
_In_ HWND WindowHandle
);
LRESULT CALLBACK PhpPropSheetWndProc(
_In_ HWND WindowHandle,
_In_ UINT uMsg,
_In_ WPARAM wParam,
_In_ LPARAM lParam
);
_Function_class_(PH_TYPE_DELETE_PROCEDURE)
VOID NTAPI PhpProcessPropPageContextDeleteProcedure(
_In_ PVOID Object,
_In_ ULONG Flags
);
UINT CALLBACK PhpStandardPropPageProc(
_In_ HWND WindowHandle,
_In_ UINT uMsg,
_In_ LPPROPSHEETPAGE ppsp
);
_Function_class_(PH_TYPE_DELETE_PROCEDURE)
VOID NTAPI PhpProcessPropPageWaitContextDeleteProcedure(
_In_ PVOID Object,
_In_ ULONG Flags
);
VOID PhpCreateProcessPropSheetWaitContext(
_In_ PPH_PROCESS_PROPCONTEXT PropContext,
_In_ HWND WindowHandle
);
VOID PhpFlushProcessPropSheetWaitContextData(
VOID
);
_Function_class_(WAIT_CALLBACK_ROUTINE)
VOID NTAPI PhpProcessPropertiesWaitCallback(
_In_ PVOID Context,
_In_ BOOLEAN TimerOrWaitFired
);
#define SET_BUTTON_ICON(Id, Icon) \
SendMessage(GetDlgItem(hwndDlg, (Id)), BM_SETIMAGE, IMAGE_ICON, (LPARAM)(Icon))
INT_PTR CALLBACK PhpProcessGeneralDlgProc(
_In_ HWND hwndDlg,
_In_ UINT uMsg,
_In_ WPARAM wParam,
_In_ LPARAM lParam
);
INT_PTR CALLBACK PhpProcessStatisticsDlgProc(
_In_ HWND hwndDlg,
_In_ UINT uMsg,
_In_ WPARAM wParam,
_In_ LPARAM lParam
);
INT_PTR CALLBACK PhpProcessPerformanceDlgProc(
_In_ HWND hwndDlg,
_In_ UINT uMsg,
_In_ WPARAM wParam,
_In_ LPARAM lParam
);
INT_PTR CALLBACK PhpProcessThreadsDlgProc(
_In_ HWND hwndDlg,
_In_ UINT uMsg,
_In_ WPARAM wParam,
_In_ LPARAM lParam
);
_Function_class_(PH_OPEN_OBJECT)
NTSTATUS NTAPI PhpOpenProcessTokenForPage(
_Out_ PHANDLE Handle,
_In_ ACCESS_MASK DesiredAccess,
_In_opt_ PVOID Context
);
_Function_class_(PH_CLOSE_OBJECT)
NTSTATUS NTAPI PhpCloseProcessTokenForPage(
_In_opt_ HANDLE Handle,
_In_opt_ BOOLEAN Release,
_In_opt_ PVOID Context
);
INT_PTR CALLBACK PhpProcessTokenHookProc(
_In_ HWND hwndDlg,
_In_ UINT uMsg,
_In_ WPARAM wParam,
_In_ LPARAM lParam
);
INT_PTR CALLBACK PhpProcessModulesDlgProc(
_In_ HWND hwndDlg,
_In_ UINT uMsg,
_In_ WPARAM wParam,
_In_ LPARAM lParam
);
INT_PTR CALLBACK PhpProcessMemoryDlgProc(
_In_ HWND hwndDlg,
_In_ UINT uMsg,
_In_ WPARAM wParam,
_In_ LPARAM lParam
);
INT_PTR CALLBACK PhpProcessEnvironmentDlgProc(
_In_ HWND hwndDlg,
_In_ UINT uMsg,
_In_ WPARAM wParam,
_In_ LPARAM lParam
);
INT_PTR CALLBACK PhpProcessHandlesDlgProc(
_In_ HWND hwndDlg,
_In_ UINT uMsg,
_In_ WPARAM wParam,
_In_ LPARAM lParam
);
_Function_class_(PH_OPEN_OBJECT)
NTSTATUS NTAPI PhpOpenProcessJobForPage(
_Out_ PHANDLE Handle,
_In_ ACCESS_MASK DesiredAccess,
_In_ PVOID Context
);
_Function_class_(PH_CLOSE_OBJECT)
NTSTATUS NTAPI PhpCloseProcessJobForPage(
_In_ HANDLE Handle,
_In_ BOOLEAN Release,
_In_opt_ PVOID Context
);
INT_PTR CALLBACK PhpProcessJobHookProc(
_In_ HWND hwndDlg,
_In_ UINT uMsg,
_In_ WPARAM wParam,
_In_ LPARAM lParam
);
INT_PTR CALLBACK PhpProcessServicesDlgProc(
_In_ HWND hwndDlg,
_In_ UINT uMsg,
_In_ WPARAM wParam,
_In_ LPARAM lParam
);
INT_PTR CALLBACK PhpProcessWmiProvidersDlgProc(
_In_ HWND hwndDlg,
_In_ UINT uMsg,
_In_ WPARAM wParam,
_In_ LPARAM lParam
);
#ifdef _M_IX86
INT_PTR CALLBACK PhpProcessVdmHostProcessDlgProc(
_In_ HWND hwndDlg,
_In_ UINT uMsg,
_In_ WPARAM wParam,
_In_ LPARAM lParam
);
#endif
extern PH_STRINGREF PhProcessPropPageLoadingText;
#define WM_PH_THREADS_UPDATED (WM_APP + 200)
#define WM_PH_THREAD_SELECTION_CHANGED (WM_APP + 201)
// begin_phapppub
typedef struct _PH_THREADS_CONTEXT
{
PPH_THREAD_PROVIDER Provider;
PH_CALLBACK_REGISTRATION ProviderRegistration;
PH_CALLBACK_REGISTRATION AddedEventRegistration;
PH_CALLBACK_REGISTRATION ModifiedEventRegistration;
PH_CALLBACK_REGISTRATION RemovedEventRegistration;
PH_CALLBACK_REGISTRATION UpdatedEventRegistration;
PH_CALLBACK_REGISTRATION LoadingStateChangedEventRegistration;
HWND WindowHandle;
// end_phapppub
HWND TreeNewHandle;
HWND StatusHandle;
HWND SearchboxHandle;
HFONT TreeNewFont;
ULONG_PTR SearchMatchHandle;
PPH_TN_FILTER_ENTRY FilterEntry;
union
{
PH_THREAD_LIST_CONTEXT ListContext;
struct
{
HWND Private; // phapppub
HWND TreeNewHandle; // phapppub
} PublicUse;
};
PH_PROVIDER_EVENT_QUEUE EventQueue;
PH_CALLBACK_REGISTRATION SymbolProviderEventRegistration;
// begin_phapppub
} PH_THREADS_CONTEXT, *PPH_THREADS_CONTEXT;
// end_phapppub
#define WM_PH_MODULES_UPDATED (WM_APP + 210)
// begin_phapppub
typedef struct _PH_MODULES_CONTEXT
{
PPH_MODULE_PROVIDER Provider;
PH_PROVIDER_REGISTRATION ProviderRegistration;
PH_CALLBACK_REGISTRATION AddedEventRegistration;
PH_CALLBACK_REGISTRATION ModifiedEventRegistration;
PH_CALLBACK_REGISTRATION RemovedEventRegistration;
PH_CALLBACK_REGISTRATION UpdatedEventRegistration;
PH_CALLBACK_REGISTRATION ChangedEventRegistration;
HWND WindowHandle;
// end_phapppub
HWND SearchboxHandle;
HWND TreeNewHandle;
HFONT TreeNewFont;
union
{
PH_MODULE_LIST_CONTEXT ListContext;
struct
{
HWND Private; // phapppub
HWND TreeNewHandle; // phapppub
} PublicUse;
};
PH_PROVIDER_EVENT_QUEUE EventQueue;
NTSTATUS LastRunStatus;
PPH_STRING ErrorMessage;
ULONG_PTR SearchMatchHandle;
PPH_TN_FILTER_ENTRY FilterEntry;
// begin_phapppub
} PH_MODULES_CONTEXT, *PPH_MODULES_CONTEXT;
// end_phapppub
#define WM_PH_HANDLES_UPDATED (WM_APP + 220)
// begin_phapppub
typedef struct _PH_HANDLES_CONTEXT
{
PPH_HANDLE_PROVIDER Provider;
PH_PROVIDER_REGISTRATION ProviderRegistration;
PH_CALLBACK_REGISTRATION AddedEventRegistration;
PH_CALLBACK_REGISTRATION ModifiedEventRegistration;
PH_CALLBACK_REGISTRATION RemovedEventRegistration;
PH_CALLBACK_REGISTRATION UpdatedEventRegistration;
PH_CALLBACK_REGISTRATION ChangedEventRegistration;
HWND WindowHandle;
// end_phapppub
HWND TreeNewHandle;
HWND SearchWindowHandle;
HFONT TreeNewFont;
union
{
PH_HANDLE_LIST_CONTEXT ListContext;
struct
{
HWND Private; // phapppub
HWND TreeNewHandle; // phapppub
} PublicUse;
};
PH_PROVIDER_EVENT_QUEUE EventQueue;
BOOLEAN SelectedHandleProtected;
BOOLEAN SelectedHandleInherit;
NTSTATUS LastRunStatus;
PPH_STRING ErrorMessage;
ULONG_PTR SearchMatchHandle;
PPH_TN_FILTER_ENTRY FilterEntry;
// begin_phapppub
} PH_HANDLES_CONTEXT, *PPH_HANDLES_CONTEXT;
// end_phapppub
// begin_phapppub
typedef struct _PH_MEMORY_CONTEXT
{
HANDLE ProcessId;
HWND WindowHandle;
// end_phapppub
HWND TreeNewHandle;
HWND SearchboxHandle;
HFONT TreeNewFont;
union
{
PH_MEMORY_LIST_CONTEXT ListContext;
struct
{
HWND Private; // phapppub
HWND TreeNewHandle; // phapppub
} PublicUse;
};
PH_MEMORY_ITEM_LIST MemoryItemList;
BOOLEAN MemoryItemListValid;
NTSTATUS LastRunStatus;
PPH_STRING ErrorMessage;
ULONG_PTR SearchMatchHandle;
PPH_TN_FILTER_ENTRY AllocationFilterEntry;
PPH_TN_FILTER_ENTRY FilterEntry;
// begin_phapppub
} PH_MEMORY_CONTEXT, *PPH_MEMORY_CONTEXT;
// end_phapppub
#define WM_PH_STATISTICS_UPDATE (WM_APP + 231)
typedef struct _PH_STATISTICS_CONTEXT
{
PH_CALLBACK_REGISTRATION ProcessesUpdatedRegistration;
HWND WindowHandle;
HWND ListViewHandle;
HFONT TreeNewFont;
PPH_LISTVIEW_CONTEXT ListViewContext;
BOOLEAN Enabled;
HANDLE ProcessHandle;
PPH_PROCESS_ITEM ProcessItem;
BOOLEAN GotCycles;
BOOLEAN GotCounters;
ULONG PagePriority;
IO_PRIORITY_HINT IoPriority;
ULONG PeakHandleCount;
ULONG HangCount;
ULONG GhostCount;
ULONGLONG RunningTime;
ULONGLONG SuspendedTime;
ULONGLONG NetworkTxRxBytes;
PH_UINT64_DELTA KeyboardDelta;
PH_UINT64_DELTA MouseDelta;
//PPH_STRING PrivateWs;
//PPH_STRING ShareableWs;
//PPH_STRING SharedWs;
PPH_STRING PeakHandles;
PPH_STRING GdiHandles;
PPH_STRING UserHandles;
PPH_STRING PeakGdiHandles;
PPH_STRING PeakUserHandles;
FLOAT CpuUsage; FLOAT CpuUsageMin; FLOAT CpuUsageMax; FLOAT CpuUsageDiff;
FLOAT CpuUsageUser; FLOAT CpuUsageUserMin; FLOAT CpuUsageUserMax; FLOAT CpuUsageUserDiff;
FLOAT CpuUsageKernel; FLOAT CpuUsageKernelMin; FLOAT CpuUsageKernelMax; FLOAT CpuUsageKernelDiff;
FLOAT CpuUsageAverage; FLOAT CpuUsageAverageMin; FLOAT CpuUsageAverageMax; FLOAT CpuUsageAverageDiff;
FLOAT CpuUsageRelative; FLOAT CpuUsageRelativeMin; FLOAT CpuUsageRelativeMax; FLOAT CpuUsageRelativeDiff;
ULONG64 CycleTime; ULONG64 CycleTimeMin; ULONG64 CycleTimeMax; ULONG64 CycleTimeDiff;
ULONG64 CycleTimeDelta; ULONG64 CycleTimeDeltaMin; ULONG64 CycleTimeDeltaMax; ULONG64 CycleTimeDeltaDiff;
ULONG64 ContextSwitches; ULONG64 ContextSwitchesMin; ULONG64 ContextSwitchesMax; ULONG64 ContextSwitchesDiff;
ULONG64 ContextSwitchesDelta; ULONG64 ContextSwitchesDeltaMin; ULONG64 ContextSwitchesDeltaMax; ULONG64 ContextSwitchesDeltaDiff;
ULONG64 KernelTime; ULONG64 KernelTimeMin; ULONG64 KernelTimeMax; ULONG64 KernelTimeDiff;
ULONG64 KernelTimeDelta; ULONG64 KernelTimeDeltaMin; ULONG64 KernelTimeDeltaMax; ULONG64 KernelTimeDeltaDiff;
ULONG64 UserTime; ULONG64 UserTimeMin; ULONG64 UserTimeMax; ULONG64 UserTimeDiff;
ULONG64 UserTimeDelta; ULONG64 UserTimeDeltaMin; ULONG64 UserTimeDeltaMax; ULONG64 UserTimeDeltaDiff;
KPRIORITY BasePriority; KPRIORITY BasePriorityMin; KPRIORITY BasePriorityMax; KPRIORITY BasePriorityDiff;
ULONG_PTR PagefileUsage; ULONG_PTR PagefileUsageMin; ULONG_PTR PagefileUsageMax; ULONG_PTR PagefileUsageDiff;
ULONG_PTR PagefileDelta; ULONG_PTR PagefileDeltaMin; ULONG_PTR PagefileDeltaMax; ULONG_PTR PagefileDeltaDiff;
ULONG64 PeakPagefileUsage; ULONG64 PeakPagefileUsageMin; ULONG64 PeakPagefileUsageMax; ULONG64 PeakPagefileUsageDiff;
ULONG64 VirtualSize; ULONG64 VirtualSizeMin; ULONG64 VirtualSizeMax; ULONG64 VirtualSizeDiff;
ULONG64 PeakVirtualSize; ULONG64 PeakVirtualSizeMin; ULONG64 PeakVirtualSizeMax; ULONG64 PeakVirtualSizeDiff;
ULONG64 PageFaultCount; ULONG64 PageFaultCountMin; ULONG64 PageFaultCountMax; ULONG64 PageFaultCountDiff;
ULONG64 PageFaultsDelta; ULONG64 PageFaultsDeltaMin; ULONG64 PageFaultsDeltaMax; ULONG64 PageFaultsDeltaDiff;
ULONG64 HardFaultCount; ULONG64 HardFaultCountMin; ULONG64 HardFaultCountMax; ULONG64 HardFaultCountDiff;
ULONG64 HardFaultsDelta; ULONG64 HardFaultsDeltaMin; ULONG64 HardFaultsDeltaMax; ULONG64 HardFaultsDeltaDiff;
ULONG64 WorkingSetSize; ULONG64 WorkingSetSizeMin; ULONG64 WorkingSetSizeMax; ULONG64 WorkingSetSizeDiff;
ULONG64 PeakWorkingSetSize; ULONG64 PeakWorkingSetSizeMin; ULONG64 PeakWorkingSetSizeMax; ULONG64 PeakWorkingSetSizeDiff;
ULONG64 QuotaPagedPoolUsage; ULONG64 QuotaPagedPoolUsageMin; ULONG64 QuotaPagedPoolUsageMax; ULONG64 QuotaPagedPoolUsageDiff;
ULONG64 QuotaPeakPagedPoolUsage; ULONG64 QuotaPeakPagedPoolUsageMin; ULONG64 QuotaPeakPagedPoolUsageMax; ULONG64 QuotaPeakPagedPoolUsageDiff;
ULONG64 QuotaNonPagedPoolUsage; ULONG64 QuotaNonPagedPoolUsageMin; ULONG64 QuotaNonPagedPoolUsageMax; ULONG64 QuotaNonPagedPoolUsageDiff;
ULONG64 QuotaPeakNonPagedPoolUsage; ULONG64 QuotaPeakNonPagedPoolUsageMin; ULONG64 QuotaPeakNonPagedPoolUsageMax; ULONG64 QuotaPeakNonPagedPoolUsageDiff;
ULONG64 NumberOfPrivatePages; ULONG64 NumberOfPrivatePagesMin; ULONG64 NumberOfPrivatePagesMax; ULONG64 NumberOfPrivatePagesDiff;
ULONG64 NumberOfShareablePages; ULONG64 NumberOfShareablePagesMin; ULONG64 NumberOfShareablePagesMax; ULONG64 NumberOfShareablePagesDiff;
ULONG64 NumberOfSharedPages; ULONG64 NumberOfSharedPagesMin; ULONG64 NumberOfSharedPagesMax; ULONG64 NumberOfSharedPagesDiff;
ULONG64 SharedCommitUsage; ULONG64 SharedCommitUsageMin; ULONG64 SharedCommitUsageMax; ULONG64 SharedCommitUsageDiff;
ULONG64 PrivateCommitUsage; ULONG64 PrivateCommitUsageMin; ULONG64 PrivateCommitUsageMax; ULONG64 PrivateCommitUsageDiff;
ULONG64 PeakPrivateCommitUsage; ULONG64 PeakPrivateCommitUsageMin; ULONG64 PeakPrivateCommitUsageMax; ULONG64 PeakPrivateCommitUsageDiff;
ULONG64 ReadOperationCount; ULONG64 ReadOperationCountMin; ULONG64 ReadOperationCountMax; ULONG64 ReadOperationCountDiff;
ULONG64 IoReadCountDelta; ULONG64 IoReadCountDeltaMin; ULONG64 IoReadCountDeltaMax; ULONG64 IoReadCountDeltaDiff;
ULONG64 ReadTransferCount; ULONG64 ReadTransferCountMin; ULONG64 ReadTransferCountMax; ULONG64 ReadTransferCountDiff;
ULONG64 IoReadDelta; ULONG64 IoReadDeltaMin; ULONG64 IoReadDeltaMax; ULONG64 IoReadDeltaDiff;
ULONG64 WriteOperationCount; ULONG64 WriteOperationCountMin; ULONG64 WriteOperationCountMax; ULONG64 WriteOperationCountDiff;
ULONG64 IoWriteCountDelta; ULONG64 IoWriteCountDeltaMin; ULONG64 IoWriteCountDeltaMax; ULONG64 IoWriteCountDeltaDiff;
ULONG64 WriteTransferCount; ULONG64 WriteTransferCountMin; ULONG64 WriteTransferCountMax; ULONG64 WriteTransferCountDiff;
ULONG64 IoWriteDelta; ULONG64 IoWriteDeltaMin; ULONG64 IoWriteDeltaMax; ULONG64 IoWriteDeltaDiff;
ULONG64 OtherOperationCount; ULONG64 OtherOperationCountMin; ULONG64 OtherOperationCountMax; ULONG64 OtherOperationCountDiff;
ULONG64 IoOtherCountDelta; ULONG64 IoOtherCountDeltaMin; ULONG64 IoOtherCountDeltaMax; ULONG64 IoOtherCountDeltaDiff;
ULONG64 OtherTransferCount; ULONG64 OtherTransferCountMin; ULONG64 OtherTransferCountMax; ULONG64 OtherTransferCountDiff;
ULONG64 IoOtherDelta; ULONG64 IoOtherDeltaMin; ULONG64 IoOtherDeltaMax; ULONG64 IoOtherDeltaDiff;
ULONG64 IoTotal; ULONG64 IoTotalMin; ULONG64 IoTotalMax; ULONG64 IoTotalDiff;
ULONG64 IoTotalDelta; ULONG64 IoTotalDeltaMin; ULONG64 IoTotalDeltaMax; ULONG64 IoTotalDeltaDiff;
} PH_STATISTICS_CONTEXT, *PPH_STATISTICS_CONTEXT;
#define WM_PH_PERFORMANCE_UPDATE (WM_APP + 241)
typedef struct _PH_PERFORMANCE_CONTEXT
{
PH_CALLBACK_REGISTRATION ProcessesUpdatedRegistration;
HWND WindowHandle;
BOOLEAN Enabled;
LONG WindowDpi;
PH_GRAPH_STATE CpuGraphState;
PH_GRAPH_STATE PrivateGraphState;
PH_GRAPH_STATE IoGraphState;
HWND CpuGraphHandle;
HWND PrivateGraphHandle;
HWND IoGraphHandle;
HWND CpuGroupBox;
HWND PrivateBytesGroupBox;
HWND IoGroupBox;
} PH_PERFORMANCE_CONTEXT, *PPH_PERFORMANCE_CONTEXT;
typedef struct _PH_ENVIRONMENT_ITEM
{
PPH_STRING Name;
PPH_STRING Value;
} PH_ENVIRONMENT_ITEM, *PPH_ENVIRONMENT_ITEM;
typedef struct _PH_ENVIRONMENT_CONTEXT
{
HWND WindowHandle;
HWND TreeNewHandle;
HWND SearchWindowHandle;
HFONT TreeNewFont;
BOOLEAN EnableStateHighlighting;
union
{
ULONG Flags;
struct
{
ULONG Reserved : 1;
ULONG HideProcessEnvironment : 1;
ULONG HideUserEnvironment : 1;
ULONG HideSystemEnvironment : 1;
ULONG HighlightProcessEnvironment : 1;
ULONG HighlightUserEnvironment : 1;
ULONG HighlightSystemEnvironment : 1;
ULONG HideCmdTypeEnvironment : 1;
ULONG HighlightCmdEnvironment : 1;
ULONG Spare : 23;
};
};
PPH_PROCESS_ITEM ProcessItem;
ULONG_PTR SearchMatchHandle;
PPH_STRING StatusMessage;
PPH_LIST NodeList;
PPH_LIST NodeRootList;
PPH_HASHTABLE NodeHashtable;
PPH_TN_FILTER_ENTRY TreeFilterEntry;
ULONG TreeNewSortColumn;
PH_TN_FILTER_SUPPORT TreeFilterSupport;
PH_SORT_ORDER TreeNewSortOrder;
PH_CM_MANAGER Cm;
PH_ARRAY Items;
} PH_ENVIRONMENT_CONTEXT, *PPH_ENVIRONMENT_CONTEXT;
#endif
================================================
FILE: SystemInformer/include/procprv.h
================================================
/*
* Copyright (c) 2022 Winsider Seminars & Solutions, Inc. All rights reserved.
*
* This file is part of System Informer.
*
* Authors:
*
* wj32 2016
* dmex 2016-2023
*
*/
#ifndef PH_PROCPRV_H
#define PH_PROCPRV_H
#define PH_RECORD_MAX_USAGE
extern PPH_OBJECT_TYPE PhProcessItemType;
extern PPH_LIST PhProcessRecordList;
extern PH_QUEUED_LOCK PhProcessRecordListLock;
extern ULONG PhStatisticsSampleCount;
extern BOOLEAN PhEnablePurgeProcessRecords;
extern BOOLEAN PhEnableCycleCpuUsage;
extern BOOLEAN PhEnableInterruptCpuUsage;
extern BOOLEAN PhEnablePackageIconSupport;
typedef enum _PH_PROCESS_PROVIDER_FLAG
{
PH_PROCESS_PROVIDER_FLAG_WSCOUNTERS = 0x1,
PH_PROCESS_PROVIDER_FLAG_GDIUSERHANDLES = 0x2,
PH_PROCESS_PROVIDER_FLAG_IOPAGEPRIORITY = 0x4,
PH_PROCESS_PROVIDER_FLAG_WINDOW = 0x8,
PH_PROCESS_PROVIDER_FLAG_DEPSTATUS = 0x10,
PH_PROCESS_PROVIDER_FLAG_TOKEN = 0x20,
PH_PROCESS_PROVIDER_FLAG_OSCONTEXT = 0x40,
PH_PROCESS_PROVIDER_FLAG_QUOTALIMITS = 0x80,
PH_PROCESS_PROVIDER_FLAG_IMAGE = 0x100,
PH_PROCESS_PROVIDER_FLAG_APPID = 0x200,
PH_PROCESS_PROVIDER_FLAG_DPIAWARENESS = 0x400,
PH_PROCESS_PROVIDER_FLAG_FILEATTRIBUTES = 0x800,
PH_PROCESS_PROVIDER_FLAG_DESKTOPINFO = 0x1000,
PH_PROCESS_PROVIDER_FLAG_USERNAME = 0x2000,
PH_PROCESS_PROVIDER_FLAG_CRITICAL = 0x4000,
PH_PROCESS_PROVIDER_FLAG_ERRORMODE = 0x8000,
PH_PROCESS_PROVIDER_FLAG_CODEPAGE = 0x10000,
PH_PROCESS_PROVIDER_FLAG_POWERTHROTTLING = 0x20000,
PH_PROCESS_PROVIDER_FLAG_PRIORITYBOOST = 0x40000,
PH_PROCESS_PROVIDER_FLAG_CFGUARD = 0x80000,
PH_PROCESS_PROVIDER_FLAG_CET = 0x100000,
PH_PROCESS_PROVIDER_FLAG_AVERAGE = 0x200000,
} PH_PROCESS_PROVIDER_FLAG;
extern ULONG PhProcessProviderFlagsMask;
extern PVOID PhProcessInformation; // only can be used if running on same thread as process provider
extern ULONG PhProcessInformationSequenceNumber;
extern SYSTEM_PERFORMANCE_INFORMATION PhPerfInformation;
extern PSYSTEM_PROCESSOR_PERFORMANCE_INFORMATION PhCpuInformation;
extern SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION PhCpuTotals;
extern ULONG PhTotalProcesses;
extern ULONG PhTotalThreads;
extern ULONG PhTotalHandles;
extern ULONG PhTotalCpuQueueLength;
extern ULONG64 PhCpuTotalCycleDelta;
extern PSYSTEM_PROCESSOR_IDLE_CYCLE_TIME_INFORMATION PhCpuIdleCycleTime; // cycle time for Idle
extern PSYSTEM_PROCESSOR_CYCLE_TIME_INFORMATION PhCpuSystemCycleTime; // cycle time for DPCs and Interrupts
extern PH_UINT64_DELTA PhCpuIdleCycleDelta;
extern PH_UINT64_DELTA PhCpuSystemCycleDelta;
extern FLOAT PhCpuKernelUsage;
extern FLOAT PhCpuUserUsage;
extern PFLOAT PhCpusKernelUsage;
extern PFLOAT PhCpusUserUsage;
extern PH_UINT64_DELTA PhCpuKernelDelta;
extern PH_UINT64_DELTA PhCpuUserDelta;
extern PH_UINT64_DELTA PhCpuIdleDelta;
extern PPH_UINT64_DELTA PhCpusKernelDelta;
extern PPH_UINT64_DELTA PhCpusUserDelta;
extern PPH_UINT64_DELTA PhCpusIdleDelta;
extern PH_UINT64_DELTA PhIoReadDelta;
extern PH_UINT64_DELTA PhIoWriteDelta;
extern PH_UINT64_DELTA PhIoOtherDelta;
extern BOOLEAN PhProcessStatisticsInitialized;
extern PH_CIRCULAR_BUFFER_FLOAT PhCpuKernelHistory;
extern PH_CIRCULAR_BUFFER_FLOAT PhCpuUserHistory;
//extern PH_CIRCULAR_BUFFER_FLOAT PhCpuOtherHistory;
extern PPH_CIRCULAR_BUFFER_FLOAT PhCpusKernelHistory;
extern PPH_CIRCULAR_BUFFER_FLOAT PhCpusUserHistory;
//extern PPH_CIRCULAR_BUFFER_FLOAT PhCpusOtherHistory;
extern PH_CIRCULAR_BUFFER_ULONG64 PhIoReadHistory;
extern PH_CIRCULAR_BUFFER_ULONG64 PhIoWriteHistory;
extern PH_CIRCULAR_BUFFER_ULONG64 PhIoOtherHistory;
extern PH_CIRCULAR_BUFFER_ULONG PhCommitHistory;
extern PH_CIRCULAR_BUFFER_ULONG PhPhysicalHistory;
extern PH_CIRCULAR_BUFFER_ULONG PhMaxCpuHistory;
extern PH_CIRCULAR_BUFFER_ULONG PhMaxIoHistory;
#ifdef PH_RECORD_MAX_USAGE
extern PH_CIRCULAR_BUFFER_FLOAT PhMaxCpuUsageHistory;
extern PH_CIRCULAR_BUFFER_ULONG64 PhMaxIoReadOtherHistory;
extern PH_CIRCULAR_BUFFER_ULONG64 PhMaxIoWriteHistory;
#endif
// begin_phapppub
// DPCs, Interrupts and System Idle Process are not real.
// Non-"real" processes can never be opened.
#define PH_IS_REAL_PROCESS_ID(ProcessId) ((LONG_PTR)(ProcessId) > 0)
// DPCs and Interrupts are fake, but System Idle Process is not.
#define PH_IS_FAKE_PROCESS_ID(ProcessId) ((LONG_PTR)(ProcessId) < 0)
// The process item has been removed.
#define PH_PROCESS_ITEM_REMOVED 0x1
// end_phapppub
// begin_phapppub
typedef struct _PH_PROCESS_RECORD *PPH_PROCESS_RECORD;
typedef struct _PH_IMAGELIST_ITEM
{
PPH_STRING FileName;
ULONG LargeIconIndex;
ULONG SmallIconIndex;
} PH_IMAGELIST_ITEM, *PPH_IMAGELIST_ITEM;
typedef struct _PH_PROCESS_ITEM
{
PH_HASH_ENTRY HashEntry;
ULONG State;
PPH_PROCESS_RECORD Record;
// Basic
HANDLE ProcessId;
HANDLE ParentProcessId;
PPH_STRING ProcessName;
ULONG SessionId;
LARGE_INTEGER CreateTime;
// Handles
HANDLE QueryHandle;
// Parameters
PPH_STRING FileName;
PPH_STRING CommandLine;
// File
ULONG_PTR SmallIconIndex;
ULONG_PTR LargeIconIndex;
PH_IMAGE_VERSION_INFO VersionInfo;
// Security
PSID Sid;
TOKEN_ELEVATION_TYPE ElevationType;
PH_INTEGRITY_LEVEL IntegrityLevel;
PPH_STRINGREF IntegrityString;
PS_PROTECTION Protection;
// Other
HANDLE ConsoleHostProcessId;
ULONGLONG ProcessStartKey;
ULONGLONG CreateInterruptTime;
ULONGLONG SessionCreateTime;
ULONG ImageChecksum;
ULONG ImageTimeStamp;
// Signature, Packed
ULONG VerifyResult;
PPH_STRING VerifySignerName;
ULONG ImportFunctions;
ULONG ImportModules;
// Flags
union
{
ULONG Flags;
struct
{
ULONG UpdateIsDotNet : 1;
ULONG IsBeingDebugged : 1;
ULONG IsDotNet : 1;
ULONG IsElevated : 1;
ULONG IsInJob : 1;
ULONG IsInSignificantJob : 1;
ULONG IsPacked : 1;
ULONG IsHandleValid : 1;
ULONG IsSuspended : 1;
ULONG IsWow64Process : 1;
ULONG IsImmersive : 1;
ULONG IsPartiallySuspended : 1;
ULONG IsProtectedHandle : 1;
ULONG IsProtectedProcess : 1;
ULONG IsSecureProcess : 1;
ULONG IsSubsystemProcess : 1;
ULONG IsPackagedProcess : 1;
ULONG IsBackgroundProcess : 1;
ULONG IsCrossSessionProcess : 1;
ULONG IsSnapshotProcess : 1;
ULONG IsFrozenProcess : 1;
ULONG IsUIAccessEnabled : 1;
ULONG IsControlFlowGuardEnabled : 1;
ULONG IsCetEnabled : 1;
ULONG IsXfgEnabled : 1;
ULONG IsXfgAuditEnabled : 1;
ULONG IsPowerThrottling : 1;
ULONG IsSystemProcess : 1;
ULONG IsSecureSystem : 1;
ULONG Spare : 3;
};
};
// Misc.
volatile LONG JustProcessed;
PH_EVENT Stage1Event;
PPH_POINTER_LIST ServiceList;
PH_QUEUED_LOCK ServiceListLock;
WCHAR ProcessIdString[PH_INT32_STR_LEN_1];
WCHAR ProcessIdHexString[PH_PTR_STR_LEN_1];
//WCHAR ParentProcessIdString[PH_INT32_STR_LEN_1];
//WCHAR SessionIdString[PH_INT32_STR_LEN_1];
// Dynamic
KPRIORITY BasePriority;
union
{
KAFFINITY AffinityMaskSingle; // Single processor group
PKAFFINITY AffinityMaskGroups; // Multiple processor groups * PhSystemProcessorInformation.NumberOfProcessorGroups
};
ULONG AffinityPopulationCount;
ULONG PriorityClass;
LARGE_INTEGER KernelTime;
LARGE_INTEGER UserTime;
ULONG NumberOfHandles;
ULONG NumberOfThreads;
FLOAT CpuUsage; // Below Windows 7, sum of kernel and user CPU usage; above Windows 7, cycle-based CPU usage.
FLOAT CpuKernelUsage;
FLOAT CpuUserUsage;
FLOAT CpuAverageUsage;
PH_UINT64_DELTA CpuKernelDelta;
PH_UINT64_DELTA CpuUserDelta;
PH_UINT64_DELTA IoReadDelta;
PH_UINT64_DELTA IoWriteDelta;
PH_UINT64_DELTA IoOtherDelta;
PH_UINT64_DELTA IoReadCountDelta;
PH_UINT64_DELTA IoWriteCountDelta;
PH_UINT64_DELTA IoOtherCountDelta;
PH_UINT64_DELTA ContextSwitchesDelta;
PH_UINT32_DELTA PageFaultsDelta;
PH_UINT32_DELTA HardFaultsDelta;
PH_UINT64_DELTA CycleTimeDelta; // since WIN7
VM_COUNTERS_EX VmCounters;
IO_COUNTERS IoCounters;
ULONGLONG WorkingSetPrivateSize; // since VISTA
ULONG PeakNumberOfThreads; // since WIN7
ULONG HardFaultCount; // since WIN7
ULONG TimeSequenceNumber;
PH_CIRCULAR_BUFFER_FLOAT CpuKernelHistory;
PH_CIRCULAR_BUFFER_FLOAT CpuUserHistory;
PH_CIRCULAR_BUFFER_ULONG64 IoReadHistory;
PH_CIRCULAR_BUFFER_ULONG64 IoWriteHistory;
PH_CIRCULAR_BUFFER_ULONG64 IoOtherHistory;
PH_CIRCULAR_BUFFER_SIZE_T PrivateBytesHistory;
//PH_CIRCULAR_BUFFER_SIZE_T WorkingSetHistory;
// New fields
PH_UINTPTR_DELTA PrivateBytesDelta;
PPH_STRING PackageFullName;
PPH_STRING UserName;
PROCESS_DISK_COUNTERS DiskCounters;
PROCESS_NETWORK_COUNTERS NetworkCounters;
ULONGLONG ContextSwitches;
ULONGLONG ProcessSequenceNumber;
PH_KNOWN_PROCESS_TYPE KnownProcessType;
ULONG JobObjectId;
SIZE_T SharedCommitCharge;
PPH_IMAGELIST_ITEM IconEntry;
NTSTATUS ImageCoherencyStatus;
FLOAT ImageCoherency;
ULONG LxssProcessId;
HANDLE FreezeHandle;
PPEB PebBaseAddress;
} PH_PROCESS_ITEM, *PPH_PROCESS_ITEM;
// end_phapppub
// begin_phapppub
// The process itself is dead.
#define PH_PROCESS_RECORD_DEAD 0x1
// An extra reference has been added to the process record for the statistics system.
#define PH_PROCESS_RECORD_STAT_REF 0x2
typedef struct _PH_PROCESS_RECORD
{
LIST_ENTRY ListEntry;
LONG RefCount;
ULONG Flags;
HANDLE ProcessId;
HANDLE ParentProcessId;
ULONG SessionId;
ULONGLONG ProcessSequenceNumber;
LARGE_INTEGER CreateTime;
LARGE_INTEGER ExitTime;
PPH_STRING ProcessName;
PPH_STRING FileName;
PPH_STRING CommandLine;
/*PPH_STRING UserName;*/
} PH_PROCESS_RECORD, *PPH_PROCESS_RECORD;
// end_phapppub
BOOLEAN PhProcessProviderInitialization(
VOID
);
// begin_phapppub
PHAPPAPI
PPH_STRING
NTAPI
PhGetClientIdName(
_In_ PCLIENT_ID ClientId
);
PHAPPAPI
PPH_STRING
NTAPI
PhGetClientIdNameEx(
_In_ PCLIENT_ID ClientId,
_In_opt_ PPH_STRING ProcessName
);
// end_phapppub
PPH_PROCESS_ITEM PhCreateProcessItem(
_In_ HANDLE ProcessId
);
// begin_phapppub
PHAPPAPI
PPH_PROCESS_ITEM
NTAPI
PhReferenceProcessItem(
_In_opt_ HANDLE ProcessId
);
PHAPPAPI
VOID
NTAPI
PhEnumProcessItems(
_Out_opt_ PPH_PROCESS_ITEM **ProcessItems,
_Out_ PULONG NumberOfProcessItems
);
// end_phapppub
typedef enum _VERIFY_RESULT VERIFY_RESULT;
typedef struct _PH_VERIFY_FILE_INFO *PPH_VERIFY_FILE_INFO;
VERIFY_RESULT PhVerifyFileWithAdditionalCatalog(
_In_ PPH_VERIFY_FILE_INFO Information,
_In_opt_ PPH_STRING PackageFullName,
_Out_opt_ PPH_STRING *SignerName
);
VERIFY_RESULT PhVerifyFileCached(
_In_ PPH_STRING FileName,
_In_opt_ PPH_STRING PackageFullName,
_Out_opt_ PPH_STRING *SignerName,
_In_ BOOLEAN NativeFileName,
_In_ BOOLEAN CachedOnly
);
VOID PhFlushVerifyCache(
VOID
);
// begin_phapppub
PHAPPAPI
_Success_(return)
BOOLEAN
NTAPI
PhGetStatisticsTime(
_In_opt_ PPH_PROCESS_ITEM ProcessItem,
_In_ ULONG Index,
_Out_ PLARGE_INTEGER Time
);
PHAPPAPI
PPH_STRING
NTAPI
PhGetStatisticsTimeString(
_In_opt_ PPH_PROCESS_ITEM ProcessItem,
_In_ ULONG Index
);
// end_phapppub
VOID PhFlushProcessQueryData(
VOID
);
_Function_class_(PH_PROVIDER_FUNCTION)
VOID PhProcessProviderUpdate(
_In_ PVOID Object
);
// begin_phapppub
PHAPPAPI
VOID
NTAPI
PhReferenceProcessRecord(
_In_ PPH_PROCESS_RECORD ProcessRecord
);
PHAPPAPI
BOOLEAN
NTAPI
PhReferenceProcessRecordSafe(
_In_ PPH_PROCESS_RECORD ProcessRecord
);
PHAPPAPI
VOID
NTAPI
PhReferenceProcessRecordForStatistics(
_In_ PPH_PROCESS_RECORD ProcessRecord
);
PHAPPAPI
VOID
NTAPI
PhDereferenceProcessRecord(
_In_ PPH_PROCESS_RECORD ProcessRecord
);
PHAPPAPI
PPH_PROCESS_RECORD
NTAPI
PhFindProcessRecord(
_In_opt_ HANDLE ProcessId,
_In_ PLARGE_INTEGER Time
);
// end_phapppub
VOID PhPurgeProcessRecords(
VOID
);
// begin_phapppub
PHAPPAPI
PPH_PROCESS_ITEM
NTAPI
PhReferenceProcessItemForParent(
_In_ PPH_PROCESS_ITEM ProcessItem
);
PHAPPAPI
PPH_PROCESS_ITEM
NTAPI
PhReferenceProcessItemForRecord(
_In_ PPH_PROCESS_RECORD Record
);
// end_phapppub
extern PPH_OBJECT_TYPE PhImageListItemType;
extern HIMAGELIST PhProcessLargeImageList;
extern HIMAGELIST PhProcessSmallImageList;
// begin_phapppub
PHAPPAPI
VOID
NTAPI
PhProcessImageListInitialization(
_In_ HWND WindowHandle,
_In_ LONG WindowDpi
);
PHAPPAPI
PPH_IMAGELIST_ITEM
NTAPI
PhImageListExtractIcon(
_In_ PPH_STRING FileName,
_In_ BOOLEAN NativeFileName,
_In_opt_ HANDLE ProcessId,
_In_opt_ PPH_STRING PackageFullName,
_In_ LONG SystemDpi
);
PHAPPAPI
VOID
NTAPI
PhImageListFlushCache(
VOID
);
PHAPPAPI
HICON
NTAPI
PhGetImageListIcon(
_In_ ULONG_PTR Index,
_In_ BOOLEAN Large
);
PHAPPAPI
HIMAGELIST
NTAPI
PhGetProcessSmallImageList(
VOID
);
// Note: Can only be called from same thread as process provider. (dmex)
_Success_(return)
PHAPPAPI
BOOLEAN
NTAPI
PhDuplicateProcessInformation(
_Outptr_ PPVOID ProcessInformation
);
// end_phapppub
PPH_PROCESS_ITEM
NTAPI
PhCreateProcessItemFromHandle(
_In_ HANDLE ProcessId,
_In_ HANDLE ProcessHandle,
_In_ BOOLEAN TerminatedProcess
);
#endif
================================================
FILE: SystemInformer/include/proctree.h
================================================
/*
* Copyright (c) 2022 Winsider Seminars & Solutions, Inc. All rights reserved.
*
* This file is part of System Informer.
*
* Authors:
*
* wj32 2009-2016
* dmex 2017-2023
*
*/
#ifndef PH_PROCTREE_H
#define PH_PROCTREE_H
#include
// Columns
// Default columns should go first
#define PHPRTLC_NAME 0
#define PHPRTLC_PID 1
#define PHPRTLC_CPU 2
#define PHPRTLC_IOTOTALRATE 3
#define PHPRTLC_PRIVATEBYTES 4
#define PHPRTLC_USERNAME 5
#define PHPRTLC_DESCRIPTION 6
#define PHPRTLC_COMPANYNAME 7
#define PHPRTLC_VERSION 8
#define PHPRTLC_FILENAME 9
#define PHPRTLC_COMMANDLINE 10
#define PHPRTLC_PEAKPRIVATEBYTES 11
#define PHPRTLC_WORKINGSET 12
#define PHPRTLC_PEAKWORKINGSET 13
#define PHPRTLC_PRIVATEWS 14
#define PHPRTLC_SHAREDWS 15
#define PHPRTLC_SHAREABLEWS 16
#define PHPRTLC_VIRTUALSIZE 17
#define PHPRTLC_PEAKVIRTUALSIZE 18
#define PHPRTLC_PAGEFAULTS 19
#define PHPRTLC_SESSIONID 20
#define PHPRTLC_PRIORITYCLASS 21
#define PHPRTLC_BASEPRIORITY 22
#define PHPRTLC_THREADS 23
#define PHPRTLC_HANDLES 24
#define PHPRTLC_GDIHANDLES 25
#define PHPRTLC_USERHANDLES 26
#define PHPRTLC_IORORATE 27
#define PHPRTLC_IOWRATE 28
#define PHPRTLC_INTEGRITY 29
#define PHPRTLC_IOPRIORITY 30
#define PHPRTLC_PAGEPRIORITY 31
#define PHPRTLC_STARTTIME 32
#define PHPRTLC_TOTALCPUTIME 33
#define PHPRTLC_KERNELCPUTIME 34
#define PHPRTLC_USERCPUTIME 35
#define PHPRTLC_VERIFICATIONSTATUS 36
#define PHPRTLC_VERIFIEDSIGNER 37
#define PHPRTLC_ASLR 38
#define PHPRTLC_RELATIVESTARTTIME 39
#define PHPRTLC_BITS 40
#define PHPRTLC_ELEVATION 41
#define PHPRTLC_WINDOWTITLE 42
#define PHPRTLC_WINDOWSTATUS 43
#define PHPRTLC_CYCLES 44
#define PHPRTLC_CYCLESDELTA 45
#define PHPRTLC_CPUHISTORY 46
#define PHPRTLC_PRIVATEBYTESHISTORY 47
#define PHPRTLC_IOHISTORY 48
#define PHPRTLC_DEP 49
#define PHPRTLC_VIRTUALIZED 50
#define PHPRTLC_CONTEXTSWITCHES 51
#define PHPRTLC_CONTEXTSWITCHESDELTA 52
#define PHPRTLC_PAGEFAULTSDELTA 53
#define PHPRTLC_IOREADS 54
#define PHPRTLC_IOWRITES 55
#define PHPRTLC_IOOTHER 56
#define PHPRTLC_IOREADBYTES 57
#define PHPRTLC_IOWRITEBYTES 58
#define PHPRTLC_IOOTHERBYTES 59
#define PHPRTLC_IOREADSDELTA 60
#define PHPRTLC_IOWRITESDELTA 61
#define PHPRTLC_IOOTHERDELTA 62
#define PHPRTLC_OSCONTEXT 63
#define PHPRTLC_PAGEDPOOL 64
#define PHPRTLC_PEAKPAGEDPOOL 65
#define PHPRTLC_NONPAGEDPOOL 66
#define PHPRTLC_PEAKNONPAGEDPOOL 67
#define PHPRTLC_MINIMUMWORKINGSET 68
#define PHPRTLC_MAXIMUMWORKINGSET 69
#define PHPRTLC_PRIVATEBYTESDELTA 70
#define PHPRTLC_SUBSYSTEM 71
#define PHPRTLC_PACKAGENAME 72
#define PHPRTLC_APPID 73
#define PHPRTLC_DPIAWARENESS 74
#define PHPRTLC_CFGUARD 75
#define PHPRTLC_TIMESTAMP 76
#define PHPRTLC_FILEMODIFIEDTIME 77
#define PHPRTLC_FILESIZE 78
#define PHPRTLC_SUBPROCESSCOUNT 79
#define PHPRTLC_JOBOBJECTID 80
#define PHPRTLC_PROTECTION 81
#define PHPRTLC_DESKTOP 82
#define PHPRTLC_CRITICAL 83
#define PHPRTLC_PIDHEX 84
#define PHPRTLC_CPUCORECYCLES 85
#define PHPRTLC_CET 86
#define PHPRTLC_IMAGE_COHERENCY 87
#define PHPRTLC_ERRORMODE 88
#define PHPRTLC_CODEPAGE 89
#define PHPRTLC_TIMELINE 90
#define PHPRTLC_POWERTHROTTLING 91
#define PHPRTLC_ARCHITECTURE 92
#define PHPRTLC_PARENTPID 93
#define PHPRTLC_PARENTCONSOLEPID 94
#define PHPRTLC_COMMITSIZE 95
#define PHPRTLC_PRIORITYBOOST 96
#define PHPRTLC_CPUAVERAGE 97
#define PHPRTLC_CPUKERNEL 98
#define PHPRTLC_CPUUSER 99
#define PHPRTLC_GRANTEDACCESS 100
#define PHPRTLC_TLSBITMAPDELTA 101
#define PHPRTLC_REFERENCEDELTA 102
#define PHPRTLC_LXSSPID 103
#define PHPRTLC_START_KEY 104
#define PHPRTLC_MITIGATION_POLICIES 105
#define PHPRTLC_SERVICES 106
#define PHPRTLC_MAXIMUM 107
#define PHPRTLC_IOGROUP_COUNT 9
#define PHPN_WSCOUNTERS 0x1
#define PHPN_GDIHANDLES 0x2
#define PHPN_IOPAGEPRIORITY 0x4
#define PHPN_WINDOW 0x8
#define PHPN_DEPSTATUS 0x10
#define PHPN_TOKEN 0x20
#define PHPN_OSCONTEXT 0x40
#define PHPN_QUOTALIMITS 0x80
#define PHPN_IMAGE 0x100
#define PHPN_APPID 0x200
#define PHPN_DPIAWARENESS 0x400
#define PHPN_FILEATTRIBUTES 0x800
#define PHPN_DESKTOPINFO 0x1000
#define PHPN_USERNAME 0x2000
#define PHPN_CRITICAL 0x4000
#define PHPN_ERRORMODE 0x8000
#define PHPN_CODEPAGE 0x10000
#define PHPN_POWERTHROTTLING 0x20000
#define PHPN_MITIGATIONPOLICIES 0x40000
#define PHPN_PRIORITYBOOST 0x80000
#define PHPN_GRANTEDACCESS 0x100000
#define PHPN_TLSBITMAPDELTA 0x200000
#define PHPN_REFERENCEDELTA 0x400000
#define PHPN_STARTKEY 0x800000
#define PHPN_SERVICES 0x1000000
#define PHPN_USERHANDLES 0x2000000
// begin_phapppub
typedef struct _PH_PROCESS_NODE
{
PH_TREENEW_NODE Node;
PH_HASH_ENTRY HashEntry;
PH_SH_STATE ShState;
HANDLE ProcessId;
PPH_PROCESS_ITEM ProcessItem;
struct _PH_PROCESS_NODE *Parent;
PPH_LIST Children;
// end_phapppub
PH_STRINGREF TextCache[PHPRTLC_MAXIMUM];
// If the user has selected certain columns we need extra information that isn't retrieved by
// the process provider.
ULONG ValidMask;
// WS counters
PH_PROCESS_WS_COUNTERS WsCounters;
// GDI, USER handles
ULONG GdiHandles;
ULONG UserHandles;
// I/O, page priority
IO_PRIORITY_HINT IoPriority;
ULONG PagePriority;
// Window
HWND WindowHandle;
PPH_STRING WindowText;
// DEP status
ULONG DepStatus;
BOOLEAN WindowHung;
BOOLEAN VirtualizationAllowed;
BOOLEAN VirtualizationEnabled;
BOOLEAN BreakOnTerminationEnabled;
BOOLEAN PowerThrottling;
BOOLEAN PriorityBoost;
// OS Context
GUID OsContextGuid;
ULONG OsContextVersion;
// Quota limits
SIZE_T MinimumWorkingSetSize;
SIZE_T MaximumWorkingSetSize;
// Image
ULONG ImageTimeDateStamp;
USHORT ImageCharacteristics;
USHORT ImageMachine;
USHORT ImageSubsystem;
USHORT ImageDllCharacteristics;
#ifdef _ARM64_
ULONG ImageCHPEVersion;
#endif
USHORT Architecture;
// App ID
PPH_STRING AppIdText;
// DPI awareness
PH_PROCESS_DPI_AWARENESS DpiAwareness;
// File attributes
LARGE_INTEGER FileLastWriteTime;
LARGE_INTEGER FileEndOfFile;
// Code page
USHORT CodePage;
// TLS bitmap
USHORT TlsBitmapCount;
// Reference count
ULONG ReferenceCount;
// Start key
ULONGLONG ProcessStartKey;
PPH_STRING FileNameWin32;
ULONG ServerSiloId;
PPH_STRING TooltipText;
ULONG64 TooltipTextValidToTickCount;
// Text buffers
WCHAR CpuUsageText[PH_INT32_STR_LEN_1 + 3];
PPH_STRING IoTotalRateText;
PPH_STRING PrivateBytesText;
PPH_STRING PeakPrivateBytesText;
PPH_STRING WorkingSetText;
PPH_STRING PeakWorkingSetText;
PPH_STRING PrivateWsText;
PPH_STRING SharedWsText;
PPH_STRING ShareableWsText;
PPH_STRING VirtualSizeText;
PPH_STRING PeakVirtualSizeText;
PPH_STRING PageFaultsText;
PPH_STRING SessionIdText;
PPH_STRING BasePriorityText;
PPH_STRING ThreadsText;
PPH_STRING HandlesText;
PPH_STRING GdiHandlesText;
PPH_STRING UserHandlesText;
PPH_STRING IoRoRateText;
PPH_STRING IoWRateText;
PPH_STRING StartTimeText;
PPH_STRING TotalCpuTimeText;
PPH_STRING KernelCpuTimeText;
PPH_STRING UserCpuTimeText;
PPH_STRING RelativeStartTimeText;
PPH_STRING WindowTitleText;
PPH_STRING DepStatusText;
PPH_STRING CyclesText;
PPH_STRING CyclesDeltaText;
PPH_STRING ContextSwitchesText;
PPH_STRING ContextSwitchesDeltaText;
PPH_STRING PageFaultsDeltaText;
PPH_STRING IoGroupText[PHPRTLC_IOGROUP_COUNT];
PPH_STRING PagedPoolText;
PPH_STRING PeakPagedPoolText;
PPH_STRING NonPagedPoolText;
PPH_STRING PeakNonPagedPoolText;
PPH_STRING MinimumWorkingSetText;
PPH_STRING MaximumWorkingSetText;
PPH_STRING PrivateBytesDeltaText;
PPH_STRING TimeStampText;
PPH_STRING FileModifiedTimeText;
PPH_STRING FileSizeText;
PPH_STRING SubprocessCountText;
PPH_STRING JobObjectIdText;
PPH_STRING ProtectionText;
PPH_STRING DesktopInfoText;
PPH_STRING CpuCoreUsageText;
PPH_STRING ImageCoherencyText;
PPH_STRING ImageCoherencyStatusText;
PPH_STRING ErrorModeText;
PPH_STRING CodePageText;
PPH_STRING ParentPidText;
PPH_STRING ParentConsolePidText;
PPH_STRING SharedCommitText;
PPH_STRING CpuAverageText;
PPH_STRING CpuKernelText;
PPH_STRING CpuUserText;
PPH_STRING GrantedAccessText;
PPH_STRING TlsBitmapDeltaText;
PPH_STRING ReferenceCountText;
PPH_STRING LxssProcessIdText;
PPH_STRING ProcessStartKeyText;
PPH_STRING MitigationPoliciesText;
PPH_STRING ServicesText;
PPH_STRING ServerSiloText;
// Graph buffers
PH_GRAPH_BUFFERS CpuGraphBuffers;
PH_GRAPH_BUFFERS PrivateGraphBuffers;
PH_GRAPH_BUFFERS IoGraphBuffers;
// begin_phapppub
} PH_PROCESS_NODE, *PPH_PROCESS_NODE;
// end_phapppub
VOID PhProcessTreeListInitialization(
VOID
);
VOID PhInitializeProcessTreeList(
_In_ HWND hwnd
);
VOID PhLoadSettingsProcessTreeList(
VOID
);
VOID PhSaveSettingsProcessTreeList(
VOID
);
VOID PhLoadSettingsProcessTreeListEx(
_In_ PPH_STRING TreeListSettings,
_In_ PPH_STRING TreeSortSettings
);
VOID PhSaveSettingsProcessTreeListEx(
_Out_ PPH_STRING *TreeListSettings,
_Out_ PPH_STRING *TreeSortSettings
);
VOID PhReloadSettingsProcessTreeList(
VOID
);
// begin_phapppub
PHAPPAPI
PPH_TN_FILTER_SUPPORT
NTAPI
PhGetFilterSupportProcessTreeList(
VOID
);
// end_phapppub
PPH_PROCESS_NODE PhAddProcessNode(
_In_ PPH_PROCESS_ITEM ProcessItem,
_In_ ULONG RunId
);
// begin_phapppub
PHAPPAPI
PPH_PROCESS_NODE
NTAPI
PhFindProcessNode(
_In_ HANDLE ProcessId
);
// end_phapppub
VOID PhRemoveProcessNode(
_In_ PPH_PROCESS_NODE ProcessNode
);
// begin_phapppub
PHAPPAPI
VOID
NTAPI
PhUpdateProcessNode(
_In_ PPH_PROCESS_NODE ProcessNode
);
// end_phapppub
VOID PhTickProcessNodes(
VOID
);
// begin_phapppub
PHAPPAPI
PPH_PROCESS_ITEM
NTAPI
PhGetSelectedProcessItem(
VOID
);
_Success_(return)
PHAPPAPI
BOOLEAN
NTAPI
PhGetSelectedProcessItems(
_Out_ PPH_PROCESS_ITEM **Processes,
_Out_ PULONG NumberOfProcesses
);
PHAPPAPI
PPH_PROCESS_NODE
NTAPI
PhGetSelectedProcessNode(
VOID
);
_Success_(return)
PHAPPAPI
BOOLEAN
NTAPI
PhGetSelectedProcessNodes(
_Out_ PPH_PROCESS_NODE** Nodes,
_Out_ PULONG NumberOfNodes
);
PHAPPAPI
VOID
NTAPI
PhGetSelectedAndPropagateProcessItems(
_Out_ PPH_PROCESS_ITEM** Processes,
_Out_ PULONG NumberOfProcesses
);
PHAPPAPI
VOID
NTAPI
PhDeselectAllProcessNodes(
VOID
);
PHAPPAPI
VOID
NTAPI
PhExpandAllProcessNodes(
_In_ BOOLEAN Expand
);
PHAPPAPI
VOID
NTAPI
PhInvalidateAllProcessNodes(
VOID
);
PHAPPAPI
BOOLEAN
NTAPI
PhSelectAndEnsureVisibleProcessNode(
_In_ PPH_PROCESS_NODE ProcessNode
);
// end_phapppub
BOOLEAN PhSelectAndEnsureVisibleProcessNodes(
_In_ PPH_PROCESS_NODE *ProcessNodes,
_In_ ULONG NumberOfProcessNodes
);
PPH_LIST PhGetProcessTreeListLines(
_In_ HWND TreeListHandle,
_In_ ULONG NumberOfNodes,
_In_ PPH_LIST RootNodes,
_In_ ULONG Mode
);
VOID PhCopyProcessTree(
VOID
);
VOID PhWriteProcessTree(
_Inout_ PPH_FILE_STREAM FileStream,
_In_ ULONG Mode
);
// begin_phapppub
PHAPPAPI
PPH_LIST
NTAPI
PhDuplicateProcessNodeList(
VOID
);
// end_phapppub
NTSTATUS PhGetProcessItemFileNameWin32(
_In_ PPH_PROCESS_ITEM ProcessItem,
_Out_ PPH_STRING *FileNameWin32
);
// begin_phapppub
typedef enum _PH_AGGREGATE_TYPE
{
AggregateTypeFloat,
AggregateTypeInt32,
AggregateTypeInt64,
AggregateTypeIntPtr
} PH_AGGREGATE_TYPE;
typedef enum _PH_AGGREGATE_LOCATION
{
AggregateProcessItem,
AggregateProcessNode,
} PH_AGGREGATE_LOCATION;
PHAPPAPI
VOID
NTAPI
PhAggregateProcessFieldIfNeeded(
_In_ PPH_PROCESS_NODE ProcessNode,
_In_ PH_AGGREGATE_TYPE Type,
_In_ PH_AGGREGATE_LOCATION Location,
_In_ PVOID BaseAddress,
_In_ SIZE_T FieldOffset,
_Inout_ PVOID AggregatedValue
);
// end_phapppub
#endif
================================================
FILE: SystemInformer/include/srvlist.h
================================================
/*
* Copyright (c) 2022 Winsider Seminars & Solutions, Inc. All rights reserved.
*
* This file is part of System Informer.
*
* Authors:
*
* wj32 2010-2016
* dmex 2017-2024
*
*/
#ifndef PH_SRVLIST_H
#define PH_SRVLIST_H
#include
// Columns
#define PHSVTLC_NAME 0
#define PHSVTLC_PID 1
#define PHSVTLC_DISPLAYNAME 2
#define PHSVTLC_TYPE 3
#define PHSVTLC_STATUS 4
#define PHSVTLC_STARTTYPE 5
#define PHSVTLC_BINARYPATH 6
#define PHSVTLC_ERRORCONTROL 7
#define PHSVTLC_GROUP 8
#define PHSVTLC_DESCRIPTION 9
#define PHSVTLC_KEYMODIFIEDTIME 10
#define PHSVTLC_VERIFICATIONSTATUS 11
#define PHSVTLC_VERIFIEDSIGNER 12
#define PHSVTLC_FILENAME 13
#define PHSVTLC_TIMELINE 14
#define PHSVTLC_EXITCODE 15
#define PHSVTLC_MAXIMUM 16
#define PHSN_CONFIG 0x1
#define PHSN_DESCRIPTION 0x2
#define PHSN_KEY 0x4
// begin_phapppub
typedef struct _PH_SERVICE_NODE
{
PH_TREENEW_NODE Node;
PH_SH_STATE ShState;
PPH_SERVICE_ITEM ServiceItem;
WCHAR StartTypeText[12 + 24 + 1];
// Config
PPH_STRING BinaryPath;
PPH_STRING LoadOrderGroup;
// Description
PPH_STRING Description;
// Key
LARGE_INTEGER KeyLastWriteTime;
PPH_STRING KeyModifiedTimeText;
// Exitcode
PPH_STRING ExitCodeText;
// end_phapppub
PPH_STRING TooltipText;
ULONG ValidMask;
PH_STRINGREF TextCache[PHSVTLC_MAXIMUM];
// begin_phapppub
} PH_SERVICE_NODE, *PPH_SERVICE_NODE;
// end_phapppub
VOID PhServiceTreeListInitialization(
VOID
);
VOID PhInitializeServiceTreeList(
_In_ HWND WindowHandle
);
VOID PhLoadSettingsServiceTreeList(
VOID
);
VOID PhSaveSettingsServiceTreeList(
VOID
);
// begin_phapppub
PHAPPAPI
PPH_TN_FILTER_SUPPORT
NTAPI
PhGetFilterSupportServiceTreeList(
VOID
);
// end_phapppub
PPH_SERVICE_NODE PhAddServiceNode(
_In_ PPH_SERVICE_ITEM ServiceItem,
_In_ ULONG RunId
);
// begin_phapppub
PHAPPAPI
PPH_SERVICE_NODE
NTAPI
PhFindServiceNode(
_In_ PPH_SERVICE_ITEM ServiceItem
);
// end_phapppub
VOID PhRemoveServiceNode(
_In_ PPH_SERVICE_NODE ServiceNode
);
// begin_phapppub
PHAPPAPI
VOID
NTAPI
PhUpdateServiceNode(
_In_ PPH_SERVICE_NODE ServiceNode
);
// end_phapppub
VOID PhTickServiceNodes(
VOID
);
// begin_phapppub
PHAPPAPI
PPH_SERVICE_ITEM
NTAPI
PhGetSelectedServiceItem(
VOID
);
PHAPPAPI
VOID
NTAPI
PhGetSelectedServiceItems(
_Out_ PPH_SERVICE_ITEM **Services,
_Out_ PULONG NumberOfServices
);
PHAPPAPI
VOID
NTAPI
PhDeselectAllServiceNodes(
VOID
);
PHAPPAPI
BOOLEAN
NTAPI
PhSelectAndEnsureVisibleServiceNode(
_In_ PPH_SERVICE_NODE ServiceNode
);
// end_phapppub
VOID PhCopyServiceList(
VOID
);
VOID PhWriteServiceList(
_Inout_ PPH_FILE_STREAM FileStream,
_In_ ULONG Mode
);
#endif
================================================
FILE: SystemInformer/include/srvprv.h
================================================
/*
* Copyright (c) 2022 Winsider Seminars & Solutions, Inc. All rights reserved.
*
* This file is part of System Informer.
*
* Authors:
*
* wj32 2009-2016
* dmex 2017-2024
*
*/
#ifndef PH_SRVPRV_H
#define PH_SRVPRV_H
extern PPH_OBJECT_TYPE PhServiceItemType;
extern BOOLEAN PhEnableServiceNonPoll;
extern BOOLEAN PhEnableServiceNonPollNotify;
extern ULONG PhServiceNonPollFlushInterval;
// begin_phapppub
typedef enum _VERIFY_RESULT VERIFY_RESULT;
typedef struct _PH_IMAGELIST_ITEM* PPH_IMAGELIST_ITEM;
typedef struct _PH_SERVICE_ITEM
{
PH_STRINGREF Key; // points to Name
PPH_STRING Name;
PPH_STRING DisplayName;
PPH_STRING FileName;
PPH_IMAGELIST_ITEM IconEntry;
volatile LONG JustProcessed;
// State
ULONG Type;
ULONG State;
ULONG ControlsAccepted;
ULONG Flags; // e.g. SERVICE_RUNS_IN_SYSTEM_PROCESS
HANDLE ProcessId;
// Config
ULONG StartType;
ULONG ErrorControl;
// ExitCode
ULONG Win32ExitCode;
ULONG ServiceSpecificExitCode;
// Signature
VERIFY_RESULT VerifyResult;
PPH_STRING VerifySignerName;
WCHAR ProcessIdString[PH_INT32_STR_LEN_1];
// end_phapppub
union
{
BOOLEAN BitFlags;
struct
{
BOOLEAN DelayedStart : 1;
BOOLEAN HasTriggers : 1;
BOOLEAN PendingProcess : 1;
BOOLEAN NeedsConfigUpdate : 1;
BOOLEAN MicrosoftService : 1;
BOOLEAN Spare : 3;
};
};
PSC_NOTIFICATION_REGISTRATION NotifyPropertyRegistration;
PSC_NOTIFICATION_REGISTRATION NotifyStatusRegistration;
union
{
BOOLEAN NotifyFlags;
struct
{
BOOLEAN NotifyCreatedPropertyRegistration : 1;
BOOLEAN NotifyCreatedStatusRegistration : 1;
BOOLEAN NotifySpare : 6;
};
};
// begin_phapppub
} PH_SERVICE_ITEM, *PPH_SERVICE_ITEM;
// end_phapppub
// begin_phapppub
typedef struct _PH_SERVICE_MODIFIED_DATA
{
PPH_SERVICE_ITEM ServiceItem;
PH_SERVICE_ITEM OldService;
} PH_SERVICE_MODIFIED_DATA, *PPH_SERVICE_MODIFIED_DATA;
typedef enum _PH_SERVICE_CHANGE
{
ServiceNone,
ServiceStarted,
ServiceContinued,
ServicePaused,
ServiceStopped,
ServiceModified,
} PH_SERVICE_CHANGE, *PPH_SERVICE_CHANGE;
// end_phapppub
BOOLEAN PhServiceProviderInitialization(
VOID
);
PPH_SERVICE_ITEM PhCreateServiceItem(
_In_opt_ LPENUM_SERVICE_STATUS_PROCESS Information
);
// begin_phapppub
PHAPPAPI
PPH_SERVICE_ITEM
NTAPI
PhReferenceServiceItem(
_In_ PPH_STRINGREF Name
);
FORCEINLINE
PPH_SERVICE_ITEM
NTAPI
PhReferenceServiceItemZ(
_In_ PCWSTR Name
)
{
PH_STRINGREF name;
PhInitializeStringRefLongHint(&name, Name);
return PhReferenceServiceItem(&name);
}
// end_phapppub
VOID PhMarkNeedsConfigUpdateServiceItem(
_In_ PPH_SERVICE_ITEM ServiceItem
);
// begin_phapppub
PHAPPAPI
PH_SERVICE_CHANGE
NTAPI
PhGetServiceChange(
_In_ PPH_SERVICE_MODIFIED_DATA Data
);
// end_phapppub
VOID PhUpdateProcessItemServices(
_In_ PPH_PROCESS_ITEM ProcessItem
);
_Function_class_(PH_PROVIDER_FUNCTION)
VOID PhServiceProviderUpdate(
_In_ PVOID Object
);
#endif
================================================
FILE: SystemInformer/include/sysinfo.h
================================================
/*
* Copyright (c) 2022 Winsider Seminars & Solutions, Inc. All rights reserved.
*
* This file is part of System Informer.
*
* Authors:
*
* wj32 2015-2016
* dmex 2017-2023
*
*/
#ifndef PH_SYSINFO_H
#define PH_SYSINFO_H
// begin_phapppub
typedef enum _PH_SYSINFO_VIEW_TYPE
{
SysInfoSummaryView,
SysInfoSectionView
} PH_SYSINFO_VIEW_TYPE;
typedef _Function_class_(PH_SYSINFO_COLOR_SETUP_FUNCTION)
VOID NTAPI PH_SYSINFO_COLOR_SETUP_FUNCTION(
_Out_ PPH_GRAPH_DRAW_INFO DrawInfo,
_In_ COLORREF Color1,
_In_ COLORREF Color2,
_In_ LONG WindowDpi
);
typedef PH_SYSINFO_COLOR_SETUP_FUNCTION* PPH_SYSINFO_COLOR_SETUP_FUNCTION;
typedef struct _PH_SYSINFO_PARAMETERS
{
HWND SysInfoWindowHandle;
HWND ContainerWindowHandle;
PPH_SYSINFO_COLOR_SETUP_FUNCTION ColorSetupFunction;
HFONT Font;
HFONT MediumFont;
HFONT LargeFont;
ULONG FontHeight;
ULONG FontAverageWidth;
ULONG MediumFontHeight;
ULONG MediumFontAverageWidth;
COLORREF GraphBackColor;
COLORREF PanelForeColor;
ULONG MinimumGraphHeight;
ULONG SectionViewGraphHeight;
LONG PanelWidth;
LONG WindowDpi;
// end_phapppub
ULONG PanelPadding;
ULONG WindowPadding;
ULONG GraphPadding;
ULONG SmallGraphWidth;
ULONG SmallGraphPadding;
ULONG SeparatorWidth;
ULONG CpuPadding;
ULONG MemoryPadding;
// begin_phapppub
} PH_SYSINFO_PARAMETERS, *PPH_SYSINFO_PARAMETERS;
typedef enum _PH_SYSINFO_SECTION_MESSAGE
{
SysInfoCreate,
SysInfoDestroy,
SysInfoTick,
SysInfoViewChanging, // PH_SYSINFO_VIEW_TYPE Parameter1, PPH_SYSINFO_SECTION Parameter2
SysInfoCreateDialog, // PPH_SYSINFO_CREATE_DIALOG Parameter1
SysInfoGraphGetDrawInfo, // PPH_GRAPH_DRAW_INFO Parameter1
SysInfoGraphGetTooltipText, // PPH_SYSINFO_GRAPH_GET_TOOLTIP_TEXT Parameter1
SysInfoGraphDrawPanel, // PPH_SYSINFO_DRAW_PANEL Parameter1
SysInfoDpiChanged, // ULONG Parameter1
MaxSysInfoMessage
} PH_SYSINFO_SECTION_MESSAGE;
typedef struct _PH_SYSINFO_SECTION *PPH_SYSINFO_SECTION;
typedef _Function_class_(PH_SYSINFO_SECTION_CALLBACK)
BOOLEAN NTAPI PH_SYSINFO_SECTION_CALLBACK(
_In_ PPH_SYSINFO_SECTION Section,
_In_ PH_SYSINFO_SECTION_MESSAGE Message,
_In_opt_ PVOID Parameter1,
_In_opt_ PVOID Parameter2
);
typedef PH_SYSINFO_SECTION_CALLBACK* PPH_SYSINFO_SECTION_CALLBACK;
typedef struct _PH_SYSINFO_CREATE_DIALOG
{
BOOLEAN CustomCreate;
// Parameters for default create
PVOID Instance;
PWSTR Template;
DLGPROC DialogProc;
PVOID Parameter;
} PH_SYSINFO_CREATE_DIALOG, *PPH_SYSINFO_CREATE_DIALOG;
typedef struct _PH_SYSINFO_GRAPH_GET_TOOLTIP_TEXT
{
ULONG Index;
PH_STRINGREF Text;
} PH_SYSINFO_GRAPH_GET_TOOLTIP_TEXT, *PPH_SYSINFO_GRAPH_GET_TOOLTIP_TEXT;
typedef struct _PH_SYSINFO_DRAW_PANEL
{
HDC hdc;
RECT Rect;
BOOLEAN CustomDraw;
// Parameters for default draw
PPH_STRING Title;
PPH_STRING SubTitle;
PPH_STRING SubTitleOverflow;
} PH_SYSINFO_DRAW_PANEL, *PPH_SYSINFO_DRAW_PANEL;
// end_phapppub
// begin_phapppub
typedef struct _PH_SYSINFO_SECTION
{
// Public
// Initialization
PH_STRINGREF Name;
ULONG Flags;
PPH_SYSINFO_SECTION_CALLBACK Callback;
PVOID Context;
// State
HWND GraphHandle;
PH_GRAPH_STATE GraphState;
PPH_SYSINFO_PARAMETERS Parameters;
// end_phapppub
// Private
struct
{
ULONG GraphHot : 1;
ULONG PanelHot : 1;
ULONG HasFocus : 1;
ULONG HideFocus : 1;
ULONG SpareFlags : 28;
};
HWND DialogHandle;
HWND PanelHandle;
ULONG PanelId;
WNDPROC GraphWindowProc;
WNDPROC PanelWindowProc;
// begin_phapppub
} PH_SYSINFO_SECTION, *PPH_SYSINFO_SECTION;
// end_phapppub
VOID PhSiNotifyChangeSettings(
VOID
);
// begin_phapppub
_Function_class_(PH_SYSINFO_COLOR_SETUP_FUNCTION)
PHAPPAPI
VOID
NTAPI
PhSiSetColorsGraphDrawInfo(
_Out_ PPH_GRAPH_DRAW_INFO DrawInfo,
_In_ COLORREF Color1,
_In_ COLORREF Color2,
_In_ LONG WindowDpi
);
_Function_class_(PH_GRAPH_LABEL_Y_FUNCTION)
PHAPPAPI
PPH_STRING
NTAPI
PhSiSizeLabelYFunction(
_In_ PPH_GRAPH_DRAW_INFO DrawInfo,
_In_ ULONG DataIndex,
_In_ FLOAT Value,
_In_ FLOAT Parameter
);
_Function_class_(PH_GRAPH_LABEL_Y_FUNCTION)
PHAPPAPI
PPH_STRING
NTAPI
PhSiDoubleLabelYFunction(
_In_ PPH_GRAPH_DRAW_INFO DrawInfo,
_In_ ULONG DataIndex,
_In_ FLOAT Value,
_In_ FLOAT Parameter
);
_Function_class_(PH_GRAPH_LABEL_Y_FUNCTION)
PHAPPAPI
PPH_STRING
NTAPI
PhSiUInt64LabelYFunction(
_In_ PPH_GRAPH_DRAW_INFO DrawInfo,
_In_ ULONG DataIndex,
_In_ FLOAT Value,
_In_ FLOAT Parameter
);
PHAPPAPI
VOID
NTAPI
PhShowSystemInformationDialog(
_In_opt_ PCWSTR SectionName
);
// end_phapppub
#endif
================================================
FILE: SystemInformer/include/sysinfop.h
================================================
/*
* Copyright (c) 2022 Winsider Seminars & Solutions, Inc. All rights reserved.
*
* This file is part of System Informer.
*
* Authors:
*
* wj32 2011-2016
* dmex 2016-2024
*
*/
#ifndef PH_SYSINFOP_H
#define PH_SYSINFOP_H
// Constants
#define PH_SYSINFO_FADE_ADD 50
#define PH_SYSINFO_PANEL_PADDING 3
#define PH_SYSINFO_WINDOW_PADDING 7
#define PH_SYSINFO_GRAPH_PADDING 4
#define PH_SYSINFO_SMALL_GRAPH_WIDTH 48
#define PH_SYSINFO_SMALL_GRAPH_PADDING 5
#define PH_SYSINFO_SEPARATOR_WIDTH 2
#define PH_SYSINFO_CPU_PADDING 5
#define PH_SYSINFO_MEMORY_PADDING 3
#define SI_MSG_SYSINFO_FIRST (WM_APP + 150)
#define SI_MSG_SYSINFO_ACTIVATE (WM_APP + 150)
#define SI_MSG_SYSINFO_UPDATE (WM_APP + 151)
#define SI_MSG_SYSINFO_CHANGE_SETTINGS (WM_APP + 152)
#define SI_MSG_SYSINFO_LAST (WM_APP + 152)
// Thread & window
extern HWND PhSipWindow;
_Function_class_(USER_THREAD_START_ROUTINE)
NTSTATUS PhSipSysInfoThreadStart(
_In_ PVOID Parameter
);
_Function_class_(DLGPROC)
INT_PTR CALLBACK PhSipSysInfoDialogProc(
_In_ HWND hwndDlg,
_In_ UINT uMsg,
_In_ WPARAM wParam,
_In_ LPARAM lParam
);
_Function_class_(DLGPROC)
INT_PTR CALLBACK PhSipContainerDialogProc(
_In_ HWND hwndDlg,
_In_ UINT uMsg,
_In_ WPARAM wParam,
_In_ LPARAM lParam
);
// Event handlers
VOID PhSipOnInitDialog(
VOID
);
VOID PhSipOnDestroy(
VOID
);
VOID PhSipOnNcDestroy(
VOID
);
BOOLEAN PhSipOnSysCommand(
_In_ ULONG Type,
_In_ LONG CursorScreenX,
_In_ LONG CursorScreenY
);
VOID PhSipOnSize(
_In_ HWND WindowHandle,
_In_ UINT State,
_In_ LONG Width,
_In_ LONG Height
);
VOID PhSipOnSizing(
_In_ ULONG Edge,
_In_ PRECT DragRectangle
);
VOID PhSipOnThemeChanged(
VOID
);
VOID PhSipOnCommand(
_In_ HWND HwndControl,
_In_ ULONG Id,
_In_ ULONG Code
);
_Success_(return)
BOOLEAN PhSipOnNotify(
_In_ NMHDR *Header,
_Out_ LRESULT *Result
);
BOOLEAN PhSipOnDrawItem(
_In_ ULONG_PTR Id,
_In_ PDRAWITEMSTRUCT DrawItemStruct
);
VOID PhSipOnUserMessage(
_In_ ULONG Message,
_In_ ULONG_PTR WParam,
_In_ ULONG_PTR LParam
);
ULONG PhSipGetProcessorRelationshipIndex(
_In_ LOGICAL_PROCESSOR_RELATIONSHIP RelationshipType,
_In_ ULONG Index
);
// Framework
VOID PhSipRegisterDialog(
_In_ HWND DialogWindowHandle
);
VOID PhSipUnregisterDialog(
_In_ HWND DialogWindowHandle
);
VOID PhSipInitializeParameters(
VOID
);
VOID PhSipDeleteParameters(
VOID
);
VOID PhSipUpdateColorParameters(
VOID
);
_Function_class_(PH_SYSINFO_CREATE_SECTION)
PPH_SYSINFO_SECTION PhSipCreateSection(
_In_ PPH_SYSINFO_SECTION Template
);
VOID PhSipDestroySection(
_In_ PPH_SYSINFO_SECTION Section
);
_Function_class_(PH_SYSINFO_FIND_SECTION)
PPH_SYSINFO_SECTION PhSipFindSection(
_In_ PPH_STRINGREF Name
);
PPH_SYSINFO_SECTION PhSipCreateInternalSection(
_In_ PWSTR Name,
_In_ ULONG Flags,
_In_ PPH_SYSINFO_SECTION_CALLBACK Callback
);
VOID PhSipDrawRestoreSummaryPanel(
_In_ PDRAWITEMSTRUCT DrawItemStruct
);
VOID PhSipDrawSeparator(
_In_ PDRAWITEMSTRUCT DrawItemStruct
);
VOID PhSipDrawPanel(
_In_ PPH_SYSINFO_SECTION Section,
_In_ HDC hdc,
_In_ PRECT Rect
);
VOID PhSipDefaultDrawPanel(
_In_ PPH_SYSINFO_SECTION Section,
_In_ PPH_SYSINFO_DRAW_PANEL DrawPanel
);
VOID PhSipLayoutSummaryView(
VOID
);
VOID PhSipLayoutSectionView(
VOID
);
_Function_class_(PH_SYSINFO_ENTER_SECTION_VIEW)
VOID PhSipEnterSectionView(
_In_ PPH_SYSINFO_SECTION NewSection
);
VOID PhSipEnterSectionViewInner(
_In_ PPH_SYSINFO_SECTION Section,
_In_ BOOLEAN FromSummaryView,
_Inout_ HDWP *DeferHandle,
_Inout_ HDWP *ContainerDeferHandle
);
_Function_class_(PH_SYSINFO_RESTORE_SUMMARY_VIEW)
VOID PhSipRestoreSummaryView(
VOID
);
VOID PhSipCreateSectionDialog(
_In_ PPH_SYSINFO_SECTION Section
);
_Function_class_(WNDPROC)
LRESULT CALLBACK PhSipGraphHookWndProc(
_In_ HWND hwnd,
_In_ UINT uMsg,
_In_ WPARAM wParam,
_In_ LPARAM lParam
);
_Function_class_(WNDPROC)
LRESULT CALLBACK PhSipPanelHookWndProc(
_In_ HWND hwnd,
_In_ UINT uMsg,
_In_ WPARAM wParam,
_In_ LPARAM lParam
);
LRESULT CALLBACK PhSipRestorePanelHookWndProc(
_In_ HWND hwnd,
_In_ UINT uMsg,
_In_ WPARAM wParam,
_In_ LPARAM lParam
);
// Misc.
VOID PhSipUpdateProcessorInformation(
VOID
);
VOID PhSipUpdateInterruptInformation(
_Out_ PULONG64 DpcCount
);
VOID PhSipUpdateProcessorFrequency(
VOID
);
VOID PhSipUpdateTimerResolution(
VOID
);
VOID PhSipUpdateProcessorPerformanceDistribution(
VOID
);
VOID PhSipUpdateThemeData(
VOID
);
VOID PhSipSaveWindowState(
VOID
);
_Function_class_(PH_CALLBACK_FUNCTION)
VOID NTAPI PhSipSysInfoUpdateHandler(
_In_opt_ PVOID Parameter,
_In_opt_ PVOID Context
);
_Function_class_(PH_CALLBACK_FUNCTION)
VOID NTAPI PhSipSysInfoSettingsCallback(
_In_opt_ PVOID Parameter,
_In_opt_ PVOID Context
);
// CPU section
_Function_class_(PH_SYSINFO_SECTION_CALLBACK)
BOOLEAN PhSipCpuSectionCallback(
_In_ PPH_SYSINFO_SECTION Section,
_In_ PH_SYSINFO_SECTION_MESSAGE Message,
_In_ PVOID Parameter1,
_In_ PVOID Parameter2
);
VOID PhSipInitializeCpuDialog(
VOID
);
VOID PhSipUninitializeCpuDialog(
VOID
);
VOID PhSipTickCpuDialog(
VOID
);
INT_PTR CALLBACK PhSipCpuDialogProc(
_In_ HWND hwndDlg,
_In_ UINT uMsg,
_In_ WPARAM wParam,
_In_ LPARAM lParam
);
INT_PTR CALLBACK PhSipCpuPanelDialogProc(
_In_ HWND hwndDlg,
_In_ UINT uMsg,
_In_ WPARAM wParam,
_In_ LPARAM lParam
);
VOID PhSipCreateCpuGraphs(
VOID
);
VOID PhSipLayoutCpuGraphs(
VOID
);
VOID PhSipSetOneGraphPerCpu(
VOID
);
_Function_class_(PH_GRAPH_MESSAGE_CALLBACK)
BOOLEAN NTAPI PhSipCpuGraphCallback(
_In_ HWND GraphHandle,
_In_ ULONG GraphMessage,
_In_ PVOID Parameter1,
_In_ PVOID Parameter2,
_In_ PVOID Context
);
VOID PhSipUpdateCpuGraphs(
VOID
);
VOID PhSipUpdateCpuPanel(
VOID
);
PPH_PROCESS_RECORD PhSipReferenceMaxCpuRecord(
_In_ LONG Index
);
PPH_STRING PhSipGetMaxCpuString(
_In_ LONG Index
);
PPH_STRING PhSipGetCpuBrandString(
VOID
);
_Success_(return)
BOOLEAN PhSipGetCpuFrequencyFromDistribution(
_Out_ DOUBLE *Frequency
);
PCPH_STRINGREF PhGetHybridProcessorType(
_In_ ULONG ProcessorIndex
);
BOOLEAN PhIsCoreParked(
_In_ ULONG ProcessorIndex
);
_Function_class_(USER_THREAD_START_ROUTINE)
NTSTATUS NTAPI PhSipCpuSMBIOSWorkRoutine(
_In_ PVOID ThreadParameter
);
// Memory section
_Function_class_(PH_SYSINFO_SECTION_CALLBACK)
BOOLEAN PhSipMemorySectionCallback(
_In_ PPH_SYSINFO_SECTION Section,
_In_ PH_SYSINFO_SECTION_MESSAGE Message,
_In_ PVOID Parameter1,
_In_ PVOID Parameter2
);
VOID PhSipInitializeMemoryDialog(
VOID
);
VOID PhSipUninitializeMemoryDialog(
VOID
);
VOID PhSipTickMemoryDialog(
VOID
);
INT_PTR CALLBACK PhSipMemoryDialogProc(
_In_ HWND hwndDlg,
_In_ UINT uMsg,
_In_ WPARAM wParam,
_In_ LPARAM lParam
);
INT_PTR CALLBACK PhSipMemoryPanelDialogProc(
_In_ HWND hwndDlg,
_In_ UINT uMsg,
_In_ WPARAM wParam,
_In_ LPARAM lParam
);
VOID PhSipCreateMemoryGraphs(
VOID
);
VOID PhSipLayoutMemoryGraphs(
_In_ HWND hwnd
);
_Function_class_(PH_GRAPH_MESSAGE_CALLBACK)
BOOLEAN NTAPI PhSipNotifyCommitGraph(
_In_ HWND GraphHandle,
_In_ ULONG GraphMessage,
_In_ PVOID Parameter1,
_In_ PVOID Parameter2,
_In_ PVOID Context
);
_Function_class_(PH_GRAPH_MESSAGE_CALLBACK)
BOOLEAN NTAPI PhSipNotifyPhysicalGraph(
_In_ HWND GraphHandle,
_In_ ULONG GraphMessage,
_In_ PVOID Parameter1,
_In_ PVOID Parameter2,
_In_ PVOID Context
);
VOID PhSipUpdateMemoryGraphs(
VOID
);
VOID PhSipUpdateMemoryPanel(
VOID
);
_Function_class_(USER_THREAD_START_ROUTINE)
NTSTATUS NTAPI PhSipLoadMmAddresses(
_In_ PVOID Parameter
);
VOID PhSipGetPoolLimits(
_Out_ PSIZE_T Paged,
_Out_ PSIZE_T NonPaged
);
_Success_(return)
BOOLEAN PhSipGetMemoryCompressionLimits(
_Out_ FLOAT *CurrentCompressedMemory,
_Out_ FLOAT *TotalCompressedMemory,
_Out_ FLOAT *TotalSavedMemory
);
// I/O section
_Function_class_(PH_SYSINFO_SECTION_CALLBACK)
BOOLEAN NTAPI PhSipIoSectionCallback(
_In_ PPH_SYSINFO_SECTION Section,
_In_ PH_SYSINFO_SECTION_MESSAGE Message,
_In_ PVOID Parameter1,
_In_ PVOID Parameter2
);
VOID PhSipInitializeIoDialog(
VOID
);
VOID PhSipUninitializeIoDialog(
VOID
);
VOID PhSipTickIoDialog(
VOID
);
INT_PTR CALLBACK PhSipIoDialogProc(
_In_ HWND hwndDlg,
_In_ UINT uMsg,
_In_ WPARAM wParam,
_In_ LPARAM lParam
);
INT_PTR CALLBACK PhSipIoPanelDialogProc(
_In_ HWND hwndDlg,
_In_ UINT uMsg,
_In_ WPARAM wParam,
_In_ LPARAM lParam
);
VOID PhSipCreateIoGraph(
VOID
);
VOID PhSipLayoutIoGraphs(
_In_ HWND WindowHandle
);
_Function_class_(PH_GRAPH_MESSAGE_CALLBACK)
BOOLEAN NTAPI PhSipNotifyIoReadGraph(
_In_ HWND GraphHandle,
_In_ ULONG GraphMessage,
_In_ PVOID Parameter1,
_In_ PVOID Parameter2,
_In_ PVOID Context
);
_Function_class_(PH_GRAPH_MESSAGE_CALLBACK)
BOOLEAN NTAPI PhSipNotifyIoWriteGraph(
_In_ HWND GraphHandle,
_In_ ULONG GraphMessage,
_In_ PVOID Parameter1,
_In_ PVOID Parameter2,
_In_ PVOID Context
);
_Function_class_(PH_GRAPH_MESSAGE_CALLBACK)
BOOLEAN NTAPI PhSipNotifyIoOtherGraph(
_In_ HWND GraphHandle,
_In_ ULONG GraphMessage,
_In_ PVOID Parameter1,
_In_ PVOID Parameter2,
_In_ PVOID Context
);
VOID PhSipUpdateIoGraph(
VOID
);
VOID PhSipUpdateIoPanel(
VOID
);
PPH_PROCESS_RECORD PhSipReferenceMaxIoRecord(
_In_ LONG Index
);
PPH_STRING PhSipGetMaxIoString(
_In_ LONG Index
);
#endif
================================================
FILE: SystemInformer/include/thrdlist.h
================================================
/*
* Copyright (c) 2022 Winsider Seminars & Solutions, Inc. All rights reserved.
*
* This file is part of System Informer.
*
* Authors:
*
* wj32 2016
* dmex 2017-2023
*
*/
#ifndef PH_THRDLIST_H
#define PH_THRDLIST_H
// Columns
typedef enum _PH_THREAD_TREELIST_COLUMN
{
PH_THREAD_TREELIST_COLUMN_TID,
PH_THREAD_TREELIST_COLUMN_CPU,
PH_THREAD_TREELIST_COLUMN_CYCLESDELTA,
PH_THREAD_TREELIST_COLUMN_STARTADDRESSWIN32,
PH_THREAD_TREELIST_COLUMN_PRIORITYSYMBOLIC,
PH_THREAD_TREELIST_COLUMN_SERVICE,
PH_THREAD_TREELIST_COLUMN_NAME,
PH_THREAD_TREELIST_COLUMN_STARTED,
PH_THREAD_TREELIST_COLUMN_STARTMODULE,
PH_THREAD_TREELIST_COLUMN_CONTEXTSWITCHES,
PH_THREAD_TREELIST_COLUMN_CONTEXTSWITCHESDELTA,
PH_THREAD_TREELIST_COLUMN_PRIORITY,
PH_THREAD_TREELIST_COLUMN_BASEPRIORITY,
PH_THREAD_TREELIST_COLUMN_PAGEPRIORITY,
PH_THREAD_TREELIST_COLUMN_IOPRIORITY,
PH_THREAD_TREELIST_COLUMN_CYCLES,
PH_THREAD_TREELIST_COLUMN_STATE,
PH_THREAD_TREELIST_COLUMN_KERNELTIME,
PH_THREAD_TREELIST_COLUMN_USERTIME,
PH_THREAD_TREELIST_COLUMN_IDEALPROCESSOR,
PH_THREAD_TREELIST_COLUMN_CRITICAL,
PH_THREAD_TREELIST_COLUMN_TIDHEX,
PH_THREAD_TREELIST_COLUMN_CPUCORECYCLES,
PH_THREAD_TREELIST_COLUMN_TOKEN_STATE,
PH_THREAD_TREELIST_COLUMN_PENDINGIRP,
PH_THREAD_TREELIST_COLUMN_LASTSYSTEMCALL,
PH_THREAD_TREELIST_COLUMN_LASTSTATUSCODE,
PH_THREAD_TREELIST_COLUMN_TIMELINE,
PH_THREAD_TREELIST_COLUMN_APARTMENTTYPE,
PH_THREAD_TREELIST_COLUMN_APARTMENTFLAGS,
PH_THREAD_TREELIST_COLUMN_FIBER,
PH_THREAD_TREELIST_COLUMN_PRIORITYBOOST,
PH_THREAD_TREELIST_COLUMN_CPUUSER,
PH_THREAD_TREELIST_COLUMN_CPUKERNEL,
//PH_THREAD_TREELIST_COLUMN_CPUHISTORY,
PH_THREAD_TREELIST_COLUMN_STACKUSAGE,
PH_THREAD_TREELIST_COLUMN_WAITTIME,
PH_THREAD_TREELIST_COLUMN_IOREADS,
PH_THREAD_TREELIST_COLUMN_IOWRITES,
PH_THREAD_TREELIST_COLUMN_IOOTHER,
PH_THREAD_TREELIST_COLUMN_IOREADBYTES,
PH_THREAD_TREELIST_COLUMN_IOWRITEBYTES,
PH_THREAD_TREELIST_COLUMN_IOOTHERBYTES,
PH_THREAD_TREELIST_COLUMN_LXSSTID,
PH_THREAD_TREELIST_COLUMN_POWERTHROTTLING,
PH_THREAD_TREELIST_COLUMN_STARTADDRESS,
PH_THREAD_TREELIST_COLUMN_KSTACKUSAGE,
PH_THREAD_TREELIST_COLUMN_RPC,
PH_THREAD_TREELIST_COLUMN_ACTUALBASEPRIORITY,
PH_THREAD_TREELIST_COLUMN_MAXIMUM,
} PH_THREAD_TREELIST_COLUMN;
typedef enum _PH_THREAD_TREELIST_MENUITEM
{
PH_THREAD_TREELIST_MENUITEM_HIDE_SUSPENDED = 1,
PH_THREAD_TREELIST_MENUITEM_HIDE_GUITHREADS,
PH_THREAD_TREELIST_MENUITEM_HIDE_UNKNOWNSTARTADDRESS,
PH_THREAD_TREELIST_MENUITEM_HIGHLIGHT_SUSPENDED,
PH_THREAD_TREELIST_MENUITEM_HIGHLIGHT_GUITHREADS,
PH_THREAD_TREELIST_MENUITEM_SAVE, // Always last (dmex)
PH_THREAD_TREELIST_MENUITEM_MAXIMUM
} PH_THREAD_TREELIST_MENUITEM;
typedef enum _PH_THREAD_TOKEN_STATE
{
PH_THREAD_TOKEN_STATE_UNKNOWN,
PH_THREAD_TOKEN_STATE_NOT_PRESENT,
PH_THREAD_TOKEN_STATE_ANONYMOUS,
PH_THREAD_TOKEN_STATE_PRESENT
} PH_THREAD_TOKEN_STATE;
// begin_phapppub
typedef struct _PH_THREAD_NODE
{
PH_TREENEW_NODE Node;
PH_SH_STATE ShState;
HANDLE ThreadId;
PPH_THREAD_ITEM ThreadItem;
// end_phapppub
PH_STRINGREF TextCache[PH_THREAD_TREELIST_COLUMN_MAXIMUM];
ULONG ValidMask;
HANDLE ThreadContextHandle;
HANDLE ThreadReadVmHandle;
BOOLEAN ThreadContextHandleValid;
BOOLEAN ThreadReadVmHandleValid;
LONG IdealProcessorMask;
ULONG PagePriority;
IO_PRIORITY_HINT IoPriority;
BOOLEAN BreakOnTermination;
BOOLEAN PendingIrp;
BOOLEAN Fiber;
BOOLEAN PriorityBoost;
ULONG SuspendCount;
FLOAT StackUsageFloat;
ULONG_PTR StackUsage;
ULONG_PTR StackLimit;
FLOAT KernelStackUsageFloat;
ULONG_PTR KernelStackUsage;
ULONG_PTR KernelStackLimit;
PH_THREAD_TOKEN_STATE TokenState;
NTSTATUS LastSystemCallStatus;
THREAD_LAST_SYSCALL_INFORMATION LastSystemCall;
NTSTATUS LastStatusValue;
NTSTATUS LastStatusQueryStatus;
PH_APARTMENT_INFO ApartmentInfo;
BOOLEAN HasRpcState;
WCHAR CpuUsageText[PH_INT32_STR_LEN_1];
WCHAR CpuUserUsageText[PH_INT32_STR_LEN_1];
WCHAR CpuKernelUsageText[PH_INT32_STR_LEN_1];
PPH_STRING CyclesDeltaText; // used for Context Switches Delta as well
PPH_STRING ContextSwitchesDeltaText;
PPH_STRING StartAddressText;
PPH_STRING CreatedText;
PPH_STRING NameText;
PPH_STRING StateText;
PPH_STRING LastSystemCallText;
PPH_STRING LastErrorCodeText;
PPH_STRING ApartmentTypeText;
PPH_STRING ApartmentFlagsText;
PPH_STRING StackUsageText;
PPH_STRING KernelStackUsageText;
WCHAR ContextSwitchesText[PH_INT64_STR_LEN_1];
WCHAR PriorityText[PH_INT32_STR_LEN_1];
WCHAR BasePriorityText[PH_INT32_STR_LEN_1];
WCHAR ActualBasePriorityText[PH_INT32_STR_LEN_1];
WCHAR CyclesText[PH_INT64_STR_LEN_1];
PPH_STRING KernelTimeText;
PPH_STRING UserTimeText;
WCHAR IdealProcessorText[PH_INT32_STR_LEN + 1 + PH_INT32_STR_LEN + 1];
WCHAR ThreadIdHexText[PH_INT32_STR_LEN_1];
WCHAR CpuCoreUsageText[PH_INT32_STR_LEN_1];
PPH_STRING WaitTimeText;
PPH_STRING IoReads;
PPH_STRING IoWrites;
PPH_STRING IoOther;
PPH_STRING IoReadBytes;
PPH_STRING IoWriteBytes;
PPH_STRING IoOtherBytes;
// begin_phapppub
} PH_THREAD_NODE, *PPH_THREAD_NODE;
// end_phapppub
typedef struct _PH_THREAD_LIST_CONTEXT
{
HWND ParentWindowHandle;
HWND TreeNewHandle;
ULONG TreeNewSortColumn;
PH_SORT_ORDER TreeNewSortOrder;
PH_CM_MANAGER Cm;
PPH_HASHTABLE NodeHashtable;
PPH_LIST NodeList;
PPH_POINTER_LIST NodeStateList;
PH_TN_FILTER_SUPPORT TreeFilterSupport;
HANDLE ProcessId;
LARGE_INTEGER ProcessCreateTime;
BOOLEAN EnableStateHighlighting;
BOOLEAN UseCycleTime;
BOOLEAN HasServices;
union
{
ULONG Flags;
struct
{
ULONG Reserved : 3;
ULONG HideSuspended : 1;
ULONG HideGuiThreads : 1;
ULONG HighlightSuspended : 1;
ULONG HighlightGuiThreads : 1;
ULONG Spare : 25;
};
};
} PH_THREAD_LIST_CONTEXT, *PPH_THREAD_LIST_CONTEXT;
VOID PhInitializeThreadList(
_In_ HWND ParentWindowHandle,
_In_ HWND TreeNewHandle,
_Out_ PPH_THREAD_LIST_CONTEXT Context
);
VOID PhDeleteThreadList(
_In_ PPH_THREAD_LIST_CONTEXT Context
);
VOID PhLoadSettingsThreadList(
_Inout_ PPH_THREAD_LIST_CONTEXT Context
);
VOID PhSaveSettingsThreadList(
_Inout_ PPH_THREAD_LIST_CONTEXT Context
);
VOID PhSetOptionsThreadList(
_Inout_ PPH_THREAD_LIST_CONTEXT Context,
_In_ ULONG Options
);
PPH_THREAD_NODE PhAddThreadNode(
_Inout_ PPH_THREAD_LIST_CONTEXT Context,
_In_ PPH_THREAD_ITEM ThreadItem,
_In_ BOOLEAN FirstRun
);
PPH_THREAD_NODE PhFindThreadNode(
_In_ PPH_THREAD_LIST_CONTEXT Context,
_In_ HANDLE ThreadId
);
VOID PhRemoveThreadNode(
_In_ PPH_THREAD_LIST_CONTEXT Context,
_In_ PPH_THREAD_NODE ThreadNode
);
VOID PhUpdateThreadNode(
_In_ PPH_THREAD_LIST_CONTEXT Context,
_In_ PPH_THREAD_NODE ThreadNode
);
VOID PhTickThreadNodes(
_In_ PPH_THREAD_LIST_CONTEXT Context
);
PPH_THREAD_ITEM PhGetSelectedThreadItem(
_In_ PPH_THREAD_LIST_CONTEXT Context
);
VOID PhGetSelectedThreadItems(
_In_ PPH_THREAD_LIST_CONTEXT Context,
_Out_ PPH_THREAD_ITEM **Threads,
_Out_ PULONG NumberOfThreads
);
#endif
================================================
FILE: SystemInformer/include/thrdprv.h
================================================
/*
* Copyright (c) 2022 Winsider Seminars & Solutions, Inc. All rights reserved.
*
* This file is part of System Informer.
*
* Authors:
*
* wj32 2009-2016
* dmex 2017-2023
*
*/
#ifndef PH_THRDPRV_H
#define PH_THRDPRV_H
extern PPH_OBJECT_TYPE PhThreadProviderType;
extern PPH_OBJECT_TYPE PhThreadItemType;
// begin_phapppub
typedef struct _PH_THREAD_ITEM
{
union
{
CLIENT_ID ClientId;
struct
{
HANDLE ProcessId;
HANDLE ThreadId;
};
};
LARGE_INTEGER CreateTime;
LARGE_INTEGER KernelTime;
LARGE_INTEGER UserTime;
PH_UINT64_DELTA CpuKernelDelta;
PH_UINT64_DELTA CpuUserDelta;
PH_UINT32_DELTA ContextSwitchesDelta;
PH_UINT64_DELTA CyclesDelta;
FLOAT CpuUsage;
FLOAT CpuKernelUsage;
FLOAT CpuUserUsage;
KPRIORITY Priority;
KPRIORITY BasePriority;
KPRIORITY ActualBasePriority; // KeSetActualBasePriorityThread (jxy-s)
union
{
KAFFINITY AffinityMaskSingle; // Single processor group
PKAFFINITY AffinityMaskGroups; // Multiple processor groups * PhSystemProcessorInformation.NumberOfProcessorGroups
};
ULONG AffinityPopulationCount;
ULONG WaitTime;
KTHREAD_STATE State;
KWAIT_REASON WaitReason;
HANDLE ThreadHandle;
PPH_STRING ServiceName;
PVOID StartAddressWin32;
PVOID StartAddress;
NTSTATUS ThreadHandleStatus;
NTSTATUS StartAddressStatus;
PPH_STRING StartAddressWin32String;
PPH_STRING StartAddressWin32FileName;
enum _PH_SYMBOL_RESOLVE_LEVEL StartAddressWin32ResolveLevel;
PPH_STRING StartAddressString;
PPH_STRING StartAddressFileName;
enum _PH_SYMBOL_RESOLVE_LEVEL StartAddressResolveLevel;
BOOLEAN IsGuiThread;
BOOLEAN JustResolved;
WCHAR ThreadIdString[PH_INT32_STR_LEN_1];
WCHAR ThreadIdHexString[PH_PTR_STR_LEN_1];
WCHAR LxssThreadIdString[PH_INT32_STR_LEN_1];
IO_COUNTERS IoCounters;
ULONG LxssThreadId;
BOOLEAN PowerThrottling;
} PH_THREAD_ITEM, *PPH_THREAD_ITEM;
typedef enum _PH_KNOWN_PROCESS_TYPE PH_KNOWN_PROCESS_TYPE;
typedef struct _PH_THREAD_PROVIDER
{
PPH_HASHTABLE ThreadHashtable;
PH_FAST_LOCK ThreadHashtableLock;
PH_CALLBACK ThreadAddedEvent;
PH_CALLBACK ThreadModifiedEvent;
PH_CALLBACK ThreadRemovedEvent;
PH_CALLBACK UpdatedEvent;
PH_CALLBACK LoadingStateChangedEvent;
HANDLE ProcessId;
HANDLE ProcessHandle;
union
{
BOOLEAN Flags;
struct
{
BOOLEAN HasServices : 1;
BOOLEAN HasServicesKnown : 1;
BOOLEAN Terminating : 1;
BOOLEAN Spare : 5;
};
};
struct _PH_SYMBOL_PROVIDER *SymbolProvider;
SLIST_HEADER QueryListHead;
PH_QUEUED_LOCK LoadSymbolsLock;
LONG SymbolsLoading;
ULONG64 RunId;
ULONG64 SymbolsLoadedRunId;
} PH_THREAD_PROVIDER, *PPH_THREAD_PROVIDER;
// end_phapppub
PPH_THREAD_PROVIDER PhCreateThreadProvider(
_In_ HANDLE ProcessId
);
VOID PhRegisterThreadProvider(
_In_ PPH_THREAD_PROVIDER ThreadProvider,
_Out_ PPH_CALLBACK_REGISTRATION CallbackRegistration
);
VOID PhUnregisterThreadProvider(
_In_ PPH_THREAD_PROVIDER ThreadProvider,
_In_ PPH_CALLBACK_REGISTRATION CallbackRegistration
);
VOID PhSetTerminatingThreadProvider(
_Inout_ PPH_THREAD_PROVIDER ThreadProvider
);
VOID PhLoadSymbolsThreadProvider(
_In_ PPH_THREAD_PROVIDER ThreadProvider
);
PPH_THREAD_ITEM PhCreateThreadItem(
_In_ CLIENT_ID ClientId
);
PPH_THREAD_ITEM PhReferenceThreadItem(
_In_ PPH_THREAD_PROVIDER ThreadProvider,
_In_ HANDLE ThreadId
);
VOID PhDereferenceAllThreadItems(
_In_ PPH_THREAD_PROVIDER ThreadProvider
);
PCPH_STRINGREF PhGetBasePrioritySymbolicString(
_In_ KPRIORITY BasePriority
);
VOID PhThreadProviderInitialUpdate(
_In_ PPH_THREAD_PROVIDER ThreadProvider
);
#endif
================================================
FILE: SystemInformer/infodlg.c
================================================
/*
* Copyright (c) 2022 Winsider Seminars & Solutions, Inc. All rights reserved.
*
* This file is part of System Informer.
*
* Authors:
*
* wj32 2010
* dmex 2016-2023
*
*/
#include
#include
#include
typedef struct _PH_INFORMATION_CONTEXT
{
PCWSTR String;
ULONG Flags;
PH_LAYOUT_MANAGER LayoutManager;
RECT MinimumSize;
} PH_INFORMATION_CONTEXT, *PPH_INFORMATION_CONTEXT;
static INT_PTR CALLBACK PhpInformationDlgProc(
_In_ HWND hwndDlg,
_In_ UINT uMsg,
_In_ WPARAM wParam,
_In_ LPARAM lParam
)
{
PPH_INFORMATION_CONTEXT context;
if (uMsg == WM_INITDIALOG)
{
context = (PPH_INFORMATION_CONTEXT)lParam;
PhSetWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT, context);
}
else
{
context = PhGetWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT);
}
if (!context)
return FALSE;
switch (uMsg)
{
case WM_INITDIALOG:
{
PhSetApplicationWindowIcon(hwndDlg);
PhInitializeLayoutManager(&context->LayoutManager, hwndDlg);
PhAddLayoutItem(&context->LayoutManager, GetDlgItem(hwndDlg, IDC_TEXT), NULL, PH_ANCHOR_ALL);
PhAddLayoutItem(&context->LayoutManager, GetDlgItem(hwndDlg, IDOK), NULL, PH_ANCHOR_RIGHT | PH_ANCHOR_BOTTOM);
PhAddLayoutItem(&context->LayoutManager, GetDlgItem(hwndDlg, IDC_COPY), NULL, PH_ANCHOR_RIGHT | PH_ANCHOR_BOTTOM);
PhAddLayoutItem(&context->LayoutManager, GetDlgItem(hwndDlg, IDC_SAVE), NULL, PH_ANCHOR_RIGHT | PH_ANCHOR_BOTTOM);
if (PhValidWindowPlacementFromSetting(SETTING_INFORMATION_WINDOW_POSITION))
PhLoadWindowPlacementFromSetting(NULL, SETTING_INFORMATION_WINDOW_SIZE, hwndDlg);
PhCenterWindow(hwndDlg, GetParent(hwndDlg));
context->MinimumSize = (RECT){ -1, -1, -1, -1 };
if (context->MinimumSize.left == -1)
{
RECT rect;
rect.left = 0;
rect.top = 0;
rect.right = 200;
rect.bottom = 140;
MapDialogRect(hwndDlg, &rect);
context->MinimumSize = rect;
context->MinimumSize.left = 0;
}
PhSetDialogItemText(hwndDlg, IDC_TEXT, context->String);
PhSetDialogFocus(hwndDlg, GetDlgItem(hwndDlg, IDOK));
PhInitializeWindowTheme(hwndDlg, PhEnableThemeSupport);
}
break;
case WM_DESTROY:
{
PhSaveWindowPlacementToSetting(SETTING_INFORMATION_WINDOW_POSITION, SETTING_INFORMATION_WINDOW_SIZE, hwndDlg);
PhDeleteLayoutManager(&context->LayoutManager);
PhRemoveWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT);
}
break;
case WM_COMMAND:
{
switch (GET_WM_COMMAND_ID(wParam, lParam))
{
case IDCANCEL:
case IDOK:
EndDialog(hwndDlg, IDOK);
break;
case IDC_COPY:
{
HWND editControl;
LONG selStart;
LONG selEnd;
PH_STRINGREF string;
editControl = GetDlgItem(hwndDlg, IDC_TEXT);
SendMessage(editControl, EM_GETSEL, (WPARAM)&selStart, (LPARAM)&selEnd);
if (selStart == selEnd)
{
// Select and copy the entire string.
PhInitializeStringRefLongHint(&string, context->String);
Edit_SetSel(editControl, 0, -1);
}
else
{
string.Buffer = PTR_ADD_OFFSET(context->String, selStart);
string.Length = (selEnd - selStart) * sizeof(WCHAR);
}
PhSetClipboardString(hwndDlg, &string);
PhSetDialogFocus(hwndDlg, editControl);
}
break;
case IDC_SAVE:
{
static PH_FILETYPE_FILTER filters[] =
{
{ L"Text files (*.txt)", L"*.txt" },
{ L"All files (*.*)", L"*.*" }
};
PVOID fileDialog;
fileDialog = PhCreateSaveFileDialog();
PhSetFileDialogFilter(fileDialog, filters, sizeof(filters) / sizeof(PH_FILETYPE_FILTER));
PhSetFileDialogFileName(fileDialog, L"Information.txt");
if (PhShowFileDialog(hwndDlg, fileDialog))
{
NTSTATUS status;
PPH_STRING fileName;
PPH_FILE_STREAM fileStream;
fileName = PH_AUTO(PhGetFileDialogFileName(fileDialog));
if (NT_SUCCESS(status = PhCreateFileStream(
&fileStream,
fileName->Buffer,
FILE_GENERIC_WRITE,
FILE_SHARE_READ,
FILE_OVERWRITE_IF,
0
)))
{
PH_STRINGREF string;
PhWriteStringAsUtf8FileStream(fileStream, (PPH_STRINGREF)&PhUnicodeByteOrderMark);
PhInitializeStringRefLongHint(&string, context->String);
PhWriteStringAsUtf8FileStream(fileStream, &string);
PhDereferenceObject(fileStream);
}
if (!NT_SUCCESS(status))
PhShowStatus(hwndDlg, L"Unable to create the file", status, 0);
}
PhFreeFileDialog(fileDialog);
}
break;
}
}
break;
case WM_DPICHANGED:
{
PhLayoutManagerUpdate(&context->LayoutManager, LOWORD(wParam));
PhLayoutManagerLayout(&context->LayoutManager);
}
break;
case WM_SIZE:
{
PhLayoutManagerLayout(&context->LayoutManager);
}
break;
case WM_SIZING:
{
PhResizingMinimumSize((PRECT)lParam, wParam, context->MinimumSize.right, context->MinimumSize.bottom);
}
break;
case WM_CTLCOLORBTN:
return HANDLE_WM_CTLCOLORBTN(hwndDlg, wParam, lParam, PhWindowThemeControlColor);
case WM_CTLCOLORDLG:
return HANDLE_WM_CTLCOLORDLG(hwndDlg, wParam, lParam, PhWindowThemeControlColor);
case WM_CTLCOLORSTATIC:
return HANDLE_WM_CTLCOLORSTATIC(hwndDlg, wParam, lParam, PhWindowThemeControlColor);
}
return FALSE;
}
VOID PhShowInformationDialog(
_In_ HWND ParentWindowHandle,
_In_ PCWSTR String,
_Reserved_ ULONG Flags
)
{
PH_INFORMATION_CONTEXT context;
memset(&context, 0, sizeof(PH_INFORMATION_CONTEXT));
context.String = String;
context.Flags = Flags;
PhDialogBox(
PhInstanceHandle,
MAKEINTRESOURCE(IDD_INFORMATION),
ParentWindowHandle,
PhpInformationDlgProc,
&context
);
}
================================================
FILE: SystemInformer/informer.c
================================================
/*
* Copyright (c) 2022 Winsider Seminars & Solutions, Inc. All rights reserved.
*
* This file is part of System Informer.
*
* Authors:
*
* jxy-s 2024-2025
*
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
typedef struct _PH_INFORMER_DB_REAP
{
ULONG64 ProcessStartKey;
ULONG64 MaxCount;
ULONG Seconds;
} PH_INFORMER_DB_REAP, *PPH_INFORMER_DB_REAP;
typedef int SQLITE_APICALL sqlite3_open_v2_fn(
const char *filename, /* Database filename (UTF-8) */
sqlite3 **ppDb, /* OUT: SQLite db handle */
int flags, /* Flags */
const char *zVfs /* Name of VFS module to use */
);
typedef int SQLITE_APICALL sqlite3_exec_fn(
sqlite3*, /* An open database */
const char *sql, /* SQL to be evaluated */
int (SQLITE_CALLBACK *callback)(void*,int,char**,char**), /* Callback function */
void *, /* 1st argument to callback */
char **errmsg /* Error msg written here */
);
typedef int SQLITE_APICALL sqlite3_create_function_v2_fn(
sqlite3 *db,
const char *zFunctionName,
int nArg,
int eTextRep,
void *pApp,
void (SQLITE_CALLBACK *xFunc)(sqlite3_context*,int,sqlite3_value**),
void (SQLITE_CALLBACK *xStep)(sqlite3_context*,int,sqlite3_value**),
void (SQLITE_CALLBACK *xFinal)(sqlite3_context*),
void(SQLITE_CALLBACK *xDestroy)(void*)
);
typedef int SQLITE_APICALL sqlite3_prepare_v2_fn(
sqlite3 *db, /* Database handle */
const char *zSql, /* SQL statement, UTF-8 encoded */
int nByte, /* Maximum length of zSql in bytes. */
sqlite3_stmt **ppStmt, /* OUT: Statement handle */
const char **pzTail /* OUT: Pointer to unused portion of zSql */
);
typedef int SQLITE_APICALL sqlite3_bind_int64_fn(sqlite3_stmt*, int, sqlite3_int64);
typedef int SQLITE_APICALL sqlite3_step_fn(sqlite3_stmt*);
typedef int SQLITE_APICALL sqlite3_reset_fn(sqlite3_stmt *pStmt);
typedef sqlite3_int64 SQLITE_APICALL sqlite3_value_int64_fn(sqlite3_value*);
typedef sqlite3_int64 SQLITE_APICALL sqlite3_column_int64_fn(sqlite3_stmt*, int iCol);
static PH_CALLBACK_REGISTRATION PhpInformerProcessesUpdatedRegistration;
static PH_CALLBACK_REGISTRATION PhpInformerProcessesRemovedRegistration;
static PH_FREE_LIST PhpInformerReapFreeList;
static PH_QUEUED_LOCK PhpInformerDatabaseLock = PH_QUEUED_LOCK_INIT;
static sqlite3* PhpInformerDB = NULL;
static sqlite3_stmt* PhpInformerDBInsert = NULL;
static sqlite3_stmt* PhpInformerDBQueryCount = NULL;
static sqlite3_stmt* PhpInformerDBReapMax = NULL;
static sqlite3_stmt* PhpInformerDBReapTime = NULL;
static sqlite3_stmt* PhpInformerDBReapProc = NULL;
static sqlite3_stmt* PhpInformerDBQuery = NULL;
static sqlite3_stmt* PhpInformerDBQueryProc = NULL;
static sqlite3_open_v2_fn* sqlite3_open_v2_I = NULL;
static sqlite3_exec_fn* sqlite3_exec_I = NULL;
static sqlite3_create_function_v2_fn* sqlite3_create_function_v2_I = NULL;
static sqlite3_prepare_v2_fn* sqlite3_prepare_v2_I = NULL;
static sqlite3_bind_int64_fn* sqlite3_bind_int64_I = NULL;
static sqlite3_step_fn* sqlite3_step_I = NULL;
static sqlite3_reset_fn* sqlite3_reset_I = NULL;
static sqlite3_value_int64_fn* sqlite3_value_int64_I = NULL;
static sqlite3_column_int64_fn* sqlite3_column_int64_I = NULL;
PH_CALLBACK_DECLARE(PhInformerCallback);
VOID PhpInformerGetKeys(
_In_ PKPH_MESSAGE Message,
_Out_ PULONG64 ActorKey,
_Out_ PULONG64 TargetKey
)
{
*ActorKey = ULONG64_MAX;
*TargetKey = ULONG64_MAX;
switch (Message->Header.MessageId)
{
case KphMsgProcessCreate:
*ActorKey = Message->Kernel.ProcessCreate.CreatingProcessStartKey;
*TargetKey = Message->Kernel.ProcessCreate.TargetProcessStartKey;
return;
case KphMsgProcessExit:
*ActorKey = Message->Kernel.ProcessExit.ProcessStartKey;
return;
case KphMsgThreadCreate:
*ActorKey = Message->Kernel.ThreadCreate.CreatingProcessStartKey;
*TargetKey = Message->Kernel.ThreadCreate.TargetProcessStartKey;
return;
case KphMsgThreadExecute:
*ActorKey = Message->Kernel.ThreadExecute.ProcessStartKey;
return;
case KphMsgThreadExit:
*ActorKey = Message->Kernel.ThreadExit.ProcessStartKey;
return;
case KphMsgImageLoad:
*ActorKey = Message->Kernel.ImageLoad.LoadingProcessStartKey;
*TargetKey = Message->Kernel.ImageLoad.TargetProcessStartKey;
return;
case KphMsgImageVerify:
*ActorKey = Message->Kernel.ImageVerify.ProcessStartKey;
return;
case KphMsgDebugPrint:
return;
default:
break;
}
if (Message->Header.MessageId >= KphMsgHandlePreCreateProcess &&
Message->Header.MessageId <= KphMsgHandlePostDuplicateDesktop)
{
*ActorKey = Message->Kernel.Handle.ContextProcessStartKey;
return;
}
if (Message->Header.MessageId >= KphMsgFilePreCreate &&
Message->Header.MessageId <= KphMsgFilePostVolumeDismount)
{
*ActorKey = Message->Kernel.File.ProcessStartKey;
return;
}
if (Message->Header.MessageId >= KphMsgRegPreDeleteKey &&
Message->Header.MessageId <= KphMsgRegPostSaveMergedKey)
{
*ActorKey = Message->Kernel.Reg.ProcessStartKey;
return;
}
}
ULONG64 PhpInformerDatabaseQueryCountUnsafe(
VOID
)
{
ULONG64 count = 0;
if (sqlite3_step_I(PhpInformerDBQueryCount) == SQLITE_ROW)
count = sqlite3_column_int64_I(PhpInformerDBQueryCount, 0);
sqlite3_reset_I(PhpInformerDBQueryCount);
return count;
}
VOID PhpInformerDatabaseInsert(
_In_ PKPH_MESSAGE Message
)
{
if (PhpInformerDBInsert)
{
ULONG64 actorKey;
ULONG64 targetKey;
PhpInformerGetKeys(Message, &actorKey, &targetKey);
PhAcquireQueuedLockExclusive(&PhpInformerDatabaseLock);
sqlite3_bind_int64_I(PhpInformerDBInsert, 1, Message->Header.TimeStamp.QuadPart);
sqlite3_bind_int64_I(PhpInformerDBInsert, 2, actorKey);
sqlite3_bind_int64_I(PhpInformerDBInsert, 3, targetKey);
sqlite3_bind_int64_I(PhpInformerDBInsert, 4, (LONG64)(LONG_PTR)Message);
sqlite3_step_I(PhpInformerDBInsert);
sqlite3_reset_I(PhpInformerDBInsert);
PhReleaseQueuedLockExclusive(&PhpInformerDatabaseLock);
}
}
VOID PhpInformerDatabaseReap(
_In_ PPH_INFORMER_DB_REAP Reap
)
{
LARGE_INTEGER systemTime;
PhAcquireQueuedLockExclusive(&PhpInformerDatabaseLock);
if (PhpInformerDBReapProc && Reap->ProcessStartKey)
{
sqlite3_bind_int64_I(PhpInformerDBReapProc, 1, Reap->ProcessStartKey);
sqlite3_step_I(PhpInformerDBReapProc);
sqlite3_reset_I(PhpInformerDBReapProc);
}
if (PhpInformerDBReapTime && Reap->Seconds)
{
KphMsgQuerySystemTime(&systemTime);
systemTime.QuadPart -= (LONG64)Reap->Seconds * 10000000;
sqlite3_bind_int64_I(PhpInformerDBReapTime, 1, systemTime.QuadPart);
sqlite3_step_I(PhpInformerDBReapTime);
sqlite3_reset_I(PhpInformerDBReapTime);
}
if (PhpInformerDBReapMax && Reap->MaxCount)
{
if (PhpInformerDatabaseQueryCountUnsafe() > Reap->MaxCount)
{
sqlite3_bind_int64_I(PhpInformerDBReapMax, 1, Reap->MaxCount);
sqlite3_step_I(PhpInformerDBReapMax);
sqlite3_reset_I(PhpInformerDBReapMax);
}
}
PhReleaseQueuedLockExclusive(&PhpInformerDatabaseLock);
}
PPH_LIST PhInformerDatabaseQuery(
_In_ ULONG64 ProcessStartKey,
_In_opt_ PLARGE_INTEGER TimeStamp
)
{
LARGE_INTEGER timeStamp;
PPH_LIST messages;
messages = PhCreateList(10);
if (PhpInformerDBQuery && PhpInformerDBQueryProc)
{
if (TimeStamp)
{
timeStamp.QuadPart = TimeStamp->QuadPart;
}
else
{
timeStamp.QuadPart = 0;
}
PhAcquireQueuedLockExclusive(&PhpInformerDatabaseLock);
if (ProcessStartKey)
{
sqlite3_bind_int64_I(PhpInformerDBQueryProc, 1, ProcessStartKey);
sqlite3_bind_int64_I(PhpInformerDBQueryProc, 2, ProcessStartKey);
sqlite3_bind_int64_I(PhpInformerDBQueryProc, 3, timeStamp.QuadPart);
while (sqlite3_step_I(PhpInformerDBQueryProc) == SQLITE_ROW)
{
PKPH_MESSAGE message;
message = (PVOID)(LONG_PTR)sqlite3_column_int64_I(PhpInformerDBQueryProc, 0);
PhAddItemList(messages, PhReferenceObject(message));
}
sqlite3_reset_I(PhpInformerDBQueryProc);
}
else
{
sqlite3_bind_int64_I(PhpInformerDBQuery, 1, timeStamp.QuadPart);
while (sqlite3_step_I(PhpInformerDBQuery) == SQLITE_ROW)
{
PKPH_MESSAGE message;
message = (PVOID)(LONG_PTR)sqlite3_column_int64_I(PhpInformerDBQuery, 0);
PhAddItemList(messages, PhReferenceObject(message));
}
sqlite3_reset_I(PhpInformerDBQuery);
}
PhReleaseQueuedLockExclusive(&PhpInformerDatabaseLock);
}
return messages;
}
VOID PhpInformerDatabaseReferenceMessage(
sqlite3_context* Context,
INT Argc,
sqlite3_value** Argv
)
{
PKPH_MESSAGE message;
message = (PVOID)(LONG_PTR)sqlite3_value_int64_I(Argv[0]);
PhReferenceObject(message);
}
VOID PhpInformerDatabaseDereferenceMessage(
sqlite3_context* Context,
INT Argc,
sqlite3_value** Argv
)
{
PKPH_MESSAGE message;
message = (PVOID)(LONG_PTR)sqlite3_value_int64_I(Argv[0]);
PhDereferenceObject(message);
}
BOOLEAN PhpInfomerLoadSQLite(
VOID
)
{
PVOID baseAddress;
if (baseAddress = PhLoadLibrary(L"winsqlite3.dll"))
{
sqlite3_open_v2_I = PhGetDllBaseProcedureAddress(baseAddress, "sqlite3_open_v2", 0);
sqlite3_exec_I = PhGetDllBaseProcedureAddress(baseAddress, "sqlite3_exec", 0);
sqlite3_create_function_v2_I = PhGetDllBaseProcedureAddress(baseAddress, "sqlite3_create_function_v2", 0);
sqlite3_prepare_v2_I = PhGetDllBaseProcedureAddress(baseAddress, "sqlite3_prepare_v2", 0);
sqlite3_bind_int64_I = PhGetDllBaseProcedureAddress(baseAddress, "sqlite3_bind_int64", 0);
sqlite3_step_I = PhGetDllBaseProcedureAddress(baseAddress, "sqlite3_step", 0);
sqlite3_reset_I = PhGetDllBaseProcedureAddress(baseAddress, "sqlite3_reset", 0);
sqlite3_value_int64_I = PhGetDllBaseProcedureAddress(baseAddress, "sqlite3_value_int64", 0);
sqlite3_column_int64_I = PhGetDllBaseProcedureAddress(baseAddress, "sqlite3_column_int64", 0);
}
return (
sqlite3_open_v2_I &&
sqlite3_exec_I &&
sqlite3_create_function_v2_I &&
sqlite3_prepare_v2_I &&
sqlite3_bind_int64_I &&
sqlite3_step_I &&
sqlite3_reset_I &&
sqlite3_value_int64_I &&
sqlite3_column_int64_I
);
}
VOID PhpInitializeInformerDatabase(
VOID
)
{
if (!PhpInfomerLoadSQLite())
return;
if (sqlite3_open_v2_I(
"informer",
&PhpInformerDB,
SQLITE_OPEN_MEMORY | SQLITE_OPEN_READWRITE | SQLITE_OPEN_NOMUTEX,
NULL
) == SQLITE_OK)
{
if (sqlite3_exec_I(
PhpInformerDB,
"PRAGMA synchronous = OFF;"
"PRAGMA journal_mode = OFF;"
"DROP TABLE IF EXISTS messages;"
"CREATE TABLE messages("
"id INTEGER PRIMARY KEY AUTOINCREMENT,"
"time_stamp INTEGER NOT NULL,"
"actor_key INTEGER NOT NULL,"
"target_key INTEGER NOT NULL,"
"message INTEGER NOT NULL"
");",
NULL,
NULL,
NULL
) == SQLITE_OK)
{
sqlite3_create_function_v2_I(
PhpInformerDB,
"reference_message",
1,
SQLITE_UTF8,
NULL,
PhpInformerDatabaseReferenceMessage,
NULL,
NULL,
NULL
);
sqlite3_create_function_v2_I(
PhpInformerDB,
"dereference_message",
1,
SQLITE_UTF8,
NULL,
PhpInformerDatabaseDereferenceMessage,
NULL,
NULL,
NULL
);
sqlite3_exec_I(
PhpInformerDB,
"CREATE TRIGGER before_insert_messages "
"BEFORE INSERT ON messages "
"FOR EACH ROW "
"BEGIN "
" SELECT reference_message(NEW.message); "
"END;",
NULL,
NULL,
NULL
);
sqlite3_exec_I(
PhpInformerDB,
"CREATE TRIGGER after_delete_messages "
"AFTER DELETE ON messages "
"FOR EACH ROW "
"BEGIN "
" SELECT dereference_message(OLD.message); "
"END;",
NULL,
NULL,
NULL
);
sqlite3_prepare_v2_I(
PhpInformerDB,
"INSERT INTO messages(time_stamp, actor_key, target_key, message)"
"VALUES(?, ?, ?, ?);",
-1,
&PhpInformerDBInsert,
NULL
);
sqlite3_prepare_v2_I(
PhpInformerDB,
"SELECT COUNT(*) FROM messages;",
-1,
&PhpInformerDBQueryCount,
NULL
);
sqlite3_prepare_v2_I(
PhpInformerDB,
"DELETE FROM messages "
"WHERE id IN ("
" SELECT id FROM messages "
" ORDER BY id ASC "
" LIMIT (SELECT COUNT(*) - ? FROM messages)"
");",
-1,
&PhpInformerDBReapMax,
NULL
);
sqlite3_prepare_v2_I(
PhpInformerDB,
"DELETE FROM messages "
"WHERE time_stamp < ?;",
-1,
&PhpInformerDBReapTime,
NULL
);
sqlite3_prepare_v2_I(
PhpInformerDB,
"DELETE FROM messages "
"WHERE actor_key = ?;",
-1,
&PhpInformerDBReapProc,
NULL
);
sqlite3_prepare_v2_I(
PhpInformerDB,
"SELECT message FROM messages "
"WHERE time_stamp >= ? "
"ORDER BY time_stamp ASC;",
-1,
&PhpInformerDBQuery,
NULL
);
sqlite3_prepare_v2_I(
PhpInformerDB,
"SELECT message FROM messages "
"WHERE (actor_key = ? OR target_key = ?) AND time_stamp >= ? "
"ORDER BY time_stamp ASC;",
-1,
&PhpInformerDBQueryProc,
NULL
);
}
}
}
NTSTATUS PhInformerReply(
_Inout_ PPH_INFORMER_CONTEXT Context,
_In_ PKPH_MESSAGE ReplyMessage
)
{
NTSTATUS status;
if (Context->Handled)
return STATUS_INVALID_PARAMETER_1;
if (NT_SUCCESS(status = KphCommsReplyMessage(Context->ReplyToken, ReplyMessage)))
Context->Handled = TRUE;
return status;
}
BOOLEAN PhInformerDispatch(
_In_ ULONG_PTR ReplyToken,
_In_ PCKPH_MESSAGE Message
)
{
PKPH_MESSAGE message;
PH_INFORMER_CONTEXT context;
message = KphCreateMessage(Message->Header.Size);
memcpy(message, Message, Message->Header.Size);
context.Message = message;
context.ReplyToken = ReplyToken;
context.Handled = FALSE;
PhpInformerDatabaseInsert(message);
PhInvokeCallback(&PhInformerCallback, &context);
PhDereferenceObject(message);
return context.Handled;
}
_Function_class_(USER_THREAD_START_ROUTINE)
NTSTATUS NTAPI PhpInformerReapWorkItemRoutine(
_In_ PVOID Parameter
)
{
PPH_INFORMER_DB_REAP reap = Parameter;
PhpInformerDatabaseReap(reap);
PhFreeToFreeList(&PhpInformerReapFreeList, reap);
return STATUS_SUCCESS;
}
_Function_class_(PH_CALLBACK_FUNCTION)
VOID NTAPI PhpInformerProcessUpdatedHandler(
_In_ PVOID Parameter,
_In_ PVOID Context
)
{
ULONG runCount = PtrToUlong(Parameter);
PPH_INFORMER_DB_REAP reap;
reap = PhAllocateFromFreeList(&PhpInformerReapFreeList);
//
// We cache the process monitor data for a lookback period so when a user
// desires to inspect real time activity for a process we can provide a
// reasonable amount of historical data. Here, reap the information that has
// expired from the configured lookback period. We also configure a cache
// limit which enforces a maximum on the total number of retained messages.
//
reap->ProcessStartKey = 0;
reap->MaxCount = PhProcessMonitorCacheLimit;
reap->Seconds = 0;
if (PhProcessMonitorLookback && runCount % PhProcessMonitorLookback == 0)
reap->Seconds = PhProcessMonitorLookback;
if (!NT_SUCCESS(PhQueueUserWorkItem(PhpInformerReapWorkItemRoutine, reap)))
PhFreeToFreeList(&PhpInformerReapFreeList, reap);
}
_Function_class_(PH_CALLBACK_FUNCTION)
VOID NTAPI PhpInformerProcessRemovedHandler(
_In_ PVOID Parameter,
_In_ PVOID Context
)
{
PPH_PROCESS_ITEM processItem = (PPH_PROCESS_ITEM)Parameter;
PPH_INFORMER_DB_REAP reap;
if (processItem->ProcessStartKey)
{
//
// This is opportunistic since the provider does not track short-lived
// processes. There is also no guarantee that more information about
// this process will be added to the database later by the asynchronous
// nature of processing informer data and the fact that the operating
// system generates activity for a process after it has "exited". That
// said, we know at this point that the user can no longer inspect the
// process to view activity associated with it so we might as well ask
// the database to reap what it can. Anything missed will be reaped by
// the periodic lookback reaper.
//
reap = PhAllocateFromFreeList(&PhpInformerReapFreeList);
reap->ProcessStartKey = processItem->ProcessStartKey;
reap->MaxCount = 0;
reap->Seconds = 0;
if (!NT_SUCCESS(PhQueueUserWorkItem(PhpInformerReapWorkItemRoutine, reap)))
PhFreeToFreeList(&PhpInformerReapFreeList, reap);
}
}
VOID PhInformerInitialize(
VOID
)
{
PhInitializeFreeList(&PhpInformerReapFreeList, sizeof(PH_INFORMER_DB_REAP), 16);
if (!PhEnableProcessMonitor)
return;
PhpInitializeInformerDatabase();
PhRegisterCallback(
PhGetGeneralCallback(GeneralCallbackProcessProviderUpdatedEvent),
PhpInformerProcessUpdatedHandler,
NULL,
&PhpInformerProcessesUpdatedRegistration
);
PhRegisterCallback(
PhGetGeneralCallback(GeneralCallbackProcessProviderRemovedEvent),
PhpInformerProcessRemovedHandler,
NULL,
&PhpInformerProcessesRemovedRegistration
);
}
VOID PhInformerActivate(
VOID
)
{
KPH_INFORMER_SETTINGS settings;
KPH_INFORMER_CLIENT_SETTINGS client;
memset(&settings, 0, sizeof(settings));
settings.Policy[KPH_INFORMER_INDEX(RequiredStateFailure)] = (KPH_RATE_LIMIT_POLICY)KPH_RATE_LIMIT_UNLIMITED;
KphSetInformerProcessSettings(NtCurrentProcess(), &settings);
if (!PhEnableProcessMonitor)
{
KphSetInformerSettings(&settings);
return;
}
memset(&settings, 0, sizeof(settings));
settings.Options.Flags = ULONG_MAX;
settings.Options.EnableStackTraces = FALSE;
settings.Options.EnableProcessCreateReply = FALSE;
settings.Options.FileEnablePreCreateReply = FALSE;
settings.Options.FileEnablePostCreateReply = FALSE;
settings.Options.FileEnablePostFileNames = FALSE;
settings.Options.FileEnableIoControlBuffers = FALSE;
settings.Options.FileEnableFsControlBuffers = FALSE;
settings.Options.FileEnableDirControlBuffers = FALSE;
settings.Options.RegEnablePostObjectNames = FALSE;
settings.Options.RegEnablePostValueNames = FALSE;
settings.Options.RegEnableValueBuffers = FALSE;
for (ULONG i = 0; i < KPH_INFORMER_COUNT; i++)
settings.Policy[i] = (KPH_RATE_LIMIT_POLICY)KPH_RATE_LIMIT_UNLIMITED;
settings.Policy[KPH_INFORMER_INDEX(DebugPrint)] = (KPH_RATE_LIMIT_POLICY)KPH_RATE_LIMIT_DENY_ALL;
KphSetInformerSettings(&settings);
memset(&client, 0, sizeof(client));
PhTimeoutFromMilliseconds(&client.MessageTimeouts.AsyncTimeout, 3000);
PhTimeoutFromMilliseconds(&client.MessageTimeouts.DefaultTimeout, 3000);
PhTimeoutFromMilliseconds(&client.MessageTimeouts.ProcessCreateTimeout, 3000);
PhTimeoutFromMilliseconds(&client.MessageTimeouts.FilePreCreateTimeout, 3000);
PhTimeoutFromMilliseconds(&client.MessageTimeouts.FilePostCreateTimeout, 3000);
//client.AsyncQueuePolicy = (KPH_RATE_LIMIT_POLICY)KPH_RATE_LIMIT_PER_SEC(1000, 30000);
client.AsyncQueuePolicy = (KPH_RATE_LIMIT_POLICY)KPH_RATE_LIMIT_DENY_ALL;
for (ULONG i = 0; i < KPH_INFORMER_COUNT; i++)
client.InformerPolicy[i] = (KPH_RATE_LIMIT_POLICY)KPH_RATE_LIMIT_UNLIMITED;
KphSetInformerClientSettings(&client);
}
================================================
FILE: SystemInformer/itemtips.c
================================================
/*
* Copyright (c) 2022 Winsider Seminars & Solutions, Inc. All rights reserved.
*
* This file is part of System Informer.
*
* Authors:
*
* wj32 2010-2015
* dmex 2017-2023
*
*/
#include
#include