I know it is possible to check the dirty bit status of a unit by running the command fsutil dirty query c: from an elevated prompt. On windows 10 it is also possible to know if C: dirty bit is set without the need of admin privileges simply going into the System and Maintenance page, if dirty bit is set there will be an advice telling it is necessary to reboot in order to repair a damage in the file sistem. How could the dirty bit status (of any unit or even only C:) be checked from a C# program?
Thanks in Advance to anyone will answer
You can get this information using a WMI query
var q = new ObjectQuery("Select * FROM Win32_Volume");
using (var searcher = new ManagementObjectSearcher(q))
using (var moc = searcher.Get())
{
foreach (ManagementObject volume in moc)
{
String label = (String)volume["Label"];
Boolean dirtyBitSet = (Boolean)(volume["DirtyBitSet"] ?? false);
Console.WriteLine($"{label} => {dirtyBitSet}");
}
}
You should add a reference to the System.Management assembly and also run your program using an elevated prompt
Related
I try to close running user apps (Application Programs) in windows by C# like word, forxitReader, whatsApp , etc.
I try to make a desktop App to secure an online exam like Safe Exam Browser.
My senario is:
1 - get the process of them only as a list (not System programs.)
2 - Kill them by Process.kill();
But I don't know how to do the first step.
How can I get a list of these programs only?
I understand you need to check the process owner to know if its a system or non-system process, and to my knowledge there is no way to get the process owner via .NET api, but you can get active process and their owner in powershell via
Get-Process -IncludeUserName
Now doing this in C# is a bit tricky because out of the box, the Process class is lacking and making CLI calls is complicated, but I was able to write this solution with only Cake.Powershell nuget package.
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Management.Automation;
using (var powershell = PowerShell.Create())
{
List<string> illegalProcesses = new List<string>() { "dota2" };
powershell.AddScript("Get-Process -IncludeUserName");
var results = powershell.Invoke();
var nonSystemResults = results.Where(x => !x.Properties["Username"].Value.ToString().StartsWith("NT AUTHORITY"));
foreach (var result in results)
{
var processOwner = result.Properties["Username"].Value as string;
if (processOwner != null && !processOwner.StartsWith("NT AUTHORITY"))
{
var processName = result.Properties["Name"].Value as string;
if (illegalProcesses.Contains(processName))
{
var processID = result.Properties["ID"].Value as int?;
var doomedProcess = Process.GetProcessById(processID.Value);
doomedProcess?.Kill();
}
}
}
}
I've made the assumption that system processes are the ones owned by users that start with NT AUTHORITY but you might find for your particular use case you want different filters. That said there are tons of properties you can get on processes that should allow you to write your own filters.
On Windows IoT (but also on the normal Windows 10), you can enable keyboard filter and shell launcher.
I enabled UWF, KB-Filter and Shell Launcher.
Now, I can't get the parameters of my keyboardfilter with a simple C#.NET program.
ManagementScope scope = new ManagementScope(#"root\standardcimv2\embedded");
using (ManagementClass mc = new ManagementClass(scope.Path.Path, "WEKF_Settings", null))
{
ManagementObjectCollection moc = mc.GetInstances();
foreach (ManagementObject mo in moc)
{
}
}
The UWF_Filter is working. WEKF_Settings and WESL_UserSetting are not working, even if they are enabled on the OS.
I get always the exception Provider Load Failure, even if the application is started as administrator.
In powershell, I get the class without any problem:
Get-CimClass wekf_settings \\.\root\standardcimv2\embedded | select -expand cimclassqualifiers
So the question: why can't I get the instances (with GetInstances()) in C# but only with powershell?
Just as info (if you get the same error):
The query fails also on Powershell, if this is 32-bit.
You need to compile the program as 64bit. Then the code will be able to query the keyboard filter.
This was the solution.
since some time now I try to figure out how to correctly setup this new UWF (Unified Write Filter). Unfortunately it seems there is only documentation for Win 8.1 industry (here), not for Win 10. I hope there were no relevant changes since.
I also asked this on the WindowsDevCenter but got no response so far.
Here is my problem:
With the WMI providers I got UWF enabled by now (UWF_Filter.Enable()), but I cannot protect any volume.
Also the volume list looks very strange: There are 4 entrys, everyone is with CurrentSession=True.
The first is for an volume with no drive letter, only a volume id.
The second is for C:
and then there are 2 identical for D: .
Should'nt there normally be 2 entrys per volume, one where CurrentSession is true and one where its false, meaning its the setting applied after reboot?
If I try to execute Protect on the ManagementObject with DriveLetter=C: I get an Access denied exception, I assume because its the object for the current session.
Also if I try uwfmgr.exe Volume Protect C: on the console it simply hangs: no reaction, no error, only a forever blinking cursor. EDIT: it turned out this was a problem caused by another installed software. See also below.
Do I have to enable or disable or do anything else before I can protect volumes?
Thanks in advance,
Sebastian
My system:
Windows 10 IOT Enterprise 2016 LTSB x64
1 SSD 250GB with Boot, C: and D:
Edit:
Here I asked a follow up question with some other details and a workaround. If I use uwfmgr.exe volume protect c: for example, it works and UWF_Volume now suddenly has (the correct) 2 entries for C:, one for the current and one for the next session.
However I want to avoid this, because IMHO it should be solveable by WMI only.
Edit 2: #sommmen
The partition layout is as following: One disk with 4 partitions.
Boot, 500MB
C:/ , 45GB
unknown, 500MB (Boot-Backup I think)
D:/ , ~200GB
PS:
Please could anyone create the tags uwf and uwfmgr? Would be nice :-)
Missing UWF_Volume instances often appeared after reboot in my tests. But if not, you can create them directly using ManagementClass.CreateInstance().
The problem here is that the official docs are not exactly correct. The description of the UWF_Volume.VolumeName property is:
The unique identifier of the volume on the current system. The
VolumeName is the same as the DeviceID property of the Win32_Volume
class for the volume.
from: https://learn.microsoft.com/en-us/windows-hardware/customize/enterprise/uwf-volume#properties
In fact, the DeviceID needs a slight modification, before using it as value for UWF_Volume.VolumeName:
DeviceID.Substring(4).TrimEnd('\\')
So, after removing prefix \\?\ and removing any trailing slashes you can create instances with CurrentSession=false for the specified device.
This also works in Windows 10 Pro without any uwfmgr.exe. Though, officially not recommended/supported.
Also, I was not able to delete instances, yet. So be sure to add only correct values.
Full Example:
// example value
var DeviceId_From_Win32_Volume = #"\\?\Volume{c2eac053-27e3-4f94-b28c-c2c53d5f4fe1}\";
// example value
var myDriveLetter = "C:";
var myDeviceId = DeviceId_From_Win32_Volume.Substring(4).TrimEnd('\\');
var wmiNamespace = "root\\standardcimv2\\embedded";
var className = "UWF_Volume";
var mgmtScope = new ManagementScope {Path = {NamespacePath = wmiNamespace}};
var mgmtPath = new ManagementPath(className);
var mgmtClass = new ManagementClass(mgmtScope, mgmtPath, null);
// prepare the new object
var newObj = mgmtClass.CreateInstance();
newObj.SetPropertyValue("DriveLetter", myDriveLetter);
newObj.SetPropertyValue("VolumeName", myDeviceId);
newObj.SetPropertyValue("CurrentSession", false);
newObj.SetPropertyValue("CommitPending", false);
newObj.SetPropertyValue("BindByDriveLetter", false);
// create the WMI instance
newObj.Put(new PutOptions {Type = PutType.CreateOnly});
I experience the similar issue in that I could not query the UWF_Volume with CurrentSession=False. However, there's one thing I did that seems to "generate" the UWF_Volume management object with CurrentSession=False. I ran "uwfmgr volume protect c:". Unfortunately, in your case running this causes it to hang.
Could you try running uwfmgr in cmd in admin? Also, if you run "uwfmgr get-config", would you be able to get the current setting of the write filter?
Another thing from your description: you said there are two identical volumes for D:, but if you looks closely at the properties, one would be CurrentSession=True, and the other one is CurrentSession=False. According to the documentation, if you want to make change, you must select the management object (UWF_Volume) with CurrentSession=False.
https://learn.microsoft.com/en-us/windows-hardware/customize/enterprise/uwf-volume
(scroll down to powershell script code sample section)
First of all a volume may have several partitions. They will show up as having the same drive label.
e.g.
C:/ //?/{some guid here}
C:/ //?/{some other guid here}
Now this is common for the %systemDrive% because this has the boot partition.
You can use the commands
mountvol
and
Diskpart
List volume
To figure out the right guid for your need (or you can protect both the boot partition and the system partition). Also using wmi you can look at Win32_volume under namespace cimv2 to get some more insight.
The command line util UWFmgr seems to create an UWF_VOLUME wmi instance once you run the protect command. The docs also hint that you need to create an object yourself.
function Set-ProtectVolume($driveLetter, [bool] $enabled) {
# Each volume has two entries in UWF_Volume, one for the current session and one for the next session after a restart
# You can only change the protection status of a drive for the next session
$nextConfig = Get-WMIObject -class UWF_Volume #CommonParams |
where {
$_.DriveLetter -eq "$driveLetter" -and $_.CurrentSession -eq $false
};
# If a volume entry is found for the drive letter, enable or disable protection based on the $enabled parameter
if ($nextConfig) {
Write-Host "Setting drive protection on $driveLetter to $enabled"
if ($Enabled -eq $true) {
$nextConfig.Protect() | Out-Null;
} else {
$nextConfig.Unprotect() | Out-Null;
}
}
=======> (!) im talking about this comment
# If the drive letter does not match a volume, create a new UWF_volume instance
else {
Write-Host "Error: Could not find $driveLetter. Protection is not enabled."
}
}
The docs however do not provide a method of doing this. For now it seems we need to use the command line util till someone has an example using the WMI provider.
To answer my own question: So far I have only a workaround but no real solution.
It is to check if there is an entry with CurrentSession=False and if not invoke the command directly:
ManagementObjectSearcher ms = new ManagementObjectSearcher(_Scope, new ObjectQuery("select * from UWF_Volume where VolumeName = \"" + volId + "\" AND CurrentSession=\"False\""));
ManagementObjectCollection c = ms.Get();
UInt32 res = 1;
foreach (ManagementObject mo in c)
{
// entry found: do it with WMI
res = (UInt32)mo.InvokeMethod(newState ? "Protect" : "Unprotect", new object[] { });
}
if (c.Count == 1 && res == 0)
// message: success
if (c.Count == 0)
{
// no entry found: invoke cmd
ProcessStartInfo info = new ProcessStartInfo("uwfmgr.exe", "volume " + (newState ? "Protect" : "Unprotect") + #" \\?\" + volId);
Process process = new Process();
info.Verb = "runas"; //needs admin
process.StartInfo = info;
process.Start();
process.WaitForExit();
}
This has the side effect that for a split second a command line window will pop up, but nevertheless it works well.
I am trying to come up with a good way to enumerate hard disks on remote workstations, possibly including administrative shares, so I can audit key files on them without having to access them via sneakernet. I have domain administrator rights. Security policy prohibits using WMI which would be a great solution.
I can retrieve a list of computers using Active Directory, but I need some way to determine what drives are available on each system. Is such a thing feasible? A fellow developer offered some VB6 code from years ago that used WNetOpenEnum, but I was hoping that since we are at .NET framework 4, maybe there's a more elegant / managed way of working with this.
Any ideas would be much appreciated!
EDIT:
I'm keen to use technologies that are more generally supported, such as standard APIs etc. WMI is a great solution but apparently is blocked by default by Windows Firewall, so its availability is not guaranteed.
Add a reference to System.Management, then:
using System;
using System.Management;
namespace WmiConnectRemote
{
class Program
{
static void Main(string[] args)
{
var machine = "XXXX";
var options = new ConnectionOptions { Username = "XXXX", Password = "XXXX" };
var scope = new ManagementScope(#"\\" + machine + #"\root\cimv2", options);
var queryString = "select Name, Size, FreeSpace from Win32_LogicalDisk where DriveType=3"; var query = new ObjectQuery(queryString);
var worker = new ManagementObjectSearcher(scope, query);
var results = worker.Get();
foreach (ManagementObject item in results)
{
Console.WriteLine("{0} {2} {1}", item["Name"], item["FreeSpace"], item["Size"]);
}
}
}
}
I am zipping files in a folder using the DotNetZip libraries. To identify files that are currently open by other processes I am using 'handle.exe' from SysInternals.com. I do this by calling it with parameters and parsing the output, along these lines.
using (Process handleProcess = new Process())
{
// -- Set up the parameters and call the process.
handleProcess.StartInfo.FileName = "handle.exe";
handleProcess.StartInfo.UseShellExecute = false;
handleProcess.StartInfo.RedirectStandardOutput = true;
handleProcess.StartInfo.Arguments = "-u " + fileName;
handleProcess.Start();
...
Which works but has the air of a kludge about it. Can anyone suggest a better approach within managed code ?
The following code shows you the files opened by other processes:
SelectQuery query = new SelectQuery("select name from cim_datafile");
using (ManagementObjectSearcher searcher = new
ManagementObjectSearcher(query))
{
foreach (ManagementObject mo in searcher.Get())
{
Console.WriteLine("File Name: {0} \nIs currently opened", mo.Properties["Name"].Value);
}
}
It is a slightly modified version of this.
See the answers in a similar question asked on SO. In another question using interop was suggested (and someone else suggested SysInternals as well).
No matter what you do, you need to have retry logic. I've written adapters before that have had to retry. The retry logic itself was encapsulated in a RetryTracker class with sub classes for different algorithms (e.g. linear delay, stepping delay, etc.).