여기서 등장하는 리버싱, 리버스 엔지니어링, RE, 역공학, 역분석 등은 모두 동일한 의미로 사용된다.
소프트웨어의 구조, 기능, 동작 등을 분석하여 원리를 이해하고, 단점을 보완하거나 새로운 아이디어를 추가하는 모든 작업
내가 배웠던 리버싱의 목적은 다양했다.
0. 학습, 연구 목적의 프로그램 분석
1. 허가된, 또는 허가되지 않은 복제품의 생산
2. 유사 제품 또는 경쟁 제품의 개발
3. 한 플랫폼에서 다른 플랫폼으로의 이식
4. 악성코드나 취약점 같은 보안 상의 이슈 분석
5. 포렌식 분야에서 유의미한 정보 추출
실행파일의 종류는 다양하다. 윈도우에서는 .exe로 끝나는 파일들이 있고, 리눅스에선 .elf로 끝나는 파일들이 있는 등등.
이 파일들을 분석하는 리버싱은 크게 두가지가 있다. 정적 분석과 동적 분석
정적 분석은 파일을 실행하지 않고 관찰하는 것으로 쉽게 말해 관상을 보는 것이다.
파일의 종류, 크기, 헤더 정보, Import/Export API, 참조된 문자열, 패킹 여부 등등을 확인하는 방법. 파일을 직접 실행시키지 않았을 뿐이지 디스어셈블러로 내부 코드와 구조를 확인하는 것도 정적 분석의 범주에 속한다.
동적분석은 파일을 직접 실행시켜서 흐름을 관찰하고 디버깅을 통해 코드와 메모리를 자세히 살펴보는 것. 파일, 레지스트리(레지스터랑 다르다.), 네트워크 등의 상태를 지켜보며 프로그램의 동작을 분석할 수 있다. 그리고 디버거를 활용해 프로그램 내부 구조와 동작 원리를 분석할 수 있다.
어느 한 방식이 우월한 것은 아니다. 동적 분석이 더 많은 것을 볼 수 있는 것으로 보이지만, 만약 실행파일이 악성코드일 경우엔 동적 분석은 큰 리스크를 안고 진행해야 한다는 위험이 있다. 그래서 둘을 모두 해야한다. 정적 분석으로 정보를 수집해서 프로그램 구조와 원리를 사전에 예측해보고 이후 동적 분석에서 인사이트를 얻어내는 것이 유용하다.
리버싱을 공부하게 되면 바이너리란 용어를 자주 보게 되는데 바이너리=실행 파일으로 해석해도 괜찮다. 개발자가 작성한 소스 코드를 컴퓨터가 이해하기 위해서는 결국 0과 1로 이루어진 이진어까지 변환되는 과정을 거쳐야 하는데 그 이진코드를 바이너리 코드라고 부를 수 있다. 결국 프로그램과 바이너리가 같은 실행 파일을 지칭하게 되는 거니 혼용해서 사용해도 이해할 수 있을 것.
리버싱을 위해선 우리가 작성된 코드가 어떻게 실행파일로 만들어지는지 Building Process를 이해해야 한다. 우리가 C나 C++로 작성한 소스 코드는 이렇게 생겼을 것이다.
#include <stdio.h>
int sum(int x, int y){
return (x+y);
}
int main(int argc, char *argv[]){
int x=3;
int y=5;
return sum(x,y);
}
이 코드는 어떤 과정을 거칠까.
1. Pre-Processor : 전처리 기구를 거치면서 필요한 헤더, 매크로, 지시자 등을 처리한다.
2. Compiler : 아키텍처에 따라 번역하는 방식은 다르지만 인간이 작성한 고수준 언어를 저수준의 어셈블리어로 번역한다.
3. Assembler : 아키텍처에 따라 번역하는 방식은 다르지만 인간이 작성한 고수준 언어를 저수준의 어셈블리어로 번역한다.
4. Linker : 별적으로 컴파일된 여러 오브젝트 파일과 라이브러리를 엮어서 하나의 실행 가능한 파일(executable file)로 만든다.