DLL Injector在某些机器上失败了吗?

时间:2013-10-04 18:06:21

标签: c# asp.net .net

首先,不,我不是黑客游戏,这是一个软件的简单IP重定向器。

我发现了一个DLL注入器,它也可以从库中调用导出,这正是我需要的。在我的机器中,它工作得很好(Win 7 x64),然而,在某些机器中,当它注入程序时只是说“停止工作”或卡住了。这是为什么?这是代码。

    using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;
using System.ComponentModel;
using System.Runtime.InteropServices;
using System.IO;

using Syringe.Win32;


namespace Syringe
{
    public class Injector : IDisposable
    {
        /// <summary>
        /// Internal class to represent an Injected Module within Injector's target process.
        /// Simply manages finding export addresses for functions, and caches these addresses for
        /// potential future use.
        /// </summary>
        private class InjectedModule
        {
            private Dictionary<string, IntPtr> exports;

            public ProcessModule Module { get; private set; }
            public IntPtr BaseAddress { get { return Module.BaseAddress; } }

            public InjectedModule(ProcessModule module)
            {
                Module = module;
                exports = new Dictionary<string, IntPtr>();
            }

            /// <summary>
            /// Get the address of a given function exported in the Module.
            /// If the function can't be found, this will throw <see cref="Win32Exception"/>
            /// </summary>
            /// <param name="func">Name of function to search for</param>
            /// <returns><see cref="IntPtr"/> representing the address of this function in the target process</returns>
            /// <exception cref="Win32Exception">Thrown if unable to find function address in module</exception>
            public IntPtr this[string func]
            {
                get
                {
                    if (!exports.ContainsKey(func))
                        exports[func] = FindExport(func);
                    return exports[func];
                }
            }

            /**
             * Actual function to find export - loosely modelled off Cypher's idea/code for loading module into this
             * process to find address. Loads module as data, finds RVA of function and uses to find address in target 
             * process
             */
            private IntPtr FindExport(string func)
            {
                IntPtr hModule = IntPtr.Zero;
                try
                {
                    // Load module into local process address space
                    hModule = Imports.LoadLibraryEx(Module.FileName, IntPtr.Zero, LoadLibraryExFlags.DontResolveDllReferences);
                    if (hModule == IntPtr.Zero)
                        throw new Win32Exception(Marshal.GetLastWin32Error());

                    // Call GetProcAddress to get the address of the function in the module locally
                    IntPtr pFunc = Imports.GetProcAddress(hModule, func);
                    if (pFunc == IntPtr.Zero)
                        throw new Win32Exception(Marshal.GetLastWin32Error());

                    // Get RVA of export and add to base address of injected module
                    // hack at the moment to deal with x64
                    bool x64 = IntPtr.Size == 8;
                    IntPtr pExportAddr;
                    if (x64)
                        pExportAddr = new IntPtr(Module.BaseAddress.ToInt64() + (pFunc.ToInt64() - hModule.ToInt64()));
                    else
                        pExportAddr = new IntPtr(Module.BaseAddress.ToInt32() + (pFunc.ToInt32() - hModule.ToInt32()));

                    return pExportAddr;
                }
                finally
                {
                    try
                    {
                        Imports.CloseHandle(hModule);
                    }
                    catch
                    {
                    }
                }
            }
        }

        private Process _process;
        private IntPtr _handle;
        private Dictionary<string, InjectedModule> injectedModules;

        public Injector(Process process) : this(process, true) { }
        public Injector(Process process, bool ejectOnDispose)
        {
            if (process == null)
                throw new ArgumentNullException("process");
            if (process.Id == Process.GetCurrentProcess().Id)
                throw new InvalidOperationException("Cannot create an injector for the current process");

            Process.EnterDebugMode();

            _handle = Imports.OpenProcess(
                ProcessAccessFlags.QueryInformation | ProcessAccessFlags.CreateThread |
                ProcessAccessFlags.VMOperation | ProcessAccessFlags.VMWrite |
                ProcessAccessFlags.VMRead, false, process.Id);

            if (_handle == IntPtr.Zero)
                throw new Win32Exception(Marshal.GetLastWin32Error());

            _process = process;
            EjectOnDispose = ejectOnDispose;
            injectedModules = new Dictionary<string, InjectedModule>();
        }

        public bool EjectOnDispose { get; set; }

        /// <summary>
        /// Injects a library into this Injector's process. <paramref name="libPath"/> can be 
        /// relative or absolute; either way, the injected module will be referred to by module name only.
        /// I.e. "c:\some\directory\library.dll", "library.dll" and "..\library.dll" will all be referred to
        /// as "library.dll"
        /// </summary>
        /// <param name="libPath">Relative or absolute path to the dll to be injected</param>
        public void InjectLibrary(string libPath)
        {
            // (in?)sanity check, pretty sure this is never possible as the constructor will error - left over from how it previously was developed
            if (_process == null)
                throw new InvalidOperationException("This injector has no associated process and thus cannot inject a library");
            if (_handle == IntPtr.Zero)
                throw new InvalidOperationException("This injector does not have a valid handle to the associated process and thus cannot inject a library");

            if (!File.Exists(libPath))
                throw new FileNotFoundException(string.Format("Unable to find library {0} to inject into process {1}", libPath, _process.ProcessName), libPath);

            // convenience variables
            string fullPath = Path.GetFullPath(libPath);
            string libName = Path.GetFileName(fullPath);

            // declare resources that need to be freed in finally
            IntPtr pLibRemote = IntPtr.Zero; // pointer to allocated memory of lib path string
            IntPtr hThread = IntPtr.Zero; // handle to thread from CreateRemoteThread
            IntPtr pLibFullPathUnmanaged = Marshal.StringToHGlobalUni(fullPath); // unmanaged C-String pointer

            try
            {
                uint sizeUni = (uint)Encoding.Unicode.GetByteCount(fullPath);

                // Get Handle to Kernel32.dll and pointer to LoadLibraryW
                IntPtr hKernel32 = Imports.GetModuleHandle("Kernel32");
                if (hKernel32 == IntPtr.Zero)
                    throw new Win32Exception(Marshal.GetLastWin32Error());
                IntPtr hLoadLib = Imports.GetProcAddress(hKernel32, "LoadLibraryW");
                if (hLoadLib == IntPtr.Zero)
                    throw new Win32Exception(Marshal.GetLastWin32Error());

                // allocate memory to the local process for libFullPath
                pLibRemote = Imports.VirtualAllocEx(_handle, IntPtr.Zero, sizeUni, AllocationType.Commit, MemoryProtection.ReadWrite);
                if (pLibRemote == IntPtr.Zero)
                    throw new Win32Exception(Marshal.GetLastWin32Error());

                // write libFullPath to pLibPath
                int bytesWritten;
                if (!Imports.WriteProcessMemory(_handle, pLibRemote, pLibFullPathUnmanaged, sizeUni, out bytesWritten) || bytesWritten != (int)sizeUni)
                    throw new Win32Exception(Marshal.GetLastWin32Error());

                // load dll via call to LoadLibrary using CreateRemoteThread
                hThread = Imports.CreateRemoteThread(_handle, IntPtr.Zero, 0, hLoadLib, pLibRemote, 0, IntPtr.Zero);
                if (hThread == IntPtr.Zero)
                    throw new Win32Exception(Marshal.GetLastWin32Error());
                if (Imports.WaitForSingleObject(hThread, (uint)ThreadWaitValue.Infinite) != (uint)ThreadWaitValue.Object0)
                    throw new Win32Exception(Marshal.GetLastWin32Error());

                // get address of loaded module - this doesn't work in x64, so just iterate module list to find injected module
                IntPtr hLibModule;// = IntPtr.Zero;
                if (!Imports.GetExitCodeThread(hThread, out hLibModule))
                    throw new Win32Exception(Marshal.GetLastWin32Error());
                if (hLibModule == IntPtr.Zero)
                    throw new Exception("Code executed properly, but unable to get an appropriate module handle, possible Win32Exception", new Win32Exception(Marshal.GetLastWin32Error()));

                // iterate modules in target process to find our newly injected module
                ProcessModule modFound = null;
                foreach (ProcessModule mod in _process.Modules)
                {
                    if (mod.ModuleName == libName)
                    {
                        modFound = mod;
                        break;
                    }
                }
                if (modFound == null)
                    throw new Exception("Injected module could not be found within the target process!");

                injectedModules.Add(libName, new InjectedModule(modFound));
            }
            finally
            {
                Marshal.FreeHGlobal(pLibFullPathUnmanaged); // free unmanaged string
                Imports.CloseHandle(hThread); // close thread from CreateRemoteThread
                Imports.VirtualFreeEx(_process.Handle, pLibRemote, 0, AllocationType.Release); // Free memory allocated
            }
        }

        /// <summary>
        /// Ejects a library that this Injector has previously injected into the target process. <paramref name="libName"/> is the name of the module to
        /// eject, as per the name stored in <see cref="Injector.InjectLibrary"/>. Passing the same value as passed to InjectLibrary should always work unless a 
        /// relative path was used and the program's working directory has changed.
        /// </summary>
        /// <param name="libName">The name of the module to eject</param>
        public void EjectLibrary(string libName)
        {
            string libSearchName = File.Exists(libName) ? Path.GetFileName(Path.GetFullPath(libName)) : libName;

            if (!injectedModules.ContainsKey(libSearchName))
                throw new InvalidOperationException("That module has not been injected into the process and thus cannot be ejected");

            // resources that need to be freed
            IntPtr hThread = IntPtr.Zero;

            try
            {
                // get handle to kernel32 and FreeLibrary
                IntPtr hKernel32 = Imports.GetModuleHandle("Kernel32");
                if (hKernel32 == IntPtr.Zero)
                    throw new Win32Exception(Marshal.GetLastWin32Error());
                IntPtr hFreeLib = Imports.GetProcAddress(hKernel32, "FreeLibrary");
                if (hFreeLib == IntPtr.Zero)
                    throw new Win32Exception(Marshal.GetLastWin32Error());

                hThread = Imports.CreateRemoteThread(_handle, IntPtr.Zero, 0, hFreeLib, injectedModules[libSearchName].BaseAddress, 0, IntPtr.Zero);
                if (hThread == IntPtr.Zero)
                    throw new Win32Exception(Marshal.GetLastWin32Error());
                if (Imports.WaitForSingleObject(hThread, (uint)ThreadWaitValue.Infinite) != (uint)ThreadWaitValue.Object0)
                    throw new Win32Exception(Marshal.GetLastWin32Error());

                // get exit code of FreeLibrary
                IntPtr pFreeLibRet;// = IntPtr.Zero;
                if (!Imports.GetExitCodeThread(hThread, out pFreeLibRet))
                    throw new Win32Exception(Marshal.GetLastWin32Error());

                if (pFreeLibRet == IntPtr.Zero)
                    throw new Exception("FreeLibrary failed in remote process");
            }
            finally
            {
                Imports.CloseHandle(hThread);
            }
        }

        /// <summary>
        /// Call an export with no parameter in the target process (i.e. SomeFunc( void );). This function will only
        /// return once the remote thread in the target process has returned.
        /// </summary>
        /// <param name="libName">Name of the injected module in which the function should be found</param>
        /// <param name="funcName">Name of the exported function to call</param>
        /// <returns><see cref="IntPtr"/> representing the return value of function <paramref name="funcName"/> in module <paramref name="libName"/></returns>
        public IntPtr CallExport(string libName, string funcName)
        {
            return CallExport((uint)ThreadWaitValue.Infinite, libName, funcName);
        }

        /// <summary>
        /// Call an export with no parameter in the target process (i.e. SomeFunc( void );). This function returns after
        /// <paramref name="timeout"/> ms have elapsed, or the remote function finishes (whichever comes first).
        /// </summary>
        /// <param name="timeout">Number of miliseconds to wait for the remote function to return</param>
        /// <param name="libName">Name of the injected module in which the function should be found</param>
        /// <param name="funcName">Name of the exported function to call</param>
        /// <returns><see cref="IntPtr"/> representing the return value of function <paramref name="funcName"/> in module <paramref name="libName"/></returns>
        public IntPtr CallExport(uint timeout, string libName, string funcName) // param location to avoid possible overload / generic confusion
        {
            return CallExportInternal(timeout, libName, funcName, IntPtr.Zero, null, 0);
        }

        /// <summary>
        /// Call an export with type <typeparamref name="T"/> data parameter. <typeparamref name="T"/> must be a struct (i.e. a 
        /// value type or user-defined struct). User-defined structs must have the <see cref="System.Runtime.InteropServices.StructLayoutAttribute"/> set to
        /// <see cref="System.Runtime.InteropServices.LayoutKind.Sequential"/> or <see cref="System.Runtime.InteropServices.LayoutKind.Explicit"/>. User-defined
        /// structs containing variable length C-style strings should adorn strings with <see cref="CustomMarshalAsAttribute"/> and set the value to the appropriate
        /// <see cref="CustomUnmanagedType"/> value.
        /// This function will wait until the remote function finishes.
        /// </summary>
        /// <typeparam name="T">Type of data remote function expects. Value type or struct only.</typeparam>
        /// <param name="libName">Name of the injected module in which the function should be found</param>
        /// <param name="funcName">Name of the exported function to call</param>
        /// <param name="data">Data of type <typeparamref name="T"/> to be sent as argument to remote function</param>
        /// <returns><see cref="IntPtr"/> representing the return value of function <paramref name="funcName"/> in module <paramref name="libName"/></returns>
        public IntPtr CallExport<T>(string libName, string funcName, T data) where T : struct
        {
            return CallExport<T>((uint)ThreadWaitValue.Infinite, libName, funcName, data);
        }

        /// <summary>
        /// Call an export with type <typeparamref name="T"/> data parameter. <typeparamref name="T"/> must be a struct (i.e. a 
        /// value type or user-defined struct). User-defined structs must have the <see cref="System.Runtime.InteropServices.StructLayoutAttribute"/> set to
        /// <see cref="System.Runtime.InteropServices.LayoutKind.Sequential"/> or <see cref="System.Runtime.InteropServices.LayoutKind.Explicit"/>. User-defined
        /// structs containing variable length C-style strings should adorn strings with <see cref="CustomMarshalAsAttribute"/> and set the value to the appropriate
        /// <see cref="CustomUnmanagedType"/> value.
        /// This function will wait for <paramref name="timeout"/> miliseconds or until the remote function finishes (whichever comes first).
        /// </summary>
        /// <typeparam name="T">Type of data remote function expects. Value type or struct only.</typeparam>
        /// <param name="timeout">Number of miliseconds to wait for the remote function to return</param>
        /// <param name="libName">Name of the injected module in which the function should be found</param>
        /// <param name="funcName">Name of the exported function to call</param>
        /// <param name="data">Data of type <typeparamref name="T"/> to be sent as argument to remote function</param>
        /// <returns><see cref="IntPtr"/> representing the return value of function <paramref name="funcName"/> in module <paramref name="libName"/></returns>
        public IntPtr CallExport<T>(uint timeout, string libName, string funcName, T data) where T : struct
        {
            IntPtr pData = IntPtr.Zero;
            try
            {
                int dataSize = CustomMarshal.SizeOf(data);
                pData = Marshal.AllocHGlobal(dataSize);
                CustomMarshal.StructureToPtr(data, pData, true);
                return CallExportInternal(timeout, libName, funcName, pData, typeof(T), (uint)dataSize);
            }
            finally
            {
                Marshal.FreeHGlobal(pData);
            }
        }

        private IntPtr CallExportInternal(uint timeout, string libName, string funcName, IntPtr data, Type dataType, uint dataSize)
        {
            string libSearchName = File.Exists(libName) ? Path.GetFileName(Path.GetFullPath(libName)) : libName;

            if (!injectedModules.ContainsKey(libSearchName))
                throw new InvalidOperationException("That module has not been injected into the process and thus cannot be ejected");

            IntPtr pFunc = injectedModules[libSearchName][funcName];
            // resources that need to be cleaned
            IntPtr pDataRemote = IntPtr.Zero;
            IntPtr hThread = IntPtr.Zero;

            try
            {
                // check if we have all required parameters to pass a data parameter
                // if we don't, assume we aren't passing any data
                if (!(data == IntPtr.Zero || dataSize == 0 || dataType == null))
                {
                    // allocate memory in remote process for parameter
                    pDataRemote = Imports.VirtualAllocEx(_handle, IntPtr.Zero, dataSize, AllocationType.Commit, MemoryProtection.ReadWrite);
                    if (pDataRemote == IntPtr.Zero)
                        throw new Win32Exception(Marshal.GetLastWin32Error());

                    // rebase the data so that pointers point to valid memory locations for the target process
                    // this renders the unmanaged structure useless in this process - should be able to re-rebase back to
                    // this target process by calling CustomMarshal.RebaseUnmanagedStructure(data, data, dataType); but not tested
                    CustomMarshal.RebaseUnmanagedStructure(data, pDataRemote, dataType);

                    int bytesWritten;
                    if (!Imports.WriteProcessMemory(_handle, pDataRemote, data, dataSize, out bytesWritten))
                        throw new Win32Exception(Marshal.GetLastWin32Error());
                }

                hThread = Imports.CreateRemoteThread(_handle, IntPtr.Zero, 0, pFunc, pDataRemote, 0, IntPtr.Zero);
                if (hThread == IntPtr.Zero)
                    throw new Win32Exception(Marshal.GetLastWin32Error());

                uint singleObject = Imports.WaitForSingleObject(hThread, timeout);
                if (!(singleObject == (uint)ThreadWaitValue.Object0 || singleObject == (uint)ThreadWaitValue.Timeout))
                    throw new Win32Exception(Marshal.GetLastWin32Error());

                IntPtr pRet;
                if (!Imports.GetExitCodeThread(hThread, out pRet))
                    throw new Win32Exception(Marshal.GetLastWin32Error());

                return pRet;
            }
            finally
            {
                Imports.VirtualFreeEx(_process.Handle, pDataRemote, 0, AllocationType.Release);
                Imports.CloseHandle(hThread);
            }

        }

        #region IDisposable Members

        public void Dispose()
        {
            if (EjectOnDispose)
            {
                foreach (string key in injectedModules.Keys)
                {
                    EjectLibrary(key);
                }
            }
            if (_handle != IntPtr.Zero)


                Imports.CloseHandle(_handle);


   _handle = IntPtr.Zero;

            Process.LeaveDebugMode();
        }

        #endregion
    }
}

现在我想你们中的一些人不会全部阅读..但我不明白为什么它在某些机器上失败了。他们正在使用win 7 x64,就像我一样。我像这样注射:

using (Process process = Process.Start(Path + @"\MyProgram.exe", "Argument1"))
                        {
                            while (process.MainWindowHandle == IntPtr.Zero && !process.HasExited)
                                Thread.Sleep(waitWindowSleep);

                            if (process.HasExited)
                                return;

                            for (int i = 0; i < maxTryCount; i++)
                            {
                                using (Injector injector = new Injector(process))
                                {
                                    try
                                    {
                                        injector.EjectOnDispose = false;
                                        injector.InjectLibrary(dll);
                                        if ("127.0.0.1" != IPAddress.Loopback.ToString())
                                            injector.CallExport<IPInfo>(dll, "SetIP", new IPInfo("127.0.0.1"));
                                        return;
                                    }
                                    catch
                                    {
                                        Thread.Sleep(failInjectSleep);
                                    }
                                }
                            }
                        }

那为什么它会在某些机器上崩溃?感谢。

编辑:调试程序.. http://puu.sh/4Hvcf.png - 我收到的错误。进程和应用程序都以管理员身份运行。

1 个答案:

答案 0 :(得分:1)

即使这是一个老问题,我觉得有必要回答它。

您收到的异常消息总结得非常好。 会发生以下情况:您正在使用的库需要调用另一个进程的OpenProcess和CreateRemoteThread。库需要启用SE_DEBUG_PRIVILEGE,因此调用“EnterDebugMode()”,它设置应用程序权限常量SE_DEBUG_NAME。要使此操作成功,用户帐户必须具有调试权限(请参阅下文)。只有管​​理员(和本地机器)才有这个权利。

解决方案:以管理员身份运行具有所需权限的应用程序。确保在应用程序的清单文件中设置“requireAdministrator”可能会有所帮助。

http://msdn.microsoft.com/en-us/library/windows/desktop/ms684320(v=vs.85).aspx http://msdn.microsoft.com/en-US/library/system.diagnostics.process.enterdebugmode(v=vs.110).aspx http://msdn.microsoft.com/en-us/library/windows/desktop/bb530716(v=vs.85).aspx http://technet.microsoft.com/en-us/library/cc787339(v=ws.10).aspx