Assembler duplicator [Reflection only] создание дубликата внешней сборки через рефлектор

Assembler duplicator [Reflection only] создание дубликата внешней сборки через рефлектор

post id: 38

post length: 9827

post datetime: 2/3/2010 5:19:51 PM

post ip: 77.51.88.183

Assembler duplicator [Reflection only] создание дубликата внешней сборки через рефлектор

Info:

Пытаемся сделать что-то похожее на ILMerge

http://research.microsoft.com/en-us/people/mbarnett/ILMerge.aspx

при помощи исключительно рефлектора.

Что реализовано :

перенос типов [NN]

переменных типа

методов типа

что опущено:

перенос конструктора типа

+ AssemblyRef

+ MemberRef

Public IData(0) As Byte _

, FileName As String = Nothing _

, METADATA As Dictionary(Of String, METADATA_OBJECT_TOKEN) _

= New Dictionary(Of String, METADATA_OBJECT_TOKEN) _

, ICheckID As Dictionary(Of String, Integer) _

= New Dictionary(Of String, Integer) _

, DICT As Dictionary(Of Integer, OpCode) _

, code As OpCode

'

'|-----------------------------------

'|- структура описателей объектов |

'| где: |

'| ID1 - новый описатель |

'| ID2 - старый описатель |

'|-----------------------------------

Structure METADATA_OBJECT_TOKEN

Dim ID1, ID2 As Integer _

, Name As String

End Structure

Sub CorrectMethodBody(ByRef Body As Byte())

For i As Integer = 0 To Body.Length - 1

Dim instructionValue As UInt16 = 0

If i < Body.Length - 1 Then

instructionValue = BitConverter.ToUInt16(Body, i)

Select Case (instructionValue And OpCodes.Prefix1.Value) = OpCodes.Prefix1.Value

Case True

instructionValue = ((65280 And instructionValue) >> 8) Or ((255 And instructionValue) << 8)

i = i + 1

Case Else

instructionValue = instructionValue And 255

End Select

Else

instructionValue = Buffer.GetByte(Body, i)

End If

If DICT.TryGetValue(instructionValue, code) Then

If code.OperandType = OperandType.InlineMethod Then

Dim ObjData As Integer = BitConverter.ToInt32(Body, i + 1) _

, token As New METADATA_OBJECT_TOKEN

Select Case code.Name.Trim.ToLower

Case "call", "callvirt"

If METADATA.TryGetValue(ObjData, token) Then

Dim b As Byte() _

= BitConverter.GetBytes(token.ID1)

Buffer.BlockCopy(b, 0, Body, i + 1, 4)

End If

End Select

i = i + 4

End If

End If

Next

End Sub

Sub Processing()

IData = OpenFile(FileName)

If IsNothing(IData) Then

Exit Sub

End If

'--------

'EDIT IT:

'--------

FileName = FileName.Split("\")(FileName.Split("\").Length - 1)

Dim IAssemblyName As New AssemblyName() _

, IFileName As String _

= String.Concat("[!] ", FileName)

With IAssemblyName

.Name = Path.GetFileNameWithoutExtension(FileName)

End With

Dim IAssembly As AssemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(IAssemblyName, AssemblyBuilderAccess.Save)

'--------------------------------

'Create opcodes array: |

' [заполняем словарь кодов] |

'--------------------------------

DICT = New Dictionary(Of Integer, OpCode)

For Each [FI] As FieldInfo In GetType(OpCodes).GetFields(BindingFlags.Static Or BindingFlags.Public)

Dim code As OpCode = DirectCast([FI].GetValue(Nothing), OpCode)

DICT.Add(code.Value, code)

Next

'-----------------

' Парсим сборку: |

'-----------------

For Each T1 As [Module] In Reflection.Assembly.Load(IData).GetModules

'-----------

' Module`s |

'-----------

Dim IModuleBuilder As ModuleBuilder = IAssembly.DefineDynamicModule(T1.ScopeName, IFileName)

For Each T2 As [Type] In T1.GetTypes

'-----------

' Type`s |

'-----------

If IsNothing(T2.DeclaringType) Then

Dim ITypeBuilder As TypeBuilder = IModuleBuilder.DefineType(T2.Name, T2.Attributes)

TCase1.Text = String.Concat(TCase1.Text, "The type is copied: ", T2.Name, vbCrLf)

For Each T3 As [FieldInfo] In T2.GetFields()

'------------------------

' Type Field`s |

' переменные [Типа] |

'------------------------

Dim IFieldBuilder As FieldBuilder _

= ITypeBuilder.DefineField(T3.Name, T3.FieldType, T3.Attributes)

TCase1.Text = String.Concat(TCase1.Text, " -> Field: ", T3.Name, vbCrLf)

Next

For Each T4 As [ConstructorInfo] In T2.GetConstructors()

'----------------------

' Type Constructor`s |

'----------------------

' [!] Перенос методов конструктора класса требует

' более детального анализа, потому этот момент

' мы пока опустим

'...

'

Next

For Each T5 As [MethodInfo] In T2.GetMethods()

'--------------------

' Method`s |

' методы [Типа] |

'--------------------

If Not IsNothing(T5) Then

Try

Dim body As Byte() = T5.GetMethodBody.GetILAsByteArray()

If body.Length - 1 > 0 Then

Try

ICheckID.Add(T5.MetadataToken, T5.MetadataToken)

Dim IMethodBuilder As MethodBuilder _

= ITypeBuilder.DefineMethod(T5.Name, T5.Attributes, Nothing, New Type() {})

'------------------------------------------------------------

' Клонировать метод мы можем двумя вариантами: |

' 1. либо через разбор кода метода |

' 2. либо через перенос его тела [наш выбор] |

' |

' [!] НО: |

' При переносе тела необходимо поправить описатели |

' методов для всех операторов [CALL::], т.к. |

' структура метаданных в новой сборке |

' не будет соответствовать оригиналу. |

' |

' Здесь правим только вызовы [методов] |

' логично было бы так же скорректировать: |

' + MemberRef [!] |

' + AssemblyRef [!] |

'-------------------------------------------------------------

CorrectMethodBody(body)

IMethodBuilder.CreateMethodBody(body, body.Length)

Dim token As New METADATA_OBJECT_TOKEN

With token

.Name = T5.Name

.ID1 = IMethodBuilder.GetToken.Token

.ID2 = T5.MetadataToken

End With

METADATA.Add(token.ID2, token)

TCase1.Text = String.Concat(TCase1.Text, " Method: ", T5.Name, " [", Hex(T5.MetadataToken), "] -> [", Hex(token.ID1), "]", vbCrLf)

Catch ex As Exception

End Try

End If

Catch ex As Exception

End Try

End If

Next

Dim IType As Type = ITypeBuilder.CreateType()

End If

Next

Next

IAssembly.Save(IFileName)

End Sub