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
,

PE(Portable Executable) format이란 Win32, Win9x, W2K, WinNT 등 Windows 계열 플랫폼에서 실행파일을 위해 설계된 파일 포맷입니다. 이는 UNIX의 COFF(Common Object File Format)에서 파생된 것으로 실행 코드외에도 리소스, relocation 데이터, 디버깅 정보, DLL import와 export에 관련된 테이블 정보 등을 포함할 수 있습니다. EXE나 DLL 외에도 드라이버 파일, ActiveX 콘트롤 등 모든 실행 파일은 PE format을 따라야 합니다. 프로세스의 가상 메모리 공간에 이러한 PE format file은 memory mapped file 기술을 이용해서 메모리로 로드되어 바로 실행될 수 있는 형태로 구성되어 있습니다.

Windows 운영체제는 실행파일에 따라서 어떠한 환경의 subsystem에서 실행되어야 하는지 판단하고 해당 플랫폼에서 실행될 수 있도록 환경을 구성해줍니다. 예를 들어 Windows 64bit 환경에서도 WOW64 기술을 이용해서 기존 x86 기반의 32bit 어플리케이션도 실행될 수 있으며, MSDOS 응용프로그램도 VDM(Virtual DOS Machine) 위에서 실행될 수 있습니다. 뿐만 아니라 POSIX 프로그램도 Windows 운영체제에서 실행될 수 있습니다. 이는 프로그램이 실행되는 시점에 Windows가 해당 응용프로그램의 PE format에 기록된 정보를 읽어서 어떠한 환경에서 실행되도록 빌드된 응용 프로그램인지 판단하고 이와 적절한 subsystem과 실행 환경을 만들어주기 때문입니다.

이처럼 실행 파일의 실행에 필요한 정보를 파일 내부에 직접 가지고 있으므로 이동이 가능하고(portable) 또한 실행이 가능(executable)합니다.

 PE format의 내부 구조에 대해서는 다음에 기회가 있으면 보다 자세히 다루도록 하고, 오늘은 PE 파일의 내부 구조를 분석해서 보여주는 툴을 통해서 어떠한 정보가 포함되어 있는지 대략 살펴보도록 하겠습니다. PE format을 보여주는 많은 툴 중에서 freeware로 사용할 수 있는 PE Viewer v1.0을 이용해서 Windows 운영체제에 기본으로 탑재된 notepad.exe 실행파일을 살펴보도록 하겠습니다.

PE Viewer v1.0
http://www.softpedia.com/get/Programming/Other-Programming-Files/PE-Viewer.shtml

--------------------------- 이하 PEVIEWER의 Misc 탭의 내용 ---------------------------------

File: C:\WINDOWS\NOTEPAD.EXE Size 10E00 (69120 bytes 67 KB)    <-- 실행파일의 크기
PE base is   E0

--->Following is IMAGE_FILE_HEADER

Offset : 00000004 Machine : 014C Intel i386   // 파일이 샐행될 CPU 플랫폼
Offset : 00000006 NumberOfSections :    3    // 섹션의 개수
Offset : 00000008 TimeDateStamp : 41107CC3    // 파일이 생성된 시간과 날짜
Offset : 0000000C PointerToSymbolTable : 00000000    // 디버깅시 사용되는 심볼 테이블의 위치
Offset : 00000010 NumberOfSymbols : 00000000   // 디버깅시 사용되는 심볼의 개수
Offset : 00000014 SizeOfOptionalHeader : 00E0   // OptionalHeader의 크기
Offset : 00000016 Characteristics : 010F No relocation. Executable. Line number stripped. Local symbols stripped. 32 bits word machine. // exe인지 dll인지 식별하는데 사용되는 플래그

--->Following is IMAGE_OPTIONAL_HEADER

Offset : 00000018 Magic : 010B
Offset : 0000001A LinkerVersion :  7.10   // Liker 버전
Offset : 0000001C SizeOfCode : 00007800
Offset : 00000020 SizeOfInitializedData : 0000A600
Offset : 00000024 SizeOfUninitializedData : 00000000
Offset : 00000028 AddressOfEntryPoint (RVA) : 0000739D   // PE 파일에서 처음 실행될 명령의 RVA 주소
Offset : 0000002C BaseOfCode (RVA) : 00001000
Offset : 00000030 BaseOfData (RVA) : 00009000
Offset : 00000034 ImageBase (RVA) : 01000000   // PE 파일 로딩시 선호되는 시작 주소
Offset : 00000038 SectionAlignment : 00001000   // 각 섹션은 4096의 배수가 되는 곳에서 시작되어야 함
Offset : 0000003C FileAlignment : 00000200 
Offset : 00000040 OperatingSystemVersion : 5.1
Offset : 00000044 ImageVersion : 5.1
Offset : 00000048 SubsystemVersion : 4.0
Offset : 0000004C Reserved1 : 00000000
Offset : 00000050 SizeOfImage : 00014000     // 메모리에 로딩될 PE 이미지의 전체 크기
Offset : 00000054 SizeOfHeaders : 00000400   // PE Header의 크기
Offset : 00000058 CheckSum : 00014F7F (Correct checksum is 00014F7F)
Offset : 0000005C Subsystem : 0002 WINDOWS_GUI (Graphics)
Offset : 0000005E DllCharacteristics : 8000
Offset : 00000060 SizeOfStackReserve : 00040000  
Offset : 00000064 SizeOfStackCommit : 00011000  
Offset : 00000068 SizeOfHeapReserve : 00100000
Offset : 0000006C SizeOfHeapCommit : 00001000
Offset : 00000070 LoaderFlags : 00000000
Offset : 00000074 NumberOfRvaAndSizes : 00000010


 ~~~~IMAGE_DATA_DIRECTORY~~~~
 Offset : 00000078 01  RVA : 00000000  Size : 00000000 Name : Export
 Offset : 00000080 02  RVA : 00007604  Size : 000000C8 Name : Import
 Offset : 00000088 03  RVA : 0000B000  Size : 00008958 Name : Resource
 Offset : 00000090 04  RVA : 00000000  Size : 00000000 Name : Exception
 Offset : 00000098 05  RVA : 00000000  Size : 00000000 Name : Security
 Offset : 000000A0 06  RVA : 00000000  Size : 00000000 Name : BaseRelocate
 Offset : 000000A8 07  RVA : 00001350  Size : 0000001C Name : Debug
 Offset : 000000B0 08  RVA : 00000000  Size : 00000000 Name : Architecture
 Offset : 000000B8 09  RVA : 00000000  Size : 00000000 Name : GlobalPtr
 Offset : 000000C0 10  RVA : 00000000  Size : 00000000 Name : TLS
 Offset : 000000C8 11  RVA : 000018A8  Size : 00000040 Name : LoadConfig
 Offset : 000000D0 12  RVA : 00000250  Size : 000000D0 Name : Bound
 Offset : 000000D8 13  RVA : 00001000  Size : 00000348 Name : ImportAddress
 Offset : 000000E0 14  RVA : 00000000  Size : 00000000 Name : DelayLoadImport
 Offset : 000000E8 15  RVA : 00000000  Size : 00000000 Name : COM
 Offset : 000000F0 16  RVA : 00000000  Size : 00000000 Name : Reserved

--->Following is Section Table

Section 1
 Offset : 000000F8 Name : .text   // 실행 코드가 위치한 섹션
 Offset : 00000100 VirtualSize : 00007748   VirtualAddress : 00001000
 Offset : 00000108 SizeOfRawData : 00007800  PointerToRawData : 00000400
 Offset : 00000110 PointerToRelocations : 00000000 PointerToLinenumbers : 00000000
 Offset : 00000118 NumberOfRelocations : 00000000 NumberOfLinenumbers : 00000000
 Offset : 0000011C Characteristics : 60000020


Section 2
 Offset : 00000120 Name : .data  // 데이터 섹션
 Offset : 00000128 VirtualSize : 00001BA8   VirtualAddress : 00009000
 Offset : 00000130 SizeOfRawData : 00000800  PointerToRawData : 00007C00
 Offset : 00000138 PointerToRelocations : 00000000 PointerToLinenumbers : 00000000
 Offset : 00000140 NumberOfRelocations : 00000000 NumberOfLinenumbers : 00000000
 Offset : 00000144 Characteristics : C0000040


Section 3
 Offset : 00000148 Name : .rsrc   // 리소스 섹션
 Offset : 00000150 VirtualSize : 00008958   VirtualAddress : 0000B000
 Offset : 00000158 SizeOfRawData : 00008A00  PointerToRawData : 00008400
 Offset : 00000160 PointerToRelocations : 00000000 PointerToLinenumbers : 00000000
 Offset : 00000168 NumberOfRelocations : 00000000 NumberOfLinenumbers : 00000000
 Offset : 0000016C Characteristics : 40000040
  Physical image size is 00010E00

--->Following is import table   // import table (동적으로 로딩해야 하는 DLL 및 API 리스트)

Offset : 00006924 Characteristics : 00007990
Offset : 00006928 TimeDateStamp : FFFFFFFF
Offset : 0000692C ForwarderChain : FFFFFFFF
Offset : 00006930 Name RVA: 00007AAC Name : comdlg32.dll
Offset : 00006934 FirstThunk : 000012C4
 Ordinal : 000F  Name : PageSetupDlgW
 Ordinal : 0006  Name : FindTextW
 Ordinal : 0012  Name : PrintDlgExW
 Ordinal : 0003  Name : ChooseFontW
 Ordinal : 0008  Name : GetFileTitleW
 Ordinal : 000A  Name : GetOpenFileNameW
 Ordinal : 0015  Name : ReplaceTextW
 Ordinal : 0004  Name : CommDlgExtendedError
 Ordinal : 000C  Name : GetSaveFileNameW

Offset : 00006938 Characteristics : 00007840
Offset : 0000693C TimeDateStamp : FFFFFFFF
Offset : 00006940 ForwarderChain : FFFFFFFF
Offset : 00006944 Name RVA: 00007AFA Name : SHELL32.dll
Offset : 00006948 FirstThunk : 00001174
 Ordinal : 001F  Name : DragFinish
 Ordinal : 0023  Name : DragQueryFileW
 Ordinal : 001E  Name : DragAcceptFiles
 Ordinal : 0103  Name : ShellAboutW

Offset : 0000694C Characteristics : 00007980
Offset : 00006950 TimeDateStamp : FFFFFFFF
Offset : 00006954 ForwarderChain : FFFFFFFF
Offset : 00006958 Name RVA: 00007B3A Name : WINSPOOL.DRV
Offset : 0000695C FirstThunk : 000012B4
 Ordinal : 0078  Name : GetPrinterDriverW
 Ordinal : 001B  Name : ClosePrinter
 Ordinal : 007E  Name : OpenPrinterW

Offset : 00006960 Characteristics : 000076EC
Offset : 00006964 TimeDateStamp : FFFFFFFF
Offset : 00006968 ForwarderChain : FFFFFFFF
Offset : 0000696C Name RVA: 00007B5E Name : COMCTL32.dll
Offset : 00006970 FirstThunk : 00001020
 Ordinal : 0008  Name : CreateStatusWindowW

Offset : 00006974 Characteristics : 000079B8
Offset : 00006978 TimeDateStamp : FFFFFFFF
Offset : 0000697C ForwarderChain : FFFFFFFF
Offset : 00006980 Name RVA: 00007C76 Name : msvcrt.dll
Offset : 00006984 FirstThunk : 000012EC
 Ordinal : 004E  Name : _XcptFilter
 Ordinal : 00F6  Name : _exit
 Ordinal : 00C5  Name : _c_exit
 Ordinal : 0317  Name : time
 Ordinal : 02D4  Name : localtime
 Ordinal : 00C8  Name : _cexit
 Ordinal : 02C6  Name : iswctype
 Ordinal : 00ED  Name : _except_handler3
 Ordinal : 0274  Name : _wtol
 Ordinal : 032F  Name : wcsncmp
 Ordinal : 01E4  Name : _snwprintf
 Ordinal : 0290  Name : exit
 Ordinal : 00A8  Name : _acmdln
 Ordinal : 006D  Name : __getmainargs
 Ordinal : 013B  Name : _initterm
 Ordinal : 009A  Name : __setusermatherr
 Ordinal : 00B6  Name : _adjust_fdiv
 Ordinal : 0080  Name : __p__commode
 Ordinal : 0085  Name : __p__fmode
 Ordinal : 0098  Name : __set_app_type
 Ordinal : 00D6  Name : _controlfp
 Ordinal : 0330  Name : wcsncpy

Offset : 00006988 Characteristics : 000076CC
Offset : 0000698C TimeDateStamp : FFFFFFFF
Offset : 00006990 ForwarderChain : FFFFFFFF
Offset : 00006994 Name RVA: 00007D08 Name : ADVAPI32.dll
Offset : 00006998 FirstThunk : 00001000
 Ordinal : 01EE  Name : RegQueryValueExW
 Ordinal : 01CA  Name : RegCloseKey
 Ordinal : 01D0  Name : RegCreateKeyW
 Ordinal : 0139  Name : IsTextUnicode
 Ordinal : 01ED  Name : RegQueryValueExA
 Ordinal : 01E3  Name : RegOpenKeyExA
 Ordinal : 01FB  Name : RegSetValueExW

Offset : 0000699C Characteristics : 00007758
Offset : 000069A0 TimeDateStamp : FFFFFFFF
Offset : 000069A4 ForwarderChain : FFFFFFFF
Offset : 000069A8 Name RVA: 000080EC Name : KERNEL32.dll
Offset : 000069AC FirstThunk : 0000108C
 Ordinal : 013E  Name : GetCurrentThreadId
 Ordinal : 01D1  Name : GetTickCount
 Ordinal : 0291  Name : QueryPerformanceCounter
 Ordinal : 016A  Name : GetLocalTime
 Ordinal : 01D5  Name : GetUserDefaultLCID
 Ordinal : 0140  Name : GetDateFormatW
 Ordinal : 01D3  Name : GetTimeFormatW
 Ordinal : 01F5  Name : GlobalLock
 Ordinal : 01FC  Name : GlobalUnlock
 Ordinal : 015A  Name : GetFileInformationByHandle
 Ordinal : 0051  Name : CreateFileMappingW
 Ordinal : 01BD  Name : GetSystemTimeAsFileTime
 Ordinal : 0346  Name : TerminateProcess
 Ordinal : 013B  Name : GetCurrentProcess
 Ordinal : 0332  Name : SetUnhandledExceptionFilter
 Ordinal : 0241  Name : LoadLibraryA
 Ordinal : 0175  Name : GetModuleHandleA
 Ordinal : 01AC  Name : GetStartupInfoA
 Ordinal : 01F1  Name : GlobalFree
 Ordinal : 016C  Name : GetLocaleInfoW
 Ordinal : 024B  Name : LocalFree
 Ordinal : 0247  Name : LocalAlloc
 Ordinal : 03B4  Name : lstrlenW
 Ordinal : 0251  Name : LocalUnlock
 Ordinal : 0038  Name : CompareStringW
 Ordinal : 024D  Name : LocalLock
 Ordinal : 00EA  Name : FoldStringW
 Ordinal : 0031  Name : CloseHandle
 Ordinal : 03AE  Name : lstrcpyW
 Ordinal : 02A3  Name : ReadFile
 Ordinal : 0052  Name : CreateFileW
 Ordinal : 03AB  Name : lstrcmpiW
 Ordinal : 013C  Name : GetCurrentProcessId
 Ordinal : 0197  Name : GetProcAddress
 Ordinal : 010A  Name : GetCommandLineW
 Ordinal : 03A5  Name : lstrcatW
 Ordinal : 00CC  Name : FindClose
 Ordinal : 00D3  Name : FindFirstFileW
 Ordinal : 0159  Name : GetFileAttributesW
 Ordinal : 03A8  Name : lstrcmpW
 Ordinal : 0263  Name : MulDiv
 Ordinal : 03B1  Name : lstrcpynW
 Ordinal : 0250  Name : LocalSize
 Ordinal : 0168  Name : GetLastError
 Ordinal : 038B  Name : WriteFile
 Ordinal : 0313  Name : SetLastError
 Ordinal : 037E  Name : WideCharToMultiByte
 Ordinal : 024E  Name : LocalReAlloc
 Ordinal : 00EC  Name : FormatMessageW
 Ordinal : 01D7  Name : GetUserDefaultUILanguage
 Ordinal : 02FD  Name : SetEndOfFile
 Ordinal : 0082  Name : DeleteFileW
 Ordinal : 00F6  Name : GetACP
 Ordinal : 035A  Name : UnmapViewOfFile
 Ordinal : 0264  Name : MultiByteToWideChar
 Ordinal : 0257  Name : MapViewOfFile
 Ordinal : 0357  Name : UnhandledExceptionFilter

Offset : 000069B0 Characteristics : 000076F4
Offset : 000069B4 TimeDateStamp : FFFFFFFF
Offset : 000069B8 ForwarderChain : FFFFFFFF
Offset : 000069BC Name RVA: 0000825E Name : GDI32.dll
Offset : 000069C0 FirstThunk : 00001028
 Ordinal : 0098  Name : EndPage
 Ordinal : 0000  Name : AbortDoc
 Ordinal : 0096  Name : EndDoc
 Ordinal : 008C  Name : DeleteDC
 Ordinal : 0248  Name : StartPage
 Ordinal : 01B5  Name : GetTextExtentPoint32W
 Ordinal : 002F  Name : CreateDCW
 Ordinal : 0210  Name : SetAbortProc
 Ordinal : 01BB  Name : GetTextFaceW
 Ordinal : 024F  Name : TextOutW
 Ordinal : 0246  Name : StartDocW
 Ordinal : 00CE  Name : EnumFontsW
 Ordinal : 01A5  Name : GetStockObject
 Ordinal : 0197  Name : GetObjectW
 Ordinal : 016B  Name : GetDeviceCaps
 Ordinal : 003D  Name : CreateFontIndirectW
 Ordinal : 008F  Name : DeleteObject
 Ordinal : 01BD  Name : GetTextMetricsW
 Ordinal : 0216  Name : SetBkMode
 Ordinal : 01CB  Name : LPtoDP
 Ordinal : 0242  Name : SetWindowExtEx
 Ordinal : 023E  Name : SetViewportExtEx
 Ordinal : 022B  Name : SetMapMode
 Ordinal : 020E  Name : SelectObject

Offset : 000069C4 Characteristics : 00007854
Offset : 000069C8 TimeDateStamp : FFFFFFFF
Offset : 000069CC ForwarderChain : FFFFFFFF
Offset : 000069D0 Name RVA: 0000873C Name : USER32.dll
Offset : 000069D4 FirstThunk : 00001188
 Ordinal : 00FF  Name : GetClientRect
 Ordinal : 024D  Name : SetCursor
 Ordinal : 022A  Name : ReleaseDC
 Ordinal : 010C  Name : GetDC
 Ordinal : 009F  Name : DialogBoxParamW
 Ordinal : 0243  Name : SetActiveWindow
 Ordinal : 0122  Name : GetKeyboardLayout
 Ordinal : 008F  Name : DefWindowProcW
 Ordinal : 0099  Name : DestroyWindow
 Ordinal : 01DB  Name : MessageBeep
 Ordinal : 0292  Name : ShowWindow
 Ordinal : 0117  Name : GetForegroundWindow
 Ordinal : 01A6  Name : IsIconic
 Ordinal : 0173  Name : GetWindowPlacement
 Ordinal : 0037  Name : CharUpperW
 Ordinal : 01C9  Name : LoadStringW
 Ordinal : 01B4  Name : LoadAcceleratorsW
 Ordinal : 015C  Name : GetSystemMenu
 Ordinal : 0218  Name : RegisterClassExW
 Ordinal : 01BE  Name : LoadImageW
 Ordinal : 01BA  Name : LoadCursorW
 Ordinal : 0282  Name : SetWindowPlacement
 Ordinal : 0061  Name : CreateWindowExW
 Ordinal : 010E  Name : GetDesktopWindow
 Ordinal : 0116  Name : GetFocus
 Ordinal : 01BC  Name : LoadIconW
 Ordinal : 0287  Name : SetWindowTextW
 Ordinal : 0201  Name : PostQuitMessage
 Ordinal : 0228  Name : RegisterWindowMessageW
 Ordinal : 02BB  Name : UpdateWindow
 Ordinal : 026F  Name : SetScrollPos
 Ordinal : 0029  Name : CharLowerW
 Ordinal : 01FE  Name : PeekMessageW
 Ordinal : 00C4  Name : EnableWindow
 Ordinal : 00BE  Name : DrawTextExW
 Ordinal : 0056  Name : CreateDialogParamW
 Ordinal : 017A  Name : GetWindowTextW
 Ordinal : 015D  Name : GetSystemMetrics
 Ordinal : 01E9  Name : MoveWindow
 Ordinal : 0193  Name : InvalidateRect
 Ordinal : 02D3  Name : WinHelpW
 Ordinal : 0110  Name : GetDlgCtrlID
 Ordinal : 003C  Name : ChildWindowFromPoint
 Ordinal : 0231  Name : ScreenToClient
 Ordinal : 010B  Name : GetCursorPos
 Ordinal : 0237  Name : SendDlgItemMessageW
 Ordinal : 0240  Name : SendMessageW
 Ordinal : 002C  Name : CharNextW
 Ordinal : 0039  Name : CheckMenuItem
 Ordinal : 0042  Name : CloseClipboard
 Ordinal : 019F  Name : IsClipboardFormatAvailable
 Ordinal : 01F3  Name : OpenClipboard
 Ordinal : 0137  Name : GetMenuState
 Ordinal : 00C2  Name : EnableMenuItem
 Ordinal : 0159  Name : GetSubMenu
 Ordinal : 012C  Name : GetMenu
 Ordinal : 01E3  Name : MessageBoxW
 Ordinal : 0281  Name : SetWindowLongW
 Ordinal : 016F  Name : GetWindowLongW
 Ordinal : 0111  Name : GetDlgItem
 Ordinal : 0256  Name : SetFocus
 Ordinal : 0254  Name : SetDlgItemTextW
 Ordinal : 02D9  Name : wsprintfW
 Ordinal : 0114  Name : GetDlgItemTextW
 Ordinal : 00C6  Name : EndDialog
 Ordinal : 0145  Name : GetParent
 Ordinal : 02AC  Name : UnhookWinEvent
 Ordinal : 00A2  Name : DispatchMessageW
 Ordinal : 02AA  Name : TranslateMessage
 Ordinal : 02A8  Name : TranslateAcceleratorW
 Ordinal : 01A2  Name : IsDialogMessageW
 Ordinal : 0200  Name : PostMessageW
 Ordinal : 013E  Name : GetMessageW
 Ordinal : 027E  Name : SetWinEventHook


There is no Export table  // Export table. 외부로 export 하는 API 리스트 정보

'Programming' 카테고리의 다른 글

응답 없음(Not Responding)  (0) 2009.05.11
I/O Stack Locations  (0) 2009.05.04
Example I/O request - an overview  (0) 2009.04.21
On-demand 메모리 관리  (0) 2009.04.02
Thread States  (0) 2009.03.27
Posted by noenemy
,

WDK 문서 중에서 I/O Request의 전체 과정을 설명하고 있는 문서가 있어서 이를 번역해 보았습니다. 번역하면서 느낀 것은 역시 기술 문서는 원어 그대로 읽고 이해하는 것이 번역된 문장을 보는 것보다 낫다는 생각이 듭니다. 가능하면 의역을 하려 했는데, 부족한 부분이 있거나 원문 내용을 보시려면 아래 문서를 참고하시기 바랍니다.

Example I/O Request - An overview
http://msdn.microsoft.com/en-us/library/ms795837.aspx

다음 그림은 user mode 어플리케이션에서 특정 file을 open하려고 했을 때 이 요청이 어떻게 커널 레벨에서 file system에 까지 전달된 뒤 그 결과 값으로 handle을 돌려 받는 과정을 설명하고 있습니다.


ms795837.2OPENDEV(en-us,MSDN.10).png

  1. Windows subsystem이 특정 이름의 파일을 open 하기 위해 I/O system service를 호출한다.
  2. I/O manager는 해당 파일이 있는지 찾고 해당 file object에 대한 symbolic links를 resolve할 수 있도록 object manager를 호출한다. 또한 subsystem이 해당 file object를 open하는데 필요한 적절한 권한을 가지고 있는지 체크하기 위해 security reference manager를 호출한다.
  3. 해당 volume이 아직 마운트되지 않았다면, I/O manager는 open 요청을 잠시 보류하고 해당 해당 file object가 인식될 때까지 여러 file system을 호출한다. 해당 file system이 마운트되면, I/O manager는 open 요청을 다시 재진행한다.
  4. I/O manager는 open 요청을 위한 IRP를 초기화하고 메모리를 할당한다. 드라이버 입장에서 'open'은 "create" 요청에 해당한다.
  5. I/O manager는 파일 시스템 드라이버를 호출하고 해당 IRP를 전달한다. 파일 시스템 드라이버는 어떠한 명령을 수행해야 하는지 판단하기 위해서 해당 IRP의 I/O stack location에 접근하고, 만약 없다면 해당 IRP의 하위 드라이버의 I/O stack location을 설정한다.
  6. 해당 IRP를 처리하고 요청된 I/O 명령을 complete 하려면 I/O manager나 다른 시스템 구성요소에 의해 제공되는 커널모드 루틴을 호출해야 한다.
  7. 드라이버는 요청한 명령에 대해 성공했거나 또는 실패한 이유를 I/O status block을 set하고 해당 IRP를 I/O manager에게 리턴한다.
  8. I/O manager는 해당 IRP로부터 I/O status 값을 얻고, 이를  subsystem을 통해 최초 호출자에게 상태 값을 리턴할 수 있게 된다.
  9. I/O manager는 completed된 IRP를 해제한다.
  10. I/O manager는 open 명령이 성공했다면 subsystem에게 해당 file object를 위한 handle을 리턴한다. 만약 실패하였다면 subsystem에게 적절한 status를 리턴한다.

Windows subsystem이 특정 파일에 대한 file object를 open 하는데 성공하면, 그 이후에는 read, write, device I/O 요청과 같은 부수적인 요청에는 리턴된 handle을 이용하게 된다. 이런 요청을 처리하기 위해서 subsystem은 I/O sysem services를 호출하고, I/O manager는 이를 처리할 드라이버에게 IRPs 보냄으로써 이러한 요청을 전달한다.


'Programming' 카테고리의 다른 글

I/O Stack Locations  (0) 2009.05.04
PEVIEWER로 살펴보는 notepad.exe  (1) 2009.04.30
On-demand 메모리 관리  (0) 2009.04.02
Thread States  (0) 2009.03.27
Windbg Stack Backtracing 명령어  (0) 2009.03.27
Posted by noenemy
,

오늘은 Windows 운영체제의 메모리 관리 방법에 대한 일반적인 얘기를 조금 나눠볼까 합니다.

이미 많은 분들이 아시다시피 x86 기반의 Windows에서는 각 프로세스별로 4GB의 virtual memory를 사용할 수 있도록 할당을 받습니다. 이 중에서 낮은 2GB의 주소(0x000000~0x7FFFFFFF)에 해당하는 공간이 User Mode 영역이고, 높은 2GB의 주소(0x80000000~0xFFFFFFFF)의 공간은 Kernel 영역에서 사용하게 됩니다. User Mode 영역의 경우 각 프로세스별로 독립적인 공간으로 다른 프로세스와는 전혀 다른 공간을 이용하게 되고, Kernel Mode 영역에 해당하는 2GB의 공간은 프로세스의 구분 없이 공유해서 사용되는 공간입니다.

지금은 하드웨어 기술이 발달해서 실제 개인용 PC에서도 물리적인 메모리를 수 GB 이상 갖춘 컴퓨터를 주변에서 흔히 볼 수 있지만, 이러한 virtual memory 개념이 처음 적용되는 당시만해도 128MB 정도의 메모리를 갖춘 컴퓨터도 흔치 않았던 시절입니다. 논리적으로는 프로세스별로 4GB의 메모리 공간을 사용할 수 있도록 허용하고 있지만, 실제로 이 정도의 공간을 필요로하는 응용 프로그램은 특수한 목적의 프로그램이 아닌 이상 거의 보기가 힘들다고 볼 수 있습니다.

Windows 운영체제는 응용 프로그램 뿐만 아니라 운영체제 자신에게도 메모리 사용에 있어서는 절약정신이 매우 투철한데 이를 간단히 'On-demand'로 관리한다라고 표현할 수 있습니다. 즉, '진짜 필요로 할 때에만 물리적인 메모리를 할당해준다'라고 표현할 수 있겠습니다.

우선 물리적 메모리는 x86 장비의 경우 4Kb 크기를 가지는 페이지(page) 단위로 잘게 나누어져 있는데 이를 응용 프로그램에서 사용하기 위해서는 먼저 VirtualAlloc이나 VirtualAllocEx 라는 API를 이용해서 사용하고자 하는 메모리 양을 운영체제에 알리도록 되어 있습니다. 이때 reserve와 commit이라는 2 단계로 구분해서 진행하게 되는데, reserved memory는 해당 프로세스 내의 쓰레드에 의해서 나중에 이 정도의 공간이 사용될 것이므로 '예약'만 해두는 것을 의미합니다. '예약'된 메모리는 아직 사용될 수는 없으며 실제 사용하려면 reserved 된 영역에 대해서 다시 'commit'을 해야 실제로 해당 프로세스에서 사용할 메모리 영역을 할당받고 사용 할 수 있게 됩니다.

이러한 과정은 마치 비행기의 자리를 예약하는 것에 비유할 수 있을 것입니다. 일반적으로 항공사는 특정 항공편에 대해서 120% 정도로 over booking을 받습니다. 이는 실제로 비행기표를 예약했다가 실제로 이륙하기 전에 예약을 취소하거나 탑승하지 못하는 고객이 있기 때문에 이를 대비해서 미리 예약을 실제 좌석수보다 넘게 받은 다음에, 실제로 탑승 수속(check in)을 하는 시점에 실제 좌석을 할당함으로써 빈 좌석으로 운항되는 것을 최대한 방지하려고 합니다. 비행기 표를 예약(reserve)하는 것과 탑승 수속시(commit)에 실제 좌석을 할당 받는 과정을 이러한 개념으로 이해하시면 조금 도움이 되실 것 같습니다.

그런데 운영체제 내부적으로 조금 더 깊게 들어가보면 commit 한 시점에서도 실제로 해당 프로세스에 물리적인 메모리 공간을 할당해주는 것은 아닙니다. 실제로 메모리에 접근할 수 있게 해주는 것은 그것보다도 더 이후로 미뤄둠으로써 메모리가 낭비되는 것을 한번 더 관리하게 되는데, 이는 commit 된 메모리에 실제로 해당 응용 프로그램에서 접근(access)하려는 시점에서야 물리적인 메모리의 특정 페이지를 사용할 수 있도록 허락해 줍니다.

mamory manager에서 물리적 메모리의 page는 프로세스의 가상 주소와 서로 맵핑 시켜주기 위해서 PTE(Page Table Entry)라는 구조체를 사용하는데, 실제로 이러한 PTE가 생성되어서 물리적 메모리가 mapping 되는 것은 최초로 commit된 영역에 대해서 접근하려는 순간에 이뤄집니다. 내부적으로는 commit된 page를 접근하려는 데 아직 PTE가 생성되지 않은 단계이므로 page fault가 발생하게 됩니다. 이 page fault에 대해서 운영체제는 '아.. 이제 진짜 메모리를 쓰려고 하는구나. 이젠 메모리를 사용할 수 있게 해줘야 겠군. 그럼 이 page를 사용해라'라고 PTE를 만들고 물리적 메모리를 맵핑시켜 줍니다.

물리적인 메모리의 사용을 가능한 뒤로 연기해 둠으로써 시스템의 메모리 자원을 절약하고자 하는 컨셉. 이것이 windows의 on-demand 메모리 관리 컨셉입니다.

'Programming' 카테고리의 다른 글

PEVIEWER로 살펴보는 notepad.exe  (1) 2009.04.30
Example I/O request - an overview  (0) 2009.04.21
Thread States  (0) 2009.03.27
Windbg Stack Backtracing 명령어  (0) 2009.03.27
Intel CPU registers  (0) 2009.03.20
Posted by noenemy
,

Thread States

Programming 2009. 3. 27. 14:54

현재 운영체제를 사용하면서 여러 가지 응용 프로그램을 동시에 사용하고 있습니다. 문서 작성을 하면서 음악을 듣고 인터넷 서핑을 동시에 진행할 수 있습니다. 하지만 CPU에서는 원래 동시에 하나의 작업만을 진행할 수 있는데, 시스템에서 실행되어야 하는 여러 작업들을 thread라는 최소 실행단위로 나누고 이러한 여러 thread를 매우 빠른 속도로 나눠서 실행하면서 마치 동시에 여러 프로그램이 실행되는 듯한 효과를 얻을 수 있는 것입니다. 따라서 thread는 시스템을 구성하는 여러 요소 중에서도 실행과 관련된 매우 중요한 역할을 담당하고 있습니다.

 

이러한 thread 들은 생성되고 실행된 뒤 소멸될 때까지 Initialized, Ready, Standby, Runnign, Waiting, Transition, Terminated, Deferred Ready(2003 이후)와 같은 여러 상태 값을 가지게 됩니다. System Dispatcher에 의해서 여러 thread 들의 상태 값을 관리하고 실행될 thread를 선택하는 과정이 이루어 지는데 내부적으로 매우 복잡한 알고리즘에 의해서 운영되고 있습니다. 여기서는 thread의 상태 값들에 대한 의미를 정리해보도록 하겠습니다.

 

 

 

각 State에 대한 의미

 

l  Ready

n  Thread가 실행되기를 기다리는 상태. Thread dispatcher는 다음에 실행할 thread를 이러한 ready state에 있는 thread list에서 찾게 된다.

n  Ready list에 있는 thread 중에서 Priority가 높은 thread 부터 먼저 실행될 대상이 된다.

 

l  Standby

n  현재 CPU에 의해서 실행 중인 thread 다음에 바로 실행될 수 있도록 선택된 thread이다. CPU당 하나의 thread standby 상태에 있을 수 있으며, 이는 ready state에 있는 여러 thread 중에서 선택된다.

 

l  Running

n  현재 CPU에 의해서 실행 중인 thread이다. 일단 Standby 상태에 있던 thread가 실행 상태가 되면 해당 thread가 실행 될 수 있도록 주어진 기간을 의미하는 퀀텀(quantum) 동안 또는 보다 높은 우선순위의 다른 thread에 의해서 선점(preempted)되기 전까지는 실행 상태를 계속 유지하게 된다. 실행이 완료되면 Ready List에 다시 들어가게 된다.

 

l  Waiting

n  Ready List에 포함되지 않고 대기 상태에 있는 thread를 의미한다. 특정 kernel object signaled 상태가 될 때까지 대기하기 위해서 WaitForSingleObject WaitForMultipleObject API를 호출하면 해당 thread Waiting 상태에 들어가게 되며 Dispatcher에서 관리하는 thread scheduling의 대상에서 제외된다. , waiting 상태에서 깨어나기 전에서 CPU에 의해서 실행되지 않는다.

 

l  Transition

n  Waiting 상태에 있다가 실행될 수 있는 Ready 상태가 되려고 하지만 해당 thread kernel stack page out 된 경우에 해당한다. Kernel stack 영역이 다시 메모리로 page in 되면 비로소 Ready 상태가 된다.

 

l  Terminated

n  특정 thread의 실행이 종료되면 terminated 상태가 된다.

 

l  Initialized

n  Thread가 생성되어 운영체제 내부적으로 초기화하고 있는 과정에 해당한다.

 

참고로 Windows Server 2003 이후 운영체제에서는  시스템이 thread scheduling을 위해서 시스템 전체 범위의 lock을 최소화해서 성능 향상을 도모하고자 위에서 설명한 state 외에 Deferred Ready라는 상태 값이 하나 더 추가 되었습니다.

 

WinDbg에서 !thread 명령어를 이용하면 특정 thread의 state 값을 아래와 같이 확인이 가능합니다.

1: kd> !thread 86c02020
THREAD 86c02020  Cid 0004.0030  Teb: 00000000 Win32Thread: 00000000
RUNNING on processor 1
IRP List:
    a7834eb8: (0006,0148) Flags: 00000000  Mdl: 00000000
Not impersonating
DeviceMap                 83207d08
Owning Process            0       Image:         <Unknown>
Attached Process          86b97720       Image:         System
Wait Start TickCount      107517         Ticks: 0
Context Switch Count      50347            
UserTime                  00:00:00.000
KernelTime                00:00:05.085
Win32 Start Address nt!ExpWorkerThread (0x82078ea7)
Stack Init 83094000 Current 83093570 Base 83094000 Limit 83091000 Call 0
Priority 13 BasePriority 12 PriorityDecrement 0 IoPriority 2 PagePriority 5

 

1: kd> !thread 89af7030 
THREAD 89af7030  Cid 0004.11e4  Teb: 00000000 Win32Thread: 00000000 WAIT: (Executive) KernelMode Non-Alertable
    820fae94  NotificationEvent
    820fae84  NotificationEvent
    820fae0c  NotificationEvent
    820faeb8  NotificationEvent
Not impersonating
DeviceMap                 83207d08
Owning Process            0       Image:         <Unknown>
Attached Process          86b97720       Image:         System
Wait Start TickCount      107512         Ticks: 5 (0:00:00:00.078)
Context Switch Count      23402            
UserTime                  00:00:00.000
KernelTime                00:00:03.463
Win32 Start Address nt!PfTLoggingWorker (0x821fffaf)
Stack Init 9d085000 Current 9d084c28 Base 9d085000 Limit 9d082000 Call 0
Priority 13 BasePriority 7 PriorityDecrement 6 IoPriority 2 PagePriority 5

 

'Programming' 카테고리의 다른 글

Example I/O request - an overview  (0) 2009.04.21
On-demand 메모리 관리  (0) 2009.04.02
Windbg Stack Backtracing 명령어  (0) 2009.03.27
Intel CPU registers  (0) 2009.03.20
Windbg Remote debugging 설정 방법  (0) 2009.03.20
Posted by noenemy
,

thread별로 로컬 변수나 함수 호출간에 전달할 파라미터 정보를 저장하기 위한 임시 공간으로서 thread stack을 사용할 수 있는데, thread의 특정 시점에 사용 중이던 stack 정보를 역으로 추적해서 현재 실행 중인 함수의 위치까지 실행된 과정을 call stack으로 보여주고, 각 함수에 전달된 파라미터 정보를 확인하는 과정을 Stack backtracing이라고 합니다. 다음에 기회가 되면 이와 관련된 함수 호출 규약(function calling convention) stack 사용 및 해제하는 과정을 다루도록 하겠습니다.

 

어느 debugger를 이용하든지 thread stack backtracing 을 해서 stack frame 정보를 확인하는 것이 live debugging이나 post mortem debugging에 필수 과정이라고 할 수 있습니다. 특히 windbg의 경우 stack backtracing을 위해 k 명령어를 제공하고 있는데 파라미터에 따라서 그 사용법이 매우 다양하기 때문에 어떤 것을 사용해야 되는지 헷갈리는 경우가 많습니다. 제 경우에도 주로 사용하는 몇 개의 명령어만 쓰고 있는데 이번 기회에 세부 사용법을 정리해보도록 하겠습니다. 일단 이것 저것 많이 써보면서 몸으로 익히는 게 중요할 것 같습니다.

 

세부 내용은 WinDbg의 매뉴얼을 참고 했습니다.

 

구문

*유저모드에서

[~Thread] k[b|p|P|v] [n] [f] [L] [FrameCount]
[~Thread] k[b|p|P|v] [n] [f] [L] = BasePtr [FrameCount]
[~Thread] k[b|p|P|v] [n] [f] [L] = BasePtr StackPtr InstructionPtr 
[~Thread] kd [WordCount]

*커널모드에서

[Processor] k[b|p|P|v] [n] [f] [L] [FrameCount]
[Processor] k[b|p|P|v] [n] [f] [L] = BasePtr [FrameCount]
[Processor] k[b|p|P|v] [n] [f] [L] = BasePtr StackPtr InstructionPtr 
[Processor] kd [WordCount]

파라미터

 

b: 각 함수에 전달된 파라미터를 3개씩 보기

p: 함수에 전달된 모든 파라미터 보기

P: 함수에 전달된 모든 파라미터 보기. 줄바꿈을 이용해서 보여준다.

v: FPO(Frame Pointer Omission) 정보 보기

n: frame number 보기

f: frame 사이의 간격(실제 스택상에서 해당 frame bytes ) 보기

L: 소스 상에서의 라인 번호 숨기기

 

실제 예

동일한 thread에서 k 명령어의 사용에 따른 차이점을 아래 예를 통해서 확인할 수 있습니다.

 

1: kd> k

ChildEBP RetAddr 

83093868 8208fbf4 nt!MmAccessFault+0x106 [d:\vista_ldr\base\ntos\mm\mmfault.c @ 251]

83093868 82091f60 nt!_KiTrap0E+0xdc [d:\vista_ldr\base\ntos\ke\i386\trap.asm @ 5651]

830938f0 8fc9dbbb nt!Exfi386InterlockedExchangeUlong [d:\vista_ldr\base\ntos\ex\i386\intrlfst.asm @ 782]

83093904 8204b2e7 C7xUSBV3!powerWaitWakeCallback+0x1b [c:\projects\wibro\usb\xp\miniport.08.06.24_328\power.c @ 399]

8309392c 822cec69 nt!PopRequestCompletion+0x38 [d:\vista_ldr\base\ntos\po\pocall.c @ 349]

83093964 820acc1b nt!IovpLocalCompletionRoutine+0xcc [d:\vista_ldr\base\ntos\io\iomgr\ioverifier.c @ 1028]

8309399c 822ceb53 nt!IopfCompleteRequest+0x11d [d:\vista_ldr\base\ntos\io\iomgr\iosubs.c @ 3848]

83093a0c 8f7431f4 nt!IovCompleteRequest+0x11c [d:\vista_ldr\base\ntos\io\iomgr\ioverifier.c @ 946]

83093a20 8f74a7f6 usbhub!UsbhCompletePdoWakeIrp+0xaf [d:\vistartm\drivers\wdm\usb\hub\usbhub\pdopwr.c @ 1864]

83093a40 8f74a9e0 usbhub!UsbhPdoRemoveCleanup+0x3f [d:\vistartm\drivers\wdm\usb\hub\usbhub\pdo.c @ 1267]

83093a58 8f72e41e usbhub!UsbhPdoPnp_RemoveDevice+0x41 [d:\vistartm\drivers\wdm\usb\hub\usbhub\pdo.c @ 2152]

83093a74 8f726c92 usbhub!UsbhPdoPnp+0x78 [d:\vistartm\drivers\wdm\usb\hub\usbhub\pnp.c @ 1759]

83093a88 822ce681 usbhub!UsbhGenDispatch+0x4a [d:\vistartm\drivers\wdm\usb\hub\usbhub\hub.c @ 1116]

83093aac 82027f1c nt!IovCallDriver+0x252 [d:\vista_ldr\base\ntos\io\iomgr\ioverifier.c @ 574]

83093ac0 825cfce9 nt!IofCallDriver+0x1b [d:\vista_ldr\base\ntos\io\iomgr\iosubs.c @ 2370]

83093b08 822ce681 ndis!ndisPnPDispatch+0x4a4 [d:\vistartm\net\ndis\sys\ndispnp.c @ 1426]

83093b2c 82027f1c nt!IovCallDriver+0x252 [d:\vista_ldr\base\ntos\io\iomgr\ioverifier.c @ 574]

83093b40 821af73b nt!IofCallDriver+0x1b [d:\vista_ldr\base\ntos\io\iomgr\iosubs.c @ 2370]

83093b74 821af9a3 nt!IopSynchronousCall+0xce [d:\vista_ldr\base\ntos\io\pnpmgr\irp.c @ 215]

83093bd0 82006592 nt!IopRemoveDevice+0xd5 [d:\vista_ldr\base\ntos\io\pnpmgr\irp.c @ 663]

 

1: kd> kd 5

83093868  83093880

8309386c  8208fbf4 nt!_KiTrap0E+0xdc [d:\vista_ldr\base\ntos\ke\i386\trap.asm @ 5651]

83093870  00000001

83093874  9112ee50

83093878  00000000

 

1: kd> kbL

ChildEBP RetAddr  Args to Child             

83093868 8208fbf4 00000001 9112ee50 00000000 nt!MmAccessFault+0x106

83093868 82091f60 00000001 9112ee50 00000000 nt!_KiTrap0E+0xdc

830938f0 8fc9dbbb a78aae90 898b30b0 a78aaf00 nt!Exfi386InterlockedExchangeUlong

83093904 8204b2e7 89683700 898b3000 00000004 C7xUSBV3!powerWaitWakeCallback+0x1b

8309392c 822cec69 00000000 a78aae90 898b3000 nt!PopRequestCompletion+0x38

83093964 820acc1b 00000000 a78aae90 830939d4 nt!IovpLocalCompletionRoutine+0xcc

8309399c 822ceb53 a78aae90 896837b8 c0000120 nt!IopfCompleteRequest+0x11d

83093a0c 8f7431f4 c0000120 896837b8 89683700 nt!IovCompleteRequest+0x11c

83093a20 8f74a7f6 89522028 89683700 c0000120 usbhub!UsbhCompletePdoWakeIrp+0xaf

83093a40 8f74a9e0 89683700 a7834eb8 a7834fdc usbhub!UsbhPdoRemoveCleanup+0x3f

83093a58 8f72e41e 89683700 a7834eb8 a7834eb8 usbhub!UsbhPdoPnp_RemoveDevice+0x41

83093a74 8f726c92 89683700 a7834eb8 89683700 usbhub!UsbhPdoPnp+0x78

83093a88 822ce681 89683700 a7834eb8 a7834eb8 usbhub!UsbhGenDispatch+0x4a

83093aac 82027f1c 825cfce9 00000000 89683700 nt!IovCallDriver+0x252

83093ac0 825cfce9 a7834eb8 892f6030 875fce30 nt!IofCallDriver+0x1b

83093b08 822ce681 892f6030 00000002 a7835000 ndis!ndisPnPDispatch+0x4a4

83093b2c 82027f1c 821af73b 83093bcc 892f6030 nt!IovCallDriver+0x252

83093b40 821af73b 89683700 89301ea8 89683700 nt!IofCallDriver+0x1b

83093b74 821af9a3 89683700 83093ba8 00000000 nt!IopSynchronousCall+0xce

83093bd0 82006592 89683700 00000002 9f7d2510 nt!IopRemoveDevice+0xd5

 

1: kd> kvL

ChildEBP RetAddr  Args to Child             

83093868 8208fbf4 00000001 9112ee50 00000000 nt!MmAccessFault+0x106 (CONV: stdcall)

83093868 82091f60 00000001 9112ee50 00000000 nt!_KiTrap0E+0xdc (FPO: [0,0] TrapFrame @ 83093880) (CONV: cdecl)

830938f0 8fc9dbbb a78aae90 898b30b0 a78aaf00 nt!Exfi386InterlockedExchangeUlong (FPO: [0,0,0])

83093904 8204b2e7 89683700 898b3000 00000004 C7xUSBV3!powerWaitWakeCallback+0x1b (FPO: [Non-Fpo]) (CONV: stdcall)

8309392c 822cec69 00000000 a78aae90 898b3000 nt!PopRequestCompletion+0x38 (CONV: stdcall)

83093964 820acc1b 00000000 a78aae90 830939d4 nt!IovpLocalCompletionRoutine+0xcc (CONV: stdcall)

8309399c 822ceb53 a78aae90 896837b8 c0000120 nt!IopfCompleteRequest+0x11d (CONV: fastcall)

83093a0c 8f7431f4 c0000120 896837b8 89683700 nt!IovCompleteRequest+0x11c (CONV: fastcall)

83093a20 8f74a7f6 89522028 89683700 c0000120 usbhub!UsbhCompletePdoWakeIrp+0xaf (FPO: [Non-Fpo]) (CONV: stdcall)

83093a40 8f74a9e0 89683700 a7834eb8 a7834fdc usbhub!UsbhPdoRemoveCleanup+0x3f (FPO: [Non-Fpo]) (CONV: stdcall)

83093a58 8f72e41e 89683700 a7834eb8 a7834eb8 usbhub!UsbhPdoPnp_RemoveDevice+0x41 (FPO: [Non-Fpo]) (CONV: stdcall)

83093a74 8f726c92 89683700 a7834eb8 89683700 usbhub!UsbhPdoPnp+0x78 (FPO: [Non-Fpo]) (CONV: stdcall)

83093a88 822ce681 89683700 a7834eb8 a7834eb8 usbhub!UsbhGenDispatch+0x4a (FPO: [Non-Fpo]) (CONV: stdcall)

83093aac 82027f1c 825cfce9 00000000 89683700 nt!IovCallDriver+0x252 (CONV: fastcall)

83093ac0 825cfce9 a7834eb8 892f6030 875fce30 nt!IofCallDriver+0x1b (CONV: fastcall)

83093b08 822ce681 892f6030 00000002 a7835000 ndis!ndisPnPDispatch+0x4a4 (FPO: [Non-Fpo]) (CONV: stdcall)

83093b2c 82027f1c 821af73b 83093bcc 892f6030 nt!IovCallDriver+0x252 (CONV: fastcall)

83093b40 821af73b 89683700 89301ea8 89683700 nt!IofCallDriver+0x1b (CONV: fastcall)

83093b74 821af9a3 89683700 83093ba8 00000000 nt!IopSynchronousCall+0xce (CONV: stdcall)

83093bd0 82006592 89683700 00000002 9f7d2510 nt!IopRemoveDevice+0xd5 (CONV: stdcall)

 

1: kd> kpL

ChildEBP RetAddr 

83093868 8208fbf4 nt!MmAccessFault(unsigned long FaultStatus = 1, void * VirtualAddress = 0x9112ee50, char PreviousMode = 0 '', void * TrapInformation = 0x83093880)+0x106

83093868 82091f60 nt!_KiTrap0E(void)+0xdc

830938f0 8fc9dbbb nt!Exfi386InterlockedExchangeUlong(void)

83093904 8204b2e7 C7xUSBV3!powerWaitWakeCallback(struct _DEVICE_OBJECT * Object = 0x89683700, unsigned char MinorFunction = 0x00 '', union _POWER_STATE PowerState = union _POWER_STATE, struct _MINIPORT_ADAPTER * Adapter = 0x9112eb50, struct _IO_STATUS_BLOCK * IoStatus = 0xa78aaea8)+0x1b

8309392c 822cec69 nt!PopRequestCompletion(struct _DEVICE_OBJECT * DeviceObject = 0x00000000, struct _IRP * Irp = 0xa78aae90, void * Context = 0x898b3000)+0x38

83093964 820acc1b nt!IovpLocalCompletionRoutine(struct _DEVICE_OBJECT * DeviceObject = 0x00000000, struct _IRP * Irp = 0xa78aae90, void * Context = 0x830939d4)+0xcc

8309399c 822ceb53 nt!IopfCompleteRequest(struct _IRP * Irp = 0x9112ee50, char PriorityBoost = 0 '')+0x11d

83093a0c 8f7431f4 nt!IovCompleteRequest(struct _IRP * Irp = 0x9112ee50, char PriorityBoost = 0 '')+0x11c

83093a20 8f74a7f6 usbhub!UsbhCompletePdoWakeIrp(struct _DEVICE_OBJECT * HubFdo = 0x89522028, struct _DEVICE_OBJECT * Pdo = 0x89683700, long NtStatus = -1073741536)+0xaf

83093a40 8f74a9e0 usbhub!UsbhPdoRemoveCleanup(struct _DEVICE_OBJECT * Pdo = 0x89683700)+0x3f

83093a58 8f72e41e usbhub!UsbhPdoPnp_RemoveDevice(struct _DEVICE_OBJECT * Pdo = 0x89683700, struct _IRP * Irp = 0xa7834eb8)+0x41

83093a74 8f726c92 usbhub!UsbhPdoPnp(struct _DEVICE_OBJECT * HubPdo = 0x89683700, struct _IRP * Irp = 0xa7834eb8)+0x78

83093a88 822ce681 usbhub!UsbhGenDispatch(struct _DEVICE_OBJECT * DeviceObject = 0x89683700, struct _IRP * Irp = 0xa7834eb8)+0x4a

83093aac 82027f1c nt!IovCallDriver(struct _DEVICE_OBJECT * DeviceObject = 0x9112ee50, struct _IRP * Irp = 0x00000000, void * ReturnAddress = 0x825cfce9)+0x252

83093ac0 825cfce9 nt!IofCallDriver(struct _DEVICE_OBJECT * DeviceObject = 0x9112ee50, struct _IRP * Irp = 0x00000000)+0x1b

83093b08 822ce681 ndis!ndisPnPDispatch(struct _DEVICE_OBJECT * DeviceObject = 0x892f6030, struct _IRP * Irp = 0x00000002)+0x4a4

83093b2c 82027f1c nt!IovCallDriver(struct _DEVICE_OBJECT * DeviceObject = 0x9112ee50, struct _IRP * Irp = 0x00000000, void * ReturnAddress = 0x821af73b)+0x252

83093b40 821af73b nt!IofCallDriver(struct _DEVICE_OBJECT * DeviceObject = 0x9112ee50, struct _IRP * Irp = 0x00000000)+0x1b

83093b74 821af9a3 nt!IopSynchronousCall(struct _DEVICE_OBJECT * DeviceObject = 0x89683700, struct _IO_STACK_LOCATION * TopStackLocation = 0x83093ba8, long DefaultStatus = -1882596448, unsigned long DefaultInformation = 0x83093bd0, unsigned long * Information = 0x00000000)+0xce

83093bd0 82006592 nt!IopRemoveDevice(struct _DEVICE_OBJECT * TargetDevice = 0x89683700, unsigned long IrpMinorCode = 2)+0xd5

 

1: kd> kPL

ChildEBP RetAddr 

83093868 8208fbf4 nt!MmAccessFault(

                                unsigned long FaultStatus = 1,

                                void * VirtualAddress = 0x9112ee50,

                                char PreviousMode = 0 '',

                                void * TrapInformation = 0x83093880)+0x106

83093868 82091f60 nt!_KiTrap0E(void)+0xdc

830938f0 8fc9dbbb nt!Exfi386InterlockedExchangeUlong(void)

83093904 8204b2e7 C7xUSBV3!powerWaitWakeCallback(

                                struct _DEVICE_OBJECT * Object = 0x89683700,

                                unsigned char MinorFunction = 0x00 '',

                                union _POWER_STATE PowerState = union _POWER_STATE,

                                struct _MINIPORT_ADAPTER * Adapter = 0x9112eb50,

                                struct _IO_STATUS_BLOCK * IoStatus = 0xa78aaea8)+0x1b

8309392c 822cec69 nt!PopRequestCompletion(

                                struct _DEVICE_OBJECT * DeviceObject = 0x00000000,

                                struct _IRP * Irp = 0xa78aae90,

                                void * Context = 0x898b3000)+0x38

83093964 820acc1b nt!IovpLocalCompletionRoutine(

                                struct _DEVICE_OBJECT * DeviceObject = 0x00000000,

                                struct _IRP * Irp = 0xa78aae90,

                                void * Context = 0x830939d4)+0xcc

8309399c 822ceb53 nt!IopfCompleteRequest(

                                struct _IRP * Irp = 0x9112ee50,

                                char PriorityBoost = 0 '')+0x11d

83093a0c 8f7431f4 nt!IovCompleteRequest(

                                struct _IRP * Irp = 0x9112ee50,

                                char PriorityBoost = 0 '')+0x11c

83093a20 8f74a7f6 usbhub!UsbhCompletePdoWakeIrp(

                                struct _DEVICE_OBJECT * HubFdo = 0x89522028,

                                struct _DEVICE_OBJECT * Pdo = 0x89683700,

                                long NtStatus = -1073741536)+0xaf

83093a40 8f74a9e0 usbhub!UsbhPdoRemoveCleanup(

                                struct _DEVICE_OBJECT * Pdo = 0x89683700)+0x3f

83093a58 8f72e41e usbhub!UsbhPdoPnp_RemoveDevice(

                                struct _DEVICE_OBJECT * Pdo = 0x89683700,

                                struct _IRP * Irp = 0xa7834eb8)+0x41

83093a74 8f726c92 usbhub!UsbhPdoPnp(

                                struct _DEVICE_OBJECT * HubPdo = 0x89683700,

                                struct _IRP * Irp = 0xa7834eb8)+0x78

83093a88 822ce681 usbhub!UsbhGenDispatch(

                                struct _DEVICE_OBJECT * DeviceObject = 0x89683700,

                                struct _IRP * Irp = 0xa7834eb8)+0x4a

83093aac 82027f1c nt!IovCallDriver(

                                struct _DEVICE_OBJECT * DeviceObject = 0x9112ee50,

                                struct _IRP * Irp = 0x00000000,

                                void * ReturnAddress = 0x825cfce9)+0x252

83093ac0 825cfce9 nt!IofCallDriver(

                                struct _DEVICE_OBJECT * DeviceObject = 0x9112ee50,

                                struct _IRP * Irp = 0x00000000)+0x1b

83093b08 822ce681 ndis!ndisPnPDispatch(

                                struct _DEVICE_OBJECT * DeviceObject = 0x892f6030,

                                struct _IRP * Irp = 0x00000002)+0x4a4

83093b2c 82027f1c nt!IovCallDriver(

                                struct _DEVICE_OBJECT * DeviceObject = 0x9112ee50,

                                struct _IRP * Irp = 0x00000000,

                                void * ReturnAddress = 0x821af73b)+0x252

83093b40 821af73b nt!IofCallDriver(

                                struct _DEVICE_OBJECT * DeviceObject = 0x9112ee50,

                                struct _IRP * Irp = 0x00000000)+0x1b

83093b74 821af9a3 nt!IopSynchronousCall(

                                struct _DEVICE_OBJECT * DeviceObject = 0x89683700,

                                struct _IO_STACK_LOCATION * TopStackLocation = 0x83093ba8,

                                long DefaultStatus = -1882596448,

                                unsigned long DefaultInformation = 0x83093bd0,

                                unsigned long * Information = 0x00000000)+0xce

83093bd0 82006592 nt!IopRemoveDevice(

                                struct _DEVICE_OBJECT * TargetDevice = 0x89683700,

                                unsigned long IrpMinorCode = 2)+0xd5

 

1: kd> kfL

  Memory  ChildEBP RetAddr 

          83093868 8208fbf4 nt!MmAccessFault+0x106

        0 83093868 82091f60 nt!_KiTrap0E+0xdc

       88 830938f0 8fc9dbbb nt!Exfi386InterlockedExchangeUlong

       14 83093904 8204b2e7 C7xUSBV3!powerWaitWakeCallback+0x1b

       28 8309392c 822cec69 nt!PopRequestCompletion+0x38

       38 83093964 820acc1b nt!IovpLocalCompletionRoutine+0xcc

       38 8309399c 822ceb53 nt!IopfCompleteRequest+0x11d

       70 83093a0c 8f7431f4 nt!IovCompleteRequest+0x11c

       14 83093a20 8f74a7f6 usbhub!UsbhCompletePdoWakeIrp+0xaf

       20 83093a40 8f74a9e0 usbhub!UsbhPdoRemoveCleanup+0x3f

       18 83093a58 8f72e41e usbhub!UsbhPdoPnp_RemoveDevice+0x41

       1c 83093a74 8f726c92 usbhub!UsbhPdoPnp+0x78

       14 83093a88 822ce681 usbhub!UsbhGenDispatch+0x4a

       24 83093aac 82027f1c nt!IovCallDriver+0x252

       14 83093ac0 825cfce9 nt!IofCallDriver+0x1b

       48 83093b08 822ce681 ndis!ndisPnPDispatch+0x4a4

       24 83093b2c 82027f1c nt!IovCallDriver+0x252

       14 83093b40 821af73b nt!IofCallDriver+0x1b

       34 83093b74 821af9a3 nt!IopSynchronousCall+0xce

       5c 83093bd0 82006592 nt!IopRemoveDevice+0xd5

'Programming' 카테고리의 다른 글

On-demand 메모리 관리  (0) 2009.04.02
Thread States  (0) 2009.03.27
Intel CPU registers  (0) 2009.03.20
Windbg Remote debugging 설정 방법  (0) 2009.03.20
Windows Error Reporting(WER)이란  (0) 2009.02.25
Posted by noenemy
,

Intel CPU registers

Programming 2009. 3. 20. 20:10
CPU에서 어떤 명령이 실행되기 위해서는 메모리에 있는 내용이 CPU로 전달되어야 하는데, CPU와 메모리 사이에 데이터가 매번 전송되는 것이 성능상에 문제가 될 수 있기 때문에 CPU 내에 여러 개의 임시 저장소를 가지고 있는데 이를 레지스터(Register)라고 합니다. CPU가 발전함에 따라 레지스터의 크기가 변하기도 하고, 새로운 기능이 추가되면서 레지스터가 추가되기도 합니다. 여기서는 Intel 80386 CPU를 기준으로 가장 일반적으로 사용되는 레지스터들을 나열해 보았습니다.

 

각 레지스터에 대한 보다 자세한 내용은 Intel CPU manual(http://www.intel.com/products/processor/manuals/)을 참고하시기 바랍니다.

 

일반목적의 레지스터

l  EAX : Accumulator (함수의 return value로 사용)

l  EBX : Points to data in DS

l  ECX : Counting

l  EDX : I/O pointer

l  ESI : Source ptr for strings

l  EDI : Destination ptr fro strings

 

특수한 목적의 레지스터

l  ESP : Stack pointer (현재 thread stacktop 위치를 가리키는 pointer)

l  EBP : Points to data on stack (stack frame을 구성하는데 사용)

l  EIP : Instruction pointer(다음에 CPU에서 실행되어야 할 instruction을 가리킴)

 

EFLAGS 레지스터

l  Status Flags

n  CF : Carry Flag

n  PF : Parity Flags

n  AF : Auxiliary Carry Flag

n  ZF : Zero Flag

n  SF : Sign Flag

n  OF : Overflow Flag

l  Control Flag

n  DF : direction Flag

l  System Flags

n  IF : Interrupt Enable Flag

n  TF : Trap Flag

n  IOPL : I/O Privilege Level

n  NT : Nested Task Flag

n  RF : Resume Flag

n  VM : Virtual 8086 mode

n  AC : Alignment Check (486 only)

 

Segment 레지스터

l  CS : Code segment

l  DS : Data segment

l  SS : Stack segment

l  ES : Extra data segment

l  FS : Extra data segment introduced with 386

l  GS : Extra data segmet introduced with 386

'Programming' 카테고리의 다른 글

Thread States  (0) 2009.03.27
Windbg Stack Backtracing 명령어  (0) 2009.03.27
Windbg Remote debugging 설정 방법  (0) 2009.03.20
Windows Error Reporting(WER)이란  (0) 2009.02.25
강제로 덤프파일 수집하기  (0) 2009.02.19
Posted by noenemy
,