pdd字体加密反爬
前言
拼多多商家后台交易数据 之前有一个场景需要获取拼多多商家后台的交易数据, 是自己的一些账号, 方便做一些监控, 然后获取的时候发现显示是乱码, 接口里面返回也是乱码, 当时也找过一些教程, 只不过一直没空折腾, 感觉很花时间, 因为可以通过其他方案(获取所有订单明细计算值)曲线救国, 所以就一直搁置着.
解决
年底了稍微有点时间, 就想着把这个事情折腾一下, 其实网上也有一些教程, 大概也能猜到大概原理, 不过毕竟不是自己动手实现, 所以还是想着记录下完整的流程. 其实核心的原理就是 iconfont, 具体的可以自行搜索, 比如iconfont原理.简单来说unicode被自定义字体渲染成svg.
知道原理之后, 首先要获取到自定义字体, 这里办法其实挺多的.
搜索spider-font 这个其实最简单, 根据自定义字体关键字去寻找, 然后打断点获取字体链接.
搜索web_spider_rule 这个其实是根据查看参数, 后面才发现的, 不一定非要用这个方法, 不过这里的确能抓到请求.
过滤字体请求 chrome支持按照类型过滤请求, 我们可以直接过滤出字体请求, 这里就2个字体, 简单判断下可以知道第二个字体是我们需要的.
下载之后, 可以先通过 fontkit 解析下字体
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
const fontkit = require('fontkit');
const fs = require('fs');
const buffer = fs.readFileSync('font.ttf');
const font = fontkit.create(buffer);
const fontInfo = {
name: font.postscriptName,
numGlyphs: font.numGlyphs,
glyphs: []
};
for (let i = 0; i < font.numGlyphs; i++) {
const glyph = font.getGlyph(i);
fontInfo.glyphs.push({
id: glyph.id,
name: glyph.name,
path: glyph.path.toSVG()
});
}
console.log(fontInfo);
输出是这样的
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
{
name: 'HelveticaNeueCE-Roman',
numGlyphs: 12,
glyphs: [
{ id: 0, name: '.notdef', path: '' },
{ id: 1, name: 'period', path: 'M83 0L194 0L194 111L83 111Z' },
{
id: 2,
name: 'uniEBEE',
path: 'M129 458Q128 490 135.5 521.5Q143 553 160 578Q177 603 203.5 618.5Q230 634 267 634Q295 634 320 625Q345 616 363.5 599Q382 582 393 558.5Q404 535 404 506Q404 469 392.5 441Q381 413 358.5 389Q336 365 302 341.5Q268 318 223 290Q185 267 151.5 242.5Q118 218 91.5 185Q65 152 47 107.5Q29 63 24 0L487 0L487 75L123 75Q129 108 148.5 133.5Q168 159 195.5 181Q223 203 256 222.5Q289 242 322 262Q355 283 386 306Q417 329 441 357.5Q465 386 479.5 422Q494 458 494 505Q494 554 476.5 592.5Q459 631 429 656.5Q399 682 358.5 695.5Q318 709 272 709Q216 709 172 690Q128 671 99 637.5Q70 604 55.5 558Q41 512 44 458Z'
},
{
id: 3,
name: 'uniED0F',
path: 'M409 472Q409 440 399 412Q389 384 370 362.5Q351 341 324 328.5Q297 316 264 316Q233 316 207.5 328.5Q182 341 163.5 362Q145 383 134.5 410Q124 437 124 466Q124 499 131.5 529Q139 559 155.5 582.5Q172 606 198.5 620Q225 634 263 634Q299 634 326 621Q353 608 371.5 585.5Q390 563 399.5 534Q409 505 409 472ZM49 171Q53 125 70.5 91Q88 57 116 34Q144 11 181.5 -0.5Q219 -12 264 -12Q393 -12 451.5 87Q510 186 510 370Q510 470 490.5 536Q471 602 438 640Q405 678 360.5 693.5Q316 709 267 709Q216 709 173 692Q130 675 99 644.5Q68 614 51 571.5Q34 529 34 478Q34 425 48.5 381.5Q63 338 91.5 307Q120 276 162 258.5Q204 241 258 241Q310 241 354 267.5Q398 294 422 339L424 337Q420 199 381 131Q342 63 264 63Q213 63 176 91Q139 119 134 171Z'
},
{
id: 4,
name: 'uniE6B8',
path: 'M509 697L50 697L50 617L422 617Q366 558 317.5 488Q269 418 232 339.5Q195 261 171.5 175.5Q148 90 142 0L237 0Q242 83 265 171Q288 259 324.5 342Q361 425 408 498Q455 571 509 624Z'
},
{
id: 5,
name: 'uniE47A',
path: 'M286 381Q322 381 349 368Q376 355 394 333.5Q412 312 420.5 282.5Q429 253 429 220Q429 188 419.5 160Q410 132 392 110Q374 88 347 75.5Q320 63 286 63Q251 63 223.5 75.5Q196 88 177.5 109Q159 130 149 159Q139 188 139 221Q139 254 148.5 283.5Q158 313 177 334.5Q196 356 223 368.5Q250 381 286 381ZM499 528Q491 615 439 662Q387 709 299 709Q223 709 173 677Q123 645 93 592Q63 539 50.5 470.5Q38 402 38 329Q38 273 46.5 213Q55 153 81 103Q107 53 155 20.5Q203 -12 282 -12Q349 -12 394.5 10.5Q440 33 467.5 68Q495 103 507 145Q519 187 519 226Q519 275 504 317Q489 359 461.5 390Q434 421 393.5 438.5Q353 456 302 456Q244 456 199.5 434Q155 412 125 363L123 365Q124 406 131 453Q138 500 156 540.5Q174 581 207.5 607.5Q241 634 295 634Q346 634 377 605Q408 576 414 528Z'
},
{
id: 6,
name: 'uniEB20',
path: 'M419 709L351 709L28 248L28 166L339 166L339 0L419 0L419 166L515 166L515 241L419 241ZM101 241L337 588L339 588L339 241Z'
},
{
id: 7,
name: 'uniE443',
path: 'M42 348Q42 309 44 267Q46 225 54 185Q62 145 77 109.5Q92 74 118 47Q144 20 183.5 4Q223 -12 278 -12Q333 -12 372.5 4Q412 20 438 47Q464 74 479 109.5Q494 145 502 185Q510 225 512 267Q514 309 514 348Q514 407 508.5 470.5Q503 534 479.5 587Q456 640 408.5 674.5Q361 709 278 709Q195 709 147.5 674.5Q100 640 76.5 587Q53 534 47.5 470.5Q42 407 42 348ZM132 349Q132 375 132.5 406.5Q133 438 137.5 469.5Q142 501 150.5 531Q159 561 175.5 583.5Q192 606 217 620Q242 634 278 634Q314 634 339 620Q364 606 380.5 583.5Q397 561 405.5 531Q414 501 418.5 469.5Q423 438 423.5 406.5Q424 375 424 349Q424 309 421.5 259.5Q419 210 405.5 166.5Q392 123 362 93Q332 63 278 63Q224 63 194 93Q164 123 150.5 166.5Q137 210 134.5 259.5Q132 309 132 349Z'
},
{
id: 8,
name: 'uniE915',
path: 'M470 697L120 697L54 329L127 325Q152 355 185 373.5Q218 392 259 392Q295 392 324.5 380Q354 368 375 346.5Q396 325 407.5 295.5Q419 266 419 231Q419 189 407 157.5Q395 126 374.5 105Q354 84 326.5 73.5Q299 63 269 63Q237 63 210.5 72.5Q184 82 164.5 99.5Q145 117 133.5 140.5Q122 164 120 191L35 191Q36 143 54 105.5Q72 68 103 41.5Q134 15 174.5 1.5Q215 -12 261 -12Q323 -12 369.5 8Q416 28 447 61Q478 94 493.5 136.5Q509 179 509 224Q509 285 491 330.5Q473 376 442 406.5Q411 437 369 452Q327 467 280 467Q244 467 207.5 454.5Q171 442 148 416L146 418L184 622L470 622Z'
},
{
id: 9,
name: 'uniE3ED',
path: 'M218 333Q242 336 269 336Q300 336 328 327.5Q356 319 376 301.5Q396 284 408 258.5Q420 233 420 200Q420 168 407.5 142.5Q395 117 374 99.5Q353 82 325 72.5Q297 63 266 63Q193 63 155 106.5Q117 150 115 219L30 219Q29 164 45.5 121Q62 78 93 48.5Q124 19 168 3.5Q212 -12 266 -12Q316 -12 360.5 2Q405 16 438 43Q471 70 490.5 110.5Q510 151 510 204Q510 268 478.5 315Q447 362 382 376L382 378Q423 397 451.5 434.5Q480 472 480 519Q480 568 463.5 604Q447 640 418 663Q389 686 349.5 697.5Q310 709 264 709Q211 709 170.5 692Q130 675 103 645Q76 615 61.5 573Q47 531 45 480L130 480Q130 511 138 539Q146 567 162.5 588Q179 609 204 621.5Q229 634 264 634Q318 634 354 605Q390 576 390 520Q390 491 379 469.5Q368 448 349.5 433.5Q331 419 306.5 411.5Q282 404 255 404Q246 404 237 404Q228 404 218 405Z'
},
{
id: 10,
name: 'uniEB5E',
path: 'M356 709L291 709Q284 669 265 643Q246 617 218.5 602Q191 587 157 581.5Q123 576 87 576L87 508L271 508L271 0L356 0Z'
},
{
id: 11,
name: 'uniEEA6',
path: 'M130 200Q130 231 141.5 256Q153 281 173.5 299Q194 317 221 326.5Q248 336 280 336Q310 336 336.5 325.5Q363 315 383 297Q403 279 414.5 254.5Q426 230 426 201Q426 171 415.5 146Q405 121 385.5 102.5Q366 84 339.5 73.5Q313 63 281 63Q215 63 172.5 99.5Q130 136 130 200ZM65 528Q65 480 92 439.5Q119 399 164 381Q104 360 72 313.5Q40 267 40 204Q40 150 58.5 109.5Q77 69 109.5 42Q142 15 186 1.5Q230 -12 281 -12Q330 -12 373 2.5Q416 17 447.5 44.5Q479 72 497.5 112.5Q516 153 516 204Q516 270 485 315.5Q454 361 390 381Q434 401 461 440.5Q488 480 488 528Q488 562 476 594.5Q464 627 438.5 652.5Q413 678 372 693.5Q331 709 273 709Q232 709 194.5 697Q157 685 128 662Q99 639 82 605.5Q65 572 65 528ZM155 525Q155 552 165 572Q175 592 191.5 606Q208 620 230.5 627Q253 634 279 634Q331 634 364.5 606.5Q398 579 398 525Q398 471 365 441Q332 411 281 411Q255 411 232 418Q209 425 192 439Q175 453 165 474.5Q155 496 155 525Z'
}
]
}
可以发现每次字体都是不一样的, unicode不一样, 好消息是svg path是一样的, 这样的话, 其实就会简单很多, 我们可以通过在线字体查看工具查看
字体详情 这样的话, 我们可以根据unicode(name: ‘uniEEA6’)匹配, 对应在线字体查看里面的数字, 可以得到每个数字的path 比如uniEEA6的path是”M130 200Q130 231 141.5 256Q153 281…”, 在线字体查看页面上可以看到是8, 所以8对应的path就是”M130 200Q130 231 141.5 256Q153 281…”, 同理整理出所有的对应关系. 接下来我们可以抓取接口的返回数据, 如果直接打印, 可能会是乱码, 或者一个”□”, 可以通过转为unicode查看
1
2
3
4
5
6
7
8
9
toUnicodeEscape(str) {
return str
.split('')
.map((char) => {
const code = char.charCodeAt(0);
return code > 127 ? `\\u${code.toString(16).padStart(4, '0')}` : char;
})
.join('');
}
后面的工作其实就是拆分结果里面返回的每个unicode, 找到对应的path, 根据path转成数字, 最后就得到到具体的数字了.
彩蛋
1
2
3
4
5
6
curl 'https://mms.pinduoduo.com/sydney/api/mallScore/queryMallScoreOverView' \
-H 'anti-content: anti-content' \
-H 'cookie: cookie' \
-H 'user-agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36' \
-H 'webspiderrule: bdd51a52268d46bfa661c3b3ade4b6cev10ctfOlJdb4tkBntso5bLM/iaIuNtYsZhUVZm1Nx+mEyJWGnQVHOBb/I4hu87smV+k' \
--data-raw '{}'
其实我后来通过模拟请求发现,这个请求的header里面有一个webspiderrule, 这个参数是非必传的, 如果不传的话, 就不会自定义字体, 直接就返回了原始数字, 就很离谱, 白折腾这么久!