| 传统的远程进程控制方式有利用HOOK技术注入DLL,和利用远线程在目标进程中建立新的执行线程的方式. 远线程不被win9x所支持,而hook技术会对目标进程性能造成一定的影响.并具可以通过枚举消息链的方式发现. 本文给出一种动态修改目标进程代码,注入DLL到目标进程的方法,效率高,不需要额外线程.缺点是使用难度大于上面二种办法,并且修改目标代码的方法,受到编译器的影响.使用不同的编译器时,需要根据编译器调整动态代码修改方式. 动态修改目标进程代码,使其加载自已的DLL的思路如下: 1. 得到目标进程句柄. 2. 在目标进程中分配一块可执行可读写的内存. 3. 写入加载DLL的代码到分配出来的内存中. 4. 修正动态写入的代码中的地址(LoadLibrary地址,DLL名字地址). 5. 修改目标进程本身的代码,使其执行到被修改代码时,跳到加载DLL的代码处. 6. 加载完DLL后,修正目标进程原来的代码,并跳回去继续执行. 这个过程中需要注意的地方是. 对寄存器的保护,堆栈的平衡. 其中第5步需要修改一块目标进程一定会执行的代码,消息循环是个不错的地方.这里以记事本为例. 打开ollydbg,在TranslateMessage函数下断点,中断后如下: 010029FC |. /75 14 |jnz short notepad.01002A12 010029FE |. |8D45 E0 |lea eax,dword ptr ss:[ebp-20] 01002A01 |. |50 |push eax ; /pMsg 01002A02 |. |FF15 98120001 |call dword ptr ds:[<&USER32.Translat>; \TranslateMessage 01002A08 |. |8D45 E0 |lea eax,dword ptr ss:[ebp-20] 01002A0B |. |50 |push eax ; /pMsg 01002A0C |. |FF15 94120001 |call dword ptr ds:[<&USER32.Dispatch>; \DispatchMessageW 01002A12 |> \56 push esi 01002A13 |. 56 |push esi 01002A14 |. 8D45 E0 |lea eax,dword ptr ss:[ebp-20] 01002A17 |. 56 |push esi 01002A18 |. 50 |push eax 01002A19 |. FFD7 |call edi 0x01002A02处是个不错的地方,可以动态修改它. 改成 mov eax, 加载dll的代码地址 jmp eax 需要注意的是,此程序基址在不同的系统上有可能不一样,可以从PE头中得到映像基址.所以此处的0x1002A02也许在你的机器上是另一个地址,请使用OD自行确定.(我是winxp sp2) 找到修改点后,然后就是分配可执行可读写内存,写入下面一段代码进去 pusha //eax中是LoadLibrary函数地址,暂时为0xffffffff //由程序动态改成真实函数地址 mov eax, 0xffffffff //要加载的DLL的名字地址,暂时为0xffffffff //由程序动态改成真实地址 mov ebx, 0xffffffff push ebx //调用LoadLibrary,加载自已的DLL //这样DLL就注入到目标进程了 call eax popa //恢复 0x1002A02处的代码,并跳回去执行 //选执eax寄存器是因为0x1002A02后面的代码没有直接使用到eax mov eax, CODE_OFFSET mov dword ptr [eax], 0x129815FF mov dword ptr [eax+4], 0x458D0100 jmp eax 下面的代码是在加载DLL后,修正目标进程原有的代码 mov eax, CODE_OFFSET mov dword ptr [eax], 0x129815FF mov dword ptr [eax+4], 0x458D0100 0x129815FF 0x458D0100 即为原有代码(字节顺序是倒过来的, 0x129815FF 倒过来即是 FF159812, 0x458D0100倒过来即是00018D45,请注意和下面代码对比 01002A02 |. |FF15 98120001 |call dword ptr ds:[<&USER32.Translat>; \TranslateMessage 01002A08 |. |8D45 E0 |lea eax,dword ptr ss:[ebp-20] 其它基本上就是编码了,例子如下,测试时,请自行编写一个 Test.dll ,放在记事本同一个目录下,不同的编译器,不同的系统请自行修正一些细节:(我是使用的Release模式,Debug模式代码会不同的) #i nclude #i nclude #i nclude using std::cout; using std::cin; using std::endl; //记事本的映象基址,不同的系统可能不一样 //这个值可以从PE头中读取 #define NOTEPAD_IMAGE_BASE 0x1000000; #define CODE_OFFSET 0x1002A02; //用来测试的DLL名 const char *dllname = "Test.dll"; void loaddll() { __asm { pusha //eax中是LoadLibrary函数地址,暂时为0xffffffff //由程序动态改成真实函数地址 mov eax, 0xffffffff //要加载的DLL的名字地址,暂时为0xffffffff //由程序动态改成真实地址 mov ebx, 0xffffffff push ebx //调用LoadLibrary,加载自已的DLL //这样DLL就注入到目标进程了 call eax popa //恢复 0x1002A02处的代码,并跳回去执行 //选执eax寄存器是因为0x1002A02后面的代码没有直接使用到eax mov eax, CODE_OFFSET mov dword ptr [eax], 0x129815FF mov dword ptr [eax+4], 0x458D0100 jmp eax } } #pragma pack(push) //保存对齐状态 #pragma pack(1) //设定为1字节对齐 struct JmpCode { byte op; char* address; WORD jmp; }; #pragma pack(pop) int main() { //找记事本主窗口句柄 HWND wnd = FindWindow(NULL, "无标题 - 记事本"); if (!IsWindow(wnd)) { cout << "记事窗口没找到" << std::endl; return 1; } //读取进程ID,和进程句柄 DWORD pid; HANDLE ph; GetWindowThreadProcessId(wnd, &pid); ph = OpenProcess(PROCESS_ALL_ACCESS, false, pid); //在记事本进程中分配一页可读写可执行的虚拟内存 void *address = VirtualAllocEx(ph, NULL, 4096, MEM_COMMIT, PAGE_EXECUTE_READWRITE); //写代码的地址(写在字符串后面) char *code = (char*)address + strlen(dllname) + 1; if (NULL == address) { cout << "分配远端内存失败" << std::endl; return 1; } DWORD ws; //把代码写到内存页中 WriteProcessMemory(ph, code, &loaddll, 4000, &ws); //把DLL名写到内存页中 WriteProcessMemory(ph, address, dllname, strlen(dllname), &ws); //把loadlibrary的地址写到eax 0xfffffff处,把0xffffffff替换为真正的地址 //code+9为eax 0xffffffff中0xffffffff处的偏移,通过反汇编工具查看+ 9是为了跳过 //编译器给loadll函数生成的框架 HMODULE module = LoadLibrary("kernel32.dll"); FARPROC pro = GetProcAddress(module, "LoadLibraryA"); WriteProcessMemory(ph, code + 9, &pro, 4, &ws); //把dllname的地址写到ebx 0xffffffff处,把dllname作为参数 WriteProcessMemory(ph, code + 14, &address, 4, &ws); //修改记事本消息循环处的代码. JmpCode jmp; jmp.op = 0xB8; jmp.address = code + 6; //这段代码是 mov eax, 地址 jmp.jmp = 0xE0FF; //jmp eax address = (void*)CODE_OFFSET; //让代码段可读写 VirtualProtectEx(ph, address, 4096, PAGE_EXECUTE_READWRITE, &ws); //改写目标处的代码,让其跳到自已的代码处执行 WriteProcessMemory(ph, address, &jmp, 8, &ws); return 0; } 运行,测试,OK! Test.dll被正确加载,目标进程现在是你的了,随便玩吧. |

浙公网安备 33010602011771号