How Do you set MOTW on an Executable - c++

How do you set MOTW (Mark of the Web) on an executable that is downloaded from the internet?

This data is stored in an NTFS alternative file stream alongside an executable. The stream is called Zone.Identifier:
Windows® Internet Explorer® uses the stream name Zone.Identifier for storage of URL security zones.
The fully qualified form is sample.txt:Zone.Identifier:$DATA
The stream is a simple text stream of the form:
[ZoneTransfer]
ZoneId=3
MSDN-SECZONES gives an explanation of security zones.
(N.B. The original has a space between the colon and "Zone" but I think this is erroneous.)
You can find the ZoneIds in UrlMon.h in the SDK; there's an enum which equates to
enum URLZONE {
URLZONE_LOCAL_MACHINE = 0,
URLZONE_INTRANET = 1,
URLZONE_TRUSTED = 2,
URLZONE_INTERNET = 3,
URLZONE_RESTRICTED = 4
};
(The original uses previous value + 1 rather than absolute values.)
As Hans says in the comments, these can be written with the standard Win32 file APIs CreateFile and WriteFile.
Firefox always writes Internet Zone, zone 3 - Firefox code here (MPL/LGPL/GPL tri-license):
bool SetInternetZoneIdentifier(const FilePath& full_path) {
const DWORD kShare = FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE;
std::wstring path = full_path.value() + L":Zone.Identifier";
HANDLE file = CreateFile(path.c_str(), GENERIC_WRITE, kShare, NULL,
OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
if (INVALID_HANDLE_VALUE == file)
return false;
const char kIdentifier[] = "[ZoneTransfer]\nZoneId=3";
DWORD written = 0;
BOOL result = WriteFile(file, kIdentifier, arraysize(kIdentifier), &written,
NULL);
CloseHandle(file);
if (!result || written != arraysize(kIdentifier)) {
DCHECK(FALSE);
return false;
}
return true;
}
Alternatively there's an IE COM API CLSID_PersistentZoneIdentifier you can use to abstract this all for you.

It is not explicitly stated in RFC 3514, but today, due to increased security requirements, implementations should really retain the information of the presence or absence of the RFC3514 bit in a network transmission, when they write files out to disk, and vice-versa for reading from disk.

Related

Writing USB Disk Sectors Results in Permission Error 5

The code below is required by my app to write disk sectors of external USB drives. It works on most Win10 PCs, but it's returning error 5 for permission denied on a couple PCs. I have exclusions created for both Windows Defender and Malwarebytes. There's nothing in the event viewer related to the failure. The read function works without error.
I tried adding calls to FSCTL_LOCK_VOLUME and FSCTL_DISMOUNT_VOLUME, but this doesn't help. Probably not needed anyway since I'm accessing the physical disk after it's been cleaned, and not any volumes.
Any idea what could cause this, or how to resolve?
Would be great to learn if there's any alternate methods of reading and writing disk sectors.
BOOL Partitioner::BlockWrite(wchar_t* devIdentifier, unsigned __int64 lngStartbyte, DWORD bytesToRead, BYTE* buf)
{
BOOL ret = FALSE;
HANDLE devHan = CreateFile(devIdentifier, GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);
if (devHan != INVALID_HANDLE_VALUE)
{
// Seek to the starting block to write
LARGE_INTEGER startByte;
startByte.QuadPart = lngStartbyte;
SetFilePointer(devHan, startByte.LowPart, (long*)&startByte.HighPart, FILE_BEGIN);
// Write the data (this is where error 5 is returned)
DWORD bytesWritten = 0;
ret = WriteFile(devHan, buf, bytesToRead, &bytesWritten, NULL);
FlushFileBuffers(devHan);
CloseHandle(devHan);
}
else
{
ret = GetLastError();
wchar_t msg[PATH_BUFFER_SIZE] = {0};
swprintf_s(msg, WCSIZE_FULL(msg), L"Error= %d, byte= %llu", ret, lngStartbyte);
mLog->LogError(msg);
}
return ret;
}
I found the answer where I wasn't expecting. I thought this had to be something related to how the file handles were being opened. Instead, turning off Real-time protection in the Virus threat protection settings for Windows 10 caused the error 5s to go away. To resolve without disabling real-time protection, you need to add an allowed app exclusion for each of the installed EXEs.
You can do this in code by scripting PowerShell:
string script = "powershell -Command \"Add-MpPreference -ControlledFolderAccessAllowedApplications '" + GetAppPath() + "\\AppServer.exe" + "'";
Process.Start(new ProcessStartInfo() { FileName = "cmd.exe", Arguments = "/c " + script, CreateNoWindow = true, WindowStyle = ProcessWindowStyle.Hidden }).WaitForExit();

Enumerate all partitions and test if they are NTFS

I'm using:
DWORD d = GetLogicalDrives();
for (int i = 0; i < 26; i++)
{
if ((1 << i) & d) // drive letter 'A' + i present on computer
{
wstring s = std::wstring(L"\\\\.\\") + wchar_t('A' + i) + L":";
PARTITION_INFORMATION diskInfo;
DWORD dwResult;
HANDLE dev = CreateFile(LPWSTR(s.c_str()), GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_READONLY, NULL);
DeviceIoControl(dev, IOCTL_DISK_GET_PARTITION_INFO, NULL, 0, &diskInfo, sizeof(diskInfo), &dwResult, NULL);
CloseHandle(dev);
if (diskInfo.PartitionType == PARTITION_IFS)
{
...
}
}
}
to enumerate all NTFS partitions of a computer.
It works on my Windows 7, on a Windows 8.1 I tried it on, and on a Windows 10 computer.
But it fails on another Windows 10 computer: on this one, the volume C: has a diskInfo.PartitionType value equal to 0x00, instead of 0x07 (PARTITION_IFS).
This value is (see the doc here):
PARTITION_ENTRY_UNUSED : 0x00 : An unused entry partition.
This is strange, since, I can confirm, the partition is really NTFS.
Questions:
Is it well-known that IOCTL_DISK_GET_PARTITION_INFO is not 100% reliable to get the partition type?
What would be a more reliable way to enumerate all NTFS volumes?
Note: I also looked at using IOCTL_DISK_GET_PARTITION_INFO_EX instead of IOCTL_DISK_GET_PARTITION_INFO but then the structure PARTITION_INFORMATION_EX does not seem to give informations about PartitionType, whereas the structure PARTITION_INFORMATION does give access to PartitionType.
As #RemyLebeau says, you are not checking the return value for each call.
PARTITION_ENTRY_UNUSED often means the DeviceIoControl() call failed. It depends on the permissions of your user. You should check your user's access rights to see if it has the FILE_READ_DATA permission (included in GENERIC_READ) on volume C:. In my test environment, if you have no access to open volume C: with GENERIC_READ, CreateFile() returns INVALID_HANDLE_VALUE, and then DeviceIoControl() fails as well.
EDIT:
I suggest using GetVolumeInformation(), for example:
wchar_t fs[MAX_PATH + 1] = { 0 };
GetVolumeInformationW(L"C:\\", NULL, 0, NULL, NULL, NULL, fs, MAX_PATH + 1);
And you will see the Type info in the fs buffer.
I did further investigation thanks to #RemyLebeau's comments with:
HANDLE dev = CreateFile(..., GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_READONLY, NULL);
if (dev == INVALID_HANDLE_VALUE)
{
DWORD err = GetLastError(); // then MessageBox
}
else
{
BOOL ret = DeviceIoControl(dev, IOCTL_DISK_GET_PARTITION_INFO, NULL, 0, &diskInfo, sizeof(diskInfo), &dwResult, NULL);
if (ret == FALSE)
{
DWORD err = GetLastError(); // then MessageBox
}
CloseHandle(dev);
}
on the computer where it failed (computer with Windows 10). I found that CreateFile succeeded but then DeviceIoControl failed with GetLastError being 1 i.e. ERROR_INVALID_FUNCTION (see System Error Codes (0-499)).
Conclusion (I quote Remy's comment):
That means IOCTL_DISK_GET_PARTITION_INFO is not supported by the device you passed to DeviceIoControl().
I then tried with IOCTL_DISK_GET_PARTITION_INFO_EX:
PARTITION_INFORMATION_EX diskInfo;
BOOL ret = DeviceIoControl(dev, IOCTL_DISK_GET_PARTITION_INFO_EX, NULL, 0, &diskInfo, sizeof(diskInfo), &lpBytesReturned, NULL);
and then it worked. I could see that diskInfo.PartitionStyle was PARTITION_STYLE_GPT (=1), and this was the reason why IOCTL_DISK_GET_PARTITION_INFO failed. I quote Remy's comment again:
IOCTL_DISK_GET_PARTITION_INFO is not supported on GPT partitioned drives.
So here's the conclusion:
use IOCTL_DISK_GET_PARTITION_INFO_EX instead of IOCTL_DISK_GET_PARTITION_INFO
if diskInfo.PartitionStyle is 0 (PARTITION_STYLE_MBR) then diskInfo.Mbr.PartitionType can be tested. If it's 0x07, it's NTFS.
if diskInfo.PartitionStyle is 1 (PARTITION_STYLE_GPT) then diskInfo.Gpt.PartitionType can be tested, see here: https://learn.microsoft.com/en-us/windows/desktop/api/winioctl/ns-winioctl-_partition_information_gpt. Even if the NTFS Wikipedia page mentions the GUID EBD0A0A2-B9E5-4433-87C0-68B6B72699C7 for NTFS in the GPT case, this GUID is in fact unrelated to file system (see comment below about this).
it's probably easier to use GetVolumeInformation() instead and just compare if the result is the "NTFS" string, as in the other answer
in my particular case, I initially wanted to test if a volume is NTFS or not before attempting an indexing with DeviceIoControl(hVol, FSCTL_ENUM_USN_DATA, ...) because I thought such MFT querying would be limited to NTFS volumes. In fact, an easier solution would be to NOT TEST if it's NTFS or not, and just do the FSCTL_ENUM_USN_DATA. The worst that can happen is that FSCTL_ENUM_USN_DATA fails with ERROR_INVALID_FUNCTION error, per the documentation:
"ERROR_INVALID_FUNCTION The file system on the specified volume does not support this control code."

Best way to get volume name of symbolic link target [NTFS]

I want a reliable way to get the volume name of a symbolic link's target that isn't super complicated.
So it looks like the FILE_NAME_INFO structure does not contain any info about the volume the file resides on. I am able to obtain the path of symlink targets from this structure, but for now I just assume the target resides on the same volume. However, I know symlinks permit targets on other volumes.
#include <windows.h>
#include <stdio.h>
#include <tchar.h>
#include <ole2.h>
struct FILE_NAME_INFO_AND_BUF {
FILE_NAME_INFO fni;
WCHAR buf[260];
};
WCHAR* getReparseTarget(WCHAR* linkFileName) {
HANDLE hFile;
WCHAR *szGuid = (WCHAR *)malloc(sizeof(WCHAR) * MAX_PATH);
BOOL result;
FILE_NAME_INFO_AND_BUF fnib = { 0 };
hFile = ::CreateFile(linkFileName, FILE_READ_ATTRIBUTES,
FILE_SHARE_READ |
FILE_SHARE_WRITE,
0,
OPEN_EXISTING,
FILE_FLAG_BACKUP_SEMANTICS, 0);
if (hFile == INVALID_HANDLE_VALUE) {
::CloseHandle(hFile);
return NULL;
}
result = ::GetFileInformationByHandleEx(hFile, FileNameInfo, &fnib, sizeof(fnib));
if (!result) {
fprintf(stderr, "GetFileInformationByHandleEx Error %d\n", ::GetLastError());
::CloseHandle(hFile);
return NULL;
}
WCHAR *targetFileName = (WCHAR *)malloc(sizeof(WCHAR) * MAX_PATH);
wmemset(targetFileName, 0, MAX_PATH);
wcsncpy(targetFileName, linkFileName, 2);
wcscat(targetFileName, fnib.fni.FileName);
return targetFileName;
}
As you can see I'm cheating and getting the volume name, in this case the drive letter, from the input string, but this wouldn't work if the target was on another volume. Also I'd prefer obtaining the volume name with the GUID e.g. \\?\Volume{f993747a-5d7a-4de1-a97a-c20c1af1ba02}\ in it than the drive letter e.g. C:\
The absolute simplest way, as long as you can target Vista or later, is to use the GetFinalPathNameByHandle function.
If you need to target XP as well then you can find a symlink's target by opening the link itself (not the file it points to) using the FILE_FLAG_OPEN_REPARSE_POINT flag, and then use the FSCTL_GET_REPARSE_POINT IO control code to find the target of the link.
Because a link's target can potentially contain other links (up to a maximum of 31 I believe), you have to do this on every element of the path to be sure you've found the final target.

Is there a Windows API for converting File ID and Volume Serial to a unique path?

Background: I know that in Windows, two volumes with the same serial cannot be mounted at the same time. Moreover, there cannot exist two files within a single mounted volume with the same file ID. EDIT: this may not be the case. Then, given any volume handle or drive letter, the file_id maps to a unique file path. Is there a Windows API that can do this for any volume?
Progress: Here's how far I got:
#import <Windows.h>
...
FILE_ID_DESCRIPTOR file_id;
file_id.dwSize = 24;
file_id.Type = FileIdType;
file_id.QuadPart = /* file reference number */;
...
BOOL File::open(HANDLE *volume) {
file = OpenFileById(*volume, &file_id, SYNCHRONIZE | FILE_READ_ATTRIBUTES
| READ_CONTROL | ACCESS_SYSTEM_SECURITY,
SHARE_MODE, NULL, OPEN_FLAGS);
if (file == INVALID_HANDLE_VALUE) {
if (GetLastError() == 1314UL) { /* SACL permissions denied */
file = OpenFileById(*volume, &file_id, SYNCHRONIZE | FILE_READ_ATTRIBUTES
| READ_CONTROL, SHARE_MODE, NULL, OPEN_FLAGS);
perms = 0x4 | 0x2 | 0x1;
}
if (GetLastError() == 5UL) { /* Security permissions denied */
file = OpenFileById(*volume, &file_id, SYNCHRONIZE | FILE_READ_ATTRIBUTES,
SHARE_MODE, NULL, OPEN_FLAGS);
perms = 0x2 | 0x1;
}
if (file == INVALID_HANDLE_VALUE) { /* All permissions denied */
file = OpenFileById(*volume, &file_id, SYNCHRONIZE,
SHARE_MODE, NULL, OPEN_FLAGS);
perms = 0x1;
}
if (file == INVALID_HANDLE_VALUE) /* File name denied|file doesn't exist */
perms = 0;
} else {
perms = 0x8 | 0x4 | 0x2 | 0x1;
}
return file != INVALID_HANDLE_VALUE;
}
...
if (perms) {
path = new WCHAR[260]; /* expected file length */
l = GetFinalPathNameByHandle(file, path, 260, 0);
if (l >= 260) {
delete[] path;
path = new WCHAR[l];
GetFinalPathNameByHandle(file, path, l, 0);
} else if (l <= 0) {
delete[] path;
path = NULL;
}
}
In the first part of the code, I open a handle to the file, using an already-open handle to the volume, which is easy to do given the mounted drive letter. file is a handle to the file I am about to open.
In the second part, I use the handle to acquire the full name including DOS-style drive name (for example, C:\Users\rmanne\Downloads\test.txt). As long as perms is non-zero, I am guaranteed a human-readable absolute path in the second part of the code. However, I have only tested this with NTFS. According to the API, it will work with CsvFS and ReFS as well, but I haven't had a chance to test it with these yet.
The two functions are provided here:
http://msdn.microsoft.com/en-us/library/windows/desktop/aa364962%28v=vs.85%29.aspx
http://msdn.microsoft.com/en-us/library/windows/desktop/aa365432%28v=vs.85%29.aspx
As you can see, GetFinalPathNameByHandle is well-defined for all filesystems that Windows can mount and read without extra drivers or work-arounds. However, OpenFileByFileId is only defined for NTFS, ReFS, and CsvFS. I do not know whether or not it is defined for FAT or exFAT drives. However, it will, by specification, not work with network shared drives (SMB).
I have the option to just cache the file name because of the way I traverse all of the files, but caching these is incredibly memory-inefficient, taking up to 500MB of memory, whereas, not caching takes about 100MB of memory. Is there a different api that would enable me to get the name of a file given the file_id?

Watching a file for changes in Windows 7

I have a Visual Studio 2008 C++ application for Windows 7 where I would like to watch a file for changes.
The file may be changed like this:
std::ofstream myfile_;
void LogData( const char* data )
{
myfile_ << data << std::endl;
// note that the file output buffer is flushed by std::endl, but the file is not closed.
}
I have tried watching the file's directory using both ReadDirectoryChangesW and FindFirstChangeNotification with FILE_NOTIFY_CHANGE_SIZE | FILE_NOTIFY_CHANGE_LAST_WRITE | FILE_NOTIFY_CHANGE_LAST_ACCESS | FILE_NOTIFY_CHANGE_SECURITY | FILE_NOTIFY_CHANGE_CREATION | FILE_NOTIFY_CHANGE_FILE_NAME flags. But, neither of those APIs will detect file changes until the file handle is actually closed.
Is there any way to detect a change when the file is actually written, but before the file handle is closed?
Thanks,
PaulH
Update
On #Edwin's suggestion, I'm attempting to use the Journal feature. But, I'm having a couple issues.
FSCTL_READ_USN_JOURNAL returns instantly. It does not block. (though, this may be related to issue 2)
Regardless of where my handle points to (I have tried opening a handle to the directory "C:\Foo\Bar" and to the file "C:\Foo\Bar\MyFile.txt") I seem to get any changes made to the the C: volume. Is there a way to limit what FSCTL_READ_USN_JOURNAL gives me?
Error checking omitted for brevity.
boost::shared_ptr< void > directory(
::CreateFileW( L"C:\\Foo\\Bar\\Myfile.txt",
GENERIC_READ,
FILE_SHARE_READ,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL ),
::CloseHandle );
USN_JOURNAL_DATA journal = { 0 };
DWORD returned = 0;
::DeviceIoControl( directory.get(), FSCTL_QUERY_USN_JOURNAL, NULL, 0, &journal, sizeof( journal ), &returned, NULL );
BYTE buffer[ 4096 ] = { 0 };
READ_USN_JOURNAL_DATA read = { 0, USN_REASON_DATA_EXTEND | USN_REASON_DATA_TRUNCATION, FALSE, 0, 0, journal.UsnJournalID };
::DeviceIoControl( directory.get(), FSCTL_READ_USN_JOURNAL, &read, sizeof( read ), &buffer, sizeof( buffer ), &returned, NULL );
for( USN_RECORD* record = ( USN_RECORD* )( buffer + sizeof( USN ) );
( ( BYTE* )record - buffer ) < returned;
record = ( USN_RECORD* )( ( BYTE* )record + record->RecordLength ) )
{
ATLTRACE( L"%s\r\n", record->FileName );
}
Example output (none of these are in the C:\Foo\Bar directory):
AeXProcessList.txt`
AeXProcessList.txt`
AeXAMInventory.txt`
AeXAMInventory.txt`
AeXProcessList.txt`
AeXProcessList.txtP
access.log`
mysqlgeneral.log
E804.tmp
apache_error.log
E804.tmp
CHROME.EXE-5FE9909D.pfh
CHROME.EXE-5FE9909D.pfp
SyncData.sqlite3-journal
CHROME.EXE-5FE9909D.pfh
CHROME.EXE-5FE9909D.pfP
1211.tmp
SyncData.sqlite3-journal
AeXAMInventory.txt
You can use
Change Journal Operations
(see MSDN docs)
That's the only 100% garanteed way to detect any change in the filesystem.
But it's pretty complicated.
To read data for a specific file or directory, I believe you want to use FSCTL_READ_FILE_USN_DATA instead of FSCTL_READ_USN_JOURNAL. I believe the latter always retrieves data for an entire volume. That does not, however, fill in the TimeStamp, Reason, or SourceInfo fields of the USN record you get. If you need those, I believe you can read them with FSCTL_READ_USN_JOURNAL, specifying the exact USN you want to read.
No, because until you close the file handle there is no guarantee a single byte ever gets written by the OS.
The exception would probably be by calling flush on your file handle and then call the Windows API function FlushFileBuffers, but unless the program writing into the file does this no bytes probably get written.
This can be done with a filter driver that monitors the FASTIO_WRITE and IRP_MJ_WRITE operations. Here is a pretty good how-to article.