外国有哪些达到“魔改”级别的武器装备?

飞舞 飞舞
回答
  • 馒头没有馅儿 馒头没有馅儿

    当执行完我们自定义的hook函数之后,又从被hook函数的首部开始执行,被hook函数一进入就被跳转了。而本文,则要实现在某个函数体内部任意地方进行hook并跳转,执行完我们的函数之后,再回到原来的位置继续向下执行完未执行的逻辑。那么,初看这种方式似乎与前面写的两篇hook没有什么差别,都是hook,都是跳转然后回到被hook的函数。但仔细一想,你会发现本文要实现的方式要比前面两种hook复杂,因为hook的地方是函数体内任意地方,那么回来的时候就不是直接调用被hook的函数了,而是要回到之前hook的地方去。这期间就涉及到hook函数的返回地址问题和被hook函数的返回地址问题。说了这么多,可能还是有点晕,先不管为什么要这么做,也不管这种hook方式能有什么用途(在本文最后会说明用途),下面我们先写一些代码,在实践中来想一想这种方式有什么用途,并且与之前的两篇hook进行比较。首先,我们需要一个自定义的hook函数,这个函数也就是被hook函数被hook后跳转到的地方,这个hook函数负责hook与unhook,还可以监视寄存器,监视内存,也可以管理hook的次数,以供我们灵活的hook需求。直接贴代码吧:
    [cpp]view plaincopyprint?include
    include
    pragma warning(d**able:4311)
    pragma warning(d**able:4312)
    define hook_bytes 5
    typedef unsigned int uint;uint hookaddr=0;char old_code[hook_bytes];char new_code[hook_bytes];void printreg**ters(void);bool hook(void)
    {
    dword dwflag;if(virtualprotect((void*)hookaddr,hook_bytes,page_execute_readwrite,&dwflag))
    {
    memcpy(old_code,(void*)hookaddr,hook_bytes);memcpy((void*)hookaddr,new_code,hook_bytes);virtualprotect((void*)hookaddr,hook_bytes,dwflag,&dwflag);return true;}
    return false;}
    void unhook(void)
    {
    dword dwflag;if(virtualprotect((void*)hookaddr,hook_bytes,page_execute_readwrite,&dwflag))
    {
    memcpy((void*)hookaddr,old_code,hook_bytes);virtualprotect((void*)hookaddr,hook_bytes,dwflag,&dwflag);}
    }
    namespace global
    {
    uint geax=0;uint gebx=0;uint gecx=0;uint gedx=0;uint gesp=0;uint gebp=0;uint gesi=0;uint gedi=0;uint gret=0;临时的返回地址
    uint gtmp=0;一些临时的值保存
    uint gpar=0;被hook函数的正常返回地址
    uint gcnt=1;当前hook的次数
    uint gmax=0;最大hook次数,为0表示一直hook
    bool bent=0;是否为第一次进入hook函数
    }
    void_declspec(naked)hook_jmp(void)
    {
    asm
    {
    entry:
    pushad
    {
    cmp global:bent,0/如果没有进入则表示需要unhook
    je_first
    cmp global:gmax,0/如果为0,则一直启用hook逻辑
    je_second
    mov eax,global:gcnt
    cmp eax,global:gmax/如果当前hook次数没有达到最大次数,则继续
    jl_second
    mov global:gcnt,1/reset state
    mov global:bent,0/reset state
    mov global:gmax,0/reset state
    mov eax,global:gpar/被hook函数的正常返回地址
    mov global:gret,eax/准备跳转到被hook函数的上层调用,结束hook
    popad
    jmp_ret
    }
    first:
    保存相关重要寄存器值
    {
    popad
    mov global:geax,eax
    mov global:gebx,ebx
    mov global:gecx,ecx
    mov global:gedx,edx
    mov global:gesp,esp
    mov global:gebp,ebp
    mov global:gesi,esi
    mov global:gedi,edi
    }
    第一次进入,unhook并监视相关状态
    pushad
    {
    mov global:bent,1/记录状态
    mov edi,global:gebp/被hook函数的ebp
    mov eax,[edi+4]/被hook函数的返回地址(其上层调用地址)
    mov global:gpar,eax/保存返回地址
    mov esi,_entry/将被hook函数的返回地址修改为
    mov[edi+4],esi/本函数的首地址,以便执行完被hook函数的
    剩余逻辑之后能够返回到本函数,决定是否
    还需要hook。call printreg**ters/打印寄存器值[测试],或者其他
    call unhook/unhook
    mov eax,hookaddr/获得被hook的内存地址
    mov global:gret,eax
    }
    popad
    pop global:gtmp/移除本函数的返回地址,并将hook的地址设置
    jmp_ret/为本函数的返回地址,从而实现跳转
    second:
    第二次进入,继续hook,这次进入是被hook函数ret返回的,没有新的ret地址被压栈
    {
    mov global:bent,0/设置状态
    add global:gcnt,1/增加hook计数
    call hook/hook
    mov eax,global:gpar/将被hook函数的返回地址设置为本函数的
    mov global:gret,eax/返回地址,从而实现正常的函数流程
    }
    popad
    ret:
    push global:gret/修改本函数的返回地址
    ret
    }
    }
    void sethookbytes(uint addr)
    {
    hookaddr=addr;new_code[0]=(char)0xe8;call 指令机器码
    (uint&)new_code[1]=(uint)hook_jmp-addr-5;计算跳转偏移
    }
    void printreg**ters(void)
    {
    printf("eax=0x%08x/n",global:geax);printf("ebx=0x%08x/n",global:gebx);printf("ecx=0x%08x/n",global:gecx);printf("edx=0x%08x/n",global:gedx);printf("esp=0x%08x/n",global:gesp);printf("ebp=0x%08x/n",global:gebp);printf("esi=0x%08x/n",global:gesi);printf("edi=0x%08x/n",global:gedi);}
    如上,hook_jmp函数即为我们自定义的hook函数,当被hook函数被hook之后,就会跳转到这个函数里,执行相关逻辑,上面我加了很详细的注释。应该很容易看懂。还是先看怎么使用这套方法,再来细说,代码如下:
    [cpp]view plaincopyprint?void testhook(void)
    {
    printf("th** ** a hook test 1./n");printf("th** ** a hook test 2./n");printf("th** ** a hook test 3./n");printf("th** ** a hook test 4./n");printf("_/n");}
    int main(void)
    {
    uint hook_addr=0x0042ec7b;sethookbytes(hook_addr);global:gmax=2;if(hook())
    {
    testhook();testhook();testhook();}
    system("pause");return 0;}
    如上,testhook函数即为被hook的函数,在main函数中,0x0042ec7b则为testhook函数里的第二个printf调用的地址,在你的机器上可能不一样。这里只是测试之用。testhook函数具体反汇编代码如下:
    [cpp]view plaincopyprint?void testhook(void)
    {
    0042ec50 push ebp
    0042ec51 mov ebp,esp
    0042ec53 sub esp,0c0h
    0042ec59 push ebx
    0042ec5a push esi
    0042ec5b push edi
    0042ec5c lea edi,[ebp-0c0h]
    0042ec62 mov ecx,30h
    0042ec67 mov eax,0cccccccch
    0042ec6c rep stos dword ptr es:[edi]
    printf("th** ** a hook test 1./n");0042ec6e push offset string"th** ** a hook test 1./n"(487e24h)
    0042ec73 call@ilt+4550(_printf)(42d1cbh)
    0042ec78 add esp,4
    printf("th** ** a hook test 2./n");0042ec7b push offset string"th** ** a hook test 2./n"(487e08h)
    0042ec80 call@ilt+4550(_printf)(42d1cbh)
    0042ec85 add esp,4
    printf("th** ** a hook test 3./n");0042ec88 push offset string"th** ** a hook test 3./n"(487dech)
    0042ec8d call@ilt+4550(_printf)(42d1cbh)
    0042ec92 add esp,4
    printf("th** ** a hook test 4./n");0042ec95 push offset string"th** ** a hook test 4./n"(487dd0h)
    0042ec9a call@ilt+4550(_printf)(42d1cbh)
    0042ec9f add esp,4
    printf("_/n");0042eca2 push offset string"_./n"(487db4h)
    0042eca7 call@ilt+4550(_printf)(42d1cbh)
    0042ecac add esp,4
    }
    0042ecaf pop edi
    0042ecb0 pop esi
    0042ecb1 pop ebx
    0042ecb2 add esp,0c0h
    0042ecb8 cmp ebp,esp
    0042ecba call@ilt+3570(_rtc_checkesp)(42cdf7h)
    0042ecbf mov esp,ebp
    0042ecc1 pop ebp
    0042ecc2 ret
    我们hook的就是第18行(0042ec7b)那句代码,sethookbytes构建了一个5字节的call语句,0xe8为call指令的机器码,后面4个字节是call的偏移量(目标地址-当前地址-call指令占用的5个字节)。在main函数中,构建了hook的5个字节之后,设置了hook次数,如main函数那段代码的第15行:global:gmax=2,则会hook两次。然后是main函数那段代码的第16行,调用hook函数,将5个字节的call指令写入0042ec7b中,并保存了0042ec7b中原来的代码到old_code中。之后,我们便可以调用testhook函数进行测试hook的流程了。最终输出结果为:
    th** ** a hook test 1.
    eax=0x00000017
    ebx=0x7ffdc000
    ecx=0x8df97741
    edx=0x00499148
    esp=0x0012fd84
    ebp=0x0012fe54
    esi=0x00000000
    edi=0x0012fe54
    th** ** a hook test 2.
    th** ** a hook test 3.
    th** ** a hook test 4.
    th** ** a hook test 1.
    eax=0x00000017
    ebx=0x7ffdc000
    ecx=0x8df97741
    edx=0x00499148
    esp=0x0012fd84
    ebp=0x0012fe54
    esi=0x00000000
    edi=0x0012fe54
    th** ** a hook test 2.
    th** ** a hook test 3.
    th** ** a hook test 4.
    th** ** a hook test 1.
    th** ** a hook test 2.
    th** ** a hook test 3.
    th** ** a hook test 4.
    可以看出,前面两次调用testhook函数时,都执行了hook_jmp函数,并调用了printreg**ters函数将寄存器打印了出来,之后又回到testhook中,继续输出后面的3句字符...

类似问答
精品推荐

友情链接

友链互换QQ:

谷财 备案编号:蜀ICP备11019336号-3商务合作:235-677-2621

Copyright 2009-2020 Chengdu Sanzilewan Technology Co.,Ltd all rights reserve

抵制不良游戏 拒绝盗版游戏 注意自我保护 谨防受骗上当 适度游戏益脑 沉迷游戏伤身 合理安排时间 享受健康生活