145 lines
4.0 KiB
C#
145 lines
4.0 KiB
C#
using System.Reflection.Emit;
|
|
|
|
namespace DTLib.Demystifier.Internal;
|
|
|
|
internal class ILReader
|
|
{
|
|
private static readonly OpCode[] singleByteOpCode;
|
|
private static readonly OpCode[] doubleByteOpCode;
|
|
|
|
private readonly byte[] _cil;
|
|
private int ptr;
|
|
|
|
static ILReader()
|
|
{
|
|
singleByteOpCode = new OpCode[225];
|
|
doubleByteOpCode = new OpCode[31];
|
|
|
|
var fields = GetOpCodeFields();
|
|
|
|
for (var i = 0; i < fields.Length; i++)
|
|
{
|
|
var code = (OpCode)fields[i].GetValue(null)!;
|
|
if (code.OpCodeType == OpCodeType.Nternal)
|
|
continue;
|
|
|
|
if (code.Size == 1)
|
|
singleByteOpCode[code.Value] = code;
|
|
else
|
|
doubleByteOpCode[code.Value & 0xff] = code;
|
|
}
|
|
}
|
|
|
|
|
|
public ILReader(byte[] cil)
|
|
{
|
|
_cil = cil;
|
|
}
|
|
|
|
public OpCode OpCode { get; private set; }
|
|
public int MetadataToken { get; private set; }
|
|
public MemberInfo? Operand { get; private set; }
|
|
|
|
public bool Read(MethodBase methodInfo)
|
|
{
|
|
if (ptr < _cil.Length)
|
|
{
|
|
OpCode = ReadOpCode();
|
|
Operand = ReadOperand(OpCode, methodInfo);
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
private OpCode ReadOpCode()
|
|
{
|
|
var instruction = ReadByte();
|
|
if (instruction < 254)
|
|
return singleByteOpCode[instruction];
|
|
return doubleByteOpCode[ReadByte()];
|
|
}
|
|
|
|
private MemberInfo? ReadOperand(OpCode code, MethodBase methodInfo)
|
|
{
|
|
MetadataToken = 0;
|
|
int inlineLength;
|
|
switch (code.OperandType)
|
|
{
|
|
case OperandType.InlineMethod:
|
|
MetadataToken = ReadInt();
|
|
Type[]? methodArgs = null;
|
|
if (methodInfo.GetType() != typeof(ConstructorInfo) &&
|
|
!methodInfo.GetType().IsSubclassOf(typeof(ConstructorInfo)))
|
|
methodArgs = methodInfo.GetGenericArguments();
|
|
Type[]? typeArgs = null;
|
|
if (methodInfo.DeclaringType is not null) typeArgs = methodInfo.DeclaringType.GetGenericArguments();
|
|
try
|
|
{
|
|
return methodInfo.Module.ResolveMember(MetadataToken, typeArgs, methodArgs);
|
|
}
|
|
catch
|
|
{
|
|
// Can return System.ArgumentException : Token xxx is not a valid MemberInfo token in the scope of module xxx.dll
|
|
return null;
|
|
}
|
|
|
|
case OperandType.InlineNone:
|
|
inlineLength = 0;
|
|
break;
|
|
|
|
case OperandType.ShortInlineBrTarget:
|
|
case OperandType.ShortInlineVar:
|
|
case OperandType.ShortInlineI:
|
|
inlineLength = 1;
|
|
break;
|
|
|
|
case OperandType.InlineVar:
|
|
inlineLength = 2;
|
|
break;
|
|
|
|
case OperandType.InlineBrTarget:
|
|
case OperandType.InlineField:
|
|
case OperandType.InlineI:
|
|
case OperandType.InlineString:
|
|
case OperandType.InlineSig:
|
|
case OperandType.InlineSwitch:
|
|
case OperandType.InlineTok:
|
|
case OperandType.InlineType:
|
|
case OperandType.ShortInlineR:
|
|
inlineLength = 4;
|
|
break;
|
|
|
|
case OperandType.InlineI8:
|
|
case OperandType.InlineR:
|
|
inlineLength = 8;
|
|
break;
|
|
|
|
default:
|
|
return null;
|
|
}
|
|
|
|
for (var i = 0; i < inlineLength; i++) ReadByte();
|
|
|
|
return null;
|
|
}
|
|
|
|
private byte ReadByte()
|
|
{
|
|
return _cil[ptr++];
|
|
}
|
|
|
|
private int ReadInt()
|
|
{
|
|
var b1 = ReadByte();
|
|
var b2 = ReadByte();
|
|
var b3 = ReadByte();
|
|
var b4 = ReadByte();
|
|
return b1 | (b2 << 8) | (b3 << 16) | (b4 << 24);
|
|
}
|
|
|
|
private static FieldInfo[] GetOpCodeFields()
|
|
{
|
|
return typeof(OpCodes).GetFields(BindingFlags.Public | BindingFlags.Static);
|
|
}
|
|
} |