每次触发断点时都会监视ECX寄存器

时间:2015-09-15 13:45:48

标签: c# assembly

如何在给定的ASM操作码下监控C#中的当前ECX寄存器?

说,我有一个:

FF8.exe+69DD8 - push ecx

使用任何准备好的调试器我可以在给定的操作码和监视寄存器上执行断点。

但我需要制作一个自动化软件:

  

每次抓住'推动ecx'调用并将其添加到表中,并调用它以了解它的调用频率。

     

这就像切割场景,播放不同的声音,我们需要知道声音播放的时间,一个接一个

为什么我不能使用现成的软件?

因为当我断点'推ecx',(注:ecx寄存器)然后跳过我失去整个时间。我需要做一个表格,其中包含加入此操作码的详细时间。

1 个答案:

答案 0 :(得分:0)

四年后,我回过头来回答自己的问题-是的,我们必须创建自己的调试器。 就像调用P / Invoke一样简单:

[DllImport("kernel32.dll")]
public static extern bool DebugActiveProcess(int dwProcessId);
  1. 获取进程ID(pId)并调用DebugActiveProcess:

    Process[] processes = Process.GetProcessesByName(args[0]);
    bool bAttached = pinvokes.DebugActiveProcess(processes[0].Id);
    
  2. 创建DEBUG_EVENT的新结构-它将包含有关断点的所有数据

  3. 在while循环中等待调试事件:

    WaitForDebugEvent(ref debug, 0xFFFFFFFF);
    
  4. 断点0xCC操作码将触发EXCEPTION_BREAKPOINT。现在,您具有与给定异常关联的threadId。现在,您必须通过GetThreadContext获取线程上下文-CONTEXT结构现在将包含CPU寄存器,包括 ECX 。您只需要为GetThreadContext添加p / invoke和为CONTEXT添加结构。完整的结构和示例可在此处查看:http://www.pinvoke.net/default.aspx/kernel32/GetThreadContext.html

    [DllImport("kernel32.dll", SetLastError = true)]
    static extern bool GetThreadContext(IntPtr hThread, ref CONTEXT lpContext);
    

即使在DLL_LOAD上,进程代码也会在每次调试事件时中断。要在给定地址处设置断点,您必须实际编写一个INT 3操作码。为此,您必须在R / W中打开进程并注入0xCC来处理内存。使用OpenProcess p / invoke和WriteProcessMemory p / invoke:

[DllImport("kernel32.dll", SetLastError = true)]
public static extern IntPtr OpenProcess(
     ProcessAccessFlags processAccess,
     bool bInheritHandle,
     int processId
);
public static IntPtr OpenProcess(Process proc, ProcessAccessFlags flags)
{
     return OpenProcess(flags, false, proc.Id);
}

[Flags]
public enum ProcessAccessFlags : uint
{
    All = 0x001F0FFF,
    Terminate = 0x00000001,
    CreateThread = 0x00000002,
    VirtualMemoryOperation = 0x00000008,
    VirtualMemoryRead = 0x00000010,
    VirtualMemoryWrite = 0x00000020,
    DuplicateHandle = 0x00000040,
    CreateProcess = 0x000000080,
    SetQuota = 0x00000100,
    SetInformation = 0x00000200,
    QueryInformation = 0x00000400,
    QueryLimitedInformation = 0x00001000,
    Synchronize = 0x00100000
}

[DllImport("kernel32.dll", SetLastError = true)]
  public static extern bool WriteProcessMemory(
  IntPtr hProcess,
  IntPtr lpBaseAddress,
  byte[] lpBuffer,
  Int32 nSize,
  out IntPtr lpNumberOfBytesWritten);

[DllImport("kernel32.dll", SetLastError = true)]
  public static extern bool WriteProcessMemory(
  IntPtr hProcess,
  IntPtr lpBaseAddress,
  [MarshalAs(UnmanagedType.AsAny)] object lpBuffer,
  int dwSize,
  out IntPtr lpNumberOfBytesWritten);

现在,我们拥有打开,编写操作码和在任何线程到达该位置时中断所需的一切:

IntPtr procHwnd = pinvokes.OpenProcess(processes[0], pinvokes.ProcessAccessFlags.All);
WriteProcessMemory(procHwnd, new IntPtr(0x469DD8), new byte[] { 0xCC }, 1, out IntPtr lpnum);

lpAddress应该是绝对虚拟地址-这意味着在问题中有FF8.exe+69DD8,因此我们需要为所选模块获取IMAGE_BASE,在这种情况下为“ FF8.exe”,然后将0x69DD8添加到该IMAGE_BASE 。自然,每个32位非ASLR进程都将IMAGE_BASE设置为0x400000。对于ASLR激活的流程,您必须获取流程模块并获取BaseAddress,如下所示:

           foreach(var mod in processes[0].Modules)
            {
                if ((mod as ProcessModule).ModuleName == args[3])
                {
                    image_base = (mod as ProcessModule).BaseAddress;
                    break;
                }
            }

其中args [3] ==“ FF8.exe”-对于32位上的非ASLR进程,它应该返回0x400000