728x90
반응형

 

공격자는 비지박스(busybox)라는 리눅스 명령어 패키지를 이용해서 wget 명령으로 악성코드를 다운로드 해 실행

 

x86-64 아키텍처

인텔의 32비트 CPU 아키텍처, 64: CPU가 한번에 처리할 수 있는 데이터의 크기
- 인텔의 x86 아키텍처와 호환되는 64bit 아키텍처 IA-64 발표

Intel 회사에서 개발한 컴퓨터의 중앙처리장치(CPU)
ISA(Instruction Set Architecture)

 

범용 레지스터(General Register)

이름 주용도
rax (accumulator register) 함수의 반환 값
rbx (base register) x64에서는 주된 용도 없음
rcx (counter register) 반복문의 반복 횟수, 각종 연산의 시행 횟수
rdx (data register) x64에서는 주된 용도 없음
rsi (source index) 데이터를 옮길 때 원본을 가리키는 포인터
rdi (destination index) 데이터를 옮길 때 목적지를 가리키는 포인터
rsp (stack pointer) 사용중인 스택의 위치를 가리키는 포인터
rbp (stack base pointer) 스택의 바닥을 가리키는 포인터

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

x64 아키텍처에는 cs, ss, ds, es, fs, gs 총 6가지 세그먼트 레지스터 존재

arm, mips, mpsl, x86

 

Q1. rax = 0x0123456789abcedf일 때, eax의 값은?

0x89abcdef

 

Q2. rax = 0x0123456789abcdef일 때, al의 값은?

0xef

 

Q3. rax에서 rbx를 뺐을 때, ZF가 설정되었다. rax와 rbx의 대소를 비교하시오.

==

 

Q4. rax = 0x0123456789abcdef일 때, ax의 값은?

0xcdef

 

Q5. rax = 0x0123456789abcdef일 때, ah의 값은?

0xcd

 

프로세스 메모리 구조

섹션

윈도우의 PE 파일은 PE 헤더와 1개 이상의 섹션으로 구성됨

".text" 섹션: PE코드, 실행 가능한 기계 코드가 위치하는 영역

".data" 섹션: PE가 실행중에 참조하는 데이터, 컴파일 시점에 값이 정해진 전역 변수들이 위치, 읽기/쓰기 권한 부여

".rdata" 섹션: 컴파일 시점에 값이 정해진 전역 상수와 참조할 DLL 및 외부 함수들의 정보 저장, 읽기 권한 부여(쓰기 불가능), str_ptr: "readonly" 문자열

섹션이 아닌 메모리

윈도우의 가상 메모리 공간에는 섹션 + 스택/힙 등이 적재됨

스택: 지역 변수나 함수의 리턴 주소가 저장됨, 읽기/쓰기 권한 부여 (기존 주소보다 낮은 주소로 확장됨)

어셈블리어와 x86-64

어셈블리 언어

컴퓨터의 기계어와 치환되는 언어, 명령어 집합구조(Instruction Set Architecture, ISA)

x64 어셈블리 언어

명령어(Operation Code, Opcode)와 목적어에 해당하는 피연산자(Operand)

x86-64 어셈블리어 문법 구조

mov eax, 3

(opcode: 대입해라, operand1: eax에, operand2: 3을)

명령어

인텔의 x64에는 매우 많은 명령어가 존재함, 본 커리큘럼에서는 이 코스와 다음 코스에 걸쳐, 이 중 중요한 21개의 명령어를 자세히 학습할 것.

명령 코드  
데이터 이동(Data Transfer) mov, lea
산술 연산(Arithmetic) inc, dec, add, sub
논리 연산(Logical) and, or, xor, not
비교(Comparison) cmp, test
분기(Branch) jmp, je, jg
스택(Stack) push, pop
프로시져(Procedure) call, ret, leave
시스템 콜(System call) syscall

피연산자

상수(Immediate Value), 레지스터(Register), 메모리(Memory)

크기 지정자(Size Directive) TYPE PTR

여기에 타입에는 BYTE, WORD, DWORD, QWORD가 올 수 있으며, 각각 1바이트, 2바이트, 4바이트, 8바이트의 크기 지정

메모리 피연산자의 예

메모리 피연산자  
QWORD PTR [0x8048000] 0x8048000의 데이터를 8바이트만큼 참조
DWORD PTR [0x8048000] 0x8048000의 데이터를 4바이트만큼 참조
WORD PTR [rax] rax가 가르키는 주소에서 데이터를 2바이트 만큼 참조

자료형 WORD의 크기가 2바이트인 이유

초기에 인텔은 WORD의 크기가 16비트인 IA-16 아키텍처를 개발했음
CPU의 WORD가 16비트였기 때문에, 어셈블리어에서도 WORD를 16비트 자료형으로 정의하는 것이 자연스러웠음

이후에 개발된 IA-32, x86-64 아키텍처는 CPU의 WORD가 32비트, 64비트로 확장됨
이 둘의 아키텍처에서는 WORD 자료형이 32비트, 64비트의 크기를 지정하는 것이 당연할 것

하지만 인텔은 WORD 자료형의 크기를 16비트로 유지함 (새로운 아키텍처 호환 여부)

DWORD(Double Word, 32bit), QWORD(Quad Word, 64bit) 자료형을 추가로 만듦

논리 연산 - and & or

# add
[Register]
eax = 0xffff0000
ebx = 0xcafebabe

[Code]
and eax, ebx

[Result]
eax = 0xcafe0000
# or
[Register]
eax = 0xffff0000
ebx = 0xcafebabe

[Code]
or eax, ebx

[Result]
eax = 0xffffbabe

논리 연산 - xor & not

[Register]
eax = 0xffffffff
ebx = 0xcafebabe

[Code]
xor eax, ebx

[Result]
eax = 0x35014541

xor dst, src: dst와 src의 비트가 서로 다르면 1, 같으면 0

- f0 e1 d2 c3 b4 a5

[Register]
eax = 0xffffffff

[Code]
not eax

[Result]
eax = 0x00000000

 

정리

종류 연산자 의미
데이터 이동 연산자 mov dst, src src의 값을 dst에 대입
데이터 이동 연산자 lea dst, src src의 유효 주소를 dst에 대입
산술 연산 add dst, src src의 값을 dst에 더함
산술 연산 sub dst, src src의 값을 dst에서 뺌
산술 연산 inc op op의 값을 1 더함
산술 연산 dec op op의 값을 1 뺌
논리 연산 and dst, src dst와 src가 모두 1이면 1, 아니면 0
논리 연산 or dst, src dst와 src 중 한 쪽이라도 1이면 1, 아니면 0
논리 연산 xor dst, src dst와 src가 다르면 1, 같으면 0
논리 연산 not op op의 비트를 모두 반전
비교 cmp op1, op2 op1에서 op2를 빼고 플래그를 설정
비교 test op1, op2 op1과 op2에 AND 연산을 하고, 플래그를 설정
분기 jmp addr addf로 rip 이동
분기 je addr 직전 비교에서 두 피연산자의 값이 같을 경우 addr로 rip 이동
분기 jg addr 직전 비교에서 두 피연산자 중 전자의 값이 더 클 경우 addr로 rip 이동

 

728x90
728x90
728x90
반응형

전처리(Preprocessing)

컴파일러가 소스 코드를 어셈블리어로 컴파일하기 전에, 필요한 형식으로 가공하는 것

add.c를 add.i로 전처리

$ gcc -E add.c > add.i
$ cat add.i

 

컴파일(Compile)

C로 작성된 소스 코드를 어셈블리어로 변역하는 것

소스 코드를 어셈블리 코드로 컴파일: "-S" 옵션

$ gcc -S add.i -o add.S
$ cat add.S

 

어셈블(Assemble)

컴파일로 생성된 어셈블리어 코드를 ELF 형식의 목적 파일(Object file)로 변환하는 과정

* ELF: 리눅스 실행파일 형식, PE: 윈도우 목적 파일 형식

목적 파일 변환 - 어셈블리 코드가 기계어로 번역되므로 더이상 사람이 해석하기 어려워짐

gcc "-c" 옵션을 통해 add.S를 목적 파일로 변환하고, 결과로 나온 파일을 16진수로 출력한 것

링크(Link)

여러 목적 파일들을 연결하여 실행 가능한 바이너리로 만드는 과정

디컴파일러(Decompiler)

-

정적 분석(Static Analysis), 동적 분석(Dynamic Analysis)

정적 분석

장점

1. 프로그램의 전체 구조를 파악하기 쉬움
2. 분석 환경의 제약에서도 비교적 자유로움
3. 바이러스와 같은 악성 프로그램의 위협으로부터 안전함

단점

1. 난독화(Obfuscation)가 적용되면 분석이 매우 어려워짐

2. 다양한 동적 요소를 고려하기 어려움

동적 분석

장점

1. 코드를 자세히 분석해보지 않고도 프로그램의 개략적인 동작을 파악할 수 있음

단점

1. 분석 환경을 구축하기 어려울 수 있음

2. 안티 디버깅(Anti Debudding)

if (is_debugging())
	exit(-1);
Func();

 

컴퓨터 구조(Computer Architecture)

컴퓨터가 효율적으로 작동할 수 있도록 하드웨어 및 소프트웨어의 기능을 고안하고, 이들을 구성하는 방법

폰 노이만 구조, 하버드 구조, 수정된 하버드 구조

명령어 집합구조(Instruction Set Architecture, ISA)

CPU가 처리해야하는 명령어를 설계하는 분야

(e.g., ARM, MIPS, AVR, 인텔의 x86 및 x86-64 아키텍처)

마이크로 아키텍처(Micro Architecture)

정의된 명령어 집합을 효율적으로 처리할 수 있도록, CPU의 회로를 설계하는 분야

 

> 폰 노이만 구조와 명령어 집합 구조(Instruction Set Architecture) 중 x86-64 아키텍처

폰 노이만: 초기 컴퓨터 과학자 중 컴퓨터에 연산, 제어, 저장의 세 가지 핵심 기능이 필요하다고 생각함

근대의 컴퓨터는 연산과 제어를 위해 중앙처리장치(Central Processing Unit, CPU)를, 저장을 위해 기억장치(memory)를 사용함

장치간에 데이터나 제어 신호를 교환할 수 있도록 버스(bus)라는 전자 통로를 사용함

중앙처리장치

산술논리장치(Arithmetic Logic Unit, ALU): CPU의 산술/논리 연산 처리

제어장치(Control Unit): CPU 제어

레지스터(Register): CPU에 필요한 데이터 저장

기억장치

컴퓨터가 동작하는데 필요한 여러 데이터를 저장하기 위해 사용됨

주기억장치: 프로그램 실행과정에서 필요한 데이터들을 임시로 저장하기 위해 사용 (e.g., 램(Random-Access Memory, RAM)

보조기억장치: 운영 체제, 프로그램 등과 같은 데이터를 장기간 보관하고자 할 때 사용 (e.g., 하드 드라이브(Hard Disk Drive, HDD), SSD(Solid State Drive))

버스

컴퓨터 부품과 부품 사이 또는 컴퓨터와 컴퓨터 사이에 신호를 전송하는 통로

데이터 버스(Data Bus): 데이터 이동

주소 버스(Address Bus): 주소 지정

 

  • 범용 레지스터(General Register)
  • 세그먼트 레지스터(Segment Register)
  • 플래그 레지스터(Flag Register)
  • 명령어 포인터 레지스터(Instruction Pointer Register, IP)

 

728x90
728x90

+ Recent posts