153 lines
5.4 KiB
C#
Raw Permalink Normal View History

2021-09-20 18:20:01 +02:00
/*
Copyright (C) 2014-2019 de4dot@gmail.com
This file is part of dnSpy
dnSpy is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
dnSpy is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with dnSpy. If not, see <http://www.gnu.org/licenses/>.
*/
using System;
using System.Collections.Generic;
using System.Diagnostics;
using dnlib.DotNet;
using dnlib.PE;
using dnSpy.Contracts.Debugger;
using dnSpy.Contracts.Documents;
using dnSpy.Contracts.Metadata;
using dnSpy.Contracts.Utilities;
namespace dnSpy.Debugger.DotNet.Metadata {
sealed class MemoryModuleDefDocument : DsDotNetDocumentBase, IModuleIdHolder {
sealed class DocKey : IDsDocumentNameKey {
readonly DbgProcess process;
readonly ulong address;
public DocKey(DbgProcess process, ulong address) {
this.process = process ?? throw new ArgumentNullException(nameof(process));
this.address = address;
}
public override bool Equals(object? obj) => obj is DocKey o && process == o.process && address == o.address;
public override int GetHashCode() => process.GetHashCode() ^ address.GetHashCode();
}
public ModuleId ModuleId { get; }
public override IDsDocumentNameKey Key => CreateKey(Process, Address);
public override DsDocumentInfo? SerializedDocument => null;
public bool AutoUpdateMemory { get; }
public DbgProcess Process { get; }
public ulong Address { get; }
public override bool IsActive => Process.State != DbgProcessState.Terminated;
readonly DbgInMemoryModuleServiceImpl owner;
readonly byte[] data;
MemoryModuleDefDocument(DbgInMemoryModuleServiceImpl owner, ModuleId moduleId, DbgProcess process, ulong address, byte[] data, ModuleDef module, bool loadSyms, bool autoUpdateMemory)
: base(module, loadSyms) {
this.owner = owner ?? throw new ArgumentNullException(nameof(owner));
ModuleId = moduleId;
Process = process ?? throw new ArgumentNullException(nameof(process));
Address = address;
this.data = data ?? throw new ArgumentNullException(nameof(data));
AutoUpdateMemory = autoUpdateMemory;
}
public static IDsDocumentNameKey CreateKey(DbgProcess process, ulong address) => new DocKey(process, address);
protected override TList<IDsDocument> CreateChildren() {
var list = new TList<IDsDocument>();
if (files is not null) {
list.AddRange(files);
files = null;
}
return list;
}
List<MemoryModuleDefDocument>? files;
public void UpdateMemory() => owner.UpdateModuleMemory(this);
internal bool TryUpdateMemory() {
if (Process.State == DbgProcessState.Terminated)
return false;
byte[]? buffer = null;
try {
buffer = GetBuffer();
int pos = 0;
var addr = Address;
while (pos < data.Length) {
int len = Math.Min(data.Length - pos, buffer.Length);
Process.ReadMemory(addr, buffer, 0, len);
if (!Equals(data, pos, buffer, len)) {
Process.ReadMemory(Address, data, 0, data.Length);
return true;
}
addr += (uint)len;
pos += len;
}
return false;
}
finally {
if (buffer is not null)
ReleaseBuffer(buffer);
}
}
WeakReference? weakBuffer;
byte[] GetBuffer() => weakBuffer?.Target as byte[] ?? new byte[0x2000];
void ReleaseBuffer(byte[] buffer) => weakBuffer = new WeakReference(buffer);
static bool Equals(byte[] a, int ai, byte[] b, int len) {
for (int i = 0; i < len; i++) {
if (a[ai + i] != b[i])
return false;
}
return true;
}
public static MemoryModuleDefDocument CreateAssembly(List<MemoryModuleDefDocument> files) {
var manifest = files[0];
var file = new MemoryModuleDefDocument(manifest.owner, manifest.ModuleId, manifest.Process, manifest.Address, manifest.data, manifest.ModuleDef!, false, manifest.AutoUpdateMemory);
file.files = new List<MemoryModuleDefDocument>(files);
return file;
}
public static MemoryModuleDefDocument Create(DbgInMemoryModuleServiceImpl owner, DbgModule module, bool loadSyms) {
Debug.Assert(!module.IsDynamic);
Debug.Assert(module.HasAddress);
var data = new byte[module.Size];
module.Process.ReadMemory(module.Address, data, 0, data.Length);
var peImage = new PEImage(data, GetImageLayout(module), true);
var mod = ModuleDefMD.Load(peImage);
mod.Location = module.IsInMemory ? string.Empty : module.Filename;
bool autoUpdateMemory = false;
if (GacInfo.IsGacPath(mod.Location))
autoUpdateMemory = false; // GAC files are not likely to decrypt methods in memory
var moduleId = module.Runtime.GetDotNetRuntime().GetModuleId(module);
return new MemoryModuleDefDocument(owner, moduleId, module.Process, module.Address, data, mod, loadSyms, autoUpdateMemory);
}
static ImageLayout GetImageLayout(DbgModule module) {
Debug.Assert(!module.IsDynamic);
switch (module.ImageLayout) {
case DbgImageLayout.File: return ImageLayout.File;
case DbgImageLayout.Memory: return ImageLayout.Memory;
case DbgImageLayout.Unknown:
default:
Debug.Fail($"Unsupported image layout: {module.ImageLayout}");
return ImageLayout.File;
}
}
}
}