简体   繁体   English

使用反射在C#中查找字段的指针

[英]Finding a pointer of a field in C# using reflection

This question is for educational purposes only. 此问题仅用于教育目的。
I am aware of how a native program is working. 我知道本机程序是如何工作的。 The compiler takes each primitive and gives it an address, then uses that address in the program. 编译器获取每个原语并为其提供一个地址,然后在程序中使用该地址。 For structures, it simply stacks the address together (with some padding) - but basically, a structure doesn't really "exist". 对于结构,它只是将地址堆叠在一起(带有一些填充) - 但基本上,结构并不真正“存在”。

The native program doesn't tell me which fields and variables it has. 本机程序不会告诉我它有哪些字段和变量。 It only accesses different addresses - and if I look at the assembly, I can name each address if I want to, but the program won't give me that information. 它只访问不同的地址 - 如果我查看程序集,我可以根据需要命名每个地址,但程序不会给我这些信息。 So assuming I am looking for a specific variable, I cannot do it without either examining the executing of the program, or it's assembly. 因此,假设我正在寻找一个特定的变量,我不能在没有检查程序执行或者程序集的情况下做到这一点。

The .NET environment does tell me which variables it has and which fields it has. .NET环境确实告诉我它有哪些变量以及它有哪些字段。 Using the Assembly class and Reflection namespace, I can load up a file and see which fields and classes it has. 使用Assembly类和Reflection命名空间,我可以加载一个文件,看看它有哪些字段和类。
Then, using a program which searches memory (whether its native or not) I can find the physical location of the field (by using it value, filtering out etc) - like Cheat Engine does. 然后,使用搜索内存的程序(无论是否是本机),我可以找到该字段的物理位置(通过使用它的值,过滤掉等) - 就像Cheat Engine一样。 It will give me the actual address of the field in the memory, which is accessed by the assembly made by the JIT . 它将为我提供内存中字段的实际地址,由JIT组装的组件访问。
I know that the MSIL does not contain information about the desired location of a specific field. 我知道MSIL不包含有关特定字段的所需位置的信息。 I am also almost certain that the JIT will never optimize the code by removing any class. 我几乎可以肯定JIT永远不会通过删除任何类来优化代码。

I know that the .NET debugger is an actual class in the program which allows Visual Studio to interact with the internal information of an application. 我知道.NET调试器是程序中的一个实际类,它允许Visual Studio与应用程序的内部信息进行交互。 When the debugger is missing, Visual Studio cannot read or write to fields, nor can it inspect the application in any way. 缺少调试器时, Visual Studio无法读取或写入字段,也无法以任何方式检查应用程序。


Is there any way, without the use of Cheat Engine or similar tools to find the physical location of a field in a static (or of a specific instance) class in a running .NET process ? 有没有办法在没有使用Cheat Engine或类似工具的情况下在正在运行的.NET进程中 查找静态(或特定实例)类中字段的物理位置 Will the address be the same after each executing (such as in native program) ? 每次执行后地址是否相同 (例如在本机程序中)? May it differ only on different platforms or machines? 它可能只在不同的平台或机器上有所不同吗? How does the JIT decide where to place a field? JIT如何决定在哪里放置一块土地?

If I was unclear, I wish to do it without access to the code of the program, ie externally by another process (like a debugger, but for programs compiled under release). 如果我不清楚,我希望无法访问程序的代码,即外部由另一个进程(如调试器,但对于在发布时编译的程序)。

Next code inject Injector method in Paint.net and get MainForm fields. 下一代码在Paint.net中注入Injector方法并获取MainForm字段。

NInject.exe NInject.exe

public static int Injector(string parameter)
{
  try
  {
    var mainForm = Application.OpenForms.OfType<Form>().FirstOrDefault(form => form.GetType().FullName.EndsWith("MainForm"));

    var builder = new StringBuilder();
    builder.AppendFormat("process: {0}\r\n\r\n", Application.ExecutablePath);
    builder.AppendFormat("type: {0}\r\n", mainForm.GetType().FullName);
    foreach (var field in mainForm.GetType().GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic))
    {
      builder.AppendFormat("field {0}: {1}\r\n", field.Name, field.GetValue(mainForm));
    }


    new Form()
    {
      Controls = 
      {
        new TextBox
        {
          Text = builder.ToString(),
          Multiline = true,
          Dock = DockStyle.Fill
        }
      }
    }
    .ShowDialog();
  }
  catch (Exception exc)
  {
    MessageBox.Show(exc.ToString());
  }
  return 0;      
}

static void Main(string[] args)
{
  var process = System.Diagnostics.Process.GetProcessesByName("PaintDotNet").FirstOrDefault();

  var processHandle = OpenProcess(ProcessAccessFlags.All, false, process.Id);

  var proxyPath = System.IO.Path.Combine(System.Windows.Forms.Application.StartupPath, "NInjector.dll");

  var pathBytes = System.Text.Encoding.ASCII.GetBytes(proxyPath);

  var remoteBuffer = VirtualAllocEx(processHandle, IntPtr.Zero, (uint)pathBytes.Length, AllocationType.Commit, MemoryProtection.ReadWrite);
  WriteProcessMemory(process.Handle, remoteBuffer, pathBytes, (uint)pathBytes.Length, IntPtr.Zero);


  var remoteThread = CreateRemoteThread(processHandle, IntPtr.Zero, 0, GetProcAddress(GetModuleHandle("kernel32"), "LoadLibraryA") , remoteBuffer, 0, IntPtr.Zero);

  WaitForSingleObject(remoteThread, unchecked((uint)-1)); 

  CloseHandle(remoteThread);

}

NInjector.dll (native) NInjector.dll(本机)

#include "MSCorEE.h"
#pragma comment  (lib, "MSCorEE")

void StartTheDotNetRuntime()
{
    MessageBox(0, L"Started", L"proxy", 0);

    ICLRRuntimeHost *pClrHost = NULL;
    HRESULT hr = CorBindToRuntimeEx(
        NULL, L"wks", 0, CLSID_CLRRuntimeHost,
        IID_ICLRRuntimeHost, (PVOID*)&pClrHost);


    hr = pClrHost->Start();


    DWORD dwRet = 0;
    hr = pClrHost->ExecuteInDefaultAppDomain(
        L"bla-bla\\NInject.exe",
        L"NInject.NInject_Program", L"Injector", L"MyParameter", &dwRet);



    hr = pClrHost->Stop();

    pClrHost->Release();
}


BOOL APIENTRY DllMain( HMODULE hModule,
                       DWORD  ul_reason_for_call,
                       LPVOID lpReserved
                     )
{
    switch (ul_reason_for_call)
    {
    case DLL_PROCESS_ATTACH:
    StartTheDotNetRuntime();
    break;
    case DLL_THREAD_ATTACH:
    break;
    case DLL_THREAD_DETACH:
    case DLL_PROCESS_DETACH:


        break;
    }
    return TRUE;
}

Output: 输出:

process: C:\Program Files\Paint.NET\PaintDotNet.exe

type: PaintDotNet.Dialogs.MainForm
field appWorkspace: PaintDotNet.Controls.AppWorkspace
field defaultButton: System.Windows.Forms.Button, Text: 
field floaters: PaintDotNet.Dialogs.FloatingToolForm[]
field floaterOpacityTimer:  [System.Windows.Forms.Timer], Interval: 25
field deferredInitializationTimer: 
field components: System.ComponentModel.Container
field killAfterInit: False
field singleInstanceManager: PaintDotNet.SystemLayer.SingleInstanceManager
field queuedInstanceMessages: System.Collections.Generic.List`1[System.String]
field processingOpen: False
field scrollPosition: {X=0,Y=0}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM