mirror of
https://github.com/overcuriousity/autopsy-flatpak.git
synced 2025-07-16 17:57:43 +00:00

Fix getting long file name string. If the extension verison was not a x03, x07 or x08 the start position was set to null to get the string so it would start at the beginning of the block so would get junk data. This change has also been pushed to the Regripper2.8 repo as well
380 lines
10 KiB
Perl
380 lines
10 KiB
Perl
#-----------------------------------------------------------
|
|
# itempos.pl
|
|
#
|
|
# History:
|
|
# 20191111 - Added default value to $jmp if $item{extver} cannot be determined.
|
|
#
|
|
# References
|
|
# http://c0nn3ct0r.blogspot.com/2011/11/windows-shellbag-forensics.html
|
|
# Andrew's Python code for Registry Decoder
|
|
# http://code.google.com/p/registrydecoder/source/browse/trunk/templates/template_files/ShellBag.py
|
|
# Joachim Metz's shell item format specification
|
|
# http://download.polytechnic.edu.na/pub4/download.sourceforge.net/pub/
|
|
# sourceforge/l/project/li/liblnk/Documentation/Windows%20Shell%20Item%20format/
|
|
# Windows%20Shell%20Item%20format.pdf
|
|
# Converting DOS Date format
|
|
# http://msdn.microsoft.com/en-us/library/windows/desktop/ms724274(v=VS.85).aspx
|
|
#
|
|
# Thanks to Willi Ballenthin and Joachim Metz for the documentation they
|
|
# provided, Andrew Case for posting the Registry Decoder code, and Kevin
|
|
# Moore for writing the shell bag parser for Registry Decoder, as well as
|
|
# assistance with some parsing.
|
|
#
|
|
# copyright 2013 Quantum Analytics Research, LLC
|
|
# Author: H. Carvey, keydet89@yahoo.com
|
|
#-----------------------------------------------------------
|
|
package itempos;
|
|
use strict;
|
|
use Time::Local;
|
|
|
|
my %config = (hive => "NTUSER\.DAT",
|
|
hivemask => 16,
|
|
output => "report",
|
|
category => "User Activity",
|
|
osmask => 16, #Win7/Win2008R2
|
|
hasShortDescr => 1,
|
|
hasDescr => 0,
|
|
hasRefs => 0,
|
|
version => 20130514);
|
|
|
|
sub getConfig{return %config}
|
|
|
|
sub getShortDescr {
|
|
return "Shell/Bags/1/Desktop ItemPos* value parsing; Win7 NTUSER.DAT hives";
|
|
}
|
|
sub getDescr{}
|
|
sub getRefs {}
|
|
sub getHive {return $config{hive};}
|
|
sub getVersion {return $config{version};}
|
|
|
|
my $VERSION = getVersion();
|
|
|
|
sub pluginmain {
|
|
my $class = shift;
|
|
my $hive = shift;
|
|
::logMsg("Launching itempos v.".$VERSION);
|
|
::rptMsg("itempos v.".$VERSION); # banner
|
|
::rptMsg("(".$config{hive}.") ".getShortDescr()."\n"); # banner
|
|
my %itempos = ();
|
|
|
|
my $reg = Parse::Win32Registry->new($hive);
|
|
my $root_key = $reg->get_root_key;
|
|
|
|
my $key_path = "Software\\Microsoft\\Windows\\Shell\\Bags\\1\\Desktop";
|
|
my $key;
|
|
|
|
if ($key = $root_key->get_subkey($key_path)) {
|
|
::rptMsg($key_path);
|
|
my $lw = $key->get_timestamp();
|
|
::rptMsg("LastWrite: ".gmtime($lw));
|
|
::rptMsg("");
|
|
|
|
my @vals = $key->get_list_of_values();
|
|
foreach my $v (@vals) {
|
|
my $name = $v->get_name();
|
|
if ($name =~ m/^ItemPos/) {
|
|
$itempos{$name} = $v->get_data();
|
|
}
|
|
}
|
|
|
|
if (scalar keys %itempos > 0) {
|
|
foreach my $i (keys %itempos) {
|
|
::rptMsg("Value: ".$i);
|
|
::rptMsg(sprintf "%-10s|%-20s|%-20s|%-20s|Name","Size","Modified","Accessed","Created");
|
|
::rptMsg(sprintf "%-10s|%-20s|%-20s|%-20s|"."-" x 10,"-" x 10,"-" x 20,"-" x 20,"-" x 20);
|
|
parseBagEntry($itempos{$i});
|
|
::rptMsg("");
|
|
}
|
|
}
|
|
else {
|
|
::rptMsg("No ItemPos* values found.");
|
|
}
|
|
}
|
|
else {
|
|
::rptMsg($key_path." not found.");
|
|
}
|
|
# ::rptMsg("");
|
|
# The following was added on 20130514 to address Windows XP systems
|
|
$key_path = "Software\\Microsoft\\Windows\\ShellNoRoam\\Bags";
|
|
if ($key = $root_key->get_subkey($key_path)) {
|
|
my @sk = $key->get_list_of_subkeys();
|
|
if (scalar(@sk) > 0) {
|
|
foreach my $s (@sk) {
|
|
my %itempos = ();
|
|
my @vals = $s->get_subkey("Shell")->get_list_of_values();
|
|
|
|
if (scalar(@vals) > 0) {
|
|
foreach my $v (@vals) {
|
|
my $name = $v->get_name();
|
|
if ($name =~ m/^ItemPos/) {
|
|
$itempos{$name} = $v->get_data();
|
|
}
|
|
}
|
|
|
|
if (scalar keys %itempos > 0) {
|
|
::rptMsg($key_path."\\".$s->get_name()."\\Shell");
|
|
foreach my $i (keys %itempos) {
|
|
::rptMsg("Value: ".$i);
|
|
::rptMsg(sprintf "%-10s|%-20s|%-20s|%-20s|Name","Size","Modified","Accessed","Created");
|
|
::rptMsg(sprintf "%-10s|%-20s|%-20s|%-20s|"."-" x 10,"-" x 10,"-" x 20,"-" x 20,"-" x 20);
|
|
parseBagEntry($itempos{$i});
|
|
::rptMsg("");
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
# No subkeys
|
|
}
|
|
}
|
|
else {
|
|
::rptMsg($key_path." not found\.");
|
|
}
|
|
}
|
|
|
|
#-----------------------------------------------------------
|
|
#
|
|
#-----------------------------------------------------------
|
|
|
|
|
|
#-----------------------------------------------------------
|
|
# parseBagEntry()
|
|
#-----------------------------------------------------------
|
|
sub parseBagEntry {
|
|
my $data = shift;
|
|
my $ofs = 24;
|
|
my $len = length($data);
|
|
while ($ofs < $len) {
|
|
my %item = ();
|
|
my $sz = unpack("v",substr($data,$ofs,2));
|
|
|
|
my $data = substr($data,$ofs,$sz);
|
|
|
|
my $type = unpack("C",substr($data,2,1));
|
|
|
|
if ($type == 0x1f) {
|
|
%item = parseSystemBagItem($data);
|
|
::rptMsg(sprintf "%-10s|%-20s|%-20s|%-20s|".$item{name},"","","","");
|
|
}
|
|
elsif ($type == 0x31 || $type == 0x32 || $type == 0x3a) {
|
|
%item = parseFolderItem($data);
|
|
|
|
my ($m,$a,$c);
|
|
(exists $item{mtime_str} && $item{mtime_str} ne "0") ? ($m = $item{mtime_str}) : ($m = "");
|
|
(exists $item{atime_str} && $item{atime_str} ne "0") ? ($a = $item{atime_str}) : ($a = "");
|
|
(exists $item{ctime_str} && $item{ctime_str} ne "0") ? ($c = $item{ctime_str}) : ($c = "");
|
|
my $str = sprintf "%-10s|%-20s|%-20s|%-20s|",$item{size},$m,$a,$c;
|
|
::rptMsg($str.$item{name});
|
|
|
|
}
|
|
else {
|
|
|
|
}
|
|
$ofs += $sz + 8;
|
|
}
|
|
}
|
|
#-----------------------------------------------------------
|
|
# parseSystemBagItem()
|
|
#-----------------------------------------------------------
|
|
sub parseSystemBagItem {
|
|
my $data = shift;
|
|
my %item = ();
|
|
my %vals = (0x00 => "Explorer",
|
|
0x42 => "Libraries",
|
|
0x44 => "Users",
|
|
0x4c => "Public",
|
|
0x48 => "My Documents",
|
|
0x50 => "My Computer",
|
|
0x58 => "My Network Places",
|
|
0x60 => "Recycle Bin",
|
|
0x68 => "Explorer",
|
|
0x70 => "Control Panel",
|
|
0x78 => "Recycle Bin",
|
|
0x80 => "My Games");
|
|
|
|
$item{type} = unpack("C",substr($data,2,1));
|
|
$item{id} = unpack("C",substr($data,3,1));
|
|
if (exists $vals{$item{id}}) {
|
|
$item{name} = $vals{$item{id}};
|
|
}
|
|
else {
|
|
$item{name} = parseGUID(substr($data,4,16));
|
|
}
|
|
return %item;
|
|
}
|
|
|
|
#-----------------------------------------------------------
|
|
# parseFolderItem()
|
|
#-----------------------------------------------------------
|
|
sub parseFolderItem {
|
|
my $data = shift;
|
|
my %item = ();
|
|
my $ofs_mdate = 0x08;
|
|
$item{type} = unpack("C",substr($data,2,1));
|
|
|
|
$item{size} = unpack("V",substr($data,4,4));
|
|
|
|
my @m = unpack("vv",substr($data,$ofs_mdate,4));
|
|
($item{mtime_str},$item{mtime}) = convertDOSDate($m[0],$m[1]);
|
|
|
|
my $ofs_shortname = $ofs_mdate + 6;
|
|
my $tag = 1;
|
|
my $cnt = 0;
|
|
my $str = "";
|
|
while($tag) {
|
|
my $s = substr($data,$ofs_shortname + $cnt,1);
|
|
if ($s =~ m/\x00/ && ((($cnt + 1) % 2) == 0)) {
|
|
$tag = 0;
|
|
}
|
|
else {
|
|
$str .= $s;
|
|
$cnt++;
|
|
}
|
|
}
|
|
# $str =~ s/\x00//g;
|
|
my $shortname = $str;
|
|
my $ofs = $ofs_shortname + $cnt + 1;
|
|
# Read progressively, 1 byte at a time, looking for 0xbeef
|
|
$tag = 1;
|
|
$cnt = 0;
|
|
while ($tag) {
|
|
if (unpack("v",substr($data,$ofs + $cnt,2)) == 0xbeef) {
|
|
$tag = 0;
|
|
}
|
|
else {
|
|
$cnt++;
|
|
}
|
|
}
|
|
$item{extver} = unpack("v",substr($data,$ofs + $cnt - 4,2));
|
|
$ofs = $ofs + $cnt + 2;
|
|
|
|
@m = unpack("vv",substr($data,$ofs,4));
|
|
($item{ctime_str},$item{ctime}) = convertDOSDate($m[0],$m[1]);
|
|
$ofs += 4;
|
|
@m = unpack("vv",substr($data,$ofs,4));
|
|
($item{atime_str},$item{atime}) = convertDOSDate($m[0],$m[1]);
|
|
|
|
my $jmp;
|
|
if ($item{extver} == 0x03) {
|
|
$jmp = 8;
|
|
}
|
|
elsif ($item{extver} == 0x07) {
|
|
$jmp = 26;
|
|
}
|
|
elsif ($item{extver} == 0x08) {
|
|
$jmp = 30;
|
|
}
|
|
else {
|
|
$jmp = 34;
|
|
}
|
|
|
|
$ofs += $jmp;
|
|
|
|
$str = substr($data,$ofs,length($data) - 30);
|
|
my $longname = (split(/\x00\x00/,$str,2))[0];
|
|
$longname =~ s/\x00//g;
|
|
|
|
if ($longname ne "") {
|
|
$item{name} = $longname;
|
|
}
|
|
else {
|
|
$item{name} = $shortname;
|
|
}
|
|
return %item;
|
|
|
|
|
|
}
|
|
|
|
#-----------------------------------------------------------
|
|
# convertDOSDate()
|
|
# subroutine to convert 4 bytes of binary data into a human-
|
|
# readable format. Returns both a string and a Unix-epoch
|
|
# time.
|
|
#-----------------------------------------------------------
|
|
sub convertDOSDate {
|
|
my $date = shift;
|
|
my $time = shift;
|
|
|
|
if ($date == 0x00 || $time == 0x00){
|
|
return (0,0);
|
|
}
|
|
else {
|
|
my $sec = ($time & 0x1f) * 2;
|
|
$sec = "0".$sec if (length($sec) == 1);
|
|
if ($sec == 60) {$sec = 59};
|
|
my $min = ($time & 0x7e0) >> 5;
|
|
$min = "0".$min if (length($min) == 1);
|
|
my $hr = ($time & 0xF800) >> 11;
|
|
$hr = "0".$hr if (length($hr) == 1);
|
|
my $day = ($date & 0x1f);
|
|
$day = "0".$day if (length($day) == 1);
|
|
my $mon = ($date & 0x1e0) >> 5;
|
|
$mon = "0".$mon if (length($mon) == 1);
|
|
my $yr = (($date & 0xfe00) >> 9) + 1980;
|
|
my $gmtime = timegm($sec,$min,$hr,$day,($mon - 1),$yr);
|
|
return ("$yr-$mon-$day $hr:$min:$sec",$gmtime);
|
|
# return gmtime(timegm($sec,$min,$hr,$day,($mon - 1),$yr));
|
|
}
|
|
}
|
|
|
|
#-----------------------------------------------------------
|
|
# parseGUID()
|
|
# Takes 16 bytes of binary data, returns a string formatted
|
|
# as an MS GUID.
|
|
#-----------------------------------------------------------
|
|
sub parseGUID {
|
|
my $data = shift;
|
|
my $d1 = unpack("V",substr($data,0,4));
|
|
my $d2 = unpack("v",substr($data,4,2));
|
|
my $d3 = unpack("v",substr($data,6,2));
|
|
my $d4 = unpack("H*",substr($data,8,2));
|
|
my $d5 = unpack("H*",substr($data,10,6));
|
|
return sprintf "{%08x-%x-%x-$d4-$d5}",$d1,$d2,$d3;
|
|
}
|
|
|
|
#-----------------------------------------------------------
|
|
# printData()
|
|
# subroutine used primarily for debugging; takes an arbitrary
|
|
# length of binary data, prints it out in hex editor-style
|
|
# format for easy debugging
|
|
#-----------------------------------------------------------
|
|
sub printData {
|
|
my $data = shift;
|
|
my $len = length($data);
|
|
my $tag = 1;
|
|
my $cnt = 0;
|
|
|
|
my $loop = $len/16;
|
|
$loop++ if ($len%16);
|
|
|
|
foreach my $cnt (0..($loop - 1)) {
|
|
# while ($tag) {
|
|
my $left = $len - ($cnt * 16);
|
|
|
|
my $n;
|
|
($left < 16) ? ($n = $left) : ($n = 16);
|
|
|
|
my $seg = substr($data,$cnt * 16,$n);
|
|
my @str1 = split(//,unpack("H*",$seg));
|
|
|
|
my @s3;
|
|
my $str = "";
|
|
|
|
foreach my $i (0..($n - 1)) {
|
|
$s3[$i] = $str1[$i * 2].$str1[($i * 2) + 1];
|
|
|
|
if (hex($s3[$i]) > 0x1f && hex($s3[$i]) < 0x7f) {
|
|
$str .= chr(hex($s3[$i]));
|
|
}
|
|
else {
|
|
$str .= "\.";
|
|
}
|
|
}
|
|
my $h = join(' ',@s3);
|
|
::rptMsg(sprintf "0x%08x: %-47s ".$str,($cnt * 16),$h);
|
|
}
|
|
}
|
|
1;
|