我创建了一个库,用于使用多种方法将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上找到它,并附带使用说明。
答案 0 :(得分:1)
进程崩溃,因为您调用VirtualFreeEx
进行内存存储,并在其中存储dll的名称(dllMemoryPointer
)。 LoadLibrary
开始使用此内存时,它可能已经无效。 APC -这是异步过程调用,因此在调用VirtualFreeEx
时LoadLibrary
已经执行或处理中,您无法知道,甚至无法开始。
有关权限-当然没有。如果您没有权限-您只会使打开进程或进程中的线程失败。结果什么也不会发生。确认您在目标流程中崩溃了,反之亦然,签证确认了您的权限。
还需要了解,在向其中注入 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