在我前两天搞定TPLINK后,近期跟我聊到路由器的同学买了个红米的AX6000,想自己刷,发现自己搞不定了求助于我,于是我们一起刷这台路由器,就有了这篇教程

准备工作

首先得把小米路由器的系统降级,这位同学拿过来的时候,他降级到了1.0.60,所以降级过程就没有什么教程啦,可以去网上找找旧版的包,然后直接通过路由器管理面板的升级部分刷就行了

打开Telnet(路由器的开发者模式)

我们降级好路由器后,先要打开telnet,才能打开SSH,打开telnet的过程不要联网!!!

实测联网会打不开telnet

首先我们要登录进路由器的管理面板,在管理面板的地址栏中有我们需要的stok,例如http://192.168.31.1/cgi-bin/luci/;stok=71871cc803318e6f85e9c73d2ed7736c,这个stok=后面的内容就是我们需要的stok,我们复制下来,替换掉下面链接中的{stok},并复制到浏览器访问(访问的结果统一会显示{code: 0},四次访问都是,不再赘述,我使用的是curl)

http://192.168.31.1/cgi-bin/luci/;stok={stok}/api/misystem/set_sys_time?timezone=%20%27%20%3B%20zz%3D%24%28dd%20if%3D%2Fdev%2Fzero%20bs%3D1%20count%3D2%202%3E%2Fdev%2Fnull%29%20%3B%20printf%20%27%A5%5A%25c%25c%27%20%24zz%20%24zz%20%7C%20mtd%20write%20-%20crash%20%3B%20

http://192.168.31.1/cgi-bin/luci/;stok={stok}/api/misystem/set_sys_time?timezone=%20%27%20%3b%20reboot%20%3b%20

访问了以后路由器会重启,重启完了以后,我们再登录到路由器管理面板,此时stok会改变,我们复制新的stok,替换下面链接中的{stok},然后丢到浏览器访问

http://192.168.31.1/cgi-bin/luci/;stok={stok}/api/misystem/set_sys_time?timezone=%20%27%20%3B%20bdata%20set%20telnet_en%3D1%20%3B%20bdata%20set%20ssh_en%3D1%20%3B%20bdata%20set%20uart_en%3D1%20%3B%20bdata%20commit%20%3B%20

此链接跟第二条一样,都是重启用的

http://192.168.31.1/cgi-bin/luci/;stok={stok}/api/misystem/set_sys_time?timezone=%20%27%20%3b%20reboot%20%3b%20

我们打开一个能够支持telnet连接的软件,用户名和密码都是空,就可以连接进去了

自动化脚本

于是我随手撸了一个脚本

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 httpx

host = "http://192.168.31.1"

# First time
stok = input("请输入第一次的stok: ")

BASE = host + "/cgi-bin/luci/;stok="

MTD_WRITE_ROUTE = "/api/misystem/set_sys_time?timezone=%20%27%20%3B%20zz%3D%24%28dd%20if%3D%2Fdev%2Fzero%20bs%3D1%20count%3D2%202%3E%2Fdev%2Fnull%29%20%3B%20printf%20%27%A5%5A%25c%25c%27%20%24zz%20%24zz%20%7C%20mtd%20write%20-%20crash%20%3B%20"
REBOOT_ROUTE = "/api/misystem/set_sys_time?timezone=%20%27%20%3b%20reboot%20%3b%20"
ENABLE_TALNET_ROUTE = "/api/misystem/set_sys_time?timezone=%20%27%20%3B%20bdata%20set%20telnet_en%3D1%20%3B%20bdata%20set%20ssh_en%3D1%20%3B%20bdata%20set%20uart_en%3D1%20%3B%20bdata%20commit%20%3B%20"

response = httpx.get(BASE + stok + MTD_WRITE_ROUTE)
print(response.json())
response = httpx.get(BASE + stok + REBOOT_ROUTE)
print(response.json())

# Second time
stok = input("请输入第二次的stok: ")
response = httpx.get(BASE + stok + ENABLE_TALNET_ROUTE)
print(response.json())
response = httpx.get(BASE + stok + REBOOT_ROUTE)
print(response.json())

打开SSH

打开任意telnet客户端通过telnet连接后,我们需要打开SSH

设置root密码

通过下面的命令可以设置root的密码为admin

1
$ echo -e 'admin\nadmin' | passwd root

其实就是运行passwd root,然后输入了两次admin而已,你也可以自己改

打开SSH

接着我们运行下面的命令打开SSH

1
2
3
4
5
6
7
8
9
bdata set boot_wait=on
bdata commit
nvram set ssh_en=1
nvram set telnet_en=1
nvram set uart_en=1
nvram set boot_wait=on
nvram commit
sed -i 's/channel=.*/channel="debug"/g' /etc/init.d/dropbear
/etc/init.d/dropbear restart

输入后是不会有任何输出的,此时SSH就已经打开了

设置SSH开机自动启动

接着我们要设置开机开启SSH,要不然重启一下就没了

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
mkdir -p /data/auto_ssh && cd /data/auto_ssh

cat <<EOF > auto_ssh.sh
#!/bin/sh

auto_ssh_dir="/data/auto_ssh"
host_key="/etc/dropbear/dropbear_rsa_host_key"
host_key_bk="${auto_ssh_dir}/dropbear_rsa_host_key"

unlock() {
# Restore the host key.
[ -f \$host_key_bk ] && ln -sf \$host_key_bk \$host_key

# Enable telnet, ssh, uart and boot_wait.
[ "\$(nvram get telnet_en)" = 0 ] && nvram set telnet_en=1 && nvram commit
[ "\$(nvram get ssh_en)" = 0 ] && nvram set ssh_en=1 && nvram commit
[ "\$(nvram get uart_en)" = 0 ] && nvram set uart_en=1 && nvram commit
[ "\$(nvram get boot_wait)" = "off" ] && nvram set boot_wait=on && nvram commit

[ "\$(uci -c /usr/share/xiaoqiang get xiaoqiang_version.version.CHANNEL)" != 'stable' ] && {
uci -c /usr/share/xiaoqiang set xiaoqiang_version.version.CHANNEL='stable'
uci -c /usr/share/xiaoqiang commit xiaoqiang_version.version 2>/dev/null
}

channel=\$(/sbin/uci get /usr/share/xiaoqiang/xiaoqiang_version.version.CHANNEL)
if [ "\$channel" = "release" ]; then
sed -i 's/channel=.*/channel="debug"/g' /etc/init.d/dropbear
fi

if [ -z "\$(pidof dropbear)" -o -z "\$(netstat -ntul | grep :22)" ]; then
/etc/init.d/dropbear restart 2>/dev/null
/etc/init.d/dropbear enable
fi
}

install() {
# unlock SSH.
unlock

# host key is empty, restart dropbear to generate the host key.
[ -s \$host_key ] || /etc/init.d/dropbear restart 2>/dev/null

# Backup the host key.
if [ ! -s \$host_key_bk ]; then
i=0
while [ \$i -le 30 ]
do
if [ -s \$host_key ]; then
cp -f \$host_key \$host_key_bk 2>/dev/null
break
fi
let i++
sleep 1s
done
fi

# Add script to system autostart
uci set firewall.auto_ssh=include
uci set firewall.auto_ssh.type='script'
uci set firewall.auto_ssh.path="\${auto_ssh_dir}/auto_ssh.sh"
uci set firewall.auto_ssh.enabled='1'
uci commit firewall
echo -e "\033[32m SSH unlock complete. \033[0m"
}

uninstall() {
# Remove scripts from system autostart
uci delete firewall.auto_ssh
uci commit firewall
echo -e "\033[33m SSH unlock has been removed. \033[0m"
}

main() {
[ -z "\$1" ] && unlock && return
case "\$1" in
install)
install
;;
uninstall)
uninstall
;;
*)
echo -e "\033[31m Unknown parameter: \$1 \033[0m"
return 1
;;
esac
}

main "\$@"
EOF

chmod +x auto_ssh.sh

# 设置自动启动
uci set firewall.auto_ssh=include
uci set firewall.auto_ssh.type='script'
uci set firewall.auto_ssh.path='/data/auto_ssh/auto_ssh.sh'
uci set firewall.auto_ssh.enabled='1'
uci commit firewall

这个文件你也可以放在别的位置,自己修改上面脚本里面的文件位置就行,不过要注意重启是否会消失,有些路由器重启会自动清除文件的(例如我前阵子弄的WAR308)

设置时区

最后一步是设置时区,使用下面的命令设置时区

1
2
3
4
uci set system.@system[0].timezone='CST-8'
uci set system.@system[0].webtimezone='CST-8'
uci set system.@system[0].timezoneindex='2.84'
uci commit

关闭开发者模式

使用下面的命令关闭开发者模式

1
mtd erase crash

最后是重启,直接打reboot就行了

通过SSH刷入uboot

当我们通过SSH连接进路由器后,我们需要保证路由器可以联网,然后运行下面的命令

1
$ cd /tmp && curl --silent -O https://fastly.jsdelivr.net/gh/miaoermua/unlock-redmi-ax6000@main/uboot.sh && chmod +x uboot.sh && ./uboot.sh

运行了以后,脚本会帮你备份你的分区文件,记得把它们弄出来,要不然没办法恢复原厂系统,分别是/tmp/mtd5_FIP.bin/tmp/mtd4_Factory.bin

拿出来以后,再运行下面的命令来刷入uboot,最后会弹出一行success,就说明完成了

1
2
3
mtd erase FIP
mtd write /tmp/mt7986_redmi_ax6000-fip-fixed-parts.bin FIP
mtd verify /tmp/mt7986_redmi_ax6000-fip-fixed-parts.bin FIP

进入uboot,刷入openwrt系统

进入uboot模式

先拔掉电源,然后用牙签/卡针之类的尖锐的东西,戳着reset键,然后插上电源等待15秒以上,就可以松开了,这个就可以用电脑访问uboot了

uboot模式下,路由器的灯不会亮

电脑访问uboot

在进入uboot之前,请先把自己的电脑的ip地址修改一下,因为uboot模式下没有DHCP

然后访问http://192.168.31.1进入uboot,界面应该是像下面这样的

我们尝试了下面的两个系统(因为我这个同学记错路由器的空间大小以为CatWrt的分区大小给小了于是刷了ImmortalWrt)

刷入系统

下载好你需要的系统包后,直接在uboot里面上传,上传后会读条,这个时候路由器在校验系统包和计算md5,直接点击update就可以了

第一次刷可能会出现下图这样的fail提示,我们返回重新上传刷一次就行了

刷好了访问系统包对应的ip地址就可以进入openwrt了

其他

进入openwrt后,发现这个机子的存储应该是256MB(图片是CatWrt的终端)

内存为512MB左右

END

怎么说呢,这次应该是我第一次真正去刷品牌路由器成功的,我以前刷过小米的AX3000T但是刷炸了;讲真,品牌路由器的内存和存储还是给得太小了

当然这次成功也离不开下面这些参考文档(注:里面有些链接是过期的,所以为什么我会综合起来写一篇文,就是避免其他人做到一半发现链接404不知道怎么做了)

ALL IN ALL,刷路由器还是很好玩的 :D

Ref:

https://docs.qq.com/doc/DS1RlUVhUYXp3YnhL

https://www.right.com.cn/forum/thread-8261104-1-1.html

https://blog.csdn.net/sxf1061700625/article/details/130328437

真正的END

因为我发现我们学校会BAN我的MAC地址,于是我顺带放出我写的MAC地址更换脚本(可以设置计划任务)

MAC备份还原脚本

避免你需要还原你路由器真正的mac的时候找不到mac,建议你用这个先备份一下你路由器的mac

如果你的网口不是eth0,请先更换一下网口!!!

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
#!/bin/sh

MAC_FILE="/root/mac"

# 获取当前MAC地址并保存到文件
save_mac() {
CURRENT_MAC=$(ip link show eth0 | grep ether | awk '{print $2}')
echo "当前的MAC地址是: $CURRENT_MAC"
echo "$CURRENT_MAC" > "$MAC_FILE"
echo "已保存当前MAC地址到 $MAC_FILE"
}

# 从文件中恢复MAC地址
restore_mac() {
if [ -f "$MAC_FILE" ]; then
SAVED_MAC=$(cat "$MAC_FILE")
echo "从文件恢复MAC地址: $SAVED_MAC"
ip link set dev eth0 down
ip link set dev eth0 address "$SAVED_MAC"
ip link set dev eth0 up
echo "已恢复MAC地址到eth0"
else
echo "MAC文件不存在,无法恢复MAC地址"
fi
}

# 检查参数并执行对应的操作
if [ "$1" = "save" ]; then
save_mac
elif [ "$1" = "restore" ]; then
restore_mac
else
echo "用法: $0 {save|restore}"
echo "save: 保存当前的MAC地址"
echo "restore: 恢复之前保存的MAC地址"
fi

Mac生成替换脚本

我这里设置了固定的前缀,是因为我路由器的MAC地址带了这三个,建议改成自己的

如果你的网口不是eth0,请先更换一下网口!!!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#!/bin/sh

# 生成一个随机的MAC地址
generate_mac() {
PREFIX="c6:1f:d8"
# 使用 /dev/urandom 获取随机数
HEX1=$(hexdump -n 1 -e '1/1 "%02X"' /dev/urandom)
HEX2=$(hexdump -n 1 -e '1/1 "%02X"' /dev/urandom)
HEX3=$(hexdump -n 1 -e '1/1 "%02X"' /dev/urandom)
echo "$PREFIX:$HEX1:$HEX2:$HEX3"
}

# 获取新的MAC地址
NEW_MAC=$(generate_mac)
echo "生成的新MAC地址为: $NEW_MAC"

# 使用新的MAC地址修改eth0的MAC地址
ip link set dev eth0 down
ip link set dev eth0 address $NEW_MAC
ip link set dev eth0 up

# 验证修改是否成功
ip link show eth0 | grep ether

Openwrt备份备份恢复脚本

注意修改前两行

1
2
3
4
5
# 定义备份目录
BACKUP_DIR="/mnt/usb1-1"

# 定义 OpenWrt 系统路径
OPENWRT_MMC="/dev/mmcblk0"
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
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
#!/bin/ash

# 定义 ANSI 转义码用于颜色
YELLOW='\e[33m'
NC='\e[0m' # No Color

# 打印带有颜色的 ASCII Art 标志
print_owrt_logo() {
echo -e "${YELLOW} _ _ _ _ _
_____ ___ __| |_ _ _| |_(_) | ___| |__
/ _ \ \ /\ / / '__| __|____| | | | __| | | / __| '_ \
| (_) \ V V /| | | ||_____| |_| | |_| | |_\__ \ | | |
\___/ \_/\_/ |_| \__| \__,_|\__|_|_(_)___/_| |_|

—— @GamerNoTitle https://bili33.top
${NC}"
}

# 调用打印 logo 的函数
print_owrt_logo

# OpenWrt 备份与恢复管理脚本

# 定义备份目录
BACKUP_DIR="/mnt/usb1-1"

# 定义 OpenWrt 系统路径
OPENWRT_MMC="/dev/mmcblk0"

# 定义配置文件路径
OPENWRT_CONFIG="/etc/config"
FIREWALL_CONFIG="/etc/config/firewall"

# 定义各类备份的子目录
OPENWRT_BACKUP_DIR="$BACKUP_DIR/openwrt-backup"
OPENWRT_CONFIG_BACKUP_DIR="$BACKUP_DIR/openwrt-config-backup"
IPTABLES_BACKUP_DIR="$BACKUP_DIR/iptables-backup"
FIREWALL_BACKUP_DIR="$BACKUP_DIR/firewall-backup"

# 获取当前日期
CURRENT_DATE=$(date +%Y%m%d)

# 创建必要的备份目录
mkdir -p "$OPENWRT_BACKUP_DIR"
mkdir -p "$OPENWRT_CONFIG_BACKUP_DIR"
mkdir -p "$IPTABLES_BACKUP_DIR"
mkdir -p "$FIREWALL_BACKUP_DIR"

# 显示菜单
show_menu() {
echo "=========== owrt-util.sh ============="
echo " OpenWrt 备份与恢复管理脚本 "
echo " Made by @GamerNoTitle "
echo " https://bili33.top "
echo "======================================"
echo "1. 备份整个 OpenWrt 为压缩镜像 (.tar.gz)"
echo "2. 从压缩镜像恢复整个 OpenWrt"
echo "3. 备份 OpenWrt 配置"
echo "4. 恢复 OpenWrt 配置"
echo "5. 备份 iptables 配置"
echo "6. 恢复 iptables 配置"
echo "7. 备份防火墙配置"
echo "8. 恢复防火墙配置"
echo "9. 挂载 USB 设备"
echo "0. 退出"
echo "======================================"
echo "当前文件备份目录:"
echo "系统镜像备份:$OPENWRT_BACKUP_DIR"
echo "OpenWrt 配置备份:$OPENWRT_CONFIG_BACKUP_DIR"
echo "iptables 配置备份:$IPTABLES_BACKUP_DIR"
echo "防火墙配置备份:$FIREWALL_BACKUP_DIR"
echo "======================================"
echo -n "请选择一个选项: "
}

# 备份整个 OpenWrt 为压缩镜像
backup_openwrt() {
echo "======================================"
echo "备份整个 OpenWrt 系统为压缩镜像 (.tar.gz)"
echo "======================================"

# 列出现有的 OpenWrt 镜像备份文件
echo "当前备份目录中的 OpenWrt 镜像文件:"
ls -lh $OPENWRT_BACKUP_DIR/openwrt-backup-*.tar.gz 2>/dev/null || echo "没有找到 OpenWrt 镜像备份文件。"

# 提示用户是否继续备份
echo -n "是否继续备份整个 OpenWrt 系统? [y/N]: "
read CONFIRMATION

if [ "$CONFIRMATION" != "y" ] && [ "$CONFIRMATION" != "Y" ]; then
echo "备份操作已取消。"
return
fi

# 创建临时文件名
TEMP_BIN_FILE="$OPENWRT_BACKUP_DIR/openwrt-temp.bin"
# 创建备份文件名
BACKUP_FILE="$OPENWRT_BACKUP_DIR/openwrt-backup-$CURRENT_DATE.tar.gz"

# 执行备份到临时文件
echo "开始备份到临时文件,请稍候..."
dd if="$OPENWRT_MMC" of="$TEMP_BIN_FILE" bs=1M

if [ $? -eq 0 ]; then
echo "成功: OpenWrt 系统已备份到临时文件 $TEMP_BIN_FILE"
# 压缩备份文件
echo "正在创建压缩备份..."
tar -czvf "$BACKUP_FILE" "$TEMP_BIN_FILE"
if [ $? -eq 0 ]; then
echo "成功: OpenWrt 系统已备份到 $BACKUP_FILE"
# 计算并保存 MD5
echo "正在计算 MD5 校验和..."
md5sum "$BACKUP_FILE" > "$BACKUP_FILE.md5"
if [ $? -eq 0 ]; then
echo "成功: MD5 校验和已保存到 $BACKUP_FILE.md5"
else
echo "警告: MD5 校验和保存失败。"
fi
else
echo "错误: OpenWrt 系统压缩备份失败。"
fi
# 清理临时文件
rm "$TEMP_BIN_FILE"
else
echo "错误: OpenWrt 系统备份到临时文件失败。"
fi
}

# 从压缩镜像恢复整个 OpenWrt
restore_openwrt() {
echo "======================================"
echo "从压缩镜像恢复整个 OpenWrt 系统"
echo "======================================"

# 列出现有的 OpenWrt 镜像备份文件
echo "当前备份目录中的 OpenWrt 镜像文件:"
ls -lh $OPENWRT_BACKUP_DIR/openwrt-backup-*.tar.gz 2>/dev/null || { echo "没有找到 OpenWrt 镜像备份文件。"; return; }

# 提示用户选择恢复的镜像文件
echo -n "请输入要恢复的镜像文件名(例如 openwrt-backup-20250106.tar.gz),或输入 'n'/'N' 取消: "
read BACKUP_FILE_NAME

if [ "$BACKUP_FILE_NAME" = "n" ] || [ "$BACKUP_FILE_NAME" = "N" ]; then
echo "恢复操作已取消。"
return
fi

# 定义备份文件路径
BACKUP_FILE="$OPENWRT_BACKUP_DIR/$BACKUP_FILE_NAME"
MD5_FILE="$BACKUP_FILE.md5"

# 检查备份文件是否存在
if [ ! -f "$BACKUP_FILE" ]; then
echo "错误: 镜像文件 $BACKUP_FILE 不存在。"
return
fi

# 校验 MD5
if [ -f "$MD5_FILE" ]; then
echo "正在校验 MD5 校验和..."
md5sum -c "$MD5_FILE"
if [ $? -ne 0 ]; then
echo "错误: MD5 校验和不匹配,文件可能已损坏。请检查备份文件。"
return
else
echo "成功: MD5 校验和匹配。"
fi
else
echo "警告: 未找到 MD5 校验和文件,无法进行校验。"
fi

# 提示用户确认恢复
echo -n "警告: 恢复操作将擦除当前 OpenWrt 系统,确定恢复? [y/N]: "
read CONFIRMATION

if [ "$CONFIRMATION" != "y" ] && [ "$CONFIRMATION" != "Y" ]; then
echo "恢复操作已取消。"
return
fi

# 获取备份文件所在目录
BACKUP_DIR_TEMP=$(dirname "$BACKUP_FILE")
TEMP_BIN_FILE="$BACKUP_DIR_TEMP/openwrt-temp.bin"

# 解压备份文件到同级目录
echo "开始解压备份文件..."
tar -xzvf "$BACKUP_FILE" -C "$BACKUP_DIR_TEMP"

if [ $? -ne 0 ]; then
echo "错误: 解压备份文件失败。"
return
fi

# 执行恢复
echo "开始从解压文件恢复,请稍候..."
dd if="$TEMP_BIN_FILE" of="$OPENWRT_MMC" bs=1M

if [ $? -eq 0 ]; then
echo "成功: OpenWrt 系统已从镜像恢复。"
else
echo "错误: OpenWrt 系统恢复失败。"
fi

# 清理解压文件
rm "$TEMP_BIN_FILE"
}

# 备份 OpenWrt 配置
backup_openwrt_config() {
echo "======================================"
echo "备份 OpenWrt 配置 (使用 sysupgrade)"
echo "======================================"

# 列出现有的 OpenWrt 配置备份文件
echo "当前备份目录中的 OpenWrt 配置备份文件:"
ls -lh $OPENWRT_CONFIG_BACKUP_DIR/openwrt-config-backup-*.bak 2>/dev/null || echo "没有找到 OpenWrt 配置备份文件。"

# 提示用户是否继续备份
echo -n "是否继续备份 OpenWrt 配置? [y/N]: "
read CONFIRMATION

if [ "$CONFIRMATION" != "y" ] && [ "$CONFIRMATION" != "Y" ]; then
echo "备份操作已取消。"
return
fi

# 创建备份文件名
OPENWRT_CONFIG_BACKUP_FILE="$OPENWRT_CONFIG_BACKUP_DIR/openwrt-config-backup-$CURRENT_DATE.bak"

# 备份 OpenWrt 配置
echo "使用 sysupgrade 进行配置备份..."
sysupgrade -b "$OPENWRT_CONFIG_BACKUP_FILE"

if [ $? -eq 0 ]; then
echo "成功: OpenWrt 配置已备份到 $OPENWRT_CONFIG_BACKUP_FILE"
# 计算并保存 MD5
echo "正在计算 MD5 校验和..."
md5sum "$OPENWRT_CONFIG_BACKUP_FILE" > "$OPENWRT_CONFIG_BACKUP_FILE.md5"
if [ $? -eq 0 ]; then
echo "成功: MD5 校验和已保存到 $OPENWRT_CONFIG_BACKUP_FILE.md5"
else
echo "警告: MD5 校验和保存失败。"
fi
else
echo "错误: OpenWrt 配置备份失败。"
fi
}

# 恢复 OpenWrt 配置
restore_openwrt_config() {
echo "======================================"
echo "从备份恢复 OpenWrt 配置 (使用 sysupgrade)"
echo "======================================"

# 列出现有的 OpenWrt 配置备份文件
echo "当前备份目录中的 OpenWrt 配置备份文件:"
ls -lh $OPENWRT_CONFIG_BACKUP_DIR/openwrt-config-backup-*.bak 2>/dev/null || { echo "没有找到 OpenWrt 配置备份文件。"; return; }

# 提示用户选择恢复的备份文件
echo -n "请输入要恢复的 OpenWrt 配置备份文件名(例如 openwrt-config-backup-20250106.bak),或输入 'n'/'N' 取消: "
read BACKUP_FILE_NAME

if [ "$BACKUP_FILE_NAME" = "n" ] || [ "$BACKUP_FILE_NAME" = "N" ]; then
echo "恢复操作已取消。"
return
fi

# 定义备份文件路径
BACKUP_FILE="$OPENWRT_CONFIG_BACKUP_DIR/$BACKUP_FILE_NAME"
MD5_FILE="$BACKUP_FILE.md5"

# 检查备份文件是否存在
if [ ! -f "$BACKUP_FILE" ]; then
echo "错误: 备份文件 $BACKUP_FILE 不存在。"
return
fi

# 校验 MD5
if [ -f "$MD5_FILE" ]; then
echo "正在校验 MD5 校验和..."
md5sum -c "$MD5_FILE"
if [ $? -ne 0 ]; then
echo "错误: MD5 校验和不匹配,文件可能已损坏。请检查备份文件。"
return
else
echo "成功: MD5 校验和匹配。"
fi
else
echo "警告: 未找到 MD5 校验和文件,无法进行校验。"
fi

# 提示用户是否继续恢复
echo -n "警告: 恢复操作将覆盖当前 OpenWrt 配置,确定恢复? [y/N]: "
read CONFIRMATION

if [ "$CONFIRMATION" != "y" ] && [ "$CONFIRMATION" != "Y" ]; then
echo "恢复操作已取消。"
return
fi

# 备份当前 OpenWrt 配置
CURRENT_BACKUP_FILE="$OPENWRT_CONFIG_BACKUP_DIR/openwrt-config-current-backup-$CURRENT_DATE.bak"
echo "备份当前 OpenWrt 配置到 $CURRENT_BACKUP_FILE..."
sysupgrade -b "$CURRENT_BACKUP_FILE"

if [ $? -ne 0 ]; then
echo "错误: 无法备份当前 OpenWrt 配置。"
return
fi

echo "当前 OpenWrt 配置已备份到 $CURRENT_BACKUP_FILE"

# 恢复 OpenWrt 配置
echo "使用 sysupgrade 恢复配置..."
sysupgrade -r "$BACKUP_FILE"

if [ $? -eq 0 ]; then
echo "成功: OpenWrt 配置已从 $BACKUP_FILE 恢复。"
else
echo "错误: OpenWrt 配置恢复失败。"
return
fi

# 重启网络服务以应用更改
echo "重启网络服务以应用更改..."
/etc/init.d/network restart

if [ $? -eq 0 ]; then
echo "成功: 网络服务已重启并应用恢复的配置。"
else
echo "错误: 网络服务重启失败。"
fi
}

# 备份 iptables 配置
backup_iptables() {
echo "======================================"
echo "备份 iptables 配置"
echo "======================================"

# 列出现有的 iptables 备份文件
echo "当前备份目录中的 iptables 备份文件:"
ls -lh $IPTABLES_BACKUP_DIR/iptables-backup-*.bak 2>/dev/null || echo "没有找到 iptables 备份文件。"

# 提示用户是否继续备份
echo -n "是否继续备份 iptables 配置? [y/N]: "
read CONFIRMATION

if [ "$CONFIRMATION" != "y" ] && [ "$CONFIRMATION" != "Y" ]; then
echo "备份操作已取消。"
return
fi

# 创建备份文件名
IPTABLES_BACKUP_FILE="$IPTABLES_BACKUP_DIR/iptables-backup-$CURRENT_DATE.bak"

# 备份 iptables 配置
echo "备份 iptables 配置..."
iptables-save > "$IPTABLES_BACKUP_FILE"

if [ $? -eq 0 ]; then
echo "成功: iptables 配置已备份到 $IPTABLES_BACKUP_FILE"
# 计算并保存 MD5
echo "正在计算 MD5 校验和..."
md5sum "$IPTABLES_BACKUP_FILE" > "$IPTABLES_BACKUP_FILE.md5"
if [ $? -eq 0 ]; then
echo "成功: MD5 校验和已保存到 $IPTABLES_BACKUP_FILE.md5"
else
echo "警告: MD5 校验和保存失败。"
fi
else
echo "错误: iptables 配置备份失败。"
fi
}

# 恢复 iptables 配置
restore_iptables() {
echo "======================================"
echo "从备份恢复 iptables 配置"
echo "======================================"

# 列出现有的 iptables 备份文件
echo "当前备份目录中的 iptables 备份文件:"
ls -lh $IPTABLES_BACKUP_DIR/iptables-backup-*.bak 2>/dev/null || { echo "没有找到 iptables 备份文件。"; return; }

# 提示用户选择恢复的备份文件
echo -n "请输入要恢复的 iptables 配置文件名(例如 iptables-backup-20250106.bak),或输入 'n'/'N' 取消: "
read BACKUP_FILE_NAME

if [ "$BACKUP_FILE_NAME" = "n" ] || [ "$BACKUP_FILE_NAME" = "N" ]; then
echo "恢复操作已取消。"
return
fi

# 定义备份文件路径
BACKUP_FILE="$IPTABLES_BACKUP_DIR/$BACKUP_FILE_NAME"
MD5_FILE="$BACKUP_FILE.md5"

# 检查备份文件是否存在
if [ ! -f "$BACKUP_FILE" ]; then
echo "错误: 备份文件 $BACKUP_FILE 不存在。"
return
fi

# 校验 MD5
if [ -f "$MD5_FILE" ]; then
echo "正在校验 MD5 校验和..."
md5sum -c "$MD5_FILE"
if [ $? -ne 0 ]; then
echo "错误: MD5 校验和不匹配,文件可能已损坏。请检查备份文件。"
return
else
echo "成功: MD5 校验和匹配。"
fi
else
echo "警告: 未找到 MD5 校验和文件,无法进行校验。"
fi

# 提示用户是否继续恢复
echo -n "警告: 恢复操作将覆盖当前 iptables 配置,确定恢复? [y/N]: "
read CONFIRMATION

if [ "$CONFIRMATION" != "y" ] && [ "$CONFIRMATION" != "Y" ]; then
echo "恢复操作已取消。"
return
fi

# 恢复 iptables 配置
echo "恢复 iptables 配置..."
iptables-restore < "$BACKUP_FILE"

if [ $? -eq 0 ]; then
echo "成功: iptables 配置已从 $BACKUP_FILE 恢复。"
else
echo "错误: iptables 配置恢复失败。"
fi
}

# 备份防火墙配置
backup_firewall() {
echo "======================================"
echo "备份防火墙配置"
echo "======================================"

# 列出现有的防火墙备份文件
echo "当前备份目录中的防火墙备份文件:"
ls -lh $FIREWALL_BACKUP_DIR/firewall-backup-*.bak 2>/dev/null || echo "没有找到防火墙备份文件。"

# 提示用户是否继续备份
echo -n "是否继续备份防火墙配置? [y/N]: "
read CONFIRMATION

if [ "$CONFIRMATION" != "y" ] && [ "$CONFIRMATION" != "Y" ]; then
echo "备份操作已取消。"
return
fi

# 创建备份文件名
FIREWALL_BACKUP_FILE="$FIREWALL_BACKUP_DIR/firewall-backup-$CURRENT_DATE.bak"

# 备份防火墙配置
echo "备份防火墙配置..."
cp "$FIREWALL_CONFIG" "$FIREWALL_BACKUP_FILE"

if [ $? -eq 0 ]; then
echo "成功: 防火墙配置已备份到 $FIREWALL_BACKUP_FILE"
# 计算并保存 MD5
echo "正在计算 MD5 校验和..."
md5sum "$FIREWALL_BACKUP_FILE" > "$FIREWALL_BACKUP_FILE.md5"
if [ $? -eq 0 ]; then
echo "成功: MD5 校验和已保存到 $FIREWALL_BACKUP_FILE.md5"
else
echo "警告: MD5 校验和保存失败。"
fi
else
echo "错误: 防火墙配置备份失败。"
fi
}

# 恢复防火墙配置
restore_firewall() {
echo "======================================"
echo "从备份恢复防火墙配置"
echo "======================================"

# 列出现有的防火墙备份文件
echo "当前备份目录中的防火墙备份文件:"
ls -lh $FIREWALL_BACKUP_DIR/firewall-backup-*.bak 2>/dev/null || { echo "没有找到防火墙备份文件。"; return; }

# 提示用户选择恢复的备份文件
echo -n "请输入要恢复的防火墙备份文件名(例如 firewall-backup-20250106.bak),或输入 'n'/'N' 取消: "
read BACKUP_FILE_NAME

if [ "$BACKUP_FILE_NAME" = "n" ] || [ "$BACKUP_FILE_NAME" = "N" ]; then
echo "恢复操作已取消。"
return
fi

# 定义备份文件路径
BACKUP_FILE="$FIREWALL_BACKUP_DIR/$BACKUP_FILE_NAME"
MD5_FILE="$BACKUP_FILE.md5"

# 检查备份文件是否存在
if [ ! -f "$BACKUP_FILE" ]; then
echo "错误: 备份文件 $BACKUP_FILE 不存在。"
return
fi

# 校验 MD5
if [ -f "$MD5_FILE" ]; then
echo "正在校验 MD5 校验和..."
md5sum -c "$MD5_FILE"
if [ $? -ne 0 ]; then
echo "错误: MD5 校验和不匹配,文件可能已损坏。请检查备份文件。"
return
else
echo "成功: MD5 校验和匹配。"
fi
else
echo "警告: 未找到 MD5 校验和文件,无法进行校验。"
fi

# 提示用户是否继续恢复
echo -n "警告: 恢复操作将覆盖当前防火墙配置,确定恢复? [y/N]: "
read CONFIRMATION

if [ "$CONFIRMATION" != "y" ] && [ "$CONFIRMATION" != "Y" ]; then
echo "恢复操作已取消。"
return
fi

# 备份当前防火墙配置
CURRENT_BACKUP_FILE="$FIREWALL_BACKUP_DIR/firewall-current-backup-$CURRENT_DATE.bak"
echo "备份当前防火墙配置到 $CURRENT_BACKUP_FILE..."
cp "$FIREWALL_CONFIG" "$CURRENT_BACKUP_FILE"

if [ $? -ne 0 ]; then
echo "错误: 无法备份当前防火墙配置。"
return
fi

echo "当前防火墙配置已备份到 $CURRENT_BACKUP_FILE"

# 恢复防火墙配置
echo "恢复防火墙配置..."
cp "$BACKUP_FILE" "$FIREWALL_CONFIG"

if [ $? -eq 0 ]; then
echo "成功: 防火墙配置已从 $BACKUP_FILE 恢复。"
else
echo "错误: 防火墙配置恢复失败。"
return
fi

# 重启防火墙服务以应用更改
echo "重启防火墙服务以应用更改..."
/etc/init.d/firewall restart

if [ $? -eq 0 ]; then
echo "成功: 防火墙服务已重启并应用恢复的配置。"
else
echo "错误: 防火墙服务重启失败。"
fi
}

# 挂载 USB 设备
mount_usb() {
echo "======================================"
echo "挂载 USB 设备"
echo "======================================"

# 显示当前已挂载的设备
echo "当前已挂载的设备:"
mount | grep "^/dev"

# 显示可用的 USB 设备
echo "可用的 USB 设备:"
lsblk -o NAME,SIZE,TYPE,MOUNTPOINT | grep -E 'sd|mmc|usb|sda|sdb|sdc|sdd'

# 提示用户输入要挂载的设备名
echo -n "请输入要挂载的设备名(例如 sda1),或输入 'n'/'N' 取消: "
read DEVICE_NAME

if [ "$DEVICE_NAME" = "n" ] || [ "$DEVICE_NAME" = "N" ]; then
echo "挂载操作已取消。"
return
fi

DEVICE_PATH="/dev/$DEVICE_NAME"

# 检查设备是否存在
if [ ! -b "$DEVICE_PATH" ]; then
echo "错误: 设备 $DEVICE_PATH 不存在。"
return
fi

# 提示用户输入挂载点路径
echo -n "请输入挂载点路径(例如 /mnt/usb1-2): "
read MOUNT_POINT

# 创建挂载点目录(如果不存在)
if [ ! -d "$MOUNT_POINT" ]; then
mkdir -p "$MOUNT_POINT"
if [ $? -ne 0 ]; then
echo "错误: 无法创建挂载点目录 $MOUNT_POINT。"
return
fi
fi

# 检查设备是否已挂载
mount | grep "$DEVICE_PATH" > /dev/null
if [ $? -eq 0 ]; then
echo "设备 $DEVICE_PATH 已挂载。"
return
fi

# 挂载设备
echo "挂载设备 $DEVICE_PATH$MOUNT_POINT..."
mount "$DEVICE_PATH" "$MOUNT_POINT"

if [ $? -eq 0 ]; then
echo "成功: 设备 $DEVICE_PATH 已挂载到 $MOUNT_POINT"
else
echo "错误: 设备 $DEVICE_PATH 挂载失败。"
fi
}

# 主菜单循环
while true; do
show_menu
read OPTION

case $OPTION in
1)
backup_openwrt
;;
2)
restore_openwrt
;;
3)
backup_openwrt_config
;;
4)
restore_openwrt_config
;;
5)
backup_iptables
;;
6)
restore_iptables
;;
7)
backup_firewall
;;
8)
restore_firewall
;;
9)
mount_usb
;;
0)
echo "退出脚本"
exit 0
;;
*)
echo "无效的选项,请重新选择!"
;;
esac

echo "按回车键继续..."
read
done