이전에도 소개 드린 바 있지만 Windows 운영체제의 커널은 여러 구성요소로 구성되어 있습니다. 그러다 보니 내부를 구성하는 함수의 종류도 많고 그 양도 많습니다. 운영체제를 위해 구현된 많은 함수들은 어느 구성요소에 속하는 함수인지를 구분하기 위해서 함수명 앞에 Prefix를 사용하고 있습니다.

커널 함수들의 Prefix를 간단히 정리하면 다음과 같습니다.

Io_ : I/O Manager
Ps_ : Process Structure Module
Mm_ : Memory manager
Ex_ : Executive (heap 관리, 동기화 처리 등)
Ob_ : Object Manager
Se_ : Security Reference Monitor
Rtl_ : Runtime Library Component (자주 사용되는 utility function들)
Zw_ : Kernel-mode에서 호출되는 Native API들
Ke_ : thread나 processor 사이의 low level 동기화를 위한 함수
Hal_ : HAL(Hardware Abstraction Layer)

Posted: Wednesday, October 14, 2009 7:

'Programming' 카테고리의 다른 글

Win7의 부팅속도 향상  (0) 2009.11.04
Windows 7의 바탕화면 슬라이드쇼  (0) 2009.10.30
DriverEntry 함수  (0) 2009.09.11
Virtual PC를 이용한 Kernel debugging  (0) 2009.09.09
Device Stack와 Driver loading 순서  (0) 2009.09.07
Posted by noenemy
,

DriverEntry 함수

Programming 2009. 9. 11. 19:03

C 언어를 이용해서 프로그램을 개발해 본 분이 있다면 main() 함수가 entry function의 역할을 한다는 것을 알고 있을 것입니다. 작성한 응용 프로그램이 실행된 후에 처음으로 불려지는 함수가 됩니다. 실제로는 C runtime library가 먼저 초기화 작업을 한 후에 개발자가 작성한 main() 함수를 호출하는 형태입니다. 아무튼 개발자가 작성하는 코드 중에서는 가장 먼저 실행되는 현관문과 같은 역할을 수행합니다.

이와 유사하게 Windows 응용 프로그램을 위해서는 WinMain()이라는 함수를 구현하도록 되어 있고, 다른 프로세스에 의해서 실행 시점에 로딩되어 사용되는 DLL 파일의 경우에는 DllMain()이라는 entry function을 구현하도록 되어 있습니다.

동일한 개념으로 device driver를 개발할 때 entry function이 되는 것이 바로 DriverEntry() 라는 함수입니다. 이 함수는 작성한 driver file이 메모리에 로드 되는 시점에 system thread(I/O manager)에 의해서 불려지는 함수입니다. 따라서 작성된 driver에서 초기화 작업을 진행할 것이 있으면 이 함수 내에서 구현하는 것이 좋습니다. 주의할 것은 DriverEntry() 함수는 driver module이 메모리에 로드 될 때 한 번만 호출 되는 것으로 해당 driver file이 이미 로드 되어 있는 상황에서 device를 새로 추가했다고 해서 다시 호출되는 것이 아닙니다. 이 점을 고려해서 DriverEntry() 함수에 포함되어야 할 코드인지를 주의하셔서 초기화 작업을 진행하시기 바랍니다.

다음은 MSDN 사이트에서 확인한 DriverEntry 함수에 대한 내용입니다. (http://msdn.microsoft.com/en-us/library/ms795702.aspx 참고)

DRIVER_INITIALIZE DriverEntry;
NTSTATUS
DriverEntry(
    __in struct _DRIVER_OBJECT  *DriverObject,
    __in PUNICODE_STRING  RegistryPath
    )
  {...}

DriverObject
         
Caller-supplied pointer to a DRIVER_OBJECT structure. This is the driver's driver object.

RegistryPath
Pointer to a counted Unicode string specifying the path to the driver's registry key.

DriverEntry가 호출 될 때 전달되는 첫 번째 인자로 I/O manager가 생성한 Driver Object의 주소를 넘겨받는데, driver 실행에 필요한 기본적인 값들에 대해서 이 구조체의 초기값을 설정해야 줘야 합니다. 예를 들면 IRP_MJ_CREATE, IRP_MJ_CLOSE, IRP_MJ_READ 등에 대한 처리를 어느 함수에서 처리할 것인지를 I/O manager에 알려줘야 해당 요청이 있을 때 적절한 dispatch handler routine이 호출됩니다.

예를 들면 다음과 같은 코드 작성이 가능합니다.

pDriverObject->MajorFunction[IRP_MJ_CREATE] = MyCreateFunction;
pDriverObject->MajorFunction[IRP_MJ_CLOSE] = MyCloseFunction;
pDriverObject->MajorFunction[IRP_MJ_READ] = MyReadFunction;
pDriverObject->MajorFunction[IRP_MJ_POWER] = MyPowerFunction;
pDriverObject->DriverExtension->AddDevice = MyAddDeviceFunction;

그리고 driver 내부에서 사용되는 object나 type, resource 등에 대한 초기화 작업을 진행하면 됩니다. 이 과정에서 동적으로 생성한 메모리가 있으면 적절한 해제 작업을 하고 리턴해야 합니다.

초기화 작업에 완료했으면 리턴 값으로 STATUS_SUCCESS를 리턴 합니다. 만약 어떤 이유로 초기화가 제대로 진행되지 못했다면 http://msdn.microsoft.com/en-us/library/aa489610.aspx 문서를 참고하셔서 관련 내용이 이벤트 로그에 남겨질 수 있도록 적절한 NTSTATUS 값을 리턴하시기 바랍니다. NTSTATUS 값은 http://msdn.microsoft.com/en-us/library/aa489585.aspx에서 확인할 수 있습니다.

다음에는 WDM에서 사용되는 주요 함수 중에서 AddDevice 함수에 대해서 간단히 정리해보도록 하겠습니다.

'Programming' 카테고리의 다른 글

Windows 7의 바탕화면 슬라이드쇼  (0) 2009.10.30
OS Kernel 함수들의 Prefix  (0) 2009.10.14
Virtual PC를 이용한 Kernel debugging  (0) 2009.09.09
Device Stack와 Driver loading 순서  (0) 2009.09.07
PnP device의 인식 과정  (0) 2009.09.07
Posted by noenemy
,

WinDbg를 이용해서 Kenel debugging을 하려면 디버깅 대상이 되는 Target machine과 이를 디버깅 및 컨트롤 하는 Host machine, 그리고 이 두 컴퓨터를 연결하기 위한 COM, IEEE1394, USB 등의 케이블이 필요합니다. Target machine에서 발생하는 문제점을 파악하기 위해서는 이렇게 물리적으로 분리된 컴퓨터를 이용해서 Kernel debugging을 하는 것이 좋습니다만, 이렇게 PC를 2대 마련하는 것이 쉽지 않습니다.

만약 분석하려는 문제점이 물리적인 장비가 아니라 Virtual PC에서도 재현이 되는 것이라면 간단히 하나의 PC에서 Virtual PC를 이용해서 Target machine을 가상으로 실행해서 Kernel debugging 하는 것이 가능합니다. (다만, Virtual PC나 VMWare와 같은 가상 머신에서 하드웨어 관련 이슈는 확인이 불가능한 경우가 많습니다.)

오늘은 Virtual PC를 이용해서 Kernel debugging 하는 방법을 알아보도록 하겠습니다.

1. Virtual PC 2007 설치하기
http://www.microsoft.com/downloads/details.aspx?FamilyId=04D26402-3199-48A3-AFA2-2DC0B40A73B6&displaylang=en

2. Virtual PC 2007에 Target OS 설치하기
본 문서에서는 Windows XP를 Virtual PC에 설치한 것으로 가정합니다. 만약 기존에 만들어진 VPC 이미지가 있다면 해당 파일을 복사한 뒤에 등록해서 사용하면 됩니다.

3. 디버깅하려는 Target OS를 선택하고 ‘Settings’ 클릭. (단, 해당 VPC는 반드시 Power off 상태여야 합니다.)

image

4. ‘COM1’은 선택하고 'Named pipe’에 “\\.\pipe\debug”라고 입력합니다. 예로서 debug라는 이름을 사용했습니다만 이름은 임의로 정하시면 됩니다. 나중에 Host Machine의 WinDbg에서 여기서 지정한 이름으로 Named pipe로 연결을 시도할 것입니다.

image

5. ‘OK’를 클릭합니다.

6. Target VPC를 부팅한 후

- Windows XP 일 경우 : c:\boot.ini 파일을 열고 다음과 같이 /debug 옵션을 추가합니다.

multi(0)disk(0)rdisk(0)partition(1)\WINDOWS="Microsoft Windows XP Professional" /debug /debugport=COM1 /baudrate=115200 /fastdetect /NoExecute=OptIn

- Windows Vista 이상일 경우 : 관리자 권한으로 prompt 창을 열고 다음 명령을 수행한다.

bcdedit –debug on
bcdedit –dbgsettings serial debugport:1 baudrate:115200

이로써 Target machine으로 사용될 VPC 이미지에 대한 setting은 완료되었습니다.

이제 Host machine에서 WinDbg에서 kernel debugging을 위해 설정하는 방법을 알아보도록 하겠습니다.

1. WinDbg를 실행합니다.

2. File 메뉴에서 ‘Kernel Debugging…’를 선택하고, Port:에 “\\.\pipe\debug”라고 입력합니다.

image

3. ‘확인’을 누르면 다음과 같이 WinDbg는 지정한 named pipe를 이용해서 Target machine과 연결될 수 있도록 대기합니다.

image

4. VPC를 재부팅 한 후 부팅 옵션에서 ‘debug enabled’ 항목을 선택합니다.

image

5. WinDbg 화면에서 다음과 같이 Kernel debugger가 연결되었음을 확인할 수 있습니다.

image

이렇게 Target machine이 연결되었으면 Vitual PC를 이용한 Kernel debugging을 할 수 있도록 환경이 설정 되었음을 의미합니다.

'Programming' 카테고리의 다른 글

OS Kernel 함수들의 Prefix  (0) 2009.10.14
DriverEntry 함수  (0) 2009.09.11
Device Stack와 Driver loading 순서  (0) 2009.09.07
PnP device의 인식 과정  (0) 2009.09.07
Wow64 - Registry Redirection  (0) 2009.09.01
Posted by noenemy
,

지난 번에 설명한 바 있지만 device driver에는 여러 종류가 있습니다. 이를 크게 분류하면 다음과 같습니다.

  • Bus driver - 특정 bus에 attach된 device들을 enumeration 하고 이를 관리
  • Function driver - 특정 device의 동작에 대한 기능을 제공
  • Filter driver – Bus 또는 Function driver의 상/하위에 위치하여 기능을 확장하거나 동작을 모니터링 하는 데 사용

그리고 각 device에 대해서 운영체제는 DEVICE_OBJECT라는 구조체 형태로 이를 관리합니다. driver의 type에 따라서 다음과 같이 각각 다른 device object로 이를 표현합니다.

  • PDO – Physical Device Object.  Bus driver가 사용하는 device object
  • FDO – Function Device Object. Function driver가 사용하는 device object
  • FiDO – Filter Device Object. Filter driver가 사용하는 device object

예를 들어 device 들은 다음과 같은 layer를 구성하여 존재하게 됩니다.

FiDO   FiDO   FiDO
  ↑        ↑        ↑
FDO    FDO    FDO
  ↑        ↑        ↑
FiDO   FiDO   FiDO
  ↑        ↑        ↑
PDO    PDO    PDO
     ↑     ↑     ↑
          FiDO
            ↑
          FDO
            ↑
          FiDO
            ↑
          PDO

그렇다면 이렇게 layer를 구성하는 device driver들은 어떠한 순서로 loading이 될까요? 여기서 driver의 loading이란 가상 메모리에 drvier file의 image가 맵핑 되는 것을 의미합니다. 이는 시스템에 의해서 각 driver의 AddDevice가 호출되는 순서와 관련이 있습니다. 시스템이 AddService를 호출하는 순서는 다음과 같습니다.

Device Lower Filters

–> Class lower filters

–> Function drivers

–> Device Upper Filters

–> Class Upper Filters

각 AddDevice 함수들은 DEVICE_OBJECT 구조체를 만들고 해당 PDO의 root stack에 링크 시킵니다. 즉, 실제 메모리에 로딩되는 순서보다는 AddDevice가 호출되면서 PDO 내에 stack에 링크되는 순서에 의해서 device stack이 구성되도록 구현되어 있습니다.

'Programming' 카테고리의 다른 글

DriverEntry 함수  (0) 2009.09.11
Virtual PC를 이용한 Kernel debugging  (0) 2009.09.09
PnP device의 인식 과정  (0) 2009.09.07
Wow64 - Registry Redirection  (0) 2009.09.01
Wow64에 대해서  (0) 2009.09.01
Posted by noenemy
,

Pnp(Plug and Play)를 지원하는 device는 내가 어떠한 device인지를 bus 측에 전달하도록 구성되어 있습니다. 운영체제에서는 이를 이용해서 새로 시스템에 부착된 device가 있는지 확인하고 새로 추가된 device id에 적절한 driver가 있는지 찾고 설치할 수 있도록 합니다.

PnP device가 있기 이전에는 먼저 하드웨어를 시스템에 부착한 뒤에 항상 해당 device에 대한 드라이버 및 필요한 모듈을 직접 설치했습니다. PnP 기술의 도입으로 부착된 device에 대한 identification이 가능해졌고 이를 통해서 해당 device에 적절한 driver 파일을 이미 가지고 있으면 이를 이용하고 아니면 웹 상에서 드라이버 파일을 찾거나 하는 기능도 가능해졌습니다. 즉, 사용자 입장에서는 device을 설치하는 과정이 보다 쉬워지게 되었습니다.

특히 최근에는 대부분의 device들이 USB 형태로 제작되는데 이를 통해서 대용량의 데이터를 빠르게 전송할 수 있기도 하고, USB 자체적으로 hot plugin/out을 지원하므로 운영체제의 재부팅 없이 언제든 device를 추가하고 제거 가능합니다.

그렇다면 이러한 PnP device는 어떠한 과정을 거쳐서 새로운 device가 추가되었음을 확인하고 관련 설치작업을 진행하게 되는 것일까요? 오늘은 이러한 절차에 대해서 알아보도록 하겠습니다.

다음은 전반적인 PnP device의 설치 과정을 도식화한 것입니다.

ms791103.instdev(en-us,MSDN.10).png

1. 사용자가 새로운 device를 시스템에 추가합니다.

2. 추가된 device가 있음을 해당 bus에서 인식하고, bus driver는 bus로부터 새로운 장치가 추가되었다는 hot-plug notification을 받습니다. bus driver는 이러한 사실을 PnP Manager에게 IoInvalidateDeviceRelations API를 호출함으로써 알립니다.

PnP manager는 IRP_MJ_PNP/IRP_MN_QERY_DEVICE_RELATIONS IRP를 해당 bus에 요청해서 새로운 PDO list를 얻습니다.

3. Kernel mode PnP manager는 User mode PnP manager에게 새로운 device가 추가되었음을 알립니다. User mode PnP manager는 설치 과정을 진행합니다.

4. User mode PnP manager는 device 설치를 위해서 newdev.dll을 rundll32.exe를 이용해서 새로운 프로세스를 생성합니다.

5. 생성된 프로세스에서 SetupAPI와 CfgMgr API를 호출함으로써 해당 장치에 대해 설치 가능한 driver 리스트를 만듭니다. 만약 해당 device에 대해서 사용 가능한 driver가 있으면 이를 이용하고, 없으면 device manager 상에서 unknown으로 표시합니다.

6. Class installer와 co-installers가 DIF 요청을 처리하게 설치 과정에 참여할 수 있습니다. 설치에 해당 device의 INF 파일의 Version 섹션에서 제공하는 Class와 ClassGUID를 이용해서 어떠한 device setup class를 사용할 지를 결정합니다.

7. 드라이버를 로드하고 해당 device를 start 시키기 위해 다시 제어를 kernel mode PnP manager로 전달합니다.

8. Kenrel mode PnP manager는 해당 function driver 및 기타 필요한 filter driver를 로드하고, 각 driver에 대해서 AddDevice routine을 호출합니다. 필요한 경우 IRP_MN_START_DEVICE를 device driver에 전달해서 해당 device를 start 시킵니다.

9. (optional)DIF_NEWDEVICEWIZARD_FINISHINSTALL 요청을 보내서 Install 완료 페이지를 보여주는 등의 작업을 진행할 수 있습니다.

10. 모든 작업이 완료되었으면 DIF_FINISHINSTALL_ACTION을 보냅니다.

*참고문헌

'Programming' 카테고리의 다른 글

Virtual PC를 이용한 Kernel debugging  (0) 2009.09.09
Device Stack와 Driver loading 순서  (0) 2009.09.07
Wow64 - Registry Redirection  (0) 2009.09.01
Wow64에 대해서  (0) 2009.09.01
Symbol file에 대해서  (0) 2009.08.05
Posted by noenemy
,

앞서 64bit 플랫폼에서 32bit용으로 개발된 기존 어플리케이션이 실행될 수 있도록 해주는 Wow64에 대해서 알아본 바 있습니다. 이번에는 그 세부 내용으로 Registry Redirection에 대해서 알아 보도록 하겠습니다.

‘Redirection’이란 용어는 다른 루트로 자동으로 이동시킨다는 의미입니다. 즉, Wow64를 이용해서 32bit 응용 프로그램이 시스템 레지스트리의 특정 경로에 접근하려고 할 때 64bit 응용 프로그램이 사용되는 공간을 함께 사용하는 것이 아니라 별도의 레지스트리를 사용할 수 있게끔 자동으로 redirection 하는 기능을 말합니다. 이로써 32bit 응용 프로그램에 의해서 64bit 플랫폼에서 사용되는 레지스트리의 값이 임의로 변경되는 것을 막을 수 있기 때문에 전체적으로는 시스템 안정성을 높일 수 있게 됩니다.

리디렉션이 되는 경로는 자동으로 Wow6432Node라는 키 하위로 맵핑 됩니다. 예를 들어 HKEY_LOCAL_MACHINE\SoftwareHKEY_LOCAL_MACHINE\Software\Wow6432Node로 변경되어 사용됩니다. 이러한 행위는 시스템 내부적으로 자동으로 이뤄지는 것이며 이러한 맵핑 경로는 향후 새로운 운영체제나 플랫폼에서 변경될 수도 있기 때문에 응용 프로그램에서 Wow6432Node로 직접 접근해서는 안됩니다.

이러한 Redirection이 모든 레지스트리 키에 대해서 발생하는 것은 아닙니다. 플랫폼에 종속적인 키들은 redirection 되어 물리적으로 독립적인 저장공간을 가지게 되지만, 그 외에 32bit 응용 프로그램과 64bit 응용 프로그램 및 플랫폼이 공유할 수 있는 키는 동일한 저장공간을 그대로 이용하게 됩니다.

예를 들어 HKEY_LOCAL_MACHINE\Software는 Redirection이 발생하지만, HKEY_LOCAL_MACHINE\Software\Classes는 Sharing되어 사용됩니다. 어떠한 항목이 Redirection 또는 Sharing 되는 지에 대해서는 아래 문서에 정리되어 있으므로 참고하시기 바랍니다.

Registry Keys Affected by WOW64
http://msdn.microsoft.com/en-us/library/aa384253(VS.85).aspx

그 외에 Registry Redirection에 대한 전반적인 내용 및 각 항목에 대한 세부 내용을 확인하려면 아래 문서를 참고하시기 바랍니다.

Registry Redirector
http://msdn.microsoft.com/en-us/library/aa384232(VS.85).aspx

'Programming' 카테고리의 다른 글

Device Stack와 Driver loading 순서  (0) 2009.09.07
PnP device의 인식 과정  (0) 2009.09.07
Wow64에 대해서  (0) 2009.09.01
Symbol file에 대해서  (0) 2009.08.05
JavaScript로 클래스 구현하기  (0) 2009.06.16
Posted by noenemy
,

Wow64에 대해서

Programming 2009. 9. 1. 16:08

예전에는 서버용 운영체제에서만 사용되던 64bit 컴퓨팅 환경이 최근에는 CPU 기술의 발달로 개인용 컴퓨터에도 보편화된 추세입니다. 32bit 환경에서는 Intel사가 독보적인 존재였지만, 64bit 환경에서는 AMD가 저렴한 가격에 Intel보다 빨리 개인 사용자용 64bit CPU를 상용화해서 출시함에 따라 시장에서 보다 우위를 차지하고 있습니다.

흔히 32bit 운영체제를 Intel CPU의 이름을 따라 ‘x86’ 기반의 플랫폼이라고 합니다. Intel이 먼저 제품을 출시하였고 이에 따라 후발업체인 AMD는 x86에 호환되도록 CPU를 개발하게 되었습니다. 64bit의 경우 AMD가 먼저 상용화하여 제품을 출시하였는데 이를 ‘x64’라고 흔히 얘기합니다. 그리고 이후에 출시된 Intel의 64bit 제품군은 ‘IA64’라는 이름으로 부릅니다.

32bit 플랫폼에서는 프로세스의 가상메모리로 4G의 영역을 사용할 수 있었습니다. 이는 32bit 플랫폼에서 주소를 가리키는 포인터가 4 byte(32bit)로 표현되기 때문에 표현가능한 주소의 범위가 0~2^32이기 때문입니다. 64bit 플랫폼에서는 포인터의 크기가 8byte로 늘어났기 때문에 프로세스별로 사용가능한 가상메모리의 크기가 크게 늘어났습니다.

하지만 새로운 운영체제를 개발할 때마다 그럼 기존에 개발된 어플리케이션에 대한 하위 호환성도 제공하느냐는 것이 이슈가 됩니다. 사용자들이 업무용이나 개인용으로 사용하고 있던 어플리케이션이 새로운 플랫폼에서 정상 동작하지 않는다면 아무리 새로운 플랫폼이 기술적으로 뛰어나다고 할지라도 새로운 플랫폼으로 이동하는데 장벽이 되기 때문입니다.

64bit 플랫폼에서도 기존에 32bit 용으로 개발된 어플리케이션이 실행될 수 있도록 하는 기능을 제공하고 있는데 이를 ‘Wow64’라고 합니다. Wow는 ‘Windows on Windows’를 의미하는 것으로 실제 64bit Windows 상에서 또 다른 32bit Windows가 실행된다는 의미로 일종의 가상 머신(virtual machine) 환경을 제공한다고 이해하시면 됩니다.

image

[그림] Wow64 아키텍쳐

앞서 이미 설명한 바 있지만 기존에 32bit용으로 개발된 어플리케이션을 64bit 플랫폼에서 실행하는데 있어서 가장 큰 문제가 되는 것은 서로 다른 가상주소 체계를 가지고 있다는 것입니다. 기존에 개발된 어플리케이션은 64bit 플랫폼에서 실행되더라도 여전히 4GB의 가상주소 체계를 사용하고 있습니다. 따라서 기존 어플리케이션의 실행을 위해서 32bit 플랫폼을 시뮬레이션 해주는 중간 계층이 필요하게 됩니다. 위 그림에서 우측 점선으로 표시된 박스가 바로 이 역할을 담당합니다.

32bit 어플리케이션에서 user mode 영역에서 가장 하위에 위치한 ntdll.dll은 원래 kernel mode의 시스템 서비스를 호출하기 위한 stub 코드를 제공합니다만, Wow64의 경우에는 32bit ntdll.dll이 kernel code를 실행하기 이전에 이를 Wow64.dll에서 가로채고 64bit 환경에 맞는 주소체계와 적절한 CPU 명령어로 이를 변환한 후에 64bit ntdll.dll로 전달합니다. 이후의 과정은 64bit kernel code에 의해서 실행되고, 이를 다시 어플리케이션으로 반환하기 위해서는 Wow64 모듈에 의해서 다시 32bit 주소체계로 변환되어 전달되는 식으로 동작합니다.

다만 Wow64가 동작하는 데에는 다음과 같은 제약사항이 있습니다.

  • user mode 영역은 기본으로 2GB, /LARGEADDRESSAWARE가 사용된 경우에는 4GB 까지만 사용 가능합니다.
  • 32bit 응용프로그램은 64bit DLL을 로딩할 수 없습니다. (Wow64 관련 모듈은 제외)
    16bit 응용프로그램은 지원하지 않습니다.
  • Virtual DOS Machine(VDM) API는 사용할 수 없습니다.
  • AWE(Address Windowing Extension), scatter/gather I/O, write-tracking 처럼 page size에 종속적인 API들은 Intel Itanium 계열에서 사용할 수 없습니다.
  • PAE(Physical Address Extension) API는 Intel Itanium 계열에서 사용할 수 없습니다.
    DirectX 하드웨어 가속 API들은 Intel Itanium 계열에서 사용할 수 없습니다.

'Programming' 카테고리의 다른 글

PnP device의 인식 과정  (0) 2009.09.07
Wow64 - Registry Redirection  (0) 2009.09.01
Symbol file에 대해서  (0) 2009.08.05
JavaScript로 클래스 구현하기  (0) 2009.06.16
Windows Sidebar Gadget 만들기 #3 - Settings  (0) 2009.06.01
Posted by noenemy
,

디버깅을 할 때 가장 우선적으로 필요한 것이 디버깅 대상이 되는 모듈들의 심볼 파일입니다. 리버싱이나 어셈블리에 익숙하다면 굳이 심볼이 없어도 물론 분석이 가능하겠지만 많은 노력과 시간을 필요로 하겠죠. 오늘은 심볼 파일에 대해서 잠깐 살펴 볼까 합니다.

프로그램을 개발한 후의 그 결과물은 대부분 실행파일(.exe, .dll)이나 드라이버 파일(.sys)입니다. 저희가 작성한 로직들이 컴파일과 링크 과정을 거쳐서 이진수로 구성된 binary file 형태가 됩니다. 모듈을 빌드할 때 디버깅에 필요한 정보를 담은 파일을 생성하면 개발시나 또는 개발이 완료되어 릴리즈된 이후에도 문제가 발생했을 때 이를 분석하는데 도움이 됩니다. 빌드시 생성되는 .pdb 또는 .dbg 확장자를 가진 파일들 심볼 파일(symbol file)이라고 부릅니다.

심볼 파일에는 다음과 같은 내용 들이 저장됩니다.

- 전역변수의 이름과 주소
- 함수명과 함수의 시작주소
- FPO(Frame pointer omission) 데이터
- 지역 변수의 이름과 위치
- 이진코드에 해당하는 소스 파일의 경로와 라인 수
- 변수나 구조체의 타입 정보 등

위와 같은 내용은 프로그램의 실행에는 필요 없는 부분이기 때문에 실행 파일에는 포함되어 있지 않습니다. 하지만 저희가 작성한 소스 코드가 컴파일된 이후에 실행파일에는 이해할 수 없는 이진 코드 형태로만 저장됩니다. 디버깅 시에는 이진 코드의 특정 영역이 어느 함수인지, 어느 변수인지, 소스 코드의 어느 라인에 해당하는지에 대한 정보가 필요하게 되는데 심볼 파일이 그 역할을 담당합니다.

Public symbol과 Private symbol

심볼 파일 내에 저장되는 정보의 종류에 따라 public symbol과 private symbol로 구분할 수 있습니다. public symbol에는 전역 변수, 함수명과 시작 주소, FPO 정보 등이 포함됩니다. Private symbole에는 public symbol의 정보 외에도 지역 변수, 소스 경로, 소스 라인번호, 변수나 구조체의 선언도 포함됩니다.

주로 public symbol은 작성한 모듈을 외부에 SDK 형태로 배포할 때 외부 업체에게 문제 분석에 도움이 될 수 있도록 제공됩니다. 대신 private symbol에는 내부 코드에 대한 세부 사항이 모두 포함되어 있으므로 자사의 코드 디버깅 용으로 사용하고 대부분 외부로는 공개하지 않습니다.

심볼 파일의 관리

프로젝트를 진행하는 개발과정에서 그리고 제품이 릴리즈 된 이후에도 소스 코드는 계속해서 변경이 됩니다. 따라서 하나의 파일에 대해서 여러 가지 버전이 존재하게 됩니다. 특정 문제를 분석하기 위해서는 어느 버전의 파일에서 문제가 발생했는지를 알아야 하고 해당 버전에 대한 심볼 파일과 소스 코드가 있어야 정확한 분석이 가능합니다.

따라서 프로그램을 작성할 때에는 빌드된 모듈의 버전 관리와 이에 해당하는 심볼 파일, 소스 코드에 대한 버전 관리가 잘 되어야 제품에 대한 기술 지원이 가능해집니다. 이러한 버전 관리에 대한 개발 인프라를 잘 구축하고, 또한 모듈을 버전별로 심볼 파일을 모아둔 심볼서버를 구성하면 문제 발생시 보다 신속하고 정확한 유지 보수가 가능해집니다.

'Programming' 카테고리의 다른 글

Wow64 - Registry Redirection  (0) 2009.09.01
Wow64에 대해서  (0) 2009.09.01
JavaScript로 클래스 구현하기  (0) 2009.06.16
Windows Sidebar Gadget 만들기 #3 - Settings  (0) 2009.06.01
주요 System Process들  (0) 2009.05.28
Posted by noenemy
,

안녕하세요. 최근에 Windows Sidebar Gadget 관련해서 테스트 프로그램을 작성하느라 오랜만에 웹 프로그래밍을 해보게 되었습니다. 일반적으로 웹 프로그래밍이 클라이언트 측에서는 서버에서 보내온 HTML 코드를 사용자에게 보여주는 역할을 하기 때문에 많은 기능이 서버측 코드에 의해서 작성 됩니다. 대부분의 비지니스 로직이 서버상의 ASP나 JSP, PHP, ASP.NET 등의 서버 코드에 의해서 동작하게 됩니다.

또한 클라이언트에서 웹 서버 사이에 요청과 응답이라는 단계가 완료되면 연결이 끊어지고 다른 작업이 필요하면 다시 서버에게 어떤 작업을 요청하게 됩니다. 이러한 connectless라는 특성때문에 웹 프로그래밍 환경은 많은 제약사항을 가지고 있었는데 AJAX라는 기술이 발달하면서 클라이언트 기반 프로그래밍으로 웹 개발 환경이 많이 바뀌었습니다.

즉, 클라이언트 측에서 구현할 수 있는 기능들이 다양해졌다는 의미입니다. 이러한 개발 환경의 변화의 중심에는 JavaScript가 있다고 생각합니다. Gadget의 경우 클라이언트에서 대부분의 코드를 가지고 실행되기 때문에 기존의 웹 프로그래밍과는 달리 서버에 의존적이 아닙니다. Gadget을 구현하는 과정의 반 이상이 JavaScript를 이용해서 로직을 구현하는 것이라고 해도 과언이 아닐 정도로 중요한 부분을 차지합니다.

그러던 중에 우연히 아래와 같은 MSDN 매거진 아티클을 발견하게 되었는데, 재밌는 내용이 많이 있어서 소개하고자 합니다.

개체지향기술을 이용한 고급 웹 프로그램 만들기
http://msdn.microsoft.com/ko-kr/magazine/cc163419.aspx

위 문서에 보면 JavaScript를 이용해서 클래스를 만들고, 상속을 구현하고, 네임스페이스를 구현하는 등 OOP 적인 프로그램을 개발하는 방법에 대해서 자세히 설명하고 있습니다. 시간 되실 때 꼭 한 번 읽어보시기 바랍니다.

또 서론이 길어졌습니다. 자. 간단히 JavaScript를 이용해서 클래스를 구현하는 방법을 알아보도록 하지요.

name, age, year라는 속성과 Show라는 메쏘드를 가지는 Student라는 클래스를 만드는 방법은 다음과 같습니다.

별도의 클래스 선언 과정없이 함수를 정의하듯이 다음과 같이 코드를 작성하면 됩니다. 이는 일반적인 OOP 언어에서 클래스의 생성자(constructor) 함수와 같은 모습을 하고 있습니다. 멤버 변수의 선언도 별도로 필요없고 클래스의 내부 범위에 속한다는 것을 명시하기 위해 this 객체를 이용해서 this.name과 같은 형식으로 표현하면 됩니다.

function Student(name, age, year)
{
    // member variables
    this.name = name;
    this.age = age;
    this.year = year;

    // member methods
    this.Show = Show;
}

마지막 줄에 Show는 멤버 메쏘드 구현의 예를 들기 위한 것인데, 아래와 같이 같은 이름으로 함수를 구현함으로써 멤버 함수를 간단히 구현할 수 있습니다. 아래 Show 함수의 구현부에는 Student 클래스의 멤버 함수라는 것은 명시적으로 드러나 있지 않지만 this 객체를 이용해서 내부 멤버 변수인 name, age, year의 값에 접근하는 것을 확인할 수 있습니다.

function Show()
{
    var strMsg = "Name:"+ this.name;
    strMsg += ", Age:" + this.age;
    strMsg += ", Year:" + this.year;
   
    alert(strMsg);
}

이렇게 만들어진 Student 클래스로 실제로 인스턴스를 만들어서 사용하는 방법을 알아보겠습니다. 이 또한 간단히 다음과 같이 작성할 수 있습니다.

var student1 = new Student("David", 22, 3);
student1.Show();

var student2 = new Student("Michael", 21, 2);
student2.Show();

student1, student2 객체를 2개 만들 때 각 인스턴스에 대한 속성 값을 지정하고 Show() 메쏘드를 호출하고 있습니다.

다음은 위 내용을 테스트하기 위한 예제 html 페이지의 전체 내용입니다.

<html>
<head>
<script language="javascript">
function Student(name, age, year)
{
    this.name = name;
    this.age = age;
    this.year = year;
   
    this.Show = Show;
}

function Show()
{
    var strMsg = "Name:"+ this.name;
    strMsg += ", Age:" + this.age;
    strMsg += ", Year:" + this.year;
   
    alert(strMsg);
}

function
</script>
</head>
<body>
Using Javascript as an OOP language.

<script language="javascript">
var student1 = new Student("David", 22, 3);
student1.Show();

var student2 = new Student("Michael", 21, 2);
student2.Show();
</script>

</body>
</html>

'Programming' 카테고리의 다른 글

Wow64에 대해서  (0) 2009.09.01
Symbol file에 대해서  (0) 2009.08.05
Windows Sidebar Gadget 만들기 #3 - Settings  (0) 2009.06.01
주요 System Process들  (0) 2009.05.28
Windows Sidebar Gadget 만들기 #2 - Flyout  (0) 2009.05.28
Posted by noenemy
,

지난 시간에는 Gadget의 Flyout 기능을 구현하고, Gadget과 Flyout 간에 서로 연동하는 방법에 대해서 간단히 살펴 보았습니다. 이번에는 Gadget의 실행에 필요한 환경 설정 기능을 구현할 수 있는 Settings 기능을 구현하는 방법을 알아보도록 하겠습니다.

전체적으로 지난 회에 작성한 예제를 그대로 이용하되 Hello.html 페이지에 보여지는 내용을 좌에서 우로 글자가 흐르는 전광판 효과 기능을 추가로 구현하고자 합니다. 그리고 Settings 페이지 기능을 담당할 Settings.html 페이지를 추가로 제작할 것입니다. Flyout.html 페이지는 기존 예제의 것을 그대로 이용합니다. 관련 내용을 확인하려면 Windows Sidebar Gadget 만들기 #2 – Flyout 문서를 참고하시기 바랍니다.

우선 Settings에 사용되는 여러 설정 정보들을 저장하고 저장된 값을 불러오는 기능이 필요합니다. 설정 값을 저장하기 위해서 System.Gadget.Settings.write 또는 System.Gadget.Settings.writeString 함수를 사용하면 되고, 저장된 값을 불러오기 위해서는 System.Gadget.Settings.read 또는 System.Gadget.Setting.readString 함수를 이용하면 됩니다. 보다 자세한 내용은 아래 MSDN 문서를 참고하시기 바랍니다.

System.Gadget.Settings Object
http://msdn.microsoft.com/en-us/library/ms723661(VS.85).aspx

자. 이제 실제 예제를 통해서 구현하는 방법을 알아보도록 하겠습니다. Gadget의 주 기능을 담당하는 Hello.html 페이지를 다음과 같이 작성하였습니다.

전체 구조는 지난 번에 작성한 코드와 동일합니다. 페이지 초기화를 담당하는 Init() 함수, 전광판 효과로 문장을 화면에 출력하는 DisplayMessage() 함수, Settings 창이 닫혔을 때 이에 대한 이벤트를 처리하는 SettingsClosed() 함수가 추가로 작성되었습니다.

Hello.html

<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <style>
    body
    {
        width:130;
        background-color:Aqua; 
        font-size:9pt;
    }
    </style>

    <script language="javascript">

    // settingsUI로 사용할 페이지 등록
    System.Gadget.settingsUI = "Settings.html";

    // settings 윈도우가 close 될 때의 이벤트 처리를 담당할 핸들러
    System.Gadget.onSettingsClosed = SettingsClosed;

    var nStart, nEnd, strMessage;
    var nMaxLength = 12; // 전광판 화면에 보여지는 글자수

   // 초기화 수행 함수
   function Init()
   {
        var strData = System.Gadget.Settings.readString("Message");  
        if (String(strData) != "")
        {
            strMessage = strData;
        }
        else
        {
            strMessage = "Hello, Gadget!! You can set some options for this gadget..."; // default string
            System.Gadget.Settings.writeString("Message", strMessage);
        }
        nStart = 0;
        nEnd = 0;
        // 250 밀리초 단위로 DisplayMessage() 함수를 호출해서 전광판 출력
        setInterval("DisplayMessage()",250);
   }

   // 전광판 효과로 문자열을 출력하는 함수
   function DisplayMessage()
   {
        var strSpace = "";
        // 보여질 문자열의 처음과 끝 인덱스 계산
        if (nEnd < strMessage.length)
            nEnd++;
        if (nEnd - nStart > nMaxLength || nEnd == strMessage.length)
                nStart++;

        // 보여질 문자가 모자라는 경우 앞을 공백으로 채움
        if (nStart == 0)
        {
            for (var i=0; i < nMaxLength - nEnd; i++)
                strSpace += " ";
        }
       // 메시지 출력    
       System.Gadget.document.getElementById("strMessage").innerText = strSpace + strMessage.substring(nStart, nEnd);
       // 문장 끝까지 출력되었으면 다시 처음부터 시작
       if (nStart == nEnd)
       {
            nStart = 0;
            nEnd = 0;
        }
   }

   // Setting 창에서 사용자가 OK 버튼을 눌렀으면 전광판을 다시 초기화시킨다.
    function SettingsClosed(event)
    {
        // 사용자가 'OK' 버튼을 눌렀는지 확인
        if (event.closeAction == event.Action.commit)
        {
            Init();       
        }
    }  

    // Flyout 페이지를 보여주는 함수
    function ShowFlyout()
    {
        System.Gadget.Flyout.file = "flyout.html";

        if (System.Gadget.Flyout.show == true)
       {
            System.Gadget.Flyout.show = false;
            System.Gadget.document.getElementById("strFlyoutCommand").innerText = "Show Flyout";    
       }
       else
       {
            System.Gadget.Flyout.show = true;
            System.Gadget.document.getElementById("strFlyoutCommand").innerText = "Hide Flyout";          
       }
    }
    </script>   

</head>
<body onload="Init();">
<b><div id="strMessage"></div></b>
<a href="javascript:void(0);" onclick="ShowFlyout();"><div id="strFlyoutCommand">Show Flyout</div></a>
</body>
</html>

우선 script에서 다음 코드는 현재 Gadget의 Settings 페이지로 ‘Settings.html’을 사용하겠다는 것을 의미합니다.

    // settingsUI로 사용할 페이지 등록
    System.Gadget.settingsUI = "Settings.html";

이렇게 settingsUI 속성에 특정 페이지를 지정하면 Gadget에서 Settings 설정을 이용할 수 있도록 활성화됩니다. 다음과 같이 Gadget에서 Settings 화면을 보려면 우측에 위치한 도구 모양의 아이콘을 클릭하거나, Gadget을 오른쪽 마우스 버튼으로 클릭한 뒤에 컨텍스트 메뉴에서 ‘Options’ 항목을 선택하면 됩니다.

image

image

[그림] settingsUI를 실행시키는 방법

Init() 함수는 Gadget 실행을 위한 초기 환경을 설정하는 역할을 담당합니다. 화면에 표시할 문자열 정보가 “Message”라는 이름으로 저장되어 있는지 확인하고 없으면 default 문자열을 저장합니다. 이번 예제에서 Settings 페이지에서는 default 문자열을 사용자가 원하는 문자열로 변경할 수 있도록 해주는 역할을 담당합니다.

script의 상단부에 보면 다음과 같이 onSettingsClosed 속성에 SettingsClosed 함수를 등록하였는데, Settings 창이 오픈 되었다가 close 될 때의 이벤트를 Gadget에서 받아서 처리할 수 있도록 여기에 등록된 함수를 호출해 줍니다.

    // settings 윈도우가 close 될 때의 이벤트 처리를 담당할 핸들러
    System.Gadget.onSettingsClosed = SettingsClosed;

Settings 페이지에서는 간단히 사용자로부터 전광판에 출력하고 싶은 문자열을 입력 받도록 되어 있는데, SettingsClosed() 이벤트 핸들러에서는 단순히 Init() 함수를 재호출함으로써 수정된 내용을 반영하도록 구현되어 있습니다.

DisplayMessage() 함수는 긴 문자열을 우측에서 좌측으로 물 흐르듯이 전광판처럼 출력해주는 기능을 구현합니다. 문장이 길 경우에 좌우 폭이 좁은 Gadget에서는 한 줄에 다 보여줄 수 없는 경우가 있기 때문에 nMaxLength 수만큼의 글자수에 해당하는 문자만 보여지도록 스트링을 처리하는 로직이 구현되어 있습니다. 사실 이 부분은 글자체나 사용하는 언어에 따라서 글자폭이 다를 수 있기 때문에 의도한 것처럼 보여지지 않을 수 있습니다.

자, 이제 Settings 페이지를 구현해보도록 하겠습니다.

Settings.html

<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <style>
    body
    {
        width:200;
        height:100;
        font-size:9pt;
    }
    </style>

    <script language="javascript">

    // Settings 창 close를 처리할 핸들러 등록
    System.Gadget.onSettingsClosing = SettingsClosing;

    // 초기화 함수 - 기존에 저장된 값을 가져와서 textbox에 출력함
    function Init()
    {
        var strMessage = System.Gadget.Settings.readString("Message");
        if (String(strMessage) != "")
        {
            txtMessage.innerText = strMessage;
        }
        txtMessage.select();
    }

    // 'OK' 버튼을 누르면 입력된 메시지를 저장하고 창을 닫는다.
    function SettingsClosing(event)
    {
        // User hit OK on the settings page.
        if (event.closeAction == event.Action.commit)
        {
            System.Gadget.Settings.writeString("Message", txtMessage.value);           
            event.cancel = false;   // 현재 창을 close 시킴
        }
    }

    </script>   
</head>
<body onload="Init();">
            <label for="txtMessage">Modify text:</label><br />
            <input type="text" name="txtMessage" id="txtMessage" title="Enter new text"/>

</body>
</html>

위 코드에서 script의 시작부분에 보면 다음과 같이 Settings 창이 종료될 때의 이벤트를 처리할 함수를 등록하는 부분이 있습니다. 이 예제에서는 SettingsClosing() 함수를 이벤트 핸들러로 등록하였습니다.

   // Settings 창 close를 처리할 핸들러 등록
    System.Gadget.onSettingsClosing = SettingsClosing;

SettingsClosing() 함수의 구현부를 보시면 사용자가 ‘OK(commit)’ 버튼을 클릭한 경우에 사용자가 입력한 텍스트를 “Message”라는 이름으로 저장하는 것을 확인할 수 있습니다. 이렇게 저장된 값은 Hello.html 페이지의 Init 함수에서 다시 읽어서 사용하므로 결과적으로 사용자가 설정한 문자열을 이용해서 전광판에 표시하는 효과를 얻을 수 있도록 구현한 것입니다.

image

[그림] Settings.html의 실행 결과

이로써 지난 시간에 이어 Flyout과 Settings을 구현하는 방법을 간단한 예제를 통해 알아 보았습니다.

'Programming' 카테고리의 다른 글

Symbol file에 대해서  (0) 2009.08.05
JavaScript로 클래스 구현하기  (0) 2009.06.16
주요 System Process들  (0) 2009.05.28
Windows Sidebar Gadget 만들기 #2 - Flyout  (0) 2009.05.28
Windows Sidebar Gadget 만들기 #1  (0) 2009.05.26
Posted by noenemy
,

Windows 운영체제를 구성하는 여러 구성요소들 중에 Windows System과 관련된 Process들이 있습니다. 이들 System Process는 부팅 이후 초기 과정에서 생성되어 사용자가 컴퓨터를 사용하는데 필요한 여러 주요 기능들을 수행합니다.

대부분의 System Process들은 컴퓨터가 시작되어 종료될 때까지 계속 실행되면서 여러 작업들을 수행합니다. 이러한 이유 때문에 일부 악성 프로그램들은 실행되고 있다는 사실을 눈에 띄지 않기 위해서 System process와 동일한 이름이나 유사한 이름을 이용하는 경우가 많습니다. 이 때문에 정상적으로 실행중인 System Process가 바이러스나 웜과 같은 악성 프로그램으로 오해 받는 경우도 있습니다.

이 글에서 알아보고자 하는 System Process 들은 아래 Windows 운영체제의 전체 구조를 도식화한 그림에서 좌측 상단에 위치한 ‘System Support Processes’에 해당합니다.

[그림] Windows Architecture (Windows Internal 4th Ed. p52)

Idle Process
CPU는 일정 시간단위로 계속 clock을 발생시키도록 되어 있는데, 아무런 동작을 하지 않고 있는 상태를 표현하는 프로세스입니다. 실제로 물리적으로 존재하는 실행 파일이 않는 가상의 프로세스입니다. 작업 관리자에서는 ‘System Idle Process’라는 이름을 가지고 있으며, Process ID는 0번을 가집니다.

System Process
이 프로세스는 Kernel에서만 실행되는 system thread들을 호스팅하는 프로세스입니다. 위 그림에서 좌측 중간에 보면 ‘System threads’라고 표시된 구성요소가 있습니다. Windows 운영체제의 시작을 위해서 최초로 생성되는 system thread 뿐만 아니라 Dirty page writer, balance set manager 등 운영체제를 구성하는 여러 executive components 들의 기능 수행을 위해 여러 system thread 들이 존재합니다. Windows XP 이후 운영체제에서는 Process ID 4번 값을 가집니다. Windows 2000에서는 PID 값이 8이었습니다.

Session Manager(SMSS)
이는 부팅 이후 최초로 생성되는 user mode 프로세스입니다. 운영체제 초기화에 필요한 여러 중요한 역할을 담당하며, csrss.exe나 winlogon.exe과 같은 주요 프로세스들을 실행시키는 역할을 담당합니다.

Winlogon
Windows Logon Process로서 사용자로부터 로그온에 필요한 계정 정보를 입력 받고 로그온, 로그오프 기능을 수행하는 프로세스 입니다.로컬 로그온, 네트워크 로그온, 지문인식 로그온 등 다양한 로그온 방법이 존재하는데 이에 따라 로그온 과정에 연동되는 모듈이 더 많아지고 그 과정도 복잡해 집니다. 특히 사용자 로그온 과정에서 UI 부분을 담당하는 모듈을 GINA(graphical Identification and Authentication)이라고 합니다. 우리가 일반적으로 부팅 후 만나게 되는 로그온 화면은 msgina.dll이라는 기본 GINA 모듈에 의해서 보여지는 화면인데, custom GINA 모듈을 개발해서 별도의 로그온 UI나 로직을 가지는 모듈을 개발할 수 있습니다.

LSASS(Local Security Authentication Server Service)
Winlogon 프로세스에 의해서 전달받은 사용자 계정 정보를 이용해서 실제로 사용자 인증을 처리해주는 프로세스입니다. 로그온 과정이 정상적으로 수행되면 해당 사용자에 대한 access token을 리턴합니다.

UserInit
로그온한 사용자를 위한 개인화된 환경을 제공할 수 있도록 초기화 과정을 진행하는 프로세스 입니다. 이 프로세스는 일련의 초기화 과정을 진행하고 그 일을 마친 후에는 종료됩니다. 따라서 사용자가 로그온 한 이후 사용 중일 때에는 이 프로세스를 찾아 볼 수가 없습니다. 로그온 스크립트를 실행하거나 그룹 정책 등을 적용하고 shell 기능을 담당하는 탐색기(explore.exe) 프로세스를 실행시킵니다.

SCM(Service Control Manager)
Services.exe 라는 이미지 파일로 실행되며 시스템에서 실행되는 모든 서비스 프로그램의 실행, 정지, 관리하는 중앙 창구 역할을 수행합니다. 부팅 이후 Winlogon 프로세스에 의해서 SCM이 실행되며 레지스트리에서 시스템에 등록된 서비스 중에서 AutoStart 속성을 가진 서비스들을 실행시키는 역할을 담당합니다.

Posted by noenemy
,

지난 시간에 만들었던 Hello Gadget 예제에 Flyout 기능을 추가해보도록 하겠습니다.

Flyout이란 원래 있는 Gadget 으로부터 숨겨져 있던 페이지가 좌우로 펼쳐지는 것을 의미합니다. 일반적으로 Gadget이 Sidebar에 도킹되어 있고 이 경우에는 화면에 보여지는 크기가 제한적입니다. 따라서 Gadget에는 아주 간단한 요약된 정보만을 보여주게 됩니다.

따라서 이러한 Gadget의 제한된 크기 때문에 충분한 정보를 보여주지 못하는 경우가 발생합니다. 우리가 게시판에서 게시물의 제목을 클릭해서 본문 내용을 확인하는 것과 같은 방법으로 보다 세부적인 내용을 보여줄 필요성이 있습니다. 이러한 경우에 Flyout 페이지를 이용해서 확장된 내용을 사용자에게 보여줄 수 있습니다.

Gadget에서는 System.Gadget.Flyout 객체를 통해서 Flyout을 제어할 수 있습니다. 예를 들어서 다음 코드는 Flyout에 사용할 페이지를 지정하고 이를 보여주도록 하는 코드 입니다. (작성시 element와 property, method의 대소문자를 잘 구분해서 입력해야 합니다.)

<script language=”javascript”>
System.Gadget.Flyout.file = "flyout.html";
System.Gadget.Flyout.show = true;
</script>

위 예제 코드에서 짐작할 수 있듯이 Flyout에 보여지는 내용도 역시 HTML 페이지라는 것을 알 수 있습니다. 여기서는 ‘flyout.html’이라는 파일을 예로 사용했습니다. 즉, 위 코드 내용은 flyout.html 이라는 페이지를 Flyout으로 지정하고 이를 화면에 보여지게 해라라는 의미입니다.

지난번에 작성했던 Hello.html에 Flyout 관련해서 코드를 다음과 같이 추가해봅시다.

Hello.html

<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <style>
    body
    {
        width:130;
        background-color:Aqua; 
        font-size:9pt;
    }
    </style>
    <script language="javascript">
    function ShowFlyout()
    {
        System.Gadget.Flyout.file = "flyout.html";

        if (System.Gadget.Flyout.show == true)
       {
            System.Gadget.Flyout.show = false;
            System.Gadget.document.getElementById("strFlyoutCommand").innerText = "Show Flyout";    
       }
       else
       {
            System.Gadget.Flyout.show = true;
            System.Gadget.document.getElementById("strFlyoutCommand").innerText = "Hide Flyout";          
       }
    }
    </script>
   

</head>
<body>
<b>Hello, Gadget !!</b>
<a href="javascript:void(0);" onclick="ShowFlyout();"><div id="strFlyoutCommand">Show Flyout</div></a>
</body>
</html>

위와 같이 작성할 경우 화면에 보여지는 Hello gadget의 모습은 다음과 같습니다. Gadget의 하단부에 ‘Show Flyout’이라는 링크가 새롭게 추가되었는데, 이를 선택하면 위 코드에서 작성한 ShowFlyout()이라는 javascript 함수를 호출하도록 구현하였습니다.

image

ShowFlyout() 함수에서 주요 내용을 살펴보면 다음과 같습니다.

우선 본 예제에서 Flyout 으로 사용할 페이지 flyout.html이라는 페이지로 지정 하였습니다. 해당 페이지에 대한 내용은 곧 설명 드리도록 하겠습니다.

       System.Gadget.Flyout.file = "flyout.html";

그 다음 코드를 살펴보도록 하겠습니다. Flyout 객체의 show 속성을 참고함으로써 현재 Flyout이 보여지고 있는 상태인지 확인하는 if 구문이 있습니다. 일종의 토글 스위치와 같은 역할을 하고 있는데, Flyout이 보여지고 있는 상태이면 이를 숨기고, 숨겨진 상태이면 이를 보여지게 하는 코드입니다. 그리고 각 상황에 맞게끔 Gadget 페이지에 보여지는 내용을 ‘Show Flyout’ 또는 ‘Hide Flyout’으로 변경하는 코드입니다.

        if (System.Gadget.Flyout.show == true)
       {
            System.Gadget.Flyout.show = false;
            System.Gadget.document.getElementById("strFlyoutCommand").innerText = "Show Flyout";    
       }

자 그럼. 이제 Flyout 페이지의 내용을 작성해보도록 하겠습니다. 다음과 같이 코드를 작성하고 hello.html 페이지가 위치한 동일한 폴더에 flyout.html 이름으로 저장합니다.

Flyout.html

<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <style>
    body
    {
        width:200;
        height:100;
        font-size:9pt;
    }
    </style>

    <script language="javascript">
    function HideFlyout()
    {
        System.Gadget.Flyout.show = false;
        System.Gadget.document.getElementById("strFlyoutCommand").innerText = "Show Flyout";   
    }


    function ChangeColor(strColor)
   {
        System.Gadget.document.body.style.backgroundColor = strColor;
   } 
    </script> 
 
</head>
<body>
Background color : <br />
<a href="javascript:void(0);" onclick="ChangeColor('white')">White</a>
<a href="javascript:void(0);" onclick="ChangeColor('gray')">Gray</a>
<a href="javascript:void(0);" onclick="ChangeColor('yellow')">Yellow</a>
<a href="javascript:void(0);" onclick="ChangeColor('red')">Red</a>
<a href="javascript:void(0);" onclick="ChangeColor('blue')">Blue</a>
<br /><br />
<a href="javascript:void(0);" onclick="HideFlyout();">Close</a>

</body>
</html>

Flyout 을 구현하는데 있어서 가장 궁금한 부분은 Flyout과 Gadget 간의 상호 연동하는 방법에 대한 것일 겁니다. 예를 들어 웹 브라우저에서 parent window와 child window 간에 서로 데이터 교환이나 창을 제어하는 등의 작업이 필요하듯이 Gadget과 Flyout도 서로 밀접하게 사용되는 일이 많을 것이기 때문입니다.

현재 페이지가 Flyout인데 Gadget 객체에 접근하려면 간단히 System.Gadget 객체를 이용하면 됩니다. 그리고 Flyout 페이지에서도 System.Gadget.Flyout 객체를 이용해서 접근해야 한다는 것만 이해하시면 나머지는 HTML DOM 객체를 계층적으로 접근하면 됩니다.

다음은 위 코드 예제에서 Flyout에서 Flyout을 숨기는 방법과 Gadget의 element에 접근해서 내용을 수정하는 예를 보여주고 있습니다.

    function HideFlyout()
    {
        System.Gadget.Flyout.show = false;
        System.Gadget.document.getElementById("strFlyoutCommand").innerText = "Show Flyout";   
    }

그리고 다음 코드는 Flyout 페이지에서 Gadget 페이지의 배경색을 변경하는 코드입니다. System.Gadget.document 로 Gadget의 document element에 접근하고 그 하위에 있는 body.style.backgroundColor 속성에 접근해서 배경색을 지정하는 코드 예입니다.

    function ChangeColor(strColor)
   {
        System.Gadget.document.body.style.backgroundColor = strColor;
   } 

이제 작성된 코드를 실행한 결과를 확인해보겠습니다.

image

[그림] Flyout을 확장한 모습

image

[그림] Flyout 페이지에서 Gadget 페이지의 배경색을 변경한 모습

image

[그림] Flyout이 숨겨진 모습

다음 회에서는 Option 페이지를 추가하는 방법에 대해서 알아보도록 하겠습니다.

'Programming' 카테고리의 다른 글

Windows Sidebar Gadget 만들기 #3 - Settings  (0) 2009.06.01
주요 System Process들  (0) 2009.05.28
Windows Sidebar Gadget 만들기 #1  (0) 2009.05.26
Remote Session인지 확인하는 방법  (0) 2009.05.22
Kernel Mode vs. User Mode  (0) 2009.05.21
Posted by noenemy
,

컴퓨터를 이용해서 영화를 많이 보기 때문인지 와이드 스크린을 가진 디스플레이 장치가 보편화 되고 있습니다. 전통적인 4:3 비율의 화면 크기에 익숙한 사람들은 16:9 비율의 화면을 보면 좌우로 넓어서 뭔가 좀 허전하게 느껴지기도 합니다. 하지만 Windows Vista에서 소개하고 있는 Windows Sidebar를 잘 활용하면 바탕화면을 보다 다양한 정보로 그리고 필요한 정보를 바로 바로 확인할 수 있습니다. 개인적으로 와이드 스크린 디스플레이 장치를 이용할 경우 보다 자연스러운 화면 구성이 가능한 것 같습니다.

다음은 제가 사용중인 노트북의 Windows Vista에서 우측 바탕화면에 자리잡고 있는 Windows Sidebar입니다. 달력이나 시계, 이미지 뷰어와 같은 작은 응용 프로그램들이 동작 중인데 Sidebar에서 실행되는 작은 프로그램을 가젯(Gadget)이라고 합니다. 이러한 Gadget은 실제로 html page와 java script를 이용해서 구현된 일종의 웹 페이지 입니다. 앞으로 연재를 통해서 Windows Sidebar Gadget을 만드는 방법을 간단한 예제를 통해서 알아보도록 하겠습니다.

sidebar

[그림] Windows Sidebar와 실행중인 Gadget들

먼저 그 첫번째 순서로서 ‘Hello, Gadget!’라는 메시지를 보여주는 간단한 샘플을 만들고 이 gadget을 추가하는 방법을 알아보도록 하겠습니다.

Gadget을 저장할 폴더 만들기

Gadget은 특정 사용자에게만 보여질 것인지, 아니면 해당 시스템을 사용하는 모든 사용자에게 보여질 것인지에 따라 저장되는 위치가 결정됩니다. 아래 위치에 가보면 기본적으로 설치된 Gadget 들의 내용을 확인이 가능합니다.

저장 위치 내용
C:\Users\[username]\AppData\Local\Microsoft\Windows Sidebar\Gadgets 해당 사용자에게만 보여지는 Gadget일 경우
C:\Program Files\Windows Sidebar\Gadgets 해당 시스템의 모든 사용자에게 보여지는 Gadget일 경우

각각의 Gadget은 위 저장위치에서 독립적인 폴더명을 가지는데 이때 반드시 ‘name.gadget’이라는 이름으로 저장되어야 합니다.

우선 Hello gadget 샘플을 위해 C:\Users\[username]\AppData\Local\Microsoft\Windows Sidebar\Gadgets\Hello.gadget 이라는 이름의 폴더를 생성하였습니다.

Hello.html 파일 만들기

앞서서 Gadget은 기본적으로 웹 페이지 형식으로 구현된다고 설명 드린바 있습니다. 화면에 간단히 Hello, Gadget이라는 인사문구를 보여주는 Gadget을 만들려고 합니다. 아래와 같이 웹 페이지를 만들고 앞서 만든 폴더에 ‘hello.html’이라는 이름으로 저장합니다.

Hello.html

<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <style>
    body
    {
        width:130;
        background-color:Aqua; 
        font-size:9pt;
    }
    </style>
</head>
<body>
<b>Hello, Gadget !!</b>
</body>
</html>

Gadget XML 정의 파일 만들기

이제 저희가 만든 Hello Gadget을 Windows Sidebar에 어떻게 등록하는지 알아보도록 하겠습니다. Gadget들을 Hosting해서 실행시켜 주는 것은 Sidebar.exe라는 프로세스입니다. 이 Sidebar 프로세스에 새로운 Gadget을 추가하려면 해당 Gadget에 대한 기본적인 정보를 알려줘야 합니다.

작성한 Gadget에 대한 정보를 Sidebar.exe 프로그램이 인식할 수 있도록 XML 파일의 형태로 제작되어야 하는데, 다음과 같이 XML 파일을 작성하고 앞에서 생성한 폴더에 gadget.xml 파일로 저장합니다.

gadget.xml

<?xml version="1.0" encoding="utf-8" ?>
<gadget>
  <name>Hello Gadget</name>
  <namespace>noenemy</namespace>
  <version>1.0.0.0</version>
  <author name="noenemy">
    <info url="noenemy.pe.kr" text="http://noenemy.pe.kr"/>
    <logo src="logo.jpg" />
  </author>
  <copyright>&#169; 2009 noenemy</copyright>
  <description>"Hello Gadget" sample</description>
  <icons>
    <icon height="50" width="50" src="icon.jpg" />
  </icons>
  <hosts>
    <host name="sidebar">
      <base type="HTML" apiVersion="1.0.0" src="Hello.html" />
      <permissions>Full</permissions>
      <platform minPlatformVersion="1.0" />
    </host>
  </hosts>
</gadget>

위 XML 문서 내용을 보면 name, author, icons 등과 같은 여러 속성 정보를 제공하고 있는 것을 알 수 있습니다. 이러한 정보들이 어떻게 사용되는 지는 위 XML 문서 내용과 아래 Gadget 추가 윈도우에서 보이는 정보를 비교해보면 쉽게 이해가 되실 겁니다. 이 XML 파일에 대한 보다 자세한 설명은 아래 문서 내용을 참고하시기 바랍니다.

Gadgets for Windows Sidebar Manifest
http://msdn.microsoft.com/ko-kr/library/aa965879(en-us,VS.85).aspx

자. 이제 만든 Hello gadget을 등록해보겠습니다. Sidebar의 여백을 오른쪽 마우스버튼으로 클릭하고  컨텍스트 메뉴에서 ‘Add Gadgets’를 선택합니다.

image

현재 추가할 수 있든 Gadget의 리스트를 확인할 수 있습니다. 저희가 생성한 Hello Gadget의 정보도 확인 가능합니다. 아래 그림처럼 gadget.xml 파일의 속성 정보가 어떻게 사용되고 있는지 확인할 수 있습니다.

image

다음과 같이 Hello, Gadget!! 이라는 문장을 보여주는 Gadget이 실행됩니다.

image



다음에는 Flyout 기능을 구현하는 방법을 알아보도록 하겠습니다.

'Programming' 카테고리의 다른 글

주요 System Process들  (0) 2009.05.28
Windows Sidebar Gadget 만들기 #2 - Flyout  (0) 2009.05.28
Remote Session인지 확인하는 방법  (0) 2009.05.22
Kernel Mode vs. User Mode  (0) 2009.05.21
LiveKD  (0) 2009.05.15
Posted by noenemy
,

Windows 운영체제는 기존에는 개인 사용자용 PC의 운영체제로 주로 사용 되었으나 Windows NT 4.0 Server의 Option Pack에 IIS가 추가되면서 HTTP나 FTP 서버로서의 역할을 수행하게 되었고, 인터넷 시장의 급성장에 발맞추어 서버 운영체제로서 주목을 받기 시작합니다. 당시 서버 시장의 추축을 이루고 있던 Unix 서버에 비해서 적은 비용으로 쉽게 서버를 구성할 수 있다는 게 큰 장점으로 부각되었습니다. 하지만 서버 가용성 측면에서, 그리고 back office 서비스를 제공해주는 서버 어플리케이션이 아직 부족했던 때라 중소 규모의 사이트에서만 주로 사용되었던 것 같습니다.

시장 상황은 Windows 2000이 출시되면서 조금 변화하게 됩니다. Windows 2000이 Home, Professional, Server, Data Center Server의 라인업을 갖추고 본격적으로 서버용 운영체제로서의 기능을 제대로 갖추기 시작했기 때문입니다.

Windows 2000 Server와 함께 소개된 여러 서버 구성요소 중에서 원격 관리를 위해 소개된 서비스가 바로 Terminal Service 입니다. Server 장비는  일반 PC와 달리 물리적으로 IDC나 전산센터 서버실 같은 원격지에 위치하는 것이 일반적입니다. 따라서 서버의 상태나 서비스를 관리하기 위해서 매번 서버가 위치한 곳으로 이동할 수 없기 때문에 네트워크를 통한 원격 관리 기능이 필수적입니다. 이를 위해서 Windows 2000 Server 제품군에 Terminal Service라는 구성요소가 추가되었고, Terminal Service Client 프로그램을 이용해서 원격에서 로그인하고 서버의 실행화면을 직접 보면서 키보드나 마우스를 통해서 원격지에서 관리가 가능하게 되었습니다.

image

[그림] 원격 장비에 접근하기 위해 사용되는 Remote Desktop

이렇게 기존과 달리 하나의 Windows 운영체제에서 컴퓨터에 직접 연결된 모니터, 키보드, 마우스를 이용해서 접근하고 있는 사용자 외에 원격지에서 접근해서 사용중인 사용자를 구분해서 관리할 필요성이 대두하였고, 이를 ‘Session’ 이라는 용어로 부르기 시작합니다. 그리고 로컬에서 console을 이용해서 접근한 session을 ‘Session 0’ 라고 명하고, 그외에 원격에서 접속한 session을 ‘Remote Session’ 이라고 이름 지었습니다. 하나의 운영체제에서 동시에 여러 개의 session이 생성되어서 독립적인 환경에서 각각 사용이 가능하게 됩니다.

Windows XP에서 도입된  ‘Fast User Swiching’ 이라는 기능 또한 일맥상통하는 기술입니다. Windows 2000 서버 에디션에만 포함되던 Terminal Service에서 사용되던 원격 관리 기술을 서버가 아닌 개인용 PC에 적용한 것입니다. 이를 이용해서 현재 로그인된 A라는 사용자의 세션을 그대로 유지한 상태에서, 새로운 사용자 B로 로그인해서 다른 세션에서 작업이 가능합니다. 동일한 PC에서 A 사용자의 세션과 B 사용자의 세션이 모두 active 한 상태에서 전환이 가능합니다.

그러면 이제 운영체제의 환경이 이렇게 되다보니 응용 프로그램을 개발하는 개발자도 고민거리가 하나 생기게 됩니다. 현재 실행되는 응용 프로그램이 로컬 컴퓨터에서 실행되고 있는지, 아니면 Remote Session에 의해서 실행되고 있는지 확인하는 방법이 필요하게 됩니다. 아주 간단한 예로 애플리케이션 라이센스에 민감한 유료 응용 프로그램이 있다면, 하나만 구입하고 서버에서 설치한 상태에서 여러 사용자들이 원격으로 접속해서 공유해서 사용하는 방법이 가능하기 때문입니다.

만약 remote session 환경인지 판단해서 “원격 환경에서는 실행되지 않는 프로그램입니다.”라는 메시지를 띄우고 종료하고 싶다면 이를 어떠한 방법으로 확인할 수 있을까요. GetSystemMetrics Win32 API를 통해 저희가 원하는 내용을 얻을 수 있습니다.

이 함수는 다음과 같이 nIndex라는 int 형 인자를 받고, int 형으로 결과를 리턴 해주는 함수 입니다.

int WINAPI GetSystemMetrics(
  __in  int nIndex
);
nIndex 값으로 다음 값을 전달하면 현재 실행되는 환경이 원격 환경인지 확인이 가능합니다.
SM_REMOTECONTROL (0x2001)

This system metric is used in a Terminal Services environment. Its value is nonzero if the current session is remotely controlled; otherwise, 0.
SM_REMOTESESSION (0x1000)

This system metric is used in a Terminal Services environment. If the calling process is associated with a Terminal Services client session, the return value is nonzero. If the calling process is associated with the Terminal Services console session, the return value is 0.

Windows Server 2003 and Windows XP:  The console session is not necessarily the physical console. For more information, see WTSGetActiveConsoleSessionId.

GetSystemMetrics 함수 및 nIndex에 대한 보다 자세한 내용은 아래 MSDN 문서를 참고하시기 바랍니다.

GetSystemMetrics
http://msdn.microsoft.com/en-us/library/ms724385(VS.85).aspx

 

'Programming' 카테고리의 다른 글

Windows Sidebar Gadget 만들기 #2 - Flyout  (0) 2009.05.28
Windows Sidebar Gadget 만들기 #1  (0) 2009.05.26
Kernel Mode vs. User Mode  (0) 2009.05.21
LiveKD  (0) 2009.05.15
TIB와 IsDebuggerPresent  (0) 2009.05.13
Posted by noenemy
,

오늘은 아주 원론적인 얘기이지만 운영체제를 이해하는 데 있어서 가장 기본이 되는 Kernel Mode와 User Mode에 대해서 알아볼까 합니다. 특히 Windows Internals 책에 이 부분에 대한 내용이 잘 정리되어 있어서 많은 부분을 참고해서 본 글을 쓰고자 합니다. 자세한 내용은 Windows Internals의 4th Edition의 p.16-21 부분을 참고하시기 바랍니다.

Intel CPU에서는 다음과 같이 'ring'이라는 용어를 이용해서 아래 그림에서 가장 내부에 있는 Ring 0로부터 가장 바깥 부분에 해당하는 Ring 3까지 크게 4가지의 특권 레벨(privilege level)로 구분하고 있습니다. 원의 중심부에 있을 수록 시스템의 주요 자원에 접근할 수 있는 권한이 많은 것이고, 바깥 부분에 있을 수록 제한적인 접근만 허용됩니다. 이렇게 4개의 레벨로 구분함으로써 각 계층에서 접근 가능한 시스템 자원의 권한을 구분함으로써 외부의 Ring에서 부적절한 코드에 의해서 시스템의 주요 자원이 손상되는 것을 막을 수 있으니 시스템의 안정성은 높아질 수 있고, 각 계층별로 할 수 있는 일과 할 수 없는 일을 구분함으로써 각 layer에 맞는 적절한 역할을 수행할 수 있도록 구조화 한 것입니다.



실제 Windows 운영체제에서는 이와 같은 4개의 특권 레벨 중에서 Ring 0와 Ring 3 2개만 사용하며, Ring 0를 'Kernel mode', Ring 3를 'User Mode' 라고 정의하고 있습니다. 기존의 Windows 운영체제에서 지원했던 Alpha나 MIPS chip에서는 2개의 특권 레벨만 지원했기 때문에 WIndows도 2계층으로 설계를 하였다고 합니다.

Windows 운영체제에서 Kenel Mode와 User Mode의 가장 큰 차이점은 접근 가능한 가상메모리 영역에 있습니다. User Mode는 일반적으로 작성되는 User Mode Application에서 사용되는 User Address Space만 접근이 가능하며 Kernel Address Space는 접근할 수 없습니다. 반면에 Kenel Mode에서 실행되는 코드는 모든 Address Space에 접근이 가능합니다. User Address Space는 각 프로세스마다 독립적인 공간을 보장 받기 때문에 해당 프로세스의 영역에 해당하는 데이터만 접근이 가능한 반면에, Kernel Address Space는 Kernel Level에서 실행되는 디바이스 드라이버나 시스템 쓰레드 등이 공유해서 함께 사용되는 영역으로 프로세스의 구분이 없습니다.

Kernel Mode에서는 모든 Address 영역에 접근이 가능하므로 코드 작성이 잘못되거나 악의적인 목적으로 특정 부분을 공격할 경우 시스템의 안정성에 크게 영향을 받게 되므로, Kernel에서 실행되는 모듈을 설치할 때는 주의할 필요가 있습니다. 악성 Kernel Module이 시스템에 설치되어 문제를 일으키는 것을 막기 위해 커널 모듈이 시스템이 설치될 때 해당 모듈을 어느 회사에서 만든 것인지 인증하기 위해 Driver Code Signing이라는 기술을 사용하기도 합니다. 실제로 Windows 2003 서버 이후 64bit 운영체제에서는 이렇게 디지털 서명되지 않은 커널 모듈은 운영체제에 의해서 로드 되지 않습니다. 어느 회사에서 만든 것인지 신분이 확실한 모듈만 선별해서 로드함으로써 시스템 안정성을 높이고자 하는 것입니다.

그렇다면 User Mode에서 실행 중인 코드에서 파일에 접근하거나 화면에 그래픽 작업을 해야하는 등의 시스템 자원을 이용해야 하는 경우에는 어떻게 해야 될까요? 외부의 Ring에서는 하위에 있는 Ring을 거쳐서만 Ring 내부로 접근이 가능합니다. 즉, Windows 에서는 User Mode에서는 Kernel Mode를 거쳐야지만 시스템 자원에 접근할 수 있다는 의미가 됩니다. 이를 위해서는 User Mode에서 실행되던 thread가 Kernel Mode에 접근할 수 있도록 특권 레벨을 부여 받은 뒤에 시스템 자원을 사용하고, 그 뒤에 다시 User Mode로 돌아갈 때에는 반대의 과정으로 낮은 특권 레벨로 변경되는 과정이 필요합니다. User Mode에서 Kernel Mode로 변경하는 과정을 System Service Call이라고 부릅니다. 말 그대로 User Mode에서 시스템에서 제공하는 서비스를 호출해서 사용하는 것을 의미합니다.

이러한 System Service Call은 원래 User Mode에서 Kernel Mode로 진입하기 전에 인터럽트 0x2e를 발생시킴으로써 Kenel에서 handler가 이를 감지하고 필요한 Service Call을 해주는 형태로 설계되어 있습니다. 매번 interrupt handler가 이를 처리하는 과정에서 발생하는 overhead를 줄이기 위해서 Pentium II 이후 버전에서는 System Service Call 만을 위한 CPU instruction이 새로 추가되어 사용되고 있습니다. 이를 위해 Intel에서는 SYSENTER를, AMD에서는 SYSCALL 명령어가 별도로 제공되고 있습니다.

이러한 System Service Call 작업에서는 thread context switching이 발생하지 않습니다. 즉, User Mode에서 실행이던 해당 thread가 System Service Call을 함으로써 Kernel Mode에서 실행되는 것을 의미합니다. Thread와 관련된 커널 구조체 중에 KTHREAD 구조체에는 실제로 특정 thread가 User Mode에서 실행된 시간과 Kernel Mode에서 실행된 시간이 각각 따로 관리되어 저장되고 있습니다. 이는 실행되는 Thread의 성격에 따라서 어떤 것은 Kernel Mode에서 더 많이 실행되고, 어떤 것은 User Mode에서 더 많이 실행됩니다. 또는 운영체제에서 생성한 System Thread라는 특이한 thread 도 있는데, 이는 운영체제가 부팅 되는 과정에서 System Process 내에 생성되어 오직 Kernel Mode에서만 실행되는 여러 시스템 관련 thread를 일컫는 용어 입니다.

다음에 기회가 되면 System Service Call의 과정에 대해서 보다 자세히 정리해보도록 하겠습니다.

'Programming' 카테고리의 다른 글

Windows Sidebar Gadget 만들기 #1  (0) 2009.05.26
Remote Session인지 확인하는 방법  (0) 2009.05.22
LiveKD  (0) 2009.05.15
TIB와 IsDebuggerPresent  (0) 2009.05.13
데이터 실행 방지(DEP)  (0) 2009.05.11
Posted by noenemy
,

LiveKD

Programming 2009. 5. 15. 19:15

WinDbg tool을 이용해서 커널 디버깅을 하려면 target machine을 host machine과 1394나 COM port를 이용해서 연결을 해야 합니다. 최근에는 제약 사항이 있긴 하지만 USB 2.0을 이용해서 좀 더 빠른 속도로 커널 디버깅을 할 수 있고, VMWare나 VirtualPC와 같은 가상 머신을 로컬 컴퓨터에 실행시킨 상태에서도 디버깅이 가능하지만 특정 문제가 발생하는 장비를 분석하기 위해 다른 host machine을 매번 연결하는 것은 번거로운 작업임에는 틀림이 없습니다.

제 경우도 사실 WinDbg를 쓰기 이전에 Numega사의 SoftIce라는 디버깅 툴을 먼저 사용하였습니다. Windows XP 부터 더 이상 버전업이 되지 않고 단종된 제품입니다만, 기존 운영체제 환경에서는 커널모드 디버깅에 있어서 매우 독보적인 존재였습니다. 무엇보다 가장 편했던 기능은 디버깅이 필요한 장비에 바로 설치해서 해당 장비의 커널디버깅을 바로 할 수 있다는 점이었습니다. 언제든지 디버깅일 필요한 시점에서 SoftICE를 호출하고, 현재 장비의 특정 위치에 breakpoint를 걸고 tracing을 하는 작업이 가능했었습니다. 무엇보다도 매우 빠른 속도로 디버깅할 수 있어서 편리하였습니다. (현재 WinDbg가 1394를 지원하기 때문에 많이 빨라졌지만, 이 당시만 해도 serial cable을 이용해서 많은 양의 디버깅 데이터를 target machine과 host machine 간에 통신을 했어야 했기에 반응 속도가 매우 느렸습니다.)

하지만 SoftICE를 설치하면 해당 장비가 오동작하거나 BSOD가 수시로 발생하는 등의 문제가 많았는데, 그것은 local machine에 설치된 운영체제에 종속성이 강하였기 때문입니다. 운영체제가 핫픽스나 서비스팩 등의 설치에 따라서 이미지가 변경될 때마 이러한 일들이 일어났고, Numega 사는 새로운 변경사항에 대한 업데이트를 하고, 이러한 과정을 거듭하다가 아마도 사업 자체를 포기한 것 같습니다. 현재 Windows 7의 출시를 앞두고 있는 상황에서는 추억속의 디버거가 됐지만 WinDbg를 사용하면서 항상 SoftICE를 가끔 그리워 하게 됩니다.

서론이 길었는데 오늘 설명하고자 하는 내용은 WinDbg를 이용해서 로컬 장비의 kernel debugging을 하는 방법을 소개하고자 합니다. 현재는 마이크로소프트사로 입사한 Mark Russinovich(sysinternals 사이트 운영자)가 Sysinternals 사이트에 공개한 LiveKD 라는 툴을 이용하면 제약사항이 있긴하지만 SoftICE 처럼 원격 장비가 아닌 로컬 장비의 커널 디버깅이 가능합니다.

먼저 아래 사이트에서 livekd.zip 파일을 다운로드 받은 후에, WinDbg가 설치된 폴더에 압축을 풉니다.
http://technet.microsoft.com/en-us/sysinternals/bb897415.aspx

해당 폴더에 가보면 livekd.exe라는 실행 파일이 있는데 이를 command window에서 실행하면 됩니다. 다음은 실행 방법에 대한 설명입니다. 실행 옵션이 많지 않기 때문에 비교적 간단하게 사용할 수 있습니다.

Usage: livekd [-w] [-d] [-k ] [debugger options]

-w Runs windbg instead of Kd (Kd is the default).
-d Runs Dumpchk exam instead of Kd (Kd is the default).
-k Specifices complete path and filename of debugger image to execute.

간단히 command에서 livekd.exe를 실행시키면 다음과 같이 console mode로 kernel debugging이 가능합니다.

livekd -w 를 실행하면 다음과 같이 WInDbg가 실행되면서 GUI 모드에서 사용이 가능합니다.

하지만 LiveKD는 실행시점에 수집한 kernel 상태에 대한 snapshot 정보를 보여주는 것이기 때문에 덤프 파일의 내용을 분석하는 것과 같은 방법으로 접근이 가능합니다. 즉, breakpoint를 걸고 tracing과 같은 interactive debugging은 되지 않습니다. 제약사항이 있긴 하지만 여러모로 유용하게 사용할 수 있는 툴인 것 같습니다.

LiveKD는 실행 시점에 시스템 정보를 snapshot을 떠서 정보를 보여주므로 실행된 이후에 변경된 시스템 정보는 확인되지 않습니다. 이 경우엔 LiveKD를 재시작함으로써 새로운 정보를 확인할 수 있습니다. q 명령을 실행하면 다음과 같이 재시작하겠냐는 메시지가 뜨는데, 여기서 y를 실행하면 다시 현재 시점의 snapshot을 얻을 수 있습니다.

0: kd> q
quit:

Execute Kd again? (y/n) y

.... LiveKd 재시작됨....

다음에는 시간되면 WinDbg를 이용한 Local Live Debugging에 대해서 다뤄볼까 합니다.

'Programming' 카테고리의 다른 글

Remote Session인지 확인하는 방법  (0) 2009.05.22
Kernel Mode vs. User Mode  (0) 2009.05.21
TIB와 IsDebuggerPresent  (0) 2009.05.13
데이터 실행 방지(DEP)  (0) 2009.05.11
응답 없음(Not Responding)  (0) 2009.05.11
Posted by noenemy
,
Win32 API 중에 IsDebuggerPresent()라는 함수가 제공되는데 이는 현재 실행 중인 프로세스에 디버거가 붙은 있는지, 즉 현재 디버거에 의해서 디버깅 되고 있는지에 대한 결과를 TRUE/FALSE로 리턴해 주는 함수입니다. 디버거에서는 디버깅되는 프로세스의 메모리에 접근해서 ReadProcessMemory, WriteProcessMemory와 같은 함수를 호출해서 자유롭게 메모리 내용을 읽고 쓰는 것이 가능하기 때문에 개발된 프로그램에 대하여 디버깅 되는 것을 원치 않을 수도 있습니다. 흔히 이를 Anti-debugging 기술이라고 불리우는데 현재 디버깅이 되고 있느냐 아니냐를 판단하는 것부터 시작을 하게 됩니다. 이때 가장 기본적으로 사용될 수 있는 것이 바로 IsDebuggerPresent()라는 함수를 호출해서 리턴값을 검사해보는 것입니다.

BOOL WINAPI IsDebuggerPresent(void);

간단한 방법으로 작성된 프로그램이 초기화 되는 부분에서 이 함수를 호출하고 리턴값이 TRUE 이면 프로그램이 종료되도록 구현할 수 있을 것입니다.

디버깅을 하려는 입장에서는 이러한 루틴이 있을 경우 디버깅을 하지 못하게 되므로 이를 다시 회피할 방법을 찾게 되는데, 가장 간단한 방법으로 IsDebuggerPresent() 함수가 무조건 FALSE를 리턴하도록 변조하는 방법을 사용합니다. 모든 해킹은 동작 원리를 알고 이를 역으로 이용하고, 보안은 다시 해킹의 동작 원리를 이해하고 이를 방어하는 방법을 찾는 과정입니다.

자, 그럼. IsDebuggerPresent() 함수가 무조건 FALSE를 리턴하도록 변조된 상황이라고 가정하고 이를 다시 감지할 방법을 찾아보도록 하겠습니다. IsDebuggerPresent()라는 함수는 현재 프로세스가 디버깅되고 있는지를 어떻게 판단할까요? 우리가 직접 해당 로직을 구현해서 판단한다면 이러한 문제를 해결할 수 있을 것 같습니다.

실제 아래에 소개하려는 내용은 Debugging Applications for Microsoft .NET and Microsoft Windows(by John Robbins) 책의 160-161 page에서 소개되고 있는 방법입니다. IsDebuggerPresent() 함수를 직접 구현해보는 과정을 설명하고 있는데, 이렇게 구현을 하더라도 다시 쉽게 해당 루틴이 변조될 것으로 예상되지만 일단 시간을 지연시킬 수 있다는 측면에서 교육적인 예제라고 생각되어서 소개하려고 합니다.

먼저 핵심부터 말씀드리자면 'TIB 구조체 중에 디버깅 중인지에 대한 정보를 저장하고 있는 멤버 변수가 있다는 것이고, IsDebuggerPresent 함수가 이 값을 참조해서 결과를 리턴해주는 함수'라는 것입니다.

자. 그럼 우선 TIB(Thread Information Block)이라는 구조체에 대해서 알아보도록 하겠습니다. TIB(Thread Information Block)은 단어 의미 그대로 특정 쓰레드에 대한 정보를 가지고 있는 구조체입니다. Windows 운영체제에서 실행의 주체가 Thread인 만큼 내부적으로 관리되어야 하는 정보가 많기 때문에 매우 많은 멤버를 가지고 있는 구조체입니다. 이 구조체 변수에 접근하면 내부적으로 해당 쓰레드가 가지고 있는 많은 정보를 참조할 수 있는데, 아래 wikipedia 사이트에서 확인이 가능합니다.

Win32 Thread Information Block 
http://en.wikipedia.org/wiki/Win32_Thread_Information_Block

실제 이 구조체 정보는 undocumented 된 내용이었는데, Windows Internals 책에서도 소개를 하고 있고 이미 인터넷 상에서도 쉽게 관련 정보를 찾을 수 있는 것이 사실입니다.

위 문서의 내용에 의하면 FS 레지스터의 0x18h에 TIB가 위치한 주소의 위치 정보가 저장되어 있음을 알 수 있습니다. 이를 응용하면 프로그램 코드에서 다음과 같은 방법으로 간단히 TIB에 접근할 수 있게 됩니다.

 void *getTib()
{
    void *pTib;
    __asm {
        mov EAX, FS:[18h]
        mov [pTib], EAX
    }
    return pTib;
}

TIB 구조체에서 0x30h offset 위치에 해당하는 값이 현재 프로세스의 디버깅 여부에 대한 값을 가지고 있는데, 이를 다음과 같이 구현할 수 있습니다.


BOOL AntiHackIsDebuggerPresent (void)
{
    BOOL bRet = TRUE;
    __asm
    {
           // TIB(Thread Information Block)의 위치 얻기
           MOV     EAX, FS:[00000018H]

           // TIB에서 0x30 위치에 해당하는 것이 debugging과 관련된 구조체에 대한 포인터임
           MOV     EAX, DWORD PTR [EAX+030H]

           // 해당 구조체에서 두번째 WORD에 저장된 값이 현재 프로세스가 디버깅 중인지에 대한 값이다.
           MOVZX  EAX, BYTE PTR [EAX+002H]

           // 결과 리턴
           MOV     bRet, EAX
    }
    return bRet;
}

현재 Anti-debugging 기술이 많이 사용되고 있고, 또한 이렇게 많이 사용되는 Anti-debugging 기술을 회피할 수 있는 방법 또한 많이 연구되고 있습니다. 따라서 기술적인 측면에서 높은 수준의 보안책은 아니지만, 이렇게 IsDebuggerPresent()라는 알려진 Win32 함수 대신에 우리가 직접 작성한 함수를 호출해서 디버깅 여부를 감지하는 것만으로도 책의 저자인 John Robbins은 어느 정도의 효과를 볼 수 있다고 설명하고 있습니다.

undocumented 된 내용이므로 TIB의 세부 구조 또는 Anti-debugging 기술에 대해서 자세히 알고 싶으신 분은 관련 책이나 다른 문서를 참고하시기 바랍니다.

'Programming' 카테고리의 다른 글

Kernel Mode vs. User Mode  (0) 2009.05.21
LiveKD  (0) 2009.05.15
데이터 실행 방지(DEP)  (0) 2009.05.11
응답 없음(Not Responding)  (0) 2009.05.11
I/O Stack Locations  (0) 2009.05.04
Posted by noenemy
,
Windows XP SP2와 Windows 2003 SP1이 릴리즈 될 때 새로 추가된 큰 기능 중에 하나가 바로 데이터 실행 방지(DEP: Data Execution Prevention) 기능입니다. 이 기능 때문에 기존에 실행되던 애플리케이션들이 Service Pack을 설치한 이후에 제대로 실행되지 않는 문제들이 발생하여 시장에 혼란이 있었던게 사실입니다.

이번 포스트에서는 호환성 이슈를 많이 일으켰던 DEP 기능에 대해서 살펴보도록 하겠습니다.

 

[그림] 데이터 실행 방지(DEP) 설정 윈도우

기존에 서버 시장에서 주목을 받지 못했던 Windows 운영체제는 Windows 2000 부터 많은 서버용 애플리케이션과 서비스 기능을 제공하면서 엔터프라이즈 시장에서의 입지를 넓혀가기 시작했는데, 수 많은 웜 바이러스의 등장으로 운영중이던 서버가 공격 받아서 서비스가 중단되는 사건이 수차례 발생하면서 시장으로부터 불안한 시스템으로 인식되되기 시작했습니다. 이를 막기위한 핫픽스가 나오긴 했지만 계속되는 유사 공격에 많은 피해를 입은 것이 사실입니다. 웜 바이러스가 감염되는 과정은 대부분 Buffer Overflow 취약점을 이용해서 운영중인 서비스를 위한 잘 알려진 네트워크 포트(예를 들어 HTTP, RPC 포트 등)에 대한 공격으로 인한 것이었습니다.

이에 마이크로소프트는 가상메모리의 최소 단위인 페이지(page)에 새로운 보호 속성(PAGE_EXECUTE, PAGE_EXECUTE_READ, PAGE_EXECUTE_READWRITE, PAGE_EXECUTE_WRITECOPY)을 추가해서 이러한 공격으로부터 시스템을 보호하고자 했는데, 기존에는 read, write 등의 권한 체크 외에 실행(execution)이 가능한지에 대해서도 체크 하도록 추가한 것입니다. 즉, 실행될 수 없는 가상 메모리 영역에서 코드가 실행되고자 할 때 STATUS_ACCESS_VIOLATION 예외를 발생시키는 것이 DEP의 동작 원리입니다.

웜 바이러스의 경우 data 영역인 thread stack을 buffer overflow 시켜 stack을 임의로 변경시킴으로써 악성 코드가 실행되도록 하는 방법으로 주로 공격을 당했는데, DEP 기능이 활성화 되어 있을 경우 이러한 공격에 의해서 악성 코드가 실행되려는 시점에 해당 영역에서는 code execution이 허용되어 있지 않으므로 예외가 발생하고 악성 코드의 실행을 막는 역할을 할 수 있습니다.

하지만 heap과 같은 데이터 영역에서 code를 실행시키는 방법은 DEP 이전에 일종의 팁으로 기존 애플리케이션에 사용되기도 했습니다. 예를 들어 runtime시에 암호화 또는 압축되어 있는 실행 코드를 데이터 영역에 복호화 또는 압축 해제한 후에 이를 실행되게끔 하도록 구현된 프로그램이 있었는데, 이러한 애플리케이션에서 DEP 기능이 실행된 시스템에서는 호환성 문제가 발생하기도 하였습니다. 지금은 이러한 DEP 기능이 시장에서 사용된 지 오래되었기 때문에 이러한 호환성 이슈는 대부분 해소된 것으로 보입니다.

'Programming' 카테고리의 다른 글

LiveKD  (0) 2009.05.15
TIB와 IsDebuggerPresent  (0) 2009.05.13
응답 없음(Not Responding)  (0) 2009.05.11
I/O Stack Locations  (0) 2009.05.04
PEVIEWER로 살펴보는 notepad.exe  (1) 2009.04.30
Posted by noenemy
,

가끔 Windows 운영체제를 사용하다보면 특정 애플리케이션이 아무런 반응을 보이지 않고 실행이 멈춘 듯한 모습을 볼 수 있습니다. 해당 어플리케이션의 타이틀바에는 '(응답 없음)'이라는 메시지가 추가되고 회색빛으로 해당 윈도우가 변해버리게 되는데, 작업 관리자에서 보면 해당 애플리케이션의 상태가 ' Not Responding(응답 없음)'이라고 표시됩니다. 이러한 증상은 해당 애플리케이션의 실행 중에 일시적으로 발생했다가 다시 정상 동작을 하기도 하고, 아니면 강제로 해당 프로세스를 종료하기 전까지는 계속 반응이 없는 상태가 되기도 합니다.

오늘은 이러한 'Not Responding' 상태에 대해서 살펴보도록 하겠습니다.

[그림] 작업관리자에서 프로세스 상태 확인하기

Windows의 작업 관리자는 어떻게 특정 애플리케이션이 응답 없는 상태인지 알고 'not resdponding'으로 표시해주는 걸까요? 이에 대한 답을 얻기 위해서는 윈도우 메카니즘에서 가장 기본이 되는 Windows Message부터 살펴봐야할 필요가 있습니다.

GUI 기반의 윈도우 운영체제로 오면서 기존의 CUI 환경과는 달리 사용자가 마우스를 이용해서 어떠한 애플리케이션에 어떠한 명령을 하려고 할지 미리 판단하기가 어려워졌습니다. 이를 위해서 애플리케이션은 '어떠한 이벤트가 발생하면 그것에 대해서 처리를 하는 식'으로 애플리케이션 개발 방법의 변화를 필요로 했는데 이를 'Event Driven Programming'이라고 합니다.

Win32 응용 프로그램을 이용해서 Hello World라는 샘플 프로그램을 개발해보신 분은 알겠지만, Window를 가지는 애플리케이션을 개발하기 위해서는 기본적으로 Window Object를 생성하고, 해당 Window에 대한 이벤트를 처리할 수 있는 Window Procedure를 제공해야 합니다. 그리고 각 Window는 자신의 Window에 대해 발생하고 처리되어야 하는 Window Message를 queue에 넣어두고 순서대로 이를 하나씩 빼와서 처리를 하게 됩니다. Window Message를 저장해두는 공간을 'Message Queue'라고 부르고, Message Queue에 메시지를 하나씩 가져와서 처리하는 과정을 반복하는 것을 'Message Loop'라고 합니다.

다음은 애플리케이션에서 일반적으로 가지고 있는 Message Loop믜 모습입니다. 애플리케이션은 처리해야할 메시지가 있으면 이에 대한 처리를 하는 과정을 반복함으로써 실행을 합니다. 만약 WM_QUIT 메시지를 전달받게 되면 Message Loop를 빠져나와서 return 하게됨으로써 애플리케이션은 종료되도록 구현되어 있습니다.

while (GetMessage(&Message,0,0,0)) {
  TranslateMessage(&Message);
  DispatchMessage(&Message);
}
return;

위 메시지 루프에서 GetMessage()라는 Win32 API에 주의를 기울일 필요가 있습니다. GetMessage 함수 호출시 2,3,4번째 인자를 모두 0(NULL)로 전달했는데 이는 이 프로그램에서 생성한 모든 Window의 메시지를 받기 원한다는 것을 의미합니다. Windows 운영체제는 전달된 Message 구조체에 Message Queue에 있는 메시지를 Pop 시켜서 채우게 됩니다. GetMessage는 blocking function으로 만약 Message Queue에서 가져올 내용이 없으면 있을 때까지 대기했다가 return을 하는 함수 입니다.

Message Queue에서 메시지를 가져왔으면 Message 구조체 변수에 해당 메시지에 대한 내용이 채워져 있을 것입니다. 이것이 Virtual Key에 대한 처리인지 확인하기 위해서 먼저 TranslateMessage API를 호출하고, DispatchMessage API에 해당 Message 구조체를 다시 전달합니다. DispatchMessage를 호출하면 Windows 운영체제는 전달된 Message를 처리할 수 있도록 해당 Window Procedure를 호출합니다. 이는 Windows 운영체제가 해당 Window Procedure를 호출한다는 것을 의미합니다. Windows Procedure가 Callback 함수 형태를 가져야 하는 이유가 여기에 있습니다. Message의 Dispatching이 완료되면 다시 GetMessage 함수를 호출해서 다음 메시지를 기다리게 됩니다.

그런데 Windows 운영체제에서 특정 프로세스로 메시지를 전달했는데 해당 프로세스의 Message Loop에서 이 메시지를 가져가지 않으면 어떻게 될까요? 예를 들어 우편함에서 편지를 찾아가지 않아서 우편물이 쌓이게 되면 집배원이 해당 수신인이 부재중이거나 수신할 수 없는 상태라고 판단하게 되듯이, Windows 운영체제도 특정 시간동안 메시지를 받지 못하는 프로세스가 있으면 해당 프로세스를 '응답 없음(Not Responding)' 상태로 판단하게 됩니다. 어떠한 메시지도 받을 수 없기 때문에 키보드나 마우스 이벤트에 아무런 반응을 하지 않게 되고, 또한 x (종료버튼)을 클릭해도 프로세스가 종료되지 않습니다.

내부적으로 작업 관리자는 애플리케이션의 응답 상태를 확인하기 위해서 WM_GETICON 메시지를 애플리케이션의 main window로 SendMessage를 이용해서 전달합니다. 만약 해당 애플리케이션이 응답하지 않는다면 이를 "응답없음(Not Responding") 상태로 보게 됩니다.

참고로 다음은 MSDN에서 GetMessage에 대한 문서에 소개되어 있는 내용으로 Windows XP에서는 top level window가 not responding 상태가 되면 이를 대체하는 ghost window를 만들어 낸다는 내용이 있습니다.

Windows XP: If a top-level window stops responding to messages for more than several seconds, the system considers the window to be not responding and replaces it with a ghost window that has the same z-order, location, size, and visual attributes. This allows the user to move it, resize it, or even close the application. However, these are the only actions available because the application is actually not responding. When an application is being debugged, the system does not generate a ghost window.

그러면 어떠한 경우에 프로세스가 응답없음 상태가 될까요. 여기에는 다음과 같은 몇가지 경우로 분류할 수 있습니다.

  • 프로그래밍 오류 : '무한루프'와 같은 프로그래밍 오류
  • 설계 오류 : 여러가지 경우가 있는데, waiting 하는 대상이 없는데 이를 무한 기다리도록 설계한 경우
  • 하드웨어 이슈 : 하드웨어 I/O 오류로 인해 이를 사용하는 프로그램이 응답없는 상태가 되는 경우
  • 악성 코드 : 바이러스나 스파이웨어로 인해 정상적인 실행이 불가능한 상태가 되는 경우

일시적으로 응답없음 상태였다가 다시 정상적으로 실행되는 것이 좋겠지만 강제 종료하지 않고는 이러한 증상이 없어지지 않는다면 처리 중인 데이터를 잃어버릴 수도 있지만 어쩔수 없이 작업 관리자의 'End Process' 명령을 내림으로써 해당 프로세스를 종료시켜야 됩니다. 이는 내부적으로 TerminateProcess Win32 API를 사용하고 있습니다.

 

'Programming' 카테고리의 다른 글

TIB와 IsDebuggerPresent  (0) 2009.05.13
데이터 실행 방지(DEP)  (0) 2009.05.11
I/O Stack Locations  (0) 2009.05.04
PEVIEWER로 살펴보는 notepad.exe  (1) 2009.04.30
Example I/O request - an overview  (0) 2009.04.21
Posted by noenemy
,

I/O Stack Locations

Programming 2009. 5. 4. 15:17
일반적으로 함수가 호출될 때마다 ESP, EBP 레지스터 정보와 함께 thread stack를 이용해서 함수에 전달되어야 하는 파라미터 정보와 함수 실행이 끝난 뒤에 리턴되어야 하는 함수의 주소 정보 등을 담아두게 된다. stack에 쌓인 이러한 정보를 역으로 추적하게 되면 현재 실행중인 함수에 이르기까지 호출된 함수들과 전달된 파라미터 정보들을 확인할 수 있는데 이를 'Call Stack'이라고 한다.

Driver에서는 이와 유사한 개념으로 특정 IRP(I/O Request Packet)를 처리하는데 관여할 수 있는 여러 driver들이 마치 chain과 같은 형태를 구성하고 있는데 이를 'Driver Stack'이라고 한다. IRP를 처리할 때 필요한 처리 루틴이나 파라미터 등의 정보를 각각의 driver 들은 I/O stack location이라는 구조체에 저장하여 해당 IRP에 append 시켜야 하며, 상위 레벨의 driver는 하위 레벨의 driver에게 IoCallDriver 를 호출해서 IRP를 전달하기 전에 해당 stack location의 정보를 반드시 채워야 한다.

I/O manager는 이러한 Driver Stack을 구현하기 위해서 각 IRP마다 I/O stack location 정보를 저장할 수 있도록 배열을 만든다. 각 driver들은 특정 IRP를 처리하기 전에 IoGetCurrentIrpStackLocation 함수를 호출함으로써 해당 driver가 I/O operation을 수앵하는데 필요한 고유의 I/O stack location 정보를 얻을 수 있다.

다음 그림은 IRP 내에 저장되는 데이터를 도식화한 것이다.

아래 부분에 흰색 블록으로 표현된 것이 하나의 I/O stack location이며, 만약 2개의 driver가 관여하고 있다면 I/O stack location이 하나 더 append 된 형태가 될 것이다.

 


하나의 I/O Stack Location 정보는 IO_STACK_LOCATION 구조체를 이용해서 정의할 수 있는데, WDK에서 확인할 수 있는 IO_STACK_LOCATION 구조체의 정의는 다음과 같다.

typedef struct _IO_STACK_LOCATION {
  UCHAR  MajorFunction;  // 수행되어야 할 I/O operation 타입
  UCHAR  MinorFunction;  // MajorFunction에 대한 Sub funtion code
  UCHAR  Flags;   // 요청에 대한 플래그 (파일 시스템 드라이버에서 주로 사용됨)
  UCHAR  Control;  //
  union {
        //
        // Parameters for
IRP_MJ_CREATE 
        //
        struct {
            PIO_SECURITY_CONTEXT SecurityContext;
            ULONG Options;
            USHORT POINTER_ALIGNMENT FileAttributes;
            USHORT ShareAccess;
            ULONG POINTER_ALIGNMENT EaLength;
        } Create;
        //
        // Parameters for
IRP_MJ_READ 
        //
        struct {
            ULONG Length;
            ULONG POINTER_ALIGNMENT Key;
            LARGE_INTEGER ByteOffset;
        } Read;
        /* 이하 생략 */
        /* full list는
http://msdn.microsoft.com/en-us/library/aa491675.aspx 참고 */
  } Parameters;  // MajorFunction과 MinorFunction 수행에 필요한 파라미터

  PDEVICE_OBJECT  DeviceObject;  // IRP를 처리해야하는 target device (DEVICE_OBJECT 구조체 포인터)
  PFILE_OBJECT  FileObject;  // DeviceObject와 관련된 file object (FILE_OBJECT 구조체 포인터 )
 .
} IO_STACK_LOCATION, *PIO_STACK_LOCATION;

WinDbg에서 !irp 명령을 이용하면 다음과 같이 특정 IRP에 관련된 I/O Stack Location 정보를 확인할 수 있다.

0: kd> !irp 0x831f4a00
Irp is active with 8 stacks 5 is current (= 0x831f4b00)
 Mdl = 82b020d8 Thread 8c622118:  Irp stack trace.
     cmd  flg cl Device   File     Completion-Context
 [  0, 0]   0  0 00000000 00000000 00000000-00000000

                        Args: 00000000 00000000 00000000 00000000
 [  0, 0]   0  0 00000000 00000000 00000000-00000000

                        Args: 00000000 00000000 00000000 00000000
 [  0, 0]   0  0 00000000 00000000 00000000-00000000

                        Args: 00000000 00000000 00000000 00000000
 [  0, 0]   0  0 00000000 00000000 00000000-00000000

                        Args: 00000000 00000000 00000000 00000000
>[  3,34]  40 e1 828517a8 00000000 842511e0-00000000 Success Error Cancel pending
               \Driver\disk     partmgr!PmReadWriteCompletion
                        Args: 00007000 00000000 fe084e00 00000004
 [  3, 0]  40 e0 82851450 00000000 842414d4-82956350 Success Error Cancel
               \Driver\PartMgr  volmgr!VmpReadWriteCompletionRoutine
                        Args: 129131bb 000000de fe084e00 00000004
 [  3, 0]   0 e0 82956298 00000000 847eeed0-829e2ba8 Success Error Cancel
               \Driver\volmgr   Ntfs!NtfsMasterIrpSyncCompletionRoutine
                        Args: 00007000 00000000 1bdae400 00000000
 [  3, 0]   0  0 82ac2020 8e879410 00000000-00000000
               \FileSystem\Ntfs
                        Args: 00007000 00000000 00018400 00000000

References 

WDK I/O Stack Locations
http://msdn.microsoft.com/en-us/library/ms795764.aspx

'Programming' 카테고리의 다른 글

데이터 실행 방지(DEP)  (0) 2009.05.11
응답 없음(Not Responding)  (0) 2009.05.11
PEVIEWER로 살펴보는 notepad.exe  (1) 2009.04.30
Example I/O request - an overview  (0) 2009.04.21
On-demand 메모리 관리  (0) 2009.04.02
Posted by noenemy
,