mirror of
https://github.com/mirror/processhacker
synced 2026-06-08 16:03:24 +00:00
7bd28e4357
git-svn-id: svn://svn.code.sf.net/p/processhacker/code@2304 21ef857c-d57f-4fe0-8362-d861dc6d29cd
513 lines
16 KiB
C#
513 lines
16 KiB
C#
/*
|
|
* Process Hacker -
|
|
* dbghelp.dll wrapper code
|
|
*
|
|
* Copyright (C) 2009 wj32
|
|
*
|
|
* This file is part of Process Hacker.
|
|
*
|
|
* Process Hacker 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.
|
|
*
|
|
* Process Hacker 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 Process Hacker. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.IO;
|
|
using System.Runtime.InteropServices;
|
|
using System.Text;
|
|
using ProcessHacker.Common;
|
|
using ProcessHacker.Common.Objects;
|
|
using ProcessHacker.Native.Api;
|
|
using ProcessHacker.Native.Objects;
|
|
|
|
namespace ProcessHacker.Native.Symbols
|
|
{
|
|
public delegate bool SymbolEnumDelegate(SymbolInformation symbolInfo);
|
|
|
|
public sealed class SymbolProvider : IDisposable
|
|
{
|
|
private sealed class SymbolHandle : BaseObject
|
|
{
|
|
private ProcessHandle _processHandle;
|
|
private IntPtr _handle;
|
|
|
|
public static implicit operator IntPtr(SymbolHandle symbolHandle)
|
|
{
|
|
return symbolHandle.Handle;
|
|
}
|
|
|
|
public SymbolHandle()
|
|
{
|
|
_handle = new IntPtr(_idGen.Pop());
|
|
|
|
using (Win32.DbgHelpLock.AcquireContext())
|
|
{
|
|
if (!Win32.SymInitialize(_handle, null, false))
|
|
Win32.ThrowLastError();
|
|
}
|
|
}
|
|
|
|
public SymbolHandle(ProcessHandle processHandle)
|
|
{
|
|
_processHandle = processHandle;
|
|
_handle = processHandle;
|
|
|
|
using (Win32.DbgHelpLock.AcquireContext())
|
|
{
|
|
if (!Win32.SymInitialize(_handle, null, false))
|
|
Win32.ThrowLastError();
|
|
}
|
|
|
|
_processHandle.Reference();
|
|
}
|
|
|
|
protected override void DisposeObject(bool disposing)
|
|
{
|
|
Win32.DbgHelpLock.Acquire();
|
|
|
|
try
|
|
{
|
|
Win32.SymCleanup(_handle);
|
|
|
|
// If we didn't use a process handle, we got it from the ID generator.
|
|
if (_processHandle == null)
|
|
_idGen.Push(_handle.ToInt32());
|
|
// Otherwise, dereference the process handle.
|
|
else
|
|
_processHandle.Dereference(disposing);
|
|
}
|
|
finally
|
|
{
|
|
Win32.DbgHelpLock.Release();
|
|
}
|
|
}
|
|
|
|
public IntPtr Handle
|
|
{
|
|
get { return _handle; }
|
|
}
|
|
}
|
|
|
|
private const int _maxNameLen = 0x100;
|
|
private static IdGenerator _idGen = new IdGenerator();
|
|
|
|
public static SymbolOptions Options
|
|
{
|
|
get
|
|
{
|
|
using (Win32.DbgHelpLock.AcquireContext())
|
|
return Win32.SymGetOptions();
|
|
}
|
|
|
|
set
|
|
{
|
|
using (Win32.DbgHelpLock.AcquireContext())
|
|
Win32.SymSetOptions(value);
|
|
}
|
|
}
|
|
|
|
private SymbolHandle _handle;
|
|
private List<KeyValuePair<ulong, string>> _modules = new List<KeyValuePair<ulong, string>>();
|
|
|
|
public SymbolProvider()
|
|
{
|
|
_handle = new SymbolHandle();
|
|
}
|
|
|
|
public SymbolProvider(ProcessHandle processHandle)
|
|
{
|
|
_handle = new SymbolHandle(processHandle);
|
|
}
|
|
|
|
public void Dispose()
|
|
{
|
|
_handle.Dispose();
|
|
}
|
|
|
|
public bool Busy
|
|
{
|
|
get
|
|
{
|
|
if (!Win32.DbgHelpLock.TryAcquire())
|
|
{
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
Win32.DbgHelpLock.Release();
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
public IntPtr Handle
|
|
{
|
|
get { return _handle; }
|
|
}
|
|
|
|
public bool PreloadModules { get; set; }
|
|
|
|
public string SearchPath
|
|
{
|
|
get
|
|
{
|
|
StringBuilder data = new StringBuilder(0x1000);
|
|
|
|
using (Win32.DbgHelpLock.AcquireContext())
|
|
{
|
|
if (!Win32.SymGetSearchPath(_handle, data, data.Capacity))
|
|
return "";
|
|
}
|
|
|
|
return data.ToString();
|
|
}
|
|
|
|
set
|
|
{
|
|
using (Win32.DbgHelpLock.AcquireContext())
|
|
Win32.SymSetSearchPath(_handle, value);
|
|
}
|
|
}
|
|
|
|
public void EnumSymbols(ulong moduleBase, SymbolEnumDelegate enumDelegate)
|
|
{
|
|
this.EnumSymbols(moduleBase, null, enumDelegate);
|
|
}
|
|
|
|
public void EnumSymbols(string mask, SymbolEnumDelegate enumDelegate)
|
|
{
|
|
this.EnumSymbols(0, mask, enumDelegate);
|
|
}
|
|
|
|
public void EnumSymbols(ulong moduleBase, string mask, SymbolEnumDelegate enumDelegate)
|
|
{
|
|
using (Win32.DbgHelpLock.AcquireContext())
|
|
{
|
|
if (!Win32.SymEnumSymbols(
|
|
_handle,
|
|
moduleBase,
|
|
mask,
|
|
(symbolInfo, symbolSize, userContext) =>
|
|
enumDelegate(new SymbolInformation(symbolInfo, symbolSize)),
|
|
IntPtr.Zero
|
|
))
|
|
Win32.ThrowLastError();
|
|
}
|
|
}
|
|
|
|
public string GetLineFromAddress(ulong address)
|
|
{
|
|
string fileName;
|
|
int lineNumber;
|
|
|
|
this.GetLineFromAddress(address, out fileName, out lineNumber);
|
|
|
|
if (fileName != null)
|
|
return fileName + ": line " + lineNumber.ToString();
|
|
else
|
|
return null;
|
|
}
|
|
|
|
public void GetLineFromAddress(ulong address, out string fileName, out int lineNumber)
|
|
{
|
|
int displacement;
|
|
|
|
this.GetLineFromAddress(address, out fileName, out lineNumber, out displacement);
|
|
}
|
|
|
|
public void GetLineFromAddress(ulong address, out string fileName, out int lineNumber, out int lineDisplacement)
|
|
{
|
|
ImagehlpLine64 line;
|
|
int displacement;
|
|
|
|
using (Win32.DbgHelpLock.AcquireContext())
|
|
{
|
|
if (!Win32.SymGetLineFromAddr64(_handle, address, out displacement, out line))
|
|
Win32.ThrowLastError();
|
|
|
|
fileName = line.FileName;
|
|
lineNumber = line.LineNumber;
|
|
lineDisplacement = displacement;
|
|
}
|
|
}
|
|
|
|
public string GetModuleFromAddress(IntPtr address, out IntPtr baseAddress)
|
|
{
|
|
ulong baseAddressULong;
|
|
string fileName = this.GetModuleFromAddress(address.ToUInt64(), out baseAddressULong);
|
|
|
|
baseAddress = baseAddressULong.ToIntPtr();
|
|
|
|
return fileName;
|
|
}
|
|
|
|
public string GetModuleFromAddress(ulong address, out ulong baseAddress)
|
|
{
|
|
lock (_modules)
|
|
{
|
|
foreach (var kvp in _modules)
|
|
{
|
|
if (address >= kvp.Key)
|
|
{
|
|
baseAddress = kvp.Key;
|
|
return kvp.Value;
|
|
}
|
|
}
|
|
}
|
|
|
|
baseAddress = 0;
|
|
|
|
return null;
|
|
}
|
|
|
|
public string GetSymbolFromAddress(ulong address)
|
|
{
|
|
SymbolFlags flags;
|
|
|
|
return this.GetSymbolFromAddress(address, out flags);
|
|
}
|
|
|
|
public string GetSymbolFromAddress(ulong address, out SymbolResolveLevel level)
|
|
{
|
|
SymbolFlags flags;
|
|
string fileName;
|
|
|
|
return this.GetSymbolFromAddress(address, out level, out flags, out fileName);
|
|
}
|
|
|
|
public string GetSymbolFromAddress(ulong address, out SymbolFlags flags)
|
|
{
|
|
SymbolResolveLevel level;
|
|
string fileName;
|
|
|
|
return this.GetSymbolFromAddress(address, out level, out flags, out fileName);
|
|
}
|
|
|
|
public string GetSymbolFromAddress(ulong address, out string fileName)
|
|
{
|
|
SymbolResolveLevel level;
|
|
SymbolFlags flags;
|
|
|
|
return this.GetSymbolFromAddress(address, out level, out flags, out fileName);
|
|
}
|
|
|
|
public string GetSymbolFromAddress(ulong address, out SymbolResolveLevel level, out SymbolFlags flags, out string fileName)
|
|
{
|
|
string symbolName;
|
|
ulong displacement;
|
|
|
|
return this.GetSymbolFromAddress(address, out level, out flags, out fileName, out symbolName, out displacement);
|
|
}
|
|
|
|
public string GetSymbolFromAddress(ulong address, out string fileName, out ulong displacement)
|
|
{
|
|
SymbolResolveLevel level;
|
|
SymbolFlags flags;
|
|
string symbolName;
|
|
|
|
this.GetSymbolFromAddress(address, out level, out flags, out fileName, out symbolName, out displacement);
|
|
|
|
return symbolName;
|
|
}
|
|
|
|
public string GetSymbolFromAddress(ulong address, out SymbolResolveLevel level, out SymbolFlags flags, out string fileName, out string symbolName, out ulong displacement)
|
|
{
|
|
// Assume failure (and stop the compiler from complaining).
|
|
if (address == 0)
|
|
{
|
|
level = SymbolResolveLevel.Invalid;
|
|
flags = 0;
|
|
fileName = null;
|
|
}
|
|
|
|
// Allocate some memory for the symbol information.
|
|
using (var data = new MemoryAlloc(Marshal.SizeOf(typeof(SymbolInfo)) + _maxNameLen))
|
|
{
|
|
var info = new SymbolInfo();
|
|
|
|
info.SizeOfStruct = Marshal.SizeOf(info);
|
|
info.MaxNameLen = _maxNameLen - 1;
|
|
|
|
Marshal.StructureToPtr(info, data, false);
|
|
|
|
// Hack for drivers, since we don't get their module sizes.
|
|
// Preloading modules will fix this.
|
|
if (this.PreloadModules)
|
|
{
|
|
ulong b;
|
|
|
|
this.GetModuleFromAddress(address, out b);
|
|
|
|
using (Win32.DbgHelpLock.AcquireContext())
|
|
Win32.SymFromAddr(_handle, b, out displacement, data);
|
|
|
|
Marshal.StructureToPtr(info, data, false);
|
|
}
|
|
|
|
// Get the symbol name.
|
|
using (Win32.DbgHelpLock.AcquireContext())
|
|
{
|
|
if (Win32.SymFromAddr(_handle, address, out displacement, data))
|
|
{
|
|
info = data.ReadStruct<SymbolInfo>();
|
|
}
|
|
}
|
|
|
|
string modFileName;
|
|
ulong modBase;
|
|
|
|
// Get the module name.
|
|
if (info.ModBase == 0)
|
|
{
|
|
modFileName = this.GetModuleFromAddress(address, out modBase);
|
|
}
|
|
else
|
|
{
|
|
modBase = info.ModBase;
|
|
|
|
lock (_modules)
|
|
modFileName = _modules.Find(kvp => kvp.Key == info.ModBase).Value;
|
|
}
|
|
|
|
// If we don't have a module name, return an address.
|
|
if (modFileName == null)
|
|
{
|
|
level = SymbolResolveLevel.Address;
|
|
flags = 0;
|
|
fileName = null;
|
|
symbolName = null;
|
|
|
|
return Utils.FormatAddress(address);
|
|
}
|
|
|
|
FileInfo fi = null;
|
|
|
|
fileName = modFileName;
|
|
|
|
try
|
|
{
|
|
fi = new FileInfo(modFileName);
|
|
fileName = fi.FullName;
|
|
}
|
|
catch
|
|
{ }
|
|
|
|
// If we have a module name but not a symbol name,
|
|
// return a module plus an offset: module+offset.
|
|
if (info.NameLen == 0)
|
|
{
|
|
level = SymbolResolveLevel.Module;
|
|
flags = 0;
|
|
symbolName = null;
|
|
|
|
if (fi != null)
|
|
{
|
|
return fi.Name + "+0x" + (address - modBase).ToString("x");
|
|
}
|
|
else
|
|
{
|
|
var s = modFileName.Split('\\');
|
|
|
|
return s[s.Length - 1] + "+0x" + (address - modBase).ToString("x");
|
|
}
|
|
}
|
|
|
|
// If we have everything, return the full symbol name: module!symbol+offset.
|
|
string name = Marshal.PtrToStringAnsi(data.Memory.Increment(Win32.SymbolInfoNameOffset), info.NameLen);
|
|
|
|
level = SymbolResolveLevel.Function;
|
|
flags = info.Flags;
|
|
symbolName = name;
|
|
|
|
if (displacement == 0)
|
|
return fi.Name + "!" + name;
|
|
else
|
|
return fi.Name + "!" + name + "+0x" + displacement.ToString("x");
|
|
}
|
|
}
|
|
|
|
public SymbolInformation GetSymbolFromName(string symbolName)
|
|
{
|
|
using (var data = new MemoryAlloc(Marshal.SizeOf(typeof(SymbolInfo)) + _maxNameLen))
|
|
{
|
|
var info = new SymbolInfo();
|
|
|
|
info.SizeOfStruct = Marshal.SizeOf(info);
|
|
info.MaxNameLen = _maxNameLen - 1;
|
|
|
|
Marshal.StructureToPtr(info, data, false);
|
|
|
|
using (Win32.DbgHelpLock.AcquireContext())
|
|
{
|
|
if (!Win32.SymFromName(_handle, symbolName, data))
|
|
Win32.ThrowLastError();
|
|
}
|
|
|
|
return new SymbolInformation(data, 0);
|
|
}
|
|
}
|
|
|
|
public void LoadModule(string fileName, IntPtr baseAddress)
|
|
{
|
|
this.LoadModule(fileName, baseAddress.ToUInt64());
|
|
}
|
|
|
|
public void LoadModule(string fileName, ulong baseAddress)
|
|
{
|
|
this.LoadModule(fileName, baseAddress, 0);
|
|
}
|
|
|
|
public void LoadModule(string fileName, IntPtr baseAddress, int size)
|
|
{
|
|
this.LoadModule(fileName, baseAddress.ToUInt64(), size);
|
|
}
|
|
|
|
public void LoadModule(string fileName, ulong baseAddress, int size)
|
|
{
|
|
using (Win32.DbgHelpLock.AcquireContext())
|
|
{
|
|
if (Win32.SymLoadModule64(_handle, IntPtr.Zero, fileName, null, baseAddress, size) == 0)
|
|
Win32.ThrowLastError();
|
|
}
|
|
|
|
lock (_modules)
|
|
{
|
|
_modules.Add(new KeyValuePair<ulong, string>(baseAddress, fileName));
|
|
_modules.Sort((kvp1, kvp2) => kvp2.Key.CompareTo(kvp1.Key));
|
|
}
|
|
}
|
|
|
|
public void UnloadModule(string fileName)
|
|
{
|
|
KeyValuePair<ulong, string> pair;
|
|
|
|
lock (_modules)
|
|
pair = _modules.Find(kvp => string.Compare(kvp.Value, fileName, true) == 0);
|
|
|
|
this.UnloadModule(pair.Key);
|
|
}
|
|
|
|
public void UnloadModule(ulong baseAddress)
|
|
{
|
|
using (Win32.DbgHelpLock.AcquireContext())
|
|
{
|
|
if (!Win32.SymUnloadModule64(_handle, baseAddress))
|
|
Win32.ThrowLastError();
|
|
}
|
|
|
|
lock (_modules)
|
|
_modules.RemoveAll(kvp => kvp.Key == baseAddress);
|
|
}
|
|
}
|
|
}
|