visual studio 6.0 debug mode - stack (esp, sbp, eip)
|
rett |
|
test 함수 |
D=4 |
|
C=3 | ||
|
| |
|
| |
A |
| |
B | ||
main의 stack |
|
← esp: main
|
B |
← ebp: main | |
A | ||
|
init |
|
main에서 test 호출
인자 2개인 경우 stack에 푸쉬
test(4,3);
1. push 3
2. push 4
3. call test → 1) push eip 2) eip = &test
( 1),2)는 실제 코드는 아니고 이런 동작을 한다는 것을 보여주며 실제 코드는 1. 2. 3. 번이다.)
visual studio로 디버그
F5 debug → view → register
⇒ EIP, ESP, EBP를 추적한다.
⇒ EIP : 004010748
⇒ ESP : 0012FF84 → 0012FF7C
⇒ EBP : 0012FFC0 → 0012FF80
&A → 0x0012ff7C
&C → 0x0012ff74
main → 0x00401020 main(void)
- disassembly를 하면
main 함수
00401020 main → 55 8B EC 83
push ebp → 55가 나온다. (401020 → main 주소와 같다.)
move ebp, esp → 8B EC가 나온다. (401021)
sub esp,50h → esp-50이란 뜻(sub는 마이너스의 의미)이며 3byte 차지(401023)
※ disassembly에서 소스위에 있는 기계어들을 init 함수라 하며 main함수가 호출하는 것으로 확인할 수 있듯이 제일 처음 실행된다.(엔트리 포인트 - 진입점)
test2 함수
004010B0 test2 → 55 8B EC 83
push ebp → 55가 나온다. (4010B0 → main 주소와 같다.)
move ebp, esp → 8B EC가 나온다. (4010B3)
sub esp,50h → esp-40이란 뜻(sub는 마이너스의 의미)이며 3byte 차지(4010B6)
main 함수 분석
⇒ int A=1;
00401038 mov dword ptr[ebp-4], 1
dword → double word (처음에 word는 16bit였다. 즉, double word는 4byte)
ptr → pointer
ebp → 주소(0012FF80)이며 즉, ebp - 4 = 0012FF80 -4 = 0015FF7C (&A의 주소값)
ptr[ebp -4]는 *0012FF7C =1; 이며 이것은 &A자리에 1을 넣으라(move)는 뜻
※ mov → 이동하다.(대입연산자)
ebp = esp →(어셈블리로 만들면) mov ebp, esp
|
20 |
ret |
|
test 함수 |
24 |
D=4 |
|
12FF28 |
C=3 | ||
2C
|
main stack ebp(old ebp) |
| |
RET |
| ||
A |
| ||
B | |||
main의 stack (12칸) |
|
|
← esp: main
|
B |
| ||
A | |||
|
12FF80 |
stack ebp RET(retrurn address)
init |
← ebp: main
|
※ visual C는 컴파일 할때 main 함수 영역에 ebp 와 esp가 동일선상에 있지 않고 변수가 적다면 esp가 48byte 만큼(12칸) 올라가 있다. 만약 변수가 A와 B 두 개라면 A와 B 위에 10칸을 남겨두고 esp는 위치해 있는 것.
- ebp는 항상 변수의 주소값의 기준이다.
- init의 위로 main 함수가 push되면 esp가 올라오며 ebp값을 끌어 올린다.
[disassmble]
--- d:\linux 공유폴더\20110428_stackview\main.c ------------------------
1: #include <stdio.h>
2:
3: void test(int A, int B);
4: void test2();
5:
6: int main()
7: {
00401030 push ebp /* init에 있는 ebp값을 main 함수 아래에 놓는다.(이곳에는 ebp 값이 저장되어 있고 나중에 return address 를 통해 ebp가 반환된다.) */
00401031 mov ebp,esp // ebp에 esp를 대입한다.
00401033 sub esp,4Ch /* esp에 4C 만큼을 뺀다. 이것은 visual C에서 임의로 main 함수의 간격을 정한 것으로 esp는 4C만큼 주소값이 작아져 stack의 위쪽으로 올라간다. main함수의 시작에서 올라간 esp만큼을 main 함수의 stack 이라 한다. push부터 sub까지의 3개의 코드를 Ent교 code라 한다.*/
00401036 push ebx
00401037 push esi
00401038 push edi
00401039 lea edi,[ebp-4Ch]
0040103C mov ecx,13h
00401041 mov eax,0CCCCCCCCh
00401046 rep stos dword ptr [edi]
8: int A=1;
00401048 mov dword ptr [ebp-4],1 /* dword는 double word를 말하는 것으로 여기서 word는 16bit 컴퓨터의 개념인 2byte로 총 4byte를 말한다. ptr은 pointer의 의미이며 ebp의 값인 0012FF80에서 4를 뺀 것으로 0012FF7C가 되어 이것은 A의 주소가 되며 ,1은 &A의 자리에 1을 대입하라는 뜻이다. */
9: int B=2;
0040104F mov dword ptr [ebp-8],2 /* 0012FF78의 주소에 B가 생성되어 2가 대입되었다.*/
10: unsigned char *P = (unsigned char *)&B;
00401056 lea eax,[ebp-8]
00401059 mov dword ptr [ebp-0Ch],eax
11:
12: printf("&main(): %08X\n&test(): %08X\n\n", main, test);
0040105C push offset @ILT+10(_test) (0040100f)
00401061 push offset @ILT+5(_main) (0040100a)
00401066 push offset string "&main(): %08X\n&test(): %08X\n\n" (00420054)
0040106B call printf (004012b0)
00401070 add esp,0Ch
13: printf("&A: %08X\n&B: %08X\n\n", &A, &B);
00401073 lea ecx,[ebp-8]
00401076 push ecx
00401077 lea edx,[ebp-4]
0040107A push edx
0040107B push offset string "&A: %08X\n&B: %08X\n\n" (0042003c)
00401080 call printf (004012b0)
00401085 add esp,0Ch
14: printf(" B: %02X %02X %02X %02X\n\n", *P, *(P+1), *(P+2), *(P+3));
00401088 mov eax,dword ptr [ebp-0Ch]
0040108B xor ecx,ecx
0040108D mov cl,byte ptr [eax+3]
00401090 push ecx
00401091 mov edx,dword ptr [ebp-0Ch]
00401094 xor eax,eax
00401096 mov al,byte ptr [edx+2]
00401099 push eax
0040109A mov ecx,dword ptr [ebp-0Ch]
0040109D xor edx,edx
0040109F mov dl,byte ptr [ecx+1]
004010A2 push edx
004010A3 mov eax,dword ptr [ebp-0Ch]
004010A6 xor ecx,ecx
004010A8 mov cl,byte ptr [eax]
004010AA push ecx
004010AB push offset string " B: %02X %02X %02X %02X\n\n" (0042001c)
004010B0 call printf (004012b0)
004010B5 add esp,14h
15:
16: test(4, 3);
004010B8 push 3 // stack 3을 넣는다.
004010BA push 4 // stack 4를 넣는다.
004010BC call @ILT+10(_test) (0040100f) /* ① push eip(→instruction pointer) ② eip =&test
004010C1 add esp,8
17: test2();
004010C4 call @ILT+0(_test2) (00401005)
18:
19: return 0;
004010C9 xor eax,eax
20: }
004010CB pop edi
004010CC pop esi
004010CD pop ebx
004010CE add esp,4Ch
004010D1 cmp ebp,esp
004010D3 call __chkesp (00401330) /* 10CB부터 여기까지는 해킹을 방지하기 위해 visual studio 에서 자동으로 삽입하는 것. */
004010D8 mov esp,ebp /* ebp를 esp에 대입한다. 즉, esp의 위치를 원상복구 시킨다. */
004010DA pop ebp // ebp를 삽입한다.① eip = *esp, ② esp =eip+4
004010DB ret
⇒ move ebp, esp → ebp = esp
sub esp,50h → esp = esp-0x50
ret
→ 위의 3개의 코드를 exit code라하고 우리가 C 소스에서 return ;을 하면 삽입된다.
⇒ stack과 stack 사이에는 return address, 인자, main ebp 등이 있다.
'내장형 하드웨어 > C언어' 카테고리의 다른 글
함수 전달의 3가지 - 참조(reference), 주소(address), 값(value), 재귀함수, touch, ifndef (0) | 2011.05.04 |
---|---|
stack 구조, 어셈블리 (MASM, 코드 분석) (0) | 2011.04.29 |
assembler - stack의 이해 (ebp, esp, eip) (0) | 2011.04.27 |
메모리 영역 (Code, Data, BSS, HEAP, Stack), Little Endian, Stack의 이해 (1) | 2011.04.26 |
NASM, MASM 설치, 함수 (실인수, 형식인수, 지역변수) (0) | 2011.04.25 |