Windows窗体的权限少于控制台应用程序吗?

时间:2018-10-20 03:29:29

标签: c# winforms winapi pinvoke

我创建了一个库,用于使用多种方法将DLL文件注入到进程中。我正在使用Windows窗体使用GUI进行测试。

除使用QueueUserAPC之外,所有方法均按预期工作。当我尝试使用此方法时,将DLL注入的过程崩溃。

我创建了一个基本的控制台应用程序来在Windows窗体之外测试此方法,并且该方法按预期工作,而不会导致进程崩溃。此外,我的错误检查告诉我,从Windows窗体使用QueueUserAPC方法时,DLL注入时没有任何错误,但是,该过程仍然崩溃。

我觉得使用Windows窗体时进程崩溃的原因与QueueUserAPC方法的代码以及Windows窗体的权限无关。但是,我可能错了,所以我将在下面的方法中添加代码。

pinvoke

[DllImport("kernel32.dll", SetLastError = true)]
public static extern IntPtr OpenProcess(ProcessPrivileges dwDesiredAccess, bool bInheritHandle, int dwProcessId);

[DllImport("kernel32.dll", SetLastError = true)]
public static extern IntPtr GetModuleHandle(string lpModuleName);

[DllImport("kernel32.dll", SetLastError = true)]
public static extern IntPtr GetProcAddress(IntPtr hModule, string procName);

[DllImport("kernel32.dll", SetLastError = true)]
public static extern IntPtr VirtualAllocEx(IntPtr hProcess, IntPtr lpAddress, uint dwSize, MemoryAllocation flAllocationType, MemoryProtection flProtect);

[DllImport("kernel32.dll", SetLastError = true)]
public static extern bool WriteProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress, byte[] lpBuffer, uint nSize, int lpNumberOfBytesWritten);

[DllImport("kernel32.dll", SetLastError = true)]
public static extern bool QueueUserAPC(IntPtr pfnAPC, IntPtr hThread, IntPtr dwData);

[DllImport("kernel32.dll", SetLastError = true)]
public static extern void CloseHandle(IntPtr handle);

[DllImport("kernel32.dll", SetLastError=true)]
public static extern void VirtualFreeEx(IntPtr hProcess, IntPtr lpAddress, int dwSize, MemoryAllocation dwFreeType);

public enum MemoryAllocation
{
    Commit = 0x1000,
    Reserve = 0x2000,
    Release = 0x8000,
    AllAccess = Commit | Reserve
}

public enum MemoryProtection
{
    PageReadWrite = 0x04,
    PageExecuteReadWrite = 0x40
}

public enum ThreadAccess
{
    SuspendResume = 0x02,
    GetContext = 0x08,
    SetContext = 0x010,
    AllAccess = SuspendResume | GetContext | SetContext
}

QueueUserAPC方法

public static class MQueueUserAPC
{
    public static bool Inject(string dllPath, string processName)
    {
        // Get the pointer to load library

        var loadLibraryPointer = GetProcAddress(GetModuleHandle("kernel32.dll"), "LoadLibraryA");

        if (loadLibraryPointer == IntPtr.Zero)
        {
            return false;
        }

        // Get the handle of the specified process

        var processId = Process.GetProcessesByName(processName)[0].Id;

        var processHandle = OpenProcess(ProcessPrivileges.AllAccess, false, processId);

        if (processHandle == IntPtr.Zero)
        {
            return false;
        }

        // Allocate memory for the dll name

        var dllNameSize = dllPath.Length + 1;

        var dllMemoryPointer = VirtualAllocEx(processHandle, IntPtr.Zero, (uint) dllNameSize, MemoryAllocation.AllAccess, MemoryProtection.PageReadWrite);

        if (dllMemoryPointer == IntPtr.Zero)
        {
            return false;
        }

        // Write the dll name into memory

        var dllBytes = Encoding.Default.GetBytes(dllPath);

        if (!WriteProcessMemory(processHandle, dllMemoryPointer, dllBytes, (uint) dllNameSize, 0))
        {
            return false;
        }

        // Call QueueUserAPC on each thread

        foreach (var thread in Process.GetProcessesByName(processName)[0].Threads.Cast<ProcessThread>())
        {
            var threadId = thread.Id;

            // Get the threads handle

            var threadHandle = OpenThread(ThreadAccess.SetContext, false, (uint) threadId);

            // Add a user-mode APC to the APC queue of the thread

            QueueUserAPC(loadLibraryPointer, threadHandle, dllMemoryPointer);

            // Close the handle to the thread

            CloseHandle(threadHandle);
        }

        // Close the previously opened handle

        CloseHandle(processHandle);

        // Free the previously allocated memory

        VirtualFreeEx(processHandle, dllMemoryPointer, dllNameSize, MemoryAllocation.Release);

        return true;
    }  
}

我如何在Windows窗体/控制台应用程序中使用它

var injector = new Injector();

if(Injector.QueueUserAPC(dllPath, processName))
{
    MessageBox.Show("No error was raised");
}

我想我要问的是Windows窗体的权限少于控制台应用程序,如果是这样,我该如何配置Windows窗体,以免在尝试使用QueueUserAPC时不会遇到进程崩溃的问题。

如果您想测试该库,请在Github上找到它,并附带使用说明。

1 个答案:

答案 0 :(得分:1)

进程崩溃,因为您调用VirtualFreeEx进行内存存储,并在其中存储dll的名称(dllMemoryPointer)。 LoadLibrary开始使用此内存时,它可能已经无效。 APC -这是异步过程调用,因此在调用VirtualFreeExLoadLibrary已经执行或处理中,您无法知道,甚至无法开始。

有关权限-当然没有。如果您没有权限-您只会使打开进程或进程中的线程失败。结果什么也不会发生。确认您在目标流程中崩溃了,反之亦然,签证确认了您的权限。

还需要了解,在向其中注入 APC 后,并非所有进程中的线程都处于警报状态。这样就可以了,尽管 APC 将成功排队-永远不会调用它。将其注入正在处理的所有线程,希望其中至少一个处于错误状态的可警报状态。起初可能不是,其次-某些工作线程根本无法设计用于调用LoadLibrary-说该线程可以没有激活上下文,不能连接到csrss,可能还有其他。所有这一切也会产生崩溃或不确定的效果。最后这根本没有效率。

通过QueueUserAPC进行注入很有用,以防您创建流程本身(处于挂起状态)并在继续之前将apc注入初始流程线程。这将是可行的(直到最低限度),因为进程中的新线程始终总是从LdrInitializeThunk(在此他初始化进程和/或调用load dlls代码)开始在用户模式下执行,并且始终在跳转到实际入口之前(总而言之)现有的Windows版本)调用ZwTestAlert。恰在此时,您的 APC 调用将被执行。但严格来说,这也不是100%正确的方法,因为我们将LoadLibrary[W/A]用作 APC 入口点。但是,如果kernel32.dll尚未映射到进程或尚未初始化时, APC 过早地执行该操作会怎样?显然那崩溃。在清晰的窗口中一定不能出现这种情况(进程完全初始化后,在调用exe入口之前,将完全加载所有静态dll,然后才调用apc)。但是可能某些驱动程序会通过 APC 调用(例如映射 kernel32.dll 事件)注入自身代码以进行处理,并强制 APC 在此执行点。结果,这里只有100%可靠的方式使用ZwQueueApcThread进行shellcode,它将仅调用ntdll api,通过LdrLoadDll加载dll,并且dll本身仅从 ntdll.dll