网易云音乐MP3地址解密



前言

最近在改博客的音乐插件时,发现其是向
https://api.i-meto.com/meting/api
查询网易的mp3地址,考虑到它不刷 listid 缓存,以后可能会有自己实现的需求,遂对网易云音乐分析了一番。

云音乐MP3地址分析

分析请求

打开 fiddler 抓一组包,很容易找到包含音乐地址的包。

image-20221010180900107

得到获取地址的接口为
https://music.163.com/weapi/song/enhance/player/url/v1?csrf_token=

简单分析请求之后,得到一组最简洁的请求格式

image-20221010180914111

这里使用的 vscodeREST Client 插件。

观察POST数据包的 body 部分,疑似加密,前半部分是典型的 urlencode + Base64 (%30%30)解开是二进制,后半部分直接是二进制。

encSecKey将成为一个突破口。

分析加解密

打开浏览器的 开发人员工具 (F12), 全局搜索(ctrl + shift + f) encSecKey,找到全部与之相关的js,

format 一下,全部下断点,实际上正常应该分析一下上下文,但是总共就 3,4 处,直接全断最方便了。

刷新网页,会断到 core_xxxxxxxxxxxxx.js 中名为 function d(d, e, f, g) 的函数,虽然名称被混淆了,
但是这里确实是加密。
当然为了验证是否是前面那个接口用到的加密,还需要往后跟到发包函数,多看几组包就能确定了。
这个过程我已经做了,由于篇幅问题,不再多说。

拓展一下,不从encSecKey分析也是可以的,搜 csrf_token 下断点,往上回溯几层,
你会跟到 window.asrsea = d,实际上拿 window.asrsea 去百度搜会发现有人写过类似的文章了。

函数很短就整个提过来了

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
!function() {
function a(a) {
var d, e, b = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789", c = "";
for (d = 0; a > d; d += 1)
e = Math.random() * b.length,
e = Math.floor(e),
c += b.charAt(e);
return c
}
function b(a, b) {
var c = CryptoJS.enc.Utf8.parse(b)
, d = CryptoJS.enc.Utf8.parse("0102030405060708")
, e = CryptoJS.enc.Utf8.parse(a)
, f = CryptoJS.AES.encrypt(e, c, {
iv: d,
mode: CryptoJS.mode.CBC
});
return f.toString()
}
function c(a, b, c) {
var d, e;
return setMaxDigits(131),
d = new RSAKeyPair(b,"",c),
e = encryptedString(d, a)
}
function d(d, e, f, g) {
var h = {}
, i = a(16);
return h.encText = b(d, g),
h.encText = b(h.encText, i),
h.encSecKey = c(i, e, f),
h
}
function e(a, b, d, e) {
var f = {};
return f.encText = c(a + e, b, d),
f
}
window.asrsea = d,
window.ecnonasr = e
}();

逐个函数看

  • function a(a),看到 a-zA-Z0-9 加个循环里面用 rand,非常典型的随机串生成算法,根据循环条件得知参数为串长度。
  • function b(a, b),它都说了是 AES 了,加密模式是 CBC,怕它不老实,不使用标准实现,随手找个在线加密网站,可以进行验证,测试一遍后能得到填充模式为 PKCS_PADDING。 参数分别是源跟密钥。
  • function c(a, b, c)RSA这个老实说不好在线验证,先放着,后面用代码验证。
  • function d(d, e, f, g) ,主加密流程,多观察几遍就会发现除了 d ,都是固定值,拷贝下来即可,而d中是个 json明文。
  • function e(a, b, d, e) ,看上去似乎是类似流程,不过跟本文无关。

有意思的是 window.asrsea 的后半部分倒过来是 aesrsa,小彩蛋?
rsanonce 对应 RSA Nonce 加密?

从js到python

最近在学习 rust,本来是准备练下手的,只可惜学艺不精,处处跟编译器对着干,就是那种明明知道该怎么写,但是用你就偏偏搞不定的感觉。

写这种小工具,还是python 最快了。
装个基础环境,vscode里面简单配置一下,几分钟就搞定了,顺便装一下 setuptoolspip,再也不用担心找不到依赖库了。
python 本身自带一个 crypt 模块,看起来有点简陋,需要重新找一个密码学库。
pip search crypt 能搜到一些库,但是担心版本太旧不兼容问题,网上搜了一下,说是现在用的比较多的是 pycryptodome

pip install pycryptodome 安装。

创建文件,写一个带测试的类

1
2
3
4
5
6
7
class MusicDecrypt(unittest.TestCase):
def __init__(self, methodName='runTest'):
super(MusicDecrypt, self).__init__(methodName)

if __name__ == "__main__":
unittest.main()

编写 AES 及测试方法

源算法中用 AES 加密了两次,第一次是固定 key 0CoJUm6Qyw8W8jud,第二次是长度为16的随机串(关于这个后面细说)。

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
##
# 加到最上面
##
from Crypto.Cipher import AES
import base64

##
# 加到 MusicDecrypt 里
##
#
# AES
# CBC
# pkcs5padding
# 128bit
#
def aes_encrypt(self, s, k, iv="0102030405060708"):
l = 16 - len(s) % 16
# pkcs5padding
s += + l * chr(l)
self.assertEqual(len(s) % 16, 0)

return base64.b64encode(AES.new(k.encode("utf-8"), AES.MODE_CBC, iv.encode("utf-8")).encrypt(s.encode("utf-8"))).decode()

def test_aes_encrypt(self):

enc = self.aes_encrypt(
s='{"ids":"[28949499]","level":"standard","encodeType":"aac","csrf_token":""}',
k="0CoJUm6Qyw8W8jud",
)
self.assertEqual(
enc,
"g1N6YybxFdV98P/fGY0407hwjh0evx5kPtxXR0nPd/WPPFsi9Lf67vFfjUnM3MDahHpqkyZMS+9goaszbHF+i1fIufNBu+8BbSvBCJSVfEU="
)
self.assertEqual(
self.aes_encrypt(
s=enc,
k="GR1dIlooUjX3zmY1",
),
"9R0jh8yE6/JTTwoH4ujCacPMOwJdbXk39BlG3ODTNe+rHMLAOSHDlp/Mza7+15lOi8bvPMtLnA6gCOujDj5iuVBJF2a2DJVkNLtrTtgl+AXpsR5hSh0+EOfuads7lq41B9EpYKktwB72zOy+kafalQ=="
)

这个是之前调试js时存下来的数据,通过对这组数据测试,就能写出正确的算法, RSA 同理。
只需要将js中的b忠实还原就行了。

编写 RSA 及测试方法

显然js中用的 RSA 并非是平常的用法,最少传进去不是个标准的公钥。

网上搜了一下 RSA 的算法解释。

得知 RSA 的加密算法核心是
c = (p ^ e) mod n
方程中p表示源串,c表示加密串,n就是文件上面说的Modules,e则为Exponent,(n, e)表示PublicKey。

通过调试js能得到3个数据,

  1. GR1dIlooUjX3zmY1
  2. 010001
  3. 一段很长的数据

根据 function c(a, b, c) 的算法,得知 b,c 构成 KeyPair,最后加密的是a,也就是 GR1dIlooUjX3zmY1GR1dIlooUjX3zmY1本身是由 function a(a) 生成的16个字节(char)长度的随机串,曾作为 AES 的 key 使用过。

那么剩下两个对应到算法里就是 en 了。
其实简单看一下就明白 3 不可能是 e,当然也可以跑一下,一个这么大的幂,根本就算不出来的。
当然跟入 js 库里面看一下就清楚了。

1
2
3
4
5
6
7
8
9
function RSAKeyPair(a, b, c) {
this.e = biFromHex(a),
this.d = biFromHex(b),
this.m = biFromHex(c),
this.chunkSize = 2 * biHighIndex(this.m),
this.radix = 16,
this.barrett = new BarrettMu(this.m)
}
function encryptedString(a, b){// 省略}

e 对应 010001, m 对应很长的串。 encryptedString 就是个大数算法,在运用公式前需要将 GR1dIlooUjX3zmY1 翻转一下。

老实说,原js里面似乎并没有先翻转,整个算法研究了一下与普通的RSA算法一样,
疑似以某种等价的方式置入了 biToHex 中,具体 biToHex 就没有分析为什么会等价了,有兴趣可以自行研究。

接下来就是写验证代码了,由于 python 原生支持大数,直接将 GR1dIlooUjX3zmY1 翻转并转成大数就行了,

这个在 python 里面只需要一句代码,如果不是在 python 中还会涉及大数运算问题,好在 python 原生就支持这个。

int(binascii.hexlify(p[::-1].encode("utf-8")), 16)

[::-1] 翻转,binascii.hexlify 转为 bytes 再通过 int(16代表源数据是16进制) 转为大数即可。

.b’1Ymz3XjUoolId1RG’
b’31596d7a33586a556f6f6c4964315247’
0X31596D7A33586A556F6F6C4964315247

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
##
# 加到最上面
##
import binascii

##
# 加到 MusicDecrypt 里
##
#
# reverse p : p = p[::-1]
# [p^e] % m
#
def rsa_encrypt(self, p, e, m):
return format(int(binascii.hexlify(p[::-1].encode("utf-8")), 16)**int(e, 16) % int(m, 16), 'x').zfill(256)

def test_rsa_encrypt(self):
self.assertEqual(
self.rsa_encrypt(
p="GR1dIlooUjX3zmY1",
e="010001",
m="00e0b509f6259df8642dbc35662901477df22677ec152b5ff68ace615bb7b725152b3ab17a876aea8a5aa76d2e417629ec4ee341f56135fccf695280104e0312ecbda92557c93870114af6c9d05c4f7f0c3685b7a46bee255932575cce10b424d813cfe4875d3e82047b97ddef52741d546b8e289dc6935b3ece0462db0a22b8e7"
),
"abc2e11cd93268085180aace6208c0caed01b7c2af641999a79adf362fb778a3fba5117f9c06541a5620d4dccd628085b53c1b22d971068a458e1ac16d831860ab2f1da4c7c8342f8bb815c6ab6c6c335cc797a4273124ff4846c9d58b0015691f933323fe080b8d026836880af99e918c7ace1813356b8bc327a52dcc24050a"
)

构造body

将两次AES之后 的结果进行 urlencode ,然后与RSA的返回值拼接。

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
## 加在最前面
import urllib
import random

randKey = ''

## 替换 __init__
def __init__(self, methodName='runTest'):
super(MusicDecrypt, self).__init__(methodName)
self.randKey = ''.join(random.sample(
string.ascii_letters + string.digits, 16))

## 加入 MusicDecrypt
def packet_body(self, id):
enc_text = self.aes_encrypt(
s='{"ids":"[' + id + ']","level":"standard","encodeType":"mp3","csrf_token":""}',
k="0CoJUm6Qyw8W8jud",
)
enc_text = self.aes_encrypt(
s=enc_text,
k=self.randKey,
)
enc_sec_key = self.rsa_encrypt(
p=self.randKey,
e="010001",
m="00e0b509f6259df8642dbc35662901477df22677ec152b5ff68ace615bb7b725152b3ab17a876aea8a5aa76d2e417629ec4ee341f56135fccf695280104e0312ecbda92557c93870114af6c9d05c4f7f0c3685b7a46bee255932575cce10b424d813cfe4875d3e82047b97ddef52741d546b8e289dc6935b3ece0462db0a22b8e7"
)

return ('params=' + urllib.parse.quote(enc_text) +
'&encSecKey=' + enc_sec_key)

简单整理之后,在 main 中调用

1
2
3
4
5
if __name__ == "__main__":
# unittest.main()

print(MusicDecrypt()
.packet_body('1297747757'))

将得到的数据放入 REST-Client 测试是否正常返回。依据这种思路,还能得到图片和歌词的地址,另外中间的参数也可以配置,
例如 encodeType 可以调整为 aac 等。

python版在这里就算告一段落了。

从js到c++

vcpkg

作为微软官方的c++包管理工具,虽然并不是很好用,但总比没有强。

1.安装 vs,本文用的 vs2019
2.安装 vcpkg,很容易,命令行跑一下就行了

看一下基本命令

  • search
  • install --triplet / install xxx:x64-windows

例如 安装 boost 库, : 后面代表的是 x64 的动态库
vcpkg install boost:x64-windows
可以随便输入一个错误信息,它会提示有哪些版本
vcpkg install boost:x

x64-windows
x64-windows-static
x86-windows
x86-windows-static

windows 上开发,大概也就这几个用的比较多了。

cryptopp 与 cpr

本文的加密库选的 cryptopp,事实上我对c版的 libsodium openssl,只是我考虑像 python 那样简洁的实现,所以语言上选的 c++,既然用 c++ 还是统一比较好。

另外本文选了 boost.test 做单元测试,偷懒的话可以不测试。

cryptopp开始用的时候还是有点不习惯。对着手册总算是翻完了。

本文为了行文方便,直接全都塞到了头文件,实际开发中应该分开,避免头文件互相引用导致重定义等问题。

编写加解密逻辑

c++部分就从简了,毕竟分析过一次实现逻辑了。

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
#pragma once

#include <ctime>
#include <cstdlib>
#include <string>
#include <algorithm>

#include <cryptopp/cryptlib.h>
#include <cryptopp/modes.h>
#include <cryptopp/aes.h>
#include <cryptopp/rsa.h>
#include <cryptopp/randpool.h>
#include <cryptopp/osrng.h>
#include <cryptopp/base64.h>
#include <cryptopp/hex.h>

#include <cpr/util.h>



namespace music{

class MusicDecrypt
{
public:

MusicDecrypt() {
// 构造 e
std::srand(static_cast<std::uint32_t>(std::time(nullptr)));
for (std::uint8_t i = 0; i < 16; i++) {
rsa_p.push_back(original_seq[rand() % original_seq.length()]);
}
};
~MusicDecrypt() {};

public:

auto aes_encrypt(std::string s, std::string k = "0CoJUm6Qyw8W8jud", std::string iv = "0102030405060708") {
std::string cipher_text;

CryptoPP::CBC_Mode<CryptoPP::AES>::Encryption cbc_enc(reinterpret_cast<const std::uint8_t*>(k.data()), k.length(), reinterpret_cast<const std::uint8_t*>(iv.data()));

CryptoPP::StringSource _(s, true,
new CryptoPP::StreamTransformationFilter(
cbc_enc, // cbc 编码
new CryptoPP::Base64Encoder( // base64解码 参数2置为false表示不换行
new CryptoPP::StringSink(cipher_text),false), // 不需要 delete, 里面包装了 https://www.cryptopp.com/wiki/StringSource
CryptoPP::BlockPaddingSchemeDef::PKCS_PADDING) // PKCS_PADDING 填充
);
return cipher_text;
}

auto rsa_encrypt(std::string p, std::string e = "", std::string m = "") {
if (e.empty())
e = default_e;
if (m.empty())
m = default_m;

std::string ps = "0x";
std::reverse(p.begin(), p.end());
CryptoPP::StringSource _(p, true,
new CryptoPP::HexEncoder(
new CryptoPP::StringSink(ps),
true,
0,
"0x"
) // HexEncoder
); // StringSource

CryptoPP::Integer ni(m.c_str()), ei(e.c_str()), pi(ps.c_str());

CryptoPP::RSA::PublicKey pubKey;
pubKey.Initialize(ni, ei);

CryptoPP::RSAES_OAEP_SHA_Encryptor pub(pubKey);

return CryptoPP::IntToString(pubKey.ApplyFunction(pi), 16);
}

auto paket_body(std::string id, std::string k = "") {
auto enc_text = u8"{\"ids\":\"[" +
id +
"]\",\"level\":\"standard\",\"encodeType\":\"mp3\",\"csrf_token\":\"\"}";

if (!k.empty())
rsa_p = k;

enc_text = aes_encrypt(enc_text);
enc_text = aes_encrypt(enc_text, rsa_p);
auto enc_sec_key = rsa_encrypt(rsa_p);

return "params=" + cpr::util::urlEncode(enc_text) +
"&encSecKey=" + enc_sec_key;
}

private:
std::string rsa_p;

private:
// 字符序列
static const std::string original_seq;
static const std::string default_e;
static const std::string default_m;
}; // MusicDecrypt

const std::string MusicDecrypt::original_seq = u8"0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
const std::string MusicDecrypt::default_e = u8"0x010001";
const std::string MusicDecrypt::default_m = u8"0x00e0b509f6259df8642dbc35662901477df22677ec152b5ff68ace615bb7b725152b3ab17a876aea8a5aa76d2e417629ec4ee341f56135fccf695280104e0312ecbda92557c93870114af6c9d05c4f7f0c3685b7a46bee255932575cce10b424d813cfe4875d3e82047b97ddef52741d546b8e289dc6935b3ece0462db0a22b8e7";

} // music

编写网络模块

cpr 的使用就比较简单了,和 pythonurllib 有的一比。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#pragma once
#include <cpr/cpr.h>

namespace music {

class MusicJson {
public:
MusicJson() {}
~MusicJson() {}

public:
auto get_json(std::string packet_body) {
auto r = cpr::Post(cpr::Url{ "https://music.163.com/weapi/song/enhance/player/url/v1?csrf_token=" },
cpr::Body{ packet_body },
cpr::Header{ {"Content-Type", "application/x-www-form-urlencoded"},{"Connection", "close"} });

return r.text;
}
}; // MusicJson

} // music

编写测试

需要注意,测试和上面的编写实际上是同步进行的

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
#include "config.h"

#if _TEST_MODULE
#define BOOST_TEST_MODULE music_test
#include <boost/test/included/unit_test.hpp>
#include <boost/log/trivial.hpp>

#include "music_decrypt.h"
#include "music_get_json.h"

// 全局测试夹具
class global_fixture
{
public:
global_fixture() {
std::cout << "开始准备测试数据------->" << std::endl;
}
virtual~global_fixture() {
std::cout << "清理测试环境<---------" << std::endl;
}
};

BOOST_GLOBAL_FIXTURE(global_fixture);
BOOST_AUTO_TEST_CASE(TestCase_4_aes_encrypt)
{
auto enc_text = music::MusicDecrypt().aes_encrypt(u8"{\"ids\":\"[28949499]\",\"level\":\"standard\",\"encodeType\":\"aac\",\"csrf_token\":\"\"}");

BOOST_LOG_TRIVIAL(info) << "开始测试 aes_encrypt";
BOOST_TEST(enc_text == "g1N6YybxFdV98P/fGY0407hwjh0evx5kPtxXR0nPd/WPPFsi9Lf67vFfjUnM3MDahHpqkyZMS+9goaszbHF+i1fIufNBu+8BbSvBCJSVfEU=");

BOOST_TEST(music::MusicDecrypt().aes_encrypt(enc_text, u8"GR1dIlooUjX3zmY1") ==
"9R0jh8yE6/JTTwoH4ujCacPMOwJdbXk39BlG3ODTNe+rHMLAOSHDlp/Mza7+15lOi8bvPMtLnA6gCOujDj5iuVBJF2a2DJVkNLtrTtgl+AXpsR5hSh0+EOfuads7lq41B9EpYKktwB72zOy+kafalQ=="
);
BOOST_LOG_TRIVIAL(info) << "结束测试 aes_encrypt";
}

BOOST_AUTO_TEST_CASE(TestCase_4_rsa_encrypt)
{
BOOST_LOG_TRIVIAL(info) << "开始测试 rsa_encrypt";
BOOST_TEST(music::MusicDecrypt().rsa_encrypt(u8"GR1dIlooUjX3zmY1") ==
"abc2e11cd93268085180aace6208c0caed01b7c2af641999a79adf362fb778a3fba5117f9c06541a5620d4dccd628085b53c1b22d971068a458e1ac16d831860ab2f1da4c7c8342f8bb815c6ab6c6c335cc797a4273124ff4846c9d58b0015691f933323fe080b8d026836880af99e918c7ace1813356b8bc327a52dcc24050a"
);
BOOST_LOG_TRIVIAL(info) << "结束测试 rsa_encrypt";
}

BOOST_AUTO_TEST_CASE(TestCase_4_paket_body)
{
BOOST_LOG_TRIVIAL(info) << "开始测试 paket_body";
BOOST_TEST(music::MusicDecrypt().paket_body(u8"28949499", u8"GR1dIlooUjX3zmY1") ==
"params=9R0jh8yE6%2fJTTwoH4ujCacPMOwJdbXk39BlG3ODTNe%2brHMLAOSHDlp%2fMza7%2b15lOi8bvPMtLnA6gCOujDj5iuT1EbsfNRzJrzZm6oIqgWhVsWcO%2bhrLFCHDHgyIYGdXOBmuWBRRiDCyMUFJAq3yrVw%3d%3d&encSecKey=abc2e11cd93268085180aace6208c0caed01b7c2af641999a79adf362fb778a3fba5117f9c06541a5620d4dccd628085b53c1b22d971068a458e1ac16d831860ab2f1da4c7c8342f8bb815c6ab6c6c335cc797a4273124ff4846c9d58b0015691f933323fe080b8d026836880af99e918c7ace1813356b8bc327a52dcc24050a"
);
BOOST_LOG_TRIVIAL(info) << "结束测试 paket_body";
}

BOOST_AUTO_TEST_CASE(TestCase_4_get_json)
{
BOOST_LOG_TRIVIAL(info) << "开始测试 get_json";
BOOST_TEST(music::MusicJson().get_json(
"params=9R0jh8yE6%2fJTTwoH4ujCacPMOwJdbXk39BlG3ODTNe%2brHMLAOSHDlp%2fMza7%2b15lOi8bvPMtLnA6gCOujDj5iuT1EbsfNRzJrzZm6oIqgWhVsWcO%2bhrLFCHDHgyIYGdXOBmuWBRRiDCyMUFJAq3yrVw%3d%3d&encSecKey=abc2e11cd93268085180aace6208c0caed01b7c2af641999a79adf362fb778a3fba5117f9c06541a5620d4dccd628085b53c1b22d971068a458e1ac16d831860ab2f1da4c7c8342f8bb815c6ab6c6c335cc797a4273124ff4846c9d58b0015691f933323fe080b8d026836880af99e918c7ace1813356b8bc327a52dcc24050a"
) != "");
BOOST_LOG_TRIVIAL(info) << "结束测试 get_json";
}

#endif

main 和 config

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include "config.h"

#if !_TEST_MODULE
#include <iostream>
#include "music_decrypt.h"
#include "music_get_json.h"


int main()
{
DEBUG_EXPR(std::cout << music::MusicJson().get_json(music::MusicDecrypt().paket_body("31830011")) << std::endl;);
}

#endif // _TEST
1
2
3
4
5
6
7
8
9
#pragma once
#define _TEST_MODULE 1


#ifdef _DEBUG
#define DEBUG_EXPR(x) do{x}while(0)
#else
#define DEBUG_EXPR(x)
#endif

补充

image-20221010181116980

该版本代码仅仅是个Demo,可以考虑 enum + map 支持一下获取歌词图片啥的,再引入json解析库做个下载器啥的。

趟坑 cpp-netlib

开始用的 cpp-netlib,毕竟支持同时客户端服务端,直到我的膝盖中了一箭。

vcpkg 默认当前安装的是 boost 1.7.0 + 版本,
当前的 cpp-netlib 1.3.0 只能用 boost 1.6.9 以下版本,具体哪个版本我也没翻到。

Error:

  1. error C3536: "delegate" 未初始化bugfix
  2. error: no member named 'get_io_service'issues_4636

总结

本文算是个大杂烩,从各方面讲了一通,算是为以后有移到php需求的时候打个底。 :D