lib: dll delayload/loadwithalteredsearchpath flags were added in v23, add version check for this

tests: add test for v22 script with dll imports. please note bytecode used here came from a malware sample!
This commit is contained in:
zc 2022-08-18 18:38:45 +01:00
parent 8108a88464
commit a9e627314a
8 changed files with 3278 additions and 14 deletions

Binary file not shown.

File diff suppressed because it is too large Load Diff

View File

@ -59,6 +59,7 @@
<ItemGroup>
<None Include="app.config" />
<None Include="CompiledCode.bin" />
<None Include="CompiledCode_v22.bin" />
<None Include="packages.config" />
</ItemGroup>
<ItemGroup>
@ -81,6 +82,9 @@
<ItemGroup>
<None Include="CompiledCode.txt" />
</ItemGroup>
<ItemGroup>
<None Include="CompiledCode_v22.txt" />
</ItemGroup>
<Import Project="$(VSToolsPath)\TeamTest\Microsoft.TestTools.targets" Condition="Exists('$(VSToolsPath)\TeamTest\Microsoft.TestTools.targets')" />
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">

View File

@ -70,6 +70,16 @@ namespace IFPSLib.Tests.Properties {
}
}
/// <summary>
/// Looks up a localized resource of type System.Byte[].
/// </summary>
internal static byte[] CompiledCode_v22 {
get {
object obj = ResourceManager.GetObject("CompiledCode_v22", resourceCulture);
return ((byte[])(obj));
}
}
/// <summary>
/// Looks up a localized string similar to .version 23
///
@ -97,5 +107,33 @@ namespace IFPSLib.Tests.Properties {
return ResourceManager.GetString("CompiledCodeDisasm", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to .version 23
///
///.entry !MAIN
///
///.type primitive(Pointer) Pointer
///.type primitive(U32) U32
///.type primitive(Variant) Variant
///.type primitive(PChar) PChar
///.type primitive(Currency) Currency
///.type primitive(Extended) Extended
///.type primitive(Double) Double
///.type primitive(Single) Single
///.type primitive(S64) S64
///.type primitive(String) String
///.type primitive(U32) U32_2
///.type primitive(S32) S32
///.type primitive(S16) S16
///.type primitive(U16) U16
///.type primitive(S8) S8
///.type(export) funcptr(void()) ANY [rest of string was truncated]&quot;;.
/// </summary>
internal static string CompiledCodeDisasm_v22 {
get {
return ResourceManager.GetString("CompiledCodeDisasm_v22", resourceCulture);
}
}
}
}

View File

@ -124,4 +124,10 @@
<data name="CompiledCodeDisasm" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\CompiledCode.txt;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8</value>
</data>
<data name="CompiledCodeDisasm_v22" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\CompiledCode_v22.txt;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8</value>
</data>
<data name="CompiledCode_v22" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\CompiledCode_v22.bin;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</data>
</root>

View File

@ -11,6 +11,7 @@ namespace IFPSLib.Tests
public class ScriptTest
{
private static readonly string origB64 = Convert.ToBase64String(Resources.CompiledCode);
private static readonly string origB64_22 = Convert.ToBase64String(Resources.CompiledCode_v22);
[TestMethod]
public void TestLoadSave()
@ -44,5 +45,38 @@ namespace IFPSLib.Tests
var savedB64 = Convert.ToBase64String(script.Save());
Assert.AreEqual(savedB64, origB64);
}
[TestMethod]
public void TestLoadSaveV22()
{
// Load the script.
var script = Script.Load(Resources.CompiledCode_v22);
// Ensure it's not null.
Assert.IsNotNull(script);
// For an official script (compiled by inno setup), the entrypoint is the first function.
Assert.AreEqual(script.EntryPoint, script.Functions[0]);
// Save the script.
var savedBytes = script.Save();
// Convert to base64 for later.
var saved = Convert.ToBase64String(savedBytes);
// Load the saved script.
var scriptSaved = Script.Load(savedBytes);
// Save again.
var savedTwice = Convert.ToBase64String(scriptSaved.Save());
// Ensure both saved scripts equal each other.
Assert.AreEqual(saved, savedTwice);
// Ensure the saved script equals the original.
Assert.AreEqual(saved, origB64_22);
// Ensure the disassemblies are equal.
Assert.AreEqual(script.Disassemble(), scriptSaved.Disassemble());
}
[TestMethod]
public void TestAsmV22()
{
var script = Assembler.Assemble(Resources.CompiledCodeDisasm_v22);
var savedB64 = Convert.ToBase64String(script.Save());
Assert.AreEqual(savedB64, origB64);
}
}
}

View File

@ -27,7 +27,7 @@ namespace IFPSLib.Emit
protected const string ClassString = "class:";
protected const string ComString = "intf:.";
internal static Base Load(BinaryReader br)
internal static Base Load(BinaryReader br, Script script)
{
var fdeclLen = br.Read<uint>();
using (var fdeclMem = new NativeMemoryArray<byte>(fdeclLen, true))
@ -39,21 +39,21 @@ namespace IFPSLib.Emit
if (fdeclSpan.EqualsAsciiString(0, DllString))
{
brDecl.BaseStream.Position = DllString.Length;
return DLL.Load(brDecl);
return DLL.Load(brDecl, script);
}
else if (fdeclSpan.EqualsAsciiString(0, ClassString))
{
brDecl.BaseStream.Position = ClassString.Length;
return Class.Load(brDecl);
return Class.Load(brDecl, script);
}
else if (fdeclSpan.EqualsAsciiString(0, ComString))
{
brDecl.BaseStream.Position = ComString.Length;
return COM.Load(brDecl);
return COM.Load(brDecl, script);
}
else
{
return Internal.Load(brDecl);
return Internal.Load(brDecl, script);
}
}
}
@ -124,14 +124,17 @@ namespace IFPSLib.Emit
internal override string Name => string.Format("{0}!{1}", DllName, ProcedureName);
internal static new DLL Load(BinaryReader br)
internal static new DLL Load(BinaryReader br, Script script)
{
var ret = new DLL();
ret.DllName = br.ReadAsciiStringTerminated();
ret.ProcedureName = br.ReadAsciiStringTerminated();
ret.CallingConvention = (NativeCallingConvention)br.ReadByte();
ret.DelayLoad = br.ReadByte() != 0;
ret.LoadWithAlteredSearchPath = br.ReadByte() != 0;
if (script.FileVersion >= Script.VERSION_MIN_DLL_LOAD_FLAGS)
{
ret.DelayLoad = br.ReadByte() != 0;
ret.LoadWithAlteredSearchPath = br.ReadByte() != 0;
}
ret.LoadArguments(br);
@ -164,8 +167,11 @@ namespace IFPSLib.Emit
bw.WriteAsciiStringTerminated(DllName);
bw.WriteAsciiStringTerminated(ProcedureName);
bw.Write(CallingConvention);
bw.Write<byte>((byte)(DelayLoad ? 1 : 0));
bw.Write<byte>((byte)(LoadWithAlteredSearchPath ? 1 : 0));
if (ctx.FileVersion >= Script.VERSION_MIN_DLL_LOAD_FLAGS)
{
bw.Write<byte>((byte)(DelayLoad ? 1 : 0));
bw.Write<byte>((byte)(LoadWithAlteredSearchPath ? 1 : 0));
}
SaveArguments(bw);
}
}
@ -181,7 +187,7 @@ namespace IFPSLib.Emit
private const byte TERMINATOR = (byte)'|';
internal static new Class Load(BinaryReader br)
internal static new Class Load(BinaryReader br, Script script)
{
var ret = new Class();
@ -297,7 +303,7 @@ namespace IFPSLib.Emit
internal override string Name => string.Format("CoInterface->vtbl[{0}]", VTableIndex);
internal static new COM Load(BinaryReader br)
internal static new COM Load(BinaryReader br, Script script)
{
var ret = new COM();
ret.VTableIndex = br.Read<uint>();
@ -325,7 +331,7 @@ namespace IFPSLib.Emit
public sealed class Internal : Base
{
internal static new Internal Load(BinaryReader br)
internal static new Internal Load(BinaryReader br, Script script)
{
var ret = new Internal();
ret.LoadArguments(br);
@ -378,7 +384,7 @@ namespace IFPSLib.Emit
ret.Name = br.ReadAsciiString(namelen);
if (exported)
{
ret.Declaration = FDecl.Base.Load(br);
ret.Declaration = FDecl.Base.Load(br, script);
if (ret.Declaration.HasReturnArgument) ret.ReturnArgument = UnknownType.Instance;
if (string.IsNullOrEmpty(ret.Name)) ret.Name = ret.Declaration.Name;
}

View File

@ -20,6 +20,7 @@ namespace IFPSLib
internal const int VERSION_MIN_ATTRIBUTES = 21;
internal const int VERSION_MAX_SETSTACKTYPE = 22; // Is this correct?
internal const int VERSION_MIN_DLL_LOAD_FLAGS = 23;
internal const int VERSION_MIN_STATICARRAYSTART = 23;
/// <summary>