Windows driver kernel: How enumerate all subdirectories and files? - c++

I'm working in a small antirootkit and i need add a functionality that is:
Delete all files on directory of rootkit and in yours possible subdiretories.
So, firstly is necessary know all these directories and files, right?
To this, i have code below that already make half of this task. He enumerate all directories and files of a specific directory but not "see" subdirectories ( files and folders).
Eg:
Output:
Code:
#include <ntifs.h>
typedef unsigned int UINT;
NTSTATUS EnumFilesInDir()
{
HANDLE hFile = NULL;
UNICODE_STRING szFileName = { 0 };
OBJECT_ATTRIBUTES Oa = { 0 };
NTSTATUS ntStatus = 0;
IO_STATUS_BLOCK Iosb = { 0 };
UINT uSize = sizeof(FILE_BOTH_DIR_INFORMATION);
FILE_BOTH_DIR_INFORMATION *pfbInfo = NULL;
BOOLEAN bIsStarted = TRUE;
RtlInitUnicodeString(&szFileName, L"\\??\\C:\\MyDirectory");
InitializeObjectAttributes(&Oa, &szFileName, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL);
ntStatus = ZwCreateFile(&hFile, GENERIC_READ | SYNCHRONIZE, &Oa, &Iosb, 0, FILE_ATTRIBUTE_NORMAL, FILE_SHARE_READ, FILE_OPEN, FILE_SYNCHRONOUS_IO_NONALERT, NULL, 0);
if (!NT_SUCCESS(ntStatus)) { return ntStatus; }
pfbInfo = ExAllocatePoolWithTag(PagedPool, uSize, '0000');
if (pfbInfo == NULL)
{
ZwClose(hFile); return STATUS_NO_MEMORY;
}
while (TRUE)
{
lbl_retry:
RtlZeroMemory(pfbInfo, uSize);
ntStatus = ZwQueryDirectoryFile(hFile, 0, NULL, NULL, &Iosb, pfbInfo, uSize, FileBothDirectoryInformation, FALSE, NULL, bIsStarted);
if (STATUS_BUFFER_OVERFLOW == ntStatus) {
ExFreePoolWithTag(pfbInfo, '000');
uSize = uSize * 2;
pfbInfo = ExAllocatePoolWithTag(PagedPool, uSize, '0000');
if (pfbInfo == NULL) { ZwClose(hFile); return STATUS_NO_MEMORY; }
goto lbl_retry;
}
else if (STATUS_NO_MORE_FILES == ntStatus)
{
ExFreePoolWithTag(pfbInfo, '000');
ZwClose(hFile); return STATUS_SUCCESS;
}
else if (STATUS_SUCCESS != ntStatus)
{
ExFreePoolWithTag(pfbInfo, '000');
ZwClose(hFile);
return ntStatus;
}
if (bIsStarted)
{
bIsStarted = FALSE;
}
while (TRUE)
{
WCHAR * szWellFormedFileName = ExAllocatePoolWithTag(PagedPool, (pfbInfo->FileNameLength + sizeof(WCHAR)), '0001');
if (szWellFormedFileName)
{
RtlZeroMemory(szWellFormedFileName, (pfbInfo->FileNameLength + sizeof(WCHAR)));
RtlCopyMemory(szWellFormedFileName, pfbInfo->FileName, pfbInfo->FileNameLength);
//KdPrint(("File name is: %S\n", szWellFormedFileName));
KdPrint((" %S\n", szWellFormedFileName));
ExFreePoolWithTag(szWellFormedFileName, '000');
}
if (pfbInfo->NextEntryOffset == 0) { break; }
pfbInfo += pfbInfo->NextEntryOffset;
}
}
ZwClose(hFile);
ExFreePoolWithTag(pfbInfo, '000');
return ntStatus;
}
So, how do this?
Thank in advance by any help or suggestion.
--------------------------------------------------------EDIT:--------------------------------------------------------------------
I found a possible solution, but i'm getting a BSOD in this line:
if ( (*pDir)->NextEntryOffset)
In KernelFindNextFile method.
Some suggestion?
Here is the code that i found:
#include <ntifs.h>
#include <stdlib.h>
HANDLE KernelCreateFile(IN PUNICODE_STRING pstrFile,IN BOOLEAN bIsDir)
{
HANDLE hFile = NULL;
NTSTATUS Status = STATUS_UNSUCCESSFUL;
IO_STATUS_BLOCK StatusBlock = {0};
ULONG ulShareAccess = FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE;
ULONG ulCreateOpt = FILE_SYNCHRONOUS_IO_NONALERT;
OBJECT_ATTRIBUTES objAttrib = {0};
ULONG ulAttributes = OBJ_CASE_INSENSITIVE|OBJ_KERNEL_HANDLE;
InitializeObjectAttributes(&objAttrib,pstrFile,ulAttributes,NULL,NULL);
ulCreateOpt |= bIsDir?FILE_DIRECTORY_FILE:FILE_NON_DIRECTORY_FILE;
Status = ZwCreateFile(
&hFile,
GENERIC_ALL,
&objAttrib,
&StatusBlock,
0,
FILE_ATTRIBUTE_NORMAL,
ulShareAccess,
FILE_OPEN_IF,
ulCreateOpt,
NULL,
0);
if (!NT_SUCCESS(Status))
{
return (HANDLE)-1;
}
return hFile;
}
PFILE_BOTH_DIR_INFORMATION KernelFindFirstFile(IN HANDLE hFile,IN ULONG ulLen,OUT PFILE_BOTH_DIR_INFORMATION pDir)
{
NTSTATUS Status = STATUS_UNSUCCESSFUL;
IO_STATUS_BLOCK StatusBlock = {0};
PFILE_BOTH_DIR_INFORMATION pFileList = (PFILE_BOTH_DIR_INFORMATION)ExAllocatePool(PagedPool,ulLen);
Status = ZwQueryDirectoryFile(
hFile,NULL,NULL,NULL,
&StatusBlock,
pDir,
ulLen,
FileBothDirectoryInformation,
TRUE,
NULL,
FALSE);
RtlCopyMemory(pFileList,pDir,ulLen);
Status = ZwQueryDirectoryFile(
hFile,NULL,NULL,NULL,
&StatusBlock,
pFileList,
ulLen,
FileBothDirectoryInformation,
FALSE,
NULL,
FALSE);
return pFileList;
}
NTSTATUS KernelFindNextFile(IN OUT PFILE_BOTH_DIR_INFORMATION* pDir)
{
if ( (*pDir)->NextEntryOffset)
{
(*pDir)=(PFILE_BOTH_DIR_INFORMATION)((UINT32)(*pDir)+(*pDir)->NextEntryOffset);
return STATUS_SUCCESS;
}
return STATUS_UNSUCCESSFUL;
}
void Traversal()
{
UNICODE_STRING ustrFolder = {0};
WCHAR szSymbol[0x512] = L"\\??\\";
UNICODE_STRING ustrPath = RTL_CONSTANT_STRING(L"C:\\MyDirectory");
HANDLE hFile = NULL;
SIZE_T nFileInfoSize = sizeof(FILE_BOTH_DIR_INFORMATION)+270*sizeof(WCHAR);
SIZE_T nSize = nFileInfoSize*0x256;
char strFileName[0x256] = {0};
PFILE_BOTH_DIR_INFORMATION pFileListBuf = NULL;
PFILE_BOTH_DIR_INFORMATION pFileList = NULL;
PFILE_BOTH_DIR_INFORMATION pFileDirInfo = (PFILE_BOTH_DIR_INFORMATION)ExAllocatePool(PagedPool,nSize);
wcscat_s(szSymbol,_countof(szSymbol),ustrPath.Buffer);
RtlInitUnicodeString(&ustrFolder,szSymbol);
hFile = KernelCreateFile(&ustrFolder,TRUE);
pFileList = pFileListBuf;
KernelFindFirstFile(hFile,nSize,pFileDirInfo);
if (pFileList)
{
RtlZeroMemory(strFileName,0x256);
RtlCopyMemory(strFileName,pFileDirInfo->FileName,pFileDirInfo->FileNameLength);
if (strcmp(strFileName,"..")!=0 || strcmp(strFileName,".")!=0)
{
if (pFileDirInfo->FileAttributes & FILE_ATTRIBUTE_DIRECTORY)
{
DbgPrint("[Directory]%S\n",strFileName);
}
else
{
DbgPrint("[File]%S\n",strFileName);
}
}
}
while (NT_SUCCESS(KernelFindNextFile(&pFileList)))
{
RtlZeroMemory(strFileName,0x256);
RtlCopyMemory(strFileName,pFileList->FileName,pFileList->FileNameLength);
if (strcmp(strFileName,"..")==0 || strcmp(strFileName,".")==0)
{
continue;
}
if (pFileList->FileAttributes & FILE_ATTRIBUTE_DIRECTORY)
{
DbgPrint("[Directory]%S\n",strFileName);
}
else
{
DbgPrint("[File]%S\n",strFileName);
}
}
RtlZeroMemory(strFileName,0x256);
RtlCopyMemory(strFileName,pFileListBuf->FileName,pFileListBuf->FileNameLength);
if (strcmp(strFileName,"..")!=0 || strcmp(strFileName,".")!=0)
{
if (pFileDirInfo->FileAttributes & FILE_ATTRIBUTE_DIRECTORY)
{
DbgPrint("[Directory]%S\n",strFileName);
}
else
{
DbgPrint("[File]%S\n",strFileName);
}
ExFreePool(pFileListBuf);
ExFreePool(pFileDirInfo);
}
}
BSOD:
FAULTING_SOURCE_LINE_NUMBER: 263
FAULTING_SOURCE_CODE:
259: }
260:
261: NTSTATUS KernelFindNextFile(IN OUT PFILE_BOTH_DIR_INFORMATION* pDir)
262: {
> 263: if ((*pDir)->NextEntryOffset)
264: {
265: (*pDir) = (PFILE_BOTH_DIR_INFORMATION)((UINT32)(*pDir) + (*pDir)->NextEntryOffset);
266: return STATUS_SUCCESS;
267: }
268:

ok, here code which tested and works. if somebody can not use it or got BSOD - probably problem not in code but in somebody skills
several notes - if you have previous mode kernel - use Nt* api (when exported) but not Zw* api. or Io* api. if you not understand why, or what is your previous mode - better even not try programming in kernel.
mandatory use FILE_OPEN_REPARSE_POINT option or even not try run this code if not understand what is this and why need use
for delete - open files with FILE_DELETE_ON_CLOSE option, for dump only - with FILE_DIRECTORY_FILE option instead.
code yourself used <= 0x1800 bytes of stack in x64 in deepest folders, like c:\Users - so this is ok for kernel, but always check stack space with IoGetRemainingStackSize
i will be not correct every comma in your code, if you can not do this yourself
#define ALLOCSIZE PAGE_SIZE
#ifdef _REAL_DELETE_
#define USE_DELETE_ON_CLOSE FILE_DELETE_ON_CLOSE
#define FILE_ACCESS FILE_GENERIC_READ|DELETE
#else
#define USE_DELETE_ON_CLOSE FILE_DIRECTORY_FILE
#define FILE_ACCESS FILE_GENERIC_READ
#endif
// int nLevel, PSTR prefix for debug only
void ntTraverse(POBJECT_ATTRIBUTES poa, ULONG FileAttributes, int nLevel, PSTR prefix)
{
if (IoGetRemainingStackSize() < PAGE_SIZE)
{
DbgPrint("no stack!\n");
return ;
}
if (!nLevel)
{
DbgPrint("!nLevel\n");
return ;
}
NTSTATUS status;
IO_STATUS_BLOCK iosb;
UNICODE_STRING ObjectName;
OBJECT_ATTRIBUTES oa = { sizeof(oa), 0, &ObjectName };
DbgPrint("%s[<%wZ>]\n", prefix, poa->ObjectName);
#ifdef _REAL_DELETE_
if (FileAttributes & FILE_ATTRIBUTE_READONLY)
{
if (0 <= NtOpenFile(&oa.RootDirectory, FILE_WRITE_ATTRIBUTES, poa, &iosb, FILE_SHARE_VALID_FLAGS, FILE_OPEN_FOR_BACKUP_INTENT|FILE_OPEN_REPARSE_POINT))
{
static FILE_BASIC_INFORMATION fbi = { {}, {}, {}, {}, FILE_ATTRIBUTE_NORMAL };
NtSetInformationFile(oa.RootDirectory, &iosb, &fbi, sizeof(fbi), FileBasicInformation);
NtClose(oa.RootDirectory);
}
}
#endif//_REAL_DELETE_
if (0 <= (status = NtOpenFile(&oa.RootDirectory, FILE_ACCESS, poa, &iosb, FILE_SHARE_VALID_FLAGS,
FILE_SYNCHRONOUS_IO_NONALERT|FILE_OPEN_REPARSE_POINT|FILE_OPEN_FOR_BACKUP_INTENT|USE_DELETE_ON_CLOSE)))
{
if (FileAttributes & FILE_ATTRIBUTE_DIRECTORY)
{
if (PVOID buffer = ExAllocatePool(PagedPool, ALLOCSIZE))
{
union {
PVOID pv;
PBYTE pb;
PFILE_DIRECTORY_INFORMATION DirInfo;
};
while (0 <= (status = NtQueryDirectoryFile(oa.RootDirectory, NULL, NULL, NULL, &iosb,
pv = buffer, ALLOCSIZE, FileDirectoryInformation, 0, NULL, FALSE)))
{
ULONG NextEntryOffset = 0;
do
{
pb += NextEntryOffset;
ObjectName.Buffer = DirInfo->FileName;
switch (ObjectName.Length = (USHORT)DirInfo->FileNameLength)
{
case 2*sizeof(WCHAR):
if (ObjectName.Buffer[1] != '.') break;
case sizeof(WCHAR):
if (ObjectName.Buffer[0] == '.') continue;
}
ObjectName.MaximumLength = ObjectName.Length;
#ifndef _REAL_DELETE_
if (DirInfo->FileAttributes & FILE_ATTRIBUTE_DIRECTORY)
#endif
{
ntTraverse(&oa, DirInfo->FileAttributes, nLevel - 1, prefix - 1);
}
#ifndef _REAL_DELETE_
else
#endif
{
DbgPrint("%s%8I64u <%wZ>\n", prefix, DirInfo->EndOfFile.QuadPart, &ObjectName);
}
} while (NextEntryOffset = DirInfo->NextEntryOffset);
}
ExFreePool(buffer);
if (status == STATUS_NO_MORE_FILES)
{
status = STATUS_SUCCESS;
}
}
}
NtClose(oa.RootDirectory);
}
if (0 > status)
{
DbgPrint("---- %x %wZ\n", status, poa->ObjectName);
}
}
void ntTraverse()
{
char prefix[MAXUCHAR + 1];
memset(prefix, '\t', MAXUCHAR);
prefix[MAXUCHAR] = 0;
STATIC_OBJECT_ATTRIBUTES(oa, "\\??\\c:\\users");
//STATIC_OBJECT_ATTRIBUTES(oa, "\\systemroot");
ntTraverse(&oa, FILE_ATTRIBUTE_DIRECTORY|FILE_ATTRIBUTE_READONLY, MAXUCHAR, prefix + MAXUCHAR);
}

Related

get the default string using EvtFormatMessage

I am trying to get the base string in the DLL using EvtFormatMessage but no matter what I do its not working. The windows help page made it sound like you can use the values and valuecount parameters to change the default behavior but when I change them nothing is printed.
Another attempt I made was to go through the publishers metadata and match it with the correct eventId and version but the EvtOpenEventMetadatEnum has no items for it to iterate through for all events publishers. It works for time change events but not for other events.
This is the code I have to return the message string. hMetadata is the publisher metadata and hEvent is the event metadata
LPWSTR GetMessageString(EVT_HANDLE hMetadata, EVT_HANDLE hEvent, EVT_FORMAT_MESSAGE_FLAGS FormatId, DWORD MsgID)
{
LPWSTR pBuffer = NULL;
DWORD dwBufferSize = 0;
DWORD dwBufferUsed = 0;
DWORD status = ERROR_SUCCESS;
if (!fpEvtFormatMessage(hMetadata, hEvent, MsgID, 0, NULL, FormatId, dwBufferSize, pBuffer, &dwBufferUsed))
{
status = GetLastError();
if (ERROR_INSUFFICIENT_BUFFER == status)
{
// An event can contain one or more keywords. The function returns keywords
// as a list of keyword strings. To process the list, you need to know the
// size of the buffer, so you know when you have read the last string, or you
// can terminate the list of strings with a second null terminator character
// as this example does.
if ((EvtFormatMessageKeyword == FormatId))
pBuffer[dwBufferSize - 1] = L'\0';
else
dwBufferSize = dwBufferUsed;
pBuffer = (LPWSTR)malloc(dwBufferSize * sizeof(WCHAR));
if (pBuffer)
{
fpEvtFormatMessage(hMetadata, hEvent, MsgID, 0, NULL, FormatId, dwBufferSize, pBuffer, &dwBufferUsed);
// Add the second null terminator character.
if ((EvtFormatMessageKeyword == FormatId))
pBuffer[dwBufferUsed - 1] = L'\0';
}
else
{
TRACE5(_T("malloc failed\n"));
}
}
else if (ERROR_EVT_MESSAGE_NOT_FOUND == status || ERROR_EVT_MESSAGE_ID_NOT_FOUND == status)
;
else
{
TRACE5(_T("EvtFormatMessage failed with %u\n"), status);
}
}
This is the code that is supposed to match the event I have with the template event in the dll
hEvents = fpEvtOpenEventMetadataEnum(g_hMetadata, 0);
if (NULL == hEvents)
{
TRACE5(_T("EvtOpenEventMetadataEnum failed with %lu\n"), GetLastError());
goto cleanup;
}
// Enumerate the events and print each event's metadata.
while (run)
{
hEvent = fpEvtNextEventMetadata(hEvents, 0);
if (NULL == hEvent)
{
if (ERROR_NO_MORE_ITEMS != (status = GetLastError()))
{
TRACE5(_T("EvtNextEventMetadata failed with %lu\n"), status);
}
break;
}
msgId = getEventID(g_hMetadata, hEvent, &tempVersion);
if (dwMsgID == msgId && tempVersion == version) {
PEVT_VARIANT pProperty = NULL; // Contains a metadata value
PEVT_VARIANT pTemp = NULL;
DWORD dwBufferSize = 0;
DWORD dwBufferUsed = 0;
DWORD status2 = ERROR_SUCCESS;
if (!fpEvtGetEventMetadataProperty(hEvent, EventMetadataEventMessageID, 0, dwBufferSize, pProperty, &dwBufferUsed))
{
status2 = GetLastError();
if (ERROR_INSUFFICIENT_BUFFER == status2)
{
dwBufferSize = dwBufferUsed;
pTemp = (PEVT_VARIANT)realloc(pProperty, dwBufferSize);
if (pTemp)
{
pProperty = pTemp;
pTemp = NULL;
fpEvtGetEventMetadataProperty(hEvent, EventMetadataEventMessageID, 0, dwBufferSize, pProperty, &dwBufferUsed);
}
else
{
TRACE5(_T("realloc failed\n"));
status2 = ERROR_OUTOFMEMORY;
goto cleanup;
}
}
if (ERROR_SUCCESS != (status2 = GetLastError()))
{
TRACE5(_T("EvtGetEventMetadataProperty failed with %d\n"), GetLastError());
goto cleanup;
}
}
if (-1 == pProperty->UInt32Val)
{
*pStrNonExpLibMsg = "Message string: \n";
}
else
{
*pStrNonExpLibMsg = GetMessageString(g_hMetadata, NULL, EvtFormatMessageId, pProperty->UInt32Val);
if (pMessage)
{
free(pMessage);
}
}
run = false;
break;
}
fpEvtClose(hEvent);
hEvent = NULL;
}
cleanup:
if (hEvents)
fpEvtClose(hEvents);
if (hEvent)
fpEvtClose(hEvent);
return status;
}
DWORD getEventID(EVT_HANDLE g_hMetadata, EVT_HANDLE hEvent, DWORD *evtVersion)
{
PEVT_VARIANT pProperty = NULL; // Contains a metadata value
PEVT_VARIANT pTemp = NULL;
DWORD dwBufferSize = 0;
DWORD dwBufferUsed = 0;
DWORD status = ERROR_SUCCESS;
DWORD retValue = NULL;
// Get the specified metadata property. If the pProperty buffer is not big enough, reallocate the buffer.
for (int i = 0; i < 2; i++)
{
if (!fpEvtGetEventMetadataProperty(hEvent, (EVT_EVENT_METADATA_PROPERTY_ID)i, 0, dwBufferSize, pProperty, &dwBufferUsed))
{
status = GetLastError();
if (ERROR_INSUFFICIENT_BUFFER == status)
{
dwBufferSize = dwBufferUsed;
pTemp = (PEVT_VARIANT)realloc(pProperty, dwBufferSize);
if (pTemp)
{
pProperty = pTemp;
pTemp = NULL;
fpEvtGetEventMetadataProperty(hEvent, (EVT_EVENT_METADATA_PROPERTY_ID)i, 0, dwBufferSize, pProperty, &dwBufferUsed);
}
else
{
TRACE5(_T("realloc failed\n"));
status = ERROR_OUTOFMEMORY;
goto cleanup;
}
}
if (ERROR_SUCCESS != (status = GetLastError()))
{
TRACE5(_T("EvtGetEventMetadataProperty failed with %d\n"), GetLastError());
goto cleanup;
}
}
if (i == 0)
{
retValue = pProperty->UInt32Val;
}
else
{
*evtVersion = pProperty->UInt32Val;
}
RtlZeroMemory(pProperty, dwBufferUsed);
}

Is there a workaround for CreateProcessWithTokenW's cmdline argument's max length?

I'm trying to create an unelevated process from an elevated process on Windows 7-10.
I used the following as a reference:
FAQ: How do I start a program as the desktop user from an elevated app?
Now, this method works wonderfully, however due to a possible legacy check, it seems that CreateProcessWithTokenW() only allows the cmdline argument to be less than or equal to 1024 characters.
The cmdline I'm required to pass through is far more than that, which causes an E_INVALIDARG error.
Has anyone run into the same issue as me? If so, how did you work around this absolutely ridiculous 1024 character limit?
for exec not elevated process from our elevated (in same session) we need do next:
create restricted token from our elevated token by
CreateRestrictedToken with LUA_TOKEN
set medium intergity level in new token
call CreateProcessAsUser - note that
If hToken is a restricted version of the caller's primary token, the
SE_ASSIGNPRIMARYTOKEN_NAME privilege is not required.
ULONG LowExec(PCWSTR lpApplicationName, PWSTR lpCommandLine)
{
HANDLE hToken, hLowToken;
ULONG cb = GetSidLengthRequired(1);
TOKEN_MANDATORY_LABEL tml = { { (PSID)alloca(cb) } };
ULONG dwError = NOERROR;
if (CreateWellKnownSid(WinMediumLabelSid, 0, tml.Label.Sid, &cb) &&
OpenProcessToken(NtCurrentProcess(), TOKEN_DUPLICATE | TOKEN_QUERY |
TOKEN_ADJUST_DEFAULT | TOKEN_ASSIGN_PRIMARY, &hToken))
{
BOOL fOk = CreateRestrictedToken(hToken, LUA_TOKEN, 0, 0, 0, 0, 0, 0, &hLowToken);
if (!fOk)
{
dwError = GetLastError();
}
CloseHandle(hToken);
if (fOk)
{
if (SetTokenInformation(hLowToken, ::TokenIntegrityLevel, &tml, sizeof(tml)))
{
STARTUPINFOW si = { sizeof(si)};
PROCESS_INFORMATION pi;
if (CreateProcessAsUser(hLowToken, lpApplicationName, lpCommandLine, 0, 0, TRUE, 0, 0, 0, &si, &pi))
{
CloseHandle(pi.hThread);
CloseHandle(pi.hProcess);
}
else
{
dwError = GetLastError();
}
}
else
{
dwError = GetLastError();
}
CloseHandle(hLowToken);
}
}
else
{
dwError = GetLastError();
}
return dwError;
}
Maybe try using CreateProcessAsUserW() instead? I know it requires special privileges, but if your process is running as administrator, there is a way to overcome it.
The idea is to impersonate a powerful token before calling the API. A powerful token is defined as a token that possesses all desired privileges.
First, enumerate process ID via API like NtQuerySystemInformation(). For each process ID, open the process token and use GetTokenInformation(hToken, TokenPrivileges, ...) to get information of what privileges are possessed by the token. If the token possesses all privileges we want, we may duplicate an impersonation token out from it by DuplicateTokenEx(..., TokenImpersonation, &hTokenImp) (and stop enumerating of course).
Now that we have the powerful token, we shall examine the desired privileges of it, see if they are all enabled. Check status with PrivilegeCheck() and enable it with AdjustTokenPrivileges(). Finally, let current thread impersonate the token by SetThreadToken(NULL, hTokenImp), call the CreateProcessAsUserW() however you like, and stop impersonation by SetThreadToken(NULL, NULL).
As for the token for CreateProcessAsUserW(), I would recommend you to use primary token of current user session by WTSQueryUserToken(), because it would work as expected even if you do OTS elevation. Last time I tried to use Linked Token as described here, and found it would not work with OTS elevation.
Here is a test code in ANSI C. When running as administrator, it will run another instance of itself un-elevated and show the command line length. And yes, CreateProcessAsUserW() supports command-line longer than 1024. :)
#include <stdio.h>
#include <Windows.h>
#include <objbase.h>
#include "EnumProcessesId.h"
#define MY_LuidEqual(a, b) \
( ((a)->HighPart == (b)->HighPart) && ((a)->LowPart == (b)->LowPart) )
static LPVOID MyAllocZero(SIZE_T cb)
{
LPVOID ptr = CoTaskMemAlloc(cb);
if (ptr) { ZeroMemory(ptr, cb); }
return ptr;
}
static void MyFree(LPVOID ptr)
{
CoTaskMemFree(ptr);
}
static DWORD MyWTSGetActiveConsoleSessionId(void)
{
typedef DWORD(WINAPI *fn_t)(void);
fn_t fn = (fn_t)GetProcAddress(LoadLibraryA("kernel32"),
"WTSGetActiveConsoleSessionId");
if (fn) { return fn(); }
return 0;
}
static BOOL MyWTSQueryUserToken(DWORD sessId, HANDLE *phToken)
{
typedef BOOL(WINAPI *fn_t)(DWORD, HANDLE*);
fn_t fn = (fn_t)GetProcAddress(LoadLibraryA("wtsapi32"),
"WTSQueryUserToken");
if (fn) {
return fn(sessId, phToken);
}
return FALSE;
}
static BOOL MyPrivIsEnabled(HANDLE hToken, LUID const *pPrivLuid)
{
BOOL isEnabled = FALSE;
PRIVILEGE_SET ps = { 0 };
ps.PrivilegeCount = 1;
ps.Privilege[0].Attributes = SE_PRIVILEGE_ENABLED_BY_DEFAULT;
PrivilegeCheck(hToken, &ps, &isEnabled);
if (!isEnabled) {
ps.Privilege[0].Attributes = SE_PRIVILEGE_ENABLED;
PrivilegeCheck(hToken, &ps, &isEnabled);
}
return isEnabled;
}
static HRESULT MyEnablePriv(HANDLE hToken, LUID const *pPrivLuid, BOOL enable)
{
BOOL ok = FALSE;
TOKEN_PRIVILEGES tp = { 0 };
tp.PrivilegeCount = 1;
tp.Privileges[0].Attributes = enable ? SE_PRIVILEGE_ENABLED : 0;
tp.Privileges[0].Luid = *pPrivLuid;
ok = AdjustTokenPrivileges(hToken, FALSE, &tp, sizeof(tp), NULL, NULL);
return ok ? S_OK : HRESULT_FROM_WIN32(GetLastError());
}
typedef struct {
/* in : */
LUID const *pLuidAPT;
LUID const *pLuidIQN;
LUID const *pLuidTCB;
/* out : */
HANDLE hptPowerful;
} MyEnumPowerfulTokenData_t;
static BOOL CALLBACK MyEnumPowerfulTokenProc(DWORD pid, void * user)
{
DWORD const ProcessQueryLimitedInfo = 0x1000;
MyEnumPowerfulTokenData_t *pData = user;
BOOL ok = FALSE, wantContinue = TRUE;
HANDLE hProc = NULL;
HANDLE hProcToken = NULL;
DWORD i = 0, cbTP = 0;
TOKEN_PRIVILEGES *pTP = NULL;
BOOL gotAPT = FALSE, gotIQN = FALSE, gotTCB = FALSE;
/* Get process token */
hProc = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, pid);
if (!hProc) {
hProc = OpenProcess(ProcessQueryLimitedInfo, FALSE, pid);
}
if (!hProc) goto eof;
ok = OpenProcessToken(hProc, TOKEN_QUERY | TOKEN_DUPLICATE, &hProcToken);
if (!ok) goto eof;
/* Check if token possesses desired privileges */
GetTokenInformation(hProcToken, TokenPrivileges, NULL, 0, &cbTP);
if (!cbTP) goto eof;
pTP = MyAllocZero(cbTP);
if (!pTP) goto eof;
ok = GetTokenInformation(hProcToken, TokenPrivileges, pTP, cbTP, &cbTP);
if (!ok) goto eof;
for (i = 0; i < pTP->PrivilegeCount; ++i)
{
LUID const *pThat = &(pTP->Privileges[i].Luid);
if (gotAPT && gotIQN && gotTCB) {
wantContinue = FALSE;
pData->hptPowerful = hProcToken;
hProcToken = NULL; /* to avoid eof CloseHandle() */
break;
}
else if (!gotAPT && MY_LuidEqual(pThat, pData->pLuidAPT)) {
gotAPT = TRUE;
}
else if (!gotIQN && MY_LuidEqual(pThat, pData->pLuidIQN)) {
gotIQN = TRUE;
}
else if (!gotTCB && MY_LuidEqual(pThat, pData->pLuidTCB)) {
gotTCB = TRUE;
}
}
eof:
if (pTP) { MyFree(pTP); }
if (hProcToken) { CloseHandle(hProcToken); }
if (hProc) { CloseHandle(hProc); }
return wantContinue;
}
static HRESULT MyCreateProcess(LPWSTR szCmdLine)
{
HRESULT hr = 0;
BOOL ok = FALSE;
LUID luidAPT = { 0 }; /* SE_ASSIGNPRIMARYTOKEN_NAME */
LUID luidIQN = { 0 }; /* SE_INCREASE_QUOTA_NAME */
LUID luidTCB = { 0 }; /* SE_TCB_NAME */
MyEnumPowerfulTokenData_t enumData = { 0 };
HANDLE hptPowerful = NULL; /* primary/process token */
HANDLE hitPowerful = NULL; /* impersonation token */
HANDLE hptCurrSessUser = NULL;
DWORD dwCurrSessId = 0;
STARTUPINFOW si = { sizeof(si) };
PROCESS_INFORMATION pi = { 0 };
ok = LookupPrivilegeValue(NULL, SE_ASSIGNPRIMARYTOKEN_NAME, &luidAPT)
&& LookupPrivilegeValue(NULL, SE_INCREASE_QUOTA_NAME, &luidIQN)
&& LookupPrivilegeValue(NULL, SE_TCB_NAME, &luidTCB);
if (!ok) {
hr = HRESULT_FROM_WIN32(GetLastError()); goto eof;
}
enumData.pLuidAPT = &luidAPT;
enumData.pLuidIQN = &luidIQN;
enumData.pLuidTCB = &luidTCB;
hr = EnumProcessesId_WinNT(MyEnumPowerfulTokenProc, &enumData);
if (FAILED(hr)) goto eof;
hptPowerful = enumData.hptPowerful;
if (!hptPowerful) {
hr = E_UNEXPECTED; goto eof;
}
ok = DuplicateTokenEx(hptPowerful, TOKEN_ADJUST_PRIVILEGES | TOKEN_IMPERSONATE,
NULL, SecurityImpersonation, TokenImpersonation, &hitPowerful);
if (!ok) {
hr = HRESULT_FROM_WIN32(GetLastError()); goto eof;
}
if (!MyPrivIsEnabled(hitPowerful, &luidAPT))
{
hr = MyEnablePriv(hitPowerful, &luidAPT, TRUE);
if (FAILED(hr)) goto eof;
}
if (!MyPrivIsEnabled(hitPowerful, &luidIQN))
{
hr = MyEnablePriv(hitPowerful, &luidIQN, TRUE);
if (FAILED(hr)) goto eof;
}
if (!MyPrivIsEnabled(hitPowerful, &luidTCB))
{
hr = MyEnablePriv(hitPowerful, &luidTCB, TRUE);
if (FAILED(hr)) goto eof;
}
ok = SetThreadToken(NULL, hitPowerful);
if (!ok) {
hr = HRESULT_FROM_WIN32(GetLastError()); goto eof;
}
dwCurrSessId = MyWTSGetActiveConsoleSessionId();
ok = MyWTSQueryUserToken(dwCurrSessId, &hptCurrSessUser);
if (!ok) {
hr = HRESULT_FROM_WIN32(GetLastError()); goto eof;
}
ok = CreateProcessAsUserW(hptCurrSessUser, NULL, szCmdLine, 0, 0, 0, 0, 0, 0, &si, &pi);
if (!ok) {
hr = HRESULT_FROM_WIN32(GetLastError()); goto eof;
}
eof:
SetThreadToken(NULL, NULL);
if (hptCurrSessUser) { CloseHandle(hptCurrSessUser); }
if (hitPowerful) { CloseHandle(hitPowerful); }
if (hptPowerful) { CloseHandle(hptPowerful); }
if (FAILED(hr)) {
printf("HRESULT = 0x%.8X \n", hr);
}
return 0;
}
int main(int argc, char **argv)
{
if (argc > 1)
{
WCHAR szMsg[999] = {0};
wsprintfW(szMsg,
L"lstrlenW(GetCommandLineW()) = %i \n",
lstrlenW(GetCommandLineW()));
MessageBoxW(NULL, szMsg, L"Test", MB_ICONINFORMATION);
}
else
{
WCHAR szMyExePath[MAX_PATH] = {0};
WCHAR szCmdLine[9999] = {0}, *p;
GetModuleFileNameW(NULL, szMyExePath, MAX_PATH);
wsprintfW(szCmdLine, L"\"%s\" ", szMyExePath);
for (p = szCmdLine; *p; ++p);
while (p < (szCmdLine + 9999 - 1))
{
*p++ = 'a';
}
MyCreateProcess(szCmdLine);
}
return 0;
}
I will leave the implementation of EnumProcessesId_WinNT() to you. The prototype of the function is:
/* return TRUE to continue */
typedef BOOL(CALLBACK *EnumProcessesId_Callback_t)
(DWORD pid, void * user);
EXTERN_C
HRESULT __stdcall
EnumProcessesId_WinNT(
EnumProcessesId_Callback_t fnCallback,
void *user
);

How to detect USB speed on Windows

I use setup API functions to find a USB device then use createfile to communicate with it. i.e. using SetupDiGetClassDevs, SetupDiEnumDeviceInterfaces, SetupDiGetDeviceInterfaceDetail, etc.
I would like to be able to determine if the device is connected at USB2 speeds or USB3 speeds ie. SuperSpeed or not
How can I do this through the Windows APIs?
This is what I ended up going with. It's convoluted. Cant believe there isn't an easier way:
#include "stdafx.h"
#include <Windows.h>
#include <Setupapi.h>
#include <winusb.h>
#undef LowSpeed
#include <Usbioctl.h>
#include <iostream>
#include <string>
#include <memory>
#include <vector>
class Usb_Device
{
private:
std::wstring _driverKey;
char _speed;
public:
Usb_Device(int adapterNumber, std::wstring devicePath, char speed);
virtual char GetSpeed(std::wstring driverKey);
};
class Usb_Hub : public Usb_Device
{
private:
bool _isRootHub;
std::wstring _deviceDescription;
std::wstring _devicePath;
std::vector<std::unique_ptr<Usb_Device>> _devices;
public:
Usb_Hub(std::wstring devicePath, char speed);
virtual char GetSpeed(std::wstring driverKey) override;
};
class Usb_Controller
{
private:
GUID _interfaceClassGuid;
std::wstring _devicePath;
std::wstring _deviceDescription;
std::wstring _driverKey;
std::vector<std::unique_ptr<Usb_Device>> _devices;
public:
Usb_Controller();
char GetSpeed(std::wstring driverKey);
};
static std::unique_ptr<Usb_Device> BuildDevice(int portCount, std::wstring devicePath)
{
std::unique_ptr<Usb_Device> ret;
HANDLE handle = INVALID_HANDLE_VALUE;
DWORD bytes = -1;
DWORD bytesReturned = -1;
BOOL isConnected = FALSE;
char speed;
// Open a handle to the Hub device
handle = CreateFile(devicePath.c_str(), GENERIC_WRITE, FILE_SHARE_WRITE, 0, OPEN_EXISTING, 0, 0);
if (handle != INVALID_HANDLE_VALUE)
{
bytes = sizeof(USB_NODE_CONNECTION_INFORMATION_EX);
PUSB_NODE_CONNECTION_INFORMATION_EX nodeConnection = (PUSB_NODE_CONNECTION_INFORMATION_EX)(new char[bytes]);
nodeConnection->ConnectionIndex = portCount;
if (DeviceIoControl(handle, IOCTL_USB_GET_NODE_CONNECTION_INFORMATION_EX, nodeConnection, bytes, nodeConnection, bytes, &bytesReturned, 0))
{
isConnected = nodeConnection->ConnectionStatus == USB_CONNECTION_STATUS::DeviceConnected;
speed = nodeConnection->Speed;
}
if (isConnected)
{
if (nodeConnection->DeviceDescriptor.bDeviceClass == 0x09 /*HubDevice*/)
{
bytes = sizeof(USB_NODE_CONNECTION_NAME);
PUSB_NODE_CONNECTION_NAME nodeConnectionName = (PUSB_NODE_CONNECTION_NAME)(new char[bytes]);
nodeConnectionName->ConnectionIndex = portCount;
if (DeviceIoControl(handle, IOCTL_USB_GET_NODE_CONNECTION_NAME, nodeConnectionName, bytes, nodeConnectionName, bytes, &bytesReturned, 0))
{
bytes = nodeConnectionName->ActualLength;
delete[] nodeConnectionName;
nodeConnectionName = (PUSB_NODE_CONNECTION_NAME)(new char[bytes]);
nodeConnectionName->ConnectionIndex = portCount;
if (DeviceIoControl(handle, IOCTL_USB_GET_NODE_CONNECTION_NAME, nodeConnectionName, bytes, nodeConnectionName, bytes, &bytesReturned, 0))
{
std::wstring name = std::wstring(L"\\\\?\\") + std::wstring(nodeConnectionName->NodeName);
ret = std::unique_ptr<Usb_Device>(new Usb_Hub(name, speed));
}
}
delete[] nodeConnectionName;
}
else
{
ret = std::unique_ptr<Usb_Device>(new Usb_Device(portCount, devicePath, speed));
}
}
else
{
// Chuck this device
}
delete[] nodeConnection;
CloseHandle(handle);
}
return ret;
}
Usb_Controller::Usb_Controller()
{
BOOL success = TRUE;
for (int index = 0; success; index++)
{
GUID guid;
HRESULT hr = CLSIDFromString(L"{3abf6f2d-71c4-462a-8a92-1e6861e6af27}", (LPCLSID)&guid);
unsigned char* ptr = new unsigned char[2048]; // Should really do two calls, but that's more effort
HDEVINFO deviceInfoHandle = SetupDiGetClassDevs(&guid, 0, 0, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
// Create a device interface data structure
SP_DEVICE_INTERFACE_DATA deviceInterfaceData = { 0 };
deviceInterfaceData.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
// Start the enumeration.
success = SetupDiEnumDeviceInterfaces(deviceInfoHandle, 0, &guid, index, &deviceInterfaceData);
if (success)
{
_interfaceClassGuid = deviceInterfaceData.InterfaceClassGuid;
// Build a DevInfo data structure.
SP_DEVINFO_DATA deviceInfoData = { 0 };
deviceInfoData.cbSize = sizeof(SP_DEVINFO_DATA);
// Now we can get some more detailed informations.
DWORD nRequiredSize = 0;
SetupDiGetDeviceInterfaceDetail(deviceInfoHandle, &deviceInterfaceData, 0, 0, &nRequiredSize, 0);
if (ERROR_INSUFFICIENT_BUFFER == GetLastError())
{
PSP_DEVICE_INTERFACE_DETAIL_DATA deviceInterfaceDetailData = (PSP_DEVICE_INTERFACE_DETAIL_DATA)(new char[nRequiredSize]);
memset(deviceInterfaceDetailData, 0, nRequiredSize);
deviceInterfaceDetailData->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA);
if (SetupDiGetDeviceInterfaceDetail(deviceInfoHandle, &deviceInterfaceData, deviceInterfaceDetailData, nRequiredSize, &nRequiredSize, &deviceInfoData))
{
_devicePath = deviceInterfaceDetailData->DevicePath;
// Get the device description and driver key name.
DWORD requiredSize = 0;
DWORD regType = REG_SZ;
if (SetupDiGetDeviceRegistryProperty(deviceInfoHandle, &deviceInfoData, SPDRP_DEVICEDESC, &regType, ptr, 2048, &requiredSize))
{
_deviceDescription = reinterpret_cast<wchar_t*>(ptr);
}
if (SetupDiGetDeviceRegistryProperty(deviceInfoHandle, &deviceInfoData, SPDRP_DRIVER, &regType, ptr, 2048, &requiredSize))
{
_driverKey = reinterpret_cast<wchar_t*>(ptr);
}
}
delete[] deviceInterfaceDetailData;
}
SetupDiDestroyDeviceInfoList(deviceInfoHandle);
std::unique_ptr<Usb_Device> hub(new Usb_Hub(_devicePath, -1));
_devices.push_back(std::move(hub));
}
else
{
success = false;
}
delete[] ptr;
}
}
char Usb_Controller::GetSpeed(std::wstring driverKey)
{
char speed = -1;
for (auto it = _devices.begin(); it != _devices.end() && speed == -1; ++it)
{
if (*it != nullptr)
{
speed = (*it)->GetSpeed(driverKey);
}
}
return speed;
}
Usb_Hub::Usb_Hub(std::wstring devicePath, char speed) :
Usb_Device(-1, devicePath, speed)
{
HANDLE handle1 = INVALID_HANDLE_VALUE;
HANDLE handle2 = INVALID_HANDLE_VALUE;
_deviceDescription = L"Standard-USB-Hub";
_devicePath = devicePath;
DWORD bytesReturned = -1;
DWORD bytes = -1;
BOOL success = TRUE;
// Open a handle to the host controller.
handle1 = CreateFile(devicePath.c_str(), GENERIC_WRITE, FILE_SHARE_WRITE, 0, OPEN_EXISTING, 0, 0);
if (handle1 != INVALID_HANDLE_VALUE)
{
USB_ROOT_HUB_NAME rootHubName;
memset(&rootHubName, 0, sizeof(USB_ROOT_HUB_NAME));
// Get the root hub name.
if (DeviceIoControl(handle1, IOCTL_USB_GET_ROOT_HUB_NAME, nullptr, 0, &rootHubName, sizeof(USB_ROOT_HUB_NAME), &bytesReturned, 0))
{
if (rootHubName.ActualLength > 0)
{
PUSB_ROOT_HUB_NAME actualRootHubName = (PUSB_ROOT_HUB_NAME)(new char[rootHubName.ActualLength]);
if (DeviceIoControl(handle1, IOCTL_USB_GET_ROOT_HUB_NAME, nullptr, 0, actualRootHubName, rootHubName.ActualLength, &bytesReturned, 0))
{
_isRootHub = true;
_deviceDescription = L"RootHub";
_devicePath = std::wstring(L"\\\\?\\") + std::wstring(actualRootHubName->RootHubName);
}
delete[] actualRootHubName;
}
}
// Now let's open the hub (based upon the hub name we got above).
int PortCount = 0;
handle2 = CreateFile(_devicePath.c_str(), GENERIC_WRITE, FILE_SHARE_WRITE, 0, OPEN_EXISTING, 0, 0);
if (handle2 != INVALID_HANDLE_VALUE)
{
bytes = sizeof(USB_NODE_INFORMATION);
PUSB_NODE_INFORMATION nodeInfo = (PUSB_NODE_INFORMATION)(new char[bytes]);
memset(nodeInfo, 0, sizeof(USB_NODE_INFORMATION));
nodeInfo->NodeType = USB_HUB_NODE::UsbHub;
// Get the hub information.
if (DeviceIoControl(handle2, IOCTL_USB_GET_NODE_INFORMATION, nodeInfo, bytes, nodeInfo, bytes, &bytesReturned, 0))
{
DWORD d = GetLastError();
PortCount = nodeInfo->u.HubInformation.HubDescriptor.bNumberOfPorts;
}
delete[] nodeInfo;
CloseHandle(handle2);
}
CloseHandle(handle1);
for (int index = 1; index <= PortCount; index++)
{
std::unique_ptr<Usb_Device> device = BuildDevice(index, _devicePath);
_devices.push_back(std::move(device));
}
}
else
{
success = FALSE;
}
}
char Usb_Hub::GetSpeed(std::wstring driverKey)
{
char speed = Usb_Device::GetSpeed(driverKey);
if (speed == -1)
{
for (auto it = _devices.begin(); it != _devices.end() && speed == -1; ++it)
{
if (*it != nullptr)
{
speed = (*it)->GetSpeed(driverKey);
}
}
}
return speed;
}
Usb_Device::Usb_Device(int adapterNumber, std::wstring devicePath, char speed)
{
_speed = speed;
HANDLE handle = CreateFile(devicePath.c_str(), GENERIC_WRITE, FILE_SHARE_WRITE, 0, OPEN_EXISTING, 0, 0);
if (handle != INVALID_HANDLE_VALUE)
{
// Get the Driver Key Name (usefull in locating a device)
DWORD bytesReturned = -1;
DWORD bytes = sizeof(USB_NODE_CONNECTION_DRIVERKEY_NAME);
PUSB_NODE_CONNECTION_DRIVERKEY_NAME driverKey = (PUSB_NODE_CONNECTION_DRIVERKEY_NAME)(new char[bytes]);
driverKey->ConnectionIndex = adapterNumber;
// Use an IOCTL call to request the Driver Key Name
if (DeviceIoControl(handle, IOCTL_USB_GET_NODE_CONNECTION_DRIVERKEY_NAME, driverKey, bytes, driverKey, bytes, &bytesReturned, 0))
{
bytes = driverKey->ActualLength;
delete[] driverKey;
driverKey = (PUSB_NODE_CONNECTION_DRIVERKEY_NAME)(new char[bytes]);
driverKey->ConnectionIndex = adapterNumber;
if (DeviceIoControl(handle, IOCTL_USB_GET_NODE_CONNECTION_DRIVERKEY_NAME, driverKey, bytes, driverKey, bytes, &bytesReturned, 0))
{
_driverKey = driverKey->DriverKeyName;
}
}
delete[] driverKey;
CloseHandle(handle);
}
}
char Usb_Device::GetSpeed(std::wstring driverKey)
{
return _speed;
}
int main()
{
Usb_Controller controller;
GUID guid;
HRESULT hr = CLSIDFromString(L"{AAAAAAAA-AAAA-AAAA-AAAA-AAAAAAAAAAAA}", (LPCLSID)&guid);
HDEVINFO deviceInfoHandle = SetupDiGetClassDevs(&guid, 0, 0, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
if (deviceInfoHandle != INVALID_HANDLE_VALUE)
{
int deviceIndex = 0;
while (true)
{
SP_DEVICE_INTERFACE_DATA deviceInterface = { 0 };
deviceInterface.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
if (SetupDiEnumDeviceInterfaces(deviceInfoHandle, 0, &guid, deviceIndex, &deviceInterface))
{
DWORD cbRequired = 0;
SetupDiGetDeviceInterfaceDetail(deviceInfoHandle, &deviceInterface, 0, 0, &cbRequired, 0);
if (ERROR_INSUFFICIENT_BUFFER == GetLastError())
{
PSP_DEVICE_INTERFACE_DETAIL_DATA deviceInterfaceDetail = (PSP_DEVICE_INTERFACE_DETAIL_DATA)(new char[cbRequired]);
memset(deviceInterfaceDetail, 0, cbRequired);
deviceInterfaceDetail->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA);
if (!SetupDiGetDeviceInterfaceDetail(deviceInfoHandle, &deviceInterface, deviceInterfaceDetail, cbRequired, &cbRequired, 0))
{
deviceIndex++;
continue;
}
// Initialize the structure before using it.
memset(deviceInterfaceDetail, 0, cbRequired);
deviceInterfaceDetail->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA);
// Call the API a second time to retrieve the actual
// device path string.
BOOL status = SetupDiGetDeviceInterfaceDetail(
deviceInfoHandle, // Handle to device information set
&deviceInterface, // Pointer to current node in devinfo set
deviceInterfaceDetail, // Pointer to buffer to receive device path
cbRequired, // Length of user-allocated buffer
&cbRequired, // Pointer to arg to receive required buffer length
NULL); // Not interested in additional data
BOOL success = TRUE;
for (int i = 0; success; i++)
{
SP_DEVINFO_DATA deviceInterfaceData = { 0 };
deviceInterfaceData.cbSize = sizeof(SP_DEVINFO_DATA);
// Start the enumeration.
success = SetupDiEnumDeviceInfo(deviceInfoHandle, i, &deviceInterfaceData);
DWORD RequiredSize = 0;
DWORD regType = REG_SZ;
unsigned char* ptr = new unsigned char[2048];
if (SetupDiGetDeviceRegistryProperty(deviceInfoHandle, &deviceInterfaceData, SPDRP_DRIVER, &regType, ptr, 2048, &RequiredSize))
{
char speed = controller.GetSpeed(reinterpret_cast<wchar_t*>(ptr));
std::wcout << std::wstring(reinterpret_cast<wchar_t*>(ptr)) << std::endl;
std::wcout << L"Speed: " << (int)speed << std::endl;
}
delete[] ptr;
}
auto hDeviceHandle = CreateFile(
deviceInterfaceDetail->DevicePath,
GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL,
OPEN_EXISTING,
FILE_FLAG_OVERLAPPED,
NULL);
CloseHandle(hDeviceHandle);
delete[] deviceInterfaceDetail;
}
}
else
{
break;
}
++deviceIndex;
}
SetupDiDestroyDeviceInfoList(deviceInfoHandle);
}
return 0;
}
I guess you will have to try WinUSB in the link there's a sample code of detecting the speed of USB. If you want the description of WinUSB you will find it here.

How to retrieve files digital signature information?

I have searched for a way to retrieve information from a digital signed PE file. I need the publisher, publisher link , issuer name and subject name. I need winapi / c / c++ code (functions) and i need a fast method , i don't need to check if the signature is valid or not.
Here is code that I wrote for a project of mine that will do this. It returns the details in a struct of type NSIGINFO. Feel free to use it - no attribution necessary, but I would appreciate it if you would leave the copyright intact.
If there's any functions missing (I had to consolidate things from a couple of different places so I may have missed something) please let me know and I'll make the necessary tweaks.
Let me know how this works for you. Good luck.
The header file, NAuthenticode.h:
// NAuthenticode.h: Functions for checking signatures in files
//
// Copyright (c) 2008-2012, Nikolaos D. Bougalis <nikb#bougalis.net>
#ifndef B82FBB5B_C0F8_43A5_9A31_619BB690706C
#define B82FBB5B_C0F8_43A5_9A31_619BB690706C
#include <wintrust.h>
#include <softpub.h>
#include <imagehlp.h>
struct NSIGINFO
{
LONG lValidationResult;
LPTSTR lpszPublisher;
LPTSTR lpszPublisherEmail;
LPTSTR lpszPublisherUrl;
LPTSTR lpszAuthority;
LPTSTR lpszFriendlyName;
LPTSTR lpszProgramName;
LPTSTR lpszPublisherLink;
LPTSTR lpszMoreInfoLink;
LPTSTR lpszSignature;
LPTSTR lpszSerial;
BOOL bHasSigTime;
SYSTEMTIME stSigTime;
};
VOID NCertFreeSigInfo(NSIGINFO *pSigInfo);
BOOL NVerifyFileSignature(LPCTSTR lpszFileName, NSIGINFO *pSigInfo, HANDLE hHandle = INVALID_HANDLE_VALUE);
BOOL NCertGetNameString(PCCERT_CONTEXT pCertContext, DWORD dwType,
DWORD dwFlags, LPTSTR *lpszNameString);
BOOL NCheckFileCertificates(HANDLE hFile,
VOID (*pCallback)(PCCERT_CONTEXT, LPVOID), PVOID pParam);
#endif
The implementation, NAuthenticode.cpp:
// NAuthenticode.cpp: Various routines related to validating file signatures
//
// Copyright (c) 2008-2012, Nikolaos D. Bougalis <nikb#bougalis.net>
#include "stdafx.h"
#include "NAuthenticode.h"
//////////////////////////////////////////////////////////////////////////
#pragma comment(lib, "crypt32")
#pragma comment(lib, "imagehlp")
#pragma comment(lib, "wintrust")
//////////////////////////////////////////////////////////////////////////
#define SIG_ENCODING (X509_ASN_ENCODING | PKCS_7_ASN_ENCODING)
//////////////////////////////////////////////////////////////////////////
// Some utility functions
LPVOID NHeapAlloc(SIZE_T dwBytes)
{
if(dwBytes == 0)
return NULL;
return HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, dwBytes);
}
//////////////////////////////////////////////////////////////////////////
LPVOID NHeapFree(LPVOID lpMem)
{
if(lpMem != NULL)
HeapFree(GetProcessHeap(), 0, lpMem);
return NULL;
}
//////////////////////////////////////////////////////////////////////////
LPSTR NConvertW2A(LPCWSTR lpszString, int nLen, UINT nCodePage)
{
ASSERT(lpszString != NULL);
int ret = WideCharToMultiByte(nCodePage, 0, lpszString, nLen, NULL, 0, NULL, NULL);
if(ret <= 0)
return NULL;
LPSTR lpszOutString = (LPSTR)NHeapAlloc((ret + 1) * sizeof(CHAR));
if(lpszOutString == NULL)
return NULL;
ret = WideCharToMultiByte(nCodePage, 0, lpszString, nLen, lpszOutString, ret, NULL, NULL);
if(ret <= 0)
lpszOutString = (LPSTR)NHeapFree(lpszOutString);
return lpszOutString;
}
//////////////////////////////////////////////////////////////////////////
LPWSTR NDupString(LPCWSTR lpszString, int nLen)
{
if(nLen == -1)
nLen = (int)wcslen(lpszString);
LPWSTR lpszOutString = (LPWSTR)NHeapAlloc((2 + nLen) * sizeof(WCHAR));
if((lpszOutString != NULL) && (nLen != 0))
wcsncpy(lpszOutString, lpszString, nLen + 1);
return lpszOutString;
}
//////////////////////////////////////////////////////////////////////////
LPTSTR NConvertW2T(LPCWSTR lpszString, int nLen, UINT nCodePage)
{
ASSERT(lpszString != NULL);
#ifndef UNICODE
return (LPTSTR)NConvertW2A(lpszString, nLen, nCodePage);
#else
return (LPTSTR)NDupString(lpszString, nLen);
#endif
}
//////////////////////////////////////////////////////////////////////////
LPWSTR NConvertA2W(LPCSTR lpszString, int nLen, UINT nCodePage)
{
ASSERT(lpszString != NULL);
int ret = MultiByteToWideChar(nCodePage, 0, lpszString, nLen, NULL, 0);
if(ret <= 0)
return NULL;
LPWSTR lpszOutString = (LPWSTR)NHeapAlloc((ret + 1) * sizeof(WCHAR));
if(lpszOutString == NULL)
return NULL;
ret = MultiByteToWideChar(nCodePage, 0, lpszString, nLen, lpszOutString, ret);
if(ret <= 0)
lpszOutString = (LPWSTR)NHeapFree(lpszOutString);
return lpszOutString;
}
//////////////////////////////////////////////////////////////////////////
LPWSTR NConvertT2W(LPCTSTR lpszString, int nLen, UINT nCodePage)
{
ASSERT(lpszString != NULL);
#ifndef UNICODE
return NConvertA2W((LPCSTR)lpszString, nLen, nCodePage);
#else
return NDupString((LPWSTR)lpszString, nLen);
#endif
}
//////////////////////////////////////////////////////////////////////////
VOID NCertFreeSigInfo(NSIGINFO *pSigInfo)
{
if(pSigInfo == NULL)
return;
__try
{ // Be extra careful
if(pSigInfo->lpszPublisher)
pSigInfo->lpszPublisher = (LPTSTR)NHeapFree(pSigInfo->lpszPublisher);
if(pSigInfo->lpszPublisherEmail)
pSigInfo->lpszPublisherEmail = (LPTSTR)NHeapFree(pSigInfo->lpszPublisherEmail);
if(pSigInfo->lpszPublisherUrl)
pSigInfo->lpszPublisherUrl = (LPTSTR)NHeapFree(pSigInfo->lpszPublisherUrl);
if(pSigInfo->lpszAuthority)
pSigInfo->lpszAuthority = (LPTSTR)NHeapFree(pSigInfo->lpszAuthority);
if(pSigInfo->lpszProgramName)
pSigInfo->lpszProgramName = (LPTSTR)NHeapFree(pSigInfo->lpszPublisher);
if(pSigInfo->lpszPublisherLink)
pSigInfo->lpszPublisherLink = (LPTSTR)NHeapFree(pSigInfo->lpszPublisher);
if(pSigInfo->lpszMoreInfoLink)
pSigInfo->lpszMoreInfoLink = (LPTSTR)NHeapFree(pSigInfo->lpszMoreInfoLink);
if(pSigInfo->lpszSignature)
pSigInfo->lpszSignature = (LPTSTR)NHeapFree(pSigInfo->lpszSignature);
if(pSigInfo->lpszSerial)
pSigInfo->lpszSerial = (LPTSTR)NHeapFree(pSigInfo->lpszSerial);
}
__except(EXCEPTION_EXECUTE_HANDLER)
{
}
}
//////////////////////////////////////////////////////////////////////////
static BOOL NCertGetNameString(PCCERT_CONTEXT pCertContext, DWORD dwType, DWORD dwFlags, LPTSTR *lpszNameString)
{
if(pCertContext == NULL)
return FALSE;
DWORD dwData = CertGetNameString(pCertContext, dwType, 0, NULL, NULL, 0);
if(dwData == 0)
return FALSE;
*lpszNameString = (LPTSTR)NHeapAlloc((dwData + 1) * sizeof(TCHAR));
if(*lpszNameString == NULL)
return FALSE;
dwData = CertGetNameString(pCertContext, dwType, dwFlags, NULL, *lpszNameString, dwData);
if(dwData == 0)
{
NHeapFree(*lpszNameString);
return FALSE;
}
return TRUE;
}
//////////////////////////////////////////////////////////////////////////
static BOOL NCryptDecodeObject(__in LPCSTR lpszObjectId, __in_bcount(cbEncoded) const BYTE *pbEncoded, __in DWORD cbEncoded,
__inout DWORD &dwBuffer, __out void *pBuffer = NULL, __in DWORD dwFlags = 0)
{
if(((pBuffer == NULL) && (dwBuffer != 0)) || ((dwBuffer == 0) && (pBuffer != NULL)))
{ // What? You're passing a NULL pointer an a non-zero size? You so crazy!!!!
ASSERT(FALSE);
SetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
}
return CryptDecodeObject(SIG_ENCODING, lpszObjectId, pbEncoded, cbEncoded, dwFlags, pBuffer, &dwBuffer);
}
//////////////////////////////////////////////////////////////////////////
static BOOL NCryptDecodeObject(__in LPCSTR lpszObjectId, __in PCRYPT_ATTR_BLOB pObject,
__inout DWORD &dwBuffer, __out void *pBuffer = NULL, __in DWORD dwFlags = 0)
{
if((pObject == NULL) || ((dwBuffer == 0) && (pBuffer != NULL)) || ((dwBuffer != 0) && (pBuffer == NULL)))
{
SetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
}
return CryptDecodeObject(SIG_ENCODING, lpszObjectId, pObject->pbData, pObject->cbData, dwFlags, pBuffer, &dwBuffer);
}
//////////////////////////////////////////////////////////////////////////
static BOOL WGetSignTimestamp(PCRYPT_ATTRIBUTES pAttributes, SYSTEMTIME &stTime, LPCSTR lpszObjId)
{
if((pAttributes == NULL) || (pAttributes->cAttr == 0) || (lpszObjId == NULL) || (*lpszObjId == 0))
return FALSE;
for(DWORD dwAttr = 0; dwAttr < pAttributes->cAttr; dwAttr++)
{
if(strcmp(lpszObjId, pAttributes->rgAttr[dwAttr].pszObjId) == 0)
{
DWORD dwSize = sizeof(FILETIME);
FILETIME ftCert;
if(NCryptDecodeObject(lpszObjId, &pAttributes->rgAttr[dwAttr].rgValue[0], dwSize, (PVOID)&ftCert))
{
FILETIME ftLocal;
if(FileTimeToLocalFileTime(&ftCert, &ftLocal) && FileTimeToSystemTime(&ftLocal, &stTime))
return TRUE;
}
}
}
return FALSE;
}
//////////////////////////////////////////////////////////////////////////
static BOOL NVerifyFileSignatureWorker(LPWSTR lpszFileName, WINTRUST_DATA &wtData, NSIGINFO *pSigInfo)
{
if(pSigInfo != NULL)
memset(pSigInfo, 0, sizeof(NSIGINFO));
GUID guidAction = WINTRUST_ACTION_GENERIC_VERIFY_V2;
BOOL bVerified = FALSE;
LONG lRet = WinVerifyTrust((HWND)INVALID_HANDLE_VALUE, &guidAction, &wtData);
if(lRet != 0)
{
if(pSigInfo != NULL)
pSigInfo->lValidationResult = lRet;
return FALSE;
}
if(pSigInfo == NULL)
return TRUE;
HCERTSTORE hStore = NULL;
HCRYPTMSG hMsg = NULL;
if(!CryptQueryObject(CERT_QUERY_OBJECT_FILE, lpszFileName, CERT_QUERY_CONTENT_FLAG_PKCS7_SIGNED_EMBED, CERT_QUERY_FORMAT_FLAG_BINARY, 0, NULL, NULL, NULL, &hStore, &hMsg, NULL))
return FALSE;
PCMSG_SIGNER_INFO pSignerInfo = NULL, pCounterSignerInfo = NULL;
DWORD dwSignerInfo = 0, dwCounterSignerInfo = 0;
if(CryptMsgGetParam(hMsg, CMSG_SIGNER_INFO_PARAM, 0, NULL, &dwSignerInfo) && (dwSignerInfo != 0))
pSignerInfo = (PCMSG_SIGNER_INFO)NHeapAlloc(dwSignerInfo);
if((pSignerInfo != NULL) && CryptMsgGetParam(hMsg, CMSG_SIGNER_INFO_PARAM, 0, (PVOID)pSignerInfo, &dwSignerInfo))
{
for(DWORD dwAttr = 0; dwAttr < pSignerInfo->AuthAttrs.cAttr; dwAttr++)
{
if((strcmp(SPC_SP_OPUS_INFO_OBJID, pSignerInfo->AuthAttrs.rgAttr[dwAttr].pszObjId) != 0))
continue;
PSPC_SP_OPUS_INFO pOpus = NULL;
DWORD dwData = 0;
if(NCryptDecodeObject(SPC_SP_OPUS_INFO_OBJID, &pSignerInfo->AuthAttrs.rgAttr[dwAttr].rgValue[0], dwData) && (dwData != 0))
pOpus = (PSPC_SP_OPUS_INFO)NHeapAlloc(dwData);
if((pOpus != NULL) && NCryptDecodeObject(SPC_SP_OPUS_INFO_OBJID, &pSignerInfo->AuthAttrs.rgAttr[dwAttr].rgValue[0], dwData, (PVOID)pOpus))
{
pSigInfo->lpszProgramName = NConvertW2T(pOpus->pwszProgramName);
if(pOpus->pPublisherInfo != NULL)
{
switch(pOpus->pPublisherInfo->dwLinkChoice)
{
case SPC_URL_LINK_CHOICE:
pSigInfo->lpszPublisherLink = NConvertW2T(pOpus->pPublisherInfo->pwszUrl);
break;
case SPC_FILE_LINK_CHOICE:
pSigInfo->lpszPublisherLink = NConvertW2T(pOpus->pPublisherInfo->pwszFile);
break;
}
}
if(pOpus->pMoreInfo != NULL)
{
switch (pOpus->pMoreInfo->dwLinkChoice)
{
case SPC_URL_LINK_CHOICE:
pSigInfo->lpszMoreInfoLink = NConvertW2T(pOpus->pMoreInfo->pwszUrl);
break;
case SPC_FILE_LINK_CHOICE:
pSigInfo->lpszMoreInfoLink = NConvertW2T(pOpus->pMoreInfo->pwszFile);
break;
}
}
}
if(pOpus != NULL)
NHeapFree(pOpus);
break;
}
CERT_INFO ci;
ci.Issuer = pSignerInfo->Issuer;
ci.SerialNumber = pSignerInfo->SerialNumber;
PCCERT_CONTEXT pCertContext = CertFindCertificateInStore(hStore, SIG_ENCODING, 0, CERT_FIND_SUBJECT_CERT, (PVOID)&ci, NULL);
if(pCertContext != NULL)
{
if(pCertContext->pCertInfo->SerialNumber.cbData != 0)
{
pSigInfo->lpszSerial = (LPTSTR)NHeapAlloc(((pCertContext->pCertInfo->SerialNumber.cbData * 2) + 1) * sizeof(TCHAR));
if(pSigInfo->lpszSerial != NULL)
{
LPTSTR lpszPointer = pSigInfo->lpszSerial;
for(DWORD dwCount = pCertContext->pCertInfo->SerialNumber.cbData; dwCount != 0; dwCount--)
lpszPointer += _stprintf(lpszPointer, _T("%02X"), pCertContext->pCertInfo->SerialNumber.pbData[dwCount - 1]);
}
}
if(!NCertGetNameString(pCertContext, CERT_NAME_FRIENDLY_DISPLAY_TYPE, CERT_NAME_ISSUER_FLAG, &pSigInfo->lpszFriendlyName))
pSigInfo->lpszFriendlyName = NULL;
if(!NCertGetNameString(pCertContext, CERT_NAME_SIMPLE_DISPLAY_TYPE, CERT_NAME_ISSUER_FLAG, &pSigInfo->lpszAuthority))
pSigInfo->lpszAuthority = NULL;
if(!NCertGetNameString(pCertContext, CERT_NAME_SIMPLE_DISPLAY_TYPE, 0, &pSigInfo->lpszPublisher))
pSigInfo->lpszPublisher = NULL;
if(!NCertGetNameString(pCertContext, CERT_NAME_URL_TYPE, 0, &pSigInfo->lpszPublisherUrl))
pSigInfo->lpszPublisherUrl = NULL;
if(!NCertGetNameString(pCertContext, CERT_NAME_EMAIL_TYPE, 0, &pSigInfo->lpszPublisherEmail))
pSigInfo->lpszPublisherEmail = NULL;
CertFreeCertificateContext(pCertContext);
}
for(DWORD dwAttr = 0, dwData; dwAttr < pSignerInfo->AuthAttrs.cAttr; dwAttr++)
{
if((strcmp(szOID_RSA_signingTime, pSignerInfo->AuthAttrs.rgAttr[dwAttr].pszObjId) == 0) && (pSignerInfo->AuthAttrs.rgAttr[dwAttr].cValue != 0))
{
FILETIME ftCert;
dwData = sizeof(FILETIME);
if(NCryptDecodeObject(szOID_RSA_signingTime, &pSignerInfo->AuthAttrs.rgAttr[dwAttr].rgValue[0], dwData, (PVOID)&ftCert))
{
FILETIME ftLocal;
if(!FileTimeToLocalFileTime(&ftCert, &ftLocal))
{
if(!FileTimeToSystemTime(&ftLocal, &pSigInfo->stSigTime))
memset(&pSigInfo->stSigTime, 0, sizeof(SYSTEMTIME));
}
}
}
}
for(DWORD dwAttr = 0; dwAttr < pSignerInfo->UnauthAttrs.cAttr; dwAttr++)
{
if(strcmp(pSignerInfo->UnauthAttrs.rgAttr[dwAttr].pszObjId, szOID_RSA_counterSign) == 0)
{
if(NCryptDecodeObject(PKCS7_SIGNER_INFO, &pSignerInfo->UnauthAttrs.rgAttr[dwAttr].rgValue[0], dwCounterSignerInfo) && (dwCounterSignerInfo != 0))
pCounterSignerInfo = (PCMSG_SIGNER_INFO)NHeapAlloc(dwCounterSignerInfo);
if((pCounterSignerInfo != NULL) && !NCryptDecodeObject(PKCS7_SIGNER_INFO, &pSignerInfo->UnauthAttrs.rgAttr[dwAttr].rgValue[0], dwCounterSignerInfo, pCounterSignerInfo))
pCounterSignerInfo = (PCMSG_SIGNER_INFO)NHeapFree(pCounterSignerInfo);
break;
}
}
if(pCounterSignerInfo != NULL)
{
pSigInfo->bHasSigTime = WGetSignTimestamp(&pCounterSignerInfo->AuthAttrs, pSigInfo->stSigTime, szOID_RSA_signingTime);
if(!pSigInfo->bHasSigTime)
memset(&pSigInfo->stSigTime, 0, sizeof(SYSTEMTIME));
}
}
if(pSignerInfo != NULL)
NHeapFree(pSignerInfo);
if(pCounterSignerInfo != NULL)
NHeapFree(pCounterSignerInfo);
if(hStore != NULL)
CertCloseStore(hStore, 0);
if(hMsg != NULL)
CryptMsgClose(hMsg);
return TRUE;
}
//////////////////////////////////////////////////////////////////////////
BOOL NVerifyFileSignature(LPCTSTR lpszFileName, NSIGINFO *pSigInfo, HANDLE hHandle)
{
if(pSigInfo != NULL)
memset(pSigInfo, 0, sizeof(NSIGINFO));
if(lpszFileName == NULL)
return FALSE;
if((lpszFileName[0] != 0) && (_tcsnicmp(lpszFileName, _T("\\??\\"), 4) == 0))
lpszFileName += 4;
if(lpszFileName[0] == 0)
return FALSE;
LPWSTR lpwszFileName = NConvertT2W(lpszFileName);
if(lpwszFileName == NULL)
return FALSE;
BOOL bOK = FALSE;
__try
{ // be very careful...
WINTRUST_FILE_INFO wtFileInfo;
memset(&wtFileInfo, 0, sizeof(WINTRUST_FILE_INFO));
wtFileInfo.cbStruct = sizeof(WINTRUST_FILE_INFO);
wtFileInfo.pcwszFilePath = lpwszFileName;
if(hHandle != INVALID_HANDLE_VALUE)
wtFileInfo.hFile = hHandle;
WINTRUST_DATA wtData;
memset(&wtData, 0, sizeof(WINTRUST_DATA));
wtData.cbStruct = sizeof(WINTRUST_DATA);
wtData.dwUIChoice = WTD_UI_NONE;
wtData.fdwRevocationChecks = WTD_REVOKE_WHOLECHAIN;
wtData.dwUnionChoice = WTD_CHOICE_FILE;
wtData.pFile = &wtFileInfo;
if(NVerifyFileSignatureWorker(lpwszFileName, wtData, pSigInfo))
bOK = TRUE;
}
__except(EXCEPTION_EXECUTE_HANDLER)
{
if(pSigInfo != NULL)
{
if(pSigInfo->lpszPublisher)
pSigInfo->lpszPublisher = (LPTSTR)NHeapFree(pSigInfo->lpszPublisher);
if(pSigInfo->lpszAuthority)
pSigInfo->lpszAuthority = (LPTSTR)NHeapFree(pSigInfo->lpszAuthority);
if(pSigInfo->lpszProgramName)
pSigInfo->lpszProgramName = (LPTSTR)NHeapFree(pSigInfo->lpszPublisher);
if(pSigInfo->lpszPublisherLink)
pSigInfo->lpszPublisherLink = (LPTSTR)NHeapFree(pSigInfo->lpszPublisher);
if(pSigInfo->lpszMoreInfoLink)
pSigInfo->lpszMoreInfoLink = (LPTSTR)NHeapFree(pSigInfo->lpszMoreInfoLink);
if(pSigInfo->lpszSignature)
pSigInfo->lpszSignature = (LPTSTR)NHeapFree(pSigInfo->lpszSignature);
if(pSigInfo->lpszSerial)
pSigInfo->lpszSerial = (LPTSTR)NHeapFree(pSigInfo->lpszSerial);
}
bOK = FALSE;
}
NHeapFree(lpwszFileName);
return bOK;
}
//////////////////////////////////////////////////////////////////////////
BOOL NCheckFileCertificates(HANDLE hFile, VOID (*pCallback)(PCCERT_CONTEXT, LPVOID), PVOID pParam)
{
DWORD dwCerts = 0;
if(!ImageEnumerateCertificates(hFile, CERT_SECTION_TYPE_ANY, &dwCerts, NULL, 0))
return FALSE;
for(DWORD dwCount = 0; dwCount < dwCerts; dwCount++)
{
WIN_CERTIFICATE wcHdr;
memset(&wcHdr, 0, sizeof(WIN_CERTIFICATE));
wcHdr.dwLength = 0;
wcHdr.wRevision = WIN_CERT_REVISION_1_0;
if(!ImageGetCertificateHeader(hFile, dwCount, &wcHdr))
return FALSE;
DWORD dwLen = sizeof(WIN_CERTIFICATE) + wcHdr.dwLength;
WIN_CERTIFICATE *pWinCert = (WIN_CERTIFICATE *)NHeapAlloc(dwLen);
if(pWinCert == NULL)
return FALSE;
if(!ImageGetCertificateData(hFile, dwCount, pWinCert, &dwLen))
{ // problem getting certificate, return failure
NHeapFree(pWinCert);
return FALSE;
}
// extract the PKCS7 signed data
CRYPT_VERIFY_MESSAGE_PARA cvmp;
memset(&cvmp, 0, sizeof(CRYPT_VERIFY_MESSAGE_PARA));
cvmp.cbSize = sizeof(CRYPT_VERIFY_MESSAGE_PARA);
cvmp.dwMsgAndCertEncodingType = SIG_ENCODING;
PCCERT_CONTEXT pCertContext = NULL;
if(!CryptVerifyMessageSignature(&cvmp, dwCount, pWinCert->bCertificate, pWinCert->dwLength, NULL, NULL, &pCertContext))
{
NHeapFree(pWinCert);
return FALSE;
}
// Now, pass this context on to our callback function (if any)
if(pCallback != NULL)
pCallback(pCertContext, pParam);
if(!CertFreeCertificateContext(pCertContext))
{
NHeapFree(pWinCert);
return FALSE;
}
NHeapFree(pWinCert);
}
return TRUE;
}
Microsoft provides a way to do it in this support link: How To Get Information from Authenticode Signed Executables
You can use the WinVerifyTrust() API to verify an Authenticode signed
executable.
Although a signature is verified, a program may also have to do the
following:
Determine the details of the certificate that signed the
executable.
Determine the date and time that the file was time
stamped.
Retrieve the URL link associated with the file.
Retrieve the timestamp certificate.
This article demonstrates how to use
CryptQueryObject() API to retrieve detailed information from an
Authenticode signed executable.

Heap allocation problems

I am running into memory errors when I try to run my C++ program in Visual Studio 2012. I am thinking that this code is the cause (since when I remove it, it runs fine):
void GetMachineHash(CString &strHashHex) {
CMD5 cMD5;
BYTE *szHash = (BYTE*)malloc(48);
LPBYTE szMachineNameHash, szNetworkAddressHash, szVolumeIdHash;
TCHAR szMachineId[100];
DWORD nMachineIdLen = 100;
TCHAR szNetworkAddress[13];
IP_ADAPTER_INFO *pAdapterInfo, *pAdapter = NULL;
DWORD dwRetVal = 0;
ULONG ulOutBufLen = sizeof(IP_ADAPTER_INFO);
TCHAR szVolumeId[20];
TCHAR szVolumeName[MAX_PATH];
TCHAR szFileSystemName[MAX_PATH];
DWORD dwSerialNumber = 0;
DWORD dwMaxComponentLen = 0;
DWORD dwFileSystemFlags = 0;
ZeroMemory(szHash, 48);
ZeroMemory(szMachineId, 100);
ZeroMemory(szVolumeId, 20);
ZeroMemory(szVolumeName, MAX_PATH);
ZeroMemory(szFileSystemName, MAX_PATH);
ZeroMemory(szNetworkAddress, 13);
GetComputerName(szMachineId, &nMachineIdLen);
cMD5.Calculate(szMachineId);
szMachineNameHash = cMD5.Hash();
pAdapterInfo = (IP_ADAPTER_INFO *) malloc(sizeof(IP_ADAPTER_INFO));
if (pAdapterInfo == NULL) {
TRACE(_T("Error allocating memory needed to call GetAdaptersinfo()"));
szNetworkAddressHash = NULL;
}
// Make an initial call to GetAdaptersInfo to get the necessary size into the ulOutBufLen variable
if (GetAdaptersInfo(pAdapterInfo, &ulOutBufLen) == ERROR_BUFFER_OVERFLOW) {
free(pAdapterInfo);
pAdapterInfo = (IP_ADAPTER_INFO *)malloc(ulOutBufLen);
if (pAdapterInfo == NULL) {
TRACE(_T("Error allocating memory needed to call GetAdaptersinfo()"));
szNetworkAddressHash = NULL;
}
}
if ((dwRetVal = GetAdaptersInfo(pAdapterInfo, &ulOutBufLen)) == NO_ERROR) {
pAdapter = pAdapterInfo;
while (pAdapter) {
if (pAdapter->Type != MIB_IF_TYPE_LOOPBACK) {
_stprintf_s(szNetworkAddress, 13, _T("%.2X%.2X%.2X%.2X%.2X%.2X"),
pAdapter->Address[0],
pAdapter->Address[1],
pAdapter->Address[2],
pAdapter->Address[3],
pAdapter->Address[4],
pAdapter->Address[5]
);
break;
}
pAdapter = pAdapter->Next;
}
} else {
TRACE(_T("GetAdaptersInfo() call failed"));
szNetworkAddressHash = NULL;
}
cMD5.Calculate(szNetworkAddress);
szNetworkAddressHash = cMD5.Hash();
if (GetVolumeInformation(
NULL,
szVolumeName,
sizeof(szVolumeName),
&dwSerialNumber,
&dwMaxComponentLen,
&dwFileSystemFlags,
szFileSystemName,
sizeof(szFileSystemName))) {
_stprintf_s(szVolumeId, 20, _T("%lu"), dwSerialNumber);
}
cMD5.Calculate(szVolumeId);
szVolumeIdHash = cMD5.Hash();
// Calculate hash from hashes
memcpy(szHash, szMachineNameHash, 16);
memcpy(szHash+16, szNetworkAddressHash, 16);
memcpy(szHash+32, szVolumeIdHash, 16);
cMD5.Calculate(szHash, 48);
strHashHex.Preallocate(33);
strHashHex = cMD5.HexHash();
free(szHash);
free(pAdapterInfo);
return;
}
And then if I leave the function and just remove this code:
strHashHex.Preallocate(33);
strHashHex = cMD5.HexHash();
Then it will work fine as well. So I am wondering if that is the code that's causing the memory problems, and if it is, how can I fix it?
Here's the CMD5 class (which utilizes the Windows API to generate a MD5 sum):
class CMD5
{
public:
CMD5() {
if(CryptAcquireContext(&m_hCryptProv, NULL, MS_ENHANCED_PROV, PROV_RSA_FULL, CRYPT_NEWKEYSET) == 0){
if(GetLastError() == NTE_EXISTS){
CryptAcquireContext(&m_hCryptProv, NULL, MS_ENHANCED_PROV, PROV_RSA_FULL, 0);
}
}
}
~CMD5() {
if(m_hCryptProv)
CryptReleaseContext(m_hCryptProv, 0);
m_hCryptProv = NULL;
free(m_szHash);
}
bool Calculate(LPCTSTR szText) {
DWORD dwLen = sizeof(TCHAR) * _tcslen(szText);
DWORD dwHashLen;
DWORD dwHashLenSize = sizeof(DWORD);
if (CryptCreateHash(m_hCryptProv, CALG_MD5, 0, 0, &m_hHash)) {
if (CryptHashData(m_hHash, (const BYTE*)szText, dwLen, 0)) {
if (CryptGetHashParam(m_hHash, HP_HASHSIZE, (BYTE *)&dwHashLen, &dwHashLenSize, 0)) {
if(m_szHash = (BYTE*)malloc(dwHashLen)) {
if (CryptGetHashParam(m_hHash, HP_HASHVAL, (BYTE*)m_szHash, &dwHashLen, 0)) {
CryptDestroyHash(m_hHash);
}
}
}
}
}
return false;
}
bool Calculate(const LPBYTE szText, DWORD dwLen) {
DWORD dwHashLen;
DWORD dwHashLenSize = sizeof(DWORD);
if (CryptCreateHash(m_hCryptProv, CALG_MD5, 0, 0, &m_hHash)) {
if (CryptHashData(m_hHash, (const BYTE*)szText, dwLen, 0)) {
if (CryptGetHashParam(m_hHash, HP_HASHSIZE, (BYTE *)&dwHashLen, &dwHashLenSize, 0)) {
if(m_szHash = (BYTE*)malloc(dwHashLen)) {
if (CryptGetHashParam(m_hHash, HP_HASHVAL, (BYTE*)m_szHash, &dwHashLen, 0)) {
CryptDestroyHash(m_hHash);
}
}
}
}
}
return false;
}
LPBYTE Hash() const {
LPBYTE szHash = new BYTE[16];
ZeroMemory(szHash, 16);
memcpy(szHash, m_szHash, 16);
return szHash;
}
LPTSTR HexHash() const {
LPTSTR szBuf = new TCHAR[33];
ZeroMemory(szBuf, 33);
for (int i=0; i<16; i++)
_stprintf_s(szBuf+i*2, 33, _T("%02X"), m_szHash[i]);
szBuf[32]=0;
return szBuf;
}
private:
BYTE *m_szHash;
DWORD m_hHash;
HCRYPTPROV m_hCryptProv;
};
Also, the error I get from VS2012 is Critical error detected c0000374 and the call stack ends with a call to HeapAlloc() from _heap_alloc. Not sure if it matters but this code is being called in a DLL.
It looks like I was able to solve the memory allocation problems by changing the CMD5::HexHash() function to
void HexHash(CString &strHash) {
for (int i=0; i<16; i++)
strHash += StringFormat(_T("%02X"), m_szHash[i]);
return;
}
and call it via cMD5.HexHash(strHashHex);