PEASS-ng/winPEAS/winPEASexe/winPEAS/ApplicationInfo.cs
2020-08-16 08:44:59 +01:00

610 lines
31 KiB
C#
Executable File

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Management;
using System.Runtime.InteropServices;
using System.Text;
using System.Text.RegularExpressions;
using Microsoft.Win32;
using Microsoft.Win32.TaskScheduler;
namespace winPEAS
{
class ApplicationInfo
{
// https://stackoverflow.com/questions/115868/how-do-i-get-the-title-of-the-current-active-window-using-c
[DllImport("user32.dll")]
static extern IntPtr GetForegroundWindow();
[DllImport("user32.dll")]
static extern int GetWindowText(IntPtr hWnd, StringBuilder text, int count);
public static string GetActiveWindowTitle()
{
const int nChars = 256;
StringBuilder Buff = new StringBuilder(nChars);
IntPtr handle = GetForegroundWindow();
if (GetWindowText(handle, Buff, nChars) > 0)
{
return Buff.ToString();
}
return null;
}
/*public static List<string> GetAppsRegistry()
{
List<string> retList = new List<string>();
try
{
RegistryKey softwareKey = Registry.LocalMachine.OpenSubKey("SOFTWARE");
foreach (string subKeyName in softwareKey.GetSubKeyNames())
{
retList.Add(subKeyName);
}
}
catch (Exception ex)
{
Beaprint.GrayPrint("Error: "+ex);
}
return retList;
}*/
public static SortedDictionary<string, Dictionary<string, string>> GetInstalledAppsPermsPath(string fpath)
{
SortedDictionary<string, Dictionary<string, string>> results = new SortedDictionary<string, Dictionary<string, string>>();
try
{
foreach (string f in Directory.GetFiles(fpath))
{
results[f] = new Dictionary<string, string>(){
{ f, String.Join(", ", MyUtils.GetPermissionsFile(f, Program.currentUserSIDs)) }
};
}
foreach (string d in Directory.GetDirectories(fpath))
{
results[d] = MyUtils.GetRecursivePrivs(d);
}
}
catch (Exception ex)
{
Beaprint.GrayPrint("Error: " + ex);
}
return results;
}
public static SortedDictionary<string, Dictionary<string, string>> GetInstalledAppsPerms()
{
//Get from Program Files
SortedDictionary<string, Dictionary<string, string>> results = GetInstalledAppsPermsPath(@Path.GetPathRoot(Environment.SystemDirectory) + "Program Files");
SortedDictionary<string, Dictionary<string, string>> results2 = GetInstalledAppsPermsPath(@Path.GetPathRoot(Environment.SystemDirectory) + "Program Files (x86)");
results.Concat(results2).ToDictionary(kvp => kvp.Key, kvp => kvp.Value);
//Get from Uninstall
string[] subkeys = MyUtils.GetRegSubkeys("HKLM", @"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall");
if (subkeys != null)
{
foreach (string app in subkeys)
{
string installLocation = MyUtils.GetRegValue("HKLM", String.Format(@"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\{0}", app), "InstallLocation");
if (String.IsNullOrEmpty(installLocation))
continue;
installLocation = installLocation.Replace("\"", "");
if (installLocation.EndsWith(@"\"))
installLocation = installLocation.Substring(0, installLocation.Length - 1);
if (!results.ContainsKey(installLocation) && Directory.Exists(installLocation))
{
bool already = false;
foreach (string path in results.Keys)
{
if (installLocation.IndexOf(path) != -1) //Check for subfoldres of already found folders
{
already = true;
break;
}
}
if (!already)
results[installLocation] = MyUtils.GetRecursivePrivs(installLocation);
}
}
}
subkeys = MyUtils.GetRegSubkeys("HKLM", @"SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall");
if (subkeys != null)
{
foreach (string app in subkeys)
{
string installLocation = MyUtils.GetRegValue("HKLM", String.Format(@"SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\{0}", app), "InstallLocation");
if (String.IsNullOrEmpty(installLocation))
continue;
installLocation = installLocation.Replace("\"", "");
if (installLocation.EndsWith(@"\"))
installLocation = installLocation.Substring(0, installLocation.Length - 1);
if (!results.ContainsKey(installLocation) && Directory.Exists(installLocation))
{
bool already = false;
foreach (string path in results.Keys)
{
if (installLocation.IndexOf(path) != -1) //Check for subfoldres of already found folders
{
already = true;
break;
}
}
if (!already)
results[installLocation] = MyUtils.GetRecursivePrivs(installLocation);
}
}
}
return results;
}
//////////////////////////////////////
/////// Get Autorun Registry ////////
//////////////////////////////////////
/// Find Autorun registry where you have write or equivalent access
public static List<Dictionary<string, string>> GetRegistryAutoRuns(Dictionary<string, string> NtAccountNames)
{
List<Dictionary<string, string>> results = new List<Dictionary<string, string>>();
try
{
List<List<String>> autorunLocations = new List<List<string>>()
{
//Common Autoruns
new List<String> {"HKLM","SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run"},
new List<String> {"HKLM","SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\RunOnce"},
new List<String> {"HKLM","SOFTWARE\\Wow6432Node\\Microsoft\\Windows\\CurrentVersion\\Run"},
new List<String> {"HKLM","SOFTWARE\\Wow6432Node\\Microsoft\\Windows\\CurrentVersion\\RunOnce"},
new List<String> {"HKCU","SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run"},
new List<String> {"HKCU","SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\RunOnce"},
new List<String> {"HKCU","SOFTWARE\\Wow6432Node\\Microsoft\\Windows\\CurrentVersion\\Run"},
new List<String> {"HKCU","SOFTWARE\\Wow6432Node\\Microsoft\\Windows\\CurrentVersion\\RunOnce"},
new List<String> {"HKLM",@"Software\Microsoft\Windows NT\CurrentVersion\Terminal Server\Install\Software\Microsoft\Windows\CurrentVersion\Run"},
new List<String> {"HKLM",@"Software\Microsoft\Windows NT\CurrentVersion\Terminal Server\Install\Software\Microsoft\Windows\CurrentVersion\Runonce"},
new List<String> {"HKLM",@"Software\Microsoft\Windows NT\CurrentVersion\Terminal Server\Install\Software\Microsoft\Windows\CurrentVersion\RunEx"},
//Service Autoruns
new List<String> {"HKLM","SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\RunService"},
new List<String> {"HKLM","SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\RunOnceService"},
new List<String> {"HKLM","SOFTWARE\\Wow6432Node\\Microsoft\\Windows\\CurrentVersion\\RunService"},
new List<String> {"HKLM","SOFTWARE\\Wow6432Node\\Microsoft\\Windows\\CurrentVersion\\RunOnceService"},
new List<String> {"HKCU", "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\RunService"},
new List<String> {"HKCU", "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\RunOnceService"},
new List<String> {"HKCU", "SOFTWARE\\Wow6432Node\\Microsoft\\Windows\\CurrentVersion\\RunService"},
new List<String> {"HKCU", "SOFTWARE\\Wow6432Node\\Microsoft\\Windows\\CurrentVersion\\RunOnceService"},
//Special Autorun
new List<String> {"HKLM","Software\\Microsoft\\Windows\\CurrentVersion\\RunOnceEx"},
new List<String> {"HKLM","Software\\Wow6432Node\\Microsoft\\Windows\\CurrentVersion\\RunOnceEx"},
new List<String> {"HKCU","Software\\Microsoft\\Windows\\CurrentVersion\\RunOnceEx"},
new List<String> {"HKCU","Software\\Wow6432Node\\Microsoft\\Windows\\CurrentVersion\\RunOnceEx"},
//Startup Path
new List<String> {"HKCU", @"Software\Microsoft\Windows\CurrentVersion\Explorer\User Shell Folders", "Common Startup"},
new List<String> {"HKCU", @"Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders", "Common Startup"},
new List<String> {"HKLM", @"SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders", "Common Startup"},
new List<String> {"HKLM", @"SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\User Shell Folders", "Common Startup"},
//Winlogon
new List<String> {"HKLM", @"SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon", "Userinit"},
new List<String> {"HKLM", @"SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon", "Shell"},
//Policy Settings
new List<String> {"HKLM", @"Software\Microsoft\Windows\CurrentVersion\Policies\Explorer", "Run"},
new List<String> {"HKCU", @"Software\Microsoft\Windows\CurrentVersion\Policies\Explorer", "Run"},
//AlternateShell in SafeBoot
new List<String> {"HKLM","SYSTEM\\CurrentControlSet\\Control\\SafeBoot", "AlternateShell"},
//Font Drivers
new List<String> {"HKLM", @"SOFTWARE\Microsoft\Windows NT\CurrentVersion\Font Drivers"},
new List<String> {"HKLM", @"SOFTWARE\WOW6432Node\Microsoft\Windows NT\CurrentVersion\Font Drivers"},
//Open Command
new List<String> {"HKLM", @"SOFTWARE\Classes\htmlfile\shell\open\command", ""}, //Get (Default) value with empty string
new List<String> {"HKLM", @"SOFTWARE\Wow6432Node\Classes\htmlfile\shell\open\command", ""}, //Get (Default) value with empty string
};
List<List<String>> autorunLocationsKeys = new List<List<String>>
{
//Installed Components
new List<String> { "HKLM","SOFTWARE\\Microsoft\\Active Setup\\Installed Components", "StubPath"},
new List<String> { "HKLM","SOFTWARE\\Wow6432Node\\Microsoft\\Active Setup\\Installed Components", "StubPath"},
new List<String> { "HKCU","SOFTWARE\\Microsoft\\Active Setup\\Installed Components", "StubPath"},
new List<String> { "HKCU","SOFTWARE\\Wow6432Node\\Microsoft\\Active Setup\\Installed Components", "StubPath"},
};
//This registry expect subkeys with the CLSID name
List<List<String>> autorunLocationsKeysCLSIDs = new List<List<String>>
{
//Browser Helper Objects
new List<String> { "HKLM", @"Software\Microsoft\Windows\CurrentVersion\Explorer\Browser Helper Objects" },
new List<String> { "HKLM", @"Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Explorer\Browser Helper Objects" },
//Internet Explorer Extensions
new List<String> { "HKLM", @"Software\Microsoft\Internet Explorer\Extensions" },
new List<String> { "HKLM", @"Software\Wow6432Node\Microsoft\Internet Explorer\Extensions" },
};
//Add the keyvalues inside autorunLocationsKeys to autorunLocations
foreach (List<String> autorunLocationKey in autorunLocationsKeys)
{
List<String> subkeys = MyUtils.GetRegSubkeys(autorunLocationKey[0], autorunLocationKey[1]).ToList();
foreach (String keyname in subkeys)
{
string clsid_name = keyname;
Match clsid = Regex.Match(keyname, @"^\W*(\{[\w\-]+\})\W*");
if (clsid.Groups.Count > 1) //Sometime the CLSID is bad writting and this kind of fix common mistakes
clsid_name = clsid.Groups[1].ToString();
if (autorunLocationKey.Count > 2)
autorunLocations.Add(new List<string> { autorunLocationKey[0], autorunLocationKey[1] + "\\" + clsid_name, autorunLocationKey[2] });
else
autorunLocations.Add(new List<string> { autorunLocationKey[0], autorunLocationKey[1] + "\\" + clsid_name });
}
}
//Read registry and get values
foreach (List<String> autorunLocation in autorunLocations)
{
Dictionary<string, object> settings = MyUtils.GetRegValues(autorunLocation[0], autorunLocation[1]);
if ((settings != null) && (settings.Count != 0))
{
foreach (KeyValuePair<string, object> kvp in settings)
{
RegistryKey key = null;
if ("HKLM" == autorunLocation[0])
key = Registry.LocalMachine.OpenSubKey(autorunLocation[1]);
else
key = Registry.CurrentUser.OpenSubKey(autorunLocation[1]);
if (autorunLocation.Count > 2 && kvp.Key != autorunLocation[2])
continue; //If only interested on 1 key of the registry and it's that one, continue
string orig_filepath = Environment.ExpandEnvironmentVariables(String.Format("{0}", kvp.Value));
string filepath = orig_filepath;
if (MyUtils.GetExecutableFromPath(Environment.ExpandEnvironmentVariables(String.Format("{0}", kvp.Value))).Length > 0)
filepath = MyUtils.GetExecutableFromPath(filepath);
string filepath_cleaned = filepath.Replace("'", "").Replace("\"", "");
string folder = System.IO.Path.GetDirectoryName(filepath_cleaned);
try
{ //If the path doesn't exist, pass
if (File.GetAttributes(filepath_cleaned).HasFlag(FileAttributes.Directory))
{ //If the path is already a folder, change the values of the params
orig_filepath = "";
folder = filepath_cleaned;
}
}
catch
{
}
results.Add(new Dictionary<string, string>()
{
{"Reg", autorunLocation[0] + "\\" + autorunLocation[1]},
{"RegKey", kvp.Key},
{"Folder", folder},
{"File", orig_filepath},
{
"RegPermissions",
string.Join(", ", MyUtils.GetMyPermissionsR(key, Program.currentUserSIDs))
},
{
"interestingFolderRights",
String.Join(", ", MyUtils.GetPermissionsFolder(folder, Program.currentUserSIDs))
},
{
"interestingFileRights",
orig_filepath.Length > 1 ? String.Join(", ", MyUtils.GetPermissionsFile(orig_filepath, Program.currentUserSIDs)) : ""
},
{"isUnquotedSpaced", MyUtils.CheckQuoteAndSpace(filepath).ToString()}
});
}
}
}
//Check the autoruns that depends on CLSIDs
foreach (List<String> autorunLocation in autorunLocationsKeysCLSIDs)
{
List<String> CLSIDs = MyUtils.GetRegSubkeys(autorunLocation[0], autorunLocation[1]).ToList();
foreach (String clsid in CLSIDs)
{
string reg = autorunLocation[1] + "\\" + clsid;
RegistryKey key = null;
if ("HKLM" == autorunLocation[0])
key = Registry.LocalMachine.OpenSubKey(reg);
else
key = Registry.CurrentUser.OpenSubKey(reg);
string orig_filepath = MyUtils.GetCLSIDBinPath(clsid);
if (String.IsNullOrEmpty(orig_filepath))
continue;
orig_filepath = Environment.ExpandEnvironmentVariables(orig_filepath).Replace("'", "").Replace("\"", "");
string folder = System.IO.Path.GetDirectoryName(orig_filepath);
results.Add(new Dictionary<string, string>()
{
{"Reg", autorunLocation[0] + "\\" + reg},
{"RegKey", ""},
{"Folder", folder},
{"File", orig_filepath},
{
"RegPermissions",
string.Join(", ", MyUtils.GetMyPermissionsR(key , Program.currentUserSIDs))
},
{
"interestingFolderRights",
String.Join(", ", MyUtils.GetPermissionsFolder(folder, Program.currentUserSIDs))
},
{
"interestingFileRights",
orig_filepath.Length > 1 ? String.Join(", ", MyUtils.GetPermissionsFile(orig_filepath, Program.currentUserSIDs)) : ""
},
{"isUnquotedSpaced", MyUtils.CheckQuoteAndSpace(orig_filepath).ToString()}
});
}
}
}
catch (Exception ex)
{
Beaprint.GrayPrint(String.Format(" [X] Exception: {0}", ex.Message));
}
return results;
}
public static List<Dictionary<string, string>> GetAutoRunsFolder()
{
List<Dictionary<string, string>> results = new List<Dictionary<string, string>>();
List<string> autorunLocations = new List<string>();
autorunLocations.Add(Environment.ExpandEnvironmentVariables(@"%appdata%\Microsoft\Windows\Start Menu\Programs\Startup"));
autorunLocations.Add(Environment.ExpandEnvironmentVariables(@"%programdata%\Microsoft\Windows\Start Menu\Programs\Startup"));
foreach (string path in autorunLocations)
{
foreach (string filepath in Directory.GetFiles(path, "*", SearchOption.TopDirectoryOnly))
{
string folder = Path.GetDirectoryName(filepath);
results.Add(new Dictionary<string, string>() {
{ "Reg", "" },
{ "RegKey", "" },
{ "RegPermissions", "" },
{ "Folder", folder },
{ "File", filepath },
{ "isWritableReg", ""},
{ "interestingFolderRights", String.Join(", ", MyUtils.GetPermissionsFolder(folder, Program.currentUserSIDs))},
{ "interestingFileRights", String.Join(", ", MyUtils.GetPermissionsFile(filepath, Program.currentUserSIDs))},
{ "isUnquotedSpaced", "" }
});
}
}
return results;
}
public static List<Dictionary<string, string>> GetAutoRunsWMIC()
{
List<Dictionary<string, string>> results = new List<Dictionary<string, string>>();
try
{
SelectQuery query = new SelectQuery("Win32_StartupCommand");
ManagementObjectSearcher searcher = new ManagementObjectSearcher(query);
ManagementObjectCollection win32_startup = searcher.Get();
foreach (ManagementObject startup in win32_startup)
{
string command = startup["command"].ToString();
command = Environment.ExpandEnvironmentVariables(String.Format("{0}", command));
string filepath = MyUtils.GetExecutableFromPath(command);
string filepath_cleaned = filepath.Replace("'", "").Replace("\"", "");
string folder = System.IO.Path.GetDirectoryName(filepath_cleaned);
results.Add(new Dictionary<string, string>()
{
{"Reg", ""},
{"RegKey", "From WMIC"},
{"RegPermissions", ""},
{"Folder", folder},
{"File", command},
{"isWritableReg", ""},
{
"interestingFolderRights",
String.Join(", ", MyUtils.GetPermissionsFolder(folder, Program.currentUserSIDs))
},
{
"interestingFileRights",
String.Join(", ", MyUtils.GetPermissionsFile(filepath, Program.currentUserSIDs))
},
{"isUnquotedSpaced", MyUtils.CheckQuoteAndSpace(command).ToString()}
});
}
}
catch (Exception e)
{
Beaprint.GrayPrint("Error getting autoruns from WMIC: " + e);
}
return results;
}
public static List<Dictionary<string, string>> GetAutoRuns(Dictionary<string,string> NtAccountNames)
{
List<Dictionary<string, string>> reg_autorus = GetRegistryAutoRuns(NtAccountNames);
List<Dictionary<string, string>> file_autorus = GetAutoRunsFolder();
List<Dictionary<string, string>> wmic_autorus = GetAutoRunsWMIC();
reg_autorus.AddRange(file_autorus);
reg_autorus.AddRange(wmic_autorus);
return reg_autorus;
}
public static List<Dictionary<string, string>> GetScheduledAppsNoMicrosoft()
{
List<Dictionary<string, string>> results = new List<Dictionary<string, string>>();
try
{
void EnumFolderTasks(TaskFolder fld)
{
foreach (Microsoft.Win32.TaskScheduler.Task task in fld.Tasks)
ActOnTask(task);
//task.Name
//task.Enabled
//task.Definition.Actions
//task.Definition
foreach (TaskFolder sfld in fld.SubFolders)
EnumFolderTasks(sfld);
}
void ActOnTask(Microsoft.Win32.TaskScheduler.Task t)
{
if (t.Enabled && (!String.IsNullOrEmpty(t.Definition.RegistrationInfo.Author) && !t.Definition.RegistrationInfo.Author.Contains("Microsoft")))
{
List<string> f_trigger = new List<string>();
foreach (Trigger trigger in t.Definition.Triggers)
f_trigger.Add(String.Format("{0}", trigger));
results.Add(new Dictionary<string, string>()
{
{ "Name", t.Name },
{ "Action", Environment.ExpandEnvironmentVariables(String.Format("{0}", t.Definition.Actions)) },
{ "Trigger", String.Join("\n ", f_trigger) },
{ "Author", String.Join(", ", t.Definition.RegistrationInfo.Author) },
{ "Description", String.Join(", ", t.Definition.RegistrationInfo.Description) },
});
}
}
EnumFolderTasks(TaskService.Instance.RootFolder);
}
catch (Exception ex)
{
Beaprint.GrayPrint("Error: " + ex);
}
return results;
}
private class EnumAPI
{
[DllImport("psapi")]
public static extern bool EnumDeviceDrivers(
UIntPtr[] driversList,
UInt32 arraySizeBytes,
out UInt32 bytesNeeded
);
[DllImport("psapi")]
public static extern int GetDeviceDriverFileName(
UIntPtr baseAddr,
StringBuilder name,
UInt32 nameSize
);
[DllImport("psapi")]
public static extern int GetDeviceDriverBaseName(
UIntPtr baseAddr,
StringBuilder name,
UInt32 nameSize
);
}
public static Dictionary<string, FileVersionInfo> GetDeviceDriversNoMicrosoft()
{
Dictionary<string, FileVersionInfo> results = new Dictionary<string, FileVersionInfo>();
// ignore ghosts
// https://devblogs.microsoft.com/oldnewthing/20160913-00/?p=94305
Regex ignore_ghosts = new Regex("^dump_", RegexOptions.Compiled | RegexOptions.IgnoreCase);
// manufacturer/providers to ignore
Regex ignore_company = new Regex("^Microsoft", RegexOptions.Compiled | RegexOptions.IgnoreCase);
string system32 = Environment.SystemDirectory;
// Get a list of loaded kernel modules
UInt32 neededBytes;
EnumAPI.EnumDeviceDrivers(null, 0, out neededBytes);
UIntPtr[] drivers = new UIntPtr[neededBytes / UIntPtr.Size];
EnumAPI.EnumDeviceDrivers(drivers, (UInt32)(drivers.Length * UIntPtr.Size), out neededBytes);
// iterate over modules
foreach (UIntPtr baseAddr in drivers)
{
StringBuilder buffer = new StringBuilder(1024);
EnumAPI.GetDeviceDriverBaseName(baseAddr, buffer, (UInt32)buffer.Capacity);
if (ignore_ghosts.IsMatch(buffer.ToString()))
{
continue;
}
EnumAPI.GetDeviceDriverFileName(baseAddr, buffer, (UInt32)buffer.Capacity);
string pathname = buffer.ToString();
// GetDeviceDriverFileName can return a path in a various number of formats, below code tries to handle them.
// https://community.osr.com/discussion/228671/querying-device-driver-list-from-kernel-mode
if (pathname.StartsWith("\\??\\"))
{
pathname = pathname.Remove(0, 4);
}
if (File.Exists(pathname))
{
// intentionally empty
}
else if (pathname[0] == '\\')
{
// path could be either in the NtObject namespace or from the filesystem root (without drive)
if (File.Exists("\\\\.\\GLOBALROOT" + pathname))
{
pathname = "\\\\.\\GLOBALROOT" + pathname;
}
else if (File.Exists(system32.Substring(0, 2) + pathname))
{
pathname = system32.Substring(0, 2) + pathname;
}
else
{
Beaprint.GrayPrint(string.Format("Ignoring unknown path {0}", pathname));
continue;
}
}
else
{
// probably module is a boot driver without a full path
if (File.Exists(system32 + "\\drivers\\" + pathname))
{
pathname = system32 + "\\drivers\\" + pathname;
}
else if (File.Exists(system32 + "\\" + pathname))
{
pathname = system32 + "\\" + pathname;
}
else
{
Beaprint.GrayPrint(string.Format("Ignoring unknown path {0}", pathname));
continue;
}
}
try
{
FileVersionInfo info = FileVersionInfo.GetVersionInfo(pathname.ToString());
if (!ignore_company.IsMatch(info.CompanyName))
{
results[pathname] = info;
}
}
catch (Exception ex)
{
Beaprint.GrayPrint("Error: " + ex);
}
}
return results;
}
}
}