MFC框架软件逆向研究

tsx81428  金牌会员 | 2024-8-14 18:39:01 | 来自手机 | 显示全部楼层 | 阅读模式
打印 上一主题 下一主题

主题 853|帖子 853|积分 2561

MFC框架简介

什么是mfc?
MFC库是开发Windows应用程序的C++接口。MFC提供了面向对象的框架,采用面向对象技能,将大部分的Windows API 封装到C++类中,以类成员函数的形式提供给程序开发职员调用。
简单来说,MFC是一种面向对象,用于开发windows应用程序的框架,突出特点是封装了大部分windows API,便于开发职员利用(写win挂方便)。
MFC程序的运行过程分为以下四步:

  • 利用全局应用程序对象theApp启动应用程序。
  • 调用全局应用程序对象的构造函数,从而调用基类(CWinApp)的构造函数,完成应用程序的一些初始化工作,并将应用程序对象的指针保存起来。
  • 进入WinMain函数。在AfxWinMain函数中获取子类的指针,利用指针实现上述的三个函数,从而完成窗口的创建注册等工作。
  • 进入消息循环,一直到WM_QUIT。
那么问题来了,我们如何逆向mfc程序呢?因为其封装了大部分windows API,逆向起来也复杂了不少,因为需要了解大量的windows api 并且熟悉windows编程。下面进行讲解。
MFC如何逆向

如下图,是MFC框架软件的基本界面,可以看到,就是一堆button,主要逆向也是check button。

那么,对于MFC逆向,我们主要需要知道的是,当我们实行某个操作(点击某个按钮)的时候,程序会实行什么处理函数。在mfc中,程序是利用消息机制来实现操作响应的,这个是消息映射表的代码:
  1. struct AFX_MSGMAP{
  2.    AFX_MSGMAP * pBaseMessageMap;
  3.    AFX_MSGMAP_ENTRY * lpEntries;
  4. }
  5. struct AFX_MSGMAP_ENTRY{
  6.    UINT nMessage;    //Windows Message
  7.    UINT nCode        //Control code or WM_NOTIFY code
  8.    UINT nID;         //control ID (or 0 for windows messages)
  9.    UINT nLastID;     //used for entries specifying a range of control id's
  10.    UINT nSig;        //signature type(action) or pointer to message
  11.    AFX_PMSG pfn;     //routine to call (or specical value)
  12. }
复制代码
其中这个AFX_MSGMAP_ENTRY中的最后一个成员AFX_PMSG就是一个函数指针,指向了当前控件绑定的函数。同时,这个nID成员形貌的是当前控件的ID,利用这个ID就能确定我们所寻找的控件。然后这个AFX_MSGMAP结构体则会记载一个指向AFX_MSGMAP_ENTRY的指针,于是查找控件的注册函数的思路可以缩小为:

  • 找到AFX_MSGMAP
  • 找到控件的ID --- 关键就是找ID
那么,我们又该怎么找到控件ID呢,俗话说“工欲善其事,必先利其器”,作为逆向分析职员,肯定要选择好分析的工具了,很光荣,我们站在巨人的肩膀上,针对mfc软件程序的逆向分析,前辈们已经开发了一些非常好用的小工具,我们可以直接利用它们。例如:

  • xspy
  • ResourceHacker
  • 彗星小助手
其中我们主要用的是xspy,mfc分析利器如下图所示
[img=720,419.0896551724138]https://m-1254331109.cos.ap-guangzhou.myqcloud.com/202408141514386.png[/img]

逆向实验-以CTF赛题为例讲解

demo1 - MFC初探

打开程序软件

程序的标题Flag就在控件中,然后界面内容是让我们找一个key。很明显,我们需要找到两个东西

  • 标题找Flag(也就是找窗口句柄)
  • 内容找key
根据这些内容,告诉我们我们去找控件,然后这时候就要掏出xspy了。不然的话,我们如果利用老一套经典分析流程,die+ida对用架构分析,会发生下面这样的事。起首die查个架构,查个壳
[img=720,359.21225382932164]https://m-1254331109.cos.ap-guangzhou.myqcloud.com/202408141514401.png[/img]

好家伙,VMP壳,PE32ida走起,如下图,emmm....
[img=720,371.625]https://m-1254331109.cos.ap-guangzhou.myqcloud.com/202408141514435.png[/img]

这样的话,我们很难继承往下分析,所以我们利用xspy分析。利用方法如下图

起首我们找到了Flag_enc(944c8d100f82f0c18b682f63e4dbaa207a2f1e72581c2f1b)我们知道特定的,窗口句柄叫 HWND
[img=720,419.0896551724138]https://m-1254331109.cos.ap-guangzhou.myqcloud.com/202408141514154.png[/img]

然后我们可以发现一条特殊的onMsgOnMsg:0464,func= 0x00402170(MFC1.exe+ 0x002170 )为什么特殊呢,因为只有它并不是以宏的形式出现,应该是作者自定义的消息,没有button等东西,所以程序怎么点击都无法触发任何效果;并且传入一个特殊数字0464,来触发效果。
[img=720,419.0896551724138]https://m-1254331109.cos.ap-guangzhou.myqcloud.com/202408141514242.png[/img]

那么,我们需要去发送这条消息来出发func函数以获取我们需要的key
  1. #include<Windows.h>
  2. #include<stdio.h>
  3. int main()
  4. {
  5.     HWND h = FindWindowA(NULL, "Flag就在控件里");
  6.     if (h)
  7.     {
  8.         SendMessage(h, 0x0464, 0, 0);
  9.         printf("success");
  10.     }
  11.     else printf("failure");
  12. }
复制代码
利用 API FindWindow 获取窗口句柄,SendMessage发送消息,得到了key{I am a Des key}
[img=574,370.9969277777778]https://m-1254331109.cos.ap-guangzhou.myqcloud.com/202408141514217.png[/img]

最后DES解密即可
[img=720,530.1098901098901]https://m-1254331109.cos.ap-guangzhou.myqcloud.com/202408141514272.png[/img]

flag{thIs_Is_real_kEy_hahaaa}
【----帮助网安学习,以下全部学习资料免费领!加vx:dctintin,备注 “博客园” 获取!】
 ① 网安学习发展路径思维导图
 ② 60+网安经典常用工具包
 ③ 100+SRC漏洞分析报告
 ④ 150+网安攻防实战技能电子书
 ⑤ 最权势巨子CISSP 认证测验指南+题库
 ⑥ 超1800页CTF实战技巧手册
 ⑦ 最新网安大厂口试题合集(含答案)
 ⑧ APP客户端安全检测指南(安卓+IOS)
Junk_instruction-西湖论剑

下面,再讲解一道大型角逐的赛题来实验打开,看到这个朴素的界面可以鉴定是MFC框架。

我们看到了一个input,还有一个check button,很明显,我们起首就需要去找check button的id&注册函数。
xspy-MFC分析

[img=720,419.0896551724138]https://m-1254331109.cos.ap-guangzhou.myqcloud.com/202408141514982.png[/img]

check按钮的id为03e9,同时窗口存在OnCommand: notifycode=0000 id=03e9,func= 0x00C72420(Junk_Instruction.exe+ 0x002420 )函数。那么对应的check逻辑肯定在基址+偏移0x002420处。打开ida,找到check函数 sub_402420 ,如下图

可以看到有一个条件判断:if ( (unsigned __int8)sub_402600(v2 + 16) )。一眼顶针,两个分支分别是弹出精确和错误的对话框,为什么呢?if else函数体内容基本一样。固然我们照旧动态调试一下
[img=720,453.96780965710286]https://m-1254331109.cos.ap-guangzhou.myqcloud.com/202408141514996.png[/img]

所以enc函数很明显就是sub_402600这个函数中就出现了许多垃圾指令了,也就对应上题目名称Junk_instruction了。
去花-IDA分析

爆红
[img=720,216.25145518044238]https://m-1254331109.cos.ap-guangzhou.myqcloud.com/202408141514059.png[/img]

花指令,经典call $+5起手,就是先用一个call压好返回所在,再把栈里的返回所在弹出来,改一下,压回去,如此反复。去掉也很简单,我们把下述累死指令块全部nop掉即可,有好几处,一模一样。
[img=720,595.741935483871]https://m-1254331109.cos.ap-guangzhou.myqcloud.com/202408141514173.png[/img]

固然,我们利用idapython脚本自动去花
  1.    from ida_bytes import get_bytes, patch_bytes
  2.    import re
  3.    addr = 0x402600
  4.    end = 0x402fe3
  5.    buf = get_bytes(addr, end-addr)
  6.    def nopp(s):
  7.        s = s.group(0)
  8.        print("".join(["%02x"%i for i in s]))
  9.        s = b"\x90"*len(s)
  10.        return s
  11.    pattern  = b"\xe8\x00\x00\x00\x00\x58\x89.*?\xc3.*?\x22"
  12.    buf = re.sub(pattern , nopp, buf, flags=re.I)
  13.    patch_bytes(addr, buf)
  14.    print("Done")
复制代码
[img=720,491.74311926605503]https://m-1254331109.cos.ap-guangzhou.myqcloud.com/202408141514187.png[/img]

加密

去除花指令,简单审计发现是对程序进行RC4加密,最后还对输入进行了个倒叙
[img=720,473.45692475463466]https://m-1254331109.cos.ap-guangzhou.myqcloud.com/202408141514831.png[/img]

[img=720,339.1212653778559]https://m-1254331109.cos.ap-guangzhou.myqcloud.com/202408141514790.png[/img]

[img=720,456.8421052631579]https://m-1254331109.cos.ap-guangzhou.myqcloud.com/202408141514798.png[/img]

去花后,整理一下,代码如下
  1. char __thiscall sub_402600(void *this, int a2)
  2. {
  3.  const WCHAR *v2; // eax
  4.  void *v3; // eax
  5.  char v5[511]; // [esp+9h] [ebp-4BBh] BYREF
  6.  int v6; // [esp+208h] [ebp-2BCh]
  7.  char *v7; // [esp+20Ch] [ebp-2B8h]
  8.  int v8; // [esp+210h] [ebp-2B4h]
  9.  size_t Count; // [esp+214h] [ebp-2B0h]
  10.  int v10; // [esp+218h] [ebp-2ACh]
  11.  size_t v11; // [esp+21Ch] [ebp-2A8h]
  12.  char *v12; // [esp+220h] [ebp-2A4h]
  13.  char *v13; // [esp+224h] [ebp-2A0h]
  14.  int v14; // [esp+228h] [ebp-29Ch]
  15.  char v15[4]; // [esp+22Ch] [ebp-298h] BYREF
  16.  char *Source; // [esp+230h] [ebp-294h]
  17.  void *v17; // [esp+234h] [ebp-290h]
  18.  char cipher[32]; // [esp+238h] [ebp-28Ch]
  19.  const char *v19; // [esp+258h] [ebp-26Ch]
  20.  char *v20; // [esp+25Ch] [ebp-268h]
  21.  int i; // [esp+260h] [ebp-264h]
  22.  char *p_Destination; // [esp+264h] [ebp-260h]
  23.  char v23; // [esp+26Dh] [ebp-257h]
  24.  char v24; // [esp+26Eh] [ebp-256h]
  25.  char v25; // [esp+26Fh] [ebp-255h]
  26.  char v26[28]; // [esp+270h] [ebp-254h] BYREF
  27.  char v27[256]; // [esp+28Ch] [ebp-238h] BYREF
  28.  char key[256]; // [esp+38Ch] [ebp-138h] BYREF
  29.  char Destination; // [esp+48Ch] [ebp-38h] BYREF
  30.  char v30[39]; // [esp+48Dh] [ebp-37h] BYREF
  31.  int v31; // [esp+4C0h] [ebp-4h]
  32.  v17 = this;
  33.  v31 = 3;
  34.  cipher[0] = 91;
  35.  cipher[1] = -42;
  36.  cipher[2] = -48;
  37.  cipher[3] = 38;
  38.  cipher[4] = -56;
  39.  cipher[5] = -35;
  40.  cipher[6] = 25;
  41.  cipher[7] = 126;
  42.  cipher[8] = 110;
  43.  cipher[9] = 62;
  44.  cipher[10] = -53;
  45.  cipher[11] = 22;
  46.  cipher[12] = -111;
  47.  cipher[13] = 125;
  48.  cipher[14] = -1;
  49.  cipher[15] = -81;
  50.  cipher[16] = -35;
  51.  cipher[17] = 118;
  52.  cipher[18] = 100;
  53.  cipher[19] = -80;
  54.  cipher[20] = -9;
  55.  cipher[21] = -27;
  56.  cipher[22] = -119;
  57.  cipher[23] = 87;
  58.  cipher[24] = -126;
  59.  cipher[25] = -97;
  60.  cipher[26] = 12;
  61.  cipher[27] = 0;
  62.  cipher[28] = -98;
  63.  cipher[29] = -48;
  64.  cipher[30] = 69;
  65.  cipher[31] = -6;
  66.  v2 = (const WCHAR *)sub_401570(&a2);
  67.  v14 = sub_4030A0(v2);
  68.  v10 = v14;
  69.  v3 = (void *)sub_401570(v14);
  70.  sub_403000(v3);
  71.  sub_4012A0(v15);
  72.  Source = (char *)unknown_libname_1(v26);
  73.  v20 = Source;
  74.  v13 = Source + 1;
  75.  v20 += strlen(v20);
  76.  v11 = ++v20 - (Source + 1);
  77.  Count = v11;
  78.  Destination = 0;
  79.  memset(v30, 0, sizeof(v30));
  80.  strncpy(&Destination, Source, v11);
  81.  if ( sub_402AF0(&Destination) )
  82.   {
  83.    v23 = 0;
  84.    v25 = 0;
  85. LABEL_7:
  86.    v24 = v25;
  87.   }
  88.  else
  89.   {
  90.    strcpy(key, "qwertyuiop");                  // key
  91.    memset(&key[11], 0, 0xF5u);
  92.    memset(v27, 0, sizeof(v27));
  93.    memset(v5, 0, sizeof(v5));
  94.    v19 = key;
  95.    v7 = &key[1];
  96.    v19 += strlen(v19);
  97.    v6 = ++v19 - &key[1];
  98.    RC4_init((int)v27, key, v19 - &key[1]);     // RC4_init
  99.    p_Destination = &Destination;
  100.    v12 = v30;
  101.    p_Destination += strlen(p_Destination);
  102.    v8 = ++p_Destination - v30;
  103.    RC4_crypt((int)v27, (int)&Destination, p_Destination - v30);// RC4_crypto
  104.    for ( i = 31; i >= 0; --i )
  105.    {
  106.      if ( v30[i - 1] != cipher[i] )            // 倒叙
  107.      {
  108.        v25 = 0;
  109.        goto LABEL_7;
  110.      }
  111.    }
  112.    v24 = 1;
  113.   }
  114.  LOBYTE(v31) = 0;
  115.  sub_403060(v26);
  116.  v31 = -1;
  117.  sub_4012A0(&a2);
  118.  return v24;
  119. }
复制代码
解密

起首提取密文,利用插件Lazy_ida 5BD6D026C8DD197E6E3ECB16917DFFAFDD7664B0F7E58957829F0C009ED045FA
[img=720,424.8052902277737]https://m-1254331109.cos.ap-guangzhou.myqcloud.com/202408141514024.png[/img]

key-->qwertyuiop
cyberchef 得解
[img=720,541.2612612612612]https://m-1254331109.cos.ap-guangzhou.myqcloud.com/202408141514350.png[/img]

flag{973387a11fa3f724d74802857d3e052f}

更多网安技能的在线实操练习,请点击这里>>
  

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

tsx81428

金牌会员
这个人很懒什么都没写!
快速回复 返回顶部 返回列表