首页
社区
课程
招聘
[原创]逆向一个MFC入口地址查找工具
发表于: 2010-11-8 01:16 9092

[原创]逆向一个MFC入口地址查找工具

2010-11-8 01:16
9092

看雪上一位大牛nOpnOp写了个MFC入口地址查找工具(http://bbs.pediy.com/showthread.php?t=81855),一用,果然好用,可是没源代码,很想知道它的运行原理,所以逆向研究了一番,有所收获,和大家分享一下。
程序使用了调试API,相当于是使用了OD啊,真厉害。ASPPACK加壳,让我用ODSCRIPT脱掉了。点击按钮,判断是否MFC程序等等,用SetTimer启动一个计时器,开始每隔50毫秒运行一次分析程序,该分析程序做了很多判断之类的,通过F5插件获得伪代码,关键点:

    使用GetThreadContext获得上下文句柄

    Context.ContextFlags = 65543;
    GetThreadContext(ProcessInformation.hThread, &Context);
    if ( *(_DWORD *)(v5 + 16) && *(_DWORD *)(v5 + 112) )
    {
      v8 = *(int (__stdcall **)(_DWORD, _DWORD, _DWORD, _DWORD, _DWORD))ReadProcessMemory;
    }
    else
    {
      v8 = *(int (__stdcall **)(_DWORD, _DWORD, _DWORD, _DWORD, _DWORD))ReadProcessMemory;
      v9 = Context.Ebp + 20;
      Buffer = 0;
      lpBaseAddress = 0;
      if ( !ReadProcessMemory(ProcessInformation.hProcess, (LPCVOID)(Context.Ebp + 12), &Buffer, 4u, 0)
        || Buffer != 0x111
        || !v8(ProcessInformation.hProcess, v9, &lpBaseAddress, 4, 0)
        || lpBaseAddress != *(LPCVOID *)(v5 + 12) )
        goto LABEL_59;
    }
    lpBaseAddress = 0;
    if ( !v8(ProcessInformation.hProcess, Context.Ebp + 8, &lpBaseAddress, 4, 0) )
    {
      if ( *(_DWORD *)(v5 + 16) )
      {
        str_Show((LPARAM)"读取ESP失败!");
        goto LABEL_14;
      }
LABEL_60:
      (*(void (__thiscall **)(int, HANDLE, _DWORD))(*(_DWORD *)v5 + 52))(v5, ProcessInformation.hProcess, 0);
      Context.ContextFlags = 65543;
      --Context.Eip;
      SetThreadContext(ProcessInformation.hThread, &Context);
      goto LABEL_61;
    }

也就是读取了两次调试进程的堆栈,第一次是读取 EBP+12 处的数据第二次是读取 EBP+20处的数据,也就是判断压入的堆栈是否等于0x111即WM_COMMAND,它拦截的是哪个函数呢,经过一番调试终于确定是CCmdTarget::OnCmdMsg里的AfxFindMessageEntry函数,AfxFindMessageEntry压入4个参数lpEntries, nMsg, nCode, nID,也就是nMsg==0x111和nID==?之类的来确定这个函数,确定之后再次读取EBP+8处的堆栈,也就是第三次读取,读取到的正是lpEntries也就是消息映射表的地址,通过这个地址可以遍历出所有的消息映射的地址,至此也就真相大白了,但要理解消息映射表,得从MFC的消息映射机制说起,说来话长,自己看书吧。
总结:作者用这个方法不尽完美,如果程序运行得很快,那么计时器就没有用了,因为该函数马上就返回了,拦截不到,在逆向这个程序之前就有想过,可以使用静态的方法去分析MFC程序,首先要获得GetMessageMap()这个函数的地址,逆向过程中发现在MFC40.DLL中是类似[EDI+30]之类的方法来获得这个函数地址,可能类都是用EDI+XXX之类的来表示,那么30就是固定的,那么首先就找到这个EDI,也就是类的基地址,这个基地址硬编码地址在某个代码段中,因为是自动编写的代码,那么应该可以用搜索代码之类的找到这个地址,得到地址+30得到GetMessageMap()的地址,再者消息映射表也是硬编码到GetMessageMap()函数中,也就是说直接可以找到消息映射表的地址。另一种想法是拦截从特定DLL返回到主程序的所有入口,也就是说获得返回程序的入口点地址,这样就应该有好几个入口点,不过这种方法比较通用,从中找一个最有可能的入口点。


[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课

收藏
免费 7
支持
分享
最新回复 (5)
雪    币: 1231
活跃值: (41)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
2
大家一起讨论一下,用什么方法最好。
2010-11-8 01:21
0
雪    币: 440
活跃值: (87)
能力值: ( LV9,RANK:200 )
在线值:
发帖
回帖
粉丝
3
用力地顶一下
2010-11-8 08:29
0
雪    币: 517
活跃值: (64)
能力值: ( LV8,RANK:130 )
在线值:
发帖
回帖
粉丝
4
以前就知道有这个工具,一直没用过。试了一下,确实很好用。
楼主分析的也很好。

查找消息映射表的方法很多。
第一种:通过特征码查询,查找GetMessageMap()函数的特征码。GetMessageMap()函数的变化不是很多,我数过的有六七种,当然可能要多点。这种方法最快,缺点程序代码加壳,变形的就处理不了。
第二种:HOOK特定的dll函数,解析函数参数,取消息映射表。缺点,静态编译的没有dll函数。
第三种:通过动态解析类,通过类的结构取虚函数表,取GetMessageMap()函数,取消息映射表。

当然这个工具的方法很新颖,还有论坛中还有一个工具mfyspy(有源码的)方法也很好。

当然还有其它的方法。
如果程序没有加密,mfc函数的401000地址处的第二个函数,其实就是一个GetMessageMap()函数。(当然没有检验通用性)
2010-11-8 10:40
0
雪    币: 1231
活跃值: (41)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
5
楼上的一席话让我收获不少,我有点想做个找入口点的OD插件,同样的,也可以做VB、Delphi的找入口点的插件,只是原理不同,花多点时间而已。
2010-11-9 01:13
0
雪    币: 585
活跃值: (568)
能力值: ( LV13,RANK:290 )
在线值:
发帖
回帖
粉丝
6
哎呀,一切尽在MFC40.DLL中
2010-12-21 16:59
0
游客
登录 | 注册 方可回帖
返回
//