보안/시스템 해킹

[ 시스템 해킹 ] Computer Science : Assembly

haena02 2022. 7. 14. 16:32
반응형

1, 어셈블리

 

컴퓨터 언어인 기계어는 0과 1로만 구성되어있어 굉장히 이해하기 힘들다.

이에 컴퓨터 과학자 David Wheeler는 EDSAC을 개발하면서 어셈블리 언어어셈블러라는 것을 고안하였다.

 

어셈블러는 개발자들이 어셈블리어로 코드를 작성하면 컴퓨터가 이해할 수 있는 기계어로 코드를 치환해주었다.

역어셈블리어는 역으로 기계어를 어셈블리어 언어로 번역해주어 소프트웨어를 역분석 하기 쉽게 해주었다.

 

2. x64 어셈블리어

 

어셈블리어도 종류가 다양하다. 

 x64의 세계에는 x64의 어셈블리어가 있고, ARM의 세계에는 ARM의 어셈블리어가 있다.

 

우리는 가장 많이 쓰는 x64 어셈블리어를 공부할 것이다.

 

2.1 기본 구조

 

어셈블리어의 문장은 명령어와 피연산자로 구성 되어있다.

피 연산자에는 3가지 종류가 있다.

- 상수

- 레지스터

- 메모리

 

이 중 메모리 피연산자는 []으로 둗러 싸인 것으로 표현되며 앞에  각각 1바이트, 2바이트, 4바이트, 8바이트의 크기를 지정하는 BYTE, WORD, DWORD, QWORD가 추가될 수 있다.

 

3 명령어

 

 

중요한 명령어들을 표로 나타내면 위와 같다.

 

3.1 데이터 이동

데이터 이동 명령어는 어떤 값을 레지스터나 메모리에 옮기도록 지시한다.

 

- mov dst, src : src에 들어있는 값을 dst에 대입

- lea dst, src : src의 유효 주소(Effective Address, EA)를 dst에 저장

 

[Register]
rbx = 0x401A40

=================================

[Memory]
0x401a40 | 0x0000000012345678
0x401a48 | 0x0000000000C0FFEE
0x401a50 | 0x00000000DEADBEEF
0x401a58 | 0x00000000CAFEBABE
0x401a60 | 0x0000000087654321

=================================

[Code]
1: mov rax, [rbx+8]
2: lea rax, [rbx+8]

 

Q. 코드를 1까지 실행했을 때 rax 에 저장된 값은 무엇인가

A. 0xC0FFEE

mov rax, [rbx+8] 는 rbx에 8만큼 더한 메모리에 들어있는 값을 rax에 넣으라는 의미인데 

rax는 0x401A40 이므로 rbx+8는 0x401A48이 된다. 

따라서 rax에는 0x401A48에 들어있는 0xC0FFEE가 들어가게된다. 

 

Q. 코드를 2까지 실행했을 때 rax 에 저장된 값은 무엇인가

A.0x401A48

lea rax, [rbx+8] 는 rbx에 8만큼 더한 메모리의 주소를 ax에 넣으라는 의미인데

rax는 0x401A40 이므로 rbx+8는 0x401A48이 된다. 

따라서 rax에는 0x401A48가 들어가게 된다. 

 

3.2 산술연산

산술 연산은 덧셈, 뺄셈, 곱셈, 나눗셈 연산을 지시한다. 

 

- add dst, src : dst에 src의 값을 더함
- sub dst, src: dst에서 src의 값을 뺌
- inc op: op의 값을 1 증가시킴
- dec op: op의 값을 1 감소 시킴

 

[Register]
rax = 0x31337
rbx = 0x555555554000
rcx = 0x2

=================================

[Memory]
0x555555554000| 0x0000000000000000
0x555555554008| 0x0000000000000001
0x555555554010| 0x0000000000000003
0x555555554018| 0x0000000000000005
0x555555554020| 0x000000000003133A

==================================

[Code]
1: add rax, [rbx+rcx*8]
2: add rcx, 2
3: sub rax, [rbx+rcx*8]
4: inc rax

Q. 코드를 1까지 실행했을 때 rax 에 저장된 값은 무엇인가

A. 0x3133A

rcx*8은 즉 0x2 * 8 가 되고 이는 0x10 이된다. 즉, rbx+rcx*8는 0x555555554010가된다.

add rax, [rbx+rcx*8] 의 의미는 rax에 'rax + [rbx+rcx*8] 값을 넣어라' 이고 이는 0x31337 + 0x0000000000000003가 된다.

 

 

Q. 코드를 3까지 실행했을 때 rax 에 저장된 값은 무엇인가

A. 0

add rcx, 2 로 인해 rcx 값 0x2 가 0x4로 변하게된다. 

sub rax, [rbx+rcx*8] 은  rax에 'rax - [rbx+rcx*8] 값을 넣어라' 가 되고 

rbx+rcx*8 는 0x555555554000 + (0x4 * 8) = 0x555555554000  + 0x20 =  0x555555554020 가된다. 

즉, 원래의 rax 값 0x000000000003133A에서 0x000000000003133A를 빼서 0이 된다.

 

Q. 코드를 4까지 실행했을 때 rax 에 저장된 값은 무엇인가

A. 1

inc rax 는 rax 값을 1 증가시키는 것임으로  0에서 1을 더한 1이된다

 

3.3 논리연산 

논리 연산 명령어는 비트 연산을 지시한다. 

 

- 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의 비트 전부 반전

 

* xor을 두번하면 원래의 값으로 돌아온다. 

 

3.4 비교 연산자

 

- cmp op1, op2: op1과 op2를 비교
- test op1, op2: op1과 op2를 비교

 

이들은 두 연산자를 빼서 대소 비교를 한다. 이의 결과는 플래그 세그먼트를 보고 판단할 수 있다. 

 

3.5 분기

 

분기 명령어는 rip를 이동시켜 실행 흐름을 바꾼다

- jmp addr: addr로 rip를 이동시킵니다.
- je addr: 직전에 비교한 두 피연산자가 같으면 점프 (jump if equal)
- jg addr: 직전에 비교한 두 연산자 중 전자가 더 크면 점프 (jump if greater)

 

 

3.6 스택

 

- push val : val을 스택 최상단에 쌓음
- pop reg : 스택 최상단의 값을 꺼내서 reg에 대입

 

3.7 프로시저

 

컴퓨터 과학에서 프로시저(Procedure)는 특정 기능을 수행하는 코드 조각을 말한다.

프로시저를 사용하면 반복되는 연산을 프로시저 호출로 대체할 수 있어서 전체 코드의 크기를 줄일 수 있으며, 기능별로 코드 조각에 이름을 붙일 수 있게 되어 코드의 가독성을 크게 높일 수 있다.

 

- call addr : addr에 위치한 프로시져 호출
- leave: 스택프레임 정리

- ret : return address로 반환

스택프레임 - 스택의 영역을 명확히 구분하기 위해 존재

 

3.8 시스템 콜

 

운영체제는 연결된 모든 하드웨어 및 소프트웨어에 접근할 수 있으며, 이들을 제어할 수도 있다.

그리고 해킹으로부터 이 막강한 권한을 보호하기 위해 커널 모드와 유저 모드로 권한을 나눈다.

커널 모드는 운영체제가 전체 시스템을 제어하기 위해 시스템 소프트웨어에 부여하는 권한이다.

파일시스템, 입력/출력, 네트워크 통신, 메모리 관리 등 모든 저수준의 작업은 사용자 모르게 커널 모드에서 진행된다.

커널 모드에서는 시스템의 모든 부분을 제어할 수 있기 때문에, 해커가 커널 모드까지 진입하게 되면 시스템은 거의 무방비 상태가 된다.

유저 모드는 운영체제가 사용자에게 부여하는 권한이다.

브라우저를 이용하여 드림핵을 보거나, 유튜브를 시청하는 것, 게임을 하고 프로그래밍을 하는 것 등은 모두 유저 모드에서 이루어진다. 리눅스에서 루트 권한으로 사용자를 추가하고, 패키지를 내려 받는 행위 등도 마찬가지이다. 저 모드에서는 해킹이 발생해도, 해커가 유저 모드의 권한까지 밖에 획득하지 못하기 때문에 해커로 부터 커널의 막강한 권한을 보호할 수 있습니다.

시스템 콜(system call, syscall)은 유저 모드에서 커널 모드의 시스템 소프트웨어에게 어떤 동작을 요청하기 위해 사용된다.  유저 모드에서는 직접 할 수 있는 일이 많이 없으므로 커널이 도움을 줘야 한다. 

여기서, 도움이 필요하다는 요청을 시스템 콜이라고 한다. 유저 모드의 소프트웨어가 필요한 도움을 요청하면, 커널이 요청한 동작을 수행하여 유저에게 결과를 반환한다.

 

요청: rax

인자 순서: rdi → rsi → rdx → rcx → r8 → r9 → stack

 

 

 

반응형