ChatGPT解决这个技术问题 Extra ChatGPT

Force x86 CLR on an 'Any CPU' .NET assembly

In .NET, the 'Platform Target: Any CPU' compiler option allows a .NET assembly to run as 64 bit on a x64 machine, and 32 bit on an x86 machine. It is also possible to force an assembly to run as x86 on an x64 machine using the 'Platform Target: x86' compiler option.

Is it possible to run an assembly with the 'Any CPU' flag, but determine whether it should be run in the x86 or x64 CLR? Normally this decision is made by the CLR/OS Loader (as is my understanding) based on the bitness of the underlying system.

I am trying to write a C# .NET application that can interact with (read: inject code into) other running processes. x64 processes can only inject into other x64 processes, and the same with x86. Ideally, I would like to take advantage of JIT compilation and the Any CPU option to allow a single application to be used to inject into either x64 or x86 processes (on an x64 machine).

The idea is that the application would be compiled as Any CPU. On an x64 machine, it would run as x64. If the target process is x86, it should relaunch itself, forcing the CLR to run it as x86. Is this possible?


D
Dweeberly

You can find out how an application will run and change it statically using the CorFlags application. To find out how the application will run, use:

corflags <PathToExe>

To change how the application will run, use:

corflags /32bit+  <PathToExe>

This will make the EXE file run as a 32-bit process. The information about how the assembly should run is stored in the PE header. See Stack Overflow question How to find if a native DLL file is compiled as x64 or x86?.

If you want to inject code at run time, you have to write a .NET profiler in C++/COM. See .NET Internals: The Profiling API and Profiling (Unmanaged API Reference) for more details.

You'll need to implement the JitCompilationStarted callback and do your work there. If you go in this direction, you'll have to build the injecting DLL file both as x86 and x64. The native DLL files will be loaded by the CLR once the following environment variables will be set:

Cor_Enable_Profiling=0x1
COR_PROFILER={CLSID-of-your-native-DLL-file}

If you have it set correctly then the 64-bit version will 'see' the 64 bit processes and the 32-bit version will 'see' the 32-bit processes.


Thanks for the info :) I was aware of the corflags app, but was wondering if there was any way to achieve a similar result programmatically at runtime.
Once the process is running the is no way to change its context!
Changing the context at run time doesn't mean just setting a bit at the PE header, 32 bit process is running under WOW emulation layer. I can't see how process can save its state at runtime, do a context switch and continue running. See this link: blogs.msdn.com/oldnewthing/archive/2008/12/22/9244582.aspx
I wasn't so much wanting the process to change it's context at runtime, but rather say I have assembly a.exe compiled as AnyCPU. It runs as 64bit on x64 machine. If it needs to inject into 32bit, I want it to launch a new instance of itself (Process.Start("a.exe")) and force that new instance to start as x86
P
Peter Mortensen

It has been a while since I tried this, but I believe that the bitness of the process that calls the assembly determines whether it will be JITed as x86 or x64.

So if you write a small console application and build it as x86, and another as x64, running one or the other will cause other assemblies loaded into the process to run as 32 or 64 bit. This, of course, assumes you are running on a 64 bit machine.


Yeh I'm aware you can force it by wrapping it in an x86 launcher assembly, but I was wondering if you could dynamically force it for 'Any CPU' compiled assemblies. Thanks anyway, will probably revert to this if I can't find anything else. Would upvote but not enough rep.
Processes are either 64 bit or 32 bit. If the assembly is loaded in a 32 bit process and it is built as Any CPU it will be JITed as 32 bit, in 64 bit process it will be JITed as 64. How do you plan to create the assembly that hosts your assemblies?
G
Graviton

I'm not sure whether I can help you with this. But this is my experience.

I have a host application,A.exe ( compiled as x86), and I have a client application, B.exe ( compiled as ANY CPU), from the host application. And I launch B.exe from A.exe, using the System.Diagnostic.Process class.

The issue now is if I put the two on a x64 machine, then A.exe will run as x86, whereas the B.exe will run as x64.

But if A.exe calls assembly c ( c.dll, which is compiled as Any CPU), and B.exe also calls c.dll, then c.dll will follow the application that calls it. In other words, in 64 bit machine when A.exe calls it, it will behave like x86 dll, whereas when B.exe calls it, it will behave like x64.


P
Peter Mortensen

I've done something similar by creating two (really three) binaries. I had one detect whether the process I was trying to inject into was 32 or 64-bit. This process will then launch either the 32-bit or 64-bit version of your injection binary (as opposed to relaunching itself like you mentioned).

It sounds messy, but you can easily achieve this at build time with a post-build event that makes a copy of your output binary and uses the CorFlags utility to force the copy to run as 32-bit. This way you don't have to deploy the CorFlags utility with your application, which probably isn't legal for some reason anyway.

I think this is quite similar to your initial idea and really doesn't require more work except for a two-line build event.