2008. 12. 14. 00:07

INTERRUPT PROCESSING VS. POLLING PROCESSING

오늘은 인터럽트 처리 방식과 폴링 방식에 대해서 알아 봅시다.

폴링 처리 방식은 지속적으로 이벤트가 발생하는 것을 감시하고 발생한 이벤트에 따른 명령을 처리하는

것을 의미합니다. 그에 반해서 인터럽트 처리 방식은 비동기적인 이벤트가 발생하는 그 순간에서만

프로세서가 관심을 가지고 해당 인터럽트를 처리합니다. 위의 두 개념만 보아도 폴링 처리 방식이 이벤

트를 지속적으로 감시하기 때문에 인터럽트 처리 방식에 비해 비효율적이라는 것을 알 수 있습니다. 뿐

만 아니라 감시하고자하는 대상이 많아질수록 그에 대한 이벤트 처리는 응답성이 떨어지는 점도 단점이

라고 할 수 있습니다.

그럼에도 불구하고 폴링 처리 방식이 사용되는 이유는 인터럽트 처리 방식에 비해 경제적이기 때문입니

다. 인터럽트 방식은 내부 혹은 소프트웨어 인터럽트 뿐만 아니라 하드웨어 외부에서 발생하는 하드웨어

인터럽트를 처리하기 위한 디지털 회로 설계 및 인터럽트 제어 레지스터와 같은 부품들이 추가적으로 필

요합니다. 결국 처리해야 할 디바이스 장치가 많을 수록 그에 따른 인터럽트 처리를 위와 같은 이유로 하

드웨적으로 구현해야 하기 때문에 하드웨어 자체의 단가가 올라갈 수 밖에 없겠지요. 폴링 방식 자체는

하드웨어 뿐만 아니라 소프트웨어적으로도 그 구현이 가능합니다.


                                             <그림1> 폴링 방식과 인터럽트 방식의 비교
                              <폴링 처리 방식>                            <인터럽트 처리 방식>



                                      <폴링 처리 방식의 소프트웨어적인 구현>

while( TRUE )
{
      
RXD = RX_char_scan();

       if( RXD != 0x00 )
      
{

            
switch( RXD )
            
{
                   
case LED1_ON_OFF: Command; break;
                   
case LED2_ON_OFF: Command; break;
            
}

       }
}


이것은 마치 윈도우 프로그래밍에서 메시지 루프의 메시지 인출 방식을 GetMessage()와

PeekMessage()로 처리하는 것과 비슷한 메커니즘이라고 할 수 있습니다. GetMessage()와

PeekMessage()의 차이를 안다면 쉽게 이해가 될 수 있는 내용이겠지요.

Written by Sim-hyeon, Choe


2008. 12. 14. 00:04

[User Mode RootKit] CreateRemoteThread를 이용한 DLL INJECTION

CreateRemoteThread를 이용한 DLL INJECTION 기법 Written By Sim-hyeon, Choe

DLL INJECTION 기법에 대해서 예전에 제가 서술한 적이 있었습니다만 다시 기고합니다.

DLL INJECTION이 필요한 배경부터 설명드리겠습니다.

Win32기반의 Virtual Memory는 어떠한 구조를 지니고 있는지 여러분들은 잘 아시리라고 생각하고

있습니다. 4GB의 주소 공간에서 하위 주소 공간 2GB는 독립적인 프로세스를 위한 주소 공간, 그리고

상위 2GB는 커널을 위한 영역으로 존재하지요.
(물론 Windows Version에 따라서 주소 공간의 Limit가 다르기도 합니다.
이것은 또 옵션에 따라 변경이 가능합니다)

잘 아시다시피 Win32 운영체제는 Protected Mode를 기반으로 동작합니다. 커널 영역의 접근에 대한

보호가 필요하기도 하지만 각각의 프로세스들 역시 서로 격리되어 자신의 주소 공간 내에서만 접근이 가

능하지요. 이것은 프로세스들의 주소 공간에서 논리적인 주소(가상 주소)는 동일하나 물리 메모리에 맵

핑되는 주소의 위치는 서로 다르다는 것을 의미합니다. 그리고 두 프로세스가 서로 같은 논리 주소를 참

조하더라도 다른 물리 주소 공간을 참조할 수 있는 것은 페이지 테이블에서 논리 주소와 맵핑된 물리

주소를 가지고 있기 때문이겠지요.

중요한 것은 프로세스간 서로의 주소 공간에 대한 접근이 불가능하다는 점입니다. 물론 IPC와 같은 기법

으로 분명 프로세스간의 통신이 가능합니다. 하지만 이는 어디까지나 두 프로세스가 정상적으로 상호 프

로토콜을 가지고 접근하기 때문에 가능하지요. 그렇다면 방법이 존재하지 않는 것일까요..?

없다면 제가 글을 쓸 이유가 없겠지요.

우선 A Process와 B Process가 존재한다고 가정하겠습니다. 그리고 A Process는 Injector 역할의

Process라고 하겠습니다. 여기서 A Process가 Injector라는 의미는 공격자(악성코드)로 보시면 됩니

다. B Process는 Injection을 당하는(공격당하는 타깃) Process로 보시면 되겠습니다. 공격자(A

Process)의 목적은 B Process가 사용하고 있는 API를 Hook하여 자신의 목적 달성을 위한 코드를 수

행할 수 있도록 설치하는 것이라고 하겠습니다. API를 Hook을 한다는 것은 타깃 프로세스의 메모리를

수정함을 의미하는데, 가능한지 의문이 생기겠지요. 그리고 코드 역시 타깃 프로세스 내에 주입이 가능

한지 의문이 드실 겁니다. 그렇다면 여기서 어떻게 타깃 프로세스에 접근하여 그 메모리 내용을 수정 할

수 있을까요? 그에 대한 해답은 Debug API에 있습니다.

예를 들어, 우리가 VC와 같은 개발툴로 어떤 프로그램 소스를 작성하고 프로그램의 각 루틴들이 문제없

이 동작하는지를 살펴보기 위해서 디버깅을 하게 되는데, 원하는 특정 위치에 Break Point(중단점)을

설정하여 실행을 일시적으로 통제 할 수 있음을 아시고 계실 겁니다. 그리고 디버깅 중에 변수들의 값을

확인 할 수도 있고 변경도 가능하며, 심지어 디버깅 중에 코드의 수정도 가능하다는 사실 역시 아실 겁니

다. 우리가 위의 작업을 하고 있다고 하였을 때, 주시해야 할 부분은 VC는 "디버거 프로세스"가 되고

어떤 프로그램 소스라고 언급하였던 녀석은 "디버기(디버깅을 당하는) 프로세스"가 된다는 것입니다.

풀어서 한번 더 쉽게 설명드리자면, 우리가 어떤 프로그램을 작성하기 위해서는 이를 작성하기 위한

툴이 당연히 실행되고 있어야 할 것입니다. 그 툴이 VC를 말하구요. VC가 프로세스 상태가 되어야 한다

는 것이지요. 그리고 작성한 프로그램이 제대로 돌아가는지 테스트 해보기 위해서는 그 프로그램을 컴파

일하고 실행 파일을 생성하여 실행하게 되는데요.. 이것도 프로세스 상태로 존재함을 말씀드리는 겁니

다. 다만 여기서 작성한 프로그램은 그냥 실행이 아니라 "디버그 모드"로 실행시킴을 의미하지요.

핵심을 정리하자면, VC라는 프로세스로 디버깅이라는 작업을 통해서 작성한 프로그램을 타깃 프로세스

로 생성하여 그 주소 공간에 접근하고 수정(변수 및 코드 변경) 및 실행을 통제(브레이크 포인트) 할 수

있다는 사실입니다. 아무리 Protected Mode 기반의 OS라 할지라도 마이크로소프트사에서는 개발하고

있는 프로그램의 디버깅을 위해 이러한 메커니즘을 제공해 줄 수 밖에 없다는 것이지요. 하지만 DEBUG

API는 말 그대로 DEBUG를 위해서 존재하는 API들입니다.

MS에서도 충분히 보안에 대한 위험성을 인지하고 제약을 걸어 두었는데요.. DEBUG API는 반드시

DEBUG 모드로 프로세스를 ACCESS해야 사용이 가능합니다. 그리고 DEBUG API는 오버 헤드 또한 크

기 때문에 되도록이면 많은 사용을 기피해야 합니다. 디버깅 작업을 할 때 크게 느려지는 이유도 바로 거

기에 있지요. DLL Injection을 위해서 필요한 DEBUG API는 WriteProcessMemory 하나면 충분합니

다. API 이름 자체가 워낙 설명적이라 용도는 생략하겠습니다(MSDN 참고).


그럼 다시 본론으로 돌아와서 DLL INJECTION을 위해 수행되는 절차를 천천히 나열하면서

설명드리도록 하겠습니다..

1. 침투 대상이 되는 프로세스를 찾는다.
2. 디버그 모드의 권한으로 프로세스를 엑세스한다.
3. 프로세스 주소 공간에 인젝션 할 DLL Name 크기의 공간을 VirtualAllocEx로 할당한다.
4. 인젝션 할 DLL Name을 3번에서 할당한 주소 공간에 WriteProcessMemory로 쓴다.
5. CreateRemoteThread로 침투 대상이 되는 프로세스에 Thread를 생성한다.
6. 루트킷 DLL이 로드되는 시점에서 API Hook을 수행한다.



1. 침투 대상이 되는 프로세스를 찾는다.

    우선은 우리가 침투할 타켓 프로세스를 엑세스하기 전에 먼저 찾아야 할 것입니다.

    이것은 ToolHelp나 ADVAPI를 사용하여 현재 상주하고 있는 프로세스를 나열하고 그 정보를

    캡쳐 할 수 있도록 윈도우즈 자체에서 라이브러리를 제공해 주고 있습니다. 캡쳐한 정보에서

    는 우리가 원하는 프로세스 이름을 가지고 있는지 확인하고 일치하면 프로세스 아이디를

    얻어 올 수 있습니다.


2. 디버그 모드의 권한으로 프로세스를 엑세스한다.

    OpenProcess로 접근하고자하는 프로세스를 얻어온 아이디로 엑세스 할 수 있습니다.

    그중에 첫 번째 매개 변수는 프로세스를 어떤 권한을 가지고 접근할지에 대한 플래그들을

    줄 수 있는데, 우리가 인젝션을 위해 필요한 플래그들을 나열 해보면,

    PROCESS_CREATE_THREAD (CreateRemoteThread 허용)
    PROCESS_VM_OPERATION   (DEBUG 모드로 접근하기 위해서 반드시 적용해야 함)
    PROCESS_VM_WRITE          (WriteProcessMemory 허용)

    위의 3개 플래그가 필수가 되겠지요.


3번과 4번은 5번부터 설명드리고 왜 그러한 작업이 필요한지에 대해서 설명드리도록 하겠습니다.


5. CreateRemoteThread로 침투 대상이 되는 프로세스에 Thread를 생성한다.

    CreateRemoteThread는 [프로세스의 핸들 + CreateThread]의 조합이라고 보시면 됩니다.

    CreateThread는 현재 프로세스 내에서 쓰레드 생성이 가능한데, CreateRemoteThread는

    프로세스의 핸들을 매개 변수로 줄 수 있으므로, 타 프로세스에 쓰레드 생성을 가능케하지요.

    CreateRemoteThread에서 중요한 매개 변수들만 보면 아래와 같습니다.

    CreateRemoteThread(        프로세스 핸들,
                                              0,
                                                  NULL,
                            쓰레드 함수의 시작 주소,
                             쓰레드 함수의 파라미터,
                                                  NULL,
                                                       0,
                                                   NULL
                              );

    여기서 유의해야 할 부분이라면 CreateRemoteThread 함수를 실행하는 녀석은 "디버거 프로세스"

    라는 점입니다. 처음에 말씀드렸던 공격자 프로세스(Injector)를 의미합니다. 그리고

    CreateRemoteThead의 네 번째 매개 변수에는 쓰레드를 생성하여 호출할 함수의 주소가 되는데요.

    Caller 역시 "디버거 프로세스"라는 것입니다. 하지만 생성된 쓰레드의 Kernel Object는 "디버기 프

    로세스"의 소유며, 쓰레드의 실행도 "디버기 프로세스"입니다. 이것을 잘 구분하고 계셔야 됩니다.

    여기서 쓰레드의 시작점으로 호출할 함수의 주소는 LoadLibrary입니다. 그렇게 해야 비로소

    루트킷이 되는 DLL을 "디버기 프로세스"에서 로드하도록 만들 수 있을 테니까요.

    하지만 네 번째 매개 변수에 LoadLibrary라고 그냥 써버리면 안된다는 것입니다.

    왜냐하면, 공격자 프로세스의 LoadLibrary의 주소와 주입당하는 프로세스의 LoadLibrary를 참조하

    는 주소는 서로 다르기 때문이지요. 물론 LoadLibrary가 실제 KERNEL32.DLL에 위치하는 주소는

    동일합니다만 문제는 두 프로세스에서 LoadLibrary를 참조하는 주소(ILT의 주소)가 서로 다르다는

    것입니다. (이해를 위해서 또 많은 설명이 필요한데요. 궁금하시면 저에게 질문해 주시면 친절히 답

    변해 드리겠습니다)

    결국 GetProcAddress로 KERNEL32.DLL에 존재하는 LoadLibrary의 주소를 동적으로 구해와야 하

    지요. 공격자 프로세스나 주입당하는 프로세스나 KERNEL32.DLL이 맵핑되는 시작 번지는 항상 동일

    합니다. 그렇기 때문에 공격자 프로세스에서 로드된 KERNEL32.DLL의 GetProcAddress의 주소를

    구해도 전혀 문제가 되질 않는다는 겁니다.

    자~ 여기까지 왔으면 쓰레드 생성을 당한 디버기 프로세스(B Process)에서 LoadLibrary를 쓰레

    드 함수로 호출이 됩니다.

    (우리는 흔히 쓰레드를 사용하면 우리가 만든 사용자 함수를 엔트리에 두는데 그 대신에

     LoadLibrary라고 보시면 됩니다)

    여기서 중요합니다. 우리는 LoadLibrary( "우리의 루트킷 DLL" ) 형태로 호출을 원합니다.

    분명 LoadLibrary를 쓰레드의 시작 함수로 호출했지요? 그런데 위의 파라미터에서 존재하는 문자열

    은 어디에 존재하는가요? 우리는 "디버기 프로세스(공격당하는 타깃 프로세스)"에서 우리의 루트킷

    DLL 문자열을 선언 및 할당한 적은 어디에도 없다는 것입니다. 이해가 되십니까?

    그렇기 때문에 디버기 프로세스에 우리의 루트킷 DLL의 문자열을 할당 해 놓고 그 문자열의 시작 주

    소값을 아까 공격자 프로세스에서 CreateRemoteThread 할 때, 다섯 번째 매개 변수 파라미터에 함

    께 전달해 주어야하는 이유가 여기에 있습니다. 제가 5번부터 설명드린 이유도 여기에 있구요.

    이제 CreateRemoteThread 하기전에 3, 4번 과정을 먼저 수행해야 이유를 이해하실 수 있을 것입니

    다..


3. 프로세스 주소 공간에 인젝션 할 DLL Name 크기의 공간을 VirtualAllocEx로 할당한다.

    Win32 API를 살펴보면, 함수 이름에서 접미사 "Ex"가 붙은 API를 보실 수 있을 겁니다.

    함수 이름에서 알 수 있듯이 VirtualAlloc은 가상 주소 공간을 할당하는 API입니다.

    물론 현재 프로세스 내에서가 되겠지요. 하지만 이 함수에 Ex가 붙으므로 프로세스 핸들을

    매개 변수로 줄 수 있습니다. 결국 다른 프로세스에 가상 주소 공간을 할당 할 수 있음을 의미하지요.

    VirtuallAllocEx로 이제 타깃 프로세스로 할당 할 수 있음을 알 수 있습니다.

    (함수에 대한 자세한 설명은 MSDN을 참고하시길..)


4. 인젝션 할 DLL Name을 3번에서 할당한 주소 공간에 WriteProcessMemory로 쓴다.

    문자열 크기 만큼 타깃 프로세스에 할당했으면 이제 그 문자열을 쓰면 되겠습니다..

    이제 1~5번까지 모두 연결이 되나요? 결론은 타깃 프로세스에서 수행되는 쓰레드의 시작 함수가

    타깃 프로세스에 맵핑된 KERNEL32.DLL의 LoadLibrary가 될 것이고, LoadLibrary할 DLL은

    루트킷이 되는 DLL이 되겠지요. 물론 그 DLL의 이름은 타깃 프로세스 주소 공간에 이미 위치하기

    때문에 LoadLibrary( "루트킷 DLL" )가 타겟 프로세스에서 무사히 수행이 될 것입니다.


6. 루트킷 DLL이 로드되는 시점에서 API Hook을 수행한다.

    그렇다면 API Hook을 어떻게 할 수 있을까요? 그 비밀은 LoadLibrary API 내부에서 로드하기 위한

    여러 과정을 수행하게 되는데요. 중요한건 로드하고 나면 로드된 DLL의 DllMainCRTStartup을 호출

    합니다. DllMainCRTStartup은 다시 DllMain을 호출하구요.

    DLL에도 메인 함수가 있습니다. 이것은 프로그래밍시 메인 함수를 생략 할 수도 있습니다.

    (컴파일 될 때 컴파일러가 묵시적으로 기입하게 되지요)

    아무튼 중요한건 DLL 메인 함수를 호출하면서 매개 변수로 DLL_PROCESS_ATTACH라는 파라미터

    를 함께 넘겨주게 됩니다. 한마디로 DLL이 로드될 때 발생하는 이벤트라고 보시면 되겠지요.

    그 파라미터가 dwReason인데, dwReason == DLL_PROCESS_ATTACH 임을 체크한 후에 우리가
    하고자하는 루틴(API Hook)을 수행해주면 끝입니다.


    결국은 CreateRemoteThread 한방으로 인젝션 및 후킹까지 이루어 질 수 있음을 의미하지요.

    설명이 길었네요. 여기서 잘 이해가 안되시거나 궁금하신 점이 있으신 분들은 여기서 저에게 질문해 

    주시거나 메일로 질문해 주시면 친절히 설명해 드리도록 하겠습니다... 이상입니다.

2008. 12. 13. 17:39

AVR-ATmega128 Architecture

이번 시간에는 AVR Micro Controller와 ATmega128에 대한 개론을 학습하는 시간을 갖도록 하겠습니다. 저도 처음 접하는 AVR-ATmega128입니다만, 내부 구조를 살펴봄으로써 임베디드 아키텍쳐를 이해하는데 좋은 계기가 될 것 같습니다. ATmega128로 간단하게 하드웨어를 제어하고 조작해 보는 것도 나쁘진 않군요.

다음글은 'AVR Controller ATmega128 정복'에서 발췌하였음을 사전에 말씀드립니다.


AVR Micro Controller

이 AVR은 Program Memory와 Data Memory를 엑세스하기 위한 Bus를 독립적으로 사용하는 Havard Architecture와 Pipe Line 처리 방식을 기반으로 하는 RISC(Reduced Instruction Set Computer)를 기반으로 하여 빠른 명령 처리 속도를 자랑한다. 뿐만 아니라 AVR은 이를 ATmel사의 장점인 플래시 메모리(Flash Memory) 기술과 접목시켜 칩내에 프로그램 코드용으로 플래시 메모리를 내장하고 여기에 사용자 프로그램을 쉽게 다운로드 할 수 있는 ISP(In-system Programming) 방식을 적용하였다.
 

ATmega Family

이 패밀리는 mega 패밀리라고도 하며, 모든 모델명이 ATmega로 시작한다. 내부에 8KB~256KB의 플래시 메모리를 가지고 있으며, 가장 규모가 크고 성능이 높은 응용 분야에 사용하는 널리 사용되며 가격이 꽤 높고 기능이나 성능도 높다. 따라서 여기에 해당하는 모델들은 패키지도 28~100핀 정도로 핀수가 상당히 많다. 이중에서 성능이 우수한 모델들은 20MHz 클럭에서 20MIPS의 명령 처리 속도를 갖는다. 내장하고 있는 플래시 메모리의 용량에 따라 기본 모델이 ATmega8, ATmega16, ATmega32, ATmega64, ATmega128, ATmega256 등으로 정착되어 가고 있다.

 

ATmega128의 주요 특징

① 고성능이면서 저소비전력형의 8비트 마이크로컨트롤러이다.
② 진보된 RISC 구조를 사용하여 16MHz에서 평균적으로 16MIPS의 명령 처리 속도를 낸다.
③ 133종의 명령어 셋을 가지며, 이것들 중의 대부분은 1 클럭 사이클에 실행된다.
④ 32개의 8비트 범용 레지스터를 가지며, 이밖에 2사이클에 실행되는 곱셈기와 많은 I/O 제어용
     레지스터를 가지고 있다.
⑤ 128KB의 ISP 방식 프로그램용 플래시 메모리를 가지고 있으며, 이것은 10,000번까지 지우고 다시
    쓸 수 있다. ISP를 수행하기 위한 전용 SPI 통신 인터페이스를 가지고 있다.
⑥ 4KB의 데이터 저장용 EEPROM을 가지고 있으며, 이것은 100,000번까지 지우고 다시 쓸 수 있다.
⑦ 4KB의 데이터 저장용 SRAM을 가지고 있다.
⑧ 외부에 약 60KB의 데이터 메모리를 인터페이스 할 수 있다. Memory-Mapped I/O 방식을 사용하므로
    외부 I/O도 이 데이터 메모리 영역에 접속하여 사용한다.
⑨ 내장 메모리의 프로그래밍과 온칩 디버그 기능을 수행하기 위하여 JTAG 인터페이스 기능을 가진다.