通过调用CreateRemoteThread弹出dll:崩溃

时间:2017-08-22 22:44:54

标签: c++ dll

我正在尝试为自己制作一个从进程中提取/释放dll的工具。我已经体验过LoadLibrary和注入,但这次逻辑似乎并不适用。 这是我的代码:

HMODULE findModuleOffset(HANDLE proc, char *mod_name) {
    //Finds module address in specified process. 0 if not found
    HMODULE hMods[2048];
    DWORD modules_byte_size;
    if (EnumProcessModules(proc, hMods, sizeof(hMods), &modules_byte_size))
    {
        for (unsigned long i = 0; i < (modules_byte_size / sizeof(HMODULE)); i++) {
            CHAR module_name[MAX_PATH];

            // Get the full path to the module's file.

            if (GetModuleFileNameExA(proc, hMods[i], module_name, sizeof(module_name))) {
                if (strcmp(strrchr(module_name,'.')+1,"exe")!=0 && compareExeName(module_name, mod_name)) {
                    return hMods[i];
                }
            }
        }
    }
    return 0;
}
bool compareExeName(char *path, char *partial_name) {
    //This will substract the filename from path and compare it with partial_name
    char *lastSlash = strrchr(path, '\\') + 1;
    if (lastSlash != NULL && strstr(lastSlash, partial_name) == lastSlash) return 1;
    return 0;
}

void unload_all_dll(char *dll_name) {
    DWORD process_ids[2048];
    DWORD process_byte_size;            //size of filled process_ids in BYTES (after the call)
    DWORD process_count;        //count of all elements in process_ids
    HMODULE  ext_dll_module;
    HANDLE opened_process;
    HANDLE Hthread;
    DWORD thread_exit_code = 1;
    CHAR exe_path[1024];

    if (EnumProcesses(process_ids, sizeof(process_ids), &process_byte_size)) {
        process_count = process_byte_size / sizeof(DWORD);

        for (int i = 0; i < process_count; i++) {
            thread_exit_code = 0;
            if ((opened_process = OpenProcess(PROCESS_ALL_ACCESS, false, process_ids[i])) == NULL) continue;

            GetModuleFileNameExA(opened_process, 0, exe_path, MAX_PATH);

            if ((ext_dll_module = findModuleOffset(opened_process, dll_name)) != 0) {

                while (thread_exit_code == 0) {
                    if ((Hthread = CreateRemoteThread(opened_process, NULL, 0, (LPTHREAD_START_ROUTINE)GetProcAddress(GetModuleHandleA("kernel32.dll"), "FreeLibrary"), (void*)ext_dll_module, 0, NULL)) == NULL) {
                        cout<<"Process closed meanwhile or dll unloaded";
                        break;  //process has closed meanwhile
                    }
                    while (WaitForSingleObject(Hthread, 1000) == WAIT_TIMEOUT);
                    GetExitCodeThread(Hthread, &thread_exit_code);
                }
                cout << "Dll unloaded from " << exe_path << endl;
            }

        }
    }
}

警告:某些变量名称可能令人困惑(我很着急) 但每次我尝试弹出一个DLL都会崩溃(当然,只有包含指定dll的应用程序)。我测试了所有可能的东西,一切似乎都很好:findModuleOffset返回的模块地址很好(根据进程资源管理器给出的值进行检查)。我没有想到createremotethread或thread_exit_code的返回值是什么,因为应用程序崩溃(它将dll驱逐出来......所以......)。你能救我吗?

1 个答案:

答案 0 :(得分:4)

(从评论中移出)

鉴于目标进程中有线程正在运行卸载的dll中的代码,它们将在dll被释放后立即崩溃 - 毕竟,CPU正在执行的代码页面正在被取消映射!

为了避免这个问题,必须以某种方式通知正在运行的线程,因此它们可以在卸载dll之前终止; Windows提供了许多IPC方法,一种很少使用的方法特别适用于这种情况,即mailslots

当注入dll时,创建的“主”线程将创建一个具有众所周知名称的邮件槽,并定期检查是否有任何消息供他使用。当你想要卸载dll时,不要粗暴地注入一个强力释放dll的线程,只要问你的“内部人”:向邮件槽发一条消息,要求它终止 1

线程将看到邮件槽中有消息,注意可能终止在目标进程内启动的其他线程(可以使用共享原子变量+ WaitForSingleObject),并且当清理时完成后,调用FreeLibraryAndExitThread自杀最后一个线程和dll。

备注

  1. 邮件槽的一个特别有趣的特点是,如果它们是使用相同的名称多次创建的,发送到这个名称的邮件将被传递到所有它们,所以如果看起来如此,你想同时关闭所有注入的dll,这大大简化了控制程序 - 甚至不需要枚举正在运行的进程。