Notice
Recent Posts
Recent Comments
Link
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | ||||||
2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 | 17 | 18 | 19 | 20 | 21 | 22 |
23 | 24 | 25 | 26 | 27 | 28 |
Tags
- Docker
- C언어
- wrefresh
- cbpf
- packet flow
- 어셈블리어
- doupdate
- vtable
- NAPT
- Compiler
- DOCKER-USER
- wnourefresh
- BOF
- ncurses
- epoll_wait
- .nret core 배포
- level trigger
- REDIS
- rfc5508
- edge trigger
- .net core 7
- architecture
- ioctl
- 풀이
- mvwin
- epoll
- packet filter
- LOB
- iptables
- 취약점
Archives
- Today
- Total
Tuuna Computer Science
인텔 기계어 정리 본문
일단 바이너리의 Opcode를 디스어셈블리하기 전에 일단 바이너리에 쓰여진 Instruction의 형태가 어떻게 구성되어 있는지 부터 알아야 할 것이다.
지금까지 공부한걸 정리할겸 포스팅 ~.~
- 기계어의 형식
컴퓨터는 0과 1밖에 모르는 기계이다. 이 기계가 특정 Instruction을 작동시키게 하려면 당연히 0과 1로 이루어진 수로 주어야 한다.
사람의 입장에서는 이 숫자들을 직관적으로 이해하기 어려워 이 Instruction을 바탕으로 mnemonic화 시켰다.
이를 Assembly어라고 한다.
디버거를 만들기 앞서 위에서 정의한 Instruction => Assembly(mnemonic)으로 디스어셈블 해야 하는 것이 목적이다.
fix
그럼 Disassemble하기 위해선 Intel의 Instruction 형식 구조를 살펴볼 필요가 있는데 이는 한글 자료가 많이 없기 때문에 원서인
intel architecture manual vol.2 이라는 최고의 책을 볼 필요가 있다.
(영어 못하는데 억지로 봄)
참고 : IA-32를 기준으로 한다. IA-32E는 ELF파싱도 안했을 뿐더러 legacy prefix랑 REX prefix가 추가로 붙는데 귀찮 ~~~~~
일단 기계어의 형식으로 보면 최대 6바이트로 이루어질 수 있다.
- Prefix
먼저 첫 번재 field인 Instruction Prefixes를 보자.
prefix는 선택적으로 쓰이는데 만약 쓰인다면 뒤에 나올 Opcode를 보좌시키는 역할을 한다.
이 prefix는 4가지의 형태로 둘 수 있다.
- Lock and repeat prefixes => 자원의 사용을 독점하거나, 반복적인 명령어를 수행해야 할때
- LOCK의 역할일 때 :: prefix로는 F0H
- REPNE, REPNZ의 역할 일땐 F2H
- REP,REPE, REPZ의 역할 일떈 F3H
- Segment override prefixes => 세그먼트를 재정의 할 때 사용된다.
- 2EH - CS segment override => mov ax, [cs:bx]
- 36H - SS segment override => mov ax. [ss:bp+si+3]
- 3EH - DS segment override
- 26H - ES segment override => mov ax, [es:60126]
- 64H - FS segment override
- 65H - GS segment override
- Operand-size override => 오퍼랜드의 크기를 재정의할 때 사용된다.
- [32 bit] 환경에서 16비트 오퍼랜드를 사용한다면 prefix로 66H를 붙여 여기서만 16비트 오퍼랜드를 사용함을 알린다.
- Address-size override prefix => 주소의 크기를 재정의할 때 사용된다.
- [32 bit] 환경에서 16비트 주소를 사용한다면 prefix로 67H를 붙여 여기서만 16비트 주소 크기를 사용함을 알린다.
- Opcode
instruction에서 핵심을 맡고 있는 핵심 명령부이다.
Opcode는 필수적으로 있어여 되는 항목이며, 최소 1Byte에서 최대 3Byte 까지 지정될 수 있다.
이 Opcode가 재밋는게(?) 같은 어셈블리어 명령일지라도 오퍼랜드의 유무, 오퍼랜드의 형식, 종류에 따라 ㅈㄴ게 바뀐다.
일단 간단하게 한번 보자
예를 들어 0x6A라는 Opcode를 보았을 때 이를 Opcode Table에서 확인해보면 상위 4비트는 세로면에서, 하위 4비트는 가로면에서 찾아보면
Push Ib 라는 Instruction이 있음을 알 수 있다.
자 명령어 뒤에 나타나는 Ib에 대해서 알아보자.
일단 Ib는 뒤에 나타나는 Operand를 명시하는 역할을 지닌다.
2개의 문자로 이루어 지는데 앞에 문자는 메모리 지정법(대문자로 표시), 뒤에 오는 문자는 Operand의 타입지정(소문자로 표시)
메모리를 지정해주는 문자를 먼저 보자 (필요하다고 생각한것만 번역함)
- A => 메모리 주소 직접 지정
- B => vex 접두사
- C => ModR/M의 reg가 controll register
- D => ModR/M의 reg가 debug register
- E => ModR/M 바이트가 opcode를 따르고 Operand(메모리 주소, 범용 레지스터)를 명시, 메모리 주소일 경우 segment register로 계산됨.
- F => EFLAGS/RFLAGS register
- G => ModR/M 바이트의 reg가 범용레지스터임을 의미
- H
- I => immediate data를 의미 (뒤에 따르는 명령어의 바이트를 edcode함)
- J => 명령어는 명령어 포인터 레지스터에 더해지는 상대적인 오프셋을 포함.
- L
- M => ModR/M 바이트는 오로지 메모리를 참조해야 함.
- N
- O => 명령어가 ModR/M 바이트를 가지지 않음
- P
- Q
- R => ModR/M 바이트의 R/M 부분은 범용 레지스터를 참조해야 함.
- S => ModR/M 바이트의 reg필드는 segment register를 선
- U
- V
- W
- X => 메모리는 ES:rDI 레지스터 쌍으로 주소 지정
- Y => 메모리는 DS:rSI 레지스터 쌍으로 주소 지정
오퍼랜드의 타입을 지정해주는 문자
- a => 메모리에서의 2개의 word operand 또는 2개의 double-word (operand, operand크기 속성 보호) only BOUND
- b => byte
- c => byte or word (operand의 크기 속성에 의존)
- d => double-word
- dq => double-quadword
- p => 32-bit, 48-bit, 80-bit pointer, operand의 크기 속성에 의존
- pd => 128-bit, 256-bit double-precision floating point data
- pi
- ps
- q => quardword
- qq => quard-quardword
- s
- sd
- ss
- si => doubleword integer register
- v => (in 64bit) word, doubleword, quardword, operand의 크기 속성 보호
- w => word
- x
- y
- z => 16bit operand 크기를 위한 Word 또는 32bit나 64bit operand 크기를 위한 double word
그럼 위에서 본 push Ib라는 instruction을 해석해 보자.
push 이 명령어는 무언가를 stack에 넣는 것을 의미한다. 그럼 그 무언가가 무엇인가. Ib를 의미하는데
위의 내용을 종합해서 I는 immediate data, b는 byte, => 상수의 값인 byte를 stack에 집어넣음을 의미한다.
- ModR/M 바이트
ModR/M 바이트는 Opcode가 지정하는 operand를 보좌하는 역할을 하는데 예를 들어 주소를 지정하는 등의 역할을 한다.
ModR/M를 쪼개면 2비트의 Mod, 3비트의 Reg, 3비트의 R/M으로 이루어져 있다.
Mod field는 R/M과 결합되어 진다. 만약 Mod의 값이 이진수로 (11)이라면 R/M과 결합되는 Operand가 레지스터임을 알린다.
나머지 값은 R/M과 결합되는 Operand가 메모리를 의미한다.
reg/opcode field는 레지스터의 번호를 명시한다.
R/M field는 오퍼랜드로써 레지스터를 구체화 시킨다.
ModR/M의 값을 계산하려면
Mod + reg + R/M으로 계산하면 된다.
- SIB 바이트
ModR/M 바이트 table에서 [-][-]를 볼 수 있을 것이다. 이는 SIB바이트를 필요로 함을 의미한다.
SIB 바이트는 ModR/M 바이트를 보좌하는 역할을 한다.
SIB 바이트는 3가지로 나뉘는데 2비트의 scale field와 3비트의 index field, 3비트의 base field가 존재한다.
먼저 scale field는 scale factor로서 scale value를 의미한다.
index field는 인덱스 레지스터의의 레지스터 번호를 구체화시킨다.
base field는 베이스 레지스터의 레지스터 번호를 구체화 시킨다.
- Displacement and Immediate Bytes
몇 몇의 주소 형식은은 즉시적인 변위를 따른다. ModR/M에서 또는 SIB에서. 만약 즉시 변위가 요구된다면 1, 2, 4byte로 이루어 진다.
- 어셈블리어에서 메모리지정 방식 (추가적으로 올림)
1. Immediate => Operand를 상수 값
2. Register => Operand를 레지스터로
3. Displacement => 지정 주소 = [Reg + Disp]
4. Base Register => 지정 주소 = [Reg + Base Reg]
5. Base Register + Displacement => 지정 주소 = [Reg + Base Reg + Disp]
6. Scale + Index Register + Displacement => 지정 주소 = [Reg + Base Reg + Scale Value + Disp]
7. Base Register + Index Register + Displacement => 지정 주소 = [Reg + Base Reg + Index Reg + Disp]
8. Base Register + Scale + Displacement => 지정 주소 = [Reg + Index Reg + Scale Value + Base Reg + Disp]
공부하면서 느낀 결론 : 빡침
이를 바탕으로 바이너리 바이트 코드를 해석 해야하는데 생각 생각 방법을 생각 생각
'Debugger' 카테고리의 다른 글
[ 32bit ELF Debugger ] 디스어셈블리러(disassembler), 디버거(debugger) 제작 일지2 (0) | 2019.02.20 |
---|---|
[debugger] 32bit binary disassembler 개발일지 (0) | 2019.01.14 |
ModR/M 바이트에서 Mod+R/M과 Reg와의 목적지(순서) 구분 & SIB 바이트 작성법 (0) | 2018.12.15 |
[디버거] ELF Parser 개발일지 .symbol내의 심볼 출력 (0) | 2018.12.04 |
[ELF] 리눅스 Debugger(디버거)만들기 (0) | 2018.12.02 |
Comments