From 71d340be5effe2b99d95b686105349972764f061 Mon Sep 17 00:00:00 2001 From: hfiref0x Date: Wed, 22 Jun 2022 09:20:21 +0700 Subject: [PATCH] 2.0.0 added entirely new handling of object names to support embedded nulls added Pico providers, Nmi, SiloMonitor and Errata manager callbacks added CmControlVector viewer added Copy Name/Copy Name (Binary) commands to the main window popup menus added program statistics (see Help->Statistics) added legend window description for process list added ability to fix image sections for dumped drivers added RegistryTransaction object view and access rights moved "Globals" from about box to the View->System Information and rearranged it output drivers dump operation can now be cancelled fix display of PUNICODE_STRING dump fix ALPC Port type objects sometimes unable to open while they can be opened plugin sdk updated to accommodate new named objects handling imagescope plugin updated to accomodate plugin sdk changes elevation required features in "extras" will now request elevation instead of just been disabled help file updated with drivers and symbols usage internal rearrange and minor UI changes (release candidate 1) --- Compiled/WHATSNEW_200.md | 26 + Compiled/WinObjEx64.exe | Bin 722944 -> 738816 bytes Compiled/plugins/ApiSetView.dll | Bin 131584 -> 131584 bytes Compiled/plugins/ExamplePlugin.dll | Bin 94720 -> 94720 bytes Compiled/plugins/ImageScope.dll | Bin 142336 -> 142336 bytes Compiled/plugins/Sonar.dll | Bin 146944 -> 146944 bytes LICENSE.md | 2 +- README.md | 6 +- Screenshots/CmControlVector.png | Bin 0 -> 46958 bytes Source/CHANGELOG.txt | 18 +- Source/FILELIST.txt | 57 +- Source/Plugins/ApiSetView/ui.h | 9 +- Source/Plugins/ImageScope/Resource.rc | Bin 7748 -> 7748 bytes Source/Plugins/ImageScope/main.c | 77 +- Source/Plugins/ImageScope/sup.c | 64 +- Source/Plugins/ImageScope/sup.h | 18 +- Source/Plugins/ImageScope/ui.c | 34 +- Source/Plugins/ImageScope/ui.h | 9 +- Source/Plugins/Sonar/ui.h | 9 +- Source/Plugins/plugin_def.h | 17 +- Source/Shared/ntos/ntos.h | 15 +- Source/Shared/ntos/ntsup.c | 184 +- Source/Shared/ntos/ntsup.h | 23 +- Source/Shared/sdk/extdef.h | 4 +- Source/Shared/treelist/treelist.c | 51 +- Source/Shared/treelist/treelist.h | 9 +- Source/WinObjEx64/Resource.rc | Bin 164904 -> 171902 bytes Source/WinObjEx64/WinObjEx64.vcxproj | 38 +- Source/WinObjEx64/WinObjEx64.vcxproj.filters | 111 +- Source/WinObjEx64/aboutDlg.c | 180 +- Source/WinObjEx64/aboutDlg.h | 25 - Source/WinObjEx64/drivers/winio.c | 4 +- Source/WinObjEx64/drivers/winio.h | 4 +- Source/WinObjEx64/excepth.c | 88 +- Source/WinObjEx64/excepth.h | 9 +- Source/WinObjEx64/extapi.c | 22 +- Source/WinObjEx64/extapi.h | 16 +- Source/WinObjEx64/extras/extras.c | 16 +- Source/WinObjEx64/extras/extras.h | 4 +- Source/WinObjEx64/extras/extrasCallbacks.c | 42 +- .../extras/extrasCallbacksPatterns.h | 4 +- Source/WinObjEx64/extras/extrasCmOpt.c | 16 +- Source/WinObjEx64/extras/extrasCmOpt.h | 23 - Source/WinObjEx64/extras/extrasDrivers.c | 379 ++- Source/WinObjEx64/extras/extrasDrivers.h | 22 - .../extras/{extrasSSDT.h => extrasHandlers.h} | 33 +- Source/WinObjEx64/extras/extrasIPC.c | 130 +- Source/WinObjEx64/extras/extrasIPC.h | 22 - Source/WinObjEx64/extras/extrasPN.c | 124 +- Source/WinObjEx64/extras/extrasPN.h | 22 - Source/WinObjEx64/extras/extrasPSList.c | 286 +- Source/WinObjEx64/extras/extrasPSList.h | 22 - Source/WinObjEx64/extras/extrasSL.c | 17 +- Source/WinObjEx64/extras/extrasSL.h | 22 - Source/WinObjEx64/extras/extrasSSDT.c | 2772 +++++++++-------- Source/WinObjEx64/extras/extrasSSDTsup.h | 102 - Source/WinObjEx64/extras/extrasUSD.c | 9 +- Source/WinObjEx64/extras/extrasUSD.h | 22 - Source/WinObjEx64/findDlg.c | 67 +- Source/WinObjEx64/findDlg.h | 22 - Source/WinObjEx64/global.h | 68 +- Source/WinObjEx64/hash.c | 4 +- Source/WinObjEx64/hash.h | 4 +- Source/WinObjEx64/kldbg.c | 859 ++--- Source/WinObjEx64/kldbg.h | 87 +- Source/WinObjEx64/kldbg_patterns.h | 6 +- Source/WinObjEx64/ksymbols.h | 4 +- Source/WinObjEx64/list.c | 601 +++- Source/WinObjEx64/list.h | 43 +- Source/WinObjEx64/log/log.c | 11 +- Source/WinObjEx64/log/log.h | 4 +- Source/WinObjEx64/main.c | 688 ++-- Source/WinObjEx64/msvcver.h | 4 +- Source/WinObjEx64/objects.c | 44 +- Source/WinObjEx64/objects.h | 15 +- Source/WinObjEx64/plugmngr.c | 142 +- Source/WinObjEx64/plugmngr.h | 18 +- Source/WinObjEx64/props/propAlpcPort.c | 10 +- Source/WinObjEx64/props/propAlpcPort.h | 25 - Source/WinObjEx64/props/propBasic.c | 154 +- Source/WinObjEx64/props/propBasic.h | 25 - Source/WinObjEx64/props/propBasicConsts.h | 6 +- Source/WinObjEx64/props/propCommon.h | 118 + Source/WinObjEx64/props/propDesktop.c | 66 +- Source/WinObjEx64/props/propDesktop.h | 25 - Source/WinObjEx64/props/propDlg.c | 599 ++-- Source/WinObjEx64/props/propDlg.h | 28 +- Source/WinObjEx64/props/propDriver.c | 21 +- Source/WinObjEx64/props/propDriver.h | 25 - Source/WinObjEx64/props/propObjectDump.c | 267 +- .../WinObjEx64/props/propObjectDumpConsts.h | 8 +- Source/WinObjEx64/props/propProcess.c | 12 +- Source/WinObjEx64/props/propProcess.h | 25 - Source/WinObjEx64/props/propSection.c | 14 +- Source/WinObjEx64/props/propSection.h | 25 - Source/WinObjEx64/props/propSecurity.c | 186 +- Source/WinObjEx64/props/propSecurity.h | 126 - Source/WinObjEx64/props/propSecurityConsts.h | 240 +- Source/WinObjEx64/props/propToken.c | 41 +- Source/WinObjEx64/props/propToken.h | 25 - Source/WinObjEx64/props/propType.c | 149 +- Source/WinObjEx64/props/propType.h | 25 - Source/WinObjEx64/props/propTypeConsts.h | 4 +- .../props/{propObjectDump.h => props.h} | 75 +- Source/WinObjEx64/resource.h | Bin 40864 -> 42908 bytes Source/WinObjEx64/sdviewDlg.c | 271 +- Source/WinObjEx64/sdviewDlg.h | 25 - Source/WinObjEx64/{ => sup}/sup.c | 1847 +++++++---- Source/WinObjEx64/{ => sup}/sup.h | 351 ++- Source/WinObjEx64/sup/sync.c | 195 ++ Source/WinObjEx64/{ => sup}/wine.c | 30 +- .../{extras/extrasCallbacks.h => sup/wine.h} | 12 +- Source/WinObjEx64/supConsts.h | 50 - Source/WinObjEx64/symparser.c | 34 +- Source/WinObjEx64/sysinfoDlg.c | 70 +- Source/WinObjEx64/sysinfoDlg.h | 23 - Source/WinObjEx64/tests/testunit.c | 315 +- Source/WinObjEx64/tests/testunit.h | 4 +- Source/WinObjEx64/ui.h | 131 +- Source/WinObjEx64/wine.h | 25 - Source/WinObjEx64/winedebug.h | 23 - WinObjEx64.sha256 | 210 +- 122 files changed, 7673 insertions(+), 6176 deletions(-) create mode 100644 Compiled/WHATSNEW_200.md create mode 100644 Screenshots/CmControlVector.png delete mode 100644 Source/WinObjEx64/aboutDlg.h delete mode 100644 Source/WinObjEx64/extras/extrasCmOpt.h delete mode 100644 Source/WinObjEx64/extras/extrasDrivers.h rename Source/WinObjEx64/extras/{extrasSSDT.h => extrasHandlers.h} (50%) delete mode 100644 Source/WinObjEx64/extras/extrasIPC.h delete mode 100644 Source/WinObjEx64/extras/extrasPN.h delete mode 100644 Source/WinObjEx64/extras/extrasPSList.h delete mode 100644 Source/WinObjEx64/extras/extrasSL.h delete mode 100644 Source/WinObjEx64/extras/extrasSSDTsup.h delete mode 100644 Source/WinObjEx64/extras/extrasUSD.h delete mode 100644 Source/WinObjEx64/findDlg.h delete mode 100644 Source/WinObjEx64/props/propAlpcPort.h delete mode 100644 Source/WinObjEx64/props/propBasic.h create mode 100644 Source/WinObjEx64/props/propCommon.h delete mode 100644 Source/WinObjEx64/props/propDesktop.h delete mode 100644 Source/WinObjEx64/props/propDriver.h delete mode 100644 Source/WinObjEx64/props/propProcess.h delete mode 100644 Source/WinObjEx64/props/propSection.h delete mode 100644 Source/WinObjEx64/props/propSecurity.h delete mode 100644 Source/WinObjEx64/props/propToken.h delete mode 100644 Source/WinObjEx64/props/propType.h rename Source/WinObjEx64/props/{propObjectDump.h => props.h} (64%) delete mode 100644 Source/WinObjEx64/sdviewDlg.h rename Source/WinObjEx64/{ => sup}/sup.c (85%) rename Source/WinObjEx64/{ => sup}/sup.h (81%) create mode 100644 Source/WinObjEx64/sup/sync.c rename Source/WinObjEx64/{ => sup}/wine.c (79%) rename Source/WinObjEx64/{extras/extrasCallbacks.h => sup/wine.h} (72%) delete mode 100644 Source/WinObjEx64/supConsts.h delete mode 100644 Source/WinObjEx64/sysinfoDlg.h delete mode 100644 Source/WinObjEx64/wine.h delete mode 100644 Source/WinObjEx64/winedebug.h diff --git a/Compiled/WHATSNEW_200.md b/Compiled/WHATSNEW_200.md new file mode 100644 index 00000000..3b1e47fc --- /dev/null +++ b/Compiled/WHATSNEW_200.md @@ -0,0 +1,26 @@ + +## What is new in 2.0.0 + + - **CmControlVector viewer** + + + +View contents of CmControlVector ntoskrnl parameters array. Can display actual values of variables or dump them when driver support is enabled. + + - **Other** + + Added entirely new handling of object names to support embedded nulls + + Added Pico providers, Nmi, SiloMonitor and Errata manager callbacks + + Added Copy Name/Copy Name (Binary) commands to the main window popup menus + + Added program statistics (see Help->Statistics) + + Added legend window description for process list + + Added ability to fix image sections for dumped drivers + + Added RegistryTransaction object view and access rights + + Moved "Globals" from about box to the View->System Information and rearranged it output + + Drivers dump operation can now be cancelled + + Fix display of PUNICODE_STRING dump + + Fix ALPC Port type objects sometimes unable to open while they can be opened + + Plugin sdk updated to accommodate new named objects handling + + Imagescope plugin updated to accomodate plugin sdk changes + + Elevation required features in "extras" will now request elevation instead of just been disabled + + Help file updated with drivers and symbols usage + + Internal rearrange and minor UI changes diff --git a/Compiled/WinObjEx64.exe b/Compiled/WinObjEx64.exe index 1eb008f2a0fb56218406354a7753b8b03daf4aab..991d5d4ce3254bf188e01e3f857c8bc7f1cb1cd6 100644 GIT binary patch delta 260592 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

eA*+g4;W<= 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

vg3*7p(;3ELdp*!Fa6|hN# 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 b14iIOU@R~c=mW&1;JJHme$&V7q5A&;_|}fZ diff --git a/Compiled/plugins/ExamplePlugin.dll b/Compiled/plugins/ExamplePlugin.dll index bb9d92fc4e7878e9d058428fe4008851b4ae0893..270adc5f8ac1452663eb3d3aa4c583846d1dad7b 100644 GIT binary patch delta 14751 zcmZ{K3tY|j|NrNG>JT|jrBg0lP85YmM?#_~J`rLrMegLjg^oLwQ^>~|^JckiWA2w( zNXcgr<5&Reim19E$UbGJ>~bZB?`D@UiSDOXiJK{h5F6WL0}r>Twk; zM6f@w!j&agD_0VgbBlSoJAsvKt1kw>m#~~eu^k}3D7EVDevMX3n$FOPS?X4Nn7Vc9(>y_t3PdT^x1(sdXc=V(6H{Tt@Pdmq zf1>HJzg5={Wu+^4XI%#-SasPbCYr*}OlE?h&5+O?#~}$PEhxWCeFAD!^f}0?t0&j0 zTZcrr3PwgRc=v8h<=ZuXsX7LCrXLF5$O1!Q3ckLEOl#SSjMRZVa1K*QNDUp_{n%CsEnbOIjF

do-|1N>P|wS?Z6_NYPBP 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 delta 14740 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 NFDtB!?53U*{}0z+;<5k$ delta 34226 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 E1Gc_YbpQYW diff --git a/Compiled/plugins/Sonar.dll b/Compiled/plugins/Sonar.dll index 28e4d3bf6c84af07c5a6adb8e9d5bb43c30f239d..816db16f99b296794c1729e0d8ac747a4ded7243 100644 GIT binary patch delta 18316 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 EABE~Uq5uE@ diff --git a/LICENSE.md b/LICENSE.md index ce28fc41..b25f53a4 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -1,4 +1,4 @@ -Copyright (c) 2015 - 2022, WinObjEx64 authors +Copyright (c) 2015 - 2022, WinObjEx64 Project Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: diff --git a/README.md b/README.md index cf431101..2ae49beb 100644 --- a/README.md +++ b/README.md @@ -177,9 +177,9 @@ WinObjEx64 works only on the following x64 Windows: Windows 7, Windows 8, Window - Jump to service entry module - Export list to file in CSV format -- CmControlVector viewer1 +- CmControlVector viewer - Show dump of Ntoskrnl CmControlVector array - - Dump value data from kernel memory to file + - Dump value data from kernel memory to file1 - Export list to file in CSV format - Most of list/trees allows to copy object address and/or name to the clipboard @@ -234,7 +234,7 @@ In order to build from source you need Microsoft Visual Studio 2015 and later ve # What is new -[Whats New in 1.9.0](https://github.com/hfiref0x/WinObjEx64/blob/master/Compiled/WHATSNEW_190.md) +[Whats New in 2.0.0](https://github.com/hfiref0x/WinObjEx64/blob/master/Compiled/WHATSNEW_200.md) [Complete changelog](https://github.com/hfiref0x/WinObjEx64/blob/master/Source/CHANGELOG.txt) diff --git a/Screenshots/CmControlVector.png b/Screenshots/CmControlVector.png new file mode 100644 index 0000000000000000000000000000000000000000..41120e90db80db3996563159df14b14d8d44c7c2 GIT binary patch literal 46958 zcmdSBWmH^Sx-LwBK(Ih?x8NE)cyM?3;O_1rxI+=#-66P3f;+)ogF^`J)U6_Wr}sYH zeY)>A#`oink&L8RwW{WtYp(ZsADM*8%ZeevzI6^?Y>;nIN)@NH}1Od?i zB_S-J?52D4;QP5<&viDC*IYcJh$R#TGKly!`3Iui_ty$qhGa}~kZ{*LS+L)c-5_&{ z`Kn~9UQe1IdC8Z(-V&pqlAKCbk9|p0oSXIfg>~mPY|@8k?NkB98&mHJrKz785sUA7 z9@SkgSOOa_qYv6gn=0>`XDvssxI8P{W)?t)jt?|f@6yJ+`7R^73;9lr^)Ajj_*kFE zCuLUxZ}cvVW97VPLR5(zxus@nR2Y!dI>}zu7^U2zWyWykwi1~;bVKgp6{00kL&wjN zOeUC*18?GwV$ATbOEQ7f%VNt4-?FF(e(kH15GV$cu%Us_>rI0@(nefAl+j6vW-eeM=T z*tbEp*D!mWpp&9Dd2px#_%8S4d^J6uUcZ%tfzOWiK8?%#w8Fj$1m0)xUsVDt#Kn0e`A_pI_Zy^IseK^t<|eF+H1geF*8;ZJy7aL0Q_?Xhna$l`l-<-1;a8&!Gd3QRKN1`owigl|UV z(+afomCovg&IHrDMo)5ogWc%IvX0{x-~H&D2mbFYCl{H%8$-Zp#_KN~>yALr>)XDL z?Zo^3)%(PbOV|5&qDw}vJ1k(zDv;NAn=w=%HF~83q2>8X8&Eue<&a%_t4k{BT|4z| zhgZzy!Mj%z=t!R zDQIcRE$e2g=nC0TH3hUa*GKlrboq)xNs7l~G~q22iZP%|rST6x=B2RMgm^F(U9DTY z)wsWMOCvtgaHOSkU1iXy*J#_shmftPrQs5)d={25c8kYXSQMP{)g7j1+|_pr_!HS@ z54fh~yKB0Bfb2bz_&Ah#mwbI&mkIRuy|28>V7%Y%IIjVAb=(WB_%5#AFg~^ekAO#V zVFEIZPz|@^lyRM+7-rhG?FA!0noyH-Sx1j%aD_{OKus4-%tV~!w zRCY9OAznRwSlc?lLr7DfeJbFqFDHQ%7oK7TZ0*%LTfds;L96p2LC)!RuO?teZLUIR zs6b<<9N+X^;l>_f8>+~jqaisoXyu#pKbT5XZg6)T9{0{3KYZ=jyYl*C>boNc1n8Z) z^S{vRSThC6`Oewi+n!tutX?Jp>w(h^kNtWl1bTOs59`3&u}9;ofzPh7!)=2eyKajdtOL!k&@o5yEMW^rst(yW-KhxFFjgFOHJEafnx-1tF{j}d zWAjX$km>QJqY=VbugHkXxJ%a(uN#M3|^Q$%R|DMdyXO0@ssF*GGFaP2ekIvFOF|oe8#YV zpI1);GtVsfZvy#`E&0!|9-%THTptHlFK#*>8P^UheGl|*mwdPRZ*qJu7duX8fu_KT z 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 R<_!2HB_=0&PuRfc{{VVGMW_G( literal 0 HcmV?d00001 diff --git a/Source/CHANGELOG.txt b/Source/CHANGELOG.txt index 64185eb5..84f5f824 100644 --- a/Source/CHANGELOG.txt +++ b/Source/CHANGELOG.txt @@ -1,7 +1,21 @@ -v1.9.4 +v2.0.0 +added entirely new handling of object names to support embedded nulls added Pico providers, Nmi, SiloMonitor and Errata manager callbacks added CmControlVector viewer -internal rearrange +added Copy Name/Copy Name (Binary) commands to the main window popup menus +added program statistics (see Help->Statistics) +added legend window description for process list +added ability to fix image sections for dumped drivers +added RegistryTransaction object view and access rights +moved "Globals" from about box to the View->System Information and rearranged it output +drivers dump operation can now be cancelled +fix display of PUNICODE_STRING dump +fix ALPC Port type objects sometimes unable to open while they can be opened +plugin sdk updated to accommodate new named objects handling +imagescope plugin updated to accomodate plugin sdk changes +elevation required features in "extras" will now request elevation instead of just been disabled +help file updated with drivers and symbols usage +internal rearrange and minor UI changes v1.9.3 updated SeCiCallbacks search for newest Windows versions diff --git a/Source/FILELIST.txt b/Source/FILELIST.txt index 388f2dcc..77acda99 100644 --- a/Source/FILELIST.txt +++ b/Source/FILELIST.txt @@ -16,11 +16,9 @@ winobjex64\drivers\alice.h * About dialog routines including window dialog procedure * winobjex64\aboutDlg.c -winobjex64\aboutDlg.h * System information dialog routines including window dialog procedure * winobjex64\sysinfoDlg.c -winobjex64\sysinfoDlg.h * Custom access violation exception handler including minidump * winobjex64\excepth.c @@ -29,6 +27,7 @@ winobjex64\excepth.h * Extras menu handler * winobjex64\extras\extras.c winobjex64\extras\extras.h +winobjex64\extras\extrasHandlers.h * Windows 7/8/8.1 missing API support * winobjex64\extapi.c @@ -36,45 +35,34 @@ winobjex64\extapi.h * Windows kernel callbacks list * winobjex64\extas\extrasCallbacks.c -winobjex64\extras\extrasCallbacks.h winobjex64\extras\extrasCallbacksPatterns.h * Drivers list * winobjex64\extras\extrasDrivers.c -winobjex64\extras\extrasDrivers.h * KiServiceTable/W32pServiceTable list * winobjex64\extras\extrasSSDT.c -winobjex64\extras\extrasSSDT.h -winobjex64\extras\extrasSSDTsup.h * Pipes and mailslots dialog * winobjex64\extras\extrasIPC.c -winobjex64\extras\extrasIPC.h * Windows Private Namespaces dialog * winobjex64\extras\extrasPN.c -winobjex64\extras\extrasPN.h * Process list dialog * winobjex64\extras\extrasPSList.c -winobjex64\extras\extrasPSList.h * Software Licensing Cache dialog * winobjex64\extras\extrasSL.c -winobjex64\extras\extrasSL.h * UserSharedData dialog * winobjex64\extras\extrasUSD.c -winobjex64\extras\extrasUSD.h * CmControlVector dialog * winobjex64\extras\extrasCmOpt.c -winobjex64\extras\extrasCmOpt.h * Find Object routines including window dialog procedure * winobjex64\findDlg.c -winobjex64\findDlg.h * Authenticode hash support * winobjex64\hash.c @@ -122,64 +110,55 @@ winobjex64\symparser.h * Property sheet for ALPC Port information * winobjex64\props\propAlpcPort.c -winobjex64\props\propAlpcPort.h * Property sheet "Basic" handlers, including window procedures and consts * winobjex64\props\propBasic.c -winobjex64\props\propBasic.h winobjex64\props\propBasicConsts.h * Property sheet "Desktop" handlers * winobjex64\props\propDesktop.c -winobjex64\props\propDesktop.h * "Properties" property sheet creation and window procedures, all sheets created here * winobjex64\props\propDlg.c winobjex64\props\propDlg.h +winobjex64\props\propCommon.h +winobjex64\props\props.h -* Dumping and decoding kernel objects for "Object" property sheet * -winobjex64\props\propDriver.c - property sheet "Driver" handlers, including window procedures -winobjex64\props\propDriver.h -winobjex64\props\propObjectDump.c -winobjex64\props\propObjectDump.h +* Property sheet "Driver" handlers * +winobjex64\props\propDriver.c -* Kernel object string converted constants * +* Dumping and decoding kernel objects for "Object" property sheet and constants* +winobjex64\props\propObjectDump.c winobjex64\props\propObjectDumpConsts.h * Property sheet "Process" handler, window procedure * winobjex64\props\propProcess.c -winobjex64\props\propProcess.h * Property sheet for Section object dump information * winobjex64\props\propSection.c -winobjex64\props\propSection.h -* "Security" property sheet handler and ISecurityInformation implementation * +* "Security" property sheet handler and ISecurityInformation implementation and constants * winobjex64\props\propSecurity.c -winobjex64\props\propSecurity.h - -* Object type access values, generic mappings here * winobjex64\props\propSecurityConsts.h * Property sheet "Token" handler, window procedure * winobjex64\props\propToken.c -winobjex64\props\propToken.h -* Property sheet "Type" handlers, including window procedure for "Procedures" sheet * +* Property sheet "Type" handlers, including window procedure for "Procedures" sheet and constants* winobjex64\props\propType.c -winobjex64\props\propType.h - -* Known object access rights converted to strings listed here * winobjex64\props\propTypeConsts.h * "View Security Descriptor" dialog routines including window procedure * winobjex64\sdviewDlg.c -winobjex64\sdviewDlg.h * Support api set and consts * -winobjex64\sup.c -winobjex64\sup.h -winobjex64\supConsts.h +winobjex64\sup\sup.c +winobjex64\sup\sup.h +winobjex64\sup\sync.c + +* Wine support header file * +winobjex64\sup\wine.c +winobjex64\sup\wine.h * All objects test code here * winobjex64\tests\testunit.c @@ -189,10 +168,6 @@ winobjex64\tests\testunit.h shared\treelist\treelist.c shared\treelist\treelist.h -* Wine support header file * -winobjex64\wine.h -winobjex64\wine.c - * Global include file * winobjex64\global.h diff --git a/Source/Plugins/ApiSetView/ui.h b/Source/Plugins/ApiSetView/ui.h index 669dc169..27e1d084 100644 --- a/Source/Plugins/ApiSetView/ui.h +++ b/Source/Plugins/ApiSetView/ui.h @@ -1,12 +1,12 @@ /******************************************************************************* * -* (C) COPYRIGHT AUTHORS, 2019 - 2021 +* (C) COPYRIGHT AUTHORS, 2019 - 2022 * * TITLE: UI.H * -* VERSION: 1.12 +* VERSION: 1.13 * -* DATE: 30 Sep 2021 +* DATE: 10 Jun 2022 * * WinObjEx64 ApiSetView UI constants, definitions and includes. * @@ -52,10 +52,11 @@ typedef struct _GUI_CONTEXT { } GUI_CONTEXT, *PGUI_CONTEXT; typedef struct _TL_SUBITEMS_FIXED { + ULONG Count; ULONG ColorFlags; COLORREF BgColor; COLORREF FontColor; PVOID UserParam; - ULONG Count; + LPTSTR CustomTooltip; LPTSTR Text[2]; } TL_SUBITEMS_FIXED, * PTL_SUBITEMS_FIXED; diff --git a/Source/Plugins/ImageScope/Resource.rc b/Source/Plugins/ImageScope/Resource.rc index 413fd058cb2ff5d252c0ec3a7856016144eaa28d..351da58e90423ae3b0af382999b7f0d585445d0a 100644 GIT binary patch delta 82 zcmX?NbHrvt9|x-;gARkiObjectDirectory) { - - Size = (1 + _strlen(Source->ObjectDirectory)) * sizeof(WCHAR); - - Dest->ObjectDirectory = (LPWSTR)supHeapAlloc(Size); - if (Dest->ObjectDirectory) { - _strcpy(Dest->ObjectDirectory, Source->ObjectDirectory); - } - else { - return; - } - - } - else { - return; - } - - if (Source->ObjectName) { - - Size = (1 + _strlen(Source->ObjectName)) * sizeof(WCHAR); - - Dest->ObjectName = (LPWSTR)supHeapAlloc(Size); - if (Dest->ObjectName) { - _strcpy(Dest->ObjectName, Source->ObjectName); - } - else { - supHeapFree(Dest->ObjectDirectory); - Dest->ObjectDirectory = NULL; - } - - } - else { - supHeapFree(Dest->ObjectDirectory); - Dest->ObjectDirectory = NULL; - } - + HANDLE HeapHandle = NtCurrentPeb()->ProcessHeap; + + return supDuplicateUnicodeString(HeapHandle, &Dest->Directory, &Source->Directory) && + supDuplicateUnicodeString(HeapHandle, &Dest->Name, &Source->Name); } /* @@ -94,14 +60,11 @@ VOID PluginFreeGlobalResources( Context->SectionAddress = NULL; } - if (Context->ParamBlock.Object.ObjectDirectory) { - supHeapFree(Context->ParamBlock.Object.ObjectDirectory); - Context->ParamBlock.Object.ObjectDirectory = NULL; - } - if (Context->ParamBlock.Object.ObjectName) { - supHeapFree(Context->ParamBlock.Object.ObjectName); - Context->ParamBlock.Object.ObjectName = NULL; - } + supFreeDuplicatedUnicodeString(NtCurrentPeb()->ProcessHeap, + &Context->ParamBlock.Object.Directory, TRUE); + + supFreeDuplicatedUnicodeString(NtCurrentPeb()->ProcessHeap, + &Context->ParamBlock.Object.Name, TRUE); if (g_Plugin->StateChangeCallback) g_Plugin->StateChangeCallback(g_Plugin, PluginStopped, NULL); @@ -188,12 +151,9 @@ NTSTATUS CALLBACK StartPlugin( &Context->ParamBlock.Object, sizeof(WINOBJEX_PARAM_OBJECT)); - PmpCopyObjectData( + if (!PmpCopyObjectData( &ParamBlock->Object, - &Context->ParamBlock.Object); - - if ((Context->ParamBlock.Object.ObjectDirectory == NULL) || - (Context->ParamBlock.Object.ObjectName == NULL)) + &Context->ParamBlock.Object)) { supHeapFree(Context); return STATUS_MEMORY_NOT_ALLOCATED; @@ -202,8 +162,8 @@ NTSTATUS CALLBACK StartPlugin( Status = Context->ParamBlock.OpenNamedObjectByType( &SectionHandle, ObjectTypeSection, - Context->ParamBlock.Object.ObjectDirectory, - Context->ParamBlock.Object.ObjectName, + &Context->ParamBlock.Object.Directory, + &Context->ParamBlock.Object.Name, SECTION_QUERY | SECTION_MAP_READ); if (!NT_SUCCESS(Status)) { @@ -312,6 +272,7 @@ BOOLEAN CALLBACK PluginInit( return FALSE; __try { + // // Set plugin name to be displayed in WinObjEx64 UI. // @@ -349,7 +310,7 @@ BOOLEAN CALLBACK PluginInit( PluginData->SupportMultipleInstances = TRUE; PluginData->MajorVersion = 1; - PluginData->MinorVersion = 0; + PluginData->MinorVersion = 1; // // Set plugin type. diff --git a/Source/Plugins/ImageScope/sup.c b/Source/Plugins/ImageScope/sup.c index 10cb1786..b081b713 100644 --- a/Source/Plugins/ImageScope/sup.c +++ b/Source/Plugins/ImageScope/sup.c @@ -4,9 +4,9 @@ * * TITLE: SUP.C * -* VERSION: 1.02 +* VERSION: 1.10 * -* DATE: 11 May 2022 +* DATE: 15 Jun 2022 * * THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF * ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED @@ -623,3 +623,63 @@ BOOL supListViewCopyItemValueToClipboard( return FALSE; } + +/* +* supFreeDuplicatedUnicodeString +* +* Purpose: +* +* Release memory allocated for duplicated string. +* +*/ +_Success_(return) +BOOL supFreeDuplicatedUnicodeString( + _In_ HANDLE HeapHandle, + _Inout_ PUNICODE_STRING DuplicatedString, + _In_ BOOL DoZeroMemory +) +{ + BOOL bResult = FALSE; + if (DuplicatedString->Buffer) { + bResult = RtlFreeHeap(HeapHandle, 0, DuplicatedString->Buffer); + if (DoZeroMemory) { + DuplicatedString->Buffer = NULL; + DuplicatedString->Length = DuplicatedString->MaximumLength = 0; + } + } + return bResult; +} + +/* +* supDuplicateUnicodeString +* +* Purpose: +* +* Duplicate existing UNICODE_STRING to another without RtlDuplicateUnicodeString. +* +* Note: Use supFreeDuplicatedUnicodeString to release allocated memory. +* +*/ +_Success_(return) +BOOL supDuplicateUnicodeString( + _In_ HANDLE HeapHandle, + _Out_ PUNICODE_STRING DestinationString, + _In_ PUNICODE_STRING SourceString +) +{ + USHORT maxLength = SourceString->MaximumLength; + PWCHAR strBuffer; + + if (maxLength == 0 || maxLength < SourceString->Length) + return FALSE; + + strBuffer = (PWCHAR)RtlAllocateHeap(HeapHandle, HEAP_ZERO_MEMORY, (SIZE_T)maxLength); + if (strBuffer) { + DestinationString->Buffer = strBuffer; + DestinationString->MaximumLength = maxLength; + RtlCopyUnicodeString(DestinationString, SourceString); + return TRUE; + } + + return FALSE; +} diff --git a/Source/Plugins/ImageScope/sup.h b/Source/Plugins/ImageScope/sup.h index 34d507aa..d4873952 100644 --- a/Source/Plugins/ImageScope/sup.h +++ b/Source/Plugins/ImageScope/sup.h @@ -4,9 +4,9 @@ * * TITLE: SUP.H * -* VERSION: 1.02 +* VERSION: 1.10 * -* DATE: 11 May 2022 +* DATE: 15 Jun 2022 * * Common header file for the plugin support routines. * @@ -97,3 +97,17 @@ BOOL supListViewCopyItemValueToClipboard( _In_ HWND hwndListView, _In_ INT iItem, _In_ INT iSubItem); + +_Success_(return) +BOOL supFreeDuplicatedUnicodeString( + _In_ HANDLE HeapHandle, + _Inout_ PUNICODE_STRING DuplicatedString, + _In_ BOOL DoZeroMemory); + +_Success_(return) +BOOL supDuplicateUnicodeString( + _In_ HANDLE HeapHandle, + _Out_ PUNICODE_STRING DestinationString, + _In_ PUNICODE_STRING SourceString); + + diff --git a/Source/Plugins/ImageScope/ui.c b/Source/Plugins/ImageScope/ui.c index 6b02d847..b5fa2c26 100644 --- a/Source/Plugins/ImageScope/ui.c +++ b/Source/Plugins/ImageScope/ui.c @@ -1,12 +1,12 @@ /******************************************************************************* * -* (C) COPYRIGHT AUTHORS, 2020 - 2021 +* (C) COPYRIGHT AUTHORS, 2020 - 2022 * * TITLE: UI.C * -* VERSION: 1.01 +* VERSION: 1.10 * -* DATE: 01 Oct 2021 +* DATE: 11 Jun 2021 * * WinObjEx64 ImageScope UI. * @@ -393,8 +393,8 @@ VOID SectionDumpStructs( ntStatus = Context->ParamBlock.OpenNamedObjectByType( §ionHandle, ObjectTypeSection, - Context->ParamBlock.Object.ObjectDirectory, - Context->ParamBlock.Object.ObjectName, + &Context->ParamBlock.Object.Directory, + &Context->ParamBlock.Object.Name, SECTION_QUERY); if (!NT_SUCCESS(ntStatus)) @@ -1466,9 +1466,8 @@ BOOL RunUI( INT i; INITCOMMONCONTROLSEX icex; - BOOL rv, mAlloc = FALSE; + BOOL rv; MSG msg1; - SIZE_T sz; LPWSTR lpTitle; WCHAR szClassName[100]; @@ -1494,23 +1493,7 @@ BOOL RunUI( TEXT("%wsWndClass"), g_Plugin->Name); - sz = (MAX_PATH + - _strlen(Context->ParamBlock.Object.ObjectDirectory) + - _strlen(Context->ParamBlock.Object.ObjectName)) * sizeof(WCHAR); - - lpTitle = supHeapAlloc(sz); - if (lpTitle) { - - StringCchPrintf(lpTitle, - sz / sizeof(WCHAR), - TEXT("Viewing :: %ws\\%ws"), - Context->ParamBlock.Object.ObjectDirectory, - Context->ParamBlock.Object.ObjectName); - - mAlloc = TRUE; - } - else - lpTitle = IMAGESCOPE_WNDTITLE; + lpTitle = IMAGESCOPE_WNDTITLE; // // Create main window. @@ -1529,9 +1512,6 @@ BOOL RunUI( g_ThisDLL, NULL); - if (mAlloc) - supHeapFree(lpTitle); - if (Context->MainWindow == 0) { kdDebugPrint("Could not create main window, err = %lu\r\n", GetLastError()); return FALSE; diff --git a/Source/Plugins/ImageScope/ui.h b/Source/Plugins/ImageScope/ui.h index cacbbcb1..26bc402b 100644 --- a/Source/Plugins/ImageScope/ui.h +++ b/Source/Plugins/ImageScope/ui.h @@ -1,12 +1,12 @@ /******************************************************************************* * -* (C) COPYRIGHT AUTHORS, 2020 - 2021 +* (C) COPYRIGHT AUTHORS, 2020 - 2022 * * TITLE: UI.H * -* VERSION: 1.01 +* VERSION: 1.02 * -* DATE: 08 Jan 2021 +* DATE: 08 Jun 2022 * * WinObjEx64 ImageScope UI constants, definitions and includes. * @@ -77,11 +77,12 @@ typedef struct _IMS_TAB { } IMS_TAB; typedef struct _TL_SUBITEMS_FIXED { + ULONG Count; ULONG ColorFlags; COLORREF BgColor; COLORREF FontColor; PVOID UserParam; - ULONG Count; + LPTSTR CustomTooltip; LPTSTR Text[2]; } TL_SUBITEMS_FIXED, * PTL_SUBITEMS_FIXED; diff --git a/Source/Plugins/Sonar/ui.h b/Source/Plugins/Sonar/ui.h index 0288897c..d2924090 100644 --- a/Source/Plugins/Sonar/ui.h +++ b/Source/Plugins/Sonar/ui.h @@ -1,12 +1,12 @@ /******************************************************************************* * -* (C) COPYRIGHT AUTHORS, 2019 - 2021 +* (C) COPYRIGHT AUTHORS, 2019 - 2022 * * TITLE: UI.H * -* VERSION: 1.14 +* VERSION: 1.15 * -* DATE: 30 Sep 2021 +* DATE: 10 Jun 2022 * * WinObjEx64 Sonar UI constants, definitions and includes. * @@ -41,11 +41,12 @@ #define SCALE_DPI_VALUE(Value, CurrentDPI) MulDiv(Value, CurrentDPI, DefaultSystemDpi) typedef struct _TL_SUBITEMS_FIXED { + ULONG Count; ULONG ColorFlags; COLORREF BgColor; COLORREF FontColor; PVOID UserParam; - ULONG Count; + LPTSTR CustomTooltip; LPTSTR Text[2]; } TL_SUBITEMS_FIXED, * PTL_SUBITEMS_FIXED; diff --git a/Source/Plugins/plugin_def.h b/Source/Plugins/plugin_def.h index e8c0cede..89b0be42 100644 --- a/Source/Plugins/plugin_def.h +++ b/Source/Plugins/plugin_def.h @@ -1,12 +1,12 @@ /******************************************************************************* * -* (C) COPYRIGHT AUTHORS, 2019 - 2021 +* (C) COPYRIGHT AUTHORS, 2019 - 2022 * * TITLE: PLUGIN_DEF.H * -* VERSION: 1.10 +* VERSION: 1.11 * -* DATE: 01 Oct 2021 +* DATE: 19 Jun 2022 * * Common header file for the plugin subsystem definitions. * @@ -19,7 +19,7 @@ #pragma once -#define WOBJ_PLUGIN_SYSTEM_VERSION 18712 +#define WOBJ_PLUGIN_SYSTEM_VERSION 20006 // // Plugin text consts, must include terminating 0. @@ -49,14 +49,13 @@ typedef UCHAR(CALLBACK* pfnGetInstructionLength)( typedef NTSTATUS(*pfnOpenNamedObjectByType)( _Out_ HANDLE* ObjectHandle, _In_ ULONG TypeIndex, - _In_ LPWSTR ObjectDirectory, - _In_opt_ LPWSTR ObjectName, + _In_ PUNICODE_STRING ObjectDirectory, + _In_ PUNICODE_STRING ObjectName, _In_ ACCESS_MASK DesiredAccess); typedef struct _WINOBJEX_PARAM_OBJECT { - LPWSTR ObjectName; - LPWSTR ObjectDirectory; - PVOID Reserved; + UNICODE_STRING Name; + UNICODE_STRING Directory; } WINOBJEX_PARAM_OBJECT, * PWINOBJEX_PARAM_OBJECT; typedef struct _WINOBJEX_PARAM_BLOCK { diff --git a/Source/Shared/ntos/ntos.h b/Source/Shared/ntos/ntos.h index 908a91c6..6626931d 100644 --- a/Source/Shared/ntos/ntos.h +++ b/Source/Shared/ntos/ntos.h @@ -5,9 +5,9 @@ * * TITLE: NTOS.H * -* VERSION: 1.197 +* VERSION: 1.198 * -* DATE: 05 Jun 2022 +* DATE: 12 Jun 2022 * * Common header file for the ntos API functions and definitions. * @@ -12989,7 +12989,7 @@ NtCreateResourceManager( _In_ HANDLE TmHandle, _In_opt_ LPGUID ResourceManagerGuid, _In_opt_ POBJECT_ATTRIBUTES ObjectAttributes, - _In_opt_ ULONG CreateOptions, + _In_ ULONG CreateOptions, _In_opt_ PUNICODE_STRING Description); NTSYSAPI @@ -13035,8 +13035,8 @@ NtCreateTransactionManager( _In_ ACCESS_MASK DesiredAccess, _In_opt_ POBJECT_ATTRIBUTES ObjectAttributes, _In_opt_ PUNICODE_STRING LogFileName, - _In_opt_ ULONG CreateOptions, - _In_opt_ ULONG CommitStrength); + _In_ ULONG CreateOptions, + _In_ ULONG CommitStrength); NTSYSAPI NTSTATUS @@ -13047,7 +13047,7 @@ NtOpenTransactionManager( _In_opt_ POBJECT_ATTRIBUTES ObjectAttributes, _In_opt_ PUNICODE_STRING LogFileName, _In_opt_ LPGUID TmIdentity, - _In_opt_ ULONG OpenOptions); + _In_ ULONG OpenOptions); /************************************************************************************ * @@ -13556,6 +13556,9 @@ NtProtectVirtualMemory( _In_ ULONG NewProtect, _Out_ PULONG OldProtect); +#define MAP_PROCESS 1L +#define MAP_SYSTEM 2L + NTSYSAPI NTSTATUS NTAPI diff --git a/Source/Shared/ntos/ntsup.c b/Source/Shared/ntos/ntsup.c index cfb0aed4..a17a4735 100644 --- a/Source/Shared/ntos/ntsup.c +++ b/Source/Shared/ntos/ntsup.c @@ -6,7 +6,7 @@ * * VERSION: 2.13 * -* DATE: 05 Jun 2022 +* DATE: 15 Jun 2022 * * Native API support functions. * @@ -103,6 +103,44 @@ PVOID ntsupVirtualAlloc( return ntsupVirtualAllocEx(Size, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE); } +/* +* ntsupVirtualLock +* +* Purpose: +* +* Wrapper for NtLockVirtualMemory. +* +*/ +BOOL ntsupVirtualLock( + _In_ LPVOID lpAddress, + _In_ SIZE_T dwSize +) +{ + return (NT_SUCCESS(NtLockVirtualMemory(NtCurrentProcess(), + &lpAddress, + &dwSize, + MAP_PROCESS))); +} + +/* +* ntsupVirtualUnlock +* +* Purpose: +* +* Wrapper for NtUnlockVirtualMemory. +* +*/ +BOOL ntsupVirtualUnlock( + _In_ LPVOID lpAddress, + _In_ SIZE_T dwSize +) +{ + return (NT_SUCCESS(NtUnlockVirtualMemory(NtCurrentProcess(), + &lpAddress, + &dwSize, + MAP_PROCESS))); +} + /* * NtSupVirtualFree * @@ -929,42 +967,58 @@ BOOL ntsupQueryThreadWin32StartAddress( } /* -* ntsupOpenDirectory +* ntsupOpenDirectoryEx * * Purpose: * * Open directory handle with DIRECTORY_QUERY access, with root directory support. * */ -NTSTATUS ntsupOpenDirectory( +_Success_(return) +NTSTATUS ntsupOpenDirectoryEx( _Out_ PHANDLE DirectoryHandle, _In_opt_ HANDLE RootDirectoryHandle, - _In_ LPCWSTR DirectoryName, + _In_ PUNICODE_STRING DirectoryName, _In_ ACCESS_MASK DesiredAccess ) { NTSTATUS ntStatus; HANDLE directoryHandle = NULL; - UNICODE_STRING usDirectory; OBJECT_ATTRIBUTES objectAttrbutes; - *DirectoryHandle = NULL; - - RtlInitUnicodeString(&usDirectory, DirectoryName); InitializeObjectAttributes(&objectAttrbutes, - &usDirectory, OBJ_CASE_INSENSITIVE, RootDirectoryHandle, NULL); + DirectoryName, OBJ_CASE_INSENSITIVE, RootDirectoryHandle, NULL); ntStatus = NtOpenDirectoryObject(&directoryHandle, DesiredAccess, &objectAttrbutes); - if (NT_SUCCESS(ntStatus)) { - *DirectoryHandle = directoryHandle; - } + *DirectoryHandle = directoryHandle; return ntStatus; } +/* +* ntsupOpenDirectory +* +* Purpose: +* +* Open directory handle with DIRECTORY_QUERY access, with root directory support. +* +*/ +NTSTATUS ntsupOpenDirectory( + _Out_ PHANDLE DirectoryHandle, + _In_opt_ HANDLE RootDirectoryHandle, + _In_ LPCWSTR DirectoryName, + _In_ ACCESS_MASK DesiredAccess +) +{ + UNICODE_STRING usName; + + RtlInitUnicodeString(&usName, DirectoryName); + return ntsupOpenDirectoryEx(DirectoryHandle, RootDirectoryHandle, &usName, DesiredAccess); +} + /* * ntsupQueryProcessName * @@ -1850,112 +1904,6 @@ NTSTATUS ntsupIsProcessElevated( return ntStatus; } -/* -* ntsupGetMappedFileName -* -* Purpose: -* -* Checks whether the specified address is within a memory-mapped file. -* If so, the function returns the name of the memory-mapped file. -* -*/ -ULONG ntsupGetMappedFileName( - _In_ PVOID BaseAddress, - _Inout_ LPWSTR FileName, - _In_ ULONG cchFileName, - _Out_ PSIZE_T cbNeeded -) -{ - OBJECT_NAME_INFORMATION* objectNameInfo; - NTSTATUS ntStatus; - SIZE_T returnedLength = 0; - ULONG errorCode, copyLength = 0; - HANDLE processHeap = NtCurrentPeb()->ProcessHeap; - - *cbNeeded = 0; - - if (cchFileName == 0) { - RtlSetLastWin32Error(ERROR_INSUFFICIENT_BUFFER); - return 0; - } - - // - // Don't be like MS authors and ask actual size. - // - ntStatus = NtQueryVirtualMemory( - NtCurrentProcess(), - BaseAddress, - MemoryMappedFilenameInformation, - NULL, - 0, - &returnedLength); - - if (ntStatus != STATUS_INFO_LENGTH_MISMATCH) { - RtlSetLastWin32Error(RtlNtStatusToDosError(ntStatus)); - return 0; - } - - // - // Allocate required buffer. - // - objectNameInfo = (OBJECT_NAME_INFORMATION*)RtlAllocateHeap( - processHeap, - HEAP_ZERO_MEMORY, - returnedLength); - - if (objectNameInfo == NULL) { - RtlSetLastWin32Error(ERROR_NOT_ENOUGH_MEMORY); - return 0; - } - - // - // Query information. - // - ntStatus = NtQueryVirtualMemory( - NtCurrentProcess(), - BaseAddress, - MemoryMappedFilenameInformation, - objectNameInfo, - returnedLength, - &returnedLength); - - if (NT_SUCCESS(ntStatus)) { - - // - // Copy filename. - // - copyLength = objectNameInfo->Name.Length >> 1; - if (cchFileName > copyLength + 1) { - errorCode = ERROR_SUCCESS; - } - else { - *cbNeeded = ((SIZE_T)copyLength + 1) * sizeof(WCHAR); - copyLength = cchFileName - 1; - errorCode = ERROR_INSUFFICIENT_BUFFER; - } - - RtlSetLastWin32Error(errorCode); - - if (copyLength) { - - RtlCopyMemory( - FileName, - objectNameInfo->Name.Buffer, - copyLength * sizeof(WCHAR)); - - FileName[copyLength] = 0; - - } - - } - else { - RtlSetLastWin32Error(RtlNtStatusToDosError(ntStatus)); - } - - RtlFreeHeap(processHeap, 0, objectNameInfo); - return copyLength; -} - /* * ntsupPurgeSystemCache * diff --git a/Source/Shared/ntos/ntsup.h b/Source/Shared/ntos/ntsup.h index 98cae5b9..916ae3da 100644 --- a/Source/Shared/ntos/ntsup.h +++ b/Source/Shared/ntos/ntsup.h @@ -6,7 +6,7 @@ * * VERSION: 2.13 * -* DATE: 04 Jun 2022 +* DATE: 15 Jun 2022 * * Common header file for the NT API support functions and definitions. * @@ -100,6 +100,14 @@ PVOID ntsupVirtualAlloc( BOOL ntsupVirtualFree( _In_ PVOID Memory); +BOOL ntsupVirtualLock( + _In_ LPVOID lpAddress, + _In_ SIZE_T dwSize); + +BOOL ntsupVirtualUnlock( + _In_ LPVOID lpAddress, + _In_ SIZE_T dwSize); + SIZE_T ntsupWriteBufferToFile( _In_ PWSTR lpFileName, _In_ PVOID Buffer, @@ -179,6 +187,13 @@ BOOL ntsupQueryThreadWin32StartAddress( _In_ HANDLE ThreadHandle, _Out_ PULONG_PTR Win32StartAddress); +_Success_(return) +NTSTATUS ntsupOpenDirectoryEx( + _Out_ PHANDLE DirectoryHandle, + _In_opt_ HANDLE RootDirectoryHandle, + _In_ PUNICODE_STRING DirectoryName, + _In_ ACCESS_MASK DesiredAccess); + NTSTATUS ntsupOpenDirectory( _Out_ PHANDLE DirectoryHandle, _In_opt_ HANDLE RootDirectoryHandle, @@ -275,12 +290,6 @@ NTSTATUS ntsupIsProcessElevated( _In_ ULONG ProcessId, _Out_ PBOOL Elevated); -ULONG ntsupGetMappedFileName( - _In_ PVOID BaseAddress, - _Inout_ LPWSTR FileName, - _In_ ULONG cchFileName, - _Out_ PSIZE_T cbNeeded); - VOID ntsupPurgeSystemCache( VOID); diff --git a/Source/Shared/sdk/extdef.h b/Source/Shared/sdk/extdef.h index 47e0045f..1b653d65 100644 --- a/Source/Shared/sdk/extdef.h +++ b/Source/Shared/sdk/extdef.h @@ -4,9 +4,9 @@ * * TITLE: EXTAPI.H * -* VERSION: 1.94 +* VERSION: 2.00 * -* DATE: 04 Jun 2022 +* DATE: 19 Jun 2022 * * Windows SDK compatibility header. * diff --git a/Source/Shared/treelist/treelist.c b/Source/Shared/treelist/treelist.c index 111f51ed..a4cd8eb2 100644 --- a/Source/Shared/treelist/treelist.c +++ b/Source/Shared/treelist/treelist.c @@ -1,12 +1,12 @@ /******************************************************************************* * -* (C) COPYRIGHT AUTHORS, 2015 - 2021 +* (C) COPYRIGHT AUTHORS, 2015 - 2022 * * TITLE: TREELIST.C * -* VERSION: 1.34 +* VERSION: 1.35 * -* DATE: 16 Sept 2021 +* DATE: 10 Jun 2022 * * TreeList control. * @@ -517,9 +517,6 @@ LRESULT CALLBACK TreeListHookProc( if (!TreeView_GetItemRect(hwnd, (HTREEITEM)hdr->lParam, &rc, TRUE)) break; - if ((subid == 0) && (rc.right < hr.right - 1)) // is tooltip from the first column? - break; - privateBuffer = (LPTSTR)GetWindowLongPtr(BaseWindow, TL_TOOLTIPSBUFFER_SLOT); privateBuffer[0] = 0; @@ -530,21 +527,34 @@ LRESULT CALLBACK TreeListHookProc( itemex.hItem = (HTREEITEM)hdr->lParam; TreeView_GetItem(hwnd, &itemex); - if ((subid > 0) && (itemex.lParam != 0)) { - subitems = (PTL_SUBITEMS)itemex.lParam; + subitems = (PTL_SUBITEMS)itemex.lParam; + + if (subid == 0) // is tooltip from the first column? + { + if (subitems) + if (subitems->CustomTooltip) + { + SendMessage(hdr->hdr.hwndFrom, TTM_SETMAXTIPWIDTH, 0, 1024); + _strncpy(privateBuffer, MAX_PATH, subitems->CustomTooltip, MAX_PATH); + hdr->lpszText = privateBuffer; + break; + } + if (rc.right < hr.right - 1) // no overflow + break; + } + + if ((subid > 0) && (subitems != 0)) { rc.left = hr.left + 3; rc.right = hr.right - 3; + /*fake DrawText for calculating bounding rectangle*/ dc = GetDC(hwnd); SelectObject(dc, (HGDIOBJ)SendMessage(hwnd, WM_GETFONT, 0, 0)); - - /*fake DrawText for calculating bounding rectangle*/ DrawText(dc, subitems->Text[subid - 1], -1, &rc, DT_VCENTER | DT_SINGLELINE | DT_CALCRECT); - ReleaseDC(hwnd, dc); - if (rc.right < hr.right - 2) + if (rc.right < hr.right - 2) // no overflow break; _strncpy(privateBuffer, MAX_PATH, subitems->Text[subid - 1], MAX_PATH); @@ -586,24 +596,27 @@ PTL_SUBITEMS PackSubitems(HANDLE hHeap, IN PTL_SUBITEMS Subitems) for (i = 0; i < Subitems->Count; i++) strings_size += (_strlen(Subitems->Text[i]) + 1) * sizeof(TCHAR); + strings_size += (_strlen(Subitems->CustomTooltip) + 1) * sizeof(TCHAR); + newsubitems = (PTL_SUBITEMS)HeapAlloc(hHeap, 0, header_size + strings_size); if (!newsubitems) return NULL; strings = (LPTSTR)((PBYTE)newsubitems + header_size); - - newsubitems->UserParam = Subitems->UserParam; - newsubitems->ColorFlags = Subitems->ColorFlags; - newsubitems->BgColor = Subitems->BgColor; - newsubitems->FontColor = Subitems->FontColor; - newsubitems->Count = Subitems->Count; + *newsubitems = *Subitems; for (i = 0; i < Subitems->Count; i++) { newsubitems->Text[i] = strings; - _strcpy(newsubitems->Text[i], Subitems->Text[i]); + _strcpy(strings, Subitems->Text[i]); strings += _strlen(Subitems->Text[i]) + 1; } + if (Subitems->CustomTooltip != NULL) + { + newsubitems->CustomTooltip = strings; + _strcpy(strings, Subitems->CustomTooltip); + } + return newsubitems; } diff --git a/Source/Shared/treelist/treelist.h b/Source/Shared/treelist/treelist.h index c6cebbec..8b63bbd9 100644 --- a/Source/Shared/treelist/treelist.h +++ b/Source/Shared/treelist/treelist.h @@ -1,12 +1,12 @@ /******************************************************************************* * -* (C) COPYRIGHT AUTHORS, 2015 - 2021 +* (C) COPYRIGHT AUTHORS, 2015 - 2022 * * TITLE: TREELIST.H * -* VERSION: 1.34 +* VERSION: 1.35 * -* DATE: 16 Sept 2021 +* DATE: 10 Jun 2022 * * Tree-List custom control header file. * @@ -47,11 +47,12 @@ #define TLSTYLE_LINKLINES 0x02 typedef struct _TL_SUBITEMS { + ULONG Count; ULONG ColorFlags; COLORREF BgColor; COLORREF FontColor; PVOID UserParam; - ULONG Count; + LPTSTR CustomTooltip; LPTSTR Text[1]; } TL_SUBITEMS, *PTL_SUBITEMS; diff --git a/Source/WinObjEx64/Resource.rc b/Source/WinObjEx64/Resource.rc index 34bea1503c3e9201981199cba1db9de4defd9d28..7936ee6c90204a44de3c691c116802bfad2f9cce 100644 GIT binary patch delta 6609 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 diff --git a/Source/WinObjEx64/WinObjEx64.vcxproj b/Source/WinObjEx64/WinObjEx64.vcxproj index 3673c16a..f188a36f 100644 --- a/Source/WinObjEx64/WinObjEx64.vcxproj +++ b/Source/WinObjEx64/WinObjEx64.vcxproj @@ -394,6 +394,9 @@ + + + @@ -412,10 +415,8 @@ - - @@ -435,25 +436,14 @@ - - - - - - - - - - - - + @@ -461,6 +451,10 @@ + + + + @@ -468,30 +462,14 @@ - - - - - - - - - - - - - - - - diff --git a/Source/WinObjEx64/WinObjEx64.vcxproj.filters b/Source/WinObjEx64/WinObjEx64.vcxproj.filters index 18914d91..b546b8f9 100644 --- a/Source/WinObjEx64/WinObjEx64.vcxproj.filters +++ b/Source/WinObjEx64/WinObjEx64.vcxproj.filters @@ -43,6 +43,9 @@ {b8b6096c-f90d-41f3-a643-a53ee0fae82b} + + {ef3c4ca0-1364-4947-a6d6-091b33037c05} + @@ -63,9 +66,6 @@ Source Files - - Source Files - Hde @@ -129,9 +129,6 @@ Source Files\props - - Source Files - Source Files\extras @@ -252,6 +249,15 @@ Source Files + + Source Files\sup + + + Source Files\sup + + + Source Files\sup + @@ -259,15 +265,9 @@ - - Header Files - Header Files - - Header Files - Header Files @@ -280,12 +280,6 @@ Header Files - - Header Files - - - Header Files - Header Files @@ -310,78 +304,27 @@ Header Files - - Header Files - Source Files\extras - - Source Files\extras - - - Source Files\extras - - - Source Files\extras - - - Source Files\extras - - - Source Files\extras - - - Source Files\extras - - - Source Files\extras - - - Source Files\props - Source Files\props - - Source Files\props - Source Files\props - - Source Files\props - - - Source Files\props - Source Files\props - - Source Files\props - - - Source Files\props - Source Files\props - - Source Files\props - Source Files\props Header Files - - Source Files\props - - - Source Files\extras - Header Files @@ -406,9 +349,6 @@ Source Files\extras - - Header Files - Ntos @@ -421,15 +361,6 @@ Ntos - - Header Files - - - Source Files\props - - - Source Files\props - Header Files @@ -457,14 +388,20 @@ Header Files - - Source Files\extras + + Source Files\props - - Source Files\extras + + Source Files\sup - - Header Files + + Source Files\sup + + + Source Files\props + + + Source Files\extras diff --git a/Source/WinObjEx64/aboutDlg.c b/Source/WinObjEx64/aboutDlg.c index b472bbd4..6ab128ff 100644 --- a/Source/WinObjEx64/aboutDlg.c +++ b/Source/WinObjEx64/aboutDlg.c @@ -4,9 +4,9 @@ * * TITLE: ABOUTDLG.C * -* VERSION: 1.94 +* VERSION: 2.00 * -* DATE: 04 Jun 2022 +* DATE: 19 Jun 2022 * * THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF * ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED @@ -16,7 +16,6 @@ *******************************************************************************/ #include "global.h" #include "msvcver.h" -#include "winedebug.h" #define T_ABOUTDLG_ICON_PROP TEXT("aboutDlgIcon") @@ -121,7 +120,7 @@ VOID AboutDialogInit( // Fill boot options. // if (g_WinObj.IsWine) { - wine_ver = (PCHAR)wine_get_version(); + wine_ver = GetWineVersion(); wine_str = (PCHAR)supHeapAlloc(_strlen_a(wine_ver) + MAX_PATH); if (wine_str) { _strcpy_a(wine_str, "Wine "); @@ -140,7 +139,7 @@ VOID AboutDialogInit( // // Query KD debugger enabled. // - if (ntsupIsKdEnabled(NULL, NULL)) { + if (supIsKdEnabled(NULL, NULL)) { _strcpy(szBuffer, TEXT("Debug, ")); } @@ -229,7 +228,7 @@ VOID AboutDialogOnNotify( if ((((LPNMHDR)lParam)->hwndFrom == GetDlgItem(hwndDlg, IDC_ABOUT_SYSLINK)) && (item.iLink == 0)) { - supShellExecInExplorerProcess(item.szUrl); + supShellExecInExplorerProcess(item.szUrl, NULL); } break; @@ -262,7 +261,7 @@ INT_PTR CALLBACK AboutDialogProc( case WM_INITDIALOG: supCenterWindow(hwndDlg); AboutDialogInit(hwndDlg); - break; + return TRUE; case WM_NOTIFY: AboutDialogOnNotify(hwndDlg, lParam); @@ -270,15 +269,174 @@ INT_PTR CALLBACK AboutDialogProc( case WM_COMMAND: - switch (GET_WM_COMMAND_ID(wParam, lParam)) { - case IDOK: - case IDCANCEL: + if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL) { + hIcon = RemoveProp(hwndDlg, T_ABOUTDLG_ICON_PROP); if (hIcon) { DestroyIcon((HICON)hIcon); } - return EndDialog(hwndDlg, S_OK); + return EndDialog(hwndDlg, TRUE); + + } + + } + return 0; +} + +static HANDLE StatsDialogThreadHandle = NULL; +static FAST_EVENT StatsDialogInitializedEvent = FAST_EVENT_INIT; +#define UPDATE_TIMER_ID 1 + +/* +* StatsTimerProc +* +* Purpose: +* +* Statistics timer callback. +* +*/ +VOID StatsTimerProc( + HWND hwnd, + UINT uMsg, + UINT_PTR idEvent, + DWORD dwTime) +{ + UNREFERENCED_PARAMETER(uMsg); + UNREFERENCED_PARAMETER(idEvent); + UNREFERENCED_PARAMETER(dwTime); + + WCHAR szBuffer[64]; + + SetDlgItemInt(hwnd, IDC_STATS_TOTALHEAPALLOC, g_WinObjStats.TotalHeapAlloc, FALSE); + SetDlgItemInt(hwnd, IDC_STATS_TOTALHEAPFREE, g_WinObjStats.TotalHeapFree, FALSE); + SetDlgItemInt(hwnd, IDC_STATS_TOTALHEAPSCREATED, g_WinObjStats.TotalHeapsCreated, FALSE); + SetDlgItemInt(hwnd, IDC_STATS_TOTALHEAPSDESTROYED, g_WinObjStats.TotalHeapsDestroyed, FALSE); + SetDlgItemInt(hwnd, IDC_STATS_TOTALTHREADSCREATED, g_WinObjStats.TotalThreadsCreated, FALSE); + + szBuffer[0] = 0; + u64tostr(g_WinObjStats.TotalHeapMemoryAllocated, &szBuffer[0]); + SetDlgItemText(hwnd, IDC_STATS_TOTALTHEAPMEMORYALLOCATED, szBuffer); + +#ifdef _DEBUG + ShowWindow(GetDlgItem(hwnd, IDC_STATS_MAXHEAPALLOCATEDSIZE_STATIC), SW_SHOW); + ShowWindow(GetDlgItem(hwnd, IDC_STATS_MAXHEAPALLOCATEDSIZE), SW_SHOW); + szBuffer[0] = 0; + u64tostr(g_WinObjStats.MaxHeapAllocatedBlockSize, &szBuffer[0]); + SetDlgItemText(hwnd, IDC_STATS_MAXHEAPALLOCATEDSIZE, szBuffer); +#endif +} + +/* +* StatsDialogProc +* +* Purpose: +* +* Statistics Dialog Window Procedure +* +* During WM_INITDIALOG centers window and sets timer callback. +* +*/ +INT_PTR CALLBACK StatsDialogProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam +) +{ + UNREFERENCED_PARAMETER(lParam); + + switch (uMsg) { + + case WM_INITDIALOG: + supCenterWindowSpecifyParent(hwndDlg, g_hwndMain); + SetTimer(hwndDlg, UPDATE_TIMER_ID, 1000, (TIMERPROC)StatsTimerProc); + break; + + case WM_DESTROY: + PostQuitMessage(0); + break; + + case WM_CLOSE: + KillTimer(hwndDlg, UPDATE_TIMER_ID); + return DestroyWindow(hwndDlg); + + case WM_COMMAND: + + switch (GET_WM_COMMAND_ID(wParam, lParam)) { + case IDCANCEL: + case IDOK: + SendMessage(hwndDlg, WM_CLOSE, 0, 0); + break; + } + break; + } + return 0; +} + +/* +* StatsDialogWorkerThread +* +* Purpose: +* +* Worker thread that creates dialog window and processes messages queue. +* +*/ +DWORD StatsDialogWorkerThread( + _In_ PVOID Parameter +) +{ + BOOL bResult; + MSG message; + HWND hwndDlg; + + UNREFERENCED_PARAMETER(Parameter); + + hwndDlg = CreateDialogParam(g_WinObj.hInstance, + MAKEINTRESOURCE(IDD_DIALOG_STATS), + 0, + (DLGPROC)&StatsDialogProc, + 0); + + supSetFastEvent(&StatsDialogInitializedEvent); + + do { + + bResult = GetMessage(&message, NULL, 0, 0); + if (bResult == -1) + break; + + if (!IsDialogMessage(hwndDlg, &message)) { + TranslateMessage(&message); + DispatchMessage(&message); } + + } while (bResult != 0); + + supResetFastEvent(&StatsDialogInitializedEvent); + + if (StatsDialogThreadHandle) { + NtClose(StatsDialogThreadHandle); + StatsDialogThreadHandle = NULL; } return 0; } + +/* +* ShowStatsDialog +* +* Purpose: +* +* Create statistics dialog if none present. +* +*/ +VOID ShowStatsDialog( + VOID +) +{ + if (!StatsDialogThreadHandle) { + + StatsDialogThreadHandle = supCreateThread(StatsDialogWorkerThread, NULL, 0); + supWaitForFastEvent(&StatsDialogInitializedEvent, NULL); + + } +} diff --git a/Source/WinObjEx64/aboutDlg.h b/Source/WinObjEx64/aboutDlg.h deleted file mode 100644 index 0a6b13f4..00000000 --- a/Source/WinObjEx64/aboutDlg.h +++ /dev/null @@ -1,25 +0,0 @@ -/******************************************************************************* -* -* (C) COPYRIGHT AUTHORS, 2015 - 2018 -* -* TITLE: ABOUTDLG.H -* -* VERSION: 1.52 -* -* DATE: 08 Jan 2018 -* -* Common header file for the About Dialog. -* -* THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF -* ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED -* TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A -* PARTICULAR PURPOSE. -* -*******************************************************************************/ -#pragma once - -INT_PTR CALLBACK AboutDialogProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam); diff --git a/Source/WinObjEx64/drivers/winio.c b/Source/WinObjEx64/drivers/winio.c index 4567d9ce..b98e8c66 100644 --- a/Source/WinObjEx64/drivers/winio.c +++ b/Source/WinObjEx64/drivers/winio.c @@ -4,9 +4,9 @@ * * TITLE: WINIO.C * -* VERSION: 1.94 +* VERSION: 2.00 * -* DATE: 01 Jun 2022 +* DATE: 19 Jun 2022 * * WinIo based reader. * diff --git a/Source/WinObjEx64/drivers/winio.h b/Source/WinObjEx64/drivers/winio.h index 84847efb..b4af20cc 100644 --- a/Source/WinObjEx64/drivers/winio.h +++ b/Source/WinObjEx64/drivers/winio.h @@ -4,9 +4,9 @@ * * TITLE: WINIO.H * -* VERSION: 1.94 +* VERSION: 2.00 * -* DATE: 01 Jun 2022 +* DATE: 19 Jun 2022 * * Common header file for the WINIO Driver Helper support. * diff --git a/Source/WinObjEx64/excepth.c b/Source/WinObjEx64/excepth.c index 067358e4..57463666 100644 --- a/Source/WinObjEx64/excepth.c +++ b/Source/WinObjEx64/excepth.c @@ -1,12 +1,12 @@ /******************************************************************************* * -* (C) COPYRIGHT AUTHORS, 2015 - 2020 +* (C) COPYRIGHT AUTHORS, 2015 - 2022 * * TITLE: EXCEPTH.C * -* VERSION: 1.85 +* VERSION: 2.00 * -* DATE: 05 Mar 2020 +* DATE: 19 Jun 2022 * * Exception handler routines. * @@ -35,44 +35,43 @@ pfnMiniDumpWriteDump pMiniDumpWriteDump; * * Purpose: * -* Writes minidump information to the specified file. +* Writes minidump information to the file. * */ BOOL exceptWriteDump( _In_ EXCEPTION_POINTERS* ExceptionPointers, - _In_ ULONGLONG IdFile + _In_ LPCWSTR lpFileName ) { - BOOL bResult; + BOOL bResult; HMODULE hDbgHelp; - HANDLE hFile; - WCHAR szFileName[MAX_PATH * 2]; //-V1072 + HANDLE hFile; + WCHAR szFileName[MAX_PATH * 2]; + UINT cch; MINIDUMP_EXCEPTION_INFORMATION mdei; bResult = FALSE; hDbgHelp = GetModuleHandle(TEXT("dbghelp.dll")); if (hDbgHelp == NULL) { + RtlSecureZeroMemory(szFileName, sizeof(szFileName)); - _strcpy(szFileName, g_WinObj.szSystemDirectory); + cch = GetSystemDirectory(szFileName, MAX_PATH); + if (cch == 0 || cch > MAX_PATH) + return FALSE; + _strcat(szFileName, TEXT("\\dbghelp.dll")); hDbgHelp = LoadLibraryEx(szFileName, 0, 0); if (hDbgHelp == NULL) - return bResult; + return FALSE; } pMiniDumpWriteDump = (pfnMiniDumpWriteDump)GetProcAddress(hDbgHelp, "MiniDumpWriteDump"); if (pMiniDumpWriteDump == NULL) - return bResult; + return FALSE; - RtlSecureZeroMemory(szFileName, sizeof(szFileName)); - _strcpy(szFileName, g_WinObj.szTempDirectory); - _strcat(szFileName, TEXT("\\wobjex")); - u64tostr(IdFile, _strend(szFileName)); - _strcat(szFileName, TEXT(".dmp")); - - hFile = CreateFile(szFileName, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL); + hFile = CreateFile(lpFileName, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL); if (hFile != INVALID_HANDLE_VALUE) { mdei.ThreadId = GetCurrentThreadId(); mdei.ExceptionPointers = ExceptionPointers; @@ -92,11 +91,12 @@ BOOL exceptWriteDump( * */ VOID exceptShowException( - _In_ EXCEPTION_POINTERS* ExceptionPointers + _In_ EXCEPTION_POINTERS* ExceptionPointers, + _In_ BOOL LastChance ) { - WCHAR szMessage[MAX_PATH * 2]; - ULONGLONG IdFile; + WCHAR szFileName[300]; + WCHAR szMessage[1000]; RtlSecureZeroMemory(&szMessage, sizeof(szMessage)); _strcpy(szMessage, TEXT("Sorry, exception occurred at address: \r\n0x")); @@ -113,18 +113,46 @@ VOID exceptShowException( } u64tohex(ExceptionPointers->ExceptionRecord->ExceptionInformation[1], _strend(szMessage)); } - IdFile = GetTickCount64(); - if (exceptWriteDump(ExceptionPointers, IdFile)) { - _strcat(szMessage, TEXT("\r\n\nMinidump wobjex")); - u64tostr(IdFile, _strend(szMessage)); - _strcat(szMessage, TEXT(".dmp is in %TEMP% directory")); + RtlSecureZeroMemory(szFileName, sizeof(szFileName)); + GetCurrentDirectory(MAX_PATH, szFileName); + _strcat(szFileName, TEXT("\\WinObjEx64.")); + ultostr(GetCurrentProcessId(), _strend(szFileName)); + _strcat(szFileName, TEXT(".")); + ultostr(GetCurrentThreadId(), _strend(szFileName)); + _strcat(szFileName, TEXT(".dmp")); + + if (exceptWriteDump(ExceptionPointers, szFileName)) { + + _strcat(szMessage, TEXT("\r\n\nMinidump saved to ")); + _strcat(szMessage, szFileName); + } else { - _strcat(szMessage, TEXT("\r\n\nThere is an error while saving minidump.")); + _strcat(szMessage, TEXT("\r\nAnd there is an error while saving minidump :(")); } - _strcat(szMessage, TEXT("\r\n\nPlease report this to the developers, thanks")); - MessageBox(GetForegroundWindow(), szMessage, NULL, MB_ICONERROR); + if (LastChance) + _strcat(szMessage, TEXT("\r\n\nThe program will be terminated.")); + + MessageBox(0, szMessage, NULL, MB_ICONERROR); +} + +/* +* exceptFilterUnhandled +* +* Purpose: +* +* Default exception filter, processing AV with minidump if available. +* +*/ +INT exceptFilterUnhandled( + _In_ struct _EXCEPTION_POINTERS* ExceptionInfo +) +{ + WDrvProvRelease(&g_kdctx.DriverContext); + exceptShowException(ExceptionInfo, TRUE); + RtlExitUserProcess(ExceptionInfo->ExceptionRecord->ExceptionCode); + return EXCEPTION_EXECUTE_HANDLER; } /* @@ -141,7 +169,7 @@ INT exceptFilter( ) { if (ExceptionCode == EXCEPTION_ACCESS_VIOLATION) { - exceptShowException(ExceptionPointers); + exceptShowException(ExceptionPointers, FALSE); return EXCEPTION_EXECUTE_HANDLER; } else { diff --git a/Source/WinObjEx64/excepth.h b/Source/WinObjEx64/excepth.h index e2973f74..907b5f79 100644 --- a/Source/WinObjEx64/excepth.h +++ b/Source/WinObjEx64/excepth.h @@ -1,12 +1,12 @@ /******************************************************************************* * -* (C) COPYRIGHT AUTHORS, 2015 - 2020 +* (C) COPYRIGHT AUTHORS, 2015 - 2022 * * TITLE: EXCEPTH.H * -* VERSION: 1.85 +* VERSION: 2.00 * -* DATE: 06 Mar 2020 +* DATE: 19 Jun 2022 * * Common header file for the exception handling routines. * @@ -26,5 +26,8 @@ INT exceptFilterWithLog( _In_ UINT ExceptionCode, _In_opt_ EXCEPTION_POINTERS* ExceptionPointers); +INT exceptFilterUnhandled( + _In_ struct _EXCEPTION_POINTERS* ExceptionInfo); + #define WOBJ_EXCEPTION_FILTER exceptFilter(GetExceptionCode(), GetExceptionInformation()) #define WOBJ_EXCEPTION_FILTER_LOG exceptFilterWithLog(GetExceptionCode(), GetExceptionInformation()) diff --git a/Source/WinObjEx64/extapi.c b/Source/WinObjEx64/extapi.c index ed7b20ef..2f3ef816 100644 --- a/Source/WinObjEx64/extapi.c +++ b/Source/WinObjEx64/extapi.c @@ -1,12 +1,12 @@ /******************************************************************************* * -* (C) COPYRIGHT AUTHORS, 2017 - 2021 +* (C) COPYRIGHT AUTHORS, 2017 - 2022 * * TITLE: EXTAPI.C * -* VERSION: 1.92 +* VERSION: 2.00 * -* DATE: 30 Oct 2021 +* DATE: 19 Jun 2022 * * Support unit for pre Windows 10 missing APIs. * @@ -39,16 +39,26 @@ NTSTATUS ExApiSetInit( RtlSecureZeroMemory(&g_ExtApiSet, sizeof(g_ExtApiSet)); - // - // New Partition API introduced in Windows 10. - // + hNtdll = GetModuleHandle(TEXT("ntdll.dll")); if (hNtdll) { + // + // New Partition API introduced in Windows 10 TH1. + // g_ExtApiSet.NtOpenPartition = (pfnNtOpenPartition)GetProcAddress(hNtdll, "NtOpenPartition"); if (g_ExtApiSet.NtOpenPartition) { g_ExtApiSet.NumberOfAPI += 1; } + + // + // Available since Windows 10 REDSTONE 1. + // + g_ExtApiSet.NtOpenRegistryTransaction = (pfnNtOpenRegistryTransaction)GetProcAddress(hNtdll, "NtOpenRegistryTransaction"); + + if (g_ExtApiSet.NtOpenRegistryTransaction) { + g_ExtApiSet.NumberOfAPI += 1; + } } // diff --git a/Source/WinObjEx64/extapi.h b/Source/WinObjEx64/extapi.h index 77eccb3b..34b0ee48 100644 --- a/Source/WinObjEx64/extapi.h +++ b/Source/WinObjEx64/extapi.h @@ -1,14 +1,14 @@ /******************************************************************************* * -* (C) COPYRIGHT AUTHORS, 2015 - 2021 +* (C) COPYRIGHT AUTHORS, 2015 - 2022 * * TITLE: EXTAPI.H * -* VERSION: 1.92 +* VERSION: 2.00 * -* DATE: 30 Oct 2021 +* DATE: 19 Jun 2022 * -* Header for pre Windows10 missing API. +* Header for pre Windows 10+ missing API. * * THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF * ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED @@ -32,6 +32,11 @@ typedef NTSTATUS (NTAPI *pfnNtManagePartition)( _In_ ULONG PartitionInformationLength ); +typedef NTSTATUS (NTAPI *pfnNtOpenRegistryTransaction)( + _Out_ PHANDLE RegistryHandle, + _In_ ACCESS_MASK DesiredAccess, + _In_ POBJECT_ATTRIBUTES ObjectAttributes); + typedef BOOL (WINAPI *pfnIsImmersiveProcess)( HANDLE hProcess ); @@ -48,11 +53,12 @@ typedef UINT (WINAPI *pfnGetDpiForWindow)( typedef UINT (WINAPI *pfnGetDpiForSystem)( VOID); -#define EXTAPI_ALL_MAPPED 6 +#define EXTAPI_ALL_MAPPED 7 typedef struct _EXTENDED_API_SET { ULONG NumberOfAPI; pfnNtOpenPartition NtOpenPartition; + pfnNtOpenRegistryTransaction NtOpenRegistryTransaction; pfnIsImmersiveProcess IsImmersiveProcess; pfnGetThreadDpiAwarenessContext GetThreadDpiAwarenessContext; pfnGetAwarenessFromDpiAwarenessContext GetAwarenessFromDpiAwarenessContext; diff --git a/Source/WinObjEx64/extras/extras.c b/Source/WinObjEx64/extras/extras.c index 26f892e4..a2fc838a 100644 --- a/Source/WinObjEx64/extras/extras.c +++ b/Source/WinObjEx64/extras/extras.c @@ -4,9 +4,9 @@ * * TITLE: EXTRAS.C * -* VERSION: 1.94 +* VERSION: 2.00 * -* DATE: 07 Jun 2022 +* DATE: 19 Jun 2022 * * THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF * ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED @@ -16,15 +16,7 @@ *******************************************************************************/ #include "global.h" #include "extras.h" -#include "extrasUSD.h" -#include "extrasPN.h" -#include "extrasSSDT.h" -#include "extrasDrivers.h" -#include "extrasIPC.h" -#include "extrasPSList.h" -#include "extrasCallbacks.h" -#include "extrasSL.h" -#include "extrasCmOpt.h" +#include "extrasHandlers.h" /* * extrasHandleSettingsChange @@ -155,7 +147,7 @@ VOID extrasProcessElevationRequiredDialogs( g_kdctx.DriverContext.LoadStatus, g_kdctx.DriverContext.OpenStatus); - MessageBox(g_WinObj.MainWindow, + MessageBox(g_hwndMain, szText, PROGRAM_NAME, MB_ICONINFORMATION); diff --git a/Source/WinObjEx64/extras/extras.h b/Source/WinObjEx64/extras/extras.h index ad8b5f8d..d028f768 100644 --- a/Source/WinObjEx64/extras/extras.h +++ b/Source/WinObjEx64/extras/extras.h @@ -4,9 +4,9 @@ * * TITLE: EXTRAS.H * -* VERSION: 1.94 +* VERSION: 2.00 * -* DATE: 07 Jun 2022 +* DATE: 19 Jun 2022 * * Common header file for Extras dialogs. * diff --git a/Source/WinObjEx64/extras/extrasCallbacks.c b/Source/WinObjEx64/extras/extrasCallbacks.c index 8f97e545..5ed02493 100644 --- a/Source/WinObjEx64/extras/extrasCallbacks.c +++ b/Source/WinObjEx64/extras/extrasCallbacks.c @@ -4,9 +4,9 @@ * * TITLE: EXTRASCALLBACKS.C * -* VERSION: 1.94 +* VERSION: 2.00 * -* DATE: 04 Jun 2022 +* DATE: 19 Jun 2022 * * THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF * ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED @@ -16,7 +16,6 @@ *******************************************************************************/ #include "global.h" #include "extras.h" -#include "extrasCallbacks.h" #include "extras/extrasCallbacksPatterns.h" #include "treelist/treelist.h" #include "hde/hde64.h" @@ -215,16 +214,16 @@ OBEX_CALLBACK_DISPATCH_ENTRY g_CallbacksDispatchTable[] = { &g_SystemCallbacks.IopNotifyLastChanceShutdownQueueHead }, { - 0, L"ObProcess", + ObjectTypeProcess, L"ObProcess", QueryCallbackGeneric, DumpObCallbacks, FindObjectTypeCallbackListHeadByType, &g_SystemCallbacks.ObProcessCallbackHead }, { - 1, L"ObThread", + ObjectTypeThread, L"ObThread", QueryCallbackGeneric, DumpObCallbacks, FindObjectTypeCallbackListHeadByType, &g_SystemCallbacks.ObThreadCallbackHead }, { - 2, L"ObDesktop", + ObjectTypeDesktop, L"ObDesktop", QueryCallbackGeneric, DumpObCallbacks, FindObjectTypeCallbackListHeadByType, &g_SystemCallbacks.ObDesktopCallbackHead }, @@ -1290,9 +1289,6 @@ BOOL FindIopFileSystemQueueHeads( case 3: *IopTapeFileSystemQueueHead = kvarAddress; break; - - default: - break; } Count += 1; if (Count == 4) @@ -1342,9 +1338,6 @@ BOOL FindIopFileSystemQueueHeads( case 3: *IopTapeFileSystemQueueHead = kvarAddress; break; - - default: - break; } Count += 1; if (Count == 4) @@ -1713,13 +1706,14 @@ OBEX_FINDCALLBACK_ROUTINE(FindSeFileSystemNotifyRoutinesHead) */ OBEX_FINDCALLBACK_ROUTINE(FindObjectTypeCallbackListHeadByType) { - ULONG Type = (ULONG)QueryFlags; ULONG_PTR ListHead = 0; ULONG ObjectSize, ObjectVersion = 0, CallbackListOffset = 0; LPWSTR TypeName = NULL; - POBJINFO CurrentObject = NULL; + POBEX_OBJECT_INFORMATION CurrentObject = NULL; PVOID ObjectTypeInformation = NULL; + UNICODE_STRING usName; + union { union { OBJECT_TYPE_7 *ObjectType_7; @@ -1730,25 +1724,31 @@ OBEX_FINDCALLBACK_ROUTINE(FindObjectTypeCallbackListHeadByType) PVOID Ref; } ObjectType; - switch (Type) { - case 0: //PsProcessType + switch ((WOBJ_OBJECT_TYPE)(ULONG)QueryFlags) { + case ObjectTypeProcess: //PsProcessType TypeName = TEXT("Process"); break; - case 1: //PsThreadType + case ObjectTypeThread: //PsThreadType TypeName = TEXT("Thread"); break; - case 2: + case ObjectTypeDesktop: //ExDesktopObjectType TypeName = TEXT("Desktop"); break; default: + // + // We cannot process this object type. + // return 0; } // // Get the reference to the object. // - CurrentObject = ObQueryObject(T_OBJECTTYPES, TypeName); + RtlInitUnicodeString(&usName, TypeName); + CurrentObject = ObQueryObjectInDirectory(&usName, + ObGetPredefinedUnicodeString(OBP_OBTYPES)); + if (CurrentObject == NULL) return 0; @@ -5510,7 +5510,7 @@ VOID SysCbDialogOnInit( extrasSetDlgIcon(pDlgContext); SetWindowText(hwndDlg, TEXT("System Callbacks")); - GetClientRect(g_WinObj.MainWindow, &rc); + GetClientRect(g_hwndMain, &rc); pDlgContext->TreeList = CreateWindowEx(WS_EX_STATICEDGE, WC_TREELIST, NULL, WS_VISIBLE | WS_CHILD | WS_TABSTOP | TLSTYLE_COLAUTOEXPAND | TLSTYLE_LINKLINES, 12, 14, rc.right - 24, rc.bottom - 24, hwndDlg, NULL, NULL, NULL); @@ -5534,7 +5534,7 @@ VOID SysCbDialogOnInit( SysCbDialogContentRefresh(hwndDlg, pDlgContext, FALSE); } - supCenterWindowSpecifyParent(hwndDlg, g_WinObj.MainWindow); + supCenterWindowSpecifyParent(hwndDlg, g_hwndMain); SendMessage(hwndDlg, WM_SIZE, 0, 0); } diff --git a/Source/WinObjEx64/extras/extrasCallbacksPatterns.h b/Source/WinObjEx64/extras/extrasCallbacksPatterns.h index 2fb23491..e247ae8b 100644 --- a/Source/WinObjEx64/extras/extrasCallbacksPatterns.h +++ b/Source/WinObjEx64/extras/extrasCallbacksPatterns.h @@ -4,9 +4,9 @@ * * TITLE: EXTRASCALLBACKSPATTERNS.H * -* VERSION: 1.94 +* VERSION: 2.00 * -* DATE: 28 May 2022 +* DATE: 19 Jun 2022 * * Header with search patterns used by Callbacks dialog routines. * diff --git a/Source/WinObjEx64/extras/extrasCmOpt.c b/Source/WinObjEx64/extras/extrasCmOpt.c index e685e5d8..67de4cce 100644 --- a/Source/WinObjEx64/extras/extrasCmOpt.c +++ b/Source/WinObjEx64/extras/extrasCmOpt.c @@ -4,9 +4,9 @@ * * TITLE: EXTRASCMOPT.C * -* VERSION: 1.94 +* VERSION: 2.00 * -* DATE: 04 Jun 2022 +* DATE: 19 Jun 2022 * * THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF * ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED @@ -242,10 +242,8 @@ BOOL CALLBACK CmOptDlgHandleNotify( _In_ EXTRASCONTEXT* Context ) { - BOOL bHandled = TRUE; INT nImageIndex; - if (NMListView->hdr.idFrom != ID_EXTRASLIST) return FALSE; @@ -272,14 +270,10 @@ BOOL CALLBACK CmOptDlgHandleNotify( Context->lvColumnToSort, nImageIndex); - break; - - default: - bHandled = FALSE; - break; + return TRUE; } - return bHandled; + return FALSE; } /* @@ -640,7 +634,7 @@ VOID CmOptDlgOnInit( SendMessage(hwndDlg, WM_SIZE, 0, 0); SetFocus(pDlgContext->ListView); - supCenterWindowSpecifyParent(hwndDlg, g_WinObj.MainWindow); + supCenterWindowSpecifyParent(hwndDlg, g_hwndMain); } /* diff --git a/Source/WinObjEx64/extras/extrasCmOpt.h b/Source/WinObjEx64/extras/extrasCmOpt.h deleted file mode 100644 index c905d435..00000000 --- a/Source/WinObjEx64/extras/extrasCmOpt.h +++ /dev/null @@ -1,23 +0,0 @@ -/******************************************************************************* -* -* (C) COPYRIGHT AUTHORS, 2022 -* -* TITLE: EXTRASCMOPT.H -* -* VERSION: 1.94 -* -* DATE: 04 Jun 2022 -* -* Common header file for Configuration Manager options dialog. -* -* THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF -* ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED -* TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A -* PARTICULAR PURPOSE. -* -*******************************************************************************/ - -#pragma once - -VOID extrasCreateCmOptDialog( - VOID); diff --git a/Source/WinObjEx64/extras/extrasDrivers.c b/Source/WinObjEx64/extras/extrasDrivers.c index e0435242..a81993b8 100644 --- a/Source/WinObjEx64/extras/extrasDrivers.c +++ b/Source/WinObjEx64/extras/extrasDrivers.c @@ -4,9 +4,9 @@ * * TITLE: EXTRASDRIVERS.C * -* VERSION: 1.94 +* VERSION: 2.00 * -* DATE: 04 Jun 2022 +* DATE: 19 Jun 2022 * * THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF * ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED @@ -16,14 +16,15 @@ *******************************************************************************/ #include "global.h" #include "extras.h" -#include "extrasDrivers.h" BOOLEAN DrvDlgShimsEnabled = FALSE; -#define ID_DRVLIST_DUMP 40001 -#define ID_DRVLIST_SAVE 40002 -#define ID_DRVLIST_PROP ID_OBJECT_PROPERTIES -#define ID_DRVLIST_REFRESH ID_VIEW_REFRESH +#define ID_DRVLIST_REFRESH ID_VIEW_REFRESH +#define ID_DRVLIST_PROP ID_OBJECT_PROPERTIES +#define ID_DRVLIST_DUMP 40005 +#define ID_DRVLIST_DUMPFIXED 40006 +#define ID_DRVLIST_SAVE 40007 + #define ID_CALC_HASH_MD5 6000 #define ID_CALC_HASH_SHA1 6001 @@ -45,6 +46,8 @@ BOOLEAN DrvDlgShimsEnabled = FALSE; #define COLUMN_DRVLIST_UNLOADED_END_ADDRESS 2 #define COLUMN_DRVLIST_UNLOADED_CURRENT_TIME 3 +#define T_DUMPDRIVER L"Dump Driver (Raw)" +#define T_DUMPDRIVER_FIXED L"Dump Driver (Fix Sections)" #define DRVLISTDLG_TRACKSIZE_MIN_X 640 #define DRVLISTDLG_TRACKSIZE_MIN_Y 480 @@ -110,7 +113,7 @@ VOID DrvListCopyHash( lpszHash = ComputeHashForFile(&fvi, (MenuId == ID_CALC_HASH_PAGE_SHA1) ? BCRYPT_SHA1_ALGORITHM : BCRYPT_SHA256_ALGORITHM, PAGE_SIZE, - g_WinObj.Heap, + g_obexHeap, TRUE); } @@ -119,7 +122,7 @@ VOID DrvListCopyHash( lpszHash = ComputeHashForFile(&fvi, CryptAlgoIdRef[MenuId - ID_CALC_HASH_MD5], PAGE_SIZE, - g_WinObj.Heap, + g_obexHeap, FALSE); } @@ -232,6 +235,7 @@ VOID DrvHandlePopupMenu( InsertMenu(hMenu, ++uPos, MF_BYPOSITION | MF_SEPARATOR, 0, NULL); if (kdConnectDriver()) { InsertMenu(hMenu, ++uPos, MF_BYCOMMAND, ID_DRVLIST_DUMP, T_DUMPDRIVER); + InsertMenu(hMenu, ++uPos, MF_BYCOMMAND, ID_DRVLIST_DUMPFIXED, T_DUMPDRIVER_FIXED); } InsertMenu(hMenu, ++uPos, MF_BYCOMMAND, ID_JUMPTOFILE, T_JUMPTOFILE); @@ -320,6 +324,244 @@ VOID DrvListViewProperties( } } +static HANDLE DumpDialogThreadHandle = NULL; +static HANDLE DumpWorkerThread = NULL; +static FAST_EVENT DumpDialogInitializedEvent = FAST_EVENT_INIT; +volatile LONG TerminateDumpOperation = FALSE; +HWND DumpWorkerWindow = NULL; + +typedef struct _OBEX_DRVDUMP { + _In_ BOOL FixSections; + _In_ ULONG DumpSize; + _In_ ULONG_PTR DumpAddress; + _In_ PBYTE Buffer; + _In_ HWND ParentWindow; + _Out_ ULONG ReadSize; + _Out_ NTSTATUS DumpStatus; + _In_ WCHAR FileName[MAX_PATH * 2]; +} OBEX_DRVDUMP, * POBEX_DRVDUMP; + +DWORD DrvDumpThread( + _In_ PVOID Parameter +) +{ + OBEX_DRVDUMP* dumpInfo = (POBEX_DRVDUMP)Parameter; + + PBYTE buffer; + ULONG_PTR dumpAddress; + ULONG totalSize = dumpInfo->DumpSize, readBytes = 0, i, remainingBytes, memIO = 0; + + for (i = 0, + buffer = dumpInfo->Buffer, + dumpAddress = dumpInfo->DumpAddress; + (i < (totalSize / PAGE_SIZE)); + i++, + dumpAddress += PAGE_SIZE, + buffer = (PBYTE)RtlOffsetToPointer(buffer, PAGE_SIZE)) + { + + if (TerminateDumpOperation) { + dumpInfo->DumpStatus = STATUS_CANCELLED; + return ERROR_CANCELLED; + } + + kdReadSystemMemoryEx(dumpAddress, buffer, PAGE_SIZE, &memIO); + readBytes = InterlockedAdd((LONG*)&dumpInfo->ReadSize, memIO); + } + + remainingBytes = totalSize % PAGE_SIZE; + if (remainingBytes) { + kdReadSystemMemoryEx(dumpAddress, buffer, remainingBytes, &memIO); + readBytes = InterlockedAdd((LONG*)&dumpInfo->ReadSize, memIO); + } + + if (readBytes == 0) { + dumpInfo->DumpStatus = STATUS_UNSUCCESSFUL; + } + else if (readBytes != totalSize) { + dumpInfo->DumpStatus = STATUS_PARTIAL_COPY; + } + else { + dumpInfo->DumpStatus = STATUS_SUCCESS; + } + + NtClose(DumpWorkerThread); + DumpWorkerThread = NULL; + + PostMessage(dumpInfo->ParentWindow, WM_CLOSE, (WPARAM)0, (LPARAM)0); + return ERROR_SUCCESS; +} + +VOID DumpTerminateWorker( + VOID +) +{ + if (DumpWorkerThread) { + _InterlockedExchange((LONG*)&TerminateDumpOperation, TRUE); + if (WaitForSingleObject(DumpWorkerThread, 20*1000) == WAIT_TIMEOUT) { + TerminateThread(DumpWorkerThread, ERROR_CANCELLED); + NtClose(DumpWorkerThread); + DumpWorkerThread = NULL; + } + } +} + +#define DUMP_PROP L"dumpProp" + +VOID DumpUpdateTimerProc( + HWND hwnd, + UINT uMsg, + UINT_PTR idEvent, + DWORD dwTime) +{ + UNREFERENCED_PARAMETER(uMsg); + UNREFERENCED_PARAMETER(idEvent); + UNREFERENCED_PARAMETER(dwTime); + + OBEX_DRVDUMP* dumpInfo; + HWND hwndProgress = GetDlgItem(hwnd, IDC_PROGRESS); + WCHAR szBuffer[100]; + + dumpInfo = (OBEX_DRVDUMP*)GetProp(hwnd, DUMP_PROP); + + if (dumpInfo) { + + szBuffer[0] = 0; + + RtlStringCchPrintfSecure(szBuffer, + RTL_NUMBER_OF(szBuffer), + TEXT("Reading %lu (%lu Kb) of %lu (%lu Kb)"), + dumpInfo->ReadSize, + dumpInfo->ReadSize / 1024, + dumpInfo->DumpSize, + dumpInfo->DumpSize / 1024); + + SetWindowText(hwndProgress, szBuffer); + } +} + +INT_PTR CALLBACK DrvDumpProgressDialogProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam +) +{ + OBEX_DRVDUMP* dumpInfo; + + switch (uMsg) { + + case WM_INITDIALOG: + dumpInfo = (POBEX_DRVDUMP)lParam; + if (dumpInfo) { + SetProp(hwndDlg, DUMP_PROP, (HANDLE)dumpInfo); + supCenterWindowSpecifyParent(hwndDlg, dumpInfo->ParentWindow); + dumpInfo->ParentWindow = hwndDlg; + _InterlockedExchange((LONG*)&TerminateDumpOperation, FALSE); + SetTimer(hwndDlg, 1, 300, DumpUpdateTimerProc); + DumpWorkerThread = supCreateThread(DrvDumpThread, (PVOID)dumpInfo, 0); + } + break; + + case WM_DESTROY: + PostQuitMessage(0); + break; + + case WM_COMMAND: + + switch (GET_WM_COMMAND_ID(wParam, lParam)) { + case IDCANCEL: + RemoveProp(hwndDlg, DUMP_PROP); + DumpTerminateWorker(); + KillTimer(hwndDlg, 1); + return DestroyWindow(hwndDlg); + } + } + return 0; +} + +DWORD DumpDialogWorkerThread( + _In_ PVOID Parameter +) +{ + BOOL bResult; + MSG message; + OBEX_DRVDUMP* dumpInfo = (POBEX_DRVDUMP)Parameter; + HWND hwndDlg, hwndParent = dumpInfo->ParentWindow; + + SIZE_T bytesIO; + WCHAR szBuffer[100]; + + hwndDlg = CreateDialogParam(g_WinObj.hInstance, + MAKEINTRESOURCE(IDD_DIALOG_PROGRESS), + 0, + (DLGPROC)&DrvDumpProgressDialogProc, + (LPARAM)dumpInfo); + + DumpWorkerWindow = hwndDlg; + + SetWindowText(hwndDlg, TEXT("Driver dump")); + + supSetFastEvent(&DumpDialogInitializedEvent); + + do { + + bResult = GetMessage(&message, NULL, 0, 0); + if (bResult == -1) + break; + + if (!IsDialogMessage(hwndDlg, &message)) { + TranslateMessage(&message); + DispatchMessage(&message); + } + + } while (bResult != 0); + + if (NT_SUCCESS(dumpInfo->DumpStatus) || (dumpInfo->DumpStatus == STATUS_PARTIAL_COPY)) { + + if (dumpInfo->FixSections) + supImageFixSections(dumpInfo->Buffer); + + bytesIO = supWriteBufferToFile(dumpInfo->FileName, dumpInfo->Buffer, + (SIZE_T)dumpInfo->DumpSize, FALSE, FALSE); + + RtlStringCchPrintfSecure(szBuffer, RTL_NUMBER_OF(szBuffer), + TEXT("Read %lu (%lu Kb), Write %lu (%lu Kb), Requested %lu (%lu Kb)"), + dumpInfo->ReadSize, + dumpInfo->ReadSize / 1024, + bytesIO, + bytesIO / 1024, + dumpInfo->DumpSize, + dumpInfo->DumpSize / 1024); + + } + else if (dumpInfo->DumpStatus == STATUS_CANCELLED) { + _strcpy(szBuffer, TEXT("Operation cancelled by user")); + } + else { + _strcpy(szBuffer, TEXT("Error while dumping memory")); + } + + supStatusBarSetText( + GetDlgItem(hwndParent, ID_EXTRASLIST_STATUSBAR), + 1, + szBuffer); + + if (dumpInfo->Buffer) { + supHeapFree(dumpInfo->Buffer); + supHeapFree(dumpInfo); + } + + supResetFastEvent(&DumpDialogInitializedEvent); + + if (DumpDialogThreadHandle) { + NtClose(DumpDialogThreadHandle); + DumpDialogThreadHandle = NULL; + } + + return 0; +} + /* * DrvDumpDriver * @@ -329,31 +571,36 @@ VOID DrvListViewProperties( * */ VOID DrvDumpDriver( - _In_ EXTRASCONTEXT* Context + _In_ EXTRASCONTEXT* Context, + _In_ BOOL FixSections ) { - BOOL bSuccess = FALSE; - INT iPos; - ULONG ImageSize; - SIZE_T sz; - LPWSTR lpDriverName = NULL; - PVOID DumpedDrv = NULL; - ULONG_PTR ImageBase = 0; - WCHAR szBuffer[MAX_PATH * 2], szDriverDumpInfo[MAX_TEXT_CONVERSION_ULONG64 + 1]; + INT nSelected; + SIZE_T sz; + LPWSTR lpDriverName = NULL; + WCHAR szBuffer[MAX_PATH * 2], szDriverDumpInfo[MAX_TEXT_CONVERSION_ULONG64]; + OBEX_DRVDUMP* DumpInfo; + ULONG_PTR dumpAddress; + ULONG dumpSize; + + if (DumpDialogThreadHandle) { + return; + } do { + // // Remember selected index. // - iPos = ListView_GetNextItem(Context->ListView, -1, LVNI_SELECTED); - if (iPos < 0) + nSelected = ListView_GetNextItem(Context->ListView, -1, LVNI_SELECTED); + if (nSelected < 0) break; // // Query selected driver name. // sz = 0; - lpDriverName = supGetItemText(Context->ListView, iPos, 1, &sz); + lpDriverName = supGetItemText(Context->ListView, nSelected, 1, &sz); if (lpDriverName == NULL) break; @@ -372,13 +619,13 @@ VOID DrvDumpDriver( RtlSecureZeroMemory(szDriverDumpInfo, sizeof(szDriverDumpInfo)); supGetItemText2( Context->ListView, - iPos, + nSelected, COLUMN_DRVLIST_DRIVER_ADDRESS, szDriverDumpInfo, MAX_TEXT_CONVERSION_ULONG64); - ImageBase = hextou64(&szDriverDumpInfo[2]); - if (ImageBase < g_kdctx.SystemRangeStart) + dumpAddress = hextou64(&szDriverDumpInfo[2]); + if (dumpAddress < g_kdctx.SystemRangeStart) break; // @@ -387,46 +634,33 @@ VOID DrvDumpDriver( RtlSecureZeroMemory(szDriverDumpInfo, sizeof(szDriverDumpInfo)); supGetItemText2( Context->ListView, - iPos, + nSelected, COLUMN_DRVLIST_SIZE, szDriverDumpInfo, MAX_TEXT_CONVERSION_ULONG64); - ImageSize = _strtoul(szDriverDumpInfo); - if (ImageSize == 0) + dumpSize = _strtoul(szDriverDumpInfo); + if (dumpSize == 0) break; - // - // Allocate buffer for dump and read kernel memory. - // - DumpedDrv = supVirtualAlloc((SIZE_T)ImageSize); - if (DumpedDrv) { - - supSetWaitCursor(TRUE); - - // - // Ignore read errors during dump. - // - bSuccess = kdReadSystemMemory(ImageBase, DumpedDrv, ImageSize); - supSetWaitCursor(FALSE); - - if (supWriteBufferToFile(szBuffer, DumpedDrv, ImageSize, FALSE, FALSE) == ImageSize) - _strcpy(szBuffer, TEXT("Driver saved to disk")); - else - _strcpy(szBuffer, TEXT("Driver save to disk error")); - - // - // Free allocated buffer. - // - supVirtualFree(DumpedDrv); - - _strcat(szBuffer, TEXT(", kernel memory read was ")); - if (bSuccess) - _strcat(szBuffer, TEXT("successful")); - else - _strcat(szBuffer, TEXT("partially successful")); + DumpInfo = (OBEX_DRVDUMP*)supHeapAlloc(sizeof(OBEX_DRVDUMP)); + if (DumpInfo == NULL) + break; - supStatusBarSetText(Context->StatusBar, 1, (LPWSTR)&szBuffer); + DumpInfo->Buffer = supHeapAlloc(dumpSize); + if (DumpInfo->Buffer == NULL) { + supHeapFree(DumpInfo); + break; + } + DumpInfo->FixSections = FixSections; + _strcpy(DumpInfo->FileName, szBuffer); + DumpInfo->DumpAddress = dumpAddress; + DumpInfo->DumpSize = dumpSize; + DumpInfo->ParentWindow = Context->hwndDlg; + + DumpDialogThreadHandle = supCreateThread(DumpDialogWorkerThread, (PVOID)DumpInfo, 0); + if (DumpDialogThreadHandle) { + supWaitForFastEvent(&DumpDialogInitializedEvent, NULL); } } while (FALSE); @@ -591,8 +825,8 @@ VOID DrvListUnloadedDrivers( _In_ BOOLEAN bRefresh ) { - HWND hwndList = Context->ListView; - WCHAR szBuffer[100]; + HWND hwndList = Context->ListView; + WCHAR szBuffer[100]; if (bRefresh) { ListView_DeleteAllItems(hwndList); @@ -631,14 +865,14 @@ VOID DrvListDrivers( _In_ BOOLEAN bRefresh ) { - INT lvItemIndex; - ULONG i; + INT lvItemIndex; + ULONG i; - PCHAR lpDriverName; - HWND hwndList = Context->ListView; + PCHAR lpDriverName; + HWND hwndList = Context->ListView; LVITEM lvitem; - WCHAR szBuffer[MAX_PATH + 1]; + WCHAR szBuffer[MAX_PATH + 1]; RTL_PROCESS_MODULES* pModulesList = NULL; PRTL_PROCESS_MODULE_INFORMATION pModule; @@ -778,7 +1012,6 @@ BOOL CALLBACK DrvDlgHandleNotify( _In_ EXTRASCONTEXT* Context ) { - BOOL bHandled = TRUE; INT nImageIndex; @@ -828,11 +1061,10 @@ BOOL CALLBACK DrvDlgHandleNotify( break; default: - bHandled = FALSE; - break; + return FALSE; } - return bHandled; + return TRUE; } /* @@ -872,7 +1104,10 @@ VOID DrvDlgHandleWMCommand( break; case ID_DRVLIST_DUMP: - DrvDumpDriver(pDlgContext); + DrvDumpDriver(pDlgContext, FALSE); + break; + case ID_DRVLIST_DUMPFIXED: + DrvDumpDriver(pDlgContext, TRUE); break; case ID_JUMPTOFILE: @@ -972,7 +1207,7 @@ VOID DrvDlgOnInit( }; SetProp(hwndDlg, T_DLGCONTEXT, (HANDLE)lParam); - supCenterWindowSpecifyParent(hwndDlg, g_WinObj.MainWindow); + supCenterWindowSpecifyParent(hwndDlg, g_hwndMain); pDlgContext->hwndDlg = hwndDlg; pDlgContext->lvColumnHit = -1; @@ -1127,6 +1362,10 @@ INT_PTR CALLBACK DrvDlgProc( } } + if (DumpWorkerWindow) { + SendMessage(DumpWorkerWindow, WM_CLOSE, 0, 0); + DumpWorkerWindow = NULL; + } DestroyWindow(hwndDlg); break; diff --git a/Source/WinObjEx64/extras/extrasDrivers.h b/Source/WinObjEx64/extras/extrasDrivers.h deleted file mode 100644 index 8cdbf4e9..00000000 --- a/Source/WinObjEx64/extras/extrasDrivers.h +++ /dev/null @@ -1,22 +0,0 @@ -/******************************************************************************* -* -* (C) COPYRIGHT AUTHORS, 2016 - 2022 -* -* TITLE: EXTRASDRIVERS.H -* -* VERSION: 1.94 -* -* DATE: 04 Jun 2022 -* -* Common header file for Drivers dialog. -* -* THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF -* ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED -* TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A -* PARTICULAR PURPOSE. -* -*******************************************************************************/ -#pragma once - -VOID extrasCreateDriversDialog( - _In_ DRIVERS_DLG_MODE Mode); diff --git a/Source/WinObjEx64/extras/extrasSSDT.h b/Source/WinObjEx64/extras/extrasHandlers.h similarity index 50% rename from Source/WinObjEx64/extras/extrasSSDT.h rename to Source/WinObjEx64/extras/extrasHandlers.h index 449dd499..0d8b2491 100644 --- a/Source/WinObjEx64/extras/extrasSSDT.h +++ b/Source/WinObjEx64/extras/extrasHandlers.h @@ -2,13 +2,13 @@ * * (C) COPYRIGHT AUTHORS, 2015 - 2022 * -* TITLE: EXTRASSSDT.H +* TITLE: EXTRAS.H * -* VERSION: 1.94 +* VERSION: 2.00 * -* DATE: 04 Jun 2022 +* DATE: 19 Jun 2022 * -* Common header file for Service Table dialog. +* Common header file for Extras dialogs handlers. * * THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF * ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED @@ -16,7 +16,32 @@ * PARTICULAR PURPOSE. * *******************************************************************************/ + #pragma once +VOID extrasCreateCallbacksDialog( + VOID); + +VOID extrasCreateCmOptDialog( + VOID); + +VOID extrasCreateDriversDialog( + _In_ DRIVERS_DLG_MODE Mode); + +VOID extrasCreateIpcDialog( + _In_ IPC_DLG_MODE Mode); + +VOID extrasCreatePNDialog( + VOID); + +VOID extrasCreatePsListDialog( + VOID); + +VOID extrasCreateSLCacheDialog( + VOID); + VOID extrasCreateSSDTDialog( _In_ SSDT_DLG_MODE Mode); + +VOID extrasCreateUsdDialog( + VOID); diff --git a/Source/WinObjEx64/extras/extrasIPC.c b/Source/WinObjEx64/extras/extrasIPC.c index ec515ce1..3cd5e9ec 100644 --- a/Source/WinObjEx64/extras/extrasIPC.c +++ b/Source/WinObjEx64/extras/extrasIPC.c @@ -4,9 +4,9 @@ * * TITLE: EXTRASIPC.C * -* VERSION: 1.94 +* VERSION: 2.00 * -* DATE: 04 Jun 2022 +* DATE: 19 Jun 2022 * * IPC supported: Pipes, Mailslots * @@ -18,9 +18,8 @@ *******************************************************************************/ #include "global.h" #include "extras.h" -#include "extrasIPC.h" #include "propDlg.h" -#include "propSecurity.h" +#include "props.h" //mailslot root #define DEVICE_MAILSLOT L"\\Device\\Mailslot\\" @@ -94,10 +93,6 @@ LPWSTR IpcCreateObjectPathWithName( LPWSTR lpFullName = NULL, lpRootDirectory = NULL; SIZE_T sz; - if (lpObjectName == NULL) { - return NULL; - } - sz = (1 + _strlen(lpObjectName)) * sizeof(WCHAR); switch (Mode) { @@ -109,9 +104,8 @@ LPWSTR IpcCreateObjectPathWithName( sz += DEVICE_MAILSLOT_LENGTH; lpRootDirectory = DEVICE_MAILSLOT; break; - default: - break; } + if (lpRootDirectory) { lpFullName = (LPWSTR)supHeapAlloc(sz); if (lpFullName == NULL) { @@ -120,6 +114,7 @@ LPWSTR IpcCreateObjectPathWithName( _strcpy(lpFullName, lpRootDirectory); _strcat(lpFullName, lpObjectName); } + return lpFullName; } @@ -132,30 +127,21 @@ LPWSTR IpcCreateObjectPathWithName( * */ BOOL CALLBACK IpcOpenObjectMethod( - _In_ PROP_OBJECT_INFO* Context, - _Inout_ PHANDLE phObject, - _In_ ACCESS_MASK DesiredAccess + _In_ PROP_OBJECT_INFO* Context, + _Inout_ PHANDLE phObject, + _In_ ACCESS_MASK DesiredAccess ) { BOOL bResult = FALSE; HANDLE hObject; NTSTATUS status; OBJECT_ATTRIBUTES obja; - UNICODE_STRING uStr; IO_STATUS_BLOCK iost; - if ( - (Context == NULL) || - (phObject == NULL) - ) - { - return bResult; - } *phObject = NULL; - RtlInitUnicodeString(&uStr, Context->lpCurrentObjectPath); - InitializeObjectAttributes(&obja, &uStr, OBJ_CASE_INSENSITIVE, NULL, NULL); hObject = NULL; + InitializeObjectAttributes(&obja, &Context->NtObjectPath, OBJ_CASE_INSENSITIVE, NULL, NULL); status = NtOpenFile(&hObject, DesiredAccess, &obja, &iost, FILE_SHARE_VALID_FLAGS, FILE_NON_DIRECTORY_FILE); @@ -168,37 +154,6 @@ BOOL CALLBACK IpcOpenObjectMethod( return bResult; } -/* -* IpcVerifyContextParameter -* -* Purpose: -* -* Sanity check of PROP_OBJECT_INFO context. -* -*/ -BOOLEAN IpcVerifyContextParameter( - _In_ PROP_OBJECT_INFO* Context, - _In_ HWND hwndDlg, - _In_ IPC_DLG_MODE DialogMode) -{ - if (Context == NULL) { - SetLastError(ERROR_NOT_ENOUGH_MEMORY); - IpcDisplayError(hwndDlg, DialogMode); - return FALSE; - } - if ( - (Context->lpObjectName == NULL) || - (Context->lpCurrentObjectPath == NULL) - ) - { - SetLastError(ERROR_OBJECT_NOT_FOUND); - IpcDisplayError(hwndDlg, DialogMode); - return FALSE; - } - - return TRUE; -} - /* * IpcMailslotQueryInfo * @@ -219,12 +174,6 @@ VOID IpcMailslotQueryInfo( FILE_MAILSLOT_QUERY_INFORMATION fmqi; - // - // Verify context. - // - if (!IpcVerifyContextParameter(Context, hwndDlg, IpcModeMailSlots)) - return; - hMailslot = NULL; if (!IpcOpenObjectMethod(Context, &hMailslot, GENERIC_READ)) { //on error display last win32 error @@ -232,7 +181,10 @@ VOID IpcMailslotQueryInfo( return; } - SetDlgItemText(hwndDlg, ID_MAILSLOT_FULLPATH, Context->lpCurrentObjectPath); + supDisplayCurrentObjectPath( + GetDlgItem(hwndDlg, ID_MAILSLOT_FULLPATH), + &Context->NtObjectPath, + FALSE); RtlSecureZeroMemory(&fmqi, sizeof(fmqi)); status = NtQueryInformationFile(hMailslot, &iost, &fmqi, sizeof(fmqi), FileMailslotQueryInformation); @@ -281,17 +233,13 @@ VOID IpcPipeQueryInfo( LPWSTR lpType; HANDLE hPipe; NTSTATUS status; - WCHAR szBuffer[MAX_PATH]; + WCHAR szBuffer[64]; IO_STATUS_BLOCK iost; FILE_PIPE_LOCAL_INFORMATION fpli; - // - // Verify context. - // - if (!IpcVerifyContextParameter(Context, hwndDlg, IpcModeNamedPipes)) - return; - - SetDlgItemText(hwndDlg, ID_PIPE_FULLPATH, Context->lpCurrentObjectPath); + supDisplayCurrentObjectPath(GetDlgItem(hwndDlg, ID_PIPE_FULLPATH), + &Context->NtObjectPath, + FALSE); //open pipe hPipe = NULL; @@ -401,7 +349,7 @@ INT_PTR CALLBACK IpcTypeDialogProc( SetProp(hwndDlg, T_PROPCONTEXT, (HANDLE)pSheet->lParam); Context = (PROP_OBJECT_INFO*)pSheet->lParam; if (Context) { - pDlgContext = (EXTRASCONTEXT*)Context->Tag; + pDlgContext = (EXTRASCONTEXT*)Context->ExtrasContext; if (pDlgContext) { hIcon = ImageList_GetIcon(pDlgContext->ImageList, @@ -425,7 +373,7 @@ INT_PTR CALLBACK IpcTypeDialogProc( if (wParam) { Context = (PROP_OBJECT_INFO*)GetProp(hwndDlg, T_PROPCONTEXT); if (Context) { - pDlgContext = (EXTRASCONTEXT*)Context->Tag; + pDlgContext = (EXTRASCONTEXT*)Context->ExtrasContext; if (pDlgContext) { switch (pDlgContext->DialogMode) { case IpcModeMailSlots: @@ -434,8 +382,6 @@ INT_PTR CALLBACK IpcTypeDialogProc( case IpcModeNamedPipes: IpcPipeQueryInfo(Context, hwndDlg); break; - default: - break; } } } @@ -446,7 +392,7 @@ INT_PTR CALLBACK IpcTypeDialogProc( case WM_DESTROY: Context = (PROP_OBJECT_INFO*)RemoveProp(hwndDlg, T_PROPCONTEXT); if (Context) { - pDlgContext = (EXTRASCONTEXT*)Context->Tag; + pDlgContext = (EXTRASCONTEXT*)Context->ExtrasContext; if (pDlgContext) { DestroyIcon(pDlgContext->ObjectIcon); pDlgContext->ObjectIcon = NULL; @@ -472,21 +418,35 @@ VOID IpcDlgShowProperties( _In_ EXTRASCONTEXT* pDlgContext ) { - INT nPages = 0; + INT nPages = 0; PROP_OBJECT_INFO* Context; - HPROPSHEETPAGE SecurityPage = NULL; - PROPSHEETPAGE Page; - PROPSHEETHEADER PropHeader; - WCHAR szCaption[MAX_PATH]; + HPROPSHEETPAGE SecurityPage = NULL; + PROPSHEETPAGE Page; + PROPSHEETHEADER PropHeader; + WCHAR szCaption[MAX_PATH]; + PROP_CONFIG propConfig; + + LPWSTR objectName, objectPathCombined; + UNICODE_STRING objectPathNt; + + RtlSecureZeroMemory(&propConfig, sizeof(propConfig)); + propConfig.ContextType = propNormal; + propConfig.ObjectTypeIndex = ObjectTypeFile; - Context = propContextCreate(NULL, OBTYPE_NAME_FILE, NULL, NULL); + objectName = supGetItemText(pDlgContext->ListView, iItem, 0, NULL); + objectPathCombined = IpcCreateObjectPathWithName(objectName, + (IPC_DLG_MODE)pDlgContext->DialogMode); + + RtlInitUnicodeString(&objectPathNt, objectPathCombined); + propConfig.NtObjectPath = &objectPathNt; + + Context = propContextCreate(&propConfig); if (Context == NULL) return; - Context->lpObjectName = supGetItemText(pDlgContext->ListView, iItem, 0, NULL); - Context->lpCurrentObjectPath = IpcCreateObjectPathWithName(Context->lpObjectName, - (IPC_DLG_MODE)pDlgContext->DialogMode); - Context->Tag = (ULONG_PTR)pDlgContext; + Context->ExtrasContext = (PVOID)pDlgContext; + + supHeapFree(objectName); RtlSecureZeroMemory(&IpcPages, sizeof(IpcPages)); // @@ -826,7 +786,7 @@ VOID IpcDlgOnInit( EXTRASCONTEXT* pDlgContext = (EXTRASCONTEXT*)lParam; SetProp(hwndDlg, T_IPCDLGCONTEXT, (HANDLE)lParam); - supCenterWindowSpecifyParent(hwndDlg, g_WinObj.MainWindow); + supCenterWindowSpecifyParent(hwndDlg, g_hwndMain); pDlgContext->lvColumnHit = -1; pDlgContext->lvItemHit = -1; diff --git a/Source/WinObjEx64/extras/extrasIPC.h b/Source/WinObjEx64/extras/extrasIPC.h deleted file mode 100644 index 8953b45f..00000000 --- a/Source/WinObjEx64/extras/extrasIPC.h +++ /dev/null @@ -1,22 +0,0 @@ -/******************************************************************************* -* -* (C) COPYRIGHT AUTHORS, 2017 - 2022 -* -* TITLE: EXTRASIPC.H -* -* VERSION: 1.94 -* -* DATE: 04 Jun 2022 -* -* Common header file for InterProcess Communication mechanisms dialog. -* -* THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF -* ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED -* TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A -* PARTICULAR PURPOSE. -* -*******************************************************************************/ -#pragma once - -VOID extrasCreateIpcDialog( - _In_ IPC_DLG_MODE Mode); diff --git a/Source/WinObjEx64/extras/extrasPN.c b/Source/WinObjEx64/extras/extrasPN.c index 1f4112cd..c2387770 100644 --- a/Source/WinObjEx64/extras/extrasPN.c +++ b/Source/WinObjEx64/extras/extrasPN.c @@ -4,9 +4,9 @@ * * TITLE: EXTRASPN.C * -* VERSION: 1.94 +* VERSION: 2.00 * -* DATE: 05 Jun 2022 +* DATE: 19 Jun 2022 * * THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF * ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED @@ -16,7 +16,6 @@ *******************************************************************************/ #include "global.h" #include "extras.h" -#include "extrasPN.h" #include "propDlg.h" EXTRASCONTEXT PnDlgContext; @@ -47,7 +46,7 @@ static FAST_EVENT PnDlgInitializedEvent = FAST_EVENT_INIT; VOID PNDlgResetOutput() { SetDlgItemText(PnDlgContext.hwndDlg, ID_NAMESPACE_ROOT, T_EmptyString); - SetDlgItemText(PnDlgContext.hwndDlg, ID_OBJECT_ADDR, T_EmptyString); + SetDlgItemText(PnDlgContext.hwndDlg, ID_NAMESPACE_ADDR, T_EmptyString); SetDlgItemText(PnDlgContext.hwndDlg, ID_SIZEOFBOUNDARYINFO, T_EmptyString); SetDlgItemText(PnDlgContext.hwndDlg, ID_BDESCRIPTOR_ADDRESS, T_EmptyString); SetDlgItemText(PnDlgContext.hwndDlg, ID_BDESCRIPTOR_NAME, T_EmptyString); @@ -70,17 +69,15 @@ VOID PNDlgShowObjectProperties( _In_ INT iItem ) { - LPWSTR lpType, lpName; - POBJREF objRef = NULL; - - OBJREFPNS pnsInfo; + POBJREF objRef = NULL; + OBJREFPNS pnsInfo; PROP_NAMESPACE_INFO propNamespace; - PROP_DIALOG_CREATE_SETTINGS propSettings; + PROP_CONFIG propConfig; // // Only one namespace object properties dialog at the same time allowed. // - ENSURE_DIALOG_UNIQUE(g_NamespacePropWindow); + supCloseKnownPropertiesDialog(propGetNamespaceWindow()); // // Get ref to object, failure here is critical. @@ -88,7 +85,7 @@ VOID PNDlgShowObjectProperties( if (!supGetListViewItemParam(PnDlgContext.ListView, iItem, (PVOID*)&objRef)) return; - RtlCopyMemory(&pnsInfo, &objRef->PrivateNamespace, sizeof(OBJREFPNS)); + pnsInfo = objRef->PrivateNamespace; RtlSecureZeroMemory(&propNamespace, sizeof(propNamespace)); propNamespace.ObjectAddress = objRef->ObjectAddress; @@ -103,23 +100,14 @@ VOID PNDlgShowObjectProperties( return; } - lpName = supGetItemText(PnDlgContext.ListView, iItem, 0, NULL); - if (lpName) { - lpType = supGetItemText(PnDlgContext.ListView, iItem, 1, NULL); - if (lpType) { - - RtlSecureZeroMemory(&propSettings, sizeof(propSettings)); - - propSettings.lpObjectName = lpName; - propSettings.lpObjectType = lpType; - propSettings.NamespaceObject = &propNamespace; - - propCreateDialog(&propSettings); + RtlSecureZeroMemory(&propConfig, sizeof(propConfig)); - supHeapFree(lpType); - } - supHeapFree(lpName); - } + propConfig.ContextType = propPrivateNamespace; + propConfig.NtObjectName = &objRef->Name; + propConfig.ObjectTypeIndex = objRef->ObjectTypeIndex; + propConfig.u1.NamespaceObject = &propNamespace; + propConfig.hwndParent = PnDlgContext.hwndDlg; + propCreateDialog(&propConfig); // // propNamespace.BoundaryDescriptor will be freed by propDestroyContext. @@ -171,31 +159,42 @@ BOOL CALLBACK PNDlgEnumerateCallback( _In_opt_ PVOID Context ) { - INT lvItemIndex; - UINT ConvertedTypeIndex; - LPCWSTR TypeName; + BOOL bNeedFree; + INT lvItemIndex; + WOBJ_OBJECT_TYPE objectTypeIndex; - LVITEM lvItem; - WCHAR szBuffer[MAX_PATH + 1]; + LVITEM lvItem; + WCHAR szBuffer[MAX_PATH + 1]; + + UNICODE_STRING objectName; + WOBJ_TYPE_DESC* typeDesc; UNREFERENCED_PARAMETER(Context); - ConvertedTypeIndex = supGetObjectNameIndexByTypeIndex((PVOID)Entry->ObjectAddress, Entry->TypeIndex); - TypeName = ObManagerGetNameByIndex(ConvertedTypeIndex); + bNeedFree = supNormalizeUnicodeStringForDisplay(PNSObjectsHeap, + &Entry->Name, + &objectName); + + if (!bNeedFree) + objectName = Entry->Name; + + objectTypeIndex = supGetObjectNameIndexByTypeIndex((PVOID)Entry->ObjectAddress, Entry->TypeIndex); + typeDesc = ObManagerGetEntryByTypeIndex(objectTypeIndex); + Entry->ObjectTypeIndex = objectTypeIndex; //Name RtlSecureZeroMemory(&lvItem, sizeof(lvItem)); lvItem.mask = LVIF_TEXT | LVIF_IMAGE | LVIF_PARAM; lvItem.iItem = MAXINT; - lvItem.iImage = ObManagerGetImageIndexByTypeIndex(ConvertedTypeIndex); - lvItem.pszText = Entry->ObjectName; + lvItem.iImage = typeDesc->ImageIndex; + lvItem.pszText = objectName.Buffer; lvItem.lParam = (LPARAM)Entry; lvItemIndex = ListView_InsertItem(PnDlgContext.ListView, &lvItem); //Type lvItem.mask = LVIF_TEXT; lvItem.iSubItem = 1; - lvItem.pszText = (LPWSTR)TypeName; + lvItem.pszText = typeDesc->Name; lvItem.iItem = lvItemIndex; ListView_SetItem(PnDlgContext.ListView, &lvItem); @@ -211,6 +210,12 @@ BOOL CALLBACK PNDlgEnumerateCallback( PNSNumberOfObjects += 1; + if (bNeedFree) { + supFreeDuplicatedUnicodeString(PNSObjectsHeap, + &objectName, + FALSE); + } + return FALSE; } @@ -423,8 +428,6 @@ BOOL CALLBACK PNDlgBoundaryDescriptorCallback( SetDlgItemText(hwndDlg, ID_INTEGRITYLABEL, szBuffer); break; - default: - break; } return FALSE; } @@ -460,7 +463,7 @@ VOID PNDlgShowNamespaceInfo( if (!supGetListViewItemParam(PnDlgContext.ListView, iItem, (PVOID*)&objRef)) return; - RtlCopyMemory(&pnsInfo, &objRef->PrivateNamespace, sizeof(OBJREFPNS)); + pnsInfo = objRef->PrivateNamespace; // // Boundary Descriptor Entries. @@ -488,7 +491,7 @@ VOID PNDlgShowNamespaceInfo( szBuffer[0] = L'0'; szBuffer[1] = L'x'; u64tohex(pnsInfo.NamespaceLookupEntry, &szBuffer[2]); - SetDlgItemText(hwndDlg, ID_OBJECT_ADDR, szBuffer); + SetDlgItemText(hwndDlg, ID_NAMESPACE_ADDR, szBuffer); // // SizeOfBoundaryInformation. @@ -603,8 +606,6 @@ VOID PNDlgHandleNotify( PNDlgShowObjectProperties(pListView->iItem); break; - default: - break; } } @@ -647,10 +648,10 @@ VOID PNDialogCreateDataHeap( ) { if (bRefresh) { - if (PNSObjectsHeap) RtlDestroyHeap(PNSObjectsHeap); + if (PNSObjectsHeap) supDestroyHeap(PNSObjectsHeap); } - PNSObjectsHeap = RtlCreateHeap(HEAP_GROWABLE, NULL, 0, 0, NULL, NULL); - if (PNSObjectsHeap) RtlSetHeapInformation(PNSObjectsHeap, HeapEnableTerminationOnCorruption, NULL, 0); + + PNSObjectsHeap = supCreateHeap(HEAP_GROWABLE, TRUE); } /* @@ -690,6 +691,8 @@ VOID PNDialogShowInfo( SetDlgItemText(PnDlgContext.hwndDlg, ID_PNAMESPACESINFO, T_NAMESPACE_QUERY_FAILED); } } + + SetFocus(PnDlgContext.ListView); } /* @@ -750,8 +753,8 @@ VOID PNDialogOnClose( _In_ HWND hwndDlg ) { + if (PNSObjectsHeap) supDestroyHeap(PNSObjectsHeap); DestroyWindow(hwndDlg); - if (PNSObjectsHeap) RtlDestroyHeap(PNSObjectsHeap); } /* @@ -766,7 +769,7 @@ VOID PNDialogOnInit( _In_ HWND hwndDlg ) { - supCenterWindowSpecifyParent(hwndDlg, g_WinObj.MainWindow); + supCenterWindowSpecifyParent(hwndDlg, g_hwndMain); } /* @@ -869,6 +872,28 @@ INT_PTR CALLBACK PNDialogProc( return FALSE; } +/* +* PNSubDlgMsgHandler +* +* Purpose: +* +* Check window message against existing properties dialog. +* +*/ +BOOL PNSubDlgMsgHandler( + _In_ LPMSG lpMsg +) +{ + HWND hwnd; + + hwnd = propGetNamespaceWindow(); + if (hwnd != NULL) + if (PropSheet_IsDialogMessage(hwnd, lpMsg)) + return TRUE; + + return FALSE; +} + /* * extrasPNDialogWorkerThread * @@ -952,6 +977,9 @@ DWORD extrasPNDialogWorkerThread( if (bResult == -1) break; + if (PNSubDlgMsgHandler(&message)) + continue; + if (IsDialogMessage(hwndDlg, &message)) { TranslateAccelerator(hwndDlg, acceleratorTable, &message); } diff --git a/Source/WinObjEx64/extras/extrasPN.h b/Source/WinObjEx64/extras/extrasPN.h deleted file mode 100644 index 0c3d9808..00000000 --- a/Source/WinObjEx64/extras/extrasPN.h +++ /dev/null @@ -1,22 +0,0 @@ -/******************************************************************************* -* -* (C) COPYRIGHT AUTHORS, 2015 - 2022 -* -* TITLE: EXTRASPN.H -* -* VERSION: 1.94 -* -* DATE: 04 Jun 2022 -* -* Common header file for Extras Private Namespaces dialog. -* -* THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF -* ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED -* TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A -* PARTICULAR PURPOSE. -* -*******************************************************************************/ -#pragma once - -VOID extrasCreatePNDialog( - VOID); diff --git a/Source/WinObjEx64/extras/extrasPSList.c b/Source/WinObjEx64/extras/extrasPSList.c index 763cdcbe..c53912d8 100644 --- a/Source/WinObjEx64/extras/extrasPSList.c +++ b/Source/WinObjEx64/extras/extrasPSList.c @@ -4,9 +4,9 @@ * * TITLE: EXTRASPSLIST.C * -* VERSION: 1.94 +* VERSION: 2.00 * -* DATE: 06 Jun 2022 +* DATE: 19 Jun 2022 * * THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF * ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED @@ -18,9 +18,28 @@ #include "global.h" #include "propDlg.h" #include "extras.h" -#include "extrasPSList.h" #include "treelist/treelist.h" -#include "resource.h" + +#define PS_COLOR_CURRENT_USER 0xffd0d0 +#define PS_COLOR_SERVICE 0xd0d0ff +#define PS_COLOR_IMMERSIVE 0xeaea00 +#define PS_COLOR_PROTECTED 0xe6ffe6 + +#define PSLIST_CELLS_COUNT 3 + +#define PSLIST_PID_CELL 0 +#define PSLIST_OBJECT_CELL 1 +#define PSLIST_USER_CELL 2 + +typedef struct _TL_SUBITEMS_PSLIST { + ULONG Count; + ULONG ColorFlags; + COLORREF BgColor; + COLORREF FontColor; + PVOID UserParam; + LPTSTR CustomTooltip; + LPTSTR Text[PSLIST_CELLS_COUNT]; +} TL_SUBITEMS_PSLIST, * PTL_SUBITEMS_PSLIST; #define Y_SPLITTER_SIZE 4 #define Y_SPLITTER_MIN 200 @@ -96,6 +115,89 @@ static LPWSTR T_WAITREASON[] = { L"WrPhysicalFault" }; +typedef struct _LEGEND_MAP { + UINT Control; + UINT Color; +} LEGEND_MAP, * PLEGEND_MAP; + +LEGEND_MAP LegendControls[] = { + { IDC_PCTL_USERPROCESS, PS_COLOR_CURRENT_USER }, + { IDC_PCTL_SERVICE_PROCES, PS_COLOR_SERVICE }, + { IDC_PCTL_IMMERSIVE_PROCESS, PS_COLOR_IMMERSIVE }, + { IDC_PCTL_PROTECTED_PROCESS, PS_COLOR_PROTECTED } +}; + +INT_PTR CALLBACK PsLegendDialogProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam +) +{ + UINT i; + HDC hdc; + HWND hwndControl; + PAINTSTRUCT paint; + RECT rect; + HBRUSH hb; + UNREFERENCED_PARAMETER(lParam); + + switch (uMsg) { + + case WM_INITDIALOG: + supCenterWindow(hwndDlg); + return TRUE; + + case WM_COMMAND: + if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL) { + return EndDialog(hwndDlg, TRUE); + } + break; + + case WM_CLOSE: + EndDialog(hwndDlg, TRUE); + return TRUE; + + case WM_PAINT: + hdc = BeginPaint(hwndDlg, &paint); + if (hdc) { + + for (i = 0; i < RTL_NUMBER_OF(LegendControls); i++) { + + hwndControl = GetDlgItem(hwndDlg, LegendControls[i].Control); + if (hwndControl) { + RtlSecureZeroMemory(&rect, sizeof(rect)); + GetClientRect(hwndControl, (LPRECT)&rect); + MapWindowPoints(hwndControl, hwndDlg, (LPPOINT)&rect, 2); + hb = CreateSolidBrush(LegendControls[i].Color); + if (hb) { + FillRect(paint.hdc, &rect, hb); + DeleteObject(hb); + } + } + + } + EndPaint(hwndDlg, &paint); + } + + break; + } + + return 0; +} + +VOID PsShowLegendDialog( + _In_ HWND hwndParent +) +{ + DialogBoxParam(g_WinObj.hInstance, + MAKEINTRESOURCE(IDD_DIALOG_PSLISTLEGEND), + hwndParent, + PsLegendDialogProc, + 0); + +} + /* * PsxAllocateUnnamedObjectEntry * @@ -116,8 +218,8 @@ PROP_UNNAMED_OBJECT_INFO* PsxAllocateUnnamedObjectEntry( if (Data == NULL) return NULL; - objectEntry = (PROP_UNNAMED_OBJECT_INFO*)RtlAllocateHeap(g_PsListHeap, - HEAP_ZERO_MEMORY, sizeof(PROP_UNNAMED_OBJECT_INFO)); + objectEntry = (PROP_UNNAMED_OBJECT_INFO*)supHeapAllocEx(g_PsListHeap, + sizeof(PROP_UNNAMED_OBJECT_INFO)); if (objectEntry == NULL) return NULL; @@ -129,9 +231,9 @@ PROP_UNNAMED_OBJECT_INFO* PsxAllocateUnnamedObjectEntry( objectEntry->ClientId.UniqueThread = NULL; objectEntry->ImageName.MaximumLength = processEntry->ImageName.MaximumLength; - objectEntry->ImageName.Buffer = (PWSTR)RtlAllocateHeap(g_PsListHeap, - HEAP_ZERO_MEMORY, + objectEntry->ImageName.Buffer = (PWSTR)supHeapAllocEx(g_PsListHeap, objectEntry->ImageName.MaximumLength); + if (objectEntry->ImageName.Buffer) { RtlCopyUnicodeString(&objectEntry->ImageName, &processEntry->ImageName); } @@ -139,11 +241,8 @@ PROP_UNNAMED_OBJECT_INFO* PsxAllocateUnnamedObjectEntry( else if (ObjectType == ObjectTypeThread) { threadEntry = (PSYSTEM_THREAD_INFORMATION)Data; - - objectEntry->ClientId.UniqueProcess = threadEntry->ClientId.UniqueProcess; - objectEntry->ClientId.UniqueThread = threadEntry->ClientId.UniqueThread; - - RtlCopyMemory(&objectEntry->ThreadInformation, Data, sizeof(SYSTEM_THREAD_INFORMATION)); + objectEntry->ClientId = threadEntry->ClientId; + objectEntry->ThreadInformation = *threadEntry; } return objectEntry; } @@ -329,7 +428,7 @@ PROP_UNNAMED_OBJECT_INFO* PsListGetObjectEntry( { INT nSelected; TVITEMEX itemex; - TL_SUBITEMS_FIXED* subitems = NULL; + TL_SUBITEMS_PSLIST* subitems = NULL; PROP_UNNAMED_OBJECT_INFO* ObjectEntry = NULL; if (bTreeList) { @@ -370,20 +469,24 @@ VOID PsListHandleObjectProp( { SIZE_T sz; LPWSTR lpName; + HWND hwndParent; HANDLE UniqueProcessId = NULL, ObjectHandle = NULL; PUNICODE_STRING ImageName = NULL; PROP_UNNAMED_OBJECT_INFO* tempEntry; - PROP_DIALOG_CREATE_SETTINGS propSettings; + PROP_CONFIG propConfig; + UNICODE_STRING usObjectName; - // - // Only one process/thread properties dialog at the same time allowed. - // - ENSURE_DIALOG_UNIQUE(g_PsPropWindow); if (bProcessList) { + // + // Only one process/thread properties dialog at the same time allowed. + // + supCloseKnownPropertiesDialog(propGetProcessesWindow()); + hwndParent = PsDlgContext.TreeList; + UniqueProcessId = ObjectEntry->ClientId.UniqueProcess; if (NT_SUCCESS(supOpenProcess( UniqueProcessId, @@ -397,6 +500,11 @@ VOID PsListHandleObjectProp( ImageName = &ObjectEntry->ImageName; } else { + // + // Only one process/thread properties dialog at the same time allowed. + // + supCloseKnownPropertiesDialog(propGetThreadsWindow()); + hwndParent = PsDlgContext.ListView; tempEntry = PsListGetObjectEntry(TRUE, NULL); if (tempEntry) { @@ -447,13 +555,14 @@ VOID PsListHandleObjectProp( ultostr(HandleToULong(ObjectEntry->ClientId.UniqueThread), _strend(lpName)); } - RtlSecureZeroMemory(&propSettings, sizeof(propSettings)); - - propSettings.lpObjectName = lpName; - propSettings.lpObjectType = (bProcessList) ? OBTYPE_NAME_PROCESS : OBTYPE_NAME_THREAD; - propSettings.UnnamedObject = ObjectEntry; - - propCreateDialog(&propSettings); + RtlSecureZeroMemory(&propConfig, sizeof(propConfig)); + RtlInitUnicodeString(&usObjectName, lpName); + propConfig.NtObjectName = &usObjectName; + propConfig.ObjectTypeIndex = (bProcessList) ? ObjectTypeProcess : ObjectTypeThread; + propConfig.ContextType = propUnnamed; + propConfig.u1.UnnamedObject = ObjectEntry; + propConfig.hwndParent = hwndParent; + propCreateDialog(&propConfig); supHeapFree(lpName); } @@ -508,12 +617,12 @@ HTREEITEM AddProcessEntryTreeList( PSID processSid = NULL; HANDLE uniqueProcessId; PROP_UNNAMED_OBJECT_INFO* objectEntry; - TL_SUBITEMS_FIXED subitems; + TL_SUBITEMS_PSLIST subitems; ULONG cbCaption; - PWSTR lpCaption = NULL, lpEnd, lpUserName = NULL; + PWSTR lpCaption = NULL, lpValue, lpUserName = NULL; BOOL bIsProtected = FALSE; - WCHAR szEPROCESS[32]; + WCHAR szEPROCESS[32], szPid[32]; objectEntry = PsxAllocateUnnamedObjectEntry(Data, ObjectTypeProcess); if (objectEntry == NULL) @@ -539,25 +648,27 @@ HTREEITEM AddProcessEntryTreeList( lpCaption = (PWSTR)supHeapAlloc(cbCaption); if (lpCaption) { - lpEnd = _strcat(lpCaption, TEXT("[")); - ultostr(HandleToULong(uniqueProcessId), lpEnd); - _strcat(lpCaption, TEXT("]")); - - _strcat(lpCaption, TEXT(" ")); - if (uniqueProcessId == 0) { - _strcat(lpCaption, T_IDLE_PROCESS); + lpValue = T_IDLE_PROCESS; } else { if (objectEntry->ImageName.Buffer) { - _strcat(lpCaption, objectEntry->ImageName.Buffer); + lpValue = objectEntry->ImageName.Buffer; } else { - _strcat(lpCaption, T_Unknown); + lpValue = T_Unknown; } } + + _strcpy(lpCaption, lpValue); } + // + // PID + // + szPid[0] = 0; + ultostr(HandleToULong(uniqueProcessId), szPid); + // // EPROCESS value (can be NULL) // @@ -569,9 +680,11 @@ HTREEITEM AddProcessEntryTreeList( } subitems.UserParam = (PVOID)objectEntry; - subitems.Count = 2; - subitems.Text[0] = szEPROCESS; - subitems.Text[1] = T_EmptyString; + subitems.Count = PSLIST_CELLS_COUNT; + + subitems.Text[PSLIST_PID_CELL] = szPid; + subitems.Text[PSLIST_OBJECT_CELL] = szEPROCESS; + subitems.Text[PSLIST_USER_CELL] = T_EmptyString; // // Colors (set order is sensitive). @@ -590,7 +703,7 @@ HTREEITEM AddProcessEntryTreeList( ((processSid) && supIsLocalServiceSid(processSid))) { subitems.ColorFlags = TLF_BGCOLOR_SET; - subitems.BgColor = 0xd0d0ff; + subitems.BgColor = PS_COLOR_SERVICE; } } @@ -601,7 +714,7 @@ HTREEITEM AddProcessEntryTreeList( if (processSid && OurSid) { if (RtlEqualSid(OurSid, processSid)) { subitems.ColorFlags = TLF_BGCOLOR_SET; - subitems.BgColor = 0xffd0d0; + subitems.BgColor = PS_COLOR_CURRENT_USER; } } @@ -613,13 +726,13 @@ HTREEITEM AddProcessEntryTreeList( if (supIsImmersiveProcess(ProcessHandle)) { subitems.ColorFlags = TLF_BGCOLOR_SET; - subitems.BgColor = 0xeaea00; + subitems.BgColor = PS_COLOR_IMMERSIVE; } if (NT_SUCCESS(supIsProtectedProcess(ProcessHandle, &bIsProtected))) { if (bIsProtected) { subitems.ColorFlags = TLF_BGCOLOR_SET; - subitems.BgColor = 0xe6ffe6; + subitems.BgColor = PS_COLOR_PROTECTED; } } @@ -631,7 +744,7 @@ HTREEITEM AddProcessEntryTreeList( if (processSid && PolicyHandle) { if (supLookupSidUserAndDomainEx(processSid, PolicyHandle, &lpUserName)) { - subitems.Text[1] = lpUserName; + subitems.Text[PSLIST_USER_CELL] = lpUserName; } } @@ -676,9 +789,9 @@ BOOL CALLBACK FindItemMatchCallback( _In_ ULONG_PTR UserContext ) { - HANDLE ParentProcessId = (HANDLE)UserContext; - TL_SUBITEMS_FIXED* subitems = NULL; - TVITEMEX itemex; + HANDLE ParentProcessId = (HANDLE)UserContext; + TL_SUBITEMS_PSLIST* subitems = NULL; + TVITEMEX itemex; PROP_UNNAMED_OBJECT_INFO* Entry; @@ -793,9 +906,7 @@ LPWSTR PsListGetThreadStateAsString( case StateTransition: lpState = TEXT("Transition"); break; - case StateUnknown: - default: - break; + } _strcpy(StateBuffer, lpState); @@ -1104,8 +1215,8 @@ DWORD WINAPI CreateProcessListProc( ListView_DeleteAllItems(PsDlgContext.ListView); if (bRefresh) { - RtlDestroyHeap(g_PsListHeap); - g_PsListHeap = RtlCreateHeap(HEAP_GROWABLE, NULL, 0, 0, NULL, NULL); + supDestroyHeap(g_PsListHeap); + g_PsListHeap = supCreateHeap(HEAP_GROWABLE, TRUE); if (g_PsListHeap == NULL) { lpErrorMsg = TEXT("Could not allocate heap for process enumeration!"); supStatusBarSetText(PsDlgContext.StatusBar, 2, lpErrorMsg); @@ -1374,8 +1485,6 @@ INT_PTR PsListHandleNotify( return 1; - default: - break; } } @@ -1400,8 +1509,6 @@ INT_PTR PsListHandleNotify( } return 1; - default: - break; } } @@ -1447,6 +1554,7 @@ INT_PTR CALLBACK PsListDialogProc( INT dy; RECT crc; INT mark; + HMENU hMenu; HWND TreeListControl, FocusWindow; if (uMsg == g_WinObj.SettingsChangeMessage) { @@ -1489,7 +1597,7 @@ INT_PTR CALLBACK PsListDialogProc( case WM_SHOWWINDOW: if (wParam == TRUE) - supCenterWindowSpecifyParent(hwndDlg, g_WinObj.MainWindow); + supCenterWindowSpecifyParent(hwndDlg, g_hwndMain); break; case WM_COMMAND: @@ -1543,8 +1651,10 @@ INT_PTR CALLBACK PsListDialogProc( } break; - default: + case ID_VIEW_LEGEND: + PsShowLegendDialog(hwndDlg); break; + } break; @@ -1602,10 +1712,15 @@ INT_PTR CALLBACK PsListDialogProc( g_PsListWait = NULL; } + hMenu = GetMenu(hwndDlg); + if (hMenu) + DestroyMenu(hMenu); + DestroyWindow(PsDlgContext.TreeList); DestroyWindow(hwndDlg); + if (g_PsListHeap) { - RtlDestroyHeap(g_PsListHeap); + supDestroyHeap(g_PsListHeap); g_PsListHeap = NULL; } return TRUE; @@ -1618,6 +1733,38 @@ INT_PTR CALLBACK PsListDialogProc( return DefDlgProc(hwndDlg, uMsg, wParam, lParam); } +/* +* PsSubDlgMsgHandler +* +* Purpose: +* +* Check window message against existing dialogs. +* +*/ +BOOL PsSubDlgMsgHandler( + _In_ LPMSG lpMsg +) +{ + HWND hwnd; + + hwnd = propGetTokenWindow(); + if (hwnd != NULL) + if (PropSheet_IsDialogMessage(hwnd, lpMsg)) + return TRUE; + + hwnd = propGetProcessesWindow(); + if (hwnd != NULL) + if (PropSheet_IsDialogMessage(hwnd, lpMsg)) + return TRUE; + + hwnd = propGetThreadsWindow(); + if (hwnd != NULL) + if (PropSheet_IsDialogMessage(hwnd, lpMsg)) + return TRUE; + + return FALSE; +} + /* * extrasPsListDialogWorkerThread * @@ -1634,6 +1781,7 @@ DWORD extrasPsListDialogWorkerThread( HDITEM hdritem; WNDCLASSEX wincls; + HMENU hMenu; HWND hwndDlg; BOOL bResult; MSG message; @@ -1678,6 +1826,9 @@ DWORD extrasPsListDialogWorkerThread( if (hwndDlg) { + hMenu = LoadMenu(g_WinObj.hInstance, MAKEINTRESOURCE(IDR_PSLISTMENU)); + if (hMenu) SetMenu(hwndDlg, hMenu); + PsDlgContext.hwndDlg = hwndDlg; if (g_kdctx.IsFullAdmin == FALSE) { @@ -1718,13 +1869,17 @@ DWORD extrasPsListDialogWorkerThread( hdritem.pszText = TEXT("Process"); TreeList_InsertHeaderItem(PsDlgContext.TreeList, 0, &hdritem); + hdritem.cxy = 80; + hdritem.pszText = TEXT("PID"); + TreeList_InsertHeaderItem(PsDlgContext.TreeList, 1, &hdritem); + hdritem.cxy = 130; hdritem.pszText = TEXT("Object"); - TreeList_InsertHeaderItem(PsDlgContext.TreeList, 1, &hdritem); + TreeList_InsertHeaderItem(PsDlgContext.TreeList, 2, &hdritem); hdritem.cxy = 180; hdritem.pszText = TEXT("User"); - TreeList_InsertHeaderItem(PsDlgContext.TreeList, 2, &hdritem); + TreeList_InsertHeaderItem(PsDlgContext.TreeList, 3, &hdritem); wndStyles = GetWindowLongPtr(PsDlgContext.TreeList, GWL_STYLE); SetWindowLongPtr(PsDlgContext.TreeList, GWL_STYLE, wndStyles | TLSTYLE_LINKLINES); @@ -1737,7 +1892,7 @@ DWORD extrasPsListDialogWorkerThread( g_PsListWait = CreateMutex(NULL, FALSE, NULL); if (g_PsListWait) { - g_PsListHeap = RtlCreateHeap(HEAP_GROWABLE, NULL, 0, 0, NULL, NULL); + g_PsListHeap = supCreateHeap(HEAP_GROWABLE, TRUE); if (g_PsListHeap) { CreateObjectList(FALSE, NULL); } @@ -1757,6 +1912,9 @@ DWORD extrasPsListDialogWorkerThread( if (bResult == -1) break; + if (PsSubDlgMsgHandler(&message)) + continue; + if (IsDialogMessage(hwndDlg, &message)) { TranslateAccelerator(hwndDlg, acceleratorTable, &message); } diff --git a/Source/WinObjEx64/extras/extrasPSList.h b/Source/WinObjEx64/extras/extrasPSList.h deleted file mode 100644 index b40a473d..00000000 --- a/Source/WinObjEx64/extras/extrasPSList.h +++ /dev/null @@ -1,22 +0,0 @@ -/******************************************************************************* -* -* (C) COPYRIGHT AUTHORS, 2019 - 2022 -* -* TITLE: EXTRASPSLIST.H -* -* VERSION: 1.94 -* -* DATE: 04 Jun 2022 -* -* Common header file for Process List dialog. -* -* THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF -* ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED -* TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A -* PARTICULAR PURPOSE. -* -*******************************************************************************/ -#pragma once - -VOID extrasCreatePsListDialog( - VOID); diff --git a/Source/WinObjEx64/extras/extrasSL.c b/Source/WinObjEx64/extras/extrasSL.c index c289fdc8..41b989b4 100644 --- a/Source/WinObjEx64/extras/extrasSL.c +++ b/Source/WinObjEx64/extras/extrasSL.c @@ -4,9 +4,9 @@ * * TITLE: EXTRASSL.C * -* VERSION: 1.94 +* VERSION: 2.00 * -* DATE: 04 Jun 2022 +* DATE: 19 Jun 2022 * * THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF * ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED @@ -117,7 +117,7 @@ LPWSTR xxxSLCacheGetDescriptorDataType( _In_ SL_KMEM_CACHE_VALUE_DESCRIPTOR* CacheDescriptor ) { - LPWSTR DataType; + LPWSTR DataType = NULL; switch (CacheDescriptor->Type) { case SL_DATA_SZ: @@ -135,11 +135,8 @@ LPWSTR xxxSLCacheGetDescriptorDataType( case SL_DATA_SUM: DataType = TEXT("SL_DATA_SUM"); break; - - default: - DataType = NULL; - break; } + return DataType; } @@ -249,8 +246,6 @@ VOID SLCacheDialogDisplayDescriptorData( EnableWindow(GetDlgItem(hwndDlg, IDC_SLVALUE_VIEWWITH), TRUE); break; - default: - break; } } @@ -297,7 +292,7 @@ VOID SLCacheDialogViewBinaryData( TRUE, FALSE)) { - supShellExecInExplorerProcess(szFileName); + supShellExecInExplorerProcess(szFileName, NULL); } } @@ -555,7 +550,7 @@ VOID SLCacheDialogOnInit( }; SetProp(hwndDlg, T_DLGCONTEXT, (HANDLE)lParam); - supCenterWindowSpecifyParent(hwndDlg, g_WinObj.MainWindow); + supCenterWindowSpecifyParent(hwndDlg, g_hwndMain); pDlgContext->hwndDlg = hwndDlg; pDlgContext->lvItemHit = -1; diff --git a/Source/WinObjEx64/extras/extrasSL.h b/Source/WinObjEx64/extras/extrasSL.h deleted file mode 100644 index eaf41d91..00000000 --- a/Source/WinObjEx64/extras/extrasSL.h +++ /dev/null @@ -1,22 +0,0 @@ -/******************************************************************************* -* -* (C) COPYRIGHT AUTHORS, 2019 - 2022 -* -* TITLE: EXTRASSL.H -* -* VERSION: 1.94 -* -* DATE: 04 Jun 2022 -* -* Common header file for Software Licensing Cache dialog. -* -* THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF -* ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED -* TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A -* PARTICULAR PURPOSE. -* -*******************************************************************************/ -#pragma once - -VOID extrasCreateSLCacheDialog( - VOID); diff --git a/Source/WinObjEx64/extras/extrasSSDT.c b/Source/WinObjEx64/extras/extrasSSDT.c index 759bc7d5..49f111c7 100644 --- a/Source/WinObjEx64/extras/extrasSSDT.c +++ b/Source/WinObjEx64/extras/extrasSSDT.c @@ -4,9 +4,9 @@ * * TITLE: EXTRASSSDT.C * -* VERSION: 1.94 +* VERSION: 2.00 * -* DATE: 05 June 2022 +* DATE: 19 June 2022 * * THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF * ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED @@ -19,8 +19,19 @@ #include "extras.h" #include "ntos/ntldr.h" #include "ksymbols.h" -#include "extrasSSDT.h" -#include "extrasSSDTsup.h" + +typedef struct _SERVICETABLEENTRY { + ULONG ServiceId; + ULONG_PTR Address; + WCHAR Name[MAX_PATH + 1]; +} SERVICETABLEENTRY, * PSERVICETABLEENTRY; + +typedef struct _SDT_TABLE { + BOOL Allocated; + ULONG Limit; + ULONG_PTR Base; + PSERVICETABLEENTRY Table; +} SDT_TABLE, * PSDT_TABLE; // // UI part @@ -38,20 +49,54 @@ // // Globals // +#define INVALID_SERVICE_ENTRY_ID 0xFFFFFFFF +#define WIN32K_START_INDEX 0x1000 + SDT_TABLE KiServiceTable; SDT_TABLE W32pServiceTable; -SYMCONTEXT *W32SymContext; +SYMCONTEXT* W32SymContext; + +// +// Win32kApiSetTable signatures +// + +// +// InitializeWin32Call search pattern +// +// push rbp +// push r12 +// push r13 +// push r14 +// push r15 +// +BYTE g_pbInitializeWin32CallPattern[] = { + 0x55, 0x41, 0x54, 0x41, 0x55, 0x41, 0x56, 0x41, 0x57 +}; + +// +// Win32kApiSetTable adapter patterns +// +BYTE Win32kApiSetAdapterPattern1[] = { + 0x4C, 0x8B, 0x15 +}; +BYTE Win32kApiSetAdapterPattern2[] = { + 0x48, 0x8B, 0x05 +}; +BYTE Win32kApiSetAdapterPattern3[] = { + 0x4C, 0x8B, 0x1D // mov r11, value +}; + +W32K_API_SET_LOOKUP_PATTERN W32kApiSetAdapters[] = { + { sizeof(Win32kApiSetAdapterPattern1), Win32kApiSetAdapterPattern1 }, + { sizeof(Win32kApiSetAdapterPattern2), Win32kApiSetAdapterPattern2 }, + { sizeof(Win32kApiSetAdapterPattern3), Win32kApiSetAdapterPattern3 } +}; static EXTRASCONTEXT SSTDlgContext[SST_Max]; static HANDLE SdtDlgThreadHandles[SST_Max] = { NULL, NULL }; static FAST_EVENT SdtDlgInitializedEvents[SST_Max] = { FAST_EVENT_INIT, FAST_EVENT_INIT }; -VOID SdtListCreate( - _In_ HWND hwndDlg, - _In_ BOOL fRescan, - _In_ EXTRASCONTEXT* pDlgContext); - /* * SdtLoadWin32kImage * @@ -131,7 +176,7 @@ ULONG_PTR SdtQueryWin32kApiSetTable( (PVOID)hModule, &SectionSize); - if (SectionBase == 0 || SectionSize < 10) + if (SectionBase == 0 || SectionSize == 0) return 0; // @@ -154,7 +199,8 @@ ULONG_PTR SdtQueryWin32kApiSetTable( if (hs.flags & F_ERROR) break; - if (hs.len == IL_Win32kApiSetTable) { + // lea reg, Win32kApiSetTable + if (hs.len == 7) { if ((ptrCode[Index] == 0x4C) && (ptrCode[Index + 1] == 0x8D)) @@ -182,1742 +228,1740 @@ ULONG_PTR SdtQueryWin32kApiSetTable( } /* -* SdtDlgCompareFunc +* SdtListOutputTable * * Purpose: * -* KiServiceTable/W32pServiceTable Dialog listview comparer function. +* Output dumped and converted syscall table to listview. * */ -INT CALLBACK SdtDlgCompareFunc( - _In_ LPARAM lParam1, - _In_ LPARAM lParam2, - _In_ LPARAM lParamSort //pointer to EXTRASCALLBACK +VOID SdtListOutputTable( + _In_ HWND hwndDlg, + _In_ PRTL_PROCESS_MODULES Modules, + _In_ PSDT_TABLE SdtTableEntry ) { - INT nResult = 0; - - EXTRASCONTEXT* pDlgContext; - EXTRASCALLBACK* CallbackParam = (EXTRASCALLBACK*)lParamSort; + INT lvIndex; + ULONG i, iImage, moduleIndex = 0; + EXTRASCONTEXT* Context = (EXTRASCONTEXT*)GetProp(hwndDlg, T_DLGCONTEXT); - if (CallbackParam == NULL) - return 0; + LVITEM lvItem; + WCHAR szBuffer[MAX_PATH + 1]; - pDlgContext = &SSTDlgContext[CallbackParam->Value]; + LPWSTR lpBaseName, lpBaseLimit; - switch (pDlgContext->lvColumnToSort) { - case COLUMN_SDTLIST_INDEX: //index - return supGetMaxOfTwoULongFromString( - pDlgContext->ListView, - lParam1, - lParam2, - pDlgContext->lvColumnToSort, - pDlgContext->bInverseSort); - case COLUMN_SDTLIST_ADDRESS: //address (hex) - return supGetMaxOfTwoU64FromHex( - pDlgContext->ListView, - lParam1, - lParam2, - pDlgContext->lvColumnToSort, - pDlgContext->bInverseSort); - case COLUMN_SDTLIST_NAME: //string (fixed size) - case COLUMN_SDTLIST_MODULE: //string (fixed size) - return supGetMaxCompareTwoFixedStrings( - pDlgContext->ListView, - lParam1, - lParam2, - pDlgContext->lvColumnToSort, - pDlgContext->bInverseSort); + if (Context->DialogMode == SST_Ntos) { + lpBaseName = L"KiServiceTable"; + lpBaseLimit = L"KiServiceLimit"; + } + else if (Context->DialogMode == SST_Win32k) { + lpBaseName = L"W32pServiceTable"; + lpBaseLimit = L"W32pServiceLimit"; } + else + return; - return nResult; -} + RtlStringCchPrintfSecure(szBuffer, + MAX_PATH, + TEXT("%ws 0x%p / %ws %lu (0x%lX)"), + lpBaseName, + (PVOID)SdtTableEntry->Base, + lpBaseLimit, + SdtTableEntry->Limit, + SdtTableEntry->Limit); -/* -* SdtHandlePopupMenu -* -* Purpose: -* -* Table list popup construction. -* -*/ -VOID SdtHandlePopupMenu( - _In_ HWND hwndDlg, - _In_ LPPOINT lpPoint, - _In_ PVOID lpUserParam -) -{ - HMENU hMenu; - UINT uPos = 0; - EXTRASCONTEXT* Context = (EXTRASCONTEXT*)lpUserParam; + supStatusBarSetText(Context->StatusBar, 0, (LPWSTR)&szBuffer); - hMenu = CreatePopupMenu(); - if (hMenu) { + iImage = ObManagerGetImageIndexByTypeIndex(ObjectTypeDevice); - if (supListViewAddCopyValueItem(hMenu, - Context->ListView, - ID_OBJECT_COPY, - uPos, - lpPoint, - &Context->lvItemHit, - &Context->lvColumnHit)) - { - InsertMenu(hMenu, ++uPos, MF_BYPOSITION | MF_SEPARATOR, 0, NULL); - } + ListView_DeleteAllItems(Context->ListView); - InsertMenu(hMenu, uPos++, MF_BYCOMMAND, ID_JUMPTOFILE, T_JUMPTOFILE); - InsertMenu(hMenu, uPos++, MF_BYCOMMAND, ID_SDTLIST_SAVE, T_EXPORTTOFILE); - InsertMenu(hMenu, uPos++, MF_BYPOSITION | MF_SEPARATOR, 0, NULL); - InsertMenu(hMenu, uPos++, MF_BYCOMMAND, ID_VIEW_REFRESH, T_VIEW_REFRESH); + //list table + for (i = 0; i < SdtTableEntry->Limit; i++) { - TrackPopupMenu(hMenu, - TPM_RIGHTBUTTON | TPM_LEFTALIGN, - lpPoint->x, - lpPoint->y, - 0, - hwndDlg, - NULL); + RtlSecureZeroMemory(szBuffer, sizeof(szBuffer)); + ultostr(SdtTableEntry->Table[i].ServiceId, szBuffer); - DestroyMenu(hMenu); - } -} + //ServiceId + RtlSecureZeroMemory(&lvItem, sizeof(lvItem)); + lvItem.mask = LVIF_TEXT | LVIF_IMAGE; + lvItem.iItem = MAXINT; + lvItem.iImage = iImage; //imagelist id + lvItem.pszText = szBuffer; + lvIndex = ListView_InsertItem(Context->ListView, &lvItem); -/* -* SdtFreeGlobals -* -* Purpose: -* -* Release memory allocated for SDT table globals. -* -*/ -BOOL CALLBACK SdtFreeGlobals( - _In_opt_ PVOID Context -) -{ - UNREFERENCED_PARAMETER(Context); + //Name + lvItem.mask = LVIF_TEXT; + lvItem.iSubItem = 1; + lvItem.pszText = (LPWSTR)SdtTableEntry->Table[i].Name; + lvItem.iItem = lvIndex; + ListView_SetItem(Context->ListView, &lvItem); - if (KiServiceTable.Allocated) { - supHeapFree(KiServiceTable.Table); - KiServiceTable.Allocated = FALSE; - } - if (W32pServiceTable.Allocated) { - supHeapFree(W32pServiceTable.Table); - W32pServiceTable.Allocated = FALSE; - } + //Address + RtlSecureZeroMemory(szBuffer, sizeof(szBuffer)); + szBuffer[0] = L'0'; + szBuffer[1] = L'x'; + u64tohex(SdtTableEntry->Table[i].Address, &szBuffer[2]); - return TRUE; + lvItem.iSubItem = 2; + lvItem.pszText = szBuffer; + ListView_SetItem(Context->ListView, &lvItem); + + //Module + RtlSecureZeroMemory(szBuffer, sizeof(szBuffer)); + + if (ntsupFindModuleEntryByAddress( + Modules, + (PVOID)SdtTableEntry->Table[i].Address, + &moduleIndex)) + { + MultiByteToWideChar( + CP_ACP, + 0, + (LPCSTR)&Modules->Modules[moduleIndex].FullPathName, + (INT)_strlen_a((char*)Modules->Modules[moduleIndex].FullPathName), + szBuffer, + MAX_PATH); + } + else { + _strcpy(szBuffer, TEXT("Unknown Module")); + } + + lvItem.iSubItem = 3; + lvItem.pszText = szBuffer; + ListView_SetItem(Context->ListView, &lvItem); + } } /* -* SdtDlgHandleNotify +* SdtListCreateTable * * Purpose: * -* WM_NOTIFY processing for dialog listview. +* KiServiceTable dump routine. * */ -BOOL SdtDlgHandleNotify( - _In_ HWND hwndDlg, - _In_ LPARAM lParam +BOOL SdtListCreateTable( + VOID ) { - INT nImageIndex, iSelectionMark; - LPNMLISTVIEW pListView = (LPNMLISTVIEW)lParam; - LPWSTR lpItem; - HWND hwndListView; - - EXTRASCONTEXT* pDlgContext; + BOOL bResult = FALSE; + ULONG EntrySize = 0; + SIZE_T memIO; + PUTable TableDump = NULL; + PBYTE Module = NULL; + PIMAGE_EXPORT_DIRECTORY ExportDirectory = NULL; + PDWORD ExportNames, ExportFunctions; + PWORD NameOrdinals; - EXTRASCALLBACK CallbackParam; - WCHAR szBuffer[MAX_PATH + 1]; + PSERVICETABLEENTRY ServiceEntry; - if (pListView == NULL) - return FALSE; + CHAR* ServiceName; + CHAR* FunctionAddress; + ULONG ServiceId, i, j; - if (pListView->hdr.idFrom != ID_EXTRASLIST) - return FALSE; + __try { - hwndListView = pListView->hdr.hwndFrom; + if ((g_kdctx.Data->KeServiceDescriptorTable.Base == 0) || + (g_kdctx.Data->KeServiceDescriptorTable.Limit == 0)) + { + if (!kdFindKiServiceTable( + (ULONG_PTR)g_kdctx.NtOsImageMap, + (ULONG_PTR)g_kdctx.NtOsBase, + &g_kdctx.Data->KeServiceDescriptorTable)) + { + __leave; + } + } - switch (pListView->hdr.code) { + // + // If table empty, dump and prepare table + // + if (KiServiceTable.Allocated == FALSE) { - case LVN_COLUMNCLICK: + Module = (PBYTE)GetModuleHandle(TEXT("ntdll.dll")); - pDlgContext = (EXTRASCONTEXT*)GetProp(hwndDlg, T_DLGCONTEXT); - if (pDlgContext) { + if (Module == NULL) + __leave; - pDlgContext->bInverseSort = (~pDlgContext->bInverseSort) & 1; - pDlgContext->lvColumnToSort = pListView->iSubItem; - CallbackParam.lParam = (LPARAM)pDlgContext->lvColumnToSort; - CallbackParam.Value = pDlgContext->DialogMode; - ListView_SortItemsEx(hwndListView, &SdtDlgCompareFunc, (LPARAM)&CallbackParam); + ExportDirectory = (PIMAGE_EXPORT_DIRECTORY)RtlImageDirectoryEntryToData( + Module, + TRUE, + IMAGE_DIRECTORY_ENTRY_EXPORT, + &EntrySize); - nImageIndex = ImageList_GetImageCount(g_ListViewImages); - if (pDlgContext->bInverseSort) - nImageIndex -= 2; - else - nImageIndex -= 1; + if (ExportDirectory == NULL) { + __leave; + } - supUpdateLvColumnHeaderImage( - hwndListView, - pDlgContext->lvColumnCount, - pDlgContext->lvColumnToSort, - nImageIndex); - } - break; + ExportNames = (PDWORD)((PBYTE)Module + ExportDirectory->AddressOfNames); + ExportFunctions = (PDWORD)((PBYTE)Module + ExportDirectory->AddressOfFunctions); + NameOrdinals = (PWORD)((PBYTE)Module + ExportDirectory->AddressOfNameOrdinals); - case NM_DBLCLK: + memIO = sizeof(SERVICETABLEENTRY) * ExportDirectory->NumberOfNames; - iSelectionMark = ListView_GetSelectionMark(hwndListView); - if (iSelectionMark >= 0) { - lpItem = supGetItemText(hwndListView, iSelectionMark, 3, NULL); - if (lpItem) { - RtlSecureZeroMemory(szBuffer, sizeof(szBuffer)); - if (supGetWin32FileName(lpItem, szBuffer, MAX_PATH)) - supShowProperties(hwndDlg, szBuffer); - supHeapFree(lpItem); + KiServiceTable.Table = (PSERVICETABLEENTRY)supHeapAlloc(memIO); + if (KiServiceTable.Table == NULL) + __leave; + + KiServiceTable.Allocated = TRUE; + + if (!supDumpSyscallTableConverted( + g_kdctx.Data->KeServiceDescriptorTable.Base, + g_kdctx.Data->KeServiceDescriptorTable.Limit, + &TableDump)) + { + supHeapFree(KiServiceTable.Table); + KiServiceTable.Allocated = FALSE; + __leave; + } + + KiServiceTable.Base = g_kdctx.Data->KeServiceDescriptorTable.Base; + + // + // Walk for syscall stubs. + // + KiServiceTable.Limit = 0; + for (i = 0; i < ExportDirectory->NumberOfNames; i++) { + + ServiceName = ((CHAR*)Module + ExportNames[i]); + + // + // Use Zw alias to skip various Nt trash like NtdllDialogWndProc/NtGetTickCount. + // + + if (*(USHORT*)ServiceName == 'wZ') { + + MultiByteToWideChar( + CP_ACP, + 0, + ServiceName, + (INT)_strlen_a(ServiceName), + KiServiceTable.Table[KiServiceTable.Limit].Name, + MAX_PATH); + + //dirty hack + KiServiceTable.Table[KiServiceTable.Limit].Name[0] = L'N'; + KiServiceTable.Table[KiServiceTable.Limit].Name[1] = L't'; + + FunctionAddress = (CHAR*)((CHAR*)Module + ExportFunctions[NameOrdinals[i]]); + ServiceEntry = &KiServiceTable.Table[KiServiceTable.Limit]; + + if (*(UCHAR*)((UCHAR*)FunctionAddress + 3) == 0xB8) { + ServiceId = *(ULONG*)((UCHAR*)FunctionAddress + 4); + if (ServiceId < g_kdctx.Data->KeServiceDescriptorTable.Limit) { + ServiceEntry->ServiceId = ServiceId; + ServiceEntry->Address = TableDump[ServiceId]; + TableDump[ServiceId] = 0; + } + else { + kdDebugPrint(">>1 %s %lu\r\n", ServiceName, KiServiceTable.Limit); + ServiceEntry->ServiceId = INVALID_SERVICE_ENTRY_ID; + } + } + else { + kdDebugPrint(">>2 %s %lu\r\n", ServiceName, KiServiceTable.Limit); + ServiceEntry->ServiceId = INVALID_SERVICE_ENTRY_ID; + } + + KiServiceTable.Limit += 1; + + }//wZ + }//for + + for (i = 0; i < KiServiceTable.Limit; i++) { + ServiceEntry = &KiServiceTable.Table[i]; + if (ServiceEntry->ServiceId == INVALID_SERVICE_ENTRY_ID) { + for (j = 0; j < g_kdctx.Data->KeServiceDescriptorTable.Limit; j++) { + if (TableDump[j] != 0) { + ServiceEntry->ServiceId = j; + ServiceEntry->Address = TableDump[j]; + TableDump[j] = 0; + break; + } + } + } } + + supHeapFree(TableDump); + TableDump = NULL; } - break; - default: - return FALSE; + bResult = TRUE; + } + __finally { - return TRUE; + if (AbnormalTermination()) + supReportAbnormalTermination(__FUNCTIONW__); + + if (TableDump) { + supHeapFree(TableDump); + } + } + + return bResult; } /* -* SdtDlgOnInit +* ApiSetExtractReferenceFromAdapter * * Purpose: * -* KiServiceTable Dialog WM_INITDIALOG handler. +* Extract apiset reference from adapter code. * */ -VOID SdtDlgOnInit( - _In_ HWND hwndDlg, - _In_ LPARAM lParam +ULONG_PTR ApiSetExtractReferenceFromAdapter( + _In_ PBYTE ptrFunction ) { - INT iImage = ImageList_GetImageCount(g_ListViewImages) - 1; - EXTRASCONTEXT* pDlgContext = (EXTRASCONTEXT*)lParam; + BOOL bFound; + PBYTE ptrCode = ptrFunction; + ULONG Index = 0, i; + LONG Rel = 0; + hde64s hs; - INT SbParts[] = { 400, -1 }; - WCHAR szText[100]; + ULONG PatternSize; + PVOID PatternData; - LVCOLUMNS_DATA columnData[] = - { - { L"Id", 80, LVCFMT_LEFT | LVCFMT_BITMAP_ON_RIGHT, iImage }, - { L"Service Name", 280, LVCFMT_LEFT | LVCFMT_BITMAP_ON_RIGHT, I_IMAGENONE }, - { L"Address", 130, LVCFMT_LEFT | LVCFMT_BITMAP_ON_RIGHT, I_IMAGENONE }, - { L"Module", 220, LVCFMT_LEFT | LVCFMT_BITMAP_ON_RIGHT, I_IMAGENONE } - }; + do { + hde64_disasm((void*)(ptrCode + Index), &hs); + if (hs.flags & F_ERROR) + break; - SetProp(hwndDlg, T_DLGCONTEXT, (HANDLE)lParam); - supCenterWindowSpecifyParent(hwndDlg, g_WinObj.MainWindow); + if (hs.len == 7) { - pDlgContext->lvColumnHit = -1; - pDlgContext->lvItemHit = -1; + bFound = FALSE; - pDlgContext->hwndDlg = hwndDlg; - pDlgContext->StatusBar = GetDlgItem(hwndDlg, ID_EXTRASLIST_STATUSBAR); - SendMessage(pDlgContext->StatusBar, SB_SETPARTS, 2, (LPARAM)&SbParts); + for (i = 0; i < RTL_NUMBER_OF(W32kApiSetAdapters); i++) { - _strcpy(szText, TEXT("Viewing ")); - if (pDlgContext->DialogMode == SST_Ntos) - _strcat(szText, TEXT("ntoskrnl service table")); - else - _strcat(szText, TEXT("win32k service table")); + PatternSize = W32kApiSetAdapters[i].Size; + PatternData = W32kApiSetAdapters[i].Data; - SetWindowText(hwndDlg, szText); + if (PatternSize == RtlCompareMemory(&ptrCode[Index], + PatternData, + PatternSize)) + { + Rel = *(PLONG)(ptrCode + Index + (hs.len - 4)); + bFound = TRUE; + break; + } - extrasSetDlgIcon(pDlgContext); + } - pDlgContext->ListView = GetDlgItem(hwndDlg, ID_EXTRASLIST); - if (pDlgContext->ListView) { + if (bFound) + break; + } - // - // Set listview imagelist, style flags and theme. - // - supSetListViewSettings(pDlgContext->ListView, - LVS_EX_FULLROWSELECT | LVS_EX_DOUBLEBUFFER | LVS_EX_LABELTIP, - FALSE, - TRUE, - g_ListViewImages, - LVSIL_SMALL); + Index += hs.len; - // - // And columns and remember their count. - // - pDlgContext->lvColumnCount = supAddLVColumnsFromArray( - pDlgContext->ListView, - columnData, - RTL_NUMBER_OF(columnData)); + } while (Index < 32); - SendMessage(hwndDlg, WM_SIZE, 0, 0); + if (Rel == 0) + return 0; - supListViewEnableRedraw(pDlgContext->ListView, FALSE); - SdtListCreate(pDlgContext->hwndDlg, FALSE, pDlgContext); - supListViewEnableRedraw(pDlgContext->ListView, TRUE); - } + + return (ULONG_PTR)ptrCode + Index + hs.len + Rel; } /* -* SdtDialogProc +* ApiSetLoadResolvedModule * * Purpose: * -* KiServiceTable Dialog window procedure. +* Final apiset resolving and loading actual file. +* +* Function return NTSTATUS value and sets ResolvedEntry parameter. * */ -INT_PTR CALLBACK SdtDialogProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam +_Success_(return == STATUS_SUCCESS) +NTSTATUS ApiSetLoadResolvedModule( + _In_ PVOID ApiSetMap, + _In_ PUNICODE_STRING ApiSetToResolve, + _Inout_ PANSI_STRING ConvertedModuleName, + _Out_ HMODULE * DllModule ) { - EXTRASCONTEXT* pDlgContext; - - if (uMsg == g_WinObj.SettingsChangeMessage) { - pDlgContext = (EXTRASCONTEXT*)GetProp(hwndDlg, T_DLGCONTEXT); - if (pDlgContext) { - extrasHandleSettingsChange(pDlgContext); - } - return TRUE; - } + BOOL ResolvedResult; + NTSTATUS Status; + UNICODE_STRING usResolvedModule; - switch (uMsg) { - - case WM_INITDIALOG: - SdtDlgOnInit(hwndDlg, lParam); - break; - - case WM_GETMINMAXINFO: - if (lParam) { - supSetMinMaxTrackSize((PMINMAXINFO)lParam, - SDTDLG_TRACKSIZE_MIN_X, - SDTDLG_TRACKSIZE_MIN_Y, - TRUE); - } - break; + if (DllModule == NULL) + return STATUS_INVALID_PARAMETER_2; + if (ConvertedModuleName == NULL) + return STATUS_INVALID_PARAMETER_3; - case WM_NOTIFY: - return SdtDlgHandleNotify(hwndDlg, lParam); + *DllModule = NULL; - case WM_SIZE: - pDlgContext = (EXTRASCONTEXT*)GetProp(hwndDlg, T_DLGCONTEXT); - if (pDlgContext) { - extrasSimpleListResize(hwndDlg); - } - break; + ResolvedResult = FALSE; + RtlInitEmptyUnicodeString(&usResolvedModule, NULL, 0); - case WM_DESTROY: - PostQuitMessage(0); - break; + // + // Resolve ApiSet. + // + Status = NtLdrApiSetResolveLibrary(ApiSetMap, + ApiSetToResolve, + NULL, + &ResolvedResult, + &usResolvedModule); - case WM_CLOSE: - pDlgContext = (EXTRASCONTEXT*)RemoveProp(hwndDlg, T_DLGCONTEXT); - if (pDlgContext) { - extrasRemoveDlgIcon(pDlgContext); - } - DestroyWindow(hwndDlg); - break; + if (NT_SUCCESS(Status)) { - case WM_COMMAND: + if (ResolvedResult) { + // + // ApiSet resolved, load result library. + // + *DllModule = LoadLibraryEx(usResolvedModule.Buffer, NULL, DONT_RESOLVE_DLL_REFERENCES); - switch (GET_WM_COMMAND_ID(wParam, lParam)) { + // + // Convert resolved name back to ANSI for module query. + // + RtlUnicodeStringToAnsiString(ConvertedModuleName, + &usResolvedModule, + TRUE); - case IDCANCEL: - SendMessage(hwndDlg, WM_CLOSE, 0, 0); - break; + RtlFreeUnicodeString(&usResolvedModule); + Status = STATUS_SUCCESS; + } + } + else { + // + // Change status code for dbg output. + // + if (Status == STATUS_UNSUCCESSFUL) + Status = STATUS_APISET_NOT_PRESENT; + } - case ID_SDTLIST_SAVE: - pDlgContext = (EXTRASCONTEXT*)GetProp(hwndDlg, T_DLGCONTEXT); - if (pDlgContext) { + return Status; +} - if (supListViewExportToFile( - TEXT("Table.csv"), - hwndDlg, - pDlgContext->ListView)) - { - supStatusBarSetText(pDlgContext->StatusBar, 1, T_LIST_EXPORT_SUCCESS); - } +/* +* ApiSetResolveWin32kTableEntry +* +* Purpose: +* +* Find entry in Win32kApiSetTable. +* +* Function return STATUS_SUCCESS on success and sets ResolvedEntry parameter. +* +*/ +NTSTATUS ApiSetResolveWin32kTableEntry( + _In_ ULONG_PTR ApiSetTable, + _In_ ULONG_PTR LookupEntry, + _In_ ULONG EntrySize, + _Out_ PVOID* ResolvedEntry +) +{ + NTSTATUS resolveStatus = STATUS_APISET_NOT_PRESENT; + PW32K_API_SET_TABLE_ENTRY pvTableEntry = (PW32K_API_SET_TABLE_ENTRY)ApiSetTable; + ULONG cEntries; + ULONG_PTR entryValue; + PULONG_PTR pvHostEntries; - } - break; + *ResolvedEntry = NULL; - case ID_VIEW_REFRESH: - pDlgContext = (EXTRASCONTEXT*)GetProp(hwndDlg, T_DLGCONTEXT); - if (pDlgContext) { - supListViewEnableRedraw(pDlgContext->ListView, FALSE); - SdtListCreate(hwndDlg, TRUE, pDlgContext); - supListViewEnableRedraw(pDlgContext->ListView, TRUE); - } - break; + // + // Lookup entry in table. + // + __try { - case ID_JUMPTOFILE: - pDlgContext = (EXTRASCONTEXT*)GetProp(hwndDlg, T_DLGCONTEXT); - if (pDlgContext) { - supJumpToFileListView(pDlgContext->ListView, 3); - } - break; + while (pvTableEntry->Host) { - case ID_OBJECT_COPY: - pDlgContext = (EXTRASCONTEXT*)GetProp(hwndDlg, T_DLGCONTEXT); - if (pDlgContext) { - supListViewCopyItemValueToClipboard(pDlgContext->ListView, - pDlgContext->lvItemHit, - pDlgContext->lvColumnHit); - } - break; + cEntries = pvTableEntry->Host->HostEntriesCount; + pvHostEntries = (PULONG_PTR)pvTableEntry->HostEntriesArray; - } + // + // Search inside table host entry array. + // + do { - break; + entryValue = (ULONG_PTR)pvHostEntries; + pvHostEntries++; - case WM_CONTEXTMENU: - pDlgContext = (EXTRASCONTEXT*)GetProp(hwndDlg, T_DLGCONTEXT); - if (pDlgContext) { + if (entryValue == LookupEntry) { + *ResolvedEntry = (PVOID)pvTableEntry; + resolveStatus = STATUS_SUCCESS; + break; + } - supHandleContextMenuMsgForListView(hwndDlg, - wParam, - lParam, - pDlgContext->ListView, - (pfnPopupMenuHandler)SdtHandlePopupMenu, - (PVOID)pDlgContext); + } while (--cEntries); + pvTableEntry = (PW32K_API_SET_TABLE_ENTRY)RtlOffsetToPointer(pvTableEntry, EntrySize); } - break; + } + __except (WOBJ_EXCEPTION_FILTER_LOG) { + // + // Should never be here. Only in case if table structure changed or ApiSetTable address points to invalid data. + // + return STATUS_ACCESS_VIOLATION; } - return FALSE; + return resolveStatus; } /* -* SdtListOutputTable +* SdtResolveServiceEntryModule * * Purpose: * -* Output dumped and converted syscall table to listview. +* Find a module for shadow table entry by parsing apisets(if present) and/or forwarders (if present). +* +* Function return NTSTATUS value and sets ResolvedModule, ResolvedModuleName, FunctionName parameters. * */ -VOID SdtListOutputTable( - _In_ HWND hwndDlg, - _In_ PRTL_PROCESS_MODULES Modules, - _In_ PSDT_TABLE SdtTableEntry +_Success_(return == STATUS_SUCCESS) +NTSTATUS SdtResolveServiceEntryModule( + _In_ PBYTE FunctionPtr, + _In_ HMODULE MappedWin32k, + _In_opt_ PVOID ApiSetMap, + _In_ ULONG_PTR Win32kApiSetTable, + _In_ PWIN32_SHADOWTABLE ShadowTableEntry, + _Out_ HMODULE * ResolvedModule, + _Inout_ PANSI_STRING ResolvedModuleName, + _Out_ LPCSTR * FunctionName ) { - INT lvIndex; - ULONG i, iImage, moduleIndex = 0; - EXTRASCONTEXT* Context = (EXTRASCONTEXT*)GetProp(hwndDlg, T_DLGCONTEXT); + BOOLEAN NeedApiSetResolve = (g_NtBuildNumber > 18885); + BOOLEAN Win32kApiSetTableExpected = (g_NtBuildNumber > 18935); - LVITEM lvItem; - WCHAR szBuffer[MAX_PATH + 1]; + ULONG ApiSetTableEntrySize; - LPWSTR lpBaseName, lpBaseLimit; + NTSTATUS resultStatus = STATUS_UNSUCCESSFUL, resolveStatus; - if (Context->DialogMode == SST_Ntos) { - lpBaseName = KSW_KiServiceTable; - lpBaseLimit = KSW_KiServiceLimit; - } - else if (Context->DialogMode == SST_Win32k) { - lpBaseName = KSW_W32pServiceTable; - lpBaseLimit = KSW_W32pServiceLimit; - } - else - return; + HMODULE DllModule = NULL; - RtlStringCchPrintfSecure(szBuffer, - MAX_PATH, - TEXT("%ws 0x%p / %ws %lu (0x%lX)"), - lpBaseName, - (PVOID)SdtTableEntry->Base, - lpBaseLimit, - SdtTableEntry->Limit, - SdtTableEntry->Limit); + LONG32 JmpAddress; + ULONG_PTR ApiSetReference; - supStatusBarSetText(Context->StatusBar, 0, (LPWSTR)&szBuffer); + LPCSTR ModuleName; + PWCHAR HostName; - iImage = ObManagerGetImageIndexByTypeIndex(ObjectTypeDevice); + W32K_API_SET_TABLE_ENTRY *pvApiSetEntry = NULL; - ListView_DeleteAllItems(Context->ListView); + UNICODE_STRING usApiSetEntry, usModuleName; + hde64s hs; - //list table - for (i = 0; i < SdtTableEntry->Limit; i++) { - RtlSecureZeroMemory(szBuffer, sizeof(szBuffer)); - ultostr(SdtTableEntry->Table[i].ServiceId, szBuffer); + *ResolvedModule = NULL; - //ServiceId - RtlSecureZeroMemory(&lvItem, sizeof(lvItem)); - lvItem.mask = LVIF_TEXT | LVIF_IMAGE; - lvItem.iItem = MAXINT; - lvItem.iImage = iImage; //imagelist id - lvItem.pszText = szBuffer; - lvIndex = ListView_InsertItem(Context->ListView, &lvItem); + hde64_disasm((void*)FunctionPtr, &hs); + if (hs.flags & F_ERROR) { + return STATUS_INTERNAL_ERROR; + } - //Name - lvItem.mask = LVIF_TEXT; - lvItem.iSubItem = 1; - lvItem.pszText = (LPWSTR)SdtTableEntry->Table[i].Name; - lvItem.iItem = lvIndex; - ListView_SetItem(Context->ListView, &lvItem); + do { - //Address - RtlSecureZeroMemory(szBuffer, sizeof(szBuffer)); - szBuffer[0] = L'0'; - szBuffer[1] = L'x'; - u64tohex(SdtTableEntry->Table[i].Address, &szBuffer[2]); + // + // See if this is new Win32kApiSetTable adapter. + // + if (Win32kApiSetTableExpected && ApiSetMap) { - lvItem.iSubItem = 2; - lvItem.pszText = szBuffer; - ListView_SetItem(Context->ListView, &lvItem); + ApiSetReference = ApiSetExtractReferenceFromAdapter(FunctionPtr); + if (ApiSetReference) { - //Module - RtlSecureZeroMemory(szBuffer, sizeof(szBuffer)); + if (g_NtBuildNumber >= NT_WINSRV_21H1) + ApiSetTableEntrySize = sizeof(W32K_API_SET_TABLE_ENTRY_V2); + else + ApiSetTableEntrySize = sizeof(W32K_API_SET_TABLE_ENTRY); - if (ntsupFindModuleEntryByAddress( - Modules, - (PVOID)SdtTableEntry->Table[i].Address, - &moduleIndex)) - { - MultiByteToWideChar( - CP_ACP, - 0, - (LPCSTR)&Modules->Modules[moduleIndex].FullPathName, - (INT)_strlen_a((char*)Modules->Modules[moduleIndex].FullPathName), - szBuffer, - MAX_PATH); - } - else { - _strcpy(szBuffer, TEXT("Unknown Module")); - } + resolveStatus = ApiSetResolveWin32kTableEntry( + Win32kApiSetTable, + ApiSetReference, + ApiSetTableEntrySize, + (PVOID*)&pvApiSetEntry); - lvItem.iSubItem = 3; - lvItem.pszText = szBuffer; - ListView_SetItem(Context->ListView, &lvItem); - } -} + if (!NT_SUCCESS(resolveStatus)) + return resolveStatus; -/* -* SdtListCreateTable -* -* Purpose: -* -* KiServiceTable dump routine. -* -*/ -BOOL SdtListCreateTable( - VOID -) -{ - BOOL bResult = FALSE; - ULONG EntrySize = 0; - SIZE_T memIO; - PUTable TableDump = NULL; - PBYTE Module = NULL; - PIMAGE_EXPORT_DIRECTORY ExportDirectory = NULL; - PDWORD ExportNames, ExportFunctions; - PWORD NameOrdinals; + // + // Host is on the same offset for both V1/V2 versions. + // + HostName = pvApiSetEntry->Host->HostName; - PSERVICETABLEENTRY ServiceEntry; + RtlInitUnicodeString(&usApiSetEntry, HostName); - CHAR* ServiceName; - CHAR* FunctionAddress; - ULONG ServiceId, i, j; + resolveStatus = ApiSetLoadResolvedModule( + ApiSetMap, + &usApiSetEntry, + ResolvedModuleName, + &DllModule); - __try { + if (NT_SUCCESS(resolveStatus)) { + if (DllModule) { + *ResolvedModule = DllModule; + *FunctionName = ShadowTableEntry->Name; + return STATUS_SUCCESS; + } + else { + return STATUS_DLL_NOT_FOUND; + } + } + else { + return resolveStatus; + } - if ((g_kdctx.Data->KeServiceDescriptorTable.Base == 0) || - (g_kdctx.Data->KeServiceDescriptorTable.Limit == 0)) - { - if (!kdFindKiServiceTable( - (ULONG_PTR)g_kdctx.NtOsImageMap, - (ULONG_PTR)g_kdctx.NtOsBase, - &g_kdctx.Data->KeServiceDescriptorTable)) - { - __leave; } + else { + resultStatus = STATUS_APISET_NOT_HOSTED; + } + } + + JmpAddress = *(PLONG32)(FunctionPtr + (hs.len - 4)); // retrieve the offset + FunctionPtr = FunctionPtr + hs.len + JmpAddress; // hs.len -> length of jmp instruction + + *FunctionName = NtRawIATEntryToImport(MappedWin32k, FunctionPtr, &ModuleName); + if (*FunctionName == NULL) { + resultStatus = STATUS_PROCEDURE_NOT_FOUND; + break; } // - // If table empty, dump and prepare table + // Convert module name to UNICODE. // - if (KiServiceTable.Allocated == FALSE) { + if (RtlCreateUnicodeStringFromAsciiz(&usModuleName, (PSTR)ModuleName)) { - Module = (PBYTE)GetModuleHandle(TEXT("ntdll.dll")); + // + // Check whatever ApiSet resolving required. + // + if (NeedApiSetResolve) { - if (Module == NULL) - __leave; + if (ApiSetMap) { + resolveStatus = ApiSetLoadResolvedModule( + ApiSetMap, + &usModuleName, + ResolvedModuleName, + &DllModule); + } + else { + resolveStatus = STATUS_INVALID_PARAMETER_3; + } - ExportDirectory = (PIMAGE_EXPORT_DIRECTORY)RtlImageDirectoryEntryToData( - Module, - TRUE, - IMAGE_DIRECTORY_ENTRY_EXPORT, - &EntrySize); + if (!NT_SUCCESS(resolveStatus)) { + RtlFreeUnicodeString(&usModuleName); + return resolveStatus; + } - if (ExportDirectory == NULL) { - __leave; + } + else { + // + // No ApiSet resolve required, load as usual. + // + DllModule = LoadLibraryEx(usModuleName.Buffer, NULL, DONT_RESOLVE_DLL_REFERENCES); + RtlUnicodeStringToAnsiString(ResolvedModuleName, &usModuleName, TRUE); } - ExportNames = (PDWORD)((PBYTE)Module + ExportDirectory->AddressOfNames); - ExportFunctions = (PDWORD)((PBYTE)Module + ExportDirectory->AddressOfFunctions); - NameOrdinals = (PWORD)((PBYTE)Module + ExportDirectory->AddressOfNameOrdinals); + RtlFreeUnicodeString(&usModuleName); - memIO = sizeof(SERVICETABLEENTRY) * ExportDirectory->NumberOfNames; + *ResolvedModule = DllModule; + resultStatus = (DllModule != NULL) ? STATUS_SUCCESS : STATUS_DLL_NOT_FOUND; + } - KiServiceTable.Table = (PSERVICETABLEENTRY)supHeapAlloc(memIO); - if (KiServiceTable.Table == NULL) - __leave; - KiServiceTable.Allocated = TRUE; + } while (FALSE); - if (!supDumpSyscallTableConverted( - g_kdctx.Data->KeServiceDescriptorTable.Base, - g_kdctx.Data->KeServiceDescriptorTable.Limit, - &TableDump)) - { - supHeapFree(KiServiceTable.Table); - KiServiceTable.Allocated = FALSE; - __leave; - } + return resultStatus; +} - KiServiceTable.Base = g_kdctx.Data->KeServiceDescriptorTable.Base; +/* +* SdtListReportEvent +* +* Purpose: +* +* Add entry to WinObjEx64 runtime log accessible through main menu. +* +*/ +VOID SdtListReportEvent( + _In_ WOBJ_ENTRY_TYPE EventType, + _In_ LPCWSTR FunctionName, + _In_ LPCWSTR ErrorString +) +{ + WCHAR szBuffer[1024]; - // - // Walk for syscall stubs. - // - KiServiceTable.Limit = 0; - for (i = 0; i < ExportDirectory->NumberOfNames; i++) { + RtlStringCchPrintfSecure(szBuffer, + RTL_NUMBER_OF(szBuffer), + TEXT("%ws, %ws"), + FunctionName, + ErrorString); - ServiceName = ((CHAR*)Module + ExportNames[i]); + logAdd(EventType, szBuffer); +} - // - // Use Zw alias to skip various Nt trash like NtdllDialogWndProc/NtGetTickCount. - // +/* +* SdtListReportFunctionResolveError +* +* Purpose: +* +* Report function name resolve error. +* +*/ +VOID SdtListReportFunctionResolveError( + _In_ LPCSTR FunctionName +) +{ + WCHAR szErrorBuffer[512]; - if (*(USHORT*)ServiceName == 'wZ') { + RtlSecureZeroMemory(szErrorBuffer, sizeof(szErrorBuffer)); - MultiByteToWideChar( - CP_ACP, - 0, - ServiceName, - (INT)_strlen_a(ServiceName), - KiServiceTable.Table[KiServiceTable.Limit].Name, - MAX_PATH); + _strcpy(szErrorBuffer, TEXT("could not resolve function ")); + MultiByteToWideChar(CP_ACP, 0, FunctionName, -1, _strend(szErrorBuffer), MAX_PATH); + _strcat(szErrorBuffer, TEXT(" address")); + SdtListReportEvent(EntryTypeError, __FUNCTIONW__, szErrorBuffer); +} - //dirty hack - KiServiceTable.Table[KiServiceTable.Limit].Name[0] = L'N'; - KiServiceTable.Table[KiServiceTable.Limit].Name[1] = L't'; +/* +* SdtListReportResolveModuleError +* +* Purpose: +* +* Report module resolve error. +* +*/ +VOID SdtListReportResolveModuleError( + _In_ NTSTATUS Status, + _In_ PWIN32_SHADOWTABLE Table, + _In_ PSTRING ResolvedModuleName, + _In_ LPCWSTR ErrorSource +) +{ + WCHAR szErrorBuffer[512]; - FunctionAddress = (CHAR*)((CHAR*)Module + ExportFunctions[NameOrdinals[i]]); - ServiceEntry = &KiServiceTable.Table[KiServiceTable.Limit]; + RtlSecureZeroMemory(szErrorBuffer, sizeof(szErrorBuffer)); - if (*(UCHAR*)((UCHAR*)FunctionAddress + 3) == 0xB8) { - ServiceId = *(ULONG*)((UCHAR*)FunctionAddress + 4); - if (ServiceId < g_kdctx.Data->KeServiceDescriptorTable.Limit) { - ServiceEntry->ServiceId = ServiceId; - ServiceEntry->Address = TableDump[ServiceId]; - TableDump[ServiceId] = 0; - } - else { - kdDebugPrint(">>1 %s %lu\r\n", ServiceName, KiServiceTable.Limit); - ServiceEntry->ServiceId = INVALID_SERVICE_ENTRY_ID; - } - } - else { - kdDebugPrint(">>2 %s %lu\r\n", ServiceName, KiServiceTable.Limit); - ServiceEntry->ServiceId = INVALID_SERVICE_ENTRY_ID; - } + // + // Most of this errors are not critical and ok. + // - KiServiceTable.Limit += 1; + switch (Status) { - }//wZ - }//for + case STATUS_INTERNAL_ERROR: + _strcpy(szErrorBuffer, TEXT("HDE Error")); + break; - for (i = 0; i < KiServiceTable.Limit; i++) { - ServiceEntry = &KiServiceTable.Table[i]; - if (ServiceEntry->ServiceId == INVALID_SERVICE_ENTRY_ID) { - for (j = 0; j < g_kdctx.Data->KeServiceDescriptorTable.Limit; j++) { - if (TableDump[j] != 0) { - ServiceEntry->ServiceId = j; - ServiceEntry->Address = TableDump[j]; - TableDump[j] = 0; - break; - } - } - } - } + case STATUS_APISET_NOT_HOSTED: + // + // Corresponding apiset not found. + // + _strcpy(szErrorBuffer, TEXT("not an apiset adapter for ")); + MultiByteToWideChar(CP_ACP, 0, Table->Name, -1, _strend(szErrorBuffer), MAX_PATH); + break; - supHeapFree(TableDump); - TableDump = NULL; - } + case STATUS_APISET_NOT_PRESENT: + // + // ApiSet extension present but empty. + // + _strcpy(szErrorBuffer, TEXT("extension contains a host for a non-existent apiset ")); + MultiByteToWideChar(CP_ACP, 0, Table->Name, -1, _strend(szErrorBuffer), MAX_PATH); + break; - bResult = TRUE; + case STATUS_PROCEDURE_NOT_FOUND: + // + // Not a critical issue. This mean we cannot pass this service next to forwarder lookup code. + // + _strcpy(szErrorBuffer, TEXT("could not resolve function name in module for service id ")); + ultostr(Table->Index, _strend(szErrorBuffer)); + _strcat(szErrorBuffer, TEXT(", service name ")); + MultiByteToWideChar(CP_ACP, 0, Table->Name, -1, _strend(szErrorBuffer), MAX_PATH); + break; - } - __finally { + case STATUS_DLL_NOT_FOUND: - if (AbnormalTermination()) - supReportAbnormalTermination(__FUNCTIONW__); + _strcpy(szErrorBuffer, TEXT("could not load import dll ")); - if (TableDump) { - supHeapFree(TableDump); - } + MultiByteToWideChar(CP_ACP, + 0, + ResolvedModuleName->Buffer, + ResolvedModuleName->Length, + _strend(szErrorBuffer), + MAX_PATH); + + break; + + default: + // + // Unexpected error code. + // + _strcpy(szErrorBuffer, TEXT("unexpected error 0x")); + ultohex(Status, _strend(szErrorBuffer)); + break; } - return bResult; + SdtListReportEvent(EntryTypeError, ErrorSource, szErrorBuffer); } /* -* ApiSetExtractReferenceFromAdapter +* SdtListCreateTableShadow * * Purpose: * -* Extract apiset reference from adapter code. +* W32pServiceTable create table routine. +* +* Note: This code only for Windows 10 RS1+ * */ -ULONG_PTR ApiSetExtractReferenceFromAdapter( - _In_ PBYTE ptrFunction +BOOL SdtListCreateTableShadow( + _In_ PRTL_PROCESS_MODULES pModules, + _Out_ PULONG Status ) { - BOOL bFound; - PBYTE ptrCode = ptrFunction; - ULONG Index = 0, i; - LONG Rel = 0; - hde64s hs; - - ULONG PatternSize; - PVOID PatternData; - - do { - hde64_disasm((void*)(ptrCode + Index), &hs); - if (hs.flags & F_ERROR) - break; - - if (hs.len == 7) { - - bFound = FALSE; - - for (i = 0; i < RTL_NUMBER_OF(W32kApiSetAdapters); i++) { - - PatternSize = W32kApiSetAdapters[i].Size; - PatternData = W32kApiSetAdapters[i].Data; + BOOLEAN NeedApiSetResolve = (g_NtBuildNumber > 18885); + BOOLEAN Win32kApiSetTableExpected = (g_NtBuildNumber > 18935); + NTSTATUS ntStatus; + BOOL bResult = FALSE; + ULONG w32u_limit, w32k_limit, c; + HMODULE w32u = NULL, w32k = NULL, DllModule, forwdll; + PBYTE fptr; + PULONG pServiceLimit, pServiceTable; + LPCSTR ModuleName, FunctionName, ForwarderDot, ForwarderFunctionName; + HANDLE EnumerationHeap = NULL; + ULONG_PTR Win32kBase = 0, kernelWin32kBase = 0; - if (PatternSize == RtlCompareMemory(&ptrCode[Index], - PatternData, - PatternSize)) - { - Rel = *(PLONG)(ptrCode + Index + (hs.len - 4)); - bFound = TRUE; - break; - } + PSERVICETABLEENTRY ServiceEntry; + PWIN32_SHADOWTABLE table, itable; + RESOLVE_INFO rfn; - } + ULONG_PTR Win32kApiSetTable = 0; - if (bFound) - break; - } + PVOID pvApiSetMap = NULL; + ULONG schemaVersion = 0; - Index += hs.len; + PRTL_PROCESS_MODULE_INFORMATION w32Module, subModule, ForwardModule; - } while (Index < 32); + LOAD_MODULE_ENTRY LoadedModulesHead; + PLOAD_MODULE_ENTRY ModuleEntry = NULL, PreviousEntry = NULL; - if (Rel == 0) - return 0; + ANSI_STRING ResolvedModuleName; + WCHAR szBuffer[MAX_PATH * 2]; + CHAR szForwarderModuleName[MAX_PATH]; - return (ULONG_PTR)ptrCode + Index + hs.len + Rel; -} + LoadedModulesHead.Next = NULL; + LoadedModulesHead.hModule = NULL; -/* -* ApiSetLoadResolvedModule -* -* Purpose: -* -* Final apiset resolving and loading actual file. -* -* Function return NTSTATUS value and sets ResolvedEntry parameter. -* -*/ -_Success_(return == STATUS_SUCCESS) -NTSTATUS ApiSetLoadResolvedModule( - _In_ PVOID ApiSetMap, - _In_ PUNICODE_STRING ApiSetToResolve, - _Inout_ PANSI_STRING ConvertedModuleName, - _Out_ HMODULE * DllModule -) -{ - BOOL ResolvedResult; - NTSTATUS Status; - UNICODE_STRING usResolvedModule; + *Status = STATUS_SUCCESS; - if (DllModule == NULL) - return STATUS_INVALID_PARAMETER_2; - if (ConvertedModuleName == NULL) - return STATUS_INVALID_PARAMETER_3; + __try { - *DllModule = NULL; + // + // Check if table already built. + // + if (W32pServiceTable.Allocated == FALSE) { - ResolvedResult = FALSE; - RtlInitEmptyUnicodeString(&usResolvedModule, NULL, 0); + // + // Find win32k loaded image base. + // + w32Module = (PRTL_PROCESS_MODULE_INFORMATION)ntsupFindModuleEntryByName( + pModules, + "win32k.sys"); - // - // Resolve ApiSet. - // - Status = NtLdrApiSetResolveLibrary(ApiSetMap, - ApiSetToResolve, - NULL, - &ResolvedResult, - &usResolvedModule); + if (w32Module == NULL) { + *Status = ErrShadowWin32kNotFound; + __leave; + } - if (NT_SUCCESS(Status)) { + Win32kBase = (ULONG_PTR)w32Module->ImageBase; - if (ResolvedResult) { // - // ApiSet resolved, load result library. + // Prepare dedicated heap for exports enumeration. // - *DllModule = LoadLibraryEx(usResolvedModule.Buffer, NULL, DONT_RESOLVE_DLL_REFERENCES); + EnumerationHeap = supCreateHeap(HEAP_GROWABLE, TRUE); + if (EnumerationHeap == NULL) { + *Status = ErrShadowMemAllocFail; + __leave; + } // - // Convert resolved name back to ANSI for module query. + // Load win32u and dump exports, in KnownDlls. // - RtlUnicodeStringToAnsiString(ConvertedModuleName, - &usResolvedModule, - TRUE); + w32u = LoadLibraryEx(TEXT("win32u.dll"), NULL, 0); + if (w32u == NULL) { + *Status = ErrShadowWin32uLoadFail; + __leave; + } - RtlFreeUnicodeString(&usResolvedModule); - Status = STATUS_SUCCESS; - } - } - else { - // - // Change status code for dbg output. - // - if (Status == STATUS_UNSUCCESSFUL) - Status = STATUS_APISET_NOT_PRESENT; - } + w32u_limit = NtRawEnumW32kExports(EnumerationHeap, w32u, &table); - return Status; -} + // + // Load win32k. + // -/* -* ApiSetResolveWin32kTableEntry -* -* Purpose: -* -* Find entry in Win32kApiSetTable. -* -* Function return STATUS_SUCCESS on success and sets ResolvedEntry parameter. -* -*/ -NTSTATUS ApiSetResolveWin32kTableEntry( - _In_ ULONG_PTR ApiSetTable, - _In_ ULONG_PTR LookupEntry, - _In_ ULONG EntrySize, - _Out_ PVOID* ResolvedEntry -) -{ - NTSTATUS resolveStatus = STATUS_APISET_NOT_PRESENT; - PW32K_API_SET_TABLE_ENTRY pvTableEntry = (PW32K_API_SET_TABLE_ENTRY)ApiSetTable; - ULONG cEntries; - ULONG_PTR entryValue; - PULONG_PTR pvHostEntries; + W32SymContext = SymParserCreate(); + w32k = SdtLoadWin32kImage(W32SymContext); + if (w32k == NULL) { + *Status = ErrShadowWin32kLoadFail; + __leave; + } - *ResolvedEntry = NULL; + if (Win32kApiSetTableExpected) { + // + // Locate Win32kApiSetTable variable. Failure will result in unresolved apiset adapters. + // + Win32kApiSetTable = SdtQueryWin32kApiSetTable(w32k, + w32Module->ImageBase, + w32Module->ImageSize); - // - // Lookup entry in table. - // - __try { + if (Win32kApiSetTable == 0) { + *Status = ErrShadowApiSetNotFound; + } + } - while (pvTableEntry->Host) { + // + // Query win32k!W32pServiceLimit. + // + pServiceLimit = (PULONG)GetProcAddress(w32k, "W32pServiceLimit"); + if (pServiceLimit == NULL) { + *Status = ErrShadowW32pServiceLimitNotFound; + __leave; + } - cEntries = pvTableEntry->Host->HostEntriesCount; - pvHostEntries = (PULONG_PTR)pvTableEntry->HostEntriesArray; + // + // Check whatever win32u is compatible with win32k data. + // + w32k_limit = *pServiceLimit; + if (w32k_limit != w32u_limit) { + *Status = ErrShadowWin32uMismatch; + __leave; + } // - // Search inside table host entry array. + // Query win32k!W32pServiceTable. // - do { + RtlSecureZeroMemory(&rfn, sizeof(RESOLVE_INFO)); + if (!NT_SUCCESS(NtRawGetProcAddress(w32k, "W32pServiceTable", &rfn))) { + *Status = ErrShadowW32pServiceTableNotFound; + __leave; + } - entryValue = (ULONG_PTR)pvHostEntries; - pvHostEntries++; + // + // Query ApiSetMap + // + if (NeedApiSetResolve) { - if (entryValue == LookupEntry) { - *ResolvedEntry = (PVOID)pvTableEntry; - resolveStatus = STATUS_SUCCESS; - break; + if (!NtLdrApiSetLoadFromPeb(&schemaVersion, (PVOID*)&pvApiSetMap)) { + *Status = ErrShadowApiSetSchemaMapNotFound; + __leave; } - } while (--cEntries); + // + // Windows 10+ uses modern ApiSetSchema version, everything else not supported. + // + if (schemaVersion != API_SET_SCHEMA_VERSION_V6) { + *Status = ErrShadowApiSetSchemaVerUnknown; + __leave; + } + } - pvTableEntry = (PW32K_API_SET_TABLE_ENTRY)RtlOffsetToPointer(pvTableEntry, EntrySize); - } - } - __except (WOBJ_EXCEPTION_FILTER_LOG) { - // - // Should never be here. Only in case if table structure changed or ApiSetTable address points to invalid data. - // - return STATUS_ACCESS_VIOLATION; - } + // + // Set global variables. + // + kernelWin32kBase = Win32kBase + (ULONG_PTR)rfn.Function - (ULONG_PTR)w32k; - return resolveStatus; -} + // + // Insert SystemRoot\System32\Drivers to the loader directories search list. + // + _strcpy(szBuffer, g_WinObj.szSystemDirectory); + _strcat(szBuffer, TEXT("\\drivers")); + SetDllDirectory(szBuffer); -/* -* SdtResolveServiceEntryModule -* -* Purpose: -* -* Find a module for shadow table entry by parsing apisets(if present) and/or forwarders (if present). -* -* Function return NTSTATUS value and sets ResolvedModule, ResolvedModuleName, FunctionName parameters. -* -*/ -_Success_(return == STATUS_SUCCESS) -NTSTATUS SdtResolveServiceEntryModule( - _In_ PBYTE FunctionPtr, - _In_ HMODULE MappedWin32k, - _In_opt_ PVOID ApiSetMap, - _In_ ULONG_PTR Win32kApiSetTable, - _In_ PWIN32_SHADOWTABLE ShadowTableEntry, - _Out_ HMODULE * ResolvedModule, - _Inout_ PANSI_STRING ResolvedModuleName, - _Out_ LPCSTR * FunctionName -) -{ - BOOLEAN NeedApiSetResolve = (g_NtBuildNumber > 18885); - BOOLEAN Win32kApiSetTableExpected = (g_NtBuildNumber > 18935); + // + // Build table. + // + pServiceTable = (PULONG)rfn.Function; - ULONG ApiSetTableEntrySize; + for (c = 0; c < w32k_limit; ++c) { - NTSTATUS resultStatus = STATUS_UNSUCCESSFUL, resolveStatus; + itable = table; + while (itable != 0) { - HMODULE DllModule = NULL; + if (itable->Index == c + WIN32K_START_INDEX) { - LONG32 JmpAddress; - ULONG_PTR ApiSetReference; + itable->KernelStubAddress = pServiceTable[c]; + fptr = (PBYTE)w32k + itable->KernelStubAddress; + itable->KernelStubAddress += Win32kBase; - LPCSTR ModuleName; - PWCHAR HostName; + // + // Resolve module name for table entry and load this module to the memory. + // - W32K_API_SET_TABLE_ENTRY *pvApiSetEntry = NULL; + DllModule = NULL; + RtlSecureZeroMemory(&ResolvedModuleName, sizeof(ResolvedModuleName)); + ntStatus = SdtResolveServiceEntryModule(fptr, + w32k, + pvApiSetMap, + Win32kApiSetTable, + itable, + &DllModule, + &ResolvedModuleName, + &FunctionName); - UNICODE_STRING usApiSetEntry, usModuleName; - hde64s hs; + if (!NT_SUCCESS(ntStatus)) { + SdtListReportResolveModuleError(ntStatus, + itable, + &ResolvedModuleName, + __FUNCTIONW__); - *ResolvedModule = NULL; + break; + } - hde64_disasm((void*)FunctionPtr, &hs); - if (hs.flags & F_ERROR) { - return STATUS_INTERNAL_ERROR; - } + ModuleName = ResolvedModuleName.Buffer; - do { + // + // Remember loaded module to the internal list. + // + ModuleEntry = (PLOAD_MODULE_ENTRY)supHeapAllocEx(EnumerationHeap, + sizeof(LOAD_MODULE_ENTRY)); - // - // See if this is new Win32kApiSetTable adapter. - // - if (Win32kApiSetTableExpected && ApiSetMap) { + if (ModuleEntry) { + ModuleEntry->Next = LoadedModulesHead.Next; + ModuleEntry->hModule = DllModule; + LoadedModulesHead.Next = ModuleEntry; + } - ApiSetReference = ApiSetExtractReferenceFromAdapter(FunctionPtr); - if (ApiSetReference) { + // + // Check function forwarding. + // + if (!NT_SUCCESS(NtRawGetProcAddress(DllModule, FunctionName, &rfn))) { + // + // Log error. + // + SdtListReportFunctionResolveError(FunctionName); + break; + } - if (g_NtBuildNumber >= NT_WINSRV_21H1) - ApiSetTableEntrySize = sizeof(W32K_API_SET_TABLE_ENTRY_V2); - else - ApiSetTableEntrySize = sizeof(W32K_API_SET_TABLE_ENTRY); + // + // Function is forward, resolve again. + // + if (rfn.ResultType == ForwarderString) { - resolveStatus = ApiSetResolveWin32kTableEntry( - Win32kApiSetTable, - ApiSetReference, - ApiSetTableEntrySize, - (PVOID*)&pvApiSetEntry); + ForwarderDot = _strchr_a(rfn.ForwarderName, '.'); + ForwarderFunctionName = ForwarderDot + 1; - if (!NT_SUCCESS(resolveStatus)) - return resolveStatus; + // + // Build forwarder module name. + // + RtlSecureZeroMemory(szForwarderModuleName, sizeof(szForwarderModuleName)); + _strncpy_a(szForwarderModuleName, sizeof(szForwarderModuleName), + rfn.ForwarderName, ForwarderDot - &rfn.ForwarderName[0]); - // - // Host is on the same offset for both V1/V2 versions. - // - HostName = pvApiSetEntry->Host->HostName; + _strcat_a(szForwarderModuleName, ".SYS"); - RtlInitUnicodeString(&usApiSetEntry, HostName); + ForwardModule = (PRTL_PROCESS_MODULE_INFORMATION)ntsupFindModuleEntryByName(pModules, + szForwarderModuleName); - resolveStatus = ApiSetLoadResolvedModule( - ApiSetMap, - &usApiSetEntry, - ResolvedModuleName, - &DllModule); + if (ForwardModule) { - if (NT_SUCCESS(resolveStatus)) { - if (DllModule) { - *ResolvedModule = DllModule; - *FunctionName = ShadowTableEntry->Name; - return STATUS_SUCCESS; - } - else { - return STATUS_DLL_NOT_FOUND; - } - } - else { - return resolveStatus; - } + if (ForwarderFunctionName) { - } - else { - resultStatus = STATUS_APISET_NOT_HOSTED; - } - } + forwdll = LoadLibraryExA(szForwarderModuleName, NULL, DONT_RESOLVE_DLL_REFERENCES); + if (forwdll) { - JmpAddress = *(PLONG32)(FunctionPtr + (hs.len - 4)); // retrieve the offset - FunctionPtr = FunctionPtr + hs.len + JmpAddress; // hs.len -> length of jmp instruction + // + // Remember loaded module to the internal list. + // + ModuleEntry = (PLOAD_MODULE_ENTRY)supHeapAllocEx(EnumerationHeap, + sizeof(LOAD_MODULE_ENTRY)); - *FunctionName = NtRawIATEntryToImport(MappedWin32k, FunctionPtr, &ModuleName); - if (*FunctionName == NULL) { - resultStatus = STATUS_PROCEDURE_NOT_FOUND; - break; - } + if (ModuleEntry) { + ModuleEntry->Next = LoadedModulesHead.Next; + ModuleEntry->hModule = forwdll; + LoadedModulesHead.Next = ModuleEntry; + } - // - // Convert module name to UNICODE. - // - if (RtlCreateUnicodeStringFromAsciiz(&usModuleName, (PSTR)ModuleName)) { + if (NT_SUCCESS(NtRawGetProcAddress(forwdll, ForwarderFunctionName, &rfn))) { - // - // Check whatever ApiSet resolving required. - // - if (NeedApiSetResolve) { + // + // Calculate routine kernel mode address. + // + itable->KernelStubTargetAddress = + (ULONG_PTR)ForwardModule->ImageBase + ((ULONG_PTR)rfn.Function - (ULONG_PTR)forwdll); + } - if (ApiSetMap) { - resolveStatus = ApiSetLoadResolvedModule( - ApiSetMap, - &usModuleName, - ResolvedModuleName, - &DllModule); - } - else { - resolveStatus = STATUS_INVALID_PARAMETER_3; - } + } + else { + // + // Log error. + // + SdtListReportEvent(EntryTypeError, __FUNCTIONW__, TEXT("could not load forwarded module")); + } - if (!NT_SUCCESS(resolveStatus)) { - RtlFreeUnicodeString(&usModuleName); - return resolveStatus; - } + } // if (ForwarderFunctionName) + + }//if (ForwardModule) + + } + else { + // + // Calculate routine kernel mode address. + // + subModule = (PRTL_PROCESS_MODULE_INFORMATION)ntsupFindModuleEntryByName(pModules, ModuleName); + if (subModule) { + itable->KernelStubTargetAddress = + (ULONG_PTR)subModule->ImageBase + ((ULONG_PTR)rfn.Function - (ULONG_PTR)DllModule); + } + RtlFreeAnsiString(&ResolvedModuleName); + + } + + } // if (itable->Index == c + WIN32K_START_INDEX) + + itable = itable->NextService; + + } //while (itable != 0); } - else { + + // + // Output table. + // + W32pServiceTable.Table = (PSERVICETABLEENTRY)supHeapAlloc(sizeof(SERVICETABLEENTRY) * w32k_limit); + if (W32pServiceTable.Table) { + + W32pServiceTable.Allocated = TRUE; + W32pServiceTable.Base = kernelWin32kBase; + // - // No ApiSet resolve required, load as usual. + // Convert table to output format. // - DllModule = LoadLibraryEx(usModuleName.Buffer, NULL, DONT_RESOLVE_DLL_REFERENCES); - RtlUnicodeStringToAnsiString(ResolvedModuleName, &usModuleName, TRUE); + W32pServiceTable.Limit = 0; + itable = table; + while (itable != 0) { + + // + // Service Id. + // + ServiceEntry = &W32pServiceTable.Table[W32pServiceTable.Limit]; + + ServiceEntry->ServiceId = itable->Index; + + // + // Routine real address. + // + if (itable->KernelStubTargetAddress) { + // + // Output stub target address. + // + ServiceEntry->Address = itable->KernelStubTargetAddress; + + } + else { + // + // Query failed, output stub address. + // + ServiceEntry->Address = itable->KernelStubAddress; + + } + + // + // Remember service name. + // + MultiByteToWideChar( + CP_ACP, + 0, + itable->Name, + (INT)_strlen_a(itable->Name), + ServiceEntry->Name, + MAX_PATH); + + W32pServiceTable.Limit += 1; + + itable = itable->NextService; + } + } - RtlFreeUnicodeString(&usModuleName); + } // if (W32pServiceTable.Allocated == FALSE) - *ResolvedModule = DllModule; - resultStatus = (DllModule != NULL) ? STATUS_SUCCESS : STATUS_DLL_NOT_FOUND; + bResult = W32pServiceTable.Allocated; + + } + __finally { + + if (AbnormalTermination()) + supReportAbnormalTermination(__FUNCTIONW__); + + // + // Restore default search order. + // + SetDllDirectory(NULL); + + // + // Unload all loaded modules. + // + for (PreviousEntry = &LoadedModulesHead, ModuleEntry = LoadedModulesHead.Next; + ModuleEntry != NULL; + PreviousEntry = ModuleEntry, ModuleEntry = ModuleEntry->Next) + { + FreeLibrary(ModuleEntry->hModule); } + if (EnumerationHeap) supDestroyHeap(EnumerationHeap); + if (w32u) FreeLibrary(w32u); + if (w32k) FreeLibrary(w32k); + if (W32SymContext) { + W32SymContext->Parser.UnloadModule(W32SymContext); + SymParserDestroy(W32SymContext); + W32SymContext = NULL; + } - } while (FALSE); + } - return resultStatus; + return bResult; } /* -* SdtListReportEvent +* SdtDlgCompareFunc * * Purpose: * -* Add entry to WinObjEx64 runtime log accessible through main menu. +* KiServiceTable/W32pServiceTable Dialog listview comparer function. * */ -VOID SdtListReportEvent( - _In_ WOBJ_ENTRY_TYPE EventType, - _In_ LPCWSTR FunctionName, - _In_ LPCWSTR ErrorString +INT CALLBACK SdtDlgCompareFunc( + _In_ LPARAM lParam1, + _In_ LPARAM lParam2, + _In_ LPARAM lParamSort //pointer to EXTRASCALLBACK ) { - WCHAR szBuffer[1024]; + INT nResult = 0; - RtlStringCchPrintfSecure(szBuffer, - RTL_NUMBER_OF(szBuffer), - TEXT("%ws, %ws"), - FunctionName, - ErrorString); + EXTRASCONTEXT* pDlgContext; + EXTRASCALLBACK* CallbackParam = (EXTRASCALLBACK*)lParamSort; - logAdd(EventType, szBuffer); + if (CallbackParam == NULL) + return 0; + + pDlgContext = &SSTDlgContext[CallbackParam->Value]; + + switch (pDlgContext->lvColumnToSort) { + case COLUMN_SDTLIST_INDEX: //index + return supGetMaxOfTwoULongFromString( + pDlgContext->ListView, + lParam1, + lParam2, + pDlgContext->lvColumnToSort, + pDlgContext->bInverseSort); + case COLUMN_SDTLIST_ADDRESS: //address (hex) + return supGetMaxOfTwoU64FromHex( + pDlgContext->ListView, + lParam1, + lParam2, + pDlgContext->lvColumnToSort, + pDlgContext->bInverseSort); + case COLUMN_SDTLIST_NAME: //string (fixed size) + case COLUMN_SDTLIST_MODULE: //string (fixed size) + return supGetMaxCompareTwoFixedStrings( + pDlgContext->ListView, + lParam1, + lParam2, + pDlgContext->lvColumnToSort, + pDlgContext->bInverseSort); + } + + return nResult; } /* -* SdtListReportFunctionResolveError +* SdtListCreate * * Purpose: * -* Report function name resolve error. +* (Re)Create service table list. * */ -VOID SdtListReportFunctionResolveError( - _In_ LPCSTR FunctionName +VOID SdtListCreate( + _In_ HWND hwndDlg, + _In_ BOOL fRescan, + _In_ EXTRASCONTEXT * pDlgContext ) { - WCHAR szErrorBuffer[512]; + BOOL bSuccess = FALSE; + ULONG returnStatus; + EXTRASCALLBACK CallbackParam; + PRTL_PROCESS_MODULES pModules = NULL; + LPWSTR lpStatusMsg; - RtlSecureZeroMemory(szErrorBuffer, sizeof(szErrorBuffer)); + __try { - _strcpy(szErrorBuffer, TEXT("could not resolve function ")); - MultiByteToWideChar(CP_ACP, 0, FunctionName, -1, _strend(szErrorBuffer), MAX_PATH); - _strcat(szErrorBuffer, TEXT(" address")); - SdtListReportEvent(EntryTypeError, __FUNCTIONW__, szErrorBuffer); -} + supStatusBarSetText(pDlgContext->StatusBar, 1, TEXT("Initializing table view")); -/* -* SdtListReportResolveModuleError -* -* Purpose: -* -* Report module resolve error. -* -*/ -VOID SdtListReportResolveModuleError( - _In_ NTSTATUS Status, - _In_ PWIN32_SHADOWTABLE Table, - _In_ PSTRING ResolvedModuleName, - _In_ LPCWSTR ErrorSource -) -{ - WCHAR szErrorBuffer[512]; + pModules = (PRTL_PROCESS_MODULES)supGetLoadedModulesList(NULL); + if (pModules == NULL) { - RtlSecureZeroMemory(szErrorBuffer, sizeof(szErrorBuffer)); + supStatusBarSetText(pDlgContext->StatusBar, 1, + TEXT("Could not allocate memory for kernel modules list!")); - // - // Most of this errors are not critical and ok. - // + __leave; + } - switch (Status) { + if (pDlgContext->DialogMode == SST_Ntos) { - case STATUS_INTERNAL_ERROR: - _strcpy(szErrorBuffer, TEXT("HDE Error")); - break; + if (fRescan) { + if (KiServiceTable.Allocated) { + KiServiceTable.Allocated = FALSE; + supHeapFree(KiServiceTable.Table); + KiServiceTable.Limit = 0; + } + } - case STATUS_APISET_NOT_HOSTED: - // - // Corresponding apiset not found. - // - _strcpy(szErrorBuffer, TEXT("not an apiset adapter for ")); - MultiByteToWideChar(CP_ACP, 0, Table->Name, -1, _strend(szErrorBuffer), MAX_PATH); - break; + bSuccess = SdtListCreateTable(); + if (bSuccess) { + SdtListOutputTable(hwndDlg, pModules, &KiServiceTable); + } + else { + supStatusBarSetText(pDlgContext->StatusBar, 1, TEXT("Error dumping table")); + } - case STATUS_APISET_NOT_PRESENT: - // - // ApiSet extension present but empty. - // - _strcpy(szErrorBuffer, TEXT("extension contains a host for a non-existent apiset ")); - MultiByteToWideChar(CP_ACP, 0, Table->Name, -1, _strend(szErrorBuffer), MAX_PATH); - break; + } + else if (pDlgContext->DialogMode == SST_Win32k) { - case STATUS_PROCEDURE_NOT_FOUND: - // - // Not a critical issue. This mean we cannot pass this service next to forwarder lookup code. - // - _strcpy(szErrorBuffer, TEXT("could not resolve function name in module for service id ")); - ultostr(Table->Index, _strend(szErrorBuffer)); - _strcat(szErrorBuffer, TEXT(", service name ")); - MultiByteToWideChar(CP_ACP, 0, Table->Name, -1, _strend(szErrorBuffer), MAX_PATH); - break; + if (fRescan) { + if (W32pServiceTable.Allocated) { + W32pServiceTable.Allocated = FALSE; + supHeapFree(W32pServiceTable.Table); + W32pServiceTable.Limit = 0; + } + } - case STATUS_DLL_NOT_FOUND: + bSuccess = SdtListCreateTableShadow(pModules, &returnStatus); + if (bSuccess) { - _strcpy(szErrorBuffer, TEXT("could not load import dll ")); + if (returnStatus == ErrShadowApiSetNotFound) { + supStatusBarSetText(pDlgContext->StatusBar, 1, + T_ERRSHADOW_APISETTABLE_NOT_FOUND); + } - MultiByteToWideChar(CP_ACP, - 0, - ResolvedModuleName->Buffer, - ResolvedModuleName->Length, - _strend(szErrorBuffer), - MAX_PATH); + SdtListOutputTable(hwndDlg, pModules, &W32pServiceTable); + } + else { - break; + switch (returnStatus) { + + case ErrShadowWin32kNotFound: + lpStatusMsg = T_ERRSHADOW_WIN32K_NOT_FOUND; + break; + + case ErrShadowMemAllocFail: + lpStatusMsg = T_ERRSHADOW_MEMORY_NOT_ALLOCATED; + break; + + case ErrShadowWin32uLoadFail: + lpStatusMsg = T_ERRSHADOW_WIN32U_LOAD_FAILED; + break; + + case ErrShadowWin32kLoadFail: + lpStatusMsg = T_ERRSHADOW_WIN32K_LOAD_FAILED; + break; + + case ErrShadowW32pServiceLimitNotFound: + lpStatusMsg = T_ERRSHADOW_WIN32KLIMIT_NOT_FOUND; + break; + + case ErrShadowWin32uMismatch: + lpStatusMsg = T_ERRSHADOW_WIN32U_MISMATCH; + break; + + case ErrShadowW32pServiceTableNotFound: + lpStatusMsg = T_ERRSHADOW_TABLE_NOT_FOUND; + break; + + case ErrShadowApiSetSchemaMapNotFound: + lpStatusMsg = T_ERRSHADOW_APISETMAP_NOT_FOUND; + break; + + case ErrShadowApiSetSchemaVerUnknown: + lpStatusMsg = T_ERRSHADOW_APISET_VER_UNKNOWN; + break; + + default: + lpStatusMsg = TEXT("Unknown error"); + break; + } + + supStatusBarSetText(pDlgContext->StatusBar, 1, lpStatusMsg); + } + } - default: - // - // Unexpected error code. - // - _strcpy(szErrorBuffer, TEXT("unexpected error 0x")); - ultohex(Status, _strend(szErrorBuffer)); - break; } + __finally { - SdtListReportEvent(EntryTypeError, ErrorSource, szErrorBuffer); + if (AbnormalTermination()) + supReportAbnormalTermination(__FUNCTIONW__); + + if (pModules) + supHeapFree(pModules); + + } + + if (bSuccess) { + supStatusBarSetText(pDlgContext->StatusBar, 1, TEXT("Table read - OK")); + CallbackParam.lParam = 0; + CallbackParam.Value = pDlgContext->DialogMode; + ListView_SortItemsEx(pDlgContext->ListView, &SdtDlgCompareFunc, (LPARAM)&CallbackParam); + SetFocus(pDlgContext->ListView); + } } /* -* SdtListCreateTableShadow +* SdtHandlePopupMenu * * Purpose: * -* W32pServiceTable create table routine. -* -* Note: This code only for Windows 10 RS1+ +* Table list popup construction. * */ -BOOL SdtListCreateTableShadow( - _In_ PRTL_PROCESS_MODULES pModules, - _Out_ PULONG Status +VOID SdtHandlePopupMenu( + _In_ HWND hwndDlg, + _In_ LPPOINT lpPoint, + _In_ PVOID lpUserParam ) { - BOOLEAN NeedApiSetResolve = (g_NtBuildNumber > 18885); - BOOLEAN Win32kApiSetTableExpected = (g_NtBuildNumber > 18935); - NTSTATUS ntStatus; - BOOL bResult = FALSE; - ULONG w32u_limit, w32k_limit, c; - HMODULE w32u = NULL, w32k = NULL, DllModule, forwdll; - PBYTE fptr; - PULONG pServiceLimit, pServiceTable; - LPCSTR ModuleName, FunctionName, ForwarderDot, ForwarderFunctionName; - HANDLE EnumerationHeap = NULL; - ULONG_PTR Win32kBase = 0, kernelWin32kBase = 0; - - PSERVICETABLEENTRY ServiceEntry; - PWIN32_SHADOWTABLE table, itable; - RESOLVE_INFO rfn; - - ULONG_PTR Win32kApiSetTable = 0; - - PVOID pvApiSetMap = NULL; - ULONG schemaVersion = 0; + HMENU hMenu; + UINT uPos = 0; + EXTRASCONTEXT* Context = (EXTRASCONTEXT*)lpUserParam; - PRTL_PROCESS_MODULE_INFORMATION w32Module, subModule, ForwardModule; + hMenu = CreatePopupMenu(); + if (hMenu) { - LOAD_MODULE_ENTRY LoadedModulesHead; - PLOAD_MODULE_ENTRY ModuleEntry = NULL, PreviousEntry = NULL; + if (supListViewAddCopyValueItem(hMenu, + Context->ListView, + ID_OBJECT_COPY, + uPos, + lpPoint, + &Context->lvItemHit, + &Context->lvColumnHit)) + { + InsertMenu(hMenu, ++uPos, MF_BYPOSITION | MF_SEPARATOR, 0, NULL); + } - ANSI_STRING ResolvedModuleName; + InsertMenu(hMenu, uPos++, MF_BYCOMMAND, ID_JUMPTOFILE, T_JUMPTOFILE); + InsertMenu(hMenu, uPos++, MF_BYCOMMAND, ID_SDTLIST_SAVE, T_EXPORTTOFILE); + InsertMenu(hMenu, uPos++, MF_BYPOSITION | MF_SEPARATOR, 0, NULL); + InsertMenu(hMenu, uPos++, MF_BYCOMMAND, ID_VIEW_REFRESH, T_VIEW_REFRESH); - WCHAR szBuffer[MAX_PATH * 2]; - CHAR szForwarderModuleName[MAX_PATH]; + TrackPopupMenu(hMenu, + TPM_RIGHTBUTTON | TPM_LEFTALIGN, + lpPoint->x, + lpPoint->y, + 0, + hwndDlg, + NULL); - LoadedModulesHead.Next = NULL; - LoadedModulesHead.hModule = NULL; + DestroyMenu(hMenu); + } +} - *Status = STATUS_SUCCESS; +/* +* SdtFreeGlobals +* +* Purpose: +* +* Release memory allocated for SDT table globals. +* +*/ +BOOL CALLBACK SdtFreeGlobals( + _In_opt_ PVOID Context +) +{ + UNREFERENCED_PARAMETER(Context); - __try { - - // - // Check if table already built. - // - if (W32pServiceTable.Allocated == FALSE) { - - // - // Find win32k loaded image base. - // - w32Module = (PRTL_PROCESS_MODULE_INFORMATION)ntsupFindModuleEntryByName( - pModules, - "win32k.sys"); - - if (w32Module == NULL) { - *Status = ErrShadowWin32kNotFound; - __leave; - } - - Win32kBase = (ULONG_PTR)w32Module->ImageBase; - - // - // Prepare dedicated heap for exports enumeration. - // - EnumerationHeap = RtlCreateHeap(HEAP_GROWABLE, NULL, 0, 0, NULL, NULL); - if (EnumerationHeap == NULL) { - *Status = ErrShadowMemAllocFail; - __leave; - } - - // - // Load win32u and dump exports, in KnownDlls. - // - w32u = LoadLibraryEx(TEXT("win32u.dll"), NULL, 0); - if (w32u == NULL) { - *Status = ErrShadowWin32uLoadFail; - __leave; - } - - w32u_limit = NtRawEnumW32kExports(EnumerationHeap, w32u, &table); - - // - // Load win32k. - // - - W32SymContext = SymParserCreate(); - w32k = SdtLoadWin32kImage(W32SymContext); - if (w32k == NULL) { - *Status = ErrShadowWin32kLoadFail; - __leave; - } - - if (Win32kApiSetTableExpected) { - // - // Locate Win32kApiSetTable variable. Failure will result in unresolved apiset adapters. - // - Win32kApiSetTable = SdtQueryWin32kApiSetTable(w32k, - w32Module->ImageBase, - w32Module->ImageSize); - - if (Win32kApiSetTable == 0) { - *Status = ErrShadowApiSetNotFound; - } - } - - // - // Query win32k!W32pServiceLimit. - // - pServiceLimit = (PULONG)GetProcAddress(w32k, KSA_W32pServiceLimit); - if (pServiceLimit == NULL) { - *Status = ErrShadowW32pServiceLimitNotFound; - __leave; - } - - // - // Check whatever win32u is compatible with win32k data. - // - w32k_limit = *pServiceLimit; - if (w32k_limit != w32u_limit) { - *Status = ErrShadowWin32uMismatch; - __leave; - } - - // - // Query win32k!W32pServiceTable. - // - RtlSecureZeroMemory(&rfn, sizeof(RESOLVE_INFO)); - if (!NT_SUCCESS(NtRawGetProcAddress(w32k, KSA_W32pServiceTable, &rfn))) { - *Status = ErrShadowW32pServiceTableNotFound; - __leave; - } - - // - // Query ApiSetMap - // - if (NeedApiSetResolve) { - - if (!NtLdrApiSetLoadFromPeb(&schemaVersion, (PVOID*)&pvApiSetMap)) { - *Status = ErrShadowApiSetSchemaMapNotFound; - __leave; - } - - // - // Windows 10+ uses modern ApiSetSchema version, everything else not supported. - // - if (schemaVersion != API_SET_SCHEMA_VERSION_V6) { - *Status = ErrShadowApiSetSchemaVerUnknown; - __leave; - } - } - - // - // Set global variables. - // - kernelWin32kBase = Win32kBase + (ULONG_PTR)rfn.Function - (ULONG_PTR)w32k; - - // - // Insert SystemRoot\System32\Drivers to the loader directories search list. - // - _strcpy(szBuffer, g_WinObj.szSystemDirectory); - _strcat(szBuffer, TEXT("\\drivers")); - SetDllDirectory(szBuffer); - - // - // Build table. - // - pServiceTable = (PULONG)rfn.Function; - - for (c = 0; c < w32k_limit; ++c) { - - itable = table; - while (itable != 0) { - - if (itable->Index == c + WIN32K_START_INDEX) { - - itable->KernelStubAddress = pServiceTable[c]; - fptr = (PBYTE)w32k + itable->KernelStubAddress; - itable->KernelStubAddress += Win32kBase; - - // - // Resolve module name for table entry and load this module to the memory. - // - - DllModule = NULL; - RtlSecureZeroMemory(&ResolvedModuleName, sizeof(ResolvedModuleName)); - ntStatus = SdtResolveServiceEntryModule(fptr, - w32k, - pvApiSetMap, - Win32kApiSetTable, - itable, - &DllModule, - &ResolvedModuleName, - &FunctionName); - - if (!NT_SUCCESS(ntStatus)) { - - SdtListReportResolveModuleError(ntStatus, - itable, - &ResolvedModuleName, - __FUNCTIONW__); - - break; - } - - ModuleName = ResolvedModuleName.Buffer; - - // - // Remember loaded module to the internal list. - // - ModuleEntry = (PLOAD_MODULE_ENTRY)RtlAllocateHeap(EnumerationHeap, - HEAP_ZERO_MEMORY, - sizeof(LOAD_MODULE_ENTRY)); - - if (ModuleEntry) { - ModuleEntry->Next = LoadedModulesHead.Next; - ModuleEntry->hModule = DllModule; - LoadedModulesHead.Next = ModuleEntry; - } - - // - // Check function forwarding. - // - if (!NT_SUCCESS(NtRawGetProcAddress(DllModule, FunctionName, &rfn))) { - // - // Log error. - // - SdtListReportFunctionResolveError(FunctionName); - break; - } - - // - // Function is forward, resolve again. - // - if (rfn.ResultType == ForwarderString) { - - ForwarderDot = _strchr_a(rfn.ForwarderName, '.'); - ForwarderFunctionName = ForwarderDot + 1; - - // - // Build forwarder module name. - // - RtlSecureZeroMemory(szForwarderModuleName, sizeof(szForwarderModuleName)); - _strncpy_a(szForwarderModuleName, sizeof(szForwarderModuleName), - rfn.ForwarderName, ForwarderDot - &rfn.ForwarderName[0]); - - _strcat_a(szForwarderModuleName, ".SYS"); - - ForwardModule = (PRTL_PROCESS_MODULE_INFORMATION)ntsupFindModuleEntryByName(pModules, - szForwarderModuleName); - - if (ForwardModule) { + if (KiServiceTable.Allocated) { + supHeapFree(KiServiceTable.Table); + KiServiceTable.Allocated = FALSE; + } + if (W32pServiceTable.Allocated) { + supHeapFree(W32pServiceTable.Table); + W32pServiceTable.Allocated = FALSE; + } - if (ForwarderFunctionName) { + return TRUE; +} - forwdll = LoadLibraryExA(szForwarderModuleName, NULL, DONT_RESOLVE_DLL_REFERENCES); - if (forwdll) { +/* +* SdtDlgHandleNotify +* +* Purpose: +* +* WM_NOTIFY processing for dialog listview. +* +*/ +BOOL SdtDlgHandleNotify( + _In_ HWND hwndDlg, + _In_ LPARAM lParam +) +{ + INT nImageIndex, iSelectionMark; + LPNMLISTVIEW pListView = (LPNMLISTVIEW)lParam; + LPWSTR lpItem; + HWND hwndListView; - // - // Remember loaded module to the internal list. - // - ModuleEntry = (PLOAD_MODULE_ENTRY)RtlAllocateHeap(EnumerationHeap, - HEAP_ZERO_MEMORY, - sizeof(LOAD_MODULE_ENTRY)); + EXTRASCONTEXT* pDlgContext; - if (ModuleEntry) { - ModuleEntry->Next = LoadedModulesHead.Next; - ModuleEntry->hModule = forwdll; - LoadedModulesHead.Next = ModuleEntry; - } + EXTRASCALLBACK CallbackParam; + WCHAR szBuffer[MAX_PATH + 1]; - if (NT_SUCCESS(NtRawGetProcAddress(forwdll, ForwarderFunctionName, &rfn))) { + if (pListView == NULL) + return FALSE; - // - // Calculate routine kernel mode address. - // - itable->KernelStubTargetAddress = - (ULONG_PTR)ForwardModule->ImageBase + ((ULONG_PTR)rfn.Function - (ULONG_PTR)forwdll); - } + if (pListView->hdr.idFrom != ID_EXTRASLIST) + return FALSE; - } - else { - // - // Log error. - // - SdtListReportEvent(EntryTypeError, __FUNCTIONW__, TEXT("could not load forwarded module")); - } + hwndListView = pListView->hdr.hwndFrom; - } // if (ForwarderFunctionName) + switch (pListView->hdr.code) { - }//if (ForwardModule) + case LVN_COLUMNCLICK: - } - else { - // - // Calculate routine kernel mode address. - // - subModule = (PRTL_PROCESS_MODULE_INFORMATION)ntsupFindModuleEntryByName(pModules, ModuleName); - if (subModule) { - itable->KernelStubTargetAddress = - (ULONG_PTR)subModule->ImageBase + ((ULONG_PTR)rfn.Function - (ULONG_PTR)DllModule); - } + pDlgContext = (EXTRASCONTEXT*)GetProp(hwndDlg, T_DLGCONTEXT); + if (pDlgContext) { - RtlFreeAnsiString(&ResolvedModuleName); + pDlgContext->bInverseSort = (~pDlgContext->bInverseSort) & 1; + pDlgContext->lvColumnToSort = pListView->iSubItem; + CallbackParam.lParam = (LPARAM)pDlgContext->lvColumnToSort; + CallbackParam.Value = pDlgContext->DialogMode; + ListView_SortItemsEx(hwndListView, &SdtDlgCompareFunc, (LPARAM)&CallbackParam); - } + nImageIndex = ImageList_GetImageCount(g_ListViewImages); + if (pDlgContext->bInverseSort) + nImageIndex -= 2; + else + nImageIndex -= 1; - } // if (itable->Index == c + WIN32K_START_INDEX) + supUpdateLvColumnHeaderImage( + hwndListView, + pDlgContext->lvColumnCount, + pDlgContext->lvColumnToSort, + nImageIndex); + } + break; - itable = itable->NextService; + case NM_DBLCLK: - } //while (itable != 0); + iSelectionMark = ListView_GetSelectionMark(hwndListView); + if (iSelectionMark >= 0) { + lpItem = supGetItemText(hwndListView, iSelectionMark, 3, NULL); + if (lpItem) { + RtlSecureZeroMemory(szBuffer, sizeof(szBuffer)); + if (supGetWin32FileName(lpItem, szBuffer, MAX_PATH)) + supShowProperties(hwndDlg, szBuffer); + supHeapFree(lpItem); } + } + break; - // - // Output table. - // - W32pServiceTable.Table = (PSERVICETABLEENTRY)supHeapAlloc(sizeof(SERVICETABLEENTRY) * w32k_limit); - if (W32pServiceTable.Table) { - - W32pServiceTable.Allocated = TRUE; - W32pServiceTable.Base = kernelWin32kBase; - - // - // Convert table to output format. - // - W32pServiceTable.Limit = 0; - itable = table; - while (itable != 0) { - - // - // Service Id. - // - ServiceEntry = &W32pServiceTable.Table[W32pServiceTable.Limit]; - - ServiceEntry->ServiceId = itable->Index; + default: + return FALSE; + } - // - // Routine real address. - // - if (itable->KernelStubTargetAddress) { - // - // Output stub target address. - // - ServiceEntry->Address = itable->KernelStubTargetAddress; + return TRUE; +} - } - else { - // - // Query failed, output stub address. - // - ServiceEntry->Address = itable->KernelStubAddress; +/* +* SdtDlgOnInit +* +* Purpose: +* +* KiServiceTable Dialog WM_INITDIALOG handler. +* +*/ +VOID SdtDlgOnInit( + _In_ HWND hwndDlg, + _In_ LPARAM lParam +) +{ + INT iImage = ImageList_GetImageCount(g_ListViewImages) - 1; + EXTRASCONTEXT* pDlgContext = (EXTRASCONTEXT*)lParam; - } + INT SbParts[] = { 400, -1 }; + WCHAR szText[100]; - // - // Remember service name. - // - MultiByteToWideChar( - CP_ACP, - 0, - itable->Name, - (INT)_strlen_a(itable->Name), - ServiceEntry->Name, - MAX_PATH); + LVCOLUMNS_DATA columnData[] = + { + { L"Id", 80, LVCFMT_LEFT | LVCFMT_BITMAP_ON_RIGHT, iImage }, + { L"Service Name", 280, LVCFMT_LEFT | LVCFMT_BITMAP_ON_RIGHT, I_IMAGENONE }, + { L"Address", 130, LVCFMT_LEFT | LVCFMT_BITMAP_ON_RIGHT, I_IMAGENONE }, + { L"Module", 220, LVCFMT_LEFT | LVCFMT_BITMAP_ON_RIGHT, I_IMAGENONE } + }; - W32pServiceTable.Limit += 1; + SetProp(hwndDlg, T_DLGCONTEXT, (HANDLE)lParam); + supCenterWindowSpecifyParent(hwndDlg, g_hwndMain); - itable = itable->NextService; - } + pDlgContext->lvColumnHit = -1; + pDlgContext->lvItemHit = -1; - } + pDlgContext->hwndDlg = hwndDlg; + pDlgContext->StatusBar = GetDlgItem(hwndDlg, ID_EXTRASLIST_STATUSBAR); + SendMessage(pDlgContext->StatusBar, SB_SETPARTS, 2, (LPARAM)&SbParts); - } // if (W32pServiceTable.Allocated == FALSE) + _strcpy(szText, TEXT("Viewing ")); + if (pDlgContext->DialogMode == SST_Ntos) + _strcat(szText, TEXT("ntoskrnl service table")); + else + _strcat(szText, TEXT("win32k service table")); - bResult = W32pServiceTable.Allocated; + SetWindowText(hwndDlg, szText); - } - __finally { + extrasSetDlgIcon(pDlgContext); - if (AbnormalTermination()) - supReportAbnormalTermination(__FUNCTIONW__); + pDlgContext->ListView = GetDlgItem(hwndDlg, ID_EXTRASLIST); + if (pDlgContext->ListView) { // - // Restore default search order. + // Set listview imagelist, style flags and theme. // - SetDllDirectory(NULL); + supSetListViewSettings(pDlgContext->ListView, + LVS_EX_FULLROWSELECT | LVS_EX_DOUBLEBUFFER | LVS_EX_LABELTIP, + FALSE, + TRUE, + g_ListViewImages, + LVSIL_SMALL); // - // Unload all loaded modules. + // And columns and remember their count. // - for (PreviousEntry = &LoadedModulesHead, ModuleEntry = LoadedModulesHead.Next; - ModuleEntry != NULL; - PreviousEntry = ModuleEntry, ModuleEntry = ModuleEntry->Next) - { - FreeLibrary(ModuleEntry->hModule); - } - if (EnumerationHeap) RtlDestroyHeap(EnumerationHeap); - if (w32u) FreeLibrary(w32u); - if (w32k) FreeLibrary(w32k); + pDlgContext->lvColumnCount = supAddLVColumnsFromArray( + pDlgContext->ListView, + columnData, + RTL_NUMBER_OF(columnData)); - if (W32SymContext) { - W32SymContext->Parser.UnloadModule(W32SymContext); - SymParserDestroy(W32SymContext); - W32SymContext = NULL; - } + SendMessage(hwndDlg, WM_SIZE, 0, 0); + supListViewEnableRedraw(pDlgContext->ListView, FALSE); + SdtListCreate(pDlgContext->hwndDlg, FALSE, pDlgContext); + supListViewEnableRedraw(pDlgContext->ListView, TRUE); } - - return bResult; } /* -* SdtListCreate +* SdtDialogProc * * Purpose: * -* (Re)Create service table list. +* KiServiceTable Dialog window procedure. * */ -VOID SdtListCreate( +INT_PTR CALLBACK SdtDialogProc( _In_ HWND hwndDlg, - _In_ BOOL fRescan, - _In_ EXTRASCONTEXT * pDlgContext + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam ) { - BOOL bSuccess = FALSE; - ULONG returnStatus; - EXTRASCALLBACK CallbackParam; - PRTL_PROCESS_MODULES pModules = NULL; - LPWSTR lpStatusMsg; - - __try { - - supStatusBarSetText(pDlgContext->StatusBar, 1, TEXT("Initializing table view")); - - pModules = (PRTL_PROCESS_MODULES)supGetLoadedModulesList(NULL); - if (pModules == NULL) { - - supStatusBarSetText(pDlgContext->StatusBar, 1, - TEXT("Could not allocate memory for kernel modules list!")); + EXTRASCONTEXT* pDlgContext; - __leave; + if (uMsg == g_WinObj.SettingsChangeMessage) { + pDlgContext = (EXTRASCONTEXT*)GetProp(hwndDlg, T_DLGCONTEXT); + if (pDlgContext) { + extrasHandleSettingsChange(pDlgContext); } + return TRUE; + } - if (pDlgContext->DialogMode == SST_Ntos) { - - if (fRescan) { - if (KiServiceTable.Allocated) { - KiServiceTable.Allocated = FALSE; - supHeapFree(KiServiceTable.Table); - KiServiceTable.Limit = 0; - } - } + switch (uMsg) { - bSuccess = SdtListCreateTable(); - if (bSuccess) { - SdtListOutputTable(hwndDlg, pModules, &KiServiceTable); - } - else { - supStatusBarSetText(pDlgContext->StatusBar, 1, TEXT("Error dumping table")); - } + case WM_INITDIALOG: + SdtDlgOnInit(hwndDlg, lParam); + break; + case WM_GETMINMAXINFO: + if (lParam) { + supSetMinMaxTrackSize((PMINMAXINFO)lParam, + SDTDLG_TRACKSIZE_MIN_X, + SDTDLG_TRACKSIZE_MIN_Y, + TRUE); } - else if (pDlgContext->DialogMode == SST_Win32k) { - - if (fRescan) { - if (W32pServiceTable.Allocated) { - W32pServiceTable.Allocated = FALSE; - supHeapFree(W32pServiceTable.Table); - W32pServiceTable.Limit = 0; - } - } - - bSuccess = SdtListCreateTableShadow(pModules, &returnStatus); - if (bSuccess) { - - if (returnStatus == ErrShadowApiSetNotFound) { - supStatusBarSetText(pDlgContext->StatusBar, 1, - T_ERRSHADOW_APISETTABLE_NOT_FOUND); - } + break; - SdtListOutputTable(hwndDlg, pModules, &W32pServiceTable); - } - else { + case WM_NOTIFY: + return SdtDlgHandleNotify(hwndDlg, lParam); - switch (returnStatus) { + case WM_SIZE: + pDlgContext = (EXTRASCONTEXT*)GetProp(hwndDlg, T_DLGCONTEXT); + if (pDlgContext) { + extrasSimpleListResize(hwndDlg); + } + break; - case ErrShadowWin32kNotFound: - lpStatusMsg = T_ERRSHADOW_WIN32K_NOT_FOUND; - break; + case WM_DESTROY: + PostQuitMessage(0); + break; - case ErrShadowMemAllocFail: - lpStatusMsg = T_ERRSHADOW_MEMORY_NOT_ALLOCATED; - break; + case WM_CLOSE: + pDlgContext = (EXTRASCONTEXT*)RemoveProp(hwndDlg, T_DLGCONTEXT); + if (pDlgContext) { + extrasRemoveDlgIcon(pDlgContext); + } + DestroyWindow(hwndDlg); + break; - case ErrShadowWin32uLoadFail: - lpStatusMsg = T_ERRSHADOW_WIN32U_LOAD_FAILED; - break; + case WM_COMMAND: - case ErrShadowWin32kLoadFail: - lpStatusMsg = T_ERRSHADOW_WIN32K_LOAD_FAILED; - break; + switch (GET_WM_COMMAND_ID(wParam, lParam)) { - case ErrShadowW32pServiceLimitNotFound: - lpStatusMsg = T_ERRSHADOW_WIN32KLIMIT_NOT_FOUND; - break; + case IDCANCEL: + SendMessage(hwndDlg, WM_CLOSE, 0, 0); + break; - case ErrShadowWin32uMismatch: - lpStatusMsg = T_ERRSHADOW_WIN32U_MISMATCH; - break; + case ID_SDTLIST_SAVE: + pDlgContext = (EXTRASCONTEXT*)GetProp(hwndDlg, T_DLGCONTEXT); + if (pDlgContext) { - case ErrShadowW32pServiceTableNotFound: - lpStatusMsg = T_ERRSHADOW_TABLE_NOT_FOUND; - break; + if (supListViewExportToFile( + TEXT("Table.csv"), + hwndDlg, + pDlgContext->ListView)) + { + supStatusBarSetText(pDlgContext->StatusBar, 1, T_LIST_EXPORT_SUCCESS); + } - case ErrShadowApiSetSchemaMapNotFound: - lpStatusMsg = T_ERRSHADOW_APISETMAP_NOT_FOUND; - break; + } + break; - case ErrShadowApiSetSchemaVerUnknown: - lpStatusMsg = T_ERRSHADOW_APISET_VER_UNKNOWN; - break; + case ID_VIEW_REFRESH: + pDlgContext = (EXTRASCONTEXT*)GetProp(hwndDlg, T_DLGCONTEXT); + if (pDlgContext) { + supListViewEnableRedraw(pDlgContext->ListView, FALSE); + SdtListCreate(hwndDlg, TRUE, pDlgContext); + supListViewEnableRedraw(pDlgContext->ListView, TRUE); + } + break; - default: - lpStatusMsg = TEXT("Unknown error"); - break; - } + case ID_JUMPTOFILE: + pDlgContext = (EXTRASCONTEXT*)GetProp(hwndDlg, T_DLGCONTEXT); + if (pDlgContext) { + supJumpToFileListView(pDlgContext->ListView, 3); + } + break; - supStatusBarSetText(pDlgContext->StatusBar, 1, lpStatusMsg); + case ID_OBJECT_COPY: + pDlgContext = (EXTRASCONTEXT*)GetProp(hwndDlg, T_DLGCONTEXT); + if (pDlgContext) { + supListViewCopyItemValueToClipboard(pDlgContext->ListView, + pDlgContext->lvItemHit, + pDlgContext->lvColumnHit); } + break; + } - } - __finally { + break; - if (AbnormalTermination()) - supReportAbnormalTermination(__FUNCTIONW__); + case WM_CONTEXTMENU: + pDlgContext = (EXTRASCONTEXT*)GetProp(hwndDlg, T_DLGCONTEXT); + if (pDlgContext) { - if (pModules) - supHeapFree(pModules); + supHandleContextMenuMsgForListView(hwndDlg, + wParam, + lParam, + pDlgContext->ListView, + (pfnPopupMenuHandler)SdtHandlePopupMenu, + (PVOID)pDlgContext); + } + break; } - if (bSuccess) { - supStatusBarSetText(pDlgContext->StatusBar, 1, TEXT("Table read - OK")); - CallbackParam.lParam = 0; - CallbackParam.Value = pDlgContext->DialogMode; - ListView_SortItemsEx(pDlgContext->ListView, &SdtDlgCompareFunc, (LPARAM)&CallbackParam); - SetFocus(pDlgContext->ListView); - } + return FALSE; } /* diff --git a/Source/WinObjEx64/extras/extrasSSDTsup.h b/Source/WinObjEx64/extras/extrasSSDTsup.h deleted file mode 100644 index 838f93fc..00000000 --- a/Source/WinObjEx64/extras/extrasSSDTsup.h +++ /dev/null @@ -1,102 +0,0 @@ -/******************************************************************************* -* -* (C) COPYRIGHT AUTHORS, 2022 -* -* TITLE: EXTRASSSDTSUP.H -* -* VERSION: 1.94 -* -* DATE: 04 Jun 2022 -* -* Header with search patterns and definitions used by SSDT dialog routines. -* -* THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF -* ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED -* TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A -* PARTICULAR PURPOSE. -* -*******************************************************************************/ - -#pragma once - -#define INVALID_SERVICE_ENTRY_ID 0xFFFFFFFF -#define WIN32K_START_INDEX 0x1000 - -typedef struct _SERVICETABLEENTRY { - ULONG ServiceId; - ULONG_PTR Address; - WCHAR Name[MAX_PATH + 1]; -} SERVICETABLEENTRY, * PSERVICETABLEENTRY; - -typedef struct _SDT_TABLE { - BOOL Allocated; - ULONG Limit; - ULONG_PTR Base; - PSERVICETABLEENTRY Table; -} SDT_TABLE, * PSDT_TABLE; - -typedef struct _W32K_API_SET_TABLE_HOST { - PWCHAR HostName; - PCHAR TableName; - PCHAR TableSizeName; - ULONG HostEntriesCount; -} W32K_API_SET_TABLE_HOST, * PW32K_API_SET_TABLE_HOST; - -typedef struct _W32K_API_SET_TABLE_ENTRY { - PVOID HostEntriesArray; - W32K_API_SET_TABLE_HOST* Host; -} W32K_API_SET_TABLE_ENTRY, * PW32K_API_SET_TABLE_ENTRY; - -typedef struct _W32K_API_SET_TABLE_ENTRY_V2 { - PVOID HostEntriesArray; - W32K_API_SET_TABLE_HOST* Host; - W32K_API_SET_TABLE_HOST* AliasHost; -} W32K_API_SET_TABLE_ENTRY_V2, * PW32K_API_SET_TABLE_ENTRY_V2; - -#define KSW_KiServiceTable L"KiServiceTable" -#define KSW_KiServiceLimit L"KiServiceLimit" -#define KSW_W32pServiceTable L"W32pServiceTable" -#define KSW_W32pServiceLimit L"W32pServiceLimit" -#define KSA_W32pServiceTable "W32pServiceTable" -#define KSA_W32pServiceLimit "W32pServiceLimit" - - - -// -// Win32kApiSetTable signatures -// - -// lea reg, Win32kApiSetTable -#define IL_Win32kApiSetTable 7 - -// -// InitializeWin32Call search pattern -// -// push rbp -// push r12 -// push r13 -// push r14 -// push r15 -// -BYTE g_pbInitializeWin32CallPattern[] = { - 0x55, 0x41, 0x54, 0x41, 0x55, 0x41, 0x56, 0x41, 0x57 -}; - -// -// Win32kApiSetTable adapter patterns -// -BYTE Win32kApiSetAdapterPattern1[] = { - 0x4C, 0x8B, 0x15 -}; -BYTE Win32kApiSetAdapterPattern2[] = { - 0x48, 0x8B, 0x05 -}; -BYTE Win32kApiSetAdapterPattern3[] = { - 0x4C, 0x8B, 0x1D // mov r11, value -}; - -W32K_API_SET_LOOKUP_PATTERN W32kApiSetAdapters[] = { - { sizeof(Win32kApiSetAdapterPattern1), Win32kApiSetAdapterPattern1 }, - { sizeof(Win32kApiSetAdapterPattern2), Win32kApiSetAdapterPattern2 }, - { sizeof(Win32kApiSetAdapterPattern3), Win32kApiSetAdapterPattern3 } -}; diff --git a/Source/WinObjEx64/extras/extrasUSD.c b/Source/WinObjEx64/extras/extrasUSD.c index 21ab91a4..22d7450a 100644 --- a/Source/WinObjEx64/extras/extrasUSD.c +++ b/Source/WinObjEx64/extras/extrasUSD.c @@ -4,9 +4,9 @@ * * TITLE: EXTRASUSD.C * -* VERSION: 1.94 +* VERSION: 2.00 * -* DATE: 04 Jun 2021 +* DATE: 19 Jun 2021 * * THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF * ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED @@ -15,9 +15,8 @@ * *******************************************************************************/ #include "global.h" -#include "propObjectDump.h" +#include "props.h" #include "extras.h" -#include "extrasUSD.h" #include "treelist/treelist.h" static EXTRASCONTEXT g_UsdDlgContext; @@ -691,7 +690,7 @@ VOID UsdDialogOnInit( ) { UsdDumpSharedRegion(hwndDlg); - supCenterWindowSpecifyParent(hwndDlg, g_WinObj.MainWindow); + supCenterWindowSpecifyParent(hwndDlg, g_hwndMain); } /* diff --git a/Source/WinObjEx64/extras/extrasUSD.h b/Source/WinObjEx64/extras/extrasUSD.h deleted file mode 100644 index 9e8633c9..00000000 --- a/Source/WinObjEx64/extras/extrasUSD.h +++ /dev/null @@ -1,22 +0,0 @@ -/******************************************************************************* -* -* (C) COPYRIGHT AUTHORS, 2015 - 2022 -* -* TITLE: EXTRASUSD.H -* -* VERSION: 1.94 -* -* DATE: 03 Jun 2022 -* -* Common header file for Extras User Shared Data dialog. -* -* THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF -* ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED -* TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A -* PARTICULAR PURPOSE. -* -*******************************************************************************/ -#pragma once - -VOID extrasCreateUsdDialog( - VOID); diff --git a/Source/WinObjEx64/findDlg.c b/Source/WinObjEx64/findDlg.c index abbbcbdc..02ba918e 100644 --- a/Source/WinObjEx64/findDlg.c +++ b/Source/WinObjEx64/findDlg.c @@ -4,9 +4,9 @@ * * TITLE: FINDDLG.C * -* VERSION: 1.94 +* VERSION: 2.00 * -* DATE: 03 Jun 2022 +* DATE: 19 Jun 2022 * * THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF * ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED @@ -15,7 +15,6 @@ * *******************************************************************************/ #include "global.h" -#include "findDlg.h" #define FINDDLG_TRACKSIZE_MIN_X 548 #define FINDDLG_TRACKSIZE_MIN_Y 230 @@ -143,27 +142,40 @@ INT CALLBACK FindDlgCompareFunc( * */ VOID FindDlgAddListItem( - _In_ HWND hList, - _In_ LPWSTR ObjectName, - _In_ LPWSTR TypeName + _In_ HWND hList, + _In_ PUNICODE_STRING ObjectName, + _In_ PUNICODE_STRING TypeName ) { - INT lvItemIndex; - LVITEM lvItem; + BOOL bNeedFree = FALSE; + INT lvItemIndex; + LVITEM lvItem; + LPWSTR lpName; + + UNICODE_STRING normalizedString; + + bNeedFree = supNormalizeUnicodeStringForDisplay(g_obexHeap, ObjectName, &normalizedString); + if (bNeedFree) + lpName = normalizedString.Buffer; + else + lpName = ObjectName->Buffer; RtlSecureZeroMemory(&lvItem, sizeof(lvItem)); lvItem.mask = LVIF_TEXT | LVIF_IMAGE; - lvItem.pszText = ObjectName; - lvItem.iImage = ObManagerGetImageIndexByTypeName(TypeName); + lvItem.pszText = lpName; + lvItem.iImage = ObManagerGetImageIndexByTypeName(TypeName->Buffer); lvItem.iItem = MAXINT; lvItemIndex = ListView_InsertItem(hList, &lvItem); lvItem.mask = LVIF_TEXT; lvItem.iSubItem = 1; - lvItem.pszText = TypeName; + lvItem.pszText = TypeName->Buffer; lvItem.iItem = lvItemIndex; ListView_SetItem(hList, &lvItem); + + if (bNeedFree) + supFreeDuplicatedUnicodeString(g_obexHeap, &normalizedString, FALSE); } /* @@ -437,10 +449,12 @@ VOID FindDlgHandleSearch( _In_ HWND hwndDlg ) { - WCHAR searchString[MAX_PATH + 1], typeName[MAX_PATH + 1]; - LPWSTR pnameStr = (LPWSTR)searchString, ptypeStr = (LPWSTR)typeName; - PFO_LIST_ITEM flist, plist; - ULONG cci; + WCHAR searchString[MAX_PATH + 1], typeName[MAX_PATH + 1]; + PFO_LIST_ITEM flist, plist; + ULONG cci; + + UNICODE_STRING usName, usType; + PUNICODE_STRING pusName = &usName, pusType = &usType; supSetWaitCursor(TRUE); EnableWindow(GetDlgItem(hwndDlg, ID_SEARCH_FIND), FALSE); @@ -461,12 +475,21 @@ VOID FindDlgHandleSearch( flist = NULL; - if (searchString[0] == 0) - pnameStr = NULL; - if (typeName[0] == L'*') - ptypeStr = 0; + if (searchString[0] == 0) { + pusName = NULL; + } + else { + RtlInitUnicodeString(&usName, searchString); + } + if (typeName[0] == L'*') { + pusType = NULL; + } + else { + RtlInitUnicodeString(&usType, typeName); + } - FindObject(KM_OBJECTS_ROOT_DIRECTORY, pnameStr, ptypeStr, &flist); + FindObject(ObGetPredefinedUnicodeString(OBP_ROOT), + pusName, pusType, &flist); // // Disable listview redraw @@ -475,7 +498,7 @@ VOID FindDlgHandleSearch( cci = 0; while (flist != NULL) { - FindDlgAddListItem(g_FindDlgContext.SearchList, flist->ObjectName, flist->ObjectType); + FindDlgAddListItem(g_FindDlgContext.SearchList, &flist->ObjectName, &flist->ObjectType); plist = flist->Prev; supHeapFree(flist); flist = plist; @@ -564,7 +587,7 @@ VOID FindDlgOnInit( } FindDlgAddTypes(hwndDlg); - supCenterWindowSpecifyParent(hwndDlg, g_WinObj.MainWindow); + supCenterWindowSpecifyParent(hwndDlg, g_hwndMain); FindDlgResize(hwndDlg, &g_FindDlgContext); SetActiveWindow(hwndDlg); } diff --git a/Source/WinObjEx64/findDlg.h b/Source/WinObjEx64/findDlg.h deleted file mode 100644 index b2c85de1..00000000 --- a/Source/WinObjEx64/findDlg.h +++ /dev/null @@ -1,22 +0,0 @@ -/******************************************************************************* -* -* (C) COPYRIGHT AUTHORS, 2015 - 2022 -* -* TITLE: FINDDLG.H -* -* VERSION: 1.94 -* -* DATE: 04 Jun 2022 -* -* Common header file for the Find Object dialog. -* -* THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF -* ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED -* TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A -* PARTICULAR PURPOSE. -* -*******************************************************************************/ -#pragma once - -VOID FindDlgCreate( - VOID); diff --git a/Source/WinObjEx64/global.h b/Source/WinObjEx64/global.h index 317a1174..7720be5a 100644 --- a/Source/WinObjEx64/global.h +++ b/Source/WinObjEx64/global.h @@ -4,9 +4,9 @@ * * TITLE: GLOBAL.H * -* VERSION: 1.94 +* VERSION: 2.00 * -* DATE: 02 Jun 2022 +* DATE: 19 Jun 2022 * * Common header file for the Windows Object Explorer. * @@ -88,20 +88,21 @@ #include #include #include +#include +#include #include #include "resource.h" #include "sdk/extdef.h" -#include "wine.h" #include "minirtl/minirtl.h" #include "minirtl/rtltypes.h" -#include "ntos\ntos.h" -#include "ntos\ntalpc.h" -#include "ntos\ntsup.h" -#include "ntos\ntbuilds.h" -#include "ntuser\ntuser.h" +#include "ntos/ntos.h" +#include "ntos/ntalpc.h" +#include "ntos/ntsup.h" +#include "ntos/ntbuilds.h" +#include "ntuser/ntuser.h" #define _NTDEF_ #include @@ -109,18 +110,19 @@ #include "symparser.h" #include "objects.h" -#include "drivers\wdrvprv.h" +#include "drivers/wdrvprv.h" +#include "log/log.h" #include "kldbg.h" +#include "propCommon.h" #include "ui.h" -#include "sup.h" -#include "supConsts.h" +#include "sup/sup.h" +#include "sup/wine.h" +#include "hash.h" +#include "extapi.h" #include "list.h" #include "excepth.h" -#include "extapi.h" #include "plugmngr.h" -#include "hash.h" -#include "log\log.h" -#include "tests\testunit.h" +#include "tests/testunit.h" #if defined(__cplusplus) #include @@ -146,6 +148,23 @@ extern pqsort rtl_qsort; #define RtlStringCchPrintfSecure rtl_swprintf_s #define RtlQuickSort rtl_qsort +typedef struct _WINOBJ_STATS { + ULONG TotalHeapAlloc; + ULONG TotalHeapFree; + ULONG TotalHeapsCreated; + ULONG TotalHeapsDestroyed; + ULONG TotalThreadsCreated; + ULONG64 TotalHeapMemoryAllocated; +#ifdef _DEBUG + ULONG64 MaxHeapAllocatedBlockSize; +#endif +} WINOBJ_STATS, *PWINOBJ_STATS; + +extern WINOBJ_STATS g_WinObjStats; + +#define OBEX_STATS_INC(Name) (_InterlockedIncrement((LONG*)&g_WinObjStats.Name)) +#define OBEX_STATS_INC64(Name, Value) (_InlineInterlockedAdd64((LONG64*)&g_WinObjStats.Name, Value)) + typedef struct _WINOBJ_GLOBALS { BOOLEAN IsWine; BOOLEAN ListViewDisplayGrid; @@ -168,7 +187,9 @@ typedef struct _WINOBJ_GLOBALS { ULONG CurrentDPI; HINSTANCE hInstance; HANDLE Heap; - LPWSTR CurrentObjectPath; + + LIST_ENTRY ObjectPathListHead; + pfnHtmlHelpW HtmlHelpW; RTL_OSVERSIONINFOW osver; @@ -180,11 +201,26 @@ typedef struct _WINOBJ_GLOBALS { extern WINOBJ_GLOBALS g_WinObj; +// +// Shared heap +// +#define g_obexHeap g_WinObj.Heap + +// +// Current object path list +// +#define g_ObjectPathListHead g_WinObj.ObjectPathListHead + #define g_ListViewImages g_WinObj.ListViewImages #define g_ToolBarMenuImages g_WinObj.ToolBarMenuImages #define g_hwndObjectList g_WinObj.ObjectListView #define g_hwndObjectTree g_WinObj.ObjectTreeView + +// +// Main program window +// #define g_hwndMain g_WinObj.MainWindow + #define g_hwndStatusBar g_WinObj.MainWindowStatusBar #define g_hwndToolBar g_WinObj.MainWindowToolBar #define g_hwndSplitter g_WinObj.MainWindowSplitter diff --git a/Source/WinObjEx64/hash.c b/Source/WinObjEx64/hash.c index e6a8a9f4..47ffa59d 100644 --- a/Source/WinObjEx64/hash.c +++ b/Source/WinObjEx64/hash.c @@ -4,9 +4,9 @@ * * TITLE: HASH.C * -* VERSION: 1.93 +* VERSION: 2.00 * -* DATE: 13 May 2022 +* DATE: 19 Jun 2022 * * THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF * ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED diff --git a/Source/WinObjEx64/hash.h b/Source/WinObjEx64/hash.h index c9f4329f..5657127a 100644 --- a/Source/WinObjEx64/hash.h +++ b/Source/WinObjEx64/hash.h @@ -4,9 +4,9 @@ * * TITLE: HASH.H * -* VERSION: 1.93 +* VERSION: 2.00 * -* DATE: 13 May 2022 +* DATE: 19 Jun 2022 * * Header file for the hash support routines. * diff --git a/Source/WinObjEx64/kldbg.c b/Source/WinObjEx64/kldbg.c index 98228668..5e9b9184 100644 --- a/Source/WinObjEx64/kldbg.c +++ b/Source/WinObjEx64/kldbg.c @@ -4,9 +4,9 @@ * * TITLE: KLDBG.C, based on KDSubmarine by Evilcry * -* VERSION: 1.94 +* VERSION: 2.00 * -* DATE: 07 Jun 2022 +* DATE: 19 Jun 2022 * * MINIMUM SUPPORTED OS WINDOWS 7 * @@ -32,12 +32,70 @@ KLDBGCONTEXT g_kdctx; //Build number ULONG g_NtBuildNumber; +WCHAR g_ObNameNormalizationSymbol = OBJ_NAME_NORMALIZATION_SYMBOL; + //Callbacks NOTIFICATION_CALLBACKS g_SystemCallbacks; //Context private data KLDBGPDATA g_kdpdata; +static UNICODE_STRING g_usObjectsRootDirectory = { + sizeof(KM_OBJECTS_ROOT_DIRECTORY) - sizeof(WCHAR), + sizeof(KM_OBJECTS_ROOT_DIRECTORY), + KM_OBJECTS_ROOT_DIRECTORY +}; + +static UNICODE_STRING g_usDirectoryType = { + sizeof(OBTYPE_NAME_DIRECTORY) - sizeof(WCHAR), + sizeof(OBTYPE_NAME_DIRECTORY), + OBTYPE_NAME_DIRECTORY +}; + +static UNICODE_STRING g_usObjectTypesDirectory = { + sizeof(OBTYPES_DIRECTORY) - sizeof(WCHAR), + sizeof(OBTYPES_DIRECTORY), + OBTYPES_DIRECTORY +}; + +static UNICODE_STRING g_usGlobalRoot = { + sizeof(OB_GLOBALROOT) - sizeof(WCHAR), + sizeof(OB_GLOBALROOT), + OB_GLOBALROOT +}; + +static UNICODE_STRING g_usGlobalNamespace = { + sizeof(OB_GLOBALNAMESPACE) - sizeof(WCHAR), + sizeof(OB_GLOBALNAMESPACE), + OB_GLOBALNAMESPACE +}; + +/* +* ObGetPredefinedUnicodeString +* +* Purpose: +* +* Return pointer to constant unicode string by id. +* +*/ +PUNICODE_STRING ObGetPredefinedUnicodeString( + _In_ ULONG Index +) +{ + switch (Index) { + case OBP_GLOBALNAMESPACE: + return &g_usGlobalNamespace; + case OBP_GLOBAL: + return &g_usGlobalRoot; + case OBP_OBTYPES: + return &g_usObjectTypesDirectory; + case OBP_DIRECTORY: + return &g_usDirectoryType; + case OBP_ROOT: + default: + return &g_usObjectsRootDirectory; + } +} /* * ObFindAddress @@ -861,31 +919,23 @@ PVOID ObDumpFltFilterObjectVersionAware( * Dump UNICODE_STRING from kernel space. * */ +_Success_(return) BOOLEAN kdDumpUnicodeString( _In_ PUNICODE_STRING InputString, _Out_ PUNICODE_STRING OutputString, - _Out_opt_ PVOID* ReferenceBufferPtr, - _In_ BOOLEAN IsKernelPtr + _Out_opt_ PVOID* ReferenceStringBuffer, + _In_ BOOLEAN IsKernelPointer ) { ULONG readBytes = 0; - SIZE_T dumpSize; LPWSTR lpStringBuffer; - UNICODE_STRING uStr; - - OutputString->Buffer = NULL; - OutputString->Length = 0; - OutputString->MaximumLength = 0; + UNICODE_STRING string; - if (ReferenceBufferPtr) - *ReferenceBufferPtr = NULL; - - RtlInitEmptyUnicodeString(&uStr, NULL, 0); - - if (IsKernelPtr) { + if (IsKernelPointer) { + RtlInitEmptyUnicodeString(&string, NULL, 0); if (kdReadSystemMemoryEx((ULONG_PTR)InputString, - &uStr, + &string, sizeof(UNICODE_STRING), &readBytes)) { @@ -894,40 +944,36 @@ BOOLEAN kdDumpUnicodeString( } } else { - - uStr.Buffer = InputString->Buffer; - uStr.Length = InputString->Length; - uStr.MaximumLength = InputString->MaximumLength; - + string = *InputString; } - if (uStr.Length == 0 || uStr.MaximumLength == 0) + if (string.Length == 0 || string.MaximumLength == 0) return FALSE; - dumpSize = (SIZE_T)uStr.MaximumLength + MAX_PATH; - lpStringBuffer = (LPWSTR)supHeapAlloc(dumpSize); - if (lpStringBuffer == NULL) - return FALSE; + lpStringBuffer = (LPWSTR)supHeapAlloc(string.Length + sizeof(UNICODE_NULL)); + if (lpStringBuffer) { - if (kdReadSystemMemoryEx((ULONG_PTR)uStr.Buffer, - lpStringBuffer, - uStr.Length, - &readBytes)) - { - if (readBytes == uStr.Length) { + if (kdReadSystemMemoryEx((ULONG_PTR)string.Buffer, + lpStringBuffer, + string.Length, + &readBytes)) + { + if (readBytes == string.Length) { - OutputString->Buffer = lpStringBuffer; - OutputString->Length = uStr.Length; - OutputString->MaximumLength = uStr.MaximumLength; + OutputString->Buffer = lpStringBuffer; + OutputString->Length = string.Length; + OutputString->MaximumLength = string.MaximumLength; - if (ReferenceBufferPtr) - *ReferenceBufferPtr = uStr.Buffer; + if (ReferenceStringBuffer) + *ReferenceStringBuffer = string.Buffer; - return TRUE; + return TRUE; + } } + + supHeapFree(lpStringBuffer); } - supHeapFree(lpStringBuffer); return FALSE; } @@ -1524,84 +1570,39 @@ BOOL kdFindKiServiceTable( } /* -* ObGetDirectoryObjectAddress -* -* Purpose: -* -* Obtain directory object kernel address by: -* 1) opening directory by name -* 2) quering resulted handle in NtQuerySystemInformation(SystemExtendedHandleInformation) handle dump -* -*/ -BOOL ObGetDirectoryObjectAddress( - _In_opt_ LPWSTR lpDirectory, - _Inout_ PULONG_PTR lpRootAddress, - _Inout_opt_ PUSHORT lpTypeIndex -) -{ - BOOL bFound = FALSE; - HANDLE hDirectory = NULL; - LPWSTR lpTarget; - - if (lpRootAddress == NULL) - return bFound; - - if (lpDirectory == NULL) { - lpTarget = KM_OBJECTS_ROOT_DIRECTORY; - } - else { - lpTarget = lpDirectory; - } - - supOpenDirectory(&hDirectory, NULL, lpTarget, DIRECTORY_QUERY); - if (hDirectory) { - - bFound = supQueryObjectFromHandle(hDirectory, - lpRootAddress, - lpTypeIndex); - - NtClose(hDirectory); - } - return bFound; -} - -/* -* ObQueryNameString +* ObQueryNameStringFromAddress * * Purpose: * -* Reads object name from kernel memory. +* Reads object name from kernel memory if present. * * If HeapHandle is g_WinObj use supHeapFree to release allocated memory. * */ -LPWSTR ObQueryNameString( +_Success_(return) +BOOL ObQueryNameStringFromAddress( + _In_ HANDLE HeapHandle, _In_ ULONG_PTR NameInfoAddress, - _Out_opt_ PSIZE_T ReturnLength, - _In_ HANDLE HeapHandle + _Out_ PUNICODE_STRING NameString ) { SIZE_T allocLength; LPWSTR objectName = NULL; - OBJECT_HEADER_NAME_INFO nameInfo; - if (ReturnLength) - *ReturnLength = 0; - RtlSecureZeroMemory(&nameInfo, sizeof(OBJECT_HEADER_NAME_INFO)); if (kdReadSystemMemory(NameInfoAddress, &nameInfo, sizeof(OBJECT_HEADER_NAME_INFO))) { - if (nameInfo.Name.Length) { + if (nameInfo.Name.Length && + supUnicodeStringValid(&nameInfo.Name)) { - allocLength = nameInfo.Name.Length + sizeof(UNICODE_NULL); + allocLength = nameInfo.Name.Length; - objectName = (LPWSTR)RtlAllocateHeap(HeapHandle, - HEAP_ZERO_MEMORY, - allocLength); + objectName = (LPWSTR)supHeapAllocEx(HeapHandle, + allocLength + sizeof(UNICODE_NULL)); if (objectName != NULL) { @@ -1611,13 +1612,15 @@ LPWSTR ObQueryNameString( objectName, nameInfo.Name.Length)) { - if (ReturnLength) - *ReturnLength = allocLength; + NameString->Buffer = objectName; + NameString->Length = nameInfo.Name.Length; + NameString->MaximumLength = nameInfo.Name.MaximumLength; + + return TRUE; } else { - RtlFreeHeap(HeapHandle, - 0, + supHeapFreeEx(HeapHandle, objectName); objectName = NULL; @@ -1625,9 +1628,10 @@ LPWSTR ObQueryNameString( } } + } - return objectName; + return FALSE; } /* @@ -1650,16 +1654,16 @@ LPWSTR ObQueryNameString( * Pointer to OBJINFO structure allocated from WinObjEx heap and filled with kernel data. * */ -POBJINFO ObpCopyObjectBasicInfo( +POBEX_OBJECT_INFORMATION ObpCopyObjectBasicInfo( _In_ ULONG_PTR ObjectAddress, _In_ ULONG_PTR ObjectHeaderAddress, _In_ BOOL ObjectHeaderAddressValid, _In_opt_ POBJECT_HEADER DumpedObjectHeader ) { - ULONG_PTR HeaderAddress = 0, InfoHeaderAddress = 0; - POBJINFO lpData = NULL; - OBJECT_HEADER ObjectHeader, * pObjectHeader; + ULONG_PTR HeaderAddress = 0, InfoHeaderAddress = 0; + OBJECT_HEADER ObjectHeader, *pObjectHeader; + POBEX_OBJECT_INFORMATION lpData = NULL; // // Convert object address to object header address. @@ -1703,7 +1707,7 @@ POBJINFO ObpCopyObjectBasicInfo( // // Allocate OBJINFO structure, exit on fail. // - lpData = (POBJINFO)supHeapAlloc(sizeof(OBJINFO)); + lpData = (POBEX_OBJECT_INFORMATION)supHeapAlloc(sizeof(OBEX_OBJECT_INFORMATION)); if (lpData == NULL) return NULL; @@ -1713,8 +1717,7 @@ POBJINFO ObpCopyObjectBasicInfo( // // Copy object header. // - supCopyMemory(&lpData->ObjectHeader, - sizeof(OBJECT_HEADER), + RtlCopyMemory(&lpData->ObjectHeader, pObjectHeader, sizeof(OBJECT_HEADER)); @@ -1737,7 +1740,49 @@ POBJINFO ObpCopyObjectBasicInfo( } /* -* ObpWalkDirectory +* ObQueryObjectByAddress +* +* Purpose: +* +* Look for object at specified address. +* Returned object memory must be released with supHeapFree when object is no longer needed. +* +*/ +POBEX_OBJECT_INFORMATION ObQueryObjectByAddress( + _In_ ULONG_PTR ObjectAddress +) +{ + ULONG_PTR ObjectHeaderAddress; + OBJECT_HEADER ObjectHeader; + + if (ObjectAddress < g_kdctx.SystemRangeStart) + return NULL; + + if (!kdConnectDriver()) + return NULL; + + // + // Read object header, fail is critical. + // + RtlSecureZeroMemory(&ObjectHeader, sizeof(OBJECT_HEADER)); + ObjectHeaderAddress = (ULONG_PTR)OBJECT_TO_OBJECT_HEADER(ObjectAddress); + + if (!kdReadSystemMemory(ObjectHeaderAddress, + &ObjectHeader, + sizeof(OBJECT_HEADER))) + { + kdReportReadErrorSimple(__FUNCTIONW__, ObjectHeaderAddress, sizeof(OBJECT_HEADER)); + return NULL; + } + + return ObpCopyObjectBasicInfo(ObjectAddress, + ObjectHeaderAddress, + TRUE, + &ObjectHeader); +} + +/* +* ObpFindObjectInDirectory * * Purpose: * @@ -1750,262 +1795,194 @@ POBJINFO ObpCopyObjectBasicInfo( * this routine change as we rely here only on HashBuckets which is on same offset. * */ -POBJINFO ObpWalkDirectory( - _In_ LPWSTR lpObjectToFind, +POBEX_OBJECT_INFORMATION ObpFindObjectInDirectory( + _In_ PUNICODE_STRING ObjectName, _In_ ULONG_PTR DirectoryAddress ) { - BOOL bFound = FALSE; - UINT BucketId; - SIZE_T retSize; - LPWSTR lpObjectName; + BOOL bFound = FALSE; + ULONG i; + OBJECT_HEADER ObjectHeader; + OBJECT_DIRECTORY DirectoryObject; + OBJECT_DIRECTORY_ENTRY DirectoryEntry; + ULONG_PTR ObjectHeaderAddress, HeadItem, LookupItem, InfoHeaderAddress; - OBJECT_HEADER ObjectHeader; - OBJECT_DIRECTORY DirectoryObject; - OBJECT_DIRECTORY_ENTRY DirectoryEntry; + UNICODE_STRING NameString; - __try { + RtlSecureZeroMemory(&DirectoryObject, sizeof(OBJECT_DIRECTORY)); - if (lpObjectToFind == NULL) - return NULL; + // + // Read object directory at address. + // + if (!kdReadSystemMemory(DirectoryAddress, + &DirectoryObject, + sizeof(OBJECT_DIRECTORY))) + { + kdReportReadErrorSimple(__FUNCTIONW__, DirectoryAddress, sizeof(OBJECT_DIRECTORY)); + return NULL; + } - // - // Read object directory at address. - // - RtlSecureZeroMemory(&DirectoryObject, sizeof(OBJECT_DIRECTORY)); + // + // Check if root special case. + // + if (supIsRootDirectory(ObjectName)) { - if (!kdReadSystemMemory(DirectoryAddress, - &DirectoryObject, - sizeof(OBJECT_DIRECTORY))) - { - kdReportReadErrorSimple(__FUNCTIONW__, DirectoryAddress, sizeof(OBJECT_DIRECTORY)); - return NULL; - } + return ObpCopyObjectBasicInfo(DirectoryAddress, + 0, + FALSE, + NULL); - // - // Check if root special case. - // - if (_strcmpi(lpObjectToFind, KM_OBJECTS_ROOT_DIRECTORY) == 0) { + } - return ObpCopyObjectBasicInfo(DirectoryAddress, - 0, - FALSE, - NULL); - } + // + // Not a root directory, scan given object directory. + // + for (i = 0; i < NUMBER_HASH_BUCKETS; i++) { - // - // Not a root directory, scan given object directory. - // - for (BucketId = 0; BucketId < NUMBER_HASH_BUCKETS; BucketId++) { + HeadItem = (ULONG_PTR)DirectoryObject.HashBuckets[i]; + if (HeadItem != 0) { - HeadItem = (ULONG_PTR)DirectoryObject.HashBuckets[BucketId]; - if (HeadItem != 0) { + LookupItem = HeadItem; - LookupItem = HeadItem; + do { - do { + // + // Read object directory entry, exit on fail. + // + RtlSecureZeroMemory(&DirectoryEntry, sizeof(OBJECT_DIRECTORY_ENTRY)); - // - // Read object directory entry, exit on fail. - // - RtlSecureZeroMemory(&DirectoryEntry, sizeof(OBJECT_DIRECTORY_ENTRY)); + if (!kdReadSystemMemory(LookupItem, + &DirectoryEntry, + sizeof(OBJECT_DIRECTORY_ENTRY))) + { + kdReportReadErrorSimple(__FUNCTIONW__, LookupItem, sizeof(OBJECT_DIRECTORY_ENTRY)); + break; + } - if (!kdReadSystemMemory(LookupItem, - &DirectoryEntry, - sizeof(OBJECT_DIRECTORY_ENTRY))) - { - kdReportReadErrorSimple(__FUNCTIONW__, LookupItem, sizeof(OBJECT_DIRECTORY_ENTRY)); - break; - } + // + // Read object header, skip entry on fail. + // + RtlSecureZeroMemory(&ObjectHeader, sizeof(OBJECT_HEADER)); + ObjectHeaderAddress = (ULONG_PTR)OBJECT_TO_OBJECT_HEADER(DirectoryEntry.Object); - // - // Read object header, skip entry on fail. - // - RtlSecureZeroMemory(&ObjectHeader, sizeof(OBJECT_HEADER)); - ObjectHeaderAddress = (ULONG_PTR)OBJECT_TO_OBJECT_HEADER(DirectoryEntry.Object); + if (!kdReadSystemMemory(ObjectHeaderAddress, + &ObjectHeader, + sizeof(OBJECT_HEADER))) + { + kdReportReadErrorSimple(__FUNCTIONW__, ObjectHeaderAddress, sizeof(OBJECT_HEADER)); + goto NextItem; + } - if (!kdReadSystemMemory(ObjectHeaderAddress, - &ObjectHeader, - sizeof(OBJECT_HEADER))) - { - kdReportReadErrorSimple(__FUNCTIONW__, ObjectHeaderAddress, sizeof(OBJECT_HEADER)); - goto NextItem; - } + // + // Check if object has name, skip entry on fail. + // + InfoHeaderAddress = 0; + if (!ObHeaderToNameInfoAddress(ObjectHeader.InfoMask, + ObjectHeaderAddress, + &InfoHeaderAddress, + HeaderNameInfoFlag)) + { + goto NextItem; + } + + // + // If object has name, query it. + // + if (ObQueryNameStringFromAddress(g_obexHeap, + InfoHeaderAddress, + &NameString)) + { // - // Check if object has name, skip entry on fail. + // Compare object name with what we look for. // - InfoHeaderAddress = 0; + bFound = RtlEqualUnicodeString(ObjectName, &NameString, TRUE); + supHeapFreeEx(g_obexHeap, NameString.Buffer); - if (!ObHeaderToNameInfoAddress(ObjectHeader.InfoMask, - ObjectHeaderAddress, - &InfoHeaderAddress, - HeaderNameInfoFlag)) - { - goto NextItem; - } + if (bFound) { - // - // If object has name, query it. - // - retSize = 0; - lpObjectName = ObQueryNameString(InfoHeaderAddress, &retSize, g_WinObj.Heap); - if ((lpObjectName != NULL) && (retSize != 0)) { + return ObpCopyObjectBasicInfo( + (ULONG_PTR)DirectoryEntry.Object, + ObjectHeaderAddress, + TRUE, + &ObjectHeader); - // - // Compare full object names. - // - bFound = (_strcmpi(lpObjectName, lpObjectToFind) == 0); - supHeapFree(lpObjectName); + } - // - // if they're identical, allocate item info and copy it. - // - if (bFound) { + } - return ObpCopyObjectBasicInfo((ULONG_PTR)DirectoryEntry.Object, - ObjectHeaderAddress, - TRUE, - &ObjectHeader); + NextItem: + LookupItem = (ULONG_PTR)DirectoryEntry.ChainLink; - } - } + } while (LookupItem != 0); - NextItem: - LookupItem = (ULONG_PTR)DirectoryEntry.ChainLink; - } while (LookupItem != 0); - } - } + } // HeadItem != 0 + } // for - } - __except (WOBJ_EXCEPTION_FILTER) { - return NULL; - } return NULL; } - + /* -* ObQueryObjectByAddress +* ObGetObjectAddressForDirectory * * Purpose: * -* Look for object at specified address. -* Returned object memory must be released with supHeapFree when object is no longer needed. +* Obtain directory object kernel address by: +* 1) opening directory by name +* 2) quering resulted handle in NtQuerySystemInformation(SystemExtendedHandleInformation) handle dump * */ -POBJINFO ObQueryObjectByAddress( - _In_ ULONG_PTR ObjectAddress +_Success_(return) +BOOL ObGetObjectAddressForDirectory( + _In_ PUNICODE_STRING DirectoryName, + _Out_ PULONG_PTR lpRootAddress, + _Out_opt_ PUSHORT lpTypeIndex ) { - ULONG_PTR ObjectHeaderAddress; - OBJECT_HEADER ObjectHeader; + BOOL bFound = FALSE; + HANDLE hDirectory = NULL; - if (ObjectAddress < g_kdctx.SystemRangeStart) - return NULL; - - if (!kdConnectDriver()) - return NULL; - - // - // Read object header, fail is critical. - // - RtlSecureZeroMemory(&ObjectHeader, sizeof(OBJECT_HEADER)); - ObjectHeaderAddress = (ULONG_PTR)OBJECT_TO_OBJECT_HEADER(ObjectAddress); + if (!NT_SUCCESS(supOpenDirectoryEx(&hDirectory, NULL, DirectoryName, DIRECTORY_QUERY))) + return FALSE; - if (!kdReadSystemMemory(ObjectHeaderAddress, - &ObjectHeader, - sizeof(OBJECT_HEADER))) - { - kdReportReadErrorSimple(__FUNCTIONW__, ObjectHeaderAddress, sizeof(OBJECT_HEADER)); - return NULL; - } + bFound = supQueryObjectFromHandle(hDirectory, + lpRootAddress, + lpTypeIndex); - return ObpCopyObjectBasicInfo(ObjectAddress, - ObjectHeaderAddress, - TRUE, - &ObjectHeader); + NtClose(hDirectory); + + return bFound; } /* -* ObQueryObject +* ObQueryObjectInDirectory * * Purpose: * * Look for object inside specified directory. -* If object is directory look for it in upper directory. * Returned object memory must be released with supHeapFree when object is no longer needed. * */ -POBJINFO ObQueryObject( - _In_ LPWSTR lpDirectory, - _In_ LPWSTR lpObjectName +POBEX_OBJECT_INFORMATION ObQueryObjectInDirectory( + _In_ PUNICODE_STRING ObjectName, + _In_ PUNICODE_STRING DirectoryName ) { - BOOL needFree = FALSE; - ULONG_PTR DirectoryAddress; - SIZE_T i, l, rdirLen, ldirSz; - LPWSTR SingleDirName, LookupDirName; + ULONG_PTR directoryAddress = 0; if (!kdConnectDriver()) return NULL; - __try { - - LookupDirName = lpDirectory; - - // - // 1) Check if object is directory self - // Extract directory name and compare (case insensitive) with object name - // Else go to 3 - // - l = 0; - rdirLen = _strlen(lpDirectory); - for (i = 0; i < rdirLen; i++) { - if (lpDirectory[i] == TEXT('\\')) - l = i + 1; - } - SingleDirName = &lpDirectory[l]; - if (_strcmpi(SingleDirName, lpObjectName) == 0) { - // - // 2) If we are looking for directory itself, move search directory up - // e.g. lpDirectory = \ObjectTypes, lpObjectName = ObjectTypes then lpDirectory = \ - // - ldirSz = rdirLen * sizeof(WCHAR) + sizeof(UNICODE_NULL); - LookupDirName = (LPWSTR)supHeapAlloc(ldirSz); - if (LookupDirName == NULL) - return NULL; - - needFree = TRUE; - - //special case for root - if (l == 1) l++; - - supCopyMemory(LookupDirName, ldirSz, lpDirectory, (l - 1) * sizeof(WCHAR)); - } - - // - // 3) Get Directory address where we will look for object - // - DirectoryAddress = 0; - if (ObGetDirectoryObjectAddress(LookupDirName, &DirectoryAddress, NULL)) { - - if (needFree) - supHeapFree(LookupDirName); - - // - // 4) Find object in directory by name (case insensitive) - // - return ObpWalkDirectory(lpObjectName, DirectoryAddress); - - } - } - - __except (WOBJ_EXCEPTION_FILTER) { + if (!ObGetObjectAddressForDirectory(DirectoryName, + &directoryAddress, + NULL)) + { return NULL; } - return NULL; + + return ObpFindObjectInDirectory(ObjectName, directoryAddress); } /* @@ -2102,7 +2079,7 @@ BOOL ObpEnumeratePrivateNamespaceTable( OBJECT_NAMESPACE_ENTRY LookupEntry; if (ListHeap == NULL) - ListHeap = g_WinObj.Heap; + ListHeap = g_obexHeap; // // Dump namespace lookup table. @@ -2171,8 +2148,7 @@ BOOL ObpEnumeratePrivateNamespaceTable( // // Allocate object entry // - ObjectEntry = (POBJREF)RtlAllocateHeap(ListHeap, - HEAP_ZERO_MEMORY, + ObjectEntry = (POBJREF)supHeapAllocEx(ListHeap, sizeof(OBJREF)); if (ObjectEntry) { @@ -2213,9 +2189,7 @@ BOOL ObpEnumeratePrivateNamespaceTable( // // Copy object name if exist. // - ObjectEntry->ObjectName = ObQueryNameString(InfoHeaderAddress, - NULL, - ListHeap); + ObQueryNameStringFromAddress(ListHeap, InfoHeaderAddress, &ObjectEntry->Name); } @@ -2271,12 +2245,12 @@ BOOL ObEnumeratePrivateNamespaceTable( typedef struct _OB_NAME_ELEMENT { LIST_ENTRY ListEntry; - LPCWSTR lpszName; + UNICODE_STRING Name; } OB_NAME_ELEMENT, * POB_NAME_ELEMENT; BOOL ObpAddNameElementEntry( _In_ PLIST_ENTRY ListHead, - _In_opt_ LPCWSTR ElementName + _In_ PUNICODE_STRING ElementName ) { POB_NAME_ELEMENT pObElement; @@ -2285,7 +2259,7 @@ BOOL ObpAddNameElementEntry( if (pObElement == NULL) return FALSE; - pObElement->lpszName = ElementName; + pObElement->Name = *ElementName; InsertHeadList(ListHead, &pObElement->ListEntry); @@ -2295,22 +2269,23 @@ BOOL ObpAddNameElementEntry( BOOL ObpDumpNameElementSpecial( _In_ PLIST_ENTRY ListHead, _In_ LPWSTR SpecialElement, - _In_ DWORD Size + _In_ ULONG Size ) { - SIZE_T allocSize; - LPWSTR lpName; + UNICODE_STRING element; + + element.Buffer = (PWSTR)supHeapAlloc(Size + sizeof(UNICODE_NULL)); - allocSize = Size + sizeof(UNICODE_NULL); - lpName = (LPWSTR)supHeapAlloc(allocSize); - if (lpName == NULL) { + if (element.Buffer == NULL) { return FALSE; } + + _strcpy(element.Buffer, SpecialElement); + element.Length = (USHORT)(Size - sizeof(UNICODE_NULL)); + element.MaximumLength = (USHORT)Size; - _strcpy(lpName, SpecialElement); - - if (!ObpAddNameElementEntry(ListHead, lpName)) { - supHeapFree(lpName); + if (!ObpAddNameElementEntry(ListHead, &element)) { + supHeapFree(element.Buffer); return FALSE; } @@ -2323,9 +2298,9 @@ BOOL ObpDumpNameElement( _Out_ PSIZE_T ElementLength ) { - SIZE_T allocSize; USHORT nameLength; - LPWSTR lpName; + LPWSTR stringBuffer; + UNICODE_STRING element; *ElementLength = 0; @@ -2333,22 +2308,25 @@ BOOL ObpDumpNameElement( if (nameLength == 0) return FALSE; - allocSize = nameLength + sizeof(UNICODE_NULL); - lpName = (LPWSTR)supHeapAlloc(allocSize); - if (lpName == NULL) { + stringBuffer = (LPWSTR)supHeapAlloc(nameLength + sizeof(UNICODE_NULL)); + if (stringBuffer == NULL) { return FALSE; } if (!kdReadSystemMemory((ULONG_PTR)NameInformation->Name.Buffer, - lpName, + stringBuffer, nameLength)) { - supHeapFree(lpName); + supHeapFree(stringBuffer); return FALSE; } - if (!ObpAddNameElementEntry(ListHead, lpName)) { - supHeapFree(lpName); + element.Buffer = stringBuffer; + element.Length = nameLength; + element.MaximumLength = nameLength + sizeof(UNICODE_NULL); + + if (!ObpAddNameElementEntry(ListHead, &element)) { + supHeapFree(stringBuffer); return FALSE; } @@ -2395,7 +2373,7 @@ SIZE_T ObpDumpObjectName( objectHeaderAddress, &headerInfoAddress, HeaderNameInfoFlag)) - { + { // // Nothing to process, object is unnamed. // @@ -2414,7 +2392,7 @@ SIZE_T ObpDumpObjectName( ObpDumpNameElementSpecial(ListHead, OBP_ERROR_NAME_LITERAL, OBP_ERROR_NAME_LITERAL_SIZE); return OBP_ERROR_NAME_LITERAL_SIZE + sizeof(OBJ_NAME_PATH_SEPARATOR); } - + *NextObject = (ULONG_PTR)nameInfo.Directory; if (ObpDumpNameElement(ListHead, &nameInfo, &pathLength)) @@ -2431,63 +2409,83 @@ SIZE_T ObpDumpObjectName( * This routine if possible builds full object namespace path for given object. * */ -LPWSTR ObQueryFullNamespacePath( - _In_ ULONG_PTR ObjectAddress +_Success_(return) +BOOL ObQueryFullNamespacePath( + _In_ ULONG_PTR ObjectAddress, + _Out_ PUNICODE_STRING Path ) { - ULONG_PTR lookupObject = ObjectAddress, nextObject; - LIST_ENTRY listHead, * listEntry; - POB_NAME_ELEMENT pNameElement; - LPWSTR lpObjectName = NULL; - SIZE_T pathLength, totalLength; + BOOL bResult = FALSE; + ULONG_PTR LookupObject = ObjectAddress, NextObject; + LIST_ENTRY ListHead; + PLIST_ENTRY Next; + POB_NAME_ELEMENT pathElement; + PWSTR stringBuffer = NULL, string; + SIZE_T memIO, length; - if (lookupObject == g_kdctx.DirectoryRootObject) { + UNICODE_STRING resultPath; + + if (LookupObject == g_kdctx.DirectoryRootObject) { + + return supDuplicateUnicodeString(g_obexHeap, + Path, + ObGetPredefinedUnicodeString(OBP_ROOT)); - lpObjectName = (LPWSTR)supHeapAlloc(sizeof(KM_OBJECTS_ROOT_DIRECTORY) + sizeof(UNICODE_NULL)); - if (lpObjectName) { - _strcpy(lpObjectName, KM_OBJECTS_ROOT_DIRECTORY); - } - return lpObjectName; } - InitializeListHead(&listHead); - totalLength = 0; + InitializeListHead(&ListHead); + memIO = 0; - while ((lookupObject != g_kdctx.DirectoryRootObject) && (lookupObject != 0)) { + while ((LookupObject != g_kdctx.DirectoryRootObject) && (LookupObject != 0)) { - nextObject = 0; - totalLength += ObpDumpObjectName(&listHead, lookupObject, &nextObject); - if (totalLength > UNICODE_STRING_MAX_BYTES) + NextObject = 0; + memIO += ObpDumpObjectName(&ListHead, LookupObject, &NextObject); + if (memIO > UNICODE_STRING_MAX_BYTES) break; - lookupObject = nextObject; + LookupObject = NextObject; } // // Build path. // - if (!IsListEmpty(&listHead)) { - - lpObjectName = (LPWSTR)supHeapAlloc(totalLength + sizeof(UNICODE_NULL)); - if (lpObjectName) { - pathLength = 0; - listEntry = listHead.Flink; - while ((listEntry != NULL) && (listEntry != &listHead)) { - pNameElement = CONTAINING_RECORD(listEntry, OB_NAME_ELEMENT, ListEntry); - if (pNameElement->lpszName) { - lpObjectName[pathLength++] = OBJ_NAME_PATH_SEPARATOR; - _strcpy(lpObjectName + pathLength, pNameElement->lpszName); - pathLength += _strlen(pNameElement->lpszName); - supHeapFree((PVOID)pNameElement->lpszName); - } - listEntry = listEntry->Flink; - supHeapFree(pNameElement); + if (!IsListEmpty(&ListHead)) { + + stringBuffer = (PWSTR)supHeapAlloc(memIO + sizeof(UNICODE_NULL)); + if (stringBuffer) { + + resultPath.MaximumLength = (USHORT)memIO + sizeof(UNICODE_NULL); + resultPath.Buffer = stringBuffer; + + string = stringBuffer; + length = 0; + + Next = ListHead.Flink; + while ((Next != NULL) && (Next != &ListHead)) { + + pathElement = CONTAINING_RECORD(Next, OB_NAME_ELEMENT, ListEntry); + + *string++ = OBJ_NAME_PATH_SEPARATOR; + length += sizeof(OBJ_NAME_PATH_SEPARATOR); + + RtlCopyMemory(string, pathElement->Name.Buffer, pathElement->Name.Length); + string = (PWSTR)RtlOffsetToPointer(string, pathElement->Name.Length); + length += pathElement->Name.Length; + + supFreeUnicodeString(g_obexHeap, &pathElement->Name); + + Next = Next->Flink; + } + + resultPath.Length = (USHORT)length; + *Path = resultPath; + bResult = TRUE; } } - return lpObjectName; + return bResult; } /* @@ -2582,11 +2580,14 @@ PVOID kdQueryIopInvalidDeviceRequest( ) { PVOID pHandler = NULL; - POBJINFO pSelfObj; ULONG_PTR drvObjectAddress; DRIVER_OBJECT drvObject; PWDRV_PROVIDER drvProvider; + POBEX_OBJECT_INFORMATION selfDriverObject; + + UNICODE_STRING usDirectory, usName; + // // Lookup using symbols. // @@ -2607,10 +2608,13 @@ PVOID kdQueryIopInvalidDeviceRequest( drvProvider = g_kdctx.DriverContext.Provider; if (drvProvider) { - pSelfObj = ObQueryObject(L"\\Driver", drvProvider->DriverName); - if (pSelfObj) { + RtlInitUnicodeString(&usName, drvProvider->DriverName); + RtlInitUnicodeString(&usDirectory, L"\\Driver"); + + selfDriverObject = ObQueryObjectInDirectory(&usName, &usDirectory); + if (selfDriverObject) { - drvObjectAddress = pSelfObj->ObjectAddress; + drvObjectAddress = selfDriverObject->ObjectAddress; RtlSecureZeroMemory(&drvObject, sizeof(drvObject)); @@ -2626,7 +2630,7 @@ PVOID kdQueryIopInvalidDeviceRequest( if (!kdAddressInNtOsImage(pHandler)) pHandler = NULL; } - supHeapFree(pSelfObj); + supHeapFree(selfDriverObject); } } } @@ -2703,7 +2707,7 @@ VOID kdReportReadError( WCHAR szBuffer[512]; RtlStringCchPrintfSecure(szBuffer, - 512, + RTL_NUMBER_OF(szBuffer), TEXT("%ws 0x%lX, read at 0x%llX, Iosb(0x%lX, 0x%lX), InputBufferLength 0x%lX"), FunctionName, Status, @@ -2806,7 +2810,7 @@ BOOL kdLoadSymbolsForNtImage( if (SymContext->ModuleBase != 0) return TRUE; - supDisplayLoadBanner(TEXT("Please wait...\r\n"), TEXT("Symbols loading"), TRUE); + supDisplayLoadBanner(TEXT("Please wait...\r\n"), TEXT("Symbols loading")); bResult = SymContext->Parser.LoadModule( SymContext, @@ -2893,12 +2897,14 @@ BOOL kdQuerySystemInformation( { PKLDBGCONTEXT Context = (PKLDBGCONTEXT)lpParameter; - // - // Query "\\" directory address and remember directory object type index. - // - ObGetDirectoryObjectAddress(NULL, - &Context->DirectoryRootObject, - &Context->DirectoryTypeIndex); + if (Context->IsFullAdmin) { + // + // Query "\\" directory address and remember directory object type index. + // + ObGetObjectAddressForDirectory(ObGetPredefinedUnicodeString(OBP_ROOT), + &Context->DirectoryRootObject, + &Context->DirectoryTypeIndex); + } // // Remember system range start value. @@ -3540,9 +3546,9 @@ BOOL kdGetFieldOffsetFromSymbol( szLog[0] = 0; RtlStringCchPrintfSecure(szLog, RTL_NUMBER_OF(szLog), - TEXT("%ws(%lu): \"%ws->%ws\", offset 0x%lX"), + TEXT("%ws(%ws): \"%ws->%ws\", offset 0x%lX"), __FUNCTIONW__, - bResult, + (bResult) ? L"SUCCESS" : L"FAIL", SymbolName, FieldName, *Offset); @@ -3632,9 +3638,9 @@ BOOL kdGetAddressFromSymbolEx( szLog[0] = 0; RtlStringCchPrintfSecure(szLog, RTL_NUMBER_OF(szLog), - TEXT("%ws(%lu): \"%ws\" address 0x%llX"), + TEXT("%ws(%ws): \"%ws\" address 0x%llX"), __FUNCTIONW__, - bResult, + (bResult) ? L"SUCCESS" : L"FAIL", SymbolName, address); @@ -3770,44 +3776,51 @@ BOOL CALLBACK symCallbackProc( * */ BOOL symInit( - VOID + _In_opt_ LPWSTR lpSymbolPath, + _In_opt_ LPWSTR lpDbgHelpDll ) { ULONG cch; WCHAR szFileName[MAX_PATH * 2]; + LPWSTR dbgHelpDll = lpDbgHelpDll; if (g_kdctx.NtOsSymContext != NULL) return TRUE; - szFileName[0] = 0; - cch = GetCurrentDirectory(MAX_PATH, szFileName); - if (cch > 0 && cch < MAX_PATH) { - - supPathAddBackSlash(szFileName); + if (lpDbgHelpDll == NULL) { - _strcat(szFileName, TEXT("symdll\\dbghelp.dll")); + szFileName[0] = 0; + cch = GetCurrentDirectory(MAX_PATH, szFileName); + if (cch > 0 && cch < MAX_PATH) { + supPathAddBackSlash(szFileName); + _strcat(szFileName, TEXT("symdll\\dbghelp.dll")); + if (!PathFileExists(szFileName)) + return FALSE; + } + else { + return FALSE; + } - if (PathFileExists(szFileName)) { + dbgHelpDll = szFileName; - if (SymGlobalsInit( - SYMOPT_CASE_INSENSITIVE | - SYMOPT_UNDNAME | - SYMOPT_FAIL_CRITICAL_ERRORS | - SYMOPT_EXACT_SYMBOLS | - SYMOPT_AUTO_PUBLICS, - NULL, - szFileName, - NULL, - g_WinObj.szSystemDirectory, - g_WinObj.szTempDirectory, - (PSYMBOL_REGISTERED_CALLBACK64)symCallbackProc, - (ULONG64)supSymCallbackReportEvent)) - { - g_kdctx.NtOsSymContext = (PVOID)SymParserCreate(); - } - } + } + if (SymGlobalsInit( + SYMOPT_CASE_INSENSITIVE | + SYMOPT_UNDNAME | + SYMOPT_FAIL_CRITICAL_ERRORS | + SYMOPT_EXACT_SYMBOLS | + SYMOPT_AUTO_PUBLICS, + NULL, + dbgHelpDll, + lpSymbolPath, + g_WinObj.szSystemDirectory, + g_WinObj.szTempDirectory, + (PSYMBOL_REGISTERED_CALLBACK64)symCallbackProc, + (ULONG64)supSymCallbackReportEvent)) + { + g_kdctx.NtOsSymContext = (PVOID)SymParserCreate(); } return (g_kdctx.NtOsSymContext != NULL); @@ -3918,12 +3931,29 @@ VOID kdInit( ) { NTSTATUS ntStatus; + OBEX_CONFIG* obexConfig = supGetParametersBlock(); WCHAR szBuffer[MAX_PATH * 2]; + LPWSTR lpSymbolPath = NULL, lpDbgHelpDll = NULL; RtlSecureZeroMemory(&g_kdctx, sizeof(g_kdctx)); RtlSecureZeroMemory(&g_kdpdata, sizeof(g_kdpdata)); RtlSecureZeroMemory(&g_SystemCallbacks, sizeof(g_SystemCallbacks)); + RtlSecureZeroMemory(obexConfig, sizeof(OBEX_CONFIG)); + + if (supReadObexConfiguration(obexConfig)) { + + if (obexConfig->SymbolsDbgHelpDllValid) + lpDbgHelpDll = obexConfig->szSymbolsDbgHelpDll; + + if (obexConfig->SymbolsPathValid) + lpSymbolPath = obexConfig->szSymbolsPath; + + if (obexConfig->szNormalizationSymbol != 0) + g_ObNameNormalizationSymbol = obexConfig->szNormalizationSymbol; + + } + g_kdctx.DriverContext.LoadStatus = STATUS_DRIVER_UNABLE_TO_LOAD; g_kdctx.DriverContext.OpenStatus = STATUS_UNSUCCESSFUL; @@ -3954,7 +3984,7 @@ VOID kdInit( // // Init symbol parser. // - symInit(); + symInit(lpSymbolPath, lpDbgHelpDll); // // Query global variables. @@ -3989,9 +4019,6 @@ VOID kdInit( g_kdctx.MitigationFlags.Signature = TRUE; g_kdctx.MitigationFlags.ASLRPolicy = TRUE; break; - - default: - break; } // diff --git a/Source/WinObjEx64/kldbg.h b/Source/WinObjEx64/kldbg.h index 4ae08eb9..bcf59e66 100644 --- a/Source/WinObjEx64/kldbg.h +++ b/Source/WinObjEx64/kldbg.h @@ -4,9 +4,9 @@ * * TITLE: KLDBG.H * -* VERSION: 1.94 +* VERSION: 2.00 * -* DATE: 05 Jun 2022 +* DATE: 19 Jun 2022 * * Common header file for the Kernel Debugger Driver support. * @@ -97,12 +97,17 @@ #define NT_REG_PREP L"\\Registry\\Machine" #define DRIVER_REGKEY L"%wS\\System\\CurrentControlSet\\Services\\%wS" +#define OBTYPES_DIRECTORY L"\\ObjectTypes" +#define OB_GLOBALROOT L"\\GLOBAL??\\GLOBALROOT" +#define OB_GLOBALNAMESPACE L"\\??" #define OBJECT_SHIFT 8 #define KM_OBJECTS_ROOT_DIRECTORY L"\\" #define OBJ_NAME_PATH_SEPARATOR L'\\' +#define OBJ_NAME_NORMALIZATION_SYMBOL L'?' + #define MM_SYSTEM_RANGE_START_7 0xFFFF080000000000 #define MM_SYSTEM_RANGE_START_8 0xFFFF800000000000 @@ -122,6 +127,15 @@ typedef ULONG_PTR *PUTable; #define OBP_ERROR_NONAME_LITERAL L"" #define OBP_ERROR_NONAME_LITERAL_SIZE (sizeof(OBP_ERROR_NONAME_LITERAL) - sizeof(UNICODE_NULL)) +// +// Predefined strings +// +#define OBP_ROOT 0 +#define OBP_DIRECTORY 1 +#define OBP_OBTYPES 2 +#define OBP_GLOBAL 3 +#define OBP_GLOBALNAMESPACE 4 + //enum with information flags used by ObGetObjectHeaderOffset typedef enum _OBJ_HEADER_INFO_FLAG { HeaderCreatorInfoFlag = 0x1, @@ -267,14 +281,12 @@ typedef struct _KLDBG { DWORD BufferSize; }KLDBG, *PKLDBG; -typedef struct _OBJINFO { - LIST_ENTRY ListEntry; - LPWSTR ObjectName; +typedef struct _OBEX_OBJECT_INFORMATION { ULONG_PTR HeaderAddress; ULONG_PTR ObjectAddress; OBJECT_HEADER_QUOTA_INFO ObjectQuotaHeader; OBJECT_HEADER ObjectHeader; -} OBJINFO, *POBJINFO; +} OBEX_OBJECT_INFORMATION, * POBEX_OBJECT_INFORMATION; typedef struct _OBJREFPNS { ULONG SizeOfBoundaryInformation; @@ -284,10 +296,11 @@ typedef struct _OBJREFPNS { typedef struct _OBJREF { LIST_ENTRY ListEntry; - LPWSTR ObjectName; + UNICODE_STRING Name; ULONG_PTR HeaderAddress; ULONG_PTR ObjectAddress; UCHAR TypeIndex; + WOBJ_OBJECT_TYPE ObjectTypeIndex; OBJREFPNS PrivateNamespace; } OBJREF, *POBJREF; @@ -353,11 +366,35 @@ typedef struct _NOTIFICATION_CALLBACKS { // extern NOTIFICATION_CALLBACKS g_SystemCallbacks; +// +// Normalization symbol +// (defined in kldbg.c) +// +extern WCHAR g_ObNameNormalizationSymbol; + typedef struct _W32K_API_SET_LOOKUP_PATTERN { ULONG Size; PVOID Data; } W32K_API_SET_LOOKUP_PATTERN, *PW32K_API_SET_LOOKUP_PATTERN; +typedef struct _W32K_API_SET_TABLE_HOST { + PWCHAR HostName; + PCHAR TableName; + PCHAR TableSizeName; + ULONG HostEntriesCount; +} W32K_API_SET_TABLE_HOST, * PW32K_API_SET_TABLE_HOST; + +typedef struct _W32K_API_SET_TABLE_ENTRY { + PVOID HostEntriesArray; + W32K_API_SET_TABLE_HOST* Host; +} W32K_API_SET_TABLE_ENTRY, * PW32K_API_SET_TABLE_ENTRY; + +typedef struct _W32K_API_SET_TABLE_ENTRY_V2 { + PVOID HostEntriesArray; + W32K_API_SET_TABLE_HOST* Host; + W32K_API_SET_TABLE_HOST* AliasHost; +} W32K_API_SET_TABLE_ENTRY_V2, * PW32K_API_SET_TABLE_ENTRY_V2; + // return true to stop enumeration typedef BOOL(CALLBACK* PENUMERATE_PRIVATE_NAMESPACE_CALLBACK)( _In_ POBJREF Entry, @@ -376,6 +413,9 @@ typedef BOOL(CALLBACK* PENUMERATE_UNLOADED_DRIVERS_CALLBACK)( _In_opt_ PVOID Context ); +PUNICODE_STRING ObGetPredefinedUnicodeString( + _In_ ULONG Index); + NTSTATUS ObIsValidUnicodeString( _In_ PCUNICODE_STRING SourceString); @@ -436,11 +476,7 @@ PVOID ObDumpFltFilterObjectVersionAware( _Out_ PULONG Size, _Out_ PULONG Version); -POBJINFO ObQueryObject( - _In_ LPWSTR lpDirectory, - _In_ LPWSTR lpObjectName); - -POBJINFO ObQueryObjectByAddress( +POBEX_OBJECT_INFORMATION ObQueryObjectByAddress( _In_ ULONG_PTR ObjectAddress); BOOL ObGetProcessImageFileName( @@ -457,11 +493,29 @@ BOOL ObHeaderToNameInfoAddress( _Inout_ PULONG_PTR HeaderInfoAddress, _In_ OBJ_HEADER_INFO_FLAG InfoFlag); +_Success_(return) +BOOL ObQueryNameStringFromAddress( + _In_ HANDLE HeapHandle, + _In_ ULONG_PTR NameInfoAddress, + _Out_ PUNICODE_STRING NameString); + +_Success_(return) +BOOL ObGetObjectAddressForDirectory( + _In_ PUNICODE_STRING DirectoryName, + _Out_ PULONG_PTR lpRootAddress, + _Out_opt_ PUSHORT lpTypeIndex); + +POBEX_OBJECT_INFORMATION ObQueryObjectInDirectory( + _In_ PUNICODE_STRING ObjectName, + _In_ PUNICODE_STRING DirectoryName); + PVOID ObGetCallbackBlockRoutine( _In_ PVOID CallbackBlock); -LPWSTR ObQueryFullNamespacePath( - _In_ ULONG_PTR ObjectAddress); +_Success_(return) +BOOL ObQueryFullNamespacePath( + _In_ ULONG_PTR ObjectAddress, + _Out_ PUNICODE_STRING Path); PVOID kdCreateObjectTypesList( VOID); @@ -566,11 +620,12 @@ BOOL kdGetAddressFromSymbolEx( _In_ ULONG_PTR ImageSize, _Inout_ ULONG_PTR* Address); +_Success_(return) BOOLEAN kdDumpUnicodeString( _In_ PUNICODE_STRING InputString, _Out_ PUNICODE_STRING OutputString, - _Out_opt_ PVOID* ReferenceBufferPtr, - _In_ BOOLEAN IsKernelPtr); + _Out_opt_ PVOID* ReferenceStringBuffer, + _In_ BOOLEAN IsKernelPointer); USHORT kdGetAlpcPortTypeIndex(); diff --git a/Source/WinObjEx64/kldbg_patterns.h b/Source/WinObjEx64/kldbg_patterns.h index 310518b6..7954c304 100644 --- a/Source/WinObjEx64/kldbg_patterns.h +++ b/Source/WinObjEx64/kldbg_patterns.h @@ -1,12 +1,12 @@ /******************************************************************************* * -* (C) COPYRIGHT AUTHORS, 2019 - 2021 +* (C) COPYRIGHT AUTHORS, 2019 - 2022 * * TITLE: KLDBG_PATTERNS.H * -* VERSION: 1.90 +* VERSION: 2.00 * -* DATE: 11 May 2021 +* DATE: 19 Jun 2022 * * Header with search patterns used by KLDBG. * diff --git a/Source/WinObjEx64/ksymbols.h b/Source/WinObjEx64/ksymbols.h index 6a32eb3d..6b26352d 100644 --- a/Source/WinObjEx64/ksymbols.h +++ b/Source/WinObjEx64/ksymbols.h @@ -4,9 +4,9 @@ * * TITLE: KSYMBOLS.H * -* VERSION: 1.94 +* VERSION: 2.00 * -* DATE: 30 May 2022 +* DATE: 19 Jun 2022 * * Header file for kernel symbol names. * diff --git a/Source/WinObjEx64/list.c b/Source/WinObjEx64/list.c index f8efb3ae..fd8c6746 100644 --- a/Source/WinObjEx64/list.c +++ b/Source/WinObjEx64/list.c @@ -1,12 +1,14 @@ /******************************************************************************* * -* (C) COPYRIGHT AUTHORS, 2015 - 2021 +* (C) COPYRIGHT AUTHORS, 2015 - 2022 * * TITLE: LIST.C * -* VERSION: 1.90 +* VERSION: 2.00 * -* DATE: 27 May 2021 +* DATE: 19 Jun 2022 +* +* Program main object listing and search logic. * * THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF * ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED @@ -16,6 +18,60 @@ *******************************************************************************/ #include "global.h" +HANDLE ListObjectsHeap = NULL; +HANDLE TreeObjectsHeap = NULL; + +BOOLEAN ListHeapCreate( + _Inout_ PHANDLE HeapHandle +) +{ + HANDLE handle; + + if (*HeapHandle) + supDestroyHeap(*HeapHandle); + + handle = supCreateHeap(HEAP_GROWABLE, TRUE); + *HeapHandle = handle; + + return (handle != NULL); +} + +VOID ListHeapDestroy( + VOID +) +{ + if (ListObjectsHeap) { + supDestroyHeap(ListObjectsHeap); + ListObjectsHeap = NULL; + } + + if (TreeObjectsHeap) { + supDestroyHeap(TreeObjectsHeap); + TreeObjectsHeap = NULL; + } +} + +POBEX_ITEM AllocateObjectItem( + _In_ HANDLE HeapHandle, + _In_ WOBJ_OBJECT_TYPE TypeIndex, + _In_ PUNICODE_STRING Name, + _In_ PUNICODE_STRING TypeName, + _In_opt_ OBEX_ITEM* Parent +) +{ + POBEX_ITEM item; + + item = supHeapAllocEx(HeapHandle, sizeof(OBEX_ITEM)); + if (item) { + item->Prev = Parent; + item->TypeIndex = TypeIndex; + supDuplicateUnicodeString(HeapHandle, &item->Name, Name); + supDuplicateUnicodeString(HeapHandle, &item->TypeName, TypeName); + } + + return item; +} + /* * GetNextSub * @@ -69,6 +125,7 @@ VOID ListToObject( if (*ObjectName != L'\\') return; + object[0] = 0; ObjectName++; item = TreeView_GetRoot(g_hwndObjectTree); lastfound = item; @@ -76,7 +133,6 @@ VOID ListToObject( while ((item != NULL) && (*ObjectName != 0)) { item = TreeView_GetChild(g_hwndObjectTree, item); - object[0] = 0; //mars workaround RtlSecureZeroMemory(object, sizeof(object)); ObjectName = GetNextSub(ObjectName, object); currentfound = FALSE; @@ -150,49 +206,83 @@ VOID ListToObject( * */ HTREEITEM AddTreeViewItem( - _In_ LPWSTR ItemName, - _In_opt_ HTREEITEM Root + _In_ HANDLE HeapHandle, + _In_ PUNICODE_STRING ItemName, + _In_opt_ HTREEITEM Root, + _Inout_opt_ OBEX_ITEM** Parent ) { - TVINSERTSTRUCT item; - - RtlSecureZeroMemory(&item, sizeof(item)); - item.hParent = Root; - item.item.mask = TVIF_TEXT | TVIF_SELECTEDIMAGE; + BOOL bNeedFree = FALSE; + HTREEITEM result; + TVINSERTSTRUCT treeItem; + OBEX_ITEM* objectRef; + UNICODE_STRING objectName; + + bNeedFree = supNormalizeUnicodeStringForDisplay(g_obexHeap, + ItemName, + &objectName); + + if (!bNeedFree) + objectName = *ItemName; + + RtlSecureZeroMemory(&treeItem, sizeof(treeItem)); + treeItem.hParent = Root; + treeItem.item.mask = TVIF_TEXT | TVIF_SELECTEDIMAGE | TVIF_PARAM; if (Root == NULL) { - item.item.mask |= TVIF_STATE; - item.item.state = TVIS_EXPANDED; - item.item.stateMask = TVIS_EXPANDED; + treeItem.item.mask |= TVIF_STATE; + treeItem.item.state = TVIS_EXPANDED; + treeItem.item.stateMask = TVIS_EXPANDED; } - item.item.iSelectedImage = 1; - item.item.pszText = ItemName; - return TreeView_InsertItem(g_hwndObjectTree, &item); + treeItem.item.iSelectedImage = 1; + + treeItem.item.pszText = objectName.Buffer; + + objectRef = AllocateObjectItem(HeapHandle, + ObjectTypeDirectory, + ItemName, + ObGetPredefinedUnicodeString(OBP_DIRECTORY), + (Parent == NULL) ? NULL : *Parent); + + if (Parent) *Parent = objectRef; + + treeItem.item.lParam = (LPARAM)objectRef; + + result = TreeView_InsertItem(g_hwndObjectTree, &treeItem); + + if (bNeedFree) + supFreeUnicodeString(g_obexHeap, &objectName); + + return result; } /* -* ListObjectDirectoryTree +* xxxListObjectDirectoryTree * * Purpose: * * List given directory to the treeview. * */ -VOID ListObjectDirectoryTree( - _In_ LPWSTR SubDirName, +VOID xxxListObjectDirectoryTree( + _In_ HANDLE HeapHandle, + _In_ PUNICODE_STRING SubDirName, _In_opt_ HANDLE RootHandle, - _In_opt_ HTREEITEM ViewRootHandle + _In_opt_ HTREEITEM ViewRootHandle, + _In_opt_ OBEX_ITEM* Parent + ) { - NTSTATUS ntStatus; - ULONG queryContext = 0, rLength; - HANDLE directoryHandle = NULL; + ULONG queryContext = 0, rLength; + NTSTATUS ntStatus; + HANDLE directoryHandle = NULL; + OBEX_ITEM* prevItem = Parent; POBJECT_DIRECTORY_INFORMATION directoryEntry; - ViewRootHandle = AddTreeViewItem(SubDirName, ViewRootHandle); + ViewRootHandle = AddTreeViewItem(HeapHandle, SubDirName, ViewRootHandle, &prevItem); - supOpenDirectory(&directoryHandle, RootHandle, SubDirName, DIRECTORY_QUERY); + supOpenDirectoryEx(&directoryHandle, RootHandle, SubDirName, DIRECTORY_QUERY); if (directoryHandle == NULL) return; @@ -206,17 +296,17 @@ VOID ListObjectDirectoryTree( rLength = 1024 * 64; } else { - + // // Request required buffer length. // rLength = 0; - ntStatus = NtQueryDirectoryObject(directoryHandle, - NULL, - 0, - TRUE, - FALSE, - &queryContext, + ntStatus = NtQueryDirectoryObject(directoryHandle, + NULL, + 0, + TRUE, + FALSE, + &queryContext, &rLength); if (ntStatus != STATUS_BUFFER_TOO_SMALL) @@ -240,14 +330,16 @@ VOID ListObjectDirectoryTree( break; } - if (0 == _strncmpi(directoryEntry->TypeName.Buffer, - OBTYPE_NAME_DIRECTORY, - directoryEntry->TypeName.Length / sizeof(WCHAR))) + if (RtlEqualUnicodeString( + &directoryEntry->TypeName, + ObGetPredefinedUnicodeString(OBP_DIRECTORY), + TRUE)) { - ListObjectDirectoryTree( - directoryEntry->Name.Buffer, + xxxListObjectDirectoryTree(HeapHandle, + &directoryEntry->Name, directoryHandle, - ViewRootHandle); + ViewRootHandle, + prevItem); } supHeapFree(directoryEntry); @@ -257,6 +349,25 @@ VOID ListObjectDirectoryTree( NtClose(directoryHandle); } +/* +* ListObjectDirectoryTree +* +* Purpose: +* +* List given directory to the treeview. +* +*/ +VOID ListObjectDirectoryTree( + _In_ PUNICODE_STRING SubDirName, + _In_opt_ HANDLE RootHandle, + _In_opt_ HTREEITEM ViewRootHandle +) +{ + ListHeapCreate(&TreeObjectsHeap); + if (TreeObjectsHeap) + xxxListObjectDirectoryTree(TreeObjectsHeap, SubDirName, RootHandle, ViewRootHandle, NULL); +} + /* * AddListViewItem * @@ -266,34 +377,48 @@ VOID ListObjectDirectoryTree( * */ VOID AddListViewItem( + _In_ HANDLE HeapHandle, _In_ HANDLE RootDirectoryHandle, - _In_ POBJECT_DIRECTORY_INFORMATION DirectoryObjectEntry + _In_ POBJECT_DIRECTORY_INFORMATION Entry, + _In_ OBEX_ITEM* Parent ) { - BOOL bFound = FALSE; - INT lvItemIndex; - PWSTR objectTypeName, objectName; - LVITEM lvItem; - WCHAR szBuffer[MAX_PATH + 1]; + BOOL bFound = FALSE, bNameAllocated; + INT lvItemIndex; + PWSTR objectTypeName; + LVITEM lvItem; + WCHAR szBuffer[MAX_PATH + 1]; WOBJ_TYPE_DESC* typeDesc; + OBEX_ITEM* objRef; + UNICODE_STRING objectName, normalizedLinkTarget; - if (!DirectoryObjectEntry) return; - - objectTypeName = DirectoryObjectEntry->TypeName.Buffer; + objectTypeName = Entry->TypeName.Buffer; typeDesc = ObManagerGetEntryByTypeName(objectTypeName); - objectName = DirectoryObjectEntry->Name.Buffer; + bNameAllocated = supNormalizeUnicodeStringForDisplay(g_obexHeap, + &Entry->Name, + &objectName); + + if (!bNameAllocated) + objectName = Entry->Name; // // Object name column. // RtlSecureZeroMemory(&lvItem, sizeof(lvItem)); lvItem.mask = LVIF_TEXT | LVIF_IMAGE | LVIF_PARAM; - lvItem.pszText = objectName; + lvItem.pszText = objectName.Buffer; lvItem.iItem = MAXINT; lvItem.iImage = typeDesc->ImageIndex; - lvItem.lParam = typeDesc->Index; + + objRef = AllocateObjectItem(HeapHandle, + typeDesc->Index, + &Entry->Name, + &Entry->TypeName, + Parent); + + lvItem.lParam = (LPARAM)objRef; lvItemIndex = ListView_InsertItem(g_hwndObjectList, &lvItem); // @@ -308,64 +433,77 @@ VOID AddListViewItem( RtlSecureZeroMemory(&szBuffer, sizeof(szBuffer)); // - // Look for object type in well known type names hashes. - // If found - query information for additional description field. + // Special case for symbolic links as their link targets must be normalized before output. + // Do not bFound to TRUE so we will fall through the end of routine. // + if (typeDesc->NameHash == OBTYPE_HASH_SYMBOLIC_LINK) { + + if (supResolveSymbolicLinkTargetNormalized( + NULL, + RootDirectoryHandle, + &Entry->Name, + &normalizedLinkTarget)) + { + lvItem.mask = LVIF_TEXT; + lvItem.iSubItem = 2; + lvItem.pszText = normalizedLinkTarget.Buffer; + lvItem.iItem = lvItemIndex; + ListView_SetItem(g_hwndObjectList, &lvItem); + supFreeDuplicatedUnicodeString(g_obexHeap, &normalizedLinkTarget, FALSE); + } - switch (typeDesc->NameHash) { - - case OBTYPE_HASH_SYMBOLIC_LINK: - - bFound = ntsupResolveSymbolicLink(RootDirectoryHandle, - &DirectoryObjectEntry->Name, - szBuffer, - MAX_PATH * sizeof(WCHAR)); + } + else { + + // + // Look for object type in well known type names hashes. + // If found - query information for additional description field. + // - break; + switch (typeDesc->NameHash) { - case OBTYPE_HASH_SECTION: - - bFound = supQuerySectionFileInfo(RootDirectoryHandle, - &DirectoryObjectEntry->Name, - szBuffer, - MAX_PATH); + case OBTYPE_HASH_SECTION: - break; + bFound = supQuerySectionFileInfo(RootDirectoryHandle, + &Entry->Name, + szBuffer, + MAX_PATH); - case OBTYPE_HASH_DRIVER: + break; - bFound = supQueryDriverDescription(objectName, - szBuffer, - MAX_PATH); + case OBTYPE_HASH_DRIVER: - break; + bFound = supQueryDriverDescription(objectName.Buffer, + szBuffer, + MAX_PATH); - case OBTYPE_HASH_DEVICE: + break; - bFound = supQueryDeviceDescription(objectName, - szBuffer, - MAX_PATH); + case OBTYPE_HASH_DEVICE: - break; + bFound = supQueryDeviceDescription(NULL, + &Entry->Name, + szBuffer, + MAX_PATH); - case OBTYPE_HASH_WINSTATION: + break; - bFound = supQueryWinstationDescription(objectName, - szBuffer, - MAX_PATH); + case OBTYPE_HASH_WINSTATION: - break; + bFound = supQueryWinstationDescription(objectName.Buffer, + szBuffer, + MAX_PATH); - case OBTYPE_HASH_TYPE: + break; - bFound = supQueryTypeInfo(objectName, - szBuffer, - MAX_PATH); + case OBTYPE_HASH_TYPE: - break; + bFound = supQueryTypeInfo(&Entry->Name, + szBuffer, + MAX_PATH); - default: - break; + break; + } } // @@ -378,29 +516,38 @@ VOID AddListViewItem( lvItem.iItem = lvItemIndex; ListView_SetItem(g_hwndObjectList, &lvItem); } + + if (bNameAllocated) + supFreeUnicodeString(g_obexHeap, &objectName); } /* -* ListObjectsInDirectory +* xxxListCurrentDirectoryObjects * * Purpose: * -* List given directory to the listview. +* List directory objects to the listview. * */ -VOID ListObjectsInDirectory( - _In_ LPWSTR lpObjectDirectory +VOID xxxListCurrentDirectoryObjects( + _In_ HANDLE HeapHandle, + _In_ OBEX_ITEM* Parent ) { - NTSTATUS ntStatus; - ULONG queryContext = 0, rLength; - HANDLE directoryHandle = NULL; + NTSTATUS ntStatus; + ULONG queryContext = 0, rLength; + HANDLE directoryHandle = NULL; + UNICODE_STRING usDirectoryName; POBJECT_DIRECTORY_INFORMATION infoBuffer; ListView_DeleteAllItems(g_hwndObjectList); - supOpenDirectory(&directoryHandle, NULL, lpObjectDirectory, DIRECTORY_QUERY); + if (supGetCurrentObjectPath(TRUE, &usDirectoryName)) { + supOpenDirectoryEx(&directoryHandle, NULL, &usDirectoryName, DIRECTORY_QUERY); + supFreeDuplicatedUnicodeString(g_obexHeap, &usDirectoryName, FALSE); + } + if (directoryHandle == NULL) return; @@ -445,7 +592,7 @@ VOID ListObjectsInDirectory( &rLength); if (NT_SUCCESS(ntStatus)) { - AddListViewItem(directoryHandle, infoBuffer); + AddListViewItem(HeapHandle, directoryHandle, infoBuffer, Parent); } else { supHeapFree(infoBuffer); @@ -466,6 +613,112 @@ VOID ListObjectsInDirectory( NtClose(directoryHandle); } + +/* +* ListCurrentDirectoryObjects +* +* Purpose: +* +* List directory objects to the listview. +* +*/ +VOID ListCurrentDirectoryObjects( + _In_ HTREEITEM ViewRootHandle +) +{ + OBEX_ITEM* objRef = NULL; + + ListHeapCreate(&ListObjectsHeap); + if (ListObjectsHeap) { + + if (supGetTreeViewItemParam(g_hwndObjectTree, + ViewRootHandle, + &objRef)) + { + xxxListCurrentDirectoryObjects(ListObjectsHeap, objRef); + } + + } +} + +PFO_LIST_ITEM AllocateFoundItem( + _In_ PFO_LIST_ITEM Previous, + _In_ PUNICODE_STRING DirectoryName, + _In_ POBJECT_DIRECTORY_INFORMATION InfoBuffer +) +{ + PFO_LIST_ITEM Item; + SIZE_T BufferLength, TypeNameOffset; + PWCH String, StringBuffer; + + BufferLength = sizeof(FO_LIST_ITEM) + + InfoBuffer->Name.Length + + InfoBuffer->TypeName.Length + + DirectoryName->Length + + sizeof(OBJ_NAME_PATH_SEPARATOR) + + 2 * sizeof(UNICODE_NULL); + + Item = (PFO_LIST_ITEM)supHeapAlloc(BufferLength); + if (Item == NULL) { + supHeapFree(InfoBuffer); + return NULL; + } + + Item->Prev = Previous; + Item->ObjectName.Buffer = (PWSTR)Item->NameBuffer; + + TypeNameOffset = (SIZE_T)DirectoryName->Length + + (SIZE_T)InfoBuffer->Name.Length + + sizeof(OBJ_NAME_PATH_SEPARATOR) + + sizeof(UNICODE_NULL); + + // + // Copy ObjectName. + // + Item->ObjectType.Buffer = (PWSTR)RtlOffsetToPointer(Item->NameBuffer, TypeNameOffset); + StringBuffer = Item->ObjectName.Buffer; + String = StringBuffer; + + RtlCopyMemory(String, DirectoryName->Buffer, DirectoryName->Length); + String = (PWCH)RtlOffsetToPointer(Item->ObjectName.Buffer, DirectoryName->Length); + + // + // Add separator if not root. + // + if (!supIsRootDirectory(DirectoryName)) + *String++ = OBJ_NAME_PATH_SEPARATOR; + + RtlCopyMemory(String, InfoBuffer->Name.Buffer, InfoBuffer->Name.Length); + String = (PWCH)RtlOffsetToPointer(String, InfoBuffer->Name.Length); + *String++ = UNICODE_NULL; + + // + // Set new Length/MaximumLength to ObjectName. + // + BufferLength = (USHORT)((ULONG_PTR)String - (ULONG_PTR)StringBuffer); + Item->ObjectName.Length = (USHORT)BufferLength - sizeof(WCHAR); + Item->ObjectName.MaximumLength = (USHORT)BufferLength; + + // + // Copy ObjectType. + // + StringBuffer = Item->ObjectType.Buffer; + String = StringBuffer; + + RtlCopyMemory(String, InfoBuffer->TypeName.Buffer, InfoBuffer->TypeName.Length); + String = (PWCH)RtlOffsetToPointer(String, InfoBuffer->TypeName.Length); + *String++ = UNICODE_NULL; + + // + // Set new Length/MaximumLength to ObjectType. + // + BufferLength = (USHORT)((ULONG_PTR)String - (ULONG_PTR)StringBuffer); + Item->ObjectType.Length = (USHORT)BufferLength - sizeof(WCHAR); + Item->ObjectType.MaximumLength = (USHORT)BufferLength; + + return Item; +} + /* * FindObject * @@ -475,26 +728,27 @@ VOID ListObjectsInDirectory( * */ VOID FindObject( - _In_ LPWSTR DirName, - _In_opt_ LPWSTR NameSubstring, - _In_opt_ LPWSTR TypeName, + _In_ PUNICODE_STRING DirectoryName, + _In_opt_ PUNICODE_STRING NameSubstring, + _In_opt_ PUNICODE_STRING TypeName, _In_ PFO_LIST_ITEM* List ) { - NTSTATUS status; - ULONG ctx, rlen; - HANDLE directoryHandle = NULL; - SIZE_T sdlen; - LPWSTR newdir; - PFO_LIST_ITEM tmp; + NTSTATUS status; + ULONG ctx, rlen; + HANDLE directoryHandle = NULL; + + PFO_LIST_ITEM Item; + SIZE_T NameSize, BufferLength; + PWCH ObjectName, String; + UNICODE_STRING SubDirectory; - POBJECT_DIRECTORY_INFORMATION objinf; + POBJECT_DIRECTORY_INFORMATION InfoBuffer; - supOpenDirectory(&directoryHandle, NULL, DirName, DIRECTORY_QUERY); + supOpenDirectoryEx(&directoryHandle, NULL, DirectoryName, DIRECTORY_QUERY); if (directoryHandle == NULL) return; - sdlen = _strlen(DirName); ctx = 0; do { @@ -512,66 +766,99 @@ VOID FindObject( break; } - objinf = (POBJECT_DIRECTORY_INFORMATION)supHeapAlloc((SIZE_T)rlen); - if (objinf == NULL) + InfoBuffer = (POBJECT_DIRECTORY_INFORMATION)supHeapAlloc((SIZE_T)rlen); + if (InfoBuffer == NULL) break; - status = NtQueryDirectoryObject(directoryHandle, objinf, rlen, TRUE, FALSE, &ctx, &rlen); + status = NtQueryDirectoryObject(directoryHandle, InfoBuffer, rlen, TRUE, FALSE, &ctx, &rlen); if (!NT_SUCCESS(status)) { - supHeapFree(objinf); + supHeapFree(InfoBuffer); break; } - if ((_strstri(objinf->Name.Buffer, NameSubstring) != 0) || (NameSubstring == NULL)) - if ((_strcmpi(objinf->TypeName.Buffer, TypeName) == 0) || (TypeName == NULL)) { + if (TypeName) { - tmp = (PFO_LIST_ITEM)supHeapAlloc(sizeof(FO_LIST_ITEM) + - objinf->Name.Length + - objinf->TypeName.Length + - (sdlen + 4) * sizeof(WCHAR)); + if (RtlEqualUnicodeString(&InfoBuffer->TypeName, TypeName, TRUE)) { - if (tmp == NULL) { - supHeapFree(objinf); - break; - } - tmp->Prev = *List; - tmp->ObjectName = tmp->NameBuffer; - tmp->ObjectType = tmp->NameBuffer + sdlen + 2 + objinf->Name.Length / sizeof(WCHAR); - _strcpy(tmp->ObjectName, DirName); - if ((DirName[0] == L'\\') && (DirName[1] == 0)) { - _strncpy(tmp->ObjectName + sdlen, 1 + objinf->Name.Length / sizeof(WCHAR), - objinf->Name.Buffer, objinf->Name.Length / sizeof(WCHAR)); + if (NameSubstring) { + + if (ULLONG_MAX != supFindUnicodeStringSubString(&InfoBuffer->Name, NameSubstring)) { + Item = AllocateFoundItem(*List, DirectoryName, InfoBuffer); + if (Item == NULL) + break; + + *List = Item; + } } else { - tmp->ObjectName[sdlen] = L'\\'; - _strncpy(tmp->ObjectName + sdlen + 1, 1 + objinf->Name.Length / sizeof(WCHAR), - objinf->Name.Buffer, objinf->Name.Length / sizeof(WCHAR)); - } - _strncpy(tmp->ObjectType, 1 + objinf->TypeName.Length / sizeof(WCHAR), - objinf->TypeName.Buffer, objinf->TypeName.Length / sizeof(WCHAR)); - *List = tmp; - }; - - if (_strcmpi(objinf->TypeName.Buffer, OBTYPE_NAME_DIRECTORY) == 0) { - - newdir = (LPWSTR)supHeapAlloc((sdlen + 4) * sizeof(WCHAR) + objinf->Name.Length); - if (newdir != NULL) { - _strcpy(newdir, DirName); - if ((DirName[0] == L'\\') && (DirName[1] == 0)) { - _strncpy(newdir + sdlen, 1 + objinf->Name.Length / sizeof(WCHAR), - objinf->Name.Buffer, objinf->Name.Length / sizeof(WCHAR)); + Item = AllocateFoundItem(*List, DirectoryName, InfoBuffer); + if (Item == NULL) + break; + + *List = Item; } - else { - newdir[sdlen] = L'\\'; - _strncpy(newdir + sdlen + 1, 1 + objinf->Name.Length / sizeof(WCHAR), - objinf->Name.Buffer, objinf->Name.Length / sizeof(WCHAR)); + + } + + } + else { + if (NameSubstring) { + if (ULLONG_MAX != supFindUnicodeStringSubString(&InfoBuffer->Name, NameSubstring)) { + Item = AllocateFoundItem(*List, DirectoryName, InfoBuffer); + if (Item == NULL) + break; + + *List = Item; } - FindObject(newdir, NameSubstring, TypeName, List); - supHeapFree(newdir); + } + else { + Item = AllocateFoundItem(*List, DirectoryName, InfoBuffer); + if (Item == NULL) + break; + + *List = Item; + } + } + + // + // If this is directory, go inside. + // + if (RtlEqualUnicodeString(&InfoBuffer->TypeName, + ObGetPredefinedUnicodeString(OBP_DIRECTORY), + TRUE)) + { + NameSize = (SIZE_T)InfoBuffer->Name.Length + + (SIZE_T)DirectoryName->Length + + sizeof(OBJ_NAME_PATH_SEPARATOR) + + sizeof(UNICODE_NULL); + + ObjectName = (PWCH)supHeapAlloc(NameSize); + if (ObjectName != NULL) { + + String = ObjectName; + + RtlCopyMemory(String, DirectoryName->Buffer, DirectoryName->Length); + String = (PWCH)RtlOffsetToPointer(String, DirectoryName->Length); + + if (!supIsRootDirectory(DirectoryName)) + *String++ = OBJ_NAME_PATH_SEPARATOR; + + RtlCopyMemory(String, InfoBuffer->Name.Buffer, InfoBuffer->Name.Length); + String = (PWCH)RtlOffsetToPointer(String, InfoBuffer->Name.Length); + *String++ = UNICODE_NULL; + + BufferLength = (USHORT)((ULONG_PTR)String - (ULONG_PTR)ObjectName); + SubDirectory.Length = (USHORT)BufferLength - sizeof(WCHAR); + SubDirectory.MaximumLength = (USHORT)BufferLength; + SubDirectory.Buffer = ObjectName; + + FindObject(&SubDirectory, NameSubstring, TypeName, List); + + supHeapFree(ObjectName); } } - supHeapFree(objinf); + supHeapFree(InfoBuffer); } while (TRUE); diff --git a/Source/WinObjEx64/list.h b/Source/WinObjEx64/list.h index 62876471..c9ee469d 100644 --- a/Source/WinObjEx64/list.h +++ b/Source/WinObjEx64/list.h @@ -1,14 +1,14 @@ /******************************************************************************* * -* (C) COPYRIGHT AUTHORS, 2015 - 2020 +* (C) COPYRIGHT AUTHORS, 2015 - 2022 * * TITLE: LIST.H * -* VERSION: 1.87 +* VERSION: 2.00 * -* DATE: 30 June 2020 +* DATE: 19 Jun 2022 * -* Common header file main program logic. +* Common header file for the program object listing logic. * * THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF * ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED @@ -20,24 +20,41 @@ typedef struct _FO_LIST_ITEM { struct _FO_LIST_ITEM *Prev; - LPWSTR ObjectName; - LPWSTR ObjectType; - WCHAR NameBuffer[2]; + UNICODE_STRING ObjectName; + UNICODE_STRING ObjectType; + WCHAR NameBuffer[2]; } FO_LIST_ITEM, *PFO_LIST_ITEM; +typedef struct _OBEX_ITEM { + struct _OBEX_ITEM *Prev; + WOBJ_OBJECT_TYPE TypeIndex; + UNICODE_STRING Name; + UNICODE_STRING TypeName; +} OBEX_ITEM, * POBEX_ITEM; + +typedef struct _OBEX_PATH_ELEMENT { + LIST_ENTRY ListEntry; + WOBJ_OBJECT_TYPE TypeIndex; + UNICODE_STRING Name; + UNICODE_STRING TypeName; +} OBEX_PATH_ELEMENT, * POBEX_PATH_ELEMENT; + +VOID ListHeapDestroy( + VOID); + VOID ListToObject( _In_ LPWSTR ObjectName); VOID ListObjectDirectoryTree( - _In_ LPWSTR SubDirName, + _In_ PUNICODE_STRING SubDirName, _In_opt_ HANDLE RootHandle, _In_opt_ HTREEITEM ViewRootHandle); VOID FindObject( - _In_ LPWSTR DirName, - _In_opt_ LPWSTR NameSubstring, - _In_opt_ LPWSTR TypeName, + _In_ PUNICODE_STRING DirectoryName, + _In_opt_ PUNICODE_STRING NameSubstring, + _In_opt_ PUNICODE_STRING TypeName, _In_ PFO_LIST_ITEM *List); -VOID ListObjectsInDirectory( - _In_ LPWSTR lpObjectDirectory); +VOID ListCurrentDirectoryObjects( + _In_ HTREEITEM ViewRootHandle); diff --git a/Source/WinObjEx64/log/log.c b/Source/WinObjEx64/log/log.c index 32864f65..dff4ba98 100644 --- a/Source/WinObjEx64/log/log.c +++ b/Source/WinObjEx64/log/log.c @@ -4,9 +4,9 @@ * * TITLE: LOG.C * -* VERSION: 1.94 +* VERSION: 2.00 * -* DATE: 07 Jun 2022 +* DATE: 19 Jun 2022 * * Simplified log. * @@ -318,7 +318,7 @@ INT_PTR CALLBACK LogViewerDialogProc( case WM_INITDIALOG: supCenterWindow(hwndDlg); LogViewerListLog(hwndDlg); - break; + return TRUE; case WM_COMMAND: @@ -328,13 +328,8 @@ INT_PTR CALLBACK LogViewerDialogProc( case ID_OBJECT_COPY: LogViewerCopyToClipboard(hwndDlg); break; - - default: - break; } - default: - break; } return 0; } diff --git a/Source/WinObjEx64/log/log.h b/Source/WinObjEx64/log/log.h index befd0b19..668392da 100644 --- a/Source/WinObjEx64/log/log.h +++ b/Source/WinObjEx64/log/log.h @@ -4,9 +4,9 @@ * * TITLE: LOG.H * -* VERSION: 1.94 +* VERSION: 2.00 * -* DATE: 31 May 2022 +* DATE: 19 Jun 2022 * * Header file for simplified log support. * diff --git a/Source/WinObjEx64/main.c b/Source/WinObjEx64/main.c index 9aa621ea..69121194 100644 --- a/Source/WinObjEx64/main.c +++ b/Source/WinObjEx64/main.c @@ -4,9 +4,9 @@ * * TITLE: MAIN.C * -* VERSION: 1.94 +* VERSION: 2.00 * -* DATE: 07 Jun 2022 +* DATE: 19 Jun 2022 * * Program entry point and main window handler. * @@ -18,10 +18,6 @@ *******************************************************************************/ #define OEMRESOURCE #include "global.h" -#include "aboutDlg.h" -#include "findDlg.h" -#include "sdviewDlg.h" -#include "sysinfoDlg.h" #include "treelist/treelist.h" #include "props/propDlg.h" #include "extras/extras.h" @@ -43,6 +39,9 @@ BOOL bMainWndSortInverse = FALSE; // WINOBJ_GLOBALS g_WinObj; +// Global stats +WINOBJ_STATS g_WinObjStats; + /* * guiExtrasDisableAdminFeatures * @@ -71,6 +70,7 @@ VOID guiExtrasDisableAdminFeatures( SetMenuItemInfo(hExtrasSubMenu, ID_EXTRAS_CALLBACKS, FALSE, &mii); SetMenuItemInfo(hExtrasSubMenu, ID_EXTRAS_DRIVERS, FALSE, &mii); SetMenuItemInfo(hExtrasSubMenu, ID_EXTRAS_UNLOADEDDRIVERS, FALSE, &mii); + SetMenuItemInfo(hExtrasSubMenu, ID_EXTRAS_PRIVATENAMESPACES, FALSE, &mii); SetMenuItemInfo(hExtrasSubMenu, ID_EXTRAS_SOFTWARELICENSECACHE, FALSE, &mii); SetMenuItemInfo(hExtrasSubMenu, ID_EXTRAS_SSDT, FALSE, &mii); SetMenuItemInfo(hExtrasSubMenu, ID_EXTRAS_W32PSERVICETABLE, FALSE, &mii); @@ -82,15 +82,6 @@ VOID guiExtrasDisableAdminFeatures( // Elevated launch. // if (g_kdctx.IsFullAdmin) { - // - // These features require driver usage. - // - /*if (FALSE == kdIoDriverLoaded()) { - SetMenuItemInfo(hExtrasSubMenu, ID_EXTRAS_SSDT, FALSE, &mii); - SetMenuItemInfo(hExtrasSubMenu, ID_EXTRAS_PRIVATENAMESPACES, FALSE, &mii); - SetMenuItemInfo(hExtrasSubMenu, ID_EXTRAS_CALLBACKS, FALSE, &mii); - SetMenuItemInfo(hExtrasSubMenu, ID_EXTRAS_UNLOADEDDRIVERS, FALSE, &mii); - }*/ // // This feature is not supported in Windows 10 10586. @@ -143,157 +134,165 @@ INT CALLBACK MainWindowObjectListCompareFunc( } /* -* MainWindowHandleObjectTreeProp +* MainWindowHandleObjectViewSD * * Purpose: * -* Object Tree properties per selected item. +* Handler for View Security Descriptor menu. * */ -VOID MainWindowHandleObjectTreeProp( - _In_ HWND hwnd +VOID MainWindowHandleObjectViewSD( + _In_ BOOL fList ) { - TV_ITEM tvi; - WCHAR szBuffer[MAX_PATH + 1]; - PROP_DIALOG_CREATE_SETTINGS propSettings; + OBEX_ITEM* objRef; + WOBJ_OBJECT_TYPE wobjType = ObjectTypeUnknown; - // - // Only one object properties dialog at the same time allowed. - // - ENSURE_DIALOG_UNIQUE(g_PropWindow); - - if (ObjectTreeSelectedItem == NULL) - return; + if (fList) { - RtlSecureZeroMemory(&tvi, sizeof(TV_ITEM)); + if (supGetListViewItemParam(g_hwndObjectList, + ListView_GetSelectionMark(g_hwndObjectList), + (PVOID)&objRef)) + { - szBuffer[0] = 0; - RtlSecureZeroMemory(&szBuffer, sizeof(szBuffer)); - tvi.pszText = szBuffer; - tvi.cchTextMax = MAX_PATH; - tvi.mask = TVIF_TEXT; - tvi.hItem = ObjectTreeSelectedItem; - if (TreeView_GetItem(g_hwndObjectTree, &tvi)) { + if (objRef) + wobjType = objRef->TypeIndex; - RtlSecureZeroMemory(&propSettings, sizeof(propSettings)); - propSettings.hwndParent = hwnd; - propSettings.lpObjectName = szBuffer; - propSettings.lpObjectType = OBTYPE_NAME_DIRECTORY; + } - propCreateDialog(&propSettings); } + else { + wobjType = ObjectTypeDirectory; + } + + SDViewDialogCreate(wobjType); + } /* -* MainWindowHandleObjectViewSD +* MainWindowCopyObjectName * * Purpose: * -* Handler for View Security Descriptor menu. +* Handler for Copy Name / Copy Name (Bin) menu. * */ -VOID MainWindowHandleObjectViewSD( - _In_ HWND hwndParent, - _In_ BOOL fList +VOID MainWindowCopyObjectName( + _In_ UINT ControlId ) { - LVITEM lvi; - TV_ITEM tvi; - WOBJ_OBJECT_TYPE wobjType; - WCHAR szBuffer[MAX_PATH + 1]; + INT nSelected; + OBEX_ITEM* objRef = NULL; + HWND hwndFocus; - szBuffer[0] = 0; + UNICODE_STRING normalizedName; - if (fList) { - - RtlSecureZeroMemory(&lvi, sizeof(LVITEM)); - lvi.mask = LVIF_PARAM | LVIF_TEXT; - lvi.iItem = ListView_GetSelectionMark(g_hwndObjectList); - lvi.pszText = szBuffer; - lvi.cchTextMax = MAX_PATH; - - if (!ListView_GetItem(g_hwndObjectList, &lvi)) - return; + hwndFocus = GetFocus(); + if (hwndFocus != g_hwndObjectList && + hwndFocus != g_hwndObjectTree) + { + return; + } - wobjType = (WOBJ_OBJECT_TYPE)lvi.lParam; + if (hwndFocus == g_hwndObjectList) { + + nSelected = ListView_GetSelectionMark(g_hwndObjectList); + if (nSelected >= 0) { + if (!supGetListViewItemParam(g_hwndObjectList, nSelected, &objRef)) + return; + } } else { - RtlSecureZeroMemory(&tvi, sizeof(TV_ITEM)); - tvi.pszText = szBuffer; - tvi.cchTextMax = MAX_PATH; - tvi.mask = TVIF_TEXT; - tvi.hItem = ObjectTreeSelectedItem; - - if (!TreeView_GetItem(g_hwndObjectTree, &tvi)) - return; + if (ObjectTreeSelectedItem) { + if (!supGetTreeViewItemParam(g_hwndObjectTree, ObjectTreeSelectedItem, &objRef)) + return; + } - wobjType = ObjectTypeDirectory; } - SDViewDialogCreate(hwndParent, - g_WinObj.CurrentObjectPath, - szBuffer, - wobjType); + if (objRef == NULL) + return; + if (ControlId == ID_OBJECT_COPY_NAME) { + if (supNormalizeUnicodeStringForDisplay(g_obexHeap, + &objRef->Name, + &normalizedName)) + { + supClipboardCopy(normalizedName.Buffer, normalizedName.Length); + supFreeDuplicatedUnicodeString(g_obexHeap, &normalizedName, FALSE); + } + } + else { + supClipboardCopyUnicodeStringRaw(&objRef->Name); + } } /* -* MainWindowHandleObjectListProp +* MainWindowShowObjectProperties * * Purpose: * -* Object List properties per selected item. +* Display properties dialog for a selected item. * */ -VOID MainWindowHandleObjectListProp( +VOID MainWindowShowObjectProperties( _In_ HWND hwnd ) { - INT nSelected; - LPWSTR lpItemText, lpType, lpDesc = NULL; - - PROP_DIALOG_CREATE_SETTINGS propSettings; + INT nSelected; + HWND hwndFocus; + OBEX_ITEM* objRef = NULL; + PROP_CONFIG propConfig; + UNICODE_STRING objectPath; + + hwndFocus = GetFocus(); + if (hwndFocus != g_hwndObjectList && + hwndFocus != g_hwndObjectTree) + { + return; + } // - // Only one object properties dialog allowed at same time. + // Get current object path. // - if (g_PropWindow != NULL) + if (!supGetCurrentObjectPath(FALSE, &objectPath)) return; // - // Query selected index, leave on failure. + // Only one object properties dialog allowed at same time. // - nSelected = ListView_GetSelectionMark(g_hwndObjectList); - if (nSelected == -1) - return; + supCloseKnownPropertiesDialog(propGetCommonWindow()); - lpItemText = supGetItemText(g_hwndObjectList, nSelected, 0, NULL); - if (lpItemText) { - lpType = supGetItemText(g_hwndObjectList, nSelected, 1, NULL); - if (lpType) { + if (hwndFocus == g_hwndObjectList) { - //lpDesc is not important, we can work if it NULL - lpDesc = supGetItemText(g_hwndObjectList, nSelected, 2, NULL); - - RtlSecureZeroMemory(&propSettings, sizeof(propSettings)); - - propSettings.hwndParent = hwnd; - propSettings.lpObjectName = lpItemText; - propSettings.lpObjectType = lpType; - propSettings.lpDescription = lpDesc; + // + // Query selected index, leave on failure. + // + nSelected = ListView_GetSelectionMark(g_hwndObjectList); + if (nSelected >= 0) { + supGetListViewItemParam(g_hwndObjectList, nSelected, &objRef); + } - propCreateDialog(&propSettings); + } + else { - if (lpDesc) { - supHeapFree(lpDesc); - } - supHeapFree(lpType); + if (ObjectTreeSelectedItem) { + supGetTreeViewItemParam(g_hwndObjectTree, ObjectTreeSelectedItem, &objRef); } - supHeapFree(lpItemText); } + + if (objRef) { + RtlSecureZeroMemory(&propConfig, sizeof(propConfig)); + propConfig.hwndParent = hwnd; + propConfig.ObjectTypeIndex = objRef->TypeIndex; + propConfig.NtObjectName = &objRef->Name; + propConfig.NtObjectPath = &objectPath; + propCreateDialog(&propConfig); + } + + supFreeUnicodeString(g_obexHeap, &objectPath); } /* @@ -308,8 +307,8 @@ VOID MainWindowOnRefresh( VOID ) { - LPWSTR CurrentPath = NULL; - SIZE_T len; + BOOL bOkay; + UNICODE_STRING currentPath, normalizedPath; supSetWaitCursor(TRUE); @@ -319,17 +318,20 @@ VOID MainWindowOnRefresh( supCreateSCMSnapshot(SERVICE_DRIVER, NULL); sapiCreateSetupDBSnapshot(); - len = _strlen(g_WinObj.CurrentObjectPath); - CurrentPath = (LPWSTR)supHeapAlloc((len + 1) * sizeof(WCHAR)); - if (CurrentPath) - _strcpy(CurrentPath, g_WinObj.CurrentObjectPath); + bOkay = supGetCurrentObjectPath(TRUE, ¤tPath); TreeView_DeleteAllItems(g_hwndObjectTree); - ListObjectDirectoryTree(L"\\", NULL, NULL); - - if (CurrentPath) { - ListToObject(CurrentPath); - supHeapFree(CurrentPath); + ListObjectDirectoryTree(ObGetPredefinedUnicodeString(OBP_ROOT), NULL, NULL); + + if (bOkay) { + if (supNormalizeUnicodeStringForDisplay(g_obexHeap, + ¤tPath, + &normalizedPath)) + { + ListToObject(normalizedPath.Buffer); + supFreeDuplicatedUnicodeString(g_obexHeap, &normalizedPath, FALSE); + } + supFreeDuplicatedUnicodeString(g_obexHeap, ¤tPath, FALSE); } supSetWaitCursor(FALSE); @@ -384,6 +386,63 @@ VOID MainWindowOnDisplayGridChange( EnumWindows((WNDENUMPROC)MainWindowEnumWndProc, (LPARAM)dwProcessId); } +/* +* MainWindowHandleGotoLinkTarget +* +* Purpose: +* +* Resolve symbolic link target and select it in winobjex window. +* +*/ +VOID MainWindowHandleGotoLinkTarget( + VOID +) +{ + UNICODE_STRING linkName, linkTarget, normalizedLinkTarget; + + if (!supGetCurrentObjectPath(TRUE, &linkName)) + return; + + // Global?? + if (RtlEqualUnicodeString(&linkName, + ObGetPredefinedUnicodeString(OBP_GLOBAL), + TRUE)) + { + ListToObject(KM_OBJECTS_ROOT_DIRECTORY); + } + + if (supResolveSymbolicLinkTarget(NULL, NULL, &linkName, &linkTarget)) { + + // + // Check against \\GLOBAL?? + // + if (RtlEqualUnicodeString(&linkTarget, + ObGetPredefinedUnicodeString(OBP_GLOBALNAMESPACE), + TRUE)) + { + // DosDevices + ListToObject(L"\\GLOBAL??"); + } + else { + + // + // Usual link, prepare it for output and do the listing. + // + if (supNormalizeUnicodeStringForDisplay(g_obexHeap, + &linkTarget, + &normalizedLinkTarget)) + { + ListToObject(normalizedLinkTarget.Buffer); + supFreeDuplicatedUnicodeString(g_obexHeap, &normalizedLinkTarget, FALSE); + } + } + + supFreeDuplicatedUnicodeString(g_obexHeap, &linkTarget, FALSE); + } + + supFreeDuplicatedUnicodeString(g_obexHeap, &linkName, FALSE); +} + /* * MainWindowHandleWMCommand * @@ -397,9 +456,7 @@ LRESULT MainWindowHandleWMCommand( _In_ WPARAM wParam ) { - LPWSTR lpItemText; - HWND hwndFocus; - WORD ControlId = LOWORD(wParam); + WORD ControlId = LOWORD(wParam); switch (ControlId) { @@ -421,45 +478,21 @@ LRESULT MainWindowHandleWMCommand( break; case ID_OBJECT_PROPERTIES: - hwndFocus = GetFocus(); - if (hwndFocus == g_hwndObjectList) { - MainWindowHandleObjectListProp(hwnd); - } - if (hwndFocus == g_hwndObjectTree) { - MainWindowHandleObjectTreeProp(hwnd); - } + MainWindowShowObjectProperties(hwnd); break; - case ID_OBJECT_GOTOLINKTARGET: - lpItemText = supGetItemText(g_hwndObjectList, - ListView_GetSelectionMark(g_hwndObjectList), 2, NULL); + case ID_OBJECT_COPY_NAME: + case ID_OBJECT_COPY_NAME_BINARY: + MainWindowCopyObjectName(ControlId); + break; - if (lpItemText) { - if (_strcmpi(lpItemText, L"\\??") == 0) { - ListToObject(L"\\GLOBAL??"); - } - else { - ListToObject(lpItemText); - } - supHeapFree(lpItemText); - } - else { - lpItemText = supGetItemText(g_hwndObjectList, - ListView_GetSelectionMark(g_hwndObjectList), 0, NULL); + case ID_OBJECT_GOTOLINKTARGET: - if (lpItemText) { - if ((_strcmpi(lpItemText, L"GLOBALROOT") == 0) && - (_strcmpi(g_WinObj.CurrentObjectPath, L"\\GLOBAL??") == 0)) - { - ListToObject(L"\\"); - } - supHeapFree(lpItemText); - } - } + MainWindowHandleGotoLinkTarget(); break; case ID_VIEW_SECURITYDESCRIPTOR: - MainWindowHandleObjectViewSD(hwnd, (GetFocus() == g_hwndObjectList)); + MainWindowHandleObjectViewSD((GetFocus() == g_hwndObjectList)); break; case ID_FIND_FINDOBJECT: @@ -503,6 +536,10 @@ LRESULT MainWindowHandleWMCommand( extrasShowDialogById(ControlId); break; + case ID_HELP_STATISTICS: + ShowStatsDialog(); + break; + case ID_HELP_ABOUT: DialogBoxParam( @@ -526,111 +563,15 @@ LRESULT MainWindowHandleWMCommand( ShowSysInfoDialog(hwnd); break; - default: - break; } if ((ControlId >= ID_MENU_PLUGINS) && (ControlId < ID_MENU_PLUGINS_MAX)) { - PmProcessEntry(GetFocus(), ControlId, ObjectTreeSelectedItem); + PmProcessEntry(GetFocus(), ControlId); } return FALSE; } -/* -* MainWindowTreeViewSelChanged -* -* Purpose: -* -* Tree List TVN_ITEMEXPANDED, TVN_SELCHANGED handler. -* -*/ -VOID MainWindowTreeViewSelChanged( - _In_ LPNMTREEVIEWW trhdr -) -{ - HTREEITEM treeItem, treeRoot; - TVITEMEX tvexItem; - POE_LIST_ITEM objectListItem = NULL, prevObjectListItem = NULL; - SIZE_T objectPathLength = 1; // size of empty string buffer in characters - WCHAR szTreeItemText[MAX_PATH + 1]; - - if (trhdr == NULL) - return; - - if (!trhdr->itemNew.hItem) - return; - - if (g_WinObj.CurrentObjectPath != NULL) - supHeapFree(g_WinObj.CurrentObjectPath); - - RtlSecureZeroMemory(&tvexItem, sizeof(tvexItem)); - - treeRoot = TreeView_GetRoot(trhdr->hdr.hwndFrom); - - // - // Build the path from bottom to top and counting string buffer size. - // - for (treeItem = trhdr->itemNew.hItem; treeItem != treeRoot; - treeItem = TreeView_GetParent(trhdr->hdr.hwndFrom, treeItem)) - { - RtlSecureZeroMemory(&szTreeItemText, sizeof(szTreeItemText)); - tvexItem.mask = TVIF_HANDLE | TVIF_TEXT; - tvexItem.hItem = treeItem; - tvexItem.pszText = szTreeItemText; - tvexItem.cchTextMax = MAX_PATH; - TreeView_GetItem(trhdr->hdr.hwndFrom, &tvexItem); - - objectPathLength += _strlen(szTreeItemText) + 1; //+1 for '\' - - objectListItem = (POE_LIST_ITEM)supHeapAlloc(sizeof(OE_LIST_ITEM)); - if (objectListItem) { - objectListItem->Prev = prevObjectListItem; - objectListItem->TreeItem = treeItem; - } - prevObjectListItem = objectListItem; - } - - if (objectListItem == NULL) { - g_WinObj.CurrentObjectPath = (LPWSTR)supHeapAlloc(2 * sizeof(WCHAR)); - if (g_WinObj.CurrentObjectPath) { - g_WinObj.CurrentObjectPath[0] = L'\\'; - g_WinObj.CurrentObjectPath[1] = 0; - } - return; - } - - objectListItem = prevObjectListItem; - g_WinObj.CurrentObjectPath = (LPWSTR)supHeapAlloc(objectPathLength * sizeof(WCHAR)); - if (g_WinObj.CurrentObjectPath) { - - objectPathLength = 0; - - // - // Building the final string. - // - while (objectListItem != NULL) { - - RtlSecureZeroMemory(&szTreeItemText, sizeof(szTreeItemText)); - tvexItem.mask = TVIF_HANDLE | TVIF_TEXT; - tvexItem.hItem = objectListItem->TreeItem; - tvexItem.pszText = szTreeItemText; - tvexItem.cchTextMax = MAX_PATH; - TreeView_GetItem(trhdr->hdr.hwndFrom, &tvexItem); - - g_WinObj.CurrentObjectPath[objectPathLength] = L'\\'; - objectPathLength++; - _strcpy(g_WinObj.CurrentObjectPath + objectPathLength, szTreeItemText); - objectPathLength += _strlen(szTreeItemText); - - prevObjectListItem = objectListItem->Prev; - supHeapFree(objectListItem); - objectListItem = prevObjectListItem; - } - } - return; -} - /* * MainWindowPopupMenuInsertViewSD * @@ -679,15 +620,19 @@ VOID MainWindowHandleTreePopupMenu( ) { HMENU hMenu; + UINT uPosition = 0; hMenu = CreatePopupMenu(); if (hMenu) { - InsertMenu(hMenu, 0, MF_BYCOMMAND, ID_OBJECT_PROPERTIES, T_PROPERTIES); + InsertMenu(hMenu, uPosition++, MF_BYCOMMAND, ID_OBJECT_COPY_NAME, T_COPY_OBJECT_NAME); + InsertMenu(hMenu, uPosition++, MF_BYCOMMAND, ID_OBJECT_COPY_NAME_BINARY, T_COPY_OBJECT_NAME_BIN); + InsertMenu(hMenu, uPosition++, MF_BYPOSITION | MF_SEPARATOR, 0, NULL); + InsertMenu(hMenu, uPosition++, MF_BYCOMMAND, ID_OBJECT_PROPERTIES, T_PROPERTIES); supSetMenuIcon(hMenu, ID_OBJECT_PROPERTIES, ImageList_ExtractIcon(g_WinObj.hInstance, g_ToolBarMenuImages, 0)); - MainWindowPopupMenuInsertViewSD(hMenu, 1); + MainWindowPopupMenuInsertViewSD(hMenu, uPosition++); PmBuildPluginPopupMenuByObjectType(hMenu, ObjectTypeDirectory); @@ -719,6 +664,9 @@ VOID MainWindowHandleObjectPopupMenu( hMenu = CreatePopupMenu(); if (hMenu == NULL) return; + InsertMenu(hMenu, uPosition++, MF_BYCOMMAND, ID_OBJECT_COPY_NAME, T_COPY_OBJECT_NAME); + InsertMenu(hMenu, uPosition++, MF_BYCOMMAND, ID_OBJECT_COPY_NAME_BINARY, T_COPY_OBJECT_NAME_BIN); + InsertMenu(hMenu, uPosition++, MF_BYPOSITION | MF_SEPARATOR, 0, NULL); InsertMenu(hMenu, uPosition++, MF_BYCOMMAND, ID_OBJECT_PROPERTIES, T_PROPERTIES); supSetMenuIcon(hMenu, ID_OBJECT_PROPERTIES, @@ -726,9 +674,6 @@ VOID MainWindowHandleObjectPopupMenu( objType = supObjectListGetObjectType(hwndlv, iItem); - // - // Only supOpenNamedObjectByType supported types. - // switch (objType) { // @@ -768,8 +713,6 @@ VOID MainWindowHandleObjectPopupMenu( MainWindowPopupMenuInsertViewSD(hMenu, uPosition); break; - default: - break; } EnableMenuItem(GetSubMenu(GetMenu(hwnd), IDMM_OBJECT), ID_OBJECT_GOTOLINKTARGET, uGotoSymLinkEnable); @@ -800,55 +743,78 @@ LRESULT MainWindowHandleWMNotify( LPTOOLTIPTEXT lpttt; LPNMLISTVIEW lvn; LPNMTREEVIEW lpnmTreeView; - LPWSTR str; - SIZE_T lcp; LVITEM lvitem; - TVHITTESTINFO hti; + TVHITTESTINFO tvhti; + LVHITTESTINFO lvhti; POINT pt; - WCHAR szItemString[MAX_PATH + 1]; + + OBEX_ITEM *objRef; if (hdr) { + // + // TreeList notify. + // if (hdr->hwndFrom == g_hwndObjectTree) { switch (hdr->code) { case TVN_ITEMEXPANDED: case TVN_SELCHANGED: SetFocus(g_hwndObjectTree); supSetWaitCursor(TRUE); - MainWindowTreeViewSelChanged((LPNMTREEVIEWW)lParam); - SendMessage(g_hwndStatusBar, WM_SETTEXT, 0, (LPARAM)g_WinObj.CurrentObjectPath); + lpnmTreeView = (LPNMTREEVIEW)lParam; + if (lpnmTreeView) { + ObjectTreeSelectedItem = lpnmTreeView->itemNew.hItem; - ListObjectsInDirectory(g_WinObj.CurrentObjectPath); + supBuildCurrentObjectList((OBEX_ITEM*)lpnmTreeView->itemNew.lParam); + ListCurrentDirectoryObjects(ObjectTreeSelectedItem); - ListView_SortItemsEx(g_hwndObjectList, &MainWindowObjectListCompareFunc, g_SortColumn); + supDisplayCurrentObjectPath(g_hwndStatusBar, NULL, TRUE); + + ListView_SortItemsEx(g_hwndObjectList, &MainWindowObjectListCompareFunc, g_SortColumn); + } supSetGotoLinkTargetToolButtonState(hwnd, 0, 0, TRUE, FALSE); supSetWaitCursor(FALSE); - - lpnmTreeView = (LPNMTREEVIEW)lParam; - if (lpnmTreeView) { - ObjectTreeSelectedItem = lpnmTreeView->itemNew.hItem; - } + break; case NM_RCLICK: GetCursorPos(&pt); - hti.pt = pt; - ScreenToClient(hdr->hwndFrom, &hti.pt); - if (TreeView_HitTest(hdr->hwndFrom, &hti) && - (hti.flags & (TVHT_ONITEM | TVHT_ONITEMRIGHT))) { - ObjectTreeSelectedItem = hti.hItem; + tvhti.pt = pt; + ScreenToClient(hdr->hwndFrom, &tvhti.pt); + if (TreeView_HitTest(hdr->hwndFrom, &tvhti) && + (tvhti.flags & (TVHT_ONITEM | TVHT_ONITEMRIGHT))) + { + ObjectTreeSelectedItem = tvhti.hItem; TreeView_SelectItem(g_hwndObjectTree, ObjectTreeSelectedItem); - SendMessage(g_hwndStatusBar, WM_SETTEXT, 0, (LPARAM)g_WinObj.CurrentObjectPath); + + if (supGetTreeViewItemParam(g_hwndObjectTree, ObjectTreeSelectedItem, &objRef)) + supBuildCurrentObjectList(objRef); + + supDisplayCurrentObjectPath(g_hwndStatusBar, NULL, TRUE); + supSetGotoLinkTargetToolButtonState(hwnd, 0, 0, TRUE, FALSE); - MainWindowHandleTreePopupMenu(hwnd, &pt); + } + break; + + case NM_DBLCLK: + GetCursorPos(&pt); + tvhti.pt = pt; + ScreenToClient(hdr->hwndFrom, &tvhti.pt); + if (TreeView_HitTest(hdr->hwndFrom, &tvhti) && + (tvhti.flags & (TVHT_ONITEM | TVHT_ONITEMRIGHT))) + { + MainWindowShowObjectProperties(hwnd); } break; } } + // + // ListView notify. + // if (hdr->hwndFrom == g_hwndObjectList) { switch (hdr->code) { case NM_SETFOCUS: @@ -866,27 +832,13 @@ LRESULT MainWindowHandleWMNotify( if ((lvn->uNewState & LVIS_SELECTED) && !(lvn->uOldState & LVIS_SELECTED)) { - RtlSecureZeroMemory(&szItemString, sizeof(szItemString)); - ListView_GetItemText(g_hwndObjectList, lvn->iItem, 0, szItemString, MAX_PATH); - lcp = _strlen(g_WinObj.CurrentObjectPath); - if (lcp) { - str = (LPWSTR)supHeapAlloc((lcp + sizeof(szItemString) + 4) * sizeof(WCHAR)); - if (str) { - - _strcpy(str, g_WinObj.CurrentObjectPath); - - if ((str[0] == L'\\') && (str[1] == 0)) { - _strcpy(str + lcp, szItemString); - } - else { - str[lcp] = L'\\'; - _strcpy(str + lcp + 1, szItemString); - } - SendMessage(g_hwndStatusBar, WM_SETTEXT, 0, (LPARAM)str); - supHeapFree(str); - } - supSetGotoLinkTargetToolButtonState(hwnd, g_hwndObjectList, lvn->iItem, FALSE, FALSE); + if (supGetListViewItemParam(g_hwndObjectList, lvn->iItem, &objRef)) { + supBuildCurrentObjectList(objRef); } + + supDisplayCurrentObjectPath(g_hwndStatusBar, NULL, TRUE); + supSetGotoLinkTargetToolButtonState(hwnd, g_hwndObjectList, lvn->iItem, FALSE, FALSE); + } break; @@ -910,12 +862,34 @@ LRESULT MainWindowHandleWMNotify( break; - case NM_DBLCLK: - MainWindowHandleObjectListProp(hwnd); + case NM_RCLICK: + GetCursorPos(&pt); + lvhti.pt = pt; + lvhti.iItem = -1; + ScreenToClient(hdr->hwndFrom, &lvhti.pt); + ListView_HitTest(hdr->hwndFrom, &lvhti); + if (lvhti.flags & LVHT_ONITEM) { + lvn = (LPNMLISTVIEW)lParam; + if (supGetListViewItemParam(g_hwndObjectList, lvn->iItem, &objRef)) { + supBuildCurrentObjectList(objRef); + } + + supDisplayCurrentObjectPath(g_hwndStatusBar, NULL, TRUE); + supSetGotoLinkTargetToolButtonState(hwnd, g_hwndObjectList, lvn->iItem, FALSE, FALSE); + } break; - default: + case NM_DBLCLK: + GetCursorPos(&pt); + lvhti.pt = pt; + lvhti.iItem = -1; + ScreenToClient(hdr->hwndFrom, &lvhti.pt); + ListView_HitTest(hdr->hwndFrom, &lvhti); + if (lvhti.flags & LVHT_ONITEM) { + MainWindowShowObjectProperties(hwnd); + } break; + } } @@ -937,9 +911,6 @@ LRESULT MainWindowHandleWMNotify( lpttt->uFlags |= TTF_DI_SETITEM; break; - default: - break; - } } } @@ -985,32 +956,33 @@ VOID MainWindowResizeHandler( } /* -* MainWindowProc +* MainWindowOnContextMenu * * Purpose: * -* Main window procedure. +* Main window WM_CONTEXTMENU handler. * */ -LRESULT CALLBACK MainWindowProc( +VOID MainWindowOnContextMenu( _In_ HWND hwnd, - _In_ UINT uMsg, - _In_ WPARAM wParam, + _In_ HWND hwndFrom, _In_ LPARAM lParam ) { - INT mark; - LONG NewSplitterPos; - RECT ToolBarRect, crc; - LPDRAWITEMSTRUCT pds; - LPMEASUREITEMSTRUCT pms; + RECT crc; + TVHITTESTINFO tvhti; + LVHITTESTINFO lvhti; + POINT pt; - switch (uMsg) { - case WM_CONTEXTMENU: - - RtlSecureZeroMemory(&crc, sizeof(crc)); + if (hwndFrom == g_hwndObjectTree) { - if ((HWND)wParam == g_hwndObjectTree) { + GetCursorPos(&pt); + tvhti.pt = pt; + ScreenToClient(g_hwndObjectTree, &tvhti.pt); + if (TreeView_HitTest(g_hwndObjectTree, &tvhti) && + (tvhti.flags & (TVHT_ONITEM | TVHT_ONITEMRIGHT))) + { + RtlSecureZeroMemory(&crc, sizeof(crc)); TreeView_GetItemRect(g_hwndObjectTree, TreeView_GetSelection(g_hwndObjectTree), &crc, TRUE); @@ -1020,19 +992,54 @@ LRESULT CALLBACK MainWindowProc( MainWindowHandleTreePopupMenu(hwnd, (LPPOINT)&crc); } - if ((HWND)wParam == g_hwndObjectList) { - mark = ListView_GetSelectionMark(g_hwndObjectList); - - if (lParam == MAKELPARAM(-1, -1)) { - ListView_GetItemRect(g_hwndObjectList, mark, &crc, TRUE); + } + else if (hwndFrom == g_hwndObjectList) { + + GetCursorPos(&pt); + lvhti.pt = pt; + lvhti.iItem = -1; + ScreenToClient(g_hwndObjectList, &lvhti.pt); + ListView_HitTest(g_hwndObjectList, &lvhti); + if (lvhti.flags & LVHT_ONITEM) { + if (lParam == MAKELPARAM(-1, -1)) + { + RtlSecureZeroMemory(&crc, sizeof(crc)); + + ListView_GetItemRect(g_hwndObjectList, lvhti.iItem, &crc, TRUE); crc.top = crc.bottom; ClientToScreen(g_hwndObjectList, (LPPOINT)&crc); } else GetCursorPos((LPPOINT)&crc); - MainWindowHandleObjectPopupMenu(hwnd, g_hwndObjectList, mark, (LPPOINT)&crc); + MainWindowHandleObjectPopupMenu(hwnd, g_hwndObjectList, lvhti.iItem, (LPPOINT)&crc); } + } +} + +/* +* MainWindowProc +* +* Purpose: +* +* Main window procedure. +* +*/ +LRESULT CALLBACK MainWindowProc( + _In_ HWND hwnd, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam +) +{ + LONG NewSplitterPos; + RECT ToolBarRect; + LPDRAWITEMSTRUCT pds; + LPMEASUREITEMSTRUCT pms; + + switch (uMsg) { + case WM_CONTEXTMENU: + MainWindowOnContextMenu(hwnd, (HWND)wParam, lParam); break; case WM_COMMAND: @@ -1119,12 +1126,16 @@ BOOL MainWindowDlgMsgHandler( _In_ LPMSG lpMsg ) { - if (g_DesktopPropWindow != NULL) - if (PropSheet_IsDialogMessage(g_DesktopPropWindow, lpMsg)) + HWND hwnd; + + hwnd = propGetDesktopWindow(); + if (hwnd != NULL) + if (PropSheet_IsDialogMessage(hwnd, lpMsg)) return TRUE; - if (g_PropWindow != NULL) - if (PropSheet_IsDialogMessage(g_PropWindow, lpMsg)) + hwnd = propGetCommonWindow(); + if (hwnd != NULL) + if (PropSheet_IsDialogMessage(hwnd, lpMsg)) return TRUE; return FALSE; @@ -1167,16 +1178,12 @@ DWORD guiInitGlobals( // // Create dedicated heap. // - Globals->Heap = RtlCreateHeap(HEAP_GROWABLE, NULL, 0, 0, NULL, NULL); + Globals->Heap = supCreateHeap(HEAP_GROWABLE, TRUE); if (Globals->Heap == NULL) { dwResult = INIT_ERROR_NOHEAP; break; } - if (IsWine == FALSE) { - RtlSetHeapInformation(Globals->Heap, HeapEnableTerminationOnCorruption, NULL, 0); - } - // // Remember %TEMP% directory. // @@ -1219,7 +1226,7 @@ DWORD guiInitGlobals( if (dwResult != INIT_NO_ERROR) { if (Globals->Heap) - RtlDestroyHeap(Globals->Heap); + supDestroyHeap(Globals->Heap); } return dwResult; @@ -1595,7 +1602,7 @@ DWORD guiCreateMainWindowAndComponents( WC_TREEVIEW, NULL, WS_VISIBLE | WS_CHILD | WS_TABSTOP | - TVS_DISABLEDRAGDROP | TVS_HASBUTTONS | TVS_HASLINES | TVS_LINESATROOT, + TVS_DISABLEDRAGDROP | TVS_HASBUTTONS | TVS_HASLINES | TVS_LINESATROOT | TVS_TRACKSELECT, 0, 0, 0, @@ -1892,10 +1899,12 @@ UINT WinObjExMain() UINT result = ERROR_SUCCESS; DWORD initResult; + RtlSecureZeroMemory(&g_WinObjStats, sizeof(g_WinObjStats)); + logCreate(); bIsFullAdmin = ntsupUserIsFullAdmin(); - bIsWine = (is_wine() == 1); + bIsWine = (IsWine() == 1); if (bIsWine) bIsFullAdmin = FALSE; // On Wine drop admin related features as they require driver. if (!InitMSVCRT()) { @@ -1942,7 +1951,7 @@ UINT WinObjExMain() guiCreateObjectListColumns(); - ListObjectDirectoryTree(KM_OBJECTS_ROOT_DIRECTORY, NULL, NULL); + ListObjectDirectoryTree(ObGetPredefinedUnicodeString(OBP_ROOT), NULL, NULL); TreeView_SelectItem(g_hwndObjectTree, TreeView_GetRoot(g_hwndObjectTree)); SetFocus(g_hwndObjectTree); @@ -1957,6 +1966,7 @@ UINT WinObjExMain() // // Do not move anywhere. // + ListHeapDestroy(); supShutdown(); logFree(); @@ -1978,6 +1988,7 @@ UINT WinObjExMain() void main() { __security_init_cookie(); + SetUnhandledExceptionFilter((LPTOP_LEVEL_EXCEPTION_FILTER)exceptFilterUnhandled); ExitProcess(WinObjExMain()); } #else @@ -1994,6 +2005,7 @@ int CALLBACK WinMain( UNREFERENCED_PARAMETER(lpCmdLine); UNREFERENCED_PARAMETER(nCmdShow); + SetUnhandledExceptionFilter((LPTOP_LEVEL_EXCEPTION_FILTER)exceptFilterUnhandled); ExitProcess(WinObjExMain()); } #endif diff --git a/Source/WinObjEx64/msvcver.h b/Source/WinObjEx64/msvcver.h index 9075a6ba..ce5e6194 100644 --- a/Source/WinObjEx64/msvcver.h +++ b/Source/WinObjEx64/msvcver.h @@ -4,9 +4,9 @@ * * TITLE: MSVCVER.H * -* VERSION: 1.93 +* VERSION: 2.00 * -* DATE: 30 Apr 2022 +* DATE: 19 Jun 2022 * * Visual Studio compiler version determination. * diff --git a/Source/WinObjEx64/objects.c b/Source/WinObjEx64/objects.c index aa38f1eb..8d5ac774 100644 --- a/Source/WinObjEx64/objects.c +++ b/Source/WinObjEx64/objects.c @@ -1,12 +1,12 @@ /******************************************************************************* * -* (C) COPYRIGHT AUTHORS, 2017 - 2021 +* (C) COPYRIGHT AUTHORS, 2017 - 2022 * * TITLE: OBJECTS.C * -* VERSION: 1.92 +* VERSION: 2.00 * -* DATE: 03 Oct 2021 +* DATE: 19 Jun 2022 * * THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF * ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED @@ -214,13 +214,13 @@ INT ObManagerComparerName( * */ LPWSTR ObManagerGetNameByIndex( - _In_ ULONG TypeIndex + _In_ WOBJ_OBJECT_TYPE TypeIndex ) { ULONG nIndex; for (nIndex = 0; nIndex < g_ObjectTypesCount; nIndex++) { - if (gpObjectTypes[nIndex]->Index == (WOBJ_OBJECT_TYPE)TypeIndex) + if (gpObjectTypes[nIndex]->Index == TypeIndex) return gpObjectTypes[nIndex]->Name; } @@ -237,19 +237,41 @@ LPWSTR ObManagerGetNameByIndex( * */ UINT ObManagerGetImageIndexByTypeIndex( - _In_ ULONG TypeIndex + _In_ WOBJ_OBJECT_TYPE TypeIndex ) { - ULONG nIndex; + ULONG i; - for (nIndex = 0; nIndex < g_ObjectTypesCount; nIndex++) { - if (gpObjectTypes[nIndex]->Index == (WOBJ_OBJECT_TYPE)TypeIndex) - return gpObjectTypes[nIndex]->ImageIndex; + for (i = 0; i < g_ObjectTypesCount; i++) { + if (gpObjectTypes[i]->Index == TypeIndex) + return gpObjectTypes[i]->ImageIndex; } return ObjectTypeUnknown; } +/* +* ObManagerGetEntryByTypeIndex +* +* Purpose: +* +* Returns object entry by type index. +* +*/ +WOBJ_TYPE_DESC* ObManagerGetEntryByTypeIndex( + _In_ WOBJ_OBJECT_TYPE TypeIndex +) +{ + ULONG i; + + for (i = 0; i < g_ObjectTypesCount; i++) { + if (gpObjectTypes[i]->Index == TypeIndex) + return gpObjectTypes[i]; + } + + return &g_TypeUnknown; +} + /* * ObManagerGetEntryByTypeName * @@ -296,7 +318,7 @@ WOBJ_TYPE_DESC* ObManagerGetEntryByTypeName( * Returns object index of known type. * */ -UINT ObManagerGetIndexByTypeName( +WOBJ_OBJECT_TYPE ObManagerGetIndexByTypeName( _In_opt_ LPCWSTR lpTypeName ) { diff --git a/Source/WinObjEx64/objects.h b/Source/WinObjEx64/objects.h index ea8c2719..77bd6286 100644 --- a/Source/WinObjEx64/objects.h +++ b/Source/WinObjEx64/objects.h @@ -4,9 +4,9 @@ * * TITLE: OBJECTS.H * -* VERSION: 1.94 +* VERSION: 2.00 * -* DATE: 31 May 2022 +* DATE: 19 Jun 2022 * * Header file for internal Windows object types handling. * @@ -20,6 +20,7 @@ // // Object Type Indexes Used By Program Only +// // NOT RELATED TO REAL OBJECTS INDEXES // ObjectTypeUnknown and ObjectTypeMax always end this list // @@ -222,17 +223,19 @@ HIMAGELIST ObManagerLoadImageList( VOID); UINT ObManagerGetImageIndexByTypeIndex( - _In_ ULONG TypeIndex); + _In_ WOBJ_OBJECT_TYPE TypeIndex); UINT ObManagerGetImageIndexByTypeName( _In_opt_ LPCWSTR lpTypeName); - -UINT ObManagerGetIndexByTypeName( +WOBJ_OBJECT_TYPE ObManagerGetIndexByTypeName( _In_opt_ LPCWSTR lpTypeName); LPWSTR ObManagerGetNameByIndex( - _In_ ULONG TypeIndex); + _In_ WOBJ_OBJECT_TYPE TypeIndex); + +WOBJ_TYPE_DESC* ObManagerGetEntryByTypeIndex( + _In_ WOBJ_OBJECT_TYPE TypeIndex); WOBJ_TYPE_DESC *ObManagerGetEntryByTypeName( _In_opt_ LPCWSTR lpTypeName); diff --git a/Source/WinObjEx64/plugmngr.c b/Source/WinObjEx64/plugmngr.c index 0fb3246d..130eb862 100644 --- a/Source/WinObjEx64/plugmngr.c +++ b/Source/WinObjEx64/plugmngr.c @@ -4,9 +4,9 @@ * * TITLE: PLUGMNGR.C * -* VERSION: 1.94 +* VERSION: 2.00 * -* DATE: 04 Jun 2022 +* DATE: 19 Jun 2022 * * Plugin manager. * @@ -18,7 +18,6 @@ *******************************************************************************/ #define OEMRESOURCE #include "global.h" -#include "ui.h" static LIST_ENTRY g_PluginsListHead; volatile UINT g_PluginCount = 0; @@ -44,6 +43,34 @@ BOOL PmpReadSystemMemoryEx( return kdReadSystemMemory2(NULL, Address, Buffer, BufferSize, NumberOfBytesRead); } +/* +* PmpOpenObjectByType +* +* Purpose: +* +* Open object by type (plugin version). +* +*/ +NTSTATUS PmpOpenObjectByType( + _Out_ HANDLE* ObjectHandle, + _In_ ULONG TypeIndex, + _In_ PUNICODE_STRING ObjectDirectory, + _In_ PUNICODE_STRING ObjectName, + _In_ ACCESS_MASK DesiredAccess +) +{ + __try { + return supOpenNamedObjectByType(ObjectHandle, + TypeIndex, + ObjectDirectory, + ObjectName, + DesiredAccess); + } + __except (WOBJ_EXCEPTION_FILTER_LOG) { + return GetExceptionCode(); + } +} + /* * PmpReportInvalidPlugin * @@ -580,15 +607,11 @@ WINOBJEX_PLUGIN_INTERNAL* PmpGetEntryById( * */ VOID PmpFreeObjectData( - _In_ PWINOBJEX_PARAM_OBJECT ObjectPtr + _In_ PWINOBJEX_PARAM_OBJECT ParamObject ) { - if (ObjectPtr->ObjectDirectory) { - HeapFree(GetProcessHeap(), 0, ObjectPtr->ObjectDirectory); - } - if (ObjectPtr->ObjectName) { - HeapFree(GetProcessHeap(), 0, ObjectPtr->ObjectName); - } + supFreeDuplicatedUnicodeString(g_obexHeap, &ParamObject->Directory, FALSE); + supFreeDuplicatedUnicodeString(g_obexHeap, &ParamObject->Name, FALSE); } /* @@ -600,84 +623,11 @@ VOID PmpFreeObjectData( * */ BOOL PmpAllocateObjectData( - _In_ HWND ParentWindow, - _In_ PWINOBJEX_PARAM_OBJECT ObjectPtr, - _In_opt_ HTREEITEM ObjectTreeItem + _In_ PWINOBJEX_PARAM_OBJECT ParamObject ) { - INT nSelected; - LPWSTR lpObjectName = NULL; - - HANDLE processHeap = GetProcessHeap(); - BOOL bNameAllocated = FALSE; - - TV_ITEM tvi; - WCHAR szBuffer[MAX_PATH + 1]; - - ObjectPtr->ObjectDirectory = NULL; - ObjectPtr->ObjectName = NULL; - ObjectPtr->Reserved = NULL; - - if (ParentWindow == g_hwndObjectList) { - - // - // Query selected index, leave on failure. - // - nSelected = ListView_GetSelectionMark(g_hwndObjectList); - if (nSelected == -1) - return FALSE; - - lpObjectName = supGetItemText(g_hwndObjectList, nSelected, 0, NULL); - if (lpObjectName) bNameAllocated = TRUE; - - } - else - if (ParentWindow == g_hwndObjectTree) { - if (ObjectTreeItem) { - - RtlSecureZeroMemory(&tvi, sizeof(TV_ITEM)); - - szBuffer[0] = 0; - RtlSecureZeroMemory(&szBuffer, sizeof(szBuffer)); - tvi.pszText = szBuffer; - tvi.cchTextMax = MAX_PATH; - tvi.mask = TVIF_TEXT; - tvi.hItem = ObjectTreeItem; - if (TreeView_GetItem(g_hwndObjectTree, &tvi)) { - lpObjectName = (LPWSTR)&szBuffer; - bNameAllocated = FALSE; - } - } - } - else - return FALSE; - - if (lpObjectName == NULL) - return FALSE; - - ObjectPtr->ObjectDirectory = (LPWSTR)HeapAlloc(processHeap, HEAP_ZERO_MEMORY, - (1 + _strlen(g_WinObj.CurrentObjectPath)) * sizeof(WCHAR)); - - if (ObjectPtr->ObjectDirectory) { - _strcpy(ObjectPtr->ObjectDirectory, g_WinObj.CurrentObjectPath); - } - else { - return FALSE; - } - - ObjectPtr->ObjectName = (LPWSTR)HeapAlloc(processHeap, HEAP_ZERO_MEMORY, - (1 + _strlen(lpObjectName)) * sizeof(WCHAR)); - - if (ObjectPtr->ObjectName) { - _strcpy(ObjectPtr->ObjectName, lpObjectName); - } - else { - HeapFree(processHeap, 0, ObjectPtr->ObjectDirectory); - ObjectPtr->ObjectDirectory = NULL; - return FALSE; - } - - return TRUE; + return supGetCurrentObjectPath(FALSE, &ParamObject->Directory) && + supGetCurrentObjectName(&ParamObject->Name); } /* @@ -690,8 +640,7 @@ BOOL PmpAllocateObjectData( */ VOID PmProcessEntry( _In_ HWND ParentWindow, - _In_ UINT Id, - _In_opt_ HTREEITEM ObjectTreeItem + _In_ UINT Id ) { NTSTATUS ntStatus; @@ -780,7 +729,7 @@ VOID PmProcessEntry( // if (PluginEntry->Plugin.Type == ContextPlugin) { - if (!PmpAllocateObjectData(ParentWindow, &ParamBlock.Object, ObjectTreeItem)) { + if (!PmpAllocateObjectData(&ParamBlock.Object)) { MessageBox(ParentWindow, TEXT("Cannot allocate memory for plugin data"), @@ -800,12 +749,12 @@ VOID PmProcessEntry( // ParamBlock.ReadSystemMemoryEx = (pfnReadSystemMemoryEx)&PmpReadSystemMemoryEx; ParamBlock.GetInstructionLength = (pfnGetInstructionLength)&kdGetInstructionLength; - ParamBlock.OpenNamedObjectByType = (pfnOpenNamedObjectByType)&supOpenNamedObjectByType; + ParamBlock.OpenNamedObjectByType = (pfnOpenNamedObjectByType)&PmpOpenObjectByType; // // Version. // - RtlCopyMemory(&ParamBlock.Version, &g_WinObj.osver, sizeof(RTL_OSVERSIONINFOW)); + ParamBlock.Version = g_WinObj.osver; ntStatus = PluginEntry->Plugin.StartPlugin(&ParamBlock); @@ -814,10 +763,9 @@ VOID PmProcessEntry( ultohex((ULONG)ntStatus, _strend(szMessage)); MessageBox(ParentWindow, szMessage, NULL, MB_ICONERROR); } - else { - if (PluginEntry->Plugin.Type == ContextPlugin) { - PmpFreeObjectData(&ParamBlock.Object); - } + + if (PluginEntry->Plugin.Type == ContextPlugin) { + PmpFreeObjectData(&ParamBlock.Object); } } @@ -1150,8 +1098,6 @@ VOID PmpHandleNotify( break; - default: - break; } } @@ -1203,7 +1149,7 @@ INT_PTR CALLBACK PmpDialogProc( switch (uMsg) { case WM_INITDIALOG: - supCenterWindowSpecifyParent(hwndDlg, g_WinObj.MainWindow); + supCenterWindowSpecifyParent(hwndDlg, g_hwndMain); PmpEnumerateEntries(hwndDlg); break; diff --git a/Source/WinObjEx64/plugmngr.h b/Source/WinObjEx64/plugmngr.h index 82da40a5..dae7d65b 100644 --- a/Source/WinObjEx64/plugmngr.h +++ b/Source/WinObjEx64/plugmngr.h @@ -4,9 +4,9 @@ * * TITLE: PLUGINMNGR.H * -* VERSION: 1.94 +* VERSION: 2.00 * -* DATE: 07 Jun 2022 +* DATE: 19 Jun 2022 * * Common header file for the plugin manager. * @@ -19,7 +19,7 @@ #pragma once -#define WOBJ_PLUGIN_SYSTEM_VERSION 18712 +#define WOBJ_PLUGIN_SYSTEM_VERSION 20006 // // Plugin init routine name. @@ -58,14 +58,13 @@ typedef UCHAR(CALLBACK* pfnGetInstructionLength)( typedef NTSTATUS(*pfnOpenNamedObjectByType)( _Out_ HANDLE* ObjectHandle, _In_ ULONG TypeIndex, - _In_ LPWSTR ObjectDirectory, - _In_opt_ LPWSTR ObjectName, + _In_ PUNICODE_STRING ObjectDirectory, + _In_ PUNICODE_STRING ObjectName, _In_ ACCESS_MASK DesiredAccess); typedef struct _WINOBJEX_PARAM_OBJECT { - LPWSTR ObjectName; - LPWSTR ObjectDirectory; - PVOID Reserved; + UNICODE_STRING Name; + UNICODE_STRING Directory; } WINOBJEX_PARAM_OBJECT, * PWINOBJEX_PARAM_OBJECT; typedef struct _WINOBJEX_PARAM_BLOCK { @@ -162,8 +161,7 @@ VOID PmDestroy(); VOID PmProcessEntry( _In_ HWND ParentWindow, - _In_ UINT Id, - _In_opt_ HTREEITEM ObjectTreeItem); + _In_ UINT Id); VOID PmBuildPluginPopupMenuByObjectType( _In_ HMENU ContextMenu, diff --git a/Source/WinObjEx64/props/propAlpcPort.c b/Source/WinObjEx64/props/propAlpcPort.c index 4dbea6ed..ecef8971 100644 --- a/Source/WinObjEx64/props/propAlpcPort.c +++ b/Source/WinObjEx64/props/propAlpcPort.c @@ -4,9 +4,9 @@ * * TITLE: PROPALPCPORT.C * -* VERSION: 1.93 +* VERSION: 2.00 * -* DATE: 11 May 2021 +* DATE: 19 Jun 2022 * * THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF * ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED @@ -15,7 +15,6 @@ * *******************************************************************************/ #include "global.h" -#include "propDlg.h" #include "extras.h" #define COLUMN_ALPCLIST_SERVER_PORT 0 @@ -138,9 +137,6 @@ VOID AlpcPortListHandleWMCommand( } break; - - default: - break; } } @@ -195,8 +191,6 @@ BOOL AlpcPortListHandleNotify( } break; - default: - break; } return FALSE; diff --git a/Source/WinObjEx64/props/propAlpcPort.h b/Source/WinObjEx64/props/propAlpcPort.h deleted file mode 100644 index 5b1c8b04..00000000 --- a/Source/WinObjEx64/props/propAlpcPort.h +++ /dev/null @@ -1,25 +0,0 @@ -/******************************************************************************* -* -* (C) COPYRIGHT AUTHORS, 2021 -* -* TITLE: PROPALPCPORT.H -* -* VERSION: 1.90 -* -* DATE: 11 May 2021 -* -* Common header file for ALPC Port property sheet. -* -* THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF -* ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED -* TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A -* PARTICULAR PURPOSE. -* -*******************************************************************************/ -#pragma once - -INT_PTR CALLBACK AlpcPortListDialogProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam); diff --git a/Source/WinObjEx64/props/propBasic.c b/Source/WinObjEx64/props/propBasic.c index c2a8222e..11958320 100644 --- a/Source/WinObjEx64/props/propBasic.c +++ b/Source/WinObjEx64/props/propBasic.c @@ -4,9 +4,9 @@ * * TITLE: PROPBASIC.C * -* VERSION: 1.94 +* VERSION: 2.00 * -* DATE: 07 Jun 2022 +* DATE: 19 Jun 2022 * * THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF * ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED @@ -33,7 +33,7 @@ typedef VOID(CALLBACK* pfnPropQueryInfoRoutine)( // VOID propSetBasicInfoEx( _In_ HWND hwndDlg, - _In_ POBJINFO InfoObject); + _In_ POBEX_OBJECT_INFORMATION InfoObject); /* * propSetObjectHeaderAddressInfo @@ -1048,10 +1048,10 @@ PROP_QUERY_INFORMATION_ROUTINE(propBasicQuerySymlink) NTSTATUS status; ULONG bytesNeeded; HANDLE hObject = NULL; - LPWSTR lpLinkTarget; WCHAR szBuffer[MAX_PATH + 1]; OBJECT_BASIC_INFORMATION obi; + UNICODE_STRING objectName, normalizedName; SetDlgItemText(hwndDlg, ID_OBJECT_SYMLINK_TARGET, T_CannotQuery); SetDlgItemText(hwndDlg, ID_OBJECT_SYMLINK_CREATION, T_CannotQuery); @@ -1063,15 +1063,24 @@ PROP_QUERY_INFORMATION_ROUTINE(propBasicQuerySymlink) return; } - // - // Copy link target from main object list for performance reasons. - // Because we don't need to query same data again. - // - lpLinkTarget = Context->lpDescription; - if (lpLinkTarget) { - SetDlgItemText(hwndDlg, ID_OBJECT_SYMLINK_TARGET, lpLinkTarget); - } + if (supCreateObjectPathFromElements(&Context->NtObjectName, + &Context->NtObjectPath, + &objectName, + TRUE)) + { + if (supResolveSymbolicLinkTargetNormalized( + hObject, + NULL, + &objectName, + &normalizedName)) + { + SetDlgItemText(hwndDlg, ID_OBJECT_SYMLINK_TARGET, normalizedName.Buffer); + supFreeDuplicatedUnicodeString(g_obexHeap, &normalizedName, FALSE); + } + supFreeDuplicatedUnicodeString(g_obexHeap, &objectName, FALSE); + } + //Query Link Creation Time RtlSecureZeroMemory(&obi, sizeof(OBJECT_BASIC_INFORMATION)); @@ -1435,6 +1444,11 @@ PROP_QUERY_INFORMATION_ROUTINE(propBasicQuerySection) szBuffer[0] = 0; ultostr(sii.SubSystemMinorVersion, szBuffer); SetDlgItemText(hwndDlg, ID_IMAGE_MNV, szBuffer); + + //Image Flags + szBuffer[0] = 0; + ultostr(sii.ImageFlags, szBuffer); + SetDlgItemText(hwndDlg, ID_IMAGE_FLAGS, szBuffer); } } } @@ -1507,24 +1521,25 @@ PROP_QUERY_INFORMATION_ROUTINE(propBasicQueryWindowStation) */ PROP_QUERY_INFORMATION_ROUTINE(propBasicQueryDriver) { - LPWSTR lpItemText; ENUMCHILDWNDDATA ChildWndData; + WCHAR szBuffer[MAX_PATH + 1]; + UNREFERENCED_PARAMETER(ExtendedInfoAvailable); - // - // For performance reasons instead of query again - // we use description from main object list. - // - lpItemText = Context->lpDescription; - if (lpItemText) { + RtlSecureZeroMemory(&szBuffer, sizeof(szBuffer)); + if (supQueryDriverDescription(Context->NtObjectName.Buffer, + szBuffer, + MAX_PATH)) + { //show hidden controls if (GetWindowRect(GetDlgItem(hwndDlg, ID_DRIVERINFO), &ChildWndData.Rect)) { ChildWndData.nCmdShow = SW_SHOW; EnumChildWindows(hwndDlg, supCallbackShowChildWindow, (LPARAM)&ChildWndData); } - SetDlgItemText(hwndDlg, ID_DRIVERDISPLAYNAME, lpItemText); + SetDlgItemText(hwndDlg, ID_DRIVERDISPLAYNAME, szBuffer); } + } /* @@ -1537,24 +1552,27 @@ PROP_QUERY_INFORMATION_ROUTINE(propBasicQueryDriver) */ PROP_QUERY_INFORMATION_ROUTINE(propBasicQueryDevice) { - LPWSTR lpItemText; ENUMCHILDWNDDATA ChildWndData; + WCHAR szBuffer[MAX_PATH + 1]; + UNREFERENCED_PARAMETER(ExtendedInfoAvailable); - // - // For performance reasons instead of query again - // we use description from main object list. - // - lpItemText = Context->lpDescription; - if (lpItemText) { + RtlSecureZeroMemory(&szBuffer, sizeof(szBuffer)); + if (supQueryDeviceDescription( + &Context->NtObjectPath, + &Context->NtObjectName, + szBuffer, + MAX_PATH)) + { //show hidden controls if (GetWindowRect(GetDlgItem(hwndDlg, ID_DEVICEINFO), &ChildWndData.Rect)) { ChildWndData.nCmdShow = SW_SHOW; EnumChildWindows(hwndDlg, supCallbackShowChildWindow, (LPARAM)&ChildWndData); } - SetDlgItemText(hwndDlg, ID_DEVICEDESCRIPTION, lpItemText); + SetDlgItemText(hwndDlg, ID_DEVICEDESCRIPTION, szBuffer); } + } /* @@ -1562,7 +1580,7 @@ PROP_QUERY_INFORMATION_ROUTINE(propBasicQueryDevice) * * Purpose: * -* Set information values for Partition object type +* Set information values for MemoryPartition object type * */ PROP_QUERY_INFORMATION_ROUTINE(propBasicQueryMemoryPartition) @@ -1584,6 +1602,33 @@ PROP_QUERY_INFORMATION_ROUTINE(propBasicQueryMemoryPartition) propCloseCurrentObject(Context, hObject); } +/* +* propBasicQueryRegistryTransaction +* +* Purpose: +* +* Set information values for RegistryTransaction object type +* +*/ +PROP_QUERY_INFORMATION_ROUTINE(propBasicQueryRegistryTransaction) +{ + HANDLE hObject = NULL; + + UNREFERENCED_PARAMETER(ExtendedInfoAvailable); + + // + // Open Registry Transaction object. + // + if (!propOpenCurrentObject(Context, &hObject, TRANSACTION_QUERY_INFORMATION)) + return; + + // + // Query object basic and type info if needed. + // + propSetDefaultInfo(Context, hwndDlg, hObject); + propCloseCurrentObject(Context, hObject); +} + /* * propBasicQueryProcess * @@ -1905,7 +1950,7 @@ PROP_QUERY_INFORMATION_ROUTINE(propBasicQueryThread) THREAD_NAME_INFORMATION *NameInformation; - Thread = &Context->UnnamedObjectInfo.ThreadInformation; + Thread = &Context->u1.UnnamedObjectInfo.ThreadInformation; // // Open Thread object. @@ -2710,7 +2755,8 @@ PROP_QUERY_INFORMATION_ROUTINE(propBasicQueryDesktop) BOOL bExtendedInfoAvailable; HANDLE hDesktop = NULL; ULONG_PTR ObjectAddress = 0, HeaderAddress = 0, InfoHeaderAddress = 0; - OBJINFO InfoObject; + + OBEX_OBJECT_INFORMATION InfoObject; UNREFERENCED_PARAMETER(ExtendedInfoAvailable); @@ -2788,7 +2834,7 @@ PROP_QUERY_INFORMATION_ROUTINE(propBasicQueryDesktop) */ VOID propSetBasicInfoEx( _In_ HWND hwndDlg, - _In_ POBJINFO InfoObject + _In_ POBEX_OBJECT_INFORMATION InfoObject ) { INT i; @@ -2852,18 +2898,30 @@ VOID propSetBasicInfo( _In_ HWND hwndDlg ) { - BOOL ExtendedInfoAvailable = FALSE, bQueryTrustLabel = FALSE; - POBJINFO InfoObject = NULL; + BOOL ExtendedInfoAvailable = FALSE, bQueryTrustLabel = FALSE; + POBEX_OBJECT_INFORMATION InfoObject = NULL; pfnPropQueryInfoRoutine propQueryInfoRoutine; - SetDlgItemText(hwndDlg, ID_OBJECT_NAME, Context->lpObjectName); - SetDlgItemText(hwndDlg, ID_OBJECT_TYPE, Context->lpObjectType); + UNICODE_STRING usObjectName; + + if (supNormalizeUnicodeStringForDisplay(g_obexHeap, + &Context->NtObjectName, + &usObjectName)) + { + SetDlgItemText(hwndDlg, ID_OBJECT_NAME, usObjectName.Buffer); + supFreeDuplicatedUnicodeString(g_obexHeap, &usObjectName, FALSE); + } + else { + SetDlgItemText(hwndDlg, ID_OBJECT_NAME, Context->NtObjectName.Buffer); + } + + SetDlgItemText(hwndDlg, ID_OBJECT_TYPE, Context->TypeDescription->Name); // // Desktops should be parsed differently. // - if (Context->TypeIndex != ObjectTypeDesktop) { + if (Context->ObjectTypeIndex != ObjectTypeDesktop) { // // Dump object information depending on context type. @@ -2871,15 +2929,16 @@ VOID propSetBasicInfo( switch (Context->ContextType) { case propPrivateNamespace: - InfoObject = ObQueryObjectByAddress(Context->NamespaceInfo.ObjectAddress); + InfoObject = ObQueryObjectByAddress(Context->u1.NamespaceInfo.ObjectAddress); break; case propUnnamed: - InfoObject = ObQueryObjectByAddress(Context->UnnamedObjectInfo.ObjectAddress); + InfoObject = ObQueryObjectByAddress(Context->u1.UnnamedObjectInfo.ObjectAddress); break; + case propNormal: default: - InfoObject = ObQueryObject(Context->lpCurrentObjectPath, Context->lpObjectName); + InfoObject = ObQueryObjectInDirectory(&Context->NtObjectName, &Context->NtObjectPath); break; } @@ -2888,11 +2947,11 @@ VOID propSetBasicInfo( if (Context->ContextType == propUnnamed) { - if (Context->UnnamedObjectInfo.ObjectAddress) { + if (Context->u1.UnnamedObjectInfo.ObjectAddress) { propSetObjectHeaderAddressInfo( hwndDlg, - Context->UnnamedObjectInfo.ObjectAddress, - (ULONG_PTR)OBJECT_TO_OBJECT_HEADER(Context->UnnamedObjectInfo.ObjectAddress)); + Context->u1.UnnamedObjectInfo.ObjectAddress, + (ULONG_PTR)OBJECT_TO_OBJECT_HEADER(Context->u1.UnnamedObjectInfo.ObjectAddress)); } } else { @@ -2902,7 +2961,7 @@ VOID propSetBasicInfo( } else { //make copy of received dump - supCopyMemory(&Context->ObjectInfo, sizeof(OBJINFO), InfoObject, sizeof(OBJINFO)); + RtlCopyMemory(&Context->ObjectInfo, InfoObject, sizeof(OBEX_OBJECT_INFORMATION)); // // Set Object Address, Header Address, NP/PP Charge, RefCount, HandleCount, Attributes. @@ -2913,7 +2972,7 @@ VOID propSetBasicInfo( // Special case for AlpcPort object type. // The only information we can get is from driver here as we cannot open port directly. // - if (Context->TypeIndex == ObjectTypePort) { + if (Context->ObjectTypeIndex == ObjectTypePort) { propBasicQueryAlpcPort(Context, hwndDlg, FALSE); } @@ -2927,7 +2986,7 @@ VOID propSetBasicInfo( // propQueryInfoRoutine = NULL; - switch (Context->TypeIndex) { + switch (Context->ObjectTypeIndex) { case ObjectTypeDirectory: bQueryTrustLabel = TRUE; //if TRUE skip this because directory is basic dialog and basic info already set @@ -2981,6 +3040,9 @@ VOID propSetBasicInfo( case ObjectTypeMemoryPartition: propQueryInfoRoutine = (pfnPropQueryInfoRoutine)propBasicQueryMemoryPartition; break; + case ObjectTypeRegistryTransaction: + propQueryInfoRoutine = (pfnPropQueryInfoRoutine)propBasicQueryRegistryTransaction; + break; case ObjectTypeProcess: propQueryInfoRoutine = (pfnPropQueryInfoRoutine)propBasicQueryProcess; break; diff --git a/Source/WinObjEx64/props/propBasic.h b/Source/WinObjEx64/props/propBasic.h deleted file mode 100644 index c0d42b29..00000000 --- a/Source/WinObjEx64/props/propBasic.h +++ /dev/null @@ -1,25 +0,0 @@ -/******************************************************************************* -* -* (C) COPYRIGHT AUTHORS, 2015 - 2021 -* -* TITLE: PROPBASIC.H -* -* VERSION: 1.90 -* -* DATE: 11 May 2021 -* -* Common header file for Basic property sheet. -* -* THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF -* ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED -* TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A -* PARTICULAR PURPOSE. -* -*******************************************************************************/ -#pragma once - -INT_PTR CALLBACK BasicPropDialogProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam); diff --git a/Source/WinObjEx64/props/propBasicConsts.h b/Source/WinObjEx64/props/propBasicConsts.h index 4d32603e..4e997e5d 100644 --- a/Source/WinObjEx64/props/propBasicConsts.h +++ b/Source/WinObjEx64/props/propBasicConsts.h @@ -1,12 +1,12 @@ /******************************************************************************* * -* (C) COPYRIGHT AUTHORS, 2015 - 2021 +* (C) COPYRIGHT AUTHORS, 2015 - 2022 * * TITLE: PROPBASICCONSTS.H * -* VERSION: 1.90 +* VERSION: 2.00 * -* DATE: 11 May 2021 +* DATE: 19 Jun 2022 * * Consts header file for Basic property sheet. * diff --git a/Source/WinObjEx64/props/propCommon.h b/Source/WinObjEx64/props/propCommon.h new file mode 100644 index 00000000..65a481ff --- /dev/null +++ b/Source/WinObjEx64/props/propCommon.h @@ -0,0 +1,118 @@ +/******************************************************************************* +* +* (C) COPYRIGHT AUTHORS, 2022 +* +* TITLE: PROPCOMMON.H +* +* VERSION: 2.00 +* +* DATE: 19 Jun 2022 +* +* Common header file for the property sheet based dialogs. +* +* THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF +* ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED +* TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A +* PARTICULAR PURPOSE. +* +*******************************************************************************/ + +#pragma once + +typedef enum _PROP_CONTEXT_TYPE { + propNormal = 0, + propPrivateNamespace = 1, + propUnnamed = 2, + propMax = 3 +} PROP_CONTEXT_TYPE; + +typedef struct _PROP_NAMESPACE_INFO { + ULONG Reserved; + ULONG SizeOfBoundaryDescriptor; + OBJECT_BOUNDARY_DESCRIPTOR* BoundaryDescriptor; + ULONG_PTR ObjectAddress; +} PROP_NAMESPACE_INFO, * PPROP_NAMESPACE_INFO; + +typedef struct _PROP_UNNAMED_OBJECT_INFO { + BOOL IsThreadToken; + ULONG_PTR ObjectAddress; + CLIENT_ID ClientId; + SYSTEM_THREAD_INFORMATION ThreadInformation; + UNICODE_STRING ImageName; +} PROP_UNNAMED_OBJECT_INFO, * PPROP_UNNAMED_OBJECT_INFO; + +typedef struct _PROP_OBJECT_INFO { + + PROP_CONTEXT_TYPE ContextType; + WOBJ_OBJECT_TYPE ObjectTypeIndex; + + // + // Object specific flags + // + DWORD ObjectFlags; + + // + // Unicode strings for object name/path where used. + // + UNICODE_STRING NtObjectName; + UNICODE_STRING NtObjectPath; + + // + // Context specific data. + // + PVOID ExtrasContext; + + // + // Reference to object type description entry in global array. + // + WOBJ_TYPE_DESC* TypeDescription; + WOBJ_TYPE_DESC* ShadowTypeDescription; //valid only for types, same as TypeDescription for everything else. + + // + // Icons assigned during runtime. + // + HICON ObjectIcon; + HICON ObjectTypeIcon; + + OBEX_OBJECT_INFORMATION ObjectInfo; //object dump related structures + + // + // Private namespace or unnamed object (process/thread/token) information. + // + union { + PROP_NAMESPACE_INFO NamespaceInfo; + PROP_UNNAMED_OBJECT_INFO UnnamedObjectInfo; + } u1; + +} PROP_OBJECT_INFO, * PPROP_OBJECT_INFO; + +typedef struct _PROP_CONFIG { + PROP_CONTEXT_TYPE ContextType; + HWND hwndParent; + + WOBJ_OBJECT_TYPE ObjectTypeIndex; + + PUNICODE_STRING NtObjectName; + PUNICODE_STRING NtObjectPath; + + union { + PVOID ObjectData; + union { + PROP_NAMESPACE_INFO* NamespaceObject; + PROP_UNNAMED_OBJECT_INFO* UnnamedObject; + } u1; + }; +} PROP_CONFIG, * PPROP_CONFIG; + +//open object method (propOpenCurrentObject) +typedef BOOL(CALLBACK* POPENOBJECTMETHOD)( + _In_ PROP_OBJECT_INFO* Context, + _Inout_ PHANDLE phObject, + _In_ ACCESS_MASK DesiredAccess + ); + +//close object method (propCloseCurrentObject) +typedef VOID(CALLBACK* PCLOSEOBJECTMETHOD)( + _In_ PROP_OBJECT_INFO* Context, + _In_ HANDLE hObject + ); diff --git a/Source/WinObjEx64/props/propDesktop.c b/Source/WinObjEx64/props/propDesktop.c index 61d05089..c58ff365 100644 --- a/Source/WinObjEx64/props/propDesktop.c +++ b/Source/WinObjEx64/props/propDesktop.c @@ -4,9 +4,9 @@ * * TITLE: PROPDESKTOP.C * -* VERSION: 1.93 +* VERSION: 2.00 * -* DATE: 11 May 2022 +* DATE: 19 Jun 2022 * * THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF * ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED @@ -37,30 +37,28 @@ BOOL CALLBACK DesktopListEnumProc( _In_ LPARAM lParam ) { - BOOL bSucc; - INT nIndex; - DWORD bytesNeeded, dwDesktopHeapSize; + BOOL bSucc; + INT nIndex; + DWORD bytesNeeded, dwDesktopHeapSize; LPWSTR lpName, StringSid; - PSID pSID; + PSID pSID; SIZE_T sz; - HDESK hDesktop; + HDESK hDesktop; LVITEM lvitem; - WCHAR szBuffer[MAX_PATH]; + WCHAR szHeap[64]; DLG_ENUM_CALLBACK_CONTEXT* enumParam = (DLG_ENUM_CALLBACK_CONTEXT*)lParam; if (enumParam == NULL) { return FALSE; } - // Desktop\\Object+0 - sz = (3 + _strlen(lpszDesktop) + _strlen(enumParam->ObjectContext->lpObjectName)) * sizeof(WCHAR); + // Object + sz = (1 + _strlen(lpszDesktop)) * sizeof(WCHAR); lpName = (LPWSTR)supHeapAlloc(sz); if (lpName == NULL) return 0; - _strcpy(lpName, enumParam->ObjectContext->lpObjectName); - _strcat(lpName, TEXT("\\")); - _strcat(lpName, lpszDesktop); + _strcpy(lpName, lpszDesktop); //Name RtlSecureZeroMemory(&lvitem, sizeof(lvitem)); @@ -126,10 +124,10 @@ BOOL CALLBACK DesktopListEnumProc( &bytesNeeded)) { if (dwDesktopHeapSize) { - RtlSecureZeroMemory(szBuffer, sizeof(szBuffer)); - ultostr(dwDesktopHeapSize / 1024, szBuffer); - _strcat(szBuffer, TEXT(" Mb")); - lvitem.pszText = szBuffer; + szHeap[0] = 0; + ultostr(dwDesktopHeapSize / 1024, szHeap); + _strcat(szHeap, TEXT(" Mb")); + lvitem.pszText = szHeap; } else { lvitem.pszText = T_EmptyString; @@ -296,15 +294,15 @@ VOID DesktopListShowProperties( ) { EXTRASCONTEXT* pDlgContext; - SIZE_T ItemTextSize, i, l; - LPWSTR lpName, lpItemText; + LPWSTR lpName; + UNICODE_STRING usObjectName; - PROP_DIALOG_CREATE_SETTINGS propSettings; + PROP_CONFIG propConfig; // // Allow only one dialog at same time. // - ENSURE_DIALOG_UNIQUE(g_DesktopPropWindow); + supCloseKnownPropertiesDialog(propGetDesktopWindow()); // // A very basic support for this type. @@ -313,28 +311,24 @@ VOID DesktopListShowProperties( pDlgContext = (EXTRASCONTEXT*)GetProp(hwndDlg, T_DLGCONTEXT); if (pDlgContext) { - ItemTextSize = 0; - lpItemText = supGetItemText( + lpName = supGetItemText( pDlgContext->ListView, ListView_GetSelectionMark(pDlgContext->ListView), 0, - &ItemTextSize); + NULL); - if (lpItemText) { - l = 0; - for (i = 0; i < ItemTextSize / sizeof(WCHAR); i++) - if (lpItemText[i] == L'\\') - l = i + 1; - lpName = &lpItemText[l]; + if (lpName) { - RtlSecureZeroMemory(&propSettings, sizeof(propSettings)); - propSettings.hwndParent = hwndDlg; - propSettings.lpObjectName = lpName; - propSettings.lpObjectType = OBTYPE_NAME_DESKTOP; + RtlInitUnicodeString(&usObjectName, lpName); - propCreateDialog(&propSettings); + RtlSecureZeroMemory(&propConfig, sizeof(propConfig)); + propConfig.hwndParent = hwndDlg; + propConfig.NtObjectName = &usObjectName; + propConfig.ObjectTypeIndex = ObjectTypeDesktop; - supHeapFree(lpItemText); + propCreateDialog(&propConfig); + + supHeapFree(lpName); } } } diff --git a/Source/WinObjEx64/props/propDesktop.h b/Source/WinObjEx64/props/propDesktop.h deleted file mode 100644 index 91ea8233..00000000 --- a/Source/WinObjEx64/props/propDesktop.h +++ /dev/null @@ -1,25 +0,0 @@ -/******************************************************************************* -* -* (C) COPYRIGHT AUTHORS, 2015 - 2018 -* -* TITLE: PROPDESKTOP.H -* -* VERSION: 1.52 -* -* DATE: 08 Jan 2018 -* -* Common header file for Desktops property sheet. -* -* THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF -* ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED -* TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A -* PARTICULAR PURPOSE. -* -*******************************************************************************/ -#pragma once - -INT_PTR CALLBACK DesktopListDialogProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam); diff --git a/Source/WinObjEx64/props/propDlg.c b/Source/WinObjEx64/props/propDlg.c index b6096b5a..4469dfd8 100644 --- a/Source/WinObjEx64/props/propDlg.c +++ b/Source/WinObjEx64/props/propDlg.c @@ -4,9 +4,9 @@ * * TITLE: PROPDLG.C * -* VERSION: 1.94 +* VERSION: 2.00 * -* DATE: 06 Jun 2022 +* DATE: 19 Jun 2022 * * THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF * ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED @@ -15,19 +15,10 @@ * *******************************************************************************/ #include "global.h" -#include "propAlpcPort.h" -#include "propBasic.h" -#include "propDesktop.h" -#include "propDriver.h" -#include "propObjectDump.h" -#include "propProcess.h" -#include "propSection.h" -#include "propSecurity.h" -#include "propToken.h" -#include "propType.h" +#include "props.h" //previously focused window -HWND hPrevFocus; +HWND PreviousFocus = NULL; //maximum number of possible pages, include space reserved for future use #define MAX_PAGE 10 @@ -37,11 +28,42 @@ HPROPSHEETPAGE PropPages[MAX_PAGE]; WNDPROC PropSheetOriginalWndProc = NULL; //handle to the PropertySheet window -HWND g_PropWindow = NULL; -HWND g_PsPropWindow = NULL; -HWND g_PsTokenWindow = NULL; -HWND g_DesktopPropWindow = NULL; -HWND g_NamespacePropWindow = NULL; +HWND CommonPropWindow = NULL; +HWND ProcessesPropWindow = NULL; +HWND ThreadsPropWindow = NULL; +HWND TokenPropWindow = NULL; +HWND DesktopPropWindow = NULL; +HWND NamespacePropWindow = NULL; + +HWND propGetCommonWindow() +{ + return CommonPropWindow; +} + +HWND propGetProcessesWindow() +{ + return ProcessesPropWindow; +} + +HWND propGetThreadsWindow() +{ + return ThreadsPropWindow; +} + +HWND propGetTokenWindow() +{ + return TokenPropWindow; +} + +HWND propGetDesktopWindow() +{ + return DesktopPropWindow; +} + +HWND propGetNamespaceWindow() +{ + return NamespacePropWindow; +} /* * propCloseCurrentObject @@ -59,6 +81,33 @@ BOOL propCloseCurrentObject( return supCloseObjectFromContext(Context, hObject); } +/* +* propIsUnsupportedTypeForOpen +* +* Purpose: +* +* Filter object opening by type as we cannot open everything. +* +*/ +BOOL propIsUnsupportedTypeForOpen( + _In_ WOBJ_OBJECT_TYPE TypeIndex +) +{ + WOBJ_OBJECT_TYPE propUnsupportedTypes[] = { + ObjectTypeUnknown, + ObjectTypeFltConnPort, + ObjectTypeFltComnPort, + ObjectTypeWaitablePort + }; + + ULONG i; + for (i = 0; i < RTL_NUMBER_OF(propUnsupportedTypes); i++) + if (TypeIndex == propUnsupportedTypes[i]) + return TRUE; + + return FALSE; +} + /* * propOpenCurrentObject * @@ -67,66 +116,52 @@ BOOL propCloseCurrentObject( * Opens currently viewed object depending on type * */ +_Success_(return) BOOL propOpenCurrentObject( _In_ PROP_OBJECT_INFO* Context, _Out_ PHANDLE phObject, _In_ ACCESS_MASK DesiredAccess ) { - BOOL bResult; - HANDLE hObject, hDirectory; - NTSTATUS status; - UNICODE_STRING ustr; - OBJECT_ATTRIBUTES obja; + BOOL bResult; + HANDLE hObject, hDirectory; + NTSTATUS status; + OBJECT_ATTRIBUTES obja; bResult = FALSE; - *phObject = NULL; - // // Filter unsupported types. // - if ( - (Context->TypeIndex == ObjectTypeUnknown) || - (Context->TypeIndex == ObjectTypeFltConnPort) || - (Context->TypeIndex == ObjectTypeFltComnPort) || - (Context->TypeIndex == ObjectTypeWaitablePort) - ) - { + if (propIsUnsupportedTypeForOpen(Context->ObjectTypeIndex)) { SetLastError(ERROR_UNSUPPORTED_TYPE); - return bResult; + return FALSE; } // // Handle window station type. // - if (Context->TypeIndex == ObjectTypeWinstation) { + if (Context->ObjectTypeIndex == ObjectTypeWinstation) { hObject = supOpenWindowStationFromContext(Context, FALSE, DesiredAccess); //WINSTA_READATTRIBUTES for query bResult = (hObject != NULL); if (bResult) { *phObject = hObject; - SetLastError(ERROR_SUCCESS); - } - else { - SetLastError(ERROR_ACCESS_DENIED); } + return bResult; } // // Handle desktop type. // - if (Context->TypeIndex == ObjectTypeDesktop) { - if (Context->lpObjectName == NULL) { - SetLastError(ERROR_INVALID_PARAMETER); - return bResult; - } - hObject = OpenDesktop(Context->lpObjectName, 0, FALSE, DesiredAccess); //DESKTOP_READOBJECTS for query + if (Context->ObjectTypeIndex == ObjectTypeDesktop) { + + hObject = OpenDesktop(Context->NtObjectName.Buffer, 0, FALSE, DesiredAccess); //DESKTOP_READOBJECTS for query bResult = (hObject != NULL); if (bResult) { *phObject = hObject; - SetLastError(ERROR_SUCCESS); } + return bResult; } @@ -155,13 +190,10 @@ BOOL propOpenCurrentObject( // Namespace objects must be handled in a special way. // if (Context->ContextType == propPrivateNamespace) { - if (Context->lpObjectName == NULL) { - SetLastError(ERROR_INVALID_PARAMETER); - return bResult; - } - RtlInitUnicodeString(&ustr, Context->lpObjectName); - InitializeObjectAttributes(&obja, &ustr, OBJ_CASE_INSENSITIVE, NULL, NULL); + InitializeObjectAttributes(&obja, &Context->NtObjectName, + OBJ_CASE_INSENSITIVE, NULL, NULL); + hObject = supOpenObjectFromContext( Context, &obja, @@ -176,14 +208,6 @@ BOOL propOpenCurrentObject( return bResult; } - if ((Context->lpObjectName == NULL) || - (Context->lpCurrentObjectPath == NULL) - ) - { - SetLastError(ERROR_INVALID_PARAMETER); - return bResult; - } - hDirectory = NULL; if (DesiredAccess == 0) { @@ -193,16 +217,16 @@ BOOL propOpenCurrentObject( // // Handle directory type. // - if (Context->TypeIndex == ObjectTypeDirectory) { + if (Context->ObjectTypeIndex == ObjectTypeDirectory) { // // If this is root, then root hDirectory = NULL. // - if (_strcmpi(Context->lpObjectName, KM_OBJECTS_ROOT_DIRECTORY) != 0) { + if (!supIsRootDirectory(&Context->NtObjectName)) { // // Otherwise open directory that keep this object. // - supOpenDirectoryForObject(&hDirectory, Context->lpObjectName, Context->lpCurrentObjectPath); + supOpenDirectoryEx(&hDirectory, NULL, &Context->NtObjectPath, DIRECTORY_QUERY); if (hDirectory == NULL) { SetLastError(ERROR_OBJECT_NOT_FOUND); return bResult; @@ -213,8 +237,9 @@ BOOL propOpenCurrentObject( // Open object in directory. // - status = supOpenDirectory(&hObject, hDirectory, - Context->lpObjectName, + status = supOpenDirectoryEx(&hObject, + hDirectory, + &Context->NtObjectName, DesiredAccess); if (!NT_SUCCESS(status)) { @@ -237,14 +262,13 @@ BOOL propOpenCurrentObject( // // Open directory which current object belongs. // - supOpenDirectoryForObject(&hDirectory, Context->lpObjectName, Context->lpCurrentObjectPath); + supOpenDirectoryEx(&hDirectory, NULL, &Context->NtObjectPath, DIRECTORY_QUERY); if (hDirectory == NULL) { SetLastError(ERROR_OBJECT_NOT_FOUND); return bResult; } - RtlInitUnicodeString(&ustr, Context->lpObjectName); - InitializeObjectAttributes(&obja, &ustr, OBJ_CASE_INSENSITIVE, hDirectory, NULL); + InitializeObjectAttributes(&obja, &Context->NtObjectName, OBJ_CASE_INSENSITIVE, hDirectory, NULL); status = STATUS_UNSUCCESSFUL; hObject = NULL; @@ -277,97 +301,92 @@ BOOL propOpenCurrentObject( * */ PPROP_OBJECT_INFO propContextCreate( - _In_opt_ LPWSTR lpObjectName, - _In_opt_ LPCWSTR lpObjectType, - _In_opt_ LPWSTR lpCurrentObjectPath, - _In_opt_ LPWSTR lpDescription + _In_ PROP_CONFIG* Config ) { - BOOL bSelectedObject = FALSE, bSelectedDirectory = FALSE; - PROP_OBJECT_INFO* Context; + PROP_OBJECT_INFO* propContext; - __try { - // - // Allocate context structure. - // - Context = (PROP_OBJECT_INFO*)supHeapAlloc(sizeof(PROP_OBJECT_INFO)); - if (Context == NULL) - return NULL; + union { + PVOID Ref; + union { + PROP_NAMESPACE_INFO* NamespaceObject; + PROP_UNNAMED_OBJECT_INFO* UnnamedObject; + }; + } ObjectRef; - Context->TypeDescription = ObManagerGetEntryByTypeName(lpObjectType); + // + // Allocate context structure. + // + propContext = (PROP_OBJECT_INFO*)supHeapAlloc(sizeof(PROP_OBJECT_INFO)); + if (propContext == NULL) + return NULL; + + propContext->ObjectTypeIndex = Config->ObjectTypeIndex; + + // + // Copy object name if given. + // + if (Config->NtObjectName) { + supDuplicateUnicodeString(g_obexHeap, &propContext->NtObjectName, Config->NtObjectName); + } + + // + // Copy object path if given because dialog is modeless. + // + if (Config->NtObjectPath) { + supDuplicateUnicodeString(g_obexHeap, &propContext->NtObjectPath, Config->NtObjectPath); + } + + propContext->TypeDescription = ObManagerGetEntryByTypeIndex(propContext->ObjectTypeIndex); + // + // Check if object is Type object. + // Type objects handled differently. + // + if (propContext->ObjectTypeIndex == ObjectTypeType) { + propContext->ShadowTypeDescription = ObManagerGetEntryByTypeName(propContext->NtObjectName.Buffer); + } + else { // // Use the same type descriptor by default for shadow. // - Context->ShadowTypeDescription = Context->TypeDescription; + propContext->ShadowTypeDescription = propContext->TypeDescription; + } - // - // Copy object name if given. - // - if (lpObjectName) { + // + // Remember namespace or unnamed object info. + // Always last. + // + ObjectRef.Ref = Config->ObjectData; - Context->lpObjectName = (LPWSTR)supHeapAlloc((1 + _strlen(lpObjectName)) * sizeof(WCHAR)); - if (Context->lpObjectName) { - _strcpy(Context->lpObjectName, lpObjectName); - bSelectedObject = (_strcmpi(Context->lpObjectName, TEXT("ObjectTypes")) == 0); - } - } + if (Config->ContextType == propPrivateNamespace) { - // - // Copy object type if given. - // - if (lpObjectType) { - Context->lpObjectType = (LPWSTR)supHeapAlloc((1 + _strlen(lpObjectType)) * sizeof(WCHAR)); - if (Context->lpObjectType) { - _strcpy(Context->lpObjectType, lpObjectType); - } - Context->TypeIndex = ObManagerGetIndexByTypeName(lpObjectType); - } - else { - Context->TypeIndex = ObjectTypeUnknown; - } + propContext->ContextType = propPrivateNamespace; + propContext->u1.NamespaceInfo = *ObjectRef.NamespaceObject; - // - // Copy CurrentObjectPath if given, as it can change because dialog is modeless. - // - if (lpCurrentObjectPath) { - Context->lpCurrentObjectPath = (LPWSTR)supHeapAlloc((1 + _strlen(lpCurrentObjectPath)) * sizeof(WCHAR)); - if (Context->lpCurrentObjectPath) { - _strcpy(Context->lpCurrentObjectPath, lpCurrentObjectPath); - bSelectedDirectory = (_strcmpi(Context->lpCurrentObjectPath, T_OBJECTTYPES) == 0); - } - } + } + else if (Config->ContextType == propUnnamed) { + propContext->ContextType = propUnnamed; // - // Copy object description, could be NULL. + // Copy generic data. // - if (lpDescription) { - Context->lpDescription = (LPWSTR)supHeapAlloc((1 + _strlen(lpDescription)) * sizeof(WCHAR)); - if (Context->lpDescription) { - _strcpy(Context->lpDescription, lpDescription); - } + propContext->u1.UnnamedObjectInfo.ObjectAddress = ObjectRef.UnnamedObject->ObjectAddress; + propContext->u1.UnnamedObjectInfo.ClientId = ObjectRef.UnnamedObject->ClientId; + if (propContext->ObjectTypeIndex == ObjectTypeThread) { + propContext->u1.UnnamedObjectInfo.ThreadInformation = ObjectRef.UnnamedObject->ThreadInformation; } // - // Check if object is Type object. - // Type objects handled differently. + // Copy image name if present. // - if ((bSelectedObject == FALSE) && (bSelectedDirectory != FALSE)) { - Context->IsType = TRUE; - // - // Query actual type index for case when user will browse Type object info. - // - if (Context->lpObjectName) { - Context->ShadowTypeDescription = ObManagerGetEntryByTypeName(Context->lpObjectName); - } - - } + supDuplicateUnicodeString(g_obexHeap, + &propContext->u1.UnnamedObjectInfo.ImageName, + &ObjectRef.UnnamedObject->ImageName); } - __except (WOBJ_EXCEPTION_FILTER_LOG) { - return NULL; - } - return Context; + + return propContext; } /* @@ -382,50 +401,68 @@ VOID propContextDestroy( _In_ PROP_OBJECT_INFO* Context ) { - __try { - - //free associated icons - supDestroyIconForObjectType(Context); - - //free name - if (Context->lpObjectName) { - supHeapFree(Context->lpObjectName); - } - //free type - if (Context->lpObjectType) { - supHeapFree(Context->lpObjectType); + //free associated icons + if (Context->ObjectTypeIndex == ObjectTypeType) { + if (Context->ObjectTypeIcon) { + DestroyIcon(Context->ObjectTypeIcon); } - //free currentobjectpath - if (Context->lpCurrentObjectPath) { - supHeapFree(Context->lpCurrentObjectPath); - } - //free description - if (Context->lpDescription) { - supHeapFree(Context->lpDescription); - } - //free boundary descriptor - if (Context->ContextType == propPrivateNamespace) { - if (Context->NamespaceInfo.BoundaryDescriptor) { - supHeapFree(Context->NamespaceInfo.BoundaryDescriptor); - } + } + if (Context->ObjectIcon) { + DestroyIcon(Context->ObjectIcon); + } + + //free boundary descriptor + if (Context->ContextType == propPrivateNamespace) { + if (Context->u1.NamespaceInfo.BoundaryDescriptor) { + supHeapFree(Context->u1.NamespaceInfo.BoundaryDescriptor); } + } + else if (Context->ContextType == propUnnamed) { //free unnamed object info - if (Context->ContextType == propUnnamed) { - if (Context->UnnamedObjectInfo.ImageName.Buffer) - supHeapFree(Context->UnnamedObjectInfo.ImageName.Buffer); - } + supFreeDuplicatedUnicodeString(g_obexHeap, &Context->u1.UnnamedObjectInfo.ImageName, FALSE); + } - if (Context->PortObjectInfo.IsAllocated) { - if (Context->PortObjectInfo.ReferenceHandle) - NtClose(Context->PortObjectInfo.ReferenceHandle); - } + supFreeDuplicatedUnicodeString(g_obexHeap, &Context->NtObjectName, FALSE); + supFreeDuplicatedUnicodeString(g_obexHeap, &Context->NtObjectPath, FALSE); - //free context itself - supHeapFree(Context); + //free context itself + supHeapFree(Context); +} +VOID propSetSharedHwnd( + _In_ HWND hwnd +) +{ + if (hwnd == TokenPropWindow) { + TokenPropWindow = NULL; } - __except (WOBJ_EXCEPTION_FILTER) { - return; + else if (hwnd == ProcessesPropWindow) { + if (TokenPropWindow) { + TokenPropWindow = NULL; + } + if (ThreadsPropWindow) { + ThreadsPropWindow = NULL; + } + ProcessesPropWindow = NULL; + } + else if (hwnd == ThreadsPropWindow) { + ThreadsPropWindow = NULL; + } + else if (hwnd == NamespacePropWindow) { + NamespacePropWindow = NULL; + } + else if (hwnd == DesktopPropWindow) { + DesktopPropWindow = NULL; + } + if (hwnd == CommonPropWindow) { + if (DesktopPropWindow) { + DesktopPropWindow = NULL; + } + //restore previous focus + if (PreviousFocus && IsWindow(PreviousFocus)) { + SetFocus(PreviousFocus); + } + CommonPropWindow = NULL; } } @@ -464,30 +501,8 @@ LRESULT WINAPI PropSheetCustomWndProc( break; case WM_CLOSE: - if (hwnd == g_PsTokenWindow) { - g_PsTokenWindow = NULL; - } - else if (hwnd == g_PsPropWindow) { - g_PsPropWindow = NULL; - } - else if (hwnd == g_NamespacePropWindow) { - g_NamespacePropWindow = NULL; - } - else if (hwnd == g_DesktopPropWindow) { - g_DesktopPropWindow = NULL; - } - if (hwnd == g_PropWindow) { - if (g_DesktopPropWindow) { - g_DesktopPropWindow = NULL; - } - //restore previous focus - if (hPrevFocus && IsWindow(hPrevFocus)) { - SetFocus(hPrevFocus); - } - g_PropWindow = NULL; - } - - return DestroyWindow(hwnd); + propSetSharedHwnd(hwnd); + DestroyWindow(hwnd); break; case WM_COMMAND: @@ -496,83 +511,10 @@ LRESULT WINAPI PropSheetCustomWndProc( return TRUE; } break; - default: - break; - } - return CallWindowProc(PropSheetOriginalWndProc, hwnd, Msg, wParam, lParam); -} - -/* -* propCopyNamespaceObject -* -* Purpose: -* -* Copy namespace object to the properties context. -* -*/ -VOID propCopyNamespaceObject( - _In_ PROP_OBJECT_INFO* DestinationContext, - _In_ PROP_NAMESPACE_INFO* NamespaceObject -) -{ - DestinationContext->ContextType = propPrivateNamespace; - - RtlCopyMemory( - &DestinationContext->NamespaceInfo, - NamespaceObject, - sizeof(PROP_NAMESPACE_INFO)); -} -/* -* propCopyUnnamedObject -* -* Purpose: -* -* Copy unnamed object to the properties context. -* -*/ -VOID propCopyUnnamedObject( - _In_ PROP_OBJECT_INFO* DestinationContext, - _In_ PROP_UNNAMED_OBJECT_INFO* SourceObject -) -{ - PVOID CopyBuffer; - SIZE_T CopySize; - - DestinationContext->ContextType = propUnnamed; - - // - // Copy generic data. - // - DestinationContext->UnnamedObjectInfo.ObjectAddress = SourceObject->ObjectAddress; - - RtlCopyMemory(&DestinationContext->UnnamedObjectInfo.ClientId, - &SourceObject->ClientId, - sizeof(CLIENT_ID)); - - if (DestinationContext->TypeIndex == ObjectTypeThread) { - - RtlCopyMemory(&DestinationContext->UnnamedObjectInfo.ThreadInformation, - &SourceObject->ThreadInformation, - sizeof(SYSTEM_THREAD_INFORMATION)); } - // - // Copy image name if present. - // - CopySize = SourceObject->ImageName.MaximumLength; - if (CopySize) { - CopyBuffer = supHeapAlloc(CopySize); - if (CopyBuffer) { - - DestinationContext->UnnamedObjectInfo.ImageName.MaximumLength = (USHORT)CopySize; - DestinationContext->UnnamedObjectInfo.ImageName.Buffer = (PWSTR)CopyBuffer; - - RtlCopyUnicodeString(&DestinationContext->UnnamedObjectInfo.ImageName, - &SourceObject->ImageName); - - } - } + return CallWindowProc(PropSheetOriginalWndProc, hwnd, Msg, wParam, lParam); } HPROPSHEETPAGE propAddPage( @@ -622,7 +564,7 @@ INT propCreatePages( // // Select dialog for basic info. // - switch (Context->TypeIndex) { + switch (Context->ObjectTypeIndex) { case ObjectTypeTimer: pszTemplate = MAKEINTRESOURCE(IDD_PROP_TIMER); break; @@ -687,7 +629,7 @@ INT propCreatePages( // Create Objects page for supported types. // if (IsDriverAssisted) { - switch (Context->TypeIndex) { + switch (Context->ObjectTypeIndex) { case ObjectTypeDirectory: case ObjectTypeDriver: case ObjectTypeDevice: @@ -715,8 +657,8 @@ INT propCreatePages( // // Create specific page for Process/Thread objects. // - if ((Context->TypeIndex == ObjectTypeProcess) || - (Context->TypeIndex == ObjectTypeThread)) + if ((Context->ObjectTypeIndex == ObjectTypeProcess) || + (Context->ObjectTypeIndex == ObjectTypeThread)) { PropPages[nPages++] = propAddPage( TEXT("Token"), @@ -728,7 +670,7 @@ INT propCreatePages( // // Create additional page(s), depending on object type. // - switch (Context->TypeIndex) { + switch (Context->ObjectTypeIndex) { case ObjectTypeDirectory: case ObjectTypePort: case ObjectTypeFltComnPort: @@ -743,6 +685,7 @@ INT propCreatePages( case ObjectTypeSession: case ObjectTypeIoCompletion: case ObjectTypeMemoryPartition: + case ObjectTypeRegistryTransaction: case ObjectTypeProcess: case ObjectTypeThread: case ObjectTypeWinstation: @@ -757,7 +700,9 @@ INT propCreatePages( // // Add desktop list for selected desktop, located here because of sheets order. // - if (Context->TypeIndex == ObjectTypeWinstation) { + // WinStation->Basic->Process->[Desktops]->Security + // + if (Context->ObjectTypeIndex == ObjectTypeWinstation) { PropPages[nPages++] = propAddPage( TEXT("Desktops"), @@ -787,7 +732,7 @@ INT propCreatePages( // if (g_NtBuildNumber >= NT_WIN10_THRESHOLD1 && - Context->TypeIndex == ObjectTypeSection + Context->ObjectTypeIndex == ObjectTypeSection && IsDriverAssisted) { PropPages[nPages++] = propAddPage( @@ -800,7 +745,7 @@ INT propCreatePages( // // Add ALPC port specific page, driver assistance required. // - if (Context->TypeIndex == ObjectTypePort && IsDriverAssisted) { + if (Context->ObjectTypeIndex == ObjectTypePort && IsDriverAssisted) { PropPages[nPages++] = propAddPage( TEXT("Connections"), @@ -822,10 +767,10 @@ INT propCreatePages( // Create Security Dialog if available. // hSecurityPage = propSecurityCreatePage( - Context, //Context - (POPENOBJECTMETHOD)&propOpenCurrentObject, //OpenObjectMethod - (PCLOSEOBJECTMETHOD)&propCloseCurrentObject, //CloseObjectMethod - SI_EDIT_OWNER | SI_EDIT_PERMS | //psiFlags + Context, //Context + (POPENOBJECTMETHOD)&propOpenCurrentObject, //OpenObjectMethod + (PCLOSEOBJECTMETHOD)&propCloseCurrentObject, //CloseObjectMethod + SI_EDIT_OWNER | SI_EDIT_PERMS | //psiFlags SI_ADVANCED | SI_NO_ACL_PROTECT | SI_NO_TREE_APPLY | SI_PAGE_TITLE ); @@ -847,59 +792,29 @@ INT propCreatePages( * */ VOID propCreateDialog( - _In_ PROP_DIALOG_CREATE_SETTINGS* Settings + _In_ PROP_CONFIG* Config ) { - BOOL IsSimpleContext = FALSE; - INT nPages; - HWND hwnd, topLevelOwner; + INT nPages; + HWND hwnd, topLevelOwner; PROP_OBJECT_INFO* propContext = NULL; - PROPSHEETHEADER PropHeader; - WCHAR szCaption[MAX_PATH * 2]; - - // - // Mutual exclusion situation. - // - if ((Settings->NamespaceObject != NULL) && (Settings->UnnamedObject != NULL)) - return; - - IsSimpleContext = (Settings->NamespaceObject != NULL) || (Settings->UnnamedObject != NULL); + PROPSHEETHEADER PropHeader; + WOBJ_TYPE_DESC* typeEntry; + WCHAR szCaption[MAX_PATH * 2]; // // Allocate context variable, copy name, type, object path. // - propContext = propContextCreate( - Settings->lpObjectName, - Settings->lpObjectType, - (IsSimpleContext) ? NULL : g_WinObj.CurrentObjectPath, - (IsSimpleContext) ? NULL : Settings->lpDescription); - + propContext = propContextCreate(Config); if (propContext == NULL) return; - - // - // Remember namespace or unnamed object info. - // - if (Settings->NamespaceObject) { - - propCopyNamespaceObject(propContext, - Settings->NamespaceObject); - - } - else if (Settings->UnnamedObject) { - - propCopyUnnamedObject(propContext, - Settings->UnnamedObject); - - } - // // Remember previously focused window. // Except special types: Desktop. // - if (propContext->TypeIndex != ObjectTypeDesktop) { - hPrevFocus = GetFocus(); + if (propContext->ObjectTypeIndex != ObjectTypeDesktop) { + PreviousFocus = GetFocus(); } nPages = propCreatePages(propContext); @@ -907,24 +822,25 @@ VOID propCreateDialog( // // Finally create property sheet. // - if (propContext->IsType) { - if (Settings->lpObjectName) { - _strncpy(szCaption, MAX_PATH, Settings->lpObjectName, _strlen(Settings->lpObjectName)); - } - else { - _strcpy(szCaption, TEXT("Unknown Object")); - } + if (propContext->ObjectTypeIndex == ObjectTypeType) { + + _strncpy(szCaption, + MAX_PATH, + propContext->NtObjectName.Buffer, + propContext->NtObjectName.Length / sizeof(WCHAR)); + } else { - if (Settings->lpObjectType) { - _strncpy(szCaption, MAX_PATH, Settings->lpObjectType, _strlen(Settings->lpObjectType)); + typeEntry = propContext->TypeDescription; + if (typeEntry->Index != ObjectTypeUnknown) { + _strncpy(szCaption, MAX_PATH, typeEntry->Name, _strlen(typeEntry->Name)); } else { _strcpy(szCaption, TEXT("Unknown Type")); } } - topLevelOwner = Settings->hwndParent; + topLevelOwner = Config->hwndParent; _strcat(szCaption, TEXT(" Properties")); RtlSecureZeroMemory(&PropHeader, sizeof(PropHeader)); @@ -942,27 +858,31 @@ VOID propCreateDialog( if (!hwnd) { if (topLevelOwner) EnableWindow(topLevelOwner, TRUE); + + propContextDestroy(propContext); return; } if (propContext->ContextType == propPrivateNamespace) { - g_NamespacePropWindow = hwnd; + NamespacePropWindow = hwnd; } else { - switch (propContext->TypeIndex) { + switch (propContext->ObjectTypeIndex) { case ObjectTypeProcess: + ProcessesPropWindow = hwnd; + break; case ObjectTypeThread: - g_PsPropWindow = hwnd; + ThreadsPropWindow = hwnd; break; case ObjectTypeToken: - g_PsTokenWindow = hwnd; + TokenPropWindow = hwnd; break; case ObjectTypeDesktop: - g_DesktopPropWindow = hwnd; + DesktopPropWindow = hwnd; break; default: - g_PropWindow = hwnd; + CommonPropWindow = hwnd; break; } @@ -975,5 +895,4 @@ VOID propCreateDialog( SetWindowLongPtr(hwnd, GWLP_WNDPROC, (LONG_PTR)&PropSheetCustomWndProc); } - supCenterWindow(hwnd); } diff --git a/Source/WinObjEx64/props/propDlg.h b/Source/WinObjEx64/props/propDlg.h index 3ffb4683..c0fa202d 100644 --- a/Source/WinObjEx64/props/propDlg.h +++ b/Source/WinObjEx64/props/propDlg.h @@ -1,12 +1,12 @@ /******************************************************************************* * -* (C) COPYRIGHT AUTHORS, 2015 - 2021 +* (C) COPYRIGHT AUTHORS, 2015 - 2022 * * TITLE: PROPDLG.H * -* VERSION: 1.90 +* VERSION: 2.00 * -* DATE: 11 May 2021 +* DATE: 19 Jun 2022 * * Common header file for properties dialog. * @@ -18,15 +18,14 @@ *******************************************************************************/ #pragma once -// -// Externs for global properties variables. -// -extern HWND g_PropWindow; -extern HWND g_PsTokenWindow; -extern HWND g_PsPropWindow; -extern HWND g_DesktopPropWindow; -extern HWND g_NamespacePropWindow; +HWND propGetCommonWindow(); +HWND propGetProcessesWindow(); +HWND propGetThreadsWindow(); +HWND propGetTokenWindow(); +HWND propGetDesktopWindow(); +HWND propGetNamespaceWindow(); +_Success_(return) BOOL propOpenCurrentObject( _In_ PROP_OBJECT_INFO *Context, _Out_ PHANDLE phObject, @@ -37,13 +36,10 @@ BOOL propCloseCurrentObject( _In_ HANDLE hObject); VOID propCreateDialog( - _In_ PROP_DIALOG_CREATE_SETTINGS *Settings); + _In_ PROP_CONFIG *Config); PPROP_OBJECT_INFO propContextCreate( - _In_opt_ LPWSTR lpObjectName, - _In_opt_ LPCWSTR lpObjectType, - _In_opt_ LPWSTR lpCurrentObjectPath, - _In_opt_ LPWSTR lpDescription); + _In_ PROP_CONFIG* Config); VOID propContextDestroy( _In_ PROP_OBJECT_INFO *Context); diff --git a/Source/WinObjEx64/props/propDriver.c b/Source/WinObjEx64/props/propDriver.c index 454e0a9b..d3858526 100644 --- a/Source/WinObjEx64/props/propDriver.c +++ b/Source/WinObjEx64/props/propDriver.c @@ -1,12 +1,12 @@ /******************************************************************************* * -* (C) COPYRIGHT AUTHORS, 2015 - 2021 +* (C) COPYRIGHT AUTHORS, 2015 - 2022 * * TITLE: PROPDRIVER.C * -* VERSION: 1.90 +* VERSION: 2.00 * -* DATE: 16 May 2021 +* DATE: 19 Jun 2022 * * THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF * ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED @@ -15,8 +15,7 @@ * *******************************************************************************/ #include "global.h" -#include "supConsts.h" -#include "propObjectDump.h" +#include "props.h" #define REGEDITWNDCLASS L"RegEdit_RegEdit" #define REGEDIT_EXE L"regedit.exe" @@ -86,7 +85,7 @@ VOID DriverSetInfo( schService = OpenService( SchSCManager, - Context->lpObjectName, + Context->NtObjectName.Buffer, SERVICE_QUERY_CONFIG | SERVICE_QUERY_STATUS | SERVICE_ENUMERATE_DEPENDENTS); if (schService == NULL) @@ -114,7 +113,7 @@ VOID DriverSetInfo( bResult = QueryServiceConfig(schService, psci, bytesNeeded, &bytesNeeded); if (bResult) { //set key name (identical to object name) - SetDlgItemText(hwndDlg, IDC_SERVICE_KEYNAME, Context->lpObjectName); + SetDlgItemText(hwndDlg, IDC_SERVICE_KEYNAME, Context->NtObjectName.Buffer); //set image path info SetDlgItemText(hwndDlg, IDC_SERVICE_IMAGEPATH, psci->lpBinaryPathName); //set display name @@ -459,9 +458,13 @@ VOID DriverJumpToKey( WCHAR szBuffer[MAX_PATH * 2]; + // + // NtObjectName does not require normalization because regedit cannot handle bogus names anyway. + // + do { - sz = _strlen(Context->lpObjectName); + sz = _strlen(Context->NtObjectName.Buffer); if (sz == 0) break; @@ -475,7 +478,7 @@ VOID DriverJumpToKey( break; _strcpy(lpRegPath, PROPDRVREGSERVICESKEY); - _strcat(lpRegPath, Context->lpObjectName); + _strcat(lpRegPath, Context->NtObjectName.Buffer); // // Start RegEdit. diff --git a/Source/WinObjEx64/props/propDriver.h b/Source/WinObjEx64/props/propDriver.h deleted file mode 100644 index e4afb33b..00000000 --- a/Source/WinObjEx64/props/propDriver.h +++ /dev/null @@ -1,25 +0,0 @@ -/******************************************************************************* -* -* (C) COPYRIGHT AUTHORS, 2015 - 2018 -* -* TITLE: PROPDRIVER.H -* -* VERSION: 1.52 -* -* DATE: 08 Jan 2018 -* -* Common header file for Driver object information page. -* -* THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF -* ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED -* TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A -* PARTICULAR PURPOSE. -* -*******************************************************************************/ -#pragma once - -INT_PTR CALLBACK DriverRegistryDialogProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam); diff --git a/Source/WinObjEx64/props/propObjectDump.c b/Source/WinObjEx64/props/propObjectDump.c index 00a485a0..b7075eb1 100644 --- a/Source/WinObjEx64/props/propObjectDump.c +++ b/Source/WinObjEx64/props/propObjectDump.c @@ -4,9 +4,9 @@ * * TITLE: PROPOBJECTDUMP.C * -* VERSION: 1.94 +* VERSION: 2.00 * -* DATE: 07 Jun 2022 +* DATE: 19 Jun 2022 * * THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF * ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED @@ -99,8 +99,8 @@ HTREEITEM propObDumpAddress( _In_ COLORREF FontColor ) { - TL_SUBITEMS_FIXED subitems; - WCHAR szValue[DUMP_CONVERSION_LENGTH + 1]; + TL_SUBITEMS_FIXED subitems; + WCHAR szValue[32]; RtlSecureZeroMemory(&subitems, sizeof(subitems)); subitems.Count = 2; @@ -151,15 +151,15 @@ HTREEITEM propObDumpAddress( VOID propObDumpAddressWithModule( _In_ HWND TreeList, _In_ HTREEITEM hParent, - _In_ LPWSTR lpszName, + _In_ LPWSTR Name, _In_opt_ PVOID Address, _In_ PRTL_PROCESS_MODULES pModules, _In_opt_ PVOID SelfDriverBase, _In_ ULONG SelfDriverSize ) { - TL_SUBITEMS_FIXED subitems; - WCHAR szValue[DUMP_CONVERSION_LENGTH + 1], szModuleName[MAX_PATH * 2]; + TL_SUBITEMS_FIXED subitems; + WCHAR szValue[32], szModuleName[MAX_PATH * 2]; RtlSecureZeroMemory(&subitems, sizeof(subitems)); subitems.Count = 2; @@ -201,7 +201,7 @@ VOID propObDumpAddressWithModule( TVIF_TEXT | TVIF_STATE, 0, 0, - lpszName, + Name, &subitems); } @@ -262,8 +262,8 @@ VOID propObDumpByte( _In_ BOOL IsBool ) { - TL_SUBITEMS_FIXED subitems; - WCHAR szValue[DUMP_CONVERSION_LENGTH + 1]; + TL_SUBITEMS_FIXED subitems; + WCHAR szValue[32]; RtlSecureZeroMemory(&subitems, sizeof(subitems)); @@ -280,7 +280,7 @@ VOID propObDumpByte( else { RtlStringCchPrintfSecure(szValue, - DUMP_CONVERSION_LENGTH, + RTL_NUMBER_OF(szValue), FORMAT_HEXBYTE, Value); @@ -382,7 +382,7 @@ HTREEITEM propObDumpUlong( ) { TL_SUBITEMS_FIXED subitems; - WCHAR szValue[DUMP_CONVERSION_LENGTH + 1]; + WCHAR szValue[32]; RtlSecureZeroMemory(&szValue, sizeof(szValue)); RtlSecureZeroMemory(&subitems, sizeof(subitems)); @@ -399,7 +399,7 @@ HTREEITEM propObDumpUlong( if (IsUShort) { RtlStringCchPrintfSecure(szValue, - DUMP_CONVERSION_LENGTH, + RTL_NUMBER_OF(szValue), FORMAT_HEXUSHORT, Value); @@ -414,7 +414,7 @@ HTREEITEM propObDumpUlong( if (IsUShort) { RtlStringCchPrintfSecure(szValue, - DUMP_CONVERSION_LENGTH, + RTL_NUMBER_OF(szValue), FORMAT_USHORT, Value); @@ -463,8 +463,8 @@ HTREEITEM propObDumpLong( _In_ COLORREF FontColor ) { - TL_SUBITEMS_FIXED subitems; - WCHAR szValue[DUMP_CONVERSION_LENGTH + 1]; + TL_SUBITEMS_FIXED subitems; + WCHAR szValue[32]; RtlSecureZeroMemory(&szValue, sizeof(szValue)); RtlSecureZeroMemory(&subitems, sizeof(subitems)); @@ -479,7 +479,7 @@ HTREEITEM propObDumpLong( if (HexDump) { RtlStringCchPrintfSecure(szValue, - DUMP_CONVERSION_LENGTH, + RTL_NUMBER_OF(szValue), FORMAT_HEXLONG, Value); } else { @@ -527,8 +527,8 @@ VOID propObDumpUlong64( _In_ COLORREF FontColor ) { - TL_SUBITEMS_FIXED subitems; - WCHAR szValue[DUMP_CONVERSION_LENGTH + 1]; + TL_SUBITEMS_FIXED subitems; + WCHAR szValue[32]; RtlSecureZeroMemory(&subitems, sizeof(subitems)); subitems.Count = 2; @@ -589,8 +589,8 @@ VOID propObDumpLong64( _In_ COLORREF FontColor ) { - TL_SUBITEMS_FIXED subitems; - WCHAR szValue[DUMP_CONVERSION_LENGTH + 1]; + TL_SUBITEMS_FIXED subitems; + WCHAR szValue[32]; RtlSecureZeroMemory(&subitems, sizeof(subitems)); subitems.Count = 2; @@ -648,7 +648,7 @@ HTREEITEM propObAddHexValue( _In_ BOOL AsPointer ) { - WCHAR szValue[DUMP_CONVERSION_LENGTH + 1]; + WCHAR szValue[32]; TL_SUBITEMS_FIXED subitems; RtlSecureZeroMemory(&subitems, sizeof(subitems)); @@ -782,9 +782,9 @@ VOID propObDumpUSHORT( _In_ BOOLEAN HexOutput ) { - LPCWSTR lpFormat; - TL_SUBITEMS_FIXED subitems; - WCHAR szValue[DUMP_CONVERSION_LENGTH + 1]; + LPCWSTR lpFormat; + TL_SUBITEMS_FIXED subitems; + WCHAR szValue[32]; RtlSecureZeroMemory(&subitems, sizeof(subitems)); RtlSecureZeroMemory(szValue, sizeof(szValue)); @@ -792,7 +792,7 @@ VOID propObDumpUSHORT( lpFormat = (HexOutput) ? FORMAT_HEXUSHORT : FORMAT_USHORT; RtlStringCchPrintfSecure(szValue, - DUMP_CONVERSION_LENGTH, + RTL_NUMBER_OF(szValue), lpFormat, Value); @@ -824,36 +824,20 @@ VOID propObDumpUnicodeStringInternal( _In_ LPWSTR StringName, _In_opt_ PUNICODE_STRING String, _In_opt_ PVOID ReferenceBufferAddress, - _In_ BOOLEAN IsKernelPtr + _In_ BOOLEAN IsKernelPointer ) { - HTREEITEM h_tviSubItem; - TL_SUBITEMS_FIXED subitems; - WCHAR szValue[DUMP_CONVERSION_LENGTH + 1]; + BOOL bNormalized = FALSE; + HTREEITEM h_tviSubItem; + TL_SUBITEMS_FIXED subitems; + WCHAR szValue[32]; + UNICODE_STRING displayString; RtlSecureZeroMemory(&subitems, sizeof(subitems)); subitems.Count = 2; - if (IsKernelPtr) { - - subitems.Text[1] = T_PUNICODE_STRING; - - if (ReferenceBufferAddress == NULL) { - subitems.Text[0] = T_NULL; - } - else { - RtlSecureZeroMemory(&szValue, sizeof(szValue)); - szValue[0] = TEXT('0'); - szValue[1] = TEXT('x'); - u64tohex((ULONG_PTR)ReferenceBufferAddress, &szValue[2]); - subitems.Text[0] = szValue; - } - - } - else { - subitems.Text[0] = T_EmptyString; - subitems.Text[1] = T_UNICODE_STRING; - } + subitems.Text[0] = T_EmptyString; + subitems.Text[1] = (IsKernelPointer) ? T_PUNICODE_STRING : T_UNICODE_STRING; // // Add root node. @@ -905,23 +889,28 @@ VOID propObDumpUnicodeStringInternal( else { RtlSecureZeroMemory(&szValue, sizeof(szValue)); - szValue[0] = TEXT('0'); - szValue[1] = TEXT('x'); + if (ReferenceBufferAddress == NULL) { + subitems.Text[0] = T_NULL; + } + else { + RtlSecureZeroMemory(&szValue, sizeof(szValue)); + szValue[0] = TEXT('0'); + szValue[1] = TEXT('x'); + u64tohex((ULONG_PTR)ReferenceBufferAddress, &szValue[2]); + subitems.Text[0] = szValue; + } - if (IsKernelPtr) { - u64tohex((ULONG_PTR)String->Buffer, &szValue[2]); + bNormalized = supNormalizeUnicodeStringForDisplay(g_obexHeap, + String, + &displayString); + if (bNormalized) + { + subitems.Text[1] = displayString.Buffer; } else { - if (ReferenceBufferAddress) { - u64tohex((ULONG_PTR)ReferenceBufferAddress, &szValue[2]); - } - else { - szValue[0] = 0; - } + subitems.Text[1] = String->Buffer; } - subitems.Text[0] = szValue; - subitems.Text[1] = String->Buffer; } supTreeListAddItem( @@ -935,6 +924,8 @@ VOID propObDumpUnicodeStringInternal( } + if (bNormalized) + supFreeDuplicatedUnicodeString(g_obexHeap, &displayString, FALSE); } /* @@ -950,7 +941,7 @@ VOID propObDumpUnicodeString( _In_ HTREEITEM hParent, _In_ LPWSTR StringName, _In_ PUNICODE_STRING InputString, - _In_ BOOLEAN IsKernelPtr + _In_ BOOLEAN IsKernelPointer ) { UNICODE_STRING dumpedString; @@ -960,19 +951,47 @@ VOID propObDumpUnicodeString( bDumpOk = kdDumpUnicodeString(InputString, &dumpedString, &pvRefAddr, - IsKernelPtr); + IsKernelPointer); propObDumpUnicodeStringInternal(TreeList, hParent, StringName, &dumpedString, pvRefAddr, - IsKernelPtr); + IsKernelPointer); if (bDumpOk) supHeapFree(dumpedString.Buffer); } +/* +* propDumpQueryFullNamespaceNormalizedPath +* +* Purpose: +* +* Query full namespace path for object with a normalization for output. +* +*/ +_Success_(return) +BOOL propDumpQueryFullNamespaceNormalizedPath( + _In_ ULONG_PTR ObjectAddress, + _Out_ PUNICODE_STRING NormalizedPath +) +{ + BOOL bResult = FALSE; + UNICODE_STRING objectName; + + if (ObQueryFullNamespacePath(ObjectAddress, &objectName)) { + + bResult = supNormalizeUnicodeStringForDisplay(g_obexHeap, + &objectName, NormalizedPath); + + supFreeUnicodeString(g_obexHeap, &objectName); + } + + return bResult; +} + /* * propDumpObjectForAddress * @@ -990,15 +1009,18 @@ VOID propDumpObjectForAddress( _In_ LPWSTR lpErrorLiteral ) { + BOOL bOkay = FALSE; COLORREF bgColor = 0; ULONG_PTR objectAddress = (ULONG_PTR)pvObject; - LPWSTR lpObjectName = NULL, lpName = NULL; + LPWSTR lpName = NULL; + + UNICODE_STRING normalizedName; if (objectAddress) { - lpObjectName = ObQueryFullNamespacePath(objectAddress); - if (lpObjectName) { - lpName = lpObjectName; + bOkay = propDumpQueryFullNamespaceNormalizedPath(objectAddress, &normalizedName); + if (bOkay) { + lpName = normalizedName.Buffer; } else { lpName = lpErrorLiteral; @@ -1015,8 +1037,8 @@ VOID propDumpObjectForAddress( (COLORREF)bgColor, (COLORREF)0); - if (lpObjectName) - supHeapFree(lpObjectName); + if (bOkay) + supFreeUnicodeString(g_obexHeap, &normalizedName); } /* @@ -1188,15 +1210,19 @@ VOID propObDumpDriverExtension( PVOID Ref; } DrvExt; + BOOL bPathAllocated; + HTREEITEM h_tviRootItem; COLORREF BgColor; PDRIVER_OBJECT SelfDriverObject; - LPWSTR lpDesc, lpObjectName; + LPWSTR lpDesc; PVOID DriverExtensionPtr; ULONG ObjectSize = 0; ULONG ObjectVersion = 0; + UNICODE_STRING normalizedPath; + DriverExtensionPtr = ObDumpDriverExtensionVersionAware((ULONG_PTR)DriverExtension, &ObjectSize, &ObjectVersion); @@ -1221,7 +1247,7 @@ VOID propObDumpDriverExtension( // BgColor = 0; lpDesc = NULL; - lpObjectName = NULL; + bPathAllocated = FALSE; //must be self-ref SelfDriverObject = DrvExt.Versions.DriverExtensionCompatible->DriverObject; @@ -1234,9 +1260,10 @@ VOID propObDumpDriverExtension( //find ref if (SelfDriverObject != NULL) { - lpObjectName = ObQueryFullNamespacePath((ULONG_PTR)SelfDriverObject); - if (lpObjectName) { - lpDesc = lpObjectName; + bPathAllocated = propDumpQueryFullNamespaceNormalizedPath( + (ULONG_PTR)SelfDriverObject, &normalizedPath); + if (bPathAllocated) { + lpDesc = normalizedPath.Buffer; } else { //sef-ref not found, notify, could be object outside directory so we don't know it name etc @@ -1250,11 +1277,13 @@ VOID propObDumpDriverExtension( propObDumpAddress(TreeList, h_tviRootItem, T_FIELD_DRIVER_OBJECT, lpDesc, SelfDriverObject, BgColor, 0); - if (lpObjectName) - supHeapFree(lpObjectName); + if (bPathAllocated) + supFreeDuplicatedUnicodeString(g_obexHeap, &normalizedPath, FALSE); //AddDevice - propObDumpAddressWithModule(TreeList, h_tviRootItem, TEXT("AddDevice"), + propObDumpAddressWithModule(TreeList, + h_tviRootItem, + TEXT("AddDevice"), DrvExt.Versions.DriverExtensionCompatible->AddDevice, ModulesList, LoaderEntry->DllBase, @@ -2762,7 +2791,7 @@ PROP_OBJECT_DUMP_ROUTINE(propObDumpSyncObject) WCHAR szValue[MAX_PATH + 1]; - switch (Context->TypeIndex) { + switch (Context->ObjectTypeIndex) { case ObjectTypeEvent: ObjectSize = sizeof(KEVENT); @@ -2803,7 +2832,7 @@ PROP_OBJECT_DUMP_ROUTINE(propObDumpSyncObject) // Object name // Header = NULL; - switch (Context->TypeIndex) { + switch (Context->ObjectTypeIndex) { case ObjectTypeEvent: lpType = T_KEVENT; Event = (KEVENT*)Object; @@ -2914,7 +2943,7 @@ PROP_OBJECT_DUMP_ROUTINE(propObDumpSyncObject) propObDumpDispatcherHeader(hwndTreeList, h_tviRootItem, Header, lpDescType, lpDesc1, lpDesc2); //type specific values - switch (Context->TypeIndex) { + switch (Context->ObjectTypeIndex) { case ObjectTypeMutant: if (Mutant) { propObDumpListEntry(hwndTreeList, h_tviRootItem, L"MutantListEntry", &Mutant->MutantListEntry); @@ -2967,7 +2996,7 @@ VOID propObDumpObjectTypeFlags( LPWSTR lpType; TL_SUBITEMS_FIXED TreeListSubitems; - WCHAR szValue[DUMP_CONVERSION_LENGTH + 1]; + WCHAR szValue[32]; if (ObjectTypeFlags) { @@ -2983,7 +3012,7 @@ VOID propObDumpObjectTypeFlags( RtlSecureZeroMemory(szValue, sizeof(szValue)); RtlStringCchPrintfSecure(szValue, - DUMP_CONVERSION_LENGTH, + RTL_NUMBER_OF(szValue), FORMAT_HEXBYTE, ObjectTypeFlags); @@ -3017,18 +3046,18 @@ VOID propObDumpObjectTypeFlags( */ PROP_OBJECT_DUMP_ROUTINE(propObDumpObjectType) { - BOOL bOkay; - HTREEITEM h_tviRootItem, h_tviSubItem, h_tviGenericMapping; - UINT i; - LPWSTR lpType = NULL; - POBJINFO CurrentObject = NULL; - PVOID ObjectTypeInformation = NULL; - PRTL_PROCESS_MODULES ModulesList = NULL; - TL_SUBITEMS_FIXED TreeListSubItems; - PVOID TypeProcs[MAX_KNOWN_OBJECT_TYPE_PROCEDURES]; - PVOID SelfDriverBase; - ULONG SelfDriverSize; - + BOOL bOkay; + HTREEITEM h_tviRootItem, h_tviSubItem, h_tviGenericMapping; + UINT i; + LPWSTR lpType = NULL; + PVOID ObjectTypeInformation = NULL; + PRTL_PROCESS_MODULES ModulesList = NULL; + TL_SUBITEMS_FIXED TreeListSubItems; + PVOID TypeProcs[MAX_KNOWN_OBJECT_TYPE_PROCEDURES]; + PVOID SelfDriverBase; + ULONG SelfDriverSize; + + POBEX_OBJECT_INFORMATION CurrentObject = NULL; ULONG ObjectSize = 0; ULONG ObjectVersion = 0; @@ -3066,7 +3095,10 @@ PROP_OBJECT_DUMP_ROUTINE(propObDumpObjectType) // // Get the reference to the object. // - CurrentObject = ObQueryObject(T_OBJECTTYPES, Context->lpObjectName); + CurrentObject = ObQueryObjectInDirectory( + &Context->NtObjectName, + ObGetPredefinedUnicodeString(OBP_OBTYPES)); + if (CurrentObject == NULL) break; @@ -3227,10 +3259,8 @@ PROP_OBJECT_DUMP_ROUTINE(propObDumpObjectType) // RtlSecureZeroMemory(TypeProcs, sizeof(TypeProcs)); - supCopyMemory( - &TypeProcs, - sizeof(TypeProcs), - &ObjectType.Versions.ObjectTypeCompatible->TypeInfo.DumpProcedure, + RtlCopyMemory(&TypeProcs, + &ObjectType.Versions.ObjectTypeCompatible->TypeInfo.DumpProcedure, sizeof(TypeProcs)); //assume ntoskrnl first in list and list initialized @@ -3499,7 +3529,7 @@ VOID propObxDumpFltFilter( ULONG objectVersion, objectSize = 0; PVOID pvFltObject; TL_SUBITEMS_FIXED subitems; - WCHAR szValue[MAX_TEXT_CONVERSION_ULONG64 + 1]; + WCHAR szValue[MAX_TEXT_CONVERSION_ULONG64]; FLT_FILTER_COMPATIBLE compatObject; @@ -4001,7 +4031,7 @@ PROP_OBJECT_DUMP_ROUTINE(propObDumpAlpcPort) ALPC_PORT_STATE PortState; TL_SUBITEMS_FIXED subitems; - WCHAR szBuffer[DUMP_CONVERSION_LENGTH + 1]; + WCHAR szValue[32]; union { union { @@ -4050,11 +4080,11 @@ PROP_OBJECT_DUMP_ROUTINE(propObDumpAlpcPort) RtlSecureZeroMemory(&subitems, sizeof(subitems)); subitems.Count = 2; - szBuffer[0] = L'0'; - szBuffer[1] = L'x'; - szBuffer[2] = 0; - u64tohex((ULONG_PTR)AlpcPort.u1.Port7600->CommunicationInfo, &szBuffer[2]); - subitems.Text[0] = szBuffer; + szValue[0] = L'0'; + szValue[1] = L'x'; + szValue[2] = 0; + u64tohex((ULONG_PTR)AlpcPort.u1.Port7600->CommunicationInfo, &szValue[2]); + subitems.Text[0] = szValue; subitems.Text[1] = TEXT("PALPC_COMMUNICATION_INFO"); h_tviSubItem = supTreeListAddItem( @@ -4293,8 +4323,6 @@ PROP_OBJECT_DUMP_ROUTINE(propObDumpAlpcPort) case OBVERSION_ALPCPORT_V4: PortState.State = AlpcPort.u1.Port10240->u1.State; break; - default: - break; } for (i = 0; i < RTL_NUMBER_OF(T_ALPC_PORT_STATE); i++) { @@ -4339,6 +4367,9 @@ PROP_OBJECT_DUMP_ROUTINE(propObDumpCallback) CALLBACK_OBJECT ObjectDump; CALLBACK_REGISTRATION CallbackRegistration; + UNICODE_STRING NormalizedName; + LPWSTR ObjectName; + // // Read object body. // @@ -4388,6 +4419,14 @@ PROP_OBJECT_DUMP_ROUTINE(propObDumpCallback) ListHead = Context->ObjectInfo.ObjectAddress + FIELD_OFFSET(CALLBACK_OBJECT, RegisteredCallbacks); ListEntry.Flink = ObjectDump.RegisteredCallbacks.Flink; Count = 0; + + if (supNormalizeUnicodeStringForDisplay(g_obexHeap, &Context->NtObjectName, &NormalizedName)) { + ObjectName = NormalizedName.Buffer; + } + else { + ObjectName = Context->NtObjectName.Buffer; + } + while ((ULONG_PTR)ListEntry.Flink != ListHead) { // @@ -4410,7 +4449,7 @@ PROP_OBJECT_DUMP_ROUTINE(propObDumpCallback) propObDumpAddressWithModule(hwndTreeList, h_tviRootItem, - Context->lpObjectName, + ObjectName, CallbackRegistration.CallbackFunction, Modules, NULL, @@ -4425,6 +4464,7 @@ PROP_OBJECT_DUMP_ROUTINE(propObDumpCallback) TEXT("This object has no registered callbacks or there is an query error.")); } + supFreeDuplicatedUnicodeString(g_obexHeap, &NormalizedName, FALSE); supHeapFree(Modules); } @@ -4600,7 +4640,7 @@ INT_PTR ObjectDumpOnInit( pvDlgContext->tlSubItemHit = -1; SetProp(hwndDlg, T_DLGCONTEXT, (HANDLE)pvDlgContext); - switch (Context->TypeIndex) { + switch (Context->ObjectTypeIndex) { case ObjectTypeDirectory: ObDumpRoutine = (pfnObDumpRoutine)propObDumpDirectoryObject; @@ -4721,9 +4761,6 @@ VOID ObjectDumpOnWMCommand( pvDlgContext->tlSubItemHit); break; - - default: - break; } } diff --git a/Source/WinObjEx64/props/propObjectDumpConsts.h b/Source/WinObjEx64/props/propObjectDumpConsts.h index 603eb142..7ddea5a6 100644 --- a/Source/WinObjEx64/props/propObjectDumpConsts.h +++ b/Source/WinObjEx64/props/propObjectDumpConsts.h @@ -1,12 +1,12 @@ /******************************************************************************* * -* (C) COPYRIGHT AUTHORS, 2015 - 2021 +* (C) COPYRIGHT AUTHORS, 2015 - 2022 * * TITLE: PROPOBJECTDUMPCONSTS.H * -* VERSION: 1.92 +* VERSION: 2.00 * -* DATE: 19 Sep 2021 +* DATE: 19 Jun 2022 * * Consts header file for structured object dumps. * @@ -24,8 +24,6 @@ #define CLR_INVL 0xa9a9a9 //silver #define CLR_LGRY 0xd3d3d3 //light grey -#define DUMP_CONVERSION_LENGTH 99 - #define FORMAT_HEXBYTE L"0x%02X" #define FORMAT_HEXUSHORT L"0x%04X" #define FORMAT_HEXDWORD L"0x%08X" diff --git a/Source/WinObjEx64/props/propProcess.c b/Source/WinObjEx64/props/propProcess.c index dc9d0ceb..55a070fa 100644 --- a/Source/WinObjEx64/props/propProcess.c +++ b/Source/WinObjEx64/props/propProcess.c @@ -4,9 +4,9 @@ * * TITLE: PROPPROCESS.C * -* VERSION: 1.94 +* VERSION: 2.00 * -* DATE: 03 Jun 2022 +* DATE: 19 Jun 2022 * * THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF * ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED @@ -18,6 +18,8 @@ #include "propDlg.h" #include "extras.h" +#define COLUMN_PSLIST_NAME 0 +#define COLUMN_PSLIST_ID 1 #define COLUMN_PSLIST_HANDLE 2 #define COLUMN_PSLIST_GRANTEDACCESS 3 @@ -83,7 +85,7 @@ INT CALLBACK ProcessListCompareFunc( goto Done; switch (lvColumnToSort) { - case 0: + case COLUMN_PSLIST_NAME: // // Name column. // @@ -100,7 +102,7 @@ INT CALLBACK ProcessListCompareFunc( nResult = _strcmpi(FirstToCompare, SecondToCompare); break; - case 1: + case COLUMN_PSLIST_ID: // // Id column. // @@ -112,8 +114,6 @@ INT CALLBACK ProcessListCompareFunc( nResult = Value1 > Value2; break; - default: - break; } Done: diff --git a/Source/WinObjEx64/props/propProcess.h b/Source/WinObjEx64/props/propProcess.h deleted file mode 100644 index 8a49eb89..00000000 --- a/Source/WinObjEx64/props/propProcess.h +++ /dev/null @@ -1,25 +0,0 @@ -/******************************************************************************* -* -* (C) COPYRIGHT AUTHORS, 2015 - 2018 -* -* TITLE: PROPPROCESS.H -* -* VERSION: 1.52 -* -* DATE: 08 Jan 2018 -* -* Common header file for Process property sheet. -* -* THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF -* ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED -* TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A -* PARTICULAR PURPOSE. -* -*******************************************************************************/ -#pragma once - -INT_PTR CALLBACK ProcessListDialogProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam); diff --git a/Source/WinObjEx64/props/propSection.c b/Source/WinObjEx64/props/propSection.c index b85ba368..6c5fe977 100644 --- a/Source/WinObjEx64/props/propSection.c +++ b/Source/WinObjEx64/props/propSection.c @@ -1,12 +1,12 @@ /******************************************************************************* * -* (C) COPYRIGHT AUTHORS, 2021 +* (C) COPYRIGHT AUTHORS, 2021 - 2022 * * TITLE: PROPSECTION.C * -* VERSION: 1.92 +* VERSION: 2.00 * -* DATE: 17 Sep 2021 +* DATE: 19 Jun 2022 * * THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF * ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED @@ -15,10 +15,9 @@ * *******************************************************************************/ #include "global.h" -#include "propDlg.h" #include "extras.h" +#include "props.h" #include "propObjectDumpConsts.h" -#include "propObjectDump.h" #define COLUMN_SECTION_VIEW_OBJECT 0 #define COLUMN_SECTION_VIEW_ADDRESS 1 @@ -730,8 +729,6 @@ VOID SectionPropertiesCreate( lpError = TEXT("Object flags are not supported."); break; - default: - break; } supObDumpShowError(hwndDlg, lpError); } @@ -788,9 +785,6 @@ INT_PTR CALLBACK SectionPropertiesDialogProc( pDlgContext->tlSubItemHit); } break; - - default: - break; } break; diff --git a/Source/WinObjEx64/props/propSection.h b/Source/WinObjEx64/props/propSection.h deleted file mode 100644 index ac794b8a..00000000 --- a/Source/WinObjEx64/props/propSection.h +++ /dev/null @@ -1,25 +0,0 @@ -/******************************************************************************* -* -* (C) COPYRIGHT AUTHORS, 2021 -* -* TITLE: PROPSECTION.H -* -* VERSION: 1.90 -* -* DATE: 11 May 2021 -* -* Common header file for Section property sheet. -* -* THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF -* ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED -* TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A -* PARTICULAR PURPOSE. -* -*******************************************************************************/ -#pragma once - -INT_PTR CALLBACK SectionPropertiesDialogProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam); diff --git a/Source/WinObjEx64/props/propSecurity.c b/Source/WinObjEx64/props/propSecurity.c index 7507f019..3b58ab9a 100644 --- a/Source/WinObjEx64/props/propSecurity.c +++ b/Source/WinObjEx64/props/propSecurity.c @@ -4,9 +4,9 @@ * * TITLE: PROPSECURITY.C * -* VERSION: 1.94 +* VERSION: 2.00 * -* DATE: 04 Jun 2022 +* DATE: 19 Jun 2022 * * THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF * ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED @@ -15,10 +15,93 @@ * *******************************************************************************/ #include "global.h" -#include "propDlg.h" -#include "propSecurity.h" #include "propSecurityConsts.h" +typedef struct _ObjectSecurityVtbl ObjectSecurityVtbl, * PObjectSecurityVtbl; + +//class +typedef struct _IObjectSecurity { + ObjectSecurityVtbl* lpVtbl; + ULONG RefCount; + ULONG psiFlags; + ULONG dwAccessMax; + GENERIC_MAPPING GenericMapping; + ACCESS_MASK ValidAccessMask; + HINSTANCE hInstance; + PROP_OBJECT_INFO* ObjectContext; + PSI_ACCESS AccessTable;//dynamically allocated access table + POPENOBJECTMETHOD OpenObjectMethod; + PCLOSEOBJECTMETHOD CloseObjectMethod; +} IObjectSecurity, * PIObjectSecurity; + + +//Vtbl prototypes + +typedef HRESULT(STDMETHODCALLTYPE* pQueryInterface)( + _In_ IObjectSecurity* This, + _In_ REFIID riid, + _Out_ void** ppvObject); + +typedef ULONG(STDMETHODCALLTYPE* pAddRef)( + _In_ IObjectSecurity* This); + +typedef ULONG(STDMETHODCALLTYPE* pRelease)( + _In_ IObjectSecurity* This); + +// *** ISecurityInformation methods *** +typedef HRESULT(STDMETHODCALLTYPE* pGetObjectInformation)( + _In_ IObjectSecurity* This, + _Out_ PSI_OBJECT_INFO pObjectInfo); + +typedef HRESULT(STDMETHODCALLTYPE* pGetSecurity)( + _In_ IObjectSecurity* This, + _In_ SECURITY_INFORMATION RequestedInformation, + _Out_ PSECURITY_DESCRIPTOR* ppSecurityDescriptor, + _In_ BOOL fDefault); + +typedef HRESULT(STDMETHODCALLTYPE* pSetSecurity)( + _In_ IObjectSecurity* This, + _In_ SECURITY_INFORMATION SecurityInformation, + _In_ PSECURITY_DESCRIPTOR pSecurityDescriptor); + +typedef HRESULT(STDMETHODCALLTYPE* pGetAccessRights)( + _In_ IObjectSecurity* This, + _In_ const GUID* pguidObjectType, + _In_ DWORD dwFlags, + _Out_ PSI_ACCESS* ppAccess, + _Out_ ULONG* pcAccesses, + _Out_ ULONG* piDefaultAccess); + +typedef HRESULT(STDMETHODCALLTYPE* pMapGeneric)( + _In_ IObjectSecurity* This, + _In_ const GUID* pguidObjectType, + _In_ UCHAR* pAceFlags, + _In_ ACCESS_MASK* pMask); + +typedef HRESULT(STDMETHODCALLTYPE* pGetInheritTypes)( + _In_ IObjectSecurity* This, + _Out_ PSI_INHERIT_TYPE* ppInheritTypes, + _Out_ ULONG* pcInheritTypes); + +typedef HRESULT(STDMETHODCALLTYPE* pPropertySheetPageCallback)( + _In_ IObjectSecurity* This, + _In_ HWND hwnd, + _In_ UINT uMsg, + _In_ SI_PAGE_TYPE uPage); + +typedef struct _ObjectSecurityVtbl { + pQueryInterface QueryInterface; + pAddRef AddRef; + pRelease Release; + pGetObjectInformation GetObjectInformation; + pGetSecurity GetSecurity; + pSetSecurity SetSecurity; + pGetAccessRights GetAccessRights; + pMapGeneric MapGeneric; + pGetInheritTypes GetInheritTypes; + pPropertySheetPageCallback PropertySheetPageCallback; +} ObjectSecurityVtbl, * PObjectSecurityVtbl; + /* * propSecurityObjectSupported * @@ -28,34 +111,41 @@ * */ BOOL propSecurityObjectSupported( - _In_ UINT nTypeIndex + _In_ WOBJ_OBJECT_TYPE nTypeIndex ) { - if ((nTypeIndex != ObjectTypePort) && - (nTypeIndex != ObjectTypeFile) && - (nTypeIndex != ObjectTypeDirectory) && - (nTypeIndex != ObjectTypeDevice) && - (nTypeIndex != ObjectTypeSection) && - (nTypeIndex != ObjectTypeEvent) && - (nTypeIndex != ObjectTypeEventPair) && - (nTypeIndex != ObjectTypeMutant) && - (nTypeIndex != ObjectTypeDesktop) && - (nTypeIndex != ObjectTypeKey) && - (nTypeIndex != ObjectTypeSemaphore) && - (nTypeIndex != ObjectTypeSymbolicLink) && - (nTypeIndex != ObjectTypeTimer) && - (nTypeIndex != ObjectTypeWinstation) && - (nTypeIndex != ObjectTypeIoCompletion) && - (nTypeIndex != ObjectTypeJob) && - (nTypeIndex != ObjectTypeSession) && - (nTypeIndex != ObjectTypeMemoryPartition) && - (nTypeIndex != ObjectTypeProcess) && - (nTypeIndex != ObjectTypeThread) && - (nTypeIndex != ObjectTypeToken)) - { - return FALSE; + WOBJ_OBJECT_TYPE SecuritySupportedTypes[] = { + ObjectTypeDesktop, + ObjectTypeDevice, + ObjectTypeDirectory, + ObjectTypeEvent, + ObjectTypeEventPair, + ObjectTypeFile, + ObjectTypeIoCompletion, + ObjectTypeJob, + ObjectTypeKey, + ObjectTypeMemoryPartition, + ObjectTypeMutant, + ObjectTypePort, + ObjectTypeProcess, + ObjectTypeRegistryTransaction, + ObjectTypeSection, + ObjectTypeSemaphore, + ObjectTypeSession, + ObjectTypeSymbolicLink, + ObjectTypeThread, + ObjectTypeTimer, + ObjectTypeToken, + ObjectTypeWinstation + }; + + UINT i; + for (i = 0; i < RTL_NUMBER_OF(SecuritySupportedTypes); i++) { + if (SecuritySupportedTypes[i] == nTypeIndex) + return TRUE; } - return TRUE; + + return FALSE; } /* @@ -72,7 +162,7 @@ PSI_ACCESS propGetAccessTable( { SI_ACCESS* AccessTable = NULL; - switch (This->ObjectContext->TypeIndex) { + switch (This->ObjectContext->ObjectTypeIndex) { case ObjectTypeDirectory: This->dwAccessMax = MAX_KNOWN_DIRECTORY_ACCESS_VALUE; @@ -169,6 +259,11 @@ PSI_ACCESS propGetAccessTable( This->dwAccessMax = MAX_KNOWN_PORT_ACCESS_VALUE; AccessTable = (PSI_ACCESS)&PortAccessValues; break; + + case ObjectTypeRegistryTransaction: + This->dwAccessMax = MAX_KNOWN_TRANSACTION_ACCESS_VALUE; + AccessTable = (PSI_ACCESS)&TransactionAccessValues; + break; } return AccessTable; @@ -296,7 +391,7 @@ HRESULT STDMETHODCALLTYPE GetObjectInformation( pObjectInfo->dwFlags = This->psiFlags; pObjectInfo->hInstance = This->hInstance; pObjectInfo->pszPageTitle = TEXT("Security"); - pObjectInfo->pszObjectName = This->ObjectContext->lpObjectName; + pObjectInfo->pszObjectName = This->ObjectContext->NtObjectName.Buffer; return S_OK; } @@ -533,30 +628,31 @@ HRESULT propSecurityConstructor( //copy object specific access table if it present if (TypeAccessTable && This->dwAccessMax) { - supCopyMemory(This->AccessTable, - Size, + + RtlCopyMemory(This->AccessTable, TypeAccessTable, - (This->dwAccessMax * sizeof(SI_ACCESS))); + This->dwAccessMax * sizeof(SI_ACCESS)); + } if (This->ValidAccessMask & DELETE) { - supCopyMemory(&This->AccessTable[This->dwAccessMax++], sizeof(SI_ACCESS), + RtlCopyMemory(&This->AccessTable[This->dwAccessMax++], &GeneralAccessValues[0], sizeof(SI_ACCESS)); } if (This->ValidAccessMask & READ_CONTROL) { - supCopyMemory(&This->AccessTable[This->dwAccessMax++], sizeof(SI_ACCESS), + RtlCopyMemory(&This->AccessTable[This->dwAccessMax++], &GeneralAccessValues[1], sizeof(SI_ACCESS)); } if (This->ValidAccessMask & WRITE_DAC) { - supCopyMemory(&This->AccessTable[This->dwAccessMax++], sizeof(SI_ACCESS), + RtlCopyMemory(&This->AccessTable[This->dwAccessMax++], &GeneralAccessValues[2], sizeof(SI_ACCESS)); } if (This->ValidAccessMask & WRITE_OWNER) { - supCopyMemory(&This->AccessTable[This->dwAccessMax++], sizeof(SI_ACCESS), + RtlCopyMemory(&This->AccessTable[This->dwAccessMax++], &GeneralAccessValues[3], sizeof(SI_ACCESS)); } if (This->ValidAccessMask & SYNCHRONIZE) { - supCopyMemory(&This->AccessTable[This->dwAccessMax++], sizeof(SI_ACCESS), + RtlCopyMemory(&This->AccessTable[This->dwAccessMax++], &GeneralAccessValues[4], sizeof(SI_ACCESS)); } hResult = S_OK; @@ -564,7 +660,7 @@ HRESULT propSecurityConstructor( } while (FALSE); //cleanup - This->CloseObjectMethod(Context, hObject); + if (hObject) This->CloseObjectMethod(Context, hObject); if (TypeInfo) { supHeapFree(TypeInfo); } @@ -598,15 +694,7 @@ HPROPSHEETPAGE propSecurityCreatePage( { IObjectSecurity* psi; - if ( - (Context == NULL) || - (OpenObjectMethod == NULL) //OpenObjectMethod is required - ) - { - return NULL; - } - - if (!propSecurityObjectSupported(Context->TypeIndex)) { + if (!propSecurityObjectSupported(Context->ObjectTypeIndex)) { return NULL; } diff --git a/Source/WinObjEx64/props/propSecurity.h b/Source/WinObjEx64/props/propSecurity.h deleted file mode 100644 index 8b7ea316..00000000 --- a/Source/WinObjEx64/props/propSecurity.h +++ /dev/null @@ -1,126 +0,0 @@ -/******************************************************************************* -* -* (C) COPYRIGHT AUTHORS, 2015 - 2020 -* -* TITLE: PROPSECURITY.H -* -* VERSION: 1.83 -* -* DATE: 21 Dec 2019 -* -* Common header file for Security property sheet. -* -* THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF -* ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED -* TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A -* PARTICULAR PURPOSE. -* -*******************************************************************************/ -#pragma once - -#include -#include - -typedef struct _ObjectSecurityVtbl ObjectSecurityVtbl, * PObjectSecurityVtbl; - -//open object method (propOpenCurrentObject) -typedef BOOL(CALLBACK* POPENOBJECTMETHOD)( - _In_ PROP_OBJECT_INFO* Context, - _Inout_ PHANDLE phObject, - _In_ ACCESS_MASK DesiredAccess - ); - -//close object method (propCloseCurrentObject) -typedef VOID(CALLBACK* PCLOSEOBJECTMETHOD)( - _In_ PROP_OBJECT_INFO* Context, - _In_ HANDLE hObject - ); - -//class -typedef struct _IObjectSecurity { - ObjectSecurityVtbl* lpVtbl; - ULONG RefCount; - ULONG psiFlags; - ULONG dwAccessMax; - GENERIC_MAPPING GenericMapping; - ACCESS_MASK ValidAccessMask; - HINSTANCE hInstance; - PROP_OBJECT_INFO* ObjectContext; - PSI_ACCESS AccessTable;//dynamically allocated access table - POPENOBJECTMETHOD OpenObjectMethod; - PCLOSEOBJECTMETHOD CloseObjectMethod; -} IObjectSecurity, * PIObjectSecurity; - - -//Vtbl prototypes - -typedef HRESULT(STDMETHODCALLTYPE* pQueryInterface)( - _In_ IObjectSecurity* This, - _In_ REFIID riid, - _Out_ void** ppvObject); - -typedef ULONG(STDMETHODCALLTYPE* pAddRef)( - _In_ IObjectSecurity* This); - -typedef ULONG(STDMETHODCALLTYPE* pRelease)( - _In_ IObjectSecurity* This); - -// *** ISecurityInformation methods *** -typedef HRESULT(STDMETHODCALLTYPE* pGetObjectInformation)( - _In_ IObjectSecurity* This, - _Out_ PSI_OBJECT_INFO pObjectInfo); - -typedef HRESULT(STDMETHODCALLTYPE* pGetSecurity)( - _In_ IObjectSecurity* This, - _In_ SECURITY_INFORMATION RequestedInformation, - _Out_ PSECURITY_DESCRIPTOR* ppSecurityDescriptor, - _In_ BOOL fDefault); - -typedef HRESULT(STDMETHODCALLTYPE* pSetSecurity)( - _In_ IObjectSecurity* This, - _In_ SECURITY_INFORMATION SecurityInformation, - _In_ PSECURITY_DESCRIPTOR pSecurityDescriptor); - -typedef HRESULT(STDMETHODCALLTYPE* pGetAccessRights)( - _In_ IObjectSecurity* This, - _In_ const GUID* pguidObjectType, - _In_ DWORD dwFlags, - _Out_ PSI_ACCESS* ppAccess, - _Out_ ULONG* pcAccesses, - _Out_ ULONG* piDefaultAccess); - -typedef HRESULT(STDMETHODCALLTYPE* pMapGeneric)( - _In_ IObjectSecurity* This, - _In_ const GUID* pguidObjectType, - _In_ UCHAR* pAceFlags, - _In_ ACCESS_MASK* pMask); - -typedef HRESULT(STDMETHODCALLTYPE* pGetInheritTypes)( - _In_ IObjectSecurity* This, - _Out_ PSI_INHERIT_TYPE* ppInheritTypes, - _Out_ ULONG* pcInheritTypes); - -typedef HRESULT(STDMETHODCALLTYPE* pPropertySheetPageCallback)( - _In_ IObjectSecurity* This, - _In_ HWND hwnd, - _In_ UINT uMsg, - _In_ SI_PAGE_TYPE uPage); - -typedef struct _ObjectSecurityVtbl { - pQueryInterface QueryInterface; - pAddRef AddRef; - pRelease Release; - pGetObjectInformation GetObjectInformation; - pGetSecurity GetSecurity; - pSetSecurity SetSecurity; - pGetAccessRights GetAccessRights; - pMapGeneric MapGeneric; - pGetInheritTypes GetInheritTypes; - pPropertySheetPageCallback PropertySheetPageCallback; -} ObjectSecurityVtbl, * PObjectSecurityVtbl; - -HPROPSHEETPAGE propSecurityCreatePage( - _In_ PROP_OBJECT_INFO* Context, - _In_ POPENOBJECTMETHOD OpenObjectMethod, - _In_opt_ PCLOSEOBJECTMETHOD CloseObjectMethod, - _In_ ULONG psiFlags); diff --git a/Source/WinObjEx64/props/propSecurityConsts.h b/Source/WinObjEx64/props/propSecurityConsts.h index 6bfbed63..3d319b5e 100644 --- a/Source/WinObjEx64/props/propSecurityConsts.h +++ b/Source/WinObjEx64/props/propSecurityConsts.h @@ -4,9 +4,9 @@ * * TITLE: PROPSECURITYCONSTS.H * -* VERSION: 1.93 +* VERSION: 2.00 * -* DATE: 24 Mar 2022 +* DATE: 19 Jun 2022 * * Consts header file for Security property sheet. * @@ -26,16 +26,18 @@ #define SI_ACCESS_DEFAULT_FLAGS SI_ACCESS_GENERAL | SI_ACCESS_SPECIFIC +#define SI_ACCESS_DEFAULT_ENTRY(Access, Name) { &GUID_NULL, Access, Name, SI_ACCESS_DEFAULT_FLAGS } + // //General Access Values // #define MAX_KNOWN_GENERAL_ACCESS_VALUE 5 static SI_ACCESS GeneralAccessValues[MAX_KNOWN_GENERAL_ACCESS_VALUE] = { - { &GUID_NULL, DELETE, L"Delete", SI_ACCESS_DEFAULT_FLAGS }, - { &GUID_NULL, READ_CONTROL, L"Read Control", SI_ACCESS_DEFAULT_FLAGS }, - { &GUID_NULL, WRITE_DAC, L"Write DAC", SI_ACCESS_DEFAULT_FLAGS }, - { &GUID_NULL, WRITE_OWNER, L"Write Owner", SI_ACCESS_DEFAULT_FLAGS }, - { &GUID_NULL, SYNCHRONIZE, L"Synchronize", SI_ACCESS_DEFAULT_FLAGS } + SI_ACCESS_DEFAULT_ENTRY(DELETE, L"Delete"), + SI_ACCESS_DEFAULT_ENTRY(READ_CONTROL, L"Read Control"), + SI_ACCESS_DEFAULT_ENTRY(WRITE_DAC, L"Write DAC"), + SI_ACCESS_DEFAULT_ENTRY(WRITE_OWNER, L"Write Owner"), + SI_ACCESS_DEFAULT_ENTRY(SYNCHRONIZE, L"Synchronize") }; // @@ -43,11 +45,11 @@ static SI_ACCESS GeneralAccessValues[MAX_KNOWN_GENERAL_ACCESS_VALUE] = { // #define MAX_KNOWN_SECTION_ACCESS_VALUE 5 static SI_ACCESS SectionAccessValues[MAX_KNOWN_SECTION_ACCESS_VALUE] = { - { &GUID_NULL, SECTION_QUERY, L"Query", SI_ACCESS_DEFAULT_FLAGS }, - { &GUID_NULL, SECTION_MAP_WRITE, L"Map Write", SI_ACCESS_DEFAULT_FLAGS }, - { &GUID_NULL, SECTION_MAP_READ, L"Map Read", SI_ACCESS_DEFAULT_FLAGS }, - { &GUID_NULL, SECTION_MAP_EXECUTE, L"Map Execute", SI_ACCESS_DEFAULT_FLAGS }, - { &GUID_NULL, SECTION_EXTEND_SIZE, L"Extend Size", SI_ACCESS_DEFAULT_FLAGS } + SI_ACCESS_DEFAULT_ENTRY(SECTION_QUERY, L"Query"), + SI_ACCESS_DEFAULT_ENTRY(SECTION_MAP_WRITE, L"Map Write"), + SI_ACCESS_DEFAULT_ENTRY(SECTION_MAP_READ, L"Map Read"), + SI_ACCESS_DEFAULT_ENTRY(SECTION_MAP_EXECUTE, L"Map Execute"), + SI_ACCESS_DEFAULT_ENTRY(SECTION_EXTEND_SIZE, L"Extend Size") }; // @@ -55,10 +57,10 @@ static SI_ACCESS SectionAccessValues[MAX_KNOWN_SECTION_ACCESS_VALUE] = { // #define MAX_KNOWN_DIRECTORY_ACCESS_VALUE 4 static SI_ACCESS DirectoryAccessValues[MAX_KNOWN_DIRECTORY_ACCESS_VALUE] = { - { &GUID_NULL, DIRECTORY_QUERY, L"Query", SI_ACCESS_DEFAULT_FLAGS }, - { &GUID_NULL, DIRECTORY_TRAVERSE, L"Traverse", SI_ACCESS_DEFAULT_FLAGS }, - { &GUID_NULL, DIRECTORY_CREATE_OBJECT, L"Create Object", SI_ACCESS_DEFAULT_FLAGS }, - { &GUID_NULL, DIRECTORY_CREATE_SUBDIRECTORY, L"Create SubDirectory", SI_ACCESS_DEFAULT_FLAGS } + SI_ACCESS_DEFAULT_ENTRY(DIRECTORY_QUERY, L"Query"), + SI_ACCESS_DEFAULT_ENTRY(DIRECTORY_TRAVERSE, L"Traverse"), + SI_ACCESS_DEFAULT_ENTRY(DIRECTORY_CREATE_OBJECT, L"Create Object"), + SI_ACCESS_DEFAULT_ENTRY(DIRECTORY_CREATE_SUBDIRECTORY, L"Create SubDirectory") }; // @@ -66,20 +68,20 @@ static SI_ACCESS DirectoryAccessValues[MAX_KNOWN_DIRECTORY_ACCESS_VALUE] = { // #define MAX_KNOWN_FILE_ACCESS_VALUE 14 static SI_ACCESS FileAccessValues[MAX_KNOWN_FILE_ACCESS_VALUE] = { - { &GUID_NULL, FILE_READ_DATA, L"Read Data", SI_ACCESS_DEFAULT_FLAGS }, - { &GUID_NULL, FILE_LIST_DIRECTORY, L"List Directory", SI_ACCESS_DEFAULT_FLAGS }, - { &GUID_NULL, FILE_WRITE_DATA, L"Write Data", SI_ACCESS_DEFAULT_FLAGS }, - { &GUID_NULL, FILE_ADD_FILE, L"Add File", SI_ACCESS_DEFAULT_FLAGS }, - { &GUID_NULL, FILE_APPEND_DATA, L"Append Data", SI_ACCESS_DEFAULT_FLAGS }, - { &GUID_NULL, FILE_ADD_SUBDIRECTORY, L"Add SubDirectory", SI_ACCESS_DEFAULT_FLAGS }, - { &GUID_NULL, FILE_CREATE_PIPE_INSTANCE, L"Create Pipe Instance", SI_ACCESS_DEFAULT_FLAGS }, - { &GUID_NULL, FILE_READ_EA, L"Read EA", SI_ACCESS_DEFAULT_FLAGS }, - { &GUID_NULL, FILE_WRITE_EA, L"Write EA", SI_ACCESS_DEFAULT_FLAGS }, - { &GUID_NULL, FILE_EXECUTE, L"Execute", SI_ACCESS_DEFAULT_FLAGS }, - { &GUID_NULL, FILE_TRAVERSE, L"Traverse", SI_ACCESS_DEFAULT_FLAGS }, - { &GUID_NULL, FILE_DELETE_CHILD, L"Delete Child", SI_ACCESS_DEFAULT_FLAGS }, - { &GUID_NULL, FILE_READ_ATTRIBUTES, L"Read Attributes", SI_ACCESS_DEFAULT_FLAGS }, - { &GUID_NULL, FILE_WRITE_ATTRIBUTES, L"Write Attributes", SI_ACCESS_DEFAULT_FLAGS } + SI_ACCESS_DEFAULT_ENTRY(FILE_READ_DATA, L"Read Data"), + SI_ACCESS_DEFAULT_ENTRY(FILE_LIST_DIRECTORY, L"List Directory"), + SI_ACCESS_DEFAULT_ENTRY(FILE_WRITE_DATA, L"Write Data"), + SI_ACCESS_DEFAULT_ENTRY(FILE_ADD_FILE, L"Add File"), + SI_ACCESS_DEFAULT_ENTRY(FILE_APPEND_DATA, L"Append Data"), + SI_ACCESS_DEFAULT_ENTRY(FILE_ADD_SUBDIRECTORY, L"Add SubDirectory"), + SI_ACCESS_DEFAULT_ENTRY(FILE_CREATE_PIPE_INSTANCE, L"Create Pipe Instance"), + SI_ACCESS_DEFAULT_ENTRY(FILE_READ_EA, L"Read EA"), + SI_ACCESS_DEFAULT_ENTRY(FILE_WRITE_EA, L"Write EA"), + SI_ACCESS_DEFAULT_ENTRY(FILE_EXECUTE, L"Execute"), + SI_ACCESS_DEFAULT_ENTRY(FILE_TRAVERSE, L"Traverse"), + SI_ACCESS_DEFAULT_ENTRY(FILE_DELETE_CHILD, L"Delete Child"), + SI_ACCESS_DEFAULT_ENTRY(FILE_READ_ATTRIBUTES, L"Read Attributes"), + SI_ACCESS_DEFAULT_ENTRY(FILE_WRITE_ATTRIBUTES, L"Write Attributes") }; // @@ -87,17 +89,16 @@ static SI_ACCESS FileAccessValues[MAX_KNOWN_FILE_ACCESS_VALUE] = { // #define MAX_KNOWN_EVENT_ACCESS_VALUE 2 static SI_ACCESS EventAccessValues[MAX_KNOWN_EVENT_ACCESS_VALUE] = { - { &GUID_NULL, EVENT_QUERY_STATE, L"Query State", SI_ACCESS_DEFAULT_FLAGS }, - { &GUID_NULL, EVENT_MODIFY_STATE, L"Modify State", SI_ACCESS_DEFAULT_FLAGS } + SI_ACCESS_DEFAULT_ENTRY(EVENT_QUERY_STATE, L"Query State"), + SI_ACCESS_DEFAULT_ENTRY(EVENT_MODIFY_STATE, L"Modify State") }; - // //Mutant Access Values // #define MAX_KNOWN_MUTANT_ACCESS_VALUE 1 static SI_ACCESS MutantAccessValues[MAX_KNOWN_MUTANT_ACCESS_VALUE] = { - { &GUID_NULL, MUTANT_QUERY_STATE, L"Query State", SI_ACCESS_DEFAULT_FLAGS } + SI_ACCESS_DEFAULT_ENTRY(MUTANT_QUERY_STATE, L"Query State") }; // @@ -105,15 +106,15 @@ static SI_ACCESS MutantAccessValues[MAX_KNOWN_MUTANT_ACCESS_VALUE] = { // #define MAX_KNOWN_DESKTOP_ACCESS_VALUE 9 static SI_ACCESS DesktopAccessValues[MAX_KNOWN_DESKTOP_ACCESS_VALUE] = { - { &GUID_NULL, DESKTOP_READOBJECTS, L"Read Objects", SI_ACCESS_DEFAULT_FLAGS }, - { &GUID_NULL, DESKTOP_CREATEWINDOW, L"Create Window", SI_ACCESS_DEFAULT_FLAGS }, - { &GUID_NULL, DESKTOP_CREATEMENU, L"Create Menu", SI_ACCESS_DEFAULT_FLAGS }, - { &GUID_NULL, DESKTOP_HOOKCONTROL, L"Hook Control", SI_ACCESS_DEFAULT_FLAGS }, - { &GUID_NULL, DESKTOP_JOURNALRECORD, L"Journal Record", SI_ACCESS_DEFAULT_FLAGS }, - { &GUID_NULL, DESKTOP_JOURNALPLAYBACK, L"Journal Playback", SI_ACCESS_DEFAULT_FLAGS }, - { &GUID_NULL, DESKTOP_ENUMERATE, L"Enumerate", SI_ACCESS_DEFAULT_FLAGS }, - { &GUID_NULL, DESKTOP_WRITEOBJECTS, L"WriteObjects", SI_ACCESS_DEFAULT_FLAGS }, - { &GUID_NULL, DESKTOP_SWITCHDESKTOP, L"Switch Desktop", SI_ACCESS_DEFAULT_FLAGS } + SI_ACCESS_DEFAULT_ENTRY(DESKTOP_READOBJECTS, L"Read Objects"), + SI_ACCESS_DEFAULT_ENTRY(DESKTOP_CREATEWINDOW, L"Create Window"), + SI_ACCESS_DEFAULT_ENTRY(DESKTOP_CREATEMENU, L"Create Menu"), + SI_ACCESS_DEFAULT_ENTRY(DESKTOP_HOOKCONTROL, L"Hook Control"), + SI_ACCESS_DEFAULT_ENTRY(DESKTOP_JOURNALRECORD, L"Journal Record"), + SI_ACCESS_DEFAULT_ENTRY(DESKTOP_JOURNALPLAYBACK, L"Journal Playback"), + SI_ACCESS_DEFAULT_ENTRY(DESKTOP_ENUMERATE, L"Enumerate"), + SI_ACCESS_DEFAULT_ENTRY(DESKTOP_WRITEOBJECTS, L"Write Objects"), + SI_ACCESS_DEFAULT_ENTRY(DESKTOP_SWITCHDESKTOP, L"Switch Desktop") }; // @@ -121,15 +122,15 @@ static SI_ACCESS DesktopAccessValues[MAX_KNOWN_DESKTOP_ACCESS_VALUE] = { // #define MAX_KNOWN_WINSTATION_ACCESS_VALUE 9 static SI_ACCESS WinStationAccessValues[MAX_KNOWN_WINSTATION_ACCESS_VALUE] = { - { &GUID_NULL, WINSTA_ENUMDESKTOPS, L"Enumerate Desktops", SI_ACCESS_DEFAULT_FLAGS }, - { &GUID_NULL, WINSTA_READATTRIBUTES, L"Read Attributes", SI_ACCESS_DEFAULT_FLAGS }, - { &GUID_NULL, WINSTA_ACCESSCLIPBOARD, L"Access Clipboard", SI_ACCESS_DEFAULT_FLAGS }, - { &GUID_NULL, WINSTA_CREATEDESKTOP, L"Create Desktop", SI_ACCESS_DEFAULT_FLAGS }, - { &GUID_NULL, WINSTA_WRITEATTRIBUTES, L"Write Attributes", SI_ACCESS_DEFAULT_FLAGS }, - { &GUID_NULL, WINSTA_ACCESSGLOBALATOMS, L"Access Global Atoms", SI_ACCESS_DEFAULT_FLAGS }, - { &GUID_NULL, WINSTA_EXITWINDOWS, L"Exit Windows", SI_ACCESS_DEFAULT_FLAGS }, - { &GUID_NULL, WINSTA_ENUMERATE, L"Enumerate", SI_ACCESS_DEFAULT_FLAGS }, - { &GUID_NULL, WINSTA_READSCREEN, L"Read Screen", SI_ACCESS_DEFAULT_FLAGS } + SI_ACCESS_DEFAULT_ENTRY(WINSTA_ENUMDESKTOPS, L"Enumerate Desktops"), + SI_ACCESS_DEFAULT_ENTRY(WINSTA_READATTRIBUTES, L"Read Attributes"), + SI_ACCESS_DEFAULT_ENTRY(WINSTA_ACCESSCLIPBOARD, L"Access Clipboard"), + SI_ACCESS_DEFAULT_ENTRY(WINSTA_CREATEDESKTOP, L"Create Desktop"), + SI_ACCESS_DEFAULT_ENTRY(WINSTA_WRITEATTRIBUTES, L"Write Attributes"), + SI_ACCESS_DEFAULT_ENTRY(WINSTA_ACCESSGLOBALATOMS, L"Access Global Atoms"), + SI_ACCESS_DEFAULT_ENTRY(WINSTA_EXITWINDOWS, L"Exit Windows"), + SI_ACCESS_DEFAULT_ENTRY(WINSTA_ENUMERATE, L"Enumerate"), + SI_ACCESS_DEFAULT_ENTRY(WINSTA_READSCREEN, L"Read Screen") }; // @@ -137,14 +138,14 @@ static SI_ACCESS WinStationAccessValues[MAX_KNOWN_WINSTATION_ACCESS_VALUE] = { // #define MAX_KNOWN_KEY_ACCESS_VALUE 8 static SI_ACCESS KeyAccessValues[MAX_KNOWN_KEY_ACCESS_VALUE] = { - { &GUID_NULL, KEY_QUERY_VALUE, L"Query Value", SI_ACCESS_DEFAULT_FLAGS }, - { &GUID_NULL, KEY_SET_VALUE, L"Set Value", SI_ACCESS_DEFAULT_FLAGS }, - { &GUID_NULL, KEY_CREATE_SUB_KEY, L"Create Subkey", SI_ACCESS_DEFAULT_FLAGS }, - { &GUID_NULL, KEY_ENUMERATE_SUB_KEYS, L"Enumerate Subkeys", SI_ACCESS_DEFAULT_FLAGS }, - { &GUID_NULL, KEY_NOTIFY, L"Notify", SI_ACCESS_DEFAULT_FLAGS }, - { &GUID_NULL, KEY_CREATE_LINK, L"Create Link", SI_ACCESS_DEFAULT_FLAGS }, - { &GUID_NULL, KEY_WOW64_64KEY, L"Access 64 bit key", SI_ACCESS_DEFAULT_FLAGS }, - { &GUID_NULL, KEY_WOW64_32KEY, L"Access 32 bit key", SI_ACCESS_DEFAULT_FLAGS } + SI_ACCESS_DEFAULT_ENTRY(KEY_QUERY_VALUE, L"Query Value"), + SI_ACCESS_DEFAULT_ENTRY(KEY_SET_VALUE, L"Set Value"), + SI_ACCESS_DEFAULT_ENTRY(KEY_CREATE_SUB_KEY, L"Create Subkey"), + SI_ACCESS_DEFAULT_ENTRY(KEY_ENUMERATE_SUB_KEYS, L"Enumerate Subkeys"), + SI_ACCESS_DEFAULT_ENTRY(KEY_NOTIFY, L"Notify"), + SI_ACCESS_DEFAULT_ENTRY(KEY_CREATE_LINK, L"Create Link"), + SI_ACCESS_DEFAULT_ENTRY(KEY_WOW64_64KEY, L"Access 64 bit key"), + SI_ACCESS_DEFAULT_ENTRY(KEY_WOW64_32KEY, L"Access 32 bit key") }; // @@ -152,8 +153,8 @@ static SI_ACCESS KeyAccessValues[MAX_KNOWN_KEY_ACCESS_VALUE] = { // #define MAX_KNOWN_SEMAPHORE_ACCESS_VALUE 2 static SI_ACCESS SemaphoreAccessValues[MAX_KNOWN_SEMAPHORE_ACCESS_VALUE] = { - { &GUID_NULL, SEMAPHORE_QUERY_STATE, L"Query State", SI_ACCESS_DEFAULT_FLAGS }, - { &GUID_NULL, SEMAPHORE_MODIFY_STATE, L"Modify State", SI_ACCESS_DEFAULT_FLAGS } + SI_ACCESS_DEFAULT_ENTRY(SEMAPHORE_QUERY_STATE, L"Query State"), + SI_ACCESS_DEFAULT_ENTRY(SEMAPHORE_MODIFY_STATE, L"Modify State") }; // @@ -161,8 +162,8 @@ static SI_ACCESS SemaphoreAccessValues[MAX_KNOWN_SEMAPHORE_ACCESS_VALUE] = { // #define MAX_KNOWN_SYMLINK_ACCESS_VALUE 2 static SI_ACCESS SymlinkAccessValues[MAX_KNOWN_SYMLINK_ACCESS_VALUE] = { - { &GUID_NULL, SYMBOLIC_LINK_QUERY, L"Link Query", SI_ACCESS_DEFAULT_FLAGS }, - { &GUID_NULL, SYMBOLIC_LINK_SET, L"Link Set", SI_ACCESS_DEFAULT_FLAGS } + SI_ACCESS_DEFAULT_ENTRY(SYMBOLIC_LINK_QUERY, L"Link Query"), + SI_ACCESS_DEFAULT_ENTRY(SYMBOLIC_LINK_SET, L"Link Set") }; // @@ -170,8 +171,8 @@ static SI_ACCESS SymlinkAccessValues[MAX_KNOWN_SYMLINK_ACCESS_VALUE] = { // #define MAX_KNOWN_TIMER_ACCESS_VALUE 2 static SI_ACCESS TimerAccessValues[MAX_KNOWN_TIMER_ACCESS_VALUE] = { - { &GUID_NULL, TIMER_QUERY_STATE, L"Query State", SI_ACCESS_DEFAULT_FLAGS }, - { &GUID_NULL, TIMER_MODIFY_STATE, L"Modify State", SI_ACCESS_DEFAULT_FLAGS } + SI_ACCESS_DEFAULT_ENTRY(TIMER_QUERY_STATE, L"Query State"), + SI_ACCESS_DEFAULT_ENTRY(TIMER_MODIFY_STATE, L"Modify State") }; // @@ -179,11 +180,11 @@ static SI_ACCESS TimerAccessValues[MAX_KNOWN_TIMER_ACCESS_VALUE] = { // #define MAX_KNOWN_JOB_ACCESS_VALUE 5 static SI_ACCESS JobAccessValues[MAX_KNOWN_JOB_ACCESS_VALUE] = { - { &GUID_NULL, JOB_OBJECT_ASSIGN_PROCESS, L"Assign Process", SI_ACCESS_DEFAULT_FLAGS }, - { &GUID_NULL, JOB_OBJECT_SET_ATTRIBUTES, L"Set Attributes", SI_ACCESS_DEFAULT_FLAGS }, - { &GUID_NULL, JOB_OBJECT_QUERY, L"Query", SI_ACCESS_DEFAULT_FLAGS }, - { &GUID_NULL, JOB_OBJECT_TERMINATE, L"Terminate", SI_ACCESS_DEFAULT_FLAGS }, - { &GUID_NULL, JOB_OBJECT_SET_SECURITY_ATTRIBUTES, L"Set Security Attributes", SI_ACCESS_DEFAULT_FLAGS } + SI_ACCESS_DEFAULT_ENTRY(JOB_OBJECT_ASSIGN_PROCESS, L"Assign Process"), + SI_ACCESS_DEFAULT_ENTRY(JOB_OBJECT_SET_ATTRIBUTES, L"Set Attributes"), + SI_ACCESS_DEFAULT_ENTRY(JOB_OBJECT_QUERY, L"Query"), + SI_ACCESS_DEFAULT_ENTRY(JOB_OBJECT_TERMINATE, L"Terminate"), + SI_ACCESS_DEFAULT_ENTRY(JOB_OBJECT_SET_SECURITY_ATTRIBUTES, L"Set Security Attributes") }; // @@ -191,7 +192,7 @@ static SI_ACCESS JobAccessValues[MAX_KNOWN_JOB_ACCESS_VALUE] = { // #define MAX_KNOWN_PORT_ACCESS_VALUE 1 static SI_ACCESS PortAccessValues[MAX_KNOWN_PORT_ACCESS_VALUE] = { - { &GUID_NULL, PORT_CONNECT, L"Connect", SI_ACCESS_DEFAULT_FLAGS } + SI_ACCESS_DEFAULT_ENTRY(PORT_CONNECT, L"Connect") }; // @@ -199,8 +200,8 @@ static SI_ACCESS PortAccessValues[MAX_KNOWN_PORT_ACCESS_VALUE] = { // #define MAX_KNOWN_SESSION_ACCESS_VALUE 2 static SI_ACCESS SessionAccessValues[MAX_KNOWN_SESSION_ACCESS_VALUE] = { - { &GUID_NULL, SESSION_QUERY_ACCESS, L"Query", SI_ACCESS_DEFAULT_FLAGS }, - { &GUID_NULL, SESSION_MODIFY_ACCESS, L"Modify", SI_ACCESS_DEFAULT_FLAGS } + SI_ACCESS_DEFAULT_ENTRY(SESSION_QUERY_ACCESS, L"Query"), + SI_ACCESS_DEFAULT_ENTRY(SESSION_MODIFY_ACCESS, L"Modify") }; // @@ -208,8 +209,8 @@ static SI_ACCESS SessionAccessValues[MAX_KNOWN_SESSION_ACCESS_VALUE] = { // #define MAX_KNOWN_IOCOMPLETION_ACCESS_VALUE 2 static SI_ACCESS IoCompletionAccessValues[MAX_KNOWN_IOCOMPLETION_ACCESS_VALUE] = { - { &GUID_NULL, IO_COMPLETION_QUERY_STATE, L"Query State", SI_ACCESS_DEFAULT_FLAGS }, - { &GUID_NULL, IO_COMPLETION_MODIFY_STATE, L"Modify State", SI_ACCESS_DEFAULT_FLAGS } + SI_ACCESS_DEFAULT_ENTRY(IO_COMPLETION_QUERY_STATE, L"Query State"), + SI_ACCESS_DEFAULT_ENTRY(IO_COMPLETION_MODIFY_STATE, L"Modify State") }; // @@ -217,8 +218,8 @@ static SI_ACCESS IoCompletionAccessValues[MAX_KNOWN_IOCOMPLETION_ACCESS_VALUE] = // #define MAX_KNOWN_MEMORYPARTITION_ACCESS_VALUE 2 static SI_ACCESS MemoryPartitionAccessValues[MAX_KNOWN_MEMORYPARTITION_ACCESS_VALUE] = { - { &GUID_NULL, MEMORY_PARTITION_QUERY_ACCESS, L"Query", SI_ACCESS_DEFAULT_FLAGS }, - { &GUID_NULL, MEMORY_PARTITION_MODIFY_ACCESS, L"Modify", SI_ACCESS_DEFAULT_FLAGS } + SI_ACCESS_DEFAULT_ENTRY(MEMORY_PARTITION_QUERY_ACCESS, L"Query"), + SI_ACCESS_DEFAULT_ENTRY(MEMORY_PARTITION_MODIFY_ACCESS, L"Modify") }; // @@ -226,20 +227,20 @@ static SI_ACCESS MemoryPartitionAccessValues[MAX_KNOWN_MEMORYPARTITION_ACCESS_VA // #define MAX_KNOWN_PROCESS_ACCESS_VALUE 14 static SI_ACCESS ProcessAccessValues[MAX_KNOWN_PROCESS_ACCESS_VALUE] = { - { &GUID_NULL, PROCESS_TERMINATE, L"Terminate", SI_ACCESS_DEFAULT_FLAGS }, - { &GUID_NULL, PROCESS_CREATE_THREAD, L"Create Thread", SI_ACCESS_DEFAULT_FLAGS }, - { &GUID_NULL, PROCESS_SET_SESSIONID, L"Set Session Id", SI_ACCESS_DEFAULT_FLAGS }, - { &GUID_NULL, PROCESS_VM_OPERATION, L"VM Operation", SI_ACCESS_DEFAULT_FLAGS }, - { &GUID_NULL, PROCESS_VM_READ, L"VM Read", SI_ACCESS_DEFAULT_FLAGS }, - { &GUID_NULL, PROCESS_VM_WRITE, L"VM Write", SI_ACCESS_DEFAULT_FLAGS }, - { &GUID_NULL, PROCESS_DUP_HANDLE, L"Duplicate Handle", SI_ACCESS_DEFAULT_FLAGS }, - { &GUID_NULL, PROCESS_CREATE_PROCESS, L"Create Process", SI_ACCESS_DEFAULT_FLAGS }, - { &GUID_NULL, PROCESS_SET_QUOTA, L"Set Quota", SI_ACCESS_DEFAULT_FLAGS }, - { &GUID_NULL, PROCESS_SET_INFORMATION, L"Set Information", SI_ACCESS_DEFAULT_FLAGS }, - { &GUID_NULL, PROCESS_QUERY_INFORMATION, L"Query Information", SI_ACCESS_DEFAULT_FLAGS }, - { &GUID_NULL, PROCESS_SUSPEND_RESUME, L"Suspend Resume", SI_ACCESS_DEFAULT_FLAGS }, - { &GUID_NULL, PROCESS_QUERY_LIMITED_INFORMATION, L"Query Limited Information", SI_ACCESS_DEFAULT_FLAGS }, - { &GUID_NULL, PROCESS_SET_LIMITED_INFORMATION, L"Set Limited Information", SI_ACCESS_DEFAULT_FLAGS } + SI_ACCESS_DEFAULT_ENTRY(PROCESS_TERMINATE, L"Terminate"), + SI_ACCESS_DEFAULT_ENTRY(PROCESS_CREATE_THREAD, L"Create Thread"), + SI_ACCESS_DEFAULT_ENTRY(PROCESS_SET_SESSIONID, L"Set Session Id"), + SI_ACCESS_DEFAULT_ENTRY(PROCESS_VM_OPERATION, L"VM Operation"), + SI_ACCESS_DEFAULT_ENTRY(PROCESS_VM_READ, L"VM Read"), + SI_ACCESS_DEFAULT_ENTRY(PROCESS_VM_WRITE, L"VM Write"), + SI_ACCESS_DEFAULT_ENTRY(PROCESS_DUP_HANDLE, L"Duplicate Handle"), + SI_ACCESS_DEFAULT_ENTRY(PROCESS_CREATE_PROCESS, L"Create Process"), + SI_ACCESS_DEFAULT_ENTRY(PROCESS_SET_QUOTA, L"Set Quota"), + SI_ACCESS_DEFAULT_ENTRY(PROCESS_SET_INFORMATION, L"Set Information"), + SI_ACCESS_DEFAULT_ENTRY(PROCESS_QUERY_INFORMATION, L"Query Information"), + SI_ACCESS_DEFAULT_ENTRY(PROCESS_SUSPEND_RESUME, L"Suspend Resume"), + SI_ACCESS_DEFAULT_ENTRY(PROCESS_QUERY_LIMITED_INFORMATION, L"Query Limited Information"), + SI_ACCESS_DEFAULT_ENTRY(PROCESS_SET_LIMITED_INFORMATION, L"Set Limited Information") }; // @@ -247,19 +248,19 @@ static SI_ACCESS ProcessAccessValues[MAX_KNOWN_PROCESS_ACCESS_VALUE] = { // #define MAX_KNOWN_THREAD_ACCESS_VALUE 13 static SI_ACCESS ThreadAccessValues[MAX_KNOWN_THREAD_ACCESS_VALUE] = { - { &GUID_NULL, THREAD_TERMINATE, L"Terminate", SI_ACCESS_DEFAULT_FLAGS }, - { &GUID_NULL, THREAD_SUSPEND_RESUME, L"Suspend Resume", SI_ACCESS_DEFAULT_FLAGS }, - { &GUID_NULL, THREAD_ALERT, L"Alert", SI_ACCESS_DEFAULT_FLAGS }, - { &GUID_NULL, THREAD_GET_CONTEXT, L"Get Context", SI_ACCESS_DEFAULT_FLAGS }, - { &GUID_NULL, THREAD_SET_CONTEXT, L"Set Context", SI_ACCESS_DEFAULT_FLAGS }, - { &GUID_NULL, THREAD_QUERY_INFORMATION, L"Query Information", SI_ACCESS_DEFAULT_FLAGS }, - { &GUID_NULL, THREAD_SET_INFORMATION, L"Set Information", SI_ACCESS_DEFAULT_FLAGS }, - { &GUID_NULL, THREAD_SET_THREAD_TOKEN, L"Set Thread Token", SI_ACCESS_DEFAULT_FLAGS }, - { &GUID_NULL, THREAD_IMPERSONATE, L"Impersonate", SI_ACCESS_DEFAULT_FLAGS }, - { &GUID_NULL, THREAD_DIRECT_IMPERSONATION, L"Direct Impersonation", SI_ACCESS_DEFAULT_FLAGS }, - { &GUID_NULL, THREAD_SET_LIMITED_INFORMATION, L"Set Limited Information", SI_ACCESS_DEFAULT_FLAGS }, - { &GUID_NULL, THREAD_QUERY_LIMITED_INFORMATION, L"Query Limited Information", SI_ACCESS_DEFAULT_FLAGS }, - { &GUID_NULL, THREAD_RESUME, L"Resume", SI_ACCESS_DEFAULT_FLAGS } + SI_ACCESS_DEFAULT_ENTRY(THREAD_TERMINATE, L"Terminate"), + SI_ACCESS_DEFAULT_ENTRY(THREAD_SUSPEND_RESUME, L"Suspend Resume"), + SI_ACCESS_DEFAULT_ENTRY(THREAD_ALERT, L"Alert"), + SI_ACCESS_DEFAULT_ENTRY(THREAD_GET_CONTEXT, L"Get Context"), + SI_ACCESS_DEFAULT_ENTRY(THREAD_SET_CONTEXT, L"Set Context"), + SI_ACCESS_DEFAULT_ENTRY(THREAD_QUERY_INFORMATION, L"Query Information"), + SI_ACCESS_DEFAULT_ENTRY(THREAD_SET_INFORMATION, L"Set Information"), + SI_ACCESS_DEFAULT_ENTRY(THREAD_SET_THREAD_TOKEN, L"Set Thread Token"), + SI_ACCESS_DEFAULT_ENTRY(THREAD_IMPERSONATE, L"Impersonate"), + SI_ACCESS_DEFAULT_ENTRY(THREAD_DIRECT_IMPERSONATION, L"Direct Impersonation"), + SI_ACCESS_DEFAULT_ENTRY(THREAD_SET_LIMITED_INFORMATION, L"Set Limited Information"), + SI_ACCESS_DEFAULT_ENTRY(THREAD_QUERY_LIMITED_INFORMATION, L"Query Limited Information"), + SI_ACCESS_DEFAULT_ENTRY(THREAD_RESUME, L"Resume") }; // @@ -268,13 +269,24 @@ static SI_ACCESS ThreadAccessValues[MAX_KNOWN_THREAD_ACCESS_VALUE] = { #define MAX_KNOWN_TOKEN_ACCESS_VALUE 9 static SI_ACCESS TokenAccessValues[MAX_KNOWN_TOKEN_ACCESS_VALUE] = { - { &GUID_NULL, TOKEN_ASSIGN_PRIMARY, L"AssignPrimary", SI_ACCESS_DEFAULT_FLAGS }, - { &GUID_NULL, TOKEN_DUPLICATE, L"Duplicate", SI_ACCESS_DEFAULT_FLAGS }, - { &GUID_NULL, TOKEN_IMPERSONATE, L"Impersonate", SI_ACCESS_DEFAULT_FLAGS }, - { &GUID_NULL, TOKEN_QUERY, L"Query", SI_ACCESS_DEFAULT_FLAGS }, - { &GUID_NULL, TOKEN_QUERY_SOURCE, L"Query Source", SI_ACCESS_DEFAULT_FLAGS }, - { &GUID_NULL, TOKEN_ADJUST_PRIVILEGES, L"Adjust Privileges", SI_ACCESS_DEFAULT_FLAGS }, - { &GUID_NULL, TOKEN_ADJUST_GROUPS, L"Adjust Groups", SI_ACCESS_DEFAULT_FLAGS }, - { &GUID_NULL, TOKEN_ADJUST_DEFAULT, L"Adjust Default", SI_ACCESS_DEFAULT_FLAGS }, - { &GUID_NULL, TOKEN_ADJUST_SESSIONID, L"Adjust SessionId", SI_ACCESS_DEFAULT_FLAGS } + SI_ACCESS_DEFAULT_ENTRY(TOKEN_ASSIGN_PRIMARY, L"Assign Primary"), + SI_ACCESS_DEFAULT_ENTRY(TOKEN_DUPLICATE, L"Duplicate"), + SI_ACCESS_DEFAULT_ENTRY(TOKEN_IMPERSONATE, L"Impersonate"), + SI_ACCESS_DEFAULT_ENTRY(TOKEN_QUERY, L"Query"), + SI_ACCESS_DEFAULT_ENTRY(TOKEN_QUERY_SOURCE, L"Query Source"), + SI_ACCESS_DEFAULT_ENTRY(TOKEN_ADJUST_PRIVILEGES, L"Adjust Privileges"), + SI_ACCESS_DEFAULT_ENTRY(TOKEN_ADJUST_GROUPS, L"Adjust Groups"), + SI_ACCESS_DEFAULT_ENTRY(TOKEN_ADJUST_DEFAULT, L"Adjust Default"), + SI_ACCESS_DEFAULT_ENTRY(TOKEN_ADJUST_SESSIONID, L"Adjust SessionId") +}; + +#define MAX_KNOWN_TRANSACTION_ACCESS_VALUE 7 +static SI_ACCESS TransactionAccessValues[MAX_KNOWN_TRANSACTION_ACCESS_VALUE] = { + SI_ACCESS_DEFAULT_ENTRY(TRANSACTION_QUERY_INFORMATION, L"Query Information"), + SI_ACCESS_DEFAULT_ENTRY(TRANSACTION_SET_INFORMATION, L"Set Information"), + SI_ACCESS_DEFAULT_ENTRY(TRANSACTION_ENLIST, L"Enlist"), + SI_ACCESS_DEFAULT_ENTRY(TRANSACTION_COMMIT, L"Commit"), + SI_ACCESS_DEFAULT_ENTRY(TRANSACTION_ROLLBACK, L"Rollback"), + SI_ACCESS_DEFAULT_ENTRY(TRANSACTION_PROPAGATE, L"Propagate"), + SI_ACCESS_DEFAULT_ENTRY(TRANSACTION_RIGHT_RESERVED1, L"Right Reserved1") }; diff --git a/Source/WinObjEx64/props/propToken.c b/Source/WinObjEx64/props/propToken.c index c45c07db..1cb88f2f 100644 --- a/Source/WinObjEx64/props/propToken.c +++ b/Source/WinObjEx64/props/propToken.c @@ -4,9 +4,9 @@ * * TITLE: PROPTOKEN.C * -* VERSION: 1.94 +* VERSION: 2.00 * -* DATE: 31 May 2022 +* DATE: 19 Jun 2022 * * THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF * ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED @@ -182,7 +182,7 @@ VOID TokenPageListInfo( WCHAR szBuffer[MAX_PATH], szPrivName[MAX_PATH + 1]; - if (Context->TypeIndex == ObjectTypeProcess) { + if (Context->ObjectTypeIndex == ObjectTypeProcess) { DesiredAccessLv1 = PROCESS_QUERY_INFORMATION; DesiredAccessLv2 = PROCESS_QUERY_LIMITED_INFORMATION; } @@ -202,7 +202,7 @@ VOID TokenPageListInfo( return; } - if (Context->TypeIndex == ObjectTypeProcess) { + if (Context->ObjectTypeIndex == ObjectTypeProcess) { Status = supOpenProcessTokenEx(ObjectHandle, &TokenHandle); if (!NT_SUCCESS(Status)) @@ -466,7 +466,7 @@ VOID TokenPageShowAdvancedProperties( { OBJECT_ATTRIBUTES ObjectAttributes = RTL_INIT_OBJECT_ATTRIBUTES((PUNICODE_STRING)NULL, 0); PROP_UNNAMED_OBJECT_INFO TokenObject; - PROP_DIALOG_CREATE_SETTINGS propSettings; + PROP_CONFIG propConfig; LPWSTR FormatStringTokenProcess = TEXT("Process Token, PID:%llu"); LPWSTR FormatStringTokenThread = TEXT("Thread Token, PID:%llu, TID:%llu"); @@ -474,10 +474,12 @@ VOID TokenPageShowAdvancedProperties( HANDLE TokenHandle = NULL; WCHAR szFakeName[MAX_PATH + 1]; + UNICODE_STRING usObjectName; + // // Only one token properties dialog at the same time allowed. // - ENSURE_DIALOG_UNIQUE(g_PsTokenWindow); + supCloseKnownPropertiesDialog(propGetTokenWindow()); RtlSecureZeroMemory(&TokenObject, sizeof(PROP_UNNAMED_OBJECT_INFO)); @@ -502,7 +504,7 @@ VOID TokenPageShowAdvancedProperties( NtClose(TokenHandle); } - RtlSecureZeroMemory(&propSettings, sizeof(propSettings)); + RtlSecureZeroMemory(&propConfig, sizeof(propConfig)); if (TokenObject.IsThreadToken) { @@ -521,13 +523,16 @@ VOID TokenPageShowAdvancedProperties( TokenObject.ClientId.UniqueProcess); } + + RtlInitUnicodeString(&usObjectName, szFakeName); - propSettings.hwndParent = hwndDlg; - propSettings.lpObjectName = szFakeName; - propSettings.lpObjectType = OBTYPE_NAME_TOKEN; - propSettings.UnnamedObject = &TokenObject; + propConfig.hwndParent = hwndDlg; + propConfig.NtObjectName = &usObjectName; + propConfig.ObjectTypeIndex = ObjectTypeToken; + propConfig.ContextType = propUnnamed; + propConfig.u1.UnnamedObject = &TokenObject; - propCreateDialog(&propSettings); + propCreateDialog(&propConfig); } /* @@ -602,8 +607,7 @@ INT_PTR TokenPageDialogOnCommand( TokenPageShowAdvancedProperties(hwndDlg); Result = 1; break; - default: - break; + } return Result; @@ -633,11 +637,11 @@ INT_PTR TokenPageDialogOnInit( // SetProp(hwndDlg, T_TOKEN_PROP_CID_PID, - Context->UnnamedObjectInfo.ClientId.UniqueProcess); + Context->u1.UnnamedObjectInfo.ClientId.UniqueProcess); SetProp(hwndDlg, T_TOKEN_PROP_CID_TID, - Context->UnnamedObjectInfo.ClientId.UniqueThread); + Context->u1.UnnamedObjectInfo.ClientId.UniqueThread); SetProp(hwndDlg, T_TOKEN_PROP_TYPE, @@ -696,7 +700,8 @@ INT_PTR CALLBACK TokenPageDialogProc( break; default: - return 0; + return FALSE; } - return 1; + + return TRUE; } diff --git a/Source/WinObjEx64/props/propToken.h b/Source/WinObjEx64/props/propToken.h deleted file mode 100644 index 25f7e8f5..00000000 --- a/Source/WinObjEx64/props/propToken.h +++ /dev/null @@ -1,25 +0,0 @@ -/******************************************************************************* -* -* (C) COPYRIGHT AUTHORS, 2019 - 2021 -* -* TITLE: PROPTOKEN.H -* -* VERSION: 1.90 -* -* DATE: 17 May 2021 -* -* Common header file for Token property sheet. -* -* THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF -* ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED -* TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A -* PARTICULAR PURPOSE. -* -*******************************************************************************/ -#pragma once - -INT_PTR CALLBACK TokenPageDialogProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam); diff --git a/Source/WinObjEx64/props/propType.c b/Source/WinObjEx64/props/propType.c index 182377f0..d7dabb2a 100644 --- a/Source/WinObjEx64/props/propType.c +++ b/Source/WinObjEx64/props/propType.c @@ -1,12 +1,12 @@ /******************************************************************************* * -* (C) COPYRIGHT AUTHORS, 2015 - 2021 +* (C) COPYRIGHT AUTHORS, 2015 - 2022 * * TITLE: PROPTYPE.C * -* VERSION: 1.92 +* VERSION: 2.00 * -* DATE: 07 Dec 2021 +* DATE: 19 Jun 2022 * * THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF * ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED @@ -531,83 +531,61 @@ VOID propSetTypeListView( * Used if object dumped info not available (restricted user, no driver etc). * */ +_Success_(return) BOOL propQueryTypeInfo( - _In_ LPWSTR lpObjectType, - _Inout_ POBJECT_TYPE_COMPATIBLE pObjectTypeDump + _In_ PUNICODE_STRING ObjectType, + _Out_ POBJECT_TYPE_COMPATIBLE Information ) { BOOL bResult = FALSE; ULONG i; - SIZE_T sz; - LPWSTR lpType; POBJECT_TYPES_INFORMATION pObjectTypes = NULL; POBJECT_TYPE_INFORMATION pObject; - if (lpObjectType == NULL) - return bResult; + pObjectTypes = (POBJECT_TYPES_INFORMATION)supGetObjectTypesInfo(); + if (pObjectTypes == NULL) + return FALSE; - __try { + pObject = OBJECT_TYPES_FIRST_ENTRY(pObjectTypes); - do { - pObjectTypes = (POBJECT_TYPES_INFORMATION)supGetObjectTypesInfo(); - if (pObjectTypes == NULL) { - break; - } + __try { - // - // Warning: older Wine/Staging incorrectly implement memory structure layout for this structure and therefore will crash. - // - - pObject = OBJECT_TYPES_FIRST_ENTRY(pObjectTypes); - - for (i = 0; i < pObjectTypes->NumberOfTypes; i++) { - - sz = (pObject->TypeName.MaximumLength) + sizeof(UNICODE_NULL); - lpType = (LPWSTR)supHeapAlloc(sz); - if (lpType) { - _strncpy(lpType, - sz / sizeof(WCHAR), - pObject->TypeName.Buffer, - pObject->TypeName.Length / sizeof(WCHAR)); - - if (_strcmpi(lpType, lpObjectType) == 0) { - pObjectTypeDump->TotalNumberOfHandles = pObject->TotalNumberOfHandles; - pObjectTypeDump->TotalNumberOfObjects = pObject->TotalNumberOfObjects; - pObjectTypeDump->TypeInfo.InvalidAttributes = pObject->InvalidAttributes; - pObjectTypeDump->TypeInfo.GenericMapping = pObject->GenericMapping; - pObjectTypeDump->TypeInfo.ValidAccessMask = pObject->ValidAccessMask; - pObjectTypeDump->TypeInfo.DefaultNonPagedPoolCharge = pObject->DefaultNonPagedPoolCharge; - pObjectTypeDump->TypeInfo.DefaultPagedPoolCharge = pObject->DefaultPagedPoolCharge; - pObjectTypeDump->HighWaterNumberOfHandles = pObject->HighWaterNumberOfHandles; - pObjectTypeDump->HighWaterNumberOfObjects = pObject->HighWaterNumberOfObjects; - pObjectTypeDump->TypeInfo.PoolType = (POOL_TYPE)pObject->PoolType; - if (pObject->SecurityRequired) { - SET_BIT(pObjectTypeDump->TypeInfo.ObjectTypeFlags, 3); - } - if (pObject->MaintainHandleCount) { - SET_BIT(pObjectTypeDump->TypeInfo.ObjectTypeFlags, 4); - } - bResult = TRUE; - } - supHeapFree(lpType); - if (bResult) { - break; - } + // + // Warning: older Wine/Staging incorrectly implement memory structure layout for this structure and therefore will crash. + // + for (i = 0; i < pObjectTypes->NumberOfTypes; i++) { + + if (RtlEqualUnicodeString(ObjectType, &pObject->TypeName, TRUE)) { + Information->TotalNumberOfHandles = pObject->TotalNumberOfHandles; + Information->TotalNumberOfObjects = pObject->TotalNumberOfObjects; + Information->TypeInfo.InvalidAttributes = pObject->InvalidAttributes; + Information->TypeInfo.GenericMapping = pObject->GenericMapping; + Information->TypeInfo.ValidAccessMask = pObject->ValidAccessMask; + Information->TypeInfo.DefaultNonPagedPoolCharge = pObject->DefaultNonPagedPoolCharge; + Information->TypeInfo.DefaultPagedPoolCharge = pObject->DefaultPagedPoolCharge; + Information->HighWaterNumberOfHandles = pObject->HighWaterNumberOfHandles; + Information->HighWaterNumberOfObjects = pObject->HighWaterNumberOfObjects; + Information->TypeInfo.PoolType = (POOL_TYPE)pObject->PoolType; + if (pObject->SecurityRequired) { + SET_BIT(Information->TypeInfo.ObjectTypeFlags, 3); + } + if (pObject->MaintainHandleCount) { + SET_BIT(Information->TypeInfo.ObjectTypeFlags, 4); } - pObject = OBJECT_TYPES_NEXT_ENTRY(pObject); + bResult = TRUE; + break; } - } while (FALSE); - - if (pObjectTypes) { - supHeapFree(pObjectTypes); + pObject = OBJECT_TYPES_NEXT_ENTRY(pObject); } + } __except (EXCEPTION_EXECUTE_HANDLER) { supReportAbnormalTermination(__FUNCTIONW__); return FALSE; } + supHeapFree(pObjectTypes); return bResult; } @@ -625,25 +603,27 @@ VOID propSetTypeInfo( _In_ HWND hwndDlg ) { - BOOL bOkay; - WOBJ_OBJECT_TYPE RealTypeIndex; - INT i; - POBJINFO pObject = NULL; - LPCWSTR lpTypeDescription = NULL; - OBJECT_TYPE_COMPATIBLE ObjectTypeDump; - WCHAR szConvertBuffer[64]; - WCHAR szType[MAX_PATH * 2]; + BOOL bOkay; + WOBJ_OBJECT_TYPE RealTypeIndex; + INT i; + LPCWSTR lpTypeDescription = NULL; + OBJECT_TYPE_COMPATIBLE ObjectTypeDump; + WCHAR szConvertBuffer[64]; + WCHAR szType[MAX_PATH * 2]; + + POBEX_OBJECT_INFORMATION pObject = NULL; + UNICODE_STRING usName; + + lpTypeDescription = Context->TypeDescription->Name; RealTypeIndex = Context->ShadowTypeDescription->Index; - if ((RealTypeIndex > ObjectTypeUnknown)) { + if (RealTypeIndex > ObjectTypeUnknown) { RealTypeIndex = ObjectTypeUnknown; } //if type is not known set it description to it type name - if (RealTypeIndex == ObjectTypeUnknown) { - lpTypeDescription = Context->lpObjectType; - } - else { + if (RealTypeIndex != ObjectTypeUnknown) { + //set description RtlSecureZeroMemory(&szType, sizeof(szType)); if (LoadString( @@ -654,9 +634,6 @@ VOID propSetTypeInfo( { lpTypeDescription = szType; } - else { - lpTypeDescription = Context->lpObjectType; - } } @@ -671,19 +648,22 @@ VOID propSetTypeInfo( // bOkay = FALSE; RtlSecureZeroMemory(&ObjectTypeDump, sizeof(ObjectTypeDump)); - if (Context->IsType) { + if (Context->ObjectTypeIndex == ObjectTypeType) { //query object by name, thus were giving us proper object type dump - pObject = ObQueryObject(T_OBJECTTYPES, Context->lpObjectName); + pObject = ObQueryObjectInDirectory( + &Context->NtObjectName, + ObGetPredefinedUnicodeString(OBP_OBTYPES)); //cannot query, no driver or other error, try second method if (pObject == NULL) { - bOkay = propQueryTypeInfo(Context->lpObjectName, &ObjectTypeDump); + bOkay = propQueryTypeInfo(&Context->NtObjectName, &ObjectTypeDump); } //if type is not known set it description to it type name - if (RealTypeIndex == ObjectTypeUnknown) - lpTypeDescription = Context->lpObjectName; + if (RealTypeIndex == ObjectTypeUnknown) { + lpTypeDescription = Context->NtObjectName.Buffer; + } else { //set description RtlSecureZeroMemory(&szType, sizeof(szType)); @@ -696,7 +676,7 @@ VOID propSetTypeInfo( lpTypeDescription = szType; } else { - lpTypeDescription = Context->lpObjectType; + lpTypeDescription = Context->TypeDescription->Name; } } } @@ -705,13 +685,16 @@ VOID propSetTypeInfo( // // Query object type object. // - pObject = ObQueryObject(T_OBJECTTYPES, Context->lpObjectType); + pObject = ObQueryObjectInDirectory( + &Context->NtObjectName, + ObGetPredefinedUnicodeString(OBP_OBTYPES)); // // If we cannot query because of no driver or other error, try second method. // if (pObject == NULL) { - bOkay = propQueryTypeInfo(Context->lpObjectType, &ObjectTypeDump); + RtlInitUnicodeString(&usName, Context->TypeDescription->Name); + bOkay = propQueryTypeInfo(&usName, &ObjectTypeDump); } } diff --git a/Source/WinObjEx64/props/propType.h b/Source/WinObjEx64/props/propType.h deleted file mode 100644 index b2e01029..00000000 --- a/Source/WinObjEx64/props/propType.h +++ /dev/null @@ -1,25 +0,0 @@ -/******************************************************************************* -* -* (C) COPYRIGHT AUTHORS, 2015 - 2018 -* -* TITLE: PROPTYPE.H -* -* VERSION: 1.52 -* -* DATE: 08 Jan 2018 -* -* Common header file for Type property sheet. -* -* THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF -* ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED -* TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A -* PARTICULAR PURPOSE. -* -*******************************************************************************/ -#pragma once - -INT_PTR CALLBACK TypePropDialogProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam); diff --git a/Source/WinObjEx64/props/propTypeConsts.h b/Source/WinObjEx64/props/propTypeConsts.h index 062768e5..e8549366 100644 --- a/Source/WinObjEx64/props/propTypeConsts.h +++ b/Source/WinObjEx64/props/propTypeConsts.h @@ -4,9 +4,9 @@ * * TITLE: PROPTYPECONSTS.H * -* VERSION: 1.93 +* VERSION: 2.00 * -* DATE: 24 Mar 2022 +* DATE: 19 Jun 2022 * * Consts header file for Type property sheet. * diff --git a/Source/WinObjEx64/props/propObjectDump.h b/Source/WinObjEx64/props/props.h similarity index 64% rename from Source/WinObjEx64/props/propObjectDump.h rename to Source/WinObjEx64/props/props.h index ddb23552..e92bdbf5 100644 --- a/Source/WinObjEx64/props/propObjectDump.h +++ b/Source/WinObjEx64/props/props.h @@ -2,13 +2,13 @@ * * (C) COPYRIGHT AUTHORS, 2015 - 2022 * -* TITLE: PROPOBJECTDUMP.H +* TITLE: PROPS.H * -* VERSION: 1.93 +* VERSION: 2.00 * -* DATE: 13 May 2022 +* DATE: 19 Jun 2022 * -* Common header file for the object dump support. +* Common header file for properties dialog definitions. * * THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF * ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED @@ -16,14 +16,79 @@ * PARTICULAR PURPOSE. * *******************************************************************************/ + #pragma once +// +// Dialog procs. +// + +INT_PTR CALLBACK AlpcPortListDialogProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam); + +INT_PTR CALLBACK BasicPropDialogProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam); + +INT_PTR CALLBACK DesktopListDialogProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam); + +INT_PTR CALLBACK DriverRegistryDialogProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam); + INT_PTR CALLBACK ObjectDumpDialogProc( _In_ HWND hwndDlg, _In_ UINT uMsg, _In_ WPARAM wParam, _In_ LPARAM lParam); +INT_PTR CALLBACK ProcessListDialogProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam); + +INT_PTR CALLBACK SectionPropertiesDialogProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam); + +INT_PTR CALLBACK TokenPageDialogProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam); + +INT_PTR CALLBACK TypePropDialogProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam); + +// +// Security page. +// +HPROPSHEETPAGE propSecurityCreatePage( + _In_ PROP_OBJECT_INFO* Context, + _In_ POPENOBJECTMETHOD OpenObjectMethod, + _In_opt_ PCLOSEOBJECTMETHOD CloseObjectMethod, + _In_ ULONG psiFlags); + +// +// Object dump +// HTREEITEM propObDumpUlong( _In_ HWND TreeList, _In_ HTREEITEM hParent, @@ -111,4 +176,4 @@ VOID propObDumpUnicodeString( _In_ HTREEITEM hParent, _In_ LPWSTR StringName, _In_ PUNICODE_STRING InputString, - _In_ BOOLEAN IsKernelPtr); + _In_ BOOLEAN IsKernelPointer); diff --git a/Source/WinObjEx64/resource.h b/Source/WinObjEx64/resource.h index b9ebfe6c3af9d9f3c771c42683f60c3c3b965de1..8a51f689491d92eed8c63cbd03d63e96252998e3 100644 GIT binary patch delta 1397 zcmb7EOHWf_5dCgjZULdZOL_Dq)?y7pTLLIVEwq(Nffg=ASWrMEqKN^cgh){&1~y2+ z!-_y*;SWIX)vyI2i!GE1^LScHXGCkwIweM?vs zDpKfL2x2Gd{J;AJZR+1FrCXt3o;+E_*fg9wu0oH-0#>jBHKEwh{FAdaqC|^id@bj zkpzzOF)w275`3MoaIPYS>zF7bE=5Irn;0uR%GpKc`N-Os+ot55uD9fb6#gJ_9o#Ou z?fh1DMHd9)p|pLOt(<^p0<4?l@{;0{=MT%_(MKbUa(uE*K;L+VeD%3rkT<6C1=%va ztik6-(WcA*@kK}^m+PS(H&YG|^_#DQij|&}LRi-H2vN%jWjzVyr}}*Gh)MJJs!BPc z>?A}dS->$b9^}z1SyRm%QGYVROEvNd=1uCMwU=3xkCQo>3!az}?`L{BPv!r3B+^J3 zNuifml*b|Acz2UVX5ZLib+ tc*foi+lfS7!(4LhYK4GD#Rf#S6=`C delta 396 zcmXAj%S!@b6ve->kFkfD#4uRcU}|diBHOeGvG;>snze`sjTT`LErOs(r-%xQm^?x) zB&H~H;X?YhVvm0yY0;u}t5z*rMPqF)zso)MoRfOdsSmnb)Q&=PIX+fJd^wC<7oMDL zENV~jkn4=>t>v|Wg~uFv6o+f^bLPQgfMO+JNx+PJ!lA8hs6jJMrt6`UQ9K8>66h(Z zm^G;CO0pt&Y(at=N@`qsq4%@jRZz}E&WlcS6LO0ba#%CCF)=5kU`Q*2DBL@ zUIgkJp+s!VxKyZxaBhQDtc1KqnYwYU(~px;GDAY>b#?x=h;3TolMQTgv$YJqumQg9 zX~gDDSih8ziwn%ZbINH7BTL44uA-kzkqOdArZj^Ngn0@{Zf6-;;E<6GTFI-pohiG1 z4*A`3E!~NqJqs)BWhvf|79c4CJ2+nEp}0(lC3)sfUevJeTQ`RRj~P;0z)9K*@#ddD R)9oName) - supHeapFree(SdViewContext->Name); - if (SdViewContext->Directory) - supHeapFree(SdViewContext->Directory); + supFreeDuplicatedUnicodeString(g_obexHeap, &Context->NtObjectDirectory, FALSE); + supFreeDuplicatedUnicodeString(g_obexHeap, &Context->NtObjectName, FALSE); - supHeapFree(SdViewContext); + supHeapFree(Context); } /* @@ -109,47 +110,26 @@ VOID FreeSDViewContext( * */ SDVIEW_CONTEXT* AllocateSDViewContext( - _In_ LPWSTR ObjectDirectory, - _In_opt_ LPWSTR ObjectName, _In_ WOBJ_OBJECT_TYPE ObjectType ) { SDVIEW_CONTEXT* ctx; - SIZE_T nLen, nNameLen = 0; - - nLen = _strlen(ObjectDirectory); - if (nLen == 0) - return NULL; - - if (ObjectName) { - nNameLen = _strlen(ObjectName); - if (nNameLen == 0) - return NULL; - } ctx = (SDVIEW_CONTEXT*)supHeapAlloc(sizeof(SDVIEW_CONTEXT)); if (ctx == NULL) return NULL; - ctx->Directory = (LPWSTR)supHeapAlloc((1 + nLen) * sizeof(WCHAR)); - if (ctx->Directory == NULL) { - FreeSDViewContext(ctx); + ctx->Type = ObjectType; + + if (!supGetCurrentObjectPath(FALSE, &ctx->NtObjectDirectory)) { + supHeapFree(ctx); return NULL; } - _strcpy(ctx->Directory, ObjectDirectory); - - ctx->Type = ObjectType; - - if (ObjectName) { - - ctx->Name = (LPWSTR)supHeapAlloc((1 + nNameLen) * sizeof(WCHAR)); - if (ctx->Name == NULL) { - FreeSDViewContext(ctx); - return NULL; - } - - _strcpy(ctx->Name, ObjectName); + if (!supGetCurrentObjectName(&ctx->NtObjectName)) { + supFreeDuplicatedUnicodeString(g_obexHeap, &ctx->NtObjectDirectory, FALSE); + supHeapFree(ctx); + return NULL; } return ctx; @@ -744,8 +724,8 @@ NTSTATUS SDViewDumpObjectSecurity( ntStatus = supOpenNamedObjectByType(&hObject, Context->Type, - Context->Directory, - Context->Name, + &Context->NtObjectDirectory, + &Context->NtObjectName, READ_CONTROL); if (!NT_SUCCESS(ntStatus)) @@ -841,7 +821,10 @@ VOID SDViewInitControls( INT i; HWND aclList = GetDlgItem(hwndDlg, IDC_SDVIEW_LIST); HWND sidOwner = GetDlgItem(hwndDlg, IDC_SDVIEW_OWNER); - HWND okButton = GetDlgItem(hwndDlg, IDOK); + + UNICODE_STRING objectName, normalizedName; + LPWSTR caption; + ULONG captionLength; // // Set listview style flags and theme. @@ -879,8 +862,35 @@ VOID SDViewInitControls( GetClientRect(hwndDlg, &Context->WindowRect); GetWindowRect(aclList, &Context->ListRect); - GetWindowRect(okButton, &Context->ButtonRect); - ScreenToClient(hwndDlg, (LPPOINT)&Context->ButtonRect); + + // + // Set dialog caption. + // + if (supCreateObjectPathFromElements(&Context->NtObjectName, + &Context->NtObjectDirectory, + &objectName, + TRUE)) + { + if (supNormalizeUnicodeStringForDisplay(g_obexHeap, &objectName, &normalizedName)) { + + captionLength = normalizedName.Length + MAX_PATH; + caption = (LPWSTR)supHeapAlloc(captionLength); + if (caption) { + + RtlStringCchPrintfSecure(caption, + captionLength / sizeof(WCHAR), + TEXT("Security Descriptor: %ws"), + normalizedName.Buffer); + + SetWindowText(hwndDlg, caption); + + supHeapFree(caption); + } + supFreeUnicodeString(g_obexHeap, &normalizedName); + } + + supFreeUnicodeString(g_obexHeap, &objectName); + } } /* @@ -939,7 +949,6 @@ VOID SDViewOnResize( ) { HWND hwndList = GetDlgItem(hwndDlg, IDC_SDVIEW_LIST); - HWND hwndButton = GetDlgItem(hwndDlg, IDOK); WORD dlgWidth = LOWORD(lParam), dlgHeight = HIWORD(lParam); INT dx, dy; @@ -952,15 +961,6 @@ VOID SDViewOnResize( dlgHeight - dy - Context->ListRect.top, SWP_NOMOVE); - dx = Context->WindowRect.right - Context->ButtonRect.left; - dy = Context->WindowRect.bottom - Context->ButtonRect.top; - - SetWindowPos(hwndButton, NULL, - dlgWidth - dx, - dlgHeight - dy, - 0, 0, - SWP_NOSIZE); - SendMessage(Context->StatusBar, WM_SIZE, 0, 0); RedrawWindow(hwndDlg, NULL, 0, RDW_ERASE | RDW_INVALIDATE | RDW_ERASENOW); } @@ -984,10 +984,8 @@ VOID SDViewDialogOnInit( SDVIEW_CONTEXT* dlgContext; ENUMCHILDWNDDATA wndData; - supCenterWindow(hwndDlg); - if (lParam == 0) - return; - + SDViewDialogWindow = hwndDlg; + supCenterWindowSpecifyParent(hwndDlg, g_hwndMain); dlgContext = (SDVIEW_CONTEXT*)lParam; SetProp(hwndDlg, T_DLGCONTEXT, (HANDLE)lParam); @@ -1012,7 +1010,6 @@ VOID SDViewDialogOnInit( SDViewInitControls(hwndDlg, dlgContext); - // // Dump object security information. // @@ -1085,6 +1082,11 @@ INT_PTR CALLBACK SDViewDialogProc( } break; + case WM_DESTROY: + SDViewDialogWindow = NULL; + PostQuitMessage(0); + break; + case WM_CLOSE: dlgContext = (SDVIEW_CONTEXT*)RemoveProp(hwndDlg, T_DLGCONTEXT); if (dlgContext) { @@ -1108,7 +1110,6 @@ INT_PTR CALLBACK SDViewDialogProc( switch (GET_WM_COMMAND_ID(wParam, lParam)) { case IDCANCEL: - case IDOK: SendMessage(hwndDlg, WM_CLOSE, 0, 0); break; @@ -1120,8 +1121,6 @@ INT_PTR CALLBACK SDViewDialogProc( dlgContext->iColumnHit); } break; - default: - break; } default: @@ -1132,102 +1131,53 @@ INT_PTR CALLBACK SDViewDialogProc( } /* -* SDViewSetCaptionTextFormatted +* SDViewDialogWorkerThread * * Purpose: * -* Set dialog window caption text. +* Create and initialize ViewSecurityDescriptor Dialog. * */ -VOID SDViewSetCaptionTextFormatted( - _In_ HWND DialogWindow, - _In_ LPWSTR ObjectDirectory, - _In_opt_ LPWSTR ObjectName +DWORD SDViewDialogWorkerThread( + _In_ PVOID Parameter ) { - LPWSTR lpText; - SIZE_T cch, l; - - cch = MAX_PATH + _strlen(ObjectDirectory); - if (ObjectName) cch += _strlen(ObjectName); + BOOL bResult; + MSG message; + HWND hwnd; + SDVIEW_CONTEXT* context = (SDVIEW_CONTEXT*)Parameter; - lpText = (LPWSTR)supHeapAlloc(cch * sizeof(WCHAR)); - if (lpText) { - - _strcpy(lpText, TEXT("Security Descriptor (")); - _strcat(lpText, ObjectDirectory); - l = _strlen(ObjectDirectory); - if (ObjectDirectory[l - 1] != L'\\') { - _strcat(lpText, TEXT("\\")); - } - if (ObjectName) { - _strcat(lpText, ObjectName); - } - _strcat(lpText, TEXT(")")); - SetWindowText(DialogWindow, lpText); - supHeapFree(lpText); - } -} + hwnd = CreateDialogParam(g_WinObj.hInstance, + MAKEINTRESOURCE(IDD_DIALOG_SDVIEW), + 0, + (DLGPROC)&SDViewDialogProc, + (LPARAM)context); -/* -* SDViewSetCaption -* -* Purpose: -* -* Format and set dialog window caption text as "Security Descriptor (ObjectDirectory\ObjectName)". -* -*/ -VOID SDViewSetCaption( - _In_ HWND DialogWindow, - _In_ LPWSTR ObjectDirectory, - _In_ LPWSTR ObjectName, - _In_ WOBJ_OBJECT_TYPE ObjectType -) -{ - SIZE_T i, l, rdirLen, ldirSz; - LPWSTR SingleDirName, ParentDir; + supSetFastEvent(&SDViewDialogInitializedEvent); + do { - if (ObjectType == ObjectTypeDirectory) { + bResult = GetMessage(&message, NULL, 0, 0); + if (bResult == -1) + break; - // - // Root case. - // - if (_strcmpi(ObjectName, KM_OBJECTS_ROOT_DIRECTORY) == 0) { - SDViewSetCaptionTextFormatted(DialogWindow, ObjectDirectory, NULL); - return; + if (!IsDialogMessage(hwnd, &message)) { + TranslateMessage(&message); + DispatchMessage(&message); } - } - - // - // Extract parent directory name, handle self case. - // - l = 0; - rdirLen = _strlen(ObjectDirectory); - for (i = 0; i < rdirLen; i++) { - if (ObjectDirectory[i] == L'\\') - l = i + 1; - } - - SingleDirName = &ObjectDirectory[l]; + } while (bResult != 0); - if (_strcmpi(SingleDirName, ObjectName) == 0) { - - ldirSz = rdirLen * sizeof(WCHAR) + sizeof(UNICODE_NULL); - ParentDir = (LPWSTR)supHeapAlloc(ldirSz); - if (ParentDir) { - if (l == 1) l++; - supCopyMemory(ParentDir, ldirSz, ObjectDirectory, (l - 1) * sizeof(WCHAR)); - SDViewSetCaptionTextFormatted(DialogWindow, ParentDir, ObjectName); - supHeapFree(ParentDir); - } + supResetFastEvent(&SDViewDialogInitializedEvent); + if (SDViewDialogThreadHandle) { + NtClose(SDViewDialogThreadHandle); + SDViewDialogThreadHandle = NULL; } - else { - SDViewSetCaptionTextFormatted(DialogWindow, ObjectDirectory, ObjectName); - } + supSetFastEvent(&SDViewDialogFinalizedEvent); + + return 0; } /* @@ -1235,41 +1185,26 @@ VOID SDViewSetCaption( * * Purpose: * -* Create and initialize ViewSecurityDescriptor Dialog. +* Create dialog worker thread. * */ VOID SDViewDialogCreate( - _In_ HWND ParentWindow, - _In_ LPWSTR ObjectDirectory, - _In_ LPWSTR ObjectName, _In_ WOBJ_OBJECT_TYPE ObjectType ) { - HWND hwndDlg; - SDVIEW_CONTEXT* SDViewContext; + SDVIEW_CONTEXT* context; - if (ObjectDirectory == NULL || ObjectName == NULL) - return; - - SDViewContext = AllocateSDViewContext(ObjectDirectory, - ObjectName, - ObjectType); - - if (SDViewContext == NULL) - return; - - hwndDlg = CreateDialogParam(g_WinObj.hInstance, - MAKEINTRESOURCE(IDD_DIALOG_SDVIEW), - ParentWindow, - (DLGPROC)&SDViewDialogProc, - (LPARAM)SDViewContext); + if (SDViewDialogThreadHandle) { + PostMessage(SDViewDialogWindow, WM_CLOSE, 0, 0); + supWaitForFastEvent(&SDViewDialogFinalizedEvent, NULL); + } - if (hwndDlg) { + context = AllocateSDViewContext(ObjectType); + if (context) { - SDViewSetCaption(hwndDlg, ObjectDirectory, ObjectName, ObjectType); + supInitFastEvent(&SDViewDialogFinalizedEvent); + SDViewDialogThreadHandle = supCreateDialogWorkerThread(SDViewDialogWorkerThread, context, 0); + supWaitForFastEvent(&SDViewDialogInitializedEvent, NULL); } - else { - supHeapFree(SDViewContext); - } } diff --git a/Source/WinObjEx64/sdviewDlg.h b/Source/WinObjEx64/sdviewDlg.h deleted file mode 100644 index a3e753a6..00000000 --- a/Source/WinObjEx64/sdviewDlg.h +++ /dev/null @@ -1,25 +0,0 @@ -/******************************************************************************* -* -* (C) COPYRIGHT AUTHORS, 2020 - 2021 -* -* TITLE: SDVIEWDLG.H -* -* VERSION: 1.88 -* -* DATE: 05 Dec 2020 -* -* Common header file for the SecurityDescriptor View Dialog. -* -* THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF -* ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED -* TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A -* PARTICULAR PURPOSE. -* -*******************************************************************************/ -#pragma once - -VOID SDViewDialogCreate( - _In_ HWND ParentWindow, - _In_ LPWSTR ObjectDirectory, - _In_ LPWSTR ObjectName, - _In_ WOBJ_OBJECT_TYPE ObjectType); diff --git a/Source/WinObjEx64/sup.c b/Source/WinObjEx64/sup/sup.c similarity index 85% rename from Source/WinObjEx64/sup.c rename to Source/WinObjEx64/sup/sup.c index 685ed234..f68a7e9f 100644 --- a/Source/WinObjEx64/sup.c +++ b/Source/WinObjEx64/sup/sup.c @@ -4,9 +4,9 @@ * * TITLE: SUP.C * -* VERSION: 1.94 +* VERSION: 2.00 * -* DATE: 05 Jun 2022 +* DATE: 19 Jun 2022 * * THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF * ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED @@ -16,24 +16,22 @@ *******************************************************************************/ #include "global.h" #include "treelist/treelist.h" +#include "props/propTypeConsts.h" LIST_ENTRY supShutdownListHead; CRITICAL_SECTION supShutdownListLock; +HANDLE ObjectPathHeap = NULL; + +OBEX_CONFIG g_LoadedParametersBlock; + // // Setup info/SCM database. // SAPIDB g_sapiDB; SCMDB g_scmDB; -HWND g_hwndBanner = NULL; - -//#define _PROFILE_MEMORY_USAGE_ - - -#ifdef _PROFILE_MEMORY_USAGE_ -ULONG g_cHeapAlloc = 0; -#endif +HWND BannerWindow = NULL; int __cdecl supxHandlesLookupCallback( void const* first, @@ -44,104 +42,153 @@ int __cdecl supxHandlesLookupCallback2( void const* second); /* -* supHeapAlloc +* supCreateHeap * * Purpose: * -* Wrapper for RtlAllocateHeap with WinObjEx heap. +* Wrapper around RtlCreateHeap with statistics support. * */ -#ifndef _PROFILE_MEMORY_USAGE_ -FORCEINLINE PVOID supHeapAlloc( - _In_ SIZE_T Size) +HANDLE supCreateHeap( + _In_ ULONG HeapFlags, + _In_ BOOL TerminateOnCorruption +) { - return RtlAllocateHeap(g_WinObj.Heap, HEAP_ZERO_MEMORY, Size); + HANDLE heapHandle; + + heapHandle = RtlCreateHeap(HeapFlags, NULL, 0, 0, NULL, NULL); + if (heapHandle == NULL) + return NULL; + + if (TerminateOnCorruption && g_WinObj.IsWine == FALSE) { + RtlSetHeapInformation(heapHandle, HeapEnableTerminationOnCorruption, NULL, 0); + } + + OBEX_STATS_INC(TotalHeapsCreated); + + return heapHandle; } -#else -PVOID supHeapAlloc( - _In_ SIZE_T Size) + +/* +* supDestroyHeap +* +* Purpose: +* +* Wrapper around RtlDestroyHeap with statistics support. +* +*/ +BOOL supDestroyHeap( + _In_ HANDLE HeapHandle +) { - LONG x; - DWORD LastError; - PVOID Buffer = NULL; - WCHAR szBuffer[100]; + BOOL bResult; - Buffer = RtlAllocateHeap(g_WinObj.Heap, HEAP_ZERO_MEMORY, Size); - LastError = GetLastError(); + bResult = (RtlDestroyHeap(HeapHandle) == NULL); + if (bResult) + OBEX_STATS_INC(TotalHeapsDestroyed); + + return bResult; +} + +/* +* supHeapAllocEx +* +* Purpose: +* +* Wrapper for RtlAllocateHeap with statistics support. +* +*/ +FORCEINLINE PVOID supHeapAllocEx( + _In_ HANDLE Heap, + _In_ SIZE_T Size +) +{ + PVOID Buffer; + +#ifdef _DEBUG + ULONG64 MaxHeapAllocatedBlockSize; +#endif + + Buffer = RtlAllocateHeap(Heap, HEAP_ZERO_MEMORY, Size); if (Buffer) { - x = InterlockedIncrement((PLONG)&g_cHeapAlloc); + OBEX_STATS_INC(TotalHeapAlloc); + OBEX_STATS_INC64(TotalHeapMemoryAllocated, Size); - RtlStringCchPrintfSecure(szBuffer, 100, - L"supHeapAlloc, block %p with size %llu, g_cHeapAlloc %x\r\n", - Buffer, Size, x); +#ifdef _DEBUG + MaxHeapAllocatedBlockSize = g_WinObjStats.MaxHeapAllocatedBlockSize; - OutputDebugString(szBuffer); - } - else { + while (1) { + + if (Size <= MaxHeapAllocatedBlockSize) + break; - RtlStringCchPrintfSecure(szBuffer, 100, - L"Allocation, block size %llu, FAILED\r\n", - Size); + MaxHeapAllocatedBlockSize = InterlockedCompareExchange64( + (LONG64*)&g_WinObjStats.MaxHeapAllocatedBlockSize, + (LONG64)Size, + (LONG64)MaxHeapAllocatedBlockSize); - OutputDebugString(szBuffer); + } +#endif } - SetLastError(LastError); return Buffer; } -#endif /* -* supHeapFree +* supHeapFreeEx * * Purpose: * -* Wrapper for RtlFreeHeap with WinObjEx heap. +* Wrapper for RtlFreeHeap with statistics support. * */ -#ifndef _PROFILE_MEMORY_USAGE_ -FORCEINLINE BOOL supHeapFree( - _In_ PVOID Memory) -{ - return RtlFreeHeap(g_WinObj.Heap, 0, Memory); -} -#else -BOOL supHeapFree( - _In_ PVOID Memory) +FORCEINLINE BOOL supHeapFreeEx( + _In_ HANDLE Heap, + _In_ PVOID Memory +) { - LONG x; - BOOL bSuccess; - DWORD LastError; - WCHAR szBuffer[100]; - - bSuccess = RtlFreeHeap(g_WinObj.Heap, 0, Memory); - LastError = GetLastError(); + BOOL Result; - if (bSuccess) { + Result = RtlFreeHeap(Heap, 0, Memory); - x = InterlockedDecrement((PLONG)&g_cHeapAlloc); + if (Result) { - RtlStringCchPrintfSecure(szBuffer, 100, - L"supHeapFree, block %p, g_cHeapAlloc %x\r\n", - Memory, x); + OBEX_STATS_INC(TotalHeapFree); - OutputDebugString(szBuffer); } - else { - RtlStringCchPrintfSecure(szBuffer, 100, - L"supHeapFree, block %p, FAILED\r\n", - Memory); + return Result; +} - OutputDebugString(szBuffer); - } +/* +* supHeapAlloc +* +* Purpose: +* +* Wrapper for RtlAllocateHeap with WinObjEx heap. +* +*/ +FORCEINLINE PVOID supHeapAlloc( + _In_ SIZE_T Size) +{ + return supHeapAllocEx(g_obexHeap, Size); +} - SetLastError(LastError); - return bSuccess; +/* +* supHeapFree +* +* Purpose: +* +* Wrapper for RtlFreeHeap with WinObjEx heap. +* +*/ +FORCEINLINE BOOL supHeapFree( + _In_ PVOID Memory) +{ + return supHeapFreeEx(g_obexHeap, Memory); } -#endif /* * supGetDPIValue @@ -328,7 +375,7 @@ VOID supClipboardCopy( if (hglbCopy != NULL) { lptstrCopy = (LPWSTR)GlobalLock(hglbCopy); if (lptstrCopy) { - supCopyMemory(lptstrCopy, dwSize, lpText, cbText); + RtlCopyMemory(lptstrCopy, lpText, cbText); } GlobalUnlock(hglbCopy); if (!SetClipboardData(CF_UNICODETEXT, hglbCopy)) @@ -614,37 +661,237 @@ HICON supGetMainIcon( } /* -* supCopyMemory +* supFreeUnicodeString * * Purpose: * -* Copies bytes between buffers. +* Release memory allocated for string. * -* dest - Destination buffer -* cbdest - Destination buffer size in bytes -* src - Source buffer -* cbsrc - Source buffer size in bytes +*/ +_Success_(return) +BOOL supFreeUnicodeString( + _In_ HANDLE HeapHandle, + _Inout_ PUNICODE_STRING String +) +{ + if (String->Buffer) { + return supHeapFreeEx(HeapHandle, String->Buffer); + } + return FALSE; +} + +/* +* supFreeDuplicatedUnicodeString +* +* Purpose: +* +* Release memory allocated for duplicated string. * */ -void supCopyMemory( - _Inout_ void* dest, - _In_ size_t cbdest, - _In_ const void* src, - _In_ size_t cbsrc +_Success_(return) +BOOL supFreeDuplicatedUnicodeString( + _In_ HANDLE HeapHandle, + _Inout_ PUNICODE_STRING DuplicatedString, + _In_ BOOL DoZeroMemory ) { - char* d = (char*)dest; - char* s = (char*)src; + BOOL bResult = FALSE; + if (DuplicatedString->Buffer) { + bResult = supHeapFreeEx(HeapHandle, DuplicatedString->Buffer); + if (DoZeroMemory) { + DuplicatedString->Buffer = NULL; + DuplicatedString->Length = DuplicatedString->MaximumLength = 0; + } + } + return bResult; +} - if ((dest == 0) || (src == 0) || (cbdest == 0)) - return; - if (cbdest < cbsrc) - cbsrc = cbdest; +/* +* supDuplicateUnicodeString +* +* Purpose: +* +* Duplicate existing UNICODE_STRING to another without RtlDuplicateUnicodeString. +* +* Note: Use supFreeDuplicatedUnicodeString to release allocated memory. +* +*/ +_Success_(return) +BOOL supDuplicateUnicodeString( + _In_ HANDLE HeapHandle, + _Out_ PUNICODE_STRING DestinationString, + _In_ PUNICODE_STRING SourceString +) +{ + USHORT maxLength = SourceString->MaximumLength; + PWCHAR strBuffer; - while (cbsrc > 0) { - *d++ = *s++; - cbsrc--; + if (maxLength == 0 || maxLength < SourceString->Length) + return FALSE; + + strBuffer = (PWCHAR)supHeapAllocEx(HeapHandle, (SIZE_T)maxLength); + if (strBuffer) { + DestinationString->Buffer = strBuffer; + DestinationString->MaximumLength = maxLength; + RtlCopyUnicodeString(DestinationString, SourceString); + return TRUE; + } + + return FALSE; +} + +/* +* supCreateObjectPathFromElements +* +* Purpose: +* +* Build object path with provided directory and name. +* +* Note: Use supFreeDuplicatedUnicodeString to release allocated memory. +* +*/ +_Success_(return) +BOOL supCreateObjectPathFromElements( + _In_ PUNICODE_STRING ObjectName, + _In_ PUNICODE_STRING DirectoryName, + _Out_ PUNICODE_STRING ObjectPath, + _In_ BOOLEAN NullTerminate +) +{ + BOOL bResult = FALSE, bIsRootDirectory; + PWSTR nameBuffer, string = NULL; + ULONG memIO; + USHORT bufferLength; + + // + // Must be valid strings. + // + if (ObjectName->Length == 0 || + DirectoryName->Length == 0) + { + return FALSE; + } + + bIsRootDirectory = supIsRootDirectory(DirectoryName); + memIO = ObjectName->Length + DirectoryName->Length; + + if (!bIsRootDirectory) + memIO += sizeof(OBJ_NAME_PATH_SEPARATOR); + + if (NullTerminate) + memIO += sizeof(UNICODE_NULL); + + nameBuffer = (PWSTR)supHeapAlloc(memIO); + string = nameBuffer; + + if (string) { + + RtlCopyMemory(string, DirectoryName->Buffer, DirectoryName->Length); + string = (PWSTR)RtlOffsetToPointer(string, DirectoryName->Length); + + if (!supIsRootDirectory(ObjectName)) { + + if (!bIsRootDirectory) + *string++ = OBJ_NAME_PATH_SEPARATOR; + + RtlCopyMemory(string, ObjectName->Buffer, ObjectName->Length); + string = (PWSTR)RtlOffsetToPointer(string, ObjectName->Length); + + } + + if (NullTerminate) + *string++ = UNICODE_NULL; + + bResult = TRUE; + } + + bufferLength = (USHORT)((ULONG_PTR)string - (ULONG_PTR)nameBuffer); + ObjectPath->Buffer = nameBuffer; + if (NullTerminate) + ObjectPath->Length = (USHORT)(bufferLength - sizeof(UNICODE_NULL)); + else + ObjectPath->Length = (USHORT)bufferLength; + + ObjectPath->MaximumLength = (USHORT)memIO; + + return bResult; +} + +/* +* supCreateObjectPathFromCurrentPath +* +* Purpose: +* +* Build string that include current directory and object name. +* +*/ +_Success_(return) +BOOL supCreateObjectPathFromCurrentPath( + _In_ PUNICODE_STRING ObjectName, + _Out_ PUNICODE_STRING ObjectPath, + _In_ BOOLEAN NullTerminate +) +{ + USHORT bufferLength; + BOOL bResult = FALSE, bIsRootDirectory; + PWSTR nameBuffer, string = NULL; + ULONG memIO; + UNICODE_STRING currentPath; + + if (ObjectName->Length == 0) + return FALSE; + + // + // If ObjectName is root, return root. + // + if (supIsRootDirectory(ObjectName)) { + return supDuplicateUnicodeString(g_obexHeap, ObjectPath, ObjectName); } + + if (!supGetCurrentObjectPath(TRUE, ¤tPath)) + return FALSE; + + bIsRootDirectory = supIsRootDirectory(¤tPath); + + memIO = ObjectName->Length + currentPath.Length; + + if (!bIsRootDirectory) + memIO += sizeof(OBJ_NAME_PATH_SEPARATOR); + + if (NullTerminate) + memIO += sizeof(UNICODE_NULL); + + nameBuffer = (PWSTR)supHeapAlloc(memIO); + string = nameBuffer; + + if (string) { + + RtlCopyMemory(string, currentPath.Buffer, currentPath.Length); + string = (PWSTR)RtlOffsetToPointer(string, currentPath.Length); + + if (!bIsRootDirectory) + *string++ = OBJ_NAME_PATH_SEPARATOR; + + RtlCopyMemory(string, ObjectName->Buffer, ObjectName->Length); + string = (PWSTR)RtlOffsetToPointer(string, ObjectName->Length); + + if (NullTerminate) + *string++ = UNICODE_NULL; + + bResult = TRUE; + } + + bufferLength = (USHORT)((ULONG_PTR)string - (ULONG_PTR)nameBuffer); + ObjectPath->Buffer = nameBuffer; + if (NullTerminate) + ObjectPath->Length = (USHORT)(bufferLength - sizeof(UNICODE_NULL)); + else + ObjectPath->Length = (USHORT)bufferLength; + + ObjectPath->MaximumLength = (USHORT)memIO; + + supFreeDuplicatedUnicodeString(g_obexHeap, ¤tPath, FALSE); + return bResult; } /* @@ -675,8 +922,8 @@ VOID CALLBACK supSymCallbackReportEvent( _In_ LPCWSTR EventText ) { - SendDlgItemMessage(g_hwndBanner, IDC_LOADING_MSG, EM_REPLACESEL, (WPARAM)0, (LPARAM)EventText); - SendDlgItemMessage(g_hwndBanner, IDC_LOADING_MSG, EM_REPLACESEL, (WPARAM)0, (LPARAM)(LPWSTR)L"\r\n"); + SendDlgItemMessage(BannerWindow, IDC_LOADING_MSG, EM_REPLACESEL, (WPARAM)0, (LPARAM)EventText); + SendDlgItemMessage(BannerWindow, IDC_LOADING_MSG, EM_REPLACESEL, (WPARAM)0, (LPARAM)(LPWSTR)L"\r\n"); } /* @@ -703,29 +950,21 @@ INT_PTR CALLBACK supxLoadBannerDialog( if (lParam) { pvData = (SUP_BANNER_DATA*)lParam; - - if (pvData->fList) { - SendDlgItemMessage(hwndDlg, IDC_LOADING_MSG, EM_SETLIMITTEXT, 0, 0); - supCenterWindowPerScreen(hwndDlg); - if (pvData->lpCaption) SetWindowText(hwndDlg, pvData->lpCaption); - SendDlgItemMessage(hwndDlg, IDC_LOADING_MSG, EM_REPLACESEL, (WPARAM)0, (LPARAM)pvData->lpText); - } - else { - supCenterWindow(hwndDlg); - SetDlgItemText(hwndDlg, IDC_LOADING_MSG, (LPWSTR)pvData->lpText); - } - + SendDlgItemMessage(hwndDlg, IDC_LOADING_MSG, EM_SETLIMITTEXT, 0, 0); + supCenterWindowPerScreen(hwndDlg); + if (pvData->lpCaption) SetWindowText(hwndDlg, pvData->lpCaption); + SendDlgItemMessage(hwndDlg, IDC_LOADING_MSG, EM_REPLACESEL, (WPARAM)0, (LPARAM)pvData->lpText); } - break; + return TRUE; case WM_CLOSE: DestroyWindow(hwndDlg); - g_hwndBanner = NULL; + BannerWindow = NULL; break; } - return 0; + return FALSE; } /* @@ -741,13 +980,13 @@ VOID supUpdateLoadBannerText( _In_ BOOL UseList ) { - if (g_hwndBanner) { + if (BannerWindow) { if (UseList) { - SendDlgItemMessage(g_hwndBanner, IDC_LOADING_MSG, EM_REPLACESEL, (WPARAM)0, (LPARAM)lpText); - SendDlgItemMessage(g_hwndBanner, IDC_LOADING_MSG, EM_REPLACESEL, (WPARAM)0, (LPARAM)(LPWSTR)L"\r\n"); + SendDlgItemMessage(BannerWindow, IDC_LOADING_MSG, EM_REPLACESEL, (WPARAM)0, (LPARAM)lpText); + SendDlgItemMessage(BannerWindow, IDC_LOADING_MSG, EM_REPLACESEL, (WPARAM)0, (LPARAM)(LPWSTR)L"\r\n"); } else { - SetDlgItemText(g_hwndBanner, IDC_LOADING_MSG, lpText); + SetDlgItemText(BannerWindow, IDC_LOADING_MSG, lpText); } } @@ -763,26 +1002,24 @@ VOID supUpdateLoadBannerText( */ VOID supDisplayLoadBanner( _In_ LPCWSTR lpMessage, - _In_opt_ LPCWSTR lpCaption, - _In_ BOOL UseList + _In_opt_ LPCWSTR lpCaption ) { SUP_BANNER_DATA bannerData; - bannerData.fList = UseList; bannerData.lpText = lpMessage; bannerData.lpCaption = lpCaption; - g_hwndBanner = CreateDialogParam( + BannerWindow = CreateDialogParam( g_WinObj.hInstance, - bannerData.fList ? MAKEINTRESOURCE(IDD_DIALOG_LOADLIST) : MAKEINTRESOURCE(IDD_DIALOG_LOAD), + MAKEINTRESOURCE(IDD_DIALOG_LOADLIST), 0, supxLoadBannerDialog, (LPARAM)&bannerData); - if (g_hwndBanner) { + if (BannerWindow) { supSetWaitCursor(TRUE); - SetCapture(g_hwndBanner); + SetCapture(BannerWindow); } } @@ -798,10 +1035,10 @@ VOID supCloseLoadBanner( VOID ) { - if (g_hwndBanner) { + if (BannerWindow) { supSetWaitCursor(FALSE); ReleaseCapture(); - SendMessage(g_hwndBanner, WM_CLOSE, 0, 0); + SendMessage(BannerWindow, WM_CLOSE, 0, 0); } } @@ -949,8 +1186,8 @@ PVOID supGetLoadedModulesList( { return ntsupGetLoadedModulesListEx(FALSE, ReturnLength, - supHeapAlloc, - supHeapFree); + (PNTSUPMEMALLOC)supHeapAlloc, + (PNTSUPMEMFREE)supHeapFree); } /* @@ -969,8 +1206,8 @@ PVOID supGetLoadedModulesList2( { return ntsupGetLoadedModulesListEx(TRUE, ReturnLength, - supHeapAlloc, - supHeapFree); + (PNTSUPMEMALLOC)supHeapAlloc, + (PNTSUPMEMFREE)supHeapFree); } /* @@ -1187,7 +1424,7 @@ HIMAGELIST supLoadImageList( * Known type names listed in objects.c, objects.h * */ -UINT supGetObjectNameIndexByTypeIndex( +WOBJ_OBJECT_TYPE supGetObjectNameIndexByTypeIndex( _In_ PVOID Object, _In_ UCHAR TypeIndex ) @@ -1310,7 +1547,7 @@ VOID supJumpToFile( _strcpy(szExplorer, g_WinObj.szWindowsDirectory); _strcat(szExplorer, TEXT("\\explorer.exe")); - supShellExecInExplorerProcessEx(szExplorer, lpCommand); + supShellExecInExplorerProcess(szExplorer, lpCommand); supHeapFree(lpCommand); } @@ -1330,6 +1567,8 @@ WOBJ_OBJECT_TYPE supObjectListGetObjectType( _In_ INT iItem ) { + OBEX_ITEM* objectReference; + LVITEM lvItem; lvItem.mask = LVIF_PARAM; @@ -1338,7 +1577,11 @@ WOBJ_OBJECT_TYPE supObjectListGetObjectType( lvItem.lParam = 0; ListView_GetItem(hwndList, &lvItem); - return (WOBJ_OBJECT_TYPE)lvItem.lParam; + objectReference = (OBEX_ITEM*)lvItem.lParam; + if (objectReference) + return objectReference->TypeIndex; + + return ObjectTypeUnknown; } /* @@ -1357,7 +1600,7 @@ VOID supSetGotoLinkTargetToolButtonState( _In_ BOOL bForceEnable ) { - UINT uEnable = MF_BYCOMMAND | MF_GRAYED; + UINT uEnable = MF_BYCOMMAND | MF_GRAYED; if (bForce) { if (bForceEnable) @@ -1740,6 +1983,28 @@ VOID supxSetProcessMitigationPolicies() } } +/* +* supxFreeCurrentObjectList +* +* Purpose: +* +* Destroy object path heap. +* +* Must be called once during program shutdown once +* +*/ +BOOL supxFreeCurrentObjectList( + _In_ PVOID Unused +) +{ + UNREFERENCED_PARAMETER(Unused); + + if (ObjectPathHeap) + supDestroyHeap(ObjectPathHeap); + + return TRUE; +} + /* * supInit * @@ -1786,6 +2051,7 @@ VOID supInit( // Remember current DPI value. // g_WinObj.CurrentDPI = supGetDPIValue(NULL); + supAddShutdownCallback(supxFreeCurrentObjectList, NULL); } /* @@ -2039,8 +2305,8 @@ BOOL sapiQueryDeviceProperty( if (PropertyBufferSize) *PropertyBufferSize = 0; - dataSize = (1 + MAX_PATH) * sizeof(WCHAR); - lpProperty = (LPWSTR)RtlAllocateHeap(SnapshotHeap, HEAP_ZERO_MEMORY, dataSize); + dataSize = (MAX_PATH * sizeof(WCHAR)) + sizeof(UNICODE_NULL); + lpProperty = (LPWSTR)supHeapAllocEx(SnapshotHeap, dataSize); if (lpProperty == NULL) return FALSE; @@ -2054,9 +2320,9 @@ BOOL sapiQueryDeviceProperty( if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) { - RtlFreeHeap(SnapshotHeap, 0, lpProperty); + supHeapFreeEx(SnapshotHeap, lpProperty); dataSize = returnLength; - lpProperty = (LPWSTR)RtlAllocateHeap(SnapshotHeap, HEAP_ZERO_MEMORY, dataSize); + lpProperty = (LPWSTR)supHeapAllocEx(SnapshotHeap, dataSize); if (lpProperty) { result = SetupDiGetDeviceRegistryProperty(hDevInfo, @@ -2073,7 +2339,7 @@ BOOL sapiQueryDeviceProperty( if (!result) { if (lpProperty) { - RtlFreeHeap(SnapshotHeap, 0, lpProperty); + supHeapFreeEx(SnapshotHeap, lpProperty); lpProperty = NULL; } dataSize = 0; @@ -2104,14 +2370,11 @@ BOOL sapiCreateSetupDBSnapshot( HANDLE Heap; HDEVINFO hDevInfo; - Heap = RtlCreateHeap(HEAP_GROWABLE, NULL, 0, 0, NULL, NULL); + Heap = supCreateHeap(HEAP_GROWABLE, TRUE); if (Heap == NULL) { return FALSE; } - if (g_WinObj.IsWine == FALSE) { - RtlSetHeapInformation(Heap, HeapEnableTerminationOnCorruption, NULL, 0); - } g_sapiDB.HeapHandle = Heap; hDevInfo = SetupDiGetClassDevs(NULL, NULL, NULL, DIGCF_PRESENT | DIGCF_ALLCLASSES); @@ -2124,7 +2387,7 @@ BOOL sapiCreateSetupDBSnapshot( for (i = 0; SetupDiEnumDeviceInfo(hDevInfo, i, &DeviceInfoData); i++) { - Entry = (PSAPIDBENTRY)RtlAllocateHeap(Heap, HEAP_ZERO_MEMORY, sizeof(SAPIDBENTRY)); + Entry = (PSAPIDBENTRY)supHeapAllocEx(Heap, sizeof(SAPIDBENTRY)); if (Entry == NULL) { bFailed = TRUE; break; @@ -2161,7 +2424,7 @@ BOOL sapiCreateSetupDBSnapshot( } if (bFailed) { - RtlDestroyHeap(Heap); + supDestroyHeap(Heap); RtlSecureZeroMemory(&g_sapiDB, sizeof(g_sapiDB)); } return bResult; @@ -2180,7 +2443,7 @@ VOID sapiFreeSnapshot( ) { EnterCriticalSection(&g_sapiDB.Lock); - RtlDestroyHeap(g_sapiDB.HeapHandle); + supDestroyHeap(g_sapiDB.HeapHandle); g_sapiDB.HeapHandle = NULL; g_sapiDB.ListHead.Blink = NULL; g_sapiDB.ListHead.Flink = NULL; @@ -2223,50 +2486,48 @@ BOOL WINAPI supCallbackShowChildWindow( BOOL supQueryWinstationDescription( _In_ LPCWSTR lpWindowStationName, _Inout_ LPWSTR Buffer, - _In_ DWORD ccBuffer //size of buffer in chars + _In_ DWORD cchBuffer //size of buffer in chars ) { BOOL bFound = FALSE; - LPCWSTR lpType; - - ULONG entryId; - - if (lpWindowStationName == NULL) { - SetLastError(ERROR_INVALID_NAME); - return bFound; - } + LPCWSTR lpType = T_UnknownType; + + ULONG i; + + struct { + LPCWSTR lpszWinSta; + LPCWSTR lpszDesc; + } lpWinstationDescriptions[] = { + { T_WINSTA_SYSTEM, L"System" }, + { T_WINSTA_ANONYMOUS, L"Anonymous" }, + { T_WINSTA_LOCALSERVICE, L"Local Service" }, + { T_WINSTA_NETWORK_SERVICE, L"Network Service" } + }; - if ((Buffer == NULL) || (ccBuffer < MAX_PATH)) { - SetLastError(ERROR_INSUFFICIENT_BUFFER); + if (lpWindowStationName == NULL || + cchBuffer < MAX_PATH) + { return bFound; } - lpType = NULL; + for (i = 0; i < RTL_NUMBER_OF(lpWinstationDescriptions); i++) { - for (entryId = 0; entryId < MAX_KNOWN_WINSTA_DESCRIPTIONS; entryId++) { + bFound = (_strstri(lpWindowStationName, + lpWinstationDescriptions[i].lpszWinSta) != NULL); - if (_strstri(lpWindowStationName, - g_WinstaDescArray[entryId].lpszWinSta) != NULL) - { - lpType = g_WinstaDescArray[entryId].lpszDesc; - bFound = TRUE; + if (bFound) { + lpType = lpWinstationDescriptions[i].lpszDesc; break; } } - if (lpType == NULL) - lpType = T_UnknownType; - _strcpy(Buffer, lpType); _strcat(Buffer, TEXT(" logon session")); return bFound; } -#include "props\propDlg.h" -#include "props\propTypeConsts.h" - /* * supQueryTypeInfo * @@ -2278,7 +2539,7 @@ BOOL supQueryWinstationDescription( * */ BOOL supQueryTypeInfo( - _In_ LPCWSTR lpTypeName, + _In_ PUNICODE_STRING TypeName, _Inout_ LPWSTR Buffer, _In_ DWORD cchBuffer //size of buffer in chars ) @@ -2303,10 +2564,8 @@ BOOL supQueryTypeInfo( objectEntry = &objectTypesList->Types[i]; - if (_strncmpi(objectEntry->TypeName->Buffer, - lpTypeName, - objectEntry->TypeName->Length / sizeof(WCHAR)) == 0) - { + if (RtlEqualUnicodeString(objectEntry->TypeName, TypeName, TRUE)) { + for (nPool = 0; nPool < MAX_KNOWN_POOL_TYPES; nPool++) { if (objectEntry->PoolType == a_PoolTypes[nPool].dwValue) { _strncpy(Buffer, @@ -2337,71 +2596,70 @@ BOOL supQueryTypeInfo( * */ BOOL supQueryDeviceDescription( - _In_ LPCWSTR lpDeviceName, + _In_opt_ PUNICODE_STRING Path, + _In_ PUNICODE_STRING Name, _Inout_ LPWSTR Buffer, - _In_ DWORD ccBuffer //size of buffer in chars + _In_ DWORD cchBuffer //size of buffer in chars ) { - BOOL bResult, bIsRoot; - SIZE_T Length; - LPWSTR lpFullDeviceName = NULL; + BOOL bResult; PLIST_ENTRY Entry; PSAPIDBENTRY Item; + SIZE_T deviceLength; + + UNICODE_STRING deviceName; bResult = FALSE; - if ((ccBuffer < MAX_PATH) || (Buffer == NULL)) { - SetLastError(ERROR_INSUFFICIENT_BUFFER); - return bResult; + RtlInitEmptyUnicodeString(&deviceName, NULL, 0); + + if (Path == NULL) { + if (!supCreateObjectPathFromCurrentPath(Name, &deviceName, TRUE)) + return FALSE; + } + else { + if (!supCreateObjectPathFromElements(Name, Path, &deviceName, TRUE)) + return FALSE; } + EnterCriticalSection(&g_sapiDB.Lock); + // - // Build full device path. + // Enumerate devices. // - Length = (4 + _strlen(lpDeviceName) + _strlen(g_WinObj.CurrentObjectPath)) * sizeof(WCHAR); - lpFullDeviceName = (LPWSTR)supHeapAlloc(Length); - if (lpFullDeviceName != NULL) { + Entry = g_sapiDB.ListHead.Flink; + while (Entry && Entry != &g_sapiDB.ListHead) { - // create full path device name for comparison - _strcpy(lpFullDeviceName, g_WinObj.CurrentObjectPath); - bIsRoot = (_strcmpi(g_WinObj.CurrentObjectPath, L"\\") == 0); - if (bIsRoot == FALSE) { - _strcat(lpFullDeviceName, L"\\"); - } - _strcat(lpFullDeviceName, lpDeviceName); + Item = CONTAINING_RECORD(Entry, SAPIDBENTRY, ListEntry); + if (Item->lpDeviceName != NULL) { - EnterCriticalSection(&g_sapiDB.Lock); + // + // lpDeviceName expects to be zero terminated. + // + deviceLength = _strlen(deviceName.Buffer); - // - // Enumerate devices. - // - Entry = g_sapiDB.ListHead.Flink; - while (Entry && Entry != &g_sapiDB.ListHead) { + if (_strncmpi(deviceName.Buffer, Item->lpDeviceName, deviceLength) == 0) { - Item = CONTAINING_RECORD(Entry, SAPIDBENTRY, ListEntry); - if (Item->lpDeviceName != NULL) { - if (_strcmpi(lpFullDeviceName, Item->lpDeviceName) == 0) { - if (Item->lpDeviceDesc != NULL) { + if (Item->lpDeviceDesc != NULL) { - _strncpy( - Buffer, - ccBuffer, - Item->lpDeviceDesc, - _strlen(Item->lpDeviceDesc)); + _strncpy( + Buffer, + cchBuffer, + Item->lpDeviceDesc, + _strlen(Item->lpDeviceDesc)); - } - bResult = TRUE; - break; } + bResult = TRUE; + break; } - - Entry = Entry->Flink; } - LeaveCriticalSection(&g_sapiDB.Lock); - - supHeapFree(lpFullDeviceName); + Entry = Entry->Flink; } + + LeaveCriticalSection(&g_sapiDB.Lock); + + supFreeDuplicatedUnicodeString(g_obexHeap, &deviceName, FALSE); return bResult; } @@ -2418,7 +2676,7 @@ BOOL supQueryDeviceDescription( BOOL supQueryDriverDescription( _In_ LPCWSTR lpDriverName, _Inout_ LPWSTR Buffer, - _In_ DWORD ccBuffer //size of buffer in chars + _In_ DWORD cchBuffer //size of buffer in chars ) { BOOL bResult; @@ -2440,11 +2698,6 @@ BOOL supQueryDriverDescription( bResult = FALSE; - if ((ccBuffer < MAX_PATH) || (Buffer == NULL)) { - SetLastError(ERROR_INSUFFICIENT_BUFFER); - return bResult; - } - // // First attempt - look in SCM database. // @@ -2472,7 +2725,7 @@ BOOL supQueryDriverDescription( continue; sz = _strlen(lpDisplayName); - _strncpy(Buffer, ccBuffer, lpDisplayName, sz); + _strncpy(Buffer, cchBuffer, lpDisplayName, sz); bResult = TRUE; break; } @@ -2546,7 +2799,7 @@ BOOL supQueryDriverDescription( dwSize = 0; bResult = VerQueryValue(vinfo, szBuffer, (LPVOID*)&lpDisplayName, (PUINT)&dwSize); if (bResult) { - _strncpy(Buffer, ccBuffer, lpDisplayName, dwSize); + _strncpy(Buffer, cchBuffer, lpDisplayName, dwSize); } } @@ -2721,12 +2974,6 @@ BOOL supQuerySectionFileInfo( WCHAR szQueryBlock[MAX_PATH + 1]; bResult = FALSE; - - if ((ccBuffer < MAX_PATH) || (Buffer == NULL)) { - SetLastError(ERROR_INSUFFICIENT_BUFFER); - return bResult; - } - vinfo = NULL; hSection = NULL; @@ -2791,76 +3038,6 @@ BOOL supQuerySectionFileInfo( return bResult; } -/* -* supOpenDirectoryForObject -* -* Purpose: -* -* Open directory for given object, handle self case. -* -*/ -NTSTATUS supOpenDirectoryForObject( - _Out_ PHANDLE DirectoryHandle, - _In_ LPCWSTR lpObjectName, - _In_ LPCWSTR lpDirectory -) -{ - BOOL needFree = FALSE; - NTSTATUS ntStatus; - SIZE_T i, l, rdirLen, ldirSz; - LPWSTR singleDirName, lookupDirName; - - *DirectoryHandle = NULL; - - if (lpObjectName == NULL) - return STATUS_INVALID_PARAMETER_2; - if (lpDirectory == NULL) - return STATUS_INVALID_PARAMETER_3; - - lookupDirName = (LPWSTR)lpDirectory; - - // - // 1) Check if object is directory self - // Extract directory name and compare (case insensitive) with object name - // Else go to 3 - // - l = 0; - rdirLen = _strlen(lookupDirName); - for (i = 0; i < rdirLen; i++) { - if (lookupDirName[i] == TEXT('\\')) - l = i + 1; - } - - singleDirName = &lookupDirName[l]; - if (_strcmpi(singleDirName, lpObjectName) == 0) { - // - // 2) If we are looking for directory, move search directory up - // e.g. lpDirectory = \ObjectTypes, lpObjectName = ObjectTypes then lpDirectory = \ - // - ldirSz = rdirLen * sizeof(WCHAR) + sizeof(UNICODE_NULL); - lookupDirName = (LPWSTR)supHeapAlloc(ldirSz); - if (lookupDirName == NULL) - return STATUS_INSUFFICIENT_RESOURCES; - - needFree = TRUE; - - //special case for root - if (l == 1) l++; - - supCopyMemory(lookupDirName, ldirSz, lpDirectory, (l - 1) * sizeof(WCHAR)); - } - // - // 3) Open directory - // - ntStatus = supOpenDirectory(DirectoryHandle, NULL, lookupDirName, DIRECTORY_QUERY); - - if (needFree) { - supHeapFree(lookupDirName); - } - - return ntStatus; -} - /* * supSaveDialogExecute * @@ -3255,14 +3432,12 @@ HWINSTA supOpenWindowStationFromContext( { HWINSTA hObject = NULL; UNICODE_STRING CurrentWinstaDir; - UNICODE_STRING WinstaDir; DWORD LastError = ERROR_ACCESS_DENIED; if (supxGetWindowStationName(&CurrentWinstaDir)) { - RtlInitUnicodeString(&WinstaDir, Context->lpCurrentObjectPath); - if (RtlEqualUnicodeString(&WinstaDir, &CurrentWinstaDir, TRUE)) { - hObject = OpenWindowStation(Context->lpObjectName, fInherit, dwDesiredAccess); + if (RtlEqualUnicodeString(&Context->NtObjectPath, &CurrentWinstaDir, TRUE)) { + hObject = OpenWindowStation(Context->NtObjectName.Buffer, fInherit, dwDesiredAccess); LastError = GetLastError(); } RtlFreeUnicodeString(&CurrentWinstaDir); @@ -3984,7 +4159,7 @@ INT supGetMaxOfTwoUlongFromHex( INT nResult; LPWSTR lpItem1 = NULL, lpItem2 = NULL; ULONG ad1, ad2; - WCHAR szText[MAX_TEXT_CONVERSION_ULONG64 + 1]; + WCHAR szText[MAX_TEXT_CONVERSION_ULONG64]; RtlSecureZeroMemory(&szText, sizeof(szText)); @@ -4035,7 +4210,7 @@ INT supGetMaxOfTwoU64FromHex( INT nResult; LPWSTR lpItem1 = NULL, lpItem2 = NULL; ULONG_PTR ad1, ad2; - WCHAR szText[MAX_TEXT_CONVERSION_ULONG64 + 1]; + WCHAR szText[MAX_TEXT_CONVERSION_ULONG64]; RtlSecureZeroMemory(&szText, sizeof(szText)); @@ -4086,7 +4261,7 @@ INT supGetMaxOfTwoLongFromString( INT nResult; LPWSTR lpItem1 = NULL, lpItem2 = NULL; LONG_PTR value1, value2; - WCHAR szText[MAX_TEXT_CONVERSION_ULONG64 + 1]; + WCHAR szText[MAX_TEXT_CONVERSION_ULONG64]; RtlSecureZeroMemory(&szText, sizeof(szText)); @@ -4137,7 +4312,7 @@ INT supGetMaxOfTwoULongFromString( INT nResult; LPWSTR lpItem1 = NULL, lpItem2 = NULL; ULONG_PTR value1, value2; - WCHAR szText[MAX_TEXT_CONVERSION_ULONG64 + 1]; + WCHAR szText[MAX_TEXT_CONVERSION_ULONG64]; RtlSecureZeroMemory(&szText, sizeof(szText)); @@ -4273,6 +4448,35 @@ INT supListViewBaseComparer( return nResult; } +/* +* supOpenLinkedToken +* +* Purpose: +* +* Query token linked token handle. +* +*/ +NTSTATUS supOpenLinkedToken( + _In_ HANDLE TokenHandle, + _Out_ PHANDLE LinkedTokenHandle +) +{ + ULONG rLen; + NTSTATUS ntStatus; + TOKEN_LINKED_TOKEN linkedToken; + + ntStatus = NtQueryInformationToken( + TokenHandle, + TokenLinkedToken, + &linkedToken, + sizeof(TOKEN_LINKED_TOKEN), + &rLen); + + *LinkedTokenHandle = linkedToken.LinkedToken; + + return ntStatus; +} + /* * supOpenTokenByParam * @@ -4374,6 +4578,39 @@ NTSTATUS supOpenDeviceObjectEx( 0); } +BOOL supxCanOpenObjectType( + _In_ UINT nTypeIndex +) +{ + UINT SupportedNamedTypes[] = { + ObjectTypeDirectory, + ObjectTypeDevice, + ObjectTypeEvent, + ObjectTypeEventPair, + ObjectTypeIoCompletion, + ObjectTypeJob, + ObjectTypeKey, + ObjectTypeKeyedEvent, + ObjectTypeMutant, + ObjectTypeMemoryPartition, + ObjectTypePort, + ObjectTypeRegistryTransaction, + ObjectTypeSemaphore, + ObjectTypeTimer, + ObjectTypeSymbolicLink, + ObjectTypeSection, + ObjectTypeSession + }; + + UINT i; + for (i = 0; i < RTL_NUMBER_OF(SupportedNamedTypes); i++) { + if (SupportedNamedTypes[i] == nTypeIndex) + return TRUE; + } + + return FALSE; +} + /* * supOpenNamedObjectByType * @@ -4381,44 +4618,24 @@ NTSTATUS supOpenDeviceObjectEx( * * Open object of supported type and return handle to it. * -* Supported types are: -* -* Directory (ObjectName parameter then should be NULL) -* Device -* Mutant -* Key -* KeyedEvent -* Semaphore -* Timer -* Event -* EventPair -* SymbolicLink -* IoCompletion -* Section -* Job -* Session -* MemoryPartition -* AlpcPort +* Supported types are list in SupportedNamedTypes array. * */ NTSTATUS supOpenNamedObjectByType( _Out_ HANDLE* ObjectHandle, _In_ ULONG TypeIndex, - _In_ LPCWSTR ObjectDirectory, - _In_ LPCWSTR ObjectName, + _In_ PUNICODE_STRING ObjectDirectory, + _In_ PUNICODE_STRING ObjectName, _In_ ACCESS_MASK DesiredAccess ) { - OBJECT_ATTRIBUTES obja; - UNICODE_STRING ustr; HANDLE rootHandle = NULL, objectHandle = NULL; NTSTATUS ntStatus = STATUS_UNSUCCESSFUL; - - LPWSTR objectFullName = NULL; - SIZE_T cchObjectFullName; - PNTOBJECTOPENPROCEDURE ObjectOpenProcedure = NULL; + UNICODE_STRING portName; + OBJECT_ATTRIBUTES obja; + *ObjectHandle = NULL; if (ObjectDirectory == NULL) @@ -4427,57 +4644,28 @@ NTSTATUS supOpenNamedObjectByType( if (ObjectName == NULL) return STATUS_INVALID_PARAMETER_4; - if ((TypeIndex != ObjectTypeDirectory) && - (TypeIndex != ObjectTypeDevice) && - (TypeIndex != ObjectTypeEvent) && - (TypeIndex != ObjectTypeEventPair) && - (TypeIndex != ObjectTypeIoCompletion) && - (TypeIndex != ObjectTypeJob) && - (TypeIndex != ObjectTypeKey) && - (TypeIndex != ObjectTypeKeyedEvent) && - (TypeIndex != ObjectTypeMutant) && - (TypeIndex != ObjectTypeMemoryPartition) && - (TypeIndex != ObjectTypePort) && - (TypeIndex != ObjectTypeSemaphore) && - (TypeIndex != ObjectTypeTimer) && - (TypeIndex != ObjectTypeSymbolicLink) && - (TypeIndex != ObjectTypeSection) && - (TypeIndex != ObjectTypeSession)) - { + if (!supxCanOpenObjectType(TypeIndex)) return STATUS_NOT_SUPPORTED; - } // // Special ALPC port case. // if (TypeIndex == ObjectTypePort) { - // - // Build full object name. - // - cchObjectFullName = 4 + _strlen(ObjectDirectory) + - _strlen(ObjectName) + - sizeof(UNICODE_NULL); - - objectFullName = (LPWSTR)supHeapAlloc(cchObjectFullName * sizeof(WCHAR)); - if (objectFullName) { - - RtlStringCchPrintfSecure(objectFullName, - cchObjectFullName, - L"%s\\%s", - ObjectDirectory, - ObjectName); - + RtlInitEmptyUnicodeString(&portName, NULL, 0); + if (supCreateObjectPathFromElements(ObjectName, + ObjectDirectory, + &portName, + TRUE)) + { // // Open port by name. // ntStatus = supOpenPortObjectByName(ObjectHandle, DesiredAccess, - NULL, - objectFullName); - - supHeapFree(objectFullName); + &portName); + supHeapFree(portName.Buffer); } return ntStatus; @@ -4490,24 +4678,20 @@ NTSTATUS supOpenNamedObjectByType( // // If this is root, then root rootHandle = NULL. - // - if (_strcmpi(ObjectName, KM_OBJECTS_ROOT_DIRECTORY) != 0) { - - ntStatus = supOpenDirectoryForObject( - &rootHandle, - ObjectName, - ObjectDirectory); - - if (!NT_SUCCESS(ntStatus)) { + // + if (!supIsRootDirectory(ObjectName)) { + // + // Otherwise open directory that keep this object. + // + ntStatus = supOpenDirectoryEx(&rootHandle, NULL, ObjectDirectory, DIRECTORY_QUERY); + if (!NT_SUCCESS(ntStatus)) return ntStatus; - } - } // // Open object in directory. // - ntStatus = supOpenDirectory(&objectHandle, rootHandle, ObjectName, DesiredAccess); + ntStatus = supOpenDirectoryEx(&objectHandle, rootHandle, ObjectName, DesiredAccess); if (rootHandle) NtClose(rootHandle); @@ -4519,13 +4703,10 @@ NTSTATUS supOpenNamedObjectByType( // // Open directory which object belongs. // - RtlInitUnicodeString(&ustr, ObjectDirectory); - InitializeObjectAttributes(&obja, &ustr, OBJ_CASE_INSENSITIVE, NULL, NULL); - - supOpenDirectoryForObject(&rootHandle, ObjectName, ObjectDirectory); - - RtlInitUnicodeString(&ustr, ObjectName); - obja.RootDirectory = rootHandle; + ntStatus = supOpenDirectoryEx(&rootHandle, NULL, ObjectDirectory, DIRECTORY_QUERY); + if (!NT_SUCCESS(ntStatus)) { + return ntStatus; + } // // Select open object procedure. @@ -4588,6 +4769,11 @@ NTSTATUS supOpenNamedObjectByType( ObjectOpenProcedure = (PNTOBJECTOPENPROCEDURE)g_ExtApiSet.NtOpenPartition; } break; + case ObjectTypeRegistryTransaction: + if (g_ExtApiSet.NtOpenRegistryTransaction) { + ObjectOpenProcedure = (PNTOBJECTOPENPROCEDURE)g_ExtApiSet.NtOpenRegistryTransaction; + } + break; default: ObjectOpenProcedure = NULL; break; @@ -4603,6 +4789,8 @@ NTSTATUS supOpenNamedObjectByType( // // Open object of the given type. // + InitializeObjectAttributes(&obja, ObjectName, OBJ_CASE_INSENSITIVE, rootHandle, NULL); + ntStatus = ObjectOpenProcedure( &objectHandle, DesiredAccess, @@ -4748,9 +4936,7 @@ BOOL supxEnumAlpcPortsCallback( pusObjectName = (PUNICODE_STRING)pBuffer; if (pusObjectName->Buffer && pusObjectName->Length) { - if (0 == _strcmpi(enumContext->ObjectFullName, - pusObjectName->Buffer)) - { + if (RtlEqualUnicodeString(enumContext->ObjectName, pusObjectName, TRUE)) { enumContext->ObjectHandle = objectHandle; bStopEnum = TRUE; break; @@ -4790,8 +4976,7 @@ BOOL supxEnumAlpcPortsCallback( NTSTATUS supOpenPortObjectByName( _Out_ PHANDLE ObjectHandle, _In_ ACCESS_MASK DesiredAccess, - _Out_opt_ PHANDLE ReferenceHandle, - _In_ LPCWSTR ObjectName + _In_ PUNICODE_STRING ObjectName ) { USHORT alpcPortTypeIndex; @@ -4801,8 +4986,6 @@ NTSTATUS supOpenPortObjectByName( if (ObjectHandle) *ObjectHandle = NULL; - if (ReferenceHandle) - *ReferenceHandle = NULL; do { @@ -4829,7 +5012,7 @@ NTSTATUS supOpenPortObjectByName( // Walk handle table looking for our named port. // enumContext.AlpcPortTypeIndex = alpcPortTypeIndex; - enumContext.ObjectFullName = ObjectName; + enumContext.ObjectName = ObjectName; enumContext.ObjectHandle = NULL; if (supEnumHandleDump(pHandles, @@ -4849,10 +5032,7 @@ NTSTATUS supOpenPortObjectByName( 0, 0); - if (ReferenceHandle) - *ReferenceHandle = enumContext.ObjectHandle; - else - NtClose(enumContext.ObjectHandle); + NtClose(enumContext.ObjectHandle); } else { @@ -4876,7 +5056,7 @@ NTSTATUS supOpenPortObjectByName( * * Purpose: * -* Open handle for ALPC port object type with handle duplication using WinObjEx64 property context. +* Open handle for ALPC port object type. * */ NTSTATUS supOpenPortObjectFromContext( @@ -4886,73 +5066,26 @@ NTSTATUS supOpenPortObjectFromContext( ) { NTSTATUS ntStatus = STATUS_UNSUCCESSFUL; - HANDLE refHandle = NULL; - LPWSTR objectFullName = NULL; - SIZE_T cchObjectFullName; - - *ObjectHandle = NULL; - /* - Context->PortObjectInfo.IsAllocated = TRUE; - Context->PortObjectInfo.ReferenceHandle = TestGetPortHandle(); - */ + UNICODE_STRING portName; - if (Context->PortObjectInfo.IsAllocated) { + *ObjectHandle = NULL; - ntStatus = NtDuplicateObject(NtCurrentProcess(), - Context->PortObjectInfo.ReferenceHandle, - NtCurrentProcess(), - ObjectHandle, + RtlInitEmptyUnicodeString(&portName, NULL, 0); + if (supCreateObjectPathFromElements( + &Context->NtObjectName, + &Context->NtObjectPath, + &portName, + TRUE)) + { + ntStatus = supOpenPortObjectByName(ObjectHandle, DesiredAccess, - 0, - 0); + &portName); + supHeapFree(portName.Buffer); } else { - - do { - - // - // Build full object name. - // - cchObjectFullName = 4 + _strlen(Context->lpCurrentObjectPath) + - _strlen(Context->lpObjectName) + - sizeof(UNICODE_NULL); - - objectFullName = (LPWSTR)supHeapAlloc(cchObjectFullName * sizeof(WCHAR)); - if (objectFullName) { - - RtlStringCchPrintfSecure(objectFullName, - cchObjectFullName, - L"%s\\%s", - Context->lpCurrentObjectPath, - Context->lpObjectName); - - // - // Open port by name. - // - ntStatus = supOpenPortObjectByName(ObjectHandle, - DesiredAccess, - &refHandle, - objectFullName); - - if (NT_SUCCESS(ntStatus)) { - - // - // Save handle as reference. - // - Context->PortObjectInfo.ReferenceHandle = refHandle; - Context->PortObjectInfo.IsAllocated = TRUE; - } - - supHeapFree(objectFullName); - } - else { - ntStatus = STATUS_INSUFFICIENT_RESOURCES; - } - - } while (FALSE); - + ntStatus = STATUS_INSUFFICIENT_RESOURCES; } return ntStatus; @@ -4988,7 +5121,7 @@ HANDLE supOpenObjectFromContext( &hPrivateNamespace, MAXIMUM_ALLOWED, &objaNamespace, - Context->NamespaceInfo.BoundaryDescriptor); + Context->u1.NamespaceInfo.BoundaryDescriptor); if (!NT_SUCCESS(ntStatus)) { *Status = ntStatus; @@ -5005,14 +5138,14 @@ HANDLE supOpenObjectFromContext( // Open object of common type. // - switch (Context->TypeIndex) { + switch (Context->ObjectTypeIndex) { case ObjectTypeProcess: if (Context->ContextType == propUnnamed) { ntStatus = supOpenProcessEx( - Context->UnnamedObjectInfo.ClientId.UniqueProcess, + Context->u1.UnnamedObjectInfo.ClientId.UniqueProcess, PROCESS_ALL_ACCESS, &hObject); @@ -5030,7 +5163,7 @@ HANDLE supOpenObjectFromContext( &hObject, DesiredAccess, ObjectAttributes, - &Context->UnnamedObjectInfo.ClientId); + &Context->u1.UnnamedObjectInfo.ClientId); } else @@ -5043,10 +5176,10 @@ HANDLE supOpenObjectFromContext( if (Context->ContextType == propUnnamed) { ntStatus = supOpenTokenByParam( - &Context->UnnamedObjectInfo.ClientId, + &Context->u1.UnnamedObjectInfo.ClientId, ObjectAttributes, DesiredAccess, - Context->UnnamedObjectInfo.IsThreadToken, + Context->u1.UnnamedObjectInfo.IsThreadToken, &hObject); } @@ -5187,6 +5320,20 @@ HANDLE supOpenObjectFromContext( break; + case ObjectTypeRegistryTransaction: + + if (g_ExtApiSet.NtOpenRegistryTransaction) { + + ntStatus = g_ExtApiSet.NtOpenRegistryTransaction( + &hObject, + DesiredAccess, + ObjectAttributes); + } + else + ntStatus = STATUS_PROCEDURE_NOT_FOUND; + + break; + default: ntStatus = STATUS_OBJECTID_NOT_FOUND; break; @@ -5222,7 +5369,7 @@ BOOL supCloseObjectFromContext( else { - switch (Context->TypeIndex) { + switch (Context->ObjectTypeIndex) { case ObjectTypeWinstation: bResult = CloseWindowStation((HWINSTA)hObject); break; @@ -5624,6 +5771,20 @@ BOOL supPrintTimeConverted( { FILETIME ConvertedTime = { 0, 0 }; TIME_FIELDS TimeFields = { 0, 0, 0, 0, 0, 0, 0, 0 }; + LPCWSTR lpszMonths[12] = { + L"Jan", + L"Feb", + L"Mar", + L"Apr", + L"May", + L"Jun", + L"Jul", + L"Aug", + L"Sep", + L"Oct", + L"Nov", + L"Dec" + }; if (FileTimeToLocalFileTime((PFILETIME)Time, (PFILETIME)&ConvertedTime)) { RtlTimeToTimeFields((PLARGE_INTEGER)&ConvertedTime, (PTIME_FIELDS)&TimeFields); @@ -5639,7 +5800,7 @@ BOOL supPrintTimeConverted( TimeFields.Minute, TimeFields.Second, TimeFields.Day, - g_szMonths[TimeFields.Month - 1], + lpszMonths[TimeFields.Month - 1], TimeFields.Year); return TRUE; @@ -5648,6 +5809,35 @@ BOOL supPrintTimeConverted( return FALSE; } +/* +* supGetTreeViewItemParam +* +* Purpose: +* +* Return TreeView item associated parameter. +* +*/ +_Success_(return) +BOOL supGetTreeViewItemParam( + _In_ HWND hwndTreeView, + _In_ HTREEITEM hTreeItem, + _Out_ PVOID* outParam +) +{ + TV_ITEM tvi; + + RtlSecureZeroMemory(&tvi, sizeof(TV_ITEM)); + + tvi.mask = TVIF_PARAM; + tvi.hItem = hTreeItem; + if (!TreeView_GetItem(hwndTreeView, &tvi)) + return FALSE; + + *outParam = (PVOID)tvi.lParam; + + return TRUE; +} + /* * supGetListViewItemParam * @@ -5656,6 +5846,7 @@ BOOL supPrintTimeConverted( * Return ListView item associated parameter. * */ +_Success_(return) BOOL supGetListViewItemParam( _In_ HWND hwndListView, _In_ INT itemIndex, @@ -5664,8 +5855,6 @@ BOOL supGetListViewItemParam( { LVITEM lvItem; - *outParam = NULL; - lvItem.mask = LVIF_PARAM; lvItem.iItem = itemIndex; lvItem.iSubItem = 0; @@ -6008,8 +6197,8 @@ PSUP_HANDLE_DUMP supHandlesCreateFilteredAndSortedList( handleDump = (PSYSTEM_HANDLE_INFORMATION_EX)ntsupGetSystemInfoEx( SystemExtendedHandleInformation, &returnLength, - supHeapAlloc, - supHeapFree); + (PNTSUPMEMALLOC)supHeapAlloc, + (PNTSUPMEMFREE)supHeapFree); if (handleDump == NULL) return NULL; @@ -6501,7 +6690,7 @@ HRESULT supxGetShellDispatchFromView(IShellView * psv, REFIID riid, void** ppv) } /* -* supShellExecInExplorerProcessEx +* supShellExecInExplorerProcess * * Purpose: * @@ -6509,7 +6698,7 @@ HRESULT supxGetShellDispatchFromView(IShellView * psv, REFIID riid, void** ppv) * making it run with IL of Windows Explorer and not WinObjEx64. * */ -HRESULT supShellExecInExplorerProcessEx( +HRESULT supShellExecInExplorerProcess( _In_ PCWSTR pszFile, _In_opt_ PCWSTR pszArguments ) @@ -6612,21 +6801,6 @@ HRESULT supShellExecInExplorerProcessEx( return hr; } -/* -* supShellExecInExplorerProcess -* -* Purpose: -* -* Run ShellExecute from Windows Explorer process through shell interfaces -* making it run with IL of Windows Explorer and not WinObjEx64. -* -*/ -HRESULT WINAPI supShellExecInExplorerProcess( - _In_ PCWSTR pszFile) -{ - return supShellExecInExplorerProcessEx(pszFile, NULL); -} - /* * supLoadIconForObjectType * @@ -6656,42 +6830,18 @@ BOOLEAN supLoadIconForObjectType( if (hIcon) { - SendDlgItemMessage(hwndDlg, ID_OBJECT_ICON, - STM_SETIMAGE, IMAGE_ICON, (LPARAM)hIcon); - - if (IsShadow) - Context->ObjectTypeIcon = hIcon; - else - Context->ObjectIcon = hIcon; - - return TRUE; - } - - return FALSE; -} - -/* -* supDestroyIconForObjectType -* -* Purpose: -* -* Destroy icon used to represent object (or its type) which properties is currently viewed. -* -*/ -VOID supDestroyIconForObjectType( - _In_ PROP_OBJECT_INFO * Context -) -{ - if (Context->IsType) { - if (Context->ObjectTypeIcon) { - DestroyIcon(Context->ObjectTypeIcon); - Context->ObjectTypeIcon = NULL; - } - } - if (Context->ObjectIcon) { - DestroyIcon(Context->ObjectIcon); - Context->ObjectIcon = NULL; + SendDlgItemMessage(hwndDlg, ID_OBJECT_ICON, + STM_SETIMAGE, IMAGE_ICON, (LPARAM)hIcon); + + if (IsShadow) + Context->ObjectTypeIcon = hIcon; + else + Context->ObjectIcon = hIcon; + + return TRUE; } + + return FALSE; } /* @@ -7408,7 +7558,7 @@ VOID supQueryAlpcPortObjectTypeIndex( sdLength = SECURITY_DESCRIPTOR_MIN_LENGTH + (ULONG)sizeof(ACL) + - 2 * (ULONG)sizeof(ACCESS_ALLOWED_ACE) + + (ULONG)(2 * sizeof(ACCESS_ALLOWED_ACE)) + RtlLengthSid(SeWorldSid) + RtlLengthSid(SeRestrictedSid) + 8; @@ -8803,12 +8953,20 @@ HANDLE supCreateThread( _In_ DWORD dwCreationFlags ) { - return CreateThread(NULL, + HANDLE threadHandle; + + threadHandle = CreateThread(NULL, 0, lpStartAddress, lpParameter, dwCreationFlags, NULL); + + if (threadHandle) { + OBEX_STATS_INC(TotalThreadsCreated); + } + + return threadHandle; } /* @@ -8845,176 +9003,653 @@ HANDLE supCreateDialogWorkerThread( } /* +* supGetCurrentObjectPath +* +* Purpose: +* +* Build full path to current object. +* +* If IncludeName is FALSE then result path does not +* include object name except for root directory. +* +* e.g. +* For \\ result will be \\ +* For \\ABC result will be \\ +* For \\ABC\\DEF result will be \\ABC +* +* If IncludeName is TRUE then result path *will* +* include object name * -* Fast events, taken from ph2 -* +* e.g. +* For \\ result will be \\ +* For \\ABC result will be \\ABC +* For \\ABC\\DEF result will be \\ABC\\DEF */ +_Success_(return != FALSE) +BOOL supGetCurrentObjectPath( + _In_ BOOLEAN IncludeName, + _Out_ PUNICODE_STRING ObjectPath +) +{ + OBEX_PATH_ELEMENT* ObjectPathEntry; + PLIST_ENTRY Head, Entry, FinalEntry, ObjectRootEntry = NULL; + + ULONG NameInfoSize, BufferLength; + PWCH StringBuffer, ObjectName; + + PUNICODE_STRING String; + + RtlInitEmptyUnicodeString(ObjectPath, NULL, 0); + + if (IsListEmpty(&g_ObjectPathListHead)) + return FALSE; + + NameInfoSize = sizeof(UNICODE_NULL); + + Head = &g_ObjectPathListHead; + Entry = Head->Blink; // Beginning of path + + if (IncludeName) { + FinalEntry = Head; + } + else { + FinalEntry = Head->Flink; // Current object name + } + + ObjectRootEntry = Entry; + while ((Entry) && (Entry != FinalEntry)) { + + ObjectPathEntry = CONTAINING_RECORD(Entry, OBEX_PATH_ELEMENT, ListEntry); + NameInfoSize += ObjectPathEntry->Name.Length; + + // + // If not last and first then add separator size. + // + if ((Entry != ObjectRootEntry) && (Entry->Blink != FinalEntry)) + NameInfoSize += sizeof(OBJ_NAME_PATH_SEPARATOR); + + Entry = Entry->Blink; + } + + // + // If this is root then leave. + // + if (NameInfoSize == sizeof(UNICODE_NULL)) { + return supDuplicateUnicodeString(g_obexHeap, ObjectPath, ObGetPredefinedUnicodeString(OBP_ROOT)); + } + + ObjectName = (PWCH)supHeapAlloc(NameInfoSize); + if (ObjectName == NULL) + return FALSE; + + StringBuffer = ObjectName; + + Head = &g_ObjectPathListHead; + Entry = Head->Blink; // Beginning of path + + if (IncludeName) { + FinalEntry = Head; + } + else { + FinalEntry = Head->Flink; // Current object name + } + + ObjectRootEntry = Entry; + while ((Entry) && (Entry != FinalEntry)) { + + ObjectPathEntry = CONTAINING_RECORD(Entry, OBEX_PATH_ELEMENT, ListEntry); + + String = &ObjectPathEntry->Name; + + RtlCopyMemory(StringBuffer, String->Buffer, String->Length); + StringBuffer = (PWCH)((PCH)StringBuffer + String->Length); + + // + // If not last and first then add separator. + // + if ((Entry != ObjectRootEntry) && (Entry->Blink != FinalEntry)) + *StringBuffer++ = OBJ_NAME_PATH_SEPARATOR; + + Entry = Entry->Blink; + } + + *StringBuffer++ = UNICODE_NULL; + + BufferLength = (USHORT)((ULONG_PTR)StringBuffer - (ULONG_PTR)ObjectName); + ObjectPath->Buffer = ObjectName; + ObjectPath->Length = (USHORT)(BufferLength - sizeof(UNICODE_NULL)); + ObjectPath->MaximumLength = (USHORT)BufferLength; + + return TRUE; +} /* -* supInitFastEvent +* supGetCurrentObjectName * * Purpose: * -* Initialize fast event. +* Return name of currently selected object. * */ -VOID supInitFastEvent( - _In_ PFAST_EVENT Event +_Success_(return) +BOOL supGetCurrentObjectName( + _Out_ PUNICODE_STRING ObjectName ) { - Event->Value = FAST_EVENT_REFCOUNT_INC; - Event->EventHandle = NULL; + OBEX_PATH_ELEMENT* entry = NULL; + LIST_ENTRY* listEntry, * head; + + RtlInitEmptyUnicodeString(ObjectName, NULL, 0); + + if (IsListEmpty(&g_ObjectPathListHead)) + return FALSE; + + head = &g_ObjectPathListHead; + listEntry = head->Flink; + if (listEntry) { + entry = CONTAINING_RECORD(listEntry, OBEX_PATH_ELEMENT, ListEntry); + return supDuplicateUnicodeString(g_obexHeap, ObjectName, &entry->Name); + } + + return FALSE; } /* -* supReferenceFastEvent +* supBuildCurrentObjectList * * Purpose: * -* Make a reference for fast event. +* Create list of current object path elements including name. * */ -VOID supReferenceFastEvent( - _In_ PFAST_EVENT Event +VOID supBuildCurrentObjectList( + _In_ PVOID ListHead ) { - _InterlockedExchangeAddPointer((PLONG_PTR)&Event->Value, FAST_EVENT_REFCOUNT_INC); + OBEX_ITEM* nextItem; + OBEX_PATH_ELEMENT* entry = NULL; + + if (ObjectPathHeap) + supDestroyHeap(ObjectPathHeap); + + ObjectPathHeap = supCreateHeap(HEAP_GROWABLE, TRUE); + if (ObjectPathHeap == NULL) + return; + + InitializeListHead(&g_ObjectPathListHead); + + nextItem = (OBEX_ITEM*)ListHead; + while (nextItem) { + entry = (OBEX_PATH_ELEMENT*)supHeapAllocEx(ObjectPathHeap, sizeof(OBEX_PATH_ELEMENT)); + if (entry) { + entry->TypeIndex = nextItem->TypeIndex; + supDuplicateUnicodeString(ObjectPathHeap, &entry->Name, &nextItem->Name); + supDuplicateUnicodeString(ObjectPathHeap, &entry->TypeName, &nextItem->TypeName); + InsertTailList(&g_ObjectPathListHead, &entry->ListEntry); + } + nextItem = nextItem->Prev; + } + } /* -* supDereferenceFastEvent +* supNormalizeUnicodeStringForDisplay * * Purpose: * -* Remove reference from fast event. +* Create a copy of unicode string, friendly for output. * */ -VOID supDereferenceFastEvent( - _In_ PFAST_EVENT Event, - _In_opt_ HANDLE EventHandle +_Success_(return) +BOOL supNormalizeUnicodeStringForDisplay( + _In_ HANDLE HeapHandle, + _In_ PUNICODE_STRING SourceString, + _Out_ PUNICODE_STRING NormalizedString ) { - ULONG_PTR value; + PWCH stringBuffer, src, dst; + ULONG i; - value = _InterlockedExchangeAddPointer((PLONG_PTR)&Event->Value, -FAST_EVENT_REFCOUNT_INC); - if (((value >> FAST_EVENT_REFCOUNT_SHIFT) & FAST_EVENT_REFCOUNT_MASK) - 1 == 0) - { - if (EventHandle) - { - NtClose(EventHandle); - Event->EventHandle = NULL; + stringBuffer = (PWCH)supHeapAllocEx(HeapHandle, + SourceString->Length + sizeof(UNICODE_NULL)); + + if (stringBuffer) { + + dst = stringBuffer; + src = SourceString->Buffer; + + i = SourceString->Length / sizeof(WCHAR); + while (i--) { + + if (*src == 0) + *dst = g_ObNameNormalizationSymbol; + else + *dst = *src; + + src++; + dst++; } + + *dst = UNICODE_NULL; + + RtlInitUnicodeString(NormalizedString, stringBuffer); + return TRUE; } + + return FALSE; } /* -* supSetFastEvent +* supDisplayCurrentObjectPath * * Purpose: * -* Set event to signaled state. +* Output current object path to the control. * */ -VOID supSetFastEvent( - _In_ PFAST_EVENT Event +VOID supDisplayCurrentObjectPath( + _In_ HWND hwnd, + _In_opt_ PUNICODE_STRING Path, + _In_ BOOLEAN NormalizePath ) { - HANDLE eventHandle; - if (!_InterlockedBitTestAndSetPointer((PLONG_PTR)&Event->Value, FAST_EVENT_SET_SHIFT)) { - eventHandle = Event->EventHandle; + BOOL bNeedFree = FALSE; + UNICODE_STRING us, ns; + + if (Path) { + us = *Path; + } + else { + if (!supGetCurrentObjectPath(TRUE, &us)) + return; + + bNeedFree = TRUE; + } + + if (NormalizePath) { + if (supNormalizeUnicodeStringForDisplay(g_obexHeap, &us, &ns)) { + + SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM)ns.Buffer); + + supFreeUnicodeString(g_obexHeap, &ns); + } + } + else { + SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM)us.Buffer); + } + + if (bNeedFree) + supFreeDuplicatedUnicodeString(g_obexHeap, &us, FALSE); + +} + +/* +* supResolveSymbolicLinkTarget +* +* Purpose: +* +* Resolve symbolic link target and copy it to the supplied buffer. +* +* Return FALSE on error. +* +*/ +_Success_(return) +BOOL supResolveSymbolicLinkTarget( + _In_opt_ HANDLE LinkHandle, + _In_opt_ HANDLE RootDirectoryHandle, + _In_ PUNICODE_STRING LinkName, + _Out_ PUNICODE_STRING LinkTarget +) +{ + BOOL bResult = FALSE; + HANDLE hObject = NULL; + ULONG rLen = 0; + NTSTATUS ntStatus; + UNICODE_STRING linkTarget; + OBJECT_ATTRIBUTES obja; + PWCH stringBuffer; + + if (LinkHandle == NULL) { + // + // There is no handle, open it. + // + InitializeObjectAttributes(&obja, LinkName, OBJ_CASE_INSENSITIVE, RootDirectoryHandle, NULL); + if (!NT_SUCCESS(NtOpenSymbolicLinkObject(&hObject, SYMBOLIC_LINK_QUERY, &obja))) + return FALSE; + } + else { + hObject = LinkHandle; + } + + RtlInitEmptyUnicodeString(&linkTarget, NULL, 0); + ntStatus = NtQuerySymbolicLinkObject(hObject, &linkTarget, &rLen); + + if (ntStatus == STATUS_BUFFER_TOO_SMALL || + ntStatus == STATUS_BUFFER_OVERFLOW) + { + stringBuffer = (PWCH)supHeapAlloc(rLen + sizeof(UNICODE_NULL)); + if (stringBuffer) { + + linkTarget.Buffer = stringBuffer; + linkTarget.Length = 0; + linkTarget.MaximumLength = (USHORT)rLen; + + ntStatus = NtQuerySymbolicLinkObject(hObject, &linkTarget, &rLen); + if (NT_SUCCESS(ntStatus)) { + *LinkTarget = linkTarget; + bResult = TRUE; + } + else { + supHeapFree(stringBuffer); + } - if (eventHandle) - { - NtSetEvent(eventHandle, NULL); } + + } + + // + // If there is no input handle close what we opened. + // + if (LinkHandle == NULL) { + if (hObject) NtClose(hObject); } + + return bResult; } /* -* supTestFastEvent +* supResolveSymbolicLinkTargetNormalized * * Purpose: * -* Returns fast even state. +* Resolve symbolic link target in a GUI friendly output form. +* +* Return FALSE on error. * */ -BOOLEAN supTestFastEvent( - _In_ PFAST_EVENT Event +_Success_(return) +BOOL supResolveSymbolicLinkTargetNormalized( + _In_opt_ HANDLE LinkHandle, + _In_opt_ HANDLE RootDirectoryHandle, + _In_ PUNICODE_STRING LinkName, + _Out_ PUNICODE_STRING NormalizedLinkTarget ) { - return (BOOLEAN)Event->Set; + BOOL bResult; + UNICODE_STRING linkTarget; + + if (!supResolveSymbolicLinkTarget( + LinkHandle, + RootDirectoryHandle, + LinkName, + &linkTarget)) + { + return FALSE; + } + + bResult = supNormalizeUnicodeStringForDisplay(g_obexHeap, &linkTarget, NormalizedLinkTarget); + + supFreeDuplicatedUnicodeString(g_obexHeap, &linkTarget, FALSE); + + return bResult; } /* -* supResetFastEvent +* supClipboardCopyUnicodeStringRaw * * Purpose: * -* Perform fast even manual reset. +* Copy UNICODE_STRING buffer to the clipboard as C array. * */ -VOID supResetFastEvent( - _In_ PFAST_EVENT Event +VOID supClipboardCopyUnicodeStringRaw( + _In_ PUNICODE_STRING String ) { - if (Event == NULL) + BYTE* src, * end; + PWCH copyBuffer, dst; + SIZE_T length; + BYTE x; + + // + // '0', 'x', ',', ' ', 'A', 'B' = 6 * sizeof(WCHAR) + // + length = 100 + ((SIZE_T)String->Length * 12); + copyBuffer = (PWCH)supHeapAlloc(length); + if (copyBuffer == NULL) return; - if (supTestFastEvent(Event)) - Event->Value = FAST_EVENT_REFCOUNT_INC; + _strcpy(copyBuffer, TEXT("unsigned char data[")); + ultostr(String->Length, _strend(copyBuffer)); + dst = _strcat(copyBuffer, TEXT("] = {")); + + src = (BYTE*)String->Buffer; + end = (BYTE*)RtlOffsetToPointer(String->Buffer, String->Length); + while (src < end) { + + *dst++ = '0'; + *dst++ = 'x'; + x = *src++; + + *dst++ = nibbletoh(x >> 4, TRUE); + *dst++ = nibbletoh(x & 15, TRUE); + + if (src != end) { + *dst++ = ','; + *dst++ = ' '; + } + } + + *dst++ = 0; + _strcat(copyBuffer, TEXT("}; ")); + + supClipboardCopy(copyBuffer, _strlen(copyBuffer) * sizeof(WCHAR)); + supHeapFree(copyBuffer); } /* -* supWaitForFastEvent +* supFindUnicodeStringSubString * * Purpose: * -* Do the wait for event, if event object not allocated - allocate it. +* Return offset to substring if found and ULLONG_MAX instead. +* +* Case Insensitive. * */ -BOOLEAN supWaitForFastEvent( - _In_ PFAST_EVENT Event, - _In_opt_ PLARGE_INTEGER Timeout +SIZE_T supFindUnicodeStringSubString( + _In_ PUNICODE_STRING String, + _In_ PUNICODE_STRING SubString ) { - BOOLEAN result; - ULONG_PTR value; - HANDLE eventHandle; + SIZE_T length1; + SIZE_T length2; + UNICODE_STRING string1; + UNICODE_STRING string2; + WCHAR c; + SIZE_T i; - value = Event->Value; - if (value & FAST_EVENT_SET) - return TRUE; + if (SubString == NULL) + return 0; - if (Timeout && Timeout->QuadPart == 0) - return FALSE; + length1 = String->Length / sizeof(WCHAR); + length2 = SubString->Length / sizeof(WCHAR); - supReferenceFastEvent(Event); - eventHandle = Event->EventHandle; + if (length2 > length1) + return ULLONG_MAX; - if (eventHandle == NULL) { + if (length2 == 0) + return 0; - NtCreateEvent(&eventHandle, EVENT_ALL_ACCESS, NULL, NotificationEvent, FALSE); - assert(eventHandle); + string1.Buffer = String->Buffer; + string1.Length = SubString->Length - sizeof(WCHAR); + string2.Buffer = SubString->Buffer; + string2.Length = SubString->Length - sizeof(WCHAR); - if (NULL != _InterlockedCompareExchangePointer( - &Event->EventHandle, - eventHandle, - NULL)) + c = RtlUpcaseUnicodeChar(*string2.Buffer++); + + for (i = length1 - length2 + 1; i != 0; i--) { + if (RtlUpcaseUnicodeChar(*string1.Buffer++) == c && + RtlEqualUnicodeString(&string1, &string2, TRUE)) { - NtClose(eventHandle); - eventHandle = Event->EventHandle; + return (ULONG_PTR)(string1.Buffer - String->Buffer - 1); } - } - if (!(Event->Value & FAST_EVENT_SET)) { - result = (NtWaitForSingleObject(eventHandle, FALSE, Timeout) == STATUS_WAIT_0); + return ULLONG_MAX; +} + +/* +* supImageFixSections +* +* Purpose: +* +* Fix sections after dump. +* +*/ +BOOL supImageFixSections( + _In_ LPVOID Buffer +) +{ + PIMAGE_DOS_HEADER idh = NULL; + PIMAGE_FILE_HEADER fh1 = NULL; + PIMAGE_NT_HEADERS ImageHeaders = NULL; + PIMAGE_SECTION_HEADER Section = NULL; + DWORD vaddr, secalign, vsize, part; + WORD i, c; + + __try { + + idh = (PIMAGE_DOS_HEADER)Buffer; + fh1 = (PIMAGE_FILE_HEADER)((ULONG_PTR)Buffer + ((PIMAGE_DOS_HEADER)Buffer)->e_lfanew + sizeof(DWORD)); + if (fh1->Machine != IMAGE_FILE_MACHINE_AMD64) { + return FALSE; + } + + ImageHeaders = (PIMAGE_NT_HEADERS)((PBYTE)Buffer + idh->e_lfanew); + Section = IMAGE_FIRST_SECTION(ImageHeaders); + secalign = ImageHeaders->OptionalHeader.SectionAlignment; + c = ImageHeaders->FileHeader.NumberOfSections; + + vaddr = Section->VirtualAddress; + for (i = 0; i < c; i++) { + + //recalculate virtual size/address for each section + vsize = Section->Misc.VirtualSize; + part = vsize % secalign; + if (part != 0) { + vsize = vsize + secalign - part; + } + Section->SizeOfRawData = vsize; + Section->PointerToRawData = vaddr; + vaddr += vsize; + Section = (PIMAGE_SECTION_HEADER)((PBYTE)Section + sizeof(IMAGE_SECTION_HEADER)); + } + } - else { - result = TRUE; + __except (WOBJ_EXCEPTION_FILTER_LOG) { + return FALSE; } + return TRUE; +} - supDereferenceFastEvent(Event, eventHandle); +/* +* supCloseKnownPropertiesDialog +* +* Purpose: +* +* Send WM_CLOSE to known properties dialog if it present. +* +*/ +VOID supCloseKnownPropertiesDialog( + _In_opt_ HWND hwndDlg +) +{ + if (hwndDlg) + SendMessage(hwndDlg, WM_CLOSE, 0, 0); +} - return result; +/* +* supReadObexConfiguration +* +* Purpose: +* +* Reads program configuration data from registry if present. +* +*/ +_Success_(return) +BOOL supReadObexConfiguration( + _Out_ POBEX_CONFIG Configuration +) +{ + HKEY hKey; + DWORD data = 0, cbData, dwType; + WCHAR szBuffer[MAX_PATH + 1]; + WCHAR symbol; + + INT i; + WCHAR szValidSymbols[] = { + '!', '"', '#', '$', '%', '\'', + '(', ')','*', '+', '-', '.', + ':', ';', '<', '>', '=', '?', + '@', ']', '[', '^', '_', '`', + '{', '}', '~' }; + + Configuration->SymbolsPathValid = FALSE; + Configuration->SymbolsDbgHelpDllValid = FALSE; + Configuration->szNormalizationSymbol = OBJ_NAME_NORMALIZATION_SYMBOL; + + if (ERROR_SUCCESS == RegOpenKeyEx(HKEY_CURRENT_USER, supObexConfiguration, 0, KEY_READ, &hKey)) { + + cbData = sizeof(DWORD); + dwType = REG_DWORD; + if (ERROR_SUCCESS == RegQueryValueEx(hKey, supObexNormalizationSymbol, + NULL, &dwType, (LPBYTE)&data, &cbData)) + { + if (dwType == REG_DWORD && cbData == sizeof(DWORD)) { + symbol = (WCHAR)data; + for (i = 0; i < RTL_NUMBER_OF(szValidSymbols); i++) { + if (szValidSymbols[i] == symbol) { + Configuration->szNormalizationSymbol = symbol; + break; + } + } + } + } + + RtlSecureZeroMemory(&szBuffer, sizeof(szBuffer)); + cbData = MAX_PATH * sizeof(WCHAR); + dwType = REG_SZ; + if (ERROR_SUCCESS == RegQueryValueEx(hKey, supObexSymPath, + NULL, &dwType, (LPBYTE)&szBuffer, &cbData)) + { + if (dwType == REG_SZ && cbData > sizeof(UNICODE_NULL)) { + _strcpy(Configuration->szSymbolsPath, szBuffer); + Configuration->SymbolsPathValid = TRUE; + } + } + + RtlSecureZeroMemory(&szBuffer, sizeof(szBuffer)); + cbData = MAX_PATH * sizeof(WCHAR); + dwType = REG_SZ; + if (ERROR_SUCCESS == RegQueryValueEx(hKey, supObexSymDbgHelpDll, + NULL, &dwType, (LPBYTE)&szBuffer, &cbData)) + { + if (dwType == REG_SZ && cbData > sizeof(UNICODE_NULL)) { + _strcpy(Configuration->szSymbolsDbgHelpDll, szBuffer); + Configuration->SymbolsDbgHelpDllValid = TRUE; + } + } + + RegCloseKey(hKey); + return TRUE; + } + + return FALSE; +} + +POBEX_CONFIG supGetParametersBlock( + VOID) +{ + return &g_LoadedParametersBlock; } diff --git a/Source/WinObjEx64/sup.h b/Source/WinObjEx64/sup/sup.h similarity index 81% rename from Source/WinObjEx64/sup.h rename to Source/WinObjEx64/sup/sup.h index 660a8c07..9b24189d 100644 --- a/Source/WinObjEx64/sup.h +++ b/Source/WinObjEx64/sup/sup.h @@ -4,9 +4,9 @@ * * TITLE: SUP.H * -* VERSION: 1.94 +* VERSION: 2.00 * -* DATE: 07 Jun 2022 +* DATE: 19 Jun 2022 * * Common header file for the program support routines. * @@ -18,17 +18,57 @@ *******************************************************************************/ #pragma once -#include -#include - #define T_DEVICE_PROCEXP152 L"\\Device\\ProcExp152" #define PE_DEVICE_TYPE 0x8335 #define IOCTL_PE_OPEN_PROCESS_TOKEN CTL_CODE(PE_DEVICE_TYPE, 0x3, METHOD_BUFFERED, FILE_ANY_ACCESS) #define IOCTL_PE_OPEN_PROCESS CTL_CODE(PE_DEVICE_TYPE, 0xF, METHOD_BUFFERED, FILE_ANY_ACCESS) +#define T_SECUREBOOTSTATEKEY L"System\\CurrentControlSet\\Control\\SecureBoot\\State" +#define T_SECUREBOOTSTATEVALUE L"UEFISecureBootEnabled" + +#define T_VERSION_TRANSLATION L"\\VarFileInfo\\Translation" +#define FORMAT_VERSION_DESCRIPTION L"\\StringFileInfo\\%04x%04x\\FileDescription" +#define HHCTRLOCXKEY L"CLSID\\{ADB880A6-D8FF-11CF-9377-00AA003B7A11}\\InprocServer32" +#define T_OBJECT_TYPES L"ObjectTypes" + +#define FORMAT_TIME_DATE_VALUE L"%02hd:%02hd:%02hd, %02hd %ws %04hd" +#define FORMAT_TIME_VALUE L"%I64u:%02hd:%02hd" +#define FORMAT_TIME_VALUE_MS L"%hd:%02hd:%02hd.%03hd" +#define T_FORMATTED_ATTRIBUTE L" 0x" + +#define HHCTRLOCX L"hhctrl.ocx" + +#define T_WINSTA_SYSTEM L"-0x0-3e7$" +#define T_WINSTA_ANONYMOUS L"-0x0-3e6$" +#define T_WINSTA_LOCALSERVICE L"-0x0-3e5$" +#define T_WINSTA_NETWORK_SERVICE L"-0x0-3e4$" + +#define supServicesRegPath L"System\\CurrentControlSet\\Services\\" +#define supServicesRegPathSize sizeof(supServicesRegPath) - sizeof(WCHAR) + +#define supObexConfiguration L"Software\\WinObjEx64" +#define supObexSymPath L"SymPath" +#define supObexSymDbgHelpDll L"SymDbgHelpDll" +#define supObexNormalizationSymbol L"NormalizationSymbol" + +// All relatives to supObexConfiguration +typedef struct _OBEX_CONFIG { + BOOLEAN SymbolsPathValid; + BOOLEAN SymbolsDbgHelpDllValid; + WCHAR szNormalizationSymbol; //supObexNormalizationSymbol + WCHAR szSymbolsPath[MAX_PATH + 1]; //supObexSymbolsPath + WCHAR szSymbolsDbgHelpDll[MAX_PATH + 1]; //supObexSymbolsDbgHelpDll +} OBEX_CONFIG, * POBEX_CONFIG; + #define INITIAL_BUFFER_SIZE (256) * (1024) +#define GET_BIT(Integer, Bit) (((Integer) >> (Bit)) & 0x1) +#define SET_BIT(Integer, Bit) ((Integer) |= 1 << (Bit)) +#define CLEAR_BIT(Integer, Bit) ((Integer) &= ~(1 << (Bit))) + +#define PathFileExists(lpszPath) (GetFileAttributes(lpszPath) != (DWORD)-1) + typedef struct _SAPIDB { LIST_ENTRY ListHead; HANDLE HeapHandle; @@ -78,7 +118,7 @@ typedef struct _OBEX_THREAD_LOOKUP_ENTRY { typedef struct _ALPCPORT_ENUM_CONTEXT { _In_ USHORT AlpcPortTypeIndex; - _In_ LPCWSTR ObjectFullName; + _In_ PUNICODE_STRING ObjectName; _Out_ HANDLE ObjectHandle; } ALPCPORT_ENUM_CONTEXT, * PALPCPORT_ENUM_CONTEXT; @@ -90,6 +130,11 @@ typedef struct _PS_HANDLE_DUMP_ENUM_CONTEXT { _In_ PVOID ProcessList; } PS_HANDLE_DUMP_ENUM_CONTEXT, *PPS_HANDLE_DUMP_ENUM_CONTEXT; +typedef struct _WINSTA_DESC { + LPCWSTR lpszWinSta; + LPCWSTR lpszDesc; +} WINSTA_DESC, * PWINSTA_DESC; + typedef BOOL(CALLBACK* PSUPSHUTDOWNCALLBACK)( _In_opt_ PVOID Context ); @@ -100,27 +145,6 @@ typedef struct _SUP_SHUTDOWN_CALLBACK { PVOID Context; } SUP_SHUTDOWN_CALLBACK, PSUP_SHUTDOWN_CALLBACK; -typedef struct _FAST_EVENT { - union { - ULONG_PTR Value; - USHORT Set : 1; - USHORT RefCount : 15; - UCHAR Reserved; - UCHAR AvailableForUse; -#ifdef _WIN64 - ULONG Spare; -#endif - }; - HANDLE EventHandle; -} FAST_EVENT, * PFAST_EVENT; - -#define FAST_EVENT_SET 0x1 -#define FAST_EVENT_SET_SHIFT 0 -#define FAST_EVENT_REFCOUNT_SHIFT 1 -#define FAST_EVENT_REFCOUNT_INC 0x2 -#define FAST_EVENT_REFCOUNT_MASK (((ULONG_PTR)1 << 15) - 1) -#define FAST_EVENT_INIT { { FAST_EVENT_REFCOUNT_INC }, NULL } - // return true to stop enumeration typedef BOOL(CALLBACK* PENUMERATE_SL_CACHE_VALUE_DESCRIPTORS_CALLBACK)( _In_ SL_KMEM_CACHE_VALUE_DESCRIPTOR* CacheDescriptor, @@ -170,10 +194,6 @@ typedef struct _PROCESS_MITIGATION_POLICY_RAW_DATA { ULONG Value; } PROCESS_MITIGATION_POLICY_RAW_DATA, *PPROCESS_MITIGATION_POLICY_RAW_DATA; -#define GET_BIT(Integer, Bit) (((Integer) >> (Bit)) & 0x1) -#define SET_BIT(Integer, Bit) ((Integer) |= 1 << (Bit)) -#define CLEAR_BIT(Integer, Bit) ((Integer) &= ~(1 << (Bit))) - typedef struct _ENUMCHILDWNDDATA { RECT Rect; INT nCmdShow; @@ -190,11 +210,6 @@ typedef struct _SAPIDBENTRY { LPWSTR lpDeviceDesc; } SAPIDBENTRY, *PSAPIDBENTRY; -extern SAPIDB g_sapiDB; -extern SCMDB g_scmDB; - -#define PathFileExists(lpszPath) (GetFileAttributes(lpszPath) != (DWORD)-1) - typedef struct tagVERBLOCK { WORD wTotLen; WORD wValLen; @@ -257,9 +272,79 @@ typedef struct _FILE_VIEW_INFO { typedef struct _SUP_BANNER_DATA { LPCWSTR lpText; LPCWSTR lpCaption; - BOOL fList; } SUP_BANNER_DATA, * PSUP_BANNER_DATA; +// +// Fast event +// +typedef struct _FAST_EVENT { + union { + ULONG_PTR Value; + USHORT Set : 1; + USHORT RefCount : 15; + UCHAR Reserved; + UCHAR AvailableForUse; +#ifdef _WIN64 + ULONG Spare; +#endif + }; + HANDLE EventHandle; +} FAST_EVENT, * PFAST_EVENT; + +#define FAST_EVENT_SET 0x1 +#define FAST_EVENT_SET_SHIFT 0 +#define FAST_EVENT_REFCOUNT_SHIFT 1 +#define FAST_EVENT_REFCOUNT_INC 0x2 +#define FAST_EVENT_REFCOUNT_MASK (((ULONG_PTR)1 << 15) - 1) +#define FAST_EVENT_INIT { { FAST_EVENT_REFCOUNT_INC }, NULL } + +VOID supInitFastEvent( + _In_ PFAST_EVENT Event); + +VOID supReferenceFastEvent( + _In_ PFAST_EVENT Event); + +VOID supDereferenceFastEvent( + _In_ PFAST_EVENT Event, + _In_opt_ HANDLE EventHandle); + +VOID supSetFastEvent( + _In_ PFAST_EVENT Event); + +BOOLEAN supTestFastEvent( + _In_ PFAST_EVENT Event); + +VOID supResetFastEvent( + _In_ PFAST_EVENT Event); + +BOOLEAN supWaitForFastEvent( + _In_ PFAST_EVENT Event, + _In_opt_ PLARGE_INTEGER Timeout); + +// +// Heap memory allocations +// +HANDLE supCreateHeap( + _In_ ULONG HeapFlags, + _In_ BOOL TerminateOnCorruption); + +BOOL supDestroyHeap( + _In_ HANDLE HeapHandle); + +PVOID supHeapAllocEx( + _In_ HANDLE Heap, + _In_ SIZE_T Size); + +BOOL supHeapFreeEx( + _In_ HANDLE Heap, + _In_ PVOID Memory); + +PVOID supHeapAlloc( + _In_ SIZE_T Size); + +BOOL supHeapFree( + _In_ PVOID Memory); + // // Use shared NTSUP forward. // @@ -274,7 +359,7 @@ typedef struct _SUP_BANNER_DATA { #define supQueryUserModeAccessibleRange ntsupQueryUserModeAccessibleRange #define supIsProcess32bit ntsupIsProcess32bit #define supQueryThreadWin32StartAddress ntsupQueryThreadWin32StartAddress -#define supOpenDirectory ntsupOpenDirectory +#define supOpenDirectoryEx ntsupOpenDirectoryEx #define supQueryProcessName ntsupQueryProcessName #define supQueryProcessEntryById ntsupQueryProcessEntryById #define supWriteBufferToFile ntsupWriteBufferToFile @@ -307,23 +392,34 @@ typedef struct _SUP_BANNER_DATA { #define supQueryThreadInformation(ThreadHandle, ThreadInformationClass, Buffer, ReturnLength) \ ntsupQueryThreadInformation(ThreadHandle, ThreadInformationClass, Buffer, ReturnLength, supHeapAlloc, supHeapFree) +FORCEINLINE BOOLEAN supUnicodeStringValid( + _In_ PUNICODE_STRING SourceString +) +{ + if (SourceString == NULL) + return FALSE; + + if (((SourceString->Length % sizeof(WCHAR)) != 0) || + ((SourceString->MaximumLength % sizeof(WCHAR)) != 0) || + (SourceString->Length > SourceString->MaximumLength) || + (SourceString->MaximumLength > (UNICODE_STRING_MAX_CHARS * sizeof(WCHAR)))) + { + return FALSE; + } + else if ((SourceString->Buffer == NULL) && + ((SourceString->Length != 0) || (SourceString->MaximumLength != 0))) + { + return FALSE; + } + + return TRUE; +} + +#define supIsRootDirectory(DirectoryName) RtlEqualUnicodeString(ObGetPredefinedUnicodeString(OBP_ROOT), DirectoryName, TRUE) + BOOL supInitMSVCRT( VOID); -#ifndef _DEBUG -FORCEINLINE PVOID supHeapAlloc( - _In_ SIZE_T Size); - -FORCEINLINE BOOL supHeapFree( - _In_ PVOID Memory); -#else -PVOID supHeapAlloc( - _In_ SIZE_T Size); - -BOOL supHeapFree( - _In_ PVOID Memory); -#endif - VOID supTreeListEnableRedraw( _In_ HWND TreeList, _In_ BOOL fEnable); @@ -360,11 +456,45 @@ HICON supGetMainIcon( _In_ INT cx, _In_ INT cy); -void supCopyMemory( - _Inout_ void* dest, - _In_ size_t ccdest, - _In_ const void* src, - _In_ size_t ccsrc); +_Success_(return) +BOOL supNormalizeUnicodeStringForDisplay( + _In_ HANDLE HeapHandle, + _In_ PUNICODE_STRING SourceString, + _Out_ PUNICODE_STRING NormalizedString); + +_Success_(return) +BOOL supFreeUnicodeString( + _In_ HANDLE HeapHandle, + _Inout_ PUNICODE_STRING String); + +_Success_(return) +BOOL supFreeDuplicatedUnicodeString( + _In_ HANDLE HeapHandle, + _Inout_ PUNICODE_STRING DuplicatedString, + _In_ BOOL DoZeroMemory); + +_Success_(return) +BOOL supDuplicateUnicodeString( + _In_ HANDLE HeapHandle, + _Out_ PUNICODE_STRING DestinationString, + _In_ PUNICODE_STRING SourceString); + +_Success_(return) +BOOL supCreateObjectPathFromElements( + _In_ PUNICODE_STRING ObjectName, + _In_ PUNICODE_STRING DirectoryName, + _Out_ PUNICODE_STRING ObjectPath, + _In_ BOOLEAN NullTerminate); + +_Success_(return) +BOOL supCreateObjectPathFromCurrentPath( + _In_ PUNICODE_STRING ObjectName, + _Out_ PUNICODE_STRING ObjectPath, + _In_ BOOLEAN NullTerminate); + +SIZE_T supFindUnicodeStringSubString( + _In_ PUNICODE_STRING String, + _In_ PUNICODE_STRING SubString); VOID supCenterWindow( _In_ HWND hwnd); @@ -388,8 +518,7 @@ VOID supCloseLoadBanner( VOID supDisplayLoadBanner( _In_ LPCWSTR lpMessage, - _In_opt_ LPCWSTR lpCaption, - _In_ BOOL UseList); + _In_opt_ LPCWSTR lpCaption); HIMAGELIST supLoadImageList( _In_ HINSTANCE hInst, @@ -399,7 +528,7 @@ HIMAGELIST supLoadImageList( PVOID supGetObjectTypesInfo( VOID); -UINT supGetObjectNameIndexByTypeIndex( +WOBJ_OBJECT_TYPE supGetObjectNameIndexByTypeIndex( _In_ PVOID Object, _In_ UCHAR TypeIndex); @@ -493,24 +622,25 @@ BOOL supQuerySectionFileInfo( _In_ DWORD ccBuffer); BOOL supQueryTypeInfo( - _In_ LPCWSTR lpTypeName, - _Inout_ LPWSTR Buffer, - _In_ DWORD cchBuffer); + _In_ PUNICODE_STRING TypeName, + _Inout_ LPWSTR Buffer, + _In_ DWORD cchhBuffer); BOOL supQueryDriverDescription( _In_ LPCWSTR lpDriverName, _Inout_ LPWSTR Buffer, - _In_ DWORD ccBuffer); + _In_ DWORD cchBuffer); BOOL supQueryDeviceDescription( - _In_ LPCWSTR lpDeviceName, + _In_opt_ PUNICODE_STRING Path, + _In_ PUNICODE_STRING Name, _Inout_ LPWSTR Buffer, _In_ DWORD ccBuffer); BOOL supQueryWinstationDescription( _In_ LPCWSTR lpWindowStationName, _Inout_ LPWSTR Buffer, - _In_ DWORD ccBuffer); + _In_ DWORD cchBuffer); PVOID supGetTokenInfo( _In_ HANDLE TokenHandle, @@ -537,11 +667,6 @@ NTSTATUS supOpenDeviceObjectEx( _In_ ACCESS_MASK DesiredAccess, _In_ POBJECT_ATTRIBUTES ObjectAttributes); -NTSTATUS supOpenDirectoryForObject( - _Out_ PHANDLE DirectoryHandle, - _In_ LPCWSTR lpObjectName, - _In_ LPCWSTR lpDirectory); - BOOL supDumpSyscallTableConverted( _In_ ULONG_PTR ServiceTableAddress, _In_ ULONG ServiceLimit, @@ -656,8 +781,8 @@ INT supGetMaxCompareTwoFixedStrings( NTSTATUS supOpenNamedObjectByType( _Out_ HANDLE* ObjectHandle, _In_ ULONG TypeIndex, - _In_ LPCWSTR ObjectDirectory, - _In_ LPCWSTR ObjectName, + _In_ PUNICODE_STRING ObjectDirectory, + _In_ PUNICODE_STRING ObjectName, _In_ ACCESS_MASK DesiredAccess); HANDLE supOpenObjectFromContext( @@ -728,6 +853,13 @@ BOOL supPrintTimeConverted( _In_ WCHAR* lpszBuffer, _In_ SIZE_T cchBuffer); +_Success_(return) +BOOL supGetTreeViewItemParam( + _In_ HWND hwndTreeView, + _In_ HTREEITEM hTreeItem, + _Out_ PVOID * outParam); + +_Success_(return) BOOL supGetListViewItemParam( _In_ HWND hwndListView, _In_ INT itemIndex, @@ -793,13 +925,10 @@ BOOLEAN supSLCacheEnumerate( _In_opt_ PENUMERATE_SL_CACHE_VALUE_DESCRIPTORS_CALLBACK Callback, _In_opt_ PVOID Context); -HRESULT supShellExecInExplorerProcessEx( +HRESULT supShellExecInExplorerProcess( _In_ PCWSTR pszFile, _In_opt_ PCWSTR pszArguments); -HRESULT WINAPI supShellExecInExplorerProcess( - _In_ PCWSTR pszFile); - VOID supShowNtStatus( _In_ HWND hWnd, _In_ LPCWSTR lpText, @@ -814,8 +943,9 @@ BOOLEAN supLoadIconForObjectType( _In_ HIMAGELIST ImageList, _In_ BOOLEAN IsShadow); -VOID supDestroyIconForObjectType( - _In_ PROP_OBJECT_INFO * Context); +NTSTATUS supOpenLinkedToken( + _In_ HANDLE TokenHandle, + _Out_ PHANDLE LinkedTokenHandle); NTSTATUS supOpenTokenByParam( _In_ CLIENT_ID * ClientId, @@ -912,8 +1042,7 @@ BOOL supEnumHandleDump( NTSTATUS supOpenPortObjectByName( _Out_ PHANDLE ObjectHandle, _In_ ACCESS_MASK DesiredAccess, - _Out_opt_ PHANDLE ReferenceHandle, - _In_ LPCWSTR ObjectName); + _In_ PUNICODE_STRING ObjectName); NTSTATUS supOpenPortObjectFromContext( _Out_ PHANDLE ObjectHandle, @@ -1039,28 +1168,52 @@ HANDLE supCreateDialogWorkerThread( _In_opt_ __drv_aliasesMem LPVOID lpParameter, _In_ DWORD dwCreationFlags); -VOID supInitFastEvent( - _In_ PFAST_EVENT Event); +VOID CALLBACK supSymCallbackReportEvent( + _In_ LPCWSTR EventText); -VOID supReferenceFastEvent( - _In_ PFAST_EVENT Event); +VOID supBuildCurrentObjectList( + _In_ PVOID ListHead); -VOID supDereferenceFastEvent( - _In_ PFAST_EVENT Event, - _In_opt_ HANDLE EventHandle); +_Success_(return != FALSE) +BOOL supGetCurrentObjectPath( + _In_ BOOLEAN IncludeName, + _Out_ PUNICODE_STRING ObjectPath); -VOID supSetFastEvent( - _In_ PFAST_EVENT Event); +_Success_(return) +BOOL supGetCurrentObjectName( + _Out_ PUNICODE_STRING ObjectName); -BOOLEAN supTestFastEvent( - _In_ PFAST_EVENT Event); +VOID supDisplayCurrentObjectPath( + _In_ HWND hwnd, + _In_opt_ PUNICODE_STRING Path, + _In_ BOOLEAN NormalizePath); -VOID supResetFastEvent( - _In_ PFAST_EVENT Event); +_Success_(return) +BOOL supResolveSymbolicLinkTarget( + _In_opt_ HANDLE LinkHandle, + _In_opt_ HANDLE RootDirectoryHandle, + _In_ PUNICODE_STRING LinkName, + _Out_ PUNICODE_STRING LinkTarget); -BOOLEAN supWaitForFastEvent( - _In_ PFAST_EVENT Event, - _In_opt_ PLARGE_INTEGER Timeout); +_Success_(return) +BOOL supResolveSymbolicLinkTargetNormalized( + _In_opt_ HANDLE LinkHandle, + _In_opt_ HANDLE RootDirectoryHandle, + _In_ PUNICODE_STRING LinkName, + _Out_ PUNICODE_STRING NormalizedLinkTarget); -VOID CALLBACK supSymCallbackReportEvent( - _In_ LPCWSTR EventText); +VOID supClipboardCopyUnicodeStringRaw( + _In_ PUNICODE_STRING String); + +BOOL supImageFixSections( + _In_ LPVOID Buffer); + +VOID supCloseKnownPropertiesDialog( + _In_opt_ HWND hwndDlg); + +_Success_(return) +BOOL supReadObexConfiguration( + _Out_ POBEX_CONFIG Configuration); + +POBEX_CONFIG supGetParametersBlock( + VOID); diff --git a/Source/WinObjEx64/sup/sync.c b/Source/WinObjEx64/sup/sync.c new file mode 100644 index 00000000..c4d43750 --- /dev/null +++ b/Source/WinObjEx64/sup/sync.c @@ -0,0 +1,195 @@ +/******************************************************************************* +* +* (C) COPYRIGHT AUTHORS, 2022 +* +* TITLE: SYNC.C +* +* VERSION: 2.00 +* +* DATE: 19 Jun 2022 +* +* Synchronization primitives. +* +* +* THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF +* ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED +* TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A +* PARTICULAR PURPOSE. +* +*******************************************************************************/ +#include "global.h" + +/* +* +* Fast events, taken from ph2 +* +*/ + +/* +* supInitFastEvent +* +* Purpose: +* +* Initialize fast event. +* +*/ +VOID supInitFastEvent( + _In_ PFAST_EVENT Event +) +{ + Event->Value = FAST_EVENT_REFCOUNT_INC; + Event->EventHandle = NULL; +} + +/* +* supReferenceFastEvent +* +* Purpose: +* +* Make a reference for fast event. +* +*/ +VOID supReferenceFastEvent( + _In_ PFAST_EVENT Event +) +{ + _InterlockedExchangeAddPointer((PLONG_PTR)&Event->Value, FAST_EVENT_REFCOUNT_INC); +} + +/* +* supDereferenceFastEvent +* +* Purpose: +* +* Remove reference from fast event. +* +*/ +VOID supDereferenceFastEvent( + _In_ PFAST_EVENT Event, + _In_opt_ HANDLE EventHandle +) +{ + ULONG_PTR value; + + value = _InterlockedExchangeAddPointer((PLONG_PTR)&Event->Value, -FAST_EVENT_REFCOUNT_INC); + if (((value >> FAST_EVENT_REFCOUNT_SHIFT) & FAST_EVENT_REFCOUNT_MASK) - 1 == 0) + { + if (EventHandle) + { + NtClose(EventHandle); + Event->EventHandle = NULL; + } + } +} + +/* +* supSetFastEvent +* +* Purpose: +* +* Set event to signaled state. +* +*/ +VOID supSetFastEvent( + _In_ PFAST_EVENT Event +) +{ + HANDLE eventHandle; + if (!_InterlockedBitTestAndSetPointer((PLONG_PTR)&Event->Value, FAST_EVENT_SET_SHIFT)) { + eventHandle = Event->EventHandle; + + if (eventHandle) + { + NtSetEvent(eventHandle, NULL); + } + } +} + +/* +* supTestFastEvent +* +* Purpose: +* +* Returns fast event state. +* +*/ +BOOLEAN supTestFastEvent( + _In_ PFAST_EVENT Event +) +{ + return (BOOLEAN)Event->Set; +} + +/* +* supResetFastEvent +* +* Purpose: +* +* Perform fast event manual reset. +* +*/ +VOID supResetFastEvent( + _In_ PFAST_EVENT Event +) +{ + if (Event == NULL) + return; + + if (supTestFastEvent(Event)) + Event->Value = FAST_EVENT_REFCOUNT_INC; +} + +/* +* supWaitForFastEvent +* +* Purpose: +* +* Do the wait for event, if event object not allocated - allocate it. +* +*/ +BOOLEAN supWaitForFastEvent( + _In_ PFAST_EVENT Event, + _In_opt_ PLARGE_INTEGER Timeout +) +{ + BOOLEAN result; + ULONG_PTR value; + HANDLE eventHandle; + + value = Event->Value; + if (value & FAST_EVENT_SET) + return TRUE; + + if (Timeout && Timeout->QuadPart == 0) + return FALSE; + + supReferenceFastEvent(Event); + eventHandle = Event->EventHandle; + + if (eventHandle == NULL) { + + NtCreateEvent(&eventHandle, EVENT_ALL_ACCESS, NULL, NotificationEvent, FALSE); + assert(eventHandle); + + if (NULL != _InterlockedCompareExchangePointer( + &Event->EventHandle, + eventHandle, + NULL)) + { + NtClose(eventHandle); + eventHandle = Event->EventHandle; + } + + } + + if (!(Event->Value & FAST_EVENT_SET)) { + result = (NtWaitForSingleObject(eventHandle, FALSE, Timeout) == STATUS_WAIT_0); + } + else { + result = TRUE; + } + + supDereferenceFastEvent(Event, eventHandle); + + return result; +} diff --git a/Source/WinObjEx64/wine.c b/Source/WinObjEx64/sup/wine.c similarity index 79% rename from Source/WinObjEx64/wine.c rename to Source/WinObjEx64/sup/wine.c index 0996be71..5f6effea 100644 --- a/Source/WinObjEx64/wine.c +++ b/Source/WinObjEx64/sup/wine.c @@ -4,9 +4,9 @@ * * TITLE: WINE.C * -* VERSION: 1.94 +* VERSION: 2.00 * -* DATE: 07 Jun 2022 +* DATE: 19 Jun 2022 * * THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF * ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED @@ -17,10 +17,14 @@ #include "global.h" #include "ntos/ntldr.h" -#include "winedebug.h" + +#define _WINE_DEBUG_MODE +#undef _WINE_DEBUG_MODE + +typedef char* (__cdecl* pwine_get_version)(void); /* -* wine_get_version +* GetWineVersion * * Purpose: * @@ -30,7 +34,9 @@ * */ #ifndef _WINE_DEBUG_MODE -const char* wine_get_version(void) +PCHAR GetWineVersion( + VOID +) { pwine_get_version pfn = NULL; HMODULE hmod; @@ -58,7 +64,7 @@ const char* wine_get_version(void) return NULL; } #else -const char* wine_get_version(void) +PCHAR WineGetVersion(void) { return "6.0"; } @@ -66,18 +72,20 @@ const char* wine_get_version(void) /* -* is_wine +* IsWine * * Purpose: * * Query if there is a Wine layer enabled. * */ -int is_wine(void) +BOOLEAN IsWine( + VOID +) { - CONST CHAR* szWine; + PCHAR lpWine; - szWine = wine_get_version(); + lpWine = GetWineVersion(); - return (szWine != NULL); + return (lpWine != NULL); } diff --git a/Source/WinObjEx64/extras/extrasCallbacks.h b/Source/WinObjEx64/sup/wine.h similarity index 72% rename from Source/WinObjEx64/extras/extrasCallbacks.h rename to Source/WinObjEx64/sup/wine.h index 717d4484..cdd48b2c 100644 --- a/Source/WinObjEx64/extras/extrasCallbacks.h +++ b/Source/WinObjEx64/sup/wine.h @@ -2,13 +2,13 @@ * * (C) COPYRIGHT AUTHORS, 2018 - 2022 * -* TITLE: EXTRASCALLBACKS.H +* TITLE: WINE.H * -* VERSION: 1.94 +* VERSION: 2.00 * -* DATE: 04 Jun 2022 +* DATE: 19 Jun 2022 * -* Common header file for Callbacks dialog. +* Wine/Wine staging support header file. * * THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF * ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED @@ -19,5 +19,5 @@ #pragma once -VOID extrasCreateCallbacksDialog( - VOID); +PCHAR GetWineVersion(VOID); +BOOLEAN IsWine(VOID); diff --git a/Source/WinObjEx64/supConsts.h b/Source/WinObjEx64/supConsts.h deleted file mode 100644 index d762bb5f..00000000 --- a/Source/WinObjEx64/supConsts.h +++ /dev/null @@ -1,50 +0,0 @@ -/******************************************************************************* -* -* (C) COPYRIGHT AUTHORS, 2015 - 2020 -* -* TITLE: SUPCONSTS.H -* -* VERSION: 1.87 -* -* DATE: 25 July 2020 -* -* Consts header file for support unit. -* -* THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF -* ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED -* TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A -* PARTICULAR PURPOSE. -* -*******************************************************************************/ -#pragma once - -#define T_SECUREBOOTSTATEKEY L"System\\CurrentControlSet\\Control\\SecureBoot\\State" -#define T_SECUREBOOTSTATEVALUE L"UEFISecureBootEnabled" - -#define T_VERSION_TRANSLATION L"\\VarFileInfo\\Translation" -#define FORMAT_VERSION_DESCRIPTION L"\\StringFileInfo\\%04x%04x\\FileDescription" -#define HHCTRLOCXKEY L"CLSID\\{ADB880A6-D8FF-11CF-9377-00AA003B7A11}\\InprocServer32" -#define T_OBJECTTYPES L"\\ObjectTypes" - -#define FORMAT_TIME_DATE_VALUE L"%02hd:%02hd:%02hd, %02hd %ws %04hd" -#define FORMAT_TIME_VALUE L"%I64u:%02hd:%02hd" -#define FORMAT_TIME_VALUE_MS L"%hd:%02hd:%02hd.%03hd" -#define T_FORMATTED_ATTRIBUTE L" 0x" - -#define HHCTRLOCX L"hhctrl.ocx" - -#define T_WINSTA_SYSTEM L"-0x0-3e7$" -#define T_WINSTA_ANONYMOUS L"-0x0-3e6$" -#define T_WINSTA_LOCALSERVICE L"-0x0-3e5$" -#define T_WINSTA_NETWORK_SERVICE L"-0x0-3e4$" - -#define supServicesRegPath L"System\\CurrentControlSet\\Services\\" -#define supServicesRegPathSize sizeof(supServicesRegPath) - sizeof(WCHAR) - -#define MAX_KNOWN_WINSTA_DESCRIPTIONS 4 -static WINSTA_DESC g_WinstaDescArray[MAX_KNOWN_WINSTA_DESCRIPTIONS] = { - { T_WINSTA_SYSTEM, L"System" }, - { T_WINSTA_ANONYMOUS, L"Anonymous" }, - { T_WINSTA_LOCALSERVICE, L"Local Service" }, - { T_WINSTA_NETWORK_SERVICE, L"Network Service" } -}; diff --git a/Source/WinObjEx64/symparser.c b/Source/WinObjEx64/symparser.c index 09162bd0..12d78202 100644 --- a/Source/WinObjEx64/symparser.c +++ b/Source/WinObjEx64/symparser.c @@ -6,7 +6,7 @@ * * VERSION: 1.18 * -* DATE: 05 Jun 2021 +* DATE: 20 Jun 2021 * * DbgHelp wrapper for symbols parser support. * @@ -1415,7 +1415,7 @@ PSYMCONTEXT SymParserCreate( Context = (PSYMCONTEXT)supHeapAlloc(sizeof(SYMCONTEXT)); if (Context) { - RtlCopyMemory(&Context->DbgHelp, &g_SymGlobals.ApiSet, sizeof(DBGHELP_PTRS)); + Context->DbgHelp = g_SymGlobals.ApiSet; Context->ProcessHandle = g_SymGlobals.ProcessHandle; Context->ModuleBase = 0; @@ -1489,7 +1489,7 @@ BOOL SymGlobalsInit( HMODULE hDbg = NULL; LPWSTR locaDbgHelplPath = NULL; SIZE_T nLen; - WCHAR szWinPath[MAX_PATH + 1]; + WCHAR szWinPath[MAX_PATH * 2]; RtlSecureZeroMemory(&g_SymGlobals, sizeof(g_SymGlobals)); @@ -1502,19 +1502,6 @@ BOOL SymGlobalsInit( return FALSE; } - nLen = _strlen(lpSystemPath); - if (nLen > MAX_PATH) { - SetLastError(ERROR_INVALID_PARAMETER); - return FALSE; - } - - RtlSecureZeroMemory(&szWinPath, sizeof(szWinPath)); - - _strncpy(szWinPath, - MAX_PATH, - lpSystemPath, - nLen); - nLen = _strlen(lpTempPath); if (nLen > MAX_PATH) { SetLastError(ERROR_INVALID_PARAMETER); @@ -1530,8 +1517,23 @@ BOOL SymGlobalsInit( } else { + + nLen = _strlen(lpSystemPath); + if (nLen > MAX_PATH) { + SetLastError(ERROR_INVALID_PARAMETER); + return FALSE; + } + + RtlSecureZeroMemory(&szWinPath, sizeof(szWinPath)); + + _strncpy(szWinPath, + MAX_PATH, + lpSystemPath, + nLen); + supPathAddBackSlash(szWinPath); _strcat(szWinPath, DEFAULT_DLL); + locaDbgHelplPath = szWinPath; } diff --git a/Source/WinObjEx64/sysinfoDlg.c b/Source/WinObjEx64/sysinfoDlg.c index 4e83fccb..d68f11e6 100644 --- a/Source/WinObjEx64/sysinfoDlg.c +++ b/Source/WinObjEx64/sysinfoDlg.c @@ -4,9 +4,9 @@ * * TITLE: SYSINFODLG.C * -* VERSION: 1.94 +* VERSION: 2.00 * -* DATE: 07 Jun 2022 +* DATE: 19 Jun 2022 * * System Information Dialog. * @@ -199,6 +199,8 @@ VOID SysInfoCollectInformation( HKEY hKey; DWORD dwType, cbData, dwValue; + OBEX_CONFIG* obConfig = supGetParametersBlock(); + PARAFORMAT ParaFormat; CHARRANGE CharRange; @@ -241,7 +243,7 @@ VOID SysInfoCollectInformation( // RtlSecureZeroMemory(szBuffer, sizeof(szBuffer)); if (g_WinObj.IsWine) { - lpWineVersion = (PCHAR)wine_get_version(); + lpWineVersion = (PCHAR)GetWineVersion(); RtlSecureZeroMemory(szWineVer, sizeof(szWineVer)); if (0 == MultiByteToWideChar(CP_ACP, 0, lpWineVersion, (INT)_strlen_a(lpWineVersion), szWineVer, RTL_NUMBER_OF(szWineVer))) @@ -345,6 +347,15 @@ VOID SysInfoCollectInformation( AddParameterValueBool(hwndOutput, TEXT("Internal.IsFullAdmin"), g_kdctx.IsFullAdmin); //admin privileges available AddParameterValueBool(hwndOutput, TEXT("Internal.IsSecureBoot"), g_kdctx.IsSecureBoot); //secure boot enabled AddParameterValueBool(hwndOutput, TEXT("Internal.IsWine"), g_WinObj.IsWine); + AddParameterValue32Hex(hwndOutput, TEXT("Internal.NameNormalizationSymbol"), (ULONG)g_ObNameNormalizationSymbol); + + if (obConfig->SymbolsDbgHelpDllValid) { + AddParameterValue(hwndOutput, TEXT("Parameters.SymbolsDbgHelpDll"), obConfig->szSymbolsDbgHelpDll); + } + if (obConfig->SymbolsPathValid) { + AddParameterValue(hwndOutput, TEXT("Parameters.SymbolsPath"), obConfig->szSymbolsPath); + } + AddParameterValueBool(hwndOutput, TEXT("MitigationFlags.ASLRPolicy"), g_kdctx.MitigationFlags.ASLRPolicy); AddParameterValueBool(hwndOutput, TEXT("MitigationFlags.DynamicCode"), g_kdctx.MitigationFlags.DynamicCode); AddParameterValueBool(hwndOutput, TEXT("MitigationFlags.ExtensionPointDisable"), g_kdctx.MitigationFlags.ExtensionPointDisable); @@ -376,7 +387,7 @@ VOID SysInfoCollectInformation( lpType = L"Microsoft"; break; } - AddParameterValue(hwndOutput, TEXT("Driver.ActiveProvider"), lpType); + AddParameterValue(hwndOutput, TEXT("Driver.SelectedProvider"), lpType); // // Ntoskrnl @@ -393,12 +404,6 @@ VOID SysInfoCollectInformation( AddParameterValue64Hex(hwndOutput, TEXT("NtSymContext.ModuleBase"), ((PSYMCONTEXT)g_kdctx.NtOsSymContext)->ModuleBase); } - // - // Directory object - // - AddParameterValue64Hex(hwndOutput, TEXT("System.DirectoryRootObject"), g_kdctx.DirectoryRootObject); //address of object root directory - AddParameterValueUlong(hwndOutput, TEXT("System.DirectoryTypeIndex"), g_kdctx.DirectoryTypeIndex); - // // Product info // @@ -411,17 +416,31 @@ VOID SysInfoCollectInformation( AddParameterValue64Hex(hwndOutput, TEXT("System.MinimumUserModeAddress"), (ULONG_PTR)g_kdctx.MinimumUserModeAddress); AddParameterValue64Hex(hwndOutput, TEXT("System.MaximumUserModeAddress"), (ULONG_PTR)g_kdctx.MaximumUserModeAddress); - // - // List kldbg data. - // - AddParameterValueBool(hwndOutput, TEXT("System.ObHeaderCookieValid"), g_kdctx.Data->ObHeaderCookie.Valid); - AddParameterValue32Hex(hwndOutput, TEXT("System.ObHeaderCookie"), g_kdctx.Data->ObHeaderCookie.Value); + if (g_kdctx.IsFullAdmin) { - AddParameterValueUlong(hwndOutput, TEXT("System.KiServiceLimit"), g_kdctx.Data->KeServiceDescriptorTable.Limit); - AddParameterValue64Hex(hwndOutput, TEXT("System.KiServiceTableAddress"), (ULONG_PTR)g_kdctx.Data->KeServiceDescriptorTable.Base); - AddParameterValue64Hex(hwndOutput, TEXT("System.IopInvalidDeviceRequest"), (ULONG_PTR)g_kdctx.Data->IopInvalidDeviceRequest); - AddParameterValue64Hex(hwndOutput, TEXT("System.PrivateNamespaceLookupTable"), (ULONG_PTR)g_kdctx.Data->PrivateNamespaceLookupTable); + // + // List kldbg data if there is something to show since this data fetched dynamically during usage. + // + AddParameterValueBool(hwndOutput, TEXT("System.ObHeaderCookieValid"), g_kdctx.Data->ObHeaderCookie.Valid); + AddParameterValue32Hex(hwndOutput, TEXT("System.ObHeaderCookie"), g_kdctx.Data->ObHeaderCookie.Value); + AddParameterValueUlong(hwndOutput, TEXT("System.DirectoryTypeIndex"), g_kdctx.DirectoryTypeIndex); + if (g_kdctx.DirectoryRootObject) + AddParameterValue64Hex(hwndOutput, TEXT("System.DirectoryRootObject"), g_kdctx.DirectoryRootObject); + + if (g_kdctx.Data->KeServiceDescriptorTable.Limit) + AddParameterValueUlong(hwndOutput, TEXT("System.KiServiceLimit"), g_kdctx.Data->KeServiceDescriptorTable.Limit); + + if (g_kdctx.Data->KeServiceDescriptorTable.Base) + AddParameterValue64Hex(hwndOutput, TEXT("System.KiServiceTableAddress"), (ULONG_PTR)g_kdctx.Data->KeServiceDescriptorTable.Base); + + if (g_kdctx.Data->IopInvalidDeviceRequest) + AddParameterValue64Hex(hwndOutput, TEXT("System.IopInvalidDeviceRequest"), (ULONG_PTR)g_kdctx.Data->IopInvalidDeviceRequest); + + if (g_kdctx.Data->PrivateNamespaceLookupTable) + AddParameterValue64Hex(hwndOutput, TEXT("System.PrivateNamespaceLookupTable"), (ULONG_PTR)g_kdctx.Data->PrivateNamespaceLookupTable); + + } // // List other data. // @@ -585,10 +604,11 @@ LRESULT CALLBACK SysInfoDialogProc( UNREFERENCED_PARAMETER(lParam); switch (uMsg) { - case WM_INITDIALOG: - - SysInfoCollectInformation(hwnd); - break; + case WM_SHOWWINDOW: + if (LOWORD(wParam)) { + SysInfoCollectInformation(hwnd); + } + return TRUE; case WM_COMMAND: switch (GET_WM_COMMAND_ID(wParam, lParam)) { @@ -615,8 +635,10 @@ VOID ShowSysInfoDialog( _In_ HWND hwndParent ) { - if (!supRichEdit32Load()) + if (!supRichEdit32Load()) { + MessageBox(hwndParent, TEXT("Could not load RichEdit library"), NULL, MB_ICONERROR); return; + } DialogBoxParam(g_WinObj.hInstance, MAKEINTRESOURCE(IDD_DIALOG_GLOBALS), diff --git a/Source/WinObjEx64/sysinfoDlg.h b/Source/WinObjEx64/sysinfoDlg.h deleted file mode 100644 index 8d9cb925..00000000 --- a/Source/WinObjEx64/sysinfoDlg.h +++ /dev/null @@ -1,23 +0,0 @@ -/******************************************************************************* -* -* (C) COPYRIGHT AUTHORS, 2022 -* -* TITLE: SYSINFODLG.H -* -* VERSION: 1.94 -* -* DATE: 06 Jun 2022 -* -* Common header file for the WinObjEx64 Globals and System Information Dialog. -* -* THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF -* ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED -* TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A -* PARTICULAR PURPOSE. -* -*******************************************************************************/ - -#pragma once - -VOID ShowSysInfoDialog( - _In_ HWND hwndParent); diff --git a/Source/WinObjEx64/tests/testunit.c b/Source/WinObjEx64/tests/testunit.c index 976114cf..995b795a 100644 --- a/Source/WinObjEx64/tests/testunit.c +++ b/Source/WinObjEx64/tests/testunit.c @@ -4,9 +4,9 @@ * * TITLE: TESTUNIT.C * -* VERSION: 1.94 +* VERSION: 2.00 * -* DATE: 06 Jun 2022 +* DATE: 19 Jun 2022 * * Test code used while debug. * @@ -26,26 +26,24 @@ #pragma warning(pop) #include -HANDLE g_TestIoCompletion = NULL, g_TestTransaction = NULL; HANDLE g_TestNamespace = NULL, g_TestMutex = NULL; HANDLE g_TestMailslot = NULL; -HANDLE g_DebugObject = NULL; -HANDLE g_TestJob = NULL; -HDESK g_TestDesktop = NULL; HANDLE g_TestThread = NULL; HANDLE g_TestPortThread = NULL; HANDLE g_PortHandle; PVOID g_MappedSection = NULL; HANDLE g_SectionVaTest = NULL; +HANDLE g_ResourceManager = NULL; +HANDLE g_TestJob = NULL; typedef struct _LPC_USER_MESSAGE { PORT_MESSAGE Header; BYTE Data[128]; -} LPC_USER_MESSAGE, *PLPC_USER_MESSAGE; +} LPC_USER_MESSAGE, * PLPC_USER_MESSAGE; typedef struct _QUERY_REQUEST { ULONG Data; -} QUERY_REQUEST, *PQUERY_REQUEST; +} QUERY_REQUEST, * PQUERY_REQUEST; #define WOBJEX_TEST_PORT L"\\Rpc Control\\WinObjEx_ServiceTestPort48429" @@ -54,6 +52,163 @@ HANDLE TestGetPortHandle() return g_PortHandle; } +typedef NTSTATUS (NTAPI* pfnNtCreateRegistryTransaction)( + _Out_ PHANDLE Handle, + _In_ ACCESS_MASK DesiredAccess, //generic + TRANSACTION_* + _In_ POBJECT_ATTRIBUTES ObjectAttributes, + _In_ DWORD Flags); + +VOID TestRegistryTransaction() +{ + NTSTATUS ntStatus; + HANDLE hObject; + OBJECT_ATTRIBUTES obja; + UNICODE_STRING usName; + pfnNtCreateRegistryTransaction NtCreateRegistryTransaction; + HMODULE hNtdll; + + hNtdll = GetModuleHandle(L"ntdll.dll"); + if (hNtdll) { + + NtCreateRegistryTransaction = (pfnNtCreateRegistryTransaction)GetProcAddress(hNtdll, "NtCreateRegistryTransaction"); + if (NtCreateRegistryTransaction != NULL) { + + RtlInitUnicodeString(&usName, L"\\RPC Control\\TestRegTransaction"); + InitializeObjectAttributes(&obja, &usName, OBJ_CASE_INSENSITIVE, NULL, NULL); + ntStatus = NtCreateRegistryTransaction(&hObject, TRANSACTION_ALL_ACCESS, &obja, 0); + if (NT_SUCCESS(ntStatus)) { + __nop(); + } + + } + + } +} + +VOID TestCreateBogusObjects() +{ + HANDLE hTimer = NULL, hDirectory = NULL, hObject = NULL; + LARGE_INTEGER liDueTime; + LPWSTR lpName; + SIZE_T l, i; + OBJECT_ATTRIBUTES obja; + UNICODE_STRING usName, usObject; + + WCHAR szBuffer[MAX_PATH + 1]; + + liDueTime.QuadPart = -1000000000000LL; + + lpName = (LPWSTR)supHeapAlloc(UNICODE_STRING_MAX_BYTES); + if (lpName) { + _strcpy(lpName, L"\\BaseNamedObjects\\BogusLongName"); + l = _strlen(lpName); + for (i = l; i < UNICODE_STRING_MAX_CHARS - l - 1; i++) + lpName[i] = L't'; + + RtlInitUnicodeString(&usName, lpName); + InitializeObjectAttributes(&obja, &usName, OBJ_CASE_INSENSITIVE, NULL, NULL); + + NtCreateTimer(&hTimer, TIMER_ALL_ACCESS, &obja, NotificationTimer); + if (hTimer) { + SetWaitableTimer(hTimer, &liDueTime, 0, NULL, NULL, 0); + } + + supHeapFree(lpName); + } + + _strcpy(szBuffer, L"\\BaseNamedObjects\\BogusEmbeddedNull"); + l = _strlen(szBuffer); + szBuffer[l++] = 0; + szBuffer[l++] = L't'; + szBuffer[l++] = L'e'; + szBuffer[l++] = L's'; + szBuffer[l++] = L't'; + + l *= 2; + + usName.Buffer = szBuffer; + usName.Length = (USHORT)l; + usName.MaximumLength = usName.Length + sizeof(UNICODE_NULL); + + InitializeObjectAttributes(&obja, &usName, OBJ_CASE_INSENSITIVE, NULL, NULL); + + NtCreateTimer(&hTimer, TIMER_ALL_ACCESS, &obja, NotificationTimer); + if (hTimer) SetWaitableTimer(hTimer, &liDueTime, 0, NULL, NULL, 0); + + _strcpy(szBuffer, L"\\RPC Control\\BogusEmbeddedNull"); + l = _strlen(szBuffer); + szBuffer[l++] = 0; + szBuffer[l++] = L't'; + szBuffer[l++] = L'e'; + szBuffer[l++] = L's'; + szBuffer[l++] = L't'; + + l *= 2; + + usName.Buffer = szBuffer; + usName.Length = (USHORT)l; + usName.MaximumLength = usName.Length + sizeof(UNICODE_NULL); + if (NT_SUCCESS(NtCreateDirectoryObject(&hDirectory, DIRECTORY_ALL_ACCESS, &obja))) { + RtlInitUnicodeString(&usName, L"SomeTimer"); + obja.RootDirectory = hDirectory; + if (NT_SUCCESS(NtCreateTimer(&hTimer, TIMER_ALL_ACCESS, + &obja, NotificationTimer))) + { + if (hTimer) SetWaitableTimer(hTimer, &liDueTime, 0, NULL, NULL, 0); + } + } + + _strcpy(szBuffer, L"SurpriseDirectory"); + l = _strlen(szBuffer); + szBuffer[l++] = 0; + szBuffer[l++] = L't'; + szBuffer[l++] = L'e'; + szBuffer[l++] = L's'; + szBuffer[l++] = L't'; + szBuffer[l++] = 0; + szBuffer[l++] = L'h'; + szBuffer[l++] = L'a'; + szBuffer[l++] = 0; + szBuffer[l++] = 0; + szBuffer[l++] = L'h'; + szBuffer[l++] = L'a'; + l *= 2; + + usName.Buffer = szBuffer; + usName.Length = (USHORT)l; + usName.MaximumLength = usName.Length + sizeof(UNICODE_NULL); + obja.RootDirectory = hDirectory; + if (NT_SUCCESS(NtCreateDirectoryObject(&hDirectory, DIRECTORY_ALL_ACCESS, &obja))) { + RtlInitUnicodeString(&usObject, L"SurpriseTimer"); + obja.RootDirectory = hDirectory; + obja.ObjectName = &usObject; + if (NT_SUCCESS(NtCreateTimer(&hTimer, TIMER_ALL_ACCESS, + &obja, NotificationTimer))) + { + if (hTimer) SetWaitableTimer(hTimer, &liDueTime, 0, NULL, NULL, 0); + + RtlInitUnicodeString(&usObject, L"\\RPC Control\\TestLink"); + InitializeObjectAttributes(&obja, &usObject, OBJ_CASE_INSENSITIVE, NULL, NULL); + + _strcpy(szBuffer, L"\\RPC Control\\BogusEmbeddedNull"); + l = _strlen(szBuffer); + szBuffer[l++] = 0; + szBuffer[l++] = L't'; + szBuffer[l++] = L'e'; + szBuffer[l++] = L's'; + szBuffer[l++] = L't'; + l *= 2; + + usName.Length = (USHORT)l; + usName.MaximumLength = usName.Length + sizeof(UNICODE_NULL); + + NtCreateSymbolicLinkObject(&hObject, SYMBOLIC_LINK_ALL_ACCESS, &obja, &usName); + + } + } + +} + DWORD WINAPI LPCListener(LPVOID lpThreadParameter) { NTSTATUS Status; @@ -135,14 +290,15 @@ VOID TestDebugObject( VOID ) { + HANDLE hObject = NULL; NTSTATUS status; OBJECT_ATTRIBUTES obja; UNICODE_STRING ustr = RTL_CONSTANT_STRING(L"\\BaseNamedObjects\\TestDebugObject"); InitializeObjectAttributes(&obja, &ustr, OBJ_CASE_INSENSITIVE, NULL, NULL); - status = NtCreateDebugObject(&g_DebugObject, DEBUG_ALL_ACCESS, &obja, 0); + status = NtCreateDebugObject(&hObject, DEBUG_ALL_ACCESS, &obja, 0); if (NT_SUCCESS(status)) { - Beep(0, 0); + __nop(); } } @@ -238,7 +394,6 @@ VOID TestPartition( VOID ) { - NTSTATUS status; HANDLE TargetHandle = NULL; OBJECT_ATTRIBUTES obja; UNICODE_STRING ustr = RTL_CONSTANT_STRING(L"\\KernelObjects\\MemoryPartition0"); @@ -246,11 +401,8 @@ VOID TestPartition( if (g_ExtApiSet.NtOpenPartition != NULL) { InitializeObjectAttributes(&obja, &ustr, OBJ_CASE_INSENSITIVE, NULL, NULL); - status = g_ExtApiSet.NtOpenPartition(&TargetHandle, MEMORY_PARTITION_QUERY_ACCESS, &obja); - if (NT_SUCCESS(status)) { - __nop(); - NtClose(TargetHandle); - } + g_ExtApiSet.NtOpenPartition(&TargetHandle, MEMORY_PARTITION_QUERY_ACCESS, &obja); + } } @@ -258,12 +410,13 @@ VOID TestIoCompletion( VOID ) { + HANDLE hCompletion = NULL; OBJECT_ATTRIBUTES obja; UNICODE_STRING ustr = RTL_CONSTANT_STRING(L"\\BaseNamedObjects\\TestIoCompletion"); //IoCompletion InitializeObjectAttributes(&obja, &ustr, OBJ_CASE_INSENSITIVE, NULL, NULL); - NtCreateIoCompletion(&g_TestIoCompletion, IO_COMPLETION_ALL_ACCESS, &obja, 100); + NtCreateIoCompletion(&hCompletion, IO_COMPLETION_ALL_ACCESS, &obja, 100); } VOID TestTimer( @@ -282,16 +435,52 @@ VOID TestTimer( } +VOID TestTransactionResourceManager( + VOID +) +{ + HANDLE hObject = NULL; + OBJECT_ATTRIBUTES obja; + UNICODE_STRING usName; + GUID tmp; + + InitializeObjectAttributes(&obja, NULL, OBJ_CASE_INSENSITIVE, NULL, NULL); + + if (NT_SUCCESS(NtCreateTransactionManager(&hObject, + TRANSACTIONMANAGER_ALL_ACCESS, + &obja, + NULL, + TRANSACTION_MANAGER_VOLATILE, + 0))) + { + if (S_OK == CoCreateGuid(&tmp)) { + RtlInitUnicodeString(&usName, L"\\BaseNamedObjects\\TestRm"); + obja.ObjectName = &usName; + if (NT_SUCCESS(NtCreateResourceManager(&g_ResourceManager, + RESOURCEMANAGER_ALL_ACCESS, + hObject, + &tmp, + &obja, + RESOURCE_MANAGER_VOLATILE, + NULL))) + { + __nop(); + } + } + } +} + VOID TestTransaction( VOID ) { + HANDLE hObject; OBJECT_ATTRIBUTES obja; UNICODE_STRING ustr = RTL_CONSTANT_STRING(L"\\BaseNamedObjects\\TestTransaction"); //TmTx InitializeObjectAttributes(&obja, &ustr, OBJ_CASE_INSENSITIVE, NULL, NULL); - NtCreateTransaction(&g_TestTransaction, TRANSACTION_ALL_ACCESS, &obja, NULL, NULL, 0, 0, 0, NULL, NULL); + NtCreateTransaction(&hObject, TRANSACTION_ALL_ACCESS, &obja, NULL, NULL, 0, 0, 0, NULL, NULL); } VOID TestPrivateNamespace( @@ -441,7 +630,7 @@ VOID TestException( _In_ BOOL bNaked ) { - if (bNaked) + if (bNaked) *(PBYTE)(NULL) = 0; else { @@ -455,30 +644,6 @@ VOID TestException( } } -#include "ui.h" - -VOID TestWinsta( - VOID -) -{ - NTSTATUS Status; - HWINSTA hWinsta; - PROP_OBJECT_INFO Context; - - //Context.lpCurrentObjectPath = L"\\Windows\\WindowStations"; - Context.lpCurrentObjectPath = L"\\Sessions\\1\\Windows\\WindowStations"; - Context.lpObjectName = L"Winsta0"; - - hWinsta = OpenWindowStation(L"WinSta0", FALSE, WINSTA_ALL_ACCESS); - //hWinsta = supOpenWindowStationFromContext(&Context, FALSE, READ_CONTROL); - if (hWinsta) { - CloseWindowStation(hWinsta); - Status = RtlGetLastNtStatus(); - if (NT_SUCCESS(Status)) - Beep(0, 0); - } -} - VOID TestJob() { UINT i; @@ -555,7 +720,7 @@ VOID TestPsObjectSecurity( } if (dwErr != ERROR_SUCCESS) - Beep(0, 0); + __nop(); supHeapFree(EmptyDacl); } @@ -565,15 +730,16 @@ VOID TestDesktop( VOID ) { + HANDLE hDesktop; DWORD LastError = 0; - g_TestDesktop = CreateDesktop(TEXT("TestDesktop"), NULL, NULL, 0, + hDesktop = CreateDesktop(TEXT("TestDesktop"), NULL, NULL, 0, DESKTOP_CREATEWINDOW | DESKTOP_SWITCHDESKTOP, NULL); - if (g_TestDesktop == NULL) { + if (hDesktop == NULL) { LastError = GetLastError(); if (LastError != 0) - Beep(0, 0); + __nop(); } } @@ -587,7 +753,7 @@ DWORD WINAPI TokenImpersonationThreadProc(PVOID Parameter) if (OpenProcessToken(GetCurrentProcess(), TOKEN_ALL_ACCESS, &hToken)) { if (!ImpersonateLoggedOnUser(hToken)) - Beep(0, 0); + __nop(); CloseHandle(hToken); } @@ -598,7 +764,7 @@ DWORD WINAPI TokenImpersonationThreadProc(PVOID Parameter) } while (i < 1000); if (!RevertToSelf()) - Beep(0, 0); + __nop(); ExitThread(0); } @@ -685,12 +851,12 @@ VOID TestApiSetResolve() } BOOL CALLBACK EnumerateSLValueDescriptorCallback( - _In_ SL_KMEM_CACHE_VALUE_DESCRIPTOR *CacheDescriptor, + _In_ SL_KMEM_CACHE_VALUE_DESCRIPTOR* CacheDescriptor, _In_opt_ PVOID Context ) { - WCHAR *EntryName; - CHAR *EntryType; + WCHAR* EntryName; + CHAR* EntryType; UNREFERENCED_PARAMETER(Context); @@ -889,7 +1055,7 @@ VOID TestShadowDirectory() RtlInitUnicodeString(&ustr, L"\\BaseNamedObjects"); InitializeObjectAttributes(&dirObja, &ustr, OBJ_CASE_INSENSITIVE, NULL, NULL); ntStatus = NtOpenDirectoryObject(&shadowDirHandle, DIRECTORY_QUERY | DIRECTORY_TRAVERSE, &dirObja); - + if (NT_SUCCESS(ntStatus)) { // @@ -915,7 +1081,7 @@ VOID TestShadowDirectory() obja.RootDirectory = NULL; ntStatus = NtOpenMutant(&testHandle2, MUTANT_ALL_ACCESS, &obja); if (NT_SUCCESS(ntStatus)) { - Beep(0, 0); + __nop(); } } } @@ -927,14 +1093,15 @@ VOID TestAlpcPortOpen() { HANDLE hObject = NULL; NTSTATUS ntStatus; + UNICODE_STRING usName; - ntStatus = supOpenPortObjectByName(&hObject, + RtlInitUnicodeString(&usName, WOBJEX_TEST_PORT); + + ntStatus = supOpenPortObjectByName(&hObject, PORT_ALL_ACCESS, - NULL, - WOBJEX_TEST_PORT); + &usName); if (NT_SUCCESS(ntStatus)) { - Beep(0, 0); NtClose(hObject); } else { @@ -971,7 +1138,7 @@ VOID TestSymbols() SYM_CHILD* pSymChild; WCHAR* pStrEnd; - WCHAR* pOutput; + WCHAR* pOutput; if (!kdIsSymAvailable((PSYMCONTEXT)g_kdctx.NtOsSymContext)) return; @@ -1098,10 +1265,10 @@ VOID TestSessions() DWORD sessionsCount, i; WTS_SESSION_INFO* pSessions; - if (WTSEnumerateSessions(WTS_CURRENT_SERVER_HANDLE, - 0, - 1, - &pSessions, + if (WTSEnumerateSessions(WTS_CURRENT_SERVER_HANDLE, + 0, + 1, + &pSessions, &sessionsCount)) { for (i = 0; i < sessionsCount; i++) { @@ -1155,7 +1322,7 @@ VOID TestObCallback() HANDLE Pid2; BYTE Spare[392]; } request; - + NTSTATUS ntStatus; DWORD procId1 = 3448; @@ -1199,16 +1366,19 @@ VOID TestStart( VOID ) { + TestCall(); + TestRegistryTransaction(); + //TestTransactionResourceManager(); + TestCreateBogusObjects(); //TestCmControlVector(); //TestObCallback(); - TestCall(); //TestSectionControlArea(); //TestSymbols(); //TestSectionImage(); //TestShadowDirectory(); //TestPsObjectSecurity(); //TestLicenseCache(); - TestApiSetResolve(); + //TestApiSetResolve(); //TestDesktop(); //TestApiPort(); //TestAlpcPortOpen(); @@ -1217,10 +1387,9 @@ VOID TestStart( //TestPartition(); //TestPrivateNamespace(); //TestIoCompletion(); - TestTimer(); + //TestTimer(); //TestTransaction(); - //TestWinsta(); - TestSessions(); + //TestSessions(); //TestThread(); //PreHashTypes(); //TestJob(); @@ -1230,10 +1399,6 @@ VOID TestStop( VOID ) { - if (g_DebugObject) NtClose(g_DebugObject); - if (g_TestIoCompletion) NtClose(g_TestIoCompletion); - if (g_TestTransaction) NtClose(g_TestTransaction); - if (g_TestMutex != NULL) { CloseHandle(g_TestMutex); } @@ -1247,9 +1412,7 @@ VOID TestStop( TerminateJobObject(g_TestJob, 0); NtClose(g_TestJob); } - if (g_TestDesktop) { - CloseDesktop(g_TestDesktop); - } + if (g_TestThread) { TerminateThread(g_TestThread, 0); CloseHandle(g_TestThread); diff --git a/Source/WinObjEx64/tests/testunit.h b/Source/WinObjEx64/tests/testunit.h index 8a90c2a0..261f0cfd 100644 --- a/Source/WinObjEx64/tests/testunit.h +++ b/Source/WinObjEx64/tests/testunit.h @@ -4,9 +4,9 @@ * * TITLE: TESTUNIT.H * -* VERSION: 1.94 +* VERSION: 2.00 * -* DATE: 02 Jun 2022 +* DATE: 19 Jun 2022 * * Common header file for test code. * diff --git a/Source/WinObjEx64/ui.h b/Source/WinObjEx64/ui.h index 9c0323a3..c9edadfc 100644 --- a/Source/WinObjEx64/ui.h +++ b/Source/WinObjEx64/ui.h @@ -4,9 +4,9 @@ * * TITLE: UI.H * -* VERSION: 1.94 +* VERSION: 2.00 * -* DATE: 31 May 2022 +* DATE: 19 Jun 2022 * * Common header file for the user interface. * @@ -47,9 +47,9 @@ typedef HWND(WINAPI *pfnHtmlHelpW)( _In_ DWORD_PTR dwData ); -#define PROGRAM_MAJOR_VERSION 1 -#define PROGRAM_MINOR_VERSION 9 -#define PROGRAM_REVISION_NUMBER 4 +#define PROGRAM_MAJOR_VERSION 2 +#define PROGRAM_MINOR_VERSION 0 +#define PROGRAM_REVISION_NUMBER 0 #define PROGRAM_BUILD_NUMBER 2206 #ifdef _USE_OWN_DRIVER @@ -61,6 +61,9 @@ typedef HWND(WINAPI *pfnHtmlHelpW)( #define WINOBJEX64_WNDCLASS L"WinObjEx64Class" #define WINOBJEX64_PSLISTCLASS L"WinObjEx64PsListClass" +#define T_COPY_OBJECT_NAME L"Copy Name" +#define T_COPY_OBJECT_NAME_BIN L"Copy Name (Binary)" + #define T_PROPERTIES L"Properties...\tEnter" #define T_GOTOLINKTARGET L"Go To Link Target\tCtrl+->" #define T_VIEWSD L"View Security Descriptor..." @@ -68,7 +71,6 @@ typedef HWND(WINAPI *pfnHtmlHelpW)( #define T_RUNASSYSTEM L"R&un as LocalSystem" #define T_EXPORTTOFILE L"Export List" #define T_JUMPTOFILE L"Jump to File" -#define T_DUMPDRIVER L"Dump Driver" #define T_VIEW_REFRESH L"Refresh\tF5" #define T_VIEW_PLUGINS L"View Plugins" #define T_EMPTY L" " @@ -103,96 +105,20 @@ typedef HWND(WINAPI *pfnHtmlHelpW)( #define IDMM_HELP 5 typedef struct _TL_SUBITEMS_FIXED { + ULONG Count; ULONG ColorFlags; COLORREF BgColor; COLORREF FontColor; PVOID UserParam; - ULONG Count; + LPTSTR CustomTooltip; LPTSTR Text[2]; } TL_SUBITEMS_FIXED, *PTL_SUBITEMS_FIXED; -// -// Property Dialogs -// - -//Variable typedefs - -typedef enum _PROP_CONTEXT_TYPE { - propNormal = 0, - propPrivateNamespace = 1, - propUnnamed = 2, - propMax = 3 -} PROP_CONTEXT_TYPE; - -typedef struct _PROP_NAMESPACE_INFO { - ULONG Reserved; - ULONG SizeOfBoundaryDescriptor; - OBJECT_BOUNDARY_DESCRIPTOR *BoundaryDescriptor; - ULONG_PTR ObjectAddress; -} PROP_NAMESPACE_INFO, *PPROP_NAMESPACE_INFO; - -typedef struct _PROP_UNNAMED_OBJECT_INFO { - ULONG_PTR ObjectAddress; - CLIENT_ID ClientId; - SYSTEM_THREAD_INFORMATION ThreadInformation; - UNICODE_STRING ImageName; - BOOL IsThreadToken; -} PROP_UNNAMED_OBJECT_INFO, *PPROP_UNNAMED_OBJECT_INFO; - -typedef struct _PROP_PORT_OBJECT { - BOOL IsAllocated; - HANDLE ReferenceHandle; -} PROP_PORT_OBJECT, * PPROP_PORT_OBJECT; - -typedef struct _PROP_OBJECT_INFO { - PROP_CONTEXT_TYPE ContextType; - BOOL IsType; //TRUE if selected object is an object type - UINT TypeIndex; - DWORD ObjectFlags;//object specific flags - LPWSTR lpObjectName; - LPWSTR lpObjectType; - LPWSTR lpCurrentObjectPath; - LPWSTR lpDescription; //description from main list (3rd column) - ULONG_PTR Tag; - WOBJ_TYPE_DESC *TypeDescription; - WOBJ_TYPE_DESC *ShadowTypeDescription; //valid only for types, same as TypeDescription for everything else. - HICON ObjectIcon; - HICON ObjectTypeIcon; - OBJINFO ObjectInfo; //object dump related structures - PROP_NAMESPACE_INFO NamespaceInfo; - PROP_UNNAMED_OBJECT_INFO UnnamedObjectInfo; - PROP_PORT_OBJECT PortObjectInfo; -} PROP_OBJECT_INFO, *PPROP_OBJECT_INFO; - -// -// If dialog already present - activate it window and return. -// -#define ENSURE_DIALOG_UNIQUE(Dialog) { \ - if (Dialog != NULL) { \ - SetActiveWindow(Dialog); \ - return; \ - } \ -} - -typedef struct _PROP_DIALOG_CREATE_SETTINGS { - HWND hwndParent; - LPWSTR lpObjectName; - LPCWSTR lpObjectType; - LPWSTR lpDescription; - PROP_NAMESPACE_INFO *NamespaceObject; - PROP_UNNAMED_OBJECT_INFO *UnnamedObject; -} PROP_DIALOG_CREATE_SETTINGS, *PPROP_DIALOG_CREATE_SETTINGS; - typedef struct _VALUE_DESC { LPWSTR lpDescription; DWORD dwValue; } VALUE_DESC, *PVALUE_DESC; -typedef struct _WINSTA_DESC { - LPCWSTR lpszWinSta; - LPCWSTR lpszDesc; -} WINSTA_DESC, * PWINSTA_DESC; - typedef struct _LVCOLUMNS_DATA { LPWSTR Name; INT Width; @@ -240,22 +166,6 @@ typedef struct _LVCOLUMNS_DATA { // prop used by ipc dialogs #define T_IPCDLGCONTEXT TEXT("IpcDlgContext") -//Calendar -static LPCWSTR g_szMonths[12] = { - L"Jan", - L"Feb", - L"Mar", - L"Apr", - L"May", - L"Jun", - L"Jul", - L"Aug", - L"Sep", - L"Oct", - L"Nov", - L"Dec" -}; - #define INIT_NO_ERROR 0 #define INIT_ERROR_NOCRT 1 #define INIT_ERROR_NOHEAP 2 @@ -295,3 +205,24 @@ static LPCWSTR g_szMonths[12] = { #define T_ERRSHADOW_TABLE_NOT_FOUND TEXT("W32pServiceTable was not found in win32k module") #define T_ERRSHADOW_APISETMAP_NOT_FOUND TEXT("ApiSetSchema map was not found") #define T_ERRSHADOW_APISET_VER_UNKNOWN TEXT("ApiSetSchema version is unknown") + +// +// Common Dialog handlers. +// +VOID FindDlgCreate( + VOID); + +VOID ShowSysInfoDialog( + _In_ HWND hwndParent); + +VOID SDViewDialogCreate( + _In_ WOBJ_OBJECT_TYPE ObjectType); + +INT_PTR CALLBACK AboutDialogProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam); + +VOID ShowStatsDialog( + VOID); diff --git a/Source/WinObjEx64/wine.h b/Source/WinObjEx64/wine.h deleted file mode 100644 index 5f65daf2..00000000 --- a/Source/WinObjEx64/wine.h +++ /dev/null @@ -1,25 +0,0 @@ -/******************************************************************************* -* -* (C) COPYRIGHT AUTHORS, 2018 - 2021 -* -* TITLE: WINE.H -* -* VERSION: 1.92 -* -* DATE: 06 Dec 2021 -* -* Wine/Wine staging support header file. -* -* THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF -* ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED -* TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A -* PARTICULAR PURPOSE. -* -*******************************************************************************/ - -#pragma once - -typedef char* (__cdecl *pwine_get_version)(void); - -const char *wine_get_version(void); -int is_wine(void); diff --git a/Source/WinObjEx64/winedebug.h b/Source/WinObjEx64/winedebug.h deleted file mode 100644 index c0431c41..00000000 --- a/Source/WinObjEx64/winedebug.h +++ /dev/null @@ -1,23 +0,0 @@ -/******************************************************************************* -* -* (C) COPYRIGHT AUTHORS, 2019 - 2020 -* -* TITLE: WINEDEBUG.H -* -* VERSION: 1.83 -* -* DATE: 01 Dec 2019 -* -* Wine debug definition header. -* -* THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF -* ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED -* TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A -* PARTICULAR PURPOSE. -* -*******************************************************************************/ - -#pragma once - -#define _WINE_DEBUG_MODE -#undef _WINE_DEBUG_MODE diff --git a/WinObjEx64.sha256 b/WinObjEx64.sha256 index 86bce409..a99792cb 100644 --- a/WinObjEx64.sha256 +++ b/WinObjEx64.sha256 @@ -3,12 +3,13 @@ e192abb83dded0fe227f3fe69cb0ac7aaa197941917afd497b4cf8796a03e041 *Compiled\WHATS fa001b1ac9bbbb6c954d5dd609de60fa2b0277a6cfe35f6428591e4b4b1e8453 *Compiled\WHATSNEW_180.md 764927e79e6226e9a5185b0672b5b6422c27f6c4955afa45b6e3032a766797e4 *Compiled\WHATSNEW_187.md d3c54e144f4ea198d761a0c89764d6cd39da19c0aa51661a9f37135e4f842a85 *Compiled\WHATSNEW_190.md +c4205a94f6ed7ff8e26b318712acaab2d2d849fa97e7d92325d25cae49200c01 *Compiled\WHATSNEW_200.md 85ea539802640fce924ee0dda14d9a0ed3e786f5ca131c4ae0815f7beeba69f8 *Compiled\WinObjEx64.chm -824252597d1bfb4e852c5e08c66f4eb79913b5a8f544f4be07034ecc591dfae4 *Compiled\WinObjEx64.exe -158720439270201f23d3220eea21286923111b4dfec32b31b072a73a754e73ce *Compiled\plugins\ApiSetView.dll -8daf3fb59b1d43475dc5ef48f0120d60d2e05468c5cd3c1c04a91e7ec0499ad8 *Compiled\plugins\ExamplePlugin.dll -e19eb5a7d43f27cfbac2335494c49afa6ad23408e2ccf6e72d031e32a6994485 *Compiled\plugins\ImageScope.dll -107b763f3f93816bf9de7db7ef675f2fc0fb61cf1ce185c0de4ce1ce213f20a0 *Compiled\plugins\Sonar.dll +6b3180a969421a4336222a3a7922ac7652df780ad0169363375f89b36137536d *Compiled\WinObjEx64.exe +b7d674453e9734472f85bd4ca3c53651e0702f32b5a801fce014a74b4d255bae *Compiled\plugins\ApiSetView.dll +24a64aa290d1c21deae5029db957df728041006ef69087ad947eee8d4482881c *Compiled\plugins\ExamplePlugin.dll +50b4c0ad3b58ac10fb0e2d386ce92287f9e30e0580d9f5b4b99a191f08d5b8ef *Compiled\plugins\ImageScope.dll +84a944cd1fc5c5b0b21198768c166e9fe2a545112419ba98721ef7fe852b445b *Compiled\plugins\Sonar.dll 91a934ed83e8d2cda56f9ada2d4026247d2f5017483bf487d1a51c4b332e9314 *Docs\Callbacks.pdf a9a7b1448aae42671a9e38df1074056d6e1f6e5c0e15d95790be3be66f6b7910 *Docs\Plugins.pdf b5760ed4f02ce90db0584eabc3f7f220ca7c69036ceb71c9aadac83c15f07c98 *Screenshots\AlpcPortConnections.png @@ -16,6 +17,7 @@ fba30e9030b549408da8e2efceb0d1aa0089d5c6621b664eba0b34b01a1a0a2e *Screenshots\Ap 54b8e37f2debeb936ac61eef516195feb3707da2214b8c7ea5d756ddcbd3ed43 *Screenshots\ApiSetView190.png 7e2b0bcb3a2f0947f1effed2306d0178e4ad28da6427d5d7735017630bfb960a *Screenshots\CallbackObjectView.png 88def410b5810caf649aa5402fed789e9be0c4bd2d18019ea3db25110b510acf *Screenshots\CallbacksView.png +c1fdc1a3ca8c1f6db37bec4c33e475475feae17a5401fbc5e83cd01d007d4dec *Screenshots\CmControlVector.png 40c9b6f06ee689921f2b11715a54ee57f8968078d66fabdb343ff92a0c5acf75 *Screenshots\FindObjectDialog.png 47da9272f9d83ed89942acaf0fec1b900b17d3d098601157f16d57514f742b35 *Screenshots\ImsSection.png c26f510707acbf18dd7e233a18a4fb7fe931242b573a01d17292c31d04f2bca3 *Screenshots\ImsStrings.png @@ -38,13 +40,13 @@ cb66fca90766db12474ce057436364dabaf3e948f6ffa7e9651f869656f0b33c *Screenshots\Vi ef65a909e8d9bc7ec94ecbc0f465f24a7968d6675eadf7f25f6414c66d6b28be *Screenshots\ViewingTypeInformation.png db0ab26d20a62ba7c9c844e916e88168b72a7e52932d3483eb2d0a2e535b75a8 *Screenshots\ViewingUserSharedData.png 9e2b64f390c609172c5791dd138a748d31bf4d2cc839f01dbd514afe1cdfd083 *Screenshots\W32pServiceTableView.png -fdeea2b058e7eeb50577cd5e3615a51ab8cd427fcccf4b0d716a974285d3073d *Source\CHANGELOG.txt -7a973f6336017a50f90a7ccd9b9d60e4cf2e2252ebb860fd0cf513cf9e4c587a *Source\FILELIST.txt +e0aeca50fc17d42b6943d0460cf8ef60c452ff4beca7c915165c5f5c98920237 *Source\CHANGELOG.txt +ab18e9c97f2090cc135e6017085122ff455bd5721c4fc2e19ab03fd87affb654 *Source\FILELIST.txt fb5db833eb13f6c7812cf9b8ca2b2e4f60b9133b700edd2065c3431c41509911 *Source\TypesWithNoDesc.txt 89f58de2ad50d5abba574acb90cc06454322a83bf0f4cc0da7e77201fde10d18 *Source\WinObjEx64.sln abb1cfa5a1ebfc0dfa4fa646b8bf969020fc413ebcfa6189fe03e78c975fb7d6 *Source\WinObjEx64_Plugins.sln 39a976ac4e1b76c2058815c5017bd3acceb69950286cfdf8c5704b7e31b8cca0 *Source\drvstore\kldbgdrv.sys -c19392cb3749add83029bdfa025f9e2a0b316ca13ec427ac86512260d5f5ac74 *Source\Plugins\plugin_def.h +b3c0d3570b4c5b355bb718cd6a4f04e245066c743a1f37cf181d9bfa0c11e7dd *Source\Plugins\plugin_def.h e36729912beb610d6499db18955104ce0a6f4318867e6c1b5a1e3ae413a6a0b3 *Source\Plugins\postbuild.cmd 3cb8b22fc2265da62aa183e1d6dcd22609e4463f01940308bc1eb9d6393132b1 *Source\Plugins\ApiSetView\ApiSetView.vcxproj 6f229b03fbc6b950b0037e15eed24d0ea603e1252f3de004fc84ca4ca06d3834 *Source\Plugins\ApiSetView\ApiSetView.vcxproj.filters @@ -56,7 +58,7 @@ e55841373762f00b9b27cfe98d1cf1531a7efd47bfe8523887c7f9fbdd275c15 *Source\Plugins 2281055032972c36f4ce314aa6f6131c0bdbe258ed01900f39945476f8a82965 *Source\Plugins\ApiSetView\query.h 3bb248eff6da831cfc760df31bb2f91d5d4011c397a617d1dcae0beeb731fbf2 *Source\Plugins\ApiSetView\resource.h 3a862ff059b3bbd2f299074add8f26143a3f6c517cd9dfeac265e72e5236f416 *Source\Plugins\ApiSetView\Resource.rc -40c11c10307e81e5cf22ab07a30afc5776059ac979f86b5eb6c761cccba6ccf8 *Source\Plugins\ApiSetView\ui.h +bf4babab7c15bb59d59ae3bf56c62f9e368875fe5bc619bc9a73e3e297ec281a *Source\Plugins\ApiSetView\ui.h 5a23963d9013636311144bc273f9e065c5545992f1b3e08e60e53ae423734a8b *Source\Plugins\ExamplePlugin\ExamplePlugin.vcxproj 011a8e38aa9df8e77e7e786666be5cc4656054f4522681bdd1e4eda4501b1481 *Source\Plugins\ExamplePlugin\ExamplePlugin.vcxproj.filters e953b026d0f383188c753487df0a4d879fa5da5ba82ac979aa877db84e89a060 *Source\Plugins\ExamplePlugin\ExamplePlugin.vcxproj.user @@ -69,15 +71,15 @@ cea96ab2b67531d4b9823d5c42897df621f0926b24741389f5165ad29dfd1856 *Source\Plugins 1968ba5ddc7b4876599413c2a5d96b70eb72a1bb2d3007764b3fc14e5c08111a *Source\Plugins\ImageScope\ImageScope.vcxproj 712e286a7176d3b7f1511b19539ce1240bcf68cd8af0026a48c7f949ae013149 *Source\Plugins\ImageScope\ImageScope.vcxproj.filters c06a75b13f855a94d46616796e024c52b499f8f92cf00ccb571ddbc6ff574676 *Source\Plugins\ImageScope\ImageScope.vcxproj.user -e58ab26eff0491c193d7f50ea87df6c6ef076690b80fb9d4e773337df72a9596 *Source\Plugins\ImageScope\main.c +b64cd0c55c502d947a86c8f03fdc6d82edc0cf2262903eb7409a97a589ca4ff6 *Source\Plugins\ImageScope\main.c a1beafe144a0b20f062de225ce53a23d7d6e0bcce8266ee488788a2adf896512 *Source\Plugins\ImageScope\query.c 70fcd612f41f100e8803326e5f28b8beccdef7bc9f22b41ccc0ef4f92fc1ae59 *Source\Plugins\ImageScope\query.h c1503020ed3120fdbf07cd802f4185844bada59eac02ba5820f92ef3b2bb3710 *Source\Plugins\ImageScope\resource.h -a465fcf43e71fa992a0f822f70f48779bede66703c0757e60cdcd35ac09aff15 *Source\Plugins\ImageScope\Resource.rc -5e44b9f2d3286788619102fac42c2c7066fccbe83d3261ea58aab522a98f3427 *Source\Plugins\ImageScope\sup.c -608baf12262249a1a3350bf465add16c8bc5794501cdf1fd0f62c7a4fb1b0bfb *Source\Plugins\ImageScope\sup.h -e0cfb8b35b852e69247519d68cbb6a1b1f8c37f5f3cafb3345421d04d615dae0 *Source\Plugins\ImageScope\ui.c -66d4face06c8db8923cd3c96c017983e6e14beed90f91e24f6425d8c15e53bb2 *Source\Plugins\ImageScope\ui.h +72371088ac62c76946e6f2bb75749cc088f9d9cd7d5121490beb86a170fee838 *Source\Plugins\ImageScope\Resource.rc +b1a964451f6305fbc1d0e446c7f5351fc92ce1b8ffaed114e0906265269ad70f *Source\Plugins\ImageScope\sup.c +ab06f4cccfd4801c99fadf18ba7cd8d91f90a52ce859ef6de7ec498464b3eb0b *Source\Plugins\ImageScope\sup.h +312487df885300838bff12693b315a66f87abafe7b6d6f178793df2e0e89c123 *Source\Plugins\ImageScope\ui.c +9ca3b622df0abe8d55298c064c7938378722a25b65720444d2c481e51b30d486 *Source\Plugins\ImageScope\ui.h 6b2236b93693d4830feb90ee504ae03555d4882d4c301bea55f7980973b5fb32 *Source\Plugins\Sonar\export.def b8828842e612e5a1cadfd9f6153dc006c296d3dec2178f48125211ef3b256111 *Source\Plugins\Sonar\global.h b0923db27c811713437c00f94b559f80c5d7f7dd535c4099993b2bfcf143720f *Source\Plugins\Sonar\main.c @@ -91,7 +93,7 @@ f9984294e5c4de3af2648c3bf0ece10fd1f06517e8264cf0dbc9662dd909551e *Source\Plugins e953b026d0f383188c753487df0a4d879fa5da5ba82ac979aa877db84e89a060 *Source\Plugins\Sonar\Sonar.vcxproj.user df39b80bc2de9b9b98665d8feac7fee9ef79127558e48a3deed3a5da99b567e2 *Source\Plugins\Sonar\sup.c d9892fb88f1f97e7e444c4a1f0eac60115fdec289aef60339686f5428756a012 *Source\Plugins\Sonar\sup.h -0c3022061db061b12f4538776dc28def2cf82b59f3028571c04aab42fd09bcf9 *Source\Plugins\Sonar\ui.h +72d1b9aa5fa158affc761ebbe4883fd46fbf01196bb9af75c0892d017796da3a *Source\Plugins\Sonar\ui.h 51a674dae96f453bd269989bbba1c38a1e2f275df412c60c6c85d2bdec239e99 *Source\Shared\minirtl\hextou64.c ceb8cd23185964369b52677950ebec681ffb254b9827d1e689337b43f345dc6c *Source\Shared\minirtl\hextoul.c aa7d751fd93a3ce22a338a898280c281aba27a0320235674be90392dc400d2a1 *Source\Shared\minirtl\i64tostr.c @@ -125,15 +127,15 @@ ffac2569a1dd61c400cda45839c7314bdd99cfcb5c17d503807db34c168628d2 *Source\Shared\ 0738401b5c38184fc36bee3561f62af2234e897f521c10119c468e93d735c2b3 *Source\Shared\ntos\ntbuilds.h 69a2ac18f7ae51f231ff70195e914fdf1331564d7e109d052d8c0e6f2c6760a4 *Source\Shared\ntos\ntldr.c 083d71cbe45e72854cfc45e20b85ec805c8dd66f18c3a111236195a980c44333 *Source\Shared\ntos\ntldr.h -68eceb062d86c1234b52e4b73c612220ff8d4a547c3055fb94e8472674c2d874 *Source\Shared\ntos\ntos.h -6979c3177228a7f2f8d9f5ce4279d057b5d355ad95b20f4d7b9adef8c566c47d *Source\Shared\ntos\ntsup.c -0efb2ffd0270cecd9d95046a4865b3264c5eda45601851f8e6efcdeecf8ac349 *Source\Shared\ntos\ntsup.h +e98e4705ca61d6c7a88ad5ab754aefb4a5033f255107fdde67a5035b46af00a3 *Source\Shared\ntos\ntos.h +750ff82aad837e9214377d78dded1d8893872518f08fcf831eea99b85c75098d *Source\Shared\ntos\ntsup.c +21c6926e7556c518533222445234853c7c5a08252f0a8c02b6782605ab3892c6 *Source\Shared\ntos\ntsup.h 9988958033a3019273cadc83bfcdc8246d171df91fb6d6628ad933f03e58c1c6 *Source\Shared\ntuser\ntuser.h -43e391b939e1e3118371a0af7209d47f30d34fcbdbf6e74ac9bb1904e38c2547 *Source\Shared\sdk\extdef.h +1ec471eaeaec9402fb4a71f25ea7aa44de3a169f346ccaf873001dfbc59987c4 *Source\Shared\sdk\extdef.h 07da31bbf0fbb8e3fbf06f5b1557cb4415f267008834684617dfdadb93a4b25f *Source\Shared\tabs\tabsctrl.c fe9f3b5ce134b8d292a6a82df44ce0a201cfb2c029ac131f54564e3ac80b7172 *Source\Shared\tabs\tabsctrl.h -ce2ec00fd84aa5db7c67e1c95f503657804ffa6b3fb6a8fffe95de99476c6a18 *Source\Shared\treelist\treelist.c -33aac331f85b82bb59f46a81c085eabc26cbb62997a331b65cbb944f02dd96fa *Source\Shared\treelist\treelist.h +a0ae082961fc1825bed5a15f15285753485edbadce1eb62cc4809f95b74f4a0c *Source\Shared\treelist\treelist.c +1c6051f0fdeccfe6ecf16f9b360c738cd03307a9f7c0242120581aba1285cb99 *Source\Shared\treelist\treelist.h 1bc873890f680f1bc71883f9ca13ce2773de254863a0539e8cb3198fbba80d44 *Source\Utils\readme.txt c776bc97ee2fbe48d3e148bb37c887862e6de212d4391d6df9b5f149e40ed223 *Source\Utils\GenAsIo2Unlock\GenAsIo2Unlock.sln c4a28bc43a63a40ff2d8699fa261ee1ced6783d199043484ea7921e8d078ea08 *Source\Utils\GenAsIo2Unlock\GenAsIo2Unlock.vcxproj @@ -146,108 +148,81 @@ b0d8cc5b64482cd97871ff55e8dff0006679fabc397002fb00e03a4f6162d19d *Source\Utils\S 50886b1d269d1b4e67cfccf01444c85882f633f620fda361f23106aede6e2649 *Source\Utils\SearchPattern\SearchPattern.vcxproj.filters 93f2393e8962a32c42afad8c407f51c86fdba50316b70ccb436bcfe9015b7f0e *Source\Utils\SearchPattern\SearchPattern.vcxproj.user 342acfe1fb4f8f882b540ed09ab519ac8731a1f754b5e41a97812bc20e4381fc *Source\Utils\SearchPattern\test.cmd -2cbb0921c88d819a50ff7cb36d4646a40221981a6af543f627bd80c24b79a126 *Source\WinObjEx64\aboutDlg.c -e2877173023bae50e74772f142fec35cb72e30ea963dd90b39f382339a8a5b24 *Source\WinObjEx64\aboutDlg.h +91822aa5d2b089b6cd60b98f61a60ad9bdcadff7fdb7c0e2fa38df63077feb4c *Source\WinObjEx64\aboutDlg.c 9e54675313dfcf120d83549865688882d6a6fd85f029c797d4be4eed9e3a58b7 *Source\WinObjEx64\driver.rc -0d8692c3888e499558e9e0b97df2da16e58163c0f104b0ebc4a9394130424614 *Source\WinObjEx64\excepth.c -8ac5c0a74e70cd77f6df9e4ab5115f44e778f60ae7460a6d145cdae8987a8b2f *Source\WinObjEx64\excepth.h -dec43704b04ac9fc791f5a9477a6d82514c474556f579a577da5e335d6451226 *Source\WinObjEx64\extapi.c -1ec98a0cf02e6b9eb0942d83b17d65a2cfe1afaa4b6bd407cbb21d417b49680b *Source\WinObjEx64\extapi.h -9eef803d4edfbbd9f4e352fcb34a683c85d25ff9f37ddd73ccea9caea42bab63 *Source\WinObjEx64\findDlg.c -448a91283daf89c3eee5a18012fe3d50271b5db6aa4dcb6f31f3702e6b3e7c14 *Source\WinObjEx64\findDlg.h -66379999da6a3b31993c67190433929f363ab62bea19266e9da9eae7ac89ec31 *Source\WinObjEx64\global.h -efae658152f746d41a6e03d13b9035410cf90cd859140ecc744d2dfa3b773c2a *Source\WinObjEx64\hash.c -e79c1a5016f2d227be91eb345f08f515902144c63e3b09403a2d99dc8e8e771a *Source\WinObjEx64\hash.h -5c70e317138e4b29a57ce4b28b71da3b3a58cb11ce5e3b953c67c929d55f182a *Source\WinObjEx64\kldbg.c -08b55baff03772a2e5372a16d5db268d9d059431f0d253c7aaf79bc68d81ab90 *Source\WinObjEx64\kldbg.h -1f0d722b6cd9b6d5c5f3ec1a7e110ce579c5d025a34a46a373a9d63cc9d85bbb *Source\WinObjEx64\kldbg_patterns.h -c5e6655cce287691588493a3ca46bac005d8d812c0291afeb275a118891ed77a *Source\WinObjEx64\ksymbols.h -db67f3b8a3981c32e0018bcfed34dbfe5e600ff1fa2b34ab76870065b0f54807 *Source\WinObjEx64\list.c -0228c8f92e3c895fbc28ba4fadd28a6563e12bb1aaa6fd2b6bb6eaca1f8f3b44 *Source\WinObjEx64\list.h -8ba70065593a341ce14e2304bacd9f00002df134b824e99674cbed35e640c249 *Source\WinObjEx64\main.c -cf2e16f01e9b3daa6de0c5c3dc3ade68207e997cfa21e7caaba864381b11acf2 *Source\WinObjEx64\msvcver.h -0f1f80b3b898fec1a89529c280cb4ac8c7e5ac840d682e7b5422ef8e61440af8 *Source\WinObjEx64\objects.c -e751bec4a013a1f9cf2c3ef5dcb2a0d30655a1c495769a8886a46a309b046c97 *Source\WinObjEx64\objects.h -fa8b91f9b565c7360aad2e50ae067d5be38c0de3fa99ba9198912461db0f01c0 *Source\WinObjEx64\plugmngr.c -37209f354c8ee4ce9e0a2f069fdfee41380e20d9f56a31513d442500f5471f53 *Source\WinObjEx64\plugmngr.h -4f7271708d0229b5168b5c2169c044bc6cb07930f21ebe0296b54080ec32face *Source\WinObjEx64\resource.h -ace6090dccdfc66ebdbfb9d3936de513b40d7176fbb3b04a22333cf642a35b84 *Source\WinObjEx64\Resource.rc -c190cb0ed80da38ce221ebe60b399f11acd8cc31d7e406875a3028a38b12f87f *Source\WinObjEx64\sdviewDlg.c -ff4d638f70a48825900fd78bfc1bbec73a7528e29b92b4dbea3c21169e722a35 *Source\WinObjEx64\sdviewDlg.h -aeaac1b0a88fc23c3e42a4ddd3d13edbb35ba99235f36267b5e9b91e1126ac13 *Source\WinObjEx64\sup.c -462101a6c6f1ec1b396c3c0f0c5276e19763ab72126ba85f09c391fa1ea6daa2 *Source\WinObjEx64\sup.h -0fe701062cd013ab03eca9e51824b2a7ecac09db933c1e1de3c8b007ee6148d2 *Source\WinObjEx64\supConsts.h -06058ab5a8c7dd8fecb21b6221b3c689d81d6dd0430a4d6e314855bef95d7599 *Source\WinObjEx64\symparser.c +86d0ea96261f15a973a42904e17e3552370e15ed1132065aab94d82eb44b4e0e *Source\WinObjEx64\excepth.c +f70944012df5d37cbb490253b5973e9743908420c86f55021c85ef7f77b22052 *Source\WinObjEx64\excepth.h +519a6ce20a3739d0adbb5f5e409a72b3563c2446d53332e16b83cb70d6715356 *Source\WinObjEx64\extapi.c +c251e49c1fa3f1f69bf0a6e767b53e626fc0fb426dfd067d66f5ad63e44d2a9c *Source\WinObjEx64\extapi.h +94f6ed43dbc7c1cb29fe9e87a580ee2522c83d006bbe4a06eec08f2921ea8825 *Source\WinObjEx64\findDlg.c +8e9026da800c7d2dfd4dc6dbb4cfe09833592dc147586152f2c8950376059c73 *Source\WinObjEx64\global.h +657ad230646b3dda6bc6d9f8af105ccef1dcc8e60757508637187c56ddf61ff2 *Source\WinObjEx64\hash.c +91877ba05d36d1001e5b6a106bc3b48dbfeab5170080691051a81245fb2d7200 *Source\WinObjEx64\hash.h +494e1578e3c9cb843a217019c7a0cf8d2813ae57b9197ccddcc0015231741d5d *Source\WinObjEx64\kldbg.c +f418ff1909ff42fd1885ad0872e8a33cc9596163f0b4309251fd98355ce3280f *Source\WinObjEx64\kldbg.h +c68b84390c641bafc2427db96e5dc5926d37035c9b8928e690d228cc3c856d4e *Source\WinObjEx64\kldbg_patterns.h +64f058bae2d97ffd4c66b74b7dac13f7f9e086a81aef02b9f4e0b951735cd72d *Source\WinObjEx64\ksymbols.h +d64d77b5771b6e775dd371852d1f7fd84d7d0432d2e567c0c637878b131b0347 *Source\WinObjEx64\list.c +18ee7f9fcc5880d69fa7d1b5070e80f0b97e19899f3fef8ed5dbe7594d3034a1 *Source\WinObjEx64\list.h +52d569a0484914e14da72cb98b15a6769020fd2b2ae0cb2e7ae08c932786d6f3 *Source\WinObjEx64\main.c +12b2254a78059c8b90747af23f1efef644c68bc4f04fb0aa621a29455f14e924 *Source\WinObjEx64\msvcver.h +81b07a2a0c6e2494d713b41578c5e7224565372a66d17fd8cf47881f1f0e31b3 *Source\WinObjEx64\objects.c +eec6b4e520a13a07729e7d0b51e123c526177cd80e8d92e59c21aa664e965901 *Source\WinObjEx64\objects.h +3881d8fd5935b12949467bddb9ad4156c553068d8fb7c4b8b98003a5f17286b2 *Source\WinObjEx64\plugmngr.c +8dc17c3c9504041c303a496bbc0a1f8a0cc49a74b7204b9ef199bf0663d4eca1 *Source\WinObjEx64\plugmngr.h +41907bc22c614ab86730ef5dc2c0202aab9585220f0a54da369f722c08133832 *Source\WinObjEx64\resource.h +efbeaedc74f74d01eb6209699119db48f396805a0e8b0a95ce0a7448d1b1d898 *Source\WinObjEx64\Resource.rc +161f371f8d53b2a2d84d953d2fee521c946d261d837f9411e69a5e739d9b7a98 *Source\WinObjEx64\sdviewDlg.c +5cba0e33a6ae9fac2d099dce0e14efa38997208c80fe628acd933a10e77b36f9 *Source\WinObjEx64\symparser.c 80d167cb85b0e0b455dbb5774119e990b0c9085ac014e49207ae8e74ee19227e *Source\WinObjEx64\symparser.h -942dd4edab146b3280556fe44e1e5abac12bc3bbb21f21aa5ccc662f6b4d1558 *Source\WinObjEx64\sysinfoDlg.c -abbea050889900e73802f21134e871059fdd139860562c4047d7b995b8cd8344 *Source\WinObjEx64\sysinfoDlg.h -c968b6f78c8002536e5b8a37b124b418eb8bccfac75aff284355232835be97d6 *Source\WinObjEx64\ui.h -195eff12652bee15834b9143ba2ef2c3f4c8d13394fa4eaf7ec73fb56444eeae *Source\WinObjEx64\wine.c -4bf27b56bba8c55641692c928c9cbecb7059a766c24d1c35cf45a73d8dbfc7ea *Source\WinObjEx64\wine.h -791272a6a27c324a3ffafe7830331272210b99feac5d8fe991e3fa5a47fa5aa4 *Source\WinObjEx64\winedebug.h -a2fe7707d31e78c94f933f0a5077c60622e7003a238a5a9c3074c29792068168 *Source\WinObjEx64\WinObjEx64.vcxproj -1b0dbfeda288a77b9e8151403c1803ca93df1a1fe06f949f176a20f06cd17720 *Source\WinObjEx64\WinObjEx64.vcxproj.filters +0d4c68e643a009280b4dfaa8c4a2229c61881bd233a5c2306cc90ab4b2feaaa5 *Source\WinObjEx64\sysinfoDlg.c +3210f885e75f616dae84507d61a272280a98a0bef84f83aa041696feef71851e *Source\WinObjEx64\ui.h +4ba9c9fb91adc3b0c7500cda62fb0ac8e331072aa792a9993b0fd107f8f6163f *Source\WinObjEx64\WinObjEx64.vcxproj +ac28ae6aeb4604a7c71da0f5b2b41110f9191c147facd15dc1dc7421d1def37d *Source\WinObjEx64\WinObjEx64.vcxproj.filters ac74885d66be7ae3d4f0f6004aa24241ffdac7cd29e2a59b38d7bf2030ea6564 *Source\WinObjEx64\WinObjEx64.vcxproj.user b9d3432c775aca7ca3bba376e15a39b8c08831a620f6825599f2712c0dc974f6 *Source\WinObjEx64\drivers\wdbgdrv.c 4e9843a81b9d5677e659074d2f696310c613f857b2847fa0d83d1b0b04a3bdf0 *Source\WinObjEx64\drivers\wdbgdrv.h 5bf35529186a052b1432e6321995b6f9428c4d90a183e63a66afa5f40f6d43cf *Source\WinObjEx64\drivers\wdrvprv.c d519634f47890a27dfe69452e5b8d875d37f16f0b90ac17395d2de439114bc11 *Source\WinObjEx64\drivers\wdrvprv.h -b4a1f1e377a4740364b4a18af2ec2f97535e15e38798a0a2f4def8e6836a6070 *Source\WinObjEx64\drivers\winio.c -b64b09630b1207d96c134f67146effe43a9fe04ad00c3f930a8968c6162147d1 *Source\WinObjEx64\drivers\winio.h -a1ed3ab18754225547d3ed64f9754a617b8e81a6e0af7c0de95fa25fcbe19dba *Source\WinObjEx64\extras\extras.c -917de5c62b213aad12f9669092b5137e081a5189bd9513bc32d3b10c7d720440 *Source\WinObjEx64\extras\extras.h -ebdd308fc6cf9e6d500f5f7dba9cac66c2982c139ddf51312a9c86f762ca9d96 *Source\WinObjEx64\extras\extrasCallbacks.c -c548666e8e5f04d59af7cb664806d9002ee25909512a5aac2bcddfcb5f37b9c7 *Source\WinObjEx64\extras\extrasCallbacks.h -550115842e8c46a14faadfd460a47528c24fba03b5ac202d6394e826d1811d15 *Source\WinObjEx64\extras\extrasCallbacksPatterns.h -d95058ca43a287f7581cf28867f67414be3500de6b7673e5fcea3ae8f7a36486 *Source\WinObjEx64\extras\extrasCmOpt.c -91fadf67b77654bb67704b3366cd2b429df95386601c0c9da13159c8b0c46404 *Source\WinObjEx64\extras\extrasCmOpt.h -54bc933218397831383c1a4be5bfabf128bafa02e9d11fae87a71c927d7ad5f4 *Source\WinObjEx64\extras\extrasDrivers.c -774ad522462d3b9dfeead7335aa8f8d40664209461dad45623031a95934a9ca7 *Source\WinObjEx64\extras\extrasDrivers.h -296f5d1d378ba24af75bd6efc719f5633d9a6397d16dd16e11434697cad9b4cf *Source\WinObjEx64\extras\extrasIPC.c -8916175eeaffe13a95e0204a64e41b2e7f4af6db56dde29f8cb2cef575daf3cd *Source\WinObjEx64\extras\extrasIPC.h -25369aa89019b3052b741ad8ac5716dcd3a1d37f901c6c707d077f141ee1470f *Source\WinObjEx64\extras\extrasPN.c -5340aa12388ed410183de1f08a4eb1a1ce2c650ccb8708c9f5fb67d2b1ae30e9 *Source\WinObjEx64\extras\extrasPN.h -75c12ac915aaac0cf0cda3873d8954e96d423992c007b7d2ab9983709d9cac61 *Source\WinObjEx64\extras\extrasPSList.c -8d32e64de73d1c8ed3a543f470ffe9b96b19f53752a7b5ab27ea35d212f49df8 *Source\WinObjEx64\extras\extrasPSList.h -7414a5871dd57d9ce4aa72f0aabb61136ffbba2b4bc94bba7012907f68f607e2 *Source\WinObjEx64\extras\extrasSL.c -cdb71550e2455d3350938f6fa4b4669c54c95ba04c01280a5097363b2b1086cb *Source\WinObjEx64\extras\extrasSL.h -19442038eb271b5eda35fe8c150f8ad4c430d271c1b469d801d535a9b50301cd *Source\WinObjEx64\extras\extrasSSDT.c -498a159c5d6291ffb2df47d66971df863c6773dbdb367c59d871f802c9fd5f3e *Source\WinObjEx64\extras\extrasSSDT.h -bf8ae4aab0d12be5a3495798f8da71f42d675c28f6cd1fdb09a2a1bfe22ff067 *Source\WinObjEx64\extras\extrasSSDTsup.h -06fe140ccf32839fbd04775eff20aaa6d5a6c9a9b2cf6a6339fc8a3837a1bc8a *Source\WinObjEx64\extras\extrasUSD.c -fc510043bc5cfdb5968224599b9ca96b989b0354fa3cb594e1c2711f910393f4 *Source\WinObjEx64\extras\extrasUSD.h +fbd174ff5481dc5688cfe024761d882ea159699b09f61fda4f26fc466832421a *Source\WinObjEx64\drivers\winio.c +720ede45bc6fbec045e22da51e14ec703d33cea2c6d3fa7fb8c46163c2faa031 *Source\WinObjEx64\drivers\winio.h +228717e08983e8c020423035e7fccb79038b17b5143a161eef5bd87d06a1ead5 *Source\WinObjEx64\extras\extras.c +a22fd439c6839ff2e323882131a1245d9a195f4b34d78721386607f597d36634 *Source\WinObjEx64\extras\extras.h +7cefb0e353a2f8aed5da8849bb6c6dc8baa92c85d6043ef62b72f299f38cdc0e *Source\WinObjEx64\extras\extrasCallbacks.c +b33ada355b61038982d48a33fcd96fc7caa482d2b8930cb72413f4136829c402 *Source\WinObjEx64\extras\extrasCallbacksPatterns.h +5572a843753c6add0c5d6b489b81e789491b2c58fd8262671de0370604a5854b *Source\WinObjEx64\extras\extrasCmOpt.c +19de4c95f796ddc21256803b7793acf5d87a3d5d0eaeb02fabd0d3aed98644d1 *Source\WinObjEx64\extras\extrasDrivers.c +3a3c13c29c978ff4c093b9bba5eca9118601ebf5a90386371ddec6b5576ff419 *Source\WinObjEx64\extras\extrasHandlers.h +313c4a71e7641fcc9205516e7f0352dd0dbf1ea5c7631ee5e0104668a08ab74b *Source\WinObjEx64\extras\extrasIPC.c +0d9082b4fad1ec1fbf5def9be3b0f15ad3739383634afa933ae3b75dfa90873a *Source\WinObjEx64\extras\extrasPN.c +4bf45285d57585e2e25c19b9bc69c1404d22c041273ccc5186dfa9dcf4e3255b *Source\WinObjEx64\extras\extrasPSList.c +653600d126604ae921c120b98512a0273518b26dd1f2c639d29c8f2537832e23 *Source\WinObjEx64\extras\extrasSL.c +efac1a3edcdf158ec964a826a40185a648244c7dd1f59ddbbb04940aade3d169 *Source\WinObjEx64\extras\extrasSSDT.c +a16e474e86b4bf5dbf3f238ee29587352c45ec0499902f15f2ca706d4c9fc050 *Source\WinObjEx64\extras\extrasUSD.c c7eb605f930f9622306e127b5674d9578e5349c2eadbeb785f26a70645e196a6 *Source\WinObjEx64\hde\hde64.c e99aa4997bda14b534c614c3d8cb78a72c4aca91a1212c8b03ec605d1d75e36e *Source\WinObjEx64\hde\hde64.h f8e6a0be357726bee35c7247b57408b54bb38d94e8324a6bb84b91c462b2be30 *Source\WinObjEx64\hde\pstdint.h b774446d2f110ce954fb0a710f4693c5562ddbd8d56fe84106f2ee80db8b50a2 *Source\WinObjEx64\hde\table64.h -b47d6f3b731fcedb23848743eac8c4987ea292c855c2719341dab54134f68757 *Source\WinObjEx64\log\log.c -9931c85224699cac3951c825814cffa3dd5b417585311d8c9e2c4009267316c3 *Source\WinObjEx64\log\log.h -dffeb8f1e7b593d7d0ec2438ba76dc8e060f18b4928520ce6975cd940c77154b *Source\WinObjEx64\props\propAlpcPort.c -f2729d1787dfc1f1d2fb9b710f0d585de0eb2207d499b95f8ee96205f89dc12e *Source\WinObjEx64\props\propAlpcPort.h -6bf2d52cf4dc0027ca8d2de07fa9e7c9dcb7776da17b03f6b12c675a8099696d *Source\WinObjEx64\props\propBasic.c -fffa7061059e8485047b4a6ad5c3ce8d1e7ba7d8d8dba891f4cc4d07b81f6fd5 *Source\WinObjEx64\props\propBasic.h -bfaa9ee9af01cc7357d6745086ff516506913c1bda073a479d792fc287e8ee0c *Source\WinObjEx64\props\propBasicConsts.h -88aec1e529aae3519ed3ee54f6e5d41d7c61f19d65eb317673b5b8b87b71bdde *Source\WinObjEx64\props\propDesktop.c -0c6e9e35aed5ffbb3b007afceafc21a8574e6990cd3458833fc4a657d74db91c *Source\WinObjEx64\props\propDesktop.h -6dfee019f7f5c53315078620a2430a483d637d3b46cc05755023a740bad90248 *Source\WinObjEx64\props\propDlg.c -4e201923cf8bdb31093aa9cbf72e70371c350ce0a42abbbd59b3cbb6a90af3ff *Source\WinObjEx64\props\propDlg.h -aa0480add4f8d50ee3d62bc4c36d9a9b4625279c28cd1fd7a42b6079d19828d5 *Source\WinObjEx64\props\propDriver.c -f6b58057ca7b133ff5423ae934905bcd890808d3b048d2f1baa7f65bf644f8a1 *Source\WinObjEx64\props\propDriver.h -007660428a192d78961d270b9651b85bba04dcf7d7c1d43fa502eded10e659b7 *Source\WinObjEx64\props\propObjectDump.c -fba3aab53909e00cc23405b70665f9e2d7887d6de2413387a93e0e3226fc9dc6 *Source\WinObjEx64\props\propObjectDump.h -763827529e6d5dcef6b73e6230f7cc0278197cc13aabcf1b678f0cdac303215f *Source\WinObjEx64\props\propObjectDumpConsts.h -8a7fc99d65a0c31ac383ebdb9bcce859f05407820d4cf9712d544772ca558ce2 *Source\WinObjEx64\props\propProcess.c -268532ddb21dde8b097d077247cf4005dda741a53576f57066413b5b8dadb590 *Source\WinObjEx64\props\propProcess.h -9789a33dd3c3fa4024c6085b8ffb0c30c1e9e2bdd628a2ba539cb86997c71216 *Source\WinObjEx64\props\propSection.c -40b6dc9adeb748e9fea4326efe55e9072e70a859cae06116e65670c11921459f *Source\WinObjEx64\props\propSection.h -585baff056db86453119b5c6560106d8055ce766c9ee17213c7a13a5319bda41 *Source\WinObjEx64\props\propSecurity.c -73d05e11e43f6515001d9389d619c6846884cdcd08257797e97042da353a742e *Source\WinObjEx64\props\propSecurity.h -aea11624287f7fea27c0dd6a785a1c42f62aaf69a23d5cab3c80685c044de38b *Source\WinObjEx64\props\propSecurityConsts.h -5cef123d244c5356e9814cb13c3b6fc55487ca38560e95476997a8acae7bcef2 *Source\WinObjEx64\props\propToken.c -92fed8e1a334e87ae36ab75f8976ec167f05f8b3333da4a1e48dd946c109ee2a *Source\WinObjEx64\props\propToken.h -c3e1a73558f86e75d8bf0cce90562db9c460b66a0cb3a02290d42c8829d4857b *Source\WinObjEx64\props\propType.c -8ae27671295d405392f03a59e01f2227dcc754d668632f7baf202b596652b383 *Source\WinObjEx64\props\propType.h -d1bd41eff5392ec858aaed280423db9b5254ffa8206efbcdb3a14dfff28c0d14 *Source\WinObjEx64\props\propTypeConsts.h +da84f23f8d6c21842c6a2a65c934bce5cab97809e0ccdd1e9038d5fcdf83c267 *Source\WinObjEx64\log\log.c +5a2ae6adbb686b1dfebf1570443d2655b0f26296f1e5ec3b0a76f9786a2058bb *Source\WinObjEx64\log\log.h +d366e96b8ea05aaebc4b0de26424ec6462ee2e2f81162d51cca7ffa955dbede2 *Source\WinObjEx64\props\propAlpcPort.c +20435b68b13b0a90cdfad8681efbc0fe94b747381ef8c4999d582eef4789b61c *Source\WinObjEx64\props\propBasic.c +852be8261bb5abd4c328b96366cd3b74ca3991a0c1c530b229cbf15b4d61eb5a *Source\WinObjEx64\props\propBasicConsts.h +cb9f4daec374362e334d7bc20c0662e77e79a8c3152125d054636ef1a6ae3402 *Source\WinObjEx64\props\propCommon.h +c3e517ef7a11a350890fe77aaba8760444f3d28ab9c872780f374a71d8f87333 *Source\WinObjEx64\props\propDesktop.c +921dbe5ceed4f5a3d185227ee401291067b477ee2f14615b3e6753627ee65167 *Source\WinObjEx64\props\propDlg.c +11dbe7d95c1cdf63650c87b8d8fbc059fa812e9b2982041aa20eb3812cd460c7 *Source\WinObjEx64\props\propDlg.h +24a93fbf8a3b305cce6cf0a44ff2e2eb73450dca784bd7648ea7ce3c553fa801 *Source\WinObjEx64\props\propDriver.c +5189fbc7e0582818850ea839f9da5d66d6b0d582589e54b79d199eb9b17e6e33 *Source\WinObjEx64\props\propObjectDump.c +c272693b113ab89cf47a299f253dac2c91db4d0870bb4e11e47d936b9145400b *Source\WinObjEx64\props\propObjectDumpConsts.h +925149939d9b7bb94cbb0daf7683b7b13575c9d9e9499ebfec7751e8219864bf *Source\WinObjEx64\props\propProcess.c +b8d3fc4cb86b5d9d7d3bc5cb94b3e58fa33002c3f1160d8e874b3878aa6e8e99 *Source\WinObjEx64\props\props.h +c32f90e2a943c657b647ec5a2b029a68d4b820f8b745d74a2e281e71e3732d32 *Source\WinObjEx64\props\propSection.c +467b7bdc640b8b2c5e784769aebe054257acea16f9b618568dd92bc16d7c9731 *Source\WinObjEx64\props\propSecurity.c +af86f247784194f17d8407e8f448bc52807e87ff9d421baa76a5e548eabacb74 *Source\WinObjEx64\props\propSecurityConsts.h +e2f0702faf4d8363bd7b201de213c7c0bec7534d0ecb911c553d4aaf30fefe72 *Source\WinObjEx64\props\propToken.c +ca8672795caa0de1d7d83c0d56f73879ebcecff6d4dbebe6ca88742523c416e1 *Source\WinObjEx64\props\propType.c +5dd7b39f26f31990eb41e1f7a90dedd6512d19a8298b336b3030945508773426 *Source\WinObjEx64\props\propTypeConsts.h 51f0d1a560dd77a7f3164ae2c8f9801d6a2902bd5cfd367db522199aca35b1ff *Source\WinObjEx64\rsrc\100.ico eca976b7dd50ea206588610ccb938fbc437f7165c667e19239bf0d36d4af22f9 *Source\WinObjEx64\rsrc\101.ico 09ee2f9dfd3a4a4d8df268ed909588a94db0e97a1601ba8d4b7e6441a1626395 *Source\WinObjEx64\rsrc\102.ico @@ -307,6 +282,11 @@ d4876437f5ea4c307b3894ff6a4ccd10922a366167104bb78b1103ebadd4f483 *Source\WinObjE 0a0782e65543df1fb04f4f747cf375f109d5c673b7d0641e4bb61025ffdb9044 *Source\WinObjEx64\rsrc\obex.manifest 9c908e205f42861f5ce840cf07886009fe7fad09352820508757ae8d8f6a34ae *Source\WinObjEx64\rsrc\pipe.ico 92dc90794153274f263de95bad0a7ffb9539c38755f5cd46d45ee1e988a6411e *Source\WinObjEx64\rsrc\security.ico -a1583d56998b70aab21709326447cf222c5d20fbd43f5ada60546d393b66727a *Source\WinObjEx64\tests\testunit.c -1e99c2c94c964ac7dc864ad9136e683e6159155d6ad60ca388c7c84b0a8e0e00 *Source\WinObjEx64\tests\testunit.h +70fb3515eb2b31ebfaf9b9dbc165538ed8fb247dee19bb1cea89666c9c596379 *Source\WinObjEx64\sup\sup.c +72c254116224c18131efedc19802cbfb99340f8c733a61ab19eba493ddd0ea87 *Source\WinObjEx64\sup\sup.h +ccc88a804d4694e9ba3f97a5678d9595465e6f9afe0ec9b9613cf7c9808703a5 *Source\WinObjEx64\sup\sync.c +11af5dbe0036bb3e36607e5446cf9ec07895e49fd5137b23933bbe3830293587 *Source\WinObjEx64\sup\wine.c +0eaaa450c1e2b5c8448eb0bafd8cacc1c2d9edda30334223339a948ab1536b53 *Source\WinObjEx64\sup\wine.h +24454db160e00d514ff9bdd0f515e249460d0b948423293241542fcb230c7d4a *Source\WinObjEx64\tests\testunit.c +4df4ac3b1294d3a6564a329f3f3049f7b770c8d38bf3aadbfbf86ecf091434e7 *Source\WinObjEx64\tests\testunit.h 1232f65b57bc8732ead29a730308f6c67bc53a2f9fafd47f8c7cc4b4f676a9e9 *Source\WinObjEx64\utils\GenAsIo2Unlock.exe