Finding a field pointer in C # using reflection

This question is for educational purposes only.
I know how the native program works. The compiler takes each primitive and gives it an address, then uses that address in the program. For structures, it simply adds the address together (with some additions), but in principle, the structure does not actually exist.

The native program does not tell me which fields and variables it has. It addresses only different addresses - and if I look at the assembly, I can name each address if I want, but the program will not give me this information. Therefore, assuming I'm looking for a specific variable, I cannot do this without studying program execution or assembly.

The .NET environment tells me which variables it has and what fields it has. Using the Assembly and Reflection namespace, I can upload the file and see what fields and classes it has.
Then, using a program that searches for memory (regardless of whether it is native or not), I can find the physical location of the field (using its value, filter, etc.) - for example, the Cheat Engine . This will give me the actual address of the field in memory accessed by the assembly made by JIT .
I know that MSIL does not contain information about the desired location of a specific field. I am also pretty sure that JIT will never optimize the code by deleting any class.

I know that the .NET debugger is the actual class in the program that allows Visual Studio interact with the internal information of the application. When there is no debugger, Visual Studio cannot read or write to fields, nor can it validate the application in any way.


Is there any way without using the Cheat Engine or similar tools to find the physical location of a field in a static (or specific instance) class at the start of the .NET process ? Will the address be repeated after each execution (for example, in my own program)? Can it differ only on different platforms or machines? How does JIT decide where to place the field?

If I were unclear, I would like to do this without access to the program code, that is, external to another process (for example, a debugger, but for programs compiled under release).

+6
source share
1 answer

The following code introduces the Injector method in Paint.net and gets the MainForm fields.

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)

 #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; } 

Conclusion:

 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} 
0
source

Source: https://habr.com/ru/post/921414/


All Articles