Is CryptMsgGetParam enough to ensure file integrity? - winapi

I have a bunch of code currently to check if the PE is signed by my company but it only checks the signature (not written by me)
// hMsg was obtained earlier by using CryptQueryObject
DWORD dwSignerInfo;
bool ret = CryptMsgGetParam(hMsg,
CMSG_SIGNER_INFO_PARAM,
0,
NULL,
&dwSignerInfo);
PCMSG_SIGNER_INFO pSignerInfo = NULL;
pSignerInfo = (PCMSG_SIGNER_INFO)LocalAlloc(LPTR, dwSignerInfo);
ret = CryptMsgGetParam(hMsg,
CMSG_SIGNER_INFO_PARAM,
0,
(PVOID)pSignerInfo,
&dwSignerInfo);
std::vector<BYTE> fileSerial;
fileSerial.assign(pSignerInfo->SerialNumber.pbData, pSignerInfo->SerialNumber.pbData + pSignerInfo->SerialNumber.cbData);
const std::array<BYTE, 16> k_serial = {0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01}
const std::vector<BYTE> k_SerialKey(k_serial.cbegin(), k_serial.cend());
if (fileSerial == k_SerialKey)
{
// Do stuff since signature is made by us
}
The above code looks like it works well enough but it doesn't seem tp really check the integrity of the file itself. I want to be able to verify that the executable did not get corrupted by a virus or something but kept the signature intact.
I was thinking that I can keep using CryptMsgGetParam and use the params
CMSG_COMPUTED_HASH_PARAM
CMSG_HASH_DATA_PARAM
These params return 2 different hashes, if I compare them and they match does it mean that my file matches the hash in the signature?
N.B. I looked at WinVerifyTrust but I feel like its wayyy overkill for what I want, all I want is verify the file still matches the file the signature was made for.

Related

How to get the timestamp of when a disk is made offline from diskmgmt or other ways in windows?

I want to know the time when a disk is made offline by user. Is there a way to know this through WMI classes or other ways?
If you cannot find a way to do it through the Win32 API/WMI or other, I do know of an alternate way which you could look into as a last-resort.
What about using NtQueryVolumeInformationFile with the FileFsVolumeInformation class? You can do this to retrieve the data about the volume and then access the data through the FILE_FS_VOLUME_INFORMATION structure. This includes the creation time.
At the end of the post, I've left some resource links for you to read more on understanding this so you can finish it off the way you'd like to implement it; I do need to quickly address something important though, which is that the documentation will lead you to
an enum definition for the _FSINFOCLASS, but just by copy-pasting it from MSDN, it probably won't work. You need to set the first entry of the enum definition to 1 manually, otherwise it will mess up and NtQueryVolumeInformationFile will return an error status of STATUS_INVALID_INFO_CLASS (because the first entry will be identified as 0 and not 1 and then all the entries following it will be -1 to what they should be unless you manually set the = 1).
Here is the edited version which should work.
typedef enum _FSINFOCLASS {
FileFsVolumeInformation = 1,
FileFsLabelInformation,
FileFsSizeInformation,
FileFsDeviceInformation,
FileFsAttributeInformation,
FileFsControlInformation,
FileFsFullSizeInformation,
FileFsObjectIdInformation,
FileFsDriverPathInformation,
FileFsVolumeFlagsInformation,
FileFsSectorSizeInformation,
FileFsDataCopyInformation,
FileFsMetadataSizeInformation,
FileFsMaximumInformation
} FS_INFORMATION_CLASS, *PFS_INFORMATION_CLASS;
Once you've opened a handle to the disk, you can call NtQueryVolumeInformationFile like this:
NTSTATUS NtStatus = 0;
HANDLE FileHandle = NULL;
IO_STATUS_BLOCK IoStatusBlock = { 0 };
FILE_FS_VOLUME_INFORMATION FsVolumeInformation = { 0 };
...
Open the handle to the disk here, and then check that you have a valid handle.
...
NtStatus = NtQueryVolumeInformationFile(FileHandle,
&IoStatusBlock,
&FsVolumeInformation,
sizeof(FILE_FS_VOLUME_INFORMATION),
FileFsVolumeInformation);
...
If NtStatus represents an NTSTATUS error code for success (e.g. STATUS_SUCCESS) then you can access the VolumeCreationTime (LARGE_INTEGER) field of the FILE_FS_VOLUME_INFORMATION structure with the FsVolumeInformation variable.
Your final task at this point will be using the LARGE_INTEGER field named VolumeCreationTime to gather proper time/date information. There are two links included at the end of the post which are focused on that topic, they should help you sort it out.
See the following for more information.
https://learn.microsoft.com/en-us/windows-hardware/drivers/ddi/content/ntifs/nf-ntifs-ntqueryvolumeinformationfile
https://learn.microsoft.com/en-us/windows-hardware/drivers/ddi/content/wdm/ne-wdm-_fsinfoclass
https://learn.microsoft.com/en-us/windows-hardware/drivers/ddi/content/ntddk/ns-ntddk-_file_fs_volume_information
https://msdn.microsoft.com/en-us/library/windows/desktop/ms724280.aspx
https://blogs.msdn.microsoft.com/joshpoley/2007/12/19/datetime-formats-and-conversions/

GetProcessImageFileName path translation [duplicate]

How would I compare 2 strings to determine if they refer to the same path in Win32 using C/C++?
While this will handle a lot of cases it misses some things:
_tcsicmp(szPath1, szPath2) == 0
For example:
forward slashes / backslashes
relative / absolute paths.
[Edit] Title changed to match an existing C# question.
Open both files with CreateFile, call GetFileInformationByHandle for both, and compare dwVolumeSerialNumber, nFileIndexLow, nFileIndexHigh. If all three are equal they both point to the same file:
GetFileInformationByHandle function
BY_HANDLE_FILE_INFORMATION Structure
Filesystem library
Since C++17 you can use the standard filesystem library. Include it using #include <filesystem>. You can access it even in older versions of C++, see footnote.
The function you are looking for is equivalent, under namespace std::filesystem:
bool std::filesystem::equivalent(const std::filesystem::path& p1, const filesystem::path& p2 );
To summarize from the documentation: this function takes two paths as parameters and returns true if they reference the same file or directory, false otherwise. There is also a noexcept overload that takes a third parameter: an std::error_code in which to save any possible error.
Example
#include <filesystem>
#include <iostream>
//...
int main() {
std::filesystem::path p1 = ".";
std::filesystem::path p2 = fs::current_path();
std::cout << std::filesystem::equivalent(p1, p2);
//...
}
Output:
1
Using filesystem before C++17
To use this library in versions prior to C++17 you have to enable experimental language features in your compiler and include the library in this way: #include <experimental/filesystem>. You can then use its functions under the namespace std::experimental::filesystem. Please note that the experimental filesystem library may differ from the C++17 one. See the documentation here.
For example:
#include <experimental/filesystem>
//...
std::experimental::filesystem::equivalent(p1, p2);
See this question: Best way to determine if two path reference to same file in C#
The question is about C#, but the answer is just the Win32 API call GetFileInformationByHandle.
use the GetFullPathName from kernel32.dll, this will give you the absolute path of the file. Then compare it against the other path that you have using a simple string compare
edit: code
TCHAR buffer1[1000];
TCHAR buffer2[1000];
TCHAR buffer3[1000];
TCHAR buffer4[1000];
GetFullPathName(TEXT("C:\\Temp\\..\\autoexec.bat"),1000,buffer1,NULL);
GetFullPathName(TEXT("C:\\autoexec.bat"),1000,buffer2,NULL);
GetFullPathName(TEXT("\\autoexec.bat"),1000,buffer3,NULL);
GetFullPathName(TEXT("C:/autoexec.bat"),1000,buffer4,NULL);
_tprintf(TEXT("Path1: %s\n"), buffer1);
_tprintf(TEXT("Path2: %s\n"), buffer2);
_tprintf(TEXT("Path3: %s\n"), buffer3);
_tprintf(TEXT("Path4: %s\n"), buffer4);
the code above will print the same path for all three path representations.. you might want to do a case insensitive search after that
A simple string comparison is not sufficient for comparing paths for equality. In windows it's quite possible for c:\foo\bar.txt and c:\temp\bar.txt to point to exactly the same file via symbolic and hard links in the file system.
Comparing paths properly essentially forces you to open both files and compare low level handle information. Any other method is going to have flaky results.
Check out this excellent post Lucian made on the subject. The code is in VB but it's pretty translatable to C/C++ as he PInvoke'd most of the methods.
http://blogs.msdn.com/vbteam/archive/2008/09/22/to-compare-two-filenames-lucian-wischik.aspx
Based on answers about GetFileInformationByHandle(), here is the code.
Note: This will only work if the file already exists...
//Determine if 2 paths point ot the same file...
//Note: This only works if the file exists
static bool IsSameFile(LPCWSTR szPath1, LPCWSTR szPath2)
{
//Validate the input
_ASSERT(szPath1 != NULL);
_ASSERT(szPath2 != NULL);
//Get file handles
HANDLE handle1 = ::CreateFileW(szPath1, 0, FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
HANDLE handle2 = ::CreateFileW(szPath2, 0, FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
bool bResult = false;
//if we could open both paths...
if (handle1 != INVALID_HANDLE_VALUE && handle2 != INVALID_HANDLE_VALUE)
{
BY_HANDLE_FILE_INFORMATION fileInfo1;
BY_HANDLE_FILE_INFORMATION fileInfo2;
if (::GetFileInformationByHandle(handle1, &fileInfo1) && ::GetFileInformationByHandle(handle2, &fileInfo2))
{
//the paths are the same if they refer to the same file (fileindex) on the same volume (volume serial number)
bResult = fileInfo1.dwVolumeSerialNumber == fileInfo2.dwVolumeSerialNumber &&
fileInfo1.nFileIndexHigh == fileInfo2.nFileIndexHigh &&
fileInfo1.nFileIndexLow == fileInfo2.nFileIndexLow;
}
}
//free the handles
if (handle1 != INVALID_HANDLE_VALUE )
{
::CloseHandle(handle1);
}
if (handle2 != INVALID_HANDLE_VALUE )
{
::CloseHandle(handle2);
}
//return the result
return bResult;
}
If you have access to the Boost libraries, try
bool boost::filesystem::path::equivalent( const path& p1, const path& p2 )
http://www.boost.org/doc/libs/1_53_0/libs/filesystem/doc/reference.html#equivalent
To summarize from the docs: Returns true if the given path objects resolve to the same file system entity, else false.
What you need to do is get the canonical path.
For each path you have ask the file system to convert to a canonical path or give you an identifier the uniquely identifies the file (such as the iNode).
Then compare the canonical path or the unique identifier.
Note:
Do not try and figure out the conical path yourself the File System can do things with symbolic links etc that that are not easily tractable unless you are very familiar with the filesystem.
Comparing the actual path strings will not produce accurate results if you refer to UNC or Canonical paths (i.e. anything other than a local path).
shlwapi.h has some Path Functions that may be of use to you in determing if your paths are the same.
It contains functions like PathIsRoot that could be used in a function of greater scope.
If the files exist and you can deal with the potential race condition and performance hit from opening the files, an imperfect solution that should work on any platform is to open one file for writing by itself, close it, and then open it for writing again after opening the other file for writing. Since write access should only be allowed to be exclusive, if you were able to open the first file for writing the first time but not the second time then chances are you blocked your own request when you tried to open both files.
(chances, of course, are also that some other part of the system has one of your files open)
Open both files and use GetFinalPathNameByHandle() against the HANDLEs. Then compare the paths.

8.3 format filenames received when files opened from context menu in Windows

I have edited the windows registry so that selected files can be opened with the program I made (from an option in context menu). Specifically, under specific file types I have added 'shell' key and under it a 'command' key with string containing "C:\MyProgram.exe %1". The file opens correctly, however my program receives the file name in old 8.3 format, and I need full file name for display. How should I fix this?
Side quest: How to open multiple files as multiple arguments in one program call instead of opening separate instances, each with only one argument(%1)?
The easiest way to get to the full path name is to call GetLongPathName. In C++ you would use something like the following:
std::wstring LongPathFromShortPath(const wchar_t* lpszShortPath) {
// Prevent truncation to MAX_PATH characters
std::wstring shortPath = L"\\\\?\\";
shortPath += lpszShortPath;
// Calculate required buffer size
std::vector<wchar_t> buffer;
DWORD requiredSize = ::GetLongPathNameW(shortPath.c_str(), buffer.data(), 0x0);
if (requiredSize == 0x0) {
throw std::runtime_error("GetLongPathNameW() failed.");
}
// Retrieve long path name
buffer.resize(static_cast<size_type>(requiredSize));
DWORD size = ::GetLongPathNameW(shortPath.c_str(), buffer.data(),
static_cast<DWORD>(buffer.size()));
if (size == 0x0) {
throw std::runtime_error("GetLongPathNameW() failed.");
}
// Construct final path name (not including the zero terminator)
return std::wstring(buffer.data(), buffer.size()-1);
}
For first part of question do that IInspectable suggested.
But if you want to do something more fancy, simple registry modification will not do the trick. You need Windows Shell Extension and implement Context Menu handler. Once I have made one, here are some useful links: here, here and here. And there are already similar questions like this

How can I safely append data to a sk_buff for IPTables target

I am working on a Linux kernel module that needs to modify network packets and append an extra header. I already implemented the modification part, recomputed the check-sums and it worked nice. But I don't know how to safely append an extra header. If my input packet is something like:
ip-header / tcp-header / data
I would like to have an output packet like:
ip-header / tcp-header / my-header / data
For what I read, I think I need something like the following code. I wrote my specific questions on the code as comments. My general concern is if the code I am writing here is memory-safe or what should I do to have a memory-safe way to append the new header. Also, if I am doing something wrong or there is a better way to do it I will also appreciate the comment. I have tried to find examples but no luck so far. Here is the code:
static unsigned int my_iptables_target(struct sk_buff *skb, const struct xt_action_param *par) {
const struct xt_mytarget_info *info = par->targinfo;
/* Some code ... */
if (!skb_make_writable(skb, skb->len)) {
//Drop the packet
return NF_DROP;
}
struct newheader* myheader;
// Check if there is enough space and do something about it
if (skb_headroom(skb) < sizeof(struct newheader)) {
// So there is no enugh space.
/* I don't know well what to put here. I read that a function called pskb_expand_head might
* do the job. I do not understand very well how it works, or why it might fail (return value
* different from zero). Does this code work:
*/
if (pskb_expand_head(skb, sizeof(struct newheader) - skb_headroom(skb), 0, GPF_ATOMIC) != 0) {
// What does it mean if the code reaches this point?
return NF_DROP;
}
}
// At this point, there should be enough space
skb_push(skb, sizeof(struct newheader));
/* I also think that skb_push() creates space at the beggining, to open space between the header and
* the body I guess I must move the network/transport headers up. Perhaps something like this:
*/
memcpy(skb->data, skb->data + sizeof(struct newheader), size_of_all_headers - sizeof(struct newheader));
// Then set myheader address and fill data.
myheader = skb->data + size_of_all_headers;
//Then just set the new header, and recompute checksums.
return XT_CONTINUE;
}
I assumed that the variable size_of_all_headers contains the size in bytes of the network and transport headers. I also think that memcpy copies bytes in increasing order, so that call shouldn't be a problem. So does the above code works? It is all memory-safe? Are there better ways to do it? Are there examples (or can you provide one) that does something like this?
I used a code similar to the one in the question and so far it has worked very well for all the test I have done. To answer some of the specific questions, I used something like:
if (skb_headroom(skb) < sizeof(struct newheader)) {
printk("I got here!\n");
if (pskb_expand_head(skb, sizeof(struct newheader) - skb_headroom(skb), 0, GPF_ATOMIC) != 0) {
printk("And also here\n");
return NF_DROP;
}
}
But none of the print statements ever executed. I suppose that happens because the OS reserves enough space in memory such that there can be no problems given the limits of the IP header. But I think it is better to leave that if statement to grow the packet if necessary.
The other difference of the code that I tested and worked is that instead of moving all the other headers up to create a space for my header, I chose to move the body of the packet down.

Identifying the EINVAL in a Kernel Control ctl_enqueuedata call

I want to send messages from a kernel extension into a userland program using kernel controls. I'm experiencing an EINVAL error when calling ctl_enqueuedata.
I've set up a Kernel Control and I'm trying to send messages through it using ctl_enqueuedata. I'm setting
ep_ctl.ctl_flags = 0
before passing to ctl_register, which, the documents suggest, should result in ctl_unit being automatically set.
To quote kern_control.h:
For a dynamically assigned control ID, do not set the CTL_FLAG_REG_ID_UNIT flag.
static struct kern_ctl_reg ep_ctl;
static kern_ctl_ref kctlref;
...
errno_t error;
bzero(&ep_ctl, sizeof(ep_ctl)); // sets ctl_unit to 0
ep_ctl.ctl_id = 0;
ep_ctl.ctl_unit = 0;
strncpy(ep_ctl.ctl_name, CONTROL_NAME, strlen(CONTROL_NAME));
ep_ctl.ctl_flags = 0x0; // not CTL_FLAG_REG_ID_UNIT so unit gets supplied. Not CTL_FLAG_PRIVILEGED either.
ep_ctl.ctl_send = EPHandleSend;
ep_ctl.ctl_getopt = EPHandleGet;
ep_ctl.ctl_setopt = EPHandleSet;
ep_ctl.ctl_connect = EPHandleConnect;
ep_ctl.ctl_disconnect = EPHandleDisconnect;
error = ctl_register(&ep_ctl, &kctlref);
printf("setupControl %d\n", error);
When I call ctl_register it returns 0 ok.
When I call ctl_enqueuedata, passing in my struct kern_ctl_reg I'm getting 22, which is EINVAL. One of those arguments appears to be incorrect. The other arguments I'm passing are a static test string and its length for data, and zero flags.
int result = ctl_enqueuedata(kctlref, ep_ctl.ctl_unit, filename, length, 0x0);
The value of my ep_ctl's .ctl_unit is 0, the value of .ctl_id is 6. Could it be that the ctl_unit value being passed to ctl_enqueuedata is invalid / un-initialized?
kern_control.h says of ctl_unit:
This field is ignored for a dynamically assigned control ID
Which suggests that it isn't required anyway?
Have I missed something in initializing my ep_ctl?
I believe you supply wrong value as the 2nd parameter of ctl_enqueuedata(). Instead of ep_ctl.ctl_unit, you have to remember struct sockaddr_ctl::sc_unit in the EPHandleConnect() callback and that's what you are supposed to pass into ctl_enqueuedata().
I suggest using OSX's kernel debugging facilities to figure out what's going on here. That will let you walk through the relevant kernel code, and should tell you where it's rejecting your input.

Resources