抱歉,您的浏览器无法访问本站
本页面需要浏览器支持(启用)JavaScript
了解详情 >

前言

最近在 GitHub 上发现了一个很有意思的 Go 项目 kirox,它能够自动化注册 AWS Builder ID(Amazon 的开发者账号)。这个项目的技术含量非常高,涉及到很多我们在日常开发中不太接触,但又非常有价值的技术。

今天这篇文章,我想以一个小白的视角,来详细拆解这个项目用到的各种技术。即使你不懂 Go 语言,也能看懂。

项目地址https://github.com/huey1in/kirox

阅读提示:这篇文章偏原理拆解,尽量用通俗语言解释。但文中涉及 OAuth、浏览器指纹、JWE、IMAP 等概念,如果你第一次接触,建议先顺着往下读一遍,第二遍再回头看代码块。

项目概览

这个项目是做什么的?

简单来说,它可以自动化地注册 AWS 开发者账号。整个过程包括:

  • 创建邮箱
  • 填写注册信息
  • 接收验证码
  • 设置密码
  • 获取访问令牌

听起来很简单对吧?但难点在于:AWS 有很多反爬虫机制,如何绕过这些检测才是真正的技术挑战

技术架构图

项目技术架构意象图
AI 生成的项目技术架构意象图

技术栈一览

模块 技术 作用
前端 HTML + CSS + JavaScript Wails 框架的 UI 层
后端 Go 语言 核心业务逻辑
HTTP 客户端 tls-client 模拟浏览器 TLS 指纹
加密 JWE + XXTEA 密码加密和指纹加密
邮件 IMAP + MoeMail API 自动获取验证码
认证 OAuth 2.0 Device Code + PKCE 流程

一、什么是反爬虫?为什么 AWS 要检测?

1.1 反爬虫的基本概念

反爬虫(Anti-Bot)是网站用来识别和阻止自动化程序访问的技术。

想象一下:如果你是一个网站管理员,你不希望有人用脚本批量注册账号、刷票、爬取数据,对吧?所以你需要想办法区分:

  • 真人用户:用鼠标点击、用键盘输入、有思考时间
  • 机器人:瞬间完成操作、没有鼠标移动、行为模式固定

1.2 AWS 检测了什么?

AWS 的反爬虫系统会检测以下几个维度:

检测维度 真人特征 机器人特征
浏览器指纹 真实的 Canvas/WebGL 渲染结果 伪造或缺失的指纹
TLS 指纹 Chrome 的 TLS 握手特征 Go/curl 的特征
交互行为 有鼠标移动、按键间隔不均匀 瞬间完成、间隔固定
请求频率 正常人速度 极快的请求速度

二、TLS 指纹伪装 – 让 Go 程序”假装”是 Chrome

2.1 什么是 TLS 指纹?

当你用浏览器访问一个网站时,浏览器会和服务器进行 TLS 握手(就是那个小锁🔒的连接过程)。

在这个过程中,浏览器会告诉服务器:

  • 我支持哪些加密算法
  • 我支持哪些 TLS 版本
  • 我有哪些扩展功能

问题是:不同的客户端(Chrome、Firefox、Go、curl)发送的这些信息是不同的!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
┌─────────────────────────────────────────────────────────────┐
│ TLS 指纹示意 │
├─────────────────────────────────────────────────────────────┤
│ │
│ Chrome 浏览器: │
│ ├── 加密套件: TLS_AES_128_GCM_SHA256, ... │
│ ├── 扩展: GREASE, server_name, ... │
│ └── 特征: JA3 = "abc123..." │
│ │
│ Go 语言 http.Client: │
│ ├── 加密套件: TLS_RSA_WITH_AES_128_CBC_SHA, ... │
│ ├── 扩展: 较少 │
│ └── 特征: JA3 = "def456..." (和 Chrome 不同!) │
│ │
│ AWS 可以通过 JA3 指纹识别:这是真人还是脚本? │
│ │
└─────────────────────────────────────────────────────────────┘

2.2 项目如何解决?

这个项目使用了一个叫 bogdanfinn/tls-client 的库,它可以模拟 Chrome 的 TLS 指纹:

1
2
3
4
5
6
7
8
9
// 创建一个"伪装成 Chrome"的 HTTP 客户端
func NewTLSClient(proxy string, followRedirect bool) tls_client.HttpClient {
opts := []tls_client.HttpClientOption{
tls_client.WithTimeoutSeconds(60),
tls_client.WithClientProfile(profiles.Chrome_144), // 关键!使用 Chrome 144 的 TLS 配置
}
client, _ := tls_client.NewHttpClient(tls_client.NewNoopLogger(), opts...)
return client
}

通俗理解:就像一个人要假装是日本人,他需要学习日语的发音方式、说话习惯。这个库让 Go 程序学会了 Chrome 的”说话方式”。

2.3 小白必知

  • TLS 指纹 = 浏览器在建立加密连接时暴露的”身份特征”
  • JA3 = 一种 TLS 指纹的计算方法
  • 解决方法 = 使用专门的库来模拟真实浏览器的 TLS 行为

三、浏览器指纹 – 比你想象的更复杂

3.1 什么是浏览器指纹?

浏览器指纹是网站用来识别你的一系列技术信息,包括:

浏览器指纹与反爬检测
AI 生成的浏览器指纹与反爬检测示意图
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
┌─────────────────────────────────────────────────────────────┐
│ 浏览器指纹包含什么? │
├─────────────────────────────────────────────────────────────┤
│ │
│ 1. Canvas 指纹 │
│ └── 网站在隐藏的画布上画图,不同显卡渲染结果不同 │
│ │
│ 2. WebGL 指纹 │
│ └── 你的 GPU 型号、驱动版本等信息 │
│ │
│ 3. Math 精度指纹 │
│ └── 不同 CPU 计算 Math.tan() 的精度有微小差异 │
│ │
│ 4. 屏幕分辨率 │
│ └── 1920x1080 还是 2560x1440? │
│ │
│ 5. 设备内存/CPU 核心数 │
│ └── navigator.deviceMemory, hardwareConcurrency │
│ │
└─────────────────────────────────────────────────────────────┘

3.2 Canvas 指纹详解

这是最有趣的部分!让我详细解释:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<!-- 网站会在隐藏的 canvas 上画图 -->
<canvas id="fp" width="150" height="60"></canvas>
<script>
const ctx = document.getElementById('fp').getContext('2d');

// 画一个橙色矩形
ctx.fillStyle = '#f60'; // RGB(255, 102, 0)
ctx.fillRect(100, 1, 62, 20);

// 画一些文字(有抗锯齿效果)
ctx.font = '11pt Arial';
ctx.fillText('Hello World', 2, 15);

// 获取像素数据
const imageData = ctx.getImageData(0, 0, 150, 60);

// 计算每个像素值的分布(直方图)
const histogram = new Array(256).fill(0);
for (let i = 0; i < imageData.data.length; i++) {
histogram[imageData.data[i]]++;
}
</script>

为什么这能作为指纹?

因为不同显卡、不同驱动程序渲染同一个 Canvas 的结果是不完全相同的!

  • 抗锯齿算法不同
  • 颜色渲染有微小差异
  • 字体渲染不同

所以:Canvas 像素数据 = 你的硬件指纹

3.3 项目如何伪造浏览器指纹?

这个项目的做法非常巧妙:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// 生成 Canvas 指纹
func generateCanvasData() (int32, [256]int) {
var bins [256]int

// 根据真实浏览器的渲染规律生成

// 特征1:大量透明背景 → bin[0] 会很大
bins[0] = 5000 + rand.Intn(10001) // 5000~15000

// 特征2:Alpha 通道 → bin[255] 会很大
bins[255] = 6000 + rand.Intn(10001)

// 特征3:橙色 #f60 的 G 通道 = 102
// 所以 bin[102] 附近会有一个"尖峰"
spike1Pos := 100 + rand.Intn(6) // 100~105
bins[spike1Pos] = 500 + rand.Intn(200)

// 计算 hash(必须和 histogram 一致)
digest := sha256.Sum256(raw)
hash := int32(binary.LittleEndian.Uint32(digest[:4]))

return hash, bins
}

通俗理解:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
真实浏览器的 Canvas 渲染结果:
┌─────────────────────────────────────────┐
│ bin[0] ████████████████████ 12000 │ ← 透明背景
│ bin[102] ███ 600 │ ← 橙色矩形
│ bin[153] ██ 400 │ ← 混合颜色
│ bin[255] ██████████████████ 10000 │ ← Alpha 通道
│ 其他 █~██ 2~30 │ ← 抗锯齿
└─────────────────────────────────────────┘

项目伪造的结果:
┌─────────────────────────────────────────┐
│ bin[0] ████████████████████ 11500 │ ✓ 符合规律
│ bin[102] ███ 580 │ ✓ 符合规律
│ bin[153] ██ 420 │ ✓ 符合规律
│ bin[255] ██████████████████ 10500 │ ✓ 符合规律
│ 其他 █~██ 3~25 │ ✓ 符合规律
└─────────────────────────────────────────┘

关键洞察:项目不是随机生成数据,而是按照真实浏览器的统计规律来生成!

3.4 为什么随机整合不会被发现?

你可能会问:既然是随机组合不同硬件的特征,会不会出现”RTX 4090 + 4GB 内存”这种奇怪组合?

答案是:目前 AWS 没有检测这个!

AWS 更关注的是:

  1. Canvas 真实性:histogram 分布是否符合渲染规律
  2. 指纹一致性:UA 版本和 sec-ch-ua 版本是否匹配
  3. 交互合理性:是否有真实的用户行为

而不是硬件组合是否合理。


四、JWE 加密 – 密码不能明文传输

4.1 为什么要加密密码?

这个是常识了:密码绝对不能明文传输!

如果有人抓包看到你的密码是 MyPassword123,那你的账号就完了。

4.2 什么是 JWE?

JWE(JSON Web Encryption)是一种标准的加密格式,常用于 JWT 的加密版本。

1
2
3
4
5
6
7
8
9
10
11
12
13
┌─────────────────────────────────────────────────────────────┐
│ JWE 的结构 │
├─────────────────────────────────────────────────────────────┤
│ │
│ header.encryptedKey.iv.ciphertext.tag │
│ │ │ │ │ │ │
│ │ │ │ │ └─ 认证标签(防篡改) │
│ │ │ │ └─ 加密后的密码 │
│ │ │ └─ 初始化向量(随机数) │
│ │ └─ 加密后的会话密钥 │
│ └─ 算法信息、密钥 ID │
│ │
└─────────────────────────────────────────────────────────────┘

4.3 混合加密原理

JWE 使用了混合加密,这是现代加密的常见模式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
┌─────────────────────────────────────────────────────────────┐
│ 混合加密流程 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 1. 生成随机的 AES 密钥(会话密钥) │
│ └── 比如:a1b2c3d4... │
│ │
│ 2. 用 RSA 公钥加密这个 AES 密钥 │
│ └── RSA 加密(a1b2c3d4...) = X9Y8Z7... │
│ │
│ 3. 用 AES 密钥加密密码 │
│ └── AES 加密("MyPassword") = 密文... │
│ │
│ 4. 把加密后的 AES 密钥 + 密文 一起发送 │
│ │
│ 为什么这样? │
│ ├── RSA 很慢,不能直接加密大量数据 │
│ ├── AES 很快,但需要安全地传递密钥 │
│ └── 所以:RSA 加密 AES 密钥,AES 加密数据 │
│ │
└─────────────────────────────────────────────────────────────┘

4.4 项目中的实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
func (j *JWEEncryptor) Encrypt(password string, publicKey map[string]string) (string, error) {
// 1. 生成随机的 256 位 AES 密钥
cek := make([]byte, 32)
rand.Read(cek)

// 2. 用 RSA-OAEP-256 加密这个 AES 密钥
pubKey, _ := jwkToPublicKey(publicKey)
encryptedCEK, _ := rsa.EncryptOAEP(sha256.New(), rand.Reader, pubKey, cek, nil)

// 3. 用 AES-256-GCM 加密密码
iv := make([]byte, 12)
rand.Read(iv)
block, _ := aes.NewCipher(cek)
gcm, _ := cipher.NewGCM(block)
ciphertext := gcm.Seal(nil, iv, plaintext, []byte(headerB64))

// 4. 组装 JWE 格式
return fmt.Sprintf("%s.%s.%s.%s.%s",
headerB64, b64url(encryptedCEK), b64url(iv), b64url(ct), b64url(tag)), nil
}

五、OAuth 2.0 – 现代认证的标准流程

5.1 什么是 OAuth?

OAuth 是一种授权框架,允许第三方应用在不获取你密码的情况下,代表你访问资源。

生活中的例子:

  • 你用微信登录某个 App
  • App 只获得了”访问你头像和昵称”的权限
  • App 不知道你的微信密码

5.2 Device Code 流程

这个项目使用的是 Device Authorization Grant(设备授权),适用于没有浏览器的场景(比如 CLI 工具、智能电视)。

OAuth Device Code 授权流程示意图
AI 生成的 OAuth Device Code 授权流程示意图
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
┌─────────────────────────────────────────────────────────────┐
│ Device Code 流程 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 1. CLI 工具向 AWS 注册,获取 clientId │
│ └── POST /client/register │
│ │
│ 2. CLI 工具获取 device_code 和 user_code │
│ └── POST /device_authorization │
│ │
│ 3. 用户在浏览器中打开链接,输入 user_code │
│ └── https://aws.amazon.com/device │
│ │
│ 4. CLI 工具不断轮询,等待用户授权 │
│ └── POST /token (每 2 秒轮询一次) │
│ │
│ 5. 用户授权后,CLI 工具获得 access_token │
│ │
└─────────────────────────────────────────────────────────────┘

5.3 PKCE 流程

PKCE(Proof Key for Code Exchange)是 OAuth 2.0 的一个安全扩展,防止授权码被截获。

PKCE 安全机制示意图
AI 生成的 PKCE 安全机制示意图
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
┌─────────────────────────────────────────────────────────────┐
│ PKCE 流程 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 1. 生成随机的 code_verifier │
│ └── 比如:dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk │
│ │
│ 2. 计算 code_challenge = SHA256(code_verifier) │
│ └── E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM │
│ │
│ 3. 发送 code_challenge 给授权服务器 │
│ │
│ 4. 用户授权后,获得 authorization_code │
│ │
│ 5. 用 code + code_verifier 换取 token │
│ └── 服务器验证 SHA256(verifier) == challenge │
│ │
│ 为什么安全? │
│ └── 即使有人截获了 code,没有 verifier 也无法换 token │
│ │
└─────────────────────────────────────────────────────────────┘

六、IMAP 协议 – 自动获取邮件验证码

6.1 什么是 IMAP?

IMAP(Internet Message Access Protocol)是用于接收邮件的协议。你用的 Outlook、Gmail 客户端底层都是用 IMAP(或类似的协议)来收邮件的。

6.2 项目如何自动获取验证码?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
┌─────────────────────────────────────────────────────────────┐
│ 自动获取验证码流程 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 1. 记录当前收件箱邮件数量(比如 42 封) │
│ │
│ 2. 触发 AWS 发送验证码邮件 │
│ │
│ 3. 轮询检查收件箱: │
│ └── 每 5 秒检查一次 │
│ └── 如果邮件数量 > 42,说明收到了新邮件 │
│ │
│ 4. 获取新邮件内容 │
│ └── FETCH 43 (BODY.PEEK[TEXT]) │
│ │
│ 5. 用正则表达式提取 6 位验证码 │
│ └── regexp: `\b(\d{6})\b` │
│ │
└─────────────────────────────────────────────────────────────┘

6.3 XOAUTH2 认证

连接 IMAP 服务器需要认证,项目使用的是 XOAUTH2 方式:

1
2
3
4
5
func buildXOAuth2(email, accessToken string) string {
// 构建认证字符串
auth := fmt.Sprintf("user=%s\x01auth=Bearer %s\x01\x01", email, accessToken)
return base64.StdEncoding.EncodeToString([]byte(auth))
}

通俗理解:就像用 API Key 访问 API 一样,XOAUTH2 是用 OAuth Token 来访问邮箱。


七、并发控制 – 同时注册多个账号

7.1 为什么需要并发?

如果要注册 100 个账号,串行执行太慢了:

  • 串行:每个 30 秒 → 100 × 30 = 3000 秒 = 50 分钟
  • 并发 10 个:3000 / 10 = 300 秒 = 5 分钟

7.2 信号量模式

项目使用了信号量来控制并发数:

1
2
3
4
5
6
7
8
9
10
11
// 创建容量为 5 的信号量
sem := make(chan struct{}, 5)

for i := 0; i < 100; i++ {
sem <- struct{}{} // 获取信号量(满了就阻塞等待)

go func(idx int) {
defer func() { <-sem }() // 释放信号量
doTask(idx) // 执行任务
}(i)
}

通俗理解:

1
2
3
4
5
6
7
8
9
10
11
12
13
信号量就像停车场的车位计数器:

停车场容量 = 5
├── 车来了 → 计数器 +1
├── 计数器 = 5 → 后面的车必须等待
├── 车走了 → 计数器 -1
└── 等待的车可以进入

代码中的对应:
├── goroutine 启动 → sem <- struct{}{}
├── sem 满了 → goroutine 阻塞等待
├── goroutine 完成 → <-sem
└── 等待的 goroutine 可以执行

7.3 熔断机制

如果检测到严重错误(比如 IP 被封),需要立即停止所有任务:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
var otpKillOnce sync.Once  // 确保只执行一次

func doTask(i int) {
result := reg.Run()

if isKillSwitchError(result["error"]) {
otpKillOnce.Do(func() {
// 只执行一次:关闭 stopCh,通知所有 goroutine 停止
close(stopCh)
})
return
}
}

// 其他 goroutine 检查 stopCh
select {
case <-stopCh:
return // 任务被取消
default:
// 继续执行
}

八、内存缓存 + 异步刷盘 – 高性能存储设计

8.1 为什么需要缓存?

如果每次读写都操作磁盘:

  • 100 个并发任务同时读写 accounts.json
  • 文件锁冲突 → 性能下降
  • 频繁的磁盘 I/O → 速度慢

8.2 解决方案

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
┌─────────────────────────────────────────────────────────────┐
│ 内存缓存 + 异步刷盘 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 读操作:直接读内存 │
│ └── GetAccountsCached() → return _accountsCache │
│ │
│ 写操作:写内存 + 标记 dirty + 调度异步刷盘 │
│ └── ModifyAccountsCached(fn) │
│ ├── _accountsCache = fn(_accountsCache) // 写内存 │
│ ├── _accountsDirty = true // 标记脏数据│
│ └── scheduleFlush() // 调度刷盘 │
│ │
│ 异步刷盘:500ms 后统一写磁盘 │
│ └── time.AfterFunc(500*time.Millisecond, flushToDisk) │
│ │
│ 效果:100 次写操作 → 可能只写磁盘 1~2 次 │
│ │
└─────────────────────────────────────────────────────────────┘

通俗理解:

1
2
3
4
想象一个记账本:
├── 传统方式:每记一笔账就重新抄一遍整个账本(慢!)
├── 优化方式:先记在便签上,攒够一定数量再统一抄写
└── 效果:减少了抄写次数,提高了效率

九、Wails 桌面应用框架

9.1 什么是 Wails?

Wails 是一个用 Go 开发桌面应用的框架,类似于 Electron,但更轻量:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
┌─────────────────────────────────────────────────────────────┐
│ Wails vs Electron │
├─────────────────────────────────────────────────────────────┤
│ │
│ Electron: │
│ ├── 前端:HTML/CSS/JS │
│ ├── 后端:Node.js │
│ ├── 打包体积:~150MB │
│ └── 内存占用:~200MB │
│ │
│ Wails: │
│ ├── 前端:HTML/CSS/JS │
│ ├── 后端:Go │
│ ├── 打包体积:~10MB │
│ └── 内存占用:~50MB │
│ │
└─────────────────────────────────────────────────────────────┘

9.2 前后端通信

Wails 使用 JS Bridge 实现前后端通信:

1
2
3
4
5
6
// 前端 JavaScript
async function startTask() {
// 调用 Go 后端的 StartTask 方法
const result = await window.go.main.App.StartTask(config);
console.log(result);
}
1
2
3
4
// 后端 Go
func (a *App) StartTask(req task.StartTaskRequest) map[string]interface{} {
return task.StartTask(req)
}

十、完整注册流程图

最后,让我们看看整个注册流程是如何串起来的:

AWS Builder ID 注册与验证场景图
AI 生成的 AWS Builder ID 注册与验证场景图
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
┌─────────────────────────────────────────────────────────────┐
│ AWS Builder ID 注册流程 │
├─────────────────────────────────────────────────────────────┤
│ │
│ [1] OIDC 注册 ─────────────────────────────────────────┐ │
│ └── 获取 clientId, clientSecret │ │
│ │
│ [2] 设备授权 ─────────────────────────────────────────┐ │
│ └── 获取 deviceCode, userCode │ │
│ │
│ [3] 获取邮箱 ─────────────────────────────────────────┐ │
│ └── Outlook 或临时邮箱 │ │
│ │
│ [4-5] Portal + 工作流初始化 ─────────────────────────┐ │
│ └── 获取 workflowHandle │ │
│ │
│ [6] 提交邮箱 ────────────────────────────────────────┐ │
│ └── 判断是“登录”还是“注册” │ │
│ │
│ [7-8] 注册流程 + Profile 初始化 ────────────────────┐ │
│ └── 获取 workflowID │ │
│ │
│ [9-10] 发送并等待验证码 ────────────────────────────┐ │
│ └── 通过 IMAP 自动获取 6 位验证码 │ │
│ │
│ [11-12] 创建身份 + 设置密码 ───────────────────────┐ │
│ └── 使用 JWE 加密密码 │ │
│ │
│ [13] 获取 SSO Token ──────────────────────────────┐ │
│ └── 轮询等待授权完成 │ │
│ │
│ [14-15] Kiro 授权 + 令牌交换 ────────────────────┐ │
│ └── PKCE 流程获取 access_token │ │
│ │
│ [验活] 验证账号是否可用 ─────────────────────────┐ │
│ └── 刷新 token + 查询用量 │ │
│ │
└─────────────────────────────────────────────────────────────┘

总结

这个项目涉及的技术栈非常丰富,我来总结一下每个技术点的核心价值:

技术 核心价值 应用场景
TLS 指纹伪装 绕过传输层检测 反爬虫、安全测试
浏览器指纹伪造 绕过应用层检测 反爬虫、隐私保护
JWE 加密 安全传输敏感数据 API 安全、JWT
OAuth 2.0 标准授权流程 第三方登录、CLI 工具
IMAP 协议 自动化邮件处理 邮件机器人、监控
并发控制 提高性能 爬虫、任务队列
内存缓存 减少 I/O 高性能应用
Wails 轻量级桌面应用 工具开发

学习建议:

  1. 先理解原理:不要急着看代码,先理解每个技术是为了解决什么问题
  2. 动手实践:自己尝试实现一个简单的 OAuth 流程或 IMAP 客户端
  3. 逆向思维:学会从”攻击者”的角度思考,才能更好地防御

这篇文章分析的技术仅供学习交流,请勿用于非法用途。

如有问题,欢迎在评论区交流。

评论




站点访问量 Loading… 站点访客数 Loading…