From 84347869e8531ae2f4dbebf31aa07ebdaf8518da Mon Sep 17 00:00:00 2001 From: zengelan Date: Sun, 16 Aug 2020 13:52:54 +0200 Subject: [PATCH] inject new squashfs into pak file, add startup script Add two features: 1. The unpack-novatek-firmware.pl gets a new -i parameter. With it will inject a new squashfs image into an existing pak firmware update file, so it can be deployed via the web UI, without serial console 2. The repack-reolink-rootfs.sh script now also plant's a startup script that allows to run any commands and scripts from an sd card directory. This allows to make chanhges to the runtime without re-flashing. An example is included where the nginx config is changed to list the directory contents of the sd card an the recordings un html and json --- README.md | 39 ++++++++ mmcscript/S99zz_ssd_script | 10 ++ mmcscript/nginx.conf | 161 +++++++++++++++++++++++++++++++++ mmcscript/nginx_update.sh | 10 ++ mmcscript/ssd_caller_script.sh | 22 +++++ mmcscript/ssd_script.sh | 20 ++++ repack-reolink-rootfs.sh | 18 ++-- unpack-novatek-firmware.pl | 73 +++++++++++++++ 8 files changed, 347 insertions(+), 6 deletions(-) create mode 100644 mmcscript/S99zz_ssd_script create mode 100644 mmcscript/nginx.conf create mode 100644 mmcscript/nginx_update.sh create mode 100644 mmcscript/ssd_caller_script.sh create mode 100644 mmcscript/ssd_script.sh diff --git a/README.md b/README.md index fe16224..1101fcc 100644 --- a/README.md +++ b/README.md @@ -228,6 +228,45 @@ There's something broken within dropbear's key initial exchange (causing a segfa might need to login using `ssh -oHostKeyAlgorithms=ssh-rsa root@ipaddress` for the very first time. I don't have time to debug this odd behaviour. +### Create new firmware update file with the modified rootfs +It is possible to now create a new firmware image file with this new rootfs which can be uploaded to the camera using the UI. +A known issue is that this only works if the original firmware image file has new version designation thet the one running on the camera. +I have not been able to _reflash_ an image file using the UI, and the updater will refuse to flash the same version again. + +With [unpack-novatek-firmware.pl](unpack-novatek-firmware.pl) one can now inject the new rootfs image file into a copy of the original +firware update (.pak) file: + +``./unpack-novatek-firmware.pl -i mtdblock6-NEW.bin IPC_51516M5M.65_20071000.RLC-410-5MP.OV05A10.5MP.REOLINK.pak`` + +The `-i` parameter takes the filename of the modified rootfs image, here `mtdblock6-NEW.bin`. The specify the original firmware (pak) file. +A new filename will be automartically generated, as the updater on these cameras (also) parses the filename to identify the version of +the image. +Example output: +``` +... +Going to inject the file 'mtdblock6-NEW.bin' into the image 'IPC_51516M5M.65_20071000.RLC-410-5MP.OV05A10.5MP.REOLINK.pak' + the output file will be named 'IPC_51516M5M.65_20071001.RLC-410-5MP.OV05A10.5MP.REOLINK.pak' + cancel now if this is not desired or press enter + +... + +Image File Section 6 name: fs +Image File Section 6 version: v1.0.0.1 +Image File Section 6 offset: 5164017 +Image File Section 6 length: 7512064 +this is the filesystem part that we want to replace + read 7077888 bytes from newimage 'mtdblock6-NEW.bin' to var newimagedata + the old fs image had 7512064 (0x72A000)(00a07200) bytes, the new one has 7077888 (0x6 +C0000)(00006c00) bytes + Combining the first 0x4EC5E1 bytes from the original image + with the contents of the new input file +the new CRC would be: 1ff31200 +Writing output file 'IPC_51516M5M.65_20071001.RLC-410-5MP.OV05A10.5MP.REOLINK.pak' + +... + +``` + Enjoy logging in to your camera with SSH. ### Firmware versions diff --git a/mmcscript/S99zz_ssd_script b/mmcscript/S99zz_ssd_script new file mode 100644 index 0000000..4cf1ee4 --- /dev/null +++ b/mmcscript/S99zz_ssd_script @@ -0,0 +1,10 @@ +#!/bin/sh + +# start a script from the ssd card, so we are flexible and can add new code changes during runtime +# we need the environment variables here, as these are not defined when this script stats +export LD_LIBRARY_PATH='/lib:/usr/local/lib:/usr/lib:/mnt/app' +export PATH='/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:/bin:/sbin' +export SHELL='/bin/sh' + +echo "Trying to start /mnt/sda/ssd_script.sh through /bin/ssd_caller_script.sh" +/bin/ssd_caller_script.sh & diff --git a/mmcscript/nginx.conf b/mmcscript/nginx.conf new file mode 100644 index 0000000..1bf487c --- /dev/null +++ b/mmcscript/nginx.conf @@ -0,0 +1,161 @@ +user root root; +worker_processes 1; +error_log stderr crit; +pid run/nginx.pid; +#rtmp_auto_push on; + + +events +{ + worker_connections 1024; +} + +rtmp +{ + server + { + listen 1935; + application vod + { + play /mnt/sda/; + #on_play http://127.0.0.1:80/api.cgi?rtmp=auth; + on_play http://127.0.0.1:80/api.cgi?rtmp=start; + on_play_done http://127.0.0.1:80/api.cgi?rtmp=stop; + #record video; + #record_path /tmp; + #record_max_size 128K; + #record_interval 30s; + #record_suffix .this.is.flv; + + #on_publish http://localhost:8080/publish; + #on_play http://localhost:8080/play; + #on_record_done http://localhost:8080/record_done; + } + application live + { + live on; + on_play http://127.0.0.1:80/api.cgi?rtmp=start; + on_play_done http://127.0.0.1:80/api.cgi?rtmp=stop; +# record video; +# record_path /mnt/app/www/; +# record_suffix -%d-%b-%y-%T.flv; + } + application bcs + { + play /mnt/app/www/; + #on_play http://127.0.0.1:80/api.cgi?rtmp=auth; + on_play http://127.0.0.1:80/api.cgi?rtmp=start; + on_play_done http://127.0.0.1:80/api.cgi?rtmp=stop; + #record video; + #record_path /tmp; + #record_max_size 128K; + #record_interval 30s; + #record_suffix .this.is.flv; + + #on_publish http://localhost:8080/publish; + #on_play http://localhost:8080/play; + #on_record_done http://localhost:8080/record_done; + } + + } +} + +http +{ + + include mime.types; + default_type application/octet-stream; + client_max_body_size 32M; + client_body_buffer_size 8k; + client_body_temp_path /mnt/tmp; + access_log off; + sendfile on; + limit_conn_zone $binary_remote_addr zone=one:1m; + add_header X-Frame-Options "SAMEORIGIN"; + #add_header Strict-Transport-Security "max-age=63072000; includeSubdomains; preload"; + add_header X-XSS-Protection "1; mode=block"; + add_header X-Content-Type-Options "nosniff"; + server_tokens off; + + server + { + listen 80; + root /mnt/app/www/; + index index.php index.htm index.html; + + location /downloadfile/ { + internal; + limit_conn one 1; + limit_rate 1024k; + alias /mnt/sda/; + } + +### start modification + location /sd + { + autoindex on; + alias /mnt/sda/; + } + + location /sdjson + { + autoindex on; + alias /mnt/sda/; + autoindex_format jsonp; + } +### end modification + + location /stat + { + rtmp_stat all; + rtmp_stat_stylesheet stat.xsl; + } + + location /stat.xsl { + root /mnt/app/www/; + } + + location /control + { + rtmp_control all; + } + location ~ .*\.cgi$ + { + fastcgi_read_timeout 150; + fastcgi_pass 127.0.0.1:9527; + fastcgi_index index.cgi; + include /mnt/app/www/conf/fastcgi_params; + } + } + + server + { + listen 443; + root /mnt/app/www/; + index index.php index.htm index.html; + + ssl on; + ssl_protocols TLSv1.2; + ssl_certificate /mnt/app/www/self.crt; + ssl_certificate_key /mnt/app/www/self.key; + + location /downloadfile/ { + internal; + limit_conn one 1; + limit_rate 1024k; + alias /mnt/sda/; + } + + location /control + { + rtmp_control all; + } + location ~ .*\.cgi$ + { + fastcgi_read_timeout 150; + fastcgi_pass 127.0.0.1:9527; + fastcgi_index index.cgi; + include /mnt/app/www/conf/fastcgi_params; + } + } +} \ No newline at end of file diff --git a/mmcscript/nginx_update.sh b/mmcscript/nginx_update.sh new file mode 100644 index 0000000..657ce0d --- /dev/null +++ b/mmcscript/nginx_update.sh @@ -0,0 +1,10 @@ +#!/bin/sh + +# an example script that can be used with the ssd_script.sh on the sd card +# it overwrites the nginx config file and reloads nginx to pick up the changes + +echo " adding updating nginx.conf" +cp /mnt/sda/script/nginx.conf /mnt/tmp/nginx.conf +echo " copied nginx.conf from /mnt/sda/script/nginx.conf to /mnt/tmp/nginx.conf" +/bin/nginx -s reload -c /mnt/tmp/nginx.conf -p /mnt/tmp/ +echo " told nginx to reload config" diff --git a/mmcscript/ssd_caller_script.sh b/mmcscript/ssd_caller_script.sh new file mode 100644 index 0000000..9667bfa --- /dev/null +++ b/mmcscript/ssd_caller_script.sh @@ -0,0 +1,22 @@ +#!/bin/sh + +# This is the caller script that tries to start /mnt/sda/ssd_script.sh every 3 seconds until it suceeeds +# as the mmc doesn't seem to be mounted when the init scripts are run +SSDSCRIPT=/mnt/sda/script/ssd_script.sh +for i in $(seq 1 21); do + echo "Try number $i to start ${SSDSCRIPT}" + nowstring=$(date) + echo "At ${nowstring} the mount table is:" + mount + if [ -f "$SSDSCRIPT" ]; then + echo "$SSDSCRIPT exists." + chmod 0777 ${SSDSCRIPT} + echo "Calling ${SSDSCRIPT}h" + ${SSDSCRIPT} & + echo "Successfully started ${SSDSCRIPT}, done" + exit 0 + fi + echo "$SSDSCRIPT does not exist, sleeping 3 secs" + echo "" + sleep 3 +done diff --git a/mmcscript/ssd_script.sh b/mmcscript/ssd_script.sh new file mode 100644 index 0000000..e7a77f1 --- /dev/null +++ b/mmcscript/ssd_script.sh @@ -0,0 +1,20 @@ +#!/bin/sh + +# Starting this script from ssd card to allow runtime changes without re-flashing. +# It can used for all sorts of things, e.g. modifying nginx config, kill processes and +# replace them with other binaries from sd card + +# please place this file on the mcc card, in a folder called scripts. +# during runtime it will be available as /mnt/sda/script/ssd_script.sh + + +echo "Starting ssd_script.sh from mmc card" + +echo " ssd_script.sh: Nothing to do today" +# add your code here + +#example: +# echo " updating nginx.conf" +# /mnt/sda/script/nginx_update.sh + +echo "Finished ssd_script.sh from mmc card" diff --git a/repack-reolink-rootfs.sh b/repack-reolink-rootfs.sh index 4e22a42..77c7121 100755 --- a/repack-reolink-rootfs.sh +++ b/repack-reolink-rootfs.sh @@ -33,6 +33,7 @@ MTDFILE=mtdblock6.bin # dump on camera with 'cat /dev/mtdblock6 > /mnt/sda/mtdblock6.bin' CONTRIB=./contrib +MMCSCRIPT=./mmcscript OUTDIR=rootfs OUTFILE=mtdblock6-NEW.bin FRSAVE=fakerootsave.$MTDFILE @@ -49,15 +50,17 @@ echo "DES-encrypted password is: $CPASS" fakeroot -s $FRSAVE unsquashfs -d $OUTDIR $MTDFILE fakeroot -i $FRSAVE -s $FRSAVE cp -v $CONTRIB/dropbear $OUTDIR/usr/sbin/ fakeroot -i $FRSAVE -s $FRSAVE cp -v $CONTRIB/S99dropbear $OUTDIR/etc/init.d/ -fakeroot -i $FRSAVE -s $FRSAVE chown -v --reference=$OUTDIR/etc $OUTDIR/usr/sbin/dropbear $OUTDIR/etc/init.d/S99dropbear +fakeroot -i $FRSAVE -s $FRSAVE cp -v $MMCSCRIPT/S99zz_ssd_script $OUTDIR/etc/init.d/ +fakeroot -i $FRSAVE -s $FRSAVE cp -v $MMCSCRIPT/ssd_caller_script.sh $OUTDIR/bin +fakeroot -i $FRSAVE -s $FRSAVE chown -v --reference=$OUTDIR/etc $OUTDIR/usr/sbin/dropbear $OUTDIR/etc/init.d/S99dropbear $OUTDIR/etc/init.d/S99zz_ssd_script $OUTDIR/bin/ssd_caller_script.sh for LINK in dbclient dropbearconvert dropbearkey scp ssh; do fakeroot -i $FRSAVE -s $FRSAVE ln -sv ../sbin/dropbear $OUTDIR/usr/bin/$LINK fakeroot -i $FRSAVE -s $FRSAVE chown -v -h --reference=$OUTDIR/etc $OUTDIR/usr/bin/$LINK -done -fakeroot -i $FRSAVE -s $FRSAVE chmod 0777 $OUTDIR/usr/sbin/dropbear $OUTDIR/etc/init.d/S99dropbear +done +fakeroot -i $FRSAVE -s $FRSAVE chmod 0777 $OUTDIR/usr/sbin/dropbear $OUTDIR/etc/init.d/S99dropbear $OUTDIR/etc/init.d/S99zz_ssd_script $OUTDIR/bin/ssd_caller_script.sh # remove comment to set root password -#fakeroot -i $FRSAVE -s $FRSAVE perl -i -p -e "s/^(root:)/\${1}${CPASS}/p" $OUTDIR/etc/passwd +fakeroot -i $FRSAVE -s $FRSAVE perl -i -p -e "s/^(root:)/\${1}${CPASS}/p" $OUTDIR/etc/passwd fakeroot -i $FRSAVE ls -l $OUTDIR/etc/init.d $OUTDIR/usr/sbin $OUTDIR/etc/passwd @@ -73,9 +76,12 @@ echo "$OUTFILE file size (65536-byte aligned): $OUTFILEASIZE" echo "Execute the following commands within u-boot:" echo +echo "WARNING: Starting firmware version v3.0.0.65_20071000 the flash layout has changed. If you use an older" +echo "firmware you have to change the start offset from 0x620000 to 0x6e0000 !" +echo echo "fatload mmc 0 0x1000000 mtdblock6-NEW.bin" -echo "sf erase 0x6e0000 $OUTFILEASIZE" -echo "sf write 0x1000000 0x6e0000 $OUTFILEASIZE" +echo "sf erase 0x620000 $OUTFILEASIZE" +echo "sf write 0x1000000 0x620000 $OUTFILEASIZE" echo "reset" echo diff --git a/unpack-novatek-firmware.pl b/unpack-novatek-firmware.pl index f17d20e..ed757c1 100755 --- a/unpack-novatek-firmware.pl +++ b/unpack-novatek-firmware.pl @@ -24,6 +24,8 @@ use strict; my $write = 0; +my $inject = 0; +my $newimage = ""; my @crc32poly = ( 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, @@ -80,16 +82,47 @@ sub crc32calc { return $crc; } +sub newfilename { + # we need to create a new filename as the update process reads the version from the filename, so cheap + my ( $oldfilename ) = @_; + #print("Oldfilename is ".$oldfilename); + $oldfilename =~ /(.*\.\d{2}_)(\d{8})(\..*)/; + my $version = $2; + #print("version is ".$version); + $version += 1; + #print("version is ".$version); + my $newname = $1.$version.$3; + #print("Newname is ".$newname); + return $newname; +} + if ( $ARGV[0] eq "-w" ) { $write++; shift(); } +if ( $ARGV[0] eq "-i" ) { + # if -i is specified, we are going to inject the file specified as into the file + $inject++; + shift(); + $newimage = $ARGV[0]; + shift(); +} + my $f = $ARGV[0]; die() if ( !$f ); open( IF, "<$f" ) || die( "Unable to open input file '$f': " . $! ); binmode(IF); +my $newname = ""; +if ($inject){ + $newname = newfilename($f); + print("Going to inject the file '".$newimage."' into the image '".$f."' \n") ; + print(" the output file will be named '".$newname."' \n"); + print(" cancel now if this is not desired or press enter"); + ; +} + my $binheader; my $binsectiondata; @@ -144,6 +177,46 @@ sub crc32calc { printf("Image File Section %2d offset: %8d\n", $s, $soff ); printf("Image File Section %2d length: %8d\n", $s, $slen ); + if ($inject and $sname eq"fs"){ + print("this is the filesystem part that we want to replace\n"); + open( NF, "<$newimage" ) || die( "Unable to open input file '$newimage': " . $! ); + binmode(NF); + my $newimagedata; + my $len_newimage = read( NF, $newimagedata, ( -s $newimage ) ) ; + close (NF); + my $len_newimage_packed = pack( "V", $len_newimage ); + print(" read ".$len_newimage." bytes from newimage '".$newimage."' to var newimagedata\n"); + printf(" the old fs image had ".$slen." (0x%X)(".unpack( "H*", pack( "V", $slen ) ).") bytes, the new one has ".$len_newimage." (0x%X)(".unpack( "H*", $len_newimage_packed ).") bytes\n",$slen,$len_newimage); + + # the new firmware file will have the following structure + #1. binheader + #2. old contents until the fs part + #3. the new image file + # first build the new binheader, still the with wrong crc: + my $new_binheader = $binheader; + # write the new size of the fs image to the new header + substr( $new_binheader, $offset + 60, 4 ) = $len_newimage_packed; + + # now lets build the payload + printf(" Combining the first 0x%X bytes from the original image\n",$soff-1552); + print(" with the contents of the new input file\n"); + my $payload = substr( $binsectiondata, 0, $soff-1552 ) . $newimagedata; + + # now we need to calculate the crc: + my $newcrc = crc32calc( 0, $payload ); + $newcrc = crc32calc( $newcrc, $btypenulled ); + $newcrc = crc32calc( $newcrc, substr( $new_binheader, 12, 704 ) ); + my $newpccrc = pack( "V*", $newcrc ); + printf( "the new CRC would be: %s\n", unpack( "H*", $newpccrc ) ); + + print "Writing output file '" . $newname . "'\n"; + open( NI, ">$newname" ) || die( "Unable to open output file '$newname': " . $! ); + binmode(NI); + print NI $new_binheader . $payload; + close(NI); + } + + if ( $write && $slen > 0 ) { my $basename = $f; $basename =~ s{^.*/|\.[^.]+$}{}g;