IDA(Interactive DisAssembler Professional)는 정적 분석 도구이다. 좀 더 자세하게 말하면, 실행 파일을 정적으로 분석하는 역공학(리버스 엔지니어링) 도구이다. 하지만 이는 동적 분석 역시 어느정도 지원한다.
밑에 첨부하는 링크는 IDA 다운로드 링크이다.
신뢰할 수 없는 프로그램을 분석할 때는 악성 프로그램일 가능성을 대비하여 정적 분석을 먼저 시도해보는 것이 바람직하다.
1. 단축키
단축키 | 기능 |
G | 특정 주소나 심볼 이름으로 이동 |
N | 현재 선택한 주소나 변수, 함수에 이름 붙이기 |
X | 현재 위치(함수, 변수, 문자열 등)를 참조하는 모든 코드 위치를 보여줌 |
Y | 현재 위치의 데이터를 특정 타입으로 지정 (byte, word, ascii 등) |
ctrl + f | 코드나 문자열 등에서 특정 텍스트 검색 |
스페이스바 | 어셈블리 ↔ 그래프 보기 전환 |
F2 | 현재 위치를 북마크로 지정하거나 해제함 (중단점 설정) |
F9 | 디버깅 |
F7 | 한 줄씩 실행 (함수 안으로 들어감, STEP INTO) |
F8 | 함수는 실행만 하고 건너뜀 (STEP OVER) |
2. IDA 기능
2.1 디스어셈블링 (Disassembling)
디스어셈블린은 기계어(Binary) → 어셈블리어(Assembly) 로 바꾸는 과정
디스어셈블링의 목적
프로그램 흐름 파악 | main → check_password → print_result 같은 구조를 파악 |
취약점 분석 | 버퍼 크기나 조건문 분석을 통해 보안 이슈를 찾음 |
라이센스 우회 분석 | 등록 코드 확인 로직을 추적 |
악성코드 분석 | 백도어, C2 서버 주소, 해킹 루틴 추적 등 |
소스 코드가 없는 프로그램을 분석 | 리버스 엔지니어링용으로 자주 사용됨 |
3. HelloWorld.exe 분석 실습
파일 다운로드 링크 첨부
IDA에 다운로드한 파일을 끌어다 놓는다.
정적 분석은 main 함수를 먼저 찾는 일에서 시작된다. 왼쪽에 있는 function 창에서 main 함수를 찾는다. function 창이 안보이면 Shift + F3를 사용하여 창을 연다. 거기서 main을 입력하면 바로 보인다.
바이너리에서 어떤 함수를 찾는 방법은 크게 두 가지가 있다. 하나는 프로그램의 시작 지점임 진입점(Entry Point, EP)부터 분석을 시작하여 원하는 함수를 찾을 때까지 탐색하는 것이고, 다른 하나는 대상 함수의 특성이나 프로그램의 여러 외적인 정보를 이용하여 탐색하는 방법이다. 우리는 후자의 방식으로 main 함수를 탐색해 보겠다.
프로그램을 정적 분석할 떄, 많이 사용되는 정보 중 하나가 프로그램에서 포함된 문자열이다.
Shift + F12를 누르면 다음과 같은 바이너리에 포함된 다음과 같은 문자열이 열거된 String 창이 나타난다.
위 창에서 "Hello, world!\n"를 검색하거나 찾은 후 더블클릭하면 밑에 사진과 같은 창이 나타난다.
정적 분석을 하다가, 어떤 수상한 값이나 함수를 찾으면 우리는 이를 참조하는 함수를 분석하고자 한다. 많은 정적 분석 도구들은 상호 참조(Cross Reference, XRef)라는기능을 통해 이를 지원한다. 앞에서 찾은 "Hello, world!\n"라는 수상한 문자열이 어디서 사용되는지 추적해보자.
"aHelloWorld"를 클릭하고 상호 참조의 단축키 X를 누르면 문자열 xref 모습과 같이 xrefs(cross reference) 창이 나타난다.
여기서 aHelloWorld는 IDA가 그 위치(주소)에 대해 "aHelloWorld"라는 이름을 붙여준 것이다.
첫 번째 항목을 더블 클릭하여 따라가면 main 함수를 찾을 수 있다.
main 함수를 찾았으니 이제 F5를 눌러 디컴파일 해보자.
이 코드를 하나씩 분석해보자.
우선 IDA가 argc, argv, envp로 3개의 인자를 받는다고 해석한 것을 알 수 있다.
Sleep(0x3E8u);
- Sleep() 함수: 밀리초(ms) 단위로 프로그램을 멈춤
- 0x3E8 = 1000 (10진수) → 1초간 멈춤
- 즉, 프로그램 시작하자마자 1초 대기함
qword_14001DBE0 = (__int64)"Hello, world!\n";
- qword_14001DBE0는 어떤 전역 포인터 변수 또는 메모리 주소
- (__int64)는 형변환 → 64비트 정수로 해석
- "Hello, world!\n" 이라는 문자열 주소를 해당 변수에 저장
- 문자열 포인터를 어딘가에 저장해두는 코드
sub_140001060("Hello, world!\n");
- sub_140001060은 이름이 아직 붙지 않은 함수 (사용자 정의 함수일 가능성 높음)
- Hello, world!\n 문자열을 인자로 넘김
- 이 함수는 printf() 함수이다.
해당 변수가 전역변수인지 지역변수인지 알아보기 위해서는 해당 변수를 더블클릭한 후 아래의 표를 참고해 확인한다.
섹션 이름 | 주로 포함된 내용 | 읽기 / 쓰기 | 권한플랫폼 예시 |
.text | 코드(기계어, 함수) | 읽기 + 실행 | Windows / Linux 공통 |
.data | 초기화된 전역/정적 변수 | 읽기 + 쓰기 | 공통 |
.bss | 초기화되지 않은 전역/정적 변수 | 읽기 + 쓰기 | 공통 |
.rodata | 읽기 전용 데이터 (문자열 상수 등) | 읽기만 가능 | 🐧 Linux/ELF |
.rdata | 읽기 전용 데이터 (문자열 상수 등) | 읽기만 가능 | 🪟 Windows/PE |
sub_140001060 함수 분석
v1 = _acrt_iob_func(1u);
→ 이건 표준 출력(stdout)스트림을 가져오는 함수
3.1 중단점 설정 및 실행
이제 앞에서 분석한 내용을 동적 분석으로 살펴보겠다.
그 전에 중단점이 무엇이고 왜 필요한지 먼저 알아보자.
중단점(Breakpoint)은 프로그램이 실행될 때 특정 위치에서 멈추게 만드는 표시이다. 빨간 부분(중단점)에서 멈췄다가 F9 누르면 다시 계속 실행된다.
main 함수에 중단점을 설정해보자. 단축키는 F2이다.
이제 F9를 눌러 디버깅 해보자.
→ 다음 창과 터미널 창이 뜨면 동적 분석을 위한 준비는 끝났다.
3.2 한 단계 실행 (Step Over, F8)
이 화면의 왼쪽 아래에 있는 Stack view에 변화가 있는 것을 확인할 수 있다. 이는 해당 명령어인 sub rsp, 38h가 rsp 레지스터 값을 0x38(56 byte) 만큼 줄여서 현재 함수(main)가 사용할 스택 공간을 확보하는 명령어이기 때문에 주소가 0x38만큼 줄어든 것을 확인 할 수 있다.
이런식으로 IDA 창내에서 바뀐 부분을 찾아 분석하는 것이다. 그렇게 실행시키다 보면 터미널 화면에 "Hello, World!"가 찍혀있는 것을 확인할 수 있다.
3.3 함수 내부로 진입하기 (Step Into, F7)
위에서 실행했던 F8은 함수 내로 진입하지 않는 것을 알 수 있다.
이번에는 다음화면처럼 중단점을 설정하고 F9를 실행시킨다.
그리고 F7을 누르면 함수 내로 rip가 이동 것이 보인다.
3.4 실행중인 프로세스 조작하기
→ 다음과 같은 위치에 중단점을 설정한다.
→ rsp + 0x20에 delay값인 0x3e8이 저장되어 있음
→ 해당 값을 클릭하고 F2를 누른후에 값을 0xf4240(=1000000)을 입력한다. 그리곤 다시 F2를 눌러 값을 저장하면 delay값이 변경된 것을 확인 할 수 있다.
이제 F9를 눌러 Sleep 함수를 호출하면 아까와 달리 한참을 기다려도 프로세스가 재개되지 않는다. 1000초는 대략 20분이므로, 20분 정도를 대기해야 프로세스가 재개된다.