This question already has answers here:
Why does the x86-64 GCC function prologue allocate less stack than the local variables?
(1 answer)
Why is there no "sub rsp" instruction in this function prologue and why are function parameters stored at negative rbp offsets?
(2 answers)
Closed 4 years ago.
I'm on the way to get idea how the stack works on x86 and x64 machines. What I observed however is that when I manually write a code and disassembly it, it differs from what I see in the code people provide (eg. in their questions and tutorials). Here is little example:
Source
int add(int a, int b) {
int c = 16;
return a + b + c;
}
int main () {
add(3,4);
return 0;
}
x86
add(int, int):
push ebp
mov ebp, esp
sub esp, 16
mov DWORD PTR [ebp-4], 16
mov edx, DWORD PTR [ebp+8]
mov eax, DWORD PTR [ebp+12]
add edx, eax
mov eax, DWORD PTR [ebp-4]
add eax, edx
leave (!)
ret
main:
push ebp
mov ebp, esp
push 4
push 3
call add(int, int)
add esp, 8
mov eax, 0
leave (!)
ret
Now goes x64
add(int, int):
push rbp
mov rbp, rsp
(?) where is `sub rsp, X`?
mov DWORD PTR [rbp-20], edi
mov DWORD PTR [rbp-24], esi
mov DWORD PTR [rbp-4], 16
mov edx, DWORD PTR [rbp-20]
mov eax, DWORD PTR [rbp-24]
add edx, eax
mov eax, DWORD PTR [rbp-4]
add eax, edx
(?) where is `mov rsp, rbp` before popping rbp?
pop rbp
ret
main:
push rbp
mov rbp, rsp
mov esi, 4
mov edi, 3
call add(int, int)
mov eax, 0
(?) where is `mov rsp, rbp` before popping rbp?
pop rbp
ret
As you can see, my main confusion is that when I compile against x86 - I see what I expect. When it's x64 - I miss leave instruction or exact following sequence: mov rsp, rbp then pop rbp. What's worng?
UPDATE
It seems like leave is missing, just because it wasn't altered previously. But then, goes another question - why there is no allocation for local vars in the frame?
To this question #melpomene gives pretty straightforward answer - because of "red zone". Which basically means the function that calls no further functions (leaf) can use the first 128 bytes below the stack without allocating space. So if I insert a call inside an add() to any other dumb function - sub rsp, X and add rsp, X will be added to prologue and epilogue respectively.
Our code is written in C++ 11 (VS2012/Win 7-64bit). The C++ library provides a sleep_for function that we use. We observed that the C++ sleep_for sometimes shows a large overshoot. In other words we request to sleep for say 15 ms but the sleep turns out to be e.g. 100 ms. We see this when the load on the system is high.
My first reaction: “of course the sleeps "take longer" if there is a lot of load on the system and other threads are using the CPU”.
However the “funny” thing is that if we replace the sleep_for by a Windows API “Sleep” call then we do not see this behavior. I also saw that the sleep_for function under water makes a call to the Window API Sleep method.
The documentation for sleep_for states:
The function blocks the calling thread for at least the time that's specified by Rel_time. This function does not throw any exceptions.
So technically the function is working. However we did not expect to see a difference between C++ sleep_for and the regular Sleep(Ex) function.
Can somebody explain this behavior?
There is quite a bit of additional code executed if using sleep_for vs SleepEx.
For example calling SleepEx(15) generates the following assembly in debug mode (Visual Studio 2015):
; 9 : SleepEx(15, false);
mov esi, esp
push 0
push 15 ; 0000000fH
call DWORD PTR __imp__SleepEx#8
cmp esi, esp
call __RTC_CheckEsp
By contrast this code
const std::chrono::milliseconds duration(15);
std::this_thread::sleep_for(duration);
Generates the following:
; 9 : std::this_thread::sleep_for(std::chrono::milliseconds(15));
mov DWORD PTR $T1[ebp], 15 ; 0000000fH
lea eax, DWORD PTR $T1[ebp]
push eax
lea ecx, DWORD PTR $T2[ebp]
call duration
push eax
call sleep_for
add esp, 4
This calls into:
duration PROC ; std::chrono::duration<__int64,std::ratio<1,1000> >::duration<__int64,std::ratio<1,1000> ><int,void>, COMDAT
; _this$ = ecx
; 113 : { // construct from representation
push ebp
mov ebp, esp
sub esp, 204 ; 000000ccH
push ebx
push esi
push edi
push ecx
lea edi, DWORD PTR [ebp-204]
mov ecx, 51 ; 00000033H
mov eax, -858993460 ; ccccccccH
rep stosd
pop ecx
mov DWORD PTR _this$[ebp], ecx
; 112 : : _MyRep(static_cast<_Rep>(_Val))
mov eax, DWORD PTR __Val$[ebp]
mov eax, DWORD PTR [eax]
cdq
mov ecx, DWORD PTR _this$[ebp]
mov DWORD PTR [ecx], eax
mov DWORD PTR [ecx+4], edx
; 114 : }
mov eax, DWORD PTR _this$[ebp]
pop edi
pop esi
pop ebx
mov esp, ebp
pop ebp
ret 4
duration ENDP
And calls into
sleep_for PROC ; std::this_thread::sleep_for<__int64,std::ratio<1,1000> >, COMDAT
; 151 : { // sleep for duration
push ebp
mov ebp, esp
sub esp, 268 ; 0000010cH
push ebx
push esi
push edi
lea edi, DWORD PTR [ebp-268]
mov ecx, 67 ; 00000043H
mov eax, -858993460 ; ccccccccH
rep stosd
mov eax, DWORD PTR ___security_cookie
xor eax, ebp
mov DWORD PTR __$ArrayPad$[ebp], eax
; 152 : stdext::threads::xtime _Tgt = _To_xtime(_Rel_time);
mov eax, DWORD PTR __Rel_time$[ebp]
push eax
lea ecx, DWORD PTR $T1[ebp]
push ecx
call to_xtime
add esp, 8
mov edx, DWORD PTR [eax]
mov DWORD PTR $T2[ebp], edx
mov ecx, DWORD PTR [eax+4]
mov DWORD PTR $T2[ebp+4], ecx
mov edx, DWORD PTR [eax+8]
mov DWORD PTR $T2[ebp+8], edx
mov eax, DWORD PTR [eax+12]
mov DWORD PTR $T2[ebp+12], eax
mov ecx, DWORD PTR $T2[ebp]
mov DWORD PTR __Tgt$[ebp], ecx
mov edx, DWORD PTR $T2[ebp+4]
mov DWORD PTR __Tgt$[ebp+4], edx
mov eax, DWORD PTR $T2[ebp+8]
mov DWORD PTR __Tgt$[ebp+8], eax
mov ecx, DWORD PTR $T2[ebp+12]
mov DWORD PTR __Tgt$[ebp+12], ecx
; 153 : sleep_until(&_Tgt);
lea eax, DWORD PTR __Tgt$[ebp]
push eax
call sleep_until
add esp, 4
; 154 : }
push edx
mov ecx, ebp
push eax
lea edx, DWORD PTR $LN5#sleep_for
call #_RTC_CheckStackVars#8
pop eax
pop edx
pop edi
pop esi
pop ebx
mov ecx, DWORD PTR __$ArrayPad$[ebp]
xor ecx, ebp
call #__security_check_cookie#4
add esp, 268 ; 0000010cH
cmp ebp, esp
call __RTC_CheckEsp
mov esp, ebp
pop ebp
ret 0
npad 3
$LN5#sleep_for:
DD 1
DD $LN4#sleep_for
$LN4#sleep_for:
DD -24 ; ffffffe8H
DD 16 ; 00000010H
DD $LN3#sleep_for
$LN3#sleep_for:
DB 95 ; 0000005fH
DB 84 ; 00000054H
DB 103 ; 00000067H
DB 116 ; 00000074H
DB 0
sleep_for ENDP
Some conversion happens:
to_xtime PROC ; std::_To_xtime<__int64,std::ratio<1,1000> >, COMDAT
; 758 : { // convert duration to xtime
push ebp
mov ebp, esp
sub esp, 348 ; 0000015cH
push ebx
push esi
push edi
lea edi, DWORD PTR [ebp-348]
mov ecx, 87 ; 00000057H
mov eax, -858993460 ; ccccccccH
rep stosd
mov eax, DWORD PTR ___security_cookie
xor eax, ebp
mov DWORD PTR __$ArrayPad$[ebp], eax
; 759 : xtime _Xt;
; 760 : if (_Rel_time <= chrono::duration<_Rep, _Period>::zero())
lea eax, DWORD PTR $T7[ebp]
push eax
call duration_zero ; std::chrono::duration<__int64,std::ratio<1,1000> >::zero
add esp, 4
push eax
mov ecx, DWORD PTR __Rel_time$[ebp]
push ecx
call chronos_operator ; std::chrono::operator<=<__int64,std::ratio<1,1000>,__int64,std::ratio<1,1000> >
add esp, 8
movzx edx, al
test edx, edx
je SHORT $LN2#To_xtime
; 761 : { // negative or zero relative time, return zero
; 762 : _Xt.sec = 0;
xorps xmm0, xmm0
movlpd QWORD PTR __Xt$[ebp], xmm0
; 763 : _Xt.nsec = 0;
mov DWORD PTR __Xt$[ebp+8], 0
; 764 : }
; 765 : else
jmp $LN3#To_xtime
$LN2#To_xtime:
; 766 : { // positive relative time, convert
; 767 : chrono::nanoseconds _T0 =
; 768 : chrono::system_clock::now().time_since_epoch();
lea eax, DWORD PTR $T5[ebp]
push eax
lea ecx, DWORD PTR $T6[ebp]
push ecx
call system_clock_now ; std::chrono::system_clock::now
add esp, 4
mov ecx, eax
call time_since_ephoch ; std::chrono::time_point<std::chrono::system_clock,std::chrono::duration<__int64,std::ratio<1,10000000> > >::time_since_epoch
push eax
lea ecx, DWORD PTR __T0$8[ebp]
call duration ; std::chrono::duration<__int64,std::ratio<1,1000000000> >::duration<__int64,std::ratio<1,1000000000> ><__int64,std::ratio<1,10000000>,void>
; 769 : _T0 += _Rel_time;
mov eax, DWORD PTR __Rel_time$[ebp]
push eax
lea ecx, DWORD PTR $T4[ebp]
call duration_ratio ; std::chrono::duration<__int64,std::ratio<1,1000000000> >::duration<__int64,std::ratio<1,1000000000> ><__int64,std::ratio<1,1000>,void>
lea ecx, DWORD PTR $T4[ebp]
push ecx
lea ecx, DWORD PTR __T0$8[ebp]
call duration_ratio ; std::chrono::duration<__int64,std::ratio<1,1000000000> >::operator+=
; 770 : _Xt.sec = chrono::duration_cast<chrono::seconds>(_T0).count();
lea eax, DWORD PTR __T0$8[ebp]
push eax
lea ecx, DWORD PTR $T3[ebp]
push ecx
call duration_cast ; std::chrono::duration_cast<std::chrono::duration<__int64,std::ratio<1,1> >,__int64,std::ratio<1,1000000000> >
add esp, 8
mov ecx, eax
call duration_count ; std::chrono::duration<__int64,std::ratio<1,1> >::count
mov DWORD PTR __Xt$[ebp], eax
mov DWORD PTR __Xt$[ebp+4], edx
; 771 : _T0 -= chrono::seconds(_Xt.sec);
lea eax, DWORD PTR __Xt$[ebp]
push eax
lea ecx, DWORD PTR $T1[ebp]
call duration_ratio ; std::chrono::duration<__int64,std::ratio<1,1> >::duration<__int64,std::ratio<1,1> ><__int64,void>
push eax
lea ecx, DWORD PTR $T2[ebp]
call duration_ratio ; std::chrono::duration<__int64,std::ratio<1,1000000000> >::duration<__int64,std::ratio<1,1000000000> ><__int64,std::ratio<1,1>,void>
lea ecx, DWORD PTR $T2[ebp]
push ecx
lea ecx, DWORD PTR __T0$8[ebp]
call duration_ratio ; std::chrono::duration<__int64,std::ratio<1,1000000000> >::operator-=
; 772 : _Xt.nsec = (long)_T0.count();
lea ecx, DWORD PTR __T0$8[ebp]
call duration_ratio ; std::chrono::duration<__int64,std::ratio<1,1000000000> >::count
mov DWORD PTR __Xt$[ebp+8], eax
$LN3#To_xtime:
; 773 : }
; 774 : return (_Xt);
mov eax, DWORD PTR $T9[ebp]
mov ecx, DWORD PTR __Xt$[ebp]
mov DWORD PTR [eax], ecx
mov edx, DWORD PTR __Xt$[ebp+4]
mov DWORD PTR [eax+4], edx
mov ecx, DWORD PTR __Xt$[ebp+8]
mov DWORD PTR [eax+8], ecx
mov edx, DWORD PTR __Xt$[ebp+12]
mov DWORD PTR [eax+12], edx
mov eax, DWORD PTR $T9[ebp]
; 775 : }
push edx
mov ecx, ebp
push eax
lea edx, DWORD PTR $LN8#To_xtime
call #_RTC_CheckStackVars#8
pop eax
pop edx
pop edi
pop esi
pop ebx
mov ecx, DWORD PTR __$ArrayPad$[ebp]
xor ecx, ebp
call #__security_check_cookie#4
add esp, 348 ; 0000015cH
cmp ebp, esp
call __RTC_CheckEsp
mov esp, ebp
pop ebp
ret 0
$LN8#To_xtime:
DD 2
DD $LN7#To_xtime
$LN7#To_xtime:
DD -24 ; ffffffe8H
DD 16 ; 00000010H
DD $LN5#To_xtime
DD -40 ; ffffffd8H
DD 8
DD $LN6#To_xtime
$LN6#To_xtime:
DB 95 ; 0000005fH
DB 84 ; 00000054H
DB 48 ; 00000030H
DB 0
$LN5#To_xtime:
DB 95 ; 0000005fH
DB 88 ; 00000058H
DB 116 ; 00000074H
DB 0
to_xtime ENDP
Eventually the imported function gets called, the same one SleepEx has used.
sleep_until PROC ; std::this_thread::sleep_until, COMDAT
; 131 : { // sleep until _Abs_time
push ebp
mov ebp, esp
sub esp, 192 ; 000000c0H
push ebx
push esi
push edi
lea edi, DWORD PTR [ebp-192]
mov ecx, 48 ; 00000030H
mov eax, -858993460 ; ccccccccH
rep stosd
; 132 : _Thrd_sleep(_Abs_time);
mov esi, esp
mov eax, DWORD PTR __Abs_time$[ebp]
push eax
call DWORD PTR __imp___Thrd_sleep
add esp, 4
cmp esi, esp
call __RTC_CheckEsp
; 133 : }
pop edi
pop esi
pop ebx
add esp, 192 ; 000000c0H
cmp ebp, esp
call __RTC_CheckEsp
mov esp, ebp
pop ebp
ret 0
sleep_until ENDP
You should also be aware even SleepEx may not give 100% exact results as per the MSDN documentation https://msdn.microsoft.com/en-us/library/windows/desktop/ms686307(v=vs.85).aspx
This function causes a thread to relinquish the remainder of its time slice and become unrunnable for an interval based on the value of dwMilliseconds. The system clock "ticks" at a constant rate. If dwMilliseconds is less than the resolution of the system clock, the thread may sleep for less than the specified length of time. If dwMilliseconds is greater than one tick but less than two, the wait can be anywhere between one and two ticks, and so on. To increase the accuracy of the sleep interval, call the timeGetDevCaps function to determine the supported minimum timer resolution and the timeBeginPeriod function to set the timer resolution to its minimum. Use caution when calling timeBeginPeriod, as frequent calls can significantly affect the system clock, system power usage, and the scheduler. If you call timeBeginPeriod, call it one time early in the application and be sure to call the timeEndPeriod function at the very end of the application.
I'm using a proprietary DLL (CP5200.dll) to communicate with 10 scrolling message LED signs. I'm using openFrameworks generate and save images of the text I want to display, and then using the DLL to package the images into data the sign can process.
I call the following functions a few times a minute, and they return a file of 2-4kb , depending on image size, but at a certain point - around 3hrs 45 minutes after startup, they start returning files of 128 bytes, which result in a blank LED display when uploaded. I'm hypothesizing that there's a buffer inside the dll that doesn't get emptied, or something of the sort, but I can't make sense of the decompiled code.
Here are the functions:
int CP5200_Program_AddPicture(HOBJECT hObj, int nWinNo, const char* pPictFile, int nMode, int nEffect, int nSpeed, int nStay, int nCompress)
int CP5200_Program_SaveToFile(HOBJECT hObj, const char* pFilename)
Decompiled functions:
Exported fn(): CP5200_Program_AddImage - Ord:00C3h
:1000FD20 51 push ecx
:1000FD21 55 push ebp
:1000FD22 8B6C240C mov ebp, dword ptr [esp+0C]
:1000FD26 85ED test ebp, ebp
:1000FD28 7508 jne 1000FD32
:1000FD2A 83C8FF or eax, FFFFFFFF
:1000FD2D 5D pop ebp
:1000FD2E 59 pop ecx
:1000FD2F C23000 ret 0030
Function fully disassembled. I couldn't manage to decompile.
:1000FC50 ; Exported entry 15. CP5200_Program_AddPicture
:1000FC50
:1000FC50 ; ¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦ S U B R O U T I N E ¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦
:1000FC50
:1000FC50
:1000FC50 public CP5200_Program_AddPicture
:1000FC50 CP5200_Program_AddPicture proc near
:1000FC50
:1000FC50 arg_0 = dword ptr 8
:1000FC50 arg_4 = dword ptr 0Ch
:1000FC50 arg_8 = dword ptr 10h
:1000FC50 arg_C = dword ptr 14h
:1000FC50 arg_10 = dword ptr 18h
:1000FC50 arg_14 = dword ptr 1Ch
:1000FC50 arg_18 = dword ptr 20h
:1000FC50 arg_1C = dword ptr 24h
:1000FC50
:1000FC50 push ebx
:1000FC51 mov ebx, [esp+arg_0]
:1000FC55 test ebx, ebx
:1000FC57 jnz short loc_1000FC60
:1000FC59 or eax, 0FFFFFFFFh
:1000FC5C pop ebx
:1000FC5D retn 20h
:1000FC60 ; ---------------------------------------------------------------------------
:1000FC60
:1000FC60 loc_1000FC60: ; CODE XREF: CP5200_Program_AddPicture+7j
:1000FC60 push esi
:1000FC61 push edi
:1000FC62 mov edi, ebx
:1000FC64 mov esi, offset aCprogram ; "CProgram"
:1000FC69 mov ecx, 9
:1000FC6E xor eax, eax
:1000FC70 repe cmpsb
:1000FC72 jz short loc_1000FC79
:1000FC74 sbb eax, eax
:1000FC76 sbb eax, 0FFFFFFFFh
:1000FC79
:1000FC79 loc_1000FC79: ; CODE XREF: CP5200_Program_AddPicture+22j
:1000FC79 test eax, eax
:1000FC7B jz short loc_1000FC86
:1000FC7D pop edi
:1000FC7E pop esi
:1000FC7F or eax, 0FFFFFFFFh
:1000FC82 pop ebx
:1000FC83 retn 20h
:1000FC86 ; ---------------------------------------------------------------------------
:1000FC86
:1000FC86 loc_1000FC86: ; CODE XREF: CP5200_Program_AddPicture+2Bj
:1000FC86 mov esi, [esp+8+arg_4]
:1000FC8A test esi, esi
:1000FC8C jl short loc_1000FD07
:1000FC8E mov ecx, ebx
:1000FC90 call sub_10018020
:1000FC95 cmp esi, eax
:1000FC97 jge short loc_1000FD07
:1000FC99 push esi
:1000FC9A mov ecx, ebx
:1000FC9C call sub_10018030
:1000FCA1 push 3Eh
:1000FCA3 mov edi, eax
:1000FCA5 call ??2#YAPAXI#Z ; operator new(uint)
:1000FCAA add esp, 4
:1000FCAD test eax, eax
:1000FCAF jz short loc_1000FCBE
:1000FCB1 mov ecx, eax
:1000FCB3 call sub_100012E0
:1000FCB8 mov esi, eax
:1000FCBA test esi, esi
:1000FCBC jnz short loc_1000FCC9
:1000FCBE
:1000FCBE loc_1000FCBE: ; CODE XREF: CP5200_Program_AddPicture+5Fj
:1000FCBE pop edi
:1000FCBF pop esi
:1000FCC0 mov eax, 0FFFFFFFCh
:1000FCC5 pop ebx
:1000FCC6 retn 20h
:1000FCC9 ; ---------------------------------------------------------------------------
:1000FCC9
:1000FCC9 loc_1000FCC9: ; CODE XREF: CP5200_Program_AddPicture+6Cj
:1000FCC9 mov eax, [esp+8+arg_8]
:1000FCCD push eax
:1000FCCE lea ecx, [esi+25h]
:1000FCD1 call sub_100076A0
:1000FCD6 mov ecx, [esp+8+arg_C]
:1000FCDA mov edx, [esp+8+arg_10]
:1000FCDE mov eax, [esp+8+arg_14]
:1000FCE2 mov [esi+2Ah], ecx
:1000FCE5 mov ecx, [esp+8+arg_18]
:1000FCE9 mov [esi+2Eh], edx
:1000FCEC mov edx, [esp+8+arg_1C]
:1000FCF0 mov [esi+36h], ecx
:1000FCF3 push esi
:1000FCF4 mov ecx, edi
:1000FCF6 mov [esi+32h], eax
:1000FCF9 mov [esi+3Ah], edx
:1000FCFC call sub_100015A0
:1000FD01 pop edi
:1000FD02 pop esi
:1000FD03 pop ebx
:1000FD04 retn 20h
:1000FD0F CP5200_Program_AddPicture endp
Exported fn(): CP5200_Program_SaveToFile - Ord:0013h
:1000CE80 8B542404 mov edx, dword ptr [esp+04]
:1000CE84 85D2 test edx, edx
:1000CE86 741F je 1000CEA7
:1000CE88 56 push esi
:1000CE89 57 push edi
:1000CE8A 8BFA mov edi, edx
:1000CE8C BE8C060610 mov esi, 1006068C
:1000CE91 B909000000 mov ecx, 00000009
:1000CE96 33C0 xor eax, eax
:1000CE98 F3 repz
:1000CE99 A6 cmpsb
:1000CE9A 5F pop edi
:1000CE9B 5E pop esi
:1000CE9C 7405 je 1000CEA3
:1000CE9E 1BC0 sbb eax, eax
:1000CEA0 83D8FF sbb eax, FFFFFFFF
I'm writing in VS 2012, with openframeworks version of_v0.8.3_vs_release.
Does the decompiled code give any clues as to what's happening in the DLL that causes this behavior?
Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 9 years ago.
Improve this question
I were asked to analyze an assembly code, which was generated from following c++ code in Visual studio IDE:
here is c++ code:
int plus(int a,int b);
int main()
{
cout<<plus(2,4);
getchar();
return 0;
}
int plus(int a,int b)
{
static int t=2;
return a+b+t;
}
And here is the assembly code (the reduced form):
_main PROC ; COMDAT
; 8 : {
push ebp
mov ebp, esp
sub esp, 192 ; 000000c0H
push ebx
push esi
push edi
lea edi, DWORD PTR [ebp-192]
mov ecx, 48 ; 00000030H
mov eax, -858993460 ; ccccccccH
rep stosd
; 9 : cout<<plus(2,4);
push 4
push 2
call ?plus##YAHHH#Z ; plus
add esp, 8
mov esi, esp
push eax
mov ecx, DWORD PTR __imp_?cout#std##3V?$basic_ostream#DU?$char_traits#D#std###1#A
call DWORD PTR __imp_??6?$basic_ostream#DU?$char_traits#D#std###std##QAEAAV01#H#Z
cmp esi, esp
call __RTC_CheckEsp
; 10 : getchar();
mov esi, esp
call DWORD PTR __imp__getchar
cmp esi, esp
call __RTC_CheckEsp
; 11 : return 0;
xor eax, eax
; 12 : }
pop edi
pop esi
pop ebx
add esp, 192 ; 000000c0H
cmp ebp, esp
call __RTC_CheckEsp
mov esp, ebp
pop ebp
ret 0
_main ENDP
; Function compile flags: /Odtp /RTCsu /ZI
_TEXT ENDS
; COMDAT ?plus##YAHHH#Z
_TEXT SEGMENT
_a$ = 8 ; size = 4
_b$ = 12 ; size = 4
?plus##YAHHH#Z PROC ; plus, COMDAT
; 15 : {
push ebp
mov ebp, esp
sub esp, 192 ; 000000c0H
push ebx
push esi
push edi
lea edi, DWORD PTR [ebp-192]
mov ecx, 48 ; 00000030H
mov eax, -858993460 ; ccccccccH
rep stosd
; 16 : static int t=2;
; 17 : return a+b+t;
mov eax, DWORD PTR _a$[ebp]
add eax, DWORD PTR _b$[ebp]
add eax, DWORD PTR ?t#?1??plus##YAHHH#Z#4HA
; 18 : }
pop edi
pop esi
pop ebx
mov esp, ebp
pop ebp
ret 0
?plus##YAHHH#Z ENDP ; plus
_TEXT ENDS
END
I have to find how does the code deal with stack and how variables stored and retrieved?
Regards.
push 4
push 2
call ?plus##YAHHH#Z
This pushes the values 4 and 2 onto the stack (reverse order to how you'd think of them in C, remember 2 is now on top of 4), then calls plus.
mov eax, DWORD PTR _a$[ebp]
add eax, DWORD PTR _b$[ebp]
add eax, DWORD PTR ?t#?1??plus##YAHHH#Z#4HA
pop edi
pop esi
pop ebx
mov esp, ebp
pop ebp
ret 0
I've ignored some stack fiddling at the top of plus, but this moves a from the stack into eax, adds b to it (which it gets from the stack) then adds t to it (I'm not familiar with MASM at all so I'm not actually sure where it gets t from). You can see the stack offsets of a and b have been stored into _a and _b as 8 and 12 further up the code. This is performed in %eax because this is where you stick the first return value of a function. There's some stack clearing and then the usual function epilogue before returning. The main code then pushes %eax onto the stack and calls the iostream stuff, which will pop it off and output it to screen.
Here is the complete tutorial:
http://www.codeproject.com/KB/cpp/reversedisasm.aspx
Please ask a specific question if you have ? Your original question is too broad.
__RTC_CheckEsp is a call that verifies the correctness of the esp, stack, register. It is called to ensure that the value of the esp was saved across a function call.
Anyone knows how it's implemented?
Well a little bit of inspection of the assembler gives it away
0044EE35 mov esi,esp
0044EE37 push 3039h
0044EE3C mov ecx,dword ptr [ebp-18h]
0044EE3F add ecx,70h
0044EE42 mov eax,dword ptr [ebp-18h]
0044EE45 mov edx,dword ptr [eax+70h]
0044EE48 mov eax,dword ptr [edx+0Ch]
0044EE4B call eax
0044EE4D cmp esi,esp
0044EE4F call #ILT+6745(__RTC_CheckEsp) (42BA5Eh)
There are 2 lines to note in this. First note at 0x44ee35 it stores the current value of esp to esi.
Then after the function call is completed it does a cmp between esp and esi. They should both be the same now. If they aren't then someone has either unwound the stack twice or not unwound it.
The _RTC_CheckEsp function looks like this:
_RTC_CheckEsp:
00475A60 jne esperror (475A63h)
00475A62 ret
esperror:
00475A63 push ebp
00475A64 mov ebp,esp
00475A66 sub esp,0
00475A69 push eax
00475A6A push edx
00475A6B push ebx
00475A6C push esi
00475A6D push edi
00475A6E mov eax,dword ptr [ebp+4]
00475A71 push 0
00475A73 push eax
00475A74 call _RTC_Failure (42C34Bh)
00475A79 add esp,8
00475A7C pop edi
00475A7D pop esi
00475A7E pop ebx
00475A7F pop edx
00475A80 pop eax
00475A81 mov esp,ebp
00475A83 pop ebp
00475A84 ret
As you can see the first thing it check is whether the result of the earlier comparison were "not equal" ie esi != esp. If thats the case then it jumps to the failure code. If they ARE the same then the function simply returns.
If you're any good at asm, maybe this helps:
jne (Jump if Not Equal) - jumps if the ZERO flag is NZ (NotZero)
_RTC_CheckEsp:
004C8690 jne esperror (4C8693h)
004C8692 ret
esperror:
004C8693 push ebp
004C8694 mov ebp,esp
004C8696 sub esp,0
004C8699 push eax
004C869A push edx
004C869B push ebx
004C869C push esi
004C869D push edi
004C869E mov eax,dword ptr [ebp+4]
004C86A1 push 0
004C86A3 push eax
004C86A4 call _RTC_Failure (4550F8h)
004C86A9 add esp,8
004C86AC pop edi
004C86AD pop esi
004C86AE pop ebx
004C86AF pop edx
004C86B0 pop eax
004C86B1 mov esp,ebp
004C86B3 pop ebp
004C86B4 ret
004C86B5 int 3
004C86B6 int 3
004C86B7 int 3
004C86B8 int 3
004C86B9 int 3
004C86BA int 3
004C86BB int 3
004C86BC int 3
004C86BD int 3
004C86BE int 3
004C86BF int 3