
개요
개발자는 개발할 때 개발 언어를 통해 아이디어를 구체화합니다.
리버서는 만들어진 프로그램을 디스어셈블러를 통해 어셈블러로 번역해서 보거나 디컴파일러로 C/C++ 코드로 보기도 합니다.
그렇다면 CPU는 개발자가 컴파일러로 만든 최종 결과물 PE파일이나 ELF파일에서 명령어 단위를 어떻게 해석하는 것일까요?
Intel Manual에서는 CPU가 이를 해석하는 방식에 대해 설명하고 있습니다.
https://cdrdv2.intel.com/v1/dl/getContent/671110
Chapter2의 Instruction Format에 대해 설명한 글을 여러 자료와 함께 정리해보도록 하겠습니다.
Instruction Format

명령어 포맷은 위와 같이 순차적으로 나열되어 있으며, ModR/M부터 SIB, Diplacement, Immediate는 옵션입니다.
Prefix는 주로 Rex Prefix가 사용되어 명령어의 크기, 피연산자, 주소를 확장하는데 사용됩니다.
Opcode는 명령어로 1~3바이트가 사용되며 ModR/M의 reg 필드에서 추가로 3비트를 더 사용하기도 합니다.
ModR/M은 diplacement 사용 여부를 알려주는 Mode와 reg, r/m 필드를 가지고 있습니다.
SIB는 Scale, Index, Base로 이뤄져 있으며 이를 통해 배열과 같은 순차적인 요소에 쉽게 접근할 수 있습니다.
Diplacement는 메모리나 주소에 접근할 때 추가로 더해지는 값으로, 구조체의 멤버 변수에 접근할 때 사용합니다.
Immediate는 상수 값으로, 데이터의 값이 상수일 때 주로 사용됩니다.
Prefix
Prefix는 Opcode가 최대 3바이트+3비트의 제약을 갖고 있기에 이를 더 확장시키고자 사용하는 것입니다.
예를 들어 Opcode에서 mov 명령어는 32비트 형식의 Mov opcode까지만을 제공하는데, 여기서 x64비트 형식에
명령어를 추가하려면 위의 제약인 3바이트+3비트 크기를 넘어가게 됩니다.
Windows는 16비트부터 시작해서 32비트, 64비트로 단계별로 넘어오게 되었으며, 이들이 다 호환될 수 있도록 설계되었습니다.
따라서 예전부터 정해진 Opcode의 크기를 바꾼다는 것은 이전 버전의 윈도우와의 호환 체계의 문제가 발생하기 때문에 Prefix라는 개념을 도입하기 시작했습니다.
이런 방식을 통해 윈도우는 Opcode 자체만으로 명령어를 이해할 수도 있고, Prefix를 통해 Opcode를 추가적으로 해석할 수 있게 됩니다.
Intel Manual에서 크게 설명하는 Legacy Prefix는 총 4가지 그룹입니다.
Group 1 : Lock(0xF0) and Repeat Prefixes (0xF2, 0xF3)
Group 2 : Segment override Prefixes
Group 3 : Operand-size override Prefix (0x66)
Group 4 : Address-size override Prefix (0x67)
여기서 주로 사용되는 Prefix는 Group1과 Group3입니다.