루트킷_3

유저모드에서 시작된 루트킷과 보안제품의 싸움은 커널모드에서도 계속된다. 3부에서는 커널모드 API 후킹의 대표적인 기법인 SDT 후킹의 원리에 대해서 살펴보고, 이를 둘러싼 해커와 보안 개발자들 사이의 전쟁에 관한 이야기에 대해서 살펴본다. 다양한 기술을 통해서 그들의 아이디어를 훔쳐보자.

커널모드 루트킷 기술

SDT 후킹의 창과 방패

신영진 pop@jiniya.net, http://www.jiniya.net|웰비아닷컴에서 보안 프로그래머로 일하고 있다. 시스템 프로그래밍에 관심이 많으며 다수의 PC 보안 프로그램 개발에 참여했다. 현재 데브피아 Visual C++ 섹션 시삽과 Microsoft Visual C++ MVP로 활동하고 있다. C와 C++, 프로그래밍에 관한 이야기를 좋아한다.

SDT 후킹

SDT(Service Descriptor Table) 후킹은 시스템 서비스 테이블의 값을 조작하는 API 후킹의 한 방법이다. 유저모드 API 후킹의 경우 프로세스간 컨텍스트가 다르기 때문에 시스템 전역 후킹을 하기 위해서는 일일이 모든 프로세스에 관련 코드를 주입(injection) 시켜야 한다는 불편한 점이 있었다. 이러한 문제를 해결한 한 가지 방법이 SDT 후킹이다. SDT 후킹은 커널모드에서 한 번만 후킹을 하면 모든 프로세스에 적용되기 때문에 별도로 컨텍스트 관리를 할 필요가 없기 때문이다.

SDT 후킹의 방법을 이해하기 위해서는 우선 윈도우의 API 호출이 어떻게 커널로 전달되는지를 알아야 한다. 간단하게 windbg를 통해서 TerminateProcess 호출이 커널로 전달되는 과정을 살펴보도록 하자. 응용 프로그램에서 호출한 Terminate Process는 ntdll.dll의 NtTerminateProcess로 이어진다. <리스트 1>에 windbg를 통해서 디스어셈블한 NtTerminateProcess 함수가 나와있다. eax에 0x101를 넣고, 0x7ffe03000에 저장된 번지를 호출하는 것을 알 수 있다.

<리스트 2>에 나와있는 것처럼 0x7ffe0300는 edx에 현재 스택 포인터를 저장하고 sysenter 명령어를 호출하는 코드임을 알 수 있다. sysenter 명령어는 유저모드에서 커널모드로 진입하기 위한 명령어로 MSRs에서 실행할 커널 함수 주소를 가져와서 그 번지로 이동시키는 역할을 한다. rdmsr 명령어를 통해서 sysenter 명령어가 수행됐을 때 실행되는 함수를 찾아보면 KiFastCall Entry라는 것을 알 수 있다. KiFastCallEntry는 eax에 저장된 서비스 번호에 해당하는 함수를 호출하는 역할을 한다.

여기까지 과정을 요약하면 TerminateProcess 호출은 ntdll. dll의NtTerminateProcess로 이어지고, NtTerminate Process는 eax에 TerminateProcess API의 서비스 함수 번호인 0x101을 넣고 커널모드에서 KiFastCallEntry를 호출한다. KiFast CallEntry는 SDT를 참조해서 eax에 저장된 서비스 번호에 해당하는 함수를 호출한다. 이제 실제로 SDT에 대해서 살펴보도록 하자. SDT는 총 네 개의 서비스 테이블로 구성된다. 각 테이블은 <리스트 3>에 나와있는 SDE 구조체로 이루어져 있다. SDE 테이블의 각 필드별 설명은 <표 1>에 나와 있다. SDT의 첫 번째 테이블은 윈도우의 네이티브(native) API 함수를 저장하고 있는 ntoskrnl 테이블이다. 두 번째 테이블은 GUI 함수들을 저장하고 있는 win32k 테이블이다. 세 번째, 네 번째 테이블은 추후 사용하기 위해서 예약된 테이블이다.

윈도우 커널에는 기본적으로 두 종류의 SDT가 포함되어 있다. KeServiceDescriptorTable은 일반적인 스레드에 사용되는 테이블로 ntoskrnl 테이블만 사용하고, 나머지 테이블은 모두 사용하지 않는다. KeServiceDescriptorTableShadow는 GUI 스레드에 사용되는 테이블로 ntoskrnl과 win32k 테이블을 사용한다. <그림 1>은 이러한 구조를 도식화한 것이다.

windbg를 통해서 실제 커널의 SDT를 살펴보도록 하자. <리스트 4>에 커널의 SDT 구조체를 덤프한 내용이 나와있다. 살펴보면 ntoskrnl 테이블의 base 주소는 0x84e46a8, counter는 0, limit은 0x11c, arg 필드 값은 0x80514eb8인 것을 알 수 있다. 함수 테이블을 덤프한 것을 보면 첫 번째 함수의 시작 주소는 0x80581302인 것을 알 수 있고, 인자 크기는 0x18 바이트란 것을 알 수 있다. 한 가지 주의해서 볼 사실은 GUI 함수 테이블과 GUI 인자 테이블은 커널 내부가 아닌 win32k.sys 내부에 존재한다는 것이다. <리스트 4>에서도 이 두 테이블의 주소만 확연히 틀린 것을 확인할 수 있다.

이제 SDT에서 TerminateProcess API의 서비스 함수를 찾아보자. <리스트 1>을 보면 eax에 0x101을 집어넣고 있으므로 TerminateProcess API의 서비스 함수 번호는 0x101이란 것을 알 수 있다. 함수 테이블은 4바이트 기준이고, 인자 테이블은 한 바이트 기준이란 점을 생각해서 해당 내용을 덤프 해보면 <리스트 5>와 같다. TerminateProcess의 서비스 함수 주소는 0x80586740이고, 인자는 0x08 바이트 크기인 것을 알 수 있다. 해당 주소를 디스어셈블하면 커널 내부의 NtTerminateProcess 함수가 출력된다.

이제 끝으로 SDT 후킹을 하는 방법을 생각해보자. SDT 후킹이라 함수 테이블의 번지를 바꾸는 작업을 하는 것을 말한다. <리스트 5>에서 0x101번에 해당하는 서비스 함수 주소는 0x80586740이다. 이 값을 우리가 만든 임의의 함수 주소로 변경한다면, 응용 프로그램에서 TerminateProcess를 호출할 때 원본 서비스 함수가 아닌 우리가 새롭게 작성한 함수가 호출된다.

SDT 복구

루트킷은 API 호출 결과를 조작하기 위해서, 보안 프로그램은 시스템 상황을 모니터링하기 위해서 SDT 후킹을 광범위하게 사용한다. 이런 경쟁 조건에서는 먼저 실행되어서 SDT를 후킹한 쪽에 우선권이 주어진다. 예를 들어 루트킷이 NtQuerySystem Information 함수를 후킹한 상태를 생각해보자. 이후 실행되는 보안 프로그램에서 호출하는 NtQuerySystemInformation의 결과값은 루트킷에 의해서 조작된 값이기 때문에 신뢰할 수 없게 된다. 따라서 보안 프로그램은 자신이 동작하는 환경이 안전한지를 점검하는 것이 최우선 과제라 할 수 있다.

이러한 문제를 해결하기 위해서 나온 것이 SDT 복구 기술이다(http://www.security.org.sg/code/sdtrestore.html). 이 기술의 원리는 간단하다. 현재 시스템의 SDT와 파일로 존재하는 커널의 SDT를 비교해서 같은지 다른지를 비교하는 것이다. SDT 후킹을 하더라도 변경되는 것은 현재 메모리의 내용일 뿐 파일의 내용은 원본 그대로 이기 때문이다. 이 방법의 전체적인 실행 순서는 다음과 같다.

♦ 현재 로드된 커널의 이미지 이름과 로드 주소를 알아낸다.
♦ 로드된 커널의 KeServiceDescriptorTable의 base 주소를 알아낸다.
♦ 동일한 이름의 커널을 LoadLibraryEx를 통해서 메모리에 로드시킨다.
♦ 로드된 이미지에서 base와 동일한 오프셋에 있는 테이블 내용을 비교한다.

<리스트 6>은 이를 간단한 의사 코드로 만들어본 것이다. 핵심적인 부분은 함수 테이블 주소의 오프셋을 사용해서 Load LibraryEx로 로드한 커널에서 해당 부분을 찾아내는 것이다. 현재 로드된 커널의 이미지 이름과 베이스 주소는 NtQuery SystemInformation 함수를 사용해서 구할 수 있다.

SDT 재배치

참 아이러니 한 사실은 보안 제품에서 루트킷을 잡기 위해서 개발된 SDT 복구 기술이 역으로 루트킷에서 사용한다는 것이다. 보안 제품도 시스템 모니터링을 하기 위해서 SDT 후킹을 사용하는데 루트킷이 보안 제품을 무력화하기 위해서 SDT 복구를 사용하는 것이다. 이러한 루트킷에 대항하기 위해서 만들어진 기술이 SDT를 재배치하는 방법이다. 이 방법은 앞서 살펴본 SDT 복구가 로드된 커널의 KeServiceDescriptorTable[0].base에서 찾는다는 사실을 이용한다. base 필드의 값이 커널 이미지 외부에 있다면 로드한 커널 파일에서는 그 부분을 찾을 수 없다. 메모리를 할당한 다음 해당 영역으로 함수 테이블을 복사하는 것이 핵심이다. 그리고 커널의 base 필드 값을 할당된 메모리로 연결해주면 된다. 이렇게 변경된 경우의 테이블 구조가 <그림 2>에 나와 있다.

SDT를 찾는 또 다른 방법

SDT 재배치 방법이 나오고 얼마지 않아 루트킷 진영에서는 오리지널 커널 이미지 파일로부터 SDT의 주소를 알아내는 새로운 방법이 나왔다(http://www.rootkit.com/newsread.php? newsid=176). 이 방법은 커널의 초기화 코드에서 base 주소를 찾아내는 방법을 사용한다. KeServiceDescriptorTable은 커널의 KiInitSystem이란 함수에서 <리스트 7>과 갈은 형태로 초기화된다. base 주소에는 KiServiceTable이란 고정적인 값이 할당되기 때문에 이 부분을 통해서 base 주소의 기본 값을 찾아낼 수 있다.

이 방법의 전체적인 실행 순서는 다음과 같다. 글만 보고는 의미를 이해하기가 쉽지 않기 때문에 <그림 3>에 나와 있는 명령어 구조와 재배치 오프셋의 관계를 보면서 의미를 파악하도록 하자. <리스트 8>에는 재배치 정보에서 SDT 주소를 찾아내는 과정에 대한 의사코드가 나와있다.

♦ 현재 로드된 커널의 이미지 이름을 알아낸다.
♦ 동일한 이름의 커널을 LoadLibraryEx를 통해서 메모리에 로드한다.
♦ GetProcAddress를 통해서 KeServiceDescriptorTable 주소를 구한다.
♦ 모든 재배치 정보를 조사해서 재배치하는 곳의 참조 주소가 KeSer viceDescriptorTable인 것을 찾는다.
♦ 찾아낸 부분의 명령어 코드 형태가 mov [mem32], imm32 형태 인지를 조사한다.
♦ 5단계까지 일치했다면 재배치 주소+4에 있는 값이 KeService DescriptorTable[0].base의 초기 값이 된다.

전용 SDT

SDT를 둘러싼 이러한 싸움이 한창일 때 SDT 후킹에 대한 원론적인 의문을 품은 사람들이 있었다. 그 의문은 다름 아닌 SDT 후킹이 과연 시스템 전역 후킹인가에 대한 것이었다. 그들은 좀 더 세밀하게 커널을 분석했고, SDT 후킹이 시스템 전역이 아니라는 결론에 도달하기에 이르렀다(http://zap.pe.kr/index. php?page=pages/researches/winternals_kr.php). <리스트 9>에 나와있는 것처럼 커널 스레드 구조체에는 ServiceTable이란 필드가 있다. 이 필드의 역할은 해당 스레드의 서비스 테이블을 가리키는 역할을 한다. 앞서 살펴본 것과 같이 일반적인 경우에 모든 스레드는 <그림 4>에 나타난 것처럼 KeService Descri ptorTable이나 KeServiceDescriptorTableShadow 중에 하나의 서비스 테이블을 가리키기 때문에 SDT 후킹이 시스템 전역인 것처럼 보였던 것이다.

전용 SDT란 <그림 5>에 나타난 것처럼 특정 스레드의 Service Table을 수정해서 전혀 새로운 서비스 테이블과 연결시키는 방법이다. 앞서 살펴보았던 방법을 통해서 커널 파일에서 원본 SDT를 추출한 다음 그 정보를 기반으로 새로운 서비스 테이블을 만들면 SDT 후킹이 이루어진 상태라 하더라도 해당 스레드는 SDT 후킹으로부터 자유로울 수 있다.

인라인 후킹 vs 트램펄린

SDT를 새롭게 만들어서 스레드에 연결시키는 방법이 나오면서 더 이상 SDT 후킹은 의미가 없어졌다. 보안 개발자들은 좀 더 하위레벨의 코드를 직접 후킹하는 방법을 선택했다. 인라인(inline) 후킹으로 불리는 이 방법은 커널 함수 도입부를 후킹된 함수로 점프하는 코드로 패칭하는 것을 말한다. <리스트 10>과 같은 NtTerminateProcess 함수 본문을 <리스트 11>과 같이 바꾸는 것이다.

후킹에 관심이 있는 개발자라면 대부분 알고 있듯이 이러한 인라인 후킹은 트램펄린 함수를 사용해서 손쉽게 무력화 시킬 수 있다. 트램펄린 함수란 원본 함수와 도입부를 동일하게 구현하고 점프 코드 다음으로 바로 이동시키는 것을 말한다. <리스트 12>에는 이러한 트램펄린 함수의 한 예가 나와있다.

보안 개발자들의 다음 선택은 단순하게 트램펄린 함수에 무력화 되지 않도록 여러 함수를 동시에 인라인 후킹하는 방법이었다. 다중 인라인 후킹으로 불리는 이 방법은 후킹을 하고자 하는 함수에서 사용하는 다른 함수도 같이 인라인 후킹을 하는 것이다. NtTerminateProcess 함수는 내부적으로 PspTerminate ThreadByPointer, ObfDereferenceObject, ObClearProcess HandleTable와 같은 함수를 사용한다. 이들 함수를 동시에 후킹하면 앞서 살펴본 TL_NtTerminateProcess를 호출하더라도 TerminateProcess 함수 본문에서 호출하는 다름 함수에서 걸리기 때문에 단순 트램펄린 함수로는 우회하기가 쉽지 않다. <그림 6>은 해커가 이러한 상황을 공격하기 위해서 각각에 대한 트램펄린 함수를 만들어둔 화면을 보여주고 있다. <그림 6>에 나와 있듯이 이렇게 각각의 트램펄린을 만든다고 하더라도 CALL로 연결된 2번 선 때문에 인라인 후킹을 무력화 할 수 없다.

물론 그렇다고 이러한 다중 인라인 후킹을 무력화할 수 있는 방법이 전혀 없는 것은 아니다. 트램펄린 대신 함수를 통째로 복사해서 사용한다면 다중 인라인 후킹도 무력화할 수 있다. <그림 7>은 이런 방식으로 함수를 통째로 복사해서 다중 인라인 후킹을 무력화하는 방법이 나와있다. NtTerminateProcess를 원본 운영체제 코드를 복사해서 그대로 만들되 CALL 하는 부분만 Psp TerminateThreadByPointer가 아닌 TrampolinePsp Termina teThreadByPointer를 호출하도록 만드는 것이다. 인라인 후킹된 함수가 많다면 굉장히 번거로운 작업이 되겠지만 결국은 시간과 노력만 투자한다면 어떤 종류의 인라인 후킹이든 우회할 수는 있다.

물론 복잡하게 트램펄린을 만들지 않고 JMP 부분만 원본 코드로 덮어 쓴다면 쉽게 후킹을 우회할 수 있다. 하지만 이렇게 값을 덮어 쓰는 방법은 SDT 후킹과 마찬가지로 경쟁 조건을 유발 시킨다. 서로 JMP와 원본 코드를 끊임없이 덮어쓴다면 그 결과는 예측할 수 없다. 또한 일부 보안 프로그램에서는 자신의 후킹 코드가 손상되는 경우에는 시스템을 중단시키기도 하기 때문에 값을 덮어쓰는 방법은 신중하게 결정해야 한다.

후킹을 넘어서

점프 코드 지뢰밭을 일일이 제거하면서 나가는 것도 재미는 있지만 속도는 더디게 마련이다. 지뢰를 굳이 제거하지 않고 그 위를 날아간다면 그 또한 좋은 방법일 것이다. 앞서 우리가 원본 SDT를 커널 파일에서 찾은 것처럼 커널 파일에 있는 원본 코드를 사용한다면 지뢰밭을 뚫지 않고 날아가는 것도 가능하다.

<그림 8>에는 이러한 아이디어가 나와있다. 커널 파일 자체도 PE 포맷이라는 점과 재배치 섹션이 존재한다는 점을 생각한다면 커널을 로딩하는 것이 불가능한 일은 아니라는 것을 알 수 있다. 커널 모드 코드에서 메모리를 할당하고 그곳에 커널을 올린다음 재배치를 수행하면 <그림 8>의 왼쪽과 같은 구조가 된다. 커널 코드가 두 벌이라는 것과 함께 각각의 커널은 자신의 자료 구조를 가질 것이다. 하지만 이렇게 되서는 곤란하다. 왜냐하면 자료 구조는 전부 기존 커널의 것을 이용해야 하기 때문이다. 약간의 트릭을 써서 커널을 할당된 메모리에 올린 다음 재배치를 기존 커널의 주소에 로딩된 것처럼 수행한다면 <그림 8> 오른쪽 그림과 같은 구조가 된다. 커널의 코드는 두 벌이 되지만 참조하는 자료 구조는 하나가 된다. 이 상황에서 새로 로딩한 커널의 NtTer minateProcess를 호출한다면 트램펄린 함수를 하나도 만들지 않고 모든 인라인 후킹을 우회할 수 있다.

'Computer Science' 카테고리의 다른 글

PHP 동영상 강좌  (0) 2008.06.29
루트킷_4  (0) 2008.06.25
루트킷_2  (0) 2008.06.23
luabind/C++ <;-> 루아 클래스간 상호 참조 및 호출 예제  (0) 2008.06.22
Using Lua with C++ &#8212; A short tutorial  (0) 2008.06.22

루트킷_2

“유저 레벨에선 결코 루트킷을 구현할 수 없다. 루트킷은 반드시 ring0 에서 제작해야만 루트킷이다” 라는 명제만이 정답은 아니다. 유저 레벨에서도 구현할 수 있는 루트킷 기법은 얼마든지 있으며 실제 바이러스들도 굳이 드라이버를 설치하지 않고도 자신을 은닉하고 모습을 많이 보여준다. 2부에서는 유저 레벨 루트킷의 종류와 검출 방법에 대해 알아본다.

Ring3에서 보여줄 수 있는 은닉의 예술

유저 레벨 루트킷

강병탁 window31@empal.com, www.window31.com|바이너리 취약점 분석 업무를 하고 있다. 안티 크래킹/안티 디버깅 엔진 개발을 다년간 해왔으며 시스템 프로그래밍과 리버스 엔지니어링에 관심이 많다. 악성 코드나 해킹툴이 내부에 담고 있는 천재적인 알고리즘에 감탄하며 오늘도 IDA를 돌린다.

루트킷은 반드시 커널 레벨에서만 가동돼야 한다는 고정관념이 있다. 유저 레벨에서 아무리 숨겨 보았자 커널에서 쉽게 발견할 수 있다는 점에서 기능상의 한계를 많이 지적하는 편이다. 하지만 루트킷을 단순히 ‘최고의 은닉’ 기술에만 국한시켜서는 안 된다. 은닉이라는 기본 테마가 ‘무엇으로부터의 은닉’ 이라는 구체적 명제에 목매이지 않는다면, 유저 레벨 루트킷도 훌륭한 투명인간이 될 수 있다. 예를 들어 유저 레벨의 루트킷은 IceSword 같은 대부분의 루트킷 디텍터에 쉽게 발견되는 편이다. 하지만 그 같은 커널 드라이버를 이용하는 루트킷 디텍터를 사용할 수 없는 경우, 그리고 루트킷을 사용할 목적지가 드라이버와 무관한 장소일 경우라면 유저 레벨 루트킷도 충분한 가치를 발산할 수 있다. 더욱이 루트킷 하면 드라이버라고 생각하는데, 유저 레벨 루트킷은 드라이버를 설치하지 않고 숨겼다는 점에서 어떤 면에서는 더욱 훌륭하다고 볼 수 있다.

그리고 유저 레벨 루트킷은 “절대로 발견할 수 없도록 숨긴다”라는 목적보다는 그 기술이나 구현론 자체에 더욱 중점을 둬야 한다고 본다. 유저 레벨 루트킷을 구현하려면 API Hooking이나 각종 시스템 지식들이 다양하게 요구된다. “절대 발견할 수 없도록 숨긴다”에 너무 치중하지만 않는다면, 유저 레벨 루트킷을 연구하면서 얻을 수 있는 시스템 개발적 지식은 아주 많으리라 생각된다. 또한 유저 레벨의 루트킷은 굳이 루트킷 구현이 아닌 다른 여러 곳에서도 이용될 수 있는 알고리즘도 있기 때문에 얻을 수 있는 것은 더욱 많다고 생각된다. 따라서 루트킷을 커널에서만 구현해야 한다는 관념에 너무 얽매일 필요는 없으며, 유저 레벨에서 구현할 수 있는 루트킷은 어떤 것이 있고, 구조는 어떠한지 살펴볼 필요가 있다.

API Hooking

유저 레벨 루트킷으로써의 가장 기본임과 동시에 모든 부분이 되기도 하는 기법이 바로 API Hooking이다. Ring3에서는 시스템에 전역적인 영향을 끼치는 커널 구조체를 컨트롤 할 수 없으므로 은닉을 위해서는 기본적으로 후킹 기법을 필수적으로 사용한다. 그리고 타겟이 되는 API는 사용자가 공격자의 코드나 바이너리를 발견하지 못하도록 모듈을 찾는 관련 함수가 직접적인 대상이 된다. 예를 들면 파일을 찾을 때 FindNext File() 이나 프로세스를 찾을 때의 Process32Next() 같은 API가 대표적인 경우이다. 따라서 유저 레벨 루트킷은 이와 같은 API를 후킹하는 작업을 필수적으로 진행하므로 먼저 API Hooking에 대해서 살펴볼 필요가 있다. API Hooking에는 기본적으로 두 가지 기법으로 나누어진다. 첫째는 IAT Hook, 둘째는 Inline Hook(Overwrite Hook)이다. 여기서 사실 IAT Hook은 루트킷을 구현하는데 있어서 실전에서는 그다지 많이 쓰이지 않는 편이다. 왜냐하면 요즘 필드에 릴리즈되는 바이너리들은 암호화를 위해 대부분 프로텍팅·패킹이 돼서 배포되고 있는데, 프로텍터나 패커들은 API Call의 정보를 숨기거나 다른 보안상의 이유로 IAT를 망가뜨리거나 여러 갈래 꼬아놓은 자체 IAT를 사용하고 있다. 그래서 이 경우는 IAT가 잘 구해지지 확인않기 때문에 해당 테이블을 후킹 할 수 없고, 당연히 내가 원하는 은닉 기술을 활성화 할 수 없다. 따라서 IAT와는 무관하게 궁극적으로 DLL의 엔트리 포인트로 이동하는 부분을 노린, Inline Hook 방법을 많이 사용한다.

API Hooking을 위한 사전 준비 지식

유저 레벨에서 API Hooking을 위해서는 반드시 해당 프로세스에 DLL을 주입해야 한다(물론 DLL없이 빈 번지를 할당하여 코드를 삽입하고 리모트 스레드를 돌리는 방법도 있지만, 그 방법 역시 지금부터 설명할 세 가지 방법 중, 3번에 해당하는 것이기 때문에 별도 설명은 생략한다). DLL을 주입하는 방법에는 세 가지 방법이 있다. 첫째는 메시지 후킹, 둘째는 AppInit_DLLs 이용, 셋째는 CreateRemoteThread 이다. 이중 첫째는 윈도우가 없는 프로세스는 DLL을 집어넣을 수 없고, 둘째의 AppInit_ DLLs은 다른 프로그램이 사용하는 경우도 있으니 루트킷 구현이 목적인 API Hooking 방법으로는 적절한 방법이 아니다. 따라서 모든 프로세스의 DLL을 집어넣기 위한 방법으로는 셋째인 CreateRemoteThread가 가장 적합하다. 이 기법은 대부분 OpenProcess -> VirtualAllocEx -> WriteProcess Memory -> CreateRemoteThread 의 패턴을 사용한다. API Hooking에 이용되는 가장 표준적인 방법이기도 하다.

다음은 어느 API를 후킹해야 루트킷이 되는지 기능별로 살펴볼 필요가 있다. 목적은 자신을 발견할 수 없게 하는 법이 우선이다. 따라서 모듈이나 프로그램을 발견하기 위해서는 파일을 찾거나 메모리를 뒤지는 방법을 생각할 수 있는데 루트킷은 그것을 저지해야 하는 기능이 반드시 포함돼야 한다. 즉 모듈을 찾을 때에는 프로세스 검색, 파일 검색 더불어 서비스 리스트 검색까지 세 가지 방법이 있으며 루트킷은 이 세 가지 검색 방법을 모두 무력화 시켜야 한다.

프로세스 숨김

프로세스 리스트에서 공격자가 만든 바이너리가 보이지 않아야 하는 것이 첫 번째 과제이다. 프로세스를 나열하는 방법은 대표적으로 툴헬프32와 PSAPI가 있다. 따라서 루트킷은 유저 레벨에서 프로세스 리스트를 링크에서 제외시키기 위하여, Pro cess32Next()와 OpenProcess()를 후킹해야 한다. Open Process()만 후킹하면, 프로세스의 핸들만 얻을 수 없다 뿐이지, 툴헬프로는 PID와 프로세스 이름까지는 구해지기 때문에 유저 레벨에선 반드시 Process32Next()까지 처리해야 한다. 자신의 핸들이나 PID를 구해 놓고, OpenProcess()나 Process32Next() 후킹 함수에서 자신을 찾으려 할 때, 자신의 정보를 숨기는 방법이다. 만약 DLL을 가진 경우는 Module32Next()도 같은 방법으로 처리해 준다.

서비스 숨김

바이러스나 백도어를 찾을 때 서비스에 등록됐는지 살펴보는 경우가 많다. 그 때 루트킷이 서비스에 등록이 되어 있는 모습이 쉽게 발견되면 안 되므로, 서비스 리스트를 뽑을 때 그 목록에서 제거할 필요가 있다. 서비스 리스트는 EnumServices StatusEx 이라는 API를 사용한다. 루트킷은 이 API를 후킹하여 자신의 링크를 끊는다.

파일 숨김

탐색기나 내 컴퓨터 등에서 파일 리스트가 보이지 않아야 한다. 원리는 프로세스 리스트 때와 비슷하며 타겟이 되는 API는 FindNextFile 이다. 유저 레벨 루트킷은 이 API도 놓치면 안 된다. 이렇게 유저 레벨 루트킷을 구현하기 위한 세 가지 처리 부분을 살펴보았다. 프로세스, 서비스, 파일 리스트에서 자신을 숨긴다면, 별다른 루트킷 디텍터의 도움을 받지 않는 이상 해당 모듈을 찾기는 결코 쉽지 않다. 커널 드라이버를 설치할 필요도 없고, EXE와 DLL만으로 루트킷을 구현할 수 있다. 이것이 유저 레벨의 루트킷이다.

유저 레벨에서 루트킷 검출 방법

그렇다면 이번엔 유저 레벨에서 루트킷을 찾는 방법에 대해 알아보자. 물론 유저 레벨에서의 검출 방법은 커널 레벨에 비해 그 활용성이 지극히 제한적이고, 기법도 다양하지 않은 편이다. 그러나 방법이 전혀 쓸모없는 것도 아니다. 또한 이런 방법이 반드시 루트킷을 찾을 때만 사용되는 기술은 아니라는 점에서 다른 여러 시스템 프로그래밍에 활용할 수 있는 부분도 있다. 그렇다면 유저 레벨에서의 검출 방법에는 어떤 것이 있는지 살펴보자.

csrss.exe 의 스레드 리스트 이용

윈도우 시스템 프로세스 중 하나인 csrss.exe를 활용하면 유저 레벨에서도 루트킷을 감지할 수 있다. csrss.exe는 Client Server Run-time SubSystem의 약자로 윈도우 시스템을 가동시키는 핵심 프로세스이다. 이 프로세스는 윈도우에서 실행되는 모든 프로세스의 오브젝트를 관장하고 있다. 따라서 csrss.exe의 스레드 리스트를 구하면 루트킷 프로세스까지 검출이 가능하다.

후킹 원복화

후킹 되어 있는 API를 직접 원복화 시켜 버리는 방법이다. 물론 툴로 제공되는 경우도 있고 직접 VirtualProtect()와 Write ProcessMemory()를 이용하여 구현할 수도 있다. 지금은 디버거를 직접 붙여서 강제로 원복화를 시켜 보도록 하겠다. <화면 4>를 보자. 현재 FindNextFile이 h.dll 로 후킹 된 상태이다.

이 상태에서 이 코드를 원복화 시켜버리자. FindNextFile의 엔트리 코드는 mov edi, edi 로 시작하며 그 이후의 코드까지 포함하여 후킹 된 5바이트를 OpCode로 표현하면 0x8B, 0xFF, 0x55, 0x8B, 0xEC이다. 이 코드로 엔트리를 덮어써 버리자. <화면 5>가 그 내용이다.

그 후킹된 코드는 사라지고 오리지날 상태의 엔트리 포인트가 된다(<화면 6> 참조). 이제 FindNextFile의 후킹은 제거했다. 따라서 무결 상태의 FindNextFile을 사용할 수 있을 것이며 루트킷의 DLL을 거치치 않게 되므로 파일 검색이 가능하다. 이런 식으로 API Hooking을 원복화 시켜서 무력화 할 수 있다.

PID 연속 대입 방법

아무리 루트킷이 프로세스를 숨겨도 PID는 당연히 있기 마련이다. 그리고 PID는 0xC번부터 0xFFFF번까지 존재한다(4번 PID는 시스템이 사용한다). 따라서 PID를 4씩 늘려가며 연속해서 프로세스의 핸들을 오픈해 본다면, 현재 프로세스 리스트엔 없지만 PID가 있는 경우가 나타날 수 있다. 이 경우는 100% 루트킷의 프로세스이다. PID Brute Force 라는 기법이며, 단점은 루트킷이 프로세스의 핸들조차 얻지 못하도록 처리해 놓았을 때는 이 방법으로 검출이 불가능하다는 점이다.

유저 레벨 루트킷은 커널 하층부에 또 하나의 시스템이 더 들어올 수 있다는 약점이 있으므로 한계가 있고, 또 모든 프로세스를 통제해야 한다는 점에서 커널 루트킷보다 오히려 더 까다로울 수도 있다. 하지만 유저 레벨 루트킷은 반드시 루트킷 구현의 목적이 아닌 시스템 프로그래밍의 진수를 보여주는 여러 가지 기법을 사용한다. DLL Injection과 API Hooking, 그리고 각종 오브젝트의 활용적인 부분까지 유저 레벨 쪽의 Windows Internals에 대한 지식을 많이 함축하고 있다. 따라서 유저 레벨의 루트킷은 그 기능의 강력함의 여부를 떠나 기술 그 자체, 구현 알고리즘 자체에 우선순위를 두어 평가하는 것도 좋지 않을까 생각한다.

참고자료

1. Rootkit - http://www.rootkit.com

2. Anti-Rootkit - http://antirootkit.com/

3. Rootkits : Subverting the Windows Kernel - by Greg Hoglund, Jamie Butler

'Computer Science' 카테고리의 다른 글

루트킷_4  (0) 2008.06.25
루트킷_3  (0) 2008.06.24
luabind/C++ <;-> 루아 클래스간 상호 참조 및 호출 예제  (0) 2008.06.22
Using Lua with C++ &#8212; A short tutorial  (0) 2008.06.22
루트킷_1  (0) 2008.06.22

'Computer Science' 카테고리의 다른 글

루트킷_3  (0) 2008.06.24
루트킷_2  (0) 2008.06.23
Using Lua with C++ &#8212; A short tutorial  (0) 2008.06.22
루트킷_1  (0) 2008.06.22
웹 브라우저 밖에서 만나는 웹 애플리케이션  (0) 2008.06.10

Using Lua with C++ — A short tutorial

Written by Christian Stigen Larsen

Using Lua is easy! In this short tutorial we'll show how to write a fully working host-program in C++ with Lua callbacks.

Since the static Lua libraries are written in C, you must import them as such:

extern "C" {#include "lua.h"}int main(){  lua_State *L = lua_open();  lua_close(L);  return 0;}

Update 2007-05-14: It has been reported that on some systems you need to include the headers lualib.h and lauxlib.h to compile the above example:

extern "C" {#include "lualib.h"#include "lauxlib.h"}
Compiling and linking with GNU g++:
g++ host.cpp -o host -Ilua-5.0.2/include/ -Llua-5.0.2/lib/ -llua

Including lualib.h and lauxlib.h makes it easy to write a fully working host:

#include <iostream>extern "C" {#include "lua.h"#include "lualib.h"#include "lauxlib.h"}void report_errors(lua_State *L, int status){  if ( status!=0 ) {    std::cerr << "-- " << lua_tostring(L, -1) << std::endl;    lua_pop(L, 1); // remove error message  }}int main(int argc, char** argv){  for ( int n=1; n<argc; ++n ) {    const char* file = argv[n];    lua_State *L = lua_open();    luaopen_io(L); // provides io.*    luaopen_base(L);    luaopen_table(L);    luaopen_string(L);    luaopen_math(L);    luaopen_loadlib(L);    std::cerr << "-- Loading file: " << file << std::endl;    int s = luaL_loadfile(L, file);    if ( s==0 ) {      // execute Lua program      s = lua_pcall(L, 0, LUA_MULTRET, 0);    }    report_errors(L, s);    lua_close(L);    std::cerr << std::endl;  }  return 0;}

Compilation and linking:

g++ host.cpp -o host -Ilua-5.0.2/include/ -Llua-5.0.2/lib/ -llua -llualib

Running Lua programs

Let's test this with some Lua programs. The files here are from the distribution, hello.lua is simply:

-- the first program in every languageio.write("Hello world, from ",_VERSION,"!\n")

Executing a couple of Lua programs with our host program produces:

[csl@eris:~/dev/lua/lua-5.0.2]$ ./host test/hello.lua test/printf.lua-- Loading file: test/hello.luaHello world, from Lua 5.0.2!-- Loading file: test/printf.luaHello csl from Lua 5.0.2 on Wed Mar  2 13:13:05 2005

Calling C functions from Lua

It gets very interesting when Lua programs call your own functions. In the following program, we define a function my_function() and register it with the Lua environment using lua_register(). Our function prints its arguments as strings and returns the integer value of 123.

#include <iostream>extern "C"{#include "lua.h"#include "lualib.h"#include "lauxlib.h"}int my_function(lua_State *L){  int argc = lua_gettop(L);  std::cerr << "-- my_function() called with " << argc    << " arguments:" << std::endl;  for ( int n=1; n<=argc; ++n ) {    std::cerr << "-- argument " << n << ": "      << lua_tostring(L, n) << std::endl;  }  lua_pushnumber(L, 123); // return value  return 1; // number of return values}void report_errors(lua_State *L, int status){  if ( status!=0 ) {    std::cerr << "-- " << lua_tostring(L, -1) << std::endl;    lua_pop(L, 1); // remove error message  }}int main(int argc, char** argv){  for ( int n=1; n<argc; ++n ) {    const char* file = argv[n];    lua_State *L = lua_open();    luaopen_io(L); // provides io.*    luaopen_base(L);    luaopen_table(L);    luaopen_string(L);    luaopen_math(L);    luaopen_loadlib(L);    // make my_function() available to Lua programs    lua_register(L, "my_function", my_function);    std::cerr << "-- Loading file: " << file << std::endl;    int s = luaL_loadfile(L, file);    if ( s==0 ) {      // execute Lua program      s = lua_pcall(L, 0, LUA_MULTRET, 0);    }    report_errors(L, s);    lua_close(L);    std::cerr << std::endl;  }  return 0;}

루트킷_1

요즘은 루트킷(RootKit, 이하 루트킷)이란 단어가 널리 알려져 있다. 하지만 루트킷에 대해 정확히 이해하는 사람은 많지 않다. 한 예로, 대부분의 사람들이 마치 루트킷이 최근에 생긴 걸로 착각하곤 하는데, 루트킷은 18 년 전인, 1990년도에 등장했다. 국내에 알려진 건 2005년, 소니 BMG의 CD 보안이 루크킷을 사용했다는 것이 Mark Russinovich에 의해 알려지면서 CD를 전량 회수하는 사건이 시발점이 됐다. 그 이후로 루트킷에 대한 사람들의 경각심이 더욱 고취됐고, 백신 프로그램도 루트킷에 얼마나 효과적으로 대응하는지가 관건이 되기도 했다.

창과 방패의 이야기

루트킷, 네 정체를 밝혀라

권용휘 rodream@gmail.com|필자는 악성코드 제거 프로그램인 ‘울타리’를 제작/배포하고 있다. 그 이외에도 여러 프리웨어를 http://rodream.net을 통해 배포하고 있다. 가끔씩 코드프로젝트나 루트킷에 글을 기고한다. 악성코드로부터 시스템을 지키기 위해 오늘도 컴퓨터를 켠다.

루트킷은 이미 오래전부터 존재했다. 유명세를 탄 무렵에는 이미 기술이 대부분 공개된 상황이었다. 기술 자체는 새롭거나 특별할 것이 없지만, 언제부터인가 ‘보안의 핵심’ 으로 대두됐다. 과연 왜 갑자기 유명세를 탔을까? 루트킷이 유명해진 이유는 앞에서 언급했듯이 큰 기업의 스캔들로 인해서 루트킷에 관심이 없던 해커와 크래커 그리고 프로그래머에게 존재가 알려졌기 때문이다. 정체가 알려지자 많은 크래커가 루트킷을 사용했다. 악의적인 일을 좀 더 효과적으로 그리고 완전히 성취하기 위해 루트킷의 소스코드나 프로그램을 활용하기 시작했다. 결국 기존의 바이러스가 좀 더 고급적인 보안 기법을 써서 시스템을 파괴했고 백신 업체들은 이를 방어하기 위해 루트킷에 대한 대응책을 마련할 수밖에 없었다.

루트킷의 정확한 의미

이제 루트킷이 정확히 무엇을 의미하는지 알아보자. 말 그대로 루트킷은 루트(Root) 권한을 쉽게 얻게 해주는 킷(Kit)이다. 루트킷을 사용하면, 보안 허점을 이용해 원하는 일을 처리하도록 도와준다. 파일이나 레지스트리를 숨기는 것이 루트킷이 하는 대표적인 일이다. 이는 악성 프로그램이나 바이러스를 시스템에서 제거하려는 시도를 무력화시키기 위한 것이다.

이와 같이 루트킷이 실제로 하는 일은 자신을 은폐하거나 삭제할 수 없도록 하는 게 대부분이다. 일반적으로 자신을 은폐하기 위해서 루트킷은 운영체제나 시스템을 변화시킨다. 우리가 쓰는 운영체제는 크게 두 개의 계층(유저 모드와 커널 모드)으로 이루어져 있다. 커널 모드에는 실제로 운영체제의 핵심적인 작업을 수행하는 코드가 동작하며, 유저 모드는 커널 모드의 코드를 사용하기 위해 API를 사용한다. 예를 들면, 파일을 지우기 위해 프로그램에서 DeleteFile API를 사용하면, 유저 모드에서는 파일을 직접 지우는 기능이 없으므로 커널 모드의 파일 지우는 코드가 동작하도록 요청한다.

또한, 백신 프로그램 등이 바이러스가 동작중인지를 찾아내기 위한 방법으로 어떤 프로그램이 실행중인지 알아내는 방법도 이와 동일한 과정을 거친다. 즉 유저 모드에서는 실제로 작업을 하는 게 아니라, 단지 커널 모드로의 이관을 도와준다. 루트킷을 써서 은폐하는 프로그램은 바로 이렇게 커널 모드로 이관하는 길목에서 자신을 은폐하기 위한 일을 한다. 은폐하는 일 자체가 점점 고도화되고 복잡하기 때문에 루트킷을 이러한 기술의 일련으로 오해할 수 도 있다. 하지만, 루트킷이라고 부르는 것은 기술 자체보다는 기술을 사용해 기존 시스템의 정책(Policy)에 역행할 수 있는 것을 의미한다.

앞에서 언급하였던 유저 모드에서 커널 모드로 이관되는 과정을 변형 시키는 것을 가리켜 후킹(Hooking)이라고 한다. 하지만, 후킹을 사용했다고 해서 모두 루트킷이라고 명명하면 안 된다. 생각해보면 이는 매우 당연한 것인데, 현재 많이 사용하는 DRM 프로그램이 포함한 실시간 암·복호화도 후킹을 사용해 구현되는 경우가 대부분이지만, 아무도 이를 루트킷이라고 말하지는 않는다. 최근 소니 USB에서 새로운 루트킷이 검출됐다는 보도가 나왔다.

기사에서는 루트킷을 판별하는 데 기술적인 측면보다 파일과 프로그램을 은닉하는 것이 악용될 수 있다는 점에 집중하고 있다. 그렇다면, 모두에게 인정받은 프로그램이 정상적인 목적으로 이러한 기능을 사용하는 경우에는 어떻게 될까? 답은 이런 경우에도 루트킷이라는 칭호를 달 수 밖에 없다는 것이다.

필자가 직접 확인했던 모 언론사의 툴바 프로그램은 다른 프로그램이나 사용자에 의해 임의로 지워지지 않도록 프로그램을 숨기고 삭제하지 못하도록 해 놨다. 분명 루트킷이라고 판별할 수 있다. 이는 프로그램을 사용해 다른 프로그램도 함께 숨겨지거나 삭제할 수 없도록 할 수 있기 때문이다. 설명을 덧붙이면, 이 프로그램이 만약 C:\Program Files\??Toolbar 라는 폴더 아래에 있는 모든 파일을 숨기고 아래에 있는 프로그램을 사용자로부터 은닉시킨다면 악성코드를 만드는 사람의 입장에서는 자신의 프로그램의 실행 파일을 자동으로 C:\Program Files\??Toolbar 폴더 아래에 자동으로 복사하도록 설정할 수 있다. 이렇게 된 경우에, 사용자가 ??Toolbar를 설치하면 자기 자신을 보호하기 위한 프로그램이 악성코드를 숨겨주는 부가 효과(Side Effect)가 발생한다. 이러한 부가 효과는 시스템에 매우 치명적이며 악영향을 미친다. 지워지지 않는 악성코드가 악성코드 제작자의 의지가 아닌 상태에서 만들어진 것이다. 만약 ??Toolbar가 매우 유명하고 대부분의 사람들이 사용한다면 모든 악성코드 제작자들은 이 폴더로 자신의 프로그램이 설치되도록 유도하게 될 것이고, 결국 ??Toolbar는 악성코드를 보호하는 프로그램이 되어 악성코드의 숙주가 된다.

루트킷의 종류

루트킷은 범용적으로 쓰이기 때문에 종류도 다양하다. 여기서 언급하는 루트킷의 종류는 필자가 개인적으로 나눈 것으로 절대적인 것이 아니라는 것을 먼저 밝힌다. 우선 루트킷의 전체를 볼 수 있도록, 어떤 루트킷이 있는지 알아보자. 루트킷이 동작하는 환경에 따라서 나누면, 운영체제 별로 루트킷이 존재한다. 윈도우에서 동작하는 루트킷만 살펴보면, 유저 모드 루트킷과 커널 모드 루트킷 그리고 그에 속하지 않는 루트킷(윈도우가 구동되기 전에 동작하는 루트킷)이 있다. 유저 모드와 커널 모드가 따로 있는 것은, 유저 모드에서 할 수 없는 일을 커널 모드에서 할 수 있는 경우가 많기 때문이다.

우리가 가장 많이 사용하는 CPU 인 인텔 호환 CPU는 명령어를 실행할 수 있는 권한을 가지고 있는데, ring0 부터 시작해서 ring1, ring2, ring3 까지, 4개의 ring 권한을 가진다. 윈도우의 경우 유저 모드에서는 ring 권한 중 가장 낮은 ring3 권한을 가지므로 CPU에 시스템 큰 영향을 줄만한 명령어를 실행할 수 없다. 하지만, 커널 모드에서는 ring0를 사용하므로 이를 위해 커널 모드 루트킷이 필요한 것이다. 또한, 생소하게 생각될 수도 있지만, 커널 모드도 유저 모드도 아닌, 루트킷이 있다. 이는 운영체제가 로드되기 전, 하드 디스크의 MBR(Master Boot Record)을 참조하게 되는데, MBR을 수정해 자신이 먼저 실행되도록 시스템을 수정하는 루트킷이다. 또한, 하드디스크가 아닌 비디오카드에 기생하는 루트킷도 존재한다.

이와 같이 다른 하드웨어에 존재하는 저장장치에 기생할 경우에 얻는 이점은 일반적으로 바이러스에 감염됐다고 판단해 하드디스크를 포맷하거나 운영체제를 재설치하는 경우가 많은데, 이러한 루트킷은 포맷과 재설치에도 불구하고 살아남는다는 특징이 있다.

루트킷은 누가 만드는가?

대체 루트킷은 누가 만드는 것일까? 실질적으로 많은 루트킷이 공개되고 배포되는 루트킷 닷컴(http://rootkit.com)에 가면 루트킷을 공개하는 사람들이 자신의 신원을 밝히고 자신의 결과물(RootKit)을 공개한다.

루트킷 자체는 ‘악성’이라기보다는 시스템 보안의 허점을 공개하는 하나의 도구이기 때문에, 그 자체가 나쁜 일은 아니기 때문이다. 물론 이를 악용하면 큰 문제를 일으킬 수 있겠지만, 아직까지 국제적인 제재는 없는 상황이다. 중국의 경우 실제로 크래킹 방법을 잡지에 기고하고 판매하는 경우도 있다. 국내 사정은 아직 먼 이야기지만. 몇 년 전 『해킹, 파괴의 광학』이라는 책이 매우 성황리에 팔렸던 적이 있다. 국내에서는 아직 이 책에 공개된 수준까지만 허용된다. 그렇다면 루트킷을 만들어서 공개하는 이들은 누구일까? 대부분 보안 프로그래머이다. 이 얘기가 다소 혼란스러울 수도 있을 것 같다. 어떻게 시스템을 파괴하기 위한 도구를 보안 프로그래머가 만들 수 있을까 하는 의문에 봉착한다. 이는 방패를 만들기 위해서는 창에 대해 잘 알아야 하고, 창을 잘 아는 사람만이 효과적인 방패를 만들 수 있는 이치와 같다. 하지만, 여기서 필자가 한 가지 명확하고 싶은 것은 루트킷을 만드는 사람과 악성코드를 만드는 사람을 확실하게 구분해야 한다는 것이다. 대부분의 악성코드 제작자는 시스템 자체에 대한 이해도가 높지 않다. 그들은 단지 루트킷을 사용하는 것뿐이다. 이러한 사람들을 스크립트 키디(Script-x Kiddie)라고 부른다. 이들은 시스템에 대한 탐구 의지보다는 과시욕이나 시스템을 파괴하려는 생각만 앞선 사람들이다.

루트킷 기술들

루트킷에서 나오는 기술은 무척 다양하다. 기술의 수준도 높은 반면에, 누구나 알만한 내용도 존재한다. 하지만 “아... 이렇게 역으로 생각할 수 도 있구나”라는 식의 역발상적인 기술이 많다. 필자 개인적으로는 한가한 시간에 여러 프로그래머나 해커의 사이트를 돌아다니면서 그들이 최근에 해온 일들을 훑어보는 습관이 있다. 루트킷에 대한 내용을 읽다 보면, 좀 더 창의적이고 기발한 생각을 떠올리도록 도와주는 경우가 있다.

가장 잘 알려진 기술인 후킹도 기술 자체는 이미 오래전에 알려졌지만(리눅스 등의 운영체제에서는 이렇게 후킹을 하는 것이 운영체제의 일부가 되있기도 하다), 루트킷을 잡아내고 복구(Restore)하는 기술이 발전함에 따라서 좀 더 지능적으로 진화해 왔다. 예를 들면, 백신 프로그램이 루트킷을 탐지해내려면 루트킷 프로그램의 연속된 특정 어셈블리 코드를 찾는 경우가 많다. 특히 <리스트 1>의 코드는 커널 모드에서 Write Protection 기능을 제거하기 위해 CR0 비트를 조작하는 코드이다. 하지만, 루트킷에 많이 쓰이면서부터, 백신 프로그램은 이 코드를 기준으로 루트킷을 탐지한다.

이러한 문제를 해결하기 위해, 안티-루트킷(Anti-RootKit) 프로그램이 특정 실행 코드(op code: 기계어)가 연속적으로 나오는 경우에만 검출해낼 수 있다는 허점을 노려, INC EAX, DEC EAX와 같이 의미 없는 코드로 코드의 흐름을 섞어 놓는 간단한 방법으로 백신의 검출망을 피해간 것이 바로 <리스트 2>의 코드이다. 이러한 의미 없는 코드는 매우 많다. <리스트 2>와 같이 어떤 일을 한 후에 다시 그 일을 되돌리는 명령을 사용하는 것도 있으며 JUMP 명령어를 사용해 이리저리 코드의 흐름을 어지럽히는 방법도 있다. 또한, 어셈블리에서는 아무것도 하지 않는 명령어로 NOP라는 명령어가 있는데, 이를 사용하는 경우도 있다. 이러한 것을 ‘Code Randomization 기법’이라고 하는데, 최근 루트킷에서 흔히 발견하는 간단한 트릭이다. 하지만, 최근에는 실행 코드 자체만 고민하던 해커들이 실행 코드보다는 코드가 사용하는 데이터에 집중하면서 DKOM (Direct Kernel Object Manipulation)이라는 기법이 새롭게 생겨났다. 이 기법은 실행 코드가 사용하는 데이터(프로세스 목록을 저장하기 위한 링크드 리스트 등)를 변경하여 자신을 감추는 방법 등으로 활용되고 있다. DKOM을 사용해 프로세스를 숨기는 방법을 간단히 설명하면 다음과 같다. 먼저, 윈도우에서는 Process 목록을 링크드 리스트를 사용하여 관리하게 되는데, 이 링크드 리스트의 각 항목들을 가지고 연결 고리를 조작하는 방법이다.

<그림 2>와 <그림 3>을 보면 <그림 2>에 있던 세 개의 프로세스가 연결된 상태에서 <그림 3>처럼 하나의 연결을 끊게 되면, 두 개의 프로세스만이 존재하는 것처럼 보인다. 이것이 바로 DKOM 기법을 사용하여 Process 목록을 숨기는 원리이다. DKOM은 매우 혁신적인 아이디어라고 할 수 있다. 왜냐하면, 이전의 루트킷이 실행 코드를 바꾸는데 전념했고, 그렇기 때문에 루트킷을 탐지해내는 방법으로 실행 코드를 확인하는 방법을 많이 사용했다. 루트킷이 아무리 똑똑해도, 언젠가는 메모리 어딘가에 자신을 실행해 달라는 흔적(보통 jmp 명령어나 자신의 코드가 있는 메모리 주소가 된다)을 남긴다. 하지만, DKOM 기법을 사용하면, 데이터 조작 여부를 판단하기가 매우 애매해진다. 이렇게 숨긴 Process를 알아채기 위한 방법은 Process 목록에서 자신을 지워버려도 언젠가 악성코드가 실행되려면 윈도우가 실행 코드 스케쥴을 하는 기반인 스레드(Thread) 목록에서는 지우지 않는 다는 것에 착안하여 검출해내는 방식을 사용한다.

필자는 2007년 10월, RootKit.com에 네이티브 애플리케이션이 루트킷으로 사용될 수 있다는 내용을 기고한 적이 있다. 비스타에서 기존 윈도우와 가장 많이 바뀐 내용이 UAC에 관련된 내용이며, 이것이 네이티브 애플리케이션을 통해 무력화 될 수 있다는 내용이었다. 최근에는 이런 식으로, 새롭게 생긴 정책의 논리적인 구멍(Hole)을 공격하는 경우가 늘고 있다. UAC에 관련된 유사한 예로, 윈도우 비스타의 정식 버전이 출시되기 전인 RC 버전에서는 윈도우의 Swap 기능을 역이용해, Swap out 된 가상 메모리의 내용을 조작하여 다시 그 코드가 Swap In 되어 실행 될 때 시스템의 권한을 가로채는 기발한 방법이 나오기도 했다. 물론 이 문제는 정식 버전이 나오기 전에 이미 수정됐다.

루트킷이 시사하는 것

루트킷이 시사하는 것은 이미 대부분의 사람들이 시스템의 허점을 이용할 준비가 돼 있다는 것이며, 이는 곧 우리가 불안한 디지털 세상을 살고 있다는 말이기도 하다. 더 이상, 크래킹은 많은 지식을 가진 고급 크래커들만의 몫이 아니다. 하지만, 대부분의 사람들이 운영체제가 잘못 설계됐기 때문이라고 오해한다. 물론 운영체제 자체의 문제도 있겠지만 새롭게 보안을 강화시킨 비스타에도 여전히 루트킷은 존재하며, 많은 사람들이 안전하다고 생각하는 유닉스와 리눅스조차도 수많은 루트킷이 존재한다.

필자는 이제 기술적인 문제에서 떠나야 한다고 생각한다. 보안 위협이 사회현상으로 부각되면서 여러 가지 법안이 제정됐지만, 충분한 생각과 고려 없이 제정된 법들은 루트킷의 악용을 규제하기 보다는 안티-루트킷 프로그램에 ‘인증’을 받으면 루트킷 기능을 이용할 수 있다는 새로운 ‘권력’ 을 생산해내기에 이르렀다. 세계적으로 많은 안티-루트킷(백신) 업체들이 있지만, 이들의 방식에도 문제가 많다.

특히, 루트킷을 진단하는 방법으로 실제 코드를 분석하는 방식을 사용하는데, 이에 대한 기준도 업체마다 달라 ‘오진’이 빈번하게 발생한다. ‘오진’의 대부분은 기술적이지 못한 사용자들에게 마치 바이러스인 것처럼 겁을 줘서 ‘오진’ 당한 업체에게는 명예 실추와 영업 손실을 끼친다. 이러한 구조로 인해 백신 소프트웨어를 만드는 업체들이 마치 프로그램 인증서를 발급하는 것과 같은 양상이 됐다. 지금은 오래된 이야기 이지만, ActiveX로 인한 악성코드 및 바이러스가 기승을 부리면서부터, ActiveX에는 인증서라는 것이 필수요소로 작용하게 되었다. 하지만, 인증서를 악성코드 제작자들이 자유롭게 취득하지 못하도록 가격이 책정돼 버렸다. 이로 인해 ActiveX를 사용해 웹 페이지에서 프로그램을 서비스하려면 인증서를 꼭 구입해야 한다. 인증서 가격은 가장 저렴한 방법으로 구매해도 1년에 25만원에 육박하는 적지 않은 가격이다. 이것은 과연 누구를 위한 것인가? 인증서를 구매한다고 해서 악성코드나 바이러스가 박멸되는 것일까? 전혀 그렇지 않다. 오히려 지금에 와서는 합법적으로 인증서를 구매해서 악성코드를 만드는 것이 당연시 되어 버렸다. 이러한 방식은 마치 벼룩을 잡기위해 초가삼간을 다 태우는 격이며, 앞으로 생길 새로운 보안 위협에 대해서는 이러한 새로운 권력을 창출해 내지는 말아야 한다고 생각한다.

웹 브라우저 밖에서 만나는 웹 애플리케이션

어도비의 새로운 CTO가 되어 최근 이슈가 되었던 매크로미디어 출신의 케빈린치(Kevin Lynch)는 어도비 AIR 1.0 버전을 공개하면서 다음과 같이 이야기 했다고 한다. ‘우리는 단지 웹으로 옮기면서 잃어버렸던 옛 데스크톱 시절의 보물들을 되찾아 가는 것뿐이다 (We’re just getting back the lost treasures of the desktop that we lost when we went to the Web)’ 실제로 많은 기업들은 웹브라우저에 갇혀있는 사이트보다는 웹의 장점을 살리면서 데스트톱에서 아이콘만 클릭하면 인터넷이 연결되어있지 않더라도 바로 실행할 수 있는 형태의 접근을 선호한다고 한다. 새로운 시장이라고 할 수는 없지만 그동안 감추어진 보물을 다시 찾은 것처럼 이것들을 차지하기 위한 보이지 않는 경쟁이 진행되고 있다. RIA 시장에서의 플래시 플레이어 사례에서도 보이듯이 먼저 플랫폼을 점령한 이점이 얼마나 강력한가를 알기 때문에 각 기업에서 이 부분에 온 힘을 다해 투자하고 있는 모습을 볼 수 있다. 개발자로써 쏟아져 나오는 새로운 도구에 감탄하기만 할 것이 아니라 이 시장에서 어떻게 기회를 잡을 수 있을지 고민해보는 것도 중요한 시기일 것이다.

이준하 koko8829@naver.com|필자는 Inspiration, Feel Good Factor for Flex Dev (http://koko8829.tistory.com) 라는 긴 이름의 블로그를 열이아빠라는 이름으로 운영하고 있으며 이제 막 세상과 이야기하는 방법을 조금씩 배워가고 있는 중이다. 명랑만화를 좋아하며 오랫동안 잠수중인 길창덕 화백 팬 카페(http://cafe.naver.com/kilchangduk)를 운영하고 있다.

지난 2년여 기간 동안 어도비에서는 AIR라는 제품을 세상에 내놓기 위해 무척이나 공을 들였다. 그러던 중 나름대로 친하게 지내던 모질라 재단에서 프리즘(Prism)이라는 이름의 프로젝트를 공개했다. 프리즘은 사용자가 쉽게 웹 응용프로그램을 자신의 데스크톱으로 가져올 수 있도록 하는 그림을 보여주었다. 일부 언론에서는 프리즘이 AIR를 겨냥해서 발표한 제품이며 파이어폭스를 기반으로 더욱 사용자들이 쉽게 이용할 수 있을 것이라는 반응을 보였다. 이에 발끈한 마이크챔버스(Mike Chambers)와 같은 일부 개발자들은 솔직하지 못한 웹(disingenuous web)이라는 도발적인 제목의 글을 올려서 자사의 AIR 제품과 프리즘은 근본적으로 다른 목적을 가지고 있다고 주장했다.

이렇게 과민하게 반응하는 이유는 웹의 변화가 어떻게 다가올지를 잘 알기 때문일지도 모르겠다. 얼마 전 올라온 통계에 의하면 사람들이 PC 앞에서 보내는 시간의 약 40%를 아웃룩이나 워드 같은 MS의 응용프로그램을 사용하는데 쓰고 있지만 Gmail이나 페이스북, 검색 같은 웹 응용프로그램에 사용하고 있는 시간이 점점 늘어나고 있다고 한다. 불과 몇 해 사이에 컴퓨터가 인스톨된 프로그램만 처리하는 도구가 아니라 웹이라는 공간과 만나 엄청난 생활의 변화를 가져오고 있는 것이다. 이러한 웹 응용프로그램들이 브라우저를 벗어나 독립된 프로그램으로 사용자들이 기존의 데스크톱 프로그램을 사용하던 습관에 따라 활용할 수 있게 된다면 그 비율이 뒤집어지는 것도 시간 문제일 것이다.

웹에서 튀어나온 새로운 경험

2008 자바원 컨퍼런스에서 소개된 이슈 중에 중요한 부분을 차지했던 ‘자바FX’는 ‘Bringing Rich Experiences To All The Screens Of Your Life’라는 말처럼 웹브라우저나 데스크톱뿐만 아니라 사용자들이 접할 수 있는 모든 화면에 새로운 경험 가치를 제공하겠다는 큰 목표를 가지고 있다.

이미 작년 로드맵에 소개되었던 것에서 크게 벗어난 부분은 아니지만 모바일과 임베디드 시장을 기반으로 플랫폼을 확장해나가겠다는 의도가 분명하게 나타났다. 이날 개발팀 라마니(Nandini Ramani)가 소개하는 데모에서는 페이스북 위젯과 관련된 데모를 통해 기존 웹에서 생각하지 못했던 새로운 경험을 만나볼 수 있었다.

웹사이트 안에서 동작하던 위젯을 마우스로 드래그해서 사용자 데스크톱의 바탕화면에 그대로 옮겨놓아 데스크톱 응용프로그램처럼 동작하게 하는 것이다. 그동안 웹과 데스크톱 사이에서는 파일을 드래그해서 업로드하거나 클립보드에 있는 자료를 옮기는 정도의 작업만 가능했는데 웹에 붙어있던 프로그램이 그대로 데스크톱에 옮겨진다는 것은 큰 충격이었다. 자바FX에 대하여는 실체를 접해보아야 알 수 있겠지만 앞서 이야기했던 기능 외에도 어도비 포토샵과 같은 디자인 툴에서 작업한 내용을 그대로 자바FX 소스코드로 변환하는 기능의 경우에는 실버라이트에서 이야기하는 협업 기능과 어도비에서 준비하고 있는 써모(Thermo)의 모습을 그대로 닮아가고 있는 것이 아닌가 생각된다.

어도비 플렉스의 경우 아이콘에 ‘Fx’라는 단어를 사용한 것이 개발초기부터 지금까지 일관되게 지켜왔는데 자바Fx는 어떠한 아이콘을 가져갈지 궁금해진다. 참고로 플렉스 응용프로그램의 확장자는 mxml(의미는 명확하지 않지만 ‘Magic eXtensible Markup Language’라는 해석이 가장 유력하다)이고 자바Fx는 fx라는 파일 확장자를 가진다.

잊고 있었던 보물찾기

앞에서 케빈린치가 이야기했던 잃어버렸던 보물들은 뭘 이야기하는 것일까? 어도비 AIR의 특징들을 살펴보면서 우리가 찾아야 하는 보물들이 어떤 것인지 알아보자.

먼저 AIR와 플렉스 사용범위의 한계에서 찾을 수 있다. 플렉스는 웹브라우저와 그 위에서 돌아가는 플래시 플레이어 기반이다. AIR는 이 한계를 넘어가고 있으며 다양한 형태의 파일과 같이 공존할 수 있다. AIR는 브라우저 대신 AIR 런타임 위에서 각각 다른 실행파일들을 지원하고 있다. 개발을 하기 위해서 전문적인 플래시 기술을 익힐 필요는 없다. 개발자 지원메뉴에서도 Ajax와 플렉스, 플래시 개발자에 대한 별도의 항목을 운영하고 있다.

AIR에서 표현할 수 있는 주요기능은 다음과 같다.

- 파일 입출력 API : 사용자 PC에 파일을 쓸 수 있다.
- SQLite 내장 데이터베이스 : SQLite 데이터베이스 기능을 내장하고 있어서 사용자 PC에 데이터를 저장하고 서버와 동기화시킬 수 있다.
- HTML 지원 : 웹킷(Webkit) 이라는 오픈소스 HTML 엔진을 내장하여 AIR 안에서도 HTML 콘텐츠를 보여줄 수 있다.
- 드래그앤드롭 지원 : 운영체제에서 시스템 드래그앤드롭을 지원한다.
- 네이티브윈도우 : 운영체제 윈도우를 제어할 수 있으며 트레이 아이콘으로 작동하게 할 수도 있다.
- 클립보드 지원 : 클립보드의 내용을 읽고 쓸 수 있다.

그리고 아직은 윈도우 API에 대한 접근에 대한 부분은 이후 버전에서 지원할 계획이라고만 되어있다. 오픈소스로 공개된 내용 중 ‘airconnector’라는 것을 통해서 내 PC에 있는 응용프로그램에 대한 접근을 테스트해볼 수 있다.

AIR의 다양한 기능을 적용한 대표적인 사례로 경매 사이트인 이베이 데스크톱(http://desktop.ebay.com/)을 들 수 있다. 사이트의 특성상 웹브라우저 내에 있는 것보다는 참여한 경매의 진행 상황을 즉시 확인할 수 있는 형태의 응용프로그램이 필요했을 것이다(경매에 참여해보신 분들은 알겠지만 사이트가 편해질수록 경매에 낙찰되기는 더욱 힘들어지는 것 같다). 하지만 웹기반으로 만들어진 사이트를 별도의 데스크톱 응용프로그램으로 재구축하기에는 부담이 되었을 것이고 기존의 장점은 살리면서 사용자에게는 좀 더 편한 사용 환경을 만들어주기 위한 선택으로 어도비 AIR는 최적의 선택이었을 것이다.

이베이 데스크톱을 사용해보려면 이베이 계정을 먼저 생성한 후에 인스톨 페이지에서 프로그램을 다운로드받아 설치하면 된다. 만약 어도비 AIR 런타임이 설치되어있지 않다면 해당 런타임을 다운로드하여 설치한 후에 프로그램이 설치된다.

air 확장자를 가지는 설치 파일을 직접 제공하는 경우도 있지만 사용자가 해당 런타임을 직접 찾아서 설치하는 번거로움을 없애기 위해 badge라는 것을 통해서 바로 설치할 수 있는 옵션도 지원한다. 응용프로그램을 웹사이트에 배포하기 위한 badge를 만드는 과정은 몇몇 파라미터 값만 전달하는 것으로 쉽게 설정할 수 있다. 여기에 약간의 디자인 감각만 추가하면 멋진 설치버튼도 직접 만들 수 있다. 이베이 데스크톱 역시 설치버튼만 클릭하면 모든 설치 과정이 자동으로 진행된다.

웹사이트에서 제공하는 기본 기능들을 제공하고 필요한 경우 해당 사이트로 사용자가 찾아갈 수 있도록 안내한다. 그리고 원하는 내용의 리스트에 대해 rss 형식으로 구독해볼 수도 있고 진행 중인 경매 상황에 대한 메시지도 보내준다. 여기서 더욱 흥미로운 것은 오프라인 상태의 기능이다. 기본적으로 service monitor 클래스를 통해 해당 사이트의 상태나 사용자 네트워크 상태를 확인하여 상황에 맞게 응용프로그램이 동작할 수 있는 기능을 제공하고 있다. 오프라인 상태에서도 프로그램을 실행할 수 있다. 검색 등의 실시간 정보를 조회할 수는 없지만 관심 목록에 저장된 물건이나 의견, 상품의 상세 내역 등은 조회할 수 있다. 퇴근길에 자신의 단말기에 관심 있는 항목을 체크해놓고 네트워크가 지원되지 않은 상황에서도 이동 중에 선택한 항목을 비교해보면서 어떤 것을 선택할지 결정할 수가 있게 된 것이다. 어도비 AIR의 기능중 하나인 SQLite와 같은 데이터베이스를 통해 온라인 상태의 정보를 저장해놓고 오프라인에서 활용할 수 있도록 하는 하나의 예시인 것이다.

오픈소스

어도비 AIR에 적용된 기술 중에서 많은 부분이 오픈소스로 공개되거나 기존의 오픈소스를 활용하여 개발되고 있는데 이러한 프로세스를 정리하기 위하여 별도의 사이트를 운영하고 있다(http://opensource.adobe.com). HTML 콘텐츠를 처리하기 위한 웹킷(Webkit)이나 모질라 재단에 의해 운영되는 Tamarin 같은 경우가 좋은 예다. 또한 SQLite 컨소시엄을 지원하기로 결정하고 스폰서가 되었다. SQLite는 AIR뿐만 아니라 포토샵 라이트룸의 이미지 데이터 처리에 사용하는 등 여러 부분에서 적용을 하고 있다.

그리고 오픈스크린프로젝트(http://www.adobe.com/open screenproject/)를 통해 데스크톱, 휴대폰, 모바일 인터넷 디바이스, 셋톱박스 등에 공통 플랫폼을 제공하기 위한 프로젝트를 어도비가 주도하여 진행하고 있는데 활동내역 중 중요한 이슈가 SWF and FLV/F4V 스펙 사용의 제한 삭제다. 이는 AMF 스펙공개에 이어 파격적인 행보다. 이러한 스펙 공개가 오픈소스는 아니지만 openAMF가 이루어내는 확장성만큼 오픈소스 플래시 플레이어가 다방면의 기기에 적용되기를 바라고 있을 것이다. 플렉스 프로젝트 같은 경우에도 상황실과 같이 24시간 운영되는 환경에서는 이에 적합한 플레이어를 적용하여 개발할 수도 있는 것이다. 이미 Gnash(http://www.gnu.org/software /gnash/)와 같은 오픈소스 플래시 플레이어가 있으며 오픈라즐로에서도 플렉스와 유사하게 swf 파일을 다룰 수 있는 기능을 제공하고 있다.

웹브라우저의 진화

앞서 이야기했던 모질라 재단의 프리즘은 새로운 파이어폭스와 함께 진화해나가고 있다. 먼저 사용자들이 쉽게 자신이 원하는 웹 응용프로그램을 데스크톱으로 가져올 수 있도록 파이어폭스에 연결되는 플러그인이 개발되었다. 이제는 별도의 프로그램 설치 없이 파이어폭스에서 원하는 사이트에 한 번만 클릭하면 자신만의 아이콘을 만들 수 있게 되었다. 파이어폭스 3 에서는 플러그인 형식으로 지원될 계획이지만 파이어폭스 4에서는 통합된 기능으로 좀 더 확장된 기능을 제공할 것이다.

프리즘의 현재 목표는 코드명이 나타내는 의미와 동일하다. 프리즘의 사전적인 의미는 ‘빛의 분산이나 굴절 등을 일으키기 위해 유리나 수정으로 만들어진 기둥 모양의 광학장치’다. 분산이라는 측면에서 웹 응용프로그램들이 다양한 스펙트럼을 가질 수 있도록 지원해주는 것이 프리즘의 목표다.

<그림 1>과 같이 사용자들이 즐겨 찾는 웹 응용프로그램을 프리즘을 통해서 데스크톱에 옮겨주는 역할을 한다. 이런 과정을 통해서 별도의 인스톨을 거쳐야 하고 업데이트할 때마다 불편함을 겪었던 데스크톱 응용프로그램들의 단점을 해결할 수 있는 대안으로 내세울 수 있게 된 것이다.

기존의 웹사이트가 그대로 데스크톱 환경으로 옮겨졌기 때문에 로컬 자원에 대한 접근이나 섬세한 그래픽 표현 등의 제한이 있을 뿐 웹의 장점을 그대로 경험할 수 있게 된다. 하지만 이러한 부분에서는 파이어폭스 3에서는 오프라인에 대한 지원계획 등의 반영으로 기존의 웹 응용프로그램들을 좀 더 강력하게 만들 수 있는 기회를 제공하고 있다.

프리즘 체험해보기

현재 프리즘을 사용할 수 있는 방법은 두 가지다.

먼저 파이어폭스 3 베타가 설치되어있다면 프리즘을 바로 체험해볼 수 있는 플러그인을 적용해보자. 해당 페이지에서 바로 설치하면 된다(https://addons.mozilla.org/en-US/firefox/ addon/6665).

플러그인을 설치했다면 도구(한국어 설치기준) 메뉴에 ‘Convert Website to Application’이라는 항목이 생겼을 것이다.

데스크톱 응용프로그램으로 만들고 싶은 사이트에 방문하여 해당 메뉴를 클릭하면 간단한 설정창을 볼 수 있다. 몇 가지 옵션만 지정하고 확인을 해주면 바탕화면에 만들어진 바로가기 아이콘을 확인할 수 있다. 아이콘은 사이트에 파비콘이 지정되어있다면 해당 파일을 자동으로 설정해준다. 실행해보면 웹브라우저가 아닌 독립된 프로그램 위에 자신이 지정한 사이트가 올라오는 것을 확인할 수 있다.

다음은 직접 프리즘 구동 프로그램을 설치해서 확인해보는 방법이다. 설치 후 실행되는 화면은 파이어폭스 플러그인과 동일하다. 다만 직접 URL 같은 항목은 입력해주어야 한다. 삭제 시에는 설치된 아이콘만 삭제하면 된다.

프리즘은 XULRunner를 바탕으로 만들어진 브라우저 형태의 Webrunner에서 시작되었다. 모질라 위키(http://wiki.mozilla. org/Prism)에서 좀 더 자세한 내용을 확인해볼 수 있다.

프리즘과 어도비 AIR는 같은 것 아닌가요

프리즘은 현재 상태로는 웹브라우저를 대체하는 기능으로만 제한되어있다. 사용자가 추가적인 정보를 저장해놓거나 콘텐츠를 제어할 수 있는 기능은 제공하지 않는다. 물론 향후의 로드맵에 따라 방향성은 변경될 수도 있지만 현재로서는 기능을 제한하고 있다. 더 상세한 기능이 추가된다면 일반적인 사용성이 떨어질 것이다. 앞서 만들었던 imaso 사이트를 보여주는 웹브라우저 형식의 기능은 어도비 AIR에서도 쉽게 만들 수 있다. 여기에서 단순하게 웹사이트를 옮겨놓는 것이 아니라 다양한 웹 기술을 접목시켜 또 다른 콘텐츠를 만들어낼 수도 있는 것이다. 웹브라우저 기능에 간단하게 해당 사이트의 이미지를 추출해내는 소스를 살펴보도록 하겠다. 코드의 내용은 AIR in Action(Manning, 2008)의 샘플을 참고했다.

실행화면에는 한 개의 텍스트 입력박스와 세 개의 창이 배치된다.

입력박스에는 원하는 url 주소를 입력하게 되고 세 개의 창에는 각각 사이트 콘텐츠, 이미지 리스트, 선택한 이미지가 표시된다. 입력상자에서 사용되어지는 API는 플렉스에서와 동일한 API를 사용하게 된다.
TextInput 콘트롤에서는 change, dataChange, enter, textInput과 같은 이벤트와 상속된 이벤트를 사용할 수 있다. 여기서 사용한 enter는 사용자가 Enter키를 눌렀을 때의 이벤트 처리다.

<mx:TextInput id=“InputBox” width=“100%” enter=“onInput(event)”/>

이벤트가 처리되면서 입력한 문자열이 currentURL이라는 변수에 들어가게 되는데 이때 ‘[Bindable]’ 이라고 선언된 변수의 경우 변수의 속성이 변경되면 자동으로 속성변경에 대한 이벤트(propertyChange)가 생성된다. 때문에 다음의 HTML 콘트롤의 location 값이 빈값에서 입력한 값으로 반영된다.

<mx:HTML id=“page” complete=“onHTMLComplete(event)” location=“{currentURL}”/>

HTML 컨트롤은 플렉스에서는 제공되지 않고 어도비 AIR만의 고유한 API다. HTML 외에도 윈도우나 파일 시스템과 같은 기능으로 활용할 수도 있다. 페이지 로딩이 완료되면 해당 이벤트를 받아 이미지를 가져오게 되는데 이때 자바스크립트의 DOM에서 document.images 속성에 접근하게 된다. 이 속성은 HTMLCollection 객체로 존재하는데 HTMLCollection이나 NodeList 같은 객체는 배열처럼 사용할 수 있다. 자바스크립트 프레임워크인 프로토타입(Prototype)에서는 $A 함수를 통해서 이러한 DOM 컬렉션 클래스들을 배열 객체로 변환시켜 사용하고 있다. 여기서는 루프를 돌면서 해당 값을 다시 배열에 담고 있다. 결과로 나온 값을 리스트에 집어넣고 해당 리스트에서 원하는 이미지를 처리하게 된다.

간단한 예제이지만 기존의 웹과 어떠한 형태로 통합되고 콘텐츠를 다룰 수 있는지 보여주는 좋은 예제다.

오프라인 웹 애플리케이션

오프라인 웹 응용프로그램을 이야기한다면 구글 기어스(Google Gears)를 빠뜨릴 수 없다. 실제 어도비 AIR 관련된 샘플이나 프로젝트 중에도 구글 기어스를 기반으로 제공하는 서비스들이 많다. 구글 독스 등이 최근 오프라인에서도 사용가능하게 되었다. 또한 모바일 부분도 지원하기 시작했다고 한다. 아직 1.0 버전이 나오기도 전에 이미 많은 곳에서 사용되면서 여러 가능성을 보여주고 있다. 구글 기어스는 정교한 데이터 저장관리와 응용프로그램 캐싱 및 멀티 스레딩 기능 자바스크립트 API를 도입하여 강력한 웹 응용프로그램 오프라인 기능을 손쉽게 추가할 수 있도록 한다. 웹 데이터 처리 부분은 최근 발표된 IE8에서도 유사한 오프라인 정보 관리 기능을 선보였다고 한다.

WPF(Windows Presentation Foundation)의 경우 뉴욕타임즈의 사례에 이어 국내 언론사 등에서 도입 하였는데 기존 언론사 사이트와는 달리 종이신문의 장점을 모니터로 옮긴 느낌을 주도록 많은 신경을 쓰고 있다. 데스크톱 응용프로그램이 가질 수 있는 가능성 중의 하나가 이러한 사용자들의 손맛을 살려주는 일이다. 웹에서는 여러 가지 제약에 어려웠던 점들이 데스크톱에서는 쉽게 해결되는 경우가 많다.

10년 후의 웹

인포메이션 디자인 업계의 전설적인 인물인 클라멘트모크(Clement Mok)는 10년 전에 이런 이야기를 했다고 한다. ‘웹을 PDA, 전화 등으로 사용할 수 있는 멀티모달(Multimodal)로 생각한다면 지금과 전혀 다른 시각에서 바라봐야 한다. 멀티모달은 한 가지 이상의 네비게이션 시스템이 가능하다. 10년 이내에 우리의 웹에 대한 시각이 멀티모달과 다차원으로 변할 것이다. TV, 컴퓨터, PDA나 전화로도 웹을 사용할 수 있을 것이다’. 10년이 지난 지금 한국에서는 휴대전화를 통해서 풀브라우징 서비스까지 가능하게 되었다. 그렇게 지나오는 기간 동안 한 가지 시각만 고집하고 있었다면 웹은 여전히 사각 모니터 안에만 존재했을 것이다. 웹의 공간이 브라우저 안에만 있는 시기는 얼마가 지나면 또 다른 추억이 되어버릴지도 모른다. 단지 웹을 데스크톱 환경으로 끌어내리려고만 기술적으로 고민했다면 지금과 같은 다양한 경험들을 만들어낼 수 없었을 것이다. 이제 플랫폼은 만들어져있고 그 위에 상상력을 펼치는 것은 여러분의 몫이다.

참고 자료
1. Adobe AIR to erase Web, desktop division | http://www.news.com/8301-10784_3-9877238-7.html
2. Mozilla Prism and the disingenuous web | http://www.mikechambers.com/blog/2007/10/25/mozilla-prism-and-the-disingenuous-web/
3. PC에서 가장 많이 사용하는 소프트웨어(서비스)는? | http://poem23.com/818
4. 자바원 개막: 썬 ‘자바FX’ 집중 조명! | http://www.zdnet.co.kr/news/enterprise/etc/0,39031164,39168588,00.htm
5. 개정판 - 예제로 배우는 어도비 플렉스(에이콘,2008)
6. airconnector | http://code.google.com/p/airconnector/
7. 파이어폭스 3 베타 다운로드 | http://www.mozilla.com/en-US/firefox/all-beta.html
8. Prism for Firefox 설치 | https://addons.mozilla.org/en-US/firefox/addon/6665
9. 파이어폭스 확장기능으로 돌아온 모질라 프리즘 | http://paperinz.com/499
10. http://en.wikipedia.org/wiki/XULRunner

bogus_SJ_chk.pdf < = 클릭하면 논문을 다운로드 받습니다.

JAVA로 MP4 스트리밍 클라이언트의 개발????????

논문사기꾼 등장이요!!!!!!



+ Recent posts