2016년 10월 11일 화요일

하이퍼바이저 구현을 위한 공부 방법

howto_study_hypervisor

Hypervisor 구현 방법을 공부하기 위한 가이드


레퍼런스, 메뉴얼


당연히 인텔/AMD 메뉴얼입니다. 무식하게 읽으면 어려워서, 중간에 포기하기 십상입니다만, 어느정도 개념이 잡히면 가장 먼저 보게 되는게 메뉴얼입니다. 경험상 인텔메뉴얼 만큼 좋은 자료는 없었습니다. (전 AMD SVM 은 안봐서 모릅니다. 동작 방식은 거의, 완전히 비슷합니다.)
개인적인 경험으로는 인텔메뉴얼의 Virtual Machine Extension 챕터를 세번정도 읽어보니, 머리속에 들어오기 시작하더군요.


온라인 자료



강추! 개인적으로 가장 추천하는 자료입니다. 엔텔메뉴얼과 같이 보면서 공부하셔야 할것입니다. 최소한 여기 나오는 내용을 확실히 다 숙지해야 합니다.



제가 예전에 만들었던 자료입니다. 마지막에 하이퍼바이저를 통해 안티키로거
제품 우회하는 시연영상도 있습니다.



서승현님 이 정리해두신 글입니다. KVM 코드 분석을 시작하시는 분들께 많이 도움될것 같습니다.



  • Series 1 Xen으로 배우는 가상화 기술의 이해 - CPU 가상화
  • Series 2 Xen으로 배우는 가상화 기술의 이해 - 메모리 가상화
  • Series 3 Xen으로 배우는 가상화 기술의 이해 - I/O 가상화

한빛리얼타임에서 PDF 로 구매하실 수 있습니다. Xen 하이퍼바이저 소스코드를 분석하면서 공부한 내용을 정리해서 책으로 출판했네요.
대단하신분들인듯, 하지만 가상머신의 기본 개념이나 VT-x 스펙에 대한 기초지식이 없으면 이해하기 쉽지 않을 것 같네요.


코드



Windows Interernals 의 저자중 한명인 Alex Ionescue 가 만든 하이퍼바이저입니다. 최소한의 어셈블리코드로 엄청나게 간결하게 작성된 멋진 하이퍼바이저입니다. 공부를 위해서라면 최고의 선택이라고 생각합니다.



또다른 멋진 오픈소스 구현입니다.


  • Xen, KVM, VirtualBox

제품이니 당연히 매우 높은 완성도를 보이죠. 개인적으로는 VirtualBox 소스코드가 가장 읽기 편했습니다만, 기본 개념을 이해하지 못한상태에서는 해독 불가능한 암호로 보일겁니다.


  • 기타

요즘은 다양한 하이퍼바이저 구현코드들을 쉽게 찾을 수 있더군요. 검색검색~~


도움이 될만 한 잔소리


아키텍처(x86, x64 같은)/운영체제를 공부해야 합니다


인터럽트 처리, 가상메모리 구현 같은, 리얼모드/보호모드 같은 운영체제 구현에 대한 기초 지식은 필수입니다. 어차피 하이퍼바이저는 운영체제가 직접 다루는 CPU 나 메모리를 가상화 하는 코드를 구현하는 작업입니다. 따라서 컨트롤 레지스터나 인터럽트들을 운영체제들이 어떻게 다루는지 정확히 알아야 제대로 구현하고, 트러블 슈팅이 가능하겠죠.


공부하기 쉽지는 않지만 꽤 재밌는 것들을 할 수 있습니다.


Full Virtualization vs Para-Virtualzation


가상머신을 구현하는 방법에는 Full virtualization 과 Para-Virtualization 으로 나눌 수 있습니다.
Vmware 나 Qemu 가 대표적인 Full Virtualization 제품에 속하고, Xen 이 대표적인 Para-Virtualization 제품에 속했습니다(과거형입니다).


전자는 모든 명령어를 가상화 하는겁니다. 모든 CPU 명령어를 에뮬레이트하기때문에 완전히 새로운 명령어 체계를 사용하는 아키텍처라 해도 가상화 할 수 있겠죠. 하지만 당연히 느립니다.


후자는 꼭 필요한 명령어만 가상화 합니다. 예를들어 아래와 같은 명령어가 있다고 가정합니다.


xor eax, eax
mov cr0, eax

첫번째 명령어는 그냥 범용 레지스터를 다르는 명령어이기 때문에 가상화 하지 않아도 됩니다. 실제 하드웨어를 그대로 사용하기 때문에 당연히 100% 하드웨어 성능을 냅니다. 하지만 두번째 명령어는 컨트롤 레지스터를 갱신해야 하기 때문에 반드시 가상화 해야 합니다.


첫번째 명령어 처럼 하드웨어를 직접 활용하는 걸 Direct execution 이라고 합니다. 이렇게 꼭 필요한 중요한 명령어만 가상화 하는 것을 Para-Virtualization 이라고 합니다. 당연히 Full Virtualization 보다 성능이 좋아지겠죠?


Para-Virtualization 을 구현하기 위해서는 결국 꼭 가상화해야 하는 명령어와 그렇지 않은 명령어를 구분할 수 있어야 합니다. 그래서 커널을 직접 수정하는 방식을 활용했습니다. mov cr0, eax 같은 명령어를 찾아서 가상머신에게 가상화해달라고 요청하도록 커널코드를 직접 수정했습니다. 응용프로그램은 어차피 반드시 가상화 해야 하는 명령어를 사용하지 않으니 별 문제 없겠죠.


이렇게 가상머신에게 해당 명령어를 가상화해달라고 요청하는것을 하이퍼 콜(hyper call) - vm 과 host 간 호출 이라고 합니다.
그래서 Xen 에서 리눅스 올리려면, 전용 리눅스 커널을 사용해야 했습니다.


그런데 Windows 같은 운영체제는 커널을 직접 수정할 수 없기때문에, VT-x/SVM 기술이 나오기 전까지는 Para-Virtualization 방식의 가상머신 제품은 Windows 운영체제를 지원하지 못했습니다. 재밌는건 Vmware 같은 경우 Full Virtualization 방식임에도 불구하고 Xen 같은 제품보다 더 빨랐다는...


아무튼 옛날엔 그랬고, VT-x/SVM 이 나온 이후 가상머신을 만드는 방식이 매우 간편해졌습니다. 간편해 진게 아니라, 간편하게 만든것이죠.
앞에서 얘기했던 반드시 가상화 해야 하는 명령어와 그렇지 않은 명령어를 CPU, 즉 아키텍처에서 알아서 구분해줍니다. 가상머신 개발자는 CPU 가 알려주는 반드시 가상화하라고 통지하는 이벤트를 상황에 맞게 알아서 잘 처리해주면 됩니다. 이걸 VMExit 이라고 합니다.
저는 하이퍼바이저 구현은 VM Exit handler/callback 작성 이라고 표현하곤 합니다.


따라서 하드웨어 가상화 기술이 나온 이후에는 Para-Virtualization 이니 뭐니 하는게 필요가 없죠. 그렇기 때문에 이제는 Xen 도 Windows 를 가상화할 수 있게 된겁니다. 개발하기도 훨씬 쉬워졌죠. 만세~


Nested VM ?


가상머신 안에 또 가상머신 안에 또 가상머신?


무지막지하게 느릴까요? 당연히 느려지기야 하겠지만, 앞에서 얘기한 direct execution 때문에 그렇게 미친듯이 느려지지는 않을것입니다. 하이퍼바이저 개발자 입장에서는 관리할 자료구조가 훨씬 복잡해지고, 메모리 변환등으로 인해 복잡하긴 하겠지만요.


QEMU 에 대해서


QEMU 는 원래 에뮬레이터입니다.
에뮬레이터이기 때문에 호스트가 x86 이라도 x64, ARM 등의 명령어를 에뮬레이트 할 수 있기때문에 다양한 아키텍처를 지원할 수 있죠. 다양한 CPU를 소프트웨어로 에뮬레이트 할 수 있으니까요. 세상에 있는 모든 cpu 를 지원한다죠? 그렇기 때문에 엄청 느렸습니다.


하드웨어 가상화(VT-x, SVM 같은) 기술이 나온 이후, CPU 에뮬레이트는 하드웨어 가상화 기능을 활용하기 시작했습니다. 그게 바로 KVM 이구요. 나머지 하드웨어들은 소프트웨어로 에뮬레이팅 합니다. 다른 Vmware, Virttual Box 같은 가상머신 제품도 마찬가지 입니다.


만일 QEMU로 호스트의 아키텍처와 다른 아키텍처를 가상머신으로 돌리고자 한다면, VT-x 같은 기술을 활용 할 수 없기때문에 100% 에뮬레이팅에 의존 할 수 없을 겁니다. 느리겠죠?


간략히 얘기하자면, QEMU 는 다양한 디바이스/하드웨어에 대한 에뮬레이팅을 담당하고, KVM 은 CPU/Memory 가상화를 담당한다고 보시면 됩니다. 따라서 VT-x/SVM 구현에 관심있는 분이라면 QEMU 코드를 살펴보기보다는 리눅스 커널에 포함된 KVM 구현 코드를 분석하셔야 합니다.

2014년 12월 27일 토요일

boost::asio timer 사용하기

오늘은 boost::asio 예제코드를 보다가 (몰라서) 헤멧던 내용을 공유할까 합니다.
c++ 이나 boost 같은 라이브러리에 익숙하지 않은 저 같은 c++ 초보자들에게 도움이 되길 바라며 :-)
오늘은 그냥… 괜히… Windows 의 타이머 객체를 사용하고 싶지 않아서, boost::asio 의 timer 예제코드 를 조금 수정해서 아래와 같은 코드를 작성했습니다. 간단히 설명하자면 정해진 시간(1초)이 지나면 callback 함수(print(…))를 5번만 호출하는 코드입니다.
void print(const boost::system::error_code& e, boost::asio::steady_timer* timer, int* count)
{
    if (*count < 5)
    {
        std::cout << *count << "\n";
        ++(*count);

        timer->expires_from_now(boost::chrono::seconds(1));
        timer->async_wait(boost::bind(print, boost::asio::placeholders::error, timer, count));
    }
}


int main()
{
    boost::asio::io_service io_service;

    int count = 0;
    boost::asio::steady_timer timer(io_service);
    timer.async_wait(boost::bind(
                                print,
                                boost::asio::placeholders::error,
                                &timer,
                                &count));
    io_service.run();

    std::cout << "count = " << count << std::endl;
    return true;
}
timer.async_wait() 함수는 아래와 같은 함수 포인터 또는 함수객체를 파라미터로 받습니다. 여기에 자세한 내용이 있습니다.
void handler(
  const boost::system::error_code& error // Result of operation.
);
예제에서는 부가적인 파라미터를 전달 받는 print() 함수를 async_wait() 의 파라미터로 사용하기 위해서 아래의 코드 처럼 boost::bind() 를 이용했습니다.
    timer.async_wait(boost::bind(
                                print,
                                boost::asio::placeholders::error,
                                &timer,
                                &count));
저는 print() 함수에 전달되는 파라미터로 boost::asio::deadline_timer 와 count 의 포인터 타입이 사용되는것이 그냥 맘에 안들어서 참조자로 바꾸고 싶었습니다.
boost::asio::placeholders::error 는 boost::bind() 를 사용할 때 _1 같은 역할을 하는 placeholder argument 이므로, boost::bind(print, _1, timer, count) 와 같으므로, timer 와 count 를 참조자로 넘겨도 아무 문제 없을 것 같았습니다.
void
print(
    const boost::system::error_code& /*e*/,
    boost::asio::deadline_timer& t,
    int& count
    )
print() 함수 파라미터를 참조자를 받도록 변경하고 컴파일하면 아래처럼 복잡한 에러가 발생합니다.
1>  All outputs are up-to-date.
1>  _test_boost_asio_timer.cpp
1>c:\Boost_x64\include\boost-1_56\boost/asio/basic_waitable_timer.hpp(514): error C2248: 'boost::asio::basic_io_object<IoObjectService>::basic_io_object' : cannot access private member declared in class 'boost::asio::basic_io_object<IoObjectService>'
1>          with
1>          [
1>              IoObjectService=boost::asio::waitable_timer_service<boost::chrono::steady_clock,boost::asio::wait_traits<boost::chrono::steady_clock>>
1>          ]
1>          c:\Boost_x64\include\boost-1_56\boost/asio/basic_io_object.hpp(163) : see declaration of 'boost::asio::basic_io_object<IoObjectService>::basic_io_object'
1>          with
1>          [
1>              IoObjectService=boost::asio::waitable_timer_service<boost::chrono::steady_clock,boost::asio::wait_traits<boost::chrono::steady_clock>>
1>          ]
1>          This diagnostic occurred in the compiler generated function 'boost::asio::basic_waitable_timer<Clock>::basic_waitable_timer(const boost::asio::basic_waitable_timer<Clock> &)'
1>          with
1>          [
1>              Clock=boost::chrono::steady_clock
1>          ]
1>
1>Build FAILED.
찬찬히 읽어보니 boost::asio::basic_io_object<IoObjectService> 클래스에 정의된 private member 에 접근하지 못한다는 에러입니다. 대체 이런 에러는 어디서 나는걸까 고민하다가 boost::bind 문서를 다시 한번 읽어보니 해답이 보이더군요.
a copy of the value of i is stored into the function object. boost::ref and boost::cref can be used to make the function object store a reference to an object, rather than a copy:
boost::bind 는 고정된 변수값을 call by value 로 넘기는게 기본인가 봅니다. (미루어 보건데 - 소스를 보지 않았으니 - boost::bind 는 함수 객체를 생성하는것으로 구현했는가 보네요.)
결국 boost::bind 호출시 사용된 timer 객체가 call by value 로 넘어가면서 복사 생성자나 대입 연산자가 호출되는 시점에 오류가 발생한 것 같습니다.
/// Base class for all I/O objects.
/**
 * @note All I/O objects are non-copyable. However, when using C++0x, certain
 * I/O objects do support move construction and move assignment.
 */
#if !defined(BOOST_ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION)
template <typename IoObjectService>
#else
template <typename IoObjectService,
    bool Movable = detail::service_has_move<IoObjectService>::value>
#endif
class basic_io_object
{
public:
...
protected:
  /// Construct a basic_io_object.
  /**
   * Performs:
   * @code get_service().construct(get_implementation()); @endcode
   */
  explicit basic_io_object(boost::asio::io_service& io_service)
    : service(boost::asio::use_service<IoObjectService>(io_service))
  {
    service.construct(implementation);
  }

private:
  basic_io_object(const basic_io_object&);
  basic_io_object& operator=(const basic_io_object&);
};
코드를 따라가 보니 class basic_waitable_timer 템플릿 클래스는 public basic_io_object 를 상속받았고, basic_io_object 템플릿 클래스의 복사 생성자와 대입연산자가 private 으로 선언되어있음을 확인 할 수 있었습니다. 클래스 자체가 복사하지 않도록 디자인된 녀석이었습니다!
포인터를 사용하지 않고, 참조자를 사용하도록 수정한 코드는 아래와 같습니다.
void print(const boost::system::error_code& e, boost::asio::steady_timer& timer, int& count)
{
    if (count < 5)
    {
        std::cout << count << "\n";
        ++(count);

        timer.expires_from_now(boost::chrono::seconds(1));
        timer.async_wait(boost::bind(print, boost::asio::placeholders::error, boost::ref(timer), boost::ref(count)));
    }
}

bool test_boost_asio_timer_3()
{
    boost::asio::io_service io_service;

    int count = 0;
    boost::asio::steady_timer timer(io_service);
    timer.async_wait(boost::bind(
                                print,
                                boost::asio::placeholders::error,
                                boost::ref(timer),
                                boost::ref(count)));
    io_service.run();

    std::cout << "count = " << count << std::endl;
    return true;
}
오늘도 하나 배웠습니다. :-)

2014년 9월 16일 화요일

Windows 에서 UEFI 빌드 환경 구성하기

Windows 에서 UEFI 빌드 환경 구성하기


요약

UDK2014 를 Windows 개발환경에서 빌드하는 방법을 설명합니다. UDK 패키지 파일내의 2014-ReleaseNotes-MyWorkSpace.txt 를 참고했습니다.

시작

  1. 개발 환경
    • windows 7, x64
    • visual studio 2010
  2. 소스코드 압축 풀기
    • UDK2014.MyWorkSpace.zip -> d:\work.uefi\MyWorkSpace
    • BaseTools(Windows).zip -> d:\work.uefi\MyWorkSpace\BaseTools\
  3. 빌드 환경 셋업 하기
    • d:\work.uefi\MyWorkSpace 에서 command prompt 하나 띄우고,
    • EDK_TOOLS_PATH 환경 변수 설정해주고
    • edksetup.bat --nt32 실행하면 준비 완료
        d:\work.uefi\MyWorkSpace>set EDK_TOOLS_PATH=d:\work.uefi\MyWorkSpace\BaseTools\BaseTools
      
        d:\work.uefi\MyWorkSpace>edksetup.bat --nt32
                  PATH      = d:\work.uefi\MyWorkSpace\BaseTools\BaseTools\Bin;d:\work.uefi\MyWork
        Space\BaseTools\BaseTools\Bin\Win32;C:\Program Files (x86)\Microsoft Visual Studio 10.0\VS
        TSDB\Deploy;...[skip]
      
             WORKSPACE      = d:\work.uefi\MyWorkSpace
        EDK_TOOLS_PATH      = d:\work.uefi\MyWorkSpace\BaseTools\BaseTools
      
        copying ... target.template to d:\work.uefi\MyWorkSpace\Conf\target.txt
        copying ... tools_def.template to d:\work.uefi\MyWorkSpace\Conf\tools_def.txt
        copying ... build_rule.template to d:\work.uefi\MyWorkSpace\Conf\build_rule.txt
      
        Rebuilding of tools is not required.  Binaries of the latest,
        tested versions of the tools have been tested and included in the
        EDK II repository.
      
        If you really want to build the tools, use the ForceRebuild option.
      
        !!! WARNING !!! No CYGWIN_HOME set, gcc build may not be used !!!
      
        d:\work.uefi\MyWorkSpace>
      
  4. 빌드하기
    • build -t <tool chain name> 명령으로 빌드
    • tool chain 을 변경하려면
      • Conf/target.txt : TOOL_CHAIN_TAG = VS2010x86 으로 변경하거나
      • build -t <tool chain name>
    • x64 머신에서 x86 버전 vs 를 사용하므로 VS2010x86 tool chain 을 사용
    • vs 는 x86 버전밖에 없다는 건 함정
    • tool chain 정보는 Conf/tools_def.txt 참고 (edksetup 에서 동적으로 생성해 주는 파일 임)
        d:\work.uefi\MyWorkSpace>build -t VS2010x86
        ..........
        ..........
        Generate Region at Offset 0x28E000
           Region Size = 0x2000
           Region Name = DATA
      
        Generate Region at Offset 0x290000
           Region Size = 0x10000
           Region Name = None
      
        GUID cross reference file can be found at d:\work.uefi\myworkspace\Build\NT32IA32\DEBUG_VS
        2010x86\FV\Guid.xref
      
        FV Space Information
        FVRECOVERY [66%Full] 2621440 total, 1733424 used, 888016 free
      
        - Done -
        Build end time: 23:37:28, Sep.16 2014
        Build total time: 00:02:12
        d:\work.uefi\MyWorkSpace>
      
  5. 하드웨어의 기능을 이용하는 빌드 옵션들
    • build -D <option> 을 이용해서 기능 추가 할 수 있음
    • build -D SECURE_BOOT_ENABLE <- 요렇게!
    • SECURE_BOOT_ENABLE
      • Provides access for generic authentication information
      • associated with specific device path.
    • TPM_UID_ENABLE
      • Provides TCG-defined service, and user identification feature.
    • IP6_NETWORK_ENABLE
      • Provides Ipv6 network stack support.
    • DUAL_NETWORK_ENABLE
      • Provides both Ipv4 and Ipv6 network stacks support.
    • SOURCE_DEBUG_ENABLE
      • Provides source debugging feature.
  6. visual studio 에서 빌드하려면?
  7. 실행은?
    • make 의 run 타겟을 이용할 수도 있고
    • d:\work.uefi\MyWorkSpace\Build\NT32IA32\DEBUG_VS2010x86\IA32\SecMain.exe 를 실행해도 됨
  8. 끝!

2014년 9월 12일 금요일

VMWARE 에서 UEFI 모드로 windows 8 설치하기

Enable efi mode in vmware


  1. vmware 로 새로운 가상머신을 생성
    • windows 8 x64 이미지 생성
    • ‘install os later’ 를 선택해서 vmx 파일만 만들고, 실제 OS 를 설치하지는 않음
  2. 가상머신 설정 수정
    • VM Tweaker 다운로드
    • VM Teaker -> EFI BIOS Tweaks -> Enable ‘efi’ BIOS boot type 체크
  3. CD rom 을 win8 x64 iso 파일로 지정하고, OS 설치
  4. OS 설치 완료 후 EFI system partition 이 있으면 ㅇㅋ!
  5. vmware tools 설치해주고, 스냅샷 떠 놓으면 끝!

2014년 8월 21일 목요일

boost build summary

Build and Install boost

  1. download boost library at http://www.boost.org/
  2. extract library to d:\work.lib\boost_1_56_0\
  3. build build tool
     d:\work.lib\boost_1_56_0>bootstrap.bat
     Building Boost.Build engine
    
     Bootstrapping is done. To build, run:
    
         .\b2
    
     To adjust configuration, edit 'project-config.jam'.
     Further information:
    
         - Command line help:
         .\b2 --help
    
         - Getting started guide:
         http://boost.org/more/getting_started/windows.html
    
         - Boost.Build documentation:
         http://www.boost.org/boost-build2/doc/html/index.html
    
     d:\work.lib\boost_1_56_0>
    
  4. build and install boost library at c:\Boost_x86\, c:\Boost_x64\
     d:\work.lib\boost_1_56_0>b2.exe toolset=msvc-10.0 --build-type=complete --build-dir=d:\work.lib\boost_1_56_0\build_dir_x86 --prefix=c:\Boost_x86 install
    
     d:\work.lib\boost_1_56_0>b2.exe toolset=msvc-10.0 address-model=64 --build-type=complete --build-dir=d:\work.lib\boost_1_56_0\build_dir_x64 --prefix=c:\Boost_x64 install
    
  5. If you want boost::python, install python before build boost. :-)

2014년 5월 21일 수요일

hypervisor 구현 세미나 자료

hypervisor 구현 관련 세미나 자료입니다.
vmware 같은 가상머신을 어떻게 만드는지에 대한 내용입니다.

재 작년인가에 세미나할때 만든 거 같은데 :-)
동영상도 있습니다. 음하하하


[slide share]

2014년 2월 4일 화요일

WinDbg :: dg 명령어

오랜만에 분석 할 꺼리가 생겨서 커널코드를 뒤적이다가, 세그먼트 관련 내용을 확인할 일이 생겼습니다.
하도 오랜만에 하는거라 그런지, dg 명령어까지는 생각이났는데, 출력 형태를 헤깔리는 바람에 쓸데없는 삽질을 하게되어 흔적이라도 남겨볼까하고요.

세그먼트 셀렉터는 상위 13비트를 GDT / LDT 인덱스로 사용합니다. 아래 처럼요.

; 15                                                2  1  0
; +----------------------------+-------------------+--+--+--+
; |                          index                 |TI| RPL |
; +----------------------------+-------------------+--+--+--+
;
; index
;   GDT 의 인덱스 값 ( GDT = index * sizeof(GDT Descriptor) )
;
; TI (Table Indicator)
;   0 이면 GDT (Global Descriptor Table)
;   1 이면 LDT (Local Descriptor Table)
;
; RPL (Requestor Privilege Level)

system context 에서 셀렉터와 세그먼트 디스크립터 출력내용입니다.

cs=0010  ss=0018  ds=002b  es=002b  fs=0053  gs=002b             efl=00000202

0: kd> dg 0 80
                                                    P Si Gr Pr Lo
Sel        Base              Limit          Type    l ze an es ng Flags
---- ----------------- ----------------- ---------- - -- -- -- -- --------
0000 00000000`00000000 00000000`00000000 <reserved> 0 Nb By Np Nl 00000000
0008 00000000`00000000 00000000`00000000 <reserved> 0 Nb By Np Nl 00000000
0010 00000000`00000000 00000000`00000000 Code RE Ac 0 Nb By P  Lo 0000029b
0018 00000000`00000000 00000000`ffffffff Data RW Ac 0 Bg Pg P  Nl 00000c93
0020 00000000`00000000 00000000`ffffffff Code RE    3 Bg Pg P  Nl 00000cfa
0028 00000000`00000000 00000000`ffffffff Data RW Ac 3 Bg Pg P  Nl 00000cf3
0030 00000000`00000000 00000000`00000000 Code RE Ac 3 Nb By P  Lo 000002fb
0038 00000000`00000000 00000000`00000000 <reserved> 0 Nb By Np Nl 00000000
0040 00000000`00b96080 00000000`00000067 TSS32 Busy 0 Nb By P  Nl 0000008b
0048 00000000`0000ffff 00000000`0000f800 <reserved> 0 Nb By Np Nl 00000000
0050 ffffffff`fffe0000 00000000`00003c00 Data RW Ac 3 Bg By P  Nl 000004f3
0058 00000000`00000000 00000000`00000000 <reserved> 0 Nb By Np Nl 00000000
0060 00000000`00000000 00000000`ffffffff Code RE    0 Bg Pg P  Nl 00000c9a
0068 00000000`00000000 00000000`00000000 <reserved> 0 Nb By Np Nl 00000000
0070 00000000`00000000 00000000`00000000 <reserved> 0 Nb By Np Nl 00000000
0078 00000000`00000000 00000000`00000000 <reserved> 0 Nb By Np Nl 00000000
0080 Unable to get descriptor

WinDbg 의 dg 명령어 출력의 Sel 부분은 셀렉터의 RPL 필드를 00 으로 간주한 상태로 셀렉터 값을 출력합니다. 이걸 깜빡하는 바람에...@,.@

따라서 fs 셀렉터 0x53 -> 0x50 으로 치환해서 dg 출력에서 찾아야 합니다.
위에서는 0xffffffff`fffa0000 를 베이스로 하고, 리미트가 0x00003c00 인 GDT 엔트리를 가지는 것을 확인 할 수 있습니다. 다시 말해서 fs:[0] -> 0xffffffff`fffa0000 라는 거죠.

cs = 0x0010 : 00010 0 00 -> r0, gdt, Sel = 0010 0 00 -> 0x10
ss = 0x0018 : 00011 0 00 -> r0, gdt, Sel = 0011 0 00 -> 0x18
fs = 0x0053 : 01010 0 11 -> r3, gdt, Sel = 1010 0 00 -> 0x50
ds,es,gs
   = 0x002b : 00101 0 11 -> r3, gdt, Sel = 0101 0 00 -> 0x28

64 비트는 분석경험이 거의 없다보니, 새롭기도 하고, 어렵기도 하고...
언젠가 64비트 악성코드가 나오기 시작하면 분석가 생활을 접겠다고 하셨던 고XX 책임님이 생각납니다. :-)