Recently I worked on a case that required I reverse engineer some file formats used by the 'Free Download Manager' application. This is a popular download management application available from www.freedownloadmanager.org.
The version of the application I analyzed stores its logs under 'userprofile\Application Data\Free Download Manager'. It uses a number of files to handle different logs and track various in-process tasks. Here's a list of the files I found there:
- dlmgrsi.sav - This is actually a short executable of some description. Not sure what it's for.
- downloads.his.sav - Log file using the following format: Starts with the null-terminated header "FDM Downloads History". Then 8 bytes of unknown data, followed by a list of records as follows, terminated by end of file after the last record:
- 4 byte filename size (little endian)
- filename
- 4 byte target path size (little endian)
- target path
- 4 byte URL size (little endian)
- URL
- 4 bytes of null
- windows file time #1 (Presumably download start)
- windows file time #2 (Presumably download end)
- windows file time #3 (Unknown. All examples found identical to time #2)
- 8 bytes of file size (little endian)
- downloads.sav - Much shorter log file, using a different file format.
- groups.sav - Apppears to be intended for specifying where downloaded files of specified types are to be placed. Starts with the null terminated header "FDM Groups", then 11 bytes of binary data, then records of the form:
- 4 byte group number
- 4 byte groupname size (little endian)
- groupname
- 4 byte download path size (little endian)
- download path
- 4 byte extension list size (little endian)
- space separated list of extensions
- 4 nulls
- history.sav - Yet another, even shorter log file, with a different format.
- mctasks.sav - My example just had the header "FDM Media Convert Tasks", followed by hex 00 00 00 01 00 00 00 00 00.
- schedules.sav - My example just had 8 null bytes.
- sites.sav - My example had nothing but the header "FDM Sites " followed by hex 00 08 38 03 00 00 00 00 00.
- spider.sav - My example just had the header "FDM Web Pages " followed by hex 00 58 88 06 00 00 00 00 00.
- uploads.1.sav - My example just had the header "FUM Uploads" followed by hex 00 54 FE 02 00 00 00 00 00.
I spent the majority of my time puzzling out the format of downloads.his.sav, but was able to draw some conclusions about the formats of downloads.sav and history.sav as well. I suspect that these latter two files may be used somewhat more like a database than traditional log files. Either that or the samples I have are corrupted (this is quite possible as they were collected while in use using EnCase Enterprise over the network). Suffice it to say that they have different formats, but also contain target paths, URLs, and windows filetimes. The filetimes can be readily identified for those using EnCase by running Lance Mueller's Windows Timestamp Search EnScript. These files may also be used to track downloads which are still in progress.
I wrote the following short perl script to parse runs of records manually extracted from the downloads.his.sav file or from unallocated space, and output them in CSV format. It doesn't handle the file header and leading binary data, so don't try to feed it the whole file. Note also that it uses the Parse::Win32Registry module from CPAN to convert the Windows filetime values.
use Parse::Win32Registry qw( :REG_ unpack_windows_time unpack_unicode_string ); while (read(STDIN, $fnsize, 4) == 4) { $translated_fnsize=unpack(I, $fnsize); read STDIN, $filename, $translated_fnsize; print "$filename, "; read STDIN, $fpsize, 4; $translated_fpsize=unpack(I, $fpsize); read STDIN, $filepath, $translated_fpsize; print "$filepath, "; read STDIN, $urlsize, 4; $translated_urlsize=unpack(I, $urlsize); read STDIN, $url, $translated_urlsize; print "$url, "; read STDIN, $nulls, 4; read STDIN, $firsttime, 8; $translated_firsttime=unpack(H16, $firsttime); $time1=unpack_windows_time( $firsttime ); ($sec1, $min1, $hour1, $mday1, $mon1, $year1, $wday1, $yday1, $isdst1) = localtime($time1+46800); $year1+=1900; $mon1++; printf "%2.2d/%2.2d/%d, %2.2d:%2.2d:%2.2d, ", $mon1,$mday1,$year1,$hour1,$min1,$sec1; read STDIN, $secondtime, 8; $translated_secondtime=unpack(H16, $secondtime); $time2=unpack_windows_time( $secondtime ); ($sec2, $min2, $hour2, $mday2, $mon2, $year2, $wday2, $yday2, $isdst2) = localtime($time2+46800); $year2+=1900; $mon2++; printf "%2.2d/%2.2d/%d, %2.2d:%2.2d:%2.2d, ", $mon2,$mday2,$year2,$hour2,$min2,$sec2; read STDIN, $thirdtime, 8; $translated_firsttime=unpack(H16, $thirdtime); $time3=unpack_windows_time( $thirdtime ); ($sec3, $min3, $hour3, $mday3, $mon3, $year3, $wday3, $yday3, $isdst3) = localtime($time3+46800); $year3+=1900; $mon3++; printf "%2.2d/%2.2d/%d, %2.2d:%2.2d:%2.2d, ", $mon3,$mday3,$year3,$hour3,$min3,$sec3; ad path read STDIN, $size, 8; $translated_size=unpack(L, $size); print "$translated_size\n"; }
As always, please feel free to leave commentary if you liked this article or want to call me on the carpet for some inaccuracy.