由远程线程注入代码引出来的一些问题

        昨天小平同学在写一个远程线程注入代码的程序,他注入成功了,能弹出对话框,但是点击确定之后被注入的程序会崩溃掉,我记得自己以前写过是没有问题,但是我是拿VC6写的,调试了一下他的程序,发现原因是向目标进程地址空间中writeprocessmemory的时候,把call checkesp也写进去了,这玩意就是用来检验ESP在函数体中是否被改变,但是这些检验函数的地址在不同PE文件中有可能是不一样的。我在VC6中写的时候直接关掉/gz编译选项就OK了,我发现在VS08中没有这一选项,但有一个缓冲区安全检查的选项,我以为这个就是类似GZ,关掉之后发现还是不行,照样会生成checkesp的代码,在naked函数中也是同样,后来在微软的在线MSDN上查到原来是在05之后将GZ换为了/rtc,将这个关掉之后就OK了。


远程加载与卸载DLL看我这篇文章

http://blog.csdn.net/evi10r/article/details/6724874



总结下远程注入代码需要注意的一些东西:
(1) 远程线程函数体不得使用kernel32.dll,user32.dll以外的函数。因为这个两个模块在各个进程的相对地址是一样的,如果一定要使用其它函数,则必须将函数体写入目标进程空间,其实就是两个进程地址空间中必须加载了同一个DLL并且基址相同。
(2) 不能使用任何字符串常量,因为字符串常量是存放在PE文件里.data这个段里面的,函数里保存的只是相对地址。(片面的)
(3) 去掉编译器的/GZ编译选项,这个选项是用来Enable Stack Frame Run-Time Error Checking。当这个选项打开时,编译器会在每个函数中加入一些代码,用来检验ESP在函数体中是否被改变,但是这些检验函数的地址在不同PE文件中有可能是不一样的。这个编译选项是在VC6里面的,/GZ 在 Visual C++ 2005 中已不推荐使用,微软推荐使用 /RTC(运行时错误检查)。
(4) 不得使用增量链接(incremental linking)。增量链接是编译器为了减少链接时间做的处理,把函数体用一个JMP指令代替,这样就可以随意改变函数的内容,而不用修改CALL指令。
(5) 不要在函数体内使用超过4kb的局部变量。当局部变量的大小超过4kb时,栈指针并不直接改版,而是调用另一个函数来分配内存,这个函数有可能在不同进程中的地址不一样。
(6) 函数体内switch语句中的case不要超过3个,否则编译器会在PE文件中使用跳转表,而这个跳转表有可能在目标进程中并不存在。



vs2008
debug编译:默认启用增量链接  基本运行时检查是两者都选(栈帧,未初始化变量)
release编译:不启用增量链接  基本运行时检查是默认(应该是都不选)


关于增量链接:
可以看我以前的一篇文章
http://blog.csdn.net/evi10r/article/details/6978016
这里再补充点, 静态的全局函数是不会有增量链接的,call了之后直接就到函数代码处,不会有JMP过渡。并且在release模式下,会直接把静态全局函数内联。(我调试的是这样的,不知道会不会出现其他情况)。所以在计算函数大小的时候可以声明两个静态全局函数,直接相减,在release模式下得到的就是确定的函数大小。但是debug模式下并没有,还是call的形式,并且两个函数之间有很多cc。


还有跟.textbss段的关系
    Incremental Linking作用不仅仅是在于减少我们重新连接程序所需要的时间,他还是我们调试时能够动态改动代码的前提。调试代码时直接修改了代码,然后VS直接在调试的时候将你的改动反映到程序里去了。这就是VS在Debug模式时动态编译代码的功能。
    实际上这个功能是基于Incremental Linking机制的,而且是使用的Incremental Linking的第二套方案——直接找个大的地儿把修改的函数挪过去。
    .textbss有关键字bss,这就说明实际上这个节没有占据实际的硬盘空间。然后text关键字告诉我们这里段是包含代码的,另外用工具查知这个段有可执行属性更是印证了这个观点。没有代码,那要这个节有啥用呢?
  你想到了么?是的,在VS动态编译的时候,他直接将被修改的函数放到了.textbss节里,然后修改了对应的 ILT表项(这应该是保存各个函数JMP中转的表吧),使他指向这个位置。
    说是简单,但实际上这个过程还个细节需要注意——你把我的函数挪地儿了,要是我正在执行这个函数怎么办?实际上,在改了ILT之后立刻会做的,就是检查当前程序所有线程的TIB,如果他们的EIP指向老的函数(它们正在执行老版本函数),我们就修改EIP使其指向新版本函数的对应位置。当然,这实际上暗示了,这个工作非要在调试程序的帮助下不可了。注意动态编译的功能只在Debug版本程序下有效,Release版本是不行的,因为Release版本默认禁用Incremental Linking。
  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
以下是一段远程线程注入代码的例子: ``` #include <Windows.h> #include <TlHelp32.h> DWORD GetProcessIdByName(const wchar_t* processName) { DWORD processId = 0; HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); if (snapshot != INVALID_HANDLE_VALUE) { PROCESSENTRY32 processEntry = { 0 }; processEntry.dwSize = sizeof(PROCESSENTRY32); if (Process32First(snapshot, &processEntry)) { do { if (_wcsicmp(processEntry.szExeFile, processName) == 0) { processId = processEntry.th32ProcessID; break; } } while (Process32Next(snapshot, &processEntry)); } CloseHandle(snapshot); } return processId; } int main() { const wchar_t* targetProcessName = L"notepad.exe"; const wchar_t* dllPath = L"C:\\path\\to\\mydll.dll"; DWORD targetProcessId = GetProcessIdByName(targetProcessName); if (targetProcessId == 0) { printf("Target process not found.\n"); return 1; } HANDLE targetProcess = OpenProcess(PROCESS_CREATE_THREAD | PROCESS_QUERY_INFORMATION | PROCESS_VM_OPERATION | PROCESS_VM_WRITE | PROCESS_VM_READ, FALSE, targetProcessId); if (targetProcess == NULL) { printf("Failed to open target process.\n"); return 1; } LPVOID dllPathAddress = VirtualAllocEx(targetProcess, NULL, wcslen(dllPath) * sizeof(wchar_t), MEM_COMMIT, PAGE_READWRITE); if (dllPathAddress == NULL) { printf("Failed to allocate memory in target process.\n"); CloseHandle(targetProcess); return 1; } if (!WriteProcessMemory(targetProcess, dllPathAddress, dllPath, wcslen(dllPath) * sizeof(wchar_t), NULL)) { printf("Failed to write to target process memory.\n"); VirtualFreeEx(targetProcess, dllPathAddress, 0, MEM_RELEASE); CloseHandle(targetProcess); return 1; } HMODULE kernel32Module = GetModuleHandle(L"kernel32.dll"); if (kernel32Module == NULL) { printf("Failed to get kernel32 module handle.\n"); VirtualFreeEx(targetProcess, dllPathAddress, 0, MEM_RELEASE); CloseHandle(targetProcess); return 1; } LPVOID loadLibraryAddress = GetProcAddress(kernel32Module, "LoadLibraryW"); if (loadLibraryAddress == NULL) { printf("Failed to get LoadLibraryW address.\n"); VirtualFreeEx(targetProcess, dllPathAddress, 0, MEM_RELEASE); CloseHandle(targetProcess); return 1; } HANDLE remoteThread = CreateRemoteThread(targetProcess, NULL, 0, (LPTHREAD_START_ROUTINE)loadLibraryAddress, dllPathAddress, 0, NULL); if (remoteThread == NULL) { printf("Failed to create remote thread.\n"); VirtualFreeEx(targetProcess, dllPathAddress, 0, MEM_RELEASE); CloseHandle(targetProcess); return 1; } WaitForSingleObject(remoteThread, INFINITE); VirtualFreeEx(targetProcess, dllPathAddress, 0, MEM_RELEASE); CloseHandle(remoteThread); CloseHandle(targetProcess); printf("Injection succeeded.\n"); return 0; } ``` 请注意,这只是一个示例代码,实际使用时需要根据具体情况进行修改和调整。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值