동기화 객체

-       멀티 쓰레드 소프트웨어 설계에서 가장 중요한 측면은 데이터 구조체와 데이터의 유효성을 보증하기 위한 동기화 메커니즘을 설계하는 방법임

-       스케줄러는 쓰레드의 대기 상태 조건이 언제 만족되고 그 때 어느 쓰레드가 실행되어야 하는지 알기 위해서 동기화 객체가 존재하는지 알아야만 함

 

Kernel mode

이벤트

· true, false 상태를 갖는 간단한 Boolean 동기화 객체

· 이벤트 객체를 대기하기 위해서 WaitForSingleObject, WaitForMultipleObject 사용)

뮤텍스

· 어느 한 시점에서 하나의 쓰레드만이 점유할 수 있는 객체 → 이미 점유된 뮤텍스를 다른 쓰레드가 점유하고 자 한다면, 해당 쓰레드가 종료되거나 해제할 때 까지 대기해야 함 → 대기하고 있는 쓰레드가 하나 이상일 경우 뮤텍스를 요청한 순서대로 소유권을 넘김

세머포어

· 뮤텍스와 비슷하며, 동시에 세마포어 객체를 소유할 수 있는 쓰레드 수 지정함

세마포어를 소유할 수 있는 쓰레드 개수가 전부 차면 세마포어의 소유권을 요청한 쓰레드는 다른 쓰레가 세머포어의 소유권으르 해지할 때 까지 대기함

User mode

크리티컬 섹션

· 뮤텍스의 최적화된 구현 형태

· 뮤텍스와 논리적으로 동일하지만 하나의 프로세스에서만 유효함

· 실질적으로 대기모드가 필요할 때만 커널 모드로 스위칭됨

[흔히 사용되는 동기화 객체]

 

프로세스 초기화 과정

 

순서

수행 내용

1

프로세스 객체와 새로운 주소공간 생성

프로세스가 CreateProcess Win32 API를 호출 → API는 프로세스 객체와 메모리 주소공간 할당

2

CreateProcess API NTDLL.DLL과 프로그램의 실행 바이너리 파일(.exe file)을 주소공간에 매핑

3

CreateProcess API는 프로세스의 첫번째 쓰레드를 생성 → 이 쓰레드를 위한 스택공간 할당

4

NTDLL.DLL LdrpInitialize함수가 실행됨으로써 프로세스의 첫 번째 쓰레드가 다시 실행

5

LdrpInitialize는 첫 번째 실행 바이너리의 임포트 테이블을 반복적으로 조사해서 실행에 필요한 모든 실행 바이너리를 메모리에 매핑

6

이 시점에서 제어권은 LdrpRunInitializeRoutines로 넘어감

(LdrpRunInitializeRoutines : NTDLL.DLL의 내부 루틴으로, 주소공간에 현재 로드된 모든 정적링크 DLL들을 초기화시키는 역할 수행함. 초기화 작업은 DLL_PROCESS_ATTACH옵션으로 각 DLL의 엔트리 포인트를 호출하는 방식)

7

모든 DLL에 대한 초기화 작업이 이루어지면 LdrpInitialize는 쓰레드의 실제적인 초기화 루틴인 KERNEL32.DLL BaseProcessStart함수를 호출함(BaseProcessStart함수는 프로세스의 초기화 작업이 완료된 시점에 실행 바이너리의 WinMain엔트리 포인트를 호출함)

 

 


'Security > Reverse Engineering' 카테고리의 다른 글

[Reversing]실행 포멧  (0) 2010.05.08
[Reversing]API  (0) 2010.05.04
[Reversing]프로세스와 쓰레드  (0) 2010.05.04
[Reversing]네임드 객체  (0) 2010.05.03
[Reversing]객체와 핸들  (0) 2010.05.03

프로세스와 쓰레드

프로세스(process)

 - 윈도우 기본 요소

 - 프로세스 메모리 주소 공간 :

· 프로그램을 실행시키기 위해 사용

  · 각 프로그램은 자신만의 고유한 주소 공간에서 실행되는 것을 보장하기 위한 것

· 시스템은 프로세스 주소공간 안에 코드 모듈을 로드

· 실행되는 프로세스는 반드시 최소 하나 이상의 쓰레드를 가져야 함

 

쓰레드(thread)

 - 가장 기본적인 코드 실행 유닛

 - 매 순간마다 프로세스는 항상 쓰레드를 실행 시킴(, 실행코드를 실행함)

 - 쓰레드가 마지막으로 실행됐을 때의 프로세스 상태를 시스템에게 알려주는 CONTEXT Data 구조체와 스택공간으로 사용된 하나 또는 두개의 메모리 공간이 결합된 데이터 구조체일 뿐

 

- 유저모드코드와 커널모드커널을 번갈아 가면서 실행(하나의 쓰레드는 두개의 스택을 가질 수 있음)

-  유저모드스택과 커널모드스택 분리(보안성, 안정성)

 

scheduler and dispatcher

- 스케줄러와 디스페처에 의해 쓰레드 관리

- 어느 쓰레드를 얼마만큼의 시간동안 실행시킬지 판단하고, 현재 실행 중인 쓰레드를 다른 쓰레드로 바꾸기 위한 실질적인 컨텍스트 스위칭을 수행함

 

# 윈도우 커널은 선점형이고 인터럽트 가능함 → 이는 커널 모드에서 실행되고 있는 쓰레드는 유저모드의 쓰레드와 만찬가지로 인터럽트될 수 있음을 의미함

 

컨텍스트 스위칭

EX: 쓰레드가 CPU를 점유하고 그 쓰레드에 대한 실질적인 인터럽트를 수행하면 안되는 경우가 있음(예:GetMessage 처리 시)

이 때 커널은 프로세서의 전체상태를 저장하고 다른 쓰레드로 스위칭해서 그 쓰레드를 실행 시킴

어느 한 프로그램의 실행이 한가한 시점에는 충분히 다른 프로그램이 CPU를 사용할 수 있고 프로세서의 실행이 지연되는 것을 막기 위해 쓰레드 스위칭을 수행함

 

퀀텀(Quantum)

 - 모든 쓰레드가 연속적으로 실행될 수 있는 최대 크기의 시간

 - 커널은 로우레벨 하드웨어 타이머를 이용해서 쓰레드가 얼마 동안 실행되고 있는지 모니터링함


네임드 객체

 - 일부 커널 객체는 시스템 상에서 자신을 고유하게 구별할 수 있는 이름을 가질 수 있음

 - 여러 프로세스가 동일한 이름의 객체를 사용하면 각 프로세스가 사용하는 해당 객체는 동일하다는 것이 보장됨

 - 이미 존재하는 객체에 대해 CreateMutex와 같이 객체를 생성하는 API가 호출되면 커널은 자동으로 해당 객체를 글로벌 테이블에 위치시키고 해당 객체에 대한 핸들을 반환함

 - 네임드 객체는 계층 디렉토리 구조로 배치됨(User ModeWin32 API로 접근 불가능)

 

[주요 객체 디렉토리]

BaseNamedObjects

- 전통적인 Win32네임드 객체 저장

- 모든 네임드 객체 Win32 API는 자동으로 이 디렉토리를 사용(APP는 이를 변경 할 수 없다)

 

Devices

- 현재 활성화된 시스템 디바이스의 디바이스 객체를 저장

 - TCP와 같은 논리적이 디바이스뿐만 아니라 Harddisk0와 같은 물리적인 디바이스인 경우에도 모두 해당됨

 - Win32 API는 직접 접근 불가능 à 심볼릭 링크를 통해 접근 가능

 

GLOBAL

 - 심볼릭 링크 디렉토리

 - 커널객체의 예전 스타일 이름(DOS 네이밍 체계를 따름)

 - Win32 App에서는 디바이스에 접근하려면 반드시 심볼릭 링크 이름을 이용해야 함

 


객체와 핸들

 - 원도우 커널은 객체 관리자에 의해서 객체가 관리됨

 - 커널 관련 객체만 관리(섹션, 파일, 디바이스 드라이버, 동기화 객체, 프로세스, 쓰레드 등)

 - 모든 객체는 단순희 nonpaged pool 커널 메모리에 저장된 데이터 구조체임

 - 객체는 해당 객체의 표준 객체 헤더만 알고 있음

 

핸들

- App는 객체 데이터 구조체에 직접 접근 못함 → 핸들을 이용해 객체 접근

 - 프로세스 안에서 객체를 구별하기 위한 숫자로 이뤄진 구분자

 - 핸들 값은 프로세스 핸들 테이블에서의 해당 객체에 대한 인덱스 값

 

[객체와 프로세스 핸들 테이블]

 

○ 액세스 마스크(Access Mask) : 두개의 16bit 액세스 플래그 워드로 구성(32bit)된 정수

- 상위워드 : GENERIC_WORD, GENERIC_WRITE와 같은 일반적인 액세스 플래그를 포함

 - 하위워드 : PROCESS_TERMINATE(핸들을 이용해서 프로세스를 종료시킬 수 있음)

             KEY_ENUMERATE_SUB_KEYS 플래그(레지스트리 키 안의 하위 키들을 열거)

 

○ 커널은 모든 객체에 대해 두개의 레퍼런스 카운트를 관리함

  - 커널 레퍼런스 카운트(kernel reference count)

  - 핸들 카운트(handle counter)
위의 두개 카운트 모두 0일 때 삭제됨

 


■ 색션 객체

- OS가 관리하는 특별한 메모리

- 하나 이상의 공간에 매핑 될 수 있음(App간에 공유메모리 설정이 쉬워진다)

- 시스템은 색션객체를 이용하여 커널모드 프로세스와 유저모드 프로세스 간에 메모리를 공유할 수 있음

- Win32에서는 메모리 맵 파일이라고 부름

 

Pagefile-Backed : 정보를 일시적으로 저장하고 프로세스 간이나 App , 커널과의 데이터 공유를 위해서 사용됨.

 

File-Backed : 하드 드라이브의 물리 파일을 매핑함

이 섹션은 매핑되면서 매핑 대상의 파일의 내용을 포함하며, 매핑된 메모리 내용이 변경되면 파일의 내용도 그대로 변경됨. 이는 메모리 영역의 포인터만을 이용해서 직접 파일의 내용에 접근이 가능함을 의미함

 

VAD 트리(Virtual Address Descriptor)

- VAD트리는 개별 프로세서의 주소할당을 관리하기 위해 윈도우가 사용하는 데이터 구조체

- 바이너리 트리 구조

- 각 프로세스마다 고유한 자신의 VAD트리를 보유

 

Mapped allocation : 주소 간 안에 매핑된 메모리 맵 파일(프로세스 주소공간에 로드된 모든 실행 바이너리와 모든 메모리 맵 파일)

 

Private allocation : 프로세스 안에 지역적으로 할당된 메모리(ex: Heap, Stack)

 

 

■ 유저 모드 메모리

Private Allocation

 - 프로세스에서 가장 기본적인 형태의 메모리 할당

 - App virtualAlloc Win32 API를 이용해서 메모리 블록을 요청

 - 항상 페이지 크기 단위

 - 시스템이 스택이나 힙을 할당할 때 사용

 

Heap

 - 힙 메모리 할당을 위해서는 virtualAlloc API를 사용하지 않음 à 런타임 라이브러리 함수 malloc이나 시스템 힘 API HeapAlloc을 사용함

 - App VirtualAlloc API를 이용해서 메모리 블록을 직접 할당함으로써 자신만의 힙을 구할 수 있음

 

Stack

 - 스레드가 생성되는 동안에 해당 스레드를 위한 스택을 자동 할당

 - 유저모드 스택은 스레드 내부에서만 사용됨

 

• 실행

 - 시스템은 App 코드를 실행하기 위해 코드를 메모리 맵 파일로서 메모리에 로드함

 

• 맵 뷰(섹션)

 - App은 메모리 맵 파일을 생성해서 그것을 주소 공간에 매핑시킬 수 있음(두개 이상의 프로그램이 메모리를 공유할 수 있는 일반적임 방법)

 

■ 메모리 관리 API

Win32 로우레벨 메모리 관리 API

VirtualAlloc

 - 유저모드 주소 공간에 메모리 블록을 할당

 - 메모리 블록의 크기는 항상 페이지 크기의 배수

    reserve type : 실제로 주소 공간에 메모리를 할당하지 않고 단순히 예약만 함

    commit type : 실제로 시스템 페이지 파일 안에 메모리 공간을 할당

 

VirtualProtect

  - 메모리 영역의 보호 설정 값을 변경함

 

VirtualQuery

  - 메모리 블록의 종류가 무엇인지, 사용되지 않고 있는 메모릴 블록인지 등과 같은 메모리 정보 질의

 

VirtualFree

  - 할당된 메모리 블록 해제


■ 윈도우 커널 메모리 주소 공간의 전형적인 레이아웃

● 커널 메모리 공간
  - 커널을 위해서는 2GB가 예약됨
  - 커널 자체의 코드뿐만 아니라 디바이스 드라이버와 같은 커널 컴포넌트들의 코드가 모두 포한됨

● Paged Pool과 Non-Paged Pool
 
- paged pool과 nonpaged pool은 모든 커널 컴포넌트에 의해서 사용되는 커널 모드 힙(Heap)메모리(커널 모드 코드만 접근 가능)

● 시스템 캐시
  - 윈도우의 캐시 관리자가 현재의 모든 캐시 마핑릉 매핑하는 공간
  - 캐싱은 파일을 메모리에 매핑함으로서 이루어지며, 메모리 관리자가 각 파일 매핑에 할당된 물리 메모리의 용약을 관리함

● 터미널 서비스 세션 공간
  - Win32 서브시스템의 커널 모드 컴포넌트가 이 메모리 공간 사용
  - 터미널 서비스 컴포넌트 : 하나의 윈도우 시스템에 여러개의 원격 GUI세션이 가능함
  - session Private라는 Win32 메모리 공간을 만들어서 시스템이 Win32서브시스템 인스턴스를 다중으로 로드할 수 있게 함

● 페이지 테이블과 하이퍼 공간
  - 페이지 테이블 공간 : 현재 활성화된 페이지 테이블이 가상 메모리에 매핑된 공간
  - 하이퍼 공간 : 주로 현재 프로세스의 워킹 셋에 대한 매핑 공간

● 시스템 워킹 셋
  - 시스템 워킹 셋 : 시스템의 물리 메모리(단, 페이징이 가능한 메모리만)를 관리하는 시스템 전역 데이터 구조체
  - 현재 사용되고 있는 페이지 현황 파악
  - 이 데이터 구조체가 관리하는 가장 큰 메모리 역역 은 Paged Pool과 시스템영역

● 시스템 페이지 테이블 엔트리(PTE)
  - 큰 커널 메모리 할당에 사용됨
  - 커널 과 드라이버가 큰 용량의 가상 메모리 공간이 필요할 때 사용되는 가상 메모리 공간(힙 아님)
  - 디바이스 드라이버 실행 코드를 매핑(MmAllocateMappingAddress커널 API 사용)
  - 커널 스택을 저장






■compiler : 소스파일을 이용해서 그에 대응되는 기계어 코드 파일을 만들어 냄. 플랫폼에 종속적임

C, C++ → 컴파일러 → 목적코드

 

목적코드 : 인간보다 기계가 이해하기 쉽다. 역으로 말하면 인간이 읽고 이해하기 힘들다.

 

Java Virtual machine : java와 같은 하이레벨 언어는 목적코드 대신에 바이트코드를 생성함. 이 바이트 코드를 해석해서 의미대로 작업을 수행함.(어느 시점에서는 바이트 코드를 CPU가 실행할 수 있는 목적코드로 변환해야 함)(플랫폼에 종속적이지 않다.)

 Java compiler → bytecode → virtual machine → object code → CPU


■원시소스가 exe실행 파일이 되는 과정



 ■메모리 구조와 각 영역의 역할

+ Recent posts