动态修改其它进程的代码,实现DLL注入

原文地址:http://www.cnblogs.com/showna/articles/691763.html

传统的远程进程控制方式有利用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被正确加载,目标进程现在是你的了,随便玩吧.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值