Author:水如烟
.NET的安全性问题是一个综合性的问题,这不是我现在能学习好理解透的,我估计也不是一般的.NET的编码者能够把握住应用好的。
至少,在代码集中,.NET的全局变量,或者说受Private修饰保护的对象,是不安全的。
在现实中有一个传话游戏,其要义是,信息在信息链的传递过程中会失真。.NET也一样,很难保证一个关键的信息在传递的过程中保持“纯洁”而不被潜伏中的攻击而受污染。特别是在大规模的代码集中。
SecureString类
在.NET的设计中,SecureString类应该是安全系数比较高的。它有两个作用,一是赋值后MakeReadOnly以便在后续的传递中不致修改,二是在必要的时候进行销毁。但是用它来传递关键信息,比如密码,还是不安全的。这个类里头有一个m_readOnly As Boolean的Private变量来保存当前实例可否修改的状态,你可以重置该值为False,便可以对它重新修改,MakeReadOnly也就失去了它本身的作用。
ReadOnlyCollection类
它的本义是不能对当前实例的项目进行增删。在它的设计中,用一个list的Private变量来存储当前集合。事实并不能保证它的作用。你甚至可以用自己的list替换了它。
测试代码
Imports System.Runtime.InteropServices
Imports System.Collections.ObjectModel
Public Class NoSecure
Public Sub TestSecureString()
Dim t As New Security.SecureString
t.AppendChar("A"c)
t.AppendChar("B"c)
t.MakeReadOnly()
Console.WriteLine(Marshal.PtrToStringAuto(Marshal.SecureStringToBSTR(t)))
Dim h As New LzmTW.uSystem.uReflection.TypeHelper(t)
h.SetMemberValue("m_readOnly", False)
t.AppendChar("C"c)
Console.WriteLine(Marshal.PtrToStringAuto(Marshal.SecureStringToBSTR(t)))
End Sub
Public Sub TestReadonlyCollection()
Dim t As New ReadOnlyCollection(Of String)(New String() {"A", "B", "C"})
For i As Integer = 0 To t.Count - 1
Console.Write(t.Item(i))
Next
Console.WriteLine()
Dim h As New LzmTW.uSystem.uReflection.TypeHelper(t)
Dim mNowList As New List(Of String)
mNowList.AddRange(New String() {"A", "B", "C", "D"})
h.SetMemberValue("list", mNowList)
For i As Integer = 0 To t.Count - 1
Console.Write(t.Item(i))
Next
End Sub
End Class
Imports System.Collections.ObjectModel
Public Class NoSecure
Public Sub TestSecureString()
Dim t As New Security.SecureString
t.AppendChar("A"c)
t.AppendChar("B"c)
t.MakeReadOnly()
Console.WriteLine(Marshal.PtrToStringAuto(Marshal.SecureStringToBSTR(t)))
Dim h As New LzmTW.uSystem.uReflection.TypeHelper(t)
h.SetMemberValue("m_readOnly", False)
t.AppendChar("C"c)
Console.WriteLine(Marshal.PtrToStringAuto(Marshal.SecureStringToBSTR(t)))
End Sub
Public Sub TestReadonlyCollection()
Dim t As New ReadOnlyCollection(Of String)(New String() {"A", "B", "C"})
For i As Integer = 0 To t.Count - 1
Console.Write(t.Item(i))
Next
Console.WriteLine()
Dim h As New LzmTW.uSystem.uReflection.TypeHelper(t)
Dim mNowList As New List(Of String)
mNowList.AddRange(New String() {"A", "B", "C", "D"})
h.SetMemberValue("list", mNowList)
For i As Integer = 0 To t.Count - 1
Console.Write(t.Item(i))
Next
End Sub
End Class
上面用到的 TypeHelp类:
TypeHelper.Methods.vb
Imports System.Reflection
Namespace LzmTW.uSystem.uReflection
Partial Class TypeHelper
Public Sub SetCurrentObj(ByVal obj As Object)
If Not obj Is Nothing AndAlso Not Me.CurrentType.IsInstanceOfType(obj) Then
Throw New ArgumentException("实例类型与内部类型不相符")
End If
Me.gCurrentObjct = obj
End Sub
Public Function GetMemberValue(ByVal name As String, ByVal ParamArray args() As Object) As Object
Return Me.CurrentType.InvokeMember( _
name, _
MemberGetBinding, _
Nothing, _
Me.CurrentObject, _
args)
End Function
Public Sub SetMemberValue(ByVal name As String, ByVal ParamArray args() As Object)
Me.CurrentType.InvokeMember( _
name, _
MemberSetBinding, _
Nothing, _
Me.CurrentObject, _
args)
End Sub
Public Function MethodInvoke(ByVal name As String, ByVal ParamArray args() As Object) As Object
Return Me.CurrentType.InvokeMember( _
name, _
MethodBinding, _
Nothing, _
Me.CurrentObject, _
args)
End Function
Public Function NewInstance(ByVal ParamArray args() As Object) As Object
Dim mParaCount As Integer = args.Length
Dim mCtors As ConstructorInfo() = Me.CurrentType.GetConstructors(MethodBinding)
For Each ctro As ConstructorInfo In mCtors
If ctro.GetParameters.Length = mParaCount Then
Return ctro.Invoke(args)
End If
Next
Return Nothing
End Function
''' <summary>
''' 可以使用*?[abc][!abc],忽略大小写
''' </summary>
Public Function FindMember(ByVal name As String) As MemberInfo()
If String.IsNullOrEmpty(name) OrElse name = "*" Then
Return Me.CurrentType.GetMembers(Binding)
End If
Dim mPattern As String = "*[*?]*"
If Not name Like mPattern Then Return Me.CurrentType.GetMember(name, Binding)
Dim mArray As New List(Of MemberInfo)
For Each m As MemberInfo In Me.CurrentType.GetMembers(Binding)
If m.Name.ToLower Like name.ToLower Then
mArray.Add(m)
End If
Next
Return mArray.ToArray
End Function
Private MemberGetBinding As BindingFlags = _
BindingFlags.Instance Or _
BindingFlags.Public Or _
BindingFlags.NonPublic Or _
BindingFlags.Static Or _
BindingFlags.GetField Or _
BindingFlags.GetProperty Or _
BindingFlags.IgnoreCase
Private MemberSetBinding As BindingFlags = _
BindingFlags.Instance Or _
BindingFlags.Public Or _
BindingFlags.NonPublic Or _
BindingFlags.Static Or _
BindingFlags.SetField Or _
BindingFlags.SetProperty Or _
BindingFlags.IgnoreCase
Private MethodBinding As BindingFlags = _
BindingFlags.Instance Or _
BindingFlags.Public Or _
BindingFlags.NonPublic Or _
BindingFlags.Static Or _
BindingFlags.InvokeMethod Or _
BindingFlags.IgnoreCase
End Class
End Namespace
Namespace LzmTW.uSystem.uReflection
Partial Class TypeHelper
Public Sub SetCurrentObj(ByVal obj As Object)
If Not obj Is Nothing AndAlso Not Me.CurrentType.IsInstanceOfType(obj) Then
Throw New ArgumentException("实例类型与内部类型不相符")
End If
Me.gCurrentObjct = obj
End Sub
Public Function GetMemberValue(ByVal name As String, ByVal ParamArray args() As Object) As Object
Return Me.CurrentType.InvokeMember( _
name, _
MemberGetBinding, _
Nothing, _
Me.CurrentObject, _
args)
End Function
Public Sub SetMemberValue(ByVal name As String, ByVal ParamArray args() As Object)
Me.CurrentType.InvokeMember( _
name, _
MemberSetBinding, _
Nothing, _
Me.CurrentObject, _
args)
End Sub
Public Function MethodInvoke(ByVal name As String, ByVal ParamArray args() As Object) As Object
Return Me.CurrentType.InvokeMember( _
name, _
MethodBinding, _
Nothing, _
Me.CurrentObject, _
args)
End Function
Public Function NewInstance(ByVal ParamArray args() As Object) As Object
Dim mParaCount As Integer = args.Length
Dim mCtors As ConstructorInfo() = Me.CurrentType.GetConstructors(MethodBinding)
For Each ctro As ConstructorInfo In mCtors
If ctro.GetParameters.Length = mParaCount Then
Return ctro.Invoke(args)
End If
Next
Return Nothing
End Function
''' <summary>
''' 可以使用*?[abc][!abc],忽略大小写
''' </summary>
Public Function FindMember(ByVal name As String) As MemberInfo()
If String.IsNullOrEmpty(name) OrElse name = "*" Then
Return Me.CurrentType.GetMembers(Binding)
End If
Dim mPattern As String = "*[*?]*"
If Not name Like mPattern Then Return Me.CurrentType.GetMember(name, Binding)
Dim mArray As New List(Of MemberInfo)
For Each m As MemberInfo In Me.CurrentType.GetMembers(Binding)
If m.Name.ToLower Like name.ToLower Then
mArray.Add(m)
End If
Next
Return mArray.ToArray
End Function
Private MemberGetBinding As BindingFlags = _
BindingFlags.Instance Or _
BindingFlags.Public Or _
BindingFlags.NonPublic Or _
BindingFlags.Static Or _
BindingFlags.GetField Or _
BindingFlags.GetProperty Or _
BindingFlags.IgnoreCase
Private MemberSetBinding As BindingFlags = _
BindingFlags.Instance Or _
BindingFlags.Public Or _
BindingFlags.NonPublic Or _
BindingFlags.Static Or _
BindingFlags.SetField Or _
BindingFlags.SetProperty Or _
BindingFlags.IgnoreCase
Private MethodBinding As BindingFlags = _
BindingFlags.Instance Or _
BindingFlags.Public Or _
BindingFlags.NonPublic Or _
BindingFlags.Static Or _
BindingFlags.InvokeMethod Or _
BindingFlags.IgnoreCase
End Class
End Namespace
TypeHelper.vb
Imports System.Reflection
Namespace LzmTW.uSystem.uReflection
Public Class TypeHelper
Private gType As Type
Private gCurrentObjct As Object
Public ReadOnly Property CurrentType() As Type
Get
Return gType
End Get
End Property
Public ReadOnly Property CurrentObject() As Object
Get
Return gCurrentObjct
End Get
End Property
Sub New(ByVal referrenceObj As Object)
If Me.IsType(referrenceObj) Then
Me.gType = CType(referrenceObj, Type)
Else
Me.gType = referrenceObj.GetType
Me.gCurrentObjct = referrenceObj
End If
End Sub
Sub New(ByVal assembly As Assembly, ByVal fullTypeName As String)
Me.InternalCreate(assembly, fullTypeName)
Me.InternalCheckIsValid(fullTypeName, True)
End Sub
Sub New(ByVal referrenceType As Type, ByVal typeName As String, Optional ByVal isNestedType As Boolean = False)
Dim mAssembly As Assembly = referrenceType.Assembly
Me.InternalCreate(mAssembly, typeName)
If Me.InternalCheckIsValid(typeName, False) Then Return
Dim mFullTypeName As String = GetFullTypeName(referrenceType, typeName, isNestedType)
Me.InternalCreate(mAssembly, mFullTypeName)
Me.InternalCheckIsValid(mFullTypeName, True)
End Sub
Private Sub InternalCreate(ByVal ass As Assembly, ByVal fulltypename As String)
gType = ass.GetType(fulltypename, False, True)
End Sub
Private Function InternalCheckIsValid(ByVal typename As String, ByVal throwOnError As Boolean) As Boolean
If gType Is Nothing Then
If throwOnError Then
Throw New ArgumentException(String.Format("typeName: {0} 不存在", typename))
Else
Return False
End If
End If
Return True
End Function
Private Function GetFullTypeName(ByVal referrenceType As Type, ByVal typeName As String, ByVal isNestedType As Boolean) As String
Dim mFullName As String = Nothing
Dim mRefFullName As String = referrenceType.FullName
If isNestedType Then
mFullName = String.Concat(mRefFullName, "+", typeName)
Else
Dim mLastIndex As Integer = mRefFullName.LastIndexOf(referrenceType.Name)
mFullName = String.Concat(mRefFullName.Substring(0, mLastIndex), typeName)
End If
Return mFullName
End Function
Private Function IsType(ByVal instance As Object) As Boolean
Return instance.GetType.IsSubclassOf(GetType(Type))
End Function
Public Const Binding As BindingFlags = _
BindingFlags.Instance Or _
BindingFlags.Public Or _
BindingFlags.NonPublic Or _
BindingFlags.Static Or _
BindingFlags.CreateInstance Or _
BindingFlags.IgnoreCase
End Class
End Namespace
Namespace LzmTW.uSystem.uReflection
Public Class TypeHelper
Private gType As Type
Private gCurrentObjct As Object
Public ReadOnly Property CurrentType() As Type
Get
Return gType
End Get
End Property
Public ReadOnly Property CurrentObject() As Object
Get
Return gCurrentObjct
End Get
End Property
Sub New(ByVal referrenceObj As Object)
If Me.IsType(referrenceObj) Then
Me.gType = CType(referrenceObj, Type)
Else
Me.gType = referrenceObj.GetType
Me.gCurrentObjct = referrenceObj
End If
End Sub
Sub New(ByVal assembly As Assembly, ByVal fullTypeName As String)
Me.InternalCreate(assembly, fullTypeName)
Me.InternalCheckIsValid(fullTypeName, True)
End Sub
Sub New(ByVal referrenceType As Type, ByVal typeName As String, Optional ByVal isNestedType As Boolean = False)
Dim mAssembly As Assembly = referrenceType.Assembly
Me.InternalCreate(mAssembly, typeName)
If Me.InternalCheckIsValid(typeName, False) Then Return
Dim mFullTypeName As String = GetFullTypeName(referrenceType, typeName, isNestedType)
Me.InternalCreate(mAssembly, mFullTypeName)
Me.InternalCheckIsValid(mFullTypeName, True)
End Sub
Private Sub InternalCreate(ByVal ass As Assembly, ByVal fulltypename As String)
gType = ass.GetType(fulltypename, False, True)
End Sub
Private Function InternalCheckIsValid(ByVal typename As String, ByVal throwOnError As Boolean) As Boolean
If gType Is Nothing Then
If throwOnError Then
Throw New ArgumentException(String.Format("typeName: {0} 不存在", typename))
Else
Return False
End If
End If
Return True
End Function
Private Function GetFullTypeName(ByVal referrenceType As Type, ByVal typeName As String, ByVal isNestedType As Boolean) As String
Dim mFullName As String = Nothing
Dim mRefFullName As String = referrenceType.FullName
If isNestedType Then
mFullName = String.Concat(mRefFullName, "+", typeName)
Else
Dim mLastIndex As Integer = mRefFullName.LastIndexOf(referrenceType.Name)
mFullName = String.Concat(mRefFullName.Substring(0, mLastIndex), typeName)
End If
Return mFullName
End Function
Private Function IsType(ByVal instance As Object) As Boolean
Return instance.GetType.IsSubclassOf(GetType(Type))
End Function
Public Const Binding As BindingFlags = _
BindingFlags.Instance Or _
BindingFlags.Public Or _
BindingFlags.NonPublic Or _
BindingFlags.Static Or _
BindingFlags.CreateInstance Or _
BindingFlags.IgnoreCase
End Class
End Namespace