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

来源:互联网 发布:手机弹钢琴软件 编辑:程序博客网 时间:2024/06/06 07:53

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//retbyte 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注入完善

阅读全文
0 0
原创粉丝点击