SEH除零异常处理及值传递引用传递汇编浅谈
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了SEH除零异常处理及值传递引用传递汇编浅谈相关的知识,希望对你有一定的参考价值。
笔记分享
// 过滤函数(发生异常之后通过__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;
以上是关于SEH除零异常处理及值传递引用传递汇编浅谈的主要内容,如果未能解决你的问题,请参考以下文章