Получение символов из отлаженного процесса MainModule

Я начал писать отладчик на С# для отладки любого процесса в моей операционной системе. На данный момент он может обрабатывать только точки останова (HW, SW и Memory), но теперь я хотел показать код операции процесса.

Моя первая попытка была с nidsasm (NASM), но это не подходит, потому что после запуска инструкции ассемблера приложения .Net отличаются от ndisasm (протестировано с CheatEngine).

Поэтому я некоторое время искал и нашел несколько методов из dbghelp.dll, которые можно вызвать, чтобы вывести список всех загруженных модулей и символов (плюс базовый адрес). Хорошо, я пытаюсь разобрать все модули отдельно с помощью SharpDisasm.

Я использую ProcessModuleCollection modules = ProcessData.Instance.MPMR.ReadProcess.Modules;, чтобы получить все загруженные модули процесса. Это отлично работает.

Теперь я попытался загрузить символы MainModule, но на этом этапе я застрял с реализацией. Я реализовал функцию SymEnumSymbols с помощью p/Invoke и других необходимых функций, таких как SymInitialize.

Когда я вызываю его с BaseAddress, например, "User32.dll", все символы печатаются отлично, но для MainModule я не получил никаких символов.

Это скриншот из CheatEngine: Символы, полученные от 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 или чего-то не хватает.

Ответы

Ответ 1

Может ли быть так, что вы ищете System.Diagnostics.SymbolStore.ISymbolScope. Взгляните на класс SymbolAccess, вы можете использовать его для получения доступа к ISymbolScope.GetLocals(), который возвращает ISymbolVariable [] и GetChildren(), снова возвращая в это время массив ISymbolVariable []

Теперь еще один интересный набор примеров эталонного кода - расширение Debugger, которое позволяет вам "перехватывать" значения, как показано здесь here