Recover Files From Bad Disk, Part II

Share/Bookmark

If you have tried the steps in my post titled Recover Files from Bad Disk and If Testdisk Doesn't Work , and still can't recover your files... Guess what? Here another way!!! (Man... I am persistent, am I not?)


Okay, here's the tools that we need:
1) dd - convert and copy a file
2) mount - mount a filesystem
3) sleuthkit - file recovery toolkit

General warning. These steps are more complicate compared to the steps I have introduced last time. But it will be a good experience for everyone :)



Step 1) Make a image copy of your disk

This step is important because you don't want to be working on a damaged disk. Moreover, if you're trying to recover deleted files from the disk, the best practice is to limit the operations done on the disk, hence we will make a bit-to-bit copy of the disk somewhere else, somewhere we can play around without tempering with the original disk.

first, install dd tool

sudo apt-get install dd
use dd to make a copy of your disk, use the command mount to find your disk (see If Testdisk Doesn't Work if you forgot how to do it). Now, lets make an exact copy of the disk to our harddisk. I'll be using my HTC phone memory as an example here, and I'll be copying the phone memory content to my hard disk partition called /media/store (you can copy it anywhere you like, just make sure the destination partition have free space at least as big as your disk total size)

sudo dd if=/dev/sdb of=/media/store/mydisk.img bs=512
Explanation: if refers to input from file, of refers to output to file, bs refers to bytes r/w

This step will take a long time if you have a large disk. How long? Let see... if we're copying a disk with memory of 16GB (17,179,869,184 bytes) and we're reading and writing at 512 bytes at a time, we're going to need 33,554,432 cycles for read operation, and another 33,554,432 cycles for write operation, totalling to 67,108,864 cycles!!! Scary? Don't worry... computers these days are fast enough...
It took only 11 minutes for my 4GB harddrive.


Step 2.a) Find and recover single file (skip to Step 2.b to recover all files)

Now we want to find the file we wish to recover. We will be using fls tool included in sleuthkit. Let say I want to recover a PNG image, so this is the command I will run

fls -f fat -r /media/store/mydisk.img | grep png
The flag -f denotes filesystem, which in my case is fat, flag -r means recursive; to tell fls to search inside directories, grep is a filter that tells fls to show only lines with png inside it. You can do without -f flag and grep filter, but it will give you a long list of all files, which we don't want. If you want to display only deleted files, insert -d flag as well ;)

Heres' the result

Now I found my deleted PNG file. Take note the numbers, those are the node numbers for each file.
Next we're going to use icat to copy that file to somewhere else, by using that node number.

icat -f fat -r /media/store/mydisk.img 188449 > shopping.png
Now, my file is recovered and available is my current directory :D


Step 2.b) Recover all files
But of course we don't want to recover each and every file manually. Imagine if we have to type icat -f fat -r (disk image) (node number) > outputfile for every single file in gigabytes of memory disk! Not a pleasant task, isn't it. Don't worry, we're in Linux. We will still be doing the recovery for each and every file, one by one, but we're going to have our PC do that for us. Lets start building the script.

First, let see what we need to tell the computer. We need to tell it to copy all files from the disk image. It will have to copy the files using their node numbers, so we need to give it the node numbers. But node numbers are not enough, because it will then copy everything into a single folder. Instead, we want to copy the files in the exact directories they are originally from. And we also want the files to retain their original name. So we need to give three things: 1) File's node number, 2) File's full directory path, and 3) File name. Lets try this command

fls -f fat -p -r /media/store/mydisk.img
Notice the new flag there? -p flag denotes full path; meaning fls will now show the full directory path of every file.
So now we know how to get the node numbers and path of each file, lets put it all in a file.

fls -f fat -p -r /media/store/mydisk.img > filelist.txt
If you have been following my tutorials until now, or any of the millions Linux tutorials out there, you'd probably realized that > means output. Here I sent the output of fls to a file called filelist.txt. Run vi filelist.txt to see if the output is successfully written. If vi shows something like this then we're on the right track



Close vi by typing this sequence on your keyboard: (Escape) : q (Enter)      (not at the same time).
And now we need a script to tell our computer to read from this text file and perform icat operation. 
Type 

vi copyfiles.sh
Oooops... sorry, bad habit. For a tutorial, lets not use vi, but use gedit instead. Gedit is like a notepad, so I think we are all familiar with it, compared to vi

gedit copyfiles.sh

Note: This is for step-by-step technical explanation, you can skip the headache by scrolling down until you find some texts in purple, and start from there. but if you're willing to learn and understand how the script works, continue reading from here ;)




Now we have an empty file called copyfiles.sh. Time to put some scripts. Start with telling the computer which shell we want it to use. Lets use BASH (Bourne Again SHell). Write this in the first line of copyfiles.sh

#!/bin/bash
Now to tell the computer to read our text file. We can either put the filename directly (cat copyfiles.sh), of we can use an input variable. In this example we're going to use input variable, so it will be more dynamic and can be re-use in other times. Write this in the next line:

cat $1 | 
$1 means the first input parameter that we will specify when we run the script (I'll show you later). cat is a tool to concatenate files and print on the standard output; in other word it will read the file and give the output. To whom the output will be given? By default it will show on the screen, but here we're telling the script to give the output to the next command, by putting an | after cat command. 

Now we want to use the information in the file and manipulate them, line-by-line, so we're going to use a while loop. Write these lines after cat's line

while read line; do

done
So the above code will tell the computer to (do something) for every line of the text file. And Now we're going to specify that (something). From now on, all codes will be written between while read line; do  and done lines, not after done line.

Now if we look at the previous image, we can see that a directory is shown in this fashion: d/d 2054:       My Documents/My Pictures and a file is shown in this fashion: r/r 3079:       My Documents/My Pictures/Album Sample_01.jpg . So it will easy to differentiate between a directory (d/d) and a file (r/r). Unfortunately, we have another complication, the deleted files. Deleted directories and files are shown in these fashions, respectively: d/d * 2054:       My Documents/My Pictures and  r/r * 3079:       My Documents/My Pictures/Album Sample_01.jpg. Notice the *(asterisk)? We need to create a script that can differentiate between a directory and a file, and between deleted and non-deleted.

Lets move on anyway, we'll tackle that along the way ;)

Easiest way to do is to assign each word in the line (separated by blank space) into a variable. like this:

type=`echo "$line" | awk {'print $1'}` 
status=`echo "$line" | awk {'print $2'}`
node=`echo "$line" | awk {'print $3'}`
filename=`echo "$line" | awk {'print $4'}`
That will work if our directories and files' names do not contain blank spaces, but in real life they do. So we need to do something about the filename. Lets do this:

filename=`echo $line | awk {'min=index($0,":")-index($2,":  ");max=length($0);print substr($0,min,max)'}`
filename=${filename##: }
The first line above will take everything from : (colon) to the end of the line. The second line will eliminate the : from filename.

What about the node number? print $3 will give the node number if there is * in the line, but if it's not there, node number will be print $2, the second element in the line. So we need to put a condition for node number, like this:

if [ $status = "*" ]; then
node=`echo "$line" | awk {'print $3'}`
else
node="$status" 
fi
Now we have all our variables in the right way. Our code right now should look like this:

#!/bin/bash 
cat $1 | 
while read line; do 
   type=`echo "$line" | awk {'print $1'}` 
   status=`echo "$line" | awk {'print $2'}`
   if [ $status = "*" ]; then
node=`echo "$line" | awk {'print $3'}`
   else
node="$status" 
   fi
   filename=`echo $line | awk {'min=index($0,":")-index($2,":  ");max=length($0);print substr($0,min,max)'}`
   filename=${filename##: }
   node=${node%:} 
done

next we're going to recreate the directories and copy all the files. To create a directory we simply use the command mkdir (directory name). And to copy a file, we're going to use the same command as in Step 2.a, which is icat -f fat -r '/media/store/mydisk.img' (node number) > (filename) . So all we need to do is to check whether the line we are reading is for a directory or a file, then put the correct command into it. This is how it goes:

   if [ "$type" = "d/d" ]; then 
      mkdir "$filename"
   else 
      icat -f fat -r '/media/store/mydisk.img' "$node" > "$filename" 
   fi 
And then what? No more... It is all done!

Here's the complete script :)

filename: copyfiles.sh
----------------------------------------------------------------------------------------------------

#!/bin/bash 


cat $1 | 
while read line; do 
   type=`echo "$line" | awk {'print $1'}` 
   status=`echo "$line" | awk {'print $2'}`


   if [ $status = "*" ]; then
node=`echo "$line" | awk {'print $3'}`
   else
node="$status" 
   fi


   filename=`echo $line | awk {'min=index($0,":")-index($2,":  ");max=length($0);print substr($0,min,max)'}`


   filename=${filename##: }


   node=${node%:} 


   if [ "$type" = "d/d" ]; then 
      mkdir "$filename"
   else 
      icat -f fat -r '/media/store/mydisk.img' "$node" > "$filename" 
   fi 


done
-----------------------------------------------------------------------------------




Now we have a text file called filelist.txt and a script file called copyfiles.sh. Run the script using this command:

sh copyfiles.sh filelist.txt
What we did here is run the script using sh tool. The script we're running is the file called copyfiles.sh, and filelist.txt is the first parameter for copyfiles.sh (remember cat $1 | in the script file?)

And wait... It's going to take some time, of course...


If you still have any problem, or any question, or maybe just to say 'hi', drop me a message through my e-mail (lifutushi@gmail.com), facebook (www.facebook.com/lifutushidotcom) or twitter (www.twitter.com/lifutushidotcom).






Related Posts by Categories



0 comments:

Post a Comment

Find us on Google+