-
Inject .net application into native Process
Hello,
I am injecting a managed application into 1.12.1 WoW. To be sure that the process is ready to host my application I do the following:
- Use WaitForInputIdle on the process object I previously launched to wait til the process is ready
- Refresh the process object and check the main window title until it is "World of Warcraft"
In most cases this is enough however for certain users the wpf window of my application doesnt appear even tho the thread is started successfully (debug message box in bootstrap c++ dll is shown without a problem).
I have two guesses:
- The clr somehow cant be spawned
- Since wpf uses DirectX my second guess is that it is related to DirectX
Anything else shouldnt be a problem consdering before the closing of the wpf window there is no modification made to memory.
Sadly I have no way to test this as we speak since this problem isnt happening on any of my computers.
Does anyone have a reliable way to be really sure that the process is "safe to inject into"? Right now I just add a two second sleep ontop of the WaitForInputIdle aswell main window title check which works but is somehow ... ugly.
Thanks for reading
Check my blog: https://zzuks.blogspot.com
-
I'm not sure if this is relevant or not, but when I have done something similar in the past, I have had to add a check to my EndScene hook to make sure that execution is in the main thread, rather than WPF's thread.
-
Post Thanks / Like - 1 Thanks
Corthezz (1 members gave Thanks to namreeb for this useful post)
-
Originally Posted by
namreeb
I'm not sure if this is relevant or not, but when I have done something similar in the past, I have had to add a check to my EndScene hook to make sure that execution is in the main thread, rather than WPF's thread.
Already added this way back (comparing current thread id to mainthread id and returning if they differ). Its definetly not related to this however I want to try something different today:
I will try to hook EndScene in my bootstrap dll and wait for the first 5 frames to render before injecting my .net application.
Check my blog: https://zzuks.blogspot.com
-
Active Member
Hello. I don't know can i help or not, but you can check same bots IceFlake 3.3.5 or cleanLayer 3.3.5 written in C# and used DomainManager for Reload application.
-
Post Thanks / Like - 1 Thanks
Corthezz (1 members gave Thanks to zdohdds for this useful post)
-
Contributor
Is there some lean and clean solution now-days (i mean injecting and calling clr into native)?
I found this so far but I'm too stupid to make it work properly: C# "Unmanaged Exports" - Stack Overflow
As my friend zdohdds, mentioned above there are iceflake and cleanlayer (once there was VWOW too) that uses third party libs as RGiesecke DllExport, but they are buggy and crash after every update of visual studio.
Thanks.
Last edited by tutrakan; 07-09-2017 at 11:34 PM.
-
Originally Posted by
tutrakan
Is there some lean and clean solution now-days (i mean injecting and calling clr into native)?
I found this so far but I'm too stupid to make it work properly:
C# "Unmanaged Exports" - Stack Overflow
As my friend zdohdds, mentioned above there are iceflake and cleanlayer (once there was VWOW too) that uses third party libs as RGiesecke DllExport, but they are buggy and crash after every update of visual studio.
Thanks.
Use this to export your functions, then use CreateRemoteThread to invoke them from your injector.
-
Post Thanks / Like - 1 Thanks
tutrakan (1 members gave Thanks to Jadd for this useful post)
-
Contributor
I asked explicitly for an alternative to
Originally Posted by
tutrakan
third party libs as RGiesecke DllExport
because they are complicate, buggy and don't work anymore on visual studio 2017 (I installed the latest version - 1.5.2). Maybe i didn't configure it the right way, but whatever - i still hate DllExport, because this isn't the first time i have headaches using it.
As Hans Passant says: "I would recommend you do this the documented way instead of relying on a undocumented hack from an author who doesn't provide support."
Here is his suggestion I'd like to make work: C# "Unmanaged Exports" - Stack Overflow.
Thanks.
Last edited by tutrakan; 07-10-2017 at 07:38 PM.
-
I did offer you an alternative, here it is again in case you missed it: GitHub - 3F/DllExport: Unmanaged Exports ( .NET DllExport )
-
Contributor
Originally Posted by
Jadd
Thank you for trying to help.
Look at the project readme.md you posted:
Code:
Copyright (c) 2009-2015 Robert Giesecke
Copyright (c) 2016-2017 Denis Kuzmin <[email protected]>
I use DllExports since i started experimenting with Iceflake about five years ago.
But one day, it happens that i updated my visual studio to 2017 and once again, i have to struggle with DllExports for one more time (note that i updated to latest version and stuff, but still not working). So, I'm pissed off -
Then i saw that promising C++/CLI alternative, but as i said, I'm too stupid to make it work at 100%: C# "Unmanaged Exports" - Stack Overflow
There is the part where you guys are supposed to help me
Last edited by tutrakan; 07-11-2017 at 10:27 PM.
-
It is essentially a different library, albeit based on the same idea and code. There has been enough changes to consider this an alternative to Robert Giesecke's original library (see: DllExport/changelog.txt at master * 3F/DllExport * GitHub)
Try it, no one else I know has ever had issues with it. So if you have issues, it's most likely a you problem.
-
Contributor
Originally Posted by
Jadd
It is essentially a different library, albeit based on the same idea and code. There has been enough changes to consider this an alternative to Robert Giesecke's original library.
I see your point, but from the beginning, i talked about the same project (i named it Robert Giesecke DllExport just for convenience).
Originally Posted by
Jadd
Try it, no one else I know has ever had issues with it. So if you have issues, it's most likely a you problem.
I'm using it extensively for about five (5) years. And i know that is my problem (bad configuration after updating the visual studio).
My point was to try something new - like using C++/CLI way of injection.
Thanks.
Last edited by tutrakan; 07-12-2017 at 07:06 AM.
-
This is how I used to do it. But I'd try to get DllExports to work if I were you, it's much more graceful.
Code:
#pragma once
enum class ClrLoadResult {
Success,
DllInjectionError,
ClrAlreadyLoadedError,
CreateInstanceError,
GetRuntimeError,
GetInterfaceError,
InternalModuleError
};
namespace Odysseus {
namespace ClrHost {
class Engine {
SINGLETON_CLASS(Engine) = default;
public:
ClrLoadResult loadClr();
int executeAssembly(wchar_t *assemblyPath, wchar_t *typeName, wchar_t *functionName, wchar_t *arguments);
void shutdown();
bool isRuntimeLoaded() {
return m_clrLoaded;
}
bool isAssemblyLoaded() {
return m_assemblyLoaded;
}
bool isShuttingDown() {
return m_shutdown;
}
private:
ICLRRuntimeHost* m_runtimeHost;
DWORD m_appDomain;
bool m_clrLoaded, m_assemblyLoaded, m_shutdown;
};
}
}
#define sEngine Odysseus::ClrHost::Engine::getInstance()
Code:
#include "Stdafx.h"
#include "Engine.h"
#pragma comment(lib, "mscoree.lib")
namespace Odysseus {
namespace ClrHost {
ClrLoadResult Engine::loadClr() {
m_shutdown = false;
if (m_clrLoaded)
return ClrLoadResult::ClrAlreadyLoadedError;
ICLRMetaHost* metaHost = nullptr;
ICLRRuntimeInfo* runtimeInfo = nullptr;
ICLRRuntimeHost* runtimeHost = nullptr;
HRESULT result;
ClrLoadResult error = ClrLoadResult::Success;
if (result = CLRCreateInstance(CLSID_CLRMetaHost, IID_PPV_ARGS(&metaHost)) == S_OK) {
if (result = metaHost->GetRuntime(L"v4.0.30319", IID_PPV_ARGS(&runtimeInfo)) == S_OK) {
if (result = runtimeInfo->GetInterface(CLSID_CLRRuntimeHost, IID_PPV_ARGS(&runtimeHost)) == S_OK)
runtimeHost->Start();
else error = ClrLoadResult::GetInterfaceError;
} else error = ClrLoadResult::GetRuntimeError;
} else error = ClrLoadResult::CreateInstanceError;
if (runtimeInfo != nullptr)
runtimeInfo->Release();
if (metaHost != nullptr)
metaHost->Release();
if (error == ClrLoadResult::Success) {
m_runtimeHost = runtimeHost;
m_clrLoaded = true;
}
return error;
}
int Engine::executeAssembly(wchar_t *assemblyPath, wchar_t *typeName, wchar_t *functionName, wchar_t *arguments) {
if (!m_clrLoaded)
return EXIT_FAILURE;
DWORD result = 0;
if (m_runtimeHost->ExecuteInDefaultAppDomain(assemblyPath, typeName, functionName, arguments, &result) != S_OK)
return EXIT_FAILURE;
m_appDomain = result;
m_assemblyLoaded = true;
return result == -1;
}
void Engine::shutdown() {
m_shutdown = true;
if (m_runtimeHost != nullptr) {
m_runtimeHost->UnloadAppDomain(m_appDomain, TRUE);
m_runtimeHost->Release();
m_runtimeHost = nullptr;
m_clrLoaded = false;
m_assemblyLoaded = false;
}
}
}
}
-
Post Thanks / Like - 1 Thanks
tutrakan (1 members gave Thanks to Jadd for this useful post)
-
Originally Posted by
tutrakan
that uses third party libs as RGiesecke DllExport, but they are buggy and crash after every update of visual studio.
The RGiesecke DllExport works great still. No need to re-invent the wheel here imo. The issue you see is due to the fact that the (at least NuGet one does, no sure about github ons/others mentioned here) that it requires (last time I checked and likely currently still does) the Microsoft Build Tools 2015 installed on the machine to build with out errors. This version of Microsoft Build Tools can be installed concurrently with the newer ones with out issue. Simply download, install, restart.
Once this is done you have 2-3 ways to go about things.. well, there are more I am sure but these are the two approaches I use and consider really as simple as you can get.
- Clone my DomainWrapper GitHub project.
Compile all versions of the project desired, such as debug/release and x86/x64 versions. Then simply have your injector find out what DLL to inject /call its export at run-tim. For example, if your application supports x64 and x86, check the process architecture you are injecting to, and tell your loader to call the x64 version of the domain wrapper or vice versa, same applies if you want different settings for debug/release builds like trace symbols debug symbols etc.
The argument passed is a direct path to the application to host, for example: C:\Users\Name\Desktop\Test\TestApp.exe, if the app being injected does not contain an entry point like a class library for example, you must use reflection to find and invoke it inside your injector e.g something like (this example uses a name, attributes fit this best though imo):
Example of finding and invoking an 'entry point' manually via reflection
Note: You may also reference the domain wrapper project its self in your project, and make build output paths relevant via visual studio UI by right clicking project -> build -> set output to what you desire, I always just use something like:
Code:
..\bin\Debug
// or just edit .cspro jsections manually, for example:
<OutputPath>..\bin\Release\</OutputPath>
- Use the UnmanagedExports Nuget to directly export functions inside your own DLL.
Make your injector just call your export. A simple example would be something like this
There is advantages and disadvantages to both options. The first option is very friendly with things like obfuscation and relative folder paths, since you have no need to apply obfuscation to the proxy dll. The second is ideal, but if you want obfuscation it can get messy due to the meta data for the exports getting destroyed sometimes if not done right.
-
Post Thanks / Like - 1 Thanks
tutrakan (1 members gave Thanks to lolp1 for this useful post)
-
Contributor
I'm impressed from this excellent answer. Thanks!
As I see you are an intelligent person, what do you think about this?
Originally Posted by
tutrakan
Last edited by tutrakan; 07-30-2017 at 07:34 PM.
-
Originally Posted by
tutrakan
I'm impressed from this excellent answer. Thanks!
As I see you are an intelligent person, what do you think about this?
I do not think some specific knowledge about a rather uncommon topic in the managed programming world makes some one intelligent, but my question would be why? The unmanaged exports works perfectly fine once you install the MS build 2015 tools, and I've never had an issue. It does not leave behind extra dlls you need to ship, it works with simple attributes, is managed via NuGet. What possible reason could there be to use anything else unless you simply were forced to due to the other version not working in your circumstances, or wanting to learn to do it on your own for fun?