【发布时间】:2019-01-16 09:02:15
【问题描述】:
我正在尝试在另一个进程中(使用 CreateRemoteThread)从外部调用 FreeLibraryAndExitThread,以便我可以卸载通过 LoadLibrary 从外部加载的模块。
我了解,虽然 CreateRemoteThread 需要 1 个参数,但如果您需要多个参数,则可以为其提供多个参数的结构。
如果尝试了以下没有卸载模块的方法。事实上,它似乎什么也没做。
请注意,我已删除所有错误检查以保持这篇文章的简单和简短
[DllImport("kernel32.dll", SetLastError = true)]
internal static extern IntPtr GetModuleHandle(string moduleName);
[DllImport("kernel32.dll", SetLastError = true)]
internal static extern IntPtr GetProcAddress(IntPtr moduleHandle, string procName);
[DllImport("kernel32.dll", SetLastError = true)]
internal static extern IntPtr VirtualAllocEx(IntPtr processHandle, IntPtr baseAddress, int size, int allocationType, int protection);
[DllImport("kernel32.dll", SetLastError = true)]
internal static extern bool WriteProcessMemory(IntPtr processHandle, IntPtr baseAddress, byte[] buffer, int size, int bytesWritten);
[DllImport("kernel32.dll", SetLastError = true)]
internal static extern IntPtr CreateRemoteThread(IntPtr processHandle, IntPtr threadAttributes, int stackSize, IntPtr startAddress, IntPtr parameter, int creationFlags, int threadId);
private struct FreeLibraryAndExitThreadParameters
{
internal IntPtr ModuleAddress;
internal int ExitCode;
}
var process = Process.GetProcessesByName("notepad")[0];
var freeLibraryAndExitThreadAddress = GetProcAddress(GetModuleHandle("kernel32.dll"), "FreeLibraryAndExitThread");
// Get an instance of the module - dllName is the name of the module I am trying to unload
var module = process.Modules.Cast<ProcessModule>().SingleOrDefault(m => string.Equals(m.ModuleName, dllName, StringComparison.OrdinalIgnoreCase));
var freeLibraryAndExitThreadParameters = new FreeLibraryAndExitThreadParameters { ModuleAddress = module.BaseAddress, ExitCode = 0 };
// This code turns the struct into a byte array
var structureSize = Marshal.SizeOf(freeLibraryAndExitThreadParameters);
var structureBytes = new byte[structureSize];
var buffer = Marshal.AllocHGlobal(structureSize);
Marshal.StructureToPtr(freeLibraryAndExitThreadParameters, buffer, true);
Marshal.Copy(buffer, structureBytes, 0, structureSize);
Marshal.FreeHGlobal(buffer);
// Allocate memory in the remote process with commit and reserve allocation type and PageExecuteReadWrite permissions
var remoteAddress = VirtualAllocEx(process.Handle, IntPtr.Zero, structureSize, 0x01000 | 0x02000, 0x040);
// Write the structure into the remote process
WriteProcessMemory(process.Handle, remoteAddress, buffer, structureSize, 0);
// Finally call CreateRemoteThread to execute the function in the remote process
CreateRemoteThread(process.Handle, IntPtr.Zero, 0, freeLibraryAndExitThreadAddress, remoteAddress, 0, 0);
所有 pinvoke 调用实际上都没有失败,我可以看到字节正在写入内存,但在创建远程线程后似乎什么都没有发生 - 在我的实际代码中,我调用 WaitForSingleObject 并且线程也完成了它的任务没问题。
谁能指出我做错了什么以及如何解决这个问题,以便我可以在远程进程中从外部调用 FreeLibraryAndExitThread?
值得一提的是,我可以通过这种方法使用 FreeLibrary - 它工作正常(删除结构,因为它只需要 1 个参数)但我特别需要对需要卸载的模块使用 FreeLibraryAndExitThread,这就是我为什么不使用更简单的 FreeLibrary。
【问题讨论】:
-
你必须传递给
CreateRemoteThread你想要卸载的模块地址而不是remoteAddress。您在远程进程中不需要任何VirtualAllocEx。为了什么? -
我正在尝试卸载远程进程中的模块。 FreeLibraryAndExitThread 需要 2 个参数,而不是像 FreeLibrary 那样,因此需要分配内存并将参数写入远程进程。我不能用一个变量同时传递参数 2。
-
不,你错了。你不需要在远程进程中分配任何东西。第二个参数未定义。不管是线程的退出代码。所以你可以调用这个api,就像它只需要1个参数一样。只需传递您要卸载的模块的地址
-
我试过了,远程线程完成后进程终止。该模块似乎也已卸载,但理想情况下我希望该过程不会崩溃
-
但是如果你卸载模块,它被进程代码使用 - 当然它可能会崩溃,如果调用卸载模块的代码。你想做什么?您尝试卸载的 dll 是什么?你需要使用调试器看看会发生什么。但在远程进程中正式调用
FreeLibraryAndExitThread非常简单。您需要CreateRemoteThread(hProcess, 0, 0, FreeLibraryAndExitThread, hmod, 0, 0),其中hmod是您要卸载的模块的地址。您不需要在远程进程中分配任何内存。该api采用2个参数-在具体情况下没问题。我们只能通过一个,一切都会好的