【黑客免杀攻防】读书笔记14 - 面向对象逆向-虚函数、MFC逆向

虚函数存在是为了克服类型域解决方案的缺陷,以使程序员可以在基类里声明一些能够在各个派生类里重新定义的函数。

1 识别简单的虚函数

代码示例:

#include "stdafx.h"
#include <Windows.h>class CObj
{
public:CObj():m_Obj_1(0xAAAAAAAA),m_Obj_2(0xBBBB){printf("CObj() Constructor...\r\n");}~CObj(){printf("CObj() Destructor...\r\n");}virtual void Show(int nID)     // 注意这里{m_Obj_1 = 1;printf("ID:%d Who is your God? I am!\r\n",nID);}
private:int  m_Obj_1;WORD m_Obj_2;
};class CPeople : public CObj
{
public:CPeople():m_People_1(0xCCCCCCCC),m_People_2(0xDDDD){printf("CPeople() Constructor...\r\n");}~CPeople(){printf("CPeople() Destructor...\r\n");}void Show(int nID){printf("ID:%d People!\r\n",nID);}
private:int  m_People_1;WORD m_People_2;
};int _tmain(int argc, _TCHAR* argv[])
{CObj obj;CPeople people;CObj *pobj;pobj = &obj;pobj->Show(0);pobj = &people;pobj->Show(1);return 0;
}
// ---------- 输出结果 ----------
// CObj() Constructor...
// CObj() Constructor...
// CPeople() Constructor...
// ID:0 Who is your God? I am!
// ID:1 People!
// CPeople() Destructor...
// CObj() Destructor...
// CObj() Destructor...
// ----------------------------

反汇编代码:

int _tmain(int argc, _TCHAR* argv[])
{
001273B0  push        ebp  
001273B1  mov         ebp,esp  
001273B3  push        0FFFFFFFFh  
001273B5  push        1B3730h  
001273BA  mov         eax,dword ptr fs:[00000000h]    
001273C0  push        eax  
001273C1  sub         esp,108h  
001273C7  push        ebx  
001273C8  push        esi  
001273C9  push        edi  
001273CA  lea         edi,[ebp+FFFFFEECh]  
001273D0  mov         ecx,42h  
001273D5  mov         eax,0CCCCCCCCh  
001273DA  rep stos    dword ptr es:[edi]  
001273DC  mov         eax,dword ptr ds:[001D9004h]
001273E1  xor         eax,ebp  
001273E3  push        eax  
001273E4  lea         eax,[ebp-0Ch]  
001273E7  mov         dword ptr fs:[00000000h],eax              ; 栈保护基址相关代码CObj obj;
001273ED  lea         ecx,[ebp-1Ch]                             ; this 指针
001273F0  call        00123D87                                  ; CObj::CObj (0123D87h)  
001273F5  mov         dword ptr [ebp-4],0                       ; 异常处理的辅助标志,以-1为结尾CPeople people;
001273FC  lea         ecx,[ebp-38h]                             ; this指针
001273FF  call        001211DB                                  ; CPeople::CPeople (01211DBh)  
00127404  mov         byte ptr [ebp-4],1  CObj *pobj;pobj = &obj;
00127408  lea         eax,[ebp-1Ch]                             ; 将obj的this指针给eax
0012740B  mov         dword ptr [ebp-44h],eax                   ; 将this指针给pobj的指针pobj->Show(0);
0012740E  mov         esi,esp  
00127410  push        0                                         ; 参数压栈
00127412  mov         eax,dword ptr [ebp-44h]  
00127415  mov         edx,dword ptr [eax]  
00127417  mov         ecx,dword ptr [ebp-44h]                   ; 将Obj的指针(指向的是   Obj的this指针)给ecx
0012741A  mov         eax,dword ptr [edx]                       ; 将Obj的this指针所指向的第一项的内容(即Vtbl的第一个元素)给eax 
0012741C  call        eax  
0012741C  ; 在调用完CPeople的构造后,程序采用如下步骤实现
0012741C  ; pobj = &obj;
0012741C  ; pobj ->Show(0);
0012741C  ; 
0012741C  ; 1、将创建完的Obj对象的this指针传递给pobj
0012741C  ; 2、将指this指针给eax
0012741C  ; 3、将this指针第一项(即虚函数表指针)传递给edx
0012741C  ; 4、将pobj的值传递给ecx(注意此步)
0012741C  ; 5、将既虚函数表数组的地址传递给eax
0012741C  ; 6、调用eax
0012741E  cmp         esi,esp  
00127420  call        00122329  pobj = &people;
00127425  lea         eax,[ebp-38h]            ; 将People的this指针传给eax
00127428  mov         dword ptr [ebp-44h],eax  ; 将People的this指针给Objpobj->Show(1);
0012742B  mov         esi,esp       
0012742D  push        1                        ; 参数压栈
0012742F  mov         eax,dword ptr [ebp-44h]  ; 将People的this指针给eax
00127432  mov         edx,dword ptr [eax]      ; 将this指针中的第一项,即Vptr给edx
00127434  mov         ecx,dword ptr [ebp-44h]  ; 将People的this指针给ecx
00127437  mov         eax,dword ptr [edx]      ; 将Vptr指向的Vtbl给eax
00127439  call        eax                      ; 调用eax
0012743B  cmp         esi,esp  
0012743D  call        00122329                 ; __RTC_CheckEspreturn 0;
00127442  mov         dword ptr [ebp+FFFFFEF0h],0  
0012744C  mov         byte ptr [ebp-4],0  
00127450  lea         ecx,[ebp-38h]  
00127453  call        00121E10  
00127458  mov         dword ptr [ebp-4],0FFFFFFFFh  
0012745F  lea         ecx,[ebp-1Ch]  
00127462  call        00123BC0  
00127467  mov         eax,dword ptr [ebp+FFFFFEF0h]  
}

如果没有Debug的符号文件,或者逆向过程中代码不是我们自己写的,那就要先判断它是否是一个类的应用。

跟进函数内部情况:

class CObj
{
00126FD0  push        ebp  
00126FD1  mov         ebp,esp  
00126FD3  sub         esp,0CCh  
00126FD9  push        ebx  
00126FDA  push        esi  
00126FDB  push        edi  
00126FDC  push        ecx  
00126FDD  lea         edi,[ebp-0CCh]  
00126FE3  mov         ecx,33h  
00126FE8  mov         eax,0CCCCCCCCh  
00126FED  rep stos    dword ptr es:[edi]  
00126FEF  pop         ecx  
00126FF0  mov         dword ptr [this],ecx        ; 取this指针 this == [ebp-8] 
00126FF3  mov         eax,dword ptr [this]        ; 取this指针
00126FF6  mov         dword ptr [eax],offset CObj::`vftable' (01B5E54h)  
public:CObj():m_Obj_1(0xAAAAAAAA),m_Obj_2(0xBBBB)
00126FFC  mov         eax,dword ptr [this]  
00126FFF  mov         dword ptr [eax+4],0AAAAAAAAh  ; 初始化m_Obj_1为0xAAAAAAAA
00127006  mov         eax,0BBBBh                    ; 初始化m_Obj_2为0xBBBB
0012700B  mov         ecx,dword ptr [this]          ; this指针 this == ecx-8
0012700E  mov         word ptr [ecx+8],ax  printf("CObj() Constructor...\r\n");
00127012  push        offset string "CObj() Constructor...\r\n" (01B5E5Ch)  printf("CObj() Constructor...\r\n");
00127017  call        _printf (0123D00h)  
0012701C  add         esp,4  }
0012701F  mov         eax,dword ptr [this]         ; 将this指针作为返回值 this == ebp-8
00127022  pop         edi  
00127023  pop         esi  
00127024  pop         ebx  
00127025  add         esp,0CCh  
0012702B  cmp         ebp,esp  
0012702D  call        __RTC_CheckEsp (0122329h)  
00127032  mov         esp,ebp  
00127034  pop         ebp  
00127035  ret  

通过阅读以上代码可以得出以下过程:

1)找出虚表位置,以及操作的流程

  • 代码里的例子操作了虚表 00126FF6 mov dword ptr [eax],offset CObj::`vftable' (01B5E54h)

这是一个保存函数地址的指针,再通过汇编上下文的猜测,则可大致确定这就是一个虚表,且将值传到了寄存器参数ecx记录地址的第一项。

  • 以寄存器参数ecx为首地址,分别给其4偏移与8偏移处赋值

  • 寄存器参数ecx又作为返回值传了回去。

  • 通过调用函数的分析,ecx里保存的是this指针,并且根据类的内存结构可知,this里的第一项是Vptr。

2)识别构造函数

  • 由于此成员函数是第一个被调用的,通过代码看出汇编函数中的第二件事是初始化数据成员。最后一件事是将this指针当做返回值返回,所以推测该函数为构造函数。

3)逐步分析函数

  • 构造函数与析构函数会对Vptr操作。
  • 在VS默认设置下,构造与析构前都会有相应的异常处理标记置位操作。
  • 虚函数的调用一般采用eax。

2 识别较复杂的虚函数

经验小结:

  • new出来的对象会以其在堆中申请空间的指针作为this指针传入参与构造。
  • new出来的对象其虚函数调用的寻址方式与普通构造出来的不同。
  • delete对象时会先析构自己,再析构父类,最后再执行delete。
  • new出来的对象如果其成员函数派生于纯虚函数,在delete时只调用父类的析构。
  • 如果此类为抽象类(包含纯虚函数),那么其虚表的对应项会填充指向库函数__purecall的函数指针。

虚函数调用的固定模式,紧盯对各个虚表的操作。从而根据上下文即可大致确定虚函数的调用与类的析构与构造。

3 识别类的继承关系

  • 根据构造函数内的构造顺序分辨此函数所属类的继承情况
  • 总结并记录分析结果
  • VS的release版中存在同时使用ecx、esi寄存器传递this指针的情况。

4 逆向MFC程序

MFC程序关键特征点

版本对应动态库静态库中使用MFC时的特征动态库中使用MFC的特征
4.0mfc40.dllcall [ebp+0x14]call [ebp+0x14]
6.0mfc42.dllcall [ebp+0x14]call [ebp+0x14]
7.1mfc71.dllcall [ebp+0x14]call [ebp+0x14]
10.0mfc100.dllcall [ebp+0x14]mov edx,[ebp+0x14]

分析核心重点

1)判断目标程序是不是MFC程序,如果是,判断其MFC版本

OD快捷键:Ctrl+E 打开模块窗口,并在模块窗口寻找类似于mfc*.dll这样的模块。

如果找到了就可以根据DLL的名称判定程序所用的MFC版本,如果找不到则证明这是一个在静态库中使用MFC的程序。

2)根据目标程序调用MFC方式的不同而采取不同的方式搜索特征

OD快捷键:Ctrl+F 搜索特征 call [ebp+0x14]

由于搜索的特征位于消息分发函数里,因此特征指令所在的位置应该是一个非常大的switch-case。

3)在合适的地方下断点,并跟进到相应消息的函数中。

设置按钮点击事件下断点,即可跟进到达相应消息的函数中。

这里可以参考:

看雪《MFC程序逆向》
https://bbs.pediy.com/thread-54150.htm

转载于:https://www.cnblogs.com/17bdw/p/8851049.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/389837.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

怎么看另一个电脑端口是否通_谁一个人睡觉另一个看看夫妻的睡眠习惯

怎么看另一个电脑端口是否通In 2014, FiveThirtyEight took a survey of about 1057 respondents to get a look at the (literal) sleeping habits of the American public beyond media portrayal. Some interesting notices: first, that about 45% of all couples sleep to…

495. 提莫攻击

495. 提莫攻击 在《英雄联盟》的世界中&#xff0c;有一个叫 “提莫” 的英雄。他的攻击可以让敌方英雄艾希&#xff08;编者注&#xff1a;寒冰射手&#xff09;进入中毒状态。 当提莫攻击艾希&#xff0c;艾希的中毒状态正好持续 duration 秒。 正式地讲&#xff0c;提莫在…

Java基础之Collection和Map

List&#xff1a;实现了collection接口&#xff0c;list可以重复&#xff0c;有顺序 实现方式&#xff1a;3种&#xff0c;分别为&#xff1a;ArrayList&#xff0c;LinkedList&#xff0c;Vector。 三者的比较&#xff1a; ArrayList底层是一个动态数组&#xff0c;数组是使用…

20155320《网络对抗》Exp4 恶意代码分析

20155320《网络对抗》Exp4 恶意代码分析 【系统运行监控】 使用schtasks指令监控系统运行 首先在C盘目录下建立一个netstatlog.bat文件&#xff08;由于是系统盘&#xff0c;所以从别的盘建一个然后拷过去&#xff09;&#xff0c;用来将记录的联网结果格式化输出到netstatlog.…

tableau 自定义省份_在Tableau中使用自定义图像映射

tableau 自定义省份We have been reading about all the ways to make our vizzes in Tableau with more creativity and appeal. During my weekly practice for creating viz as part of makeovermonday2020 community, I came across geographical data which in way requir…

2055. 蜡烛之间的盘子

2055. 蜡烛之间的盘子 给你一个长桌子&#xff0c;桌子上盘子和蜡烛排成一列。给你一个下标从 0 开始的字符串 s &#xff0c;它只包含字符 ‘’ 和 ‘|’ &#xff0c;其中 ’ 表示一个 盘子 &#xff0c;’|’ 表示一支 蜡烛 。 同时给你一个下标从 0 开始的二维整数数组 q…

Template、ItemsPanel、ItemContainerStyle、ItemTemplate

原文:Template、ItemsPanel、ItemContainerStyle、ItemTemplate先来看一张图(网上下的图&#xff0c;加了几个字) 实在是有够“乱”的&#xff0c;慢慢来理一下&#xff1b; 1、Template是指控件的样式 在WPF中所有继承自contentcontrol类的控件都含有此属性&#xff0c;&#…

熊猫烧香分析报告_熊猫分析进行最佳探索性数据分析

熊猫烧香分析报告目录 (Table of Contents) Introduction 介绍 Overview 总览 Variables 变数 Interactions 互动互动 Correlations 相关性 Missing Values 缺失值 Sample 样品 Summary 摘要 介绍 (Introduction) There are countless ways to perform exploratory data analys…

2060. 同源字符串检测

2060. 同源字符串检测 原字符串由小写字母组成&#xff0c;可以按下述步骤编码&#xff1a; 任意将其 分割 为由若干 非空 子字符串组成的一个 序列 。 任意选择序列中的一些元素&#xff08;也可能不选择&#xff09;&#xff0c;然后将这些元素替换为元素各自的长度&#x…

vue中的data用return返回

为什么在大型项目中data需要使用return返回数据呢&#xff1f;答&#xff1a;不使用return包裹的数据会在项目的全局可见&#xff0c;会造成变量污染&#xff1b;使用return包裹后数据中变量只在当前组件中生效&#xff0c;不会影响其他组件。 1、在简单的vue实例中看到的Vue实…

白裤子变粉裤子怎么办_使用裤子构建构建数据科学的monorepo

白裤子变粉裤子怎么办At HousingAnywhere, one of the first major obstacles we had to face when scaling the Data team was building a centralised repository that contains our ever-growing machine learning applications. Between these projects, many of them shar…

ubuntu+anaconda+tensorflow 及相关问题

配置tensorflow部分参考&#xff1a;https://blog.csdn.net/XUTIAN1129/article/details/78997633 装完anaconda, source ~/.bashrc后, 可以直接 pip install tensorflow-gpu , 珍爱生命&#xff0c;远离bazel。但想要c/c调用tf的时候远离不了&#xff0c;还是得bazel编译安装t…

2022. 将一维数组转变成二维数组

2022. 将一维数组转变成二维数组 给你一个下标从 0 开始的一维整数数组 original 和两个整数 m 和 n 。你需要使用 original 中 所有 元素创建一个 m 行 n 列的二维数组。 original 中下标从 0 到 n - 1 &#xff08;都 包含 &#xff09;的元素构成二维数组的第一行&#xf…

支持向量机SVM算法原理及应用(R)

支持向量机SVM算法原理及应用&#xff08;R&#xff09; 2016年08月17日 16:37:25 阅读数&#xff1a;22292更多 个人分类&#xff1a; 数据挖掘实战应用版权声明&#xff1a;本文为博主原创文章&#xff0c;转载请注明来源。 https://blog.csdn.net/csqazwsxedc/article/detai…

mad离群值_全部关于离群值

mad离群值An outlier is a data point in a data set that is distant from all other observations. A data point that lies outside the overall distribution of the dataset. Or in a layman term, we can say, an outlier is something that behaves differently from th…

2057. 值相等的最小索引

2057. 值相等的最小索引 给你一个下标从 0 开始的整数数组 nums &#xff0c;返回 nums 中满足 i mod 10 nums[i] 的最小下标 i &#xff1b;如果不存在这样的下标&#xff0c;返回 -1 。 x mod y 表示 x 除以 y 的 余数 。 示例 1&#xff1a;输入&#xff1a;nums [0,1,2…

SpringBoot中各配置文件的优先级及加载顺序

我们在写程序的时候会碰到各种环境(开发、测试、生产)&#xff0c;因而&#xff0c;在我们切换环境的时候&#xff0c;我们需要手工切换配置文件的内容。这大大的加大了运维人员的负担&#xff0c;同时会带来一定的安全隐患。 为此&#xff0c;为了能更合理地重写各属性的值&am…

青年报告_了解青年的情绪

青年报告Youth-led media is any effort created, planned, implemented, and reflected upon by young people in the form of media, including websites, newspapers, television shows, and publications. Such platforms connect writers, artists, and photographers in …

post提交参数过多时,取消Tomcat对 post长度限制

1.Tomcat 默认的post参数的最大大小为2M&#xff0c; 当超过时将会出错&#xff0c;可以配置maxPostSize参数来改变大小。 从 apache-tomcat-7.0.63 开始&#xff0c;参数 maxPostSize 的含义就变了&#xff1a; 如果将值设置为 0&#xff0c;表示 POST 最大值为 0&#xff0c;…

2048. 下一个更大的数值平衡数

2048. 下一个更大的数值平衡数 如果整数 x 满足&#xff1a;对于每个数位 d &#xff0c;这个数位 恰好 在 x 中出现 d 次。那么整数 x 就是一个 数值平衡数 。 给你一个整数 n &#xff0c;请你返回 严格大于 n 的 最小数值平衡数 。 示例 1&#xff1a;输入&#xff1a;n …