模块工作流程如下:
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. 先用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的挂起的线程开始执行了)很快就死掉了。我百思不得其解,有谁做过类似的东西,希望给予指教
如果是创建进程时注入dll,不要用远程线程
在hook过的CreateProcessInternalW中
QueueUserAPC(LoadLibraryW,pi.hThread,dllname);
ResumeThread(pi.hThread);即可注入
我把极度压缩后的问题编码如下:
#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;
}
但我不能理解为什么只有部分进程会死掉。
经过我调试, notepad.exe 是在 NtUserCreateWindowEx 返回0时主动退出的进程。
我觉得这也非问题所在,很可能有别的进程以别的方式产生问题。
在ResumeThread(pi.hThread);之前那个进程很多东西都没准备好,包括kernel32.dll都没加载,所以我不建议使用CreateRemoteThread注入
于是我就采有HOOK父进程的创建进程的函数用来第一时间注入。
easyhook 是使用创建挂起进程的方式
detours 是使用创建挂起进程后修改导入表的方式。
还有别的方式吗?
关于api hook我有个修改函数开头指令的,不过解决了多线程重入的问题,如果你要留下邮箱
但我还有想和你讨论的,我刚才和别的做过这个的交流过,交流的结果发现,如果注入的远程线程不退出就没事。
对于这个结论也只是用实践证明的,没有理论依据。下面我贴出代码,你有兴趣的话可以试一下,如果能深穷出原因来,那就更好不过了。
我直接注入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;
}
这是封装线程过程的函数,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注入则不会有这种问题
看来不能直接使用 LoadLibraryW 做为线程函数,应该自己构造 SHELLCODE
我再试下。
需要考虑64位兼容性
以前没用过这个,研究下。
谢谢列宁
刚才测试了下,显然比注入和手写SHELLCODE稳定性要好的多,微软提供的API,兼容性应该没得说。