Retired Microsoft Blog disclaimer

This directory is a mirror of retired "Decrypt My World" MSDN blog and is provided as is. All posting authorship and copyrights belong to respective authors.

Posts on this page:

Original URL: https://blogs.msdn.microsoft.com/alejacma/2010/03/17/how-to-change-drive-letters-vbscript/
Post name: How to change drive letters (VBScript)
Original author: Alejandro Campos Magencio
Posting date: 2010-03-17T03:17:00+00:00


Hi all,


Imagine you need to map some shared folders to specific drive letters for all users in your domain, so some internal apps your company needs work fine. Imagine your users connected i.e. USBdevices to their systems, so the drive lettersthose apps need are in use when you are going to map them.


The following VBScript sample accepts a list of forbidden drive letters, and it will rename all the drive letters of the system in that list to the next available letter.

Option Explicit

'************************************************************************
' PARAMETERS
'************************************************************************

' Reserved drives list
'
Dim arrReservedDrives
arrReservedDrives = Array("E:", "F:", "H:", "Y:", "Z:")

wscript.echo "Reserved drives:"
ShowArray arrReservedDrives

'************************************************************************
' MAIN
'************************************************************************

Dim objWMIService, objDrive
Dim colDrives
Dim arrUsedDrives, arrForbiddenDrives
Dim strComputer, strDrive, strNewDrive, strCurrentDrive
Dim i

' Get all drives currently in use
'
strComputer = "."
Set objWMIService = GetObject("winmgmts:" _
& "{impersonationLevel=impersonate}!\\" & strComputer & "\root\cimv2")

Set colDrives = objWMIService.ExecQuery _
("Select * from Win32_LogicalDisk")

ReDim arrUsedDrives(colDrives.Count - 1)
i = 0
For Each objDrive in colDrives
arrUsedDrives(i) = objDrive.DeviceID
i = i + 1
Next

wscript.echo "Used drives:"
ShowArray arrUsedDrives

' Create a list which contains all drives in use + all reserved drives.
' Drives in this list cannot be used at all.
' Note I don't care about duplicated values in this list
'
arrForbiddenDrives = JoinArrays(arrUsedDrives, arrReservedDrives)

' Check each drive currently in use
'
For Each strDrive in arrUsedDrives
If ArrayContains(arrReservedDrives, strDrive) Then

' We found a drive that cannot be used
'
Wscript.echo strDrive & " is in use, and it shouldn't"

' Find next available drive
'
strNewDrive = ""
For i = 68 to 90 ' From 'D' to 'Z'
strCurrentDrive = CStr(Chr(i)) & ":"
If (Not ArrayContains(arrForbiddenDrives, strCurrentDrive)) Then
' We found it
'
strNewDrive = strCurrentDrive
Exit For
End If
Next

If strNewDrive = "" Then
' There are no more available drives!
'
Wscript.echo "Error: There are no more available drives in the system!!!!"
Exit For
End If

' Change drive that cannot be used to the available drive we found
'
wscript.echo "Changing " & strDrive & " to " & strNewDrive
ChangeDriveLetterWithMountvol strDrive, strNewDrive
wscript.echo

' Add the new drive to the list of forbidden drives
'
AddToArray arrForbiddenDrives, strNewDrive
End If
Next

' The end
'
wscript.echo "We are done!"

'************************************************************************
' HELPER FUNCTIONS
'************************************************************************

' Change the drive in one drive letter to another drive letter using
' mountvol.exe tool
'
Sub ChangeDriveLetterWithMountvol(strSourceDrive, strTargetDrive)

Dim objShell, objExec
Dim strVolume

Set objShell = WScript.CreateObject("WScript.Shell")

' Get volume associated to the old drive letter.
'
Set objExec = objShell.Exec("mountvol " & strSourceDrive & " /L")
strVolume = Trim(objExec.StdOut.ReadLine())
while objExec.Status = 0
WScript.Sleep(100)
Wend

' Unmount the drive.
'
Set objExec = objShell.Exec("mountvol " & strSourceDrive & " /D")
while objExec.Status = 0
WScript.Sleep(100)
Wend

' Mount the drive on the new drive letter.
'
Set objExec = objShell.Exec("mountvol " & strTargetDrive & " " & strVolume)
while objExec.Status = 0
WScript.Sleep(100)
Wend

End Sub

' Join two arrays
'
Function JoinArrays(arrA, arrB)

Dim i, a, b

ReDim arrNew(UBound(arrA) + UBound(arrB) + 1)

i = 0
For a = 0 to UBound(arrA)
arrNew(i) = arrA(a)
i = i + 1
Next

For b = 0 to UBound(arrB)
arrNew(i) = arrB(b)
i = i + 1
Next

JoinArrays = arrNew

End Function

' Looks for a value in an array
'
Function ArrayContains(arrStrings, strValue)

Dim i

ArrayContains = false
For i = 0 to UBound(arrStrings)
If arrStrings(i) = strValue Then
ArrayContains = true
Exit For
End If
Next

End Function

' Adds a value to an array
'
Function AddToArray(arrStrings, strNewValue)

ReDim Preserve arrStrings(UBound(arrStrings) + 1)
arrStrings(UBound(arrStrings)) = strNewValue
AddToArray = arrStrings

End Function

' Shows contents of an array of strings
'
Sub ShowArray(arrStrings)

Dim str

For Each str in arrStrings
wscript.echo str
Next
wscript.echo

End Sub


I hope this helps.


Regards,



Alex (Alejandro Campos Magencio)

Original URL: https://blogs.msdn.microsoft.com/alejacma/2010/02/17/signersignex-returns-error-0x80070020/
Post name: SignerSignEx returns error 0x80070020
Original author: Alejandro Campos Magencio
Posting date: 2010-02-17T07:40:00+00:00


Hi all,


A customer of mine used my code in How to sign EXE files with an Authenticode certificate (part 2) and followed my recommendations in SignerSignEx returns error 0x800b0003to be able to sign all type of files (EXEs, DLLs, OCXs, MSIs, etc.)with SignerSignEx. This worked fine.


But when passing a valid time stamp server URL to the API's pwszHttpTimeStamp parameterin order to time stamp the signature, he got the following error when signing i.e. EXEs: 0x80070020 / -2147024864 / ERROR_SHARING_VIOLATION / "The process cannot access the file because it is being used by another process".


Note he didn't get the error when signing i.e. MSIs. Also note that after the API failed, the file was correctly signed as we could see with i.e. Explorer, but signature was not time stamped as expected.


Fortunatelly we could time stamp the signatures of all file types by doing the following: first, call SignerSignEx without passing time stamp server URL, so it signs all type of files without returning the error above. Immediately after that, call SignerTimeStampEx to time stamp the signature. The following sample shows how to achieve this:

#include "windows.h"
#include "Wincrypt.h"
#include "stdio.h"
#include "conio.h"

// STRUCTS

typedef struct _SIGNER_FILE_INFO {
DWORD cbSize;
LPCWSTR pwszFileName;
HANDLE hFile;
} SIGNER_FILE_INFO, *PSIGNER_FILE_INFO;

typedef struct _SIGNER_BLOB_INFO {
DWORD cbSize;
GUID *pGuidSubject;
DWORD cbBlob;
BYTE *pbBlob;
LPCWSTR pwszDisplayName;
} SIGNER_BLOB_INFO, *PSIGNER_BLOB_INFO;

typedef struct _SIGNER_SUBJECT_INFO {
DWORD cbSize;
DWORD *pdwIndex;
DWORD dwSubjectChoice;
union {
SIGNER_FILE_INFO *pSignerFileInfo;
SIGNER_BLOB_INFO *pSignerBlobInfo;
} ;
} SIGNER_SUBJECT_INFO, *PSIGNER_SUBJECT_INFO;

typedef struct _SIGNER_CERT_STORE_INFO {
DWORD cbSize;
PCCERT_CONTEXT pSigningCert;
DWORD dwCertPolicy;
HCERTSTORE hCertStore;
} SIGNER_CERT_STORE_INFO, *PSIGNER_CERT_STORE_INFO;

typedef struct _SIGNER_SPC_CHAIN_INFO {
DWORD cbSize;
LPCWSTR pwszSpcFile;
DWORD dwCertPolicy;
HCERTSTORE hCertStore;
} SIGNER_SPC_CHAIN_INFO, *PSIGNER_SPC_CHAIN_INFO;

typedef struct _SIGNER_CERT {
DWORD cbSize;
DWORD dwCertChoice;
union {
LPCWSTR pwszSpcFile;
SIGNER_CERT_STORE_INFO *pCertStoreInfo;
SIGNER_SPC_CHAIN_INFO *pSpcChainInfo;
} ;
HWND hwnd;
} SIGNER_CERT, *PSIGNER_CERT;

typedef struct _SIGNER_ATTR_AUTHCODE {
DWORD cbSize;
BOOL fCommercial;
BOOL fIndividual;
LPCWSTR pwszName;
LPCWSTR pwszInfo;
} SIGNER_ATTR_AUTHCODE, *PSIGNER_ATTR_AUTHCODE;

typedef struct _SIGNER_SIGNATURE_INFO {
DWORD cbSize;
ALG_ID algidHash;
DWORD dwAttrChoice;
union {
SIGNER_ATTR_AUTHCODE *pAttrAuthcode;
} ;
PCRYPT_ATTRIBUTES psAuthenticated;
PCRYPT_ATTRIBUTES psUnauthenticated;
} SIGNER_SIGNATURE_INFO, *PSIGNER_SIGNATURE_INFO;

typedef struct _SIGNER_PROVIDER_INFO {
DWORD cbSize;
LPCWSTR pwszProviderName;
DWORD dwProviderType;
DWORD dwKeySpec;
DWORD dwPvkChoice;
union {
LPWSTR pwszPvkFileName;
LPWSTR pwszKeyContainer;
} ;
} SIGNER_PROVIDER_INFO, *PSIGNER_PROVIDER_INFO;

typedef struct _SIGNER_CONTEXT {
DWORD cbSize;
DWORD cbBlob;
BYTE *pbBlob;
} SIGNER_CONTEXT, *PSIGNER_CONTEXT;

// EXPORTS

typedef HRESULT (WINAPI* SignerFreeSignerContextType)(
__in SIGNER_CONTEXT *pSignerContext
);

typedef HRESULT (WINAPI *SignerSignExType)(
__in DWORD dwFlags,
__in SIGNER_SUBJECT_INFO *pSubjectInfo,
__in SIGNER_CERT *pSignerCert,
__in SIGNER_SIGNATURE_INFO *pSignatureInfo,
__in_opt SIGNER_PROVIDER_INFO *pProviderInfo,
__in_opt LPCWSTR pwszHttpTimeStamp,
__in_opt PCRYPT_ATTRIBUTES psRequest,
__in_opt LPVOID pSipData,
__out SIGNER_CONTEXT **ppSignerContext
);

typedef HRESULT (WINAPI *SignerTimeStampExType)(
__reserved DWORD dwFlags,
__in SIGNER_SUBJECT_INFO *pSubjectInfo,
__in LPCWSTR pwszHttpTimeStamp,
__in PCRYPT_ATTRIBUTES psRequest,
__in LPVOID pSipData,
__out SIGNER_CONTEXT **ppSignerContext
);

// MAIN

void main()
{
// PARAMETERS

// File to sign
LPCWSTR pwszFileName = L"C:\\PathToFile\\FileToSign.msi";

// Signing Cert Subject
LPCWSTR pwszCertSubject = L"My cert Subject";

// VARIABLES
HRESULT hResult = S_OK;
BOOL bResult = TRUE;
HMODULE hMssign32 = NULL;
SignerSignExType pfSignerSignEx = NULL;
SignerTimeStampExType pfSignerTimeStampEx = NULL;
SignerFreeSignerContextType pfSignerFreeSignerContext = NULL;
HCERTSTORE hCertStore = NULL;
PCCERT_CONTEXT pCertContext = NULL;
DWORD dwIndex = 0;
SIGNER_FILE_INFO signerFileInfo;
SIGNER_SUBJECT_INFO signerSubjectInfo;
SIGNER_CERT_STORE_INFO signerCertStoreInfo;
SIGNER_CERT signerCert;
SIGNER_SIGNATURE_INFO signerSignatureInfo;
SIGNER_CONTEXT * pSignerContext = NULL;

// MAIN

// Attach a debugger now!
printf("<< Press any key to continue>>\n");
_getch();

// Load library containing SignerSignEx and SignerFreeSignerContext
printf("LoadLibrary...");
hMssign32 = LoadLibrary(L"Mssign32.dll");
if (!hMssign32)
{
printf("Error #%d\n", GetLastError()); goto cleanup;
}
printf("Done!\n");

// Get SignerSignEx function
printf("GetProcAddress(SignerSignEx)...");
pfSignerSignEx = (SignerSignExType) GetProcAddress(hMssign32, "SignerSignEx");
if (!pfSignerSignEx)
{
printf("Error #%d\n", GetLastError()); goto cleanup;
}
printf("Done!\n");

// Get SignerTimeStampEx function
printf("GetProcAddress(SignerTimeStampEx)...");
pfSignerTimeStampEx = (SignerTimeStampExType) GetProcAddress(hMssign32, "SignerTimeStampEx");
if (!pfSignerTimeStampEx)
{
printf("Error #%d\n", GetLastError()); goto cleanup;
}
printf("Done!\n");

// Get SignerFreeSignerContext function
printf("GetProcAddress(SignerFreeSignerContext)...");
pfSignerFreeSignerContext = (SignerFreeSignerContextType) GetProcAddress(hMssign32, "SignerFreeSignerContext");
if (!pfSignerFreeSignerContext)
{
printf("Error #%d\n", GetLastError()); goto cleanup;
}
printf("Done!\n");

// Open MY cert store
printf("CertOpenStore...");
hCertStore = CertOpenStore(
CERT_STORE_PROV_SYSTEM,
0,
NULL,
CERT_SYSTEM_STORE_CURRENT_USER,
L"MY"
);
if (!hCertStore)
{
printf("Error #%d\n", GetLastError()); goto cleanup;
}
printf("Done!\n");

// Find signing cert in MY cert store
printf("CertFindCertificateInStore...");
pCertContext = CertFindCertificateInStore(
hCertStore,
X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
0,
CERT_FIND_SUBJECT_STR,
(void *)pwszCertSubject,
NULL
);
if (!pCertContext)
{
printf("Error #%d\n", GetLastError()); goto cleanup;
}
printf("Done!\n");

// Prepare SIGNER_FILE_INFO struct
signerFileInfo.cbSize = sizeof(SIGNER_FILE_INFO);
signerFileInfo.pwszFileName = pwszFileName;
signerFileInfo.hFile = NULL;

// Prepare SIGNER_SUBJECT_INFO struct
signerSubjectInfo.cbSize = sizeof(SIGNER_SUBJECT_INFO);
dwIndex = 0;
signerSubjectInfo.pdwIndex = &dwIndex;
signerSubjectInfo.dwSubjectChoice = 1; // SIGNER_SUBJECT_FILE
signerSubjectInfo.pSignerFileInfo = &signerFileInfo;

// Prepare SIGNER_CERT_STORE_INFO struct
signerCertStoreInfo.cbSize = sizeof(SIGNER_CERT_STORE_INFO);
signerCertStoreInfo.pSigningCert = pCertContext;
signerCertStoreInfo.dwCertPolicy = 2; // SIGNER_CERT_POLICY_CHAIN
signerCertStoreInfo.hCertStore = NULL;

// Prepare SIGNER_CERT struct
signerCert.cbSize = sizeof(SIGNER_CERT);
signerCert.dwCertChoice = 2; // SIGNER_CERT_STORE
signerCert.pCertStoreInfo = &signerCertStoreInfo;
signerCert.hwnd = NULL;

// Prepare SIGNER_SIGNATURE_INFO struct
signerSignatureInfo.cbSize = sizeof(SIGNER_SIGNATURE_INFO);
signerSignatureInfo.algidHash = CALG_SHA1;
signerSignatureInfo.dwAttrChoice = 0; // SIGNER_NO_ATTR
signerSignatureInfo.pAttrAuthcode = NULL;
signerSignatureInfo.psAuthenticated = NULL;
signerSignatureInfo.psUnauthenticated = NULL;

// Sign file with cert
printf("SignerSignEx...");
hResult = pfSignerSignEx(
0,
&signerSubjectInfo,
&signerCert,
&signerSignatureInfo,
NULL,
NULL,
NULL,
NULL,
&pSignerContext
);
if (S_OK != hResult)
{
printf("Error #%d\n", hResult); goto cleanup;
}
printf("Done!\n");

// Time stamp the signature
printf("SignerTimeStampEx...");
hResult = pfSignerTimeStampEx(
0,
&signerSubjectInfo,
L"http://timestamp.globalsign.com/scripts/timstamp.dll",
NULL,
NULL,
&pSignerContext
);
if (S_OK != hResult)
{
printf("Error #%d\n", hResult); goto cleanup;
}
printf("Done!\n");

printf("\nSUCCESS!!!\n");

// Clean up
cleanup:

if (pSignerContext)
{
hResult = pfSignerFreeSignerContext(pSignerContext);
}

if (pCertContext)
{
bResult = CertFreeCertificateContext(pCertContext);
}

if (hCertStore)
{
bResult = CertCloseStore(hCertStore, CERT_CLOSE_STORE_CHECK_FLAG);
}

if (hMssign32)
{
bResult = FreeLibrary(hMssign32);
}

// Exit
printf("<< Press any key to exit >>\n");
_getch();
return;
}


Note that this sample also deals with error 0x800b0003 commented at the beginning of this post.


Regards,



Alex (Alejandro Campos Magencio)

Original URL: https://blogs.msdn.microsoft.com/alejacma/2010/02/17/signersignex-returns-error-0x800b0003/
Post name: SignerSignEx returns error 0x800b0003
Original author: Alejandro Campos Magencio
Posting date: 2010-02-17T07:19:00+00:00


Hi all,


A customer of mine tried my SignerSignEx sample in How to sign EXE files with an Authenticode certificate (part 2), and it worked just fine when signing EXEs, DLLs, OCXs, CABs... but not when signing certain MSI files. When signing those type of files, he got error 0x800b0003/ -2146762749 / TRUST_E_SUBJECT_FORM_UNKNOWN / "The form specified for the subject is not one supported orknown by the specified trust provider".


Check this piece of code from my sample:

// Prepare SIGNER_FILE_INFO struct
signerFileInfo.cbSize = sizeof(SIGNER_FILE_INFO);
signerFileInfo.pwszFileName = pwszFileName;
signerFileInfo.hFile = hFile;


When we provide a valid file handle to the API, SignerSignEx ignores the file name passed to pwszFileName and uses the handle passed to hFile.


SIGNER_FILE_INFO Structure
"
hFile
An open handle to the file specified by the pwszFileName member. If this member contains a valid handle, this handle is used to access the file. This member can be set to NULL.
"


The API then uses the handle to find out i.e. the file type that it's dealing with. PE files (EXE , DLL, OCX...), CABs and Catalog/CTLs are checked in a very specific way. All other files are checked differently, including MSIs. For the problematic MSIs, I've seen that the API is just not able to find out which type they are, and it returns the error we saw above.


If we forget about the file handle (and get rid of the CreateFile call that I used in my sample to get the handle) and just pass the file name to the API, it should be able to sign all type of files, including problematic MSIs:

// Prepare SIGNER_FILE_INFO struct
signerFileInfo.cbSize = sizeof(SIGNER_FILE_INFO);
signerFileInfo.pwszFileName = pwszFileName;
signerFileInfo.hFile = NULL;


I hope this helps.


Cheers,



Alex (Alejandro Campos Magencio)

Original URL: https://blogs.msdn.microsoft.com/alejacma/2010/01/13/how-to-get-all-dcs-in-a-forest-vbscript/
Post name: How to get all DCs in a Forest (VBScript)
Original author: Alejandro Campos Magencio
Posting date: 2010-01-13T07:04:00+00:00


Hi all,


The following VBScript sample lists all DCs in a Forest:

' Create log file
'
Set objFSO = CreateObject("Scripting.FileSystemObject")
Set objLog = objFSO.CreateTextFile ("log.txt")

' Get Forest's root
'
Set objRoot = GetObject("LDAP://rootDSE")

' Get root's Configuration
'
Set objConfig = GetObject("LDAP://" & objRoot.Get("ConfigurationNamingContext"))

' Search for the Partitions container in root's Configuration
'
objConfig.Filter = Array("crossRefContainer")
For Each objPartition in objConfig
strPartition = "LDAP://" & objPartition.Get("distinguishedName")
Next

' Search in Partitions for all domains in Forest
'
Set objConnection = CreateObject("ADODB.Connection")
objConnection.Open "Provider=ADsDSOObject;"

Set objCommand = CreateObject("ADODB.Command")
objCommand.ActiveConnection = objConnection
objCommand.Properties("Page Size") = 1000

objCommand.CommandText = "<" & strPartition & ">;(&(systemFlags=3));nCName,systemFlags;subTree"
Set objRecordset = objCommand.Execute

' List all domains in Forest
'
Do While Not objRecordset.EOF
' List all DCs in one domain
'
objLog.WriteLine("=================================================")
objLog.WriteLine(objRecordset.Fields(0))
objLog.WriteLine("=================================================")
Set objDCs = GetObject("GC://OU=Domain Controllers," & objRecordset.Fields(0))
For Each objDC in objDCs
objLog.WriteLine(objDC.Get("Name"))
Next
objLog.WriteLine("-------------------------------------------------")

objRecordset.MoveNext
Loop

' We are done!
'
objLog.Close
MsgBox "The End"



I hope this helps.


Regards,



Alex (Alejandro Campos Magencio)

Original URL: https://blogs.msdn.microsoft.com/alejacma/2010/01/13/how-to-set-sound-volume-programmatically/
Post name: How to set sound volume programmatically
Original author: Alejandro Campos Magencio
Posting date: 2010-01-13T03:43:00+00:00


Hi all,

From time to time I like to post aboutother issues which are not related tocrypto stuff, but I consider useful because of thenumber of request I get on them. And this is one commonquestion I've seen: how do I set sound volume programmatically on Windows?

On Windows XP we can manipulate audio settings with Wave API or Dsound API (Direct Sound). The following function may help here: waveOutSetVolume.Here is the rest of the API: Waveform Functions. And herewe can find a .NET sample which mixes .NET classes and that API: Using P/Invoke to Call Unmanaged APIs from Your Managed Classes. Another sample, this time from a third-party: Mute volume in vb.net program.

On Windows Vista and later that API is no longer available. We may use MMDevice API and EndpointVolume API instead. Here you can see some samples: How do I change the master volume in Windows Vista & Vista Core Audio API Master Volume Control.

I hope this helps.

Regards,

Alex (Alejandro Campos Magencio)