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

传统的远程进程控制方式有利用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模式代码会不同的)

#include <iostream>
#include <windows.h>
#include <psapi.h>

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被正确加载,目标进程现在是你的了,随便玩吧.
 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值