Computer Security

#16 어셈블리어-스택과 프로시저 본문

컴퓨터구조&어셈블리어

#16 어셈블리어-스택과 프로시저

쿠리 Kuri 2022. 5. 30. 18:30

스택

-후입선출(LIFO, Last-In-First-Out) 자료구조

-스택포인터(SP, Stack Pointer)는 스택의 최상위(top) 원소를 가리키는 주소를 저장하는 레지스터

-8086은 스택 세그먼트 레지스터 SS와 스택 포인터 SP를 제공(80386+는 ESP)

-스택은 상위 주소에서 하위 주소로 거꾸로 성장함

-레지스터에 있는 데이터를 스택에 저장하는 연산을 PUSH, 스택에 저장된 데이터를 꺼내서 레지스터에 적재하는 연산을 POP이라 한다.

 

PUSH:스택은 상위주소에서 하위주소로 내려간다.

POP:마지막으로 들어왔던 데이터부터 꺼내서 레지스터에 적재

 

PUSH 명령어

-피연산자로 지정한 워드/이중워드(80386+) 크기 레지스터나 메모리 데이터를 스택에 저장한다.

-플래그:아무것도 영향을 미치지 않는다.

       PUSH reg16/mem16

    PUSH reg32/mem32(80386+)

 

POP 명령어

-피연산자로 지정한 워드/이중워드(80386+) 크기 레지스터나 메모리 공간에 스택 최상위에 저장된 데이터를 꺼내서 저장한다.

-플래그: 아무 영향 x

     POP reg16/mem16

  POP reg32/mem32 (80386+)

 

PUSHA(80286+) / PUSHAD(80386+) 

-모든 범용 레지스터를 PUSH한다.

-PUSHA: AX, CX, DX, BX, SP, BP, SI, DI 를 순서대로 스택에 push 하고 SP를 16 감소시킨다.(80286+)

-PUSHAD: EAX, ECX, EDX, EBX, ESP, EBP, ESI, EDI를 순서대로 스택에 push 하고 ESP를 32감소시킨다.(80386+)

-플래그: 아무 영향 x

 

POPA(80286+) / POPAD(80386+)

-모든 범용 레지스터를 POP한다.

-POPA: 스택 최상위에 있는 8개의 워드를 POP하여 DI, SI, BP, SP, BX, DX, CX, AX에 순서대로 넣는다.(80286+)

-POPAD: 스택 최상위에 있는 8개의 이중워드를 POP하여 EDI, ESI, EBP, ESP, EBX, EDX, ECX, EAX에 순서대로 넣는다.(80386+)

-플래그: 아무 영향 x

 

PUSHF/PUSHFD

-플래그를 스택에 PUSH한다.

-PUSHF:16비트 플래그 레지스터의 내용을 스택에 PUSH하고 SP를 2감소시킨다.

-PUSHFD:32비트 플래그 레지스터의 내용을 스택에 PUSH하고 ESP를 4감소시킨다.(80386+)

-플래그: 아무 영향 x

 

POPF/POPFD

-스택으로부터 플래그를 POP한다.

-POPF: 스택의 최상위에 있는 워드를 POP하여 16비트 플래그 레지스터에 넣고 SP를 2증가 시킨다.

-POPFD: 스택의 최상위에 있는 이중워드를 POP하여 32비트 플래그 레지스터에 넣고 SP를 4증가 시킨다.(80386+)

-플래그: 모든 플래그에 영향을 미친다.

 

 

스택의 활용

-서브루틴 호출 이후 본 프로그램으로 복귀하기 위한 복귀 주소(return address)를 저장할 때 사용된다.

-프로시저로 매개변수를 전달하는 방법 중 하나로 스택을 이용한다.

-고급언어에서 지역변수는 스택 영역에 할당된다.

-서브루틴을 호출하거나 인터럽트가 발생해서 인터럽트 서비스 루틴(ISR)이 호출되면 기존의 레지스터 값들이 변경될 수 있기 때문에 레지스터 값들을 보존할 때 스택에 저장한다.

 

 

프로시저

원래 코드를 쭉 읽다가 Call PROC(이름)을 만나면 그쪽 으로 가서 거기 실행하다가 RET(return)을 만나면 다시 원래 메모리로 돌아가서 실행하는 구조

 

프로시저 정의와 종료

name PROC type

,,,,

name ENDP

 

-name은 프로시저의 이름이다. 식별자 규칙을 따라야 한다.

-type은 프로시저의 유형이다. NEAR 또는 FAR로 지정 가능하다.

-NEAR은 세그먼트 내부에서 사용되는 근거리 프로시저를 가리킨다.

-FAR은 원거리 프로시저로, 세그먼트 밖에서도 호출 가능하다.

-type을 별도로 지정하지 않으면, NEAR 형식이 기본값으로 지정된다.

 

 

프로시저 명령어

CALL 명령어

-어셈블러는 피호출 프로시저가 NEAR이면 근거리 CALL을 사용하고, FAR이면 원거리 CALL을 생성한다.

-근거리인 경우 CALL은 IP(다음 명령어의 주소)를 스택에 푸시한다. 그 다음에 IP(Intrupt pointer)를 목적지 오프셋 주소로 적재한다.

-원거리인 경우 CALL은 CS를 스택에 푸시하고 세그먼트 간 포인터를 스택에 적재한다.

-그 다음에 IP를 푸시하고 목적지 오프셋 주소로 적재한다.

-복귀할  때 RETN과 RETF는 이 과정을 반대로 수행한다.

-플래그: 아무 영향 x

 

CALL  reg/mem

 

RET/RETN/RETF

-어셈블러는 NEAR이라는 레이블이 붙은 프로시저인 경우는 근거리 RET를 생성하고, FAR이라는 레이블이 붙은 프로시저인 경우는 원거리 RET를 생성한다.

-근거리 RET는 스택의 꼭대기에 있는 워드를 IP로 옮기고 SP를 2 증가시킨다.

-원거리의 경우 RET는 스택의 꼭대기에 있는 워드들을 IP와 CS에 옮기고 SP를 4증가 시킨다.

-임의의 수치 피연산자(예를 들어 RET 4)가 SP에 더해질 수 있다.

-플래그: 아무 영향 x

 

RET/RETN/RETF [즉시값]

 

 

매개변수 전달

 

레지스터를 이용한 방법

-매개변수 전달하기 위한 방법 중 하나로 특정 레지스터를 지정하는 방법이 있다.

-레지스터 개수가 제한적이기 때문에 제약이 따른다.

 

레지스터를 이용하게되면

main PROC FAR


MOV DX, data 에서의 DX를    

 

ㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡ

 

sub1 PROC NEAR

 

MOV AX, DX  여기서 불러와서도 쓸 수 있다.

 

스택을 이용한 방법

-프로시저를 호출하기 전에 스택에 데이터들을 push하여 매개변수를 전달하는 방법이ㅣ 있다.

-스택 포인터 SP의 값을 보존하면서도, 매개변수 접근을 위한 베이스 레지스터로 BP를 사용한다.

 

 

 

호출규약

-호출규약에 따라 매개변수를 전달하고 스택을 정리하는 방법이 달라진다.

-C에서 사용하는 cdecl, 윈도 API에서 주로 사용하는 stdcall 등이 존재한다.

Comments