Fritz! Box 7490: Cara mengunggah file konfigurasi yang dimodifikasi

11

Saya memiliki Fritz! Box 7490 perumahan DSL / Modem / Router oleh AVM.

Satu dapat membuang konfigurasi saat ini router sebagai file (di bawah "System> Backup> Save Tab"). Hasilnya adalah file teks terstruktur sederhana, bukan JSON, tetapi hampir begitu (Keputusan desain yang sangat baik, AVM!)

Saya ingin memodifikasinya secara tidak konvensional dan mengunggahnya lagi untuk mengubah konfigurasi router.

Apa yang ingin saya modifikasi? Pertama, mengosongkan cache DHCP yang sekarang sangat besar - melakukan ini melalui antarmuka membutuhkan waktu lama (berjam-jam mengklik). Kedua, memodifikasi server DNS yang diteruskan ke klien DHCP pada LAN, opsi servercfg.user_dns1_for_ipv4dan servercfg.user_dns2_for_ipv4, yang tidak dapat diakses melalui antarmuka 7490.

Namun, fasilitas unggah konfigurasi router ("System> Backup> Restore Tab") memverifikasi integritas file. Tampaknya itu menghitung checksum dari konten dan memverifikasi ini terhadap checksum yang tertanam dalam file untuk diunggah. Ini adalah baris berikut di bagian paling akhir:

**** END OF EXPORT 0428BE3C ****

Jika ada ketidaksesuaian, unggahan ditolak dengan "File yang ditentukan bukan file impor yang valid." (Jika ada kecocokan, unggahan diterapkan tanpa basa-basi lagi dan router dinyalakan kembali. Aduh!)

Dalam model 7390, seseorang dapat menonaktifkan pemeriksaan integritas dengan menambahkan NoChecks=yessebelum string intro **** CFGFILE:ar7.cfgdi bagian paling atas (lihat misalnya Mengubah DNS pada Fritzbox Anda ) Ini tampaknya tidak lagi berfungsi di 7490 (terlalu banyak orang yang merusak perangkat mereka?)

Apakah ada solusi lain untuk membuat POKE versi modern ini berfungsi?

David Tonhofer
sumber
Seorang kolega sebenarnya telah menemukan algoritma perhitungan checksum di Fritz! Box VBScripte . Hmmmm ... saatnya menguji ini (dan menulis ulang untuk Perl?)
David Tonhofer
Anda juga dapat menerima pertanyaan yang Anda jawab sendiri.
peterh
Apakah mungkin untuk mengatur pengaturan yang ditolak oleh UI? Misalnya, menggunakan seluruh ruang Unicode di SSID?
nikeee
@nikeee, aku tidak tahu. Sangat mungkin.
David Tonhofer

Jawaban:

12

Berikut adalah skrip yang tampaknya benar menghitung checksum, yang ternyata merupakan checksum CRC32. Pada akhirnya, saya tidak berhasil menghapus cache DHCP dari Fritz! Box via dump / memodifikasi / mengembalikan konfigurasi. Mendesah.

#!/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"
}
David Tonhofer
sumber
1
Menguji skrip dengan Fritz! Box 7390 saya dan dapat mengunggah konfigurasi yang dimodifikasi.
galaksi
alat yang luar biasa! mengkonfirmasikan (seperti halnya galaksi) masih bekerja pada 7390. Firmware saya: 6.30 :)
Madivad
3
Periksa versi python (termasuk win32 exe gui yang sudah dibangun sebelumnya) di github.com/mementum/fritzchecksum
mementum
1
@ David benar-benar baik, terima kasih banyak. Saya dapat mengkonfirmasi bahwa ini sedang bekerja pada LAB-BETA (6.69) saat ini dari F! Box
7490