실전바이너리분석 - 1장

2023. 4. 19. 12:36코딩/공부 [STUDY]

반응형

실전 바이너리 분석이란 책을 읽고나서 공부 한 부분들을 정리하고 이해가 안가는 부분은 더 공부해서 포스팅을 해보려한다.


바이너리 코드 : 컴퓨터가 연산 작업을 수행할 때 0,1로만 표현하는데 이를 바이너리 코드라고 부른다.

 

실행 가능한 바이너리 파일 : 시스템에 포함된 다양한 프로그램을 모두 분석하려면 각 프로그램이 갖고 있는 코드와 데이터를 추출하여 별도로 저장하는 방법이 필요함. 개중에는 실행시킬 수 있는 바이너리 프로그램이 있는데 이를 실행 가능한 바이너리 파일이라고 부름

 

컴파일러의 컴파일 과정 : 우리가 c언어로 된 프로그램을 컴파일러를 통해 컴파일한다고 가정해보자.

컴파일러는 c언어를 컴파일하여 해당 프로세서가 구동이 가능하게 만들어야 할 것이다.

따라서 c언어를 프로세서가 이해 가능한 언어로 변환해주어야 한다. 

기계어로 말이다 고급언어인 c언어에서 저급언어인 기계어로의 치환 과정을 알아보자면,

전처리 -> 컴파일 -> 어셈블리 -> 링킹 과정을 거치게 된다.

 

1. 전처리 과정

전처리 과정이란 간단하게 말해서 컴파일러가 처리할 부분만 남기도록 정리하는 과정이다.

우리가 c언어 코드에서 #include등을 이용하여 헤더파일등을 c언어 코드에 삽입하게 되는데, 

해당 단계에서 이 부분들을 모두 처리하고 컴파일할 코드만을 남겨놓는다.

stdio 헤더 파일에는 전역 변수 설정, 타입정의 등의 정보가 내포되어있는데 이를

복사하여 소스코드 내부에 삽입하는 것이다.

 

2. 컴파일 과정

위의 과정을 거치고 넘어온 전처리된 코드는 컴파일러의 컴파일 단계를 거쳐 어셈블리어로 변환된다.

곧장 기계어로 변환 되는 것이 아니라 어셈코드로 변환 되는 이유는 여러 언어에 대한 컴파일러를 일일히 만든다면

굉장히 많은 시간,비용이 소요된다. 따라서 고급언어 -> 어셈 -> 기계어 순으로 처리 되는 것이다.

 

3.어셈블 과정

컴파일 단계에서 얻은 어셈블리 코드를 이제 목적파일로 변환해야한다.

보통 하나의 소스코드는 하나의 어셈코드로 치환되고 이는 또 하나의 목적파일로 치환된다.

이 목적파일에서 가장 중요한 부분은 재배치가 가능하다는 것인데,

왜냐하면 목적파일은 서로 독립적으로 컴파일되기 때문에 어셈블러는 각 목적 파일을 어셈블할대 서로 다른 목적파일의 메모리 주소를 참조할수있는 방법이 없기 때문이다. 따라서 목적파일은 재배치 가능한 형태로 존재해야 순서대로 연결하여 실행 가능한 바이너리 파일을 만들 수 있게 된다.

 

4.링킹 과정

마지막으로 이 과정에서는 3번 과정에서 만든 목적 파일들을 모두 종합하여 실행 가능한 바이너리 파일로 만든다.

이 과정이 진행 될 때는 재배치 기호에 의한 참조 방식을 기호 참조라고 한다.

마지막으로 링커는 프로그램에 포함된 모든 목적 파일들을 병하바여 하나의 실행가능한 형태로 변환하고

메모리의 특정 주소 공간에 로드되도록 한다.

여기서 공유 라이브러리의 경우에는 시스템에서 동작 중인 모든 프로그램이 함께 공유하는데 때문에 모두 공유하는 메모리 공간에 배치된다.

 

심벌,스트립 바이너리 : 인간이 코딩할 땐 함수와 변수명을 정의할 때 의미가 있거나 이해하기 쉬운 이름으로 정의한다.

하지만 컴파일 과정을 거치고 나면 컴파일러는 이를 '심벌'이라는 일종의 기호를 이용하여 각 이름을 처리하고 바이너리 코드와 데이터들을 심벌의 상관관계로 기록한다.

리눅스의 경우에는 ELF의 바이너리에는 디버깅 심벌을 dwarf형식으로 생성되고

윈도으의 경우 PE바이너리에 PDB의 형식을 사용한다.

 

바이너리가 메모리에 적재되는 과정 : 특정 바이너리를 실행할 시에 운영체제는 먼저 해당 프로그램을 실행하고자 새로운 프로세스를 설정하고

이를 위한 가상 메모리 주소를 준비한다. 그 후에는 운영체제는 인터프리터를 해당 프로세스의 가상 메모리에 연결한다.

일반적으로 리눅스의 경우에는 ld-linux.so라는 공유 라이브러리를 사용하고 윈도우의 경우에는 ntdll.dll파일 내부에 기능이 구현 되어있다. 인터프리터가 구동괴면 커널이 제어 권한을 부여하고 인터프리터가 해당 바이너리를 사용자 영역에서 수행 하게 한다.

1. 목적 파일을 objdump 도구를 이용하여 디스어셈블 해보면 문자열을 가리키는 포인터, call 명령의 피연산자 등이 엉뚱한 위치를 가리키고 있다. 그 이유를 설명하시오

-목적파일에서 데이터와 코드를 참조할 때에 아직 정확한 참조 의존성이 해결되지 않은 상태이기 때문이다
따라서 컴파일러는 아직 해당 파일이 실제로 로드될때의 베이스 주소가 어떻게 설정될지 알 수 없기 때문이다.

2. 다음 중 strip --strip-all a.out 명령어를 사용하여 바이너리를 스트립했을 때 제거되지 않는 정보는?
  1) 사용자 정의 함수의 이름
  2) 전역변수의 이름
  3) 바이너리가 메모리에 로드될 때 동적으로 의존성 문제를 해결하고자 필요한 정보

-3번

3. 하드디스크에 저장돼 있는 바이너리가 메모리에 적재될 때 그 형태가 반드시 일대일로 대응되지 않는 이유 중 하나를 말하시오
-  대용량의 데이터가 0으로 초기화된 상태라면 해당 바이너리가 디스크에 축소되어서 저장된다. 하지만 메모리에 로드 될때에는 실제로 0들을 덧붙인만큼 확장되어서 로드 된다. 이러한 이유들로 일대일 대응이 되지 않는다.

4. 지연 바인딩에 대해 설명하시오
- 인터프리터는 바이너리를 가상 메모리 공간에 적재하는데 이때 바이너리 코드영역에 라이브러리 참고를 위한 주소 값을 채워 넣으면서 마무리 하게 되는데, 특정 함수에 대한 수요가 발생하는 처음 순간에만 참조 절차를 수행하도록 하는 것이다.


5. C언어로 작성된 프로그램의 컴파일 과정 4단계를 차례대로 말하시오
- 전처리 컴파일 어셈블 링킹

6. ELF 바이너리에서 코드와 데이터가 존재하는 영역을 분리시킨 이유 중 하나를 말하시오
-코드와 데이터를 분리함으로써 실행 효율성을 높이기 위함이다.
코드영역은 읽기전용으로 설정되어 있어서 코드가 실행될때 더 이상 수정되지 않을 것이고
따라서 코드영역을 분리함으로써 캐시메모리에 더 많은 코드를 저장하고 실행 시킬수 있다.

7. 디버깅 정보들이 제품 출시용 바이너리에 포함되지 않는 이유를 2가지 이상 말하시오

-디버깅 심볼들이 출시용 바이너리에 포함된다면 포함된 심볼들을 이용하여 역공학 공격을 행하는 단서들을 많이 제공하기 때문이다.

8. 링커가 하는 일을 설명하시오
- 어셈블 과정에서 생성된 독립적인 목적파일들을 재배치하여 실행가능한 바이너리 파일로 만든다.

9. 컴파일 단계에서 기계가 바로 해석할 수 있는 기계어 코드가 아닌 어셈블리 언어를 사용하는 이유를 설명하시오
-각각의 언어에 맞는 머신코드를 직접 생성하는 컴파일러를 일일이 만들기엔 복잡하고 시간이 많이 소요되기 때문에.

10. gcc 에서 전처리 단계까지만 수행하기 위해 필요한 옵션을 말하시오
- -E



반응형

'코딩 > 공부 [STUDY]' 카테고리의 다른 글

실전바이너리분석 - 6  (1) 2023.05.03
[STUDY] - 스택,덱,큐  (0) 2023.04.22
[STUDY] 유클리드 호제법  (0) 2023.04.17
[STUDY] 동적계획법 (dynamic programming)  (0) 2023.04.16
[STUDY] 어셈블리어 - 6  (0) 2023.02.28