Hacking/Pwnable

어셈블리어 (Assembly)

GunP4ng 2024. 3. 22. 22:29

어셈블리어 정리


1. 어셈블리어

1. 어셈블리어란?

어셈블리어는 기계어와 1대1로 대응되는 명령어 체계를 가진 컴퓨터 프로그래밍 언어이다.

intel 문법과 AT&T 문법이 존재하고, 서로 호환이 되지 않는다

어셈블리어 구성

Opcode : 명령어

Operand : 피연산자

 

Intel Operand 의 경우 Operand2 가 Source 가 되고 Operand1 이 Destination 이 된다.

위의 경우에는 EBX 의 값을 EAX 에 더한다 라는 뜻이 된다.

AT&T의 경우는 반대가 되어 EAX 의 값을 EBX 로 더한다 라는 뜻이 된다.

 

Intel 문법은 숫자 그대로 사용한다.

1, 2, 3, 4, 5

AT&T 문법은 숫자 앞에 $ 를 붙인다.

$1, $2, $3, $4, $5

 

앞으로 나올 어셈블리는 Intel 문법을 기준으로 작성하였다.

 

2. 함수의 프롤로그, 에필로그

어셈블리어의 명령어를 알아보기 전에 먼저 함수의 프롤로그와 에필로그를 알아야한다.

 

Prologue (프롤로그) : 스택 프레임을 잡아주는 것. 함수가 호출될 때 프롤로그라고 한다.

→ push EBP               ebp 의 값을 push 하고 esp 의 값을 ebp 에 저장한다

     mov EBP ESP 

 

Epilogue (에필로그) : 스택 프레임을 해제하는 것. 함수를 종료할 때를 에필로그라고 한다. (leave 라고도 쓴다)

→ mov ESP EBP        ebp 의 값을 esp 에 저장한다. esp 가 ebp 를 가리키면서 스택이 초기화된다.

     pop EBP                그리고 난 후 ebp 의 값을 스택에서 꺼낸다. -> 이전 함수로 ebp 가 돌아간다.

 

함수의 에필로그 프롤로그는 나중에 스택 프레임 설명할 때 더 자세하게 설명하겠다.

 

 

2. 기본 명령어 정리

1. 기본 명령어 정리

~ PTR : 데이터 타입 재정의

BYTE : 부호 없는 1바이트 (8bit)

WORD : 부호없는 2바이트 (16bit)

DWORD : 부호없는 4바이트 (32bit)

QWORD : 부호없는 8바이트 (64bit)

lea ecx, dword ptr [ebp-4]

메모리에 접근할 때는 항상 ptr 로 사이즈를 정의해야 한다.

 

inc val : val 의 값 1 증가

dec val : val 의 값 1 감소

 

add val1 val2 : 두 값을 더하여 val2 에 저장

sub val1 val2 : val1 에서 val2 를 빼고 val1 에 저장

 

push : 값만큼 스택에 쌓음. esp 값도 변화함

pop : 스택 최상단 값을 가져옴

 

TEST : 묵시적 and 연산 (and 가 아니지만 and 와 같은 연산을 한다. 함수의 성공 여부 분별)

함수의 반환값은 무조건 eax 에 저장되기 때문에 TEST eax eax 둘 다 1이기 때문에 1이 나온다.

and 와 차이점은 and 연산은 저장을 하는 것이다.

 

2. [ebp-4] 와 ebp-4 의 차이점

어셈블리에서 메모리 주소를 가리킬 때는 [ ] 를 사용한다

ebx = 12345678 일 때

mov eax, ebx 와 mov eax [ebx] 를 비교하면

mov eax, ebx 는 ebx 의 값 12345678 을 eax 에 복사하는 것이다.

mov eax, [ebx] 는 ebx 의 값을 메모리 주소로 써서 0x12345678 에 위치한 값을 eax 에 복사하는 것이다.

 

 

3. MOV 명령어

1. MOV

데이터를 복사하는 명령어이다.

mov eax, ebx	; ebx 의 값을 eax 로 복사한다.

레지스터에서 같은 사이즈의 데이터끼리만 복사가 가능하다.

메모리에서 메모리로 복사가 안된다. 레지스터를 통해 복사해야 한다.

세그먼트 레지스터의 내용은 범용 레지스터에 이동이 안된다.

 

2. MOVZX

첫 번째 데이터가 두 번째보다 크기가 커도 복사가 된다.

movzx eax, ebx

eax 가 ebx 보다 커도 복사가 가능하다.

남는 공간은 0으로 채워지게 된다.

 

 

4. LEA 명령어

1. LEA

지정한 주소값을 가져오는 명령어로, 값을 가져와야 하는 경우에 쓰인다.

다른 레지스터에 주소값을 저장하고 싶을 때 사용한다

lea ecx, dword ptr [ebp-4]	;ebp-4 에 저장된 값이 가리키고 있는 곳의 주소값을 ecx에 저장한다

 

2. MOV 와 LEA 차이점

lea eax, [ebx] 는

mov eax, ebx 와 같은 의미이다.

하지만 LEA 는 유효한 주소값만 가져오는 것이고

MOV 는 유효하지 않은 주소값도 여과 없이 복사한다.

 

mov eax, [ebx] 는

ebx 에 있는 주소값에 들어있는 값을 복사하는 것이다.

주소가 아닌 값을 복사한다.

 

 

5. CMP 명령어

1. CMP

비교하는 명령어이다.

CMP 명령은 첫 번째 값에서 두 번째 값을 뺐을 때 음수( - ) 가 되는 경우 CF = 1

0일 경우 ZF = 1 플래그가 설정된다.

 

CMP 명령으로 플래그가 어떻게 설정되는지에 따라 조건 분기 명령이 다 다르다

 

2. 연산자 정리

JE : 두 값을 뺐을 때 0이면 점프 (ZF=1)

JA : 첫 번째 값이 큰 경우 점프 (CF=0 and ZF=0) (부호 X)

JB : 두 번째 값이 큰 경우 점프 (CF=1) (부호 X)

JAE : 첫 번째 값이 크거나 같은 경우 점프 (CF=0) (부호 X)

JBE : 두 번째 값이 크거나 같은 경우 점프 (CF=1 and ZF=1) (부호 X)

JG : 첫 번째 값이 큰 경우 점프 (CF=0 and SF=OF) (부호 O)

JGE : 두 번째 값이 큰 경우 점프 (SF!=OF) (부호 O)

JL : 첫 번째 값이 크거나 같은 경우 점프 (SF=OF) (부호 O)

JLE : 두 번째 값이 크거나 같은 경우 점프 (ZF=1 and SF!=OF) (부호 O)

 

JNE, JNA, JNG 와 같이 N이 들어가면 설명한 조건들의 반대가 된다.

 

JC : CF가 1이면 점프

JNC : CF가 0이면 점프

JO : OF가 1이면 점프

JNO : OF가 0이면 점프

JP(JPE) : PF가 1이면 점프

JNP(JPO) : PF가 0이면 점프

JS : SF가 1이면 점프

JNS : SF가 0이면 점프

JCXZ : CX 레지스터 값이 설정되어 있지 않으면 점프

 

 

6. CALL / JMP / RET / REP 명령어

1. CALL

프로시저(함수)를 호출하는 명령어

호출되고 난 후에 이전에 실행 중이던 프로시저로 복귀해야 한다.

때문에 RET 명령이 필요하다

 

2. JMP

프로그램 실행 흐름을 바꾸는 명령어다

CMP 명령과 같이 사용되어 조건 분기가 가능한 다양한 명령어가 존재한다.

CALL 명령과 다르게 RET 명령이 필요없다.

 

3. RET

CALL 을 이용하여 함수를 호출하면 프롤로그 과정부터 수행하게 된다.

함수를 호출한 뒤 복귀할 때 사용하는 것이 RET 명령이다.

 

4. REP

REP 명령어는 REP 뒤에 오는 명령 (MOVS, STOS 등)을 CX 레지스터가 0이 될 때까지 반복하는 명령이다

REP MOVS byte ptr [edi], byte ptr [esi]

한 번 명령이 실행되면 CX 레지스터의 값은 1 감소한다.

 

중간에 STOS, MOVS, SCAS 가 올 수 있다

  • STOS : 크기에 따라 (AL / AH / AX / EAX)를 EDI 가 가리키는 주소에 저장한다.
  • MOVS : ESI, EDI 증가
  • SCAS : AX와 비교하여 같으면 다음 명령줄로 아니면 계속 명령을 실행한다.

 

 

Reference

https://plummmm.tistory.com/348

https://coding-factory.tistory.cofm/651#google_vignette