On opening a directory via zwopenfile(open directory option), it returns a handle for the directory path. Now I need to get the directory path from the handle. Its my requirement.
I could see an example(please see the code below) where file name can be fetched from file handle. But the below example does not help for directory. Is there any possibilities in fetching directory name from its opened handle.
CHAR* GetFileNameFromHandle(HANDLE hFile)
{
BOOL bSuccess = FALSE;
TCHAR pszFilename[MAX_PATH+1];
HANDLE hFileMap;
// Get the file size.
DWORD dwFileSizeHi = 0;
DWORD dwFileSizeLo = GetFileSize(hFile, &dwFileSizeHi);
if( dwFileSizeLo == 0 && dwFileSizeHi == 0 )
{
printf("Cannot map a file with a length of zero.\n");
return FALSE;
}
// Create a file mapping object.
hFileMap = CreateFileMappingW(hFile,
NULL,
PAGE_READONLY,
0,
1,
NULL);
if (hFileMap)
{
// Create a file mapping to get the file name.
void* pMem = MapViewOfFile(hFileMap, FILE_MAP_READ, 0, 0, 1);
if (pMem)
{
if (GetMappedFileNameW (GetCurrentProcess(),
pMem,
pszFilename,
MAX_PATH))
{
// Translate path with device name to drive letters.
TCHAR szTemp[1024];
szTemp[0] = '\0';
if (GetLogicalDriveStrings(1024-1, szTemp))
{
TCHAR szName[MAX_PATH];
TCHAR szDrive[3] = TEXT(" :");
BOOL bFound = FALSE;
TCHAR* p = szTemp;
do
{
// Copy the drive letter to the template string
*szDrive = *p;
// Look up each device name
if (QueryDosDevice(szDrive, szName, MAX_PATH))
{
UINT uNameLen = _tcslen(szName);
if (uNameLen < MAX_PATH)
{
bFound = _tcsnicmp(pszFilename, szName, uNameLen) == 0;
if (bFound && *(pszFilename + uNameLen) == _T('\\'))
{
// Reconstruct pszFilename using szTempFile
// Replace device path with DOS path
TCHAR szTempFile[MAX_PATH];
StringCchPrintf(szTempFile,
MAX_PATH,
TEXT("%s%s"),
szDrive,
pszFilename+uNameLen);
StringCchCopyN(pszFilename, MAX_PATH+1, szTempFile, _tcslen(szTempFile));
}
}
}
// Go to the next NULL character.
while (*p++);
} while (!bFound && *p); // end of string
}
}
bSuccess = TRUE;
UnmapViewOfFile(pMem);
}
CloseHandle(hFileMap);
}
_tprintf(TEXT("File name is %s\n"), pszFilename);
return( pszFilename);
}
NtQueryObject did it.
The example from MSDN, which you posted, gives a 'fully qualified' file name, i.e. with the drive letter and the full path.
With that, it should be easy to get the directory name: just strip off everything after the last \.
Related
I have developed a program that is a simple file manager. For example, it lets a user create a file, delete a file, see the attribute of the files, change directory, and etc based on a CIFS path. However, I have found out a problem when I was implementing the cd command.
I have defined a global variable which will be initialized with a new path when I call FmCommandChangeDirectory. Now I wanted to check the existence of a directory/folder before I change the global working path to the new directory. I have implemented the following function but it has not able to find out a folder exists or not.
bool ChangePathRoot(const char* arg_computer_name)
{
strcpy_s(g_c_RootDrive, "C:\\");
strcpy_s(g_c_CurrentPath, "\\\\");
strcat(g_c_CurrentPath, arg_computer_name);
strcpy_s(g_c_ShareName, "\\C$");
strcat(g_c_CurrentPath, g_c_ShareName);
strcpy_s(g_c_SystemModifiablePath, "\\");
strcat(g_c_CurrentPath, g_c_SystemModifiablePath);
return true;
}
void ChangePathReverseBack(const char* arg_computer_name)
{
char c_PathBuffer[_MAX_PATH];
char c_Drive[_MAX_DRIVE];
char c_DirectoryMain[_MAX_DIR], c_DirectoryTemp[_MAX_DIR];
char c_Filename[_MAX_FNAME];
char c_Extension[_MAX_EXT];
strcpy(c_PathBuffer, g_c_CurrentPath);
_splitpath(c_PathBuffer, c_Drive, c_DirectoryMain, c_Filename, c_Extension);
_splitpath(c_PathBuffer, c_Drive, c_DirectoryTemp, c_Filename, c_Extension);
char* c_MainToken = strtok(c_DirectoryMain, "\\");
int i_StringsCounter = 0;
while (c_MainToken != NULL)
{
c_MainToken = strtok(NULL, "\\");
i_StringsCounter++;
}
char* c_TempToken = strtok(c_DirectoryTemp, "\\");
int i_StringsCounterTemp = i_StringsCounter - 1;
char c_FinalModifiedPath[MAX_PATH];
strcpy(c_FinalModifiedPath, "\\\\");
for (size_t i = 0; i < i_StringsCounterTemp; i++)
{
strcat(c_FinalModifiedPath, c_TempToken);
strcat(c_FinalModifiedPath, "\\");
c_TempToken = strtok(NULL, "\\");
}
char* c_TokenBase = NULL;
char* c_TokenDir = NULL;
c_TokenBase = strtok_s(c_FinalModifiedPath, "\\", &c_TokenDir);
char* c_TokenRoot = NULL;
char* c_TokenDirectory = NULL;
c_TokenRoot = strtok_s(c_TokenDir, "$", &c_TokenDirectory);
ChangePathSecondary(c_TokenRoot, arg_computer_name, c_TokenDirectory);
}
bool ChangePathRelative(const char* arg_directory)
{
strcat(g_c_SystemModifiablePath, arg_directory);
strcat(g_c_SystemModifiablePath, "\\");
strcat(g_c_CurrentPath, arg_directory);
strcat(g_c_CurrentPath, "\\");
return true;
}
void FmCommandChangeDirectory(const char* arg_computer_name)
{
char c_PathBuffer[_MAX_PATH];
char c_Drive[_MAX_DRIVE];
char c_DirectoryMain[_MAX_DIR];
char c_Filename[_MAX_FNAME];
char c_Extension[_MAX_EXT];
scanf_s("%s", c_PathBuffer, MAX_PATH - 1);
_splitpath(c_PathBuffer, c_Drive, c_DirectoryMain, c_Filename, c_Extension);
if (!strcmp(c_PathBuffer, "\\") || !strcmp(c_PathBuffer, "/"))
{
ChangePathRoot(arg_computer_name);
}
else if (!strcmp(c_PathBuffer, ".."))
{
ChangePathReverseBack(arg_computer_name);
}
else
{
ChangePathRelative(c_PathBuffer);
}
}
Now I wanted to implement a function that checks a path (directory) that has exist or not but I don't know how Can I Implement such function to check a directory has been existed or not.
There are two APIs for you to use:
In addition to GetFileAttributes mentioned by #IInspectable, you can also refer PathFileExists.
GetFileAttributes:
Retrieves file system attributes for a specified file or directory.
Code:
DWORD dwAttrib = GetFileAttributes(directorypath);
if(dwAttrib != INVALID_FILE_ATTRIBUTES &&(dwAttrib & FILE_ATTRIBUTE_DIRECTORY))
{
//exist
}
else
{
//not a vaild path
}
PathFileExists:
Determines whether a path to a file system object such as a file or
folder is valid.
Code:
BOOL retval = PathFileExists(directorypath);
if(retval == 1)
{
//file exists
}
else
{
//not a vaild file
}
I've been looking around for methods by which a directory can be monitored for file creation/modification etc. however all the previous posts I've found for Windows are C++ specific.
Microsoft does list ReadDirectoryChangesW, but this too is for C++ (I haven't the knowledge to assess whether these are compatible for C). I've only knowledge with inotify for Linux, which is fairly straightforward, and wondered if there are any simple examples of the Windows equivalent? (I do not want to use inotify on Windows despite it technically being achievable).
If you are just looking for methods, maybe this will help a bit:
https://www.geeksforgeeks.org/c-program-list-files-sub-directories-directory/
(just copy-pasted the code in case)
Tested it on linux machine and it seems to work. Not recursive though.
int main(void)
{
struct dirent *de; /* Pointer for directory entry */
/* opendir() returns a pointer of DIR type. */
DIR *dr = opendir(".");
if (dr == NULL) /* opendir returns NULL if couldn't open directory */
{
printf("Could not open current directory" );
return 0;
}
/* Refer http://pubs.opengroup.org/onlinepubs/7990989775/xsh/readdir.html
for readdir() */
while ((de = readdir(dr)) != NULL)
printf("%s\n", de->d_name);
closedir(dr);
return 0;
}
Also, see this question if you need to check if a listed file is a directory:
Checking if a dir. entry returned by readdir is a directory, link or file
This method may not be as portable as it seems, but worth a try.
Cheers!
Using FindFirstFile to hit the first node of certain directory, then to call FindNextFile to iterate files one by one inside one directory layer.
Here is my sample code for your reference, there is a recursive funcion.
#include "stdafx.h"
#include <string>
static void iterate_dir(std::string dir) {
WIN32_FIND_DATA fd;
HANDLE hFind;
std::wstring fn_ws;
std::string fn;
int pos = 0;
int count_bg = 0;
int count_fg = 0;
std::string dir_bkp = dir;
std::string dir_sub;
std::string str_wide_char_for_any = "*.*";
std::string str_folder_node = "..";
if (dir.length() - dir.find_last_of("\\") > 1) //dir ends without "\\"
dir += "\\";
dir += str_wide_char_for_any;
std::wstring dir_wstr = std::wstring(dir.begin(), dir.end());
LPCWSTR dir_wc = dir_wstr.c_str();
hFind = FindFirstFile(dir_wc, &fd);
if (hFind == INVALID_HANDLE_VALUE) {
FindClose(hFind);
return;
}
while(true) {
if (!FindNextFile(hFind, &fd)) {
break;
}
if ((fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {
fn_ws = std::wstring(fd.cFileName);
fn = std::string(fn_ws.begin(), fn_ws.end());
if (fn.compare(str_folder_node) == 0) {
continue;
}
else {
if ((pos = dir.rfind(str_wide_char_for_any)) != std::string::npos) {
dir_sub = dir;
dir_sub = dir_sub.replace(dir_sub.begin()+pos, dir_sub.end(), fn.begin(), fn.end());
}
else if (dir.length() - (pos = dir.rfind("\\")) > 1) {
dir_sub = dir;
dir_sub += "\\";
dir_sub += fn;
}
else {
dir_sub = dir;
dir_sub += fn;
}
printf("[%s][directory]:%s\n", __func__, dir.c_str());
iterate_dir(dir_sub);
continue;
}
}
else if (fd.dwFileAttributes & FILE_ATTRIBUTE_ARCHIVE) {
fn_ws = std::wstring(fd.cFileName);
fn = std::string(fn_ws.begin(), fn_ws.end());
printf("[%s][file]:%s\n", __func__, fn.c_str());
}
else {
fn_ws = std::wstring(fd.cFileName);
fn = std::string(fn_ws.begin(), fn_ws.end());
printf("[%s][unspecified attribute file]:%s\n", __func__, fn.c_str());
}
}
FindClose(hFind);
return;
}
You can have a main.cpp like:
int main() {
std::string dir_name("C:\\test");
iterate_dir(dir);
return 0;
}
I'm sure this has been addressed in the past. My apologies.
I want to understand why I'm getting error LNK2019.
And what direction to take, to get this resolved.
Errors are LNK2019:
unresolved external symbol __imp__ReportError referenced in function _wmain
unresolved external symbol __imp__Options referenced in function _wmain
This occurs when I build solution in Visual Studio 2010 and up.
Contents of header file for the above methods are described like so:
LIBSPEC DWORD Options (int, LPCTSTR *, LPCTSTR, ...);
LIBSPEC DWORD Options (int, LPCTSTR *, LPCTSTR, ...);
Main code:
#include "Everything.h"
BOOL TraverseDirectory(LPTSTR, LPTSTR, DWORD, LPBOOL);
DWORD FileType(LPWIN32_FIND_DATA);
BOOL ProcessItem(LPWIN32_FIND_DATA, DWORD, LPBOOL);
int _tmain(int argc, LPTSTR argv[])
{
BOOL flags[MAX_OPTIONS], ok = TRUE;
TCHAR searchPattern[MAX_PATH + 1], currPath[MAX_PATH_LONG+1], parentPath[MAX_PATH_LONG+1];
LPTSTR pSlash, pSearchPattern;
int i, fileIndex;
DWORD pathLength;
fileIndex = Options(argc, argv, _T("Rl"), &flags[0], &flags[1], NULL);
pathLength = GetCurrentDirectory(MAX_PATH_LONG, currPath);
if (pathLength == 0 || pathLength >= MAX_PATH_LONG) { /* pathLength >= MAX_PATH_LONG (32780) should be impossible */
ReportError(_T("GetCurrentDirectory failed"), 1, TRUE);
}
if (argc < fileIndex + 1)
ok = TraverseDirectory(currPath, _T("*"), MAX_OPTIONS, flags);
else for (i = fileIndex; i < argc; i++) {
if (_tcslen(argv[i]) >= MAX_PATH) {
ReportError(_T("The command line argument is longer than the maximum this program supports"), 2, FALSE);
}
_tcscpy(searchPattern, argv[i]);
_tcscpy(parentPath, argv[i]);
pSlash = _tstrrchr(parentPath, _T('\\'));
if (pSlash != NULL) {
*pSlash = _T('\0');
_tcscat(parentPath, _T("\\"));
SetCurrentDirectory(parentPath);
pSlash = _tstrrchr(searchPattern, _T('\\'));
pSearchPattern = pSlash + 1;
} else {
_tcscpy(parentPath, _T(".\\"));
pSearchPattern = searchPattern;
}
ok = TraverseDirectory(parentPath, pSearchPattern, MAX_OPTIONS, flags) && ok;
SetCurrentDirectory(currPath);
}
return ok ? 0 : 1;
}
static BOOL TraverseDirectory(LPTSTR parentPath, LPTSTR searchPattern, DWORD numFlags, LPBOOL flags)
{
HANDLE searchHandle;
WIN32_FIND_DATA findData;
BOOL recursive = flags[0];
DWORD fType, iPass, lenParentPath;
TCHAR subdirectoryPath[MAX_PATH + 1];
/* Open up the directory search handle and get the
first file name to satisfy the path name.
Make two passes. The first processes the files
and the second processes the directories. */
if ( _tcslen(searchPattern) == 0 ) {
_tcscat(searchPattern, _T("*"));
}
/* Add a backslash, if needed, at the end of the parent path */
if (parentPath[_tcslen(parentPath)-1] != _T('\\') ) { /* Add a \ to the end of the parent path, unless there already is one */
_tcscat (parentPath, _T("\\"));
}
/* Open up the directory search handle and get the
first file name to satisfy the path name. Make two passes.
The first processes the files and the second processes the directories. */
for (iPass = 1; iPass <= 2; iPass++) {
searchHandle = FindFirstFile(searchPattern, &findData);
if (searchHandle == INVALID_HANDLE_VALUE) {
ReportError(_T("Error opening Search Handle."), 0, TRUE);
return FALSE;
}
do {
fType = FileType(&findData);
if (iPass == 1) /* ProcessItem is "print attributes". */
ProcessItem(&findData, MAX_OPTIONS, flags);
lenParentPath = (DWORD)_tcslen(parentPath);
/* Traverse the subdirectory on the second pass. */
if (fType == TYPE_DIR && iPass == 2 && recursive) {
_tprintf(_T("\n%s%s:"), parentPath, findData.cFileName);
SetCurrentDirectory(findData.cFileName);
if (_tcslen(parentPath) + _tcslen(findData.cFileName) >= MAX_PATH_LONG-1) {
ReportError(_T("Path Name is too long"), 10, FALSE);
}
_tcscpy(subdirectoryPath, parentPath);
_tcscat (subdirectoryPath, findData.cFileName); /* The parent path terminates with \ before the _tcscat call */
TraverseDirectory(subdirectoryPath, _T("*"), numFlags, flags);
SetCurrentDirectory(_T("..")); /* Restore the current directory */
}
/* Get the next file or directory name. */
} while (FindNextFile(searchHandle, &findData));
FindClose(searchHandle);
}
return TRUE;
}
static DWORD FileType(LPWIN32_FIND_DATA pFileData)
{
BOOL isDir;
DWORD fType;
fType = TYPE_FILE;
isDir =(pFileData->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
if (isDir)
if (lstrcmp(pFileData->cFileName, _T(".")) == 0
|| lstrcmp(pFileData->cFileName, _T("..")) == 0)
fType = TYPE_DOT;
else fType = TYPE_DIR;
return fType;
}
static BOOL ProcessItem(LPWIN32_FIND_DATA pFileData, DWORD numFlags, LPBOOL flags)
{
const TCHAR fileTypeChar[] = {_T(' '), _T('d')};
DWORD fType = FileType(pFileData);
BOOL longList = flags[1];
SYSTEMTIME lastWrite;
if (fType != TYPE_FILE && fType != TYPE_DIR) return FALSE;
_tprintf(_T("\n"));
if (longList) {
_tprintf(_T("%c"), fileTypeChar[fType - 1]);
_tprintf(_T("%10d"), pFileData->nFileSizeLow);
FileTimeToSystemTime(&(pFileData->ftLastWriteTime), &lastWrite);
_tprintf(_T(" %02d/%02d/%04d %02d:%02d:%02d"),
lastWrite.wMonth, lastWrite.wDay,
lastWrite.wYear, lastWrite.wHour,
lastWrite.wMinute, lastWrite.wSecond);
}
_tprintf(_T(" %s"), pFileData->cFileName);
return TRUE;
}
The error you're getting is from the linker, and it's telling you that it cannot find the definitions of the ReportError and Options functions (both of which are referenced from your main function).
You say that you have included a header file that contains these functions, but that header contains only the declarations of these functions. You know, like their signature. It doesn't have the body (implementation) of the functions. You need the definition for that.
Definitions for functions that you've written are usually located in *.cpp files. If you've written these functions, make sure that the code file that contains their definitions has been added to your project.
For functions that you have not written (i.e. are part of a reusable library of code), you usually provide the linker with a *.lib file that contains what it needs to call the functions. If these functions are from a library, make sure that you've added the *.lib file that came with them to the list of files that the linker will search. To do that in Visual Studio, follow these steps:
Right-click on your project in the Solution Explorer, and open its Properties.
Expand the "Linker" category, and select the "Input" node.
Click in the "Additional Dependencies" field, and press the ... button.
Enter the name of your *.lib file on a new line in the textbox that appears.
Just because you declared a function in a header file, doesn't mean it has a body anywhere.
The link error says the function doesn't have a body.
This is likely because you didn't link to the proper Library (a .lib file).
I am using an .ini file to store some values and retrieve values from it using the iniparser.
When I give (hardcode) the query and retrive the value through the command line, I am able to retrive the ini file and do some operation.
But when I pass the query through http, then I am getting an error (file not found), i.e., the ini file couldn't be loaded.
Command line :
int main(void)
{
printf("Content-type: text/html; charset=utf-8\n\n");
char* data = "/cgi-bin/set.cgi?pname=x&value=700&url=http://IP/home.html";
//perform some operation
}
Through http:
.html
function SetValue(id)
{
var val;
var URL = window.location.href;
if(id =="set")
{
document.location = "/cgi-bin/set.cgi?pname="+rwparams+"&value="+val+"&url="+URL;
}
}
.c
int * Value(char* pname)
{
dictionary * ini ;
char *key1 = NULL;
char *key2 =NULL;
int i =0;
int val;
ini = iniparser_load("file.ini");
if(ini != NULL)
{
//key for fetching the value
key1 = (char*)malloc(sizeof(char)*50);
if(key1 != NULL)
{
strcpy(key1,"ValueList:");
key2 = (char*)malloc(sizeof(char)*50);
if(key2 != NULL)
{
strcpy(key2,pname);
strcat(key1,key2);
val = iniparser_getint(ini, key1, -1);
if(-1 == val || 0 > val)
{
return 0;
}
}
else
{
//error
free(key1);
return;
}
}
else
{
printf("ERROR : Memory Allocation Failure ");
return;
}
}
else
{
printf("ERROR : .ini File Missing");
return;
}
iniparser_freedict(ini);
free(key1);
free(key2);
return (int *)val;
}
void get_Value(char* pname,char* value)
{
int result =0;
result = Value(pname);
printf("Result : %d",result);
}
int main(void)
{
printf("Content-type: text/html; charset=utf-8\n\n");
char* data = getenv("QUERY_STRING");
//char* data = "/cgi-bin/set.cgi?pname=x&value=700&url=http://10.50.25.40/home.html";
//Parse to get the values seperately as parameter name, parameter value, url
//Calling get_Value method to set the value
get_Value(final_para,final_val);
}
*
file.ini
*
[ValueList]
x = 100;
y = 70;
When the request is sent through html page, I am always getting .ini file missing. If directly the request is sent from C file them it works fine.
How to resolve this?
Perhaps you have a problem with encoding of the URL parameters? You can't just pass any arbitrary string through a URL - there are some characters that must be encoded. Read this page about URL encoding.
Showing the value of the data string in your C program could be of great help with solving your problem.
Update:
There could be a difference as to where your program executes when called by the web server or directly by you. Are you sure it's being executed with the same "current directory". Chances are it's different, and thus when you attempt to open the ini file you fail. Try to print out the current directory (i.e. using the getcwd function) and compare both cases.
I'm implementing a process elevation helper for Windows. It's a program that will run in elevated mode and launch other programs with administrator privileges without displaying additional UAC prompts. For security reasons, I want to make sure only binaries that are digitally signed with my company's Authenticode key can be executed.
The WinVerifyTrust function gets me halfway there, but it only ensures that a binary is signed by some key that is part of Microsoft's chain of trust. Is there a relatively simple way to perform the Authenticode verification AND ensure that it is signed by our private key?
I believe what you're looking for is CryptQueryObject.
With it you should be able to pull the involved certificate out of a PE, and do any additional checks you want.
By way of example, this will get you to a HCRYPTMSG. From there you can use CryptMsgGetParam to pull out whatever you want. I'd hoped to make something more 'robust', but these APIs are pretty hairy insomuch as they require a lot of branching to handle all their return cases.
So, here's a p/invoke-rific c# example (I started in C, but that was basically unreadable):
static class Crypt32
{
//Omitting flag constants; you can look these up in WinCrypt.h
[DllImport("CRYPT32.DLL", EntryPoint = "CryptQueryObject", CharSet = CharSet.Auto, SetLastError = true)]
public static extern bool CryptQueryObject(
int dwObjectType,
IntPtr pvObject,
int dwExpectedContentTypeFlags,
int dwExpectedFormatTypeFlags,
int dwFlags,
out int pdwMsgAndCertEncodingType,
out int pdwContentType,
out int pdwFormatType,
ref IntPtr phCertStore,
ref IntPtr phMsg,
ref IntPtr ppvContext);
}
class Program
{
static void Main(string[] args)
{
//Path to executable here
// I tested with MS-Office .exe's
string path = "";
int contentType;
int formatType;
int ignored;
IntPtr context = IntPtr.Zero;
IntPtr pIgnored = IntPtr.Zero;
IntPtr cryptMsg = IntPtr.Zero;
if (!Crypt32.CryptQueryObject(
Crypt32.CERT_QUERY_OBJECT_FILE,
Marshal.StringToHGlobalUni(path),
Crypt32.CERT_QUERY_CONTENT_FLAG_ALL,
Crypt32.CERT_QUERY_FORMAT_FLAG_ALL,
0,
out ignored,
out contentType,
out formatType,
ref pIgnored,
ref cryptMsg,
ref context))
{
int error = Marshal.GetLastWin32Error();
Console.WriteLine((new Win32Exception(error)).Message);
return;
}
//expecting '10'; CERT_QUERY_CONTENT_PKCS7_SIGNED_EMBED
Console.WriteLine("Context Type: " + contentType);
//Which implies this is set
Console.WriteLine("Crypt Msg: " + cryptMsg.ToInt32());
return;
}
To get the certificate information from signed code use this:
using System.Security.Cryptography.X509Certificates;
X509Certificate basicSigner = X509Certificate.CreateFromSignedFile(filename);
X509Certificate2 cert = new X509Certificate2(basicSigner);
Then you can get the cert details like this:
Console.WriteLine(cert.IssuerName.Name);
Console.WriteLine(cert.SubjectName.Name);
// etc
these are some of the nastiest APIs I've ever worked with
A word of warning: it's worse than you already thought.
At least since introducing SHA-256 signing (has this always been the case?), it's possible for Authenticode to have multiple signatures. They're not encoded as multiple signatures in the PKCS-7 signature message; instead, they're unauthenticated message attributes of type OID_NESTED_SIGNATURE, each containing another complete PKCS-7 signature message.
WinVerifyTrust will tell you the file is valid if any of the signatures are valid and come from a trusted certificate chain. However it won't tell you which of the signatures was valid. If you then use CryptQueryObject to read the full PKCS-7 message, and only look at the certificate for the primary signature (as in the code samples here and on MSDN), you're not necessarily looking at a verified certificate. The associated signature might not match the executable, and/or the certificate might not have a trusted CA chain.
If you're using the details of the primary signature to validate that the certificate is one your software trusts, you're vulnerable to a situation where WinVerifyTrust is trusting a secondary signature, but your code is checking the primary signature's certificate is what you expected, and you haven't noticed that the signature from the primary certificate is nonsense. An attacker could use your public certificate without owning its private key, combined with some other code-signing certificate issued to someone else, to bypass a publisher check this way.
From Win8 onwards, WinVerifyTrust can optionally validate specific signatures, so you should be able to iterate the signatures to find one that is valid and one that satisfies your requirements.
If you have to be Win7-compatible, though, as far as I know the best you can manage is MsiGetFileSignatureInformation. From experimentation (as for everything else here, the actual documentation is frustratingly woolly), it seems to return the trusted certificate when WinVerifyTrust trusts one. But if there isn't a trusted signature, it returns the primary signature's certificate anyway, so you still have to use WinVerifyTrust to check that first.
Of course there also plenty of possible time-of-check/time-of-use problems here.
found the solution here:
http://www.ucosoft.com/how-to-program-to-retrieve-the-authenticode-information.html
here it is with indentation:
#define _UNICODE 1
#define UNICODE 1
#include <windows.h>
#include <tchar.h>
#include <wincrypt.h>
#include <Softpub.h>
#include <stdio.h>
#include <stdlib.h>
#pragma comment (lib, "Crypt32")
// the Authenticode Signature is encode in PKCS7
#define ENCODING (X509_ASN_ENCODING | PKCS_7_ASN_ENCODING)
// Information structure of authenticode sign
typedef struct
{
LPWSTR lpszProgramName;
LPWSTR lpszPublisherLink;
LPWSTR lpszMoreInfoLink;
DWORD cbSerialSize;
LPBYTE lpSerialNumber;
LPTSTR lpszIssuerName;
LPTSTR lpszSubjectName;
}
SPROG_SIGNATUREINFO, *PSPROG_SIGNATUREINFO;
VOID GetProgAndPublisherInfo(PCMSG_SIGNER_INFO pSignerInfo, PSPROG_SIGNATUREINFO pInfo);
VOID GetCertificateInfo(HCERTSTORE hStore, PCMSG_SIGNER_INFO pSignerInfo, PSPROG_SIGNATUREINFO pInfo);
BOOL GetAuthenticodeInformation(LPCTSTR lpszFileName, PSPROG_SIGNATUREINFO pInfo)
{
HCERTSTORE hStore = NULL;
HCRYPTMSG hMsg = NULL;
PCMSG_SIGNER_INFO pSignerInfo = NULL;
DWORD dwSignerInfo;
BOOL bRet = FALSE;
__try
{
// as CryptQueryObject() only accept WCHAR file name, convert first
WCHAR wszFileName[MAX_PATH];
#ifdef UNICODE
if ( !lstrcpynW( wszFileName, lpszFileName, MAX_PATH))
__leave;
#else
if ( mbstowcs( wszFileName, lpszFileName, MAX_PATH) == -1)
__leave;
#endif
//Retrieve the Message Handle and Store Handle
DWORD dwEncoding, dwContentType, dwFormatType;
if ( !CryptQueryObject( CERT_QUERY_OBJECT_FILE, wszFileName,
CERT_QUERY_CONTENT_FLAG_PKCS7_SIGNED_EMBED,
CERT_QUERY_FORMAT_FLAG_BINARY, 0, &dwEncoding,
&dwContentType, &dwFormatType, &hStore,
&hMsg, NULL))
__leave;
//Get the length of SignerInfo
if ( !CryptMsgGetParam( hMsg, CMSG_SIGNER_INFO_PARAM, 0, NULL, &dwSignerInfo))
__leave;
// allocate the memory for SignerInfo
if ( !(pSignerInfo = (PCMSG_SIGNER_INFO)LocalAlloc( LPTR, dwSignerInfo)))
__leave;
// get the SignerInfo
if ( !CryptMsgGetParam( hMsg, CMSG_SIGNER_INFO_PARAM, 0, (PVOID)pSignerInfo, &dwSignerInfo))
__leave;
//get the Publisher from SignerInfo
GetProgAndPublisherInfo( pSignerInfo, pInfo);
//get the Certificate from SignerInfo
GetCertificateInfo( hStore, pSignerInfo, pInfo);
bRet = TRUE;
}
__finally
{
// release the memory
if (pSignerInfo != NULL) LocalFree(pSignerInfo);
if (hStore != NULL) CertCloseStore(hStore, 0);
if (hMsg != NULL) CryptMsgClose(hMsg);
}
return bRet;
}
LPWSTR AllocateAndCopyWideString(LPCWSTR inputString)
{
LPWSTR outputString = NULL;
// allocate the memory
outputString = (LPWSTR)VirtualAlloc(NULL, (wcslen(inputString) + 1) * sizeof(TCHAR), MEM_COMMIT, PAGE_READWRITE);
// copy
if (outputString != NULL)
{
lstrcpyW(outputString, inputString);
}
return outputString;
}
VOID GetProgAndPublisherInfo(PCMSG_SIGNER_INFO pSignerInfo, PSPROG_SIGNATUREINFO pInfo)
{
PSPC_SP_OPUS_INFO OpusInfo = NULL;
DWORD dwData;
__try
{
// query SPC_SP_OPUS_INFO_OBJID OID in Authenticated Attributes
for (DWORD n = 0; n < pSignerInfo->AuthAttrs.cAttr; n++)
{
if (lstrcmpA(SPC_SP_OPUS_INFO_OBJID, pSignerInfo->AuthAttrs.rgAttr[n].pszObjId) == 0)
{
// get the length of SPC_SP_OPUS_INFO
if ( !CryptDecodeObject(ENCODING,
SPC_SP_OPUS_INFO_OBJID,
pSignerInfo->AuthAttrs.rgAttr[n].rgValue[0].pbData,
pSignerInfo->AuthAttrs.rgAttr[n].rgValue[0].cbData,
0,
NULL,
&dwData))
__leave;
// allocate the memory for SPC_SP_OPUS_INFO
if ( !(OpusInfo = (PSPC_SP_OPUS_INFO)LocalAlloc(LPTR, dwData)))
__leave;
// get SPC_SP_OPUS_INFO structure
if ( !CryptDecodeObject(ENCODING,
SPC_SP_OPUS_INFO_OBJID,
pSignerInfo->AuthAttrs.rgAttr[n].rgValue[0].pbData,
pSignerInfo->AuthAttrs.rgAttr[n].rgValue[0].cbData,
0,
OpusInfo,
&dwData))
__leave;
// copy the Program Name of SPC_SP_OPUS_INFO to the return variable
if (OpusInfo->pwszProgramName)
{
pInfo->lpszProgramName = AllocateAndCopyWideString(OpusInfo->pwszProgramName);
}
else
pInfo->lpszProgramName = NULL;
// copy the Publisher Info of SPC_SP_OPUS_INFO to the return variable
if (OpusInfo->pPublisherInfo)
{
switch (OpusInfo->pPublisherInfo->dwLinkChoice)
{
case SPC_URL_LINK_CHOICE:
pInfo->lpszPublisherLink = AllocateAndCopyWideString(OpusInfo->pPublisherInfo->pwszUrl);
break;
case SPC_FILE_LINK_CHOICE:
pInfo->lpszPublisherLink = AllocateAndCopyWideString(OpusInfo->pPublisherInfo->pwszFile);
break;
default:
pInfo->lpszPublisherLink = NULL;
break;
}
}
else
{
pInfo->lpszPublisherLink = NULL;
}
// copy the More Info of SPC_SP_OPUS_INFO to the return variable
if (OpusInfo->pMoreInfo)
{
switch (OpusInfo->pMoreInfo->dwLinkChoice)
{
case SPC_URL_LINK_CHOICE:
pInfo->lpszMoreInfoLink = AllocateAndCopyWideString(OpusInfo->pMoreInfo->pwszUrl);
break;
case SPC_FILE_LINK_CHOICE:
pInfo->lpszMoreInfoLink = AllocateAndCopyWideString(OpusInfo->pMoreInfo->pwszFile);
break;
default:
pInfo->lpszMoreInfoLink = NULL;
break;
}
}
else
{
pInfo->lpszMoreInfoLink = NULL;
}
break; // we have got the information, break
}
}
}
__finally
{
if (OpusInfo != NULL) LocalFree(OpusInfo);
}
}
VOID GetCertificateInfo(HCERTSTORE hStore, PCMSG_SIGNER_INFO pSignerInfo, PSPROG_SIGNATUREINFO pInfo)
{
PCCERT_CONTEXT pCertContext = NULL;
__try
{
CERT_INFO CertInfo;
DWORD dwData;
// query Signer Certificate in Certificate Store
CertInfo.Issuer = pSignerInfo->Issuer;
CertInfo.SerialNumber = pSignerInfo->SerialNumber;
if ( !(pCertContext = CertFindCertificateInStore( hStore,
ENCODING, 0, CERT_FIND_SUBJECT_CERT,
(PVOID)&CertInfo, NULL)))
__leave;
dwData = pCertContext->pCertInfo->SerialNumber.cbData;
// SPROG_SIGNATUREINFO.cbSerialSize
pInfo->cbSerialSize = dwData;
// SPROG_SIGNATUREINFO.lpSerialNumber
pInfo->lpSerialNumber = (LPBYTE)VirtualAlloc(NULL, dwData, MEM_COMMIT, PAGE_READWRITE);
memcpy( pInfo->lpSerialNumber, pCertContext->pCertInfo->SerialNumber.pbData, dwData);
// SPROG_SIGNATUREINFO.lpszIssuerName
__try
{
// get the length of Issuer Name
if (!(dwData = CertGetNameString( pCertContext,
CERT_NAME_SIMPLE_DISPLAY_TYPE,
CERT_NAME_ISSUER_FLAG, NULL, NULL, 0)))
__leave;
// allocate the memory
if ( !(pInfo->lpszIssuerName = (LPTSTR)VirtualAlloc(NULL, dwData * sizeof(TCHAR), MEM_COMMIT, PAGE_READWRITE)))
__leave;
// get Issuer Name
if (!(CertGetNameString(pCertContext,
CERT_NAME_SIMPLE_DISPLAY_TYPE,
CERT_NAME_ISSUER_FLAG, NULL, pInfo->
lpszIssuerName, dwData)))
__leave;
}
__finally
{
}
// SPROG_SIGNATUREINFO.lpszSubjectName
__try
{
//get the length of Subject Name
if (!(dwData = CertGetNameString( pCertContext, CERT_NAME_SIMPLE_DISPLAY_TYPE, 0, NULL, NULL, 0)))
__leave;
// allocate the memory
if ( !(pInfo->lpszSubjectName = (LPTSTR)VirtualAlloc(NULL, dwData * sizeof(TCHAR), MEM_COMMIT, PAGE_READWRITE)))
__leave;
// get Subject Name
if (!(CertGetNameString( pCertContext, CERT_NAME_SIMPLE_DISPLAY_TYPE, 0, NULL, pInfo->lpszSubjectName, dwData)))
__leave;
}
__finally
{
}
}
__finally
{
if (pCertContext != NULL)
CertFreeCertificateContext(pCertContext);
}
}
int _tmain(int argc, TCHAR *argv[])
{
if (argc != 2)
{
_tprintf(_T("Usage: SignedFileInfo \n"));
return 0;
}
else
{
SPROG_SIGNATUREINFO SignInfo;
ZeroMemory(&SignInfo, sizeof(SignInfo));
GetAuthenticodeInformation( argv[1], &SignInfo);
wprintf(L"Program Name: %s\n", SignInfo.lpszProgramName);
wprintf(L"Publisher Link: %s\n", SignInfo.lpszPublisherLink);
wprintf(L"More Info Link: %s\n", SignInfo.lpszMoreInfoLink);
{
_tprintf(_T("Serial Number: "));
DWORD dwData = SignInfo.cbSerialSize;
for (DWORD n = 0; n < dwData; n++)
{
_tprintf(_T("%02x "),
SignInfo.lpSerialNumber[dwData - (n + 1)]);
}
_tprintf(_T("\n"));
}
_tprintf(_T("Issuer Name: %s\n"), SignInfo.lpszIssuerName);
_tprintf(_T("Subject Name: %s\n"), SignInfo.lpszSubjectName);
if ( SignInfo.lpszProgramName) VirtualFree(SignInfo.lpszProgramName, 0, MEM_RELEASE);
if ( SignInfo.lpszPublisherLink) VirtualFree(SignInfo.lpszPublisherLink, 0, MEM_RELEASE);
if ( SignInfo.lpszMoreInfoLink) VirtualFree(SignInfo.lpszMoreInfoLink, 0, MEM_RELEASE);
if ( SignInfo.lpSerialNumber) VirtualFree(SignInfo.lpSerialNumber, 0, MEM_RELEASE);
if ( SignInfo.lpszIssuerName) VirtualFree(SignInfo.lpszIssuerName, 0, MEM_RELEASE);
if ( SignInfo.lpszSubjectName) VirtualFree(SignInfo.lpszSubjectName, 0, MEM_RELEASE);
return 0;
}
}