카테고리:

3 분 소요

문제 상황

Assembly.LoadFrom을 사용하여 인터넷에서 다운로드 받은 .dll 파일을 읽어들이려고 할 때 아래와 같은 메시지를 출력하며 작동이 중지된다.

System.IO.FileLoadException: ‘파일이나 어셈블리 ‘file:///C:\Users\User\Downloads\Example.dll’ 또는 여기에 종속되어 있는 파일이나 어셈블리 중 하나를 로드할 수 없습니다. 작업이 지원되지 않습니다. (예외가 발생한 HRESULT: 0x80131515)’

 

NotSupportedException: 이전 버전의 .NET Framework에서 어셈블리에 샌드박스가 적용된 네트워크 위치에서 어셈블리를 로드하려고 했습니다. .NET Framework의 이 릴리스는 기본적으로 CAS 정책을 사용하도록 설정하지 않으므로 이러한 로드는 위험할 수 있습니다. 이러한 로드가 어셈블리에 샌드박스를 적용하기 위한 것이 아니면 loadFromRemoteSources 스위치를 사용하도록 설정하십시오. 자세한 내용은 http://go.microsoft.com/fwlink/?LinkId=155569를 참조하십시오.

문제 원인

문제의 원인은 Windows가 인터넷에서 다운로드 받은 파일의 Alternate Data Stream(ADS)영역에 Zone.Identifier라는 보안 태그를 붙이기 때문이다. .NET에서는 정책상 안전을 위해 위험한 PE 파일은 불러오지 않는다.

쉬운 방법은 Assembly.UnsafeLoadFrom(String) 메서드를 사용하는 것이지만, 외부에서 다운로드 받았다는 기록을 삭제하는 코드를 사용하여 해결하는 방법을 소개하고자 한다.

Zone.Identifier를 확인하는 명령어는 다음과 같은 명령어(more < [파일명]:Zone.Identifier)로 확인해 볼 수 있다.

Alternate Data Stream(ADS)의 내용

문제 해결

문제의 해결은 단순하다. Assembly.LoadFrom를 실행하기 전에 읽어들이고자 하는 파일의 Zone.Identifier를 제거하면 된다. Zone.Identifier는 일반적인 파일이 아니므로, C#의 기본 함수인 File.ExistsFile.Delete로는 확인 및 삭제할 수 없고 WinAPI를 P/Invoke하여 확인 및 삭제해야 한다. 존재하는지 확인하고 삭제하는 코드는 아래와 같다.

const uint GENERIC_READ = 0x80000000;
const uint OPEN_EXISTING = 3;

[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
static extern IntPtr CreateFile(string lpFileName, uint dwDesiredAccess,uint dwShareMode, 
    IntPtr lpSecurityAttributes, uint dwCreationDisposition, uint dwFlagsAndAttributes, IntPtr hTemplateFile);

[DllImport("kernel32.dll", SetLastError = true)]
static extern bool CloseHandle(IntPtr hObject);

[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
static extern bool DeleteFile(string lpFileName);

static void ZoneIdentifierExists(string filePath) {
    string adsPath = filePath + ":Zone.Identifier";

    IntPtr handle = CreateFile(adsPath, GENERIC_READ, 0, IntPtr.Zero, OPEN_EXISTING, 0,IntPtr.Zero);

    if (handle.ToInt64() != -1) {
        CloseHandle(handle);
        
        if (DeleteFile(adsPath)) {
            Console.WriteLine("Zone.Identifier 삭제 완료");
        } else {
            int errorCode = Marshal.GetLastWin32Error();
            Console.WriteLine($"삭제 실패 (오류 코드: {errorCode})");
        }
    }
}

태그: ADS, Alternate Data Stream, Assembly.LoadFrom, Assembly.UnsafeLoadFrom, CAS, DllImport, FileLoadException, GENERIC_READ, kernel32.dll, loadFromRemoteSources, NotSupportedException, Zone.Identifier, 샌드박스, 어셈블리

업데이트: