Upload
others
View
17
Download
0
Embed Size (px)
Citation preview
시스템 해킹과 보안
리버스 엔지니어링
2/60
Contents
01 리버스 엔지니어링의 이해
02 리버스 엔지니어링 툴
03 리버스 엔지니어링 공격
04 리버스 엔지니어링의 대응책
학습목표
리버스 엔지니어링의 개념을 이해한다
리버스 엔지니어링 툴을 사용할 수 있다
리버스 엔지니어링 공격을 수행할 수 있다
리버스 엔지니어링 공격의 대응책을 이해한다
3/60
리버스 엔지니어링의 이해
리버스 엔지니어링(Reverse Engineering) : 역공학(逆工學), 장치나 시스템의 구조를 분석하여 원리를 발견하는 과정
그림 5-1 리버스 엔지니어링의 개념
➊ 이미 만들어진 프로그램 동작 원리 이해, 유사 프로그램 제작
➋ 프로그램의 보안 문제, 동작 문제, 오류 등을 이용,
제품 출시 전 문제점과 오류 제거·검토
➌ 바이러스(또는 웜), 백신 제작
4/60
리버스 엔지니어링 툴
이뮤니티 디버거
그림 5-2 이뮤니티 디버거 다운로드
5/60
리버스 엔지니어링 툴
그림 5-3 이뮤니티 디버거 인터페이스
6/60
리버스 엔지니어링 툴
➊ 메모리 주소 : 프로그램을 메모리에 로드했을 때 메모리상에 로드된 프로그램 명령어
코드 및 데이터 가상 주소와 라벨을 표시한다.
➋ 기계어 코드와 어셈블리어 코드 : 프로그램의 기계어 코드와 어셈블리어 코드를 출력한
다.
➌ 주석 창 : 이뮤니티 디버거가 분석한 API 함수, 문자열, 함수 인자를 확인할 수 있고,
사용자가 주석을 입력할 수도 있다.
➍ 레지스터와 플래그 창 : 주요 레지스터와 플래그의 상태 값을 확인할 수 있다.
➎ 변수 값 출력 창 : 디버깅 중에 주요 변수에 어떤 값이 들어갔는지 확인할 수 있다.
➏ HEX 덤프 창 : 프로그램을 HEX 값으로 덤프하여 보여주며,
바이너리 파일을 직접 수정할 때 사용한다.
➐ 스택 : 스택의 현재 상태 등의 확인할 수 있다.
➑ 명령 창 : 파이썬 코드 등을 실행한다.
그림 5-2 이뮤니티 디버거 다운로드
7/60
리버스 엔지니어링 툴
표 5-1 이뮤니티 디버거 키와 기능
8/60
리버스 엔지니어링 툴
그림 5-4 Step into, Step over, Execute till return의 차이
A 변수를 정의하고 Plus 함수를 호출했다.
Plus 함수는 A 변수에 2를 열 번 반복하여 더하고 리턴한다.
Plus 함수를 호출하기 바로 직전에 Step into하면, A 변수가 0부터 2씩 반복해서 더하여
2,4,6, …이 되어 가는 과정을 볼 수 있다.
Step over하면, A 변수 값이 Plus 함수를 한 번에 실행하여 20이 된 결과만 나온다.
9/60
리버스 엔지니어링 툴
이뮤니티 디버거 사용법
그림 5-5 REVERSE_1.EXE 실행 결과
10/60
리버스 엔지니어링 툴
[그림 5-3]에서 REVERSE_1.EXE 파일을 열고 F9를 눌러 프로그램을 실행하면 다음과 같
다.
ModuleEntryPoint라는 곳에서 프로그램이 멈춘다. ModuleEntryPoint는 운영체제에서 프
로그램으로 처음 진입하는 지점인데, 일반적으로 프로그램의 main 함수와 동일한 의미로
쓰지만 실행하는 프로그램의 종류 및 환경에 따라 라이브러리의 한 지점일 수도 있다.
그림 5-6 ModuleEntryPoint 진입
11/60
리버스 엔지니어링 툴
[그림 5-3]에서 REVERSE_1.EXE 파일을 열고 F9를 눌러 프로그램을 실행하면 다음과 같
다.
ModuleEntryPoint에서 이뮤니티 디버거가 멈추는 것은 설정 사항이다.
[Options]-[Debugging options] 메뉴의 [Events] 탭에서 설정 사항을 확인할 수 있다.
그림 5-7 첫 번째 중지 지점 설정
12/60
리버스 엔지니어링 툴
[그림 5-8]의 어셈블리어 창에서 마우스 오른쪽 버튼을 눌러
[Search for]-[All referenced text strings] 메뉴를 선택해 보자.
그림 5-8 프로그램에 있는 모든 문자열을 찾는 메뉴 실행
13/60
리버스 엔지니어링 툴
[그림 5-9]의 문자열이 위치한 주소와 어셈블리 명령어, 찾은 문자열 내용을 확인할 수 있다.
그림 5-9 프로그램에 있는 모든 문자열을 찾는 메뉴 실행
14/60
리버스 엔지니어링 툴
커서가 있는 0x00401028에서 “Hello World!” 문자열을 확인할 수 있는데, printf 함수가
위치한 곳이다. 그리고 REVERSE_!.EXE 파일의 main 함수 시작인 0x00401028 주소 중에
브레이크 포인트가 설정된 것을 확인할 수 있다.
그림 5-10 “Hello World!” 문자열을 이용한 printf 함수 위치 확인
15/60
리버스 엔지니어링 툴
커서가 main 함수에 이미 브레이크 포인트가 설정되어 있으니 F4(Run)를 누르면
브레이트 포인트를 설정한 줄(0x00401010 main)까지 실행한다.
그림 5-11 0x00401010 main까지 실행
16/60
리버스 엔지니어링 툴
이제 F7(Step into)을 눌러 한 줄씩 살펴보자. PUSH EBP를 실행한 결과는 EIP 값에서도
확인할 수 있다. EIP 값은 다음에 수행할 명령어 주소를 가리키고, 현재 값은 00401011이다
EBP 값은 0019FF80이고, 이 값은 오른쪽 아래 스택 창에서 맨 윗줄 0019FF40에 들어 있다
그림 5-12 PUSH EBP를 실행한 화면
17/60
리버스 엔지니어링 툴
다시 한 번 F7(Step into)을 눌러 한 줄을 실행해 보자.
00401011 줄에서 ‘MOV EBP, ESP’를 실행한다.
실행 결과 EBP 값은 빨간색으로 표시하며, ESP에 저장된 값(0019FF40)으로 바꾼다.
그림 5-13 MOV EBP, ESP를 실행한 화면 : [그림 5-12] +F7 한 번
18/60
리버스 엔지니어링 툴
SUB ESP, 40 : ESP에서 64를 빼는 것으로, 스택에 0x40(64)바이트 공간을 할당한다.
PUSH EBX, PUSH ESI, PUSH EDI : 스택에 EBX, ESI, EDI 값을 저장한다.
그림 5-14 PUSH EBX, PUSH ESI, PUSH EDI를 실행한 화면 : [그림 5-13]+F7 네 번
19/60
리버스 엔지니어링 툴
다음으로 커서가 LEA EDI, DWORD RTP SS:[EBP-40] 줄에 위치한다.
이 명령은 EBP에서 0x40(64)를 뺀 주소(0012FF40=0012FF88-40)를 EDI에 저장한다.
그림 5-15 LEA EDI, DWORD PTR SS : [EBP-40]을 실행한 화면 : [그림 5-14]+F7 한 번
20/60
리버스 엔지니어링 툴
다시 F7을 두 번 누르자.
MOV ECX, 10 : ECX에 00000010을 저장한다.
MOV EAX, CCCCCCCC : EAX에 CCCCCCCC값을 저장한다.
그림 5-16 MOV ECX. 10, MOV EAX, CCCCCCCC를 실행한 화면 : [그림5-15]+F7 두 번
21/60
리버스 엔지니어링 툴
레지스트리의 EDI 값과 스택에서 EDI가 가리키는 주소, ECX 값 변화를 주시하면서 F7을
두 번 더 눌러보자. 레지스트리 창에서 ECX 값이 2 줄어 0000000E가 된다.
그림 5-17 REP STOS DWORD PTR ES : [EDI]를 반복 실행한 결과 : [그림5-16]+F7 두 번
22/60
리버스 엔지니어링 툴
이후부터는 F7을 한 번 누를 때마다 ECX 는 1씩 줄어들고, EDI 는 4씩 늘어난다.
그리고 [그림 5 -18]과 같이 EDI가 가리키는 스택은 매번 CCCCCCCC로 채운다.
이렇게 카운터 ECX가 0이 될 때까지 ‘REP STOS DWORD PTR ES :[EDI]’ 줄을 실행한다.
그림 5-18 REP STOS DWORD PTR ES : [EDI]를 반복 실행한 결과 : [그림5-17]+F7 네 번
23/60
리버스 엔지니어링 툴
‘REP STOS DWORD PTR ES :[EDI ]’ 줄에서 다음 줄로 넘어가려면 ECX 값인 16 (0x10,‘
MOV ECX ,10 ’ 줄의 실행 결과)만큼(열여섯 번) F7을 눌러야 하는 것이다.
F7을 계속 눌러 ‘REP STOS DWORD PTR ES :[EDI ]’를 모두 실행하고,
커서가 [그림 5-19]와 같이 ‘CALL REVERSE _.00401060 ’ 줄에 위치하도록 하자.
이 줄은 바로 “Hello World !”를 실행하려고 printf 명령을 호출하는 포인트이다.
여기에서 사용자는 다시 F7(Step into)이나 (Step over)을 선택할 수 있다.
그림 5-19 CALL REVERSE_00401060 함수 줄 확인 : [그림5-18]+F7 열 번
24/60
리버스 엔지니어링 툴
먼저 F7을 누르면 다음과 같이 실제로 printf 함수가 구현된 어셈블리어 코드로 화면이 넘어간다.
그림 5-20 CALL REVERSE_00401060 함수 줄에서 F7을 실행한 결과 : [그림5-19]+F7 한 번
25/60
리버스 엔지니어링 툴
먼저 F7을 누르면 다음과 같이 실제로 printf 함수가 구현된 어셈블리어 코드로 화면이 넘어간다. 하지만 F8(Step over)를 누르면 다음과 같이 ‘CALL REVERSE_.00401060’이 실행된 바로 다음 줄로 넘어간다.
즉, F8은 원칙적으로 한 줄씩 실행하며, F8은 해당 줄을 일괄적으로 수행한다. F8은 앞서 살펴본
‘REP STOS DWORD PTR ES: [EDI]’ 줄에서도 똑같이 적용된다.
그림 5-21 CALL REVERSE_00401060 함수 줄에서 F8을 실행한 결과 : [그림5-20]+F8 한 번
26/60
리버스 엔지니어링 툴
프로그램의 변수 값 변경 후 디버깅
레지스트리 값 수정
그림 5-22 ECX 레지스트리 값을 직접 변경
27/60
리버스 엔지니어링 툴
어셈블리어 코드 수정
그
그림 5-23 어셈블리어 코드를 수정하여 ECX 레지스트리 값 변경
28/60
리버스 엔지니어링 툴
값 찾기
문자열 찾기 : 분석할 포인트를 찾는 데 가장 많이 사용하는 기능
그림 5-24 문자열 검색 창
29/60
리버스 엔지니어링 툴
값 찾기
스택과 파일 덤프 위치 찾기
[그림5-25]와 같이 REP STOS DWORD PTR ES:[EDI] 줄에서 F7을 눌러 두세 번 실행한
후 EDI 값에서 마우스 오른쪽 버튼을 누르면 [Follow in Dump]와 [Fllow in stack] 메뉴를
확인.
그림 5-25 레지스터에 기록된 주소 값의 덤프와 스택 값을 확인하는 메뉴
30/60
리버스 엔지니어링 툴
‘Follow in Dump’와 ‘Follow in Stack’ 명령으로 [그림 5-26]과 같이 각각 왼쪽 아래 파일 덤프 창과 오른쪽 아래 스택 창에서 0019FF00 주소 위치로 이동할 수 있다
([그림 5-26]은 ‘Follow in Dump’와 ‘Follow in Stack’ 명령을 모두 실행한 결과이다).
그림 5-26 파일 덤프 창과 스택 창에서 0019FF00 주소 값 확인
31/60
리버스 엔지니어링 툴
스택 창 기능
EBP, ESP 주소 값으로 스택 창 이동
그림 5-27 EBP에 해당하는 주소로 스택 창 이동
그림 5-28 EBP에 해당하는 주소로 스택 창을 이동한 결과
32/60
리버스 엔지니어링 툴
스택 고정
스택 값 확인
스택 상대 주소 확인
이뮤니티 디버거에서는 스택 창에서 EBP, ESP 또는 사용자가 선택한 부분에서 상대 주소를 확인할 수 있다. 현재 EBP를 기준으로 스택의 상대 주소를 확인할 때는 [Address]-[Relative to EBP] 메뉴를 선택한다.
그림 5-29 EBP의 상대 주소로 스택 주소 창 값을 변환
33/60
리버스 엔지니어링 툴
그러면 스택 창에 EBP 위치가 표시되고, 각 스택의 주소가 EBP 기준으로 몇 바이트 위치에 있는지 표시한다.
그림 5-30 EBP를 기준으로 스택의 상대 주소를 출력한 결과
34/60
리버스 엔지니어링으로 얻을 수 있는 것은 프로그램 실행 구조와 로직이다.
실습 5-1 바이너리 파일을 수정하여 리버스 엔지니어링 공격하기
35/60
REVERSE_2.exe 동작 확인하기
실행시 다음과 같이 패스워드를 입력
abcd 입력시 틀리면 ‘∼ !! is a Wrong Password!’,
맞으면 ‘abcd !! is the Correct Password!’ 출력
그림 5-31 Reverse_2.exe 실행 결과
파일 열기
[File]-[Open]에서 Reverse_2.exe 파일 열기
실습 5-1 바이너리 파일을 수정하여 리버스 엔지니어링 공격하기
1
2
36/60
리버스 엔지니어링 포인트 찾기
잘못된 패스워드 입력시, ‘∼ !! is a Wrong Password!’메시지 출력한다는 것을 확인.
이 메시지 출력 지점을 시작점으로 검증 로직을 찾아보자.
[그림 5-6]과 같이 F9로 프로그램을 실행시켜 ModuleEntryPoint에 진입한 후
이뮤니티 디버거의 어셈블리어 창에서 마우스 오른쪽 버튼을 눌러 [Search for]-[All
referenced text strings] 메뉴 실행
그림 5-32 [Search for]-[All referenced text strings] 메뉴의 실행
실습 5-1 바이너리 파일을 수정하여 리버스 엔지니어링 공격하기
3
37/60
리버스 엔지니어링 포인트 찾기
해당 문자열에서 마우스 오른쪽 버튼을 누른 후 [Follow in Disassembler] 메뉴를 선택
한다.
그림 5-33 [Follow in Disassembler] 메뉴 실행
실습 5-1 바이너리 파일을 수정하여 리버스 엔지니어링 공격하기
3
38/60
프로그램 변경 포인트 찾기
표 5-2 [그림 5-33]에서 확인한 어셈블리어 코드
실습 5-1 바이너리 파일을 수정하여 리버스 엔지니어링 공격하기
4
39/60
(1)번의 ‘CALL REVERSE_.004010E0’부터 (3)번까지 : 어떤 값을 비교
(4)번, (10)번 : 점프문
(4)번 점프 주소 : 0040108A, 이 값은 (11)번 주소 값
(10)번 점프 주소 : 0040109B, 이 값은 (16)번 주소 값
문자열을 비교(어셈블리어코드(1)∼(3))
ZF값 0이 아니면 0040108A 주소로 점프
0040108A 주소에‘%s !! is a Wrong Password!’
출력 구문(어셈블리어 코드 (11)∼(14))
ZF 값이 0 이면 맞는 패스워드 출력
(어셈블리어 코드 (5)∼(8)) 0040109B로 점프
함수종료
실습 5-1 바이너리 파일을 수정하여 리버스 엔지니어링 공격하기
40/60
JNZ 다음의 주소를 00401077로 바꾸면 ZF 값에 관계없이
문자열 비교 결과와 상관없이 항상 맞는 패스워드 출력, 함수 종료
실습 5-1 바이너리 파일을 수정하여 리버스 엔지니어링 공격하기
41/60
프로그램 변경하기
변경할 줄에서 마우스 오른쪽 버튼 클릭 [Assemble] 메뉴 선택
JNZ 목적 주소지 00401077로 바꾼 뒤 <Assemble> 버튼 클릭 팝업창 닫기
그림 5-36 어셈블리어 구문 변경
실습 5-1 바이너리 파일을 수정하여 리버스 엔지니어링 공격하기
5
42/60
프로그램 변경하기
해당 줄에서 마우스 오른쪽 버튼 클릭 [Copy to executable]-[Selection] 메뉴 선택
마우스 오른쪽 버튼 클릭 [Save File] 메뉴로 파일을 다른 이름으로 저장
(a) 변경된 프로그램 저장하기 1
실습 5-1 바이너리 파일을 수정하여 리버스 엔지니어링 공격하기
5
43/60
프로그램 변경하기
(b) 변경된 프로그램 저장하기 2
그림 5-37 변경된 프로그램 저장
실습 5-1 바이너리 파일을 수정하여 리버스 엔지니어링 공격하기
5
44/60
결과 확인하기
그림 5-38 변경된 프로그램 확인
실습 5-1 바이너리 파일을 수정하여 리버스 엔지니어링 공격하기
6
45/60
프로그램의 검증 로직을 우회하는 공격 수행해 보자.
파일 열기
리버스 엔지니어링 포인트 찾기
그림 5-39 REVERSE_2.exe 검증 로직 부분
실습 5-2 프로그램 로직을 분석하여 리버스 엔지니어링 공격하기
1
2
46/60
로직 분석하기
‘PUSH OFFSET REVERSE_.0042206C’부터 앞서 수정한 ‘JNZ SHORT
REVERSE_.0040108A’까지 살펴보자.
실습 5-2 프로그램 로직을 분석하여 리버스 엔지니어링 공격하기
3
표 5-3 [그림 5-39]에서 확인한 어셈블리어 코드
47/60
(1), (2)번 프로그램 실행 시 출력되는 ‘Input the password!!’ 출력
(3) ∼ (8) 입력된 패스워드 스택 저장
(9)∼(10)번 SS : [EBP-18] 주소 값 ECX에 저장
(11)∼(12)번 SS : [EBP-C] 주소 값 EDX에 저장
(13)번에서 REVERSE_004010E0(strcmp) 함수 호출
실습 5-2 프로그램 로직을 분석하여 리버스 엔지니어링 공격하기
48/60
로직 확인
004010E0에서 F2를 눌러 브레이크 포인트 설정→F4로 브레이크 포인트까지 실행
DOS 창에서 ‘Inputthe password!!’ 메시지 확인
그림 5-40 Reverse_2.exe 프로그램 실행
실습 5-2 프로그램 로직을 분석하여 리버스 엔지니어링 공격하기
4
49/60
로직 확인
[그림 5-40]과 같이 aaaa를 입력하고 Enter : 이뮤니티 디버거에서 브레이크 포인트에
서 정지 확인
그림 5-41 REVERSE_2.exe 프로그램 실행 후 브레이크 포인트 확인
4
실습 5-2 프로그램 로직을 분석하여 리버스 엔지니어링 공격하기
50/60
로직 확인
(9)∼(10)번 SS : [EBP-18] 주소 값 ECX에 저장
(11)∼(12)번 SS : [EBP-C] 주소 값 EDX에 저장
ECX에는 스택의 주소 값인 0019FF28을 저장, EDX에는 0019FF34를 저장
그림 5-42 0019FF28, 0019FF34 스택 내용 확인
4
실습 5-2 프로그램 로직을 분석하여 리버스 엔지니어링 공격하기
51/60
로직 확인
[그림 5-41]부터 F7로 한 줄씩 실행. 004010FA가지 실행하면 ECX에 있는 값에 EDX
값을 1바이트씩 비교한다. [그림 5-43]에서는 abcd의 a와 aaaa의 a를 비교하며, [그림
5-44]에서는 abcd의 b와 aaaa의 a를 비교한다.
그림 5-43 004010F2 실행
그림 5-44 004010FA 실행
4
실습 5-2 프로그램 로직을 분석하여 리버스 엔지니어링 공격하기
52/60
리버스 엔지니어링에 대한 대응책
패킹
안티 리버싱(Anti-Reversing) : 리버스 엔지니어링을 어렵게 만드는 기술
패킹(Packing) : 원본 프로그램을 새로운 파일에 패킹된 형태로 압축, 암호화하여 저장하
는 기술
그림 5-45 패킹 개념
패킹된 프로그램은 프로그램이 정상적으로 실행될 수 있도록 복호화하고 압축을 풀어내
는 로직 포함
53/60
리버스 엔지니어링에 대한 대응책
안티 디버깅 : 수행되는 디버거의 활동을 탐지하여 프로그램 강제 종료
타이밍 체크
프로세스가 디버깅 중 CPU 연산 시간이 정상적으로 실행되었을 때보다 많이 걸린다는 점
에서 착안
RDTSC(Read Time-Stamp Counter) 명령 : 두 구간 사이의 Time Stemp 값 비교
시간이 많이 걸리는 특정구간을 디버깅 중으로 판단, 프로그램이나 디버거 종료
쓰레기 코드 넣기와 치환
코드 사이에 의미가 없는 쓰레기(Garbage Code) 코드를 넣고
다단계의 점프 문을 섞어 코드를 치환(Permutation) 배치
54/60
UPX : 가장 일반적으로 쓰이는 패킹기술, 주목적은 코드의 암호화가 아닌 압축로직 확인
UPX 다운로드
그림 5-46 UPX 다운로드
실습 5-3 UPX 패킹하기
1
55/60
UPX를 통한 패킹 수행 : 옵션 확인
그림 5-47 UPX 옵션 확인
실습 5-3 UPX 패킹하기
2
56/60
압축을 위한 패커
그림 5-48 UPX를 이용한 REVERSE_2.exe 패킹
실습 5-3 UPX 패킹하기
57/60
UPX를 통한 패킹 결과 확인
UPX를 이용 패킹한 REVERSE_2.exe 파일 올리 디버거로 다시 열어보자.
F4로 실행하면 다음과 같이 코드를 압축, 암호화등을 해서 매우 분석하기 어렵다는 경
고창이 뜬다.
그림 5-49 UPX로 패킹한 REVERSE_2 파일
실습 5-3 UPX 패킹하기
3
58/60
UPX 파일 언패킹
UPX 등으로 패킹된 파일 분석 위해서는 다시 언패킹
언패킹하기 전에 어떤 패커로 패킹되었는지 확인(PEiD, Exeinfo PE와 같은 툴 사용)
Exeinfo PE를 사용하여 패킹된 REVERSE_2.exe 파일을 열어본다.
그림 5-50 Exeinfo PE를 사용하여 UPX로 패킹된 파일 확인
실습 5-3 UPX 패킹하기
4
59/60
UPX로 패킹된 파일은 -d 옵션 이용 다시 풀 수 있다.
그림 5-51 UPX를 이용한 REVERSE_2.exe 언패킹
실습 5-3 UPX 패킹하기
시스템 해킹과 보안