Wednesday, January 16, 2008

EnScript Tutorial IV

It has been several weeks since my last EnScript Tutorial, so I decided it was time for the next installment in the EnScript tutorial series.

In the last tutorial, we learned how to recurse through all the evidence in EnCase and select a specific object (file or folder) based on its name matching some specific text. In this tutorial, we will learn how to read data from a file, commonly referred to as File I/O.

In this tutorial, we will recurse through all the evidence to find a file named "boot.ini". Once we find this file, we will open it so we can read specific data from it. We can also bookmark a small portion of the file in the same manner as an examiner may sweep certain text in a file and bookmark only that data. We start with the following basic code:



The third line of code recurses through all the evidence loaded in EnCase in a loop. Line four compares the name of each object to see if the name matches exactly "boot.ini". If the name matches that text, then it writes the full path to the console. When this EnScript is run, we get the following output in the console:

"Case 1\C\boot.ini"

So the EnScript successfully ran, recursed through all the evidence and found one file that has the name of "boot.ini", which is located in the root of the volume named "C".

The next step is to "open" this file so we can read data from inside the file. We first need to create a variable of the proper type to hold a pointer to the file so we can then reference that specific file and perform operations. The proper class type is the EntryFileClass. We need to first create a variable of this class type. To do this we can use the following line of code:

EntryFileClass file();

This creates a variable named "file" that is of the EntryFileClass type. The open and closed parenthesis immediately after the variable name initializes the variable and prepares it to be used. Instead of using the parenthesis, you could create the variable object alternatively using the following code:

EntryFileClass file;
file = new EntryFileClass();

This code alternatively creates a variable named "file" of the EntryFileClass type, but does not initialize it. Then the next line initializes the variable and it can then be used. Either way works, the first example just uses one line of code as opposed to two.

Once we have created the proper variable type and initialized it, we can now use it and open the file we found so we can read data from it. To open the file, there is a member function of the EntryFileClass named "Open" (that's intuitive!). We can use this function to open the file so we can move a pointer around in the file and read data. The following code "opens" the file:

file.Open(entry, 0);

This function accepts two options. The first is an EntryClass object that is a pointer to the file you want to open. Line three above causes the EnScript to recurse through each object and temporarily assigns each object to the variable named "entry". Line four then compares the name property of the entry object to the text "boot.ini". The file.Open function needs to know what EntryFileClass object you wish to open. In this example, since we are using a conditional IF statement, the EnScript will open the "entry" object as long as its name property equals "boot.ini".

The second parameter specifies if you want to open the file with some options, such as include SLACK space, don't treat an erased file as consecutive clusters or if you want to write to the file. This brings up a good point that needs clarification. You cannot write to a file in the evidence file. EnCase does not allow you to alter evidence in any way. The WRITE option is used if you want to open (create) a file on your forensic machine and then write data to it.

Once you open the file, you have to tell EnCase how you want to read data from that file, for example as UNICODE, ANSI, UTF7, etc. To do this, we use the following function, which is a member of the FileClass. If you look at the EnScript Type tab in EnCase and examine the EntryFileClass type, you will notice at the very top it states "inherits:FileClass". This means that this class inherits all the functions and properties that the FileClass has. The FileClass has a member function named SetCodePage. Since EntryFileClass objects inherit functions from the FileClass, you can call this function on a EntryFileClass object. Therefore, the following code can be used to tell EnCase how we want to read the data:

file.SetCodePage(CodePageClass::ANSI);

This tells EnCase we want to read the data as simple ANSI text, one byte at a time, as opposed to UNICODE, which would read two bytes at a time.

When a file is opened for reading, a pointer is created and placed at the very beginning of the file at offset zero. As you read data the pointer moves along through the file so that if you read data again, it knows where to continue from and it won’t read the same data again, unless you explicitly instruct it to do so. Let’s assume we want to only read a specific line in the boot.ini file. If we know where that line is we could move the pointer to where we want to read the data. For example, if we knew that the data we wanted to read started at offset 100, we could use the following function to move the pointer:

file.Seek(100);

This would move the pointer to offset 100 in the file and if we then began reading data, it would start at offset 100. But what if we don't know the offset or it changes dynamically based on the computer system and how it is configured? We could read through the file and then make a decision base don what we read. In this example, we are opening the "boot.ini" file, which is used as part of the boot process on a Windows system to decide if it is capable of multi-booting different operating systems. The boot.ini file I am using in this example looks like the following:



Lets assume we want to read the line of data that specifies what the default boot location is if the user does not specify otherwise at boot time, specifically this line:

"default=multi(0)disk(0)rdisk(0)partition(1)\WINDOWS"

One way to accomplish this is to read each line and then decide if its the one we want. In this case, there is some static text in the line we want that can be used to help us identify and decide if its the data we want. We already have the file open and ready to be read. The next step is to begin reading data. We have two options here, we could read one byte at a time or we can read a string of data. In this case, we can read each line and then decide if its the line we want. The following code can be used to accomplish this:

file.ReadString(text, -1, "\x0d\x0a");

This function reads a string of data until a carriage return (0x0d in hex) and line feed (0x0a in hex) are encountered and places the text into the "text" variable. Here is the entire code with comments:



Line 8 opens the file
Line 9 sets the code page to ANSI
Line 10 creates a variable of String type to hold a line of data as we read it
Line 11 enter a DO loop to read one line at a time until the end of the file is reached or until we find the line we are looking for
Line 12 read one line of text and place the data into the "text" variable
Line 13 if the data in the "text" variable contains "default=" then this is the line we are looking for.
Line 14 print the entire line we just found
Line 15 break out of the DO loop since we just found the line we were looking for
Line 18 End of the loop, which we will exit if we have reached the end of the file

If we run this code, we get the following output in the console:

"Case 1\C\boot.ini"
"default=multi(0)disk(0)rdisk(0)partition(1)\WINDOWS"

Tthe first line is the result of the first Console.WriteLine statement (line 5). The second line is the result of finding the text we want by checking if the text "default=" is in the string we just read.

The example EnScript can be downloaded here.Please feel free to post questions, comments, complaints or the upcoming winning lottery numbers.

3 comments:

cci Wednesday, 06 May, 2009  

Your EnScript tutorial is so good for me, I really enjoyed it.

I'm a EnScript beginner and now writing
device acquisition code, but I have one problem.

I'd like to show remaining time at the status bar during acquisition, but I don't know how to code it.

I know I should use SystemClass::StatusRange() and SystemClass::StatusInc() for progressive status bar (equal to showing remaining time) in combination with loop like 'for' statement.
Acquire method of EvidenceFileClass, however, terminates at one time.

Though I posted the problem to GSI support forum, nobody replies to me.

Could you give me an advice about it?

Lance Mueller Wednesday, 06 May, 2009  

Sure, you can send me your code and questions at:
lance (at) forensicskb.com

sms spy Thursday, 09 June, 2011  

Love this EnScript tutorial! It was very helpful! Thank you for taking the time to do all of that.

Post a Comment

Computer Forensics, Malware Analysis & Digital Investigations

Random Articles