zoukankan      html  css  js  c++  java
  • 劫持正在运行进程的EIP注入代码的方法

    标题】: 劫持正在运行进程的EIP注入代码的方法
    【作者】: 火血狼(QQ:65845743)
    【工具】: VC++2005, WINXP, WIN7
    【声明】: 1.禁止用来做破坏;2.转载请告知作者.
    -----------------------------------------------------------------------------
    【灵感来源】
    近日,在读<<Windows内核编程>>的时候,偶然发现,一个函数GetThreadContext,该函数可以使用户级的 代码访问并操作指定线程的上下文:CONTEXT,通过这个CONTEXT里的一个字段EIP,我们可以得到CPU寄存器的当前值。当时就想,如果通过这 个EIP允许修改,不就可以控制程序流程了吗?查了查资料,果然可以被另外用户态的进程修改,于是做了如下实验,实验目标:劫持EIP,执行自己代码,然 后恢复EIP。

    【第一步】修改另外进程的EIP寄存器
    SuspendThread(hThread);//这里先让线程挂起,避免EIP乱跑
    CONTEXT context;
    context.ContextFlags = CONTEXT_CONTROL;
    GetThreadContext(hThread, &context);
    DWORD dwEIP = context.Eip;
    context.ContextFlags = CONTEXT_CONTROL;
    //
    context.Eip = 0x000000; //这里随便设一个EIP值,导致目标进程崩溃
    SetThreadContext(hThread, &context);
    ResumeThread(hThread);

    通过上面的代码实验,得出结论,EIP的设置是不受限制的。(其中hThread为目标进程的主线程句柄,至于如何得到,很多地方有例子,这里不再普及基础知识)

    【第二步】构建合法的EIP值,引导目标进程EIP进入指定代码
    在进行这一步的时候我遇到了以下几个问题:1.目标进程只能访问自身的虚拟内存地址;2.如何向内存中放入指定代码。
    要解决第一个问题,就要用到
    PVOID pCodeRemote = VirtualAllocEx(hProcess, NULL, (size_t)dwCodeSize, MEM_COMMIT, PAGE_EXECUTE_READWRITE));
    这个代码将在目标进程的虚拟内存里申请一块儿大小为dwCodeSize的鲜活内存空间,并把内存起始指针返回。(并且页权限为可执行,可读写)
    解决第二个问题,要用到汇编啦
    写这样一个函数void __declspec(naked) __stdcall ASM_RemoteFunc(){
    _asm{ int 3 }
    }
    然后把这个函数Copy到刚才的内存中,用到代码
    WriteProcessMemory(hProcess, pCodeRemote, (PVOID)ASM_RemoteFunc, (size_t)dwCodeSize, NULL)
    到这里,又有疑问了,怎么确定dwCodeSize呢?嗯,可以在函数末尾加个特殊值,然后查找到这个值,就可以确定函数的末尾地址了,嘿嘿,来试试
    (naked修饰这里也不解释,请读者自行查资料)
    void __declspec(naked) __stdcall ASM_RemoteFunc(){
    _asm{ int 3; push 0x12345679 }
    }

    这样写搜索代码
    void* find_ptr(void* mem, DWORD dwv)
    {
    void* ret_ptr;
    __asm
    {
    mov eax, mem
    jmp comp
    diff: inc eax
    comp: mov ebx, [eax]
    cmp ebx, dwv
    jnz diff
    mov ret_ptr, eax
    }
    return ret_ptr;
    }
    最后,函数大小可以通过下面代码来计算:
    DWORD dwCodeStart = (DWORD)ASM_RemoteFunc; PVOID ptrCodeLocal = (PVOID)dwCodeStart; DWORD dwCodeEnd = (DWORD)find_ptr(ptrCodeLocal, PLACE_HOLDER_END) + 4; DWORD dwCodeSize = dwCodeEnd - dwCodeStart;
    好了,第二部问题解决了,实验一下,果然,目标进程产生中断异常,说明执行了指定代码,但是最终程序还是会崩溃。如何能让程序不崩溃呢?

    【第三步】寄存器和堆栈恢复
    先分析一下程序为啥崩溃:因为我们改变EIP的时候,其代码有可能处于任何位置,执行完我们的代码后,并没有恢复原来的EIP指针,也没有保护好各个寄存器的值,目标进程会出现不可预计的现象。
    如何恢复EIP呢,写过shellcode的人都知道,ret可以做到这一点,于是我们先push当前的EIP,然后,再结束的时候ret,就会返回到原来的地方执行EIP啦,于是这样写:
    void __declspec(naked) __stdcall ASM_RemoteFunc(){
    _asm{
    push 0x12345670
    ret
    push 0x12345679
    }
    }
    呵呵,有人奇怪了,为啥用0x12345670而不用真正的EIP呢,因为这会儿我们无法得到,运行的时候才有。那怎么办呢?不用急,我们用找函数大小的方法找到0x12345670的地址,然后把目标进程的当前EIP,写入,不就行啦。
    void * placeHolderEIP = find_ptr(ptrCodeLocal, 0x12345670);
    memcpy((void *)placeHolderEIP, &dwEIP, 4);
    运行,安静的通过,哈哈。
    下面保护寄存器,并且调用一些有意思的代码:
    #define PLACE_HOLDER_EIP 0x12345670
    #define PLACE_HOLDER_ST1 0x12345671
    #define PLACE_HOLDER_ST2 0x12345672
    #define PLACE_HOLDER_FUN 0x12345678
    #define PLACE_HOLDER_END 0x12345679
    void __declspec(naked) __stdcall ASM_RemoteFunc(){
    _asm{
    push PLACE_HOLDER_EIP;
    pushfd;
    pushad;
    push MB_OK | MB_ICONINFORMATION
    push PLACE_HOLDER_ST1;
    push PLACE_HOLDER_ST2;
    push NULL
    mov eax, PLACE_HOLDER_FUN;
    call eax;
    popad;
    popfd;
    ret;
    push PLACE_HOLDER_END
    }
    }

    按照同样的内存查找的方法,把指定地方放入自己的值:
    HMODULE hModule = 0;
    if (!(hModule = LoadLibrary(_T("User32.dll")))) return false;
    DWORD funRemote = 0;
    if (!(funRemote = (DWORD)GetProcAddress(hModule, "MessageBoxA"))) return false;

    PVOID strRemote1 = NULL;
    if (!(strRemote1 = VirtualAllocEx(hProcess, NULL, (size_t)(strlen(strPam1) + 1), MEM_COMMIT, PAGE_READWRITE))) return false;
    PVOID strRemote2 = NULL;
    if (!(strRemote2 = VirtualAllocEx(hProcess, NULL, (size_t)(strlen(strPam2) + 1), MEM_COMMIT, PAGE_READWRITE))) return false;
    PVOID pCodeRemote = NULL;
    if (!(pCodeRemote = VirtualAllocEx(hProcess, NULL, (size_t)dwCodeSize, MEM_COMMIT, PAGE_EXECUTE_READWRITE))) return false;
    void * placeHolderEIP = find_ptr(ptrCodeLocal, PLACE_HOLDER_EIP);
    void * placeHolderST1 = find_ptr(ptrCodeLocal, PLACE_HOLDER_ST1);
    void * placeHolderST2 = find_ptr(ptrCodeLocal, PLACE_HOLDER_ST2);
    void * placeHolderFUN = find_ptr(ptrCodeLocal, PLACE_HOLDER_FUN);
    memcpy((void *)placeHolderEIP, &dwEIP, 4);
    memcpy((void *)placeHolderST1, &strRemote1, 4);
    memcpy((void *)placeHolderST2, &strRemote2, 4);
    memcpy((void *)placeHolderFUN, &funRemote, 4);

    最后,把自己的函数复制到目标进程
    if (!WriteProcessMemory(hProcess, strRemote1, (LPCVOID)strPam1, strlen(strPam1), NULL)) return false;
    if (!WriteProcessMemory(hProcess, strRemote2, (LPCVOID)strPam2, strlen(strPam2), NULL)) return false;
    if (!WriteProcessMemory(hProcess, pCodeRemote, ptrCodeLocal, (size_t)dwCodeSize, NULL)) return false;
    然后,ResumeThread(hThread),弹出messagebox,标题是strRemote1,内容为strRemote2指定的字符串值。
    点ok后,目标进程安全无恙。大功告成!!

    【结论】 EIP就是一切!!
  • 相关阅读:
    github添加版本号
    centOS 7联网
    运用session来控制用户的异地登录被挤下线情况
    寻找节点d=n的节点算法
    基于Seajs的可控台球碰撞游戏
    RequireJs 源码解读及思考
    BackBone 源码解读及思考
    关于「远程兼职」
    cocos2d-html5 中的性能优化
    细说移动前端Android联调
  • 原文地址:https://www.cnblogs.com/hgy413/p/3693484.html
Copyright © 2011-2022 走看看