주석이 추가된 x86 디스어셈블리

다음 섹션에서는 디스어셈블리 예제를 안내합니다.

소스 코드

다음은 분석할 함수에 대한 코드입니다.

HRESULT CUserView::CloseView(void)
{
    if (m_fDestroyed) return S_OK;

    BOOL fViewObjectChanged = FALSE;
    ReleaseAndNull(&m_pdtgt);

    if (m_psv) {
        m_psb->EnableModelessSB(FALSE);
        if(m_pws) m_pws->ViewReleased();

        IShellView* psv;

        HWND hwndCapture = GetCapture();
        if (hwndCapture && hwndCapture == m_hwnd) {
            SendMessage(m_hwnd, WM_CANCELMODE, 0, 0);
        }

        m_fHandsOff = TRUE;
        m_fRecursing = TRUE;
        NotifyClients(m_psv, NOTIFY_CLOSING);
        m_fRecursing = FALSE;

        m_psv->UIActivate(SVUIA_DEACTIVATE);

        psv = m_psv;
        m_psv = NULL;

        ReleaseAndNull(&_pctView);

        if (m_pvo) {
            IAdviseSink *pSink;
            if (SUCCEEDED(m_pvo->GetAdvise(NULL, NULL, &pSink)) && pSink) {
                if (pSink == (IAdviseSink *)this)
                    m_pvo->SetAdvise(0, 0, NULL);
                pSink->Release();
            }

            fViewObjectChanged = TRUE;
            ReleaseAndNull(&m_pvo);
        }

        if (psv) {
            psv->SaveViewState();
            psv->DestroyViewWindow();
            psv->Release();
        }

        m_hwndView = NULL;
        m_fHandsOff = FALSE;

        if (m_pcache) {
            GlobalFree(m_pcache);
            m_pcache = NULL;
        }

        m_psb->EnableModelessSB(TRUE);

        CancelPendingActions();
    }

    ReleaseAndNull(&_psf);

    if (fViewObjectChanged)
        NotifyViewClients(DVASPECT_CONTENT, -1);

    if (m_pszTitle) {
        LocalFree(m_pszTitle);
        m_pszTitle = NULL;
    }

    SetRect(&m_rcBounds, 0, 0, 0, 0);
    return S_OK;
}

어셈블리 코드

이 섹션에는 주석이 추가된 디스어셈블리 예제가 포함되어 있습니다.

ebp 레지스터를 프레임 포인터로 사용하는 함수는 다음과 같이 시작됩니다.

HRESULT CUserView::CloseView(void)
SAMPLE!CUserView__CloseView:
71517134 55               push    ebp
71517135 8bec             mov     ebp,esp

이렇게 하면 함수가 ebp의 양수 오프셋으로 매개 변수에 액세스하고 지역 변수를 음의 오프셋으로 액세스할 수 있도록 프레임이 설정됩니다.

이는 프라이빗 COM 인터페이스의 메서드이므로 호출 규칙이 __stdcall. 즉, 매개 변수가 오른쪽에서 왼쪽으로 푸시되고(이 경우 없음), "this" 포인터가 푸시된 다음 함수가 호출됩니다. 따라서 함수에 진입할 때 스택은 다음과 같습니다.

[esp+0] = return address
[esp+4] = this

위의 두 지침 후에는 매개 변수에 다음과 같이 액세스할 수 있습니다.

[ebp+0] = previous ebp pushed on stack
[ebp+4] = return address
[ebp+8] = this

ebp를 프레임 포인터로 사용하는 함수의 경우 첫 번째 푸시된 매개 변수는 [ebp+8]에서 액세스할 수 있습니다. 후속 매개 변수는 더 높은 DWORD 주소에서 연속으로 액세스할 수 있습니다.

71517137 51               push    ecx
71517138 51               push    ecx

이 함수에는 두 개의 로컬 스택 변수만 필요하므로 하위 esp, 8 명령입니다. 그런 다음 푸시된 값을 [ebp-4] 및 [ebp-8]로 사용할 수 있습니다.

ebp를 프레임 포인터로 사용하는 함수의 경우 ebp 레지스터의 음수 오프셋에서 스택 지역 변수에 액세스할 수 있습니다.

71517139 56               push    esi

이제 컴파일러는 함수 호출에서 보존해야 하는 레지스터를 저장합니다. 실제로 실제 코드의 첫 번째 줄과 인터리빙된 비트와 조각으로 저장합니다.

7151713a 8b7508           mov     esi,[ebp+0x8]     ; esi = this
7151713d 57               push    edi               ; save another registers

따라서 CloseView는 기본 개체의 오프셋 12에 있는 ViewState의 메서드입니다. 따라서 이는 ViewState 클래스에 대한 포인터이지만 다른 기본 클래스와 혼동될 수 있는 경우 더 신중하게 (ViewState*)로 지정됩니다.

    if (m_fDestroyed)
7151713e 33ff             xor     edi,edi           ; edi = 0

레지스터를 자체로 XORing하는 것은 레지스터를 0으로 만드는 표준 방법입니다.

71517140 39beac000000     cmp     [esi+0xac],edi    ; this->m_fDestroyed == 0?
71517146 7407             jz      NotDestroyed (7151714f)  ; jump if equal

cmp 명령은 두 값을 빼서 비교합니다. jz 명령은 결과가 0인지 확인하여 비교된 두 값이 같음을 나타냅니다.

cmp 명령은 두 값을 비교합니다. 비교 결과에 따라 후속 j 명령이 점프합니다.

    return S_OK;
71517148 33c0             xor     eax,eax           ; eax = 0 = S_OK
7151714a e972010000       jmp     ReturnNoEBX (715172c1) ; return, do not pop EBX

컴파일러가 함수의 뒷부분까지 EBX 레지스터 저장을 지연시켰기 때문에 프로그램이 이 테스트에서 "조기 종료"될 경우 종료 경로는 EBX를 복원하지 않는 경로여야 합니다.

    BOOL fViewObjectChanged = FALSE;
    ReleaseAndNull(&m_pdtgt);

이러한 두 코드 줄의 실행은 인터리브되므로 주의해야 합니다.

NotDestroyed:
7151714f 8d86c0000000     lea     eax,[esi+0xc0]    ; eax = &m_pdtgt

lea 명령은 메모리 액세스의 효과 주소를 계산하고 대상에 저장합니다. 실제 메모리 주소는 역참조되지 않습니다.

lea 명령은 변수의 주소를 사용합니다.

71517155 53               push    ebx

손상되기 전에 해당 EBX 레지스터를 저장해야 합니다.

71517156 8b1d10195071     mov ebx,[_imp__ReleaseAndNull]

ReleaseAndNull을 자주 호출하므로 EBX에서 주소를 캐시하는 것이 좋습니다.

7151715c 50               push    eax               ; parameter to ReleaseAndNull
7151715d 897dfc           mov     [ebp-0x4],edi     ; fViewObjectChanged = FALSE
71517160 ffd3             call    ebx               ; call ReleaseAndNull
    if (m_psv) {
71517162 397e74           cmp     [esi+0x74],edi    ; this->m_psv == 0?
71517165 0f8411010000     je      No_Psv (7151727c) ; jump if zero

잠시 뒤로 EDI 등록을 0으로 설정했고 EDI는 함수 호출에서 유지되는 레지스터입니다(따라서 ReleaseAndNull 에 대한 호출이 변경되지 않음). 따라서 여전히 값이 0이며 0을 빠르게 테스트하는 데 사용할 수 있습니다.

        m_psb->EnableModelessSB(FALSE);
7151716b 8b4638           mov     eax,[esi+0x38]    ; eax = this->m_psb
7151716e 57               push    edi               ; FALSE
7151716f 50               push    eax               ; "this" for callee
71517170 8b08             mov     ecx,[eax]         ; ecx = m_psb->lpVtbl
71517172 ff5124           call    [ecx+0x24]        ; __stdcall EnableModelessSB

위의 패턴은 COM 메서드 호출의 지시 기호입니다.

COM 메서드 호출은 매우 인기가 있으므로 이를 인식하는 방법을 배우는 것이 좋습니다. 특히 Vtable 오프셋에서 QueryInterface=0, AddRef=4 및 Release=8의 세 가지 IUnknown 메서드를 직접 인식할 수 있어야 합니다.

        if(m_pws) m_pws->ViewReleased();
71517175 8b8614010000     mov     eax,[esi+0x114]   ; eax = this->m_pws
7151717b 3bc7             cmp     eax,edi           ; eax == 0?
7151717d 7406             jz      NoWS (71517185) ; if so, then jump
7151717f 8b08             mov     ecx,[eax]         ; ecx = m_pws->lpVtbl
71517181 50               push    eax               ; "this" for callee
71517182 ff510c           call    [ecx+0xc]         ; __stdcall ViewReleased
NoWS:
        HWND hwndCapture = GetCapture();
71517185 ff15e01a5071    call [_imp__GetCapture]    ; call GetCapture

전역을 통한 간접 호출은 Microsoft Win32에서 함수 가져오기를 구현하는 방법입니다. 로더는 대상의 실제 주소를 가리키도록 전역을 수정합니다. 이것은 당신이 추락 한 기계를 조사 할 때 베어링을 얻을 수있는 편리한 방법입니다. 가져온 함수 및 대상에 대한 호출을 찾습니다. 일반적으로 소스 코드에 있는 위치를 결정하는 데 사용할 수 있는 일부 가져온 함수의 이름이 있습니다.

        if (hwndCapture && hwndCapture == m_hwnd) {
            SendMessage(m_hwnd, WM_CANCELMODE, 0, 0);
        }
7151718b 3bc7             cmp     eax,edi           ; hwndCapture == 0?
7151718d 7412             jz      No_Capture (715171a1) ; jump if zero

함수 반환 값은 EAX 레지스터에 배치됩니다.

7151718f 8b4e44           mov     ecx,[esi+0x44]    ; ecx = this->m_hwnd
71517192 3bc1             cmp     eax,ecx           ; hwndCapture = ecx?
71517194 750b             jnz     No_Capture (715171a1) ; jump if not

71517196 57               push    edi               ; 0
71517197 57               push    edi               ; 0
71517198 6a1f             push    0x1f              ; WM_CANCELMODE
7151719a 51               push    ecx               ; hwndCapture
7151719b ff1518195071     call    [_imp__SendMessageW] ; SendMessage
No_Capture:
        m_fHandsOff = TRUE;
        m_fRecursing = TRUE;
715171a1 66818e0c0100000180 or    word ptr [esi+0x10c],0x8001 ; set both flags at once

        NotifyClients(m_psv, NOTIFY_CLOSING);
715171aa 8b4e20           mov     ecx,[esi+0x20]    ; ecx = (CNotifySource*)this.vtbl
715171ad 6a04             push    0x4               ; NOTIFY_CLOSING
715171af 8d4620           lea     eax,[esi+0x20]    ; eax = (CNotifySource*)this
715171b2 ff7674           push    [esi+0x74]        ; m_psv
715171b5 50               push    eax               ; "this" for callee
715171b6 ff510c           call    [ecx+0xc]         ; __stdcall NotifyClients

사용자 고유의 다른 기본 클래스에서 메서드를 호출할 때 "this" 포인터를 어떻게 변경해야 했는지 알아차립니다.

        m_fRecursing = FALSE;
715171b9 80a60d0100007f   and     byte ptr [esi+0x10d],0x7f
        m_psv->UIActivate(SVUIA_DEACTIVATE);
715171c0 8b4674           mov     eax,[esi+0x74]    ; eax = m_psv
715171c3 57               push    edi               ; SVUIA_DEACTIVATE = 0
715171c4 50               push    eax               ; "this" for callee
715171c5 8b08             mov     ecx,[eax]         ; ecx = vtbl
715171c7 ff511c           call    [ecx+0x1c]        ; __stdcall UIActivate
        psv = m_psv;
        m_psv = NULL;
715171ca 8b4674           mov     eax,[esi+0x74]    ; eax = m_psv
715171cd 897e74           mov     [esi+0x74],edi    ; m_psv = NULL
715171d0 8945f8           mov     [ebp-0x8],eax     ; psv = eax

첫 번째 지역 변수는 psv입니다.

        ReleaseAndNull(&_pctView);
715171d3 8d466c           lea     eax,[esi+0x6c]    ; eax = &_pctView
715171d6 50               push    eax               ; parameter
715171d7 ffd3             call    ebx               ; call ReleaseAndNull
        if (m_pvo) {
715171d9 8b86a8000000     mov     eax,[esi+0xa8]    ; eax = m_pvo
715171df 8dbea8000000     lea     edi,[esi+0xa8]    ; edi = &m_pvo
715171e5 85c0             test    eax,eax           ; eax == 0?
715171e7 7448             jz      No_Pvo (71517231) ; jump if zero

컴파일러는 잠시 동안 자주 사용하려고 하기 때문에 m_pvo 멤버의 주소를 추측적으로 준비했습니다. 따라서 주소를 편리하게 사용하면 코드가 작아질 수 있습니다.

            if (SUCCEEDED(m_pvo->GetAdvise(NULL, NULL, &pSink)) && pSink) {
715171e9 8b08             mov     ecx,[eax]         ; ecx = m_pvo->lpVtbl
715171eb 8d5508           lea     edx,[ebp+0x8]     ; edx = &pSink
715171ee 52               push    edx               ; parameter
715171ef 6a00             push    0x0               ; NULL
715171f1 6a00             push    0x0               ; NULL
715171f3 50               push    eax               ; "this" for callee
715171f4 ff5120           call    [ecx+0x20]        ; __stdcall GetAdvise
715171f7 85c0             test    eax,eax           ; test bits of eax
715171f9 7c2c             jl      No_Advise (71517227) ; jump if less than zero
715171fb 33c9             xor     ecx,ecx           ; ecx = 0
715171fd 394d08           cmp     [ebp+0x8],ecx     ; _pSink == ecx?
71517200 7425             jz      No_Advise (71517227)

컴파일러는 들어오는 "this" 매개 변수가 필요하지 않다는 결론을 내렸습니다(오래 전에 ESI 레지스터에 숨겨졌기 때문). 따라서 메모리를 로컬 변수 pSink로 재사용했습니다.

함수가 EBP 프레임을 사용하는 경우 들어오는 매개 변수는 EBP에서 양수 오프셋에 도달하고 지역 변수는 음수 오프셋에 배치됩니다. 그러나 이 경우와 마찬가지로 컴파일러는 어떤 용도로든 해당 메모리를 자유롭게 재사용할 수 있습니다.

주의를 기울이면 컴파일러가 이 코드를 좀 더 잘 최적화할 수 있음을 알 수 있습니다. 그것은 lea edi를 지연 수 있습니다., [esi+0xa8]푸시 0x0 명령 후까지 명령, 푸시 edi로 대체. 2바이트를 저장했을 것입니다.

                if (pSink == (IAdviseSink *)this)

다음 몇 줄은 C++에서 (IAdviseSink *)NULL 이 여전히 NULL이어야 한다는 사실을 보완하기 위한 것입니다. 따라서 "this"가 실제로 "(ViewState*)NULL"인 경우 캐스트의 결과는 IAdviseSink와 IBrowserService 사이의 거리가 아니라 NULL 이어야 합니다.

71517202 8d46ec           lea     eax,[esi-0x14]    ; eax = -(IAdviseSink*)this
71517205 8d5614           lea     edx,[esi+0x14]    ; edx = (IAdviseSink*)this
71517208 f7d8             neg     eax               ; eax = -eax (sets carry if != 0)
7151720a 1bc0             sbb     eax,eax           ; eax = eax - eax - carry
7151720c 23c2             and     eax,edx           ; eax = NULL or edx

펜티엄에는 조건부 이동 명령이 있지만 기본 i386 아키텍처는 그렇지 않으므로 컴파일러는 특정 기술을 사용하여 점프를 수행하지 않고 조건부 이동 명령을 시뮬레이션합니다.

조건부 평가의 일반적인 패턴은 다음과 같습니다.

        neg     r
        sbb     r, r
        and     r, (val1 - val2)
        add     r, val2

neg rr이 0이 아닌 경우 0에서 빼서 값을 부정하기 때문에 캐리 플래그를 설정합니다. 또한 0에서 빼면 0이 아닌 값을 빼면 대여(캐리 설정)가 생성됩니다. 또한 r 레지스터의 값도 손상되지만, 어쨌든 덮어쓰려고 하기 때문에 허용됩니다.

다음으로 , sbb r, r 명령은 자체에서 값을 빼고 항상 0이 됩니다. 그러나 캐리(대여) 비트도 뺍니다. 따라서 순 결과는 각각 캐리가 명확한지 또는 설정되었는지에 따라 r 을 0 또는 -1로 설정하는 것입니다.

따라서 sbb r, rr 의 원래 값이 0이면 r 을 0으로 설정하고 원래 값이 0이 아니면 -1로 설정합니다.

세 번째 명령은 마스크를 수행합니다. r 레지스터가 0 또는 -1이기 때문에 "this"는 r 0을 그대로 두거나 r을 -1에서 (val1 - val1)로 변경하는 역할을 합니다. 이 경우 ANDing 모든 값이 -1이면 원래 값이 남습니다.

따라서 "and r, (val1 - val1)"의 결과는 r의 원래 값이 0이면 r을 0으로 설정하거나 r의 원래 값이 0이 아니면 "(val1 - val2)"로 설정합니다.

마지막으로 val2r에 추가하여 val2 또는 (val1 - val2) + val2 = val1을 생성합니다.

따라서 이 일련의 명령의 궁극적인 결과는 원래 0인 경우 rval2 로 설정하거나 0이 아닌 경우 val1 로 설정하는 것입니다. r = r ? val1 : val2와 동일한 어셈블리입니다.

이 특정 instance val2 = 0val1 = (IAdviseSink*)를 볼 수 있습니다. (컴파일러가 최종 add eax, 0 명령이 효과가 없기 때문에 제외되었습니다.)

7151720e 394508           cmp     [ebp+0x8],eax ; pSink == (IAdviseSink*)this?
71517211 750b             jnz     No_SetAdvise (7151721e) ; jump if not equal

이 섹션의 앞부분에서 EDI를 m_pvo 멤버의 주소로 설정합니다. 당신은 지금 그것을 사용하려고합니다. 또한 이전에 ECX 레지스터를 0으로 표시했습니다.

                    m_pvo->SetAdvise(0, 0, NULL);
71517213 8b07             mov     eax,[edi]         ; eax = m_pvo
71517215 51               push    ecx               ; NULL
71517216 51               push    ecx               ; 0
71517217 51               push    ecx               ; 0
71517218 8b10             mov     edx,[eax]         ; edx = m_pvo->lpVtbl
7151721a 50               push    eax               ; "this" for callee
7151721b ff521c           call    [edx+0x1c]        ; __stdcall SetAdvise
No_SetAdvise:
                pSink->Release();
7151721e 8b4508           mov     eax,[ebp+0x8]     ; eax = pSink
71517221 50               push    eax               ; "this" for callee
71517222 8b08             mov     ecx,[eax]         ; ecx = pSink->lpVtbl
71517224 ff5108           call    [ecx+0x8]         ; __stdcall Release
No_Advise:

이러한 모든 COM 메서드 호출은 매우 친숙해 보일 것입니다.

다음 두 문의 평가는 인터리브됩니다. EBX에 ReleaseAndNull 주소가 포함되어 있다는 것을 잊지 마세요.

            fViewObjectChanged = TRUE;
            ReleaseAndNull(&m_pvo);
71517227 57               push    edi               ; &m_pvo
71517228 c745fc01000000   mov     dword ptr [ebp-0x4],0x1 ; fViewObjectChanged = TRUE
7151722f ffd3             call    ebx               ; call ReleaseAndNull
No_Pvo:
        if (psv) {
71517231 8b7df8           mov     edi,[ebp-0x8]     ; edi = psv
71517234 85ff             test    edi,edi           ; edi == 0?
71517236 7412             jz      No_Psv2 (7151724a) ; jump if zero
            psv->SaveViewState();
71517238 8b07             mov     eax,[edi]         ; eax = psv->lpVtbl
7151723a 57               push    edi               ; "this" for callee
7151723b ff5034           call    [eax+0x34]        ; __stdcall SaveViewState

다음은 더 많은 COM 메서드 호출입니다.

            psv->DestroyViewWindow();
7151723e 8b07             mov     eax,[edi]         ; eax = psv->lpVtbl
71517240 57               push    edi               ; "this" for callee
71517241 ff5028           call    [eax+0x28]        ; __stdcall DestroyViewWindow
            psv->Release();
71517244 8b07             mov     eax,[edi]         ; eax = psv->lpVtbl
71517246 57               push    edi               ; "this" for callee
71517247 ff5008           call    [eax+0x8]         ; __stdcall Release
No_Psv2:
        m_hwndView = NULL;
7151724a 83667c00         and     dword ptr [esi+0x7c],0x0 ; m_hwndView = 0

0으로 메모리 위치를 ANDing하면 AND 0이 0이므로 0으로 설정하는 것과 같습니다. 컴파일러는 속도가 느리더라도 해당 mov 명령보다 훨씬 짧기 때문에 이 양식을 사용합니다. (이 코드는 속도가 아닌 크기에 최적화되었습니다.)

        m_fHandsOff = FALSE;
7151724e 83a60c010000fe   and     dword ptr [esi+0x10c],0xfe
        if (m_pcache) {
71517255 8b4670           mov     eax,[esi+0x70]    ; eax = m_pcache
71517258 85c0             test    eax,eax           ; eax == 0?
7151725a 740b             jz      No_Cache (71517267) ; jump if zero
            GlobalFree(m_pcache);
7151725c 50               push    eax               ; m_pcache
7151725d ff15b4135071     call    [_imp__GlobalFree]    ; call GlobalFree
            m_pcache = NULL;
71517263 83667000         and     dword ptr [esi+0x70],0x0 ; m_pcache = 0
No_Cache:
        m_psb->EnableModelessSB(TRUE);
71517267 8b4638           mov     eax,[esi+0x38]    ; eax = this->m_psb
7151726a 6a01             push    0x1               ; TRUE
7151726c 50               push    eax               ; "this" for callee
7151726d 8b08             mov     ecx,[eax]         ; ecx = m_psb->lpVtbl
7151726f ff5124           call    [ecx+0x24]        ; __stdcall EnableModelessSB
        CancelPendingActions();

CancelPendingActions를 호출하려면 (ViewState*)에서 (CUserView*)로 이동해야 합니다. CancelPendingActions는 __stdcall 대신 __thiscall 호출 규칙을 사용합니다. __thiscall 따르면 스택에 전달되는 대신 ECX 레지스터에 "this" 포인터가 전달됩니다.

71517272 8d4eec           lea     ecx,[esi-0x14]    ; ecx = (CUserView*)this
71517275 e832fbffff       call CUserView::CancelPendingActions (71516dac) ; __thiscall
    ReleaseAndNull(&_psf);
7151727a 33ff             xor     edi,edi           ; edi = 0 (for later)
No_Psv:
7151727c 8d4678           lea     eax,[esi+0x78]    ; eax = &_psf
7151727f 50               push    eax               ; parameter
71517280 ffd3             call    ebx               ; call ReleaseAndNull
    if (fViewObjectChanged)
71517282 397dfc           cmp     [ebp-0x4],edi     ; fViewObjectChanged == 0?
71517285 740d             jz      NoNotifyViewClients (71517294) ; jump if zero
       NotifyViewClients(DVASPECT_CONTENT, -1);
71517287 8b46ec           mov     eax,[esi-0x14]    ; eax = ((CUserView*)this)->lpVtbl
7151728a 8d4eec           lea     ecx,[esi-0x14]    ; ecx = (CUserView*)this
7151728d 6aff             push    0xff              ; -1
7151728f 6a01             push    0x1               ; DVASPECT_CONTENT = 1
71517291 ff5024           call    [eax+0x24]        ; __thiscall NotifyViewClients
NoNotifyViewClients:
    if (m_pszTitle)
71517294 8b8680000000     mov     eax,[esi+0x80]    ; eax = m_pszTitle
7151729a 8d9e80000000     lea     ebx,[esi+0x80]    ; ebx = &m_pszTitle (for later)
715172a0 3bc7             cmp     eax,edi           ; eax == 0?
715172a2 7409             jz      No_Title (715172ad) ; jump if zero
        LocalFree(m_pszTitle);
715172a4 50               push    eax               ; m_pszTitle
715172a5 ff1538125071     call   [_imp__LocalFree]
        m_pszTitle = NULL;

EDI는 여전히 0이고 EBX는 여전히 &m_pszTitle 있습니다. 이러한 레지스터는 함수 호출에 의해 유지되기 때문입니다.

715172ab 893b             mov     [ebx],edi         ; m_pszTitle = 0
No_Title:
    SetRect(&m_rcBounds, 0, 0, 0, 0);
715172ad 57               push    edi               ; 0
715172ae 57               push    edi               ; 0
715172af 57               push    edi               ; 0
715172b0 81c6fc000000     add     esi,0xfc          ; esi = &this->m_rcBounds
715172b6 57               push    edi               ; 0
715172b7 56               push    esi               ; &m_rcBounds
715172b8 ff15e41a5071     call   [_imp__SetRect]

더 이상 "this" 값이 필요하지 않으므로 컴파일러는 주소를 보관하기 위해 다른 레지스터를 사용하는 대신 추가 명령을 사용하여 수정합니다. V 파이프는 산술 연산을 수행할 수 있지만 계산을 처리할 수는 없으므로 펜티엄 u/v 파이프라인으로 인해 실제로 성능이 향상됩니다.

    return S_OK;
715172be 33c0             xor     eax,eax           ; eax = S_OK

마지막으로 보존에 필요한 레지스터를 복원하고 스택을 클린 호출자에게 돌아와 들어오는 매개 변수를 제거합니다.

715172c0 5b               pop     ebx               ; restore
ReturnNoEBX:
715172c1 5f               pop     edi               ; restore
715172c2 5e               pop     esi               ; restore
715172c3 c9               leave                     ; restores EBP and ESP simultaneously
715172c4 c20400           ret     0x4               ; return and clear parameters