筆記分享
// 過濾函數(發生異常以後經過__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;指針