반응형

다음과 같은 소스로 실습을 진행할 것이다.

정수인자 a,b,c,d를 가진 test_function을 선언했고, 함수의 지역변수로  flag와 10개의 문자를 가진 buffer를 선언했다. 이 변수들의 메모리는 '스택 세그먼트'에 있고, 함수의 기계어 코드는 텍스트 세그먼트에 저장되어 있다.

디스어셈해보면 main은 다음과 같다.

<main>

test_function은 다음과 같다

main() 함수는 0x08048357 부터 시작한다. sub esp,eax(0x08048365) 부분 까지를 우리는 '프롤로그'라 부르며, 스택 프레임을 구성한다

프로그램이 실행되면 test_function()을 호출하는 main()함수가 호출된다.test_function()이 main() 함수에서 호출될 때 다음과 같이 스택의 시작점을 만들기 위해 많은 값이 스택에 푸시된다. test_function()이 호출되면 함수의 인자가 역순으로 스택에 푸시가되므로(FILO구조) 우리는 소스에서 1,2,3,4 순으로 넣엇지만 4,3,2,1순으로 푸시된다. 이 값들은 각기 함수의 d,c,b,a에 대응된다.


어셈블리 명령이 실행되면 복귀 주소가 스택에 푸시되고, 실행 흐름이 test_function()의 시작점인 0x08048344로 점프한다. 


call 명령은 복귀 주소를 스택에 저장하고 EIP를 test_function()의 시작점으로 점프한다. 그래서 test_function()의 프로시저 프롤로그 명령은 스택프레임을 구성하는 것을 끝마치고 , 여기서 EBP의 현재값은 스택에 푸시된다. 이 값을 우리는 앞에 설명한 'SFP'라 한다. 이것은 나중에 EBP를 원상태로 복귀하는데 사용한다.

프레임 포인터는 함수의 지역변수(flag, buffer)를 참조하는데 사용한다.

프로시저 프롤로그가 끝난뒤의 스택 프레임의 모습은 다음과 같다.

 buffer

 flag

 SFP

ret 

a

b

c

d


이제 그러면 GDB를 사용해서 스택에 구성된 스택 프레임을 살펴보도록 하자.

main()에서 test_function()을 호출하기 전과 test_function() 시작 부분에 중지점을 설정할 것이다. 프로그램을 실행하면 레지스터의 ESP, EBP, EIP를 살펴볼 수 있는 곳에서 실행이 멈춘다.

<1번 break>

이 중지점은 test_function() 스택 프레임이 생기기 바로전에 있다. 이것은 이 스택 프레임의 바닥이 ESP의 현재값인 0xbffffa30이라는 것을 의미한다. 다음 중지점은 test_function() 프로시저 프롤로그 바로 다음이라서, 계속하면 스택 프레임이 구성된다. 아래의 결과는 두 번째 중지점에서 비슷한 정보를 보여준다. 지역변수(현재 소스에서는 flag, buffer)는 프레임 포인터(EBP)에서 상대적 위치로 참조된다.

스택에 스택프레임이 구성됬다. 함수의 네 개 인자가 스택프레임의 바닥에 있고, 복귀주소는 바로 그위에 있다(0x804838b) 또 그위에는 앞의 스택 프레임인 EBp(0xbffffa48)이 있고, 나머지 주소는 지역 변수 flag와 buffer 용이다. EBP와의 상대적 주소를 계산에 스택프레임에서의 정확한 위치를 알 수 있다.


함수의 실행이 끝나면 전체 스택 프레임이 스택에서 제거되고 EIP는 함수 호출 전에 하던 작업을 계속 실행할 수 있게 복귀주소로 설정된다. 호출된 함수에서 또 다른 함수가 호출되면 추가적인 스택 프레임이 생성되는 과정이 반복 된다.


(1) 이론 에서 설명했다 싶이 힙과 스택은 모두 크기가 동적으로 변하며 서로를 마주보는 방향으로 증가한다. 이런 구조는 메모리 공간 낭비를 최소화 하며, 힙이 작을 때는 스택이 커질수도 있고, 힙이 클때는 스택이 작아 질 수 있는 구조를 가진다.


반응형
,
반응형

주관적인 생각이지만, 이 개념을 잘 알고 있어야 BOF, ROP 등등의 여러기법을 이해하는데 수월합니다. 또한 설명에 있어서 이하 존칭은 생략하겟습니다.

<큰 그림>

 환경변수

 스택

 힙

데이터 

 코드(=text)

컴파일된 프로그램 메모리는

 텍스트(=code영역), 데이터, BSS,(Block Started Symbol), 힙, 스택의 5개 세그먼트로 나뉜다( 추가적으로 환경변수가 있지만 이번 설명에서 생략)


텍스트 세그먼트(=코드 세그먼트) ~>EIP가 이곳에 존재한다.

이 세그먼트에는 프로그램의 기계어 명령이 들어있다. 이 세그먼트의 명령 실행 순서는 순차적이지 않다. 왜냐하면 하이레벨 언어의 제어 구조가 어셈블리 언어의 branch,jump,call등의 명령으로 변환되기 떄문이다. 프로그램이 실행되면 EIP는 텍스트 세그먼트의 맨처음 위치로 설정된다. 그리고 프로세서는 다음 작업을 반복 실행한다.

정리해보면,

1.EIP가 가리키고 있는 명령을 읽는다

2. 해당 명령의 길이를 EIP에 더한다.

3.1단계에서 읽은 명령을 수행한다.

4.1단계로 돌아간다.


때때로 이 명령이 jump나 call일 경우가 있는데, 이 전 포스팅에서도 말했듯이 EIP는 메모리의 전혀 다른주소로 바뀌게 된다. EIP는 다음 명령어의 주소를 리턴값으로 저장해두고 넘어가게 된다.

또한, 텍스트 세그먼트는 변수가 아닌 코드만을 저장하고 있으므로 쓰기가 금지되어 있다.텍스트 세그먼트에 쓰려는 시도가 있을 경우 운영체제가 그 사실을 사용자에게 알리고 프로그램은 종료된다. 추가적으로 텍스트 세그먼트가 읽기 전용일 때의 장점으로는 한 프로그램을 여러번 실행했을 때에도 텍스트 세그먼트를 공유할 수 있다는 점이다. 그리고 텍스트 세그먼트는 바뀌는 것이 없으므로 크기도 고정되어 있다.


데이터,BSS세그먼트

전역 변수와 정적 프로그램 변수를 저장하는데 쓰인다. 

데이터 세그먼트: 초기화된 전역 변수와 정적변수가 저장된다.

BSS 세그먼트: 초기화되지 않은 전역 변수와 정적 변수가 저장된다.

~> 이 두 세그먼트 모두 쓰기가 가능하지만 크기는 고정되어 있다.


힙 세그먼트(낮은주소 ->높은주소)

프로그래머가 직접 접근할 수 있는 메모리 세그먼트다. 프로그래머가 어떤 것을 필요로 하든지 이 세그먼트의 메모리 블록을 할당해 사용할 수 있다. 힙 세그먼트의 특징은 크기가 고정되어 있지 않다는 것이다. 그래서 힙은 필요에 따라 크기가 커지거나 작아질 수 있다.

~>힙 세그먼트에 들어있는 모든 메모리는 '할당'알고리즘과 '해제'알고리즘에 의해 관리된다. 힙은 얼마나 많은 메모리가 예약 됐는지에 따라 크기가 증가하거나 감소한다.


스택 세그먼트(높은주소->낮은주소)

크기가 간변적이며, 지역 함수 변수를 저장하고 함수 호출시의 임시 컨텍스트를 저장하는데 쓰인다. 프로그램에서 어떤 함수를 호출할 경우 그 함수는 자신만의 변수 공간을 갖는다. 그리고 그함수의 코드는 다른 메모리에 위치해 있는 텍스트 세그먼트에 저장된다.

~>함수가 호출될 떄에는 컨텍스트와 EIP가 변경되어야 하므로 스택은 함수 호출시 전달된 모든 인자와 EIP가 되돌아 가야 할 주소와 함수에서 사용된 모든 지역 변수를 저장하는 데 쓰인다. PUSH 와 POP으로 메모리가 관리되며 FILO (First in Last Out)형태이다.


ESP는 스택의 맨 끝 주소를 추적하는데 사용한다. 이 주소는 스택에 데이터가 추가되거나 제거됨에 따라 계속 바뀐다. 스택은 매우 동적으로 변하므로 크기가 고정돼 있지 않다. 스택은 힙과 달리 높은 메모리주소에서 낮은 메모리 주소로 증가한다.


EBP는 현재 스택 프레임에 있는 지역함수 변수를 참조하는 데 쓰인다. 각 스택 프레임에는 함수의 입력인자, 함수의 로컬 변수, 자신을 호출한 곳으로 되돌아가는데 필요한 정보를 담고 있는 두 포인터, 저장된 프레임 포인터(SFP: Saved Frame Pointer)와 복귀주소가 있다. SFP는 EBP를 원래 값으로 돌리는데 쓰이고, 복귀 주소는 EIP의 값을 함수 호출이 끝난 뒤에 실행해야 할 주소로 되돌리는데 쓰인다.


반응형
,
반응형


전제 : C코드가 기계어로 컴파일돼야 한다!

예제 소스:) 위 소스에서 string.h 헤더가 들어간 이유는 strcpy함수가 위의 헤더에 정의되어 있기 때문이다.


앞의 프로그램에서 20문자 배열은 str_a로 선언되었다. 배열은 [0]번째 부터 값이 들어간다는 것을 알고 있어야한다. 또한, 배열의 맨 마지막에는 '널 바이트' 가 들어가게 된다

하지만 20바이트중에 실질적으로 12바이트만 사용 되었다. 끝에 들어가게되는 '널바이트'는 어떤 함수가 문자열을 처리할 때 여기서 중단하라고 알리기 위한 '구획문자(Delimiter)'로 사용된다.(남은 여분의 바이트들은 쓰레기 값으로 무시된다)


이제 이 프로그램을 GDB로 살펴보자. GDB로 컴파일된 프로그램을 열고 strcpy() 호출 부분과 호출 전과 후에 중지점을 설정할 것이다.

프로그램이 실행되면 strcpy() 중지점이 결정된다. 각 중지점에서 EIP와 EIP가 가리키는 명령을 살펴 볼 것이다.

(원래 세번째 중지점의 진행 사항까지 올리려 했으나 잘려버렷다 ..)

두번째 중지점에서의 EIP주소는 strcpy()함수의 코드가 라이브러리에 있으므로 다르다.

이유는 다음과같다.

결론부터 말하자면 call 명령 때문이다.

자 그러면 생각해보자. EIP는 다음 명령을들 하나하나 읽어가면서 넘어간다. 여기서 EIP는 jmp,call등으로 인해 다른 주소로 넘어갓다고 가정하자. 하지만 돌아와야 할 방법이 필요하다. 예를 들면 우리가 집을 나갓다가 다시 돌아오기 위해서는 집의 위치(주소)를 알고 있어야 한다. 그래서 Call 명령이 사용 될때  다음 명령의 주소가 저장이된 상태로 EIP가 넘어가게 된다.




반응형
,
반응형

gdb 컴파일러 사용시 gdb에서 소스코드를 볼 수 있는 추가 디버그 정보를 포함시키려면 -g 플래그를 사용한다 (이를 사용하면 list 명령을 이용해 소스코드를 gdb 안에서 볼 수 있다)

ex. gcc -g firstprog.c

<disas main>


1.break의 설정,instruction pointer 관찰 해보기

이제 본격적으로 실습에 들어가보도록 하겠다.

main()의 시작점에 중지점(break) 를 설정하고 프로그램을 실행 시킨다. 이 중지점은 디버거가 해당 지점에 가면 프로그램 실행을 중지하도록 한다. 중지점이 main()함수 시작점에 설정됐으므로 프로그램은 main()의 어떤 명령도 실행하기 전에 중지점에 다다르고 멈춘다. 그리고 EIP(instruction pointer)의 값이 출력된다.

여기서 EIP가 main() 함수의 역어셈블 명령을 가리키는 메모리 주소를 갖고 있음을 주시해서 보자. 이메모리 주소 전에 기울임 꼴로 표기된 명령들은 함수의 '프롤로그'라 불리고, main()함수의 나머지 전역 변수를 위한 메모리를 할당하려고 컴파일러가 생성한 것이다.

~>스택프레임을 만든다 라고 이해하면된다.


2.GDB의 조사명령

또한, GDB 는 examine을 줄인 명령 x를 사용해 메모리를 조사하는 방법을 제공한다

형식 : x/조사명령

형식은 다음과 같다

o:8진법으로 보여준다(octal)

x: 16진법으로 보여준다(hexa)

u: 부호헙는 표준 10진법으로 보여준다(unsigned decimal)

t : 2진법으로 보여준다(아마도.. twin(?)정확하지않음)

좀더 이해하기 쉽게 밑에다가 예제를 첨부한다.

참고로 x/u $eip 과 같은 것도 사용할 수가 있는데, 여기서 '$eip'는 그 순간에 EIP가 갖고있는 값을 의미한다

메모리 단일 유닛의 기본 크기는 워드(word)라 불리는 4바이트이다. 


3.print 명령

print 명령은 간단한 계산을 할 때 사용될 수 있고, 결과는 디버거의 임시 변수에 저장된다.print명령은  'print 주소 연산 주소' 와 같은 형식등 여러가지 방법으로 사용해볼 수 있다.어려운 주제는 아니므로 각자 한번씩 실습해보자



반응형
,
반응형

시작하기 전 .. 

 C코드는 컴파일 된다는 사실을 인지하고 있어야한다. 코드는 컴파일돼 실행 가는한 바이너리 파일이 될 때 까지는 아무것도 할 수 없다. C소스코드 자체가 프로그램이라 생각하는 것은  가장 일반적인 오해다.

그리고 어셈블러에는 AT&T 방식과 intel방식이 있는데 여기서 사용한 것은 intel 방식을 사용하고 있으니 숙지하고 내용을 보기 바란다.


objdump나 gdb에대한 자세한 사용방법과 분석방법에대해서는 차차 진행할 것이니 이번 포스팅에서는 열어보고 어떤 내용이 있는지에 대해서만 짚어보고 넘어가도록 하자



다음과 같은 소스 코드를 바이너리 파일로 한번 열어보자 (odjdump 를 사용하여 main()함수를 기계어로 살펴볼 것이다)

<다음과 같은 명령어를 사용한다. 참고로 위의 소스코드를 컴파일 했다고 가정하고 진행한다>

objudmp 사용하기 

objdump -D a.out | grep -A 20 main.:

제일 왼쪽부터 

메모리 주소, 기계어 , 어셈블러가 되시겠다 . 위에도 언급 했듯이 이번 포스팅에서는 어떤내용을 보여주는지에 대해서만 보고넘어가도록 한다.


cf)

 각기 주소를 갖고 있는 집들의 열처럼 메모리도 각기 메모리 주소를 가진 바이트들의 열로 생각할 수 있다. 각 메모리 바이트에 주소로 접근할 수 있다.

~> 컴파일된 프로그램의 기계어 명령을 찾으려면 CPU가 메모리에 접근한다. 


프로세서는 레지스터라는 자신만의 특별한 변수 세트를 갖고있다. 대부분의 명령은 데이터를 읽거나 쓰기 위해 레지스터를 사용한다. 그래서 프로세서의 레지스터를 이해하는 것은 명령을 이해하는데 중요하다(이전에 포스팅한 글 참조하시길...)


gdb 사용하기 

명령)gdb -q a.out -> disas main

위와같은 명령을 사용하면 다음과 같은 어셈블리어들이 쭈루루룩 뜨게 된다. 위의 코드를 분석하는 것은 다음 포스팅에서 진행하도록 하고, 추가적인 명령어들을 말하고 이번 포스팅을 끝내도록 하겠다.(추가적으로 써놓은 명령어들을 한번씩은 꼭 사용해보길 바란다, 다음 포스팅에서 도움이 될지도..?)

break (함수) 또는 주소  : 해당 함수 또는 주소에 break를 건다~ > b 만쓰고 해도된다

run : 브레이크를 건 부분 직전까지 프로그램을 실핸한다 ~> r만써도됨 

continue : 브레이크 다음 진행

info register (추가 옵션 eip, esp 등등 ) : 브레이크 걸려진 상황에서 브레이크 건 직전부분 까지 프로그램을 수행하였을 때의 레지스터들의 상태들을 보여준다.



반응형
,
반응형

EP(Entry Point)

Windows 실행 파일(EXE, DLL, SYS등)의 코드 시작점, 프로그램이 실행될 때 'CPU에 의해 가장 먼저 실행되는 코드 시작위치'

 

 명령어

단축키

설명 

 Restart

ctrl + F2 

다시 처음부터 디버깅 시작(디버깅을 당하는 프로세스를 종료하고 재실행) 

Step Into 

F7 

하나의 OP code 실행(CALL 명령을 만나면, 그 함수 코드 내부로 따라들어감) 

Step Over 

F8 

하나의 OP Code 실행( CALL 명령을 만나면, 따라 들어가지 않고 그냥 함수 자체를 실행)

 Execute till Return

 ctrl + F9

 함수 코드 내에서 RETN 명령어 까지 실행( 함수 탈출 목적)

 

Packer(Run Time Packer)

실행 압축 유틸리티. 실행 파일의 코드, 데이터, 리소스 등을 압축시킴. 일반 압축 파일과 다른 점은 실행 압축된 파일 그 자체도 실행 파일 이라는 것

Protector

실행 압축 기능 외에 파일과 그 프로세스를 보호하려는 목적으로 anti-debugging, anti-emulating, anti-dump 등의 기능을 추가한 유틸리티.Protector를 상세 분석하려면 난이도 상승.


~> API라는 것은 OS에서 제공한 함수이고, 실제로 API는 C:\Windows\system32\ 폴더에 *.dll 파일 내부에 구현되어 있다. 우리가 만드는 프로그램이 어떤 의미 있는 일(각종I/O)을 하려면 반드시 OS에서 제공된 API를 사용해서 OS에게 요청해야 하고, 그 API가 실제 구현된 시스템 DLL 파일들은 우리 프로그램의 프로세스 메모리에 로딩되어야 한다.

 

※프로그램이 메모리에 로딩될 때 최소 기본단위( 보통 1000)가 있다. 비록 프로그램 내에서 메모리를 100 크기 만큼만 사용한다 해도 실제로 메모리에 로딩될 때에는 최소 기본단위인 1000만큼의 크기가 잡히는 것이다(나머지 F00크기의 사용되지 않는 영역은 NULL로 채워진다)

 

레지스터(Register)

CPU 내부에 존재하는 다목적 저장 곤간. CPU가 RAM에 있는 데이터를 엑세스 하기 위해서는 물리적으로 먼 길을 돌아가야 하기 때문에 시간이 오래걸린다. 하지만 레지스터는 CPU와 한 몸이기 때문에 고속으로 데이터를 처리할 수 있다.

 

Basic Program Execution Register

-General Purpose Register(32bit -8개 )

-Segment Register (16bit - 6개)

-Program Status and Control Register(32bit -1개)

-Instruction Pointer(32bit -1개)

 

General Purpose Register(32bit -8개 ) →범용 레지스터

보통 상수/주소 등을 저장할 때 주로 사용되며, 특정 어셈블리 명령어 에서는 특정 레지스터를 조작하기도 한다. 또한 어떤 레지스터 들은 특수한 용도로 사용되기도 한다.

31                              16 15                               8  7                                  0               16-bit                            32-bit

 

 AH

AL 

AX 

EAX 

 

BH 

BL 

BX 

EBX 

 

CH 

CL 

CX 

ECX 

 

DH 

DL 

DX 

EDX 

 

 BPㄱ

 

EBP 

 

 SI

 

ESI 

 

 DI

 

EDI 

 

 SP

 

ESP 

EAX: Accumulator for operands and results data

EBX: Pointer to data in the DS segment

ECX: Counter for string and loop operations

EDX: I/O pointer

위 4개의 레지스터 들은 주로 산술연산 명령어에서 상수/ 변수 값의 저장용도로 많이 사용된다. 어떤 어셈블리 명령어 (MUL, DIV, LODS 등)들은 특정 레지스터를 직접 조작하기도 한다(이런 명령어가 실행된 이후에 특정 레지스터들의 값이 변경된다)

그리고 추가적으로 ECX와 EAX는 특수한 용도로도 사용된다. ECX는 반복문 명령어(LOOP)에서 반복 카운트로 사용된다(루프를 돌 때마다 ECX를 1씩 감소). EAX는 일반적으로 함수 리턴값에 사용된다. 모든 Win32 API 함수들은 리턴 값을 EAX에 저장한후 리턴한다.

 

EBP : Pointer to data on the stack(in the SS segment)

ESI : source pointer for string operation

EDI : destination pointer for string operations

ESP: Stack Pointer(in the SS segment)

위 4개의 레지스터들은 주로 메모리 주소를 저장하는 포인터로 사용된다. ESP는 주로 스택 메모리 주소를 가리킨다. 어떤 명령어들(PUSH, POP, CALL , RET)은 ESP를 직접 조작하기도 한다( 스택 메모리 관리는 프로그램에서 매우 중요하기 때문에 ESP를 다른용도로 사용 안할것을 권장)

EBP는 함수가 호출되었을 때 그 순간의 ESP를 저장하고 있다가, 함수가 리턴하기 직전에 다시 ESP에 값을 되돌려줘서 스택이 꺠지지 않도록 한다(Stack Frame)

 

세그먼트 레지스터

세그먼트(segment) : 메모리를 조각내어 각 조각마다 시작 주소, 범위, 접근 권한 든을 부여해서 메모리를 보호하는 기법, 또한 세그먼트는 페이징 기법과 함께 가상 메모리를 실제 물리 메모리로 변경할때 사용한다. 세그먼트 메모리는 'Segment Descriptor Table(SDT)'라는 곳에 기술되어 있는데 세그먼트 레지스터는 바로 이 SDT의 index를 가지고 있다.

※세그먼트 레지스터가 가리키는 세그먼트 디스크립터와 가상메모리가 조합되어 선형주소가 되며, 페이징 기법에 의해 선형주소가 최종적으로 물리주소로 변환된다.

 

CS : Code Segment   SS: Stack Segment    DS: Data Segment    ES: Extra Segment   FS: Data Segment    GS:Data Segment

ES,FS,GS -> 추가적인 데이터 세그먼트

 

EFLAGS: Flag Register

-ZF(Zero Flag)

연산 명령후에 결과 값이 0이되면 ZF가 1(TRUE)로 세팅

-OF(Overflow Flag)

부호있는 수의 오버플로가 발생했을때 1로 세팅. 그리고 MSB가 변경되었을 때 1로세팅

-CF

부호없는 수의 오버플로가 발생했을 때 1로 세팅

 

스택 -> FILO(First In Last Out)

-함수 내의 로컬 변수 임시저장

-함수 호출 시 파라미터 전달

-복귀 주소 저장

 

→리버싱에서 기존의 코드(혹은 데이터)를 의도적으로 다른 코드로 덮어 쓰는 행위를 "패치시킨다"라고 말한다.

 

 

반응형
,
반응형

*시스템이 초기화 되기 시작하면 시스템은 커널을 메모리에 적재시키고 가용 메모리 영역을 확인하게 된다.

 

시스템은 운영에 필요한 기본적인 명령어를 '커널'에서 찾기 때문에 커널 영역은 반드시 저 위치에 있어야 한다.

(스택의 가장 낮은 부분) 운영체제는 하나의 프로세스를 실행시키면, 이 프로세스를 segment 라는 단위로 묶어서 가용 메모리 영역에 저장 시킨다.

 ~> 세그먼트는 하나의 프로세스를 묶은 것으로 실행 시점에 실제 메모리의 어느 위치에 저장될 지가 결정된다. 

 

cs (code segment), ds(data segment), ss(stack segment) Code segment     

 

시스템이 알아들을 수 있는 명령어. 즉, instruction 들이 들어있다. 이것은 기계어 코드로서, 컴파일러가 만들어낸 코드이다.

 

instruction들은 명령을 수행하면서 많은 분기 과정과 점프, 시스템 호출등을 수행하게 되는데 분기와 점프의 경우 메모리 상의 특정 위치에 있는 명령을 지정 해 주어야 한다. 

하지만 segment는 자신이 현재 메모리 상에 어느 위치에 저장될지 컴파일 과정에서는 알 수 없기 때문에 정확한 주소를 지정할 수 없다. 따라서 segment에서는 logical address 를 사용한다. Logical address는 실제 메모리 상의 주소와 mapping되어 있다. 즉, 세그먼트는 segment selector에 의해서 

자신의 시작 위치(offset)를 찾을 수 있고 자신의 시작 위치로 부터의 위치에 있는 명령어를 수행할 지를 결정하게 되는 것이다. 

Physical address = offset(시작위치) + logical address Data segment     

 

프로그램이 실행시에 사용되는 데이터가 들어간다. 여기서 말하는 데이터는 전역 변수들. 

 

프로그램 내에서 전역변수를 선언하면 그 변수가 data segment에 자리잡게 된다. data segment는 다시 네개의 data segment로 나뉘는데 각각 현재 모듈의 data structure, 상위 레벨로 부터 받아들이는 데이터 모듈, 동적 생성 데이터, 다른 프로그램과 공유하는 공유 데이터 부분이다. 

 

Stack segment     현재 수행되고 있는 handler, task, program이 저장하는 데이터 영역으로 우리가 사용하는 버퍼가 바로 이 stack segment에 자리잡게 된다. 또한 프로그램이 사용하는 multiple스택을 생성할 수 있고 각 스택들간의 switch가 가능하다. 지역 변수들이 자리 잡는 공간이다. 

 

~> 스택은 처음 생성될 때 그 필요한 크기만큼 만들어지고 프로세스의 명령에 의해 데이터를 저장해 나가는 과정을 거치게 되는데, 이것은 stack pointer(SP)라고 하는 레지스터가 스택의 맨 꼭대기를 가리키고 있다. 

스택에 데이터를 저장하고 읽어 들이는 과정은 push 와 pop 명령어에 의해 수행된다. 레지스터 : CPU가 재빨리 읽고 쓰기를 해야하는 데이터들이므로 CPU 내부에 존재하는 메모리를 사용한다. 이러한 저장 공간을 '레지스터'라 한다. 

 

범용 레지스터 : 논리 연산, 수리 연산에 사용되는 피연산자, 주소를 계산하는데 사용되는 피연산자, 그리고 메모리 포인터가 저장되는 레지스터

→프로그래머가 임의로 조작할 수 있게 허용되어 있는 레지스터, 4개의 32bit 변수(EAX,EBX,ECX,EDX)

→EAX : 피연산자와 연산 결과의 저장소

→EBX : DS(Data Segment)안의 데이터를 가리키는 포인터

→ECX : 문자열 처리나 루프를 위한 카운터

→EDX : I/O 포인터

→ESI : DS 레지스터가 가리키는 data segment 내의 어느 데이터를 가리키고 있는 포인터

→EDI : ES 레지스터가 가리키는 data segment 내의 어느 데이터를 가리키고 있는 포인터

→ESP : SS 레지스터가 가리키는 stack segment의 맨 꼭대기를 가리키는 포인터

→EBP : SS 레지스터가 가리키는 스택상의 한 데이터를 가리키는 포인터

 

세그먼트 레지스터: code segment, data segment, stack segment를 가리키는 주소가 들어있는 레지스터

플래그 레지스터 : 현재 상태나 조건등을 검사하는데 사용되는 플래그

인스트럭션 포인터 : 다음 수행해야 하는 명령이 있는 메모리 상의 주소가 들어가 있는 레지스터

 

 *우리는 세그먼트 레지스터가 가리키는 위치를 바탕으로 우리가 원하는 segment안의 특정 데이터, 명령어들을 정확하게 끄집어 낼수가 있다

 

플래그 레지스터 : 시스템이 리셋되어 초기화 되면 0x00000002 값을 가진다.

c.f 컨트롤 플래그 레지스터 : 상태 플래그, 컨트롤 플래그, 시스템 플래그들의 집합

carry & borrow : 덧셈 연산시 bit bound를 넘어가거나 뺄셈을 하는데 빌려오는 경우

Instruction Pointer

다음 실행할 명령어가 있는 현재 code segment의 offset(시작위치) 값을 가진다. 이것은 하나의 명령어 범위에서 선형 명령 집합의 다음 위치를 가리킬 수 있다. 

→EIP 레지스터를 읽을 수 있는 방법 : CALL instruction을 수행하고 나서 프로시저 스택으로부터 리턴하는 instruction의 address를 읽는다.

 

반응형
,