'Windows'에 해당되는 글 5건

  1. 2011.03.17 ACL을 이용한 적절한 사용 권한 설정
  2. 2008.10.02 Handle leak 찾기 (3)
  3. 2007.11.03 DLL에서 EXE의 함수 호출하기 (1)
  4. 2007.07.23 커널 오브젝트 - 핸들
  5. 2006.12.11 Writing a Windows Service Program (8)

1. ACL이 중요한 이유는?

ACL(Access Control List)은  개개의 사용자들이 디렉토리나 파일과 같은 특정 시스템 개체에 접근할 수 있는 권한을 설정해 놓은 표이다.

  • 공격자(해커)가 리소스에 접근 할 수 있다면 게임 끝이다.
    • “만약 레지스트리키의 ACL이 Everyone에게 모든 권한 허용으로 설정 되어 있으면, 누구나 해당 레지스트리의 키 데이터를 읽고, 쓰고 심지어는 다른 사람의 접근을 차단 할 수 있다
  • ACL을 이용해 리소스에 대한 접근 권한을 설정 해 두지 않으면 예측하지 못한 다양한 방법 공격이 가능하다.
    ex) 64byte 로 지정된 버퍼에 더 많은 데이터를 써버려 오버런 발생
         대량의 데이터를 읽고 쓰게 강제
2. ACL의 구성
ACE(Access Control Entry) 란?
ACL의 구성 요소. ACL은 0개 혹은 이상의 ACE들을 가질 수 있고, 각 ACE들은 특정 자원에 대한 접근을 제어하거나 모니터링 할 수 있다.

  • Windows NT, 2000, XP와 이후 버젼에서 지원
  • 95, 98, Me, CE는 ACL을 지원하지 않음
  • NT와 이후 버젼 OS에서는 DACL, SACL 두 가지 지원
    • DACL(Discretionary Access Control List) - 접근 권한 결정
    • SACL(System Access Control List) - 감사 정책 결정
    • 둘은 비슷해 보이나 DACL은 권한을 설정하는 역할. SACL은 파일이나 폴더 혹은 주변 기기 오브젝트에 접근하는 이벤트 발생시 기록(log)을 할 수있음
  • DACL로 접근 권한을 제어 하거나, SACL로 감사 할 수 있는 보안 리소스의 예
    http://technet.microsoft.com/ko-kr/library/bb795082.aspx 참고
  • DACL
    • 각 DACL들은 0개 혹은 그 이상의 ACE(Access Control Entry)들로 구성 됨.
    • ACE는 SID(Security ID)와 SID가 리소스에 대해 가능한 작업들의 설명.
      두 가지 주요 요소로 구성 된다(그 외에도 여러 요소들이 있다).
    • SID - 사용자, 그룹, 컴퓨터를 나타내는 아이디
    • 거부 ACE - 특정 접근 권한을 거부하는 ACE
      ex) Everyone에 대해 모든 권한을 거부(owner 빼고 심지어 관리자 조차도 접근 불가)
      ACL은 항상 거부 ACE로 시작한다. 그렇지 않으면 허용 접근 권한이 지정되지 않는다.
3. 적적한 ACE 선택 방법?

ACL 내의 모든 ACE는 반드시 의미가 있다 - 의미를 모르겠다면 제거한다.


  1. 사용할 리소스가 무엇인지 결정
  2. 비즈니스에서 정의된 접근 요구 사항을 결정
  3. 적합한 접근 제어 기술을 선택한다.
  4. 접근 요구사항을 접근 제어 기술로 변환한다.

“주체(subject)는 대상 개체(Object)에게 어떤 행동을 수행할 수 있다”



4. ACL 생성

  • Window NT 4에서 ACL 생성
    • EXPLICIT_ACCESS
    • AllocateAndInitializeSid()
    • InitailizeSecurityDescripter()
    • SetSecurityDescriptorDacl(),
      SetSecurityDescriptorOwner(),
      SetSecurityDescriptorGroup(),
      SetSecurityDescriptorSacl()
  • Windows 2000에서 ACL 생성
    • SDDL(Security Descriptor Definition Language) 사용

5. ACE 권한 지정 순서
거부 ACE가 다른 ACE 보다 먼저 지정되어야 한다(이렇게 하지 않으면 허용 접근 권한이 지정되지 않는다)

  1. 명시적 거부
  2. 명시적 허용
  3. 부모 컨테이너에서 상속된 거부
  4. 부모 컨테이너에서 상속된 허용....
6. NULL DACL과 그 밖의 위험한 ACE

NULL DACL == 방어하지 않는다


  • Everyone(WRITE_DAC)
    오브젝트의 DACL을 변경할 수 있는 권한. 공격자가 SD에 기술된 ACL 변경으로 자신만 해당 오브젝트에 접근하고 다른 사람은 거부하도록 만들 수 있다.
  • Everyone(WRITE_ONWER)
    오브젝트의 소유자를 변경할 수 있는 권한. 공격자가 SD에 기술된 오브젝트 소유자를 변경해 버릴 수 있다.
  • Everyone(FILE_ADD_FILE)
    공격자가 새로운 실행 파일을 파일 시스템에 추가 할 수 있다.
  • Everyone(DELETE)
    누구나 오브젝트를 삭제 가능하다
  • Everyone(FILE_DELETE_CHILD)
    공격자가 디렉토리에 FILE_DELETE_CHILD 권한을 가지면, 차일드 오브젝트에 대한 권한관계 없이 삭제 가능하다.
  • Everyon(GENERIC_ALL)
    NULL DACL과 같다.

What Are SD(Security Descriptors) and Access Control Lists?
...security descriptors to help control access to the objects. Security descriptors include information about who owns an object, who can access it and in what way, and what types of access are audited. Security descriptors, in turn, contain the access control list (ACL) of an object,...
http://technet.microsoft.com/ko-kr/library/cc783702(WS.10).aspx
7. 운영체제에 포함되지 않는 기타 접근 권한 메커니즘
  • Net 프레임 워크 역할(role), COM+ 역할
    ※ 역할이란 정책을 강제하기 위해 애플리케이션에서 사용.
       예를 들면 그룹을 지정하고 해당 그룹에 허용되는 트랜젝션들을 정의(애플리케이션 종속적)
  • IP제한
  • SQL 서버의 트리거와 SQL 서버의 사용 권한
    업데이트시 그룹을 검사하여 적절한 권한의 그룹이 아닌 경우 롤백을 구동하는 트리거
Posted by kukuta

댓글을 달아 주세요

이 글의 대상은 윈도우 프로그래밍에 대해서 어느 정도 이해를 하고 있는 사람들을 대상으로 합니다. 기본적으로 핸들과 커널 오브젝트에 대한 개념에 대해 알고 있어야 하며(모른다면 여기로), 디버깅에 대해서 약간의 기본적 지식을 가지고 계시는 것이 좋습니다.

본 포스트는 디버깅에 관련된 툴들의 사용법에 대해서 다루고 있으며, 자세한 사용법 보다는 기본적인 사용법위주로 문제 해결에 관련된 부분만 다루고 있습니다. 보다 자세한 정보를 원하시면 해당 툴에 관련된 링크를 따라 가시면 많은 도움이 될 것입니다.


프로그래밍을 하다 보면 원하든 원하지 않든(거의 이 경우가 대부분이만) 종종 자원을 흘리고 다니는 경우가 있다. 여기서 말하는 자원이란 것은 파일이든, 메모리든 핸들이든 여러가지가 될 수 있지만 오늘은 특히 '핸들(handle)'의 누수를 어떻게 찾아 낼 수 있는가에 대해서 알아 보도록 하겠다.

핸들의 누수를 찾기 위해서는 아래와 같은 준비물이 필요하다 :
 - 핸들을 흘리고 있을 것이라 짐작되는 실행 프로그램

 - 윈도우를 깔면 기본적으로 제공되는 작업관리자(Task mananger)
 - Process Explorer(http://technet.microsoft.com/en-us/sysinternals/bb896653.aspx)
 - Debugging Tools for Windows 패키지에 포함 되어 있는 Windbg
   (http://www.microsoft.com/whdc/DevTools/Debugging/default.mspx)
 ※ 링크가 깨어져 있을 경우는 구글에서 검색하시면 금방 찾을 수 있을 겁니다.
 ※
위의 준비물이 다 준비 되었다면 핸들 누수를 찾아 보도록 하자.

1. 이 어플리케이션에서 핸들이 새고 있을까요?
가장 먼저 해야 할 일은 어플리케이션이 핸들을 질질 흘리고 다니는지 아닌지 판단 해야한다. 판단하는 방법으로는 작업관리자를 이용하면 된다. 여기서 잠깐 작업관리자에 대한 사족을 달자면, 이 녀석은 프로세스에 대한 각종 유용한 정보를 알려 주는 녀석으로써, 앞으로도 디버깅 관련 포스팅에 약방에 감초 처럼 등장 할 녀석이다.

맨위의 매뉴에서 '보기>열선택'(영어로는 어떻게 쓰여져 있는지 모르겠다) 순서로 클릭해 들어가면 수많은 체크박스들이 보일 것이다. 그것들 중에 관심있는 박스에 체크를 하고 확인을 누르면 되겠다. 여기서는 핸들이 누수 되고 있는지 아닌지를 찾을 것이기에 '핸들 수'에 체크를 하자. 그럼 이제 부터 작업관리자의 프로세스 탭에서 '핸들'에 관련된 정보를 볼 수 있을 것이다.

핸들이 누수 되고 있을 거라고 생각 되는 프로세스의 핸들 숫자를 살펴 보자. 핸들 카운트가 늘어나고 있는가? 여기서 한가지 유의 할 점은 핸들 카운트가 늘어난다고 해서 무조건 누수라고 봐서는 안된다는 것이다. 단순히 사용하는 핸들이 많아서 핸들 카운트가 늘어 나는 것일 수도 있고, 캐싱이나 등을 위해서 일부러 핸들을 클로즈하지 않고 있는 것일 수 있다. 프로세스가 아무것도 하지 않고 아이들(idle) 상태에 있으면서도 핸들 카운트가 증가하고 있거나 감소하지 않는다면 의심해 볼만 하다.

2. 어떤 핸들이 새고 있을까요?
프로세스에서 핸들이 누수되고 있다고 확신을 가지고 있다면(위에서 어렵게 이야기 했지만, 핸들이 누수되고 있다는 사실을 판단하는 것은 직관적으로도 알 수 있는 쉬운 작업이다) 어떤 타입의 핸들이 누수되고 있는지 파악한다면 잘못을 바로 잡는데 상당히 시간을 절약 할 수 있을 것이다. 이럴 때 사용 할 수 있는 것이 바로 "Process Explorer"이라는 녀석이다.

이 녀석은 현재 선택된 프로세스에서 만들어 지고 있는 핸들의 타입에 대한 정보들을 일목요연하게 보여준다. 만일 이 녀석으로 프로세스를 감시 하고 있는데 줄어들지 않고 늘어 나기만 하는 핸들이 있다면 바로 그녀석이 누수의 원인. 어떤 타입의 핸들이 새고 있는지 알고, 핸들의 이름도 알 수 있으니 코드를 수정하는 것은 시간 문제다. 아래의 스크린샷은 파일 핸들을 지속적으로 만들어 내고 있는 프로세스를 모니터링 하고 있는 것이다 :

위에 보이는 HeapMemoryLeak라는 프로그램은 필자가 의도적으로 핸들 및 메모리등의 자원을 누수 하도록 만든 프로그램이다. 아래쪽 윈도우를 보면 똑같이 생긴 File 타입의 핸들들이 중복해서 만들어 지고 있는 것을 볼 수 있다.

"Process Explorer"를 사용하는 방법에 대해 좀 더 자세히 알고 싶다면, http://technet.microsoft.com/en-us/sysinternals/bb896653.aspx 를 참고 하자.

3. Windbg 사용하기(Make use of Resource leak detection tools)
지금까지 1번과 2번의 과정에서 프로세스의 핸들 누수 여부를 결정하는 일, 어떤 타입의 핸들이 누수 되는지 구분하는 방법에 대해 알아 보았다. 하지만 핸들 누수라는 것이 언제나 정기적으로 발생하는 것도 아니고, 복잡한 환경에서 구동되는 프로그램이라면 동일한 파라메터에 동일한 시간이 지났음에도 불구하고 누수되는 양이나 누수되는 곳이 다를 수도 있다. 이럴 때 유용하게 사용 할 수 있는 것이 Windbg!htrace다.

htrace는 Windbg의 확장 기능으로 핸들의 OPEN과 CLOSE에 대해서 함수 호출 스택, 핸들을 열거나 닫은 프로세스 아이디, 쓰레드 아이디등을 추적 할 수 있으며, 결론적으로는 열리기는 했으되 닫히지 않은 모든 핸들들만을 선별하여 위에서 언급한 정보들을 제공 할 수 있다. 열리기만 하고 닫히지 않은 핸들은 누수되고 있을 확률이 높고, 어디서 만들었는지에 대한 정보도 제공해 주니 이 어찌 좋지 아니한가.

htrace에 대해서 시작하기 전에 windbg의 사용법에 대해서 기본적인 것을 알아 보자 :
1. Windbg.exe를 실행 한다. 
커맨드로 실행 해도 좋고, 시작 메뉴에서 부터 실행 해도 좋다. 일단 실행 하자.

2. 디버깅할 프로그램에 attach 하거나 Windbg에서 executable 파일을 로드한다.
디버깅하기 위해서는 이미지를 로드 해야 한다. Windbg가 로드하는 방법으로는 두 가지가 있는데 구동중인 프로세스에 Windbg를 붙이는 방법과 Windbg에서 디버깅할 프로세스를 로드 하는 방법이 있다.
 
구동 중인 프로세스에 붙이기(Attach to a running process)
 프로세스가 실행 중이어야 한다.
 프로세스 아이디를 이용하여 붙일 수 있다.
 windbg.exe -p PID
 혹은 프로세스 이름을 이용하여 붙일 수도 있다. 하지만 동일한 이름의 프로세스가 2개 이상이면 안 된다.
 windbg.exe -pn ProcessName
 혹은 GUI 모드에서 File>Attatch 를 하면 된다.

Windbg에서 executable파일 로드하기(Spawning new process)

 Windbg.exe [-o] ProgramName
 혹은 GUI 모드에서 File>Open Executable 을 하면 된다.

간단하게 나마 Windbg의 실행 방법에 대해서 알아 보았다. 이제 windbg에서 핸들이 누수되고 있을 것이라고 의심되는 프로세스를 로드하는 것은 끝났다. 이제 어떻게 htrace를 이용 할 것인가?

CommandLine: E:\Project\HeapMemoryLeak\Debug\HeapMemoryLeak.exe
Symbol search path is: *** Invalid ***
****************************************************************************
* Symbol loading may be unreliable without a symbol search path.           *
* Use .symfix to have the debugger choose a symbol path.                   *
* After setting your symbol path, use .reload to refresh symbol locations. *
****************************************************************************
Executable search path is:
ModLoad: 00400000 0041b000   HeapMemoryLeak.exe
...
(1774.368): Break instruction exception - code 80000003 (first chance)
eax=00251eb4 ebx=7ffd6000 ecx=00000003 edx=00000008 esi=00251f48 edi=00251eb4
eip=7c931230 esp=0012fb20 ebp=0012fc94 iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000202
...
7c931230 cc              int     3

프로세스를 로드하면 제일 먼저 나오는 화면이다. 심볼 패스가 제대로 로드 안되었다는 에러메시지가 눈에 거슬리긴 하지만 무시하도록 하자. 가장 먼저 해야 할 일은 htrace를 활성화 시키는 것이다 :
0:001> !htrace -h
!htrace [handle [max_traces]]
!htrace -enable [max_traces]
!htrace -disable
!htrace -snapshot
!htrace -diff
0:001> !htrace -enable
Handle tracing enabled.
Handle tracing information snapshot successfully taken.

htrace가 활성화 되고나면 윈도우는 모든 핸들의 생성과 소멸에 관련된 호출에 대해서 기록하기 시작한다.
이제는 프로세스가 자원을 누수 할 수 있도록 프로세스를 구동 시키자(지금까지 프로세스는 블록킹 상태였다).

0:001> g

'g'를 누르게 되면 프로세스는 구동상태로 들어가게 되고, 자원을 누수하는데 필요한 자신의 일을 할 것이다. 자원 누수를 경험 할 수 있는 적정 시간이 경과 되고 난후 디버깅을 잠시 멈춰 주자. 맨위의 GUI 메뉴를 살펴 보면 '일시 정지' 버튼을 누르면 된다 :

(1774.254): Break instruction exception - code 80000003 (first chance)
eax=7ffd6000 ebx=00000001 ecx=00000002 edx=00000003 esi=00000004 edi=00000005
eip=7c931230 esp=003affcc ebp=003afff4 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=0038  gs=0000             efl=00000246
ntdll!DbgBreakPoint:
7c931230 cc              int     3

프로세스가 정상적으로 블록킹 상태로 빠졌다면 이제 !htrace 명령으로 그동안 생성되고 소멸된 핸들을 찾아내면 된다 :

0:001> !htrace
--------------------------------------
Handle = 0x00000690 - OPEN
Thread ID = 0x00000368, Process ID = 0x00001774
0x7c801a4f: kernel32!CreateFileA+0x0000002b
0x102c43f5: MSVCR90D!open+0x000008a5
0x102c3e97: MSVCR90D!open+0x00000347
0x102c4fe0: MSVCR90D!sopen_s+0x00000020
0x102552bd: MSVCR90D!flsbuf+0x00000ddd
0x102565f5: MSVCR90D!fsopen+0x000001d5
0x10256644: MSVCR90D!fopen+0x00000014
0x0041147d: HeapMemoryLeak!main+0x0000003d
0x00411ad8: HeapMemoryLeak!__tmainCRTStartup+0x000001a8
--------------------------------------
...
Parsed 0x4 stack traces.
Dumped 0x4 stack traces.

간단하게 하기 위해 많은 부분을 생략 했다. 위의 로그에 나와 있는 부분들은 너무 직관적이라 따로 설명하지 않도록 하겠다. 다만 Handle = <value> - OPEN 의 경우 핸들 생성 콜을 나타내고 반대로 CLOSE라는 것이 있다는 것을 알아 두자. 동일한 핸들 값에 OPEN만 있고 CLOSE가 없는 녀석들이 바로 우리가 찾는 녀석들이다. 여기서 분명히 불평하시는 분 있을 것이다. 저 많은 로그들 사이에서 어떻게 특정녀석들을 찾냐고... 그래서 htrace는 획기적은 툴을 하나 더 제공한다. 바로 -diff 옵션이다.

바로 이 -diff를 사용하면 OPEN만 있고 CLOSE가 없는 모든 녀석들을 찾아 낼 수가 있다 :
0:001> !htrace -diff
Handle tracing information snapshot successfully taken.
0x32 new stack traces since the previous snapshot.
Ignoring handles that were already closed...
Outstanding handles opened since the previous snapshot:
--------------------------------------
0:001> !htrace -diff
Handle tracing information snapshot successfully taken.
0x32 new stack traces since the previous snapshot.
Ignoring handles that were already closed...
Outstanding handles opened since the previous snapshot:
--------------------------------------
Handle = 0x00000690 - OPEN
Thread ID = 0x00000368, Process ID = 0x00001774
0x7c801a4f: kernel32!CreateFileA+0x0000002b
0x102c43f5: MSVCR90D!open+0x000008a5
0x102c3e97: MSVCR90D!open+0x00000347
0x102c4fe0: MSVCR90D!sopen_s+0x00000020
0x102552bd: MSVCR90D!flsbuf+0x00000ddd
0x102565f5: MSVCR90D!fsopen+0x000001d5
0x10256644: MSVCR90D!fopen+0x00000014
0x0041147d: HeapMemoryLeak!main+0x0000003d
0x00411ad8: HeapMemoryLeak!__tmainCRTStartup+0x000001a8
--------------------------------------
... 

0x00000690의 값을 가진 핸들이 여전히 close 되지 않고 버티고 있는 것을 볼 수 있다. 이 프로그램은 파일을 열고 닫지를 않으니 모든 핸들이 줄줄줄 새는 것이 당연하다. 그리고 콜 스택을 살펴보면 tmain 함수에서 호출하는 fopen을 따라 핸들을 생성하는 것을 알 수 있다.

4. 관련 문서



이상 핸들 누수를 어떻게 감지하는지 알아 보았습니다. 글을 적고 있는 필자도 그렇게 위의 툴들에 해박한 지식을 가지고 있는 것이 아니라 공부하고 있는 처지이기에 많이 부족한 글이라 생각합니다만 이 정도 만으로도 충분히 어느정도의 핸들 누수는 잡아 내는데 도움을 줄 수 있을 것이라 생각하며 이만 포스트를 접도록 하겠습니다..

'도구의발견' 카테고리의 다른 글

cvs diff 출력 형식 보기  (0) 2008.12.11
키보드 리매핑하기(레지스트리 변경)  (7) 2008.12.05
Handle leak 찾기  (3) 2008.10.02
cvs 파일 상태 기호  (2) 2008.08.19
GDB Tutorial  (0) 2008.08.19
Break point  (0) 2008.08.18
Posted by kukuta

댓글을 달아 주세요

  1. Favicon of https://daewonyoon.tistory.com BlogIcon daewonyoon 2008.11.11 15:17 신고  댓글주소  수정/삭제  댓글쓰기

    유용한 글 잘 읽었습니다.

  2. ㅎㅎ 2011.06.22 00:44  댓글주소  수정/삭제  댓글쓰기

    좋은 정보 감사합니다. 퍼갈게요 ^^

 일반적으로 EXE에서 로드된 DLL의 함수를 호출하는 것은 일반적인 일이다. 하지만 플러그인 프로그램이나 기타 다른 이유로 인해 DLL에서 EXE의 함수를 호출 해야하는 경우가 있다. 이 문서는 그런 경우 유용한 팁을 소개하고 있다. 이 문서는 윈도우에서 DLL을 만들고 로드 하는 방법을 알고 있다는 가정하에 작성 된 것이므로 DLL프로젝트의 생성이나 로드 방법에 대해서는 자세하게 다루지 않는다.

1. 함수를 exporting하는 exe 만들기
exe에서 함수를 export하는 것은 dll에서 하는 그것과 동일한 작업을 하면 된다.

extern "C"
{
  __declspec(dllexport) void ExportFunctionFromEXE(const std::string& callFrom)
  {
    std::cout << "Exe export function called From " << callFrom << std::endl;
  }
}

※ extern "C"
C++스타일 맹글링은 일반 문자열로 표현하기 힘들기 때문에 C스타일의 함수 이름을 노출 하도록 하기 위해 사용 한다. 만일 extern "C" 가 싫다면 .def파일에 노출 하고자 하는 함수의 이름을 적어 주면 된다. 첨부된 예제에서는 .def 파일을 사용하고 있다. 

2. 함수를 exporting 하는 DLL 만들기
예제로 소개되는 함수는 exe에의해 호출 되기도 하지만, 동시에 exe에서 export하고 있는 함수를 import 하기도 한다. 파란색으로 하이라이트 표시 되어 있는 GetModuleHandle이라는 함수에게 인자로 NULL을 전달하게 되면 자신이 속한 exe(or 프로세스)의 핸들을 리턴한다. 그 핸들을 이용해 dll에서 import하는 것과 동일하게 exe의 함수를 호출 할 수 있다.

__declspec(dllexport) void ExportFunctionFromDLL(const std::string& callFrom) {
    std::cout << "DLL export function called From " << callFrom << std::endl;
    HINSTANCE hExe = GetModuleHandle(NULL);
    FuncPtrT pFunc = (FuncPtrT)::GetProcAddress(hExe, "ExportFunctionFromEXE");
    if(pFunc)
    {
        (*pFunc)("DLL");
    }
    else
   {
        std::cerr << "Not found proper " << callFrom << " function" << std::endl;
    }
}

3. 결론

결론을 말하자면 함수의 인터페이스를 노출하는 것을 결정하는 것은 파일의 타입이 아니라 코드 내부의 선언이라는 것이다. 우리는 위의 예제에서 exe, dll에 관계없이 함수 인터페이스를 노출 하고 그것을 로드 하는 것을 보았다.

참고적으로 dll에서 exe의 함수를 load 할 때 꼭 같은 프로세스 여야만 한다는 제약도 없다. 위의 GetModuleHandle() 대신 LoadLibrary 함수를 사용하여 load 할 파일에 dll이 아니라 exe파일의 경로를 넘겨
 줘도 결과는 같다.
 
첨부 파일 :
(.Net 2003 프로젝트 )


Ref.
 Win32 실행 파일 구조 : http://quartzd.tistory.com/244
 원문 : http://www.codeguru.com/Cpp/W-P/dll/article.php/c3649

'진리는어디에' 카테고리의 다른 글

volatile - Multithreaded Programmer's Best Friend  (6) 2007.12.05
Decorator Pattern  (3) 2007.11.11
DLL에서 EXE의 함수 호출하기  (1) 2007.11.03
Flyweight Pattern  (2) 2007.10.24
Composite Pattern  (2) 2007.10.23
RST 메시지  (0) 2007.10.10
Posted by kukuta
TAG C/C++, DLL, Windows

댓글을 달아 주세요

  1. 2009.06.08 12:00  댓글주소  수정/삭제  댓글쓰기

    비밀댓글입니다

/**
 갈 수록 윈도우와 리눅스 사이에서 왔다 갔다 해야 할 일이 많아 지고 있다.
 오늘은 윈도우의 커널 오브젝트(Kernel Object)라는 것에 대해서 알아 보도록 하자.
*/


유저 모드와 커널 모드
 '커널 오브젝트'에 대해 알아보기 전에 '유저 모드'와 '커널 모드'의 실행이라는 것에 대해서 알아 보도록 하자. 프로그램을 실행하게 되면 사용자가 직접 접근할 수 있는 유저 영역과 시스템 콜등을 통해서만 접근할 수 있는 커널 영역이 있다. 주로 커널 영역에는 운영체제, 프로세스간에 공유되는 데이터들이 보존되어 함부로 메모리 억세스를 할 수 없도록 제한하고 굳이 그 자원을 사용하기 위해서는 필수적으로 커널에게 자원을 요청하여 그에 대해 구분 할 수 있는 무엇인가를 얻어와 커널의 허가 아래서 사용해야 한다. 그렇지 않다면 사용자가 운영체제가 상주하는 메모리 영역을 침범하여 마음대로 바꾸어 놓는다거나 하여 전체 시스템에 장애를 가져 올 수 있다.

커널 오브젝트와 오브젝트 핸들
 위에서 언급했듯이 커널 모드에서 사용되는 자원들을 구분 해 놓은 것을 커널 오브젝트라고 하고, 사용자가 커널에게 자원을 요청하고 오브젝트를 참조 할 수 있는 무엇인가를 할당 받은 것이 오브젝트 핸들이라고 한다. 커널 오브젝트에는 파일, 쓰레드, 프로세스, 뮤텍스, 세마포어, 이벤트 핸들..등의 다양한 종류가 있고, 사용자가 오브젝트의 핸들을 할 당 받는 방법은 다양하다.

 일반적으로 커널 오브젝트는 프로세스에서 생성하므로 프로세스가 끝나면 소멸 될 것으로 생각하는데, 커널 오브젝트의 소유자는 사용자 프로세스가 아니라 커널이다. 그러므로 사용자 프로세스가 죽더라도 커널 오브젝트는 남아 다른 프로세스에 의해 재사용 될 수 있다(이 말은 곧 프로세스가 끝나도 자원이 해제가 안되는 '자원 누수'라는 뜻이기도 하다).

커널 오브젝트의 상태
 윈도우의 커널 오브젝트는 signaled, non-signaled의 두 가지 상태를 가진다. 글자 그대로 신호를 받은 상태와 그렇지 못 한 상태를 나타낸다. 커널 오브젝트는 이 상태를 표시하기 위해 boolean 변수를 하나 가지고 있고, TRUE면 signaled 상태, FALSE이면 non-signaled 상태를 가리킨다. 일반적으로 커널 오브젝트가 생성 되면 non-signaled 상태가 되며(하지만 사용자가 직접 지정 해서 생성 할 수도 있다), 어떠한 상황이 발생하면 signaled가 된다. 어떠한 상황이라는 것은 각 커널 오브젝트마다 다르다. 예로 Thead와 Process의 커널 오브젝트(Handle)는 non-signaled 상태에 있다가 종료하는 경우에 signaled로 변경된다.

참고 : http://www.tipssoft.com/bulletin/board.php?bo_table=FAQ&wr_id=17&page

'진리는어디에' 카테고리의 다른 글

XML 기초  (9) 2007.10.04
정규 표현식(Regular Expressions)  (0) 2007.08.31
커널 오브젝트 - 핸들  (0) 2007.07.23
connected UDP socket  (0) 2007.05.14
C++ operator 사용하기  (3) 2007.04.22
Threads Scheduling  (1) 2007.04.19
Posted by kukuta

댓글을 달아 주세요

/**

얼마 전 리눅스의 데몬(Daemon)이라는 것에 대해 약간이나마 공부를 해야 할 일이 있었습니다. 그리고 그 과정에서 윈도우에서는 그와 비슷한 서비스라는 것이 있다는 것을 알았습니다. 이번 포스팅에서는 서비스 프로그램을 작성하는 방법에 대해 알아 보도록 하겠습니다. 원문은 코드구루(http://www.codeguru.com)의 'Writing a Service Program(Jonathan Ng)' 을 기본으로 하고 있습니다.

*/

서비스(services)라는 것은 일반적으로 시스템 부팅시점 부터 시작해 시스템이 끝나는 시점 까지 그 수명을 같이 하는 프로세스를 가리키기도 합니다. 예를 들어 윈도우 베이스 프로그램과 컴포넌트가 남기는 로그를 기록해주는 '로그 이벤트'같은 프로세스 같은 것이 있습니다. 리눅스(혹은 유닉스) 시스템에서 윈도우의 서비스와 비슷한 역할을 하는 것으로는 데몬(daemon) 프로세스가 있습니다.

서비스 에플릿은 Control Panel > Administrator tools > Service에서 찾아 볼 수 있으며, 새로운 서비스 설치는 이 애플릿과 Registry(HKEY_LOCAL_MACHINE\System\CurrentControlSet\Service)에 앤트리를 생성합니다. 이렇게 해서 시스템은 부트타임에 어떤 서비스들이 등록 되어야 하는지 알 수 있게 되는 것이지요.

모든 서비스들은 반드시 아래의 standard event에 응답하도록 디자인 되어야만 합니다. 아래의 이벤트들은 윈도우 서비스에서 버튼의 형태로 보여지게 됩니다 :

  • START : 서비스를 수동으로 시작하거나, 서비스가 중단 되었다면 서비스를 시작한다.
  • STOP : 서비스를 중단한다.
  • PAUSE : 서비스를 일시 중지한다.
  • CONTINUE : 중단되었던 서비스를 재 시작한다.

모든 서비스는 Service Control Manager 시스템에 의해 관리 됩니다. Service Control Manager는 레지스트리에 서비스 목록을 유지하며, 부트타임이나 아니면 따로 매뉴얼하게 스타트가 요구 될 경우 레지스트리에 등록되어 있는 서비스 프로그램을 실행하게 됩니다.

일반적으로 서비스 프로그램은 일반 exe 확장자 형태를 가지고 있지만 Service Sonctrol Manager(SCM)에 정상적으로 접속하기 위해서는 아래의 특정한 요구사항들을 만족 시켜야 합니다 :

  1. EXE 는 반드시 main 또는 WinMain 함수를 가지고 있어야하고, StartServiceCtrlDiespatcher 함수를 호출 해야 한다. StartServiceCtrlDiespatcher 함수는 EXE 파일을 SCM에 등록하고, SCM이 ServiceMain 함수에게 포인터를 넘겨 주는 역할을 한다.
    void main(int argc, char *argv[])
    {
        SERVICE_TABLE_ENTRY serviceTable[] =
        {
            { ServiceName, (LPSERVICE_MAIN_FUNCTION) ServiceMain},
            { NULL, NULL}
        };

        BOOL success;
        if(argc == 2)
           //..check arguments
        else
        {
            //register with SCM
            success = StartServiceCtrlDispatcher(serviceTable);
            if (!success)
                ErrorHandler("StartServiceCtrlDispatcher", GetLastError());
        }
    }
  2. SCM은 서비스 시작 요청이 들어 오면 ServiceMain 함수를 호출한다. ServiceMain 함수는 그 즉시 RegisterServiceCtrlHandler 함수를 호출하여 Handler 함수를 SCM에 등록한다.
    void ServiceMain(DWORD argc, LPTSTR *argv)
    {
        BOOL success;

        //레지스터 함수 호출
        serviceStatusHandle =
             RegisterServiceCtrlHandler(ServiceName, (LPHANDLER_FUNCTION)ServiceCtrlHandler);

        //SCM에게 통보 및 종료 이벤트 생성
        terminateEvent = CreateEvent (0, TRUE, FALSE, 0);
        .
        .
        //notify SCM
        .
        .
        //startup parameter 체크
        //start service
        //notify SCM service is runnning
        SendStatusToSCM(SERVICE_RUNNING, NO_ERROR, 0 , 0, 0);

        //wait for stop signal and then terminate
        WaitForSingleObject(terminateEvent, INFINITE);

        terminate(0);
    }
  3. Handler 함수는 SCM으로 부터 넘어오는 요청을 처리하는 switch 구문으로 이루어진다.
    void ServiceCtrlHandler(DWORD controlCode)
    {
        DWORD currentState = 0;
        BOOL success;

        switch(controlCode)
        {
            // START = ServiceMain()

            // STOP
            case SERVICE_CONTROL_STOP:
                currentState = SERVICE_STOP_PENDING;
                //notify SCM
                SendStatusToSCM(SERVICE_STOP_PENDING,
                                            NO_ERROR,
                                            0,
                                            1,
                                            5000);
                //stop service
                StopService();
                return;

            // PAUSE
            case SERVICE_CONTROL_PAUSE:
                if (runningService && !pauseService)
                {
                     //notify SCM
                     success = SendStatusToSCM(SERVICE_PAUSE_PENDING,
                                                                 NO_ERROR,
                                                                 0,
                                                                 1,
                                                                 1000);
                     PauseService();
                     currentState = SERVICE_PAUSED;
                }
                break;

            // RESUME
            case SERVICE_CONTROL_CONTINUE:
                if (runningService && pauseService)
                {
                     //notify SCM
                     success = SendStatusToSCM(SERVICE_CONTINUE_PENDING,
                                                                 NO_ERROR,
                                                                 0,
                                                                 1,
                                                                 1000);
                     ResumeService();
                     currentState = SERVICE_RUNNING;
                }
                break;

            // UPDATE
            case SERVICE_CONTROL_INTERROGATE:
                //update status out of switch()
                break;

            case SERVICE_CONTROL_SHUTDOWN:
                //do nothing
                return;
            default:
                break;
        }
        //notify SCM current state
        SendStatusToSCM(currentState, NO_ERROR, 0, 0, 0);
    }
  4. 서비스 프로그램은 main, ServiceMain, Handler 함수를 뿐만 아니라 서비스 자신을 위한 쓰레드를 갖추었을때 비로소 서비스 프로그램으로써 모든 것을 다 갖추었다고 말 할 수 있습니다.

위와 같이 서비스 프로그램을 만들었다고 해서 모든 것이 끝난 것은 아닙니다. 서비스는 특별한 프로세스로 취급되기에 일반 exe 처럼 더블클릭이나 커맨드 창에 이름을 적어 주는 것으로 실행 되지 않습니다. 서비스 프로세스는 서비스 매니저에 의해서 실행 되어져야 하며, 서비스 매니저에 의해 관리 되기 위해서는 등록 과정이 필요합니다. 이런 등록을 위해서 'sc'라는 바이너리가 기본적으로 제공되어지고 있습니다. 프로그램 내에서 특별한 인자를 받아 스스로 등록하도록 하는 방법도 있지만 여기서는 sc 에 대해서만 다루고 나머지는 따로 자리를 마련하도록 하겠습니다.

c:\> sc create [service name] [binPath= ] ..

기본 형태는 위와 같습니다. 서비스 등록을 위해서 sc 뒤에 create라는 인자를 주고 등록할 서비스의 이름과 실행할 바이너리 경로를 적어줍니다. 이때 주의할 점은 binPath= 와 뒤에 들어가는 패스 사이에는 반드시 스페이스 문자가 있어야 한다는 것입니다. 예를 들어 binPath= C:\bin\path\some.exe 와 같은 형식으로 말입니다. sc /? 를 커맨드 창에 입력하시면 보다 자세한 설명이 출력 됩니다.

원문 보기 : http://www.codeguru.com/Cpp/W-P/system/services/article.php/c5785/

'진리는어디에' 카테고리의 다른 글

const vs mutable  (4) 2006.12.21
PHP - 문자열 취급하기  (0) 2006.12.14
가변인자를 이용한 함수(va_list)  (10) 2006.12.13
SQL command  (0) 2006.12.13
Writing a Windows Service Program  (8) 2006.12.11
Python 정규 표현식  (1) 2006.12.11
Posted by kukuta

댓글을 달아 주세요

  1. Favicon of http://blog.ggamsso.wo.tc/ BlogIcon 깜쏘 2006.12.13 17:55  댓글주소  수정/삭제  댓글쓰기

    선배, 오늘 zdnet에서 기사를 봤는데, 선배 경력 관리해야 할 것 같아요.
    볼 때마다 새로운 걸 하고 있으니...
    아직 취업한지 얼마 안되었지만 관리 하세욧!!!

  2. Favicon of https://kukuta.tistory.com BlogIcon kukuta 2006.12.14 10:08 신고  댓글주소  수정/삭제  댓글쓰기

    음...이거는 그냥 취미 생활이야..
    내가 원래 윈도우였으니까 윈도우를 잊지 않기 위해서 공부하는거지..

    나의 주 임무는...잡무다...-_-

  3. Favicon of https://kukuta.tistory.com BlogIcon kukuta 2006.12.15 14:28 신고  댓글주소  수정/삭제  댓글쓰기

    한번 댓글 단 글은 더 이상 댓글을 달 수 없나??

  4. Favicon of http://www.thesispaperswriting.com/assignments_writing.htm BlogIcon Custom Assignment Writing 2011.05.26 00:47  댓글주소  수정/삭제  댓글쓰기

    yes...:) great work ! I definitely enjoying every little bit of it I have you bookmarked to check out new stuff you post