~/components/README.md

组件一览:一页用上全部正文组件

文章里能用的每个正文组件各用一次,展示它们的样子和交互;数据全部是示例(文档地址段、example.com 主机、虚构的哈希)。

目录
  1. 0x00网络
  2. 0x01字节与布局
  3. 0x02调试与逆向
  4. 0x03代码
  5. 0x04数据
  6. 0x05安全分析产物
  7. 0x06过程与叙述

这一页把每个正文组件各用一次,展示它们在文章里的样子和交互,也方便在同一处检查样式、脚本和性能。数据全部是示例:地址取自文档保留段,主机用 example.com 系列域名,哈希、序列号和时间除非正文另有说明,都是虚构的。

网络

原始请求和响应,代替代理工具请求面板的截图:

login · Set-Cookie
请求
POST /login HTTP/1.1
Host: app.example.com
Content-Type: application/x-www-form-urlencoded

user=alice&password=REDACTED
响应
HTTP/1.1 303 See Other
Location: /
Set-Cookie: session=REDACTED; Path=/; Secure; HttpOnly; SameSite=Lax

数据包列表、字段树和字节,代替协议分析器的截图:

DNS 查询pcap
序号时间源地址目的地址协议长度信息
10.000000192.0.2.10192.0.2.53DNS71Standard query 0x4a7c A example.com
20.012204192.0.2.53192.0.2.10DNS87Standard query response 0x4a7c A example.com A 192.0.2.80
#1 · DNS · Standard query 0x4a7c A example.com
  • Ethernet II, Src: 00:00:5e:00:53:0a, Dst: 00:00:5e:00:53:35
    • Destination: 00:00:5e:00:53:35
    • Source: 00:00:5e:00:53:0a
    • Type: IPv4 (0x0800)
  • Internet Protocol Version 4, Src: 192.0.2.10, Dst: 192.0.2.53
    • 0100 .... = Version: 4
    • .... 0101 = Header Length: 20 bytes (5)
    • Time to Live: 64
    • Protocol: UDP (17)
    • Header Checksum: 0xda2e 未校验
    • Source Address: 192.0.2.10
    • Destination Address: 192.0.2.53
  • User Datagram Protocol, Src Port: 53001, Dst Port: 53
    • Source Port: 53001
    • Destination Port: 53
    • Length: 37
    • Checksum: 0x923b
  • Domain Name System (query)
    • Transaction ID: 0x4a7c
    • Flags: 0x0100 Standard query
      • 0... .... .... .... = Response: Message is a query
      • .... ...1 .... .... = Recursion desired: Do query recursively
    • Questions: 1
    • Queries
      • example.com: type A, class IN
        • Name: example.com
        • Type: A (1)
        • Class: IN (0x0001)
0000 00 00 5E 00 53 35 00 00 5E 00 53 0A 08 00 45 00
0010 00 39 1C 46 00 00 40 11 DA 2E C0 00 02 0A C0 00
0020 02 35 CF 09 00 35 00 25 92 3B 4A 7C 01 00 00 01
0030 00 00 00 00 00 00 07 65 78 61 6D 70 6C 65 03 63
0040 6F 6D 00 00 01 00 01
2 个数据包

字节与布局

十六进制转储加字段标注,代替十六进制编辑器的截图:

hello · ELF64 文件头(前 32 字节)
000000007F454C46020101000000000000000000
0000001003003E00010000006010000000000000
  1. EI_MAG0x00–0x03魔数 0x7F 加上 ASCII 的 E、L、F
  2. EI_CLASS0x042 = ELFCLASS64
  3. EI_DATA0x051 = 小端
  4. e_type0x10–0x113 = ET_DYN,PIE 也用这个值
  5. e_machine0x12–0x130x3E = x86-64
  6. e_entry0x18–0x1F入口点 0x1060

C 结构体的字节视图和字段表,代替结构体示意图或 pahole 输出:

struct malloc_chunk(glibc,x86-64)x86-64 · LE
偏移名称类型大小值
0x00mchunk_prev_size空闲时存放前一个块的大小size_t8
0x08mchunk_size块大小;低 3 位是标志位size_t80x31
0x10fdstruct malloc_chunk *80x55555555a2c0
0x18bk已被覆盖struct malloc_chunk *80x4141414141414141
0x20fd_nextsizestruct malloc_chunk *8
0x28bk_nextsizestruct malloc_chunk *8
sizeof(struct malloc_chunk) = 0x30(48 字节)

寄存器的位布局,代替处理器手册里的插图(翻转任意一位,各字段随之重新解码):

x86-64 页表项(4 KiB 页)64 位
XDPK0x0Address0x0000012345
AddressGPATDAPCDPWTU/SR/WP
值0x80000000_12345067= 9223372037160194151
位字段值
63XD禁止执行1
62–59PK保护键0x0
51–12Address页框的物理地址0x0000012345
8G全局页0
7PAT0
6D已写入(脏)1
5A已访问1
4PCD0
3PWT0
2U/S
  • 0 内核态
  • 1 用户态
1
1R/W
  • 0 只读
  • 1 可读写
1
0P存在1

进程树,代替进程管理器的截图:

进程树
  • explorer.exepid 4012user alice
    • WINWORD.EXEpid 6120打开了 invoice.docm
      • cmd.exepid 6644
        • powershell.exepid 7020cmd powershell -nop -w hidden -enc …编码过的命令行
          • conhost.exepid 7028
    • OneDrive.exepid 4388
      • FileCoAuth.exepid 4502
7 个进程

调试与逆向

带地址和机器码的反汇编,代替反汇编器的截图:

.text:_start · x86-64 · Intel 语法
0x1060f3 0f 1e faendbr64
0x106431 edxorebp, ebp; 清零帧指针,标记最外层栈帧
0x106649 89 d1movr9, rdx; rtld_fini → 第 6 个参数
0x106d48 83 e4 f0andrsp, 0xfffffffffffffff0; 栈按 16 字节对齐
0x107848 8d 3d ca 00 00 00leardi, [rip+0xca]; main @ 0x1149
0x107fff 15 53 2f 00 00callqword ptr [rip+0x2f53]; __libc_start_main@GOT

可以逐步查看的调试器快照,代替调试器截图或单步执行的 GIF:

leave; ret:拆除栈帧x86-64
  1. 步骤 1/3leave 之前

    rbp 指向保存的帧指针,返回地址就在它上方。

    代码
    0x401136<add+16>8b 45 fcmoveax, dword ptr [rbp-0x4]
    当前指令:0x401139<add+19>c9leave
    0x40113a<add+20>c3ret
    寄存器
    rax
    0x2a
    rbp
    0x7ffe3a8c1e40
    rsp
    0x7ffe3a8c1e30
    rip
    0x401139 <add+19>
    标志
    • ZF1
    • PF1
    栈
    指向此处的寄存器地址值说明
    rsp0x7ffe3a8c1e300x0000000000000000
    0x7ffe3a8c1e380x0000002a00000000局部变量
    rbp0x7ffe3a8c1e400x00007ffe3a8c1e60保存的 rbp
    0x7ffe3a8c1e480x0000000000401160返回地址
    内存
    地址字节
    0x7ffe3a8c1e4060 1e 8c 3a fe 7f 00 00 60 11 40 00 00 00 00 00
  2. 步骤 2/3leave 之后

    leave 等于 mov rsp, rbp 再 pop rbp。

    代码
    0x401136<add+16>8b 45 fcmoveax, dword ptr [rbp-0x4]
    0x401139<add+19>c9leave
    当前指令:0x40113a<add+20>c3ret
    寄存器
    rax
    0x2a
    rbp
    0x7ffe3a8c1e60(已改变,原值 0x7ffe3a8c1e40)
    rsp
    0x7ffe3a8c1e48(已改变,原值 0x7ffe3a8c1e30)
    rip
    0x40113a <add+20>(已改变,原值 0x401139)
    标志
    • ZF1
    • PF1
    栈
    指向此处的寄存器地址值说明
    0x7ffe3a8c1e300x0000000000000000
    0x7ffe3a8c1e380x0000002a00000000局部变量
    0x7ffe3a8c1e400x00007ffe3a8c1e60保存的 rbp
    rsp0x7ffe3a8c1e480x0000000000401160返回地址
    内存
    地址字节
    0x7ffe3a8c1e4060 1e 8c 3a fe 7f 00 00 60 11 40 00 00 00 00 00
  3. 步骤 3/3ret 之后

    ret 把返回地址弹进 rip。

    代码
    0x401136<add+16>8b 45 fcmoveax, dword ptr [rbp-0x4]
    0x401139<add+19>c9leave
    0x40113a<add+20>c3ret
    寄存器
    rax
    0x2a
    rbp
    0x7ffe3a8c1e60
    rsp
    0x7ffe3a8c1e50(已改变,原值 0x7ffe3a8c1e48)
    rip
    0x401160 <main+26>(已改变,原值 0x40113a)
    标志
    • ZF1
    • PF1
    栈
    地址值说明
    0x7ffe3a8c1e300x0000000000000000
    0x7ffe3a8c1e380x0000002a00000000局部变量
    0x7ffe3a8c1e400x00007ffe3a8c1e60保存的 rbp
    0x7ffe3a8c1e480x0000000000401160返回地址
    内存
    地址字节
    0x7ffe3a8c1e4060 1e 8c 3a fe 7f 00 00 60 11 40 00 00 00 00 00

标出关键帧、折叠库函数帧的调用栈,代替崩溃对话框的截图:

png_read_row 中的 SIGSEGV(示例查看器)x86-64
  1. #0(系统帧)0x7f3a2b9a1f2flibc.so.6!__memmove_avx_unaligned_erms+0x2f
  2. #1(系统帧)0x7f3a2b9a2a5dlibc.so.6!__memcpy_chk+0x1d
  3. #2(关键帧)0x7f3a2c01d9e4libpng16.so.16!png_read_row+0x1c4pngread.c:512

    row_buf 只有 0x40 字节,而这一行有 0x200 字节。

  4. #30x7f3a2c01e1a0libpng16.so.16!png_read_image+0x5cpngread.c:720
  5. #40x55d0c0a01234viewer!load_image+0x9asrc/image.c:88
  6. #50x55d0c0a00f8aviewer!main+0x2asrc/main.c:31
  7. #6(系统帧)0x7f3a2b829d90libc.so.6!__libc_start_call_main+0x80
  8. #7(系统帧)0x7f3a2b829e40libc.so.6!__libc_start_main+0x89
  9. #80x55d0c0a00b55viewer!_start+0x25
9 帧 · 最内层在上 · 4 个系统帧

控制流图,代替反汇编器图形视图的截图:

check_keyx86-64
entry入口
0x401126  cmp esi, 0x10
0x401129  jne bad
0x40112b  xor ecx, ecx

后继:bad(条件成立)、loop(条件不成立)。

loopecx = i
0x40112d  movzx eax, byte ptr [rdi+rcx]
0x401131  xor   al, 0x5a
0x401133  cmp   al, byte ptr [rdx+rcx]
0x401136  jne   bad

后继:bad(条件成立)、next(条件不成立)。

bad
0x401145  xor eax, eax
0x401147  ret

没有后继。

next
0x401138  inc ecx
0x40113a  cmp ecx, 0x10
0x40113d  jl  loop

后继:loop(条件成立)、ok(条件不成立)。

ok
0x40113f  mov eax, 1
0x401144  ret

没有后继。

5 个基本块 · 6 条边条件成立条件不成立高亮路径

可执行文件的头部、节区和导入表,代替 PE 查看器的截图:

hello.exePE32+
架构
x86-64
类型
EXE(控制台)
入口点
0x140001400
映像基址
0x140000000
安全属性
  • ASLR:启用
  • DEP:启用
  • CFG:未启用
  • SafeSEHn/a
头部字段
Machine
0x8664
AMD64
NumberOfSections
4
TimeDateStamp
0x66F2A1C0
DllCharacteristics
0x8160
HIGH_ENTROPY_VA | DYNAMIC_BASE | NX_COMPAT
节区4
名称RVA内存大小文件偏移文件大小权限熵说明
.text入口0x000010000x1A2C0x000004000x1C00r-x6.21
.rdata0x000030000xF100x000020000x1000r--4.87
.data0x000040000x6400x000030000x200rw-1.02
.rsrc0x000050000x1E00x000032000x200r--7.61压缩过的图标

熵(位/字节)< 1 稀疏< 6.8 常规6.8–7.2 偏高≥ 7.2 压缩或加密

导入2 个库 · 6 个符号
KERNEL32.dll4
  • GetStdHandle
  • WriteConsoleW
  • ExitProcess
  • VirtualProtect运行时修改页面保护属性
VCRUNTIME140.dll2
  • memset
  • __C_specific_handler

进程内存布局,代替终端里 vmmap 或 /proc/<pid>/maps 的截图:

demo(pid 4242):/proc/4242/maps64-bit

8 个区域 · 已映射 2.02 MiB · 4 处空洞 · 1 处 W+X

  • 映像5
  • 堆1
  • 私有1
  • 栈1
  • 可写且可执行
  • 作者标注
  • 未映射(已压缩)
起始结束大小权限类型模块 / 备注
000055555555400000005555555550004 KiBr--p映像/usr/local/bin/demo
000055555555500000005555555560004 KiBr-xp映像/usr/local/bin/demo
000055555555600000005555555580008 KiBrw-p映像/usr/local/bin/demo
000055555555800000005555555590004 KiB未映射
0000555555559000000055555557a000132 KiBrw-p堆[heap]
000055555557a00000007ffff7d8300042.7 TiB未映射
00007ffff7d8300000007ffff7dab000160 KiBr--p映像/usr/lib/x86_64-linux-gnu/libc.so.6
00007ffff7dab00000007ffff7f400001.58 MiBr-xp映像/usr/lib/x86_64-linux-gnu/libc.so.6
00007ffff7f4000000007ffff7fbb000492 KiB未映射
00007ffff7fbb00000007ffff7fbc0004 KiBrwxpW+X私有mmap(PROT_WRITE | PROT_EXEC) 得到的 RWX 页
00007ffff7fbc00000007ffffffde000128 MiB未映射
00007ffffffde00000007ffffffff000132 KiBrw-p栈[stack]

代码

读者只复制命令的终端会话,代替终端截图:

lab · file
$ file ./hello
./hello: ELF 64-bit LSB pie executable, x86-64
$ id -u
1000

给代码行加上编号注释,代替画了箭头的截图:

read_header.cc
ssize_t n = read(fd, hdr, sizeof *hdr);
if (n != sizeof *hdr) return -1;
if (hdr->len > MAX_BODY) return -1;
body = malloc(hdr->len);
  1. 即使读的是普通文件,read 返回的字节数也可能少于请求的数量。
  2. 这里把读不满当作错误;n 也可能是 -1,同时设置了 errno。
  3. 长度来自输入,用它决定分配大小之前必须先检查。

逐词标记、可以切换并排视图的补丁,代替代码审查页面的截图:

复制前先检查长度+3 −2
src/parse.c
@@ -41,7 +41,8 @@ int parse_header(int fd, struct hdr *out)
unsigned char buf[64];
ssize_t n = read(fd, buf, sizeof buf);
if (n < 0)
if (n < (ssize_t)sizeof *out)
return -1;
memcpy(out, buf, n);
memcpy(out, buf, sizeof *out);
out->len = be16toh(out->len);
return 0;
}

并排的两段代码,代替两张拼在一起的截图:

Smali
smali
.method public static check(I)Z
    const/4 v0, 0x1
    return v0
.end method
Java
java
static boolean check(int x) {
    return true;
}

数据

可以折叠、搜索的 JSON,代替解码后令牌的截图:

token.json(解码后的 JWT)json
{
"header": {
"alg": "RS256",
"kid": "2026-09"
},
"payload": {
"iss": "https://login.example.com/",
"aud": "api.example.com",必须校验它的 API
"sub": "user-4711",
"exp": 1790000000,
"scope": [
"read:posts",
"write:posts"
],
"admin": false,
"nonce": null
},
"sig_bits": 2048
}
对象 · 3 个键 · 324 B

可排序、可筛选、自动识别列类型的表格,代替电子表格的截图:

192.0.2.10 上监听的服务6 行
端口 协议 服务 Banner 最近发现
22 tcp ssh OpenSSH_9.6 2025-03-02
53 udp domain — 2025-02-28
161 udp snmp — 2025-02-27
443 tcp https nginx 2025-03-02
3306 tcp mysql 8.0.36 2025-03-01
8080 tcp http-alt Werkzeug/3.0.1 2025-03-01

直接由数字画出、附带数据表的图表,代替图表图片:

各版本的模糊测试崩溃数
唯一崩溃数
  • AFL++
  • libFuzzer

版本

数据表 4 × 2
版本AFL++libFuzzer
v1.0128
v1.11815
v1.2911
v1.32219

可以按级别和文字筛选的日志,代替日志控制台的截图:

auth-service.loglog
7 行ERROR 1WARN 4INFO 2
2025-03-02T09:14:00.120Z INFO  [main] auth - listening on 127.0.0.1:8443
2025-03-02T09:14:07.004Z WARN  [http-3] auth - login failed user=admin src=198.51.100.7
2025-03-02T09:14:07.513Z WARN  [http-3] auth - login failed user=admin src=198.51.100.7
2025-03-02T09:14:08.020Z WARN  [http-3] auth - login failed user=admin src=198.51.100.7
2025-03-02T09:14:08.533Z WARN  [http-3] auth - login failed user=admin src=198.51.100.7
2025-03-02T09:14:09.041Z ERROR [http-3] auth - rate limit reached, blocking 198.51.100.7 for 900s
2025-03-02T09:14:31.870Z INFO  [http-5] auth - login ok user=deploy src=192.0.2.24 mfa=totp

公式用 TeX 书写,不贴公式图片。行内:异或会抵消自身(a⊕a=0a \oplus a = 0);单独成行:

crc⁡(a⊕b⊕c)=crc⁡(a)⊕crc⁡(b)⊕crc⁡(c)\operatorname{crc}(a \oplus b \oplus c) = \operatorname{crc}(a) \oplus \operatorname{crc}(b) \oplus \operatorname{crc}(c)

安全分析产物

徽章颜色跟随 CVSS 分数的漏洞卡片,代替 CVE 页面的截图:

CVE-2026-XXXXXCVSS 5.5 MEDIUM示例数据

示例 ELF 解析库越界读

某示例 ELF 解析库在处理畸形的 e_shnum 时发生越界读取,可导致进程崩溃。

AVNALP
ACLH
PRNLH
UINR
SCU
CHLN
IHLN
AHLN
CVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:U/C:N/I:N/A:H
影响版本
< 2.4.1
修复版本
2.4.1
CWE
CWE-125
  1. 报告
  2. 确认
  3. 修复
  4. 公开

多引擎扫描结果,代替在线扫描服务的截图(样本是 EICAR 测试文件,引擎名是虚构的):

多引擎扫描COM

58/66

eicar.com

66 个引擎中 58 个判为恶意,1 个判为可疑

类型
EICAR 反病毒测试文件(ASCII 文本)
大小
68 字节
扫描时间
  • 恶意 58
  • 可疑 1
  • 未检出 7
  • 不支持 4
MD5
44d88612fea8a8f36de82e1278abb02f
SHA-1
3395856ce81f2b7382dee72602f798b642f14140
SHA-256
275a021bbfb6489e54d471899f7db9d1663fc695ec2fe2a2c4538aabf651fd0f
引擎判定检测名版本更新
Aegis恶意EICAR-Test-File (not a virus)4.2.1
Bastion恶意EICAR_Test_File—
Cinder可疑Heur.TestString——
Dagger未检出———
Ember不支持———
列出 5 个引擎,共 70 个

按给定检查日期判断有效期的证书链,代替浏览器证书查看器的截图:

www.example.comX.509
  1. 终端证书www.example.com有效期内
    主体
    CN=www.example.com, O=Example Corp, C=US
    签发者
    CN=Example TLS RSA CA 2026, O=Example Trust, C=US
    有效期
    → 剩余 111 天 · 共 365 天
    公钥
    EC P-256
    签名算法
    sha256WithRSAEncryption
    备用名称
    • DNS www.example.com
    • DNS example.com
    详细信息序列号 · 1 个扩展 · 用途
    序列号
    04:7A:C1:9E:5B:20:3D:8F:61:0E:A4:C7:92:1B:58:E3
    密钥用途
    digitalSignature
    扩展用途
    serverAuth, clientAuth
    扩展
    • basicConstraints 关键CA:FALSE

    由下一级签发

  2. 中间 CAExample TLS RSA CA 2026有效期内
    主体
    CN=Example TLS RSA CA 2026, O=Example Trust, C=US
    签发者
    CN=Example Root CA, O=Example Trust, C=US
    有效期
    → 剩余 1661 天 · 共 3652 天
    公钥
    RSA 2048
    签名算法
    sha256WithRSAEncryption

    由下一级签发

  3. 根 CAExample Root CA受信任有效期内自签名
    主体
    CN=Example Root CA, O=Example Trust, C=US
    签发者
    CN=Example Root CA, O=Example Trust, C=US
    有效期
    → 剩余 1872 天 · 共 9132 天
    公钥
    RSA 4096
    签名算法
    sha1WithRSAEncryption
检查日期 · 有效期按此日期计算

分组并去活(defang)的失陷指标,代替威胁报告里表格的截图:

加载器攻击活动,2026 年 9 月TLP:AMBER
SHA-256 1
  • 275a021bbfb6489e54d471899f7db9d1663fc695ec2fe2a2c4538aabf651fd0f
    投放器(EICAR 测试文件)首次发现
IPv4 1
  • 192[.]0[.]2[.]44:8443
    C2 监听端口
IPv6 1
  • 2001[:]db8[:][:]44
域名 1
  • update[.]example[.]com
    C2,解析到下面的地址首次发现
URL 1
  • hxxps://cdn[.]example[.]net/assets/app[.]js?v=3
    第二阶段载荷
邮箱 1
  • billing[@]example[.]test
    钓鱼邮件发件人
注册表键 1
  • HKCU\Software\Microsoft\Windows\CurrentVersion\Run\ExampleSync
    持久化
互斥体 1
  • Global\example-sync-7f3a
8 个指标 · 8 类 · 已去活(defang)

过程与叙述

带日期的时间线,代替时间线幻灯片:

披露时间线:示例路由器2024-03 → 2024-06
2024
  1. 报告 报告给厂商

    PoC 已发送到 security@example.com。

  2. +2 天
    确认 厂商确认收到报告
  3. +12 天
    CVE 编号 预留 CVE-2024-XXXX
  4. +2 天
    修复 开发并测试修复

    → · 37 天

  5. +43 天
    发布 发布固件 2.4.1
  6. +31 天
    披露 发布安全公告

    按约定在报告 90 天后公开。

报告 → 发布 59 天 · 发布 → 披露 31 天

带折叠输出的编号步骤,代替一连串截图:

复现路径穿越3 步
  1. 1

    启动有漏洞的版本

    bash
    docker run --rm -p 127.0.0.1:8080:8080 example/fileserver:1.4.2
  2. 2

    请求 Web 根目录之外的文件

    服务端拼接路径时没有做规范化:

    bash
    curl -s --path-as-is 'http://127.0.0.1:8080/static/../../etc/hostname'
    输出响应2 行fileserver-7f3c
    响应output
    fileserver-7f3c
    
  3. 3

    确认修复

    1.4.3 版对同样的请求返回 400 Bad Request。

同一件事在不同平台上的做法,代替每个系统各一张截图:

检查 TLS 证书
Linux
bash
openssl s_client -connect example.com:443 -servername example.com </dev/null | openssl x509 -noout -dates
Windows

PowerShell 没有 openssl,可以直接从连接上读取证书:

powershell
$tcp = [Net.Sockets.TcpClient]::new("example.com", 443)
$tls = [Net.Security.SslStream]::new($tcp.GetStream())
$tls.AuthenticateAsClient("example.com")
$tls.RemoteCertificate.GetExpirationDateString()

在浏览器里绘制的时序图,代替导出的图片:

sequenceDiagram
  accTitle: SameSite=Lax 下的跨站 POST
  accDescr: evil.example 上的页面向 app.example.com 提交表单,浏览器不带会话 Cookie,服务端拒绝。
  participant E as evil.example
  participant B as 浏览器
  participant A as app.example.com
  E->>B: 自动提交的 form POST
  B->>A: POST /transfer(无 session Cookie)
  A-->>B: 403 Forbidden

在页面里播放的终端录像,代替 GIF:

guest@lab:~/demo — forge.py
下载终端录像 crc32-forge.cast

图片只留给只有图片才能表达的内容:

xxd 输出:config.ini 里的 admin=0 改成了 admin=1(偏移 0x18),文件末尾追加了 ce 1a 39 22 四个字节,CRC32 仍是 102da2c5

编译器风格的提示块,分别用于补充说明、易错点和漏洞本身:

最后是藏在剧透后面的 CTF flag:

FLAGflag{c0mp0n3nts_3v3rywh3r3}
NORMAL~/components/README.md§--
0%zh-CN