├── .gitignore ├── LICENSE ├── README.md ├── SimpleOpt.h ├── dbgext.cxx ├── dbgext.hpp ├── dllmain.cxx ├── filter.cxx ├── gdiexts.cxx ├── handle.cxx ├── handle.hpp ├── help.cxx ├── object ├── palette.hpp ├── region.hpp └── suface.hpp ├── stdafx.cpp ├── stdafx.h ├── targetver.h ├── utils.cxx ├── utils.hpp ├── win32kext.def ├── win32kext.sln ├── win32kext.vcxproj └── win32kext.vcxproj.filters /.gitignore: -------------------------------------------------------------------------------- 1 | # Prerequisites 2 | *.d 3 | 4 | # Compiled Object files 5 | *.slo 6 | *.lo 7 | *.o 8 | *.obj 9 | 10 | # Precompiled Headers 11 | *.gch 12 | *.pch 13 | 14 | # Compiled Dynamic libraries 15 | *.so 16 | *.dylib 17 | *.dll 18 | 19 | # Fortran module files 20 | *.mod 21 | *.smod 22 | 23 | # Compiled Static libraries 24 | *.lai 25 | *.la 26 | *.a 27 | *.lib 28 | 29 | # Executables 30 | *.exe 31 | *.out 32 | *.app 33 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # win32kext 2 | 3 | windbg plugin for win32k debugging 4 | 5 | ## Usage 6 | 7 | ``` 8 | gh [object handle] -- HMGR entry of handle (GDI object like DC/BITMAP/PALETTE etc) 9 | uh [object handle] -- USER entry of handle (USER object like WINDOW/MENU etc) 10 | duh [-h] -- Dump USER entry of handle (USER object like WINDOW/MENU etc) 11 | dgh [-h] -- Dump HMGR entry of handle (GDI object like DC/BITMAP/PALETTE etc) 12 | dpsurf [SURFACE ptr] -- SURFACE 13 | dpso [SURFOBJ ptr] -- SURFACE struct from SURFOBJ 14 | dr [REGION ptr] -- REGION 15 | cr [REGION ptr] -- check REGION 16 | dppal [PALETTE ptr] -- PALETTE 17 | help -- show help 18 | ``` 19 | 20 | eg 21 | 22 | ``` 23 | kd> !gh rcx 24 | Object Type DC(1) 25 | Handle 0x18010665 26 | Object 0xffffa03dc4838010 27 | kd> dq 0xffffa03dc4838010+1f8 l1 28 | ffffa03d`c4838208 ffffa03d`c02a4b00 29 | kd> !dpsurf ffffa03d`c02a4b00 30 | !!!WARNING!!!surface struct has been changed(our=(0x278, system=0x2b8) please check it.and the information may be wrong 31 | SURFACE structure at 0xffffa03dc02a4b00: 32 | -------------------------------------------------- 33 | DHSURF dhsurf 0x0000000000000000 34 | HSURF hsurf 0x000000000005068a 35 | DHPDEV dhpdev 0x0000000000000000 36 | HDEV hdev 0xffffa03dc001c010 37 | SIZEL sizlBitmap.cx 0x63d 38 | SIZEL sizlBitmap.cy 0x32 39 | ULONG cjBits 0x4dfa8 40 | PVOID pvBits 0x00000000046e0000 41 | PVOID pvScan0 0x00000000046e0000 42 | LONG lDelta 0x18f4 43 | ULONG iUniq 0x940 44 | ULONG iBitmapFormat 0x6, BMF_32BPP 45 | USHORT iType 0x0, STYPE_BITMAP 46 | USHORT fjBitmap 0x811 47 | PPALETTE ppal 0xffffa03dc3d33e10 48 | -------------------------------------------------- 49 | ``` 50 | 51 | ``` 52 | 1: kd> !duh -h 53 | Usage: !duh [args] 54 | 55 | args list: 56 | -p [process] filter object by process 57 | -t [type id] filter object by type id 58 | valid type: 59 | id:1 - Window 60 | id:2 - Menu 61 | id:3 - Cursor 62 | id:4 - DeferWindowPos 63 | id:5 - WindowHook 64 | id:6 - MemoryHandle 65 | id:7 - CPD 66 | id:8 - AcceleratorTable 67 | id:9 - CsDde 68 | id:10 - Conversation 69 | id:11 - pxs 70 | id:12 - Monitor 71 | id:13 - Keyboard 72 | id:14 - KeyboardLayout 73 | id:15 - EventHook 74 | id:16 - Timer 75 | id:17 - InputContext 76 | id:18 - HidData 77 | id:20 - TouchInputInfo 78 | id:21 - GestureInfo 79 | id:23 - BaseWindow 80 | example: 81 | !duh 82 | will dump user object in system 83 | !duh -p 0xffffffff13450080 84 | will dump user object create by process 0xffffffff13450080 85 | !duh -t 1 86 | will dump all window object 87 | !duh -t 1 -p 0xffffffff13450080 88 | will dump all window object create by process 0xffffffff13450080 89 | 90 | ``` 91 | 92 | eg 93 | 94 | ``` 95 | 1: kd> !duh 96 | Total 0x380 handles 97 | handle=0x00010002 object=0xffffa99640830000 process=0xffffc409f16d4080 type=(01)Window 98 | handle=0x00010003 object=0xffffa99640850000 process=0xffffc409f16d4080 type=(03)Cursor 99 | handle=0x00010004 object=0xffffa99640830150 process=0xffffc409f16d4080 type=(01)Window 100 | handle=0x00010005 object=0xffffa996408500a0 process=0xffffc409f16d4080 type=(03)Cursor 101 | handle=0x00010006 object=0xffffa996408302a0 process=0xffffc409f16d4080 type=(01)Window 102 | 103 | 1: kd> !duh -p 0xffffc409f16d4080 -t 1 104 | Total 0x380 handles 105 | handle=0x00010002 object=0xffffa99640830000 process=0xffffc409f16d4080 type=(01)Window 106 | handle=0x00010004 object=0xffffa99640830150 process=0xffffc409f16d4080 type=(01)Window 107 | handle=0x00010006 object=0xffffa996408302a0 process=0xffffc409f16d4080 type=(01)Window 108 | handle=0x00010008 object=0xffffa996408303f0 process=0xffffc409f16d4080 type=(01)Window 109 | handle=0x0001000a object=0xffffa99640830540 process=0xffffc409f16d4080 type=(01)Window 110 | handle=0x0001000c object=0xffffa99640830690 process=0xffffc409f16d4080 type=(01)Window 111 | user control-c break 112 | 113 | 1: kd> !duh -t 1 114 | Total 0x380 handles 115 | handle=0x00010002 object=0xffffa99640830000 process=0xffffc409f16d4080 type=(01)Window w 116 | handle=0x00020016 object=0xffffa99640832e70 process=0xffffc409f168e240 type=(01)Window 117 | handle=0x00020018 object=0xffffa99640832d20 process=0xffffc409e96c1080 type=(01)Window 118 | handle=0x0002001a object=0xffffa99640832bd0 process=0xffffc409e96c1080 type=(01)Window 119 | handle=0x0003001c object=0xffffa99640832a80 process=0xffffc409f168e240 type=(01)Window 120 | user control-c break 121 | 122 | 123 | ``` 124 | 125 | ``` 126 | 1: kd> !dgh -h 127 | Usage: !dgh [args] 128 | 129 | args list: 130 | -p [process] filter object by process 131 | -t [type id] filter object by type id 132 | valid type: 133 | id:1 - DC 134 | id:2 - ColorTransform 135 | id:4 - Rgn 136 | id:5 - Bitmap 137 | id:7 - Path 138 | id:8 - Palette 139 | id:9 - ColorSpace 140 | id:10 - Font 141 | id:14 - ColorTransform 142 | id:15 - Sprite 143 | id:16 - Brush 144 | id:18 - LogicSurface 145 | id:19 - Space 146 | id:21 - ServerMetafile 147 | id:28 - Driver 148 | id:138 - Font2 149 | example: 150 | !dgh 151 | will dump gdi object in system 152 | !dgh -p 0xffffffff13450080 153 | will dump gdi object create by process 0xffffffff13450080 154 | !dgh -t 5 155 | will dump all bitmap object 156 | !dgh -t 1 -p 0xffffffff13450080 157 | will dump all bitmap object create by process 0xffffffff13450080 158 | 159 | ``` 160 | 161 | eg 162 | 163 | ``` 164 | 1: kd> !dgh -t 5 165 | Handle:0x0085000f Object=0xffffa99640890000 Type=Bitmap(5) entry=0xffffa99640a00168 processx=0x0 166 | Handle:0x0005001d Object=0xffffa99640890580 Type=Bitmap(5) entry=0xffffa99640a002b8 processx=0x0 167 | Handle:0x00050031 Object=0xffffa996408902c0 Type=Bitmap(5) entry=0xffffa99640a00498 processx=0x0 168 | Handle:0x00050032 Object=0xffffa99640890840 Type=Bitmap(5) entry=0xffffa99640a004b0 processx=0x0 169 | Handle:0x00050033 Object=0xffffa99640890b00 Type=Bitmap(5) entry=0xffffa99640a004c8 processx=0x0 170 | 171 | 1: kd> !dgh 172 | Handle:0x0004000a Object=0xffffa99640602d00 Type=Rgn(4) entry=0xffffa99640a000f0 processx=0x0 173 | Handle:0x0088000b Object=0xffffa996408e0000 Type=Palette(8) entry=0xffffa99640a00108 processx=0x0 174 | Handle:0x0008000c Object=0xffffa996408e0090 Type=Palette(8) entry=0xffffa99640a00120 processx=0x0 175 | Handle:0x0008000d Object=0xffffa996408e0120 Type=Palette(8) entry=0xffffa99640a00138 processx=0x0 176 | Handle:0x0008000e Object=0xffffa996408e01b0 Type=Palette(8) entry=0xffffa99640a00150 processx=0x0 177 | 178 | ``` 179 | 180 | 181 | 182 | ## Supported system 183 | 184 | !!!ONLY TEST ON!!! 185 | windows 10 1803 1903 64bits 186 | 187 | why not 32 bits? 188 | 189 | Guys you need change your Computer 190 | 191 | ## TODO 192 | 193 | - [ ] DC OBJECT 194 | - [ ] BRUSH OBJECT 195 | - [ ] WINDOW OBJECT 196 | - [ ] MENU OBJECT 197 | - [ ] PDEV OBJECT 198 | - [x] DUMP HANDLE 199 | - [ ] FONT OBJECT 200 | - [ ] DCOMP OBJECT 201 | 202 | ## Thanks to 203 | 204 | [SimpleOpt](https://github.com/brofield/simpleopt) 205 | -------------------------------------------------------------------------------- /SimpleOpt.h: -------------------------------------------------------------------------------- 1 | /*! @file SimpleOpt.h 2 | 3 | @version 3.6 4 | 5 | @brief A cross-platform command line library which can parse almost any 6 | of the standard command line formats in use today. It is designed 7 | explicitly to be portable to any platform and has been tested on Windows 8 | and Linux. See CSimpleOptTempl for the class definition. 9 | 10 | @section features FEATURES 11 | - MIT Licence allows free use in all software (including GPL 12 | and commercial) 13 | - multi-platform (Windows 95/98/ME/NT/2K/XP, Linux, Unix) 14 | - supports all lengths of option names: 15 | 16 |
- 17 | switch character only (e.g. use stdin for input) 18 |
-o 19 | short (single character) 20 |
-long 21 | long (multiple character, single switch character) 22 |
--longer 23 | long (multiple character, multiple switch characters) 24 |
25 | - supports all types of arguments for options: 26 | 27 |
--option 28 | short/long option flag (no argument) 29 |
--option ARG 30 | short/long option with separate required argument 31 |
--option=ARG 32 | short/long option with combined required argument 33 |
--option[=ARG] 34 | short/long option with combined optional argument 35 |
-oARG 36 | short option with combined required argument 37 |
-o[ARG] 38 | short option with combined optional argument 39 |
40 | - supports options with multiple or variable numbers of arguments: 41 | 42 |
--multi ARG1 ARG2 43 | Multiple arguments 44 |
--multi N ARG-1 ARG-2 ... ARG-N 45 | Variable number of arguments 46 |
47 | - supports case-insensitive option matching on short, long and/or 48 | word arguments. 49 | - supports options which do not use a switch character. i.e. a special 50 | word which is construed as an option. 51 | e.g. "foo.exe open /directory/file.txt" 52 | - supports clumping of multiple short options (no arguments) in a string 53 | e.g. "foo.exe -abcdef file1" <==> "foo.exe -a -b -c -d -e -f file1" 54 | - automatic recognition of a single slash as equivalent to a single 55 | hyphen on Windows, e.g. "/f FILE" is equivalent to "-f FILE". 56 | - file arguments can appear anywhere in the argument list: 57 | "foo.exe file1.txt -a ARG file2.txt --flag file3.txt file4.txt" 58 | files will be returned to the application in the same order they were 59 | supplied on the command line 60 | - short-circuit option matching: "--man" will match "--mandate" 61 | invalid options can be handled while continuing to parse the command 62 | line valid options list can be changed dynamically during command line 63 | processing, i.e. accept different options depending on an option 64 | supplied earlier in the command line. 65 | - implemented with only a single C++ header file 66 | - optionally use no C runtime or OS functions 67 | - char, wchar_t and Windows TCHAR in the same program 68 | - complete working examples included 69 | - compiles cleanly at warning level 4 (Windows/VC.NET 2003), warning 70 | level 3 (Windows/VC6) and -Wall (Linux/gcc) 71 | 72 | @section usage USAGE 73 | The SimpleOpt class is used by following these steps: 74 |
    75 |
  1. Include the SimpleOpt.h header file 76 |
      77 |         \#include "SimpleOpt.h"
      78 |         
    79 |
  2. Define an array of valid options for your program. 80 |
      81 | @link CSimpleOptTempl::SOption CSimpleOpt::SOption @endlink g_rgOptions[] = {
      82 |     { OPT_FLAG, _T("-a"),     SO_NONE    }, // "-a"
      83 |     { OPT_FLAG, _T("-b"),     SO_NONE    }, // "-b"
      84 |     { OPT_ARG,  _T("-f"),     SO_REQ_SEP }, // "-f ARG"
      85 |     { OPT_HELP, _T("-?"),     SO_NONE    }, // "-?"
      86 |     { OPT_HELP, _T("--help"), SO_NONE    }, // "--help"
      87 |     SO_END_OF_OPTIONS                       // END
      88 | };
      89 | 
    90 | Note that all options must start with a hyphen even if the slash will 91 | be accepted. This is because the slash character is automatically 92 | converted into a hyphen to test against the list of options. 93 | For example, the following line matches both "-?" and "/?" 94 | (on Windows). 95 |
      96 |     { OPT_HELP, _T("-?"),     SO_NONE    }, // "-?"
      97 | 
    98 |
  3. Instantiate a CSimpleOpt object supplying argc, argv and the option 99 | table 100 |
     101 | @link CSimpleOptTempl CSimpleOpt @endlink args(argc, argv, g_rgOptions);
     102 | 
    103 |
  4. Process the arguments by calling Next() until it returns false. 104 | On each call, first check for an error by calling LastError(), then 105 | either handle the error or process the argument. 106 |
     107 | while (args.Next()) {
     108 |     if (args.LastError() == SO_SUCCESS) {
     109 |         handle option: use OptionId(), OptionText() and OptionArg()
     110 |     }
     111 |     else {
     112 |         handle error: see ESOError enums
     113 |     }
     114 | }
     115 | 
    116 |
  5. Process all non-option arguments with File(), Files() and FileCount() 117 |
     118 | ShowFiles(args.FileCount(), args.Files());
     119 | 
    120 |
121 | 122 | @section notes NOTES 123 | - In MBCS mode, this library is guaranteed to work correctly only when 124 | all option names use only ASCII characters. 125 | - Note that if case-insensitive matching is being used then the first 126 | matching option in the argument list will be returned. 127 | 128 | @section licence MIT LICENCE 129 |
 130 |     The licence text below is the boilerplate "MIT Licence" used from:
 131 |     http://www.opensource.org/licenses/mit-license.php
 132 | 
 133 |     Copyright (c) 2006-2013, Brodie Thiesfield
 134 | 
 135 |     Permission is hereby granted, free of charge, to any person obtaining a
 136 |     copy of this software and associated documentation files (the "Software"),
 137 |     to deal in the Software without restriction, including without limitation
 138 |     the rights to use, copy, modify, merge, publish, distribute, sublicense,
 139 |     and/or sell copies of the Software, and to permit persons to whom the
 140 |     Software is furnished to do so, subject to the following conditions:
 141 | 
 142 |     The above copyright notice and this permission notice shall be included
 143 |     in all copies or substantial portions of the Software.
 144 | 
 145 |     THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 
 146 |     OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 
 147 |     MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 
 148 |     IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 
 149 |     CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 
 150 |     TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 
 151 |     SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 152 | 
153 | */ 154 | 155 | /*! @mainpage 156 | 157 | 158 |
Library SimpleOpt 159 |
Author Brodie Thiesfield [code at jellycan dot com] 160 |
Source http://code.jellycan.com/simpleopt/ 161 |
162 | 163 | @section SimpleOpt SimpleOpt 164 | 165 | A cross-platform library providing a simple method to parse almost any of 166 | the standard command-line formats in use today. 167 | 168 | See the @link SimpleOpt.h SimpleOpt @endlink documentation for full 169 | details. 170 | 171 | @section SimpleGlob SimpleGlob 172 | 173 | A cross-platform file globbing library providing the ability to 174 | expand wildcards in command-line arguments to a list of all matching 175 | files. 176 | 177 | See the @link SimpleGlob.h SimpleGlob @endlink documentation for full 178 | details. 179 | */ 180 | 181 | #ifndef INCLUDED_SimpleOpt 182 | #define INCLUDED_SimpleOpt 183 | 184 | // Default the max arguments to a fixed value. If you want to be able to 185 | // handle any number of arguments, then predefine this to 0 and it will 186 | // use an internal dynamically allocated buffer instead. 187 | #ifdef SO_MAX_ARGS 188 | # define SO_STATICBUF SO_MAX_ARGS 189 | #else 190 | # include // malloc, free 191 | # include // memcpy 192 | # define SO_STATICBUF 50 193 | #endif 194 | 195 | //! Error values 196 | typedef enum _ESOError 197 | { 198 | //! No error 199 | SO_SUCCESS = 0, 200 | 201 | /*! It looks like an option (it starts with a switch character), but 202 | it isn't registered in the option table. */ 203 | SO_OPT_INVALID = -1, 204 | 205 | /*! Multiple options matched the supplied option text. 206 | Only returned when NOT using SO_O_EXACT. */ 207 | SO_OPT_MULTIPLE = -2, 208 | 209 | /*! Option doesn't take an argument, but a combined argument was 210 | supplied. */ 211 | SO_ARG_INVALID = -3, 212 | 213 | /*! SO_REQ_CMB style-argument was supplied to a SO_REQ_SEP option 214 | Only returned when using SO_O_PEDANTIC. */ 215 | SO_ARG_INVALID_TYPE = -4, 216 | 217 | //! Required argument was not supplied 218 | SO_ARG_MISSING = -5, 219 | 220 | /*! Option argument looks like another option. 221 | Only returned when NOT using SO_O_NOERR. */ 222 | SO_ARG_INVALID_DATA = -6 223 | } ESOError; 224 | 225 | //! Option flags 226 | enum _ESOFlags 227 | { 228 | /*! Disallow partial matching of option names */ 229 | SO_O_EXACT = 0x0001, 230 | 231 | /*! Disallow use of slash as an option marker on Windows. 232 | Un*x only ever recognizes a hyphen. */ 233 | SO_O_NOSLASH = 0x0002, 234 | 235 | /*! Permit arguments on single letter options with no equals sign. 236 | e.g. -oARG or -o[ARG] */ 237 | SO_O_SHORTARG = 0x0004, 238 | 239 | /*! Permit single character options to be clumped into a single 240 | option string. e.g. "-a -b -c" <==> "-abc" */ 241 | SO_O_CLUMP = 0x0008, 242 | 243 | /*! Process the entire argv array for options, including the 244 | argv[0] entry. */ 245 | SO_O_USEALL = 0x0010, 246 | 247 | /*! Do not generate an error for invalid options. errors for missing 248 | arguments will still be generated. invalid options will be 249 | treated as files. invalid options in clumps will be silently 250 | ignored. */ 251 | SO_O_NOERR = 0x0020, 252 | 253 | /*! Validate argument type pedantically. Return an error when a 254 | separated argument "-opt arg" is supplied by the user as a 255 | combined argument "-opt=arg". By default this is not considered 256 | an error. */ 257 | SO_O_PEDANTIC = 0x0040, 258 | 259 | /*! Case-insensitive comparisons for short arguments */ 260 | SO_O_ICASE_SHORT = 0x0100, 261 | 262 | /*! Case-insensitive comparisons for long arguments */ 263 | SO_O_ICASE_LONG = 0x0200, 264 | 265 | /*! Case-insensitive comparisons for word arguments 266 | i.e. arguments without any hyphens at the start. */ 267 | SO_O_ICASE_WORD = 0x0400, 268 | 269 | /*! Case-insensitive comparisons for all arg types */ 270 | SO_O_ICASE = 0x0700 271 | }; 272 | 273 | /*! Types of arguments that options may have. Note that some of the _ESOFlags 274 | are not compatible with all argument types. SO_O_SHORTARG requires that 275 | relevant options use either SO_REQ_CMB or SO_OPT. SO_O_CLUMP requires 276 | that relevant options use only SO_NONE. 277 | */ 278 | typedef enum _ESOArgType { 279 | /*! No argument. Just the option flags. 280 | e.g. -o --opt */ 281 | SO_NONE, 282 | 283 | /*! Required separate argument. 284 | e.g. -o ARG --opt ARG */ 285 | SO_REQ_SEP, 286 | 287 | /*! Required combined argument. 288 | e.g. -oARG -o=ARG --opt=ARG */ 289 | SO_REQ_CMB, 290 | 291 | /*! Optional combined argument. 292 | e.g. -o[ARG] -o[=ARG] --opt[=ARG] */ 293 | SO_OPT, 294 | 295 | /*! Multiple separate arguments. The actual number of arguments is 296 | determined programatically at the time the argument is processed. 297 | e.g. -o N ARG1 ARG2 ... ARGN --opt N ARG1 ARG2 ... ARGN */ 298 | SO_MULTI 299 | } ESOArgType; 300 | 301 | //! this option definition must be the last entry in the table 302 | #define SO_END_OF_OPTIONS { -1, NULL, SO_NONE } 303 | 304 | #ifdef _DEBUG 305 | # ifdef _MSC_VER 306 | # include 307 | # define SO_ASSERT(b) _ASSERTE(b) 308 | # else 309 | # include 310 | # define SO_ASSERT(b) assert(b) 311 | # endif 312 | #else 313 | # define SO_ASSERT(b) //!< assertion used to test input data 314 | #endif 315 | 316 | // --------------------------------------------------------------------------- 317 | // MAIN TEMPLATE CLASS 318 | // --------------------------------------------------------------------------- 319 | 320 | /*! @brief Implementation of the SimpleOpt class */ 321 | template 322 | class CSimpleOptTempl 323 | { 324 | public: 325 | /*! @brief Structure used to define all known options. */ 326 | struct SOption { 327 | /*! ID to return for this flag. Optional but must be >= 0 */ 328 | int nId; 329 | 330 | /*! arg string to search for, e.g. "open", "-", "-f", "--file" 331 | Note that on Windows the slash option marker will be converted 332 | to a hyphen so that "-f" will also match "/f". */ 333 | const SOCHAR * pszArg; 334 | 335 | /*! type of argument accepted by this option */ 336 | ESOArgType nArgType; 337 | }; 338 | 339 | /*! @brief Initialize the class. Init() must be called later. */ 340 | CSimpleOptTempl() 341 | : m_rgShuffleBuf(NULL) 342 | { 343 | Init(0, NULL, NULL, 0); 344 | } 345 | 346 | /*! @brief Initialize the class in preparation for use. */ 347 | CSimpleOptTempl( 348 | int argc, 349 | SOCHAR * argv[], 350 | const SOption * a_rgOptions, 351 | int a_nFlags = 0 352 | ) 353 | : m_rgShuffleBuf(NULL) 354 | { 355 | Init(argc, argv, a_rgOptions, a_nFlags); 356 | } 357 | 358 | #ifndef SO_MAX_ARGS 359 | /*! @brief Deallocate any allocated memory. */ 360 | ~CSimpleOptTempl() { if (m_rgShuffleBuf) free(m_rgShuffleBuf); } 361 | #endif 362 | 363 | /*! @brief Initialize the class in preparation for calling Next. 364 | 365 | The table of options pointed to by a_rgOptions does not need to be 366 | valid at the time that Init() is called. However on every call to 367 | Next() the table pointed to must be a valid options table with the 368 | last valid entry set to SO_END_OF_OPTIONS. 369 | 370 | NOTE: the array pointed to by a_argv will be modified by this 371 | class and must not be used or modified outside of member calls to 372 | this class. 373 | 374 | @param a_argc Argument array size 375 | @param a_argv Argument array 376 | @param a_rgOptions Valid option array 377 | @param a_nFlags Optional flags to modify the processing of 378 | the arguments 379 | 380 | @return true Successful 381 | @return false if SO_MAX_ARGC > 0: Too many arguments 382 | if SO_MAX_ARGC == 0: Memory allocation failure 383 | */ 384 | bool Init( 385 | int a_argc, 386 | SOCHAR * a_argv[], 387 | const SOption * a_rgOptions, 388 | int a_nFlags = 0 389 | ); 390 | 391 | /*! @brief Change the current options table during option parsing. 392 | 393 | @param a_rgOptions Valid option array 394 | */ 395 | inline void SetOptions(const SOption * a_rgOptions) { 396 | m_rgOptions = a_rgOptions; 397 | } 398 | 399 | /*! @brief Change the current flags during option parsing. 400 | 401 | Note that changing the SO_O_USEALL flag here will have no affect. 402 | It must be set using Init() or the constructor. 403 | 404 | @param a_nFlags Flags to modify the processing of the arguments 405 | */ 406 | inline void SetFlags(int a_nFlags) { m_nFlags = a_nFlags; } 407 | 408 | /*! @brief Query if a particular flag is set */ 409 | inline bool HasFlag(int a_nFlag) const { 410 | return (m_nFlags & a_nFlag) == a_nFlag; 411 | } 412 | 413 | /*! @brief Advance to the next option if available. 414 | 415 | When all options have been processed it will return false. When true 416 | has been returned, you must check for an invalid or unrecognized 417 | option using the LastError() method. This will be return an error 418 | value other than SO_SUCCESS on an error. All standard data 419 | (e.g. OptionText(), OptionArg(), OptionId(), etc) will be available 420 | depending on the error. 421 | 422 | After all options have been processed, the remaining files from the 423 | command line can be processed in same order as they were passed to 424 | the program. 425 | 426 | @return true option or error available for processing 427 | @return false all options have been processed 428 | */ 429 | bool Next(); 430 | 431 | /*! Stops processing of the command line and returns all remaining 432 | arguments as files. The next call to Next() will return false. 433 | */ 434 | void Stop(); 435 | 436 | /*! @brief Return the last error that occurred. 437 | 438 | This function must always be called before processing the current 439 | option. This function is available only when Next() has returned true. 440 | */ 441 | inline ESOError LastError() const { return m_nLastError; } 442 | 443 | /*! @brief Return the nId value from the options array for the current 444 | option. 445 | 446 | This function is available only when Next() has returned true. 447 | */ 448 | inline int OptionId() const { return m_nOptionId; } 449 | 450 | /*! @brief Return the pszArg from the options array for the current 451 | option. 452 | 453 | This function is available only when Next() has returned true. 454 | */ 455 | inline const SOCHAR * OptionText() const { return m_pszOptionText; } 456 | 457 | /*! @brief Return the argument for the current option where one exists. 458 | 459 | If there is no argument for the option, this will return NULL. 460 | This function is available only when Next() has returned true. 461 | */ 462 | inline SOCHAR * OptionArg() const { return m_pszOptionArg; } 463 | 464 | /*! @brief Validate and return the desired number of arguments. 465 | 466 | This is only valid when OptionId() has return the ID of an option 467 | that is registered as SO_MULTI. It may be called multiple times 468 | each time returning the desired number of arguments. Previously 469 | returned argument pointers are remain valid. 470 | 471 | If an error occurs during processing, NULL will be returned and 472 | the error will be available via LastError(). 473 | 474 | @param n Number of arguments to return. 475 | */ 476 | SOCHAR ** MultiArg(int n); 477 | 478 | /*! @brief Returned the number of entries in the Files() array. 479 | 480 | After Next() has returned false, this will be the list of files (or 481 | otherwise unprocessed arguments). 482 | */ 483 | inline int FileCount() const { return m_argc - m_nLastArg; } 484 | 485 | /*! @brief Return the specified file argument. 486 | 487 | @param n Index of the file to return. This must be between 0 488 | and FileCount() - 1; 489 | */ 490 | inline SOCHAR * File(int n) const { 491 | SO_ASSERT(n >= 0 && n < FileCount()); 492 | return m_argv[m_nLastArg + n]; 493 | } 494 | 495 | /*! @brief Return the array of files. */ 496 | inline SOCHAR ** Files() const { return &m_argv[m_nLastArg]; } 497 | 498 | private: 499 | CSimpleOptTempl(const CSimpleOptTempl &); // disabled 500 | CSimpleOptTempl & operator=(const CSimpleOptTempl &); // disabled 501 | 502 | SOCHAR PrepareArg(SOCHAR * a_pszString) const; 503 | bool NextClumped(); 504 | void ShuffleArg(int a_nStartIdx, int a_nCount); 505 | int LookupOption(const SOCHAR * a_pszOption) const; 506 | int CalcMatch(const SOCHAR *a_pszSource, const SOCHAR *a_pszTest) const; 507 | 508 | // Find the '=' character within a string. 509 | inline SOCHAR * FindEquals(SOCHAR *s) const { 510 | while (*s && *s != (SOCHAR)'=') ++s; 511 | return *s ? s : NULL; 512 | } 513 | bool IsEqual(SOCHAR a_cLeft, SOCHAR a_cRight, int a_nArgType) const; 514 | 515 | inline void Copy(SOCHAR ** ppDst, SOCHAR ** ppSrc, int nCount) const { 516 | #ifdef SO_MAX_ARGS 517 | // keep our promise of no CLIB usage 518 | while (nCount-- > 0) *ppDst++ = *ppSrc++; 519 | #else 520 | memcpy(ppDst, ppSrc, nCount * sizeof(SOCHAR*)); 521 | #endif 522 | } 523 | 524 | private: 525 | const SOption * m_rgOptions; //!< pointer to options table 526 | int m_nFlags; //!< flags 527 | int m_nOptionIdx; //!< current argv option index 528 | int m_nOptionId; //!< id of current option (-1 = invalid) 529 | int m_nNextOption; //!< index of next option 530 | int m_nLastArg; //!< last argument, after this are files 531 | int m_argc; //!< argc to process 532 | SOCHAR ** m_argv; //!< argv 533 | const SOCHAR * m_pszOptionText; //!< curr option text, e.g. "-f" 534 | SOCHAR * m_pszOptionArg; //!< curr option arg, e.g. "c:\file.txt" 535 | SOCHAR * m_pszClump; //!< clumped single character options 536 | SOCHAR m_szShort[3]; //!< temp for clump and combined args 537 | ESOError m_nLastError; //!< error status from the last call 538 | SOCHAR ** m_rgShuffleBuf; //!< shuffle buffer for large argc 539 | }; 540 | 541 | // --------------------------------------------------------------------------- 542 | // IMPLEMENTATION 543 | // --------------------------------------------------------------------------- 544 | 545 | template 546 | bool 547 | CSimpleOptTempl::Init( 548 | int a_argc, 549 | SOCHAR * a_argv[], 550 | const SOption * a_rgOptions, 551 | int a_nFlags 552 | ) 553 | { 554 | m_argc = a_argc; 555 | m_nLastArg = a_argc; 556 | m_argv = a_argv; 557 | m_rgOptions = a_rgOptions; 558 | m_nLastError = SO_SUCCESS; 559 | m_nOptionIdx = 0; 560 | m_nOptionId = -1; 561 | m_pszOptionText = NULL; 562 | m_pszOptionArg = NULL; 563 | m_nNextOption = (a_nFlags & SO_O_USEALL) ? 0 : 1; 564 | m_szShort[0] = (SOCHAR)'-'; 565 | m_szShort[2] = (SOCHAR)'\0'; 566 | m_nFlags = a_nFlags; 567 | m_pszClump = NULL; 568 | 569 | #ifdef SO_MAX_ARGS 570 | if (m_argc > SO_MAX_ARGS) { 571 | m_nLastError = SO_ARG_INVALID_DATA; 572 | m_nLastArg = 0; 573 | return false; 574 | } 575 | #else 576 | if (m_rgShuffleBuf) { 577 | free(m_rgShuffleBuf); 578 | } 579 | if (m_argc > SO_STATICBUF) { 580 | m_rgShuffleBuf = (SOCHAR**) malloc(sizeof(SOCHAR*) * m_argc); 581 | if (!m_rgShuffleBuf) { 582 | return false; 583 | } 584 | } 585 | #endif 586 | 587 | return true; 588 | } 589 | 590 | template 591 | bool 592 | CSimpleOptTempl::Next() 593 | { 594 | #ifdef SO_MAX_ARGS 595 | if (m_argc > SO_MAX_ARGS) { 596 | SO_ASSERT(!"Too many args! Check the return value of Init()!"); 597 | return false; 598 | } 599 | #endif 600 | 601 | // process a clumped option string if appropriate 602 | if (m_pszClump && *m_pszClump) { 603 | // silently discard invalid clumped option 604 | bool bIsValid = NextClumped(); 605 | while (*m_pszClump && !bIsValid && HasFlag(SO_O_NOERR)) { 606 | bIsValid = NextClumped(); 607 | } 608 | 609 | // return this option if valid or we are returning errors 610 | if (bIsValid || !HasFlag(SO_O_NOERR)) { 611 | return true; 612 | } 613 | } 614 | SO_ASSERT(!m_pszClump || !*m_pszClump); 615 | m_pszClump = NULL; 616 | 617 | // init for the next option 618 | m_nOptionIdx = m_nNextOption; 619 | m_nOptionId = -1; 620 | m_pszOptionText = NULL; 621 | m_pszOptionArg = NULL; 622 | m_nLastError = SO_SUCCESS; 623 | 624 | // find the next option 625 | SOCHAR cFirst; 626 | int nTableIdx = -1; 627 | int nOptIdx = m_nOptionIdx; 628 | while (nTableIdx < 0 && nOptIdx < m_nLastArg) { 629 | SOCHAR * pszArg = m_argv[nOptIdx]; 630 | m_pszOptionArg = NULL; 631 | 632 | // find this option in the options table 633 | cFirst = PrepareArg(pszArg); 634 | if (pszArg[0] == (SOCHAR)'-') { 635 | // find any combined argument string and remove equals sign 636 | m_pszOptionArg = FindEquals(pszArg); 637 | if (m_pszOptionArg) { 638 | *m_pszOptionArg++ = (SOCHAR)'\0'; 639 | } 640 | } 641 | nTableIdx = LookupOption(pszArg); 642 | 643 | // if we didn't find this option but if it is a short form 644 | // option then we try the alternative forms 645 | if (nTableIdx < 0 646 | && !m_pszOptionArg 647 | && pszArg[0] == (SOCHAR)'-' 648 | && pszArg[1] 649 | && pszArg[1] != (SOCHAR)'-' 650 | && pszArg[2]) 651 | { 652 | // test for a short-form with argument if appropriate 653 | if (HasFlag(SO_O_SHORTARG)) { 654 | m_szShort[1] = pszArg[1]; 655 | int nIdx = LookupOption(m_szShort); 656 | if (nIdx >= 0 657 | && (m_rgOptions[nIdx].nArgType == SO_REQ_CMB 658 | || m_rgOptions[nIdx].nArgType == SO_OPT)) 659 | { 660 | m_pszOptionArg = &pszArg[2]; 661 | pszArg = m_szShort; 662 | nTableIdx = nIdx; 663 | } 664 | } 665 | 666 | // test for a clumped short-form option string and we didn't 667 | // match on the short-form argument above 668 | if (nTableIdx < 0 && HasFlag(SO_O_CLUMP)) { 669 | m_pszClump = &pszArg[1]; 670 | ++m_nNextOption; 671 | if (nOptIdx > m_nOptionIdx) { 672 | ShuffleArg(m_nOptionIdx, nOptIdx - m_nOptionIdx); 673 | } 674 | return Next(); 675 | } 676 | } 677 | 678 | // The option wasn't found. If it starts with a switch character 679 | // and we are not suppressing errors for invalid options then it 680 | // is reported as an error, otherwise it is data. 681 | if (nTableIdx < 0) { 682 | if (!HasFlag(SO_O_NOERR) && pszArg[0] == (SOCHAR)'-') { 683 | m_pszOptionText = pszArg; 684 | break; 685 | } 686 | 687 | pszArg[0] = cFirst; 688 | ++nOptIdx; 689 | if (m_pszOptionArg) { 690 | *(--m_pszOptionArg) = (SOCHAR)'='; 691 | } 692 | } 693 | } 694 | 695 | // end of options 696 | if (nOptIdx >= m_nLastArg) { 697 | if (nOptIdx > m_nOptionIdx) { 698 | ShuffleArg(m_nOptionIdx, nOptIdx - m_nOptionIdx); 699 | } 700 | return false; 701 | } 702 | ++m_nNextOption; 703 | 704 | // get the option id 705 | ESOArgType nArgType = SO_NONE; 706 | if (nTableIdx < 0) { 707 | m_nLastError = (ESOError) nTableIdx; // error code 708 | } 709 | else { 710 | m_nOptionId = m_rgOptions[nTableIdx].nId; 711 | m_pszOptionText = m_rgOptions[nTableIdx].pszArg; 712 | 713 | // ensure that the arg type is valid 714 | nArgType = m_rgOptions[nTableIdx].nArgType; 715 | switch (nArgType) { 716 | case SO_NONE: 717 | if (m_pszOptionArg) { 718 | m_nLastError = SO_ARG_INVALID; 719 | } 720 | break; 721 | 722 | case SO_REQ_SEP: 723 | if (m_pszOptionArg) { 724 | // they wanted separate args, but we got a combined one, 725 | // unless we are pedantic, just accept it. 726 | if (HasFlag(SO_O_PEDANTIC)) { 727 | m_nLastError = SO_ARG_INVALID_TYPE; 728 | } 729 | } 730 | // more processing after we shuffle 731 | break; 732 | 733 | case SO_REQ_CMB: 734 | if (!m_pszOptionArg) { 735 | m_nLastError = SO_ARG_MISSING; 736 | } 737 | break; 738 | 739 | case SO_OPT: 740 | // nothing to do 741 | break; 742 | 743 | case SO_MULTI: 744 | // nothing to do. Caller must now check for valid arguments 745 | // using GetMultiArg() 746 | break; 747 | } 748 | } 749 | 750 | // shuffle the files out of the way 751 | if (nOptIdx > m_nOptionIdx) { 752 | ShuffleArg(m_nOptionIdx, nOptIdx - m_nOptionIdx); 753 | } 754 | 755 | // we need to return the separate arg if required, just re-use the 756 | // multi-arg code because it all does the same thing 757 | if ( nArgType == SO_REQ_SEP 758 | && !m_pszOptionArg 759 | && m_nLastError == SO_SUCCESS) 760 | { 761 | SOCHAR ** ppArgs = MultiArg(1); 762 | if (ppArgs) { 763 | m_pszOptionArg = *ppArgs; 764 | } 765 | } 766 | 767 | return true; 768 | } 769 | 770 | template 771 | void 772 | CSimpleOptTempl::Stop() 773 | { 774 | if (m_nNextOption < m_nLastArg) { 775 | ShuffleArg(m_nNextOption, m_nLastArg - m_nNextOption); 776 | } 777 | } 778 | 779 | template 780 | SOCHAR 781 | CSimpleOptTempl::PrepareArg( 782 | SOCHAR * a_pszString 783 | ) const 784 | { 785 | #ifdef _WIN32 786 | // On Windows we can accept the forward slash as a single character 787 | // option delimiter, but it cannot replace the '-' option used to 788 | // denote stdin. On Un*x paths may start with slash so it may not 789 | // be used to start an option. 790 | if (!HasFlag(SO_O_NOSLASH) 791 | && a_pszString[0] == (SOCHAR)'/' 792 | && a_pszString[1] 793 | && a_pszString[1] != (SOCHAR)'-') 794 | { 795 | a_pszString[0] = (SOCHAR)'-'; 796 | return (SOCHAR)'/'; 797 | } 798 | #endif 799 | return a_pszString[0]; 800 | } 801 | 802 | template 803 | bool 804 | CSimpleOptTempl::NextClumped() 805 | { 806 | // prepare for the next clumped option 807 | m_szShort[1] = *m_pszClump++; 808 | m_nOptionId = -1; 809 | m_pszOptionText = NULL; 810 | m_pszOptionArg = NULL; 811 | m_nLastError = SO_SUCCESS; 812 | 813 | // lookup this option, ensure that we are using exact matching 814 | int nSavedFlags = m_nFlags; 815 | m_nFlags = SO_O_EXACT; 816 | int nTableIdx = LookupOption(m_szShort); 817 | m_nFlags = nSavedFlags; 818 | 819 | // unknown option 820 | if (nTableIdx < 0) { 821 | m_pszOptionText = m_szShort; // invalid option 822 | m_nLastError = (ESOError) nTableIdx; // error code 823 | return false; 824 | } 825 | 826 | // valid option 827 | m_pszOptionText = m_rgOptions[nTableIdx].pszArg; 828 | ESOArgType nArgType = m_rgOptions[nTableIdx].nArgType; 829 | if (nArgType == SO_NONE) { 830 | m_nOptionId = m_rgOptions[nTableIdx].nId; 831 | return true; 832 | } 833 | 834 | if (nArgType == SO_REQ_CMB && *m_pszClump) { 835 | m_nOptionId = m_rgOptions[nTableIdx].nId; 836 | m_pszOptionArg = m_pszClump; 837 | while (*m_pszClump) ++m_pszClump; // must point to an empty string 838 | return true; 839 | } 840 | 841 | // invalid option as it requires an argument 842 | m_nLastError = SO_ARG_MISSING; 843 | return true; 844 | } 845 | 846 | // Shuffle arguments to the end of the argv array. 847 | // 848 | // For example: 849 | // argv[] = { "0", "1", "2", "3", "4", "5", "6", "7", "8" }; 850 | // 851 | // ShuffleArg(1, 1) = { "0", "2", "3", "4", "5", "6", "7", "8", "1" }; 852 | // ShuffleArg(5, 2) = { "0", "1", "2", "3", "4", "7", "8", "5", "6" }; 853 | // ShuffleArg(2, 4) = { "0", "1", "6", "7", "8", "2", "3", "4", "5" }; 854 | template 855 | void 856 | CSimpleOptTempl::ShuffleArg( 857 | int a_nStartIdx, 858 | int a_nCount 859 | ) 860 | { 861 | SOCHAR * staticBuf[SO_STATICBUF]; 862 | SOCHAR ** buf = m_rgShuffleBuf ? m_rgShuffleBuf : staticBuf; 863 | int nTail = m_argc - a_nStartIdx - a_nCount; 864 | 865 | // make a copy of the elements to be moved 866 | Copy(buf, m_argv + a_nStartIdx, a_nCount); 867 | 868 | // move the tail down 869 | Copy(m_argv + a_nStartIdx, m_argv + a_nStartIdx + a_nCount, nTail); 870 | 871 | // append the moved elements to the tail 872 | Copy(m_argv + a_nStartIdx + nTail, buf, a_nCount); 873 | 874 | // update the index of the last unshuffled arg 875 | m_nLastArg -= a_nCount; 876 | } 877 | 878 | // match on the long format strings. partial matches will be 879 | // accepted only if that feature is enabled. 880 | template 881 | int 882 | CSimpleOptTempl::LookupOption( 883 | const SOCHAR * a_pszOption 884 | ) const 885 | { 886 | int nBestMatch = -1; // index of best match so far 887 | int nBestMatchLen = 0; // matching characters of best match 888 | int nLastMatchLen = 0; // matching characters of last best match 889 | 890 | for (int n = 0; m_rgOptions[n].nId >= 0; ++n) { 891 | // the option table must use hyphens as the option character, 892 | // the slash character is converted to a hyphen for testing. 893 | SO_ASSERT(m_rgOptions[n].pszArg[0] != (SOCHAR)'/'); 894 | 895 | int nMatchLen = CalcMatch(m_rgOptions[n].pszArg, a_pszOption); 896 | if (nMatchLen == -1) { 897 | return n; 898 | } 899 | if (nMatchLen > 0 && nMatchLen >= nBestMatchLen) { 900 | nLastMatchLen = nBestMatchLen; 901 | nBestMatchLen = nMatchLen; 902 | nBestMatch = n; 903 | } 904 | } 905 | 906 | // only partial matches or no match gets to here, ensure that we 907 | // don't return a partial match unless it is a clear winner 908 | if (HasFlag(SO_O_EXACT) || nBestMatch == -1) { 909 | return SO_OPT_INVALID; 910 | } 911 | return (nBestMatchLen > nLastMatchLen) ? nBestMatch : SO_OPT_MULTIPLE; 912 | } 913 | 914 | // calculate the number of characters that match (case-sensitive) 915 | // 0 = no match, > 0 == number of characters, -1 == perfect match 916 | template 917 | int 918 | CSimpleOptTempl::CalcMatch( 919 | const SOCHAR * a_pszSource, 920 | const SOCHAR * a_pszTest 921 | ) const 922 | { 923 | if (!a_pszSource || !a_pszTest) { 924 | return 0; 925 | } 926 | 927 | // determine the argument type 928 | int nArgType = SO_O_ICASE_LONG; 929 | if (a_pszSource[0] != '-') { 930 | nArgType = SO_O_ICASE_WORD; 931 | } 932 | else if (a_pszSource[1] != '-' && !a_pszSource[2]) { 933 | nArgType = SO_O_ICASE_SHORT; 934 | } 935 | 936 | // match and skip leading hyphens 937 | while (*a_pszSource == (SOCHAR)'-' && *a_pszSource == *a_pszTest) { 938 | ++a_pszSource; 939 | ++a_pszTest; 940 | } 941 | if (*a_pszSource == (SOCHAR)'-' || *a_pszTest == (SOCHAR)'-') { 942 | return 0; 943 | } 944 | 945 | // find matching number of characters in the strings 946 | int nLen = 0; 947 | while (*a_pszSource && IsEqual(*a_pszSource, *a_pszTest, nArgType)) { 948 | ++a_pszSource; 949 | ++a_pszTest; 950 | ++nLen; 951 | } 952 | 953 | // if we have exhausted the source... 954 | if (!*a_pszSource) { 955 | // and the test strings, then it's a perfect match 956 | if (!*a_pszTest) { 957 | return -1; 958 | } 959 | 960 | // otherwise the match failed as the test is longer than 961 | // the source. i.e. "--mant" will not match the option "--man". 962 | return 0; 963 | } 964 | 965 | // if we haven't exhausted the test string then it is not a match 966 | // i.e. "--mantle" will not best-fit match to "--mandate" at all. 967 | if (*a_pszTest) { 968 | return 0; 969 | } 970 | 971 | // partial match to the current length of the test string 972 | return nLen; 973 | } 974 | 975 | template 976 | bool 977 | CSimpleOptTempl::IsEqual( 978 | SOCHAR a_cLeft, 979 | SOCHAR a_cRight, 980 | int a_nArgType 981 | ) const 982 | { 983 | // if this matches then we are doing case-insensitive matching 984 | if (m_nFlags & a_nArgType) { 985 | if (a_cLeft >= 'A' && a_cLeft <= 'Z') a_cLeft += 'a' - 'A'; 986 | if (a_cRight >= 'A' && a_cRight <= 'Z') a_cRight += 'a' - 'A'; 987 | } 988 | return a_cLeft == a_cRight; 989 | } 990 | 991 | // calculate the number of characters that match (case-sensitive) 992 | // 0 = no match, > 0 == number of characters, -1 == perfect match 993 | template 994 | SOCHAR ** 995 | CSimpleOptTempl::MultiArg( 996 | int a_nCount 997 | ) 998 | { 999 | // ensure we have enough arguments 1000 | if (m_nNextOption + a_nCount > m_nLastArg) { 1001 | m_nLastError = SO_ARG_MISSING; 1002 | return NULL; 1003 | } 1004 | 1005 | // our argument array 1006 | SOCHAR ** rgpszArg = &m_argv[m_nNextOption]; 1007 | 1008 | // Ensure that each of the following don't start with an switch character. 1009 | // Only make this check if we are returning errors for unknown arguments. 1010 | if (!HasFlag(SO_O_NOERR)) { 1011 | for (int n = 0; n < a_nCount; ++n) { 1012 | SOCHAR ch = PrepareArg(rgpszArg[n]); 1013 | if (rgpszArg[n][0] == (SOCHAR)'-') { 1014 | rgpszArg[n][0] = ch; 1015 | m_nLastError = SO_ARG_INVALID_DATA; 1016 | return NULL; 1017 | } 1018 | rgpszArg[n][0] = ch; 1019 | } 1020 | } 1021 | 1022 | // all good 1023 | m_nNextOption += a_nCount; 1024 | return rgpszArg; 1025 | } 1026 | 1027 | 1028 | // --------------------------------------------------------------------------- 1029 | // TYPE DEFINITIONS 1030 | // --------------------------------------------------------------------------- 1031 | 1032 | /*! @brief ASCII/MBCS version of CSimpleOpt */ 1033 | typedef CSimpleOptTempl CSimpleOptA; 1034 | 1035 | /*! @brief wchar_t version of CSimpleOpt */ 1036 | typedef CSimpleOptTempl CSimpleOptW; 1037 | 1038 | #if defined(_UNICODE) 1039 | /*! @brief TCHAR version dependent on if _UNICODE is defined */ 1040 | # define CSimpleOpt CSimpleOptW 1041 | #else 1042 | /*! @brief TCHAR version dependent on if _UNICODE is defined */ 1043 | # define CSimpleOpt CSimpleOptA 1044 | #endif 1045 | 1046 | #endif // INCLUDED_SimpleOpt 1047 | -------------------------------------------------------------------------------- /dbgext.cxx: -------------------------------------------------------------------------------- 1 | 2 | #include "stdafx.h" 3 | #include "dbgext.hpp" 4 | 5 | 6 | PDEBUG_CLIENT4 g_ExtClient; 7 | PDEBUG_CONTROL4 g_ExtControl; 8 | PDEBUG_SYMBOLS2 g_ExtSymbols; 9 | 10 | WINDBG_EXTENSION_APIS ExtensionApis; 11 | 12 | ULONG TargetMachine; 13 | BOOL Connected; 14 | 15 | // Queries for all debugger interfaces. 16 | extern "C" HRESULT 17 | ExtQuery(PDEBUG_CLIENT4 Client) 18 | { 19 | HRESULT Status; 20 | 21 | if ((Status = Client->QueryInterface(__uuidof(IDebugControl4), 22 | (void**)&g_ExtControl)) != S_OK) 23 | { 24 | goto Fail; 25 | } 26 | if ((Status = Client->QueryInterface(__uuidof(IDebugSymbols2), 27 | (void**)&g_ExtSymbols)) != S_OK) 28 | { 29 | goto Fail; 30 | } 31 | g_ExtClient = Client; 32 | 33 | return S_OK; 34 | 35 | Fail: 36 | ExtRelease(); 37 | return Status; 38 | } 39 | 40 | // Cleans up all debugger interfaces. 41 | void 42 | ExtRelease(void) 43 | { 44 | g_ExtClient = NULL; 45 | EXT_RELEASE(g_ExtControl); 46 | EXT_RELEASE(g_ExtSymbols); 47 | } 48 | 49 | 50 | // Normal output. 51 | void __cdecl 52 | ExtOut(PCSTR Format, ...) 53 | { 54 | va_list Args; 55 | 56 | va_start(Args, Format); 57 | g_ExtControl->OutputVaList(DEBUG_OUTPUT_NORMAL, Format, Args); 58 | va_end(Args); 59 | } 60 | 61 | // Error output. 62 | void __cdecl 63 | ExtErr(PCSTR Format, ...) 64 | { 65 | va_list Args; 66 | 67 | va_start(Args, Format); 68 | g_ExtControl->OutputVaList(DEBUG_OUTPUT_ERROR, Format, Args); 69 | va_end(Args); 70 | } 71 | 72 | // Warning output. 73 | void __cdecl 74 | ExtWarn(PCSTR Format, ...) 75 | { 76 | va_list Args; 77 | 78 | va_start(Args, Format); 79 | g_ExtControl->OutputVaList(DEBUG_OUTPUT_WARNING, Format, Args); 80 | va_end(Args); 81 | } 82 | 83 | void __cdecl 84 | ExtOutDml(PCSTR Format, ...) 85 | { 86 | va_list Args; 87 | 88 | va_start(Args, Format); 89 | g_ExtControl->ControlledOutputVaList(DEBUG_OUTCTL_DML, DEBUG_OUTPUT_NORMAL, Format, Args); 90 | va_end(Args); 91 | } 92 | 93 | EXTERN_C 94 | WIN32KEXT_API 95 | HRESULT 96 | CALLBACK 97 | DebugExtensionInitialize( 98 | OUT PULONG Version, 99 | OUT PULONG Flags) 100 | { 101 | IDebugClient* DebugClient; 102 | PDEBUG_CONTROL4 DebugControl; 103 | HRESULT hr = S_OK; 104 | 105 | *Version = DEBUG_EXTENSION_VERSION(1, 0); 106 | *Flags = 0; 107 | 108 | // 109 | // Create a new debugclient 110 | // 111 | 112 | if ((hr = DebugCreate(__uuidof(IDebugClient), (void**)&DebugClient)) != S_OK) { 113 | return hr; 114 | } 115 | 116 | if ((hr = DebugClient->QueryInterface(__uuidof(IDebugControl), 117 | (void**)&DebugControl)) == S_OK){ 118 | 119 | // 120 | // Get the windbg-style extension APIS 121 | // 122 | 123 | ExtensionApis.nSize = sizeof(ExtensionApis); 124 | hr = DebugControl->GetWindbgExtensionApis64(&ExtensionApis); 125 | 126 | // 127 | // check is in kernel mode 128 | // 129 | 130 | ULONG dbgClass, dbgQualifier; 131 | if (SUCCEEDED(DebugControl->GetDebuggeeType(&dbgClass, &dbgQualifier))) { 132 | if (dbgClass != DEBUG_CLASS_KERNEL) { 133 | dprintf("This extension is for kernel-mode only\n"); 134 | hr = S_FALSE; 135 | } 136 | } 137 | 138 | // 139 | // check os 140 | // 141 | 142 | ULONG PlatformId, Win32Major, Win32Minor, KdMajor, KdMinor; 143 | if (SUCCEEDED(DebugControl->GetSystemVersionValues(&PlatformId, &Win32Major, &Win32Minor, 144 | &KdMajor, &KdMinor))) { 145 | DEBUGPRINT("OS Detected: %d.%d.%d.%d\n", Win32Major, Win32Minor, KdMajor, KdMinor); 146 | } 147 | 148 | DebugControl->Release(); 149 | 150 | } 151 | DebugClient->Release(); 152 | return hr; 153 | } 154 | 155 | EXTERN_C 156 | WIN32KEXT_API 157 | void 158 | CALLBACK 159 | DebugExtensionUninitialize(void) 160 | { 161 | //DEBUGPRINT("DebugExtensionUninitialize\n"); 162 | return; 163 | } -------------------------------------------------------------------------------- /dbgext.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #ifndef IfFailedReturn 4 | #define IfFailedReturn(EXPR) do { hr = (EXPR); if (FAILED(hr)) { return hr; }} while(FALSE, FALSE) 5 | #endif // IfFailedReturn 6 | 7 | #ifdef __cplusplus 8 | extern "C" { 9 | #endif 10 | 11 | 12 | #define INIT_API() \ 13 | HRESULT Status; \ 14 | if ((Status = ExtQuery(Client)) != S_OK) return Status; 15 | 16 | #define EXT_RELEASE(Unk) \ 17 | ((Unk) != NULL ? ((Unk)->Release(), (Unk) = NULL) : NULL) 18 | 19 | #define EXIT_API ExtRelease 20 | 21 | 22 | // Global variables initialized by query. 23 | extern PDEBUG_CLIENT4 g_ExtClient; 24 | extern PDEBUG_CONTROL4 g_ExtControl; 25 | extern PDEBUG_SYMBOLS2 g_ExtSymbols; 26 | 27 | extern BOOL Connected; 28 | extern ULONG TargetMachine; 29 | 30 | HRESULT 31 | ExtQuery(PDEBUG_CLIENT4 Client); 32 | 33 | void 34 | ExtRelease(void); 35 | 36 | void __cdecl 37 | ExtOutDml(PCSTR Format, ...); 38 | 39 | #ifdef __cplusplus 40 | } 41 | #endif -------------------------------------------------------------------------------- /dllmain.cxx: -------------------------------------------------------------------------------- 1 | // dllmain.cpp : Defines the entry point for the DLL application. 2 | #include "stdafx.h" 3 | 4 | BOOL APIENTRY DllMain( HMODULE hModule, 5 | DWORD ul_reason_for_call, 6 | LPVOID lpReserved 7 | ) 8 | { 9 | switch (ul_reason_for_call) 10 | { 11 | case DLL_PROCESS_ATTACH: 12 | case DLL_THREAD_ATTACH: 13 | case DLL_THREAD_DETACH: 14 | case DLL_PROCESS_DETACH: 15 | break; 16 | } 17 | return TRUE; 18 | } 19 | 20 | -------------------------------------------------------------------------------- /filter.cxx: -------------------------------------------------------------------------------- 1 | 2 | #include "stdafx.h" 3 | #include 4 | 5 | typedef struct _SYSCALL_FILTER 6 | { 7 | LPSTR *lpFuncName; 8 | ULONGLONG nCounts; 9 | }SYSCALL_FILTER, *PSYSCALL_FILTER; 10 | 11 | BOOL gFilterListInit = FALSE; 12 | SYSCALL_FILTER gSyscallFilterList[7] = {0}; 13 | 14 | typedef enum _FILTER_LEVEL 15 | { 16 | NormalAPP = 0x0, 17 | Rs1RestrictedAppcontainer = 0x1, 18 | Rs1RestrictedAppcontainerPlugin = 0x2, 19 | FontDrvHost = 0x3, 20 | Rs1RestrictedAppcontainerMiniPlugin = 0x4, 21 | Rs3RestrictedAppcontainer = 0x5, 22 | Rs3HvsiRdpClient = 0x6, 23 | }FILTER_LEVEL; 24 | 25 | 26 | BOOL 27 | ReadString( 28 | WDBG_PTR ppstr, 29 | std::string &strRead 30 | ) 31 | { 32 | int i = 0; 33 | 34 | strRead = ""; 35 | while (1) 36 | { 37 | CHAR c; 38 | if(!ReadMemory((ULONG_PTR)ppstr + i, &c, 1, NULL)){ 39 | return FALSE; 40 | } 41 | 42 | if (!c){ 43 | break; 44 | } 45 | 46 | strRead += c; 47 | i++; 48 | } 49 | 50 | return TRUE; 51 | } 52 | 53 | EXTERN_C 54 | HRESULT CALLBACK 55 | fl(PDEBUG_CLIENT4 Client, PCSTR args) 56 | { 57 | ULONG Offset; 58 | ULONG FilterSet = 0; 59 | 60 | if (!gFilterListInit){ 61 | 62 | // 63 | // read from win32kbase 64 | // 65 | 66 | WDBG_PTR kgSharedInfo = GetExpression("win32kbase!gaWin32KSyscallList"); 67 | 68 | ReadMemory(kgSharedInfo, gSyscallFilterList, sizeof(gSyscallFilterList), NULL); 69 | 70 | gFilterListInit = TRUE; 71 | } 72 | 73 | WDBG_PTR pProcess = GetExpression("$proc"); 74 | GetFieldOffset("nt!_EPROCESS", "Win32KFilterSet", &Offset); 75 | ReadMemory(pProcess + Offset, &FilterSet, sizeof(FilterSet), NULL); 76 | dprintf("current proc filter set: %d\n", FilterSet); 77 | if (FilterSet < 0 && FilterSet > 6) { 78 | dprintf("level in [0-6]\n"); 79 | return S_OK; 80 | } 81 | //iLevel = FilterSet; 82 | 83 | 84 | if (!gSyscallFilterList[FilterSet].nCounts || !gSyscallFilterList[FilterSet].lpFuncName){ 85 | dprintf("no filter\n"); 86 | return S_OK; 87 | } 88 | 89 | 90 | dprintf("allow list:\n"); 91 | for (int i = 0; i < gSyscallFilterList[FilterSet].nCounts; i++) { 92 | std::string strRead; 93 | 94 | // 95 | // read the string pointer 96 | // 97 | 98 | ULONG_PTR pFuncNameAddr = (ULONG_PTR)gSyscallFilterList[FilterSet].lpFuncName + i * sizeof(ULONG_PTR); 99 | ULONG_PTR pFuncName; 100 | if (!ReadMemory(pFuncNameAddr, (PVOID)&pFuncName, sizeof(pFuncName), NULL)) { 101 | break; 102 | } 103 | 104 | // 105 | // read the function name 106 | // 107 | 108 | if (!ReadString((WDBG_PTR)pFuncName, strRead)){ 109 | break; 110 | } 111 | dprintf("\t\t%s\n", strRead.c_str()); 112 | } 113 | 114 | return S_OK; 115 | } -------------------------------------------------------------------------------- /gdiexts.cxx: -------------------------------------------------------------------------------- 1 | 2 | #include "stdafx.h" 3 | #include "object/region.hpp" 4 | #include "object/suface.hpp" 5 | #include "object/palette.hpp" 6 | 7 | VOID Gdidr( 8 | REGION * prgn 9 | ); 10 | 11 | VOID Gdicr( 12 | REGION * prgn 13 | ); 14 | 15 | VOID Gdidppal( 16 | PALETTE * pvServer 17 | ); 18 | 19 | VOID Gdidpsurf( 20 | PVOID pvServer 21 | ); 22 | 23 | #define DbgPrint 24 | 25 | /**************************************************************************\ 26 | * 27 | * move(dst, src ptr) 28 | * 29 | \**************************************************************************/ 30 | 31 | #define move(dst, src) \ 32 | ReadMemory((ULONG_PTR)(src), &(dst), sizeof(dst), NULL); 33 | 34 | /**************************************************************************\ 35 | * 36 | * move2(dst ptr, src ptr, num bytes) 37 | * 38 | \**************************************************************************/ 39 | #define move2(dst, src, size) \ 40 | ReadMemory((ULONG_PTR) (src), &(dst), (size), NULL); 41 | 42 | 43 | EXTERN_C 44 | HRESULT CALLBACK 45 | dr(PDEBUG_CLIENT4 Client, PCSTR args) 46 | { 47 | /*ULONG prgn; 48 | 49 | if (*args != '\0') 50 | sscanf(args, "%lx", &prgn); 51 | else 52 | { 53 | dprintf("Please supply an argument \n"); 54 | return; 55 | }*/ 56 | 57 | ULONG_PTR prgn; 58 | prgn = GetExpression(args); 59 | if (!prgn){ 60 | dprintf("Please supply an argument \n"); 61 | return S_OK; 62 | } 63 | 64 | Gdidr((REGION *)prgn); 65 | return S_OK; 66 | } 67 | 68 | 69 | /******************************Public*Routine******************************\ 70 | * VOID Gdidr ( 71 | * PVOID prgn 72 | * ) 73 | * 74 | * Debugger extension to dump a region. 75 | * 76 | * History: 77 | * 14-Feb-1992 -by- Mike Harrington [Mikehar] 78 | * Wrote it. 79 | \**************************************************************************/ 80 | 81 | VOID Gdidr( 82 | REGION * prgn 83 | ) 84 | { 85 | REGION rgn; 86 | PSCAN pscnHead; 87 | //ULONG i; 88 | BOOL bCheck = FALSE; 89 | 90 | move(rgn, prgn); 91 | //pscnHead = (PSCAN)((PBYTE)prgn + sizeof(rgn)); 92 | pscnHead = (PSCAN)((PBYTE)prgn + offsetof(REGION, scan)); 93 | 94 | dprintf( 95 | "hHmgr 0x%p\n" 96 | "cExLock 0x%p\n" 97 | "tid 0x%p\n" 98 | "sizeObj 0x%lx\n" 99 | "sizeRgn 0x%lx\n" 100 | "cRefs %ld\n" 101 | "cScans %ld\n" 102 | "rcl {0x%lx 0x%lx 0x%lx 0x%lx}\n" 103 | "pscnHead 0x%p\n" 104 | "pscnTail 0x%p\n", 105 | 106 | rgn.hHmgr, 107 | rgn.cExclusiveLock, 108 | rgn.Tid, 109 | rgn.sizeObj, 110 | rgn.sizeRgn, 111 | rgn.cRefs, 112 | rgn.cScans, 113 | rgn.rcl.left, rgn.rcl.top, rgn.rcl.right, rgn.rcl.bottom, 114 | pscnHead, 115 | rgn.pscnTail); 116 | 117 | /* 118 | * make the region data accessible. 119 | */ 120 | 121 | /* 122 | i = 0; 123 | 124 | { 125 | PSCAN pscn = (PSCAN)(prgn + 1); 126 | ULONG cscn = rgn.cScans; 127 | 128 | LONG lPrevBottom = NEG_INFINITY; 129 | LONG lPrevRight; 130 | 131 | while (cscn--) 132 | { 133 | LONG yTop; 134 | LONG yBottom; 135 | LONG cWalls; 136 | LONG cWalls2; 137 | LONG left; 138 | LONG right; 139 | ULONG iWall = 0; 140 | 141 | move(yTop, (PBYTE)pscn + offsetof(SCAN, yTop)); 142 | move(yBottom, (PBYTE)pscn + offsetof(SCAN, yBottom)); 143 | move(cWalls, (PBYTE)pscn + offsetof(SCAN, cWalls)); 144 | 145 | if (bCheck){ 146 | if (yTop < lPrevBottom) 147 | { 148 | dprintf("top < prev bottom, scan %ld, pscn @ 0x%lx\n", 149 | rgn.cScans - cscn, (BYTE *)pscn - (BYTE *)prgn); 150 | return; 151 | } 152 | 153 | if (yTop > yBottom) 154 | { 155 | dprintf("top > bottom, scan %ld, pscn @ 0x%lx\n", 156 | rgn.cScans - cscn, (BYTE *)pscn - (BYTE *)prgn); 157 | return; 158 | } 159 | } 160 | 161 | 162 | lPrevBottom = yBottom; 163 | lPrevRight = NEG_INFINITY; 164 | 165 | while ((LONG)iWall < cWalls) 166 | { 167 | move(left, (PBYTE)pscn + offsetof(SCAN, ai_x[iWall])); 168 | move(right, (PBYTE)pscn + offsetof(SCAN, ai_x[iWall + 1])); 169 | 170 | if (bCheck) { 171 | if ((left <= lPrevRight) || (right <= left)) 172 | { 173 | dprintf("left[i] < left[i+1], pscn @ 0x%lx, iWall = 0x%lx\n", 174 | (BYTE *)pscn - (BYTE *)prgn, iWall); 175 | return; 176 | } 177 | } 178 | 179 | lPrevRight = right; 180 | 181 | dprintf("\tRectangle #%d { 0x%lx, 0x%lx, 0x%lx, 0x%lx }\n", 182 | i, left, yTop, right, yBottom); 183 | 184 | ++i; 185 | 186 | iWall += 2; 187 | 188 | if (CheckControlC()) 189 | return; 190 | } 191 | 192 | move(cWalls2, (PBYTE)pscn + offsetof(SCAN, ai_x[iWall])); 193 | 194 | if (bCheck) { 195 | if (cWalls != cWalls2) 196 | { 197 | dprintf("cWalls != cWalls2 @ 0x%lx\n", 198 | (BYTE *)pscn - (BYTE *)prgn); 199 | return; 200 | } 201 | } 202 | 203 | pscn = (PSCAN)((PBYTE)pscn + (cWalls * sizeof(LONG) + sizeof(SCAN))); 204 | 205 | if ((ULONG_PTR)pscn >= (ULONG_PTR)rgn.pscnTail) 206 | { 207 | //dprintf("Went past end of region\n"); 208 | return; 209 | } 210 | } 211 | }*/ 212 | 213 | ULONG cScans = rgn.cScans; 214 | PSCAN pscn = pscnHead; 215 | 216 | if (cScans){ 217 | dprintf("------------dump cScan------------\n"); 218 | } 219 | 220 | for (ULONG i = 0; 221 | i < cScans; 222 | i++) 223 | { 224 | ULONG cWalls; 225 | LONG yTop; 226 | LONG yBottom; 227 | LONG left; 228 | LONG right; 229 | 230 | move(yTop, (PBYTE)pscn + offsetof(SCAN, yTop)); 231 | move(yBottom, (PBYTE)pscn + offsetof(SCAN, yBottom)); 232 | move(cWalls, (PBYTE)pscn + offsetof(SCAN, cWalls)); 233 | 234 | 235 | if (!cWalls){ 236 | dprintf("\tScan #%d: yTop = 0x%lx, yBottom = 0x%lx, cWalls = %ld\n", 237 | i, yTop, yBottom, cWalls); 238 | }else{ 239 | for (ULONG j = 0; 240 | j < cWalls; 241 | j += 2) 242 | { 243 | move(left, (PBYTE)pscn + offsetof(SCAN, ai_x[j])); 244 | move(right, (PBYTE)pscn + offsetof(SCAN, ai_x[j + 1])); 245 | 246 | dprintf("\tScan #%d: { 0x%lx, 0x%lx, 0x%lx, 0x%lx }\n", 247 | i, left, yTop, right, yBottom); 248 | } 249 | } 250 | 251 | //pscn = pscnGet(pscn); 252 | pscn = (PSCAN)((PBYTE)pscn + (cWalls * sizeof(LONG) + sizeof(SCAN))); 253 | } 254 | } 255 | 256 | 257 | EXTERN_C 258 | HRESULT CALLBACK 259 | cr(PDEBUG_CLIENT4 Client, PCSTR args) 260 | { 261 | ULONG_PTR prgn; 262 | prgn = GetExpression(args); 263 | if (!prgn) { 264 | dprintf("Please supply an argument \n"); 265 | return S_OK; 266 | } 267 | 268 | Gdicr((REGION *)prgn); 269 | return S_OK; 270 | } 271 | 272 | 273 | VOID Gdicr( 274 | REGION * prgn 275 | ) 276 | { 277 | REGION rgn; 278 | PSCAN pscnHead; 279 | BOOL bCheck = TRUE; 280 | 281 | move(rgn, prgn); 282 | pscnHead = (PSCAN)((PBYTE)prgn + sizeof(rgn)); 283 | 284 | dprintf("pr = %lx, sizeof(rgn) = %lx, pscnHead = %lx\n", prgn, sizeof(rgn), pscnHead); 285 | 286 | dprintf( 287 | "hHmgr 0x%p\n" 288 | "cExLock 0x%p\n" 289 | "tid 0x%p\n" 290 | "sizeObj 0x%lx\n" 291 | "sizeRgn 0x%lx\n" 292 | "cRefs %ld\n" 293 | "cScans %ld\n" 294 | "rcl {0x%lx 0x%lx 0x%lx 0x%lx}\n" 295 | "pscnHead 0x%p\n" 296 | "pscnTail 0x%p\n", 297 | 298 | rgn.hHmgr, 299 | rgn.cExclusiveLock, 300 | rgn.Tid, 301 | rgn.sizeObj, 302 | rgn.sizeRgn, 303 | rgn.cRefs, 304 | rgn.cScans, 305 | rgn.rcl.left, rgn.rcl.top, rgn.rcl.right, rgn.rcl.bottom, 306 | pscnHead, 307 | rgn.pscnTail); 308 | 309 | if ((pscnHead > rgn.pscnTail) || 310 | (rgn.sizeObj < rgn.sizeRgn)) 311 | { 312 | DbgPrint("Error in region\n"); 313 | return; 314 | } 315 | 316 | /* 317 | * make the region data accessable. 318 | */ 319 | 320 | { 321 | PSCAN pscn = (PSCAN)((REGION *)prgn + 1); 322 | ULONG cscn = rgn.cScans; 323 | 324 | LONG lPrevBottom = NEG_INFINITY; 325 | LONG lPrevRight; 326 | 327 | while (cscn--) 328 | { 329 | LONG yTop; 330 | LONG yBottom; 331 | LONG cWalls; 332 | LONG cWalls2; 333 | LONG left; 334 | LONG right; 335 | ULONG iWall = 0; 336 | 337 | move(yTop, (PBYTE)pscn + offsetof(SCAN, yTop)); 338 | move(yBottom, (PBYTE)pscn + offsetof(SCAN, yBottom)); 339 | move(cWalls, (PBYTE)pscn + offsetof(SCAN, cWalls)); 340 | 341 | if (yTop < lPrevBottom) 342 | { 343 | dprintf("top(0x%lx) < prev bottom(0x%lx), scan %ld, pscn @ 0x%lx\n", 344 | yTop, lPrevBottom, rgn.cScans - cscn, (BYTE *)pscn - (BYTE *)prgn); 345 | bCheck = FALSE; 346 | break; 347 | } 348 | 349 | if (yTop > yBottom) 350 | { 351 | dprintf("top(0x%lx) > bottom(0x%lx), scan %ld, pscn @ 0x%lx\n", 352 | yTop, yBottom, rgn.cScans - cscn, (BYTE *)pscn - (BYTE *)prgn); 353 | bCheck = FALSE; 354 | break; 355 | } 356 | 357 | lPrevBottom = yBottom; 358 | lPrevRight = NEG_INFINITY; 359 | 360 | while ((LONG)iWall < cWalls) 361 | { 362 | move(left, (PBYTE)pscn + offsetof(SCAN, ai_x[iWall])); 363 | move(right, (PBYTE)pscn + offsetof(SCAN, ai_x[iWall + 1])); 364 | 365 | if ((left <= lPrevRight) || (right <= left)) 366 | { 367 | dprintf("left[i] < left[i+1], pscn @ 0x%lx, iWall = 0x%lx\n", 368 | (BYTE *)pscn - (BYTE *)prgn, iWall); 369 | bCheck = FALSE; 370 | break; 371 | } 372 | 373 | lPrevRight = right; 374 | 375 | iWall += 2; 376 | 377 | if (CheckControlC()) 378 | return; 379 | } 380 | if (!bCheck){ 381 | break; 382 | } 383 | 384 | move(cWalls2, (PBYTE)pscn + offsetof(SCAN, ai_x[iWall])); 385 | 386 | if (cWalls != cWalls2) 387 | { 388 | dprintf("cWalls != cWalls2 @ 0x%lx\n", 389 | (BYTE *)pscn - (BYTE *)prgn); 390 | bCheck = FALSE; 391 | break; 392 | } 393 | 394 | pscn = (PSCAN)((PBYTE)pscn + (cWalls * sizeof(LONG) + sizeof(SCAN))); 395 | 396 | if ((ULONG_PTR)pscn >= (ULONG_PTR)rgn.pscnTail) 397 | { 398 | //dprintf("Went past end of region\n"); 399 | break; 400 | } 401 | } 402 | } 403 | 404 | if (!bCheck){ 405 | dprintf("region check failed\n"); 406 | }else{ 407 | dprintf("region check success\n"); 408 | } 409 | 410 | } 411 | 412 | PSZ gapszBMF[] = 413 | { 414 | "BMF_ERROR", 415 | "BMF_1BPP", 416 | "BMF_4BPP", 417 | "BMF_8BPP", 418 | "BMF_16BPP", 419 | "BMF_24BPP", 420 | "BMF_32BPP", 421 | "BMF_4RLE", 422 | "BMF_8RLE" 423 | }; 424 | 425 | PSZ gapszSTYPE[] = 426 | { 427 | "STYPE_BITMAP", 428 | "STYPE_DEVICE", 429 | "Unused", 430 | "STYPE_DEVBITMAP", 431 | }; 432 | 433 | ULONG ulSizeSURFACE() 434 | { 435 | ULONG surfSize; 436 | move(surfSize, GetExpression("win32kbase!SURFACE::tSize")); 437 | if (surfSize != sizeof(SURFACE)){ 438 | DEBUGPRINT("!!!WARNING!!!surface struct has been changed(our=(0x%x, system=0x%x) please check it." \ 439 | "and the information may be wrong\n", 440 | sizeof(SURFACE), surfSize); 441 | } 442 | return sizeof(SURFACE); 443 | } 444 | 445 | VOID vPrintSURFACE(PVOID pv) 446 | { 447 | SURFACE *pso = (SURFACE *)pv; 448 | 449 | // 450 | // SURFACE structure 451 | // 452 | 453 | dprintf("--------------------------------------------------\n"); 454 | dprintf("DHSURF dhsurf 0x%p\n", pso->so.dhsurf); 455 | dprintf("HSURF hsurf 0x%p\n", pso->so.hsurf); 456 | dprintf("DHPDEV dhpdev 0x%p\n", pso->so.dhpdev); 457 | dprintf("HDEV hdev 0x%p\n", pso->so.hdev); 458 | dprintf("SIZEL sizlBitmap.cx 0x%lx\n", pso->so.sizlBitmap.cx); 459 | dprintf("SIZEL sizlBitmap.cy 0x%lx\n", pso->so.sizlBitmap.cy); 460 | dprintf("ULONG cjBits 0x%lx\n", pso->so.cjBits); 461 | dprintf("PVOID pvBits 0x%p\n", pso->so.pvBits); 462 | dprintf("PVOID pvScan0 0x%p\n", pso->so.pvScan0); 463 | dprintf("LONG lDelta 0x%lx\n", pso->so.lDelta); 464 | dprintf("ULONG iUniq 0x%lx\n", pso->so.iUniq); 465 | dprintf("ULONG iBitmapFormat 0x%lx, %s\n", pso->so.iBitmapFormat, 466 | pso->so.iBitmapFormat > BMF_8RLE ? "ERROR" : gapszBMF[pso->so.iBitmapFormat]); 467 | 468 | #if 0 469 | if (pso->DIB.hDIBSection) 470 | { 471 | dprintf("USHORT iType DIBSECTION\n"); 472 | 473 | dprintf("HANDLE hDIBSection 0x%lx\n", pso->DIB.hDIBSection); 474 | dprintf("HANDLE hSecure 0x%lx\n", pso->DIB.hSecure); 475 | dprintf("DWORD dwOffset 0x%lx\n", pso->DIB.dwOffset); 476 | }else{ 477 | dprintf("USHORT iType 0x%x, %s\n", pso->so.iType, 478 | pso->so.iType > STYPE_DEVBITMAP ? "ERROR" : gapszSTYPE[pso->so.iType]); 479 | } 480 | #endif 481 | 482 | dprintf("USHORT iType 0x%x, %s\n", pso->so.iType, 483 | pso->so.iType > STYPE_DEVBITMAP ? "ERROR" : gapszSTYPE[pso->so.iType]); 484 | 485 | 486 | dprintf("USHORT fjBitmap 0x%x\n", pso->so.fjBitmap); 487 | 488 | #if 0 489 | dprintf("XDCOBJ* pdcoAA 0x%ln\n", pso->pdcoAA); 490 | dprintf("FLONG flags 0x%lx\n", pso->SurfFlags); 491 | #endif 492 | dprintf("PPALETTE ppal 0x%p\n", pso->pPal); 493 | 494 | #if 0 495 | dprintf("PFN_DrvBitBlt pfnBitBlt 0x%lx\n", pso->pFnBitBlt); 496 | dprintf("PFN_DrvTextOut pfnTextOut 0x%lx\n", pso->pFnTextOut); 497 | #endif 498 | 499 | #if 0 500 | if ((pso->so.iType == STYPE_BITMAP) || 501 | (pso->so.iType == STYPE_DEVBITMAP)){ 502 | 503 | dprintf("HDC hdc 0x%lx\n", pso->EBitmap.hdc); 504 | dprintf("ULONG cRef 0x%lx\n", pso->EBitmap.cRef); 505 | dprintf("HPALETTE hpalHint 0x%lx\n", pso->EBitmap.hpalHint); 506 | } 507 | 508 | if (pso->flags & PDEV_SURFACE) 509 | { 510 | dprintf("This is the enabled surface for a PDEV\n"); 511 | } 512 | 513 | #endif 514 | 515 | dprintf("--------------------------------------------------\n"); 516 | } 517 | 518 | VOID Gdidpsurf( 519 | PVOID pvServer) 520 | { 521 | char pso[1024]; 522 | 523 | move2(pso, pvServer, ulSizeSURFACE()); 524 | 525 | dprintf("SURFACE structure at 0x%p:\n", pvServer); 526 | vPrintSURFACE((PVOID)pso); 527 | } 528 | 529 | 530 | EXTERN_C 531 | HRESULT CALLBACK 532 | dpsurf(PDEBUG_CLIENT4 Client, PCSTR args) 533 | { 534 | ULONG_PTR psurf; 535 | psurf = GetExpression(args); 536 | if (!psurf) { 537 | dprintf("Please supply an argument \n"); 538 | return S_OK; 539 | } 540 | 541 | Gdidpsurf((PVOID *)psurf); 542 | return S_OK; 543 | } 544 | 545 | VOID Gdidpso( 546 | PVOID pvServer) 547 | { 548 | char pso[1024]; 549 | 550 | // 551 | // subtract offset of BASE OBJECT FROM SURFOBJ 552 | // 553 | 554 | pvServer = (PVOID)((PUCHAR)pvServer - /*sizeof(BASEOBJECT)*/0x18); 555 | 556 | move2(pso, pvServer, ulSizeSURFACE()); 557 | 558 | dprintf("SURFACE structure at 0x%p:\n", pvServer); 559 | vPrintSURFACE((PVOID)pso); 560 | } 561 | 562 | 563 | EXTERN_C 564 | HRESULT CALLBACK 565 | dpso(PDEBUG_CLIENT4 Client, PCSTR args) 566 | { 567 | ULONG_PTR pso; 568 | pso = GetExpression(args); 569 | if (!pso) { 570 | dprintf("Please supply an argument \n"); 571 | return S_OK; 572 | } 573 | 574 | Gdidpso((PVOID *)pso); 575 | return S_OK; 576 | } 577 | 578 | 579 | ULONG ulSizePALETTE() 580 | { 581 | return((ULONG) sizeof(PALETTE)); 582 | } 583 | 584 | VOID 585 | vPrintPALETTE( 586 | PALETTE * pvServer 587 | ) 588 | { 589 | PALETTE *ppal; 590 | char pso[1024]; 591 | 592 | move2(pso, pvServer, ulSizePALETTE()); 593 | 594 | dprintf("EPALOBJ structure at 0x%p:\n", pvServer); 595 | 596 | ppal = (PALETTE *)pso; 597 | 598 | dprintf("--------------------------------------------------\n"); 599 | dprintf("FLONG flPal 0x%lx\n", ppal->flPal); 600 | dprintf("ULONG cEntries 0x%lx\n", ppal->cEntries); 601 | dprintf("ULONG ulTime 0x%lx\n", ppal->ulTime); 602 | dprintf("HDC hdcHead 0x%p\n", ppal->hdcHead); 603 | dprintf("HDEVPPAL hSelected 0x%p\n", ppal->hSelected.hdev); 604 | dprintf("ULONG cRefhpal 0x%lx\n", ppal->cRefhpal); 605 | dprintf("ULONG cRefRegular 0x%lx\n", ppal->cRefRegular); 606 | dprintf("PTRANSLATE ptransFore 0x%p\n", ppal->ptransFore); 607 | dprintf("PTRANSLATE ptransCurrent 0x%p\n", ppal->ptransCurrent); 608 | dprintf("PTRANSLATE ptransOld 0x%p\n", ppal->ptransOld); 609 | 610 | // For DirectDraw surfaces, sometimes the colour table is shared 611 | // with another palette: 612 | 613 | if (ppal->ppalColor != ppal) 614 | { 615 | dprintf("PPALETTE ppalColor 0x%p\n", ppal->ppalColor); 616 | } 617 | 618 | dprintf("PAL_ULONG apalColor 0x%p\n", ppal->apalColor); 619 | dprintf("--------------------------------------------------\n"); 620 | } 621 | 622 | 623 | EXTERN_C 624 | HRESULT CALLBACK 625 | dppal(PDEBUG_CLIENT4 Client, PCSTR args) 626 | { 627 | PALETTE* ppal; 628 | ppal = (PALETTE*)GetExpression(args); 629 | if (!ppal) { 630 | dprintf("Please supply an argument \n"); 631 | return S_OK; 632 | } 633 | 634 | Gdidppal(ppal); 635 | return S_OK; 636 | } 637 | 638 | void Gdidppal( 639 | PALETTE * pvServer) 640 | { 641 | vPrintPALETTE(pvServer); 642 | } -------------------------------------------------------------------------------- /handle.cxx: -------------------------------------------------------------------------------- 1 | 2 | #include "stdafx.h" 3 | #include "dbgext.hpp" 4 | #include "handle.hpp" 5 | 6 | #include "utils.hpp" 7 | #include "SimpleOpt.h" 8 | 9 | 10 | typedef struct _TYPE_DESC 11 | { 12 | int Type; 13 | LPCSTR lpszTypeDesc; 14 | }TYPE_DESC, *PTYPE_DESC; 15 | 16 | TYPE_DESC gGdiTypeDesc[] = 17 | { 18 | { 0x01, "DC" }, 19 | { 0x02, "ColorTransform" }, 20 | { 0x04, "Rgn" }, 21 | { 0x05, "Bitmap" }, 22 | { 0x07, "Path" }, 23 | { 0x08, "Palette" }, 24 | { 0x09, "ColorSpace" }, 25 | { 0x0a, "Font" }, 26 | { 0x0e, "ColorTransform" }, 27 | { 0x0f, "Sprite" }, 28 | { 0x10, "Brush" }, 29 | { 0x12, "LogicSurface" }, 30 | { 0x13, "Space" }, 31 | { 0x15, "ServerMetafile" }, 32 | { 0x1c, "Driver" }, 33 | { 0x8a, "Font2" }, 34 | }; 35 | 36 | TYPE_DESC gUserTypeDesc[] = 37 | { 38 | { 0x01, "Window" }, //0x190 39 | { 0x02, "Menu" }, //0xb0 40 | { 0x03, "Cursor" }, //0x98 41 | { 0x04, "DeferWindowPos" }, //0x30 42 | { 0x05, "WindowHook" }, //0x60 43 | { 0x06, "MemoryHandle" }, 44 | { 0x07, "CPD" }, //0x48 45 | { 0x08, "AcceleratorTable" }, 46 | { 0x09, "CsDde" }, //0x40 47 | { 0x0a, "Conversation" }, //0x60 48 | { 0x0b, "pxs" }, //0x48 49 | { 0x0c, "Monitor" }, //0x260 50 | { 0x0d, "Keyboard" }, //0x78 51 | { 0x0e, "KeyboardLayout" }, //0x78 52 | { 0x0f, "EventHook" }, //0x60 53 | { 0x10, "Timer" }, //0x88 54 | { 0x11, "InputContext" }, //0x48 55 | { 0x12, "HidData" }, 56 | { 0x14, "TouchInputInfo" }, 57 | { 0x15, "GestureInfo" }, 58 | { 0x17, "BaseWindow" } //0x80 59 | }; 60 | 61 | 62 | #define HMINDEXBITS 0x0000FFFF // bits where index is stored 63 | #define HMUNIQSHIFT 16 // bits to shift uniqueness 64 | #define HMUNIQBITS 0xFFFF // valid uniqueness bits 65 | #define HMIndexFromHandle(h) (((DWORD)h) & HMINDEXBITS) 66 | #define HMUniqFromHandle(h) ((WORD)((((DWORD)h) >> HMUNIQSHIFT) & HMUNIQBITS)) 67 | 68 | #define OCF_THREADOWNED 0x0001 69 | #define OCF_PROCESSOWNED 0x0002 70 | #define OCF_MARKTHREAD 0x0004 71 | #define OCF_USEQUOTA 0x0008 72 | 73 | const LPCSTR gTypeUnknonw = "Unknown"; 74 | 75 | LPCSTR 76 | GetUserTypeDesc( 77 | IN UCHAR Type 78 | ) 79 | { 80 | LPCSTR lpTypeDesc = gTypeUnknonw; 81 | for (int i = 0; i < _countof(gUserTypeDesc); i++) { 82 | if (gUserTypeDesc[i].Type == Type) { 83 | lpTypeDesc = gUserTypeDesc[i].lpszTypeDesc; 84 | break; 85 | } 86 | } 87 | return lpTypeDesc; 88 | } 89 | 90 | BOOL 91 | IsUserTypeValid( 92 | IN UCHAR Type 93 | ) 94 | { 95 | for (int i = 0; i < _countof(gGdiTypeDesc); i++) { 96 | if (gUserTypeDesc[i].Type == Type) { 97 | return TRUE; 98 | } 99 | } 100 | return FALSE; 101 | } 102 | 103 | BOOL 104 | UserObjectGetEntry( 105 | IN UINT32 Index, 106 | OUT PHANDLEENTRY pphe, 107 | OUT PKERNEL_HANDLEENTRY pKrlEntry, 108 | OPTIONAL OUT PUSER_TYPE_DESC pCreateInfo 109 | ); 110 | 111 | EXTERN_C 112 | HRESULT 113 | CALLBACK 114 | uh(PDEBUG_CLIENT4 Client, PCSTR args) 115 | { 116 | ULONG64 hUser; 117 | hUser = GetExpression(args); 118 | 119 | ULONG Index = HMIndexFromHandle(hUser); 120 | WORD wUniq = HMUniqFromHandle(hUser); 121 | 122 | HANDLEENTRY HandleEntry; 123 | KERNEL_HANDLEENTRY KernelHandleEntry; 124 | USER_TYPE_DESC CreateInfo; 125 | 126 | INIT_API(); 127 | 128 | do 129 | { 130 | BOOL bRet = UserObjectGetEntry(Index, &HandleEntry, &KernelHandleEntry, &CreateInfo); 131 | if (!bRet){ 132 | break; 133 | } 134 | 135 | if (KernelHandleEntry.pOwner) { 136 | 137 | ULONG_PTR Handle; 138 | LPCSTR lpszOwnerType = "Unknown"; 139 | ULONG_PTR pObject = NULL; 140 | 141 | // 142 | // read the handle from object 143 | // the user object always start with "HEAD" 144 | // 145 | 146 | if (!ReadPointer((ULONG64)KernelHandleEntry.pObject, &Handle)) { 147 | dprintf("can not handle at 0x%p\n", KernelHandleEntry.pObject); 148 | break; 149 | } 150 | 151 | if (CreateInfo.CreateFlag & OCF_PROCESSOWNED) { 152 | 153 | // 154 | // the owner is PROCESSINFO 155 | // W32PROCESS 156 | // 157 | 158 | lpszOwnerType = "Process"; 159 | 160 | if (!ReadPointer((ULONG64)KernelHandleEntry.pOwner, &pObject)) { 161 | dprintf("can not handle at 0x%p\n", KernelHandleEntry.pOwner); 162 | break; 163 | } 164 | 165 | }else if (CreateInfo.CreateFlag & OCF_THREADOWNED) { 166 | 167 | // 168 | // The Owner is THREADINFO 169 | // W32THREAD 170 | // 171 | 172 | ULONG_PTR pWin32Thread; 173 | lpszOwnerType = "Thread"; 174 | 175 | if (!ReadPointer((ULONG64)KernelHandleEntry.pOwner, &pWin32Thread)) { 176 | dprintf("can not handle at 0x%p\n", KernelHandleEntry.pOwner); 177 | break; 178 | } 179 | 180 | // 181 | // So here is _ETHREAD 182 | // 183 | 184 | if (GetFieldValue(pWin32Thread, "nt!_KTHREAD", "Process", pObject)) { 185 | dprintf("can not process at thread 0x%p\n", pWin32Thread); 186 | break; 187 | } 188 | }else{ 189 | 190 | } 191 | 192 | ExtOutDml( 193 | "Object Handle 0x%llx\n" 194 | "Object Type %s(%d)\n" 195 | "Create Flag 0x%x\n" 196 | "Object 0x%p\n" 197 | "pOwner 0x%p\n" 198 | "process 0x%p\n", 199 | hUser, 200 | GetUserTypeDesc(HandleEntry.Type), (ULONG)HandleEntry.Type, 201 | CreateInfo.CreateFlag, 202 | KernelHandleEntry.pObject, 203 | KernelHandleEntry.pOwner, 204 | pObject, pObject 205 | ); 206 | } 207 | } while (FALSE); 208 | 209 | EXIT_API(); 210 | 211 | return S_OK; 212 | } 213 | 214 | LPCSTR 215 | GetGdiTypeDesc( 216 | IN UCHAR Type 217 | ) 218 | { 219 | LPCSTR lpTypeDesc = gTypeUnknonw; 220 | for (int i = 0; i < _countof(gGdiTypeDesc); i++) { 221 | if (gGdiTypeDesc[i].Type == Type) { 222 | lpTypeDesc = gGdiTypeDesc[i].lpszTypeDesc; 223 | break; 224 | } 225 | } 226 | return lpTypeDesc; 227 | } 228 | 229 | BOOL 230 | IsGdiTypeValid( 231 | IN UCHAR Type 232 | ) 233 | { 234 | for (int i = 0; i < _countof(gGdiTypeDesc); i++) { 235 | if (gGdiTypeDesc[i].Type == Type) { 236 | return TRUE; 237 | } 238 | } 239 | return FALSE; 240 | } 241 | 242 | BOOL 243 | DirectoryGetEntry( 244 | IN UINT32 Index, 245 | OUT PVOID* ppEntry, 246 | OUT PVOID* pLookupEntry 247 | ) 248 | { 249 | ULONG uReturn; 250 | BOOL bRet; 251 | 252 | HANDLEMGR HandleMgr; 253 | WDBG_PTR kHandleMgr = GetExpression("poi(win32kbase!gpHandleManager)"); 254 | 255 | if (ppEntry) { 256 | *ppEntry = NULL; 257 | } 258 | 259 | if (pLookupEntry) { 260 | *pLookupEntry = NULL; 261 | } 262 | 263 | // 264 | // get entire handle mgr 265 | // 266 | 267 | bRet = ReadMemory(kHandleMgr, &HandleMgr, sizeof(HandleMgr), &uReturn); 268 | if (!bRet) { 269 | dprintf("can not read win32kbase!gpHandleManager at %p\n", kHandleMgr); 270 | return FALSE; 271 | } 272 | 273 | if (HandleMgr.TotalGdiHandleCounts <= 0x10000){ 274 | Index = (USHORT)Index; 275 | } 276 | 277 | // 278 | // read handle directory 279 | // 280 | 281 | HANDLEENTRYDIR HandleDir; 282 | bRet = ReadMemory((WDBG_PTR)(ULONG_PTR)(HandleMgr.pHandleEntryDir), &HandleDir, sizeof(HandleDir), &uReturn); 283 | if (!bRet) { 284 | dprintf("can not read handle directory at %p\n", HandleMgr.pHandleEntryDir); 285 | return FALSE; 286 | } 287 | 288 | 289 | // 290 | // GdiHandleEntryDirectory::_RetrieveTableAndTableEntryIndex 291 | // calculate the directory index and handle index 292 | // 293 | 294 | ULONG MaxDirGdiHandleCount = HandleDir.MaxDirGdiHandleCount; 295 | if (Index > (MaxDirGdiHandleCount + (UINT32)((HandleDir.DirCounts - 1) << 16))) { 296 | return FALSE; 297 | } 298 | 299 | UINT32 EntryIndex = Index; 300 | UINT32 DirIndex = 0; 301 | if (Index >= MaxDirGdiHandleCount) { 302 | DirIndex = ((Index - MaxDirGdiHandleCount) >> 16) + 1; 303 | } 304 | 305 | // 306 | // Read EntryTable 307 | // 308 | 309 | PVOID pEntryTableAddr = (PVOID)HandleDir.pEntryTable[DirIndex]; 310 | HANDLEENTRYTABLE HandleEntryTable; 311 | bRet = ReadMemory((WDBG_PTR)pEntryTableAddr, &HandleEntryTable, sizeof(HandleEntryTable), &uReturn); 312 | if (!bRet) { 313 | dprintf("can not read win32kbase!gpHandleManager at %p\n", kHandleMgr); 314 | return FALSE; 315 | } 316 | 317 | // 318 | // Calculate Index of EntryTable 319 | // Here dirIndex is less than ushort_max 320 | // 321 | // the meaning of the EntryIndex: 322 | // 323 | // +--------+--------+ 324 | // | lookup | index | 325 | // +--------+--------+ 326 | // 16 8 0 327 | // 328 | 329 | if (DirIndex) { 330 | EntryIndex = Index - MaxDirGdiHandleCount - ((DirIndex - 1) << 16); 331 | } 332 | 333 | // 334 | // Read lookup table 335 | // 336 | 337 | ENTRYDATALOOKUPTABLE EntryLookupTable; 338 | bRet = ReadMemory((WDBG_PTR)HandleEntryTable.pEntryDataLookupTable, &EntryLookupTable, sizeof(EntryLookupTable), &uReturn); 339 | if (!bRet) { 340 | dprintf("can not read pEntryDataLookupTable at %p\n", HandleEntryTable.pEntryDataLookupTable); 341 | return FALSE; 342 | } 343 | 344 | // 345 | // Read lookup table entry 346 | // 347 | 348 | PLOOKUP_ENTRY pLookEntris; 349 | bRet = ReadMemory((WDBG_PTR)& EntryLookupTable.ppLookupEntries[EntryIndex >> 8], 350 | &pLookEntris, sizeof(pLookEntris), &uReturn); 351 | if (!bRet) { 352 | dprintf("can not read lookup Table Ptr at %p\n", EntryLookupTable.ppLookupEntries); 353 | return FALSE; 354 | } 355 | 356 | if (!pLookEntris){ 357 | dprintf("lookup entry Table is null\n"); 358 | return FALSE; 359 | } 360 | 361 | if (pLookupEntry) { 362 | *pLookupEntry = &pLookEntris[(UCHAR)EntryIndex]; 363 | } 364 | 365 | // 366 | // read Handle Entry 367 | // 368 | 369 | if (ppEntry) { 370 | *ppEntry = &HandleEntryTable.pEntries[EntryIndex]; 371 | } 372 | 373 | return TRUE; 374 | 375 | } 376 | 377 | EXTERN_C 378 | HRESULT 379 | CALLBACK 380 | gh(PDEBUG_CLIENT4 Client, PCSTR args) 381 | { 382 | ULONG uReturn; 383 | BOOL bRet; 384 | 385 | ULONG64 hGdi; 386 | hGdi = GetExpression(args) & 0xffffffff; 387 | 388 | // 389 | // first check the handle is valid 390 | // 391 | 392 | UCHAR Type = (hGdi >> 16) & 0xFF; 393 | ULONG Handle = (ULONG)hGdi; 394 | 395 | // 396 | // format handle. remove the type info and 397 | // move directory index 398 | // 399 | 400 | /* 401 | +--------+--------+--------+--------+ 402 | | dir | type | lookup | index | 403 | +--------+--------+--------+--------+ 404 | 32 24 16 8 0 405 | */ 406 | 407 | Handle = (USHORT)Handle | ((Handle >> 8) & 0xFF0000); 408 | 409 | PVOID pEntry = NULL; 410 | PVOID pLookupEntry = NULL; 411 | if (DirectoryGetEntry(Handle, &pEntry, &pLookupEntry)) { 412 | 413 | // 414 | // Read the handle entry 415 | // 416 | 417 | ENTRY entry; 418 | LOOKUP_ENTRY LookupEntry; 419 | 420 | bRet = ReadMemory((WDBG_PTR)pLookupEntry, &LookupEntry, sizeof(LookupEntry), &uReturn); 421 | if (!bRet) { 422 | dprintf("can not read handle entry at %p\n", pEntry); 423 | return S_OK; 424 | } 425 | 426 | bRet = ReadMemory((WDBG_PTR)pEntry, &entry, sizeof(entry), &uReturn); 427 | if (!bRet) { 428 | dprintf("can not read handle entry at %p\n", pEntry); 429 | return S_OK; 430 | } 431 | 432 | if (entry.DirOrType != (USHORT)(hGdi >> 16)) { 433 | dprintf("!Handle Type mismatch please check it\n"); 434 | } 435 | 436 | dprintf( 437 | "Object Type %s(%d)\n" 438 | "Handle 0x%llx\n" 439 | "Object 0x%p\n" 440 | "entry 0x%p\n" 441 | "processx 0x%x\n", 442 | GetGdiTypeDesc(Type), Type, 443 | hGdi, 444 | LookupEntry.pObject, 445 | pEntry, entry.ProcessIdOrSome 446 | ); 447 | }else{ 448 | dprintf("invalid handle value\n"); 449 | } 450 | return S_OK; 451 | } 452 | 453 | ULONG 454 | GetTotalHandleCounts() 455 | { 456 | BOOL bRet; 457 | ULONG uReturn; 458 | HANDLEMGR HandleMgr; 459 | WDBG_PTR kHandleMgr = GetExpression("poi(win32kbase!gpHandleManager)"); 460 | 461 | 462 | // 463 | // get entire handle mgr 464 | // 465 | 466 | bRet = ReadMemory(kHandleMgr, &HandleMgr, sizeof(HandleMgr), &uReturn); 467 | if (!bRet) { 468 | dprintf("can not read win32kbase!gpHandleManager at %p\n", kHandleMgr); 469 | return 0; 470 | } 471 | 472 | return HandleMgr.TotalGdiHandleCounts; 473 | } 474 | 475 | typedef struct _FILTER_CONTEXT 476 | { 477 | DWORD Flags; 478 | PVOID Process; 479 | int Type; 480 | }FILTER_CONTEXT, * PFILTER_CONTEXT; 481 | 482 | enum { 483 | OPT_HELP, 484 | OPT_FL_PROC = 1 << 0, 485 | OPT_FL_TYPE = 1 << 1 486 | }; 487 | CSimpleOptA::SOption g_Options[] = 488 | { 489 | { OPT_HELP, "-h", SO_NONE }, 490 | { OPT_HELP, "-?", SO_NONE }, 491 | { OPT_HELP, "--help", SO_NONE }, 492 | { OPT_FL_PROC, "-p", SO_REQ_SEP }, 493 | { OPT_FL_TYPE, "-t", SO_REQ_SEP }, 494 | SO_END_OF_OPTIONS 495 | }; 496 | 497 | VOID 498 | ShowDumpGdiHandleHelp() 499 | { 500 | dprintf("Usage: !dgh [args]\n"); 501 | dprintf("\n"); 502 | dprintf("args list:\n"); 503 | dprintf("-p [process] filter object by process\n"); 504 | dprintf("-t [type id] filter object by type id\n"); 505 | dprintf(" valid type:\n"); 506 | 507 | LPCSTR lpTypeDesc = gTypeUnknonw; 508 | for (int i = 0; i < _countof(gGdiTypeDesc); i++) { 509 | dprintf(" id:%d - %s\n", gGdiTypeDesc[i].Type, gGdiTypeDesc[i].lpszTypeDesc); 510 | } 511 | 512 | dprintf("example:\n"); 513 | dprintf("!dgh\n"); 514 | dprintf(" will dump gdi object in system\n"); 515 | dprintf("!dgh -p 0xffffffff13450080\n"); 516 | dprintf(" will dump gdi object create by process 0xffffffff13450080\n"); 517 | dprintf("!dgh -t 5\n"); 518 | dprintf(" will dump all bitmap object\n"); 519 | dprintf("!dgh -t 1 -p 0xffffffff13450080\n"); 520 | dprintf(" will dump all bitmap object create by process 0xffffffff13450080\n"); 521 | } 522 | 523 | 524 | BOOL 525 | DumpGdiHandle( 526 | IN PFILTER_CONTEXT Context 527 | ) 528 | { 529 | /* 530 | +--------+--------+--------+--------+ 531 | | dir | type | lookup | index | 532 | +--------+--------+--------+--------+ 533 | 32 24 16 8 0 534 | */ 535 | 536 | UINT32 TotalHandleCounts; 537 | UINT32 HandleV; 538 | 539 | // 540 | // Check type 541 | // 542 | 543 | if (Context->Flags & OPT_FL_TYPE) { 544 | if (!IsGdiTypeValid(Context->Type)) { 545 | dprintf("Invalid Gdi object type\n"); 546 | return FALSE; 547 | } 548 | } 549 | 550 | TotalHandleCounts = GetTotalHandleCounts(); 551 | 552 | if (!TotalHandleCounts) { 553 | dprintf("Have none gdi handle\n"); 554 | return S_OK; 555 | } 556 | 557 | for (HandleV = 1; HandleV < TotalHandleCounts; HandleV++) 558 | { 559 | 560 | // 561 | // Get handle entry 562 | // 563 | 564 | PVOID pEntry = NULL; 565 | PVOID pLookupEntry = NULL; 566 | if (DirectoryGetEntry(HandleV, &pEntry, &pLookupEntry)) { 567 | 568 | // 569 | // Read the handle entry 570 | // 571 | 572 | BOOL bRet; 573 | ENTRY entry; 574 | LOOKUP_ENTRY LookupEntry; 575 | ULONG uReturn; 576 | 577 | bRet = ReadMemory((WDBG_PTR)pLookupEntry, &LookupEntry, sizeof(LookupEntry), &uReturn); 578 | if (!bRet) { 579 | dprintf("can not read handle entry at %p\n", pEntry); 580 | continue; 581 | } 582 | 583 | bRet = ReadMemory((WDBG_PTR)pEntry, &entry, sizeof(entry), &uReturn); 584 | if (!bRet) { 585 | dprintf("can not read handle entry at %p\n", pEntry); 586 | continue; 587 | } 588 | 589 | 590 | // 591 | // Is object exist? 592 | // 593 | 594 | if (LookupEntry.pObject) { 595 | 596 | // 597 | // combine handle 598 | // 599 | 600 | ULONG Handle; 601 | 602 | Handle = (ULONG)entry.DirOrType << 16 | (USHORT)HandleV; 603 | 604 | if ((Context->Flags & OPT_FL_TYPE && Context->Type != entry.Type)){ 605 | continue; 606 | } 607 | 608 | dprintf("Handle:0x%08x Object=0x%p Type=%s(%d) entry=0x%p processx=0x%x\n", 609 | (ULONG64)Handle, 610 | LookupEntry.pObject, 611 | GetGdiTypeDesc(entry.Type), entry.Type, 612 | pEntry, entry.ProcessIdOrSome 613 | ); 614 | } 615 | } 616 | 617 | if (CheckControlC() == TRUE) { 618 | dprintf("user control-c break\n"); 619 | break; 620 | } 621 | } 622 | 623 | return TRUE; 624 | } 625 | 626 | EXTERN_C 627 | HRESULT 628 | CALLBACK 629 | dgh(PDEBUG_CLIENT4 Client, PCSTR args) 630 | { 631 | CStringA strCmds; 632 | 633 | int argc; 634 | PSTR* argv; 635 | FILTER_CONTEXT FilterContext = { 0 }; 636 | 637 | strCmds.Format("!dgh %s", args); 638 | argv = CommandLineToArgvA(strCmds, &argc); 639 | CSimpleOptA Args(argc, argv, g_Options); 640 | 641 | while (Args.Next()) { 642 | if (Args.LastError() == SO_SUCCESS) { 643 | switch (Args.OptionId()) 644 | { 645 | case OPT_FL_PROC: 646 | FilterContext.Flags |= OPT_FL_PROC; 647 | FilterContext.Process = (PVOID)strtoull(Args.OptionArg(), NULL, 16); 648 | dprintf("!!!!!filter by process not implement!!!!!!!!\n"); 649 | break; 650 | case OPT_FL_TYPE: 651 | FilterContext.Flags |= OPT_FL_TYPE; 652 | FilterContext.Type = strtol(Args.OptionArg(), NULL, 10); 653 | break; 654 | 655 | case OPT_HELP: 656 | ShowDumpGdiHandleHelp(); 657 | return S_OK; 658 | default: 659 | break; 660 | } 661 | } 662 | } 663 | 664 | INIT_API(); 665 | DumpGdiHandle(&FilterContext); 666 | EXIT_API(); 667 | 668 | return S_OK; 669 | } 670 | 671 | 672 | ULONG 673 | GetUserObjCounts() 674 | { 675 | ULONG uReturn; 676 | BOOL bRet; 677 | 678 | WDBG_PTR kgSharedInfo = GetExpression("win32kbase!gSharedInfo"); 679 | SHAREDINFO SharedInfo; 680 | bRet = ReadMemory(kgSharedInfo, &SharedInfo, sizeof(SharedInfo), &uReturn); 681 | if (!bRet) { 682 | dprintf("can not read win32kbase!gSharedInfo at %p\n", kgSharedInfo); 683 | return 0; 684 | } 685 | 686 | // 687 | // read cHandleEntries from gpsi 688 | // 689 | 690 | ULONG_PTR cbHandleEntries = 0; 691 | bRet = ReadMemory((WDBG_PTR)SharedInfo.psi + sizeof(ULONG_PTR), &cbHandleEntries, 692 | sizeof(cbHandleEntries), &uReturn); 693 | if (!bRet) { 694 | dprintf("can not read gSharedInfo!cbHandleEntries at %p\n", (WDBG_PTR)SharedInfo.psi + sizeof(ULONG_PTR)); 695 | return 0; 696 | } 697 | 698 | return (ULONG)cbHandleEntries; 699 | } 700 | 701 | BOOL 702 | UserObjectGetEntry( 703 | IN UINT32 Index, 704 | OUT PHANDLEENTRY pphe, 705 | OUT PKERNEL_HANDLEENTRY pKrlEntry, 706 | OPTIONAL OUT PUSER_TYPE_DESC pCreateInfo 707 | ) 708 | { 709 | ULONG uReturn; 710 | BOOL bRet; 711 | 712 | if (!pphe || !pKrlEntry){ 713 | return FALSE; 714 | } 715 | 716 | WDBG_PTR kpKernelHandleTable = GetExpression("win32kbase!gpKernelHandleTable"); 717 | if (!kpKernelHandleTable) { 718 | dprintf("can not get symbol win32kbase!gpKernelHandleTable\n"); 719 | return FALSE; 720 | } 721 | WDBG_PTR pKernelHandleTable; 722 | bRet = ReadMemory(kpKernelHandleTable, &pKernelHandleTable, sizeof(pKernelHandleTable), &uReturn); 723 | if (!bRet) { 724 | dprintf("can not read win32kbase!gpKernelHandleTable at %p\n", kpKernelHandleTable); 725 | return FALSE; 726 | } 727 | 728 | 729 | WDBG_PTR kgSharedInfo = GetExpression("win32kbase!gSharedInfo"); 730 | SHAREDINFO SharedInfo; 731 | bRet = ReadMemory(kgSharedInfo, &SharedInfo, sizeof(SharedInfo), &uReturn); 732 | if (!bRet) { 733 | dprintf("can not read win32kbase!gpKernelHandleTable at %p\n", kpKernelHandleTable); 734 | return FALSE; 735 | } 736 | 737 | // 738 | // read cHandleEntries from gpsi 739 | // 740 | 741 | ULONG_PTR cbHandleEntries = 0; 742 | bRet = ReadMemory((WDBG_PTR)SharedInfo.psi + sizeof(ULONG_PTR), &cbHandleEntries, 743 | sizeof(cbHandleEntries), &uReturn); 744 | if (!bRet) { 745 | dprintf("can not read gSharedInfo!cbHandleEntries at %p\n", (WDBG_PTR)SharedInfo.psi + sizeof(ULONG_PTR)); 746 | return FALSE; 747 | } 748 | 749 | // 750 | // check the handle 751 | // 752 | 753 | if (Index > cbHandleEntries) { 754 | dprintf("Invalid handle\n"); 755 | return FALSE; 756 | } 757 | 758 | if (SharedInfo.aheSize != sizeof(HANDLEENTRY)) { 759 | dprintf("SharedInfo.aheSize != sizeof(HANDLEENTRY) please check\n"); 760 | } 761 | 762 | 763 | // 764 | // read handle entry 765 | // 766 | 767 | bRet = ReadMemory((WDBG_PTR)SharedInfo.aheList + (ULONG_PTR)SharedInfo.aheSize * Index, pphe, 768 | sizeof(*pphe), &uReturn); 769 | if (!bRet) { 770 | dprintf("can not read phe at %p\n", (WDBG_PTR)SharedInfo.aheList + (ULONG_PTR)SharedInfo.aheSize * Index); 771 | return FALSE; 772 | } 773 | 774 | // 775 | // Check type 776 | // 777 | 778 | if (pphe->Type < 1){ 779 | return FALSE; 780 | } 781 | 782 | // 783 | // get the real object 784 | // 785 | 786 | bRet = ReadMemory((WDBG_PTR)pKernelHandleTable + sizeof(KERNEL_HANDLEENTRY) * Index, pKrlEntry, 787 | sizeof(*pKrlEntry), &uReturn); 788 | if (!bRet) { 789 | dprintf("can not read kernel phe at %p\n", (WDBG_PTR)pKernelHandleTable + sizeof(KERNEL_HANDLEENTRY) * Index); 790 | return FALSE; 791 | } 792 | 793 | // 794 | // object createflag 795 | // 796 | 797 | if (pCreateInfo){ 798 | WDBG_PTR kgahti = GetExpression("win32kbase!gahti"); 799 | bRet = ReadMemory((WDBG_PTR)kgahti + sizeof(USER_TYPE_DESC) * pphe->Type, pCreateInfo, 800 | sizeof(*pCreateInfo), &uReturn); 801 | if (!bRet) { 802 | dprintf("can not read create info at %p\n", (WDBG_PTR)kgahti + sizeof(USER_TYPE_DESC) * pphe->Type); 803 | return FALSE; 804 | } 805 | } 806 | 807 | 808 | return TRUE; 809 | } 810 | 811 | VOID 812 | ShowDumpUserHandleHelp() 813 | { 814 | dprintf( "Usage: !duh [args]\n"); 815 | dprintf("\n"); 816 | dprintf("args list:\n"); 817 | dprintf("-p [process] filter object by process\n"); 818 | dprintf("-t [type id] filter object by type id\n"); 819 | dprintf(" valid type:\n"); 820 | 821 | LPCSTR lpTypeDesc = gTypeUnknonw; 822 | for (int i = 0; i < _countof(gUserTypeDesc); i++) { 823 | dprintf(" id:%d - %s\n", gUserTypeDesc[i].Type, gUserTypeDesc[i].lpszTypeDesc); 824 | } 825 | 826 | dprintf("example:\n"); 827 | dprintf("!duh\n"); 828 | dprintf(" will dump user object in system\n"); 829 | dprintf("!duh -p 0xffffffff13450080\n"); 830 | dprintf(" will dump user object create by process 0xffffffff13450080\n"); 831 | dprintf("!duh -t 1\n"); 832 | dprintf(" will dump all window object\n"); 833 | dprintf("!duh -t 1 -p 0xffffffff13450080\n"); 834 | dprintf(" will dump all window object create by process 0xffffffff13450080\n"); 835 | } 836 | 837 | BOOL 838 | DumpUserHandles( 839 | IN PFILTER_CONTEXT Context 840 | ) 841 | { 842 | UINT32 TotalHandleCounts; 843 | UINT32 HandleV; 844 | 845 | // 846 | // Check type 847 | // 848 | 849 | if (Context->Flags & OPT_FL_TYPE) { 850 | if (!IsUserTypeValid(Context->Type)) { 851 | dprintf("Invalid user object type\n"); 852 | return FALSE; 853 | } 854 | } 855 | 856 | g_ExtControl->Execute(DEBUG_OUTCTL_IGNORE, ".reload nt", DEBUG_EXECUTE_NOT_LOGGED); 857 | g_ExtControl->Execute(DEBUG_OUTCTL_IGNORE, ".reload win32kbase.sys", DEBUG_EXECUTE_NOT_LOGGED); 858 | g_ExtControl->Execute(DEBUG_OUTCTL_IGNORE, ".reload win32kfull.sys", DEBUG_EXECUTE_NOT_LOGGED); 859 | 860 | TotalHandleCounts = GetUserObjCounts(); 861 | if (!TotalHandleCounts) { 862 | dprintf("Have none user handle\n"); 863 | return TRUE; 864 | } 865 | 866 | dprintf("Total 0x%x handles\n", TotalHandleCounts); 867 | 868 | for (HandleV = 1; HandleV < TotalHandleCounts; HandleV++) 869 | { 870 | 871 | HANDLEENTRY HandleEntry; 872 | KERNEL_HANDLEENTRY KernelHandleEntry; 873 | USER_TYPE_DESC CreateInfo; 874 | if (UserObjectGetEntry(HandleV, &HandleEntry, &KernelHandleEntry, &CreateInfo)) { 875 | 876 | // 877 | // owner == NULL mean this handle has been destroy 878 | // 879 | 880 | if (KernelHandleEntry.pOwner) { 881 | 882 | ULONG_PTR Handle; 883 | LPCSTR lpszOwnerType = "Unknown"; 884 | ULONG_PTR pObject = NULL; 885 | 886 | // 887 | // read the handle from object 888 | // the user object always start with "HEAD" 889 | // 890 | 891 | if (!ReadPointer((ULONG64)KernelHandleEntry.pObject, &Handle)) { 892 | dprintf("can not handle at 0x%p\n", KernelHandleEntry.pObject); 893 | continue; 894 | } 895 | 896 | if (CreateInfo.CreateFlag & OCF_PROCESSOWNED) { 897 | 898 | // 899 | // the owner is PROCESSINFO 900 | // W32PROCESS 901 | // 902 | 903 | lpszOwnerType = "Process"; 904 | 905 | if (!ReadPointer((ULONG64)KernelHandleEntry.pOwner, &pObject)) { 906 | dprintf("can not handle at 0x%p\n", KernelHandleEntry.pOwner); 907 | continue; 908 | } 909 | 910 | }else if (CreateInfo.CreateFlag & OCF_THREADOWNED) { 911 | 912 | // 913 | // The Owner is THREADINFO 914 | // W32THREAD 915 | // 916 | 917 | ULONG_PTR pWin32Thread; 918 | lpszOwnerType = "Thread"; 919 | 920 | if (!ReadPointer((ULONG64)KernelHandleEntry.pOwner, &pWin32Thread)) { 921 | dprintf("can not handle at 0x%p\n", KernelHandleEntry.pOwner); 922 | continue; 923 | } 924 | 925 | // 926 | // So here is _ETHREAD 927 | // 928 | 929 | if (GetFieldValue(pWin32Thread, "nt!_KTHREAD", "Process", pObject)) { 930 | dprintf("can not process at thread 0x%p\n", pWin32Thread); 931 | continue; 932 | } 933 | 934 | 935 | }else { 936 | 937 | } 938 | 939 | //dprintf("handle:0x%08x object=0x%p type=(%02d)%-14s owner=0x%p process=0x%p createflag=0x%04x\n", 940 | // (ULONG64)Handle, 941 | // KernelHandleEntry.pObject, 942 | // HandleEntry.Type, GetUserTypeDesc(HandleEntry.Type), 943 | // KernelHandleEntry.pOwner, pObject, CreateInfo.CreateFlag 944 | //); 945 | 946 | if ((Context->Flags & OPT_FL_PROC && Context->Process != (PVOID)pObject) || 947 | (Context->Flags & OPT_FL_TYPE && Context->Type != (int)HandleEntry.Type)){ 948 | continue; 949 | } 950 | 951 | ExtOutDml("handle=0x%08x object=0x%p " \ 952 | "process=0x%p type=(%02d)%-14s\n", 953 | (ULONG64)Handle, (ULONG64)Handle, 954 | KernelHandleEntry.pObject, 955 | pObject, pObject, 956 | HandleEntry.Type, GetUserTypeDesc(HandleEntry.Type) 957 | ); 958 | 959 | } 960 | } 961 | 962 | if (CheckControlC() == TRUE) { 963 | dprintf("user control-c break\n"); 964 | break; 965 | } 966 | } 967 | 968 | return TRUE; 969 | } 970 | 971 | 972 | EXTERN_C 973 | HRESULT 974 | CALLBACK 975 | duh(PDEBUG_CLIENT4 Client, PCSTR args) 976 | { 977 | 978 | CStringA strCmds; 979 | 980 | int argc; 981 | PSTR* argv; 982 | FILTER_CONTEXT FilterContext = {0}; 983 | 984 | strCmds.Format("!duh %s", args); 985 | argv = CommandLineToArgvA(strCmds, &argc); 986 | CSimpleOptA Args(argc, argv, g_Options); 987 | 988 | while (Args.Next()) { 989 | if (Args.LastError() == SO_SUCCESS) { 990 | switch (Args.OptionId()) 991 | { 992 | case OPT_FL_PROC: 993 | FilterContext.Flags |= OPT_FL_PROC; 994 | FilterContext.Process = (PVOID)strtoull(Args.OptionArg(), NULL, 16); 995 | break; 996 | case OPT_FL_TYPE: 997 | FilterContext.Flags |= OPT_FL_TYPE; 998 | FilterContext.Type = strtol(Args.OptionArg(), NULL, 10); 999 | break; 1000 | 1001 | case OPT_HELP: 1002 | ShowDumpUserHandleHelp(); 1003 | return S_OK; 1004 | default: 1005 | break; 1006 | } 1007 | } 1008 | } 1009 | 1010 | INIT_API(); 1011 | DumpUserHandles(&FilterContext); 1012 | EXIT_API(); 1013 | 1014 | return S_OK; 1015 | } 1016 | 1017 | -------------------------------------------------------------------------------- /handle.hpp: -------------------------------------------------------------------------------- 1 | 2 | typedef struct _ENTRY 3 | { 4 | ULONG_PTR HandleNextFree; 5 | ULONG ProcessIdOrSome; 6 | USHORT DirOrType; 7 | UCHAR Type; 8 | UCHAR field_F; 9 | ULONG_PTR field_10; 10 | }ENTRY, *PENTRY; 11 | 12 | typedef struct _LOOKUP_ENTRY 13 | { 14 | PVOID pLock; 15 | PVOID pObject; 16 | }LOOKUP_ENTRY, *PLOOKUP_ENTRY; 17 | 18 | typedef struct _ENTRYDATALOOKUPTABLE 19 | { 20 | PLOOKUP_ENTRY *ppLookupEntries; 21 | ULONG Counts; 22 | LOOKUP_ENTRY LookupEntries[1]; 23 | }ENTRYDATALOOKUPTABLE, *PENTRYDATALOOKUPTABLE; 24 | 25 | typedef struct _HANDLEENTRYTABLE 26 | { 27 | PENTRY pEntries; 28 | ULONG Counts; 29 | ULONG LastFreeIndex; 30 | ULONG CurDirIndex; 31 | ULONG CurLookupIndex; 32 | PENTRYDATALOOKUPTABLE pEntryDataLookupTable; 33 | //ENTRY Entries[1]; 34 | }HANDLEENTRYTABLE, *PHANDLEENTRYTABLE; 35 | 36 | typedef struct _HANDLEENTRYDIR 37 | { 38 | UCHAR bFilled; 39 | UCHAR field_1; 40 | USHORT DirCounts; 41 | ULONG field_4; 42 | PHANDLEENTRYTABLE pEntryTable[0x100]; 43 | ULONG MaxDirGdiHandleCount; 44 | ULONG field_80C; 45 | }HANDLEENTRYDIR, *PHANDLEENTRYDIR; 46 | 47 | 48 | typedef struct _HANDLEMGR 49 | { 50 | ULONG TotalGdiHandleCounts; 51 | ULONG CurrentGdiHandleCounts; 52 | ULONG MaxGdiHandleCountSystem; 53 | ULONG field_C; 54 | PHANDLEENTRYDIR pHandleEntryDir; 55 | PULONG pHandleValueBackup; 56 | }HANDLEMGR, *PHANDLEMGR; 57 | 58 | 59 | typedef struct _HANDLEENTRY 60 | { 61 | ULONG_PTR ObjectOffset; 62 | ULONG_PTR ThreadOrProcessId; 63 | PVOID rpDisk; 64 | UCHAR Type; 65 | UCHAR field_19; 66 | USHORT wUniq; 67 | ULONG field_1C; 68 | }HANDLEENTRY, *PHANDLEENTRY; 69 | 70 | 71 | typedef struct tagSHAREDINFO 72 | { 73 | PVOID psi; 74 | PHANDLEENTRY aheList; 75 | ULONG aheSize; 76 | ULONG field_14; 77 | PVOID dispInfo; 78 | }SHAREDINFO, *PSHAREDINFO; 79 | 80 | 81 | typedef struct _KERNEL_HANDLEENTRY 82 | { 83 | PVOID pObject; 84 | PVOID pOwner; 85 | PVOID field_10; 86 | }KERNEL_HANDLEENTRY, *PKERNEL_HANDLEENTRY; 87 | 88 | typedef struct _USER_TYPE_DESC 89 | { 90 | PVOID fnDestroy; 91 | ULONG Tag; 92 | ULONG CreateFlag; 93 | ULONG field_10; 94 | ULONG field_14; 95 | }USER_TYPE_DESC, *PUSER_TYPE_DESC; 96 | 97 | 98 | 99 | 100 | -------------------------------------------------------------------------------- /help.cxx: -------------------------------------------------------------------------------- 1 | 2 | #include "stdafx.h" 3 | 4 | 5 | char *gaszHelp[] = { 6 | "=======================================================================\n" 7 | ,"win32k debugger extentions:\n" 8 | ,"author: pgboy\n" 9 | ,"-----------------------------------------------------------------------\n" 10 | ,"\n" 11 | ,"gh [object handle] -- HMGR entry of handle (GDI object like DC/BITMAP/PALETTE etc)\n" 12 | ,"uh [object handle] -- USER entry of handle (USER object like WINDOW/MENU etc)\n" 13 | ,"duh [-h] -- Dump USER entry of handle (USER object like WINDOW/MENU etc)\n" 14 | ,"dgh [-h] -- Dump HMGR entry of handle (GDI object like DC/BITMAP/PALETTE etc)\n" 15 | ,"dpsurf [SURFACE ptr] -- SURFACE\n" 16 | ,"dpso [SURFOBJ ptr] -- SURFACE struct from SURFOBJ\n" 17 | ,"dr [REGION ptr] -- REGION\n" 18 | ,"cr [REGION ptr] -- check REGION\n" 19 | ,"dppal [PALETTE ptr] -- PALETTE\n" 20 | ,"=======================================================================\n" 21 | ,NULL 22 | }; 23 | 24 | EXTERN_C 25 | HRESULT CALLBACK 26 | help(PDEBUG_CLIENT4 Client, PCSTR args) 27 | { 28 | for (char **ppsz = gaszHelp; *ppsz; ppsz++) 29 | dprintf("%s", *ppsz); 30 | return S_OK; 31 | } 32 | -------------------------------------------------------------------------------- /object/palette.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | struct _PALETTE; 6 | typedef struct _PALETTE PALETTE; 7 | 8 | typedef union _HDEVPPAL 9 | { 10 | HANDLE hdev; 11 | PALETTE* ppal; 12 | } HDEVPPAL; 13 | 14 | typedef struct _PALETTE 15 | { 16 | HPALETTE Handle; 17 | ULONG_PTR field_8; 18 | ULONG_PTR field_10; 19 | ULONG flPal; 20 | ULONG cEntries; 21 | ULONG ulTime; 22 | ULONG field_24; 23 | HDC hdcHead; 24 | HDEVPPAL hSelected; 25 | ULONG cRefhpal; 26 | ULONG cRefRegular; 27 | ULONG field_40; 28 | ULONG field_44; 29 | PVOID ptransFore; 30 | PVOID ptransCurrent; 31 | PVOID ptransOld; 32 | HANDLE hcmXform; 33 | PALETTE* ppalNext; 34 | PVOID apalColor; 35 | PVOID ppalColor; 36 | PVOID apalColorTable; 37 | }PALETTE ,*PPALETTE; 38 | 39 | 40 | -------------------------------------------------------------------------------- /object/region.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #define NEG_INFINITY 0x80000000 6 | #define POS_INFINITY 0x7fffffff 7 | 8 | 9 | typedef struct _BASEOBJECT 10 | { 11 | HANDLE hHmgr; 12 | PVOID pEntry; 13 | LONG cExclusiveLock; 14 | PVOID Tid; 15 | } BASEOBJECT, *POBJ; 16 | 17 | 18 | typedef struct _SCAN 19 | { 20 | ULONG cWalls; 21 | LONG yTop; 22 | LONG yBottom; 23 | LONG ai_x[1]; 24 | }SCAN, *PSCAN; 25 | 26 | typedef struct _REGION 27 | { 28 | HANDLE hHmgr; 29 | ULONG_PTR cExclusiveLock; 30 | PVOID Tid; 31 | ULONG sizeObj; 32 | ULONG UniqueId; 33 | ULONG cRefs; 34 | ULONG field_24; 35 | PSCAN pscnTail; 36 | LIST_ENTRY List; 37 | ULONG field_40; 38 | ULONG field_44; 39 | ULONG field_48; 40 | ULONG field_4C; 41 | ULONG sizeRgn; 42 | ULONG cScans; 43 | RECTL rcl; 44 | SCAN scan; 45 | }REGION; 46 | -------------------------------------------------------------------------------- /object/suface.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #define STYPE_BITMAP 0L 6 | #define STYPE_DEVICE 1L 7 | #define STYPE_JOURNAL 2L 8 | #define STYPE_DEVBITMAP 3L 9 | 10 | #define BMF_1BPP 1L 11 | #define BMF_4BPP 2L 12 | #define BMF_8BPP 3L 13 | #define BMF_16BPP 4L 14 | #define BMF_24BPP 5L 15 | #define BMF_32BPP 6L 16 | #define BMF_4RLE 7L 17 | #define BMF_8RLE 8L 18 | 19 | #define PDEV_SURFACE 0x80000000 // specifies the surface is for a pdev 20 | #define ABORT_SURFACE 0x40000000 // Abort operations on the surface 21 | #define DYNAMIC_MODE_PALETTE 0x20000000 // The surface is a Device Dependent 22 | // Bitmap whose palette was added 23 | // by GreDynamicModeChange 24 | #define UNREADABLE_SURFACE 0x10000000 // Reads not allowed from this surface 25 | #define PALETTE_SELECT_SET 0x08000000 // We wrote palette in at select time. 26 | #define DELETEABLE_PUBLIC_SURF 0x04000000 // deleteable even though user made public 27 | #define BANDING_SURFACE 0x02000000 // used for banding 28 | #define LAZY_DELETE_SURFACE 0x01000000 // DeleteObject has been called 29 | #define DDB_SURFACE 0x00800000 // Non-monochrome Device Dependent 30 | // Bitmap surface 31 | 32 | #define SURF_FLAGS 0xff800000 // Logical OR of all above flags 33 | 34 | 35 | 36 | typedef struct _SURFOBJ 37 | { 38 | HANDLE dhsurf; 39 | HANDLE hsurf; 40 | HANDLE dhpdev; 41 | HANDLE hdev; 42 | SIZEL sizlBitmap; 43 | ULONG cjBits; 44 | int field_2C; 45 | PVOID pvBits; 46 | PVOID pvScan0; 47 | LONG lDelta; 48 | ULONG iUniq; 49 | ULONG iBitmapFormat; 50 | WORD iType; 51 | WORD fjBitmap; 52 | }SURFOBJ, *PSURFOBJ; 53 | 54 | typedef struct _SURFACE 55 | { 56 | HANDLE Handle; 57 | HANDLE field_8; 58 | HANDLE field_10; 59 | SURFOBJ so; 60 | __int64 field_68; 61 | int field_70; 62 | int field_74; 63 | int field_78; 64 | int field_7C; 65 | PVOID pPal; 66 | __int64 field_88; 67 | __int64 field_90; 68 | __int64 field_98; 69 | __int64 field_A0; 70 | int field_A8; 71 | int field_AC; 72 | __int64 field_B0; 73 | int ProcessId; 74 | int field_BC; 75 | __int64 field_C0; 76 | __int64 field_C8; 77 | int field_D0; 78 | int field_D4; 79 | __int64 field_D8; 80 | int field_E0; 81 | int field_E4; 82 | __int64 field_E8; 83 | LIST_ENTRY list1; 84 | __int64 field_100; 85 | __int64 field_108; 86 | __int64 field_110; 87 | __int64 field_118; 88 | __int64 field_120; 89 | __int64 field_128; 90 | __int64 field_130; 91 | __int64 field_138; 92 | __int64 field_140; 93 | int field_148; 94 | int field_14C; 95 | __int64 field_150; 96 | __int64 field_158; 97 | __int64 field_160; 98 | __int64 field_168; 99 | __int64 field_170; 100 | __int64 field_178; 101 | __int64 field_180; 102 | __int64 field_188; 103 | __int64 field_190; 104 | __int64 field_198; 105 | __int64 field_1A0; 106 | __int64 field_1A8; 107 | __int64 field_1B0; 108 | __int64 field_1B8; 109 | __int64 field_1C0; 110 | LIST_ENTRY list2; 111 | __int64 field_1D8; 112 | __int64 field_1E0; 113 | __int64 field_1E8; 114 | __int64 field_1F0; 115 | __int64 field_1F8; 116 | __int64 field_200; 117 | __int64 field_208; 118 | __int64 field_210; 119 | __int64 field_218; 120 | int field_220; 121 | int field_224; 122 | int field_228; 123 | int field_22C; 124 | __int64 field_230; 125 | __int64 field_238; 126 | __int64 field_240; 127 | __int64 field_248; 128 | int field_250; 129 | int field_254; 130 | __int64 field_258; 131 | __int64 field_260; 132 | __int64 field_268; 133 | __int64 field_270; 134 | }SURFACE, *PSURFACE; 135 | 136 | -------------------------------------------------------------------------------- /stdafx.cpp: -------------------------------------------------------------------------------- 1 | // stdafx.cpp : source file that includes just the standard includes 2 | // win32kext.pch will be the pre-compiled header 3 | // stdafx.obj will contain the pre-compiled type information 4 | 5 | #include "stdafx.h" 6 | 7 | // TODO: reference any additional headers you need in STDAFX.H 8 | // and not in this file 9 | -------------------------------------------------------------------------------- /stdafx.h: -------------------------------------------------------------------------------- 1 | // stdafx.h : include file for standard system include files, 2 | // or project specific include files that are used frequently, but 3 | // are changed infrequently 4 | // 5 | 6 | #pragma once 7 | 8 | #include "targetver.h" 9 | 10 | #define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers 11 | // Windows Header Files: 12 | #include 13 | #include 14 | 15 | 16 | // TODO: reference additional headers your program requires here 17 | 18 | 19 | #ifdef _WIN64 20 | #define KDEXT_64BIT 21 | #else 22 | #define KDEXT_32BIT 23 | #endif 24 | 25 | #include 26 | #include 27 | 28 | #pragma comment(lib, "dbgeng.lib") 29 | 30 | #ifdef _DEBUG 31 | # define DEBUGPRINT dprintf 32 | #else 33 | # define DEBUGPRINT(...) __noop 34 | #endif 35 | 36 | #ifdef WIN32KEXT_EXPORTS 37 | #define WIN32KEXT_API __declspec(dllexport) 38 | #else 39 | #define WIN32KEXT_API __declspec(dllimport) 40 | #endif 41 | 42 | 43 | // 44 | // windbg pointer is 64-bit 45 | // 46 | 47 | typedef ULONG64 WDBG_PTR, *PWDBG_PTR; 48 | -------------------------------------------------------------------------------- /targetver.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // Including SDKDDKVer.h defines the highest available Windows platform. 4 | 5 | // If you wish to build your application for a previous Windows platform, include WinSDKVer.h and 6 | // set the _WIN32_WINNT macro to the platform you wish to support before including SDKDDKVer.h. 7 | 8 | #include 9 | -------------------------------------------------------------------------------- /utils.cxx: -------------------------------------------------------------------------------- 1 | 2 | #include "stdafx.h" 3 | #include "utils.hpp" 4 | 5 | PCHAR* 6 | CommandLineToArgvA( 7 | LPCSTR CmdLine, 8 | int* _argc 9 | ) 10 | { 11 | PCHAR* argv; 12 | PCHAR _argv; 13 | ULONG len; 14 | ULONG argc; 15 | CHAR a; 16 | ULONG i, j; 17 | 18 | BOOLEAN in_QM; 19 | BOOLEAN in_TEXT; 20 | BOOLEAN in_SPACE; 21 | 22 | len = strlen(CmdLine); 23 | i = ((len + 2) / 2) * sizeof(PVOID) + sizeof(PVOID); 24 | 25 | argv = (PCHAR*)LocalAlloc(0, 26 | i + (len + 2) * sizeof(CHAR)); 27 | if (!argv){ 28 | return NULL; 29 | } 30 | 31 | _argv = (PCHAR)(((PUCHAR)argv) + i); 32 | 33 | argc = 0; 34 | argv[argc] = _argv; 35 | in_QM = FALSE; 36 | in_TEXT = FALSE; 37 | in_SPACE = TRUE; 38 | i = 0; 39 | j = 0; 40 | 41 | while (a = CmdLine[i]) { 42 | if (in_QM) { 43 | if (a == '\"') { 44 | in_QM = FALSE; 45 | } 46 | else { 47 | _argv[j] = a; 48 | j++; 49 | } 50 | } 51 | else { 52 | switch (a) { 53 | case '\"': 54 | in_QM = TRUE; 55 | in_TEXT = TRUE; 56 | if (in_SPACE) { 57 | argv[argc] = _argv + j; 58 | argc++; 59 | } 60 | in_SPACE = FALSE; 61 | break; 62 | case ' ': 63 | case '\t': 64 | case '\n': 65 | case '\r': 66 | if (in_TEXT) { 67 | _argv[j] = '\0'; 68 | j++; 69 | } 70 | in_TEXT = FALSE; 71 | in_SPACE = TRUE; 72 | break; 73 | default: 74 | in_TEXT = TRUE; 75 | if (in_SPACE) { 76 | argv[argc] = _argv + j; 77 | argc++; 78 | } 79 | _argv[j] = a; 80 | j++; 81 | in_SPACE = FALSE; 82 | break; 83 | } 84 | } 85 | i++; 86 | } 87 | _argv[j] = '\0'; 88 | argv[argc] = NULL; 89 | 90 | (*_argc) = argc; 91 | return argv; 92 | } -------------------------------------------------------------------------------- /utils.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | 4 | PCHAR* 5 | CommandLineToArgvA( 6 | LPCSTR CmdLine, 7 | int* _argc 8 | ); -------------------------------------------------------------------------------- /win32kext.def: -------------------------------------------------------------------------------- 1 | LIBRARY WIN32KEXT 2 | 3 | EXPORTS 4 | dr 5 | cr 6 | uh 7 | gh 8 | dgh 9 | duh 10 | dpsurf 11 | dpso 12 | dppal 13 | help 14 | fl -------------------------------------------------------------------------------- /win32kext.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 14 4 | VisualStudioVersion = 14.0.25420.1 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "win32kext", "win32kext.vcxproj", "{53C877EF-71AF-4F96-B560-EFEE166B28C6}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|x64 = Debug|x64 11 | Debug|x86 = Debug|x86 12 | Release|x64 = Release|x64 13 | Release|x86 = Release|x86 14 | EndGlobalSection 15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 16 | {53C877EF-71AF-4F96-B560-EFEE166B28C6}.Debug|x64.ActiveCfg = Debug|x64 17 | {53C877EF-71AF-4F96-B560-EFEE166B28C6}.Debug|x64.Build.0 = Debug|x64 18 | {53C877EF-71AF-4F96-B560-EFEE166B28C6}.Debug|x86.ActiveCfg = Debug|Win32 19 | {53C877EF-71AF-4F96-B560-EFEE166B28C6}.Debug|x86.Build.0 = Debug|Win32 20 | {53C877EF-71AF-4F96-B560-EFEE166B28C6}.Release|x64.ActiveCfg = Release|x64 21 | {53C877EF-71AF-4F96-B560-EFEE166B28C6}.Release|x64.Build.0 = Release|x64 22 | {53C877EF-71AF-4F96-B560-EFEE166B28C6}.Release|x86.ActiveCfg = Release|Win32 23 | {53C877EF-71AF-4F96-B560-EFEE166B28C6}.Release|x86.Build.0 = Release|Win32 24 | EndGlobalSection 25 | GlobalSection(SolutionProperties) = preSolution 26 | HideSolutionNode = FALSE 27 | EndGlobalSection 28 | EndGlobal 29 | -------------------------------------------------------------------------------- /win32kext.vcxproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | Debug 14 | x64 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | 22 | {53C877EF-71AF-4F96-B560-EFEE166B28C6} 23 | Win32Proj 24 | win32kext 25 | 10.0 26 | win32kext 27 | 28 | 29 | 30 | DynamicLibrary 31 | true 32 | v142 33 | Unicode 34 | 35 | 36 | DynamicLibrary 37 | false 38 | v142 39 | true 40 | Unicode 41 | 42 | 43 | DynamicLibrary 44 | true 45 | v142 46 | Unicode 47 | 48 | 49 | DynamicLibrary 50 | false 51 | v142 52 | true 53 | Unicode 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | true 75 | 76 | 77 | true 78 | 79 | 80 | false 81 | 82 | 83 | false 84 | 85 | 86 | 87 | 88 | 89 | Level3 90 | Disabled 91 | WIN32;_DEBUG;_WINDOWS;_USRDLL;WIN32KEXT_EXPORTS;%(PreprocessorDefinitions) 92 | 93 | 94 | Windows 95 | true 96 | win32kext.def 97 | 98 | 99 | 100 | 101 | Use 102 | Level3 103 | Disabled 104 | _DEBUG;_WINDOWS;_USRDLL;WIN32KEXT_EXPORTS;%(PreprocessorDefinitions) 105 | MultiThreadedDebug 106 | 4838 107 | 108 | 109 | Windows 110 | true 111 | win32kext.def 112 | 113 | 114 | 115 | 116 | Level3 117 | 118 | 119 | MaxSpeed 120 | true 121 | true 122 | WIN32;NDEBUG;_WINDOWS;_USRDLL;WIN32KEXT_EXPORTS;%(PreprocessorDefinitions) 123 | MultiThreaded 124 | 4838 125 | 126 | 127 | Windows 128 | true 129 | true 130 | true 131 | win32kext.def 132 | 133 | 134 | 135 | 136 | Level3 137 | Use 138 | MaxSpeed 139 | true 140 | true 141 | NDEBUG;_WINDOWS;_USRDLL;WIN32KEXT_EXPORTS;%(PreprocessorDefinitions) 142 | MultiThreaded 143 | 144 | 145 | Windows 146 | true 147 | true 148 | true 149 | win32kext.def 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | false 169 | 170 | 171 | false 172 | 173 | 174 | false 175 | 176 | 177 | false 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | Create 187 | Create 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | -------------------------------------------------------------------------------- /win32kext.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hh;hpp;hxx;hm;inl;inc;xsd 11 | 12 | 13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} 14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms 15 | 16 | 17 | {90f31c2f-e578-4b73-b3e7-9014e5df3ba1} 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | Header Files 26 | 27 | 28 | Header Files 29 | 30 | 31 | Header Files 32 | 33 | 34 | Header Files\object 35 | 36 | 37 | Header Files\object 38 | 39 | 40 | Header Files\object 41 | 42 | 43 | Header Files 44 | 45 | 46 | Header Files 47 | 48 | 49 | 50 | 51 | Source Files 52 | 53 | 54 | Source Files 55 | 56 | 57 | Source Files 58 | 59 | 60 | Source Files 61 | 62 | 63 | Source Files 64 | 65 | 66 | Source Files 67 | 68 | 69 | Source Files 70 | 71 | 72 | Source Files 73 | 74 | 75 | 76 | 77 | Source Files 78 | 79 | 80 | --------------------------------------------------------------------------------