잡동산..정리가 필요함. MFC

2006/11/21 22:26

복사 http://blog.naver.com/zelatul0/31165666

AfxGetApp()->

(CDFConnect*)AfxGetMainWnd() 메인프레임에 접근

///////////////////////////////////////////////////////////////////////////////

void CDrawgraphDlg::OnPaint()
{
CPaintDC dc(this);

UpdateWndow(); //이거 한줄여..ㅡㅡ;;
CWnd* graphFrame = (CWnd*)GetDlgItem(IDC_GRAPH_FRAME);
CDC* pDC = graphFrame->GetDC();

testGraph->DrawGraph(pDC); //다른 클래스에 dc값을 넘겨 그림을 그려줌..

ReleaseDC(pDC);
}


저 한줄은 하위컨트롤들 먼저그려주게 하는거에염...
그러구 나서 그위에 그리면 윈도우가 밖으로 외출하지 않아도 되져..^^;;
주의하실 점은 만약 그리신 것이 컨트롤들을 덮어버림 보이지 않는다는 점이져..

사실 소스를 바꾸기가 그래서 그런데..혹시 이상하심
CDialog::OnPaint();
UpdateWindow();
으로 바꿔주세염.. CPaintDC부분 빼시구염..

그럼 즐푸하셈... 꾸벅


///////////////////////////////////////////////////////////////////////////////////


C_App::InitInstance()에보면

DialogBox를 생성하는 부분이 있습니다.

"int nResponse = dlg.DoModal();"

이부분을 주석처리해주면 처음부터 다이얼로그는 뜨지 않습니다.

다른 방법으로는 타이머를 이용해서

다이얼로그박스가 생성된 후 일정 시간 후에 SW_HIDE메세지를 보내서 사라지게 하는 방법이 있습니다.

트레이에만 표시되고 실행되는 경우라면 전자를 추천합니다.


///////////////////////////////////////////////////////////////////////////////

GetDlgItem(IDC_MAIN_WEEK)->ShowWindow(SW_HIDE);

///////////////////////////////////////////////////////////////////////////////


btn.EnableWindow(TRUE); --> 활성..

btn.EnableWindow(FALSE) --> 비활성..


////////////////////////////////////////////////////////////////////////////////
RegCreateKey//키값 만들기?

RegOpenKey//키값 등록
RegCloseKey//

RegEnumKey//읽기 하위 검색(?)
RegEnumKeyEx
RegEnumValue
RegQueryValue//읽기 하위는 검색안함(?)
RegQueryValueEx
RegSetValue//쓰기
RegDeleteKey//삭제

///////////////////////////////////////////////////////////////////////////////////
8. 스킨의 형태가 모서리가 둥글 경우 코딩
// Dialog 의 모서리를 라운드로 돌린다.
CRect rc;
GetWindowRect(rc);

CRgn rnd;
rnd.CreateRoundRectRgn(rc.left, rc.top, rc.right, rc.bottom, 26, 26);

::SetWindowRgn(this->m_hWnd, (HRGN)rnd, TRUE);


//////////////////////////////////////////////////////////////////////////////////

시작 프로그램 등록

내컴퓨터\HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Run 에 등록하면 된


////////////////////////////////////////////////////////////////////////////////////

윈도 최대화: ShowWindow(SW_MAIMIZE);

윈도 복구: ShowWindow(SW_RESTORE);

윈도 복구 할 때는 IsZoomed() 메소드로 확인한다음에 복구 합니다.

윈도 최소화: ShowWindow(SW_MINIMIZE);

IsIconic() 메소드가 TRUE를 반환한다면 최소화 상태임을 나타냅니다.

//////////////////////////////////////////////////////////////////////////////////////


SendMessage(WM_SYSCOMMAND, SC_MAXIMIZE, NULL);


////////////////////////////////////////////////////////////////////////////////


문자열의 실제 픽셀 길이를 알려줌.
GetTextExtent()

///////////////////////////////////////////////////////////////////////////////////

앞쪽 공백 자동정렬

Alt + F8


/////////////////////////////////////////////////////////////////////////////////////

문자열 블럭지정
SetSel()

////////////////////////////////////////////////////////////////////////////////////
[답변] MFC/동적생성/ComboBox 메시지 받기


파일 첨부가 안되네.. 쩝..

.................................................

{//임의로 콤보박스를 생성...

CComboBox* pCombo = new CComboBox();

RECT rc = {10, (m_id * 25), 100, (m_id * 25) + 120};

pCombo->Create(WS_CHILD | WS_VISIBLE | CBS_DROPDOWN | WS_VSCROLL, rc, this, m_id++);

pCombo->AddString("하나 추가요");

pCombo->AddString("둘 추가요");

pCombo->AddString("셋 추가요");

}

//메시지 맵에 콤보 CBN_SELCHANGE메시지를 기입..

BEGIN_MESSAGE_MAP(CDComboDlg, CDialog)

//{{AFX_MSG_MAP(CDComboDlg)

ON_WM_PAINT()

ON_WM_QUERYDRAGICON()

ON_BN_CLICKED(IDC_CREATE, OnCreate)

//}}AFX_MSG_MAP

ON_CONTROL_RANGE(CBN_SELCHANGE, 1, 9999, OnSelChange)

END_MESSAGE_MAP()

//이 함수로 메시지가 날라온다..~~

void CDComboDlg::OnSelChange(UINT nID)

{

CString strText, strSel;

CComboBox* pCombo = (CComboBox*)GetDlgItem(nID);

pCombo->GetLBText(pCombo->GetCurSel(), strText);

strSel.Format("선택한 ID:%d, 문자열:%s", nID, strText);

MessageBox(strSel);

}
///////////////////////////////////////////////

double을 문자열로..

_gcvt()

///////////////////////////////////////////////
ini파일 작성
WritePrivateProfileString()


////////////////////////////////////////////////
작업표시줄 내리기 올리기
APPBARDATA appBarData;
ZeroMemory(&appBarData, sizeof(APPBARDATA));
appBarData.cbSize = sizeof(APPBARDATA);
appBarData.hWnd = GetSafeHwnd();
appBarData.lParam = AUTOHIDE;//ABS_ALWAYSONTOP

SHAppBarMessage(ABM_SETSTATE, &appBarData);

////////////////////////////////////////////////
미니덤프 만들기
MiniDumpWriteDump()

////////////////////////////////////////////////////

마이크로세컨드까지 시스템 카운트 구하기

데브피아 불펌

LARGE_INTEGER nStartClk;

LARGE_INTEGER nEndClk;

QueryPerformanceCounter(&nStartClk); // 시작 클록

// 시간 잴 부분을 여기에....

QueryPerformanceCounter(&nEndClk); // 끝나는 클록

LARGE_INTEGER frequency;

LONGLONG timeinmicroseconds = 0;

QueryPerformanceFrequency(&frequency); // 시스템 클록을 얻어옴

if( !frequency.QuadPart )

timeinmicroseconds = -1;

else

timeinmicroseconds // 1000이면 밀리세컨, 1000*1000이면 마이크로초 1000*1000*1000이면 나노초~

= (nEndClk.QuadPart-nStartClk.QuadPart)*1000*1000/frequency.QuadPart;

////////////////////////////////////////////////////////

작업표시줄 윈도우 핸들

FindWindow("Shell_traywnd", "")

////////////////////////////////////////////////////////

내 네트워크 정보

netstat.exe

/////////////////////////////////////////////////////

곡선 사각형

RoundRect()

////////////////////////////////////////////////////

점이 사각형 안에 있는지 판별

PtInRect()

////////////////////////////////////////////////////

투명윈도우

SetWindowLong(this->m_hWnd, GWL_EXSTYLE, GetWindowLong( this->m_hWnd, GWL_EXSTYLE ) | WS_EX_LAYERED);

SetLayeredWindowAttributes(NULL, TransLevelToNum(nLevel), LWA_ALPHA);

IT 사이버강의
  • 다운로드
  • 다운로드
  • 다운로드
  • 다운로드
  • 다운로드
어셈블리어
웹프로그래밍
윈도우프로그래밍
인터넷언어
XML
C# 프로그래밍
Mobile XML
인터넷시스템 프로그래밍
디지털영상 컨텐츠제작
멀티미디어 프로그래밍
멀티미디어개론및응용기술
애니메이션1
웹3D 활용
웹디자인
컴퓨터그래픽스와VR
HCI
게임프로그래밍
가상현실
게임디지털콘텐츠 디자인
자료구조
컴퓨터구조
컴퓨터의이해
알고리즘
정보검색론
데이터마이닝
데이터베이스시스템
데이터분석처리
VB.NET 데이터베이스 응용
컴퓨터네트워크
네트워크관리
네트워크프로그래밍
멀티미디어통신
무선통신 네트워크
광대역통합망(BcN)
컴퓨터망과TCP/IP
무선인터넷
무선인터넷(대학원)
영상통신
인터넷/차세대인터넷 통신 프로토콜 및 라우팅 기술
CDMA이동 멀티미디어 통신공학
정보보호 및 암호학
전자상거래보안
정보보호이론
컴퓨터및네트워크보안
정보보호프로토콜
해킹과 사이버보안
인터넷보안(대학원)
정보보호 시스템
리눅스실습
운영체제
ERP 시스템
객체지향시스템
모바일컴퓨팅
모바일프로그래밍
인공지능
임베디드시스템
컴포넌트기반기술
신경회로망 이론 및 응용
ASIC설계및실습
VLSI CAD
나노제작과 정보산업에의 응용
정보가전이론 및 응용
최신 DSP 응용설계
고속디지털 가입자 정합장치
디스플레이
안테나공학
전자상거래 응용
전자상거래법
전자상거래시스템
전자지불시스템
e-Business 모델과 전략
고급 전자지불시스템
품질공학
GIS이론및응용
생체인식

'Computer Science' 카테고리의 다른 글

잡동산..정리가 필요함. MFC  (0) 2008.08.01
VHDL 동영상강좌  (0) 2008.07.19
VHDL(VHSIC Hardware Description Language)......  (0) 2008.07.06
PHP 동영상 강좌  (0) 2008.06.29
루트킷_4  (0) 2008.06.25

VHDL(VHSIC Hardware Description Language)......

1) 배경 (ALTERA사용설명서)

VHDL은 80년대초 미 국방성(DoD; Department of Depense)의 요구에 따라 연구되기 시작하여 1987년 12월에 IEEE 1076 표준안으로 발표되어 1992년에 표준화 되었다. 미 국방성에서 하드웨어 기술 언어를 개발하고 표준화 해야 할 필요성을 느끼기 시작한 것은 무기 체제에 채용되는 전자장비의 개선과 유지보수 하는데 과도한 경비의 지출이 요구되었기 때문이다. 최신 무기체제의 개발은 보통 2-30년이 소요되며 개발도중 발전된 기술을 채용하기 위해서는 그 이상의 시간이 걸리는 것이 보통이었다. 결과적으로 새기술을 채용하고 유지하는데 드는 비용은 처음에 예상했던 것보다 상상외로 과도한것이 되어 버린다. 새기술을 비록 DoD에서 소유 하더라도 기존의 무기체제에 적용하기 위해서는 많은 어려움을 격게 되는것이 사실이었다. 만일 DoD에서 새롭게 개발된 기술을 기존의 무기체계에 도입하기로 결정 했다면 몇가지 고려할 사항이 필요해 진다. 기존의 무기에 새기술이 이용된 부품을 채택하려면 원래의 장비 제작자는 새부품에 맞도록 공정을 새로 구성해야 하며, 기존 부품의 수명 기간동안 예비 부품들을 유지하는데 필요한 경비의 부담을 안게 된다. 이러한 이중의 경비 부담은 부품들 사이에 하드웨어적인 표준이 이루어 지고 있지 않기 때문에 발생하는 것이다. 따라서 DoD는 하드웨어의 기술하는데 필요한 언어를 표준화 함으로서 서로다른 전자장치들을 서로 결합하는데 특별한 해석없이 이루어지도록 해야할 필요가 있었던 것이다.

오늘날 DoD에서 채택한 VHDL은 매우 범용적인 형식을 취하고 있는데, 하드웨어의 기술에 표준을 두도록 함으로써 특별한 설계 툴에 구애받지 않으며 최종 상품을 제작하는데 특정 제작자를 선정해야하는 제한이 필요 없도록 한 것이다.

VHDL의 표준화는 DoD의 고속 집적회로(VHSIC; Very High Speed Integrated Circuit) 프로그램의 산물이다. 이 프로그램 수행자들은 상당히 복잡한 전자회로에 대해서도 고도의 추상화를 통하여 개발을 수행할 수 있는 새로운 기법이 필요했던 것이다. 이러한 고도의 추상화의 필요성은 예전에 하나의 전체 시스템으로 여겨지던 것이 오늘날에는 단일의 집적회로 칩정도로 간주되어 버렸기 때문이다. 오늘날 새로운 시스템의 설계는 과거의 설계기법으로 적정한 기간내에 수행할 수 없을 정도로 매우 복잡하다. 이와 같이 시스템이 매우 복잡해지자 VHDL의 능력에 대한 설계자들로부터의 요구가 있다. 먼저 개념적인 시뮬레이션 단계에서부터 세부적인 타이밍적인 시뮬레이션이 이루어질 수 있는 매우 융통성있는 언어가 필요하다. 또한 이러한 언어는 고도의 추상화 레벨에서 그보다 낮은 설계 레벨로의 전환이 이루어지며 어떠한 디자인 레벨에서도 시뮬레이션이 수행될 수 있어야 할 것이다. VHDL은 이러한 탑-다운(Top-Down) 방식의 설계에 매우 적절한 언어이다.

VHDL은 매우 넓은 범위의 설계수준(Design Level)을 지원한다. 그 예로서 다음과 같은 승가산기(MAC; Multiply Accumulator)알고리즘을 생각해 보자. MAC은 DSP 프로세서에 널리 이용되는것으로 영상처리를 비롯한 디지탈 신호처리에 폭넓게 이용된다.

FOR i IN 1 TO 1024 LOOP

result := result + a(i)*b(i);

END LOOP

c <= result;

VHDL은 위와 같이 +, -, *, /와 같은 수학 연산자(mathmatical operator)를 이용한 매우 추상화된 수준과 배열 형태의 기술(array of modeling)을 지원하므로서 설계자로 하여금 복잡한 디지탈 회로를 구체적으로 설계하기 전에 새 아이디어를 매우 신속하게 디지털 회로 특성에 따라 모의실험(simulation)을 해낼 수 있도록 해준다.

탑-다운 설계 (Top-Down Design)

탑-다운 설계방식은 고도의 추상화된 수준에서 세부적인 디지탈 회로로 세분해 나가는것을 말한다. 높은 추상화 수준(high level of abstraction)에서의 모의실험은 개발 초기단계에서 이루어질 것이며, 이때 기능적인 면(function)에서 검증이 이루어 질 것이다. VHDL이 지원하는 넓은 범위의 모델링 스타일은 어떤 아이디어의 구현 가능성을 검증하는 단계에서도 이용될 수 있음을 보여준다. 위에서 예로든 승가산기를 예로서 탑-다운 방식의 설계과정에 대하여 살펴 보기로 한다. 맨처음 단계로 승가산기를 추상적으로 기술하여 그 기능의 적합성을 판별해 보도록 한다. 앞서 지적했듯이 승가산기(MAC; Multiply Accumulator)는 콘볼버(convolver), 퓨리어 변환(DFT), 행렬 곱셈등에 이용되는 매우 기본적이면서 응용분야가 매우 넓은 기능이라 할 수 있다. 승가산기는 가장 높은 수준에서 다음과 같이 기술될 수 있을 것이다.

c <= multiply_accum(a,b);

이때 승가산기의 기능이 응용분야에 적절하다고 판단되면, 다음의 낮은 단계로 설계해 내려간다. 이 단계에서는 아이디어의 단계에서 구현의 단계로 전환됨을 의미한다. 승가산기 기능의 데이타 플로우 기술(dataflow description)은 그림 1-2와 같다. 데이타 흐름의 형식(dataflow format)이 결정되면 다음 단계로 게이트 수준의 설계가 이어질 것이다. 게이트 수준으로의 전환 단계에서는 자동 혹은 수동의 신디시스(synthesis)가 이루어지며 그 결과로 얻어지는 게이트 수준의 회로는 실제 제작을 위한 것으로 때때로 제작기술 지향적인(Technology specific) 것이 될수도 있다. 게이트 수준의 회로는 그림 1-3과 같다.

2) VHDL의 장점

VHDL의 가장 큰 장점은 위에서와 같은 모든 수준에서의 기술이 가능하다는 점일 것이다. 이러한 장점은 곧 단일 언어를 사용한다는 것 뿐만 아니라 어떠한 설계단계에서도 단일의 모의실험기(simulator)를 사용하므로서 설계오류검증은 물론 수정등 관리적인 측면에서 매우 효율적이 될 것이다. 기존의 디지탈 회로 설계과정을 보면, C 언어와 같은 고급 언어로 알고리즘의 검증과 기능 수준에서의 모의 실험을 할 수 있었다. 그리고 고도로 숙련된 설계자일 경우에 게이트 수준으로의 설계단계 전환이 가능할 것이며 이미 이때부터 하드웨어의 기술및 표현방법은 물론 모의실험이 달라지게 되며 때때로 특정 기술에 의존하는 설계 방식이 되어 버린다. 낮은 수준에서 발견되는 기능적인 오류의 수정은 그보다 상위 수준에서 관리하기는 매우 어렵게 되며, 때때로 제작기술의 전환은 불가능 하거나 많은 인력과 시간이 소요되는 작업이 되어 버린다.

3) 용어의 정의와 표현

혼란을 피하고 능률적인 내용의 전개를 위하여 전반적으로 사용되는 용어들과 표현 방법에 대하여 집고 넘어 가기로 한다.

ㆍ기능적 모델(Behavioral Model) - 고도로 추상적으로 기술한 시뮬레이션용 모델이다. 이러한 모델의 경우 디지탈 회로의 구현에 대한 세부적 내용은 없다. 보통 수학적인 연산자들이 그대로 사용된다.

ㆍ데이타 플로우 모델(Dataflow Model) - 디지탈 회로의 자동 신디시스(automatic synthesis) 툴을 겨냥한 시뮬레이션용 모델이다. 보통 기본적인 프로시져 구조(procedure constructs)나, 함수적인 연산자(functional operator)들로 기술된다.

ㆍ구조적 모델(Structural Model) - 구성 장치들 사이의 네트 리스트(netlist)적인 표현이다.

ㆍ혼합 수준 모델(Mixed-Level Model) - 위에서 열거한 세가지 모델을 혼용하여 기술한 경우이다.

ㆍ병행 처리(Cuncurrent) - 기술된 구문들이 서로 병행처리(operate in parallel) 되거나 관계 신호의 변화에 반응(initiate by the signal value changes)하여 처리되는 것을 말한다.

ㆍ순차 처리(Sequential) - 구문들이 서술된 순서에 따라 처리되는 것을 말한다. 이 경우 대부분의 프로그램밍 언어들과 같은 이치이나 특히 VHDL과 같은 하드웨어의 기술에서는 순차처리의 실행순서는 매우 중요하다.

ㆍ구동기(Drivers) - VHDL의 신호들은 구동기에 결합되어 있으며 이에 의하여 제어된다. 새로운 신호들은 모두 구동기를 통하여 전파된다.

ㆍ사건(Events) - 사건은 연결된 신호선의 값의 변화일때 발생한다.

ㆍ처리(Transaction) - 신호선에 값이 주어질때마다 처리가 이루어진다고 한다. 이때 값은 변화되었거나 또는 같은 값일 수 있다. 즉, 사건이 될 수 있거나 아닐 수도 있다.

ㆍ감응 리스트(Sensitivity List) - 사건에 따라 반응하게될 신호선들의 묶음이다.

VHDL강좌 및 요점 정리

◈ VHDL이란 : Very high speed integrated circuit Hardware Description Language의 약자로서 상위의 동작 레벨에서부터 하위의 게이트 레벨까지 하드 웨어를 기술하고 설계 하도록 하는 CAD 업계 및 IEEE 표준언어이며 미국 정부가 지원을 공인한 하드웨어 설 계 언어.

◈ VHDL의 특징

- VHDL은 IEEE에 의해 공인되어 하드웨어 개발과 문서화에 표준언어로 사용된다.

- 광범위한 기술 능력으로 시스템 레벨에서 게이트 레벨까지 하드웨어 회로 표현이 가능 하다.

◈ VHDL 규칙과 표현

1. VHDL의 규칙

- VHDL은 다른 언어와는 달리 대소문자의 구분을 요구하지 않는다.

- 파일명은 반드시 공백을 가지지 않는 문자열이어야 한다.

- VHDL에서 문장간의 구별은 여느 프로그램 언어와 같이 세미콜론( ; )으로 표시한다. - 주석은 "--"로 표시하며 이는 VHDL의 수행에는 영향을 미치지 않으므로 프로그램에 대한 설명 등의 문서화를 위해서 사용할 수 있다.

- 주석 부호가 있는 부분부터 그 줄 끝까 지 주석문으로 간주한다.

2. VHDL의 기본 구성과 표현

VHDL의 기본 구성으로써 여러 가지 종류의 Design Unit이 있다. 그중 가장 기본이 되는 최소한의 단위로써 Entity Declaration과 Architecture Body가 있다.

ㆍ객체(Object)와 자료형(Data Type) 및 연산자(Operator)

ㆍ동작적 표현(Behavioral Description)과 구조적 표현(Structural Description)

ㆍ순차 처리문과 병행 처리문

Entity 선언과 Architecture Body 선언

1. Entity 선언부는 사용자가 설계하고자 하는 시스템의 외적 연결을 담당하는 부분이다. 회로의 내부적인 구조나 연결 등을 고려할 필요가 없으며 여기서 정의한 것을 통해 다 음의 Architecture Body에서 내부적 동작을 여러 가지 방법으로 표현할 수 있다. 다시말 해외부와의 통신을 위한 입출력 선을 정의하는 것을 Entity 선언이라고 한다. 간단히 2 입력 and 게이트 회로를 가지고 예를 들어 보겠다.

예) entity mylogic_2and is

port( in_a, in_b : in std_logic;

out_y : out std_logic );

end mylogic_2and;

여기서 entity의 이름을 설정하는 것에 유의하도록 하자. VHDL은 2and_mylogic이나 mylogic 2and와 같이 앞에 숫자를 먼저 표기할 수 없으며, 공백이 있어서는 안된다. 외 부와의 통신을 위한 입력 선으로 in_a와 in_b가 출력 선으로 out_y가 선언되었다. 예에 나타낸 port와 std_logic에 대해서는 뒤에서 설명하겠다.

2. Architecture Body는 사용자가 설계하고자 하는 시스템 내부의 동작을 세부적으로 정 의 하는 부분이다.

예) architecture sample of mylogic_2and is

begin

out_y <= in_a and in_b;

end sample;

여기서 sample이란 architecture의 이름이다. 이것은 정해진 것이 아니라 임의대로 바꾸어 도 된다. architecture의 시작과 끝은 begin과 end이다. entity declaration에서도 begin을 사용하지만 드문 일이다. 예에서처럼 2 입력 and 게이트 회로에 대한 내부 동작은 표현이 간단하다. 이정도의 간단한 표현이라면 초보자들도 두려움을 가지기 힘들 것이다.

객체(Object)와 자료형(Data Type) 및 연산자(Operator)

1. 객체(Object)란 VHDL에서 값을 가질 수 있는 것을 말한다. 객체를 선언함에 있어 객체 가 어떠한 형태, 즉 자료형을 사용할 것인가를 결정해야 한다.

① 객체의 종류

ⅰ) Signal

VHDL 합성시에 Wire로 구현이 되며, 각 Component의 연결에 사용되는 외적 변 수이다. Signal 형태의 객체에 값을 대입하기 위해서는 예에서와 같이 '<='를 사 용한다.

외부 연결 신호의 선언에는 Signal로 선언하는 방법과 Port로 선언하는 방법이 있 다. Port와 Signal 선언에서의 또다른 차이는 입출력의 구분, 즉 Mode에 대한 정 의 여부이다.

◈ Port로 선언하는 방법

선언위치는 Entity 내부이다. Entity에 대한 설명은 앞장을 참고 하기 바란다.

예) port ( a, b : in std_logic;

c : out std_logic );

▶ Signal로 선언하는 방법

Signal의 선언위치는 architecture와 architecture의 시작을 나타내는 begin 사이이다.

예) architecture sample of logic is

signal a, b : std_logic;

signal temp : std_logic_vector(3 downto 0) := "1100"; -- 초기값을 대 입할 때는 기호 ':='를 사용한다.

begin

Signal 사용시 주의할 점은 대입기호 '<='는 그 값이 즉시 대입되는 것이 아니다. Process문 내에서 값이 대입되는 시점은 Process문이 끝나는 End process를 만날 때 비 로써 그 값이 대입됨을 유의해야 한다. 즉 시간이 지남에 따라 변한 값을 가진다.

ⅱ) Variable

Variable은 process나 부프로그램, 즉 function과 procedure에서 사용되며 process 내부에 서만 유효한 내적 변수이다. Variable은 Signal과 같이 Wire로 구현되지 않으며 단지 중 간 연산단계에 주로 이용된다. Variable에 사용되는 대입기호는 ':='이다. 이 기호는 값이 즉시 입력된다. 선언방식은 Signal과 유사하다.

▶ Variable로 선언하는 방법

Variable의 선언위치는 process와 process의 시작을 나타내는 begin 사이이다.

예) process(a, b)

variable temp1, temp2 : std_logic;

begin

또한 Variable은 즉시 값이 대입되지만 process 문을 빠져 나오면 그 값을 상실해 버린 다. 따라서 값을 보존 하기 위해서는 process를 빠져 나오기 전에 그 값을 Signal에 대입 해야 한다.

ⅲ) Constant

Constant는 초기에 선언한 상수의 값을 유지하는데 사용하며, 선언된 초기값을 바꿀 수 없다. 대입기호 역시 Variable과 마찬가지로 ':='를 사용한다.

▶ Constant로 선언하는 방법

Constant의 선언위치는 loop, generate문, 부프로그램 또는 generic에서 선언된다.

예) architecture sample of logic is

constant delay : integer := 5ns;

begin

2. 자료형(Data Type)은 어떤 임의의 상태값이나 결과를 받아들이거나 입력값으로 처리 할 수 있는 형태를 말하며, 기능과 표현상의 분류할 수 있다. VHDL에서 객체는 거의 무한한 종류의 자료형을 사용할 수 있다. 또한 사용자가 직접 만들 수 있다.

배열형 (Array Type)

제한형 (Constraint Type)

TO, DOWNTO

무제한형

(Unconstraint Type)

BIT_VECTOR, STRING, STD_LOGIC_VECTOR

집합형 (Record Type)

RECORD

① 자료형의 종류

자료형의 종류로는 크게 기능과 표현상의 분류로 구분되다. 간단히 요약하면 다음과 같다.

ⅰ) 표현적 분류

◈ Predefinde Data Type (이미 정의된 VHDL)

◈ User-Definde Type (사용자 정의)

ⅱ) 기능적 분류

◈ Scalar Type(숫자형)

숫자형이라는 것은 0, 1, 2, 3 … 등의 숫자로 그 순서를 정의할 수 있는 자료형이 다. 이러한 숫자형은 또다시 세부적으로 나뉜다.

열거형 (Enumeration Type)

BIT, BOOLEAN, HARACTER, STD_LOGIC

정수형 (Integer Type)

INTEGER

실수형 (Floating Type)

REAL

물리형 (Physical Type)

UNIT : TIME, DISTANCE

◈ Composite Type(복합형)

혼합형은 이러한 숫자형을 재 조합하여 필요한 만큼만 사용할 수 있도록 선언할 경 우와 여러 가지를 혼합하여 사용하고 싶을 때 사용한다. 혼합형은 배열형과 집합형 으로 나뉜다.

배열형 (Array Type)

제한형 (Constraint Type)

TO, DOWNTO

무제한형(Unconstraint Type)

BIT_VECTOR, STRING, STD_LOGIC_VECTOR

집합형 (Record Type)

RECORD

◈ Access Type(연결형)

연결형은 일반 Computer 언어에서의 Linked List를 사용할 수 있도록 만들어진 Point 형식과 유사하다. 연결형을 사용할 때 정확한 표현이 아니면 전혀 다른 결과 를 출력하기 때문에 언어를 시작하는 초보자라면 되도록 사용을 피하는 것이 좋다. 또한 연결형은 시뮬레이션을 위한 용도 외에 하드웨어적으로 구현하는 것으로는 아 직까지 그 지원이 미흡하다.

연결형 (Access Type)

ACCESS, NEW


◈ File Type

File Type은 File 입출력에 관련된 Data Type으로써 외부와의 입출력이나 특정 File의 변수를 선택하여 사용한다. 또한 이 자료형은 VHDL의 회로 표현에 대한 검 증 단계, 즉 Simulation Debugging에서 많이 사용되며 특별한 회로동작의 입출력 을 제외하면 거의 사용하지 않는다. 그래서 Synthetic Code가 아니란 것을 유의하 자. 파일형은 연결형과 같이 초보자에게는 어려운 점이 있으니 유의하기 바란다.

파일형 (File Type)

FILE

② 선언 방법

자료형을 선언할 때는 Type ~ is라는 예약어를 사용한다. 여기서는 Std_logic_1164 Package에 선언된 내용을 예로 들었다.

ⅰ) 열거형 :: 문자열을 하나의 Data로 묶어서 선언하는 것이다.

예) Type bit is ('0', '1');

Type boolean is (true, false);

ⅱ) 정수형 :: 정수형의 범위는 아래와 같이 구격화되어 있다.

예) Type integer is range -2147483647 to 2147483647;

Type byte is range -127 to 127;

ⅲ) 실수형 :: 실수형의 범위 역시 아래와 같이 -1.0E38에서 1.0E38까지 규격화되어 있다. 실수형의 최대값과 최소값은 High와 Low라는 Attribute를 사용해서 나타낼 수도 있 다.

예) Type real is range -1.0E38 to +1.0E38;

ⅳ) 물리형 :: 물리형은 시간, 거리 전류 등의 물리적인 단위를 나타낸다. 사용자가 범위를 정의하지 않으면 기본 단위의 고정 범위는 정수형의 범위과 같은 -2147483647에서 2147483647까지 이다.

예) Type time is range -1.0E38 to +1.0E38;

unit ps;

ns = 1000ps;

us = 1000ns;

ms = 1000us;

sec = 1000ms;

min = 60sec;

end unit;

ⅴ) 집합형 :: 집합형은 다른 종류의 숫자형이나 배열형을 하나로 만들어 사용할 수 있는 것이다. 각각의 Field로 나뉘어져서 별개의 Data Type을 선언할 수 있다. Record Type 으로 선언된 객체의 요소 값을 참조하기 위해서 객체와 요소사이에 '.'을 사용한다.

예) Type information is

record value : bit;

context : integer;

end record;

variable kk : information;

kk.value := '1';

kk.context := 9;

ⅵ) 배열형 :: 같은 종류의 숫자형을 한군데 묶어서 사용할 수 있다. 선언 방법에 따라 두 가지로 나뉜다.

◈ 제한형 :: 이미 선언된 Type을 정해진 범위에서 열거하게 만든 것으로 일종의 BUS Type으로 만든 것이다. 여기서 to와 downto는 내림차순과 오름차순으로 정의하는 것이 다.

예) Type byte is array( 7 downto 0 ) of bit;

variable a : byte;

a := "01110000";

◈ 무제한형 :: 제한형과는 다르게 선언된 Type을 또 하나의 변형된 새로운 Type으로 다시 만드는 열거형이다.

예) Type bit_vector( natural range <> ) of bit;

Type string is array ( positive range <> ) of character;

ⅶ) 연결형 :: 연결형은 동작적 모델링이나 하드웨어/소프트웨어 혼합형을 모델링 할 때 동적 메모리 할당(Dynamic Memory Allocation)을 필요로 하는 경우에 유용하고, 자료구 조(Data Structure)의 크기를 미리 결정할 수 없는 경우에 사용한다. 예를 들어 큐(Que)나 스택(Stack) 구조에서 연결 리스트(Linked List)를 만들기 위해 사용한다. 연결형에는 3가 지로 구분할 수 있으나 2가지의 형식만 알고 넘어가자.

◈ 불완전 선언 :: Type 선언시 아무런 표현도 하지 않고 선언자만을 표시

예) Type CELL;

◈ 일반 선언 :: Type 선언시 아무런 표현도 하지 않고 선언자만을 표시

예) Type LINK is access CELL;

ⅷ) 파일형 :: 파일형은 VHDL 규격 패키지의 TEXTIO 부분에 선언되어 있기 때문에 활 용만 하면 된다. 이것 또한 형식만 알고 넘어가자.

예) Type TEXT is file of STRING;

file INPUT : TEXT is in "std_input";

file OUTPUT : TEXT is out "std_output";

우선순위

높다

낮다

논리 연산자(Local Operator)

or, and, nor, nand,

xor,xnor

관계연산자(Relational Operator)

=, /=, >, <, >=, <=

덧셈연산자(Adding Operator)

+, -, &

단항연산자(Unary Operator)

+, -

곱셈(Multiplying)

*, /, mod, rem

기타 연산자

**, abs, not

3. 연산자(Operator)는 아래 표에 간단히 요약했다. 연산자를 연속으로 사용할 경우 우선 순위에 따라 연산 순서가 결정된다. 괄호 안의 연산자는 우선한다. 특히 논리 연산자는 두 피연산자의 자료형이 같아야 하며 연산자의 우선 순위가 가장 낮으나 not는 논리 연 산자이지만 다른 연산자보다 우선 순위가 높다. and나 or 연산자는 결합법칙이 성립되나, 여러 논리연산자를 함께 사용할 경우나 nand 혹은 nor을 연속해서 사용할 경우 결합법 칙이 성립되지 않으므로 괄호를 사용하고 결합순서에 주의해야 한다.

동작적 표현(Behavioral Description)과 구조적 표현(Structural Desciption)

VHDL의 표현 방법은 설계의 의도와 용도에 따라 방법을 선택하거나 혼합해서 사용할 수 있다.

1. 동작적 표현은 자료흐름적 표현과 Process문에 의한 표현으로 나뉘어 진다.

① 자료흐름적 표현(Data Flow Description)

부울 대수식, RTL 혹은 연산자를 사용하여 입력과 출력사이의 관계를 나타내는 것을 말한다. 자료흐름적 표현은 주로 병행 처리문에서 사용되며, 문장의 순서에 무관하게 동 시에 수행된다.

예) architecture data_flow of compare_logic is

begin

equal <= not( a(1) xor b(1) ) and not( a(0) xor b(0) );

end data_flow;

② Process문에 의한 표현

자료흐름적 표현 방법보다 추상화된 개념이다. 회로의 기능적 표현을 기능적 혹은 알고 리즘적으로 기술하는 것이다. Process문은 하드웨어 시스템을 모듈별로 기술하는데 편리 하다. 시스템은 하드웨어 모듈로 구성되어 있고, 각 모듈은 병행처리를 하면서 서로간의 통신을 통해 관계를 유지한다. Architecture내에 여러 개의 Process문이 있을 수 있으며 각 Process문은 병행처리를 하지만, Process문 내부는 순차처리를 한다. 또한 Process문 은 감지신호의 변화를 통해 동작하며 이를 통해 서로 통신을 한다.

예) architecture sample of compare_logic is

begin

process(a, b) -- ( )안의 a, b를 감지신호라 한다.

begin

if a = b then

equal <= '1';

else

equal <= '0';

end if;

end process;

end sample;

2. 구조적 표현은 동작적 표현보다 하드웨어 구조에 가장 가까운 표현이라 할 수 있다. 하 드웨어 회로상에서 IC소자들을 서로 선으로 연결하여 시스템을 구성하듯이 이미 설계 된 Component를 이용하여, 그 Component들을 서로 연결하여 System을 기술하려는 방 식이다. 구조적 표현에는 Generate문과 Generic문이 있다.

① Component문

이미 설계한 Entity를 부품으로 간주하여 구조적으로 설계하는 문이다. 이를 사용하기 위해서는 Component를 선언해야 한다. 또한 Port map()이란 예약어를 사용하여 Component를 사례화 시켜야 한다.

예) entity nand_component is

port( in1, in2, in3, in4 : in std_logic;

out1, out2 : out std_logic );

end nand_component;

architecture sample of compare_logic is

component nand2 -- component nand2를 선언

port( a, b : in std_logic;

y : out std_logic );

end component;

begin

u1 : nand2 port map( in1, in2, out1);

-- nand2를 사례화, 위치결합 방식

u2 : nand2 port map( a=>in3, b=>in4, y=>out2 );

-- 이름결합 방식

end sample;

예의 port map에서 위치결합과 이름결합 방식이 있다. 위치결합은 component와 결합할 때, port signal이 나열된 위치순서대로 연결되며, 이름결합은 port signal이 나열된 위치와 상관없이 각각의 '형식이름' => '실제이름'으로 연결된다. 이름결합은 직접 이름으로 연 결되기 때문에 결합의 순서에 무관하다. 또한 component를 사용하기 위해서는 사용할 c omponent가 작업 디렉토리에 등록이 되어 있어야 한다.

② Generate문

Generate문은 Component를 반복적으로 사용하기 위해서 사용한다. Generate문은 단순 반 복생성을 위한 for-generate문과 주어진 조건에 따라 반복처리하는 if-generate문이 있다. 예를 들기 위해 긴 entity 이름을 사용했다. 사용자는 굳이 그럴 필요는 없다.

◈ for-generate문

예) entity nand_component_for_generate is

port ( a, b : in std_logic_vector( 3 downto 0 );

y : out std_logic_vector( 3 downto 0 ) );

end nand_component_generate;

architecture sample of nand_component_generate is

component nand2

port ( a, b : in std_logic;

y : out std_logic );

end component;

begin

g1 : for i in 3 downto 0 generate

ux : nand2 port map ( a(i), b(i), y(i) );

end generate g1;

end sample;

◈ if-generate문

예) entity xor_component_if_generate is

port ( a : in std_logic_vector( 4 downto 0 );

prity_check : out std_logic );

end nand_component_generate;

architecture sample of nand_component_generate is

signal y : std_logic_vector( 3 downto 0 );

component xor2

port ( a, b : in std_logic;

c : out std_logic );

end component;

begin

g1 : for i in 3 downto 0 generate

-- g1, g2, g3, u4, ux는 레이블이다.

g2 : if i =3 generate

u4 : xor2 port map ( a(i+1), a(i), y(i) );

end generate g2;

g3 : if i < 3 generate

ux : xor2 port map ( y(i+1), a(i), y(i) );

end generate g3;

end generate g1;

parity_check <= y(0);

end sample;


③ Generic문

Generic(일반화)이란 Entity 내에 기술하며 Generic의 매개변수를 Entity에 전달함으로써, 회로의 개수나 입출력의 크기가 매개변수에 의해 결정되게 하는 것을 말한다. Generic의 매개변수로 사용되는 객체는 상수이며, 모드는 in으로 되어 있기 때문에 반드시 명시할 필 요가 없다. 또한 이 매개변수는 사용용도에 따라 두 가지로 나뉜다. 반복생성의 개수를 위 한 매개상수와 입출력의 크기를 위한 매개상수이다.

Generic의 사용은 사용할 Component의 Entity에 먼저 선언을 하고 다음 Component를 사 용하는 Logic System에서 이를 이용한다.

◈ 반복생성의 개수를 위한 매개상수

예) --[ 사용될 Component ] --

entity nand_generic is

generic ( size : integer := 8 );

port ( x, y : in std_logic_vector( size-1 downto 0 );

z : out std_logic_vector ( size-1 downto 0 ) );

end nand_generic;

architecture sample of nand_generic is

begin

z <= x nand y;

end sample;

-- [ Generic으로 선언된 Component를 사용하는 Logic System ] --

entity nand_sys is

port( a, b : in std_logic_vector( 3 downto 0 );

c : out std_logic_vector( 3 downto 0 ) );

end nand_sys;

architecture sample of nand_sys is

component nand_generic

generic( size : integer );

port ( x,y : in std_logic_vector( size-1 downto 0 );

z : out std_logic_vector( size-1 downto 0 ) );

end component;

begin

ux : nandg generic map(4) port map( a, b, c );

end sample;


◈ 입출력의 크기를 위한 매개상수

예) --[ 사용될 Component ] --

entity nandx_generic is

generic ( size : integer );

port ( x : in std_logic_vector( size-1 downto 0 );

z : out std_logic );

end nandx_generic;

architecture sample of nandx_generic is

begin

process( x )

variable temp : std_logic;

begin

temp := x(0);

for I in 1 to size-1 loop

temp := temp and x(i);

end loop;

z <= not (temp);

end process;

end sample;

-- [ Generic으로 선언된 Component를 사용하는 Logic System ] --

entity nand_sys is

port( a : in std_logic_vector( 3 downto 0 );

b : out std_logic );

end nand_sys;

architecture sample of nand_sys is

component nandx_generic

generic ( size : integer );

port ( x : in std_logic_vector( size-1 downto 0 );

z : out std_logic );

end component;

begin

ux : nandx generic map(4) port map( a, b );

end sample;


순차 처리문과 병행 처리문

1. 순차 처리문은 Process문 내에서 기술되며 wait문, if문, case문for-loop문이 있다. 그러나 각각의 Process문은 병행처리한다고 말한 바있다. 이제 순차 처리문의 종류를 알 아보자.

① Process문

Process문은 다음과 같은 형식으로 사용한다. 레이블은 생략해도 되며, 감지신호 리스트 는 대개 Process내의 입력신호들로 구성되며, 이 감지신호의 변화가 생길 때 process문 이 수행된다. 또한 하나의 Entity내에 여러 개의 Architecture가 있을 수 있는 것처럼 Process문 또한 하나의 Architecture 내에 여러 개의 Process문을 사용할 수 있다.

[레이블 :] process [(감지신호 리스트)]

begin

순차처리문;

end process [레이블];

② If문

고급언어와 같이 조건이 참이면 수행되는 if (조건) then 형식이 있다. if문은 또한 사용방 법에 따라 다음과 같이 3가지의 동작을 할 수 있다.

◈ 일반적 if문

if문은 멀티 플랙서(Multiplexer, 이하 MUX로 표기)를 이용하여 등가적으로 표현할 수 있으며, if문의 하드웨어 구현에 대한 이해에 도움을 준다.

예) if ( sel = '1' ) then

y <= a;

else

y <= b;

end if;

◈ 다중 if문

다중 if문의 경우에도 같은 방식의 MUX 논리를 적용할 수 있다. 다중 if문은 여러 개의 조건을 갖는 경우이다.

예) if ( sel0 = '1' ) then

y <= a;

elsif ( sel1 = '1' ) then

y <= b;

else

y <= c;

end if;

◈ 기억소자가 내포된 if문

이것은 else문이 없는 if문이다. 여기서는 if문에서는 조건이 거짓일 경우 수행되어야 하는 else문이 없으므로, else에 해당되는 출력은 과거의 출력 값을 그대로 유지하게 된다. 따라 서 출력은 조건이 거짓일 때는 과거의 출력 값을 지녀야 하므로 VHDL 합성시에 기억소자 가 내포된다. 이러한 기억소자가 내포된 if문의 예가 Latch와 Flip/Flop이다. Latch와 Flip/Flop의 차이는 if의 조건이 Level Trigger에 의해 출력이 변화되면 Latch이고, if의 조 건이 신호의 상승 혹은 하강에 의해,즉 Rising Edge나 Falling Edge Trigger에 의해 출력 이 변화되면 Flip/Flop이다. VHDL에서 이들을 표현하기 위해 event라는 예약어를 사용 한다.

예) if clk'event and clk = '1' then -- Rising Edge에서 동작하는 Flip/Flop

q <= d;

end if;

if clk'event and clk = '0' then -- Falling Edge에서 동작하는 Flip/Flop

q <= d;

end if;

if en = '1' then -- Leve Trigger에 의해 동작하는 Latch

q <= d;

end if;

③ Case문

Case문은 수식 값에 따라 문장을 선택한다. Case문의 수식 값은 integer형, 열거형 등과 같 은 자료형이 주로 사용되며, 진리표와 같은 기능표에 대한 설계에 적합하다. Case문은 When의 값과 비교하여 일치하면 그 문장을 수행한다. When의 수식을 여러 값으로 표현 할 때는 '|'(또는)이라는 기호를 사용한다.

예) case sel is

when "00" => y <= d(0);

when "01" => y <= d(1);

when "02" => y <= d(2);

when others => y <= d(3);

end case;





④ Loop문

Loop문은 반복처리 하기 위한 것이다. Loop문은 For-Loop형식과 While-Loop형식 및 단 순 Loop형식이 있다. 형식은 다음과 같다.

◆ [ 레이블 ] : for 루프변수 in 변수범위 loop

순차 처리문; -- 변수 범위만큼 반복

end loop [ 레이블 ];

◆ [ 레이블 ] : while 조건 loop

순차 처리문; -- 조건이 참일 때까지 반복

end loop [ 레이블 ];

◆ [ 레이블 ] : loop

순차 처리문; -- 무한 반복

end loop [ 레이블 ];

◈ For-Loop형식

For-Loop문은 루프변수가 1씩 증가 또는 감소하면서, 최종값에 도달할 때까지 Loop문에 둘러싸인 순차 처리문을 반복처리한다. 루프변수는 어떠한 객체로도 선언되지 않아야 되며, 오직 For-Loop의 루프변수로만 사용되어야 한다.

예) entity and_logic is

port ( a, b : in std_logic_vector( 3 downto 0 );

y : out std_logic_vector( 3 downto 0 ) );

end and_logic;

architecture sample of and_logic is

begin

process( a, b )

begin

for I in 3 downto 0 loop

y(i) <= a(i) and b(i); -- 변수 i에 대해서 4번 반복

end loop;

end process;

end sample;

◈ While-Loop형식

While (조건) Loop문은 조건이 참이면 loop에 둘러싸인 순차 처리문을 반복수행한다. While-Loop문의 조건은 반복횟수가 명확히 결정되지 않으면, 논리합성을 할 수 없으므로 주의해야 한다.단순 Loop문은 무한히 반복하므로 Loop를 빠져 나오기 위해서 exit문이 필 요하며, While문의 경우와 같이 반복횟수가 정해지지 않을 경우에는 VHDL 논리합성이 될 수 없다. 대부분의 VHDL 합성기는 While-Loop문과 단순 Loop문을 지원하지 않는다.

2. 병행 처리문

하드웨어 회로에서는 입력선로에서 출력선로로 신호가 전달되어 처리될 때 순차처리되는 것이 아니라 병행처리된다. 따라서 하드웨어 구조를 기술하는VHDL의 문장은 병행처리에 기반을 두고 있다고 볼 수 있다. Architecture문 내부에 표현되는 모든 VHDL 문장은 Process문의 내부를 제외하고는 모두가 순서에 무관하는 병행 처리문이다. 이러한 병행 처 리문에는 조건적 병행처리문(When~Else)과 선택적 병행 처리문(With~Select)이 있다.

① 조건적 병행 처리문 :: 순차처리문인 if문과 유사하다.

signal_이름 <= 파형1 when (조건1) else

파형2 when (조건2) else

파형n-1 when (조건n-1) else

파형n;

예) entity logic is

port ( a, b, c, d : in std_logic;

y : out std_logic );

end logic;

architecture sample of logic is

begin

y <= '0' when d='0' else

'0' when c='1' else

'0' when ( a='1' ) and ( b='1' ) else

'1';

end sample;



② 선택적 병행 처리문 :: with이하의 수식값에 의해 판단하며, Case문과 유사하다.

with (수식) select

signal_이름 <= 파형1 when (선택값1),

파형2 when (선택값2),

파형n-1 when (선택값n-1) else

파형n when others;

예) entity logic is

port ( x : in std_logic_vector( 1 downto 0 );

y : out std_logic_vector( 3 downto 0 ) );

end logic;

architecture sample of logic is

begin

with x select

y <= "0001" when "00",

"0010" when "01",

"0100" when "10",

"1000" when others;

end sample;

'Computer Science' 카테고리의 다른 글

VHDL 동영상강좌  (0) 2008.07.19
itfind IT 사이버 (동영상) 강좌  (0) 2008.07.06
PHP 동영상 강좌  (0) 2008.06.29
루트킷_4  (0) 2008.06.25
루트킷_3  (0) 2008.06.24
01. HTML이란 무엇인가!
02. 하이퍼링크, 테이블태그의 사용방법
03. 테이블 태그의 심화
04. 스타일 시트
05. 웹서버/웹브라우져/PHP란 무엇인가
06. Hello PHP
07. Get방식을 이용한 변수의 전달
08. Get방식의 전달과 테이블, 스타일태그의 사용
09. form태그를 이용한 값의 전달
10. 그외의 form관련 태그들
11. 메일 발송하기
12. IF문의 사용
13. IF문을 이용한 수우미양가 출력하기
14. 여러개의 파일을 거쳐서 값전달하기
15. 여러개의 파일을 이용한 설문조사
16. for 반복문의 사용
17. 배열변수의 사용
18. 파일의 생성과 사용
19. 파일을 이용한 메모장 만들기 #1
20. 파일을 이용한 메모장 만들기 #2
21. 파일을 이용한 메모장 만들기 #3
22. 다른 사이트의 특정부위만 가져오기
23. 파일을 이용한 간단한 카운터 제작하기
24. 쿠키의 사용
25. 회원가입처리 / 로그인 구현
26. 쿠키의 시간제한
27. 기본적인 환경변수들
28. SQL이란, phpmyadmin 설치방법
29. 테이블을 만들어보자!
30. 테이블에 값을 추가해보자
31. DB에서 값을 가져와 출력하기
32. 자료의 정렬순서를 바꿔보자
33. 자료를 삭제해보자
34. 자료를 수정해보자
35. 사용자 정의함수의 사용
36. 사용자 정의함수 심화학습
37. 기본적인 페이징 처리하기
38. 삭제시 비밀번호 물어보기
39. 회원가입/로그인 처리
40. 파일의 업로드
41. 추천사이트(북마크)프로그램 만들기
42. 세션의 사용
43. 공용함수의 제작/쿠키로그인(보안)
44. 즐겨찾기 제작하기
45. 우편번호 검색기 제작해보기
46. 다중검색기 제작해보기
47. phpmyadmin 최신버젼 설치하기
48. 엑셀파일 db에 넣기, 동적 select 구현
49. 만년달력 제작해보기

'Computer Science' 카테고리의 다른 글

itfind IT 사이버 (동영상) 강좌  (0) 2008.07.06
VHDL(VHSIC Hardware Description Language)......  (0) 2008.07.06
루트킷_4  (0) 2008.06.25
루트킷_3  (0) 2008.06.24
루트킷_2  (0) 2008.06.23
뒤에서 수군거리는 거 다 안다
상사에게 찍히는 ‘나쁜 습관’
정치적 행동은 ‘왕따’ 지름길 … 동료 몰래 처세 잘한다는 생각은 착각
이코노미스트 우리는 누구나 ‘을(乙)’이다. 직장인은 늘 상사 앞에 작아지는 ‘을’일 수밖에 없다. 대한민국 최고 ‘갑’ 대통령조차 국민 앞에서는 ‘을’이다. ‘을’이 살아남으려면 생사여탈권을 쥔 ‘갑(甲)’과 잘 소통해야 한다. 그런데 혹 작은 습관 때문에 찍히고 있지 않은가. 1년의 반이 지난 6월, 이코노미스트와 함께 상사에게 찍히는 ‘나쁜’ 습관은 없는지 체크해 보자.
“자신보다 유능한 후배를 키우는 지도자가 돼라.” 평사원에서 최고경영자에 오른 샐러리맨 신화의 주인공, 조정남 전 SK텔레콤 대표이사 부회장은 지난 24년을 돌아보며 이렇게 말했다.


후배 양성에 힘쓰고 누구에게든 친근하게 다가섰다는 조 전 부회장이 가장 아낀 직원은 팀워크를 잘 이루는 사람이다. 그는 “기업을 경영하는 한 사람으로서 나는 올바른 인격을 바탕으로 주변과 조화를 이룰 줄 아는 사람을 좋아한다”고 말한 바 있다.

또 한 명의 샐러리맨 신화, 이구택 포스코 회장은 포스코 그룹 신임 임원 교육장에서 직원이 갖춰야 할 요소를 열정으로 꼽았다.

“직장생활을 오래 하다 보면 사람들을 평가하기도, 평가받기도 하는데 신임 임원 여러분은 어떤 요소들로 직원들을 평가합니까? 아마 조금씩 관점의 차이는 있겠지만 저는 무엇인가를 해보겠다는 ‘열정’을 가장 높게 평가합니다.”

대기업 평사원에서 최고경영자로 그리고, 10여 년 이상을 최고의 자리에 머문 이 두 사람은 모범사례라 할 수 있다. 이 둘이 꼽은 될성부른 부하 직원의 덕목은 팀워크와 열정이었다. 당연한 얘기지만 많은 경영진이 이를 강조하는 데에는 팀워크와 열정을 보여주는 직원은 드물기 때문일 것이다.

인사관리(HR)에 강점을 가진 컨설팅펌의 대표들은 인정받기는커녕 찍히는 직원에겐 공통적으로 ‘나쁜’ 습관이 있다고 말한다.

‘우리 팀이 단결이 안 되는 건 ○○ 때문이야’ ‘부장은 ○○만 예뻐해’라고 투덜거리고 있다면, 자신의 직장생활을 돌아보자. 만약 아래의 나쁜 습관이 단 하나라도 있다면 당신은 찍혔다.

정치적으로 행동한다

타워스패린의 박광서 한국지사장은 “정치적으로 행동하는 것이 성공의 지름길인 줄 아는데 이것은 명백히 착각”이라고 단언했다. “한참 열정을 다해 역량계발을 해야 할 직원이 정치적인 꼼수만 부리는 것을 누가 탐탁하게 여기느냐”는 것이다.

정치적으로 행동하는 직원이 가장 잘하는 것 중 하나가 뒷담화다.

“상사는 사실 누가 여기 가서 이 말하고 저기 가서 저 말하는지 다 안다. 시차가 있을 뿐이다. 물론, 뒷담화에 참여를 안 하면 ‘왕따’를 당할 수도 있다. 그러나 뒷담화를 잘하는 사람은 누구에게나 경계대상 1호다. 뒷담화 잘하는 사람이 내 욕 안 하라는 법이 없기 때문이다.”

그러면 상사는 정치적인 직원을 어떻게 아는 것일까. 정보의 속성 때문이다. 박 지사장은 “상사는 여러 가지 채널이 있어 다양한 곳에서 정보가 들어온다. 정보의 속성은 위로 갈수록 하나로 모아진다. 정치적으로 행동하는 사람의 특징은 상사나 동료 모르게 자신만 처세를 잘한다고 생각한다”고 설명했다.

이때 상사는 정치적인 부하 직원에게 바로 직격탄을 날리지는 않는다. 그야말로 찍어두는 것이다. “괜히 건드렸다간 시끄러워지니 내버려두지만 크게 키워주지는 않을 것”이라고 설명했다. 정치적인 사람은 팀워크에 약할 수밖에 없기 때문이다.

팀워크를 약화시킬 뿐 아니라 뒷담화는 상사를 곤란에 빠뜨리기도 한다. 뒷담화에서 나온 이야기가 ‘정보’로 둔갑할 때다.

박 지사장은 “한국처럼 뒷담화 잘하는 사회에서는 평가의 공정성이 문제될 수밖에 없다”며 “임원 리더십 교육과정에서 균형감각과 공정한 평가를 늘 강조한다”고 말했다.

시키는 일만 잘한다

박경미 휴잇어소시어츠 코리아 대표는 “부하 직원이 예전처럼 시키는 일만 따박따박 받아서 하는 습관, 즉 공급자의 역할로는 눈 밖에 나기 쉽다”고 조언했다.

“이제는 한 걸음 나아가 팀내에서 수요를 창출하는 역할도 할 줄 알아야 한다. 윈-윈 조직으로서 팀의 성공에 필요한 것을 미리 상사에게 제시할 줄 아는 부하직원이 인정받는다”는 것이다.

조정남 SK텔레콤 전 부회장이 “봉급생활자라면 머리를 신발장에 넣지 말고 반드시 들고 회사에 출근해야 한다”고 말한 데에 성공하는 직장생활의 노하우가 담겨 있는 것이다.

회의시간에 새로운 아이디어를 제안하지 못한다거나, 동료 의견에 반론이나 보충조차 할 수 없다면 상사는 당신에 대한 애정을 거둘 것이다. 그렇다고 혼자 튀려고 해서는 곤란하다. 이때는 다 알다시피 찍힌다.

헤이그룹 노재항 부사장도 “과거의 성과를 능가하는 무엇인가를 만들어내려는 생각 없이, 기존에 해왔던 행동방식이나 사고를 그대로 수행하려는 자세를 보이는 것을 피하라”고 조언했다.

부하의 이러한 자세는 상사에게 그 직원이 진취적이고 성취지향적인 태도가 없다는 나쁜 인상을 심어주게 되고 ‘성취 의욕이 없는 사람’으로 바로 찍히게 되는 것이다.

상사 입장에서 생각해 보면 ‘시키는 일만 잘하는 직원’이 나쁜 이유가 분명해진다. 예전에는 성과만을 강조하다 보니 단기적인 성과에 너무 치중하는 경향이 있었다. 그래서 부하 직원의 업적을 가로채면서 승진하는 사례도 생기고, 상사와 부하 직원의 관계가 수요와 공급의 관계를 형성해 왔다.

그러나 요즘 우수한 기업일수록 단기적 성과보다는 성장의 지속 가능성을 중요하게 여긴다. 보다 장기적 성과, 기업의 윤리성, 투명성, 공정성 등이 강조되고 있는 추세다.

박 대표는 “변화하는 조직문화에서 상사와 부하 직원의 관계를 ‘상하관계’가 아닌 윈-윈 (Win-Win) 관계로 이해하는 것이 필요하다”고 설명했다.

“그건 너무 어려운 일인데요?”

한때 현대중공업의 광고가 화제가 된 적이 있다. 이른바 현대중공업의 지면광고 ‘해봤어?’ 시리즈다. 고(故) 정주영 명예회장이 생전에 즐겨 쓰던 표현인 “해봤어?”는 정 회장이 전경련 회장 시절 가까이서 보필했던 박정웅씨의 책 『이봐, 해봤어』를 통해 알려졌다. 무에서 유를 창조한 정 회장의 기업가 정신과 신화 창조의 일화는 직장인으로서 한 번쯤 꿈꿀 만한 일이다.

박광서 대표는 “정주영 명예회장이 말하듯 많은 CEO가 직원에게 ‘해봤냐’고 말하고 싶어 한다. 쉬운 일은 없다. 경영이란 것 자체가 어려운 일을 해결하는 것”이라고 말했다. ‘어렵다’고 불평하는 직원에게 상사는 “이번에 쟤 또 무슨 이유를 댈까”라고 생각하기 십상이다.



임성은 기자 lsecono@joongang.co.kr

루트킷_4

루트킷의 특징을 간단히 표현하자면 ‘은닉’과 ‘위장’이라고 할 수 있다. 말 그대로 은닉은 ‘숨는 것’이고 위장은 ‘아닌 척’하는 것이다. 루트킷이라는 용어 자체가 자신을 숨기기 위한 기술이라는 의미로 사용되듯이 은닉은 루트킷에 대한 설명에서 떼어놓을 수 없다. 위장은 은닉을 수행하기 위한 또 다른 방법으로써 자신의 존재가 들어나도 아닌 척 하거나 아예 일부러 자신을 들어내서 혼란을 주는 것이다. 예를 들면, 대부분의 루트킷 탐지가 은닉된 프로세스나 파일들을 대상으로 하고 있는데, 그런 루트킷 탐지 소프트웨어가 작동했을 때 스스로 은닉을 풀어서 루트킷 탐지 소프트웨어에 의해 검출되지 않도록 하는 것이다. 4부에서는 루트킷이 은닉을 위해서 사용할 수 있는 가상화(Virtualization) 기술에 대해 살펴본다.

루트킷의 예고된 위험

가상화(Virtualization)

윤근용 happyme9@nhncorp.com | 필자는 시스템 프로그래머로 시스템에 대한 다양한 분야에 관심이 많다. 특히 보안 문제에 대한 관심이 높아 다년간 보안 업무에 종사하고 있다. 바이러스 보안 업체를 거쳐 현재는 NHN에서 주로 보안 관련 프로젝트를 수행하고 있다.

루트킷은 자신을 감추기 위해서 다양한 기술을 이용한다. 가장 대표적인 것이 후킹을 이용하는 방법이다. 주로 SSDT 후킹과 같은 커널 레벨에서의 후킹이 많이 사용되는데 이를 위해서는 루트킷이 커널 드라이버 형태로 작성돼야 한다. 루트킷은 스스로를 더욱 교묘하고 은밀하게 숨기기 위해 커널 패치와 같은 로우 레벨의 조작을 수행하는 경우가 많으며, 심지어는 BIOS와 같은 펌웨어를 이용하기도 한다. 가상화 기술을 이용하는 루트킷 기술은 이제 더 이상 새로운 것이 아니며, 이미 새로운 위협으로 예상되어 왔다. 하지만 현재 대부분의 보안 제품으로는 탐지하기 힘들다면 것이 문제라고 할 수 있다. 즉 가상화 기술을 이용해서 자신의 VM(Virtual Machine) 안에서 독자적으로 루트킷이 실행된다면 현재의 보안제품으로는 탐지가 불가능하다. 따라서 악성 코드가 가상화 기술을 이용하는 경우에는 문제가 더욱 심각하다.

가상화의 위협

루트킷 탐지가 어려운 이유 중 하나는, 루트킷 탐지 소프트웨어와 동일한 레벨의 권한을 가지고 수행된다는 것이다. 대부분의 루트킷 탐지 소프트웨어는 커널 레벨에서 동작하지만 루트킷 또한 커널 레벨에서 동작하는 커널 드라이버 형태로 많이 작성된다. 따라서 서로 동일한 능력을 가지고 대치되기 때문에 탐지 및 무력화가 쉽지 않은 것이 현실이다. 따라서 어느 한쪽이 더 높은 권한을 가진다면 승패는 한쪽으로 기울 것이다. 커널 레벨보다 한 층 더 높은 권한을 부여 받을 수 있는 방법이 바로 가상화다. 가상화로 커널 레벨보다 높은 권한을 갖는 루트킷은 커널 레벨에서 동작하는 루트킷 탐지 소프트웨어를 무력화 시킬 수 있다(<그림 1> 참조). 이와 반대로 루트킷 탐지 소프트웨어가 커널 레벨보다 더 높은 권한을 갖는다면 커널 레벨에서 동작하는 루트킷은 부처님 손바닥 안의 손오공 신세가 돼 버릴 것이다(<그림 2> 참조).

가상화

가상화(Virtualization)라는 용어는 다양한 분야에서 사용된다. 컴퓨터 시스템에서의 가상화는 시스템의 유효한 리소스(하드웨어, 운영체제, 데이터 등)를 보다 효율적으로 사용하기 위해서 연구되어 왔다. 그 중 하나가 운영체제 가상화인데, 이는 하드웨어 리소스가 부족했던 6~70년대 VMM(Virtual Machine Monitor)이라는 개념으로 출발했다. VMM은 하드웨어와 운영체제 사이에 위치해서 하드웨어에 대한 리소스(CPU, Memory, Physical Disk)를 가상화한다. VMM이 운영체제에게 제공하는 인터페이스는 하드웨어가 운영체제에게 제공하는 인터페이스와 동일하기 때문에 운영체제 입장에서는 VMM의 존재 여부에 영향을 받지 않고(존재 자체를 모르고) 동작할 수 있으며, VMM에 의해서 동시에 여러 운영체제가 동작할 수 있다.

하이퍼바이저

하이퍼바이저(Hypervisor)란 하나의 시스템에서 동시에 여러 개의 운영체제를 사용할 수 있게 해주는 가상화 플랫폼을 말한다. 가장 대표적이고 쉬운 예로 VM웨어를 들 수 있다. 하이퍼바이저는 다음과 같이 크게 두 가지 형태로 구분된다. 물론 아래의 두 형태가 복합된 형태 또한 가능하다. Type 1에서는 VMM (Virtual Machine Monitor)이 Host 운영체제 안에서 동작한다.

Type 2에서는 VMM(Virtual Machine Monitor)이 하드웨어와 게스트 운영체제(Guest OS) 사이에 존재한다.

다음은 Type 1형태로 구현된 VM웨어의 내부 구성요소이다.

- VMApp : 유저 레벨 애플리케이션(Host OS의 App로써 동작)
- VMDriver : 호스트 시스템을 위한 디바이스 드라이버(Host OS의 커널에서 동작, 호스트 운영체제와 게스트 운영체제 간의 실행 context switching을 담당한다.)
- VMM : VMDriver가 로드되면서 생성되는 virtual machine monitor

소프트웨어 가상화

IA-32에서 운영체제는 Ring 0, 애플리케이션은 Ring 3 권한으로 동작한다. 하지만 가상화 환경에서는, 하드웨어와 직접 연결되어 가상화를 수행하는 VMM이 Ring 0 권한으로 실행돼야 한다. 이때, 운영체제도 동일하게 Ring 0 권한으로 실행되면 운영체제가 VMM 코드를 제어할 수 있을 뿐만 아니라 VMM에 의한 가상화 자체가 불가능해 진다. 따라서 운영체제는 VMM보다 낮은 권한을 부여한다. 이러한 작업을 Ring Deprivileging이라고 하는데, Ring Deprivileging은 다음과 같은 두 가지 형태로 수행된다.

♦VMM : Ring 0
Guest OS : Ring 1
App : Ring 3

♦VMM : Ring 0
Guest OS : Ring 3
App : Ring 3

이렇게 운영체제의 실행 권한이 하향 조정됨으로써 야기되는 문제점 때문에 VMM의 역할 비중이 커졌다. 즉 운영체제가 하드웨어에 접근하는 작업이나 특정한 시스템 콜과 같이 Ring 0 권한이 요구되는 작업을 모두 모니터링해서 그에 맞는 작업을 수행해야 한다. 동시에 여러 개의 운영체제가 동작 중이라면 이들에 대한 Context Switching을 VMM이 담당해야 한다. 실행권한 변경에 따른 문제점을 극복하기 위해서 Paravirtualization, Binary Translation 등의 방법들이 이용됐다.
하지만 Paravirtualization은 운영체제의 커널을 수정해서 특정한 VMM에서 가상화가 가능하도록 하는 것인데, 이는 리눅스와 같은 오픈소스 운영체제에서나 가능한 일이며 일반적으로 적용할 수 있는 방법이 아니다. 또한 Binary Translation은 상대적인 성능상의 부하가 문제가 된다. 이런 소프트웨어 가상화에 의한 문제점을 해결해 주는 것이 CPU 레벨의 가상화라고 할 수 있다. 즉 가상화를 구현하기 위해서 사용된 Paravirtualization, Binary Translation과 같은 작업을 CPU 레벨에서 지원하는 것이다.

하드웨어 가상화

인텔, AMD의 CPU 레벨에서 제공하는 하이퍼바이저를 하드웨어 가상화라고 할 수 있다. 인텔이나 AMD에서 CPU 레벨의 가상화를 지원하기 전까지는 대부분 소프트웨어 가상화에 의존해왔다. 하지만, IA-32 자체가 가상화에 맞지 않는 구조를 가지기 때문에, (애초에 하나의 운영체제만 동작하도록 설계됨) 소프트웨어 가상화 구현의 어려움 뿐만 아니라 그에 따른 부가적인 문제점을 갖게 된다.

인텔

- VT(Virtual Technology, 코드명: Vanderpool) 간단히 IVT라고 한다.
- IA-32 기반 CPU에서의 IVT를 VT-x라고 하며 IA-64(itanium)기반 CPU에서의 IVT를 VT-i라고 한다.
- 제공 CPU: 일부 펜티엄 4, Xeon, 코어듀오, 코어 2 듀오 프로세서(IVT기능을 지원하는 정확한 인텔 CPU 목록은 http://process orfinder.intel.com에서 조회 할 수 있다.)

AMD

- AMD-V(AMD Virtualization, 코드명: Pacifica)
- 제공 CPU: (64비트 x86 아키텍처) AMD 애슬론 64, 애슬론 64×2, 튜리온 64?2, 옵테론, 페놈

VT-x

이제 인텔 CPU에서 제공하는 VT-x에 대해서 간단히 살펴보자. VT-x에서는 VMX(Virtual Machine Extension)라는 실행 모드를 지원함으로써 가상화를 가능케 한다. VMX는 두 가지 모드로 나뉘는데, VMX root operation(fully privileged Ring 0) 모드와 VMX non-root operation(less privileged Ring 0) 모드이다. 여기서 VMM은 VMX root operation 모드로 동작하고 VM은 VMX non-root operation 모드로 동작한다. VMX root operation 모드는 몇 가지를 제외하고는 일반적인 CPU 동작 모드와 거의 동일하다. VM non-root operation 모드에서의 CPU 작업은 가상화를 지원하기 위해서 제약되거나 변경된다. 더 정확히 말하면 root operation 모드로 동작하는 VMM은 Ring 0 보다 권한이 높고, non-root operation 모드로 동작하는 게스트 운영체제는 Ring 0, App는 Ring 3 권한으로 동작한다. VMX의 동작 모드 변경은 다음 두 가지 경우가 있다.

♦ VM entry: VMM이 VMX non-root operation 모드로 VMX not-root operation 모드로 동작하는 VM의 코드(운영체제, 애플리케이션)가 실행된다.
♦ VM exit: VMM이 VMX root operation 모드로 VMX root operation 모드로 동작하는 VMM으로 CPU 제어권이 반환된다.

VMS operation의 시작과 종료는 VMXON, VMXOFF 명령에 의해서 수행된다. 특정한 명령이나 이벤트(예외 발생, I/O 디바이스 접근, 특정 레지스터 접근 등)가 발생하면 VM exit에 의해서 VMX root operation 모드가 된다. VMCS(Virtual Machine Control Structure)의해서 VMX의 모드 변경과 VMX non-root operation이 관리된다(AMD-V의 경우는 VMCB (Virtual Machine Control Block)).

<그림 5>에서 보는 바와 같이 루트킷 디텍터 등의 보안 프로그램은 어디까지나 루트킷 VM에 의해서 로드되고 컨트롤 되는 하나의 게스트 운영체제 상의 커널 모듈이거나 일반적인 애플리케이션일 뿐이다. 따라서 이런 구조에서의 루트킷 탐지는 거의 불가능하다(100 퍼센트 불가능한 것은 아님).

가상화 기술을 이용하는 루트킷

이제 앞에서 언급한 가상화 기술을 이용하는 루트킷에 대해서 알아보자.

SubVirt

SubVirt는 마이크로소프트와 미시간 대에서 가상화 기술을 이용한 루트킷이 가능한지 여부를 증명하기 위해서 만든 것이며, 기존의 가상화 소프트웨어(VM웨어, 버추얼-PC)를 이용한 것이므로 진정한 의미의 가상화를 이용한 루트킷이라고 보기 힘들다. SubVirt는 호스트 운영체제로 리눅스를 이용하며 시스템의 Boot Sequence를 수정해서 원래의 운영체제를 로드한다.

Vitriol

Dino Dai Zovi에 의해서 개발된 VM 루트킷으로써 MaxOS X 운영체제에서 동작하며 인텔 VT-x(인텔 코어듀오/솔로)기술을 이용했다(2006년 10월 마이크로소프트의 BlueHat 컨퍼런스에서 발표되었다).

BluePill

Vista x64(AMD 64)에서 AMD-V를 이용한 VM 루트킷. BIOS 수정이나 SubVirt처럼 Boot Sequence를 변경시키는 작업을 수행하지 않으므로 재부팅 없이 언제든지 설치가 가능하며 어떤 입출력에 대해서도 가상화를 않기 때문에 성능상의 부하가 거의 없다.

현재 그리고 미래

루트킷과 악성코드를 구분하는 게 모호하긴 하지만 엄밀히 말하면 같지는 않다. 루트킷과 악성코드의 결합, 정확히 말하면 악성 코드가 루트킷 기술을 사용하면(이미 이런 악성코드가 많은 것이 현실이다.) 더욱 강력한 공격력과 전파력을 보유한다. 그러면 탐지나 방어, 치료(제거)와 같은 후속 조치가 더 어려워질 것이다. 사실, MPack과 같은 공격 툴킷이 거래되고 있는 블랙마켓이 존재하는 것이 현실이다. 심지어 악의적인 행위 자체도 거래가 된다. 이를 통한 잠재적인 피해 규모는 산정하기 힘들 정도다. 예전에는 악성 코드를 여러 형태로 분류했지만 이미 오래 전부터 대부분의 악성코드가 복합적인 성격을 띄기 시작했다. 서로간의 사용 기술을 복합적으로 사용하고 있을 뿐만 아니라 목적 자체도 복합적인 것들이 많아졌다.

CPU에서 제공하는 가상화 기능을 이용하는 것은 단지 루트킷의 은닉 기술 중 하나일 뿐이다. 가상화 이외에도 하드디스크의 사용하지 않는 섹터를 이용해서 은닉하는 기술, ATAPI 스펙을 이용한 은닉과 같이 탐지가 불가능한 경우가 상당히 많다. 가장 무서운 것은 알려지지 않은 루트킷, 좀더 정확히 말하면 알려지지 않은 기술을 사용한 루트킷은 존재여부 조차도 모르고 있다. 더 큰 문제는 기존의 보안 프로그램이 교묘한 방법을 사용하지 않고 전통적인 기술을 사용하는 루트킷도 만족스럽게 탐지하지 못한다는 점이다. 탐지해도 안전하게 제거하는 기술은 아직 미흡한 실정이다. 루트킷 탐지를 위한 기술 영역뿐만 아니라 루트킷을 안전하고 완벽하게 제거하는 기술 영역도 지속적인 연구가 필요하다. 현재까지 안정적으로 루트킷을 탐지할 수 있는 증명된 기술은 없다. 루트킷이 사용하는 기술과 보안 소프트웨어가 사용하는 기술이 크게 다르지 않기 때문에 누가 먼저 새로운 기술을 이용하느냐에 따라서 그 승패가 좌우된다. 마이크로소프트에서 얼마 전에 코모쿠(komoku)라고 하는 안티 루트킷 솔루션 업체를 인수했다는 소식은 시사하는 바가 크다고 할 수 있다.

'Computer Science' 카테고리의 다른 글

VHDL(VHSIC Hardware Description Language)......  (0) 2008.07.06
PHP 동영상 강좌  (0) 2008.06.29
루트킷_3  (0) 2008.06.24
루트킷_2  (0) 2008.06.23
luabind/C++ <;-> 루아 클래스간 상호 참조 및 호출 예제  (0) 2008.06.22

루트킷_3

유저모드에서 시작된 루트킷과 보안제품의 싸움은 커널모드에서도 계속된다. 3부에서는 커널모드 API 후킹의 대표적인 기법인 SDT 후킹의 원리에 대해서 살펴보고, 이를 둘러싼 해커와 보안 개발자들 사이의 전쟁에 관한 이야기에 대해서 살펴본다. 다양한 기술을 통해서 그들의 아이디어를 훔쳐보자.

커널모드 루트킷 기술

SDT 후킹의 창과 방패

신영진 pop@jiniya.net, http://www.jiniya.net|웰비아닷컴에서 보안 프로그래머로 일하고 있다. 시스템 프로그래밍에 관심이 많으며 다수의 PC 보안 프로그램 개발에 참여했다. 현재 데브피아 Visual C++ 섹션 시삽과 Microsoft Visual C++ MVP로 활동하고 있다. C와 C++, 프로그래밍에 관한 이야기를 좋아한다.

SDT 후킹

SDT(Service Descriptor Table) 후킹은 시스템 서비스 테이블의 값을 조작하는 API 후킹의 한 방법이다. 유저모드 API 후킹의 경우 프로세스간 컨텍스트가 다르기 때문에 시스템 전역 후킹을 하기 위해서는 일일이 모든 프로세스에 관련 코드를 주입(injection) 시켜야 한다는 불편한 점이 있었다. 이러한 문제를 해결한 한 가지 방법이 SDT 후킹이다. SDT 후킹은 커널모드에서 한 번만 후킹을 하면 모든 프로세스에 적용되기 때문에 별도로 컨텍스트 관리를 할 필요가 없기 때문이다.

SDT 후킹의 방법을 이해하기 위해서는 우선 윈도우의 API 호출이 어떻게 커널로 전달되는지를 알아야 한다. 간단하게 windbg를 통해서 TerminateProcess 호출이 커널로 전달되는 과정을 살펴보도록 하자. 응용 프로그램에서 호출한 Terminate Process는 ntdll.dll의 NtTerminateProcess로 이어진다. <리스트 1>에 windbg를 통해서 디스어셈블한 NtTerminateProcess 함수가 나와있다. eax에 0x101를 넣고, 0x7ffe03000에 저장된 번지를 호출하는 것을 알 수 있다.

<리스트 2>에 나와있는 것처럼 0x7ffe0300는 edx에 현재 스택 포인터를 저장하고 sysenter 명령어를 호출하는 코드임을 알 수 있다. sysenter 명령어는 유저모드에서 커널모드로 진입하기 위한 명령어로 MSRs에서 실행할 커널 함수 주소를 가져와서 그 번지로 이동시키는 역할을 한다. rdmsr 명령어를 통해서 sysenter 명령어가 수행됐을 때 실행되는 함수를 찾아보면 KiFastCall Entry라는 것을 알 수 있다. KiFastCallEntry는 eax에 저장된 서비스 번호에 해당하는 함수를 호출하는 역할을 한다.

여기까지 과정을 요약하면 TerminateProcess 호출은 ntdll. dll의NtTerminateProcess로 이어지고, NtTerminate Process는 eax에 TerminateProcess API의 서비스 함수 번호인 0x101을 넣고 커널모드에서 KiFastCallEntry를 호출한다. KiFast CallEntry는 SDT를 참조해서 eax에 저장된 서비스 번호에 해당하는 함수를 호출한다. 이제 실제로 SDT에 대해서 살펴보도록 하자. SDT는 총 네 개의 서비스 테이블로 구성된다. 각 테이블은 <리스트 3>에 나와있는 SDE 구조체로 이루어져 있다. SDE 테이블의 각 필드별 설명은 <표 1>에 나와 있다. SDT의 첫 번째 테이블은 윈도우의 네이티브(native) API 함수를 저장하고 있는 ntoskrnl 테이블이다. 두 번째 테이블은 GUI 함수들을 저장하고 있는 win32k 테이블이다. 세 번째, 네 번째 테이블은 추후 사용하기 위해서 예약된 테이블이다.

윈도우 커널에는 기본적으로 두 종류의 SDT가 포함되어 있다. KeServiceDescriptorTable은 일반적인 스레드에 사용되는 테이블로 ntoskrnl 테이블만 사용하고, 나머지 테이블은 모두 사용하지 않는다. KeServiceDescriptorTableShadow는 GUI 스레드에 사용되는 테이블로 ntoskrnl과 win32k 테이블을 사용한다. <그림 1>은 이러한 구조를 도식화한 것이다.

windbg를 통해서 실제 커널의 SDT를 살펴보도록 하자. <리스트 4>에 커널의 SDT 구조체를 덤프한 내용이 나와있다. 살펴보면 ntoskrnl 테이블의 base 주소는 0x84e46a8, counter는 0, limit은 0x11c, arg 필드 값은 0x80514eb8인 것을 알 수 있다. 함수 테이블을 덤프한 것을 보면 첫 번째 함수의 시작 주소는 0x80581302인 것을 알 수 있고, 인자 크기는 0x18 바이트란 것을 알 수 있다. 한 가지 주의해서 볼 사실은 GUI 함수 테이블과 GUI 인자 테이블은 커널 내부가 아닌 win32k.sys 내부에 존재한다는 것이다. <리스트 4>에서도 이 두 테이블의 주소만 확연히 틀린 것을 확인할 수 있다.

이제 SDT에서 TerminateProcess API의 서비스 함수를 찾아보자. <리스트 1>을 보면 eax에 0x101을 집어넣고 있으므로 TerminateProcess API의 서비스 함수 번호는 0x101이란 것을 알 수 있다. 함수 테이블은 4바이트 기준이고, 인자 테이블은 한 바이트 기준이란 점을 생각해서 해당 내용을 덤프 해보면 <리스트 5>와 같다. TerminateProcess의 서비스 함수 주소는 0x80586740이고, 인자는 0x08 바이트 크기인 것을 알 수 있다. 해당 주소를 디스어셈블하면 커널 내부의 NtTerminateProcess 함수가 출력된다.

이제 끝으로 SDT 후킹을 하는 방법을 생각해보자. SDT 후킹이라 함수 테이블의 번지를 바꾸는 작업을 하는 것을 말한다. <리스트 5>에서 0x101번에 해당하는 서비스 함수 주소는 0x80586740이다. 이 값을 우리가 만든 임의의 함수 주소로 변경한다면, 응용 프로그램에서 TerminateProcess를 호출할 때 원본 서비스 함수가 아닌 우리가 새롭게 작성한 함수가 호출된다.

SDT 복구

루트킷은 API 호출 결과를 조작하기 위해서, 보안 프로그램은 시스템 상황을 모니터링하기 위해서 SDT 후킹을 광범위하게 사용한다. 이런 경쟁 조건에서는 먼저 실행되어서 SDT를 후킹한 쪽에 우선권이 주어진다. 예를 들어 루트킷이 NtQuerySystem Information 함수를 후킹한 상태를 생각해보자. 이후 실행되는 보안 프로그램에서 호출하는 NtQuerySystemInformation의 결과값은 루트킷에 의해서 조작된 값이기 때문에 신뢰할 수 없게 된다. 따라서 보안 프로그램은 자신이 동작하는 환경이 안전한지를 점검하는 것이 최우선 과제라 할 수 있다.

이러한 문제를 해결하기 위해서 나온 것이 SDT 복구 기술이다(http://www.security.org.sg/code/sdtrestore.html). 이 기술의 원리는 간단하다. 현재 시스템의 SDT와 파일로 존재하는 커널의 SDT를 비교해서 같은지 다른지를 비교하는 것이다. SDT 후킹을 하더라도 변경되는 것은 현재 메모리의 내용일 뿐 파일의 내용은 원본 그대로 이기 때문이다. 이 방법의 전체적인 실행 순서는 다음과 같다.

♦ 현재 로드된 커널의 이미지 이름과 로드 주소를 알아낸다.
♦ 로드된 커널의 KeServiceDescriptorTable의 base 주소를 알아낸다.
♦ 동일한 이름의 커널을 LoadLibraryEx를 통해서 메모리에 로드시킨다.
♦ 로드된 이미지에서 base와 동일한 오프셋에 있는 테이블 내용을 비교한다.

<리스트 6>은 이를 간단한 의사 코드로 만들어본 것이다. 핵심적인 부분은 함수 테이블 주소의 오프셋을 사용해서 Load LibraryEx로 로드한 커널에서 해당 부분을 찾아내는 것이다. 현재 로드된 커널의 이미지 이름과 베이스 주소는 NtQuery SystemInformation 함수를 사용해서 구할 수 있다.

SDT 재배치

참 아이러니 한 사실은 보안 제품에서 루트킷을 잡기 위해서 개발된 SDT 복구 기술이 역으로 루트킷에서 사용한다는 것이다. 보안 제품도 시스템 모니터링을 하기 위해서 SDT 후킹을 사용하는데 루트킷이 보안 제품을 무력화하기 위해서 SDT 복구를 사용하는 것이다. 이러한 루트킷에 대항하기 위해서 만들어진 기술이 SDT를 재배치하는 방법이다. 이 방법은 앞서 살펴본 SDT 복구가 로드된 커널의 KeServiceDescriptorTable[0].base에서 찾는다는 사실을 이용한다. base 필드의 값이 커널 이미지 외부에 있다면 로드한 커널 파일에서는 그 부분을 찾을 수 없다. 메모리를 할당한 다음 해당 영역으로 함수 테이블을 복사하는 것이 핵심이다. 그리고 커널의 base 필드 값을 할당된 메모리로 연결해주면 된다. 이렇게 변경된 경우의 테이블 구조가 <그림 2>에 나와 있다.

SDT를 찾는 또 다른 방법

SDT 재배치 방법이 나오고 얼마지 않아 루트킷 진영에서는 오리지널 커널 이미지 파일로부터 SDT의 주소를 알아내는 새로운 방법이 나왔다(http://www.rootkit.com/newsread.php? newsid=176). 이 방법은 커널의 초기화 코드에서 base 주소를 찾아내는 방법을 사용한다. KeServiceDescriptorTable은 커널의 KiInitSystem이란 함수에서 <리스트 7>과 갈은 형태로 초기화된다. base 주소에는 KiServiceTable이란 고정적인 값이 할당되기 때문에 이 부분을 통해서 base 주소의 기본 값을 찾아낼 수 있다.

이 방법의 전체적인 실행 순서는 다음과 같다. 글만 보고는 의미를 이해하기가 쉽지 않기 때문에 <그림 3>에 나와 있는 명령어 구조와 재배치 오프셋의 관계를 보면서 의미를 파악하도록 하자. <리스트 8>에는 재배치 정보에서 SDT 주소를 찾아내는 과정에 대한 의사코드가 나와있다.

♦ 현재 로드된 커널의 이미지 이름을 알아낸다.
♦ 동일한 이름의 커널을 LoadLibraryEx를 통해서 메모리에 로드한다.
♦ GetProcAddress를 통해서 KeServiceDescriptorTable 주소를 구한다.
♦ 모든 재배치 정보를 조사해서 재배치하는 곳의 참조 주소가 KeSer viceDescriptorTable인 것을 찾는다.
♦ 찾아낸 부분의 명령어 코드 형태가 mov [mem32], imm32 형태 인지를 조사한다.
♦ 5단계까지 일치했다면 재배치 주소+4에 있는 값이 KeService DescriptorTable[0].base의 초기 값이 된다.

전용 SDT

SDT를 둘러싼 이러한 싸움이 한창일 때 SDT 후킹에 대한 원론적인 의문을 품은 사람들이 있었다. 그 의문은 다름 아닌 SDT 후킹이 과연 시스템 전역 후킹인가에 대한 것이었다. 그들은 좀 더 세밀하게 커널을 분석했고, SDT 후킹이 시스템 전역이 아니라는 결론에 도달하기에 이르렀다(http://zap.pe.kr/index. php?page=pages/researches/winternals_kr.php). <리스트 9>에 나와있는 것처럼 커널 스레드 구조체에는 ServiceTable이란 필드가 있다. 이 필드의 역할은 해당 스레드의 서비스 테이블을 가리키는 역할을 한다. 앞서 살펴본 것과 같이 일반적인 경우에 모든 스레드는 <그림 4>에 나타난 것처럼 KeService Descri ptorTable이나 KeServiceDescriptorTableShadow 중에 하나의 서비스 테이블을 가리키기 때문에 SDT 후킹이 시스템 전역인 것처럼 보였던 것이다.

전용 SDT란 <그림 5>에 나타난 것처럼 특정 스레드의 Service Table을 수정해서 전혀 새로운 서비스 테이블과 연결시키는 방법이다. 앞서 살펴보았던 방법을 통해서 커널 파일에서 원본 SDT를 추출한 다음 그 정보를 기반으로 새로운 서비스 테이블을 만들면 SDT 후킹이 이루어진 상태라 하더라도 해당 스레드는 SDT 후킹으로부터 자유로울 수 있다.

인라인 후킹 vs 트램펄린

SDT를 새롭게 만들어서 스레드에 연결시키는 방법이 나오면서 더 이상 SDT 후킹은 의미가 없어졌다. 보안 개발자들은 좀 더 하위레벨의 코드를 직접 후킹하는 방법을 선택했다. 인라인(inline) 후킹으로 불리는 이 방법은 커널 함수 도입부를 후킹된 함수로 점프하는 코드로 패칭하는 것을 말한다. <리스트 10>과 같은 NtTerminateProcess 함수 본문을 <리스트 11>과 같이 바꾸는 것이다.

후킹에 관심이 있는 개발자라면 대부분 알고 있듯이 이러한 인라인 후킹은 트램펄린 함수를 사용해서 손쉽게 무력화 시킬 수 있다. 트램펄린 함수란 원본 함수와 도입부를 동일하게 구현하고 점프 코드 다음으로 바로 이동시키는 것을 말한다. <리스트 12>에는 이러한 트램펄린 함수의 한 예가 나와있다.

보안 개발자들의 다음 선택은 단순하게 트램펄린 함수에 무력화 되지 않도록 여러 함수를 동시에 인라인 후킹하는 방법이었다. 다중 인라인 후킹으로 불리는 이 방법은 후킹을 하고자 하는 함수에서 사용하는 다른 함수도 같이 인라인 후킹을 하는 것이다. NtTerminateProcess 함수는 내부적으로 PspTerminate ThreadByPointer, ObfDereferenceObject, ObClearProcess HandleTable와 같은 함수를 사용한다. 이들 함수를 동시에 후킹하면 앞서 살펴본 TL_NtTerminateProcess를 호출하더라도 TerminateProcess 함수 본문에서 호출하는 다름 함수에서 걸리기 때문에 단순 트램펄린 함수로는 우회하기가 쉽지 않다. <그림 6>은 해커가 이러한 상황을 공격하기 위해서 각각에 대한 트램펄린 함수를 만들어둔 화면을 보여주고 있다. <그림 6>에 나와 있듯이 이렇게 각각의 트램펄린을 만든다고 하더라도 CALL로 연결된 2번 선 때문에 인라인 후킹을 무력화 할 수 없다.

물론 그렇다고 이러한 다중 인라인 후킹을 무력화할 수 있는 방법이 전혀 없는 것은 아니다. 트램펄린 대신 함수를 통째로 복사해서 사용한다면 다중 인라인 후킹도 무력화할 수 있다. <그림 7>은 이런 방식으로 함수를 통째로 복사해서 다중 인라인 후킹을 무력화하는 방법이 나와있다. NtTerminateProcess를 원본 운영체제 코드를 복사해서 그대로 만들되 CALL 하는 부분만 Psp TerminateThreadByPointer가 아닌 TrampolinePsp Termina teThreadByPointer를 호출하도록 만드는 것이다. 인라인 후킹된 함수가 많다면 굉장히 번거로운 작업이 되겠지만 결국은 시간과 노력만 투자한다면 어떤 종류의 인라인 후킹이든 우회할 수는 있다.

물론 복잡하게 트램펄린을 만들지 않고 JMP 부분만 원본 코드로 덮어 쓴다면 쉽게 후킹을 우회할 수 있다. 하지만 이렇게 값을 덮어 쓰는 방법은 SDT 후킹과 마찬가지로 경쟁 조건을 유발 시킨다. 서로 JMP와 원본 코드를 끊임없이 덮어쓴다면 그 결과는 예측할 수 없다. 또한 일부 보안 프로그램에서는 자신의 후킹 코드가 손상되는 경우에는 시스템을 중단시키기도 하기 때문에 값을 덮어쓰는 방법은 신중하게 결정해야 한다.

후킹을 넘어서

점프 코드 지뢰밭을 일일이 제거하면서 나가는 것도 재미는 있지만 속도는 더디게 마련이다. 지뢰를 굳이 제거하지 않고 그 위를 날아간다면 그 또한 좋은 방법일 것이다. 앞서 우리가 원본 SDT를 커널 파일에서 찾은 것처럼 커널 파일에 있는 원본 코드를 사용한다면 지뢰밭을 뚫지 않고 날아가는 것도 가능하다.

<그림 8>에는 이러한 아이디어가 나와있다. 커널 파일 자체도 PE 포맷이라는 점과 재배치 섹션이 존재한다는 점을 생각한다면 커널을 로딩하는 것이 불가능한 일은 아니라는 것을 알 수 있다. 커널 모드 코드에서 메모리를 할당하고 그곳에 커널을 올린다음 재배치를 수행하면 <그림 8>의 왼쪽과 같은 구조가 된다. 커널 코드가 두 벌이라는 것과 함께 각각의 커널은 자신의 자료 구조를 가질 것이다. 하지만 이렇게 되서는 곤란하다. 왜냐하면 자료 구조는 전부 기존 커널의 것을 이용해야 하기 때문이다. 약간의 트릭을 써서 커널을 할당된 메모리에 올린 다음 재배치를 기존 커널의 주소에 로딩된 것처럼 수행한다면 <그림 8> 오른쪽 그림과 같은 구조가 된다. 커널의 코드는 두 벌이 되지만 참조하는 자료 구조는 하나가 된다. 이 상황에서 새로 로딩한 커널의 NtTer minateProcess를 호출한다면 트램펄린 함수를 하나도 만들지 않고 모든 인라인 후킹을 우회할 수 있다.

'Computer Science' 카테고리의 다른 글

PHP 동영상 강좌  (0) 2008.06.29
루트킷_4  (0) 2008.06.25
루트킷_2  (0) 2008.06.23
luabind/C++ <;-> 루아 클래스간 상호 참조 및 호출 예제  (0) 2008.06.22
Using Lua with C++ &#8212; A short tutorial  (0) 2008.06.22

루트킷_2

“유저 레벨에선 결코 루트킷을 구현할 수 없다. 루트킷은 반드시 ring0 에서 제작해야만 루트킷이다” 라는 명제만이 정답은 아니다. 유저 레벨에서도 구현할 수 있는 루트킷 기법은 얼마든지 있으며 실제 바이러스들도 굳이 드라이버를 설치하지 않고도 자신을 은닉하고 모습을 많이 보여준다. 2부에서는 유저 레벨 루트킷의 종류와 검출 방법에 대해 알아본다.

Ring3에서 보여줄 수 있는 은닉의 예술

유저 레벨 루트킷

강병탁 window31@empal.com, www.window31.com|바이너리 취약점 분석 업무를 하고 있다. 안티 크래킹/안티 디버깅 엔진 개발을 다년간 해왔으며 시스템 프로그래밍과 리버스 엔지니어링에 관심이 많다. 악성 코드나 해킹툴이 내부에 담고 있는 천재적인 알고리즘에 감탄하며 오늘도 IDA를 돌린다.

루트킷은 반드시 커널 레벨에서만 가동돼야 한다는 고정관념이 있다. 유저 레벨에서 아무리 숨겨 보았자 커널에서 쉽게 발견할 수 있다는 점에서 기능상의 한계를 많이 지적하는 편이다. 하지만 루트킷을 단순히 ‘최고의 은닉’ 기술에만 국한시켜서는 안 된다. 은닉이라는 기본 테마가 ‘무엇으로부터의 은닉’ 이라는 구체적 명제에 목매이지 않는다면, 유저 레벨 루트킷도 훌륭한 투명인간이 될 수 있다. 예를 들어 유저 레벨의 루트킷은 IceSword 같은 대부분의 루트킷 디텍터에 쉽게 발견되는 편이다. 하지만 그 같은 커널 드라이버를 이용하는 루트킷 디텍터를 사용할 수 없는 경우, 그리고 루트킷을 사용할 목적지가 드라이버와 무관한 장소일 경우라면 유저 레벨 루트킷도 충분한 가치를 발산할 수 있다. 더욱이 루트킷 하면 드라이버라고 생각하는데, 유저 레벨 루트킷은 드라이버를 설치하지 않고 숨겼다는 점에서 어떤 면에서는 더욱 훌륭하다고 볼 수 있다.

그리고 유저 레벨 루트킷은 “절대로 발견할 수 없도록 숨긴다”라는 목적보다는 그 기술이나 구현론 자체에 더욱 중점을 둬야 한다고 본다. 유저 레벨 루트킷을 구현하려면 API Hooking이나 각종 시스템 지식들이 다양하게 요구된다. “절대 발견할 수 없도록 숨긴다”에 너무 치중하지만 않는다면, 유저 레벨 루트킷을 연구하면서 얻을 수 있는 시스템 개발적 지식은 아주 많으리라 생각된다. 또한 유저 레벨의 루트킷은 굳이 루트킷 구현이 아닌 다른 여러 곳에서도 이용될 수 있는 알고리즘도 있기 때문에 얻을 수 있는 것은 더욱 많다고 생각된다. 따라서 루트킷을 커널에서만 구현해야 한다는 관념에 너무 얽매일 필요는 없으며, 유저 레벨에서 구현할 수 있는 루트킷은 어떤 것이 있고, 구조는 어떠한지 살펴볼 필요가 있다.

API Hooking

유저 레벨 루트킷으로써의 가장 기본임과 동시에 모든 부분이 되기도 하는 기법이 바로 API Hooking이다. Ring3에서는 시스템에 전역적인 영향을 끼치는 커널 구조체를 컨트롤 할 수 없으므로 은닉을 위해서는 기본적으로 후킹 기법을 필수적으로 사용한다. 그리고 타겟이 되는 API는 사용자가 공격자의 코드나 바이너리를 발견하지 못하도록 모듈을 찾는 관련 함수가 직접적인 대상이 된다. 예를 들면 파일을 찾을 때 FindNext File() 이나 프로세스를 찾을 때의 Process32Next() 같은 API가 대표적인 경우이다. 따라서 유저 레벨 루트킷은 이와 같은 API를 후킹하는 작업을 필수적으로 진행하므로 먼저 API Hooking에 대해서 살펴볼 필요가 있다. API Hooking에는 기본적으로 두 가지 기법으로 나누어진다. 첫째는 IAT Hook, 둘째는 Inline Hook(Overwrite Hook)이다. 여기서 사실 IAT Hook은 루트킷을 구현하는데 있어서 실전에서는 그다지 많이 쓰이지 않는 편이다. 왜냐하면 요즘 필드에 릴리즈되는 바이너리들은 암호화를 위해 대부분 프로텍팅·패킹이 돼서 배포되고 있는데, 프로텍터나 패커들은 API Call의 정보를 숨기거나 다른 보안상의 이유로 IAT를 망가뜨리거나 여러 갈래 꼬아놓은 자체 IAT를 사용하고 있다. 그래서 이 경우는 IAT가 잘 구해지지 확인않기 때문에 해당 테이블을 후킹 할 수 없고, 당연히 내가 원하는 은닉 기술을 활성화 할 수 없다. 따라서 IAT와는 무관하게 궁극적으로 DLL의 엔트리 포인트로 이동하는 부분을 노린, Inline Hook 방법을 많이 사용한다.

API Hooking을 위한 사전 준비 지식

유저 레벨에서 API Hooking을 위해서는 반드시 해당 프로세스에 DLL을 주입해야 한다(물론 DLL없이 빈 번지를 할당하여 코드를 삽입하고 리모트 스레드를 돌리는 방법도 있지만, 그 방법 역시 지금부터 설명할 세 가지 방법 중, 3번에 해당하는 것이기 때문에 별도 설명은 생략한다). DLL을 주입하는 방법에는 세 가지 방법이 있다. 첫째는 메시지 후킹, 둘째는 AppInit_DLLs 이용, 셋째는 CreateRemoteThread 이다. 이중 첫째는 윈도우가 없는 프로세스는 DLL을 집어넣을 수 없고, 둘째의 AppInit_ DLLs은 다른 프로그램이 사용하는 경우도 있으니 루트킷 구현이 목적인 API Hooking 방법으로는 적절한 방법이 아니다. 따라서 모든 프로세스의 DLL을 집어넣기 위한 방법으로는 셋째인 CreateRemoteThread가 가장 적합하다. 이 기법은 대부분 OpenProcess -> VirtualAllocEx -> WriteProcess Memory -> CreateRemoteThread 의 패턴을 사용한다. API Hooking에 이용되는 가장 표준적인 방법이기도 하다.

다음은 어느 API를 후킹해야 루트킷이 되는지 기능별로 살펴볼 필요가 있다. 목적은 자신을 발견할 수 없게 하는 법이 우선이다. 따라서 모듈이나 프로그램을 발견하기 위해서는 파일을 찾거나 메모리를 뒤지는 방법을 생각할 수 있는데 루트킷은 그것을 저지해야 하는 기능이 반드시 포함돼야 한다. 즉 모듈을 찾을 때에는 프로세스 검색, 파일 검색 더불어 서비스 리스트 검색까지 세 가지 방법이 있으며 루트킷은 이 세 가지 검색 방법을 모두 무력화 시켜야 한다.

프로세스 숨김

프로세스 리스트에서 공격자가 만든 바이너리가 보이지 않아야 하는 것이 첫 번째 과제이다. 프로세스를 나열하는 방법은 대표적으로 툴헬프32와 PSAPI가 있다. 따라서 루트킷은 유저 레벨에서 프로세스 리스트를 링크에서 제외시키기 위하여, Pro cess32Next()와 OpenProcess()를 후킹해야 한다. Open Process()만 후킹하면, 프로세스의 핸들만 얻을 수 없다 뿐이지, 툴헬프로는 PID와 프로세스 이름까지는 구해지기 때문에 유저 레벨에선 반드시 Process32Next()까지 처리해야 한다. 자신의 핸들이나 PID를 구해 놓고, OpenProcess()나 Process32Next() 후킹 함수에서 자신을 찾으려 할 때, 자신의 정보를 숨기는 방법이다. 만약 DLL을 가진 경우는 Module32Next()도 같은 방법으로 처리해 준다.

서비스 숨김

바이러스나 백도어를 찾을 때 서비스에 등록됐는지 살펴보는 경우가 많다. 그 때 루트킷이 서비스에 등록이 되어 있는 모습이 쉽게 발견되면 안 되므로, 서비스 리스트를 뽑을 때 그 목록에서 제거할 필요가 있다. 서비스 리스트는 EnumServices StatusEx 이라는 API를 사용한다. 루트킷은 이 API를 후킹하여 자신의 링크를 끊는다.

파일 숨김

탐색기나 내 컴퓨터 등에서 파일 리스트가 보이지 않아야 한다. 원리는 프로세스 리스트 때와 비슷하며 타겟이 되는 API는 FindNextFile 이다. 유저 레벨 루트킷은 이 API도 놓치면 안 된다. 이렇게 유저 레벨 루트킷을 구현하기 위한 세 가지 처리 부분을 살펴보았다. 프로세스, 서비스, 파일 리스트에서 자신을 숨긴다면, 별다른 루트킷 디텍터의 도움을 받지 않는 이상 해당 모듈을 찾기는 결코 쉽지 않다. 커널 드라이버를 설치할 필요도 없고, EXE와 DLL만으로 루트킷을 구현할 수 있다. 이것이 유저 레벨의 루트킷이다.

유저 레벨에서 루트킷 검출 방법

그렇다면 이번엔 유저 레벨에서 루트킷을 찾는 방법에 대해 알아보자. 물론 유저 레벨에서의 검출 방법은 커널 레벨에 비해 그 활용성이 지극히 제한적이고, 기법도 다양하지 않은 편이다. 그러나 방법이 전혀 쓸모없는 것도 아니다. 또한 이런 방법이 반드시 루트킷을 찾을 때만 사용되는 기술은 아니라는 점에서 다른 여러 시스템 프로그래밍에 활용할 수 있는 부분도 있다. 그렇다면 유저 레벨에서의 검출 방법에는 어떤 것이 있는지 살펴보자.

csrss.exe 의 스레드 리스트 이용

윈도우 시스템 프로세스 중 하나인 csrss.exe를 활용하면 유저 레벨에서도 루트킷을 감지할 수 있다. csrss.exe는 Client Server Run-time SubSystem의 약자로 윈도우 시스템을 가동시키는 핵심 프로세스이다. 이 프로세스는 윈도우에서 실행되는 모든 프로세스의 오브젝트를 관장하고 있다. 따라서 csrss.exe의 스레드 리스트를 구하면 루트킷 프로세스까지 검출이 가능하다.

후킹 원복화

후킹 되어 있는 API를 직접 원복화 시켜 버리는 방법이다. 물론 툴로 제공되는 경우도 있고 직접 VirtualProtect()와 Write ProcessMemory()를 이용하여 구현할 수도 있다. 지금은 디버거를 직접 붙여서 강제로 원복화를 시켜 보도록 하겠다. <화면 4>를 보자. 현재 FindNextFile이 h.dll 로 후킹 된 상태이다.

이 상태에서 이 코드를 원복화 시켜버리자. FindNextFile의 엔트리 코드는 mov edi, edi 로 시작하며 그 이후의 코드까지 포함하여 후킹 된 5바이트를 OpCode로 표현하면 0x8B, 0xFF, 0x55, 0x8B, 0xEC이다. 이 코드로 엔트리를 덮어써 버리자. <화면 5>가 그 내용이다.

그 후킹된 코드는 사라지고 오리지날 상태의 엔트리 포인트가 된다(<화면 6> 참조). 이제 FindNextFile의 후킹은 제거했다. 따라서 무결 상태의 FindNextFile을 사용할 수 있을 것이며 루트킷의 DLL을 거치치 않게 되므로 파일 검색이 가능하다. 이런 식으로 API Hooking을 원복화 시켜서 무력화 할 수 있다.

PID 연속 대입 방법

아무리 루트킷이 프로세스를 숨겨도 PID는 당연히 있기 마련이다. 그리고 PID는 0xC번부터 0xFFFF번까지 존재한다(4번 PID는 시스템이 사용한다). 따라서 PID를 4씩 늘려가며 연속해서 프로세스의 핸들을 오픈해 본다면, 현재 프로세스 리스트엔 없지만 PID가 있는 경우가 나타날 수 있다. 이 경우는 100% 루트킷의 프로세스이다. PID Brute Force 라는 기법이며, 단점은 루트킷이 프로세스의 핸들조차 얻지 못하도록 처리해 놓았을 때는 이 방법으로 검출이 불가능하다는 점이다.

유저 레벨 루트킷은 커널 하층부에 또 하나의 시스템이 더 들어올 수 있다는 약점이 있으므로 한계가 있고, 또 모든 프로세스를 통제해야 한다는 점에서 커널 루트킷보다 오히려 더 까다로울 수도 있다. 하지만 유저 레벨 루트킷은 반드시 루트킷 구현의 목적이 아닌 시스템 프로그래밍의 진수를 보여주는 여러 가지 기법을 사용한다. DLL Injection과 API Hooking, 그리고 각종 오브젝트의 활용적인 부분까지 유저 레벨 쪽의 Windows Internals에 대한 지식을 많이 함축하고 있다. 따라서 유저 레벨의 루트킷은 그 기능의 강력함의 여부를 떠나 기술 그 자체, 구현 알고리즘 자체에 우선순위를 두어 평가하는 것도 좋지 않을까 생각한다.

참고자료

1. Rootkit - http://www.rootkit.com

2. Anti-Rootkit - http://antirootkit.com/

3. Rootkits : Subverting the Windows Kernel - by Greg Hoglund, Jamie Butler

'Computer Science' 카테고리의 다른 글

루트킷_4  (0) 2008.06.25
루트킷_3  (0) 2008.06.24
luabind/C++ <;-> 루아 클래스간 상호 참조 및 호출 예제  (0) 2008.06.22
Using Lua with C++ &#8212; A short tutorial  (0) 2008.06.22
루트킷_1  (0) 2008.06.22

'Computer Science' 카테고리의 다른 글

루트킷_3  (0) 2008.06.24
루트킷_2  (0) 2008.06.23
Using Lua with C++ &#8212; A short tutorial  (0) 2008.06.22
루트킷_1  (0) 2008.06.22
웹 브라우저 밖에서 만나는 웹 애플리케이션  (0) 2008.06.10

Using Lua with C++ — A short tutorial

Written by Christian Stigen Larsen

Using Lua is easy! In this short tutorial we'll show how to write a fully working host-program in C++ with Lua callbacks.

Since the static Lua libraries are written in C, you must import them as such:

extern "C" {#include "lua.h"}int main(){  lua_State *L = lua_open();  lua_close(L);  return 0;}

Update 2007-05-14: It has been reported that on some systems you need to include the headers lualib.h and lauxlib.h to compile the above example:

extern "C" {#include "lualib.h"#include "lauxlib.h"}
Compiling and linking with GNU g++:
g++ host.cpp -o host -Ilua-5.0.2/include/ -Llua-5.0.2/lib/ -llua

Including lualib.h and lauxlib.h makes it easy to write a fully working host:

#include <iostream>extern "C" {#include "lua.h"#include "lualib.h"#include "lauxlib.h"}void report_errors(lua_State *L, int status){  if ( status!=0 ) {    std::cerr << "-- " << lua_tostring(L, -1) << std::endl;    lua_pop(L, 1); // remove error message  }}int main(int argc, char** argv){  for ( int n=1; n<argc; ++n ) {    const char* file = argv[n];    lua_State *L = lua_open();    luaopen_io(L); // provides io.*    luaopen_base(L);    luaopen_table(L);    luaopen_string(L);    luaopen_math(L);    luaopen_loadlib(L);    std::cerr << "-- Loading file: " << file << std::endl;    int s = luaL_loadfile(L, file);    if ( s==0 ) {      // execute Lua program      s = lua_pcall(L, 0, LUA_MULTRET, 0);    }    report_errors(L, s);    lua_close(L);    std::cerr << std::endl;  }  return 0;}

Compilation and linking:

g++ host.cpp -o host -Ilua-5.0.2/include/ -Llua-5.0.2/lib/ -llua -llualib

Running Lua programs

Let's test this with some Lua programs. The files here are from the distribution, hello.lua is simply:

-- the first program in every languageio.write("Hello world, from ",_VERSION,"!\n")

Executing a couple of Lua programs with our host program produces:

[csl@eris:~/dev/lua/lua-5.0.2]$ ./host test/hello.lua test/printf.lua-- Loading file: test/hello.luaHello world, from Lua 5.0.2!-- Loading file: test/printf.luaHello csl from Lua 5.0.2 on Wed Mar  2 13:13:05 2005

Calling C functions from Lua

It gets very interesting when Lua programs call your own functions. In the following program, we define a function my_function() and register it with the Lua environment using lua_register(). Our function prints its arguments as strings and returns the integer value of 123.

#include <iostream>extern "C"{#include "lua.h"#include "lualib.h"#include "lauxlib.h"}int my_function(lua_State *L){  int argc = lua_gettop(L);  std::cerr << "-- my_function() called with " << argc    << " arguments:" << std::endl;  for ( int n=1; n<=argc; ++n ) {    std::cerr << "-- argument " << n << ": "      << lua_tostring(L, n) << std::endl;  }  lua_pushnumber(L, 123); // return value  return 1; // number of return values}void report_errors(lua_State *L, int status){  if ( status!=0 ) {    std::cerr << "-- " << lua_tostring(L, -1) << std::endl;    lua_pop(L, 1); // remove error message  }}int main(int argc, char** argv){  for ( int n=1; n<argc; ++n ) {    const char* file = argv[n];    lua_State *L = lua_open();    luaopen_io(L); // provides io.*    luaopen_base(L);    luaopen_table(L);    luaopen_string(L);    luaopen_math(L);    luaopen_loadlib(L);    // make my_function() available to Lua programs    lua_register(L, "my_function", my_function);    std::cerr << "-- Loading file: " << file << std::endl;    int s = luaL_loadfile(L, file);    if ( s==0 ) {      // execute Lua program      s = lua_pcall(L, 0, LUA_MULTRET, 0);    }    report_errors(L, s);    lua_close(L);    std::cerr << std::endl;  }  return 0;}

+ Recent posts