본문 바로가기
메가IT아카데미 국기과정/리눅스와 시스템보안

[3-9] 어셈블리어

by 한님폐하 2022. 9. 12.

1. 어셈블리어 형식

  • 형식 : OPCODE 오퍼랜드1, 오퍼랜드2
  • Intel 문법 : OPCODE [Destination] [Source]
  • AT&T 문법 : OPCODE [Source] [Destination] <- 현재 수업에서 사용하는 문법

 

2. 스택 프레임

  • 스택 프레임은 EBP 레지스터를 사용하여 스택 내의 지역 변수, 함수 인자값, 리턴 주소에 접근하는 기법이다.
  • 스택에는 지역 변수, 함수 인자값, 리턴 주소들이 저장된다.
  • 스택 프레임은 함수가 호출될 때 생성되고, 함수 처리 완료되면 자동으로 소멸된다.

 

3. 어셈블리어 분석

vi stack.c
#include <stdio.h>

int add(int a, int b)
{
int x = a , y = b;
return (x + y);
}

int main(int argc, char* argv[])
{
int a = 6 , b = 2;
printf("%d\n", add(a, b));
return 0;
}
gcc -mpreferred-stack-boundary=2 -S -o stack.a stack.c

 

4. gdb를 이용한 실행 파일 분석

gcc -mpreferred-stack-boundary=2 -o stack stack.c
./stack 
gdb -q stack
b *main
run
disas main
info reg
x/12x $esp

 

[참고] gdb 기본 명령어

ni => 위치 명령어를 실행하고 다음 명령어로 넘어감
si 함수 내부 명령어로 진입함
break *0x08048348 특정 주소에 브레이크 포인트 지정
run 프로그램 실행
cont 다음 브레이크 포인트까지 프로그램 실행
info reg 레지스터 상태 확인
x/12x $esp ESP 기준으로 높은 주소(밑) 스택 정보 12개 출력

 

5. 지역 변수

  • 해당 함수 내에서 사용하는 변수이며, 함수 처리가 완료되면 소멸된다.
  • 함수 안에서 지역 변수를 선언하면, 스택에 SUB 명령어를 이용하여 공간을 확보한다.
  • 이때, 확보되는 공간 크기는 자료형 타입과 선언된 지역 변수 개수에 따라서 다르다.
  • 지역 변수는 스택에 저장되기 때문에 SS(Stack Segment)에 저장된다.
  • 올리디버거에서는 해당 값들이 저장될 위치 주소 앞에 'DS'로 출력된다.
#include <stdio.h>

int main()
{
int num1 = 6;	
int num2 = 2;
return 0;
}

 

6. 전역 변수

  • 다른 함수에서도 사용할 수 있는 변수를 의미하며 프로그램이 종료되면 소멸된다.
  • 전역 변수는 DS(Data Segment)에 저장되어 있기 때문에 스택에 별도의 공간을 확보하지 않는다.
  • 즉, 전역 변수는 스택에서 처리되는 것이 아니라, 데이터 세그먼트 영역에 저장되어 처리된다.
  • 올리디버거에서는 해당 값들이 저장될 위치 주소 앞에 'DS'로 출력된다.
#include <stdio.h>

int num1;
int num2;

int main()
{
num1 = 6;	
num2 = 2;
return 0;
}

 

7.  구조체

  •  여러 자료형을 가진 변수들을 하나의 그룹 형태로 묶어서 사용하는 자료형이다.
  • 'struct' 구조체 이름을 정의하고 중괄호 안에 필요한 자료형 변수들을 선언한다.
#include <stdio.h>

struct num 
{
int num1;
int num2;
int num3;
};

int main()
{
struct num st;
st.num1 = 6;
st.num2 = 2;
st.num3 = 8;
printf("%x, %x, %x\n", st.num1, st.num2, st.num3);
return 0;
}

 

8. 함수

  • 특정 기능을 수행할 수 있도록 코드들이 모여있는 일종의 작은 프로그램 단위를 의미한다.
  • 함수 처리 과정 : 함수 호출 -> 함수 시작(프롤로그) -> 함수 본체 -> 함수 종료(에필로그)
  • 함수가 호출되기 이전에 인자값들이 스택에 PUSH가 되고, 리턴주소를 저장한 다음에 함수가 시작된다.
  • 함수 처리가 완료되면 결과값(리턴값)을 EAX 레지스터에 저장하여 리턴하고 함수를 종료한다.
#include <stdio.h>

int func(int num1, int num2)
{
return num1+num2;
}

int main()
{
func(6, 2);
return 0;
}

 

9. 배열

  • 문자열은 크기가 일정하지 않기 때문에 문자열을 저장하는 기본 자료형은 없다.
  • 그렇기 때문에 char 자료형을 이용하여 문자 1개씩(1byte) 단위로 배열한다.
  • 마지막에 NULL 문자(문자열의 끝을 의미함)가 포함되어 있기 때문에 배열 문자 개수+1로 선언해야 한다.
#include <stdio.h>

int main()
{
char str[6] = "abcde";
return 0;
}

 

10. IF 구문

  • 조건에 만족되면 코드가 실행되는 조건문이다.
  • 만약, 조건에 만족하지 않으면 다른 코드를 실행하거나 프로그램을 종료한다.
#include <stdio.h>

int main()
{
int num1 = 6;
int num2 = 6;

if(num1!=num2) {
printf("Do Not Jump\n");
} else {
printf("Jump\n");
}
return 0;
}

 

11. switch 문

  • 조건에 만족하면 코드를 실행하는 조건문이다.
  • 대신, if 구문과 차이점은 변수 값에 따라서 다른 동작을 실시할 수 있다.
  • 또한, 'break' 제어문과 같이 사용하여 조건에 만족되면 코드를 실행하고 조건 검사를 종료할 수 있다.
#include <stdio.h>

int main()
{
int num1 = 3;

switch (num1) {

case 1:
printf("num1 is 1\n");	
break;

case 2:
printf("num1 is 2\n");	
break;

case 3:
printf("num1 is 3\n");	
break;

default:
printf("num1 is not 1,2,3\n");	
break;
}
return 0;
}

 

12. 'for' 문

  • 일정 횟수를 정해 놓고 반복 작업을 실시하는 반복문이다.
  • for 구문은 어셈블리어 초기문 -> JMP(아래로) -> 조건분기(아래로) 및 수행문 -> JMP(위로) -> 증감문 패턴을 갖고 있다.
  •  ① 초기문에 의해서 반복하는 변수의 값이 정해진다.
     ② 조건문을 검사하고 조건에 맞지 않으면 수행문을 수행한다.
     ③ 수행문을 수행한 이후 증감문을 수행하여 반복카운트를 증가한다.
     ④ 그리고 조건문을 검사하여 조건에 맞지 않으면 수행문을 수행한다.
     ⑤ 만약, 조건에 맞는다면 반복을 종료한다.
#include <stdio.h>

int main()
{
int num1;
for(num1=0; num1<5; num1++) {
num1 = num1;
printf("Loop Count = %d\n", num1);
}
return 0;
}

 

13. 'while' 문

  • 일정 횟수를 정해 놓지 않고 조건이 만족할때 까지 반복 작업을 실시하는 반복문이다. (물론 일정 횟수를 정할 수 있다.)
  • while 구문은 어셈블리어에서 초기문 -> 조건분기(아래로) -> 증감문 -> JMP(위로) 패턴을 갖고 있다.
  •  ① 초기문에 의해서 반복하는 변수의 값이 정해진다.
     ② 조건문을 검사하고 조건에 맞지 않으면 수행문을 수행한다.
     ③ 그리고 조건문으로 다시 이동한다.
     ④ 조건문을 검사하여 조건에 맞지 않으면 다시 증가문을 수행한다.
     ⑤ 만약, 조건에 맞다면 반복을 종료한다.
#include <stdio.h>

int main()
{
int num1 = 0;
while(num1<5) {
num1++;
printf("Loop Count = %d\n", num1);
}
return 0;
}

 

  •  MOV
    • 형식 : MOV [Source] [Destination]
    • 내용 : [Source]를 [Destination]로 대입/이동한다. 이때, [Source] 값은 변경되지 않는다.
  •  LEA
    • 형식 : LEA [Source] [Destination]
    • 내용 : [Source]의 주소 값을 [Destination]으로 대입한다.
  • ADD
    • 형식 : ADD [Operand 1] [Operand 2]
    • 내용1 : [Operand 2]에 [Operand 1]을 더해서 [Operand 2]에 저장한다. 
    • 내용2 : 또한, 스택에 저장된 지역 변수 및 PUSH된 인자값을 삭제할때 사용한다.
  • SUB
    • 형식 : SUB [Operand 1] [Operand 2]
    • 내용1 : [Operand 2]에서 [Operand 1]를 빼서 [Operand 2]에 저장한다.
    • 내용2 : 또한, 스택에 저장될 지역 변수 공간을 확보할때 사용한다. 
  • IMUL
    • 형식 : IMUL [Operand 1] [Operand 2]
    • 내용 : [Operand 2]에 [Operand 1]를 곱해서 [Operand 2]에 저장한다.
  • IDIV
    • 형식 : IDIV [Operand] 
    • 내용 : EAX에서 [Operand]를 나눈 이후, 해당 값을 EAX에 저장한다.
  • CMP
    • 형식 : CMP [Operand1] [Operand2] 
    • 내용 : [Operand1]에서 [Operand2] 빼서 결과 값이 '0'인지를 확인한다. 즉, 함수의 성공 여부를 확인할 수 있다.
  • TEST
    • 형식 : TEST [Operand1] [Operand2] 
    • 내용 : [Operand1]과 [Operand2]를 AND 연산하여 결과 값이 '0'인지을 확인한다. 즉, 함수의 성공 여부를 확인할 수 있다.
  • JMP
    • 조건과 관계 없이 무조건 점프
  • JE/JZ
    • 형식 : JZ [Code Address]
    • 내용 : 비교 결과 값이 '0'이면(ZF=1 설정된 경우), 해당 주소로 점프한다.
    • 조건 : Operand 1 == Operand 2
  • JNE&JNZ
    • 형식 : JNZ [Code Address]
    • 내용 : 비교 결과 값이 '0'이 아니면(ZF=0 설정된 경우), 해당 주소로 점프한다.
    • 조건 : Operand 1 != Operand 2
  • JLE(JBE 비슷함)
    • 형식 : JLE [Code Address]
    • 내용 : 비교 결과 값이 '0'이거나(ZF=1), Operand1이 크거나 같은 경우 해당 주소로 점프한다.
    • 조건 : Operand 1 >= Operand 2
  • JGE(JAE 비슷함)
    • 형식 : JGE [Code Address]
    • 내용 : 비교 결과 값이 '0'이거나(ZF=1), Operand1이 작거나 같은 경우 해당 주소로 점프한다.
    • 조건 : Operand 1 <= Operand 2
  • JL(JB 비슷함)
    • 형식 : JL [Code Address]
    • 내용 : Operand 1이 Operand 2보다 큰 경우 해당 주소로 점프한다.
    • 조건 : Operand 1 > Operand 2
  • JG(JA 비슷함)
    • 형식 : JG [Code Address]
    • 내용 : Operand 1이 Operand 2보다 작은 경우 해당 주소로 점프한다.
    • 조건 : Operand 1 < Operand 2

'메가IT아카데미 국기과정 > 리눅스와 시스템보안' 카테고리의 다른 글

[3-11] FTZ 9~19  (0) 2022.09.12
[3-10] 메모리 구조  (0) 2022.09.12
[3-8] 레지스터 구조  (0) 2022.09.12
[3-7] BeEF  (0) 2022.09.12
[3-6] Setoolkit  (0) 2022.09.12