Sunday, August 21, 2011

firmware reversing Axis network camera AXIS M1031-W

After obtaining the firmware from axis website I went ahead with binwalk to identify the sections in the file,
   1: root@system:~/bin/binwalk-0.3.7/src# binwalk AXIS_M1031-W.bin 
   2:  
   3: DECIMAL         HEX             DESCRIPTION
   4: -------------------------------------------------------------------------------------------------------
   5: 12165           0x2F85          LZMA compressed data, properties: 0x02, dictionary size: 8388608 bytes, uncompressed size: 1073741824 bytes
   6: 12189           0x2F9D          LZMA compressed data, properties: 0x02, dictionary size: 8388608 bytes, uncompressed size: 1073741824 bytes
   7: 12213           0x2FB5          LZMA compressed data, properties: 0x02, dictionary size: 8388608 bytes, uncompressed size: 1073741824 bytes
   8: 68693           0x10C55         LZMA compressed data, properties: 0x02, dictionary size: 8388608 bytes, uncompressed size: 1073741824 bytes
   9: 68717           0x10C6D         LZMA compressed data, properties: 0x02, dictionary size: 8388608 bytes, uncompressed size: 1073741824 bytes
  10: 68741           0x10C85         LZMA compressed data, properties: 0x02, dictionary size: 8388608 bytes, uncompressed size: 1073741824 bytes
  11: 82110           0x140BE         LZMA compressed data, properties: 0x00, dictionary size: 8388608 bytes, uncompressed size: 192 bytes
  12: 95704           0x175D8         gzip compressed data, from Unix, last modified: Mon Oct 25 14:03:18 2010, max compression
  13: 2899809         0x2C3F61        LZMA compressed data, properties: 0xE0, dictionary size: 8912896 bytes, uncompressed size: 8912896 bytes
  14: 5390381         0x52402D        LZMA compressed data, properties: 0xAF, dictionary size: 9371648 bytes, uncompressed size: 9371648 bytes
  15: 7143469         0x6D002D        LZMA compressed data, properties: 0x9E, dictionary size: 28246016 bytes, uncompressed size: 28246016 bytes
  16: 7421997         0x71402D        LZMA compressed data, properties: 0xBD, dictionary size: 48693248 bytes, uncompressed size: 48693248 bytes
  17: 7782445         0x76C02D        LZMA compressed data, properties: 0xAF, dictionary size: 2555904 bytes, uncompressed size: 2555904 bytes
  18: 8212417         0x7D4FC1        LZMA compressed data, properties: 0x80, dictionary size: 268435456 bytes, uncompressed size: 268435456 bytes
  19: 8216581         0x7D6005        LZMA compressed data, properties: 0x90, dictionary size: 268435456 bytes, uncompressed size: 268435456 bytes
  20: 8220745         0x7D7049        LZMA compressed data, properties: 0xA0, dictionary size: 262144000 bytes, uncompressed size: 262144000 bytes
  21: 8224813         0x7D802D        LZMA compressed data, properties: 0xAF, dictionary size: 6291456 bytes, uncompressed size: 6291456 bytes
  22: 8224977         0x7D80D1        LZMA compressed data, properties: 0xB0, dictionary size: 268435456 bytes, uncompressed size: 268435456 bytes
  23: 8229141         0x7D9115        LZMA compressed data, properties: 0xC0, dictionary size: 268435456 bytes, uncompressed size: 268435456 bytes
  24: 8233305         0x7DA159        LZMA compressed data, properties: 0xD0, dictionary size: 268435456 bytes, uncompressed size: 268435456 bytes
  25: 8237469         0x7DB19D        LZMA compressed data, properties: 0xE0, dictionary size: 239861760 bytes, uncompressed size: 239861760 bytes
  26: 9443925         0x901A55        Zip archive data,  at least v2.0 to extract
  27: 9444533         0x901CB5        Zip archive data,  at least v2.0 to extract
  28: 9449893         0x9031A5        Zip archive data,  at least v2.0 to extract
  29: 9450294         0x903336        Zip archive data,  at least v2.0 to extract
  30: 9470202         0x9080FA        Zip archive data,  at least v2.0 to extract
  31: 9471043         0x908443        Zip archive data,  at least v2.0 to extract
  32: 9484593         0x90B931        Zip archive data,  at least v2.0 to extract
  33: 9485547         0x90BCEB        Zip archive data,  at least v2.0 to extract
  34: 9486419         0x90C053        Zip archive data,  at least v2.0 to extract
  35: 9497953         0x90ED61        Zip archive data,  at least v2.0 to extract
  36: 9499916         0x90F50C        Zip archive data,  at least v2.0 to extract
  37: 11812909        0xB4402D        LZMA compressed data, properties: 0x8F, dictionary size: 9764864 bytes, uncompressed size: 9764864 bytes
  38: 12894253        0xC4C02D        LZMA compressed data, properties: 0x8F, dictionary size: 4063232 bytes, uncompressed size: 4063232 bytes
  39: 13754341        0xD1DFE5        LZMA compressed data, properties: 0x80, dictionary size: 268435456 bytes, uncompressed size: 268435456 bytes
  40: 13758505        0xD1F029        LZMA compressed data, properties: 0x90, dictionary size: 264241152 bytes, uncompressed size: 264241152 bytes
  41: 13762605        0xD2002D        LZMA compressed data, properties: 0x9F, dictionary size: 4194304 bytes, uncompressed size: 4194304 bytes
  42: 13762737        0xD200B1        LZMA compressed data, properties: 0xA0, dictionary size: 268435456 bytes, uncompressed size: 268435456 bytes
  43: 13766901        0xD210F5        LZMA compressed data, properties: 0xB0, dictionary size: 268435456 bytes, uncompressed size: 268435456 bytes
  44: 13771065        0xD22139        LZMA compressed data, properties: 0xC0, dictionary size: 268435456 bytes, uncompressed size: 268435456 bytes
  45: 13775229        0xD2317D        LZMA compressed data, properties: 0xD0, dictionary size: 241958912 bytes, uncompressed size: 241958912 bytes
  46: 13778989        0xD2402D        LZMA compressed data, properties: 0xDE, dictionary size: 26476544 bytes, uncompressed size: 26476544 bytes
  47: 13779461        0xD24205        LZMA compressed data, properties: 0xE0, dictionary size: 268435456 bytes, uncompressed size: 268435456 bytes
  48: 13986717        0xD56B9D        LZMA compressed data, properties: 0x80, dictionary size: 268435456 bytes, uncompressed size: 268435456 bytes
  49: 13990881        0xD57BE1        LZMA compressed data, properties: 0x90, dictionary size: 67633152 bytes, uncompressed size: 67633152 bytes
  50: 13991981        0xD5802D        LZMA compressed data, properties: 0x94, dictionary size: 200802304 bytes, uncompressed size: 200802304 bytes
  51: 13995113        0xD58C69        LZMA compressed data, properties: 0xA0, dictionary size: 268435456 bytes, uncompressed size: 268435456 bytes
  52: 13999277        0xD59CAD        LZMA compressed data, properties: 0xB0, dictionary size: 268435456 bytes, uncompressed size: 268435456 bytes
  53: 14003441        0xD5ACF1        LZMA compressed data, properties: 0xC0, dictionary size: 268435456 bytes, uncompressed size: 268435456 bytes
  54: 14007605        0xD5BD35        LZMA compressed data, properties: 0xD0, dictionary size: 45350912 bytes, uncompressed size: 45350912 bytes
  55: 14008365        0xD5C02D        LZMA compressed data, properties: 0xD2, dictionary size: 223084544 bytes, uncompressed size: 223084544 bytes
  56: 14011837        0xD5CDBD        LZMA compressed data, properties: 0xE0, dictionary size: 268435456 bytes, uncompressed size: 268435456 bytes
  57: 14794797        0xE1C02D        LZMA compressed data, properties: 0x80, dictionary size: 268435456 bytes, uncompressed size: 268435456 bytes
  58: 14798961        0xE1D071        LZMA compressed data, properties: 0x90, dictionary size: 268435456 bytes, uncompressed size: 268435456 bytes
  59: 14803125        0xE1E0B5        LZMA compressed data, properties: 0xA0, dictionary size: 268435456 bytes, uncompressed size: 268435456 bytes
  60: 14807289        0xE1F0F9        LZMA compressed data, properties: 0xB0, dictionary size: 250609664 bytes, uncompressed size: 250609664 bytes
  61: 14811181        0xE2002D        LZMA compressed data, properties: 0xBE, dictionary size: 17825792 bytes, uncompressed size: 17825792 bytes
  62: 14811521        0xE20181        LZMA compressed data, properties: 0xC0, dictionary size: 268435456 bytes, uncompressed size: 268435456 bytes
  63: 14815685        0xE211C5        LZMA compressed data, properties: 0xD0, dictionary size: 268435456 bytes, uncompressed size: 268435456 bytes
  64: 14819849        0xE22209        LZMA compressed data, properties: 0xE0, dictionary size: 268435456 bytes, uncompressed size: 268435456 bytes

Interesting, so many lzma headers with huge/inaccurate uncompressed sizes and same dictionary size. Something is not correct here and let’s have a closer look on it,


   1: root@system:~/axis# dd if=AXIS_M1031-W.bin bs=1 skip=12165 count=32 2>/dev/null | hexdump -C   
   2: 00000000  02 00 00 80 00 00 00 00  40 00 00 00 00 00 00 52  |........@......R|
   3: 00000010  28 00 10 39 00 00 00 00  02 00 00 80 00 00 00 00  |(..9............|
   4: 00000020
   5: root@system:~/axis# dd if=AXIS_M1031-W.bin bs=1 skip=12189 count=32 2>/dev/null | hexdump -C      
   6: 00000000  02 00 00 80 00 00 00 00  40 00 00 00 00 00 00 69  |........@......i|
   7: 00000010  28 00 10 79 00 00 00 00  02 00 00 80 00 00 00 00  |(..y............|
   8: 00000020
   9: root@system:~/axis# dd if=AXIS_M1031-W.bin bs=1 skip=12213 count=32 2>/dev/null | hexdump -C      
  10: 00000000  02 00 00 80 00 00 00 00  40 00 00 00 00 00 00 80  |........@.......|
  11: 00000010  28 00 10 72 00 00 00 00  02 00 00 80 00 00 00 00  |(..r............|
  12: 00000020
  13: root@system:~/axis# dd if=AXIS_M1031-W.bin bs=1 skip=68693 count=32 2>/dev/null | hexdump -C      
  14: 00000000  02 00 00 80 00 00 00 00  40 00 00 00 00 00 00 94  |........@.......|
  15: 00000010  c0 00 c0 39 00 00 00 00  02 00 00 80 00 00 00 00  |...9............|
  16: 00000020

It seems that binwalk is matching the first bytes at each section to lzma compression magic numbers incorrectly. From this point on and after doing some more tests with the rest of the lzma suggested entries we will assume that binwalk’s analysis is inaccurate on that and we will not take it in consideration.

Removing the lzma entries our table will look more like this:


   1:  
   2: DECIMAL         HEX             DESCRIPTION
   3: -------------------------------------------------------------------------------------------------------
   4: 95704           0x175D8         gzip compressed data, from Unix, last modified: Mon Oct 25 14:03:18 2010, max compression
   5:  
   6: 9443925         0x901A55        Zip archive data,  at least v2.0 to extract
   7: 9444533         0x901CB5        Zip archive data,  at least v2.0 to extract
   8: 9449893         0x9031A5        Zip archive data,  at least v2.0 to extract
   9: 9450294         0x903336        Zip archive data,  at least v2.0 to extract
  10: 9470202         0x9080FA        Zip archive data,  at least v2.0 to extract
  11: 9471043         0x908443        Zip archive data,  at least v2.0 to extract
  12: 9484593         0x90B931        Zip archive data,  at least v2.0 to extract
  13: 9485547         0x90BCEB        Zip archive data,  at least v2.0 to extract
  14: 9486419         0x90C053        Zip archive data,  at least v2.0 to extract
  15: 9497953         0x90ED61        Zip archive data,  at least v2.0 to extract
  16: 9499916         0x90F50C        Zip archive data,  at least v2.0 to extract

Starting with the gzip section, we can verify the signature,


   1: dd if=AXIS_M1031-W.bin bs=1 skip=95704 2>/dev/null | file -     
   2: /dev/stdin: gzip compressed data, from Unix, last modified: Mon Oct 25 14:03:18 2010, max compression

A point of start for the moment there, let’s extract the header of the file first and we will proceed later with the gzip section.


   1: root@system:~/axis/analysis# dd if=AXIS_M1031-W.bin bs=1 count=95704 of=header_before.gzip.section
   2: 95704+0 records in
   3: 95704+0 records out
   4: 95704 bytes (96 kB) copied, 1.86194 s, 51.4 kB/s
   5: root@system:~/axis/analysis# file header_before.gzip.section 
   6: header_before.gzip.section: data
   7: root@system:~/axis/analysis# strings header_before.gzip.section > header.str
   8: root@system:~/axis/analysis# less header.str 
   9: <...>
  10:  
  11: NAND 2GiB 3,3V 8-bit
  12: NAND 2GiB 1,8V 16-bit
  13: NAND 2GiB 3,3V 16-bit
  14: AND 128MiB 3,3V 8-bit
  15:          gUp
  16: 9j"\
  17: 9i2\ 
  18: 40K%
  19:  -- System halted
  20: ran out of input data
  21: Malloc error
  22: Out of memory
  23: incomplete literal tree
  24: incomplete distance tree
  25: bad gzip magic numbers
  26: internal error, invalid method
  27: Input is encrypted
  28: Multi part input
  29: Input has invalid flags
  30: invalid compressed format (err=1)
  31: invalid compressed format (err=2)
  32: out of memory
  33: invalid compressed format (other)
  34: crc error
  35: length error
  36: Uncompressing Linux...
  37:  done, booting the kernel.

We can see that this is the Linux bootloader running in the camera startup process. Few days ago I submitted as bug at the binwalk developers the identification of binary format files. Binwalk was not able to identify binary files properly giving false results, at the time of this writing binwalk has been updated adding a new feature, binary file identification. The results of program now verify what we found earlier,


   1: root@system:~/axis/analysis# binwalk -A bootloader | head
   2:  
   3: DECIMAL         HEX             DESCRIPTION
   4: -------------------------------------------------------------------------------------------------------
   5: 8512            0x2140          ARM function prologue
   6: 8828            0x227C          ARM function epilogue
   7: 9328            0x2470          ARM function prologue
   8: 9336            0x2478          ARM function epilogue
   9: 9380            0x24A4          ARM function prologue
  10: 9392            0x24B0          ARM function epilogue
  11: 17816           0x4598          ARM function prologue
Arm Linux bootloader on the camera.
Proceeding with the section identified as gzip we extract the data until next identified signature.


   1: root@system:~/axis/analysis# dd if=AXIS_M1031-W.bin bs=1 skip=95704 count=9348221 of=gzip.section.gz
   2: 9348221+0 records in
   3: 9348221+0 records out
   4: 9348221 bytes (9.3 MB) copied, 207.353 s, 45.1 kB/s

As mentioned before there is no clear mark on where is the end of the gzip section, a lot of false positives, so I go ahead and extract the section until the Zip first identified area. gunzip here was able to extract the contents with a twist,


   1: root@system:~/axis/analysis# ls -Fal gzip.section.gz
   2: -rw-r--r-- 1 root root 9348221 2011-08-21 09:01 gzip.section.gz
   3: root@system:~/axis/analysis# gunzip gzip.section.gz
   4: gzip: gzip.section.gz: decompression OK, trailing garbage ignored
   5: root@system:~/axis/analysis# ls -Fal gzip.section
   6: -rw-r--r-- 1 root root 2695168 2011-08-20 22:45 gzip.section

From the initial file that was almost 9.3 Mb the extracted contents are nearly 2.7 Mb, something is not correct here either there is a lot of padding in the file or there is something more inside the contents. At this phase I was stuck, I tried different tools with no apparent results, tools that were used are deezee from matasano’s black bag ( www.matasano.com ) , signsrch from Luigi Auriemma and again binwalk.


   1: root@system:~/axis/analysis# ../../src/signsrch gzip.section.gz
   2:  
   3: Signsrch 0.1.6a
   4: by Luigi Auriemma
   5: e-mail: aluigi@autistici.org
   6: web:    aluigi.org
   7:   optimized search function from Andrew http://www.team5150.com/~andrew/
   8:   disassembler engine from Oleh Yuschuk
   9:  
  10: - open file "gzip.section.gz"
  11: - 9348221 bytes allocated
  12: - load signatures
  13: - open file ../../src/signsrch.sig
  14: - 1774060 bytes allocated for the signatures
  15: - 2278 signatures in the database
  16: - start signatures scanning:
  17:  
  18:   offset   num  description [bits.endian.size]
  19:   --------------------------------------------
  20:   0029cf30 31   Adler CRC32 (0x191b3141) [32.le.1024]
  21:   0029d330 33   Adler CRC32 (0x01c26a37) [32.le.1024]
  22:   0029d730 35   Adler CRC32 (0xb8bc6765) [32.le.1024]
  23:  
  24: - 3 signatures found in the file

I found a solution after a while looking at the actual hex code on the file. Using hexdump to convert the file I notice a large section inside filled with a sort of padding,


   1: 00146100  c8 1a 85 c0 c0 16 85 c0  d0 32 70 c0 a0 16 85 c0  |.........2p.....|
   2: 00146110  bc 16 85 c0 56 32 70 c0  a4 16 85 c0 ac 16 85 c0  |....V2p.........|
   3: 00146120  9c 16 85 c0 58 00 70 c0  88 96 84 c0 90 96 84 c0  |....X.p.........|
   4: 00146130  94 16 85 c0 a8 16 85 c0  00 00 00 00 00 00 00 00  |................|
   5: 00146140  00 00 00 00 00 00 00 00  00 00 00 00 ff ff ff ff  |................|
   6: 00146150  ff ff ff ff ff ff ff ff  ff ff ff ff ff ff ff ff  |................|
   7: *
   8: 001fca20  ff ff ff ff ff ff ff ff  85 19 01 e0 30 00 00 00  |............0...|
   9: 001fca30  78 be 3e fa 01 00 00 00  00 00 00 00 02 00 00 00  |x.>.............|
  10: 001fca40  23 6d c5 4c 08 08 00 00  51 ae 45 03 63 c1 56 6c  |#m.L....Q.E.c.Vl|
  11: 001fca50  2e 64 65 76 2e 74 61 72  85 19 02 e0 96 01 00 00  |.dev.tar........|
  12: 001fca60  38 e0 68 15 02 00 00 00  01 00 00 00 a4 81 00 00  |8.h.............|
  13: 001fca70  00 00 00 00 00 f0 00 00  23 6d c5 4c 23 6d c5 4c  |........#m.L#m.L|

Apparently there is a section between locations 001fca20 and 00146150 filed with ff ff ff ff. Few bytes below address 001fca20, there is visible a dev.tar ( possible filesystem data ? ) entry. I cut through the file to verify that gzip compression data are still valid after chopping the tail of that contents.


   1: root@system:~/axis/analysis# dd if=gzip.section.gz bs=1 count=$((0x00146150)) of=until_padding.gz
   2: 1335632+0 records in
   3: 1335632+0 records out
   4: 1335632 bytes (1.3 MB) copied, 36.0775 s, 37.0 kB/s
   5: root@system:~/axis/analysis# gunzip until_padding.gz 
   6: gzip: until_padding.gz: decompression OK, trailing garbage ignored
   7: root@system:~/axis/analysis# l until_padding 
   8: -rw-r--r-- 1 root root 2695168 2011-08-21 09:21 until_padding

Extracted area is giving a file around 1.3 MB of compressed data that expanded is still giving us the same results, trying a bit more and cutting bytes from the end I was able later even to correct the warning from gunzip about trailing garbage.

Further more, strings shows some interesting data on the file like the existence of a jffs2 filesystem and more details to ensure that this is the kernel that we are looking at


   1: root@system:~/axis/analysis# strings until_padding | more
   2: -romE=
   3: (hsqs
   4: 1fs-
   5: UUUU
   6: VUUU
   7: root=/dev/mtdblock3 mem=64M init=/linuxrc rootfstype=jffs2 i2c-argus.sda=15 i2c-argus.scl=14 panic=1
   8: l??b
   9: uncached
  10: buffered
  11: writethrough
  12: <snip>


   1: root@system:~/axis/analysis# strings until_padding | grep kernel
   2: No init found.  Try passing init= option to kernel.
   3: Booting kernel
   4: <4>start_kernel(): bug: interrupts were enabled *very* early, fixing it
   5: <2>start_kernel(): bug: interrupts were enabled early
   6: arch/arm/kernel/process.c
   7: <2>kernel BUG at %s:%d!
   8: Division by zero in kernel.
   9: <2>%s: bad page in kernel page table
  10: <1>Unable to handle kernel %s at virtual address %08lx
  11: kernel/sched_rt.c
  12: kernel/fork.c
  13: <4>Disabling lock debugging due to kernel taint
  14: kernel/exit.c
  15: kernel/softirq.c
  16: kernel/resource.c
  17: kernel/sysctl.c
  18: kernel/timer.c
  19: kernel/workqueue.c
  20: kernel/rcupdate.c
  21: kernel/params.c
  22: kernel/hrtimer.c
  23: kernel/srcu.c
  24: kernel/async.c
  25: kernel/futex.c
  26: kernel/rtmutex.c
  27: kernel/up.c
  28: <4>Please see the file Documentation/feature-removal-schedule.txt in the kernel source tree for more details.
  29: <4>%s: module license '%s' taints kernel.
  30: kernel/irq/manage.c
  31: kernel/irq/chip.c
  32: kernel/irq/devres.c
  33: <4>You cannot use older JFFS2 filesystems with newer kernels
  34: <3>%s %s: uevent: unsupported action-string; this will be ignored in a future kernel version
  35: kernel_max
  36: please mail a stack trace to linux-scsi@vger.kernel.org
  37: <3>%s %s: Could not create connection due to crc32c loading error. Make sure the crc32c module is built as a module or into the kernel
  38: kernel_thread
  39: kernel_execve
  40: pgprot_kernel
  41: kernel_power_off
  42: kernel_halt
  43: kernel_restart
  44: kernel_kobj
  45: prepare_kernel_cred
  46: current_kernel_time
  47: probe_kernel_write
  48: probe_kernel_read
  49: kernel_read
  50: copy_strings_kernel
  51: kernel_sock_shutdown
  52: kernel_sock_ioctl
  53: kernel_sendpage
  54: kernel_setsockopt
  55: kernel_getsockopt
  56: kernel_getpeername

and


   1: root@system:~/axis/analysis# strings until_padding | grep -i linux
   2: root=/dev/mtdblock3 mem=64M init=/linuxrc rootfstype=jffs2 i2c-argus.sda=15 i2c-argus.scl=14 panic=1
   3: Linux version 2.6.31 (madhavi@eater-1) (gcc version 4.3.1 20080521 (prerelease) [gcc-4_3-branch revision 135713] (GCC 4.3.1 Axis release R11/1.1) ) #1 Mon Oct 25 13:03:16 CEST 2010
   4: TERM=linux
   5: include/linux/security.h
   6: Linux
   7: include/linux/gfp.h
   8: please mail a stack trace to linux-scsi@vger.kernel.org
   9: include/linux/skbuff.h
  10: Linux

Getting the data after the kernel entry point near address 001fca20, and adding 8 bytes for cleanup



   1: 001fca20  ff ff ff ff ff ff ff ff  85 19 01 e0 30 00 00 00  |............0...|
   2: 001fca30  78 be 3e fa 01 00 00 00  00 00 00 00 02 00 00 00  |x.>.............|
   3: 001fca40  23 6d c5 4c 08 08 00 00  51 ae 45 03 63 c1 56 6c  |#m.L....Q.E.c.Vl|

using dd again ,


   1: root@system:~/axis/analysis# dd if=gzip.section.gz bs=1 skip=$((0x001fca28)) of=after_kernel
   2: 7264853+0 records in
   3: 7264853+0 records out
   4: 7264853 bytes (7.3 MB) copied, 157.416 s, 46.2 kB/s
file command is giving us now something

   1: root@system:~/axis/analysis# file after_kernel 
   2: after_kernel: Linux jffs2 filesystem data little endian

looking for the common busybox application usually found in embedded installations,


   1: root@system:~/axis/analysis# strings jffs2.fs | grep busybox
   2: busybox
   3: busybox
   4: <snip>

And there is the start of the jffs2 filesystem. Final part, let’s add the zip sections and mount the filesystem. In this part I could had done an easier approach but in my initial assumption the zip sections were unknown sections since I had no knowledge of the jffs2 filesystem’s location.

A bit more slice and dice,


   1: root@system:~/axis/analysis# dd if=AXIS_M1031-W.bin bs=1 skip=9443925 of=Zip.section.parts                  
   2: 8463811+0 records in
   3: 8463811+0 records out
   4: 8463811 bytes (8.5 MB) copied, 158.606 s, 53.4 kB/s
   5: root@system:~/axis/analysis# cat jffs2.fs Zip.section.parts > jffs_and_tail         

Final cleanup of the jffs filesystem,


   1: root@system:~/axis/analysis# hexdump -C jffs_and_tail | tail 
   2: 00dee3d0  e9 ba 6c 54 77 73 5f 75  73 65 72 73 85 19 02 e0  |..lTws_users....|
   3: 00dee3e0  44 00 00 00 1d fb f7 98  b6 02 00 00 01 00 00 00  |D...............|
   4: 00dee3f0  a0 81 00 00 89 00 89 00  00 00 00 00 b2 bf 66 4c  |..............fL|
   5: 00dee400  b2 bf 66 4c b2 bf 66 4c  00 00 00 00 00 00 00 00  |..fL..fL........|
   6: 00dee410  00 00 00 00 00 00 00 00  00 00 00 00 f8 bc df cb  |................|
   7: 00dee420  ff ff ff ff ff ff ff ff  ff ff ff ff ff ff ff ff  |................|
   8: *
   9: 00f00000  31 37 30 20 20 20 20 20  20 20 20 20 20 20 32 36  |170           26|
  10: 00f00010  39 32 34 30 30 39 34 31                           |92400941|
  11: 00f00018
  12: root@system:~/axis/analysis# dd if=jffs_and_tail bs=1 count=$((00dee420)) of=jffs_clear

In order to mount the jffs2 we have two ways to do that, either

  1. Have a block device emulate a Memory Technology Device (MTD) via block2mtd. or
  2. Have kernel memory emulate a MTD via mtdram.

I choose the second way


   1: root@system:~/axis/analysis# mknod /tmp/mtdblock1 b 31 0
   2: root@system:~/axis/analysis# modprobe mtdblock
   3: root@system:~/axis/analysis# modprobe mtdram total_size=196608 erase_size=256
   4: root@system:~/axis/analysis# dd if=jffs_clear of=/tmp/mtdblock1
   5: root@system:~/axis/analysis# mount -t jffs2 /tmp/mtdblock1 /media/jffs2
   6: root@system:~/axis/analysis# cd /media/jffs2/
   7: root@system:/media/jffs2# ls -Fal
   8: total 97
   9: drwxr-xr-x 15 root root     0 1970-01-01 02:00 ./
  10: drwxr-xr-x  5 root root  4096 2011-08-21 14:52 ../
  11: drwxr-xr-x  2 root root     0 2010-10-25 14:43 bin/
  12: drwxr-xr-x  5 root root     0 2010-10-25 14:42 dev/
  13: -rw-r--r--  1 root root 61440 2010-10-25 14:42 .dev.tar
  14: lrwxrwxrwx  1 root root    13 2010-10-25 14:42 etc -> mnt/flash/etc
  15: drwxr-xr-x  7 root root     0 2010-10-25 14:43 lib/
  16: drwxr-xr-x  3 root root     0 2010-10-25 14:43 libs/
  17: -rwxr-xr-x  1 root root  1770 2010-10-25 14:30 linuxrc*
  18: drwxr-xr-x  8 root root     0 2010-10-25 14:43 mnt/
  19: drwxr-xr-x  2 root root     0 2010-10-25 14:42 proc/
  20: drwx------  2 root root     0 2010-10-25 14:42 root/
  21: drwxr-xr-x  2 root root     0 2006-03-17 13:04 sbin/
  22: drwxr-xr-x  2 root root     0 2010-10-25 14:42 share/
  23: drwxr-xr-x  2 root root     0 2010-10-25 14:42 sys/
  24: lrwxrwxrwx  1 root root     7 2010-10-25 14:42 tmp -> var/tmp
  25: drwxr-xr-x 11 root root     0 2010-10-25 14:42 usr/
  26: drwxr-xr-x  2 root root     0 2010-10-25 14:42 var/
  27: -rw-r--r--  1 root root 30720 2010-10-25 14:42 .var.tar
  28: root@system:/media/jffs2# 

Until next time !

Html files and cgi content for further analysis, axis

11 comments:

  1. Hi Nicolas,

    I hope you still posting about reverse engineering.

    I'm also reverse engineering an IP Camera (VStarcam H6837WI): http://acassis.wordpress.com/category/ipcam/

    At this moment I already discover how to compile and flash a linux kernel and file system on it. Now I'm trying to understand how video processing is executed by internal DSP (CEVA MM2000) and how to get raw (yuv) camera image from this dsp to do image processing inside this camera.

    []'s

    ReplyDelete
    Replies
    1. Hi,
      Always happy to see comments here. I will start posting again soon more on reverse engineering and firmware reversing.

      When you will find the way with the IP Camera it will be nice to share your findings with others through a blog post. Let me know your blog address when you are ready and I will make a comment also in mine.

      Nicolas

      Delete
  2. Hi,
    Very interesting post!
    Can you please reupload a file http://chaos.deventum.com/axis.html.tar.gz ?
    Link is broken... i would like to take a look since I have a project with that camera.
    Did you tried to modify firmware and install it on camera?

    Thanks in advance!

    ReplyDelete
  3. Hi, the proper link is http://chaos.deventum.com/research/axis.html.tar.gz. I didn't try for that one but I don't think it's much of a problem.

    ReplyDelete
  4. Hello,

    Using your methodology, i successfully get access to a linux kerne, but can get access to html & cgi files.

    Did you met this kind of issue ?

    Best regards
    Cedric Baillet

    ReplyDelete
  5. Hi Cedric,
    You extracted a different partition there is a jffs2 partition with the data.

    Regards,
    Nicolas

    ReplyDelete
  6. Nicolas, I am having trouble reproducing your steps. Binwalk identifies the start of the JFFS2 block now, but when I carve it out and mount, using your steps, I get a mangled filesystem with directories working, but corrupt files. Some of the files are text files with binaries in the middle of them. Have you seen this before? I think I am using the same Axis firmware as well: M1031-W_5_20_3.bin. I am doing this on the most recent kali. Do you think there are MTD settings in the kernel that are munging the filesystem?

    ReplyDelete
  7. Hi,
    Try to use the latest binwalk also you might want to take a look at firmware mod kit, https://code.google.com/p/firmware-mod-kit/.

    Check if you have the same results

    ReplyDelete
  8. Thanks for response, yes, latest binwalk and fmk (btw, fmk does not extract the firmware). I am using updated kali as well. I did notice that the Binary Analysis Toolkit BAT does extract the firmware, but it uses jffs2dump and then walks that mapping to extract the files... it is missing some directories as well...but at least the files are there, that it does extract. Would it be possible for you to check your steps on kali, so I know if I am nuts or not... I appreciate any feedback you can give, I would like to simply carve out the JFFS2 portion of the firmware and mount it through MTD but that is not working... again, files are missing and or corrupted.

    ReplyDelete
  9. Shard’s IP Camera Installation, wireless cameras and cloud cameras allow you set up a security system without the need to run wires around your home or business.

    And

    We provide Phone Systems to businesses across the UK, working with world-class manufacturers to offer a full range of telephony systems that can be tailored to your needs. We cater for businesses of any size, and offer a diverse product range from basic two-user phone systems through to multi-feature systems.

    ReplyDelete

  10. Nice post!!Thanks for sharing...While searching i had come across one site that provides both wired and wireless design network camera at an affordable price...

    ReplyDelete