首页 > 编程语言 >C++逆向分析——模版

C++逆向分析——模版

时间:2023-08-02 18:35:25浏览次数:33  
标签:逆向 arr int 模版 C++ base dword ptr 模板

模版

假设有一个冒泡排序的函数:

void Sort(int* arr, int nLength)

{

int i, k;


for (i = 0; i < nLength; i++)

 {

for (k = 0; k < nLength-1-i; k++)

 {

if(arr[k] > arr[k+1]) {


int temp = arr[k];


 arr[k] = arr[k+1];


 arr[k+1] = temp;

 }
 }
 }
}

但是这个冒泡排序的函数只能对int类型,如果我们想要使用char类型的时候就要重新写一个函数,这就违背了C++的初衷,重复造轮子了:

C++逆向分析——模版_冒泡排序

那么如何避免重复造轮子呢?C++中使用模板来解决这个问题。

函数模板的语法是这样的:

template<class 形参名, class 形参名, ...>

返回类型 函数名(参数列表) {
 函数体;
}

用模板的方式来修改一下这个冒泡排序函数:

template<class T>


void Sort(T* arr, int nLength)

{

int i, k;


for (i = 0; i < nLength; i++)

 {

for (k = 0; k < nLength-1-i; k++)

 {

if(arr[k] > arr[k+1])

 {
 T temp = arr[k];

 arr[k] = arr[k+1];


 arr[k+1] = temp;

 }
 }
 }
}

在当前这个函数中我们只有一个地方需要替换修改,所以在写模板关键词时候尖括号内的class形参只有一个,而我们只需要将需要替换的地方改成形参的名字即可。

那么模版其原理是什么,编译器做了什么工作呢?我们可以看一下如下代码的反汇编代码:

C++逆向分析——模版_Max_02

使用不同类型的数组传入冒泡排序函数,观察一下执行地址:

C++逆向分析——模版_编译器_03

可以看见,两个函数的地址完全不一样,这就说明模板的本质就是编译器会在看见不同的传入类型时创建不同的函数

模板除了可以在函数中使用也可以在结构体(类)中使用模板,其格式如下所示:

template<class 形参名, class 形参名, ...>


class 类名 {

 ...;
}

如下代码,一个结构体,有两个成员函数,一个是比较返回最大的数,一个则是最小的数:

struct Base {

int a;


int b;

 

char x;


char y;

 

int Max() {


if (a>b) {


return a;


 } else {


return b;

 }
 }
 

char Min() {


if (x<y) {


return x; 


 } else {


return y;

 }
 }
};

但这个结构体已经指定了成员变量的数据宽度int、char,而我们想要比较任意类型的话,可以使用模板改造下这段代码:

 

这个模板想使用的话,我们就需要告诉编译器模板中的T、M分别对应什么,所以如果直接使用Base b;则无法编译。

使用如下格式即可:

Base<int, char> b;

C++逆向分析——模版_编译器_04

 

在我的vs2022里实验下:

#include <stdio.h>


template<class T, class M>
struct Base {
	T a;
	T b;

	M x;
	M y;

	T Max() {
		if (a > b) {
			return a;
		}
		else {
			return b;
		}
	}

	M Min() {
		if (x < y) {
			return x;
		}
		else {
			return y;
		}
	}
};

void main() {
	Base<int, float> base;
	base.a = 3;
	base.b = 4;
	base.x = 1.2f;
	base.y = 3.4f;

	int max = base.Max();
	float min = base.Min();
	printf("%d %f\n", max, min);
	return;
}

 看下反汇编代码:

Base<int, float> base;
	base.a = 3;
004C197F C7 45 E8 03 00 00 00 mov         dword ptr [base],3  
	base.b = 4;
004C1986 C7 45 EC 04 00 00 00 mov         dword ptr [ebp-14h],4  
	base.x = 1.2f;
004C198D F3 0F 10 05 38 7B 4C 00 movss       xmm0,dword ptr [__real@3f99999a (04C7B38h)]  
004C1995 F3 0F 11 45 F0       movss       dword ptr [ebp-10h],xmm0  
	base.y = 3.4f;
004C199A F3 0F 10 05 3C 7B 4C 00 movss       xmm0,dword ptr [__real@4059999a (04C7B3Ch)]  
004C19A2 F3 0F 11 45 F4       movss       dword ptr [ebp-0Ch],xmm0  

	int max = base.Max();
004C19A7 8D 4D E8             lea         ecx,[base]  
004C19AA E8 CE F6 FF FF       call        Base<int,float>::Max (04C107Dh)  
004C19AF 89 45 DC             mov         dword ptr [max],eax  
	float min = base.Min();
004C19B2 8D 4D E8             lea         ecx,[base]  
004C19B5 E8 AF F6 FF FF       call        Base<int,float>::Min (04C1069h)  
004C19BA D9 5D D0             fstp        dword ptr [min]  
	printf("%d %f\n", max, min);
004C19BD F3 0F 5A 45 D0       cvtss2sd    xmm0,dword ptr [min]  
004C19C2 83 EC 08             sub         esp,8  
004C19C5 F2 0F 11 04 24       movsd       mmword ptr [esp],xmm0  
004C19CA 8B 45 DC             mov         eax,dword ptr [max]  
004C19CD 50                   push        eax  
004C19CE 68 30 7B 4C 00       push        offset string "%d %f\n" (04C7B30h)  
004C19D3 E8 FF F6 FF FF       call        _printf (04C10D7h)

 

分析下:

C++逆向分析——模版_编译器_05

 

看下1.2的16进制表示:

C++逆向分析——模版_冒泡排序_06

 

C++逆向分析——模版_冒泡排序_07

 

可以看到就是1.2和3.4,只是编译器,将这两个数字放到了常量区。

 

因此,本质上,模板就是给不同的类和函数在编译期间根据类型取了一个新的名字:

C++逆向分析——模版_编译器_08

 

 

 

 

 

标签:逆向,arr,int,模版,C++,base,dword,ptr,模板
From: https://blog.51cto.com/u_11908275/6941546

相关文章

  • C++逆向分析——运算符重载
    运算符重载现在有一个类,其中有一个函数用于比较2个类的成员大小:#include<stdio.h>classNumber{private:intx;inty;public:Number(intx,inty){this->x=x;this->y=y;}intMax(Number&n){returnthis->x>n.x&&this->y......
  • C++逆向分析——多态和虚表
    虚表上一章了解了多态,那么我们来了解一下多态在C++中是如何实现的。了解本质,那就通过反汇编代码去看就行了,首先我们看下非多态的情况下的反汇编代码:然后再来看下多态情况下的反汇编代码:很明显这里多态的情况下会根据edx间接调用,而非多态则会直接调用。那么我们来看下间接调用的流程......
  • C++逆向分析——继承与封装
    面向对象程序设计之继承与封装之前已经学习过继承和封装了,但是要在实际开发中使用,光学语法和原理是不够的,在设计层面我们需要做一些优化。如下代码是继承的例子:#include<stdio.h>classPerson{public:intAge;intSex;voidWork(){printf("Person:Work()"......
  • C++逆向分析——引用
    voidmain(){intx=1;int&ref=x;ref=2;printf("%d\n",ref);return;}反汇编代码:intx=1;00724A5FC745F401000000movdwordptr[x],1int&ref=x;00724A668D45F4lea......
  • C++逆向分析——类成员的访问控制
    类成员的访问控制课外→好的编程习惯:定义与实现分开写,提升代码可读性。如下代码,Student这个类的所有成员我们都可以调用,但是我们不想让被人调用Print1这个方法该怎么?structStudent{intage;intsex;voidPrint1(){printf("FuncPrint1");}voidPrint(){......
  • C++逆向分析——new和delete new[]和delete[]
    在堆中创建对象我们可以在什么地方创建对象?全局变量区,在函数外面在栈中创建对象,也就是函数内在堆中创建对象注意:之前一直提到的堆栈实际上是两个概念->堆、栈,我们之前所讲的就是栈,从本章开始要严格区分。在C语言中,我们可以通过一个函数去申请一块内存,就是malloc(N);申请的这一块内存......
  • C++逆向分析——继承
    继承structPerson{intage;intsex;};structTeacher{intage;intsex;intlevel;intclassId;};如上代码中可以看见,Teacher类与Person类都存在着相同的2个成员age和sex,那么这就相当于重复编写了,我们可以通过继承的方式避免这样重复的编写(当前类名称:要......
  • C++逆向分析——this指针
    this指针概述C++是对C的拓展,C原有的语法C++都支持,并在此基础上拓展了一些语法:封装、继承、多态、模板等等。C++拓展新的语法是为了让使用更加方便、高效,这样就需要编译器多做了很多事情,接下来我们就需要一一学习这些概念。封装之前我们学习过结构体这个概念,那么结构体可以做参数传......
  • C语言逆向——预处理之宏定义、条件编译与文件包含
    预处理之宏定义、条件编译与文件包含预处理一般是指在程序源代码被转换为二进制代码之前,由预处理器对程序源代码文本进行处理,处理后的结果再由编译器进一步编译。预处理功能主要包括宏定义、文件包含、条件编译三部分。宏定义简单的宏:#define标识符字符序列#defineFALSE0#d......
  • C语言逆向分析——Switch语句,为何大多数情况较if语句更高效?就是因为查找表
    Switch语句Switch语句也是分支语句的一种,其语法如下:switch(表达式){case常量表达式1:语句;break;case常量表达式:语句;break;case常量表达式:语句;break;......default:语句;break;}需要注意如下几点:表达式结束不能是浮点数case后的常量......