mirror of
https://github.com/Wack0/IFPSTools.NET.git
synced 2025-06-18 10:45:36 -04:00
libifps: allow reading invalid operands
this should allow disassembling some malware samples (etc)
This commit is contained in:
parent
9a8df96f7c
commit
58e78597e7
@ -24,6 +24,10 @@ namespace IFPSLib.Emit
|
||||
/// <summary>
|
||||
/// Operand refers to an element of an indexed variable, where the index is specified as a local or global variable, or an argument.
|
||||
/// </summary>
|
||||
IndexedVariable
|
||||
IndexedVariable,
|
||||
/// <summary>
|
||||
/// Operand is invalid. Stops execution.
|
||||
/// </summary>
|
||||
Invalid = 0xff
|
||||
}
|
||||
}
|
||||
|
@ -5,6 +5,8 @@ using System.IO;
|
||||
using System.Text;
|
||||
using System.Linq;
|
||||
using System.Collections;
|
||||
using System.Net;
|
||||
using System.Data;
|
||||
|
||||
namespace IFPSLib.Emit
|
||||
{
|
||||
@ -380,6 +382,31 @@ namespace IFPSLib.Emit
|
||||
return FixBranchOffset(br, br.Read<uint>());
|
||||
}
|
||||
|
||||
private static T TryReadIndex<T>(BinaryReader br, IList<T> list)
|
||||
{
|
||||
var index = (int)br.Read<uint>();
|
||||
if (index < list.Count) return list[index];
|
||||
return default;
|
||||
}
|
||||
|
||||
private static Operand TryReadFunction(BinaryReader br, Script script)
|
||||
{
|
||||
var func = TryReadIndex(br, script.Functions);
|
||||
if (func == null) func = new ScriptFunction()
|
||||
{
|
||||
Name = "INVALID",
|
||||
Arguments = new List<FunctionArgument>()
|
||||
};
|
||||
return Operand.Create(func);
|
||||
}
|
||||
|
||||
private static Operand TryReadType(BinaryReader br, Script script)
|
||||
{
|
||||
var type = TryReadIndex(br, script.Types);
|
||||
if (type == null) type = UnknownType.Instance;
|
||||
return Operand.Create(type);
|
||||
}
|
||||
|
||||
internal static Instruction Load(BinaryReader br, Script script, ScriptFunction function)
|
||||
{
|
||||
var ret = new Instruction();
|
||||
@ -437,10 +464,10 @@ namespace IFPSLib.Emit
|
||||
ret.m_Operands = new List<Operand>(2) { new Operand(new TypedData(InstructionType.Instance, FixBranchOffset(br, brOffset))), valOp };
|
||||
break;
|
||||
case OperandType.InlineFunction:
|
||||
ret.m_Operands = new List<Operand>(1) { Operand.Create(script.Functions[(int)br.Read<uint>()]) };
|
||||
ret.m_Operands = new List<Operand>(1) { TryReadFunction(br, script) };
|
||||
break;
|
||||
case OperandType.InlineType:
|
||||
ret.m_Operands = new List<Operand>(1) { Operand.Create(script.Types[(int)br.Read<uint>()]) };
|
||||
ret.m_Operands = new List<Operand>(1) { TryReadType(br, script) };
|
||||
break;
|
||||
case OperandType.InlineCmpValue:
|
||||
ret.m_Operands = new List<Operand>(3) { Operand.LoadValue(br, script, function), Operand.LoadValue(br, script, function), Operand.LoadValue(br, script, function) };
|
||||
@ -449,7 +476,7 @@ namespace IFPSLib.Emit
|
||||
ret.m_Operands = new List<Operand>(3) {
|
||||
Operand.LoadValue(br, script, function),
|
||||
Operand.LoadValue(br, script, function),
|
||||
Operand.Create(script.Types[(int)br.Read<uint>()])
|
||||
TryReadType(br, script)
|
||||
};
|
||||
break;
|
||||
case OperandType.InlineEH:
|
||||
@ -466,7 +493,7 @@ namespace IFPSLib.Emit
|
||||
break;
|
||||
case OperandType.InlineTypeVariable:
|
||||
ret.m_Operands = new List<Operand>(2) {
|
||||
Operand.Create(script.Types[(int)br.Read<uint>()]),
|
||||
TryReadType(br, script),
|
||||
new Operand(VariableBase.Load(br, script, function))
|
||||
};
|
||||
return ret;
|
||||
|
@ -105,6 +105,12 @@ namespace IFPSLib.Emit
|
||||
m_Value = op.m_Value;
|
||||
}
|
||||
|
||||
private Operand(Exception ex = null)
|
||||
{
|
||||
m_Type = BytecodeOperandType.Invalid;
|
||||
if (ex != null) m_Value.Immediate = new TypedData(Types.UnknownType.Instance, ex);
|
||||
}
|
||||
|
||||
public static Operand Create<TType>(Types.PrimitiveType type, TType value)
|
||||
{
|
||||
return new Operand(TypedData.Create(type, value));
|
||||
@ -156,31 +162,39 @@ namespace IFPSLib.Emit
|
||||
return new Operand(arr, varIdx);
|
||||
}
|
||||
|
||||
public static Operand CreateInvalid(Exception ex = null)
|
||||
{
|
||||
return new Operand(ex);
|
||||
}
|
||||
|
||||
internal static Operand LoadValue(BinaryReader br, Script script, ScriptFunction function)
|
||||
{
|
||||
var type = (BytecodeOperandType)br.ReadByte();
|
||||
|
||||
switch (type)
|
||||
try
|
||||
{
|
||||
case BytecodeOperandType.Variable:
|
||||
return new Operand(VariableBase.Load(br, script, function));
|
||||
case BytecodeOperandType.Immediate:
|
||||
return new Operand(TypedData.Load(br, script));
|
||||
case BytecodeOperandType.IndexedImmediate:
|
||||
{
|
||||
var variable = VariableBase.Load(br, script, function);
|
||||
var idx = br.Read<uint>();
|
||||
return Create(variable, idx);
|
||||
}
|
||||
case BytecodeOperandType.IndexedVariable:
|
||||
{
|
||||
var variable = VariableBase.Load(br, script, function);
|
||||
var idx = VariableBase.Load(br, script, function);
|
||||
return new Operand(variable, idx);
|
||||
}
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException(string.Format("Invalid operand type {0}", (byte)type));
|
||||
}
|
||||
switch (type)
|
||||
{
|
||||
case BytecodeOperandType.Variable:
|
||||
return new Operand(VariableBase.Load(br, script, function));
|
||||
case BytecodeOperandType.Immediate:
|
||||
return new Operand(TypedData.Load(br, script));
|
||||
case BytecodeOperandType.IndexedImmediate:
|
||||
{
|
||||
var variable = VariableBase.Load(br, script, function);
|
||||
var idx = br.Read<uint>();
|
||||
return Create(variable, idx);
|
||||
}
|
||||
case BytecodeOperandType.IndexedVariable:
|
||||
{
|
||||
var variable = VariableBase.Load(br, script, function);
|
||||
var idx = VariableBase.Load(br, script, function);
|
||||
return new Operand(variable, idx);
|
||||
}
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException(string.Format("Invalid operand type {0}", (byte)type));
|
||||
}
|
||||
} catch (Exception ex) { return new Operand(ex); }
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
@ -195,6 +209,8 @@ namespace IFPSLib.Emit
|
||||
return String.Format("{0}[{1}]", IndexedVariable.Name, IndexImmediate);
|
||||
case BytecodeOperandType.IndexedVariable:
|
||||
return String.Format("{0}[{1}]", IndexedVariable.Name, IndexVariable.Name);
|
||||
case BytecodeOperandType.Invalid:
|
||||
return "$INVALID";
|
||||
default:
|
||||
return "";
|
||||
}
|
||||
|
@ -313,7 +313,11 @@ namespace IFPSLib
|
||||
switch (Type.BaseType)
|
||||
{
|
||||
case PascalTypeCode.Type:
|
||||
return ValueAs<IType>().Name;
|
||||
{
|
||||
var type = ValueAs<IType>();
|
||||
if (type is UnknownType) return "$UNKNOWN";
|
||||
return type.Name;
|
||||
}
|
||||
case PascalTypeCode.Instruction:
|
||||
if (Value == null) return "null";
|
||||
return string.Format("loc_{0}", ValueAs<Emit.Instruction>().Offset.ToString("x"));
|
||||
|
@ -19,7 +19,7 @@ Library implementing an assembler for IFPS scripts.
|
||||
|
||||
Depends on IFPSLib.
|
||||
|
||||
Assembling the output of IFPSLib's disassembler is expected to output an identical binary, not doing so is considered a bug.
|
||||
Assembling the output of IFPSLib's disassembler (for sane bytecode with no invalid instructions/operands) is expected to output an identical binary, not doing so is considered a bug.
|
||||
|
||||
## ifpsdasm
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user