2018-01-03 14:43:27 -05:00

423 lines
11 KiB
Perl

#-----------------------------------------------------------
# shellbags_test.pl
#
#
# License: GPL v3
# copyright 2012 Quantum Analytics Research, LLC
# Author: H. Carvey, keydet89@yahoo.com
#-----------------------------------------------------------
package shellbags_test;
use strict;
require 'shellitems.pl';
my %config = (hive => "USRCLASS\.DAT",
hivemask => 32,
output => "report",
category => "User Activity",
osmask => 20, #Vista, Win7/Win2008R2
hasShortDescr => 1,
hasDescr => 0,
hasRefs => 0,
version => 20130528);
sub getConfig{return %config}
sub getShortDescr {
return "Shell/BagMRU traversal in XP/Win7 user hives";
}
sub getDescr{}
sub getRefs {}
sub getHive {return $config{hive};}
sub getVersion {return $config{version};}
my $VERSION = getVersion();
my %item = ();
my $XP = 0;
my $root_key;
sub pluginmain {
my $class = shift;
my $hive = shift;
::logMsg("Launching shellbags_test v.".$VERSION);
::rptMsg("shellbags_test v.".$VERSION); # banner
::rptMsg("(".getHive().") ".getShortDescr()."\n"); # banner
my $reg = Parse::Win32Registry->new($hive);
$root_key = $reg->get_root_key;
my %paths = ("Win7" => "Local Settings\\Software\\Microsoft\\Windows\\Shell\\BagMRU",
"XP" => "Software\\Microsoft\\Windows\\ShellNoRoam\\BagMRU");
my $key;
if ($key = $root_key->get_subkey($paths{"Win7"})) {
setup($key);
}
elsif ($key = $root_key->get_subkey($paths{"XP"})) {
$XP = 1;
setup($key);
}
}
sub setup {
my $key = shift;
($XP == 1) ? ($item{path} = "ShellNoRoam\\BagMRU\\") : ($item{path} = "Shell\\BagMRU\\");
$item{name} = "Desktop\\";
# Print header info
::rptMsg(sprintf "%-20s |%-20s | %-20s | %-20s | %-20s |Resource","MRU Time","Modified","Accessed","Created","Zip_Subfolder");
::rptMsg(sprintf "%-20s |%-20s | %-20s | %-20s | %-20s |"."-" x 12,"-" x 12,"-" x 12,"-" x 12,"-" x 12,"-" x 12);
traverse($key,\%item);
}
sub traverse {
my $key = shift;
my $parent = shift;
my %item = ();
my @vals = $key->get_list_of_values();
my %values;
foreach my $v (@vals) {
my $name = $v->get_name();
$values{$name} = $v->get_data();
}
my $mru;
if (exists $values{MRUListEx}) {
$mru = unpack("V",substr($values{MRUListEx},0,4));
}
delete $values{MRUListEx};
foreach my $v (sort {$a <=> $b} keys %values) {
next unless ($v =~ m/^\d/);
my $nodeslot = "";
eval {
$nodeslot = $key->get_subkey($v)->get_value("NodeSlot")->get_data();
};
my $type = unpack("C",substr($values{$v},2,1));
my $size = unpack("v",substr($values{$v},0,2));
# probe($values{$v});
# Need to first check to see if the parent of the item was a zip folder
# and if the 'zipsubfolder' value is set to 1
if (exists ${$parent}{zipsubfolder} && ${$parent}{zipsubfolder} == 1) {
if ($XP == 0) {
%item = parseZipSubFolderItem($values{$v});
$item{zipsubfolder} = 1;
}
}
elsif (length($values{$v}) == 22 && $type != 0x47) {
$item{name} = parseGUID(substr($values{$v},4,16));
}
elsif (substr($values{$v},0x0d,2) =~ m/\x3a\x3a/){
%item = parseXPShellDeviceItem($values{$v});
}
elsif ($type == 0x00) {
# Variable/Property Sheet
%item = parseVariableEntry($values{$v});
}
elsif ($type == 0x01) {
#
%item = parse01ShellItem($values{$v});
}
elsif ($type == 0x1F) {
# System Folder
%item = parseSystemFolderEntry($values{$v});
}
elsif ($type == 0x2e) {
# Device
%item = parseDeviceEntry($values{$v});
}
elsif ($type == 0x2F) {
# Volume (Drive Letter)
%item = parseDriveEntry($values{$v});
}
elsif ($type == 0xc3 || $type == 0x41 || $type == 0x42 || $type == 0x46 || $type == 0x47) {
# Network stuff
my $id = unpack("C",substr($values{$v},3,1));
if ($type == 0xc3 && $id != 0x01) {
%item = parseNetworkEntry($values{$v});
}
else {
%item = parseNetworkEntry($values{$v});
}
}
elsif ($type == 0x31 || $type == 0x32 || $type == 0xb1 || $type == 0x74) {
# Folder or Zip File
%item = parseFolderEntry($values{$v});
# if (exists $item{mft_rec_num}) {
# print "MFT record number : ".$item{mft_rec_num}."\n";
# print "MFT sequence number: ".$item{mft_seq_num}."\n";
# }
# probe($values{$v});
}
elsif ($type == 0x35) {
%item = parseFolderEntry2($values{$v});
}
elsif ($type == 0x64 || $type == 0x65 || $type == 0x69) {
%item = parseType64Item($values{$v});
}
elsif ($type == 0x71) {
# Control Panel
if ($size == 0x1e) {
%item = parseControlPanelEntry($values{$v});
}
else {
$item{name} = parseGUID(substr($values{$v},0xe,16));
}
}
elsif ($type == 0x61) {
# URI type
%item = parseURIEntry($values{$v});
}
elsif ($type == 0x53) {
%item = parseTypex53($values{$v});
}
else {
# Unknown type
$item{name} = sprintf "Unknown Type (0x%x)",$type;
# probe($values{$v});
}
if ($type == 0x32) {
if (lc($item{name}) =~ m/\.zip$/) {
$item{zipsubfolder} = 1;
}
}
# for debug purposes
# $item{name} = $item{name}."[".$v."]";
# ::rptMsg(${$parent}{path}.$item{name});
if ($mru != 4294967295 && ($v == $mru)) {
$item{mrutime} = $key->get_timestamp();
$item{mrutime_str} = $key->get_timestamp_as_string();
$item{mrutime_str} =~ s/T/ /;
$item{mrutime_str} =~ s/Z/ /;
}
else {
$item{mrutime_str} = "";
}
my ($m,$a,$c,$o) = "";
(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 = "");
(exists $item{datetime} && $item{datetime} ne "N/A") ? ($o = $item{datetime}) : ($o = "");
if ($item{name} eq "" || $item{name} =~ m/\\$/) {
}
else {
$item{name} = $item{name}."\\";
}
$item{name} = ${$parent}{name}.$item{name};
$item{path} = ${$parent}{path}.$v."\\";
my $resource = $item{name};
if (exists $item{filesize}) {
$resource .= " [".$item{filesize}."]";
}
my $str = sprintf "%-20s |%-20s | %-20s | %-20s | %-20s |".$resource." [".$item{path}."]",$item{mrutime_str},$m,$a,$c,$o;
::rptMsg($str);
# For XP, check NodeSlot value
if ($XP == 1 && $nodeslot ne "") {
my %itempos = getItemPos($nodeslot);
if (scalar(keys %itempos) > 0) {
foreach my $name (keys %itempos) {
my $n = $name;
$n .= " [".$itempos{$name}{size}."]" if ($itempos{$name}{size} ne "");
$n .= " [ShellNoRoam\\Bags\\".$nodeslot."\\Shell\\".$itempos{$name}{itempos}."]";
my $str = sprintf "%-20s |%-20s | %-20s | %-20s | %-20s | ","",$itempos{$name}{mtime_str},$itempos{$name}{atime_str},$itempos{$name}{ctime_str},"";
::rptMsg($str.$n);
}
}
}
traverse($key->get_subkey($v),\%item);
}
}
#-----------------------------------------------------------
# getItemPos()
#-----------------------------------------------------------
sub getItemPos {
my $nodeslot = shift;
my %item = ();
my $key_path = "Software\\Microsoft\\Windows\\ShellNoRoam\\Bags\\".$nodeslot."\\Shell";
my $key;
if ($key = $root_key->get_subkey($key_path)) {
my @vals = $key->get_list_of_values();
if (scalar(@vals) > 0) {
foreach my $v (@vals) {
my $name = $v->get_name();
if ($name =~ m/^ItemPos/) {
%item = parseBagEntry($v->get_data(),$name);
}
}
}
}
else {
::rptMsg($key_path." not found\.");
}
return %item;
}
#-----------------------------------------------------------
# parseBagEntry()
#-----------------------------------------------------------
sub parseBagEntry {
my $data = shift;
my $name = shift;
my $ofs = 24;
my $len = length($data);
my %bag = ();
while ($ofs < $len) {
my %item = ();
my $sz = unpack("v",substr($data,$ofs,2));
my $dat = substr($data,$ofs,$sz);
my $type = unpack("C",substr($dat,2,1));
if ($type == 0x1f) {
%item = parseSystemBagItem($dat);
$bag{$item{name}}{itempos} = $name;
$bag{$item{name}}{mtime_str} = "";
$bag{$item{name}}{atime_str} = "";
$bag{$item{name}}{ctime_str} = "";
$bag{$item{name}}{size} = "";
}
elsif ($type == 0x31 || $type == 0x32 || $type == 0x3a) {
%item = parseFolderItem($dat);
$bag{$item{name}}{itempos} = $name;
(exists $item{mtime_str} && $item{mtime_str} ne "0") ? ($bag{$item{name}}{mtime_str} = $item{mtime_str}) : ($bag{$item{name}}{mtime_str} = "");
(exists $item{atime_str} && $item{atime_str} ne "0") ? ($bag{$item{name}}{atime_str} = $item{atime_str}) : ($bag{$item{name}}{atime_str} = "");
(exists $item{ctime_str} && $item{ctime_str} ne "0") ? ($bag{$item{name}}{ctime_str} = $item{ctime_str}) : ($bag{$item{name}}{ctime_str} = "");
$bag{$item{name}}{size} = $item{size};
}
else {
}
$ofs += $sz + 8;
}
return %bag;
}
#-----------------------------------------------------------
# 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);
return %item unless (defined $s);
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) {
my $s = substr($data,$ofs + $cnt,2);
return %item unless (defined $s);
if (unpack("v",$s) == 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 {}
$ofs += $jmp;
$str = substr($data,$ofs,length($data) - $ofs);
my $longname = (split(/\x00\x00/,$str,2))[0];
$longname =~ s/\x00//g;
if ($longname ne "") {
$item{name} = $longname;
}
else {
$item{name} = $shortname;
}
return %item;
}
1;