2011-03-19 4 views
9

저는 C#에서 간단한 데스크톱 클라이언트/서버 응용 프로그램을 작성하고 있습니다. 자체 교육 목적을 위해, 필자는 tcp/ip 소켓 연결을 통해 두 응용 프로그램간에 앞뒤로 보내는 메시지 (클래스로 정의 됨)에 대한 자체 직렬화 시스템을 구축했습니다. 시스템은 초기화시 반사를 사용하여 IL을 방출하여 각 메시지 유형에 대해 직렬화/비 직렬화 메소드를 생성합니다.동적으로 생성 된 어셈블리에 반영 권한 부여

이 시스템의 첫 번째 버전은 생성 된 IL (메시지 유형의 임의 필드에서 작동하는)이 액세스 권한을 무시할 수 있도록하기 위해 생성자에 true를 전달하는 DynamicMethod를 사용했습니다. 이것은 효과가 있었고 사람들은 기뻐했지만 결과 함수를 얼마나 고통스럽게 불투명하게 디버깅했는지 불만스러워했습니다. 그래서 DynamicMethod를 설치하고 * Builder 클래스를 사용하여 선택적으로 디스크에 저장하고 .NET Reflector와 같은 도구로 검사 할 수있는 동적 어셈블리를 생성하기로 결정했습니다.

시스템을 리팩터링 한 다음 약간의 벽돌 벽을 맞았습니다. 새 직렬화 함수 중 하나가 내 메시지 유형 중 하나에서 개인 필드 또는 메소드에 액세스하려고 할 때마다 FieldAccessException 또는 MethodAccessException이 발생합니다. 많은 인터넷 검색 및 치아 gnashing 후, 나는 권한 중 하나로 문제를 좁혔다 생각; 특히 동적으로 생성 된 어셈블리에는 호출/생성 어셈블리 (모든 반사 된 유형이있는 경우)에 상대적인 ReflectionPermissionFlag.MemberAccess 권한이 없다고 생각합니다.

불행히도 어셈블리에 동적 어셈블리 작성 프로세스를 수정하여 어셈블리에 다시 작성 권한이있는 것처럼 보이지 않습니다. DefineDynamicAssembly에 대한 사용 권한 매개 변수는 사용 권한을 부여하는 것이 아니라 사용 권한을 제한하는 것과 관련이있는 것처럼 보이므로 Evidence 매개 변수가 필요합니다. 증거가 마술처럼 일련의 사용 권한으로 변환되는 것처럼 보이지만 이런 일이 발생하는 데 유용한 예제 나 설명을 찾을 수 없습니다.

그래서 제 질문은 다음과 같습니다

(1) 내 문제는 내 동적으로 생성 된 어셈블리의 권한이 부족 내 가정에서 수정 건가요?

(2) 그렇다면 호출 어셈블리로서 내 동적 어셈블리에 필요한 권한을 어떻게 부여합니까?

현재 동적 어셈블리를 생성 코드 : 내 프로젝트는 .NET 3.5을 목표로하고있다

 AssemblyName assembly_name = new AssemblyName("LCSerialization"); 
     assembly_name.Version = new Version(1, 0, 0, 0); 

     m_SerializationAssembly = current_domain.DefineDynamicAssembly(assembly_name, AssemblyBuilderAccess.RunAndSave); // Fix me 
     m_SerializationModule = m_SerializationAssembly.DefineDynamicModule("MainModule", "LCSerialization.dll"); 
     m_SerializationWrapperClass = m_SerializationModule.DefineType("CSerializationWrapper", TypeAttributes.Public); 

참고; 설명서에서는 .NET 4.0이 다른 보안 개념을 사용하고 DefineDynamicAssembly에서 Evidence/PemissionSet 기반 메서드를 사용하지 않습니다.

는, 구체적인 예를주고 내가 좋아하는 클래스를 있다고 가정합니다 :

[NetworkMessage] 
public class CTestMessage 
{ 
    public CTestMessage(int cheeseburgers) 
    { 
     m_CheeseBurgers = cheeseburgers 
    } 

    private int m_CheeseBurgers = 0; 
} 

다음 내 직렬화 시스템, 말까지 일 것이다, 초기화 반사하는 동안이 발생시 (컷 앤를 상기 방법은 연속적으로 실행될 때

MethodBuilder serialization_builder = m_SerializationWrapperClass.DefineMethod("Serialize_" + type.Name, 
                       MethodAttributes.Public | MethodAttributes.Static, 
                       null, 
                       new [] { type, typeof(BinaryWriter) }); 

ILGenerator s_il_gen = serialization_builder.GetILGenerator(); 
BindingFlags binding_flags_local_non_static = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly; 

s_il_gen.Emit(OpCodes.Ldarg_1); // Eval Stack: BinaryWriter 
s_il_gen.Emit(OpCodes.Ldarg_0); // Eval Stack: BinaryWriter, testmessage 
s_il_gen.Emit(OpCodes.Ldfld, type.GetField("m_CheeseBurgers", binding_flags_local_non_static)); // Eval Stack: BinaryWriter, int 
s_il_gen.Emit(OpCodes.Callvirt, typeof(BinaryWriter).GetMethod("Write", new Type[] { typeof(Int32) })); // Eval Stack: 
s_il_gen.Emit(OpCodes.Ret); 

하면, 예외가 Ldfld의 instruc에 발생한다 : 종류 = 대해서 typeof (CTestMessage) 다음으로) 여기 가능한 밤은 페이스트 .

편집 : 내가 할 수있는 것이 가능한 것이 틀림 없다는 것을 보여주는 더 자세한 내용.DynamicMethod와 MethodBuilder을 위의 코드를 가지고 있지만, 대체 :

DynamicMethod serialization_builder = new DynamicMethod("Serialize_" + type.Name, null, new [] { type, typeof(BinaryWriter) }, true); 

는 이제 DynamicMethod에서 대리자를 만들 :

delegate void TestDelegate(CTestMessage, BinaryWriter); 

TestDelegate test_delegate = serialization_builder.CreateDelegate(typeof(TestDelegate)); 

을이 대리인이 JITed를 얻을 수 및 오류없이 제대로 실행 :

CTestMessage test_message = new CTestMessage(5); 
BinaryWriter writer = new BinaryWriter(some_stream); 
test_delegate(test_message, writer); 

답변

3

문제는 필드가 비공개입니다. public으로 만들면 외부 메서드가 제대로 작동합니다. 당신은 아마 어떤 꽤 많이 반사를 사용할 필요가

// pCurrentClass can be NULL in the case of a global function 
// pCurrentClass it the point from which we're trying to access something 
// pTargetClass is the class containing the member we are trying to access 
// dwMemberAccess is the member access within pTargetClass of the member we are trying to access 
BOOL ClassLoader::CheckAccess(EEClass *pCurrentClass, 
           Assembly *pCurrentAssembly, 
           EEClass *pTargetClass, 
           Assembly *pTargetAssembly, 
           DWORD dwMemberAccess) 
{ 
    // we're trying to access a member that is contained in the class pTargetClass, so need to 
    // check if have access to pTargetClass itself from the current point before worry about 
    // having access to the member within the class 
    if (! CanAccessClass(pCurrentClass, 
         pCurrentAssembly, 
         pTargetClass, 
         pTargetAssembly)) 
     return FALSE; 

    if (IsMdPublic(dwMemberAccess)) 
     return TRUE; 

    // This is module-scope checking, to support C++ file & function statics. 
    if (IsMdPrivateScope(dwMemberAccess)) { 
     if (pCurrentClass == NULL) 
      return FALSE; 

     _ASSERTE(pTargetClass); 

     return (pCurrentClass->GetModule() == pTargetClass->GetModule()); 
    } 

외부 private 필드를 액세스하려면 : SSCLI, [email protected] 사람 - DynamicMethod은 CLR 분명히 내 모듈 private 필드에 액세스 할 수 있기 때문에 개인 임에도 불구하고 작동 목적을 이깁니다.

편집 그냥 명확히하기 위해, 당신이 게시하면 어셈블리를 만들 반사를 사용하지만,이 분야에 액세스 할 반사를 사용하지 않는 생성하는 IL은 - 그 대상 필드 때문에 폭발 평범한 구식 직접 필드 액세스,이다 외부 및 비공개입니다. 당신은 Type.GetField()를 사용하는 일리노이를 방출해야합니다. GetValue()는 꽤 무의미합니다.

+0

답변 해 주셔서 감사합니다. 좀 더 파고 들자 나는이 MSDN 링크를 보았습니다 : [link] (http://msdn.microsoft.com/en-us/library/9syytdak.aspx) 동적 어셈블리가 비 - 공개 반사 허가. 파머. –

0

예, 동적 어셈블리는 .NET 3.5 또는 4+에서 이러한 액세스를 허용하지 않습니다. 나는 똑같은 문제가 있었다. 내 해결 방법은 실제 IL 발광 코드를 ILGenerator를 사용하는 함수로 분해하고 동일한 인수를 사용하여 두 번 호출하는 것입니다. 한 번 (선택적) ILGenerator를 동적 어셈블리의 메소드에서 디스크로 저장합니다. peverify/ildasm/etc. DynamicMethod의 ILGenerator를 사용하면됩니다. 이렇게하면 동일한 IL이 두 방법으로 방출됩니다.