看過幾篇教程之后我已經知道怎么備份固件了。但是現在有一個問題,我的本意是把K2P原機帶的固件備份出來,用教程上的方法進行開啟telnet、備份固件等操作是否會改變固件呢?下面我們來驗證這個問題。
OpenWrt的文件系統
K2P使用的是OpenWrt系統,我們先來看一下K2P的Flash Layout(圖片來自恩山)
圖片上的 firmware 就是我要的固件,它分成 Kernel 和 rootfs 兩部分。其中 rootfs 使用SquashFS和JFFS2實現了一個可讀寫的文件系統,但實際上SquashFS是只讀的壓縮包,所有的文件修改都是記錄在JFFS2,也就是rootfs_data上的。
感興趣的話可以看看:
https://wiki.openwrt.org/doc/techref/filesystems
https://blog.csdn.net/lee244868149/article/details/57076615
路由器重置的原理
我們知道所有的文件修改都是記錄在 rootfs_data 上的,系統重置實際上就是將這部分數據抹掉,我們來驗證一下。
我之前下了一個網友備份的固件,地址。
打開 "/usr/lib/lua/luci/controller/admin/backuprestore.lua" 文件,查看第136~141行。
136 elseif reset_avail and luci.http.formvalue("reset") then 137 luci.template.render("backuprestore", { 138 reset_process = 1 139 }) 140 fork_exec("sleep 3; killall dropbear lighttpd miniupnpd; sleep 3; mtd -r erase rootfs_data") 141 else
mtd -r erase rootfs_data 這一句就是抹掉 rootfs_data 的數據。
通過配置文件開啟telnet的原理
我們來看一下恢復配置文件是怎么實現的。
還是 "/usr/lib/lua/luci/controller/admin/backuprestore.lua" 這個文件,查看第81~136行。
81 elseif luci.http.formvalue("restore") then 82 local fs = require("luci.fs") 83 luci.http.formvalue("filename") 84 --校驗配置文件 85 luci.util.exec("encryconfig decrypt /tmp/backupFile_encode /tmp/backupFile") 86 nixio.fs.unlink("/tmp/backupFile_encode") 87 local fd = io.open("/tmp/backupFile", r) 88 local restore_error_message 89 if fd ~= nil then 90 local line = fd:read() 91 fd:close() 92 if line ~= nil then 93 if not checkfwversion() then 94 nixio.fs.unlink("/tmp/backupFile") 95 restore_error_fwversion = {"restore_error"} 96 luci.template.render("backuprestore", { 97 restore_error_fwversion = restore_error_fwversion 98 }) 99 else 100 luci.util.exec("sed 1,10d /tmp/backupFile >/tmp/restore_rm_header") 101 luci.util.exec("tar -xzC/ -f /tmp/restore_rm_header") 102 nixio.fs.unlink("/tmp/restore_rm_header") 103 local cur_lan_mac = luci.util.exec("uci get network.lan.macaddr") 104 local cur_wan_mac = luci.util.exec("uci get network.wan.macaddr") 105 local flash_lan_mac = luci.util.exec("eth_mac r lan") 106 local flash_wan_mac = luci.util.exec("eth_mac r wan") 107 if cur_lan_mac ~= flash_lan_mac then 108 luci.util.exec("uci set network.lan.macaddr=%s" % flash_lan_mac) 109 end 110 if cur_wan_mac ~= flash_wan_mac then 111 luci.util.exec("uci set network.wan.macaddr=%s" % flash_wan_mac) 112 end 113 luci.util.exec("uci commit") 114 local upload = luci.http.formvalue("restore") 115 if upload and #upload > 0 then 116 luci.template.render("backuprestore", { 117 restore_avail = 1 118 }) 119 fork_exec("sleep 3; reboot") 120 end 121 end 122 else 123 restore_error_message = {"restore_error"} 124 nixio.fs.unlink("/tmp/backupFile") 125 luci.template.render("backuprestore", { 126 restore_error_message = restore_error_message 127 }) 128 end 129 else 130 nixio.fs.unlink("/tmp/backupFile") 131 restore_error_message = {"restore_error"} 132 luci.template.render("backuprestore", { 133 restore_error_message = restore_error_message 134 }) 135 end 136 elseif reset_avail and luci.http.formvalue("reset") then
第101行,tar -xzC/ -f /tmp/restore_rm_header,這條命令把配置文件解壓縮到根目錄下覆蓋現在用的文件。
利用這個特性我們可以向系統寫入我們想要的數據,比如自啟動腳本。
/etc/rc.local
在 "/etc/rc.local" 腳本中加入命令開啟telnet服務是一個不錯的選擇。
修改后的文件如下:
1 # Put your custom commands here that should be executed once 2 # the system init finished. By default this file does nothing. 3 4 case `cat /proc/cpuinfo | grep MT76` in 5 *7621*) 6 CONFIG_RALINK_MT7621=y 7 ;; 8 *7623*) 9 CONFIG_ARCH_MT7623=y 10 ;; 11 esac 12 if [ "$CONFIG_RALINK_MT7621" = "y" ]; then 13 echo 2048 > /proc/sys/vm/min_free_kbytes 14 #echo 2 > /proc/sys/vm/overcommit_memory 15 #echo 50 > /proc/sys/vm/overcommit_ratio 16 fi 17 block mount 18 19 # add nat rule manually 20 21 smp.sh wifi 22 hwnat-enable.sh 23 brctl addif br-lan ra0 24 brctl addif br-lan rax0 25 /usr/sbin/telnetd -l /bin/login.sh 26 exit 0
第25行 /usr/sbin/telnetd -l /bin/login.sh 就是開啟服務的命令。注意,在這個命令中要寫完整路徑名。
打包配置文件
生成配置文件的代碼在 "/sbin/sysupgrade" 腳本里,第131~174行。
1 do_save_conffiles() { 2 local conf_tar="${1:-$CONF_TAR}" 3 4 if [ -z "$CONF_BACKUP" ]; then 5 platform_config_prepare 6 fi 7 8 [ -z "$(rootfs_type)" ] && { 9 echo "Cannot save config while running from ramdisk." 10 ask_bool 0 "Abort" && exit 11 return 0 12 } 13 run_hooks "$CONFFILES" $sysupgrade_init_conffiles 14 ask_bool 0 "Edit config file list" && vi "$CONFFILES" 15 16 # v "Saving config files..." 17 [ "$VERBOSE" -gt 1 ] && TAR_V="v" || TAR_V="" 18 tar c${TAR_V}zf "$conf_tar" -T "$CONFFILES" 19 20 if [ -n "$CONF_BACKUP" ]; then 21 #fix project name must be first line 22 #phic_fac -g product > /tmp/backup_add_header 23 #fix project hardware info must be second line 24 #phic_fac -g hw_ver >> /tmp/backup_add_header 25 #fix project software version must be third line 26 #phic_fac -g fw_ver >> /tmp/backup_add_header 27 #we leave here 7 blank lines,so we have total 10 lines include above 3 lines at the top of /tmp/backup_2 28 echo "product=`uci get system.system.hostname`" >> /tmp/backupFile 29 echo "hw_ver=`uci get system.system.hw_ver`" >> /tmp/backupFile 30 echo "fw_ver=`uci get system.system.fw_ver`" >> /tmp/backupFile 31 for i in 4 5 6 7 8 9 10 32 do 33 echo "" >> /tmp/backupFile 34 done 35 cat $conf_tar >> /tmp/backupFile 36 encryconfig encrypt /tmp/backupFile /tmp/backupFile_encode 37 #cat /tmp/backup_add_header_encry 2>/dev/null 38 39 #rm -f "/tmp/backup_add_header" 40 #rm -f "/tmp/backup_add_header_encry" 41 rm -f "$conf_tar" 42 fi 43 rm -f "$CONFFILES" 44 }
配置文件由文件頭和一個tar包組成。文件頭是10行文本,這個可以在備份出的文件中截取。在tar包里放我們想寫入的文件,與文件頭合並成在一起。最后用openssl加密就可以了。