新建进程时对进程进行DLL注入的问题

贪食蛇男 2011-10-28 03:07:53
模块工作流程如下:
1. 先用CBT HOOK将我的DLL(记作my.dll)注入到一个进程(记做进程A)中
2. HOOK 进程A的CreateProcessInternalW
3. 在 CreateProcessInternalW 的处理中将参数加 SUSPEND 将要创建的进程(记做进程B)的主线程挂起
4. 使用远程线程(记做线程A)将 my.dll 注入进进程B

my.dll 里这么写:
1. 线程A(即DllMain的线程)创建一个线程(记做线程B)
2. 线程B加载其他需要的DLL
3. 线程B看是否需要恢复进程B的主线程的运行,如果需要,就打开进程B的主线程,调用 ResumeThread以恢复B进程的正常执行

如果一切正常的话,此举会构成一张HOOK网,以监视系统中大部分进程(除一些服务和比较底层的进程)

问题来了:
32位WIN7上没有什么问题。每个进程新创建后马上被HOOK,然后才开始执行
XP 上大部分进程也正常,但有少量进程(包括notepad.exe)在线程B调用完 ResumeThread 后(并且恢复运行成功,调试表明进程B的挂起的线程开始执行了)很快就死掉了。

我百思不得其解,有谁做过类似的东西,希望给予指教
...全文
757 20 打赏 收藏 转发到动态 举报
写回复
用AI写文章
20 条回复
切换为时间正序
请发表友善的回复…
发表回复
贪食蛇男 2011-10-29
  • 打赏
  • 举报
回复
好,看起来可行性比较高,也比写SHELLCODE靠谱。
以前没用过这个,研究下。
谢谢列宁
贪食蛇男 2011-10-29
  • 打赏
  • 举报
回复
现在工程已经建立并有一定规模了,我看下能改用QueueUserAPC注入不。
需要考虑64位兼容性
贪食蛇男 2011-10-29
  • 打赏
  • 举报
回复
太好了,谢谢!
刚才测试了下,显然比注入和手写SHELLCODE稳定性要好的多,微软提供的API,兼容性应该没得说。
Lactoferrin 2011-10-28
  • 打赏
  • 举报
回复
我不是说了用apc注入更好吗
贪食蛇男 2011-10-28
  • 打赏
  • 举报
回复
我想监控一些进程的活动,这些进程可能是控制台程序,无法通过全局消息钩子注入
只能出此下策。
请问对于此需求,还有什么好的办法吗
Lactoferrin 2011-10-28
  • 打赏
  • 举报
回复
为何一定要远程线程来注入dll
贪食蛇男 2011-10-28
  • 打赏
  • 举报
回复
说的好,看起来的确如此。
看来不能直接使用 LoadLibraryW 做为线程函数,应该自己构造 SHELLCODE
我再试下。
[Quote=引用 13 楼 lactoferrin 的回复:]

初步想法是线程过程返回后要做清理工作,但是你的代码中运行环境可能还没初始化好
这是封装线程过程的函数,CreateRemoteThread出来的线程先执行它,由它执行ThreadProc

VOID
BaseThreadStart(
IN LPTHREAD_START_ROUTINE lpStartAddress,
IN LPVOID lpParameter
……
[/Quote]
Lactoferrin 2011-10-28
  • 打赏
  • 举报
回复
初步想法是线程过程返回后要做清理工作,但是你的代码中运行环境可能还没初始化好
这是封装线程过程的函数,CreateRemoteThread出来的线程先执行它,由它执行ThreadProc

VOID
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注入则不会有这种问题
贪食蛇男 2011-10-28
  • 打赏
  • 举报
回复
谢谢列宁先生,分会给你的。
但我还有想和你讨论的,我刚才和别的做过这个的交流过,交流的结果发现,如果注入的远程线程不退出就没事。
对于这个结论也只是用实践证明的,没有理论依据。下面我贴出代码,你有兴趣的话可以试一下,如果能深穷出原因来,那就更好不过了。
我直接注入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;
}

Lactoferrin 2011-10-28
  • 打赏
  • 举报
回复
发了ss
speacegenaul 2011-10-28
  • 打赏
  • 举报
回复
给我分啊
贪食蛇男 2011-10-28
  • 打赏
  • 举报
回复
hiroyukki@126.com
谢谢
[Quote=引用 8 楼 lactoferrin 的回复:]

注入dll的方式就用我说的QueueUserAPC
关于api hook我有个修改函数开头指令的,不过解决了多线程重入的问题,如果你要留下邮箱
[/Quote]
Lactoferrin 2011-10-28
  • 打赏
  • 举报
回复
注入dll的方式就用我说的QueueUserAPC
关于api hook我有个修改函数开头指令的,不过解决了多线程重入的问题,如果你要留下邮箱
贪食蛇男 2011-10-28
  • 打赏
  • 举报
回复
请问有没有别的变通方式,因为我的模块注入后要做API HOOK,如果目标进程中的线程状态不确定,我怕会造成我改写的API首部指令正被别的线程执行的问题。
于是我就采有HOOK父进程的创建进程的函数用来第一时间注入。
easyhook 是使用创建挂起进程的方式
detours 是使用创建挂起进程后修改导入表的方式。
还有别的方式吗?
[Quote=引用 6 楼 lactoferrin 的回复:]

进程的很多初始化工作是主线程开始执行LdrInitializeThunk中完成的,在ResumeThread(pi.hThread);之后
在ResumeThread(pi.hThread);之前那个进程很多东西都没准备好,包括kernel32.dll都没加载,所以我不建议使用CreateRemoteThread注入
[/Quote]
Lactoferrin 2011-10-28
  • 打赏
  • 举报
回复
进程的很多初始化工作是主线程开始执行LdrInitializeThunk中完成的,在ResumeThread(pi.hThread);之后
在ResumeThread(pi.hThread);之前那个进程很多东西都没准备好,包括kernel32.dll都没加载,所以我不建议使用CreateRemoteThread注入
jamseyang 2011-10-28
  • 打赏
  • 举报
回复
顶起!
贪食蛇男 2011-10-28
  • 打赏
  • 举报
回复
即是说,只要向一个挂起的进程创建了远程线程,就会有这个问题。
但我不能理解为什么只有部分进程会死掉。
经过我调试, notepad.exe 是在 NtUserCreateWindowEx 返回0时主动退出的进程。
我觉得这也非问题所在,很可能有别的进程以别的方式产生问题。
贪食蛇男 2011-10-28
  • 打赏
  • 举报
回复
列宁先生,我痛苦地发现,这应该不是我的DLL的问题。换言之,我的DLL在这个注入方面很可能没有问题(因为它在WIN7上运行良好),我极度压缩了对此问题的描述,悲剧地发现,很可能是XP远程线程机制我不够了解。
我把极度压缩后的问题编码如下:

[Quote=引用 2 楼 lactoferrin 的回复:]

pi.hThread就是那个PROCESS_INFORMATION.hThread
[/Quote]

#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;
}

Lactoferrin 2011-10-28
  • 打赏
  • 举报
回复
pi.hThread就是那个PROCESS_INFORMATION.hThread
Lactoferrin 2011-10-28
  • 打赏
  • 举报
回复
线程B加载其他需要的DLL,这是什么意思?
如果是创建进程时注入dll,不要用远程线程
在hook过的CreateProcessInternalW中
QueueUserAPC(LoadLibraryW,pi.hThread,dllname);
ResumeThread(pi.hThread);即可注入

15,471

社区成员

发帖
与我相关
我的任务
社区描述
VC/MFC 进程/线程/DLL
社区管理员
  • 进程/线程/DLL社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧