[Case Study] 루트킷의 오늘 1 : 2세대 루트킷
<연재 목차>
1. 루트킷의 어제 : 1세대 루트킷(2012년 2월호)
2. 루트킷의 오늘 1 : 2세대 루트킷(이번 호)
3. 루트킷의 오늘 2 : 3세대 루트킷(2012년 4월호)
4. 루트킷의 내일 : 차세대 루트킷(2012년 5월호)
지난 호에서는 1세대 루트킷에 대해 살펴보았다. 1세대 루트킷은 현재 통용되는 의미의 루트킷–보안 프로그램 또는 운영체제로부터 자신의 행위를 숨기기 위한 프로그램–으로 보기에는 어려운 부분이 있었다. 이번 호에서 살펴볼 2세대 루트킷들의 구현 기술은 현재에도 많은 악성코드와 보안 제품의 기본 기술로 활용되고 있다.
지난 호에서 설명한 것처럼, 1세대 루트킷은 프로그램을 바꿔치기하거나 새로운 프로그램을 백그라운드로 실행하는 정도였다. 이 루트킷은 시스템 파일을 변경하는 것이기 때문에 쉽게 탐지할 수 있었다. 디스크상에 존재하는 파일의 무결성이 훼손되기 때문이다.
2세대 이후의 루트킷은 프로그램이 실행되는 시점, 즉 디스크에 있는 파일이 메모리에 로드되어 실행되는 시점에 변경 을 가하는 방식으로 발전했다. 이렇게 실행되는 프로그램의 메모리 영역을 변경하면 디스크에 존재하는 파일의 무결성이 훼손되지 않기 때문에 기존의 방식으로는 탐지할 수 없게 된다.
실행 파일은 크게 코드 영역과 데이터 영역으로 구분할 수 있다. 코드 영역은 프로그램의 로직을 담고 있는 부분인데, 이 영역에는 프로그램이 실행될 수 있는 CPU의 기계어들이 순서대로 조합되어 있다.

[그림 1] notepad.exe의 코드 영역(헥사 코드)

[그림 2] notepad.exe의 코드 영역(어셈블리)
[그림 1]은 메모장(notepad.exe)의 코드 영역을 헥사 코드로 덤프한 화면이다. 8B FF 55 BC로 시작하는 부분이 프로그램 코드의 시작 지점인데 이를 우리가 읽을 수 있도록 어셈블리로 변경하면 [그림 2]와 같다.

[그림 3] notepad.exe의 데이터(리소스) 영역
[그림 3]은 메모장 프로그램의 데이터 영역이다. 한눈에 보기에도 문자열들이 저장되어 있음을 알 수 있다(화면에 보이는 영역은 manifest라는 파일의 내용. 프로그램 실행 시에 참조해야 하는 외부 모듈들에 대한 정보 등을 보관하고 있다). 이 영역에는 프로그램 실행 시 필요한 데이터들이 저장되어 있는데, 보통 문자열, 이미지, 아이콘 등 초기화된 데이터와 프로그램 내부적으로 사용하기 위한 초기화되지 않은 데이터들이 들어있다.
코드 영역은 [그림 1]과 [그림 2]에서 보았던 것처럼 프로그램의 기능을 구현하는 로직, 즉 CPU의 명령어들이 저장된 영역이므로 프로그램 실행 시 메모리에 로드된 상황에서 변경될 이유가 전혀 없다. 데이터 영역 중 초기화된 데이터 영역 역시 코드 영역과 마찬가지로 프로그램 실행 도중 변경될 필요가 없다.
이 글에서 말하는 2세대 루트킷은 변경될 이유가 없는 프로그램의 코드 영역 또는 초기화된 데이터 영역을 실행 시점에 변조 해서 공격자의 의도대로 프로그램의 로직을 변경하는 루트킷을 의미한다.
PE의 구조
프로그램의 실행 시점에 프로그램의 코드 영역을 변경하기(런타임 코드 패칭 : Runtime Code Patching) 위해서는 기본적으로 실행 파일의 구조에 대한 사전 지식이 필요하다. 윈도우 운영체제에서 사용하는 실행 파일은 PE(Portable Executable) 구조를 사용한다.

[그림 4] PE의 구조
[그림 4]는 윈도우 운영체제의 실행 파일 구조인 PE를 도식화한 것이다. 실행 파일 PE는 디스크상에서 여러 종류의 헤더와 여러 개의 섹션(Section)으로 구분되어 있다. [그림 4]의 PE 구조에서 런타임 코드 패칭과 밀접한 연관을 맺고 있는 것이 섹션 부분이다. 섹션은 성격이 비슷한 데이터, 코드들을 모아서 보관하는 영역이다. 대표적인 섹션으로는 ’.idata’라는 이름의 Import 섹션과 ’.edata’라는 이름의 Export 섹션, 그리고 ’.text’라는 이름을 가진 Text 섹션이 있다.

[그림 5] 섹션 헤더
[그림 5]는 각 섹션에 대한 정보를 보관하는 섹션 헤더를 덤프한 화면이다. 실수로 실행 파일을 노트패드 같은 에디터로 열었을 때 [그림 5]처럼 .text, .data 등의 문자열을 본 적이 있을 것이다.
Import 섹션은 프로그램이 참조하는 외부 모듈(예: kernel32.dll, ntdll.dll 등)에 대한 정보를 보관하고 있고, Export 섹션에는 프로그램이 외부로 노출시키는 함수, 데이터들에 대한 참조 주소가 기록되어 있다. Text 섹션은 프로그램의 로직을 담고 있는 코드가 저장된 공간이다.
IAT 후킹
Import 섹션은 프로그램이 참조하는 외부 모듈에 대한 정보를 담고 있다.

[그림 6] notepad.exe의 Import 정보
[그림 6]은 dependency walker 프로그램으로 메모장 프로그램의 Import 정보를 확인한 그림이다. 상당히 많은 외부 DLL들을 참고하고 있고, 각 DLL로부터 참조하고 있는 함수의 이름 또는 Ordinal 값이 보여지며 참조한 함수의 가상 메모리 주소 값도 확인할 수 있다.

[그림 7] Import 테이블 구조
[그림 7]은 실행 파일의 Import 테이블의 구조를 좀더 자세히 표현한 것이다. 실행 파일의 Import 섹션은 IMAGE_IMPORT_DESCRIPTOR라는 구조체의 배열로 구성되어 있는데 이 구조체에는 Import한 DLL의 이름(예: ntdll.dll, kernel32.dll 등) 정보와 해당 DLL에서 Import한 함수들의 이름 또는 서수 정보를 담고 있는 IMAGE_IMPORT_BY_NAME 구조체 배열의 시작 주소가 들어있다. Import 섹션이 가리키는 최종 목적지인 IMAGE_IMPORT_BY_NAME 구조체 배열을 INT(Import Name Table)라 한다.
프로그램을 실행하면 윈도우의 실행 파일 로더는 실행 파일을 메모리상에 각 섹션별로 매핑한다. 이때 프로그램의 Import 섹션 정보를 읽어서 필요한 DLL을 함께 메모리에 로드한다. DLL이 정상적으로 메모리에 로드되면 Import Name Table에 명시된 함수들의 주소를 계산해서 테이블을 생성하고, IMAGE_IMPORT_BY_NAME 구조체의 FirstThunk가 가리키던 값을 Import Name Table에서 생성한 테이블로 변경하는데 이 과정을 DLL 바인딩이라고 한다.

[그림 8] DLL 바인딩이 실패한 경우 발생하는 오류 메시지
이 DLL 바인딩 과정이 완료되면 [그림 9]처럼 FirstThunk 값은 Import Name Table이 아닌 Import Name Table에 명시된 함수들의 메모리상의 실제 주소 값을 가진 배열을 가리키게 된다. 참조된 함수들의 메모리상의 실제 주소를 가리키는 테이블을 Import Address Table(IAT)라고 한다.

[그림 9] DLL 바인딩이 완료된 상태
IAT 후킹이란 이름에서 알 수 있듯이, DLL 바인딩 후 만들어지는 Import Address Table의 값을 변경하는 방식이다.

[그림 10] IAT 후킹
[그림 10]은 IAT 훅이 설치된 이후의 메모리 상태를 나타낸다. 앞서 설명한 PE의 구조, 특히 Import Address Table에 대한 개념이 정리되었다면 매우 간단한 구조라는 것을 알 수 있을 것이다.

[그림 11] IAT 후킹을 이용한 평문 유출의 예
예를 들어, 프로그램에서 advapi32.dll의 CryptEncrypt( ) 함수를 이용해서 중요한 정보를 암호화한다고 가정해보자. 응용 프로그램은 Import Address Table의 CryptEncrypt 함수를 호출하겠지만 실제로는 Import Address Table에 저장된 CryptEncrypt 함수의 주소가 HACKER.dll의 Hack_CryptEncrypt 함수 주소로 변경된 상태이기 때문에 암호화되지 않은 데이터가 그대로 노출된다.
IAT 후킹의 한계
IAT 후킹은 프로그램의 실행 시에 메모리에 동적으로 만들어진 Import Address Table(IAT)의 4바이트 주소 값만을 변경해서 프로그램의 로직을 변경할 수 있다. 또한 디스크상의 파일을 건드리지도 않기 때문에 매우 효과적인 방법이라 할 수 있다. 하지만 IAT 후킹에는 한계점이 존재하는데 참조 함수에 대한 명시적 호출을 수행하는 경우 IAT 훅이 동작하지 않는다.

[그림 12] 명시적 함수 호출
[그림 12]의 코드처럼 외부 참조 함수를 명시적으로 불러다 쓰는 경우 프로그램의 Import Address를 참조하지 않기 때문에 프로그램의 로직을 변경할 수 없는 상황이 발생할 수 있다.
EAT 후킹
Import Table은 프로그램이 참조하는 외부 모듈의 이름과 참조하는 함수의 이름 또는 서수 정보를 담고 있다. 그렇다면 윈도우의 실행 파일 로더는 어떻게 외부 모듈로부터 실제 메모리상의 함수 주소를 가져올 수 있는 것일까?
PE에는 Import 섹션 외에 ’.edata’라는 이름을 가진 Export 섹션이 존재한다고 앞에서 설명하였다.

[그림 13] Kernel32.dll의 Export 섹션 정보
[그림 13]은 kernel32.dll의 Export 섹션 정보를 보여준다. DLL의 이름, 서수 값(Ordinal), 함수의 이름, 함수의 메모리 주소 값이 출력됨을 확인할 수 있다.
앞에서 설명한 DLL 바인딩 과정에서 윈도우 실행 파일 로더는 DLL을 로드하고, 로드한 DLL의 Export 섹션 정보를 분석한다. 또한 Import 섹션에서 명시한 DLL의 이름과 API의 이름 또는 서수 값과 매칭되는 정보를 Export 섹션에서 찾아서 Import Address Table을 구성한다.

[그림 14] Export Table 구조
[그림 14]는 Export table의 구조를 도식화한 것이다. Export table은 IMAGE_EXPORT_DIRECTORY라는 구조체로 정의되어 있는데 구조체의 가장 중요한 부분은 [그림 14]에 표시된 Export Address Table, Export Name Table, Export Ordinal Table이다.
Export Address Table은 DLL이 외부에 노출한 API들의 메모리상의 주소를 배열한 테이블이고, Export Name Table은 노출한 API들의 이름 문자열 배열이다. Export Ordinal Table은 노출한 API가 이름으로 노출되지 않고, 서수 값으로 노출된 경우 해당 API의 서수 값 배열이다. .
[그림 15] Export Address Table의 메모리상의 배치
Export Address Table은 DLL(혹은 EXE)이 메모리에 적재되면서 DLL이 로드된 메모리의 기준 주소에 따라서 주소 값에 대한 재배치가 이루어진다. DLL이 디스크에 존재할 때는 RVA(Relative Virtual Address)라는 값으로 존재하는데, 이 값은 DLL 또는 실행 파일이 실행을 위해서 메모리에 적재된 시작 주소로부터의 오프셋 값이다.
예를 들어, 디스크상의 DLL 파일에는 ActivateActCtx 함수의 주소 값이 0x000005B7로 기록되어 있고, 이 DLL이 0x7DD70000에 로드되었다면 메모리상에 만들어진 Export Address Table에는 0x7DD70587이라는 값이 설정된다.

[그림 16] EAT 후킹
EAT 후킹은 메모리상에 로드된 DLL 또는 EXE의 Export Address Table의 주소 값을 바꿔치기해서 프로그램의 로직을 변경하는 것이다.
프로그램의 EAT가 변조되어 있기 때문에 프로그램에서 해당 DLL을 명시적으로 참조한다고 하더라도 변조된 로직이 정상 작동하기 때문에 IAT 훅의 단점을 극복할 수 있다.
EAT 후킹의 한계
만일 변경하고자 하는 함수가 IAT에도 없고, EAT에도 없다면 어떻게 처리할 수 있을까? 혹은 함수의 중간쯤에서 변수 하나의 값만 필요에 따라 변경해야 한다면?
IAT 후킹이나 EAT 후킹의 경우 PE 구조에 정의된 함수 포인터 배열의 값만 바꿔치기하는 방식이기 때문에 해당 함수가 테이블에 존재하지 않거나 특정 코드의 중간에서 로직을 변경하는 것은 불가능하다.
Inline 후킹
EAT 후킹은 Export table에 있는 함수 주소의 값을 바꿔치기해서 다른 DLL 쪽으로 제어권을 넘기는 방식을 이용했다. Inline 후킹은 Export table에 있는 함수의 주소를 변경하는 것이 아니라 함수의 주소가 가리키는 실제 프로그램 코드를 변경한 것이다.

[그림 17] Export Address Table
[그림 17]은 Export table의 구조를 좀더 자세히 표현한 것이다. Export Address Table이 가리키는 위치는 프로그램의 구현 코드가 저장된 메모리 공간의 주소이다. 여기에는 CPU 명령어들이 저장되어 있는데 이 영역에 직접 CPU 명령어를 덮어써 원하는 기능을 구현하는 것이 Inline 후킹의 기본 원리이다.

[그림 18] Inline 후킹의 흐름
Inline 후킹을 하려면 먼저 루트킷 코드로 이동(Jump)하기 위한 CPU 명령어를 덮어쓸 위치를 찾아야 한다. 보통 함수의 시작 지점에 명령어를 덮어쓰는 경우가 많다. 함수의 중간에서 코드를 덮어쓰면 코드 실행 도중 상태 정보를 가지고 있는 변수나 스택의 상태 등을 원상 복원하는 데 문제가 발생할 수 있기 때문이다. 하지만 최근에는 보안 제품들의 탐지를 회피하기 위해서 함수의 중간 부분에서 후킹을 하는 경우도 많다.
점프 코드를 삽입할 위치를 결정했으면 덮어쓸 명령어 위치에 존재하는 원본 코드를 백업해 두어야 한다. 입력 값이나 원본 함수가 리턴한 출력 값을 조작하기 위해서 원본 함수를 정상적으로 호출해야 하기 때문이다. 만일 원본 함수를 호출하지 않아도 되는 상황이라면 코드를 백업해두지 않아도 된다.

[그림 19] Inline 후킹의 호출 흐름
Inline 훅이 설치된 후 외부에서 함수를 호출하면 삽입된 jmp 코드로 인해 detour function으로 이동하게 된다. Detour function은 jmp 코드가 덮어쓴 원본 코드의 백업과 덮어쓴 이후의 위치로 jmp하는 코드를 실행한다.
결국 경로가 변경되었을 뿐 원본 함수의 실행에는 아무런 문제가 없다. 원본 코드, 즉 Target function이 실행되면 다시 detour function으로 리턴하고 detour function에서는 호출자에게 리턴하면 모든 호출 흐름이 종료된다.
Detour function은 이름처럼 함수 호출의 흐름을 변경하는 함수이며 호출자가 넘겨준 파라미터에 대한 조작이 가능하다. 또한 원본 함수가 실행되고 리턴된 값에 대한 조작도 가능하기 때문에 필요한 모든 기능은 detour function에서 구현하는 것이 일반적이다.
Inline 훅은 거의 모든 함수에, 함수의 전체 길이가 jmp 코드를 삽입할 수 있을 정도의 크기만 된다면 적용이 가능하기 때문에 많은 악성코드나 보안 제품들이 주로 사용하는 후킹 기법이다.

[그림 20] 프로세스 가상 메모리
근대의 범용 운영체제는 한정된 물리적 메모리 자원을 효율적으로 사용하고, 응용 프로그램들이 독립적으로 자신만의 메모리 공간을 사용할 수 있게 하는 가상 메모리 시스템을 지원한다. 따라서 [그림 20]에서 보듯이 [프로세스 A]와 [프로세스 B]는 서로 독립적인 메모리 공간을 사용하고 있으며 상대방의 메모리 영역에 직접적으로 접근할 수 없다.

[그림 21] DLL 인젝션
앞서 설명한 IAT 훅, EAT 훅, Inline 훅 등은 모두 실행 코드의 로직을 변경하는 방법이며 타깃 프로세스가 사용중인 메모리 영역을 수정해야 하는 기법들이다. 그런데 프로세스들은 서로 독립적인 메모리 공간을 사용하고 있기 때문에 정상적인 방법으로는 후킹이 불가능하다. 따라서 타깃 프로세스 공간에 실행 가능한 코드를 주입(injection)하는 방법이 필요하다. 실행 가능한 코드 블록을 주입하는 것보다는 다양한 기능을 쉽게 구현하기 위해서 DLL을 만들어 주입하는 방법을 이용한다. 이를 DLL 인젝션(Injection)이라고 한다.
일단 DLL이 타깃 프로세스 공간에 로드되면 타깃 프로세스와 동일한 권한을 가지고 코드가 실행되므로 타깃 프로세스의 메모리를 조작하는 데 아무런 문제가 없다. DLL 인젝션 기법은 여러 가지가 있지만 가장 대표적인 두 가지 방법에 대해서만 설명하겠다.
SetWindowsHookEx( ) API를 이용한 DLL 인젝션

[그림 22] 윈도우 운영체제의 메시지 처리 구조
윈도우 운영체제는 이벤트라는 메시지를 통해서 응용 프로그램의 GUI와 운영체제 간의 통신을 처리한다. 예를 들어, 키보드가 눌리면 운영체제는 WM_KEYDOWN이라는 메시지를 만들어 운영체제의 메시지 큐에 등록한다. 운영체제가 메시지 큐로부터 메시지를 꺼내어 현재 활성화된 응용 프로그램의 메시지 큐에 등록하면 응용 프로그램은 해당 윈도우 메시지를 처리하는 방식이다.
이 메시지 큐는 윈도우 메시지 훅 체인이라는 것을 가지고 있는데, 운영체제의 메시지 큐에서 애플리케이션 메시지 큐로 메시지가 전달될 때 미리 지정한 유형의 윈도우 메시지라면 애플리케이션의 메시지 큐에 등록하기 전에 등록된 훅 프로시저에게 먼저 처리할 수 있도록 우선권을 제공하는 메커니즘이다.

[그림 23] Microsoft Spy++ 화면
[그림 23]은 마이크로소프트가 만든 Spy++이라는 프로그램으로 윈도우 운영체제에서 발생하는 모든 윈도우 메시지를 볼 수 있다. 윈도우 메시지 훅 체인 메커니즘은 운영체제의 변경 없이도 응용 프로그램 개발자들이 GUI나 기타 애플리케이션의 기능을 자유롭게 확장할 수 있도록 만들어둔 장치로 보인다.
SetWindowsHookEx( ) 함수를 이용하면 간단히 윈도우 메시지 훅 프로시저를 등록할 수 있다.

[그림 24] SetWindowsHookEx( ) 함수를 이용한 메시지 훅 설치
[그림 24]의 코드는 'inject.dll'이라는 DLL의 'KeyboardHookProcedure'라는 함수를 WH_KEYBOARD 윈도우 메시지의 핸들러로 등록하는 코드인데, SetWindowsHookEx( ) 함수의 마지막 파라미터가 0인 경우 운영체제의 모든 스레드에 메시지 훅 체인이 적용된다는 의미이다. WH_KEYBOARD 메시지에 대한 훅을 설치했기 때문에 키보드 이벤트가 발생할 때마다 활성화된 윈도우를 가진 애플리케이션의 스레드에서 inject.dll의 HookProcedure 함수가 호출된다. Inject.dll의 HookProcedure 함수에서는 IAT 훅, EAT 훅, Inline 훅 등을 이용해서 다른 프로세스의 로직을 자유롭게 변경할 수 있다.
SetWindowsHookEx( ) API의 한계
SetWindowsHookEx( ) 함수를 이용한 DLL 인젝션의 경우 기능 자체가 운영체제에서 제공하는 정식 기능이기 때문에 가장 안정적인 방법이며, 윈도우 9x부터 최신의 운영체제 버전까지 사용할 수 있다.
그러나 SetWindowsHookEx( ) 함수를 이용한 DLL 인젝션에는 큰 약점이 있다. 기본적으로 운영체제로부터 윈도우 메시지를 받기 위해서는 윈도우 핸들이 필요한데 이 윈도우 핸들은 GUI를 가지는 애플리케이션에만 있다. 따라서 콘솔 모드 응용 프로그램 또는 서비스 프로그램들은 DLL 인젝션이 불가능하다.
CreateRemoteThread( ) API를 이용한 DLL 인젝션 CreateRemoteThread( ) API는 다른 프로세스에 새로운 스레드를 생성하는 API로 여러 파라미터를 전달받지만 DLL 인젝션에 중요한 파라미터는 아래와 같다. CreateRemoteThread( ) API를 통한 DLL 인젝션의 키 포인트는 4번째 파라미터인 LPTHREAD_START_ROUTINE lpStartAddress 파라미터이다. LPTHREAD_START_ROUTINE은 아래와 같이 정의되어 있다. DWORD 값을 리턴하고, stdcall 호출 방식을 사용하며, 포인터 유형의 파라미터를 받는 함수이다. 그런데 윈도우 API 중에서 LoadLibrary( ) 함수가 있는데 이 함수는 DLL을 호출한 프로세스 공간에 로드하는 기능을 제공한다. 이 함수도 마찬가지로 HMODULE(DWORD와 차지하는 메모리의 크기가 같음)을 리턴하며 stdcall 호출 방식을 사용하고, 포인터 변수를 파라미터로 받아들인다. 결국 LoadLibrary( ) 함수는 PTHREAD_START_ROUTINE과 일치하기 때문에 CreateRemoteThread( ) API의 파라미터로 전달이 가능하다. CreateRemoteThread( ) API를 이용한 DLL 인젝션은 다음과 같은 순서로 진행된다. STEP #1 SETP #2 STEP #3 STEP #4 이 모든 일련의 작업이 완료되면 타깃 프로세스 공간에서 IAT 훅, EAT 훅, Inline 훅 등을 이용해서 다른 프로세스의 로직을 자유롭게 변경할 수 있다. CreateRemoteThread( ) API의 한계 런타임 코드 패칭 기법의 활용 1) 리눅스 커널 루트킷 다음 예제는 루트킷이 LKM을 이용해서 운영체제에서 제공하는 시스템 콜 테이블을 변경하여 특정 조건이 일치하는 경우 관리자 권한(root)을 할당하는 예이다. LKM이 로드되면서 SYS_stat 시스템 콜과 SYS_getuid 시스템 콜 핸들러를 각각 bd_stat( ), bd_getuid( ) 함수로 바꿔치기 한다. Import Address Table, Export Address Table을 후킹하는 방식과 동일하다. 일반 사용자가 로그인해서 'touch foobar' 명령을 내리면 루트킷은 명령을 내린 사용자의 uid를 보관해둔다. 해당 사용자가 로그아웃해서 다시 로그인하는 경우 bd_get( ) 함수에서 해당 사용자를 루트(root)로 탈바꿈시킨다. 2) SSDT 후킹 윈도우 운영체제는 KeServiceDescriptorTable이라는 특별한 테이블을 관리한다. 이 테이블은 윈도우 커널의 일부로 운영체제가 응용 프로그램에게 제공하는 서비스(예: 파일 관리, 프로세스/스레드 관리, 메모리 관리 등)를 위한 함수의 배열이다. 이 테이블을 SSDT(System Service Descriptor Table)라고 하며 Import Address Table이나 Export Address Table처럼 함수 포인터의 연속된 배열이다. 이 테이블에 있는 함수의 주소를 다른 주소로 바꿔치기한다면 얼마든지 원하는 기능을 구현할 수 있다. 특히 SSDT는 응용 프로그램과는 달리 운영체제 커널의 일부이기 때문에 시스템에서 실행 중인 모든 프로세스에 적용된다. 따라서 악성코드에게는 최적의 공격 포인트가 된다. 물론 보안 프로그램에게도 SSDT가 악성코드 행위를 차단하기 위한 최적의 포인트이기도 하다. 지금까지 2세대 루트킷의 구현 기술에 대해 살펴보았다. 2세대 루트킷 구현 기술은 한마디로 정리하자면 런타임 코드 패칭 기술로 운영체제 또는 소프트웨어의 설계•구현 수준에서 생각해 볼 때 변경되도록 의도되지 않았던 영역에 대한 변경인 셈이다. 다음 호에서는 3세대 루트킷의 구현 기술과 그에 대한 탐지 기술에 대해서 살펴보겠다.@
CreateRemoteThread( ) 함수를 이용해서 타깃 프로세스에 DLL을 인젝션하는 기법은 1994년 [Microsoft Systems Journal]에서 Jeffrey Ritcher가 「Load Your 32-bit DLL into Another Process’s」라는 기사에서 공개했다. 또한 유명한 프로그래밍 서적인 [Programming Application Microsoft Windows 4th Edition]의 「Injecting a DLL Using Remote Threads」에도 자세히 소개되어 있다.


[표] 네트워크 공격 유형에 따른 분류

OpenProcess( ) API를 통해서 타깃 프로세스의 핸들을 오픈한다.
GetProcessAddress( ) API를 통해서 'LoadLibrary()' API의 주소를 구한다.
DLL은 메모리 영역을 수정하지 않는 한 모든 프로세스가 공유하므로 injector 프로세스에서 구한 API의 주소와 타깃 프로세스의 주소가 동일하다.
CreateRemoteThread( )를 호출할 때 주입할 DLL의 경로를 파라미터로 전달한다.
타깃 프로세스에서 생성된 새로운 스레드는 CreateRemoteThread( ) API의 파라미터로 전달받은 LoadLibrary( ) 함수를 실행할 것이다. 또한 CreateRemoteThread( )의 다섯번째 파라미터로 주입할 DLL의 경로를 전달했으므로 결국 최종적으로는 다음의 코드가 실행된다.![]()
CreateRemoteThread( ) API는 SetWindowsHookEx( ) API와는 달리 윈도우 핸들을 가지지 않는 콘솔 프로그램 또는 서비스 프로그램에도 동일하게 DLL을 주입할 수 있기 때문에 가장 많이 사용된다. 하지만 윈도우 9x 계열 운영체제에서는 해당 API를 지원하지 않기 때문에 이 기법을 사용할 수 없다.
지금까지 IAT 후킹, EAT 후킹 그리고 응용 프로그램 레벨에서의 후킹을 위한 DLL 인젝션에 대해서 설명했다. 윈도우 커널 레벨에서의 후킹도 OS 커널이라는 특수한 환경으로 인해 고려해야 할 사항들이 많긴 하지만 코드 패칭 기법과 기본 개념은 동일하다.
리눅스는 LKM(Loadable Kernel Module)을 이용해서 동적으로 외부 모듈을 커널에 적재할 수 있는 기능이 있다. 
[그림 25] SYS_stat 시스템 콜 후킹
[그림 26] 루트킷 코드

[그림 27] KeServiceDescriptorTable
[그림 28] SSDT 후킹
- AhnLabA-FIRST 노용환 선임