During Thanksgiving weekend, I had an interesting challenge.

A friend of mine lost his photos on a xD when transferring them to his PC running the you-know-what-OS. Somehow the disk became "unformatted". He came to me with little hope that those photos can be recovered.

The first thing I thought was that maybe it is just the partition table been destroyed, if I can restore the partition table, which should be relative simple, then we will be good.

Anyway, we needed to get the disk image first. So with my laptop running Nevada build 50, I did following:

$ dd if=/dev/dsk/c3t0d2p0 of=xd.img

Used xxd which is distributed with vim, I peeked into the first sector to see if there is 0x55AA mark. It looked like a directory structure for FAT and of course, 0x55AA was not there. Look further, the FAT table was ruined as well.

Now it would be hard to recover from the file system perspective. Since this is digital camera, most of it would be JPEG files and those are what we care about, and more likely it would be continuous data rather than scatter around the disk. With that assumption, I googled for the JPEG file format, and find this page by TsuruZoh Tachibanaya. With that, I was able to write a simply Ruby script to play with the disk image.

Viola, 567 images are recovered and only 4 of them having problem. Happy Thanksgiving!

For those who might be interested, here is the script. Not perfect but working, and given that I am still learning the language, comments are welcome. :-)

def fn(sno, ext="jpg")
  sprintf("F%06d.%s", sno, ext)
end

def to_hex(x)
  sprintf("%X", x)
end

def find_SOI(f)
  found = false
  until f.eof? || found
    x = f.getc
    if x == 0xff then
      x = f.getc
      if x == 0xd8 then
        found = true
        print "SOI detected at #{to_hex(f.pos - 2)}\n"
      else
        f.ungetc(x)
      end
    end
  end
  found
end
        
def find_EOI(f, of)
  found = false
  until f.eof? || found
    x = f.getc
    of.putc(x)
    if x == 0xff then
      x = f.getc
      if x == 0xd9 then
        found = true
        of.putc(x)
        print "EOI detected at #{to_hex(f.pos - 2)}\n"
      else
        f.ungetc(x)
      end
    end
  end
  found
end
        
def find_SOS(f, of)
  found = false
  until found
    x = f.getc
    if x != 0xff then
      print "Error: Marker expected at #{to_hex(f.pos - 1)}\n"
      return false
    end
    of.putc(x)
    x = f.getc
    of.putc(x)
    print "Marker 0xFF#{to_hex(x)} detected at #{to_hex(f.pos - 2)},"
    if x == 0xda then
      found = true
    end
    x = f.getc
    of.putc(x)
    sz = x << 8
    x = f.getc
    of.putc(x)
    sz |= x
    print " with size #{sz}\n"
    of.write(f.read(sz - 2))
  end
  found
end
  
f = File.open(ARGV[0])
seq = 0
until f.eof?
  rv = find_SOI(f)
  if rv then
    seq += 1
    of=File.open("#{fn(seq)}", "w")
    of.putc(0xff)
    of.putc(0xd8)
    rv = find_SOS(f, of) && find_EOI(f, of)
    of.close
    unless rv then
      File.rename(fn(seq), fn(seq, "dat"))
    end
  end
end
f.close

# vim: set ts=2 sw=2 tw=80 et:
Comments:

Post a Comment:
  • HTML Syntax: NOT allowed

This blog copyright 2009 by slowhog