├── README.md ├── bin ├── x64 │ └── poolinfo.dll └── x86 │ └── poolinfo.dll └── src ├── .gitignore ├── poolinfo.sln └── poolinfo ├── SimpleOpt.h ├── context.cpp ├── context.h ├── pool.cpp ├── pool.h ├── poolinfo.cpp ├── poolinfo.def ├── poolinfo.h ├── poolinfo.vcxproj ├── poolinfo.vcxproj.filters ├── stdafx.cpp ├── stdafx.h ├── syms.cpp ├── syms.h ├── targetver.h ├── util.cpp └── util.h /README.md: -------------------------------------------------------------------------------- 1 | poolinfo 2 | ======== 3 | 4 | A handy windbg extenstion for Windows kernel pool introspection 5 | 6 | Usage 7 | ----- 8 | 9 | ``` 10 | _ _ __ 11 | _ __ ___ ___ | (_)_ __ / _| ___ 12 | | '_ \ / _ \ / _ \| | | '_ \| |_ / _ \ 13 | | |_) | (_) | (_) | | | | | | _| (_) | 14 | | .__/ \___/ \___/|_|_|_| |_|_| \___/ 15 | |_| 16 | by: jfisher @debugregister 17 | 18 | !poollist [options] [pool types] 19 | types: 20 | -a, --all display all pool descriptors (default) 21 | -n, --nonpaged display the NonPaged pool descriptors 22 | -p, --paged display the Paged pool descriptors 23 | -s, --session display the Session pool descriptors 24 | options: 25 | -l, --lookaside display the lookaside descriptors as well 26 | 27 | !poolinfo [options] 28 | pool options: 29 | -s, --summary show a summary of the pool descriptor (default) 30 | -v, --verbose display a full dump of the pool descriptor 31 | -f, --free show the freelist (ListHeads) 32 | -b, --bucket the bucket to show in the freelist (default is all) 33 | lookaside options: 34 | -l, --lookaside treat the supplied address as a lookaside pointer 35 | -t, --looktype the lookaside type. one of: nonpaged, paged, session 36 | this option specifies the # of buckets in the list 37 | and the size of the lookaside structure itself 38 | -b, --bucket the bucket to show from the lookaside 39 | other lookaside options 40 | -lb, --numbuckets <#> override the number of lookaside buckets 41 | -ls, --looksize override the size of the lookaside structure 42 | 43 | !poolpage [options] address 44 | options: 45 | -r, --raw display the raw POOL_HEADER structures 46 | 47 | !poolchunk
48 | ``` 49 | 50 | Example Usage 51 | ============= 52 | 53 | List pool descriptors 54 | --------------------- 55 | 56 | ``` 57 | kd> !poollist 58 | Pool Descriptors 59 | NonPagedPoolNx[0]: fffff8002e100d80 60 | NonPagedPool[0]: fffff8002e101ec0 61 | PagedPool[0]: ffffe000bfc3a000 62 | PagedPool[1]: ffffe000bfc3b140 63 | PagedPool[2]: ffffe000bfc3c280 64 | PagedPool[3]: ffffe000bfc3d3c0 65 | PagedPool[4]: ffffe000bfc3e500 66 | PagedPoolSession[0]: ffffd0011d4b0d00 67 | ``` 68 | 69 | ``` 70 | kd> !poollist -l 71 | Pool Descriptors 72 | NonPagedPoolNx[0]: fffff8002e100d80 73 | NonPagedPool[0]: fffff8002e101ec0 74 | PagedPool[0]: ffffe000bfc3a000 75 | PagedPool[1]: ffffe000bfc3b140 76 | PagedPool[2]: ffffe000bfc3c280 77 | PagedPool[3]: ffffe000bfc3d3c0 78 | PagedPool[4]: ffffe000bfc3e500 79 | PagedPoolSession[0]: ffffd0011d4b0d00 80 | Lookaside Descriptors 81 | Lookaside NonPagedPoolNx[0]: fffff8002e117a00 82 | Lookaside NonPagedPool[0]: fffff8002e118600 83 | Lookaside PagedPool[0]: fffff8002e119200 84 | Lookaside PagedPoolSession[0]: ffffd0011d4b00c0 85 | ``` 86 | 87 | 88 | Get pool descriptor and lookaside information 89 | --------------------------------------------- 90 | 91 | ### Display the descriptor itself 92 | 93 | ``` 94 | kd> !poolinfo -v fffff8002e100d80 95 | Pool Descriptor NonPagedPoolNx[0] at fffff8002e100d80 96 | PoolType: 200 (NonPagedPoolNx) 97 | PoolIndex: 00000000 98 | PendingFreeDepth: 0000001B 99 | RunningAllocs: 00068798 100 | RunningDeAllocs: 000562CE 101 | TotalBigPages: 00001097 102 | ThreadsProcessingDeferrals: 00000000 103 | TotalBytes: 02410E00 104 | TotalPages: 0000146A 105 | ``` 106 | 107 | ### Display the pool freelist (ListHeads) 108 | 109 | ``` 110 | kd> !poolinfo -v -f fffff8002e100d80 111 | Pool Descriptor NonPagedPoolNx[0] at fffff8002e100d80 112 | PoolType: 200 (NonPagedPoolNx) 113 | PoolIndex: 00000000 114 | PendingFreeDepth: 0000001B 115 | RunningAllocs: 00068798 116 | RunningDeAllocs: 000562CE 117 | TotalBigPages: 00001097 118 | ThreadsProcessingDeferrals: 00000000 119 | TotalBytes: 02410E00 120 | TotalPages: 0000146A 121 | ListHeads[01]: size=020 122 | ffffe000c21a1f90: size:020 prev:0E0 index:00 type:00 tag:Free 123 | ffffe000c1ad8940: size:020 prev:260 index:00 type:00 tag:Free 124 | ffffe000c1ad8e20: size:020 prev:090 index:00 type:00 tag:Free 125 | ffffe000c19e50e0: size:020 prev:0E0 index:00 type:00 tag:Free 126 | ffffe000c1980e50: size:020 prev:250 index:00 type:00 tag:Free 127 | ffffe000c1ade150: size:020 prev:150 index:00 type:00 tag:Free 128 | ffffe000bfe2dee0: size:020 prev:080 index:00 type:00 tag:Free 129 | ffffe000c219f3b0: size:020 prev:220 index:00 type:00 tag:Free 130 | ffffe000c1ae38b0: size:020 prev:090 index:00 type:00 tag:Free 131 | ffffe000c1ae8c10: size:020 prev:090 index:00 type:00 tag:Free 132 | ffffe000c1ad9d30: size:020 prev:0D0 index:00 type:00 tag:Free 133 | ffffe000c1ae65e0: size:020 prev:090 index:00 type:00 tag:Free 134 | ffffe000c1ae8170: size:020 prev:090 index:00 type:00 tag:Free 135 | ffffe000c1aeeeb0: size:020 prev:090 index:00 type:00 tag:Free 136 | ffffe000c1aff810: size:020 prev:810 index:00 type:00 tag:Free 137 | ffffe000c1b01eb0: size:020 prev:0E0 index:00 type:00 tag:Free 138 | ffffe000c1b077f0: size:020 prev:170 index:00 type:00 tag:Free 139 | ffffe000c1aeeb30: size:020 prev:230 index:00 type:00 tag:Free 140 | ffffe000c19e5370: size:020 prev:0A0 index:00 type:00 tag:Free 141 | ffffe000c1af9560: size:020 prev:090 index:00 type:00 tag:Free 142 | ffffe000c1aef220: size:020 prev:220 index:00 type:00 tag:Free 143 | ffffe000c1aef4d0: size:020 prev:150 index:00 type:00 tag:Free 144 | ffffe000c1afd080: size:020 prev:080 index:00 type:00 tag:Free 145 | ffffe000c1b0aa10: size:020 prev:230 index:00 type:00 tag:Free 146 | ffffe000c1b15190: size:020 prev:190 index:00 type:00 tag:Free 147 | ffffe000c2082510: size:020 prev:110 index:00 type:00 tag:Free 148 | ffffe000c1b0f400: size:020 prev:400 index:00 type:00 tag:Free 149 | ffffe000c1b0ae00: size:020 prev:0E0 index:00 type:00 tag:Free 150 | ffffe000c1b10400: size:020 prev:400 index:00 type:00 tag:Free 151 | ffffe000c1b14be0: size:020 prev:090 index:00 type:00 tag:Free 152 | ffffe000c1b1a130: size:020 prev:130 index:00 type:00 tag:Free 153 | ...SNIP... 154 | ``` 155 | 156 | ### Display only free list for size 0xBD0 157 | 158 | ``` 159 | kd> !poolinfo -f -b BD0 fffff8002e100d80 160 | Pool Descriptor NonPagedPoolNx[0] at fffff8002e100d80 161 | PoolType: 200 (NonPagedPoolNx) 162 | PoolIndex: 00000000 163 | PendingFreeDepth: 0000001B 164 | ListHeads[BC]: size=BD0 165 | ffffe000c025d430: size:BD0 prev:060 index:00 type:00 tag:FMic 166 | ffffe000c0245430: size:BD0 prev:060 index:00 type:00 tag:FMic 167 | ffffe000c025f430: size:BD0 prev:060 index:00 type:00 tag:FMic 168 | ffffe000c02e3430: size:BD0 prev:060 index:00 type:00 tag:FMic 169 | ffffe000c0243430: size:BD0 prev:060 index:00 type:00 tag:FMic 170 | ffffe000c023d430: size:BD0 prev:060 index:00 type:00 tag:Free 171 | ``` 172 | 173 | ### Display some lookaside bucket 174 | 175 | ``` 176 | kd> !poolinfo -l -t Paged -b 180 fffff8002e119200 177 | Lookaside[17]: size=180, fffff8002e119aa0 178 | ffffc0000b94fa50: size:180 prev:010 index:01 type:05 tag:PfRL 179 | ffffc000093a3e40: size:180 prev:010 index:04 type:05 tag:FMfn 180 | ``` 181 | 182 | ### Display contents of a pool page 183 | 184 | This is much like the build-in *!pool* command but a bit easier to read at a glance. 185 | 186 | ``` 187 | kd> !poolpage ffffc0000b94fa50 188 | walking pool page @ ffffc0000b94f000 189 | Addr A/F BlockSize PreviousSize PoolIndex PoolType Tag 190 | --------------------------------------------------------------------------- 191 | ffffc0000b94f000: InUse 0260 (026) 0000 (000) 01 03 FMfn 192 | ffffc0000b94f260: InUse 0170 (017) 0260 (026) 01 03 NtFs 193 | ffffc0000b94f3d0: InUse 0280 (028) 0170 (017) 01 03 FMfn 194 | ffffc0000b94f650: InUse 0060 (006) 0280 (028) 01 03 CMNb 195 | ffffc0000b94f6b0: InUse 00C0 (00C) 0060 (006) 01 03 FIcs 196 | ffffc0000b94f770: InUse 0050 (005) 00C0 (00C) 01 03 CMNb 197 | ffffc0000b94f7c0: InUse 0070 (007) 0050 (005) 01 03 CMNb 198 | ffffc0000b94f830: InUse 0050 (005) 0070 (007) 01 03 CMNb 199 | ffffc0000b94f880: InUse 0050 (005) 0050 (005) 01 03 CMNb 200 | ffffc0000b94f8d0: InUse 0040 (004) 0050 (005) 01 03 CMNb 201 | ffffc0000b94f910: InUse 0060 (006) 0040 (004) 01 03 CMNb 202 | ffffc0000b94f970: InUse 0060 (006) 0060 (006) 01 03 CMNb 203 | ffffc0000b94f9d0: InUse 0070 (007) 0060 (006) 01 0B DxgK 204 | ffffc0000b94fa40: Free 0010 (001) 0070 (007) 01 00 Free 205 | *ffffc0000b94fa50: Free 0180 (018) 0010 (001) 01 05 PfRL 206 | ffffc0000b94fbd0: InUse 01D0 (01D) 0180 (018) 01 03 FMfn 207 | ffffc0000b94fda0: InUse 0260 (026) 01D0 (01D) 01 03 FMfn 208 | ``` 209 | 210 | ### Lookup a particular chunk 211 | 212 | Example of a free chunk in ListHeads 213 | 214 | ``` 215 | kd> !poolchunk ffffe000c21a1f90 216 | ffffe000c21a1f90: size:020 prev:0E0 index:00 type:00 tag:Free 217 | chunk appears to be free, searching descriptors... 218 | ffffe000c21a1f90: #1 in ListHeads[01] in pool NonPagedPoolNx[0] (fffff8002e100d80) 219 | ``` 220 | 221 | Example of a free chunk on a Lookaside. 222 | 223 | ``` 224 | kd> !poolchunk ffffe000c02a3c20 225 | ffffe000c02a3c20: size:150 prev:030 index:00 type:04 tag:File 226 | chunk appears to be free, searching descriptors... 227 | ffffe000c02a3c20: #11 in NonPagedPoolNx Lookaside[14] (size=150) at fffff8002e118180 228 | ``` 229 | 230 | Example of an allocated chunk: 231 | 232 | ``` 233 | kd> !poolchunk ffffe000c21a1eb0 234 | ffffe000c21a1eb0: size:0E0 prev:0E0 index:00 type:02 tag:EtwR 235 | ffffe000c21a1eb0 is InUse on NonPagedPoolNx[0], page: ffffe000c21a1000 236 | ``` 237 | 238 | -------------------------------------------------------------------------------- /bin/x64/poolinfo.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fishstiqz/poolinfo/7ebfd4e394b7a38ece19c413b4bcba9a1ac3deb3/bin/x64/poolinfo.dll -------------------------------------------------------------------------------- /bin/x86/poolinfo.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fishstiqz/poolinfo/7ebfd4e394b7a38ece19c413b4bcba9a1ac3deb3/bin/x86/poolinfo.dll -------------------------------------------------------------------------------- /src/.gitignore: -------------------------------------------------------------------------------- 1 | #OS junk files 2 | [Tt]humbs.db 3 | *.DS_Store 4 | 5 | #Visual Studio files 6 | *.[Oo]bj 7 | *.user 8 | *.aps 9 | *.pch 10 | *.vspscc 11 | *.vssscc 12 | *_i.c 13 | *_p.c 14 | *.ncb 15 | *.suo 16 | *.tlb 17 | *.tlh 18 | *.bak 19 | *.[Cc]ache 20 | *.ilk 21 | *.log 22 | *.lib 23 | *.sbr 24 | *.sdf 25 | *.opensdf 26 | *.unsuccessfulbuild 27 | ipch/ 28 | [Oo]bj/ 29 | [Bb]in 30 | [Dd]ebug*/ 31 | [Rr]elease*/ 32 | [Ww]in32/ 33 | [Xx]64/ 34 | Ankh.NoLoad 35 | 36 | -------------------------------------------------------------------------------- /src/poolinfo.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 2012 4 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "poolinfo", "poolinfo\poolinfo.vcxproj", "{0BCE2D35-07D1-4D84-BF8E-37D83A61E6AF}" 5 | EndProject 6 | Global 7 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 8 | Debug|Win32 = Debug|Win32 9 | Debug|x64 = Debug|x64 10 | Release|Win32 = Release|Win32 11 | Release|x64 = Release|x64 12 | EndGlobalSection 13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 14 | {0BCE2D35-07D1-4D84-BF8E-37D83A61E6AF}.Debug|Win32.ActiveCfg = Debug|Win32 15 | {0BCE2D35-07D1-4D84-BF8E-37D83A61E6AF}.Debug|Win32.Build.0 = Debug|Win32 16 | {0BCE2D35-07D1-4D84-BF8E-37D83A61E6AF}.Debug|x64.ActiveCfg = Debug|x64 17 | {0BCE2D35-07D1-4D84-BF8E-37D83A61E6AF}.Debug|x64.Build.0 = Debug|x64 18 | {0BCE2D35-07D1-4D84-BF8E-37D83A61E6AF}.Release|Win32.ActiveCfg = Release|Win32 19 | {0BCE2D35-07D1-4D84-BF8E-37D83A61E6AF}.Release|Win32.Build.0 = Release|Win32 20 | {0BCE2D35-07D1-4D84-BF8E-37D83A61E6AF}.Release|x64.ActiveCfg = Release|x64 21 | {0BCE2D35-07D1-4D84-BF8E-37D83A61E6AF}.Release|x64.Build.0 = Release|x64 22 | EndGlobalSection 23 | GlobalSection(SolutionProperties) = preSolution 24 | HideSolutionNode = FALSE 25 | EndGlobalSection 26 | EndGlobal 27 | -------------------------------------------------------------------------------- /src/poolinfo/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 | 1048 | -------------------------------------------------------------------------------- /src/poolinfo/context.cpp: -------------------------------------------------------------------------------- 1 | #include "stdafx.h" 2 | 3 | 4 | 5 | WDBG_PTR *GetAllKprcb(PDEBUG_DATA_SPACES3 pDebugDataSpaces, PULONG pNum) 6 | { 7 | HRESULT res; 8 | ULONG i, numProc = 0; 9 | WDBG_PTR kKprcb; 10 | WDBG_PTR *kKprcbs = NULL; 11 | ULONG dataSize = 0; 12 | 13 | // first get the number of KPRCB 14 | while (TRUE) 15 | { 16 | res = pDebugDataSpaces->ReadProcessorSystemData(numProc, DEBUG_DATA_KPRCB_OFFSET, &kKprcb, sizeof(kKprcb), &dataSize); 17 | if (FAILED(res)) 18 | break; 19 | 20 | numProc++; 21 | } 22 | 23 | // now put them all in a list 24 | if (numProc > 0) 25 | { 26 | kKprcbs = (WDBG_PTR *) calloc(numProc, sizeof(WDBG_PTR)); 27 | for (i = 0; i < numProc; i++) 28 | { 29 | res = pDebugDataSpaces->ReadProcessorSystemData(numProc, DEBUG_DATA_KPRCB_OFFSET, &kKprcb, sizeof(kKprcb), &dataSize); 30 | kKprcbs[i] = kKprcb; 31 | } 32 | } 33 | 34 | *pNum = numProc; 35 | return kKprcbs; 36 | } 37 | 38 | HRESULT ReleaseCmdCtx(PCMD_CTX ctx) 39 | { 40 | #define _RELEASE_IFACE(p) \ 41 | if (p) { (p)->Release(); (p) = NULL; } 42 | 43 | _RELEASE_IFACE(ctx->pDebugSymbols); 44 | _RELEASE_IFACE(ctx->pDebugDataSpaces); 45 | _RELEASE_IFACE(ctx->pDebugControl); 46 | _RELEASE_IFACE(ctx->pDebugSystemObjects); 47 | 48 | #undef _RELEASE_IFACE 49 | 50 | #define _FREE_PTR(s) \ 51 | if (s) { free(s); (s) = NULL; } 52 | 53 | _FREE_PTR(ctx->pStructPoolDescriptor); 54 | _FREE_PTR(ctx->pStructEprocess); 55 | _FREE_PTR(ctx->pStructMmSessionSpace); 56 | _FREE_PTR(ctx->pStructKPRCB); 57 | _FREE_PTR(ctx->pStructGeneralLookasidePool); 58 | 59 | _FREE_PTR(ctx->pKprcbs); 60 | 61 | #undef _FREE_STRUCT 62 | 63 | return S_OK; 64 | } 65 | 66 | HRESULT InitCmdCtx(PCMD_CTX ctx, PDEBUG_CLIENT4 pClient) 67 | { 68 | HRESULT ret; 69 | ULONG dbgClass, dbgQualifier; 70 | 71 | memset(ctx, 0, sizeof(CMD_CTX)); 72 | 73 | ctx->pDebugClient = pClient; 74 | 75 | #define _CREATE_IFACE(type, ptr) \ 76 | if ((ret = DebugCreate(__uuidof(type), (PVOID *)(ptr))) != S_OK) { \ 77 | dprintf("failed to create " #type "\n"); \ 78 | ReleaseCmdCtx(ctx); \ 79 | return ret; \ 80 | } 81 | 82 | _CREATE_IFACE(IDebugSymbols3, &ctx->pDebugSymbols) 83 | _CREATE_IFACE(IDebugDataSpaces3, &ctx->pDebugDataSpaces) 84 | _CREATE_IFACE(IDebugControl4, &ctx->pDebugControl) 85 | _CREATE_IFACE(IDebugSystemObjects4, &ctx->pDebugSystemObjects) 86 | 87 | #undef _CREATE_IFACE 88 | 89 | // is this in the kernel? 90 | if (FAILED(ret = ctx->pDebugControl->GetDebuggeeType(&dbgClass, &dbgQualifier))) 91 | { 92 | dprintf("GetDebuggeeType failed\n"); 93 | ReleaseCmdCtx(ctx); 94 | return ret; 95 | } 96 | 97 | if (dbgClass != DEBUG_CLASS_KERNEL) 98 | { 99 | dprintf("This extension is for kernel-mode only\n"); 100 | ReleaseCmdCtx(ctx); 101 | return ret; 102 | } 103 | 104 | // whats the platform? 105 | ULONG platformId; 106 | ret = ctx->pDebugControl->GetSystemVersionValues(&platformId, &ctx->osMajor, &ctx->osMinor, &ctx->osFree, &ctx->osBuild); 107 | if (FAILED(ret)) 108 | { 109 | dprintf("GetSystemVersionValues failed"); 110 | ReleaseCmdCtx(ctx); 111 | return ret; 112 | } 113 | 114 | DEBUGPRINT("OS Detected: %d.%d.%d Free=%u\n", ctx->osMajor, ctx->osMinor, ctx->osBuild, (ctx->osFree == 0xF)); 115 | 116 | ctx->poolInUse = (ctx->osMajor <= 5 ? POOL_INUSE_XP : POOL_INUSE); 117 | 118 | if (ctx->pDebugControl->IsPointer64Bit() == S_OK) 119 | { 120 | ctx->b64BitTarget = TRUE; 121 | ctx->pointerLen = 8; 122 | ctx->poolBlockSize = POOL_BLOCK_SIZE64; 123 | ctx->listHeadsBuckets = 0xff + 1; 124 | ctx->sessionLookasideBuckets = 0x15; 125 | } 126 | else 127 | { 128 | ctx->b64BitTarget = FALSE; 129 | ctx->pointerLen = 4; 130 | ctx->poolBlockSize = POOL_BLOCK_SIZE32; 131 | ctx->listHeadsBuckets = 0x1ff + 1; 132 | ctx->sessionLookasideBuckets = 0x19; 133 | } 134 | 135 | 136 | if (FAILED(ret = ResolveAllStructs(ctx))) 137 | { 138 | dprintf("Failed to resolve the required structures. Are symbols loaded?\n"); 139 | ReleaseCmdCtx(ctx); 140 | return ret; 141 | } 142 | 143 | // get sizes for lookasides 144 | PSYM_FIELD lastField = GetStructField(ctx->pStructGeneralLookasidePool, "Future"); 145 | ctx->lookasideStructSize = lastField->fieldOffset + lastField->fieldSize; 146 | ctx->lookasideBuckets = POOL_LOOKASIDE_BUCKETS; 147 | ctx->sessionLookasideStructSize = SESSION_LOOKASIDE_SIZE; 148 | 149 | // get all the KPRCB 150 | ctx->pKprcbs = GetAllKprcb(ctx->pDebugDataSpaces, &ctx->numKprcb); 151 | if (ctx->pKprcbs == NULL) 152 | { 153 | ReleaseCmdCtx(ctx); 154 | return E_FAIL; 155 | } 156 | 157 | return ret; 158 | } 159 | 160 | -------------------------------------------------------------------------------- /src/poolinfo/context.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // windbg pointer is 64-bit 4 | typedef ULONG64 WDBG_PTR, *PWDBG_PTR; 5 | 6 | // 7 | // abstract structure representation 8 | // 9 | typedef struct _SYM_FIELD 10 | { 11 | const char * fieldName; 12 | BOOL fieldOptional; 13 | ULONG fieldOffset; 14 | ULONG fieldTypeId; 15 | ULONG fieldSize; 16 | PVOID fieldData; 17 | } 18 | SYM_FIELD, *PSYM_FIELD; 19 | 20 | #define DECL_FIELD(name) { #name, FALSE, 0, 0, 0, NULL } 21 | #define DECL_FIELD_OPT(name) { #name, TRUE , 0, 0, 0, NULL } 22 | #define END_FIELD { NULL, FALSE, 0, 0, 0, NULL } 23 | 24 | #define FIELD_NOT_PRESENT ((ULONG)-1) 25 | 26 | 27 | // 28 | // command context 29 | // 30 | typedef struct _CMD_CTX 31 | { 32 | // os versioning stuff 33 | ULONG osMajor; 34 | ULONG osMinor; 35 | ULONG osFree; 36 | ULONG osBuild; 37 | BOOL b64BitTarget; 38 | ULONG pointerLen; 39 | ULONG poolBlockSize; 40 | ULONG listHeadsBuckets; 41 | ULONG poolInUse; 42 | ULONG lookasideStructSize; 43 | ULONG lookasideBuckets; 44 | ULONG sessionLookasideStructSize; 45 | ULONG sessionLookasideBuckets; 46 | 47 | WDBG_PTR * pKprcbs; 48 | ULONG numKprcb; 49 | 50 | // debugging interfaces 51 | PDEBUG_CLIENT4 pDebugClient; 52 | PDEBUG_SYMBOLS3 pDebugSymbols; 53 | PDEBUG_CONTROL4 pDebugControl; 54 | PDEBUG_DATA_SPACES3 pDebugDataSpaces; 55 | PDEBUG_SYSTEM_OBJECTS4 pDebugSystemObjects; 56 | 57 | // structure defs 58 | PSYM_FIELD pStructPoolDescriptor; 59 | PSYM_FIELD pStructEprocess; 60 | PSYM_FIELD pStructMmSessionSpace; 61 | PSYM_FIELD pStructKPRCB; 62 | PSYM_FIELD pStructGeneralLookasidePool; 63 | 64 | } 65 | CMD_CTX, *PCMD_CTX; 66 | 67 | // 68 | // function prototypes 69 | // 70 | WDBG_PTR *GetAllKprcb(PDEBUG_DATA_SPACES3 pDebugDataSpaces, PULONG pNum); 71 | HRESULT ReleaseCmdCtx(PCMD_CTX ctx); 72 | HRESULT InitCmdCtx(PCMD_CTX ctx, PDEBUG_CLIENT4 pClient); 73 | 74 | 75 | -------------------------------------------------------------------------------- /src/poolinfo/pool.cpp: -------------------------------------------------------------------------------- 1 | #include "stdafx.h" 2 | 3 | 4 | ULONG GetBlockSize(PCMD_CTX ctx, PVOID pHeader) 5 | { 6 | ULONG BlockSize; 7 | 8 | if (ctx->b64BitTarget) 9 | BlockSize = ((PPOOL_HEADER64)pHeader)->BlockSize; 10 | else 11 | BlockSize = ((PPOOL_HEADER32)pHeader)->BlockSize; 12 | 13 | return BlockSize; 14 | } 15 | 16 | void GetPoolTag(ULONG32 tagDword, char *tag) 17 | { 18 | int i; 19 | unsigned char *p = (unsigned char *)(&tagDword); 20 | 21 | for (i = 0; i < 4; i++) 22 | tag[i] = ( isprint((int)p[i]) ? p[i] : '.' ); 23 | 24 | tag[4] = '\0'; 25 | } 26 | 27 | 28 | const char *PoolTypeName(POOL_TYPE poolType) 29 | { 30 | #define POOL_TYPE_CASE(t) \ 31 | case t: return #t 32 | 33 | switch (poolType) 34 | { 35 | POOL_TYPE_CASE(NonPagedPool); 36 | POOL_TYPE_CASE(PagedPool); 37 | POOL_TYPE_CASE(NonPagedPoolSession); 38 | POOL_TYPE_CASE(PagedPoolSession); 39 | POOL_TYPE_CASE(NonPagedPoolNx); 40 | POOL_TYPE_CASE(NonPagedPoolSessionNx); 41 | default: 42 | return "Unknown Pool"; 43 | } 44 | } 45 | 46 | const char *GetPoolType(PCMD_CTX ctx, WDBG_PTR kPoolDescriptor, ULONG *pPoolIndex) 47 | { 48 | const char *name; 49 | PSYM_FIELD pStructDesc; 50 | PVOID pStructData; 51 | 52 | pStructDesc = CloneStruct(ctx->pStructPoolDescriptor); 53 | if (pStructDesc == NULL) 54 | return NULL; 55 | 56 | if (FAILED(ReadVirtualStruct(ctx, kPoolDescriptor, pStructDesc, &pStructData))) 57 | { 58 | free(pStructDesc); 59 | return NULL; 60 | } 61 | 62 | name = PoolTypeName((POOL_TYPE) GS_ULONG(pStructDesc, "PoolType")); 63 | if (pPoolIndex != NULL) 64 | *pPoolIndex = GS_ULONG(pStructDesc, "PoolIndex"); 65 | 66 | free(pStructDesc); 67 | free(pStructData); 68 | 69 | return name; 70 | } 71 | 72 | // return the typename of the lookaside 73 | const char *GetLookasideType(PCMD_CTX ctx, WDBG_PTR kLookaside) 74 | { 75 | const char *name; 76 | PSYM_FIELD pStructDesc; 77 | PVOID pStructData; 78 | 79 | pStructDesc = CloneStruct(ctx->pStructGeneralLookasidePool); 80 | if (pStructDesc == NULL) 81 | return NULL; 82 | 83 | if (FAILED(ReadVirtualStruct(ctx, kLookaside, pStructDesc, &pStructData))) 84 | { 85 | free(pStructDesc); 86 | return NULL; 87 | } 88 | 89 | name = PoolTypeName((POOL_TYPE) GS_ULONG(pStructDesc, "Type")); 90 | 91 | free(pStructDesc); 92 | free(pStructData); 93 | 94 | return name; 95 | } 96 | 97 | const char *GetLookasideInfoType(ULONG poolType) 98 | { 99 | const char *infotype = "Unknown"; 100 | 101 | if (poolType == POOL_TYPE_NONPAGED) 102 | infotype = "NonPaged"; 103 | else if (poolType == POOL_TYPE_PAGED) 104 | infotype = "Paged"; 105 | else if (poolType == POOL_TYPE_SESSION) 106 | infotype = "Session"; 107 | 108 | return infotype; 109 | } 110 | 111 | 112 | 113 | // 114 | // get the paged pool info 115 | // 116 | POOL_DESC *GetPagedPools(PCMD_CTX ctx, PDWORD numPools) 117 | { 118 | DWORD num, i; 119 | POOL_DESC *descs; 120 | 121 | WDBG_PTR kNum = GetExpression("nt!ExpNumberOfPagedPools"); 122 | WDBG_PTR kDescList = GetExpression("nt!ExpPagedPoolDescriptor"); 123 | 124 | // read the number of paged pools 125 | if (FAILED(ReadVirtual(ctx, kNum, &num, sizeof(num)))) 126 | return NULL; 127 | 128 | // add 1 to the number of paged pools 129 | // this accounts for the extra descriptor at ExpPagedPoolDescriptor[0] 130 | num++; 131 | 132 | descs = (POOL_DESC *) calloc(num, sizeof(POOL_DESC)); 133 | if (descs == NULL) 134 | return NULL; 135 | 136 | // read the pool descriptor pointers 137 | for (i = 0; i < num; i++) 138 | { 139 | if (FAILED(ReadTargetPointer(ctx, kDescList + (i * ctx->pointerLen), &descs[i].kPoolDesc))) 140 | { 141 | free(descs); 142 | return NULL; 143 | } 144 | descs[i].poolType = POOL_TYPE_PAGED; 145 | } 146 | 147 | *numPools = num; 148 | return descs; 149 | } 150 | 151 | 152 | // 153 | // get the non-paged pools 154 | // this is from tarjei mandt's kernel pool paper... 155 | // 156 | POOL_DESC *GetNonPagedPools(PCMD_CTX ctx, PDWORD numPools) 157 | { 158 | DWORD numProcs = 0; 159 | WDBG_PTR kDescList; 160 | WDBG_PTR kVectorBase; 161 | DWORD numDescs = 0; 162 | POOL_DESC *descs; 163 | DWORD i; 164 | 165 | // how many processors? 166 | // should we instead be looking at nt!ExpNumberOfNonPagedPools ? 167 | WDBG_PTR kNumProc = GetExpression("nt!KeNumberProcessors"); 168 | if (FAILED(ReadVirtual(ctx, kNumProc, &numProcs, sizeof(numProcs)))) 169 | return NULL; 170 | 171 | if (numProcs > 1) 172 | { 173 | // multi-processor: read from nt!ExpNonPagedPoolDescriptor 174 | kDescList = GetExpression("nt!ExpNonPagedPoolDescriptor"); 175 | numDescs = numProcs; 176 | 177 | descs = (POOL_DESC *) calloc(numDescs, sizeof(POOL_DESC)); 178 | if (descs == NULL) 179 | return NULL; 180 | 181 | // read the pool descriptor pointers 182 | for (i = 0; i < numDescs; i++) 183 | { 184 | if (FAILED(ReadTargetPointer(ctx, kDescList + (i * ctx->pointerLen), &descs[i].kPoolDesc))) 185 | { 186 | free(descs); 187 | return NULL; 188 | } 189 | descs[i].poolType = POOL_TYPE_NONPAGED; 190 | } 191 | } 192 | else 193 | { 194 | // uni-processor: read from nt!PoolVector[0] 195 | kVectorBase = GetExpression("nt!PoolVector"); 196 | numDescs = 1; 197 | 198 | // we cannot trust osMinor on Windows 8.1 and above thanks to 199 | // the deprecation of GetVersionEx.. which I assume dbgeng must be using 200 | // given that it returns the wrong info for 8.1... 201 | // However, the build number is returned correctly. 202 | // Any build number >= 9200 is windows 8+ 203 | if (ctx->osBuild >= 9200) 204 | numDescs++; 205 | 206 | // read the descriptors pointer 207 | if (FAILED(ReadTargetPointer(ctx, kVectorBase, &kDescList))) 208 | return NULL; 209 | 210 | descs = (POOL_DESC *) calloc(numDescs, sizeof(POOL_DESC)); 211 | if (descs == NULL) 212 | return NULL; 213 | 214 | for (i = 0; i < numDescs; i++) 215 | { 216 | descs[i].kPoolDesc = kDescList; 217 | descs[i].poolType = POOL_TYPE_NONPAGED; 218 | 219 | kDescList += 0x140 + 0x1000; 220 | } 221 | } 222 | 223 | *numPools = numDescs; 224 | return descs; 225 | } 226 | 227 | WDBG_PTR GetSessionLookaside(PCMD_CTX ctx) 228 | { 229 | WDBG_PTR kptrLookaside = 0; 230 | WDBG_PTR p = GetExpression("nt!ExpSessionPoolLookaside"); 231 | 232 | if (FAILED(ReadTargetPointer(ctx, p, &kptrLookaside))) 233 | return NULL; 234 | 235 | return kptrLookaside; 236 | } 237 | 238 | WDBG_PTR GetSessionPool(PCMD_CTX ctx, PWDBG_PTR pkptrLookaside) 239 | { 240 | WDBG_PTR pSessionPool = 0; 241 | ULONG64 eprocOffset; 242 | PSYM_FIELD pStructEproc = NULL; 243 | PSYM_FIELD pStructSession = NULL; 244 | PVOID pEprocData = NULL; 245 | 246 | if (pkptrLookaside != NULL) 247 | *pkptrLookaside = 0; 248 | 249 | // get the current EPROCESS 250 | if (FAILED(ctx->pDebugSystemObjects->GetImplicitProcessDataOffset(&eprocOffset))) 251 | return NULL; 252 | 253 | pStructEproc = CloneStruct(ctx->pStructEprocess); 254 | if (pStructEproc == NULL) 255 | return NULL; 256 | 257 | pStructSession = CloneStruct(ctx->pStructMmSessionSpace); 258 | if (pStructSession == NULL) 259 | { 260 | free(pStructEproc); 261 | return NULL; 262 | } 263 | 264 | // read the EPROCESS 265 | if (SUCCEEDED(ReadVirtualStruct(ctx, (WDBG_PTR)eprocOffset, pStructEproc, &pEprocData))) 266 | { 267 | WDBG_PTR addrSession = GetStructPointer(ctx, pStructEproc, "Session"); 268 | 269 | // not every process will have a session, such as System 270 | if (addrSession != 0) 271 | { 272 | // get address of MM_SESSION_SPACE.PagedPool 273 | pSessionPool = addrSession + GetStructField(pStructSession, "PagedPool")->fieldOffset; 274 | 275 | // lookaside @ &MM_SESSION_SPACE.Lookaside 276 | if (pkptrLookaside != NULL) 277 | *pkptrLookaside = addrSession + GetStructField(pStructSession, "Lookaside")->fieldOffset; 278 | 279 | //DEBUGPRINT("kptrLookaside: %p\n", *pkptrLookaside); 280 | } 281 | 282 | free(pEprocData); 283 | } 284 | 285 | free(pStructSession); 286 | free(pStructEproc); 287 | 288 | return pSessionPool; 289 | } 290 | 291 | POOL_DESC *GetAllPoolDescriptors(PCMD_CTX ctx, ULONG *pNum) 292 | { 293 | POOL_DESC *pools = NULL; 294 | ULONG count = 0; 295 | ULONG total = 0, i = 0; 296 | POOL_DESC *NPDescs, *PDescs; 297 | WDBG_PTR SDesc; 298 | DWORD numNP, numP; 299 | PBYTE p; 300 | 301 | *pNum = 0; 302 | 303 | NPDescs = GetNonPagedPools(ctx, &numNP); 304 | PDescs = GetPagedPools(ctx, &numP); 305 | SDesc = GetSessionPool(ctx, NULL); 306 | 307 | if (NPDescs != NULL) count += numNP; 308 | if (PDescs != NULL) count += numP; 309 | if (SDesc != NULL) count += 1; 310 | 311 | if (count == 0) 312 | return NULL; 313 | 314 | pools = (POOL_DESC *) calloc(count, sizeof(POOL_DESC)); 315 | if (pools != NULL) 316 | { 317 | p = (PBYTE) pools; 318 | 319 | if (NPDescs != NULL) 320 | { 321 | memcpy(p, NPDescs, numNP * sizeof(POOL_DESC)); 322 | p += numNP * sizeof(POOL_DESC); 323 | } 324 | 325 | if (PDescs != NULL) 326 | { 327 | memcpy(p, PDescs, numP * sizeof(POOL_DESC)); 328 | p += numP * sizeof(POOL_DESC); 329 | } 330 | 331 | if (SDesc != NULL) 332 | { 333 | POOL_DESC *tmp = (POOL_DESC *)p; 334 | tmp->kPoolDesc = SDesc; 335 | tmp->poolType = POOL_TYPE_SESSION; 336 | } 337 | 338 | *pNum = count; 339 | } 340 | 341 | if (NPDescs != NULL) free(NPDescs); 342 | if (PDescs != NULL) free(PDescs); 343 | 344 | return pools; 345 | } 346 | 347 | 348 | 349 | // Get the kernel pointer to the specified field of the current KPRCB 350 | WDBG_PTR GetPrcbFieldOffset(PCMD_CTX ctx, WDBG_PTR kprcb, const char *fieldName) 351 | { 352 | if (kprcb == NULL) 353 | kprcb = GetExpression("@$prcb"); // use current if none specified 354 | return kprcb + GetStructField(ctx->pStructKPRCB, fieldName)->fieldOffset; 355 | } 356 | 357 | // 358 | // fetch the specified lookaside from the list KPRCB 359 | // 360 | LOOKASIDE_DESC *GetKPRCBLookasides(PCMD_CTX ctx, const char *lookasideName, ULONG lookasideType, PULONG pNum) 361 | { 362 | ULONG i; 363 | 364 | LOOKASIDE_DESC *pLookasides = (LOOKASIDE_DESC *) calloc(ctx->numKprcb, sizeof(LOOKASIDE_DESC)); 365 | if (pLookasides == NULL) 366 | return NULL; 367 | 368 | for (i = 0; i < ctx->numKprcb; i++) 369 | { 370 | pLookasides[i].kLookasideDesc = GetPrcbFieldOffset(ctx, ctx->pKprcbs[i], lookasideName); 371 | pLookasides[i].poolType = lookasideType; 372 | pLookasides[i].numBuckets = ctx->lookasideBuckets; 373 | pLookasides[i].structSize = ctx->lookasideStructSize; 374 | pLookasides[i].processor = i; 375 | } 376 | 377 | *pNum = ctx->numKprcb; 378 | return pLookasides; 379 | } 380 | 381 | LOOKASIDE_DESC *GetNonPagedLookasides(PCMD_CTX ctx, PULONG pNum) 382 | { 383 | return GetKPRCBLookasides(ctx, "PPNPagedLookasideList", POOL_TYPE_NONPAGED, pNum); 384 | } 385 | 386 | LOOKASIDE_DESC *GetNonPagedNxLookasides(PCMD_CTX ctx, PULONG pNum) 387 | { 388 | PSYM_FIELD pField = GetStructField(ctx->pStructKPRCB, "PPNxPagedLookasideList"); 389 | if (pField == NULL || pField->fieldTypeId == FIELD_NOT_PRESENT) 390 | return NULL; 391 | else 392 | return GetKPRCBLookasides(ctx, "PPNxPagedLookasideList", POOL_TYPE_NONPAGED, pNum); 393 | } 394 | 395 | LOOKASIDE_DESC *GetPagedLookasides(PCMD_CTX ctx, PULONG pNum) 396 | { 397 | return GetKPRCBLookasides(ctx, "PPPagedLookasideList", POOL_TYPE_PAGED, pNum); 398 | } 399 | 400 | LOOKASIDE_DESC *GetSessionLookasides(PCMD_CTX ctx, PULONG pNum) 401 | { 402 | WDBG_PTR kLookaside; 403 | WDBG_PTR kPool = GetSessionPool(ctx, &kLookaside); 404 | if (kPool) 405 | { 406 | LOOKASIDE_DESC *ret = (LOOKASIDE_DESC *) malloc(sizeof(LOOKASIDE_DESC)); 407 | if (ret != NULL) 408 | { 409 | ret->kLookasideDesc = kLookaside; 410 | ret->structSize = ctx->sessionLookasideStructSize; 411 | ret->numBuckets = ctx->sessionLookasideBuckets; 412 | ret->poolType = POOL_TYPE_SESSION; 413 | ret->processor = 0; 414 | 415 | *pNum = 1; 416 | } 417 | return ret; 418 | } 419 | 420 | return NULL; 421 | } 422 | 423 | 424 | LOOKASIDE_DESC *GetAllLookasides(PCMD_CTX ctx, PULONG pNum) 425 | { 426 | ULONG numNPNx = 0; 427 | ULONG numNP = 0; 428 | ULONG numP = 0; 429 | ULONG numS = 0; 430 | 431 | ULONG total = 0; 432 | 433 | LOOKASIDE_DESC *pNPNx = GetNonPagedNxLookasides(ctx, &numNPNx); 434 | LOOKASIDE_DESC *pNP = GetNonPagedLookasides(ctx, &numNP); 435 | LOOKASIDE_DESC *pP = GetPagedLookasides(ctx, &numP); 436 | LOOKASIDE_DESC *pS = GetSessionLookasides(ctx, &numS); 437 | LOOKASIDE_DESC *ret; 438 | PBYTE p; 439 | 440 | if (pNPNx) total += numNPNx; 441 | if (pNP) total += numNP; 442 | if (pP) total += numP; 443 | if (pS) total += numS; 444 | 445 | if (total == 0) 446 | return NULL; 447 | 448 | ret = (LOOKASIDE_DESC *) calloc(total, sizeof(LOOKASIDE_DESC)); 449 | if (ret != NULL) 450 | { 451 | p = (PBYTE) ret; 452 | 453 | if (pNPNx) 454 | { 455 | memcpy(p, pNPNx, numNPNx * sizeof(LOOKASIDE_DESC)); 456 | p += numNPNx * sizeof(LOOKASIDE_DESC); 457 | } 458 | 459 | if (pNP) 460 | { 461 | memcpy(p, pNP, numNP * sizeof(LOOKASIDE_DESC)); 462 | p += numNP * sizeof(LOOKASIDE_DESC); 463 | } 464 | 465 | if (pP) 466 | { 467 | memcpy(p, pP, numP * sizeof(LOOKASIDE_DESC)); 468 | p += numP * sizeof(LOOKASIDE_DESC); 469 | } 470 | 471 | if (pS) 472 | { 473 | memcpy(p, pS, numS * sizeof(LOOKASIDE_DESC)); 474 | p += numS * sizeof(LOOKASIDE_DESC); 475 | } 476 | 477 | *pNum = total; 478 | } 479 | 480 | return ret; 481 | } 482 | 483 | 484 | 485 | void PrintTabularChunkHeader(PCMD_CTX ctx, ULONG indentLevel) 486 | { 487 | ULONG i; 488 | 489 | for (i = 0; i < indentLevel; i++) dprintf(" "); 490 | dprintf(" %-*s A/F BlockSize PreviousSize PoolIndex PoolType Tag\n", ctx->pointerLen*2, "Addr"); 491 | for (i = 0; i < indentLevel; i++) dprintf(" "); 492 | dprintf(" "); 493 | for (i = 0; i < (ctx->pointerLen * 2); i++) dprintf("-"); 494 | dprintf("-----------------------------------------------------------\n"); 495 | } 496 | 497 | template 498 | HRESULT PrintPoolHeaderTempl(PCMD_CTX ctx, WDBG_PTR kPtrChunk, HeaderType *hdr, BOOL bTabular, BOOL bRaw, ULONG indentLevel, BOOL bHighlight) 499 | { 500 | HRESULT ret = S_OK; 501 | char tag[5]; 502 | DWORD raw[4]; 503 | HeaderType tmpHdr; 504 | 505 | // if no header data, just read it here 506 | if (hdr == NULL) 507 | { 508 | if (FAILED(ret = ReadVirtual(ctx, kPtrChunk, &tmpHdr, sizeof(tmpHdr)))) 509 | return ret; 510 | hdr = &tmpHdr; 511 | } 512 | 513 | GetPoolTag(hdr->PoolTag, tag); 514 | 515 | for (ULONG i = 0; i < indentLevel; i++) 516 | dprintf(" "); 517 | 518 | if (bHighlight) 519 | OutputDml(ctx, ""); 520 | 521 | if (bTabular) 522 | { 523 | if (bHighlight) 524 | dprintf("*"); 525 | else 526 | dprintf(" "); 527 | 528 | dprintf("%p: %-5s %04X (%03X) %04X (%03X) %02X %02X %4s", 529 | kPtrChunk, 530 | ((hdr->PoolType & ctx->poolInUse) ? "InUse" : "Free"), 531 | hdr->BlockSize * ctx->poolBlockSize, 532 | hdr->BlockSize, 533 | hdr->PreviousSize * ctx->poolBlockSize, 534 | hdr->PreviousSize, 535 | hdr->PoolIndex, 536 | hdr->PoolType, 537 | tag); 538 | } 539 | else 540 | { 541 | dprintf("%p: size:%03X prev:%03X index:%02X type:%02X tag:%4s", 542 | kPtrChunk, 543 | hdr->BlockSize * ctx->poolBlockSize, 544 | hdr->PreviousSize * ctx->poolBlockSize, 545 | hdr->PoolIndex, 546 | hdr->PoolType, 547 | tag); 548 | } 549 | 550 | if (bRaw) 551 | { 552 | if (FAILED(ret = ReadVirtual(ctx, kPtrChunk, &raw, sizeof(raw)))) 553 | { 554 | dprintf("\n"); 555 | return ret; 556 | } 557 | 558 | dprintf(" %08X %08X %08X %08X", 559 | raw[0], raw[1], raw[2], raw[3]); 560 | } 561 | 562 | if (bHighlight) 563 | OutputDml(ctx, ""); 564 | 565 | dprintf("\n"); 566 | return ret; 567 | } 568 | 569 | HRESULT PrintPoolHeader(PCMD_CTX ctx, WDBG_PTR kPtrChunk, PVOID hdr, BOOL bTabular, BOOL bRaw, ULONG indentLevel, BOOL bHighlight) 570 | { 571 | if (ctx->b64BitTarget) 572 | return PrintPoolHeaderTempl(ctx, kPtrChunk, (POOL_HEADER64 *)hdr, bTabular, bRaw, indentLevel, bHighlight); 573 | else 574 | return PrintPoolHeaderTempl(ctx, kPtrChunk, (POOL_HEADER32 *)hdr, bTabular, bRaw, indentLevel, bHighlight); 575 | } 576 | 577 | 578 | BOOL EnumPoolPage(PCMD_CTX ctx, WDBG_PTR kPtr, LPARAM lParam, ENUMPAGEPROC enumProc) 579 | { 580 | ULONG offset = 0; 581 | BYTE page[PAGE_SIZE]; 582 | WDBG_PTR kPtrPage = PAGE_ALIGN(kPtr); 583 | WDBG_PTR kChunk; 584 | ULONG BlockSize; 585 | 586 | // read the entire page 587 | if (FAILED(ReadVirtual(ctx, kPtrPage, page, PAGE_SIZE))) 588 | return FALSE; 589 | 590 | while (TRUE) 591 | { 592 | // get the BlockSize 593 | BlockSize = GetBlockSize(ctx, (page + offset)); 594 | 595 | // kernel pointer to the pool chunk 596 | kChunk = PAGE_ALIGN(kPtr) + offset; 597 | 598 | // call the callback function 599 | if (!enumProc(ctx, kChunk, (page + offset), lParam)) 600 | { 601 | // callback requested a stop 602 | return FALSE; 603 | } 604 | 605 | // advance the offset to the next chunk 606 | // check that we don't have a 0-sized blocksize, which would infinite loop 607 | if (BlockSize == 0) 608 | { 609 | dprintf("Invalid BlockSize(0) at %p. Is this a pool page?\n", kChunk); 610 | return FALSE; 611 | } 612 | 613 | offset += (BlockSize * ctx->poolBlockSize); 614 | 615 | // if on next page, terminate 616 | if (PAGE_ALIGN(kPtrPage + offset) != kPtrPage) 617 | { 618 | // done with this page, did the last chunk make sense? 619 | // should be at beginning of next page 620 | if ((kPtrPage + offset) != (kPtrPage + PAGE_SIZE)) 621 | { 622 | dprintf("** Invalid BlockSize %03X for %p ??\n", BlockSize, kChunk); 623 | } 624 | 625 | break; 626 | } 627 | } 628 | 629 | return TRUE; 630 | } 631 | 632 | 633 | BOOL EnumListHeadsBucket(PCMD_CTX ctx, WDBG_PTR kPoolDesc, ULONG bucket, LPARAM lParam, ENUMLISTHEADSPROC enumProc) 634 | { 635 | BOOL bRet = TRUE; 636 | 637 | WDBG_PTR head; 638 | WDBG_PTR Flink; 639 | WDBG_PTR tmp; 640 | ULONG count = 0; 641 | 642 | PSYM_FIELD field = GetStructField(ctx->pStructPoolDescriptor, "ListHeads"); 643 | WDBG_PTR kListHeads = kPoolDesc + field->fieldOffset; 644 | WDBG_PTR kBucketPtr = kListHeads + (bucket * (2 * ctx->pointerLen)); 645 | 646 | // read the initial Flink 647 | if (FAILED(ReadTargetPointer(ctx, kBucketPtr, &Flink))) 648 | return FALSE; 649 | 650 | head = kBucketPtr; 651 | 652 | if (Flink == head) 653 | { 654 | // empty 655 | bRet = enumProc(ctx, bucket, TRUE, NULL, count, lParam); 656 | } 657 | else 658 | { 659 | while (Flink != head) 660 | { 661 | WDBG_PTR kChunk = Flink - (2 * ctx->pointerLen); 662 | 663 | // call the enum proc 664 | bRet = enumProc(ctx, bucket, FALSE, kChunk, count++, lParam); 665 | 666 | // terminate enumeration on FALSE return from enum proc 667 | if (!bRet) 668 | break; 669 | 670 | // read the Flink 671 | if (FAILED(ReadTargetPointer(ctx, Flink, &tmp))) 672 | return FALSE; 673 | 674 | // go to next entry 675 | Flink = tmp; 676 | } 677 | } 678 | 679 | return bRet; 680 | } 681 | 682 | 683 | BOOL EnumListHeads(PCMD_CTX ctx, WDBG_PTR kPoolDesc, ULONG bucket, LPARAM lParam, ENUMLISTHEADSPROC enumProc) 684 | { 685 | BOOL ret; 686 | ULONG i; 687 | 688 | if (bucket == BUCKET_ALL) 689 | { 690 | // dump all the buckets 691 | for (i = 0; i < ctx->listHeadsBuckets; i++) 692 | { 693 | ret = EnumListHeadsBucket(ctx, kPoolDesc, i, lParam, enumProc); 694 | // terminate enumeration on FALSE return 695 | if (!ret) 696 | break; 697 | } 698 | } 699 | else 700 | { 701 | // if just one bucket, dump out this bucket 702 | ret = EnumListHeadsBucket(ctx, kPoolDesc, bucket, lParam, enumProc); 703 | } 704 | 705 | return ret; 706 | } 707 | 708 | 709 | 710 | 711 | 712 | 713 | BOOL EnumLookasideBucket(PCMD_CTX ctx, LOOKASIDE_DESC *pLookasideDesc, ULONG bucket, LPARAM lParam, ENUMLOOKASIDEPROC enumProc) 714 | { 715 | BOOL bRet = TRUE; 716 | WDBG_PTR kPtrs[2], pCur, pNext; 717 | ULONG count = 0; 718 | WDBG_PTR kLookasideBucket; 719 | PSYM_FIELD listField; 720 | 721 | if (bucket >= pLookasideDesc->numBuckets) 722 | return FALSE; 723 | 724 | kLookasideBucket = pLookasideDesc->kLookasideDesc + (bucket * pLookasideDesc->structSize); 725 | listField = GetStructField(ctx->pStructGeneralLookasidePool, "SingleListHead"); 726 | 727 | // read the list head 728 | if (FAILED(ReadTargetPointerArray(ctx, kLookasideBucket + listField->fieldOffset, kPtrs, _countof(kPtrs)))) 729 | return FALSE; 730 | 731 | // 64-bit lookaside struct has the Next pointer at [1] 732 | if (ctx->b64BitTarget) 733 | pCur = kPtrs[1]; 734 | else 735 | pCur = kPtrs[0]; 736 | 737 | if (pCur == NULL) 738 | { 739 | // empty list 740 | bRet = enumProc(ctx, kLookasideBucket, bucket, TRUE, NULL, 0, lParam); 741 | } 742 | else 743 | { 744 | while (pCur != NULL) 745 | { 746 | // get the chunk pointer 747 | WDBG_PTR kChunk = pCur - (2 * ctx->pointerLen); 748 | 749 | // call the enum proc 750 | bRet = enumProc(ctx, kLookasideBucket, bucket, FALSE, kChunk, count, lParam); 751 | 752 | // read the next pointer 753 | if (FAILED(ReadTargetPointer(ctx, pCur, &pNext))) 754 | return FALSE; 755 | 756 | pCur = pNext; // go to next entry in the list 757 | count++; 758 | } 759 | } 760 | 761 | return bRet; 762 | } 763 | 764 | 765 | 766 | BOOL EnumLookasides(PCMD_CTX ctx, LOOKASIDE_DESC *pLookasideDesc, ULONG bucket, LPARAM lParam, ENUMLOOKASIDEPROC enumProc) 767 | { 768 | BOOL bRet = FALSE; 769 | ULONG i; 770 | 771 | if (bucket == BUCKET_ALL) 772 | { 773 | // dump all the buckets 774 | for (i = 0; i < pLookasideDesc->numBuckets; i++) 775 | { 776 | bRet = EnumLookasideBucket(ctx, pLookasideDesc, i, lParam, enumProc); 777 | // terminate enumeration on FALSE return 778 | if (!bRet) 779 | break; 780 | } 781 | } 782 | else 783 | { 784 | // if just one bucket, dump out this bucket 785 | if (bucket < pLookasideDesc->numBuckets) 786 | bRet = EnumLookasideBucket(ctx, pLookasideDesc, bucket, lParam, enumProc); 787 | } 788 | 789 | return bRet; 790 | } 791 | 792 | 793 | BOOL CALLBACK EnumProcPrintHeader(PCMD_CTX ctx, WDBG_PTR kChunk, PVOID pHeader, LPARAM lParam) 794 | { 795 | PPRINT_OPTS pPrintOpts = (PPRINT_OPTS)lParam; 796 | BOOL bHighlight = FALSE; 797 | ULONG BlockSize; 798 | 799 | if (pPrintOpts->bHighlight) 800 | { 801 | BlockSize = GetBlockSize(ctx, pHeader); 802 | if ((pPrintOpts->kPtr >= kChunk) && (pPrintOpts->kPtr < (kChunk + (BlockSize * ctx->poolBlockSize)))) 803 | bHighlight = TRUE; 804 | } 805 | 806 | // if tabular and first chunk, print the header 807 | if (pPrintOpts->bTabular && pPrintOpts->count == 0) 808 | PrintTabularChunkHeader(ctx, pPrintOpts->indentLevel); 809 | 810 | PrintPoolHeader(ctx, kChunk, pHeader, pPrintOpts->bTabular, pPrintOpts->bRaw, pPrintOpts->indentLevel, bHighlight); 811 | 812 | pPrintOpts->count++; 813 | return TRUE; 814 | } 815 | 816 | 817 | 818 | BOOL CALLBACK EnumPoolProcFindHeader(PCMD_CTX ctx, WDBG_PTR kChunk, PVOID pHeader, LPARAM lParam) 819 | { 820 | PFIND_PTR find = (PFIND_PTR) lParam; 821 | ULONG BlockSize = GetBlockSize(ctx, pHeader); 822 | 823 | if (PTR_IN_CHUNK(ctx, find->kPointer, kChunk, BlockSize)) 824 | { 825 | find->kChunk = kChunk; 826 | return FALSE; // stop enumerating 827 | } 828 | 829 | return TRUE; 830 | } 831 | 832 | WDBG_PTR FindPoolHeader(PCMD_CTX ctx, WDBG_PTR kPtr) 833 | { 834 | FIND_PTR find = {0}; 835 | find.kChunk = NULL; 836 | find.kPointer = kPtr; 837 | 838 | EnumPoolPage(ctx, kPtr, (LPARAM)&find, EnumPoolProcFindHeader); 839 | 840 | return find.kChunk; 841 | } 842 | 843 | BOOL CALLBACK EnumListHeadsProcFindChunk(PCMD_CTX ctx, ULONG bucket, BOOL bEmpty, WDBG_PTR kChunk, ULONG count, LPARAM lParam) 844 | { 845 | BYTE header[8]; 846 | PFIND_PTR find = (PFIND_PTR)lParam; 847 | 848 | if (!bEmpty) 849 | { 850 | if (FAILED(ReadVirtual(ctx, kChunk, header, sizeof(header)))) 851 | return FALSE; 852 | 853 | ULONG BlockSize = GetBlockSize(ctx, header); 854 | 855 | if (PTR_IN_CHUNK(ctx, find->kPointer, kChunk, BlockSize)) 856 | { 857 | find->kChunk = kChunk; 858 | find->bucket = bucket; 859 | find->count = count; 860 | return FALSE; 861 | } 862 | } 863 | 864 | return TRUE; 865 | } 866 | 867 | BOOL CALLBACK EnumLookasideProcFindChunk(PCMD_CTX ctx, WDBG_PTR kLookasideBucket, ULONG bucket, BOOL bEmpty, WDBG_PTR kChunk, ULONG count, LPARAM lParam) 868 | { 869 | BYTE header[8]; 870 | PFIND_PTR find = (PFIND_PTR)lParam; 871 | 872 | if (!bEmpty) 873 | { 874 | if (FAILED(ReadVirtual(ctx, kChunk, header, sizeof(header)))) 875 | return FALSE; 876 | 877 | ULONG BlockSize = GetBlockSize(ctx, header); 878 | 879 | // is the pointer in this chunk? 880 | if (PTR_IN_CHUNK(ctx, find->kPointer, kChunk, BlockSize)) 881 | { 882 | find->kChunk = kChunk; 883 | find->bucket = bucket; 884 | find->count = count; 885 | find->kDesc = kLookasideBucket; 886 | return FALSE; 887 | } 888 | } 889 | 890 | return TRUE; 891 | } 892 | 893 | BOOL FindChunkInListHeads(PCMD_CTX ctx, WDBG_PTR kPoolDesc, WDBG_PTR kPtr, PFIND_PTR pFind) 894 | { 895 | pFind->kPointer = kPtr; 896 | 897 | if (EnumListHeads(ctx, kPoolDesc, BUCKET_ALL, (LPARAM)pFind, EnumListHeadsProcFindChunk) == FALSE) 898 | { 899 | // chunk found 900 | return TRUE; 901 | } 902 | else 903 | { 904 | // didn't find it 905 | return FALSE; 906 | } 907 | } 908 | 909 | BOOL FindChunkInLookasideBucket(PCMD_CTX ctx, WDBG_PTR kLookasideBucket, WDBG_PTR kPtr, ULONG bucket, WDBG_PTR kLookaside, const char *lookasideType) 910 | { 911 | BOOL bFound = FALSE; 912 | WDBG_PTR kPtrs[2], pCur, pNext; 913 | ULONG count = 0; 914 | ULONG BlockSize; 915 | BYTE header[POOL_HEADER_SIZE]; 916 | PSYM_FIELD listField = GetStructField(ctx->pStructGeneralLookasidePool, "SingleListHead"); 917 | 918 | // read the list head 919 | if (FAILED(ReadTargetPointerArray(ctx, kLookasideBucket + listField->fieldOffset, kPtrs, _countof(kPtrs)))) 920 | return FALSE; 921 | 922 | if (ctx->b64BitTarget) 923 | pCur = kPtrs[1]; 924 | else 925 | pCur = kPtrs[0]; 926 | 927 | while (pCur != NULL) 928 | { 929 | // read the pool header 930 | WDBG_PTR kChunk = pCur - (2 * ctx->pointerLen); 931 | if (FAILED(ReadVirtual(ctx, kChunk, &header, sizeof(header)))) 932 | break; 933 | 934 | BlockSize = GetBlockSize(ctx, header); 935 | 936 | // is the pointer in this chunk? 937 | if (PTR_IN_CHUNK(ctx, kPtr, kChunk, BlockSize)) 938 | { 939 | const char *name = GetLookasideType(ctx, kLookaside); 940 | dprintf("%p: #%u in %s Lookaside[%02X] (size=%03X) at %p\n", 941 | kPtr, count + 1, 942 | BUCKET_TO_SIZE(bucket, ctx->poolBlockSize), 943 | lookasideType, kLookaside, 944 | (name ? name : "Unknown"), 945 | bucket, 946 | BUCKET_TO_SIZE(bucket, ctx->poolBlockSize), 947 | kLookasideBucket); 948 | bFound = TRUE; 949 | break; 950 | } 951 | 952 | // read the next pointer 953 | if (FAILED(ReadTargetPointer(ctx, pCur, &pNext))) 954 | break; 955 | 956 | pCur = pNext; // go to next entry in the list 957 | count++; 958 | } 959 | 960 | 961 | return bFound; 962 | } 963 | 964 | BOOL FindFreeChunk(PCMD_CTX ctx, WDBG_PTR kPtr) 965 | { 966 | BOOL bFound = FALSE; 967 | POOL_DESC *pPoolDescs; 968 | LOOKASIDE_DESC *pLookasideDescs; 969 | DWORD numDescs = 0; 970 | DWORD i; 971 | FIND_PTR find = {0}; 972 | const char *poolName; 973 | ULONG poolIndex = 0; 974 | 975 | // chunk is free, this is harder as the PoolType will be 0 976 | // loop through all the pool descriptors and their ListHeads (yuck) 977 | // if not there, try the lookaside 978 | // if not there, ragequit 979 | 980 | pPoolDescs = GetAllPoolDescriptors(ctx, &numDescs); 981 | if (pPoolDescs == NULL) 982 | return FALSE; 983 | 984 | DEBUGPRINT("Searching ListHeads\n"); 985 | 986 | // optimization? if PoolType == 0 search ListHeads? 987 | for (i = 0; i < numDescs; i++) 988 | { 989 | DEBUGPRINT("Searching in pool %p\n", pPoolDescs[i].kPoolDesc); 990 | 991 | if (FindChunkInListHeads(ctx, pPoolDescs[i].kPoolDesc, kPtr, &find)) 992 | { 993 | poolName = GetPoolType(ctx, pPoolDescs[i].kPoolDesc, &poolIndex); 994 | OutputDml(ctx, 995 | "%p: #%u in ListHeads[%02X] in pool %s[%u] (%p)\n", 996 | kPtr, 997 | find.count + 1, 998 | BUCKET_TO_SIZE(find.bucket, ctx->poolBlockSize), 999 | pPoolDescs[i].kPoolDesc, 1000 | find.bucket, 1001 | poolName ? poolName : "Unknown", 1002 | poolIndex, 1003 | pPoolDescs[i].kPoolDesc); 1004 | 1005 | bFound = TRUE; 1006 | break; 1007 | } 1008 | } 1009 | 1010 | free(pPoolDescs); 1011 | pPoolDescs = NULL; 1012 | 1013 | if (!bFound) 1014 | { 1015 | DEBUGPRINT("Searching Lookasides\n"); 1016 | 1017 | pLookasideDescs = GetAllLookasides(ctx, &numDescs); 1018 | if (pLookasideDescs == NULL) 1019 | return FALSE; 1020 | 1021 | // optimization? if PoolType != 0 search Lookaside of correct pool... 1022 | 1023 | FIND_PTR find = {0}; 1024 | find.kPointer = kPtr; 1025 | 1026 | for (i = 0; i < numDescs; i++) 1027 | { 1028 | DEBUGPRINT("Searching lookaside %p\n", pLookasideDescs[i].kLookasideDesc); 1029 | 1030 | if (!EnumLookasides(ctx, &pLookasideDescs[i], BUCKET_ALL, (LPARAM)&find, EnumLookasideProcFindChunk)) 1031 | { 1032 | // enum returned false, we found the pointer 1033 | 1034 | const char *lookasideInfoType = GetLookasideInfoType(pLookasideDescs[i].poolType); 1035 | const char *lookasideName = GetLookasideType(ctx, find.kDesc); 1036 | 1037 | OutputDml(ctx, 1038 | "%p: #%u in %s Lookaside[%02X] (size=%03X) at %p\n", 1039 | kPtr, 1040 | find.count + 1, 1041 | BUCKET_TO_SIZE(find.bucket, ctx->poolBlockSize), 1042 | lookasideInfoType, 1043 | pLookasideDescs->kLookasideDesc, 1044 | (lookasideName ? lookasideName : "Unknown"), 1045 | find.bucket, 1046 | BUCKET_TO_SIZE(find.bucket, ctx->poolBlockSize), 1047 | find.kDesc); 1048 | 1049 | bFound = TRUE; 1050 | break; 1051 | } 1052 | } 1053 | 1054 | free(pLookasideDescs); 1055 | } 1056 | 1057 | return bFound; 1058 | } 1059 | 1060 | HRESULT PrintExtendedChunkInfo(PCMD_CTX ctx, WDBG_PTR kPtr) 1061 | { 1062 | HRESULT ret = S_OK; 1063 | ULONG PoolType; 1064 | ULONG PoolIndex; 1065 | BYTE header[POOL_HEADER_SIZE]; 1066 | POOL_DESC *poolDescs; 1067 | DWORD numDescs = 0; 1068 | WDBG_PTR kLookasideSession; 1069 | BOOL bFound = FALSE; 1070 | 1071 | // find the pool that this chunk is in. 1072 | if (FAILED(ret = ReadVirtual(ctx, kPtr, header, sizeof(header)))) 1073 | return ret; 1074 | 1075 | if (ctx->b64BitTarget) 1076 | { 1077 | PoolType = ((PPOOL_HEADER64)header)->PoolType; 1078 | PoolIndex = ((PPOOL_HEADER64)header)->PoolIndex; 1079 | } 1080 | else 1081 | { 1082 | PoolType = ((PPOOL_HEADER32)header)->PoolType; 1083 | PoolIndex = ((PPOOL_HEADER32)header)->PoolIndex; 1084 | } 1085 | 1086 | // is the chunk in-use? if so, we can get the pooltype 1087 | if (PoolType & ctx->poolInUse) 1088 | { 1089 | if ((PoolType & PagedPoolSession) == PagedPoolSession) 1090 | { 1091 | poolDescs = (POOL_DESC *) malloc(sizeof(WDBG_PTR) * 1); 1092 | if (poolDescs == NULL) 1093 | return E_OUTOFMEMORY; 1094 | numDescs = 1; 1095 | poolDescs[0].kPoolDesc = GetSessionPool(ctx, &kLookasideSession); 1096 | poolDescs[0].poolType = POOL_TYPE_SESSION; 1097 | } 1098 | else if ((PoolType & PagedPool) == PagedPool) 1099 | { 1100 | poolDescs = GetPagedPools(ctx, &numDescs); 1101 | } 1102 | else 1103 | { 1104 | poolDescs = GetNonPagedPools(ctx, &numDescs); 1105 | } 1106 | 1107 | // does the PoolIndex make sense? 1108 | if (PoolIndex >= numDescs) 1109 | { 1110 | dprintf("PoolIndex %02X is too large for the %s\n", 1111 | PoolIndex, PoolTypeName((POOL_TYPE)PoolType)); 1112 | ret = E_FAIL; 1113 | } 1114 | else 1115 | { 1116 | const char *poolName = GetPoolType(ctx, poolDescs[PoolIndex].kPoolDesc, NULL); 1117 | dprintf("%p is InUse on %s[%u], page: ", kPtr, (poolName ? poolName : "Unknown"), PoolIndex); 1118 | OutputDml(ctx, "%p\n", PAGE_ALIGN(kPtr), PAGE_ALIGN(kPtr)); 1119 | } 1120 | } 1121 | else 1122 | { 1123 | dprintf("chunk appears to be free, searching descriptors...\n"); 1124 | 1125 | bFound = FindFreeChunk(ctx, kPtr); 1126 | 1127 | if (!bFound) 1128 | { 1129 | dprintf("Failed to locate %p\n", kPtr); 1130 | } 1131 | } 1132 | 1133 | return ret; 1134 | } 1135 | 1136 | -------------------------------------------------------------------------------- /src/poolinfo/pool.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // 4 | // structure definitions 5 | // 6 | typedef struct _PRINT_OPTS 7 | { 8 | BOOL bRaw; 9 | BOOL bHighlight; 10 | BOOL bTabular; 11 | BOOL bPrintIfEmpty; 12 | BOOL bSummary; 13 | ULONG indentLevel; 14 | WDBG_PTR kPtr; 15 | ULONG count; 16 | } 17 | PRINT_OPTS, *PPRINT_OPTS; 18 | 19 | typedef struct _FIND_PTR 20 | { 21 | WDBG_PTR kPointer; 22 | WDBG_PTR kChunk; 23 | // list heads stuff 24 | ULONG bucket; 25 | ULONG count; 26 | WDBG_PTR kDesc; 27 | } 28 | FIND_PTR, *PFIND_PTR; 29 | 30 | // structure to keep track of a pool descriptor 31 | typedef struct _POOL_DESC 32 | { 33 | WDBG_PTR kPoolDesc; 34 | ULONG poolType; 35 | } 36 | POOL_DESC, *PPOOL_DESC; 37 | 38 | // structure to keep track of a lookaside descriptor 39 | typedef struct _LOOKASIDE_DESC 40 | { 41 | WDBG_PTR kLookasideDesc; 42 | ULONG poolType; 43 | ULONG processor; 44 | ULONG numBuckets; 45 | ULONG structSize; 46 | } 47 | LOOKASIDE_DESC, *PLOOKASIDE_DESC; 48 | 49 | // 50 | // macros 51 | // 52 | #define PTR_IN_CHUNK(ctx, ptr, chunk, blocksize) \ 53 | ( ( (ptr) >= (chunk) ) && ( (ptr) < ( (chunk) + ((blocksize) * (ctx)->poolBlockSize) ) ) ) 54 | 55 | // 56 | // functions 57 | // 58 | 59 | typedef BOOL (CALLBACK* ENUMPAGEPROC)(PCMD_CTX ctx, WDBG_PTR kChunk, PVOID pHeader, LPARAM lParam); 60 | typedef BOOL (CALLBACK* ENUMLISTHEADSPROC)(PCMD_CTX ctx, ULONG bucket, BOOL bEmpty, WDBG_PTR kChunk, ULONG count, LPARAM lParam); 61 | typedef BOOL (CALLBACK* ENUMLOOKASIDEPROC)(PCMD_CTX ctx, WDBG_PTR kLookasideBucket, ULONG bucket, BOOL bEmpty, WDBG_PTR kChunk, ULONG count, LPARAM lParam); 62 | 63 | 64 | ULONG GetBlockSize(PCMD_CTX ctx, PVOID pHeader); 65 | void GetPoolTag(ULONG32 tagDword, char *tag); 66 | const char *PoolTypeName(POOL_TYPE poolType); 67 | const char *GetPoolType(PCMD_CTX ctx, WDBG_PTR kPoolDescriptor, ULONG *pPoolIndex); 68 | const char *GetLookasideType(PCMD_CTX ctx, WDBG_PTR kLookaside); 69 | const char *GetLookasideInfoType(ULONG poolType); 70 | 71 | 72 | // 73 | // get the paged pool info 74 | // 75 | POOL_DESC *GetPagedPools(PCMD_CTX ctx, PDWORD numPools); 76 | 77 | // 78 | // get the non-paged pools 79 | // this is from tarjei mandt's kernel pool paper... 80 | // 81 | POOL_DESC *GetNonPagedPools(PCMD_CTX ctx, PDWORD numPools); 82 | 83 | WDBG_PTR GetSessionLookaside(PCMD_CTX ctx); 84 | WDBG_PTR GetSessionPool(PCMD_CTX ctx, PWDBG_PTR pkptrLookaside); 85 | 86 | POOL_DESC *GetAllPoolDescriptors(PCMD_CTX ctx, ULONG *pNum); 87 | 88 | 89 | // Get the kernel pointer to the specified field of the current KPRCB 90 | WDBG_PTR GetPrcbFieldOffset(PCMD_CTX ctx, WDBG_PTR kprcb, const char *fieldName); 91 | 92 | // 93 | // fetch the specified lookaside from the list KPRCB 94 | // 95 | LOOKASIDE_DESC *GetKPRCBLookasides(PCMD_CTX ctx, const char *lookasideName, ULONG lookasideType, PULONG pNum); 96 | LOOKASIDE_DESC *GetNonPagedLookasides(PCMD_CTX ctx, PULONG pNum); 97 | LOOKASIDE_DESC *GetNonPagedNxLookasides(PCMD_CTX ctx, PULONG pNum); 98 | LOOKASIDE_DESC *GetPagedLookasides(PCMD_CTX ctx, PULONG pNum); 99 | LOOKASIDE_DESC *GetSessionLookasides(PCMD_CTX ctx, PULONG pNum); 100 | LOOKASIDE_DESC *GetAllLookasides(PCMD_CTX ctx, PULONG pNum); 101 | 102 | 103 | 104 | 105 | void PrintTabularChunkHeader(PCMD_CTX ctx, ULONG indentLevel); 106 | 107 | template 108 | HRESULT PrintPoolHeaderTempl(PCMD_CTX ctx, WDBG_PTR kPtrChunk, HeaderType *hdr, BOOL bTabular, BOOL bRaw, ULONG indentLevel, BOOL bHighlight); 109 | HRESULT PrintPoolHeader(PCMD_CTX ctx, WDBG_PTR kPtrChunk, PVOID hdr, BOOL bTabular, BOOL bRaw, ULONG indentLevel, BOOL bHighlight); 110 | 111 | BOOL EnumPoolPage(PCMD_CTX ctx, WDBG_PTR kPtr, LPARAM lParam, ENUMPAGEPROC enumProc); 112 | 113 | BOOL CALLBACK EnumProcPrintHeader(PCMD_CTX ctx, WDBG_PTR kChunk, PVOID pHeader, LPARAM lParam); 114 | 115 | BOOL EnumListHeadsBucket(PCMD_CTX ctx, WDBG_PTR kPoolDesc, ULONG bucket, LPARAM lParam, ENUMLISTHEADSPROC enumProc); 116 | BOOL EnumListHeads(PCMD_CTX ctx, WDBG_PTR kPoolDesc, ULONG bucket, LPARAM lParam, ENUMLISTHEADSPROC enumProc); 117 | 118 | BOOL EnumLookasideBucket(PCMD_CTX ctx, LOOKASIDE_DESC *pLookasideDesc, ULONG bucket, LPARAM lParam, ENUMLOOKASIDEPROC enumProc); 119 | BOOL EnumLookasides(PCMD_CTX ctx, LOOKASIDE_DESC *pLookasideDesc, ULONG bucket, LPARAM lParam, ENUMLOOKASIDEPROC enumProc); 120 | 121 | WDBG_PTR FindPoolHeader(PCMD_CTX ctx, WDBG_PTR kPtr); 122 | BOOL FindChunkInListHeads(PCMD_CTX ctx, WDBG_PTR kPoolDesc, WDBG_PTR kPtr); 123 | 124 | BOOL FindChunkInLookasideBucket(PCMD_CTX ctx, WDBG_PTR kLookasideBucket, WDBG_PTR kPtr, ULONG bucket, WDBG_PTR kLookaside, const char *lookasideType); 125 | BOOL FindChunkInLookaside(PCMD_CTX ctx, WDBG_PTR kLookaside, ULONG lookasideStructSize, ULONG lookasideBuckets, WDBG_PTR kPtr, const char *lookasideType); 126 | BOOL FindChunkInLookasides(PCMD_CTX ctx, WDBG_PTR *kLookasides, ULONG numLookasides, ULONG lookasideStructSize, ULONG lookasideBuckets, WDBG_PTR kPtr, const char *lookasideType); 127 | BOOL FindFreeChunk(PCMD_CTX ctx, WDBG_PTR kPtr); 128 | 129 | HRESULT PrintExtendedChunkInfo(PCMD_CTX ctx, WDBG_PTR kPtr); 130 | -------------------------------------------------------------------------------- /src/poolinfo/poolinfo.cpp: -------------------------------------------------------------------------------- 1 | #include "stdafx.h" 2 | 3 | #include "SimpleOpt.h" 4 | 5 | //#define USE_TABULAR_FOR_FREELIST 6 | 7 | // globals 8 | WINDBG_EXTENSION_APIS ExtensionApis = {0}; 9 | 10 | #ifdef _DEBUG 11 | BOOL g_bDebug = TRUE; 12 | #else 13 | BOOL g_bDebug = FALSE; 14 | #endif 15 | 16 | 17 | 18 | // command line option ids 19 | CSimpleOptA::SOption g_pp_opts[] = { 20 | HELP_OPTS, 21 | { OPT_PP_RAWHDR, "-r", SO_NONE }, 22 | { OPT_PP_RAWHDR, "--raw", SO_NONE }, 23 | SO_END_OF_OPTIONS 24 | }; 25 | 26 | void Usage_CmdPoolPage(PCMD_CTX ctx) 27 | { 28 | OutputDml(ctx, "!poolpage"); dprintf(" [options] address\n"); 29 | dprintf( 30 | " options:\n" 31 | " -r, --raw display the raw POOL_HEADER structures\n" 32 | ); 33 | } 34 | 35 | HRESULT CALLBACK poolpage(PDEBUG_CLIENT4 pClient, PCSTR args) 36 | { 37 | HRESULT ret = S_OK; 38 | 39 | CMD_CTX ctx; 40 | CSimpleOptA cs; 41 | 42 | char **argv = NULL; 43 | int argc; 44 | BOOL bRaw = FALSE; 45 | 46 | WDBG_PTR address = 0; 47 | WDBG_PTR page; 48 | 49 | BYTE pageContents[PAGE_SIZE]; 50 | 51 | 52 | // initialize the command context 53 | if (FAILED(ret = InitCmdCtx(&ctx, pClient))) 54 | return ret; 55 | 56 | // parse !poolpage options 57 | argv = MakeCmdline("!poolpage", args, &argc); 58 | if (argv == NULL) 59 | { 60 | ret = E_OUTOFMEMORY; 61 | goto cleanup; 62 | } 63 | 64 | cs.Init(argc, argv, g_pp_opts); 65 | while (cs.Next()) 66 | { 67 | if (cs.LastError() == SO_SUCCESS) 68 | { 69 | // handle options 70 | switch (cs.OptionId()) 71 | { 72 | default: 73 | case OPT_HELP: 74 | Usage_CmdPoolPage(&ctx); 75 | goto cleanup; 76 | break; 77 | case OPT_PP_RAWHDR: 78 | bRaw = TRUE; 79 | break; 80 | } 81 | } 82 | } 83 | 84 | // make sure an address is specified 85 | if (cs.FileCount() == 0) 86 | { 87 | Usage_CmdPoolPage(&ctx); 88 | goto cleanup; 89 | } 90 | 91 | // resolve the expression 92 | address = GetExpressionArgv(cs.Files(), cs.FileCount()); 93 | 94 | // get the page base 95 | page = PAGE_ALIGN(address); 96 | 97 | // fetch the entire pool page 98 | if (FAILED(ret = ReadVirtual(&ctx, page, pageContents, PAGE_SIZE))) 99 | { 100 | dprintf("invalid address: %p\n", page); 101 | ret = S_OK; 102 | goto cleanup; 103 | } 104 | 105 | // start walking the page 106 | dprintf("walking pool page @ %p\n", page); 107 | 108 | // enum the pool page 109 | PRINT_OPTS printOpts; 110 | memset(&printOpts, 0, sizeof(printOpts)); 111 | printOpts.bHighlight = TRUE; 112 | printOpts.bRaw = bRaw; 113 | printOpts.bTabular = TRUE; 114 | printOpts.indentLevel = 0; 115 | printOpts.kPtr = address; 116 | printOpts.count = 0; 117 | 118 | EnumPoolPage(&ctx, address, (LPARAM)&printOpts, EnumProcPrintHeader); 119 | 120 | cleanup: 121 | if (argv != NULL) 122 | free(argv); 123 | 124 | ReleaseCmdCtx(&ctx); 125 | return ret; 126 | } 127 | 128 | 129 | void PrintPoolDescInfo(PCMD_CTX ctx, PPOOL_DESC pPoolDesc) 130 | { 131 | char buf[50]; 132 | const char *name; 133 | ULONG poolIndex; 134 | 135 | name = GetPoolType(ctx, pPoolDesc->kPoolDesc, &poolIndex); 136 | sprintf_s(buf, sizeof(buf), "%s[%u]:", (name ? name : "Unknown"), poolIndex); 137 | 138 | OutputDml(ctx, 139 | " %-30s %p\n", 140 | buf, pPoolDesc->kPoolDesc, pPoolDesc->kPoolDesc); 141 | } 142 | 143 | void PrintLookasideInfo(PCMD_CTX ctx, PLOOKASIDE_DESC pLookasideDesc) 144 | { 145 | char buf[50]; 146 | const char *name; 147 | const char *infotype = GetLookasideInfoType(pLookasideDesc->poolType); 148 | 149 | name = GetLookasideType(ctx, pLookasideDesc->kLookasideDesc); 150 | sprintf_s(buf, sizeof(buf), "Lookaside %s[%u]:", (name ? name : "Unknown"), pLookasideDesc->processor); 151 | 152 | OutputDml(ctx, 153 | " %-30s %p\n", 154 | buf, 155 | infotype, 156 | pLookasideDesc->kLookasideDesc, pLookasideDesc->kLookasideDesc); 157 | } 158 | 159 | CSimpleOptA::SOption g_pl_opts[] = { 160 | HELP_OPTS, 161 | { OPT_PL_ALL, "-a", SO_NONE }, 162 | { OPT_PL_ALL, "--all", SO_NONE }, 163 | { OPT_PL_NONPAGED, "-n", SO_NONE }, 164 | { OPT_PL_NONPAGED, "--nonpaged", SO_NONE }, 165 | { OPT_PL_PAGED, "-p", SO_NONE }, 166 | { OPT_PL_PAGED, "--paged", SO_NONE }, 167 | { OPT_PL_SESSION, "-s", SO_NONE }, 168 | { OPT_PL_SESSION, "--session", SO_NONE }, 169 | { OPT_PL_LOOKASIDE, "-l", SO_NONE }, 170 | { OPT_PL_LOOKASIDE, "--lookaside", SO_NONE }, 171 | SO_END_OF_OPTIONS 172 | }; 173 | 174 | 175 | void Usage_CmdPoolList(PCMD_CTX ctx) 176 | { 177 | OutputDml(ctx, "!poollist"); dprintf(" [options] [pool types]\n"); 178 | dprintf( 179 | " types:\n" 180 | " -a, --all display all pool descriptors (default)\n" 181 | " -n, --nonpaged display the NonPaged pool descriptors\n" 182 | " -p, --paged display the Paged pool descriptors\n" 183 | " -s, --session display the Session pool descriptors\n" 184 | " options:\n" 185 | " -l, --lookaside display the lookaside descriptors as well\n" 186 | ); 187 | } 188 | 189 | HRESULT CALLBACK poollist(PDEBUG_CLIENT4 pClient, PCSTR args) 190 | { 191 | HRESULT ret = S_OK; 192 | 193 | CMD_CTX ctx; 194 | CSimpleOptA cs; 195 | 196 | char **argv = NULL; 197 | int argc; 198 | 199 | DWORD poolTypes = 0; 200 | BOOL bLookaside = FALSE; 201 | 202 | DWORD i; 203 | DWORD numDescs; 204 | 205 | POOL_DESC *pPoolDescs = NULL; 206 | LOOKASIDE_DESC *pLookasideDescs = NULL; 207 | 208 | // initialize the command context 209 | if (FAILED(ret = InitCmdCtx(&ctx, pClient))) 210 | return ret; 211 | 212 | // parse !poolpage options 213 | argv = MakeCmdline("!poollist", args, &argc); 214 | if (argv == NULL) 215 | { 216 | ret = E_OUTOFMEMORY; 217 | goto cleanup; 218 | } 219 | 220 | cs.Init(argc, argv, g_pl_opts); 221 | while (cs.Next()) 222 | { 223 | if (cs.LastError() == SO_SUCCESS) 224 | { 225 | // handle options 226 | switch (cs.OptionId()) 227 | { 228 | default: 229 | case OPT_HELP: 230 | Usage_CmdPoolList(&ctx); 231 | goto cleanup; 232 | break; 233 | case OPT_PL_NONPAGED: 234 | poolTypes |= POOL_TYPE_NONPAGED; 235 | break; 236 | case OPT_PL_PAGED: 237 | poolTypes |= POOL_TYPE_PAGED; 238 | break; 239 | case OPT_PL_SESSION: 240 | poolTypes |= POOL_TYPE_SESSION; 241 | break; 242 | case OPT_PL_ALL: 243 | poolTypes = 0xffffffff; 244 | break; 245 | case OPT_PL_LOOKASIDE: 246 | bLookaside = TRUE; 247 | break; 248 | } 249 | } 250 | } 251 | 252 | // if none specified, do all 253 | if (poolTypes == 0) 254 | poolTypes = 0xffffffff; 255 | 256 | // get all the pool descriptors 257 | pPoolDescs = GetAllPoolDescriptors(&ctx, &numDescs); 258 | if (pPoolDescs != NULL) 259 | { 260 | dprintf("Pool Descriptors\n"); 261 | for (i = 0; i < numDescs; i++) 262 | { 263 | if (pPoolDescs[i].poolType & poolTypes) 264 | PrintPoolDescInfo(&ctx, &pPoolDescs[i]); 265 | } 266 | } 267 | 268 | if (bLookaside) 269 | { 270 | pLookasideDescs = GetAllLookasides(&ctx, &numDescs); 271 | if (pLookasideDescs != NULL) 272 | { 273 | dprintf("Lookaside Descriptors\n"); 274 | for (i = 0; i < numDescs; i++) 275 | { 276 | if (pLookasideDescs[i].poolType & poolTypes) 277 | PrintLookasideInfo(&ctx, &pLookasideDescs[i]); 278 | } 279 | } 280 | } 281 | 282 | cleanup: 283 | if (pPoolDescs != NULL) 284 | free(pPoolDescs); 285 | 286 | if (pLookasideDescs != NULL) 287 | free(pLookasideDescs); 288 | 289 | if (argv != NULL) 290 | free(argv); 291 | 292 | ReleaseCmdCtx(&ctx); 293 | return ret; 294 | } 295 | 296 | BOOL CALLBACK EnumListHeadsProcPrint(PCMD_CTX ctx, ULONG bucket, BOOL bEmpty, WDBG_PTR kChunk, ULONG count, LPARAM lParam) 297 | { 298 | PPRINT_OPTS print = (PPRINT_OPTS)lParam; 299 | 300 | if (bEmpty) 301 | { 302 | if (print->bPrintIfEmpty) 303 | { 304 | dprintf("ListHeads[%02X]: size=%03X\n", bucket, BUCKET_TO_SIZE(bucket, ctx->poolBlockSize)); 305 | for (ULONG i = 0; i < print->indentLevel; i++) dprintf(" "); 306 | dprintf("Empty\n"); 307 | } 308 | } 309 | else 310 | { 311 | if (count == 0) 312 | dprintf("ListHeads[%02X]: size=%03X\n", bucket, BUCKET_TO_SIZE(bucket, ctx->poolBlockSize)); 313 | 314 | if (print->bTabular) 315 | PrintTabularChunkHeader(ctx, print->indentLevel); 316 | 317 | PrintPoolHeader(ctx, kChunk, NULL, print->bTabular, print->bRaw, print->indentLevel, print->bHighlight); 318 | } 319 | 320 | return TRUE; 321 | } 322 | 323 | 324 | HRESULT PrintPoolDescriptor(PCMD_CTX ctx, WDBG_PTR ptr, BOOL bSummary) 325 | { 326 | HRESULT ret; 327 | PSYM_FIELD pStructDesc; 328 | PVOID pStructData = NULL; 329 | 330 | pStructDesc = CloneStruct(ctx->pStructPoolDescriptor); 331 | if (pStructDesc == NULL) 332 | return E_OUTOFMEMORY; 333 | 334 | if (FAILED(ret = ReadVirtualStruct(ctx, ptr, pStructDesc, &pStructData))) 335 | { 336 | free(pStructDesc); 337 | return ret; 338 | } 339 | 340 | // print the descriptor details 341 | dprintf("Pool Descriptor %s[%u] at %p\n", 342 | PoolTypeName((POOL_TYPE) GS_ULONG(pStructDesc, "PoolType")), 343 | GS_ULONG32(pStructDesc, "PoolIndex"), 344 | ptr); 345 | 346 | dprintf(" PoolType: %02X (%s)\n", 347 | GS_ULONG(pStructDesc, "PoolType"), 348 | PoolTypeName((POOL_TYPE) GS_ULONG(pStructDesc, "PoolType"))); 349 | 350 | dprintf(" PoolIndex: %08X\n", GS_ULONG32(pStructDesc, "PoolIndex")); 351 | dprintf(" PendingFreeDepth: %08X\n", GS_LONG32(pStructDesc, "PendingFreeDepth")); 352 | 353 | if (!bSummary) 354 | { 355 | dprintf(" RunningAllocs: %08X\n", GS_LONG32(pStructDesc, "RunningAllocs")); 356 | dprintf(" RunningDeAllocs: %08X\n", GS_LONG32(pStructDesc, "RunningDeAllocs")); 357 | dprintf(" TotalBigPages: %08X\n", GS_LONG32(pStructDesc, "TotalBigPages")); 358 | dprintf(" ThreadsProcessingDeferrals: %08X\n", GS_LONG32(pStructDesc, "ThreadsProcessingDeferrals")); 359 | dprintf(" TotalBytes: %08X\n", GS_ULONG32(pStructDesc, "TotalBytes")); 360 | dprintf(" TotalPages: %08X\n", GS_ULONG32(pStructDesc, "TotalPages")); 361 | } 362 | 363 | free(pStructDesc); 364 | free(pStructData); 365 | 366 | return ret; 367 | } 368 | 369 | HRESULT PrintLookaside(PCMD_CTX ctx, WDBG_PTR ptr, BOOL bSummary) 370 | { 371 | HRESULT ret; 372 | PSYM_FIELD pStructDesc = NULL; 373 | PVOID pStructData = NULL; 374 | char tag[5]; 375 | 376 | pStructDesc = CloneStruct(ctx->pStructGeneralLookasidePool); 377 | if (pStructDesc == NULL) 378 | return E_OUTOFMEMORY; 379 | 380 | if (FAILED(ret = ReadVirtualStruct(ctx, ptr, pStructDesc, &pStructData))) 381 | { 382 | free(pStructDesc); 383 | return ret; 384 | } 385 | 386 | // print the lookaside details 387 | dprintf(" Type: %02X (%s)\n", 388 | GS_ULONG(pStructDesc, "Type"), 389 | PoolTypeName((POOL_TYPE) GS_ULONG(pStructDesc, "Type"))); 390 | 391 | GetPoolTag(GS_ULONG32(pStructDesc, "Tag"), tag); 392 | dprintf(" Size: %08X\n", GS_ULONG32(pStructDesc, "Size")); 393 | dprintf(" Tag: %08X (%s)\n", GS_ULONG32(pStructDesc, "Tag"), tag); 394 | 395 | if (!bSummary) 396 | { 397 | dprintf(" Depth: %04X\n", GS_WORD(pStructDesc, "Depth")); 398 | dprintf(" MaximumDepth: %04X\n", GS_WORD(pStructDesc, "MaximumDepth")); 399 | //dprintf(" AllocateMisses: %08X\n", GS_LONG32(pStructDesc, "AllocateMisses")); 400 | dprintf(" AllocateHits: %08X\n", GS_ULONG32(pStructDesc, "AllocateHits")); 401 | dprintf(" TotalFrees: %08X\n", GS_ULONG32(pStructDesc, "TotalFrees")); 402 | //dprintf(" FreeMisses: %08X\n", GS_ULONG32(pStructDesc, "FreeMisses")); 403 | dprintf(" FreeHits: %08X\n", GS_ULONG32(pStructDesc, "FreeHits")); 404 | } 405 | 406 | free(pStructDesc); 407 | free(pStructData); 408 | 409 | return ret; 410 | } 411 | 412 | BOOL FindLookasideFromPointer(PCMD_CTX ctx, WDBG_PTR kLookaside, PLOOKASIDE_DESC pLookasideDesc) 413 | { 414 | BOOL bRet = FALSE; 415 | ULONG i, num = 0; 416 | PLOOKASIDE_DESC pDescs; 417 | 418 | pDescs = GetAllLookasides(ctx, &num); 419 | if (pDescs != NULL) 420 | { 421 | for (i = 0; i < num; i++) 422 | { 423 | if (pDescs[i].kLookasideDesc == kLookaside) 424 | { 425 | memcpy(pLookasideDesc, &pDescs[i], sizeof(LOOKASIDE_DESC)); 426 | bRet = TRUE; 427 | break; 428 | } 429 | } 430 | 431 | free(pDescs); 432 | } 433 | 434 | return bRet; 435 | } 436 | 437 | BOOL CALLBACK EnumLookasideProcPrint(PCMD_CTX ctx, WDBG_PTR kLookasideBucket, ULONG bucket, BOOL bEmpty, WDBG_PTR kChunk, ULONG count, LPARAM lParam) 438 | { 439 | PPRINT_OPTS print = (PPRINT_OPTS)lParam; 440 | 441 | if (count == 0 && (!bEmpty || (bEmpty && print->bPrintIfEmpty))) 442 | { 443 | dprintf("Lookaside[%02X]: size=%03X, %p\n", bucket, BUCKET_TO_SIZE(bucket, ctx->poolBlockSize), kLookasideBucket); 444 | // XXX: print more header stuff here... 445 | if (!print->bSummary) 446 | PrintLookaside(ctx, kLookasideBucket, FALSE); 447 | } 448 | 449 | if (bEmpty) 450 | { 451 | if (print->bPrintIfEmpty) 452 | { 453 | for (ULONG i = 0; i < print->indentLevel; i++) dprintf(" "); 454 | dprintf("Empty\n"); 455 | } 456 | } 457 | else 458 | { 459 | if (print->bTabular) 460 | PrintTabularChunkHeader(ctx, print->indentLevel); 461 | 462 | PrintPoolHeader(ctx, kChunk, NULL, print->bTabular, print->bRaw, print->indentLevel, print->bHighlight); 463 | } 464 | 465 | return TRUE; 466 | } 467 | 468 | CSimpleOptA::SOption g_pi_opts[] = { 469 | HELP_OPTS, 470 | { OPT_PI_SUMMARY, "-s", SO_NONE }, 471 | { OPT_PI_SUMMARY, "--summary", SO_NONE }, 472 | { OPT_PI_VERBOSE, "-v", SO_NONE }, 473 | { OPT_PI_VERBOSE, "--verbose", SO_NONE }, 474 | { OPT_PI_FREELIST, "-f", SO_NONE }, 475 | { OPT_PI_FREELIST, "--free", SO_NONE }, 476 | { OPT_PI_BUCKET, "-b", SO_REQ_SEP }, 477 | { OPT_PI_BUCKET, "--bucket", SO_REQ_SEP }, 478 | { OPT_PI_LOOKASIDE, "-l", SO_NONE }, 479 | { OPT_PI_LOOKASIDE, "--lookaside", SO_NONE }, 480 | { OPT_PI_LOOKTYPE, "-t", SO_REQ_SEP }, 481 | { OPT_PI_LOOKTYPE, "--looktype", SO_REQ_SEP }, 482 | { OPT_PI_NUMBUCKETS,"-lb", SO_REQ_SEP }, 483 | { OPT_PI_NUMBUCKETS,"--numbuckets", SO_REQ_SEP }, 484 | { OPT_PI_LOOKSIZE, "-ls", SO_REQ_SEP }, 485 | { OPT_PI_LOOKSIZE, "--looksize", SO_REQ_SEP }, 486 | SO_END_OF_OPTIONS 487 | }; 488 | 489 | 490 | void Usage_CmdPoolInfo(PCMD_CTX ctx) 491 | { 492 | OutputDml(ctx, "!poolinfo"); dprintf(" [options] \n"); 493 | dprintf( 494 | " pool options:\n" 495 | " -s, --summary show a summary of the pool descriptor (default)\n" 496 | " -v, --verbose display a full dump of the pool descriptor\n" 497 | " -f, --free show the freelist (ListHeads)\n" 498 | " -b, --bucket the bucket to show in the freelist (default is all)\n" 499 | " lookaside options:\n" 500 | " -l, --lookaside treat the supplied address as a lookaside pointer\n" 501 | " -t, --looktype the lookaside type. one of: nonpaged, paged, session\n" 502 | " this option specifies the # of buckets in the list\n" 503 | " and the size of the lookaside structure itself\n" 504 | " -b, --bucket the bucket to show from the lookaside\n" 505 | " other lookaside options\n" 506 | " -lb, --numbuckets <#> override the number of lookaside buckets\n" 507 | " -ls, --looksize override the size of the lookaside structure\n" 508 | ); 509 | } 510 | 511 | 512 | HRESULT CALLBACK poolinfo(PDEBUG_CLIENT4 pClient, PCSTR args) 513 | { 514 | HRESULT ret = S_OK; 515 | CMD_CTX ctx; 516 | CSimpleOptA cs; 517 | 518 | char **argv = NULL; 519 | int argc; 520 | 521 | // options 522 | BOOL bSummary = TRUE; 523 | BOOL bFreeList = FALSE; 524 | BOOL bLookaside = FALSE; 525 | ULONG sizeBucket = BUCKET_ALL; 526 | WDBG_PTR address = 0; 527 | ULONG lookasideStructSize = 0; 528 | ULONG lookasideBuckets = 0; 529 | ULONG bucket; 530 | 531 | // initialize the command context 532 | if (FAILED(ret = InitCmdCtx(&ctx, pClient))) 533 | return ret; 534 | 535 | // parse !poolpage options 536 | argv = MakeCmdline("!poolinfo", args, &argc); 537 | if (argv == NULL) 538 | { 539 | ret = E_OUTOFMEMORY; 540 | goto cleanup; 541 | } 542 | 543 | cs.Init(argc, argv, g_pi_opts); 544 | while (cs.Next()) 545 | { 546 | if (cs.LastError() == SO_SUCCESS) 547 | { 548 | // handle options 549 | switch (cs.OptionId()) 550 | { 551 | default: 552 | case OPT_HELP: 553 | Usage_CmdPoolInfo(&ctx); 554 | goto cleanup; 555 | case OPT_PI_SUMMARY: 556 | bSummary = TRUE; 557 | break; 558 | case OPT_PI_VERBOSE: 559 | bSummary = FALSE; 560 | break; 561 | case OPT_PI_FREELIST: 562 | bFreeList = TRUE; 563 | break; 564 | case OPT_PI_BUCKET: 565 | sizeBucket = strtoul(cs.OptionArg(), NULL, 16); 566 | break; 567 | case OPT_PI_LOOKASIDE: 568 | bLookaside = TRUE; 569 | break; 570 | case OPT_PI_LOOKTYPE: 571 | if ((_stricmp(cs.OptionArg(), "nonpaged") == 0) || 572 | (_stricmp(cs.OptionArg(), "paged") == 0)) 573 | { 574 | lookasideStructSize = ctx.lookasideStructSize; 575 | lookasideBuckets = ctx.lookasideBuckets; 576 | } 577 | else if (_stricmp(cs.OptionArg(), "session") == 0) 578 | { 579 | lookasideStructSize = ctx.sessionLookasideStructSize; 580 | lookasideBuckets = ctx.sessionLookasideBuckets; 581 | } 582 | else 583 | { 584 | // bad argument 585 | Usage_CmdPoolInfo(&ctx); 586 | goto cleanup; 587 | } 588 | break; 589 | case OPT_PI_NUMBUCKETS: 590 | lookasideBuckets = strtoul(cs.OptionArg(), NULL, 16); 591 | case OPT_PI_LOOKSIZE: 592 | lookasideStructSize = strtoul(cs.OptionArg(), NULL, 16); 593 | break; 594 | } 595 | } 596 | } 597 | 598 | // do the arguments make sense? 599 | if (bLookaside) 600 | { 601 | // XXX: convert sizeBucket from size to bucket 602 | bucket = SIZE_TO_BUCKET(sizeBucket, ctx.poolBlockSize); 603 | 604 | if (lookasideBuckets == 0 || lookasideStructSize == 0) 605 | { 606 | Usage_CmdPoolInfo(&ctx); 607 | goto cleanup; 608 | } 609 | 610 | if (sizeBucket != BUCKET_ALL && bucket >= lookasideBuckets) 611 | { 612 | dprintf("bucket size is too large. must be between 0 and %02X\n\n", 613 | BUCKET_TO_SIZE(lookasideBuckets-1, ctx.poolBlockSize)); 614 | Usage_CmdPoolInfo(&ctx); 615 | goto cleanup; 616 | } 617 | } 618 | 619 | // make sure an address is specified 620 | if (cs.FileCount() == 0) 621 | { 622 | Usage_CmdPoolInfo(&ctx); 623 | goto cleanup; 624 | } 625 | 626 | // resolve the expression 627 | address = GetExpressionArgv(cs.Files(), cs.FileCount()); 628 | 629 | if (bLookaside) 630 | { 631 | LOOKASIDE_DESC lookasideDesc = {0}; 632 | if (!FindLookasideFromPointer(&ctx, address, &lookasideDesc)) 633 | { 634 | dprintf("%p doesn't look like a real lookaside address, aborting\n", address); 635 | } 636 | else 637 | { 638 | bucket = sizeBucket; 639 | if (bucket != BUCKET_ALL) 640 | bucket = SIZE_TO_BUCKET(bucket, ctx.poolBlockSize); 641 | 642 | PRINT_OPTS print = {0}; 643 | print.indentLevel = 2; 644 | print.bPrintIfEmpty = FALSE; 645 | print.bSummary = bSummary; 646 | 647 | EnumLookasides(&ctx, &lookasideDesc, bucket, (LPARAM)&print, EnumLookasideProcPrint); 648 | } 649 | } 650 | else 651 | { 652 | // the pointer was a pool descriptor 653 | 654 | // dump the pool descriptor 655 | PrintPoolDescriptor(&ctx, address, bSummary); 656 | 657 | // if specified, dump the freelist(s) 658 | if (bFreeList) 659 | { 660 | bucket = sizeBucket; 661 | if (bucket != BUCKET_ALL) 662 | bucket = SIZE_TO_BUCKET(bucket, ctx.poolBlockSize); 663 | 664 | PRINT_OPTS print = {0}; 665 | print.indentLevel = 2; 666 | print.bPrintIfEmpty = FALSE; 667 | 668 | EnumListHeads(&ctx, address, bucket, (LPARAM)&print, EnumListHeadsProcPrint); 669 | } 670 | } 671 | 672 | 673 | cleanup: 674 | if (argv != NULL) 675 | free(argv); 676 | 677 | ReleaseCmdCtx(&ctx); 678 | return ret; 679 | } 680 | 681 | 682 | 683 | CSimpleOptA::SOption g_pc_opts[] = { 684 | HELP_OPTS, 685 | SO_END_OF_OPTIONS 686 | }; 687 | 688 | 689 | void Usage_CmdPoolChunk(PCMD_CTX ctx) 690 | { 691 | OutputDml(ctx, "!poolchunk"); dprintf("
\n"); 692 | } 693 | 694 | 695 | HRESULT CALLBACK poolchunk(PDEBUG_CLIENT4 pClient, PCSTR args) 696 | { 697 | HRESULT ret = S_OK; 698 | CMD_CTX ctx; 699 | CSimpleOptA cs; 700 | 701 | char **argv = NULL; 702 | int argc; 703 | 704 | WDBG_PTR address; 705 | 706 | WDBG_PTR kChunk; 707 | 708 | // initialize the command context 709 | if (FAILED(ret = InitCmdCtx(&ctx, pClient))) 710 | return ret; 711 | 712 | // parse !poolpage options 713 | argv = MakeCmdline("!poolchunk", args, &argc); 714 | if (argv == NULL) 715 | { 716 | ret = E_OUTOFMEMORY; 717 | goto cleanup; 718 | } 719 | 720 | cs.Init(argc, argv, g_pi_opts); 721 | while (cs.Next()) 722 | { 723 | if (cs.LastError() == SO_SUCCESS) 724 | { 725 | // handle options 726 | switch (cs.OptionId()) 727 | { 728 | default: 729 | case OPT_HELP: 730 | Usage_CmdPoolChunk(&ctx); 731 | goto cleanup; 732 | } 733 | } 734 | } 735 | 736 | // make sure an address is specified 737 | if (cs.FileCount() == 0) 738 | { 739 | Usage_CmdPoolInfo(&ctx); 740 | goto cleanup; 741 | } 742 | 743 | // resolve the expression 744 | address = GetExpressionArgv(cs.Files(), cs.FileCount()); 745 | 746 | // walk through the pool page and find the pool header for 747 | // this address 748 | kChunk = FindPoolHeader(&ctx, address); 749 | if (kChunk != NULL) 750 | { 751 | PrintPoolHeader(&ctx, kChunk, NULL, FALSE, FALSE, 0, FALSE); 752 | PrintExtendedChunkInfo(&ctx, kChunk); 753 | } 754 | 755 | cleanup: 756 | if (argv != NULL) 757 | free(argv); 758 | 759 | ReleaseCmdCtx(&ctx); 760 | return ret; 761 | } 762 | 763 | void ShowBanner(PCMD_CTX ctx) 764 | { 765 | OutputDml(ctx, 766 | "\n" 767 | " _ _ __ \n" 768 | " _ __ ___ ___ | (_)_ __ / _| ___ \n" 769 | " | '_ \\ / _ \\ / _ \\| | | '_ \\| |_ / _ \\ \n" 770 | " | |_) | (_) | (_) | | | | | | _| (_) |\n" 771 | " | .__/ \\___/ \\___/|_|_|_| |_|_| \\___/ \n" 772 | " |_| \n" 773 | " by: jfisher @debugregister\n" 774 | "\n" 775 | ); 776 | 777 | Usage_CmdPoolList(ctx); 778 | dprintf("\n"); 779 | Usage_CmdPoolInfo(ctx); 780 | dprintf("\n"); 781 | Usage_CmdPoolPage(ctx); 782 | dprintf("\n"); 783 | Usage_CmdPoolChunk(ctx); 784 | dprintf("\n"); 785 | } 786 | 787 | // The entry point for the extension 788 | extern "C" HRESULT CALLBACK DebugExtensionInitialize(PULONG Version, PULONG Flags) 789 | { 790 | HRESULT ret = S_OK; 791 | PDEBUG_CLIENT4 pDebugClient; 792 | PDEBUG_CONTROL pDebugControl; 793 | CMD_CTX ctx; 794 | 795 | if (FAILED(ret = DebugCreate(__uuidof(IDebugClient4),(PVOID *)&pDebugClient))) 796 | return ret; 797 | 798 | if (FAILED(ret = pDebugClient->QueryInterface(__uuidof(IDebugControl),(PVOID *)&pDebugControl))) 799 | return ret; 800 | 801 | ExtensionApis.nSize = sizeof(ExtensionApis); 802 | //pDebugControl->GetWindbgExtensionApis32((PWINDBG_EXTENSION_APIS32) &ExtensionApis); 803 | pDebugControl->GetWindbgExtensionApis64((PWINDBG_EXTENSION_APIS64) &ExtensionApis); 804 | pDebugControl->Release(); 805 | 806 | DEBUGPRINT("DebugExtensionInitialize: Retrieved ExtensionApis\n"); 807 | 808 | if (SUCCEEDED(ret = InitCmdCtx(&ctx, pDebugClient))) 809 | { 810 | ShowBanner(&ctx); 811 | ReleaseCmdCtx(&ctx); 812 | } 813 | 814 | pDebugClient->Release(); 815 | return ret; 816 | } 817 | 818 | 819 | extern "C" HRESULT CALLBACK DebugExtensionUninitialize(void) 820 | { 821 | DEBUGPRINT("DebugExtensionUninitialize\n"); 822 | return S_OK; 823 | } 824 | 825 | BOOL APIENTRY DllMain(HMODULE hModule, DWORD reason, LPVOID lpReserved) 826 | { 827 | switch (reason) 828 | { 829 | case DLL_PROCESS_ATTACH: 830 | case DLL_THREAD_ATTACH: 831 | case DLL_THREAD_DETACH: 832 | case DLL_PROCESS_DETACH: 833 | break; 834 | } 835 | return TRUE; 836 | } 837 | 838 | 839 | -------------------------------------------------------------------------------- /src/poolinfo/poolinfo.def: -------------------------------------------------------------------------------- 1 | LIBRARY "poolinfo" 2 | VERSION 0.1 3 | EXPORTS 4 | DebugExtensionInitialize PRIVATE 5 | DebugExtensionUninitialize PRIVATE 6 | poolpage 7 | poollist 8 | poolinfo 9 | poolchunk 10 | -------------------------------------------------------------------------------- /src/poolinfo/poolinfo.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #ifdef _DEBUG 4 | # define DEBUGPRINT dprintf 5 | #else 6 | # define DEBUGPRINT(...) __noop 7 | #endif 8 | 9 | extern BOOL g_bDebug; 10 | 11 | // this is stupid but the api wants a sign extended 32-bit pointer 12 | #define SIGN_EXTEND32(x) ((ULONG64)((LONG32)(x))) 13 | 14 | 15 | // Pool stuff 16 | #define PAGE_SIZE (0x1000) 17 | #define PAGE_MASK (~(PAGE_SIZE-1)) 18 | #define PAGE_ALIGN(p) ( ((WDBG_PTR)(p)) & PAGE_MASK ) 19 | 20 | #define POOL_INUSE 0x2 21 | #define POOL_INUSE_XP 0x4 22 | 23 | 24 | #define POOL_BLOCK_SIZE32 0x8 25 | #define POOL_BLOCK_SIZE64 0x10 26 | 27 | // 32-bit POOL_HEADER 28 | typedef struct _POOL_HEADER32 29 | { 30 | union { 31 | struct { 32 | ULONG PreviousSize: 9; 33 | ULONG PoolIndex: 7; 34 | ULONG BlockSize: 9; 35 | ULONG PoolType: 7; 36 | }; 37 | ULONG ULong1; 38 | }; 39 | ULONG PoolTag; 40 | } POOL_HEADER32, *PPOOL_HEADER32; 41 | 42 | // 64-bit pool header 43 | typedef struct _POOL_HEADER64 44 | { 45 | union { 46 | struct { 47 | BYTE PreviousSize; 48 | BYTE PoolIndex; 49 | BYTE BlockSize; 50 | BYTE PoolType; 51 | }; 52 | ULONG ULong1; 53 | }; 54 | ULONG PoolTag; 55 | } POOL_HEADER64, *PPOOL_HEADER64; 56 | 57 | #define POOL_HEADER_SIZE 0x8 58 | 59 | #define BUCKET_TO_SIZE(bucket, blockSize) (((bucket) + 1) * (blockSize)) 60 | 61 | #define ROUND_POOL_SIZE(size, blockSize) ( ((size) + ((blockSize) - 1)) & ~((blockSize) - 1) ) 62 | #define SIZE_TO_BUCKET(size, bs) ( (ROUND_POOL_SIZE((size), (bs)) < (bs)) ? 0 : ((ROUND_POOL_SIZE((size), (bs)) / (bs)) - 1) ) 63 | 64 | typedef enum _POOL_TYPE { 65 | NonPagedPool, 66 | NonPagedPoolExecute = NonPagedPool, 67 | PagedPool, 68 | NonPagedPoolMustSucceed = NonPagedPool + 2, 69 | DontUseThisType, 70 | NonPagedPoolCacheAligned = NonPagedPool + 4, 71 | PagedPoolCacheAligned, 72 | NonPagedPoolCacheAlignedMustS = NonPagedPool + 6, 73 | MaxPoolType, 74 | NonPagedPoolBase = 0, 75 | NonPagedPoolBaseMustSucceed = NonPagedPoolBase + 2, 76 | NonPagedPoolBaseCacheAligned = NonPagedPoolBase + 4, 77 | NonPagedPoolBaseCacheAlignedMustS = NonPagedPoolBase + 6, 78 | NonPagedPoolSession = 32, 79 | PagedPoolSession = NonPagedPoolSession + 1, 80 | NonPagedPoolMustSucceedSession = PagedPoolSession + 1, 81 | DontUseThisTypeSession = NonPagedPoolMustSucceedSession + 1, 82 | NonPagedPoolCacheAlignedSession = DontUseThisTypeSession + 1, 83 | PagedPoolCacheAlignedSession = NonPagedPoolCacheAlignedSession + 1, 84 | NonPagedPoolCacheAlignedMustSSession = PagedPoolCacheAlignedSession + 1, 85 | NonPagedPoolNx = 512, 86 | NonPagedPoolNxCacheAligned = NonPagedPoolNx + 4, 87 | NonPagedPoolSessionNx = NonPagedPoolNx + 32, 88 | } POOL_TYPE; 89 | 90 | 91 | // size of the lookaside struct for session 92 | // i hope this is pretty portable... 93 | #define SESSION_LOOKASIDE_SIZE 0x80 94 | 95 | #define POOL_LOOKASIDE_BUCKETS 0x20 96 | 97 | 98 | // 99 | // options stuff 100 | // 101 | 102 | #define POOL_TYPE_NONPAGED 0x1 103 | #define POOL_TYPE_PAGED 0x2 104 | #define POOL_TYPE_SESSION 0x4 105 | 106 | #define BUCKET_ALL ((ULONG)-1) 107 | 108 | // command-line option ids 109 | enum { 110 | OPT_HELP, 111 | OPT_PP_RAWHDR, 112 | OPT_PL_ALL, 113 | OPT_PL_NONPAGED, 114 | OPT_PL_PAGED, 115 | OPT_PL_SESSION, 116 | OPT_PL_LOOKASIDE, 117 | OPT_PI_SUMMARY, 118 | OPT_PI_VERBOSE, 119 | OPT_PI_FREELIST, 120 | OPT_PI_BUCKET, 121 | OPT_PI_LOOKASIDE, 122 | OPT_PI_LOOKTYPE, 123 | OPT_PI_NUMBUCKETS, 124 | OPT_PI_LOOKSIZE 125 | }; 126 | 127 | #define HELP_OPTS \ 128 | { OPT_HELP, "-h", SO_NONE }, \ 129 | { OPT_HELP, "--help", SO_NONE }, \ 130 | { OPT_HELP, "-?", SO_NONE } 131 | 132 | 133 | 134 | -------------------------------------------------------------------------------- /src/poolinfo/poolinfo.vcxproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Debug 10 | x64 11 | 12 | 13 | Release 14 | Win32 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | 22 | {0BCE2D35-07D1-4D84-BF8E-37D83A61E6AF} 23 | Win32Proj 24 | poolinfo 25 | 26 | 27 | 28 | DynamicLibrary 29 | true 30 | v110 31 | Unicode 32 | 33 | 34 | DynamicLibrary 35 | true 36 | v110 37 | Unicode 38 | 39 | 40 | DynamicLibrary 41 | false 42 | v110 43 | true 44 | Unicode 45 | 46 | 47 | DynamicLibrary 48 | false 49 | v110 50 | true 51 | Unicode 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | true 71 | $(SolutionDir)$(Platform)\$(Configuration)\ 72 | $(Platform)\$(Configuration)\ 73 | 74 | 75 | true 76 | 77 | 78 | false 79 | $(SolutionDir)$(Platform)\$(Configuration)\ 80 | $(Platform)\$(Configuration)\ 81 | 82 | 83 | false 84 | 85 | 86 | 87 | Use 88 | Level3 89 | Disabled 90 | WIN32;_DEBUG;_WINDOWS;_USRDLL;POOLINFO_EXPORTS;%(PreprocessorDefinitions) 91 | MultiThreadedDebug 92 | 93 | 94 | Windows 95 | true 96 | c:\Program Files (x86)\Windows Kits\8.1\Debuggers\lib\x86;%(AdditionalIncludeDirectories) 97 | dbgeng.lib;shell32.lib;kernel32.lib;user32.lib;gdi32.lib;uuid.lib;%(AdditionalDependencies) 98 | poolinfo.def 99 | 100 | 101 | 102 | 103 | Use 104 | Level3 105 | Disabled 106 | WIN32;_DEBUG;_WINDOWS;_USRDLL;POOLINFO_EXPORTS;%(PreprocessorDefinitions) 107 | MultiThreadedDebug 108 | 109 | 110 | Windows 111 | true 112 | c:\Program Files (x86)\Windows Kits\8.1\Debuggers\lib\x64;%(AdditionalIncludeDirectories) 113 | dbgeng.lib;shell32.lib;kernel32.lib;user32.lib;gdi32.lib;uuid.lib;%(AdditionalDependencies) 114 | poolinfo.def 115 | 116 | 117 | 118 | 119 | Level3 120 | Use 121 | MaxSpeed 122 | true 123 | true 124 | WIN32;NDEBUG;_WINDOWS;_USRDLL;POOLINFO_EXPORTS;%(PreprocessorDefinitions) 125 | MultiThreaded 126 | 127 | 128 | Windows 129 | false 130 | true 131 | true 132 | poolinfo.def 133 | c:\Program Files (x86)\Windows Kits\8.1\Debuggers\lib\x86;%(AdditionalIncludeDirectories) 134 | dbgeng.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) 135 | 136 | 137 | 138 | 139 | Level3 140 | Use 141 | MaxSpeed 142 | true 143 | true 144 | WIN32;NDEBUG;_WINDOWS;_USRDLL;POOLINFO_EXPORTS;%(PreprocessorDefinitions) 145 | MultiThreaded 146 | 147 | 148 | Windows 149 | false 150 | true 151 | true 152 | poolinfo.def 153 | c:\Program Files (x86)\Windows Kits\8.1\Debuggers\lib\x64;%(AdditionalIncludeDirectories) 154 | dbgeng.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | Create 173 | Create 174 | Create 175 | Create 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | -------------------------------------------------------------------------------- /src/poolinfo/poolinfo.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;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 | 18 | 19 | Header Files 20 | 21 | 22 | Header Files 23 | 24 | 25 | Header Files 26 | 27 | 28 | Header Files 29 | 30 | 31 | Header Files 32 | 33 | 34 | Header Files 35 | 36 | 37 | Header Files 38 | 39 | 40 | Header Files 41 | 42 | 43 | 44 | 45 | Source Files 46 | 47 | 48 | Source Files 49 | 50 | 51 | Source Files 52 | 53 | 54 | Source Files 55 | 56 | 57 | Source Files 58 | 59 | 60 | Source Files 61 | 62 | 63 | 64 | 65 | Source Files 66 | 67 | 68 | -------------------------------------------------------------------------------- /src/poolinfo/stdafx.cpp: -------------------------------------------------------------------------------- 1 | // stdafx.cpp : source file that includes just the standard includes 2 | // poolinfo.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 | -------------------------------------------------------------------------------- /src/poolinfo/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 | #include 11 | #include 12 | #include 13 | 14 | 15 | #define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers 16 | // Windows Header Files: 17 | #include 18 | 19 | #define KDEXT_64BIT 20 | #include 21 | #include 22 | 23 | #include "context.h" 24 | 25 | #include "poolinfo.h" 26 | #include "pool.h" 27 | #include "syms.h" 28 | #include "util.h" 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /src/poolinfo/syms.cpp: -------------------------------------------------------------------------------- 1 | #include "stdafx.h" 2 | 3 | 4 | // nt!_POOL_DESCRIPTOR 5 | SYM_FIELD g_StructPoolDescriptor[] = 6 | { 7 | DECL_FIELD(PoolType), 8 | DECL_FIELD(PoolIndex), 9 | DECL_FIELD(PendingFreeDepth), 10 | DECL_FIELD(RunningAllocs), 11 | DECL_FIELD(RunningDeAllocs), 12 | DECL_FIELD(TotalBigPages), 13 | DECL_FIELD(ThreadsProcessingDeferrals), 14 | DECL_FIELD(TotalBytes), 15 | DECL_FIELD(TotalPages), 16 | DECL_FIELD(ListHeads), 17 | END_FIELD 18 | }; 19 | 20 | // nt!_EPROCESS 21 | SYM_FIELD g_StructEprocess[] = 22 | { 23 | DECL_FIELD(Session), 24 | END_FIELD 25 | }; 26 | 27 | // nt!_MM_SESSION_SPACE 28 | SYM_FIELD g_StructMmSessionSpace[] = 29 | { 30 | DECL_FIELD(SessionId), 31 | DECL_FIELD(Lookaside), 32 | DECL_FIELD(PagedPool), 33 | END_FIELD 34 | }; 35 | 36 | // nt!_KPRCB 37 | SYM_FIELD g_StructKPRCB[] = 38 | { 39 | DECL_FIELD(PPLookasideList), 40 | DECL_FIELD_OPT(PPNxPagedLookasideList), 41 | DECL_FIELD(PPNPagedLookasideList), 42 | DECL_FIELD(PPPagedLookasideList), 43 | END_FIELD 44 | }; 45 | 46 | // nt!_GENERAL_LOOKASIDE_POOL 47 | SYM_FIELD g_StructGeneralLookasidePool[] = 48 | { 49 | DECL_FIELD(SingleListHead), 50 | DECL_FIELD(Depth), 51 | DECL_FIELD(MaximumDepth), 52 | DECL_FIELD(AllocateMisses), 53 | DECL_FIELD(AllocateHits), 54 | DECL_FIELD(TotalFrees), 55 | DECL_FIELD(FreeMisses), 56 | DECL_FIELD(FreeHits), 57 | DECL_FIELD(Type), 58 | DECL_FIELD(Size), 59 | DECL_FIELD(Tag), 60 | DECL_FIELD(Future), // the last entry, for size calculation 61 | END_FIELD 62 | }; 63 | 64 | 65 | 66 | HRESULT ResolveStruct(PDEBUG_SYMBOLS3 pDebugSymbols, const char *moduleName, const char *structName, PSYM_FIELD symFields) 67 | { 68 | HRESULT ret = S_OK; 69 | ULONG64 modBase = 0; 70 | PSYM_FIELD field = symFields; 71 | ULONG typeId; 72 | ULONG fieldOffset; 73 | ULONG fieldTypeId; 74 | ULONG fieldSize; 75 | 76 | if (moduleName != NULL) 77 | { 78 | if (FAILED(ret = pDebugSymbols->GetModuleByModuleName(moduleName, 0, NULL, &modBase))) 79 | { 80 | dprintf("failed to find module: %s\n", moduleName); 81 | return ret; 82 | } 83 | } 84 | else 85 | { 86 | if (FAILED(ret = pDebugSymbols->GetSymbolModule(structName, &modBase))) 87 | { 88 | dprintf("failed to find module for symbol: %s\n", structName); 89 | return ret; 90 | } 91 | } 92 | 93 | if (FAILED(ret = pDebugSymbols->GetTypeId(modBase, structName, &typeId))) 94 | { 95 | dprintf("failed to find structName: %s, modBase: %p\n", structName, modBase); 96 | return ret; 97 | } 98 | 99 | while (field->fieldName != NULL) 100 | { 101 | if (FAILED(ret = pDebugSymbols->GetFieldTypeAndOffset(modBase, typeId, field->fieldName, &fieldTypeId, &fieldOffset))) 102 | { 103 | if (!field->fieldOptional) 104 | { 105 | dprintf("failed to resolve field for %s\n", field->fieldName); 106 | return ret; 107 | } 108 | else 109 | { 110 | field->fieldOffset = 0; 111 | field->fieldTypeId = FIELD_NOT_PRESENT; 112 | field->fieldSize = 0; 113 | field++; 114 | continue; 115 | } 116 | } 117 | 118 | if (FAILED(ret = pDebugSymbols->GetTypeSize(modBase, fieldTypeId, &fieldSize))) 119 | { 120 | dprintf("failed to resolve field size for %s\n", field->fieldName); 121 | return ret; 122 | } 123 | 124 | field->fieldOffset = fieldOffset; 125 | field->fieldTypeId = fieldTypeId; 126 | field->fieldSize = fieldSize; 127 | field++; 128 | } 129 | 130 | return ret; 131 | } 132 | 133 | void DumpResolvedStruct(PSYM_FIELD pStruct) 134 | { 135 | PSYM_FIELD pField = pStruct; 136 | while (pField->fieldName != NULL) 137 | { 138 | dprintf("%-30s: offset=%08X, type=%08X, size=%08X\n", 139 | pField->fieldName, pField->fieldOffset, pField->fieldTypeId, pField->fieldSize); 140 | pField++; 141 | } 142 | } 143 | 144 | 145 | // clone a struct 146 | PSYM_FIELD CloneStruct(PSYM_FIELD pBaseStruct) 147 | { 148 | ULONG len = 0; 149 | PSYM_FIELD pClone; 150 | PSYM_FIELD pField = pBaseStruct; 151 | 152 | // find the last field to determine the size 153 | while (pField->fieldName != NULL) 154 | { 155 | len++; 156 | pField++; 157 | } 158 | 159 | // shallow copy the struct 160 | pClone = (PSYM_FIELD) malloc((len + 1) * sizeof(SYM_FIELD)); 161 | if (pClone != NULL) 162 | memcpy(pClone, pBaseStruct, (len + 1) * sizeof(SYM_FIELD)); 163 | 164 | return pClone; 165 | } 166 | 167 | HRESULT ResolveAllStructs(PCMD_CTX ctx) 168 | { 169 | HRESULT ret = S_OK; 170 | 171 | #define _RESOLVE(name, symStruct, ctxStruct) \ 172 | ctxStruct = CloneStruct(symStruct); \ 173 | if (ctxStruct == NULL) { \ 174 | return E_OUTOFMEMORY; \ 175 | } \ 176 | if (FAILED(ret = ResolveStruct(ctx->pDebugSymbols, NULL, (name), (ctxStruct)))) { \ 177 | dprintf("failed to resolve %s\n", (name)); \ 178 | return ret; \ 179 | } \ 180 | //DEBUGPRINT("%s\n", (name)); \ 181 | //if (g_bDebug) DumpResolvedStruct(symStruct) 182 | 183 | _RESOLVE("nt!_POOL_DESCRIPTOR", g_StructPoolDescriptor, ctx->pStructPoolDescriptor); 184 | _RESOLVE("nt!_EPROCESS", g_StructEprocess, ctx->pStructEprocess); 185 | _RESOLVE("nt!_MM_SESSION_SPACE", g_StructMmSessionSpace, ctx->pStructMmSessionSpace); 186 | _RESOLVE("nt!_KPRCB", g_StructKPRCB, ctx->pStructKPRCB); 187 | _RESOLVE("nt!_GENERAL_LOOKASIDE_POOL", g_StructGeneralLookasidePool, ctx->pStructGeneralLookasidePool); 188 | 189 | return ret; 190 | } 191 | 192 | 193 | 194 | // 195 | // returns the amount we need to read to get 196 | // the entire structure as defind by the PSYM_FIELD. 197 | // this is NOT the actual struct length, just what we 198 | // care about 199 | // 200 | ULONG GetReadStructLength(PSYM_FIELD pStruct) 201 | { 202 | ULONG length = 0; 203 | ULONG offset = 0; 204 | 205 | PSYM_FIELD pField = pStruct; 206 | while (pField->fieldName != NULL) 207 | { 208 | if (pField->fieldOffset > offset) 209 | { 210 | offset = pField->fieldOffset; 211 | length = offset + pField->fieldSize; 212 | } 213 | 214 | pField++; 215 | } 216 | 217 | return length; 218 | } 219 | 220 | // 221 | // reads a struct from a virtual address 222 | // the struct data is placed in pStructData and should be freed when done 223 | // the pStruct should be a cloned structure which will have its fieldData 224 | // pointers updated to point to the the appropriate location in pStructData 225 | // 226 | HRESULT ReadVirtualStruct(PCMD_CTX ctx, WDBG_PTR addr, PSYM_FIELD pStruct, PVOID *pStructData) 227 | { 228 | HRESULT ret; 229 | ULONG structLen = GetReadStructLength(pStruct); 230 | 231 | PBYTE buf = (PBYTE) malloc(structLen); 232 | if (buf == NULL) 233 | return E_OUTOFMEMORY; 234 | 235 | if (SUCCEEDED(ret = ReadVirtual(ctx, addr, buf, structLen))) 236 | { 237 | // assign the fieldData pointer to the proper location in buf 238 | PSYM_FIELD pField = pStruct; 239 | while (pField->fieldName != NULL) 240 | { 241 | pField->fieldData = buf + pField->fieldOffset; 242 | pField++; 243 | } 244 | 245 | *pStructData = buf; 246 | } 247 | 248 | return ret; 249 | } 250 | 251 | PSYM_FIELD GetStructField(PSYM_FIELD pStruct, const char *name) 252 | { 253 | PSYM_FIELD pField = pStruct; 254 | while (pField->fieldName != NULL) 255 | { 256 | if (strcmp(pField->fieldName, name) == 0) 257 | return pField; 258 | 259 | pField++; 260 | } 261 | 262 | return NULL; 263 | } 264 | 265 | WDBG_PTR GetStructPointer(PCMD_CTX ctx, PSYM_FIELD pStruct, const char *name) 266 | { 267 | WDBG_PTR val = 0; 268 | PSYM_FIELD pField = GetStructField(pStruct, name); 269 | 270 | if (pField != NULL) 271 | { 272 | if (ctx->pointerLen == 8) 273 | { 274 | ULONG64 ptr = *((ULONG64 *)(pField->fieldData)); 275 | val = ptr; 276 | } 277 | else 278 | { 279 | ULONG32 ptr = *((ULONG32 *)(pField->fieldData)); 280 | val = SIGN_EXTEND32(ptr); 281 | } 282 | } 283 | 284 | return val; 285 | } 286 | 287 | template 288 | Type GetStructValTempl(PSYM_FIELD pStruct, const char *name, Type defVal) 289 | { 290 | Type val = defVal; 291 | PSYM_FIELD pField = GetStructField(pStruct, name); 292 | if (pField != NULL) 293 | val = *((Type *)(pField->fieldData)); 294 | return val; 295 | } 296 | 297 | #define DECLARE_STRUCT_ACCESSOR(type, defVal) \ 298 | type GS_ ## type (PSYM_FIELD pStruct, const char *name) { \ 299 | return GetStructValTempl< type >(pStruct, name, (defVal)); \ 300 | } 301 | 302 | 303 | DECLARE_STRUCT_ACCESSOR(ULONG, 0); 304 | DECLARE_STRUCT_ACCESSOR(DWORD, 0); 305 | DECLARE_STRUCT_ACCESSOR(WORD, 0); 306 | DECLARE_STRUCT_ACCESSOR(BYTE, 0); 307 | DECLARE_STRUCT_ACCESSOR(ULONG32, 0); 308 | DECLARE_STRUCT_ACCESSOR(LONG32, 0); 309 | 310 | #undef DECLARE_STRUCT_ACCESSOR 311 | -------------------------------------------------------------------------------- /src/poolinfo/syms.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | 4 | // 5 | // structs resolved with ResolveAllStructs 6 | // 7 | extern SYM_FIELD g_StructPoolDescriptor[]; 8 | extern SYM_FIELD g_StructEprocess[]; 9 | extern SYM_FIELD g_StructMmSessionSpace[]; 10 | extern SYM_FIELD g_StructKPRCB[]; 11 | extern SYM_FIELD g_StructGeneralLookasidePool[]; 12 | 13 | 14 | 15 | // 16 | // functions 17 | // 18 | extern HRESULT ResolveStruct(PDEBUG_SYMBOLS3 pDebugSymbols, const char *moduleName, const char *structName, PSYM_FIELD symFields); 19 | extern void DumpResolvedStruct(PSYM_FIELD pStruct); 20 | extern HRESULT ResolveAllStructs(PCMD_CTX ctx); 21 | extern PSYM_FIELD CloneStruct(PSYM_FIELD pBaseStruct); 22 | extern ULONG GetReadStructLength(PSYM_FIELD pStruct); 23 | extern HRESULT ReadVirtualStruct(PCMD_CTX ctx, WDBG_PTR addr, PSYM_FIELD pStruct, PVOID *pStructData); 24 | PSYM_FIELD GetStructField(PSYM_FIELD pStruct, const char *name); 25 | WDBG_PTR GetStructPointer(PCMD_CTX ctx, PSYM_FIELD pStruct, const char *name); 26 | 27 | // accessor template 28 | template 29 | Type GetStructValTempl(PSYM_FIELD pStruct, const char *name, Type defVal); 30 | 31 | // accessors 32 | #define DECLARE_STRUCT_ACCESSOR(type) \ 33 | extern type GS_ ## type (PSYM_FIELD pStruct, const char *name); 34 | 35 | 36 | 37 | DECLARE_STRUCT_ACCESSOR(ULONG); 38 | DECLARE_STRUCT_ACCESSOR(DWORD); 39 | DECLARE_STRUCT_ACCESSOR(WORD); 40 | DECLARE_STRUCT_ACCESSOR(BYTE); 41 | DECLARE_STRUCT_ACCESSOR(ULONG32); 42 | DECLARE_STRUCT_ACCESSOR(LONG32); 43 | 44 | #undef DECLARE_STRUCT_ACCESSOR 45 | -------------------------------------------------------------------------------- /src/poolinfo/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 | 10 | #define _WIN32_WINNT 0x0500 11 | #include 12 | 13 | -------------------------------------------------------------------------------- /src/poolinfo/util.cpp: -------------------------------------------------------------------------------- 1 | #include "stdafx.h" 2 | 3 | #pragma warning(disable: 4996) 4 | 5 | // 6 | // mostly taken from http://alter.org.ua/docs/win/args/ 7 | // thanks to Alter 8 | // 9 | PCHAR* CommandLineToArgvA(PCHAR CmdLine, int* _argc) 10 | { 11 | PCHAR* argv; 12 | PCHAR _argv; 13 | size_t len; 14 | ULONG argc; 15 | CHAR a; 16 | size_t 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*)GlobalAlloc(GMEM_FIXED, i + (len+2)*sizeof(CHAR)); 26 | argv = (PCHAR*)malloc(i + (len+2)*sizeof(CHAR)); 27 | 28 | _argv = (PCHAR)(((PUCHAR)argv)+i); 29 | 30 | argc = 0; 31 | argv[argc] = _argv; 32 | in_QM = FALSE; 33 | in_TEXT = FALSE; 34 | in_SPACE = TRUE; 35 | i = 0; 36 | j = 0; 37 | 38 | while( a = CmdLine[i] ) { 39 | if(in_QM) { 40 | if(a == '\"') { 41 | in_QM = FALSE; 42 | } else { 43 | _argv[j] = a; 44 | j++; 45 | } 46 | } else { 47 | switch(a) { 48 | case '\"': 49 | in_QM = TRUE; 50 | in_TEXT = TRUE; 51 | if(in_SPACE) { 52 | argv[argc] = _argv+j; 53 | argc++; 54 | } 55 | in_SPACE = FALSE; 56 | break; 57 | case ' ': 58 | case '\t': 59 | case '\n': 60 | case '\r': 61 | if(in_TEXT) { 62 | _argv[j] = '\0'; 63 | j++; 64 | } 65 | in_TEXT = FALSE; 66 | in_SPACE = TRUE; 67 | break; 68 | default: 69 | in_TEXT = TRUE; 70 | if(in_SPACE) { 71 | argv[argc] = _argv+j; 72 | argc++; 73 | } 74 | _argv[j] = a; 75 | j++; 76 | in_SPACE = FALSE; 77 | break; 78 | } 79 | } 80 | i++; 81 | } 82 | _argv[j] = '\0'; 83 | argv[argc] = NULL; 84 | 85 | (*_argc) = argc; 86 | return argv; 87 | } 88 | 89 | char **MakeCmdline(PCSTR argv0, PCSTR args, int* pargc) 90 | { 91 | char **argv = NULL; 92 | 93 | // make a temporary full command line 94 | size_t len = strlen(argv0) + strlen(args) + 2; 95 | char *cmdline = (char *) malloc(len); 96 | if (cmdline == NULL) 97 | return NULL; 98 | 99 | strcpy(cmdline, argv0); 100 | strcat(cmdline, " "); 101 | strcat(cmdline, args); 102 | 103 | argv = CommandLineToArgvA(cmdline, pargc); 104 | free(cmdline); 105 | return argv; 106 | } 107 | 108 | HRESULT OutputDmlBase(PDEBUG_CONTROL4 pDebugControl, const char *fmt, va_list ap) 109 | { 110 | return pDebugControl->ControlledOutputVaList(DEBUG_OUTCTL_AMBIENT_DML, DEBUG_OUTPUT_NORMAL, fmt, ap); 111 | } 112 | 113 | HRESULT OutputDml(PCMD_CTX ctx, const char *fmt, ...) 114 | { 115 | HRESULT ret; 116 | va_list ap; 117 | 118 | va_start(ap, fmt); 119 | ret = OutputDmlBase(ctx->pDebugControl, fmt, ap); 120 | //ret = ctx->pDebugControl->ControlledOutputVaList(DEBUG_OUTCTL_AMBIENT_DML, DEBUG_OUTPUT_NORMAL, fmt, ap); 121 | va_end(ap); 122 | return ret; 123 | } 124 | 125 | HRESULT ReadVirtual(PCMD_CTX ctx, WDBG_PTR src, PVOID dst, ULONG len) 126 | { 127 | HRESULT ret; 128 | ULONG numRead = 0; 129 | WDBG_PTR ptrToRead = src; 130 | 131 | if (ctx->b64BitTarget) 132 | ret = ctx->pDebugDataSpaces->ReadVirtual(src, dst, len, &numRead); 133 | else 134 | ret = ctx->pDebugDataSpaces->ReadVirtual(SIGN_EXTEND32(src), dst, len, &numRead); 135 | 136 | if (ret == S_OK) 137 | { 138 | if (numRead != len) 139 | { 140 | dprintf("poolinfo: ReadVirtual returned only %u bytes; %u were requested\n", numRead, len); 141 | ret = E_NOT_SUFFICIENT_BUFFER; 142 | } 143 | } 144 | 145 | return ret; 146 | } 147 | 148 | HRESULT ReadTargetPointer(PCMD_CTX ctx, WDBG_PTR src, WDBG_PTR *pPtr) 149 | { 150 | HRESULT ret; 151 | 152 | if (ctx->pointerLen == 8) 153 | { 154 | ULONG64 ptr64; 155 | ret = ReadVirtual(ctx, src, &ptr64, sizeof(ptr64)); 156 | *pPtr = ptr64; 157 | } 158 | else 159 | { 160 | LONG32 ptr32; 161 | ret = ReadVirtual(ctx, src, &ptr32, sizeof(ptr32)); 162 | *pPtr = SIGN_EXTEND32(ptr32); 163 | } 164 | 165 | return ret; 166 | } 167 | 168 | HRESULT ReadTargetPointerArray(PCMD_CTX ctx, WDBG_PTR src, WDBG_PTR *pPtrList, DWORD num) 169 | { 170 | HRESULT ret; 171 | DWORD i; 172 | 173 | for (i = 0; i < num; i++) 174 | { 175 | if (FAILED(ret = ReadTargetPointer(ctx, src + (i * ctx->pointerLen), &pPtrList[i]))) 176 | break; 177 | } 178 | 179 | return ret; 180 | } 181 | 182 | 183 | 184 | char *FlattenArgv(char **argv, int argc) 185 | { 186 | char *flat, *p; 187 | int i; 188 | size_t len = 0; 189 | 190 | // calculate the size of the resulting string 191 | for (i = 0; i < argc; i++) 192 | len += strlen(argv[i]) + 1; 193 | 194 | // allocate a string 195 | if ((flat = (char *) malloc(len + 1)) == NULL) 196 | return NULL; 197 | 198 | // copy the argv members into flat 199 | for (p = flat, i = 0; i < argc; i++) 200 | { 201 | strcpy(p, argv[i]); 202 | p += strlen(argv[i]); 203 | strcpy(p, " "); 204 | p++; 205 | } 206 | *p = '\0'; 207 | 208 | return flat; 209 | } 210 | 211 | // 212 | // get an expression from an array of char * 213 | // 214 | WDBG_PTR GetExpressionArgv(char **argv, int argc) 215 | { 216 | WDBG_PTR ret = 0; 217 | 218 | // flatten the argv into a single string 219 | char *flat = FlattenArgv(argv, argc); 220 | if (flat != NULL) 221 | { 222 | // get the expression 223 | ret = GetExpression(flat); 224 | free(flat); 225 | } 226 | 227 | return ret; 228 | } 229 | -------------------------------------------------------------------------------- /src/poolinfo/util.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // utility functions 4 | extern PCHAR* CommandLineToArgvA(PCHAR CmdLine,int* _argc); 5 | extern char **MakeCmdline(PCSTR argv0, PCSTR args, int* pargc); 6 | extern HRESULT OutputDmlBase(PDEBUG_CONTROL4 pDebugControl, const char *fmt, va_list ap); 7 | extern HRESULT OutputDml(PCMD_CTX ctx, _In_ PCSTR Format, ...); 8 | extern HRESULT ReadVirtual(PCMD_CTX ctx, WDBG_PTR src, PVOID dst, ULONG len); 9 | extern HRESULT ReadTargetPointer(PCMD_CTX ctx, WDBG_PTR src, WDBG_PTR *pPtr); 10 | extern HRESULT ReadTargetPointerArray(PCMD_CTX ctx, WDBG_PTR src, WDBG_PTR *pPtrList, DWORD num); 11 | extern char *FlattenArgv(char **argv, int argc); 12 | extern WDBG_PTR GetExpressionArgv(char **argv, int argc); 13 | --------------------------------------------------------------------------------