原創: 星戰紫輝 http://www.shnenglu.com/rawdata 2009-2-23
關鍵字: 打印 SPL EMF 文件格式
問題:
Windows的假脫機打印會在Windows\System32\spool\PRINERS目錄下生成.spl和.shd文件,其中的打印內
容存貯在.spl文件中,但是.spl文件格式似乎未公開,那么如何才能將未知的.spl文件剝離成.emf文件呢?
首先,讓我們了解一下Windows打印機制:
這是微軟的官網的一副打印流程圖片:

其中ISV是應用軟件接口,IHV是硬件接口,左邊是XP的打印模型,右邊是Vista最新的XPS打印模
型,但兩者可以互相轉換,具有良好的兼容性。不過,這里暫時只關心XP系統的打印過程。
網絡打印過程圖:

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

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

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

文件尾就是這個樣子:

當定位到0x50的文件位置,讀取2個DWORD數據之后,就是.emf文件開始了。.emf文件格式是公開的,而
且非常簡單,是一系列EMR_XXX開口結構的緊密排列,通常以EMR_HEADER(0x01)開頭,以EMR_EOF
(0x0E)結尾。其實我們根本沒有必要去解析.emf文件格式,Windows SDK有專門顯示.emf文件的API,3個函數就
搞定:
HENHMETAFILE hEMF = GetEnhMetaFile("EMF_DumpOK.emf");
PlayEnhMetaFile (dc.m_hDC, hEMF, &rc) ;
DeleteEnhMetaFile (hEMF) ;
然后.spl文件還有一些東西,我現在還沒有解析出來,但是.emf文件已經剝離出來了,后面的可以先不理它。
然后,開始寫程序嘍,因為比較簡單,所以代碼有點隨便哦~~:)
http://www.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("未知數據",dwTmp);
259
260
fread(&dwTmp,sizeof(DWORD),1,pFile);
261
262
PFT("未知數據",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

4

5

6

7

8

9

10

11

12

13

14

15



16

17

18

19

20



21

22

23

24

25



26

27

28

29

30



31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

150

151

152

153

154

155

156

157



158

159

160

161

162

163

164

165

166

167

168

169

170

171

172



173

174

175

176

177

178

179

180



181

182

183

184

185


186

187

188

189


190

191

192

193

194

195

196

197

198

199

200

201

202

203

204

205

206

207

208

209

210

211

212

213



214

215

216

217

218



219

220

221

222

223

224

225

226

227

228

229

230

231

232

233

234

235

236

237

238

239

240



241

242

243

244

245

246

247

248

249

250

251


252

253

254

255

256

257

258

259

260

261

262

263

264


265

266

267

268

269



270

271

272

273

274

275

276

277

278

279

280

281

282

283

284



285

286

287

288

289

290

291

292

293

294

295

296

297

298

299

300

301

302

303

304

305

306

307

308

309

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