├── p2x5124.dll ├── scanner.exe ├── Forensic Scanner Usage.pdf ├── license ├── plugins ├── win_dll.pl ├── tasks.pl ├── ntshrui.pl ├── win_temp.pl ├── prefetch.pl ├── hosts.pl ├── spoolsv.pl ├── spp_clients.pl ├── appinitdlls.pl ├── visal.pl ├── zeus.pl ├── imm32.pl ├── ws2_32.pl ├── acmru.pl ├── esent.pl ├── typedpaths.pl ├── runmru.pl ├── direct.pl ├── mndmru.pl ├── appcertdlls.pl ├── sysinternals.pl ├── muicache.pl ├── usertemp.pl ├── wordwheelquery.pl ├── winlogon.pl ├── soft_run.pl ├── typedurlstime.pl ├── typedurls.pl ├── filehistory.pl ├── user_run.pl ├── emdmgmt.pl ├── tsclient.pl ├── imagefile.pl ├── tif.pl ├── userassist.pl ├── mp2.pl ├── arpcache.pl ├── networklist.pl ├── winbackup.pl ├── ssid.pl ├── evtrpt.pl ├── appcompatcache.pl └── samparse.pl ├── time.pl ├── WinFile.pm ├── Engine.pm ├── scanner.pl ├── WinSetup.pm └── ReadPE.pm /p2x5124.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/appliedsec/forensicscanner/HEAD/p2x5124.dll -------------------------------------------------------------------------------- /scanner.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/appliedsec/forensicscanner/HEAD/scanner.exe -------------------------------------------------------------------------------- /Forensic Scanner Usage.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/appliedsec/forensicscanner/HEAD/Forensic Scanner Usage.pdf -------------------------------------------------------------------------------- /license: -------------------------------------------------------------------------------- 1 | This program is provided without any guarantee or warranty as to it's 2 | effectiveness, as well as without any implied warranty as to it's fitness 3 | for a particular purpose. 4 | 5 | This program is licensed under the Perl Artistic License: 6 | http://dev.perl.org/licenses/artistic.html -------------------------------------------------------------------------------- /plugins/win_dll.pl: -------------------------------------------------------------------------------- 1 | package win_dll; 2 | #----------------------------------------------------------- 3 | # win_dll - List all DLL files in the %SystemRoot% dir 4 | # 5 | # Change History: 6 | # 20120822 - updated to new format 7 | # 20110323 - created 8 | # 9 | # copyright 2012 10 | # Author: H. Carvey, keydet89@yahoo.com 11 | #----------------------------------------------------------- 12 | use strict; 13 | 14 | my %config = (hasShortDescr => 1, 15 | shortDescr => "Check for potential for Explorer\.exe DLL hijacking; ". 16 | "DLLs in Windows dir", 17 | category => "Malware", 18 | type => "File", 19 | class => 0, 20 | output => "report", 21 | osmask => 3, 22 | version => 20120822); 23 | 24 | sub getConfig{return \%config} 25 | my $VERSION = $config{version}; 26 | sub getShortDescr { return $config{shortDescr};} 27 | sub pluginmain { 28 | my $class = shift; 29 | my $win = WinFile->new(); 30 | my $parent = ::getConfig(); 31 | ::logMsg("win_dll v.".$VERSION); 32 | ::rptMsg("-" x 60); 33 | ::rptMsg("win_dll v.".$VERSION); 34 | ::rptMsg(getShortDescr()); 35 | ::rptMsg("Category: ".$config{category}); 36 | ::rptMsg(""); 37 | my $dir = $parent->{systemroot}; 38 | if (-e $dir && -d $dir) { 39 | opendir(DIR,$dir); 40 | my @dlls = grep(/\.dll$/,readdir(DIR)); 41 | closedir(DIR); 42 | foreach (@dlls) { 43 | ::rptMsg($_); 44 | } 45 | } 46 | else { 47 | ::rptMsg($dir." not found."); 48 | } 49 | } 50 | 1; -------------------------------------------------------------------------------- /plugins/tasks.pl: -------------------------------------------------------------------------------- 1 | package tasks; 2 | #----------------------------------------------------------- 3 | # tasks - List .job files in %SystemRoot%\Tasks dir 4 | # 5 | # Change History: 6 | # 20120928 - updated to FS format 7 | # 20110323 - created 8 | # 9 | # copyright 2011 Quantum Analytics Research, LLC 10 | #----------------------------------------------------------- 11 | use strict; 12 | 13 | my %config = (hasShortDescr => 1, 14 | shortDescr => "List \.job files in %SystemRoot%\\Tasks dir", 15 | category => "Malware", 16 | type => "File", 17 | output => "report", 18 | class => 0, #system 19 | osmask => 3, #for now, XP/2003 20 | version => 20120928); 21 | 22 | sub getConfig{return \%config} 23 | my $VERSION = $config{version}; 24 | sub getShortDescr { return $config{shortDescr};} 25 | sub pluginmain { 26 | my $class = shift; 27 | my %parent = ::getConfig(); 28 | ::logMsg("tasks v.".$VERSION); 29 | ::rptMsg("-" x 60); 30 | ::rptMsg("tasks v.".$VERSION); 31 | ::rptMsg(getShortDescr()); 32 | ::rptMsg("Category: ".$config{category}); 33 | ::rptMsg(""); 34 | my $dir = $parent{systemroot}."Tasks"; 35 | 36 | my @files; 37 | 38 | if (-e $dir && -d $dir) { 39 | opendir(DIR,$dir); 40 | my @files = grep(/\.job$/,readdir(DIR)); 41 | closedir(DIR); 42 | 43 | if (scalar @files > 0) { 44 | foreach my $f (@files) { 45 | ::rptMsg(" ".$f); 46 | } 47 | } 48 | else { 49 | ::rptMsg($dir." has no \.job files."); 50 | } 51 | 52 | } 53 | else { 54 | ::rptMsg($dir." not found."); 55 | } 56 | } 57 | 1; -------------------------------------------------------------------------------- /plugins/ntshrui.pl: -------------------------------------------------------------------------------- 1 | package ntshrui; 2 | #----------------------------------------------------------- 3 | # ntshrui - check for ntshrui.dll in the %SystemRoot% dir 4 | # 5 | # Change History: 6 | # 20120816 - updated 7 | # 20110323 - created 8 | # 9 | # References: 10 | # 11 | # 12 | # TODO: 13 | # -Add ability to read/parse PE header via ReadPE.pm 14 | # 15 | # copyright 2012 16 | # Author: H. Carvey, keydet89@yahoo.com 17 | #----------------------------------------------------------- 18 | use strict; 19 | 20 | my %config = (hasShortDescr => 1, 21 | shortDescr => "Check for Explorer DLL hijacking via ntshrui\.dll", 22 | category => "Malware", 23 | class => 0, # system = 0, user = 1 24 | output => "report", 25 | type => "File", 26 | osmask => 31, #XP - Win7 27 | version => 20120816); 28 | 29 | sub getConfig{return \%config} 30 | my $VERSION = $config{version}; 31 | sub getShortDescr { 32 | return "Checks for ntshrui\.dll in the Windows directory (DLL Search Hijacking)"; 33 | } 34 | 35 | sub pluginmain { 36 | my $class = shift; 37 | my $parent = ::getConfig(); 38 | ::logMsg("ntshrui v.".$VERSION); 39 | ::rptMsg("-" x 60); 40 | ::rptMsg("ntshrui v.".$VERSION); 41 | ::rptMsg(getShortDescr()); 42 | ::rptMsg("Category: ".$config{category}); 43 | ::rptMsg(""); 44 | my $win = WinFile->new(); 45 | my $file = $parent->{drive}."Windows\\ntshrui\.dll"; 46 | if (-e $file && -f $file) { 47 | ::rptMsg($file); 48 | ::rptMsg(" MD5 : ".$win->getMD5($file)); 49 | } 50 | else { 51 | ::rptMsg($file." not found."); 52 | } 53 | } 54 | 1; -------------------------------------------------------------------------------- /plugins/win_temp.pl: -------------------------------------------------------------------------------- 1 | package win_temp; 2 | #----------------------------------------------------------- 3 | # win_temp - List files in the %SystemRoot%\Temp dir 4 | # 5 | # Change History: 6 | # 20120822 - updated to new format 7 | # 20110323 - created 8 | # 9 | # copyright 2012 10 | # Author: H. Carvey, keydet89@yahoo.com 11 | #----------------------------------------------------------- 12 | use strict; 13 | 14 | my %config = (hasShortDescr => 1, 15 | shortDescr => "Check for PE files in %SystemRoot%\\Temp", 16 | category => "Malware", 17 | type => "File", 18 | osmask => 3, 19 | class => 0, 20 | output => "report", 21 | version => 20120822); 22 | 23 | sub getConfig{return \%config} 24 | my $VERSION = $config{version}; 25 | sub getShortDescr { return $config{shortDescr};} 26 | 27 | sub pluginmain { 28 | my $class = shift; 29 | # my $win = WinFile->new(); 30 | my $parent = ::getConfig(); 31 | ::logMsg("win_temp v.".$VERSION); 32 | ::rptMsg("-" x 60); 33 | ::rptMsg("win_temp v.".$VERSION); 34 | ::rptMsg(getShortDescr()); 35 | ::rptMsg("Category: ".$config{category}); 36 | ::rptMsg(""); 37 | my $dir = $parent->{systemroot}."Temp"; 38 | ::rptMsg("Directory: ".$dir); 39 | my @files; 40 | 41 | if (-e $dir && -d $dir) { 42 | opendir(DIR,$dir); 43 | my @f = readdir(DIR); 44 | closedir(DIR); 45 | 46 | my @files; 47 | foreach (@f) { 48 | push(@files,$_) unless (($_ =~ m/^\.$/) || ($_ =~ m/^\.\.$/)); 49 | } 50 | 51 | if (scalar @files > 0) { 52 | foreach my $f (@files) { 53 | ::rptMsg(" -> ".$f); 54 | } 55 | } 56 | else { 57 | ::rptMsg($dir." has no files."); 58 | } 59 | } 60 | else { 61 | ::rptMsg($dir." not found."); 62 | } 63 | } 64 | 1; -------------------------------------------------------------------------------- /plugins/prefetch.pl: -------------------------------------------------------------------------------- 1 | package prefetch; 2 | #----------------------------------------------------------- 3 | # prefetch - List .pf files in %SystemRoot%\Prefetch dir 4 | # 5 | # Change History: 6 | # 20120822 - updated to current plugin format 7 | # 20110323 - created 8 | # 9 | # To-Do: 10 | # Implement Prefetch file parsing 11 | # 12 | # copyright 2012 13 | # Author: H. Carvey, keydet89@yahoo.com 14 | #----------------------------------------------------------- 15 | use strict; 16 | 17 | my %config = (hasShortDescr => 1, 18 | shortDescr => "List \.pf files in %SystemRoot%\\Prefetch dir", 19 | category => "Malware", 20 | type => "File", 21 | output => "report", 22 | class => 0, # 0 = system, 1 = user 23 | osmask => 21, #XP,Vista, Win7 24 | version => 20120822); 25 | 26 | sub getConfig{return \%config} 27 | my $VERSION = $config{version}; 28 | sub getShortDescr { 29 | return "Gets contents of Prefetch folder"; 30 | } 31 | 32 | sub pluginmain { 33 | my $class = shift; 34 | my $parent = ::getConfig(); 35 | ::logMsg("prefetch v.".$VERSION); 36 | ::rptMsg("-" x 60); 37 | ::rptMsg("prefetch v.".$VERSION); 38 | ::rptMsg(getShortDescr()); 39 | ::rptMsg("Category: ".$config{category}); 40 | ::rptMsg(""); 41 | 42 | my $dir = $parent->{systemroot}."Prefetch"; 43 | ::rptMsg("Directory : ".$dir); 44 | my @files; 45 | 46 | if (-e $dir && -d $dir) { 47 | opendir(DIR,$dir); 48 | my @files = grep(/\.pf$/,readdir(DIR)); 49 | closedir(DIR); 50 | 51 | if (scalar @files > 0) { 52 | foreach my $f (@files) { 53 | ::rptMsg(" ".$f); 54 | } 55 | } 56 | else { 57 | ::rptMsg($dir." has no \.pf files."); 58 | } 59 | 60 | } 61 | else { 62 | ::rptMsg($dir." not found."); 63 | } 64 | } 65 | 1; -------------------------------------------------------------------------------- /plugins/hosts.pl: -------------------------------------------------------------------------------- 1 | package hosts; 2 | #----------------------------------------------------------- 3 | # hosts - check hosts file for signs of tampering; prints out 4 | # only those lines that are not comments and not blank 5 | # 6 | # Change History: 7 | # 20120816 - updated to latest version of scanner 8 | # 20111004 - Updated 9 | # 20100928 - Created 10 | # 11 | # References: 12 | # 13 | # 14 | # 15 | # copyright 2012 16 | # Author: H. Carvey, keydet89@yahoo.com 17 | #----------------------------------------------------------- 18 | use strict; 19 | 20 | my %config = (hasShortDescr => 1, 21 | shortDescr => "Check hosts file for indications of tampering", 22 | category => "Malware", 23 | output => "report", 24 | class => 0, # system = 0, user = 1 25 | type => "File", 26 | osmask => 31, #XP - Win7; need to test on Win8 27 | version => 20120816); 28 | 29 | sub getConfig{return \%config} 30 | my $VERSION = $config{version}; 31 | sub getShortDescr { return $config{shortDescr};} 32 | sub pluginmain { 33 | my $class = shift; 34 | # my $win = WinFile->new(); 35 | my $parent = ::getConfig(); 36 | my $drv = $parent->{drive}; 37 | ::logMsg("hosts v.".$VERSION); 38 | ::rptMsg("-" x 60); 39 | ::rptMsg("hosts v.".$VERSION); 40 | ::rptMsg(getShortDescr()); 41 | ::rptMsg("Category: ".$config{category}); 42 | ::rptMsg(""); 43 | # ::rptMsg($config{shortDescr}); 44 | # ::rptMsg(""); 45 | my $file = $drv."Windows\\system32\\drivers\\etc\\hosts"; 46 | ::rptMsg("File: ".$file); 47 | if (-e $file && -f $file) { 48 | my %hosts; 49 | open(FH,"<",$file); 50 | while() { 51 | chomp; 52 | # skip comment lines or empty lines 53 | next if ($_ =~ m/^#/ || $_ =~ m/^\s+$/); 54 | ::rptMsg($_); 55 | } 56 | close(FH); 57 | } 58 | else { 59 | ::rptMsg($file." not found."); 60 | } 61 | } 62 | 1; -------------------------------------------------------------------------------- /plugins/spoolsv.pl: -------------------------------------------------------------------------------- 1 | package spoolsv; 2 | #----------------------------------------------------------- 3 | # spoolsv - plugin to check for tampering of spoolsv.exe 4 | # 5 | # Change history 6 | # 20120822 - updated to new format 7 | # 20111004 - Updated 8 | # 20100923 - created 9 | # 10 | # copyright 2012 11 | # Author: H. Carvey, keydet89@yahoo.com 12 | #----------------------------------------------------------- 13 | use strict; 14 | 15 | my %config = (hasShortDescr => 1, 16 | shortDescr => "Check spoolsv\.exe for indications of tampering", 17 | type => "File", 18 | category => "Malware", 19 | class => 0, 20 | output => "report", 21 | osmask => 3, 22 | version => 20120822); 23 | 24 | sub getConfig{return \%config} 25 | my $VERSION = $config{version}; 26 | sub getShortDescr { return $config{shortDescr};} 27 | my @md5; 28 | my $count = 0; 29 | 30 | sub pluginmain { 31 | my $class = shift; 32 | my $win = WinFile->new(); 33 | my $parent = ::getConfig(); 34 | my $drv = $parent->{drive}; 35 | my @md5; 36 | ::logMsg("spoolsv v.".$VERSION); 37 | ::rptMsg("-" x 60); 38 | ::rptMsg("spoolsv v.".$VERSION); 39 | ::rptMsg(getShortDescr()); 40 | ::rptMsg("Category: ".$config{category}); 41 | ::rptMsg(""); 42 | my $file = $drv."Windows\\system32\\spoolsv\.exe"; 43 | ::rptMsg("File: ".$file); 44 | if (-e $file && -f $file) { 45 | $count++; 46 | $md5[0] = $win->getMD5($file); 47 | ::rptMsg(" MD5 : ".$md5[0]); 48 | } 49 | else { 50 | ::rptMsg($file." not found."); 51 | } 52 | ::rptMsg(""); 53 | my $file = $drv."Windows\\system32\\dllcache\\spoolsv\.exe"; 54 | ::rptMsg("File: ".$file); 55 | if (-e $file && -f $file) { 56 | $count++; 57 | $md5[1] = $win->getMD5($file); 58 | ::rptMsg(" MD5 : ".$md5[1]); 59 | } 60 | else { 61 | ::rptMsg($file." not found."); 62 | } 63 | ::rptMsg(""); 64 | if ($count > 1) { 65 | if ($md5[0] == $md5[1]) { 66 | ::rptMsg("Both copies of spoolsv\.exe match."); 67 | } 68 | else { 69 | ::rptMsg("The copies of spoolsv\.exe do NOT match."); 70 | } 71 | } 72 | } 73 | 74 | 1; -------------------------------------------------------------------------------- /plugins/spp_clients.pl: -------------------------------------------------------------------------------- 1 | #----------------------------------------------------------- 2 | # spp_clients 3 | # Get the contents of a value that illustrates which volumes are monitored 4 | # for VSCs 5 | # 6 | # History 7 | # 20120925 - updated to RS format 8 | # 20120914 - created 9 | # 10 | # copyright 2012 Quantum Analytics Research, LLC 11 | # Author: H. Carvey, keydet89@yahoo.com 12 | #----------------------------------------------------------- 13 | package spp_clients; 14 | use strict; 15 | 16 | my %config = (hive => "Software", 17 | hivemask => 0x08, 18 | type => "Reg", 19 | output => "report", 20 | class => 0, 21 | category => "Config", 22 | hasShortDescr => 1, 23 | hasDescr => 0, 24 | hasRefs => 0, 25 | osmask => 50, #Vista, Win7, Win 8 26 | version => 20120925); 27 | 28 | sub getConfig{return \%config} 29 | sub getShortDescr { 30 | return "Determines volumes monitored by VSS"; 31 | } 32 | sub getDescr{} 33 | sub getRefs {} 34 | sub getHive {return $config{hive};} 35 | sub getVersion {return $config{version};} 36 | 37 | my $VERSION = getVersion(); 38 | 39 | sub pluginmain { 40 | my $class = shift; 41 | my $parent = ::getConfig(); 42 | 43 | ::logMsg("spp_clients v.".$VERSION); 44 | ::rptMsg("-" x 60); 45 | ::rptMsg("spp_clients v.".$VERSION); 46 | ::rptMsg(getShortDescr()); 47 | ::rptMsg("Category: ".$config{category}); 48 | ::rptMsg(""); 49 | 50 | my $hive = $parent->{software}; 51 | my $reg = Parse::Win32Registry->new($hive); 52 | my $root_key = $reg->get_root_key; 53 | 54 | my $key_path = 'Microsoft\\Windows NT\\CurrentVersion\\SPP\\Clients'; 55 | my $key; 56 | if ($key = $root_key->get_subkey($key_path)) { 57 | ::rptMsg("SPP_Clients"); 58 | ::rptMsg($key_path); 59 | ::rptMsg("LastWrite Time ".gmtime($key->get_timestamp())." (UTC)"); 60 | ::rptMsg(""); 61 | 62 | my $mon; 63 | eval { 64 | $mon = $key->get_value("{09F7EDC5-294E-4180-AF6A-FB0E6A0E9513}")->get_data(); 65 | ::rptMsg("Monitored volumes: ".$mon); 66 | }; 67 | 68 | } 69 | else { 70 | ::rptMsg($key_path." not found."); 71 | } 72 | } 73 | 1; -------------------------------------------------------------------------------- /plugins/appinitdlls.pl: -------------------------------------------------------------------------------- 1 | #----------------------------------------------------------- 2 | # appinitdlls - check for suspicious entries in AppInit_Dlls value 3 | # 4 | # Change History 5 | # 20120925 - updated to RS format 6 | # 20080324 - created 7 | # 8 | # copyright 2012 Quantum Analytics Research, LLC 9 | # Author: H. Carvey, keydet89@yahoo.com 10 | #----------------------------------------------------------- 11 | package appinitdlls; 12 | use strict; 13 | 14 | my %config = (hive => "Software", 15 | hivemask => 0x08, 16 | type => "Reg", 17 | category => "Malware", 18 | output => "report", 19 | class => 0, 20 | hasShortDescr => 1, 21 | hasDescr => 0, 22 | hasRefs => 1, 23 | osmask => 31, #XP-Win7 24 | version => 20120925); 25 | 26 | sub getConfig{return \%config} 27 | sub getShortDescr { 28 | return "Gets contents of AppInit_DLLs value"; 29 | } 30 | sub getDescr{} 31 | sub getRefs { 32 | my %refs = ("Working with the AppInit_DLLs Reg Value" => 33 | "http://support.microsoft.com/kb/q197571"); 34 | return %refs; 35 | } 36 | sub getHive {return $config{hive};} 37 | sub getVersion {return $config{version};} 38 | 39 | my $VERSION = getVersion(); 40 | 41 | sub pluginmain { 42 | my $class = shift; 43 | my $parent = ::getConfig(); 44 | 45 | ::logMsg("appinitdlls v.".$VERSION); 46 | ::rptMsg("-" x 60); 47 | ::rptMsg("appinitdlls v.".$VERSION); 48 | ::rptMsg(getShortDescr()); 49 | ::rptMsg("Category: ".$config{category}); 50 | ::rptMsg(""); 51 | 52 | my $hive = $parent->{software}; 53 | 54 | my $reg = Parse::Win32Registry->new($hive); 55 | my $root_key = $reg->get_root_key; 56 | 57 | my $key_path = 'Microsoft\\Windows NT\\CurrentVersion\\Windows'; 58 | my $key; 59 | if ($key = $root_key->get_subkey($key_path)) { 60 | ::rptMsg($key_path); 61 | ::rptMsg("LastWrite Time ".gmtime($key->get_timestamp())." (UTC)"); 62 | ::rptMsg(""); 63 | my @vals = $key->get_list_of_values(); 64 | foreach my $v (@vals) { 65 | my $name = $v->get_name(); 66 | if ($name eq "AppInit_DLLs") { 67 | my $data = $v->get_data(); 68 | $data = "{blank}" if ($data eq ""); 69 | ::rptMsg($name." -> ".$data); 70 | ::rptMsg(""); 71 | ::rptMsg("Verify any entries if the AppInit_DLLs value is not blank."); 72 | } 73 | } 74 | } 75 | else { 76 | ::rptMsg($key_path." not found."); 77 | } 78 | } 79 | 1; -------------------------------------------------------------------------------- /plugins/visal.pl: -------------------------------------------------------------------------------- 1 | package visal; 2 | #----------------------------------------------------------- 3 | # visal 4 | # Check for indications of Visal.B infection 5 | # 6 | # Change History: 7 | # 20120928 - updated to FS format 8 | # 20100928 - created 9 | # 10 | # References: 11 | # http://www.microsoft.com/security/portal/Threat/Encyclopedia/ 12 | # Entry.aspx?Name=Worm%3aWin32%2fVisal.B 13 | # 14 | # copyright 2010 Quantum Analytics Research, LLC 15 | #----------------------------------------------------------- 16 | use strict; 17 | 18 | my %config = (hasShortDescr => 1, 19 | shortDescr => "Check for Visal\.B infection", 20 | category => "Malware", 21 | type => "File+Reg", 22 | output => "report", 23 | class => 0, 24 | osmask => 31, 25 | version => 20120928); 26 | 27 | sub getConfig{return \%config} 28 | my $VERSION = $config{version}; 29 | sub getShortDescr { return $config{shortDescr};} 30 | sub pluginmain { 31 | my $class = shift; 32 | my $win = WinFile->new(); 33 | my $parent = ::getConfig(); 34 | my $drv = $parent->{drive}; 35 | my $checks = 0; 36 | 37 | ::logMsg("visal\.b v.".$VERSION); 38 | ::rptMsg("-" x 60); 39 | ::rptMsg("visal\.b v.".$VERSION); 40 | ::rptMsg(getShortDescr()); 41 | ::rptMsg("Category: ".$config{category}); 42 | ::rptMsg(""); 43 | my $reg = Parse::Win32Registry->new($parent->{software}); 44 | my $root_key = $reg->get_root_key; 45 | my $key_path = "Microsoft\\Windows NT\\CurrentVersion\\WinLogon"; 46 | my $key; 47 | if ($key = $root_key->get_subkey($key_path)) { 48 | ::rptMsg($key_path); 49 | ::rptMsg("LastWrite Time ".gmtime($key->get_timestamp())." (UTC)"); 50 | my $shell; 51 | eval { 52 | $shell = $key->get_value("Shell")->get_data(); 53 | ::rptMsg("Shell = ".$shell); 54 | if (grep(/csrss/,$shell)) { 55 | ::rptMsg("**Possible Visal\.B detected."); 56 | $checks++; 57 | } 58 | }; 59 | ::rptMsg("Error getting Shell value: ".$@) if ($@); 60 | } 61 | else { 62 | ::rptMsg($key_path." not found."); 63 | } 64 | ::rptMsg(""); 65 | my $file = $drv."Windows\\csrss\.exe"; 66 | # ::rptMsg("File: ".$file); 67 | if (-e $file && -f $file) { 68 | my $md5 = $win->getMD5($file); 69 | ::rptMsg(" MD5 : ".$md5); 70 | $checks++; 71 | } 72 | else { 73 | ::rptMsg($file." not found."); 74 | } 75 | if ($checks > 0) { 76 | ::rptMsg(""); 77 | ::rptMsg("Possible Visal\.B infection detected."); 78 | } 79 | } 80 | 81 | 1; -------------------------------------------------------------------------------- /plugins/zeus.pl: -------------------------------------------------------------------------------- 1 | package zeus; 2 | #----------------------------------------------------------- 3 | # zeus - simple plugin to check for potential Zeus/Zbot 4 | # infection 5 | # 6 | # Change History 7 | # 20111004 - Updated 8 | # 20100923 - Created 9 | # 10 | # 11 | # copyright 2011 Quantum Analytics Research, LLC 12 | #----------------------------------------------------------- 13 | use strict; 14 | 15 | my %config = (hasShortDescr => 1, 16 | shortDescr => "Check for Zeus/Zbot sdra64\.exe", 17 | hasRefs => 0, 18 | osmask => 3, #XP, 2003 19 | class => 0, 20 | type => "File,Reg", 21 | hive => "Software", 22 | hivemask => 8, #Software hive 23 | category => "Malware", 24 | version => 20111004); 25 | 26 | sub getConfig{return \%config}; 27 | my $VERSION = $config{version}; 28 | 29 | sub pluginmain { 30 | my $class = shift; 31 | my %parent = ::getConfig(); 32 | ::logMsg("Zeus v.".$VERSION); 33 | ::rptMsg("-" x 60); 34 | ::rptMsg("Zeus v.".$VERSION); 35 | ::rptMsg(getShortDescr()); 36 | ::rptMsg("Category: ".$config{category}); 37 | ::rptMsg(""); 38 | ::rptMsg("Simple checks for indicators of Zeus/ZBot"); 39 | ::rptMsg(""); 40 | my $tag = "sdra64"; 41 | my $check = 0; 42 | 43 | my $reg = Parse::Win32Registry->new($parent{softwarehive}); 44 | my $root_key = $reg->get_root_key; 45 | my $key_path = "Microsoft\\Windows NT\\CurrentVersion\\Winlogon"; 46 | my $key; 47 | if ($key = $root_key->get_subkey($key_path)) { 48 | ::rptMsg($key_path); 49 | ::rptMsg("LastWrite Time ".gmtime($key->get_timestamp())." (UTC)"); 50 | my $ui; 51 | eval { 52 | $ui = $key->get_value("Userinit")->get_data(); 53 | ::rptMsg(" Userinit: ".$ui); 54 | ::rptMsg(""); 55 | if (grep(/$tag/,$ui)) { 56 | $check++; 57 | } 58 | else { 59 | ::rptMsg("*Zeus apparently not found."); 60 | } 61 | }; 62 | } 63 | ::rptMsg(""); 64 | $parent{systemroot} = $parent{systemroot}."\\" unless ($parent{systemroot} =~ m/\\$/); 65 | my $file = $parent{systemroot}."system32\\sdra64\.exe"; 66 | if (-e $file && -f $file) { 67 | ::rptMsg($file." found!"); 68 | $check++; 69 | } 70 | else { 71 | ::rptMsg($file." not found."); 72 | } 73 | 74 | if ($check == 0) { 75 | ::rptMsg($parent{computername}." is apparently not infected with Zeus."); 76 | } 77 | else { 78 | ::rptMsg($check." check(s) of two succeeded."); 79 | } 80 | } 81 | 82 | 1; -------------------------------------------------------------------------------- /plugins/imm32.pl: -------------------------------------------------------------------------------- 1 | package imm32; 2 | #----------------------------------------------------------- 3 | # imm32 - check imm32.dll for signs of tampering; during an 4 | # exam, imm32.dll had been modified to add another section, as well 5 | # as add a reference to a malicious DLL to the import table (IAT). 6 | # 7 | # Change History: 8 | # 20120816 - updated 9 | # 20111004 - Updated 10 | # 20110323 - Updated reading of sections to use ReadPE.pm 11 | # 20100928 - updated to include parsing of PE Sections (Win32::Exe) 12 | # 20100927 - Created 13 | # 14 | # References: 15 | # http://vil.nai.com/vil/content/v_142626.htm 16 | # 17 | # 18 | # copyright 2012 19 | # Author: H. Carvey, keydet89@yahoo.com 20 | #----------------------------------------------------------- 21 | use strict; 22 | 23 | my %config = (hasShortDescr => 1, 24 | shortDescr => "Check imm32\.dll for indications of tampering", 25 | category => "Malware", 26 | class => 0, # system = 0, user = 1 27 | output => "report", 28 | type => "File", 29 | osmask => 3, #as of 20120816, focus on XP, 2003 primarily 30 | version => 20120816); 31 | 32 | sub getConfig{return \%config} 33 | my $VERSION = $config{version}; 34 | sub getShortDescr { return $config{shortDescr};} 35 | sub pluginmain { 36 | my $class = shift; 37 | my $parent = ::getConfig(); 38 | ::logMsg("imm32 v.".$VERSION); 39 | ::rptMsg("-" x 60); 40 | ::rptMsg("imm32 v.".$VERSION); 41 | ::rptMsg(getShortDescr()); 42 | ::rptMsg("Category: ".$config{category}); 43 | ::rptMsg(""); 44 | my $win = WinFile->new(); 45 | my $file = $parent->{drive}."Windows\\system32\\imm32\.dll"; 46 | ::rptMsg("File: ".$file); 47 | if (-e $file && -f $file) { 48 | my $md5 = $win->getMD5($file); 49 | ::rptMsg(" MD5 : ".$md5); 50 | 51 | my %sect = getPESections($file); 52 | foreach my $s (keys %sect) { 53 | ::rptMsg("Section: ".$s." Virt Size: ".$sect{$s}{virt_sz}); 54 | ::rptMsg(""); 55 | } 56 | } 57 | else { 58 | ::rptMsg($file." not found."); 59 | } 60 | } 61 | 62 | sub getPESections { 63 | my $file = shift; 64 | my %sect; 65 | my $pe = ReadPE::new($file); 66 | my %dos = $pe->getDOSHeader(); 67 | my %fh = $pe->getFileHeader($dos{e_lfanew}); 68 | my %opt32 = $pe->getOptionalHeader32($dos{e_lfanew},$fh{size_opt_header}); 69 | my $sections_offset = $dos{e_lfanew} + 24 + 96 + (8*$opt32{rva_num}); 70 | my %sections = $pe->getImageSectionHeaders($sections_offset,$fh{number_sections}); 71 | 72 | $pe->close(); 73 | return %sections; 74 | } 75 | 1; -------------------------------------------------------------------------------- /plugins/ws2_32.pl: -------------------------------------------------------------------------------- 1 | #----------------------------------------------------------- 2 | # ws32_2.pl 3 | # Check to determining tampering of w32_2.dll; was told by malware 4 | # analyst that they were seeing WFP being disabled, and one of the 5 | # copies of ws32_2.dll being modified. This plugin checks the file 6 | # in the system32 dir against the one in the dllcache directory. 7 | # 8 | # Change history 9 | # 20120928 - updated to FS format 10 | # 20100923 - created 11 | # 12 | # References 13 | # 14 | # 15 | # copyright 2010-2012 Quantum Analytics Research, LLC 16 | # Author: H. Carvey, keydet89@yahoo.com 17 | #----------------------------------------------------------- 18 | package ws2_32; 19 | use strict; 20 | 21 | my %config = (hasShortDescr => 1, 22 | category => "Malware", 23 | type => "File", 24 | output => "report", 25 | class => 0, 26 | osmask => 3, #XP/2003 27 | version => 20120928); 28 | 29 | sub getConfig{return \%config} 30 | my $VERSION = $config{version}; 31 | sub getShortDescr { 32 | return "Check ws2_32\.dll for indications of tampering"; 33 | } 34 | 35 | sub pluginmain { 36 | my $class = shift; 37 | my $win = WinFile->new(); 38 | my $parent = ::getConfig(); 39 | my $drv = $parent->{drive}; 40 | 41 | my (@sz,@md5); 42 | my $count = 0; 43 | 44 | ::logMsg("ws2_32 v.".$VERSION); 45 | ::rptMsg("-" x 60); 46 | ::rptMsg("ws2_32 v.".$VERSION); 47 | ::rptMsg(getShortDescr()); 48 | ::rptMsg("Category: ".$config{category}); 49 | ::rptMsg(""); 50 | my $file = $drv."Windows\\system32\\ws2_32\.dll"; 51 | ::rptMsg("File: ".$file); 52 | if (-e $file && -f $file) { 53 | # my $ver = $win->getFileVersionInfo($file); 54 | # ::rptMsg(" FileVersion: ".$ver->{FileVersion}); 55 | $sz[0] = $win->getSize($file); 56 | $md5[0] = $win->getMD5($file); 57 | ::rptMsg(" MD5 : ".$md5[0]); 58 | $count++; 59 | } 60 | else { 61 | ::rptMsg($file." not found."); 62 | } 63 | ::rptMsg(""); 64 | my $file = $drv."Windows\\system32\\dllcache\\ws2_32\.dll"; 65 | ::rptMsg("File: ".$file); 66 | if (-e $file && -f $file) { 67 | # my $ver = $win->getFileVersionInfo($file); 68 | # ::rptMsg(" FileVersion: ".$ver->{FileVersion}); 69 | $sz[1] = $win->getSize($file); 70 | $md5[1] = $win->getMD5($file); 71 | ::rptMsg(" MD5 : ".$md5[1]); 72 | $count++; 73 | } 74 | else { 75 | ::rptMsg($file." not found."); 76 | } 77 | ::rptMsg(""); 78 | 79 | if ($count > 1) { 80 | if ($md5[0] == $md5[1]) { 81 | ::rptMsg("Both copies of ws2_32\.dll match."); 82 | } 83 | else { 84 | ::rptMsg("The copies of ws2_32\.dll do NOT match."); 85 | } 86 | } 87 | 88 | } 89 | 90 | 1; -------------------------------------------------------------------------------- /plugins/acmru.pl: -------------------------------------------------------------------------------- 1 | #----------------------------------------------------------- 2 | # acmru.pl 3 | # Plugin for Registry Ripper, NTUSER.DAT edition - gets the 4 | # ACMru values 5 | # 6 | # Change history 7 | # 20120823 - updated to Forensic Scanner format 8 | # 20080324 - created 9 | # 10 | # References 11 | # 12 | # 13 | # copyright 2012 14 | # Author: H. Carvey, keydet89@yahoo.com 15 | #----------------------------------------------------------- 16 | package acmru; 17 | use strict; 18 | 19 | my %config = (hive => "NTUSER\.DAT", 20 | hivemask => 0x10, 21 | hasShortDescr => 1, 22 | hasDescr => 0, 23 | category => "User Activity", 24 | type => "Reg", 25 | output => "report", 26 | class => 1, 27 | hasRefs => 0, 28 | osmask => 3, #XP/2003 29 | version => 20120823); 30 | 31 | sub getConfig{return \%config} 32 | sub getShortDescr { 33 | return "Gets contents of user's ACMru key"; 34 | } 35 | sub getDescr{} 36 | sub getRefs {} 37 | sub getHive {return $config{hive};} 38 | sub getVersion {return $config{version};} 39 | 40 | my $VERSION = getVersion(); 41 | 42 | sub pluginmain { 43 | my $class = shift; 44 | my $parent = ::getConfig(); 45 | my $profile = $parent->{userprofile}; 46 | 47 | ::logMsg("acmru v.".$VERSION); 48 | ::rptMsg("-" x 60); 49 | ::rptMsg("acmru v.".$VERSION); 50 | ::rptMsg(getShortDescr()); 51 | ::rptMsg("Category: ".$config{category}); 52 | ::rptMsg(""); 53 | 54 | ::rptMsg("Profile: ".$profile); 55 | $profile .= "\\" unless ($profile =~ m/\\$/); 56 | 57 | my $ntuser = $profile."NTUSER\.DAT"; 58 | 59 | my $reg = Parse::Win32Registry->new($ntuser); 60 | my $root_key = $reg->get_root_key; 61 | 62 | my $key_path = 'Software\\Microsoft\\Search Assistant\\ACMru'; 63 | my $key; 64 | if ($key = $root_key->get_subkey($key_path)) { 65 | ::rptMsg($key_path); 66 | ::rptMsg("LastWrite Time ".gmtime($key->get_timestamp())." (UTC)"); 67 | my @subkeys = $key->get_list_of_subkeys(); 68 | if (scalar(@subkeys) > 0) { 69 | foreach my $s (@subkeys) { 70 | ::rptMsg($s->get_name()." [".gmtime($s->get_timestamp())." (UTC)]"); 71 | my @vals = $s->get_list_of_values(); 72 | my %ac_vals; 73 | foreach my $v (@vals) { 74 | $ac_vals{$v->get_name()} = $v->get_data(); 75 | } 76 | foreach my $a (sort {$a <=> $b} keys %ac_vals) { 77 | ::rptMsg("\t".$a." -> ".$ac_vals{$a}); 78 | } 79 | ::rptMsg(""); 80 | } 81 | } 82 | else { 83 | ::rptMsg($key_path." has no subkeys."); 84 | } 85 | } 86 | else { 87 | ::rptMsg($key_path." not found."); 88 | } 89 | } 90 | 91 | 1; -------------------------------------------------------------------------------- /plugins/esent.pl: -------------------------------------------------------------------------------- 1 | #----------------------------------------------------------- 2 | # esent 3 | # Get contents of Esent\Process key from Software hive 4 | # 5 | # Change History 6 | # 20120925 - updated to RS Format 7 | # 20101202 - created 8 | # 9 | # 10 | # Note: Not sure why I wrote this one; just thought it might come 11 | # in handy as info about this key is developed. 12 | # 13 | # copyright 2012 Quantum Analytics Research, LLC 14 | # Author: H. Carvey, keydet89@yahoo.com 15 | #----------------------------------------------------------- 16 | package esent; 17 | use strict; 18 | 19 | my %config = (hive => "Software", 20 | hivemask => 0x08, 21 | type => "Reg", 22 | category => "Program Execution", 23 | class => 0, 24 | output => "report", 25 | osmask => 1, #I've only ever seen this on XP 26 | hasShortDescr => 1, 27 | hasDescr => 0, 28 | hasRefs => 1, 29 | version => 20120925); 30 | 31 | sub getConfig{return \%config} 32 | 33 | sub getShortDescr { 34 | return "Get ESENT\\Process key contents"; 35 | } 36 | sub getDescr{} 37 | sub getRefs {} 38 | sub getHive {return $config{hive};} 39 | sub getVersion {return $config{version};} 40 | 41 | my $VERSION = getVersion(); 42 | 43 | sub pluginmain { 44 | my $class = shift; 45 | my $parent = ::getConfig(); 46 | 47 | ::logMsg("esent v.".$VERSION); 48 | ::rptMsg("-" x 60); 49 | ::rptMsg("esent v.".$VERSION); 50 | ::rptMsg(getShortDescr()); 51 | ::rptMsg("Category: ".$config{category}); 52 | ::rptMsg(""); 53 | 54 | my $reg = Parse::Win32Registry->new($parent->{software}); 55 | my $root_key = $reg->get_root_key; 56 | 57 | my $key_path = "Microsoft\\ESENT\\Process"; 58 | my $key; 59 | if ($key = $root_key->get_subkey($key_path)) { 60 | ::rptMsg($key_path); 61 | # ::rptMsg("LastWrite Time ".gmtime($key->get_timestamp())." (UTC)"); 62 | ::rptMsg(""); 63 | 64 | my @sk = $key->get_list_of_subkeys(); 65 | 66 | if (scalar(@sk) > 0) { 67 | my %esent; 68 | 69 | foreach my $s (@sk) { 70 | my $sk = $s->get_subkey("DEBUG"); 71 | # my $lw = $s->get_timestamp(); 72 | my $lw = $sk->get_timestamp(); 73 | 74 | my $name = $s->get_name(); 75 | 76 | push(@{$esent{$lw}},$name); 77 | } 78 | 79 | foreach my $t (reverse sort {$a <=> $b} keys %esent) { 80 | ::rptMsg(gmtime($t)." (UTC)"); 81 | foreach my $item (@{$esent{$t}}) { 82 | ::rptMsg(" $item"); 83 | } 84 | } 85 | 86 | } 87 | else { 88 | ::rptMsg($key_path." has no subkeys."); 89 | } 90 | } 91 | else { 92 | ::rptMsg($key_path." not found."); 93 | } 94 | } 95 | 96 | 1; -------------------------------------------------------------------------------- /plugins/typedpaths.pl: -------------------------------------------------------------------------------- 1 | #----------------------------------------------------------- 2 | # typedpaths.pl 3 | # For Windows 7, Desktop Address Bar History 4 | # 5 | # Change history 6 | # 20120925 - updated to RS format 7 | # 20100330 - created 8 | # 9 | # References 10 | # 11 | # 12 | # copyright 2012 Quantum Analytics Research, LLC 13 | # Author: H. Carvey, keydet89@yahoo.com 14 | #----------------------------------------------------------- 15 | package typedpaths; 16 | use strict; 17 | 18 | my %config = (hive => "NTUSER\.DAT", 19 | hivemask => 0x10, 20 | class => 1, 21 | category => "User Activity", 22 | type => "Reg", 23 | output => "report", 24 | hasShortDescr => 1, 25 | hasDescr => 0, 26 | hasRefs => 0, 27 | osmask => 0x10, #Win7 28 | version => 20120925); 29 | 30 | sub getConfig{return \%config} 31 | sub getShortDescr { 32 | return "Gets contents of user's typedpaths key"; 33 | } 34 | sub getDescr{} 35 | sub getRefs {} 36 | sub getHive {return $config{hive};} 37 | sub getVersion {return $config{version};} 38 | 39 | my $VERSION = getVersion(); 40 | 41 | sub pluginmain { 42 | my $class = shift; 43 | my $parent = ::getConfig(); 44 | 45 | ::logMsg("typedpaths v.".$VERSION); 46 | ::rptMsg("-" x 60); 47 | ::rptMsg("typedpaths v.".$VERSION); 48 | ::rptMsg(getShortDescr()); 49 | ::rptMsg("Category: ".$config{category}); 50 | ::rptMsg(""); 51 | 52 | my $profile = $parent->{userprofile}; 53 | ::rptMsg("Profile: ".$profile); 54 | # my @u = split(/\\/,$profile); 55 | # my $n = scalar(@u) - 1; 56 | # my $user = $u[$n]; 57 | 58 | $profile .= "\\" unless ($profile =~ m/\\$/); 59 | my $hive = $profile."NTUSER\.DAT"; 60 | 61 | my $reg = Parse::Win32Registry->new($hive); 62 | my $root_key = $reg->get_root_key; 63 | 64 | my $key_path = "Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\TypedPaths"; 65 | my $key; 66 | if ($key = $root_key->get_subkey($key_path)) { 67 | ::rptMsg($key_path); 68 | ::rptMsg("LastWrite Time ".gmtime($key->get_timestamp())." (UTC)"); 69 | ::rptMsg(""); 70 | my @vals = $key->get_list_of_values(); 71 | if (scalar(@vals) > 0) { 72 | my %paths; 73 | foreach my $v (@vals) { 74 | my $name = $v->get_name(); 75 | $name =~ s/^url//; 76 | my $data = $v->get_data(); 77 | $paths{$name} = $data; 78 | } 79 | foreach my $p (sort {$a <=> $b} keys %paths) { 80 | ::rptMsg(sprintf "%-8s %-30s","url".$p,$paths{$p}); 81 | } 82 | } 83 | else { 84 | ::rptMsg($key_path." has no values."); 85 | } 86 | } 87 | else { 88 | ::rptMsg($key_path." not found."); 89 | } 90 | } 91 | 92 | 1; -------------------------------------------------------------------------------- /plugins/runmru.pl: -------------------------------------------------------------------------------- 1 | #----------------------------------------------------------- 2 | # runmru.pl 3 | # Plugin for Registry Ripper, NTUSER.DAT edition - gets the 4 | # RunMru values 5 | # 6 | # Change history 7 | # 20120925 - updated to RS format 8 | # 20080324 - created 9 | # 10 | # References 11 | # 12 | # 13 | # copyright 2012 Quantum Analytics Research, LLC 14 | # Author: H. Carvey, keydet89@yahoo.com 15 | #----------------------------------------------------------- 16 | package runmru; 17 | use strict; 18 | 19 | my %config = (hive => "NTUSER\.DAT", 20 | hivemask => 0x10, 21 | type => "Reg", 22 | output => "report", 23 | class => 1, 24 | category => "User Activity", 25 | hasShortDescr => 1, 26 | hasDescr => 0, 27 | hasRefs => 0, 28 | osmask => 31, 29 | version => 20120925); 30 | 31 | sub getConfig{return \%config} 32 | sub getShortDescr { 33 | return "Gets contents of user's RunMRU key"; 34 | } 35 | sub getDescr{} 36 | sub getRefs {} 37 | sub getHive {return $config{hive};} 38 | sub getVersion {return $config{version};} 39 | 40 | my $VERSION = getVersion(); 41 | 42 | sub pluginmain { 43 | my $class = shift; 44 | my $parent = ::getConfig(); 45 | 46 | ::logMsg("runmru v.".$VERSION); 47 | ::rptMsg("-" x 60); 48 | ::rptMsg("runmru v.".$VERSION); 49 | ::rptMsg(getShortDescr()); 50 | ::rptMsg("Category: ".$config{category}); 51 | ::rptMsg(""); 52 | 53 | my $profile = $parent->{userprofile}; 54 | ::rptMsg("Profile: ".$profile); 55 | # my @u = split(/\\/,$profile); 56 | # my $n = scalar(@u) - 1; 57 | # my $user = $u[$n]; 58 | 59 | $profile .= "\\" unless ($profile =~ m/\\$/); 60 | my $hive = $profile."NTUSER\.DAT"; 61 | my $reg = Parse::Win32Registry->new($hive); 62 | my $root_key = $reg->get_root_key; 63 | 64 | my $key_path = 'Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\RunMRU'; 65 | my $key; 66 | if ($key = $root_key->get_subkey($key_path)) { 67 | ::rptMsg($key_path); 68 | ::rptMsg("LastWrite Time ".gmtime($key->get_timestamp())." (UTC)"); 69 | my @vals = $key->get_list_of_values(); 70 | my %runvals; 71 | my $mru; 72 | if (scalar(@vals) > 0) { 73 | foreach my $v (@vals) { 74 | $runvals{$v->get_name()} = $v->get_data() unless ($v->get_name() =~ m/^MRUList/i); 75 | $mru = $v->get_data() if ($v->get_name() =~ m/^MRUList/i); 76 | } 77 | ::rptMsg("MRUList = ".$mru); 78 | foreach my $r (sort keys %runvals) { 79 | ::rptMsg($r." ".$runvals{$r}); 80 | } 81 | } 82 | else { 83 | ::rptMsg($key_path." has no values."); 84 | } 85 | } 86 | else { 87 | ::rptMsg($key_path." not found."); 88 | } 89 | } 90 | 91 | 1; -------------------------------------------------------------------------------- /plugins/direct.pl: -------------------------------------------------------------------------------- 1 | #----------------------------------------------------------- 2 | # direct.pl 3 | # This plugin runs through the Direct* subkeys beneath the Microsoft key 4 | # in the Software hive (as well as the Wow6432Node key, if it exists) and 5 | # looks to see if there is a MostRecentApplication subkey; if there is, it 6 | # then tries to retrieve the "Name" value/data 7 | # 8 | # History: 9 | # 20120925 - updated to RS format 10 | # 20120513 - created 11 | # 12 | # copyright 2012 Quantum Analytics Research, LLC 13 | # Author: H. Carvey, keydet89@yahoo.com 14 | #----------------------------------------------------------- 15 | package direct; 16 | use strict; 17 | 18 | my %config = (hive => "Software", 19 | hivemask => 0x08, 20 | hasShortDescr => 1, 21 | class => 0, 22 | category => "Program Execution", 23 | type => "Reg", 24 | output => "report", 25 | hasDescr => 0, 26 | hasRefs => 1, 27 | osmask => 31, #XP - Win7 28 | version => 20120925); 29 | 30 | sub getConfig{return \%config} 31 | sub getShortDescr { 32 | return "Searches Direct* keys for MostRecentApplication subkeys"; 33 | } 34 | sub getDescr{} 35 | sub getHive {return $config{hive};} 36 | sub getVersion {return $config{version};} 37 | 38 | my $VERSION = getVersion(); 39 | 40 | sub pluginmain { 41 | my $class = shift; 42 | my $parent = ::getConfig(); 43 | 44 | ::logMsg("direct v.".$VERSION); 45 | ::rptMsg("-" x 60); 46 | ::rptMsg("direct v.".$VERSION); 47 | ::rptMsg(getShortDescr()); 48 | ::rptMsg("Category: ".$config{category}); 49 | ::rptMsg(""); 50 | 51 | my $hive = $parent->{software}; 52 | 53 | my @keys = ('Microsoft','Wow6432Node\\Microsoft'); 54 | my $reg = Parse::Win32Registry->new($hive); 55 | my $root_key = $reg->get_root_key; 56 | 57 | foreach my $key_path (@keys) { 58 | my $key; 59 | if ($key = $root_key->get_subkey($key_path)) { 60 | ::rptMsg($key_path); 61 | # ::rptMsg("LastWrite Time ".gmtime($key->get_timestamp())." (UTC)"); 62 | # ::rptMsg(""); 63 | my @subkeys = $key->get_list_of_subkeys(); 64 | if (scalar(@subkeys) > 0) { 65 | foreach my $s (@subkeys) { 66 | next unless ($s->get_name() =~ m/^Direct/); 67 | my $name = $s->get_name(); 68 | 69 | eval { 70 | my $app; 71 | $app = $s->get_subkey("MostRecentApplication"); 72 | my $app_lw = gmtime($app->get_timestamp()); 73 | my $app_name = $app->get_value("Name")->get_data(); 74 | ::rptMsg(sprintf "%-25s %-50s",$app_lw,$s->get_name()."\\".$app->get_name()." - ".$app_name); 75 | 76 | }; 77 | } 78 | } 79 | else { 80 | ::rptMsg($key_path." has no subkeys."); 81 | } 82 | ::rptMsg(""); 83 | } 84 | else { 85 | ::rptMsg($key_path." not found."); 86 | } 87 | } 88 | } 89 | 1; -------------------------------------------------------------------------------- /plugins/mndmru.pl: -------------------------------------------------------------------------------- 1 | #----------------------------------------------------------- 2 | # mndmru.pl 3 | # Plugin for Registry Ripper, 4 | # Map Network Drive MRU parser 5 | # 6 | # Change history 7 | # 20120925 - updated to RS format 8 | # 20080324 - created 9 | # 10 | # References 11 | # 12 | # copyright 2012 Quantum Analytics Research, LLC 13 | # Author: H. Carvey, keydet89@yahoo.com 14 | #----------------------------------------------------------- 15 | package mndmru; 16 | use strict; 17 | 18 | my %config = (hive => "NTUSER\.DAT", 19 | hivemask => 0x10, 20 | type => "Reg", 21 | output => "report", 22 | class => 1, 23 | category => "User Network Access", 24 | hasShortDescr => 1, 25 | hasDescr => 0, 26 | hasRefs => 0, 27 | osmask => 31, 28 | version => 20120925); 29 | 30 | sub getConfig{return \%config} 31 | sub getShortDescr { 32 | return "Get contents of user's Map Network Drive MRU"; 33 | } 34 | sub getDescr{} 35 | sub getRefs {} 36 | sub getHive {return $config{hive};} 37 | sub getVersion {return $config{version};} 38 | 39 | my $VERSION = getVersion(); 40 | 41 | sub pluginmain { 42 | my $class = shift; 43 | my $parent = ::getConfig(); 44 | 45 | ::logMsg("mndmru v.".$VERSION); 46 | ::rptMsg("-" x 60); 47 | ::rptMsg("mndmru v.".$VERSION); 48 | ::rptMsg(getShortDescr()); 49 | ::rptMsg("Category: ".$config{category}); 50 | ::rptMsg(""); 51 | 52 | my $profile = $parent->{userprofile}; 53 | ::rptMsg("Profile: ".$profile); 54 | # my @u = split(/\\/,$profile); 55 | # my $n = scalar(@u) - 1; 56 | # my $user = $u[$n]; 57 | $profile .= "\\" unless ($profile =~ m/\\$/); 58 | my $ntuser = $profile."NTUSER\.DAT"; 59 | 60 | my $reg = Parse::Win32Registry->new($ntuser); 61 | my $root_key = $reg->get_root_key; 62 | 63 | my $key_path = 'Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Map Network Drive MRU'; 64 | my $key; 65 | if ($key = $root_key->get_subkey($key_path)) { 66 | ::rptMsg("Map Network Drive MRU"); 67 | ::rptMsg($key_path); 68 | ::rptMsg("LastWrite Time ".gmtime($key->get_timestamp())." (UTC)"); 69 | my @vals = $key->get_list_of_values(); 70 | if (scalar(@vals) > 0) { 71 | my %mnd; 72 | # Retrieve values and load into a hash for sorting 73 | foreach my $v (@vals) { 74 | my $val = $v->get_name(); 75 | my $data = $v->get_data(); 76 | $mnd{$val} = $data; 77 | } 78 | # Print sorted content to report file 79 | if (exists $mnd{"MRUList"}) { 80 | ::rptMsg(" MRUList = ".$mnd{"MRUList"}); 81 | delete $mnd{"MRUList"}; 82 | } 83 | foreach my $m (sort {$a <=> $b} keys %mnd) { 84 | ::rptMsg(" ".$m." ".$mnd{$m}); 85 | } 86 | } 87 | else { 88 | ::rptMsg($key_path." has no values."); 89 | } 90 | } 91 | else { 92 | ::rptMsg($key_path." not found."); 93 | } 94 | } 95 | 96 | 1; -------------------------------------------------------------------------------- /plugins/appcertdlls.pl: -------------------------------------------------------------------------------- 1 | #----------------------------------------------------------- 2 | # appcertdlls.pl 3 | # 4 | # History: 5 | # 20120925 - updated to RS format 6 | # 20120912 - created 7 | # 8 | # References: 9 | # Blog post: https://blog.mandiant.com/archives/2459 10 | # Whitepaper: http://fred.mandiant.com/Whitepaper_ShimCacheParser.pdf 11 | # Tool: https://github.com/mandiant/ShimCacheParser 12 | # 13 | # This plugin is based solely on the work and examples provided by Mandiant; 14 | # thanks to them for sharing this information, and making the plugin possible. 15 | # 16 | # copyright 2012 Quantum Analytics Research, LLC 17 | # Author: H. Carvey, keydet89@yahoo.com 18 | #----------------------------------------------------------- 19 | package appcertdlls; 20 | use strict; 21 | 22 | my %config = (hive => "System", 23 | hivemask => 4, 24 | class => 0, #system = 0, user = 1 25 | type => "Reg", 26 | output => "report", 27 | category => "Malware", 28 | hasShortDescr => 1, 29 | hasDescr => 0, 30 | hasRefs => 0, 31 | osmask => 31, #XP - Win7 32 | version => 20120925); 33 | 34 | sub getConfig{return \%config} 35 | sub getShortDescr { 36 | return "Get entries from AppCertDlls key"; 37 | } 38 | sub getDescr{} 39 | sub getRefs {} 40 | sub getHive {return $config{hive};} 41 | sub getVersion {return $config{version};} 42 | 43 | my $VERSION = getVersion(); 44 | my %files; 45 | my @temps; 46 | 47 | sub pluginmain { 48 | my $class = shift; 49 | my $parent = ::getConfig(); 50 | ::logMsg("appcertdlls v.".$VERSION); 51 | ::rptMsg("-" x 60); 52 | ::rptMsg("appcertdlls v.".$VERSION); 53 | ::rptMsg(getShortDescr()); 54 | ::rptMsg("Category: ".$config{category}); 55 | ::rptMsg(""); 56 | 57 | my $reg = Parse::Win32Registry->new($parent->{system}); 58 | my $root_key = $reg->get_root_key; 59 | # First thing to do is get the ControlSet00x marked current...this is 60 | # going to be used over and over again in plugins that access the system 61 | # file 62 | my ($current,$ccs); 63 | my $key_path = 'Select'; 64 | my $key; 65 | if ($key = $root_key->get_subkey($key_path)) { 66 | $current = $key->get_value("Current")->get_data(); 67 | $ccs = "ControlSet00".$current; 68 | my $appcert_path = $ccs."\\Control\\Session Manager\\AppCertDlls"; 69 | my $appcert; 70 | if ($appcert = $root_key->get_subkey($appcert_path)) { 71 | my @vals = $appcert->get_list_of_values(); 72 | if (scalar(@vals) > 0) { 73 | foreach my $v (@vals) { 74 | my $name = $v->get_name(); 75 | my $data = $v->get_data(); 76 | ::rptMsg($name." - ".$data); 77 | } 78 | } 79 | else { 80 | ::rptMsg($appcert_path."has no values."); 81 | } 82 | } 83 | else { 84 | ::rptMsg($appcert_path." not found."); 85 | } 86 | } 87 | else { 88 | ::rptMsg($key_path." not found."); 89 | } 90 | } 91 | 92 | 1; -------------------------------------------------------------------------------- /plugins/sysinternals.pl: -------------------------------------------------------------------------------- 1 | #----------------------------------------------------------- 2 | # sysinternals.pl 3 | # When a user installs and runs the SysInternals tools from MS, 4 | # they have to accept a EULA in order to run the tools. When they 5 | # do, this creates a Registry key for the tool, with a value indicating 6 | # that the EULA was accepted. 7 | # 8 | # Change history 9 | # 20120925 - updated to RS format 10 | # 20120608 - created 11 | # 12 | # References 13 | # 14 | # 15 | # copyright 2012 Quantum Analytics Research, LLC 16 | # Author: H. Carvey, keydet89@yahoo.com 17 | #----------------------------------------------------------- 18 | package sysinternals; 19 | use strict; 20 | 21 | my %config = (hive => "NTUSER\.DAT", 22 | hivemask => 0x10, 23 | type => "Reg", 24 | category => "User Activity", 25 | class => 1, 26 | output => "report", 27 | hasShortDescr => 1, 28 | hasDescr => 0, 29 | hasRefs => 0, 30 | osmask => 63,#XP - Win8 31 | version => 20120925); 32 | 33 | sub getConfig{return \%config} 34 | sub getShortDescr { 35 | return "Checks for SysInternals apps keys"; 36 | } 37 | sub getDescr{} 38 | sub getRefs {} 39 | sub getHive {return $config{hive};} 40 | sub getVersion {return $config{version};} 41 | 42 | my $VERSION = getVersion(); 43 | 44 | sub pluginmain { 45 | my $class = shift; 46 | my $parent = ::getConfig(); 47 | ::logMsg("sysinternals v.".$VERSION); 48 | ::rptMsg("-" x 60); 49 | ::rptMsg("sysinternals v.".$VERSION); 50 | ::rptMsg(getShortDescr()); 51 | ::rptMsg("Category: ".$config{category}); 52 | ::rptMsg(""); 53 | 54 | my $profile = $parent->{userprofile}; 55 | ::rptMsg("Profile: ".$profile); 56 | # my @u = split(/\\/,$profile); 57 | # my $n = scalar(@u) - 1; 58 | # my $user = $u[$n]; 59 | 60 | $profile .= "\\" unless ($profile =~ m/\\$/); 61 | my $hive = $profile."NTUSER\.DAT"; 62 | my $reg = Parse::Win32Registry->new($hive); 63 | my $root_key = $reg->get_root_key; 64 | 65 | my $key_path = 'Software\\SysInternals'; 66 | my $key; 67 | if ($key = $root_key->get_subkey($key_path)) { 68 | ::rptMsg("SysInternals"); 69 | ::rptMsg($key_path); 70 | ::rptMsg("LastWrite Time ".gmtime($key->get_timestamp())." (UTC)"); 71 | my @subkeys = $key->get_list_of_subkeys(); 72 | if (scalar(@subkeys) > 0) { 73 | foreach my $s (@subkeys) { 74 | ::rptMsg($s->get_name()." [".gmtime($s->get_timestamp())." (UTC)]"); 75 | 76 | my $eula; 77 | eval { 78 | $eula = $s->get_value("EulaAccepted")->get_data(); 79 | }; 80 | if ($@) { 81 | ::rptMsg(" EulaAccepted value not found."); 82 | } 83 | else { 84 | ::rptMsg(" EulaAccepted: ".$eula); 85 | } 86 | ::rptMsg(""); 87 | } 88 | } 89 | else { 90 | ::rptMsg($key_path." has no subkeys."); 91 | } 92 | } 93 | else { 94 | ::rptMsg($key_path." not found."); 95 | } 96 | } 97 | 98 | 1; -------------------------------------------------------------------------------- /plugins/muicache.pl: -------------------------------------------------------------------------------- 1 | #! c:\perl\bin\perl.exe 2 | #----------------------------------------------------------- 3 | # muicache.pl 4 | # Gets user's MUICache values that do not start with '@' 5 | # 6 | # Change history 7 | # 20120925 - updated to RS format 8 | # 20120522 - updated to collect info from Win7 USRCLASS.DAT 9 | # 10 | # 11 | # copyright 2012 Quantum Research Analytics, LLC 12 | # Author: H. Carvey, keydet89@yahoo.com 13 | #----------------------------------------------------------- 14 | package muicache; 15 | use strict; 16 | 17 | my %config = (hive => "NTUSER\.DAT,USRCLASS\.DAT", 18 | hivemask => 48, #NTUSER.DAT or USRCLASS.DAT, depending upon the OS version 19 | type => "Reg", 20 | class => 1, # User 21 | output => "report", 22 | category => "Program Execution", 23 | hasShortDescr => 1, 24 | hasDescr => 0, 25 | hasRefs => 0, 26 | osmask => 23, #XP,2003,Vista,Win7/2008R2 27 | version => 20120925); 28 | 29 | sub getConfig{return \%config} 30 | sub getShortDescr { 31 | return "Gets EXEs from user's MUICache key (entries that do not start with \"@\")"; 32 | } 33 | sub getDescr{} 34 | sub getRefs {} 35 | sub getHive {return $config{hive};} 36 | sub getVersion {return $config{version};} 37 | 38 | my $VERSION = getVersion(); 39 | 40 | sub pluginmain { 41 | my $class = shift; 42 | my $parent = ::getConfig(); 43 | my $profile = $parent->{userprofile}; 44 | 45 | ::logMsg("muicache v.".$VERSION); 46 | ::rptMsg("-" x 60); 47 | ::rptMsg("muicache v.".$VERSION); 48 | ::rptMsg(getShortDescr()); 49 | ::rptMsg("Category: ".$config{category}); 50 | ::rptMsg(""); 51 | ::rptMsg("Profile: ".$profile); 52 | $profile .= "\\" unless ($profile =~ m/\\$/); 53 | ::rptMsg(""); 54 | 55 | my $hive; 56 | my $key_path; 57 | 58 | if ($parent->{"CurrentVersion"} >= 6.0) { 59 | $hive = $profile."AppData\\Local\\Microsoft\\Windows\\USRCLASS\.DAT"; 60 | $key_path = 'Local Settings\\Software\\Microsoft\\Windows\\Shell\\MUICache'; 61 | } 62 | else { 63 | $hive = $profile."NTUSER\.DAT"; 64 | $key_path = 'Software\\Microsoft\\Windows\\ShellNoRoam\\MUICache'; 65 | } 66 | 67 | my $reg = Parse::Win32Registry->new($hive); 68 | my $root_key = $reg->get_root_key; 69 | # my $key_path = 'Software\\Microsoft\\Windows\\ShellNoRoam\\MUICache'; 70 | my $key; 71 | if ($key = $root_key->get_subkey($key_path)) { 72 | ::rptMsg($key_path); 73 | ::rptMsg("LastWrite Time ".gmtime($key->get_timestamp())." (UTC)"); 74 | my @vals = $key->get_list_of_values(); 75 | if (scalar(@vals) > 0) { 76 | foreach my $v (@vals) { 77 | my $name = $v->get_name(); 78 | next if ($name =~ m/^@/ || $name eq "LangID"); 79 | my $data = $v->get_data(); 80 | ::rptMsg("\t".$name." (".$data.")"); 81 | } 82 | } 83 | else { 84 | ::rptMsg($key_path." has no values."); 85 | } 86 | } 87 | else { 88 | ::rptMsg($key_path." not found."); 89 | ::rptMsg(""); 90 | } 91 | 92 | } 93 | 1; -------------------------------------------------------------------------------- /plugins/usertemp.pl: -------------------------------------------------------------------------------- 1 | package usertemp; 2 | #----------------------------------------------------------- 3 | # usertemp - Plugin to parse through user profile Temp folder looking 4 | # for suspicious files. 5 | # 6 | # XP/2003: Local Settings\Temp 7 | # Vista+ : AppData\Local\Temp 8 | # 9 | # History 10 | # 20120823 - renamed; was "localsettings.pl", changed to current name 11 | # 20120822 - updated to current format 12 | # 20100927 - created 13 | # 14 | # copyright 2012 15 | # Author: H. Carvey, keydet89@yahoo.com 16 | #----------------------------------------------------------- 17 | use strict; 18 | 19 | my %config = (hasShortDescr => 1, 20 | category => "Malware", 21 | shortDescr => "Parse users Local Settings\\Temp dirs for suspicious files", 22 | type => "File", 23 | class => 1, 24 | output => "report", 25 | osmask => 31, 26 | version => 20120822); 27 | 28 | sub getConfig{return \%config}; 29 | my $VERSION = $config{version}; 30 | sub getShortDescr { 31 | return $config{shortDescr}; 32 | } 33 | sub pluginmain { 34 | my $class = shift; 35 | my $parent = ::getConfig(); 36 | 37 | ::logMsg("usertemp v.".$VERSION); 38 | ::rptMsg("-" x 60); 39 | ::rptMsg("usertemp v.".$VERSION); 40 | ::rptMsg(getShortDescr()); 41 | ::rptMsg("Category: ".$config{category}); 42 | ::rptMsg(""); 43 | my $profile = $parent->{userprofile}; 44 | ::rptMsg("Profile: ".$profile); 45 | $profile .= "\\" unless ($profile =~ m/\\$/); 46 | 47 | my $temp; 48 | if ($parent->{CurrentVersion} >= 6.0) { 49 | $temp = $profile."AppData\\Local\\Temp\\"; 50 | } 51 | else { 52 | $temp = $profile."Local Settings\\Temp\\"; 53 | } 54 | 55 | checkTmp($temp); 56 | checkExe($temp); 57 | 58 | } 59 | 60 | sub checkTmp { 61 | my $path = shift; 62 | my $win = WinFile->new(); 63 | my @files; 64 | 65 | opendir(DIR,$path); 66 | @files = map{$path.$_}(grep(/\.tmp$/,readdir(DIR))); 67 | closedir(DIR); 68 | 69 | if (scalar @files > 0) { 70 | foreach my $f (@files) { 71 | if ($win->isMZSig($f)) { 72 | ::rptMsg("File with \.tmp extension and MZ signature:"); 73 | ::rptMsg(" ".$f); 74 | ::rptMsg(" MD5: ".$win->getMD5($f)); 75 | ::rptMsg(""); 76 | } 77 | } 78 | } 79 | else { 80 | ::rptMsg("No files with \.tmp extension found."); 81 | ::rptMsg(""); 82 | } 83 | } 84 | 85 | sub checkExe { 86 | my $path = shift; 87 | my $win = WinFile->new(); 88 | my @files; 89 | 90 | opendir(DIR,$path); 91 | @files = map{$path.$_}(grep(/\.exe$/,readdir(DIR))); 92 | closedir(DIR); 93 | 94 | if (scalar @files > 0) { 95 | foreach my $f (@files) { 96 | if ($win->isMZSig($f)) { 97 | ::rptMsg("File with \.exe extension found:"); 98 | ::rptMsg(" ".$f); 99 | ::rptMsg(" MD5: ".$win->getMD5($f)); 100 | ::rptMsg(""); 101 | } 102 | } 103 | } 104 | else { 105 | ::rptMsg("No files with a \.exe extension found."); 106 | ::rptMsg(""); 107 | } 108 | } 109 | 110 | 1; -------------------------------------------------------------------------------- /time.pl: -------------------------------------------------------------------------------- 1 | #------------------------------------------------------------- 2 | # time.pl 3 | # This file contains helper functions for translating time values 4 | # into something readable. This file is accessed by the main UI 5 | # code via the 'require' pragma. 6 | # 7 | # Note: The main UI code (GUI or CLI) must 'use' the Time::Local 8 | # module. 9 | # 10 | # Change history: 11 | # 20120925 - created 12 | # 13 | # copyright 2012 Quantum Analytics Research, LLC 14 | # Author: H. Carvey, keydet89@yahoo.com 15 | # This software is released under the Perl Artistic License: 16 | # http://dev.perl.org/licenses/artistic.html 17 | #------------------------------------------------------------- 18 | 19 | #------------------------------------------------------------- 20 | # getTime() 21 | # Translate FILETIME object (2 DWORDS) to Unix time, to be passed 22 | # to gmtime() or localtime() 23 | # 24 | # The code was borrowed from Andreas Schuster's excellent work 25 | #------------------------------------------------------------- 26 | sub getTime($$) { 27 | my $lo = $_[0]; 28 | my $hi = $_[1]; 29 | my $t; 30 | 31 | if ($lo == 0 && $hi == 0) { 32 | $t = 0; 33 | } else { 34 | $lo -= 0xd53e8000; 35 | $hi -= 0x019db1de; 36 | $t = int($hi*429.4967296 + $lo/1e7); 37 | }; 38 | $t = 0 if ($t < 0); 39 | return $t; 40 | } 41 | 42 | #----------------------------------------------------------- 43 | # convertDOSDate() 44 | # subroutine to convert 4 bytes of binary data into a human- 45 | # readable format. Returns both a string and a Unix-epoch 46 | # time. 47 | #----------------------------------------------------------- 48 | sub convertDOSDate { 49 | my $date = shift; 50 | my $time = shift; 51 | 52 | if ($date == 0x00 || $time == 0x00){ 53 | return (0,0); 54 | } 55 | else { 56 | my $sec = ($time & 0x1f) * 2; 57 | $sec = "0".$sec if (length($sec) == 1); 58 | if ($sec == 60) {$sec = 59}; 59 | my $min = ($time & 0x7e0) >> 5; 60 | $min = "0".$min if (length($min) == 1); 61 | my $hr = ($time & 0xF800) >> 11; 62 | $hr = "0".$hr if (length($hr) == 1); 63 | my $day = ($date & 0x1f); 64 | $day = "0".$day if (length($day) == 1); 65 | my $mon = ($date & 0x1e0) >> 5; 66 | $mon = "0".$mon if (length($mon) == 1); 67 | my $yr = (($date & 0xfe00) >> 9) + 1980; 68 | my $gmtime = timegm($sec,$min,$hr,$day,($mon - 1),$yr); 69 | return ("$yr-$mon-$day $hr:$min:$sec",$gmtime); 70 | # return gmtime(timegm($sec,$min,$hr,$day,($mon - 1),$yr)); 71 | } 72 | } 73 | 74 | #----------------------------------------------------------- 75 | # convertSystemTime() 76 | # Converts 128-bit SYSTEMTIME object to readable format 77 | #----------------------------------------------------------- 78 | sub convertSystemTime { 79 | my $date = $_[0]; 80 | my @months = ("Jan","Feb","Mar","Apr","May","Jun","Jul", 81 | "Aug","Sep","Oct","Nov","Dec"); 82 | my @days = ("Sun","Mon","Tue","Wed","Thu","Fri","Sat"); 83 | my ($yr,$mon,$dow,$dom,$hr,$min,$sec,$ms) = unpack("v*",$date); 84 | $hr = "0".$hr if ($hr < 10); 85 | $min = "0".$min if ($min < 10); 86 | $sec = "0".$sec if ($sec < 10); 87 | my $str = $days[$dow]." ".$months[$mon - 1]." ".$dom." ".$hr.":".$min.":".$sec." ".$yr; 88 | return $str; 89 | } 90 | 91 | 1; -------------------------------------------------------------------------------- /plugins/wordwheelquery.pl: -------------------------------------------------------------------------------- 1 | #----------------------------------------------------------- 2 | # wordwheelquery.pl 3 | # For Windows 7 only; gets user's Desktop search MRU 4 | # 5 | # Change history 6 | # 20120925 - updated to RS format 7 | # 20100330 - created 8 | # 9 | # References 10 | # http://www.winhelponline.com/blog/clear-file-search-mru-history-windows-7/ 11 | # 12 | # copyright 2012 Quantum Analytics Research, LLC 13 | # Author: H. Carvey, keydet89@yahoo.com 14 | #----------------------------------------------------------- 15 | package wordwheelquery; 16 | use strict; 17 | 18 | my %config = (hive => "NTUSER\.DAT", 19 | hivemask => 0x10, 20 | type => "Reg", 21 | output => "report", 22 | class => 1, 23 | category => "User Activity", 24 | hasShortDescr => 1, 25 | hasDescr => 0, 26 | hasRefs => 0, 27 | osmask => 0x10, 28 | version => 20120925); 29 | 30 | sub getConfig{return \%config} 31 | sub getShortDescr { 32 | return "Gets contents of user's WordWheelQuery key"; 33 | } 34 | sub getDescr{} 35 | sub getRefs {} 36 | sub getHive {return $config{hive};} 37 | sub getVersion {return $config{version};} 38 | 39 | my $VERSION = getVersion(); 40 | 41 | sub pluginmain { 42 | my $class = shift; 43 | my $parent = ::getConfig(); 44 | 45 | ::logMsg("wordwheelquery v.".$VERSION); 46 | ::rptMsg("-" x 60); 47 | ::rptMsg("wordwheelquery v.".$VERSION); 48 | ::rptMsg(getShortDescr()); 49 | ::rptMsg("Category: ".$config{category}); 50 | ::rptMsg(""); 51 | 52 | my $profile = $parent->{userprofile}; 53 | ::rptMsg("Profile: ".$profile); 54 | # my @u = split(/\\/,$profile); 55 | # my $n = scalar(@u) - 1; 56 | # my $user = $u[$n]; 57 | 58 | $profile .= "\\" unless ($profile =~ m/\\$/); 59 | my $hive = $profile."NTUSER\.DAT"; 60 | my $reg = Parse::Win32Registry->new($hive); 61 | my $root_key = $reg->get_root_key; 62 | 63 | my $key_path = "Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\WordWheelQuery"; 64 | my $key; 65 | if ($key = $root_key->get_subkey($key_path)) { 66 | ::rptMsg($key_path); 67 | ::rptMsg("LastWrite Time ".gmtime($key->get_timestamp())." (UTC)"); 68 | my @vals = $key->get_list_of_values(); 69 | if (scalar(@vals) > 0) { 70 | my @list; 71 | my %wwq; 72 | foreach my $v (@vals) { 73 | my $name = $v->get_name(); 74 | if ($name eq "MRUListEx") { 75 | @list = unpack("V*",$v->get_data()); 76 | pop(@list) if ($list[scalar(@list) - 1] == 0xffffffff); 77 | } 78 | else { 79 | my $data = $v->get_data(); 80 | $data =~ s/\00//g; 81 | $wwq{$name} = $data; 82 | } 83 | } 84 | # list searches in MRUListEx order 85 | ::rptMsg(""); 86 | ::rptMsg("Searches listed in MRUListEx order"); 87 | ::rptMsg(""); 88 | foreach my $l (@list) { 89 | ::rptMsg(sprintf "%-4d %-30s",$l,$wwq{$l}); 90 | } 91 | } 92 | else { 93 | ::rptMsg($key_path." has no values."); 94 | } 95 | } 96 | else { 97 | ::rptMsg($key_path." not found."); 98 | } 99 | } 100 | 101 | 1; -------------------------------------------------------------------------------- /plugins/winlogon.pl: -------------------------------------------------------------------------------- 1 | #----------------------------------------------------------- 2 | # WinLogon 3 | # Get values from WinLogon key 4 | # 5 | # History 6 | # 20120925 - updated to RS format 7 | # 20100219 - Updated output to better present some data 8 | # 20080415 - created 9 | # 10 | # copyright 2010 Quantum Analytics Research, LLC 11 | #----------------------------------------------------------- 12 | package winlogon; 13 | use strict; 14 | 15 | my %config = (hive => "Software", 16 | hivemask => 0x08, 17 | type => "Reg", 18 | class => 0, 19 | output => "report", 20 | category => "System Config", 21 | osmask => 63, #XP - Win8 22 | hasShortDescr => 1, 23 | hasDescr => 0, 24 | hasRefs => 0, 25 | version => 20100925); 26 | 27 | sub getConfig{return \%config} 28 | 29 | sub getShortDescr { 30 | return "Get values from the HKLM\\\.\.\\WinLogon key"; 31 | } 32 | sub getDescr{} 33 | sub getRefs {} 34 | sub getHive {return $config{hive};} 35 | sub getVersion {return $config{version};} 36 | 37 | my $VERSION = getVersion(); 38 | 39 | sub pluginmain { 40 | my $class = shift; 41 | my $parent = ::getConfig(); 42 | 43 | ::logMsg("winlogon v.".$VERSION); 44 | ::rptMsg("-" x 60); 45 | ::rptMsg("winlogon v.".$VERSION); 46 | ::rptMsg(getShortDescr()); 47 | ::rptMsg("Category: ".$config{category}); 48 | ::rptMsg(""); 49 | 50 | my $hive = $parent->{software}; 51 | my $reg = Parse::Win32Registry->new($hive); 52 | my $root_key = $reg->get_root_key; 53 | my $key_path = "Microsoft\\Windows NT\\CurrentVersion\\Winlogon"; 54 | my $key; 55 | if ($key = $root_key->get_subkey($key_path)) { 56 | ::rptMsg($key_path); 57 | ::rptMsg("LastWrite Time ".gmtime($key->get_timestamp())." (UTC)"); 58 | 59 | my @vals = $key->get_list_of_values(); 60 | if (scalar(@vals) > 0) { 61 | my %wl; 62 | foreach my $v (@vals) { 63 | my $name = $v->get_name(); 64 | my $data = $v->get_data(); 65 | my $len = length($data); 66 | next if ($name eq ""); 67 | if ($v->get_type() == 3 && $name ne "DCacheUpdate") { 68 | $data = _translateBinary($data); 69 | } 70 | 71 | $data = sprintf "0x%x",$data if ($name eq "SfcQuota"); 72 | if ($name eq "DCacheUpdate") { 73 | my @v = unpack("VV",$data); 74 | $data = gmtime(::getTime($v[0],$v[1])); 75 | } 76 | 77 | push(@{$wl{$len}},$name." = ".$data); 78 | } 79 | 80 | foreach my $t (sort {$a <=> $b} keys %wl) { 81 | foreach my $item (@{$wl{$t}}) { 82 | ::rptMsg(" $item"); 83 | } 84 | } 85 | 86 | ::rptMsg(""); 87 | ::rptMsg("Analysis Tips: The UserInit and Shell values are executed when a user logs on."); 88 | 89 | } 90 | else { 91 | ::rptMsg($key_path." has no values."); 92 | } 93 | } 94 | else { 95 | ::rptMsg($key_path." not found."); 96 | } 97 | } 98 | 99 | sub _translateBinary { 100 | my $str = unpack("H*",$_[0]); 101 | my $len = length($str); 102 | my @nstr = split(//,$str,$len); 103 | my @list = (); 104 | foreach (0..($len/2)) { 105 | push(@list,$nstr[$_*2].$nstr[($_*2)+1]); 106 | } 107 | return join(' ',@list); 108 | } 109 | 1; -------------------------------------------------------------------------------- /plugins/soft_run.pl: -------------------------------------------------------------------------------- 1 | #----------------------------------------------------------- 2 | # soft_run 3 | # Get contents of Run key from Software hive 4 | # 5 | # 6 | # History: 7 | # 20120824 - updated to FS format 8 | # 20120524 - updated to support newer OS's, and 64-bit 9 | # 20080328 - created 10 | # 11 | # 12 | # copyright 2012 Quantum Analytics Research, LLC 13 | # Author: H. Carvey, keydet89@yahoo.com 14 | #----------------------------------------------------------- 15 | package soft_run; 16 | use strict; 17 | 18 | my %config = (hive => "Software", 19 | hivemask => 0x08, 20 | type => "Reg", 21 | category => "AutoStart", 22 | output => "report", 23 | class => 0, 24 | osmask => 63, #XP - Win8 25 | hasShortDescr => 1, 26 | hasDescr => 0, 27 | hasRefs => 1, 28 | version => 20120824); 29 | 30 | sub getConfig{return \%config} 31 | 32 | sub getShortDescr { 33 | return "Get Run key contents from Software hive"; 34 | } 35 | sub getDescr{} 36 | sub getRefs { 37 | my %refs = ("Definition of the Run keys in the WinXP Registry" => 38 | "http://support.microsoft.com/kb/314866"); 39 | return %refs; 40 | } 41 | sub getHive {return $config{hive};} 42 | sub getVersion {return $config{version};} 43 | 44 | my $VERSION = getVersion(); 45 | 46 | sub pluginmain { 47 | my $class = shift; 48 | my $parent = ::getConfig(); 49 | 50 | ::logMsg("soft_run v.".$VERSION); 51 | ::rptMsg("-" x 60); 52 | ::rptMsg("soft_run v.".$VERSION); 53 | ::rptMsg(getShortDescr()); 54 | ::rptMsg("Category: ".$config{category}); 55 | ::rptMsg(""); 56 | 57 | my $hive = $parent->{software}; 58 | 59 | my $reg = Parse::Win32Registry->new($hive); 60 | my $root_key = $reg->get_root_key; 61 | 62 | my @paths = ("Microsoft\\Windows\\CurrentVersion\\Run", 63 | "Wow6432Node\\Microsoft\\Windows\\CurrentVersion\\Run"); 64 | 65 | foreach my $key_path (@paths) { 66 | 67 | my $key; 68 | if ($key = $root_key->get_subkey($key_path)) { 69 | ::rptMsg($key_path); 70 | ::rptMsg("LastWrite Time ".gmtime($key->get_timestamp())." (UTC)"); 71 | 72 | my %vals = getKeyValues($key); 73 | if (scalar(keys %vals) > 0) { 74 | foreach my $v (keys %vals) { 75 | ::rptMsg(" ".$v." - ".$vals{$v}); 76 | } 77 | ::rptMsg(""); 78 | } 79 | else { 80 | ::rptMsg($key_path." has no values."); 81 | } 82 | 83 | my @sk = $key->get_list_of_subkeys(); 84 | if (scalar(@sk) > 0) { 85 | foreach my $s (@sk) { 86 | ::rptMsg(""); 87 | ::rptMsg($key_path."\\".$s->get_name()); 88 | ::rptMsg("LastWrite Time ".gmtime($s->get_timestamp())." (UTC)"); 89 | my %vals = getKeyValues($s); 90 | foreach my $v (keys %vals) { 91 | ::rptMsg(" ".$v." -> ".$vals{$v}); 92 | } 93 | ::rptMsg(""); 94 | } 95 | } 96 | else { 97 | ::rptMsg($key_path." has no subkeys."); 98 | ::rptMsg(""); 99 | } 100 | } 101 | else { 102 | ::rptMsg($key_path." not found."); 103 | ::rptMsg(""); 104 | } 105 | } 106 | } 107 | 108 | sub getKeyValues { 109 | my $key = shift; 110 | my %vals; 111 | 112 | my @vk = $key->get_list_of_values(); 113 | if (scalar(@vk) > 0) { 114 | foreach my $v (@vk) { 115 | next if ($v->get_name() eq "" && $v->get_data() eq ""); 116 | $vals{$v->get_name()} = $v->get_data(); 117 | } 118 | } 119 | else { 120 | 121 | } 122 | return %vals; 123 | } 124 | 125 | 1; -------------------------------------------------------------------------------- /plugins/typedurlstime.pl: -------------------------------------------------------------------------------- 1 | #! c:\perl\bin\perl.exe 2 | #----------------------------------------------------------- 3 | # typedurlstime.pl 4 | # Plugin for Registry Ripper, NTUSER.DAT edition - gets the 5 | # TypedURLsTime values/data from Windows 8 systems 6 | # 7 | # Change history 8 | # 20120824 - updated to FS format 9 | # 20120613 - created 10 | # 11 | # References 12 | # http://dfstream.blogspot.com/2012/05/windows-8-typedurlstime.html 13 | # 14 | # Notes: New entries aren't added to the key until the current 15 | # instance of IE is terminated. 16 | # 17 | # copyright 2012 Quantum Analytics Research, LLC 18 | # Author: H. Carvey, keydet89@yahoo.com 19 | #----------------------------------------------------------- 20 | package typedurlstime; 21 | use strict; 22 | 23 | my %config = (hive => "NTUSER\.DAT", 24 | hivemask => 0x10, 25 | hasShortDescr => 1, 26 | category => "User Activity", 27 | class => 1, 28 | output => "report", 29 | hasDescr => 0, 30 | hasRefs => 1, 31 | osmask => 0x20, #Windows8 32 | version => 20120824); 33 | 34 | sub getConfig{return \%config} 35 | sub getShortDescr { 36 | return "Returns contents of user's TypedURLsTime key."; 37 | } 38 | sub getDescr{} 39 | sub getRefs {} 40 | sub getHive {return $config{hive};} 41 | sub getVersion {return $config{version};} 42 | 43 | my $VERSION = getVersion(); 44 | 45 | sub pluginmain { 46 | my $class = shift; 47 | my $parent = ::getConfig(); 48 | 49 | ::logMsg("typedurlstime v.".$VERSION); 50 | ::rptMsg("-" x 60); 51 | ::rptMsg("typedurlstime v.".$VERSION); 52 | ::rptMsg(getShortDescr()); 53 | ::rptMsg("Category: ".$config{category}); 54 | ::rptMsg(""); 55 | 56 | my $profile = $parent->{userprofile}; 57 | ::rptMsg("Profile: ".$profile); 58 | 59 | my @u = split(/\\/,$profile); 60 | my $n = scalar(@u) - 1; 61 | my $user = $u[$n]; 62 | 63 | $profile .= "\\" unless ($profile =~ m/\\$/); 64 | my $ntuser = $profile."NTUSER\.DAT"; 65 | 66 | my $reg = Parse::Win32Registry->new($ntuser); 67 | my $root_key = $reg->get_root_key; 68 | 69 | my $key_path = 'Software\\Microsoft\\Internet Explorer\\TypedURLsTime'; 70 | my $key; 71 | if ($key = $root_key->get_subkey($key_path)) { 72 | ::rptMsg("TypedURLsTime"); 73 | ::rptMsg($key_path); 74 | ::rptMsg("LastWrite Time ".gmtime($key->get_timestamp())." (UTC)"); 75 | my @vals = $key->get_list_of_values(); 76 | if (scalar(@vals) > 0) { 77 | my %urls; 78 | # Retrieve values and load into a hash for sorting 79 | foreach my $v (@vals) { 80 | my $val = $v->get_name(); 81 | my ($t0,$t1) = unpack("VV",$v->get_data()); 82 | my $data = ::getTime($t0,$t1); 83 | my $tag = (split(/url/,$val))[1]; 84 | $urls{$tag} = $val.":".$data; 85 | } 86 | # Print sorted content to report file 87 | foreach my $u (sort {$a <=> $b} keys %urls) { 88 | my ($val,$data) = split(/:/,$urls{$u},2); 89 | 90 | my $url; 91 | eval { 92 | $url = $root_key->get_subkey('Software\\Microsoft\\Internet Explorer\\TypedURLs')->get_value($val)->get_data(); 93 | }; 94 | 95 | if ($data == 0) { 96 | ::rptMsg(" ".$val." -> ".$data); 97 | } 98 | else { 99 | ::rptMsg(" ".$val." -> ".gmtime($data)." Z (".$url.")"); 100 | } 101 | } 102 | } 103 | else { 104 | ::rptMsg($key_path." has no values."); 105 | } 106 | } 107 | else { 108 | ::rptMsg($key_path." not found."); 109 | } 110 | } 111 | 112 | 1; -------------------------------------------------------------------------------- /plugins/typedurls.pl: -------------------------------------------------------------------------------- 1 | #! c:\perl\bin\perl.exe 2 | #----------------------------------------------------------- 3 | # typedurls.pl 4 | # Plugin for Registry Ripper, NTUSER.DAT edition - gets the 5 | # TypedURLs values 6 | # 7 | # Change history 8 | # 20120925 - updated to RS format 9 | # 20120827 - TLN version created 10 | # 20080324 - created 11 | # 12 | # References 13 | # http://support.microsoft.com/kb/157729 14 | # http://msdn2.microsoft.com/en-us/library/aa908115.aspx 15 | # 16 | # Notes: Reportedly, only the last 20 entries are maintained; 17 | # Also, new entries aren't added to the key until the current 18 | # instance of IE is terminated. 19 | # 20 | # copyright 2008 H. Carvey 21 | #----------------------------------------------------------- 22 | package typedurls; 23 | use strict; 24 | 25 | my %config = (hive => "NTUSER\.DAT", 26 | hivemask => 0x10, 27 | class => 1, 28 | type => "Reg", 29 | output => "report", 30 | category => "User Activity", 31 | hasShortDescr => 1, 32 | hasDescr => 0, 33 | hasRefs => 1, 34 | osmask => 31, #XP-Win7 35 | version => 20120925); 36 | 37 | sub getConfig{return \%config} 38 | sub getShortDescr { 39 | return "Returns contents of user's TypedURLs key."; 40 | } 41 | sub getDescr{} 42 | sub getRefs { 43 | my %refs = ("IESample Registry Settings" => 44 | "http://msdn2.microsoft.com/en-us/library/aa908115.aspx", 45 | "How to clear History entries in IE" => 46 | "http://support.microsoft.com/kb/157729"); 47 | return %refs; 48 | } 49 | sub getHive {return $config{hive};} 50 | sub getVersion {return $config{version};} 51 | 52 | my $VERSION = getVersion(); 53 | 54 | sub pluginmain { 55 | my $class = shift; 56 | my $parent = ::getConfig(); 57 | 58 | ::logMsg("typedpaths v.".$VERSION); 59 | ::rptMsg("-" x 60); 60 | ::rptMsg("typedpaths v.".$VERSION); 61 | ::rptMsg(getShortDescr()); 62 | ::rptMsg("Category: ".$config{category}); 63 | ::rptMsg(""); 64 | 65 | my $profile = $parent->{userprofile}; 66 | ::rptMsg("Profile: ".$profile); 67 | # my @u = split(/\\/,$profile); 68 | # my $n = scalar(@u) - 1; 69 | # my $user = $u[$n]; 70 | 71 | $profile .= "\\" unless ($profile =~ m/\\$/); 72 | my $hive = $profile."NTUSER\.DAT"; 73 | my $reg = Parse::Win32Registry->new($hive); 74 | my $root_key = $reg->get_root_key; 75 | 76 | my $key_path = 'Software\\Microsoft\\Internet Explorer\\TypedURLs'; 77 | my $key; 78 | if ($key = $root_key->get_subkey($key_path)) { 79 | ::rptMsg("TypedURLs"); 80 | ::rptMsg($key_path); 81 | ::rptMsg("LastWrite Time ".gmtime($key->get_timestamp())." (UTC)"); 82 | my @vals = $key->get_list_of_values(); 83 | if (scalar(@vals) > 0) { 84 | my %urls; 85 | # Retrieve values and load into a hash for sorting 86 | foreach my $v (@vals) { 87 | my $val = $v->get_name(); 88 | my $data = $v->get_data(); 89 | my $tag = (split(/url/,$val))[1]; 90 | $urls{$tag} = $val.":".$data; 91 | } 92 | # Print sorted content to report file 93 | foreach my $u (sort {$a <=> $b} keys %urls) { 94 | my ($val,$data) = split(/:/,$urls{$u},2); 95 | ::rptMsg(" ".$val." -> ".$data); 96 | } 97 | } 98 | else { 99 | ::rptMsg($key_path." has no values."); 100 | ::logMsg($key_path." has no values."); 101 | } 102 | } 103 | else { 104 | ::rptMsg($key_path." not found."); 105 | ::logMsg($key_path." not found."); 106 | } 107 | } 108 | 109 | 1; -------------------------------------------------------------------------------- /plugins/filehistory.pl: -------------------------------------------------------------------------------- 1 | #----------------------------------------------------------- 2 | # filehistory.pl 3 | # Get filehistory settings 4 | # 5 | # Change history 6 | # 20120925 - updated to RS format 7 | # 20120722 - updated %config hash 8 | # 20120620 - updated/modified by H. Carvey 9 | # 20120607 - created by K. Johnson 10 | # 11 | # References 12 | # This RegRipper plugin was created based on research I have done on 13 | # the FileHistory Feature of Windows 8. 14 | # http://randomthoughtsofforensics.blogspot.com/ 15 | # 16 | # FileHistoy Plugin copyright 2012 K. Johnson 17 | # Edited by H. Carvey, keydet89@yahoo.com 18 | #----------------------------------------------------------- 19 | package filehistory; 20 | use strict; 21 | 22 | my %config = (hive => "NTUSER\.DAT", 23 | hivemask => 0x10, 24 | type => "Reg", 25 | class => 1, 26 | output => "report", 27 | category => "User Activity", 28 | hasShortDescr => 1, 29 | hasDescr => 0, 30 | hasRefs => 0, 31 | osmask => 32, #Windows 8 32 | version => 20120925); 33 | 34 | sub getConfig{return \%config} 35 | sub getShortDescr { 36 | return "Gets filehistory settings (Win8)"; 37 | } 38 | sub getDescr{} 39 | sub getRefs {} 40 | sub getHive {return $config{hive};} 41 | sub getVersion {return $config{version};} 42 | 43 | my $VERSION = getVersion(); 44 | 45 | sub pluginmain { 46 | my $class = shift; 47 | my $parent = ::getConfig(); 48 | 49 | ::logMsg("filehistory v.".$VERSION); 50 | ::rptMsg("-" x 60); 51 | ::rptMsg("filehistory v.".$VERSION); 52 | ::rptMsg(getShortDescr()); 53 | ::rptMsg("Category: ".$config{category}); 54 | ::rptMsg(""); 55 | 56 | my $profile = $parent->{userprofile}; 57 | ::rptMsg("Profile: ".$profile); 58 | # my @u = split(/\\/,$profile); 59 | # my $n = scalar(@u) - 1; 60 | # my $user = $u[$n]; 61 | $profile .= "\\" unless ($profile =~ m/\\$/); 62 | my $ntuser = $profile."NTUSER\.DAT"; 63 | my $reg = Parse::Win32Registry->new($ntuser); 64 | my $root_key = $reg->get_root_key; 65 | 66 | my $key_path = "Software\\Microsoft\\Windows\\CurrentVersion\\FileHistory"; 67 | my $key; 68 | if ($key = $root_key->get_subkey($key_path)) { 69 | ::rptMsg($key_path); 70 | ::rptMsg("LastWrite Time ".gmtime($key->get_timestamp())." (UTC)"); 71 | ::rptMsg(""); 72 | my @vals = $key->get_list_of_values(); 73 | 74 | if (scalar(@vals) > 0) { 75 | foreach my $v (@vals) { 76 | 77 | if ($v->get_name() eq "ProtectedUpToTime") { 78 | my @t = unpack("VV",$v->get_data()); 79 | my $pft = ::getTime($t[0],$t[1]); 80 | ::rptMsg(" ProtectedUpToTime = ".gmtime($pft)." (UTC)"); 81 | } 82 | 83 | if ($v->get_name() eq "ReassociationPerformed") { 84 | ::rptMsg(sprintf "%-20s 0x%x","ReassociationPerformed",$v->get_data()); 85 | } 86 | 87 | if ($v->get_name() eq "RestoreAllowed") { 88 | ::rptMsg(sprintf "%-20s 0x%x","RestoreAllowed",$v->get_data()); 89 | } 90 | 91 | if ($v->get_name() eq "SearchRebuildRequired") { 92 | ::rptMsg(sprintf "%-20s 0x%x","SearchRebuildRequired",$v->get_data()); 93 | } 94 | 95 | if ($v->get_name() eq "TargetChanged") { 96 | ::rptMsg(sprintf "%-20s 0x%x","TargetChanged",$v->get_data()); 97 | } 98 | } 99 | } 100 | else { 101 | ::rptMsg($key_path." has no values."); 102 | ::rptMsg("File History may not be configured for this user."); 103 | } 104 | } 105 | else { 106 | ::rptMsg($key_path." not found."); 107 | } 108 | } 109 | 110 | 1; -------------------------------------------------------------------------------- /plugins/user_run.pl: -------------------------------------------------------------------------------- 1 | #----------------------------------------------------------- 2 | # user_run 3 | # Get contents of Run key from user's hive 4 | # 5 | # History 6 | # 20120824 - updated to FS format 7 | # 20080328 - created 8 | # 9 | # References: 10 | # http://msdn2.microsoft.com/en-us/library/aa376977.aspx 11 | # http://support.microsoft.com/kb/170086 12 | # 13 | # 14 | # copyright 2008 H. Carvey, keydet89@yahoo.com 15 | #----------------------------------------------------------- 16 | package user_run; 17 | use strict; 18 | 19 | my %config = (hive => "NTUSER\.DAT", 20 | osmask => 63, #XP - Win8 21 | class => 1, 22 | output => "report", 23 | type => "Reg", 24 | category => "AutoStart", 25 | hasShortDescr => 1, 26 | hasDescr => 0, 27 | hasRefs => 1, 28 | version => 20120824); 29 | 30 | sub getConfig{return \%config} 31 | 32 | sub getShortDescr { 33 | return "Get HKU\\\.\.\\Run key contents from NTUSER\.DAT hive"; 34 | } 35 | sub getDescr{} 36 | sub getRefs { 37 | my %refs = ("Definition of the Run keys in the WinXP Registry" => 38 | "http://support.microsoft.com/kb/314866"); 39 | return %refs; 40 | } 41 | sub getHive {return $config{hive};} 42 | sub getVersion {return $config{version};} 43 | 44 | my $VERSION = getVersion(); 45 | 46 | sub pluginmain { 47 | my $class = shift; 48 | my $parent = ::getConfig(); 49 | 50 | ::logMsg("user_run v.".$VERSION); 51 | ::rptMsg("-" x 60); 52 | ::rptMsg("user_run v.".$VERSION); 53 | ::rptMsg(getShortDescr()); 54 | ::rptMsg("Category: ".$config{category}); 55 | ::rptMsg(""); 56 | 57 | my $profile = $parent->{userprofile}; 58 | ::rptMsg("Profile: ".$profile); 59 | 60 | my @u = split(/\\/,$profile); 61 | my $n = scalar(@u) - 1; 62 | my $user = $u[$n]; 63 | 64 | $profile .= "\\" unless ($profile =~ m/\\$/); 65 | my $hive = $profile."NTUSER\.DAT"; 66 | 67 | my $reg = Parse::Win32Registry->new($hive); 68 | my $root_key = $reg->get_root_key; 69 | 70 | my $key_path = "Software\\Microsoft\\Windows\\CurrentVersion\\Run"; 71 | my $key; 72 | if ($key = $root_key->get_subkey($key_path)) { 73 | ::rptMsg($key_path); 74 | ::rptMsg("LastWrite Time ".gmtime($key->get_timestamp())." (UTC)"); 75 | 76 | my %vals = getKeyValues($key); 77 | if (scalar(keys %vals) > 0) { 78 | foreach my $v (keys %vals) { 79 | ::rptMsg("\t".$v." -> ".$vals{$v}); 80 | } 81 | } 82 | else { 83 | ::rptMsg($key_path." has no values."); 84 | } 85 | 86 | my @sk = $key->get_list_of_subkeys(); 87 | if (scalar(@sk) > 0) { 88 | foreach my $s (@sk) { 89 | ::rptMsg(""); 90 | ::rptMsg($key_path."\\".$s->get_name()); 91 | ::rptMsg("LastWrite Time ".gmtime($s->get_timestamp())." (UTC)"); 92 | my %vals = getKeyValues($s); 93 | foreach my $v (keys %vals) { 94 | ::rptMsg("\t".$v." -> ".$vals{$v}); 95 | } 96 | } 97 | } 98 | else { 99 | ::rptMsg(""); 100 | ::rptMsg($key_path." has no subkeys."); 101 | } 102 | } 103 | else { 104 | ::rptMsg($key_path." not found."); 105 | ::logMsg($key_path." not found."); 106 | } 107 | 108 | } 109 | 110 | sub getKeyValues { 111 | my $key = shift; 112 | my %vals; 113 | 114 | my @vk = $key->get_list_of_values(); 115 | if (scalar(@vk) > 0) { 116 | foreach my $v (@vk) { 117 | next if ($v->get_name() eq "" && $v->get_data() eq ""); 118 | $vals{$v->get_name()} = $v->get_data(); 119 | } 120 | } 121 | else { 122 | 123 | } 124 | return %vals; 125 | } 126 | 127 | 1; -------------------------------------------------------------------------------- /plugins/emdmgmt.pl: -------------------------------------------------------------------------------- 1 | #----------------------------------------------------------- 2 | # emdmgmt.pl 3 | # 4 | # Change History 5 | # 20120925 - updated to RS format 6 | # 20120207 - created 7 | # 8 | # copyright 2012 Quantum Analytics Research, LLC 9 | # Author: H. Carvey, keydet89@yahoo.com 10 | #----------------------------------------------------------- 11 | package emdmgmt; 12 | use strict; 13 | 14 | my %config = (hive => "Software", 15 | hivemask => 0x08, 16 | type => "Reg", 17 | output => "report", 18 | class => 0, 19 | category => "Devices", 20 | hasShortDescr => 1, 21 | hasDescr => 0, 22 | hasRefs => 1, 23 | osmask => 20, #Vista,Win7/Win2008R2 24 | version => 20120925); 25 | 26 | sub getConfig{return \%config} 27 | sub getShortDescr { 28 | return "Gets contents of EMDMgmt subkeys and values"; 29 | } 30 | sub getDescr{} 31 | sub getHive {return $config{hive};} 32 | sub getVersion {return $config{version};} 33 | 34 | my $VERSION = getVersion(); 35 | 36 | sub pluginmain { 37 | my $class = shift; 38 | my $parent = ::getConfig(); 39 | 40 | ::logMsg("emdmgmt v.".$VERSION); 41 | ::rptMsg("-" x 60); 42 | ::rptMsg("emdmgmt v.".$VERSION); 43 | ::rptMsg(getShortDescr()); 44 | ::rptMsg("Category: ".$config{category}); 45 | ::rptMsg(""); 46 | 47 | my $hive = $parent->{software}; 48 | my $reg = Parse::Win32Registry->new($hive); 49 | my $root_key = $reg->get_root_key; 50 | 51 | my $key_path = 'Microsoft\\Windows NT\\CurrentVersion\\EMDMgmt'; 52 | my $key; 53 | if ($key = $root_key->get_subkey($key_path)) { 54 | ::rptMsg("EMDMgmt"); 55 | ::rptMsg($key_path); 56 | ::rptMsg("LastWrite Time ".gmtime($key->get_timestamp())." (UTC)"); 57 | ::rptMsg(""); 58 | my @sk = $key->get_list_of_subkeys(); 59 | foreach my $s (@sk) { 60 | my $name = $s->get_name(); 61 | if ($name =~ m/^_\?\?_USBSTOR/) { 62 | my ($usb,$sn,$vol) = (split(/#/,$name,4))[1,2,3]; 63 | ::rptMsg($usb); 64 | ::rptMsg(" LastWrite: ".gmtime($s->get_timestamp())." Z"); 65 | ::rptMsg(" SN: ".$sn); 66 | $vol =~ s/{53f56307-b6bf-11d0-94f2-00a0c91efb8b}//; 67 | my ($volname,$vsn) = split(/_/,$vol,2); 68 | $vsn = uc(sprintf "%x",$vsn); 69 | if (length($vsn) >= 8) { 70 | my ($f,$l) = unpack("(A4)*",$vsn); 71 | $vsn = $f."-".$l; 72 | } 73 | ::rptMsg(" Vol Name: ".$volname) if ($volname ne ""); 74 | ::rptMsg(" VSN: ".$vsn); 75 | my $last = $s->get_value_data("LastTestedTime"); 76 | my ($lo,$hi) = unpack("VV",$last); 77 | if ($lo != 0 && $hi != 0) { 78 | ::rptMsg(" LastTestedTime: ".gmtime(::getTime($lo,$hi))." Z"); 79 | } 80 | ::rptMsg(""); 81 | } 82 | else { 83 | my @n = split(/_/,$name); 84 | my $t = scalar(@n); 85 | my $volname = $n[$t - 2]; 86 | my $vsn = $n[$t - 1]; 87 | $vsn = uc(sprintf "%x",$vsn); 88 | if (length($vsn) >= 8) { 89 | my ($f,$l) = unpack("(A4)*",$vsn); 90 | $vsn = $f."-".$l; 91 | } 92 | $volname = "Unknown Volume" unless ($volname ne ""); 93 | ::rptMsg($volname); 94 | ::rptMsg(" LastWrite: ".gmtime($s->get_timestamp())." Z"); 95 | ::rptMsg(" VSN: ".$vsn); 96 | 97 | my $last = $s->get_value_data("LastTestedTime"); 98 | my ($lo,$hi) = unpack("VV",$last); 99 | if ($lo != 0 && $hi != 0) { 100 | ::rptMsg(" LastTestedTime: ".gmtime(::getTime($lo,$hi))." Z"); 101 | } 102 | ::rptMsg(""); 103 | } 104 | } 105 | } 106 | else { 107 | ::rptMsg($key_path." not found."); 108 | } 109 | } 110 | 1; -------------------------------------------------------------------------------- /plugins/tsclient.pl: -------------------------------------------------------------------------------- 1 | #----------------------------------------------------------- 2 | # tsclient.pl 3 | # Plugin for Registry Ripper 4 | # 5 | # Change history 6 | # 20120827 - updated 7 | # 20080324 - created 8 | # 9 | # References 10 | # http://support.microsoft.com/kb/312169 11 | # 12 | # copyright 2012 Quantum Analytics Research, LLC 13 | # Author: H. Carvey, keydet89@yahoo.com 14 | #----------------------------------------------------------- 15 | package tsclient; 16 | use strict; 17 | 18 | my %config = (hive => "NTUSER\.DAT", 19 | hivemask => 0x10, 20 | class => 1, 21 | type => "Reg", 22 | category => "User Activity", 23 | output => "report", 24 | hasShortDescr => 0, 25 | hasDescr => 0, 26 | hasRefs => 0, 27 | osmask => 31, 28 | version => 20120925); 29 | 30 | sub getConfig{return \%config} 31 | sub getShortDescr { 32 | return "Displays contents of user's TermServClient Default and Servers keys"; 33 | } 34 | sub getDescr{} 35 | sub getRefs {} 36 | sub getHive {return $config{hive};} 37 | sub getVersion {return $config{version};} 38 | 39 | my $VERSION = getVersion(); 40 | 41 | sub pluginmain { 42 | my $class = shift; 43 | my $parent = ::getConfig(); 44 | 45 | ::logMsg("typedpaths v.".$VERSION); 46 | ::rptMsg("-" x 60); 47 | ::rptMsg("typedpaths v.".$VERSION); 48 | ::rptMsg(getShortDescr()); 49 | ::rptMsg("Category: ".$config{category}); 50 | ::rptMsg(""); 51 | 52 | my $profile = $parent->{userprofile}; 53 | ::rptMsg("Profile: ".$profile); 54 | # my @u = split(/\\/,$profile); 55 | # my $n = scalar(@u) - 1; 56 | # my $user = $u[$n]; 57 | 58 | $profile .= "\\" unless ($profile =~ m/\\$/); 59 | my $hive = $profile."NTUSER\.DAT"; 60 | my $reg = Parse::Win32Registry->new($hive); 61 | my $root_key = $reg->get_root_key; 62 | 63 | my $key_path = 'Software\\Microsoft\\Terminal Server Client\\Default'; 64 | my $key; 65 | if ($key = $root_key->get_subkey($key_path)) { 66 | ::rptMsg("TSClient"); 67 | ::rptMsg($key_path); 68 | ::rptMsg("LastWrite Time ".gmtime($key->get_timestamp())." (UTC)"); 69 | my @vals = $key->get_list_of_values(); 70 | if (scalar(@vals) > 0) { 71 | my %mrus; 72 | foreach my $v (@vals) { 73 | my $val = $v->get_name(); 74 | my $data = $v->get_data(); 75 | my $tag = (split(/MRU/,$val))[1]; 76 | $mrus{$tag} = $val.":".$data; 77 | } 78 | foreach my $u (sort {$a <=> $b} keys %mrus) { 79 | my ($val,$data) = split(/:/,$mrus{$u},2); 80 | ::rptMsg(" ".$val." -> ".$data); 81 | } 82 | } 83 | else { 84 | ::rptMsg($key_path." has no values."); 85 | } 86 | } 87 | else { 88 | ::rptMsg($key_path." not found."); 89 | } 90 | ::rptMsg(""); 91 | 92 | my $key_path = 'Software\\Microsoft\\Terminal Server Client\\Servers'; 93 | my $key; 94 | if ($key = $root_key->get_subkey($key_path)) { 95 | ::rptMsg($key_path); 96 | ::rptMsg("LastWrite Time ".gmtime($key->get_timestamp())." (UTC)"); 97 | ::rptMsg(""); 98 | my @subkeys = $key->get_list_of_subkeys(); 99 | if (scalar(@subkeys) > 0) { 100 | foreach my $s (@subkeys) { 101 | my $name = $s->get_name(); 102 | my $lw = $s->get_timestamp(); 103 | ::rptMsg($name." LastWrite: ".gmtime($lw)); 104 | my $hint; 105 | eval { 106 | $hint = $s->get_value("UsernameHint")->get_data(); 107 | ::rptMsg(" UsernameHint: ".$hint); 108 | }; 109 | } 110 | ::rptMsg(""); 111 | } 112 | else { 113 | ::rptMsg($key_path." has no subkeys."); 114 | } 115 | } 116 | else { 117 | ::rptMsg($key_path." not found."); 118 | } 119 | } 120 | 121 | 1; -------------------------------------------------------------------------------- /WinFile.pm: -------------------------------------------------------------------------------- 1 | package WinFile; 2 | #--------------------------------------------------------------------- 3 | # WinFile.pm 4 | # 5 | # 6 | # Change history 7 | # 20100922 - created 8 | # 9 | # 10 | # References 11 | # 12 | # 13 | # copyright 2012 ASI 14 | # This software is released under the Perl Artistic License: 15 | # http://dev.perl.org/licenses/artistic.html 16 | #--------------------------------------------------------------------- 17 | use strict; 18 | use Exporter; 19 | use Digest::MD5; 20 | 21 | use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS); 22 | 23 | $VERSION = 0.1; 24 | @ISA = qw(Exporter); 25 | @EXPORT = (); 26 | @EXPORT_OK = qw(new); 27 | 28 | # Items to export into callers namespace by default. Note: do not export 29 | # names by default without a very good reason. Use EXPORT_OK instead. 30 | # Do not simply export all your public functions/methods/constants. 31 | 32 | # Global variables 33 | # self reference 34 | my $self = {}; 35 | 36 | #--------------------------------------------------------------------- 37 | # new() 38 | # 39 | #--------------------------------------------------------------------- 40 | sub new { 41 | my $class = shift; 42 | return bless($self, $class); 43 | } 44 | 45 | #--------------------------------------------------------------------- 46 | # getMD5() 47 | # Returns an MD5 hash 48 | #--------------------------------------------------------------------- 49 | sub getMD5 { 50 | open(FH, $_[1]) or die "Can't open ".$_[1].": $!"; 51 | binmode(FH); 52 | return Digest::MD5->new->addfile(*FH)->hexdigest(); 53 | } 54 | 55 | #--------------------------------------------------------------------- 56 | # getSize() 57 | # 58 | #--------------------------------------------------------------------- 59 | sub getSize {return (stat($_[1]))[7];} 60 | 61 | #--------------------------------------------------------------------- 62 | # getFileExt() 63 | # 64 | #--------------------------------------------------------------------- 65 | sub getFileExt { 66 | # strip file name from path 67 | my @list = split(/\\/,$_[1]); 68 | my $i = scalar @list; 69 | my $file = $list[$i - 1]; 70 | my @name = split(/\./,$file); 71 | my $sc = scalar @name; 72 | if ($sc == 2) { 73 | return $name[1]; 74 | } 75 | elsif ($sc > 2) { 76 | return $name[$sc - 1]; 77 | } 78 | else { 79 | # something happened, or there is no file extension 80 | } 81 | 82 | } 83 | 84 | #--------------------------------------------------------------------- 85 | # isMZSig() 86 | # checks first two bytes for 'MZ' (0x5a4d) 87 | #--------------------------------------------------------------------- 88 | sub isMZSig { 89 | my $data; 90 | open(FH,$_[1]); 91 | binmode(FH); 92 | seek(FH,0,0); 93 | read(FH,$data,2); 94 | if ($data eq "MZ") { 95 | return 1; 96 | } 97 | else { 98 | return 0; 99 | } 100 | } 101 | 102 | 103 | #---------------------------------------------------------------- 104 | # getError() 105 | # returns the error message for the module 106 | #---------------------------------------------------------------- 107 | sub getError {return $self->{error};} 108 | 109 | 110 | 1; 111 | __END__ 112 | 113 | =head1 NAME 114 | 115 | WinFile - Helper module for FSS Scanner 116 | 117 | =head1 SYNOPSIS 118 | 119 | see example files 120 | 121 | =head1 DESCRIPTION 122 | 123 | WinFile is a Perl module the provides helper functions for the main 124 | driver component of the Forensic Scanner. 125 | 126 | =head1 SEE ALSO 127 | 128 | 129 | 130 | =head1 AUTHOR 131 | 132 | Harlan Carvey, Ekeydet89@yahoo.comE 133 | 134 | =head1 COPYRIGHT AND LICENSE 135 | 136 | Copyright (C) 2009 by Harlan Carvey (keydet89@yahoo.com) 137 | 138 | This library is free software; you can redistribute it and/or modify 139 | it as you like. However, please be sure to provide proper credit where 140 | it is due. 141 | 142 | =cut 143 | -------------------------------------------------------------------------------- /plugins/imagefile.pl: -------------------------------------------------------------------------------- 1 | #----------------------------------------------------------- 2 | # imagefile 3 | # 4 | # References: 5 | # http://msdn2.microsoft.com/en-us/library/a329t4ed(VS\.80)\.aspx 6 | # http://support.microsoft.com/kb/2264107 7 | # 8 | # Change history: 9 | # 20120824 - updated to FS format 10 | # 20100824 - added check for "CWDIllegalInDllSearch" value 11 | # 12 | # copyright 2012 13 | # Author: H. Carvey, keydet89@yahoo.com 14 | #----------------------------------------------------------- 15 | package imagefile; 16 | use strict; 17 | 18 | my %config = (hive => "Software", 19 | hivemask => 0x08, 20 | type => "Reg", 21 | category => "Malware", 22 | output => "report", 23 | class => 0, 24 | hasShortDescr => 1, 25 | hasDescr => 0, 26 | hasRefs => 0, 27 | osmask => 31, 28 | version => 20120824); 29 | 30 | sub getConfig{return \%config} 31 | sub getShortDescr { 32 | return "Checks IFEO subkeys for Debugger/CWDIllegalInDllSearch values"; 33 | } 34 | sub getDescr{} 35 | sub getRefs {} 36 | sub getHive {return $config{hive};} 37 | sub getVersion {return $config{version};} 38 | 39 | my $VERSION = getVersion(); 40 | 41 | sub pluginmain { 42 | my $class = shift; 43 | 44 | my $parent = ::getConfig(); 45 | 46 | ::logMsg("imagefile v.".$VERSION); 47 | ::rptMsg("-" x 60); 48 | ::rptMsg("imagefile v.".$VERSION); 49 | ::rptMsg(getShortDescr()); 50 | ::rptMsg("Category: ".$config{category}); 51 | ::rptMsg(""); 52 | 53 | my $hive = $parent->{software}; 54 | 55 | my $reg = Parse::Win32Registry->new($hive); 56 | my $root_key = $reg->get_root_key; 57 | my $key_path = "Microsoft\\Windows NT\\CurrentVersion\\Image File Execution Options"; 58 | my $key; 59 | if ($key = $root_key->get_subkey($key_path)) { 60 | ::rptMsg("Image File Execution Options"); 61 | ::rptMsg($key_path); 62 | ::rptMsg("LastWrite Time ".gmtime($key->get_timestamp())." (UTC)"); 63 | ::rptMsg(""); 64 | 65 | my @subkeys = $key->get_list_of_subkeys(); 66 | if (scalar(@subkeys) > 0) { 67 | my %debug; 68 | my $i = "Your Image File Name here without a path"; 69 | foreach my $s (@subkeys) { 70 | my $name = $s->get_name(); 71 | next if ($name =~ m/^$i/i); 72 | my $debugger = ""; 73 | eval { 74 | $debugger = $s->get_value("Debugger")->get_data(); 75 | }; 76 | # If the eval{} throws an error, it's b/c the Debugger value isn't 77 | # found within the key, so we don't need to do anything w/ the error 78 | if ($debugger ne "") { 79 | $debug{$name}{debug} = $debugger; 80 | $debug{$name}{lastwrite} = $s->get_timestamp(); 81 | } 82 | 83 | my $dllsearch = ""; 84 | eval { 85 | $dllsearch = $s->get_value("CWDIllegalInDllSearch")->get_data(); 86 | }; 87 | # If the eval{} throws an error, it's b/c the Debugger value isn't 88 | # found within the key, so we don't need to do anything w/ the error 89 | if ($dllsearch ne "") { 90 | $debug{$name}{dllsearch} = $debugger; 91 | $debug{$name}{lastwrite} = $s->get_timestamp(); 92 | } 93 | } 94 | 95 | if (scalar (keys %debug) > 0) { 96 | foreach my $d (keys %debug) { 97 | ::rptMsg($d." LastWrite: ".gmtime($debug{$d}{lastwrite})); 98 | ::rptMsg(" Debugger : ".$debug{$d}{debug}) if (exists $debug{$d}{debug}); 99 | ::rptMsg(" CWDIllegalInDllSearch: ".$debug{$d}{dllsearch}) if (exists $debug{$d}{dllsearch}); 100 | } 101 | } 102 | else { 103 | ::rptMsg("No Debugger/CWDIllegalInDllSearch values found."); 104 | } 105 | } 106 | else { 107 | ::rptMsg($key_path." has no subkeys."); 108 | ::logMsg($key_path." has no subkeys"); 109 | } 110 | } 111 | else { 112 | ::rptMsg($key_path." not found."); 113 | ::logMsg($key_path." not found."); 114 | } 115 | } 116 | 1; -------------------------------------------------------------------------------- /plugins/tif.pl: -------------------------------------------------------------------------------- 1 | package tif; 2 | #----------------------------------------------------------- 3 | # tif - Plugin to parse through user profile Temporary Internet Files 4 | # dir (based on ProfileList key) and look for "suspicious" files. 5 | # 6 | # Change History 7 | # 20120823 - updated to FS format 8 | # 20100927 - created 9 | # 10 | # copyright 2012 11 | # Author: H. Carvey, keydet89@yahoo.com 12 | #----------------------------------------------------------- 13 | use strict; 14 | 15 | my %config = (hasShortDescr => 1, 16 | category => "Malware", 17 | shortDescr => "Parse users Local Settings\\Temp dirs for suspicious files", 18 | type => "File", 19 | output => "report", 20 | osmask => 31, 21 | class => 1, 22 | version => 20120823); 23 | 24 | sub getConfig{return \%config}; 25 | my $VERSION = $config{version}; 26 | sub getShortDescr { return $config{shortDescr};} 27 | sub pluginmain { 28 | my $class = shift; 29 | my $parent = ::getConfig(); 30 | 31 | ::logMsg("tif v.".$VERSION); 32 | ::rptMsg("-" x 60); 33 | ::rptMsg("tif v.".$VERSION); 34 | ::rptMsg(getShortDescr()); 35 | ::rptMsg("Category: ".$config{category}); 36 | ::rptMsg(""); 37 | 38 | my $profile = $parent->{userprofile}; 39 | ::rptMsg("Profile: ".$profile); 40 | $profile .= "\\" unless ($profile =~ m/\\$/); 41 | 42 | my $path; 43 | 44 | if ($parent->{CurrentVersion} >= 6.0) { 45 | $path = $profile."AppData\\Local\\Microsoft\\Windows\\Temporary Internet Files\\Low\\Content\.IE5\\"; 46 | } 47 | else { 48 | $path = $profile."Local Settings\\Temporary Internet Files\\Content\.IE5\\"; 49 | } 50 | 51 | if (-e $path && -d $path) { 52 | ::rptMsg("Path: ".$path); 53 | my @dirs; 54 | opendir(DIR,$path); 55 | while(readdir(DIR)) { 56 | next if ($_ =~ m/^\./); 57 | push(@dirs,$_) if (-d $path.$_); 58 | } 59 | closedir(DIR); 60 | 61 | foreach my $d (@dirs) { 62 | my $tif = $path.$d."\\"; 63 | ::rptMsg("Checking ".$tif."..."); 64 | checkTMP($tif); 65 | checkPDF($tif); 66 | checkEXE($tif); 67 | ::rptMsg(""); 68 | } 69 | } 70 | else { 71 | ::rptMsg($path." not found."); 72 | } 73 | } 74 | 75 | sub checkTMP { 76 | my $path = shift; 77 | my $win = WinFile->new(); 78 | my @files; 79 | 80 | opendir(DIR,$path); 81 | @files = map{$path.$_}(grep(/\.tmp$/i,readdir(DIR))); 82 | closedir(DIR); 83 | 84 | if (scalar @files > 0) { 85 | foreach my $f (@files) { 86 | ::rptMsg(" File with \.tmp extension:"); 87 | ::rptMsg(" ".$f); 88 | ::rptMsg(" MD5: ".$win->getMD5($f)); 89 | ::rptMsg("**File has MZ signature!") if ($win->isMZSig($f)); 90 | } 91 | } 92 | else { 93 | ::rptMsg(" No files with \.tmp extension found."); 94 | } 95 | } 96 | 97 | sub checkPDF { 98 | my $path = shift; 99 | my $win = WinFile->new(); 100 | my @files; 101 | 102 | opendir(DIR,$path); 103 | @files = map{$path.$_}(grep(/\.pdf$/i,readdir(DIR))); 104 | closedir(DIR); 105 | 106 | if (scalar @files > 0) { 107 | foreach my $f (@files) { 108 | ::rptMsg(" File with \.pdf extension:"); 109 | ::rptMsg(" ".$f); 110 | ::rptMsg(" MD5: ".$win->getMD5($f)); 111 | } 112 | } 113 | else { 114 | ::rptMsg(" No files with \.pdf extension found."); 115 | } 116 | } 117 | 118 | 119 | sub checkEXE { 120 | my $path = shift; 121 | my $win = WinFile->new(); 122 | my @files; 123 | 124 | opendir(DIR,$path); 125 | @files = map{$path.$_}(grep(/\.exe$/,readdir(DIR))); 126 | closedir(DIR); 127 | 128 | if (scalar @files > 0) { 129 | foreach my $f (@files) { 130 | if ($win->isMZSig($f)) { 131 | ::rptMsg(" File with \.exe extension and signature found:"); 132 | ::rptMsg(" ".$f); 133 | ::rptMsg(" MD5: ".$win->getMD5($f)); 134 | } 135 | } 136 | } 137 | else { 138 | ::rptMsg(" No files with a \.exe extension found."); 139 | } 140 | } 141 | 142 | 1; -------------------------------------------------------------------------------- /plugins/userassist.pl: -------------------------------------------------------------------------------- 1 | #! c:\perl\bin\perl.exe 2 | #----------------------------------------------------------- 3 | # userassist.pl 4 | # Plugin for Registry Ripper, NTUSER.DAT edition - gets the 5 | # UserAssist values 6 | # 7 | # Change history 8 | # 20120823 - updated to FS format 9 | # 20100322 - Added CLSID list reference 10 | # 20100308 - created, based on original userassist.pl plugin 11 | # 12 | # References 13 | # Control Panel Applets - http://support.microsoft.com/kb/313808 14 | # CLSIDs - http://www.autohotkey.com/docs/misc/CLSID-List.htm 15 | # 16 | # copyright 2010 Quantum Analytics Research, LLC 17 | #----------------------------------------------------------- 18 | package userassist; 19 | use strict; 20 | 21 | my %config = (hive => "NTUSER\.DAT", 22 | hivemask => 0x10, 23 | hasShortDescr => 1, 24 | hasDescr => 0, 25 | hasRefs => 0, 26 | type => "Reg", 27 | class => 1, 28 | output => "report", 29 | osmask => 31, 30 | version => 20120823); 31 | 32 | sub getConfig{return \%config} 33 | sub getShortDescr { 34 | return "Displays contents of UserAssist subkeys"; 35 | } 36 | sub getDescr{} 37 | sub getRefs {"Description of Control Panel Files in XP" => "http://support.microsoft.com/kb/313808"} 38 | sub getHive {return $config{hive};} 39 | sub getVersion {return $config{version};} 40 | 41 | my $VERSION = getVersion(); 42 | 43 | sub pluginmain { 44 | my $class = shift; 45 | 46 | my $parent = ::getConfig(); 47 | 48 | ::logMsg("userassist v.".$VERSION); 49 | ::rptMsg("-" x 60); 50 | ::rptMsg("userassist v.".$VERSION); 51 | ::rptMsg(getShortDescr()); 52 | ::rptMsg("Category: ".$config{category}); 53 | ::rptMsg(""); 54 | my $profile = $parent->{userprofile}; 55 | ::rptMsg("Profile: ".$profile); 56 | $profile .= "\\" unless ($profile =~ m/\\$/); 57 | my $ntuser = $profile."NTUSER\.DAT"; 58 | 59 | my $reg = Parse::Win32Registry->new($ntuser); 60 | my $root_key = $reg->get_root_key; 61 | 62 | my $key_path = "Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\UserAssist"; 63 | my $key; 64 | 65 | if ($key = $root_key->get_subkey($key_path)) { 66 | ::rptMsg("UserAssist"); 67 | ::rptMsg($key_path); 68 | ::rptMsg("LastWrite Time ".gmtime($key->get_timestamp())." (UTC)"); 69 | ::rptMsg(""); 70 | my @subkeys = $key->get_list_of_subkeys(); 71 | if (scalar(@subkeys) > 0) { 72 | foreach my $s (@subkeys) { 73 | ::rptMsg($s->get_name()); 74 | processKey($s); 75 | ::rptMsg(""); 76 | } 77 | } 78 | else { 79 | ::rptMsg($key_path." has no subkeys."); 80 | } 81 | } 82 | else { 83 | ::rptMsg($key_path." not found."); 84 | } 85 | } 86 | 87 | sub processKey { 88 | my $ua = shift; 89 | 90 | my $key = $ua->get_subkey("Count"); 91 | 92 | my %ua; 93 | my $hrzr = "HRZR"; 94 | 95 | my @vals = $key->get_list_of_values(); 96 | if (scalar(@vals) > 0) { 97 | foreach my $v (@vals) { 98 | my $value_name = $v->get_name(); 99 | my $data = $v->get_data(); 100 | 101 | # Windows XP/2003/Vista/2008 102 | if (length($data) == 16) { 103 | my ($session,$count,$val1,$val2) = unpack("V*",$data); 104 | if ($val2 != 0) { 105 | my $time_value = ::getTime($val1,$val2); 106 | if ($value_name =~ m/^$hrzr/) { 107 | $value_name =~ tr/N-ZA-Mn-za-m/A-Za-z/; 108 | } 109 | $count -= 5 if ($count > 5); 110 | push(@{$ua{$time_value}},$value_name." (".$count.")"); 111 | } 112 | } 113 | # Windows 7 114 | elsif (length($data) == 72) { 115 | $value_name =~ tr/N-ZA-Mn-za-m/A-Za-z/; 116 | # if (unpack("V",substr($data,0,4)) == 0) { 117 | # my $count = unpack("V",substr($data,4,4)); 118 | # my @t = unpack("VV",substr($data,60,8)); 119 | # next if ($t[0] == 0 && $t[1] == 0); 120 | # my $time_val = ::getTime($t[0],$t[1]); 121 | # print " .-> ".$time_val."\n"; 122 | # push(@{$ua{$time_val}},$value_name." (".$count.")"); 123 | # } 124 | my $count = unpack("V",substr($data,4,4)); 125 | my @t = unpack("VV",substr($data,60,8)); 126 | next if ($t[0] == 0 && $t[1] == 0); 127 | my $time_val = ::getTime($t[0],$t[1]); 128 | push(@{$ua{$time_val}},$value_name." (".$count.")"); 129 | } 130 | else { 131 | # Nothing else to do 132 | } 133 | } 134 | foreach my $t (reverse sort {$a <=> $b} keys %ua) { 135 | ::rptMsg(gmtime($t)." Z"); 136 | foreach my $i (@{$ua{$t}}) { 137 | ::rptMsg(" ".$i); 138 | } 139 | } 140 | } 141 | } 142 | 1; -------------------------------------------------------------------------------- /plugins/mp2.pl: -------------------------------------------------------------------------------- 1 | #----------------------------------------------------------- 2 | # mp2.pl 3 | # Parse user's MountPoints2 key 4 | # 5 | # Change history 6 | # 20120925 - updated to RS format 7 | # 20120330 - updated to include parsing of UUID v1 GUIDs to get unique 8 | # MAC addresses 9 | # 20091116 - updated output/sorting; added getting 10 | # _LabelFromReg value 11 | # 20090115 - Removed printing of "volumes" 12 | # 13 | # References 14 | # http://support.microsoft.com/kb/932463 15 | # 16 | # copyright 2012 Quantum Analytics Research, LLC 17 | # Author: H. Carvey 18 | #----------------------------------------------------------- 19 | package mp2; 20 | use strict; 21 | 22 | my %config = (hive => "NTUSER\.DAT", 23 | hivemask => 0x10, 24 | type => "Reg", 25 | output => "report", 26 | class => 1, 27 | category => "User Activity", 28 | hasShortDescr => 1, 29 | hasDescr => 0, 30 | hasRefs => 0, 31 | osmask => 31, 32 | version => 20120925); 33 | 34 | sub getConfig{return \%config} 35 | sub getShortDescr { 36 | return "Gets user's MountPoints2 key contents"; 37 | } 38 | sub getDescr{} 39 | sub getRefs {} 40 | sub getHive {return $config{hive};} 41 | sub getVersion {return $config{version};} 42 | 43 | my $VERSION = getVersion(); 44 | 45 | sub pluginmain { 46 | my $class = shift; 47 | my $parent = ::getConfig(); 48 | 49 | ::logMsg("mp2 v.".$VERSION); 50 | ::rptMsg("-" x 60); 51 | ::rptMsg("mp2 v.".$VERSION); 52 | ::rptMsg(getShortDescr()); 53 | ::rptMsg("Category: ".$config{category}); 54 | ::rptMsg(""); 55 | 56 | my $profile = $parent->{userprofile}; 57 | ::rptMsg("Profile: ".$profile); 58 | # my @u = split(/\\/,$profile); 59 | # my $n = scalar(@u) - 1; 60 | # my $user = $u[$n]; 61 | 62 | $profile .= "\\" unless ($profile =~ m/\\$/); 63 | my $hive = $profile."NTUSER\.DAT"; 64 | 65 | my %drives; 66 | my %volumes; 67 | my %remote; 68 | my %macs; 69 | 70 | my $reg = Parse::Win32Registry->new($hive); 71 | my $root_key = $reg->get_root_key; 72 | 73 | my $key_path = 'Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\MountPoints2'; 74 | my $key; 75 | if ($key = $root_key->get_subkey($key_path)) { 76 | ::rptMsg("MountPoints2"); 77 | ::rptMsg($key_path); 78 | ::rptMsg("LastWrite Time ".gmtime($key->get_timestamp())." (UTC)"); 79 | my @subkeys = $key->get_list_of_subkeys(); 80 | if (scalar @subkeys > 0) { 81 | foreach my $s (@subkeys) { 82 | my $name = $s->get_name(); 83 | if ($name =~ m/^{/) { 84 | my $label; 85 | eval { 86 | $label = $s->get_value("_LabelFromReg")->get_data(); 87 | }; 88 | 89 | my $m = (split(/-/,$name,5))[4]; 90 | $m =~ s/}$//; 91 | $m = uc($m); 92 | $m = join(':',unpack("(A2)*",$m)); 93 | $macs{$m} = 1; 94 | 95 | $name = $name." (".$label.")" unless ($@); 96 | 97 | push(@{$volumes{$s->get_timestamp()}},$name); 98 | } 99 | elsif ($name =~ m/^[A-Z]/) { 100 | push(@{$drives{$s->get_timestamp()}},$name); 101 | } 102 | elsif ($name =~ m/^#/) { 103 | push(@{$remote{$s->get_timestamp()}},$name); 104 | } 105 | else { 106 | ::rptMsg(" Key name = ".$name); 107 | } 108 | } 109 | ::rptMsg(""); 110 | ::rptMsg("Remote Drives:"); 111 | foreach my $t (reverse sort {$a <=> $b} keys %remote) { 112 | ::rptMsg(gmtime($t)." (UTC)"); 113 | foreach my $item (@{$remote{$t}}) { 114 | ::rptMsg(" $item"); 115 | } 116 | } 117 | 118 | ::rptMsg(""); 119 | ::rptMsg("Volumes:"); 120 | foreach my $t (reverse sort {$a <=> $b} keys %volumes) { 121 | ::rptMsg(gmtime($t)." (UTC)"); 122 | foreach my $item (@{$volumes{$t}}) { 123 | ::rptMsg(" $item"); 124 | } 125 | } 126 | ::rptMsg(""); 127 | ::rptMsg("Drives:"); 128 | foreach my $t (reverse sort {$a <=> $b} keys %drives) { 129 | my $d = join(',',(@{$drives{$t}})); 130 | ::rptMsg(gmtime($t)." (UTC) - ".$d); 131 | } 132 | ::rptMsg(""); 133 | ::rptMsg("Unique MAC Addresses:"); 134 | foreach (keys %macs) { 135 | ::rptMsg($_); 136 | } 137 | 138 | ::rptMsg(""); 139 | ::rptMsg("Analysis Tip: Correlate the Volume entries to those found in the MountedDevices"); 140 | ::rptMsg("entries that begin with \"\\??\\Volume\"\."); 141 | } 142 | else { 143 | ::rptMsg($key_path." has no subkeys."); 144 | } 145 | } 146 | else { 147 | ::rptMsg($key_path." not found."); 148 | } 149 | } 150 | 151 | 1; -------------------------------------------------------------------------------- /plugins/arpcache.pl: -------------------------------------------------------------------------------- 1 | #----------------------------------------------------------- 2 | # arpcache.pl 3 | # Retrieves CurrentVersion\App Management\ARPCache entries; subkeys appear 4 | # to maintain information about paths to installed applications in the 5 | # SlowInfoCache value(0x10 - FILETIME object, null term. string with path 6 | # starts at 0x1c) 7 | # 8 | # Change history 9 | # 20120925 - updated to RS format 10 | # 20090413 - Created 11 | # 12 | # References 13 | # No references, but the subkeys appear to hold information about 14 | # installed applications; some SlowInfoCache values appear to contain 15 | # timestamp data (FILETIME object) and/or path information. Posts on 16 | # the Internet indicate the existence of Kazaa beneath the APRCache key, 17 | # as well as possibly an "Outerinfo" subkey indicating that spyware is 18 | # installed. 19 | # 20 | # copyright 2012 Quantum Analytics Research, LLC 21 | # Author: H. Carvey, keydet89@yahoo.com 22 | #----------------------------------------------------------- 23 | package arpcache; 24 | use strict; 25 | 26 | my %config = (hive => "NTUSER\.DAT", 27 | hivemask => 16, 28 | type => "Reg", 29 | class => 1, 30 | output => "report", 31 | category => "Malware", 32 | hasShortDescr => 1, 33 | hasDescr => 0, 34 | hasRefs => 0, 35 | osmask => 31, 36 | version => 20120925); 37 | 38 | sub getConfig{return \%config} 39 | sub getShortDescr { 40 | return "Retrieves CurrentVersion\\App Management\\ARPCache entries, if available"; 41 | } 42 | sub getDescr{} 43 | sub getRefs {} 44 | sub getHive {return $config{hive};} 45 | sub getVersion {return $config{version};} 46 | 47 | my $VERSION = getVersion(); 48 | 49 | my %arpcache; 50 | 51 | sub pluginmain { 52 | my $class = shift; 53 | my $parent = ::getConfig(); 54 | my $profile = $parent->{userprofile}; 55 | 56 | ::logMsg("arpcache v.".$VERSION); 57 | ::rptMsg("-" x 60); 58 | ::rptMsg("arpcache v.".$VERSION); 59 | ::rptMsg(getShortDescr()); 60 | ::rptMsg("Category: ".$config{category}); 61 | ::rptMsg(""); 62 | 63 | ::rptMsg("Profile: ".$profile); 64 | $profile .= "\\" unless ($profile =~ m/\\$/); 65 | 66 | my $hive = $profile."NTUSER\.DAT"; 67 | 68 | my $reg = Parse::Win32Registry->new($hive); 69 | my $root_key = $reg->get_root_key; 70 | 71 | my $key_path = 'Software\\Microsoft\\Windows\\CurrentVersion\\App Management\\ARPCache'; 72 | my $key; 73 | if ($key = $root_key->get_subkey($key_path)) { 74 | ::rptMsg($key_path); 75 | ::rptMsg("LastWrite Time ".gmtime($key->get_timestamp())." (UTC)"); 76 | ::rptMsg(""); 77 | my @subkeys = $key->get_list_of_subkeys(); 78 | if (scalar(@subkeys) > 0) { 79 | foreach my $s (@subkeys) { 80 | my $lw = $s->get_timestamp(); 81 | my $name = $s->get_name(); 82 | 83 | my $path; 84 | eval { 85 | my $i = $s->get_value("SlowInfoCache")->get_data(); 86 | $path = parsePath($i); 87 | }; 88 | ($@) ? ($name .= "|") : ($name .= "|".$path); 89 | 90 | my $date; 91 | eval { 92 | my $i = $s->get_value("SlowInfoCache")->get_data(); 93 | $date = parseDate($i); 94 | }; 95 | ($@) ? ($name .= "|") : ($name .= "|".$date); 96 | push(@{$arpcache{$lw}},$name); 97 | } 98 | 99 | foreach my $t (reverse sort {$a <=> $b} keys %arpcache) { 100 | ::rptMsg(gmtime($t)." (UTC)"); 101 | foreach my $item (@{$arpcache{$t}}) { 102 | my ($name,$path,$date) = split(/\|/,$item,3); 103 | ::rptMsg(" ".$name); 104 | my $str = $path unless ($path eq ""); 105 | $str .= " [".gmtime($date)."]" unless ($date == 0); 106 | ::rptMsg(" -> ".$str) unless ($str eq ""); 107 | } 108 | } 109 | } 110 | else { 111 | ::rptMsg($key_path." has no subkeys."); 112 | } 113 | } 114 | else { 115 | ::rptMsg($key_path." not found."); 116 | } 117 | } 118 | 119 | 1; 120 | 121 | sub parseDate { 122 | my $data = shift; 123 | my ($t1,$t2) = unpack("VV",substr($data,0x10,8)); 124 | return ::getTime($t1,$t2); 125 | } 126 | 127 | sub parsePath { 128 | my $data = shift; 129 | my $ofs = 0x1c; 130 | my $tag = 1; 131 | 132 | my $str = substr($data,$ofs,2); 133 | if (unpack("v",$str) == 0) { 134 | return ""; 135 | } 136 | else { 137 | while($tag) { 138 | $ofs += 2; 139 | my $i = substr($data,$ofs,2); 140 | if (unpack("v",$i) == 0) { 141 | $tag = 0; 142 | } 143 | else { 144 | $str .= $i; 145 | } 146 | } 147 | } 148 | $str =~ s/\00//g; 149 | return $str; 150 | } -------------------------------------------------------------------------------- /plugins/networklist.pl: -------------------------------------------------------------------------------- 1 | #----------------------------------------------------------- 2 | # networklist.pl - Plugin to extract information from the 3 | # NetworkList key, including the MAC address of the default 4 | # gateway 5 | # 6 | # 7 | # Change History: 8 | # 20120925 - updated to RS format 9 | # 20120917 - updated to include NameType value 10 | # 20090812 - updated code to parse DateCreated and DateLastConnected 11 | # values; modified output, as well 12 | # 20090811 - created 13 | # 14 | # References 15 | # 16 | # copyright 2009 H. Carvey, keydet89@yahoo.com 17 | #----------------------------------------------------------- 18 | package networklist; 19 | use strict; 20 | 21 | my %config = (hive => "Software", 22 | hivemask => 0x08, #Software hive 23 | class => 0, 24 | type => "Reg", 25 | osmask => 52, #Vista,Win7/2008R2,Win8 26 | output => "report", 27 | category => "Networking", 28 | hasShortDescr => 1, 29 | hasDescr => 0, 30 | hasRefs => 0, 31 | version => 20120925); 32 | 33 | sub getConfig{return \%config} 34 | 35 | sub getShortDescr { 36 | return "Collects network info from Vista+ NetworkList key"; 37 | } 38 | sub getDescr{} 39 | sub getRefs {} 40 | sub getHive {return $config{hive};} 41 | sub getVersion {return $config{version};} 42 | 43 | my $VERSION = getVersion(); 44 | 45 | my %types = (0x47 => "wireless", 46 | 0x06 => "wired", 47 | 0x17 => "broadband (3g)"); 48 | 49 | sub pluginmain { 50 | my $class = shift; 51 | my $parent = ::getConfig(); 52 | 53 | ::logMsg("networklist v.".$VERSION); 54 | ::rptMsg("-" x 60); 55 | ::rptMsg("networklist v.".$VERSION); 56 | ::rptMsg(getShortDescr()); 57 | ::rptMsg("Category: ".$config{category}); 58 | ::rptMsg(""); 59 | 60 | my $hive = $parent->{software}; 61 | 62 | my $reg = Parse::Win32Registry->new($hive); 63 | my $root_key = $reg->get_root_key; 64 | my $base_path = "Microsoft\\Windows NT\\CurrentVersion\\NetworkList"; 65 | 66 | # First, get profile info 67 | my $key_path = $base_path."\\Profiles"; 68 | my $key; 69 | my %nl; # hash of hashes to hold data 70 | if ($key = $root_key->get_subkey($key_path)) { 71 | ::rptMsg($key_path); 72 | 73 | my @sk = $key->get_list_of_subkeys(); 74 | if (scalar(@sk) > 0) { 75 | foreach my $s (@sk) { 76 | my $name = $s->get_name(); 77 | $nl{$name}{LastWrite} = $s->get_timestamp(); 78 | eval { 79 | $nl{$name}{ProfileName} = $s->get_value("ProfileName")->get_data(); 80 | $nl{$name}{Description} = $s->get_value("Description")->get_data(); 81 | $nl{$name}{Managed} = $s->get_value("Managed")->get_data(); 82 | 83 | my $create = $s->get_value("DateCreated")->get_data(); 84 | $nl{$name}{DateCreated} = ::convertSystemTime($create) if (length($create) == 16); 85 | my $conn = $s->get_value("DateLastConnected")->get_data(); 86 | $nl{$name}{DateLastConnected} = ::convertSystemTime($conn) if (length($conn) == 16); 87 | 88 | $nl{$name}{NameType} = $s->get_value("NameType")->get_data(); 89 | 90 | if (exists $types{$nl{$name}{NameType}}) { 91 | $nl{$name}{Type} = $types{$nl{$name}{NameType}}; 92 | } 93 | else { 94 | $nl{$name}{Type} = $nl{$name}{NameType}; 95 | } 96 | 97 | }; 98 | } 99 | 100 | # Get additional information from the Signatures subkey 101 | $key_path = $base_path."\\Signatures\\Managed"; 102 | if ($key = $root_key->get_subkey($key_path)) { 103 | my @sk = $key->get_list_of_subkeys(); 104 | if (scalar(@sk) > 0) { 105 | foreach my $s (@sk) { 106 | eval { 107 | my $prof = $s->get_value("ProfileGuid")->get_data(); 108 | my $tmp = substr($s->get_value("DefaultGatewayMac")->get_data(),0,6); 109 | my $mac = uc(unpack("H*",$tmp)); 110 | my @t = split(//,$mac); 111 | $nl{$prof}{DefaultGatewayMac} = $t[0].$t[1]."-".$t[2].$t[3]. 112 | "-".$t[4].$t[5]."-".$t[6].$t[7]."-".$t[8].$t[9]."-".$t[10].$t[11]; 113 | }; 114 | } 115 | } 116 | } 117 | 118 | $key_path = $base_path."\\Signatures\\Unmanaged"; 119 | if ($key = $root_key->get_subkey($key_path)) { 120 | my @sk = $key->get_list_of_subkeys(); 121 | if (scalar(@sk) > 0) { 122 | foreach my $s (@sk) { 123 | eval { 124 | my $prof = $s->get_value("ProfileGuid")->get_data(); 125 | my $tmp = substr($s->get_value("DefaultGatewayMac")->get_data(),0,6); 126 | my $mac = uc(unpack("H*",$tmp)); 127 | my @t = split(//,$mac); 128 | $nl{$prof}{DefaultGatewayMac} = $t[0].$t[1]."-".$t[2].$t[3]. 129 | "-".$t[4].$t[5]."-".$t[6].$t[7]."-".$t[8].$t[9]."-".$t[10].$t[11]; 130 | }; 131 | } 132 | } 133 | } 134 | 135 | # Now, display the information 136 | foreach my $n (keys %nl) { 137 | my $str = sprintf "%-15s Gateway Mac: ".$nl{$n}{DefaultGatewayMac},$nl{$n}{ProfileName}; 138 | ::rptMsg($nl{$n}{ProfileName}); 139 | ::rptMsg(" Key LastWrite : ".gmtime($nl{$n}{LastWrite})." UTC"); 140 | ::rptMsg(" DateLastConnected: ".$nl{$n}{DateLastConnected}); 141 | ::rptMsg(" DateCreated : ".$nl{$n}{DateCreated}); 142 | ::rptMsg(" DefaultGatewayMac: ".$nl{$n}{DefaultGatewayMac}); 143 | ::rptMsg(" Type : ".$nl{$n}{Type}); 144 | ::rptMsg(""); 145 | } 146 | 147 | } 148 | else { 149 | ::rptMsg($key_path." has not subkeys"); 150 | } 151 | } 152 | else { 153 | ::rptMsg($key_path." not found."); 154 | } 155 | } 156 | 157 | 1; -------------------------------------------------------------------------------- /plugins/winbackup.pl: -------------------------------------------------------------------------------- 1 | #----------------------------------------------------------- 2 | # winbackup.pl 3 | # 4 | # Change History 5 | # 20120925 - updated to RS format (H. Carvey) 6 | # 20120812 [fpi] % created from winver.pl 7 | # 8 | # References 9 | # 10 | # copyright 2012 M. DeGrazia, arizona4n6@gmail.com 11 | #----------------------------------------------------------- 12 | package winbackup; 13 | use strict; 14 | 15 | my %config = (hive => "Software", 16 | hivemask => 0x08, 17 | type => "Reg", 18 | output => "report", 19 | class => 0, 20 | category => "System Config", 21 | osmask => 16, 22 | hasShortDescr => 1, 23 | hasDescr => 0, 24 | hasRefs => 0, 25 | version => 20120925); 26 | 27 | sub getConfig{return \%config} 28 | 29 | sub getShortDescr { 30 | return "Get Windows Backup Info"; 31 | } 32 | sub getDescr{} 33 | sub getRefs {} 34 | sub getHive {return $config{hive};} 35 | sub getVersion {return $config{version};} 36 | 37 | my $VERSION = getVersion(); 38 | 39 | sub pluginmain { 40 | my $class = shift; 41 | my $hive = shift; 42 | my $parent = ::getConfig(); 43 | 44 | ::logMsg("winbackup v.".$VERSION); 45 | ::rptMsg("-" x 60); 46 | ::rptMsg("winbackup v.".$VERSION); 47 | ::rptMsg(getShortDescr()); 48 | ::rptMsg("Category: ".$config{category}); 49 | ::rptMsg(""); 50 | 51 | my $reg = Parse::Win32Registry->new($parent->{software}); 52 | my $root_key = $reg->get_root_key; 53 | 54 | my $key_path = "Microsoft\\Windows\\CurrentVersion\\WindowsBackup\\ScheduleParams\\TargetDevice"; 55 | my $key; 56 | if ($key = $root_key->get_subkey($key_path)) { 57 | ::rptMsg($key_path); 58 | ::rptMsg("LastWrite Time ".gmtime($key->get_timestamp())." (UTC)"); 59 | ::rptMsg(""); 60 | 61 | my $name; 62 | eval { 63 | $name = $key->get_value("PresentableName")->get_data(); 64 | ::rptMsg(" PresentableName = ".$name); 65 | }; 66 | if ($@) { 67 | # ::rptMsg("PresentableName value not found."); 68 | } 69 | 70 | my $uniquename; 71 | eval { 72 | $uniquename = $key->get_value("UniqueName")->get_data(); 73 | ::rptMsg(" UniqueName = ".$uniquename); 74 | }; 75 | if ($@) { 76 | # ::rptMsg("UniqueName value not found."); 77 | } 78 | 79 | 80 | my $devlabel; 81 | eval { 82 | $devlabel = $key->get_value("Label")->get_data(); 83 | ::rptMsg(" Label = ".$devlabel); 84 | }; 85 | if ($@) { 86 | # ::rptMsg("Label value not found."); 87 | } 88 | 89 | my $vendor; 90 | eval { 91 | $vendor = $key->get_value("DeviceVendor")->get_data(); 92 | ::rptMsg(" DeviceVendor = ".$vendor); 93 | }; 94 | if ($@) { 95 | # ::rptMsg("DeviceVendor value not found."); 96 | } 97 | 98 | my $deviceproduct; 99 | eval { 100 | $deviceproduct = $key->get_value("DeviceProduct")->get_data(); 101 | ::rptMsg(" DeviceProduct = ".$deviceproduct); 102 | }; 103 | if ($@) { 104 | # ::rptMsg("DeviceVendor value not found."); 105 | } 106 | 107 | my $deviceversion; 108 | eval { 109 | $deviceversion = $key->get_value("DeviceVersion")->get_data(); 110 | ::rptMsg(" DeviceVersion = ".$deviceversion); 111 | }; 112 | if ($@) { 113 | # ::rptMsg("DeviceVendor value not found."); 114 | } 115 | 116 | my $devserial; 117 | eval { 118 | $devserial = $key->get_value("DeviceSerial")->get_data(); 119 | ::rptMsg(" DeviceSerial = ".$devserial); 120 | }; 121 | if ($@) { 122 | # ::rptMsg("DeviceSerial value not found."); 123 | } 124 | } 125 | else { 126 | ::rptMsg($key_path." not found."); 127 | } 128 | 129 | #status 130 | ::rptMsg(""); 131 | my $key_path = "Microsoft\\Windows\\CurrentVersion\\WindowsBackup\\Status"; 132 | my $key; 133 | if ($key = $root_key->get_subkey($key_path)) { 134 | ::rptMsg($key_path); 135 | ::rptMsg("LastWrite Time ".gmtime($key->get_timestamp())." (UTC)"); 136 | ::rptMsg(""); 137 | 138 | my $lastresulttime; 139 | eval { 140 | $lastresulttime = $key->get_value("LastResultTime")->get_data(); 141 | my @vals = unpack("VV",$lastresulttime); 142 | my $lrt = ::getTime($vals[0],$vals[1]); 143 | ::rptMsg(" LastResultTime = ".gmtime($lrt)." (UTC)"); 144 | }; 145 | if ($@) { 146 | # ::rptMsg("LastSuccess value not found."); 147 | } 148 | 149 | my $lastsuccess; 150 | eval { 151 | $lastsuccess = $key->get_value("LastSuccess")->get_data(); 152 | my @vals = unpack("VV",$lastsuccess); 153 | my $ls = ::getTime($vals[0],$vals[1]); 154 | ::rptMsg(" LastSuccess = ".gmtime($ls)." (UTC)"); 155 | }; 156 | if ($@) { 157 | # ::rptMsg("LastSuccess value not found."); 158 | } 159 | 160 | my $lasttarget; 161 | eval { 162 | $lasttarget = $key->get_value("LastResultTarget")->get_data(); 163 | ::rptMsg(" LastResultTarget = ".$lasttarget); 164 | }; 165 | if ($@) { 166 | # ::rptMsg("LastResultTarget value not found."); 167 | } 168 | 169 | my $LRTPrestName; 170 | eval { 171 | $LRTPrestName = $key->get_value("LastResultTargetPresentableName")->get_data(); 172 | ::rptMsg(" LastResultTargetPresentableName = ".$LRTPrestName); 173 | }; 174 | if ($@) { 175 | # ::rptMsg("LastResultTargetPresentableName value not found."); 176 | } 177 | 178 | my $LRTTargetLabel; 179 | eval { 180 | $LRTTargetLabel = $key->get_value("LastResultTargetLabel")->get_data(); 181 | ::rptMsg(" LastResultTargetLabel = ".$LRTTargetLabel); 182 | }; 183 | if ($@) { 184 | # ::rptMsg("LastResultTargetLabel value not found."); 185 | } 186 | } 187 | else { 188 | ::rptMsg($key_path." not found."); 189 | } 190 | } 191 | 1; -------------------------------------------------------------------------------- /plugins/ssid.pl: -------------------------------------------------------------------------------- 1 | #----------------------------------------------------------- 2 | # ssid 3 | # Gets SSID and other info from WZCSVC key 4 | # 5 | # 6 | # Change History: 7 | # 20120925 - updated to RS format 8 | # 20100301 - Updated References; removed dwCtlFlags being 9 | # printed; minor adjustments to formatting 10 | # 20091102 - added code to parse EAPOL values for SSIDs 11 | # 20090807 - updated code in accordance with WZC_WLAN_CONFIG 12 | # structure 13 | # 14 | # References 15 | # http://msdn.microsoft.com/en-us/library/aa448338.aspx 16 | # 17 | # copyright 2012 Quantum Analytics Research, LLC 18 | # Author: H. Carvey, keydet89@yahoo.com 19 | #----------------------------------------------------------- 20 | package ssid; 21 | use strict; 22 | 23 | my %config = (hive => "Software", 24 | hivemask => 0x08, 25 | type => "Reg", 26 | output => "report", 27 | category => "Wireless", 28 | class => 0, 29 | hasShortDescr => 1, 30 | hasDescr => 0, 31 | hasRefs => 0, 32 | osmask => 1, #I've only verified this on XP 33 | version => 20120925); 34 | 35 | sub getConfig{return \%config} 36 | sub getShortDescr { 37 | return "Get WZCSVC SSID Info (XP)"; 38 | } 39 | sub getDescr{} 40 | sub getRefs {} 41 | sub getHive {return $config{hive};} 42 | sub getVersion {return $config{version};} 43 | 44 | my $VERSION = getVersion(); 45 | my $error; 46 | 47 | sub pluginmain { 48 | my $class = shift; 49 | my $parent = ::getConfig(); 50 | 51 | ::logMsg("ssid v.".$VERSION); 52 | ::rptMsg("-" x 60); 53 | ::rptMsg("ssid v.".$VERSION); 54 | ::rptMsg(getShortDescr()); 55 | ::rptMsg("Category: ".$config{category}); 56 | ::rptMsg(""); 57 | 58 | my $hive = $parent->{software}; 59 | # Get the NetworkCards values 60 | my %nc; 61 | if (%nc = getNetworkCards($hive)) { 62 | 63 | } 64 | else { 65 | ::rptMsg("Problem w/ SSIDs, getting NetworkCards: ".$error); 66 | return; 67 | } 68 | 69 | my $reg = Parse::Win32Registry->new($hive); 70 | my $root_key = $reg->get_root_key; 71 | my $key_path = "Microsoft\\WZCSVC\\Parameters\\Interfaces"; 72 | my $key; 73 | if ($key = $root_key->get_subkey($key_path)) { 74 | ::rptMsg("SSID"); 75 | ::rptMsg($key_path); 76 | ::rptMsg(""); 77 | my @subkeys = $key->get_list_of_subkeys(); 78 | if (scalar(@subkeys) > 0) { 79 | foreach my $s (@subkeys) { 80 | my $name = $s->get_name(); 81 | if (exists($nc{$name})) { 82 | ::rptMsg("NIC: ".$nc{$name}{descr}); 83 | ::rptMsg("Key LastWrite: ".gmtime($s->get_timestamp())." UTC"); 84 | ::rptMsg(""); 85 | my @vals = $s->get_list_of_values(); 86 | if (scalar(@vals) > 0) { 87 | foreach my $v (@vals) { 88 | my $n = $v->get_name(); 89 | if ($n =~ m/^Static#/) { 90 | my $data = $v->get_data(); 91 | # my $w = unpack("V",substr($data,0x04,0x04)); 92 | # printf "dwCtlFlags = 0x%x\n",$w; 93 | 94 | my $l = unpack("V",substr($data, 0x10, 0x04)); 95 | my $ssid = substr($data,0x14,$l); 96 | 97 | my $tm = uc(unpack("H*",substr($data,0x08,0x06))); 98 | my @t = split(//,$tm); 99 | my $mac = $t[0].$t[1]."-".$t[2].$t[3]."-".$t[4].$t[5]."-".$t[6].$t[7]."-".$t[8].$t[9]."-".$t[10].$t[11]; 100 | 101 | my ($t1,$t2) = unpack("VV",substr($data,0x2B8,8)); 102 | my $t = ::getTime($t1,$t2); 103 | my $str = sprintf gmtime($t)." MAC: %-18s %-8s",$mac,$ssid; 104 | ::rptMsg($str); 105 | } 106 | } 107 | } 108 | else { 109 | ::rptMsg($name." has no values."); 110 | } 111 | } 112 | } 113 | } 114 | else { 115 | ::rptMsg($key_path." has no subkeys."); 116 | } 117 | } 118 | else { 119 | ::rptMsg($key_path." not found."); 120 | } 121 | 122 | # Now, go to the EAPOL key, locate the appropriate subkeys and parse out 123 | # any available SSIDs 124 | # EAPOL is Extensible Authentication Protocol over LAN 125 | my $key_path = "Microsoft\\EAPOL\\Parameters\\Interfaces"; 126 | my $key; 127 | if ($key = $root_key->get_subkey($key_path)) { 128 | ::rptMsg(""); 129 | ::rptMsg($key_path); 130 | ::rptMsg(""); 131 | my @subkeys = $key->get_list_of_subkeys(); 132 | if (scalar(@subkeys) > 0) { 133 | foreach my $s (@subkeys) { 134 | my $name = $s->get_name(); 135 | if (exists $nc{$name}) { 136 | ::rptMsg("NIC: ".$nc{$name}{descr}); 137 | } 138 | else { 139 | ::rptMsg("NIC: ".$name); 140 | } 141 | ::rptMsg("LastWrite time: ".gmtime($s->get_timestamp())." UTC"); 142 | 143 | my @vals = $s->get_list_of_values(); 144 | my %eapol; 145 | if (scalar(@vals) > 0) { 146 | foreach my $v (@vals) { 147 | $eapol{$v->get_name()} = parseEAPOLData($v->get_data()); 148 | } 149 | foreach my $i (sort {$a <=> $b} keys %eapol) { 150 | my $str = sprintf "%-3d %s",$i,$eapol{$i}; 151 | ::rptMsg($str); 152 | } 153 | } 154 | ::rptMsg(""); 155 | } 156 | } 157 | else { 158 | ::rtpMsg($key_path." has no subkeys."); 159 | } 160 | } 161 | else { 162 | ::rptMsg($key_path." not found."); 163 | } 164 | } 165 | 166 | sub getNetworkCards { 167 | my $hive = shift; 168 | my %nc; 169 | my $reg = Parse::Win32Registry->new($hive); 170 | my $root_key = $reg->get_root_key; 171 | my $key_path = "Microsoft\\Windows NT\\CurrentVersion\\NetworkCards"; 172 | my $key; 173 | if ($key = $root_key->get_subkey($key_path)) { 174 | my @subkeys = $key->get_list_of_subkeys(); 175 | if (scalar(@subkeys) > 0) { 176 | foreach my $s (@subkeys) { 177 | my $service = $s->get_value("ServiceName")->get_data(); 178 | $nc{$service}{descr} = $s->get_value("Description")->get_data(); 179 | $nc{$service}{lastwrite} = $s->get_timestamp(); 180 | } 181 | } 182 | else { 183 | $error = $key_path." has no subkeys."; 184 | } 185 | } 186 | else { 187 | $error = $key_path." not found."; 188 | } 189 | return %nc; 190 | } 191 | 192 | sub parseEAPOLData { 193 | my $data = shift; 194 | my $size = unpack("V",substr($data,0x10,4)); 195 | return substr($data,0x14,$size); 196 | } 197 | 198 | 1; -------------------------------------------------------------------------------- /Engine.pm: -------------------------------------------------------------------------------- 1 | package Engine; 2 | #--------------------------------------------------------------------- 3 | # Engine - Forensic System Scanner Engine 4 | # 5 | # 6 | # 7 | # created for ASI 8 | # Author: H. Carvey, keydet89@yahoo.com 9 | # 10 | # This software is released under the Perl Artistic License: 11 | # http://dev.perl.org/licenses/artistic.html 12 | #--------------------------------------------------------------------- 13 | use strict; 14 | use Exporter; 15 | 16 | use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS); 17 | 18 | $VERSION = 0.1; 19 | @ISA = qw(Exporter); 20 | @EXPORT = (); 21 | @EXPORT_OK = qw(new); 22 | 23 | use WinSetup; 24 | 25 | # Global variables 26 | # self reference 27 | my $self = {}; 28 | my $win; 29 | my %sys = (); 30 | my %plugins0; # hash-of-lists for system class plugins 31 | my %plugins1; # hash-of-lists for user class plugins 32 | 33 | #--------------------------------------------------------------------- 34 | # new() 35 | # 36 | #--------------------------------------------------------------------- 37 | sub new { 38 | my $class = shift; 39 | $win = WinSetup->new(); 40 | return bless($self, $class); 41 | } 42 | 43 | #--------------------------------------------------------------------- 44 | # systemDir() 45 | # 46 | #--------------------------------------------------------------------- 47 | sub systemDir { 48 | my $class = $_[0]; 49 | if ($_[1] eq "") { 50 | return $self->{systemdir}; 51 | } 52 | else { 53 | # should end in system32 54 | $self->{systemdir} = $_[1]; 55 | $self->{systemdir} .= "\\" unless ($self->{systemdir} =~ m/\\$/); 56 | my @segs = split(/\\/,$self->{systemdir}); 57 | my $num = scalar(@segs); 58 | $self->{drive} = join('\\',@segs[0..($num - 3)])."\\"; 59 | } 60 | } 61 | 62 | #--------------------------------------------------------------------- 63 | # 64 | # 65 | #--------------------------------------------------------------------- 66 | 67 | #--------------------------------------------------------------------- 68 | # getSystemInfo () 69 | # 70 | #--------------------------------------------------------------------- 71 | sub getSystemInfo { 72 | my %hives = $win->setup($self->{systemdir}); 73 | %sys = $win->getOSData(); 74 | $sys{drive} = $self->{drive}; 75 | 76 | # Need to get SystemRoot path for the image, so it can be passed to the 77 | # plugins 78 | my ($d,$w) = split(/\\/,$sys{"SystemRoot"},2); 79 | $sys{systemroot} = $sys{drive}.$w; 80 | $sys{systemroot} .= "\\" unless ($sys{systemroot} =~ m/\\$/); 81 | $self->{osflag} = $sys{osflag}; 82 | return %sys; 83 | } 84 | 85 | sub getUserInfo { 86 | my %users = $win->getUserData(); 87 | return %users; 88 | } 89 | 90 | #--------------------------------------------------------------------- 91 | # pluginsDir() 92 | # Returns the plugins dir path 93 | #--------------------------------------------------------------------- 94 | sub pluginsDir { 95 | my $class = $_[0]; 96 | ($_[1] eq "") ? (return $self->{plugindir}) : ($self->{plugindir} = $_[1]); 97 | } 98 | 99 | #--------------------------------------------------------------------- 100 | # sortPlugins() 101 | # 102 | # Accesses the plugins dir to get a list of plugins 103 | # 1. Check OSMask value (osflag & osmask) 104 | # 2. Generates (2) hashes-of-arrays, one for system class plugins, the 105 | # other for user class plugins 106 | # 3. The keys of each hash are the categories. 107 | # 4. UI must then call the appropriate function to retrieve the 108 | # appropropriate hash 109 | # 110 | #--------------------------------------------------------------------- 111 | sub sortPlugins { 112 | my $class = shift; 113 | my $output = shift || "report"; 114 | my %plugins; 115 | 116 | %plugins0 = (); 117 | %plugins1 = (); 118 | 119 | my $cwd = Win32::GetCwd(); 120 | $cwd .= "\\" unless ($cwd =~ m/\\$/); 121 | my $pluginsdir = $cwd."plugins\\"; 122 | 123 | my @plugs; 124 | opendir(DIR,$pluginsdir); 125 | @plugs = grep {!/^\./ && m/\.pl$/} readdir(DIR); 126 | closedir(DIR); 127 | 128 | foreach my $p (@plugs) { 129 | eval { 130 | require $pluginsdir.$p; 131 | my $pkg = (split(/\./,$p,2))[0]; 132 | # Get the config information, create the list of plugins to run 133 | my $pluginconfig = $pkg->getConfig(); 134 | 135 | if (($pluginconfig->{osmask} & $self->{osflag}) && ($pluginconfig->{"output"} =~ m/^$output/i)) { 136 | if ($pluginconfig->{class} == 0) { 137 | my @categories = split(/,/,$pluginconfig->{category}); 138 | foreach my $c (@categories) { 139 | push(@{$plugins0{$c}},$pluginsdir.$p); 140 | } 141 | } 142 | else { 143 | my @categories = split(/,/,$pluginconfig->{category}); 144 | foreach my $c (@categories) { 145 | push(@{$plugins1{$c}},$pluginsdir.$p); 146 | } 147 | } 148 | } 149 | 150 | }; 151 | ::logMsg("Engine Error: ".$@) if ($@); 152 | } 153 | } 154 | #---------------------------------------------------------------- 155 | # getSystemPlugins() 156 | # returns the hash-of-lists of system plugins 157 | #---------------------------------------------------------------- 158 | sub getSystemPlugins { 159 | return %plugins0; 160 | } 161 | 162 | #---------------------------------------------------------------- 163 | # getUserPlugins() 164 | # returns the hash-of-lists of user plugins 165 | #---------------------------------------------------------------- 166 | sub getUserPlugins { 167 | return %plugins1; 168 | } 169 | 170 | #---------------------------------------------------------------- 171 | # getError() 172 | # returns the error message for the module 173 | #---------------------------------------------------------------- 174 | sub getError {return $self->{error};} 175 | 176 | 177 | 1; 178 | __END__ 179 | 180 | =head1 NAME 181 | 182 | Name 183 | 184 | =head1 SYNOPSIS 185 | 186 | see example files 187 | 188 | =head1 DESCRIPTION 189 | 190 | Blah is a Perl module ... 191 | 192 | =head1 SEE ALSO 193 | 194 | 195 | 196 | =head1 AUTHOR 197 | 198 | Harlan Carvey, Ekeydet89@yahoo.comE 199 | 200 | =head1 COPYRIGHT AND LICENSE 201 | 202 | Copyright (C) 2012 203 | 204 | This library is free software; you can redistribute it and/or modify 205 | it as you like. However, please be sure to provide proper credit where 206 | it is due. 207 | 208 | =cut 209 | -------------------------------------------------------------------------------- /plugins/evtrpt.pl: -------------------------------------------------------------------------------- 1 | package evtrpt; 2 | #----------------------------------------------------------- 3 | # evtrpt 4 | # Generate statistics for Event Logs/.evt files from WinXP/2003 5 | # systems 6 | # 7 | # Change History: 8 | # 20120816 - updated for latest version of the scanner 9 | # 20100928 - created 10 | # 11 | # References: 12 | # 13 | # 14 | # copyright 2012 15 | # Author: H. Carvey, keydet89@yahoo.com 16 | #----------------------------------------------------------- 17 | use strict; 18 | 19 | my %config = (hasShortDescr => 1, 20 | shortDescr => "Generate EVT reporting statistics", 21 | category => "Reporting", 22 | output => "report", 23 | class => 0, # system = 0, user = 1 24 | type => "File", 25 | osmask => 3, #XP, 2003 26 | version => 20120816); 27 | 28 | sub getConfig{return \%config} 29 | my $VERSION = $config{version}; 30 | sub getShortDescr { return $config{shortDescr};} 31 | sub pluginmain { 32 | my $class = shift; 33 | # my $win = WinFile->new(); 34 | my $parent = ::getConfig(); 35 | my $drv = $parent->{drive}; 36 | ::logMsg("evtrpt v.".$VERSION); 37 | ::rptMsg("-" x 60); 38 | ::rptMsg("evtrpt v.".$VERSION); 39 | ::rptMsg(getShortDescr()); 40 | ::rptMsg("Category: ".$config{category}); 41 | ::rptMsg(""); 42 | 43 | my @evt = qw/secevent sysevent appevent/; 44 | foreach my $e (@evt) { 45 | my $file = $drv."Windows\\system32\\config\\".$e."\.evt"; 46 | ::rptMsg("File: ".$file); 47 | if (-e $file && -f $file) { 48 | parseEvt($file); 49 | ::rptMsg(""); 50 | } 51 | else { 52 | ::rptMsg($file." not found."); 53 | } 54 | } 55 | } 56 | #--------------------------------------------------------------------- 57 | # parseEvt() 58 | #--------------------------------------------------------------------- 59 | 60 | sub parseEvt { 61 | my $file = shift; 62 | my $data; 63 | # Structures to maintain data 64 | my %freq_types; 65 | my %dates; 66 | my %er; 67 | my %types = (0x0001 => "Error", 68 | 0x0010 => "Failure", 69 | 0x0008 => "Success", 70 | 0x0004 => "Info", 71 | 0x0002 => "Warn"); 72 | 73 | my $size = (stat($file))[7]; 74 | my $ofs = 0; 75 | open(FH,"<",$file) || die "Could not open $file: $!\n"; 76 | binmode(FH); 77 | 78 | while ($ofs < $size) { 79 | seek(FH,$ofs,0); 80 | read(FH,$data,4); 81 | if (unpack("V",$data) == 0x654c664c) { 82 | 83 | seek(FH,$ofs - 4,0); 84 | read(FH,$data,4); 85 | my $l = unpack("V",$data); 86 | 87 | seek(FH,$ofs - 4,0); 88 | read(FH,$data,$l); 89 | my $f = unpack("V",substr($data,$l - 4,4)); 90 | 91 | # printf "Record located at offset 0x%08x; Length = 0x%x, Final Length = 0x%x\n",$ofs - 4,$l,$f; 92 | if ($l == $f) { 93 | # print "\t-> Valid record\n"; 94 | # print "\t**HDR Record\n" if ($l == 0x30); 95 | # print "\t**EOF Record\n" if ($l == 0x28); 96 | 97 | if ($l > 0x38) { 98 | my %r = parseRec($data); 99 | 100 | $freq_types{$r{evt_type}}++; 101 | 102 | if ($r{source} eq "Security" && ($r{evt_id} > 527 && $r{evt_id} < 541)) { 103 | my $type = getLoginType($r{evt_id},$r{strings}); 104 | $r{evt_id} = $r{evt_id}.",".$type; 105 | } 106 | 107 | if (exists $er{$r{source}.":".$r{evt_id}}) { 108 | $er{$r{source}.":".$r{evt_id}}++; 109 | } 110 | else { 111 | $er{$r{source}.":".$r{evt_id}} = 1; 112 | } 113 | $dates{$r{time_gen}} = 1; 114 | } 115 | $ofs += $l; 116 | } 117 | else { 118 | # If this check ($l == $f) fails, then the record isn't valid 119 | $ofs += 4; 120 | } 121 | } 122 | else { 123 | $ofs += 4; 124 | } 125 | } 126 | close(FH); 127 | 128 | # Print out the report 129 | #print "Total number of event records counted: ".$total."\n"; 130 | ::rptMsg("-" x 30); 131 | ::rptMsg("Event Source/ID Frequency"); 132 | ::rptMsg(""); 133 | ::rptMsg(sprintf "%-40s %10s %8s","Source","Event ID","Count"); 134 | ::rptMsg(sprintf "%-40s %10s %8s","-" x 10,"-" x 8,"-" x 5); 135 | my $er_total = 0; 136 | foreach my $i (sort keys %er) { 137 | my ($source,$id) = split(/:/,$i,2); 138 | ::rptMsg(sprintf "%-40s %10s %8s",$source,$id,$er{$i}); 139 | $er_total += $er{$i}; 140 | } 141 | ::rptMsg(""); 142 | ::rptMsg("Total: ".$er_total); 143 | ::rptMsg("-" x 30); 144 | ::rptMsg("Event Type Frequency"); 145 | ::rptMsg(""); 146 | ::rptMsg(sprintf "%-15s %10s","Type","Count"); 147 | ::rptMsg(sprintf "%-15s %10s","-" x 10,"-" x 5); 148 | my $tot_f = 0; 149 | foreach my $t (keys %freq_types) { 150 | ::rptMsg(sprintf "%-15s %10s",$types{$t},$freq_types{$t}); 151 | $tot_f += $freq_types{$t}; 152 | } 153 | # Put this in just as a check to make sure that the total 154 | # adds up correctly 155 | ::rptMsg(""); 156 | ::rptMsg("Total: ".$tot_f); 157 | ::rptMsg("-" x 30); 158 | ::rptMsg("Date Range (UTC)"); 159 | my @daterange = sort {$a <=> $b} keys %dates; 160 | my $i = scalar(@daterange) - 1; 161 | ::rptMsg(gmtime($daterange[0])." to ".gmtime($daterange[$i])); 162 | } 163 | 164 | #--------------------------------------------------------------------- 165 | # parseRec() 166 | # Parse the binary Event Record 167 | # References: 168 | # http://msdn.microsoft.com/en-us/library/aa363646(VS.85).aspx 169 | #--------------------------------------------------------------------- 170 | sub parseRec { 171 | my $data = shift; 172 | my %rec; 173 | my $hdr = substr($data,0,56); 174 | ($rec{length},$rec{magic},$rec{rec_num},$rec{time_gen},$rec{time_wrt}, 175 | $rec{evt_id},$rec{evt_id2},$rec{evt_type},$rec{num_str},$rec{category}, 176 | $rec{c_rec},$rec{str_ofs},$rec{sid_len},$rec{sid_ofs},$rec{data_len}, 177 | $rec{data_ofs}) = unpack("V5v5x2V6",$hdr); 178 | 179 | # Get the end of the Source/Computername field 180 | my $src_end; 181 | ($rec{sid_len} == 0) ? ($src_end = $rec{str_ofs}) : ($src_end = $rec{sid_ofs}); 182 | my $s = substr($data,0x38,$src_end); 183 | ($rec{source},$rec{computername}) = (split(/\x00\x00/,$s))[0,1]; 184 | $rec{source} =~ s/\x00//g; 185 | $rec{computername} =~ s/\x00//g; 186 | 187 | # Get SID 188 | if ($rec{sid_len} > 0) { 189 | my $sid = substr($data,$rec{sid_ofs},$rec{sid_len}); 190 | $rec{sid} = translateSID($sid); 191 | } 192 | else { 193 | $rec{sid} = "N/A"; 194 | } 195 | 196 | # Get strings from event record 197 | my $strs = substr($data,$rec{str_ofs},$rec{data_ofs} - $rec{str_ofs}); 198 | my @str = split(/\x00\x00/,$strs, $rec{num_str}); 199 | $rec{strings} = join(',',@str); 200 | $rec{strings} =~ s/\x00//g; 201 | $rec{strings} =~ s/\x09//g; 202 | $rec{strings} =~ s/\n/ /g; 203 | $rec{strings} =~ s/\x0D//g; 204 | 205 | return %rec; 206 | } 207 | 208 | #--------------------------------------------------------------------- 209 | # translateSID() 210 | # Translate binary data into a SID 211 | # References: 212 | # http://blogs.msdn.com/oldnewthing/archive/2004/03/15/89753.aspx 213 | # http://support.microsoft.com/kb/286182/ 214 | # http://support.microsoft.com/kb/243330 215 | #--------------------------------------------------------------------- 216 | sub translateSID { 217 | my $sid = $_[0]; 218 | my $len = length($sid); 219 | my $revision; 220 | my $dashes; 221 | my $idauth; 222 | if ($len < 12) { 223 | # Is a SID ever less than 12 bytes? 224 | return "SID less than 12 bytes"; 225 | } 226 | elsif ($len == 12) { 227 | $revision = unpack("C",substr($sid,0,1)); 228 | $dashes = unpack("C",substr($sid,1,1)); 229 | $idauth = unpack("H*",substr($sid,2,6)); 230 | $idauth =~ s/^0+//g; 231 | my $sub = unpack("V",substr($sid,8,4)); 232 | return "S-".$revision."-".$idauth."-".$sub; 233 | } 234 | elsif ($len > 12) { 235 | $revision = unpack("C",substr($sid,0,1)); 236 | $dashes = unpack("C",substr($sid,1,1)); 237 | $idauth = unpack("H*",substr($sid,2,6)); 238 | $idauth =~ s/^0+//g; 239 | my @sub = unpack("V*",substr($sid,8,($len-2))); 240 | my $rid = unpack("v",substr($sid,24,2)); 241 | my $s = join('-',@sub); 242 | return "S-".$revision."-".$idauth."-".$s; 243 | # return "S-".$revision."-".$idauth."-".$s."-".$rid; 244 | } 245 | else { 246 | # Nothing to do 247 | } 248 | } 249 | 250 | #--------------------------------------------------------------------- 251 | # getLoginType() 252 | # Returns a login type from the event strings 253 | #--------------------------------------------------------------------- 254 | sub getLoginType { 255 | my $id = shift; 256 | my $str = shift; 257 | my @vals = split(/,/,$str); 258 | if ($id == 528 || $id == 538 || $id == 540) { 259 | return $vals[4]; 260 | } 261 | elsif ($id == 529 || $id =~ m/^53/) { 262 | return $vals[2]; 263 | } 264 | else { 265 | return 0; 266 | } 267 | } 268 | 269 | 1; -------------------------------------------------------------------------------- /plugins/appcompatcache.pl: -------------------------------------------------------------------------------- 1 | #----------------------------------------------------------- 2 | # appcompatcache.pl 3 | # 4 | # History: 5 | # 20120823 - updated to Forensic Scanner format 6 | # 20120817 - updated to address issue with residual data in XP data blocks 7 | # 20120722 - updated the %config hash 8 | # 20120523 - updated to send all files to a single hash, and check for temp paths 9 | # 20120515 - Updated to support 64-bit Win2003 and Vista/Win2008 10 | # 20120424 - Modified/updated 11 | # 20120418 - created 12 | # 13 | # References: 14 | # Blog post: https://blog.mandiant.com/archives/2459 15 | # Whitepaper: http://fred.mandiant.com/Whitepaper_ShimCacheParser.pdf 16 | # Tool: https://github.com/mandiant/ShimCacheParser 17 | # 18 | # This plugin is based solely on the work and examples provided by Mandiant; 19 | # thanks to them for sharing this information, and making the plugin possible. 20 | # 21 | # copyright 2012 22 | # Author: H. Carvey, keydet89@yahoo.com 23 | #----------------------------------------------------------- 24 | package appcompatcache; 25 | use strict; 26 | 27 | my %config = (hive => "System", 28 | hivemask => 4, 29 | output => "report", 30 | category => "Program Execution", 31 | hasShortDescr => 1, 32 | class => 0, 33 | hasDescr => 0, 34 | hasRefs => 0, 35 | osmask => 31, #XP - Win7 36 | version => 20120823); 37 | 38 | sub getConfig{return \%config} 39 | sub getShortDescr { 40 | return "Parse files from System hive Shim Cache"; 41 | } 42 | sub getDescr{} 43 | sub getRefs {} 44 | sub getHive {return $config{hive};} 45 | sub getVersion {return $config{version};} 46 | 47 | my $VERSION = getVersion(); 48 | my %files; 49 | my @temps; 50 | 51 | sub pluginmain { 52 | my $class = shift; 53 | my $parent = ::getConfig(); 54 | ::logMsg("appcompatcache v.".$VERSION); 55 | ::rptMsg("-" x 60); 56 | ::rptMsg("appcompatcache v.".$VERSION); 57 | ::rptMsg(getShortDescr()); 58 | ::rptMsg("Category: ".$config{category}); 59 | ::rptMsg(""); 60 | 61 | my $reg = Parse::Win32Registry->new($parent->{system}); 62 | my $root_key = $reg->get_root_key; 63 | # First thing to do is get the ControlSet00x marked current...this is 64 | # going to be used over and over again in plugins that access the system 65 | # file 66 | my ($current,$ccs); 67 | my $key_path = 'Select'; 68 | my $key; 69 | if ($key = $root_key->get_subkey($key_path)) { 70 | $current = $key->get_value("Current")->get_data(); 71 | $ccs = "ControlSet00".$current; 72 | my $appcompat_path = $ccs."\\Control\\Session Manager"; 73 | my $appcompat; 74 | if ($appcompat = $root_key->get_subkey($appcompat_path)) { 75 | 76 | my $app_data; 77 | 78 | eval { 79 | $app_data = $appcompat->get_subkey("AppCompatibility")->get_value("AppCompatCache")->get_data(); 80 | }; 81 | 82 | eval { 83 | $app_data = $appcompat->get_subkey("AppCompatCache")->get_value("AppCompatCache")->get_data(); 84 | }; 85 | 86 | # ::rptMsg("Length of data: ".length($app_data)); 87 | my $sig = unpack("V",substr($app_data,0,4)); 88 | ::rptMsg(sprintf "Signature: 0x%x",$sig); 89 | 90 | if ($sig == 0xdeadbeef) { 91 | eval { 92 | appXP32Bit($app_data); 93 | }; 94 | } 95 | elsif ($sig == 0xbadc0ffe) { 96 | eval { 97 | appWin2k3($app_data); 98 | }; 99 | } 100 | elsif ($sig == 0xbadc0fee) { 101 | eval { 102 | appWin7($app_data); 103 | }; 104 | 105 | } 106 | else { 107 | ::rptMsg("Unknown signature"); 108 | } 109 | # this is where we print out the files 110 | foreach my $f (keys %files) { 111 | ::rptMsg($f); 112 | push(@temps,$f) if (grep(/temp/i,$f)); 113 | ::rptMsg("ModTime: ".gmtime($files{$f}{modtime})." Z"); 114 | ::rptMsg("UpdTime: ".gmtime($files{$f}{updtime})." Z") if (exists $files{$f}{updtime}); 115 | ::rptMsg("Size : ".$files{$f}{size}." bytes") if (exists $files{$f}{size}); 116 | ::rptMsg("Executed") if (exists $files{$f}{executed}); 117 | ::rptMsg(""); 118 | } 119 | 120 | if (scalar(@temps) > 0) { 121 | ::rptMsg("Temp paths found:"); 122 | foreach (@temps) { 123 | ::rptMsg($_); 124 | } 125 | } 126 | } 127 | else { 128 | ::rptMsg($appcompat_path." not found."); 129 | } 130 | } 131 | else { 132 | ::rptMsg($key_path." not found."); 133 | } 134 | } 135 | 136 | #----------------------------------------------------------- 137 | # appXP32Bit() 138 | # parse 32-bit XP data 139 | #----------------------------------------------------------- 140 | sub appXP32Bit { 141 | my $data = shift; 142 | ::rptMsg("WinXP, 32-bit"); 143 | # header is 400 bytes; each structure is 552 bytes in size 144 | my $num_entries = unpack("V",substr($data,4,4)); 145 | 146 | # print "Num entries: ".$num_entries."\n"; 147 | eval { 148 | foreach my $i (0..($num_entries - 1)) { 149 | my $x = substr($data,(400 + ($i * 552)),552); 150 | my $file = (split(/\00\00/,substr($x,0,488)))[0]; 151 | $file =~ s/\00//g; 152 | $file =~ s/^\\\?\?\\//; 153 | 154 | my ($mod1,$mod2) = unpack("VV",substr($x,528,8)); 155 | my $modtime = ::getTime($mod1,$mod2); 156 | my ($sz1,$sz2) = unpack("VV",substr($x,536,8)); 157 | my $sz; 158 | ($sz2 == 0)?($sz = $sz1):($sz = "Too big"); 159 | my ($up1,$up2) = unpack("VV",substr($x,544,8)); 160 | my $updtime = ::getTime($up1,$up2); 161 | 162 | $files{$file}{size} = $sz; 163 | $files{$file}{modtime} = $modtime; 164 | $files{$file}{updtime} = $updtime; 165 | } 166 | }; 167 | ::rptMsg("appXP32Bit Error: ".$@) if ($@); 168 | } 169 | #----------------------------------------------------------- 170 | # appWin2k3() 171 | # parse Win2k3, Vista, Win2k8 data 172 | #----------------------------------------------------------- 173 | sub appWin2k3 { 174 | my $data = shift; 175 | my $num_entries = unpack("V",substr($data,4,4)); 176 | # ::rptMsg("Num_entries: ".$num_entries); 177 | my $struct_sz = 0; 178 | my ($len,$max_len,$padding) = unpack("vvV",substr($data,8,8)); 179 | if (($max_len - $len) == 2) { 180 | # if $padding == 0, 64-bit; otherwise, 32-bit 181 | if ($padding == 0) { 182 | $struct_sz = 32; 183 | ::rptMsg("Win2K3/Vista/Win2K8, 64-bit"); 184 | } 185 | else { 186 | $struct_sz = 24; 187 | ::rptMsg("Win2K3/Vista/Win2K8, 32-bit"); 188 | } 189 | } 190 | 191 | foreach my $i (0..($num_entries - 1)) { 192 | my $struct = substr($data,(8 + ($struct_sz * $i)),$struct_sz); 193 | if ($struct_sz == 24) { 194 | my ($len,$max_len,$ofs,$t0,$t1,$f0,$f1) = unpack("vvVVVVV",$struct); 195 | 196 | my $file = substr($data,$ofs,$len); 197 | $file =~ s/\00//g; 198 | $file =~ s/^\\\?\?\\//; 199 | my $t = ::getTime($t0,$t1); 200 | # ::rptMsg($file); 201 | # ::rptMsg(" LastMod: ".gmtime($t)." Z"); 202 | # ::rptMsg(" [Executed]") if (($f0 < 4) && ($f0 & 0x2)); 203 | # ::rptMsg(""); 204 | $files{$file}{modtime} = $t; 205 | $files{$file}{executed} = 1 if (($f0 < 4) && ($f0 & 0x2)); 206 | } 207 | elsif ($struct_sz == 32) { 208 | my ($len,$max_len,$padding,$ofs0,$ofs1,$t0,$t1,$f0,$f1) = unpack("vvVVVVVVV",$struct); 209 | my $file = substr($data,$ofs0,$len); 210 | $file =~ s/\00//g; 211 | $file =~ s/^\\\?\?\\//; 212 | my $t = ::getTime($t0,$t1); 213 | # ::rptMsg($file); 214 | # ::rptMsg(" LastMod: ".gmtime($t)." Z"); 215 | # ::rptMsg(" Size : ".$f0) if (($f1 == 0) && ($f0 > 3)); 216 | # ::rptMsg(" [Executed]") if (($f0 < 4) && ($f0 & 0x2)); 217 | # ::rptMsg(""); 218 | $files{$file}{modtime} = $t; 219 | $files{$file}{size} = $f0 if (($f1 == 0) && ($f0 > 3)); 220 | $files{$file}{executed} = 1 if (($f0 < 4) && ($f0 & 0x2)); 221 | } 222 | else { 223 | 224 | 225 | } 226 | } 227 | } 228 | 229 | #----------------------------------------------------------- 230 | # appWin7() 231 | # parse Win2k8R2, Win7 data 232 | #----------------------------------------------------------- 233 | sub appWin7 { 234 | my $data = shift; 235 | my $struct_sz = 0; 236 | my $num_entries = unpack("V",substr($data,4,4)); 237 | # ::rptMsg("Num_entries: ".$num_entries); 238 | # 128-byte header 239 | my ($len,$max_len,$padding) = unpack("vvV",substr($data,128,8)); 240 | if (($max_len - $len) == 2) { 241 | if ($padding == 0) { 242 | $struct_sz = 48; 243 | ::rptMsg("Win2K8R2/Win7, 64-bit"); 244 | } 245 | else { 246 | $struct_sz = 32; 247 | ::rptMsg("Win2K8R2/Win7, 32-bit"); 248 | } 249 | } 250 | 251 | foreach my $i (0..($num_entries - 1)) { 252 | my $struct = substr($data,(128 + ($struct_sz * $i)),$struct_sz); 253 | if ($struct_sz == 32) { 254 | my ($len,$max_len,$ofs,$t0,$t1,$f0,$f1) = unpack("vvV5x8",$struct); 255 | my $file = substr($data,$ofs,$len); 256 | $file =~ s/\00//g; 257 | $file =~ s/^\\\?\?\\//; 258 | my $t = ::getTime($t0,$t1); 259 | # ::rptMsg($file); 260 | # ::rptMsg(" LastModTime: ".gmtime($t)." Z"); 261 | # ::rptMsg(" [Executed]") if ($f0 & 0x2); 262 | # ::rptMsg(""); 263 | $files{$file}{modtime} = $t; 264 | $files{$file}{executed} = 1 if ($f0 & 0x2); 265 | } 266 | else { 267 | my ($len,$max_len,$padding,$ofs0,$ofs1,$t0,$t1,$f0,$f1) = unpack("vvV7x16",$struct); 268 | my $file = substr($data,$ofs0,$len); 269 | $file =~ s/\00//g; 270 | $file =~ s/^\\\?\?\\//; 271 | my $t = ::getTime($t0,$t1); 272 | # ::rptMsg($file); 273 | # ::rptMsg(" LastModTime: ".gmtime($t)." Z"); 274 | # ::rptMsg(" [Executed]") if ($f0 & 0x2); 275 | # ::rptMsg(""); 276 | $files{$file}{modtime} = $t; 277 | $files{$file}{executed} = 1 if ($f0 & 0x2); 278 | } 279 | } 280 | } 281 | 282 | 283 | #----------------------------------------------------------- 284 | # printData() 285 | # subroutine used primarily for debugging; takes an arbitrary 286 | # length of binary data, prints it out in hex editor-style 287 | # format for easy debugging 288 | #----------------------------------------------------------- 289 | sub printData { 290 | my $data = shift; 291 | my $len = length($data); 292 | my $tag = 1; 293 | my $cnt = 0; 294 | 295 | my $loop = $len/16; 296 | $loop++ if ($len%16); 297 | 298 | foreach my $cnt (0..($loop - 1)) { 299 | # while ($tag) { 300 | my $left = $len - ($cnt * 16); 301 | 302 | my $n; 303 | ($left < 16) ? ($n = $left) : ($n = 16); 304 | 305 | my $seg = substr($data,$cnt * 16,$n); 306 | my @str1 = split(//,unpack("H*",$seg)); 307 | 308 | my @s3; 309 | my $str = ""; 310 | 311 | foreach my $i (0..($n - 1)) { 312 | $s3[$i] = $str1[$i * 2].$str1[($i * 2) + 1]; 313 | 314 | if (hex($s3[$i]) > 0x1f && hex($s3[$i]) < 0x7f) { 315 | $str .= chr(hex($s3[$i])); 316 | } 317 | else { 318 | $str .= "\."; 319 | } 320 | } 321 | my $h = join(' ',@s3); 322 | ::rptMsg(sprintf "0x%08x: %-47s ".$str,($cnt * 16),$h); 323 | } 324 | } 325 | 1; -------------------------------------------------------------------------------- /plugins/samparse.pl: -------------------------------------------------------------------------------- 1 | #----------------------------------------------------------- 2 | # samparse.pl 3 | # Parse the SAM hive file for user/group membership info 4 | # 5 | # Change history: 6 | # 20120925 - updated to RS format 7 | # 20120722 - updated %config hash 8 | # 20110303 - Fixed parsing of SID, added check for account type 9 | # Acct type determined based on Dustin Hulburt's "Forensic 10 | # Determination of a User's Logon Status in Windows" 11 | # from 10 Aug 2009 (link below) 12 | # 20100712 - Added References entry 13 | # 20091020 - Added extracting UserPasswordHint value 14 | # 20090413 - Added account creation date 15 | # 20080415 - created 16 | # 17 | # References 18 | # Source available here: http://pogostick.net/~pnh/ntpasswd/ 19 | # http://accessdata.com/downloads/media/Forensic_Determination_Users_Logon_Status.pdf 20 | # 21 | # copyright 2012 Quantum Analytics Research, LLC 22 | # Author: H. Carvey, keydet89@yahoo.com 23 | #----------------------------------------------------------- 24 | package samparse; 25 | use strict; 26 | 27 | my %config = (hive => "SAM", 28 | hivemask => 2, 29 | output => "report", 30 | type => "Reg", 31 | output => "report", 32 | category => "", 33 | osmask => 63, #XP - Win8 34 | hasShortDescr => 1, 35 | hasDescr => 0, 36 | hasRefs => 1, 37 | version => 20120925); 38 | 39 | sub getConfig{return \%config} 40 | 41 | sub getShortDescr { 42 | return "Parse SAM file for user & group mbrshp info"; 43 | } 44 | sub getDescr{} 45 | sub getRefs { 46 | my %refs = ("Well-known SIDs" => "http://support.microsoft.com/kb/243330"); 47 | return %refs; 48 | } 49 | sub getHive {return $config{hive};} 50 | sub getVersion {return $config{version};} 51 | 52 | my $VERSION = getVersion(); 53 | 54 | my %acb_flags = (0x0001 => "Account Disabled", 55 | 0x0002 => "Home directory required", 56 | 0x0004 => "Password not required", 57 | 0x0008 => "Temporary duplicate account", 58 | 0x0010 => "Normal user account", 59 | 0x0020 => "MNS logon user account", 60 | 0x0040 => "Interdomain trust account", 61 | 0x0080 => "Workstation trust account", 62 | 0x0100 => "Server trust account", 63 | 0x0200 => "Password does not expire", 64 | 0x0400 => "Account auto locked"); 65 | 66 | my %types = (0xbc => "Default Admin User", 67 | 0xd4 => "Custom Limited Acct", 68 | 0xb0 => "Default Guest Acct"); 69 | 70 | sub pluginmain { 71 | my $class = shift; 72 | my $parent = ::getConfig(); 73 | 74 | ::logMsg("samparse v.".$VERSION); 75 | ::rptMsg("-" x 60); 76 | ::rptMsg("samparse v.".$VERSION); 77 | ::rptMsg(getShortDescr()); 78 | ::rptMsg("Category: ".$config{category}); 79 | ::rptMsg(""); 80 | 81 | my $hive = $parent->{sam}; 82 | my $reg = Parse::Win32Registry->new($hive); 83 | my $root_key = $reg->get_root_key; 84 | ::rptMsg(""); 85 | # Get user information 86 | ::rptMsg("User Information"); 87 | ::rptMsg("-" x 25); 88 | my $key_path = 'SAM\\Domains\\Account\\Users'; 89 | my $key; 90 | if ($key = $root_key->get_subkey($key_path)) { 91 | my @user_list = $key->get_list_of_subkeys(); 92 | if (scalar(@user_list) > 0) { 93 | foreach my $u (@user_list) { 94 | my $rid = $u->get_name(); 95 | my $ts = $u->get_timestamp(); 96 | my $tag = "0000"; 97 | if ($rid =~ m/^$tag/) { 98 | my $v_value = $u->get_value("V"); 99 | my $v = $v_value->get_data(); 100 | my %v_val = parseV($v); 101 | $rid =~ s/^0000//; 102 | $rid = hex($rid); 103 | 104 | my $c_date; 105 | eval { 106 | my $create_path = $key_path."\\Names\\".$v_val{name}; 107 | if (my $create = $root_key->get_subkey($create_path)) { 108 | $c_date = $create->get_timestamp(); 109 | } 110 | }; 111 | 112 | ::rptMsg("Username : ".$v_val{name}." [".$rid."]"); 113 | ::rptMsg("Full Name : ".$v_val{fullname}); 114 | ::rptMsg("User Comment : ".$v_val{comment}); 115 | ::rptMsg("Account Type : ".$v_val{type}); 116 | ::rptMsg("Account Created : ".gmtime($c_date)." Z") if ($c_date > 0); 117 | 118 | my $f_value = $u->get_value("F"); 119 | my $f = $f_value->get_data(); 120 | my %f_val = parseF($f); 121 | 122 | my $lastlogin; 123 | my $pwdreset; 124 | my $pwdfail; 125 | ($f_val{last_login_date} == 0) ? ($lastlogin = "Never") : ($lastlogin = gmtime($f_val{last_login_date})." Z"); 126 | ($f_val{pwd_reset_date} == 0) ? ($pwdreset = "Never") : ($pwdreset = gmtime($f_val{pwd_reset_date})." Z"); 127 | ($f_val{pwd_fail_date} == 0) ? ($pwdfail = "Never") : ($pwdfail = gmtime($f_val{pwd_fail_date})." Z"); 128 | 129 | my $pw_hint; 130 | eval { 131 | $pw_hint = $u->get_value("UserPasswordHint")->get_data(); 132 | $pw_hint =~ s/\00//g; 133 | }; 134 | ::rptMsg("Password Hint : ".$pw_hint) unless ($@); 135 | ::rptMsg("Last Login Date : ".$lastlogin); 136 | ::rptMsg("Pwd Reset Date : ".$pwdreset); 137 | ::rptMsg("Pwd Fail Date : ".$pwdfail); 138 | ::rptMsg("Login Count : ".$f_val{login_count}); 139 | foreach my $flag (keys %acb_flags) { 140 | ::rptMsg(" --> ".$acb_flags{$flag}) if ($f_val{acb_flags} & $flag); 141 | } 142 | ::rptMsg(""); 143 | } 144 | } 145 | } 146 | } 147 | else { 148 | ::rptMsg($key_path." not found."); 149 | } 150 | ::rptMsg("-" x 25); 151 | ::rptMsg("Group Membership Information"); 152 | ::rptMsg("-" x 25); 153 | # Get Group membership information 154 | my $key_path = 'SAM\\Domains\\Builtin\\Aliases'; 155 | if ($key = $root_key->get_subkey($key_path)) { 156 | my %grps; 157 | my @groups = $key->get_list_of_subkeys(); 158 | if (scalar(@groups) > 0) { 159 | foreach my $k (@groups) { 160 | my $name = $k->get_name(); 161 | if ($name =~ m/^0000/) { 162 | $grps{$name}{LastWrite} = $k->get_timestamp(); 163 | $grps{$name}{C_value} = $k->get_value("C")->get_data(); 164 | } 165 | } 166 | 167 | foreach my $k (keys %grps) { 168 | my $name = $k; 169 | $name =~ s/^0000//; 170 | my %c_val = parseC($grps{$k}{C_value}); 171 | ::rptMsg("Group Name : ".$c_val{group_name}." [".$c_val{num_users}."]"); 172 | ::rptMsg("LastWrite : ".gmtime($grps{$k}{LastWrite})." Z"); 173 | ::rptMsg("Group Comment : ".$c_val{comment}); 174 | if ($c_val{num_users} == 0) { 175 | ::rptMsg("Users : None"); 176 | }else { 177 | my %users = parseCUsers($grps{$k}{C_value}); 178 | if (scalar(keys %users) != $c_val{num_users}) { 179 | ::logMsg("parseC function reports ".$c_val{num_users}."; parseCUsers function returned ".(scalar(keys %users))); 180 | } 181 | ::rptMsg("Users :"); 182 | foreach my $u (keys %users) { 183 | ::rptMsg(" ".$u); 184 | } 185 | 186 | } 187 | ::rptMsg(""); 188 | } 189 | ::rptMsg("Analysis Tips:"); 190 | ::rptMsg(" - For well-known SIDs, see http://support.microsoft.com/kb/243330"); 191 | ::rptMsg(" - S-1-5-4 = Interactive"); 192 | ::rptMsg(" - S-1-5-11 = Authenticated Users"); 193 | ::rptMsg(" - Correlate the user SIDs to the output of the ProfileList plugin"); 194 | ::rptMsg(""); 195 | } 196 | else { 197 | ::rptMsg($key_path." has no subkeys."); 198 | } 199 | } 200 | else { 201 | ::rptMsg($key_path." not found."); 202 | } 203 | } 204 | 205 | sub parseF { 206 | my $f = shift; 207 | my %f_value = (); 208 | my @tv; 209 | # last login date 210 | @tv = unpack("VV",substr($f,8,8)); 211 | $f_value{last_login_date} = ::getTime($tv[0],$tv[1]); 212 | # password reset/acct creation 213 | @tv = unpack("VV",substr($f,24,8)); 214 | $f_value{pwd_reset_date} = ::getTime($tv[0],$tv[1]); 215 | # Account expires 216 | @tv = unpack("VV",substr($f,32,8)); 217 | $f_value{acct_exp_date} = ::getTime($tv[0],$tv[1]); 218 | # Incorrect password 219 | @tv = unpack("VV",substr($f,40,8)); 220 | $f_value{pwd_fail_date} = ::getTime($tv[0],$tv[1]); 221 | $f_value{rid} = unpack("V",substr($f,48,4)); 222 | $f_value{acb_flags} = unpack("v",substr($f,56,2)); 223 | $f_value{failed_count} = unpack("v",substr($f,64,2)); 224 | $f_value{login_count} = unpack("v",substr($f,66,2)); 225 | return %f_value; 226 | } 227 | 228 | sub parseV { 229 | my $v = shift; 230 | my %v_val = (); 231 | my $header = substr($v,0,44); 232 | my @vals = unpack("V*",$header); 233 | $v_val{type} = $types{$vals[1]}; 234 | $v_val{name} = _uniToAscii(substr($v,($vals[3] + 0xCC),$vals[4])); 235 | $v_val{fullname} = _uniToAscii(substr($v,($vals[6] + 0xCC),$vals[7])) if ($vals[7] > 0); 236 | $v_val{comment} = _uniToAscii(substr($v,($vals[9] + 0xCC),$vals[10])) if ($vals[10] > 0); 237 | return %v_val; 238 | } 239 | 240 | sub parseC { 241 | my $cv = $_[0]; 242 | my %c_val = (); 243 | my $header = substr($cv,0,0x34); 244 | my @vals = unpack("V*",$header); 245 | 246 | $c_val{group_name} = _uniToAscii(substr($cv,(0x34 + $vals[4]),$vals[5])); 247 | $c_val{comment} = _uniToAscii(substr($cv,(0x34 + $vals[7]),$vals[8])); 248 | $c_val{num_users} = $vals[12]; 249 | 250 | return %c_val; 251 | } 252 | 253 | sub parseCUsers { 254 | my $cv = $_[0]; 255 | my %members = (); 256 | my $header = substr($cv,0,0x34); 257 | my @vals = unpack("V*",$header); 258 | 259 | my $num = $vals[12]; 260 | 261 | my @users = (); 262 | my $ofs; 263 | if ($num > 0) { 264 | my $count = 0; 265 | foreach my $c (1..$num) { 266 | my $ofs = $vals[10] + 52 + $count; 267 | my $tmp = unpack("V",substr($cv,$ofs,4)); 268 | 269 | if ($tmp == 0x101) { 270 | $ofs++ if (unpack("C",substr($cv,$ofs,1)) == 0); 271 | $members{_translateSID(substr($cv,$ofs,12))} = 1; 272 | $count += 12; 273 | } 274 | elsif ($tmp == 0x501) { 275 | $members{_translateSID(substr($cv,$ofs,28))} = 1; 276 | $count += 28; 277 | } 278 | else { 279 | 280 | } 281 | } 282 | } 283 | return %members; 284 | } 285 | 286 | #--------------------------------------------------------------------- 287 | # _translateSID() 288 | # Translate binary data into a SID 289 | # References: 290 | # http://blogs.msdn.com/oldnewthing/archive/2004/03/15/89753.aspx 291 | # http://support.microsoft.com/kb/286182/ 292 | # http://support.microsoft.com/kb/243330 293 | #--------------------------------------------------------------------- 294 | sub _translateSID { 295 | my $sid = $_[0]; 296 | my $len = length($sid); 297 | my $revision; 298 | my $dashes; 299 | my $idauth; 300 | if ($len < 12) { 301 | # Is a SID ever less than 12 bytes? 302 | return "SID less than 12 bytes"; 303 | } 304 | elsif ($len == 12) { 305 | $revision = unpack("C",substr($sid,0,1)); 306 | $dashes = unpack("C",substr($sid,1,1)); 307 | $idauth = unpack("H*",substr($sid,2,6)); 308 | $idauth =~ s/^0+//g; 309 | my $sub = unpack("V",substr($sid,8,4)); 310 | return "S-".$revision."-".$idauth."-".$sub; 311 | } 312 | elsif ($len > 12) { 313 | $revision = unpack("C",substr($sid,0,1)); 314 | $dashes = unpack("C",substr($sid,1,1)); 315 | $idauth = unpack("H*",substr($sid,2,6)); 316 | $idauth =~ s/^0+//g; 317 | my @sub = unpack("V4",substr($sid,8,16)); 318 | my $rid = unpack("V",substr($sid,24,4)); 319 | my $s = join('-',@sub); 320 | return "S-".$revision."-".$idauth."-".$s."-".$rid; 321 | } 322 | else { 323 | # Nothing to do 324 | } 325 | } 326 | 327 | #--------------------------------------------------------------------- 328 | # _uniToAscii() 329 | #--------------------------------------------------------------------- 330 | sub _uniToAscii { 331 | my $str = $_[0]; 332 | $str =~ s/\00//g; 333 | return $str; 334 | } 335 | 336 | 1; -------------------------------------------------------------------------------- /scanner.pl: -------------------------------------------------------------------------------- 1 | #! c:\perl\bin\perl.exe 2 | #----------------------------------------------------------- 3 | # Forensic Scanner 4 | # 5 | # Change History: 6 | # 20120815 - created 7 | # 8 | # created for ASI 9 | # Author: H. Carvey, keydet89@yahoo.com 10 | # 11 | # This software is released under the Perl Artistic License: 12 | # http://dev.perl.org/licenses/artistic.html 13 | #----------------------------------------------------------- 14 | #use strict; 15 | use Win32::GUI(); 16 | use Time::Local; 17 | use Parse::Win32Registry qw(:REG_); 18 | 19 | use Engine; 20 | use WinFile; 21 | use ReadPE; 22 | #use Win32::URLCache; 23 | 24 | # File containing 'helper' functions, available to the plugins 25 | require 'time.pl'; 26 | 27 | # Included to permit compiling via Perl2Exe 28 | #perl2exe_include "Parse/Win32Registry.pm"; 29 | #perl2exe_include "Parse/Win32Registry/Key.pm"; 30 | #perl2exe_include "Parse/Win32Registry/Entry.pm"; 31 | #perl2exe_include "Parse/Win32Registry/Value.pm"; 32 | #perl2exe_include "Parse/Win32Registry/File.pm"; 33 | #perl2exe_include "Parse/Win32Registry/Win95/File.pm"; 34 | #perl2exe_include "Parse/Win32Registry/Win95/Key.pm"; 35 | #perl2exe_include "Encode.pm"; 36 | #perl2exe_include "Encode/Byte.pm"; 37 | #perl2exe_include "Encode/Unicode.pm"; 38 | #perl2exe_include "utf8.pm"; 39 | #perl2exe_include "unicore/Heavy.pl"; 40 | #perl2exe_include "unicore/To/Upper.pl"; 41 | #----------------------------------------------------------- 42 | # Global variables 43 | #----------------------------------------------------------- 44 | my $VERSION = "20120926"; 45 | my %env; 46 | my $eng; 47 | my $tag; 48 | my %users; 49 | 50 | my %os_table = ('5.1' => 1, 51 | '5.2' => 2, 52 | '6.0' => 4, 53 | '6.1' => 16); 54 | 55 | #----------------------------------------------------------- 56 | # GUI 57 | #----------------------------------------------------------- 58 | # create our menu 59 | my $menu = Win32::GUI::MakeMenu( 60 | "&File" => "File", 61 | " > O&pen..." => { -name => "Open"}, 62 | " > -" => 0, 63 | " > E&xit" => { -name => "Exit", -onClick => sub {exit 1;}}, 64 | "&Help" => "Help", 65 | " > &About" => { -name => "About", -onClick => \&FS_OnAbout}, 66 | ); 67 | 68 | # Create Main Window 69 | my $main = new Win32::GUI::Window ( 70 | -name => "Main", 71 | -title => "Forensic Scanner, v.".$VERSION, 72 | -pos => [200, 200], 73 | # Format: [width, height] 74 | -maxsize => [490, 530], 75 | -size => [490, 530], 76 | -menu => $menu, 77 | -dialogui => 1, 78 | ) or die "Could not create a new Window: $!\n"; 79 | 80 | $main->AddLabel( 81 | -text => "", 82 | -name => "border1", 83 | -pos => [10,5], 84 | -size => [445,130], 85 | -frame => etched, 86 | -sunken => 1 87 | ); 88 | 89 | $main->AddLabel( 90 | -text => "System32 Path:", 91 | -left => 20, 92 | -top => 20); 93 | 94 | my $path = $main->AddTextfield( 95 | -name => "path", 96 | -tabstop => 1, 97 | -left => 100, 98 | -top => 20, 99 | -width => 250, 100 | -height => 22, 101 | -tabstop => 1, 102 | -foreground => "#000000", 103 | -background => "#FFFFFF"); 104 | 105 | my $browse1 = $main->AddButton( 106 | -name => 'browse1', 107 | -left => 375, 108 | -top => 20, 109 | -width => 50, 110 | -height => 22, 111 | -tabstop => 1, 112 | -text => "Browse"); 113 | 114 | $main->AddLabel( 115 | -text => "Report Dir:", 116 | -left => 20, 117 | -top => 60); 118 | 119 | my $rptdir = $main->AddTextfield( 120 | -name => "rptdir", 121 | -tabstop => 1, 122 | -left => 100, 123 | -top => 60, 124 | -width => 250, 125 | -height => 22, 126 | -tabstop => 1, 127 | -foreground => "#000000", 128 | -background => "#FFFFFF"); 129 | 130 | my $browse2 = $main->AddButton( 131 | -name => 'browse2', 132 | -left => 375, 133 | -top => 60, 134 | -width => 50, 135 | -height => 22, 136 | -tabstop => 1, 137 | -text => "Browse"); 138 | 139 | my $init = $main->AddButton( 140 | -name => 'init', 141 | -left => 375, 142 | -top => 100, 143 | -width => 50, 144 | -height => 22, 145 | -tabstop => 1, 146 | -text => "Init"); 147 | 148 | my $user1 = $main->AddListbox( 149 | -name => 'user1', 150 | -pos => [40,145], 151 | -size => [130,90], 152 | -vscroll => 1, 153 | -multisel => 2); 154 | 155 | my $add = $main->AddButton( 156 | -name => 'add', 157 | -left => 205, 158 | -top => 150, 159 | -width => 50, 160 | -height => 22, 161 | -tabstop => 1, 162 | -text => ">>"); 163 | 164 | my $remove = $main->AddButton( 165 | -name => 'remove', 166 | -left => 205, 167 | -top => 200, 168 | -width => 50, 169 | -height => 22, 170 | -tabstop => 1, 171 | -text => "<<"); 172 | 173 | my $user2 = $main->AddListbox( 174 | -name => 'user2', 175 | -pos => [290,145], 176 | -size => [130,90], 177 | -vscroll => 1, 178 | -multisel => 2); 179 | 180 | 181 | $main->AddLabel( 182 | -text => "", 183 | -name => "border2", 184 | -pos => [10,240], 185 | -size => [445,160], 186 | -frame => etched, 187 | -sunken => 1 188 | ); 189 | 190 | my $report = $main->AddTextfield( 191 | -name => "Report", 192 | -pos => [20,250], 193 | -size => [425,140], 194 | -multiline => 1, 195 | -vscroll => 1, 196 | -autohscroll => 1, 197 | -autovscroll => 1, 198 | -keepselection => 1 , 199 | -tabstop => 1, 200 | ); 201 | 202 | my $scan = $main->AddButton( 203 | -name => 'scan', 204 | -left => 320, 205 | -top => 410, 206 | -width => 50, 207 | -height => 25, 208 | -tabstop => 1, 209 | -text => "Scan"); 210 | 211 | $main->AddButton( 212 | -name => 'close', 213 | -left => 390, 214 | -top => 410, 215 | -width => 50, 216 | -height => 25, 217 | -tabstop => 1, 218 | -text => "Close"); 219 | 220 | my $status = new Win32::GUI::StatusBar($main, 221 | -text => "Forensic Scanner v.".$VERSION." opened.", 222 | ); 223 | 224 | 225 | #$status->Text("blah"); 226 | 227 | $main->Show(); 228 | Win32::GUI::Dialog(); 229 | #----------------------------------------------------------- 230 | sub Open_Click { 231 | \&browse1_Click(); 232 | } 233 | 234 | sub browse1_Click { 235 | my $dir = Win32::GUI::BrowseForFolder( 236 | -title => "System32 Dir", 237 | -root => 0x0011, 238 | -folderonly => 1, 239 | -includefiles => 0); 240 | 241 | $path->Text($dir); 242 | 0; 243 | } 244 | 245 | sub browse2_Click { 246 | my $dir = Win32::GUI::BrowseForFolder( 247 | -title => "Report Dir", 248 | -root => 0x0011, 249 | -folderonly => 1, 250 | -includefiles => 0); 251 | 252 | $rptdir->Text($dir); 253 | 0; 254 | } 255 | 256 | sub init_Click { 257 | $user1->ResetContent(); 258 | $user2->ResetContent(); 259 | 260 | my $system32dir = $path->Text(); 261 | # Add check to access the mounted volume 262 | if (opendir(DIR,$system32dir)) { 263 | 264 | } 265 | else { 266 | Win32::GUI::MessageBox($main,"You cannot access ".$system32dir.".\r\n", 267 | "Access Error!",16); 268 | return; 269 | } 270 | 271 | # Generate a unique tag for the scan 272 | $tag = genTag(); 273 | 274 | my $reportdir = $rptdir->Text(); 275 | $reportdir .= "\\" unless ($reportdir =~ m/\\$/); 276 | 277 | # Get System information 278 | $eng = Engine->new(); 279 | $eng->systemDir($system32dir); 280 | # $eng->reportDir($reportdir); 281 | 282 | # Populate the global %env hash; data needs to be available 283 | # to the plugins 284 | %env = $eng->getSystemInfo(); 285 | 286 | $env{reportfile} = $reportdir.$env{computername}."-".$tag."\.txt"; 287 | $env{logfile} = $reportdir.$env{computername}."-".$tag."\.log"; 288 | 289 | $report->Append("Environment variables populated, available to the plugins\r\n"); 290 | $report->Append("Report File: ".$env{reportfile}."\r\n"); 291 | $report->Append("Log File : ".$env{logfile}."\r\n"); 292 | # user profiles 293 | %users = $eng->getUserInfo(); 294 | foreach my $u (keys %users) { 295 | $user1->InsertString($u); 296 | } 297 | $report->Append("List of user profiles populated.\r\n"); 298 | Win32::GUI::DoEvents(); 299 | } 300 | 301 | # Copy a user profile name from the left panel to the right 302 | sub add_Click { 303 | my @list = $user1->SelectedItems(); 304 | foreach my $i (sort {$a <=> $b} @list) { 305 | my $str = $user1->GetString($i); 306 | $user2->InsertString($str); 307 | } 308 | } 309 | 310 | # Remove a user profile name from the right panel 311 | sub remove_Click { 312 | my @list = $user2->SelectedItems(); 313 | foreach my $i (reverse @list) { 314 | $user2->DeleteString($i); 315 | } 316 | } 317 | 318 | sub scan_Click { 319 | # Get users to scan from right-side Listbox; if 320 | # no users selected, none to scan 321 | my %usertemp = (); 322 | my $count = $user2->GetCount(); 323 | foreach my $u (0..($count - 1)) { 324 | my $str = $user2->GetString($u); 325 | if (exists $users{$str}) { 326 | $usertemp{$str} = $users{$str}; 327 | } 328 | } 329 | %users = %usertemp; 330 | 331 | # Add configuration information to the log file 332 | logMsg("Scan Configuration Info"); 333 | logMsg("-" x 60); 334 | logMsg($env{ProductName}." (".$env{CurrentVersion}.") ".$env{CSDVersion}); 335 | logMsg("HostName: ".$env{hostname}." InstallDate: ".$env{InstallDate}); 336 | logMsg(""); 337 | logMsg("User Profiles"); 338 | foreach (keys %users) { 339 | logMsg(sprintf "%-20s %-50s",$_,$users{$_}); 340 | } 341 | logMsg(""); 342 | # Tell Engine.pm to sort the plugins, and build it's lists 343 | $eng->sortPlugins(); 344 | # Hash to maintain a list of executed plugins 345 | my %executed = (); 346 | # get the system plugins 347 | my %plugins = $eng->getSystemPlugins(); 348 | foreach my $pl (keys %plugins) { 349 | # print "Running ".$pl." plugins...\n"; 350 | foreach my $s (@{$plugins{$pl}}) { 351 | my @path = split(/\\/,$s); 352 | my $num = scalar(@path); 353 | my $plugin = $path[$num - 1]; 354 | my $pkg = (split(/\./,$plugin,2))[0]; 355 | $report->Append("Running plugin ".$pkg."...\r\n"); 356 | # Check to see if plugin has already been executed; if so, 357 | # don't do anything (write a log entry); otherwise, run it and 358 | # add it to the list. 359 | if (exists $executed{$pkg}) { 360 | ::logMsg("Plugin ".$pkg." has already been executed."); 361 | } 362 | else { 363 | eval{ 364 | require $s; 365 | $pkg->pluginmain(); 366 | }; 367 | ::logMsg($s." Error: ".$@) if ($@); 368 | $executed{$pkg} = 1; 369 | } 370 | Win32::GUI::DoEvents(); 371 | } 372 | } 373 | 374 | # Run user plugins 375 | my %plugins = $eng->getUserPlugins(); 376 | 377 | foreach my $u (keys %users) { 378 | # Each plugin that access a user profile needs to call ::getConfig() and 379 | # get $sys{userprofile} 380 | $env{userprofile} = $users{$u}; 381 | my $reportdir = $rptdir->Text(); 382 | $reportdir .= "\\" unless ($reportdir =~ m/\\$/); 383 | $env{reportfile} = $reportdir.$u."-".$tag."\.txt"; 384 | my %executed = (); 385 | 386 | foreach my $pl (keys %plugins) { 387 | # print "Running ".$pl." plugins against ".$u." profile...\n"; 388 | foreach my $up (@{$plugins{$pl}}) { 389 | my @path = split(/\\/,$up); 390 | my $num = scalar(@path); 391 | my $plugin = $path[$num - 1]; 392 | my $pkg = (split(/\./,$plugin,2))[0]; 393 | $report->Append("Running plugin ".$pkg." against ".$u." profile...\r\n"); 394 | # Check to see if plugin has already been executed; if so, 395 | # don't do anything (write a log entry); otherwise, run it and 396 | # add it to the list. 397 | if (exists $executed{$pkg}) { 398 | ::logMsg("Plugin ".$pkg." has already been executed."); 399 | } 400 | else { 401 | eval{ 402 | require $up; 403 | $pkg->pluginmain(); 404 | }; 405 | ::logMsg($s." Error: ".$@) if ($@); 406 | $executed{$pkg} = 1; 407 | } 408 | Win32::GUI::DoEvents(); 409 | } 410 | } 411 | } 412 | $status->Text("Scan complete\."); 413 | $report->Append("Scan complete\.\r\n"); 414 | } 415 | 416 | sub close_Click { 417 | exit 1; 418 | } 419 | 420 | # About box 421 | sub FS_OnAbout { 422 | my $self = shift; 423 | 424 | $self->MessageBox( 425 | "Forensic Scanner, v.".$VERSION."\r\n". 426 | "\r\n". 427 | "\r\n". 428 | "Copyright 2012 \r\n". 429 | "Author: H\. Carvey, keydet89\@yahoo\.com", 430 | "About...", 431 | MB_ICONINFORMATION | MB_OK, 432 | ); 433 | 0; 434 | } 435 | #----------------------------------------------------------- 436 | 437 | #----------------------------------------------------------- 438 | sub logMsg { 439 | open(FH,">>",$env{logfile}); 440 | print FH localtime(time).": ".$_[0]."\n"; 441 | close(FH); 442 | } 443 | 444 | sub rptMsg { 445 | open(FH,">>",$env{reportfile}); 446 | binmode FH,":utf8"; 447 | print FH $_[0]."\n"; 448 | close(FH); 449 | } 450 | 451 | sub getConfig { 452 | return \%env; 453 | } 454 | 455 | sub genTag { 456 | # Generate a unique tag for the scan 457 | my @t = gmtime(); 458 | my $yy = sprintf("%02d", $t[5] % 100); 459 | my $mm = sprintf("%02d", $t[4] + 1); 460 | my $dd = sprintf("%02d", $t[3]); 461 | my $hr = sprintf("%02d", $t[2]); 462 | my $min = sprintf("%02d", $t[1]); 463 | my $sec = sprintf("%02d", $t[0]); 464 | return $yy.$mm.$dd.$hr.$min.$sec; 465 | } 466 | -------------------------------------------------------------------------------- /WinSetup.pm: -------------------------------------------------------------------------------- 1 | package WinSetup; 2 | #--------------------------------------------------------------------- 3 | # WinSetup.pm 4 | # Helper package to collect data about the Windows OS from the Registry 5 | # hives 6 | # 7 | # Change history 8 | # 20120813 - updated 9 | # 20100926 - created 10 | # 11 | # 12 | # References 13 | # Getting ProductType info to support the version 14 | # http://msdn.microsoft.com/en-us/library/ms724834%28VS.85%29.aspx 15 | # 16 | # copyright 2012 ASI 17 | # Author: H. Carvey, keydet89@yahoo.com 18 | # This software is released under the Perl Artistic License: 19 | # http://dev.perl.org/licenses/artistic.html 20 | #--------------------------------------------------------------------- 21 | use strict; 22 | use Exporter; 23 | use Parse::Win32Registry qw(:REG_); 24 | 25 | # Included to permit compiling via Perl2Exe 26 | #perl2exe_include "Parse/Win32Registry.pm"; 27 | #perl2exe_include "Parse/Win32Registry/Entry.pm"; 28 | #perl2exe_include "Parse/Win32Registry/Key.pm"; 29 | #perl2exe_include "Parse/Win32Registry/Value.pm"; 30 | #perl2exe_include "Parse/Win32Registry/File.pm"; 31 | #perl2exe_include "Parse/Win32Registry/Win95/File.pm"; 32 | #perl2exe_include "Parse/Win32Registry/Win95/Key.pm"; 33 | #perl2exe_include "Encode/Unicode.pm"; 34 | 35 | use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS); 36 | 37 | $VERSION = 0.1; 38 | @ISA = qw(Exporter); 39 | @EXPORT = (); 40 | @EXPORT_OK = qw(new); 41 | 42 | # Items to export into callers namespace by default. Note: do not export 43 | # names by default without a very good reason. Use EXPORT_OK instead. 44 | # Do not simply export all your public functions/methods/constants. 45 | 46 | # Global variables 47 | # self reference 48 | my $self = {}; 49 | my %systemhives = (); 50 | my %userprofiles = (); 51 | 52 | #--------------------------------------------------------------------- 53 | # new() 54 | # 55 | #--------------------------------------------------------------------- 56 | sub new { 57 | my $class = shift; 58 | return bless($self, $class); 59 | } 60 | 61 | #--------------------------------------------------------------------- 62 | # setup() 63 | # 64 | #--------------------------------------------------------------------- 65 | sub setup { 66 | my $class = shift; 67 | my $path = shift; 68 | 69 | $path .= "\\" unless ($path =~ m/\\$/); 70 | my @segs = split(/\\/,$path); 71 | my $num = scalar(@segs); 72 | $self->{drive} = join('\\',@segs[0..($num - 3)]); 73 | $self->{drive} .= "\\" unless ($self->{drive} =~ m/\\$/); 74 | 75 | my $hive_dir = $path."config"; 76 | if (-e $hive_dir && -d $hive_dir) { 77 | if (-e $hive_dir."\\software" && -f $hive_dir."\\software") { 78 | my %guess = $self->getHiveType($hive_dir."\\software"); 79 | foreach my $g (keys %guess) { 80 | if ($guess{"Software"} == 1) { 81 | $systemhives{software} = ($hive_dir."\\software"); 82 | $self->{softwarehive} = ($hive_dir."\\software"); 83 | } 84 | } 85 | } 86 | if (-e $hive_dir."\\system" && -f $hive_dir."\\system") { 87 | my %guess = $self->getHiveType($hive_dir."\\system"); 88 | foreach my $g (keys %guess) { 89 | if ($guess{"System"} == 1) { 90 | $systemhives{system} = ($hive_dir."\\system"); 91 | $self->{systemhive} = ($hive_dir."\\system"); 92 | } 93 | } 94 | } 95 | if (-e $hive_dir."\\SAM" && -f $hive_dir."\\SAM") { 96 | my %guess = $self->getHiveType($hive_dir."\\SAM"); 97 | foreach my $g (keys %guess) { 98 | if ($guess{"SAM"} == 1) { 99 | $systemhives{sam} = ($hive_dir."\\SAM"); 100 | $self->{samhive} = ($hive_dir."\\SAM"); 101 | } 102 | } 103 | } 104 | if (-e $hive_dir."\\security" && -f $hive_dir."\\security") { 105 | my %guess = $self->getHiveType($hive_dir."\\security"); 106 | foreach my $g (keys %guess) { 107 | if ($guess{"Security"} == 1) { 108 | $systemhives{security} = ($hive_dir."\\security"); 109 | $self->{securityhive} = ($hive_dir."\\security"); 110 | } 111 | } 112 | } 113 | return %systemhives; 114 | } 115 | else { 116 | $self->{error} = "hive directory not found."; 117 | return 0; 118 | } 119 | } 120 | 121 | #--------------------------------------------------------------------- 122 | # getOSData() 123 | # Get OS version information 124 | # 125 | # For CurrentVersion value: 126 | # http://msdn.microsoft.com/en-us/library/ms724832%28VS.85%29.aspx 127 | # 128 | #--------------------------------------------------------------------- 129 | sub getOSData { 130 | my %osdata; 131 | 132 | my %flag = ("5\.1" => 0x1, 133 | "5\.2" => 0x2, 134 | "6\.0" => 0x4, #Vista 135 | "6\.1" => 0x10, 136 | "6\.2" => 0x20); 137 | 138 | my $reg = Parse::Win32Registry->new($self->{softwarehive}); 139 | my $root_key = $reg->get_root_key; 140 | if (my $key = $root_key->get_subkey("Microsoft\\Windows NT\\CurrentVersion")) { 141 | # Reference for the CurrentVersion value: 142 | # http://msdn.microsoft.com/en-us/library/ms724832%28v=vs.85%29.aspx 143 | # if CV <= 5.2, 2003 or below; if CV > 5.2 (ie, 6.0, 6.1), Vista or above 144 | my @vals = ("ProductName","CSDVersion","CurrentVersion","CurrentBuildNumber", 145 | "InstallDate","CurrentType","SystemRoot"); 146 | 147 | foreach my $v (@vals) { 148 | eval { 149 | $osdata{$v} = $key->get_value($v)->get_data(); 150 | $osdata{$v} = gmtime($key->get_value($v)->get_data()) if ($v eq "InstallDate"); 151 | }; 152 | } 153 | $osdata{software} = $self->{softwarehive}; 154 | $osdata{system} = $self->{systemhive}; 155 | $osdata{security} = $self->{securityhive}; 156 | $osdata{sam} = $self->{samhive}; 157 | $osdata{computername} = getComputerName(); 158 | $osdata{hostname} = getHostName(); 159 | 160 | if (exists $flag{$osdata{"CurrentVersion"}}) { 161 | $osdata{osflag} = $flag{$osdata{"CurrentVersion"}}; 162 | } 163 | else { 164 | $osdata{osflag} = 0xffff; 165 | } 166 | 167 | $self->{version} = $osdata{"CurrentVersion"}; 168 | return %osdata; 169 | } 170 | else { 171 | return; 172 | } 173 | } 174 | 175 | #--------------------------------------------------------------------- 176 | # getUserData() 177 | # 178 | # A ProfileList subkey is created for every user who logs onto a Windows 179 | # system. 180 | #--------------------------------------------------------------------- 181 | sub getUserData { 182 | my %paths; 183 | my $dirpath; 184 | 185 | if ($self->{version} >= 6.0) { 186 | $dirpath = $self->{drive}."Users\\"; 187 | } 188 | else { 189 | $dirpath = $self->{drive}."Documents and Settings\\"; 190 | } 191 | 192 | my @profiles; 193 | opendir(DIR,$dirpath); 194 | @profiles = readdir(DIR); 195 | closedir(DIR); 196 | 197 | foreach my $p (@profiles) { 198 | next if ($p =~ m/\./); 199 | $paths{$p} = $dirpath.$p; 200 | } 201 | 202 | return %paths; 203 | } 204 | 205 | #--------------------------------------------------------------------- 206 | # getCurrentControlSet() 207 | # 208 | # gets the ControlSet marked as 'Current' from the System hive 209 | #--------------------------------------------------------------------- 210 | sub getCurrentControlSet { 211 | my $reg = Parse::Win32Registry->new($self->{systemhive}); 212 | my $root_key = $reg->get_root_key; 213 | my $key; 214 | 215 | eval { 216 | $key = $root_key->get_subkey("Select")->get_value("Current")->get_data(); 217 | return $key; 218 | }; 219 | return if ($@); 220 | } 221 | 222 | #--------------------------------------------------------------------- 223 | # getComputerName() 224 | #--------------------------------------------------------------------- 225 | sub getComputerName { 226 | my $reg = Parse::Win32Registry->new($self->{systemhive}); 227 | my $root_key = $reg->get_root_key; 228 | 229 | my $curr; 230 | eval { 231 | $curr = $root_key->get_subkey("Select")->get_value("Current")->get_data(); 232 | }; 233 | if ($@) { 234 | # print "Error: $@\n"; 235 | return; 236 | } 237 | 238 | my $key_path = "ControlSet00".$curr."\\Control\\ComputerName\\ComputerName"; 239 | if (my $key = $root_key->get_subkey($key_path)) { 240 | my $type; 241 | eval { 242 | $type = $key->get_value("ComputerName")->get_data(); 243 | }; 244 | if ($@) { 245 | # print "Error: $@\n"; 246 | return; 247 | } 248 | else { 249 | return $type; 250 | } 251 | } 252 | else { 253 | return; 254 | } 255 | } 256 | 257 | #--------------------------------------------------------------------- 258 | # getTimeZoneInfo() 259 | #--------------------------------------------------------------------- 260 | sub getTimeZoneInfo { 261 | my $reg = Parse::Win32Registry->new($self->{systemhive}); 262 | my $root_key = $reg->get_root_key; 263 | my %tz; 264 | my $curr; 265 | eval { 266 | $curr = $root_key->get_subkey("Select")->get_value("Current")->get_data(); 267 | }; 268 | if ($@) { 269 | # print "Error: $@\n"; 270 | return; 271 | } 272 | 273 | my $key_path = "ControlSet00".$curr."\\Control\\TimeZoneInformation"; 274 | if (my $key = $root_key->get_subkey($key_path)) { 275 | my @vals = $key->get_list_of_values(); 276 | if (scalar @vals > 0) { 277 | foreach my $v (@vals) { 278 | $tz{$v->get_name()} = $v->get_data(); 279 | } 280 | return %tz; 281 | } 282 | } 283 | else { 284 | return; 285 | } 286 | } 287 | 288 | #--------------------------------------------------------------------- 289 | # getHostName() 290 | #--------------------------------------------------------------------- 291 | sub getHostName { 292 | my $reg = Parse::Win32Registry->new($self->{systemhive}); 293 | my $root_key = $reg->get_root_key; 294 | 295 | my $curr; 296 | eval { 297 | $curr = $root_key->get_subkey("Select")->get_value("Current")->get_data(); 298 | }; 299 | if ($@) { 300 | # print "Error: $@\n"; 301 | return; 302 | } 303 | 304 | my $key_path = "ControlSet00".$curr."\\Services\\Tcpip\\Parameters"; 305 | if (my $key = $root_key->get_subkey($key_path)) { 306 | my $type; 307 | eval { 308 | $type = $key->get_value("Hostname")->get_data(); 309 | }; 310 | if ($@) { 311 | # print "Error: $@\n"; 312 | return; 313 | } 314 | else { 315 | return $type; 316 | } 317 | } 318 | else { 319 | return; 320 | } 321 | 322 | 323 | } 324 | 325 | #--------------------------------------------------------------------- 326 | # getProductType() 327 | # 328 | # Get ProductType value 329 | #--------------------------------------------------------------------- 330 | sub getProductType { 331 | my $reg = Parse::Win32Registry->new($self->{systemhive}); 332 | my $root_key = $reg->get_root_key; 333 | 334 | my $curr; 335 | eval { 336 | $curr = $root_key->get_subkey("Select")->get_value("Current")->get_data(); 337 | }; 338 | if ($@) { 339 | # print "Error: $@\n"; 340 | return; 341 | } 342 | 343 | my $key_path = "ControlSet00".$curr."\\Control\\ProductOptions"; 344 | if (my $key = $root_key->get_subkey($key_path)) { 345 | my $type; 346 | eval { 347 | $type = $key->get_value("ProductType")->get_data(); 348 | }; 349 | if ($@) { 350 | # print "Error: $@\n"; 351 | return; 352 | } 353 | else { 354 | return $type; 355 | } 356 | } 357 | else { 358 | return; 359 | } 360 | } 361 | 362 | #--------------------------------------------------------------------- 363 | # is64bit() 364 | # boolean 365 | #--------------------------------------------------------------------- 366 | sub is64bit { 367 | my $reg = Parse::Win32Registry->new($self->{softwarehive}); 368 | my $root_key = $reg->get_root_key; 369 | if ($root_key->get_subkey("WOW6432Node")) { 370 | return 1; 371 | } 372 | else { 373 | return 0; 374 | } 375 | } 376 | 377 | #--------------------------------------------------------------------- 378 | # getHiveType() 379 | # Attempts to guess the type of hive; NTUSER, SAM, Security, System, Software 380 | # If the hive is NTUSER, attempts to get the SID and "logon user name" 381 | # If the hive is System, attempts to get the ComputerName value 382 | #--------------------------------------------------------------------- 383 | sub getHiveType { 384 | my $class = shift; 385 | my $hive = shift; 386 | 387 | my $reg; 388 | my $root_key; 389 | my %guess; 390 | 391 | eval { 392 | $reg = Parse::Win32Registry->new($hive); 393 | $root_key = $reg->get_root_key; 394 | }; 395 | 396 | # Check for SAM 397 | eval { 398 | $guess{SAM} = 1 if (my $key = $root_key->get_subkey("SAM\\Domains\\Account\\Users")); 399 | }; 400 | # Check for Software 401 | eval { 402 | $guess{Software} = 1 if ($root_key->get_subkey("Microsoft\\Windows\\CurrentVersion") && 403 | $root_key->get_subkey("Microsoft\\Windows NT\\CurrentVersion")); 404 | }; 405 | 406 | # Check for System 407 | eval { 408 | $guess{ucfirst "system"} = 1 if ($root_key->get_subkey("MountedDevices") && 409 | $root_key->get_subkey("Select")); 410 | }; 411 | 412 | if ($guess{ucfirst "system"} == 1) { 413 | eval { 414 | my $control = $root_key->get_subkey("Select")->get_value("Current")->get_data(); 415 | $guess{compname} = $root_key->get_subkey("ControlSet00".$control."\\Control\\ComputerName\\ComputerName") 416 | ->get_value("ComputerName")->get_data(); 417 | }; 418 | } 419 | 420 | # Check for Security 421 | eval { 422 | $guess{Security} = 1 if ($root_key->get_subkey("Policy\\Accounts") && 423 | $root_key->get_subkey("Policy\\PolAdtEv")); 424 | }; 425 | # Check for NTUSER.DAT 426 | # if it is an NTUSER.DAT hive, attempt to determine the SID and logon user name 427 | eval { 428 | $guess{NTUSER} = 1 if ($root_key->get_subkey("Software\\Microsoft\\Windows\\CurrentVersion")); 429 | }; 430 | 431 | if ($guess{NTUSER} == 1) { 432 | my @sids; 433 | eval { 434 | my @subkeys = $root_key->get_subkey("Software\\Microsoft\\Protected Storage System Provider")->get_list_of_subkeys(); 435 | map{push(@sids,$_->get_name())}@subkeys; 436 | }; 437 | # die "Error attempting to locate SID: $!\n" if ($@); 438 | $guess{sid} = $sids[0] if (scalar(@sids) == 1); 439 | 440 | eval { 441 | $guess{username} = $root_key->get_subkey("Software\\Microsoft\\Windows\\CurrentVersion\\Explorer") 442 | ->get_value("Logon User Name")->get_data(); 443 | }; 444 | } 445 | 446 | eval { 447 | $guess{USRCLASS} = 1 if ($root_key->get_subkey("CLSID") && $root_key->get_subkey("LocalSettings") 448 | && $root_key->get_subkey("Interface")); 449 | }; 450 | 451 | return %guess; 452 | } 453 | #---------------------------------------------------------------- 454 | # getError() 455 | # returns the error message for the module 456 | #---------------------------------------------------------------- 457 | sub getError {return $self->{error};} 458 | 459 | 460 | 1; 461 | __END__ 462 | 463 | =head1 NAME 464 | 465 | WinSetup - Helper package for Scanner 466 | 467 | =head1 SYNOPSIS 468 | 469 | see example files 470 | 471 | =head1 DESCRIPTION 472 | 473 | WinSetup is a Perl module the provides helper functions for the main 474 | driver component of the scanner. Data is returned to the main driver 475 | component so that decisions can be made. 476 | 477 | =head1 SEE ALSO 478 | 479 | 480 | 481 | =head1 AUTHOR 482 | 483 | Harlan Carvey, Ekeydet89@yahoo.comE 484 | 485 | =head1 COPYRIGHT AND LICENSE 486 | 487 | Copyright (C) 2011 by Harlan Carvey (keydet89@yahoo.com) 488 | 489 | This library is free software; you can redistribute it and/or modify 490 | it as you like. However, please be sure to provide proper credit where 491 | it is due. 492 | 493 | =cut 494 | -------------------------------------------------------------------------------- /ReadPE.pm: -------------------------------------------------------------------------------- 1 | package ReadPE; 2 | $VERSION = 0.1; 3 | #------------------------------------------------------ 4 | # ReadPE.pm 5 | # Perl module to make accessing PE file headers easier 6 | # 7 | # Usage: within a Perl script; use ReadPE; 8 | # Installation: Copy this file to the site/lib/File directory 9 | # within your Perl installation 10 | # 11 | # copyright 2006-2012 H. Carvey keydet89@yahoo.com 12 | # 13 | # This software is released under the Perl Artistic License: 14 | # http://dev.perl.org/licenses/artistic.html 15 | #------------------------------------------------------ 16 | use strict; 17 | use vars qw($VERSION @ISA @EXPORT_OK); 18 | use Carp; 19 | 20 | require Exporter; 21 | 22 | @ISA = qw(Exporter); 23 | @EXPORT_OK = qw(new); 24 | 25 | my $self; # self reference 26 | 27 | #--------------------------------------------------------------------- 28 | # new() 29 | # Opens file in binary mode; blesses self, including file handle 30 | # Input : Name of file to be opened 31 | # Output: Blessed object reference; includes file handle 32 | # Sets : file handle 33 | #--------------------------------------------------------------------- 34 | sub new { 35 | $self = {}; 36 | my $file = shift; 37 | # $self->{filesize} = (stat($self->{file}))[7]; 38 | if (open($self->{hFile},"<",$file)) { 39 | binmode($self->{hFile}); 40 | return bless($self); 41 | } 42 | else { 43 | carp "Could not open ".$file." : $! \n"; 44 | } 45 | } 46 | 47 | #------------------------------------------------------- 48 | # getDOSHeader() 49 | # Reads first 64 bytes of file 50 | # Input : 51 | # Output: DOS header hash (magic number and e_lfanew elements) 52 | #------------------------------------------------------- 53 | sub getDOSHeader { 54 | $self = $_[0]; 55 | my %dos = (); 56 | my $record; 57 | seek($self->{hFile},0,0); 58 | my $bytes = read($self->{hFile},$record,64); 59 | if (64 == $bytes) { 60 | ($dos{magic},$dos{e_ss},$dos{e_sp},$dos{e_csum},$dos{e_ip}, 61 | $dos{e_cs},$dos{e_oemid},$dos{e_oeminfo},$dos{e_lfanew}) 62 | = unpack("vx12v5x12v2x20V",$record); 63 | return %dos; 64 | } 65 | else { 66 | $self->{error} = "$bytes of 64 bytes read in getDOSHeader()"; 67 | return %dos; 68 | } 69 | } 70 | 71 | #------------------------------------------------------- 72 | # getNTHeader() 73 | # Input : 'e_lfanew' value 74 | # Output: DWORD located at 'e_lfanew' 75 | #------------------------------------------------------- 76 | sub getNTHeader { 77 | $self = $_[0]; 78 | my $ofs = $_[1]; 79 | my $record; 80 | seek($self->{hFile},$ofs,0); 81 | my $bytes = read($self->{hFile},$record,4); 82 | if (4 == $bytes) { 83 | return unpack("V",$record); 84 | } 85 | else { 86 | $self->{error} = "$bytes of 4 bytes read in getNTHeader()"; 87 | return 0; 88 | } 89 | } 90 | 91 | #------------------------------------------------------- 92 | # getFileHeader() 93 | # Input : 'e_lfanew' value 94 | # Output: Hash containing elements of IMAGE_FILE_HEADER 95 | # Ref : http://msdn.microsoft.com/library/default.asp?url= 96 | # /library/en-us/debug/base/image_file_header_str.asp 97 | #------------------------------------------------------- 98 | sub getFileHeader { 99 | $self = $_[0]; 100 | my $ofs = $_[1]; 101 | my %ifh = (); 102 | my $record; 103 | seek($self->{hFile},$ofs + 4,0); 104 | my $bytes = read($self->{hFile},$record,20); 105 | if ($bytes == 20) { 106 | ($ifh{machine},$ifh{number_sections},$ifh{datetimestamp},$ifh{ptr_symbol_table}, 107 | $ifh{number_symbols},$ifh{size_opt_header},$ifh{characteristics}) 108 | = unpack("vvVVVvv",$record); 109 | $self->{size_opt_header} = $ifh{size_opt_header}; 110 | return %ifh; 111 | } 112 | else { 113 | $self->{error} = "$bytes of 20 bytes read in getFileHeader()"; 114 | return %ifh; 115 | } 116 | } 117 | 118 | #------------------------------------------------------- 119 | # getFileHeaderCharacteristics() 120 | # Input : WORD (2 byte) 'characteristics' value from 121 | # IMAGE_FILE_HEADER structure 122 | # Output: List containing characteristics 123 | #------------------------------------------------------- 124 | sub getFileHeaderCharacteristics { 125 | $self = $_[0]; 126 | my $char = $_[1]; 127 | my @list = (); 128 | my %chars = (0x0001 => "IMAGE_FILE_RELOCS_STRIPPED", 129 | 0x0002 => "IMAGE_FILE_EXECUTABLE_IMAGE", 130 | 0x0004 => "IMAGE_FILE_LINE_NUMS_STRIPPED", 131 | 0x0008 => "IMAGE_FILE_LOCAL_SYMS_STRIPPED", 132 | 0x0010 => "IMAGE_FILE_AGGRESIVE_WS_TRIM", 133 | 0x0020 => "IMAGE_FILE_LARGE_ADDRESS_AWARE", 134 | 0x0080 => "IMAGE_FILE_BYTES_REVERSED_LO", 135 | 0x0100 => "IMAGE_FILE_32BIT_MACHINE", 136 | 0x0200 => "IMAGE_FILE_DEBUG_STRIPPED", 137 | 0x0400 => "IMAGE_FILE_REMOVABLE_RUN_FROM_SWAP", 138 | 0x0800 => "IMAGE_FILE_NET_RUN_FROM_SWAP", 139 | 0x1000 => "IMAGE_FILE_SYSTEM", 140 | 0x2000 => "IMAGE_FILE_DLL", 141 | 0x4000 => "IMAGE_FILE_UP_SYSTEM_ONLY", 142 | 0x8000 => "IMAGE_FILE_BYTES_REVERSED_HI"); 143 | 144 | foreach my $c (keys %chars) { 145 | push(@list,$chars{$c}) if ($char & $c); 146 | } 147 | return @list; 148 | } 149 | 150 | #------------------------------------------------------- 151 | # getFileHeaderMachine() 152 | # Input : WORD (2 byte) 'machine' value from 153 | # IMAGE_FILE_HEADER structure 154 | # Output: Architecture type of the image 155 | #------------------------------------------------------- 156 | sub getFileHeaderMachine { 157 | $self = $_[0]; 158 | my $word = $_[1]; 159 | my %mach = (0x014c => "IMAGE_FILE_MACHINE_I386", 160 | 0x014d => "IMAGE_FILE_MACHINE_I860", 161 | 0x0184 => "IMAGE_FILE_MACHINE_ALPHA", 162 | 0x01c0 => "IMAGE_FILE_MACHINE_ARM", 163 | 0x01c2 => "IMAGE_FILE_MACHINE_THUMB", 164 | 0x01f0 => "IMAGE_FILE_MACHINE_POWERPC", 165 | 0x0284 => "IMAGE_FILE_MACHINE_ALPHA64", 166 | 0x0200 => "IMAGE_FILE_MACHINE_IA64", 167 | 0x8664 => "IMAGE_FILE_MACHINE_AMD64"); 168 | 169 | foreach my $m (keys %mach) { 170 | return $mach{$m} if ($word & $m); 171 | } 172 | } 173 | 174 | #------------------------------------------------------- 175 | # getOptionalHeaderMagic() 176 | # Determine which optional header needs to be read 177 | # Input : 'e_lfanew' value 178 | # Output: Value of the magic number of the IMAGE_OPTIONAL_HEADER 179 | # structure 180 | #------------------------------------------------------- 181 | sub getOptionalHeaderMagic { 182 | $self = $_[0]; 183 | my $ofs = $_[1]; 184 | my $record; 185 | # size of the IMAGE_NT_HEADER is 4 bytes, and the size of the 186 | # IMAGE_FILE_HEADER is 20 bytes; the IMAGE_OPTIONAL_HEADER structure 187 | # immediately follows the IMAGE_FILE_HEADER structure 188 | seek($self->{hFile},$ofs + 24,0); 189 | read($self->{hFile},$record,2); 190 | my $magic = unpack("v",$record); 191 | $self->{optHdr32} = 1 if (0x10b == $magic); 192 | return $magic; 193 | } 194 | 195 | #------------------------------------------------------- 196 | # getOptionalHeader32() 197 | # Input : 'e_lfanew' value and the size of the optional header 198 | # (derived from the IMAGE_FILE_HEADER structure) 199 | # Output: Hash containing elements of IMAGE_OPTIONAL_HEADER 200 | # (32-bit version) 201 | #------------------------------------------------------- 202 | sub getOptionalHeader32 { 203 | $self = $_[0]; 204 | my $ofs = $_[1]; 205 | my $size = $_[2]; 206 | my %opt32 = (); 207 | my $record; 208 | # size of the IMAGE_NT_HEADER is 4 bytes, and the size of the 209 | # IMAGE_FILE_HEADER is 20 bytes; the IMAGE_OPTIONAL_HEADER structure 210 | # immediately follows the IMAGE_FILE_HEADER structure 211 | seek($self->{hFile},$ofs + 24,0); 212 | my $bytes = read($self->{hFile},$record,$size); 213 | if ($bytes == $size) { 214 | ($opt32{magic},$opt32{majlinkver},$opt32{minlinkver},$opt32{codesize}, 215 | $opt32{initdatasz},$opt32{uninitdatasz},$opt32{addr_entrypt},$opt32{codbase}, 216 | $opt32{database},$opt32{imagebase},$opt32{sectalign},$opt32{filealign}, 217 | $opt32{os_maj},$opt32{os_min},$opt32{image_maj},$opt32{image_min}, 218 | $opt32{image_sz},$opt32{head_sz},$opt32{checksum},$opt32{subsystem}, 219 | $opt32{dll_char},$opt32{rva_num}) = unpack("vCCV9v4x8V3vvx20Vx4",$record); 220 | return %opt32; 221 | } 222 | else { 223 | $self->{error} = "$bytes of $size bytes read in getOptionalHeader32()"; 224 | return %opt32; 225 | } 226 | } 227 | 228 | #--------------------------------------------------------------------- 229 | # getOptionalHeaderSubsystem() 230 | # Determine the subsystem required to run this image 231 | # Input : WORD (2-byte) 'subsystem' value from the IMAGE_OPTIONAL_HEADER 232 | # structure 233 | # Output: String containing name of subsystem 234 | #--------------------------------------------------------------------- 235 | sub getOptionalHeaderSubsystem { 236 | $self = shift; 237 | my $word = shift; 238 | my %subs = (0 => "IMAGE_SUBSYSTEM_UNKNOWN", 239 | 1 => "IMAGE_SUBSYSTEM_NATIVE", 240 | 3 => "IMAGE_SUBSYSTEM_WINDOWS_CUI", 241 | 2 => "IMAGE_SUBSYSTEM_WINDOWS_GUI", 242 | 5 => "IMAGE_SUBSYSTEM_OS2_CUI", 243 | 7 => "IMAGE_SUBSYSTEM_POSIX_CUI", 244 | 8 => "IMAGE_SUBSYSTEM_NATIVE_WINDOWS", 245 | 9 => "IMAGE_SUBSYSTEM_WINDOWS_CE_GUI", 246 | 14 => "IMAGE_SUBSYSTEM_XBOX"); 247 | 248 | foreach my $s (keys %subs) { 249 | return $subs{$s} if ($word == $s); 250 | } 251 | } 252 | 253 | #------------------------------------------------------- 254 | # getImageDataDirectories() 255 | # Input : 'e_lfanew' value, and 'rva_num' value from the 256 | # IMAGE_OPTIONAL_HEADER structure 257 | # Output: Hash of hashes containing listing of IMAGE_DATA_DIRECTORY 258 | # structures 259 | #------------------------------------------------------- 260 | sub getImageDataDirectories { 261 | $self = shift; 262 | my $ofs = shift; 263 | my $rvas = shift; 264 | my %dd = (); 265 | my $record; 266 | 267 | my @dd_names = qw/ExportTable ImportTable ResourceTable ExceptionTable 268 | CertificateTable BaseRelocTable DebugTable ArchSpecific 269 | GlobalPtrReg TLSTable LoadConfigTable BoundImportTable 270 | IAT DelayImportDesc CLIHeader unused/; 271 | # The IMAGE_FILE_HEADER is 24 bytes in size; 272 | # The header of the IMAGE_OPTIONAL_HEADER structure is 96 bytes in size; 273 | # The IMAGE_DATA_DIRECTORY structures immediately follow the header of the 274 | # IMAGE_OPTIONAL_HEADER structure 275 | my $opt_hdr_size = 96 if (1 == $self->{optHdr32}); 276 | seek($self->{hFile},$ofs + 24 + $opt_hdr_size,0); 277 | my $bytes = read($self->{hFile},$record,8*$rvas); 278 | if ($bytes == (8*$rvas)) { 279 | my @rva_list = unpack("VV" x $rvas,$record); 280 | foreach my $i (0..($rvas - 1)) { 281 | $dd{$dd_names[$i]}{rva} = $rva_list[($i*2)]; 282 | $dd{$dd_names[$i]}{size} = $rva_list[($i*2)+1]; 283 | } 284 | return %dd; 285 | } 286 | else { 287 | $self->{error} = "$bytes of ".(8*$rvas)." bytes read in getDataDirectories()"; 288 | return %dd; 289 | } 290 | } 291 | 292 | #------------------------------------------------------- 293 | # getImageSectionHeaders() 294 | # Input : 295 | # Output: 296 | # http://msdn.microsoft.com/library/default.asp?url=/library/en-us/debug/ 297 | # base/image_section_header_str.asp 298 | #------------------------------------------------------- 299 | sub getImageSectionHeaders { 300 | $self = shift; 301 | my $ofs = shift; 302 | my $num = shift; 303 | # Each section is 40 bytes in size, and all sections are contiguous 304 | my $sec_sz = 40; 305 | my $record; 306 | my %sec = (); 307 | foreach my $i (0..($num - 1)) { 308 | seek($self->{hFile},$ofs + ($sec_sz * $i),0); 309 | read($self->{hFile},$record,$sec_sz); 310 | my ($name,$virt_sz,$virt_addr,$rdata_sz,$rdata_ptr,$char) 311 | = unpack("a8V4x12V",$record); 312 | $name =~ s/\00+$//; 313 | $sec{$name}{virt_sz} = $virt_sz; 314 | $sec{$name}{virt_addr} = $virt_addr; 315 | $sec{$name}{rdata_sz} = $rdata_sz; 316 | $sec{$name}{rdata_ptr} = $rdata_ptr; 317 | $sec{$name}{characteristics} = $char; 318 | } 319 | return %sec; 320 | } 321 | #---------------------------------------------------------------- 322 | # getImageSectionCharacteristics() subroutines 323 | # Input : 'characteristics' value from the section header structure 324 | # Output: list containing characteristics 325 | #---------------------------------------------------------------- 326 | sub getImageSectionCharacteristics { 327 | $self = shift; 328 | my $char = shift; 329 | my @characteristics = (); 330 | my %char_hash = (0x00000000 => "IMAGE_SCN_TYPE_REG", 331 | 0x00000001 => "IMAGE_SCN_TYPE_DSECT", 332 | 0x00000002 => "IMAGE_SCN_TYPE_NOLOAD", 333 | 0x00000004 => "IMAGE_SCN_TYPE_GROUP", 334 | 0x00000008 => "IMAGE_SCN_TYPE_NO_PAD", 335 | 0x00000010 => "IMAGE_SCN_TYPE_COPY", 336 | 0x00000020 => "IMAGE_SCN_CNT_CODE", 337 | 0x00000040 => "IMAGE_SCN_CNT_INITIALIZED_DATA", 338 | 0x00000080 => "IMAGE_SCN_CNT_UNINITIALIZED_DATA", 339 | 0x00000100 => "IMAGE_SCN_LNK_OTHER", 340 | 0x00000200 => "IMAGE_SCN_LNK_INFO", 341 | 0x00000400 => "IMAGE_SCN_TYPE_OVER", 342 | 0x00001000 => "IMAGE_SCN_LNK_COMDAT", 343 | 0x00008000 => "IMAGE_SCN_MEM_FARDATA", 344 | 0x00020000 => "IMAGE_SCN_MEM_PURGEABLE", 345 | 0x00020000 => "IMAGE_SCN_MEM_16BIT", 346 | 0x00040000 => "IMAGE_SCN_MEM_LOCKED", 347 | 0x00080000 => "IMAGE_SCN_MEM_PRELOAD", 348 | 0x00100000 => "IMAGE_SCN_ALIGN_1BYTES", 349 | 0x00200000 => "IMAGE_SCN_ALIGN_2BYTES", 350 | 0x00300000 => "IMAGE_SCN_ALIGN_4BYTES", 351 | 0x00400000 => "IMAGE_SCN_ALIGN_8BYTES", 352 | 0x00500000 => "IMAGE_SCN_ALIGN_16BYTES", 353 | 0x00600000 => "IMAGE_SCN_ALIGN_32BYTES", 354 | 0x00700000 => "IMAGE_SCN_ALIGN_64BYTES", 355 | 0x00800000 => "IMAGE_SCN_ALIGN_128BYTES", 356 | 0x00900000 => "IMAGE_SCN_ALIGN_256BYTES", 357 | 0x00A00000 => "IMAGE_SCN_ALIGN_512BYTES", 358 | 0x00B00000 => "IMAGE_SCN_ALIGN_1024BYTES", 359 | 0x00C00000 => "IMAGE_SCN_ALIGN_2048BYTES", 360 | 0x00D00000 => "IMAGE_SCN_ALIGN_4096BYTES", 361 | 0x00E00000 => "IMAGE_SCN_ALIGN_8192BYTES", 362 | 0x01000000 => "IMAGE_SCN_LNK_NRELOC_OVFL", 363 | 0x02000000 => "IMAGE_SCN_MEM_DISCARDABLE", 364 | 0x04000000 => "IMAGE_SCN_MEM_NOT_CACHED", 365 | 0x08000000 => "IMAGE_SCN_MEM_NOT_PAGED", 366 | 0x10000000 => "IMAGE_SCN_MEM_SHARED", 367 | 0x20000000 => "IMAGE_SCN_MEM_EXECUTE", 368 | 0x40000000 => "IMAGE_SCN_MEM_READ", 369 | 0x80000000 => "IMAGE_SCN_MEM_WRITE"); 370 | 371 | foreach my $ch (keys %char_hash) { 372 | push(@characteristics, $char_hash{$ch}) if ($char & $ch); 373 | } 374 | return @characteristics; 375 | } 376 | 377 | #---------------------------------------------------------------- 378 | # getXXXX() subroutines 379 | # Gets the contents of various elements of $self 380 | #---------------------------------------------------------------- 381 | sub getError {return $self->{error};} 382 | sub getFileHandle {return $self->{hFile};} 383 | #--------------------------------------------------------------------- 384 | # close() 385 | # close the filehandle 386 | #--------------------------------------------------------------------- 387 | sub close {close($self->{hFile});} 388 | 389 | 1; 390 | __END__ 391 | 392 | =head1 NAME 393 | 394 | File::ReadPE 395 | 396 | =head1 VERSION 397 | 398 | Version 0.1 399 | 400 | =head1 DESCRIPTION 401 | 402 | File::ReadPE - Perl module to read/parse Windows PE file structures without using the Win32 API. This allows 403 | for Win32 image (ie, malware) analysis on any platform that supports Perl. This module retrieves relative 404 | portions of the following structures: 405 | 406 | IMAGE_DOS_HEADER 407 | IMAGE_FILE_HEADER 408 | IMAGE_NT_HEADER 409 | IMAGE_OPTIONAL_HEADER(32) 410 | IMAGE_DATA_DIRECTORY 411 | IMAGE_SECTION_HEADER 412 | 413 | The module also contains several functions to perform translation of 'characteristics' values from the 414 | various structures. 415 | 416 | =head1 SYNOPSIS 417 | 418 | use ReadPE; 419 | my $pefile = shift || die "You must enter a filename.\n"; 420 | die "File not found.\n" unless (-e $pefile); 421 | 422 | my $pe = ReadPE::new($pefile); 423 | my %dos; 424 | if (%dos = $pe->getDOSHeader()) { 425 | printf "magic : 0x%x\n",$dos{magic}; 426 | printf "e_lfanew : 0x%x\n",$dos{e_lfanew}; 427 | } 428 | else { 429 | print "Error : ".$pe->getError()."\n"; 430 | } 431 | 432 | printf "PE header = 0x%x\n",$pe->getNTHeader($dos{e_lfanew}); 433 | 434 | my %fh = $pe->getFileHeader($dos{e_lfanew}); 435 | map{printf "%-30s 0x%x\n",$_,$fh{$_};}(keys %fh); 436 | 437 | print "\n"; 438 | my @list = $pe->getFileHeaderCharacteristics($fh{characteristics}); 439 | map{print "\t$_\n";}@list; 440 | print "\n"; 441 | 442 | my $hdr = $pe->getOptionalHeaderMagic($dos{e_lfanew}); 443 | printf "Optional header magic = 0x%x\n",$hdr; 444 | print "\n"; 445 | 446 | my %opt32 = $pe->getOptionalHeader32($dos{e_lfanew},$fh{size_opt_header}); 447 | print "Subsystem = ".$pe->getOptionalHeaderSubsystem($opt32{subsystem})."\n"; 448 | print "\n"; 449 | 450 | my %dd = $pe->getImageDataDirectories($dos{e_lfanew},$opt32{rva_num}); 451 | foreach my $d (keys %dd) { 452 | printf "%-20s 0x%08x 0x%08x\n",$d,$dd{$d}{rva},$dd{$d}{size}; 453 | } 454 | print "\n"; 455 | 456 | # The PE header structures are contiguous from the $dos{e_lfanew} value; 457 | # To compute the location of the IMAGE_SECTION_HEADER structures, add the 458 | # size of the IMAGE_FILE_HEADER (24 bytes), the IMAGE_OPTIONAL_HEADER headers 459 | # (96 bytes for a 32-bit image), and the total size of the IMAGE_DATA_DIRECTORY 460 | # structures (8 bytes x the number of data directories) 461 | my $sections_offset = $dos{e_lfanew} + 24 + 96 + (8*$opt32{rva_num}); 462 | my %sections = $pe->getImageSectionHeaders($sections_offset,$fh{number_sections}); 463 | foreach my $sect (keys %sections) { 464 | print "$sect\n"; 465 | } 466 | 467 | $pe->close(); 468 | 469 | =head1 METHODS 470 | 471 | =head2 $pe = File::ReadPE::new($filename) 472 | 473 | Creates a new ReadPE object. Opens a file handle and blesses the object. It is up to the script 474 | using this module to determine whether the file exists or not. 475 | 476 | =head2 %dos = $pe->getDOSHeader() 477 | 478 | Reads the first 64 bytes of the file and returns a hash containing the DOS header elements. The 479 | DOS header 'magic' ('MZ', or 0x5a4d) number is used to determine whether the image is a valid 480 | executable. The 'e_lfanew' value is is the offset of the new, PE header, and is used as an 481 | input argument for some of the other methods. 482 | 483 | =head2 $signature = $pe->getNTHeader($offset) 484 | 485 | Reads the IMAGE_NT_HEADER signature located at the offset pointed to by 'e_lfanew'. The method returns 486 | the DWORD value located at the 'e_lfanew' location, which should be 'PE\00\00' for a PE file. 487 | 488 | =head2 %ifh = $pe->getFileHeader($offset) 489 | 490 | Reads the IMAGE_FILE_HEADER structure, which follows the DWORD (4 byte) IMAGE_NT_HEADER structure. On 491 | success, returns a Perl hash containing the elements of the IMAGE_FILE_HEADER structure. 492 | 493 | =head2 @list = $pe->getFileHeaderCharacteristics($characteristics) 494 | 495 | Takes the 'characteristic' WORD value from the IMAGE_FILE_HEADER structure as an input, and returns a 496 | (Perl) list of characteristics of the image. 497 | 498 | =head2 $machine = $pe->getFileHeaderMachine($machine) 499 | 500 | Takes the 'machine' WORD value from the IMAGE_FILE_HEADER structure as an input, and returns the 501 | architecture type of the system. 502 | 503 | =head2 $magic = $pe->getOptionalHeaderMagic($offset) 504 | 505 | Reads the 'magic' WORD value from the IMAGE_OPTIONAL_HEADER structure. This value determines the type of 506 | the image (ie, 32-bit, 64-bit, or ROM). This value is used in conjuction with the 'SizeofOptionalHeader' 507 | WORD value from the IMAGE_FILE_HEADER structure to determine the type and size of the IMAGE_OPTIONAL_HEADER 508 | structure. 509 | 510 | =head2 %opt32 = $pe->getOptionalHeader32($offset,$size) 511 | 512 | Reads in the IMAGE_OPTIONAL_HEADER structure as a 32-bit image header. On success, returns a Perl hash 513 | containing the elements of the 32-bit IMAGE_OPTIONAL_HEADER structure. 514 | 515 | =head2 $sys = $pe->getOptionalHeaderSubsystem($subsystem) 516 | 517 | Takes the 'subsystem' value from the IMAGE_OPTIONAL_HEADER structure and returns the subsystem required for 518 | the image. 519 | 520 | =head2 %dd = $pe->getImageDataDirectories($offset,$opt32{num_rvas}) 521 | 522 | Reads in the IMAGE_DATA_DIRECTORY structures as a hash-of-hashes Perl data structure. Needs an offset, 523 | as well as the number of RVAs from the IMAGE_OPTIONAL_HEADER structure. 524 | 525 | =head2 %sect = $pe->getImageSectionHeaders($offset,$ifh{number_sections}) 526 | 527 | Reads in the IMAGE_SECTION_HEADER structures as a hash-of-hashes Perl data structure. Needs an offset, 528 | and the number of sections to be read (WORD value from the IMAGE_FILE_HEADER structure) 529 | 530 | =head2 @char = $pe->getImageSectionCharacteristics($characteristics) 531 | 532 | Takes in the 'characteristics' value from the IMAGE_SECTION_HEADER structure, and returns a list 533 | containing the various characteristics. 534 | 535 | =head2 $pe->getError() 536 | 537 | Returns the message stored in $self->{error} 538 | 539 | =head2 $pe->getFileHandle() 540 | 541 | Returns the file handle used by the object ($self->{hFile}); useful if you want to access bytes within 542 | the file from the script. 543 | 544 | =head2 $pe->close() 545 | 546 | Closes the file handle 547 | 548 | =head1 REFERENCES 549 | 550 | Structure definitions are located in winnt.h 551 | 552 | pe_image.h - PE image structure definitions 553 | http://research.microsoft.com/invisible/include/loaders/pe_image.h.htm 554 | 555 | PE File Structure 556 | http://www.madchat.org/vxdevl/papers/winsys/pefile/pefile.htm 557 | 558 | Iczelion's PE Tutorial 2: Detecting a Valid PE File 559 | http://win32assembly.online.fr/pe-tut2.html 560 | 561 | ImageHlp API Structures 562 | http://msdn.microsoft.com/library/default.asp?url=/library/en-us/debug/base/imagehlp_structures.asp 563 | 564 | HOWTO: How To Determine Whether an Application is Console or GUI 565 | http://support.microsoft.com/default.aspx?scid=kb;en-us;90493 566 | 567 | =head1 AUTHOR 568 | 569 | Harlan Carvey, Ekeydet89 at yahoo dot comE 570 | 571 | =head1 DOCUMENTATION 572 | 573 | You can find documentation for this module using the perldoc command. 574 | 575 | =head1 BUGS 576 | 577 | Please report any bugs and feature requests to keydet89 at yahoo dot com. 578 | 579 | =head1 TODO 580 | 581 | Need to create modules for parsing import/export tables, resource tables, etc. 582 | 583 | =head1 COPYRIGHT AND LICENSE 584 | 585 | Copyright (C) 2006 by Harlan Carvey (keydet89 at yahoo dot com) 586 | 587 | This library is free software; you can redistribute it and/or modify 588 | it as you like. However, please be sure to provide proper credit where 589 | it is due. 590 | 591 | =cut 592 | --------------------------------------------------------------------------------