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寄存器保存
}

// 主函數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;指針

相關文章
相關標籤/搜索