原創(chuàng): 星戰(zhàn)紫輝 http://m.shnenglu.com/rawdata 2009-2-23
關(guān)鍵字: 打印 SPL EMF 文件格式
問題:
Windows的假脫機打印會在Windows\System32\spool\PRINERS目錄下生成.spl和.shd文件,其中的打印內(nèi)
容存貯在.spl文件中,但是.spl文件格式似乎未公開,那么如何才能將未知的.spl文件剝離成.emf文件呢?
首先,讓我們了解一下Windows打印機制:
這是微軟的官網(wǎng)的一副打印流程圖片:
其中ISV是應(yīng)用軟件接口,IHV是硬件接口,左邊是XP的打印模型,右邊是Vista最新的XPS打印模
型,但兩者可以互相轉(zhuǎn)換,具有良好的兼容性。不過,這里暫時只關(guān)心XP系統(tǒng)的打印過程。
網(wǎng)絡(luò)打印過程圖:

但是這些圖似乎還不夠詳細,那么請看下面一副:(摘錄于論文:《基于關(guān)鍵字匹配的打印數(shù)據(jù)截獲
系統(tǒng)》):

基本的思路是: 打印過程發(fā)生時,GDI模塊和打印驅(qū)動(由打印機廠商提供)進行基本的數(shù)據(jù)交換,在假
脫機設(shè)置環(huán)境下,生成打印機命令文件:.spl或.emf文件,作為一個打印池的作業(yè),然后Windows后臺打印線
程處理打印作業(yè),將數(shù)據(jù)文件送至打印機打印,打印完刪除該打印文件。
好,現(xiàn)在回到正題:.spl文件該如何剝離成.emf呢?看一個例子:
在WinHex中打開一個.spl文件:

參考: http://www.undocprint.org/formats/winspool/spl 中一些打印結(jié)構(gòu)的定義。
首先,.spl文件都是以0x00010000簽名開頭,然后一個DWORD 是emf相關(guān)區(qū)的文件偏移,第3個
DWORD是文檔描述字符串(UNICODE)的文件偏移,第4個DWORD 描述的是端口說明字符串(UNICODE)。大
致結(jié)構(gòu)如下:

文件尾就是這個樣子:

當定位到0x50的文件位置,讀取2個DWORD數(shù)據(jù)之后,就是.emf文件開始了。.emf文件格式是公開的,而
且非常簡單,是一系列EMR_XXX開口結(jié)構(gòu)的緊密排列,通常以EMR_HEADER(0x01)開頭,以EMR_EOF
(0x0E)結(jié)尾。其實我們根本沒有必要去解析.emf文件格式,Windows SDK有專門顯示.emf文件的API,3個函數(shù)就
搞定:
HENHMETAFILE hEMF = GetEnhMetaFile("EMF_DumpOK.emf");
PlayEnhMetaFile (dc.m_hDC, hEMF, &rc) ;
DeleteEnhMetaFile (hEMF) ;
然后.spl文件還有一些東西,我現(xiàn)在還沒有解析出來,但是.emf文件已經(jīng)剝離出來了,后面的可以先不理它。
然后,開始寫程序嘍,因為比較簡單,所以代碼有點隨便哦~~:)
http://m.shnenglu.com/rawdata/ 星綻紫輝
程序截圖如下:


1
2
3
#include <windows.h>
4
#include <winspool.h>
5
#include <stdio.h>
6
#include <locale.h>
7
#include <tchar.h>
8
#include <iostream>
9
using namespace std;
10
11
12
BOOL AnalyseFile(const char* pszFileName);
13
14
void PFT(const char* pszInfo,DWORD dwData)
15

{
16
printf("%s: 0x%08X\n",pszInfo,dwData);
17
}
18
19
void PFM(const char* pszInfo)
20

{
21
printf("%s\n",pszInfo);
22
}
23
24
void UPFM(const wchar_t pszInfo[])
25

{
26
wprintf(L"%s\n",pszInfo);
27
}
28
29
static char* ID_Func[] =
30

{
31
"EMR_HEADER",
32
"EMR_POLYBEZIER",
33
"EMR_POLYGON",
34
"EMR_POLYLINE",
35
"EMR_POLYBEZIERTO",
36
"EMR_POLYLINETO",
37
"EMR_POLYPOLYLINE",
38
"EMR_POLYPOLYGON",
39
"EMR_SETWINDOWEXTEX",
40
"EMR_SETWINDOWORGEX",
41
"EMR_SETVIEWPORTEXTEX",
42
"EMR_SETVIEWPORTORGEX",
43
"EMR_SETBRUSHORGEX",
44
"EMR_EOF",
45
"EMR_SETPIXELV",
46
"EMR_SETMAPPERFLAGS",
47
"EMR_SETMAPMODE",
48
"EMR_SETBKMODE",
49
"EMR_SETPOLYFILLMODE",
50
"EMR_SETROP2",
51
"EMR_SETSTRETCHBLTMODE",
52
"EMR_SETTEXTALIGN",
53
"EMR_SETCOLORADJUSTMENT",
54
"EMR_SETTEXTCOLOR",
55
"EMR_SETBKCOLOR",
56
"EMR_OFFSETCLIPRGN",
57
"EMR_MOVETOEX",
58
"EMR_SETMETARGN",
59
"EMR_EXCLUDECLIPRECT",
60
"EMR_INTERSECTCLIPRECT",
61
"EMR_SCALEVIEWPORTEXTEX",
62
"EMR_SCALEWINDOWEXTEX",
63
"EMR_SAVEDC",
64
"EMR_RESTOREDC",
65
"EMR_SETWORLDTRANSFORM",
66
"EMR_MODIFYWORLDTRANSFORM",
67
"EMR_SELECTOBJECT",
68
"EMR_CREATEPEN",
69
"EMR_CREATEBRUSHINDIRECT",
70
"EMR_DELETEOBJECT",
71
"EMR_ANGLEARC",
72
"EMR_ELLIPSE",
73
"EMR_RECTANGLE",
74
"EMR_ROUNDRECT",
75
"EMR_ARC",
76
"EMR_CHORD",
77
"EMR_PIE",
78
"EMR_SELECTPALETTE",
79
"EMR_CREATEPALETTE",
80
"EMR_SETPALETTEENTRIES",
81
"EMR_RESIZEPALETTE",
82
"EMR_REALIZEPALETTE",
83
"EMR_EXTFLOODFILL",
84
"EMR_LINETO",
85
"EMR_ARCTO",
86
"EMR_POLYDRAW",
87
"EMR_SETARCDIRECTION",
88
"EMR_SETMITERLIMIT",
89
"EMR_BEGINPATH",
90
"EMR_ENDPATH",
91
"EMR_CLOSEFIGURE",
92
"EMR_FILLPATH",
93
"EMR_STROKEANDFILLPATH",
94
"EMR_STROKEPATH",
95
"EMR_FLATTENPATH",
96
"EMR_WIDENPATH",
97
"EMR_SELECTCLIPPATH",
98
"EMR_ABORTPATH",
99
"69--Unknown",
100
101
"EMR_GDICOMMENT",
102
"EMR_FILLRGN",
103
"EMR_FRAMERGN",
104
"EMR_INVERTRGN",
105
"EMR_PAINTRGN ",
106
"EMR_EXTSELECTCLIPRGN",
107
"EMR_BITBLT ",
108
"EMR_STRETCHBLT",
109
"EMR_MASKBLT",
110
"EMR_PLGBLT",
111
"EMR_SETDIBITSTODEVICE",
112
"EMR_STRETCHDIBITS",
113
"EMR_EXTCREATEFONTINDIRECTW",
114
"EMR_EXTTEXTOUTA ",
115
"EMR_EXTTEXTOUTW",
116
"EMR_POLYBEZIER16",
117
"EMR_POLYGON16 ",
118
"EMR_POLYLINE16 ",
119
"EMR_POLYBEZIERTO16",
120
"EMR_POLYLINETO16 ",
121
"EMR_POLYPOLYLINE16",
122
"EMR_POLYPOLYGON16",
123
"EMR_POLYDRAW16 ",
124
"EMR_CREATEMONOBRUSH ",
125
"EMR_CREATEDIBPATTERNBRUSHPT",
126
"EMR_EXTCREATEPEN",
127
"EMR_POLYTEXTOUTA ",
128
"EMR_POLYTEXTOUTW",
129
"EMR_SETICMMODE ",
130
"EMR_CREATECOLORSPACE",
131
"EMR_SETCOLORSPACE ",
132
"EMR_DELETECOLORSPACE ",
133
"EMR_GLSRECORD ",
134
"EMR_GLSBOUNDEDRECORD",
135
"EMR_PIXELFORMAT",
136
"EMR_RESERVED_105 ",
137
"EMR_RESERVED_106 ",
138
"EMR_RESERVED_107",
139
"EMR_RESERVED_108 ",
140
"EMR_RESERVED_109",
141
"EMR_RESERVED_110 ",
142
"EMR_COLORCORRECTPALETTE",
143
"EMR_SETICMPROFILEA ",
144
"EMR_SETICMPROFILEW ",
145
"EMR_ALPHABLEND",
146
"EMR_SETLAYOUT ",
147
"EMR_TRANSPARENTBLT",
148
"EMR_RESERVED_117 ",
149
"EMR_GRADIENTFILL",
150
"EMR_RESERVED_119 ",
151
"EMR_RESERVED_120",
152
"EMR_COLORMATCHTOTARGETW",
153
"EMR_CREATECOLORSPACEW"
154
};
155
156
int main()
157

{
158
setlocale(LC_ALL,"");
159
160
const char* pszFileName = "C:\\Documents and Settings\\joe\\桌面\\1\\00053.SPL";
161
162
if(!AnalyseFile(pszFileName))
163
PFM("Analyse File Failed!");
164
else
165
PFM("Analyse File Successed Completed!");
166
167
return 0;
168
}
169
170
171
BOOL AnalyseFile(const char* pszFileName)
172

{
173
BOOL bRet = FALSE;
174
175
DWORD dwStartPos = 0;
176
177
FILE* pFile = fopen(pszFileName,"rb");
178
179
if(!pFile)
180
{
181
PFM("Open File Failed!");
182
return bRet;
183
}
184
185
PFM("Begin Analyse
");
186
187
PFM("[1] Begin to read SPL HeaderInfo:");
188
189
/**//* =======================Headers================================ */
190
DWORD dwTmp = 0;
191
192
fseek(pFile,0,0);
193
194
fread(&dwTmp,sizeof(DWORD),1,pFile);
195
196
PFT("簽名",dwTmp);
197
198
199
fread(&dwTmp,sizeof(DWORD),1,pFile);
200
201
dwStartPos = dwTmp;
202
203
PFT("正文信息偏移:",dwTmp);
204
205
fread(&dwTmp,sizeof(DWORD),1,pFile);
206
207
PFT("文檔信息偏移(UNICODE):",dwTmp);
208
209
long pos = ftell(pFile);
210
211
fseek(pFile,dwTmp,SEEK_SET);
212
213
wchar_t pszInfo[256] =
{0};
214
pszInfo[0] = L'(';
215
216
WORD wTmp;
217
for(int i = 1;;i++)
218
{
219
fread(&wTmp,sizeof(wTmp),1,pFile);
220
221
if(!wTmp)
222
break;
223
224
memcpy((char*)&pszInfo[i],&wTmp,sizeof(wTmp));
225
}
226
pszInfo[i] = L')';
227
UPFM(pszInfo);
228
229
fseek(pFile,pos,SEEK_SET);
230
231
fread(&dwTmp,sizeof(DWORD),1,pFile);
232
233
PFT("打印端口信息偏移(UNICODE):",dwTmp);
234
235
fseek(pFile,dwTmp,SEEK_SET);
236
237
memset(pszInfo,0,sizeof(wchar_t)*256);
238
pszInfo[0] = L'(';
239
for(i = 1;;i++)
240
{
241
fread(&wTmp,sizeof(wTmp),1,pFile);
242
243
if(!wTmp)
244
break;
245
246
memcpy((char*)&pszInfo[i],&wTmp,sizeof(wTmp));
247
}
248
pszInfo[i] = L')';
249
UPFM(pszInfo);
250
251
/**//* ======================== Unknown datas ================================= */
252
PFM("[2] Begin to read SPL Unknown Datas:");
253
254
fseek(pFile,dwStartPos,SEEK_SET);
255
256
fread(&dwTmp,sizeof(DWORD),1,pFile);
257
258
PFT("未知數(shù)據(jù)",dwTmp);
259
260
fread(&dwTmp,sizeof(DWORD),1,pFile);
261
262
PFT("未知數(shù)據(jù)",dwTmp);
263
264
/**//* ======================== Record datas ================================= */
265
PFM("[3] Begin to read Record Datas:");
266
267
DWORD dwTmp2 = 0;
268
for(int i=0;;i++)
269
{
270
pos = ftell(pFile);
271
272
fread(&dwTmp,sizeof(DWORD),1,pFile);
273
274
fread(&dwTmp2,sizeof(DWORD),1,pFile);
275
276
277
printf("index: (%04d) type: 0x%04X size: %04d 0x%08X (%s)\n",i,dwTmp,dwTmp2,pos,ID_Func[dwTmp-1]);
278
279
// printf("位置: %08X",pos);
280
281
// printf("(%s)\n",ID_Func[dwTmp]);
282
283
if(dwTmp == 0x0E)
284
{
285
// printf("index: (%04d) type: 0x%04X size: %04d 0x%08X (End)\n",i,dwTmp,dwTmp2,pos,);
286
PFM("End of Record Datas.");
287
break;
288
}
289
290
fseek(pFile,pos+dwTmp2,SEEK_SET);
291
}
292
293
if(pFile) fclose(pFile);
294
bRet = TRUE;
295
296
return bRet;
297
}
298
299
300
301
302
303
304
305
306
307
308
309

2

3
#include <windows.h>4
#include <winspool.h>5
#include <stdio.h>6
#include <locale.h> 7
#include <tchar.h>8
#include <iostream>9
using namespace std;10

11

12
BOOL AnalyseFile(const char* pszFileName);13

14
void PFT(const char* pszInfo,DWORD dwData)15


{16
printf("%s: 0x%08X\n",pszInfo,dwData);17
}18

19
void PFM(const char* pszInfo)20


{21
printf("%s\n",pszInfo);22
}23

24
void UPFM(const wchar_t pszInfo[])25


{26
wprintf(L"%s\n",pszInfo);27
}28

29
static char* ID_Func[] =30


{31
"EMR_HEADER",32
"EMR_POLYBEZIER",33
"EMR_POLYGON",34
"EMR_POLYLINE",35
"EMR_POLYBEZIERTO",36
"EMR_POLYLINETO",37
"EMR_POLYPOLYLINE",38
"EMR_POLYPOLYGON",39
"EMR_SETWINDOWEXTEX", 40
"EMR_SETWINDOWORGEX", 41
"EMR_SETVIEWPORTEXTEX", 42
"EMR_SETVIEWPORTORGEX", 43
"EMR_SETBRUSHORGEX", 44
"EMR_EOF", 45
"EMR_SETPIXELV", 46
"EMR_SETMAPPERFLAGS", 47
"EMR_SETMAPMODE", 48
"EMR_SETBKMODE", 49
"EMR_SETPOLYFILLMODE", 50
"EMR_SETROP2", 51
"EMR_SETSTRETCHBLTMODE", 52
"EMR_SETTEXTALIGN", 53
"EMR_SETCOLORADJUSTMENT", 54
"EMR_SETTEXTCOLOR", 55
"EMR_SETBKCOLOR", 56
"EMR_OFFSETCLIPRGN", 57
"EMR_MOVETOEX", 58
"EMR_SETMETARGN", 59
"EMR_EXCLUDECLIPRECT", 60
"EMR_INTERSECTCLIPRECT", 61
"EMR_SCALEVIEWPORTEXTEX", 62
"EMR_SCALEWINDOWEXTEX", 63
"EMR_SAVEDC", 64
"EMR_RESTOREDC", 65
"EMR_SETWORLDTRANSFORM", 66
"EMR_MODIFYWORLDTRANSFORM", 67
"EMR_SELECTOBJECT", 68
"EMR_CREATEPEN", 69
"EMR_CREATEBRUSHINDIRECT", 70
"EMR_DELETEOBJECT", 71
"EMR_ANGLEARC", 72
"EMR_ELLIPSE", 73
"EMR_RECTANGLE", 74
"EMR_ROUNDRECT", 75
"EMR_ARC", 76
"EMR_CHORD", 77
"EMR_PIE", 78
"EMR_SELECTPALETTE", 79
"EMR_CREATEPALETTE", 80
"EMR_SETPALETTEENTRIES", 81
"EMR_RESIZEPALETTE", 82
"EMR_REALIZEPALETTE", 83
"EMR_EXTFLOODFILL", 84
"EMR_LINETO", 85
"EMR_ARCTO", 86
"EMR_POLYDRAW", 87
"EMR_SETARCDIRECTION", 88
"EMR_SETMITERLIMIT", 89
"EMR_BEGINPATH", 90
"EMR_ENDPATH", 91
"EMR_CLOSEFIGURE", 92
"EMR_FILLPATH", 93
"EMR_STROKEANDFILLPATH", 94
"EMR_STROKEPATH", 95
"EMR_FLATTENPATH", 96
"EMR_WIDENPATH", 97
"EMR_SELECTCLIPPATH", 98
"EMR_ABORTPATH",99
"69--Unknown",100

101
"EMR_GDICOMMENT",102
"EMR_FILLRGN",103
"EMR_FRAMERGN",104
"EMR_INVERTRGN",105
"EMR_PAINTRGN ",106
"EMR_EXTSELECTCLIPRGN",107
"EMR_BITBLT ",108
"EMR_STRETCHBLT",109
"EMR_MASKBLT",110
"EMR_PLGBLT",111
"EMR_SETDIBITSTODEVICE",112
"EMR_STRETCHDIBITS",113
"EMR_EXTCREATEFONTINDIRECTW",114
"EMR_EXTTEXTOUTA ",115
"EMR_EXTTEXTOUTW",116
"EMR_POLYBEZIER16",117
"EMR_POLYGON16 ",118
"EMR_POLYLINE16 ",119
"EMR_POLYBEZIERTO16",120
"EMR_POLYLINETO16 ",121
"EMR_POLYPOLYLINE16",122
"EMR_POLYPOLYGON16",123
"EMR_POLYDRAW16 ",124
"EMR_CREATEMONOBRUSH ",125
"EMR_CREATEDIBPATTERNBRUSHPT",126
"EMR_EXTCREATEPEN",127
"EMR_POLYTEXTOUTA ",128
"EMR_POLYTEXTOUTW",129
"EMR_SETICMMODE ",130
"EMR_CREATECOLORSPACE",131
"EMR_SETCOLORSPACE ",132
"EMR_DELETECOLORSPACE ",133
"EMR_GLSRECORD ",134
"EMR_GLSBOUNDEDRECORD",135
"EMR_PIXELFORMAT",136
"EMR_RESERVED_105 ",137
"EMR_RESERVED_106 ",138
"EMR_RESERVED_107",139
"EMR_RESERVED_108 ",140
"EMR_RESERVED_109",141
"EMR_RESERVED_110 ",142
"EMR_COLORCORRECTPALETTE",143
"EMR_SETICMPROFILEA ",144
"EMR_SETICMPROFILEW ",145
"EMR_ALPHABLEND",146
"EMR_SETLAYOUT ",147
"EMR_TRANSPARENTBLT",148
"EMR_RESERVED_117 ",149
"EMR_GRADIENTFILL",150
"EMR_RESERVED_119 ",151
"EMR_RESERVED_120",152
"EMR_COLORMATCHTOTARGETW",153
"EMR_CREATECOLORSPACEW"154
};155

156
int main()157


{158
setlocale(LC_ALL,""); 159

160
const char* pszFileName = "C:\\Documents and Settings\\joe\\桌面\\1\\00053.SPL";161

162
if(!AnalyseFile(pszFileName))163
PFM("Analyse File Failed!");164
else165
PFM("Analyse File Successed Completed!");166

167
return 0;168
}169

170

171
BOOL AnalyseFile(const char* pszFileName)172


{173
BOOL bRet = FALSE;174

175
DWORD dwStartPos = 0;176

177
FILE* pFile = fopen(pszFileName,"rb");178

179
if(!pFile)180

{181
PFM("Open File Failed!");182
return bRet;183
}184

185
PFM("Begin Analyse
");186

187
PFM("[1] Begin to read SPL HeaderInfo:");188

189

/**//* =======================Headers================================ */190
DWORD dwTmp = 0;191

192
fseek(pFile,0,0);193

194
fread(&dwTmp,sizeof(DWORD),1,pFile);195

196
PFT("簽名",dwTmp);197

198

199
fread(&dwTmp,sizeof(DWORD),1,pFile);200

201
dwStartPos = dwTmp;202

203
PFT("正文信息偏移:",dwTmp);204

205
fread(&dwTmp,sizeof(DWORD),1,pFile);206

207
PFT("文檔信息偏移(UNICODE):",dwTmp);208

209
long pos = ftell(pFile);210

211
fseek(pFile,dwTmp,SEEK_SET);212

213

wchar_t pszInfo[256] =
{0};214
pszInfo[0] = L'(';215
216
WORD wTmp;217
for(int i = 1;;i++)218

{219
fread(&wTmp,sizeof(wTmp),1,pFile);220

221
if(!wTmp)222
break;223

224
memcpy((char*)&pszInfo[i],&wTmp,sizeof(wTmp));225
}226
pszInfo[i] = L')';227
UPFM(pszInfo);228

229
fseek(pFile,pos,SEEK_SET);230

231
fread(&dwTmp,sizeof(DWORD),1,pFile);232

233
PFT("打印端口信息偏移(UNICODE):",dwTmp);234

235
fseek(pFile,dwTmp,SEEK_SET);236

237
memset(pszInfo,0,sizeof(wchar_t)*256);238
pszInfo[0] = L'(';239
for(i = 1;;i++)240

{241
fread(&wTmp,sizeof(wTmp),1,pFile);242

243
if(!wTmp)244
break;245

246
memcpy((char*)&pszInfo[i],&wTmp,sizeof(wTmp));247
}248
pszInfo[i] = L')';249
UPFM(pszInfo);250

251

/**//* ======================== Unknown datas ================================= */252
PFM("[2] Begin to read SPL Unknown Datas:");253

254
fseek(pFile,dwStartPos,SEEK_SET);255

256
fread(&dwTmp,sizeof(DWORD),1,pFile);257

258
PFT("未知數(shù)據(jù)",dwTmp);259

260
fread(&dwTmp,sizeof(DWORD),1,pFile);261

262
PFT("未知數(shù)據(jù)",dwTmp);263

264

/**//* ======================== Record datas ================================= */265
PFM("[3] Begin to read Record Datas:");266

267
DWORD dwTmp2 = 0;268
for(int i=0;;i++)269

{270
pos = ftell(pFile);271

272
fread(&dwTmp,sizeof(DWORD),1,pFile);273

274
fread(&dwTmp2,sizeof(DWORD),1,pFile);275

276
277
printf("index: (%04d) type: 0x%04X size: %04d 0x%08X (%s)\n",i,dwTmp,dwTmp2,pos,ID_Func[dwTmp-1]);278

279
// printf("位置: %08X",pos);280

281
// printf("(%s)\n",ID_Func[dwTmp]);282

283
if(dwTmp == 0x0E)284

{285
// printf("index: (%04d) type: 0x%04X size: %04d 0x%08X (End)\n",i,dwTmp,dwTmp2,pos,);286
PFM("End of Record Datas.");287
break;288
}289

290
fseek(pFile,pos+dwTmp2,SEEK_SET);291
}292

293
if(pFile) fclose(pFile);294
bRet = TRUE;295

296
return bRet;297
}298

299

300

301

302

303

304

305

306

307

308

309

有了以上的分析,你應(yīng)該很容易寫一個spl To EMF 文件格式的程序了。
如果代碼有什么謬誤或者Bug,請留言或者EmailToMe: xiaolu69soft@yahoo.com.cn
祝你好運~
rawdata

