Some optimizations and cleanups

Some optimizations and cleanups of the code.

- BinaryFile.cs -- Avoiding allocations and unnecessary O(2) loop in the "ParseDotNetFunctions"

- BinaryEmulator.cs -- Changing some fixed values to readonly and optimizing the sorting of memory region indexes. and also optimizing GetThreadsSnapshot by not using LINQ.

- WindowsGuest.cs -- Avoiding LINQ  in the rules path and caching ntdll module. also avoiding BitConverter.GetBytes and reusing the thread state.

The rest of the commit are to ensure the state of the thread is cleared after it's death.
This commit is contained in:
AdvDebug
2026-06-21 01:52:42 +03:00
parent 7c2c71a9af
commit ed25c1d579
10 changed files with 287 additions and 262 deletions
+165 -170
View File
@@ -102,6 +102,7 @@ namespace Brovan.Core
/// <returns>The structure read from the supplied data.</returns>
/// <exception cref="ArgumentNullException"></exception>
/// <exception cref="ArgumentOutOfRangeException"></exception>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public T ReadStruct<T>(byte[] Data, int Offset) where T : unmanaged
{
if (Data == null || Data.Length == 0)
@@ -133,6 +134,7 @@ namespace Brovan.Core
/// <returns>The structure read from the supplied data.</returns>
/// <exception cref="ArgumentNullException"></exception>
/// <exception cref="ArgumentOutOfRangeException"></exception>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public T ReadStruct<T>(ReadOnlySpan<byte> Data, int Offset) where T : unmanaged
{
if (Data.IsEmpty || Data.Length == 0)
@@ -787,14 +789,13 @@ namespace Brovan.Core
MaxEnd = SectionEnd;
uint EndOffset = StartOffset;
byte[] ChunkData = GC.AllocateUninitializedArray<byte>(16);
for (uint EndSearch = StartOffset; EndSearch < MaxEnd; EndSearch++)
{
if (EndSearch + 16 > (uint)Data.Length)
break;
// Disassemble a chunk of code at the current position
byte[] ChunkData = new byte[16];
DataSpan.Slice((int)EndSearch, 16).CopyTo(ChunkData);
X86Instruction[] Instructions = Disassembler.Disassemble(ChunkData, EndSearch);
@@ -1053,6 +1054,7 @@ namespace Brovan.Core
/// <param name="Rva">The RVA to convert.</param>
/// <param name="FileOffset">Receives the file offset when conversion succeeds.</param>
/// <returns>True if the RVA could be converted; otherwise, false.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private bool TryRvaToFileOffset(uint Rva, out uint FileOffset)
{
if (RvaToFileOffsetCache != null &&
@@ -1714,7 +1716,7 @@ namespace Brovan.Core
/// <summary>
/// Parse and read .NET Binary information.
/// </summary>
private void ParseDotNetFunctions()
private unsafe void ParseDotNetFunctions()
{
List<DotNetFunction> DotNetFunctionList = new List<DotNetFunction>();
List<DotNetProperty> DotNetPropertyList = new List<DotNetProperty>();
@@ -1722,196 +1724,189 @@ namespace Brovan.Core
List<DotNetType> DotNetTypeList = new List<DotNetType>();
List<DotNetMember> DotNetMemberList = new List<DotNetMember>();
MetadataReader MetadataReader = null;
byte[] Data = this.Data.ToArray();
try
{
using (MemoryStream Stream = new MemoryStream(Data))
// Gonna use PEReader (no manual parsing for now)
PEReader PEReader = new PEReader(Data.BasePtr, Data.Length, true);
MetadataReader = PEReader.GetMetadataReader();
foreach (TypeDefinitionHandle TypeHandle in MetadataReader.TypeDefinitions)
{
// Gonna use PEReader (no manual parsing for now)
PEReader PEReader = new PEReader(Stream);
MetadataReader = PEReader.GetMetadataReader();
foreach (TypeDefinitionHandle TypeHandle in MetadataReader.TypeDefinitions)
TypeDefinition TypeDef = MetadataReader.GetTypeDefinition(TypeHandle);
string TypeNamespace = TypeDef.Namespace.IsNil ? "" : MetadataReader.GetString(TypeDef.Namespace);
string TypeName = MetadataReader.GetString(TypeDef.Name);
AssemblyDefinition AsmDef = MetadataReader.GetAssemblyDefinition();
string AssemblyName = MetadataReader.GetString(AsmDef.Name);
foreach (MethodDefinitionHandle MethodHandle in TypeDef.GetMethods())
{
TypeDefinition TypeDef = MetadataReader.GetTypeDefinition(TypeHandle);
string TypeNamespace = TypeDef.Namespace.IsNil ? "" : MetadataReader.GetString(TypeDef.Namespace);
string TypeName = MetadataReader.GetString(TypeDef.Name);
AssemblyDefinition AsmDef = MetadataReader.GetAssemblyDefinition();
string AssemblyName = MetadataReader.GetString(AsmDef.Name);
MethodDefinition MethodDef = MetadataReader.GetMethodDefinition(MethodHandle);
string MethodName = MetadataReader.GetString(MethodDef.Name);
foreach (MethodDefinitionHandle MethodHandle in TypeDef.GetMethods())
if (MethodDef.RelativeVirtualAddress == 0)
continue;
uint RVA = (uint)MethodDef.RelativeVirtualAddress;
uint FileOffset = RvaToFileOffset(RVA, PE.Sections);
if (FileOffset == 0 || FileOffset >= Data.Length - 1)
continue;
byte HeaderByte = Data[FileOffset];
bool IsTiny = (HeaderByte & 0x03) == 0x02;
uint CodeSize;
if (IsTiny)
{
MethodDefinition MethodDef = MetadataReader.GetMethodDefinition(MethodHandle);
string MethodName = MetadataReader.GetString(MethodDef.Name);
if (MethodDef.RelativeVirtualAddress == 0)
CodeSize = (uint)(HeaderByte >> 2);
FileOffset += 1;
}
else
{
if (FileOffset + 12 > Data.Length)
continue;
uint RVA = (uint)MethodDef.RelativeVirtualAddress;
uint FileOffset = RvaToFileOffset(RVA, PE.Sections);
if (FileOffset == 0 || FileOffset >= Data.Length - 1)
ushort Flags_Size = ReadStruct<ushort>(DataSpan, (int)FileOffset);
CodeSize = ReadStruct<uint>(DataSpan, (int)FileOffset + 4);
FileOffset += 12;
if (CodeSize > 1024 * 1024)
continue;
}
byte HeaderByte = Data[FileOffset];
bool IsTiny = (HeaderByte & 0x03) == 0x02;
uint CodeSize;
if (CodeSize == 0 || FileOffset + CodeSize > Data.Length)
continue;
if (IsTiny)
ReadOnlySpan<byte> ILCode = DataSpan.Slice((int)FileOffset, (int)CodeSize);
int ParameterCount = 0;
foreach (ParameterHandle ParamHandle in MethodDef.GetParameters())
{
ParameterCount++;
}
BlobReader SignatureReader = MetadataReader.GetBlobReader(MethodDef.Signature);
byte Convention = SignatureReader.ReadByte();
bool IsInstance = (Convention & 0x20) != 0;
int Token = MetadataTokens.GetToken(MethodHandle);
if (!string.IsNullOrEmpty(MethodName))
{
int LocalsCount = 0;
if (ILCode != null)
{
CodeSize = (uint)(HeaderByte >> 2);
FileOffset += 1;
}
else
{
if (FileOffset + 12 > Data.Length)
continue;
ushort Flags_Size = ReadStruct<ushort>(Data, (int)FileOffset);
CodeSize = ReadStruct<uint>(Data, (int)FileOffset + 4);
FileOffset += 12;
if (CodeSize > 1024 * 1024)
continue;
}
if (CodeSize == 0 || FileOffset + CodeSize > Data.Length)
continue;
byte[] ILCode = new byte[CodeSize];
Array.Copy(Data, (int)FileOffset, ILCode, 0, (int)CodeSize);
int ParameterCount = 0;
foreach (ParameterHandle ParamHandle in MethodDef.GetParameters())
{
ParameterCount++;
}
BlobReader SignatureReader = MetadataReader.GetBlobReader(MethodDef.Signature);
byte Convention = SignatureReader.ReadByte();
bool IsInstance = (Convention & 0x20) != 0;
int Token = MetadataTokens.GetToken(MethodHandle);
if (!string.IsNullOrEmpty(MethodName))
{
int LocalsCount = 0;
if (ILCode != null)
MethodBodyBlock MethodBody = PEReader.GetMethodBody(MethodDef.RelativeVirtualAddress);
if (!MethodBody.LocalSignature.IsNil)
{
MethodBodyBlock MethodBody = PEReader.GetMethodBody(MethodDef.RelativeVirtualAddress);
if (!MethodBody.LocalSignature.IsNil)
{
StandaloneSignature LocalSignature = MetadataReader.GetStandaloneSignature(MethodBody.LocalSignature);
StandaloneSignature LocalSignature = MetadataReader.GetStandaloneSignature(MethodBody.LocalSignature);
// Get the signature blob reader to read the local variables count
BlobReader LocalSignatureReader = MetadataReader.GetBlobReader(LocalSignature.Signature);
SignatureHeader SigHeader = LocalSignatureReader.ReadSignatureHeader();
if (SigHeader.Kind == SignatureKind.LocalVariables)
{
LocalsCount = LocalSignatureReader.ReadCompressedInteger();
}
// Get the signature blob reader to read the local variables count
BlobReader LocalSignatureReader = MetadataReader.GetBlobReader(LocalSignature.Signature);
SignatureHeader SigHeader = LocalSignatureReader.ReadSignatureHeader();
if (SigHeader.Kind == SignatureKind.LocalVariables)
{
LocalsCount = LocalSignatureReader.ReadCompressedInteger();
}
}
DotNetFunctionList.Add(new DotNetFunction
{
FunctionName = MethodName,
TypeName = TypeName,
RVA = RVA,
FileOffset = FileOffset,
CodeSize = CodeSize,
Flags = (ushort)MethodDef.Attributes,
Token = Token,
ParameterCount = ParameterCount,
LocalsCount = LocalsCount,
ImplFlags = (MethodImplementations)MethodDef.ImplAttributes,
ILCode = ILCode,
IsInstance = IsInstance,
DeclaringType = TypeNamespace + "." + TypeName,
AssemblyName = AssemblyName
});
}
}
foreach (PropertyDefinitionHandle PropertyHandle in TypeDef.GetProperties())
{
PropertyDefinition PropertyDef = MetadataReader.GetPropertyDefinition(PropertyHandle);
int Token = MetadataTokens.GetToken(PropertyHandle);
DotNetPropertyList.Add(new DotNetProperty
{
PropertyName = MetadataReader.GetString(PropertyDef.Name),
Token = Token
});
}
foreach (FieldDefinitionHandle FieldHandle in TypeDef.GetFields())
{
FieldDefinition FieldDef = MetadataReader.GetFieldDefinition(FieldHandle);
int Token = MetadataTokens.GetToken(FieldHandle);
DotNetFieldList.Add(new DotNetField()
{
FieldName = MetadataReader.GetString(FieldDef.Name),
Token = Token
});
}
DotNetTypeList.Add(new DotNetType
{
TypeName = TypeName,
Token = MetadataReader.GetToken(TypeHandle)
});
foreach (MemberReferenceHandle MemberHandle in MetadataReader.MemberReferences)
{
MemberReference MemberRef = MetadataReader.GetMemberReference(MemberHandle);
string MemberName = MetadataReader.GetString(MemberRef.Name);
int Token = MetadataTokens.GetToken(MemberHandle);
string TypeNameMember = string.Empty;
string DeclaringType = string.Empty;
string AssemblyNameMember = string.Empty;
EntityHandle ParentHandle = MemberRef.Parent;
if (ParentHandle.Kind == HandleKind.TypeReference)
{
TypeReference TypeRef = MetadataReader.GetTypeReference((TypeReferenceHandle)ParentHandle);
TypeNameMember = MetadataReader.GetString(TypeRef.Name);
DeclaringType = MetadataReader.GetString(TypeRef.Namespace) + "." + TypeNameMember;
EntityHandle Scope = TypeRef.ResolutionScope;
if (Scope.Kind == HandleKind.AssemblyReference)
{
AssemblyReference AsmRef = MetadataReader.GetAssemblyReference((AssemblyReferenceHandle)Scope);
AssemblyNameMember = MetadataReader.GetString(AsmRef.Name);
}
}
else if (ParentHandle.Kind == HandleKind.TypeDefinition)
{
TypeDefinition TypeDefMember = MetadataReader.GetTypeDefinition((TypeDefinitionHandle)ParentHandle);
DeclaringType = MetadataReader.GetString(TypeDefMember.Namespace) + "." + MetadataReader.GetString(TypeDefMember.Name);
AssemblyDefinition AsmDefMember = MetadataReader.GetAssemblyDefinition();
AssemblyNameMember = MetadataReader.GetString(AsmDefMember.Name);
}
else
{
DeclaringType = ParentHandle.ToString();
}
// Read method signature blob
bool IsInstance = false;
BlobReader SignatureReader = MetadataReader.GetBlobReader(MemberRef.Signature);
SignatureHeader SignatureHeader = SignatureReader.ReadSignatureHeader();
IsInstance = SignatureHeader.IsInstance;
DotNetMemberList.Add(new DotNetMember
DotNetFunctionList.Add(new DotNetFunction
{
MemberName = MemberName,
FunctionName = MethodName,
TypeName = TypeName,
RVA = RVA,
FileOffset = FileOffset,
CodeSize = CodeSize,
Flags = (ushort)MethodDef.Attributes,
Token = Token,
TypeName = TypeNameMember,
DeclaringType = DeclaringType,
AssemblyName = AssemblyNameMember,
IsInstance = IsInstance
ParameterCount = ParameterCount,
LocalsCount = LocalsCount,
ImplFlags = (MethodImplementations)MethodDef.ImplAttributes,
ILCode = ILCode.ToArray(),
IsInstance = IsInstance,
DeclaringType = TypeNamespace + "." + TypeName,
AssemblyName = AssemblyName
});
}
}
foreach (PropertyDefinitionHandle PropertyHandle in TypeDef.GetProperties())
{
PropertyDefinition PropertyDef = MetadataReader.GetPropertyDefinition(PropertyHandle);
int Token = MetadataTokens.GetToken(PropertyHandle);
DotNetPropertyList.Add(new DotNetProperty
{
PropertyName = MetadataReader.GetString(PropertyDef.Name),
Token = Token
});
}
foreach (FieldDefinitionHandle FieldHandle in TypeDef.GetFields())
{
FieldDefinition FieldDef = MetadataReader.GetFieldDefinition(FieldHandle);
int Token = MetadataTokens.GetToken(FieldHandle);
DotNetFieldList.Add(new DotNetField()
{
FieldName = MetadataReader.GetString(FieldDef.Name),
Token = Token
});
}
DotNetTypeList.Add(new DotNetType
{
TypeName = TypeName,
Token = MetadataReader.GetToken(TypeHandle)
});
}
foreach (MemberReferenceHandle MemberHandle in MetadataReader.MemberReferences)
{
MemberReference MemberRef = MetadataReader.GetMemberReference(MemberHandle);
string MemberName = MetadataReader.GetString(MemberRef.Name);
int Token = MetadataTokens.GetToken(MemberHandle);
string TypeNameMember = string.Empty;
string DeclaringType = string.Empty;
string AssemblyNameMember = string.Empty;
EntityHandle ParentHandle = MemberRef.Parent;
if (ParentHandle.Kind == HandleKind.TypeReference)
{
TypeReference TypeRef = MetadataReader.GetTypeReference((TypeReferenceHandle)ParentHandle);
TypeNameMember = MetadataReader.GetString(TypeRef.Name);
DeclaringType = MetadataReader.GetString(TypeRef.Namespace) + "." + TypeNameMember;
EntityHandle Scope = TypeRef.ResolutionScope;
if (Scope.Kind == HandleKind.AssemblyReference)
{
AssemblyReference AsmRef = MetadataReader.GetAssemblyReference((AssemblyReferenceHandle)Scope);
AssemblyNameMember = MetadataReader.GetString(AsmRef.Name);
}
}
else if (ParentHandle.Kind == HandleKind.TypeDefinition)
{
TypeDefinition TypeDefMember = MetadataReader.GetTypeDefinition((TypeDefinitionHandle)ParentHandle);
DeclaringType = MetadataReader.GetString(TypeDefMember.Namespace) + "." + MetadataReader.GetString(TypeDefMember.Name);
AssemblyDefinition AsmDefMember = MetadataReader.GetAssemblyDefinition();
AssemblyNameMember = MetadataReader.GetString(AsmDefMember.Name);
}
else
{
DeclaringType = ParentHandle.ToString();
}
// Read method signature blob
bool IsInstance = false;
BlobReader SignatureReader = MetadataReader.GetBlobReader(MemberRef.Signature);
SignatureHeader SignatureHeader = SignatureReader.ReadSignatureHeader();
IsInstance = SignatureHeader.IsInstance;
DotNetMemberList.Add(new DotNetMember
{
MemberName = MemberName,
Token = Token,
TypeName = TypeNameMember,
DeclaringType = DeclaringType,
AssemblyName = AssemblyNameMember,
IsInstance = IsInstance
});
}
}
catch (Exception ex)
+13 -4
View File
@@ -362,8 +362,8 @@ namespace Brovan.Core.Emulation
public bool IsArmGuest => UnicornArch == Arch.ARM;
public bool IsX64Guest => UnicornArch == Arch.X86 && UnicornMode == Mode.MODE_64;
public bool IsArchX86Guest => UnicornArch == Arch.X86;
public ulong BaseAddress = 0x10000000UL; // Base Start
public ulong MaxAddress = 0x7FFFFFFFFUL; // Max address limit
public readonly ulong BaseAddress = 0x10000000UL; // Base Start
public readonly ulong MaxAddress = 0x7FFFFFFFFUL; // Max address limit
private ulong _timestampCounter = 0x100000000UL;
private const ulong TscCyclesPerInstruction = 3;
@@ -371,6 +371,7 @@ namespace Brovan.Core.Emulation
private const ulong RdtscReadCycles = 60;
private const ulong RdtscpReadCycles = 90;
private readonly long EmulatedSystemTimeBaseFileTimeUtc = DateTime.UtcNow.ToFileTimeUtc();
private readonly Comparison<int> _memoryRegionIndexComparer;
/// <summary>
/// Current deterministic guest tick count in milliseconds.
@@ -476,6 +477,8 @@ namespace Brovan.Core.Emulation
if (Binary.Architecture == BinaryArchitecture.Unknown)
throw new BadImageFormatException("Unsupported binary architecture.");
_memoryRegionIndexComparer = CompareMemoryRegionIndex;
_binary = Binary;
UnicornArch = Arch.X86;
UnicornMode = Binary.Architecture == BinaryArchitecture.x64 ? Mode.MODE_64 : Mode.MODE_32;
@@ -515,6 +518,7 @@ namespace Brovan.Core.Emulation
if (Data == null || Data.Length == 0)
throw new NullReferenceException(nameof(Data));
_memoryRegionIndexComparer = CompareMemoryRegionIndex;
_binary = Binary ?? new BinaryFile(Data, true);
UnicornArch = arch;
UnicornMode = mode;
@@ -1032,7 +1036,7 @@ namespace Brovan.Core.Emulation
for (int i = 0; i < _memory.Count; i++)
MemoryRegionIndex.Add(i);
MemoryRegionIndex.Sort(CompareMemoryRegionIndex);
MemoryRegionIndex.Sort(_memoryRegionIndexComparer);
MemoryRegionIndexDirty = false;
}
@@ -1705,7 +1709,12 @@ namespace Brovan.Core.Emulation
/// </summary>
public List<EmulatedThread> GetThreadsSnapshot()
{
return Threads.Values.OrderBy(Thread => Thread.ThreadId).ToList();
List<EmulatedThread> Snapshot = new List<EmulatedThread>(Threads.Count);
foreach (var Thread in Threads.Values)
Snapshot.Add(Thread);
Snapshot.Sort((a, b) => a.ThreadId.CompareTo(b.ThreadId));
return Snapshot;
}
/// <summary>
+59 -47
View File
@@ -35,6 +35,7 @@ namespace Brovan.Core.Emulation.Guests
private readonly BlobData _blob;
private readonly WindowsBlobLaunchMode _blobLaunchMode;
private IReadOnlyDictionary<uint, IWinSyscall> WinSyscallTable = new Dictionary<uint, IWinSyscall>();
private WinModule _ntdllModule;
private bool UsesDirectBlobStartup => IsBlob && _blobLaunchMode == WindowsBlobLaunchMode.Direct;
@@ -458,11 +459,6 @@ namespace Brovan.Core.Emulation.Guests
WinHelper?.InvokeException(ThreadState.ExceptionInformation.Status, ThreadState.ExceptionInformation);
// The exception is no longer pending once KiUserExceptionDispatcher has been entered.
// Keep IsHandlingException set until NtContinue/RtlRestoreContext completes the
// continuation, but do not leave DispatchException set while guest ntdll is
// dispatching handlers. NtContinue may be called from that path and must be
// treated as a local context switch, not as a request to terminate the process.
ThreadState.DispatchException = false;
State = Instance._emulator.Emulate(Instance.ReadRegister(Instance.IPRegister), 0, 0, QuantumInstructions);
@@ -539,11 +535,19 @@ namespace Brovan.Core.Emulation.Guests
if (Rule != null)
{
int MaxArgs = Rule.ArgsCount;
ulong[] Args = MaxArgs <= 0
? Array.Empty<ulong>()
: (CaptureSyscallHistory && MaxArgs <= HistoryArgs.Length
? HistoryArgs.Take(MaxArgs).ToArray()
: ReadWindowsSyscallArguments(Instance, MaxArgs));
ulong[] Args = Array.Empty<ulong>();
if (MaxArgs > 0)
{
if (CaptureSyscallHistory && MaxArgs <= HistoryArgs.Length)
{
Args = new ulong[MaxArgs];
Array.Copy(HistoryArgs, Args, MaxArgs);
}
else
{
Args = ReadWindowsSyscallArguments(Instance, MaxArgs);
}
}
SyscallContext Ctx = Instance.Syscalls.HandleSyscall(Syscall, HandlerName, Args);
if (Ctx.Handled)
@@ -711,11 +715,11 @@ namespace Brovan.Core.Emulation.Guests
uint ThreadId = (uint)Instance.CurrentThreadId;
if (!Instance.Threads.TryGetValue(ThreadId, out EmulatedThread Thread) || Thread == null)
return;
if (WinEmulatedThread.GetState(Thread).IsHandlingException)
WindowsThreadState State = WinEmulatedThread.GetState(Thread);
if (State.IsHandlingException)
{
WinEmulatedThread.GetState(Thread).ExceptionNesting++;
if (WinEmulatedThread.GetState(Thread).ExceptionNesting > 2)
State.ExceptionNesting++;
if (State.ExceptionNesting > 2)
{
Instance.WinHelper.AbandonMutexesOwnedByThread(Thread.ThreadId);
Thread.State = EmulatedThreadState.Terminated;
@@ -727,15 +731,15 @@ namespace Brovan.Core.Emulation.Guests
}
else
{
WinEmulatedThread.GetState(Thread).IsHandlingException = true;
WinEmulatedThread.GetState(Thread).ExceptionNesting = 1;
State.IsHandlingException = true;
State.ExceptionNesting = 1;
}
ExceptionInformation ExceptionInfo = Info ?? new ExceptionInformation();
ExceptionInfo.Status = Status;
Thread.State = EmulatedThreadState.Exception;
WinEmulatedThread.GetState(Thread).DispatchException = true;
WinEmulatedThread.GetState(Thread).ExceptionInformation = ExceptionInfo;
State.DispatchException = true;
State.ExceptionInformation = ExceptionInfo;
Instance.Threads[ThreadId] = Thread;
Instance._emulator.StopEmulation();
}
@@ -747,9 +751,9 @@ namespace Brovan.Core.Emulation.Guests
return;
if (Instance._binary.Architecture == BinaryArchitecture.x64)
Instance._emulator.WriteMemory(Teb + 0x68, BitConverter.GetBytes(LastError));
Instance._emulator.WriteMemory(Teb + 0x68, LastError);
else
Instance._emulator.WriteMemory(Teb + 0x34, BitConverter.GetBytes(LastError));
Instance._emulator.WriteMemory(Teb + 0x34, LastError);
}
public void SetLastWinErrorRegister(BinaryEmulator Instance, NTSTATUS Status)
@@ -782,8 +786,6 @@ namespace Brovan.Core.Emulation.Guests
return Teb;
}
byte[] PebPtr = BitConverter.GetBytes(PEB);
byte[] TebPtr = BitConverter.GetBytes(Teb);
Instance._emulator.WriteMemoryByte(Teb, 0, 0x2000);
Instance._emulator.WriteMemory(Teb, ulong.MaxValue, 8);
Instance._emulator.WriteMemory(Teb + 0x8, Thread.StackAddress + Thread.StackSize);
@@ -791,10 +793,10 @@ namespace Brovan.Core.Emulation.Guests
Instance._emulator.WriteMemory(Teb + 0x18, 0UL, 8);
Instance._emulator.WriteMemory(Teb + 0x20, 0UL, 8);
Instance._emulator.WriteMemory(Teb + 0x28, 0UL, 8);
Instance._emulator.WriteMemory(Teb + 0x30, TebPtr, 8);
Instance._emulator.WriteMemory(Teb + 0x40, BitConverter.GetBytes(WinHelper.PID), 8);
Instance._emulator.WriteMemory(Teb + 0x48, BitConverter.GetBytes(Thread.ThreadId));
Instance._emulator.WriteMemory(Teb + 0x60, PebPtr);
Instance._emulator.WriteMemory(Teb + 0x30, Teb, 8);
Instance._emulator.WriteMemory(Teb + 0x40, WinHelper.PID, 8);
Instance._emulator.WriteMemory(Teb + 0x48, Thread.ThreadId);
Instance._emulator.WriteMemory(Teb + 0x60, PEB, 8);
Instance._emulator.WriteMemory(Teb + 0x68, (uint)0u);
Instance._emulator.WriteMemory(Teb + 0x108, (uint)0x0409u);
Instance._emulator.WriteMemory(Teb + 0x1760, (uint)0u);
@@ -819,11 +821,10 @@ namespace Brovan.Core.Emulation.Guests
if (LdrInitializeThunk != 0)
return;
WinModule Module = WinHelper.WinModules.FirstOrDefault(Mod => Mod.Name == "ntdll.dll");
if (Module == null)
if (_ntdllModule == null)
throw new Exception("ntdll.dll is not loaded.");
LdrInitializeThunk = Instance.TranslateVirtualAddress(Module.ExportsByName["LdrInitializeThunk"], "ntdll.dll");
LdrInitializeThunk = Instance.TranslateVirtualAddress(_ntdllModule.ExportsByName["LdrInitializeThunk"], "ntdll.dll");
}
public void ResolveRtlUserThreadStart(BinaryEmulator Instance)
@@ -831,11 +832,10 @@ namespace Brovan.Core.Emulation.Guests
if (RtlUserThreadStart != 0)
return;
WinModule Module = WinHelper.WinModules.FirstOrDefault(Mod => Mod.Name == "ntdll.dll");
if (Module == null)
if (_ntdllModule == null)
throw new Exception("ntdll.dll is not loaded.");
RtlUserThreadStart = Instance.TranslateVirtualAddress(Module.ExportsByName["RtlUserThreadStart"], "ntdll.dll");
RtlUserThreadStart = Instance.TranslateVirtualAddress(_ntdllModule.ExportsByName["RtlUserThreadStart"], "ntdll.dll");
}
/// <summary>
@@ -871,10 +871,10 @@ namespace Brovan.Core.Emulation.Guests
GuestState = new WindowsThreadState()
};
WinEmulatedThread.GetState(Thread);
WindowsThreadState State = WinEmulatedThread.GetState(Thread);
Thread.Name ??= $"Thread_{Thread.ThreadId}";
WinEmulatedThread.GetState(Thread).ImpersonationToken = new WinToken
State.ImpersonationToken = new WinToken
{
IsElevated = false,
IsRestricted = false,
@@ -884,7 +884,7 @@ namespace Brovan.Core.Emulation.Guests
SessionId = 1
};
WinEmulatedThread.GetState(Thread).Teb = AllocateAndInitializeTEB(Instance, Thread, CreateFlags, InitialThread);
State.Teb = AllocateAndInitializeTEB(Instance, Thread, CreateFlags, InitialThread);
ulong InitialStack = (Thread.StackAddress + Thread.StackSize) & ~0xFUL;
if (Instance._binary.Architecture == BinaryArchitecture.x64)
@@ -943,10 +943,10 @@ namespace Brovan.Core.Emulation.Guests
GuestState = new WindowsThreadState()
};
WinEmulatedThread.GetState(Thread);
WindowsThreadState State = WinEmulatedThread.GetState(Thread);
Thread.Name ??= $"Thread_{Thread.ThreadId}";
WinEmulatedThread.GetState(Thread).ImpersonationToken = new WinToken
State.ImpersonationToken = new WinToken
{
IsElevated = false,
IsRestricted = false,
@@ -956,7 +956,7 @@ namespace Brovan.Core.Emulation.Guests
SessionId = 1
};
WinEmulatedThread.GetState(Thread).Teb = AllocateAndInitializeTEB(Instance, Thread, CreateFlags, InitialThread);
State.Teb = AllocateAndInitializeTEB(Instance, Thread, CreateFlags, InitialThread);
ulong InitialRSP = (Thread.StackAddress + Thread.StackSize) & ~0xFUL;
InitialRSP -= 8;
@@ -980,7 +980,7 @@ namespace Brovan.Core.Emulation.Guests
if (Instance._binary.Architecture == BinaryArchitecture.x64)
{
Thread.Context.RCX = contextAddress;
Thread.Context.RDX = WinHelper.WinModules.FirstOrDefault(Mod => Mod.Name == "ntdll.dll")?.MappedBase ?? 0;
Thread.Context.RDX = _ntdllModule?.MappedBase ?? 0;
}
Instance.Threads[Thread.ThreadId] = Thread;
@@ -1049,8 +1049,14 @@ namespace Brovan.Core.Emulation.Guests
{ "PATH", @"C:\WINDOWS\system32;C:\WINDOWS;C:\WINDOWS\System32\Wbem;C:\WINDOWS\System32\WindowsPowerShell\v1.0\;C:\WINDOWS\System32\OpenSSH\" }
};
string EnvironmentString = string.Join('\0', Env.Select(kv => $"{kv.Key}={kv.Value}")) + "\0\0";
byte[] EnvBytes = Encoding.Unicode.GetBytes(EnvironmentString);
StringBuilder Builder = new StringBuilder(1024);
foreach (var kv in Env)
{
Builder.Append(kv.Key).Append('=').Append(kv.Value).Append('\0');
}
Builder.Append('\0');
byte[] EnvBytes = Encoding.Unicode.GetBytes(Builder.ToString());
size = (ulong)EnvBytes.Length;
return EnvBytes;
}
@@ -1193,9 +1199,9 @@ namespace Brovan.Core.Emulation.Guests
ulong Handle = WinHelper.ConsoleHandle.Handle;
Instance._emulator.WriteMemory(ProcessParams + 0x10, Handle, 8);
Instance._emulator.WriteMemory(ProcessParams + 0x18, new byte[] { 0x00, 0x00, 0x00, 0x00 });
Instance._emulator.WriteMemory(ProcessParams + 0x20, BitConverter.GetBytes(WinHelper.STD_IN.Handle), 8);
Instance._emulator.WriteMemory(ProcessParams + 0x28, BitConverter.GetBytes(WinHelper.STD_OUT.Handle), 8);
Instance._emulator.WriteMemory(ProcessParams + 0x30, BitConverter.GetBytes(WinHelper.STD_OUT.Handle), 8);
Instance._emulator.WriteMemory(ProcessParams + 0x20, WinHelper.STD_IN.Handle, 8);
Instance._emulator.WriteMemory(ProcessParams + 0x28, WinHelper.STD_OUT.Handle, 8);
Instance._emulator.WriteMemory(ProcessParams + 0x30, WinHelper.STD_OUT.Handle, 8);
}
WriteInlineUnicodeString(0x60, ImagePath);
WriteInlineUnicodeString(0x70, CommandLine);
@@ -1268,8 +1274,11 @@ namespace Brovan.Core.Emulation.Guests
Instance._emulator.WriteMemory(LdrData + 0x4, (byte)1, 1);
List<ulong> Entries = new List<ulong>();
foreach (WinModule Module in WinHelper.WinModules.Where(Module => Module != null && Module.MappedBase != 0 && Module.SizeOfImage != 0))
foreach (WinModule Module in WinHelper.WinModules)
{
if (Module == null || Module.MappedBase == 0 || Module.SizeOfImage == 0)
continue;
ulong Entry = Instance.MapUniqueAddress(EntrySize, MemoryProtection.ReadWrite);
if (Entry == 0)
continue;
@@ -1303,8 +1312,11 @@ namespace Brovan.Core.Emulation.Guests
Instance._emulator.WriteMemory(LdrData + 0x4, (byte)1, 1);
List<ulong> Entries = new List<ulong>();
foreach (WinModule Module in WinHelper.WinModules.Where(Module => Module != null && Module.MappedBase != 0 && Module.SizeOfImage != 0))
foreach (WinModule Module in WinHelper.WinModules)
{
if (Module == null || Module.MappedBase == 0 || Module.SizeOfImage == 0)
continue;
ulong Entry = Instance.MapUniqueAddress(EntrySize, MemoryProtection.ReadWrite);
if (Entry == 0)
continue;
@@ -1433,7 +1445,7 @@ namespace Brovan.Core.Emulation.Guests
using (BinaryFile Library = new BinaryFile(NtdllPath, true))
{
Instance.LoadWinLibrary(Library, true, MapBySections: false);
_ntdllModule = Instance.LoadWinLibrary(Library, true, MapBySections: false);
}
}
catch (Exception ex)
@@ -95,6 +95,7 @@ namespace Brovan.Core.Emulation.OS.Windows
if (WinEmulatedThread.GetState(CurrentThread).ExceptionNesting > 2)
{
Instance.WinHelper.AbandonMutexesOwnedByThread(CurrentThread.ThreadId);
Instance.WinHelper.ClearTerminationState(CurrentThread);
CurrentThread.State = EmulatedThreadState.Terminated;
CurrentThread.ExitCode = unchecked((int)ExceptionCode);
Instance.Threads[(uint)Instance.CurrentThreadId] = CurrentThread;
@@ -59,13 +59,9 @@ namespace Brovan.Core.Emulation.OS.Windows
continue;
Instance.WinHelper.AbandonMutexesOwnedByThread(ProcessThread.ThreadId);
Instance.WinHelper.ClearTerminationState(ProcessThread);
ProcessThread.ExitCode = unchecked((int)ExitStatus);
ProcessThread.State = EmulatedThreadState.Terminated;
ProcessThread.WaitActive = false;
ProcessThread.WaitHandles = null;
ProcessThread.WaitDeadline = -1;
ProcessThread.WaitTimedOut = false;
ProcessThread.WaitSatisfiedIndex = -1;
}
Instance.StopEmulation();
@@ -23,6 +23,7 @@ namespace Brovan.Core.Emulation.OS.Windows
{
if (EmuThread.ThreadId != CurrentTID)
{
Instance.WinHelper.ClearTerminationState(EmuThread);
EmuThread.State = EmulatedThreadState.Terminated;
EmuThread.ExitCode = (int)ExitCode;
}
@@ -39,6 +40,7 @@ namespace Brovan.Core.Emulation.OS.Windows
continue;
Instance.WinHelper.AbandonMutexesOwnedByThread(ProcessThreads.ThreadId);
Instance.WinHelper.ClearTerminationState(ProcessThreads);
ProcessThreads.State = EmulatedThreadState.Terminated;
}
Instance.StopEmulation();
@@ -28,25 +28,8 @@ namespace Brovan.Core.Emulation.OS.Windows
Instance.WinHelper.AbandonMutexesOwnedByThread(TargetThread.ThreadId);
TargetThread.ExitCode = unchecked((int)(uint)ExitStatus);
Instance.WinHelper.ClearTerminationState(TargetThread);
TargetThread.State = EmulatedThreadState.Terminated;
TargetThread.WaitActive = false;
TargetThread.WaitHandles = null;
TargetThread.WaitDeadline = -1;
TargetThread.WaitTimedOut = false;
TargetThread.WaitSatisfiedIndex = -1;
WindowsThreadState State = WinEmulatedThread.TryGetState(TargetThread);
if (State != null)
{
State.ApcAlertable = false;
State.WaitAlertable = false;
State.WaitCompleted = false;
State.WaitStatus = NTSTATUS.STATUS_SUCCESS;
State.WaitResumeRIP = 0;
State.WaitReturnRIP = 0;
State.WorkerFactoryWaitActive = false;
State.AlertByThreadIdWaitActive = false;
}
if (Instance.CurrentThread != null && TargetThread.ThreadId == (uint)Instance.CurrentThreadId)
{
@@ -1,5 +1,3 @@
using System;
using System.Collections.Generic;
using static Brovan.Core.Helpers.BinaryHelpers;
namespace Brovan.Core.Emulation.OS.Windows
@@ -5,6 +5,7 @@ using Brovan.Core.Helpers;
using System.Text;
using System.Buffers.Binary;
using Brovan.Core.Emulation.OS.SharedHelpers;
using System.IO;
namespace Brovan.Core.Emulation.OS.Windows
{
@@ -205,6 +206,38 @@ namespace Brovan.Core.Emulation.OS.Windows
}
}
/// <summary>
/// Clears wait, worker-factory, and exception state for a thread that is exiting.
/// </summary>
public void ClearTerminationState(EmulatedThread Thread, bool ClearAlertByThreadId = false)
{
if (Thread == null)
return;
ClearWaitState(Thread, true);
WindowsThreadState State = WinEmulatedThread.TryGetState(Thread);
if (State == null)
return;
State.WaitCompleted = false;
State.WaitStatus = NTSTATUS.STATUS_SUCCESS;
State.WaitObjects = null;
State.DispatchException = false;
State.IsHandlingException = false;
State.ExceptionNesting = 0;
State.ExceptionInformation = null;
State.WorkerFactoryWaitActive = false;
State.WorkerFactoryHandle = 0;
State.WorkerFactoryMiniPackets = 0;
State.WorkerFactoryPacketsReturned = 0;
State.WorkerFactoryMaxPackets = 0;
State.WorkerFactoryReservedEntries?.Clear();
State.AlertByThreadIdPending = false;
State.AlertByThreadIdWaitActive = false;
State.AlertByThreadIdAddress = 0;
}
/// <summary>
/// Writes a 64-bit IO_STATUS_BLOCK to emulated memory.
/// </summary>
@@ -1006,6 +1039,7 @@ namespace Brovan.Core.Emulation.OS.Windows
private ulong UserSharedDelta;
public ulong ActiveWindow;
public ulong FocusWindow;
private readonly Dictionary<string, string> _normalizedPathCache = new(StringComparer.OrdinalIgnoreCase);
/// <summary>
/// Initialize the environment for the helper (Processes, Console, etc).
@@ -2113,17 +2147,6 @@ namespace Brovan.Core.Emulation.OS.Windows
return 0;
}
public byte[] StructureToBytes<T>(T str)
{
int size = Marshal.SizeOf<T>();
byte[] arr = new byte[size];
IntPtr ptr = Marshal.AllocHGlobal(size);
Marshal.StructureToPtr(str, ptr, false);
Marshal.Copy(ptr, arr, 0, size);
Marshal.FreeHGlobal(ptr);
return arr;
}
/// <summary>
/// Convert windows memory protection to the internal enum.
/// </summary>
@@ -3375,8 +3398,13 @@ namespace Brovan.Core.Emulation.OS.Windows
if (string.IsNullOrEmpty(NtPath))
return null;
if (_normalizedPathCache.TryGetValue(NtPath, out string Cached))
return Cached;
NtPath = FixupNtRegistryPath(NtPath);
return NormalizeKeyPath(NtPath);
NtPath = NormalizeKeyPath(NtPath);
_normalizedPathCache[NtPath] = NtPath;
return NtPath;
}
private bool IsVirtualRegistryRoot(string NtPath)
@@ -3389,7 +3417,7 @@ namespace Brovan.Core.Emulation.OS.Windows
NtPath.Equals("\\Registry\\User", StringComparison.OrdinalIgnoreCase);
}
private void AddVirtualRegistrySubKeys(string NtPath, SortedSet<string> Names)
private void AddVirtualRegistrySubKeys(string NtPath, List<string> Names)
{
if (string.IsNullOrEmpty(NtPath) || Names == null)
return;
@@ -3887,7 +3915,7 @@ namespace Brovan.Core.Emulation.OS.Windows
if (string.IsNullOrEmpty(NtPath) || IsRegistryPathDeleted(NtPath))
return false;
SortedSet<string> Names = new SortedSet<string>(StringComparer.OrdinalIgnoreCase);
List<string> Names = new List<string>();
AddVirtualRegistrySubKeys(NtPath, Names);
if (RegKey.Hive != null && RegKey.Hive.Reader != null && RegKey.HasParsedKey)
@@ -3912,7 +3940,8 @@ namespace Brovan.Core.Emulation.OS.Windows
if (Index >= Names.Count)
return false;
Name = Names.ElementAt(Index);
Names.Sort(StringComparer.OrdinalIgnoreCase);
Name = Names[Index];
return true;
}
+1 -1
View File
@@ -1113,7 +1113,7 @@ namespace Brovan.Core.Helpers
private MemoryMappedFile Mmf;
private MemoryMappedViewAccessor View;
private SafeMemoryMappedViewHandle Handle;
private byte* BasePtr;
public byte* BasePtr;
public int Length { get; private set; }
public bool IsMemoryMapped => ManagedData == null;