2024. 2. 20. 14:02ㆍInformation Security 정보보안/Reverse Engineering 리버스 엔지니어링
배울내용:
중앙처리장치 구조 및 작동원리
레지스터 구조
EAX
RAX
스택영역
힙영역
메모리 구조 및 작동원리
메모리 프로세싱 구조
메모리 Process 구조
프로그램 동작 및 작동원리
Reversing Engineering
리버싱 엔지니어링 기초
레지스터 (Register)
레지스터의 작동은 다음과 같이 이루어진다.
- 레지스터에 데이터 저장: CPU는 레지스터에 필요한 데이터를 로드합니다. 이 데이터는 RAM이나 캐시로부터 읽어올 수도 있고, 다른 레지스터에서 가져올 수도 있다.
- 데이터 처리: ALU는 데이터 레지스터에 있는 데이터를 조작하여 원하는 작업을 수행한다. 예를 들어, 덧셈, 뺄셈, 비교 등의 연산을 수행할 수 있다.
- 결과 저장: 처리된 데이터는 다시 레지스터에 저장되거나 메모리로 전송될 수 있다.
각각의 레지스터는 x86 아키텍처의 레지스터들로, 각각의 이름은 해당 레지스터가 저장할 수 있는 데이터의 크기와 목적을 나타내고. 이들은 16비트, 32비트, 또는 64비트 데이터를 저장할 수 있는 레지스터들이다.
- AH (High byte of AX):
- 8비트 데이터를 저장하는 레지스터
- AX 레지스터의 상위 8비트를 저장
- 주로 문자열 처리나 입출력 연산 등에서 사용
- AL (Low byte of AX):
- 8비트 데이터를 저장하는 레지스터
- AX 레지스터의 하위 8비트를 저장
- 주로 문자열 처리나 입출력 연산 등에서 사용
- AX:
- 16비트 데이터를 저장하는 레지스터
- AH와 AL을 합쳐서 사용
- 주로 메모리 주소 계산, 데이터 전송 등에서 사용
- EAX (Extended AX):
- 32비트 데이터를 저장하는 레지스터
- AX 레지스터를 확장한 것으로, 16비트의 AX 레지스터와 상위 16비트를 추가로 저장합
- 주로 산술 및 논리 연산, 함수 인수 전달 등에서 사용
- RAX (64-bit AX):
- 64비트 데이터를 저장하는 레지스터
- EAX 레지스터를 확장한 것으로, 32비트의 EAX 레지스터와 상위 32비트를 추가로 저장
- 64비트 시스템에서 주로 사용되며, 주소 계산, 산술 연산, 데이터 전송 등에 사용
테이블로 간단하게 볼수도 있고 레지스터는 이러한 구조로 이루어 져있다
그러면 주기억장치에는 어떠한 구조로 되어있고 작동을 하게 될까?
아래의 그림을 보자
4개의 영역으로 스택, 힙,데이터,코드 영역으로 나눠져있다
프로그래머가 스택 영역에서 할당하는 지역 변수나 매개 변수는 정적으로 크기가 결정된다.
그러나 힙 영역은 런타임에 동적으로 크기가 결정되는데,
이는 프로그램이 실행되는 동안 필요한 메모리를 동적으로 할당하거나 해제할 수 있도록 한다.
스택영역 과 힙 영역을 합해서 버퍼 오버플로우 라고 한다
이메모리 영역을 넘쳐흘르게 해서 공격자의 원하는 코드로 변조할수있다.
사용자가 힙이나 스택에 할당된 메모리 공간을 넘어서 데이터를 쓰거나 읽는 상황으로
보통은 프로그래머가 의도하지 않은 크기의 데이터를 저장하거나 읽을 때 발생수있다
이러한 공격은 보안상 매우 위험하며, 공격자가 제어할 수 없는 코드를 실행하도록 하는데 이용될 수 있다.
사용자가 운영체제한테 프로그램 실행 요청하면 보조기억장치( HDD ) 에 파일형태로 저장되어있는 파일을 주기억장치로 보내서(로딩) 실행이된다 이러한 일련의 과정을 Process 구조 라고 한다
그런데 이때 혼자서 실행을 하는게 아닌 CPU 의 허락을 맡고 실행이 된다
우리가 아까봣던 주기억장치의 구조중 Free Store가 껴있는데 이부분은 Stack 과 Heap 부분이 서로 점유할수있는 영역이다 . (Stack은 정해져있음, 정적이기 떄문, Heap 은 실행할때 프로그램 사이즈에 따라서 정해짐)
이게 만약 실행중이면 Processing 상태라고 볼수있다
아래의 리눅스를 켰을떄 ps aux 를 했을때 나오는 프로세스들이 모두 Processing 인 상태의 프로세스들이다
위와 같은 설명을 그림으로 보면 아래와 같다
중간에 Low 와 High 가 있는데 이는 주소의 방향 으로
Low address(낮은 주소)와 High address(높은 주소)는 메모리 주소의 방향을 나타낸다. 컴퓨터 메모리는 일반적으로 바이트 단위로 주소화되며, 이 주소들은 일반적으로 0부터 시작하여 메모리의 크기에 따라 증가한다.
- Low address(낮은 주소): 메모리의 가장 낮은 주소를 나타낸다. 주소가 낮을수록 메모리의 시작 부분에 해당
- 주소가 작아지면서 메모리의 시작부터 끝으로 이동한다
- High address(높은 주소): 메모리의 가장 높은 주소를 나타낸다. 주소가 높을수록 메모리의 끝 부분에 해당
- 주소가 커지면서 메모리의 끝에서 시작으로 이동한다.
이번엔 프로그램 (Program) 의 구조와 작동원리에 대해서 어떻게 되어있을까?
프로그래밍 언어중 C 언어의 가장 간단한 코드중 Hello World를 출력하는 것이다
왼쪽은 함수구조로 되어있고 오른쪽은 라이브러리를 불러와서 printf 라는 함수를 불러온것이다
예를들어 도서관에서 printf 하는 방법의 책을 가져와서 printf를 하는것이다
C 소스컴파일 과정은 아래처럼 된다
- 전처리(Preprocessing):
- 소스 코드 내에 있는 전처리 지시문(Preprocessor directives)이 처리된다. 이는 #include로 시작하는 헤더 파일 포함, 매크로 확장, 조건부 컴파일 등을 포함한다.
- 컴파일(Compilation):
- 전처리가 완료된 소스 코드가 컴파일러에 의해 어셈블리 코드로 변환된다. 이 과정에서 문법 오류를 확인하고 기계어로 변환된다.
- 어셈블(Assembly):
- 어셈블러가 어셈블리 코드를 기계어로 변환한다. 이 과정에서 각각의 명령어가 해당하는 기계어 코드로 변환한다.
- 링킹(Linking):
- 프로그램이 여러 개의 파일로 나누어져 있거나 외부 라이브러리를 사용하는 경우, 컴파일된 객체 파일들이 링커에 의해 하나의 실행 파일로 결합된다. 이때 필요한 라이브러리들도 추가된다.
간단하게 보면 소스코드 를 컴파일하면 링커가 실행파일이 만들어낸다.
3,4번 라인에 함수들이다
7,8번 라인은 전역변수 들이다 이부분들만 데이터 영역에 저장되고
그외에 지역변수나 매개변수는 스택영역으로 가게 된다
이걸 나중에 OllyDBG로 열었을때 함수들이 한번에 보이게 된다
그리고 우리는 그걸이용해 하나하나 디버거 해보면서 어떻게 진행되는지
어떻게 변조되었는지 확인할수있다
작동원리는 3가지 기능을 하는걸 볼수있다 main, fc1, fc2
중간에 힙영역에는 그럼 빌까?
정답은 아니다. 아직까지 실행이 안됐을 뿐이고 힙과 관련된 함수들이 작동을 안했기 때문이다
main 함수안에 malloc 동적할당함수이고 free 는 할당된 메모리영역을 해제하는 함수이다
이 이외에 new 도 있고 다른 함수들도 있다
그러면 어떤 함수들이 어떤 영역에 포함되는지 이젠 알수있다
그리고 이 함수들이 어떤 영역에서 오버플로우가 되어 변조되고 공격되는지 또한 알수도있다.