【发布时间】:2015-11-02 15:51:08
【问题描述】:
我开始用 C# 编写调试器,以调试操作系统上的任何进程。目前,它只能处理断点(硬件、软件和内存),但现在我想显示进程的操作码。
我的第一次尝试是使用 nidsasm (NASM),但这不合适,因为启动后 a.Net 应用程序汇编指令与 ndisasm 不同(使用 CheatEngine 测试)。
所以我搜索了一段时间,从 dbghelp.dll 中找到了一些方法,可以调用这些方法来列出所有加载的模块和符号(加上基地址)。好的,我的尝试是,用 SharpDisasm 分别拆解所有模块。
我使用ProcessModuleCollection modules = ProcessData.Instance.MPMR.ReadProcess.Modules; 来获取进程的所有加载模块。这完美无缺。
现在我尝试加载 MainModule 的符号,但在这一点上,我坚持执行。我使用 p/Invoke 和其他必要的函数(如 SymInitialize)实现了 SymEnumSymbols 函数。
当我使用例如“User32.dll”的 BaseAddress 调用它时,所有符号都完美打印,但对于 MainModule,我没有得到任何符号。
这是来自 CheatEngine 的截图: Symbols gained from Cheat Engine
如您所见,有像“Form1_Load”这样的符号,我的实现中没有。
这是必要的代码示例:
if (!DebugApi.SymInitialize(ProcessData.Instance.MPMR.M_hProcess, null, false))
{
var err = Marshal.GetLastWin32Error();
//throw new Exception("GetMemoryInfo failed : GetLastError() : " + new Win32Exception(err).Message);
Console.WriteLine("GetMemoryInfo failed : GetLastError() : " + new Win32Exception(err).Message);
return;
}
if (!DebugApi.SymEnumSymbols(ProcessData.Instance.MPMR.M_hProcess, (ulong)ProcessData.Instance.MPMR.ReadProcess.MainModule.BaseAddress, "!", DebugApi.EnumSyms, IntPtr.Zero))
{
var err = Marshal.GetLastWin32Error();
//throw new Exception("GetMemoryInfo failed : GetLastError() : " + new Win32Exception(err).Message);
Console.WriteLine("GetMemoryInfo failed : GetLastError() : " + new Win32Exception(err).Message);
return;
}
DebugApi.SymCleanup(ProcessData.Instance.MPMR.M_hProcess);
还有我的 DebugApi,以及所有必要的 p/Invoke 函数。
public class DebugApi
{
[DllImport("dbghelp.dll", SetLastError = true, CharSet = CharSet.Unicode)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool SymInitialize(IntPtr hProcess, string UserSearchPath, [MarshalAs(UnmanagedType.Bool)]bool fInvadeProcess);
[DllImport("dbghelp.dll", SetLastError = true, CharSet = CharSet.Unicode)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool SymCleanup(IntPtr hProcess);
[DllImport("dbghelp.dll", SetLastError = true, CharSet = CharSet.Unicode)]
public static extern ulong SymLoadModuleEx(IntPtr hProcess, IntPtr hFile, string ImageName, string ModuleName, long BaseOfDll, int DllSize, IntPtr Data, int Flags);
[DllImport("dbghelp.dll", SetLastError = true, CharSet = CharSet.Unicode)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool SymEnumSymbols(IntPtr hProcess, ulong BaseOfDll, string Mask, PSYM_ENUMERATESYMBOLS_CALLBACK EnumSymbolsCallback, IntPtr UserContext);
public delegate bool PSYM_ENUMERATESYMBOLS_CALLBACK(ref SYMBOL_INFO pSymInfo, uint SymbolSize, IntPtr UserContext);
public static bool EnumSyms(ref SYMBOL_INFO pSymInfo, uint SymbolSize, IntPtr UserContext)
{
Console.Out.WriteLine("Name: " + pSymInfo.Name);
return true;
}
[Flags]
public enum SymFlag : uint
{
VALUEPRESENT = 0x00000001,
REGISTER = 0x00000008,
REGREL = 0x00000010,
FRAMEREL = 0x00000020,
PARAMETER = 0x00000040,
LOCAL = 0x00000080,
CONSTANT = 0x00000100,
EXPORT = 0x00000200,
FORWARDER = 0x00000400,
FUNCTION = 0x00000800,
VIRTUAL = 0x00001000,
THUNK = 0x00002000,
TLSREL = 0x00004000,
}
[Flags]
public enum SymTagEnum : uint
{
Null,
Exe,
Compiland,
CompilandDetails,
CompilandEnv,
Function,
Block,
Data,
Annotation,
Label,
PublicSymbol,
UDT,
Enum,
FunctionType,
PointerType,
ArrayType,
BaseType,
Typedef,
BaseClass,
Friend,
FunctionArgType,
FuncDebugStart,
FuncDebugEnd,
UsingNamespace,
VTableShape,
VTable,
Custom,
Thunk,
CustomType,
ManagedType,
Dimension
};
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
public struct SYMBOL_INFO
{
public uint SizeOfStruct;
public uint TypeIndex;
public ulong Reserved1;
public ulong Reserved2;
public uint Reserved3;
public uint Size;
public ulong ModBase;
public SymFlag Flags;
public ulong Value;
public ulong Address;
public uint Register;
public uint Scope;
public SymTagEnum Tag;
public int NameLen;
public int MaxNameLen;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 1024)]
public string Name;
}
}
我的函数应该没问题,因为它适用于其他模块(例如加载的 dll)。也许我不理解 .Net 可执行文件的符号概念或缺少某些东西。
【问题讨论】:
-
不,错误的调试引擎。下载 mdbg 示例以了解如何调试托管代码。
-
祝你好运 Cedric,调试的世界需要深入研究普通人不知道的各种东西。这就是为什么带有 SOS 扩展的 WINDBG 是最好的。但即便如此,该工具也需要一段时间来学习。如果你能改进,你就有了一些东西……我同意 Hans 的观点,MDBG 是开始的地方。
-
谢谢汉斯,我已经找到了 mdbg 示例,但在示例中找不到我的问题的解决方案。我使用 NativeDebugWrappers 作为调试器,调用 kernel32 函数进行调试。但是对于从 pe 可执行文件(在本例中为 .Net 可执行文件)的 MainModule 加载符号,我没有在那里找到任何东西,也许我的眼睛已经厌倦了一整天的互联网搜索。
-
@Strece 我知道它已经过时了,但仍然......你说 MainModule 的符号是什么意思?我认为可以直接从元数据中提取有关程序集类/函数的信息。当您需要将执行的 IL 代码绑定到源代码时,符号特别有用,例如在调试器中显示当前代码语句。
标签: c# debugging pinvoke debug-symbols