笔记分享 // 过滤函数(发生异常之后通过__except(过滤表达式调用))

DWORD Filters(DWORD Code, PEXCEPTION_POINTERS ExceptionInfo)
{
	/*这只是个测试,捕获到除零异常之后根据情况判断什么类型的异常(根据实际情况来)*/
	switch (Code)
	{
	// 内存访问方面异常
	case EXCEPTION_ACCESS_VIOLATION:
		break;
	// 除零异常
	case STATUS_INTEGER_DIVIDE_BY_ZERO:
	{
		int a = 10;
		// 012C44E1  mov         dword ptr[ebp - 0Ch], 0Ah
		// 改变Ecx的地址 PEXCEPTION_POINTERS结构体记录了异常环境
		ExceptionInfo->ContextRecord->Ecx = (DWORD)&a;
		// 012C44E8  mov         eax, dword ptr[ebp + 0Ch]
		// 012C44EB  mov         ecx, dword ptr[eax + 4]
		// 012C44EE  lea         edx, [ebp - 0Ch]
		// 012C44F1  mov         dword ptr[ecx + 000000ACh], edx
		// 上面理解不透彻 希望懂得老铁们进行讲解 大体就是找到了[ecx + 000000ACh],局部变量10地址替换
	}
	break;
	}
	// 继续执行产异常的指令, 在执行的时候 [ecx] 访问的是局部变量10,所以正常执行
	// 012C44F7  or          eax, 0FFFFFFFFh
	return EXCEPTION_CONTINUE_EXECUTION;
}
// 除法(异常)函数
int* Div(int *a, int  *b)
{
	int c = *a / *b;
	/*
	 汇编代码:
		1. 为int c开辟一个空间
			013642F5  mov         dword ptr [ebp-4],eax 
		2. 入栈的是地址 保存的第一个参数的地址[ebp+8] 根据函数调用约定 右->左入栈
			013642F8  mov         eax,dword ptr [ebp+8]		3. 十进制:10  
			013642FB  mov         ecx,dword ptr [ebp+0Ch]	4. 十进制: 0
		5. 会发现除法也好 加法也好总会先保存到一个寄存器中(一般是指令默认操作寄存器)
			013642FE  mov         eax,dword ptr [eax]  
		6. 数据扩展指令,将双字数据扩展为四字类型
			01364300  cdq  
		7. 有符号除法指令 结果保存到eax中
			01364301  idiv        eax,dword ptr [ecx]
		8. 因为是除0 会出现异常处理 原因是因为[ecx]中保存数值为0
	*/
	// 这时候b没有变 变得只是寄存器里面的地址把b的地址替换成了自定义的局部变量int a = 10的地址
	// 所以正常运行 返回 10 / 10 = 1
	return &c;
	//	warning C4172 : 返回局部变量或临时变量的地址 EAX寄存器保存
}

// 主函数

int main(int argc, char** argv)
{
	// SEH:Struct Except Handler 结构化异常处理 微软处理异常的一种机制
	__try
	{
		int a = 10;
		int b = 0;		
		int *p = Div(&a, &b);
		/*
		说明:从平衡堆栈的的方式 add esp, 8 函数采用了cdcel C调用约定,入栈顺序右到左,并且可以知道传参进去是两个参数 平衡堆栈大小8个字节(还有一种好玩的叫栈回溯,找调用者函数)
		1. 函数原型:int Div(int a, int b);
		2. 调用: Div(&a, &b);
			1.1 第二个参数地址入栈 0
			01386240  lea         eax,[ebp-30h]  
			01386243  push        eax
			1.2 第二个参数地址入栈 10
			01386244  lea         ecx, [ebp - 24h]
			01386247  push        ecx
			1.3 调用函数Div
			01386248  call        Div(01381483h)
			1.4 调用者平衡堆栈
			0138624D  add         esp, 8
		==========================================================
		1. 函数原型:int Div(int a, int b);
		2. 调用:Div(a, b)。
		3. 可以发现每个参数入栈的汇编指令都是两条指令,区别在于入栈地址与入栈立即数
			002E617D  mov         eax, dword ptr[ebp - 2Ch]
			002E6180  push        eax
			002E6181  mov         ecx, dword ptr[ebp - 20h]
			002E6184  push        ecx
			002E6185  call        002E14BF
			002E618A  add         esp, 8
		==========================================================
		1. 函数原型 int Div(int &a, int &b);
		2. 调用:	Div(a, b); 引用在汇编传参的时候回事怎样
			00126180  lea         eax,[ebp-30h]
			00126183  push        eax
			00126184  lea         ecx,[ebp-24h]
			00126187  push        ecx
			00126188  call        001214C4
			0012618D  add         esp,8
		结论:其实引用与指针在函数传参,汇编来看没有区别的,只是语法使用确实有约束
		再看:
			int nCount = 1;
			int &nVar = nCount;
		汇编如下:
			00C76172  mov         dword ptr [ebp-24h],1
			00C76179  lea         eax,[ebp-24h]
			00C7617C  mov         dword ptr [ebp-30h],eax
			引用自己开启的内存中保存的是被引用变量的地址。
		*/
		cout << "正常执行 a / b :" << *p << endl;
	}
	// 捕获异常 过滤表达式处理 这里是一个函数
	// 00059135  call        000510D7  
	// 0F963922  call        ecx
	// 0F969263  call        0F963912
	// 76FF34BF  call        ecx  
	// 76FF348E  call        76FF349B
	// 大概经过层次Ret 返回到了Div return 处 因VS没有看到调用的函数
	__except (Filters(GetExceptionCode(), GetExceptionInformation()))
	{
		cout << "异常块" << endl;
	}
	system("pause");
	return 0;
}

关于指针与类型偏移量的一些笔记 C/C++代码:

	char Arrnumber[][2] = { "1", "2", "3", "4", };
	int* p = (int *)Arrnumber;
	p += 1;
	char* p1 = Arrnumber[0];
	p1 += 1;
	short* p2 = (short *)Arrnumber;
	p2 += 1;

对照汇编:

注释:二维数组略显复杂 从数据段ds:中取出内容 2个字节放入ax寄存器(16位),
	 后进行ebp操作(可以简单理解放入函数栈开辟的局部变量)
C/C: char Arrnumber[][2] = { "1", "2", "3", "4", };
	00FD2C68  mov         ax,word ptr ds:[00FDDA88h]  
	00FD2C6E  mov         word ptr [ebp-10h],ax  
	00FD2C72  mov         ax,word ptr ds:[00FDDAD0h]  
	00FD2C78  mov         word ptr [ebp-0Eh],ax  
	00FD2C7C  mov         ax,word ptr ds:[00FDDAE4h]  
	00FD2C82  mov         word ptr [ebp-0Ch],ax  
	00FD2C86  mov         ax,word ptr ds:[00FDDB00h]  
	00FD2C8C  mov         word ptr [ebp-0Ah],ax 
C/C: int* p = (int *)Arrnumber;
注释:【ebp-10】是存储数组中第一个元素1内存单元,lea拿到单元地址送入到eax寄存器中
	00FD2C90  lea         eax,[ebp-10h] 
注释:eax保存的是地址,dword ptr则是声明几个字节(双字),元素1的地址传送到【ebp-1Ch】内存单元中,其实也就是[p]	
	00FD2C93  mov         dword ptr [ebp-1Ch],eax 
注释:元素1的地址从单元中给了eax,准备做加法运算
	00FD2C96  mov         eax,dword ptr [ebp-1Ch] 
C/C: p += 1;
注释:add eax, 4	
	00FD2C99  add         eax,4 
注释:eax是add之后的值,在送回p的内存单元中。
	00FD2C9C  mov         dword ptr [ebp-1Ch],eax 
以上完成了 p += 1;的过程,根据数据类型进行的偏移量

;以下内容都是一样	
	00FD2C9F  mov         eax,2  
	00FD2CA4  imul        ecx,eax,0  
	00FD2CA7  lea         edx,[ebp+ecx-10h]  
	00FD2CAB  mov         dword ptr [ebp-28h],edx  
	00FD2CAE  mov         eax,dword ptr [ebp-28h]  
	00FD2CB1  add         eax,1  
	00FD2CB4  mov         dword ptr [ebp-28h],eax  
	00FD2CB7  lea         eax,[ebp-10h]  
	00FD2CBA  mov         dword ptr [ebp-34h],eax  
	00FD2CBD  mov         eax,dword ptr [ebp-34h]  
	00FD2CC0  add         eax,2  
	00FD2CC3  mov         dword ptr [ebp-34h],eax  
	
相对比一维数组初始化操作汇编指令少很多,直接立即数传送到局部变量
但是对于指针进行加法操作的时候,是一样的。
C/C++源码:
	初始化数组一维:
		char Arrnumber[] = { 1, 2, 3, 4, };
对照汇编
	00192C68  mov         byte ptr [ebp-0Ch],1  
	00192C6C  mov         byte ptr [ebp-0Bh],2  
	00192C70  mov         byte ptr [ebp-0Ah],3  
	00192C74  mov         byte ptr [ebp-9],4  
	00192C78  lea         eax,[ebp-0Ch]  
	00192C7B  mov         dword ptr [ebp-18h],eax  
	00192C7E  mov         eax,dword ptr [ebp-18h]  
	00192C81  add         eax,4  
	00192C84  mov         dword ptr [ebp-18h],eax  
	00192C87  lea         eax,[ebp-0Ch]  
	00192C8A  mov         dword ptr [ebp-24h],eax  
	00192C8D  mov         eax,dword ptr [ebp-24h]  
	00192C90  add         eax,1  
	00192C93  mov         dword ptr [ebp-24h],eax  
	00192C96  lea         eax,[ebp-0Ch]  
	00192C99  mov         dword ptr [ebp-30h],eax  
	00192C9C  mov         eax,dword ptr [ebp-30h]  
	00192C9F  add         eax,2  
	00192CA2  mov         dword ptr [ebp-30h],eax 

引用:《C++反汇编与逆向分析技术揭秘》p34页的指针寻址公式: p + n 目标地址 = 首地址 + sizeof(指针类型 type) * n;