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 책임님이 생각납니다. :-)

2014년 1월 28일 화요일

const 위치와 의미

자주 사용하지만, 항상 뭔가 헤깔리는 그런것들이 있는데, const 도 그런 것들 중에 하나입니다. (저만 그럴지도...)
위치에 따른 const 의미를 정리해 보았습니다.

/**
 * @brief 
**/
class ConstPositionTest
{
public: 
    //> (const char*) msg : char* 가 const, 즉 msg 가 가리키는 데이터 변경 불가
    char* Function1(const char* msg)  
    {
        msg[0] = 't'; // error
        return m_msg;
    }

    //> char* (const msg) : msg 변수가 const, 즉 msg 포인터 변수 변경 불가
    char* Function2(char* const msg)  
    {    
        msg = m_msg; //error
        return m_msg;
    }

    //> 메소드 상수화, 이 메소드는 클래스 멤버를 읽을 수는 있으나 변경 할 수는 없음
    char* Function3(char* msg) const 
    {
        m_msg = msg; //error
        return m_msg;  
    }

    //> (const char*) : 리턴 값이 const char* 이므로 리턴 받는 변수도 const char* 이어야 함
    //> 따라서 리턴되는 포인터가 가리키는 데이터 변경 불가
    const char* Function4(char* msg)  
    {
        m_msg = msg;
        return m_msg; //반환 받는 타입이 const가 아닐 경우 error
    }
private:
    char* m_msg;
};

/**
 * @brief 
 * @param 
 * @see  
 * @remarks 
 * @code  
 * @endcode 
 * @return 
**/
bool test_const_position()
{
    ConstPositionTest test;
    char msg[] = "hello, const!";
 
    test.Function1(msg);
    test.Function2(msg);
    test.Function3(msg);

    const char* pMessage = test.Function4(msg);
    pMessage[0] = 0; // error
}