Manipulating the zone identifier to specify where a file was download from

Raymond Chen

Raymond

When you download a file via Internet Explorer,
the file is tagged with a little bit of information known as
a zone identifier which remembers where the file was
downloaded from.
This is what tells Explorer to put up the “Yo, did you really want
to run this program?” prompt
and which is taken into account by applications
so that they can do things like disable scripting
and macros when they open the document, just in case the file is
malicious.

Today’s Little Program is really three Little Programs:
One to read the zone identifier, one to set the zone identifier,
and one to clear it.

#define STRICT
#include <windows.h>
#include <atlbase.h>
#include <urlmon.h>
#include <stdlib.h>
int __cdecl wmain(int argc, wchar_t **argv)
{
 if (argc < 2) return 0;
 CCoInitialize init;
 CComPtr<IZoneIdentifier> spzi;
 spzi.CoCreateInstance(CLSID_PersistentZoneIdentifier);
 DWORD dwZone;
 if (SUCCEEDED(CComQIPtr<IPersistFile>(spzi)
                   ->Load(argv[1], STGM_READ)) &&
     SUCCEEDED(spzi->GetId(&dwZone))) {
  printf("Zone identifier is %d\n", dwZone);
 } else {
  printf("Couldn't get zone identifier (perhaps there isn't one)\n");
 }
 return 0;
}

The first program takes a file name on the command line
(fully-qualified path, please)
and prints the zone identifier associated with it.
The numeric values for the most commonly-encountered zone identifiers are

IdentifierValue
URLZONE_LOCAL_MACHINE0
URLZONE_INTRANET1
URLZONE_TRUSTED2
URLZONE_INTERNET3
URLZONE_UNTRUSTED4

Note also that if you want your application to be sensitive
to the file zone (so that you can disable features
for untrusted documents),
you should use the
IInternet­Security­Manager::Map­Url­To­Zone function rather
than using only the file zone identifier,
because the effective zone of a file is a combination of the
file’s declared zone as well as its physical location.
(For example, a file in the Temporary Internet Files directory
or on an untrusted server should not be given full trust
regardless of what it claims.

Additional reading
.)

Here’s a program that uses
IInternet­Security­Manager::Map­Url­To­Zone
to determine the effective security zone:

#define STRICT
#include <windows.h>
#include <atlbase.h>
#include <urlmon.h>
#include <stdlib.h>
int __cdecl wmain(int argc, wchar_t **argv)
{
 if (argc < 2) return 0;
 CCoInitialize init;
 CComPtr<IInternetSecurityManager> spism;
 spzi.CoCreateInstance(CLSID_InternetSecurityManager);
 DWORD dwZone;
 if (SUCCEEDED(spism->MapUrlToZone(
                   argv[1],
                   &dwZone,
                   MUTZ_ISFILE | MUTZ_DONT_UNESCAPE))) {
  printf("Zone is %d\n", dwZone);
 } else {
  printf("Couldn't get zone\n");
 }
 return 0;
}

The MUTZ_IS­FILE flag
saves you the hassle of having to prepend
file: in front of the path,
but you still have to pass a full path
because the first parameter is a URL, not a path.

Okay, that was a bit of a digression there.
Let’s write another Little Program which changes the zone identifier.

#define STRICT
#include <windows.h>
#include <atlbase.h>
#include <urlmon.h>
#include <stdlib.h>
int __cdecl wmain(int argc, wchar_t **argv)
{
 if (argc < 3) return 0;
 CCoInitialize init;
 CComPtr<IZoneIdentifier> spzi;
 spzi.CoCreateInstance(CLSID_PersistentZoneIdentifier);
 spzi->SetId(_wtol(argv[2]));
 CComQIPtr<IPersistFile>(spzi)->Save(argv[1], TRUE);
 return 0;
}

This program takes two parameters:
A fully-qualified path and a zone (in integer form).
It applies the zone to the file,
overwriting the existing zone if any.

Finally, here’s a Little Program to remove the zone information
from the file entirely.
This is the equivalent of clicking the
Unblock button in the file property sheet.

#define STRICT
#include <windows.h>
#include <atlbase.h>
#include <urlmon.h>
#include <stdlib.h>
int __cdecl wmain(int argc, wchar_t **argv)
{
 if (argc < 2) return 0;
 CCoInitialize init;
 CComPtr<IZoneIdentifier> spzi;
 spzi.CoCreateInstance(CLSID_PersistentZoneIdentifier);
 spzi->Remove();
 CComQIPtr<IPersistFile>(spzi)->Save(argv[1], TRUE);
 return 0;
}
Raymond Chen
Raymond Chen

Follow Raymond