-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathatom.xml
605 lines (302 loc) · 636 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
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
<title>何仕鹏的个人博客</title>
<subtitle>healthy,weathy,freedom,lucky,happy</subtitle>
<link href="http://example.com/atom.xml" rel="self"/>
<link href="http://example.com/"/>
<updated>2022-06-30T08:59:46.201Z</updated>
<id>http://example.com/</id>
<author>
<name>拾光的碎羽</name>
</author>
<generator uri="https://hexo.io/">Hexo</generator>
<entry>
<title>AST解混淆之处理数值与字符串</title>
<link href="http://example.com/AST%E8%A7%A3%E6%B7%B7%E6%B7%86%E4%B9%8B%E5%A4%84%E7%90%86%E6%95%B0%E5%80%BC%E5%8D%81%E5%85%AD%E8%BF%9B%E5%88%B6,%20%E4%B8%AD%E8%8B%B1%E6%96%87Unicode%E5%AD%97%E7%AC%A6%E4%B8%B2/"/>
<id>http://example.com/AST%E8%A7%A3%E6%B7%B7%E6%B7%86%E4%B9%8B%E5%A4%84%E7%90%86%E6%95%B0%E5%80%BC%E5%8D%81%E5%85%AD%E8%BF%9B%E5%88%B6,%20%E4%B8%AD%E8%8B%B1%E6%96%87Unicode%E5%AD%97%E7%AC%A6%E4%B8%B2/</id>
<published>2022-06-30T03:29:25.000Z</published>
<updated>2022-06-30T08:59:46.201Z</updated>
<content type="html"><![CDATA[<p>比如有下面一段代码:</p><figure class="highlight js"><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">m7z = {</span><br><span class="line"> <span class="string">'\x67\x74'</span>: V7z[M9r.C8z(<span class="number">190</span>)][M9r.C8z(<span class="number">189</span>)],</span><br><span class="line"> <span class="string">'\x63\x68\x61\x6c\x6c\x65\x6e\x67\x65'</span>: V7z[M9r.R8z(<span class="number">190</span>)][M9r.R8z(<span class="number">425</span>)],</span><br><span class="line"> <span class="string">'\x77'</span>: r7z + H7z,</span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> a = <span class="number">0x25</span>,b = <span class="number">0b10001001</span>,c = <span class="number">0o123456</span>, e = <span class="string">"\u0068\u0065\u006c\u006c\u006f\u002c\u0041\u0053\u0054"</span>;</span><br></pre></td></tr></table></figure><p>这种把字符串处理成unicode或者utf8编码,把数字处理成非10进制,不利于我们进行调试,我们可以对其进行反混淆,变成我们更加习惯的编码或者进制。</p><p>观察AST结构:</p><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="https://img.heshipeng.com/202206301559685.png?watermark/2/text/5YWz5rOo5b6u5L-h5YWs5LyX5Y-377ya6YCG5ZCR5LiA5q2l5q2l/font/5a6L5L2T/fontsize/300" alt="image-20220630155927641" title=""> </div> <div class="image-caption">image-20220630155927641</div> </figure><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="https://img.heshipeng.com/202206301609536.png?watermark/2/text/5YWz5rOo5b6u5L-h5YWs5LyX5Y-377ya6YCG5ZCR5LiA5q2l5q2l/font/5a6L5L2T/fontsize/300" alt="image-20220630160915492" title=""> </div> <div class="image-caption">image-20220630160915492</div> </figure><p>可以看到在extra节点中的raw是utf-8编码的,而value的值是正常的。官网手册查询得知,<strong>NumericLiteral、StringLiteral类型的extra节点并非必需,这样在将其删除时,不会影响原节点</strong>。所以一种通用的解决方案是直接删除extra节点即可。</p><p>所以解混淆插件代码如下:</p><figure class="highlight js"><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">const</span> visitor =</span><br><span class="line">{</span><br><span class="line"> <span class="comment">//TODO write your code here!</span></span><br><span class="line"> <span class="function"><span class="title">NumericLiteral</span>(<span class="params">{node}</span>)</span> {</span><br><span class="line"> <span class="keyword">if</span> (node.extra && <span class="regexp">/^0[obx]/i</span>.test(node.extra.raw)) {</span><br><span class="line"> node.extra = <span class="literal">undefined</span>;</span><br><span class="line"> }</span><br><span class="line"> },</span><br><span class="line"> <span class="function"><span class="title">StringLiteral</span>(<span class="params">{node}</span>)</span></span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">if</span> (node.extra && <span class="regexp">/\\[ux]/gi</span>.test(node.extra.raw)) {</span><br><span class="line"> node.extra = <span class="literal">undefined</span>;</span><br><span class="line"> }</span><br><span class="line"> },</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>遍历遇到NumericLiteral节点时,判断extra是否为二进制,八进制,十六进制,如果是的话直接置空;同理,遍历遇到StringLiteral节点时,判断其extra是否为unicode编码或者utf8编码,如果是的话也置空。</p><p>最后解混淆的结果如下:</p><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="https://img.heshipeng.com/202206301617590.png?watermark/2/text/5YWz5rOo5b6u5L-h5YWs5LyX5Y-377ya6YCG5ZCR5LiA5q2l5q2l/font/5a6L5L2T/fontsize/300" alt="image-20220630161728547" title=""> </div> <div class="image-caption">image-20220630161728547</div> </figure>]]></content>
<summary type="html"><p>比如有下面一段代码:</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span></summary>
<category term="JS逆向" scheme="http://example.com/categories/JS%E9%80%86%E5%90%91/"/>
<category term="AST反混淆" scheme="http://example.com/categories/JS%E9%80%86%E5%90%91/AST%E5%8F%8D%E6%B7%B7%E6%B7%86/"/>
<category term="AST" scheme="http://example.com/tags/AST/"/>
<category term="JS" scheme="http://example.com/tags/JS/"/>
<category term="逆向" scheme="http://example.com/tags/%E9%80%86%E5%90%91/"/>
</entry>
<entry>
<title>AST解混淆之反混淆代码模版</title>
<link href="http://example.com/AST%E8%A7%A3%E6%B7%B7%E6%B7%86%E4%B9%8B%E5%8F%8D%E6%B7%B7%E6%B7%86%E4%BB%A3%E7%A0%81%E6%A8%A1%E7%89%88/"/>
<id>http://example.com/AST%E8%A7%A3%E6%B7%B7%E6%B7%86%E4%B9%8B%E5%8F%8D%E6%B7%B7%E6%B7%86%E4%BB%A3%E7%A0%81%E6%A8%A1%E7%89%88/</id>
<published>2022-05-26T14:32:54.000Z</published>
<updated>2022-06-30T02:59:16.554Z</updated>
<content type="html"><![CDATA[<p>话不多说,直接上模版:</p><figure class="highlight js"><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"><span class="comment">//babel库及文件模块导入</span></span><br><span class="line"><span class="keyword">const</span> fs = <span class="built_in">require</span>(<span class="string">'fs'</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment">//babel库相关,解析,转换,构建,生产</span></span><br><span class="line"><span class="keyword">const</span> parser = <span class="built_in">require</span>(<span class="string">"@babel/parser"</span>);</span><br><span class="line"><span class="keyword">const</span> traverse = <span class="built_in">require</span>(<span class="string">"@babel/traverse"</span>).default;</span><br><span class="line"><span class="keyword">const</span> types = <span class="built_in">require</span>(<span class="string">"@babel/types"</span>);</span><br><span class="line"><span class="keyword">const</span> generator = <span class="built_in">require</span>(<span class="string">"@babel/generator"</span>).default;</span><br><span class="line"></span><br><span class="line"><span class="comment">//读取文件</span></span><br><span class="line"><span class="keyword">if</span> (process.argv.length < <span class="number">4</span>) {</span><br><span class="line"><span class="built_in">console</span>.log(<span class="string">"Usage: node ${file}.js ${encode}.js ${decode}.js"</span>);</span><br><span class="line"> process.exit(<span class="number">1</span>);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">let</span> input_file = process.argv[<span class="number">2</span>], output_file = process.argv[<span class="number">3</span>];</span><br><span class="line"></span><br><span class="line"><span class="keyword">let</span> jscode = fs.readFileSync(input_file, {<span class="attr">encoding</span>: <span class="string">"utf-8"</span>});</span><br><span class="line"><span class="comment">//转换为ast树</span></span><br><span class="line"><span class="keyword">let</span> ast = parser.parse(jscode);</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> visitor = </span><br><span class="line">{</span><br><span class="line"> <span class="comment">//TODO write your code here!</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">//some function code</span></span><br><span class="line"></span><br><span class="line"><span class="comment">//调用插件,处理源代码</span></span><br><span class="line">traverse(ast, visitor);</span><br><span class="line"></span><br><span class="line"><span class="comment">//生成新的js code,并保存到文件中输出</span></span><br><span class="line"><span class="keyword">let</span> {code} = generator(ast);</span><br><span class="line">fs.writeFile(output_file, code, <span class="function">(<span class="params">err</span>)=></span>{});</span><br></pre></td></tr></table></figure><p>上面代码保存成一个文件,比如decode_obfuscator.js ,然后执行以下命令即可完成反混淆:</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">node decode_obfuscator.js input.js output.js</span><br></pre></td></tr></table></figure><p>input.js表示待解混淆的文件,output.js表示解混淆之后保存的结果文件。</p><p>接着看看decode_obfuscator.js文件的内容:</p><p>主要分三步,第一步是引入parser模块,并调用相关的方法把JS源码转化为AST;第二步,对AST进行遍历,处理相应的节点,主要用到的是babel库的traverse模块;第三步是调用generator模块的相关方法,把AST还原成JS代码。其中第二步可以调用多次traverse,编写多个visitor,因为下一次遍历AST可能需要上一次遍历并处理之后的结果。</p>]]></content>
<summary type="html"><p>话不多说,直接上模版:</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span</summary>
<category term="JS逆向" scheme="http://example.com/categories/JS%E9%80%86%E5%90%91/"/>
<category term="AST反混淆" scheme="http://example.com/categories/JS%E9%80%86%E5%90%91/AST%E5%8F%8D%E6%B7%B7%E6%B7%86/"/>
<category term="AST" scheme="http://example.com/tags/AST/"/>
<category term="JS" scheme="http://example.com/tags/JS/"/>
<category term="逆向" scheme="http://example.com/tags/%E9%80%86%E5%90%91/"/>
</entry>
<entry>
<title>AST解混淆系列汇总</title>
<link href="http://example.com/AST%E8%A7%A3%E6%B7%B7%E6%B7%86%E7%B3%BB%E5%88%97%E6%B1%87%E6%80%BB/"/>
<id>http://example.com/AST%E8%A7%A3%E6%B7%B7%E6%B7%86%E7%B3%BB%E5%88%97%E6%B1%87%E6%80%BB/</id>
<published>2022-05-26T14:05:38.000Z</published>
<updated>2022-06-30T08:36:26.134Z</updated>
<content type="html"><![CDATA[<h1 id="入门篇"><a href="#入门篇" class="headerlink" title="入门篇"></a>入门篇</h1><p><a href="https://blog.heshipeng.com/JS%E9%80%86%E5%90%91%E4%B9%8B%E4%BB%A3%E7%A0%81%E6%B7%B7%E6%B7%86%E7%9A%84%E5%8E%9F%E7%90%86/">JS逆向之代码混淆的原理</a></p><p><a href="https://blog.heshipeng.com/JS%E4%BB%A3%E7%A0%81%E5%AE%89%E5%85%A8%E9%98%B2%E6%8A%A4%E5%8E%9F%E7%90%86%E2%80%94%E2%80%94AST%E6%B7%B7%E6%B7%86%E5%8E%9F%E7%90%86/">JS代码安全防护原理——AST混淆原理</a></p><p><a href="https://blog.heshipeng.com/%E4%B8%80%E6%96%87%E6%95%99%E4%BD%A0%E5%A6%82%E4%BD%95%E5%88%A9%E7%94%A8AST%E8%BF%98%E5%8E%9FJS%E6%B7%B7%E6%B7%86/">一文教你如何利用AST还原JS混淆</a></p><p><a href="https://blog.heshipeng.com/AST解混淆之反混淆代码模版/">AST解混淆之反混淆代码模版</a></p><h1 id="基础篇"><a href="#基础篇" class="headerlink" title="基础篇"></a>基础篇</h1><p><a href="https://blog.heshipeng.com/AST解混淆之处理数值十六进制, 中英文Unicode字符串/">AST解混淆之处理数值十六进制, 中英文Unicode字符串</a></p><h1 id="案例篇"><a href="#案例篇" class="headerlink" title="案例篇"></a>案例篇</h1><p>持续更新中…</p>]]></content>
<summary type="html"><h1 id="入门篇"><a href="#入门篇" class="headerlink" title="入门篇"></a>入门篇</h1><p><a href="https://blog.heshipeng.com/JS%E9%80%86%E5%90%91%E4%B9%8B%</summary>
<category term="JS逆向" scheme="http://example.com/categories/JS%E9%80%86%E5%90%91/"/>
<category term="AST反混淆" scheme="http://example.com/categories/JS%E9%80%86%E5%90%91/AST%E5%8F%8D%E6%B7%B7%E6%B7%86/"/>
<category term="AST" scheme="http://example.com/tags/AST/"/>
<category term="JS" scheme="http://example.com/tags/JS/"/>
<category term="逆向" scheme="http://example.com/tags/%E9%80%86%E5%90%91/"/>
</entry>
<entry>
<title>一文教你如何利用AST还原JS混淆</title>
<link href="http://example.com/%E4%B8%80%E6%96%87%E6%95%99%E4%BD%A0%E5%A6%82%E4%BD%95%E5%88%A9%E7%94%A8AST%E8%BF%98%E5%8E%9FJS%E6%B7%B7%E6%B7%86/"/>
<id>http://example.com/%E4%B8%80%E6%96%87%E6%95%99%E4%BD%A0%E5%A6%82%E4%BD%95%E5%88%A9%E7%94%A8AST%E8%BF%98%E5%8E%9FJS%E6%B7%B7%E6%B7%86/</id>
<published>2022-04-25T15:06:51.000Z</published>
<updated>2022-06-29T15:20:26.409Z</updated>
<content type="html"><![CDATA[<blockquote><p>如果不了解JS混淆原理以及常见的混淆手段,可以戳我以前写过的2篇文章:<a href="https://blog.heshipeng.com/JS%E4%BB%A3%E7%A0%81%E5%AE%89%E5%85%A8%E9%98%B2%E6%8A%A4%E5%8E%9F%E7%90%86%E2%80%94%E2%80%94AST%E6%B7%B7%E6%B7%86%E5%8E%9F%E7%90%86/">JS代码安全防护原理——AST混淆原理</a> 和 <a href="https://blog.heshipeng.com/JS%E9%80%86%E5%90%91%E4%B9%8B%E4%BB%A3%E7%A0%81%E6%B7%B7%E6%B7%86%E7%9A%84%E5%8E%9F%E7%90%86/">JS逆向之代码混淆的原理</a></p></blockquote><p>开篇先回答一个问题,为什么需要反混淆?因为一般具有防护的JS代码都会经过混淆处理,虽然经过混淆的代码完全可以不处理混淆依旧对其进行逆向,但是由于字符串,数字都是混淆的,同时源码中冗余了很多无关的代码,控制流程平台化的存在更是让我们在代码的阅读上有了很大的障碍,这样下来导致逆向源码耗费的时间需要成倍的增加。如果有一种方式,能够让我们获得的源码就是高可阅读性,也没有冗余的代码,能帮我们不止一点点地提高逆向源码的效率,你学还是不学?</p><h1 id="什么是AST?"><a href="#什么是AST?" class="headerlink" title="什么是AST?"></a>什么是AST?</h1><p>所谓磨刀不误砍材功,既然反混淆是必要的,那跟AST有啥关系?不着急,容我慢慢解释。试想一下,要反混淆的话,是不是得处理JS源码?对于源码而言,如果只是一个文本文件,是不是非常不好处理(变量,常量,函数,分支什么的都是分散的)?所以我们必须把源码预处理成一种利于解混淆的形式,比如JSON格式?如果大学有接触过编译原理的话,一定有接触一个概念叫做语法树。没错,AST(Abstract Syntax Tree),中文抽象语法树,简称语法树(Syntax Tree),是源代码的抽象语法结构的树状表现形式,树上的每个节点都表示源代码中的一种结构。</p><p>让我们看看AST长啥样?打开AST提供的一个解析网站:<a href="https://astexplorer.net/">https://astexplorer.net/</a> ,其顶部可以选择语言,编译器。语法树没有单一的格式,选择不同的语言、不同的编译器,得到的结果也是不一样的,在 JavaScript 中,编译器有 Acorn、Espree、Esprima、Recast、Uglify-JS 等,使用最多的是 Babel,后续的学习也是以 Babel 为例。如下图:</p><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="https://img.heshipeng.com/202206292307135.png?watermark/2/text/5YWz5rOo5b6u5L-h5YWs5LyX5Y-377ya6YCG5ZCR5LiA5q2l5q2l/font/5a6L5L2T/fontsize/300" alt="image-20220629155332110" title=""> </div> <div class="image-caption">image-20220629155332110</div> </figure><h1 id="Babel简介"><a href="#Babel简介" class="headerlink" title="Babel简介"></a>Babel简介</h1><p>Babel 是一个 JavaScript 编译器,也可以说是一个解析库。Babel 内置了很多分析 JavaScript 代码的方法,我们可以利用 Babel 将 JavaScript 代码转换成 AST 语法树,然后增删改查等操作之后,再转换成 JavaScript 代码。</p><p>在做逆向解混淆中,主要用到了 Babel 的以下几个功能包,本文也仅介绍以下几个功能包:</p><ol><li><code>@babel/core</code>:Babel 编译器本身,提供了 babel 的编译 API;</li><li><code>@babel/parser</code> :将 JavaScript 代码解析成 AST 语法树;</li><li><code>@babel/traverse</code>: 遍历、修改 AST 语法树的各个节点;</li><li><code>@babel/generator</code>:将 AST 还原成 JavaScript 代码;</li><li><code>@babel/types</code>:判断、验证节点的类型、构建新 AST 节点等。</li></ol><p>用一张图说明上面各个模块的功能:</p><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="https://img.heshipeng.com/202206292307165.png?watermark/2/text/5YWz5rOo5b6u5L-h5YWs5LyX5Y-377ya6YCG5ZCR5LiA5q2l5q2l/font/5a6L5L2T/fontsize/300" alt="640" title=""> </div> <div class="image-caption">640</div> </figure><h2 id="babel库的安装"><a href="#babel库的安装" class="headerlink" title="babel库的安装"></a>babel库的安装</h2><p>安装完NodeJS之后,使用命令<code>npm install @babel/core</code>进行安装即可。</p><h2 id="babel-core"><a href="#babel-core" class="headerlink" title="@babel/core"></a>@babel/core</h2><p>Babel 编译器本身,被拆分成了三个模块:<code>@babel/parser</code>、<code>@babel/traverse</code>、<code>@babel/generator</code>,比如以下方法的导入效果都是一样的:</p><figure class="highlight js"><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="keyword">const</span> parse = <span class="built_in">require</span>(<span class="string">"@babel/parser"</span>).parse;</span><br><span class="line"><span class="keyword">const</span> parse = <span class="built_in">require</span>(<span class="string">"@babel/core"</span>).parse;</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> traverse = <span class="built_in">require</span>(<span class="string">"@babel/traverse"</span>).default</span><br><span class="line"><span class="keyword">const</span> traverse = <span class="built_in">require</span>(<span class="string">"@babel/core"</span>).traverse</span><br></pre></td></tr></table></figure><h2 id="babel-parser"><a href="#babel-parser" class="headerlink" title="@babel/parser"></a>@babel/parser</h2><p><code>@babel/parser</code> 可以将 JavaScript 代码解析成 AST 语法树,其中主要提供了两个方法:</p><ul><li><code>parser.parse(code, [{options}])</code>:解析一段 JavaScript 代码;</li><li><code>parser.parseExpression(code, [{options}])</code>:考虑到了性能问题,解析单个 JavaScript 表达式。</li></ul><p>部分可选参数 <code>options</code>:</p><div class="table-container"><table><thead><tr><th style="text-align:left">参数</th><th style="text-align:left">描述</th></tr></thead><tbody><tr><td style="text-align:left">allowImportExportEverywhere</td><td style="text-align:left">默认 import 和 export 声明语句只能出现在程序的最顶层,设置为 true 则在任何地方都可以声明</td></tr><tr><td style="text-align:left">allowReturnOutsideFunction</td><td style="text-align:left">默认如果在顶层中使用 return 语句会引起错误,设置为 true 就不会报错</td></tr><tr><td style="text-align:left">sourceType</td><td style="text-align:left">默认为 script,当代码中含有 import 、export 等关键字时会报错,需要指定为 module</td></tr><tr><td style="text-align:left">errorRecovery</td><td style="text-align:left">默认如果 babel 发现一些不正常的代码就会抛出错误,设置为 true 则会在保存解析错误的同时继续解析代码,错误的记录将被保存在最终生成的 AST 的 errors 属性中,当然如果遇到严重的错误,依然会终止解析</td></tr></tbody></table></div><p>好了,看完理论知识,来实践下:</p><figure class="highlight js"><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="keyword">const</span> parser = <span class="built_in">require</span>(<span class="string">"@babel/parser"</span>);</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> code = <span class="string">"var a = '\u0068\u0065\u006c\u006c\u006f\u002c\u0041\u0053\u0054'; \n console['log'](a);"</span>;</span><br><span class="line"><span class="keyword">const</span> ast = parser.parse(code, {<span class="attr">sourceType</span>: <span class="string">"module"</span>})</span><br><span class="line"><span class="built_in">console</span>.log(ast)</span><br></pre></td></tr></table></figure><p>执行,结果如下,可以看到跟在 <a href="https://astexplorer.net/">https://astexplorer.net/</a> 中看到的是一致的。</p><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="https://img.heshipeng.com/202206292307184.png?watermark/2/text/5YWz5rOo5b6u5L-h5YWs5LyX5Y-377ya6YCG5ZCR5LiA5q2l5q2l/font/5a6L5L2T/fontsize/300" alt="image-20220629171249989" title=""> </div> <div class="image-caption">image-20220629171249989</div> </figure><h2 id="babel-generator"><a href="#babel-generator" class="headerlink" title="@babel/generator"></a>@babel/generator</h2><p><code>@babel/generator</code> 可以将 AST 还原成 JavaScript 代码,提供了一个 <code>generate</code> 方法:<code>generate(ast, [{options}], code)</code>。</p><p>部分可选参数 <code>options</code>:</p><div class="table-container"><table><thead><tr><th style="text-align:left">参数</th><th style="text-align:left">描述</th></tr></thead><tbody><tr><td style="text-align:left">auxiliaryCommentBefore</td><td style="text-align:left">在输出文件内容的头部添加注释块文字</td></tr><tr><td style="text-align:left">auxiliaryCommentAfter</td><td style="text-align:left">在输出文件内容的末尾添加注释块文字</td></tr><tr><td style="text-align:left">comments</td><td style="text-align:left">输出内容是否包含注释</td></tr><tr><td style="text-align:left">compact</td><td style="text-align:left">输出内容是否不添加空格,避免格式化</td></tr><tr><td style="text-align:left">concise</td><td style="text-align:left">输出内容是否减少空格使其更紧凑一些</td></tr><tr><td style="text-align:left">minified</td><td style="text-align:left">是否压缩输出代码</td></tr><tr><td style="text-align:left">retainLines</td><td style="text-align:left">尝试在输出代码中使用与源代码中相同的行号</td></tr></tbody></table></div><p>我们运行一段下面的代码:</p><figure class="highlight js"><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">const</span> parser = <span class="built_in">require</span>(<span class="string">"@babel/parser"</span>);</span><br><span class="line"><span class="keyword">const</span> generate = <span class="built_in">require</span>(<span class="string">"@babel/generator"</span>).default</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> code = <span class="string">"var a = 'Hello, world'"</span>;</span><br><span class="line"><span class="keyword">const</span> ast = parser.parse(code, {<span class="attr">sourceType</span>: <span class="string">"module"</span>})</span><br><span class="line"></span><br><span class="line"><span class="comment">// 修改id名为b</span></span><br><span class="line">ast.program.body[<span class="number">0</span>].declarations[<span class="number">0</span>].id.name = <span class="string">"b"</span>;</span><br><span class="line"><span class="comment">// 修改变量值为Hello</span></span><br><span class="line">ast.program.body[<span class="number">0</span>].declarations[<span class="number">0</span>].init.value = <span class="string">"Hello"</span>;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment">// generate模块将AST语法树转换成JS代码</span></span><br><span class="line"><span class="keyword">const</span> result = generate(ast, {<span class="attr">minified</span>: <span class="literal">true</span>})</span><br><span class="line"><span class="built_in">console</span>.log(result.code)</span><br></pre></td></tr></table></figure><p>最终输出结果<code>var b="Hello";</code>,变量名和值都成功更改了,由于加了压缩处理,等号左右两边的空格也没了。</p><p>代码里 <code>{minified: true}</code> 演示了如何添加可选参数,这里表示压缩输出代码,<code>generate</code> 得到的 <code>result</code> 得到的是一个对象,其中的 <code>code</code> 属性才是最终的 JS 代码。</p><p>代码里 <code>ast.program.body[0].declarations[0].id.name</code> 是 a 在 AST 中的位置,<code>ast.program.body[0].declarations[0].init.value</code> 是 a的值即字符串Hello 在 AST 中的位置,如下图所示:</p><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="https://img.heshipeng.com/202206292307216.png?watermark/2/text/5YWz5rOo5b6u5L-h5YWs5LyX5Y-377ya6YCG5ZCR5LiA5q2l5q2l/font/5a6L5L2T/fontsize/300" alt="image-20220629175017218" title=""> </div> <div class="image-caption">image-20220629175017218</div> </figure><h2 id="babel-traverse"><a href="#babel-traverse" class="headerlink" title="@babel/traverse"></a>@babel/traverse</h2><p>当代码多了,我们不可能像前面那样挨个定位并修改,对于相同类型的节点,我们可以直接遍历所有节点来进行修改,这里就用到了 <code>@babel/traverse</code>,它通常和 <code>visitor</code> 一起使用,<code>visitor</code> 是一个对象,这个名字是可以随意取的,<code>visitor</code> 里可以定义一些方法来过滤节点,这里还是用一个例子来演示:</p><figure class="highlight js"><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="keyword">const</span> parser = <span class="built_in">require</span>(<span class="string">"@babel/parser"</span>);</span><br><span class="line"><span class="keyword">const</span> generate = <span class="built_in">require</span>(<span class="string">"@babel/generator"</span>).default</span><br><span class="line"><span class="keyword">const</span> traverse = <span class="built_in">require</span>(<span class="string">"@babel/traverse"</span>).default</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> code = <span class="string">"var a = 'Hello, world'"</span>;</span><br><span class="line"><span class="keyword">const</span> ast = parser.parse(code, {<span class="attr">sourceType</span>: <span class="string">"module"</span>})</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> visitor = {</span><br><span class="line"> <span class="function"><span class="title">Identifier</span>(<span class="params">path</span>)</span> {</span><br><span class="line"> path.node.name = path.node.name == <span class="string">'a'</span> ? <span class="string">'a'</span> : <span class="string">'b'</span>;</span><br><span class="line"> },</span><br><span class="line"> <span class="function"><span class="title">StringLiteral</span>(<span class="params">path</span>)</span>{</span><br><span class="line"> path.node.value = <span class="string">"I Love JavaScript!"</span></span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">traverse(ast, visitor)</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> result = generate(ast, {<span class="attr">minified</span>: <span class="literal">true</span>})</span><br><span class="line"><span class="built_in">console</span>.log(result.code)</span><br></pre></td></tr></table></figure><p>输出为<code>var b="I Love JavaScript!";</code>,通过AST语法树,将变量a更名为变量b,同时将其值修改为”I Love JavaScript!”。</p><p>我们看看代码主逻辑,首先定义一个<code>visitor</code>,然后定义对应类型的处理方法,<code>traverse</code> 接收两个参数,第一个是 AST 对象,第二个是 <code>visitor</code>,当 <code>traverse</code> 遍历所有节点,遇到节点类型为 <code>StringLiteral</code> 和 <code>Identifier</code> 时,就会调用 <code>visitor</code> 中对应的处理方法。 之所以定义<code>StringLiteral</code>和<code>Identifier</code>两种类型,是因为在抽象语法树中变量a是一个标识符,其类型为<code>Identifier</code>,而a的值是一个字符串,其类型为<code>StringLiteral</code>。<code>visitor</code> 中的方法会接收一个当前节点的 <code>path</code> 对象,该对象的类型是 <code>NodePath</code>,该对象有非常多的属性,以下介绍几种最常用的:</p><div class="table-container"><table><thead><tr><th style="text-align:left">属性</th><th style="text-align:left">描述</th></tr></thead><tbody><tr><td style="text-align:left">toString()</td><td style="text-align:left">当前路径的源码</td></tr><tr><td style="text-align:left">node</td><td style="text-align:left">当前路径的节点</td></tr><tr><td style="text-align:left">parent</td><td style="text-align:left">当前路径的父级节点</td></tr><tr><td style="text-align:left">parentPath</td><td style="text-align:left">当前路径的父级路径</td></tr><tr><td style="text-align:left">type</td><td style="text-align:left">当前路径的类型</td></tr></tbody></table></div><p><code>path</code> 对象除了有很多属性以外,还有很多方法,比如替换节点、删除节点、插入节点、寻找父级节点、获取同级节点、添加注释、判断节点类型等,可在需要时查询相关文档或查看源码,后续介绍 <code>@babel/types</code> 部分将会举部分例子来演示,以后的实战文章中也会有相关实例。</p><p>如果多个类型的节点,处理的方式都一样,那么还可以使用 <code>|</code> 将所有节点连接成字符串,将同一个方法应用到所有节点:</p><figure class="highlight js"><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="keyword">const</span> visitor = {</span><br><span class="line"> <span class="string">"Identifier|StringLiteral"</span>(path) {</span><br><span class="line"> path.node.name = path.node.name == <span class="string">'a'</span> ? <span class="string">'a'</span> : <span class="string">'b'</span>;</span><br><span class="line"> path.node.value = path.node.name == <span class="string">'a'</span> ? path.node.value : <span class="string">"I Love JavaScript!"</span></span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p><code>visitor</code> 对象有多种写法,以下几种写法的效果都是一样的:</p><figure class="highlight js"><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> visitor = {</span><br><span class="line"> <span class="function"><span class="title">Identifier</span>(<span class="params">path</span>)</span> {</span><br><span class="line"> path.node.name = path.node.name == <span class="string">'a'</span> ? <span class="string">'a'</span> : <span class="string">'b'</span>;</span><br><span class="line"> },</span><br><span class="line"> <span class="function"><span class="title">StringLiteral</span>(<span class="params">path</span>)</span>{</span><br><span class="line"> path.node.value = <span class="string">"I Love JavaScript!"</span></span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> visitor = {</span><br><span class="line"> <span class="attr">Identifier</span>: <span class="function"><span class="keyword">function</span>(<span class="params">path</span>) </span>{</span><br><span class="line"> path.node.name = path.node.name == <span class="string">'a'</span> ? <span class="string">'a'</span> : <span class="string">'b'</span>;</span><br><span class="line"> },</span><br><span class="line"> <span class="attr">StringLiteral</span>: <span class="function"><span class="keyword">function</span>(<span class="params">path</span>)</span>{</span><br><span class="line"> path.node.value = <span class="string">"I Love JavaScript!"</span></span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> visitor = {</span><br><span class="line"> <span class="attr">Identifier</span>: {</span><br><span class="line"> <span class="function"><span class="title">enter</span>(<span class="params">path</span>)</span> {</span><br><span class="line"> path.node.name = path.node.name == <span class="string">'a'</span> ? <span class="string">'a'</span> : <span class="string">'b'</span>;</span><br><span class="line"> }</span><br><span class="line"> },</span><br><span class="line"> <span class="attr">StringLiteral</span>: {</span><br><span class="line"> <span class="function"><span class="title">enter</span>(<span class="params">path</span>)</span>{</span><br><span class="line"> path.node.value = <span class="string">"I Love JavaScript!"</span></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"><span class="keyword">const</span> visitor = {</span><br><span class="line"> <span class="function"><span class="title">enter</span>(<span class="params">path</span>)</span> {</span><br><span class="line"> <span class="keyword">if</span> (path.node.type == <span class="string">"Identifier"</span>) {</span><br><span class="line"> path.node.name = path.node.name == <span class="string">'a'</span> ? <span class="string">'a'</span> : <span class="string">'b'</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">else</span> <span class="keyword">if</span> (path.node.type === <span class="string">"StringLiteral"</span>) {</span><br><span class="line"> path.node.value = <span class="string">"I Love JavaScript!"</span></span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>以上几种写法中有用到了 <code>enter</code> 方法,在节点的遍历过程中,进入节点(enter)与退出(exit)节点都会访问一次节点,<code>traverse</code> 默认在进入节点时进行节点的处理,如果要在退出节点时处理,那么在 <code>visitor</code> 中就必须声明 <code>exit</code> 方法。</p><h2 id="babel-types"><a href="#babel-types" class="headerlink" title="@babel/types"></a>@babel/types</h2><p>前面提到过,babel/types主要2个作用,一是包含了许多节点的类型,经常用来做类型判断。比如:</p><figure class="highlight js"><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">types.stringLiteral(<span class="string">"Hello World"</span>); <span class="comment">// string</span></span><br><span class="line">types.numericLiteral(<span class="number">100</span>); <span class="comment">// number</span></span><br><span class="line">types.booleanLiteral(<span class="literal">true</span>); <span class="comment">// boolean</span></span><br><span class="line">types.nullLiteral(); <span class="comment">// null</span></span><br><span class="line">types.identifier(); <span class="comment">// undefined</span></span><br><span class="line">types.regExpLiteral(<span class="string">"\\.js?$"</span>, <span class="string">"g"</span>); <span class="comment">// 正则</span></span><br></pre></td></tr></table></figure><p>输出如下:</p><figure class="highlight javascript"><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="string">"Hello World"</span></span><br><span class="line"><span class="number">100</span></span><br><span class="line"><span class="literal">true</span></span><br><span class="line"><span class="literal">null</span></span><br><span class="line"><span class="literal">undefined</span></span><br><span class="line">/\.js?$/g</span><br></pre></td></tr></table></figure><p>另一个作用是构建新的 AST 节点。通过一个简单的例子解释下吧。比如我们有如下代码</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> a=<span class="string">"Hello"</span>;</span><br></pre></td></tr></table></figure><p>想在这句常量的定义之后插入<code>let b=1;</code> 这段代码,变成如下:</p><figure class="highlight js"><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="keyword">const</span> a=<span class="string">"Hello"</span>;</span><br><span class="line"><span class="keyword">let</span> b=<span class="number">1</span>;</span><br></pre></td></tr></table></figure><p>我们在 <a href="https://astexplorer.net/">https://astexplorer.net/</a> 中输入这2行代码,然后对照着看下代码该如何写?</p><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="https://img.heshipeng.com/202206292307245.png?watermark/2/text/5YWz5rOo5b6u5L-h5YWs5LyX5Y-377ya6YCG5ZCR5LiA5q2l5q2l/font/5a6L5L2T/fontsize/300" alt="image-20220629221958113" title=""> </div> <div class="image-caption">image-20220629221958113</div> </figure><p>可以看到增加了一个变量声明之后,会在body下面增加一个VariableDeclaration节点。所以我们编写visitor的时候,相应的代码也应该在VariableDeclaration下面。</p><p>我们的思路就是在遍历节点时,遍历到 <code>VariableDeclaration</code> 节点,就在其后面增加一个 <code>VariableDeclaration</code> 节点,生成 <code>VariableDeclaration</code> 节点,可以使用 <code>types.variableDeclaration()</code> 方法,在 types 中各种方法名称和我们在 AST 中看到的是一样的,只不过首字母是小写的,所以我们不需要知道所有方法的情况下,也能大致推断其方法名,只知道这个方法还不行,还得知道传入的参数是什么,可以查文档。</p><p>通过文档,可以看到variableDeclarator需要<code>kind</code> 和 <code>declarations</code> 两个参数,其中 <code>declarations</code> 是 <code>VariableDeclarator</code> 类型的节点组成的列表。kind表示声明的类型,比如const, var, let。这里顺便问下为什么declarations是一个列表呢?因为JS是支持同时声明多个变量的,比如<code>let a, b, c = 1</code>,显然,declarations有多个。</p><p>接下来我们还需要进一步定义 <code>declarator</code>,也就是 <code>VariableDeclarator</code> 类型的节点。调用的是<code>variableDeclarator</code>方法,这个方法接受2个参数,第一个是类型,我们传入<code>types.identifier("b")</code>,表示一个名为b的标识符,第二个参数是变量的初始化方式,这里我们传入<code>types.numericLiteral(1)</code>,因为我们是要给b赋值一个数值类型1。</p><p>最后我们要指明这条赋值表达式的插入位置,<code>path.insertAfter()</code> 插入节点语句后面加了一句 <code>path.stop()</code>,表示插入完成后立即停止遍历当前节点和后续的子节点,添加的新节点也是 <code>VariableDeclaration</code>,如果不加停止语句的话,就会无限循环插入下去。</p><p>完整的代码如下:</p><figure class="highlight javascript"><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"><span class="keyword">const</span> parser = <span class="built_in">require</span>(<span class="string">"@babel/parser"</span>);</span><br><span class="line"><span class="keyword">const</span> generate = <span class="built_in">require</span>(<span class="string">"@babel/generator"</span>).default</span><br><span class="line"><span class="keyword">const</span> traverse = <span class="built_in">require</span>(<span class="string">"@babel/traverse"</span>).default</span><br><span class="line"><span class="keyword">const</span> types = <span class="built_in">require</span>(<span class="string">"@babel/types"</span>);</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> code = <span class="string">"const a = 'Hello'"</span>;</span><br><span class="line"><span class="keyword">const</span> ast = parser.parse(code, {<span class="attr">sourceType</span>: <span class="string">"module"</span>})</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> visitor = {</span><br><span class="line"> <span class="function"><span class="title">VariableDeclaration</span>(<span class="params">path</span>)</span>{</span><br><span class="line"> <span class="keyword">let</span> init = types.numericLiteral(<span class="number">1</span>); <span class="comment">// 变量的初始化方式</span></span><br><span class="line"> <span class="keyword">let</span> declarator = types.variableDeclarator(types.identifier(<span class="string">"b"</span>), init) <span class="comment">// 声明变量名</span></span><br><span class="line"> <span class="keyword">let</span> declaration = types.variableDeclaration(<span class="string">"let"</span>, [declarator]); <span class="comment">// 声明类型</span></span><br><span class="line"> path.insertAfter(declaration) <span class="comment">// 插入位置</span></span><br><span class="line"> path.stop()</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">traverse(ast, visitor)</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> result = generate(ast, {<span class="attr">minified</span>: <span class="literal">true</span>})</span><br><span class="line"><span class="built_in">console</span>.log(result.code)</span><br></pre></td></tr></table></figure><h1 id="小案例"><a href="#小案例" class="headerlink" title="小案例"></a>小案例</h1><p>最后通过一个小案例来为这篇入门文章结尾吧。</p><p>我们有如下一段代码:</p><figure class="highlight js"><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">var</span> a = <span class="string">"\x44\x61\x74\x65"</span>;</span><br><span class="line"><span class="keyword">const</span> b = <span class="string">"\u0068\u0065\u006c\u006c\u006f\u002c\u0041\u0053\u0054"</span>;</span><br><span class="line"><span class="keyword">let</span> c = <span class="number">0b10001001</span>;</span><br><span class="line"><span class="keyword">let</span> d = <span class="number">0o123456</span>;</span><br></pre></td></tr></table></figure><p>这些数字或者字符串看起来非常不方便,所以我们需要对其进行解混淆处理,解混淆代码如下:</p><figure class="highlight js"><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"><span class="keyword">const</span> parser = <span class="built_in">require</span>(<span class="string">"@babel/parser"</span>);</span><br><span class="line"><span class="keyword">const</span> generate = <span class="built_in">require</span>(<span class="string">"@babel/generator"</span>).default</span><br><span class="line"><span class="keyword">const</span> traverse = <span class="built_in">require</span>(<span class="string">"@babel/traverse"</span>).default</span><br><span class="line"><span class="keyword">const</span> types = <span class="built_in">require</span>(<span class="string">"@babel/types"</span>);</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> code = <span class="string">"var a = '\x44\x61\x74\x65'; \</span></span><br><span class="line"><span class="string">const b = '\u0068\u0065\u006c\u006c\u006f\u002c\u0041\u0053\u0054'; \</span></span><br><span class="line"><span class="string">let c = 0b10001001; \</span></span><br><span class="line"><span class="string">let d = 0o123456;"</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> ast = parser.parse(code, {<span class="attr">sourceType</span>: <span class="string">"module"</span>})</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> visitor = {</span><br><span class="line"> <span class="function"><span class="title">NumericLiteral</span>(<span class="params">{node}</span>)</span> {</span><br><span class="line"> <span class="keyword">if</span> (node.extra && <span class="regexp">/^0[obx]/i</span>.test(node.extra.raw)) {</span><br><span class="line"> node.extra = <span class="literal">undefined</span>;</span><br><span class="line"> }</span><br><span class="line"> },</span><br><span class="line"> <span class="function"><span class="title">StringLiteral</span>(<span class="params">{node}</span>)</span></span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">if</span> (node.extra && <span class="regexp">/\\[ux]/gi</span>.test(node.extra.raw)) {</span><br><span class="line"> node.extra = <span class="literal">undefined</span>;</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">traverse(ast, visitor)</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> result = generate(ast, {<span class="attr">minified</span>: <span class="literal">true</span>})</span><br><span class="line"><span class="built_in">console</span>.log(result.code)</span><br></pre></td></tr></table></figure><p>解混淆结果如下:</p><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="https://img.heshipeng.com/202206292307274.png?watermark/2/text/5YWz5rOo5b6u5L-h5YWs5LyX5Y-377ya6YCG5ZCR5LiA5q2l5q2l/font/5a6L5L2T/fontsize/300" alt="image-20220629224644829" title=""> </div> <div class="image-caption">image-20220629224644829</div> </figure>]]></content>
<summary type="html"><blockquote>
<p>如果不了解JS混淆原理以及常见的混淆手段,可以戳我以前写过的2篇文章:<a href="https://blog.heshipeng.com/JS%E4%BB%A3%E7%A0%81%E5%AE%89%E5%85%A8%E9%98%B2%E6%8A</summary>
<category term="JS逆向" scheme="http://example.com/categories/JS%E9%80%86%E5%90%91/"/>
<category term="AST反混淆" scheme="http://example.com/categories/JS%E9%80%86%E5%90%91/AST%E5%8F%8D%E6%B7%B7%E6%B7%86/"/>
<category term="AST" scheme="http://example.com/tags/AST/"/>
<category term="JS" scheme="http://example.com/tags/JS/"/>
<category term="逆向" scheme="http://example.com/tags/%E9%80%86%E5%90%91/"/>
</entry>
<entry>
<title>JS逆向案例——天眼查过极验滑块验证码</title>
<link href="http://example.com/JS%E9%80%86%E5%90%91%E6%A1%88%E4%BE%8B%E2%80%94%E2%80%94%E5%A4%A9%E7%9C%BC%E6%9F%A5%E8%BF%87%E6%9E%81%E9%AA%8C%E6%BB%91%E5%9D%97%E9%AA%8C%E8%AF%81%E7%A0%81/"/>
<id>http://example.com/JS%E9%80%86%E5%90%91%E6%A1%88%E4%BE%8B%E2%80%94%E2%80%94%E5%A4%A9%E7%9C%BC%E6%9F%A5%E8%BF%87%E6%9E%81%E9%AA%8C%E6%BB%91%E5%9D%97%E9%AA%8C%E8%AF%81%E7%A0%81/</id>
<published>2022-04-24T11:21:03.000Z</published>
<updated>2022-04-28T08:51:14.972Z</updated>
<content type="html"><![CDATA[<blockquote><p> 免责声明:<strong>本文章中所有内容仅供学习交流,抓包内容、敏感网址、数据接口均已做脱敏处理,严禁用于商业用途和非法用途,否则由此产生的一切后果均与作者无关,若有侵权,请联系我立即删除!</strong></p></blockquote><h3 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h3><p>本文是机器过极验滑块验证码系列文章的第四篇,通过一个实际网站介绍如何利用逆向分析自动过极验滑块,极验滑块系列包含乱序底图还原,验证码w参数生成,补环境,利用像素点RGB差值获取缺口位置以及通过机器学习获取缺口位置。而极验滑块系列只是验证码系列的第一个系列,后边会罗列市面上常用的验证码,然后发文一一解决。</p><p>上一篇文章见:<a href="https://blog.heshipeng.com/JS%E9%80%86%E5%90%91%E6%A1%88%E4%BE%8B%E2%80%94%E2%80%94%E6%9E%81%E9%AA%8C%E6%BB%91%E5%9D%97%E9%AA%8C%E8%AF%81%E7%A0%81%E8%A1%A5%E7%8E%AF%E5%A2%83/">JS逆向案例——极验滑块验证码补环境</a></p><h3 id="模拟登录天眼查"><a href="#模拟登录天眼查" class="headerlink" title="模拟登录天眼查"></a>模拟登录天眼查</h3><h4 id="极验滑块验证码与登录接口的关系"><a href="#极验滑块验证码与登录接口的关系" class="headerlink" title="极验滑块验证码与登录接口的关系"></a>极验滑块验证码与登录接口的关系</h4><p>网址:aHR0cHM6Ly93d3cudGlhbnlhbmNoYS5jb20v</p><p>以天眼查的登录为例,在进行滑块验证时,进行抓包分析。</p><p>在极验系列第一篇 <a href="https://blog.heshipeng.com/JS%E9%80%86%E5%90%91%E6%A1%88%E4%BE%8B%E2%80%94%E2%80%94%E6%9E%81%E9%AA%8C%E6%BB%91%E5%9D%97%E9%AA%8C%E8%AF%81%E7%A0%81%E5%BA%95%E5%9B%BE%E8%BF%98%E5%8E%9F/">JS逆向案例——极验滑块验证码底图还原</a> 中,说过geetest的几个请求之间的关系,这里重新梳理一下,然后顺便梳理下geetest请求与天眼查登录接口的关系。如下图:</p><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="https://img.heshipeng.com/202204242240627.png?watermark/2/text/5YWz5rOo5b6u5L-h5YWs5LyX5Y-377ya6YCG5ZCR5LiA5q2l5q2l/font/5a6L5L2T/fontsize/300" alt="image-20220424224028967" title=""> </div> <div class="image-caption">image-20220424224028967</div> </figure><p>可以看到每一次请求都是环环相扣,一共发起5次请求,下一次请求的入参或多或少都用到上一次请求的返回。</p><h4 id="第一次请求"><a href="#第一次请求" class="headerlink" title="第一次请求"></a>第一次请求</h4><p>封装一个geetest_xhtml请求,用于获取challenge和gt,代码如下:</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="keyword">import</span> time</span><br><span class="line"><span class="keyword">import</span> math</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> requests</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">geetest_xhtml</span>():</span></span><br><span class="line"> cookies = {</span><br><span class="line"> <span class="comment"># cookie值</span></span><br><span class="line"> <span class="string">'acw_sc__v2'</span>: <span class="string">'acw_sc__v2的值'</span></span><br><span class="line"> }</span><br><span class="line"> </span><br><span class="line"> headers = {</span><br><span class="line"> <span class="comment"># header值</span></span><br><span class="line"> }</span><br><span class="line"> </span><br><span class="line"> json_data = {</span><br><span class="line"> <span class="string">'uuid'</span>: math.floor(time.time() * <span class="number">1000</span>),</span><br><span class="line"> }</span><br><span class="line"> </span><br><span class="line"> response = requests.post(<span class="string">'https://www.tianyancha.com/verify/geetest.xhtml'</span>, cookies=cookies, headers=headers, json=json_data)</span><br><span class="line"> <span class="keyword">return</span> response.text</span><br></pre></td></tr></table></figure><p>cookie值里面仅仅需要一个acw_sc__v2,而acw_sc__v2的值是有时效性的,所以在实际生产中肯定也要逆向去动态生成这个cookie值,但是本文重点放在极验滑动验证码上,所以cookie直接拷贝,并没有细究cookie是如何生成的,后边有空了可以回过头了看看这个cookie是如何生成的。</p><p>运行结果如下:</p><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="https://img.heshipeng.com/202204250016632.png?watermark/2/text/5YWz5rOo5b6u5L-h5YWs5LyX5Y-377ya6YCG5ZCR5LiA5q2l5q2l/font/5a6L5L2T/fontsize/300" alt="image-20220425001609069" title=""> </div> <div class="image-caption">image-20220425001609069</div> </figure><h4 id="第二次请求"><a href="#第二次请求" class="headerlink" title="第二次请求"></a>第二次请求</h4><p>封装一个get_type请求,入参是第一个请求返回的gt,用正则匹配get_type返回的path和type,这两个值将在下一个请求中用到,由于这里只是做一个简单的演示,所以并未考虑程序的健壮性,写的有点糙,略略略。</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="function"><span class="keyword">def</span> <span class="title">get_type</span>(<span class="params">gt</span>):</span></span><br><span class="line"> headers = {</span><br><span class="line"> <span class="comment"># header值</span></span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> params = {</span><br><span class="line"> <span class="string">'gt'</span>: gt,</span><br><span class="line"> <span class="string">'callback'</span>: <span class="string">f'geetest_<span class="subst">{math.floor(time.time() * <span class="number">1000</span>)}</span>'</span>,</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> response = requests.get(<span class="string">'https://api.geetest.com/gettype.php'</span>, params=params, headers=headers)</span><br><span class="line"> <span class="built_in">print</span>(response.text)</span><br><span class="line"> path = re.findall(<span class="string">'\"path\": \"(.*?)\"'</span>, response.text)[<span class="number">0</span>]</span><br><span class="line"> _<span class="built_in">type</span> = re.findall(<span class="string">'\"type\": \"(.*?)\"'</span>, response.text)[<span class="number">0</span>]</span><br><span class="line"> <span class="keyword">return</span> path, _<span class="built_in">type</span></span><br></pre></td></tr></table></figure><p>代码运行结果如下:</p><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="https://img.heshipeng.com/202204250030914.png?watermark/2/text/5YWz5rOo5b6u5L-h5YWs5LyX5Y-377ya6YCG5ZCR5LiA5q2l5q2l/font/5a6L5L2T/fontsize/300" alt="image-20220425003036734" title=""> </div> <div class="image-caption">image-20220425003036734</div> </figure><h4 id="第三次请求"><a href="#第三次请求" class="headerlink" title="第三次请求"></a>第三次请求</h4><p>封装一个get请求,如参是第一次请求返回的gt和challenge以及第二次请求返回的type和path,该请求返回新的challenge(实际上在之前的challenge后边拼接了2个字符串),c,s,bg,fullbg。</p><figure class="highlight js"><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">def get(gt, challenge, _type, path):</span><br><span class="line"> url = f<span class="string">"https://api.geetest.com/get.php?gt={gt}&challenge={challenge}&product=popup&offline=false&"</span> \</span><br><span class="line"> f<span class="string">"protocol=https://&type={_type}&path={path}&callback=geetest_{math.floor(time.time() * 1000)}"</span></span><br><span class="line"></span><br><span class="line"> headers = {</span><br><span class="line"> <span class="string">'authority'</span>: <span class="string">'api.geetest.com'</span>,</span><br><span class="line"> <span class="string">'accept'</span>: <span class="string">'*/*'</span>,</span><br><span class="line"> <span class="string">'accept-language'</span>: <span class="string">'zh-CN,zh;q=0.9'</span>,</span><br><span class="line"> <span class="string">'cache-control'</span>: <span class="string">'no-cache'</span>,</span><br><span class="line"> <span class="string">'pragma'</span>: <span class="string">'no-cache'</span>,</span><br><span class="line"> <span class="string">'referer'</span>: <span class="string">'https://www.tianyancha.com/'</span>,</span><br><span class="line"> <span class="string">'sec-ch-ua'</span>: <span class="string">'" Not A;Brand";v="99", "Chromium";v="100", "Google Chrome";v="100"'</span>,</span><br><span class="line"> <span class="string">'sec-ch-ua-mobile'</span>: <span class="string">'?0'</span>,</span><br><span class="line"> <span class="string">'sec-ch-ua-platform'</span>: <span class="string">'"macOS"'</span>,</span><br><span class="line"> <span class="string">'sec-fetch-dest'</span>: <span class="string">'script'</span>,</span><br><span class="line"> <span class="string">'sec-fetch-mode'</span>: <span class="string">'no-cors'</span>,</span><br><span class="line"> <span class="string">'sec-fetch-site'</span>: <span class="string">'cross-site'</span>,</span><br><span class="line"> <span class="string">'user-agent'</span>: <span class="string">'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.4896.88 Safari/537.36'</span>,</span><br><span class="line"> <span class="string">'Cookie'</span>: <span class="string">'GeeTestAjaxUser=feae0be0cdd2042a5e570e9d3245e949; GeeTestUser=52dc984495fafeb0d018864b96edccd9'</span></span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> response = requests.get(url, headers=headers)</span><br><span class="line"></span><br><span class="line"> <span class="keyword">return</span> response.text</span><br></pre></td></tr></table></figure><p>运行结果如下:</p><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="https://img.heshipeng.com/202204251119325.png?watermark/2/text/5YWz5rOo5b6u5L-h5YWs5LyX5Y-377ya6YCG5ZCR5LiA5q2l5q2l/font/5a6L5L2T/fontsize/300" alt="image-20220425111939144" title=""> </div> <div class="image-caption">image-20220425111939144</div> </figure><h4 id="第四次请求"><a href="#第四次请求" class="headerlink" title="第四次请求"></a>第四次请求</h4><p>第四次请求需要滑块的轨迹,轨迹数组需要通过滑块缺口的差值来生成,要想计算滑块缺口的差值,需要还原滑块底图。</p><h5 id="滑块底图还原"><a href="#滑块底图还原" class="headerlink" title="滑块底图还原"></a>滑块底图还原</h5><p>底图还原参考文章: <a href="https://blog.heshipeng.com/JS%E9%80%86%E5%90%91%E6%A1%88%E4%BE%8B%E2%80%94%E2%80%94%E6%9E%81%E9%AA%8C%E6%BB%91%E5%9D%97%E9%AA%8C%E8%AF%81%E7%A0%81%E5%BA%95%E5%9B%BE%E8%BF%98%E5%8E%9F/">JS逆向案例——极验滑块验证码底图还原</a></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><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="function"><span class="keyword">def</span> <span class="title">restore_pic</span>(<span class="params">pic_path, new_pic_path</span>):</span></span><br><span class="line"> unordered_pic = Image.<span class="built_in">open</span>(pic_path)</span><br><span class="line"> ordered_pic = unordered_pic.copy()</span><br><span class="line"></span><br><span class="line"> <span class="comment"># 裁剪并拼接</span></span><br><span class="line"> <span class="keyword">for</span> i, d <span class="keyword">in</span> <span class="built_in">enumerate</span>(div_offset):</span><br><span class="line"> im = unordered_pic.crop((math.fabs(d[<span class="string">'x'</span>]), math.fabs(d[<span class="string">'y'</span>]), math.fabs(d[<span class="string">'x'</span>]) + <span class="number">10</span>, math.fabs(d[<span class="string">'y'</span>]) + <span class="number">58</span>))</span><br><span class="line"> <span class="comment"># 上半区</span></span><br><span class="line"> <span class="keyword">if</span> d[<span class="string">'y'</span>] != <span class="number">0</span>:</span><br><span class="line"> ordered_pic.paste(im, (<span class="number">10</span> * (i % (<span class="built_in">len</span>(div_offset) // <span class="number">2</span>)), <span class="number">0</span>), <span class="literal">None</span>)</span><br><span class="line"> <span class="keyword">else</span>:</span><br><span class="line"> ordered_pic.paste(im, (<span class="number">10</span> * (i % (<span class="built_in">len</span>(div_offset) // <span class="number">2</span>)), <span class="number">58</span>), <span class="literal">None</span>)</span><br><span class="line"></span><br><span class="line"> ordered_pic.save(new_pic_path)</span><br></pre></td></tr></table></figure><h5 id="缺口计算"><a href="#缺口计算" class="headerlink" title="缺口计算"></a>缺口计算</h5><p>缺口计算的方式基本上分2种,一种是图片处理,计算图片的每个像素点位置的色差去判断缺口;一种是通过深度学习去识别缺口位置。会专门出一篇文章计算这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="function"><span class="keyword">def</span> <span class="title">diff_rgb</span>(<span class="params">rgb1, rgb2</span>):</span></span><br><span class="line"> <span class="keyword">return</span> math.fabs(rgb1[<span class="number">0</span>] - rgb2[<span class="number">0</span>]) + math.fabs(rgb1[<span class="number">1</span>] - rgb2[<span class="number">1</span>]) + math.fabs(rgb1[<span class="number">2</span>] - rgb2[<span class="number">2</span>]) > <span class="number">255</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">get_moving_dst</span>(<span class="params">complete_pic_path, incomplete_pic_path</span>):</span></span><br><span class="line"> complete_pic = Image.<span class="built_in">open</span>(complete_pic_path)</span><br><span class="line"> incomplete_pic = Image.<span class="built_in">open</span>(incomplete_pic_path)</span><br><span class="line"></span><br><span class="line"> w, h = complete_pic.size</span><br><span class="line"></span><br><span class="line"> <span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">0</span>, w):</span><br><span class="line"> <span class="keyword">for</span> j <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">0</span>, h):</span><br><span class="line"> complete_pic_pixel_rgb = complete_pic.getpixel((i, j))</span><br><span class="line"> incomplete_pic_pixel_rgb = incomplete_pic.getpixel((i, j))</span><br><span class="line"> <span class="keyword">if</span> diff_rgb(complete_pic_pixel_rgb, incomplete_pic_pixel_rgb):</span><br><span class="line"> <span class="keyword">return</span> i</span><br><span class="line"></span><br><span class="line"> <span class="keyword">return</span> <span class="number">0</span></span><br></pre></td></tr></table></figure><h5 id="生成轨迹数组"><a href="#生成轨迹数组" class="headerlink" title="生成轨迹数组"></a>生成轨迹数组</h5><p>在网上找了一些轨迹算法,效果似乎不太好。就想着搭建一个本地的轨迹库,然后在实际使用的时候根据滑动距离从本地库中去拿。</p><p>具体思路是:</p><ol><li>用Flask搭建一个轨迹收集服务</li></ol><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> json</span><br><span class="line"><span class="keyword">import</span> math</span><br><span class="line"><span class="keyword">import</span> pickle</span><br><span class="line"></span><br><span class="line"><span class="keyword">from</span> flask <span class="keyword">import</span> Flask, request</span><br><span class="line"><span class="keyword">from</span> flask_cors <span class="keyword">import</span> cross_origin</span><br><span class="line"></span><br><span class="line">app = Flask(__name__)</span><br><span class="line"></span><br><span class="line"><span class="meta">@app.post(<span class="params"><span class="string">"/track"</span></span>)</span></span><br><span class="line"><span class="meta">@cross_origin(<span class="params">supports_credentials=<span class="literal">True</span>, methods=<span class="string">"*"</span>, allow_headers=<span class="string">"*"</span></span>)</span></span><br><span class="line"><span class="meta">@cross_origin()</span></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">track</span>():</span></span><br><span class="line"> tracks = pickle.load(<span class="built_in">open</span>(<span class="string">"tracks.pkl"</span>, <span class="string">"rb"</span>))</span><br><span class="line"> d = json.loads(request.data.decode())</span><br><span class="line"> <span class="built_in">print</span>(d)</span><br><span class="line"></span><br><span class="line"> <span class="comment"># 下载乱序的缺口图和完整图</span></span><br><span class="line"> download_image(d[<span class="string">'bg'</span>], <span class="string">"bg.png"</span>)</span><br><span class="line"> download_image(d[<span class="string">'fullbg'</span>], <span class="string">"fullbg.png"</span>)</span><br><span class="line"> </span><br><span class="line"> <span class="comment"># 还原乱序的缺口图和完整图</span></span><br><span class="line"> restore_pic(<span class="string">"bg.png"</span>, <span class="string">"new_bg.png"</span>)</span><br><span class="line"> restore_pic(<span class="string">"fullbg.png"</span>, <span class="string">"new_fullbg.png"</span>)</span><br><span class="line"> </span><br><span class="line"> <span class="comment"># 获取缺口的位置</span></span><br><span class="line"> x = get_moving_dst(<span class="string">"new_bg.png"</span>, <span class="string">"new_fullbg.png"</span>)</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> tracks.get(x):</span><br><span class="line"> tracks[x].append({<span class="string">'track'</span>: d[<span class="string">'track'</span>], <span class="string">'g7z'</span>: d[<span class="string">'g7z'</span>]})</span><br><span class="line"> <span class="keyword">else</span>:</span><br><span class="line"> tracks[x] = [{<span class="string">'track'</span>: d[<span class="string">'track'</span>], <span class="string">'g7z'</span>: d[<span class="string">'g7z'</span>]}]</span><br><span class="line"> pickle.dump(tracks, <span class="built_in">open</span>(<span class="string">"tracks.pkl"</span>, <span class="string">"wb"</span>))</span><br><span class="line"></span><br><span class="line"> <span class="keyword">return</span> <span class="string">'ok'</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">download_image</span>(<span class="params">url, image_file</span>):</span></span><br><span class="line"> <span class="keyword">with</span> <span class="built_in">open</span>(image_file, <span class="string">"wb"</span>) <span class="keyword">as</span> f:</span><br><span class="line"> f.write(requests.get(url).content)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> __name__ == <span class="string">'__main__'</span>:</span><br><span class="line"> track_data = {}</span><br><span class="line"> pickle.dump(track_data, <span class="built_in">open</span>(<span class="string">"tracks.pkl"</span>, <span class="string">"wb"</span>))</span><br><span class="line"> app.run(host=<span class="string">'0.0.0.0'</span>, port=<span class="number">8088</span>)</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>先手动通过滑块验证100-200次,收集起来每一次g7z(g7z为实际滑动的距离)对应的轨迹方程。</p><ol><li>使用</li></ol><figure class="highlight js"><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">const</span> Http = <span class="keyword">new</span> XMLHttpRequest();</span><br><span class="line"><span class="keyword">const</span> url=<span class="string">'http://127.0.0.1:8088/track'</span>;</span><br><span class="line">Http.open(<span class="string">"POST"</span>, url);</span><br><span class="line">Http.onload = <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>{</span><br><span class="line"><span class="built_in">console</span>.log(<span class="string">"请求成功, track: "</span> + <span class="built_in">JSON</span>.stringify(X1z));</span><br><span class="line"><span class="comment">// 请求结束后,在此处写处理代码</span></span><br><span class="line">};</span><br><span class="line">Http.send(<span class="built_in">JSON</span>.stringify({</span><br><span class="line"><span class="attr">track</span>: X1z,</span><br><span class="line"> <span class="attr">bg</span>: <span class="regexp">/\"(.*?)\"/g</span>.exec(<span class="built_in">document</span>.getElementsByClassName(<span class="string">"gt_cut_bg_slice"</span>)[<span class="number">0</span>].style.backgroundImage)[<span class="number">1</span>],</span><br><span class="line"><span class="attr">fullbg</span>: <span class="regexp">/\"(.*?)\"/g</span>.exec(<span class="built_in">document</span>.getElementsByClassName(<span class="string">"gt_cut_fullbg_slice"</span>)[<span class="number">0</span>].style.backgroundImage)[<span class="number">1</span>],</span><br><span class="line"> g7z</span><br><span class="line">}));</span><br></pre></td></tr></table></figure><p>在返回加密轨迹数组的之前去调用Flask服务,这样拿到的轨迹数组就是最终的轨迹数组,从而可以收集正确的轨迹数组。如下图:</p><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="https://img.heshipeng.com/202204281558130.png?watermark/2/text/5YWz5rOo5b6u5L-h5YWs5LyX5Y-377ya6YCG5ZCR5LiA5q2l5q2l/font/5a6L5L2T/fontsize/300" alt="image-20220428155854981" title=""> </div> <div class="image-caption">image-20220428155854981</div> </figure><p>通过手动过滑块就可以把轨迹数组收集到tracks.pkl文件了。</p><h5 id="生成w参数"><a href="#生成w参数" class="headerlink" title="生成w参数"></a>生成w参数</h5><p>w参数逆向分析与其服务搭建参考:<a href="https://blog.heshipeng.com/JS%E9%80%86%E5%90%91%E6%A1%88%E4%BE%8B%E2%80%94%E2%80%94%E6%9E%81%E9%AA%8C%E6%BB%91%E5%9D%97%E9%AA%8C%E8%AF%81%E7%A0%81w%E5%8F%82%E6%95%B0%E7%94%9F%E6%88%90/">JS逆向案例——极验滑块验证码w参数生成</a> 和 <a href="https://blog.heshipeng.com/JS%E9%80%86%E5%90%91%E6%A1%88%E4%BE%8B%E2%80%94%E2%80%94%E6%9E%81%E9%AA%8C%E6%BB%91%E5%9D%97%E9%AA%8C%E8%AF%81%E7%A0%81%E8%A1%A5%E7%8E%AF%E5%A2%83/">JS逆向案例——极验滑块验证码补环境</a></p><p>鉴于我们早已经搭建好了w请求服务,这里直接调用接口就好了,代码如下:</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="function"><span class="keyword">def</span> <span class="title">get_w</span>(<span class="params">gt, challenge, tracks, c, s, v</span>):</span></span><br><span class="line"> url = <span class="string">"http://127.0.01:8081/geetest/w"</span></span><br><span class="line"> data = {</span><br><span class="line"> <span class="string">"tracks"</span>: json.dumps(tracks),</span><br><span class="line"> <span class="string">"c"</span>: json.dumps(c),</span><br><span class="line"> <span class="string">"s"</span>: s,</span><br><span class="line"> <span class="string">"challenge"</span>: challenge,</span><br><span class="line"> <span class="string">"gt"</span>: gt,</span><br><span class="line"> <span class="string">"v"</span>: v</span><br><span class="line"> }</span><br><span class="line"> response = requests.post(url, json=data)</span><br><span class="line"> <span class="keyword">return</span> response.json()</span><br></pre></td></tr></table></figure><p>运行结果如下:</p><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="https://img.heshipeng.com/202204251531087.png?watermark/2/text/5YWz5rOo5b6u5L-h5YWs5LyX5Y-377ya6YCG5ZCR5LiA5q2l5q2l/font/5a6L5L2T/fontsize/300" alt="image-20220425153143590" title=""> </div> <div class="image-caption">image-20220425153143590</div> </figure><h5 id="进行滑块的验证"><a href="#进行滑块的验证" class="headerlink" title="进行滑块的验证"></a>进行滑块的验证</h5><p>进行滑块验证的请求很简单:</p><figure class="highlight js"><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">def ajax(gt, challenge, w):</span><br><span class="line"> url = f<span class="string">'https://api.geetest.com/ajax.php?gt={gt}&challenge={challenge}&w={w}&callback=geetest_{t}'</span></span><br><span class="line"> headers = {</span><br><span class="line"> <span class="string">'authority'</span>: <span class="string">'api.geetest.com'</span>,</span><br><span class="line"> <span class="string">'accept'</span>: <span class="string">'*/*'</span>,</span><br><span class="line"> <span class="string">'accept-language'</span>: <span class="string">'zh-CN,zh;q=0.9'</span>,</span><br><span class="line"> <span class="string">'cache-control'</span>: <span class="string">'no-cache'</span>,</span><br><span class="line"> <span class="string">'pragma'</span>: <span class="string">'no-cache'</span>,</span><br><span class="line"> <span class="string">'referer'</span>: <span class="string">'https://www.tianyancha.com/'</span>,</span><br><span class="line"> <span class="string">'sec-ch-ua'</span>: <span class="string">'" Not A;Brand";v="99", "Chromium";v="100", "Google Chrome";v="100"'</span>,</span><br><span class="line"> <span class="string">'sec-ch-ua-mobile'</span>: <span class="string">'?0'</span>,</span><br><span class="line"> <span class="string">'sec-ch-ua-platform'</span>: <span class="string">'"macOS"'</span>,</span><br><span class="line"> <span class="string">'sec-fetch-dest'</span>: <span class="string">'script'</span>,</span><br><span class="line"> <span class="string">'sec-fetch-mode'</span>: <span class="string">'no-cors'</span>,</span><br><span class="line"> <span class="string">'sec-fetch-site'</span>: <span class="string">'cross-site'</span>,</span><br><span class="line"> <span class="string">'user-agent'</span>: <span class="string">'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.4896.88 Safari/537.36'</span>,</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> response = requests.get(url, headers=headers)</span><br><span class="line"> <span class="keyword">return</span> response.text</span><br></pre></td></tr></table></figure><p>就一个get请求,带上gt,challenge和w参数就行。</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><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></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">main</span>(<span class="params">tracks</span>):</span></span><br><span class="line"> <span class="comment"># 获取gt, challenge</span></span><br><span class="line"> d = geetest_xhtml()</span><br><span class="line"> gt = d[<span class="string">'data'</span>][<span class="string">'gt'</span>]</span><br><span class="line"> challenge = d[<span class="string">'data'</span>][<span class="string">'challenge'</span>]</span><br><span class="line"> </span><br><span class="line"> <span class="comment"># 获取path, type</span></span><br><span class="line"> path, _<span class="built_in">type</span> = get_type(gt)</span><br><span class="line"> m = get(gt, challenge, _<span class="built_in">type</span>, path)</span><br><span class="line"> </span><br><span class="line"> <span class="comment"># 下载乱序的缺口图和背景图</span></span><br><span class="line"> url = <span class="string">"https://static.geetest.com/"</span></span><br><span class="line"> unordered_complete_pic = urljoin(url, m[<span class="string">'fullbg'</span>])</span><br><span class="line"> unordered_incomplete_pic = urljoin(url, m[<span class="string">'bg'</span>])</span><br><span class="line"> <span class="keyword">with</span> <span class="built_in">open</span>(<span class="string">"image1.png"</span>, <span class="string">"wb"</span>) <span class="keyword">as</span> f:</span><br><span class="line"> f.write(requests.get(unordered_complete_pic).content)</span><br><span class="line"> <span class="keyword">with</span> <span class="built_in">open</span>(<span class="string">"image2.png"</span>, <span class="string">"wb"</span>) <span class="keyword">as</span> f:</span><br><span class="line"> f.write(requests.get(unordered_incomplete_pic).content)</span><br><span class="line"></span><br><span class="line"> <span class="comment"># 还原乱序图 </span></span><br><span class="line"> restore_pic(<span class="string">"image1.png"</span>, <span class="string">"new_image1.png"</span>)</span><br><span class="line"> restore_pic(<span class="string">"image2.png"</span>, <span class="string">"new_image2.png"</span>)</span><br><span class="line"></span><br><span class="line"> <span class="comment"># 获取缺口位置</span></span><br><span class="line"> dist = get_moving_dst(<span class="string">"new_image1.png"</span>, <span class="string">"new_image2.png"</span>)</span><br><span class="line"> </span><br><span class="line"> <span class="comment"># 从本地的tracks.pkl中读取轨迹数组</span></span><br><span class="line"> <span class="keyword">if</span> tracks.get(dist):</span><br><span class="line"> track = tracks[dist][<span class="number">0</span>][<span class="string">'track'</span>]</span><br><span class="line"> g7z = tracks[dist][<span class="number">0</span>][<span class="string">'g7z'</span>]</span><br><span class="line"><span class="keyword">else</span>:</span><br><span class="line"> <span class="comment"># 如果未拿到轨迹数组,直接返回</span></span><br><span class="line"> <span class="keyword">return</span></span><br><span class="line"></span><br><span class="line"> <span class="comment"># 调用Express服务获取w参数</span></span><br><span class="line"> w = get_w(m[<span class="string">'gt'</span>], m[<span class="string">'challenge'</span>], track, m[<span class="string">'c'</span>], m[<span class="string">'s'</span>], m[<span class="string">'version'</span>], g7z)</span><br><span class="line"></span><br><span class="line"> <span class="comment"># 进行验证</span></span><br><span class="line"> validator = ajax(w[<span class="string">'gt'</span>], w[<span class="string">'challenge'</span>], w[<span class="string">'w'</span>])</span><br><span class="line"> <span class="built_in">print</span>(validator)</span><br></pre></td></tr></table></figure><p>运行结果:</p><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="https://img.heshipeng.com/202204281609390.png?watermark/2/text/5YWz5rOo5b6u5L-h5YWs5LyX5Y-377ya6YCG5ZCR5LiA5q2l5q2l/font/5a6L5L2T/fontsize/300" alt="image-20220428160923233" title=""> </div> <div class="image-caption">image-20220428160923233</div> </figure><h5 id="登录天眼查"><a href="#登录天眼查" class="headerlink" title="登录天眼查"></a>登录天眼查</h5><p>别忘了我们这篇文章的最终目的是模拟登录天眼查,拿到滑块验证成功返回的validate之后,要请求天眼查的登录接口,先看下请求接口的代码如下:</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></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">login</span>(<span class="params">mobile, password, challenge, validator</span>):</span></span><br><span class="line"> cookies = {</span><br><span class="line"> <span class="string">'acw_sc__v2'</span>: <span class="string">'acw_sc__v2的值'</span>,</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> headers = {</span><br><span class="line"> <span class="comment"># header值</span></span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> json_data = {</span><br><span class="line"> <span class="string">'mobile'</span>: mobile,</span><br><span class="line"> <span class="string">'cdpassword'</span>: password,</span><br><span class="line"> <span class="string">'loginway'</span>: <span class="string">'PL'</span>,</span><br><span class="line"> <span class="string">'autoLogin'</span>: <span class="literal">False</span>,</span><br><span class="line"> <span class="string">'type'</span>: <span class="string">''</span>,</span><br><span class="line"> <span class="string">'challenge'</span>: challenge,</span><br><span class="line"> <span class="string">'validate'</span>: validator,</span><br><span class="line"> <span class="string">'seccode'</span>: <span class="string">f'<span class="subst">{validator}</span>|jordan'</span>,</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> response = requests.post(<span class="string">'https://www.tianyancha.com/cd/login.json'</span>, cookies=cookies, headers=headers,</span><br><span class="line"> json=json_data)</span><br><span class="line"> <span class="keyword">return</span> response.text</span><br></pre></td></tr></table></figure><p>就是一个post请求,没什么说的。</p><p>接着在主程序main方法的末尾加入调用这个login的代码:</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="keyword">if</span> validator:</span><br><span class="line"> validate = re.findall(<span class="string">"\"validate\": \"(.*?)\""</span>, validator)[<span class="number">0</span>]</span><br><span class="line"> login_res = login(<span class="string">'17777777777'</span>, <span class="string">'202cb962ac59075b964b07152d234b70'</span>, m[<span class="string">'challenge'</span>], validate)</span><br><span class="line"> <span class="keyword">return</span> login_res</span><br></pre></td></tr></table></figure><p>还是前面提到的,我们的主要目的放在极验验证上,所以这里cookie的acw_sc__v2值跟前面一样,复制下来就可,只不过这个cookie和第一个请求的cookie保持一致就行。另外,password也是一个加密的字符串,32位很符合md5码的特征,但是管它是啥呢?我们复制过来用就行。还有一点要注意的是,challenge要传get请求返回过来的,不要传第一个请求返回的。</p><p>程序运行的结果如下:</p><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="https://img.heshipeng.com/202204281633498.png?watermark/2/text/5YWz5rOo5b6u5L-h5YWs5LyX5Y-377ya6YCG5ZCR5LiA5q2l5q2l/font/5a6L5L2T/fontsize/300" alt="image-20220428163341351" title=""> </div> <div class="image-caption">image-20220428163341351</div> </figure><p>提示密码错误,很显然,登录接口成功了。</p><h3 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h3><p>极验滑块的总算是成功了,暂时告一段落。我们是针对6.0.9这个版本的极验js文件进行的逆向,小版本的极验逻辑都差不多。大版本的可能区别会大一些,不过思路也差不多。极验的最新版本好像是4,后边会看看4这个大版本有什么不同。然后除了极验的滑块,还会试着看看能否搞定极验的点选与推理验证。</p>]]></content>
<summary type="html"><blockquote>
<p> 免责声明:<strong>本文章中所有内容仅供学习交流,抓包内容、敏感网址、数据接口均已做脱敏处理,严禁用于商业用途和非法用途,否则由此产生的一切后果均与作者无关,若有侵权,请联系我立即删除!</strong></p>
</blockquote></summary>
<category term="JS逆向" scheme="http://example.com/categories/JS%E9%80%86%E5%90%91/"/>
<category term="JS" scheme="http://example.com/tags/JS/"/>
<category term="逆向" scheme="http://example.com/tags/%E9%80%86%E5%90%91/"/>
<category term="验证码" scheme="http://example.com/tags/%E9%AA%8C%E8%AF%81%E7%A0%81/"/>
</entry>
<entry>
<title>JS逆向案例——极验滑块验证码补环境</title>
<link href="http://example.com/JS%E9%80%86%E5%90%91%E6%A1%88%E4%BE%8B%E2%80%94%E2%80%94%E6%9E%81%E9%AA%8C%E6%BB%91%E5%9D%97%E9%AA%8C%E8%AF%81%E7%A0%81%E8%A1%A5%E7%8E%AF%E5%A2%83/"/>
<id>http://example.com/JS%E9%80%86%E5%90%91%E6%A1%88%E4%BE%8B%E2%80%94%E2%80%94%E6%9E%81%E9%AA%8C%E6%BB%91%E5%9D%97%E9%AA%8C%E8%AF%81%E7%A0%81%E8%A1%A5%E7%8E%AF%E5%A2%83/</id>
<published>2022-04-24T09:44:38.000Z</published>
<updated>2022-04-28T09:05:31.436Z</updated>
<content type="html"><![CDATA[<blockquote><p>免责声明:<strong>本文章中所有内容仅供学习交流,抓包内容、敏感网址、数据接口均已做脱敏处理,严禁用于商业用途和非法用途,否则由此产生的一切后果均与作者无关,若有侵权,请联系我立即删除!</strong></p></blockquote><h4 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h4><p>本文是机器过极验滑块验证码系列文章的第三篇,接上篇w参数抠出来之后,补环境。后边还会陆陆续续发文,如何包括利用像素点RGB差值获取缺口位置以及通过机器学习获取缺口位置,最后会通过几个采用极验验证码的网站去完整的展示整个自动化过程。而极验滑块系列只是验证码系列的第一个系列,后边会罗列市面上常用的验证码,然后发文一一解决。</p><p>上一篇文章见:<a href="http://blog.heshipeng.com/JS%E9%80%86%E5%90%91%E6%A1%88%E4%BE%8B%E2%80%94%E2%80%94%E6%9E%81%E9%AA%8C%E6%BB%91%E5%9D%97w%E5%8F%82%E6%95%B0%E7%94%9F%E6%88%90/">JS逆向案例——极验滑块w参数生成</a></p><h4 id="补环境过程"><a href="#补环境过程" class="headerlink" title="补环境过程"></a>补环境过程</h4><p>抠好w参数生成的代码之后,贴到VS中,先补上window和document:</p><figure class="highlight js"><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">var</span> <span class="built_in">window</span> = <span class="built_in">global</span>;</span><br><span class="line"><span class="keyword">var</span> Document = <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>{};</span><br><span class="line"><span class="built_in">document</span> = <span class="keyword">new</span> Document();</span><br><span class="line"><span class="built_in">window</span>.document = <span class="built_in">document</span>;</span><br></pre></td></tr></table></figure><p>运行之后报错,报navigator不存在</p><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="https://img.heshipeng.com/202204241808114.png?watermark/2/text/5YWz5rOo5b6u5L-h5YWs5LyX5Y-377ya6YCG5ZCR5LiA5q2l5q2l/font/5a6L5L2T/fontsize/300" alt="image-20220424180831738" title=""> </div> <div class="image-caption">image-20220424180831738</div> </figure><p>补上navigator:</p><figure class="highlight js"><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">Navigator = <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>{};</span><br><span class="line">navigator = <span class="keyword">new</span> Navigator();</span><br><span class="line"></span><br><span class="line"><span class="built_in">window</span>.navigator = navigator;</span><br></pre></td></tr></table></figure><p>补好之后接着报错,document缺少方法createElement,可以看到createElement这个方法接受一个参数,并且返回为一个object。</p><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="https://img.heshipeng.com/202204241817808.png?watermark/2/text/5YWz5rOo5b6u5L-h5YWs5LyX5Y-377ya6YCG5ZCR5LiA5q2l5q2l/font/5a6L5L2T/fontsize/300" alt="image-20220424181756529" title=""> </div> <div class="image-caption">image-20220424181756529</div> </figure><p>我们给document补上createElement方法:</p><figure class="highlight js"><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">Document.prototype.createElement = <span class="function"><span class="keyword">function</span>(<span class="params">a</span>) </span>{</span><br><span class="line"> <span class="keyword">if</span> (a == <span class="string">'img'</span>) <span class="keyword">return</span> {};</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>运行下,竟然成功了。。。没想到补环境如此轻松。</p><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="https://img.heshipeng.com/202204241830790.png?watermark/2/text/5YWz5rOo5b6u5L-h5YWs5LyX5Y-377ya6YCG5ZCR5LiA5q2l5q2l/font/5a6L5L2T/fontsize/300" alt="image-20220424183013485" title=""> </div> <div class="image-caption">image-20220424183013485</div> </figure><h4 id="利用Express封装成一个服务"><a href="#利用Express封装成一个服务" class="headerlink" title="利用Express封装成一个服务"></a>利用Express封装成一个服务</h4><p>将gt,challenge,c,s,版本v以及轨迹数组X1z作为参数传入,返回加密后的w参数,代码如下:</p><figure class="highlight js"><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> crypto = <span class="built_in">require</span>(<span class="string">"crypto"</span>);</span><br><span class="line"><span class="keyword">var</span> md5 = crypto.createHash(<span class="string">"md5"</span>);</span><br><span class="line"><span class="keyword">var</span> express = <span class="built_in">require</span>(<span class="string">'express'</span>);</span><br><span class="line"><span class="keyword">var</span> app = express();</span><br><span class="line"></span><br><span class="line">app.use(express.json());</span><br><span class="line">app.use(express.urlencoded({ <span class="attr">extended</span>: <span class="literal">true</span> }));</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> <span class="built_in">window</span> = <span class="built_in">global</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> Document = <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>{};</span><br><span class="line">Document.prototype.createElement = <span class="function"><span class="keyword">function</span>(<span class="params">a</span>) </span>{</span><br><span class="line"> <span class="keyword">if</span> (a == <span class="string">'img'</span>) <span class="keyword">return</span> {};</span><br><span class="line">}</span><br><span class="line"><span class="built_in">document</span> = <span class="keyword">new</span> Document();</span><br><span class="line"></span><br><span class="line">Navigator = <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>{};</span><br><span class="line">navigator = <span class="keyword">new</span> Navigator();</span><br><span class="line"></span><br><span class="line"><span class="built_in">window</span>.document = <span class="built_in">document</span>;</span><br><span class="line"><span class="built_in">window</span>.navigator = navigator;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> G0b = <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>{}</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">H1W</span>(<span class="params"></span>) </span>{</span><br><span class="line"> <span class="keyword">return</span> (<span class="number">65536</span> * (<span class="number">1</span> + <span class="built_in">Math</span>.random()) | <span class="number">0</span>).toString(<span class="number">16</span>).substring(<span class="number">1</span>);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> wb = H1W() + H1W() + H1W() + H1W();</span><br><span class="line"></span><br><span class="line">G0b.prototype.wb = <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>{</span><br><span class="line"> <span class="keyword">return</span> wb;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> _v0B;</span><br><span class="line"><span class="keyword">var</span> _n0B;</span><br><span class="line"><span class="keyword">var</span> _e7B;</span><br><span class="line"><span class="keyword">var</span> _i7B;</span><br><span class="line"><span class="keyword">var</span> _p7B;</span><br><span class="line"><span class="keyword">var</span> _I0B;</span><br><span class="line"></span><br><span class="line"><span class="comment">/*</span></span><br><span class="line"><span class="comment">中间抠的JS代码省略</span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">get_H7z</span>(<span class="params"></span>) </span>{</span><br><span class="line"> <span class="keyword">let</span> g0b = <span class="keyword">new</span> G0b();</span><br><span class="line"> <span class="keyword">let</span> aaa = <span class="keyword">new</span> _v0B();</span><br><span class="line"> <span class="keyword">return</span> aaa.encrypt(g0b.wb());</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">get_q7z</span>(<span class="params">X1z, c, s, gt, challenge, v</span>) </span>{</span><br><span class="line"> <span class="keyword">let</span> g0b = <span class="keyword">new</span> G0b();</span><br><span class="line"> <span class="keyword">let</span> passtime = <span class="number">0</span>;</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">let</span> index = <span class="number">0</span>; index < X1z.length; index++) {</span><br><span class="line"> passtime += X1z[index][<span class="number">2</span>];</span><br><span class="line"> }</span><br><span class="line"> <span class="built_in">console</span>.log(_e7B.u(_e7B.t(<span class="keyword">new</span> <span class="built_in">Date</span>().getTime(), X1z), c, s));</span><br><span class="line"> <span class="keyword">let</span> Y7z = {</span><br><span class="line"> <span class="string">"aa"</span>: _e7B.u(_e7B.t(<span class="keyword">new</span> <span class="built_in">Date</span>().getTime(), X1z), c, s),</span><br><span class="line"> <span class="string">"userresponse"</span>: _i7B.C(<span class="built_in">Math</span>.floor(<span class="built_in">Math</span>.random() * <span class="number">200</span>), challenge),</span><br><span class="line"> <span class="string">"passtime"</span>: passtime,</span><br><span class="line"> <span class="string">"imgload"</span>: <span class="built_in">Math</span>.floor(<span class="built_in">Math</span>.random() * <span class="number">200</span>),</span><br><span class="line"> <span class="string">"ep"</span>: {<span class="string">"v"</span>: v}, </span><br><span class="line"> <span class="string">"rp"</span>: _I0B(gt + challenge.slice(<span class="number">0</span>, <span class="number">32</span>) + passtime) </span><br><span class="line"> };</span><br><span class="line"> <span class="keyword">return</span> _n0B.encrypt(<span class="built_in">JSON</span>.stringify(Y7z), g0b.wb());</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">get_r7z</span>(<span class="params">q7z</span>) </span>{</span><br><span class="line"> <span class="keyword">return</span> _p7B.Ha(q7z);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">app.post(<span class="string">'/geetest/w'</span>, <span class="function"><span class="keyword">function</span> (<span class="params">req, res</span>) </span>{</span><br><span class="line"> <span class="built_in">console</span>.log(req.body.tracks);</span><br><span class="line"> <span class="keyword">let</span> X1z = <span class="built_in">JSON</span>.parse(req.body.tracks);</span><br><span class="line"> <span class="keyword">let</span> c = <span class="built_in">JSON</span>.parse(req.body.c);</span><br><span class="line"> <span class="keyword">let</span> s = req.body.s;</span><br><span class="line"> <span class="keyword">let</span> challenge = req.body.challenge;</span><br><span class="line"> <span class="keyword">let</span> gt = req.body.gt;</span><br><span class="line"> <span class="keyword">let</span> v = req.body.v;</span><br><span class="line"> </span><br><span class="line"> <span class="keyword">let</span> h7z = get_H7z();</span><br><span class="line"> <span class="keyword">let</span> q7z = get_q7z(X1z, c, s, gt, challenge, v);</span><br><span class="line"></span><br><span class="line"> <span class="keyword">let</span> r7z = get_r7z(q7z);</span><br><span class="line"></span><br><span class="line"> <span class="keyword">let</span> w = r7z + h7z;</span><br><span class="line"> res.send(<span class="built_in">JSON</span>.stringify({</span><br><span class="line"> w, gt, challenge</span><br><span class="line"> }));</span><br><span class="line">});</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> server = app.listen(<span class="number">8081</span>, <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>{</span><br><span class="line"> <span class="keyword">var</span> port = server.address().port</span><br><span class="line"> <span class="built_in">console</span>.log(<span class="string">"Server started, address: http://localhost:%s"</span>, port)</span><br><span class="line">});</span><br></pre></td></tr></table></figure><p>运行结果如下:</p><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="https://img.heshipeng.com/202204241919217.png?watermark/2/text/5YWz5rOo5b6u5L-h5YWs5LyX5Y-377ya6YCG5ZCR5LiA5q2l5q2l/font/5a6L5L2T/fontsize/300" alt="image-20220424191932778" title=""> </div> <div class="image-caption">image-20220424191932778</div> </figure>]]></content>
<summary type="html"><blockquote>
<p>免责声明:<strong>本文章中所有内容仅供学习交流,抓包内容、敏感网址、数据接口均已做脱敏处理,严禁用于商业用途和非法用途,否则由此产生的一切后果均与作者无关,若有侵权,请联系我立即删除!</strong></p>
</blockquote>
</summary>
<category term="JS逆向" scheme="http://example.com/categories/JS%E9%80%86%E5%90%91/"/>
<category term="JS" scheme="http://example.com/tags/JS/"/>
<category term="逆向" scheme="http://example.com/tags/%E9%80%86%E5%90%91/"/>
<category term="验证码" scheme="http://example.com/tags/%E9%AA%8C%E8%AF%81%E7%A0%81/"/>
</entry>
<entry>
<title>JS逆向案例——极验滑块验证码w参数生成</title>
<link href="http://example.com/JS%E9%80%86%E5%90%91%E6%A1%88%E4%BE%8B%E2%80%94%E2%80%94%E6%9E%81%E9%AA%8C%E6%BB%91%E5%9D%97%E9%AA%8C%E8%AF%81%E7%A0%81w%E5%8F%82%E6%95%B0%E7%94%9F%E6%88%90/"/>
<id>http://example.com/JS%E9%80%86%E5%90%91%E6%A1%88%E4%BE%8B%E2%80%94%E2%80%94%E6%9E%81%E9%AA%8C%E6%BB%91%E5%9D%97%E9%AA%8C%E8%AF%81%E7%A0%81w%E5%8F%82%E6%95%B0%E7%94%9F%E6%88%90/</id>
<published>2022-04-21T07:13:39.000Z</published>
<updated>2022-04-28T07:00:51.810Z</updated>
<content type="html"><![CDATA[<blockquote><p> 免责声明:<strong>本文章中所有内容仅供学习交流,抓包内容、敏感网址、数据接口均已做脱敏处理,严禁用于商业用途和非法用途,否则由此产生的一切后果均与作者无关,若有侵权,请联系我立即删除!</strong></p></blockquote><h4 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h4><p>本文是机器过极验滑块验证码系列文章的第二篇,提交验证请求的w参数逆向分析。后边还会陆陆续续发文,讲解如何补环境,如何利用像素点RGB差值获取缺口位置以及通过机器学习获取缺口位置,最后会通过几个采用极验验证码的网站去完整的展示整个自动化过程。而极验滑块系列只是验证码系列的第一个系列,后边会罗列市面上常用的验证码,然后发文一一解决。</p><p>上一篇文章见:<a href="https://blog.heshipeng.com/JS%E9%80%86%E5%90%91%E6%A1%88%E4%BE%8B%E2%80%94%E2%80%94%E6%9E%81%E9%AA%8C%E6%BB%91%E5%9D%97%E9%AA%8C%E8%AF%81%E7%A0%81%E5%BA%95%E5%9B%BE%E8%BF%98%E5%8E%9F/">JS逆向案例——极验滑块验证码底图还原</a></p><h4 id="逆向分析过程"><a href="#逆向分析过程" class="headerlink" title="逆向分析过程"></a>逆向分析过程</h4><p>网址:aHR0cHM6Ly93d3cudGlhbnlhbmNoYS5jb20v</p><p>以天眼查的登录为例,提交滑块验证请求时,w参数跟值。</p><h5 id="抓包分析"><a href="#抓包分析" class="headerlink" title="抓包分析"></a>抓包分析</h5><p>在底图还原的那篇文章中就提到过一共有四个重要的包,其中前面三个包都没有用到w参数,只有在向服务器提交验证码验证的请求时才需要w参数,如下:</p><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="https://img.heshipeng.com/202204212301379.png?watermark/2/text/5YWz5rOo5b6u5L-h5YWs5LyX5Y-377ya6YCG5ZCR5LiA5q2l5q2l/font/5a6L5L2T/fontsize/300" alt="image-20220421230146225" title=""> </div> <div class="image-caption">image-20220421230146225</div> </figure><p>我们点进去ajax.php这个请求的方法调用栈:</p><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="https://img.heshipeng.com/202204212303683.png?watermark/2/text/5YWz5rOo5b6u5L-h5YWs5LyX5Y-377ya6YCG5ZCR5LiA5q2l5q2l/font/5a6L5L2T/fontsize/300" alt="image-20220421230321601" title=""> </div> <div class="image-caption">image-20220421230321601</div> </figure><p>我们可以看到gt, challenge以及w参数生成位置,如下图:</p><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="https://img.heshipeng.com/202204212305223.png?watermark/2/text/5YWz5rOo5b6u5L-h5YWs5LyX5Y-377ya6YCG5ZCR5LiA5q2l5q2l/font/5a6L5L2T/fontsize/300" alt="image-20220421230550163" title=""> </div> <div class="image-caption">image-20220421230550163</div> </figure><h5 id="确定逆向目标"><a href="#确定逆向目标" class="headerlink" title="确定逆向目标"></a>确定逆向目标</h5><p>通过上边的抓包分析,可以确定的是w参数有2个变量r7z和H7z拼接而成,然后r7z是调用一个函数生成,这个函数接受一个参数q7z;H7z也是调用一个函数生成。这样逆向的目标也就确定了,就是抠出这2个函数以及参数q7z。</p><h5 id="H7z生成规则"><a href="#H7z生成规则" class="headerlink" title="H7z生成规则"></a>H7z生成规则</h5><p>H7z是由一个无参的函数V7z[M9r.C8z(92)]生成,所以先看这个函数。可以看到每次调用的结果都不同。</p><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="https://img.heshipeng.com/202204212317007.png?watermark/2/text/5YWz5rOo5b6u5L-h5YWs5LyX5Y-377ya6YCG5ZCR5LiA5q2l5q2l/font/5a6L5L2T/fontsize/300" alt="image-20220421231717931" title=""> </div> <div class="image-caption">image-20220421231717931</div> </figure><p>可以看到这个函数采用了流程平坦化,<strong>对于流程平坦化,在调试时看下return语句返回的值,如果有多个return语句,则每个return语句都打上断点,如果只有一个return语句,看下return的那个变量,每个给这个变量赋值的语句都要打上断点</strong>,显然这里函数返回的是Y0B,则给这个变量赋值的几个地方都断上:</p><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="https://img.heshipeng.com/202204212319591.png?watermark/2/text/5YWz5rOo5b6u5L-h5YWs5LyX5Y-377ya6YCG5ZCR5LiA5q2l5q2l/font/5a6L5L2T/fontsize/300" alt="image-20220421231912531" title=""> </div> <div class="image-caption">image-20220421231912531</div> </figure><p>我们断进来发现,只走了M9r.k9r()[18][36][24]这个分支,实例化一个v0B对象,然后调用这个对象的某一个方法,返回一串加密后的字符串。</p><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="https://img.heshipeng.com/202204212324263.png?watermark/2/text/5YWz5rOo5b6u5L-h5YWs5LyX5Y-377ya6YCG5ZCR5LiA5q2l5q2l/font/5a6L5L2T/fontsize/300" alt="image-20220421232420203" title=""> </div> <div class="image-caption">image-20220421232420203</div> </figure><p>利用console把<code>Y0B = new v0B()[M9r.C8z(699)](g0B[M9r.C8z(818)](D0B))</code>这行代码简化为<code>Y0B = new v0B().encrypt(g0B.wb())</code>,最后D0B为undefine所以省略。</p><p>所以这里要得到Y0B就先得抠出g0b的wb方法,然后抠出v0B的encrypt方法,我们一步步来吧。</p><ol><li>g0b对象的wb方法</li></ol><p>我们点进去wb方法,可以看到虽然是流程平坦化,但是只有一个case,最后返回的是一个逗号表达式,我们只看最后一个,也就是函数真正返回的是J0B变量,而J0B变量是调用C7B函数生成的。如下图:</p><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="https://img.heshipeng.com/202204212338521.png?watermark/2/text/5YWz5rOo5b6u5L-h5YWs5LyX5Y-377ya6YCG5ZCR5LiA5q2l5q2l/font/5a6L5L2T/fontsize/300" alt="image-20220421233831380" title=""> </div> <div class="image-caption">image-20220421233831380</div> </figure><p>没办法,我们只有接着追进去调试C7B函数,可以看到花里胡哨的其实只是调用了四次H1W函数,然后拼接成一个字符串返回。如图:</p><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="https://img.heshipeng.com/202204212341304.png?watermark/2/text/5YWz5rOo5b6u5L-h5YWs5LyX5Y-377ya6YCG5ZCR5LiA5q2l5q2l/font/5a6L5L2T/fontsize/300" alt="image-20220421234115235" title=""> </div> <div class="image-caption">image-20220421234115235</div> </figure><p>我们再扒一扒H1W函数的代码看下,将返回的那条语句反混淆之后结果为<code>(65536 * (1 + Math.random()) | 0).toString(16).substring(1)</code> 。</p><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="https://img.heshipeng.com/202204212348080.png?watermark/2/text/5YWz5rOo5b6u5L-h5YWs5LyX5Y-377ya6YCG5ZCR5LiA5q2l5q2l/font/5a6L5L2T/fontsize/300" alt="image-20220421234837015" title=""> </div> <div class="image-caption">image-20220421234837015</div> </figure><p>到这里都是调用内置的函数了,而且算法比较简单,所以不需要进一步往下挖了。整理一下:</p><figure class="highlight js"><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">var</span> G0b = <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>{}</span><br><span class="line">G0b.prototype.wb = <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>{</span><br><span class="line"> <span class="keyword">return</span> H1W() + H1W() + H1W() + H1W();</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">H1W</span>(<span class="params"></span>) </span>{</span><br><span class="line"> <span class="keyword">return</span> (<span class="number">65536</span> * (<span class="number">1</span> + <span class="built_in">Math</span>.random()) | <span class="number">0</span>).toString(<span class="number">16</span>).substring(<span class="number">1</span>);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">g0b = <span class="keyword">new</span> G0b();</span><br></pre></td></tr></table></figure><ol><li>v0B对象的encrypt方法</li></ol><p>接着看下v0B对象的encrypt方法。点进去看这个方法体,发现这个方法还依赖了很多其它的方法,而且依赖的这些方法都不是内置方法,如果单抠这样的一个个方法就很麻烦了,更不用想去一个个理清楚这些方法的逻辑,然后用Python去自己实现了。遇到这样的情况一般比较简单的方法是全抠整个JS,然后想办法导出所需要的方法即可。</p><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="https://img.heshipeng.com/202204220021962.png?watermark/2/text/5YWz5rOo5b6u5L-h5YWs5LyX5Y-377ya6YCG5ZCR5LiA5q2l5q2l/font/5a6L5L2T/fontsize/300" alt="image-20220422002133796" title=""> </div> <div class="image-caption">image-20220422002133796</div> </figure><p>想要导出V9B方法,就需要导出v0B对象,因为V9B方法属于v0B对象,而如何导出v0B对象呢?就要看这个对象的上一层是什么。如果遇到代码行数很多,代码层次比较多的时候推荐使用Notepad++方便去管理这种层次结构。下面介绍这个小技巧:</p><p>使用Notepad++查看JS代码层次结构小技巧:先拷贝整个文件至Notepad++,然后选择试图->折叠所有层次。</p><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="https://img.heshipeng.com/202204220034063.png?watermark/2/text/5YWz5rOo5b6u5L-h5YWs5LyX5Y-377ya6YCG5ZCR5LiA5q2l5q2l/font/5a6L5L2T/fontsize/300" alt="image-20220422003418919" title=""> </div> <div class="image-caption">image-20220422003418919</div> </figure><p>接着CTRL+F搜索我们要的代码,比如这里是v0B = function()</p><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="https://img.heshipeng.com/202204220036294.png?watermark/2/text/5YWz5rOo5b6u5L-h5YWs5LyX5Y-377ya6YCG5ZCR5LiA5q2l5q2l/font/5a6L5L2T/fontsize/300" alt="image-20220422003646204" title=""> </div> <div class="image-caption">image-20220422003646204</div> </figure><p>可以看到搜索结果只展开了包含我们搜索代码的那些分支,其余的依旧没有展开,这样非常方便我们观察代码的层次结构。</p><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="https://img.heshipeng.com/202204220037208.png?watermark/2/text/5YWz5rOo5b6u5L-h5YWs5LyX5Y-377ya6YCG5ZCR5LiA5q2l5q2l/font/5a6L5L2T/fontsize/300" alt="image-20220422003746128" title=""> </div> <div class="image-caption">image-20220422003746128</div> </figure><p>通过上图可以看到,只要加载这整个JS文件,就会执行流程平坦化中的代码,也就会得到v0B对象就会被定义,我们只需要全局定义一个变量接收v0B即可。如下图:</p><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="https://img.heshipeng.com/202204220043297.png?watermark/2/text/5YWz5rOo5b6u5L-h5YWs5LyX5Y-377ya6YCG5ZCR5LiA5q2l5q2l/font/5a6L5L2T/fontsize/300" alt="image-20220422004359174" title=""> </div> <div class="image-caption">image-20220422004359174</div> </figure><p>代码改好之后,我们放到console上试验一下,成功拿到encrypt方法。</p><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="https://img.heshipeng.com/202204220047547.png?watermark/2/text/5YWz5rOo5b6u5L-h5YWs5LyX5Y-377ya6YCG5ZCR5LiA5q2l5q2l/font/5a6L5L2T/fontsize/300" alt="image-20220422004733474" title=""> </div> <div class="image-caption">image-20220422004733474</div> </figure><p>既然wb方法和encrypt方法都拿到了,那我们也就算是H7z的值。我们同样试验一下,也没问题。</p><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="https://img.heshipeng.com/202204220050357.png?watermark/2/text/5YWz5rOo5b6u5L-h5YWs5LyX5Y-377ya6YCG5ZCR5LiA5q2l5q2l/font/5a6L5L2T/fontsize/300" alt="image-20220422005005272" title=""> </div> <div class="image-caption">image-20220422005005272</div> </figure><p>H7z的值拿到了,接下来就是r7z,而r7z依赖于q7z,所以我们先看q7z。</p><h5 id="q7z生成规则"><a href="#q7z生成规则" class="headerlink" title="q7z生成规则"></a>q7z生成规则</h5><p>抠出q7z生成的代码:<code>q7z = n0B[M9r.R8z(699)](h7B[M9r.C8z(105)](Y7z), V7z[M9r.R8z(818)]())</code>,反混淆之后为:<code>q7z = n0B.encrypt(h7B.stringify(Y7z), V7z.wb())</code></p><p>encrypt,stringify,wb 🤔这几个怎么看起来那么眼熟?</p><p>V7z.wb实际上跟我们前面抠的g0b.wb一毛一样,然后h7B.stringify实际上就等同于JSON.stringify</p><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="https://img.heshipeng.com/202204221229448.png?watermark/2/text/5YWz5rOo5b6u5L-h5YWs5LyX5Y-377ya6YCG5ZCR5LiA5q2l5q2l/font/5a6L5L2T/fontsize/300" alt="image-20220422122917004" title=""> </div> <div class="image-caption">image-20220422122917004</div> </figure><p>n0B.encrypt则显然与前面抠的v0B.encrypt不同,这两个方法参数个数不同,返回值类型也不同。</p><p>所以要解决q7z,则需要抠出Y7z是如何生成的以及抠出n0B.encrypt。看下我们前面抠v0B的方式,是不是可以如法炮制🤨</p><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="https://img.heshipeng.com/202204221241305.png?watermark/2/text/5YWz5rOo5b6u5L-h5YWs5LyX5Y-377ya6YCG5ZCR5LiA5q2l5q2l/font/5a6L5L2T/fontsize/300" alt="image-20220422124107011" title=""> </div> <div class="image-caption">image-20220422124107011</div> </figure><p>运行结果如下,可以看到成功拿到了n0B.encrypt,只不过n0B.encrypt和之前的v0B.encrypt使用不一样。</p><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="https://img.heshipeng.com/202204221245985.png?watermark/2/text/5YWz5rOo5b6u5L-h5YWs5LyX5Y-377ya6YCG5ZCR5LiA5q2l5q2l/font/5a6L5L2T/fontsize/300" alt="image-20220422124505892" title=""> </div> <div class="image-caption">image-20220422124505892</div> </figure><p>接下来就是Y7z了。我们先看下Y7z是个啥?🤓</p><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="https://img.heshipeng.com/202204221250611.png?watermark/2/text/5YWz5rOo5b6u5L-h5YWs5LyX5Y-377ya6YCG5ZCR5LiA5q2l5q2l/font/5a6L5L2T/fontsize/300" alt="image-20220422125036518" title=""> </div> <div class="image-caption">image-20220422125036518</div> </figure><p>随机滑动滑块几次,观察这几个参数哪些是固定的,哪些是变化的。目测除了版本号v之外,其余几个参数都不是固定的,心累😅</p><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="https://img.heshipeng.com/202204221544594.png?watermark/2/text/5YWz5rOo5b6u5L-h5YWs5LyX5Y-377ya6YCG5ZCR5LiA5q2l5q2l/font/5a6L5L2T/fontsize/300" alt="image-20220422154448445" title=""> </div> <div class="image-caption">image-20220422154448445</div> </figure><p>找到Y7z定义的地方,如下图,我们挨个看吧。</p><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="https://img.heshipeng.com/202204221558976.png?watermark/2/text/5YWz5rOo5b6u5L-h5YWs5LyX5Y-377ya6YCG5ZCR5LiA5q2l5q2l/font/5a6L5L2T/fontsize/300" alt="image-20220422155807811" title=""> </div> <div class="image-caption">image-20220422155807811</div> </figure><ol><li>先看看aa是如何生成的。</li></ol><p>可以看到aa是由F7z变量赋值,我们在当前函数中找给F7z赋值的语句,并断上,如下图:</p><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="https://img.heshipeng.com/202204221604649.png?watermark/2/text/5YWz5rOo5b6u5L-h5YWs5LyX5Y-377ya6YCG5ZCR5LiA5q2l5q2l/font/5a6L5L2T/fontsize/300" alt="image-20220422160401488" title=""> </div> <div class="image-caption">image-20220422160401488</div> </figure><p><code>F7z = e7B[M9r.C8z(779)](V7z[M9r.R8z(602)])</code>反混淆为<code>F7z = e7B.t(V7z.b)</code>,V7z.b为一个13位的时间戳。我们跟进去这个方法:</p><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="https://img.heshipeng.com/202204221727047.png?watermark/2/text/5YWz5rOo5b6u5L-h5YWs5LyX5Y-377ya6YCG5ZCR5LiA5q2l5q2l/font/5a6L5L2T/fontsize/300" alt="image-20220422172757864" title=""> </div> <div class="image-caption">image-20220422172757864</div> </figure><p>我们先看返回值:<code>f1z[M9r.R8z(592)](M9r.C8z(346)) + M9r.C8z(370) + B1z[M9r.R8z(592)](M9r.R8z(346)) + M9r.R8z(370) + o1z[M9r.R8z(592)](M9r.C8z(346))</code>反混淆为<code>f1z.join("") + "!!" B1z.join("") + "!!" + o1z.join("")</code>,从这里可知,想要得到aa的值,就必须知道flz,Blz,olz的值。</p><p>我们再看看X1z:X1z是一个轨迹数组,每个元素都是一个包含三个向量的数组,分别是x坐标,y坐标,时间。经过分析知这个轨迹数组是浏览器监听鼠标事件得出来的,我们用机器去自动过验证码的时候是没办法通过这种方式得到的这个轨迹数组的,唯一的方式可能是写一种模拟拖动滑块的算法,生成这种轨迹,然后传给这个函数去计算aa的值。</p><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="https://img.heshipeng.com/202204221732544.png?watermark/2/text/5YWz5rOo5b6u5L-h5YWs5LyX5Y-377ya6YCG5ZCR5LiA5q2l5q2l/font/5a6L5L2T/fontsize/300" alt="image-20220422173201445" title=""> </div> <div class="image-caption">image-20220422173201445</div> </figure><p>分析到这里,很显然我们要魔改这个生成aa的函数,传入一个轨迹数组,返回aa的值。由于这个函数也很复杂,所以考虑直接抠出这个函数,而不必去纠结具体的flz,Blz,olz是怎么得到的。</p><p>说干就干。操作如下,解释说明下几个标红的地方。开头定义一个全局变量_e7B用于导出生成aa的函数’\x74’,然后我们定位到’\x74’函数属于e7B对象,把这个对象赋值给_e7B,然后把X1z变量作为参数提上来,把里面的这个变量删掉。</p><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="https://img.heshipeng.com/202204221744356.png?watermark/2/text/5YWz5rOo5b6u5L-h5YWs5LyX5Y-377ya6YCG5ZCR5LiA5q2l5q2l/font/5a6L5L2T/fontsize/300" alt="image-20220422174419168" title=""> </div> <div class="image-caption">image-20220422174419168</div> </figure><p>代码改好之后,我们测试一下,可以看到生成的aa与我们网站得到的一致。</p><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="https://img.heshipeng.com/202204221752482.png?watermark/2/text/5YWz5rOo5b6u5L-h5YWs5LyX5Y-377ya6YCG5ZCR5LiA5q2l5q2l/font/5a6L5L2T/fontsize/300" alt="image-20220422175211230" title=""> </div> <div class="image-caption">image-20220422175211230</div> </figure><p>虽然我们拿到了aa的值,但是跟生成F7z的里面那个值有出入,那就是说定义aa的地方虽然调用了e7B.t方法生成aa,但是外面有其它地方对这个值进行了修改。</p><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="https://img.heshipeng.com/202204221823454.png?watermark/2/text/5YWz5rOo5b6u5L-h5YWs5LyX5Y-377ya6YCG5ZCR5LiA5q2l5q2l/font/5a6L5L2T/fontsize/300" alt="image-20220422182356210" title=""> </div> <div class="image-caption">image-20220422182356210</div> </figure><p>我们在这个函数中搜索F7z,然后给所有包含F7z的赋值语句下断,一步步调试后发现修改F7z的地方如下:</p><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="https://img.heshipeng.com/202204221851258.png?watermark/2/text/5YWz5rOo5b6u5L-h5YWs5LyX5Y-377ya6YCG5ZCR5LiA5q2l5q2l/font/5a6L5L2T/fontsize/300" alt="image-20220422185131813" title=""> </div> <div class="image-caption">image-20220422185131813</div> </figure><p>我们抠出来这条语句,然后反混淆为:<code>e7B.u(F7z, V7z.d.c, V7z.d.s)</code>,我们看下V7z.d.c和V7z.d.s:</p><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="https://img.heshipeng.com/202204221855613.png?watermark/2/text/5YWz5rOo5b6u5L-h5YWs5LyX5Y-377ya6YCG5ZCR5LiA5q2l5q2l/font/5a6L5L2T/fontsize/300" alt="image-20220422185546434" title=""> </div> <div class="image-caption">image-20220422185546434</div> </figure><p>看着又有点似曾相识,emm,没错分别对应get.php返回的c和s:</p><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="https://img.heshipeng.com/202204221856167.png?watermark/2/text/5YWz5rOo5b6u5L-h5YWs5LyX5Y-377ya6YCG5ZCR5LiA5q2l5q2l/font/5a6L5L2T/fontsize/300" alt="image-20220422185646048" title=""> </div> <div class="image-caption">image-20220422185646048</div> </figure><p>然后我们得抠下e7B.u这个方法,意外的发现,其实这个方法包含的对象前面抠过来,既然对象已经抠了,这个方法自然就有了。</p><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="https://img.heshipeng.com/202204221859968.png?watermark/2/text/5YWz5rOo5b6u5L-h5YWs5LyX5Y-377ya6YCG5ZCR5LiA5q2l5q2l/font/5a6L5L2T/fontsize/300" alt="image-20220422185947808" title=""> </div> <div class="image-caption">image-20220422185947808</div> </figure><p>验证一下,结果没毛病:</p><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="https://img.heshipeng.com/202204221904380.png?watermark/2/text/5YWz5rOo5b6u5L-h5YWs5LyX5Y-377ya6YCG5ZCR5LiA5q2l5q2l/font/5a6L5L2T/fontsize/300" alt="image-20220422190458150" title=""> </div> <div class="image-caption">image-20220422190458150</div> </figure><ol><li>再看看userresponse如何生成的</li></ol><p>抠出生成userresponse的代码,然后反混淆为:<code>i7B.C(g7z, V7z.d.challenge)</code>,g7z是拖拽鼠标滑动滑块的距离,可以通过轨迹数组计算出这个滑动的距离。</p><p>代码如下:</p><figure class="highlight js"><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="keyword">for</span> (<span class="keyword">let</span> index = <span class="number">0</span>; index < X1z.length; index++) {</span><br><span class="line">passtime += X1z[index][<span class="number">2</span>];</span><br><span class="line">g7z += X1z[index][<span class="number">0</span>];</span><br><span class="line">}</span><br><span class="line">g7z -= X1z[<span class="number">0</span>][<span class="number">0</span>];</span><br></pre></td></tr></table></figure><p>V7z.d.challenge是get.php返回的:</p><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="https://img.heshipeng.com/202204222101497.png?watermark/2/text/5YWz5rOo5b6u5L-h5YWs5LyX5Y-377ya6YCG5ZCR5LiA5q2l5q2l/font/5a6L5L2T/fontsize/300" alt="image-20220422210127144" title=""> </div> <div class="image-caption">image-20220422210127144</div> </figure><p>i7B.C这个函数的话,按照上边介绍的方法,先抠出i7B对象,自然就可以拿到C方法了。</p><ol><li>passtime的计算</li></ol><p>目测passtime是轨迹数组的每个向量的时间累积:</p><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="https://img.heshipeng.com/202204222113188.png?watermark/2/text/5YWz5rOo5b6u5L-h5YWs5LyX5Y-377ya6YCG5ZCR5LiA5q2l5q2l/font/5a6L5L2T/fontsize/300" alt="image-20220422211356975" title=""> </div> <div class="image-caption">image-20220422211356975</div> </figure><p>计算代码如下:</p><figure class="highlight js"><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">let</span> passtime = <span class="number">0</span>;</span><br><span class="line"><span class="keyword">for</span> (<span class="keyword">let</span> index = <span class="number">0</span>; index < X1z.length; index++) {</span><br><span class="line"> passtime += X1z[index][<span class="number">2</span>];</span><br><span class="line">}</span><br></pre></td></tr></table></figure><ol><li>imgload生成</li></ol><p>imgload表示当前页面加载的图片数,这里我们用random随机一个值。</p><ol><li>rp的计算</li></ol><p>rp计算的代码为:<code>I0B(V7z[M9r.R8z(190)][M9r.R8z(189)] + V7z[M9r.C8z(190)][M9r.C8z(425)][M9r.R8z(504)](0, 32) + Y7z[M9r.C8z(193)])</code>反混淆之后为:<code>I0B(gt + challenge.slice(0, 32) + passtime)</code>,gt,challenge,passtime都已经算出,抠出I0B方法即可,如何抠?参照上面的方式。</p><h5 id="r7z生成规则"><a href="#r7z生成规则" class="headerlink" title="r7z生成规则"></a>r7z生成规则</h5><p>有了q7z,r7z自然就很简单了。因为前面说过<code>r7z = p7B.Ha(q7z)</code>,只需要抠出p7B即可,过程同理。</p><h5 id="编写代码"><a href="#编写代码" class="headerlink" title="编写代码"></a>编写代码</h5><p>按照上面的抠出相应的方法后,然后编写生成w参数的代码,代码如下:</p><figure class="highlight js"><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> G0b = <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>{}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">H1W</span>(<span class="params"></span>) </span>{</span><br><span class="line"> <span class="keyword">return</span> (<span class="number">65536</span> * (<span class="number">1</span> + <span class="built_in">Math</span>.random()) | <span class="number">0</span>).toString(<span class="number">16</span>).substring(<span class="number">1</span>);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> wb = H1W() + H1W() + H1W() + H1W();</span><br><span class="line"></span><br><span class="line">G0b.prototype.wb = <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>{</span><br><span class="line"> <span class="keyword">return</span> wb;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> _v0B;</span><br><span class="line"><span class="keyword">var</span> _n0B;</span><br><span class="line"><span class="keyword">var</span> _e7B;</span><br><span class="line"><span class="keyword">var</span> _i7B;</span><br><span class="line"><span class="keyword">var</span> _p7B;</span><br><span class="line"><span class="keyword">var</span> _I0B;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 抠出对应的object </span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">get_H7z</span>(<span class="params"></span>) </span>{</span><br><span class="line"> <span class="keyword">let</span> g0b = <span class="keyword">new</span> G0b();</span><br><span class="line"> <span class="keyword">let</span> aaa = <span class="keyword">new</span> _v0B();</span><br><span class="line"> <span class="keyword">return</span> aaa.encrypt(g0b.wb());</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">get_q7z</span>(<span class="params">X1z, c, s, gt, challenge</span>) </span>{</span><br><span class="line"> <span class="keyword">let</span> g0b = <span class="keyword">new</span> G0b();</span><br><span class="line"> <span class="keyword">let</span> passtime = <span class="number">0</span>;</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">let</span> index = <span class="number">0</span>; index < X1z.length; index++) {</span><br><span class="line"> passtime += X1z[index][<span class="number">2</span>];</span><br><span class="line"> }</span><br><span class="line"> <span class="built_in">console</span>.log(_e7B.u(_e7B.t(<span class="keyword">new</span> <span class="built_in">Date</span>().getTime(), X1z), c, s));</span><br><span class="line"> <span class="keyword">let</span> Y7z = {</span><br><span class="line"> <span class="string">"aa"</span>: _e7B.u(_e7B.t(<span class="keyword">new</span> <span class="built_in">Date</span>().getTime(), X1z), c, s),</span><br><span class="line"> <span class="string">"userresponse"</span>: _i7B.C(<span class="built_in">Math</span>.floor(<span class="built_in">Math</span>.random() * <span class="number">200</span>), challenge),</span><br><span class="line"> <span class="string">"passtime"</span>: passtime,</span><br><span class="line"> <span class="string">"imgload"</span>: <span class="built_in">Math</span>.floor(<span class="built_in">Math</span>.random() * <span class="number">200</span>),</span><br><span class="line"> <span class="string">"ep"</span>: {<span class="string">"v"</span>: <span class="string">"6.0.9"</span>}, </span><br><span class="line"> <span class="string">"rp"</span>: _I0B(gt + challenge.slice(<span class="number">0</span>, <span class="number">32</span>) + passtime) </span><br><span class="line"> };</span><br><span class="line"> <span class="keyword">return</span> _n0B.encrypt(<span class="built_in">JSON</span>.stringify(Y7z), g0b.wb());</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">get_r7z</span>(<span class="params">q7z</span>) </span>{</span><br><span class="line"> <span class="keyword">return</span> _p7B.Ha(q7z);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> X1z = [[<span class="number">21</span>,<span class="number">30</span>,<span class="number">0</span>],[<span class="number">1</span>,<span class="number">0</span>,<span class="number">22</span>],[<span class="number">2</span>,<span class="number">0</span>,<span class="number">8</span>],[<span class="number">2</span>,<span class="number">0</span>,<span class="number">17</span>],[<span class="number">3</span>,<span class="number">0</span>,<span class="number">17</span>],[<span class="number">3</span>,<span class="number">0</span>,<span class="number">16</span>],[<span class="number">2</span>,<span class="number">0</span>,<span class="number">17</span>],[<span class="number">3</span>,<span class="number">0</span>,<span class="number">16</span>],[<span class="number">2</span>,<span class="number">0</span>,<span class="number">17</span>],[<span class="number">3</span>,<span class="number">0</span>,<span class="number">17</span>],[<span class="number">2</span>,<span class="number">0</span>,<span class="number">17</span>],[<span class="number">1</span>,<span class="number">0</span>,<span class="number">16</span>],[<span class="number">1</span>,<span class="number">0</span>,<span class="number">17</span>],[<span class="number">1</span>,<span class="number">0</span>,<span class="number">33</span>],[<span class="number">1</span>,<span class="number">0</span>,<span class="number">17</span>],[<span class="number">2</span>,<span class="number">0</span>,<span class="number">16</span>],[<span class="number">2</span>,<span class="number">0</span>,<span class="number">17</span>],[<span class="number">1</span>,<span class="number">0</span>,<span class="number">17</span>],[<span class="number">1</span>,<span class="number">0</span>,<span class="number">16</span>],[<span class="number">1</span>,<span class="number">0</span>,<span class="number">17</span>],[<span class="number">1</span>,<span class="number">1</span>,<span class="number">50</span>],[<span class="number">1</span>,<span class="number">0</span>,<span class="number">67</span>],[<span class="number">0</span>,<span class="number">0</span>,<span class="number">18383</span>]];</span><br><span class="line"><span class="keyword">var</span> c = [<span class="number">12</span>, <span class="number">58</span>, <span class="number">98</span>, <span class="number">36</span>, <span class="number">43</span>, <span class="number">95</span>, <span class="number">62</span>, <span class="number">15</span>, <span class="number">12</span>];</span><br><span class="line"><span class="keyword">var</span> s = <span class="string">"424f4e78"</span>;</span><br><span class="line"><span class="keyword">var</span> challenge = <span class="string">"bb56791bfda35fac04bd7f7b14a5c8654r"</span>;</span><br><span class="line"><span class="keyword">var</span> gt = <span class="string">"f5c10f395211c77e386566112c6abf21"</span>;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> h7z = get_H7z();</span><br><span class="line"><span class="keyword">var</span> q7z = get_q7z(X1z, c, s, gt, challenge);</span><br><span class="line"><span class="keyword">var</span> r7z = get_r7z(q7z);</span><br><span class="line"><span class="keyword">var</span> w = r7z + h7z;</span><br><span class="line"></span><br><span class="line"><span class="built_in">console</span>.log(w);</span><br></pre></td></tr></table></figure><h5 id="查找bug"><a href="#查找bug" class="headerlink" title="查找bug"></a>查找bug</h5><p>从开始逆向极验滑块,到完整的抠出w的算法只花了一天时间,本来一切顺风顺水,本来以为so easy,但是去用抠出来的w参数去发起ajax.php请求时,一直不成功。然后就是各种查找bug,查找bug花了我四天时间…期间各种猜想都尝试过了,感觉当时想着要不算了。但是想着自己前后花了快一个星期的时间,不能轻易言弃。这里列举主要的几个问题。</p><ol><li>w参数的h7z和r7z两部分的关联性</li></ol><p>前面说过,w参数是由2部分组成h7z和r7z。两部分看起来没有关联,其实这里有一个坑,这俩是有关联的。我们再理一下思路:</p><figure class="highlight js"><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">h7z = V0B.encrypt(g0b.wb())</span><br><span class="line">q7z = n0B.encypt(<span class="built_in">JSON</span>.stringify(Y7z), g0b.wb())</span><br><span class="line">r7z = p7B.Ha(q7z)</span><br></pre></td></tr></table></figure><p>h7z和r7z的生成这俩都用到了一个随机字符串wb,但是这两个随机字符串必须一致!!!,就是说后端会解密h7z和r7z然后比较这两个随机字符串是否一致,如果不一致就会不通过。我前期就是在生成h7z和r7z的地方调用了2次wb,导致验证一直不通过。正确的做法调用一次wb,并用一个全局变量保存,然后生成h7z和r7z的地方直接去拿这个全局变量即可。</p><ol><li>同名的对象</li></ol><p>另外导致一直通过的另外一部分原因是同名的对象有一些,再抠对象的时候一定要仔细,千万不能抠错。</p><ol><li>aa轨迹的确定</li></ol><p>跟某一个参数的值的时候,除了要在变量声明的地方分析变量值是如何变化的,还要注意在其它地方,尤其是逗号表达式的地方也隐藏着值的变化。比如:<code>j1r = (F7z = e7B[M9r.R8z(544)](F7z, V7z[M9r.R8z(190)][M9r.C8z(540)], V7z[M9r.R8z(190)][M9r.R8z(6)])</code>表面上是变量j1r的赋值,隐藏着变量F7z的值的变化。</p><p>这里介绍一些查找bug的技巧:</p><p>这里的w跟值包含2部分,r7z和h7z。可以先定位是h7z还是r7z的问题,定位到是哪半区的问题后,然后根据实际网站运行的结果和你自己编写的代码运行的结果一步步调试,2者结果为什么不一致,具体分析原因。</p><p>也可以利用浏览器的override功能或者hook尽可能的多输出一些变量的值,对比自己程序运行的结果和网站输出的值,看是哪一步出现问题。我们在调bug的时候就利用了override替换js文件输出了很多日志,如下图:</p><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="https://img.heshipeng.com/202204281459340.png?watermark/2/text/5YWz5rOo5b6u5L-h5YWs5LyX5Y-377ya6YCG5ZCR5LiA5q2l5q2l/font/5a6L5L2T/fontsize/300" alt="image-20220428145928176" title=""> </div> <div class="image-caption">image-20220428145928176</div> </figure>]]></content>
<summary type="html"><blockquote>
<p> 免责声明:<strong>本文章中所有内容仅供学习交流,抓包内容、敏感网址、数据接口均已做脱敏处理,严禁用于商业用途和非法用途,否则由此产生的一切后果均与作者无关,若有侵权,请联系我立即删除!</strong></p>
</blockquote></summary>
<category term="JS逆向" scheme="http://example.com/categories/JS%E9%80%86%E5%90%91/"/>
<category term="JS" scheme="http://example.com/tags/JS/"/>
<category term="逆向" scheme="http://example.com/tags/%E9%80%86%E5%90%91/"/>
<category term="验证码" scheme="http://example.com/tags/%E9%AA%8C%E8%AF%81%E7%A0%81/"/>
</entry>
<entry>
<title>JS逆向案例——极验滑块验证码底图还原</title>
<link href="http://example.com/JS%E9%80%86%E5%90%91%E6%A1%88%E4%BE%8B%E2%80%94%E2%80%94%E6%9E%81%E9%AA%8C%E6%BB%91%E5%9D%97%E9%AA%8C%E8%AF%81%E7%A0%81%E5%BA%95%E5%9B%BE%E8%BF%98%E5%8E%9F/"/>
<id>http://example.com/JS%E9%80%86%E5%90%91%E6%A1%88%E4%BE%8B%E2%80%94%E2%80%94%E6%9E%81%E9%AA%8C%E6%BB%91%E5%9D%97%E9%AA%8C%E8%AF%81%E7%A0%81%E5%BA%95%E5%9B%BE%E8%BF%98%E5%8E%9F/</id>
<published>2022-04-20T02:41:37.000Z</published>
<updated>2022-04-24T09:04:10.998Z</updated>
<content type="html"><![CDATA[<blockquote><p>免责声明:<strong>本文章中所有内容仅供学习交流,抓包内容、敏感网址、数据接口均已做脱敏处理,严禁用于商业用途和非法用途,否则由此产生的一切后果均与作者无关,若有侵权,请联系我立即删除!</strong></p></blockquote><h4 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h4><p>本文是机器过极验滑块验证码系列文章的第一篇,底图的还原,包括带缺口的底图以及完整底图的还原。后边还会陆陆续续发文,讲解提交验证过程请求w参数的跟值,如何补环境,如何包括利用像素点RGB差值获取缺口位置以及通过机器学习获取缺口位置,最后会通过几个采用极验验证码的网站去完整的展示整个自动化过程。而极验滑块系列只是验证码系列的第一个系列,后边会罗列市面上常用的验证码,然后发文一一解决。</p><h4 id="逆向分析"><a href="#逆向分析" class="headerlink" title="逆向分析"></a>逆向分析</h4><p>网址:aHR0cHM6Ly93d3cudGlhbnlhbmNoYS5jb20v</p><p>以天眼查的登录为例,在进行滑块验证时,进行抓包分析。</p><h5 id="抓包"><a href="#抓包" class="headerlink" title="抓包"></a>抓包</h5><p>将一些重要的请求罗列如下:</p><ol><li>geetest.xhtml</li></ol><p>传入的是一个13位时间戳的uuid</p><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="https://img.heshipeng.com/202204201143166.png?watermark/2/text/5YWz5rOo5b6u5L-h5YWs5LyX5Y-377ya6YCG5ZCR5LiA5q2l5q2l/font/5a6L5L2T/fontsize/300" alt="image-20220420114316016" title=""> </div> <div class="image-caption">image-20220420114316016</div> </figure><p>返回的是challenge和gt。</p><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="https://img.heshipeng.com/202204201144667.png?watermark/2/text/5YWz5rOo5b6u5L-h5YWs5LyX5Y-377ya6YCG5ZCR5LiA5q2l5q2l/font/5a6L5L2T/fontsize/300" alt="image-20220420114408595" title=""> </div> <div class="image-caption">image-20220420114408595</div> </figure><ol><li>gettype.php</li></ol><p>获取验证码类型,传入的是第一个请求返回的gt。</p><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="https://img.heshipeng.com/202204201153409.png?watermark/2/text/5YWz5rOo5b6u5L-h5YWs5LyX5Y-377ya6YCG5ZCR5LiA5q2l5q2l/font/5a6L5L2T/fontsize/300" alt="image-20220420115339355" title=""> </div> <div class="image-caption">image-20220420115339355</div> </figure><p>返回的是验证码类型以及一些JS文件:</p><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="https://img.heshipeng.com/202204201154420.png?watermark/2/text/5YWz5rOo5b6u5L-h5YWs5LyX5Y-377ya6YCG5ZCR5LiA5q2l5q2l/font/5a6L5L2T/fontsize/300" alt="image-20220420115438377" title=""> </div> <div class="image-caption">image-20220420115438377</div> </figure><ol><li>get.php</li></ol><p>入参同样是前边返回的challenge和gt。</p><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="https://img.heshipeng.com/202204201231886.png?watermark/2/text/5YWz5rOo5b6u5L-h5YWs5LyX5Y-377ya6YCG5ZCR5LiA5q2l5q2l/font/5a6L5L2T/fontsize/300" alt="image-20220420123131763" title=""> </div> <div class="image-caption">image-20220420123131763</div> </figure><p>返回乱序之后的完整验证码底图和缺口验证码底图,同时返回了一个新的challenge。</p><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="https://img.heshipeng.com/202204201502291.png?watermark/2/text/5YWz5rOo5b6u5L-h5YWs5LyX5Y-377ya6YCG5ZCR5LiA5q2l5q2l/font/5a6L5L2T/fontsize/300" alt="image-20220420150236107" title=""> </div> <div class="image-caption">image-20220420150236107</div> </figure><ol><li>ajax.php</li></ol><p>这个是我们手动进行验证码提交时发的包,重要的入参是gt,challenge以及w。其中w是加密字符串,包含滑块的轨迹。challenge是更新之后的那个新的值,即上一步get.php获取到的。</p><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="https://img.heshipeng.com/202204201506631.png?watermark/2/text/5YWz5rOo5b6u5L-h5YWs5LyX5Y-377ya6YCG5ZCR5LiA5q2l5q2l/font/5a6L5L2T/fontsize/300" alt="image-20220420150610485" title=""> </div> <div class="image-caption">image-20220420150610485</div> </figure><p>返回滑块是否验证成功,如下:</p><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="https://img.heshipeng.com/202204201506832.png?watermark/2/text/5YWz5rOo5b6u5L-h5YWs5LyX5Y-377ya6YCG5ZCR5LiA5q2l5q2l/font/5a6L5L2T/fontsize/300" alt="image-20220420150645777" title=""> </div> <div class="image-caption">image-20220420150645777</div> </figure><h5 id="逆向目标确定"><a href="#逆向目标确定" class="headerlink" title="逆向目标确定"></a>逆向目标确定</h5><p>经过上边的抓包分析之后,可以发现几个关键点,首先是几个关键参数:challenge,gt,w,然后是乱序的完整底图和乱序的带缺口底图,最后是缺口位置的识别。</p><ul><li><p>challenge和gt自始自终都是通过请求接口返回,所以这个不需要逆向。</p></li><li><p>w参数是最后一步向服务端提交验证码验证结果的值,包含了环境监测,滑块轨迹的加密。如果使用playwright这种模拟浏览器工具去提交验证码,这个参数可以不用逆向,如果是自己走JS或者Python发包,则需要逆向刨一下算法是怎么写的。</p></li><li><p>由于无法直接的获取正确的完整底图和带缺口的底图(有些网站上极验验证正确的底图可能是用canvas加载),所以需要进分析如何从无序的底图变为有序的底图。</p></li><li>缺口位置的识别,有2种方案。一种是通过比较完整底图和缺口底图,利用像素点的RGB像素差值,来判断缺口位置;另一种是通过机器学习进行训练,然后得到一个模型用于识别缺口位置。</li></ul><p>上面几点,不管如何偷懒,第三步底图的还原是必然需要的,不然没办法计算缺口位置,没办法生成轨迹,自然没办法进行验证。所以底图还原是整个滑块验证的第一步。</p><h5 id="底图缺口还原"><a href="#底图缺口还原" class="headerlink" title="底图缺口还原"></a>底图缺口还原</h5><p>我们先看下滑块的大小:</p><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="https://img.heshipeng.com/202204201632353.png?watermark/2/text/5YWz5rOo5b6u5L-h5YWs5LyX5Y-377ya6YCG5ZCR5LiA5q2l5q2l/font/5a6L5L2T/fontsize/300" alt="image-20220420163229256" title=""> </div> <div class="image-caption">image-20220420163229256</div> </figure><p>图片大小 w = 260px, h = 116px。我们点击图片选择审查元素。</p><p>可以看到底图是由52个div组成,每个div的w = 10px,h = 58px。分为上下两个半区,每个半区26个div。刚好组成260px * 116px的矩形验证码。</p><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="https://img.heshipeng.com/202204201636328.png?watermark/2/text/5YWz5rOo5b6u5L-h5YWs5LyX5Y-377ya6YCG5ZCR5LiA5q2l5q2l/font/5a6L5L2T/fontsize/300" alt="image-20220420163621256" title=""> </div> <div class="image-caption">image-20220420163621256</div> </figure><p>我们看第一个div,即上半区左上角的第一个div,background-position = -157px -58px。表示将background-image向左偏移157个像素,向上偏移58个像素,作为第一个div放在上半区最左边。由于前面分析过,每个div的宽是10px,高是58px。所以第一个div四个顶点在background-image上的相对坐标是(157, 58), (167, 58), (157, 116), (167, 116)。</p><p>同理,我们推测上半区第二个div的四个顶点的相对坐标分别是(145, 0), (155, 0), (145, 58), (155, 58)。</p><p>此外,background-image就是我们抓包分析的第三步获取到的乱序图。</p><p>知道了每一个个div的坐标,以及乱序的背景图,就可以通过从乱序图上裁剪出一个个div,然后再拼接到一起,这样不就构成了正确有序的图片?</p><h5 id="编程实现"><a href="#编程实现" class="headerlink" title="编程实现"></a>编程实现</h5><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> math</span><br><span class="line"><span class="keyword">from</span> PIL <span class="keyword">import</span> Image</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">div_offset = [</span><br><span class="line"> {<span class="string">"x"</span>: -<span class="number">157</span>, <span class="string">"y"</span>: -<span class="number">58</span>}, </span><br><span class="line"> <span class="comment"># 省略若干行 </span></span><br><span class="line"> {<span class="string">"x"</span>: -<span class="number">205</span>, <span class="string">"y"</span>: <span class="number">0</span>}</span><br><span class="line">]</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">restore_pic</span>(<span class="params">pic_path, new_pic_path</span>):</span></span><br><span class="line"> unordered_pic = Image.<span class="built_in">open</span>(pic_path)</span><br><span class="line"> ordered_pic = unordered_pic.copy()</span><br><span class="line"></span><br><span class="line"> <span class="comment"># 裁剪并拼接</span></span><br><span class="line"> <span class="keyword">for</span> i, d <span class="keyword">in</span> <span class="built_in">enumerate</span>(div_offset):</span><br><span class="line"> im = unordered_pic.crop((math.fabs(d[<span class="string">'x'</span>]), math.fabs(d[<span class="string">'y'</span>]), math.fabs(d[<span class="string">'x'</span>]) + <span class="number">10</span>, math.fabs(d[<span class="string">'y'</span>]) + <span class="number">58</span>))</span><br><span class="line"> <span class="comment"># 上半区</span></span><br><span class="line"> <span class="keyword">if</span> d[<span class="string">'y'</span>] != <span class="number">0</span>:</span><br><span class="line"> ordered_pic.paste(im, (<span class="number">10</span> * (i % (<span class="built_in">len</span>(div_offset) // <span class="number">2</span>)), <span class="number">0</span>), <span class="literal">None</span>)</span><br><span class="line"> <span class="keyword">else</span>:</span><br><span class="line"> ordered_pic.paste(im, (<span class="number">10</span> * (i % (<span class="built_in">len</span>(div_offset) // <span class="number">2</span>)), <span class="number">58</span>), <span class="literal">None</span>)</span><br><span class="line"></span><br><span class="line"> ordered_pic.save(new_pic_path)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> __name__ == <span class="string">'__main__'</span>:</span><br><span class="line"> restore_pic(<span class="string">"img.png"</span>, <span class="string">"new_img.png"</span>)</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>说一下用到的PIL库的几个方法:copy表示复制一张图片;crop表示以矩形区域裁剪,入参是一个四个元素的元组,分别是矩形左上角顶点的x坐标,左上角顶点的y坐标,右下角顶点的x坐标,右下角顶点的y坐标;paste表示粘贴图片。</p><p>罗列一下程序执行的结果,底图乱序与正序如下:</p><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="https://img.heshipeng.com/202204202003975.png?watermark/2/text/5YWz5rOo5b6u5L-h5YWs5LyX5Y-377ya6YCG5ZCR5LiA5q2l5q2l/font/5a6L5L2T/fontsize/300" alt="image-20220420200318898" title=""> </div> <div class="image-caption">image-20220420200318898</div> </figure><h4 id="代码获取"><a href="#代码获取" class="headerlink" title="代码获取"></a>代码获取</h4><p>扫码关注微信号——逆向一步步,公众号内回复关键词<code>07</code>就可以获取本案例的全部代码。</p><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="https://img.heshipeng.com/202204202011837.jpg" alt="qrcode_for_gh_509fdefd3c81_258" title=""> </div> <div class="image-caption">qrcode_for_gh_509fdefd3c81_258</div> </figure>]]></content>
<summary type="html"><blockquote>
<p>免责声明:<strong>本文章中所有内容仅供学习交流,抓包内容、敏感网址、数据接口均已做脱敏处理,严禁用于商业用途和非法用途,否则由此产生的一切后果均与作者无关,若有侵权,请联系我立即删除!</strong></p>
</blockquote>
</summary>
<category term="JS逆向" scheme="http://example.com/categories/JS%E9%80%86%E5%90%91/"/>
<category term="JS" scheme="http://example.com/tags/JS/"/>
<category term="逆向" scheme="http://example.com/tags/%E9%80%86%E5%90%91/"/>
<category term="验证码" scheme="http://example.com/tags/%E9%AA%8C%E8%AF%81%E7%A0%81/"/>
</entry>
<entry>
<title>JS逆向案例——网上管家婆</title>
<link href="http://example.com/JS%E9%80%86%E5%90%91%E6%A1%88%E4%BE%8B%E2%80%94%E2%80%94%E7%BD%91%E4%B8%8A%E7%AE%A1%E5%AE%B6%E5%A9%86/"/>
<id>http://example.com/JS%E9%80%86%E5%90%91%E6%A1%88%E4%BE%8B%E2%80%94%E2%80%94%E7%BD%91%E4%B8%8A%E7%AE%A1%E5%AE%B6%E5%A9%86/</id>
<published>2022-04-17T08:38:11.000Z</published>
<updated>2022-04-18T09:57:15.676Z</updated>
<content type="html"><![CDATA[<blockquote><p>免责声明:<strong>本文章中所有内容仅供学习交流,抓包内容、敏感网址、数据接口均已做脱敏处理,严禁用于商业用途和非法用途,否则由此产生的一切后果均与作者无关,若有侵权,请联系我立即删除!</strong></p></blockquote><p>网址:aHR0cHM6Ly9sb2dpbi53c2dqcC5jb20uY24v</p><p>逆向目标:登录接口参数clientinfo,userName,password生成规则</p><p>先看下这几个参数长啥样:</p><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="https://img.heshipeng.com/202204181636387.png?watermark/2/text/5YWz5rOo5b6u5L-h5YWs5LyX5Y-377ya6YCG5ZCR5LiA5q2l5q2l/font/5a6L5L2T/fontsize/300" alt="image-20220418163645276" title=""> </div> <div class="image-caption">image-20220418163645276</div> </figure><p>userName和password长度都是128位,盲猜是AES。</p><p>全局搜索一下clientinfo,找到了clientinfo生成的地方:</p><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="https://img.heshipeng.com/202204181638700.png?watermark/2/text/5YWz5rOo5b6u5L-h5YWs5LyX5Y-377ya6YCG5ZCR5LiA5q2l5q2l/font/5a6L5L2T/fontsize/300" alt="image-20220418163838625" title=""> </div> <div class="image-caption">image-20220418163838625</div> </figure><p>点进去这个方法:</p><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="https://img.heshipeng.com/202204181639679.png?watermark/2/text/5YWz5rOo5b6u5L-h5YWs5LyX5Y-377ya6YCG5ZCR5LiA5q2l5q2l/font/5a6L5L2T/fontsize/300" alt="image-20220418163925615" title=""> </div> <div class="image-caption">image-20220418163925615</div> </figure><p>可以看到先是调用一个doGetInfo获取info信息,然后调用base64encode方法对info进行编码,直接抠出这2个方法即可。</p><p>抠出来之后,执行下doGetInfo函数,结果如下,其实就是把环境的一些参数信息用特殊符号拼接,为了不被检测出来,最好对比浏览器的实际结果,把相应的参数的值补上。</p><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="https://img.heshipeng.com/202204181726654.png?watermark/2/text/5YWz5rOo5b6u5L-h5YWs5LyX5Y-377ya6YCG5ZCR5LiA5q2l5q2l/font/5a6L5L2T/fontsize/300" alt="image-20220418172659541" title=""> </div> <div class="image-caption">image-20220418172659541</div> </figure><p>接着看下userName和password,全局搜索下password:</p><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="https://img.heshipeng.com/202204181734700.png?watermark/2/text/5YWz5rOo5b6u5L-h5YWs5LyX5Y-377ya6YCG5ZCR5LiA5q2l5q2l/font/5a6L5L2T/fontsize/300" alt="image-20220418173455546" title=""> </div> <div class="image-caption">image-20220418173455546</div> </figure><p>发现 userName和password采用的一种加密,就以password为例吧。</p><p>encryptedString加密方法接受2个参数,第一个是key,第二个是加密的明文。key的生成,往上面看几行,就会找到<code>var key = new RSAKeyPair();</code>这行代码,然后去抠出RSAKeyPair这个对象,缺啥补啥,一直抠就完事了,比较简单。</p><p>这里有一个注意的就是,下面一行代码如果漏掉了,就会陷入死循环,导致加密结果出不来:</p><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="https://img.heshipeng.com/202204181745538.png?watermark/2/text/5YWz5rOo5b6u5L-h5YWs5LyX5Y-377ya6YCG5ZCR5LiA5q2l5q2l/font/5a6L5L2T/fontsize/300" alt="image-20220418174523439" title=""> </div> <div class="image-caption">image-20220418174523439</div> </figure><p>我们在抠主要逻辑的时候,一定要看下它前后的逻辑,一些看似无关的逻辑能保留的尽量保留,比如我们要的encryptedString方法,是在postLogin方法中调用的,postLogin前边部分是一些从html表单中取值的逻辑,后边部分是发包的逻辑,除去这2部分,中间一条<code>setMaxDigits(129);</code>这个逻辑不知道是干啥用的,就尽量保留。</p><p>抠完JS并补好环境后,整个执行的结果如下:</p><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="https://img.heshipeng.com/202204181753715.png?watermark/2/text/5YWz5rOo5b6u5L-h5YWs5LyX5Y-377ya6YCG5ZCR5LiA5q2l5q2l/font/5a6L5L2T/fontsize/300" alt="image-20220418175307668" title=""> </div> <div class="image-caption">image-20220418175307668</div> </figure><p>补环境的代码如下:</p><figure class="highlight js"><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">var</span> <span class="built_in">window</span> = {};</span><br><span class="line"><span class="keyword">var</span> <span class="built_in">document</span> = {};</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> Navigator = <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>{</span><br><span class="line"></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">Navigator.prototype.userAgent = <span class="string">"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.4896.88 Safari/537.36"</span>;</span><br><span class="line">Navigator.prototype.platform = <span class="string">"MacIntel"</span>;</span><br><span class="line">navigator = <span class="keyword">new</span> Navigator();</span><br><span class="line"></span><br><span class="line"><span class="built_in">window</span> = {<span class="string">"navigator"</span>: navigator};</span><br></pre></td></tr></table></figure><p>代码获取?扫码关注微信公众号——逆向一步步,公众号内回复关键字<code>06</code>就可获取完整代码。</p><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="https://img.heshipeng.com/202204181756282.jpg" alt="qrcode_for_gh_509fdefd3c81_258" title=""> </div> <div class="image-caption">qrcode_for_gh_509fdefd3c81_258</div> </figure>]]></content>
<summary type="html"><blockquote>
<p>免责声明:<strong>本文章中所有内容仅供学习交流,抓包内容、敏感网址、数据接口均已做脱敏处理,严禁用于商业用途和非法用途,否则由此产生的一切后果均与作者无关,若有侵权,请联系我立即删除!</strong></p>
</blockquote>
</summary>
<category term="JS逆向" scheme="http://example.com/categories/JS%E9%80%86%E5%90%91/"/>
<category term="JS" scheme="http://example.com/tags/JS/"/>
<category term="逆向" scheme="http://example.com/tags/%E9%80%86%E5%90%91/"/>
</entry>
<entry>
<title>JS逆向之Fiddler编程猫插件使用</title>
<link href="http://example.com/JS%E9%80%86%E5%90%91%E4%B9%8BFiddler%E7%BC%96%E7%A8%8B%E7%8C%AB%E6%8F%92%E4%BB%B6%E4%BD%BF%E7%94%A8/"/>
<id>http://example.com/JS%E9%80%86%E5%90%91%E4%B9%8BFiddler%E7%BC%96%E7%A8%8B%E7%8C%AB%E6%8F%92%E4%BB%B6%E4%BD%BF%E7%94%A8/</id>
<published>2022-04-10T11:17:22.000Z</published>
<updated>2022-04-10T16:42:17.438Z</updated>
<content type="html"><![CDATA[<h4 id="安装FD,配置编程猫插件"><a href="#安装FD,配置编程猫插件" class="headerlink" title="安装FD,配置编程猫插件"></a>安装FD,配置编程猫插件</h4><p>首先需要安装Fiddler,要求版本>=4.6.3。建议官网下载,下载地址:<a href="https://www.telerik.com/download/fiddler/fiddler4">https://www.telerik.com/download/fiddler/fiddler4</a></p><p>下载安装完FD之后,找到FD的安装目录,打开一个叫做Scripts的文件夹:</p><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="https://img.heshipeng.com/202204102042092.png?watermark/2/text/5YWz5rOo5b6u5L-h5YWs5LyX5Y-377ya6YCG5ZCR5LiA5q2l5q2l/font/5a6L5L2T/fontsize/300" alt="image-20220410204247917" title=""> </div> <div class="image-caption">image-20220410204247917</div> </figure><p>解压FD编程猫插件(插件下载地址见文末),将里面的所有.dll扩展文件拷贝到上边的Scripts文件夹下:</p><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="https://img.heshipeng.com/202204102044610.png?watermark/2/text/5YWz5rOo5b6u5L-h5YWs5LyX5Y-377ya6YCG5ZCR5LiA5q2l5q2l/font/5a6L5L2T/fontsize/300" alt="image-20220410204433530" title=""> </div> <div class="image-caption">image-20220410204433530</div> </figure><p>然后关闭Fiddler,并重新启动Fiddler,注意第一次启动需要以管理员身份运行。打开之后界面如下表示安装成功:</p><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="https://img.heshipeng.com/202204102046340.png?watermark/2/text/5YWz5rOo5b6u5L-h5YWs5LyX5Y-377ya6YCG5ZCR5LiA5q2l5q2l/font/5a6L5L2T/fontsize/300" alt="image-20220410204605262" title=""> </div> <div class="image-caption">image-20220410204605262</div> </figure><h4 id="Fiddler的使用"><a href="#Fiddler的使用" class="headerlink" title="Fiddler的使用"></a>Fiddler的使用</h4><h5 id="Chrome配置Fiddler抓包"><a href="#Chrome配置Fiddler抓包" class="headerlink" title="Chrome配置Fiddler抓包"></a>Chrome配置Fiddler抓包</h5><p>安装SwitchyOmega代理管理Chrome浏览器插件,在Chrome扩展应用商城中搜索SwitchyOmega,然后安装。添加好插件后,打开SwitchyOmega点击新建情景模式:</p><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="https://img.heshipeng.com/202204102220279.png?watermark/2/text/5YWz5rOo5b6u5L-h5YWs5LyX5Y-377ya6YCG5ZCR5LiA5q2l5q2l/font/5a6L5L2T/fontsize/300" alt="image-20220410222059063" title=""> </div> <div class="image-caption">image-20220410222059063</div> </figure><p>我们就把新建的情景命名为Fiddler,然后第一行,代理协议填HTTP,代理服务器地址填:127.0.0.1(因为我是用的虚拟机,所以这里填虚拟机的ip地址),端口填8888。</p><p>在最左边选择应用选项,就保存了刚才的配置。</p><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="https://img.heshipeng.com/202204102227326.png?watermark/2/text/5YWz5rOo5b6u5L-h5YWs5LyX5Y-377ya6YCG5ZCR5LiA5q2l5q2l/font/5a6L5L2T/fontsize/300" alt="image-20220410222742171" title=""> </div> <div class="image-caption">image-20220410222742171</div> </figure><p>打开一个页面,比如百度,然后点击SwitchyOmega,选择Fiddler插件,然后刷新页面。</p><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="https://img.heshipeng.com/202204102246448.png?watermark/2/text/5YWz5rOo5b6u5L-h5YWs5LyX5Y-377ya6YCG5ZCR5LiA5q2l5q2l/font/5a6L5L2T/fontsize/300" alt="image-20220410224632321" title=""> </div> <div class="image-caption">image-20220410224632321</div> </figure><p>这个时候可以看到Fiddler中已经成功抓到了百度的页面请求,但是由于百度使用的是HTTPS协议,我们还没有配置证书,导致Fiddler抓包的数据不正常,并且百度首页也如同上边展示的那样。FD配置证书抓取HTTPS,也正是接下来要讲的。</p><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="https://img.heshipeng.com/202204102248727.png?watermark/2/text/5YWz5rOo5b6u5L-h5YWs5LyX5Y-377ya6YCG5ZCR5LiA5q2l5q2l/font/5a6L5L2T/fontsize/300" alt="image-20220410224830653" title=""> </div> <div class="image-caption">image-20220410224830653</div> </figure><h5 id="Fiddler配置抓取HTTPS"><a href="#Fiddler配置抓取HTTPS" class="headerlink" title="Fiddler配置抓取HTTPS"></a>Fiddler配置抓取HTTPS</h5><p>打开Fiddler,点击工具栏的Tools->Options,点击HTTPS选项,按照如下图勾选相应的选项:</p><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="https://img.heshipeng.com/202204102256630.png?watermark/2/text/5YWz5rOo5b6u5L-h5YWs5LyX5Y-377ya6YCG5ZCR5LiA5q2l5q2l/font/5a6L5L2T/fontsize/300" alt="image-20220410225612499" title=""> </div> <div class="image-caption">image-20220410225612499</div> </figure><p>点击Trust Root Certificate,点击Export Root Certificate将FD的证书保存到桌面。这时候桌面上会出现证书FiddlerRoot.cer文件,点击OK设置成功,关闭fiddler。</p><p>打开Chrome浏览器,在浏览器地址中输入:chrome://settings/进入chrome的设置页面,在搜索栏中输入管理证书,点击管理证书会跳转到钥匙串管理程序(Mac系统)。</p><p>将桌面的FD证书拖拽到这个钥匙串管理程序中,拖拽成功后,证书默认是不信任的,如下图:</p><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="https://img.heshipeng.com/202204102301070.png?watermark/2/text/5YWz5rOo5b6u5L-h5YWs5LyX5Y-377ya6YCG5ZCR5LiA5q2l5q2l/font/5a6L5L2T/fontsize/300" alt="image-20220410230101918" title=""> </div> <div class="image-caption">image-20220410230101918</div> </figure><p>双击这个DO_NOT_TRUST_FiddlerRoot证书,在弹出的窗口中点击信任,选择始终信任。如下图:</p><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="https://img.heshipeng.com/202204102302871.png?watermark/2/text/5YWz5rOo5b6u5L-h5YWs5LyX5Y-377ya6YCG5ZCR5LiA5q2l5q2l/font/5a6L5L2T/fontsize/300" alt="image-20220410230244807" title=""> </div> <div class="image-caption">image-20220410230244807</div> </figure><p>点击左上角关闭按钮,弹出对话框输入root密码。</p><p>打开Fiddler,刷新百度首页,可以看到百度首页正常加载,Fiddler中也能正常的抓到百度的请求。</p><h5 id="编程猫插件的使用"><a href="#编程猫插件的使用" class="headerlink" title="编程猫插件的使用"></a>编程猫插件的使用</h5><p>点击右边的编程猫专用插件,会出现一列导航,如下:</p><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="https://img.heshipeng.com/202204102345154.png?watermark/2/text/5YWz5rOo5b6u5L-h5YWs5LyX5Y-377ya6YCG5ZCR5LiA5q2l5q2l/font/5a6L5L2T/fontsize/300" alt="image-20220410234536919" title=""> </div> <div class="image-caption">image-20220410234536919</div> </figure><p>生成易代码可以忽略,是易语言专用。JS调试工具,内置了一个V8引擎,可以在这个tab里面编辑JS代码,然后进行调试。JSON解析是只格式化JSON数据。数据加密解密,则提供一些常见的加密算法,比如MD5,Sha,AES,DES等。编码解码则提供了一些常见的编码工具,比如Base64。这个编程猫工具重点关注的是注入Hook与内存漫游。</p><p>首先说注入Hook,默认提供了一个Hook Cookie的代码,如下图。勾选左边的开启,然后地址栏为空表示对所有的地址都注入Hook代码,如果只对特定的地址注入代码,则填上地址即可。</p><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="https://img.heshipeng.com/202204102353138.png?watermark/2/text/5YWz5rOo5b6u5L-h5YWs5LyX5Y-377ya6YCG5ZCR5LiA5q2l5q2l/font/5a6L5L2T/fontsize/300" alt="image-20220410235308981" title=""> </div> <div class="image-caption">image-20220410235308981</div> </figure><p>我们以百度首页为例,开启注入Hook之后,刷新百度首页,在console控制台上打印了日志信息,如下图:</p><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="https://img.heshipeng.com/202204102358312.png?watermark/2/text/5YWz5rOo5b6u5L-h5YWs5LyX5Y-377ya6YCG5ZCR5LiA5q2l5q2l/font/5a6L5L2T/fontsize/300" alt="image-20220410235817134" title=""> </div> <div class="image-caption">image-20220410235817134</div> </figure><p>除了注入Hook Cookie的代码,还可以注入Hook window属性,websocket,内置函数或者自定义函数。参考<a href="https://blog.heshipeng.com/JS%E9%80%86%E5%90%91%E4%B9%8B%E5%BF%AB%E9%80%9F%E5%AE%9A%E4%BD%8D%E5%85%B3%E9%94%AE%E4%BB%A3%E7%A0%81/">JS逆向之快速定位关键代码</a></p><p>接着说内存漫游。所谓浏览器内存漫游,其原理通过ast把浏览器中所有的变量,参数中间值在内存中的变化进行存储,然后我们就可以搜索这些值,从而根据值确定这个参数在浏览器中出现的位置,变化情况等等。</p><p>我们以极验的w参数为例。FD上点击内存漫游tab,然后点击下边的开启内存漫游按钮:</p><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="https://img.heshipeng.com/202204110028459.png?watermark/2/text/5YWz5rOo5b6u5L-h5YWs5LyX5Y-377ya6YCG5ZCR5LiA5q2l5q2l/font/5a6L5L2T/fontsize/300" alt="image-20220411002857220" title=""> </div> <div class="image-caption">image-20220411002857220</div> </figure><p>我们打开极验的测试网址:<a href="https://www.geetest.com/demo/slide-float.html,然后拖动滑块完成验证。找到这个ajax.php的请求,复制下来w参数的值,如下图:">https://www.geetest.com/demo/slide-float.html,然后拖动滑块完成验证。找到这个ajax.php的请求,复制下来w参数的值,如下图:</a></p><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="https://img.heshipeng.com/202204110030813.png?watermark/2/text/5YWz5rOo5b6u5L-h5YWs5LyX5Y-377ya6YCG5ZCR5LiA5q2l5q2l/font/5a6L5L2T/fontsize/300" alt="image-20220411003041687" title=""> </div> <div class="image-caption">image-20220411003041687</div> </figure><p>接着在console面板输入<code>hook.search(刚才复制下来的w的值)</code>,执行之后就可以看到w参数在浏览器中整个的运行情况:</p><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="https://img.heshipeng.com/202204110035276.png?watermark/2/text/5YWz5rOo5b6u5L-h5YWs5LyX5Y-377ya6YCG5ZCR5LiA5q2l5q2l/font/5a6L5L2T/fontsize/300" alt="image-20220411003509171" title=""> </div> <div class="image-caption">image-20220411003509171</div> </figure><p>可以看到w参数一共出现了2次,第一次是初始化,第二次是调用。我们随便点击一个的文件位置进去(比如点击第一个),然后打上断点:</p><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="https://img.heshipeng.com/202204110036242.png?watermark/2/text/5YWz5rOo5b6u5L-h5YWs5LyX5Y-377ya6YCG5ZCR5LiA5q2l5q2l/font/5a6L5L2T/fontsize/300" alt="image-20220411003649162" title=""> </div> <div class="image-caption">image-20220411003649162</div> </figure><p>刷新页面,再次进行验证码的验证,可以看到成功断上了,并且debugger面板上也可以看到我们跟的值是w这个参数:</p><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="https://img.heshipeng.com/202204110039808.png?watermark/2/text/5YWz5rOo5b6u5L-h5YWs5LyX5Y-377ya6YCG5ZCR5LiA5q2l5q2l/font/5a6L5L2T/fontsize/300" alt="image-20220411003930689" title=""> </div> <div class="image-caption">image-20220411003930689</div> </figure><h4 id="编程猫插件下载地址"><a href="#编程猫插件下载地址" class="headerlink" title="编程猫插件下载地址"></a>编程猫插件下载地址</h4><p>扫码关注微信公众号——逆向一步步,然后公众号内回复“FD编程猫插件”,就可以获得插件下载地址。</p><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="https://img.heshipeng.com/202202231720872.jpg" alt="qrcode_for_gh_509fdefd3c81_258" title=""> </div> <div class="image-caption">qrcode_for_gh_509fdefd3c81_258</div> </figure>]]></content>
<summary type="html"><h4 id="安装FD,配置编程猫插件"><a href="#安装FD,配置编程猫插件" class="headerlink" title="安装FD,配置编程猫插件"></a>安装FD,配置编程猫插件</h4><p>首先需要安装Fiddler,要求版本&gt;=4.6.3。建</summary>
<category term="JS逆向" scheme="http://example.com/categories/JS%E9%80%86%E5%90%91/"/>
<category term="JS" scheme="http://example.com/tags/JS/"/>
<category term="逆向" scheme="http://example.com/tags/%E9%80%86%E5%90%91/"/>
</entry>
<entry>
<title>JS逆向案例——问财网补环境</title>
<link href="http://example.com/JS%E9%80%86%E5%90%91%E6%A1%88%E4%BE%8B%E2%80%94%E2%80%94%E9%97%AE%E8%B4%A2%E7%BD%91%E8%A1%A5%E7%8E%AF%E5%A2%83/"/>
<id>http://example.com/JS%E9%80%86%E5%90%91%E6%A1%88%E4%BE%8B%E2%80%94%E2%80%94%E9%97%AE%E8%B4%A2%E7%BD%91%E8%A1%A5%E7%8E%AF%E5%A2%83/</id>
<published>2022-04-10T04:43:49.000Z</published>
<updated>2022-04-14T17:02:28.328Z</updated>
<content type="html"><![CDATA[<blockquote><p>免责声明:<strong>本文章中所有内容仅供学习交流,抓包内容、敏感网址、数据接口均已做脱敏处理,严禁用于商业用途和非法用途,否则由此产生的一切后果均与作者无关,若有侵权,请联系我立即删除!</strong></p></blockquote><h4 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h4><p>本篇文章通过一个案例介绍JS逆向过程中抠JS之后的补环境工作。</p><h4 id="逆向过程"><a href="#逆向过程" class="headerlink" title="逆向过程"></a>逆向过程</h4><p>目标网址:aHR0cDovL3d3dy5pd2VuY2FpLmNvbS91bmlmaWVkd2FwL3Jlc3VsdD93PSVFNyVCQiVCRiVFOCU4OSVCMiVFNyU5NCVCNSVFNSU4QSU5QiVFNiVBNiU4MiVFNSVCRiVCNSZxdWVyeXR5cGU9c3RvY2s=</p><p>逆向目标:cookie中的v值</p><h5 id="Hook-Cookie"><a href="#Hook-Cookie" class="headerlink" title="Hook Cookie"></a>Hook Cookie</h5><p>对于处理Cookie种某一个键值对生成这类问题第一反应应该是想到采用Hook的方式。这里介绍2种Hook的方式,一种是通过FD编程猫插件,一种是通过油猴插件。</p><ol><li>使用油猴插件</li></ol><p>油猴插件的使用参照:<a href="https://blog.heshipeng.com/JS%E9%80%86%E5%90%91%E4%B9%8BTampermonkey%E5%B7%A5%E5%85%B7%E7%AF%87/">JS逆向之Tampermonkey工具篇</a></p><p>插件内容为:</p><figure class="highlight js"><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="comment">// ==UserScript==</span></span><br><span class="line"><span class="comment">// @name hook iwencai</span></span><br><span class="line"><span class="comment">// @namespace http://tampermonkey.net/</span></span><br><span class="line"><span class="comment">// @version 0.1</span></span><br><span class="line"><span class="comment">// @description try to take over the world!</span></span><br><span class="line"><span class="comment">// @author You</span></span><br><span class="line"><span class="comment">// @include *</span></span><br><span class="line"><span class="comment">// @icon https://www.google.com/s2/favicons?sz=64&domain=aqistudy.cn</span></span><br><span class="line"><span class="comment">// @grant none</span></span><br><span class="line"><span class="comment">// ==/UserScript==</span></span><br><span class="line"></span><br><span class="line">(<span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>{</span><br><span class="line"><span class="meta"> 'use strict'</span>;</span><br><span class="line"> <span class="comment">//endebug = function(off, code) {};</span></span><br><span class="line"> <span class="keyword">var</span> cookie_cache = <span class="built_in">document</span>.cookie;</span><br><span class="line"> <span class="built_in">Object</span>.defineProperty(<span class="built_in">document</span>, <span class="string">'cookie'</span>, {</span><br><span class="line"> <span class="attr">get</span>: <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>{</span><br><span class="line"> <span class="built_in">console</span>.log(<span class="string">'Getting cookie'</span>);</span><br><span class="line"> <span class="keyword">return</span> cookie_cache;</span><br><span class="line"> },</span><br><span class="line"> <span class="attr">set</span>: <span class="function"><span class="keyword">function</span>(<span class="params">val</span>) </span>{</span><br><span class="line"> <span class="built_in">console</span>.log(<span class="string">'Setting cookie'</span>, val);</span><br><span class="line"> <span class="keyword">if</span> (val.indexOf(<span class="string">"v="</span>) != -<span class="number">1</span>) {</span><br><span class="line"> <span class="keyword">debugger</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">var</span> cookie = val.split(<span class="string">";"</span>)[<span class="number">0</span>];</span><br><span class="line"> <span class="keyword">var</span> ncookie = cookie.split(<span class="string">"="</span>);</span><br><span class="line"> <span class="keyword">var</span> flag = <span class="literal">false</span>;</span><br><span class="line"> <span class="keyword">var</span> cache = cookie_cache.split(<span class="string">"; "</span>);</span><br><span class="line"> cache = cache.map(<span class="function"><span class="keyword">function</span>(<span class="params">a</span>)</span>{</span><br><span class="line"> <span class="keyword">if</span> (a.split(<span class="string">"="</span>)[<span class="number">0</span>] === ncookie[<span class="number">0</span>]) {</span><br><span class="line"> flag = <span class="literal">true</span>;</span><br><span class="line"> <span class="keyword">return</span> cookie;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> a;</span><br><span class="line"> });</span><br><span class="line"> cookie_cache = cache.join(<span class="string">"; "</span>);</span><br><span class="line"> <span class="keyword">if</span> (!flag) {</span><br><span class="line"> cookie_cache += cookie + <span class="string">"; "</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="built_in">this</span>._value = val;</span><br><span class="line"> <span class="keyword">return</span> cookie_cache;</span><br><span class="line"> }</span><br><span class="line"> })</span><br><span class="line"> <span class="comment">// Your code here...</span></span><br><span class="line">})();</span><br></pre></td></tr></table></figure><ol><li>使用FD编程猫插件</li></ol><p>FD编程猫插件用法参照:<a href="https://blog.heshipeng.com/JS%E9%80%86%E5%90%91%E4%B9%8BFiddler%E7%BC%96%E7%A8%8B%E7%8C%AB%E6%8F%92%E4%BB%B6%E4%BD%BF%E7%94%A8/">JS逆向之Fiddler编程猫插件使用</a></p><figure class="highlight js"><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></pre></td><td class="code"><pre><span class="line"><span class="comment">//当前版本hook工具只支持Content-Type为html的自动hook</span></span><br><span class="line"><span class="comment">//下面是一个示例:这个示例演示了hook全局的cookie设置点</span></span><br><span class="line">(<span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>{</span><br><span class="line"> <span class="comment">//严谨模式 检查所有错误</span></span><br><span class="line"><span class="meta"> 'use strict'</span>;</span><br><span class="line"> <span class="comment">//document 为要hook的对象 这里是hook的cookie</span></span><br><span class="line"><span class="keyword">var</span> cookieTemp = <span class="string">""</span>;</span><br><span class="line"> <span class="built_in">Object</span>.defineProperty(<span class="built_in">document</span>, <span class="string">'cookie'</span>, {</span><br><span class="line"><span class="comment">//hook set方法也就是赋值的方法 </span></span><br><span class="line"><span class="attr">set</span>: <span class="function"><span class="keyword">function</span>(<span class="params">val</span>) </span>{</span><br><span class="line"> <span class="keyword">if</span> (val.indexOf(<span class="string">"v="</span>)) {</span><br><span class="line"> <span class="keyword">debugger</span>;</span><br><span class="line"> }</span><br><span class="line"><span class="comment">//这样就可以快速给下面这个代码行下断点</span></span><br><span class="line"><span class="comment">//从而快速定位设置cookie的代码</span></span><br><span class="line"><span class="built_in">console</span>.log(<span class="string">'Hook捕获到cookie设置->'</span>, val);</span><br><span class="line">cookieTemp = val;</span><br><span class="line"><span class="keyword">return</span> val;</span><br><span class="line">},</span><br><span class="line"><span class="comment">//hook get方法也就是取值的方法 </span></span><br><span class="line"><span class="attr">get</span>: <span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"><span class="keyword">return</span> cookieTemp;</span><br><span class="line">}</span><br><span class="line"> });</span><br><span class="line">})();</span><br></pre></td></tr></table></figure><h5 id="逆向分析"><a href="#逆向分析" class="headerlink" title="逆向分析"></a>逆向分析</h5><p>无论是采用哪一种方式,都可以成功断住,如下图:</p><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="https://img.heshipeng.com/202204122034420.png?watermark/2/text/5YWz5rOo5b6u5L-h5YWs5LyX5Y-377ya6YCG5ZCR5LiA5q2l5q2l/font/5a6L5L2T/fontsize/300" alt="image-20220412203406372" title=""> </div> <div class="image-caption">image-20220412203406372</div> </figure><p> 跟栈,进到o方法,发现第二个参数t就是需要的v的值。</p><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="https://img.heshipeng.com/202204122135603.png?watermark/2/text/5YWz5rOo5b6u5L-h5YWs5LyX5Y-377ya6YCG5ZCR5LiA5q2l5q2l/font/5a6L5L2T/fontsize/300" alt="image-20220412213516531" title=""> </div> <div class="image-caption">image-20220412213516531</div> </figure><p>继续跟栈,进到D方法,setCookie就是上边的o方法,第二个参数n就是上边的t也就是Cookie v的值。而且n是由rt.update生成的。</p><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="https://img.heshipeng.com/202204122136727.png?watermark/2/text/5YWz5rOo5b6u5L-h5YWs5LyX5Y-377ya6YCG5ZCR5LiA5q2l5q2l/font/5a6L5L2T/fontsize/300" alt="image-20220412213620698" title=""> </div> <div class="image-caption">image-20220412213620698</div> </figure><p>我们看下rt对象:</p><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="https://img.heshipeng.com/202204122144363.png?watermark/2/text/5YWz5rOo5b6u5L-h5YWs5LyX5Y-377ya6YCG5ZCR5LiA5q2l5q2l/font/5a6L5L2T/fontsize/300" alt="image-20220412214401314" title=""> </div> <div class="image-caption">image-20220412214401314</div> </figure><p>一个Init方法应该是对算法进行初始化,一个update方法则是生成v。</p><p>我们断进去update方法,update方法就是这个D方法,D方法又调用了O方法,我们跟进去。</p><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="https://img.heshipeng.com/202204131330275.png?watermark/2/text/5YWz5rOo5b6u5L-h5YWs5LyX5Y-377ya6YCG5ZCR5LiA5q2l5q2l/font/5a6L5L2T/fontsize/300" alt="image-20220413133045178" title=""> </div> <div class="image-caption">image-20220413133045178</div> </figure><p>我们单步执行到S.toBuffer();可以看到S是一个l对象,并且包含一个base_fields属性。如下图:</p><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="https://img.heshipeng.com/202204131335310.png?watermark/2/text/5YWz5rOo5b6u5L-h5YWs5LyX5Y-377ya6YCG5ZCR5LiA5q2l5q2l/font/5a6L5L2T/fontsize/300" alt="image-20220413133536272" title=""> </div> <div class="image-caption">image-20220413133536272</div> </figure><p>那我们点进去S.toBuffer看看能不能找到l对象的原型,qn返回了l也就是前面的S,我们住需要抠出qn就可以得到l了,注意的是qn本来就执行了,返回一个逗号表达式,逗号表达式的最后是l,所以生成l对象的正确写法是<code>new qn(xxx)</code>。</p><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="https://img.heshipeng.com/202204131523915.png?watermark/2/text/5YWz5rOo5b6u5L-h5YWs5LyX5Y-377ya6YCG5ZCR5LiA5q2l5q2l/font/5a6L5L2T/fontsize/300" alt="image-20220413152301870" title=""> </div> <div class="image-caption">image-20220413152301870</div> </figure><p>l对象在初始化的时候需要传一个r参数,我们全局搜索一下new qn看看是否能找到一个实例,看看这个r传的是啥。搜索结果如下:</p><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="https://img.heshipeng.com/202204131532338.png?watermark/2/text/5YWz5rOo5b6u5L-h5YWs5LyX5Y-377ya6YCG5ZCR5LiA5q2l5q2l/font/5a6L5L2T/fontsize/300" alt="image-20220413153204291" title=""> </div> <div class="image-caption">image-20220413153204291</div> </figure><p>我们断住这个地方,调试发现a固定为4,n固定为1,e固定为3,t固定为2。知道了如何抠出S,也知道了如果初始化S,下面我们进行检验。我们新建一个snippet然后拷贝全部的代码:</p><figure class="highlight js"><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> _qn;</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> TOKEN_SERVER_TIME = <span class="number">1649765993.630</span>;</span><br><span class="line">!<span class="function"><span class="keyword">function</span>(<span class="params">n, t</span>) </span>{</span><br><span class="line"> !<span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>{</span><br><span class="line"> <span class="keyword">var</span> r, e, a;</span><br><span class="line"> </span><br><span class="line"> <span class="comment">// 省略若干行</span></span><br><span class="line"> </span><br><span class="line"> <span class="keyword">var</span> qn = <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>{</span><br><span class="line"> <span class="keyword">var</span> n, t, r;</span><br><span class="line"> n = t = r = a;</span><br><span class="line"> <span class="keyword">var</span> e, o, i;</span><br><span class="line"> e = o = i = s;</span><br><span class="line"> <span class="keyword">var</span> u = o[<span class="number">15</span>]</span><br><span class="line"> , c = o[<span class="number">102</span>]</span><br><span class="line"> , f = e[<span class="number">103</span>];</span><br><span class="line"> <span class="function"><span class="keyword">function</span> <span class="title">l</span>(<span class="params">r</span>) </span>{</span><br><span class="line"> <span class="keyword">var</span> a = o[<span class="number">102</span>]</span><br><span class="line"> , i = e[<span class="number">103</span>];</span><br><span class="line"> <span class="built_in">this</span>[n[<span class="number">76</span>]] = r;</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">var</span> u = t[<span class="number">52</span>], c = r[a + g + i]; u < c; u++)</span><br><span class="line"> <span class="built_in">this</span>[u] = t[<span class="number">52</span>]</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> l[e[<span class="number">104</span>]][w + m + I + u] = <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>{</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">var</span> a = e[<span class="number">105</span>], u = <span class="built_in">this</span>[a + y], c = [], s = -e[<span class="number">0</span>], v = o[<span class="number">2</span>], f = u[r[<span class="number">56</span>]]; v < f; v++)</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">var</span> l = <span class="built_in">this</span>[v], p = u[v], d = s += p; c[d] = l & <span class="built_in">parseInt</span>(t[<span class="number">77</span>], n[<span class="number">78</span>]),</span><br><span class="line"> --p != r[<span class="number">52</span>]; )</span><br><span class="line"> --d,</span><br><span class="line"> l >>= <span class="built_in">parseInt</span>(n[<span class="number">79</span>], i[<span class="number">106</span>]);</span><br><span class="line"> <span class="keyword">return</span> c</span><br><span class="line"> }</span><br><span class="line"> ,</span><br><span class="line"> l[v(t[<span class="number">80</span>], t[<span class="number">81</span>], b)][ot(i[<span class="number">107</span>])] = <span class="function"><span class="keyword">function</span>(<span class="params">n</span>) </span>{</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">var</span> r = e[<span class="number">8</span>], a = <span class="built_in">this</span>[ot(e[<span class="number">108</span>], e[<span class="number">109</span>])], o = t[<span class="number">52</span>], u = e[<span class="number">2</span>], s = a[c + r + f]; u < s; u++) {</span><br><span class="line"> <span class="keyword">var</span> v = a[u]</span><br><span class="line"> , l = i[<span class="number">2</span>];</span><br><span class="line"> <span class="keyword">do</span> {</span><br><span class="line"> l = (l << t[<span class="number">82</span>]) + n[o++]</span><br><span class="line"> } <span class="keyword">while</span> (--v > t[<span class="number">52</span>]);</span><br><span class="line"> <span class="built_in">this</span>[u] = l >>> i[<span class="number">2</span>]</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> ,</span><br><span class="line"> l</span><br><span class="line"> }(), zn;</span><br><span class="line"> _qn = qn; </span><br><span class="line"> </span><br><span class="line"> <span class="comment">// 省略若干行</span></span><br><span class="line"> }()</span><br><span class="line">}([<span class="string">""</span>, <span class="number">9527</span>, <span class="comment">/* 省略若干行 */</span> <span class="string">"V587"</span>]);</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>定一个全局变量_qn,导出qn。运行文件,没有报错,然后我们生成一个S对象:</p><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="https://img.heshipeng.com/202204131543170.png?watermark/2/text/5YWz5rOo5b6u5L-h5YWs5LyX5Y-377ya6YCG5ZCR5LiA5q2l5q2l/font/5a6L5L2T/fontsize/300" alt="image-20220413154327048" title=""> </div> <div class="image-caption">image-20220413154327048</div> </figure><p>这里我们就成功的生成了S,并且可以看到decodeBuffer方法和mm.toBuffer方法也都有。别忘了我们的最终目标是得到Cookie中的v,我们回到前面的O方法,看初始化并得到S之后,后续生成v的逻辑。我们看到<code>Jn.serverTimeNow()</code>这一行代码,发现每次取出来的都是一个定值。</p><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="https://img.heshipeng.com/202204131559743.png?watermark/2/text/5YWz5rOo5b6u5L-h5YWs5LyX5Y-377ya6YCG5ZCR5LiA5q2l5q2l/font/5a6L5L2T/fontsize/300" alt="image-20220413155910667" title=""> </div> <div class="image-caption">image-20220413155910667</div> </figure><p>其实这个获取的就是该JS文件最前面定义的TOKEN_SERVER_TIME,这个值可能会随着JS文件更新而发生变化,所以我们采取局部扣JS的方法可能会很麻烦,因为可能需要定期更新TOKEN_SERVER_TIME的这个值。那只能全扣了啊,问题是抠下来全部的JS,放在本地执行之后,去哪里拿这个Cookie呢?答案是从document中拿,但是本地没有document呀,所以接下来就是补环境了。</p><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="https://img.heshipeng.com/202204131613967.png?watermark/2/text/5YWz5rOo5b6u5L-h5YWs5LyX5Y-377ya6YCG5ZCR5LiA5q2l5q2l/font/5a6L5L2T/fontsize/300" alt="image-20220413161327925" title=""> </div> <div class="image-caption">image-20220413161327925</div> </figure><h5 id="补环境"><a href="#补环境" class="headerlink" title="补环境"></a>补环境</h5><p>按照<a href="https://blog.heshipeng.com/JS%E9%80%86%E5%90%91%E4%B9%8Bvscode%E6%97%A0%E7%8E%AF%E5%A2%83%E8%81%94%E8%B0%83/">JS逆向之vscode无环境联调</a>介绍的,把整个JS代码粘贴到VS Code中,然后开启DevTools,运行之后报document不存在:</p><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="https://img.heshipeng.com/202204131640444.png" alt="image-20220413164041346" title=""> </div> <div class="image-caption">image-20220413164041346</div> </figure><blockquote><p><strong>像这种拷贝整个JS然后补环境,需要补头补尾,中间的整个JS文件不能动,这样做的好处是中间的文件可以用一个占位符表示,以后每次JS更新了,只需要更新这个占位符的内容,这样更加通用,维护起来更加容易</strong></p></blockquote><p>我们补好window和document之后,再次运行,接着报错:</p><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="https://img.heshipeng.com/202204131657365.png?watermark/2/text/5YWz5rOo5b6u5L-h5YWs5LyX5Y-377ya6YCG5ZCR5LiA5q2l5q2l/font/5a6L5L2T/fontsize/300" alt="image-20220413165734237" title=""> </div> <div class="image-caption">image-20220413165734237</div> </figure><p>这里r[51]是document,报错的一句r[51].getElementsByTagName(p + d)[r[52]]实际上是document.getElementsByTagName(‘head’)[0];继续补代码如下:</p><figure class="highlight js"><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">var</span> <span class="built_in">window</span> = <span class="built_in">this</span>;</span><br><span class="line"><span class="keyword">var</span> Document = <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>{};</span><br><span class="line">Document.prototype.getElementsByTagName = <span class="function"><span class="keyword">function</span>(<span class="params">x</span>) </span>{</span><br><span class="line"> <span class="keyword">if</span> (x == <span class="string">'head'</span>) {</span><br><span class="line"> <span class="keyword">return</span> [{}]</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> [{}]</span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> <span class="built_in">document</span> = <span class="keyword">new</span> Document();</span><br></pre></td></tr></table></figure><p>解释一下为什么这么补,补方法的时候关注3点,一是参数个数,二是返回值类型,三是对实际传入的参数进行特殊处理。因为getElementsByTagName只接受一个参数,所以只需要定义一个形参x。</p><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="https://img.heshipeng.com/202204131853457.png?watermark/2/text/5YWz5rOo5b6u5L-h5YWs5LyX5Y-377ya6YCG5ZCR5LiA5q2l5q2l/font/5a6L5L2T/fontsize/300" alt="image-20220413185332326" title=""> </div> <div class="image-caption">image-20220413185332326</div> </figure><p>通过在原网站上调试知传入的实际参数为字符串head,且返回的是一个对象数组。所以上边也对head进行了特殊处理,而且方法的返回值是对象数组。</p><p>补好getElementsByTagName方法后,接着运行,接着报错。</p><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="https://img.heshipeng.com/202204132028023.png?watermark/2/text/5YWz5rOo5b6u5L-h5YWs5LyX5Y-377ya6YCG5ZCR5LiA5q2l5q2l/font/5a6L5L2T/fontsize/300" alt="image-20220413202853960" title=""> </div> <div class="image-caption">image-20220413202853960</div> </figure><p>接着补createElement:</p><figure class="highlight js"><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">Document.prototype.createElement = <span class="function"><span class="keyword">function</span>(<span class="params">x</span>) </span>{</span><br><span class="line"> <span class="keyword">if</span> (x == <span class="string">"div"</span>) {</span><br><span class="line"> <span class="keyword">return</span> <span class="function"><span class="keyword">function</span> <span class="title">onwheel</span>(<span class="params"></span>) </span>{};</span><br><span class="line"> }</span><br><span class="line">};</span><br></pre></td></tr></table></figure><p>为什么这么补?我们看下面一张图,作说明:</p><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="https://img.heshipeng.com/202204132038930.png?watermark/2/text/5YWz5rOo5b6u5L-h5YWs5LyX5Y-377ya6YCG5ZCR5LiA5q2l5q2l/font/5a6L5L2T/fontsize/300" alt="image-20220413203850845" title=""> </div> <div class="image-caption">image-20220413203850845</div> </figure><p>首先很显然createElement要补到Document对象下,然后s[171]为div,并且调用createElement后要返回一个onwheel方法。</p><p>补好之后,运行接着报错,如图:</p><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="https://img.heshipeng.com/202204132042064.png?watermark/2/text/5YWz5rOo5b6u5L-h5YWs5LyX5Y-377ya6YCG5ZCR5LiA5q2l5q2l/font/5a6L5L2T/fontsize/300" alt="image-20220413204248014" title=""> </div> <div class="image-caption">image-20220413204248014</div> </figure><p>n.attachElement没有定义,补一下:</p><figure class="highlight js"><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">Document.prototype.attachEvent = <span class="function"><span class="keyword">function</span>(<span class="params">x, y</span>) </span>{</span><br><span class="line"></span><br><span class="line">};</span><br></pre></td></tr></table></figure><p>补好之后,依旧报错,嗯!逆向分析就是需要耐心:</p><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="https://img.heshipeng.com/202204132121104.png?watermark/2/text/5YWz5rOo5b6u5L-h5YWs5LyX5Y-377ya6YCG5ZCR5LiA5q2l5q2l/font/5a6L5L2T/fontsize/300" alt="image-20220413212157946" title=""> </div> <div class="image-caption">image-20220413212157946</div> </figure><p>补navigator,可以看到navigator.plugins是一个对象数组,我们这里补一个空就行。</p><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="https://img.heshipeng.com/202204132136268.png?watermark/2/text/5YWz5rOo5b6u5L-h5YWs5LyX5Y-377ya6YCG5ZCR5LiA5q2l5q2l/font/5a6L5L2T/fontsize/300" alt="image-20220413213604130" title=""> </div> <div class="image-caption">image-20220413213604130</div> </figure><figure class="highlight js"><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">Navigator = <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>{};</span><br><span class="line">Navigator.prototype.plugins = [];</span><br><span class="line">navigator = <span class="keyword">new</span> Navigator();</span><br></pre></td></tr></table></figure><p>补好之后,运行接着报错:</p><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="https://img.heshipeng.com/202204132144716.png?watermark/2/text/5YWz5rOo5b6u5L-h5YWs5LyX5Y-377ya6YCG5ZCR5LiA5q2l5q2l/font/5a6L5L2T/fontsize/300" alt="image-20220413214446556" title=""> </div> <div class="image-caption">image-20220413214446556</div> </figure><p>我们看看这个几个变量是什么,如图:</p><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="https://img.heshipeng.com/202204132145038.png?watermark/2/text/5YWz5rOo5b6u5L-h5YWs5LyX5Y-377ya6YCG5ZCR5LiA5q2l5q2l/font/5a6L5L2T/fontsize/300" alt="image-20220413214519986" title=""> </div> <div class="image-caption">image-20220413214519986</div> </figure><p>我们找一下l的声明处,如图:</p><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="https://img.heshipeng.com/202204142359764.png?watermark/2/text/5YWz5rOo5b6u5L-h5YWs5LyX5Y-377ya6YCG5ZCR5LiA5q2l5q2l/font/5a6L5L2T/fontsize/300" alt="image-20220414235906611" title=""> </div> <div class="image-caption">image-20220414235906611</div> </figure><p>可以看到l是取自window中的document,所以我们把document作为属性放到window对象下:</p><figure class="highlight js"><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="built_in">document</span> = <span class="keyword">new</span> Document();</span><br><span class="line"><span class="keyword">var</span> <span class="built_in">window</span> = {<span class="string">"document"</span>: <span class="built_in">document</span>};</span><br></pre></td></tr></table></figure><p>补好之后,再次运行,再次报错:</p><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="https://img.heshipeng.com/202204150009814.png?watermark/2/text/5YWz5rOo5b6u5L-h5YWs5LyX5Y-377ya6YCG5ZCR5LiA5q2l5q2l/font/5a6L5L2T/fontsize/300" alt="image-20220415000927626" title=""> </div> <div class="image-caption">image-20220415000927626</div> </figure><p>这里就很奇怪了,因为原始网站这里应该是直接进去上边的if分支,而不是进到这里的else if判断。我们跟进去这个方法m,看看这个m是什么?</p><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="https://img.heshipeng.com/202204150011590.png?watermark/2/text/5YWz5rOo5b6u5L-h5YWs5LyX5Y-377ya6YCG5ZCR5LiA5q2l5q2l/font/5a6L5L2T/fontsize/300" alt="image-20220415001131494" title=""> </div> <div class="image-caption">image-20220415001131494</div> </figure><p>可以看到o是localStorage,s[83]是window对象,这里的意思是判断window对象下是否存在localStorage,并判断这个属性是否为空。我们自己定义一个localStorage给到window。</p><figure class="highlight js"><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">LocalStorage = <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>{}</span><br><span class="line"><span class="built_in">localStorage</span> = <span class="keyword">new</span> LocalStorage();</span><br><span class="line"><span class="built_in">window</span> = {<span class="string">"document"</span>: <span class="built_in">document</span>, <span class="string">"localStorage"</span>: <span class="built_in">localStorage</span>};</span><br></pre></td></tr></table></figure><p>补好之后运行,接着报错:</p><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="https://img.heshipeng.com/202204150019132.png?watermark/2/text/5YWz5rOo5b6u5L-h5YWs5LyX5Y-377ya6YCG5ZCR5LiA5q2l5q2l/font/5a6L5L2T/fontsize/300" alt="image-20220415001954038" title=""> </div> <div class="image-caption">image-20220415001954038</div> </figure><p>这里的f是localStorage,缺少getItem,我们补一下:</p><figure class="highlight js"><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">LocalStorage.prototype.getItem = <span class="function"><span class="keyword">function</span>(<span class="params">x</span>) </span>{</span><br><span class="line"><span class="keyword">if</span> (x == <span class="string">"hexin-v"</span>) {</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">null</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">null</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>我们通过原网站,看到该方法接受一个参数,并且只调用了一次,传的参数是hexin-v,返回null,我们照着补就行。</p><p>补好之后,接着运行:</p><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="https://img.heshipeng.com/202204150024912.png?watermark/2/text/5YWz5rOo5b6u5L-h5YWs5LyX5Y-377ya6YCG5ZCR5LiA5q2l5q2l/font/5a6L5L2T/fontsize/300" alt="image-20220415002404829" title=""> </div> <div class="image-caption">image-20220415002404829</div> </figure><p>通过调用栈我们看看这个n是啥?n实际是上层函数传入的参数,即navigator.userAgent,原始网站有值,我们没有定义。</p><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="https://img.heshipeng.com/202204150032994.png?watermark/2/text/5YWz5rOo5b6u5L-h5YWs5LyX5Y-377ya6YCG5ZCR5LiA5q2l5q2l/font/5a6L5L2T/fontsize/300" alt="image-20220415003226879" title=""> </div> <div class="image-caption">image-20220415003226879</div> </figure><p>我们补上userAgent:</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">Navigator.prototype.userAgent = <span class="string">"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.4896.75 Safari/537.36"</span>;</span><br></pre></td></tr></table></figure><p>补好之后,运行报错,如下图:</p><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="https://img.heshipeng.com/202204150034401.png?watermark/2/text/5YWz5rOo5b6u5L-h5YWs5LyX5Y-377ya6YCG5ZCR5LiA5q2l5q2l/font/5a6L5L2T/fontsize/300" alt="image-20220415003446318" title=""> </div> <div class="image-caption">image-20220415003446318</div> </figure><p>通过原始网站知,javaEnabled方法返回false,那我们也给navigator对象加一个方法javaEnabled返回false即可:</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">Navigator.prototype.javaEnabled = <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>{ <span class="keyword">return</span> <span class="literal">false</span> };</span><br></pre></td></tr></table></figure><p>补好之后运行报错:</p><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="https://img.heshipeng.com/202204150037277.png?watermark/2/text/5YWz5rOo5b6u5L-h5YWs5LyX5Y-377ya6YCG5ZCR5LiA5q2l5q2l/font/5a6L5L2T/fontsize/300" alt="image-20220415003756208" title=""> </div> <div class="image-caption">image-20220415003756208</div> </figure><p>这里a[65]是window对象,a[175]是navigator对象,上面报错是说window.navigator为undefine,我们把navigator作为window的属性即可,修改上面补环境的代码:</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> <span class="built_in">window</span> = {<span class="string">"document"</span>: <span class="built_in">document</span>, <span class="string">"localStorage"</span>: <span class="built_in">localStorage</span>, <span class="string">"navigator"</span>: navigator};</span><br></pre></td></tr></table></figure><p>改好之后运行:</p><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="https://img.heshipeng.com/202204150040672.png?watermark/2/text/5YWz5rOo5b6u5L-h5YWs5LyX5Y-377ya6YCG5ZCR5LiA5q2l5q2l/font/5a6L5L2T/fontsize/300" alt="image-20220415004026612" title=""> </div> <div class="image-caption">image-20220415004026612</div> </figure><p>提示location未定义,我们定义一个location对象:</p><figure class="highlight js"><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">Location = <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>{}</span><br><span class="line">location = <span class="keyword">new</span> Location();</span><br></pre></td></tr></table></figure><p>接着运行,依然报错:</p><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="https://img.heshipeng.com/202204150042850.png?watermark/2/text/5YWz5rOo5b6u5L-h5YWs5LyX5Y-377ya6YCG5ZCR5LiA5q2l5q2l/font/5a6L5L2T/fontsize/300" alt="image-20220415004230766" title=""> </div> <div class="image-caption">image-20220415004230766</div> </figure><p>这里的c[140]是href,location.href为空,我们查看原始网站知,location.href就是当前的页面地址,我们补上即可:</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">Location.prototype.href = <span class="string">"http://www.iwencai.com/unifiedwap/result?w=%E7%BB%BF%E8%89%B2%E7%94%B5%E5%8A%9B%E6%A6%82%E5%BF%B5&querytype=stock"</span>;</span><br></pre></td></tr></table></figure><p>接着运行:</p><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="https://img.heshipeng.com/202204150045114.png?watermark/2/text/5YWz5rOo5b6u5L-h5YWs5LyX5Y-377ya6YCG5ZCR5LiA5q2l5q2l/font/5a6L5L2T/fontsize/300" alt="image-20220415004504041" title=""> </div> <div class="image-caption">image-20220415004504041</div> </figure><p>提示location.hostname不存在,我们根据原网站取到location.hostname的值补上:</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">Location.prototype.hostname = <span class="string">"www.iwencai.com"</span>;</span><br></pre></td></tr></table></figure><p>补好运行,报错:</p><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="https://img.heshipeng.com/202204150048255.png?watermark/2/text/5YWz5rOo5b6u5L-h5YWs5LyX5Y-377ya6YCG5ZCR5LiA5q2l5q2l/font/5a6L5L2T/fontsize/300" alt="image-20220415004845137" title=""> </div> <div class="image-caption">image-20220415004845137</div> </figure><p>提示localStorage的setItem方法不存在,得嘞,补一个空方法:</p><figure class="highlight js"><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">LocalStorage.prototype.setItem = <span class="function"><span class="keyword">function</span>(<span class="params">x, y</span>) </span>{</span><br><span class="line"></span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>补好之后运行,没有报错!!!</p><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="https://img.heshipeng.com/202204150050421.gif" alt="此处应该有掌声表情包- 搜狗图片搜索" title=""> </div> <div class="image-caption">此处应该有掌声表情包- 搜狗图片搜索</div> </figure><p>我们取一下cookie的值,也成功看到了v:</p><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="https://img.heshipeng.com/202204150052689.png?watermark/2/text/5YWz5rOo5b6u5L-h5YWs5LyX5Y-377ya6YCG5ZCR5LiA5q2l5q2l/font/5a6L5L2T/fontsize/300" alt="image-20220415005229606" title=""> </div> <div class="image-caption">image-20220415005229606</div> </figure><p>上面补环境的代码比较零散,这里统一整理如下:</p><figure class="highlight js"><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></pre></td><td class="code"><pre><span class="line">Document = <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>{};</span><br><span class="line">Document.prototype.getElementsByTagName = <span class="function"><span class="keyword">function</span>(<span class="params">x</span>) </span>{</span><br><span class="line"> <span class="keyword">if</span> (x == <span class="string">'head'</span>) {</span><br><span class="line"> <span class="keyword">return</span> [{}]</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> [{}]</span><br><span class="line">};</span><br><span class="line">Document.prototype.createElement = <span class="function"><span class="keyword">function</span>(<span class="params">x</span>) </span>{</span><br><span class="line"> <span class="keyword">if</span> (x == <span class="string">"div"</span>) {</span><br><span class="line"> <span class="keyword">return</span> <span class="function"><span class="keyword">function</span> <span class="title">onwheel</span>(<span class="params"></span>) </span>{};</span><br><span class="line"> } <span class="keyword">else</span> <span class="keyword">if</span> (x == <span class="string">"canvas"</span>) {</span><br><span class="line"> <span class="keyword">return</span> {<span class="attr">getContext</span>: <span class="function"><span class="keyword">function</span>(<span class="params">y</span>) </span>{</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">undefined</span>;</span><br><span class="line"> }}</span><br><span class="line"> }</span><br><span class="line">};</span><br><span class="line">Document.prototype.attachEvent = <span class="function"><span class="keyword">function</span>(<span class="params">x, y</span>) </span>{};</span><br><span class="line">Document.prototype.documentElement = {</span><br><span class="line"> <span class="attr">addBehavior</span>: <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>{}</span><br><span class="line">};</span><br><span class="line"><span class="built_in">document</span> = <span class="keyword">new</span> Document();</span><br><span class="line"></span><br><span class="line">Navigator = <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>{};</span><br><span class="line">Navigator.prototype.javaEnabled = <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>{ <span class="keyword">return</span> <span class="literal">false</span> };</span><br><span class="line">Navigator.prototype.plugins = [];</span><br><span class="line">Navigator.prototype.userAgent = <span class="string">"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.4896.75 Safari/537.36"</span>;</span><br><span class="line">navigator = <span class="keyword">new</span> Navigator();</span><br><span class="line"></span><br><span class="line">Location = <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>{};</span><br><span class="line">Location.prototype.href = <span class="string">"http://www.iwencai.com/unifiedwap/result?w=%E7%BB%BF%E8%89%B2%E7%94%B5%E5%8A%9B%E6%A6%82%E5%BF%B5&querytype=stock"</span>;</span><br><span class="line">Location.prototype.hostname = <span class="string">"www.iwencai.com"</span>;</span><br><span class="line">location = <span class="keyword">new</span> Location();</span><br><span class="line"></span><br><span class="line">LocalStorage = <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>{}</span><br><span class="line">LocalStorage.prototype.getItem = <span class="function"><span class="keyword">function</span>(<span class="params">x</span>) </span>{</span><br><span class="line"><span class="keyword">if</span> (x == <span class="string">"hexin-v"</span>) {</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">null</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">null</span>;</span><br><span class="line">}</span><br><span class="line">LocalStorage.prototype.setItem = <span class="function"><span class="keyword">function</span>(<span class="params">x, y</span>) </span>{</span><br><span class="line"></span><br><span class="line">}</span><br><span class="line"><span class="built_in">localStorage</span> = <span class="keyword">new</span> LocalStorage();</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> <span class="built_in">window</span> = {<span class="string">"document"</span>: <span class="built_in">document</span>, <span class="string">"localStorage"</span>: <span class="built_in">localStorage</span>, <span class="string">"navigator"</span>: navigator};</span><br><span class="line"></span><br></pre></td></tr></table></figure><h4 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h4><p>本文通过一个例子,介绍了手动补环境的过程,总结如下:拷贝整个JS然后补环境,需要补头补尾,原则上中间的整个JS文件一点也不能动,这样做的好处是中间的文件可以用一个占位符表示,以后每次JS更新了,只需要更新这个占位符的内容,这样更加通用,维护起来更加容易。</p><h4 id="关于代码的获取"><a href="#关于代码的获取" class="headerlink" title="关于代码的获取"></a>关于代码的获取</h4><p>扫码关注微信号——逆向一步步,公众号内回复关键词<code>05</code>就可以获取本案例的全部代码。</p><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="https://img.heshipeng.com/202204150057282.jpg" alt="qrcode_for_gh_509fdefd3c81_258" title=""> </div> <div class="image-caption">qrcode_for_gh_509fdefd3c81_258</div> </figure>]]></content>
<summary type="html"><blockquote>
<p>免责声明:<strong>本文章中所有内容仅供学习交流,抓包内容、敏感网址、数据接口均已做脱敏处理,严禁用于商业用途和非法用途,否则由此产生的一切后果均与作者无关,若有侵权,请联系我立即删除!</strong></p>
</blockquote>
</summary>
<category term="JS逆向" scheme="http://example.com/categories/JS%E9%80%86%E5%90%91/"/>
<category term="JS" scheme="http://example.com/tags/JS/"/>
<category term="逆向" scheme="http://example.com/tags/%E9%80%86%E5%90%91/"/>
</entry>
<entry>
<title>JS逆向之vscode无环境联调</title>
<link href="http://example.com/JS%E9%80%86%E5%90%91%E4%B9%8Bvscode%E6%97%A0%E7%8E%AF%E5%A2%83%E8%81%94%E8%B0%83/"/>
<id>http://example.com/JS%E9%80%86%E5%90%91%E4%B9%8Bvscode%E6%97%A0%E7%8E%AF%E5%A2%83%E8%81%94%E8%B0%83/</id>
<published>2022-04-09T14:12:32.000Z</published>
<updated>2022-04-18T09:23:30.908Z</updated>
<content type="html"><![CDATA[<h5 id="为啥使用VSCode"><a href="#为啥使用VSCode" class="headerlink" title="为啥使用VSCode"></a>为啥使用VSCode</h5><p>VSCode强大,支持多种语言。在JS逆向项目中,会涉及到Python和JS代码,如果Python使用Pycharm调试,JS使用WebStorm或者Hbuilder进行调试,那么需要在多个开发者工具中切换,比较麻烦,VSCode的多语言支持完美的解决这个麻烦事。此外,最重要的是VSCode可以配置JS代码的无环境联调,即可以与浏览器的dev-tools工具交互实现在VSCode中开发,浏览器工具中调试,这也是其它工具没有的。</p><h5 id="安装VSCode"><a href="#安装VSCode" class="headerlink" title="安装VSCode"></a>安装VSCode</h5><p>VSCode下载地址:<a href="https://code.visualstudio.com/">https://code.visualstudio.com/</a></p><p>安装好VSCode之后,可以配置语言为中文。具体方法是,点击下图的图标,选择Extension,然后在搜索中输入”Chinese”,选择简体中文,然后重启浏览器即可。</p><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="https://img.heshipeng.com/202204092230545.png" alt="image-20220409223010250" title=""> </div> <div class="image-caption">image-20220409223010250</div> </figure><h5 id="配置无环境联调"><a href="#配置无环境联调" class="headerlink" title="配置无环境联调"></a>配置无环境联调</h5><p>首先安装好NodeJS环境。</p><p>然后打开工作目录,选择任意一个JS文件,选择运行->启动调试就可以运行或者debug JS代码了。</p><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="https://img.heshipeng.com/202204092352976.png" alt="image-20220409235241659" title=""> </div> <div class="image-caption">image-20220409235241659</div> </figure><p>然后通过命令<code>npm install -g node-inspect</code>全局安装依赖包,如果是Linux/Mac系统,需要sudo权限。</p><p>输入命令<code>node-inspect</code>,查看node-inspect是否安装成功:</p><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="https://img.heshipeng.com/202204100006494.png" alt="image-20220410000634331" title=""> </div> <div class="image-caption">image-20220410000634331</div> </figure><p>我们选择运行->打开配置:</p><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="https://img.heshipeng.com/202204092354173.png" alt="image-20220409235420113" title=""> </div> <div class="image-caption">image-20220409235420113</div> </figure><p>出现一个新的文件launch.json文件,这个文件用来配置项目的运行环境,比如执行脚本,解释器,命令行参数等等。修改这个配置文件,内容如下:</p><figure class="highlight json"><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><br><span class="line"> <span class="comment">// 使用 IntelliSense 了解相关属性。 </span></span><br><span class="line"> <span class="comment">// 悬停以查看现有属性的描述。</span></span><br><span class="line"> <span class="comment">// 欲了解更多信息,请访问: https://go.microsoft.com/fwlink/?linkid=830387</span></span><br><span class="line"> <span class="attr">"version"</span>: <span class="string">"0.2.0"</span>,</span><br><span class="line"> <span class="attr">"configurations"</span>: [</span><br><span class="line"> {</span><br><span class="line"> <span class="attr">"type"</span>: <span class="string">"node"</span>,</span><br><span class="line"> <span class="attr">"request"</span>: <span class="string">"launch"</span>,</span><br><span class="line"> <span class="attr">"name"</span>: <span class="string">"启动程序"</span>,</span><br><span class="line"> <span class="attr">"skipFiles"</span>: [</span><br><span class="line"> <span class="string">"<node_internals>/**"</span></span><br><span class="line"> ],</span><br><span class="line"> <span class="attr">"program"</span>: <span class="string">"${workspaceFolder}/aerfaying.js"</span></span><br><span class="line"> },</span><br><span class="line"> {</span><br><span class="line"> <span class="attr">"type"</span>: <span class="string">"node"</span>,</span><br><span class="line"> <span class="attr">"request"</span>: <span class="string">"launch"</span>,</span><br><span class="line"> <span class="attr">"name"</span>: <span class="string">"无环境浏览器"</span>,</span><br><span class="line"> <span class="attr">"skipFiles"</span>: [</span><br><span class="line"> <span class="string">"<node_internals>/**"</span></span><br><span class="line"> ],</span><br><span class="line"> <span class="attr">"runtimeExecutable"</span>: <span class="string">"node-inspect"</span>,</span><br><span class="line"> <span class="attr">"program"</span>: <span class="string">"${workspaceFolder}/aerfaying.js"</span></span><br><span class="line"> },</span><br><span class="line"> ]</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>把之前的运行环境改了个名,叫做启动程序,然后新建了一个环境,叫做无环境浏览器,这个环境除了名字之外唯一不同的是增加了一个runtimeExecutable,把我们上边安装的node-inspect包给引入进来。</p><p>保存好配置文件之后,这个时候我们左上角就出现2个环境供我们选择了:</p><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="https://img.heshipeng.com/202204100014508.png" alt="image-20220410001442317" title=""> </div> <div class="image-caption">image-20220410001442317</div> </figure><p>我们点击无环境浏览器,就会启动无环境浏览器模式下的调试模式。打开任意一个Chrome浏览器窗口,打开开发者工具,在开发者工具的Elements面板左边出现一个绿色的图标,如下:</p><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="https://img.heshipeng.com/202204100017478.png?watermark/2/text/5YWz5rOo5b6u5L-h5YWs5LyX5Y-377ya6YCG5ZCR5LiA5q2l5q2l/font/5a6L5L2T/fontsize/300" alt="image-20220410001701254" title=""> </div> <div class="image-caption">image-20220410001701254</div> </figure><p>表示开启了VSCode与浏览器的联调模式,如果这个图标为灰色,表示关闭了联调模式。</p><p>点击这个图标,就弹出了DevTools:</p><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="https://img.heshipeng.com/202204100018481.png?watermark/2/text/5YWz5rOo5b6u5L-h5YWs5LyX5Y-377ya6YCG5ZCR5LiA5q2l5q2l/font/5a6L5L2T/fontsize/300" alt="image-20220410001856382" title=""> </div> <div class="image-caption">image-20220410001856382</div> </figure><p>可以看到已经帮我们把刚才调试的那个JS文件的内容自动导入,同时开启了debug模式。为什么叫做无环境浏览器?就是这个DevTools跟我们在浏览器中调试几乎是一模一样的,只不过一些浏览器的环境变量没有,比如window,document等等。这样既可以继续使用浏览器进行调试,又可以没有window/document等环境,模拟真实独立的开发环境,方便我们补环境。</p><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="https://img.heshipeng.com/202204100027373.png?watermark/2/text/5YWz5rOo5b6u5L-h5YWs5LyX5Y-377ya6YCG5ZCR5LiA5q2l5q2l/font/5a6L5L2T/fontsize/300" alt="image-20220410002734125" title=""> </div> <div class="image-caption">image-20220410002734125</div> </figure><h5 id="用VS-Code进行调试"><a href="#用VS-Code进行调试" class="headerlink" title="用VS Code进行调试"></a>用VS Code进行调试</h5><p>首先看下左下角的这2个选项,如下图:</p><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="https://img.heshipeng.com/202204181659615.png?watermark/2/text/5YWz5rOo5b6u5L-h5YWs5LyX5Y-377ya6YCG5ZCR5LiA5q2l5q2l/font/5a6L5L2T/fontsize/300" alt="image-20220418165953520" title=""> </div> <div class="image-caption">image-20220418165953520</div> </figure><p>Caught Exception指的是对于加了try catch的代码块,如果执行了catch逻辑(即代码抛了异常),依然在抛异常的地方断住。如下图:</p><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="https://img.heshipeng.com/202204181710387.png?watermark/2/text/5YWz5rOo5b6u5L-h5YWs5LyX5Y-377ya6YCG5ZCR5LiA5q2l5q2l/font/5a6L5L2T/fontsize/300" alt="image-20220418171006294" title=""> </div> <div class="image-caption">image-20220418171006294</div> </figure><p>Uncaught Exceptions指的是忽略try catch中的异常,在try 之外的代码块报错,才会断住。如图:</p><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="https://img.heshipeng.com/202204181714424.png?watermark/2/text/5YWz5rOo5b6u5L-h5YWs5LyX5Y-377ya6YCG5ZCR5LiA5q2l5q2l/font/5a6L5L2T/fontsize/300" alt="image-20220418171418283" title=""> </div> <div class="image-caption">image-20220418171418283</div> </figure><p>一般会勾选Uncaught Exceptions,因为try中的代码抛异常属于正常逻辑。如果这俩选项都没勾选,也没有自定义断点,程序就会直接运行完,并不会断住。如下图:</p><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="https://img.heshipeng.com/202204181716202.png?watermark/2/text/5YWz5rOo5b6u5L-h5YWs5LyX5Y-377ya6YCG5ZCR5LiA5q2l5q2l/font/5a6L5L2T/fontsize/300" alt="image-20220418171636116" title=""> </div> <div class="image-caption">image-20220418171636116</div> </figure><p>再说说调试用的的一组按钮,如下图:</p><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="https://img.heshipeng.com/202204181717469.png?watermark/2/text/5YWz5rOo5b6u5L-h5YWs5LyX5Y-377ya6YCG5ZCR5LiA5q2l5q2l/font/5a6L5L2T/fontsize/300" alt="image-20220418171746410" title=""> </div> <div class="image-caption">image-20220418171746410</div> </figure><p>第一个按钮,表示继续,从当前停顿的地方一直执行到下一个断点,如果没有下一个断点,则按照程序的正常执行顺序,顺序执行一步。</p><p>第二个按钮,表示单步执行,从当前位置开始,顺序执行一步,如果遇到函数调用,不进入函数内部顺序执行。</p><p>第三个按钮,表示单步执行,从当前位置开始,顺序执行一步,如果遇到函数调用,进入到函数内部单步执行。</p><p>第四个按钮,表示单步执行,如果当前调试是在函数内部单步执行,则跳过函数剩余的执行代码,回到函数调用处,往下执行一步。</p><p>第五个按钮,重启程序。</p><p>第六个按钮,停止。</p>]]></content>
<summary type="html"><h5 id="为啥使用VSCode"><a href="#为啥使用VSCode" class="headerlink" title="为啥使用VSCode"></a>为啥使用VSCode</h5><p>VSCode强大,支持多种语言。在JS逆向项目中,会涉及到Python和JS</summary>
<category term="JS逆向" scheme="http://example.com/categories/JS%E9%80%86%E5%90%91/"/>
<category term="JS" scheme="http://example.com/tags/JS/"/>
<category term="逆向" scheme="http://example.com/tags/%E9%80%86%E5%90%91/"/>
</entry>
<entry>
<title>JS逆向案例——阿尔法营webpack抠JS</title>
<link href="http://example.com/JS%E9%80%86%E5%90%91%E6%A1%88%E4%BE%8B%E2%80%94%E2%80%94%E9%98%BF%E5%B0%94%E6%B3%95%E8%90%A5webpack%E6%8A%A0JS/"/>
<id>http://example.com/JS%E9%80%86%E5%90%91%E6%A1%88%E4%BE%8B%E2%80%94%E2%80%94%E9%98%BF%E5%B0%94%E6%B3%95%E8%90%A5webpack%E6%8A%A0JS/</id>
<published>2022-04-06T12:35:48.000Z</published>
<updated>2022-04-06T08:23:35.746Z</updated>
<content type="html"><![CDATA[<blockquote><p>免责声明:<strong>本文章中所有内容仅供学习交流,抓包内容、敏感网址、数据接口均已做脱敏处理,严禁用于商业用途和非法用途,否则由此产生的一切后果均与作者无关,若有侵权,请联系我立即删除!</strong></p></blockquote><h4 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h4><p>webpack抠JS的一个案例,同样用来熟练如何从webpack打包的JS代码中抠关键的JS代码。</p><h4 id="抠JS过程"><a href="#抠JS过程" class="headerlink" title="抠JS过程"></a>抠JS过程</h4><p>网址:aHR0cHM6Ly9hZXJmYXlpbmcuY29tLw==</p><p>目标:登录接口的请求参数t和s分析。</p><p>老规矩,先抓包。</p><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="https://img.heshipeng.com/202204061540833.png?watermark/2/text/5YWz5rOo5b6u5L-h5YWs5LyX5Y-377ya6YCG5ZCR5LiA5q2l5q2l/font/5a6L5L2T/fontsize/300" alt="image-20220406154058629" title=""> </div> <div class="image-caption">image-20220406154058629</div> </figure><p>可以看到请求参数中username和password都是明文,t参数目测是一个时间戳,s是一串加密之后的密文。</p><p>从请求堆栈Initiator中一个个点进去,看看能否找到s生成的地方。</p><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="https://img.heshipeng.com/202204061545622.png?watermark/2/text/5YWz5rOo5b6u5L-h5YWs5LyX5Y-377ya6YCG5ZCR5LiA5q2l5q2l/font/5a6L5L2T/fontsize/300" alt="image-20220406154534518" title=""> </div> <div class="image-caption">image-20220406154534518</div> </figure><p>点击上面的文件,发现有个报错:</p><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="https://img.heshipeng.com/202204061546076.png?watermark/2/text/5YWz5rOo5b6u5L-h5YWs5LyX5Y-377ya6YCG5ZCR5LiA5q2l5q2l/font/5a6L5L2T/fontsize/300" alt="image-20220406154640011" title=""> </div> <div class="image-caption">image-20220406154640011</div> </figure><p>遇到这种<code>Could not load content for webpack:///</code>的错误,需要更改下浏览器的默认配置,Settings/Preferences/Sources,去掉Enable Javascript source maps的勾选。</p><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="https://img.heshipeng.com/202204061546397.png?watermark/2/text/5YWz5rOo5b6u5L-h5YWs5LyX5Y-377ya6YCG5ZCR5LiA5q2l5q2l/font/5a6L5L2T/fontsize/300" alt="image-20220406154623354" title=""> </div> <div class="image-caption">image-20220406154623354</div> </figure><p>修改配置之后,就可以看到相关JS文件的代码了。找到一块疑似加密的代码,如下:</p><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="https://img.heshipeng.com/202204061551050.png?watermark/2/text/5YWz5rOo5b6u5L-h5YWs5LyX5Y-377ya6YCG5ZCR5LiA5q2l5q2l/font/5a6L5L2T/fontsize/300" alt="image-20220406155132967" title=""> </div> <div class="image-caption">image-20220406155132967</div> </figure><p>window.Blockey.SecuritySalt,w和n都是固定的值。</p><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="https://img.heshipeng.com/202204061601020.png?watermark/2/text/5YWz5rOo5b6u5L-h5YWs5LyX5Y-377ya6YCG5ZCR5LiA5q2l5q2l/font/5a6L5L2T/fontsize/300" alt="image-20220406160129468" title=""> </div> <div class="image-caption">image-20220406160129468</div> </figure><p>根据上面生成s的代码,我们整理代码如下:</p><figure class="highlight js"><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="keyword">var</span> <span class="built_in">window</span> = <span class="built_in">global</span>;</span><br><span class="line"><span class="keyword">var</span> n = <span class="string">"/WebApi/Users/Login"</span>;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">get_sign</span>(<span class="params">username, password</span>) </span>{</span><br><span class="line"> <span class="keyword">let</span> x = <span class="string">"DUE$DEHFYE(YRUEHD*&"</span>;</span><br><span class="line"> <span class="keyword">let</span> w = <span class="string">"username="</span> + username + <span class="string">"&"</span> + <span class="string">"password="</span> + password;</span><br><span class="line"> <span class="keyword">return</span> c.default((n + <span class="string">"?"</span> + w + x));</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>然后我们的目标就很明确了,就是抠出c.default的函数,填入到我们的JS文件,我们浏览器中点进去c.default函数:</p><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="https://img.heshipeng.com/202204061607543.png?watermark/2/text/5YWz5rOo5b6u5L-h5YWs5LyX5Y-377ya6YCG5ZCR5LiA5q2l5q2l/font/5a6L5L2T/fontsize/300" alt="image-20220406160747460" title=""> </div> <div class="image-caption">image-20220406160747460</div> </figure><p>我们上边猜想的t是一个时间戳,不准确,可以看到t是一个时间戳加上了一个固定的数值。然后s参数是一个Sha1算法生成的密文。</p><p>我们继续点进去Sha1.hash函数:</p><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="https://img.heshipeng.com/202204061611573.png?watermark/2/text/5YWz5rOo5b6u5L-h5YWs5LyX5Y-377ya6YCG5ZCR5LiA5q2l5q2l/font/5a6L5L2T/fontsize/300" alt="image-20220406161134381" title=""> </div> <div class="image-caption">image-20220406161134381</div> </figure><p>然后将抠出来的JS函数一个个填充到JS文件中去,缺啥抠啥。具体详细过程不一步步展示了。最终整理的代码如下:</p><figure class="highlight js"><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="keyword">var</span> n = <span class="string">"/WebApi/Users/Login"</span>;</span><br><span class="line"><span class="keyword">var</span> c = {};</span><br><span class="line"><span class="keyword">var</span> Sha1 = {};</span><br><span class="line"></span><br><span class="line">Sha1.hash = <span class="function"><span class="keyword">function</span>(<span class="params">n, t</span>) </span>{</span><br><span class="line"> <span class="comment">// 代码较长,省略</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">Sha1.f = <span class="function"><span class="keyword">function</span>(<span class="params">n, t, i, r</span>) </span>{</span><br><span class="line"> <span class="keyword">switch</span> (n) {</span><br><span class="line"> <span class="keyword">case</span> <span class="number">0</span>:</span><br><span class="line"> <span class="keyword">return</span> t & i ^ ~t & r;</span><br><span class="line"> <span class="keyword">case</span> <span class="number">1</span>:</span><br><span class="line"> <span class="keyword">return</span> t ^ i ^ r;</span><br><span class="line"> <span class="keyword">case</span> <span class="number">2</span>:</span><br><span class="line"> <span class="keyword">return</span> t & i ^ t & r ^ i & r;</span><br><span class="line"> <span class="keyword">case</span> <span class="number">3</span>:</span><br><span class="line"> <span class="keyword">return</span> t ^ i ^ r</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">Sha1.ROTL = <span class="function"><span class="keyword">function</span>(<span class="params">n, t</span>) </span>{</span><br><span class="line"> <span class="keyword">return</span> n << t | n >>> <span class="number">32</span> - t</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">Sha1.utf8Encode = <span class="function"><span class="keyword">function</span>(<span class="params">n</span>) </span>{</span><br><span class="line"> <span class="keyword">return</span> <span class="built_in">unescape</span>(<span class="built_in">encodeURIComponent</span>(n))</span><br><span class="line">}</span><br><span class="line">;</span><br><span class="line"></span><br><span class="line">c.default = <span class="function"><span class="keyword">function</span>(<span class="params">e, t</span>) </span>{</span><br><span class="line"> <span class="keyword">var</span> n = (<span class="keyword">new</span> <span class="built_in">Date</span>).getTime() + <span class="number">2592e6</span> + (t || <span class="number">3e4</span>)</span><br><span class="line"> , r = (e || <span class="string">""</span>) + <span class="string">"&t="</span> + n;</span><br><span class="line"> <span class="keyword">return</span> {</span><br><span class="line"> <span class="attr">t</span>: n,</span><br><span class="line"> <span class="attr">s</span>: Sha1.hash(r)</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">get_sign</span>(<span class="params">username, password</span>) </span>{</span><br><span class="line"> <span class="keyword">let</span> x = <span class="string">"DUE$DEHFYE(YRUEHD*&"</span>;</span><br><span class="line"> <span class="keyword">let</span> w = <span class="string">"username="</span> + username + <span class="string">"&"</span> + <span class="string">"password="</span> + password;</span><br><span class="line"> <span class="keyword">return</span> c.default((n + <span class="string">"?"</span> + w + x));</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="built_in">console</span>.log(get_sign(<span class="string">"17777777777"</span>, <span class="string">"123456"</span>));</span><br></pre></td></tr></table></figure><p>输出结果,跟预期一致:</p><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="https://img.heshipeng.com/202204061618301.png?watermark/2/text/5YWz5rOo5b6u5L-h5YWs5LyX5Y-377ya6YCG5ZCR5LiA5q2l5q2l/font/5a6L5L2T/fontsize/300" alt="image-20220406161810100" title=""> </div> <div class="image-caption">image-20220406161810100</div> </figure><h4 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h4><p>这个案例比较简单,没有什么难度,提这个案例主要目的有2点,第一就是遇到按照webpack方式组织的JS代码,不一定非得按照前面介绍的分五步走,先找模块加载器,然后编写自执行等等,一些简单的webpack可以直接去抠JS代码的。第二就是遇到<code>`Could not load content for webpack:///</code>这种报错,需要去修改浏览器配置。</p><h4 id="关于代码"><a href="#关于代码" class="headerlink" title="关于代码"></a>关于代码</h4><p>扫码关注微信号——逆向一步步,公众号内回复关键词<code>04</code>就可以获取本案例的全部代码。</p><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="https://img.heshipeng.com/202204061622110.jpg" alt="qrcode_for_gh_509fdefd3c81_258" title=""> </div> <div class="image-caption">qrcode_for_gh_509fdefd3c81_258</div> </figure>]]></content>
<summary type="html"><blockquote>
<p>免责声明:<strong>本文章中所有内容仅供学习交流,抓包内容、敏感网址、数据接口均已做脱敏处理,严禁用于商业用途和非法用途,否则由此产生的一切后果均与作者无关,若有侵权,请联系我立即删除!</strong></p>
</blockquote>
</summary>
<category term="JS逆向" scheme="http://example.com/categories/JS%E9%80%86%E5%90%91/"/>
<category term="JS" scheme="http://example.com/tags/JS/"/>
<category term="逆向" scheme="http://example.com/tags/%E9%80%86%E5%90%91/"/>
<category term="WebPack" scheme="http://example.com/tags/WebPack/"/>
</entry>
<entry>
<title>JS逆向案例——某远海运公司webpack抠JS</title>
<link href="http://example.com/JS%E9%80%86%E5%90%91%E6%A1%88%E4%BE%8B%E2%80%94%E2%80%94%E6%9F%90%E8%BF%9C%E6%B5%B7%E8%BF%90%E5%85%AC%E5%8F%B8webpack%E6%8A%A0JS/"/>
<id>http://example.com/JS%E9%80%86%E5%90%91%E6%A1%88%E4%BE%8B%E2%80%94%E2%80%94%E6%9F%90%E8%BF%9C%E6%B5%B7%E8%BF%90%E5%85%AC%E5%8F%B8webpack%E6%8A%A0JS/</id>
<published>2022-04-03T15:28:00.000Z</published>
<updated>2022-04-06T08:22:52.302Z</updated>
<content type="html"><![CDATA[<blockquote><p>免责声明:<strong>本文章中所有内容仅供学习交流,抓包内容、敏感网址、数据接口均已做脱敏处理,严禁用于商业用途和非法用途,否则由此产生的一切后果均与作者无关,若有侵权,请联系我立即删除!</strong></p></blockquote><h4 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h4><p>webpack抠JS的一个案例,用来熟练如何从webpack打包的JS代码中抠关键的JS代码。</p><h4 id="抠JS过程"><a href="#抠JS过程" class="headerlink" title="抠JS过程"></a>抠JS过程</h4><p>网址:aHR0cHM6Ly9zeW5jb25odWIuY29zY29zaGlwcGluZy5jb20v</p><p>目标:登录接口的密码加密JS代码分析。</p><p>老规矩,先抓包。</p><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="https://img.heshipeng.com/202204041509227.png?watermark/2/text/5YWz5rOo5b6u5L-h5YWs5LyX5Y-377ya6YCG5ZCR5LiA5q2l5q2l/font/5a6L5L2T/fontsize/300" alt="image-20220404150944421" title=""> </div> <div class="image-caption">image-20220404150944421</div> </figure><p>可以看到登录请求的密码这个参数是加密的。</p><p>搜索关键词<code>password</code>,找到一个疑似加密的函数,如下:</p><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="https://img.heshipeng.com/202204041624199.png?watermark/2/text/5YWz5rOo5b6u5L-h5YWs5LyX5Y-377ya6YCG5ZCR5LiA5q2l5q2l/font/5a6L5L2T/fontsize/300" alt="image-20220404162426120" title=""> </div> <div class="image-caption">image-20220404162426120</div> </figure><p>点进去看到加密的地方:</p><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="https://img.heshipeng.com/202204041633567.png?watermark/2/text/5YWz5rOo5b6u5L-h5YWs5LyX5Y-377ya6YCG5ZCR5LiA5q2l5q2l/font/5a6L5L2T/fontsize/300" alt="image-20220404163306525" title=""> </div> <div class="image-caption">image-20220404163306525</div> </figure><p>接着点进去看这个<code>o.a</code>函数:</p><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="https://img.heshipeng.com/202204041634530.png?watermark/2/text/5YWz5rOo5b6u5L-h5YWs5LyX5Y-377ya6YCG5ZCR5LiA5q2l5q2l/font/5a6L5L2T/fontsize/300" alt="image-20220404163401493" title=""> </div> <div class="image-caption">image-20220404163401493</div> </figure><p>可以看到,对密码采用了RSA加密,然后进行Base64编码。然后看下代码结构,很显然是按照webpack模块化编程进行组织的。</p><p>按照前面介绍的——<a href="https://blog.heshipeng.com/JS%E9%80%86%E5%90%91%E4%B9%8Bwebpack%E6%89%A3JS%E6%80%9D%E8%B7%AF/">JS逆向之webpack扣JS思路</a>——这篇文章总结的方法:</p><ol><li>找到模块加载器。</li></ol><p>快速定位模块加载器,一般可以通过搜索<code>}({</code>,迅速定位到,但是包含加密函数的JS文件中并未找到模块加载器,没有找到的话,我们自己写一个好了。代码如下:</p><figure class="highlight js"><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="function"><span class="keyword">function</span> <span class="title">b</span>(<span class="params">n</span>) </span>{</span><br><span class="line"> <span class="keyword">if</span> (e[n])</span><br><span class="line"> <span class="keyword">return</span> e[n].exports;</span><br><span class="line"> <span class="keyword">var</span> u = e[n] = {</span><br><span class="line"> <span class="attr">i</span>: n,</span><br><span class="line"> <span class="attr">l</span>: !<span class="number">1</span>,</span><br><span class="line"> <span class="attr">exports</span>: {}</span><br><span class="line"> };</span><br><span class="line"> <span class="keyword">return</span> c[n].call(u.exports, u, u.exports, b),</span><br><span class="line"> u.l = !<span class="number">0</span>,</span><br><span class="line"> u.exports</span><br><span class="line">}</span><br></pre></td></tr></table></figure><ol><li>构造自执行。</li></ol><p>这个也比较简单,我们把上边的代码,稍微做修改:</p><figure class="highlight js"><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="function"><span class="keyword">function</span>(<span class="params">c</span>) </span>{</span><br><span class="line"> <span class="keyword">var</span> e = {};</span><br><span class="line"> <span class="function"><span class="keyword">function</span> <span class="title">b</span>(<span class="params">n</span>) </span>{</span><br><span class="line"> <span class="keyword">if</span> (e[n])</span><br><span class="line"> <span class="keyword">return</span> e[n].exports;</span><br><span class="line"> <span class="keyword">var</span> u = e[n] = {</span><br><span class="line"> <span class="attr">i</span>: n,</span><br><span class="line"> <span class="attr">l</span>: !<span class="number">1</span>,</span><br><span class="line"> <span class="attr">exports</span>: {}</span><br><span class="line"> };</span><br><span class="line"> <span class="keyword">return</span> c[n].call(u.exports, u, u.exports, b),</span><br><span class="line"> u.l = !<span class="number">0</span>,</span><br><span class="line"> u.exports</span><br><span class="line"> }</span><br><span class="line"> encode = b;</span><br><span class="line">}({</span><br><span class="line"><span class="comment">// TODO</span></span><br><span class="line">});</span><br></pre></td></tr></table></figure><ol><li>找到并抠出需要的模块。</li></ol><p>抠JS就变成了一道填空题,把加密方法依赖到的模块抠出来,作为参数填到上边自执行函数中去。我们先抠出加密方法, 观察下依赖哪些模块:</p><figure class="highlight js"><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"><span class="keyword">var</span> r = n(<span class="string">"XBrZ"</span>);</span><br><span class="line"><span class="keyword">var</span> t = r.pki.publicKeyFromPem(</span><br><span class="line"> <span class="string">"-----BEGIN PUBLIC KEY-----\n MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsy4xppPDUT2eAOR5h0cyydzxtKB9O80A\n GjUT6FmDgg6CwelpnE0C2h2JQyP1gCveJs6GDwSDn20RVVpD67f//YPYErjaH/CBOxNG3k5IkW1o\n Qx04uqFNMtWvjzk0aFh2eJLsBi7Ha4elw3WySg00B8oZCL4VBay4ML9kyOAjjCj5jHCX8a2yxIMJ\n IF+EjW3kBR68IMwBvuDL45Qa0oB24vTffaSEs+hGjMTQvoCciOfti3pmEAlVc438/cBgAhK5cIMf\n IMElxYAVvmsDy0I7RCUTrajetKjX94Q+JuQUxnIHNC3IVtYsl1x0lNRtb93IhlRCkZ9djOu350eq\n hZIOXQIDAQAB\n -----END PUBLIC KEY-----"</span>).encrypt(e, <span class="string">"RSA-OAEP"</span>, {</span><br><span class="line"> <span class="attr">md</span>: r.md.sha256.create(),</span><br><span class="line"> <span class="attr">mgf1</span>: {</span><br><span class="line"> <span class="attr">md</span>: r.md.sha1.create()</span><br><span class="line"> }</span><br><span class="line">});</span><br><span class="line"><span class="keyword">return</span> <span class="built_in">window</span>.btoa(t)</span><br></pre></td></tr></table></figure><p>通过debug知e是我们填入的密码——即123456,唯一用到的模块是键为<code>XBrZ</code>的模块。我们全局搜索<code>XBrZ:</code>找到对应的模块定义:</p><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="https://img.heshipeng.com/202204041656123.png?watermark/2/text/5YWz5rOo5b6u5L-h5YWs5LyX5Y-377ya6YCG5ZCR5LiA5q2l5q2l/font/5a6L5L2T/fontsize/300" alt="image-20220404165620085" title=""> </div> <div class="image-caption">image-20220404165620085</div> </figure><p>点进去发现这个模块又引用了很多其它的模块,如果按照模块一个一个的抠的话,比较费时。所以我们将这整个文件中定义的模块全部抠下来,作为我们上边定义的自执行函数的参数。</p><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="https://img.heshipeng.com/202204041656763.png?watermark/2/text/5YWz5rOo5b6u5L-h5YWs5LyX5Y-377ya6YCG5ZCR5LiA5q2l5q2l/font/5a6L5L2T/fontsize/300" alt="image-20220404165643735" title=""> </div> <div class="image-caption">image-20220404165643735</div> </figure><p>整理完了之后,我们将代码放到浏览器中检验一下,防止出错:</p><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="https://img.heshipeng.com/202204041701058.png?watermark/2/text/5YWz5rOo5b6u5L-h5YWs5LyX5Y-377ya6YCG5ZCR5LiA5q2l5q2l/font/5a6L5L2T/fontsize/300" alt="image-20220404170102015" title=""> </div> <div class="image-caption">image-20220404170102015</div> </figure><p>不出意外的话就不会出意外,没有报错。</p><ol><li>导出相应的模块。</li></ol><p>模块通过模块加载器加载,所以想要得到加密方法依赖的模块,只需要导出模块函数即可。定义一个全局变量,比如<code>encode</code>,然后将上边实现的模块加载函数赋值给这个全局变量即可。代码如下:</p><figure class="highlight js"><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="keyword">var</span> encode;</span><br><span class="line"></span><br><span class="line">!<span class="function"><span class="keyword">function</span>(<span class="params">c</span>) </span>{</span><br><span class="line"> <span class="keyword">var</span> e = {};</span><br><span class="line"> <span class="function"><span class="keyword">function</span> <span class="title">b</span>(<span class="params">n</span>) </span>{</span><br><span class="line"> <span class="keyword">if</span> (e[n])</span><br><span class="line"> <span class="keyword">return</span> e[n].exports;</span><br><span class="line"> <span class="keyword">var</span> u = e[n] = {</span><br><span class="line"> <span class="attr">i</span>: n,</span><br><span class="line"> <span class="attr">l</span>: !<span class="number">1</span>,</span><br><span class="line"> <span class="attr">exports</span>: {}</span><br><span class="line"> };</span><br><span class="line"> <span class="keyword">return</span> c[n].call(u.exports, u, u.exports, b),</span><br><span class="line"> u.l = !<span class="number">0</span>,</span><br><span class="line"> u.exports</span><br><span class="line"> }</span><br><span class="line"> encode = b;</span><br><span class="line">}({</span><br><span class="line"> <span class="comment">// 此处省略若干行模块函数的定义</span></span><br><span class="line">});</span><br></pre></td></tr></table></figure><ol><li>编写测试代码。</li></ol><p>对于这个案例而言,测试代码就是那一串加密代码,看能否如期的得到类似的加密字符串,就证明我们抠的JS没有问题,测试代码如下:</p><figure class="highlight js"><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="function"><span class="keyword">function</span> <span class="title">get_pass</span>(<span class="params">passwd</span>) </span>{</span><br><span class="line"> <span class="keyword">var</span> r = encode(<span class="string">"XBrZ"</span>); <span class="comment">// 通过模块加载器加载XBrZ模块。</span></span><br><span class="line"> <span class="keyword">var</span> t = r.pki.publicKeyFromPem(<span class="string">"-----BEGIN PUBLIC KEY-----\n MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsy4xppPDUT2eAOR5h0cyydzxtKB9O80A\n GjUT6FmDgg6CwelpnE0C2h2JQyP1gCveJs6GDwSDn20RVVpD67f//YPYErjaH/CBOxNG3k5IkW1o\n Qx04uqFNMtWvjzk0aFh2eJLsBi7Ha4elw3WySg00B8oZCL4VBay4ML9kyOAjjCj5jHCX8a2yxIMJ\n IF+EjW3kBR68IMwBvuDL45Qa0oB24vTffaSEs+hGjMTQvoCciOfti3pmEAlVc438/cBgAhK5cIMf\n IMElxYAVvmsDy0I7RCUTrajetKjX94Q+JuQUxnIHNC3IVtYsl1x0lNRtb93IhlRCkZ9djOu350eq\n hZIOXQIDAQAB\n -----END PUBLIC KEY-----"</span>).encrypt(passwd, <span class="string">"RSA-OAEP"</span>, {</span><br><span class="line"> <span class="attr">md</span>: r.md.sha256.create(),</span><br><span class="line"> <span class="attr">mgf1</span>: {</span><br><span class="line"> <span class="attr">md</span>: r.md.sha1.create()</span><br><span class="line"> }</span><br><span class="line"> });</span><br><span class="line"> <span class="keyword">return</span> <span class="built_in">window</span>.btoa(t)</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="built_in">console</span>.log(get_pass(<span class="string">"123456"</span>));</span><br></pre></td></tr></table></figure><p>运行测试代码,正常的输出了密码加密之后的密文:</p><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="https://img.heshipeng.com/202204041709186.png?watermark/2/text/5YWz5rOo5b6u5L-h5YWs5LyX5Y-377ya6YCG5ZCR5LiA5q2l5q2l/font/5a6L5L2T/fontsize/300" alt="image-20220404170927135" title=""> </div> <div class="image-caption">image-20220404170927135</div> </figure><h4 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h4><p>从webpack组织的JS代码中抠JS,虽然看起来比较简单,但是如果遇到复杂一点的案例,并且对JS语法不太熟的话,还是有一定的难度的的,所以需要对这一块多多练习。后边关于webpack的,还会再出一些案例,一些更加复杂的案例。</p><h4 id="关于代码的获取"><a href="#关于代码的获取" class="headerlink" title="关于代码的获取"></a>关于代码的获取</h4><p>扫码关注微信号——逆向一步步,公众号内回复关键词<code>03</code>就可以获取本案例的全部代码。</p><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="https://img.heshipeng.com/202202231720872.jpg" alt="qrcode_for_gh_509fdefd3c81_258" title=""> </div> <div class="image-caption">qrcode_for_gh_509fdefd3c81_258</div> </figure>]]></content>
<summary type="html"><blockquote>
<p>免责声明:<strong>本文章中所有内容仅供学习交流,抓包内容、敏感网址、数据接口均已做脱敏处理,严禁用于商业用途和非法用途,否则由此产生的一切后果均与作者无关,若有侵权,请联系我立即删除!</strong></p>
</blockquote>
</summary>
<category term="JS逆向" scheme="http://example.com/categories/JS%E9%80%86%E5%90%91/"/>
<category term="JS" scheme="http://example.com/tags/JS/"/>
<category term="逆向" scheme="http://example.com/tags/%E9%80%86%E5%90%91/"/>
<category term="WebPack" scheme="http://example.com/tags/WebPack/"/>
</entry>
<entry>
<title>JS逆向之webpack扣JS思路</title>
<link href="http://example.com/JS%E9%80%86%E5%90%91%E4%B9%8Bwebpack%E6%89%A3JS%E6%80%9D%E8%B7%AF/"/>
<id>http://example.com/JS%E9%80%86%E5%90%91%E4%B9%8Bwebpack%E6%89%A3JS%E6%80%9D%E8%B7%AF/</id>
<published>2022-03-28T08:59:43.000Z</published>
<updated>2022-04-04T08:37:54.956Z</updated>
<content type="html"><![CDATA[<div align="center"><iframe width="560" height="315" src="https://www.youtube.com/embed/iJAxeafvn8Y" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe></div><h4 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h4><p>这篇文章通过站在逆向的角度,解决遇到JS文件如果通过webpack的方式去组织代码模块如何扣JS代码,进行逆向分析的问题。</p><h3 id="关于webpack"><a href="#关于webpack" class="headerlink" title="关于webpack"></a>关于webpack</h3><h4 id="JS的自执行函数"><a href="#JS的自执行函数" class="headerlink" title="JS的自执行函数"></a>JS的自执行函数</h4><p>IIFE 全称 Immediately-invoked Function Expressions,即自执行函数。这种模式本质上就是函数表达式(命名的或者匿名的)在创建后立即执行。当函数变成立即执行的函数表达式时,表达式中的变量不能从外部访问。IIFE 主要用来隔离作用域,避免污染。</p><h5 id="自执行函数的几种形式"><a href="#自执行函数的几种形式" class="headerlink" title="自执行函数的几种形式"></a>自执行函数的几种形式</h5><ol><li>匿名函数前面加上一元操作符,后面加上 <code>()</code>。</li></ol><figure class="highlight js"><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="function"><span class="keyword">function</span>(<span class="params"></span>) </span>{</span><br><span class="line"></span><br><span class="line">}();</span><br><span class="line"></span><br><span class="line">+<span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>{</span><br><span class="line"> </span><br><span class="line">}();</span><br><span class="line"></span><br><span class="line">-<span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>{</span><br><span class="line"> </span><br><span class="line">}();</span><br><span class="line"></span><br><span class="line">~<span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>{</span><br><span class="line"> </span><br><span class="line">}();</span><br></pre></td></tr></table></figure><ol><li>匿名函数后边加上<code>()</code>,然后再用<code>()</code>将整个括起来。</li></ol><figure class="highlight js"><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="function"><span class="keyword">function</span>(<span class="params"></span>) </span>{</span><br><span class="line"> <span class="built_in">console</span>.log(<span class="string">"Hello, world!"</span>);</span><br><span class="line">})();</span><br></pre></td></tr></table></figure><ol><li>先用 <code>()</code> 将匿名函数括起来,再在后面加上 <code>()</code>。</li></ol><figure class="highlight js"><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="function"><span class="keyword">function</span> (<span class="params"></span>) </span>{</span><br><span class="line"> <span class="built_in">console</span>.log(<span class="string">"Hello, world!"</span>);</span><br><span class="line">})();</span><br></pre></td></tr></table></figure><ol><li>使用箭头函数表达式,先用 <code>()</code> 将箭头函数表达式括起来,再在后面加上 <code>()</code>。</li></ol><figure class="highlight js"><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="function">() =></span> {</span><br><span class="line"> <span class="built_in">console</span>.log(<span class="string">"Hello, world!"</span>);</span><br><span class="line">})();</span><br></pre></td></tr></table></figure><ol><li>匿名函数前面加上 <code>void</code> 关键字,后面加上 <code>()</code>, <code>void</code> 指定要计算或运行一个表达式,但是不返回值。</li></ol><figure class="highlight js"><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="keyword">void</span> <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>{</span><br><span class="line"> <span class="built_in">console</span>.log(<span class="string">"Hello, world!"</span>);</span><br><span class="line">}();</span><br></pre></td></tr></table></figure><p>有的时候,我们还有可能见到立即执行函数前面后分号的情况,比如:</p><figure class="highlight js"><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="function"><span class="keyword">function</span> (<span class="params"></span>) </span>{</span><br><span class="line"> <span class="built_in">console</span>.log(<span class="string">"Hello, world!"</span>);</span><br><span class="line">}())</span><br><span class="line"></span><br><span class="line">;!<span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>{</span><br><span class="line"> <span class="built_in">console</span>.log(<span class="string">"Hello, world!"</span>);</span><br><span class="line">}()</span><br></pre></td></tr></table></figure><h5 id="自执行函数传参"><a href="#自执行函数传参" class="headerlink" title="自执行函数传参"></a>自执行函数传参</h5><p>将参数放在末尾的 <code>()</code> 里即可实现参数传递,如:</p><figure class="highlight js"><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"><span class="keyword">var</span> list = [<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>, <span class="number">4</span>, <span class="number">5</span>];</span><br><span class="line"></span><br><span class="line">(<span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>{</span><br><span class="line"> <span class="keyword">var</span> sum = <span class="number">0</span>;</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">var</span> i = <span class="number">0</span>; i < list.length; i++) {</span><br><span class="line"> sum += list[i];</span><br><span class="line"> }</span><br><span class="line"> <span class="built_in">console</span>.log(sum);</span><br><span class="line">})(list);</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> dict = {<span class="attr">name</span>: <span class="string">"Bob"</span>, <span class="attr">age</span>: <span class="string">"20"</span>};</span><br><span class="line"></span><br><span class="line">(<span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>{</span><br><span class="line"> <span class="built_in">console</span>.log(dict.name);</span><br><span class="line">})(dict);</span><br><span class="line"></span><br><span class="line">(<span class="function"><span class="keyword">function</span> (<span class="params">a, b, c, d</span>) </span>{</span><br><span class="line"> <span class="built_in">console</span>.log(a + b + c + d);</span><br><span class="line">})(<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>, <span class="number">4</span>);</span><br></pre></td></tr></table></figure><h4 id="call-apply-bind三兄弟"><a href="#call-apply-bind三兄弟" class="headerlink" title="call, apply, bind三兄弟"></a>call, apply, bind三兄弟</h4><p><code>Function.prototype.call()</code>、<code>Function.prototype.apply()</code>、<code>Function.prototype.bind()</code> 都是比较常用的方法。它们的作用一毛一样,即<strong>改变函数中的 <code>this</code> 指向</strong>,它们的区别如下:</p><ul><li><code>call()</code> 方法会立即执行这个函数,接受一个多个参数,参数之间用逗号隔开;</li><li><code>apply()</code> 方法会立即执行这个函数,接受一个包含多个参数的数组;</li><li><code>bind()</code> 方法不会立即执行这个函数,返回的是一个修改过后的函数,便于稍后调用,接受的参数和 <code>call()</code> 一样。</li></ul><h5 id="call"><a href="#call" class="headerlink" title="call"></a>call</h5><p><code>call()</code> 方法接受多个参数,第一个参数 thisArg 指定了函数体内 this 对象的指向,如果这个函数处于非严格模式下,指定为 null 或 undefined 时会自动替换为指向全局对象(浏览器中就是 window 对象),在严格模式下,函数体内的 this 还是为 null。从第二个参数开始往后,每个参数被依次传入函数,基本语法如下:</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">call</span>(<span class="params">thisArg, arg1, arg2, ...</span>)</span></span><br></pre></td></tr></table></figure><p>比如:</p><figure class="highlight js"><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="function"><span class="keyword">function</span> <span class="title">func1</span>(<span class="params">a, b</span>) </span>{</span><br><span class="line"> <span class="keyword">return</span> a + b;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="built_in">console</span>.log(func1.call(<span class="literal">null</span>, <span class="number">1</span>, <span class="number">2</span>)); <span class="comment">// 3</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">func2</span>(<span class="params"></span>) </span>{</span><br><span class="line"> <span class="keyword">return</span> <span class="built_in">this</span>[<span class="number">0</span>] + <span class="built_in">this</span>[<span class="number">1</span>];</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="built_in">console</span>.log(func2.call([<span class="number">1</span>, <span class="number">2</span>])); <span class="comment">// 3</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">func3</span>(<span class="params"></span>) </span>{</span><br><span class="line"> <span class="keyword">return</span> <span class="built_in">this</span>.a + <span class="built_in">this</span>.b;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="built_in">console</span>.log(func3.call({<span class="string">"a"</span>: <span class="number">1</span>, <span class="string">"b"</span>: <span class="number">2</span>})); <span class="comment">// 3</span></span><br></pre></td></tr></table></figure><h5 id="apply"><a href="#apply" class="headerlink" title="apply"></a>apply</h5><p><code>apply()</code> 方法接受两个参数,第一个参数 thisArg 与 <code>call()</code> 方法一致,第二个参数为一个带下标的集合,这个集合可以为数组,也可以为类数组,<code>apply()</code> 方法把这个集合中的元素作为参数传递给被调用的函数,基本语法如下:</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">function</span>.apply(thisArg, [arg1, arg2, ...])</span><br></pre></td></tr></table></figure><p>比如:</p><figure class="highlight js"><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="function"><span class="keyword">function</span> <span class="title">func2</span>(<span class="params"></span>) </span>{</span><br><span class="line"> <span class="keyword">return</span> <span class="built_in">this</span>[<span class="number">0</span>] + <span class="built_in">this</span>[<span class="number">1</span>];</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="built_in">console</span>.log(func2.apply([<span class="number">1</span>, <span class="number">2</span>])); <span class="comment">// 3</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">func3</span>(<span class="params"></span>) </span>{</span><br><span class="line"> <span class="keyword">return</span> <span class="built_in">this</span>.a + <span class="built_in">this</span>.b;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="built_in">console</span>.log(func3.apply({<span class="string">"a"</span>: <span class="number">1</span>, <span class="string">"b"</span>: <span class="number">2</span>})); <span class="comment">// 3</span></span><br></pre></td></tr></table></figure><h5 id="bind"><a href="#bind" class="headerlink" title="bind"></a>bind</h5><p><code>bind()</code> 方法和 <code>call()</code> 接受的参数是相同的,只不过 <code>bind()</code> 返回的是一个函数,基本语法如下:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">function.bind(thisArg, arg1, arg2, ...)</span><br></pre></td></tr></table></figure><p>比如:</p><figure class="highlight js"><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="function"><span class="keyword">function</span> <span class="title">func</span>(<span class="params">a, b, c</span>) </span>{</span><br><span class="line"> <span class="keyword">return</span> a + b + c;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="built_in">console</span>.log(func.bind(<span class="literal">null</span>, <span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>)()); <span class="comment">// 6</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">func1</span>(<span class="params"></span>) </span>{</span><br><span class="line"> <span class="keyword">return</span> <span class="built_in">this</span>[<span class="number">0</span>] + <span class="built_in">this</span>[<span class="number">1</span>];</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="built_in">console</span>.log(func1.bind([<span class="number">1</span>, <span class="number">2</span>])()); <span class="comment">// 3</span></span><br></pre></td></tr></table></figure><h4 id="理解webpack"><a href="#理解webpack" class="headerlink" title="理解webpack"></a>理解webpack</h4><p>有了以上知识后,我们再来理解一下模块化编程,也就是前面所说的 webpack 写法:</p><figure class="highlight js"><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">!<span class="function"><span class="keyword">function</span> (<span class="params">allModule</span>) </span>{</span><br><span class="line"> <span class="function"><span class="keyword">function</span> <span class="title">useModule</span>(<span class="params">whichModule, xxx, xxx, <span class="comment">/*...*/</span></span>) </span>{</span><br><span class="line"> allModule[whichModule].call(<span class="literal">null</span>, xxx, xxx, <span class="comment">/*...*/</span>);</span><br><span class="line"> }</span><br><span class="line"> useModule(<span class="number">0</span>, <span class="string">'abc'</span>, <span class="literal">null</span>, <span class="comment">/*...*/</span>)</span><br><span class="line">}([</span><br><span class="line"> <span class="function"><span class="keyword">function</span> <span class="title">module0</span>(<span class="params">param</span>) </span>{</span><br><span class="line"> <span class="built_in">console</span>.log(<span class="string">"module0: "</span> + param)</span><br><span class="line"> },</span><br><span class="line"> <span class="function"><span class="keyword">function</span> <span class="title">module1</span>(<span class="params">param</span>) </span>{</span><br><span class="line"> <span class="built_in">console</span>.log(<span class="string">"module1: "</span> + param)</span><br><span class="line"> },</span><br><span class="line"> <span class="function"><span class="keyword">function</span> <span class="title">module2</span>(<span class="params">param</span>) </span>{</span><br><span class="line"> <span class="built_in">console</span>.log(<span class="string">"module2: "</span> + param)</span><br><span class="line"> },</span><br><span class="line">]);</span><br></pre></td></tr></table></figure><p>所谓webpack模块化编程,就是把一类函数——这些函数服务于某个或者某几个功能起作用——以列表或者对象的形式放在一起,封装到一个自执行的函数中,这些函数对外是不可见的,并且只对外暴露一个函数,这个函数叫做模块加载函数,外部通过这个加载函数访问自执行函数内部的函数,从而起到模块化的作用。</p><h5 id="webpack模块化编程的JS代码结构特点"><a href="#webpack模块化编程的JS代码结构特点" class="headerlink" title="webpack模块化编程的JS代码结构特点"></a>webpack模块化编程的JS代码结构特点</h5><figure class="highlight js"><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"><span class="function"><span class="keyword">function</span> (<span class="params">x</span>) </span>{</span><br><span class="line"> <span class="comment">/*加载模块的方法*/</span></span><br><span class="line"> <span class="function"><span class="keyword">function</span> <span class="title">xx</span>(<span class="params">yy</span>) </span>{</span><br><span class="line"> x[yy].call(x1, x2, x3);</span><br><span class="line"> <span class="comment">// x[yy].apply([x1, x2, x3]);</span></span><br><span class="line"> <span class="comment">// x[yy].bind(x1, x2, x3)();</span></span><br><span class="line"> </span><br><span class="line"> }([</span><br><span class="line"> <span class="comment">// 可供加载的模块列表</span></span><br><span class="line"> <span class="function"><span class="keyword">function</span>(<span class="params">x1, x2, x3</span>) </span>{},</span><br><span class="line"> <span class="function"><span class="keyword">function</span>(<span class="params">x1, x2, x3</span>) </span>{}</span><br><span class="line"> ]</span><br><span class="line"> <span class="comment">// 或者是</span></span><br><span class="line"> {</span><br><span class="line"> <span class="string">"xxx"</span>: <span class="function"><span class="keyword">function</span>(<span class="params">x1, x2, x3</span>) </span>{},</span><br><span class="line"> <span class="string">"xxx"</span>: <span class="function"><span class="keyword">function</span>(<span class="params">x1, x2, x3</span>) </span>{}</span><br><span class="line"> }</span><br><span class="line"> );</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>webpack模块化编程的JS代码特点是包含2个部分,上面是一个加载模块的方法,也叫模块加载器。下面是可供加载的模块列表。可供加载的模块列表是一个类数组(可以是数组,可以是对象)。</p><h5 id="webpack扣JS的步骤"><a href="#webpack扣JS的步骤" class="headerlink" title="webpack扣JS的步骤"></a>webpack扣JS的步骤</h5><p>我们以这个网址——<a href="https://www.gm99.com/">G妹游戏</a>——为例来介绍webpack扣js的一般步骤。我们的目的是抠出密码加密算法的那一部分JS代码。</p><p>找到我们要扣JS的那个文件,抓包,打断点分析的过程就不赘述了,直接贴文件:</p><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="https://img.heshipeng.com/202203290055624.png?watermark/2/text/5YWz5rOo5b6u5L-h5YWs5LyX5Y-377ya6YCG5ZCR5LiA5q2l5q2l/font/5a6L5L2T/fontsize/300" alt="image-20220329005533139" title=""> </div> <div class="image-caption">image-20220329005533139</div> </figure><ol><li>找到模块加载器(加载模块的方法)</li></ol><p>根据前面提到的webpack模块化编程的JS代码结构特点,很显然这个模块加载为:</p><figure class="highlight js"><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="function"><span class="keyword">function</span> <span class="title">e</span>(<span class="params">s</span>) </span>{</span><br><span class="line"> <span class="keyword">var</span> i = {};</span><br><span class="line"><span class="keyword">if</span> (i[s])</span><br><span class="line"><span class="keyword">return</span> i[s].exports;</span><br><span class="line"><span class="keyword">var</span> n = i[s] = {</span><br><span class="line"> <span class="attr">exports</span>: {},</span><br><span class="line"> <span class="attr">id</span>: s,</span><br><span class="line"> <span class="attr">loaded</span>: !<span class="number">1</span></span><br><span class="line"> };</span><br><span class="line"> <span class="keyword">return</span> t[s].call(n.exports, n, n.exports, e),</span><br><span class="line"> n.loaded = !<span class="number">0</span>,</span><br><span class="line"> n.exports</span><br><span class="line">}</span><br></pre></td></tr></table></figure><ol><li>构造一个自执行。可以是构造一个空的自执行,也可以是把网站的自执行JS扣下来,然后删除不必要的方法。如下:</li></ol><figure class="highlight js"><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="function"><span class="keyword">function</span>(<span class="params">t</span>) </span>{</span><br><span class="line"> <span class="keyword">var</span> i = {};</span><br><span class="line"> <span class="function"><span class="keyword">function</span> <span class="title">e</span>(<span class="params">s</span>) </span>{</span><br><span class="line"> <span class="keyword">if</span> (i[s])</span><br><span class="line"> <span class="keyword">return</span> i[s].exports;</span><br><span class="line"> <span class="keyword">var</span> n = i[s] = {</span><br><span class="line"> <span class="attr">exports</span>: {},</span><br><span class="line"> <span class="attr">id</span>: s,</span><br><span class="line"> <span class="attr">loaded</span>: !<span class="number">1</span></span><br><span class="line"> };</span><br><span class="line"> <span class="keyword">return</span> t[s].call(n.exports, n, n.exports, e),</span><br><span class="line"> n.loaded = !<span class="number">0</span>,</span><br><span class="line"> n.exports</span><br><span class="line"> }</span><br><span class="line">}();</span><br></pre></td></tr></table></figure><p>注意构造的这个自执行方法只需要保留模块加载方法。</p><ol><li>找到并抠出调用的模块。从可供加载的模块列表中抠出包含我们需要的加密方法的模块。</li></ol><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="https://img.heshipeng.com/202203291612665.png?watermark/2/text/5YWz5rOo5b6u5L-h5YWs5LyX5Y-377ya6YCG5ZCR5LiA5q2l5q2l/font/5a6L5L2T/fontsize/300" alt="image-20220329161258366" title=""> </div> <div class="image-caption">image-20220329161258366</div> </figure><p>通过抓包,找请求调用栈,定位到密码加密相关的地方。打上断点,调试这个方法,如下:</p><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="https://img.heshipeng.com/202203291613208.png?watermark/2/text/5YWz5rOo5b6u5L-h5YWs5LyX5Y-377ya6YCG5ZCR5LiA5q2l5q2l/font/5a6L5L2T/fontsize/300" alt="image-20220329161353804" title=""> </div> <div class="image-caption">image-20220329161353804</div> </figure><p>接着断点,接着调试:</p><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="https://img.heshipeng.com/202203291614519.png?watermark/2/text/5YWz5rOo5b6u5L-h5YWs5LyX5Y-377ya6YCG5ZCR5LiA5q2l5q2l/font/5a6L5L2T/fontsize/300" alt="image-20220329161453244" title=""> </div> <div class="image-caption">image-20220329161453244</div> </figure><p>最终找到需要调用的模块如图:</p><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="https://img.heshipeng.com/202203291616929.png?watermark/2/text/5YWz5rOo5b6u5L-h5YWs5LyX5Y-377ya6YCG5ZCR5LiA5q2l5q2l/font/5a6L5L2T/fontsize/300" alt="image-20220329161649646" title=""> </div> <div class="image-caption">image-20220329161649646</div> </figure><p>接着就是抠出这个调用模块,我们可以把整个文件复制下来,我们发现可供加载的模块是一个对象,用数字作为键,模块作为值,为了方便我们把键为0的叫做模块0,键为1的叫做模块1,依此类推。搜索关键代码<code>qe.prototype.encrypt</code>,根据代码缩进,找到封装<code>qe.prototype.encrypt</code>这个方法的模块是模块4,我们拷贝整个模块4的代码,粘贴到我们第二步构建的那个自执行方法的可供加载的模块列表中去,注意是以object的方法,不要用列表形式,同时给这个模块取一个名字,比如就叫做<code>encrypt</code>。</p><p>注意到我们前面跟栈的示意图,生成密码的地方是调用模块3的<code>encode</code> 方法,而<code>encode</code>方法是调用模块4的<code>encrypt</code>方法,我们上边已经抠出来了模块4,不要忘了抠出模块3(虽然模块3比较简单,完全可以自己写)。同样的给模块3重新取个名为<code>encode</code>。</p><p>最终模块3和模块4抠出来的代码如下:</p><figure class="highlight js"><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></pre></td><td class="code"><pre><span class="line">!<span class="function"><span class="keyword">function</span>(<span class="params">t</span>) </span>{</span><br><span class="line"> <span class="function"><span class="keyword">function</span> <span class="title">e</span>(<span class="params">s</span>) </span>{</span><br><span class="line"> <span class="keyword">if</span> (i[s])</span><br><span class="line"> <span class="keyword">return</span> i[s].exports;</span><br><span class="line"> <span class="keyword">var</span> n = i[s] = {</span><br><span class="line"> <span class="attr">exports</span>: {},</span><br><span class="line"> <span class="attr">id</span>: s,</span><br><span class="line"> <span class="attr">loaded</span>: !<span class="number">1</span></span><br><span class="line"> };</span><br><span class="line"> <span class="keyword">return</span> t[s].call(n.exports, n, n.exports, e),</span><br><span class="line"> n.loaded = !<span class="number">0</span>,</span><br><span class="line"> n.exports</span><br><span class="line"> }</span><br><span class="line">}({</span><br><span class="line"> <span class="string">"encrypt"</span>: <span class="function"><span class="keyword">function</span>(<span class="params">t, e, i</span>) </span>{</span><br><span class="line"> <span class="keyword">var</span> s, n, r;</span><br><span class="line"> s = <span class="function"><span class="keyword">function</span>(<span class="params">t, e, i</span>) </span>{</span><br><span class="line"> </span><br><span class="line"> <span class="comment">/* 省略若干行代码 */</span></span><br><span class="line"> </span><br><span class="line"> qe.prototype.decrypt = <span class="function"><span class="keyword">function</span>(<span class="params">t</span>) </span>{</span><br><span class="line"> <span class="keyword">try</span> {</span><br><span class="line"> <span class="keyword">return</span> <span class="built_in">this</span>.getKey().decrypt(ye(t))</span><br><span class="line"> } <span class="keyword">catch</span> (t) {</span><br><span class="line"> <span class="keyword">return</span> !<span class="number">1</span></span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> ,</span><br><span class="line"> qe.prototype.encrypt = <span class="function"><span class="keyword">function</span>(<span class="params">t</span>) </span>{</span><br><span class="line"> <span class="keyword">try</span> {</span><br><span class="line"> <span class="keyword">return</span> be(<span class="built_in">this</span>.getKey().encrypt(t))</span><br><span class="line"> } <span class="keyword">catch</span> (t) {</span><br><span class="line"> <span class="keyword">return</span> !<span class="number">1</span></span><br><span class="line"> }</span><br><span class="line"> } </span><br><span class="line"></span><br><span class="line"> <span class="comment">/* 省略若干行代码 */</span></span><br><span class="line"></span><br><span class="line"> })</span><br><span class="line"> }</span><br><span class="line"> .call(e, i, e, t),</span><br><span class="line"> !(<span class="keyword">void</span> <span class="number">0</span> !== s && (t.exports = s))</span><br><span class="line"> },</span><br><span class="line"> <span class="string">"encode"</span>: <span class="function"><span class="keyword">function</span>(<span class="params">t, e, i</span>) </span>{</span><br><span class="line"> <span class="keyword">var</span> s;</span><br><span class="line"> s = <span class="function"><span class="keyword">function</span>(<span class="params">t, e, s</span>) </span>{</span><br><span class="line"> <span class="function"><span class="keyword">function</span> <span class="title">n</span>(<span class="params"></span>) </span>{</span><br><span class="line"> <span class="string">"undefined"</span> != <span class="keyword">typeof</span> r && (<span class="built_in">this</span>.jsencrypt = <span class="keyword">new</span> r.JSEncrypt,</span><br><span class="line"> <span class="built_in">this</span>.jsencrypt.setPublicKey(<span class="string">"-----BEGIN PUBLIC KEY-----MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDq04c6My441Gj0UFKgrqUhAUg+kQZeUeWSPlAU9fr4HBPDldAeqzx1UR92KJHuQh/zs1HOamE2dgX9z/2oXcJaqoRIA/FXysx+z2YlJkSk8XQLcQ8EBOkp//MZrixam7lCYpNOjadQBb2Ot0U/Ky+jF2p+Ie8gSZ7/u+Wnr5grywIDAQAB-----END PUBLIC KEY-----"</span>))</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">var</span> r = i(<span class="number">4</span>);</span><br><span class="line"> n.prototype.encode = <span class="function"><span class="keyword">function</span>(<span class="params">t, e</span>) </span>{</span><br><span class="line"> <span class="keyword">var</span> i = e ? e + <span class="string">"|"</span> + t : t;</span><br><span class="line"> <span class="keyword">return</span> <span class="built_in">encodeURIComponent</span>(<span class="built_in">this</span>.jsencrypt.encrypt(i))</span><br><span class="line"> }</span><br><span class="line"> ,</span><br><span class="line"> s.exports = n</span><br><span class="line"> }</span><br><span class="line"> .call(e, i, e, t),</span><br><span class="line"> !(<span class="keyword">void</span> <span class="number">0</span> !== s && (t.exports = s))</span><br><span class="line"> }</span><br><span class="line">});</span><br></pre></td></tr></table></figure><p>扣完代码之后,我们将整个代码放到浏览器中执行一遍,验证下抠的JS代码没有问题:</p><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="https://img.heshipeng.com/202203291634343.png?watermark/2/text/5YWz5rOo5b6u5L-h5YWs5LyX5Y-377ya6YCG5ZCR5LiA5q2l5q2l/font/5a6L5L2T/fontsize/300" alt="image-20220329163418015" title=""> </div> <div class="image-caption">image-20220329163418015</div> </figure><ol><li>导出相应的模块方法。我们只需要导出模块加载函数就行,因为加密中用到的<code>encode</code>和<code>encrypt</code>方法都是通过模块加载函数加载的。我们可以在自执行方法外面定义一个全局变量,然后把模块加载函数赋值给这个全局变量,这样就可以从自执行函数内部导出模块加载方法了。代码如下:</li></ol><figure class="highlight js"><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="comment">// 对外暴露模块加载函数</span></span><br><span class="line"><span class="keyword">var</span> _n = e;</span><br><span class="line"></span><br><span class="line">!<span class="function"><span class="keyword">function</span>(<span class="params">t</span>) </span>{</span><br><span class="line"> <span class="function"><span class="keyword">function</span> <span class="title">e</span>(<span class="params">s</span>) </span>{</span><br><span class="line"> <span class="keyword">if</span> (i[s])</span><br><span class="line"> <span class="keyword">return</span> i[s].exports;</span><br><span class="line"> <span class="keyword">var</span> n = i[s] = {</span><br><span class="line"> <span class="attr">exports</span>: {},</span><br><span class="line"> <span class="attr">id</span>: s,</span><br><span class="line"> <span class="attr">loaded</span>: !<span class="number">1</span></span><br><span class="line"> };</span><br><span class="line"> <span class="keyword">return</span> t[s].call(n.exports, n, n.exports, e),</span><br><span class="line"> n.loaded = !<span class="number">0</span>,</span><br><span class="line"> n.exports</span><br><span class="line"> }</span><br><span class="line">}({</span><br><span class="line"> <span class="string">"encrypt"</span>: <span class="function"><span class="keyword">function</span>(<span class="params">t, e, i</span>) </span>{</span><br><span class="line"> <span class="comment">// 省略函数体</span></span><br><span class="line"> },</span><br><span class="line"> <span class="string">"encode"</span>: <span class="function"><span class="keyword">function</span>(<span class="params">t, e, i</span>) </span>{</span><br><span class="line"> <span class="comment">// 省略函数体</span></span><br><span class="line"> }</span><br><span class="line">});</span><br></pre></td></tr></table></figure><p>修改完代码之后,我们在浏览器中验证一下:</p><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="https://img.heshipeng.com/202203291814720.png?watermark/2/text/5YWz5rOo5b6u5L-h5YWs5LyX5Y-377ya6YCG5ZCR5LiA5q2l5q2l/font/5a6L5L2T/fontsize/300" alt="image-20220329181407362" title=""> </div> <div class="image-caption">image-20220329181407362</div> </figure><p>可以看到成功加载了<code>encrypt</code>函数,加载<code>encode</code>函数的时候报错,我们打上断点调试,发现报错的那一行代码是:</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> r = i(<span class="number">4</span>);</span><br></pre></td></tr></table></figure><p>这里的i是<code>encode</code>的第三个参数,是<code>t[s].call(n.exports, n, n.exports, e)</code>的第四个参数即模块加载函数(前面提到过call的第一个参数是影响this指针的参数),表示调用模块4,然而模块4我们改名为<code>encrypt</code>(熟悉的模块3调用模块4,但是模块3盒模块4我们都改名了,😮💨真是给自己挖坑,其实完全跟原始JS的保持一致的命名)。所以这里我们只需要把这行代码改为:</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> r = i(<span class="string">"encrpty"</span>);</span><br></pre></td></tr></table></figure><p>改好之后,我们再次在浏览器中调试,没有报错。</p><ol><li>编写代码测试。</li></ol><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="https://img.heshipeng.com/202203292001605.png?watermark/2/text/5YWz5rOo5b6u5L-h5YWs5LyX5Y-377ya6YCG5ZCR5LiA5q2l5q2l/font/5a6L5L2T/fontsize/300" alt="image-20220329200136091" title=""> </div> <div class="image-caption">image-20220329200136091</div> </figure><p>可以看到测试结果符合预期。</p><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="https://img.heshipeng.com/202203292004706.png" alt="output-onlinepngtools" title=""> </div> <div class="image-caption">output-onlinepngtools</div> </figure><h4 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h4><p>这篇文章主要介绍了webpack模块化以及如何从中抠出相应的模块。抠JS的方法,总结起来分五步:</p><ul><li>找到模块加载器</li><li>构造自执行</li><li>找到并抠出需要的模块</li><li>导出相应的模块方法</li><li>编写代码测试</li></ul>]]></content>
<summary type="html"><div align="center"><iframe width="560" height="315" src="https://www.youtube.com/embed/iJAxeafvn8Y" title="YouTube video player" frameborde</summary>
<category term="JS逆向" scheme="http://example.com/categories/JS%E9%80%86%E5%90%91/"/>
<category term="JS" scheme="http://example.com/tags/JS/"/>
<category term="逆向" scheme="http://example.com/tags/%E9%80%86%E5%90%91/"/>
<category term="WebPack" scheme="http://example.com/tags/WebPack/"/>
</entry>
<entry>
<title>JS逆向之WebSocket协议</title>
<link href="http://example.com/JS%E9%80%86%E5%90%91%E4%B9%8BWebSocket%E5%8D%8F%E8%AE%AE/"/>
<id>http://example.com/JS%E9%80%86%E5%90%91%E4%B9%8BWebSocket%E5%8D%8F%E8%AE%AE/</id>
<published>2022-03-27T09:45:51.000Z</published>
<updated>2022-03-27T15:22:35.255Z</updated>
<content type="html"><![CDATA[<div align="center"><iframe width="560" height="315" src="https://www.youtube.com/embed/Hlp8XD0R5qo" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe></div><blockquote><p>免责声明:<strong>本文章中所有内容仅供学习交流,抓包内容、敏感网址、数据接口均已做脱敏处理,严禁用于商业用途和非法用途,否则由此产生的一切后果均与作者无关,若有侵权,请联系我立即删除!</strong></p></blockquote><h3 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h3><p>本文的目的不是着重讲WebSockets,而是解决在JS逆向过程中遇到网站使用WebSockets协议的时候如何处理。</p><h3 id="WebSocket"><a href="#WebSocket" class="headerlink" title="WebSocket"></a>WebSocket</h3><h4 id="什么是WebSocket"><a href="#什么是WebSocket" class="headerlink" title="什么是WebSocket"></a>什么是WebSocket</h4><p>WebSocket是一种网络传输协议,可在单个TCP连接上进行全双工通信,位于OSI模型的应用层。WebSocket使得客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客户端推送数据。WebSocket的最大特点就是浏览器和服务器只需要完成一次握手,两者之间就可以创建持久性的连接,并进行双向数据传输。</p><p>WebSocket协议规范将<code>ws</code>(WebSocket)和<code>wss</code>(WebSocket Secure)定义为两个新的统一资源标识符(URI)方案,分别对应明文和加密连接。除了方案名称和片段ID(不支持<code>#</code>)之外,其余的URI组件都被定义为此URI的通用语法。</p><p><img src="https://img.heshipeng.com/202203272028182.png?watermark/2/text/5YWz5rOo5b6u5L-h5YWs5LyX5Y-377ya6YCG5ZCR5LiA5q2l5q2l/font/5a6L5L2T/fontsize/300" alt="img"></p><p>WebSocket的其它特点:</p><ol><li>建立在 TCP 协议之上,服务器端的实现比较容易。</li><li>与 HTTP 协议有着良好的兼容性。默认端口也是80和443,并且握手阶段采用 HTTP 协议,因此握手时不容易屏蔽,能通过各种 HTTP 代理服务器。</li><li>数据格式比较轻量,性能开销小,通信高效。</li><li>可以发送文本(json/xml/纯文本),也可以发送二进制数据(protobuf)。</li><li>没有同源限制,客户端可以与任意服务器通信。</li><li>协议标识符是<code>ws</code>(如果加密,则为<code>wss</code>),服务器网址就是 URL。如ws(wss)://example.com:80/some/path。</li></ol><p><img src="https://img.heshipeng.com/202203272034038.jpg?watermark/2/text/5YWz5rOo5b6u5L-h5YWs5LyX5Y-377ya6YCG5ZCR5LiA5q2l5q2l/font/5a6L5L2T/fontsize/300" alt="img"></p><h4 id="为什么需要WebSocket"><a href="#为什么需要WebSocket" class="headerlink" title="为什么需要WebSocket"></a>为什么需要WebSocket</h4><p>我们已经有了 HTTP 协议,为什么还需要另一个协议?它能带来什么好处?答案很简单,因为 HTTP 协议有一个缺陷:通信只能由客户端发起。试想一下,现在有个场景,比如聊天室。如果A用户与B用户聊天,如果用HTTP协议,只能是A用户客户端向服务器发起一个HTTP请求,询问是否有B用户的消息,同样的对于B用户也一样。这种单向请求的特点,注定了如果服务器有连续的状态变化,客户端要获知就非常麻烦。就只能用轮询的方式:每隔一段时间就发出一个询问,了解服务器有没有新的信息。</p><p>轮询的效率低,非常浪费资源(因为必须不停连接,或者 HTTP 连接始终打开)。因此,工程师们一直在思考,有没有更好的方法。WebSocket 就是这样发明的。</p><h4 id="握手协议"><a href="#握手协议" class="headerlink" title="握手协议"></a>握手协议</h4><p>WebSocket 是独立的、创建在TCP上的协议。Websocket 通过HTTP</p><h5 id="协议说明"><a href="#协议说明" class="headerlink" title="协议说明"></a>协议说明</h5><p>客户端请求:</p> <figure class="highlight plaintext"><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">GET /chat HTTP/1.1</span><br><span class="line">Host: server.example.com</span><br><span class="line">Upgrade: websocket</span><br><span class="line">Connection: Upgrade</span><br><span class="line">Origin: http://example.com</span><br><span class="line">Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==</span><br><span class="line">Sec-WebSocket-Protocol: chat, superchat</span><br><span class="line">Sec-WebSocket-Version: 13</span><br></pre></td></tr></table></figure><p>服务器回应:</p><figure class="highlight plaintext"><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">HTTP/1.1 101 Switching Protocols</span><br><span class="line">Upgrade: websocket</span><br><span class="line">Connection: Upgrade</span><br><span class="line">Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=</span><br><span class="line">Sec-WebSocket-Protocol: chat</span><br></pre></td></tr></table></figure><p>字段说明:</p><ul><li>Connection必须设置Upgrade,表示客户端希望连接升级。</li><li>Upgrade字段必须设置Websocket,表示希望升级到Websocket协议。</li><li>Sec-WebSocket-Key是随机的字符串,服务器端会用这些数据来构造出一个SHA-1的信息摘要,然后进行Base64编码。</li><li>Sec-WebSocket-Version 表示支持的Websocket版本。</li><li>Origin字段是必须的。如果缺少origin字段,WebSocket服务器需要回复HTTP 403 状态码(禁止访问)。</li></ul><h4 id="WebSocket-NodeJS版的客户端API"><a href="#WebSocket-NodeJS版的客户端API" class="headerlink" title="WebSocket NodeJS版的客户端API"></a>WebSocket NodeJS版的客户端API</h4><h5 id="简单示例"><a href="#简单示例" class="headerlink" title="简单示例"></a>简单示例</h5><p>客户端代码:</p><figure class="highlight js"><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">const</span> WebSocket = <span class="built_in">require</span>(<span class="string">'ws'</span>)</span><br><span class="line"><span class="keyword">const</span> ws = <span class="keyword">new</span> WebSocket(<span class="string">'ws://localhost:3000'</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment">// 接受</span></span><br><span class="line">ws.on(<span class="string">'message'</span>, <span class="function">(<span class="params">message</span>) =></span> {</span><br><span class="line"> <span class="built_in">console</span>.log(message.toString())</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 当数字达到 10 时,断开连接</span></span><br><span class="line"> <span class="keyword">if</span> (<span class="built_in">Number</span>.parseInt(message) === <span class="number">10</span>) {</span><br><span class="line"> ws.send(<span class="string">'close'</span>);</span><br><span class="line"> ws.close()</span><br><span class="line"> }</span><br><span class="line">})</span><br></pre></td></tr></table></figure><p>服务端代码:</p><figure class="highlight js"><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">const</span> WebSocket = <span class="built_in">require</span>(<span class="string">'ws'</span>)</span><br><span class="line"><span class="keyword">const</span> WebSocketServer = WebSocket.Server;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 创建 websocket 服务器 监听在 3000 端口</span></span><br><span class="line"><span class="keyword">const</span> wss = <span class="keyword">new</span> WebSocketServer({<span class="attr">port</span>: <span class="number">3000</span>})</span><br><span class="line"></span><br><span class="line"><span class="comment">// 服务器被客户端连接</span></span><br><span class="line">wss.on(<span class="string">'connection'</span>, <span class="function">(<span class="params">ws</span>) =></span> {</span><br><span class="line"> <span class="comment">// 通过 ws 对象,就可以获取到客户端发送过来的信息和主动推送信息给客户端</span></span><br><span class="line"></span><br><span class="line"> <span class="keyword">let</span> i = <span class="number">0</span>;</span><br><span class="line"> <span class="built_in">setInterval</span>(<span class="function"><span class="keyword">function</span> <span class="title">f</span>(<span class="params"></span>) </span>{</span><br><span class="line"> ws.send(i++) <span class="comment">// 每隔 1 秒给连接方报一次数</span></span><br><span class="line"> }, <span class="number">1000</span>)</span><br><span class="line">})</span><br></pre></td></tr></table></figure><h5 id="详细API介绍"><a href="#详细API介绍" class="headerlink" title="详细API介绍"></a>详细API介绍</h5><ol><li>WebSocket 构造函数</li></ol><p>WebSocket 对象作为一个构造函数,用于新建 WebSocket 实例。</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> ws = <span class="keyword">new</span> WebSocket(<span class="string">'ws://localhost:8080'</span>);</span><br></pre></td></tr></table></figure><p>执行上面语句之后,客户端就会与服务器进行连接。</p><ol><li>webSocket.readyState</li></ol><p><code>readyState</code>属性返回实例对象的当前状态,共有四种。</p><ul><li>CONNECTING:值为0,表示正在连接。</li><li>OPEN:值为1,表示连接成功,可以通信了。</li><li>CLOSING:值为2,表示连接正在关闭。</li><li>CLOSED:值为3,表示连接已经关闭,或者打开连接失败。</li></ul><p>下面是一个示例:</p><figure class="highlight js"><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="keyword">switch</span> (ws.readyState) {</span><br><span class="line"> <span class="keyword">case</span> WebSocket.CONNECTING:</span><br><span class="line"> <span class="comment">// do something</span></span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> <span class="keyword">case</span> WebSocket.OPEN:</span><br><span class="line"> <span class="comment">// do something</span></span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> <span class="keyword">case</span> WebSocket.CLOSING:</span><br><span class="line"> <span class="comment">// do something</span></span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> <span class="keyword">case</span> WebSocket.CLOSED:</span><br><span class="line"> <span class="comment">// do something</span></span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> <span class="keyword">default</span>:</span><br><span class="line"> <span class="comment">// this never happens</span></span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><ol><li>webSocket.onopen</li></ol><p>实例对象的<code>onopen</code>属性,用于指定连接成功后的回调函数。</p><figure class="highlight js"><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">ws.onopen = <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>{</span><br><span class="line"> ws.send(<span class="string">'Hello Server!'</span>);</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>如果要指定多个回调函数,可以使用<code>addEventListener</code>方法。</p><figure class="highlight js"><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">ws.addEventListener(<span class="string">'open'</span>, <span class="function"><span class="keyword">function</span> (<span class="params">event</span>) </span>{</span><br><span class="line"> ws.send(<span class="string">'Hello Server!'</span>);</span><br><span class="line">});</span><br></pre></td></tr></table></figure><ol><li>webSocket.onclose</li></ol><p>实例对象的<code>onclose</code>属性,用于指定连接关闭后的回调函数。</p><figure class="highlight js"><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">ws.onclose = <span class="function"><span class="keyword">function</span>(<span class="params">event</span>) </span>{</span><br><span class="line"> <span class="keyword">var</span> code = event.code;</span><br><span class="line"> <span class="keyword">var</span> reason = event.reason;</span><br><span class="line"> <span class="keyword">var</span> wasClean = event.wasClean;</span><br><span class="line"> <span class="comment">// handle close event</span></span><br><span class="line">};</span><br><span class="line"></span><br><span class="line">ws.addEventListener(<span class="string">"close"</span>, <span class="function"><span class="keyword">function</span>(<span class="params">event</span>) </span>{</span><br><span class="line"> <span class="keyword">var</span> code = event.code;</span><br><span class="line"> <span class="keyword">var</span> reason = event.reason;</span><br><span class="line"> <span class="keyword">var</span> wasClean = event.wasClean;</span><br><span class="line"> <span class="comment">// handle close event</span></span><br><span class="line">});</span><br></pre></td></tr></table></figure><ol><li>webSocket.onmessage</li></ol><p>实例对象的<code>onmessage</code>属性,用于指定收到服务器数据后的回调函数。</p><figure class="highlight js"><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">ws.onmessage = <span class="function"><span class="keyword">function</span>(<span class="params">event</span>) </span>{</span><br><span class="line"> <span class="keyword">var</span> data = event.data;</span><br><span class="line"> <span class="comment">// 处理数据</span></span><br><span class="line">};</span><br><span class="line"></span><br><span class="line">ws.addEventListener(<span class="string">"message"</span>, <span class="function"><span class="keyword">function</span>(<span class="params">event</span>) </span>{</span><br><span class="line"> <span class="keyword">var</span> data = event.data;</span><br><span class="line"> <span class="comment">// 处理数据</span></span><br><span class="line">});</span><br></pre></td></tr></table></figure><p>注意,服务器数据可能是文本,也可能是二进制数据(<code>blob</code>对象或<code>Arraybuffer</code>对象)。</p><figure class="highlight js"><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">ws.onmessage = <span class="function"><span class="keyword">function</span>(<span class="params">event</span>)</span>{</span><br><span class="line"> <span class="keyword">if</span>(<span class="keyword">typeof</span> event.data === <span class="built_in">String</span>) {</span><br><span class="line"> <span class="built_in">console</span>.log(<span class="string">"Received data string"</span>);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span>(event.data <span class="keyword">instanceof</span> <span class="built_in">ArrayBuffer</span>){</span><br><span class="line"> <span class="keyword">var</span> buffer = event.data;</span><br><span class="line"> <span class="built_in">console</span>.log(<span class="string">"Received arraybuffer"</span>);</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>除了动态判断收到的数据类型,也可以使用<code>binaryType</code>属性,显式指定收到的二进制数据类型。</p><figure class="highlight js"><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="comment">// 收到的是 blob 数据</span></span><br><span class="line">ws.binaryType = <span class="string">"blob"</span>;</span><br><span class="line">ws.onmessage = <span class="function"><span class="keyword">function</span>(<span class="params">e</span>) </span>{</span><br><span class="line"> <span class="built_in">console</span>.log(e.data.size);</span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="comment">// 收到的是 ArrayBuffer 数据</span></span><br><span class="line">ws.binaryType = <span class="string">"arraybuffer"</span>;</span><br><span class="line">ws.onmessage = <span class="function"><span class="keyword">function</span>(<span class="params">e</span>) </span>{</span><br><span class="line"> <span class="built_in">console</span>.log(e.data.byteLength);</span><br><span class="line">};</span><br></pre></td></tr></table></figure><ol><li>webSocket.send()</li></ol><p>实例对象的<code>send()</code>方法用于向服务器发送数据。</p><p>发送文本的例子。</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">ws.send(<span class="string">'your message'</span>);</span><br></pre></td></tr></table></figure><p>发送 Blob 对象的例子。</p><figure class="highlight js"><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">var</span> file = <span class="built_in">document</span></span><br><span class="line"> .querySelector(<span class="string">'input[type="file"]'</span>)</span><br><span class="line"> .files[<span class="number">0</span>];</span><br><span class="line">ws.send(file);</span><br></pre></td></tr></table></figure><p>发送 ArrayBuffer 对象的例子。</p><figure class="highlight js"><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">// Sending canvas ImageData as ArrayBuffer</span></span><br><span class="line"><span class="keyword">var</span> img = canvas_context.getImageData(<span class="number">0</span>, <span class="number">0</span>, <span class="number">400</span>, <span class="number">320</span>);</span><br><span class="line"><span class="keyword">var</span> binary = <span class="keyword">new</span> <span class="built_in">Uint8Array</span>(img.data.length);</span><br><span class="line"><span class="keyword">for</span> (<span class="keyword">var</span> i = <span class="number">0</span>; i < img.data.length; i++) {</span><br><span class="line"> binary[i] = img.data[i];</span><br><span class="line">}</span><br><span class="line">ws.send(binary.buffer);</span><br><span class="line"></span><br></pre></td></tr></table></figure><ol><li>webSocket.bufferedAmount</li></ol><p>实例对象的<code>bufferedAmount</code>属性,表示还有多少字节的二进制数据没有发送出去。它可以用来判断发送是否结束。</p><figure class="highlight js"><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="keyword">var</span> data = <span class="keyword">new</span> <span class="built_in">ArrayBuffer</span>(<span class="number">10000000</span>);</span><br><span class="line">socket.send(data);</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> (socket.bufferedAmount === <span class="number">0</span>) {</span><br><span class="line"> <span class="comment">// 发送完毕</span></span><br><span class="line">} <span class="keyword">else</span> {</span><br><span class="line"> <span class="comment">// 发送还没结束</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure><ol><li>webSocket.onerror</li></ol><p>实例对象的<code>onerror</code>属性,用于指定报错时的回调函数。</p><figure class="highlight js"><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">socket.onerror = <span class="function"><span class="keyword">function</span>(<span class="params">event</span>) </span>{</span><br><span class="line"> <span class="comment">// handle error event</span></span><br><span class="line">};</span><br><span class="line"></span><br><span class="line">socket.addEventListener(<span class="string">"error"</span>, <span class="function"><span class="keyword">function</span>(<span class="params">event</span>) </span>{</span><br><span class="line"> <span class="comment">// handle error event</span></span><br><span class="line">});</span><br></pre></td></tr></table></figure><h4 id="逆向案例介绍"><a href="#逆向案例介绍" class="headerlink" title="逆向案例介绍"></a>逆向案例介绍</h4><h5 id="蝌蚪聊天室"><a href="#蝌蚪聊天室" class="headerlink" title="蝌蚪聊天室"></a>蝌蚪聊天室</h5><p>网址:<a href="http://kedou.workerman.net/">http://kedou.workerman.net/</a></p><p>按照JS逆向的步骤,即抓包,分析包信息,调试,本地运行。先进行抓包,网络面板过滤器选择WS,如下:</p><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="https://img.heshipeng.com/202203272250420.png?watermark/2/text/5YWz5rOo5b6u5L-h5YWs5LyX5Y-377ya6YCG5ZCR5LiA5q2l5q2l/font/5a6L5L2T/fontsize/300" alt="image-20220327225044186" title=""> </div> <div class="image-caption">image-20220327225044186</div> </figure><p>点中这个WebSocket请求,然后切换到Message面板,如图:</p><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="https://img.heshipeng.com/202203272253279.png?watermark/2/text/5YWz5rOo5b6u5L-h5YWs5LyX5Y-377ya6YCG5ZCR5LiA5q2l5q2l/font/5a6L5L2T/fontsize/300" alt="image-20220327225356124" title=""> </div> <div class="image-caption">image-20220327225356124</div> </figure><p>这个Message信息是实时的消息,因为WebSocket一经连接,除非断开就会一直存在,会实时发送消息。上边每条消息前面都有一个箭头,红色的表示服务器发给客户端的,绿色的箭头表示我们发送给服务器的。随便选中一条信息,可以看到它的详细信息,很显然这里的消息格式是使用JSON。</p><p>接下来就是断点分析,我们可以通过这个WebSocket请求的Initiator找到相应的代码:</p><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="https://img.heshipeng.com/202203272301258.png?watermark/2/text/5YWz5rOo5b6u5L-h5YWs5LyX5Y-377ya6YCG5ZCR5LiA5q2l5q2l/font/5a6L5L2T/fontsize/300" alt="image-20220327230133082" title=""> </div> <div class="image-caption">image-20220327230133082</div> </figure><p>也可以通过全局搜索关键字<code>.onopen</code>,如图:</p><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="https://img.heshipeng.com/202203272303646.png?watermark/2/text/5YWz5rOo5b6u5L-h5YWs5LyX5Y-377ya6YCG5ZCR5LiA5q2l5q2l/font/5a6L5L2T/fontsize/300" alt="image-20220327230318560" title=""> </div> <div class="image-caption">image-20220327230318560</div> </figure><p>不管用什么方法,都会定位到如下的代码段:</p><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="https://img.heshipeng.com/202203272304410.png?watermark/2/text/5YWz5rOo5b6u5L-h5YWs5LyX5Y-377ya6YCG5ZCR5LiA5q2l5q2l/font/5a6L5L2T/fontsize/300" alt="image-20220327230426306" title=""> </div> <div class="image-caption">image-20220327230426306</div> </figure><p>点进去<code>onMessage</code>方法, 然后打上断点,然后刷新网页,成功断上:</p><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="https://img.heshipeng.com/202203272305422.png?watermark/2/text/5YWz5rOo5b6u5L-h5YWs5LyX5Y-377ya6YCG5ZCR5LiA5q2l5q2l/font/5a6L5L2T/fontsize/300" alt="image-20220327230536234" title=""> </div> <div class="image-caption">image-20220327230536234</div> </figure><p>这里的数据消息比较简单,明文JSON,没有任何的加密。</p><h4 id="参考链接"><a href="#参考链接" class="headerlink" title="参考链接"></a>参考链接</h4><p><a href="https://www.ruanyifeng.com/blog/2017/05/websocket.html">WebSocket 教程</a></p><h3 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h3><p>WebSocket与HTTP都是网络传输协议,它们的相同点是:</p><ul><li>建立在TCP之上,通过TCP协议来传输数据</li><li>都是可靠性传输协议</li><li>都是应用层协议</li></ul><p>它们的不同点:</p><ul><li>WebSocket是HTML5中的协议,支持持久连接,HTTP不支持持久连接</li><li>HTTP是单向协议,只能由客户端发起,做不到服务器主动向客户端推送信息</li></ul>]]></content>
<summary type="html"><div align="center"><iframe width="560" height="315" src="https://www.youtube.com/embed/Hlp8XD0R5qo" title="YouTube video player" frameborde</summary>
<category term="JS逆向" scheme="http://example.com/categories/JS%E9%80%86%E5%90%91/"/>
<category term="JS" scheme="http://example.com/tags/JS/"/>
<category term="逆向" scheme="http://example.com/tags/%E9%80%86%E5%90%91/"/>
<category term="WebSockets" scheme="http://example.com/tags/WebSockets/"/>
</entry>
<entry>
<title>JS逆向之加解密进阶篇</title>
<link href="http://example.com/JS%E9%80%86%E5%90%91%E4%B9%8B%E5%8A%A0%E8%A7%A3%E5%AF%86%E8%BF%9B%E9%98%B6%E7%AF%87/"/>
<id>http://example.com/JS%E9%80%86%E5%90%91%E4%B9%8B%E5%8A%A0%E8%A7%A3%E5%AF%86%E8%BF%9B%E9%98%B6%E7%AF%87/</id>
<published>2022-03-22T12:43:08.000Z</published>
<updated>2022-03-23T13:51:30.923Z</updated>
<content type="html"><![CDATA[<h4 id="不一样的加密算法"><a href="#不一样的加密算法" class="headerlink" title="不一样的加密算法"></a>不一样的加密算法</h4><h5 id="栅栏密码"><a href="#栅栏密码" class="headerlink" title="栅栏密码"></a>栅栏密码</h5><p>栅栏密码(Rail-fence Cipher)就是把要加密的明文分为N个一组,然后把每组的第一个字符组合,每组的第二个字符组合…每组的第N个(最后一个分组可能不足N个)个字符组合,最后把他们全部连接起来就是密文。这里以2栏栅栏加密为例。</p><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="https://img.heshipeng.com/202203221629016.png?watermark/2/text/5YWz5rOo5b6u5L-h5YWs5LyX5Y-377ya6YCG5ZCR5LiA5q2l5q2l/font/5a6L5L2T/fontsize/300" alt="image-20220322162944513" title=""> </div> <div class="image-caption">image-20220322162944513</div> </figure><p>以一句话Are you ok为例:</p><ol><li>去空格,结果为Areyouok</li><li><p>分组,结果为Ar ey ou ok</p></li><li><p>重组</p><ul><li>第一组:Aeoo</li><li>第二组:ryuk</li></ul></li><li>生成密文,结果为:Aeooryuk</li></ol><p><strong>Python代码演示</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><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="function"><span class="keyword">def</span> <span class="title">encode</span>(<span class="params">flag, num</span>):</span></span><br><span class="line"> <span class="keyword">from</span> math <span class="keyword">import</span> ceil</span><br><span class="line"> length = <span class="built_in">len</span>(flag)</span><br><span class="line"> lines = ceil(length / num)</span><br><span class="line"> arr = [flag[i:i+num] <span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">0</span>, lines * num, num)]</span><br><span class="line"> flag = <span class="string">''</span></span><br><span class="line"> <span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(<span class="built_in">len</span>(arr[<span class="number">0</span>])):</span><br><span class="line"> <span class="keyword">for</span> j <span class="keyword">in</span> arr:</span><br><span class="line"> <span class="keyword">try</span>:</span><br><span class="line"> flag += j[i]</span><br><span class="line"> <span class="keyword">except</span>:</span><br><span class="line"> <span class="keyword">pass</span></span><br><span class="line"></span><br><span class="line"> <span class="keyword">return</span> flag</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">decode</span>(<span class="params">flag, num</span>):</span></span><br><span class="line"> length = <span class="built_in">len</span>(flag) <span class="comment"># flag的长度</span></span><br><span class="line"> lines = length // num <span class="comment"># 判断共有几层并减一</span></span><br><span class="line"> remainder = num * (lines + <span class="number">1</span>) - length <span class="comment"># 相差的数量</span></span><br><span class="line"> <span class="comment"># 补全flag</span></span><br><span class="line"> result = flag[:length-lines*remainder]</span><br><span class="line"> <span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(remainder-<span class="number">1</span>, -<span class="number">1</span>, -<span class="number">1</span>):</span><br><span class="line"> result += flag[length - (i + <span class="number">1</span>) * lines:length - i * lines] + <span class="string">'*'</span></span><br><span class="line"> <span class="comment"># 还原flag</span></span><br><span class="line"> lines += <span class="number">1</span></span><br><span class="line"> arr = [result[i:i + lines] <span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">0</span>, <span class="built_in">len</span>(result), lines)]</span><br><span class="line"> flag = <span class="string">''</span></span><br><span class="line"> <span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(<span class="built_in">len</span>(arr[<span class="number">0</span>])):</span><br><span class="line"> <span class="keyword">for</span> j <span class="keyword">in</span> arr:</span><br><span class="line"> flag += j[i]</span><br><span class="line"> <span class="keyword">return</span> flag[:length]</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> __name__ == <span class="string">'__main__'</span>:</span><br><span class="line"> <span class="built_in">print</span>(encode(<span class="string">"Are you ok"</span>, <span class="number">2</span>))<span class="comment"># Aeyuor o k</span></span><br><span class="line"> <span class="built_in">print</span>(decode(<span class="string">"Aeyuor o k"</span>, <span class="number">2</span>))<span class="comment"># Are you ok</span></span><br><span class="line"></span><br></pre></td></tr></table></figure><h5 id="列位移密码"><a href="#列位移密码" class="headerlink" title="列位移密码"></a>列位移密码</h5><p>列位移密码(Columnar Transposition Cipher)是一种比较简单,易于实现的换位密码,通过一个简单的规则将明文打乱混合成密文。将明文填入事先约定填充的行列数,如果明文不能填充完表格,可以约定使用某个字母进行填充,然后根据密钥在字母表中出现的先后顺序进行编号,根据编码即可推出密文。</p><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="https://img.heshipeng.com/202203221940630.png?watermark/2/text/5YWz5rOo5b6u5L-h5YWs5LyX5Y-377ya6YCG5ZCR5LiA5q2l5q2l/font/5a6L5L2T/fontsize/300" alt="image-20220322194007474" title=""> </div> <div class="image-caption">image-20220322194007474</div> </figure><p>还是以那句话Are you ok为明文,以car为密钥,x为填充字符:</p><ol><li>去空格,结果为:Areyouok</li><li>因为密钥car的长度为3,所以以3个字符为一个单位分组,如下:</li></ol><div class="table-container"><table><thead><tr><th style="text-align:center">c</th><th style="text-align:center">a</th><th style="text-align:center">r</th></tr></thead><tbody><tr><td style="text-align:center">A</td><td style="text-align:center">r</td><td style="text-align:center">e</td></tr><tr><td style="text-align:center">y</td><td style="text-align:center">o</td><td style="text-align:center">u</td></tr><tr><td style="text-align:center">o</td><td style="text-align:center">k</td><td style="text-align:center">x</td></tr></tbody></table></div><p>最后一组只有2个元素不足三个,所以用x填充。由于密钥car的字母顺序排序是a>c>r,所以上述列排序也要根据这个顺序重新排列,如下:</p><div class="table-container"><table><thead><tr><th style="text-align:center">a</th><th style="text-align:center">c</th><th style="text-align:center">r</th></tr></thead><tbody><tr><td style="text-align:center">r</td><td style="text-align:center">A</td><td style="text-align:center">e</td></tr><tr><td style="text-align:center">o</td><td style="text-align:center">y</td><td style="text-align:center">u</td></tr><tr><td style="text-align:center">k</td><td style="text-align:center">o</td><td style="text-align:center">x</td></tr></tbody></table></div><p>重新排序完成之后,从第一列到最后一列按列取值组成的字符串就是密文了,很显然密钥是不能出现在明文中的,填充字符可以出现在明文中,所以每一列的第一个字符被忽略,最终的密文为:rokAyoeux。</p><p><strong>Python代码演示</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="keyword">from</span> pycipher <span class="keyword">import</span> ColTrans</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">ColTrans(<span class="string">"car"</span>).encipher(<span class="string">'Are you ok'</span>) <span class="comment"># ROKAYOEU</span></span><br><span class="line">ColTrans(<span class="string">"car"</span>).decipher(<span class="string">'ROKAYOEU'</span>) <span class="comment"># AREYOUOK</span></span><br></pre></td></tr></table></figure><h5 id="凯撒密码"><a href="#凯撒密码" class="headerlink" title="凯撒密码"></a>凯撒密码</h5><p>凯撒密码(Caesar Cipher或称凯撒加密,凯撒变换,变换加密,位移加密)是一种替换加密,明文中的所有字母都在字母表上向后(或向前)按照一个固定的数目进行偏移后被替换成密文。例,当偏移量是3的时候,所有的字母A都将被替换成D,B变成E,依此类推。</p><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="https://img.heshipeng.com/202203222207955.png?watermark/2/text/5YWz5rOo5b6u5L-h5YWs5LyX5Y-377ya6YCG5ZCR5LiA5q2l5q2l/font/5a6L5L2T/fontsize/300" alt="image-20220322220707572" title=""> </div> <div class="image-caption">image-20220322220707572</div> </figure><p> 依旧以Are you ok为例:</p><ol><li><p>去除空格,变为Areyouok</p></li><li><p>以5作为偏移映射,A对应F,r对应w,e对应j,依此往下推,转换后的密文为Fwjdtztp。</p></li></ol><p><strong>Python代码演示</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><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></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">encrypt_char</span>(<span class="params">char, key</span>):</span></span><br><span class="line"> <span class="keyword">return</span> <span class="built_in">chr</span>(<span class="built_in">ord</span>(<span class="string">'A'</span>) + (<span class="built_in">ord</span>(char) - <span class="built_in">ord</span>(<span class="string">'A'</span>) + key) % <span class="number">26</span>)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">encrypt_message</span>(<span class="params">message, key</span>):</span></span><br><span class="line"> message = message.upper()</span><br><span class="line"> cipher = <span class="string">''</span></span><br><span class="line"> <span class="keyword">for</span> char <span class="keyword">in</span> message:</span><br><span class="line"> <span class="keyword">if</span> char <span class="keyword">not</span> <span class="keyword">in</span> <span class="string">' ,.'</span>:</span><br><span class="line"> cipher += encrypt_char(char, key)</span><br><span class="line"> <span class="keyword">else</span>:</span><br><span class="line"> cipher += char</span><br><span class="line"> <span class="keyword">return</span> cipher</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">decrypt_char</span>(<span class="params">char, key</span>):</span></span><br><span class="line"> <span class="keyword">return</span> <span class="built_in">chr</span>(<span class="built_in">ord</span>(<span class="string">'A'</span>) + (<span class="built_in">ord</span>(char) - <span class="built_in">ord</span>(<span class="string">'A'</span>) + <span class="number">26</span> - key) % <span class="number">26</span>)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">decrypt_message</span>(<span class="params">cipher, key</span>):</span></span><br><span class="line"> cipher = cipher.upper()</span><br><span class="line"> message = <span class="string">''</span></span><br><span class="line"> <span class="keyword">for</span> char <span class="keyword">in</span> cipher:</span><br><span class="line"> <span class="keyword">if</span> char <span class="keyword">not</span> <span class="keyword">in</span> <span class="string">' ,.'</span>:</span><br><span class="line"> message += decrypt_char(char, key)</span><br><span class="line"> <span class="keyword">else</span>:</span><br><span class="line"> message += char</span><br><span class="line"> <span class="keyword">return</span> message</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> __name__ == <span class="string">'__main__'</span>:</span><br><span class="line"> <span class="built_in">print</span>(encrypt_message(<span class="string">"Are you ok"</span>, <span class="number">5</span>))<span class="comment"># FWJ DTZ TP</span></span><br><span class="line"> <span class="built_in">print</span>(decrypt_message(<span class="string">"FWJ DTZ TP"</span>, <span class="number">5</span>)) <span class="comment"># ARE YOU OK</span></span><br></pre></td></tr></table></figure><h5 id="其它加密"><a href="#其它加密" class="headerlink" title="其它加密"></a>其它加密</h5><p>JSfuck仅使用6个字符,即[]+()!,来编写js程序。</p><p>jother是一种可以运用于javascript语言中利用少量字符构造精简的匿名函数方法对于字符串进行的编码方式。其中8个少量字符包括:!+()[]{}。只用这些字符就可以完成任意字符串的编码。</p><p>jjencode将JS代码转换成只有符号的字符串。</p><p>aaencode可以将JS代码转换成常用的网络表情,也就是我们说的颜文字JS加密。</p><h4 id="JS加密上的实例分析与变化"><a href="#JS加密上的实例分析与变化" class="headerlink" title="JS加密上的实例分析与变化"></a>JS加密上的实例分析与变化</h4><h5 id="凯撒加密变种加密算法分析"><a href="#凯撒加密变种加密算法分析" class="headerlink" title="凯撒加密变种加密算法分析"></a>凯撒加密变种加密算法分析</h5><p>密文:lQyjcvi|dcQR pktg t 2 cxl 9tixQR0 wxujzzxg0 gxijgc cxl 9tixQR V t 3 ZYY0rQRR</p><p>算法:</p><figure class="highlight js"><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="function"><span class="keyword">function</span> <span class="title">_$aU</span>(<span class="params">_$Fj</span>) </span>{</span><br><span class="line"> <span class="keyword">var</span> _$U4 = _$Fj.length;</span><br><span class="line"> <span class="keyword">var</span> _$_L, _$cu = <span class="keyword">new</span> <span class="built_in">Array</span>(_$U4 - <span class="number">1</span>), _$h9 = _$Fj.charCodeAt(<span class="number">0</span>) - <span class="number">97</span>;</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">var</span> _$1B = <span class="number">0</span>, _$lp = <span class="number">1</span>; _$lp < _$U4; ++_$lp) {</span><br><span class="line"> _$_L = _$Fj.charCodeAt(_$lp);</span><br><span class="line"> <span class="keyword">if</span> (_$_L >= <span class="number">40</span> && _$_L < <span class="number">92</span>) {</span><br><span class="line"> _$_L += _$h9;</span><br><span class="line"> <span class="keyword">if</span> (_$_L >= <span class="number">92</span>)</span><br><span class="line"> _$_L -= <span class="number">52</span>;</span><br><span class="line"> } <span class="keyword">else</span> <span class="keyword">if</span> (_$_L >= <span class="number">97</span> && _$_L < <span class="number">127</span>) {</span><br><span class="line"> _$_L += _$h9;</span><br><span class="line"> <span class="keyword">if</span> (_$_L >= <span class="number">127</span>)</span><br><span class="line"> _$_L -= <span class="number">30</span>;</span><br><span class="line"> }</span><br><span class="line"> _$cu[_$1B++] = _$_L;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> <span class="built_in">String</span>.fromCharCode.apply(<span class="literal">null</span>, _$cu);</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>使用上述算法解密密文结果如下:</p><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="https://img.heshipeng.com/202203231807071.png?watermark/2/text/5YWz5rOo5b6u5L-h5YWs5LyX5Y-377ya6YCG5ZCR5LiA5q2l5q2l/font/5a6L5L2T/fontsize/300" alt="image-20220323180744951" title=""> </div> <div class="image-caption">image-20220323180744951</div> </figure><p>可以看到上面是一个无限debugger的字符串,然后通过eval调用可以达到无限debugger的目的,通过前面学习了凯撒密码之后,我们除了可以使用hook的方式绕过无限debugger,还可以使用凯撒密码达到目的,我们删除密文中的<code>wxujzzxg0</code>,然后执行:</p><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="https://img.heshipeng.com/202203231813548.png?watermark/2/text/5YWz5rOo5b6u5L-h5YWs5LyX5Y-377ya6YCG5ZCR5LiA5q2l5q2l/font/5a6L5L2T/fontsize/300" alt="image-20220323181330460" title=""> </div> <div class="image-caption">image-20220323181330460</div> </figure><p>可以看到,同样干掉了debugger关键字。</p><h4 id="常规加密算法的变种"><a href="#常规加密算法的变种" class="headerlink" title="常规加密算法的变种"></a>常规加密算法的变种</h4><h5 id="借鉴古典密码学改造现在密码学"><a href="#借鉴古典密码学改造现在密码学" class="headerlink" title="借鉴古典密码学改造现在密码学"></a>借鉴古典密码学改造现在密码学</h5><p>看一段AES实际案例(某常见人机验证码代码部分案例)</p><figure class="highlight js"><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="keyword">var</span> CryptoJS = <span class="built_in">require</span>(<span class="string">"crypto-js"</span>);</span><br><span class="line"><span class="keyword">const</span> key = <span class="string">"ABC1234567891234"</span>;</span><br><span class="line"><span class="keyword">const</span> iv = <span class="string">"1234567812345678"</span>;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">encrypt</span>(<span class="params">text</span>) </span>{</span><br><span class="line"> <span class="keyword">return</span> CryptoJS.AES.encrypt(text, CryptoJS.enc.Utf8.parse(key), {</span><br><span class="line"> <span class="attr">iv</span>: CryptoJS.enc.Utf8.parse(iv),</span><br><span class="line"> <span class="attr">mode</span>: CryptoJS.mode.CBC,</span><br><span class="line"> <span class="attr">padding</span>: CryptoJS.pad.Pkcs7</span><br><span class="line"> })</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">decrypt</span>(<span class="params">text</span>) </span>{</span><br><span class="line"> <span class="keyword">var</span> result = CryptoJS.AES.decrypt(text, CryptoJS.enc.Utf8.parse(key), {</span><br><span class="line"> <span class="attr">iv</span>: CryptoJS.enc.Utf8.parse(iv),</span><br><span class="line"> <span class="attr">mode</span>: CryptoJS.mode.CBC,</span><br><span class="line"> <span class="attr">padding</span>: CryptoJS.pad.Pkcs7</span><br><span class="line"> });</span><br><span class="line"> <span class="keyword">return</span> result.toString(CryptoJS.enc.Utf8);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">let</span> encrypted = encrypt(<span class="string">"123456"</span>);</span><br><span class="line"><span class="keyword">var</span> s = [];</span><br><span class="line"><span class="keyword">for</span> (<span class="keyword">var</span> i = <span class="number">0</span>; i < encrypted.ciphertext.sigBytes; i++) {</span><br><span class="line"> <span class="keyword">var</span> x = encrypted.ciphertext.words[i >>> <span class="number">2</span>] >>> <span class="number">24</span> - i % <span class="number">4</span> * <span class="number">8</span> & <span class="number">255</span>;</span><br><span class="line"> s.push(x)</span><br><span class="line">}</span><br><span class="line"><span class="keyword">let</span> data = encrypt(<span class="built_in">String</span>.fromCharCode.apply(<span class="literal">null</span>, s)).toString();</span><br><span class="line"><span class="built_in">console</span>.log(encrypted.toString());<span class="comment">// 6S3YRylMmp9vIFOplWxypw==</span></span><br><span class="line"><span class="built_in">console</span>.log(data);<span class="comment">// u79HHbvmZtoSScagKO2JsQzytZH1L5SJGSh338DbaMA=</span></span><br><span class="line"><span class="built_in">console</span>.log(decrypt(data));<span class="comment">// é-ØG)Lo S©lr§</span></span><br></pre></td></tr></table></figure><p>如上,假如加密代码出现了cryptojs关键字,然后让人以为是传统的cryptojs加密,但是实际上做了一点修改,导致如果直接拿data去做解密,会得到的乱码,达到混淆视听的目的。</p>]]></content>
<summary type="html"><h4 id="不一样的加密算法"><a href="#不一样的加密算法" class="headerlink" title="不一样的加密算法"></a>不一样的加密算法</h4><h5 id="栅栏密码"><a href="#栅栏密码" class="headerlink" </summary>
<category term="JS逆向" scheme="http://example.com/categories/JS%E9%80%86%E5%90%91/"/>
<category term="JS" scheme="http://example.com/tags/JS/"/>
<category term="逆向" scheme="http://example.com/tags/%E9%80%86%E5%90%91/"/>
</entry>
<entry>
<title>你不可不知的CSS反爬</title>
<link href="http://example.com/%E4%BD%A0%E4%B8%8D%E5%8F%AF%E4%B8%8D%E7%9F%A5%E7%9A%84CSS%E5%8F%8D%E7%88%AC/"/>
<id>http://example.com/%E4%BD%A0%E4%B8%8D%E5%8F%AF%E4%B8%8D%E7%9F%A5%E7%9A%84CSS%E5%8F%8D%E7%88%AC/</id>
<published>2022-03-20T10:19:57.000Z</published>
<updated>2022-03-21T15:05:05.053Z</updated>
<content type="html"><![CDATA[<h4 id="为什么使用CSS做反爬"><a href="#为什么使用CSS做反爬" class="headerlink" title="为什么使用CSS做反爬"></a>为什么使用CSS做反爬</h4><ol><li>成本低</li></ol><ul><li>只需要前段混淆样式</li><li>不需要复杂的加密技术</li><li>不需要验证码,流量检测等额外配置</li></ul><p>因此,对于企业来说,仅仅用一些CSS技巧就可以防住爬虫,可以不需要投入很多资源,这样会节省资金以及时间资源。</p><ol><li>效果好</li></ol><ul><li>难以识别<ul><li>抓取内容与预期内容相近,容易误导爬虫工程师</li><li>反爬措施不容易发觉</li><li>可以混淆竞争对手</li></ul></li><li>没有成熟的破解套路<ul><li>破解CSS混淆的反爬措施需要想象力</li><li>没有统一的破解套路,需要人工干预</li></ul></li></ul><p>用CSS的反爬效果好,所以企业可以花很少的时间成本来获取较大的反爬效益,这种措施显得尤为具有吸引力。</p><h4 id="CSS反爬类别"><a href="#CSS反爬类别" class="headerlink" title="CSS反爬类别"></a>CSS反爬类别</h4><h5 id="图片伪装反爬虫"><a href="#图片伪装反爬虫" class="headerlink" title="图片伪装反爬虫"></a>图片伪装反爬虫</h5><p>图片伪装指的是将带有文字的图片与正常文字混合在一起,以达到“鱼目混珠”的效果。这种混淆方式并不会影响用户阅读,但是可以让爬虫程序无法获得“所见”的文字内容。图片反爬虫通常会将一些关键信息以图片的形式展示出来。</p><p><strong>举例</strong></p><p>网址:aHR0cHM6Ly93d3cuZ3hyYy5jb20vY29tcGFueS9hYmFmMjU1Yy1mZjE3LTQ2YjAtOWM0Zi03YzkyOTZiY2FmMTc=,把电话信息通过图片的形式展示,如下:</p><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="https://img.heshipeng.com/202203201930370.png?watermark/2/text/5YWz5rOo5b6u5L-h5YWs5LyX5Y-377ya6YCG5ZCR5LiA5q2l5q2l/font/5a6L5L2T/fontsize/300" alt="image-20220320193029220" title=""> </div> <div class="image-caption">image-20220320193029220</div> </figure><p>对于这种图片的文字提取,需要用到光学字符识别技术OCR。</p><p>推荐几个好用的OCR库:</p><ul><li><p><a href="https://github.com/PaddlePaddle/PaddleOCR">PaddleOCR</a> 。</p></li><li><p><a href="https://github.com/JaidedAI/EasyOCR">EasyOCR</a></p></li><li><p><a href="https://github.com/madmaze/pytesseract">pytesseract</a></p></li></ul><p>本来想用PaddleOCR来进行文字提取的,但是我用的是MacOS M1芯,安装的时候有各种问题,所以这里也先不折腾,就用了EasyOCR进行识别,识别结果如下:</p><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="https://img.heshipeng.com/202203202215407.png?watermark/2/text/5YWz5rOo5b6u5L-h5YWs5LyX5Y-377ya6YCG5ZCR5LiA5q2l5q2l/font/5a6L5L2T/fontsize/300" alt="image-20220320221502215" title=""> </div> <div class="image-caption">image-20220320221502215</div> </figure><p>虽然爬虫工程师可以借助渲染工具获得页面渲染后的网页文本,但爬虫无法直接从图片这种媒体文件中获取字符。光学字符识别技术也有一定的缺陷,在面对扭曲文字、生僻字和有复杂干扰信息的图片时,它就无法发挥作用了。</p><h5 id="利用字体反爬虫"><a href="#利用字体反爬虫" class="headerlink" title="利用字体反爬虫"></a>利用字体反爬虫</h5><p>字体反爬原理:</p><ul><li>主要利用了font-family这个属性,例如设置为my-font</li><li>在HTML里不常见的unicode</li><li>在CSS的字体中将其映射到常见的(可读的)字体,例如数字</li><li>爬虫在抓取数据的时候只能抓到unicode,而不是真实的数据</li></ul><p>解决方案:</p><ul><li>下载woff字体文件,转为tff文件</li><li>用<a href="https://kekee000.github.io/fonteditor/index.html">百度字体编辑器</a>打开,并确定其unicode与实际的映射关系</li><li>将下载的HTML内容按照映射关系进行替换</li><li>解析HTML并获取正确的数据</li></ul><p>难点:</p><ul><li>有些网站会动态的生成woff,这种反爬措施比较难以自动化绕开</li></ul><p><strong>举例</strong></p><p>网址:aHR0cHM6Ly9jbHViLmF1dG9ob21lLmNvbS5jbi9iYnMvdGhyZWFkL2QxNzUxYzdiZDA1MzlkZTAvNzkyMjk2NjgtMS5odG1s,部分文字以特殊字体展示,如图:</p><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="https://img.heshipeng.com/202203212009466.png?watermark/2/text/5YWz5rOo5b6u5L-h5YWs5LyX5Y-377ya6YCG5ZCR5LiA5q2l5q2l/font/5a6L5L2T/fontsize/300" alt="image-20220321200937272" title=""> </div> <div class="image-caption">image-20220321200937272</div> </figure><p>我们通过抓包,拿到对应的字体文件,然后用百度文字编辑器打开:</p><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="https://img.heshipeng.com/202203212010776.png?watermark/2/text/5YWz5rOo5b6u5L-h5YWs5LyX5Y-377ya6YCG5ZCR5LiA5q2l5q2l/font/5a6L5L2T/fontsize/300" alt="image-20220321201048528" title=""> </div> <div class="image-caption">image-20220321201048528</div> </figure><p>拷贝大字对应的那一个奇怪的字符,转为unicode编码,如图:</p><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="https://img.heshipeng.com/202203212012477.png?watermark/2/text/5YWz5rOo5b6u5L-h5YWs5LyX5Y-377ya6YCG5ZCR5LiA5q2l5q2l/font/5a6L5L2T/fontsize/300" alt="image-20220321201232378" title=""> </div> <div class="image-caption">image-20220321201232378</div> </figure><p>可以看到ed68正好对应上边的$ED68这个汉子,即大。这个网站的字体是动态加载的,每次使用特殊字体的汉子是随机的,增加了反爬的难度。</p><h5 id="CSS偏移反爬虫"><a href="#CSS偏移反爬虫" class="headerlink" title="CSS偏移反爬虫"></a>CSS偏移反爬虫</h5><p>CSS 偏移反爬虫指的是利用 CSS 样式将乱序的文字排版为人类正常阅 读顺序的行为。这个概念不是很好理解,我们可以通过对比两段文字 来加深对这个概念的理解:</p><ul><li><p>HTML 文本中的文字:我的学号是 1308205,我在北京大学读书。 </p></li><li><p>浏览器显示的文字:我的学号是 1380205,我在北京大学读书。</p></li></ul><p>爬虫提取到的学号是 1308205,但用户在浏览器中看到的却是 1380205。如果不细心观察,爬虫工程师很容易被爬取结果糊弄。这种混淆方法和图片伪装一样,是不会影响用户阅读的。让人好奇的是浏览器如何将 HTML 文本中的数字按照开发者的意愿排序或放置呢? 这种放置规则是如何运作的呢?</p><p><strong>举例</strong></p><p>网址:<a href="http://www.porters.vip/confusion/flight.html#">http://www.porters.vip/confusion/flight.html#</a></p><p>可以看到航班机票不能直接拿到,而是用到CSS偏移:</p><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="https://img.heshipeng.com/202203212048770.png?watermark/2/text/5YWz5rOo5b6u5L-h5YWs5LyX5Y-377ya6YCG5ZCR5LiA5q2l5q2l/font/5a6L5L2T/fontsize/300" alt="image-20220321204846610" title=""> </div> <div class="image-caption">image-20220321204846610</div> </figure><p>我们可以看出规律,77764排成一列,每个宽度为16px,然后6这个数字左偏移32px就变成了76774,4这个数字向左偏移64个px就变成了46777,由于设置展示宽度为48px,所以只看到前三个数字,就刚好是467。</p><h5 id="利用伪类反爬虫"><a href="#利用伪类反爬虫" class="headerlink" title="利用伪类反爬虫"></a>利用伪类反爬虫</h5><p>反爬原理:</p><ul><li>不直接将内容展现到html的元素中</li><li>通过伪类的content属性将要展示的值展示出来。<code>例如:鼠标悬浮的时候展示数据</code></li></ul><p>解决方案:</p><ul><li>利用playwright或者pyppeteer这样的自动化测试工具</li><li>在页面上执行下面的JS代码,即可获取content。注意:before是伪类,也可能是after。</li></ul><figure class="highlight js"><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="keyword">const</span> el = <span class="built_in">document</span>.querySelector(<span class="string">"类选择器"</span>)</span><br><span class="line"><span class="keyword">const</span> styles = getComputedStyle(el,<span class="string">'before'</span>)</span><br><span class="line"><span class="built_in">console</span>.log(styles.content) # 打印数据值</span><br></pre></td></tr></table></figure><h5 id="利用字符切割反爬虫"><a href="#利用字符切割反爬虫" class="headerlink" title="利用字符切割反爬虫"></a>利用字符切割反爬虫</h5><p>反爬原理:</p><ul><li>将字符串用标签分割</li><li>由于是内联块级(inline-block),可以一行展示</li><li>通常还混淆有不现实的标签(display:none)</li></ul><p>解决方案:</p><ul><li>将内联块级标签的innerText拼接起来</li><li>注意过滤掉所有的display:none的属性</li></ul><h5 id="SVG映射反爬虫"><a href="#SVG映射反爬虫" class="headerlink" title="SVG映射反爬虫"></a>SVG映射反爬虫</h5><p>SVG 是用于描述二维矢量图形的一种图形格式。它基于 XML 描述图 形,对图形进行放大或缩小操作都不会影响图形质量。矢量图形的这 个特点使得它被广泛应用在 Web 网站中。</p><p>SVG反爬虫手段用矢量图形代替具体的文字,不会影响用户正常阅读,但爬虫程序 却无法像读取文字那样获得SVG图形中的内容。由于 SVG中的图形代表的也是一个个文字,所以在使用时必须在后端或前端将真实的文字与对应的SVG图形进行映射和替换,这种反爬虫手段被称为SVG映射反爬虫。</p><p><strong>举例</strong>:</p><p>网址:<a href="http://www.porters.vip/confusion/food.html">http://www.porters.vip/confusion/food.html</a></p><p>可以看到,很多数字在原始HTML页面中都没有,而是以标签形式出现,如下图:</p><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="https://img.heshipeng.com/202203212226921.png?watermark/2/text/5YWz5rOo5b6u5L-h5YWs5LyX5Y-377ya6YCG5ZCR5LiA5q2l5q2l/font/5a6L5L2T/fontsize/300" alt="image-20220321222654775" title=""> </div> <div class="image-caption">image-20220321222654775</div> </figure><p>而商家电话号码处的显示就更奇怪了,一 个数字都没有。商家电话对应的 HTML 代码如下:</p><figure class="highlight html"><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="tag"><<span class="name">div</span> <span class="attr">class</span>=<span class="string">"col more"</span>></span>电话:</span><br><span class="line"> <span class="tag"><<span class="name">d</span> <span class="attr">class</span>=<span class="string">"vhkbvu"</span>></span><span class="tag"></<span class="name">d</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">d</span> <span class="attr">class</span>=<span class="string">"vhk08k"</span>></span><span class="tag"></<span class="name">d</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">d</span> <span class="attr">class</span>=<span class="string">"vhk08k"</span>></span><span class="tag"></<span class="name">d</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">d</span> <span class="attr">class</span>=<span class="string">""</span>></span>-<span class="tag"></<span class="name">d</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">d</span> <span class="attr">class</span>=<span class="string">"vhk84t"</span>></span><span class="tag"></<span class="name">d</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">d</span> <span class="attr">class</span>=<span class="string">"vhk6zl"</span>></span><span class="tag"></<span class="name">d</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">d</span> <span class="attr">class</span>=<span class="string">"vhkqsc"</span>></span><span class="tag"></<span class="name">d</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">d</span> <span class="attr">class</span>=<span class="string">"vhkqsc"</span>></span><span class="tag"></<span class="name">d</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">d</span> <span class="attr">class</span>=<span class="string">"vhk6zl"</span>></span><span class="tag"></<span class="name">d</span>></span></span><br><span class="line"><span class="tag"></<span class="name">div</span>></span></span><br></pre></td></tr></table></figure><p>我们推测它是用d标签来占位代表一个数字,通过class属性来区分代表数字的含义。我们通过页面上已经展示的数字,来建立一个映射关系,如下:</p><figure class="highlight html"><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="tag"><<span class="name">d</span> <span class="attr">class</span>=<span class="string">"vhk08k"</span>></span><span class="tag"></<span class="name">d</span>></span> <span class="comment"><!-- 0 --></span></span><br><span class="line"><span class="tag"><<span class="name">d</span> <span class="attr">class</span>=<span class="string">"vhk6zl"</span>></span><span class="tag"></<span class="name">d</span>></span> <span class="comment"><!-- 1 --></span></span><br><span class="line"><span class="tag"><<span class="name">d</span> <span class="attr">class</span>=<span class="string">"vhk9or"</span>></span><span class="tag"></<span class="name">d</span>></span> <span class="comment"><!-- 2 --></span></span><br><span class="line"><span class="tag"><<span class="name">d</span> <span class="attr">class</span>=<span class="string">"vhkbvu"</span>></span><span class="tag"></<span class="name">d</span>></span> <span class="comment"><!-- 4 --></span></span><br><span class="line"><span class="tag"><<span class="name">d</span> <span class="attr">class</span>=<span class="string">"vhk84t"</span>></span><span class="tag"></<span class="name">d</span>></span> <span class="comment"><!-- 5 --></span></span><br><span class="line"><span class="tag"><<span class="name">d</span> <span class="attr">class</span>=<span class="string">"vhkqsc"</span>></span><span class="tag"></<span class="name">d</span>></span> <span class="comment"><!-- 7 --></span></span><br><span class="line"><span class="tag"><<span class="name">d</span> <span class="attr">class</span>=<span class="string">"vhkjj4"</span>></span><span class="tag"></<span class="name">d</span>></span> <span class="comment"><!-- 8 --></span></span><br></pre></td></tr></table></figure><p>那剩余的几个数字如3,6,9到哪里去找呢?既然是通过class区分,那么class的样式肯定会在CSS文件中有定义。</p><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="https://img.heshipeng.com/202203212245377.png?watermark/2/text/5YWz5rOo5b6u5L-h5YWs5LyX5Y-377ya6YCG5ZCR5LiA5q2l5q2l/font/5a6L5L2T/fontsize/300" alt="image-20220321224556130" title=""> </div> <div class="image-caption">image-20220321224556130</div> </figure><p>我们去CSS文件中找,果然找到了以下代码:</p><figure class="highlight css"><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="selector-class">.vhk08k</span> {</span><br><span class="line"> <span class="attribute">background</span>: -<span class="number">274px</span> -<span class="number">141px</span>;</span><br><span class="line">}</span><br><span class="line"><span class="selector-class">.vhk6zl</span> {</span><br><span class="line"> <span class="attribute">background</span>: -<span class="number">7px</span> -<span class="number">15px</span>;</span><br><span class="line">}</span><br><span class="line"><span class="selector-class">.vhk9or</span> {</span><br><span class="line"> <span class="attribute">background</span>: -<span class="number">330px</span> -<span class="number">141px</span>;</span><br><span class="line">}</span><br><span class="line"><span class="selector-class">.vhkfln</span> {</span><br><span class="line"> <span class="attribute">background</span>: -<span class="number">428px</span> -<span class="number">15px</span>;</span><br><span class="line">}</span><br><span class="line"><span class="selector-class">.vhkbvu</span> {</span><br><span class="line"> <span class="attribute">background</span>: -<span class="number">386px</span> -<span class="number">97px</span>;</span><br><span class="line">}</span><br><span class="line"><span class="selector-class">.vhk84t</span> {</span><br><span class="line"> <span class="attribute">background</span>: -<span class="number">176px</span> -<span class="number">141px</span>;</span><br><span class="line">}</span><br><span class="line"><span class="selector-class">.vhkvxd</span> {</span><br><span class="line"> <span class="attribute">background</span>: -<span class="number">246px</span> -<span class="number">141px</span>;</span><br><span class="line">}</span><br><span class="line"><span class="selector-class">.vhkqsc</span> {</span><br><span class="line"> <span class="attribute">background</span>: -<span class="number">288px</span> -<span class="number">141px</span>;</span><br><span class="line">}</span><br><span class="line"><span class="selector-class">.vhkjj4</span> {</span><br><span class="line"> <span class="attribute">background</span>: -<span class="number">316px</span> -<span class="number">141px</span>;</span><br><span class="line">}</span><br><span class="line"><span class="selector-class">.vhk0f1</span> {</span><br><span class="line"> <span class="attribute">background</span>: -<span class="number">316px</span> -<span class="number">97px</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>从上到下依次与我们上边推断的几个数字相对应,因此,判定<code><d class="vhkfln"></d></code>对应数字3,<code><d class="vhkvxd"></d></code>对应数字6,<code><d class="vhk0f1"></d></code>对应数字9。至此,就破解了全部的数字了。</p><h4 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h4><p>本文简单介绍了几种常见的CSS反爬,然后举了一些简单的例子加以分析,并没有涉及太多的代码层面,而且涉及到的知识也比较浅显,只是浅尝则止。因为本文是作为CSS反爬的开篇内容,后边会有专门的文章,利用更常见的网站,难度更大的网站,去深入分析每一种CSS反爬。</p>]]></content>
<summary type="html"><h4 id="为什么使用CSS做反爬"><a href="#为什么使用CSS做反爬" class="headerlink" title="为什么使用CSS做反爬"></a>为什么使用CSS做反爬</h4><ol>
<li>成本低</li>
</ol>
<ul>
<li>只需要前</summary>
<category term="爬虫" scheme="http://example.com/categories/%E7%88%AC%E8%99%AB/"/>
<category term="CSS" scheme="http://example.com/tags/CSS/"/>
<category term="爬虫" scheme="http://example.com/tags/%E7%88%AC%E8%99%AB/"/>
</entry>
<entry>
<title>JS逆向之混淆JS手动逆向</title>
<link href="http://example.com/JS%E9%80%86%E5%90%91%E4%B9%8B%E6%B7%B7%E6%B7%86JS%E6%89%8B%E5%8A%A8%E9%80%86%E5%90%91/"/>
<id>http://example.com/JS%E9%80%86%E5%90%91%E4%B9%8B%E6%B7%B7%E6%B7%86JS%E6%89%8B%E5%8A%A8%E9%80%86%E5%90%91/</id>
<published>2022-03-08T12:24:52.000Z</published>
<updated>2022-06-30T03:55:30.326Z</updated>
<content type="html"><![CDATA[<blockquote><p> 免责声明:<strong>本文章中所有内容仅供学习交流,抓包内容、敏感网址、数据接口均已做脱敏处理,严禁用于商业用途和非法用途,否则由此产生的一切后果均与作者无关,若有侵权,请联系我立即删除!</strong></p></blockquote><h4 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h4><p>写作目的:记录手动逆向一个JS高度混淆的网站的整个过程。</p><p>网址:aHR0cHM6Ly81NTI0OTY5Ni5jb206Nzc3Ny8=</p><h4 id="逆向过程"><a href="#逆向过程" class="headerlink" title="逆向过程"></a>逆向过程</h4><p>话不多说,直接开始调试。输入用户名17777777777和密码123456点击登录,弹出验证码:</p><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="https://img.heshipeng.com/202203081636681.png?watermark/2/text/5YWz5rOo5b6u5L-h5YWs5LyX5Y-377ya6YCG5ZCR5LiA5q2l5q2l/font/5a6L5L2T/fontsize/300" alt="image-20220308163635586" title=""> </div> <div class="image-caption">image-20220308163635586</div> </figure><p><strong>一般来说网站如果出现复杂验证码都会配合JS参数加密增加防护等级</strong>。我们抓包抓到2个XHR请求:</p><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="https://img.heshipeng.com/202203081638469.png?watermark/2/text/5YWz5rOo5b6u5L-h5YWs5LyX5Y-377ya6YCG5ZCR5LiA5q2l5q2l/font/5a6L5L2T/fontsize/300" alt="image-20220308163823432" title=""> </div> <div class="image-caption">image-20220308163823432</div> </figure><p>初步推测第一个请求get是获取验证码,第二个请求check是校验验证码。今天的目标就是破解这两个请求的加密参数与返回值。</p><p> 我们观察这2个接口,发现check请求的参数包含get请求的参数,所以只需要解决check请求的参数就行了。</p><p>我们直接看check请求,在Source面板打上XHR断点,断住checkv3.php请求:</p><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="https://img.heshipeng.com/202203081704604.png?watermark/2/text/5YWz5rOo5b6u5L-h5YWs5LyX5Y-377ya6YCG5ZCR5LiA5q2l5q2l/font/5a6L5L2T/fontsize/300" alt="image-20220308170430516" title=""> </div> <div class="image-caption">image-20220308170430516</div> </figure><p>在Call Stack中找到一个疑似加密点:</p><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="https://img.heshipeng.com/202203081705338.png?watermark/2/text/5YWz5rOo5b6u5L-h5YWs5LyX5Y-377ya6YCG5ZCR5LiA5q2l5q2l/font/5a6L5L2T/fontsize/300" alt="image-20220308170529279" title=""> </div> <div class="image-caption">image-20220308170529279</div> </figure><p>点击进去,可以看到JS代码基本上是高度混淆的:</p><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="https://img.heshipeng.com/202203081706000.png?watermark/2/text/5YWz5rOo5b6u5L-h5YWs5LyX5Y-377ya6YCG5ZCR5LiA5q2l5q2l/font/5a6L5L2T/fontsize/300" alt="image-20220308170617796" title=""> </div> <div class="image-caption">image-20220308170617796</div> </figure><p>我们把断点断到拼接请求参数的地方:</p><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="https://img.heshipeng.com/202203091058349.png?watermark/2/text/5YWz5rOo5b6u5L-h5YWs5LyX5Y-377ya6YCG5ZCR5LiA5q2l5q2l/font/5a6L5L2T/fontsize/300" alt="image-20220309105805200" title=""> </div> <div class="image-caption">image-20220309105805200</div> </figure><h5 id="逆向URL生成规则"><a href="#逆向URL生成规则" class="headerlink" title="逆向URL生成规则"></a>逆向URL生成规则</h5><p>先看下URL的生成,扣出代码:</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="string">'url'</span>: _0x15a5d0[_0x59cb56(<span class="number">0x8bc</span>, <span class="number">0x763</span>, <span class="number">0xba1</span>, <span class="string">'0jdF'</span>, <span class="number">0x6bd</span>)](_0x15a5d0[_0x59cb56(<span class="number">0x8ea</span>, <span class="number">0x8b8</span>, <span class="number">0x9b2</span>, <span class="string">'m*3l'</span>, <span class="number">0xcfc</span>)], _0x36b61f),</span><br></pre></td></tr></table></figure><p>我们再取出<code>_0x15a5d0[_0x59cb56(0x8bc, 0x763, 0xba1, '0jdF', 0x6bd)]</code>放到console上输出,发现其是一个函数:</p><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="https://img.heshipeng.com/202203091101267.png?watermark/2/text/5YWz5rOo5b6u5L-h5YWs5LyX5Y-377ya6YCG5ZCR5LiA5q2l5q2l/font/5a6L5L2T/fontsize/300" alt="image-20220309110157210" title=""> </div> <div class="image-caption">image-20220309110157210</div> </figure><p>点击进去,看到它是一个花指令,就是把两个参数相加:</p><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="https://img.heshipeng.com/202203091102623.png?watermark/2/text/5YWz5rOo5b6u5L-h5YWs5LyX5Y-377ya6YCG5ZCR5LiA5q2l5q2l/font/5a6L5L2T/fontsize/300" alt="image-20220309110253933" title=""> </div> <div class="image-caption">image-20220309110253933</div> </figure><p>所以URL参数的生成实际上是调用一个加法,把两个参数相加。我们再看这个加法传入的2个参数。<code>_0x15a5d0[_0x59cb56(0x8ea, 0x8b8, 0x9b2, 'm*3l', 0xcfc)]</code>和<code>_0x36b61f</code>。</p><p><code>`_0x15a5d0[_0x59cb56(0x8ea, 0x8b8, 0x9b2, 'm*3l', 0xcfc)]</code>是一个固定的地址。</p><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="https://img.heshipeng.com/202203091104606.png?watermark/2/text/5YWz5rOo5b6u5L-h5YWs5LyX5Y-377ya6YCG5ZCR5LiA5q2l5q2l/font/5a6L5L2T/fontsize/300" alt="image-20220309110445528" title=""> </div> <div class="image-caption">image-20220309110445528</div> </figure><p><code>_0x36b61f</code>是一个13位数的时间戳。</p><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="https://img.heshipeng.com/202203091106206.png?watermark/2/text/5YWz5rOo5b6u5L-h5YWs5LyX5Y-377ya6YCG5ZCR5LiA5q2l5q2l/font/5a6L5L2T/fontsize/300" alt="image-20220309110602734" title=""> </div> <div class="image-caption">image-20220309110602734</div> </figure><p>所以URL实际上就是一个固定的地址拼接一个13位的时间戳,即/yzmtest/checkv3.php?t={13位时间戳}。</p><h5 id="逆向data生成规则"><a href="#逆向data生成规则" class="headerlink" title="逆向data生成规则"></a>逆向data生成规则</h5><p>先扣出data生成那一部分代码:</p><figure class="highlight js"><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="string">'data'</span>: _0xf828e5[_0x59cb56(<span class="number">0x7c7</span>, <span class="number">0x4a1</span>, <span class="number">0x425</span>, <span class="string">'F^5Z'</span>, <span class="number">0x1f3</span>)](<span class="built_in">JSON</span>[_0x2e7c5c(<span class="number">0x9ff</span>, <span class="number">0x5fa</span>, <span class="number">0x813</span>, <span class="string">'Ra[M'</span>, <span class="number">0x821</span>) + _0x160f59(<span class="number">0x921</span>, <span class="number">0x690</span>, <span class="number">0x2a4</span>, <span class="string">'ZP*j'</span>, <span class="number">0x657</span>)](</span><br><span class="line"> {</span><br><span class="line"> <span class="string">'d'</span>: _0x15a5d0[_0x2e7c5c(<span class="number">0x2ce</span>, <span class="number">0x61d</span>, <span class="number">0xa5a</span>, <span class="string">'4[E4'</span>, <span class="number">0x33d</span>)](_0x15a5d0[_0x160f59(<span class="number">0x4a2</span>, <span class="number">0x99e</span>, <span class="number">0xb7e</span>, <span class="string">'ZHhp'</span>, <span class="number">0xb6b</span>)](_0x15a5d0[_0x59cb56(<span class="number">0x92b</span>, <span class="number">0x683</span>, <span class="number">0x968</span>, <span class="string">'F^5Z'</span>, <span class="number">0x9a7</span>)](_0x15a5d0[_0x160f59(<span class="number">0x11f</span>, <span class="number">0x403</span>, <span class="number">0x29d</span>, <span class="string">'AVXK'</span>, <span class="number">0x518</span>)](_0x4fd69b[_0x8eb3(<span class="number">0xc7a</span>, <span class="number">0x728</span>, <span class="number">0x704</span>, <span class="string">'OBf4'</span>, <span class="number">0xc4c</span>)](<span class="string">''</span>), <span class="string">''</span>), _0xf828e5[_0x8eb3(<span class="number">0x136</span>, <span class="number">0x44f</span>, <span class="number">0x748</span>, <span class="string">'HZxj'</span>, <span class="number">0x2a3</span>) + <span class="string">'en'</span>]), _0x36b61f[_0x556be9(<span class="number">0x5cf</span>, <span class="number">0x870</span>, <span class="number">0x50b</span>, <span class="string">'Ra[M'</span>, <span class="number">0x3af</span>) + <span class="string">'r'</span>](-(<span class="number">0x2670</span> + -<span class="number">0x77d</span> * -<span class="number">0x2</span> + -<span class="number">0x3568</span> * <span class="number">0x1</span>))), _0xf828e5[_0x160f59(<span class="number">0xae3</span>, <span class="number">0x5f9</span>, <span class="number">0x92d</span>, <span class="string">'NUpf'</span>, <span class="number">0x8a5</span>)]),</span><br><span class="line"> <span class="string">'username'</span>: _0xf828e5[_0x2e7c5c(<span class="number">0x241</span>, <span class="number">0x70d</span>, <span class="number">0x75c</span>, <span class="string">'o4oN'</span>, <span class="number">0x1eb</span>) + _0x8eb3(<span class="number">0xac</span>, <span class="number">0x90</span>, <span class="number">0x5c9</span>, <span class="string">'o4oN'</span>, <span class="number">0x3a8</span>)],</span><br><span class="line"> <span class="string">'password'</span>: _0xf828e5[_0x2e7c5c(<span class="number">0x4b3</span>, <span class="number">0x757</span>, <span class="number">0x5d1</span>, <span class="string">'*EQ3'</span>, <span class="number">0x86a</span>) + _0x2e7c5c(<span class="number">0xb07</span>, <span class="number">0x6f1</span>, <span class="number">0xa86</span>, <span class="string">'ZP*j'</span>, <span class="number">0x2bd</span>)]</span><br><span class="line"> }</span><br><span class="line">))</span><br></pre></td></tr></table></figure><p>在console面板上,很容易看出,username是我们输入的账号17777777777前面拼接了e5。</p><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="https://img.heshipeng.com/202203091816525.png?watermark/2/text/5YWz5rOo5b6u5L-h5YWs5LyX5Y-377ya6YCG5ZCR5LiA5q2l5q2l/font/5a6L5L2T/fontsize/300" alt="image-20220309181634727" title=""> </div> <div class="image-caption">image-20220309181634727</div> </figure><p> password则是明文:</p><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="https://img.heshipeng.com/202203091817805.png?watermark/2/text/5YWz5rOo5b6u5L-h5YWs5LyX5Y-377ya6YCG5ZCR5LiA5q2l5q2l/font/5a6L5L2T/fontsize/300" alt="image-20220309181714546" title=""> </div> <div class="image-caption">image-20220309181714546</div> </figure><p> 所以上面的代码可以加以简化为:</p><figure class="highlight js"><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="string">'data'</span>: _0xf828e5[_0x59cb56(<span class="number">0x7c7</span>, <span class="number">0x4a1</span>, <span class="number">0x425</span>, <span class="string">'F^5Z'</span>, <span class="number">0x1f3</span>)](<span class="built_in">JSON</span>[_0x2e7c5c(<span class="number">0x9ff</span>, <span class="number">0x5fa</span>, <span class="number">0x813</span>, <span class="string">'Ra[M'</span>, <span class="number">0x821</span>) + _0x160f59(<span class="number">0x921</span>, <span class="number">0x690</span>, <span class="number">0x2a4</span>, <span class="string">'ZP*j'</span>, <span class="number">0x657</span>)](</span><br><span class="line"> {</span><br><span class="line"> <span class="string">'d'</span>: _0x15a5d0[_0x2e7c5c(<span class="number">0x2ce</span>, <span class="number">0x61d</span>, <span class="number">0xa5a</span>, <span class="string">'4[E4'</span>, <span class="number">0x33d</span>)](_0x15a5d0[_0x160f59(<span class="number">0x4a2</span>, <span class="number">0x99e</span>, <span class="number">0xb7e</span>, <span class="string">'ZHhp'</span>, <span class="number">0xb6b</span>)](_0x15a5d0[_0x59cb56(<span class="number">0x92b</span>, <span class="number">0x683</span>, <span class="number">0x968</span>, <span class="string">'F^5Z'</span>, <span class="number">0x9a7</span>)](_0x15a5d0[_0x160f59(<span class="number">0x11f</span>, <span class="number">0x403</span>, <span class="number">0x29d</span>, <span class="string">'AVXK'</span>, <span class="number">0x518</span>)](_0x4fd69b[_0x8eb3(<span class="number">0xc7a</span>, <span class="number">0x728</span>, <span class="number">0x704</span>, <span class="string">'OBf4'</span>, <span class="number">0xc4c</span>)](<span class="string">''</span>), <span class="string">''</span>), _0xf828e5[_0x8eb3(<span class="number">0x136</span>, <span class="number">0x44f</span>, <span class="number">0x748</span>, <span class="string">'HZxj'</span>, <span class="number">0x2a3</span>) + <span class="string">'en'</span>]), _0x36b61f[_0x556be9(<span class="number">0x5cf</span>, <span class="number">0x870</span>, <span class="number">0x50b</span>, <span class="string">'Ra[M'</span>, <span class="number">0x3af</span>) + <span class="string">'r'</span>](-(<span class="number">0x2670</span> + -<span class="number">0x77d</span> * -<span class="number">0x2</span> + -<span class="number">0x3568</span> * <span class="number">0x1</span>))), _0xf828e5[_0x160f59(<span class="number">0xae3</span>, <span class="number">0x5f9</span>, <span class="number">0x92d</span>, <span class="string">'NUpf'</span>, <span class="number">0x8a5</span>)]),</span><br><span class="line"> <span class="string">'username'</span>: <span class="string">'e517777777777'</span>,</span><br><span class="line"> <span class="string">'password'</span>: <span class="string">'123456'</span></span><br><span class="line"> }</span><br><span class="line">))</span><br></pre></td></tr></table></figure><p>调试下<code>_0x59cb56(0x7c7, 0x4a1, 0x425, 'F^5Z', 0x1f3)</code></p><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="https://img.heshipeng.com/202203091823518.png?watermark/2/text/5YWz5rOo5b6u5L-h5YWs5LyX5Y-377ya6YCG5ZCR5LiA5q2l5q2l/font/5a6L5L2T/fontsize/300" alt="image-20220309182331379" title=""> </div> <div class="image-caption">image-20220309182331379</div> </figure><p>再调试下<code>_0x2e7c5c(0x9ff, 0x5fa, 0x813, 'Ra[M', 0x821) + _0x160f59(0x921, 0x690, 0x2a4, 'ZP*j', 0x657)</code>:</p><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="https://img.heshipeng.com/202203091824415.png?watermark/2/text/5YWz5rOo5b6u5L-h5YWs5LyX5Y-377ya6YCG5ZCR5LiA5q2l5q2l/font/5a6L5L2T/fontsize/300" alt="image-20220309182359819" title=""> </div> <div class="image-caption">image-20220309182359819</div> </figure><p>把上面的代码简化下:</p><figure class="highlight js"><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="string">'data'</span>: _0xf828e5.enc.JSON.stringify(</span><br><span class="line"> {</span><br><span class="line"> <span class="string">'d'</span>: _0x15a5d0[_0x2e7c5c(<span class="number">0x2ce</span>, <span class="number">0x61d</span>, <span class="number">0xa5a</span>, <span class="string">'4[E4'</span>, <span class="number">0x33d</span>)](_0x15a5d0[_0x160f59(<span class="number">0x4a2</span>, <span class="number">0x99e</span>, <span class="number">0xb7e</span>, <span class="string">'ZHhp'</span>, <span class="number">0xb6b</span>)](_0x15a5d0[_0x59cb56(<span class="number">0x92b</span>, <span class="number">0x683</span>, <span class="number">0x968</span>, <span class="string">'F^5Z'</span>, <span class="number">0x9a7</span>)](_0x15a5d0[_0x160f59(<span class="number">0x11f</span>, <span class="number">0x403</span>, <span class="number">0x29d</span>, <span class="string">'AVXK'</span>, <span class="number">0x518</span>)](_0x4fd69b[_0x8eb3(<span class="number">0xc7a</span>, <span class="number">0x728</span>, <span class="number">0x704</span>, <span class="string">'OBf4'</span>, <span class="number">0xc4c</span>)](<span class="string">''</span>), <span class="string">''</span>), _0xf828e5[_0x8eb3(<span class="number">0x136</span>, <span class="number">0x44f</span>, <span class="number">0x748</span>, <span class="string">'HZxj'</span>, <span class="number">0x2a3</span>) + <span class="string">'en'</span>]), _0x36b61f[_0x556be9(<span class="number">0x5cf</span>, <span class="number">0x870</span>, <span class="number">0x50b</span>, <span class="string">'Ra[M'</span>, <span class="number">0x3af</span>) + <span class="string">'r'</span>](-(<span class="number">0x2670</span> + -<span class="number">0x77d</span> * -<span class="number">0x2</span> + -<span class="number">0x3568</span> * <span class="number">0x1</span>))), _0xf828e5[_0x160f59(<span class="number">0xae3</span>, <span class="number">0x5f9</span>, <span class="number">0x92d</span>, <span class="string">'NUpf'</span>, <span class="number">0x8a5</span>)]),</span><br><span class="line"> <span class="string">'username'</span>: <span class="string">'e517777777777'</span>,</span><br><span class="line"> <span class="string">'password'</span>: <span class="string">'123456'</span></span><br><span class="line"> }</span><br><span class="line">))</span><br></pre></td></tr></table></figure><p>可以看到,这里是用了某种加密算法对{“d”: xxx, “username”: “xxx”, “password”: “xxx”}进行加密从而得到data。这个加密算法到底是什么加密呢?我们console面板上输入_0xf828e5.enc然后点击进去:</p><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="https://img.heshipeng.com/202203091844956.png?watermark/2/text/5YWz5rOo5b6u5L-h5YWs5LyX5Y-377ya6YCG5ZCR5LiA5q2l5q2l/font/5a6L5L2T/fontsize/300" alt="image-20220309184438821" title=""> </div> <div class="image-caption">image-20220309184438821</div> </figure><p>看到了整个方法有2个分支,我们分别对2个分支打上断点,发现只进去else了。如果对JS常见的加密有过了解的话,这里看到iv,mode,padding这三个关键字立马会想到用的是AES加密算法。第一个参数_0x4d04a3是待加密的字符串:</p><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="https://img.heshipeng.com/202203101006438.png?watermark/2/text/5YWz5rOo5b6u5L-h5YWs5LyX5Y-377ya6YCG5ZCR5LiA5q2l5q2l/font/5a6L5L2T/fontsize/300" alt="image-20220310100612354" title=""> </div> <div class="image-caption">image-20220310100612354</div> </figure><p>第二个参数this[_0x19e2aa(0x93, 0x454, ‘cu5X’, 0x139, 0x43d) + ‘e’]是密钥。我们打印出其内容:</p><figure class="highlight js"><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"><span class="keyword">var</span> key = {</span><br><span class="line"> <span class="string">"words"</span>: [</span><br><span class="line"> <span class="number">1701066809</span>,</span><br><span class="line"> <span class="number">929182054</span>,</span><br><span class="line"> <span class="number">1698117986</span>,</span><br><span class="line"> <span class="number">1697659188</span></span><br><span class="line"> ],</span><br><span class="line"> <span class="string">"sigBytes"</span>: <span class="number">16</span></span><br><span class="line">};</span><br></pre></td></tr></table></figure><p>第三个参数是AES加密的一些参数,mode一般是CBC,padding不重要可以不传。最后我们在console上输出iv的值:</p><figure class="highlight js"><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"><span class="keyword">var</span> iv = {</span><br><span class="line"> <span class="string">"words"</span>: [</span><br><span class="line"> <span class="number">1668053103</span>,</span><br><span class="line"> <span class="number">875983984</span>,</span><br><span class="line"> <span class="number">1731224932</span>,</span><br><span class="line"> <span class="number">943273826</span></span><br><span class="line"> ],</span><br><span class="line"> <span class="string">"sigBytes"</span>: <span class="number">16</span></span><br><span class="line">};</span><br></pre></td></tr></table></figure><p>有了AES加密的这几个参数我们就可以很简单的还原出解密算法了。代码如下:</p><figure class="highlight js"><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"><span class="keyword">var</span> CryptoJS = <span class="built_in">require</span>(<span class="string">"crypto-js"</span>);</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> key = {</span><br><span class="line"> <span class="string">"words"</span>: [</span><br><span class="line"> <span class="number">1701066809</span>,</span><br><span class="line"> <span class="number">929182054</span>,</span><br><span class="line"> <span class="number">1698117986</span>,</span><br><span class="line"> <span class="number">1697659188</span></span><br><span class="line"> ],</span><br><span class="line"> <span class="string">"sigBytes"</span>: <span class="number">16</span></span><br><span class="line">}; <span class="comment">// 密钥,已经转化为128bit的格式。</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> iv = {</span><br><span class="line"> <span class="string">"words"</span>: [</span><br><span class="line"> <span class="number">1668053103</span>,</span><br><span class="line"> <span class="number">875983984</span>,</span><br><span class="line"> <span class="number">1731224932</span>,</span><br><span class="line"> <span class="number">943273826</span></span><br><span class="line"> ],</span><br><span class="line"> <span class="string">"sigBytes"</span>: <span class="number">16</span></span><br><span class="line">}; <span class="comment">// IV,已经转化为128bit的格式。</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">Decrypt</span>(<span class="params">word</span>) </span>{</span><br><span class="line"> <span class="keyword">let</span> a = CryptoJS.AES.decrypt(word, key, { <span class="attr">iv</span>: iv, <span class="attr">mode</span>: CryptoJS.mode.CBC });</span><br><span class="line"> <span class="keyword">return</span> CryptoJS.enc.Utf8.stringify(a);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">let</span> data = <span class="string">""</span>;</span><br><span class="line"><span class="built_in">console</span>.log(Decrypt(data));</span><br></pre></td></tr></table></figure><p>还记得前面我们提到过抓到2个XHR请求吗?一个是请求验证码的,一个是进行验证码验证的。我们看第一个请求验证码的请求。</p><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="https://img.heshipeng.com/202203101015210.png?watermark/2/text/5YWz5rOo5b6u5L-h5YWs5LyX5Y-377ya6YCG5ZCR5LiA5q2l5q2l/font/5a6L5L2T/fontsize/300" alt="image-20220310101530093" title=""> </div> <div class="image-caption">image-20220310101530093</div> </figure><p>这个返回值c是不是就是用的AES加密呢?我们用上边的解码程序试验一下。果不其然,可以正确反解出加密内容:</p><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="https://img.heshipeng.com/202203101017949.png?watermark/2/text/5YWz5rOo5b6u5L-h5YWs5LyX5Y-377ya6YCG5ZCR5LiA5q2l5q2l/font/5a6L5L2T/fontsize/300" alt="image-20220310101738862" title=""> </div> <div class="image-caption">image-20220310101738862</div> </figure><p>里面的一串JSON正好是我们刚才出现的验证码的内容:</p><figure class="highlight js"><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"> {<span class="attr">id</span>: <span class="string">'90b389d8490d42a8'</span>, <span class="attr">txt</span>: <span class="string">'鸭子'</span>},</span><br><span class="line">{<span class="attr">id</span>: <span class="string">'a7985fb229d9e935'</span>, <span class="attr">txt</span>: <span class="string">'长颈鹿'</span>},</span><br><span class="line">{<span class="attr">id</span>: <span class="string">'c38548f7b6c0a3d8'</span>, <span class="attr">txt</span>: <span class="string">'小马'</span>},</span><br><span class="line">{<span class="attr">id</span>: <span class="string">'4c2c8bc886b8bf8d'</span>, <span class="attr">txt</span>: <span class="string">'海马'</span>},</span><br><span class="line">{<span class="attr">id</span>: <span class="string">'25897767b2ffc531'</span>, <span class="attr">txt</span>: <span class="string">'牛'</span>},</span><br><span class="line">{<span class="attr">id</span>: <span class="string">'24bafe8f4a1eac0e'</span>, <span class="attr">txt</span>: <span class="string">'斑马'</span>},</span><br><span class="line">]</span><br></pre></td></tr></table></figure><p>记住这个JSON,我们待会还有用。我们接着回到data破解的思路上去。data的生成代码进一步简化:</p><figure class="highlight js"><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="string">'data'</span>: AES.encode(</span><br><span class="line"> {</span><br><span class="line"> <span class="string">'d'</span>: _0x15a5d0[_0x2e7c5c(<span class="number">0x2ce</span>, <span class="number">0x61d</span>, <span class="number">0xa5a</span>, <span class="string">'4[E4'</span>, <span class="number">0x33d</span>)](_0x15a5d0[_0x160f59(<span class="number">0x4a2</span>, <span class="number">0x99e</span>, <span class="number">0xb7e</span>, <span class="string">'ZHhp'</span>, <span class="number">0xb6b</span>)](_0x15a5d0[_0x59cb56(<span class="number">0x92b</span>, <span class="number">0x683</span>, <span class="number">0x968</span>, <span class="string">'F^5Z'</span>, <span class="number">0x9a7</span>)](_0x15a5d0[_0x160f59(<span class="number">0x11f</span>, <span class="number">0x403</span>, <span class="number">0x29d</span>, <span class="string">'AVXK'</span>, <span class="number">0x518</span>)](_0x4fd69b[_0x8eb3(<span class="number">0xc7a</span>, <span class="number">0x728</span>, <span class="number">0x704</span>, <span class="string">'OBf4'</span>, <span class="number">0xc4c</span>)](<span class="string">''</span>), <span class="string">''</span>), _0xf828e5[_0x8eb3(<span class="number">0x136</span>, <span class="number">0x44f</span>, <span class="number">0x748</span>, <span class="string">'HZxj'</span>, <span class="number">0x2a3</span>) + <span class="string">'en'</span>]), _0x36b61f[_0x556be9(<span class="number">0x5cf</span>, <span class="number">0x870</span>, <span class="number">0x50b</span>, <span class="string">'Ra[M'</span>, <span class="number">0x3af</span>) + <span class="string">'r'</span>](-(<span class="number">0x2670</span> + -<span class="number">0x77d</span> * -<span class="number">0x2</span> + -<span class="number">0x3568</span> * <span class="number">0x1</span>))), _0xf828e5[_0x160f59(<span class="number">0xae3</span>, <span class="number">0x5f9</span>, <span class="number">0x92d</span>, <span class="string">'NUpf'</span>, <span class="number">0x8a5</span>)]),</span><br><span class="line"> <span class="string">'username'</span>: <span class="string">'e517777777777'</span>,</span><br><span class="line"> <span class="string">'password'</span>: <span class="string">'123456'</span></span><br><span class="line"> }</span><br><span class="line">))</span><br></pre></td></tr></table></figure><p>接下来看看最核心的d的生成规则。_0x15a5d0[_0x2e7c5c(0x2ce, 0x61d, 0xa5a, ‘4[E4’, 0x33d)]是一个加法的花指令。</p><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="https://img.heshipeng.com/202203101024724.png?watermark/2/text/5YWz5rOo5b6u5L-h5YWs5LyX5Y-377ya6YCG5ZCR5LiA5q2l5q2l/font/5a6L5L2T/fontsize/300" alt="image-20220310102400570" title=""> </div> <div class="image-caption">image-20220310102400570</div> </figure><p>_0x15a5d0[_0x59cb56(0x92b, 0x683, 0x968, ‘F^5Z’, 0x9a7)]也是一个加法的花指令:</p><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="https://img.heshipeng.com/202203101026613.png?watermark/2/text/5YWz5rOo5b6u5L-h5YWs5LyX5Y-377ya6YCG5ZCR5LiA5q2l5q2l/font/5a6L5L2T/fontsize/300" alt="image-20220310102640758" title=""> </div> <div class="image-caption">image-20220310102640758</div> </figure><p>_0x15a5d0[_0x59cb56(0x92b, 0x683, 0x968, ‘F^5Z’, 0x9a7)]也是加法花指令。</p><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="https://img.heshipeng.com/202203101027076.png?watermark/2/text/5YWz5rOo5b6u5L-h5YWs5LyX5Y-377ya6YCG5ZCR5LiA5q2l5q2l/font/5a6L5L2T/fontsize/300" alt="image-20220310102715978" title=""> </div> <div class="image-caption">image-20220310102715978</div> </figure><p>_0x15a5d0[_0x160f59(0x11f, 0x403, 0x29d, ‘AVXK’, 0x518)]依旧是一个加法花指令。</p><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="https://img.heshipeng.com/202203101028062.png?watermark/2/text/5YWz5rOo5b6u5L-h5YWs5LyX5Y-377ya6YCG5ZCR5LiA5q2l5q2l/font/5a6L5L2T/fontsize/300" alt="image-20220310102811012" title=""> </div> <div class="image-caption">image-20220310102811012</div> </figure><p>_0x4fd69b[_0x8eb3(0xc7a, 0x728, 0x704, ‘OBf4’, 0xc4c)]是内置的join方法。</p><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="https://img.heshipeng.com/202203101029626.png?watermark/2/text/5YWz5rOo5b6u5L-h5YWs5LyX5Y-377ya6YCG5ZCR5LiA5q2l5q2l/font/5a6L5L2T/fontsize/300" alt="image-20220310102934544" title=""> </div> <div class="image-caption">image-20220310102934544</div> </figure><p>_0x4fd69b是个字符串:</p><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="https://img.heshipeng.com/202203101059338.png?watermark/2/text/5YWz5rOo5b6u5L-h5YWs5LyX5Y-377ya6YCG5ZCR5LiA5q2l5q2l/font/5a6L5L2T/fontsize/300" alt="image-20220310105915453" title=""> </div> <div class="image-caption">image-20220310105915453</div> </figure><p>_0x8eb3(0x136, 0x44f, 0x748, ‘HZxj’, 0x2a3) + ‘en’是字符串$strlen</p><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="https://img.heshipeng.com/202203101032240.png?watermark/2/text/5YWz5rOo5b6u5L-h5YWs5LyX5Y-377ya6YCG5ZCR5LiA5q2l5q2l/font/5a6L5L2T/fontsize/300" alt="image-20220310103214178" title=""> </div> <div class="image-caption">image-20220310103214178</div> </figure><p>_0xf828e5[_0x8eb3(0x136, 0x44f, 0x748, ‘HZxj’, 0x2a3) + ‘en’]则是取_0xf828e5这个object的$strlen属性,这里的值是3。</p><p>_0x556be9(0x5cf, 0x870, 0x50b, ‘Ra[M’, 0x3af) + ‘r’这里是substr。</p><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="https://img.heshipeng.com/202203101041001.png?watermark/2/text/5YWz5rOo5b6u5L-h5YWs5LyX5Y-377ya6YCG5ZCR5LiA5q2l5q2l/font/5a6L5L2T/fontsize/300" alt="image-20220310104111880" title=""> </div> <div class="image-caption">image-20220310104111880</div> </figure><p>_0x36b61f是13位的时间戳。</p><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="https://img.heshipeng.com/202203101042436.png?watermark/2/text/5YWz5rOo5b6u5L-h5YWs5LyX5Y-377ya6YCG5ZCR5LiA5q2l5q2l/font/5a6L5L2T/fontsize/300" alt="image-20220310104215390" title=""> </div> <div class="image-caption">image-20220310104215390</div> </figure><p>-(0x2670 + -0x77d <em> -0x2 + -0x3568 </em> 0x1)是固定的常量,-2。</p><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="https://img.heshipeng.com/202203101042243.png?watermark/2/text/5YWz5rOo5b6u5L-h5YWs5LyX5Y-377ya6YCG5ZCR5LiA5q2l5q2l/font/5a6L5L2T/fontsize/300" alt="image-20220310104254188" title=""> </div> <div class="image-caption">image-20220310104254188</div> </figure><p>_0x160f59(0xae3, 0x5f9, 0x92d, ‘NUpf’, 0x8a5)是字符串$ver。这个ver其实在我们刚才解码第一个请求的返回值的里面就有了,值为3587,跟这里的吻合。</p><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="https://img.heshipeng.com/202203101100041.png?watermark/2/text/5YWz5rOo5b6u5L-h5YWs5LyX5Y-377ya6YCG5ZCR5LiA5q2l5q2l/font/5a6L5L2T/fontsize/300" alt="image-20220310110046938" title=""> </div> <div class="image-caption">image-20220310110046938</div> </figure><p>最终简化代码如下:</p><figure class="highlight js"><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="string">'data'</span>: AES.encode(</span><br><span class="line"> {</span><br><span class="line"> <span class="string">'d'</span>: add(add(add(add(<span class="string">"4c2c8bc886b8bf8d"</span>.join(<span class="string">''</span>), <span class="string">''</span>), _0xf828e5.$strlen), _0x36b61f.substr(-<span class="number">2</span>)), _0xf828e5.$ver),</span><br><span class="line"> <span class="comment">// 'd': "4c2c8bc886b8bf8d" + _0xf828e5.$strlen + _0x36b61f.substr(-2)) + _0xf828e5.$ver</span></span><br><span class="line"> <span class="string">'username'</span>: <span class="string">'e517777777777'</span>,</span><br><span class="line"> <span class="string">'password'</span>: <span class="string">'123456'</span></span><br><span class="line"> }</span><br><span class="line">))</span><br></pre></td></tr></table></figure><p>到这里d的生成算法基本上一目了然了。d=字符串(这里是4c2c8bc886b8bf8d)+_0xf828e5.$strlen(这里是3)+13位时间戳的最后2位(这里是17)+版本号(第一个请求接口有返回为3587)=4c2c8bc886b8bf8d3173587。扣出d的生成代码,在console上输出,验证下:</p><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="https://img.heshipeng.com/202203101114177.png?watermark/2/text/5YWz5rOo5b6u5L-h5YWs5LyX5Y-377ya6YCG5ZCR5LiA5q2l5q2l/font/5a6L5L2T/fontsize/300" alt="image-20220310111442039" title=""> </div> <div class="image-caption">image-20220310111442039</div> </figure><p>完全吻合。现在唯一的问题是_0x4fd69b这个字符串怎么来的以及_0xf828e5.$strlen这个值怎么算出来的,解决了这两个问题,d的生成规则就破解了。</p><p>我们手动搜索_0x4fd69b这个字符串,总共找到三处:</p><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="https://img.heshipeng.com/202203101121754.png?watermark/2/text/5YWz5rOo5b6u5L-h5YWs5LyX5Y-377ya6YCG5ZCR5LiA5q2l5q2l/font/5a6L5L2T/fontsize/300" alt="image-20220310112140608" title=""> </div> <div class="image-caption">image-20220310112140608</div> </figure><p>扣出第二处的代码如下:</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">_0x4fd69b[_0x219591(<span class="string">'FZa9'</span>, <span class="number">0x79c</span>, <span class="number">0x826</span>, <span class="number">0x6a9</span>, <span class="number">0x45f</span>)](_0xf828e5[_0x59b553(<span class="string">'W]B)'</span>, <span class="number">0xd30</span>, <span class="number">0x809</span>, <span class="number">0x809</span>, <span class="number">0x2c4</span>)][_0x19f5d9[_0x3ebe03(<span class="string">'nCyg'</span>, <span class="number">0xb6c</span>, <span class="number">0x868</span>, <span class="number">0x4ee</span>, <span class="number">0xd78</span>)](<span class="built_in">parseInt</span>, _0x1bf4a9[_0x59b553(<span class="string">'#og4'</span>, <span class="number">0xbb9</span>, <span class="number">0x8a2</span>, <span class="number">0x953</span>, <span class="number">0x441</span>) + <span class="string">'ce'</span>](_0x19f5d9[_0x3ebe03(<span class="string">'F^5Z'</span>, <span class="number">0x448</span>, <span class="number">0xc1</span>, -<span class="number">0x2ba</span>, <span class="number">0x8c</span>)], <span class="string">''</span>))][<span class="string">'d'</span>]);</span><br></pre></td></tr></table></figure><p>按照上边提供的方法,在console上分段调试代码含义,代码反混淆如下(<font style="color: red;">为节省篇幅,从这里开始,代码反混淆过程都不会写了,直接给出反混淆的结果</font>):</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">_0x4fd69b.push(_0xf828e5.$list[<span class="built_in">parseInt</span>(<span class="string">"btncanv_3"</span>.replace(<span class="string">"btncanv_"</span>, <span class="string">""</span>))][<span class="string">'d'</span>]);</span><br></pre></td></tr></table></figure><p>这个代码的意思就是取上边我们提到的验证码数组中索引为3的值,即4c2c8bc886b8bf8d,把这个值push到数组_0x4fd69b(这里虽然取得是d这个属性,但是实际上d属性跟id属性的值是一样的,_0x281004[‘d’] = _0x3aa5c6[‘id’])。这里的这个btncanv_3恰好是验证码的答案,即正确答案的元素的id。</p><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="https://img.heshipeng.com/202203101405702.png?watermark/2/text/5YWz5rOo5b6u5L-h5YWs5LyX5Y-377ya6YCG5ZCR5LiA5q2l5q2l/font/5a6L5L2T/fontsize/300" alt="image-20220310140550569" title=""> </div> <div class="image-caption">image-20220310140550569</div> </figure><p>那么这个<code>btncanv_3</code>是怎么确定的呢?是我们手动点选验证码图案的时候选中的,我们手动选中了验证码图案,JS代码会根据我们选中的图案,拿到它的id(如果有选中了多个,也只取第一个),然后进行JS加密,后端会根据相应的解密算法,拿到我们上传的那个验证码。我们开篇提到过“一般来说网站如果出现复杂验证码都会配合JS参数加密增加防护等级”,这里就验证了这句话。<strong>我们这里虽然破解了验证码验证接口表单数据的加密算法,但是验证码的点选,我们还需要辅助相应的验证码识别的算法,帮助我们完成验证码的识别与点选</strong>。这里主要是讲解手动反混淆方案,验证码后边会有专门的专题文章进行介绍,先埋一个坑后边补上。</p><p>接下来看下另外一个_0xf828e5.$strlen的生成规则。我们文件中全局搜索’en’(为什么要搜索这个?因为前面$strlen的字符串混淆是_0x8eb3(0x136, 0x44f, 0x748, ‘HZxj’, 0x2a3) + ‘en’),果然被我们找到了。代码如下:</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">this</span>[_0x2a85c2(<span class="number">0x8a2</span>, <span class="number">0x60c</span>, <span class="number">0xa3d</span>, <span class="number">0x7b0</span>, <span class="string">'0NjW'</span>) + <span class="string">'en'</span>] = _0x15a5d0[_0x19b874(<span class="number">0xc8f</span>, <span class="number">0xe5a</span>, <span class="number">0x13b9</span>, <span class="number">0xb8b</span>, <span class="string">'W]B)'</span>)](<span class="built_in">Math</span>[_0x26b2f7(<span class="number">0xf44</span>, <span class="number">0xc7b</span>, <span class="number">0xbc1</span>, <span class="number">0x1077</span>, <span class="string">'o4oN'</span>)](_0x15a5d0[_0x26b578(<span class="number">0xfa3</span>, <span class="number">0xcfb</span>, <span class="number">0x820</span>, <span class="number">0x11b8</span>, <span class="string">'m*3l'</span>)](-<span class="number">0x1bcd</span> + <span class="number">0x1</span> * <span class="number">0x1b23</span> + -<span class="number">0xaf</span> * -<span class="number">0x1</span>, <span class="built_in">Math</span>[_0x2a85c2(<span class="number">0x11fb</span>, <span class="number">0xf8b</span>, <span class="number">0xd69</span>, <span class="number">0x137a</span>, <span class="string">'*EQ3'</span>) + <span class="string">'m'</span>]())), -<span class="number">0x727</span> * <span class="number">0x2</span> + <span class="number">0x24d7</span> + -<span class="number">0x1f</span> * <span class="number">0xba</span>)</span><br></pre></td></tr></table></figure><p>代码反混淆之后整理如下:</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">this</span>.$strlen = <span class="built_in">Math</span>.floor(<span class="number">5</span> * <span class="built_in">Math</span>.random()) + <span class="number">3</span></span><br></pre></td></tr></table></figure><p> 至此,整个data数据生成过程调试完了。最终的算法伪代码整理如下:</p><figure class="highlight js"><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="keyword">let</span> _0xf828e5.$strlen = <span class="built_in">Math</span>.floor(<span class="number">5</span> * <span class="built_in">Math</span>.random()) + <span class="number">3</span>;</span><br><span class="line"><span class="keyword">let</span> _0x36b61f = <span class="keyword">new</span> <span class="built_in">Date</span>().getTime();</span><br><span class="line"><span class="keyword">let</span> _0xf828e5.$ver = <span class="string">"3587"</span>;</span><br><span class="line"><span class="keyword">let</span> code = [</span><br><span class="line"> {<span class="attr">id</span>: <span class="string">'90b389d8490d42a8'</span>, <span class="attr">txt</span>: <span class="string">'鸭子'</span>},</span><br><span class="line">{<span class="attr">id</span>: <span class="string">'a7985fb229d9e935'</span>, <span class="attr">txt</span>: <span class="string">'长颈鹿'</span>},</span><br><span class="line">{<span class="attr">id</span>: <span class="string">'c38548f7b6c0a3d8'</span>, <span class="attr">txt</span>: <span class="string">'小马'</span>},</span><br><span class="line">{<span class="attr">id</span>: <span class="string">'4c2c8bc886b8bf8d'</span>, <span class="attr">txt</span>: <span class="string">'海马'</span>},</span><br><span class="line">{<span class="attr">id</span>: <span class="string">'25897767b2ffc531'</span>, <span class="attr">txt</span>: <span class="string">'牛'</span>},</span><br><span class="line">{<span class="attr">id</span>: <span class="string">'24bafe8f4a1eac0e'</span>, <span class="attr">txt</span>: <span class="string">'斑马'</span>},</span><br><span class="line">]; <span class="comment">// 这个验证码的JSON从第一个接口中拿</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// AES的密钥以及IV值上边已经给出</span></span><br><span class="line"><span class="string">'data'</span>: AES.encode(</span><br><span class="line"> {</span><br><span class="line"> <span class="string">'d'</span>: code[<span class="comment">/*人工选中的第一个验证码的索引*/</span>].id + _0xf828e5.$strlen + _0x36b61f.substr(-<span class="number">2</span>) + _0xf828e5.$ver</span><br><span class="line"> <span class="string">'username'</span>: <span class="string">'e517777777777'</span>,</span><br><span class="line"> <span class="string">'password'</span>: <span class="string">'123456'</span></span><br><span class="line"> }</span><br><span class="line">))</span><br></pre></td></tr></table></figure><h5 id="逆向clientid生成规则"><a href="#逆向clientid生成规则" class="headerlink" title="逆向clientid生成规则"></a>逆向clientid生成规则</h5><p>扣出clientid生成相关的代码:</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">this</span>[_0x26b2f7(<span class="number">0x9fc</span>, <span class="number">0xa9b</span>, <span class="number">0xe1c</span>, <span class="number">0x6b2</span>, <span class="string">'%jat'</span>) + _0x34ce5a(<span class="number">0x1321</span>, <span class="number">0xfb2</span>, <span class="number">0x11d0</span>, <span class="number">0x109c</span>, <span class="string">'m*3l'</span>)] = _0x1c3499[_0x2a85c2(<span class="number">0xf23</span>, <span class="number">0xc3d</span>, <span class="number">0x99d</span>, <span class="number">0xf50</span>, <span class="string">'OBf4'</span>)](<span class="string">''</span>)[_0x26b2f7(<span class="number">0xf47</span>, <span class="number">0xb49</span>, <span class="number">0xe76</span>, <span class="number">0xc02</span>, <span class="string">'W]B)'</span>) + <span class="string">'r'</span>](<span class="number">0x4f</span> * -<span class="number">0x1</span> + <span class="number">0x1779</span> + -<span class="number">0x172a</span>, -<span class="number">0x4ff</span> * <span class="number">0x3</span> + -<span class="number">0x3</span> * -<span class="number">0x75c</span> + <span class="number">0x1</span> * -<span class="number">0x70d</span>)</span><br></pre></td></tr></table></figure><p>代码反混淆如下:</p><figure class="highlight js"><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="built_in">this</span>.$clientid = _0x1c3499.join(<span class="string">""</span>).substr(<span class="number">0</span>, <span class="number">10</span>)</span><br><span class="line"><span class="comment">// _0x1c3499 = ['54mwjp6', 8, 'zvc']</span></span><br></pre></td></tr></table></figure><p>$clientid的生成规则依赖_0x1c3499,我继续往下看,扣出_0x1c3499的相关代码:</p><figure class="highlight js"><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">_0x1c3499 = []</span><br><span class="line">_0x1c3499[_0x26b578(<span class="number">0xc37</span>, <span class="number">0xd61</span>, <span class="number">0x122c</span>, <span class="number">0xeeb</span>, <span class="string">'g(lc'</span>)](_0x3e49dd[_0x2a85c2(<span class="number">0x1ea</span>, <span class="number">0x608</span>, <span class="number">0x810</span>, <span class="number">0xf3</span>, <span class="string">'o4oN'</span>) + <span class="string">'r'</span>](-<span class="number">0x5f</span> * <span class="number">0x1b</span> + -<span class="number">0x1</span> * <span class="number">0x15c1</span> + <span class="number">0x1fc6</span>, _0x15a5d0[_0x26b578(<span class="number">0xee3</span>, <span class="number">0xb7c</span>, <span class="number">0x893</span>, <span class="number">0xddb</span>, <span class="string">'HZxj'</span>)](_0x3badf1, -<span class="number">0x1</span> * -<span class="number">0x14e3</span> + <span class="number">0x26</span> * -<span class="number">0x56</span> + <span class="number">0x81e</span> * -<span class="number">0x1</span>))),</span><br><span class="line">_0x1c3499[_0x34ce5a(<span class="number">0x10ca</span>, <span class="number">0xef3</span>, <span class="number">0xa62</span>, <span class="number">0x10c1</span>, <span class="string">'[tJe'</span>)](_0x3badf1),</span><br><span class="line">_0x1c3499[_0x19b874(<span class="number">0x4bb</span>, <span class="number">0x55a</span>, <span class="number">0x35d</span>, <span class="number">0x7ce</span>, <span class="string">'R[NP'</span>)](_0x3e49dd[_0x2a85c2(<span class="number">0x11c</span>, <span class="number">0x5da</span>, <span class="number">0xde</span>, <span class="number">0x229</span>, <span class="string">'FrGG'</span>) + <span class="string">'r'</span>](_0x3badf1, _0x3e49dd[_0x26b578(<span class="number">0xf55</span>, <span class="number">0xbca</span>, <span class="number">0x95d</span>, <span class="number">0xd00</span>, <span class="string">'0jdF'</span>) + <span class="string">'t'</span>])),</span><br></pre></td></tr></table></figure><p>代码反混淆如下:</p><figure class="highlight js"><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">_0x1c3499 = [];</span><br><span class="line">_0x1c3499.push(_0x3e49dd.substr(<span class="number">0</span>, _0x3badf1 - <span class="number">1</span>)); <span class="comment">//_0x1c3499.push(54mwjp6)</span></span><br><span class="line">_0x1c3499.push(_0x3badf1); <span class="comment">//_0x1c3499.push(8)</span></span><br><span class="line">_0x3e49dd.push(_0x3e49dd.substr(_0x3badf1, _0x3e49dd.length)); <span class="comment">//_0x1c3499.push(zvc)</span></span><br><span class="line"><span class="comment">// _0x3e49dd = "54mwjp6tzvc"</span></span><br><span class="line"><span class="comment">// _0x3badf1 = 8</span></span><br></pre></td></tr></table></figure><p>可以看到__0x1c3499的值又依赖_0x3e49dd和_0x3badf1。我们再扣出相应的代码:</p><figure class="highlight js"><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">_0x3e49dd = _0x15a5d0[_0x2a85c2(<span class="number">0x240</span>, <span class="number">0x6a9</span>, <span class="number">0x570</span>, <span class="number">0x230</span>, <span class="string">'ZP*j'</span>)](<span class="built_in">Number</span>, _0x15a5d0[_0x26b578(<span class="number">0x8cc</span>, <span class="number">0xaab</span>, <span class="number">0xa86</span>, <span class="number">0xf1d</span>, <span class="string">'NUpf'</span>)](<span class="built_in">Math</span>[_0x19b874(<span class="number">0x12ba</span>, <span class="number">0xd7c</span>, <span class="number">0xb35</span>, <span class="number">0xe72</span>, <span class="string">'o4oN'</span>) + <span class="string">'m'</span>]()[_0x34ce5a(<span class="number">0xffa</span>, <span class="number">0xa91</span>, <span class="number">0x802</span>, <span class="number">0xe92</span>, <span class="string">'uUCz'</span>) + _0x19b874(<span class="number">0x174</span>, <span class="number">0x684</span>, <span class="number">0x9d7</span>, <span class="number">0x737</span>, <span class="string">'G0Im'</span>)]()[_0x26b2f7(<span class="number">0xaa8</span>, <span class="number">0x6bd</span>, <span class="number">0x562</span>, <span class="number">0x35a</span>, <span class="string">'F^5Z'</span>) + <span class="string">'r'</span>](<span class="number">0x10</span> * <span class="number">0xc2</span> + <span class="number">0x6d</span> * -<span class="number">0x5</span> + -<span class="number">0x9</span> * <span class="number">0x11c</span>, <span class="number">0xb3</span> * <span class="number">0x35</span> + <span class="number">0x13</span> * <span class="number">0x1a5</span> + -<span class="number">0x444a</span> * <span class="number">0x1</span>), <span class="built_in">Date</span>[_0x26b2f7(<span class="number">0xbf3</span>, <span class="number">0xd8c</span>, <span class="number">0xaf2</span>, <span class="number">0xacf</span>, <span class="string">'g(lc'</span>)]()))[_0x26b578(<span class="number">0x326</span>, <span class="number">0x69f</span>, <span class="number">0xaa3</span>, <span class="number">0x78d</span>, <span class="string">'*EQ3'</span>) + _0x26b2f7(<span class="number">0x8dc</span>, <span class="number">0x900</span>, <span class="number">0x8f0</span>, <span class="number">0xa3a</span>, <span class="string">'hROy'</span>)](<span class="number">0x143b</span> + <span class="number">0x53c</span> + -<span class="number">0x1953</span>)</span><br><span class="line"> </span><br><span class="line"> _0x3badf1 = _0x15a5d0[_0x34ce5a(<span class="number">0x11ce</span>, <span class="number">0xdf7</span>, <span class="number">0x9a2</span>, <span class="number">0xad1</span>, <span class="string">'!OnF'</span>)](<span class="built_in">parseInt</span>, _0x3577fe[<span class="built_in">Math</span>[_0x19b874(<span class="number">0x871</span>, <span class="number">0xb0b</span>, <span class="number">0xf76</span>, <span class="number">0xb41</span>, <span class="string">'#og4'</span>)](_0x15a5d0[_0x2a85c2(<span class="number">0x123a</span>, <span class="number">0xeb6</span>, <span class="number">0xbf1</span>, <span class="number">0x10d4</span>, <span class="string">'bsj&'</span>)](<span class="built_in">Math</span>[_0x2a85c2(<span class="number">0x7ca</span>, <span class="number">0x776</span>, <span class="number">0x9aa</span>, <span class="number">0x847</span>, <span class="string">'R[NP'</span>) + <span class="string">'m'</span>](), _0x3577fe[_0x26b2f7(<span class="number">0x2ac</span>, <span class="number">0x73b</span>, <span class="number">0xbfc</span>, <span class="number">0x1ff</span>, <span class="string">'FrGG'</span>) + <span class="string">'h'</span>]))])</span><br></pre></td></tr></table></figure><p>代码反混淆后结果如下:</p><figure class="highlight js"><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">_0x3e49dd = <span class="built_in">Number</span>(<span class="built_in">Math</span>.random().toString().substr(<span class="number">3</span>, <span class="number">4</span>) + <span class="built_in">Date</span>.now().toString()).toString(<span class="number">36</span>)</span><br><span class="line">_0x3badf1 = <span class="built_in">parseInt</span>(_0x3577fe[<span class="built_in">Math</span>.floor(<span class="built_in">Math</span>.random() * _0x3577fe.length)])</span><br></pre></td></tr></table></figure><p>_0x3577fe的值是固定的三个元素的数组,如下:</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">_0x3577fe = [-<span class="number">0x2090</span> + -<span class="number">0x1b</span> * <span class="number">0x139</span> + <span class="number">0x4197</span>, <span class="number">0x959</span> + -<span class="number">0x248c</span> + <span class="number">0x45</span> * <span class="number">0x65</span>, <span class="number">0x2</span> * -<span class="number">0x1ea</span> + <span class="number">0x229f</span> + -<span class="number">0x1ec3</span>]; <span class="comment">// 4 6 8</span></span><br></pre></td></tr></table></figure><p>到这里为止,$clientid的生成规则就全部反混淆出来了,最终的代码整理如下:</p><figure class="highlight js"><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="keyword">let</span> _0x3577fe = [<span class="number">4</span>, <span class="number">6</span>, <span class="number">8</span>];</span><br><span class="line"><span class="keyword">let</span> _0x3e49dd = <span class="built_in">Number</span>(<span class="built_in">Math</span>.random().toString().substr(<span class="number">3</span>, <span class="number">4</span>) + <span class="built_in">Date</span>.now().toString()).toString(<span class="number">36</span>);</span><br><span class="line"><span class="keyword">let</span> _0x3badf1 = <span class="built_in">parseInt</span>(_0x3577fe[<span class="built_in">Math</span>.floor(<span class="built_in">Math</span>.random() * _0x3577fe.length)]);</span><br><span class="line"></span><br><span class="line"><span class="keyword">let</span> _0x1c3499 = [];</span><br><span class="line">_0x1c3499.push(_0x3e49dd.substr(<span class="number">0</span>, _0x3badf1 - <span class="number">1</span>));</span><br><span class="line">_0x1c3499.push(_0x3badf1);</span><br><span class="line">_0x1c3499.push(_0x3e49dd.substr(_0x3badf1, _0x3e49dd.length));</span><br><span class="line"></span><br><span class="line"><span class="keyword">let</span> clientid = _0x1c3499.join(<span class="string">""</span>).substr(<span class="number">0</span>,<span class="number">10</span>);</span><br><span class="line"><span class="built_in">console</span>.log(clientid);</span><br></pre></td></tr></table></figure><p>真是一层一层剥开你的心🥴</p><h5 id="逆向token生成规则"><a href="#逆向token生成规则" class="headerlink" title="逆向token生成规则"></a>逆向token生成规则</h5><p>扣出token生成的相关代码:</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="string">'token'</span>: _0xf828e5[_0x2e7c5c(<span class="number">0x3c9</span>, <span class="number">0x4c</span>, -<span class="number">0x2fc</span>, <span class="string">'hROy'</span>, -<span class="number">0x1f8</span>)](_0x15a5d0[_0x2e7c5c(<span class="number">0x78e</span>, <span class="number">0x6f8</span>, <span class="number">0x927</span>, <span class="string">'(e@x'</span>, <span class="number">0x3d0</span>)](_0x15a5d0[_0x59cb56(<span class="number">0x398</span>, <span class="number">0x6f8</span>, <span class="number">0x7eb</span>, <span class="string">'(e@x'</span>, <span class="number">0x571</span>)](_0x15a5d0[_0x160f59(<span class="number">0xbca</span>, <span class="number">0xad5</span>, <span class="number">0x7c2</span>, <span class="string">'FZa9'</span>, <span class="number">0xa2c</span>)](_0xf828e5[_0x2e7c5c(<span class="number">0x56b</span>, <span class="number">0x25a</span>, <span class="number">0x1e6</span>, <span class="string">'g(lc'</span>, -<span class="number">0x22c</span>) + _0x556be9(<span class="number">0x527</span>, <span class="number">0x2b</span>, <span class="number">0x30c</span>, <span class="string">'su5h'</span>, <span class="number">0x58f</span>)], _0xf828e5[_0x8eb3(<span class="number">0x1bf</span>, <span class="number">0x17c</span>, <span class="number">0x58</span>, <span class="string">'Ra[M'</span>, -<span class="number">0xec</span>) + _0x2e7c5c(<span class="number">0x203</span>, <span class="number">0x237</span>, -<span class="number">0x41</span>, <span class="string">'Qm)6'</span>, <span class="number">0x40c</span>)]), _0xf828e5[_0x2e7c5c(<span class="number">0xd48</span>, <span class="number">0x8de</span>, <span class="number">0x717</span>, <span class="string">'j[vi'</span>, <span class="number">0xd5e</span>) + _0x59cb56(<span class="number">0x301</span>, <span class="number">0x4c3</span>, <span class="number">0x1e5</span>, <span class="string">'0jdF'</span>, <span class="number">0x8fb</span>)]), _0x15a5d0[_0x59cb56(-<span class="number">0x1ee</span>, <span class="number">0xb3</span>, <span class="number">0x35</span>, <span class="string">'OBf4'</span>, <span class="number">0x524</span>)]))</span><br></pre></td></tr></table></figure><p>反混淆之后的最终代码如下:</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="string">'token'</span>: _0xf828e5[<span class="string">"sign"</span>](_0xf828e5.$clientid + _0xf828e5.$username + _0x15a5d0.sGZgF))</span><br></pre></td></tr></table></figure><p>庆幸的是,这里的_0x15a5d0.sGZgF是一个固定的字符串,内容为”x045783”。所以重点在于破解这个加密方法sign。我们扣出相应的代码:</p><figure class="highlight js"><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">_0x58b6e0[_0x339e58(<span class="number">0x61a</span>, -<span class="number">0x24f</span>, <span class="number">0x94</span>, <span class="string">'nCyg'</span>, <span class="number">0x267</span>) + _0x18c3fa(<span class="number">0x813</span>, <span class="number">0x825</span>, <span class="number">0x36f</span>, <span class="string">'TEE1'</span>, <span class="number">0x6be</span>)][_0x1d90d1(<span class="number">0x18a</span>, <span class="number">0x8b2</span>, -<span class="number">0x158</span>, <span class="string">'Qm)6'</span>, <span class="number">0x348</span>)] = <span class="function"><span class="keyword">function</span>(<span class="params">_0x1c6621</span>) </span>{</span><br><span class="line"> <span class="comment">// ...此处省略若干行</span></span><br><span class="line"> <span class="keyword">var</span> _0x4f239d = []</span><br><span class="line"> , _0x4996c2 = cjs[_0x8a0b67(<span class="string">'FrGG'</span>, <span class="number">0x9dc</span>, <span class="number">0x3fa</span>, <span class="number">0xaba</span>, <span class="number">0x868</span>)](_0x15a5d0[_0x343f8b(<span class="string">'ZiBy'</span>, <span class="number">0x377</span>, <span class="number">0x152</span>, <span class="number">0x542</span>, <span class="number">0x4df</span>)](_0x1c6621, _0x46b6c3[_0x8a0b67(<span class="string">'TEE1'</span>, <span class="number">0x3db</span>, <span class="number">0x7c8</span>, <span class="number">0x6a4</span>, <span class="number">0x347</span>) + _0x429176(<span class="string">'NUpf'</span>, <span class="number">0x241</span>, -<span class="number">0x1c1</span>, <span class="number">0x4ab</span>, <span class="number">0x81</span>)][_0x343f8b(<span class="string">'HZxj'</span>, <span class="number">0x49c</span>, <span class="number">0x718</span>, <span class="number">0xa22</span>, <span class="number">0x854</span>)][_0x429176(<span class="string">'F^5Z'</span>, <span class="number">0x472</span>, <span class="number">0x2c9</span>, <span class="number">0x687</span>, <span class="number">0x272</span>) + _0x8a0b67(<span class="string">'g(lc'</span>, <span class="number">0x39e</span>, <span class="number">0x19b</span>, <span class="number">0x616</span>, <span class="number">0x5b8</span>) + <span class="string">'e'</span>]()))[_0x4b55c2(<span class="string">'OBf4'</span>, -<span class="number">0x22b</span>, -<span class="number">0x342</span>, <span class="number">0x13a</span>, <span class="number">0x2c</span>) + _0x8a0b67(<span class="string">'xVxp'</span>, -<span class="number">0x78</span>, <span class="number">0x45c</span>, <span class="number">0x287</span>, <span class="number">0x16b</span>)]();</span><br><span class="line"> <span class="keyword">return</span> _0x4f239d[_0xafe698(<span class="string">'Ra[M'</span>, <span class="number">0x1d0</span>, -<span class="number">0x66b</span>, <span class="number">0x2ad</span>, -<span class="number">0x177</span>)](_0x4996c2[_0xafe698(<span class="string">'IF#P'</span>, <span class="number">0x2d0</span>, <span class="number">0x958</span>, <span class="number">0x506</span>, <span class="number">0x55e</span>) + <span class="string">'r'</span>](-<span class="number">0x15d</span> + -<span class="number">0x49</span> * -<span class="number">0x45</span> + -<span class="number">0x1246</span>, <span class="number">0x8c2</span> + <span class="number">0x25af</span> + -<span class="number">0x2e6c</span>)),</span><br><span class="line"> _0x4f239d[_0x8a0b67(<span class="string">'g(lc'</span>, <span class="number">0x34a</span>, <span class="number">0x43e</span>, <span class="number">0x955</span>, <span class="number">0x683</span>)](_0x4996c2[_0x4b55c2(<span class="string">'ZiBy'</span>, <span class="number">0x71</span>, <span class="number">0x10f</span>, <span class="number">0x9a7</span>, <span class="number">0x506</span>) + <span class="string">'r'</span>](<span class="number">0x142b</span> * -<span class="number">0x1</span> + <span class="number">0x21b7</span> + <span class="number">0xd85</span> * -<span class="number">0x1</span>, -<span class="number">0xb69</span> * -<span class="number">0x3</span> + -<span class="number">0x61</span> * <span class="number">0x58</span> + -<span class="number">0xde</span>)),</span><br><span class="line"> _0x4f239d[_0x429176(<span class="string">'W]B)'</span>, <span class="number">0x82f</span>, <span class="number">0x119</span>, <span class="number">0x27e</span>, <span class="number">0x4fb</span>)](_0x4996c2[_0x4b55c2(<span class="string">'AVXK'</span>, <span class="number">0x3e8</span>, <span class="number">0x4fc</span>, <span class="number">0x6f2</span>, <span class="number">0x79f</span>) + <span class="string">'r'</span>](<span class="number">0x61b</span> + <span class="number">0xc</span> * -<span class="number">0x11e</span> + <span class="number">0x75c</span>, -<span class="number">0x677</span> * <span class="number">0x1</span> + -<span class="number">0x2272</span> * <span class="number">0x1</span> + <span class="number">0x28ee</span>)),</span><br><span class="line"> _0x4f239d[_0xafe698(<span class="string">'0NjW'</span>, <span class="number">0x3c5</span>, <span class="number">0x181</span>, -<span class="number">0x47</span>, <span class="number">0x125</span>)](_0x4996c2[_0x8a0b67(<span class="string">'uUCz'</span>, <span class="number">0xcea</span>, <span class="number">0x99c</span>, <span class="number">0x405</span>, <span class="number">0x8c3</span>) + <span class="string">'r'</span>](-<span class="number">0x4ba</span> + -<span class="number">0x59d</span> * <span class="number">0x1</span> + <span class="number">0xa6b</span>, -<span class="number">0x2</span> * <span class="number">0xe80</span> + <span class="number">0x1c01</span> + <span class="number">0x104</span>)),</span><br><span class="line"> _0x4f239d[_0x429176(<span class="string">'IF#P'</span>, <span class="number">0x2b</span>, -<span class="number">0x2cb</span>, <span class="number">0x33e</span>, <span class="number">0x20d</span>)](_0x4996c2[_0xafe698(<span class="string">'TEE1'</span>, <span class="number">0xcb6</span>, <span class="number">0x668</span>, <span class="number">0x5fa</span>, <span class="number">0x833</span>) + <span class="string">'r'</span>](-<span class="number">0x4b</span> * -<span class="number">0x4</span> + -<span class="number">0x2a</span> * <span class="number">0x86</span> + <span class="number">0x5</span> * <span class="number">0x42e</span>, -<span class="number">0xd</span> * -<span class="number">0x119</span> + -<span class="number">0x1791</span> + <span class="number">0x951</span>)),</span><br><span class="line"> _0x4f239d[_0x429176(<span class="string">'j[vi'</span>, <span class="number">0x5e6</span>, <span class="number">0x2bd</span>, <span class="number">0x5e2</span>, <span class="number">0x648</span>)](_0x4996c2[_0x429176(<span class="string">'#og4'</span>, <span class="number">0xd5</span>, <span class="number">0x68e</span>, <span class="number">0x54b</span>, <span class="number">0x516</span>) + <span class="string">'r'</span>](-<span class="number">0x193d</span> + <span class="number">0x399</span> * -<span class="number">0x5</span> + -<span class="number">0x1</span> * -<span class="number">0x2b55</span>, -<span class="number">0x8a8</span> * -<span class="number">0x4</span> + <span class="number">0xd6</span> * <span class="number">0x6</span> + <span class="number">0x3</span> * -<span class="number">0xd35</span>)),</span><br><span class="line"> _0x4f239d[_0x8a0b67(<span class="string">'Cy2U'</span>, <span class="number">0x689</span>, <span class="number">0x578</span>, <span class="number">0x485</span>, <span class="number">0x71e</span>)](_0x4996c2[_0x4b55c2(<span class="string">'4[E4'</span>, <span class="number">0x272</span>, <span class="number">0x5ce</span>, <span class="number">0x4e1</span>, <span class="number">0x668</span>) + <span class="string">'r'</span>](-<span class="number">0x97</span> * <span class="number">0x17</span> + -<span class="number">0xe</span> * <span class="number">0x46</span> + <span class="number">0x1166</span>, -<span class="number">0x18f4</span> * -<span class="number">0x1</span> + -<span class="number">0x93</span> * <span class="number">0x3b</span> + <span class="number">0x8ef</span> * <span class="number">0x1</span>)),</span><br><span class="line"> _0x4f239d[_0x343f8b(<span class="string">'&zHf'</span>, -<span class="number">0xb</span>, -<span class="number">0x5bc</span>, -<span class="number">0x16b</span>, -<span class="number">0x108</span>)](<span class="string">''</span>);</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>看到这么大一段代码不要慌!!!我们慢慢开始剥洋葱。🙃,剥到最后,很简单。</p><figure class="highlight js"><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">_0x58b6e0.prototype.sign = <span class="function"><span class="keyword">function</span>(<span class="params">_0x1c6621</span>) </span>{</span><br><span class="line"> <span class="keyword">var</span> _0x4f239d = [], _0x4996c2 = MD5(_0x1c6621 + <span class="built_in">document</span>.location.href.toLowerCase()).toString();</span><br><span class="line"> <span class="keyword">return</span> _0x4f239d.push(_0x4996c2.substr(<span class="number">10</span>, <span class="number">5</span>)), _0x4f239d.push(_0x4996c2.substr(<span class="number">7</span>, <span class="number">5</span>)), _0x4f239d.push(_0x4996c2.substr(<span class="number">15</span>, <span class="number">5</span>)), _0x4f239d.push(_0x4996c2.substr(<span class="number">20</span>, <span class="number">5</span>)), _0x4f239d.push(_0x4996c2.substr(<span class="number">22</span>, <span class="number">5</span>)), _0x4f239d.push(_0x4996c2.substr(<span class="number">27</span>, <span class="number">5</span>)), _0x4f239d.push(_0x4996c2.substr(<span class="number">1</span>, <span class="number">2</span>)), _0x4f239d.join(<span class="string">""</span>);</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>其实就是对传入的参数做了一个md5的加密,然后进行乱序处理。</p><p>到这里我们两个接口的所有请求参数加密算法,以及接口的返回值的解密算法都已经破解了。我们接下来简单验证下是否正确。</p><h4 id="验证"><a href="#验证" class="headerlink" title="验证"></a>验证</h4><p>由于check接口需要机器学习对验证码进行识别,所以这里只验证get接口的参数。用NodeJS的Express框架搭建好获取token的服务,供Python调用。</p><p>运行结果如下:</p><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="https://img.heshipeng.com/202203122207679.png?watermark/2/text/5YWz5rOo5b6u5L-h5YWs5LyX5Y-377ya6YCG5ZCR5LiA5q2l5q2l/font/5a6L5L2T/fontsize/300" alt="image-20220312220733810" title=""> </div> <div class="image-caption">image-20220312220733810</div> </figure><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="https://img.heshipeng.com/202203122208606.png?watermark/2/text/5YWz5rOo5b6u5L-h5YWs5LyX5Y-377ya6YCG5ZCR5LiA5q2l5q2l/font/5a6L5L2T/fontsize/300" alt="image-20220312220820149" title=""> </div> <div class="image-caption">image-20220312220820149</div> </figure><h4 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h4><p>本文通过一个网站,手动对AST混淆代码进行了一个反混淆。在<a href="https://blog.heshipeng.com/JS%E4%BB%A3%E7%A0%81%E5%AE%89%E5%85%A8%E9%98%B2%E6%8A%A4%E5%8E%9F%E7%90%86%E2%80%94%E2%80%94AST%E6%B7%B7%E6%B7%86%E5%8E%9F%E7%90%86/">JS代码安全防护原理——AST混淆原理</a>中提到的几种混淆原理基本都出现过了。比如数组混淆,数组逆向,花指令,流程平坦化,逗号表达式混淆,字符串加密,常量加密等。可以看到,手动混淆的过程是极其容易出错,工作量非常大且十分痛苦的。接下来会写相关文章,介绍如何通过工具对AST混淆代码进行自动反混淆,不过,手动混淆这种能力也是必须要掌握的,万一你使用的工具失效了,或者说遇到一些更加特殊的网站,只能通过手动混淆呢?如果想获取本文的完整代码,扫码关注公众号,然后公众号内回复关键字<code>02</code>即可获取。</p><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="https://img.heshipeng.com/202203122210618.jpg" alt="qrcode_for_gh_509fdefd3c81_258" title=""> </div> <div class="image-caption">qrcode_for_gh_509fdefd3c81_258</div> </figure>]]></content>
<summary type="html"><blockquote>
<p> 免责声明:<strong>本文章中所有内容仅供学习交流,抓包内容、敏感网址、数据接口均已做脱敏处理,严禁用于商业用途和非法用途,否则由此产生的一切后果均与作者无关,若有侵权,请联系我立即删除!</strong></p>
</blockquote></summary>
<category term="JS逆向" scheme="http://example.com/categories/JS%E9%80%86%E5%90%91/"/>
<category term="AST" scheme="http://example.com/tags/AST/"/>
<category term="JS" scheme="http://example.com/tags/JS/"/>
<category term="逆向" scheme="http://example.com/tags/%E9%80%86%E5%90%91/"/>
</entry>
<entry>
<title>JS代码安全防护原理——AST混淆原理</title>
<link href="http://example.com/JS%E4%BB%A3%E7%A0%81%E5%AE%89%E5%85%A8%E9%98%B2%E6%8A%A4%E5%8E%9F%E7%90%86%E2%80%94%E2%80%94AST%E6%B7%B7%E6%B7%86%E5%8E%9F%E7%90%86/"/>
<id>http://example.com/JS%E4%BB%A3%E7%A0%81%E5%AE%89%E5%85%A8%E9%98%B2%E6%8A%A4%E5%8E%9F%E7%90%86%E2%80%94%E2%80%94AST%E6%B7%B7%E6%B7%86%E5%8E%9F%E7%90%86/</id>
<published>2022-03-03T14:22:11.000Z</published>
<updated>2022-06-29T03:52:35.590Z</updated>
<content type="html"><![CDATA[<h4 id="常量的混淆原理"><a href="#常量的混淆原理" class="headerlink" title="常量的混淆原理"></a>常量的混淆原理</h4><p>本篇所用的demo如下。接下来的案例都是围绕这个demo进行混淆。</p><figure class="highlight js"><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="built_in">Date</span>.prototype.format = <span class="function"><span class="keyword">function</span>(<span class="params">formatStr</span>) </span>{</span><br><span class="line"> <span class="keyword">var</span> str = formatStr;</span><br><span class="line"> <span class="keyword">var</span> Week = [<span class="string">'日'</span>, <span class="string">'一'</span>, <span class="string">'二'</span>, <span class="string">'三'</span>, <span class="string">'四'</span>, <span class="string">'五'</span>, <span class="string">'六'</span>];</span><br><span class="line"> str = str.replace(<span class="regexp">/yyyy|YYYY/</span>, <span class="built_in">this</span>.getFullYear());</span><br><span class="line"> str = str.replace(<span class="regexp">/MM/</span>, (<span class="built_in">this</span>.getMonth() + <span class="number">1</span>) > <span class="number">9</span> ? (<span class="built_in">this</span>.getMonth() + <span class="number">1</span>).toString() : <span class="string">'0'</span> + (<span class="built_in">this</span>.getMonth() + <span class="number">1</span>));</span><br><span class="line"> str = str.replace(<span class="regexp">/dd|DD/</span>, <span class="built_in">this</span>.getDate() > <span class="number">9</span> ? <span class="built_in">this</span>.getDate().toString() : <span class="string">'0'</span> + <span class="built_in">this</span>.getDate());</span><br><span class="line"> <span class="keyword">return</span> str;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="built_in">console</span>.log(<span class="keyword">new</span> <span class="built_in">Date</span>().format(<span class="string">'yyyy-MM-dd'</span>));</span><br></pre></td></tr></table></figure><h5 id="对象属性的两种访问方式"><a href="#对象属性的两种访问方式" class="headerlink" title="对象属性的两种访问方式"></a>对象属性的两种访问方式</h5><p>看下面一段代码:</p><figure class="highlight js"><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="function"><span class="keyword">function</span> <span class="title">People</span>(<span class="params">name</span>) </span>{</span><br><span class="line"> <span class="built_in">this</span>.name = name;</span><br><span class="line">}</span><br><span class="line">People.prototype.sayHello = <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>{</span><br><span class="line"> <span class="built_in">console</span>.log(<span class="string">'Hello'</span>);</span><br><span class="line">}</span><br><span class="line"><span class="keyword">var</span> p = <span class="keyword">new</span> People(<span class="string">'zhang san'</span>);</span><br><span class="line"><span class="built_in">console</span>.log(p.name); <span class="comment">// zhang san</span></span><br><span class="line">p.sayHello(); <span class="comment">// Hello</span></span><br><span class="line"><span class="built_in">console</span>.log(p[<span class="string">'name'</span>]); <span class="comment">// zhang san</span></span><br><span class="line">p[<span class="string">'sayHello'</span>]();<span class="comment">// Hello</span></span><br></pre></td></tr></table></figure><ol><li>p.name这种方式name是一个标识符,必须明确出现在代码中,不能加密和拼接。</li><li>p[‘name’]这种方式name是一个字符串。由于是字符串,所以访问的时候可以进行拼接和加密等操作。所以在JS混淆中,一般会选择这种方式访问属性。</li></ol><p>所以改变对象属性的访问方式,是代码混淆的前提。</p><p>所以开篇提到的demo改变对象属性的访问方式之后,代码修改如下:</p><figure class="highlight js"><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="built_in">window</span>[<span class="string">'Date'</span>][<span class="string">'prototype'</span>][<span class="string">'format'</span>] = <span class="function"><span class="keyword">function</span>(<span class="params">formatStr</span>) </span>{</span><br><span class="line"> <span class="keyword">var</span> str = formatStr;</span><br><span class="line"> <span class="keyword">var</span> Week = [<span class="string">'日'</span>, <span class="string">'一'</span>, <span class="string">'二'</span>, <span class="string">'三'</span>, <span class="string">'四'</span>, <span class="string">'五'</span>, <span class="string">'六'</span>];</span><br><span class="line"> str = str[<span class="string">'replace'</span>](<span class="regexp">/yyyy|YYYY/</span>, <span class="built_in">this</span>[<span class="string">'getFullYear'</span>]());</span><br><span class="line"> str = str[<span class="string">'replace'</span>](<span class="regexp">/MM/</span>, (<span class="built_in">this</span>[<span class="string">'getMonth'</span>]() + <span class="number">1</span>) > <span class="number">9</span> ? (<span class="built_in">this</span>[<span class="string">'getMonth'</span>]() + <span class="number">1</span>).toString() : <span class="string">'0'</span> + (<span class="built_in">this</span>[<span class="string">'getMonth'</span>]() + <span class="number">1</span>));</span><br><span class="line"> str = str[<span class="string">'replace'</span>](<span class="regexp">/dd|DD/</span>, <span class="built_in">this</span>[<span class="string">'getDate'</span>]() > <span class="number">9</span> ? <span class="built_in">this</span>[<span class="string">'getDate'</span>]().toString() : <span class="string">'0'</span> + <span class="built_in">this</span>[<span class="string">'getDate'</span>]());</span><br><span class="line"> <span class="keyword">return</span> str;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="built_in">console</span>.log(<span class="keyword">new</span> <span class="built_in">window</span>[<span class="string">'Date'</span>]()[<span class="string">'format'</span>](<span class="string">'yyyy-MM-dd'</span>));</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>Date是JS的内置对象,在JS中很多内置对象都属于window的属性。另外,代码中定义的全局变量都是全局对象window的属性,代码中定义的全局方法都是全局对象window的方法。全局对象的属性或者方法在调用的时候可以省略全局对象名。比如,new window.Date()等同于new Date()。由于把Date变成了字符串,所以前面必须加window。</p><h5 id="十六进制字符串"><a href="#十六进制字符串" class="headerlink" title="十六进制字符串"></a>十六进制字符串</h5><p>在JS中支持字符串的十六进制形式表示,所以可以用字符串的十六进制形式来代替原有的字符串。比如’yyyy-MM-dd’可以表示成’\x79\x79\x79\x79\x2d\x4d\x4d\x2d\x64\x64’。其实,0x79就是字母y的ASCII码的十六进制形式,其余的字母类推。可以用一个方法来完成十六进制字符串的转换。</p><figure class="highlight js"><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="function"><span class="keyword">function</span> <span class="title">hexEnc</span>(<span class="params">code</span>) </span>{</span><br><span class="line"> <span class="keyword">let</span> hexStr = []</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">let</span> i = <span class="number">0</span>, s; i < code.length; i++) {</span><br><span class="line"> s = code.charCodeAt(i).toString(<span class="number">16</span>);</span><br><span class="line"> hexStr += <span class="string">'\\x'</span> + s;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> hexStr;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>开篇的demo转换为十六进制字符串之后如下:</p><figure class="highlight js"><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="built_in">window</span>[<span class="string">'\x44\x61\x74\x65'</span>][<span class="string">'\x70\x72\x6f\x74\x6f\x74\x79\x70\x65'</span>][<span class="string">'\x66\x6f\x72\x6d\x61\x74'</span>] = <span class="function"><span class="keyword">function</span>(<span class="params">formatStr</span>) </span>{</span><br><span class="line"> <span class="keyword">var</span> str = formatStr;</span><br><span class="line"> <span class="keyword">var</span> Week = [<span class="string">'\x65e5'</span>, <span class="string">'\x4e00'</span>, <span class="string">'\x4e8c'</span>, <span class="string">'\x4e09'</span>, <span class="string">'\x56db'</span>, <span class="string">'\x4e94'</span>, <span class="string">'\x516d'</span>];</span><br><span class="line"> str = str[<span class="string">'\x72\x65\x70\x6c\x61\x63\x65'</span>](<span class="regexp">/yyyy|YYYY/</span>, <span class="built_in">this</span>[<span class="string">'\x67\x65\x74\x46\x75\x6c\x6c\x59\x65\x61\x72'</span>]());</span><br><span class="line"> str = str[<span class="string">'\x72\x65\x70\x6c\x61\x63\x65'</span>](<span class="regexp">/MM/</span>, (<span class="built_in">this</span>[<span class="string">'\x67\x65\x74\x4d\x6f\x6e\x74\x68'</span>]() + <span class="number">1</span>) > <span class="number">9</span> ? (<span class="built_in">this</span>[<span class="string">'\x67\x65\x74\x4d\x6f\x6e\x74\x68'</span>]() + <span class="number">1</span>).toString() : <span class="string">'\x30'</span> + (<span class="built_in">this</span>[<span class="string">'\x67\x65\x74\x4d\x6f\x6e\x74\x68'</span>]() + <span class="number">1</span>));</span><br><span class="line"> str = str[<span class="string">'\x72\x65\x70\x6c\x61\x63\x65'</span>](<span class="regexp">/dd|DD/</span>, <span class="built_in">this</span>[<span class="string">'\x67\x65\x74\x44\x61\x74\x65'</span>]() > <span class="number">9</span> ? <span class="built_in">this</span>[<span class="string">'\x67\x65\x74\x44\x61\x74\x65'</span>]().toString() : <span class="string">'\x30'</span> + <span class="built_in">this</span>[<span class="string">'\x67\x65\x74\x44\x61\x74\x65'</span>]());</span><br><span class="line"> <span class="keyword">return</span> str;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="built_in">console</span>.log(<span class="keyword">new</span> <span class="built_in">window</span>[<span class="string">'\x44\x61\x74\x65'</span>]()[<span class="string">'\x66\x6f\x72\x6d\x61\x74'</span>](<span class="string">'\x79\x79\x79\x79\x2d\x4d\x4d\x2d\x64\x64'</span>));</span><br></pre></td></tr></table></figure><h5 id="unicode字符串"><a href="#unicode字符串" class="headerlink" title="unicode字符串"></a>unicode字符串</h5><p>在JS中,字符串除了可以表示成十六进制的形式外,还支持使用unicode形式表示。比如:</p><ol><li>以var Week = [‘日’, ‘一’, ‘二’, ‘三’, ‘四’, ‘五’, ‘六’]为例,可以表示成var Week = [‘\u65e5’, ‘\u4e00’, ‘\u4e8c’, ‘\u4e09’, ‘\u56db’, ‘\u4e94’, ‘\u516d’]</li><li>非中文的情况,Date可以表示成’\u0044\u0061\u0074\u0065’</li></ol><p>从上述例子不难看出,unicode形式就是\u开头,后面跟四位数的十六进制形式,不足四位的补0。可以通过以下代码完成unicode转换:</p><figure class="highlight js"><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="function"><span class="keyword">function</span> <span class="title">unicodeEnc</span>(<span class="params">str</span>) </span>{</span><br><span class="line"> <span class="keyword">var</span> value = <span class="string">''</span>;</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">var</span> i = <span class="number">0</span>; i < str.length; i++) {</span><br><span class="line"> value += <span class="string">"\\u"</span> + (<span class="string">"0000"</span> + <span class="built_in">parseInt</span>(str.charCodeAt(i)).toString(<span class="number">16</span>)).substr(-<span class="number">4</span>);</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> value;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>开篇的demo转换为unicode编码之后如下:</p><figure class="highlight js"><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="built_in">window</span>[<span class="string">'\u0044\u0061\u0074\u0065'</span>][<span class="string">'\u0070\u0072\u006f\u0074\u006f\u0074\u0079\u0070\u0065'</span>][<span class="string">'\u0066\u006f\u0072\u006d\u0061\u0074'</span>] = <span class="function"><span class="keyword">function</span>(<span class="params">formatStr</span>) </span>{</span><br><span class="line"> <span class="keyword">var</span> str = formatStr;</span><br><span class="line"> <span class="keyword">var</span> Week = [<span class="string">'\u65e5'</span>, <span class="string">'\u4e00'</span>, <span class="string">'\u4e8c'</span>, <span class="string">'\u4e09'</span>, <span class="string">'\u56db'</span>, <span class="string">'\u4e94'</span>, <span class="string">'\u516d'</span>];</span><br><span class="line"> str = str[<span class="string">'\u0072\u0065\u0070\u006c\u0061\u0063\u0065'</span>](<span class="regexp">/yyyy|YYYY/</span>, <span class="built_in">this</span>[<span class="string">'\u0067\u0065\u0074\u0046\u0075\u006c\u006c\u0059\u0065\u0061\u0072'</span>]());</span><br><span class="line"> str = str[<span class="string">'\u0072\u0065\u0070\u006c\u0061\u0063\u0065'</span>](<span class="regexp">/MM/</span>, (<span class="built_in">this</span>[<span class="string">'\u0067\u0065\u0074\u004d\u006f\u006e\u0074\u0068'</span>]() + <span class="number">1</span>) > <span class="number">9</span> ? (<span class="built_in">this</span>[<span class="string">'\u0067\u0065\u0074\u004d\u006f\u006e\u0074\u0068'</span>]() + <span class="number">1</span>).toString() : <span class="string">'\u0030'</span> + (<span class="built_in">this</span>[<span class="string">'\u0067\u0065\u0074\u004d\u006f\u006e\u0074\u0068'</span>]() + <span class="number">1</span>));</span><br><span class="line"> str = str[<span class="string">'\u0072\u0065\u0070\u006c\u0061\u0063\u0065'</span>](<span class="regexp">/dd|DD/</span>, <span class="built_in">this</span>[<span class="string">'\u0067\u0065\u0074\u0044\u0061\u0074\u0065'</span>]() > <span class="number">9</span> ? <span class="built_in">this</span>[<span class="string">'\u0067\u0065\u0074\u0044\u0061\u0074\u0065'</span>]().toString() : <span class="string">'\u0030'</span> + <span class="built_in">this</span>[<span class="string">'\u0067\u0065\u0074\u0044\u0061\u0074\u0065'</span>]());</span><br><span class="line"> <span class="keyword">return</span> str;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="built_in">console</span>.log(<span class="keyword">new</span> <span class="built_in">window</span>[<span class="string">'\u0044\u0061\u0074\u0065'</span>]()[<span class="string">'\u0066\u006f\u0072\u006d\u0061\u0074'</span>](<span class="string">'\u0079\u0079\u0079\u0079\u002d\u004d\u004d\u002d\u0064\u0064'</span>));</span><br></pre></td></tr></table></figure><p>unicode字符和十六进制字符串都能轻易的还原,即直接把字符串放到控制台打印即可还原。</p><h5 id="字符串的ASCII码混淆"><a href="#字符串的ASCII码混淆" class="headerlink" title="字符串的ASCII码混淆"></a>字符串的ASCII码混淆</h5><p>首先关注2个方法。</p><figure class="highlight js"><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="built_in">console</span>.log(<span class="string">'x'</span>.charCodeAt(<span class="number">0</span>)); <span class="comment">//120</span></span><br><span class="line"><span class="built_in">console</span>.log(<span class="string">'b'</span>.charCodeAt(<span class="number">0</span>)); <span class="comment">//98</span></span><br><span class="line"><span class="built_in">console</span>.log(<span class="built_in">String</span>.fromCharCode(<span class="number">120</span>, <span class="number">98</span>)); <span class="comment">//xb</span></span><br></pre></td></tr></table></figure><p>charCodeAt方法表示把字符串转换成ASCII编码,fromCharCode表示把ASCII码转换为字符串形式。</p><p>可以通过以下代码将字符串变成字节数组。</p><figure class="highlight js"><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="function"><span class="keyword">function</span> <span class="title">stringToByte</span>(<span class="params">str</span>) </span>{</span><br><span class="line"> <span class="keyword">var</span> byteArr = [];</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">var</span> i = <span class="number">0</span>; i < str.length; i++) {</span><br><span class="line"> byteArr.push(str.charCodeAt(i));</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> byteArr;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>比如demo中的format可以用字节数组表示为[102, 111, 114, 109, 97, 116],因此代码中的format可以表示成String.fromCharCode(102, 111, 114, 109, 97, 116)。注意,fromCharCode接受的参数类型不是数组,而是可变参数类型。如果非要传一个数组,可以使用String.fromCharCode.apply(null, [102, 111, 114, 109, 97, 116])。<strong>JS的函数也是对象,可以给函数定义属性和方法,而函数本身也自带一些属性和方法。apply就是从函数的原型对象Function.prototype继承过来的方法</strong>。</p><p>ASCII码混淆不仅可以用于混淆字符串,还可以用来做代码混淆。比如我们把代码<code>str = str['replace'](/yyyy|YYYY/, this['getFullYear']());</code>看作一个字符串:</p><figure class="highlight js"><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">stringToByte(<span class="string">"str = str['replace'](/yyyy|YYYY/, this['getFullYear']());"</span>);</span><br><span class="line"><span class="comment">// [ 115, 116, 114, 32, 61, 32, 115, 116, 114, 91, 39, 114, 101, 112, 108, 97, 99, 101, 39, 93, 40, 47, 121, 121, 121, 121, 124, 89, 89, 89, 89, 47, 44, 32, 116, 104, 105, 115, 91, 39, 103, 101, 116, 70, 117, 108, 108, 89, 101, 97, 114, 39, 93, 40, 41, 41, 59 ]</span></span><br></pre></td></tr></table></figure><p>然后再把这个字符串当作代码执行即可。在JS中把字符串当作代码执行的有2个方法,eval和Function。其中eval用来执行一段代码,Function用来生成一个函数。</p><p>所以我们之前的demo可以改写如下:</p><figure class="highlight js"><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="built_in">window</span>[<span class="string">'\u0044\u0061\u0074\u0065'</span>][<span class="string">'\u0070\u0072\u006f\u0074\u006f\u0074\u0079\u0070\u0065'</span>][<span class="string">'\u0066\u006f\u0072\u006d\u0061\u0074'</span>] = <span class="function"><span class="keyword">function</span>(<span class="params">formatStr</span>) </span>{</span><br><span class="line"> <span class="keyword">var</span> str = formatStr;</span><br><span class="line"> <span class="keyword">var</span> Week = [<span class="string">'\u65e5'</span>, <span class="string">'\u4e00'</span>, <span class="string">'\u4e8c'</span>, <span class="string">'\u4e09'</span>, <span class="string">'\u56db'</span>, <span class="string">'\u4e94'</span>, <span class="string">'\u516d'</span>];</span><br><span class="line"> <span class="built_in">eval</span>(<span class="built_in">String</span>.fromCharCode(<span class="number">115</span>, <span class="number">116</span>, <span class="number">114</span>, <span class="number">32</span>, <span class="number">61</span>, <span class="number">32</span>, <span class="number">115</span>, <span class="number">116</span>, <span class="number">114</span>, <span class="number">91</span>, <span class="number">39</span>, <span class="number">114</span>, <span class="number">101</span>, <span class="number">112</span>, <span class="number">108</span>, <span class="number">97</span>, <span class="number">99</span>, <span class="number">101</span>, <span class="number">39</span>, <span class="number">93</span>, <span class="number">40</span>, <span class="number">47</span>, <span class="number">121</span>, <span class="number">121</span>, <span class="number">121</span>, <span class="number">121</span>, <span class="number">124</span>, <span class="number">89</span>, <span class="number">89</span>, <span class="number">89</span>, <span class="number">89</span>, <span class="number">47</span>, <span class="number">44</span>, <span class="number">32</span>, <span class="number">116</span>, <span class="number">104</span>, <span class="number">105</span>, <span class="number">115</span>, <span class="number">91</span>, <span class="number">39</span>, <span class="number">103</span>, <span class="number">101</span>, <span class="number">116</span>, <span class="number">70</span>, <span class="number">117</span>, <span class="number">108</span>, <span class="number">108</span>, <span class="number">89</span>, <span class="number">101</span>, <span class="number">97</span>, <span class="number">114</span>, <span class="number">39</span>, <span class="number">93</span>, <span class="number">40</span>, <span class="number">41</span>, <span class="number">41</span>, <span class="number">59</span>))</span><br><span class="line"> str = str[<span class="string">'\u0072\u0065\u0070\u006c\u0061\u0063\u0065'</span>](<span class="regexp">/MM/</span>, (<span class="built_in">this</span>[<span class="string">'\u0067\u0065\u0074\u004d\u006f\u006e\u0074\u0068'</span>]() + <span class="number">1</span>) > <span class="number">9</span> ? (<span class="built_in">this</span>[<span class="string">'\u0067\u0065\u0074\u004d\u006f\u006e\u0074\u0068'</span>]() + <span class="number">1</span>).toString() : <span class="string">'\u0030'</span> + (<span class="built_in">this</span>[<span class="string">'\u0067\u0065\u0074\u004d\u006f\u006e\u0074\u0068'</span>]() + <span class="number">1</span>));</span><br><span class="line"> str = str[<span class="string">'\u0072\u0065\u0070\u006c\u0061\u0063\u0065'</span>](<span class="regexp">/dd|DD/</span>, <span class="built_in">this</span>[<span class="string">'\u0067\u0065\u0074\u0044\u0061\u0074\u0065'</span>]() > <span class="number">9</span> ? <span class="built_in">this</span>[<span class="string">'\u0067\u0065\u0074\u0044\u0061\u0074\u0065'</span>]().toString() : <span class="string">'\u0030'</span> + <span class="built_in">this</span>[<span class="string">'\u0067\u0065\u0074\u0044\u0061\u0074\u0065'</span>]());</span><br><span class="line"> <span class="keyword">return</span> str;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="built_in">console</span>.log(<span class="keyword">new</span> <span class="built_in">window</span>[<span class="string">'\u0044\u0061\u0074\u0065'</span>]()[<span class="string">'\u0066\u006f\u0072\u006d\u0061\u0074'</span>](<span class="string">'\u0079\u0079\u0079\u0079\u002d\u004d\u004d\u002d\u0064\u0064'</span>));</span><br></pre></td></tr></table></figure><h5 id="字符串常量加密"><a href="#字符串常量加密" class="headerlink" title="字符串常量加密"></a>字符串常量加密</h5><p>字符串加密的最核心思想是先把字符串加密得到密文,然后在使用之前,调用对应的函数去解密,得到明文。代码中仅仅出现解密函数和明密文。当然也可以使用不同的加密方法去加密字符串,然后再调用不同的解密函数去解密。字符串加密最简单的方式是Base64编码。</p><p>浏览器中自带Base64的编码与解码的函数,其中btoa用来编码,atob用来解码。但是实际的应用中最好是自己去实现,然后加以混淆。注意,字符串加密之后,需要把对应的解码函数放入其中,方能正常运转。比如开头的demo可以改写如下:</p><figure class="highlight js"><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="built_in">window</span>[atob(<span class="string">'RGF0ZQ=='</span>)][atob(<span class="string">'cHJvdG90eXBl'</span>)][atob(<span class="string">'Zm9ybWF0'</span>)] = <span class="function"><span class="keyword">function</span>(<span class="params">formatStr</span>) </span>{</span><br><span class="line"> <span class="keyword">var</span> str = formatStr;</span><br><span class="line"> <span class="keyword">var</span> Week = [<span class="string">'\u65e5'</span>, <span class="string">'\u4e00'</span>, <span class="string">'\u4e8c'</span>, <span class="string">'\u4e09'</span>, <span class="string">'\u56db'</span>, <span class="string">'\u4e94'</span>, <span class="string">'\u516d'</span>];</span><br><span class="line"> str = str[atob(<span class="string">'cmVwbGFjZQ=='</span>)](<span class="regexp">/yyyy|YYYY/</span>, <span class="built_in">this</span>[atob(<span class="string">'Z2V0RnVsbFllYXI='</span>)]());</span><br><span class="line"> str = str[atob(<span class="string">'cmVwbGFjZQ=='</span>)](<span class="regexp">/MM/</span>, (<span class="built_in">this</span>[atob(<span class="string">'Z2V0TW9udGg='</span>)]() + <span class="number">1</span>) > <span class="number">9</span> ? (<span class="built_in">this</span>[atob(<span class="string">'Z2V0TW9udGg='</span>)]() + <span class="number">1</span>).toString() : atob(<span class="string">'MA=='</span>) + (<span class="built_in">this</span>[atob(<span class="string">'Z2V0TW9udGg='</span>)]() + <span class="number">1</span>));</span><br><span class="line"> str = str[atob(<span class="string">'cmVwbGFjZQ=='</span>)](<span class="regexp">/dd|DD/</span>, <span class="built_in">this</span>[atob(<span class="string">'Z2V0RGF0ZQ=='</span>)]() > <span class="number">9</span> ? <span class="built_in">this</span>[atob(<span class="string">'Z2V0RGF0ZQ=='</span>)]().toString() : atob(<span class="string">'MA=='</span>) + <span class="built_in">this</span>[atob(<span class="string">'Z2V0RGF0ZQ=='</span>)]());</span><br><span class="line"> <span class="keyword">return</span> str;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="built_in">console</span>.log(<span class="keyword">new</span> <span class="built_in">window</span>[atob(<span class="string">'RGF0ZQ=='</span>)]()[atob(<span class="string">'Zm9ybWF0'</span>)](atob(<span class="string">'eXl5eS1NTS1kZA=='</span>)));</span><br></pre></td></tr></table></figure><p>在实际的混淆应用中,标识符必须处理成没有语义的,不然很容易定位到关键代码。此外,建议减少使用系统函数,自己去实现相应的函数,因为不管怎样混淆,最终执行过程中,系统函数是固定的,通过Hook技术很容易定位到关键代码。</p><h5 id="数值常量加密"><a href="#数值常量加密" class="headerlink" title="数值常量加密"></a>数值常量加密</h5><p>算法加密过程中,会使用一些固定的数值常量,如MD5中的常量0×67452301,0xefcdab89,0x98badcfe和0x10325476,以及SHA1中的常量0x67452301, 0xefcdab89,0x98badcte,0x10325476和0xc3d2e1f0。因此,在标准算法逆向中,会通过搜索这些数值量,来定位代码关键位置,或者确定使用的是哪个算法。当然,在代码中不一定会写十六进制形式,如0x67452301,在代码中可能会与成十进制的1732584193。 安全起见,可以把这些数值常量也进行简单加密。<br>可以利用位异或的特性来加密。例如,如果a^b=c,那么c^b=a。以SHA1算法中的0xc3d2e1f0常量为例,0xc3d2elf0^0x12345678=0xd1e6b788,那么在代码中可以用0xd1e66788^0x12345678来代替0xc3d2e1f0,其中0x12345678可以理解成密钥,它可以随机生成。</p><h4 id="增加JS逆向者的工作量"><a href="#增加JS逆向者的工作量" class="headerlink" title="增加JS逆向者的工作量"></a>增加JS逆向者的工作量</h4><h5 id="数组混淆"><a href="#数组混淆" class="headerlink" title="数组混淆"></a>数组混淆</h5><p>看一行代码:</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">console</span>.log(<span class="keyword">new</span> <span class="built_in">window</span>.Date().getTime());</span><br></pre></td></tr></table></figure><p>按照前面介绍的对象属性的2种访问方式,使用第二种改写如下:</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">console</span>[<span class="string">'log'</span>](<span class="keyword">new</span> <span class="built_in">window</span>[<span class="string">'Date'</span>]()[<span class="string">'getTime'</span>]());</span><br></pre></td></tr></table></figure><p>这样产生了三个字符串,我们把三个字符串放在数组里面。</p><figure class="highlight js"><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="keyword">var</span> bigArr = [<span class="string">'Date'</span>, <span class="string">'getTime'</span>, <span class="string">'log'</span>];</span><br><span class="line"><span class="built_in">console</span>[bigArr[<span class="number">2</span>]](<span class="keyword">new</span> <span class="built_in">window</span>[bigArr[<span class="number">0</span>]]()[bigArr[<span class="number">1</span>]]());</span><br></pre></td></tr></table></figure><p>这就是数组混淆。当代码有上千行,那么数组可以提取的字符串可能也有上千个,然后在代码中引用字符串的时候,全部以bigArr[1001],bigArr[1002]这种去访问,这样更加不容易建立映射关系了。</p><p>在JS中,同一个数组可以存放各种类型,比如布尔,数值,字符串,数组,对象和函数等。因此可以把代码中的一部分函数提取到大数组中去。为了安全,通常对提取到数组中的字符串进行加密处理,把代码处理成字符串就可以进行加密了。比如,对于String.fromCharCode可以改写成:</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="string">""</span>[<span class="string">"constructor"</span>][<span class="string">"fromCharCode"</span>]</span><br></pre></td></tr></table></figure><p>最前面的表示任意字符串对象或者空字符串,constructor表示构造方法,这样<code>""["constructor"]</code>就相当于String。前面的demo处理成数组混淆的形式如下:</p><figure class="highlight js"><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="keyword">var</span> bigArr = [</span><br><span class="line"> <span class="string">'\u65e5'</span>, <span class="string">'\u4e00'</span>, <span class="string">'\u4e8c'</span>, <span class="string">'\u4e09'</span>, <span class="string">'\u56db'</span>, <span class="string">'\u4e94'</span>, <span class="string">'\u516d'</span>, <span class="string">'cmVwbGFjZQ=='</span>, <span class="string">'Z2V0TW9udGg='</span>, <span class="string">'MA=='</span>, <span class="string">'Z2V0RGF0ZQ=='</span>, <span class="string">'RGF0ZQ=='</span>, <span class="string">'Zm9ybWF0'</span>, <span class="string">'eXl5eS1NTS1kZA=='</span>, <span class="string">""</span>[<span class="string">'constructor'</span>][<span class="string">'fromCharCode'</span>]</span><br><span class="line">];</span><br><span class="line"></span><br><span class="line"><span class="built_in">window</span>[atob(<span class="string">'RGF0ZQ=='</span>)][atob(<span class="string">'cHJvdG90eXBl'</span>)][atob(<span class="string">'Zm9ybWF0'</span>)] = <span class="function"><span class="keyword">function</span>(<span class="params">formatStr</span>) </span>{</span><br><span class="line"> <span class="keyword">var</span> str = formatStr;</span><br><span class="line"> <span class="keyword">var</span> Week = [bigArr[<span class="number">0</span>], bigArr[<span class="number">1</span>], bigArr[<span class="number">2</span>], bigArr[<span class="number">3</span>], bigArr[<span class="number">4</span>], bigArr[<span class="number">5</span>], bigArr[<span class="number">6</span>]];</span><br><span class="line"> <span class="built_in">eval</span>(bigArr[<span class="number">14</span>](<span class="number">115</span>, <span class="number">116</span>, <span class="number">114</span>, <span class="number">32</span>, <span class="number">61</span>, <span class="number">32</span>, <span class="number">115</span>, <span class="number">116</span>, <span class="number">114</span>, <span class="number">91</span>, <span class="number">39</span>, <span class="number">114</span>, <span class="number">101</span>, <span class="number">112</span>, <span class="number">108</span>, <span class="number">97</span>, <span class="number">99</span>, <span class="number">101</span>, <span class="number">39</span>, <span class="number">93</span>, <span class="number">40</span>, <span class="number">47</span>, <span class="number">121</span>, <span class="number">121</span>, <span class="number">121</span>, <span class="number">121</span>, <span class="number">124</span>, <span class="number">89</span>, <span class="number">89</span>, <span class="number">89</span>, <span class="number">89</span>, <span class="number">47</span>, <span class="number">44</span>, <span class="number">32</span>, <span class="number">116</span>, <span class="number">104</span>, <span class="number">105</span>, <span class="number">115</span>, <span class="number">91</span>, <span class="number">39</span>, <span class="number">103</span>, <span class="number">101</span>, <span class="number">116</span>, <span class="number">70</span>, <span class="number">117</span>, <span class="number">108</span>, <span class="number">108</span>, <span class="number">89</span>, <span class="number">101</span>, <span class="number">97</span>, <span class="number">114</span>, <span class="number">39</span>, <span class="number">93</span>, <span class="number">40</span>, <span class="number">41</span>, <span class="number">41</span>, <span class="number">59</span>));</span><br><span class="line"> str = str[atob(bigArr[<span class="number">7</span>])](<span class="regexp">/MM/</span>, (<span class="built_in">this</span>[atob(bigArr[<span class="number">8</span>])]() + <span class="number">1</span>) > <span class="number">9</span> ? (<span class="built_in">this</span>[atob(bigArr[<span class="number">8</span>])]() + <span class="number">1</span>).toString() : atob(bigArr[<span class="number">9</span>]) + (<span class="built_in">this</span>[atob(bigArr[<span class="number">8</span>])]() + <span class="number">1</span>));</span><br><span class="line"> str = str[atob(bigArr[<span class="number">7</span>])](<span class="regexp">/dd|DD/</span>, <span class="built_in">this</span>[atob(bigArr[<span class="number">10</span>])]() > <span class="number">9</span> ? <span class="built_in">this</span>[atob(bigArr[<span class="number">10</span>])]().toString() : atob(bigArr[<span class="number">9</span>]) + <span class="built_in">this</span>[atob(bigArr[<span class="number">10</span>])]());</span><br><span class="line"> <span class="keyword">return</span> str;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="built_in">console</span>.log(<span class="keyword">new</span> <span class="built_in">window</span>[atob(bigArr[<span class="number">11</span>])]()[atob(bigArr[<span class="number">12</span>])](atob(bigArr[<span class="number">13</span>])));</span><br></pre></td></tr></table></figure><h5 id="数组乱序"><a href="#数组乱序" class="headerlink" title="数组乱序"></a>数组乱序</h5><p>上边进行数组混淆之后,数组下标索引与数组成员是一一对应。比如引用bigArr[14]的地方,需要成员String.fromCharCode,而该数组的下标为14的成员刚好是这个方法。可以将数组成员打乱,这样在分析的时候就更加费力,然后在执行的时候,通过一个方法将打乱的数组还原,从而不影响正确的逻辑。</p><p>可以使用以下代码打乱数组:</p><figure class="highlight js"><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">var</span> bigArr = [</span><br><span class="line"> <span class="string">'\u65e5'</span>, <span class="string">'\u4e00'</span>, <span class="string">'\u4e8c'</span>, <span class="string">'\u4e09'</span>, <span class="string">'\u56db'</span>, <span class="string">'\u4e94'</span>, <span class="string">'\u516d'</span>, <span class="string">'cmVwbGFjZQ=='</span>, <span class="string">'Z2V0TW9udGg='</span>, <span class="string">'MA=='</span>, <span class="string">'Z2V0RGF0ZQ=='</span>, <span class="string">'RGF0ZQ=='</span>, <span class="string">'Zm9ybWF0'</span>, <span class="string">'eXl5eS1NTS1kZA=='</span>, <span class="string">""</span>[<span class="string">'constructor'</span>][<span class="string">'fromCharCode'</span>]</span><br><span class="line">];</span><br><span class="line"></span><br><span class="line">(<span class="function"><span class="keyword">function</span>(<span class="params">arr, num</span>) </span>{</span><br><span class="line"> <span class="keyword">var</span> shuffer = <span class="function"><span class="keyword">function</span>(<span class="params">nums</span>) </span>{</span><br><span class="line"> <span class="keyword">while</span> (--nums) {</span><br><span class="line"> arr.unshift(arr.pop());</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> shuffer(++num);</span><br><span class="line">}(bigArr, <span class="number">0x20</span>));</span><br></pre></td></tr></table></figure><p>可以使用以下代码还原数组:</p><figure class="highlight js"><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">var</span> bigArr = [</span><br><span class="line"> <span class="string">'eXl5eS1NTS1kZA=='</span>, <span class="string">""</span>[<span class="string">'constructor'</span>][<span class="string">'fromCharCode'</span>], <span class="string">'\u65e5'</span>, <span class="string">'\u4e00'</span>, <span class="string">'\u4e8c'</span>, <span class="string">'\u4e09'</span>, <span class="string">'\u56db'</span>, <span class="string">'\u4e94'</span>, <span class="string">'\u516d'</span>, <span class="string">'cmVwbGFjZQ=='</span>, <span class="string">'Z2V0TW9udGg='</span>, <span class="string">'MA=='</span>, <span class="string">'Z2V0RGF0ZQ=='</span>, <span class="string">'RGF0ZQ=='</span>, <span class="string">'Zm9ybWF0'</span></span><br><span class="line">];</span><br><span class="line"></span><br><span class="line">(<span class="function"><span class="keyword">function</span>(<span class="params">arr, num</span>) </span>{</span><br><span class="line"> <span class="keyword">var</span> shuffer = <span class="function"><span class="keyword">function</span>(<span class="params">nums</span>) </span>{</span><br><span class="line"> <span class="keyword">while</span> (--nums) {</span><br><span class="line"> arr[<span class="string">'push'</span>](arr[<span class="string">'shift'</span>]());</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> shuffer(++num);</span><br><span class="line">}(bigArr, <span class="number">0x20</span>));</span><br></pre></td></tr></table></figure><p>所以最前面的demo经过数组混淆和数组乱序之后,可以改写为如下:</p><figure class="highlight js"><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"><span class="keyword">var</span> bigArr = [</span><br><span class="line"> <span class="string">'eXl5eS1NTS1kZA=='</span>, <span class="string">""</span>[<span class="string">'constructor'</span>][<span class="string">'fromCharCode'</span>], <span class="string">'\u65e5'</span>, <span class="string">'\u4e00'</span>, <span class="string">'\u4e8c'</span>, <span class="string">'\u4e09'</span>, <span class="string">'\u56db'</span>, <span class="string">'\u4e94'</span>, <span class="string">'\u516d'</span>, <span class="string">'cmVwbGFjZQ=='</span>, <span class="string">'Z2V0TW9udGg='</span>, <span class="string">'MA=='</span>, <span class="string">'Z2V0RGF0ZQ=='</span>, <span class="string">'RGF0ZQ=='</span>, <span class="string">'Zm9ybWF0'</span></span><br><span class="line">];</span><br><span class="line"></span><br><span class="line">(<span class="function"><span class="keyword">function</span>(<span class="params">arr, num</span>) </span>{</span><br><span class="line"> <span class="keyword">var</span> shuffer = <span class="function"><span class="keyword">function</span>(<span class="params">nums</span>) </span>{</span><br><span class="line"> <span class="keyword">while</span> (--nums) {</span><br><span class="line"> arr[<span class="string">'push'</span>](arr[<span class="string">'shift'</span>]());</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> shuffer(++num);</span><br><span class="line">}(bigArr, <span class="number">0x20</span>));</span><br><span class="line"></span><br><span class="line"><span class="built_in">window</span>[atob(<span class="string">'RGF0ZQ=='</span>)][atob(<span class="string">'cHJvdG90eXBl'</span>)][atob(<span class="string">'Zm9ybWF0'</span>)] = <span class="function"><span class="keyword">function</span>(<span class="params">formatStr</span>) </span>{</span><br><span class="line"> <span class="keyword">var</span> str = formatStr;</span><br><span class="line"> <span class="keyword">var</span> Week = [bigArr[<span class="number">0</span>], bigArr[<span class="number">1</span>], bigArr[<span class="number">2</span>], bigArr[<span class="number">3</span>], bigArr[<span class="number">4</span>], bigArr[<span class="number">5</span>], bigArr[<span class="number">6</span>]];</span><br><span class="line"> <span class="built_in">eval</span>(bigArr[<span class="number">14</span>](<span class="number">115</span>, <span class="number">116</span>, <span class="number">114</span>, <span class="number">32</span>, <span class="number">61</span>, <span class="number">32</span>, <span class="number">115</span>, <span class="number">116</span>, <span class="number">114</span>, <span class="number">91</span>, <span class="number">39</span>, <span class="number">114</span>, <span class="number">101</span>, <span class="number">112</span>, <span class="number">108</span>, <span class="number">97</span>, <span class="number">99</span>, <span class="number">101</span>, <span class="number">39</span>, <span class="number">93</span>, <span class="number">40</span>, <span class="number">47</span>, <span class="number">121</span>, <span class="number">121</span>, <span class="number">121</span>, <span class="number">121</span>, <span class="number">124</span>, <span class="number">89</span>, <span class="number">89</span>, <span class="number">89</span>, <span class="number">89</span>, <span class="number">47</span>, <span class="number">44</span>, <span class="number">32</span>, <span class="number">116</span>, <span class="number">104</span>, <span class="number">105</span>, <span class="number">115</span>, <span class="number">91</span>, <span class="number">39</span>, <span class="number">103</span>, <span class="number">101</span>, <span class="number">116</span>, <span class="number">70</span>, <span class="number">117</span>, <span class="number">108</span>, <span class="number">108</span>, <span class="number">89</span>, <span class="number">101</span>, <span class="number">97</span>, <span class="number">114</span>, <span class="number">39</span>, <span class="number">93</span>, <span class="number">40</span>, <span class="number">41</span>, <span class="number">41</span>, <span class="number">59</span>));</span><br><span class="line"> str = str[atob(bigArr[<span class="number">7</span>])](<span class="regexp">/MM/</span>, (<span class="built_in">this</span>[atob(bigArr[<span class="number">8</span>])]() + <span class="number">1</span>) > <span class="number">9</span> ? (<span class="built_in">this</span>[atob(bigArr[<span class="number">8</span>])]() + <span class="number">1</span>).toString() : atob(bigArr[<span class="number">9</span>]) + (<span class="built_in">this</span>[atob(bigArr[<span class="number">8</span>])]() + <span class="number">1</span>));</span><br><span class="line"> str = str[atob(bigArr[<span class="number">7</span>])](<span class="regexp">/dd|DD/</span>, <span class="built_in">this</span>[atob(bigArr[<span class="number">10</span>])]() > <span class="number">9</span> ? <span class="built_in">this</span>[atob(bigArr[<span class="number">10</span>])]().toString() : atob(bigArr[<span class="number">9</span>]) + <span class="built_in">this</span>[atob(bigArr[<span class="number">10</span>])]());</span><br><span class="line"> <span class="keyword">return</span> str;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="built_in">console</span>.log(<span class="keyword">new</span> <span class="built_in">window</span>[atob(bigArr[<span class="number">11</span>])]()[atob(bigArr[<span class="number">12</span>])](atob(bigArr[<span class="number">13</span>])));</span><br></pre></td></tr></table></figure><h5 id="花指令"><a href="#花指令" class="headerlink" title="花指令"></a>花指令</h5><p>所谓花指令,就是添加一些没有意义却可以混淆视听的代码。以前面提到的demo中的某一行代码为例:</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">str = str.replace(<span class="regexp">/MM/</span>, (<span class="built_in">this</span>.getMonth() + <span class="number">1</span>) > <span class="number">9</span> ? (<span class="built_in">this</span>.getMonth() + <span class="number">1</span>).toString() : <span class="string">'0'</span> + (<span class="built_in">this</span>.getMonth() + <span class="number">1</span>));</span><br></pre></td></tr></table></figure><p>把this.getMonth() + 1这个二项式改写为:</p><figure class="highlight js"><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="function"><span class="keyword">function</span> <span class="title">_0x20abefx1</span>(<span class="params">a, b</span>) </span>{</span><br><span class="line"> <span class="keyword">return</span> a + b;</span><br><span class="line">}</span><br><span class="line"><span class="comment">// str = str.replace(/MM/, (_0x20abefx1(new Date().getMonth(), 1)) > 9 ? (_0x20abefx1(new Date().getMonth(), 1)).toString() : '0' + (_0x20abefx1(new Date().getMonth(), 1)));</span></span><br></pre></td></tr></table></figure><p>本质是把一个二项式拆成三个部分,最左边和最右边的参数,中间的运算符可以封装成一个函数,这个函数没有什么意义,但是能瞬间增加代码量,从而增加JS你逆向者的工作量。</p><p>二项式转成函数时,还可以多级嵌套:</p><figure class="highlight js"><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="function"><span class="keyword">function</span> <span class="title">_0x20abefx1</span>(<span class="params">a, b</span>) </span>{</span><br><span class="line"> <span class="keyword">return</span> a + b;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">_0x20abefx2</span>(<span class="params">a, b</span>) </span>{</span><br><span class="line"> <span class="keyword">return</span> _0x20abefx1(a, b);</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>具有同样功能的二项式,可以调用不同的函数,比如:</p><figure class="highlight js"><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="function"><span class="keyword">function</span> <span class="title">_0x20abefx1</span>(<span class="params">a, b</span>) </span>{</span><br><span class="line"> <span class="keyword">return</span> a + b;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">_0x20abefx2</span>(<span class="params">a, b</span>) </span>{</span><br><span class="line"> <span class="keyword">return</span> a + b;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">_0x20abefx3</span>(<span class="params">a, b</span>) </span>{</span><br><span class="line"> <span class="keyword">return</span> _0x20abefx1(a, b);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// str = str.replace(/MM/, (_0x20abefx1(new Date().getMonth(), 1)) > 9 ? (_0x20abefx2(new Date().getMonth(), 1)).toString() : '0' + (_0x20abefx3(new Date().getMonth(), 1)));</span></span><br></pre></td></tr></table></figure><p>除了二项式转为函数可以使用花指令,函数调用表达式也可以处理成类似的花指令:</p><figure class="highlight js"><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"><span class="function"><span class="keyword">function</span> <span class="title">_0x20abefx1</span>(<span class="params">a, b, c</span>) </span>{</span><br><span class="line"> <span class="keyword">return</span> a(b, c);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">_0x20abefx2</span>(<span class="params">a, b, c</span>) </span>{</span><br><span class="line"> <span class="keyword">return</span> _0x20abefx1(a, b, c);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">_0x20abefx3</span>(<span class="params">a, b</span>) </span>{</span><br><span class="line"> <span class="keyword">return</span> a + b;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">str = str.replace(</span><br><span class="line"> <span class="regexp">/MM/</span>, </span><br><span class="line"> _0x20abefx2(_0x20abefx3, <span class="keyword">new</span> <span class="built_in">Date</span>().getMonth(), <span class="number">1</span>) > <span class="number">9</span> ? (_0x20abefx2(_0x20abefx3, <span class="keyword">new</span> <span class="built_in">Date</span>().getMonth(), <span class="number">1</span>)).toString() : _0x20abefx2(_0x20abefx3, <span class="string">'0'</span>, (_0x20abefx2(_0x20abefx3, <span class="keyword">new</span> <span class="built_in">Date</span>().getMonth(), <span class="number">1</span>)))</span><br><span class="line">);</span><br></pre></td></tr></table></figure><h5 id="jsfuck"><a href="#jsfuck" class="headerlink" title="jsfuck"></a>jsfuck</h5><p>jsfuck可以看作一种编码方式,可以把JS代码通过只用(),[],+,!这6种字符表示的代码,并且可以正常阅读。比如数值常量8可以表示成:</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">(!+[]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+[])</span><br></pre></td></tr></table></figure><p>[]表示空数组,+[]表示把空数组转换为数值然后进行运行,由于空数组是0,所以+[]表示数值0,!+[]表示对数值0取反为布尔值true。</p><p>JS中有七种值为false,其余都为true。这7种值为false, undefine, null, 0, -0, NaN和””。所以!![]为true。</p><p>JS中的+运算符作为一个二元运算符时,假如有一边是字符串则代表字符串拼接,否则代表数值相加。所以true + true = 2。</p><p>在实际的开发中,jsfuck的应用有限。只会应用于js文件中的一部分代码。主要它的代码量非常庞大,且易于还原,只需要将代码复制到console即可还原。在jsfuck的混淆中,通过用()来进行分组,如果我们遇到JS文件局部应用jsfuck的代码,可以先通过()将代码分组,然后逐组逐组的分析还原。</p><h4 id="代码执行流程的防护原理"><a href="#代码执行流程的防护原理" class="headerlink" title="代码执行流程的防护原理"></a>代码执行流程的防护原理</h4><h5 id="流程平坦化"><a href="#流程平坦化" class="headerlink" title="流程平坦化"></a>流程平坦化</h5><p>在流程平坦化混淆中,会用到switch语句,因为switch语句中的case块是平级的,而且调换case块的前后顺序并不影响代码原先的执行逻辑。看一段代码:</p><figure class="highlight js"><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"><span class="function"><span class="keyword">function</span> <span class="title">test</span>(<span class="params"></span>) </span>{</span><br><span class="line"> <span class="keyword">var</span> a = <span class="number">100</span>;</span><br><span class="line"> <span class="keyword">var</span> b = a + <span class="number">200</span>;</span><br><span class="line"> <span class="keyword">var</span> c = b + <span class="number">300</span>;</span><br><span class="line"> <span class="keyword">var</span> d = c + <span class="number">400</span>;</span><br><span class="line"> <span class="keyword">var</span> e = d + <span class="number">500</span>;</span><br><span class="line"> <span class="keyword">var</span> f = e + <span class="number">600</span>;</span><br><span class="line"> <span class="keyword">return</span> f;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>混淆test方法的执行流程为:首先把代码分块,且打乱代码块的顺序,分别添加到不同的case块中,方便起见,这里处理场一行代码对应一个case块的形式,代码如下:</p><figure class="highlight js"><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"><span class="keyword">switch</span> () {</span><br><span class="line"> <span class="keyword">case</span> <span class="string">'1'</span>:</span><br><span class="line"> <span class="keyword">var</span> c = b + <span class="number">300</span>;</span><br><span class="line"> <span class="keyword">case</span> <span class="string">'2'</span>:</span><br><span class="line"> <span class="keyword">var</span> e = d + <span class="number">500</span>;</span><br><span class="line"> <span class="keyword">case</span> <span class="string">'3'</span>:</span><br><span class="line"> <span class="keyword">var</span> d = c + <span class="number">400</span>;</span><br><span class="line"> <span class="keyword">case</span> <span class="string">'4'</span>:</span><br><span class="line"> <span class="keyword">var</span> f = e + <span class="number">600</span>;</span><br><span class="line"> <span class="keyword">case</span> <span class="string">'5'</span>:</span><br><span class="line"> <span class="keyword">var</span> b = a + <span class="number">200</span>;</span><br><span class="line"> <span class="keyword">case</span> <span class="string">'6'</span>:</span><br><span class="line"> <span class="keyword">return</span> f;</span><br><span class="line"> <span class="keyword">case</span> <span class="string">'7'</span>:</span><br><span class="line"> <span class="keyword">var</span> a = <span class="number">100</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>可以看到,当代码块打乱之后,如果想跟原先的执行顺序一样,那么case块的跳转顺序应该是7,5,1,3,2,4,6。只有case块按照这个流程执行,才能跟原始代码块的顺序保持一致。其次,需要一个循环,因为switch语句只计算一次switch表达式。整个代码改写如下:</p><figure class="highlight js"><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="keyword">while</span> (!![]) {</span><br><span class="line"> <span class="keyword">switch</span> () {</span><br><span class="line"> <span class="keyword">case</span> <span class="string">'1'</span>:</span><br><span class="line"> <span class="keyword">var</span> c = b + <span class="number">300</span>;</span><br><span class="line"> <span class="keyword">continue</span>;</span><br><span class="line"> <span class="keyword">case</span> <span class="string">'2'</span>:</span><br><span class="line"> <span class="keyword">var</span> e = d + <span class="number">500</span>;</span><br><span class="line"> <span class="keyword">continue</span>;</span><br><span class="line"> <span class="keyword">case</span> <span class="string">'3'</span>:</span><br><span class="line"> <span class="keyword">var</span> d = c + <span class="number">400</span>;</span><br><span class="line"> <span class="keyword">continue</span>;</span><br><span class="line"> <span class="keyword">case</span> <span class="string">'4'</span>:</span><br><span class="line"> <span class="keyword">var</span> f = e + <span class="number">600</span>;</span><br><span class="line"> <span class="keyword">continue</span>;</span><br><span class="line"> <span class="keyword">case</span> <span class="string">'5'</span>:</span><br><span class="line"> <span class="keyword">var</span> b = a + <span class="number">200</span>;</span><br><span class="line"> <span class="keyword">continue</span>;</span><br><span class="line"> <span class="keyword">case</span> <span class="string">'6'</span>:</span><br><span class="line"> <span class="keyword">return</span> f;</span><br><span class="line"> <span class="keyword">case</span> <span class="string">'7'</span>:</span><br><span class="line"> <span class="keyword">var</span> a = <span class="number">100</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>这是一个死循环,假如函数有返回值,则执行到相应的case语句块后直接返回。假如函数没有返回值,则代码块执行到最后就需要让switch计算出来的表达式的值与每一个case的值都不匹配,那么就会执行最后的break来跳出循环。</p><p>接着我们需要构造一个分发器,里面记录代码块执行的真实顺序,例如var arrStr = ‘7|5|1|3|2|4|6’.split(‘|’),i=0。把这个字符串’7|5|1|3|2|4|6’通过split分割成一个数组。i作为计数器,每次递增,按顺序引用数组中的每一个成员。因此,switch中的表达式就可以写成switch(arrStr[i++]),完整代码如下:</p><figure class="highlight js"><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="function"><span class="keyword">function</span> <span class="title">test</span>(<span class="params"></span>) </span>{</span><br><span class="line"> <span class="keyword">var</span> arrStr = <span class="string">'7|5|1|3|2|4|6'</span>.split(<span class="string">'|'</span>),i=<span class="number">0</span>;</span><br><span class="line"> <span class="keyword">while</span> (!![]) {</span><br><span class="line"> <span class="keyword">switch</span> (arrStr[i++]) {</span><br><span class="line"> <span class="keyword">case</span> <span class="string">'1'</span>:</span><br><span class="line"> <span class="keyword">var</span> c = b + <span class="number">300</span>;</span><br><span class="line"> <span class="keyword">continue</span>;</span><br><span class="line"> <span class="keyword">case</span> <span class="string">'2'</span>:</span><br><span class="line"> <span class="keyword">var</span> e = d + <span class="number">500</span>;</span><br><span class="line"> <span class="keyword">continue</span>;</span><br><span class="line"> <span class="keyword">case</span> <span class="string">'3'</span>:</span><br><span class="line"> <span class="keyword">var</span> d = c + <span class="number">400</span>;</span><br><span class="line"> <span class="keyword">continue</span>;</span><br><span class="line"> <span class="keyword">case</span> <span class="string">'4'</span>:</span><br><span class="line"> <span class="keyword">var</span> f = e + <span class="number">600</span>;</span><br><span class="line"> <span class="keyword">continue</span>;</span><br><span class="line"> <span class="keyword">case</span> <span class="string">'5'</span>:</span><br><span class="line"> <span class="keyword">var</span> b = a + <span class="number">200</span>;</span><br><span class="line"> <span class="keyword">continue</span>;</span><br><span class="line"> <span class="keyword">case</span> <span class="string">'6'</span>:</span><br><span class="line"> <span class="keyword">return</span> f;</span><br><span class="line"> <span class="keyword">case</span> <span class="string">'7'</span>:</span><br><span class="line"> <span class="keyword">var</span> a = <span class="number">100</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> } </span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>如果函数没有返回值,即switch中没有return语句,最后一次递增i会导致数组越界,JS中数组越界不会报错,而是取出来为undefined,然后匹配不到任何的switch语句就会执行break跳出死循环。</p><p>在了解了流程平坦化之后,我们可以对之前的demo进一步混淆:</p><figure class="highlight js"><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> bigArr = [</span><br><span class="line"> <span class="string">'eXl5eS1NTS1kZA=='</span>, <span class="string">""</span>[<span class="string">'constructor'</span>][<span class="string">'fromCharCode'</span>], <span class="string">'\u65e5'</span>, <span class="string">'\u4e00'</span>, <span class="string">'\u4e8c'</span>, <span class="string">'\u4e09'</span>, <span class="string">'\u56db'</span>, <span class="string">'\u4e94'</span>, <span class="string">'\u516d'</span>, <span class="string">'cmVwbGFjZQ=='</span>, <span class="string">'Z2V0TW9udGg='</span>, <span class="string">'MA=='</span>, <span class="string">'Z2V0RGF0ZQ=='</span>, <span class="string">'RGF0ZQ=='</span>, <span class="string">'Zm9ybWF0'</span></span><br><span class="line">];</span><br><span class="line"></span><br><span class="line">(<span class="function"><span class="keyword">function</span>(<span class="params">arr, num</span>) </span>{</span><br><span class="line"> <span class="keyword">var</span> shuffer = <span class="function"><span class="keyword">function</span>(<span class="params">nums</span>) </span>{</span><br><span class="line"> <span class="keyword">while</span> (--nums) {</span><br><span class="line"> arr[<span class="string">'push'</span>](arr[<span class="string">'shift'</span>]());</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> shuffer(++num);</span><br><span class="line">}(bigArr, <span class="number">0x20</span>));</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">_0x20abefx1</span>(<span class="params">a, b, c</span>) </span>{</span><br><span class="line"> <span class="keyword">return</span> a(b, c);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">_0x20abefx2</span>(<span class="params">a, b, c</span>) </span>{</span><br><span class="line"> <span class="keyword">return</span> _0x20abefx1(a, b, c);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">_0x20abefx3</span>(<span class="params">a, b</span>) </span>{</span><br><span class="line"> <span class="keyword">return</span> a + b;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="built_in">window</span>[atob(<span class="string">'RGF0ZQ=='</span>)][atob(<span class="string">'cHJvdG90eXBl'</span>)][atob(<span class="string">'Zm9ybWF0'</span>)] = <span class="function"><span class="keyword">function</span>(<span class="params">formatStr</span>) </span>{</span><br><span class="line"> <span class="keyword">var</span> arrStr = <span class="string">'7|5|1|3|2|4'</span>.split(<span class="string">"|"</span>), i = <span class="number">0</span>;</span><br><span class="line"> <span class="keyword">while</span> (!![]) {</span><br><span class="line"> <span class="keyword">switch</span> (arrStr[i++]) {</span><br><span class="line"> <span class="keyword">case</span> <span class="string">'1'</span>:</span><br><span class="line"> <span class="built_in">eval</span>(bigArr[<span class="number">14</span>](<span class="number">115</span>, <span class="number">116</span>, <span class="number">114</span>, <span class="number">32</span>, <span class="number">61</span>, <span class="number">32</span>, <span class="number">115</span>, <span class="number">116</span>, <span class="number">114</span>, <span class="number">91</span>, <span class="number">39</span>, <span class="number">114</span>, <span class="number">101</span>, <span class="number">112</span>, <span class="number">108</span>, <span class="number">97</span>, <span class="number">99</span>, <span class="number">101</span>, <span class="number">39</span>, <span class="number">93</span>, <span class="number">40</span>, <span class="number">47</span>, <span class="number">121</span>, <span class="number">121</span>, <span class="number">121</span>, <span class="number">121</span>, <span class="number">124</span>, <span class="number">89</span>, <span class="number">89</span>, <span class="number">89</span>, <span class="number">89</span>, <span class="number">47</span>, <span class="number">44</span>, <span class="number">32</span>, <span class="number">116</span>, <span class="number">104</span>, <span class="number">105</span>, <span class="number">115</span>, <span class="number">91</span>, <span class="number">39</span>, <span class="number">103</span>, <span class="number">101</span>, <span class="number">116</span>, <span class="number">70</span>, <span class="number">117</span>, <span class="number">108</span>, <span class="number">108</span>, <span class="number">89</span>, <span class="number">101</span>, <span class="number">97</span>, <span class="number">114</span>, <span class="number">39</span>, <span class="number">93</span>, <span class="number">40</span>, <span class="number">41</span>, <span class="number">41</span>, <span class="number">59</span>));</span><br><span class="line"> <span class="keyword">continue</span>;</span><br><span class="line"> <span class="keyword">case</span> <span class="string">'2'</span>:</span><br><span class="line"> str = str[atob(bigArr[<span class="number">7</span>])](</span><br><span class="line"> <span class="regexp">/dd|DD/</span>,</span><br><span class="line"> <span class="built_in">this</span>[atob(bigArr[<span class="number">10</span>])] > <span class="number">9</span> ? <span class="built_in">this</span>[atob(bigArr[<span class="number">10</span>])]().toString() : _0x20abefx2(_0x20abefx3, atob(bigArr[<span class="number">9</span>]), <span class="built_in">this</span>[atob(bigArr[<span class="number">10</span>])]())</span><br><span class="line">);</span><br><span class="line"> <span class="keyword">continue</span>;</span><br><span class="line"> <span class="keyword">case</span> <span class="string">'3'</span>:</span><br><span class="line"> str = str[atob(bigArr[<span class="number">7</span>])](</span><br><span class="line"> <span class="regexp">/MM/</span>,</span><br><span class="line"> _0x20abefx2(_0x20abefx3, <span class="built_in">this</span>[atob(bigArr[<span class="number">8</span>])](), <span class="number">1</span>) > <span class="number">9</span> ? (_0x20abefx2(_0x20abefx3, <span class="built_in">this</span>[atob(bigArr[<span class="number">8</span>])](), <span class="number">1</span>)).toString() : _0x20abefx2(_0x20abefx3, atob(bigArr[<span class="number">9</span>]), (_0x20abefx2(_0x20abefx3, <span class="built_in">this</span>[atob(bigArr[<span class="number">8</span>])](), <span class="number">1</span>)))</span><br><span class="line">);</span><br><span class="line"> <span class="keyword">continue</span>;</span><br><span class="line"> <span class="keyword">case</span> <span class="string">'4'</span>:</span><br><span class="line"> <span class="keyword">return</span> str;</span><br><span class="line"> <span class="keyword">case</span> <span class="string">'5'</span>:</span><br><span class="line"> <span class="keyword">var</span> Week = [bigArr[<span class="number">0</span>], bigArr[<span class="number">1</span>], bigArr[<span class="number">2</span>], bigArr[<span class="number">3</span>], bigArr[<span class="number">4</span>], bigArr[<span class="number">5</span>], bigArr[<span class="number">6</span>]];</span><br><span class="line"> <span class="keyword">continue</span>;</span><br><span class="line"> <span class="keyword">case</span> <span class="string">'7'</span>:</span><br><span class="line"> <span class="keyword">var</span> \u0073\u0074\u0072 = \u0066\u006f\u0072\u006d\u0061\u0074\u0053\u0074\u0072; </span><br><span class="line"> <span class="keyword">continue</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> str;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="built_in">console</span>.log(<span class="keyword">new</span> <span class="built_in">window</span>[atob(bigArr[<span class="number">11</span>])]()[atob(bigArr[<span class="number">12</span>])](atob(bigArr[<span class="number">13</span>])));</span><br></pre></td></tr></table></figure><h5 id="逗号表达式混淆"><a href="#逗号表达式混淆" class="headerlink" title="逗号表达式混淆"></a>逗号表达式混淆</h5><p>逗号运算符的主要作用是把多个表达式或语句连接成一个复合语句。比如前面提到的test方法,可以改写为:</p><figure class="highlight js"><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="function"><span class="keyword">function</span> <span class="title">test</span>(<span class="params"></span>) </span>{</span><br><span class="line"> <span class="keyword">var</span> a, b, c, d, e, f;</span><br><span class="line"> <span class="keyword">return</span> a = <span class="number">100</span>, b = a + <span class="number">200</span>, c = b + <span class="number">300</span>, d = c + <span class="number">400</span>, e = d + <span class="number">500</span>, f = e + <span class="number">600</span>, f;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>return语句通常只跟一个语句,但是逗号表达式可以把多个语句符合成一个语句,这样会依次执行前面的语句,然后把最后一条语句作为返回值。</p><p>再看一个例子:</p><figure class="highlight js"><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="keyword">var</span> a = (a = <span class="number">100</span>, a += <span class="number">200</span>)</span><br><span class="line"><span class="built_in">console</span>.log(a); <span class="comment">// 300</span></span><br></pre></td></tr></table></figure><p>括号会作为一个整体,先把括号里面的运算完,然后把这个整体的值赋值给a。明白了这个道理,我们再改写下test方法:</p><figure class="highlight js"><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="function"><span class="keyword">function</span> <span class="title">test</span>(<span class="params"></span>) </span>{</span><br><span class="line"> <span class="keyword">var</span> a, b, c, d, e, f;</span><br><span class="line"> <span class="keyword">return</span> f = (e = (d = (c = (b = (a = <span class="number">100</span>, a + <span class="number">200</span>), b + <span class="number">300</span>), c + <span class="number">400</span>), d + <span class="number">500</span>), e + <span class="number">600</span>)</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>可以进一步优化,这里声明了一系列的变量,可以把这些变量作为参数传入,同时,在每一个变量的赋值的逗号表达式中可以插入花指令:</p><figure class="highlight js"><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="function"><span class="keyword">function</span> <span class="title">test</span>(<span class="params">a, b, c, d, e, f</span>) </span>{</span><br><span class="line"> <span class="keyword">return</span> f = (e = (d = (c = (b = (a = <span class="number">100</span>, b + <span class="number">1000</span>, c + <span class="number">2000</span>, d + <span class="number">3000</span>, e + <span class="number">4000</span>, f + <span class="number">5000</span>, a + <span class="number">200</span>), c + <span class="number">2000</span>, d + <span class="number">3000</span>, e + <span class="number">4000</span>, f + <span class="number">5000</span>, b + <span class="number">300</span>), d + <span class="number">3000</span>, e + <span class="number">4000</span>, f + <span class="number">5000</span>, c + <span class="number">400</span>), e + <span class="number">4000</span>, f + <span class="number">5000</span>, d + <span class="number">500</span>), f + <span class="number">5000</span>, e + <span class="number">600</span>)</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>虽然需要6个参数,但是实际上不传任何参数,依然是正确的代码逻辑。同时中间的花指令,b+1000,c+2000,d+3000,e+4000,f+5000是没有意义的。</p><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="https://img.heshipeng.com/202203081500383.png?watermark/2/text/5YWz5rOo5b6u5L-h5YWs5LyX5Y-377ya6YCG5ZCR5LiA5q2l5q2l/font/5a6L5L2T/fontsize/300" alt="image-20220308150018181" title=""> </div> <div class="image-caption">image-20220308150018181</div> </figure><p>我们对前面给的demo进行改写:</p><figure class="highlight js"><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"><span class="keyword">var</span> bigArr = [</span><br><span class="line"> <span class="string">'eXl5eS1NTS1kZA=='</span>, <span class="string">""</span>[<span class="string">'constructor'</span>][<span class="string">'fromCharCode'</span>], <span class="string">'\u65e5'</span>, <span class="string">'\u4e00'</span>, <span class="string">'\u4e8c'</span>, <span class="string">'\u4e09'</span>, <span class="string">'\u56db'</span>, <span class="string">'\u4e94'</span>, <span class="string">'\u516d'</span>, <span class="string">'cmVwbGFjZQ=='</span>, <span class="string">'Z2V0TW9udGg='</span>, <span class="string">'MA=='</span>, <span class="string">'Z2V0RGF0ZQ=='</span>, <span class="string">'RGF0ZQ=='</span>, <span class="string">'Zm9ybWF0'</span></span><br><span class="line">];</span><br><span class="line"></span><br><span class="line">(<span class="function"><span class="keyword">function</span>(<span class="params">arr, num</span>) </span>{</span><br><span class="line"> <span class="keyword">var</span> shuffer = <span class="function"><span class="keyword">function</span>(<span class="params">nums</span>) </span>{</span><br><span class="line"> <span class="keyword">while</span> (--nums) {</span><br><span class="line"> arr[<span class="string">'push'</span>](arr[<span class="string">'shift'</span>]());</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> shuffer(++num);</span><br><span class="line">}(bigArr, <span class="number">0x20</span>));</span><br><span class="line"></span><br><span class="line"><span class="built_in">window</span>[atob(<span class="string">'RGF0ZQ=='</span>)][atob(<span class="string">'cHJvdG90eXBl'</span>)][atob(<span class="string">'Zm9ybWF0'</span>)] = <span class="function"><span class="keyword">function</span>(<span class="params">formatStr, str, Week</span>) </span>{</span><br><span class="line"> <span class="keyword">return</span> str = (</span><br><span class="line"> (str = (</span><br><span class="line"> Week = (</span><br><span class="line"> \u0073\u0074\u0072 = \u0066\u006f\u0072\u006d\u0061\u0074\u0053\u0074\u0072,</span><br><span class="line"> [bigArr[<span class="number">0</span>], bigArr[<span class="number">1</span>], bigArr[<span class="number">2</span>], bigArr[<span class="number">3</span>], bigArr[<span class="number">4</span>], bigArr[<span class="number">5</span>], bigArr[<span class="number">6</span>]]</span><br><span class="line"> ),</span><br><span class="line"> <span class="built_in">eval</span>(bigArr[<span class="number">14</span>](<span class="number">115</span>, <span class="number">116</span>, <span class="number">114</span>, <span class="number">32</span>, <span class="number">61</span>, <span class="number">32</span>, <span class="number">115</span>, <span class="number">116</span>, <span class="number">114</span>, <span class="number">91</span>, <span class="number">39</span>, <span class="number">114</span>, <span class="number">101</span>, <span class="number">112</span>, <span class="number">108</span>, <span class="number">97</span>, <span class="number">99</span>, <span class="number">101</span>, <span class="number">39</span>, <span class="number">93</span>, <span class="number">40</span>, <span class="number">47</span>, <span class="number">121</span>, <span class="number">121</span>, <span class="number">121</span>, <span class="number">121</span>, <span class="number">124</span>, <span class="number">89</span>, <span class="number">89</span>, <span class="number">89</span>, <span class="number">89</span>, <span class="number">47</span>, <span class="number">44</span>, <span class="number">32</span>, <span class="number">116</span>, <span class="number">104</span>, <span class="number">105</span>, <span class="number">115</span>, <span class="number">91</span>, <span class="number">39</span>, <span class="number">103</span>, <span class="number">101</span>, <span class="number">116</span>, <span class="number">70</span>, <span class="number">117</span>, <span class="number">108</span>, <span class="number">108</span>, <span class="number">89</span>, <span class="number">101</span>, <span class="number">97</span>, <span class="number">114</span>, <span class="number">39</span>, <span class="number">93</span>, <span class="number">40</span>, <span class="number">41</span>, <span class="number">41</span>, <span class="number">59</span>)),</span><br><span class="line"> str[atob(bigArr[<span class="number">7</span>])](<span class="regexp">/MM/</span>, (<span class="built_in">this</span>[atob(bigArr[<span class="number">8</span>])]() + <span class="number">1</span>) > <span class="number">9</span> ? (<span class="built_in">this</span>[atob(bigArr[<span class="number">8</span>])]() + <span class="number">1</span>).toString() : atob(bigArr[<span class="number">9</span>]) + (<span class="built_in">this</span>[atob(bigArr[<span class="number">8</span>])]() + <span class="number">1</span>))</span><br><span class="line"> ),</span><br><span class="line"> str[atob(bigArr[<span class="number">7</span>])](<span class="regexp">/dd|DD/</span>, <span class="built_in">this</span>[atob(bigArr[<span class="number">10</span>])]() > <span class="number">9</span> ? <span class="built_in">this</span>[atob(bigArr[<span class="number">10</span>])]().toString() : atob(bigArr[<span class="number">9</span>]) + <span class="built_in">this</span>[atob(bigArr[<span class="number">10</span>])]())</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"><span class="built_in">console</span>.log(<span class="keyword">new</span> <span class="built_in">window</span>[atob(bigArr[<span class="number">11</span>])]()[atob(bigArr[<span class="number">12</span>])](atob(bigArr[<span class="number">13</span>])));</span><br></pre></td></tr></table></figure><h4 id="其它代码防护方案"><a href="#其它代码防护方案" class="headerlink" title="其它代码防护方案"></a>其它代码防护方案</h4><h5 id="eval加密"><a href="#eval加密" class="headerlink" title="eval加密"></a>eval加密</h5><p>看一段代码:</p><figure class="highlight js"><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="built_in">eval</span>(<span class="function"><span class="keyword">function</span> (<span class="params">p, a, c, k, e, r</span>) </span>{</span><br><span class="line"> e = <span class="function"><span class="keyword">function</span> (<span class="params">c</span>) </span>{</span><br><span class="line"> <span class="keyword">return</span> c.toString(<span class="number">36</span>)</span><br><span class="line"> };</span><br><span class="line"> <span class="keyword">if</span> (<span class="string">'0'</span>.replace(<span class="number">0</span>, e) == <span class="number">0</span>) {</span><br><span class="line"> <span class="keyword">while</span> (c--)</span><br><span class="line"> r[e(c)] = k[c];</span><br><span class="line"> k = [<span class="function"><span class="keyword">function</span> (<span class="params">e</span>) </span>{</span><br><span class="line"> <span class="keyword">return</span> r[e] || e</span><br><span class="line"> }</span><br><span class="line"> ];</span><br><span class="line"> e = <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>{</span><br><span class="line"> <span class="keyword">return</span> <span class="string">'[2-8a-f]'</span></span><br><span class="line"> };</span><br><span class="line"> c = <span class="number">1</span></span><br><span class="line"> };</span><br><span class="line"> <span class="keyword">while</span> (c--)</span><br><span class="line"> <span class="keyword">if</span> (k[c])</span><br><span class="line"> p = p.replace(<span class="keyword">new</span> <span class="built_in">RegExp</span>(<span class="string">'\\b'</span> + e(c) + <span class="string">'\\b'</span>, <span class="string">'g'</span>), k[c]);</span><br><span class="line"> <span class="keyword">return</span> p</span><br><span class="line">}(<span class="string">'7.prototype.8=function(a){b 2=a;b Week=[\'日\',\'一\',\'二\',\'三\',\'四\',\'五\',\'六\'];2=2.4(/c|YYYY/,3.getFullYear());2=2.4(/d/,(3.5()+1)>9?(3.5()+1).e():\'0\'+(3.5()+1));2=2.4(/f|DD/,3.6()>9?3.6().e():\'0\'+3.6());return 2};console.log(new 7().8(\'c-d-f\'));'</span>, [], <span class="number">16</span>, <span class="string">'||str|this|replace|getMonth|getDate|Date|format||formatStr|var|yyyy|MM|toString|dd'</span>.split(<span class="string">'|'</span>), <span class="number">0</span>, {}))</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>传给eval的是一个匿名函数,而不是一个字符串,这就是说先通过匿名函数将加密的代码解密成字符串代码,然后再通过eval之心这串代码。所以eval加密跟eval关系不大,重要的是这个解密函数,eval只是执行解密后的结果,并不参与加解密。</p><p>通过解密函数解密出来的字符串代码为:</p><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="https://img.heshipeng.com/202203081534835.png?watermark/2/text/5YWz5rOo5b6u5L-h5YWs5LyX5Y-377ya6YCG5ZCR5LiA5q2l5q2l/font/5a6L5L2T/fontsize/300" alt="image-20220308153442661" title=""> </div> <div class="image-caption">image-20220308153442661</div> </figure><p>通过eval执行解密出来的字符串结果为:</p><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="https://img.heshipeng.com/202203081535577.png?watermark/2/text/5YWz5rOo5b6u5L-h5YWs5LyX5Y-377ya6YCG5ZCR5LiA5q2l5q2l/font/5a6L5L2T/fontsize/300" alt="image-20220308153523029" title=""> </div> <div class="image-caption">image-20220308153523029</div> </figure><h5 id="内存爆破"><a href="#内存爆破" class="headerlink" title="内存爆破"></a>内存爆破</h5><p>内存爆破是指在代码中加入死代码,正常情况下这段代码不执行。当检测到函数被格式化或者函数被Hook的时候,就跳转到这段代码执行,直到内存溢出,浏览器会提示Out of Memory程序奔溃。内存爆破的代码如下:</p><figure class="highlight js"><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">var</span> d = [<span class="number">0x1</span>, <span class="number">0x0</span>, <span class="number">0x0</span>];</span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">b</span>(<span class="params"></span>) </span>{</span><br><span class="line"> <span class="keyword">for</span>(<span class="keyword">var</span> i = <span class="number">0x0</span>, c = d.length; i < c; i++) {</span><br><span class="line"> d.push(<span class="built_in">Math</span>.round(<span class="built_in">Math</span>.round()));</span><br><span class="line"> c = d.length;</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>上述代码中的for循环是一个死循环,但是代码写的又不像 while(true) 这样明显。尤其是代码混淆以后,会更具迷惑性。这段代码其实是从某网站简化而来,原先的代码如下:</p><figure class="highlight js"><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="built_in">this</span>[<span class="string">'NsTJKl'</span>] = [<span class="number">0x1</span>, <span class="number">0x0</span>, <span class="number">0x0</span>]</span><br><span class="line">......</span><br><span class="line"><span class="number">0x4b1809</span>[<span class="string">'prototype'</span>][<span class="string">'xTDWoN'</span>] = <span class="function"><span class="keyword">function</span>(<span class="params">_0x597ca7</span>) </span>{</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">var</span> _0x3e27c4 = <span class="number">0x0</span>, _0x192434 = <span class="built_in">this</span>[<span class="string">'NsTJKl'</span>][<span class="string">'length'</span>]; _0x3e27c4 < _0x192434; _0x3e27c4++) {</span><br><span class="line"> <span class="built_in">this</span>[<span class="string">'NsTJKl'</span>][<span class="string">'push'</span>](<span class="built_in">Math</span>[<span class="string">'round'</span>](<span class="built_in">Math</span>[<span class="string">'random'</span>]()))</span><br><span class="line"> _0x192434 = <span class="built_in">this</span>[<span class="string">'NsTJKl'</span>][<span class="string">'length'</span>]</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> _0x597ca7(<span class="built_in">this</span>[<span class="string">'NsTJKl'</span>][<span class="number">0x0</span>])</span><br><span class="line">}</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>for循环的结束条件是 _03e27c4 < _0x92434,其中 _0x192434 的初始化值是数组的大小。看着像是一个遍历数组的操作,是在循环中,又往数组中push了成员,接着又重新给 _0x192434 赋值为数组的大小。这时这段代码就永远也不会结束了,直到内存溢出。</p><h5 id="检测代码是否被格式化"><a href="#检测代码是否被格式化" class="headerlink" title="检测代码是否被格式化"></a>检测代码是否被格式化</h5><p>检测的思路很简单,在JS中,函数是可以转为字符串的。因此可以选择一个函数转为字符串,然后跟内置的字符串对比或者用正则匹配。函数转为字符串很简单:</p><figure class="highlight js"><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="function"><span class="keyword">function</span> <span class="title">add</span> (<span class="params">a, b</span>) </span>{</span><br><span class="line"> <span class="keyword">return</span> a + b</span><br><span class="line">}</span><br><span class="line"><span class="built_in">console</span>.log(add + <span class="string">''</span>)</span><br><span class="line"><span class="built_in">console</span>.log(add.toString())</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>在调试窗口使用格式化之后,会产生一个后缀为:formatted的文件。之后这个文件中设置断点,触发断点后,会停在这个文件中,选中这个函数,鼠标悬停在上面,会显示出他原来没有格式化之前的样子。</p>]]></content>
<summary type="html"><h4 id="常量的混淆原理"><a href="#常量的混淆原理" class="headerlink" title="常量的混淆原理"></a>常量的混淆原理</h4><p>本篇所用的demo如下。接下来的案例都是围绕这个demo进行混淆。</p>
<figure clas</summary>
<category term="JS逆向" scheme="http://example.com/categories/JS%E9%80%86%E5%90%91/"/>
<category term="JS" scheme="http://example.com/tags/JS/"/>
<category term="逆向" scheme="http://example.com/tags/%E9%80%86%E5%90%91/"/>
</entry>
</feed>