171029 逆向-以CM41为例进行Dll注入(中)

本文详细介绍了使用DLL注入实现Hook的技术细节,包括处理C++语言的参数传递问题、使用内联汇编调用原函数的方法及如何判断特定按钮事件。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1625-5 王子昂 总结《2017年10月29日》 【连续第394天总结】
A. CrackMe41–Dll注入(中)
B.
本次注入Dll所要注意的地方比较多
首先因为编写代码语言是C++(其实用内联汇编代码写起来费事点,但是处理乱七八糟的堆栈和参数传递要省心的多),因此要注意参数问题
其次是Hook点

Hook函数的执行次序为

Created with Raphaël 2.1.0原程序被Hook函数跳转执行Dll函数是否执行原函数Dll函数内脱钩,执行原函数回到Dll函数,再次挂钩控制流回到被Hook函数的Return Address,继续原程序yesno

Hook函数应该尽可能地降低影响,来避免内存访问错误

是否要执行原函数由具体情况决定,为了降低对原程序的影响程度,最好执行
因为原函数内可能会执行一些对其他地方有影响的操作,不止是堆栈和寄存器的操作,还有一些全局变量操作等等

代码编写起来比较简单,主要是代码的改变,和hook函数的代码

为了执行在hook函数中执行原函数,需要考虑函数指针,两种解决办法:

DLL调用exe里的函数,如果知道函数地址的话 2种方法
1 dll里用函数指针指向exe里的函数
比如exe里有个函数 int SetHook(HWND, HWND);
DLL里就这样
typedef int (CALLBACK* sthook)(HWND, HWND);
sthook SetHook;

SetHook = (sthook)0x00XXXXXX;//0x00XXXXXX是 SetHook的地址
然后DLL里就能用SetHook函数了

2 内联汇编
比如exe里有个函数 int SetHook(HWND, HWND); , 函数地址是0x00XXXXXX
DLL中
__asm {
push eax
mov eax, hwnd_arg2
push eax
mov eax, hwnd_arg1
push eax
mov eax, 0x00XXXXXX
call eax
add esp, 8
pop eax
}

写好代码以后原函数调用总是失败
发现原函数的参数传递是使用寄存器eax,ebx
这里写图片描述
而C++编译的函数是通过调用约定来提取参数的,存在于eax或堆栈中
也就是说C语言写的函数提不到ebx的值,即使是写在函数最顶端的内联汇编也由于编译而拿不到原始的ebx

如果Hook函数不需要判断的话,参数倒是无所谓。但是本次Hook由于需要判断被点击的按钮是否为”Exit”来决定弹窗,因此必须拿到参数

最后没办法,想到了令被Hook函数在跳转前将参数保存到全局变量中的方法
即在push func_addr; retn之前 mov par_addr, eax
然后判断par即可

调试发现dll中使用C->函数指针的方法调用函数,实际上会占用寄存器:
这里写图片描述
这样的话eax参数就不可能送进去了╮(╯_╰)╭

只能使用内联汇编的方法了
由于我使用的Code::Blocks编译器支持的是gcc格式的内联汇编,是AT&T记法,所以用起来比较麻烦
有空赶紧换VS编译器_(:з」∠)_VC格式的内联汇编(INTEL记法)才比较熟悉

    asm("movl %%eax, %0\n"
        "movl %%ebx, %1\n"
        "movl %%ecx, %2\n"
        "call %%ecx"
        :
        :"a"(eax),"b"(ebx),"c"(hook_addr)
        );

这样就将之前存储在变量中的值分别送入eax,ebx,ecx中了,然后call ecx就调用了原函数

我们的目标是为Exit按钮添加事件,事件内容很简单,关键在于如何判断Exit按钮
之前静态patch时使用的方法是Hook二级菜单的事件,判断edx(控件下标),由于这次Hook的是窗体事件的函数,控件下标edx已经丢失了,所以只能思考别的办法
eax中存储的是该控件结构体的指针,因此只要在其中找到Caption即可判断按钮。内存中下断分析以后发现是0xe8的位置开始(&Exit),因此只需要判断eax+0xe9的值是否为’E’即可

Dll代码如下:

#include "main.h"

#define hook_addr 0x42f3c0

//mov edx, dl
//push addr
//ret
byte pBuf[20] = {0x89, 0x5,0,0,0,0,0x89, 0x1d, 0,0,0,0, 0x68};
byte OrgBytes[20];
int ebx;
int eax;
int* ebx_p;
int* eax_p;

BOOL hook_by_code(PROC pfnNew)
{
    DWORD* pfnOrg;
    DWORD oldProtect;

    pfnOrg = (DWORD*)hook_addr;
    ebx_p = &ebx;
    eax_p = &eax;

    memcpy(OrgBytes, pfnOrg, 18);
    memcpy(&pBuf[2], &eax_p, 4);
    memcpy(&pBuf[8], &ebx_p, 4);
    memcpy(&pBuf[13], &pfnNew, 4);
    //ret
    pBuf[17] = 0xc3;
    //修改内存属性为可写
    VirtualProtect((LPVOID)pfnOrg, 10, PAGE_EXECUTE_READWRITE, &oldProtect);

    memcpy(pfnOrg, pBuf, 18);
    return TRUE;
}

BOOL New_42f3c0()
{
    /*函数指针调用方法,由于eax传参而废弃
    typedef int (CALLBACK* func)();
    org_func = (func)another_func_addr;
    func org_func;
    org_func();
    */
    DWORD* pfnOrg;


    org_func = (func)hook_addr;
    byte oldBuf[7] = {0x53, 0x8b, 0xd8, 0x80, 0x7b, 0x2d, };
    pfnOrg = (DWORD*)hook_addr;
    //脱钩
    memcpy(pfnOrg, OrgBytes, 18);
    //送入参数并执行原函数
    asm("movl %%eax, %0\n"
        "movl %%ebx, %1\n"
        "movl %%ecx, %2\n"
        "call %%ecx"
        :
        :"a"(eax),"b"(ebx),"c"(hook_addr)
        );

    //check
    if(*(byte*)(eax + 0xe9)=='E')
        if(MessageBoxA(NULL, "Do you fickbirne really want to quit?", "Exit", MB_YESNO)==6)
            exit(0);
        else
            ;
    //再次挂钩
    memcpy(pfnOrg, pBuf, 18);
    return 0;

}

extern "C" DLL_EXPORT BOOL APIENTRY DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
{
    //MessageBoxA(NULL, "Dll inject success", "First", MB_OK);
    switch (fdwReason)
    {
        case DLL_PROCESS_ATTACH:
            // attach to process
            // return FALSE to fail DLL load
            hook_by_code((PROC)New_42f3c0);
            break;
    }
    return TRUE; 
}

这样就可以通过Dll注入的方法来外部添加事件了,相比起静态Patch而言这种方法要更加灵活
如果Hook的是API的话,参数乖乖按照调用约定通过堆栈传递的话,C的代码写起来要比汇编方便许多

PS:
中间我也试过许多其他的方法,例如像静态Patch的方法一样Hook二级菜单的函数,也吃了不少教训。
例如说Hook点最好在函数开头,参数与该函数一致,这样可以尽可能地保持堆栈平衡
像静态Patch那样直接掐下来二级菜单的函数,通过id判断来执行两个事件而不执行原函数的方法对于该程序也是可以的,并且相对而言要简单不少
只不过方法来讲不是太完善,因为完全跳过了原函数,如果其中还有什么其他处理的话就会造成程序不稳定。因此标准起见,应该在Hook函数内执行原函数。

明天来继续完善这个程序,例如说可以注出Dll,还原patch;检测是否被注入过,进行错误处理,等等

C. 明日计划
Dll注入完善

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值