반응형
서론
- 시스템 해커의 기본 지식
- 컴퓨터 언어, 운영체제, 네트워크, 암호학
그 중에서도 컴퓨터 언어는 가장 기본적으로 습득해야 하는 지식이다. - 시스템 해커는 컴퓨터의 언어로 작성된 소프트웨어에서 취약점을 발견해야 하기 때문이다.
- 컴퓨터 언어, 운영체제, 네트워크, 암호학
- 기계어(Machine Code)
- 컴퓨터 속에는 하나의 거대한 세계가 있따. 복잡한 논리적 인과관계가 존재하고, 여러개채가 상호작용하는데
- 그 세계에서는 기계어가 통용 된다. (해커는 그 세계의 허점을 공격하여 시스템을 장악한다.
- 0과 1로만 구성돼 있어서, 이해하기 어렵다.
- David Wheeler
- EDSAC을 개발하면서어셈블리 언어(Assembly Language), 어셈블러(Assembler) 를 고안했따.
- 어셈블리어로 코드를 작성하면 컴퓨터가 이해할 수 있는 기계어로 코드를 치환해주었다.
어셈블리어가 기계어보다 이해하기 훨씬 쉬웠기떄문에 개발자들이 편리하게 개발할 수 있었다.
- 역어셈블러(Disassembler)
- 소프트웨어를 역분석하는 사람들이 개발했다.
- 기계어를 어셈블리 언어로 번역하는것 이다.
기계어로 구성된 소프트웨어를 어셈블리 코드로 번역해준다.
어셈블리 언어🤖
- 어셈블리 언어 🤖
- 컴퓨터의 기계어와 치환되는 언어이다. 기계어가 여러 종류라면 어셈블리어도 여러 종류여야 한다.
- 명령어 집합구조를 설명했을때 CPU에 사용되는 명령어 집합 구조는 여러 종류가 있다.
(IA-32, x86-64, ARM, MIPS)
x64 어셈블리 언어
- 기본 구조🧱
- x64 어셈블리 언어는 단순한 문법 구조를 가진다.
- 동사에 해당하는 명령어(Operation Code, Opcode) 와 목적어에 해당하는 피연산자(Operand)로 구성된다.
명령어🔫
- x64에는 많은 명령어가 존재한다. 그중에서도 중요한 21개의 명령어를 자세히 학습한다.
- 피연산자 🎯
- 총 3가지 종류가 올 수 있다.
- 상수(Immediate Value)
- 레지스터(Register)
- 메모리(Menory)
- 메모리 피연산자는 []으로 둘러싸이게 표현한다, 앞에 크기 지정자(Size Directive) TYPE PTR이 추가될 수 있다.
- BYTE,WORD, DWORD, QWORD 가 올 수 있고 각각, 1바이트, 2바이트, 4바이트, 8바이트의 크기이다.
- 총 3가지 종류가 올 수 있다.
🐦자료형 WORD의 크기가 2바이트인 이유
- 초기에는 WORD의 크기가 16비트인 IA-16 아키텍처를 개발했었다, CPU에서 WORD는 16비트연기 떄문에 어셈블리어에서도 16비트로 정의하는 것이 자연스러웠다.
- 이후에 IA-32, x86-64 아키텍처는 CPU의 WORD가 32,64비트로 확장됐따. 그래서 이둘의 아키텍처에선 WORD의 자료형이 32,64비트의 크기가 자연스럽다.
- 하지만 인텔은 WORD 자료형의 크기를 변경하면 기존 프로그램들이 새로운 아키텍쳐와 호환되지 않을 수 있어서 WORD의 크기를 16비트로 유지하고, DWORD(Double Word, 32bit)와 QWORD(Quad Word, 64bit)를 추가했다.
x86-64 어셈블리 명령어
- 데이터 이동 🚚
- 어떤 값을 레지스터나 메모리에 옮기도록 지시한다.
mov dst, src :src 에 들어있는 값을 dst에 대입(src의 값이 없어지지 않는다.) | |
mov rdi, rsi | rsi의 값을 rdi에 대입 |
mov QWORD PTR[rdi], rsi | rsi의 값을 rdi가 가리키는 주소에 대입 |
mov QWORD PTR[rdi+8*rcx], rsi | rsi의 값을 rdi+8*rcx가 가리키는 주소에 대입 |
lea dst, src :src의 유효주소(Effective Address, EA)를 dst에 저장한다. | |
lea rsi, [rbx+8*rcx] | rbx+8*rcx를 rsi에 대입 |
- 산술 연산 ❌
- 덧셈, 뺄셈, 곱셈, 나눗셈연산을 지시한다. (곱셈과 나눗셈은 여기서 설명하지 않는다.)
add dst, src :dst에 src의 값을 더한다. | |
add eax, 3 | eax += 3 |
add ax, WORD PTR[rdi] | eax += 3 |
sub dst, str :dst에서 src의 값을 뺍니다. | |
sub eax, 3 | eax -= 3 |
sub ax, WORD PTR[rdi] | ax -= *(WORD*)rdi |
inc op: op : op의 값을 1 증가 | |
inc eax | eax +=1 |
dec op :op의 값을 1 감소 시킴 | |
dec eax | eax -= 1 |
- 논리연산 🤔 -and & or
- 논리 연산 명령어는 and, or, xor, neg등의 비트 연산을 지시한다. 이 연산은 비트 단위로 이루어진다.
and dst, src: dst와 src의 비트가 모두 1이면 1, 아니면 0
[Register]
eax = 0xffff0000
ebx = 0xcafebabe
[Code]
and eax, ebx
[Result]
eax = 0xcafe0000
or dst, src: dst와 src의 비트 중 하나라도 1이면 1, 아니면 0
[Register]
eax = 0xffff0000
ebx = 0xcafebabe
[Code]
or eax, ebx
[Result]
eax = 0xffffbabe
- 논리연산 🤔 -xor & not
xor dst, src: dst와 stc의 비트가 서로 다르면 1, 같으면 0
[Register]
eax = 0xffffffff
ebx = 0xcafebabe
[Code]
xor eax, ebx
[Result]
eax = 0x35014541
not op: op의 비트 전부 반전
[Register]
eax = 0xffffffff
[Code]
not eax
[Result]
eax = 0x00000000
비교 ⚖️
- 비교 명령어는 두 피연사자의 값을 비교하고 플래그를 설정한다.
- cmp op1, op2: op1과 op2를 비교
- cmp는 두 피연산자를 빼서 대소를 비교한다. 연산의 결과는 op1에 대입하지 않는다.
[Code]
1: mov rax, 0xA
2: mov rbx, 0xA
3: cmp rax, rbx ; ZF=1
// 예를 들어, 서로 같은 두 수를 뺴면 결과가 0이 뙤어 ZF플래그가 설정되는데,
// 이후에 cpu는 이 플래그를 보고 두 값이 같았는지 판단할 수 있다.
- test op1, op2: op1과 op2를 비교
- test는 두 피연산자에 AND 비트연산을 취한다. 연산의 결과는 op1에 대입하지 않는다.
[Code]
1: mov rax, 0xA
2: mov rbx, 0xA
3: cmp rax, rbx ; ZF=1
// 예를 들어, 서로 같은 두 수를 뺴면 결과가 0이 뙤어 ZF플래그가 설정되는데,
// 이후에 cpu는 이 플래그를 보고 두 값이 같았는지 판단할 수 있다.
분기🔀
- 분기 명령어는 rip를 이동시켜 실행 흐름을 바꾼다.
분기문은 여기 소개된 것 외에 굉장히 많은 수가 존재한다. 그러나 몇개만 살펴보면 이름에서 직관적으로 의미를 파악할 수 있기 떄문에, 이를 전부 다루기 보단, 앞으로 코드를 분석하면서 배울 수 있또록 한다.
- jmp addr: addr로 rip를 이동한다.
[Code]
1: xor rax, rax
2: jmp 1 ; jump to 1
- je addr: 직전에 비교한 두 피연산자가 같으면 점프 (jump if equal)
[Code]
1: mov rax, 0xcafebabe
2: mov rbx, 0xcafebabe
3: cmp rax, rbx ; rax == rbx
4: je 1 ; jump to 1
- jg addr: 직전에 비교한 두 연산자 중 전자가 더 크면 점프(jump if greater)
[Code]
1: mov rax, 0x31337
2: mov rbx, 0x13337
3: cmp rax, rbx ; rax > rbx
4: jg 1 ; jump to 1
결론
- 데이터 이동 연산자
- 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: addr로 rip이동
- je addr: 직전 비교에서 두 피연산자의 값이 같을 경우 addr로 rip 이동
- jg addr: 직전 비교에서 두 피연산자 중 전자의 값이 더 클 경우 addr 로 rip이동
끝
반응형
'hacking > pwnable' 카테고리의 다른 글
[Dream hack] Exploit Tech: Shellcode (0) | 2024.05.16 |
---|---|
[Dream hack] x86 Assembly🤖: Essential Part(2) (0) | 2024.05.09 |
[Dream hack] Quiz: Linux Memory Layout-7 (0) | 2024.05.01 |
[Dream hack] Quiz: Linux Memory Layout-6 (0) | 2024.05.01 |
[Dream hack] Quiz: Linux Memory Layout-5 (0) | 2024.05.01 |