Determine the Drive Letter of a mounted TrueCrypt volume

北战南征 提交于 2019-12-19 06:16:14

问题


After a TrueCrypt container has been mounted to a drive letter, is it possible to determine in a batch file which container the drive letter was mounted from, or which drive letter the container was mounted to?

In a batch file, I want to mount a specified TrueCrypt container to a specified drive letter. TrueCrypt errors if the container is already mounted or if the drive letter isn't available, so I want to run TrueCrypt only if the specified container hasn't already been mounted to the specified drive letter, that is, only if the action hasn't been completed already.

Any suggestions would be appreciated.

Edit

Bounty Summary In short imagine you have volumes C:\Vol1.tc and C:\Vol2.tc mounted to drives X and Y. How can you tel that C:\Vol1.tc is mounted to drive X and C:\Vol2.tc to drive Y programaticaly with a batch file or C# code?


回答1:


A way to do that would be to directly ask to Truecrypt driver himself. This could be achieved with the DeviceIoControl function. In fact, that's exactly what the TrueCrypt GUI is doing.

Note that it's easier to do that in c++. You'll find a good article here.

The idea is to call the DeviceIoControl function, asking for the TC_IOCTL_GET_MOUNTED_VOLUMES You will obtain a structure, with all of the mounted volumes path and drive letters. In fact it's a 26 elements array (one for each possible drive letter), called wszVolume, which contain the path of the truecrypt volume which is mounted on.

Hope the following sample will help you find how to do in your case.

Sample Code in C# :

class Program
{
    static void Main(string[] args)
    {
        uint size = (uint)Marshal.SizeOf(typeof(MOUNT_LIST_STRUCT));
        IntPtr buffer = Marshal.AllocHGlobal((int)size);
        uint bytesReturned;
        IntPtr _hdev = CreateFile("\\\\.\\TrueCrypt", FileAccess.ReadWrite, FileShare.ReadWrite, IntPtr.Zero, FileMode.Open, 0, IntPtr.Zero);
        bool bResult = DeviceIoControl(_hdev, TC_IOCTL_GET_MOUNTED_VOLUMES, buffer, size, buffer, size, out bytesReturned, IntPtr.Zero);
        MOUNT_LIST_STRUCT mount = new MOUNT_LIST_STRUCT();
        Marshal.PtrToStructure(buffer, mount);
        Marshal.FreeHGlobal(buffer);

        for (int i = 0; i < 26; i++)
            Console.WriteLine("{0}: => {1}", (char)('A' + i), mount.wszVolume[i]);
    }

    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)]
    class MOUNT_LIST_STRUCT
    {
        public readonly UInt32 ulMountedDrives; /* Bitfield of all mounted drive letters */
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 26)]
        public readonly MOUNT_LIST_STRUCT_VOLUME_NAME[] wszVolume;  /* Volume names of mounted volumes */
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 26)]
        public readonly UInt64[] diskLength;
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 26)]
        public readonly int[] ea;
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 26)]
        public readonly int[] volumeType;   /* Volume type (e.g. PROP_VOL_TYPE_OUTER, PROP_VOL_TYPE_OUTER_VOL_WRITE_PREVENTED, etc.) */
    }

    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)]
    struct MOUNT_LIST_STRUCT_VOLUME_NAME
    {
        [MarshalAs(UnmanagedType.ByValArray, ArraySubType = UnmanagedType.I2, SizeConst = 260)]
        public readonly char[] wszVolume;   /* Volume names of mounted volumes */

        public override string ToString()
        {
            return (new String(wszVolume)).TrimEnd('\0');
        }
    }

    public static int CTL_CODE(int DeviceType, int Function, int Method, int Access)
    {
        return (((DeviceType) << 16) | ((Access) << 14) | ((Function) << 2)
          | (Method));
    }
    private static readonly uint TC_IOCTL_GET_MOUNTED_VOLUMES = (uint)CTL_CODE(0x00000022, 0x800 + (6), 0, 0);

    [DllImport("kernel32.dll", ExactSpelling = true, SetLastError = true, CharSet = CharSet.Auto)]
    static extern bool DeviceIoControl(IntPtr hDevice, uint dwIoControlCode,
    IntPtr lpInBuffer, uint nInBufferSize,
    IntPtr lpOutBuffer, uint nOutBufferSize,
    out uint lpBytesReturned, IntPtr lpOverlapped);

    [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    public static extern IntPtr CreateFile(
         [MarshalAs(UnmanagedType.LPTStr)] string filename,
         [MarshalAs(UnmanagedType.U4)] FileAccess access,
         [MarshalAs(UnmanagedType.U4)] FileShare share,
         IntPtr securityAttributes, // optional SECURITY_ATTRIBUTES struct or IntPtr.Zero
         [MarshalAs(UnmanagedType.U4)] FileMode creationDisposition,
         [MarshalAs(UnmanagedType.U4)] FileAttributes flagsAndAttributes,
         IntPtr templateFile);
}



回答2:


This is what I have so far:

I am creating a custom app written in c# that informs the user what volumes are mounted/dismounted and if they are mounted I need to inform the user on what drive. In order to know if a volume is mounted I have the following class:

Note I make use of handle.exe that program can be downloaded at http://technet.microsoft.com/en-us/sysinternals/bb896655.aspx or also from http://download.sysinternals.com/files/Handle.zip

Also I belive you must be running the program as an administrator

class TrueCryptHelp
{
    // I have that program on the working directory
    // it can be downloaded from http://technet.microsoft.com/en-us/sysinternals/bb896655.aspx
    const string HandleExeLocation = "handle.exe"; 
    static string systemProcessFiles;
    static DateTime dateFilesLockedInfo = new DateTime();
    static string SystemProcessFiles
    {
        get
        {
            if ((DateTime.Now - dateFilesLockedInfo).TotalSeconds > 2)
            {
                Process p = new Process();
                var psi = new ProcessStartInfo();
                psi.RedirectStandardOutput = true;
                psi.UseShellExecute = false;
                psi.FileName = HandleExeLocation;
                p.StartInfo = psi;
                p.Start();

                var output = p.StandardOutput.ReadToEnd();

                systemProcessFiles = string.Empty;

                foreach (Match m in Regex.Matches(output ?? "", @"(?sx) -{20}  [^-]  .+?  -{20}"))
                {
                    if (Regex.Match(m.Value ?? "", @"(?xi) -{10}  [\s\r\n]+  System \s pid").Success)
                    {
                        if (Regex.Match(m.Value ?? "", @"(?xi)  \) \s+ \\clfs \s* (\r|\n)").Success)
                        {
                            systemProcessFiles = m.Value.ToLower();
                            break;
                        }
                    }
                }

            }

            dateFilesLockedInfo = DateTime.Now;

            return systemProcessFiles;
        }
    }

    public static bool IsVolumeMounted(string volumeLocation)
    {
        //DriveInfo d = new System.IO.DriveInfo(volume.DriveLetter);
        //if (d == null)
        //return false;

        //if (d.DriveType != System.IO.DriveType.Fixed)
        //return false;

        //if ((d.DriveFormat ?? "").ToLower().Contains("fat") == false)
        //return false;

        if (SystemProcessFiles.Contains(volumeLocation.ToLower()))
        {
            return true;
        }
        else
        {
            return false;
        }

    }
}

then if I want to know if the volume located at C:\Users\Tono\Desktop\v1.tc is mounted I will call the method as:

var isVolMounted = TrueCryptHelp.IsVolumeMounted(@"A:\Users\Tono\Desktop\v1.tc");

Now I am missing to answer the question! With the class I posted I am able to know that the volume located at C:\Users\etc... is mounted but into what drive letter!?




回答3:


just to extend the answer to VeraCrypt, modified from Gerard Walace's post:

TrueCrypt and VeraCrypt usage in MSFT_Helpers.cs @ https://github.com/BananaAcid/Selfcontained-C-Sharp-WPF-compatible-utility-classes

Simple usage example here: http://github.com/BananaAcid/VeraCrypt-Cmd

public static class VcGetMounts
{

    public static async Task<Dictionary<char, string>> getMounted()
    {
        return await Task.Run<Dictionary<char, string>>(() =>
        {
            var ret = new Dictionary<char, string>();

            uint size = (uint)Marshal.SizeOf(typeof(MOUNT_LIST_STRUCT));
            IntPtr buffer = Marshal.AllocHGlobal((int)size);
            uint bytesReturned;
            IntPtr _hdev = CreateFile("\\\\.\\VeraCrypt", FileAccess.ReadWrite, FileShare.ReadWrite, IntPtr.Zero, FileMode.Open, 0, IntPtr.Zero);
            bool bResult = DeviceIoControl(_hdev, TC_IOCTL_GET_MOUNTED_VOLUMES, buffer, size, buffer, size, out bytesReturned, IntPtr.Zero);
            // IMPORTANT! Otherwise, the struct fills up with random bytes from memory, if no VeraCrypt is available
            if (!bResult) return ret;
            MOUNT_LIST_STRUCT mount = new MOUNT_LIST_STRUCT();
            Marshal.PtrToStructure(buffer, mount);
            Marshal.FreeHGlobal(buffer);

            for (int i = 0; i < 26; i++)
                if (mount.wszVolume[i].ToString().Length > 0)
                    ret.Add((char)('A' + i), mount.wszVolume[i].ToString());

            return ret;
        });
    }

    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)]
    class MOUNT_LIST_STRUCT
    {
        public readonly UInt32 ulMountedDrives; /* Bitfield of all mounted drive letters */
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 26)]
        public readonly MOUNT_LIST_STRUCT_VOLUME_NAME[] wszVolume;  /* Volume names of mounted volumes */
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 26)]
        public readonly MOUNT_LIST_STRUCT_VOLUME_LABEL[] wszLabel;  /* Volume labels of mounted volumes */
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 26)]
        public readonly MOUNT_LIST_STRUCT_VOLUME_ID[] volumeID;  /* Volume labels of mounted volumes */
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 26)]
        public readonly UInt64[] diskLength;
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 26)]
        public readonly int[] ea;
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 26)]
        public readonly int[] volumeType;   /* Volume type (e.g. PROP_VOL_TYPE_OUTER, PROP_VOL_TYPE_OUTER_VOL_WRITE_PREVENTED, etc.) */
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 26)]
        public readonly bool[] truecryptMode;
    }

    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)]
    struct MOUNT_LIST_STRUCT_VOLUME_NAME
    {
        [MarshalAs(UnmanagedType.ByValArray, ArraySubType = UnmanagedType.I2, SizeConst = 260)]
        public readonly char[] wszVolume;   /* Volume names of mounted volumes */

        public override string ToString()
        {
            return (new String(wszVolume)).TrimEnd('\0');
        }
    }

    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)]
    struct MOUNT_LIST_STRUCT_VOLUME_ID
    {
        [MarshalAs(UnmanagedType.ByValArray, ArraySubType = UnmanagedType.I2, SizeConst = 32)]
        public readonly char[] volumeID;   /* Volume ids of mounted volumes */

        public override string ToString()
        {
            return (new String(volumeID)).TrimEnd('\0');
        }
    }

    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)]
    struct MOUNT_LIST_STRUCT_VOLUME_LABEL
    {
        [MarshalAs(UnmanagedType.ByValArray, ArraySubType = UnmanagedType.I2, SizeConst = 33)]
        public readonly char[] wszLabel;   /* Volume labels of mounted volumes */

        public override string ToString()
        {
            return (new String(wszLabel)).TrimEnd('\0');
        }
    }

    public static int CTL_CODE(int DeviceType, int Function, int Method, int Access)
    {
        return (((DeviceType) << 16) | ((Access) << 14) | ((Function) << 2)
          | (Method));
    }
    private static readonly uint TC_IOCTL_GET_MOUNTED_VOLUMES = (uint)CTL_CODE(0x00000022, 0x800 + (6), 0, 0);

    [DllImport("kernel32.dll", ExactSpelling = true, SetLastError = true, CharSet = CharSet.Auto)]
    static extern bool DeviceIoControl(IntPtr hDevice, uint dwIoControlCode,
    IntPtr lpInBuffer, uint nInBufferSize,
    IntPtr lpOutBuffer, uint nOutBufferSize,
    out uint lpBytesReturned, IntPtr lpOverlapped);

    [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    public static extern IntPtr CreateFile(
         [MarshalAs(UnmanagedType.LPTStr)] string filename,
         [MarshalAs(UnmanagedType.U4)] FileAccess access,
         [MarshalAs(UnmanagedType.U4)] FileShare share,
         IntPtr securityAttributes, // optional SECURITY_ATTRIBUTES struct or IntPtr.Zero
         [MarshalAs(UnmanagedType.U4)] FileMode creationDisposition,
         [MarshalAs(UnmanagedType.U4)] FileAttributes flagsAndAttributes,
         IntPtr templateFile);
}



回答4:


I'm unsure about determining volume name by querying the drive letter or visa-versa. A workaround that may be acceptable depending on your needs is to query for file locks on the volume. If the volume is locked, chances are good its mounted. You could get more fancy if you wanted and check that it's locked by the System process specifically, using for /f.

I should mentioned that using /quit background in conjunction with /silent when calling truecrypt.exe will just fail silently if the drive is already mounted. Not sure if it's just error display you're concerned with. Not sure if you've noticed the command line reference.

Here is a simple cmd script to mount a volume only if it's not already mounted, thereby preventing errors being triggered by truecrypt.exe. It makes a number of naive assumptions, but should give an idea.

@echo off
setlocal enableextensions enabledelayedexpansion

:: Dependencies: Truecrypt obviously, and sysinternals handles.exe (live.sysinternals.com).

:: To use command line arguments instead of static assignments like these, reference %1 %2 from within script..
set driveletter=x
set yourvolume=c:\temp\your_volume.vol
set yourpassword=your_password
set truecrypt_loc=c:\program files\truecrypt\truecrypt.exe
set handle_loc=c:\temp\handle.exe

:: - check if volume already mounted
"%handle_loc%" -a "%yourvolume%" /accepteula >nul
if %errorlevel% EQU 0 echo This volume is already mounted. && goto :EOF

:: - check if drive letter is in use
if exist %driveletter% echo This drive is already mounted. && goto :EOF

:: - mount tc volume to a specified drive letter
:: silent flag suppresses errors being displayed to user
"%truecrypt_loc%" /letter %driveletter% /password "%yourpassword%" /volume "%yourvolume%" /mountoption rm /quit background /silent

:: - check if drive letter is in use
if exist %driveletter%:\ echo Drive was mounted successfully as %driveletter%: && goto :EOF
echo Drive could not be mounted.

Note that due to what may be a bug in some versions of TrueCrypt, unmounting from the GUI causes the drive letter to be unavailable on future mounts. One workaround is to unmount from the command line using: truecrypt.exe /q /dx (where x is the drive letter).




回答5:


I may be oversimplifying, but are the drive labels on the mounted drives unique? If so you could use a simple lookup such as:

private string GetDriveLetter(string volumeLabel)
{
    string driveLetter = "";

    DriveInfo[] dis = DriveInfo.GetDrives();
    foreach (DriveInfo di in dis)
    {
        var dt = di.DriveType;
        if (dt == DriveType.Fixed || dt == DriveType.Removable)
        {
            if (di.VolumeLabel == volumeLabel)
            {
                driveLetter = di.Name.Substring(0, 1).ToUpper();
                break;
            }
        }
    }

    return driveLetter;
}



回答6:


Just to make sure I've got this right: you want to make sure that TrueCrypt mounts a certain volume to a certain drive letter via a script, and that it will not attempt to do anything if the volume is already mounted.

Maybe Several Potential Solutions. I'll try to highlight a few options below; let me know if any are worth pursuing further and I'll continue to research and update with more details.

Option 1: Favorites and Auto-Mount

TrueCrypt supports "favorite volumes".

From the page -- Favorite volumes are used when:

  • You have a volume that always needs to be mounted to a particular drive letter.
  • You have a volume that needs to be automatically mounted when its host device gets connected to the computer (for example, a container located on a USB flash drive or external USB hard drive).
  • You have a volume that needs to be automatically mounted when you log on to the operating system.
  • You have a volume that always needs to be mounted as read-only or removable medium.

Some things worth noting:

  • A special label can be applied to each favorite volume -- could be handy
  • You could run "TrueCrypt.exe /a favorites /quit" to auto-mount your favorite volumes and then quit. You may want to test this, as the truecrypt favorites page notes that If it is already mounted, an Explorer window is opened for it.

Option 2: Check Drive Letter Only

If you always mount the volume to a certain drive letter, you may be able to make a reasonable assumption that nothing else is going to be sitting on that drive letter.

If that's a reasonable assumption, you can always just check the drive with PowerShell:

$DriveLetterToCheck = "s:"
$DriveLetterMounted = Test-Path $DriveLetterToCheck #true if it exists
if(!$DriveLetterMounted)
{
  # Run your TrueCrypt mount command
}

I'll update if I think of something better. Let me know if I'm on the right track.



来源:https://stackoverflow.com/questions/16753345/determine-the-drive-letter-of-a-mounted-truecrypt-volume

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!