我有一个弗里茨!盒子 7490AVM 生产的住宅 DSL/调制解调器/路由器。
可以将路由器的当前配置转储为文件(在“系统 > 备份 > 保存选项卡”下)。结果是一个简单的结构化文本文件,不是 JSON,但几乎是(出色的设计决策,AVM!)
我想非常规地修改它并再次上传以改变路由器的配置。
我想要修改什么?首先,清空现在非常大的 DHCP 缓存 - 通过界面执行此操作需要很长时间(点击数小时)。其次,修改传递给 LAN 上的 DHCP 客户端的 DNS 服务器、选项 servercfg.user_dns1_for_ipv4
和servercfg.user_dns2_for_ipv4
,这些选项无法通过 7490 接口访问。
但是,路由器的配置上传功能(“系统 > 备份 > 恢复选项卡”)会验证文件的完整性。显然,它会根据内容计算校验和,并将其与要上传的文件中嵌入的校验和进行验证。这是最后一行:
**** END OF EXPORT 0428BE3C ****
如果不匹配,则拒绝上传,并显示“指定的文件不是有效的导入文件。”(如果匹配,则立即应用上传并重新启动路由器。哎哟!)
在 7390 型号中,可以通过在最顶部的NoChecks=yes
介绍字符串前添加来禁用完整性检查**** CFGFILE:ar7.cfg
(例如,参见更改 Fritzbox 上的 DNS)这显然在 7490 上不再起作用(太多人损坏他们的设备?)
有没有其他解决方法,使这个现代版本的戳工作?
答案1
这是一个似乎可以正确计算校验和的脚本,结果显示是 CRC32 校验和。最后,我没能通过转储/修改/恢复配置来清除 Fritz!Box 的 DHCP 缓存。唉。
#!/usr/bin/perl
# Read Fritz!Box configuration dump file and compute its checksum using CRC32.
# The problem is only knowing what to checksum exactly, and in this case its not pretty.
# Inspired from the very compact Visual Basic script by Michael Engelke available at
# http://www.mengelke.de/Projekte/FritzBoxVBScript
# The Fritz!Box accepts a modified file where the checksum has been changed
# manually to the output of this program in the last line. I have no idea what
# happens if there is a syntax error anywhere inside the config file, so beware.
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
use strict;
use warnings;
# ---
# Compute CRC start values
# ---
sub build_crc_table() {
my @crctbl = (); # Accepts 256 values
for (my $a = 0; $a < 256; $a++) {
my $c = $a;
for (my $b = 0; $b < 8; $b++) {
my $d = $c & 0x01;
$c = ($c >> 1) & 0x7FFFFFFF;
if ($d) {
$c = $c ^ 0xEDB88320;
}
}
push @crctbl, $c
}
my $print = 0;
if ($print) {
my $i = 0;
foreach my $x (@crctbl) {
print sprintf("CRC table value $i: %08x\n", $x);
$i++
}
}
return @crctbl
}
# ---
# Transform a string into a vector of its ASCII code points
# ---
sub numerize {
my ($str) = @_;
my @res = ();
foreach my $ch (split('',$str)) {
push @res, ord($ch)
}
return @res;
}
# ---
# Transform a hexstring into a vector of its 8-bit values
# ---
sub hexnumerize {
my ($str) = @_;
my @res = ();
my $tmp;
my $i = 0;
foreach my $ch (split('',$str)) {
if ($i == 0) { $tmp = $ch; $i++; }
else { $tmp.= $ch; $i=0; push @res,hex($tmp) }
}
if ($i != 0) {
die "Irregular hex string: $str\n";
}
return @res;
}
# ---
# Compute CRC-32 checksum
# See https://en.wikipedia.org/wiki/Cyclic_redundancy_check
# This must yield 414fa339:
# print sprintf("%08Xi\n", compute_crc32(numerize("The quick brown fox jumps over the lazy dog")));
# ---
sub compute_crc32 {
my(@data) = @_;
my @crctbl = build_crc_table(); # not very efficient on multiple calls
my $crc = 0xFFFFFFFF;
my $i = 0;
foreach my $x (@data) {
my $index = ($crc ^ $x) & 0xFF;
$crc = $crctbl[$index] ^ ($crc >> 8); # ">>" is zero-filling in Perl
$crc = $crc & 0xFFFFFFFF; # format to 32 bit
#if ($i > 98700 && $i < 99000) {
# print sprintf("$i %c : $x => %08X\n",$x,$crc);
#}
$i++
}
return $crc ^ 0xFFFFFFFF
}
# ---
# The name of the configuration file may have been given on the command line.
# If so slurp the file.
# If nothing has been given, slurp STDIN.
# After that, the input can be found in "@lines" (line terminators are still in there)
# ---
my @lines;
if ($ARGV[0]) {
open(my $fh,'<',$ARGV[0]) or die "Could not open file '$ARGV[0]': $!";
@lines = <$fh>;
close($fh)
}
else {
@lines = <>;
}
# ---
# Stateful analysis.
# If there are lines after "END OF EXPORT" we will disregard them
# ---
my $firmware; # will capture firmware version
my $fritzbox; # will capture name string in header
my @data = (); # will capture 8-bit values to be CRC-checksummed later
my $cursum; # will capture the current CRC checksum in the text
# "state" indicates where we are in the file
my $state = 'NOTSTARTED';
# we consider the lines as a stack and push/pop to the stack
@lines = reverse @lines;
while (@lines && $state ne 'END') {
my $line = pop @lines;
if ($state eq 'NOTSTARTED') {
if ($line =~ /^\*{4} (.*?) CONFIGURATION EXPORT/) {
$fritzbox = $1;
$state = 'HEADER'
}
else {
chomp $line;
die "Expected 'CONFIGURATION EXPORT' in NOTSTARTED state, got '$line'"
}
next
}
if ($state eq 'HEADER') {
chomp $line;
if ($line =~ /(\w+)=([\w\$\.]+)$/) {
my $key = $1;
my $val = $2;
if ($key eq 'FirmwareVersion') { $firmware = $val }
push @data,numerize($key);
push @data,numerize($val);
push @data,0;
}
elsif ($line =~ /^\*{4}/) {
$state = 'NEXTSECTION';
push @lines,$line
}
else {
die "Unexpected line in HEADER state: '$line'"
}
next
}
if ($state eq 'NEXTSECTION') {
chomp $line;
if ($line =~ /^\*{4} (?:CRYPTED)?(CFG|BIN)FILE:(\S+)/) {
$state = "INSIDESECTION_$1";
my $secname = $2;
print "Section $line\n";
push @data,numerize($secname);
push @data,0
}
elsif ($line =~ /^\*{4} END OF EXPORT (\w{8}) \*{4}/) {
$state = 'END';
$cursum = $1
}
else {
die "Unexpected line in NEXTSECTION state: '$line'"
}
next
}
if ($state eq 'INSIDESECTION_BIN') {
chomp $line;
if ($line =~ /^\*{4} END OF FILE \*{4}/) {
$state = 'NEXTSECTION'
}
else {
push @data,hexnumerize($line)
}
next
}
if ($state eq 'INSIDESECTION_CFG') {
if ($line =~ /^\*{4} END OF FILE \*{4}/) {
# Something unbelievably dirty: All section-internal linefeeds (and carriage returns)
# are kept as is for checksumming, except for the last one before the "END OF FILE"
if ($data[-1] == ord("\n")) {
pop @data;
if ($data[-1] == ord("\r")) {
pop @data;
}
}
$state = 'NEXTSECTION'
}
else {
# More dirty stuff: Double backspaces are replaced by single ones for some reason.
$line =~ s/\\\\/\\/g;
push @data,numerize($line)
}
next
}
die "Unexpected state $state"
}
if ($state ne 'END') {
die 'Did not find proper end of configuration'
}
if (@lines) {
my $n = @lines;
print "There are still $n lines left that will be disregarded"
}
my $crc = compute_crc32(@data);
my $newsum = sprintf("%08X", $crc);
print "$fritzbox with firmware $firmware\n";
if ($newsum eq $cursum) {
print "Checksum is OK: $cursum\n"
}
else {
print "Found new checksum: $newsum\n";
print "Checksum embedded in file is $cursum\n"
}