web-dev-qa-db-fra.com

Forcer x86 CLR sur un assemblage .NET 'Any CPU'

Dans .NET, l'option de compilation "Platform Target: Any CPU" permet à un assemblage .NET de s'exécuter en 64 bits sur une machine x64 et 32 ​​bits sur une machine x86. Il est également possible de forcer un assembly à s'exécuter en tant que x86 sur une machine x64 à l'aide de l'option de compilateur "Platform Target: x86".

Est-il possible d'exécuter un assembly avec l'indicateur 'Any CPU', mais de déterminer s'il doit être exécuté dans le CLR x86 ou x64? Normalement, cette décision est prise par le chargeur CLR/OS (comme je le comprends) en fonction du nombre de bits du système sous-jacent.

J'essaie d'écrire une application C # .NET qui peut interagir avec (lire: injecter du code dans) d'autres processus en cours d'exécution. Les processus x64 ne peuvent injecter que dans d'autres processus x64, et la même chose avec x86. Idéalement, je voudrais profiter de la compilation JIT et de l'option Any CP pour permettre à une seule application d'être utilisée pour injecter dans des processus x64 ou x86 (sur une machine x64).

L'idée est que l'application serait compilée sous la forme Any CP. Sur une machine x64, il fonctionnerait en tant que x64. Si le processus cible est x86, il devrait se relancer, forçant le CLR à l'exécuter en tant que x86. Est-ce possible?

56
jeffora

Vous pouvez découvrir comment une application s'exécutera et la modifier statiquement à l'aide de l'application CorFlags . Pour savoir comment s'exécutera l'application, utilisez:

corflags <PathToExe>

Pour modifier le mode d'exécution de l'application, utilisez:

corflags /32bit+  <PathToExe>

Cela fera exécuter le fichier EXE comme un processus 32 bits. Les informations sur le fonctionnement de l'assembly sont stockées dans l'en-tête PE. Voir la question Stack Overflow Comment savoir si un fichier natif DLL est compilé en x64 ou x86?.

Si vous souhaitez injecter du code au moment de l'exécution, vous devez écrire un profileur . NET dans C++ /COM. Voir .NET Internals: The Profiling API and Profiling (Unmanaged) Référence API) pour plus de détails.

Vous devrez implémenter le rappel JitCompilationStarted et y faire votre travail. Si vous allez dans cette direction, vous devrez créer le fichier d'injection DLL à la fois en x86 et x64. Les fichiers natifs DLL seront chargés par le CLR une fois que les variables d'environnement suivantes seront définies:

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

Si vous l'avez défini correctement, la version 64 bits "verra" les processus 64 bits et la version 32 bits "verra" les processus 32 bits.

60
Ohad Horesh

Cela fait un moment que je n'ai pas essayé, mais je pense que le témoin du processus qui appelle l'Assemblée détermine s'il sera JIT en x86 ou x64.

Donc, si vous écrivez une petite application console et la créez en tant que x86, et une autre en tant que x64, l'exécution de l'un ou l'autre entraînera l'exécution d'autres assemblys chargés dans le processus en 32 ou 64 bits. Bien entendu, cela suppose que vous exécutez sur une machine 64 bits.

9
jnoss

Je ne sais pas si je peux vous aider avec ça. Mais c'est mon expérience.

J'ai une application Host, A.exe (compilé en x86), et j'ai une application cliente, B.exe (compilé comme ANY CPU), depuis l'application Hôte. Et je lance B.exe de A.exe, en utilisant la classe System.Diagnostic.Process .

Le problème est maintenant que si je mets les deux sur une machine x64, alors A.exe s'exécutera en tant que x86, tandis que le B.exe s'exécutera en x64 .

Mais si A.exe appelle l'Assemblée c (c.dll, qui est compilé comme Any CPU), et B.exe appelle également c.dll, puis c.dll suivra l'application qui l'appelle. En d'autres termes, dans une machine 64 bits lorsque A.exe l'appelle, il se comportera comme x86 dll, alors que lorsque B.exe l'appelle, il se comportera comme x64.

6
Graviton

J'ai fait quelque chose de similaire en créant deux (vraiment trois) binaires. J'en ai eu un pour détecter si le processus dans lequel j'essayais d'injecter était de 32 ou 64 bits. Ce processus lancera ensuite la version 32 bits ou 64 bits de votre binaire d'injection (au lieu de se relancer comme vous l'avez mentionné).

Cela semble désordonné, mais vous pouvez facilement y parvenir au moment de la construction avec un événement post-génération qui crée une copie de votre binaire de sortie et utilise l'utilitaire CorFlags pour forcer la copie à s'exécuter en 32 bits. De cette façon, vous n'avez pas à déployer l'utilitaire CorFlags avec votre application, ce qui n'est probablement pas légal pour une raison quelconque.

Je pense que cela est assez similaire à votre idée initiale et ne nécessite vraiment pas plus de travail, sauf pour un événement de construction sur deux lignes.

6
KarlW