Unblock a file in windows from a python script

Question:

Could I unblock a file in windows(7), which is automatically blocked by windows (downloaded from Internet) from a python script? A WindowsError is raised when such a file is encountered. I thought of catching this exception, and running a powershell script that goes something like:

Parameter Set: ByPath
Unblock-File [-Path] <String[]> [-Confirm] [-WhatIf] [ <CommonParameters>]

Parameter Set: ByLiteralPath
Unblock-File -LiteralPath <String[]> [-Confirm] [-WhatIf] [ <CommonParameters>]

I don’t know powershell scripting. But if I had one I could call it from python. Could you folks help?

Asked By: user3058846

||

Answers:

Yes, all you have to do is call the following command line from Python:

powershell.exe -Command Unblock-File -Path "c:pathtoblocked file.ps1"
Answered By: user189198

Late to the party . . . .
I have found that the Block status is simply an extra ‘file’ (stream) attached in NTFS and it can actually be accessed and somewhat manipulated by ordinary means. These are called Alternative Data Streams.
The ADS for file blocking (internet zone designation) is called ‘:Zone.Identifier’ and contains, I think, some useful information:

[ZoneTransfer]
ZoneId=3
ReferrerUrl=https://www.google.com/
HostUrl=https://imgs.somewhere.com/product/1969297/some-pic.jpg

All the other info I have found says to just delete this extra stream…. But, personally, I want to keep this info…. So I tried changing the ZoneId to 0, but it still shows as Blocked in Windows File Properties.
I settled on moving it to another stream name so I can still find it later.

The below script originated from a more generic script called pyADS. I only care about deleting / changing the Zone.Identifier attached stream — which can all be done with simple Python commands. So this is a stripped-down version. It has several really nice background references listed. I am currently running the latest Windows 10 and Python 3.8+; I make no guarantees this works on older versions.

import os

'''
References:
    Accessing alternative data-streams of files on an NTFS volume   https://www.codeproject.com/Articles/2670/Accessing-alternative-data-streams-of-files-on-an
    Original ADS class (pyADS)                                      https://github.com/RobinDavid/pyADS
    SysInternal streams applet                                      https://docs.microsoft.com/en-us/sysinternals/downloads/streams
    Windows: killing the Zone.Identifier NTFS alternate data stream https://wiert.me/2011/11/25/windows-killing-the-zone-identifier-ntfs-alternate-data-stream-from-a-file-to-prevent-security-warning-popup/
    Zone.Information                                                https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-fscc/6e3f7352-d11c-4d76-8c39-2516a9df36e8
    About URL Security Zones                                        https://docs.microsoft.com/en-us/previous-versions/windows/internet-explorer/ie-developer/platform-apis/ms537183(v=vs.85)?redirectedfrom=MSDN
    GREAT info: How Windows Determines That the File....            http://woshub.com/how-windows-determines-that-the-file-has-been-downloaded-from-the-internet/
    Dixin's Blog: Understanding File Blocking and Unblocking        https://weblogs.asp.net/dixin/understanding-the-internet-file-blocking-and-unblocking

'''
class ADS2():
    def __init__(self, filename):
        self.filename = filename

    def full_filename(self, stream):
        return "%s:%s" % (self.filename, stream)

    def add_stream_from_file(self, filename):
        if os.path.exists(filename):
            with open(filename, "rb") as f: content = f.read()
            return self.add_stream_from_string(filename, content)
        else:
            print("Could not find file: {0}".format(filename))
            return False

    def add_stream_from_string(self, stream_name, bytes):
        fullname = self.full_filename(os.path.basename(stream_name))
        if os.path.exists(fullname):
            print("Stream name already exists")
            return False
        else:
            fd = open(fullname, "wb")
            fd.write(bytes)
            fd.close()
            return True

    def delete_stream(self, stream):
        try:
            os.remove(self.full_filename(stream))
            return True
        except:
            return False

    def get_stream_content(self, stream):
        fd = open(self.full_filename(stream), "rb")
        content = fd.read()
        fd.close()
        return content
def UnBlockFile(file, retainInfo=True):
    ads = ADS2(file)
    if zi := ads.get_stream_content("Zone.Identifier"):
        ads.delete_stream("Zone.Identifier")
        if retainInfo: ads.add_stream_from_string("Download.Info", zi)
### Usage:
from unblock_files import UnBlockFile
UnBlockFile(r"D:Downloadssome-pic.jpg")

Before:

D:downloads>dir /r
 Volume in drive D is foo
 Directory of D:downloads

11/09/2021  10:05 AM                 8 some-pic.jpg
                                   126 some-pic.jpg:Zone.Identifier:$DATA
               1 File(s)              8 bytes

D:downloads>more <some-pic.jpg:Zone.Identifier:$DATA
[ZoneTransfer]
ZoneId=3
ReferrerUrl=https://www.google.com/
HostUrl=https://imgs.somewhere.com/product/1969297/some-pic.jpg

After:

D:downloads>dir /r
 Volume in drive D is foo
 Directory of D:downloads

11/09/2021  10:08 AM                 8 some-pic.jpg
                                   126 some-pic.jpg:Download.Info:$DATA
               1 File(s)              8 bytes
Answered By: Thomas Oatman

From this page about the Unblock-File command: https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.utility/unblock-file?view=powershell-7.2

Internally, the Unblock-File cmdlet removes the Zone.Identifier alternate data stream, which has a value of 3 to indicate that it was downloaded from the internet.

To remove an alternate data stream ads_name from a file pathtofile.ext, simply delete pathtofile.ext:ads_name:

try:
    os.remove(your_file_path + ':Zone.Identifier')
except FileNotFoundError:
    # The ADS did not exist, it was already unblocked or
    # was never blocked in the first place
    pass
# No need to open up a PowerShell subprocess!

(And similarly, to check if a file is blocked you can use os.path.isfile(your_file_path + ':Zone.Identifier'))

In a PowerShell script, you can use Unblock-File for this, or simply Remove-Item -Path $your_file_path':Zone.Identifier'.
Remove-Item also has a specific flag for alternate data streams: Remove-Item -Stream Zone.Identifier (which you can pipe in multiple files to, or a single -Path)

Answered By: Artyer
Categories: questions Tags: ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.