笔记分享
// 过滤函数(发生异常以后经过__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寄存器保存 }
// 主函数ide
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;指针