locked
CLR Injection. How to call method from injected library RRS feed

  • Question

  • Hi,

    I have 2 libraries and 1 executable, with executable i inject c++ library to a remote process(notepad.exe) this library loads CLR and C# library and call a method,

    The question is how to call method(void Loader) after i inject c++ library

    C++ Library:

     

    #include <stdio.h>
    #include <windows.h>
    #include <mscoree.h>
    #include <metahost.h>
    #pragma comment(lib, "mscoree.lib")
    
    #import "C:\\Windows\\Microsoft.NET\\Framework\\v4.0.30319\\mscorlib.tlb"	\
    	raw_interfaces_only														\
     high_property_prefixes("_get","_put","_putref")							\
     rename("ReportEvent", "InteropServices_ReportEvent")
    
    using namespace mscorlib;
    
    extern "C" __declspec(dllexport) void __cdecl Loader();
    
    void Loader()
    {
    	ICLRMetaHost *pMetaHost = NULL;
    	ICLRRuntimeInfo *pRuntimeInfo = NULL;
    	ICLRRuntimeHost *pClrRuntimeHost = NULL;
    	ICorRuntimeHost *pCorRuntimeHost = NULL;
    	IUnknownPtr spAppDomainThunk = NULL;
    	_AppDomainPtr spDefaultAppDomain = NULL;
    	_AssemblyPtr spAssembly = NULL;
    	_TypePtr spType = NULL;
    	SAFEARRAY *psaStaticMethodArgs = NULL;
    	variant_t vtEmpty;
    	variant_t vtLengthRet;
    	bstr_t bstrAssemblyName(L"ARPP");
    	bstr_t bstrClassName(L"ARPP.Program");
    	bstr_t bstrStaticMethodName(L"Main");
    
    	HRESULT hr= CLRCreateInstance(CLSID_CLRMetaHost, IID_PPV_ARGS(&pMetaHost));
    	if (FAILED(hr)) goto Cleanup;
    
    	hr = pMetaHost->GetRuntime(L"v4.0.30319", IID_PPV_ARGS(&pRuntimeInfo));
    	if (FAILED(hr)) goto Cleanup;
    
    	BOOL fLoadable;
    
     hr = pRuntimeInfo->IsLoadable(&fLoadable);
    	if (FAILED(hr) || !fLoadable) goto Cleanup;
    
    	hr = pRuntimeInfo->GetInterface(CLSID_CorRuntimeHost, IID_PPV_ARGS(&pCorRuntimeHost));
    	if (FAILED(hr)) goto Cleanup;
    
    	hr = pCorRuntimeHost->Start();
    	if (FAILED(hr)) goto Cleanup;
    
    	hr = pCorRuntimeHost->GetDefaultDomain(&spAppDomainThunk);
    	if (FAILED(hr)) goto Cleanup;
    
    	hr = spAppDomainThunk->QueryInterface(IID_PPV_ARGS(&spDefaultAppDomain));
    	if (FAILED(hr)) goto Cleanup;
    
    	hr = spDefaultAppDomain->Load_2(bstrAssemblyName, &spAssembly);
    	if (FAILED(hr)) goto Cleanup;
    
    	hr = spAssembly->GetType_2(bstrClassName, &spType);
    	if (FAILED(hr)) goto Cleanup;
    
    	psaStaticMethodArgs = SafeArrayCreateVector(VT_VARIANT, 0, 0);
    
    	hr = spType->InvokeMember_3(bstrStaticMethodName, static_cast<BindingFlags>(
     BindingFlags_InvokeMethod | BindingFlags_Static | BindingFlags_Public), 
     NULL, vtEmpty, psaStaticMethodArgs, &vtLengthRet);
    
    Cleanup:
    	if (pMetaHost)
     {
     pMetaHost->Release();
     pMetaHost = NULL;
     }
     if (pRuntimeInfo)
     {
     pRuntimeInfo->Release();
     pRuntimeInfo = NULL;
     }
     if (pCorRuntimeHost)
     {
     pCorRuntimeHost->Release();
     pCorRuntimeHost = NULL;
     }
    
     if (psaStaticMethodArgs)
     {
     SafeArrayDestroy(psaStaticMethodArgs);
     psaStaticMethodArgs = NULL;
     }
    }
    
    

     

    C# Library:

     

    using System.Windows.Forms;
    
    namespace ARPP
    {
     public class Program
     {
     public static void Main()
     {
      MessageBox.Show("BINGO");
     }
     }
    }
    
    

     

    C# executable:

     

    using System.Runtime.InteropServices;
    using System;
    using System.IO;
    using System.Diagnostics;
    using System.Reflection;
    
    namespace ARPPLoader
    {
     class Program
     {
     [DllImport("kernel32.dll")]
     public static extern IntPtr OpenProcess(UInt32 dwDesiredAccess, Int32 bInheritHandle, Int32 dwProcessId);
    
     [DllImport("kernel32.dll")]
     public static extern Int32 CloseHandle(IntPtr hObject);
    
     [DllImport("kernel32.dll", SetLastError = true, ExactSpelling = true)]
     static extern IntPtr VirtualAllocEx(IntPtr hProcess, IntPtr lpAddress, uint dwSize, uint flAllocationType, uint flProtect);
    
     [DllImport("kernel32.dll")]
     static extern bool WriteProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress, string lpBuffer, UIntPtr nSize, out IntPtr lpNumberOfBytesWritten);
    
     [DllImport("kernel32.dll", CharSet = CharSet.Ansi, ExactSpelling = true, SetLastError = true)]
     public static extern UIntPtr GetProcAddress(IntPtr hModule, string procName);
    
     [DllImport("kernel32.dll", CharSet = CharSet.Auto)]
     public static extern IntPtr GetModuleHandle(string lpModuleName);
    
     [DllImport("kernel32")]
     public static extern IntPtr CreateRemoteThread(IntPtr hProcess, IntPtr lpThreadAttributes, uint dwStackSize, UIntPtr lpStartAddress, IntPtr lpParameter, uint dwCreationFlags, out IntPtr lpThreadId);
    
     [DllImport("kernel32.dll", SetLastError = true)]
     static extern UInt32 WaitForSingleObject(IntPtr hHandle, UInt32 dwMilliseconds);
    
     [DllImport("kernel32.dll")]
     static extern bool GetExitCodeThread(IntPtr hThread, out IntPtr lpExitCode);
    
     [DllImport("kernel32", SetLastError = true)]
     static extern IntPtr LoadLibrary(string lpFileName);
    
     [DllImport("kernel32.dll", SetLastError = true)]
     static extern bool FreeLibrary(IntPtr hModule);
    
    
     static void Main(string[] args)
     {
      using (var process = Process.GetProcessesByName("notepad")[0])
      {
      IntPtr bytesout;
    
      string loaderDLL = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "Loader.dll");
    
      if (File.Exists(loaderDLL))
      {
       var hLoader = LoadLibrary(loaderDLL);
       var hLoaderProc = GetProcAddress(hLoader, "Loader");
       var moduleOffset = ((uint)hLoaderProc - (uint)hLoader);
       FreeLibrary(hLoader);
    
       Int32 bufferSize = loaderDLL.Length + 1;
       IntPtr hProcess = OpenProcess(0x1F0FFF, 1, process.Id);
       IntPtr AllocMem = VirtualAllocEx(hProcess, IntPtr.Zero, (uint)bufferSize, 0x1000, 0x04);
       WriteProcessMemory(hProcess, AllocMem, loaderDLL, (UIntPtr)bufferSize, out bytesout);
       UIntPtr Injector = (UIntPtr)GetProcAddress(GetModuleHandle("kernel32.dll"), "LoadLibraryA");
       IntPtr hThread = (IntPtr)CreateRemoteThread(hProcess, (IntPtr)null, 0, Injector, AllocMem, 0, out bytesout);
       WaitForSingleObject(hThread, 5000);
       IntPtr hModule;
       GetExitCodeThread(hThread, out hModule);
       CloseHandle(hThread);
       IntPtr AllocMem2 = VirtualAllocEx(hProcess, IntPtr.Zero, 1, 0x1000, 0x04);
       IntPtr hThread2 = (IntPtr)CreateRemoteThread(hProcess, IntPtr.Zero, 0, (UIntPtr)((uint)hModule + moduleOffset), IntPtr.Zero, 0, out bytesout);
       WaitForSingleObject(hThread, 5000);
       CloseHandle(hThread2);
      }
    
      Console.ReadLine();
      }
     }
     }
    }
    
    

     



    • Edited by Rotaru Anatol Saturday, April 9, 2011 12:14 PM gramatical mistake
    Saturday, April 9, 2011 12:10 PM

Answers

  • The code from question is correct, i found the problem ARPP.dll must be in folder with remote process executable, or registered in GAC
    • Marked as answer by Rotaru Anatol Saturday, April 9, 2011 6:52 PM
    Saturday, April 9, 2011 6:52 PM

All replies

  • Step zero; change the function prototype of your Loader() entry point, so that it takes a LPCWSTR as its single parameter and returns an int; i.e. the same signature as LoadLibrary() and therefore equally valid as a thread start function address...

    Step one; inject using load library and a remote thread. Do nothing clever in the injected DLLs DLLMain(). Store the HMODULE that is returned as the exit code of the injecting thread, this is the HMODULE of the injected DLL and the return value of LoadLibrary().

    Step two; load the injected DLL using LoadLibrary into the process that is doing the injecting. Then find the offset of your Loader() entrypoint in YOUR address space and subtract from it the HMODULE of your injected DLL in YOUR address space. You now have the relative offset of the Loader() function. Take the HMODULE of the injected DLL in the target process (i.e. the value you saved in step one) and add the relative address of Loader() to it. You now have the address of Loader() in your target process.

    Step three; call Loader() in the target process using the same 'remote thread' approach that you used to call LoadLibrary(). You can pass a string to the Loader() call, this can be anything you fancy.

    What I tend to do is pass a unique string key that I use as part of a named pipe name. The Injected DLL and the injecting process now both know the name of a named pipe and you can communicate between them. The Loader() function isn't DLLMain() and doesn't suffer from the restrictions that affect DLLMain() (as it's not called from within LoadLibrary, etc) and so you can do normal stuff in it. Once the injected DLL and the injecting process are connected via a named pipe you can pass commands and data results back and forth as you like. Since I pass my "Loader()" equivalent function a string I can make sure that the named pipe is unique for this particular instance of your injecting process and this particular injected dll which means you can run multiple instances of the injecting process at the same time and each process can inject into multiple target processes and all of these communication channels are unique and controllable.

    See here: http://stackoverflow.com/questions/1162050/createremotethread-loadlibrary-and-postthreadmessage-whats-the-proper-ipc-met/1163681#1163681 for more slightly more detail in the comments.

    Saturday, April 9, 2011 5:41 PM
  • The code from question is correct, i found the problem ARPP.dll must be in folder with remote process executable, or registered in GAC
    • Marked as answer by Rotaru Anatol Saturday, April 9, 2011 6:52 PM
    Saturday, April 9, 2011 6:52 PM