模块工作流程如下:
1. 先用CBT HOOK将我的DLL(记作my.dll)注入到一个进程(记做进程A)中
2. HOOK 进程A的CreateProcessInternalW
3. 在 CreateProcessInternalW 的处理中将参数加 SUSPEND 将要创建的进程(记做进程B)的主线程挂起
4. 使用远程线程(记做线程A)将 my.dll 注入进进程Bmy.dll 里这么写:
1. 线程A(即DllMain的线程)创建一个线程(记做线程B)
2. 线程B加载其他需要的DLL
3. 线程B看是否需要恢复进程B的主线程的运行,如果需要,就打开进程B的主线程,调用 ResumeThread以恢复B进程的正常执行如果一切正常的话,此举会构成一张HOOK网,以监视系统中大部分进程(除一些服务和比较底层的进程)问题来了:
32位WIN7上没有什么问题。每个进程新创建后马上被HOOK,然后才开始执行
XP 上大部分进程也正常,但有少量进程(包括notepad.exe)在线程B调用完 ResumeThread 后(并且恢复运行成功,调试表明进程B的挂起的线程开始执行了)很快就死掉了。我百思不得其解,有谁做过类似的东西,希望给予指教

解决方案 »

  1.   

    线程B加载其他需要的DLL,这是什么意思?
    如果是创建进程时注入dll,不要用远程线程
    在hook过的CreateProcessInternalW中
    QueueUserAPC(LoadLibraryW,pi.hThread,dllname);
    ResumeThread(pi.hThread);即可注入
      

  2.   

    pi.hThread就是那个PROCESS_INFORMATION.hThread
      

  3.   

    列宁先生,我痛苦地发现,这应该不是我的DLL的问题。换言之,我的DLL在这个注入方面很可能没有问题(因为它在WIN7上运行良好),我极度压缩了对此问题的描述,悲剧地发现,很可能是XP远程线程机制我不够了解。
    我把极度压缩后的问题编码如下:
    #include <stdio.H>
    #include <windows.h>typedef DWORD (*THREAD_PROC)(void*);BOOL WINAPI InjectDllToProcW(DWORD procID, LPCWSTR dll)
    {
        BOOL bRet = FALSE;
        HANDLE hProc = NULL;
        void* remoteMem = 0;
        size_t len = 0;    do
        {
            THREAD_PROC loadLib = (THREAD_PROC)GetProcAddress(GetModuleHandleA("kernel32.dll"), "LoadLibraryW");
            if(!loadLib)
            {
                break;
            }        hProc = OpenProcess(PROCESS_VM_OPERATION | PROCESS_VM_WRITE | PROCESS_CREATE_THREAD | PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, procID);
            if(!hProc)
            {
                break;
            }        len = (wcslen(dll) + 1) * sizeof(wchar_t);
            // 多分配俩字节,为了可能产生的内存对齐用
            remoteMem = VirtualAllocEx(hProc, NULL, len, MEM_COMMIT, PAGE_READWRITE);
            if(!remoteMem)
            {
                break;
            }        char* dllAddr = (char*)remoteMem;
            // 最好是2的整数倍,否则在WIN7上有平台兼容性问题
            if((int)dllAddr % 2)
            {
                dllAddr++;
            }        if(!WriteProcessMemory(hProc, dllAddr, (void*)dll, len, NULL))
            {
                break;
            }        DWORD threadID = 0;
            HANDLE hThread = CreateRemoteThread(hProc,
                                                NULL,
                                                0,
                                                (LPTHREAD_START_ROUTINE)loadLib,
                                                dllAddr,
                                                CREATE_SUSPENDED,
                                                &threadID);
                                                
            if(!hThread)
            {
                break;
            }
            ResumeThread(hThread);
            CloseHandle(hThread);        bRet = TRUE;
        } while (FALSE);    if(hProc)
        {
            CloseHandle(hProc);
        }
        return bRet;
    }int main()
    {
        STARTUPINFO si = {0};
        si.cb = sizeof(si);
        si.wShowWindow = SW_SHOW;
        si.dwFlags = STARTF_USESHOWWINDOW;
        
        PROCESS_INFORMATION pi = {0};
        CreateProcess(NULL, "C:\\windows\\system32\\notepad.exe", NULL, NULL, FALSE, CREATE_SUSPENDED, NULL, NULL, &si, &pi);
        
        // 注入不存在的都会有问题,看来是 CreateRemoteThread 本身的机制问题,不创建远程线程就没事
        // if(InjectDllToProcW(pi.dwProcessId, L"a"))
        if(InjectDllToProcW(pi.dwProcessId, L"C:\\windows\\system32\\kernel32.dll"))
        {
            printf("注入成功\n");
            getchar();
        }
        else
        {
            printf("注入失败\n");
        }
        
        
        ResumeThread(pi.hThread);
        printf("%d\n", GetLastError());
        return 0;
    }
      

  4.   

    即是说,只要向一个挂起的进程创建了远程线程,就会有这个问题。
    但我不能理解为什么只有部分进程会死掉。
    经过我调试, notepad.exe 是在 NtUserCreateWindowEx 返回0时主动退出的进程。
    我觉得这也非问题所在,很可能有别的进程以别的方式产生问题。
      

  5.   

    进程的很多初始化工作是主线程开始执行LdrInitializeThunk中完成的,在ResumeThread(pi.hThread);之后
    在ResumeThread(pi.hThread);之前那个进程很多东西都没准备好,包括kernel32.dll都没加载,所以我不建议使用CreateRemoteThread注入
      

  6.   

    请问有没有别的变通方式,因为我的模块注入后要做API HOOK,如果目标进程中的线程状态不确定,我怕会造成我改写的API首部指令正被别的线程执行的问题。
    于是我就采有HOOK父进程的创建进程的函数用来第一时间注入。
    easyhook 是使用创建挂起进程的方式
    detours 是使用创建挂起进程后修改导入表的方式。
    还有别的方式吗?
      

  7.   

    注入dll的方式就用我说的QueueUserAPC
    关于api hook我有个修改函数开头指令的,不过解决了多线程重入的问题,如果你要留下邮箱
      

  8.   

    谢谢列宁先生,分会给你的。
    但我还有想和你讨论的,我刚才和别的做过这个的交流过,交流的结果发现,如果注入的远程线程不退出就没事。
    对于这个结论也只是用实践证明的,没有理论依据。下面我贴出代码,你有兴趣的话可以试一下,如果能深穷出原因来,那就更好不过了。
    我直接注入SHELLCODE运行,分别用一个死循环和一个返回指令做为远程线程函数的函数体。死循环情况下,进程成功启动。#include <stdio.H>
    #include <windows.h>static DWORD gs_mainThreadId = 0;BOOL WINAPI InjectProcW(DWORD procID)
    {
        BOOL bRet = FALSE;
        HANDLE hProc = NULL;
        void* remoteMem = 0;    do
        {
            hProc = OpenProcess(PROCESS_VM_OPERATION | PROCESS_VM_WRITE | PROCESS_CREATE_THREAD | PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, procID);
            if(!hProc)
            {
                break;
            }        remoteMem = VirtualAllocEx(hProc, NULL, 3, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
            if(!remoteMem)
            {
                break;
            }
            // 死循环
            byte ttt[3] = {0x90, 0xeb, 0xFe};
            // retn 4
            // byte ttt[3] = {0xc2, 0x04, 0x00};
            if(!WriteProcessMemory(hProc, remoteMem, (void*)ttt, 3, NULL))
            {
                break;
            }        HANDLE hThread = CreateRemoteThread(hProc,
                NULL,
                0,
                (LPTHREAD_START_ROUTINE)remoteMem,
                0,
                CREATE_SUSPENDED,
                NULL);        if(!hThread)
            {
                break;
            }        ResumeThread(hThread);
            CloseHandle(hThread);
            bRet = TRUE;
        } while (FALSE);    if(hProc)
        {
            CloseHandle(hProc);
        }
        return bRet;
    }int main()
    {
        STARTUPINFOA si = {0};
        si.cb = sizeof(si);
        si.wShowWindow = SW_SHOW;
        si.dwFlags = STARTF_USESHOWWINDOW;    PROCESS_INFORMATION pi = {0};
        CreateProcessA(NULL, "C:\\windows\\system32\\notepad.exe", NULL, NULL, FALSE, CREATE_SUSPENDED, NULL, NULL, &si, &pi);    gs_mainThreadId = pi.dwProcessId;
        if(InjectProcW(pi.dwProcessId))
        {
            printf("注入成功\n");
        }
        else
        {
            printf("注入失败\n");
        }    ResumeThread(pi.hThread);
        return 0;
    }
      

  9.   

    初步想法是线程过程返回后要做清理工作,但是你的代码中运行环境可能还没初始化好
    这是封装线程过程的函数,CreateRemoteThread出来的线程先执行它,由它执行ThreadProcVOID
    BaseThreadStart(
        IN LPTHREAD_START_ROUTINE lpStartAddress,
        IN LPVOID lpParameter
        )/*++Routine Description:    This function is called to start a Win32 thread. Its purpose
        is to call the thread, and if the thread returns, to terminate
        the thread and delete it's stack.Arguments:    lpStartAddress - Supplies the starting address of the new thread.  The
            address is logically a procedure that never returns and that
            accepts a single 32-bit pointer argument.    lpParameter - Supplies a single parameter value passed to the thread.Return Value:    None.--*/{
        try {        //
            // test for fiber start or new thread
            //        //
            // WARNING WARNING DO NOT CHANGE INIT OF NtTib.Version. There is
            // external code depending on this initialization !
            //
            if ( NtCurrentTeb()->NtTib.Version == OS2_VERSION ) {
                if ( !BaseRunningInServerProcess ) {
                    CsrNewThread();
                    }
                }
            ExitThread((lpStartAddress)(lpParameter));
            }
        except(UnhandledExceptionFilter( GetExceptionInformation() )) {
            if ( !BaseRunningInServerProcess ) {
                ExitProcess(GetExceptionCode());
                }
            else {
                ExitThread(GetExceptionCode());
                }
            }
    }后面ExitThread的正确运行需要进程的主线程先调用完成LdrInitializeThunk,比如它要遍历模块,而模块链表是由主线程建立的,你的代码中可能在初始化完成前线程就返回
    如果用QueueUserAPC注入则不会有这种问题
      

  10.   

    说的好,看起来的确如此。
    看来不能直接使用 LoadLibraryW 做为线程函数,应该自己构造 SHELLCODE
    我再试下。
      

  11.   

    为何一定要远程线程来注入dll
      

  12.   

    现在工程已经建立并有一定规模了,我看下能改用QueueUserAPC注入不。
    需要考虑64位兼容性
      

  13.   

    好,看起来可行性比较高,也比写SHELLCODE靠谱。
    以前没用过这个,研究下。
    谢谢列宁
      

  14.   

    太好了,谢谢!
    刚才测试了下,显然比注入和手写SHELLCODE稳定性要好的多,微软提供的API,兼容性应该没得说。